{-# OPTIONS --cubical --no-import-sorts --safe #-}

module Cubical.Algebra.Group.Higher where

open import Cubical.Core.Everything
open import Cubical.Data.Nat
open import Cubical.Data.Sigma
open import Cubical.Data.Unit
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Prelude hiding (comp)
open import Cubical.Foundations.Pointed
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.Univalence
open import Cubical.Homotopy.Loopspace
open import Cubical.Homotopy.Connected
open import Cubical.Homotopy.Base
open import Cubical.Homotopy.PointedFibration
open import Cubical.Algebra.Group.Base
open import Cubical.Algebra.Group.EilenbergMacLane1
open import Cubical.HITs.EilenbergMacLane1

open import Cubical.Algebra.Group.Base
open import Cubical.Algebra.Group.Morphism
open import Cubical.Algebra.Group.MorphismProperties
open import Cubical.Foundations.GroupoidLaws
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Equiv.Properties
open import Cubical.HITs.PropositionalTruncation renaming (rec to propRec)
open import Cubical.HITs.Truncation.FromNegOne as Trunc renaming (rec to trRec)
open import Cubical.HITs.SetTruncation
open import Cubical.Functions.Surjection
open import Cubical.Functions.Embedding

import Cubical.Foundations.GroupoidLaws as GL

private
  variable
    ℓ ℓ' : Level

record HigherGroup ℓ : Type (ℓ-suc ℓ) where
  constructor highergroup
  field
    base : Pointed ℓ
    isConn : isConnected 2 (typ base)

record BGroup ℓ (n k : ℕ) : Type (ℓ-suc ℓ) where
  no-eta-equality
  constructor bgroup
  field
    base : Pointed ℓ
    isConn : isConnected (k + 1) (typ base)
    isTrun : isOfHLevel (n + k + 2) (typ base)

BGroupΣ : {ℓ : Level} (n k : ℕ) → Type (ℓ-suc ℓ)
BGroupΣ {ℓ} n k = Σ[ A ∈ Type ℓ ] A × (isConnected (k + 1) A) × (isOfHLevel (n + k + 2) A)

module _ where
  open BGroup
  η-BGroup : {n k : ℕ} {BG BH : BGroup ℓ n k}
             → (p : typ (base BG) ≡ typ (base BH))
             → (q : PathP (λ i → p i) (pt (base BG)) (pt (base BH)))
             → BG ≡ BH
  base (η-BGroup p q i) .fst = p i
  base (η-BGroup p q i) .snd = q i
  isConn (η-BGroup {k = k} {BG = BG} {BH = BH} p q i) = r i
    where
      r : PathP (λ i → isConnected (k + 1) (p i)) (isConn BG) (isConn BH)
      r = isProp→PathP (λ _ → isPropIsOfHLevel 0) (isConn BG) (isConn BH)
  isTrun (η-BGroup {n = n} {k = k} {BG = BG} {BH = BH} p q i) = s i
    where
      s : PathP (λ i → isOfHLevel (n + k + 2) (p i)) (isTrun BG) (isTrun BH)
      s = isProp→PathP (λ i → isPropIsOfHLevel (n + k + 2)) (isTrun BG) (isTrun BH)

-- morphisms

BGroupHom : {n k : ℕ} (G : BGroup ℓ n k) (H : BGroup ℓ' n k) → Type (ℓ-max ℓ ℓ')
BGroupHom G H = (BGroup.base G) →∙ (BGroup.base H)

BGroupHomΣ : {n k : ℕ} (BG : BGroupΣ {ℓ} n k) (BH : BGroupΣ {ℓ'} n k) → Type (ℓ-max ℓ ℓ')
BGroupHomΣ (base , pt , _) (base' , pt' , _) = (base , pt) →∙ (base' , pt')

isOfHLevel-BGroupHomΣ : {n k : ℕ} (BG : BGroupΣ {ℓ} n k) (BH : BGroupΣ {ℓ'} n k)
                        → isOfHLevel (n + 2) (BGroupHomΣ BG BH)
isOfHLevel-BGroupHomΣ {n = n} {k = k} BG BH = x'
  where
    z : isOfHLevel (n + k + 2) (fst BH)
    z = snd (snd (snd BH))
    z' : isOfHLevel (n + 1 + 1 + k) (fst BH)
    z' = subst (λ m → isOfHLevel m (fst BH))
               (n + k + 2 ≡⟨ sym (+-assoc n k 2) ⟩
               n + (k + 2) ≡⟨ cong (n +_) (+-comm k 2) ⟩
               n + (2 + k) ≡⟨ +-assoc n 2 k ⟩
               (n + 2) + k ≡⟨ cong (_+ k) (+-assoc n 1 1) ⟩
               n + 1 + 1 + k ∎)
               z
    x : isOfHLevel (n + 1 + 1) (BGroupHomΣ BG BH)
    x = sec∙Trunc (fst BG , fst (snd BG)) (fst (snd (snd BG))) λ _ → (fst BH , fst (snd BH)) , z'
    x' : isOfHLevel (n + 2) (BGroupHomΣ BG BH)
    x' = subst (λ m → isOfHLevel m (BGroupHomΣ BG BH)) (sym (+-assoc n 1 1)) x

BGroupIso : {n k : ℕ} (G : BGroup ℓ n k) (H : BGroup ℓ' n k) → Type (ℓ-max ℓ ℓ')
BGroupIso G H = (BGroup.base G) ≃∙ (BGroup.base H)

BGroupIdIso : {n k : ℕ} (BG : BGroup ℓ n k) → BGroupIso BG BG
BGroupIdIso BG = idEquiv∙ (BGroup.base BG)

BGroupIso→≡ : {n k : ℕ} {BG BH : BGroup ℓ n k}
                (f : BGroupIso BG BH)
                → BG ≡ BH
BGroupIso→≡ {BG = BG} {BH = BH} f = η-BGroup (ua (≃∙→≃ f)) (toPathP ((uaβ ((≃∙→≃ f)) (pt (BGroup.base BG))) ∙ f .fst .snd))

-- getters
carrier : {ℓ : Level} {n k : ℕ} (G : BGroup ℓ n k) → Pointed ℓ
carrier {k = k} BG = (Ω^ k) base
  where
    open BGroup BG

basetype : {ℓ : Level} {n k : ℕ} (BG : BGroup ℓ n k) → Type ℓ
basetype BG = typ (BGroup.base BG)

basepoint : {ℓ : Level} {n k : ℕ} (BG : BGroup ℓ n k) → basetype BG
basepoint BG = pt (BGroup.base BG)

baseΣ : {ℓ : Level} {n k : ℕ} (BG : BGroupΣ {ℓ} n k) → Σ[ A ∈ Type ℓ ] A
baseΣ (base , * , _ , _) = (base , *)

-- special cases
1BGroup : (ℓ : Level) → Type (ℓ-suc ℓ)
1BGroup ℓ = BGroup ℓ 0 1

1BGroupΣ : {ℓ : Level} → Type (ℓ-suc ℓ)
1BGroupΣ {ℓ} = BGroupΣ {ℓ} 0 1

-- first fundamental group of 1BGroups
π₁-1BGroup : {ℓ : Level} (BG : 1BGroup ℓ) → Group {ℓ}
π₁-1BGroup BG =
  makeGroup {G = (pt base) ≡ (pt base)}
            refl
            _∙_
            sym
            (isTrun (pt base) (pt base))
            GL.assoc
            (λ a → sym (GL.rUnit a))
            (λ g → sym (GL.lUnit g))
            GL.rCancel
            GL.lCancel
    where
      open BGroup BG

π₁-1BGroupΣ : {ℓ : Level} (BG : BGroupΣ {ℓ} 0 1) → Group {ℓ}
π₁-1BGroupΣ (BG , pt , conn , trunc) = π₁-1BGroup (bgroup (BG , pt) conn trunc)

-- coercions
Group→1BGroup : (G : Group {ℓ}) → 1BGroup ℓ
BGroup.base (Group→1BGroup G) .fst = EM₁ G
BGroup.base (Group→1BGroup G) .snd = embase
BGroup.isConn (Group→1BGroup G) = EM₁Connected G
BGroup.isTrun (Group→1BGroup G) = EM₁Groupoid G

-- functoriality of π₁ on 1BGroups
module _ (BG : 1BGroup ℓ) (BH : 1BGroup ℓ') where
  private
    π₁BG = π₁-1BGroup BG
    π₁BH = π₁-1BGroup BH

  π₁-1BGroup-functor : BGroupHom BG BH → GroupHom π₁BG π₁BH
  GroupHom.fun (π₁-1BGroup-functor f) g = sym (snd f) ∙∙ cong (fst f) g ∙∙ snd f
  GroupHom.isHom (π₁-1BGroup-functor f) g g' = q
    where
      f₁ = fst f
      f₂ = snd f
      f₂- = sym (snd f)
      abstract
        q = (f₂- ∙∙ cong f₁ (g ∙ g') ∙∙ f₂)
                 ≡⟨ doubleCompPath-elim' f₂- (cong f₁ (g ∙ g')) f₂ ⟩
            f₂- ∙ cong f₁ (g ∙ g') ∙ f₂
              ≡⟨ cong (λ z → (f₂- ∙ z ∙ f₂))
                      (congFunct f₁ g g') ⟩
            f₂- ∙ (cong f₁ g ∙ cong f₁ g') ∙ f₂
              ≡⟨ cong (λ z → (f₂- ∙ (cong f₁ g ∙ z) ∙ f₂))
                      (lUnit (cong f₁ g')) ⟩
            f₂- ∙ (cong f₁ g ∙ refl ∙ cong f₁ g') ∙ f₂
              ≡⟨ cong (λ z → (f₂- ∙ (cong f₁ g ∙ z ∙ cong f₁ g') ∙ f₂))
                      (sym (rCancel f₂)) ⟩
            f₂- ∙ (cong f₁ g ∙ (f₂ ∙ f₂-) ∙ cong f₁ g') ∙ f₂
              ≡⟨ cong (λ z → (f₂- ∙ (cong f₁ g ∙ z) ∙ f₂))
                      (sym (assoc _ _ _)) ⟩
            f₂- ∙ (cong f₁ g ∙ (f₂ ∙ (f₂- ∙ cong f₁ g'))) ∙ f₂
              ≡⟨ cong (λ z → (f₂- ∙ z ∙ f₂))
                      (assoc _ _ _) ⟩
            (f₂- ∙ ((cong f₁ g ∙ f₂) ∙ (f₂- ∙ cong f₁ g')) ∙ f₂)
              ≡⟨ cong (f₂- ∙_)
                      (sym (assoc _ _ _)) ⟩
            (f₂- ∙ ((cong f₁ g ∙ f₂) ∙ ((f₂- ∙ cong f₁ g') ∙ f₂)))
              ≡⟨ cong (λ z → (f₂- ∙ ((cong f₁ g ∙ f₂) ∙ z)))
                      (sym (assoc _ _ _)) ⟩
            (f₂- ∙ ((cong f₁ g ∙ f₂) ∙ (f₂- ∙ cong f₁ g' ∙ f₂)))
              ≡⟨ assoc _ _ _ ⟩
            (f₂- ∙ cong f₁ g ∙ f₂) ∙ (f₂- ∙ cong f₁ g' ∙ f₂)
              ≡⟨ cong (_∙ (f₂- ∙ cong f₁ g' ∙ f₂))
                      (sym (doubleCompPath-elim' f₂- (cong f₁ g) f₂)) ⟩
            (f₂- ∙∙ cong f₁ g ∙∙ f₂) ∙ (f₂- ∙ cong f₁ g' ∙ f₂)
              ≡⟨ cong ((f₂- ∙∙ cong f₁ g ∙∙ f₂) ∙_)
                      (sym (doubleCompPath-elim' f₂- (cong f₁ g') f₂)) ⟩
            (f₂- ∙∙ cong f₁ g ∙∙ f₂) ∙ (f₂- ∙∙ cong f₁ g' ∙∙ f₂) ∎

-- π₁ is a left inverse to EM₁
π₁EM₁≃inv : (G : Group {ℓ}) → GroupEquiv G (π₁-1BGroup (Group→1BGroup G))
GroupEquiv.eq (π₁EM₁≃inv G) = isoToEquiv (invIso (ΩEM₁Iso G))
GroupEquiv.isHom (π₁EM₁≃inv G) g g' = emloop-comp G g g'

π₁EM₁≃ : (G : Group {ℓ}) → GroupEquiv (π₁-1BGroup (Group→1BGroup G)) G
π₁EM₁≃ G = invGroupEquiv (π₁EM₁≃inv G)

-- the functorial action of EM₁ on groups
-- is a left inverse to the functorial action of π₁
-- on 1BGroups.
module _ (H : Group {ℓ}) (BG : 1BGroup ℓ') where
  private
    EM₁H = Group→1BGroup H
    π₁EM₁H = π₁-1BGroup EM₁H
    π₁BG = π₁-1BGroup BG

  -- from the EM construction it follows
  -- that there is a homomorphism H → π₁ (EM₁ H)
  H→π₁EM₁H : GroupHom H π₁EM₁H
  H→π₁EM₁H = GroupEquiv.hom (π₁EM₁≃inv H)

  -- the promised functorial left inverse
  -- split up into the three components:
  -- function, is equivalence and pointedness.
  -- pointedness component is trivial

  -- on the object level
  EM₁-functor-lInv-function : GroupHom π₁EM₁H π₁BG → basetype EM₁H → basetype BG
  EM₁-functor-lInv-function f =
    rec' H
        (BGroup.isTrun BG)
        (basepoint BG)
        (GroupHom.fun (compGroupHom H→π₁EM₁H f))
        λ g h → sym (GroupHom.isHom (compGroupHom H→π₁EM₁H f) g h)

  EM₁-functor-lInv-pointed : (f : GroupHom π₁EM₁H π₁BG)
                             → EM₁-functor-lInv-function f (basepoint EM₁H) ≡ basepoint BG
  EM₁-functor-lInv-pointed f = refl

  -- produces an equivalence proof when given a group iso
  EM₁-functor-lInv-onIso-isEquiv : (f : GroupEquiv π₁EM₁H π₁BG)
                                 → isEquiv (EM₁-functor-lInv-function (GroupEquiv.hom f))
  EM₁-functor-lInv-onIso-isEquiv f =
    isEmbedding×isSurjection→isEquiv (isEmbedding-φ , isSurjection-φ)
    where
      φ₁ : basetype EM₁H → basetype BG
      φ₁ = EM₁-functor-lInv-function (GroupEquiv.hom f)
      abstract
        isEmbedding-φ : isEmbedding φ₁
        isEmbedding-φ = reduceToBp isEmbPt
          where
            isEmb : (x y : basetype EM₁H) → Type (ℓ-max ℓ ℓ')
            isEmb x y = isEquiv (cong {x = x} {y = y} φ₁)

            isPropIsEmb : (x y : basetype EM₁H) → isProp (isEmb x y)
            isPropIsEmb x y = isPropIsEquiv (cong {x = x} {y = y} φ₁)

            f-equiv : (basepoint EM₁H ≡ basepoint EM₁H) ≃ (basepoint BG ≡ basepoint BG)
            f-equiv =  GroupEquiv.eq f
            f₁ = fst f-equiv

            γ : ⟨ H ⟩ ≃ ⟨ π₁EM₁H ⟩
            γ = GroupEquiv.eq (π₁EM₁≃inv H)
            β : ⟨ π₁EM₁H ⟩ ≃ typ (carrier BG)
            β = f-equiv
            δ : ⟨ H ⟩ ≃ typ (carrier BG)
            δ = compEquiv γ β
            p : equivFun δ ≡ (λ h → cong φ₁ (equivFun γ h))
            p = refl
            η = (λ (h : ⟨ H ⟩) → cong φ₁ (equivFun γ h))
            isEquiv-η : isEquiv η
            isEquiv-η = equivFun≡→isEquiv δ η λ _ → refl
            congφ∼f : (h : ⟨ H ⟩) → f₁ (GroupHom.fun H→π₁EM₁H h) ≡ cong φ₁ (GroupHom.fun H→π₁EM₁H h)
            congφ∼f p = refl

            isEmbPt : isEmb (basepoint EM₁H) (basepoint EM₁H)
            isEmbPt = equivCompLCancel γ (cong φ₁) isEquiv-η

            g : Unit → basetype EM₁H
            g _ = basepoint EM₁H

            isConn-g : isConnectedFun 1 g
            isConn-g = isConnectedPoint 1 (BGroup.isConn EM₁H) (basepoint EM₁H)

            reduceToBp1 : ((a : Unit) → isEmb (basepoint EM₁H) (g a))
                         → (h' : basetype EM₁H) → isEmb (basepoint EM₁H) h'
            reduceToBp1 =
              Iso.inv (elim.isIsoPrecompose g
                                            1
                                            (λ h' → (isEmb (basepoint EM₁H) h') , isPropIsEmb (basepoint EM₁H) h')
                                            isConn-g)
            reduceToBp1' : isEmb (basepoint EM₁H) (basepoint EM₁H)
                         → (h' : basetype EM₁H) → isEmb (basepoint EM₁H) h'
            reduceToBp1' p = reduceToBp1 λ _ → p

            reduceToBp2 : (h' : basetype EM₁H) → isEmb (basepoint EM₁H) h'
                          → ((a : Unit) → isEmb (g a) h') → (h : basetype EM₁H) → isEmb h h'
            reduceToBp2 h' p =
              Iso.inv (elim.isIsoPrecompose g
                                            1
                                            (λ h → (isEmb h h') , (isPropIsEmb h h'))
                                            isConn-g)
            reduceToBp2' : ((h' : basetype EM₁H) → isEmb (basepoint EM₁H) h')
                           → (h h' : basetype EM₁H) → isEmb h h'
            reduceToBp2' Q h h' = reduceToBp2 h' (Q h') (λ _ → Q h') h

            reduceToBp : isEmb (basepoint EM₁H) (basepoint EM₁H) → (h h' : basetype EM₁H) → isEmb h h'
            reduceToBp p = reduceToBp2' (reduceToBp1' p)

        isSurjection-φ : isSurjection φ₁
        isSurjection-φ g = propTruncΣ← (λ x → φ₁ x ≡ g) ∣ basepoint EM₁H , fst r ∣
          where
            r : isContr ∥ φ₁ (basepoint EM₁H) ≡ g ∥
            r = isContrRespectEquiv (invEquiv propTrunc≃Trunc1)
                                    (isConnectedPath 1
                                                     (BGroup.isConn BG)
                                                     (φ₁ (basepoint EM₁H))
                                                     g)


{-# OPTIONS --without-K --safe #-}
module Cats.Category.Cat.Facts.Exponential where

open import Data.Product using (_,_)
open import Level using (_⊔_)
open import Relation.Binary using (IsEquivalence)

open import Cats.Bifunctor using
  (Bifunctor ; Bifunctor→Functor₁ ; transposeBifunctor₁ ; transposeBifunctor₁-resp)
open import Cats.Category
open import Cats.Category.Cat as Cat′ using
  (Cat ; Functor ; _∘_ ; _≈_) renaming
  (id to Id)
open import Cats.Category.Cat.Facts.Product using (hasBinaryProducts ; ⟨_×_⟩)
open import Cats.Category.Fun using (_↝_ ; Trans ; ≈-intro ; ≈-elim)
open import Cats.Category.Fun.Facts.Iso using (≈→≅)
open import Cats.Category.Product.Binary using (_×_)
open import Cats.Trans.Iso as NatIso using (NatIso)
open import Cats.Util.Conv

import Cats.Category.Base as Base
import Cats.Category.Constructions.Unique as Unique
import Cats.Category.Constructions.Iso as Iso

open Functor
open Trans
open Iso.Iso

private
  module Cat≈ {lo la l≈ lo′ la′ l≈′}
    {C : Category lo la l≈} {D : Category lo′ la′ l≈′}
    = IsEquivalence (Cat′.equiv {C = C} {D})

  module Fun {lo la l≈ lo′ la′ l≈′}
    {C : Category lo la l≈} {D : Category lo′ la′ l≈′}
    = Category (C ↝ D)

module _ {lo la l≈ lo′ la′ l≈′}
  {B : Category lo la l≈} {C : Category lo′ la′ l≈′}
  where

  private
    module B = Category B
    module C = Category C

  Eval : Bifunctor (B ↝ C) B C
  Eval = record
      { fobj = λ where
          (F , x) → fobj F x
      ; fmap = λ where
          {F , a} {G , b} (θ , f) → fmap G f C.∘ component θ a
      ; fmap-resp = λ where
          {F , a} {G , b} {θ , f} {ι , g} (θ≈ι , f≈g) →
            C.∘-resp (fmap-resp G f≈g) (≈-elim θ≈ι)
      ; fmap-id = λ { {F , b} → C.≈.trans (C.∘-resp-l (fmap-id F)) C.id-l }
      ; fmap-∘ = λ where
          {F , a} {G , b} {H , c} {θ , f} {ι , g} →
            begin
              (fmap H f C.∘ component θ b) C.∘ (fmap G g C.∘ component ι a)
            ≈⟨ C.∘-resp-l (C.≈.sym (natural θ)) ⟩
              (component θ c C.∘ fmap G f) C.∘ (fmap G g C.∘ component ι a)
            ≈⟨ C.assoc ⟩
              component θ c C.∘ fmap G f C.∘ (fmap G g C.∘ component ι a)
            ≈⟨ C.∘-resp-r C.unassoc ⟩
              component θ c C.∘ (fmap G f C.∘ fmap G g) C.∘ component ι a
            ≈⟨ C.∘-resp-r (C.∘-resp-l (fmap-∘ G)) ⟩
              component θ c C.∘ fmap G (f B.∘ g) C.∘ component ι a
            ≈⟨ C.unassoc ⟩
              (component θ c C.∘ fmap G (f B.∘ g)) C.∘ component ι a
            ≈⟨ C.∘-resp-l (natural θ) ⟩
              (fmap H (f B.∘ g) C.∘ component θ a) C.∘ component ι a
            ≈⟨ C.assoc ⟩
              fmap H (f B.∘ g) C.∘ component θ a C.∘ component ι a
            ∎
      }
    where
      open C.≈-Reasoning

module _ {lo la l≈ lo′ la′ l≈′ lo″ la″ l≈″}
  {B : Category lo la l≈} {C : Category lo′ la′ l≈′} {D : Category lo″ la″ l≈″}
  where

  private
    module B = Category B
    module C = Category C
    module D = Category D

  Curry : Bifunctor B C D → Functor B (C ↝ D)
  Curry F = transposeBifunctor₁ F

  Curry-resp : ∀ {F G} → F ≈ G → Curry F ≈ Curry G
  Curry-resp = transposeBifunctor₁-resp

  Curry-correct : ∀ {F} → Eval ∘ ⟨ Curry F × Id ⟩ ≈ F
  Curry-correct {F} = record
      { iso = D.≅.refl
      ; forth-natural = λ where
        {a , a′} {b , b′} {f , f′} →
          begin
            D.id D.∘ fmap F (B.id , f′) D.∘ fmap F (f , C.id)
          ≈⟨ D.≈.trans D.id-l (fmap-∘ F) ⟩
            fmap F (B.id B.∘ f , f′ C.∘ C.id)
          ≈⟨ fmap-resp F (B.id-l , C.id-r) ⟩
            fmap F (f , f′)
          ≈⟨ D.≈.sym D.id-r ⟩
            fmap F (f , f′) D.∘ D.id
          ∎
      }
    where
      open D.≈-Reasoning

  Curry-unique : ∀ {F Curry′}
    → Eval ∘ ⟨ Curry′ × Id ⟩ ≈ F
    → Curry′ ≈ Curry F
  Curry-unique {F} {Curry′} eq@record { iso = iso ; forth-natural = fnat } = record
      { iso = λ {x} →
          let open Fun.≅-Reasoning in
          Fun.≅.sym (
            begin
              fobj (Curry F) x
            ≈⟨ NatIso.iso (Curry-resp (Cat≈.sym eq)) ⟩
              fobj (Curry (Eval ∘ ⟨ Curry′ × Id ⟩)) x
            ≈⟨ ≈→≅ (lem x) ⟩
              fobj Curry′ x
            ∎
          )
      ; forth-natural = λ {a} {b} {f} → ≈-intro λ {x} →
          -- TODO We need a simplification tactic.
          let open D.≈-Reasoning in
          triangle (forth iso D.∘ component (fmap Curry′ f) x)
          ( begin
              ((forth iso D.∘
                 (fmap (fobj Curry′ b) C.id D.∘ component (fmap Curry′ B.id) x) D.∘ D.id) D.∘
               D.id D.∘ D.id) D.∘
              component (fmap Curry′ f) x
            ≈⟨ D.∘-resp-l (D.≈.trans (D.∘-resp-r D.id-l) (D.≈.trans D.id-r
                (D.≈.trans (D.∘-resp-r (D.≈.trans D.id-r (D.≈.trans
                  (D.∘-resp (fmap-id (fobj Curry′ b))
                    (≈-elim (fmap-id Curry′))) D.id-l))) D.id-r))) ⟩
              forth iso D.∘ component (fmap Curry′ f) x
            ∎
          )
          ( begin
              fmap F (f , C.id) D.∘
              (forth iso D.∘ (fmap (fobj Curry′ a) C.id D.∘ component (fmap Curry′ B.id) x) D.∘ D.id) D.∘
              D.id D.∘ D.id
            ≈⟨ D.∘-resp-r (D.≈.trans (D.∘-resp-r D.id-r) (D.≈.trans D.id-r
                (D.≈.trans (D.∘-resp-r (D.≈.trans D.id-r (D.≈.trans
                  (D.∘-resp-l (fmap-id (fobj Curry′ a)))
                    (D.≈.trans D.id-l (≈-elim (fmap-id Curry′)))))) D.id-r))) ⟩
              fmap F (f , C.id) D.∘ forth iso
            ≈⟨ D.≈.sym fnat ⟩
              forth iso D.∘ fmap (fobj Curry′ b) C.id D.∘ component (fmap Curry′ f) x
            ≈⟨ D.∘-resp-r (D.≈.trans (D.∘-resp-l (fmap-id (fobj Curry′ b))) D.id-l) ⟩
              forth iso D.∘ component (fmap Curry′ f) x
            ∎
          )
      }
    where
      lem : ∀ x
        → NatIso (Bifunctor→Functor₁ (Eval ∘ ⟨ Curry′ × Id ⟩) x) (fobj Curry′ x)
      lem x = record
          { iso = D.≅.refl
          ; forth-natural = D.≈.trans D.id-l (D.∘-resp-r (≈-elim (fmap-id Curry′)))
          }

  -- We get the following lemmas for free from HasExponentials, but only when
  -- all the categories are at the same (single) level.

  Uncurry : Functor B (C ↝ D) → Bifunctor B C D
  Uncurry F = Eval ∘ ⟨ F × Id ⟩

  Curry∘Uncurry : {F : Functor B (C ↝ D)}
    → Curry (Uncurry F) ≈ F
  Curry∘Uncurry {F} = Cat≈.sym (Curry-unique {Curry′ = F} Cat≈.refl)

  Uncurry∘Curry : {F : Bifunctor B C D}
    → Uncurry (Curry F) ≈ F
  Uncurry∘Curry {F} = Curry-correct {F = F}

instance
  hasExponentials : ∀ {l} → HasExponentials (Cat l l l)
  hasExponentials {l} = record
      { _↝′_ = λ B C → record
          { Cᴮ = B ↝ C
          ; eval = Eval
          ; curry′ = λ {A} F → record
              { arr = Curry F
              ; prop = Curry-correct {B = A} {B} {C} {F}
              ; unique = λ {g} eq → Cat≈.sym (Curry-unique eq)
              }
          }
      }


------------------------------------------------------------------------
-- The Agda standard library
--
-- Some derivable properties
------------------------------------------------------------------------

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

open import Algebra

module Algebra.Properties.CommutativeMonoid {g₁ g₂} (M : CommutativeMonoid g₁ g₂) where

open import Algebra.Operations.CommutativeMonoid M
open import Algebra.Solver.CommutativeMonoid M
open import Relation.Binary as B using (_Preserves_⟶_)
open import Function
open import Function.Equality using (_⟨$⟩_)
open import Data.Product
open import Data.Nat using (ℕ; zero; suc)
open import Data.Fin using (Fin; zero; suc)
open import Data.List as List using ([]; _∷_)
import Data.Fin.Properties as FP
open import Data.Fin.Permutation as Perm using (Permutation; Permutation′; _⟨$⟩ˡ_; _⟨$⟩ʳ_)
open import Data.Fin.Permutation.Components as PermC
open import Data.Table as Table
open import Data.Table.Relation.Binary.Equality as TE using (_≗_)
open import Data.Unit using (tt)
import Data.Table.Properties as TP
open import Relation.Binary.PropositionalEquality as P using (_≡_)
open import Relation.Nullary using (yes; no)
open import Relation.Nullary.Negation using (contradiction)
open import Relation.Nullary.Decidable using (⌊_⌋)

open CommutativeMonoid M
  renaming
  ( ε         to 0#
  ; _∙_       to _+_
  ; ∙-cong    to +-cong
  ; ∙-congˡ   to +-congˡ
  ; ∙-congʳ   to +-congʳ
  ; identityˡ to +-identityˡ
  ; identityʳ to +-identityʳ
  ; assoc     to +-assoc
  ; comm      to +-comm
  )
open import Algebra.FunctionProperties _≈_
open import Relation.Binary.Reasoning.Setoid setoid

module _ {n} where
  open B.Setoid (TE.setoid setoid n) public
    using ()
    renaming (_≈_ to _≋_)

-- When summing over a function from a finite set, we can pull out any value and move it to the front.

sumₜ-remove : ∀ {n} {i : Fin (suc n)} t → sumₜ t ≈ lookup t i + sumₜ (remove i t)
sumₜ-remove {_}     {zero}   t = refl
sumₜ-remove {zero}  {suc ()} t
sumₜ-remove {suc n} {suc i}  t′ =
  begin
    t₀ + ∑t           ≈⟨ +-congˡ (sumₜ-remove t) ⟩
    t₀ + (tᵢ + ∑t′)   ≈⟨ solve 3 (λ x y z → x ⊕ (y ⊕ z) ⊜ y ⊕ (x ⊕ z)) refl t₀ tᵢ ∑t′ ⟩
    tᵢ + (t₀ + ∑t′)   ∎
  where
  t = tail t′
  t₀ = head t′
  tᵢ = lookup t i
  ∑t = sumₜ t
  ∑t′ = sumₜ (remove i t)

-- '_≈_' is a congruence over 'sumTable n'.

sumₜ-cong-≈ : ∀ {n} → sumₜ {n} Preserves _≋_ ⟶ _≈_
sumₜ-cong-≈ {zero}  p = refl
sumₜ-cong-≈ {suc n} p = +-cong (p _) (sumₜ-cong-≈ (p ∘ suc))

-- '_≡_' is a congruence over 'sum n'.

sumₜ-cong-≡ : ∀ {n} → sumₜ {n} Preserves _≗_ ⟶ _≡_
sumₜ-cong-≡ {zero}  p = P.refl
sumₜ-cong-≡ {suc n} p = P.cong₂ _+_ (p _) (sumₜ-cong-≡ (p ∘ suc))

-- If addition is idempotent on a particular value 'x', then summing over a
-- nonzero number of copies of 'x' gives back 'x'.

sumₜ-idem-replicate : ∀ n {x} → _+_ IdempotentOn x → sumₜ (replicate {n = suc n} x) ≈ x
sumₜ-idem-replicate zero        idem = +-identityʳ _
sumₜ-idem-replicate (suc n) {x} idem = begin
  x + (x + sumₜ (replicate {n = n} x))  ≈⟨ sym (+-assoc _ _ _) ⟩
  (x + x) + sumₜ (replicate {n = n} x)  ≈⟨ +-congʳ idem ⟩
  x + sumₜ (replicate {n = n} x)        ≈⟨ sumₜ-idem-replicate n idem ⟩
  x                                 ∎

-- The sum over the constantly zero function is zero.

sumₜ-zero : ∀ n → sumₜ (replicate {n = n} 0#) ≈ 0#
sumₜ-zero n = begin
  sumₜ (replicate {n = n} 0#)      ≈⟨ sym (+-identityˡ _) ⟩
  0# + sumₜ (replicate {n = n} 0#) ≈⟨ sumₜ-idem-replicate n (+-identityˡ 0#) ⟩
  0#                               ∎

-- The '∑' operator distributes over addition.

∑-distrib-+ : ∀ n (f g : Fin n → Carrier) → ∑[ i < n ] (f i + g i) ≈ ∑[ i < n ] f i + ∑[ i < n ] g i
∑-distrib-+ zero    f g = sym (+-identityˡ _)
∑-distrib-+ (suc n) f g = begin
  f₀ + g₀ + ∑fg          ≈⟨ +-assoc _ _ _ ⟩
  f₀ + (g₀ + ∑fg)        ≈⟨ +-congˡ (+-congˡ (∑-distrib-+ n _ _)) ⟩
  f₀ + (g₀ + (∑f + ∑g))  ≈⟨ solve 4 (λ a b c d → a ⊕ (c ⊕ (b ⊕ d)) ⊜ (a ⊕ b) ⊕ (c ⊕ d)) refl f₀ ∑f g₀ ∑g ⟩
  (f₀ + ∑f) + (g₀ + ∑g)  ∎
  where
  f₀ = f zero
  g₀ = g zero
  ∑f  = ∑[ i < n ] f (suc i)
  ∑g  = ∑[ i < n ] g (suc i)
  ∑fg = ∑[ i < n ] (f (suc i) + g (suc i))

-- The '∑' operator commutes with itself.

∑-comm : ∀ n m (f : Fin n → Fin m → Carrier) → ∑[ i < n ] ∑[ j < m ] f i j ≈ ∑[ j < m ] ∑[ i < n ] f i j
∑-comm zero    m f = sym (sumₜ-zero m)
∑-comm (suc n) m f = begin
  ∑[ j < m ] f zero j + ∑[ i < n ] ∑[ j < m ] f (suc i) j  ≈⟨ +-congˡ (∑-comm n m _) ⟩
  ∑[ j < m ] f zero j + ∑[ j < m ] ∑[ i < n ] f (suc i) j  ≈⟨ sym (∑-distrib-+ m _ _) ⟩
  ∑[ j < m ] (f zero j + ∑[ i < n ] f (suc i) j)           ∎

-- Any permutation of a table has the same sum as the original.

sumₜ-permute : ∀ {m n} t (π : Permutation m n) → sumₜ t ≈ sumₜ (permute π t)
sumₜ-permute {zero}  {zero}  t π = refl
sumₜ-permute {zero}  {suc n} t π = contradiction π (Perm.refute λ())
sumₜ-permute {suc m} {zero}  t π = contradiction π (Perm.refute λ())
sumₜ-permute {suc m} {suc n} t π = begin
  sumₜ t                                                                            ≡⟨⟩
  lookup t 0i           + sumₜ (remove 0i t)                                        ≡⟨ P.cong₂ _+_ (P.cong (lookup t) (P.sym (Perm.inverseʳ π))) P.refl ⟩
  lookup πt (π ⟨$⟩ˡ 0i) + sumₜ (remove 0i t)                                        ≈⟨ +-congˡ (sumₜ-permute (remove 0i t) (Perm.remove (π ⟨$⟩ˡ 0i) π)) ⟩
  lookup πt (π ⟨$⟩ˡ 0i) + sumₜ (permute (Perm.remove (π ⟨$⟩ˡ 0i) π) (remove 0i t))  ≡⟨ P.cong₂ _+_ P.refl (sumₜ-cong-≡ (P.sym ∘ TP.remove-permute π 0i t)) ⟩
  lookup πt (π ⟨$⟩ˡ 0i) + sumₜ (remove (π ⟨$⟩ˡ 0i) πt)                              ≈⟨ sym (sumₜ-remove (permute π t)) ⟩
  sumₜ πt                                                                           ∎
  where
  0i = zero
  πt = permute π t

∑-permute : ∀ {m n} f (π : Permutation m n) → ∑[ i < n ] f i ≈ ∑[ i < m ] f (π ⟨$⟩ʳ i)
∑-permute = sumₜ-permute ∘ tabulate

-- If the function takes the same value at 'i' and 'j', then transposing 'i' and
-- 'j' then selecting 'j' is the same as selecting 'i'.

select-transpose : ∀ {n} t (i j : Fin n) → lookup t i ≈ lookup t j → ∀ k → (lookup (select 0# j t) ∘ PermC.transpose i j) k ≈ lookup (select 0# i t) k
select-transpose _ i j e k with k FP.≟ i
... | yes p rewrite P.≡-≟-identity FP._≟_ {j} P.refl = sym e
... | no ¬p with k FP.≟ j
...   | no ¬q rewrite proj₂ (P.≢-≟-identity FP._≟_ ¬q) = refl
...   | yes q rewrite proj₂ (P.≢-≟-identity FP._≟_
                               (¬p ∘ P.trans q ∘ P.sym)) = refl

-- Summing over a pulse gives you the single value picked out by the pulse.

sumₜ-select : ∀ {n i} (t : Table Carrier n) → sumₜ (select 0# i t) ≈ lookup t i
sumₜ-select {zero}  {()}
sumₜ-select {suc n} {i} t = begin
  sumₜ (select 0# i t)                                        ≈⟨ sumₜ-remove {i = i} (select 0# i t) ⟩
  lookup (select 0# i t) i + sumₜ (remove i (select 0# i t))  ≡⟨ P.cong₂ _+_ (TP.select-lookup t) (sumₜ-cong-≡ (TP.select-remove i t)) ⟩
  lookup t i + sumₜ (replicate {n = n} 0#)                    ≈⟨ +-congˡ (sumₜ-zero n) ⟩
  lookup t i + 0#                                             ≈⟨ +-identityʳ _ ⟩
  lookup t i                                                  ∎

-- Converting to a table then summing is the same as summing the original list

sumₜ-fromList : ∀ xs → sumₜ (fromList xs) ≡ sumₗ xs
sumₜ-fromList []       = P.refl
sumₜ-fromList (x ∷ xs) = P.cong (_ +_) (sumₜ-fromList xs)

-- Converting to a list then summing is the same as summing the original table

sumₜ-toList : ∀ {n} (t : Table Carrier n) → sumₜ t ≡ sumₗ (toList t)
sumₜ-toList {zero}  _ = P.refl
sumₜ-toList {suc n} _ = P.cong (_ +_) (sumₜ-toList {n} _)


------------------------------------------------------------------------
-- A lookahead operator cannot be defined
------------------------------------------------------------------------

-- In "Parsing with First-Class Derivatives" Brachthäuser, Rendel and
-- Ostermann state that "Lookahead and [...] cannot be expressed as
-- user defined combinators". Here I give a formal proof of a variant
-- of this statement (in the present setting).

module TotalParserCombinators.NoLookahead where

open import Data.List
open import Data.List.Properties
open import Data.Product
open import Data.Unit
open import Function.Base
open import Function.Equality using (_⟨$⟩_)
open import Function.Equivalence using (Equivalence; _⇔_)
open import Relation.Binary.PropositionalEquality as PE using (_≡_)
open import Relation.Nullary

open import TotalParserCombinators.Lib
open import TotalParserCombinators.Parser
open import TotalParserCombinators.Semantics.Continuation

-- It is impossible to define a lookahead operator satisfying a
-- certain specification (without changing the interface of the parser
-- combinator library).

no-lookahead :
  ¬ ∃ λ (look : ∀ {Tok R} {f-bag : List Tok → List R} →
                ((s : List Tok) → Parser Tok R (f-bag s)) →
                Parser Tok R (f-bag [])) →
      ∀ {Tok R x s₁ s₂} {f-bag : List Tok → List R}
        {f : (s : List Tok) → Parser Tok R (f-bag s)} →
      x ⊕ s₂ ∈ look f · s₁ ⇔ x ⊕ s₂ ∈ f s₁ · s₁
no-lookahead (look , correct) = ¬f[-]·[-] f[-]·[-]
  where
  f-bag : List ⊤ → List ⊤
  f-bag []      = [ _ ]
  f-bag (_ ∷ _) = []

  f : (s : List ⊤) → Parser ⊤ ⊤ (f-bag s)
  f []      = return _
  f (_ ∷ _) = fail

  f[]·[] : _ ⊕ [] ∈ f [] · []
  f[]·[] = return

  look-f·[] : _ ⊕ [] ∈ look f · []
  look-f·[] = Equivalence.from correct ⟨$⟩ f[]·[]

  look-f·[-] : _ ⊕ [ _ ] ∈ look f · [ _ ]
  look-f·[-] = extend look-f·[]

  f[-]·[-] : _ ⊕ [ _ ] ∈ f [ _ ] · [ _ ]
  f[-]·[-] = Equivalence.to correct ⟨$⟩ look-f·[-]

  ¬f[-]·[-] : ¬ _ ⊕ [ _ ] ∈ f [ _ ] · [ _ ]
  ¬f[-]·[-] ()


------------------------------------------------------------------------
-- Context extensions with the leftmost element in the outermost
-- position
------------------------------------------------------------------------

open import Data.Universe.Indexed

module deBruijn.Context.Extension.Left
  {i u e} (Uni : IndexedUniverse i u e) where

import deBruijn.Context.Basics          as Basics
import deBruijn.Context.Extension.Right as Right
open import Function
open import Level using (_⊔_)
open import Relation.Binary.PropositionalEquality as P using (_≡_)

open Basics Uni
open Right  Uni
open P.≡-Reasoning

------------------------------------------------------------------------
-- Context extensions along with various operations making use of them

-- Context extensions.

infixr 5 _◅_

data Ctxt₊ (Γ : Ctxt) : Set (i ⊔ u ⊔ e) where
  ε   : Ctxt₊ Γ
  _◅_ : (σ : Type Γ) (Γ₊ : Ctxt₊ (Γ ▻ σ)) → Ctxt₊ Γ

-- A synonym.

ε₊[_] : (Γ : Ctxt) → Ctxt₊ Γ
ε₊[ _ ] = ε

-- Appends a context extension to a context.

infixl 5 _++₊_

_++₊_ : (Γ : Ctxt) → Ctxt₊ Γ → Ctxt
Γ ++₊ ε        = Γ
Γ ++₊ (σ ◅ Γ₊) = Γ ▻ σ ++₊ Γ₊

-- The following operations append a context extension to a context
-- extension.

infixl 5 _₊++₊_ _⁺++₊_

_₊++₊_ : ∀ {Γ} Γ₊ → Ctxt₊ (Γ ++₊ Γ₊) → Ctxt₊ Γ
ε        ₊++₊ Γ₊₊ = Γ₊₊
(σ ◅ Γ₊) ₊++₊ Γ₊₊ = σ ◅ (Γ₊ ₊++₊ Γ₊₊)

_⁺++₊_ : ∀ {Γ} Γ⁺ → Ctxt₊ (Γ ++⁺ Γ⁺) → Ctxt₊ Γ
ε      ⁺++₊ Γ₊ = Γ₊
Γ⁺ ▻ σ ⁺++₊ Γ₊ = Γ⁺ ⁺++₊ (σ ◅ Γ₊)

-- Application of context morphisms to context extensions.

infixl 8 _/̂₊_

_/̂₊_ : ∀ {Γ Δ} → Ctxt₊ Γ → Γ ⇨̂ Δ → Ctxt₊ Δ
ε        /̂₊ ρ̂ = ε
(σ ◅ Γ₊) /̂₊ ρ̂ = σ /̂ ρ̂ ◅ Γ₊ /̂₊ ρ̂ ↑̂

-- N-ary lifting of context morphisms.

infixl 10 _↑̂₊_

_↑̂₊_ : ∀ {Γ Δ} (ρ̂ : Γ ⇨̂ Δ) Γ₊ → Γ ++₊ Γ₊ ⇨̂ Δ ++₊ Γ₊ /̂₊ ρ̂
ρ̂ ↑̂₊ ε        = ρ̂
ρ̂ ↑̂₊ (σ ◅ Γ₊) = ρ̂ ↑̂ ↑̂₊ Γ₊

-- N-ary weakening.

ŵk₊ : ∀ {Γ} Γ₊ → Γ ⇨̂ Γ ++₊ Γ₊
ŵk₊ ε        = îd
ŵk₊ (σ ◅ Γ₊) = ŵk[ σ ] ∘̂ ŵk₊ Γ₊

------------------------------------------------------------------------
-- Equality

-- Equality of context extensions.

record [Ctxt₊] : Set (i ⊔ u ⊔ e) where
  constructor [_]
  field
    {Γ} : Ctxt
    Γ₊  : Ctxt₊ Γ

infix 4 _≅-Ctxt₊_

_≅-Ctxt₊_ : ∀ {Γ₁} (Γ₊₁ : Ctxt₊ Γ₁)
              {Γ₂} (Γ₊₂ : Ctxt₊ Γ₂) → Set _
Γ₊₁ ≅-Ctxt₊ Γ₊₂ = _≡_ {A = [Ctxt₊]} [ Γ₊₁ ] [ Γ₊₂ ]

≅-Ctxt₊-⇒-≡ : ∀ {Γ} {Γ₊₁ Γ₊₂ : Ctxt₊ Γ} →
              Γ₊₁ ≅-Ctxt₊ Γ₊₂ → Γ₊₁ ≡ Γ₊₂
≅-Ctxt₊-⇒-≡ P.refl = P.refl

-- Certain uses of substitutivity can be removed.

drop-subst-Ctxt₊ : ∀ {a} {A : Set a} {x₁ x₂}
                   (f : A → Ctxt) {Γ₊} (x₁≡x₂ : x₁ ≡ x₂) →
                   P.subst (λ x → Ctxt₊ (f x)) x₁≡x₂ Γ₊ ≅-Ctxt₊ Γ₊
drop-subst-Ctxt₊ f P.refl = P.refl

------------------------------------------------------------------------
-- Some congruences

ε₊-cong : ∀ {Γ₁ Γ₂} → Γ₁ ≅-Ctxt Γ₂ → ε₊[ Γ₁ ] ≅-Ctxt₊ ε₊[ Γ₂ ]
ε₊-cong P.refl = P.refl

◅-cong : ∀ {Γ₁ σ₁} {Γ₊₁ : Ctxt₊ (Γ₁ ▻ σ₁)}
           {Γ₂ σ₂} {Γ₊₂ : Ctxt₊ (Γ₂ ▻ σ₂)} →
         Γ₊₁ ≅-Ctxt₊ Γ₊₂ → σ₁ ◅ Γ₊₁ ≅-Ctxt₊ σ₂ ◅ Γ₊₂
◅-cong P.refl = P.refl

++₊-cong : ∀ {Γ₁} {Γ₊₁ : Ctxt₊ Γ₁} {Γ₂} {Γ₊₂ : Ctxt₊ Γ₂} →
           Γ₊₁ ≅-Ctxt₊ Γ₊₂ → Γ₁ ++₊ Γ₊₁ ≅-Ctxt Γ₂ ++₊ Γ₊₂
++₊-cong P.refl = P.refl

₊++₊-cong : ∀ {Γ₁ Γ₊₁} {Γ₊₊₁ : Ctxt₊ (Γ₁ ++₊ Γ₊₁)}
              {Γ₂ Γ₊₂} {Γ₊₊₂ : Ctxt₊ (Γ₂ ++₊ Γ₊₂)} →
            Γ₊₁ ≅-Ctxt₊ Γ₊₂ → Γ₊₊₁ ≅-Ctxt₊ Γ₊₊₂ →
            Γ₊₁ ₊++₊ Γ₊₊₁ ≅-Ctxt₊ Γ₊₂ ₊++₊ Γ₊₊₂
₊++₊-cong P.refl P.refl = P.refl

⁺++₊-cong : ∀ {Γ₁ Γ⁺₁} {Γ₊₁ : Ctxt₊ (Γ₁ ++⁺ Γ⁺₁)}
              {Γ₂ Γ⁺₂} {Γ₊₂ : Ctxt₊ (Γ₂ ++⁺ Γ⁺₂)} →
            Γ⁺₁ ≅-Ctxt⁺ Γ⁺₂ → Γ₊₁ ≅-Ctxt₊ Γ₊₂ →
            Γ⁺₁ ⁺++₊ Γ₊₁ ≅-Ctxt₊ Γ⁺₂ ⁺++₊ Γ₊₂
⁺++₊-cong P.refl P.refl = P.refl

/̂₊-cong : ∀ {Γ₁ Δ₁} {Γ₊₁ : Ctxt₊ Γ₁} {ρ̂₁ : Γ₁ ⇨̂ Δ₁}
            {Γ₂ Δ₂} {Γ₊₂ : Ctxt₊ Γ₂} {ρ̂₂ : Γ₂ ⇨̂ Δ₂} →
          Γ₊₁ ≅-Ctxt₊ Γ₊₂ → ρ̂₁ ≅-⇨̂ ρ̂₂ → Γ₊₁ /̂₊ ρ̂₁ ≅-Ctxt₊ Γ₊₂ /̂₊ ρ̂₂
/̂₊-cong P.refl P.refl = P.refl

↑̂₊-cong : ∀ {Γ₁ Δ₁} {ρ̂₁ : Γ₁ ⇨̂ Δ₁} {Γ₊₁ : Ctxt₊ Γ₁}
            {Γ₂ Δ₂} {ρ̂₂ : Γ₂ ⇨̂ Δ₂} {Γ₊₂ : Ctxt₊ Γ₂} →
          ρ̂₁ ≅-⇨̂ ρ̂₂ → Γ₊₁ ≅-Ctxt₊ Γ₊₂ → ρ̂₁ ↑̂₊ Γ₊₁ ≅-⇨̂ ρ̂₂ ↑̂₊ Γ₊₂
↑̂₊-cong P.refl P.refl = P.refl

ŵk₊-cong : ∀ {Γ₁} {Γ₊₁ : Ctxt₊ Γ₁} {Γ₂} {Γ₊₂ : Ctxt₊ Γ₂} →
           Γ₊₁ ≅-Ctxt₊ Γ₊₂ → ŵk₊ Γ₊₁ ≅-⇨̂ ŵk₊ Γ₊₂
ŵk₊-cong P.refl = P.refl

------------------------------------------------------------------------
-- Some properties

abstract

  -- Γ ++₊_ has at most one right identity.

  private

    ++₊-right-identity-unique′ :
      ∀ {Γ} Γ⁺ Γ₊ → Γ ≅-Ctxt Γ ++⁺ Γ⁺ ++₊ Γ₊ → Γ₊ ≅-Ctxt₊ ε₊[ Γ ++⁺ Γ⁺ ]
    ++₊-right-identity-unique′ Γ⁺ ε        _  = P.refl
    ++₊-right-identity-unique′ Γ⁺ (σ ◅ Γ₊) eq
      with ++₊-right-identity-unique′ (Γ⁺ ▻ σ) Γ₊ eq
    ++₊-right-identity-unique′ Γ⁺ (σ ◅ .ε) eq | P.refl
      with ++⁺-right-identity-unique (Γ⁺ ▻ σ) eq
    ... | ()

  ++₊-right-identity-unique :
    ∀ {Γ} Γ₊ → Γ ≅-Ctxt Γ ++₊ Γ₊ → Γ₊ ≅-Ctxt₊ ε₊[ Γ ]
  ++₊-right-identity-unique Γ₊ = ++₊-right-identity-unique′ ε Γ₊

  -- Γ ++₊_ is left cancellative.

  private

    cancel-++₊-left′ : ∀ {Γ} Γ⁺₁ Γ₊₁ Γ⁺₂ Γ₊₂ →
                       Γ ++⁺ Γ⁺₁ ++₊ Γ₊₁ ≅-Ctxt Γ ++⁺ Γ⁺₂ ++₊ Γ₊₂ →
                       Γ⁺₁ ⁺++₊ Γ₊₁ ≅-Ctxt₊ Γ⁺₂ ⁺++₊ Γ₊₂
    cancel-++₊-left′ Γ⁺₁ ε          Γ⁺₂ ε          eq = ⁺++₊-cong (cancel-++⁺-left Γ⁺₁ Γ⁺₂ eq) (ε₊-cong eq)
    cancel-++₊-left′ Γ⁺₁ (σ₁ ◅ Γ₊₁) Γ⁺₂ (σ₂ ◅ Γ₊₂) eq = cancel-++₊-left′ (Γ⁺₁ ▻ σ₁) Γ₊₁ (Γ⁺₂ ▻ σ₂) Γ₊₂ eq
    cancel-++₊-left′ Γ⁺₁ ε          Γ⁺₂ (σ₂ ◅ Γ₊₂) eq = cancel-++₊-left′  Γ⁺₁       ε   (Γ⁺₂ ▻ σ₂) Γ₊₂ eq
    cancel-++₊-left′ Γ⁺₁ (σ₁ ◅ Γ₊₁) Γ⁺₂ ε          eq = cancel-++₊-left′ (Γ⁺₁ ▻ σ₁) Γ₊₁  Γ⁺₂       ε   eq

  cancel-++₊-left : ∀ {Γ} (Γ₊₁ Γ₊₂ : Ctxt₊ Γ) →
                    Γ ++₊ Γ₊₁ ≅-Ctxt Γ ++₊ Γ₊₂ → Γ₊₁ ≅-Ctxt₊ Γ₊₂
  cancel-++₊-left Γ₊₁ Γ₊₂ = cancel-++₊-left′ ε Γ₊₁ ε Γ₊₂

  -- _++₊_/_₊++₊_ are associative.

  ++₊-++₊ : ∀ {Γ} Γ₊ Γ₊₊ → Γ ++₊ Γ₊ ++₊ Γ₊₊ ≅-Ctxt Γ ++₊ (Γ₊ ₊++₊ Γ₊₊)
  ++₊-++₊ ε        Γ₊₊ = P.refl
  ++₊-++₊ (σ ◅ Γ₊) Γ₊₊ = ++₊-++₊ Γ₊ Γ₊₊

  -- The identity substitution has no effect.

  /̂₊-îd : ∀ {Γ} (Γ₊ : Ctxt₊ Γ) → Γ₊ /̂₊ îd ≅-Ctxt₊ Γ₊
  /̂₊-îd ε        = P.refl
  /̂₊-îd (σ ◅ Γ₊) = ◅-cong (/̂₊-îd Γ₊)

  -- The n-ary lifting of the identity substitution is the identity
  -- substitution.

  îd-↑̂₊ : ∀ {Γ} (Γ₊ : Ctxt₊ Γ) → îd ↑̂₊ Γ₊ ≅-⇨̂ îd[ Γ ++₊ Γ₊ ]
  îd-↑̂₊ ε        = P.refl
  îd-↑̂₊ (σ ◅ Γ₊) = begin
    [ îd ↑̂ ↑̂₊ Γ₊ ]  ≡⟨ P.refl ⟩
    [ îd ↑̂₊ Γ₊   ]  ≡⟨ îd-↑̂₊ Γ₊ ⟩
    [ îd         ]  ∎

  -- The identity substitution has no effect even if lifted.

  /̂-îd-↑̂₊ : ∀ {Γ} Γ₊ (σ : Type (Γ ++₊ Γ₊)) → σ /̂ îd ↑̂₊ Γ₊ ≅-Type σ
  /̂-îd-↑̂₊ Γ₊ σ = begin
    [ σ /̂ îd ↑̂₊ Γ₊ ]  ≡⟨ /̂-cong (P.refl {x = [ σ ]}) (îd-↑̂₊ Γ₊) ⟩
    [ σ /̂ îd       ]  ≡⟨ P.refl ⟩
    [ σ            ]  ∎

  -- Applying two substitutions is equivalent to applying their
  -- composition.

  /̂₊-∘̂ : ∀ {Γ Δ Ε} Γ₊ (ρ̂₁ : Γ ⇨̂ Δ) (ρ̂₂ : Δ ⇨̂ Ε) →
         Γ₊ /̂₊ ρ̂₁ ∘̂ ρ̂₂ ≅-Ctxt₊ Γ₊ /̂₊ ρ̂₁ /̂₊ ρ̂₂
  /̂₊-∘̂ ε        ρ̂₁ ρ̂₂ = P.refl
  /̂₊-∘̂ (σ ◅ Γ₊) ρ̂₁ ρ̂₂ = ◅-cong (/̂₊-∘̂ Γ₊ (ρ̂₁ ↑̂) (ρ̂₂ ↑̂))

  -- _↑̂₊_ distributes over _∘̂_.

  ∘̂-↑̂₊ : ∀ {Γ Δ Ε} (ρ̂₁ : Γ ⇨̂ Δ) (ρ̂₂ : Δ ⇨̂ Ε) Γ₊ →
         (ρ̂₁ ∘̂ ρ̂₂) ↑̂₊ Γ₊ ≅-⇨̂ ρ̂₁ ↑̂₊ Γ₊ ∘̂ ρ̂₂ ↑̂₊ (Γ₊ /̂₊ ρ̂₁)
  ∘̂-↑̂₊ ρ̂₁ ρ̂₂ ε        = P.refl
  ∘̂-↑̂₊ ρ̂₁ ρ̂₂ (σ ◅ Γ₊) = begin
    [ (ρ̂₁ ∘̂ ρ̂₂) ↑̂ ↑̂₊ Γ₊                     ]  ≡⟨ P.refl ⟩
    [ (ρ̂₁ ↑̂ ∘̂ ρ̂₂ ↑̂) ↑̂₊ Γ₊                   ]  ≡⟨ ∘̂-↑̂₊ (ρ̂₁ ↑̂) (ρ̂₂ ↑̂) Γ₊ ⟩
    [ (ρ̂₁ ↑̂ ↑̂₊ Γ₊) ∘̂ (ρ̂₂ ↑̂ ↑̂₊ (Γ₊ /̂₊ ρ̂₁ ↑̂)) ]  ∎

  -- A corollary.

  /̂-↑̂₊-/̂-ŵk-↑̂₊ : ∀ {Γ Δ} σ (ρ̂ : Γ ⇨̂ Δ) (Γ₊ : Ctxt₊ Γ) τ →
                 τ /̂ ρ̂ ↑̂₊ Γ₊ /̂ ŵk[ σ /̂ ρ̂ ] ↑̂₊ (Γ₊ /̂₊ ρ̂) ≅-Type
                 τ /̂ ŵk[ σ ] ↑̂₊ Γ₊ /̂ ρ̂ ↑̂ ↑̂₊ (Γ₊ /̂₊ ŵk)
  /̂-↑̂₊-/̂-ŵk-↑̂₊ σ ρ̂ Γ₊ τ = /̂-cong (P.refl {x = [ τ ]}) (begin
    [ ρ̂ ↑̂₊ Γ₊ ∘̂ ŵk ↑̂₊ (Γ₊ /̂₊ ρ̂)         ]  ≡⟨ P.sym $ ∘̂-↑̂₊ ρ̂ ŵk Γ₊ ⟩
    [ (ρ̂ ∘̂ ŵk) ↑̂₊ Γ₊                    ]  ≡⟨ P.refl ⟩
    [ (ŵk[ σ ] ∘̂ ρ̂ ↑̂) ↑̂₊ Γ₊             ]  ≡⟨ ∘̂-↑̂₊ ŵk[ σ ] (ρ̂ ↑̂) Γ₊ ⟩
    [ ŵk[ σ ] ↑̂₊ Γ₊ ∘̂ ρ̂ ↑̂ ↑̂₊ (Γ₊ /̂₊ ŵk) ]  ∎)

  -- ŵk₊ commutes (modulo lifting) with arbitrary context morphisms.

  ∘̂-ŵk₊ : ∀ {Γ Δ} (ρ̂ : Γ ⇨̂ Δ) Γ₊ →
          ρ̂ ∘̂ ŵk₊ (Γ₊ /̂₊ ρ̂) ≅-⇨̂ ŵk₊ Γ₊ ∘̂ ρ̂ ↑̂₊ Γ₊
  ∘̂-ŵk₊ ρ̂ ε        = P.refl
  ∘̂-ŵk₊ ρ̂ (σ ◅ Γ₊) = begin
    [ ρ̂ ∘̂ ŵk ∘̂ ŵk₊ (Γ₊ /̂₊ ρ̂ ↑̂)         ]  ≡⟨ P.refl ⟩
    [ ŵk[ σ ] ∘̂ ρ̂ ↑̂ ∘̂ ŵk₊ (Γ₊ /̂₊ ρ̂ ↑̂)  ]  ≡⟨ ∘̂-cong (P.refl {x = [ ŵk ]}) (∘̂-ŵk₊ (ρ̂ ↑̂) Γ₊) ⟩
    [ ŵk ∘̂ ŵk₊ Γ₊ ∘̂ ρ̂ ↑̂ ↑̂₊ Γ₊          ]  ∎

  -- ŵk₊ is homomorphic with respect to _₊++₊_/_∘̂_.

  ŵk₊-₊++₊ : ∀ {Γ} (Γ₊ : Ctxt₊ Γ) (Γ₊₊ : Ctxt₊ (Γ ++₊ Γ₊)) →
             ŵk₊ (Γ₊ ₊++₊ Γ₊₊) ≅-⇨̂ ŵk₊ Γ₊ ∘̂ ŵk₊ Γ₊₊
  ŵk₊-₊++₊ ε        Γ₊₊ = P.refl
  ŵk₊-₊++₊ (σ ◅ Γ₊) Γ₊₊ = ∘̂-cong (P.refl {x = [ ŵk ]}) (ŵk₊-₊++₊ Γ₊ Γ₊₊)

  -- Two n-ary liftings can be merged into one.

  ↑̂₊-₊++₊ : ∀ {Γ Δ} (ρ̂ : Γ ⇨̂ Δ) Γ₊ Γ₊₊ →
            ρ̂ ↑̂₊ (Γ₊ ₊++₊ Γ₊₊) ≅-⇨̂ ρ̂ ↑̂₊ Γ₊ ↑̂₊ Γ₊₊
  ↑̂₊-₊++₊ ρ̂ ε        Γ₊₊ = P.refl
  ↑̂₊-₊++₊ ρ̂ (σ ◅ Γ₊) Γ₊₊ = ↑̂₊-₊++₊ (ρ̂ ↑̂) Γ₊ Γ₊₊

  -- _/̂₊_ distributes over _₊++₊_ (sort of).

  ++₊-₊++₊-/̂₊ :
    ∀ {Γ Δ} (ρ̂ : Γ ⇨̂ Δ) Γ₊ Γ₊₊ →
    Δ ++₊ (Γ₊ ₊++₊ Γ₊₊) /̂₊ ρ̂ ≅-Ctxt Δ ++₊ Γ₊ /̂₊ ρ̂ ++₊ Γ₊₊ /̂₊ ρ̂ ↑̂₊ Γ₊
  ++₊-₊++₊-/̂₊         ρ̂ ε        Γ₊₊ = P.refl
  ++₊-₊++₊-/̂₊ {Δ = Δ} ρ̂ (σ ◅ Γ₊) Γ₊₊ = ++₊-₊++₊-/̂₊ (ρ̂ ↑̂) Γ₊ Γ₊₊


module OlderBasicILP.Direct.Translation where

open import Common.Context public

-- import OlderBasicILP.Direct.Hilbert.Sequential as HS
import OlderBasicILP.Direct.Hilbert.Nested as HN
import OlderBasicILP.Direct.Gentzen as G

-- open HS using () renaming (_⊢×_ to HS⟨_⊢×_⟩ ; _⊢_ to HS⟨_⊢_⟩) public
open HN using () renaming (_⊢_ to HN⟨_⊢_⟩ ; _⊢⋆_ to HN⟨_⊢⋆_⟩) public
open G using  () renaming (_⊢_ to G⟨_⊢_⟩ ; _⊢⋆_ to G⟨_⊢⋆_⟩) public

-- Translation from sequential Hilbert-style to nested.

-- hs→hn : ∀ {A Γ} → HS⟨ Γ ⊢ A ⟩ → HN⟨ Γ ⊢ A ⟩
-- hs→hn = ?

-- Translation from nested Hilbert-style to sequential.

-- hn→hs : ∀ {A Γ} → HN⟨ Γ ⊢ A ⟩ → HS⟨ Γ ⊢ A ⟩
-- hn→hs = ?

-- Deduction theorem for sequential Hilbert-style.

-- hs-lam : ∀ {A B Γ} → HS⟨ Γ , A ⊢ B ⟩ → HS⟨ Γ ⊢ A ▻ B ⟩
-- hs-lam = hn→hs ∘ HN.lam ∘ hs→hn

-- Translation from Hilbert-style to Gentzen-style.

mutual
  hn→gᵇᵒˣ : HN.Box → G.Box
  hn→gᵇᵒˣ HN.[ t ] = {!G.[ hn→g t ]!}

  hn→gᵀ : HN.Ty → G.Ty
  hn→gᵀ (HN.α P)   = G.α P
  hn→gᵀ (A HN.▻ B) = hn→gᵀ A G.▻ hn→gᵀ B
  hn→gᵀ (T HN.⦂ A) = hn→gᵇᵒˣ T G.⦂ hn→gᵀ A
  hn→gᵀ (A HN.∧ B) = hn→gᵀ A G.∧ hn→gᵀ B
  hn→gᵀ HN.⊤      = G.⊤

  hn→gᵀ⋆ : Cx HN.Ty → Cx G.Ty
  hn→gᵀ⋆ ∅       = ∅
  hn→gᵀ⋆ (Γ , A) = hn→gᵀ⋆ Γ , hn→gᵀ A

  hn→gⁱ : ∀ {A Γ} → A ∈ Γ → hn→gᵀ A ∈ hn→gᵀ⋆ Γ
  hn→gⁱ top     = top
  hn→gⁱ (pop i) = pop (hn→gⁱ i)

  hn→g : ∀ {A Γ} → HN⟨ Γ ⊢ A ⟩ → G⟨ hn→gᵀ⋆ Γ ⊢ hn→gᵀ A ⟩
  hn→g (HN.var i)   = G.var (hn→gⁱ i)
  hn→g (HN.app t u) = G.app (hn→g t) (hn→g u)
  hn→g HN.ci        = G.ci
  hn→g HN.ck        = G.ck
  hn→g HN.cs        = G.cs
  hn→g (HN.box t)   = {!G.box (hn→g t)!}
  hn→g HN.cdist     = {!G.cdist!}
  hn→g HN.cup       = {!G.cup!}
  hn→g HN.cdown     = G.cdown
  hn→g HN.cpair     = G.cpair
  hn→g HN.cfst      = G.cfst
  hn→g HN.csnd      = G.csnd
  hn→g HN.unit      = G.unit

-- hs→g : ∀ {A Γ} → HS⟨ Γ ⊢ A ⟩ → G⟨ Γ ⊢ A ⟩
-- hs→g = hn→g ∘ hs→hn

-- Translation from Gentzen-style to Hilbert-style.

mutual
  g→hnᵇᵒˣ : G.Box → HN.Box
  g→hnᵇᵒˣ G.[ t ] = {!HN.[ g→hn t ]!}

  g→hnᵀ : G.Ty → HN.Ty
  g→hnᵀ (G.α P)   = HN.α P
  g→hnᵀ (A G.▻ B) = g→hnᵀ A HN.▻ g→hnᵀ B
  g→hnᵀ (T G.⦂ A) = g→hnᵇᵒˣ T HN.⦂ g→hnᵀ A
  g→hnᵀ (A G.∧ B) = g→hnᵀ A HN.∧ g→hnᵀ B
  g→hnᵀ G.⊤      = HN.⊤

  g→hnᵀ⋆ : Cx G.Ty → Cx HN.Ty
  g→hnᵀ⋆ ∅       = ∅
  g→hnᵀ⋆ (Γ , A) = g→hnᵀ⋆ Γ , g→hnᵀ A

  g→hnⁱ : ∀ {A Γ} → A ∈ Γ → g→hnᵀ A ∈ g→hnᵀ⋆ Γ
  g→hnⁱ top     = top
  g→hnⁱ (pop i) = pop (g→hnⁱ i)

  g→hn : ∀ {A Γ} → G⟨ Γ ⊢ A ⟩ → HN⟨ g→hnᵀ⋆ Γ ⊢ g→hnᵀ A ⟩
  g→hn (G.var i)         = HN.var (g→hnⁱ i)
  g→hn (G.lam t)         = HN.lam (g→hn t)
  g→hn (G.app t u)       = HN.app (g→hn t) (g→hn u)
--  g→hn (G.multibox ts u) = {!HN.multibox (g→hn⋆ ts) (g→hn u)!}
  g→hn (G.down t)        = HN.down (g→hn t)
  g→hn (G.pair t u)      = HN.pair (g→hn t) (g→hn u)
  g→hn (G.fst t)         = HN.fst (g→hn t)
  g→hn (G.snd t)         = HN.snd (g→hn t)
  g→hn G.unit            = HN.unit

  g→hn⋆ : ∀ {Ξ Γ} → G⟨ Γ ⊢⋆ Ξ ⟩ → HN⟨ g→hnᵀ⋆ Γ ⊢⋆ g→hnᵀ⋆ Ξ ⟩
  g→hn⋆ {∅}     ∙        = ∙
  g→hn⋆ {Ξ , A} (ts , t) = g→hn⋆ ts , g→hn t

-- g→hs : ∀ {A Γ} → G⟨ Γ ⊢ A ⟩ → HS⟨ Γ ⊢ A ⟩
-- g→hs = hn→hs ∘ g→hn


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

-- Reconstruct the whole combinator from context
getℂκ : ∀ {A B} → A ↔ B → Context {A} {B} → ∃[ C ] ∃[ D ] (C ↔ D)
getℂκ c ☐ = _ , _ , c
getℂκ c (☐⨾ c₂ • κ) = getℂκ (c ⨾ c₂) κ
getℂκ c (c₁ ⨾☐• κ) = getℂκ (c₁ ⨾ c) κ
getℂκ c (☐⊕ c₂ • κ) = getℂκ (c ⊕ c₂) κ
getℂκ c (c₁ ⊕☐• κ) = getℂκ (c₁ ⊕ c) κ
getℂκ c (☐⊗[ c₂ , x ]• κ) = getℂκ (c ⊗ c₂) κ
getℂκ c ([ c₁ , x ]⊗☐• κ) = getℂκ (c₁ ⊗ c) κ

getℂ : (st : State) → st ≢ ⊠ → ∃[ A ] ∃[ B ] (A ↔ B)
getℂ ⟨ c ∣ _ ∣ κ ⟩▷  _ = getℂκ c κ
getℂ ［ c ∣ _ ∣ κ ］▷ _ = getℂκ c κ
getℂ ⟨ c ∣ _ ∣ κ ⟩◁ _ = getℂκ c κ
getℂ ［ c ∣ _ ∣ κ ］◁ _ = getℂκ c κ
getℂ ⊠ st≢⊠ = ⊥-elim (st≢⊠ refl)

-- The reconstructed combinator stays the same
ℂInvariant : ∀ {st st'} → st ↦ st' → (¬fail : st ≢ ⊠) → (¬fail' : st' ≢ ⊠) → getℂ st ¬fail ≡ getℂ st' ¬fail'
ℂInvariant ↦⃗₁ _ _ = refl
ℂInvariant ↦⃗₂ _ _ = refl
ℂInvariant ↦⃗₃ _ _ = refl
ℂInvariant ↦⃗₄ _ _ = refl
ℂInvariant ↦⃗₅ _ _ = refl
ℂInvariant ↦⃗₆ _ _ = refl
ℂInvariant ↦⃗₇ _ _ = refl
ℂInvariant ↦⃗₈ _ _ = refl
ℂInvariant ↦⃗₉ _ _ = refl
ℂInvariant ↦⃗₁₀ _ _ = refl
ℂInvariant ↦⃗₁₁ _ _ = refl
ℂInvariant ↦⃗₁₂ _ _ = refl
ℂInvariant ↦⃖₁ _ _ = refl
ℂInvariant ↦⃖₂ _ _ = refl
ℂInvariant ↦⃖₃ _ _ = refl
ℂInvariant ↦⃖₄ _ _ = refl
ℂInvariant ↦⃖₅ _ _ = refl
ℂInvariant ↦⃖₆ _ _ = refl
ℂInvariant ↦⃖₇ _ _ = refl
ℂInvariant ↦⃖₈ _ _ = refl
ℂInvariant ↦⃖₉ _ _ = refl
ℂInvariant ↦⃖₁₀ _ _ = refl
ℂInvariant ↦⃖₁₁ _ _ = refl
ℂInvariant ↦⃖₁₂ _ _ = refl
ℂInvariant ↦η₊₁ _ _ = refl
ℂInvariant ↦η₊₂ _ _ = refl
ℂInvariant ↦ε₊₁ _ _ = refl
ℂInvariant ↦ε₊₂ _ _ = refl
ℂInvariant ↦⃗ηₓ _ _ = refl
ℂInvariant ↦⃖ηₓ₁ _ _ = refl
ℂInvariant ↦⃖ηₓ₂ _ ¬fail' = ⊥-elim (¬fail' refl)
ℂInvariant ↦⃗εₓ₁ _ _ = refl
ℂInvariant ↦⃗εₓ₂ _ ¬fail' = ⊥-elim (¬fail' refl)
ℂInvariant ↦⃖εₓ _ _ = refl

ℂInvariant* : ∀ {st st'} → st ↦* st' → (¬fail : st ≢ ⊠) → (¬fail' : st' ≢ ⊠) → getℂ st ¬fail ≡ getℂ st' ¬fail'
ℂInvariant* {⟨ _ ∣ _ ∣ _ ⟩▷} ◾ _ _ = refl
ℂInvariant* {［ _ ∣ _ ∣ _ ］▷} ◾ _ _ = refl
ℂInvariant* {⟨ _ ∣ _ ∣ _ ⟩◁} ◾ _ _ = refl
ℂInvariant* {［ _ ∣ _ ∣ _ ］◁} ◾ _ _ = refl
ℂInvariant* {⊠} ◾ ¬fail _ = ⊥-elim (¬fail refl)
ℂInvariant* (r ∷ ◾) ¬fail ¬fail' = ℂInvariant r ¬fail ¬fail'
ℂInvariant* (r ∷ (r' ∷ rs)) ¬fail ¬fail' = trans (ℂInvariant r ¬fail λ {refl → ⊠-is-stuck refl (_ , r')})
                                                 (ℂInvariant* (r' ∷ rs) _ ¬fail')

-- Get the type of the deepest context
get𝕌 : ∀ {A B} → Context {A} {B} → 𝕌 × 𝕌
get𝕌 {A} {B} ☐ = A , B
get𝕌 (☐⨾ c₂ • κ) = get𝕌 κ
get𝕌 (c₁ ⨾☐• κ) = get𝕌 κ
get𝕌 (☐⊕ c₂ • κ) = get𝕌 κ
get𝕌 (c₁ ⊕☐• κ) = get𝕌 κ
get𝕌 (☐⊗[ c₂ , x ]• κ) = get𝕌 κ
get𝕌 ([ c₁ , x ]⊗☐• κ) = get𝕌 κ

get𝕌State : (st : State) → st ≢ ⊠ → 𝕌 × 𝕌
get𝕌State ⟨ c ∣ v ∣ κ ⟩▷ _ = get𝕌 κ
get𝕌State ［ c ∣ v ∣ κ ］▷ _ = get𝕌 κ
get𝕌State ⟨ c ∣ v ∣ κ ⟩◁ _ = get𝕌 κ
get𝕌State ［ c ∣ v ∣ κ ］◁ _ = get𝕌 κ
get𝕌State ⊠ nf = ⊥-elim (nf refl)

-- Append a context to another context
appendκ : ∀ {A B} → (ctx : Context {A} {B}) → let (C , D) = get𝕌  ctx
                                              in  Context {C} {D} → Context {A} {B}
appendκ ☐ ctx = ctx
appendκ (☐⨾ c₂ • κ) ctx = ☐⨾ c₂ • appendκ κ ctx
appendκ (c₁ ⨾☐• κ) ctx = c₁ ⨾☐• appendκ κ ctx
appendκ (☐⊕ c₂ • κ) ctx = ☐⊕ c₂ • appendκ κ ctx
appendκ (c₁ ⊕☐• κ) ctx = c₁ ⊕☐• appendκ κ ctx
appendκ (☐⊗[ c₂ , x ]• κ) ctx = ☐⊗[ c₂ , x ]• appendκ κ ctx
appendκ ([ c₁ , x ]⊗☐• κ) ctx = [ c₁ , x ]⊗☐• appendκ κ ctx

appendκState : ∀ st → (nf : st ≢ ⊠)
             → let (A , B) = get𝕌State st nf
               in  Context {A} {B} → State
appendκState ⟨ c ∣ v ∣ κ ⟩▷ _ ctx = ⟨ c ∣ v ∣ appendκ κ ctx ⟩▷
appendκState ［ c ∣ v ∣ κ ］▷ _ ctx = ［ c ∣ v ∣ appendκ κ ctx ］▷
appendκState ⟨ c ∣ v ∣ κ ⟩◁ _ ctx = ⟨ c ∣ v ∣ appendκ κ ctx ⟩◁
appendκState ［ c ∣ v ∣ κ ］◁ _ ctx = ［ c ∣ v ∣ appendκ κ ctx ］◁
appendκState ⊠ nf = ⊥-elim (nf refl)

-- The type of context does not change during execution
𝕌Invariant : ∀ {st st'} → st ↦ st' → (nf : st ≢ ⊠) → (nf' : st' ≢ ⊠) → get𝕌State st nf ≡ get𝕌State st' nf'
𝕌Invariant ↦⃗₁ nf nf' = refl
𝕌Invariant ↦⃗₂ nf nf' = refl
𝕌Invariant ↦⃗₃ nf nf' = refl
𝕌Invariant ↦⃗₄ nf nf' = refl
𝕌Invariant ↦⃗₅ nf nf' = refl
𝕌Invariant ↦⃗₆ nf nf' = refl
𝕌Invariant ↦⃗₇ nf nf' = refl
𝕌Invariant ↦⃗₈ nf nf' = refl
𝕌Invariant ↦⃗₉ nf nf' = refl
𝕌Invariant ↦⃗₁₀ nf nf' = refl
𝕌Invariant ↦⃗₁₁ nf nf' = refl
𝕌Invariant ↦⃗₁₂ nf nf' = refl
𝕌Invariant ↦⃖₁ nf nf' = refl
𝕌Invariant ↦⃖₂ nf nf' = refl
𝕌Invariant ↦⃖₃ nf nf' = refl
𝕌Invariant ↦⃖₄ nf nf' = refl
𝕌Invariant ↦⃖₅ nf nf' = refl
𝕌Invariant ↦⃖₆ nf nf' = refl
𝕌Invariant ↦⃖₇ nf nf' = refl
𝕌Invariant ↦⃖₈ nf nf' = refl
𝕌Invariant ↦⃖₉ nf nf' = refl
𝕌Invariant ↦⃖₁₀ nf nf' = refl
𝕌Invariant ↦⃖₁₁ nf nf' = refl
𝕌Invariant ↦⃖₁₂ nf nf' = refl
𝕌Invariant ↦η₊₁ nf nf' = refl
𝕌Invariant ↦η₊₂ nf nf' = refl
𝕌Invariant ↦ε₊₁ nf nf' = refl
𝕌Invariant ↦ε₊₂ nf nf' = refl
𝕌Invariant ↦⃗ηₓ nf nf' = refl
𝕌Invariant ↦⃖ηₓ₁ nf nf' = refl
𝕌Invariant ↦⃖ηₓ₂ nf nf' = ⊥-elim (nf' refl)
𝕌Invariant ↦⃗εₓ₁ nf nf' = refl
𝕌Invariant ↦⃗εₓ₂ nf nf' = ⊥-elim (nf' refl)
𝕌Invariant ↦⃖εₓ nf nf' = refl

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

-- Appending context does not affect reductions
appendκ↦ : ∀ {st st'} → (r : st ↦ st') (nf : st ≢ ⊠) (nf' : st' ≢ ⊠) (eq : get𝕌State st nf ≡  get𝕌State st' nf')
         → (κ : Context {proj₁ (get𝕌State st nf)} {proj₂ (get𝕌State st nf)})
         → appendκState st nf κ ↦ appendκState st' nf' (subst (λ {(A , B) → Context {A} {B}}) eq κ)
appendκ↦ ↦⃗₁ nf nf' refl ctx = ↦⃗₁
appendκ↦ ↦⃗₂ nf nf' refl ctx = ↦⃗₂
appendκ↦ ↦⃗₃ nf nf' refl ctx = ↦⃗₃
appendκ↦ ↦⃗₄ nf nf' refl ctx = ↦⃗₄
appendκ↦ ↦⃗₅ nf nf' refl ctx = ↦⃗₅
appendκ↦ ↦⃗₆ nf nf' refl ctx = ↦⃗₆
appendκ↦ ↦⃗₇ nf nf' refl ctx = ↦⃗₇
appendκ↦ ↦⃗₈ nf nf' refl ctx = ↦⃗₈
appendκ↦ ↦⃗₉ nf nf' refl ctx = ↦⃗₉
appendκ↦ ↦⃗₁₀ nf nf' refl ctx = ↦⃗₁₀
appendκ↦ ↦⃗₁₁ nf nf' refl ctx = ↦⃗₁₁
appendκ↦ ↦⃗₁₂ nf nf' refl ctx = ↦⃗₁₂
appendκ↦ ↦⃖₁ nf nf' refl ctx = ↦⃖₁
appendκ↦ ↦⃖₂ nf nf' refl ctx = ↦⃖₂
appendκ↦ ↦⃖₃ nf nf' refl ctx = ↦⃖₃
appendκ↦ ↦⃖₄ nf nf' refl ctx = ↦⃖₄
appendκ↦ ↦⃖₅ nf nf' refl ctx = ↦⃖₅
appendκ↦ ↦⃖₆ nf nf' refl ctx = ↦⃖₆
appendκ↦ ↦⃖₇ nf nf' refl ctx = ↦⃖₇
appendκ↦ ↦⃖₈ nf nf' refl ctx = ↦⃖₈
appendκ↦ ↦⃖₉ nf nf' refl ctx = ↦⃖₉
appendκ↦ ↦⃖₁₀ nf nf' refl ctx = ↦⃖₁₀
appendκ↦ ↦⃖₁₁ nf nf' refl ctx = ↦⃖₁₁
appendκ↦ ↦⃖₁₂ nf nf' refl ctx = ↦⃖₁₂
appendκ↦ ↦η₊₁ nf nf' refl ctx = ↦η₊₁
appendκ↦ ↦η₊₂ nf nf' refl ctx = ↦η₊₂
appendκ↦ ↦ε₊₁ nf nf' refl ctx = ↦ε₊₁
appendκ↦ ↦ε₊₂ nf nf' refl ctx = ↦ε₊₂
appendκ↦ ↦⃗ηₓ nf nf' refl ctx = ↦⃗ηₓ
appendκ↦ (↦⃖ηₓ₁ {eq = eq}) nf nf' refl ctx = ↦⃖ηₓ₁ {eq = eq}
appendκ↦ ↦⃖ηₓ₂ nf nf' eq ctx = ⊥-elim (nf' refl)
appendκ↦ (↦⃗εₓ₁ {eq = eq}) nf nf' refl ctx = ↦⃗εₓ₁ {eq = eq}
appendκ↦ ↦⃗εₓ₂ nf nf' eq ctx = ⊥-elim (nf' refl)
appendκ↦ ↦⃖εₓ nf nf' refl ctx = ↦⃖εₓ

appendκ↦* : ∀ {st st'} → (r : st ↦* st') (nf : st ≢ ⊠) (nf' : st' ≢ ⊠) (eq : get𝕌State st nf ≡  get𝕌State st' nf')
          → (κ : Context {proj₁ (get𝕌State st nf)} {proj₂ (get𝕌State st nf)})
          → appendκState st nf κ ↦* appendκState st' nf' (subst (λ {(A , B) → Context {A} {B}}) eq κ)
appendκ↦* {⟨ _ ∣ _ ∣ _ ⟩▷} ◾ nf nf' refl ctx = ◾
appendκ↦* {［ _ ∣ _ ∣ _ ］▷} ◾ nf nf' refl ctx = ◾
appendκ↦* {⟨ _ ∣ _ ∣ _ ⟩◁} ◾ nf nf' refl ctx = ◾
appendκ↦* {［ _ ∣ _ ∣ _ ］◁} ◾ nf nf' refl ctx = ◾
appendκ↦* {⊠} ◾ nf nf' eq ctx = ⊥-elim (nf' refl)
appendκ↦* (↦⃗₁ {b = b} {v} {κ} ∷ rs) nf nf' eq ctx = appendκ↦ (↦⃗₁ {b = b} {v} {κ}) nf (λ ()) refl ctx ∷ appendκ↦* rs (λ ()) nf' eq ctx
appendκ↦* (↦⃗₂ {v = v} {κ} ∷ rs) nf nf' eq ctx = appendκ↦ (↦⃗₂ {v = v} {κ}) nf (λ ()) refl ctx ∷ appendκ↦* rs (λ ()) nf' eq ctx
appendκ↦* (↦⃗₃ {v = v} {κ} ∷ rs) nf nf' eq ctx = appendκ↦ (↦⃗₃ {v = v} {κ}) nf (λ ()) refl ctx ∷ appendκ↦* rs (λ ()) nf' eq ctx
appendκ↦* (↦⃗₄ {x = x} {κ} ∷ rs) nf nf' eq ctx = appendκ↦ (↦⃗₄ {x = x} {κ}) nf (λ ()) refl ctx ∷ appendκ↦* rs (λ ()) nf' eq ctx
appendκ↦* (↦⃗₅ {y = y} {κ} ∷ rs) nf nf' eq ctx = appendκ↦ (↦⃗₅ {y = y} {κ}) nf (λ ()) refl ctx ∷ appendκ↦* rs (λ ()) nf' eq ctx
appendκ↦* (↦⃗₆ {κ = κ} ∷ rs) nf nf' eq ctx = appendκ↦ (↦⃗₆ {κ = κ}) nf (λ ()) refl ctx ∷ appendκ↦* rs (λ ()) nf' eq ctx
appendκ↦* (↦⃗₇ {κ = κ} ∷ rs) nf nf' eq ctx = appendκ↦ (↦⃗₇ {κ = κ}) nf (λ ()) refl ctx ∷ appendκ↦* rs (λ ()) nf' eq ctx
appendκ↦* (↦⃗₈ {κ = κ} ∷ rs) nf nf' eq ctx = appendκ↦ (↦⃗₈ {κ = κ}) nf (λ ()) refl ctx ∷ appendκ↦* rs (λ ()) nf' eq ctx
appendκ↦* (↦⃗₉ {κ = κ} ∷ rs) nf nf' eq ctx = appendκ↦ (↦⃗₉ {κ = κ}) nf (λ ()) refl ctx ∷ appendκ↦* rs (λ ()) nf' eq ctx
appendκ↦* (↦⃗₁₀ {κ = κ} ∷ rs) nf nf' eq ctx = appendκ↦ (↦⃗₁₀ {κ = κ}) nf (λ ()) refl ctx ∷ appendκ↦* rs (λ ()) nf' eq ctx
appendκ↦* (↦⃗₁₁ {κ = κ} ∷ rs) nf nf' eq ctx = appendκ↦ (↦⃗₁₁ {κ = κ}) nf (λ ()) refl ctx ∷ appendκ↦* rs (λ ()) nf' eq ctx
appendκ↦* (↦⃗₁₂ {κ = κ} ∷ rs) nf nf' eq ctx = appendκ↦ (↦⃗₁₂ {κ = κ}) nf (λ ()) refl ctx ∷ appendκ↦* rs (λ ()) nf' eq ctx
appendκ↦* (↦⃖₁ {b = b} {v} {κ} ∷ rs) nf nf' eq ctx = appendκ↦ (↦⃖₁ {b = b} {v} {κ}) nf (λ ()) refl ctx ∷ appendκ↦* rs (λ ()) nf' eq ctx
appendκ↦* (↦⃖₂ {v = v} {κ} ∷ rs) nf nf' eq ctx = appendκ↦ (↦⃖₂ {v = v} {κ}) nf (λ ()) refl ctx ∷ appendκ↦* rs (λ ()) nf' eq ctx
appendκ↦* (↦⃖₃ {v = v} {κ} ∷ rs) nf nf' eq ctx = appendκ↦ (↦⃖₃ {v = v} {κ}) nf (λ ()) refl ctx ∷ appendκ↦* rs (λ ()) nf' eq ctx
appendκ↦* (↦⃖₄ {x = x} {κ} ∷ rs) nf nf' eq ctx = appendκ↦ (↦⃖₄ {x = x} {κ}) nf (λ ()) refl ctx ∷ appendκ↦* rs (λ ()) nf' eq ctx
appendκ↦* (↦⃖₅ {y = y} {κ} ∷ rs) nf nf' eq ctx = appendκ↦ (↦⃖₅ {y = y} {κ}) nf (λ ()) refl ctx ∷ appendκ↦* rs (λ ()) nf' eq ctx
appendκ↦* (↦⃖₆ {κ = κ} ∷ rs) nf nf' eq ctx = appendκ↦ (↦⃖₆ {κ = κ}) nf (λ ()) refl ctx ∷ appendκ↦* rs (λ ()) nf' eq ctx
appendκ↦* (↦⃖₇ {κ = κ} ∷ rs) nf nf' eq ctx = appendκ↦ (↦⃖₇ {κ = κ}) nf (λ ()) refl ctx ∷ appendκ↦* rs (λ ()) nf' eq ctx
appendκ↦* (↦⃖₈ {κ = κ} ∷ rs) nf nf' eq ctx = appendκ↦ (↦⃖₈ {κ = κ}) nf (λ ()) refl ctx ∷ appendκ↦* rs (λ ()) nf' eq ctx
appendκ↦* (↦⃖₉ {κ = κ} ∷ rs) nf nf' eq ctx = appendκ↦ (↦⃖₉ {κ = κ}) nf (λ ()) refl ctx ∷ appendκ↦* rs (λ ()) nf' eq ctx
appendκ↦* (↦⃖₁₀ {κ = κ} ∷ rs) nf nf' eq ctx = appendκ↦ (↦⃖₁₀ {κ = κ}) nf (λ ()) refl ctx ∷ appendκ↦* rs (λ ()) nf' eq ctx
appendκ↦* (↦⃖₁₁ {κ = κ} ∷ rs) nf nf' eq ctx = appendκ↦ (↦⃖₁₁ {κ = κ}) nf (λ ()) refl ctx ∷ appendκ↦* rs (λ ()) nf' eq ctx
appendκ↦* (↦⃖₁₂ {κ = κ} ∷ rs) nf nf' eq ctx = appendκ↦ (↦⃖₁₂ {κ = κ}) nf (λ ()) refl ctx ∷ appendκ↦* rs (λ ()) nf' eq ctx
appendκ↦* (↦η₊₁ {κ = κ} ∷ rs) nf nf' eq ctx = appendκ↦ (↦η₊₁ {κ = κ}) nf (λ ()) refl ctx ∷ appendκ↦* rs (λ ()) nf' eq ctx
appendκ↦* (↦η₊₂ {κ = κ} ∷ rs) nf nf' eq ctx = appendκ↦ (↦η₊₂ {κ = κ}) nf (λ ()) refl ctx ∷ appendκ↦* rs (λ ()) nf' eq ctx
appendκ↦* (↦ε₊₁ {κ = κ} ∷ rs) nf nf' eq ctx = appendκ↦ (↦ε₊₁ {κ = κ}) nf (λ ()) refl ctx ∷ appendκ↦* rs (λ ()) nf' eq ctx
appendκ↦* (↦ε₊₂ {κ = κ} ∷ rs) nf nf' eq ctx = appendκ↦ (↦ε₊₂ {κ = κ}) nf (λ ()) refl ctx ∷ appendκ↦* rs (λ ()) nf' eq ctx
appendκ↦* (↦⃗ηₓ ∷ rs) nf nf' eq ctx = appendκ↦ ↦⃗ηₓ nf (λ ()) refl ctx ∷ appendκ↦* rs (λ ()) nf' eq ctx
appendκ↦* (↦⃖ηₓ₁ {eq = eq'} ∷ rs) nf nf' eq ctx = appendκ↦ (↦⃖ηₓ₁ {eq = eq'}) nf (λ ()) refl ctx ∷ appendκ↦* rs (λ ()) nf' eq ctx
appendκ↦* (↦⃖ηₓ₂ ∷ ◾) nf nf' eq ctx = ⊥-elim (nf' refl)
appendκ↦* (↦⃗εₓ₁ {eq = eq'} ∷ rs) nf nf' eq ctx = appendκ↦ (↦⃗εₓ₁ {eq = eq'}) nf (λ ()) refl ctx ∷ appendκ↦* rs (λ ()) nf' eq ctx
appendκ↦* (↦⃗εₓ₂ ∷ ◾) nf nf' eq ctx = ⊥-elim (nf' refl)
appendκ↦* (↦⃖εₓ ∷ rs) nf nf' eq ctx = appendκ↦ ↦⃖εₓ nf (λ ()) refl ctx ∷ appendκ↦* rs (λ ()) nf' eq ctx

appendκ↦⊠ : ∀ {st} → (r : st ↦ ⊠) (nf : st ≢ ⊠)
          → (κ : Context {proj₁ (get𝕌State st nf)} {proj₂ (get𝕌State st nf)})
          → appendκState st nf κ ↦ ⊠
appendκ↦⊠ (↦⃖ηₓ₂ {neq = neq}) nf ctx = ↦⃖ηₓ₂ {neq = neq}
appendκ↦⊠ (↦⃗εₓ₂ {neq = neq}) nf ctx = ↦⃗εₓ₂ {neq = neq}

appendκ↦*⊠ : ∀ {st} → (r : st ↦* ⊠) (nf : st ≢ ⊠)
           → (κ : Context {proj₁ (get𝕌State st nf)} {proj₂ (get𝕌State st nf)})
           → appendκState st nf κ ↦* ⊠
appendκ↦*⊠ ◾ nf ctx = ⊥-elim (nf refl)
appendκ↦*⊠ (r ∷ ◾) nf ctx = appendκ↦⊠ r nf ctx ∷ ◾
appendκ↦*⊠ (↦⃗₁ ∷ (r' ∷ rs)) nf ctx = ↦⃗₁ ∷ appendκ↦*⊠ (r' ∷ rs) (λ ()) ctx
appendκ↦*⊠ (↦⃗₂ ∷ (r' ∷ rs)) nf ctx = ↦⃗₂ ∷ appendκ↦*⊠ (r' ∷ rs) (λ ()) ctx
appendκ↦*⊠ (↦⃗₃ ∷ (r' ∷ rs)) nf ctx = ↦⃗₃ ∷ appendκ↦*⊠ (r' ∷ rs) (λ ()) ctx
appendκ↦*⊠ (↦⃗₄ ∷ (r' ∷ rs)) nf ctx = ↦⃗₄ ∷ appendκ↦*⊠ (r' ∷ rs) (λ ()) ctx
appendκ↦*⊠ (↦⃗₅ ∷ (r' ∷ rs)) nf ctx = ↦⃗₅ ∷ appendκ↦*⊠ (r' ∷ rs) (λ ()) ctx
appendκ↦*⊠ (↦⃗₆ ∷ (r' ∷ rs)) nf ctx = ↦⃗₆ ∷ appendκ↦*⊠ (r' ∷ rs) (λ ()) ctx
appendκ↦*⊠ (↦⃗₇ ∷ (r' ∷ rs)) nf ctx = ↦⃗₇ ∷ appendκ↦*⊠ (r' ∷ rs) (λ ()) ctx
appendκ↦*⊠ (↦⃗₈ ∷ (r' ∷ rs)) nf ctx = ↦⃗₈ ∷ appendκ↦*⊠ (r' ∷ rs) (λ ()) ctx
appendκ↦*⊠ (↦⃗₉ ∷ (r' ∷ rs)) nf ctx = ↦⃗₉ ∷ appendκ↦*⊠ (r' ∷ rs) (λ ()) ctx
appendκ↦*⊠ (↦⃗₁₀ ∷ (r' ∷ rs)) nf ctx = ↦⃗₁₀ ∷ appendκ↦*⊠ (r' ∷ rs) (λ ()) ctx
appendκ↦*⊠ (↦⃗₁₁ ∷ (r' ∷ rs)) nf ctx = ↦⃗₁₁ ∷ appendκ↦*⊠ (r' ∷ rs) (λ ()) ctx
appendκ↦*⊠ (↦⃗₁₂ ∷ (r' ∷ rs)) nf ctx = ↦⃗₁₂ ∷ appendκ↦*⊠ (r' ∷ rs) (λ ()) ctx
appendκ↦*⊠ (↦⃖₁ ∷ (r' ∷ rs)) nf ctx = ↦⃖₁ ∷ appendκ↦*⊠ (r' ∷ rs) (λ ()) ctx
appendκ↦*⊠ (↦⃖₂ ∷ (r' ∷ rs)) nf ctx = ↦⃖₂ ∷ appendκ↦*⊠ (r' ∷ rs) (λ ()) ctx
appendκ↦*⊠ (↦⃖₃ ∷ (r' ∷ rs)) nf ctx = ↦⃖₃ ∷ appendκ↦*⊠ (r' ∷ rs) (λ ()) ctx
appendκ↦*⊠ (↦⃖₄ ∷ (r' ∷ rs)) nf ctx = ↦⃖₄ ∷ appendκ↦*⊠ (r' ∷ rs) (λ ()) ctx
appendκ↦*⊠ (↦⃖₅ ∷ (r' ∷ rs)) nf ctx = ↦⃖₅ ∷ appendκ↦*⊠ (r' ∷ rs) (λ ()) ctx
appendκ↦*⊠ (↦⃖₆ ∷ (r' ∷ rs)) nf ctx = ↦⃖₆ ∷ appendκ↦*⊠ (r' ∷ rs) (λ ()) ctx
appendκ↦*⊠ (↦⃖₇ ∷ (r' ∷ rs)) nf ctx = ↦⃖₇ ∷ appendκ↦*⊠ (r' ∷ rs) (λ ()) ctx
appendκ↦*⊠ (↦⃖₈ ∷ (r' ∷ rs)) nf ctx = ↦⃖₈ ∷ appendκ↦*⊠ (r' ∷ rs) (λ ()) ctx
appendκ↦*⊠ (↦⃖₉ ∷ (r' ∷ rs)) nf ctx = ↦⃖₉ ∷ appendκ↦*⊠ (r' ∷ rs) (λ ()) ctx
appendκ↦*⊠ (↦⃖₁₀ ∷ (r' ∷ rs)) nf ctx = ↦⃖₁₀ ∷ appendκ↦*⊠ (r' ∷ rs) (λ ()) ctx
appendκ↦*⊠ (↦⃖₁₁ ∷ (r' ∷ rs)) nf ctx = ↦⃖₁₁ ∷ appendκ↦*⊠ (r' ∷ rs) (λ ()) ctx
appendκ↦*⊠ (↦⃖₁₂ ∷ (r' ∷ rs)) nf ctx = ↦⃖₁₂ ∷ appendκ↦*⊠ (r' ∷ rs) (λ ()) ctx
appendκ↦*⊠ (↦η₊₁ ∷ (r' ∷ rs)) nf ctx = ↦η₊₁ ∷ appendκ↦*⊠ (r' ∷ rs) (λ ()) ctx
appendκ↦*⊠ (↦η₊₂ ∷ (r' ∷ rs)) nf ctx = ↦η₊₂ ∷ appendκ↦*⊠ (r' ∷ rs) (λ ()) ctx
appendκ↦*⊠ (↦ε₊₁ ∷ (r' ∷ rs)) nf ctx = ↦ε₊₁ ∷ appendκ↦*⊠ (r' ∷ rs) (λ ()) ctx
appendκ↦*⊠ (↦ε₊₂ ∷ (r' ∷ rs)) nf ctx = ↦ε₊₂ ∷ appendκ↦*⊠ (r' ∷ rs) (λ ()) ctx
appendκ↦*⊠ (↦⃗ηₓ ∷ (r' ∷ rs)) nf ctx = ↦⃗ηₓ ∷ appendκ↦*⊠ (r' ∷ rs) (λ ()) ctx
appendκ↦*⊠ (↦⃖ηₓ₁ {eq = eq} ∷ (r' ∷ rs)) nf ctx = ↦⃖ηₓ₁ {eq = eq} ∷ appendκ↦*⊠ (r' ∷ rs) (λ ()) ctx
appendκ↦*⊠ (↦⃗εₓ₁ {eq = eq} ∷ (r' ∷ rs)) nf ctx = ↦⃗εₓ₁ {eq = eq} ∷ appendκ↦*⊠ (r' ∷ rs) (λ ()) ctx
appendκ↦*⊠ (↦⃖εₓ ∷ (r' ∷ rs)) nf ctx = ↦⃖εₓ ∷ appendκ↦*⊠ (r' ∷ rs) (λ ()) ctx


import      Lvl
open import Type

module Structure.Logic.Classical.NaturalDeduction {ℓₗ} {Formula : Type{ℓₗ}} {ℓₘₗ} (Proof : Formula → Type{ℓₘₗ}) where

open import Functional hiding (Domain)
import      Structure.Logic.Constructive.NaturalDeduction
private module Constructive = Structure.Logic.Constructive.NaturalDeduction(Proof)

-- TODO: Maybe it is worth to try and take a more minimal approach? (Less axioms? Is this more practical/impractical?)

module Propositional where
  open Constructive.Propositional hiding (Theory) public

  -- A theory of classical propositional logic expressed using natural deduction rules
  record Theory ⦃ sign : Signature ⦄ : Type{ℓₘₗ Lvl.⊔ ℓₗ} where
    open Signature(sign)

    field
      instance ⦃ constructive ⦄ : Constructive.Propositional.Theory ⦃ sign ⦄

    open Constructive.Propositional.Theory (constructive) public

    field
      excluded-middle : ∀{P} → Proof(P ∨ (¬ P))

module Domained = Constructive.Domained

module MultiSorted = Constructive.MultiSorted

  {-
  Propositional-from-[∧][∨][⊥] : ∀{ℓₗ} → (_∧_ _∨_ : Formula → Formula → Formula) → (⊥ : Formula) →
    ([∧]-intro : ∀{X Y} → X → Y → (X ∧ Y)) →
    ([∧]-elimₗ  : ∀{X Y} → (X ∧ Y) → X) →
    ([∧]-elimᵣ  : ∀{X Y} → (X ∧ Y) → Y) →
    ([∨]-introₗ : ∀{X Y} → X → (X ∨ Y)) →
    ([∨]-introᵣ : ∀{X Y} → Y → (X ∨ Y)) →
    ([∨]-elim  : ∀{X Y Z : Formula} → (X → Z) → (Y → Z) → (X ∨ Y) → Z) →
    ([⊥]-intro : ∀{X : Formula} → X → (X → ⊥) → ⊥) →
    ([⊥]-elim  : ∀{X : Formula} → ⊥ → X) →
    Propositional{ℓₗ}
  Propositional-from-[∧][∨][⊥]
    (_∧_) (_∨_) (⊥)
    ([∧]-intro)
    ([∧]-elimₗ)
    ([∧]-elimᵣ)
    ([∨]-introₗ)
    ([∨]-introᵣ)
    ([∨]-elim)
    ([⊥]-intro)
    ([⊥]-elim)
    = record{
      _∧_  = _∧_ ;
      _∨_  = _∨_ ;
      _⟶_ = (x ↦ y ↦ (x ∨ (¬ y))) ;
      _⟵_ = swap _⟶_ ;
      _⟷_ = (x ↦ y ↦ ((x ⟵ y)∧(x ⟶ y))) ;
      ¬_   = (x ↦ (x ⟶ ⊥)) ;
      ⊥    = ⊥ ;
      ⊤    = ¬ ⊥
    }
  -}

module _ {ℓₒ} (Domain : Type{ℓₒ}) where
  record ClassicalLogicSignature : Type{ℓₘₗ Lvl.⊔ (ℓₗ Lvl.⊔ ℓₒ)} where
    open Domained(Domain)

    field
      instance ⦃ propositionalSignature ⦄ : Propositional.Signature
      instance ⦃ predicateSignature ⦄     : Predicate.Signature
      instance ⦃ equalitySignature ⦄      : Equality.Signature

    constructiveLogicSignature : Constructive.ConstructiveLogicSignature(Domain)
    constructiveLogicSignature =
      record{
        propositionalSignature = propositionalSignature ;
        predicateSignature     = predicateSignature ;
        equalitySignature      = equalitySignature
      }

    open Propositional.Signature(propositionalSignature) public
    open Predicate.Signature(predicateSignature) public
    open Equality.Signature(equalitySignature) public
    open Equality.PropositionallyDerivedSignature ⦃ propositionalSignature ⦄ ⦃ equalitySignature ⦄ public
    open Uniqueness.Signature ⦃ propositionalSignature ⦄ ⦃ predicateSignature ⦄ ⦃ equalitySignature ⦄ public

  -- A theory of classical predicate/(first-order) logic expressed using natural deduction rules
  record ClassicalLogic : Type{ℓₘₗ Lvl.⊔ (ℓₗ Lvl.⊔ ℓₒ)} where
    open Domained(Domain)

    field
      instance ⦃ classicalLogicSignature ⦄ : ClassicalLogicSignature
    open ClassicalLogicSignature(classicalLogicSignature) public

    field
      instance ⦃ propositionalTheory ⦄ : Propositional.Theory ⦃ propositionalSignature ⦄
      instance ⦃ predicateTheory ⦄     : Predicate.Theory
      instance ⦃ equalityTheory ⦄      : Equality.Theory
      instance ⦃ existentialWitness ⦄  : Predicate.ExistentialWitness(∃ₗ)
      ⦃ nonEmptyDomain ⦄               : NonEmptyDomain

    constructivePropositionalTheory = Propositional.Theory.constructive(propositionalTheory)

    constructiveLogic : Constructive.ConstructiveLogic(Domain)
    constructiveLogic =
      record{
        constructiveLogicSignature = constructiveLogicSignature ;
        propositionalTheory        = constructivePropositionalTheory ;
        predicateTheory            = predicateTheory ;
        equalityTheory             = equalityTheory
      }

    open Propositional.Theory(propositionalTheory) public
    open Predicate.Theory(predicateTheory) public
    open Equality.Theory(equalityTheory) public
    open Predicate.ExistentialWitness(existentialWitness) public
    open Uniqueness.WitnessTheory ⦃ propositionalSignature ⦄ ⦃ predicateSignature ⦄ ⦃ equalitySignature ⦄ ⦃ constructivePropositionalTheory ⦄ ⦃ predicateTheory ⦄ ⦃ equalityTheory ⦄ ⦃ existentialWitness ⦄ public
    open NonEmptyDomain ⦃ nonEmptyDomain ⦄ public


  postulate
    F  : Set₂ → Set₃
    #_ : Set₁ → Set₂
    !_ : Set₀ → Set₁

  infix 1 F
  infix 2 #_
  infix 3 !_

  syntax F x = ! x

  ok₁ : Set₁ → Set₃
  ok₁ X = ! # X

  ok₂ : Set₀ → Set₂
  ok₂ X = # ! X


module _ where

record R : Set₁ where
  field x : Set

g : Set₁

module M (r : R) where
  open R r
  f = x

g = R


open import Agda.Builtin.Char
open import Agda.Builtin.Coinduction
open import Agda.Builtin.IO
open import Agda.Builtin.List
open import Agda.Builtin.Unit
open import Agda.Builtin.String

data Colist {a} (A : Set a) : Set a where
  []  : Colist A
  _∷_ : A → ∞ (Colist A) → Colist A

{-# FOREIGN GHC
  data Colist a    = Nil | Cons a (MAlonzo.RTE.Inf (Colist a))
  type Colist' l a = Colist a

  fromColist :: Colist a -> [a]
  fromColist Nil         = []
  fromColist (Cons x xs) = x : fromColist (MAlonzo.RTE.flat xs)
  #-}

{-# COMPILE GHC Colist = data Colist' (Nil | Cons) #-}

to-colist : ∀ {a} {A : Set a} → List A → Colist A
to-colist []       = []
to-colist (x ∷ xs) = x ∷ ♯ to-colist xs

a-definition-that-uses-♭ :
  ∀ {a} {A : Set a} → Colist A → Colist A
a-definition-that-uses-♭ []       = []
a-definition-that-uses-♭ (x ∷ xs) =
  x ∷ ♯ a-definition-that-uses-♭ (♭ xs)

postulate
  putStr : Colist Char → IO ⊤

{-# COMPILE GHC putStr = putStr . fromColist #-}

main : IO ⊤
main = putStr (a-definition-that-uses-♭
         (to-colist (primStringToList "apa\n")))


module Luau.Type where

open import FFI.Data.Maybe using (Maybe; just; nothing; just-inv)
open import Agda.Builtin.Equality using (_≡_; refl)
open import Properties.Dec using (Dec; yes; no)
open import Properties.Equality using (cong)
open import FFI.Data.Maybe using (Maybe; just; nothing)

data Type : Set where
  nil : Type
  _⇒_ : Type → Type → Type
  never : Type
  unknown : Type
  boolean : Type
  number : Type
  string : Type
  _∪_ : Type → Type → Type
  _∩_ : Type → Type → Type

data Scalar : Type → Set where

  number : Scalar number
  boolean : Scalar boolean
  string : Scalar string
  nil : Scalar nil

lhs : Type → Type
lhs (T ⇒ _) = T
lhs (T ∪ _) = T
lhs (T ∩ _) = T
lhs nil = nil
lhs never = never
lhs unknown = unknown
lhs number = number
lhs boolean = boolean
lhs string = string

rhs : Type → Type
rhs (_ ⇒ T) = T
rhs (_ ∪ T) = T
rhs (_ ∩ T) = T
rhs nil = nil
rhs never = never
rhs unknown = unknown
rhs number = number
rhs boolean = boolean
rhs string = string

_≡ᵀ_ : ∀ (T U : Type) → Dec(T ≡ U)
nil ≡ᵀ nil = yes refl
nil ≡ᵀ (S ⇒ T) = no (λ ())
nil ≡ᵀ never = no (λ ())
nil ≡ᵀ unknown = no (λ ())
nil ≡ᵀ number = no (λ ())
nil ≡ᵀ boolean = no (λ ())
nil ≡ᵀ (S ∪ T) = no (λ ())
nil ≡ᵀ (S ∩ T) = no (λ ())
nil ≡ᵀ string = no (λ ())
(S ⇒ T) ≡ᵀ string = no (λ ())
never ≡ᵀ string = no (λ ())
unknown ≡ᵀ string = no (λ ())
boolean ≡ᵀ string = no (λ ())
number ≡ᵀ string = no (λ ())
(S ∪ T) ≡ᵀ string = no (λ ())
(S ∩ T) ≡ᵀ string = no (λ ())
(S ⇒ T) ≡ᵀ nil = no (λ ())
(S ⇒ T) ≡ᵀ (U ⇒ V) with (S ≡ᵀ U) | (T ≡ᵀ V)
(S ⇒ T) ≡ᵀ (S ⇒ T) | yes refl | yes refl = yes refl
(S ⇒ T) ≡ᵀ (U ⇒ V) | _ | no p = no (λ q → p (cong rhs q))
(S ⇒ T) ≡ᵀ (U ⇒ V) | no p | _ = no (λ q → p (cong lhs q))
(S ⇒ T) ≡ᵀ never = no (λ ())
(S ⇒ T) ≡ᵀ unknown = no (λ ())
(S ⇒ T) ≡ᵀ number = no (λ ())
(S ⇒ T) ≡ᵀ boolean = no (λ ())
(S ⇒ T) ≡ᵀ (U ∪ V) = no (λ ())
(S ⇒ T) ≡ᵀ (U ∩ V) = no (λ ())
never ≡ᵀ nil = no (λ ())
never ≡ᵀ (U ⇒ V) = no (λ ())
never ≡ᵀ never = yes refl
never ≡ᵀ unknown = no (λ ())
never ≡ᵀ number = no (λ ())
never ≡ᵀ boolean = no (λ ())
never ≡ᵀ (U ∪ V) = no (λ ())
never ≡ᵀ (U ∩ V) = no (λ ())
unknown ≡ᵀ nil = no (λ ())
unknown ≡ᵀ (U ⇒ V) = no (λ ())
unknown ≡ᵀ never = no (λ ())
unknown ≡ᵀ unknown = yes refl
unknown ≡ᵀ number = no (λ ())
unknown ≡ᵀ boolean = no (λ ())
unknown ≡ᵀ (U ∪ V) = no (λ ())
unknown ≡ᵀ (U ∩ V) = no (λ ())
number ≡ᵀ nil = no (λ ())
number ≡ᵀ (T ⇒ U) = no (λ ())
number ≡ᵀ never = no (λ ())
number ≡ᵀ unknown = no (λ ())
number ≡ᵀ number = yes refl
number ≡ᵀ boolean = no (λ ())
number ≡ᵀ (T ∪ U) = no (λ ())
number ≡ᵀ (T ∩ U) = no (λ ())
boolean ≡ᵀ nil = no (λ ())
boolean ≡ᵀ (T ⇒ U) = no (λ ())
boolean ≡ᵀ never = no (λ ())
boolean ≡ᵀ unknown = no (λ ())
boolean ≡ᵀ boolean = yes refl
boolean ≡ᵀ number = no (λ ())
boolean ≡ᵀ (T ∪ U) = no (λ ())
boolean ≡ᵀ (T ∩ U) = no (λ ())
string ≡ᵀ nil = no (λ ())
string ≡ᵀ (x ⇒ x₁) = no (λ ())
string ≡ᵀ never = no (λ ())
string ≡ᵀ unknown = no (λ ())
string ≡ᵀ boolean = no (λ ())
string ≡ᵀ number = no (λ ())
string ≡ᵀ string = yes refl
string ≡ᵀ (U ∪ V) = no (λ ())
string ≡ᵀ (U ∩ V) = no (λ ())
(S ∪ T) ≡ᵀ nil = no (λ ())
(S ∪ T) ≡ᵀ (U ⇒ V) = no (λ ())
(S ∪ T) ≡ᵀ never = no (λ ())
(S ∪ T) ≡ᵀ unknown = no (λ ())
(S ∪ T) ≡ᵀ number = no (λ ())
(S ∪ T) ≡ᵀ boolean = no (λ ())
(S ∪ T) ≡ᵀ (U ∪ V) with (S ≡ᵀ U) | (T ≡ᵀ V)
(S ∪ T) ≡ᵀ (S ∪ T) | yes refl | yes refl = yes refl
(S ∪ T) ≡ᵀ (U ∪ V) | _ | no p = no (λ q → p (cong rhs q))
(S ∪ T) ≡ᵀ (U ∪ V) | no p | _ = no (λ q → p (cong lhs q))
(S ∪ T) ≡ᵀ (U ∩ V) = no (λ ())
(S ∩ T) ≡ᵀ nil = no (λ ())
(S ∩ T) ≡ᵀ (U ⇒ V) = no (λ ())
(S ∩ T) ≡ᵀ never = no (λ ())
(S ∩ T) ≡ᵀ unknown = no (λ ())
(S ∩ T) ≡ᵀ number = no (λ ())
(S ∩ T) ≡ᵀ boolean = no (λ ())
(S ∩ T) ≡ᵀ (U ∪ V) = no (λ ())
(S ∩ T) ≡ᵀ (U ∩ V) with (S ≡ᵀ U) | (T ≡ᵀ V)
(S ∩ T) ≡ᵀ (U ∩ V) | yes refl | yes refl = yes refl
(S ∩ T) ≡ᵀ (U ∩ V) | _ | no p = no (λ q → p (cong rhs q))
(S ∩ T) ≡ᵀ (U ∩ V) | no p | _ = no (λ q → p (cong lhs q))

_≡ᴹᵀ_ : ∀ (T U : Maybe Type) → Dec(T ≡ U)
nothing ≡ᴹᵀ nothing = yes refl
nothing ≡ᴹᵀ just U = no (λ ())
just T ≡ᴹᵀ nothing = no (λ ())
just T ≡ᴹᵀ just U with T ≡ᵀ U
(just T ≡ᴹᵀ just T) | yes refl = yes refl
(just T ≡ᴹᵀ just U) | no p = no (λ q → p (just-inv q))

src : Type → Type
src nil = never
src number = never
src boolean = never
src string = never
src (S ⇒ T) = S
src (S ∪ T) = (src S) ∩ (src T)
src (S ∩ T) = (src S) ∪ (src T)
src never = unknown
src unknown = never

tgt : Type → Type
tgt nil = never
tgt (S ⇒ T) = T
tgt never = never
tgt unknown = unknown
tgt number = never
tgt boolean = never
tgt string = never
tgt (S ∪ T) = (tgt S) ∪ (tgt T)
tgt (S ∩ T) = (tgt S) ∩ (tgt T)

optional : Type → Type
optional nil = nil
optional (T ∪ nil) = (T ∪ nil)
optional T = (T ∪ nil)

normalizeOptional : Type → Type
normalizeOptional (S ∪ T) with normalizeOptional S | normalizeOptional T
normalizeOptional (S ∪ T) | (S′ ∪ nil) | (T′ ∪ nil) = (S′ ∪ T′) ∪ nil
normalizeOptional (S ∪ T) | S′         | (T′ ∪ nil) = (S′ ∪ T′) ∪ nil
normalizeOptional (S ∪ T) | (S′ ∪ nil) | T′         = (S′ ∪ T′) ∪ nil
normalizeOptional (S ∪ T) | S′         | nil        = optional S′
normalizeOptional (S ∪ T) | nil        | T′         = optional T′
normalizeOptional (S ∪ T) | S′         | T′         = S′ ∪ T′
normalizeOptional T = T


module CompleteSemantics.Iemhoff where

open import Syntax public

-- Introspective brilliant Kripke models.

record Model : Set₁ where
  infix 3 _⊩ᵅ_
  field
    World   : Set
    _≤_     : World → World → Set
    refl≤   : ∀ {w} → w ≤ w
    trans≤  : ∀ {w w′ w″} → w ≤ w′ → w′ ≤ w″ → w ≤ w″
    _R_     : World → World → Set
    reflR   : ∀ {w} → w R w
    transR  : ∀ {w w′ w″} → w R w′ → w′ R w″ → w R w″
    _⊩ᵅ_   : World → Atom → Set
    mono⊩ᵅ : ∀ {w w′ P} → w ≤ w′ → w ⊩ᵅ P → w′ ⊩ᵅ P

  -- Introspection.
  field
    peek : World → Context

  _R⨾≤_ : World → World → Set
  _R⨾≤_ = _R_ ⨾ _≤_

  -- Brilliance.
  field
    R⨾≤→R : ∀ {w v′} → w R⨾≤ v′ → w R v′

  -- Vindication, as a consequence of brilliance.
  ≤→R : ∀ {w v′} → w ≤ v′ → w R v′
  ≤→R {w} ψ = R⨾≤→R (w , (reflR , ψ))

open Model {{…}} public

-- Forcing in a particular world of a particular model.

module _ {{_ : Model}} where
  mutual
    infix 3 _⊪_
    _⊪_ : World → Type → Set
    w ⊪ α P    = w ⊩ᵅ P
    w ⊪ A ⇒ B = ∀ {w′} → w ≤ w′ → w′ ⊩ A → w′ ⊩ B
    w ⊪ □ A    = ∀ {v′} → w R v′ → ∅ ⁏ π₂ (peek v′) ⊢ A ∧ v′ ⊩ A
    w ⊪ A ⩕ B  = w ⊩ A ∧ w ⊩ B
    w ⊪ ⫪     = ⊤
    w ⊪ ⫫     = ⊥
    w ⊪ A ⩖ B  = w ⊩ A ∨ w ⊩ B

    infix 3 _⊩_
    _⊩_ : World → Type → Set
    w ⊩ A = ∀ {C w′} → w ≤ w′ →
             (∀ {w″} → w′ ≤ w″ → w″ ⊪ A → peek w″ ⊢ⁿᶠ C) →
             peek w′ ⊢ⁿᶠ C

  infix 3 _⊩⋆_
  _⊩⋆_ : World → Stack Type → Set
  w ⊩⋆ ∅     = ⊤
  w ⊩⋆ Ξ , A = w ⊩⋆ Ξ ∧ w ⊩ A

-- Monotonicity of forcing with respect to constructive accessibility.

module _ {{_ : Model}} where
  mutual
    mono⊪ : ∀ {A w w′} → w ≤ w′ → w ⊪ A → w′ ⊪ A
    mono⊪ {α P}    ψ s       = mono⊩ᵅ ψ s
    mono⊪ {A ⇒ B} ψ f       = λ ψ′ a → f (trans≤ ψ ψ′) a
    mono⊪ {□ A}    ψ f       = λ ρ → f (transR (≤→R ψ) ρ)
    mono⊪ {A ⩕ B}  ψ (a , b) = mono⊩ {A} ψ a , mono⊩ {B} ψ b
    mono⊪ {⫪}     ψ ∙       = ∙
    mono⊪ {⫫}     ψ ()
    mono⊪ {A ⩖ B}  ψ (ι₁ a)  = ι₁ (mono⊩ {A} ψ a)
    mono⊪ {A ⩖ B}  ψ (ι₂ b)  = ι₂ (mono⊩ {B} ψ b)

    mono⊩ : ∀ {A w w′} → w ≤ w′ → w ⊩ A → w′ ⊩ A
    mono⊩ ψ a = λ ψ′ κ → a (trans≤ ψ ψ′) κ

  mono⊩⋆ : ∀ {Ξ w w′} → w ≤ w′ → w ⊩⋆ Ξ → w′ ⊩⋆ Ξ
  mono⊩⋆ {∅}     ψ ∙       = ∙
  mono⊩⋆ {Ξ , A} ψ (ξ , s) = mono⊩⋆ {Ξ} ψ ξ , mono⊩ {A} ψ s

-- Continuation-passing equipment.

module _ {{_ : Model}} where
  return : ∀ {A w} → w ⊪ A → w ⊩ A
  return {A} a = λ ψ κ → κ refl≤ (mono⊪ {A} ψ a)

  bind : ∀ {A B w} → w ⊩ A →
         (∀ {w′} → w ≤ w′ → w′ ⊪ A → w′ ⊩ B) →
         w ⊩ B
  bind a κ = λ ψ κ′ → a ψ
               λ ψ′ a′ → κ (trans≤ ψ ψ′) a′ refl≤
                 λ ψ″ a″ → κ′ (trans≤ ψ′ ψ″) a″

-- Additional equipment.

module _ {{_ : Model}} where
  lookup : ∀ {Ξ A w} → A ∈ Ξ → w ⊩⋆ Ξ → w ⊩ A
  lookup top     (ξ , s) = s
  lookup (pop i) (ξ , s) = lookup i ξ

-- Forcing in all worlds of all models, or semantic entailment.

infix 3 _⊨_
_⊨_ : Context → Type → Set₁
Γ ⁏ Δ ⊨ A = ∀ {{_ : Model}} {w} →
             w ⊩⋆ Γ →
             (∀ {v′} → w R v′ → ∅ ⁏ π₂ (peek v′) ⊢⋆ Δ) →
             (∀ {v′} → w R v′ → v′ ⊩⋆ Δ) →
             w ⊩ A

-- Soundness of the semantics with respect to the syntax.

reflect : ∀ {Γ Δ A} → Γ ⁏ Δ ⊢ A → Γ ⁏ Δ ⊨ A
reflect (var i)                  γ τ δ = lookup i γ
reflect (mvar i)                 γ τ δ = lookup i (δ reflR)
reflect (lam {A} {B} d)          γ τ δ = return {A ⇒ B}
                                           λ ψ a → reflect d (mono⊩⋆ ψ γ , a)
                                                              (λ ρ → τ (transR (≤→R ψ) ρ))
                                                              (λ ρ → δ (transR (≤→R ψ) ρ))
reflect (app {A} {B} d e)        γ τ δ = bind {A ⇒ B} {B} (reflect d γ τ δ)
                                           λ ψ f → f refl≤ (mono⊩ {A} ψ (reflect e γ τ δ))
reflect (box {A} d)              γ τ δ = return {□ A}
                                           λ ρ → graft⊢ ∙ (τ ρ) d ,
                                                  reflect d ∙
                                                            (λ ρ′ → τ (transR ρ ρ′))
                                                            (λ ρ′ → δ (transR ρ ρ′))
reflect (unbox {A} {C} d e)      γ τ δ = bind {□ A} {C} (reflect d γ τ δ)
                                           λ ψ s → reflect e (mono⊩⋆ ψ γ)
                                                              (λ ρ → τ (transR (≤→R ψ) ρ) , π₁ (s ρ))
                                                              (λ ρ → δ (transR (≤→R ψ) ρ) , π₂ (s ρ))
reflect (pair {A} {B} d e)       γ τ δ = return {A ⩕ B} (reflect d γ τ δ , reflect e γ τ δ)
reflect (fst {A} {B} d)          γ τ δ = bind {A ⩕ B} {A} (reflect d γ τ δ)
                                           λ { ψ (a , b) → a }
reflect (snd {A} {B} d)          γ τ δ = bind {A ⩕ B} {B} (reflect d γ τ δ)
                                           λ { ψ (a , b) → b }
reflect unit                     γ τ δ = return {⫪} ∙
reflect (boom {C} d)             γ τ δ = bind {⫫} {C} (reflect d γ τ δ)
                                           λ ψ → elim⊥
reflect (left {A} {B} d)         γ τ δ = return {A ⩖ B} (ι₁ (reflect d γ τ δ))
reflect (right {A} {B} d)        γ τ δ = return {A ⩖ B} (ι₂ (reflect d γ τ δ))
reflect (case {A} {B} {C} d e f) γ τ δ = bind {A ⩖ B} {C} (reflect d γ τ δ)
                                           λ { ψ (ι₁ a) → reflect e (mono⊩⋆ ψ γ , a)
                                                                     (λ ρ → τ (transR (≤→R ψ) ρ))
                                                                     (λ ρ → δ (transR (≤→R ψ) ρ))
                                             ; ψ (ι₂ b) → reflect f (mono⊩⋆ ψ γ , b)
                                                                     (λ ρ → τ (transR (≤→R ψ) ρ))
                                                                     (λ ρ → δ (transR (≤→R ψ) ρ))
                                             }

-- The canonical model.

private
  instance
    canon : Model
    canon = record
      { World   = Context
      ; _≤_     = _⊆²_
      ; refl≤   = refl⊆²
      ; trans≤  = trans⊆²
      ; _R_     = λ { (_ ⁏ Δ) (_ ⁏ Δ′) → Δ ⊆ Δ′ }
      ; reflR   = refl⊆
      ; transR  = trans⊆
      ; _⊩ᵅ_   = λ { (Γ ⁏ Δ) P → Γ ⁏ Δ ⊢ⁿᵉ α P }
      ; mono⊩ᵅ = mono⊢ⁿᵉ
      ; peek    = id
      ; R⨾≤→R  = λ { (_ , (ρ , (_ , ρ′))) → trans⊆ ρ ρ′ }
      }

-- Soundness and completeness of the canonical model with respect to the syntax.

mutual
  reflectᶜ : ∀ {A Γ Δ} → Γ ⁏ Δ ⊢ⁿᵉ A → Γ ⁏ Δ ⊩ A
  reflectᶜ {α P}    d = return {α P} d
  reflectᶜ {A ⇒ B} d = return {A ⇒ B}
                          λ ψ a → reflectᶜ (appⁿᵉ (mono⊢ⁿᵉ ψ d) (reifyᶜ a))
  reflectᶜ {□ A}    d = λ ψ κ → neⁿᶠ (unboxⁿᵉ (mono⊢ⁿᵉ ψ d)
                                               (κ (refl⊆ , weak⊆)
                                                  λ ρ′ → mono⊢ (bot , ρ′) mv₀ ,
                                                          reflectᶜ (mono⊢ⁿᵉ (bot , ρ′) mv₀ⁿᵉ)))
  reflectᶜ {A ⩕ B}  d = return {A ⩕ B} (reflectᶜ (fstⁿᵉ d) , reflectᶜ (sndⁿᵉ d))
  reflectᶜ {⫪}     d = return {⫪} ∙
  reflectᶜ {⫫}     d = λ ψ κ → neⁿᶠ (boomⁿᵉ (mono⊢ⁿᵉ ψ d))
  reflectᶜ {A ⩖ B}  d = λ ψ κ → neⁿᶠ (caseⁿᵉ (mono⊢ⁿᵉ ψ d)
                                              (κ (weak⊆ , refl⊆) (ι₁ (reflectᶜ v₀ⁿᵉ)))
                                              (κ (weak⊆ , refl⊆) (ι₂ (reflectᶜ v₀ⁿᵉ))))

  reifyᶜ : ∀ {A Γ Δ} → Γ ⁏ Δ ⊩ A → Γ ⁏ Δ ⊢ⁿᶠ A
  reifyᶜ {α P}    κ = κ refl⊆²
                        λ ψ s → neⁿᶠ s
  reifyᶜ {A ⇒ B} κ = κ refl⊆²
                        λ ψ f → lamⁿᶠ (reifyᶜ (f (weak⊆ , refl⊆) (reflectᶜ v₀ⁿᵉ)))
  reifyᶜ {□ A}    κ = κ refl⊆²
                        λ ψ f → boxⁿᶠ (π₁ (f {∅ ⁏ _} refl⊆))
  reifyᶜ {A ⩕ B}  κ = κ refl⊆²
                        λ { ψ (a , b) → pairⁿᶠ (reifyᶜ a) (reifyᶜ b) }
  reifyᶜ {⫪}     κ = κ refl⊆²
                        λ { ψ ∙ → unitⁿᶠ }
  reifyᶜ {⫫}     κ = κ refl⊆²
                        λ ψ ()
  reifyᶜ {A ⩖ B}  κ = κ refl⊆²
                        λ { ψ (ι₁ a) → leftⁿᶠ (reifyᶜ a)
                          ; ψ (ι₂ b) → rightⁿᶠ (reifyᶜ b)
                          }

-- Reflexivity of simultaneous forcing.

refl⊩⋆ : ∀ {Γ Δ} → Γ ⁏ Δ ⊩⋆ Γ
refl⊩⋆ {∅}     = ∙
refl⊩⋆ {Γ , A} = mono⊩⋆ (weak⊆ , refl⊆) refl⊩⋆ , reflectᶜ v₀ⁿᵉ

mrefl⊩⋆ : ∀ {Δ Γ} → Γ ⁏ Δ ⊩⋆ Δ
mrefl⊩⋆ {∅}     = ∙
mrefl⊩⋆ {Δ , A} = mono⊩⋆ (refl⊆ , weak⊆) mrefl⊩⋆ , reflectᶜ mv₀ⁿᵉ

-- Completeness of the semantics with respect to the syntax.

reify : ∀ {Γ Δ A} → Γ ⁏ Δ ⊨ A → Γ ⁏ Δ ⊢ⁿᶠ A
reify s = reifyᶜ (s refl⊩⋆ (λ ρ → mono⊢⋆ (refl⊆ , ρ) mrefl⊢⋆)
                            (λ ρ → mono⊩⋆ (refl⊆ , ρ) mrefl⊩⋆))

-- Normalisation by evaluation.

nbe : ∀ {Γ Δ A} → Γ ⁏ Δ ⊢ A → Γ ⁏ Δ ⊢ⁿᶠ A
nbe = reify ∘ reflect


-- Andreas, 2012-10-30 Sections
-- Reported by guillaume.brunerie, Oct 24 2012
module Issue735 where

import Common.Level
open import Common.Prelude using (Nat; zero; suc)

{- I often want to use something like sections in Coq. I usually use
parametrized modules but this has at least two issues that Coq does
not have:

1) I need to give a name to the module, and there is often no
   meaningful name to be given

2) It exports the module, so I cannot define a module in another file
   with the same name

I guess I can workaround 2) by declaring the module private, but this
will mean that everything will be indented further (again) and I
really want to avoid that.

I think this can easily be fixed by adding a kind of anonymous
modules.  An anonymous module would be declared by naming it `_`, and
the following code

  module _ params where
    declarations

would be translated to:

  private
    module Fresh params where
      declarations

  open Fresh public

where Fresh is a new name. -}

-- Andreas: this translation is not necessary, because Agda
-- has a concept of sections internally (in Abstract syntax).

-- However, the translation and the actual implementation should
-- have the same semantics, except that a name 'Fresh' is never generated.

-- An empty section with no parameters

module _ where

-- A section with a non-hidden parameter

module _ {a} (A : Set a) where

  data List : Set a where
    []  : List
    _∷_ : (x : A) (xs : List) → List

-- A section with all hidden parameters

module _ {a} {A : Set a} where

  _++_ : List A → List A → List A
  []       ++ ys = ys
  (x ∷ xs) ++ ys = x ∷ (xs ++ ys)

  module _ (default : A) where

    head : List A → A
    head  []     = default
    head (x ∷ _) = x

test : List Nat
test = head 1 [] ∷ ((2 ∷ []) ++ (3 ∷ []))


-- Andreas, 2012-09-17 documenting an internal error in InternalToAbstract.hs
-- {-# OPTIONS -v syntax.reify.con:30 -v tc.with.type:30 #-}
module NameFirstIfHidden where

Total : (D : (A : Set) → A → Set)(A : Set) → Set
Total D A = forall a → D A a

data D (A : Set) : (a : A) → Set where
  c : Total D A

postulate P : {A : Set}{a : A} → D A a → Set

f : (A : Set)(a : A) → P (c a) → D A a
f A a p with Total
... | _ = c a
-- should succeed, triggered an internal error before


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

open import HoTT
open import cohomology.Theory
open import homotopy.PushoutSplit
open import homotopy.DisjointlyPointedSet
open import cw.CW

module cw.cohomology.WedgeOfCells {i} (OT : OrdinaryTheory i)
  {n} (⊙skel : ⊙Skeleton {i} (S n)) where

open OrdinaryTheory OT
open import cohomology.Sphere OT

Xₙ/Xₙ₋₁ : Ptd i
Xₙ/Xₙ₋₁ = ⊙Cofiber (⊙cw-incl-last ⊙skel)

module _ (m : ℤ) where
  CXₙ/Xₙ₋₁ : Group i
  CXₙ/Xₙ₋₁ = C m Xₙ/Xₙ₋₁

  CEl-Xₙ/Xₙ₋₁ : Type i
  CEl-Xₙ/Xₙ₋₁ = Group.El CXₙ/Xₙ₋₁

  abstract
    CXₙ/Xₙ₋₁-is-abelian : is-abelian CXₙ/Xₙ₋₁
    CXₙ/Xₙ₋₁-is-abelian = C-is-abelian m Xₙ/Xₙ₋₁

  CXₙ/Xₙ₋₁-abgroup : AbGroup i
  CXₙ/Xₙ₋₁-abgroup = CXₙ/Xₙ₋₁ , CXₙ/Xₙ₋₁-is-abelian

{- the equivalence is in the opposite direction because
   cohomology functors are contravariant. -}
BigWedge-⊙equiv-Xₙ/Xₙ₋₁ : ⊙BigWedge {A = ⊙cells-last ⊙skel} (λ _ → ⊙Sphere (S n)) ⊙≃ Xₙ/Xₙ₋₁
BigWedge-⊙equiv-Xₙ/Xₙ₋₁ = ≃-to-⊙≃
  (PS.split-equiv ∘e equiv to from to-from from-to) idp
  where
    open AttachedSkeleton (⊙Skeleton.skel ⊙skel)

    module PS = PushoutLSplit (uncurry attaching) (λ _ → tt) fst

    module SphereToCofiber (a : fst cells) = SuspRec {A = Sphere n}
      (cfbase' (fst :> (fst cells × Sphere n → fst cells)))
      (cfcod a) (λ s → cfglue (a , s))

    module To = BigWedgeRec {A = fst cells} {X = λ _ → ⊙Sphere (S n)}
      cfbase SphereToCofiber.f (λ _ → idp)
    to = To.f

    module From = CofiberRec {f = fst :> (fst cells × Sphere n → fst cells)}
      (bwbase {A = fst cells} {X = λ _ → ⊙Sphere (S n)})
      (λ a → bwin a south) (λ{(a , s) → bwglue a ∙ ap (bwin a) (merid s)})
    from = From.f

    abstract
      from-to : ∀ b → from (to b) == b
      from-to = BigWedge-elim
        idp
        (λ a → Susp-elim (bwglue a) idp
          (λ s → ↓-='-from-square $
            ( ap-∘ from (SphereToCofiber.f a) (merid s)
            ∙ ap (ap from) (SphereToCofiber.merid-β a s)
            ∙ From.glue-β (a , s))
            ∙v⊡ (tl-square (bwglue a) ⊡h vid-square)))
        (λ a → ↓-∘=idf-from-square from to $
          ap (ap from) (To.glue-β a) ∙v⊡ br-square (bwglue a))

      to-from : ∀ c → to (from c) == c
      to-from = Cofiber-elim
        idp (λ a → idp)
        (λ{(a , s) → ↓-∘=idf-in' to from $
            ap (ap to) (From.glue-β (a , s))
          ∙ ap-∙ to (bwglue a) (ap (bwin a) (merid s))
          ∙ ap2 _∙_ (To.glue-β a)
                    ( ∘-ap to (bwin a) (merid s)
                    ∙ SphereToCofiber.merid-β a s)})

CXₙ/Xₙ₋₁-β : ∀ m → ⊙has-cells-with-choice 0 ⊙skel i
  → C m Xₙ/Xₙ₋₁ ≃ᴳ Πᴳ (⊙cells-last ⊙skel) (λ _ → C m (⊙Lift (⊙Sphere (S n))))
CXₙ/Xₙ₋₁-β m ac = C-additive-iso m (λ _ → ⊙Lift (⊙Sphere (S n))) (⊙cells-last-has-choice ⊙skel ac)
              ∘eᴳ C-emap m ( BigWedge-⊙equiv-Xₙ/Xₙ₋₁
                         ⊙∘e ⊙BigWedge-emap-r (λ _ → ⊙lower-equiv))

CXₙ/Xₙ₋₁-β-diag : ⊙has-cells-with-choice 0 ⊙skel i
  → CXₙ/Xₙ₋₁ (ℕ-to-ℤ (S n)) ≃ᴳ Πᴳ (⊙cells-last ⊙skel) (λ _ → C2 0)
CXₙ/Xₙ₋₁-β-diag ac = Πᴳ-emap-r (⊙cells-last ⊙skel) (λ _ → C-Sphere-diag (S n))
                 ∘eᴳ CXₙ/Xₙ₋₁-β (ℕ-to-ℤ (S n)) ac

abstract
  CXₙ/Xₙ₋₁-≠-is-trivial : ∀ {m} (m≠Sn : m ≠ ℕ-to-ℤ (S n))
    → ⊙has-cells-with-choice 0 ⊙skel i
    → is-trivialᴳ (CXₙ/Xₙ₋₁ m)
  CXₙ/Xₙ₋₁-≠-is-trivial {m} m≠Sn ac =
    iso-preserves'-trivial (CXₙ/Xₙ₋₁-β m ac) $
      Πᴳ-is-trivial (⊙cells-last ⊙skel)
        (λ _ → C m (⊙Lift (⊙Sphere (S n))))
        (λ _ → C-Sphere-≠-is-trivial m (S n) m≠Sn)

  CXₙ/Xₙ₋₁-<-is-trivial : ∀ {m} (m<Sn : m < S n)
    → ⊙has-cells-with-choice 0 ⊙skel i
    → is-trivialᴳ (CXₙ/Xₙ₋₁ (ℕ-to-ℤ m))
  CXₙ/Xₙ₋₁-<-is-trivial m<Sn = CXₙ/Xₙ₋₁-≠-is-trivial (ℕ-to-ℤ-≠ (<-to-≠ m<Sn))

  CXₙ/Xₙ₋₁->-is-trivial : ∀ {m} (m>Sn : S n < m)
    → ⊙has-cells-with-choice 0 ⊙skel i
    → is-trivialᴳ (CXₙ/Xₙ₋₁ (ℕ-to-ℤ m))
  CXₙ/Xₙ₋₁->-is-trivial m>Sn = CXₙ/Xₙ₋₁-≠-is-trivial (≠-inv (ℕ-to-ℤ-≠ (<-to-≠ m>Sn)))


------------------------------------------------------------------------
-- Some results related to strong bisimilarity for the delay monad
------------------------------------------------------------------------

{-# OPTIONS --sized-types #-}

open import Prelude

module Bisimilarity.Delay-monad {a} {A : Type a} where

open import Delay-monad
open import Delay-monad.Bisimilarity as D using (force)
open import Equality.Propositional
open import Logical-equivalence using (_⇔_)

open import Function-universe equality-with-J hiding (id; _∘_)

open import Labelled-transition-system
open import Labelled-transition-system.Delay-monad A

open import Bisimilarity delay-monad
import Bisimilarity.Equational-reasoning-instances
open import Equational-reasoning

------------------------------------------------------------------------
-- The direct and the indirect definitions of strong bisimilarity are
-- pointwise logically equivalent

-- An emulation of the constructor D.later.

later-cong : ∀ {i x y} →
             [ i ] force x ∼′ force y → [ i ] later x ∼ later y
later-cong x∼′y =
  ⟨ (λ { later → _ , later , x∼′y })
  , (λ { later → _ , later , x∼′y })
  ⟩

-- The direct definition of strong bisimilarity is contained in the
-- one obtained from the transition relation.

direct→indirect : ∀ {i x y} →
                  D.[ i ] x ∼ y → [ i ] x ∼ y
direct→indirect D.now       = reflexive
direct→indirect (D.later p) = later-cong λ { .force →
                                direct→indirect (force p) }

-- The definition of strong bisimilarity obtained from the transition
-- relation is contained in the direct one.

indirect→direct : ∀ {i} x y → [ i ] x ∼ y → D.[ i ] x ∼ y
indirect→direct (now x) (now y) nx∼ny
  with left-to-right nx∼ny now
... | _ , now , _ = D.now

indirect→direct (later x) (later y) lx∼ly
  with left-to-right lx∼ly later
... | _ , later , x∼′y  = D.later λ { .force →
                            indirect→direct _ _ (force x∼′y) }

indirect→direct (now x) (later y) nx∼ly
  with left-to-right nx∼ly now
... | _ , () , _

indirect→direct (later x) (now y) lx∼ny
  with left-to-right lx∼ny later
... | _ , () , _

-- The direct definition of strong bisimilarity is pointwise logically
-- equivalent to the one obtained from the transition relation.
--
-- TODO: Are the two definitions isomorphic?

direct⇔indirect : ∀ {i x y} → D.[ i ] x ∼ y ⇔ [ i ] x ∼ y
direct⇔indirect = record
  { to   = direct→indirect
  ; from = indirect→direct _ _
  }


module Structure.Topology where

open import Logic
import      Lvl
open import Sets.ExtensionalPredicateSet renaming (_≡_ to _≡ₛ_) hiding (map)
open import Structure.Setoid
open import Type

-- Definition of topological spaces via open sets.
-- The interpretation is that X is the collection of points and 𝓣 is the collection of open sets of X.
-- (X,𝓣) is called a topological space.
-- 𝓣 is called a topology on X.
record TopologicalSpace {ℓ₁ ℓ₂ ℓ₃} {X : Type{ℓ₁}} ⦃ equiv : Equiv{ℓ₁ Lvl.⊔ ℓ₃}(X) ⦄ (𝓣 : PredSet{ℓ₂}(PredSet{ℓ₁ Lvl.⊔ ℓ₃}(X))) : Type{Lvl.𝐒(Lvl.of(X)) Lvl.⊔ Lvl.of(Type.of(𝓣))} where
  field
    contains-empty        : (∅ ∈ 𝓣)
    contains-universe     : (𝐔 ∈ 𝓣)
    intersection-closure  : ∀{A B} → (A ∈ 𝓣) → (B ∈ 𝓣) → ((A ∩ B) ∈ 𝓣)
    indexed-union-closure : ∀{I : Type{ℓ₁ Lvl.⊔ ℓ₃}}{Ai : I → PredSet{ℓ₁ Lvl.⊔ ℓ₃}(X)} → (∀{i} → (Ai(i) ∈ 𝓣)) → ((⋃ᵢ Ai) ∈ 𝓣)

  Open : PredSet{ℓ₁ Lvl.⊔ ℓ₃}(X) → Stmt
  Open(A) = (A ∈ 𝓣)

  Closed : PredSet{ℓ₁ Lvl.⊔ ℓ₃}(X) → Stmt
  Closed(A) = Open(∁ A)

  record Neighborhood (p : X) (N : PredSet{ℓ₁ Lvl.⊔ ℓ₃}(X)) : Stmt{Lvl.𝐒(ℓ₁) Lvl.⊔ ℓ₂ Lvl.⊔ Lvl.𝐒(ℓ₃)} where
    constructor intro
    eta-equality
    field
      O : PredSet{ℓ₁ Lvl.⊔ ℓ₃}(X)
      ⦃ open-set ⦄       : Open(O)
      ⦃ covers ⦄         : O ⊆ N
      ⦃ contains-point ⦄ : p ∈ O

  open import Data
  open import Data.Proofs
  open import Data.Either as Either using (_‖_)
  open import Data.Either.Setoid
  open import Data.Boolean
  open import Data.Tuple as Tuple using (_⨯_ ; _,_)
  open import Functional
  open import Lang.Instance
  open import Logic.Propositional
  open import Logic.Predicate
  open import Lvl.Proofs
  open import Structure.Function.Domain
  open import Structure.Function
  open import Structure.Relator.Proofs
  open import Structure.Relator.Properties
  open import Structure.Relator
  open import Syntax.Function
  open import Syntax.Transitivity

  module _ where
    open import Relator.Equals.Proofs.Equiv{T = Bool} renaming ([≡]-equiv to bool-equiv)

    union-closure : ∀{A B} → (A ∈ 𝓣) → (B ∈ 𝓣) → ((A ∪ B) ∈ 𝓣)
    union-closure {A}{B} pa pb = substitute₂(_∋_) (reflexivity(_≡_) {x = 𝓣}) (⋃ᵢ-of-bijection ([∃]-intro Lvl.Up.obj) 🝖 ⋃ᵢ-of-boolean) (indexed-union-closure f-proof) where
      f-proof : ∀{i} → ((if i then B else A) ∈ 𝓣)
      f-proof {𝐹} = pa
      f-proof {𝑇} = pb

  instance
    Neighborhood-unaryRelator : ∀{N} → UnaryRelator(p ↦ Neighborhood p N)
    UnaryRelator.substitution Neighborhood-unaryRelator xy (intro O ⦃ contains-point = p ⦄) = intro O ⦃ contains-point = substitute₁(_∈ O) xy p ⦄

  -- TODO: Is it usable when defined like this?
  record Base {I : Type{ℓ₁ Lvl.⊔ ℓ₃}} (Bi : I → PredSet{ℓ₁ Lvl.⊔ ℓ₃}(X)) : Stmt{Lvl.𝐒(ℓ₁ Lvl.⊔ ℓ₃)} where
    constructor intro
    field
      covers-space : (∀{x} → (x ∈ (⋃ᵢ Bi)))
      generator : (x : X) → (i₁ i₂ : I) → ⦃ _ : (x ∈ (Bi(i₁) ∩ Bi(i₂))) ⦄ → I
      generator-contains-point : ∀{x : X}{i₁ i₂ : I} ⦃ _ : x ∈ (Bi(i₁) ∩ Bi(i₂)) ⦄ → (x ∈ Bi(generator x i₁ i₂))
      generator-subset : ∀{x : X}{i₁ i₂ : I} ⦃ _ : x ∈ (Bi(i₁) ∩ Bi(i₂)) ⦄ → (Bi(generator x i₁ i₂) ⊆ (Bi(i₁) ∩ Bi(i₂)))

  record ClosurePoint (A : PredSet{ℓ₁ Lvl.⊔ ℓ₃}(X)) (p : X) : Stmt{Lvl.𝐒(ℓ₁) Lvl.⊔ ℓ₂ Lvl.⊔ Lvl.𝐒(ℓ₃)} where
    constructor intro
    field proof : ∀{N} → ⦃ _ : Neighborhood(p)(N) ⦄ → NonEmpty(A ∩ N)

  instance
    ClosurePoint-unaryRelator : ∀{A} → UnaryRelator(ClosurePoint(A))
    ClosurePoint.proof (UnaryRelator.substitution ClosurePoint-unaryRelator xy Ax) {N} ⦃ neigh-y ⦄ = [∃]-map-proof id (ClosurePoint.proof Ax {N} ⦃ substitute₁ₗ(p ↦ Neighborhood p N) xy neigh-y ⦄)

  InternalPoint = swap Neighborhood

  record LimitPoint (A : PredSet{ℓ₁ Lvl.⊔ ℓ₃}(X)) (p : X) : Stmt{Lvl.𝐒(ℓ₁) Lvl.⊔ ℓ₂ Lvl.⊔ Lvl.𝐒(ℓ₃)} where
    constructor intro
    field proof : ∀{N} → ⦃ _ : Neighborhood(p)(N) ⦄ → NonEmpty(A ∩ (N ∖ (• p)))

  -- TODO: Use how IsolatedPoint and LimitPoint are related to prove this
  instance
    postulate LimitPoint-unaryRelator : ∀{A} → UnaryRelator(LimitPoint(A))
    {-LimitPoint.proof (UnaryRelator.substitution (LimitPoint-unaryRelator {A = A}) xy (intro proof)) {N} ⦃ neigh ⦄ = substitute₁(_) xy (proof ⦃ substitute₁ₗ(_) xy neigh ⦄) where
      instance
        inst : UnaryRelator(x ↦ NonEmpty(A ∩ (N ∖ (• x))))
        inst = [∘]-unaryRelator {f = x ↦ A ∩ (N ∖ (• x))} ⦃ {!!} ⦄ {P = NonEmpty} ⦃ {!!} ⦄
    -}

  record IsolatedPoint (A : PredSet{ℓ₁ Lvl.⊔ ℓ₃}(X)) (p : X) : Stmt{Lvl.𝐒(ℓ₁) Lvl.⊔ ℓ₂ Lvl.⊔ Lvl.𝐒(ℓ₃)} where
    constructor intro
    eta-equality
    field
      N : PredSet{ℓ₁ Lvl.⊔ ℓ₃}(X)
      ⦃ neighborhood ⦄ : Neighborhood(p)(N)
      proof : ((A ∩ N) ≡ₛ (• p))

  instance
    IsolatedPoint-unaryRelator : ∀{A} → UnaryRelator(IsolatedPoint(A))
    UnaryRelator.substitution IsolatedPoint-unaryRelator xy (intro N p) = intro N ⦃ substitute₁(a ↦ Neighborhood a N) xy infer ⦄ (p 🝖 (congruence₁ (•_) ⦃ singleton-function ⦃ equiv ⦄ ⦄ xy))

  Closure : PredSet{ℓ₁ Lvl.⊔ ℓ₃}(X) → PredSet(X)
  Closure(A) = intro(ClosurePoint(A))

  Interior : PredSet{ℓ₁ Lvl.⊔ ℓ₃}(X) → PredSet(X)
  Interior(A) = intro(InternalPoint(A))

  ∂ : PredSet{ℓ₁ Lvl.⊔ ℓ₃}(X) → PredSet(X)
  ∂ A = Closure(A) ∖ Interior(A)

  Discrete : PredSet{ℓ₁ Lvl.⊔ ℓ₃}(X) → Stmt
  Discrete(A) = A ⊆ intro(IsolatedPoint(A))

  Dense : PredSet{ℓ₁ Lvl.⊔ ℓ₃}(X) → Stmt
  Dense(A) = Closure(A) ⊆ A

  Perfect : PredSet{ℓ₁ Lvl.⊔ ℓ₃}(X) → Stmt
  Perfect(A) = ∀{p} → (¬ IsolatedPoint(A)(p))

  open import Numeral.Natural
  open import Numeral.Natural.Relation.Order using (_>_)

  record _converges-to_ (f : ℕ → X) (L : X) : Stmt{Lvl.𝐒(ℓ₁) Lvl.⊔ ℓ₂ Lvl.⊔ Lvl.𝐒(ℓ₃)} where
    constructor intro
    field
      min : ∃(Neighborhood(L)) → ℕ
      proof : ∀{NN@([∃]-intro N) : ∃(Neighborhood(L))}{n : ℕ} → (n > min(NN)) → (f(n) ∈ N)
  lim : (f : ℕ → X) → ⦃ _ : ∃(f converges-to_) ⦄ → X
  lim f ⦃ [∃]-intro L ⦄ = L

module _
  {ℓₗ₁ ℓₗ₂ ℓₗ₃} {X : Type{ℓₗ₁}} ⦃ equivₗ : Equiv{ℓₗ₁ Lvl.⊔ ℓₗ₃}(X) ⦄ (𝓣ₗ : PredSet{ℓₗ₂}(PredSet{ℓₗ₁ Lvl.⊔ ℓₗ₃}(X)))
  ⦃ _ : TopologicalSpace{ℓₗ₁}{ℓₗ₂}{ℓₗ₃} (𝓣ₗ) ⦄
  {ℓᵣ₁ ℓᵣ₂ ℓᵣ₃} {Y : Type{ℓᵣ₁}} ⦃ equivᵣ : Equiv{ℓᵣ₁ Lvl.⊔ ℓᵣ₃}(Y) ⦄ (𝓣ᵣ : PredSet{ℓᵣ₂}(PredSet{ℓᵣ₁ Lvl.⊔ ℓᵣ₃}(Y)))
  ⦃ _ : TopologicalSpace{ℓᵣ₁}{ℓᵣ₂}{ℓᵣ₃} (𝓣ᵣ) ⦄
  where
  open TopologicalSpace ⦃ … ⦄

  open import Logic.Predicate
  open import Structure.Function

  record ContinuousAt (f : X → Y) ⦃ _ : Function(f) ⦄ (x : X) : Stmt{Lvl.𝐒(ℓₗ₁ Lvl.⊔ ℓₗ₃ Lvl.⊔ ℓᵣ₁ Lvl.⊔ ℓᵣ₃) Lvl.⊔ ℓₗ₂ Lvl.⊔ ℓᵣ₂} where
    constructor intro
    field
      map : ∃(Neighborhood(f(x))) → ∃(Neighborhood(x))
      proof : ∀{NB@([∃]-intro B) : ∃(Neighborhood(f(x)))}
            → let ([∃]-intro A) = map(NB)
              in  (A ⊆ unmap f(B))

  Continuous : (f : X → Y) ⦃ _ : Function(f) ⦄ → Stmt{Lvl.𝐒(ℓₗ₁ Lvl.⊔ ℓₗ₃ Lvl.⊔ ℓᵣ₁ Lvl.⊔ ℓᵣ₃) Lvl.⊔ ℓₗ₂ Lvl.⊔ ℓᵣ₂}
  Continuous(f) = ∀{x} → ContinuousAt f(x)

module _
  {ℓₗ₁ ℓₗ₂ ℓₗ₃} {X : Type{ℓₗ₁}} ⦃ equivₗ : Equiv{ℓₗ₁ Lvl.⊔ ℓₗ₃}(X) ⦄ (𝓣ₗ : PredSet{ℓₗ₂}(PredSet{ℓₗ₁ Lvl.⊔ ℓₗ₃}(X)))
  ⦃ _ : TopologicalSpace{ℓₗ₁}{ℓₗ₂}{ℓₗ₃} (𝓣ₗ) ⦄
  {ℓᵣ₁ ℓᵣ₂ ℓᵣ₃} {Y : Type{ℓᵣ₁}} ⦃ equivᵣ : Equiv{ℓᵣ₁ Lvl.⊔ ℓᵣ₃}(Y) ⦄ (𝓣ᵣ : PredSet{ℓᵣ₂}(PredSet{ℓᵣ₁ Lvl.⊔ ℓᵣ₃}(Y)))
  ⦃ _ : TopologicalSpace{ℓᵣ₁}{ℓᵣ₂}{ℓᵣ₃} (𝓣ᵣ) ⦄
  where
  open TopologicalSpace ⦃ … ⦄

  open import Function.Inverse
  open import Structure.Function.Domain hiding (bijective)
  open import Structure.Function

  record Homeomorphism (f : X → Y) ⦃ func : Function(f) ⦄ : Stmt{Lvl.𝐒(ℓₗ₁ Lvl.⊔ ℓₗ₃ Lvl.⊔ ℓᵣ₁ Lvl.⊔ ℓᵣ₃) Lvl.⊔ ℓₗ₂ Lvl.⊔ ℓᵣ₂} where
    constructor intro
    field
      ⦃ invertible ⦄         : Invertible(f)
      ⦃ continuous ⦄         : Continuous(𝓣ₗ)(𝓣ᵣ) (f)
      ⦃ continuous-inverse ⦄ : Continuous(𝓣ᵣ)(𝓣ₗ) (inv f) ⦃ inv-function ⦄


postulate Name : Set
{-# BUILTIN QNAME Name #-}

data ⊤ : Set where
  tt : ⊤

data Term : Set where
  con : Name → Term → Term

data X : ⊤ → Set where
  x : {t : ⊤} → X t

data Y : Set where
  y : Y

-- this type checks
g : {t : ⊤} → Term → X t
g {t} (con nm args) = x {t}

-- this doesn't
f : {t : ⊤} → Term → X t
f {t} (con (quote Y.y) args) = x {t}
-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-- offending line is above
f t = x

-- t != args of type ⊤
-- when checking that the expression x {t} has type X args


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

open import Agda.Primitive
open import Prelude
open import GSeTT.Syntax
open import GSeTT.Rules

{- Categorical structure of the type theory for globular sets -}
module GSeTT.CwF-structure where

  {- cut-admissibility : action of substitutions preserves derivability -}
  []T : ∀ {Γ A Δ γ} → Γ ⊢T A → Δ ⊢S γ > Γ → Δ ⊢T (A [ γ ]Pre-Ty)
  []t : ∀ {Γ A t Δ γ} → Γ ⊢t t # A → Δ ⊢S γ > Γ → Δ ⊢t (t [ γ ]Pre-Tm) # (A [ γ ]Pre-Ty)

  []T (ob Γ⊢) Δ⊢γ:Γ = ob (Δ⊢γ:Γ→Δ⊢ Δ⊢γ:Γ)
  []T (ar Γ⊢t:A Γ⊢u:A) Δ⊢γ:Γ = ar ([]t Γ⊢t:A Δ⊢γ:Γ) ([]t Γ⊢u:A Δ⊢γ:Γ)
  []t {Γ = (Γ :: _)} {t = Var x} (var Γ+⊢@(cc Γ⊢ _ idp) (inl x∈Γ)) Δ⊢γ+:Γ+@(sc Δ⊢γ:Γ _ _ idp) with (eqdecℕ x (length Γ))
  ...                                                                                | inl idp = ⊥-elim (lΓ∉Γ Γ⊢ x∈Γ)
  ...                                                                                | inr _ = trT (wk[]T (Γ⊢t:A→Γ⊢A (var Γ⊢ x∈Γ)) Δ⊢γ+:Γ+ ^) ([]t (var Γ⊢ x∈Γ) Δ⊢γ:Γ)
  []t {Γ = (Γ :: _)} {t = Var x} (var Γ+⊢@(cc Γ⊢ Γ⊢A idp) (inr (idp , idp))) Δ⊢γ+:Γ+@(sc Δ⊢γ:Γ x₁ Δ⊢t:A[γ] idp) with (eqdecℕ x (length Γ))
  ...                                                                                | inl _ = trT (wk[]T Γ⊢A Δ⊢γ+:Γ+ ^) Δ⊢t:A[γ]
  ...                                                                                | inr x≠x = ⊥-elim (x≠x idp)

  {- action of identity on types and terms is trivial (true on syntax) -}
  [id]T : ∀ Γ A → (A [ Pre-id Γ ]Pre-Ty) == A
  [id]t : ∀ Γ t → (t [ Pre-id Γ ]Pre-Tm) == t

  [id]T Γ ∗ = idp
  [id]T Γ (⇒ A t u) = ⇒= ([id]T Γ A) ([id]t Γ t) ([id]t Γ u)
  [id]t nil (Var x) = idp
  [id]t (Γ :: (y , B)) (Var x) with (eqdecℕ x y)
  ...                              | inl x=y = Var= (x=y ^)
  ...                              | inr _ = [id]t Γ (Var x)

  {- identity is well-formed -}
  Γ⊢id:Γ : ∀ {Γ} → Γ ⊢C → Γ ⊢S Pre-id Γ > Γ
  Γ⊢id:Γ ec = es ec
  Γ⊢id:Γ Γ,x:A⊢@(cc Γ⊢ Γ⊢A idp) = sc (wkS (Γ⊢id:Γ Γ⊢) Γ,x:A⊢) Γ,x:A⊢ (var Γ,x:A⊢ (inr (idp , [id]T _ _))) idp

  {- action of substitutions on types and terms respects composition -}
  [∘]T : ∀ {Γ Δ Θ A γ δ} → Γ ⊢T A → Δ ⊢S γ > Γ → Θ ⊢S δ > Δ → ((A [ γ ]Pre-Ty) [ δ ]Pre-Ty) == (A [ γ ∘ δ ]Pre-Ty)
  [∘]t : ∀ {Γ Δ Θ A t γ δ} → Γ ⊢t t # A → Δ ⊢S γ > Γ → Θ ⊢S δ > Δ → ((t [ γ ]Pre-Tm) [ δ ]Pre-Tm) == (t [ γ ∘ δ ]Pre-Tm)

  [∘]T (ob _) _ _ = idp
  [∘]T (ar Γ⊢t:A Γ⊢u:A) Δ⊢γ:Γ Θ⊢δ:Δ = ⇒= ([∘]T (Γ⊢t:A→Γ⊢A Γ⊢t:A) Δ⊢γ:Γ Θ⊢δ:Δ) ([∘]t Γ⊢t:A Δ⊢γ:Γ Θ⊢δ:Δ) ([∘]t Γ⊢u:A Δ⊢γ:Γ Θ⊢δ:Δ)
  [∘]t (var {x = x} Γ⊢ x∈Γ) (sc {x = y} Δ⊢γ:Γ _ Δ⊢t:A[γ] idp) Θ⊢δ:Δ with (eqdecℕ x y )
  ... | inl idp = idp
  [∘]t (var Γ,y:A⊢ (inr (idp , idp))) (sc Δ⊢γ:Γ _ Δ⊢t:A[γ] idp) Θ⊢δ:Δ | inr x≠x = ⊥-elim (x≠x idp)
  [∘]t (var (cc Γ⊢ _ _) (inl x∈Γ)) (sc Δ⊢γ:Γ _ Δ⊢t:A[γ] idp) Θ⊢δ:Δ | inr _ = [∘]t (var Γ⊢ x∈Γ) Δ⊢γ:Γ Θ⊢δ:Δ

  {- composition of well-formed substitutions is well-formed -}
  ∘-admissibility : ∀ {Γ Δ Θ γ δ} → Δ ⊢S γ > Γ → Θ ⊢S δ > Δ → Θ ⊢S (γ ∘ δ) > Γ
  ∘-admissibility (es Δ⊢) Θ⊢δ:Δ = es (Δ⊢γ:Γ→Δ⊢ Θ⊢δ:Δ)
  ∘-admissibility (sc Δ⊢γ:Γ Γ,x:A⊢@(cc _ Γ⊢A _) Δ⊢t:A[γ] idp) Θ⊢δ:Δ = sc (∘-admissibility Δ⊢γ:Γ Θ⊢δ:Δ) Γ,x:A⊢ (trT ([∘]T Γ⊢A Δ⊢γ:Γ Θ⊢δ:Δ) ([]t Δ⊢t:A[γ] Θ⊢δ:Δ)) idp

  {- composition is associative, this is true only for well-formed substitutions -}
  ∘-associativity : ∀ {Γ Δ Θ Ξ γ δ θ} → Δ ⊢S γ > Γ → Θ ⊢S δ > Δ → Ξ ⊢S θ > Θ → ((γ ∘ δ) ∘ θ) == (γ ∘ (δ ∘ θ))
  ∘-associativity (es _) _ _ = idp
  ∘-associativity (sc Δ⊢γ:Γ _ Δ⊢t:A[γ] idp) Θ⊢δ:Δ Ξ⊢θ:Θ = ::= (∘-associativity Δ⊢γ:Γ Θ⊢δ:Δ Ξ⊢θ:Θ) (×= idp ([∘]t Δ⊢t:A[γ] Θ⊢δ:Δ Ξ⊢θ:Θ))

  {- Left unitality of composition -}

  -- To prove right-unitality, we need a analoguous of wk[]T and wk[]t for substitutions
  -- Composing if θ is a subst without x, acting (γ :: (x , u)) on it is same as acting just γ on it
  wk[]S : ∀ {Γ Δ γ x u B Θ θ} → Γ ⊢S θ > Θ → Δ ⊢S (γ :: (x , u)) > (Γ :: (x , B)) → (θ ∘ (γ :: (x , u))) == (θ ∘ γ)
  wk[]S (es _) _ = idp
  wk[]S (sc Γ⊢θ:Θ _ Γ⊢t:A[θ] idp) Δ⊢γ+:Γ+ = ::= (wk[]S Γ⊢θ:Θ Δ⊢γ+:Γ+) (×= idp (wk[]t Γ⊢t:A[θ] Δ⊢γ+:Γ+))

  ∘-left-unit : ∀{Γ Δ γ} → Δ ⊢S γ > Γ → (Pre-id Γ ∘ γ) == γ
  ∘-left-unit (es _) = idp
  ∘-left-unit Δ⊢γ+:Γ+@(sc {x = x} Δ⊢γ:Γ (cc Γ⊢ _ _) _ idp) with (eqdecℕ x x)
  ...                                                 | inl _ = ::= (wk[]S (Γ⊢id:Γ Γ⊢) Δ⊢γ+:Γ+ >> ∘-left-unit Δ⊢γ:Γ) idp
  ...                                                 | inr x≠x = ⊥-elim (x≠x idp)

 {- Right unitality of composition (true on syntax)-}
  ∘-right-unit : ∀ {Δ γ} →  (γ ∘ Pre-id Δ) == γ
  ∘-right-unit {Δ} {nil} = idp
  ∘-right-unit {Δ} {γ :: (y , t)} = ::= ∘-right-unit (×= idp ([id]t Δ t))

  {- Structure of CwF -}
  Γ,x:A⊢π:Γ : ∀ {Γ x A} → (Γ :: (x , A)) ⊢C → (Γ :: (x , A)) ⊢S Pre-π Γ x A > Γ
  Γ,x:A⊢π:Γ Γ,x:A⊢@(cc Γ⊢ _ _) = wkS (Γ⊢id:Γ Γ⊢) Γ,x:A⊢

  -- TODO : complete the CwF structure