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

open import HoTT
open import groups.KernelImage

module groups.KernelImageEmap where

module _ {i₀ i₁ j₀ j₁ k₀ k₁}
  {G₀ : Group i₀} {H₀ : Group j₀} {K₀ : Group k₀}
  {G₁ : Group i₁} {H₁ : Group j₁} {K₁ : Group k₁}
  {ψ₀ : H₀ →ᴳ K₀} {φ₀ : G₀ →ᴳ H₀} (H₀-ab : is-abelian H₀)
  {ψ₁ : H₁ →ᴳ K₁} {φ₁ : G₁ →ᴳ H₁} (H₁-ab : is-abelian H₁)
  {ξG : G₀ →ᴳ G₁} {ξH : H₀ →ᴳ H₁} {ξK : K₀ →ᴳ K₁}
  (ψ₀∼ψ₁ : CommSquareᴳ ψ₀ ψ₁ ξH ξK)
  (φ₀∼φ₁ : CommSquareᴳ φ₀ φ₁ ξG ξH)
  where

  module ξG = GroupHom ξG
  module ξH = GroupHom ξH
  module ξK = GroupHom ξK
  module ψ₀ = GroupHom ψ₀
  module ψ₁ = GroupHom ψ₁
  module φ₀ = GroupHom φ₀
  module φ₁ = GroupHom φ₁

  private
    f : Ker/Im.El ψ₀ φ₀ H₀-ab → Ker/Im.El ψ₁ φ₁ H₁-ab
    f = SetQuot-rec to pres-rel
      where
        abstract
          ξh-in-Ker : ∀ h (ψ₀h=0 : ψ₀.f h == Group.ident K₀)
            → ψ₁.f (ξH.f h) == Group.ident K₁
          ξh-in-Ker h ψ₀h=0 = (! $ ψ₀∼ψ₁ □$ᴳ h) ∙ ap ξK.f ψ₀h=0 ∙ ξK.pres-ident

        to : Ker.El ψ₀ → Ker/Im.El ψ₁ φ₁ H₁-ab
        to (h , ψ₀h=0) = q[ ξH.f h , ξh-in-Ker h ψ₀h=0 ]

        abstract
          pres-rel : ∀ {h h'}
            → ker/im-rel ψ₀ φ₀ H₀-ab h h'
            → to h == to h'
          pres-rel {(h , _)} {(h' , _)} = Trunc-rec
            λ{(g , φg=h-h') → quot-rel
              [ ξG.f g , (! $ φ₀∼φ₁ □$ᴳ g) ∙ ap ξH.f φg=h-h' ∙ ξH.pres-diff h h' ]}

    abstract
      f-is-hom : preserves-comp (Ker/Im.comp ψ₀ φ₀ H₀-ab) (Ker/Im.comp ψ₁ φ₁ H₁-ab) f
      f-is-hom = SetQuot-elim
        (λ{(h , _) → SetQuot-elim
          (λ{(h' , _) → ap q[_] (Ker.El=-out ψ₁ (ξH.pres-comp h h'))})
          (λ _ → prop-has-all-paths-↓)})
        (λ _ → prop-has-all-paths-↓)

  Ker/Im-fmap : Ker/Im ψ₀ φ₀ H₀-ab →ᴳ Ker/Im ψ₁ φ₁ H₁-ab
  Ker/Im-fmap = group-hom f f-is-hom

module _ {i₀ i₁ j₀ j₁ k₀ k₁}
  {G₀ : Group i₀} {H₀ : Group j₀} {K₀ : Group k₀}
  {G₁ : Group i₁} {H₁ : Group j₁} {K₁ : Group k₁}
  {ψ₀ : H₀ →ᴳ K₀} {φ₀ : G₀ →ᴳ H₀} (H₀-ab : is-abelian H₀)
  {ψ₁ : H₁ →ᴳ K₁} {φ₁ : G₁ →ᴳ H₁} (H₁-ab : is-abelian H₁)
  {ξG : G₀ →ᴳ G₁} {ξH : H₀ →ᴳ H₁} {ξK : K₀ →ᴳ K₁}
  (ψ₀∼ψ₁ : CommSquareᴳ ψ₀ ψ₁ ξH ξK)
  (φ₀∼φ₁ : CommSquareᴳ φ₀ φ₁ ξG ξH)
  (ξG-ise : is-equiv (GroupHom.f ξG))
  (ξH-ise : is-equiv (GroupHom.f ξH))
  (ξK-ise : is-equiv (GroupHom.f ξK))
  where

  private
    to : Ker/Im.El ψ₀ φ₀ H₀-ab → Ker/Im.El ψ₁ φ₁ H₁-ab
    to = GroupHom.f (Ker/Im-fmap H₀-ab H₁-ab ψ₀∼ψ₁ φ₀∼φ₁)

    from : Ker/Im.El ψ₁ φ₁ H₁-ab → Ker/Im.El ψ₀ φ₀ H₀-ab
    from = GroupHom.f (Ker/Im-fmap H₁-ab H₀-ab (CommSquareᴳ-inverse-v ψ₀∼ψ₁ ξH-ise ξK-ise) (CommSquareᴳ-inverse-v φ₀∼φ₁ ξG-ise ξH-ise))

    abstract
      to-from : ∀ x → to (from x) == x
      to-from = SetQuot-elim
        {{=-preserves-level ⟨⟩}}
        (λ{(h , _) → ap q[_] $ Ker.El=-out ψ₁ $ is-equiv.f-g ξH-ise h})
        (λ {h} {h'} _ → prop-has-all-paths-↓ {{has-level-apply SetQuot-is-set (to (from q[ h' ])) q[ h' ]}})

      from-to : ∀ x → from (to x) == x
      from-to = SetQuot-elim
        {{=-preserves-level ⟨⟩}}
        (λ{(h , _) → ap q[_] $ Ker.El=-out ψ₀ $ is-equiv.g-f ξH-ise h})
        (λ {h} {h'} _ → prop-has-all-paths-↓ {{has-level-apply SetQuot-is-set (from (to q[ h' ])) q[ h' ]}})

  Ker/Im-emap : Ker/Im ψ₀ φ₀ H₀-ab ≃ᴳ Ker/Im ψ₁ φ₁ H₁-ab
  Ker/Im-emap = Ker/Im-fmap H₀-ab H₁-ab ψ₀∼ψ₁ φ₀∼φ₁ ,
    is-eq to from to-from from-to


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

module sets.int.definition where

open import sum
open import equality
open import function
open import sets.nat.core
open import hott.level

private
  data Z : Set where
    mk-int : ℕ → ℕ → Z

ℤ : Set
ℤ = Z

_[-]_ : ℕ → ℕ → ℤ
_[-]_ = mk-int

postulate
  eq-ℤ : (n m d : ℕ) → n [-] m ≡ (d + n) [-] (d + m)
  hℤ : h 2 ℤ

IsAlg-ℤ : ∀ {i} → Set i → Set _
IsAlg-ℤ X = Σ (ℕ → ℕ → X) λ f
          → (∀ n m d → f n m ≡ f (d + n) (d + m))

aℤ : IsAlg-ℤ ℤ
aℤ = _[-]_ , eq-ℤ

map-Alg-ℤ : ∀ {i j}{X : Set i}{Y : Set j}
          → (X → Y) → IsAlg-ℤ X → IsAlg-ℤ Y
map-Alg-ℤ {X = X}{Y} h (f , u) = (g , v)
  where
    g : ℕ → ℕ → Y
    g n m = h (f n m)

    v : ∀ n m d → g n m ≡ g (d + n) (d + m)
    v n m d = ap h (u n m d)

map-Alg-ℤ-id : ∀ {i}{X : Set i}(aX : IsAlg-ℤ X)
             → map-Alg-ℤ (λ x → x) aX ≡ aX
map-Alg-ℤ-id aX = unapΣ (refl ,
  funext λ n → funext λ m → funext λ d → ap-id _)

map-Alg-ℤ-hom : ∀ {i j k}{X : Set i}{Y : Set j}{Z : Set k}
     → (f : Y → Z)(g : X → Y)(aX : IsAlg-ℤ X)
     → map-Alg-ℤ (f ∘' g) aX
     ≡ map-Alg-ℤ f (map-Alg-ℤ g aX)
map-Alg-ℤ-hom f g aX = unapΣ (refl , funext λ n → funext λ m → funext λ d
  → sym (ap-hom g f _))

Hom-ℤ : ∀ {i j}{X : Set i}{Y : Set j}
      → IsAlg-ℤ X → IsAlg-ℤ Y → Set _
Hom-ℤ {X = X}{Y} aX aY = Σ (X → Y) λ h → map-Alg-ℤ h aX ≡ aY

id-ℤ : ∀ {i}{X : Set i}(aX : IsAlg-ℤ X) → Hom-ℤ aX aX
id-ℤ aX = (λ x → x) , map-Alg-ℤ-id aX

_⋆ℤ_ : ∀ {i j k}{X : Set i}{Y : Set j}{Z : Set k}
     → {aX : IsAlg-ℤ X}{aY : IsAlg-ℤ Y}{aZ : IsAlg-ℤ Z}
     → Hom-ℤ aY aZ → Hom-ℤ aX aY → Hom-ℤ aX aZ
_⋆ℤ_ {aX = aX} (f , u) (g , v) = f ∘' g , map-Alg-ℤ-hom f g aX · ap (map-Alg-ℤ f) v · u

module _ {i}{X : Set i}(hX : h 2 X)(aX : IsAlg-ℤ X) where

  elim-ℤ : ℤ → X
  elim-ℤ (mk-int n n') = proj₁ aX n n'

  postulate elim-β-ℤ : ∀ n m d → ap elim-ℤ (eq-ℤ n m d) ≡ proj₂ aX n m d

  elim-Alg-β-ℤ : map-Alg-ℤ elim-ℤ aℤ ≡ aX
  elim-Alg-β-ℤ = unapΣ (refl , funext λ n → funext λ m → funext λ d → elim-β-ℤ n m d)

  elim-Alg-ℤ : Hom-ℤ aℤ aX
  elim-Alg-ℤ = elim-ℤ , elim-Alg-β-ℤ

  postulate univ-ℤ : (h : Hom-ℤ aℤ aX) → elim-Alg-ℤ ≡ h

  Hom-ℤ-contr : contr (Hom-ℤ aℤ aX)
  Hom-ℤ-contr = elim-Alg-ℤ , univ-ℤ

elim-prop-ℤ : ∀ {i}{X : ℤ → Set i} → (∀ n → h 1 (X n))
            → (f : ∀ n m → X (n [-] m))
            → ∀ n → X n
elim-prop-ℤ {X = X} hX f n = subst X (i-β₀ n) (proj₂ (s₀ n))
  where
    Y : Set _
    Y = Σ ℤ X

    hY : h 2 Y
    hY = Σ-level hℤ λ n → h↑ (hX n)

    aY : IsAlg-ℤ Y
    aY = (λ n m → (n [-] m , f n m))
       , (λ n m d → unapΣ (eq-ℤ n m d , h1⇒prop (hX ((d + n) [-] (d + m))) _ _))

    pY : Hom-ℤ aY aℤ
    pY = proj₁ , unapΣ (refl , h1⇒prop
      (Π-level λ n → Π-level λ m → Π-level λ p → hℤ _ _) _ _)

    s : Hom-ℤ aℤ aY
    s = elim-Alg-ℤ hY aY

    s₀ : ℤ → Y
    s₀ = proj₁ s

    i-β₀ : ∀ n → proj₁ (s₀ n) ≡ n
    i-β₀ n = ap (λ f → proj₁ f n)
      (contr⇒prop (Hom-ℤ-contr hℤ aℤ) (pY ⋆ℤ s) (id-ℤ aℤ))


module Krivine (Atom Const : Set) where

open import Data.Nat
open import Data.Sum
open import Data.Vec as Vec using (Vec; _∷_; [])

data Type : Set where
  atom : Atom → Type
  _⇒_  : Type → Type → Type

Basis : ℕ → Set
Basis = Vec Type

data Var : ∀ {n} → Basis n → Type → Set where
  vzero : ∀ {n} {Γ : Basis n} {τ}   → Var (τ ∷ Γ) τ
  vsuc  : ∀ {n} {Γ : Basis n} {σ τ} → Var Γ τ → Var (σ ∷ Γ) τ

data Term (Σ : Const → Type) {n} (Γ : Basis n) : Type → Set where
  app : ∀ {σ τ} → Term Σ Γ (σ ⇒ τ) → Term Σ Γ σ → Term Σ Γ τ
  lam : ∀ {σ τ} → Term Σ (σ ∷ Γ) τ → Term Σ Γ (σ ⇒ τ)
  var : ∀ {τ}   → Var Γ τ → Term Σ Γ τ

mutual
  data Subst (A : ∀ {n} (Γ : Basis n) → Type → Set) : ∀ {n₁} (Γ₁ : Basis n₁) {n₂} (Γ₂ : Basis n₂) → Set where
    comp  : ∀ {n₁} {Γ₁ : Basis n₁} {n₂} {Γ₂ : Basis n₂} {n₃} {Γ₃ : Basis n₃} → Subst A Γ₁ Γ₂ → Subst A Γ₂ Γ₃ → Subst A Γ₁ Γ₃
    cons  : ∀ {n₁} {Γ₁ : Basis n₁} {n₂} {Γ₂ : Basis n₂} {τ}                  → A Γ₂ τ → Subst A Γ₁ Γ₂ → Subst A (τ ∷ Γ₁) Γ₂
    id    : ∀ {n} {Γ : Basis n}                                              → Subst A Γ Γ
    lift  : ∀ {n₁} {Γ₁ : Basis n₁} {n₂} {Γ₂ : Basis n₂} {τ}                  → Subst A Γ₁ Γ₂ → Subst A (τ ∷ Γ₁) (τ ∷ Γ₂)
    shift : ∀ {n} {Γ : Basis n} {τ}                                          → Subst A Γ (τ ∷ Γ)

  data Thunk (A : ∀ {n} (Γ : Basis n) → Type → Set) {n} (Γ : Basis n) τ : Set where
    thunk : ∀ {n'} {Γ' : Basis n'} → A Γ' τ → Subst (Thunk A) Γ' Γ → Thunk A Γ τ

lookup : ∀ {A : ∀ {n} (Γ : Basis n) → Type → Set} → (∀ {n} {Γ : Basis n} {σ} → Var Γ σ → Thunk A Γ σ) → (∀ {n₁} {Γ₁ : Basis n₁} {n₂} {Γ₂ : Basis n₂} {τ}
       → Thunk A Γ₁ τ → Subst (Thunk A) Γ₁ Γ₂ → Thunk A Γ₂ τ) → ∀ {n₁} {Γ₁ : Basis n₁} {n₂} {Γ₂ : Basis n₂} {τ} → Var Γ₁ τ → Subst (Thunk A) Γ₁ Γ₂ → Thunk A Γ₂ τ
lookup {A} box under = lookup'
  where
    lookup' : ∀ {n₁} {Γ₁ : Basis n₁} {n₂} {Γ₂ : Basis n₂} {τ} → Var Γ₁ τ → Subst (Thunk A) Γ₁ Γ₂ → Thunk A Γ₂ τ
    lookup' v        (comp ρ σ) = under (lookup' v ρ) σ
    lookup' vzero    (cons x _) = x
    lookup' (vsuc v) (cons _ σ) = lookup' v σ
    lookup' v        id         = box v
    lookup' vzero    (lift _)   = box vzero
    lookup' (vsuc v) (lift σ)   = under (lookup' v σ) shift
    lookup' v        shift      = box (vsuc v)

boxvar : ∀ {Σ : Const → Type} {n} {Γ : Basis n} {τ} → Var Γ τ → Thunk (Term Σ) Γ τ
boxvar v = thunk (var v) id

close : ∀ {Σ : Const → Type} {n₁} {Γ₁ : Basis n₁} {n₂} {Γ₂ : Basis n₂} {τ} → Thunk (Term Σ) Γ₁ τ → Subst (Thunk (Term Σ)) Γ₁ Γ₂ → Thunk (Term Σ) Γ₂ τ
close (thunk t ρ) σ = thunk t (comp ρ σ)

mutual
  subst : ∀ {Σ : Const → Type} {n₁} {Γ₁ : Basis n₁} {n₂} {Γ₂ : Basis n₂} {τ} → Term Σ Γ₁ τ → Subst (Thunk (Term Σ)) Γ₁ Γ₂ → Term Σ Γ₂ τ
  subst (app s t) σ  = app (subst s σ) (subst t σ)
  subst (lam t)   σ  = lam (subst t (lift σ))
  subst (var v)   id = var v
  subst (var v)   σ  = force (lookup boxvar close v σ)

  force : ∀ {Σ : Const → Type} {n} {Γ : Basis n} {τ} → Thunk (Term Σ) Γ τ → Term Σ Γ τ
  force (thunk t ρ) = subst t ρ

data Context (A : ∀ {n} (Γ : Basis n) → Type → Set) : ∀ {n₁} (Γ₁ : Basis n₁) {n₂} (Γ₂ : Basis n₂) → Type → Type → Set where
  app₁ : ∀ {n₁} {Γ₁ : Basis n₁} {n₂} {Γ₂ : Basis n₂} {ρ σ τ} → Context A Γ₁ Γ₂ σ τ → A Γ₁ ρ → Context A Γ₁ Γ₂ (ρ ⇒ σ) τ
  app₂ : ∀ {n₁} {Γ₁ : Basis n₁} {n₂} {Γ₂ : Basis n₂} {ρ σ τ} → A Γ₁ (ρ ⇒ σ) → Context A Γ₁ Γ₂ σ τ → Context A Γ₁ Γ₂ ρ τ
  lam₁ : ∀ {n₁} {Γ₁ : Basis n₁} {n₂} {Γ₂ : Basis n₂} {ρ σ τ} → Context A Γ₁ Γ₂ (ρ ⇒ σ) τ → Context A (ρ ∷ Γ₁) Γ₂ σ τ
  top  : ∀ {n} {Γ : Basis n} {τ}                             → Context A Γ Γ τ τ

foldctx : ∀ {A : ∀ {n} (Γ : Basis n) → Type → Set} {Σ : Const → Type} {n₁} {Γ₁ : Basis n₁} {n₂} {Γ₂ : Basis n₂} {σ τ}
        → (∀ {n} {Γ : Basis n} {ρ} → A Γ ρ → Term Σ Γ ρ) → Term Σ Γ₁ σ → Context A Γ₁ Γ₂ σ τ → Term Σ Γ₂ τ
foldctx f z (app₁ ctx y) = foldctx f (app z (f y)) ctx
foldctx f z (app₂ x ctx) = foldctx f (app (f x) z) ctx
foldctx f z (lam₁ ctx)   = foldctx f (lam z)       ctx
foldctx _ z top          = z

data Head (Σ : Const → Type) {n} (Γ : Basis n) : Type → Set where
  var : ∀ {τ} → Var Γ τ → Head Σ Γ τ

data Spine (Σ : Const → Type) (A : (∀ {n} (Γ : Basis n) → Type → Set) → ∀ {n} (Γ : Basis n) → Type → Set) {n} (Γ : Basis n) τ : Set where
  spine : ∀ {n'} {Γ' : Basis n'} {σ} → Head Σ Γ' σ → Context (A (Term Σ)) Γ' Γ σ τ → Spine Σ A Γ τ

eval : ∀ {Σ : Const → Type} {n₁} {Γ₁ : Basis n₁} {n₂} {Γ₂ : Basis n₂} {σ τ} → Thunk (Term Σ) Γ₁ σ → Context (Thunk (Term Σ)) Γ₁ Γ₂ σ τ → Spine Σ Thunk Γ₂ τ
eval (thunk (app s t) σ)  ctx         = eval (thunk s σ) (app₁ ctx (thunk t σ))
eval (thunk (lam t)   σ) (app₁ ctx x) = eval (thunk t (cons x σ)) ctx
eval (thunk (lam t)   σ)  ctx         = eval (thunk t (lift σ)) (lam₁ ctx)
eval (thunk (var v)   id) ctx         = spine (var v) ctx
eval (thunk (var v)   σ)  ctx         = eval (lookup boxvar close v σ) ctx

data Subst' (A : ∀ {n} (Γ : Basis n) → Type → Set) : ∀ {n₁} (Γ₁ : Basis n₁) {n₂} (Γ₂ : Basis n₂) → Set where
  comp  : ∀ {n₁} {Γ₁ : Basis n₁} {n₂} {Γ₂ : Basis n₂} {n₃} {Γ₃ : Basis n₃} → Subst A Γ₁ Γ₂ → Subst' A Γ₂ Γ₃ → Subst' A Γ₁ Γ₃
  id    : ∀ {n} {Γ : Basis n}                                              → Subst' A Γ Γ

data Thunk' (A : ∀ {n} (Γ : Basis n) → Type → Set) {n} (Γ : Basis n) τ : Set where
  thunk : ∀ {n'} {Γ' : Basis n'} → A Γ' τ → Subst' (Thunk' A) Γ' Γ → Thunk' A Γ τ

data Machine (Σ : Const → Type) {n} (Γ : Basis n) τ : Set where
  machine : ∀ {n'} {Γ' : Basis n'} {σ} → Thunk' (Term Σ) Γ' σ → Context (Thunk' (Term Σ)) Γ' Γ σ τ → Machine Σ Γ τ

coerce : ∀ {A : ∀ {n} (Γ : Basis n) → Type → Set} {n₁} {Γ₁ : Basis n₁} {n₂} {Γ₂ : Basis n₂} → Subst' A Γ₁ Γ₂ → Subst A Γ₁ Γ₂
coerce (comp ρ σ) = comp ρ (coerce σ)
coerce id         = id

step : ∀ {Σ : Const → Type} {n} {Γ : Basis n} {τ} → Machine Σ Γ τ → Spine Σ Thunk' Γ τ ⊎ Machine Σ Γ τ
step (machine (thunk (var v)         (comp shift                σ)) ctx) = inj₂ (machine (thunk (var (vsuc v)) σ)                                    ctx)
step (machine (thunk (var vzero)     (comp (cons (thunk u π) _) σ)) ctx) = inj₂ (machine (thunk u              (comp (coerce π) σ))                  ctx)
step (machine (thunk (var vzero)     (comp (lift _)             σ)) ctx) = inj₂ (machine (thunk (var vzero)    σ)                                    ctx)
step (machine (thunk (var (vsuc v))  (comp (cons (thunk _ _) ρ) σ)) ctx) = inj₂ (machine (thunk (var v)        (comp ρ          σ))                  ctx)
step (machine (thunk (var (vsuc v))  (comp (lift ρ)             σ)) ctx) = inj₂ (machine (thunk (var v)        (comp ρ (comp shift σ)))              ctx)
step (machine (thunk t               (comp (comp π ρ)           σ)) ctx) = inj₂ (machine (thunk t              (comp π (comp ρ σ)))                  ctx)
step (machine (thunk t               (comp id                   σ)) ctx) = inj₂ (machine (thunk t              σ)                                    ctx)
step (machine (thunk t               σ)          (app₂ (thunk s ρ) ctx)) = inj₂ (machine (thunk s              ρ)                 (app₁ ctx (thunk t σ)))
step (machine (thunk (app s (var v)) σ)                             ctx) = inj₂ (machine (thunk (var v)        σ)                 (app₂ (thunk s σ) ctx))
step (machine (thunk (app s t)       σ)                             ctx) = inj₂ (machine (thunk s              σ)                 (app₁ ctx (thunk t σ)))
step (machine (thunk (lam t)         σ)                    (app₁ ctx x)) = inj₂ (machine (thunk t              (comp (cons x (coerce σ)) id))        ctx)
step (machine (thunk (lam t)         σ)                             ctx) = inj₂ (machine (thunk t              (comp (lift (coerce σ)) id))   (lam₁ ctx))
step (machine (thunk (var v)         id)                            ctx) = inj₁ (spine (var v) ctx)

reduce : ∀ {Σ : Const → Type} {n} {Γ : Basis n} {τ} → Machine Σ Γ τ → Spine Σ Thunk' Γ τ
reduce m with step m
...      | inj₁ x  = x
...      | inj₂ m' = reduce m'


{-# OPTIONS --universe-polymorphism --allow-unsolved-metas #-}

module Issue209 where

postulate
  Level : Set
  zero : Level
  suc  : Level → Level
  _⊔_ : Level -> Level -> Level

{-# BUILTIN LEVEL     Level #-}
{-# BUILTIN LEVELZERO zero  #-}
{-# BUILTIN LEVELSUC  suc   #-}
{-# BUILTIN LEVELMAX _⊔_ #-}

data _≡_ {a} {A : Set a} (x : A) : A → Set where
  refl : x ≡ x

data _≅_ {a} {A : Set a} (x : A) : ∀ {b} {B : Set b} → B → Set where
  refl : x ≅ x

subst : ∀ {a p} {A : Set a} (P : A → Set p) {x y} → x ≡ y → P x → P y
subst P refl p = p

lemma : ∀ {A} (P : A → Set) {x y} (eq : x ≡ y) z →
        subst P eq z ≅ z
lemma P refl z = refl

-- An internal error has occurred. Please report this as a bug.
-- Location of the error: src/full/Agda/TypeChecking/Telescope.hs:51

-- The problematic call to reorderTel is
--   reorderTel tel3
-- in Agda.TypeChecking.Rules.LHS.Instantiate.


{-# OPTIONS --cubical --no-import-sorts --safe #-}
module Cubical.ZCohomology.Properties where

open import Cubical.ZCohomology.Base
open import Cubical.HITs.S1
open import Cubical.HITs.Sn
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Pointed
open import Cubical.Foundations.Transport
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.GroupoidLaws
open import Cubical.Foundations.Univalence
open import Cubical.Data.Empty
open import Cubical.Data.Sigma hiding (_×_)
open import Cubical.HITs.Susp
open import Cubical.HITs.Wedge
open import Cubical.HITs.SetTruncation renaming (rec to sRec ; elim to sElim ; elim2 to sElim2)
open import Cubical.HITs.Nullification
open import Cubical.Data.Int hiding (_+_)
open import Cubical.Data.Nat
open import Cubical.Data.Prod
open import Cubical.HITs.Truncation renaming (elim to trElim ; map to trMap ; rec to trRec ; elim3 to trElim3)
open import Cubical.Homotopy.Loopspace
open import Cubical.Homotopy.Connected
open import Cubical.Homotopy.Freudenthal
open import Cubical.HITs.SmashProduct.Base
open import Cubical.Structures.Group

open import Cubical.HITs.Pushout
open import Cubical.Data.Sum.Base
open import Cubical.Data.HomotopyGroup
open import Cubical.Data.Bool hiding (_⊕_)

open import Cubical.ZCohomology.KcompPrelims

private
  variable
    ℓ ℓ' : Level
    A : Type ℓ
    B : Type ℓ'
    A' : Pointed ℓ

{- Equivalence between cohomology of A and reduced cohomology of (A + 1) -}
coHomRed+1Equiv : (n : ℕ) →
                  (A : Type ℓ) →
                  (coHom n A) ≡ (coHomRed n ((A ⊎ Unit , inr (tt))))
coHomRed+1Equiv zero A i = ∥ helpLemma {C = (Int , pos 0)} i ∥₂
  module coHomRed+1 where
  helpLemma : {C : Pointed ℓ} → ( (A → (typ C)) ≡  ((((A ⊎ Unit) , inr (tt)) →∙ C)))
  helpLemma {C = C} = isoToPath (iso map1
                                     map2
                                     (λ b → linvPf b)
                                     (λ _  → refl))
    where
    map1 : (A → typ C) → ((((A ⊎ Unit) , inr (tt)) →∙ C))
    map1 f = map1' , refl
      module helpmap where
      map1' : A ⊎ Unit → fst C
      map1' (inl x) = f x
      map1' (inr x) = pt C

    map2 : ((((A ⊎ Unit) , inr (tt)) →∙ C)) → (A → typ C)
    map2 (g , pf) x = g (inl x)

    linvPf : (b :((((A ⊎ Unit) , inr (tt)) →∙ C))) →  map1 (map2 b) ≡ b
    linvPf (f , snd) i = (λ x → helper x i)  , λ j → snd ((~ i) ∨ j)
      where
      helper : (x : A ⊎ Unit) → ((helpmap.map1') (map2 (f , snd)) x) ≡ f x
      helper (inl x) = refl
      helper (inr tt) = sym snd

coHomRed+1Equiv (suc n) A i = ∥ coHomRed+1.helpLemma A i {C = ((coHomK (suc n)) , ∣ north ∣)} i ∥₂

-----------

Kn→ΩKn+1 : (n : ℕ) → coHomK n → typ (Ω (coHomK-ptd (suc n)))
Kn→ΩKn+1 n = Iso.fun (Iso2-Kn-ΩKn+1 n)

ΩKn+1→Kn : {n : ℕ} → typ (Ω (coHomK-ptd (suc n))) → coHomK n
ΩKn+1→Kn {n = n} = Iso.inv (Iso2-Kn-ΩKn+1 n)

Kn≃ΩKn+1 : {n : ℕ} → coHomK n ≃ typ (Ω (coHomK-ptd (suc n)))
Kn≃ΩKn+1 {n = n} = isoToEquiv (Iso-Kn-ΩKn+1 n)

---------- Algebra/Group stuff --------

0ₖ : {n : ℕ} → coHomK n
0ₖ {n = zero} = pt (coHomK-ptd 0)
0ₖ {n = suc n} = pt (coHomK-ptd (suc n))

infixr 35 _+ₖ_

_+ₖ_ : {n : ℕ} → coHomK n → coHomK n → coHomK n
_+ₖ_ {n = n} x y  = ΩKn+1→Kn (Kn→ΩKn+1 n x ∙ Kn→ΩKn+1 n y)

-ₖ_ : {n : ℕ} → coHomK n → coHomK n
-ₖ_ {n = n} x = ΩKn+1→Kn (sym (Kn→ΩKn+1 n x))

Kn→ΩKn+10ₖ : (n : ℕ) → Kn→ΩKn+1 n 0ₖ ≡ refl
Kn→ΩKn+10ₖ zero = refl
Kn→ΩKn+10ₖ (suc n) = (λ i → cong ∣_∣ (rCancel (merid north) i))

rUnitₖ : {n : ℕ} (x : coHomK n) → x +ₖ 0ₖ ≡ x
rUnitₖ {n = zero} x = cong ΩKn+1→Kn (sym (rUnit (Kn→ΩKn+1 zero x))) ∙
                      Iso.leftInv (Iso2-Kn-ΩKn+1 zero) x
rUnitₖ {n = suc n} x = (λ i → ΩKn+1→Kn (Kn→ΩKn+1 (suc n) x ∙ Kn→ΩKn+10ₖ (suc n) i)) ∙
                       (λ i → ΩKn+1→Kn (rUnit (Kn→ΩKn+1 (suc n) x) (~ i))) ∙
                       Iso.leftInv (Iso2-Kn-ΩKn+1 (suc n)) x

lUnitₖ : {n : ℕ} (x : coHomK n) → 0ₖ +ₖ x ≡ x
lUnitₖ {n = zero} x = cong ΩKn+1→Kn (sym (lUnit (Kn→ΩKn+1 zero x))) ∙
                      Iso.leftInv (Iso2-Kn-ΩKn+1 zero) x
lUnitₖ {n = suc n} x = (λ i → ΩKn+1→Kn (Kn→ΩKn+10ₖ (suc n) i ∙ Kn→ΩKn+1 (suc n) x)) ∙
                       (λ i → ΩKn+1→Kn (lUnit (Kn→ΩKn+1 (suc n) x) (~ i))) ∙
                       Iso.leftInv (Iso2-Kn-ΩKn+1 (suc n)) x

rCancelₖ  : {n : ℕ} (x : coHomK n) → x +ₖ (-ₖ x) ≡ 0ₖ
rCancelₖ {n = zero} x = (λ i → ΩKn+1→Kn (Kn→ΩKn+1 zero x ∙ Iso.rightInv (Iso2-Kn-ΩKn+1 zero) (sym (Kn→ΩKn+1 zero x)) i)) ∙
                        cong ΩKn+1→Kn (rCancel (Kn→ΩKn+1 zero x))
rCancelₖ {n = suc zero} x = (λ i → ΩKn+1→Kn (Kn→ΩKn+1 1 x ∙ Iso.rightInv (Iso2-Kn-ΩKn+1 1) (sym (Kn→ΩKn+1 1 x)) i)) ∙
                            cong ΩKn+1→Kn (rCancel (Kn→ΩKn+1 1 x))
rCancelₖ {n = suc (suc n)} x = (λ i → ΩKn+1→Kn (Kn→ΩKn+1 (2 + n) x ∙ Iso.rightInv (Iso2-Kn-ΩKn+1 (2 + n)) (sym (Kn→ΩKn+1 (2 + n) x)) i)) ∙
                               cong ΩKn+1→Kn (rCancel (Kn→ΩKn+1 (2 + n) x)) ∙
                               (λ i → ΩKn+1→Kn (Kn→ΩKn+10ₖ (suc (suc n)) (~ i))) ∙
                               Iso.leftInv (Iso2-Kn-ΩKn+1 (suc (suc n))) 0ₖ

lCancelₖ : {n : ℕ} (x : coHomK n) → (-ₖ x) +ₖ x  ≡ 0ₖ
lCancelₖ {n = zero} x = (λ i → ΩKn+1→Kn (Iso.rightInv (Iso2-Kn-ΩKn+1 zero) (sym (Kn→ΩKn+1 zero x)) i ∙ Kn→ΩKn+1 zero x)) ∙
                        cong ΩKn+1→Kn (lCancel (Kn→ΩKn+1 zero x))
lCancelₖ {n = suc zero} x = ((λ i → ΩKn+1→Kn (Iso.rightInv (Iso2-Kn-ΩKn+1 1) (sym (Kn→ΩKn+1 1 x)) i ∙ Kn→ΩKn+1 1 x))) ∙
                            cong ΩKn+1→Kn (lCancel (Kn→ΩKn+1 1 x))
lCancelₖ {n = suc (suc n)} x = (λ i → ΩKn+1→Kn (Iso.rightInv (Iso2-Kn-ΩKn+1 (2 + n)) (sym (Kn→ΩKn+1 (2 + n) x)) i ∙ Kn→ΩKn+1 (2 + n) x)) ∙
                               cong ΩKn+1→Kn (lCancel (Kn→ΩKn+1 (2 + n) x)) ∙
                               (λ i → ΩKn+1→Kn (Kn→ΩKn+10ₖ (suc (suc n)) (~ i))) ∙
                               Iso.leftInv (Iso2-Kn-ΩKn+1 (suc (suc n))) 0ₖ

assocₖ : {n : ℕ} (x y z : coHomK n) → ((x +ₖ y) +ₖ z) ≡ (x +ₖ (y +ₖ z))
assocₖ {n = n} x y z = (λ i → ΩKn+1→Kn (Kn→ΩKn+1 n (ΩKn+1→Kn (Kn→ΩKn+1 n x ∙ Kn→ΩKn+1 n y)) ∙ Kn→ΩKn+1 n z)) ∙
                          (λ i → ΩKn+1→Kn (Iso.rightInv (Iso2-Kn-ΩKn+1 n) (Kn→ΩKn+1 n x ∙ Kn→ΩKn+1 n y) i ∙ Kn→ΩKn+1 n z)) ∙
                          (λ i → ΩKn+1→Kn (assoc (Kn→ΩKn+1 n x) (Kn→ΩKn+1 n y) (Kn→ΩKn+1 n z) (~ i))) ∙
                          (λ i → ΩKn+1→Kn ((Kn→ΩKn+1 n x) ∙ Iso.rightInv (Iso2-Kn-ΩKn+1 n) ((Kn→ΩKn+1 n y ∙ Kn→ΩKn+1 n z)) (~ i)))

commₖ : {n : ℕ} (x y : coHomK n) → (x +ₖ y) ≡ (y +ₖ x)
commₖ {n = n} x y i = ΩKn+1→Kn (EH-instance (Kn→ΩKn+1 n x) (Kn→ΩKn+1 n y) i)
  where
  EH-instance : (p q : typ (Ω ((∥ S₊ (suc n) ∥ (2 + (suc n))) , ∣ north ∣))) → p ∙ q ≡ q ∙ p
  EH-instance = transport (λ i → (p q : K-Id n (~ i)) → p ∙ q ≡ q ∙ p)
                          λ p q → Eckmann-Hilton 0 p q
    where
    K-Id : (n : HLevel) → typ (Ω (hLevelTrunc (3 + n) (S₊ (1 + n)) , ∣ north ∣)) ≡ typ ((Ω^ 2) (hLevelTrunc (4 + n) (S₊ (2 + n)) , ∣  north ∣ ))
    K-Id n = (λ i → typ (Ω (isoToPath (Iso2-Kn-ΩKn+1 (suc n)) i , hcomp (λ k → λ { (i = i0) → ∣ north ∣
                                                                                ;  (i = i1) → transportRefl (λ j → ∣ rCancel (merid north) k j ∣) k})
                                                                         (transp (λ j → isoToPath (Iso2-Kn-ΩKn+1 (suc n)) (i ∧ j)) (~ i)  ∣ north ∣))))

-distrₖ : {n : ℕ} → (x y : coHomK n) → -ₖ (x +ₖ y) ≡ (-ₖ x) +ₖ (-ₖ y)
-distrₖ {n = n} x y = (λ i → ΩKn+1→Kn (sym (Kn→ΩKn+1 n (ΩKn+1→Kn (Kn→ΩKn+1 n x ∙ Kn→ΩKn+1 n y))))) ∙
                      (λ i → ΩKn+1→Kn (sym (Iso.rightInv (Iso2-Kn-ΩKn+1 n) (Kn→ΩKn+1 n x ∙ Kn→ΩKn+1 n y) i))) ∙
                      (λ i → ΩKn+1→Kn (symDistr (Kn→ΩKn+1 n x) (Kn→ΩKn+1 n y) i)) ∙
                      (λ i → ΩKn+1→Kn (Iso.rightInv (Iso2-Kn-ΩKn+1 n) (sym (Kn→ΩKn+1 n y)) (~ i) ∙ (Iso.rightInv (Iso2-Kn-ΩKn+1 n) (sym (Kn→ΩKn+1 n x)) (~ i)))) ∙
                      commₖ (-ₖ y) (-ₖ x)

---- Group structure of cohomology groups ---

private
  § : isSet ∥ A ∥₂
  § = setTruncIsSet

_+ₕ_ : {n : ℕ} → coHom n A → coHom n A → coHom n A
_+ₕ_ = sElim2 (λ _ _ → §) λ a b → ∣ (λ x → a x +ₖ b x) ∣₂

-ₕ  : {n : ℕ} → coHom n A → coHom n A
-ₕ  = sRec § λ a → ∣ (λ x → -ₖ a x) ∣₂

0ₕ : {n : ℕ} → coHom n A
0ₕ = ∣ (λ _ → 0ₖ) ∣₂

rUnitₕ : {n : ℕ} (x : coHom n A) → x +ₕ 0ₕ ≡ x
rUnitₕ  = sElim (λ _ → isOfHLevelPath 1 (§ _ _))
                λ a i → ∣ funExt (λ x → rUnitₖ (a x)) i ∣₂

lUnitₕ : {n : ℕ} (x : coHom n A) → 0ₕ +ₕ x ≡ x
lUnitₕ = sElim (λ _ → isOfHLevelPath 1 (§ _ _))
               λ a i → ∣ funExt (λ x → lUnitₖ (a x)) i ∣₂

rCancelₕ : {n : ℕ} (x : coHom n A) → x +ₕ (-ₕ x) ≡ 0ₕ
rCancelₕ = sElim (λ _ → isOfHLevelPath 1 (§ _ _))
                 λ a i → ∣ funExt (λ x → rCancelₖ (a x)) i ∣₂

lCancelₕ : {n : ℕ} (x : coHom n A) → (-ₕ x) +ₕ x  ≡ 0ₕ
lCancelₕ = sElim (λ _ → isOfHLevelPath 1 (§ _ _))
                 λ a i → ∣ funExt (λ x → lCancelₖ (a x)) i ∣₂

assocₕ : {n : ℕ} (x y z : coHom n A) → ((x +ₕ y) +ₕ z) ≡ (x +ₕ (y +ₕ z))
assocₕ = elim3 (λ _ _ _ → isOfHLevelPath 1 (§ _ _))
               λ a b c i → ∣ funExt (λ x → assocₖ (a x) (b x) (c x)) i ∣₂

commₕ : {n : ℕ} (x y : coHom n A) → (x +ₕ y) ≡ (y +ₕ x)
commₕ {n = n} = sElim2 (λ _ _ → isOfHLevelPath 1 (§ _ _))
                       λ a b i → ∣ funExt (λ x → commₖ (a x) (b x)) i ∣₂

---- Group structure of reduced cohomology groups (in progress - might need K to compute properly first) ---

+ₕ∙ : {A : Pointed ℓ} (n : ℕ) → coHomRed n A → coHomRed n A → coHomRed n A
+ₕ∙ zero = sElim2 (λ _ _ → §) λ { (a , pa) (b , pb) → ∣ (λ x → a x +ₖ b x) , (λ i → (pa i +ₖ pb i)) ∣₂ }
+ₕ∙ (suc n) = sElim2 (λ _ _ → §) λ { (a , pa) (b , pb) → ∣ (λ x → a x +ₖ b x) , (λ i → pa i +ₖ pb i) ∙ lUnitₖ 0ₖ ∣₂ }

-ₕ∙  : {A : Pointed ℓ} (n : ℕ) → coHomRed n A → coHomRed n A
-ₕ∙ zero = sRec § λ {(a , pt) → ∣ (λ x → -ₖ a x ) , (λ i → -ₖ (pt i)) ∣₂}
-ₕ∙ (suc zero) = sRec § λ {(a , pt) → ∣ (λ x → -ₖ a x ) , (λ i → -ₖ (pt i)) ∙ (λ i → ΩKn+1→Kn (sym (Kn→ΩKn+10ₖ (suc zero) i))) ∣₂}
-ₕ∙ (suc (suc n)) = sRec § λ {(a , pt) → ∣ (λ x → -ₖ a x ) , (λ i → -ₖ (pt i)) ∙
                                                              (λ i → ΩKn+1→Kn (sym (Kn→ΩKn+10ₖ (suc (suc n)) i))) ∙
                                                              (λ i → ΩKn+1→Kn (Kn→ΩKn+10ₖ (suc (suc n)) (~ i))) ∙
                                                              Iso.leftInv (Iso2-Kn-ΩKn+1 (suc (suc n))) ∣ north ∣ ∣₂}

0ₕ∙ : {A : Pointed ℓ} (n : ℕ) → coHomRed n A
0ₕ∙ zero = ∣ (λ _ → 0ₖ) , refl ∣₂
0ₕ∙ (suc n) = ∣ (λ _ → 0ₖ) , refl ∣₂


module Text.Lex where

open import Prelude

record TokenDFA {s} (A : Set) (Tok : Set) : Set (lsuc s) where
  field
    State   : Set s
    initial : State
    accept  : State → Maybe Tok
    consume : A → State → Maybe State

instance
  FunctorTokenDFA : ∀ {s} {A : Set} → Functor (TokenDFA {s = s} A)
  TokenDFA.State   (fmap {{FunctorTokenDFA}} f dfa) = TokenDFA.State dfa
  TokenDFA.initial (fmap {{FunctorTokenDFA}} f dfa) = TokenDFA.initial dfa
  TokenDFA.accept  (fmap {{FunctorTokenDFA}} f dfa) s = f <$> TokenDFA.accept dfa s
  TokenDFA.consume (fmap {{FunctorTokenDFA}} f dfa) = TokenDFA.consume dfa

keywordToken : {A : Set} {{EqA : Eq A}} → List A → TokenDFA A ⊤
TokenDFA.State   (keywordToken {A = A} kw)    = List A
TokenDFA.initial (keywordToken kw)            = kw
TokenDFA.accept  (keywordToken kw) []         = just _
TokenDFA.accept  (keywordToken kw) (_ ∷ _)    = nothing
TokenDFA.consume (keywordToken kw) _ []       = nothing
TokenDFA.consume (keywordToken kw) y (x ∷ xs) = ifYes (x == y) then just xs else nothing

matchToken : ∀ {A : Set} (p : A → Bool) → TokenDFA A (List (Σ A (IsTrue ∘ p)))
TokenDFA.State (matchToken {A = A} p) = List (Σ A (IsTrue ∘ p))
TokenDFA.initial (matchToken _)       = []
TokenDFA.accept  (matchToken _) xs    = just (reverse xs)
TokenDFA.consume (matchToken p) x xs  = if′ p x then just ((x , it) ∷ xs) else nothing

natToken : TokenDFA Char Nat
natToken = pNat <$> matchToken isDigit
  where pNat = foldl (λ { n (d , _) → 10 * n + (charToNat d - charToNat '0') }) 0

identToken : ∀ {A : Set} → (A → Bool) → (A → Bool) → TokenDFA A (List A)
TokenDFA.State   (identToken {A = A} _ _) = Maybe (List A)
TokenDFA.initial (identToken _ _) = nothing
TokenDFA.accept  (identToken _ _) = fmap reverse
TokenDFA.consume (identToken first _) x nothing   = if first x then just (just [ x ])    else nothing
TokenDFA.consume (identToken _ then)  x (just xs) = if then  x then just (just (x ∷ xs)) else nothing

module _ {s : Level} {A Tok : Set} where
  private
    DFA = TokenDFA {s = s} A Tok
    open TokenDFA

    init : DFA → Σ DFA State
    init d = d , initial d

    feed : A → Σ DFA State → Either DFA (Σ DFA State)
    feed x (d , s) = maybe (left d) (right ∘ _,_ d) (consume d x s)

    accepts : List (Σ DFA State) → List Tok
    accepts = concatMap (λ { (d , s) → maybe [] [_] (accept d s) })

    tokenize-loop : List DFA → List (Σ DFA State) → List A → List Tok
    tokenize-loop idle active [] =
      case accepts active of λ where
        []      → []  -- not quite right if there are active DFAs
        (t ∷ _) → [ t ]
    tokenize-loop idle [] (x ∷ xs) =
      flip uncurry (partitionMap (feed x) (map init idle)) λ where
        idle₁ []      → []
        idle₁ active₁ → tokenize-loop idle₁ active₁ xs
    tokenize-loop idle active (x ∷ xs) =
      flip uncurry (partitionMap (feed x) active) λ where
        idle₁ [] →
          case accepts active of λ where
            []      → []
            (t ∷ _) →
              flip uncurry (partitionMap (feed x) (map init (idle ++ idle₁))) λ where
                _ []          → t ∷ []
                idle₂ active₂ → t List.∷ tokenize-loop idle₂ active₂ xs
        idle₁ active₁ → tokenize-loop (idle ++ idle₁) active₁ xs

  tokenize : List (TokenDFA {s = s} A Tok) → List A → List Tok
  tokenize dfas xs = tokenize-loop dfas [] xs


{-
This second-order term syntax was created from the following second-order syntax description:

syntax CommRing | CR

type
  * : 0-ary

term
  zero : * | 𝟘
  add  : *  *  ->  * | _⊕_ l20
  one  : * | 𝟙
  mult : *  *  ->  * | _⊗_ l30
  neg  : *  ->  * | ⊖_ r50

theory
  (𝟘U⊕ᴸ) a |> add (zero, a) = a
  (𝟘U⊕ᴿ) a |> add (a, zero) = a
  (⊕A) a b c |> add (add(a, b), c) = add (a, add(b, c))
  (⊕C) a b |> add(a, b) = add(b, a)
  (𝟙U⊗ᴸ) a |> mult (one, a) = a
  (𝟙U⊗ᴿ) a |> mult (a, one) = a
  (⊗A) a b c |> mult (mult(a, b), c) = mult (a, mult(b, c))
  (⊗D⊕ᴸ) a b c |> mult (a, add (b, c)) = add (mult(a, b), mult(a, c))
  (⊗D⊕ᴿ) a b c |> mult (add (a, b), c) = add (mult(a, c), mult(b, c))
  (𝟘X⊗ᴸ) a |> mult (zero, a) = zero
  (𝟘X⊗ᴿ) a |> mult (a, zero) = zero
  (⊖N⊕ᴸ) a |> add (neg (a), a) = zero
  (⊖N⊕ᴿ) a |> add (a, neg (a)) = zero
  (⊗C) a b |> mult(a, b) = mult(b, a)
-}

module CommRing.Syntax where

open import SOAS.Common
open import SOAS.Context
open import SOAS.Variable
open import SOAS.Families.Core
open import SOAS.Construction.Structure
open import SOAS.ContextMaps.Inductive

open import SOAS.Metatheory.Syntax

open import CommRing.Signature

private
  variable
    Γ Δ Π : Ctx
    α : *T
    𝔛 : Familyₛ

-- Inductive term declaration
module CR:Terms (𝔛 : Familyₛ) where

  data CR : Familyₛ where
    var  : ℐ ⇾̣ CR
    mvar : 𝔛 α Π → Sub CR Π Γ → CR α Γ

    𝟘   : CR * Γ
    _⊕_ : CR * Γ → CR * Γ → CR * Γ
    𝟙   : CR * Γ
    _⊗_ : CR * Γ → CR * Γ → CR * Γ
    ⊖_  : CR * Γ → CR * Γ

  infixl 20 _⊕_
  infixl 30 _⊗_
  infixr 50 ⊖_

  open import SOAS.Metatheory.MetaAlgebra ⅀F 𝔛

  CRᵃ : MetaAlg CR
  CRᵃ = record
    { 𝑎𝑙𝑔 = λ where
      (zeroₒ ⋮ _)     → 𝟘
      (addₒ  ⋮ a , b) → _⊕_ a b
      (oneₒ  ⋮ _)     → 𝟙
      (multₒ ⋮ a , b) → _⊗_ a b
      (negₒ  ⋮ a)     → ⊖_  a
    ; 𝑣𝑎𝑟 = var ; 𝑚𝑣𝑎𝑟 = λ 𝔪 mε → mvar 𝔪 (tabulate mε) }

  module CRᵃ = MetaAlg CRᵃ

  module _ {𝒜 : Familyₛ}(𝒜ᵃ : MetaAlg 𝒜) where

    open MetaAlg 𝒜ᵃ

    𝕤𝕖𝕞 : CR ⇾̣ 𝒜
    𝕊 : Sub CR Π Γ → Π ~[ 𝒜 ]↝ Γ
    𝕊 (t ◂ σ) new = 𝕤𝕖𝕞 t
    𝕊 (t ◂ σ) (old v) = 𝕊 σ v
    𝕤𝕖𝕞 (mvar 𝔪 mε) = 𝑚𝑣𝑎𝑟 𝔪 (𝕊 mε)
    𝕤𝕖𝕞 (var v) = 𝑣𝑎𝑟 v

    𝕤𝕖𝕞  𝟘        = 𝑎𝑙𝑔 (zeroₒ ⋮ tt)
    𝕤𝕖𝕞 (_⊕_ a b) = 𝑎𝑙𝑔 (addₒ  ⋮ 𝕤𝕖𝕞 a , 𝕤𝕖𝕞 b)
    𝕤𝕖𝕞  𝟙        = 𝑎𝑙𝑔 (oneₒ  ⋮ tt)
    𝕤𝕖𝕞 (_⊗_ a b) = 𝑎𝑙𝑔 (multₒ ⋮ 𝕤𝕖𝕞 a , 𝕤𝕖𝕞 b)
    𝕤𝕖𝕞 (⊖_  a)   = 𝑎𝑙𝑔 (negₒ  ⋮ 𝕤𝕖𝕞 a)

    𝕤𝕖𝕞ᵃ⇒ : MetaAlg⇒ CRᵃ 𝒜ᵃ 𝕤𝕖𝕞
    𝕤𝕖𝕞ᵃ⇒ = record
      { ⟨𝑎𝑙𝑔⟩ = λ{ {t = t} → ⟨𝑎𝑙𝑔⟩ t }
      ; ⟨𝑣𝑎𝑟⟩ = refl
      ; ⟨𝑚𝑣𝑎𝑟⟩ = λ{ {𝔪 = 𝔪}{mε} → cong (𝑚𝑣𝑎𝑟 𝔪) (dext (𝕊-tab mε)) }  }
      where
      open ≡-Reasoning
      ⟨𝑎𝑙𝑔⟩ : (t : ⅀ CR α Γ) → 𝕤𝕖𝕞 (CRᵃ.𝑎𝑙𝑔 t) ≡ 𝑎𝑙𝑔 (⅀₁ 𝕤𝕖𝕞 t)
      ⟨𝑎𝑙𝑔⟩ (zeroₒ ⋮ _) = refl
      ⟨𝑎𝑙𝑔⟩ (addₒ  ⋮ _) = refl
      ⟨𝑎𝑙𝑔⟩ (oneₒ  ⋮ _) = refl
      ⟨𝑎𝑙𝑔⟩ (multₒ ⋮ _) = refl
      ⟨𝑎𝑙𝑔⟩ (negₒ  ⋮ _) = refl

      𝕊-tab : (mε : Π ~[ CR ]↝ Γ)(v : ℐ α Π) → 𝕊 (tabulate mε) v ≡ 𝕤𝕖𝕞 (mε v)
      𝕊-tab mε new = refl
      𝕊-tab mε (old v) = 𝕊-tab (mε ∘ old) v

    module _ (g : CR ⇾̣ 𝒜)(gᵃ⇒ : MetaAlg⇒ CRᵃ 𝒜ᵃ g) where

      open MetaAlg⇒ gᵃ⇒

      𝕤𝕖𝕞! : (t : CR α Γ) → 𝕤𝕖𝕞 t ≡ g t
      𝕊-ix : (mε : Sub CR Π Γ)(v : ℐ α Π) → 𝕊 mε v ≡ g (index mε v)
      𝕊-ix (x ◂ mε) new = 𝕤𝕖𝕞! x
      𝕊-ix (x ◂ mε) (old v) = 𝕊-ix mε v
      𝕤𝕖𝕞! (mvar 𝔪 mε) rewrite cong (𝑚𝑣𝑎𝑟 𝔪) (dext (𝕊-ix mε))
        = trans (sym ⟨𝑚𝑣𝑎𝑟⟩) (cong (g ∘ mvar 𝔪) (tab∘ix≈id mε))
      𝕤𝕖𝕞! (var v) = sym ⟨𝑣𝑎𝑟⟩

      𝕤𝕖𝕞! 𝟘 = sym ⟨𝑎𝑙𝑔⟩
      𝕤𝕖𝕞! (_⊕_ a b) rewrite 𝕤𝕖𝕞! a | 𝕤𝕖𝕞! b = sym ⟨𝑎𝑙𝑔⟩
      𝕤𝕖𝕞! 𝟙 = sym ⟨𝑎𝑙𝑔⟩
      𝕤𝕖𝕞! (_⊗_ a b) rewrite 𝕤𝕖𝕞! a | 𝕤𝕖𝕞! b = sym ⟨𝑎𝑙𝑔⟩
      𝕤𝕖𝕞! (⊖_ a) rewrite 𝕤𝕖𝕞! a = sym ⟨𝑎𝑙𝑔⟩

-- Syntax instance for the signature
CR:Syn : Syntax
CR:Syn = record
  { ⅀F = ⅀F
  ; ⅀:CS = ⅀:CompatStr
  ; mvarᵢ = CR:Terms.mvar
  ; 𝕋:Init = λ 𝔛 → let open CR:Terms 𝔛 in record
    { ⊥ = CR ⋉ CRᵃ
    ; ⊥-is-initial = record { ! = λ{ {𝒜 ⋉ 𝒜ᵃ} → 𝕤𝕖𝕞 𝒜ᵃ ⋉ 𝕤𝕖𝕞ᵃ⇒ 𝒜ᵃ }
    ; !-unique = λ{ {𝒜 ⋉ 𝒜ᵃ} (f ⋉ fᵃ⇒) {x = t} → 𝕤𝕖𝕞! 𝒜ᵃ f fᵃ⇒ t } } } }

-- Instantiation of the syntax and metatheory
open Syntax CR:Syn public
open CR:Terms public
open import SOAS.Families.Build public
open import SOAS.Syntax.Shorthands CRᵃ public
open import SOAS.Metatheory CR:Syn public


{-# OPTIONS --safe #-}

open import Cubical.Foundations.Everything renaming (Iso to _≅_)
open import Cubical.Data.List
open import Cubical.Data.Unit
open import Cubical.Data.Prod hiding (map)
open import Cubical.Data.Nat

module Cubical.Data.List.Dependent where

open _≅_

data ListP {ℓA ℓB} {A : Type ℓA} (B : A → Type ℓB) : (as : List A) → Type (ℓ-max ℓA ℓB) where
  [] : ListP B []
  _∷_ : {x : A} (y : B x) {xs : List A} (ys : ListP B xs) → ListP B (x ∷ xs)

-- Represent ListP via known operations in order to derive properties more easily.
RepListP : ∀ {ℓA ℓB} {A : Type ℓA} (B : A → Type ℓB) (as : List A) → Type (ℓ-max ℓA ℓB)
RepListP B [] = Lift Unit
RepListP B (a ∷ as) = B a × RepListP B as

isoRepListP : ∀ {ℓA ℓB} {A : Type ℓA} (B : A → Type ℓB) (as : List A) → ListP B as ≅ RepListP B as
fun (isoRepListP B []) bs = lift tt
inv (isoRepListP B []) u = []
rightInv (isoRepListP B []) u = refl
leftInv (isoRepListP B []) [] = refl
fun (isoRepListP B (a ∷ as)) (b ∷ bs) = b , fun (isoRepListP B as) bs
inv (isoRepListP B (a ∷ as)) (b , br) = b ∷ inv (isoRepListP B as) br
rightInv (isoRepListP B (a ∷ as)) (b , br) i = b , rightInv (isoRepListP B as) br i
leftInv (isoRepListP B (a ∷ as)) (b ∷ bs) i = b ∷ leftInv (isoRepListP B as) bs i

equivRepListP : ∀ {ℓA ℓB} {A : Type ℓA} (B : A → Type ℓB) (as : List A) → ListP B as ≃ RepListP B as
equivRepListP B as = isoToEquiv (isoRepListP B as)

pathRepListP : ∀ {ℓA ℓB} {A : Type ℓA} (B : A → Type ℓB) (as : List A) → ListP B as ≡ RepListP B as
pathRepListP B as = ua (equivRepListP B as)

private
  isOfHLevelSucSuc-RepListP : ∀ {ℓA ℓB} (n : HLevel)
    → {A : Type ℓA}
    → {B : A → Type ℓB} → ((a : A) → isOfHLevel (suc (suc n)) (B a))
    → (as : List A)
    → isOfHLevel (suc (suc n)) (RepListP B as)
  isOfHLevelSucSuc-RepListP n isHB [] = isOfHLevelLift (suc (suc n)) (isOfHLevelUnit (suc (suc n)))
  isOfHLevelSucSuc-RepListP n isHB (a ∷ as) = isOfHLevelProd (suc (suc n)) (isHB a) (isOfHLevelSucSuc-RepListP n isHB as)

isOfHLevelSucSuc-ListP : ∀ {ℓA ℓB} (n : HLevel)
  → {A : Type ℓA}
  → {B : A → Type ℓB} → ((a : A) → isOfHLevel (suc (suc n)) (B a))
  → {as : List A}
  → isOfHLevel (suc (suc n)) (ListP B as)
isOfHLevelSucSuc-ListP n {A} {B} isHB {as} =
  subst⁻ (isOfHLevel (suc (suc n))) (pathRepListP B as) (isOfHLevelSucSuc-RepListP n isHB as)

--------------------------

mapP : ∀ {ℓA ℓA' ℓB ℓB'} {A : Type ℓA} {A' : Type ℓA'} {B : A → Type ℓB} {B' : A' → Type ℓB'}
  (f : A → A') (g : (a : A) → B a → B' (f a)) → ∀ as → ListP B as → ListP B' (map f as)
mapP f g [] [] = []
mapP f g (a ∷ as) (b ∷ bs) = g _ b ∷ mapP f g as bs

mapOverIdfun : ∀ {ℓA ℓB ℓB'} {A : Type ℓA} {B : A → Type ℓB} {B' : A → Type ℓB'}
  (g : (a : A) → B a → B' a) → ∀ as → ListP B as → ListP B' as
mapOverIdfun g [] [] = []
mapOverIdfun g (a ∷ as) (b ∷ bs) = g a b ∷ mapOverIdfun g as bs

mapOverIdfun-idfun : ∀ {ℓA ℓB} {A : Type ℓA} {B : A → Type ℓB} as → mapOverIdfun (λ a → idfun _) as ≡ (idfun (ListP B as))
mapOverIdfun-idfun [] i [] = []
mapOverIdfun-idfun (a ∷ as) i (b ∷ bs) = b ∷ mapOverIdfun-idfun as i bs

mapOverIdfun-∘ : ∀ {ℓA ℓB ℓB' ℓB''} {A : Type ℓA} {B : A → Type ℓB} {B' : A → Type ℓB'} {B'' : A → Type ℓB''}
  (h : (a : A) → B' a → B'' a) (g : (a : A) → B a → B' a) → ∀ as
  → mapOverIdfun (λ a → h a ∘ g a) as ≡ mapOverIdfun h as ∘ mapOverIdfun g as
mapOverIdfun-∘ h g [] i [] = []
mapOverIdfun-∘ h g (a ∷ as) i (b ∷ bs) = h a (g a b) ∷ mapOverIdfun-∘ h g as i bs


module CTL.Modalities.AN where

open import FStream.Core
open import Library

-- Always (in) Next : p ⊧ φ ⇔ p[1] ⊧ φ
AN' : ∀ {ℓ₁ ℓ₂} {C : Container ℓ₁}
    → FStream' C (Set ℓ₂) → Set (ℓ₁ ⊔ ℓ₂)
AN' props = APred head (inF (tail props))

AN : ∀ {ℓ₁ ℓ₂} {C : Container ℓ₁}
   → FStream C (Set ℓ₂) → Set (ℓ₁ ⊔ ℓ₂)
AN props = APred AN' (inF props)

mutual
  AN'ₛ : ∀ {i} {ℓ₁ ℓ₂} {C : Container ℓ₁}
       → FStream' C (Set ℓ₂) → FStream' {i} C (Set (ℓ₁ ⊔ ℓ₂))
  head (AN'ₛ props) = AN' props
  tail (AN'ₛ props) = ANₛ (tail props)

  ANₛ : ∀ {i} {ℓ₁ ℓ₂} {C : Container ℓ₁}
      → FStream C (Set ℓ₂) → FStream {i} C (Set (ℓ₁ ⊔ ℓ₂))
  inF (ANₛ props) = fmap AN'ₛ (inF props)


module HelpDefs where

-- open import Relation.Binary.PropositionalEquality

open import StdLibStuff

open import Syntax
open import FSC
open import STT
open import DerivedProps

m-weak-h : ∀ {n} Γ₁ Γ₂ {t} → {Γ₃ : Ctx n} → Form (Γ₃ ++ Γ₁) t → Form (Γ₃ ++ (Γ₂ r++ Γ₁)) t
m-weak-h {n} Γ₁ ε = λ f → f
m-weak-h {n} Γ₁ (t' ∷ Γ₂) {t} {Γ₃} = λ f → m-weak-h {n} (t' ∷ Γ₁) Γ₂ {t} {Γ₃} (weak-i Γ₃ Γ₁ f)

m-weak2 : ∀ {n Γ₁} Γ₂ → {t : Type n} → Form Γ₁ t → Form (Γ₂ ++ Γ₁) t
m-weak2 {n} {Γ₁} ε f = f
m-weak2 {n} {Γ₁} (t' ∷ Γ₂) f = weak (m-weak2 {n} {Γ₁} Γ₂ f)

hypos-i-h : ∀ {n Γ-t₁ Γ-t₂ Γ-t₃} → FSC-Ctx n Γ-t₁ → Form (Γ-t₃ ++ (Γ-t₂ r++ Γ-t₁)) $o → Form (Γ-t₃ ++ (Γ-t₂ r++ Γ-t₁)) $o
hypos-i-h ε = λ f → f
hypos-i-h {n} {Γ-t₁} {Γ-t₂} {Γ-t₃} (h ∷h Γ) = λ f → hypos-i-h {n} {Γ-t₁} {Γ-t₂} {Γ-t₃} Γ (m-weak-h Γ-t₁ Γ-t₂ {$o} {Γ-t₃} (m-weak2 Γ-t₃ h) => f)
hypos-i-h {n} {.(t ∷ _)} {Γ-t₂} {Γ-t₃} (t ∷ Γ) = hypos-i-h {n} {_} {t ∷ Γ-t₂} {Γ-t₃} Γ

m-weak : ∀ {n Γ₁ Γ₂ t} → Form Γ₁ t → Form (Γ₂ r++ Γ₁) t
m-weak {n} {Γ₁} {Γ₂} {t} = m-weak-h {n} Γ₁ Γ₂ {t} {ε}

hypos-i : ∀ {n Γ-t₁ Γ-t₂} → FSC-Ctx n Γ-t₁ → Form (Γ-t₂ r++ Γ-t₁) $o → Form (Γ-t₂ r++ Γ-t₁) $o
hypos-i {n} {Γ-t₁} {Γ-t₂} = hypos-i-h {n} {Γ-t₁} {Γ-t₂} {ε}

hypos : ∀ {n Γ-t} → FSC-Ctx n Γ-t → Form Γ-t $o → Form Γ-t $o
hypos {n} {Γ-t} = hypos-i {n} {Γ-t} {ε}

-- Props

eq-1-i : ∀ {n} Γ₁ Γ₂ → (t : Type n) (X₁ : Form (t ∷ Γ₁) $o) (X₂ : Form Γ₁ $o) →
         X₁ ≡ weak-i {n} {$o} {t} ε Γ₁ X₂ →
         m-weak-h Γ₁ Γ₂ {$o} {t ∷ ε} X₁ ≡ weak-i {n} {$o} {t} ε (Γ₂ r++ Γ₁) (m-weak-h Γ₁ Γ₂ {$o} {ε} X₂)
eq-1-i Γ₁ ε t X₁ X₂ = λ h → h
eq-1-i Γ₁ (u ∷ Γ₂) t X₁ X₂ = λ h → eq-1-i (u ∷ Γ₁) Γ₂ t (weak-i {_} {$o} {u} (t ∷ ε) Γ₁ X₁) (weak-i {_} {$o} {u} ε Γ₁ X₂) (trans (cong (weak-i {_} {$o} {u} (t ∷ ε) Γ₁) h) (weak-weak-p-1 Γ₁ t u $o X₂))

eq-1 : ∀ {n} Γ₁ Γ₂ → (t : Type n) (X : Form Γ₁ $o) →
       m-weak-h Γ₁ Γ₂ {$o} {t ∷ ε} (weak-i {n} {$o} {t} ε Γ₁ X) ≡ weak-i {n} {$o} {t} ε (Γ₂ r++ Γ₁) (m-weak-h Γ₁ Γ₂ {$o} {ε} X)
eq-1 Γ₁ Γ₂ t X = eq-1-i Γ₁ Γ₂ t (weak-i {_} {$o} {t} ε Γ₁ X) X refl

eq-2-2 : ∀ n → (Γ-t Γ' : Ctx n) → (t : Type n) (X₁ : Form (t ∷ Γ-t) $o) (X₂ : Form Γ-t $o) →
 weak-i {n} {$o} {t} ε Γ-t X₂ ≡ X₁ →
 (h₂ : t ∷ (Γ' r++ Γ-t) ≡ (Γ' ++ (t ∷ ε)) r++ Γ-t) →
 m-weak-h Γ-t (Γ' ++ (t ∷ ε)) {$o} {ε} X₂ ≡
 subst (λ z → Form z $o) h₂ (m-weak-h Γ-t Γ' {$o} {t ∷ ε} X₁)
eq-2-2 n Γ-t ε t X₁ X₂ h refl = h
eq-2-2 n Γ-t (u ∷ Γ') t X₁ X₂ h h₂ = eq-2-2 n (u ∷ Γ-t) Γ' t (weak-i {_} {$o} {u} (t ∷ ε) Γ-t X₁) (weak-i {_} {$o} {u} ε Γ-t X₂) (
 trans (sym (weak-weak-p-1 Γ-t t u $o X₂)) (cong (weak-i {_} {$o} {u} (t ∷ ε) Γ-t) h))
 h₂

eq-2-1 : ∀ n → (Γ-t Γ' : Ctx n) → (t : Type n) (h : Form Γ-t $o) (F : Form (t ∷ (Γ' r++ Γ-t)) $o) →
         (Γ'' : Ctx n)
         (h₁₁ : Γ'' ≡ (Γ' ++ (t ∷ ε)) r++ Γ-t) →
         (h₁₂ : t ∷ (Γ' r++ Γ-t) ≡  Γ'') →
         app (app A (app N (m-weak-h Γ-t (Γ' ++ (t ∷ ε)) {$o} {ε} h))) (subst (λ z → Form z $o) h₁₁ (subst (λ z → Form z $o) h₁₂ F)) ≡
         subst (λ z → Form z $o) h₁₁ (app (app A (app N (subst (λ z → Form z $o) h₁₂ (m-weak-h Γ-t Γ' {$o} {t ∷ ε} (weak-i ε Γ-t h))))) (subst (λ z → Form z $o) h₁₂ F))
eq-2-1 n Γ-t Γ' t h F .((Γ' ++ (t ∷ ε)) r++ Γ-t) refl h₁₂ = cong (λ z → app (app A (app N z)) (subst (λ z → Form z $o) h₁₂ F)) (eq-2-2 n Γ-t Γ' t (weak-i {_} {$o} {t} ε Γ-t h) h refl h₁₂)

eq-2-i : ∀ {n} Γ-t → (t : Type n) (Γ : FSC-Ctx n Γ-t) (Γ' : Ctx n) (F : Form (t ∷ (Γ' r++ Γ-t)) $o) →
       (h₁ : t ∷ (ε ++ (Γ' r++ Γ-t)) ≡ (Γ' ++ (t ∷ ε)) r++ Γ-t) →
       hypos-i-h {n} {Γ-t} {Γ' ++ (t ∷ ε)} {ε} Γ (subst (λ z → Form z $o) h₁ F) ≡ subst (λ z → Form z $o) h₁ (hypos-i-h {n} {Γ-t} {Γ'} {t ∷ ε} Γ F)
eq-2-i .ε t ε Γ' F h₁ = refl
eq-2-i .(u ∷ _) t (u ∷ Γ) Γ' F h₁ = eq-2-i _ t Γ (u ∷ Γ') F h₁
eq-2-i Γ-t t (h ∷h Γ) Γ' F h₁ = trans (cong (hypos-i-h {_} {Γ-t} {Γ' ++ (t ∷ ε)} {ε} Γ) (eq-2-1 _ Γ-t Γ' t h F _ h₁ refl)) (eq-2-i Γ-t t Γ Γ' (app (app A (app N (m-weak-h Γ-t Γ' {$o} {t ∷ ε} (weak-i ε Γ-t h)))) F) h₁)

eq-2 : ∀ {n Γ-t} → (t : Type n) (Γ : FSC-Ctx n Γ-t) (F : Form (t ∷ Γ-t) $o) →
       hypos-i-h {n} {Γ-t} {t ∷ ε} {ε} Γ F ≡ hypos-i-h {n} {Γ-t} {ε} {t ∷ ε} Γ F
eq-2 {_} {Γ-t} t Γ F = eq-2-i {_} Γ-t t Γ ε F refl

traverse-hypos-i : ∀ {n Γ-t₁ Γ-t₂} → (F G : Form (Γ-t₂ r++ Γ-t₁) $o) (Γ : FSC-Ctx n Γ-t₁) → (⊢ (F => G)) → ⊢ (hypos-i {n} {Γ-t₁} {Γ-t₂} Γ F => hypos-i {n} {Γ-t₁} {Γ-t₂} Γ G)
traverse-hypos-i F G ε h = h
traverse-hypos-i {n} {Γ-t₁} {Γ-t₂} F G (X ∷h Γ) h = traverse-hypos-i {n} {Γ-t₁} {Γ-t₂} (m-weak {n} {Γ-t₁} {Γ-t₂} X => F) (m-weak {n} {Γ-t₁} {Γ-t₂} X => G) Γ (inf-V ax-4-s h)
traverse-hypos-i {n} {.(t ∷ _)} {Γ-t₂} F G (t ∷ Γ) h = traverse-hypos-i {n} {_} {t ∷ Γ-t₂} F G Γ h

traverse-hypos : ∀ {n Γ-t} → (F G : Form Γ-t $o) (Γ : FSC-Ctx n Γ-t) → (⊢ (F => G)) → ⊢ (hypos Γ F => hypos Γ G)
traverse-hypos {n} {Γ-t} = traverse-hypos-i {n} {Γ-t} {ε}

traverse-hypos-pair-I-i : ∀ {n Γ-t₁ Γ-t₂} → (F G : Form (Γ-t₂ r++ Γ-t₁) $o) (Γ : FSC-Ctx n Γ-t₁) → ⊢ (hypos-i {n} {Γ-t₁} {Γ-t₂} Γ F => (hypos-i {n} {Γ-t₁} {Γ-t₂} Γ G => hypos-i {n} {Γ-t₁} {Γ-t₂} Γ (F & G)))
traverse-hypos-pair-I-i F G ε = lemb1 F G
traverse-hypos-pair-I-i {n} {Γ-t₁} {Γ-t₂} F G (x ∷h Γ) = inf-V (inf-V (ax-4-s {_} {_} {(hypos-i {n} {Γ-t₁} {Γ-t₂} Γ (m-weak {n} {Γ-t₁} {Γ-t₂} x => G) => (hypos-i {n} {Γ-t₁} {Γ-t₂} Γ ((m-weak {n} {Γ-t₁} {Γ-t₂} x => F) & (m-weak {n} {Γ-t₁} {Γ-t₂} x => G))))})
 (inf-V (ax-4-s {_} {_} {(hypos-i {n} {Γ-t₁} {Γ-t₂} Γ ((m-weak {n} {Γ-t₁} {Γ-t₂} x => F) & (m-weak {n} {Γ-t₁} {Γ-t₂} x => G)))}) (traverse-hypos-i {n} {Γ-t₁} {Γ-t₂} ((m-weak {n} {Γ-t₁} {Γ-t₂} x => F) & (m-weak {n} {Γ-t₁} {Γ-t₂} x => G)) (m-weak {n} {Γ-t₁} {Γ-t₂} x => (F & G)) Γ (lem2 (m-weak {n} {Γ-t₁} {Γ-t₂} x) F G))))
 (traverse-hypos-pair-I-i {n} {Γ-t₁} {Γ-t₂} (m-weak {n} {Γ-t₁} {Γ-t₂} x => F) (m-weak {n} {Γ-t₁} {Γ-t₂} x => G) Γ)
traverse-hypos-pair-I-i {n} {.(t ∷ _)} {Γ-t₂} F G (t ∷ Γ) = traverse-hypos-pair-I-i {n} {_} {t ∷ Γ-t₂} F G Γ

traverse-hypos-pair-I : ∀ {n Γ-t} → (F G : Form Γ-t $o) (Γ : FSC-Ctx n Γ-t) → ⊢ (hypos Γ F => (hypos Γ G => hypos Γ (F & G)))
traverse-hypos-pair-I {n} {Γ-t} = traverse-hypos-pair-I-i {n} {Γ-t} {ε}

traverse-hypos-elim-i : ∀ {n Γ-t₁ Γ-t₂} → (F : Form (Γ-t₂ r++ Γ-t₁) $o) (Γ : FSC-Ctx n Γ-t₁) (x : HVar Γ) → ⊢ (hypos-i {n} {Γ-t₁} {Γ-t₂} Γ (m-weak {n} {Γ-t₁} {Γ-t₂} (lookup-hyp Γ x) => F) => hypos-i {n} {Γ-t₁} {Γ-t₂} Γ F)
traverse-hypos-elim-i {n} {Γ-t₁} {Γ-t₂} F .(h ∷h Γ) (zero {._} {h} {Γ}) = traverse-hypos-i {n} {Γ-t₁} {Γ-t₂} (m-weak {n} {Γ-t₁} {Γ-t₂} h => (m-weak {n} {Γ-t₁} {Γ-t₂} h => F)) (m-weak {n} {Γ-t₁} {Γ-t₂} h => F) Γ (lem3 (m-weak {n} {Γ-t₁} {Γ-t₂} h) F)
traverse-hypos-elim-i {n} {Γ-t₁} {Γ-t₂} F .(h ∷h Γ) (succ {._} {h} {Γ} x) = inf-V ax-3-s (inf-V (inf-V (ax-4-s {_} {_} {~ (hypos-i {n} {Γ-t₁} {Γ-t₂} Γ (m-weak {n} {Γ-t₁} {Γ-t₂} (lookup-hyp Γ x) => (m-weak {n} {Γ-t₁} {Γ-t₂} h => F)))}) (inf-V (lem5 (hypos-i {n} {Γ-t₁} {Γ-t₂} Γ (m-weak {n} {Γ-t₁} {Γ-t₂} h => (m-weak {n} {Γ-t₁} {Γ-t₂} (lookup-hyp Γ x) => F))) (hypos-i {n} {Γ-t₁} {Γ-t₂} Γ (m-weak {n} {Γ-t₁} {Γ-t₂} (lookup-hyp Γ x) => (m-weak {n} {Γ-t₁} {Γ-t₂} h => F)))) (traverse-hypos-i {n} {Γ-t₁} {Γ-t₂} (m-weak {n} {Γ-t₁} {Γ-t₂} h => (m-weak {n} {Γ-t₁} {Γ-t₂} (lookup-hyp Γ x) => F)) (m-weak {n} {Γ-t₁} {Γ-t₂} (lookup-hyp Γ x) => (m-weak {n} {Γ-t₁} {Γ-t₂} h => F)) Γ (lem4 (m-weak {n} {Γ-t₁} {Γ-t₂} h) (m-weak {n} {Γ-t₁} {Γ-t₂} (lookup-hyp Γ x)) F)))) (inf-V ax-3-s (traverse-hypos-elim-i {n} {Γ-t₁} {Γ-t₂} (m-weak {n} {Γ-t₁} {Γ-t₂} h => F) Γ x)))
traverse-hypos-elim-i {n} {.(t ∷ _)} {Γ-t₂} F (t ∷ Γ) (skip x) = traverse-hypos-elim-i {n} {_} {t ∷ Γ-t₂} F Γ x

traverse-hypos-elim : ∀ {n Γ-t} → (F : Form Γ-t $o) (Γ : FSC-Ctx n Γ-t) (x : HVar Γ) → ⊢ (hypos Γ (lookup-hyp Γ x => F) => hypos Γ F)
traverse-hypos-elim {n} {Γ-t} = traverse-hypos-elim-i {n} {Γ-t} {ε}

traverse-hypos-use-i : ∀ {n Γ-t₁ Γ-t₂} → (F : Form (Γ-t₂ r++ Γ-t₁) $o) (Γ : FSC-Ctx n Γ-t₁) → ⊢ (F) → ⊢ (hypos-i {n} {Γ-t₁} {Γ-t₂} Γ F)
traverse-hypos-use-i F ε h = h
traverse-hypos-use-i {n} {Γ-t₁} {Γ-t₂} F (X ∷h Γ) h = traverse-hypos-use-i {n} {Γ-t₁} {Γ-t₂} (m-weak {n} {Γ-t₁} {Γ-t₂} X => F) Γ (inf-V ax-3-s (inf-V ax-2-s h))
traverse-hypos-use-i {n} {.(t ∷ _)} {Γ-t₂} F (t ∷ Γ) h = traverse-hypos-use-i {n} {_} {t ∷ Γ-t₂} F Γ h

traverse-hypos-use : ∀ {n Γ-t} → (F : Form Γ-t $o) (Γ : FSC-Ctx n Γ-t) → ⊢ (F) → ⊢ (hypos Γ F)
traverse-hypos-use {n} {Γ-t} = traverse-hypos-use-i {n} {Γ-t} {ε}

traverse-hypos2-i : ∀ {n Γ-t₁ Γ-t₂} → (F G H : Form (Γ-t₂ r++ Γ-t₁) $o) (Γ : FSC-Ctx n Γ-t₁) → (⊢ (F => (G => H))) → ⊢ (hypos-i {n} {Γ-t₁} {Γ-t₂} Γ F => (hypos-i {n} {Γ-t₁} {Γ-t₂}Γ G => hypos-i {n} {Γ-t₁} {Γ-t₂} Γ H))
traverse-hypos2-i F G H ε imp = imp
traverse-hypos2-i {n} {Γ-t₁} {Γ-t₂} F G H (X ∷h Γ) imp = traverse-hypos2-i {n} {Γ-t₁} {Γ-t₂} (m-weak {n} {Γ-t₁} {Γ-t₂} X => F) (m-weak {n} {Γ-t₁} {Γ-t₂} X => G) (m-weak {n} {Γ-t₁} {Γ-t₂} X => H) Γ (inf-V (lem8 (m-weak {n} {Γ-t₁} {Γ-t₂} X) F G H) imp)
traverse-hypos2-i {n} {.(t ∷ _)} {Γ-t₂} F G H (t ∷ Γ) imp = traverse-hypos2-i {n} {_} {t ∷ Γ-t₂} F G H Γ imp

traverse-hypos2 : ∀ {n Γ-t} → (F G H : Form Γ-t $o) (Γ : FSC-Ctx n Γ-t) → (⊢ (F => (G => H))) → ⊢ (hypos Γ F => (hypos Γ G => hypos Γ H))
traverse-hypos2 {n} {Γ-t} = traverse-hypos2-i {n} {Γ-t} {ε}

traverse-hypos3-i : ∀ {n Γ-t₁ Γ-t₂} → (F G H I : Form (Γ-t₂ r++ Γ-t₁) $o) (Γ : FSC-Ctx n Γ-t₁) → (⊢ (F => (G => (H => I)))) → ⊢ (hypos-i {n} {Γ-t₁} {Γ-t₂} Γ F => (hypos-i {n} {Γ-t₁} {Γ-t₂}Γ G => (hypos-i {n} {Γ-t₁} {Γ-t₂} Γ H => hypos-i {n} {Γ-t₁} {Γ-t₂} Γ I)))
traverse-hypos3-i F G H I ε imp = imp
traverse-hypos3-i {n} {Γ-t₁} {Γ-t₂} F G H I (X ∷h Γ) imp = traverse-hypos3-i {n} {Γ-t₁} {Γ-t₂} (m-weak {n} {Γ-t₁} {Γ-t₂} X => F) (m-weak {n} {Γ-t₁} {Γ-t₂} X => G) (m-weak {n} {Γ-t₁} {Γ-t₂} X => H) (m-weak {n} {Γ-t₁} {Γ-t₂} X => I) Γ (inf-V (lem8-3 (m-weak {n} {Γ-t₁} {Γ-t₂} X) F G H I) imp)
traverse-hypos3-i {n} {.(t ∷ _)} {Γ-t₂} F G H I (t ∷ Γ) imp = traverse-hypos3-i {n} {_} {t ∷ Γ-t₂} F G H I Γ imp

traverse-hypos3 : ∀ {n Γ-t} → (F G H I : Form Γ-t $o) (Γ : FSC-Ctx n Γ-t) → (⊢ (F => (G => (H => I)))) → ⊢ (hypos Γ F => (hypos Γ G => (hypos Γ H => hypos Γ I)))
traverse-hypos3 {n} {Γ-t} = traverse-hypos3-i {n} {Γ-t} {ε}

traverse-=>-I-dep-i : ∀ {n Γ-t₁ Γ-t₂} → (t : Type n) (F : Form (t ∷ (Γ-t₂ r++ Γ-t₁)) $o) (Γ : FSC-Ctx n Γ-t₁) →
                      ⊢ (![ t ] hypos-i-h {n} {Γ-t₁} {Γ-t₂} {t ∷ ε} Γ F) →
                      ⊢ (hypos-i {n} {Γ-t₁} {Γ-t₂} Γ (![ t ] F))
traverse-=>-I-dep-i t F ε h = h
traverse-=>-I-dep-i {n} {Γ-t₁} {Γ-t₂} t F (X ∷h Γ) h = inf-V (traverse-hypos-i {n} {Γ-t₁} {Γ-t₂} (![ t ] (weak (m-weak {n} {Γ-t₁} {Γ-t₂} X) => F)) (m-weak {n} {Γ-t₁} {Γ-t₂} X => (![ t ] F)) Γ (ax-6-s {_} {_} {_} {~ (m-weak {n} {Γ-t₁} {Γ-t₂} X)} {F})) (traverse-=>-I-dep-i {n} {Γ-t₁} {Γ-t₂} t (weak (m-weak {n} {Γ-t₁} {Γ-t₂} X) => F) Γ
 (subst (λ z → ⊢_ {n} {_r++_ {n} Γ-t₂ Γ-t₁} (app {n} {_>_ {_} t ($o {_})} {$o {_}} {_r++_ {n} Γ-t₂ Γ-t₁} (Π {n} {t} {_r++_ {n} Γ-t₂ Γ-t₁}) (lam {n} {$o {_}} {_r++_ {n} Γ-t₂ Γ-t₁} t  ((hypos-i-h {n} {Γ-t₁} {Γ-t₂} {_∷_ {_} t (ε {_})} Γ (_=>_ {_} {_∷_ {_} t (_r++_ {n} Γ-t₂ Γ-t₁)} z F))))))
        (eq-1 {n} Γ-t₁ Γ-t₂ t X) h))
traverse-=>-I-dep-i {n} {.(t' ∷ _)} {Γ-t₂} t F (t' ∷ Γ) h = traverse-=>-I-dep-i {n} {_} {t' ∷ Γ-t₂} t F Γ h

traverse-=>-I-dep : ∀ {n Γ-t} → (t : Type n) (F : Form (t ∷ Γ-t) $o) (Γ : FSC-Ctx n Γ-t) →
                    ⊢ (![ t ] hypos (t ∷ Γ) F) → ⊢ (hypos Γ (![ t ] F))
traverse-=>-I-dep {n} {Γ-t} t F Γ h = traverse-=>-I-dep-i {n} {Γ-t} {ε} t F Γ (subst (λ z → ⊢ app Π (lam t (z))) (eq-2 t Γ F) h)

traverse-=>-I-dep'-I2 : ∀ {n Γ-t₁ Γ-t₂} → (t : Type n) (F : Form (t ∷ (Γ-t₂ r++ Γ-t₁)) $o) (G : Form (Γ-t₂ r++ Γ-t₁) $o) (Γ : FSC-Ctx n Γ-t₁) →
                        (⊢ (F => weak-i ε (Γ-t₂ r++ Γ-t₁) G)) →
                        ⊢ (hypos-i-h {n} {Γ-t₁} {Γ-t₂} {t ∷ ε} Γ F => weak-i ε (Γ-t₂ r++ Γ-t₁) (hypos-i-h {n} {Γ-t₁} {Γ-t₂} {ε} Γ G))
traverse-=>-I-dep'-I2 t F G ε h = h
traverse-=>-I-dep'-I2 {n} {t' ∷ Γ-t₁} {Γ-t₂} t F G (.t' ∷ Γ) h = traverse-=>-I-dep'-I2 {n} {Γ-t₁} {t' ∷ Γ-t₂} t F G Γ h
traverse-=>-I-dep'-I2 {n} {Γ-t₁} {Γ-t₂} t F G (X ∷h Γ) h = traverse-=>-I-dep'-I2 {n} {Γ-t₁} {Γ-t₂} t (m-weak-h Γ-t₁ Γ-t₂ {$o} {t ∷ ε} (weak-i ε Γ-t₁ X) => F) (m-weak-h Γ-t₁ Γ-t₂ {$o} {ε} X => G) Γ (
 subst (λ z → ⊢ ((m-weak-h Γ-t₁ Γ-t₂ {$o} {t ∷ ε} (weak-i ε Γ-t₁ X) => F) => (z => weak-i ε (Γ-t₂ r++ Γ-t₁) G))) (eq-1 Γ-t₁ Γ-t₂ t X) (inf-V ax-4-s h))

traverse-=>-I-dep'-I : ∀ {n Γ-t} → (t : Type n) (F : Form (t ∷ Γ-t) $o) (G : Form Γ-t $o) (Γ : FSC-Ctx n Γ-t) →
                       (⊢ (F => weak-i ε Γ-t G)) →
                       ⊢ (hypos (t ∷ Γ) F => weak-i ε Γ-t (hypos Γ G))
traverse-=>-I-dep'-I {n} {Γ-t} t F G Γ h = subst (λ z → ⊢ (z => weak-i ε Γ-t (hypos Γ G))) (sym (eq-2 t Γ F)) (traverse-=>-I-dep'-I2 {n} {Γ-t} {ε} t F G Γ h)

traverse-=>-I-dep' : ∀ {n Γ-t} → (t : Type n) (F : Form Γ-t (t > $o)) (Γ : FSC-Ctx n Γ-t) →
                     ⊢ hypos (t ∷ Γ) (weak F · $ this {refl}) → ⊢ (hypos Γ (!'[ t ] F))
traverse-=>-I-dep' t F Γ h = extend-context t (inf-V (traverse-=>-I-dep'-I t (weak F · $ this {refl}) (!'[ t ] F) Γ (inf-VI this (occurs-p-2 F) refl)) h)

-- -----------------------------------

traverse-hypos-eq-=>-I-i : ∀ {n Γ-t₁ Γ-t₂} → {t : Type n} → (F : Form (t ∷ (Γ-t₂ r++ Γ-t₁)) $o) (G : Form (Γ-t₂ r++ Γ-t₁) $o) (Γ : FSC-Ctx n Γ-t₁) → (⊢ (F => weak G)) → ⊢ hypos-i-h {n} {Γ-t₁} {Γ-t₂} {t ∷ ε} Γ F → ⊢ hypos-i {n} {Γ-t₁} {Γ-t₂} Γ G
traverse-hypos-eq-=>-I-i {n} {.ε} {Γ-t₂} {t} F G ε = λ z z' → extend-context t (inf-V z z')
traverse-hypos-eq-=>-I-i {n} {Γ-t₁} {Γ-t₂} {t} F G (X ∷h Γ) = λ h → traverse-hypos-eq-=>-I-i {n} {Γ-t₁} {Γ-t₂} {t} (m-weak-h {_} Γ-t₁ Γ-t₂ {_} {t ∷ ε} (m-weak2 (t ∷ ε) X) => F) (m-weak-h {_} Γ-t₁ Γ-t₂ {_} {ε} (m-weak2 ε X) => G) Γ (subst (λ z → ⊢ ((m-weak-h {_} Γ-t₁ Γ-t₂ {_} {t ∷ ε} (m-weak2 (t ∷ ε) X) => F) => (z => weak G))) (eq-1 Γ-t₁ Γ-t₂ t X) (inf-V ax-4-s h))
traverse-hypos-eq-=>-I-i {n} {t₁ ∷ Γ-t₁} {Γ-t₂} {t} F G ((.t₁) ∷ Γ) = traverse-hypos-eq-=>-I-i {n} {Γ-t₁} {t₁ ∷ Γ-t₂} {t} F G Γ

traverse-hypos-eq-=>-I'' : ∀ {n Γ-t} → {t : Type n} (F : Form (t ∷ Γ-t) $o) (G : Form Γ-t $o) (Γ : FSC-Ctx n Γ-t) →
 ⊢ (F => weak G) →
 ⊢ hypos (t ∷ Γ) F → ⊢ hypos Γ G
traverse-hypos-eq-=>-I'' {n} {Γ-t} F G Γ = λ h₁ h₂ → traverse-hypos-eq-=>-I-i {n} {Γ-t} {ε} F G Γ h₁ (subst (λ z → ⊢ z) (eq-2 _ Γ F) h₂)

tst3-ε : ∀ {n} → (Γ₁ Γ₂ : Ctx n) (F : Form Γ₁ $o)
 (eq : Γ₁ ≡ Γ₂) →
 ⊢_ {n} {Γ₂} (subst (λ z → Form z $o) eq F) →
 ⊢_ {n} {Γ₁} F
tst3-ε .Γ₂ Γ₂ F refl p = p  -- rewrite eq = p

tst3b : ∀ {n} → {t u : Type n} → (Γ-t₁ Γ-t₂ : Ctx n) (X : Form Γ-t₁ u) →
 (eq2 : t ∷ (Γ-t₂ r++ Γ-t₁) ≡ (Γ-t₂ ++ (t ∷ ε)) r++ Γ-t₁) →
 m-weak-h Γ-t₁ (Γ-t₂ ++ (t ∷ ε)) {_} {ε} X ≡
 subst (λ z → Form z u) eq2 (weak-i ε (Γ-t₂ r++ Γ-t₁) (m-weak-h Γ-t₁ Γ-t₂ {_} {ε} X))
tst3b Γ-t₁ ε X refl = refl
tst3b Γ-t₁ (v ∷ Γ-t₂) X eq2 = tst3b (v ∷ Γ-t₁) Γ-t₂ (weak-i ε Γ-t₁ X) eq2

tst3'-∷h : ∀ {n Γ-t₁ Γ-t₂} → {t : Type n} (F : Form (t ∷ (Γ-t₂ r++ Γ-t₁)) $o) (Γ : FSC-Ctx n Γ-t₁) →
 (X : Form Γ-t₁ $o)
 (Γ1 : Ctx n)
 (eq1 : Γ1 ≡ (Γ-t₂ ++ (t ∷ ε)) r++ Γ-t₁)
 (eq2 : t ∷ (Γ-t₂ r++ Γ-t₁) ≡ Γ1)
 (p : ⊢ hypos-i-h {n} {Γ-t₁} {Γ-t₂ ++ (t ∷ ε)} {ε} Γ (app (app A (app N (m-weak-h {n} Γ-t₁ (Γ-t₂ ++ (t ∷ ε)) {$o} {ε} X))) (subst (λ z → Form z $o) eq1 (subst (λ z → Form z $o) eq2 F)))) →
 ⊢ hypos-i-h {n} {Γ-t₁} {Γ-t₂ ++ (t ∷ ε)} {ε} Γ (subst (λ z → Form z $o) eq1 (app (app A (app N (subst (λ z → Form z $o) eq2 (weak-i ε (Γ-t₂ r++ Γ-t₁) (m-weak-h Γ-t₁ Γ-t₂ {$o} {ε} X))))) (subst (λ z → Form z $o) eq2 F)))
tst3'-∷h {n} {Γ-t₁} {Γ-t₂} {t} F Γ X .((Γ-t₂ ++ (t ∷ ε)) r++ Γ-t₁) refl eq2 p = subst (λ z → ⊢ hypos-i-h {n} {Γ-t₁} {Γ-t₂ ++ (t ∷ ε)} {ε} Γ (app (app A (app N z)) (subst (λ z → Form z $o) eq2 F))) (tst3b {n} {t} Γ-t₁ Γ-t₂ X eq2) p

tst3' : ∀ {n Γ-t₁ Γ-t₂} → {t : Type n} (F : Form (t ∷ (Γ-t₂ r++ Γ-t₁)) $o) (Γ : FSC-Ctx n Γ-t₁) →
 (eq : t ∷ (Γ-t₂ r++ Γ-t₁) ≡ (Γ-t₂ ++ (t ∷ ε)) r++ Γ-t₁) →
 ⊢ hypos-i-h {n} {Γ-t₁} {Γ-t₂ ++ (t ∷ ε)} {ε} Γ (subst (λ z → Form z $o) eq F) →
 ⊢ hypos-i-h {n} {Γ-t₁} {Γ-t₂} {ε} Γ (![ t ] F)
tst3' {n} {ε} {Γ-t₂} {t} F ε eq = λ p → inf-VI-s (tst3-ε {n} (t ∷ (Γ-t₂ r++ ε)) ((Γ-t₂ ++ (t ∷ ε)) r++ ε) F eq p)
tst3' {n} {t' ∷ Γ-t₁} {Γ-t₂} F ((.t') ∷ Γ) eq = tst3' {n} {Γ-t₁} {t' ∷ Γ-t₂} F Γ eq
tst3' {n} {Γ-t₁} {Γ-t₂} {t} F (X ∷h Γ) eq = λ p → inf-V (traverse-hypos-i {n} {Γ-t₁} {Γ-t₂} (![ t ] (weak (~ (m-weak-h Γ-t₁ Γ-t₂ {_} {ε} X)) || F)) (m-weak-h {n} Γ-t₁ Γ-t₂ {$o} {ε} X => ![ t ] F) Γ (inf-V (inf-V ax-4-s ax-6-s) (lem2h2hb _))) (tst3' {n} {Γ-t₁} {Γ-t₂} {t} (weak (~ (m-weak-h Γ-t₁ Γ-t₂ {_} {ε} X)) || F) Γ eq (tst3'-∷h {n} {Γ-t₁} {Γ-t₂} {t} F Γ X (t ∷ (Γ-t₂ r++ Γ-t₁)) eq refl p))

traverse-hypos-eq-=>-I : ∀ {n Γ-t} → {t₁ t₂ : Type n} (F₁ F₂ : Form (t₁ ∷ Γ-t) t₂) (Γ : FSC-Ctx n Γ-t) →
 ⊢ hypos (t₁ ∷ Γ) (F₁ == F₂) →
 ⊢ hypos Γ (lam t₁ F₁ == lam t₁ F₂)
traverse-hypos-eq-=>-I {_} {Γ-t} {t₁} {t₂} F₁ F₂ Γ p = inf-V (traverse-hypos (![ t₁ ] (F₁ == F₂)) (lam t₁ F₁ == lam t₁ F₂) Γ ax-10-b-s) (tst3' {_} {Γ-t} {ε} (F₁ == F₂) Γ refl p)


module snoc where

open import unit
open import empty
open import bool
open import product

data Snoc (A : Set) : Set where
  [] : Snoc A
  _::_ : Snoc A → A → Snoc A

infixl 6 _::_ _++_

[_] : {A : Set} → A → Snoc A
[ x ] = [] :: x

_++_ : {A : Set} → Snoc A → Snoc A → Snoc A
[] ++ l₂ = l₂
(l₁ :: x) ++ l₂ = (l₁ ++ l₂) :: x

member : {A : Set} → (A → A → 𝔹) → A → Snoc A → 𝔹
member _=A_ x (l :: y) with x =A y
... | tt = tt
... | ff = ff
member _=A_ x _ = ff

inPairSnocFst : {A B : Set} → (A → A → 𝔹) → A → Snoc (A × B) → Set
inPairSnocFst _=A_ x [] = ⊥
inPairSnocFst _=A_ x (l :: (a , _)) with x =A a
... | tt = ⊤
... | ff = inPairSnocFst _=A_ x l


-- Kit instances and generic term traversals
module Syntax.Substitution.Instances where

open import Syntax.Types
open import Syntax.Context
open import Syntax.Terms
open import Syntax.Substitution.Kits

open import Data.Sum
open import Relation.Binary.PropositionalEquality as ≡
    using (_≡_ ; refl ; sym ; cong ; subst)
open import Function using (id ; flip ; _∘_)
open ≡.≡-Reasoning

-- | Variable and term schemas
Var : Schema
Var = flip _∈_

Term : Schema
Term = _⊢_

-- | Variable kit

-- Variable kit
𝒱ar : Kit Var
𝒱ar = record
    { 𝓋 = id
    ; 𝓉 = var
    ; 𝓌 = pop
    ; 𝒶 = 𝒶-var
    }
    where
    𝒶-var : ∀{A Γ} → Var Γ (A always) → Var (Γ ˢ) (A always)
    𝒶-var top = top
    𝒶-var (pop {B = B now} v) = 𝒶-var v
    𝒶-var (pop {B = B always} v) = pop (𝒶-var v)

-- Generic substitution in a variable to any schema
subst-var : ∀ {𝒮 Γ Δ A} -> Subst 𝒮 Γ Δ → Var Γ A → 𝒮 Δ A
subst-var ● ()
subst-var (σ ▸ T) top = T
subst-var (σ ▸ T) (pop v) = subst-var σ v

-- Substitutable variable kit
𝒱arₛ : SubstKit Var
𝒱arₛ = record { 𝓀 = 𝒱ar ; 𝓈 = subst-var }

-- | Term traversal

module K {𝒮 : Schema} (k : Kit 𝒮) where
    open Kit k

    -- | Type-preserving term traversal
    -- | Traverses the syntax tree of the term, applying
    -- | the given substitution to the variables.
    mutual
        traverse : ∀{Γ Δ A} -> Subst 𝒮 Γ Δ -> Γ ⊢ A -> Δ ⊢ A
        traverse σ (var x)     = 𝓉 (subst-var σ x)
        traverse σ (lam M)     = lam (traverse (σ ↑ k) M)
        traverse σ (M $ N)     = traverse σ M $ traverse σ N
        traverse σ unit        = unit
        traverse σ [ M ,, N ]  = [ traverse σ M ,, traverse σ N ]
        traverse σ (fst M)     = fst (traverse σ M)
        traverse σ (snd M)     = snd (traverse σ M)
        traverse σ (inl M)     = inl (traverse σ M)
        traverse σ (inr M)     = inr (traverse σ M)
        traverse σ (case M inl↦ N₁ ||inr↦ N₂)
                               = case traverse σ M
                                   inl↦ traverse (σ ↑ k) N₁
                                 ||inr↦ traverse (σ ↑ k) N₂
        traverse σ (sample M) = sample (traverse σ M)
        traverse σ (stable M)  = stable (traverse (σ ↓ˢ k) M)
        traverse σ (sig M)     = sig (traverse σ M)
        traverse σ (letSig S In M) = letSig traverse σ S
                                         In traverse (σ ↑ k) M
        traverse σ (event E)   = event (traverse′ σ E)

        traverse′ : ∀{Γ Δ A} -> Subst 𝒮 Γ Δ -> Γ ⊨ A -> Δ ⊨ A
        traverse′ σ (pure M) = pure (traverse σ M)
        traverse′ σ (letSig S InC C) = letSig traverse σ S
                                         InC traverse′ (σ ↑ k) C
        traverse′ σ (letEvt E In C)  = letEvt traverse σ E
                                         In traverse′ (σ ↓ˢ k ↑ k) C
        traverse′ σ (select E₁ ↦ C₁ || E₂ ↦ C₂ ||both↦ C₃) =
            select traverse σ E₁ ↦ traverse′ (σ ↓ˢ k ↑ k ↑ k) C₁
                || traverse σ E₂ ↦ traverse′ (σ ↓ˢ k ↑ k ↑ k) C₂
                ||both↦            traverse′ (σ ↓ˢ k ↑ k ↑ k) C₃
open K

-- Renaming is a term traversal with variable substitutions
rename : ∀{A Γ Δ} -> Subst Var Γ Δ -> Γ ⊢ A -> Δ ⊢ A
rename = traverse 𝒱ar

-- Weakening is a renaming with a weakening substitution
weaken-top : ∀{B Γ A} -> Γ ⊢ A → Γ , B ⊢ A
weaken-top = rename (weak-topₛ 𝒱arₛ)

-- Weakening is a renaming with a weakening substitution
weaken′-top : ∀{B Γ A} -> Γ ⊨ A → Γ , B ⊨ A
weaken′-top = traverse′ 𝒱ar (weak-topₛ 𝒱arₛ)

-- | Term kit

-- Term kit
𝒯erm : Kit Term
𝒯erm = record
    { 𝓋 = var
    ; 𝓉 = id
    ; 𝓌 = weaken-top
    ; 𝒶 = 𝒶-term
    }
    where
    𝒶-term : {Γ : Context} {A : Type} → Γ ⊢ A always → Γ ˢ ⊢ A always
    𝒶-term {∙} M = M
    𝒶-term {Γ , B now} (var (pop x)) = 𝒶-term (var x)
    𝒶-term {Γ , B always} (var top) = var top
    𝒶-term {Γ , B always} (var {A = A} (pop x)) = weaken-top (𝒶-term (var x))
    𝒶-term {Γ , B now} (stable M) = 𝒶-term {Γ} (stable M)
    𝒶-term {Γ , B always} (stable {A = A} M) =
        stable (subst (λ x → x , B always ⊢ A now) (sym (ˢ-idemp Γ)) M)

-- Substitution is a traversal with term substitutions
substitute : ∀{Γ Δ A} -> Subst Term Γ Δ -> Γ ⊢ A -> Δ ⊢ A
substitute = traverse 𝒯erm

-- Computational substitution is a traversal with term substitutions
substitute′ : ∀{Γ Δ A} -> Subst Term Γ Δ -> Γ ⊨ A -> Δ ⊨ A
substitute′ = traverse′ 𝒯erm

-- Substitutable term kit
𝒯ermₛ : SubstKit Term
𝒯ermₛ = record { 𝓀 = 𝒯erm ; 𝓈 = substitute }


{-# OPTIONS --cubical --safe #-}

module Cubical.HITs.Cylinder.Base where

open import Cubical.Core.Everything

open import Cubical.Foundations.Everything

open import Cubical.HITs.PropositionalTruncation

import Cubical.Data.Everything as Data
open Data hiding (inl; inr)

open import Cubical.HITs.Interval

-- Cylinder A is a cylinder object in the category of cubical types.
--
--   https://ncatlab.org/nlab/show/cylinder+object
data Cylinder {ℓ} (A : Type ℓ) : Type ℓ where
  inl : A → Cylinder A
  inr : A → Cylinder A
  cross : ∀ x → inl x ≡ inr x

-- Dual to this is the cocylinder or path space object.
--
--   https://ncatlab.org/nlab/show/path+space+object
Cocylinder : ∀ {ℓ} → Type ℓ → Type ℓ
Cocylinder A = Interval → A

module _ {ℓ} {A : Type ℓ} where
  -- The cylinder is part of a factorization of the obvious mapping
  -- of type A ⊎ A → A into a pair of mappings:
  --
  --   A ⊎ A → Cylinder A ≃ A
  --
  -- include is the first part of the factorization.
  include : A ⊎ A → Cylinder A
  include (Data.inl x) = inl x
  include (Data.inr x) = inr x

  -- The above inclusion is surjective
  includeSurjective : ∀ c → ∥ Σ[ s ∈ A ⊎ A ] include s ≡ c ∥
  includeSurjective (inl x) = ∣ Data.inl x , refl ∣
  includeSurjective (inr x) = ∣ Data.inr x , refl ∣
  includeSurjective (cross x i) =
    squash
      ∣ Data.inl x , (λ j → cross x (i ∧ j)) ∣
      ∣ Data.inr x , (λ j → cross x (i ∨ ~ j)) ∣
      i

  elimCyl
    : ∀{ℓ'} {B : Cylinder A → Type ℓ'}
    → (f : (x : A) → B (inl x))
    → (g : (x : A) → B (inr x))
    → (p : ∀ x → PathP (λ i → B (cross x i)) (f x) (g x))
    → (c : Cylinder A) → B c
  elimCyl f _ _ (inl x) = f x
  elimCyl _ g _ (inr x) = g x
  elimCyl _ _ p (cross x i) = p x i

  private
    out : Cylinder A → A
    out (inl x) = x
    out (inr x) = x
    out (cross x i) = x

    inl-out : (c : Cylinder A) → inl (out c) ≡ c
    inl-out (inl x) = refl
    inl-out (inr x) = cross x
    inl-out (cross x i) = λ j → cross x (i ∧ j)

    out-inl : ∀(x : A) → out (inl x) ≡ x
    out-inl x = refl

  -- The second part of the factorization above.
  CylinderA≃A : Cylinder A ≃ A
  CylinderA≃A = isoToEquiv (iso out inl out-inl inl-out)

  -- The cocylinder has a similar equivalence that is part
  -- of factorizing the diagonal mapping.
  private
    inco : A → Cocylinder A
    inco x _ = x

    outco : Cocylinder A → A
    outco f = f zero

    A→CocylinderA→A : (x : A) → outco (inco x) ≡ x
    A→CocylinderA→A x = refl

    CocylinderA→A→CocylinderA : (c : Cocylinder A) → inco (outco c) ≡ c
    CocylinderA→A→CocylinderA c j zero = c zero
    CocylinderA→A→CocylinderA c j one = c (seg j)
    CocylinderA→A→CocylinderA c j (seg i) = c (seg (j ∧ i))

  A≃CocylinderA : A ≃ Cocylinder A
  A≃CocylinderA =
    isoToEquiv (iso inco outco CocylinderA→A→CocylinderA A→CocylinderA→A)

  project : Cocylinder A → A × A
  project c = c zero , c one

-- Since we can construct cylinders for every type, Cylinder actually
-- constitutes a cylinder functor:
--
--   https://ncatlab.org/nlab/show/cylinder+functor
--
-- e₀ = inl
-- e₁ = inr
-- σ = out
module Functorial where
  private
    variable
      ℓa ℓb ℓc : Level
      A : Type ℓa
      B : Type ℓb
      C : Type ℓc

  mapCylinder : (A → B) → Cylinder A → Cylinder B
  mapCylinder f (inl x) = inl (f x)
  mapCylinder f (inr x) = inr (f x)
  mapCylinder f (cross x i) = cross (f x) i

  mapCylinderId : mapCylinder (λ(x : A) → x) ≡ (λ x → x)
  mapCylinderId i (inl x) = inl x
  mapCylinderId i (inr x) = inr x
  mapCylinderId i (cross x j) = cross x j

  mapCylinder∘
    : (f : A → B) → (g : B → C)
    → mapCylinder (λ x → g (f x)) ≡ (λ x → mapCylinder g (mapCylinder f x))
  mapCylinder∘ f g i (inl x) = inl (g (f x))
  mapCylinder∘ f g i (inr x) = inr (g (f x))
  mapCylinder∘ f g i (cross x j) = cross (g (f x)) j

  -- There is an adjunction between the cylinder and coyclinder
  -- functors.
  --
  --   Cylinder ⊣ Cocylinder
  adj₁ : (Cylinder A → B) → A → Cocylinder B
  adj₁ f x zero = f (inl x)
  adj₁ f x one = f (inr x)
  adj₁ f x (seg i) = f (cross x i)

  adj₂ : (A → Cocylinder B) → Cylinder A → B
  adj₂ g (inl x) = g x zero
  adj₂ g (inr x) = g x one
  adj₂ g (cross x i) = g x (seg i)

  adj₁₂ : (g : A → Cocylinder B) → adj₁ (adj₂ g) ≡ g
  adj₁₂ g _ x zero = g x zero
  adj₁₂ g _ x one = g x one
  adj₁₂ g _ x (seg i) = g x (seg i)

  adj₂₁ : (f : Cylinder A → B) → adj₂ (adj₁ f) ≡ f
  adj₂₁ f j (inl x) = f (inl x)
  adj₂₁ f j (inr x) = f (inr x)
  adj₂₁ f j (cross x i) = f (cross x i)

module IntervalEquiv where
  -- There is an equivalence between the interval and the
  -- cylinder over the unit type.
  Interval→CylinderUnit : Interval → Cylinder Unit
  Interval→CylinderUnit zero = inl _
  Interval→CylinderUnit one = inr _
  Interval→CylinderUnit (seg i) = cross _ i

  CylinderUnit→Interval : Cylinder Unit → Interval
  CylinderUnit→Interval (inl _) = zero
  CylinderUnit→Interval (inr _) = one
  CylinderUnit→Interval (cross _ i) = seg i

  Interval→CylinderUnit→Interval
    : ∀ i → CylinderUnit→Interval (Interval→CylinderUnit i) ≡ i
  Interval→CylinderUnit→Interval zero = refl
  Interval→CylinderUnit→Interval one = refl
  Interval→CylinderUnit→Interval (seg i) = refl

  CylinderUnit→Interval→CylinderUnit
    : ∀ c → Interval→CylinderUnit (CylinderUnit→Interval c) ≡ c
  CylinderUnit→Interval→CylinderUnit (inl _) = refl
  CylinderUnit→Interval→CylinderUnit (inr _) = refl
  CylinderUnit→Interval→CylinderUnit (cross _ i) = refl

  CylinderUnit≃Interval : Cylinder Unit ≃ Interval
  CylinderUnit≃Interval =
    isoToEquiv (iso CylinderUnit→Interval Interval→CylinderUnit Interval→CylinderUnit→Interval CylinderUnit→Interval→CylinderUnit)

  -- More generally, there is an equivalence between the cylinder
  -- over any type A and the product of A and the interval.
  module _ {ℓ} {A : Type ℓ} where
    private
      Cyl : Type ℓ
      Cyl = A × Interval

    CylinderA→A×Interval : Cylinder A → Cyl
    CylinderA→A×Interval (inl x) = x , zero
    CylinderA→A×Interval (inr x) = x , one
    CylinderA→A×Interval (cross x i) = x , seg i

    A×Interval→CylinderA : Cyl → Cylinder A
    A×Interval→CylinderA (x , zero) = inl x
    A×Interval→CylinderA (x , one) = inr x
    A×Interval→CylinderA (x , seg i) = cross x i

    A×Interval→CylinderA→A×Interval
      : ∀ c → CylinderA→A×Interval (A×Interval→CylinderA c) ≡ c
    A×Interval→CylinderA→A×Interval (x , zero) = refl
    A×Interval→CylinderA→A×Interval (x , one) = refl
    A×Interval→CylinderA→A×Interval (x , seg i) = refl

    CylinderA→A×Interval→CylinderA
      : ∀ c → A×Interval→CylinderA (CylinderA→A×Interval c) ≡ c
    CylinderA→A×Interval→CylinderA (inl x) = refl
    CylinderA→A×Interval→CylinderA (inr x) = refl
    CylinderA→A×Interval→CylinderA (cross x i) = refl

    CylinderA≃A×Interval : Cylinder A ≃ Cyl
    CylinderA≃A×Interval =
      isoToEquiv
        (iso CylinderA→A×Interval
             A×Interval→CylinderA
             A×Interval→CylinderA→A×Interval
             CylinderA→A×Interval→CylinderA)

-- The cylinder is also the pushout of the identity on A with itself.
module Push {ℓ} {A : Type ℓ} where
  open import Cubical.HITs.Pushout

  private
    Push : Type ℓ
    Push = Pushout (λ(x : A) → x) (λ x → x)

    Cyl : Type ℓ
    Cyl = Cylinder A

  Cylinder→Pushout : Cyl → Push
  Cylinder→Pushout (inl x) = inl x
  Cylinder→Pushout (inr x) = inr x
  Cylinder→Pushout (cross x i) = push x i

  Pushout→Cylinder : Push → Cyl
  Pushout→Cylinder (inl x) = inl x
  Pushout→Cylinder (inr x) = inr x
  Pushout→Cylinder (push x i) = cross x i

  Pushout→Cylinder→Pushout : ∀ p → Cylinder→Pushout (Pushout→Cylinder p) ≡ p
  Pushout→Cylinder→Pushout (inl x) = refl
  Pushout→Cylinder→Pushout (inr x) = refl
  Pushout→Cylinder→Pushout (push x i) = refl

  Cylinder→Pushout→Cylinder : ∀ c → Pushout→Cylinder (Cylinder→Pushout c) ≡ c
  Cylinder→Pushout→Cylinder (inl x) = refl
  Cylinder→Pushout→Cylinder (inr x) = refl
  Cylinder→Pushout→Cylinder (cross x i) = refl

  Pushout≃Cylinder : Push ≃ Cyl
  Pushout≃Cylinder =
    isoToEquiv
      (iso Pushout→Cylinder
           Cylinder→Pushout
           Cylinder→Pushout→Cylinder
           Pushout→Cylinder→Pushout)


module Oscar.Data.Permutation where

--open import Data.Permutation public
import Data.Permutation as P
open import Data.Permutation renaming (delete to deleteP; _∘_ to _∘P_; enum to allInj)

open import Oscar.Data.Vec renaming (delete to deleteV; map to mapV)

open import Relation.Binary.PropositionalEquality
open import Data.Product
open import Data.Fin renaming (inject to injectF)

open ≡-Reasoning

open import Data.Permutation.Properties

open import Oscar.Data.Vec.Properties
open import Function

open import Data.Sum
open import Agda.Builtin.Nat

<>-inv : ∀ {n} (ps : Permutation n) i → i ≡ < ps > (< ps ⁻¹ > i)
<>-inv ps i =
  trans
    (sym (id-is-id i))
    (subst (< P.id > _ ≡_)
           (inj-correct (ps ⁻¹) ps i)
           (cong (flip <_> _)
                 (sym (inv-left ps))))

_⋟_÷_ : ∀ {a m n} {A : Set a} → Vec A n → Vec A m → Inj m n → Set a
x ⋟ px ÷ p = ∀ i → lookup i px ≡ lookup (< p > i) x

lookup-ext : ∀ {a n} {A : Set a} {pv₁ pv₂ : Vec A n} → (∀ i → lookup i pv₁ ≡ lookup i pv₂) → pv₁ ≡ pv₂
lookup-ext {pv₁ = []} {[]} x = refl
lookup-ext {pv₁ = x₁ ∷ pv₁} {x₂ ∷ pv₂} x with lookup-ext {pv₁ = pv₁} {pv₂} (x ∘ suc) | x zero
… | refl | refl = refl

injected-inj : ∀ {a n m} {A : Set a} {v : Vec A m} {pv₁ pv₂ : Vec A n} {p : Inj n m} → v ⋟ pv₁ ÷ p → v ⋟ pv₂ ÷ p → pv₁ ≡ pv₂
injected-inj {pv₁ = pv₁} {pv₂} v⋟pv₁÷p v⋟pv₂÷p = lookup-ext foo where
  foo : (i : Fin _) → lookup i pv₁ ≡ lookup i pv₂
  foo i = trans (v⋟pv₁÷p i) (sym (v⋟pv₂÷p i))

permuted-inj : ∀ {a n} {A : Set a} {v : Vec A n} {pv₁ pv₂ : Vec A n} {p : Permutation n} → v ⋟ pv₁ ÷ p → v ⋟ pv₂ ÷ p → pv₁ ≡ pv₂
permuted-inj {pv₁ = pv₁} {pv₂} v⋟pv₁÷p v⋟pv₂÷p = lookup-ext foo where
  foo : (i : Fin _) → lookup i pv₁ ≡ lookup i pv₂
  foo i = trans (v⋟pv₁÷p i) (sym (v⋟pv₂÷p i))

permutedLookup : ∀ {a n} {A : Set a} → Permutation n → Vec A n → Fin n → A
permutedLookup p v = flip lookup v ∘ < p >

inject : ∀ {a n m} {A : Set a} → (p : Inj n m) → (v : Vec A m) → Vec A n
inject p v = tabulate (flip lookup v ∘ < p >)

permute : ∀ {a n} {A : Set a} → (p : Permutation n) → (v : Vec A n) → Vec A n
permute p v = tabulate (flip lookup v ∘ < p >)

inject-correct : ∀ {a n m} {A : Set a} → (p : Inj n m) → (v : Vec A m) →
                  v ⋟ inject p v ÷ p
inject-correct p v = lookup∘tabulate (flip lookup v ∘ < p >)

permute-correct : ∀ {a n} {A : Set a} → (p : Permutation n) → (v : Vec A n) →
                  v ⋟ permute p v ÷ p
permute-correct p v = lookup∘tabulate (flip lookup v ∘ < p >)

Permute : ∀ {a n} {A : Set a} → (p : Permutation n) → (v : Vec A n) →
          ∃ (v ⋟_÷ p)
Permute p v = permute p v , permute-correct p v

_⋟_÷? : ∀ {a m n} {A : Set a} → Vec A n → Vec A m → Set a
_⋟_÷? x px = ∃ (x ⋟ px ÷_)

∈-allInj : ∀ {n m} (i : Inj n m) → i ∈ allInj n m
∈-allInj {zero} [] = here
∈-allInj {suc n} (i ∷ is) = ∈-map₂ _∷_ (∈-allFin i) (∈-allInj is)

tabulate₂ : ∀ {n m a} {A : Set a} → (Inj n m → A) → Vec A (size n m)
tabulate₂ f = mapV f (allInj _ _)

∈-tabulate₂ : ∀ {n m a} {A : Set a} (f : Inj n m → A) i → f i ∈ tabulate₂ f
∈-tabulate₂ f i = ∈-map f (∈-allInj i)

ENUM₂ : ∀ {a n} {A : Set a} → (v : Vec A n) → Vec (∃ (v ⋟_÷?)) (size n n)
ENUM₂ v = tabulate₂ (λ p → permute p v , p , permute-correct p v)

enumᵢ : ∀ {a n m} {A : Set a} → Vec A m → Vec (Vec A n) (size n m)
enumᵢ v = tabulate₂ (flip inject v)

enum₂ : ∀ {a n} {A : Set a} → Vec A n → Vec (Vec A n) (size n n)
enum₂ v = tabulate₂ (flip permute v)

enumᵢ-sound : ∀ {a} {A : Set a} {n m} (x : Vec A m) (px : Vec A n) → px ∈ enumᵢ x → x ⋟ px ÷?
enumᵢ-sound x _ px∈enum₂x with map-∈ px∈enum₂x
enumᵢ-sound x _ _ | p , refl = p , inject-correct p x

enum₂-sound : ∀ {a} {A : Set a} {n} (x px : Vec A n) → px ∈ enum₂ x → x ⋟ px ÷?
enum₂-sound x _ px∈enum₂x with map-∈ px∈enum₂x
enum₂-sound x _ _ | p , refl = p , permute-correct p x

remove-÷-zero : ∀ {n a} {A : Set a} {x : A} {xs : Vec A n} {px : A}
                  {pxs : Vec A n} {ps : Inj n n} →
                (x ∷ xs) ⋟ px ∷ pxs ÷ (zero ∷ ps) →
                xs ⋟ pxs ÷ ps
remove-÷-zero f i = f (suc i)

enumᵢ-complete : ∀ {a} {A : Set a} {n m} (x : Vec A m) (px : Vec A n) → x ⋟ px ÷? → px ∈ enumᵢ x
enumᵢ-complete x px (p , x⋟px÷p) = proof where
  p∈allInj : p ∈ allInj _ _
  p∈allInj = ∈-allInj p
  permutepx∈enum₂x : inject p x ∈ enumᵢ x
  permutepx∈enum₂x = ∈-map (flip inject x) p∈allInj
  x⋟permutepx÷p : x ⋟ inject p x ÷ p
  x⋟permutepx÷p = inject-correct p x
  proof : px ∈ enumᵢ x
  proof = subst (_∈ _) (injected-inj {p = p} x⋟permutepx÷p x⋟px÷p) permutepx∈enum₂x

enum₂-complete : ∀ {a} {A : Set a} {n} (x px : Vec A n) → x ⋟ px ÷? → px ∈ enum₂ x
enum₂-complete x px (p , x⋟px÷p) = proof where
  p∈allInj : p ∈ allInj _ _
  p∈allInj = ∈-allInj p
  permutepx∈enum₂x : permute p x ∈ enum₂ x
  permutepx∈enum₂x = ∈-map (flip permute x) p∈allInj
  x⋟permutepx÷p : x ⋟ permute p x ÷ p
  x⋟permutepx÷p = permute-correct p x
  proof : px ∈ enum₂ x
  proof = subst (_∈ _) (permuted-inj {p = p} x⋟permutepx÷p x⋟px÷p) permutepx∈enum₂x

Enum₂ : ∀ {a n} {A : Set a} → (x : Vec A n) → Σ[ pxs ∈ Vec (Vec A n) (size n n) ] (∀ px → (px ∈ pxs → x ⋟ px ÷?) × (x ⋟ px ÷? → px ∈ pxs))
Enum₂ x = enum₂ x , (λ px → enum₂-sound x px , enum₂-complete x px)

_∃⊎∀_ : ∀ {a} {A : Set a} {l r} (L : A → Set l) (R : A → Set r) {p} (P : A → Set p) → Set _
(L ∃⊎∀ R) P = (∃ λ x → P x × L x) ⊎ (∀ x → P x → R x)

open import Agda.Primitive

stepDecide∃⊎∀ : ∀ {a} {A : Set a} {l r} {L : A → Set l} {R : A → Set r} → (∀ y → L y ⊎ R y) → ∀ {p} (P : A → Set p) →
                ∀ {d} (done : Vec A d) {nd} (not-done : Vec A nd) →
                (∀ y → y ∈ done ⊎ y ∈ not-done → P y) →
                (∀ y → P y → y ∈ done ⊎ y ∈ not-done) →
                (∀ y → y ∈ done → R y) →
                (L ∃⊎∀ R) P
stepDecide∃⊎∀ dec P done {zero} not-done dnd-sound dnd-complete done-R = inj₂ (λ y Py → done-R y (dnd-done y Py)) where
  dnd-done : ∀ y → P y → y ∈ done
  dnd-done y Py with dnd-complete y Py
  dnd-done y Py | inj₁ y∈done = y∈done
  dnd-done y Py | inj₂ ()
stepDecide∃⊎∀ dec P done {suc nd} (step ∷ not-dones) dnd-sound dnd-complete done-R with dec step
… | inj₁ l = inj₁ (step , (dnd-sound step (inj₂ here)) , l)
… | inj₂ r = stepDecide∃⊎∀ dec P (step ∷ done) not-dones stepdnd-sound stepdnd-complete (stepdone-R r) where
  stepdnd-sound : ∀ y → y ∈ step ∷ done ⊎ y ∈ not-dones → P y
  stepdnd-sound _ (inj₁ here) = dnd-sound step (inj₂ here)
  stepdnd-sound y (inj₁ (there y∈done)) = dnd-sound y (inj₁ y∈done)
  stepdnd-sound y (inj₂ y∈not-dones) = dnd-sound y (inj₂ (there y∈not-dones))

  stepdnd-complete : ∀ y → P y → y ∈ step ∷ done ⊎ y ∈ not-dones
  stepdnd-complete y Py with dnd-complete y Py
  stepdnd-complete y Py | inj₁ y∈done = inj₁ (there y∈done)
  stepdnd-complete y Py | inj₂ here = inj₁ here
  stepdnd-complete y Py | inj₂ (there y∈not-dones) = inj₂ y∈not-dones

  stepdone-R : _ → ∀ y → y ∈ step ∷ done → _
  stepdone-R Rstep _ here = Rstep
  stepdone-R Rstep y (there y∈done) = done-R y y∈done

decide∃⊎∀ : ∀ {a} {A : Set a} {l r} {L : A → Set l} {R : A → Set r} → (∀ y → L y ⊎ R y) → ∀ {p} (P : A → Set p) →
            ∀ {nd} (not-done : Vec A nd) →
            (∀ y → y ∈ not-done → P y) →
            (∀ y → P y → y ∈ not-done) →
            (L ∃⊎∀ R) P
decide∃⊎∀ dec P not-done nd-sound nd-complete = stepDecide∃⊎∀ dec P [] not-done []nd-sound []nd-complete (λ {_ ()}) where
  []nd-sound : ∀ y → y ∈ [] ⊎ y ∈ not-done → P y
  []nd-sound y (inj₁ ())
  []nd-sound y (inj₂ y∈not-done) = nd-sound y y∈not-done

  []nd-complete : ∀ y → P y → y ∈ [] ⊎ y ∈ not-done
  []nd-complete y Py = inj₂ (nd-complete y Py)

decidePermutations : ∀ {a n} {A : Set a} {l r} {L : Vec A n → Set l} {R : Vec A n → Set r} → ∀ x → (∀ y → L y ⊎ R y) →
                     (L ∃⊎∀ R) (x ⋟_÷?)
decidePermutations x f = decide∃⊎∀ f _ (enum₂ x) (enum₂-sound x) (enum₂-complete x)

decideInjections : ∀ {a n m} {A : Set a} {l r} {L : Vec A n → Set l} {R : Vec A n → Set r} → ∀ (x : Vec A m) → (∀ y → L y ⊎ R y) →
                     (L ∃⊎∀ R) (x ⋟_÷?)
decideInjections x f = decide∃⊎∀ f _ (enumᵢ x) (enumᵢ-sound x) (enumᵢ-complete x)

-- -- sym-⋟∣ : ∀ {a n} {A : Set a} → (y x : Vec A n) → (p : Permutation n) →
-- --          y ⋟ x ∣ p → x ⋟ y ∣ (p ⁻¹)
-- -- sym-⋟∣ y x ps y⋟x∣p i =
-- --   trans (cong (flip lookup x) {x = i} {y = < ps > (< ps ⁻¹ > i)} (<>-inv ps i)) (sym (y⋟x∣p (< ps ⁻¹ > i)))

-- -- open import Oscar.Data.Vec.Properties

-- -- Permute : ∀ {a n} {A : Set a} → (p : Permutation n) → (v : Vec A n) →
-- --           ∃ λ w → w ⋟ v ∥ p
-- -- Permute [] v = v , λ ()
-- -- Permute p@(p' ∷ ps) v@(v' ∷ vs) =
-- --   let ws , [vs≡ws]ps = Permute ps (deleteV p' v)
-- --       w = lookup p' v ∷ ws
-- --       [v≡w]p : w ⋟ v ∥ p
-- --       [v≡w]p = {!!}
-- --       {-
-- --       [v≡w]p = λ
-- --         { zero → here
-- --         ; (suc f) → there (subst (ws [ f ]=_)
-- --                                  (lookup-delete-thin p' (< ps > f) v)
-- --                                  ([vs≡ws]ps f)) }
-- --       -}
-- --   in
-- --   w , [v≡w]p

-- -- -- permute : ∀ {a n} {A : Set a} → Permutation n → Vec A n → Vec A n
-- -- -- permute p v = proj₁ (Permute p v)

-- -- -- permute-correct : ∀ {a n} {A : Set a} → (p : Permutation n) → (v : Vec A n) → v ⋟ permute p v ∥ p
-- -- -- permute-correct p v = proj₂ (Permute p v)

-- -- -- open import Function

-- -- -- --∈-map-proj₁ : mapV proj₁ (mapV (λ p → F p , G p) xs) ≡ mapV F xs
-- -- -- open import Data.Nat

-- -- -- ∈-enum : ∀ {n m} (i : Inj n m) → i ∈ enum n m
-- -- -- ∈-enum {zero} [] = here
-- -- -- ∈-enum {suc n} (i ∷ is) = ∈-map₂ _∷_ (∈-allFin i) (∈-enum is)

-- -- -- open import Data.Permutation.Properties

-- -- -- [thin]=→delete[]= : ∀ {a n} {A : Set a} {i j} {v : Vec A (suc n)} {x} → v [ thin i j ]= x → deleteV i v [ j ]= x
-- -- -- [thin]=→delete[]= {i = zero} {v = x ∷ v} (there x₂) = x₂
-- -- -- [thin]=→delete[]= {n = zero} {i = suc ()} x₁
-- -- -- [thin]=→delete[]= {n = suc n} {i = suc i} {zero} {x ∷ v} here = here
-- -- -- [thin]=→delete[]= {n = suc n} {i = suc i} {suc j} {x ∷ v} (there v[thinij]=?) = there ([thin]=→delete[]= {i = i} v[thinij]=?)

-- -- -- delete≡Permutation : ∀ {a n} {A : Set a} {v₀ : A} {v₊ : Vec A n} {w : Vec A (suc n)} {p : Permutation (suc n)} →
-- -- --                      (v₀ ∷ v₊) ⋟ w ∥ p →
-- -- --                      v₊ ⋟ deleteV (annihilator p) w ∥ delete (annihilator p) p
-- -- -- delete≡Permutation {v₀ = v₀} {v₊} {w} {p} [v₀∷v₊≡w]p f = [thin]=→delete[]= {i = annihilator p} {j = f} qux where
-- -- --   foo : thin (< p > (annihilator p)) (< delete (annihilator p) p > f) ≡ < p > (thin (annihilator p) f)
-- -- --   foo = inj-thin p (annihilator p) f

-- -- --   foo2 : suc (< delete (annihilator p) p > f) ≡ < p > (thin (annihilator p) f)
-- -- --   foo2 = subst (λ y → thin y (< delete (annihilator p) p > f) ≡ < p > (thin (annihilator p) f)) (ann-correct p) foo

-- -- --   bar : w [ thin (annihilator p) f ]= lookup (< p > (thin (annihilator p) f)) (v₀ ∷ v₊)
-- -- --   bar = [v₀∷v₊≡w]p (thin (annihilator p) f)

-- -- --   qux : w [ thin (annihilator p) f ]= lookup (< delete (annihilator p) p > f) v₊
-- -- --   qux = subst (λ y → w [ thin (annihilator p) f ]= lookup y (v₀ ∷ v₊)) (sym foo2) bar

-- -- -- permute-complete-step : ∀ {a n} {A : Set a} {v₀ : A} {w : Vec A (suc n)} {v₊ : Vec A n} (x : Fin (suc n)) →
-- -- --   w [ x ]= v₀ →
-- -- --   deleteV x w ∈ mapV (flip permute v₊) (enum n n) →
-- -- --   w ∈ mapV (flip permute (v₀ ∷ v₊)) (enum (suc n) (suc n))
-- -- -- permute-complete-step {n = zero} {w = x₃ ∷ []} {[]} zero here here = here
-- -- -- permute-complete-step {n = zero} {w = x₃ ∷ []} {[]} zero x₁ (there ())
-- -- -- permute-complete-step {n = zero} {w = x₃ ∷ []} {[]} (suc ()) x₁ x₂
-- -- -- permute-complete-step {n = suc n} {w = w ∷ ws} x w∷ws[x]=v₀ x₂ = {!!}

-- -- -- permute-lemma : ∀ {a n} {A : Set a} (v w : Vec A n) (p : Permutation n) →
-- -- --   v ⋟ w ∥ p →
-- -- --   w ≡ permute (p ⁻¹) v
-- -- -- permute-lemma v w p x = {!permute-correct p w!}

-- -- -- {-
-- -- -- permute-complete' : ∀ {a n} {A : Set a} (v w : Vec A n) (p : Permutation n) →
-- -- --   v ⋟ w ∥ p →
-- -- --   ∀ {m} {ps : Vec (Permutation n) m} → p ∈ ps →
-- -- --   w ≡ permute p v
-- -- --   w ∈ mapV (flip
-- -- -- -}

-- -- -- permute-complete : ∀ {a n} {A : Set a} (v w : Vec A n) →
-- -- --            v ∃≡Permutation w →
-- -- --            w ∈ mapV (flip permute v) (enum n n)
-- -- -- permute-complete [] [] (p , [v≡w]p) = here
-- -- -- permute-complete {n = suc n} v@(v₀ ∷ v₊) w (p , [v≡w]p) = permute-complete-step (annihilator p) w[ap]=v₀ pc' where
-- -- --   w[ap]=v₀ : w [ annihilator p ]= v₀
-- -- --   w[ap]=v₀ = {![v≡w]p (annihilator p)!}
-- -- --   pc' : deleteV (annihilator p) w ∈ mapV (flip permute v₊) (enum n n)
-- -- --   pc' = permute-complete v₊ (deleteV (annihilator p) w) (delete (annihilator p) p , delete≡Permutation {p = p} [v≡w]p)

-- -- -- EnumPermutations : ∀ {a n} {A : Set a} → (v : Vec A n) →
-- -- --                    Σ (Vec (∃ (v ∃≡Permutation_)) (size n n)) λ ws
-- -- --                    → ∀ w → (v ∃≡Permutation w → w ∈ mapV proj₁ ws)
-- -- -- EnumPermutations {n = n} v = mapV (λ p → permute p v , p , permute-correct p v) (enum n n) , (λ w v∃≡Pw → subst (w ∈_) (map-∘ proj₁ (λ p → permute p v , p , permute-correct p v) (enum n n)) (permute-complete v w v∃≡Pw))

-- -- -- enumPermutations : ∀ {a n} {A : Set a} → Vec A n → Vec (Vec A n) (size n n)
-- -- -- enumPermutations {n = n} xs = mapV (λ p → permute p xs) (enum n n)

-- -- -- tryPermutations : ∀ {a n} {A : Set a} {l r} {L : Vec A n → Set l} {R : Vec A n → Set r} → ∀ x → (f : ∀ y → L y ⊎ R y) → Vec (∃ λ y → x ∃≡Permutation y × L y ⊎ R y) (size n n)
-- -- -- tryPermutations x f = mapV (λ x₁ → x₁ , {!!}) (enumPermutations x)


------------------------------------------------------------------------
-- Code related to the paper "Partiality, Revisited: The Partiality
-- Monad as a Quotient Inductive-Inductive Type"
--
-- Thorsten Altenkirch, Nils Anders Danielsson and Nicolai Kraus
------------------------------------------------------------------------

-- Note that this is a version of the code that does not quite match
-- the description in the paper. Some key changes (there might be
-- others):
--
-- * The partiality monad is not postulated, but defined as a QIIT
--   (Agda's cubical features are used). The types and eliminators are
--   marked as "abstract", so the eliminators do not compute.
--
-- * Rewrite rules are no longer used. Instead the propositional
--   truncation and set quotient operators are defined as
--   (non-abstract) QITs.
--
-- * The set quotient operator that is used can be applied to
--   arbitrary binary relations, not only propositional ones.

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

module README where

-- Note that our definition of the partiality monad and some of the
-- results are heavily inspired by the section on Cauchy reals in the
-- HoTT book (first edition).

-- The partiality algebra code uses ideas and concepts from "Inductive
-- types in homotopy type theory" by Awodey, Gambino and Sojakova,
-- "Inductive Types in Homotopy Type Theory" by Sojakova, "Quotient
-- inductive-inductive types" by Altenkirch, Capriotti, Dijkstra and
-- Nordvall Forsberg, and Gabe Dijkstra's PhD thesis.

------------------------------------------------------------------------
-- Pointers to results from the paper

-- In order to more easily find code corresponding to results from the
-- paper, see the following module. Note that some of the code
-- referenced below is not discussed at all in the paper.

import README.Pointers-to-results-from-the-paper

------------------------------------------------------------------------
-- Partiality algebras

-- Partiality algebras.

import Partiality-algebra

-- Some partiality algebra properties.

import Partiality-algebra.Properties

-- Partiality algebra categories.

import Partiality-algebra.Category

-- Eliminators and initiality.

import Partiality-algebra.Eliminators

-- Monotone functions.

import Partiality-algebra.Monotone

-- ω-continuous functions.

import Partiality-algebra.Omega-continuous

-- Strict ω-continuous functions.

import Partiality-algebra.Strict-omega-continuous

-- Fixpoint combinators.

import Partiality-algebra.Fixpoints

-- Pi with partiality algebra families as codomains.

import Partiality-algebra.Pi

------------------------------------------------------------------------
-- The partiality monad

-- A quotient inductive-inductive definition of the partiality monad.

import Partiality-monad.Inductive

-- Specialised eliminators.

import Partiality-monad.Inductive.Eliminators

-- A function that runs computations.

import Partiality-monad.Inductive.Approximate

-- An alternative characterisation of the information ordering, along
-- with related results.

import Partiality-monad.Inductive.Alternative-order

-- Monotone functions.

import Partiality-monad.Inductive.Monotone

-- ω-continuous functions.

import Partiality-monad.Inductive.Omega-continuous

-- The partiality monad's monad instance.

import Partiality-monad.Inductive.Monad

-- The partiality monad's monad instance, defined via an adjunction.

import Partiality-monad.Inductive.Monad.Adjunction

-- Strict ω-continuous functions.

import Partiality-monad.Inductive.Strict-omega-continuous

-- Fixpoint combinators.

import Partiality-monad.Inductive.Fixpoints

------------------------------------------------------------------------
-- Some examples

-- A function that, given a stream, tries to find an element
-- satisfying a predicate.

import Search

-- Examples involving simple λ-calculi.

import README.Lambda

------------------------------------------------------------------------
-- An alternative definition of the delay monad

-- The delay monad, defined using increasing sequences of potential
-- values.

import Delay-monad.Alternative

-- Various properties.

import Delay-monad.Alternative.Properties

-- Theorems relating the coinductive definition of the delay
-- monad to the alternative one and to another type.

import Delay-monad.Alternative.Equivalence

-- Termination predicates.

import Delay-monad.Alternative.Termination

-- Information orderings.

import Delay-monad.Alternative.Partial-order

-- Weak bisimilarity.

import Delay-monad.Alternative.Weak-bisimilarity

-- Two eliminators for Delay-monad.Alternative.Delay (A / R).

import Delay-monad.Alternative.Eliminators

------------------------------------------------------------------------
-- The delay monad quotiented by weak bisimilarity

-- The delay monad quotiented by weak bisimilarity.

import Partiality-monad.Coinductive

-- A partial order.

import Partiality-monad.Coinductive.Partial-order

-- An alternative definition of the partiality monad: a variant of the
-- delay monad quotiented by a notion of weak bisimilarity.

import Partiality-monad.Coinductive.Alternative

------------------------------------------------------------------------
-- A proof of equivalence

-- The partiality monads in Partiality-monad.Inductive and
-- Partiality-monad.Coinductive are pointwise equivalent, for sets,
-- assuming extensionality and countable choice.

import Partiality-monad.Equivalence

------------------------------------------------------------------------
-- ω-cpos

-- Pointed and non-pointed ω-cpos.

import Omega-cpo

-- The code in the following three modules is based on a suggestion
-- from Paolo Capriotti.

-- A quotient inductive-inductive definition of the lifting
-- construction on ω-cpos.

import Lifting

-- An alternative but equivalent definition of the partiality monad
-- (but only for sets), based on the lifting construction in Lifting.

import Lifting.Partiality-monad


{-# OPTIONS --without-K #-}
module Explore.Examples where

open import Type
open import Level.NP
open import Data.Maybe.NP
open import Data.List
open import Data.Zero
open import Data.One
open import Data.Two
open import Data.Product
open import Data.Sum.NP
open import HoTT using (UA)
open import Function.NP
open import Function.Extensionality using (FunExt)
open import Relation.Binary.PropositionalEquality hiding ([_])

open import Explore.Core
open import Explore.Explorable
open import Explore.Universe.Type {𝟘}
open import Explore.Universe.Base
open import Explore.Monad {₀} ₀ public renaming (map to map-explore)
open import Explore.Two
open import Explore.Product
open Explore.Product.Operators

module E where
  open FromExplore public

module M {Msg Digest : ★}
         (_==_ : Digest → Digest → 𝟚)
         (H : Msg → Digest)
         (exploreMsg : ∀ {ℓ} → Explore ℓ Msg)
         (d : Digest) where

  module V1 where
    list-H⁻¹ : List Msg
    list-H⁻¹ = exploreMsg [] _++_ (λ m → [0: [] 1: [ m ] ] (H m == d))

  module ExploreMsg = FromExplore {A = Msg} exploreMsg

  module V2 where
    first-H⁻¹ : Maybe Msg
    first-H⁻¹ = ExploreMsg.findKey (λ m → H m == d)

  module V3 where
    explore-H⁻¹ : Explore ₀ Msg
    explore-H⁻¹ ε _⊕_ f = exploreMsg ε _⊕_ (λ m → [0: ε 1: f m ] (H m == d))

  module V4 where
    explore-H⁻¹ : Explore ₀ Msg
    explore-H⁻¹ = exploreMsg >>= λ m → [0: empty-explore 1: point-explore m ] (H m == d)

  module V5 where

    explore-H⁻¹ : ∀ {ℓ} → Explore ℓ Msg
    explore-H⁻¹ = filter-explore (λ m → H m == d) exploreMsg

    list-H⁻¹ : List Msg
    list-H⁻¹ = E.list explore-H⁻¹

    first-H⁻¹ : Maybe Msg
    first-H⁻¹ = E.first explore-H⁻¹

  module V6 where
    explore-H⁻¹ : ∀ {ℓ} → Explore ℓ Msg
    explore-H⁻¹ = explore-endo (filter-explore (λ m → H m == d) exploreMsg)

    list-H⁻¹ : List Msg
    list-H⁻¹ = E.list explore-H⁻¹

    first-H⁻¹ : Maybe Msg
    first-H⁻¹ = E.first explore-H⁻¹

    last-H⁻¹ : Maybe Msg
    last-H⁻¹ = E.last explore-H⁻¹

Msg = 𝟚 × 𝟚
Digest = 𝟚
-- _==_ : Digest → Digest → 𝟚
H : Msg → Digest
H (x , y) = x xor y
Msgᵉ : ∀ {ℓ} → Explore ℓ Msg
Msgᵉ = 𝟚ᵉ ×ᵉ 𝟚ᵉ
module N5 = M.V5 _==_ H Msgᵉ
module N6 = M.V6 _==_ H Msgᵉ
test5 = N5.list-H⁻¹
test6-list : N6.list-H⁻¹ 0₂ ≡ (0₂ , 0₂) ∷ (1₂ , 1₂) ∷ []
test6-list = refl
test6-rev-list : E.list (E.backward (N6.explore-H⁻¹ 0₂)) ≡ (1₂ , 1₂) ∷ (0₂ , 0₂) ∷ []
test6-rev-list = refl
test6-first : N6.first-H⁻¹ 0₂ ≡ just (0₂ , 0₂)
test6-first = refl
test6-last : N6.last-H⁻¹ 0₂ ≡ just (1₂ , 1₂)
test6-last = refl
-- -}

𝟛ᵁ : U
𝟛ᵁ = 𝟙ᵁ ⊎ᵁ 𝟚ᵁ

prop-∧-comm : 𝟚 × 𝟚 → 𝟚
prop-∧-comm (x , y) = x ∧ y == y ∧ x

module _ {{_ : UA}}{{_ : FunExt}} where
  check-∧-comm : ∀ x y → ✓ (x ∧ y == y ∧ x)
  check-∧-comm x y = check! (𝟚ᵁ ×ᵁ 𝟚ᵁ) prop-∧-comm (x , y)

prop-∧-∨-distr : 𝟚 × 𝟚 × 𝟚 → 𝟚
prop-∧-∨-distr (x , y , z) = x ∧ (y ∨ z) == x ∧ y ∨ x ∧ z

module _ {{_ : UA}}{{_ : FunExt}} where
  check-∧-∨-distr : ∀ x y z → ✓ (x ∧ (y ∨ z) == x ∧ y ∨ x ∧ z)
  check-∧-∨-distr x y z =
    check! (𝟚ᵁ ×ᵁ 𝟚ᵁ ×ᵁ 𝟚ᵁ) prop-∧-∨-distr (x , y , z)

list22 = list (𝟚ᵁ →ᵁ 𝟚ᵁ)
list33 = list (𝟛ᵁ →ᵁ 𝟛ᵁ)

{-
module _ {{_ : UA}}{{_ : FunExt}} where
  module _ (fᵁ : El (𝟚ᵁ →ᵁ 𝟚ᵁ)) x where
    f = →ᵁ→→ 𝟚ᵁ 𝟚ᵁ fᵁ
    check22 : ✓ (f x == f (f (f x)))
    check22 = check! ((𝟚ᵁ →ᵁ 𝟚ᵁ) ×ᵁ 𝟚ᵁ) (λ { (f , x) → let f' = →ᵁ→→ 𝟚ᵁ 𝟚ᵁ f in f' x == f' (f' (f' x)) }) {{!!}} ((f 0₂ , f 1₂) , x)
  {-
  check22 : ∀ (f : 𝟚 → 𝟚) x → ✓ (f x == f (f (f x)))
  check22 f x = let k = check! ((𝟚ᵁ →ᵁ 𝟚ᵁ) ×ᵁ 𝟚ᵁ) (λ { (f , x) → let f' = →ᵁ→→ 𝟚ᵁ 𝟚ᵁ f in f' x == f' (f' (f' x)) }) {{!!}} ((f 0₂ , f 1₂) , x) in {!k!}
-- -}
-- -}
-- -}
-- -}