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

open import lib.Basics
open import lib.NConnected
open import lib.types.Nat
open import lib.types.TLevel
open import lib.types.Empty
open import lib.types.Group
open import lib.types.Pi
open import lib.types.Pointed
open import lib.types.Paths
open import lib.types.Sigma
open import lib.types.Truncation
open import lib.cubical.Square

module lib.types.LoopSpace where

module _ {i} where

  ⊙Ω : Ptd i → Ptd i
  ⊙Ω (A , a) = ⊙[ (a == a) , idp ]

  Ω : Ptd i → Type i
  Ω = fst ∘ ⊙Ω

  ⊙Ω^ : (n : ℕ) → Ptd i → Ptd i
  ⊙Ω^ O X = X
  ⊙Ω^ (S n) X = ⊙Ω (⊙Ω^ n X)

  Ω^ : (n : ℕ) → Ptd i → Type i
  Ω^ n X = fst (⊙Ω^ n X)

idp^ : ∀ {i} (n : ℕ) {X : Ptd i} → Ω^ n X
idp^ n {X} = snd (⊙Ω^ n X)

{- for n ≥ 1, we have a group structure on the loop space -}
module _ {i} (n : ℕ) {X : Ptd i} where
  !^S : Ω^ (S n) X → Ω^ (S n) X
  !^S = !

  conc^S : Ω^ (S n) X → Ω^ (S n) X → Ω^ (S n) X
  conc^S = _∙_

{- pointed versions of functions on paths -}

private
  pt-lemma : ∀ {i} {A : Type i} {x y : A} (p : x == y)
    → ! p ∙ (idp ∙' p) == idp
  pt-lemma idp = idp

⊙conc : ∀ {i} {X : Ptd i} → fst (⊙Ω X ⊙× ⊙Ω X ⊙→ ⊙Ω X)
⊙conc = (uncurry _∙_ , idp)

⊙ap : ∀ {i j} {X : Ptd i} {Y : Ptd j}
  → fst (X ⊙→ Y) → fst (⊙Ω X ⊙→ ⊙Ω Y)
⊙ap (f , fpt) = ((λ p → ! fpt ∙ ap f p ∙' fpt) , pt-lemma fpt)

⊙ap2 : ∀ {i j k} {X : Ptd i} {Y : Ptd j} {Z : Ptd k}
  → fst (X ⊙× Y ⊙→ Z) → fst (⊙Ω X ⊙× ⊙Ω Y ⊙→ ⊙Ω Z)
⊙ap2 (f , fpt) = ((λ {(p , q) → ! fpt ∙ ap2 (curry f) p q ∙' fpt}) ,
                  pt-lemma fpt)

⊙ap-∘ : ∀ {i j k} {X : Ptd i} {Y : Ptd j} {Z : Ptd k}
  (g : fst (Y ⊙→ Z)) (f : fst (X ⊙→ Y))
  → ⊙ap (g ⊙∘ f) == ⊙ap g ⊙∘ ⊙ap f
⊙ap-∘ (g , idp) (f , idp) = ⊙λ= (λ p → ap-∘ g f p) idp

⊙ap-idf : ∀ {i} {X : Ptd i} → ⊙ap (⊙idf X) == ⊙idf _
⊙ap-idf = ⊙λ= ap-idf idp

⊙ap2-fst : ∀ {i j} {X : Ptd i} {Y : Ptd j}
  → ⊙ap2 {X = X} {Y = Y} ⊙fst == ⊙fst
⊙ap2-fst = ⊙λ= (uncurry ap2-fst) idp

⊙ap2-snd : ∀ {i j} {X : Ptd i} {Y : Ptd j}
  → ⊙ap2 {X = X} {Y = Y} ⊙snd == ⊙snd
⊙ap2-snd = ⊙λ= (uncurry ap2-snd) idp

⊙ap-ap2 : ∀ {i j k l} {X : Ptd i} {Y : Ptd j} {Z : Ptd k} {W : Ptd l}
  (G : fst (Z ⊙→ W)) (F : fst (X ⊙× Y ⊙→ Z))
  → ⊙ap G ⊙∘ ⊙ap2 F == ⊙ap2 (G ⊙∘ F)
⊙ap-ap2 (g , idp) (f , idp) =
  ⊙λ= (uncurry (ap-ap2 g (curry f))) idp

⊙ap2-ap : ∀ {i j k l m}
  {X : Ptd i} {Y : Ptd j} {U : Ptd k} {V : Ptd l} {Z : Ptd m}
  (G : fst ((U ⊙× V) ⊙→ Z)) (F₁ : fst (X ⊙→ U)) (F₂ : fst (Y ⊙→ V))
  → ⊙ap2 G ⊙∘ pair⊙→ (⊙ap F₁) (⊙ap F₂) == ⊙ap2 (G ⊙∘ pair⊙→ F₁ F₂)
⊙ap2-ap (g , idp) (f₁ , idp) (f₂ , idp) =
  ⊙λ= (λ {(p , q) → ap2-ap-l (curry g) f₁ p (ap f₂ q)
                  ∙ ap2-ap-r (λ x v → g (f₁ x , v)) f₂ p q})
      idp

⊙ap2-diag : ∀ {i j} {X : Ptd i} {Y : Ptd j} (F : fst (X ⊙× X ⊙→ Y))
  → ⊙ap2 F ⊙∘ ⊙diag == ⊙ap (F ⊙∘ ⊙diag)
⊙ap2-diag (f , idp) = ⊙λ= (ap2-diag (curry f)) idp

{- ap and ap2 for higher loop spaces -}

ap^ : ∀ {i j} (n : ℕ) {X : Ptd i} {Y : Ptd j}
  → fst (X ⊙→ Y) → fst (⊙Ω^ n X ⊙→ ⊙Ω^ n Y)
ap^ O F = F
ap^ (S n) F = ⊙ap (ap^ n F)

ap2^ : ∀ {i j k} (n : ℕ) {X : Ptd i} {Y : Ptd j} {Z : Ptd k}
  → fst ((X ⊙× Y) ⊙→ Z)
  → fst ((⊙Ω^ n X ⊙× ⊙Ω^ n Y) ⊙→ ⊙Ω^ n Z)
ap2^ O F = F
ap2^ (S n) F = ⊙ap2 (ap2^ n F)

ap^-idf : ∀ {i} (n : ℕ) {X : Ptd i} → ap^ n (⊙idf X) == ⊙idf _
ap^-idf O = idp
ap^-idf (S n) = ap ⊙ap (ap^-idf n) ∙ ⊙ap-idf

ap^-ap2^ : ∀ {i j k l} (n : ℕ) {X : Ptd i} {Y : Ptd j} {Z : Ptd k} {W : Ptd l}
  (G : fst (Z ⊙→ W)) (F : fst ((X ⊙× Y) ⊙→ Z))
  → ap^ n G ⊙∘ ap2^ n F == ap2^ n (G ⊙∘ F)
ap^-ap2^ O G F = idp
ap^-ap2^ (S n) G F = ⊙ap-ap2 (ap^ n G) (ap2^ n F) ∙ ap ⊙ap2 (ap^-ap2^ n G F)

ap2^-fst : ∀ {i j} (n : ℕ) {X : Ptd i} {Y : Ptd j}
  → ap2^ n {X} {Y} ⊙fst == ⊙fst
ap2^-fst O = idp
ap2^-fst (S n) = ap ⊙ap2 (ap2^-fst n) ∙ ⊙ap2-fst

ap2^-snd : ∀ {i j} (n : ℕ) {X : Ptd i} {Y : Ptd j}
  → ap2^ n {X} {Y} ⊙snd == ⊙snd
ap2^-snd O = idp
ap2^-snd (S n) = ap ⊙ap2 (ap2^-snd n) ∙ ⊙ap2-snd

ap2^-ap^ : ∀ {i j k l m} (n : ℕ)
  {X : Ptd i} {Y : Ptd j} {U : Ptd k} {V : Ptd l} {Z : Ptd m}
  (G : fst ((U ⊙× V) ⊙→ Z)) (F₁ : fst (X ⊙→ U)) (F₂ : fst (Y ⊙→ V))
  → ap2^ n G ⊙∘ pair⊙→ (ap^ n F₁) (ap^ n F₂) == ap2^ n (G ⊙∘ pair⊙→ F₁ F₂)
ap2^-ap^ O G F₁ F₂ = idp
ap2^-ap^ (S n) G F₁ F₂ =
  ⊙ap2-ap (ap2^ n G) (ap^ n F₁) (ap^ n F₂) ∙ ap ⊙ap2 (ap2^-ap^ n G F₁ F₂)

ap2^-diag : ∀ {i j} (n : ℕ) {X : Ptd i} {Y : Ptd j} (F : fst (X ⊙× X ⊙→ Y))
  → ap2^ n F ⊙∘ ⊙diag == ap^ n (F ⊙∘ ⊙diag)
ap2^-diag O F = idp
ap2^-diag (S n) F = ⊙ap2-diag (ap2^ n F) ∙ ap ⊙ap (ap2^-diag n F)

module _ {i} {X : Ptd i} (n : ℕ) where

  {- Prove these as lemmas now
   - so we don't have to deal with the n = O case later -}

  conc^S-unit-l : (q : Ω^ (S n) X)
    → (conc^S n (idp^ (S n)) q) == q
  conc^S-unit-l _ = idp

  conc^S-unit-r : (q : Ω^ (S n) X)
    → (conc^S n q (idp^ (S n))) == q
  conc^S-unit-r = ∙-unit-r

  conc^S-assoc : (p q r : Ω^ (S n) X)
    → conc^S n (conc^S n p q) r == conc^S n p (conc^S n q r)
  conc^S-assoc = ∙-assoc

  !^S-inv-l : (p : Ω^ (S n) X)
    → conc^S n (!^S n p) p == idp^ (S n)
  !^S-inv-l = !-inv-l

  !^S-inv-r : (p : Ω^ (S n) X)
    → conc^S n p (!^S n p) == idp^ (S n)
  !^S-inv-r = !-inv-r

abstract
  ap^S-conc^S : ∀ {i j} (n : ℕ)
    {X : Ptd i} {Y : Ptd j} (F : fst (X ⊙→ Y)) (p q : Ω^ (S n) X)
    → fst (ap^ (S n) F) (conc^S n p q)
      == conc^S n (fst (ap^ (S n) F) p) (fst (ap^ (S n) F) q)
  ap^S-conc^S n {X = X} {Y = Y} F p q =
    ! gpt ∙ ap g (p ∙ q) ∙' gpt
      =⟨ ap-∙ g p q |in-ctx (λ w → ! gpt ∙ w ∙' gpt) ⟩
    ! gpt ∙ (ap g p ∙ ap g q) ∙' gpt
      =⟨ lemma (ap g p) (ap g q) gpt ⟩
    (! gpt ∙ ap g p ∙' gpt) ∙ (! gpt ∙ ap g q ∙' gpt) ∎
    where
    g : Ω^ n X → Ω^ n Y
    g = fst (ap^ n F)

    gpt : g (idp^ n) == idp^ n
    gpt = snd (ap^ n F)

    lemma : ∀ {i} {A : Type i} {x y : A}
      → (p q : x == x) (r : x == y)
      → ! r ∙ (p ∙ q) ∙' r == (! r ∙ p ∙' r) ∙ (! r ∙ q ∙' r)
    lemma p q idp = idp

{- ap^ preserves (pointed) equivalences -}
module _ {i j} {X : Ptd i} {Y : Ptd j} where

  is-equiv-ap^ : (n : ℕ) (F : fst (X ⊙→ Y)) (e : is-equiv (fst F))
    → is-equiv (fst (ap^ n F))
  is-equiv-ap^ O F e = e
  is-equiv-ap^ (S n) F e =
         pre∙-is-equiv (! (snd (ap^ n F)))
    ∘ise post∙'-is-equiv (snd (ap^ n F))
    ∘ise snd (equiv-ap (_ , is-equiv-ap^ n F e) _ _)

  equiv-ap^ : (n : ℕ) (F : fst (X ⊙→ Y)) (e : is-equiv (fst F))
    → Ω^ n X ≃ Ω^ n Y
  equiv-ap^ n F e = (fst (ap^ n F) , is-equiv-ap^ n F e)

Ω^-level-in : ∀ {i} (m : ℕ₋₂) (n : ℕ) (X : Ptd i)
  → (has-level (⟨ n ⟩₋₂ +2+ m) (fst X) → has-level m (Ω^ n X))
Ω^-level-in m O X pX = pX
Ω^-level-in m (S n) X pX =
  Ω^-level-in (S m) n X
    (transport (λ k → has-level k (fst X)) (! (+2+-βr ⟨ n ⟩₋₂ m)) pX)
    (idp^ n) (idp^ n)

Ω^-conn-in : ∀ {i} (m : ℕ₋₂) (n : ℕ) (X : Ptd i)
  → (is-connected (⟨ n ⟩₋₂ +2+ m) (fst X)) → is-connected m (Ω^ n X)
Ω^-conn-in m O X pX = pX
Ω^-conn-in m (S n) X pX =
  path-conn $ Ω^-conn-in (S m) n X $
    transport (λ k → is-connected k (fst X)) (! (+2+-βr ⟨ n ⟩₋₂ m)) pX

{- Eckmann-Hilton argument -}
module _ {i} {X : Ptd i} where

  ap2-conc-is-conc^S : (α β : Ω^ 2 X) → ap2 _∙_ α β == conc^S 1 α β
  ap2-conc-is-conc^S α β = ap2-out _∙_ α β ∙ ap2 _∙_ (lemma α) (ap-idf β)
    where
    lemma : ∀ {i} {A : Type i} {x y : A} {p q : x == y} (α : p == q)
      → ap (λ r → r ∙ idp) α == ∙-unit-r p ∙ α ∙' ! (∙-unit-r q)
    lemma {p = idp} idp = idp

  ⊙ap2-conc-is-conc : ⊙ap2 (⊙conc {X = X}) == ⊙conc
  ⊙ap2-conc-is-conc = ⊙λ= (uncurry ap2-conc-is-conc^S) idp

  conc^2-comm : (α β : Ω^ 2 X) → conc^S 1 α β == conc^S 1 β α
  conc^2-comm α β = ! (⋆2=conc^ α β) ∙ ⋆2=⋆'2 α β ∙ ⋆'2=conc^ α β
    where
      ⋆2=conc^ : (α β : Ω^ 2 X) → α ⋆2 β == conc^S 1 α β
      ⋆2=conc^ α β = ap (λ π → π ∙ β) (∙-unit-r α)

      ⋆'2=conc^ : (α β : Ω^ 2  X) → α ⋆'2 β == conc^S 1 β α
      ⋆'2=conc^ α β = ap (λ π → β ∙ π) (∙-unit-r α)

{- Pushing truncation through loop space -}
module _ {i} where

  Trunc-Ω^ : (m : ℕ₋₂) (n : ℕ) (X : Ptd i)
    → ⊙Trunc m (⊙Ω^ n X) == ⊙Ω^ n (⊙Trunc (⟨ n ⟩₋₂ +2+ m) X)
  Trunc-Ω^ m O X = idp
  Trunc-Ω^ m (S n) X =
    ⊙Trunc m (⊙Ω^ (S n) X)
      =⟨ ! (pair= (Trunc=-path [ _ ] [ _ ]) (↓-idf-ua-in _ idp)) ⟩
    ⊙Ω (⊙Trunc (S m) (⊙Ω^ n X))
      =⟨ ap ⊙Ω (Trunc-Ω^ (S m) n X) ⟩
    ⊙Ω^ (S n) (⊙Trunc (⟨ n ⟩₋₂ +2+ S m) X)
      =⟨ +2+-βr ⟨ n ⟩₋₂ m |in-ctx (λ k → ⊙Ω^ (S n) (⊙Trunc k X)) ⟩
    ⊙Ω^ (S n) (⊙Trunc (S ⟨ n ⟩₋₂ +2+ m) X) ∎

  Ω-Trunc-equiv : (m : ℕ₋₂) (X : Ptd i)
    → Ω (⊙Trunc (S m) X) ≃ Trunc m (Ω X)
  Ω-Trunc-equiv m X = Trunc=-equiv [ snd X ] [ snd X ]

{- A loop space is a pregroup, and a group if it has the right level -}
module _ {i} (n : ℕ) (X : Ptd i) where

  Ω^S-group-structure : GroupStructure (Ω^ (S n) X)
  Ω^S-group-structure = record {
    ident = idp^ (S n);
    inv = !^S n;
    comp = conc^S n;
    unitl = conc^S-unit-l n;
    unitr = conc^S-unit-r n;
    assoc = conc^S-assoc n;
    invr = !^S-inv-r n;
    invl = !^S-inv-l n
    }

  Ω^S-group : has-level ⟨ S n ⟩ (fst X) → Group i
  Ω^S-group pX = group
    (Ω^ (S n) X)
    (Ω^-level-in 0 (S n) X $
       transport (λ t → has-level t (fst X)) (+2+-comm 0 ⟨ S n ⟩₋₂) pX)
    Ω^S-group-structure

{- Our definition of Ω^ builds up loops on the outside,
 - but this is equivalent to building up on the inside -}
module _ {i} where
  ⊙Ω^-inner-path : (n : ℕ) (X : Ptd i)
    → ⊙Ω^ (S n) X == ⊙Ω^ n (⊙Ω X)
  ⊙Ω^-inner-path O X = idp
  ⊙Ω^-inner-path (S n) X = ap ⊙Ω (⊙Ω^-inner-path n X)

  ⊙Ω^-inner-out : (n : ℕ) (X : Ptd i)
    → fst (⊙Ω^ (S n) X ⊙→ ⊙Ω^ n (⊙Ω X))
  ⊙Ω^-inner-out O _ = (idf _ , idp)
  ⊙Ω^-inner-out (S n) X = ap^ 1 (⊙Ω^-inner-out n X)

  Ω^-inner-out : (n : ℕ) (X : Ptd i)
    → (Ω^ (S n) X → Ω^ n (⊙Ω X))
  Ω^-inner-out n X = fst (⊙Ω^-inner-out n X)

  Ω^S-inner-out-conc^S : (n : ℕ)
    (X : Ptd i) (p q : Ω^ (S (S n)) X)
    → Ω^-inner-out (S n) X (conc^S (S n) p q)
      == conc^S n (Ω^-inner-out (S n) X p) (Ω^-inner-out (S n) X q)
  Ω^S-inner-out-conc^S n X p q =
    ap^S-conc^S 0 (⊙Ω^-inner-out n X) p q

  Ω^-inner-is-equiv : (n : ℕ) (X : Ptd i)
    → is-equiv (fst (⊙Ω^-inner-out n X))
  Ω^-inner-is-equiv O X = is-eq (idf _) (idf _) (λ _ → idp) (λ _ → idp)
  Ω^-inner-is-equiv (S n) X =
    is-equiv-ap^ 1 (⊙Ω^-inner-out n X) (Ω^-inner-is-equiv n X)

  Ω^-inner-equiv : (n : ℕ) (X : Ptd i) → Ω^ (S n) X ≃ Ω^ n (⊙Ω X)
  Ω^-inner-equiv n X = _ , Ω^-inner-is-equiv n X


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

open import M-types.Base.Core
open import M-types.Base.Sum
open import M-types.Base.Prod
open import M-types.Base.Eq
open import M-types.Base.Equi
open import M-types.Base.Axiom

module M-types.Base.Rel where
    SpanRel : Ty ℓ → Ty (ℓ-suc ℓ)
    SpanRel {ℓ} X = ∑[ ty ∈ Ty ℓ ] (ty → X) × (ty → X)

    ρ₀ : {X : Ty ℓ₀} {Y Z : X → Ty ℓ₁} →
        ∏[ s ∈ ∑[ x ∈ X ] (Y x × Z x) ] Y (pr₀ s)
    ρ₀ = pr₀ ∘ pr₁

    ρ₁ : {X : Ty ℓ₀} {Y Z : X → Ty ℓ₁} →
        ∏[ s ∈ ∑[ x ∈ X ] (Y x × Z x) ] Z (pr₀ s)
    ρ₁ = pr₁ ∘ pr₁

    SpanRel-syntax : {X : Ty ℓ} →
        ∏[ ∼ ∈ SpanRel X ] ∏[ x₀ ∈ X ] ∏[ x₁ ∈ X ] Ty ℓ
    SpanRel-syntax ∼ x₀ x₁ = ∑[ s ∈ (ty ∼) ] ((ρ₀ ∼ s ≡ x₀) × (ρ₁ ∼ s ≡ x₁))
    syntax SpanRel-syntax ∼ x₀ x₁ = x₀ ⟨ ∼ ⟩ x₁

    ≡-spanRel : {X : Ty ℓ} →
        SpanRel X
    ≡-spanRel {_} {X} = (X , id , id)

    SpanRelMor : {X : Ty ℓ} →
        SpanRel X → SpanRel X → Ty ℓ
    SpanRelMor {_} {X} ∼ ≈ =
        ∑[ fun ∈ (ty ∼ → ty ≈) ]
        (ρ₀ ≈ ∘ fun ≡ ρ₀ ∼) × (ρ₁ ≈ ∘ fun ≡ ρ₁ ∼)

    com₀ : {X : Ty ℓ₀} {Y Z : X → Ty ℓ₁} →
        ∏[ s ∈ ∑[ x ∈ X ] (Y x × Z x) ] Y (pr₀ s)
    com₀ = pr₀ ∘ pr₁

    com₁ : {X : Ty ℓ₀} {Y Z : X → Ty ℓ₁} →
        ∏[ s ∈ ∑[ x ∈ X ] (Y x × Z x) ] Z (pr₀ s)
    com₁ = pr₁ ∘ pr₁

    id-spanRel : {X : Ty ℓ} {∼ : SpanRel X} →
        SpanRelMor ∼ ∼
    id-spanRel {_} {X} {∼} = (id , refl , refl)

    infixr 9 _∘-spanRel_
    _∘-spanRel_ : {X : Ty ℓ} {∼₀ ∼₁ ∼₂ : SpanRel X} →
        SpanRelMor ∼₁ ∼₂ → SpanRelMor ∼₀ ∼₁ → SpanRelMor ∼₀ ∼₂
    g ∘-spanRel f =
        (
            fun g ∘ fun f ,
            ap (λ k → k ∘ fun f) (com₀ g) · com₀ f ,
            ap (λ k → k ∘ fun f) (com₁ g) · com₁ f
        )

    DepRel : Ty ℓ → Ty (ℓ-suc ℓ)
    DepRel {ℓ} X = X → X → Ty ℓ

    DepRel-syntax : {X : Ty ℓ} →
        ∏[ ∼ ∈ DepRel X ] ∏[ x₀ ∈ X ] ∏[ x₁ ∈ X ] Ty ℓ
    DepRel-syntax ∼ x₀ x₁ = ∼ x₀ x₁
    syntax DepRel-syntax ∼ x₀ x₁ = x₀ [ ∼ ] x₁

    ≡-depRel : {X : Ty ℓ} →
        DepRel X
    ≡-depRel = λ x₀ → λ x₁ → x₀ ≡ x₁

    DepRelMor : {X : Ty ℓ} →
        DepRel X → DepRel X → Ty ℓ
    DepRelMor {_} {X} ∼ ≈ =
        ∏[ x₀ ∈ X ] ∏[ x₁ ∈ X ]
        (x₀ [ ∼ ] x₁ → x₀ [ ≈ ] x₁)

    id-depRel : {X : Ty ℓ} {∼ : DepRel X} →
        DepRelMor ∼ ∼
    id-depRel {_} {X} {∼} = λ x₀ → λ x₁ → id

    infixr 9 _∘-depRel_
    _∘-depRel_ : {X : Ty ℓ} {∼₀ ∼₁ ∼₂ : DepRel X} →
        DepRelMor ∼₁ ∼₂ → DepRelMor ∼₀ ∼₁ → DepRelMor ∼₀ ∼₂
    g ∘-depRel f = λ x₀ → λ x₁ → g x₀ x₁ ∘ f x₀ x₁

    SpanRel→DepRel : {X : Ty ℓ} →
        SpanRel X → DepRel X
    SpanRel→DepRel {_} {X} ∼ = λ x₀ → λ x₁ → x₀ ⟨ ∼ ⟩ x₁

    DepRel→SpanRel : {X : Ty ℓ} →
        DepRel X → SpanRel X
    DepRel→SpanRel {_} {X} ∼ =
        (
            (∑[ x₀ ∈ X ] ∑[ x₁ ∈ X ] x₀ [ ∼ ] x₁) ,
            pr₀ ,
            pr₀ ∘ pr₁
        )

    SpanRel→DepRel-pres : {X : Ty ℓ} →
        ∏[ ∼ ∈ SpanRel X ] ∏[ x₀ ∈ X ] ∏[ x₁ ∈ X ]
        (x₀ [ SpanRel→DepRel ∼ ] x₁) ≡ (x₀ ⟨ ∼ ⟩ x₁)
    SpanRel→DepRel-pres ∼ x₀ x₁ = refl

    DepRel→SpanRel-pres : {X : Ty ℓ} →
        ∏[ ∼ ∈ DepRel X ] ∏[ x₀ ∈ X ] ∏[ x₁ ∈ X ]
        (x₀ ⟨ DepRel→SpanRel ∼ ⟩ x₁) ≃ (x₀ [ ∼ ] x₁)
    DepRel→SpanRel-pres ∼ x₀ x₁ =
        (
            (λ ((y₀ , y₁ , s) , p₀ , p₁) →
                tra (λ x → x [ ∼ ] x₁) p₀ (tra (λ y → y₀ [ ∼ ] y) p₁ s)
            ) ,
            Qinv→IsEqui (
                (λ s → ((x₀ , x₁ , s) , refl , refl)) ,
                (λ {((x₀ , x₁ , s) , refl , refl) → refl}) ,
                (λ s → refl)
            )
        )

    SpanRel→SpanRel-mor : {X : Ty ℓ} →
        ∏[ ∼ ∈ SpanRel X ] SpanRelMor ∼ (DepRel→SpanRel (SpanRel→DepRel ∼))
    SpanRel→SpanRel-mor ∼ =
        (
            (λ s → (ρ₀ ∼ s , ρ₁ ∼ s , (s , refl , refl))) ,
            refl ,
            refl
        )

    DepRel→DepRel-mor : {X : Ty ℓ} →
        ∏[ ∼ ∈ DepRel X ] DepRelMor ∼ (SpanRel→DepRel (DepRel→SpanRel ∼))
    DepRel→DepRel-mor ∼ = λ x₀ → λ x₁ → λ s → ((x₀ , x₁ , s) , refl , refl)

    ≡-SpanRel→DepRel-mor : {X : Ty ℓ} →
        DepRelMor {ℓ} {X} (SpanRel→DepRel ≡-spanRel) ≡-depRel
    ≡-SpanRel→DepRel-mor = λ x₀ → λ x₁ → λ(s , p₀ , p₁) → p₀ ⁻¹ · p₁

    ≡-DepRel→SpanRel-mor : {X : Ty ℓ} →
        SpanRelMor {ℓ} {X} (DepRel→SpanRel ≡-depRel) ≡-spanRel
    ≡-DepRel→SpanRel-mor =
        (
            (λ (x₀ , x₁ , p) → x₀) ,
            refl ,
            funext (λ (x₀ , x₁ , p) → p)
        )

    SpanRelMor→DepRelMor : {X : Ty ℓ} {∼ ≈ : SpanRel X} →
        SpanRelMor ∼ ≈ → DepRelMor (SpanRel→DepRel ∼) (SpanRel→DepRel ≈)
    SpanRelMor→DepRelMor (fun , refl , refl) x₀ x₁ (s , refl , refl) =
        (
            fun s ,
            refl ,
            refl
        )

    DepRelMor→SpanRelMor : {X : Ty ℓ} {∼ ≈ : DepRel X} →
        DepRelMor ∼ ≈ → SpanRelMor (DepRel→SpanRel ∼) (DepRel→SpanRel ≈)
    DepRelMor→SpanRelMor f =
        (
            (λ (x₀ , x₁ , s) → (x₀ , x₁ , f x₀ x₁ s)) ,
            refl {_} {_} {pr₀}  ,
            refl
        )


-- Basic intuitionistic modal logic S4, without ∨, ⊥, or ◇.
-- Hilbert-style formalisation of syntax with context pairs.
-- Nested terms.

module BasicIS4.Syntax.DyadicHilbert where

open import BasicIS4.Syntax.Common public

-- Derivations.

infix 3 _⊢_
data _⊢_ : Cx² Ty Ty → Ty → Set where
  var   : ∀ {A Γ Δ}     → A ∈ Γ → Γ ⁏ Δ ⊢ A
  app   : ∀ {A B Γ Δ}   → Γ ⁏ Δ ⊢ A ▻ B → Γ ⁏ Δ ⊢ A → Γ ⁏ Δ ⊢ B
  ci    : ∀ {A Γ Δ}     → Γ ⁏ Δ ⊢ A ▻ A
  ck    : ∀ {A B Γ Δ}   → Γ ⁏ Δ ⊢ A ▻ B ▻ A
  cs    : ∀ {A B C Γ Δ} → Γ ⁏ Δ ⊢ (A ▻ B ▻ C) ▻ (A ▻ B) ▻ A ▻ C
  mvar  : ∀ {A Γ Δ}     → A ∈ Δ → Γ ⁏ Δ ⊢ A
  box   : ∀ {A Γ Δ}     → ∅ ⁏ Δ ⊢ A → Γ ⁏ Δ ⊢ □ A
  cdist : ∀ {A B Γ Δ}   → Γ ⁏ Δ ⊢ □ (A ▻ B) ▻ □ A ▻ □ B
  cup   : ∀ {A Γ Δ}     → Γ ⁏ Δ ⊢ □ A ▻ □ □ A
  cdown : ∀ {A Γ Δ}     → Γ ⁏ Δ ⊢ □ A ▻ A
  cpair : ∀ {A B Γ Δ}   → Γ ⁏ Δ ⊢ A ▻ B ▻ A ∧ B
  cfst  : ∀ {A B Γ Δ}   → Γ ⁏ Δ ⊢ A ∧ B ▻ A
  csnd  : ∀ {A B Γ Δ}   → Γ ⁏ Δ ⊢ A ∧ B ▻ B
  unit  : ∀ {Γ Δ}       → Γ ⁏ Δ ⊢ ⊤

infix 3 _⊢⋆_
_⊢⋆_ : Cx² Ty Ty → Cx Ty → Set
Γ ⁏ Δ ⊢⋆ ∅     = 𝟙
Γ ⁏ Δ ⊢⋆ Ξ , A = Γ ⁏ Δ ⊢⋆ Ξ × Γ ⁏ Δ ⊢ A

-- Monotonicity with respect to context inclusion.

mono⊢ : ∀ {A Γ Γ′ Δ} → Γ ⊆ Γ′ → Γ ⁏ Δ ⊢ A → Γ′ ⁏ Δ ⊢ A
mono⊢ η (var i)   = var (mono∈ η i)
mono⊢ η (app t u) = app (mono⊢ η t) (mono⊢ η u)
mono⊢ η ci        = ci
mono⊢ η ck        = ck
mono⊢ η cs        = cs
mono⊢ η (mvar i)  = mvar i
mono⊢ η (box t)   = box t
mono⊢ η cdist     = cdist
mono⊢ η cup       = cup
mono⊢ η cdown     = cdown
mono⊢ η cpair     = cpair
mono⊢ η cfst      = cfst
mono⊢ η csnd      = csnd
mono⊢ η unit      = unit

mono⊢⋆ : ∀ {Ξ Γ Γ′ Δ} → Γ ⊆ Γ′ → Γ ⁏ Δ ⊢⋆ Ξ → Γ′ ⁏ Δ ⊢⋆ Ξ
mono⊢⋆ {∅}     η ∙        = ∙
mono⊢⋆ {Ξ , A} η (ts , t) = mono⊢⋆ η ts , mono⊢ η t

-- Monotonicity with respect to modal context inclusion.

mmono⊢ : ∀ {A Γ Δ Δ′} → Δ ⊆ Δ′ → Γ ⁏ Δ ⊢ A → Γ ⁏ Δ′ ⊢ A
mmono⊢ θ (var i)   = var i
mmono⊢ θ (app t u) = app (mmono⊢ θ t) (mmono⊢ θ u)
mmono⊢ θ ci        = ci
mmono⊢ θ ck        = ck
mmono⊢ θ cs        = cs
mmono⊢ θ (mvar i)  = mvar (mono∈ θ i)
mmono⊢ θ (box t)   = box (mmono⊢ θ t)
mmono⊢ θ cdist     = cdist
mmono⊢ θ cup       = cup
mmono⊢ θ cdown     = cdown
mmono⊢ θ cpair     = cpair
mmono⊢ θ cfst      = cfst
mmono⊢ θ csnd      = csnd
mmono⊢ θ unit      = unit

mmono⊢⋆ : ∀ {Ξ Γ Δ Δ′} → Δ ⊆ Δ′ → Γ ⁏ Δ ⊢⋆ Ξ → Γ ⁏ Δ′ ⊢⋆ Ξ
mmono⊢⋆ {∅}     θ ∙        = ∙
mmono⊢⋆ {Ξ , A} θ (ts , t) = mmono⊢⋆ θ ts , mmono⊢ θ t

-- Monotonicity using context pairs.

mono²⊢ : ∀ {A Π Π′} → Π ⊆² Π′ → Π ⊢ A → Π′ ⊢ A
mono²⊢ (η , θ) = mono⊢ η ∘ mmono⊢ θ

-- Shorthand for variables.

v₀ : ∀ {A Γ Δ} → Γ , A ⁏ Δ ⊢ A
v₀ = var i₀

v₁ : ∀ {A B Γ Δ} → Γ , A , B ⁏ Δ ⊢ A
v₁ = var i₁

v₂ : ∀ {A B C Γ Δ} → Γ , A , B , C ⁏ Δ ⊢ A
v₂ = var i₂

mv₀ : ∀ {A Γ Δ} → Γ ⁏ Δ , A ⊢ A
mv₀ = mvar i₀

mv₁ : ∀ {A B Γ Δ} → Γ ⁏ Δ , A , B ⊢ A
mv₁ = mvar i₁

mv₂ : ∀ {A B C Γ Δ} → Γ ⁏ Δ , A , B , C ⊢ A
mv₂ = mvar i₂

-- Reflexivity.

refl⊢⋆ : ∀ {Γ Δ} → Γ ⁏ Δ ⊢⋆ Γ
refl⊢⋆ {∅}     = ∙
refl⊢⋆ {Γ , A} = mono⊢⋆ weak⊆ refl⊢⋆ , v₀

mrefl⊢⋆ : ∀ {Δ Γ} → Γ ⁏ Δ ⊢⋆ □⋆ Δ
mrefl⊢⋆ {∅}     = ∙
mrefl⊢⋆ {Δ , A} = mmono⊢⋆ weak⊆ mrefl⊢⋆ , box mv₀

-- Deduction theorem.

lam : ∀ {A B Γ Δ} → Γ , A ⁏ Δ ⊢ B → Γ ⁏ Δ ⊢ A ▻ B
lam (var top)     = ci
lam (var (pop i)) = app ck (var i)
lam (app t u)     = app (app cs (lam t)) (lam u)
lam ci            = app ck ci
lam ck            = app ck ck
lam cs            = app ck cs
lam (mvar i)      = app ck (mvar i)
lam (box t)       = app ck (box t)
lam cdist         = app ck cdist
lam cup           = app ck cup
lam cdown         = app ck cdown
lam cpair         = app ck cpair
lam cfst          = app ck cfst
lam csnd          = app ck csnd
lam unit          = app ck unit

lam⋆ : ∀ {Ξ A Γ Δ} → Γ ⧺ Ξ ⁏ Δ ⊢ A → Γ ⁏ Δ ⊢ Ξ ▻⋯▻ A
lam⋆ {∅}     = I
lam⋆ {Ξ , B} = lam⋆ {Ξ} ∘ lam

lam⋆₀ : ∀ {Γ A Δ} → Γ ⁏ Δ ⊢ A → ∅ ⁏ Δ ⊢ Γ ▻⋯▻ A
lam⋆₀ {∅}     = I
lam⋆₀ {Γ , B} = lam⋆₀ ∘ lam

-- Combined axioms of distribution and transitivity.

cdistup : ∀ {A B Γ Δ} → Γ ⁏ Δ ⊢ □ (□ A ▻ B) ▻ □ A ▻ □ B
cdistup = app (app cs (app (app cs (app ck cs))
                           (app (app cs (app (app cs (app ck cs))
                                             (lam (lam cdist))))
                                (app (app cs (app ck ck)) ci))))
              (app (app cs (app (app cs (app ck cs))
                                (lam (lam cup))))
                   (app ck ci))

-- Modal deduction theorem.

mlam : ∀ {A B Γ Δ} → Γ ⁏ Δ , A ⊢ B → Γ ⁏ Δ ⊢ □ A ▻ B
mlam (var i)        = app ck (var i)
mlam (app t u)      = app (app cs (mlam t)) (mlam u)
mlam ci             = app ck ci
mlam ck             = app ck ck
mlam cs             = app ck cs
mlam (mvar top)     = cdown
mlam (mvar (pop i)) = app ck (mvar i)
mlam (box t)        = app cdistup (box (mlam t))
mlam cdist          = app ck cdist
mlam cup            = app ck cup
mlam cdown          = app ck cdown
mlam cpair          = app ck cpair
mlam cfst           = app ck cfst
mlam csnd           = app ck csnd
mlam unit           = app ck unit

mlam⋆ : ∀ {Ξ A Γ Δ} → Γ ⁏ Δ ⧺ Ξ ⊢ A → Γ ⁏ Δ ⊢ □⋆ Ξ ▻⋯▻ A
mlam⋆ {∅}     = I
mlam⋆ {Ξ , B} = mlam⋆ {Ξ} ∘ mlam

mlam⋆₀ : ∀ {Δ A Γ} → Γ ⁏ Δ ⊢ A → Γ ⁏ ∅ ⊢ □⋆ Δ ▻⋯▻ A
mlam⋆₀ {∅}     = I
mlam⋆₀ {Δ , B} = mlam⋆₀ ∘ mlam

-- Detachment theorems.

det : ∀ {A B Γ Δ} → Γ ⁏ Δ ⊢ A ▻ B → Γ , A ⁏ Δ ⊢ B
det t = app (mono⊢ weak⊆ t) v₀

det⋆ : ∀ {Ξ A Γ Δ} → Γ ⁏ Δ ⊢ Ξ ▻⋯▻ A → Γ ⧺ Ξ ⁏ Δ ⊢ A
det⋆ {∅}     = I
det⋆ {Ξ , B} = det ∘ det⋆ {Ξ}

det⋆₀ : ∀ {Γ A Δ} → ∅ ⁏ Δ ⊢ Γ ▻⋯▻ A → Γ ⁏ Δ ⊢ A
det⋆₀ {∅}     = I
det⋆₀ {Γ , B} = det ∘ det⋆₀

mdet : ∀ {A B Γ Δ} → Γ ⁏ Δ ⊢ □ A ▻ B → Γ ⁏ Δ , A ⊢ B
mdet t = app (mmono⊢ weak⊆ t) (box mv₀)

mdet⋆ : ∀ {Ξ A Γ Δ} → Γ ⁏ Δ ⊢ □⋆ Ξ ▻⋯▻ A → Γ ⁏ Δ ⧺ Ξ ⊢ A
mdet⋆ {∅}     = I
mdet⋆ {Ξ , B} = mdet ∘ mdet⋆ {Ξ}

mdet⋆₀ : ∀ {Δ A Γ} → Γ ⁏ ∅ ⊢ □⋆ Δ ▻⋯▻ A → Γ ⁏ Δ ⊢ A
mdet⋆₀ {∅}     = I
mdet⋆₀ {Δ , B} = mdet ∘ mdet⋆₀

-- Context manipulation.

merge : ∀ {Δ A Γ} → Γ ⁏ Δ ⊢ A → Γ ⧺ (□⋆ Δ) ⁏ ∅ ⊢ A
merge {Δ} = det⋆ {□⋆ Δ} ∘ mlam⋆₀

mmerge : ∀ {Γ A Δ} → □⋆ Γ ⁏ Δ ⊢ A → ∅ ⁏ Δ ⧺ Γ ⊢ A
mmerge {Γ} = mdet⋆ {Γ} ∘ lam⋆₀

split : ∀ {Δ A Γ} → Γ ⧺ (□⋆ Δ) ⁏ ∅ ⊢ A → Γ ⁏ Δ ⊢ A
split {Δ} = mdet⋆₀ ∘ lam⋆ {□⋆ Δ}

msplit : ∀ {Γ A Δ} → ∅ ⁏ Δ ⧺ Γ ⊢ A → □⋆ Γ ⁏ Δ ⊢ A
msplit {Γ} = det⋆₀ ∘ mlam⋆ {Γ}

merge⋆ : ∀ {Ξ Δ Γ} → Γ ⁏ Δ ⊢⋆ Ξ → Γ ⧺ (□⋆ Δ) ⁏ ∅ ⊢⋆ Ξ
merge⋆ {∅}     ∙        = ∙
merge⋆ {Ξ , A} (ts , t) = merge⋆ ts , merge t

split⋆ : ∀ {Ξ Δ Γ} → Γ ⧺ (□⋆ Δ) ⁏ ∅ ⊢⋆ Ξ → Γ ⁏ Δ ⊢⋆ Ξ
split⋆ {∅}     ∙        = ∙
split⋆ {Ξ , A} (ts , t) = split⋆ ts , split t

-- Cut and multicut.

cut : ∀ {A B Γ Δ} → Γ ⁏ Δ ⊢ A → Γ , A ⁏ Δ ⊢ B → Γ ⁏ Δ ⊢ B
cut t u = app (lam u) t

mcut : ∀ {A B Γ Δ} → Γ ⁏ Δ ⊢ □ A → Γ ⁏ Δ , A ⊢ B → Γ ⁏ Δ ⊢ B
mcut t u = app (mlam u) t

multicut : ∀ {Ξ A Γ Δ} → Γ ⁏ Δ ⊢⋆ Ξ → Ξ ⁏ Δ ⊢ A → Γ ⁏ Δ ⊢ A
multicut {∅}     ∙        u = mono⊢ bot⊆ u
multicut {Ξ , B} (ts , t) u = app (multicut ts (lam u)) t

mmulticut : ∀ {Ξ A Γ Δ} → Γ ⁏ Δ ⊢⋆ □⋆ Ξ → Γ ⁏ Ξ ⊢ A → Γ ⁏ Δ ⊢ A
mmulticut {∅}     ∙        u = mmono⊢ bot⊆ u
mmulticut {Ξ , B} (ts , t) u = app (mmulticut ts (mlam u)) t

-- Transitivity.

trans⊢⋆₀ : ∀ {Γ″ Γ′ Γ} → Γ ⁏ ∅ ⊢⋆ Γ′ → Γ′ ⁏ ∅ ⊢⋆ Γ″ → Γ ⁏ ∅ ⊢⋆ Γ″
trans⊢⋆₀ {∅}      ts ∙        = ∙
trans⊢⋆₀ {Γ″ , A} ts (us , u) = trans⊢⋆₀ ts us , multicut ts u

trans⊢⋆ : ∀ {Ξ Γ Γ′ Δ Δ′} → Γ ⁏ Δ ⊢⋆ Γ′ ⧺ (□⋆ Δ′) → Γ′ ⁏ Δ′ ⊢⋆ Ξ → Γ ⁏ Δ ⊢⋆ Ξ
trans⊢⋆ ts us = split⋆ (trans⊢⋆₀ (merge⋆ ts) (merge⋆ us))

-- Contraction.

ccont : ∀ {A B Γ Δ} → Γ ⁏ Δ ⊢ (A ▻ A ▻ B) ▻ A ▻ B
ccont = lam (lam (app (app v₁ v₀) v₀))

cont : ∀ {A B Γ Δ} → Γ , A , A ⁏ Δ ⊢ B → Γ , A ⁏ Δ ⊢ B
cont t = det (app ccont (lam (lam t)))

mcont : ∀ {A B Γ Δ} → Γ ⁏ Δ , A , A ⊢ B → Γ ⁏ Δ , A ⊢ B
mcont t = mdet (app ccont (mlam (mlam t)))

-- Exchange, or Schönfinkel’s C combinator.

cexch : ∀ {A B C Γ Δ} → Γ ⁏ Δ ⊢ (A ▻ B ▻ C) ▻ B ▻ A ▻ C
cexch = lam (lam (lam (app (app v₂ v₀) v₁)))

exch : ∀ {A B C Γ Δ} → Γ , A , B ⁏ Δ ⊢ C → Γ , B , A ⁏ Δ ⊢ C
exch t = det (det (app cexch (lam (lam t))))

mexch : ∀ {A B C Γ Δ} → Γ ⁏ Δ , A , B ⊢ C → Γ ⁏ Δ , B , A ⊢ C
mexch t = mdet (mdet (app cexch (mlam (mlam t))))

-- Composition, or Schönfinkel’s B combinator.

ccomp : ∀ {A B C Γ Δ} → Γ ⁏ Δ ⊢ (B ▻ C) ▻ (A ▻ B) ▻ A ▻ C
ccomp = lam (lam (lam (app v₂ (app v₁ v₀))))

comp : ∀ {A B C Γ Δ} → Γ , B ⁏ Δ ⊢ C → Γ , A ⁏ Δ ⊢ B → Γ , A ⁏ Δ ⊢ C
comp t u = det (app (app ccomp (lam t)) (lam u))

mcomp : ∀ {A B C Γ Δ} → Γ ⁏ Δ , B ⊢ □ C → Γ ⁏ Δ , A ⊢ □ B → Γ ⁏ Δ , A ⊢ □ C
mcomp t u = mdet (app (app ccomp (mlam t)) (mlam u))

-- Useful theorems in functional form.

dist : ∀ {A B Γ Δ} → Γ ⁏ Δ ⊢ □ (A ▻ B) → Γ ⁏ Δ ⊢ □ A → Γ ⁏ Δ ⊢ □ B
dist t u = app (app cdist t) u

up : ∀ {A Γ Δ} → Γ ⁏ Δ ⊢ □ A → Γ ⁏ Δ ⊢ □ □ A
up t = app cup t

down : ∀ {A Γ Δ} → Γ ⁏ Δ ⊢ □ A → Γ ⁏ Δ ⊢ A
down t = app cdown t

distup : ∀ {A B Γ Δ} → Γ ⁏ Δ ⊢ □ (□ A ▻ B) → Γ ⁏ Δ ⊢ □ A → Γ ⁏ Δ ⊢ □ B
distup t u = dist t (up u)

unbox : ∀ {A C Γ Δ} → Γ ⁏ Δ ⊢ □ A → Γ ⁏ Δ , A ⊢ C → Γ ⁏ Δ ⊢ C
unbox t u = app (mlam u) t

pair : ∀ {A B Γ Δ} → Γ ⁏ Δ ⊢ A → Γ ⁏ Δ ⊢ B → Γ ⁏ Δ ⊢ A ∧ B
pair t u = app (app cpair t) u

fst : ∀ {A B Γ Δ} → Γ ⁏ Δ ⊢ A ∧ B → Γ ⁏ Δ ⊢ A
fst t = app cfst t

snd : ∀ {A B Γ Δ} → Γ ⁏ Δ ⊢ A ∧ B → Γ ⁏ Δ ⊢ B
snd t = app csnd t

-- Internalisation, or lifting, and additional theorems.

lift : ∀ {Γ A Δ} → Γ ⁏ Δ ⊢ A → □⋆ Γ ⁏ Δ ⊢ □ A
lift {∅}     t = box t
lift {Γ , B} t = det (app cdist (lift (lam t)))

hypup : ∀ {A B Γ Δ} → Γ ⁏ Δ ⊢ A ▻ B → Γ ⁏ Δ ⊢ □ A ▻ B
hypup t = lam (app (mono⊢ weak⊆ t) (down v₀))

hypdown : ∀ {A B Γ Δ} → Γ ⁏ Δ ⊢ □ □ A ▻ B → Γ ⁏ Δ ⊢ □ A ▻ B
hypdown t = lam (app (mono⊢ weak⊆ t) (up v₀))

cxup : ∀ {Γ A Δ} → Γ ⁏ Δ ⊢ A → □⋆ Γ ⁏ Δ ⊢ A
cxup {∅}     t = t
cxup {Γ , B} t = det (hypup (cxup (lam t)))

cxdown : ∀ {Γ A Δ} → □⋆ □⋆ Γ ⁏ Δ ⊢ A → □⋆ Γ ⁏ Δ ⊢ A
cxdown {∅}     t = t
cxdown {Γ , B} t = det (hypdown (cxdown (lam t)))

box⋆ : ∀ {Ξ Γ Δ} → ∅ ⁏ Δ ⊢⋆ Ξ → Γ ⁏ Δ ⊢⋆ □⋆ Ξ
box⋆ {∅}     ∙        = ∙
box⋆ {Ξ , A} (ts , t) = box⋆ ts , box t

lift⋆ : ∀ {Ξ Γ Δ} → Γ ⁏ Δ ⊢⋆ Ξ → □⋆ Γ ⁏ Δ ⊢⋆ □⋆ Ξ
lift⋆ {∅}     ∙        = ∙
lift⋆ {Ξ , A} (ts , t) = lift⋆ ts , lift t

up⋆ : ∀ {Ξ Γ Δ} → Γ ⁏ Δ ⊢⋆ □⋆ Ξ → Γ ⁏ Δ ⊢⋆ □⋆ □⋆ Ξ
up⋆ {∅}     ∙        = ∙
up⋆ {Ξ , A} (ts , t) = up⋆ ts , up t

down⋆ : ∀ {Ξ Γ Δ} → Γ ⁏ Δ ⊢⋆ □⋆ Ξ → Γ ⁏ Δ ⊢⋆ Ξ
down⋆ {∅}     ∙        = ∙
down⋆ {Ξ , A} (ts , t) = down⋆ ts , down t

multibox : ∀ {Ξ A Γ Δ} → Γ ⁏ Δ ⊢⋆ □⋆ Ξ → □⋆ Ξ ⁏ ∅ ⊢ A → Γ ⁏ Δ ⊢ □ A
multibox ts u = multicut (up⋆ ts) (mmono⊢ bot⊆ (lift u))

dist′ : ∀ {A B Γ Δ} → Γ ⁏ Δ ⊢ □ (A ▻ B) → Γ ⁏ Δ ⊢ □ A ▻ □ B
dist′ t = lam (dist (mono⊢ weak⊆ t) v₀)

mpair : ∀ {A B Γ Δ} → Γ ⁏ Δ ⊢ □ A → Γ ⁏ Δ ⊢ □ B → Γ ⁏ Δ ⊢ □ (A ∧ B)
mpair t u = dist (dist (box cpair) t) u

mfst : ∀ {A B Γ Δ} → Γ ⁏ Δ ⊢ □ (A ∧ B) → Γ ⁏ Δ ⊢ □ A
mfst t = dist (box cfst) t

msnd : ∀ {A B Γ Δ} → Γ ⁏ Δ ⊢ □ (A ∧ B) → Γ ⁏ Δ ⊢ □ B
msnd t = dist (box csnd) t

-- Closure under context concatenation.

concat : ∀ {A B Γ} Γ′ {Δ} → Γ , A ⁏ Δ ⊢ B → Γ′ ⁏ Δ ⊢ A → Γ ⧺ Γ′ ⁏ Δ ⊢ B
concat Γ′ t u = app (mono⊢ (weak⊆⧺₁ Γ′) (lam t)) (mono⊢ weak⊆⧺₂ u)

mconcat : ∀ {A B Γ Δ} Δ′ → Γ ⁏ Δ , A ⊢ B → Γ ⁏ Δ′ ⊢ □ A → Γ ⁏ Δ ⧺ Δ′ ⊢ B
mconcat Δ′ t u = app (mmono⊢ (weak⊆⧺₁ Δ′) (mlam t)) (mmono⊢ weak⊆⧺₂ u)

-- Convertibility.

data _⋙_ {Γ Δ : Cx Ty} : ∀ {A} → Γ ⁏ Δ ⊢ A → Γ ⁏ Δ ⊢ A → Set where
  refl⋙     : ∀ {A} → {t : Γ ⁏ Δ ⊢ A}
                     → t ⋙ t

  trans⋙    : ∀ {A} → {t t′ t″ : Γ ⁏ Δ ⊢ A}
                     → t ⋙ t′ → t′ ⋙ t″
                     → t ⋙ t″

  sym⋙      : ∀ {A} → {t t′ : Γ ⁏ Δ ⊢ A}
                     → t ⋙ t′
                     → t′ ⋙ t

  congapp⋙  : ∀ {A B} → {t t′ : Γ ⁏ Δ ⊢ A ▻ B} → {u u′ : Γ ⁏ Δ ⊢ A}
                       → t ⋙ t′ → u ⋙ u′
                       → app t u ⋙ app t′ u′

  congi⋙    : ∀ {A} → {t t′ : Γ ⁏ Δ ⊢ A}
                     → t ⋙ t′
                     → app ci t ⋙ app ci t′

  congk⋙    : ∀ {A B} → {t t′ : Γ ⁏ Δ ⊢ A} → {u u′ : Γ ⁏ Δ ⊢ B}
                       → t ⋙ t′ → u ⋙ u′
                       → app (app ck t) u ⋙ app (app ck t′) u′

  congs⋙    : ∀ {A B C} → {t t′ : Γ ⁏ Δ ⊢ A ▻ B ▻ C} → {u u′ : Γ ⁏ Δ ⊢ A ▻ B} → {v v′ : Γ ⁏ Δ ⊢ A}
                         → t ⋙ t′ → u ⋙ u′ → v ⋙ v′

  -- NOTE: Rejected by Pfenning and Davies.
  -- congbox⋙  : ∀ {A} → {t t′ : ∅ ⁏ Δ ⊢ A}
  --                    → t ⋙ t′
  --                    → box {Γ} t ⋙ box {Γ} t′

  congdist⋙ : ∀ {A B} → {t t′ : Γ ⁏ Δ ⊢ □ (A ▻ B)} → {u u′ : Γ ⁏ Δ ⊢ □ A}
                       → t ⋙ t′ → u ⋙ u′
                       → app (app cdist t) u ⋙ app (app cdist t′) u′

  congup⋙   : ∀ {A} → {t t′ : Γ ⁏ Δ ⊢ □ A}
                     → t ⋙ t′
                     → app cup t ⋙ app cup t′

  congdown⋙ : ∀ {A} → {t t′ : Γ ⁏ Δ ⊢ □ A}
                     → t ⋙ t′
                     → app cdown t ⋙ app cdown t′

  congpair⋙ : ∀ {A B} → {t t′ : Γ ⁏ Δ ⊢ A} → {u u′ : Γ ⁏ Δ ⊢ B}
                       → t ⋙ t′ → u ⋙ u′
                       → app (app cpair t) u ⋙ app (app cpair t′) u′

  congfst⋙  : ∀ {A B} → {t t′ : Γ ⁏ Δ ⊢ A ∧ B}
                       → t ⋙ t′
                       → app cfst t ⋙ app cfst t′

  congsnd⋙  : ∀ {A B} → {t t′ : Γ ⁏ Δ ⊢ A ∧ B}
                       → t ⋙ t′
                       → app csnd t ⋙ app csnd t′

  -- TODO: Verify this.
  beta▻ₖ⋙   : ∀ {A B} → {t : Γ ⁏ Δ ⊢ A} → {u : Γ ⁏ Δ ⊢ B}
                       → app (app ck t) u ⋙ t

  -- TODO: Verify this.
  beta▻ₛ⋙   : ∀ {A B C} → {t : Γ ⁏ Δ ⊢ A ▻ B ▻ C} → {u : Γ ⁏ Δ ⊢ A ▻ B} → {v : Γ ⁏ Δ ⊢ A}
                         → app (app (app cs t) u) v ⋙ app (app t v) (app u v)

  -- TODO: What about eta for ▻? What about beta, eta, and commuting conversions for □?

  beta∧₁⋙   : ∀ {A B} → {t : Γ ⁏ Δ ⊢ A} → {u : Γ ⁏ Δ ⊢ B}
                       → app cfst (app (app cpair t) u) ⋙ t

  beta∧₂⋙   : ∀ {A B} → {t : Γ ⁏ Δ ⊢ A} → {u : Γ ⁏ Δ ⊢ B}
                       → app csnd (app (app cpair t) u) ⋙ u

  eta∧⋙     : ∀ {A B} → {t : Γ ⁏ Δ ⊢ A ∧ B}
                       → t ⋙ app (app cpair (app cfst t)) (app csnd t)

  eta⊤⋙    : ∀ {t : Γ ⁏ Δ ⊢ ⊤} → t ⋙ unit


module OldBasicILP.Syntax.Projection where

open import Common.UntypedContext public

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

-- Projection of types and derivations to a form parametrised by a closed, untyped representation of syntax.

module ClosedHilbertSequential where
  open import OldBasicILP.Syntax.ClosedHilbertSequential

  mutual
    ⌊_⌋ᵀ : Ty → CHS.Ty
    ⌊ α P ⌋ᵀ   = CHS.α P
    ⌊ A ▻ B ⌋ᵀ = ⌊ A ⌋ᵀ CHS.▻ ⌊ B ⌋ᵀ
    ⌊ p ⦂ A ⌋ᵀ = ⌊ p ⌋ᴾ CHS.⦂ ⌊ A ⌋ᵀ
    ⌊ A ∧ B ⌋ᵀ = ⌊ A ⌋ᵀ CHS.∧ ⌊ B ⌋ᵀ
    ⌊ ⊤ ⌋ᵀ    = CHS.⊤

    -- FIXME: WHat is going on here?
    postulate
      ⌊_⌋ᴾ : ∀ {Ξ A} → Proof Ξ A → CHS.Proof

    ⌊_⌋ᵀ⋆ : Cx Ty → Cx CHS.Ty
    ⌊ ∅ ⌋ᵀ⋆     = ∅
    ⌊ Γ , A ⌋ᵀ⋆ = ⌊ Γ ⌋ᵀ⋆ , ⌊ A ⌋ᵀ

    ⌊_⌋∈ : ∀ {Ξ A} → A ∈ Ξ → ⌊ A ⌋ᵀ ∈ ⌊ Ξ ⌋ᵀ⋆
    ⌊ top ⌋∈   = top
    ⌊ pop i ⌋∈ = pop ⌊ i ⌋∈

    ⌊_⌋ᴰ : ∀ {Ξ} → ⊢ᴰ Ξ → CHS.⊢ᴰ ⌊ Ξ ⌋ᵀ⋆
    ⌊ nil ⌋ᴰ      = CHS.nil
    ⌊ mp i j d ⌋ᴰ = CHS.mp ⌊ i ⌋∈ ⌊ j ⌋∈ ⌊ d ⌋ᴰ
    ⌊ ci d ⌋ᴰ     = CHS.ci ⌊ d ⌋ᴰ
    ⌊ ck d ⌋ᴰ     = CHS.ck ⌊ d ⌋ᴰ
    ⌊ cs d ⌋ᴰ     = CHS.cs ⌊ d ⌋ᴰ
    ⌊ nec `d d ⌋ᴰ = {!CHS.nec ⌊ `d ⌋ᴰ ⌊ d ⌋ᴰ!}
    ⌊ cdist d ⌋ᴰ  = {!CHS.cdist ⌊ d ⌋ᴰ!}
    ⌊ cup d ⌋ᴰ    = {!CHS.cup ⌊ d ⌋ᴰ!}
    ⌊ cdown d ⌋ᴰ  = CHS.cdown ⌊ d ⌋ᴰ
    ⌊ cpair d ⌋ᴰ  = CHS.cpair ⌊ d ⌋ᴰ
    ⌊ cfst d ⌋ᴰ   = CHS.cfst ⌊ d ⌋ᴰ
    ⌊ csnd d ⌋ᴰ   = CHS.csnd ⌊ d ⌋ᴰ
    ⌊ unit d ⌋ᴰ   = CHS.unit ⌊ d ⌋ᴰ

    ⌊_⌋ : ∀ {A} → ⊢ A → CHS.⊢ ⌊ A ⌋ᵀ
    ⌊ Ξ , d ⌋ = ⌊ Ξ ⌋ᵀ⋆ , ⌊ d ⌋ᴰ

-- Projection of types and derivations to a form parametrised by a closed, untyped representation of syntax.

module ClosedHilbert where
  open import OldBasicILP.Syntax.ClosedHilbert

  mutual
    ⌊_⌋ᵀ : Ty → CH.Ty
    ⌊ α P ⌋ᵀ   = CH.α P
    ⌊ A ▻ B ⌋ᵀ = ⌊ A ⌋ᵀ CH.▻ ⌊ B ⌋ᵀ
    ⌊ p ⦂ A ⌋ᵀ = ⌊ p ⌋ᴾ CH.⦂ ⌊ A ⌋ᵀ
    ⌊ A ∧ B ⌋ᵀ = ⌊ A ⌋ᵀ CH.∧ ⌊ B ⌋ᵀ
    ⌊ ⊤ ⌋ᵀ    = CH.⊤

    ⌊_⌋ᴾ : ∀ {A} → Proof A → CH.Proof
    ⌊ [ d ] ⌋ᴾ = CH.[ CH.ᴿ⌊ ⌊ d ⌋ ⌋ ]

    ⌊_⌋ : ∀ {A} → ⊢ A → CH.⊢ ⌊ A ⌋ᵀ
    ⌊ app d₁ d₂ ⌋ = CH.app ⌊ d₁ ⌋ ⌊ d₂ ⌋
    ⌊ ci ⌋        = CH.ci
    ⌊ ck ⌋        = CH.ck
    ⌊ cs ⌋        = CH.cs
    ⌊ box d ⌋     = CH.box ⌊ d ⌋
    ⌊ cdist ⌋     = CH.cdist
    ⌊ cup ⌋       = CH.cup
    ⌊ cdown ⌋     = CH.cdown
    ⌊ cpair ⌋     = CH.cpair
    ⌊ cfst ⌋      = CH.cfst
    ⌊ csnd ⌋      = CH.csnd
    ⌊ unit ⌋      = CH.unit


-- Intuitionistic propositional calculus.
-- Kripke-style semantics with exploding abstract worlds.

module IPC.Semantics.KripkeExploding where

open import IPC.Syntax.Common public

-- Intuitionistic Kripke-CPS models, with exploding worlds.

record Model : Set₁ where
  infix 3 _⊪ᵅ_
  field
    World : Set

    -- Intuitionistic accessibility; preorder.
    _≤_    : World → World → Set
    refl≤  : ∀ {w} → w ≤ w
    trans≤ : ∀ {w w′ w″} → w ≤ w′ → w′ ≤ w″ → w ≤ w″

    -- Strong forcing for atomic propositions; monotonic.
    _⊪ᵅ_   : World → Atom → Set
    mono⊪ᵅ : ∀ {P w w′} → w ≤ w′ → w ⊪ᵅ P → w′ ⊪ᵅ P

    -- Exploding.
    _‼_ : World → Ty → Set

open Model {{…}} public

-- Strong forcing and forcing, in a particular world of a particular model.

module _ {{_ : Model}} where
  mutual
    infix 3 _⊪_
    _⊪_ : World → Ty → Set
    w ⊪ α P   = w ⊪ᵅ P
    w ⊪ A ▻ B = ∀ {w′} → w ≤ w′ → w′ ⊩ A → w′ ⊩ B
    w ⊪ A ∧ B = w ⊩ A × w ⊩ B
    w ⊪ ⊤    = 𝟙
    w ⊪ ⊥    = 𝟘
    w ⊪ A ∨ B = w ⊩ A ⊎ w ⊩ B

    infix 3 _⊩_
    _⊩_ : World → Ty → Set
    w ⊩ A = ∀ {C w′} → w ≤ w′ → (∀ {w″} → w′ ≤ w″ → w″ ⊪ A → w″ ‼ C) → w′ ‼ C

  infix 3 _⊩⋆_
  _⊩⋆_ : World → Cx Ty → Set
  w ⊩⋆ ∅     = 𝟙
  w ⊩⋆ Ξ , A = w ⊩⋆ Ξ × w ⊩ A

-- Monotonicity with respect to intuitionistic 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 ∧ 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 = λ ξ′ k′ → a (trans≤ ξ ξ′) k′

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

-- Additional useful equipment.

module _ {{_ : Model}} where
  _⟪$⟫_ : ∀ {A B w} → w ⊪ A ▻ B → w ⊩ A → w ⊩ B
  s ⟪$⟫ a = s refl≤ a

  return : ∀ {A w} → w ⊪ A → w ⊩ A
  return {A} a = λ ξ k → k refl≤ (mono⊪ {A} ξ a)

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

-- Forcing in a particular world of a particular model, for sequents.

module _ {{_ : Model}} where
  infix 3 _⊩_⇒_
  _⊩_⇒_ : World → Cx Ty → Ty → Set
  w ⊩ Γ ⇒ A = w ⊩⋆ Γ → w ⊩ A

  infix 3 _⊩_⇒⋆_
  _⊩_⇒⋆_ : World → Cx Ty → Cx Ty → Set
  w ⊩ Γ ⇒⋆ Ξ = w ⊩⋆ Γ → w ⊩⋆ Ξ

-- Entailment, or forcing in all worlds of all models, for sequents.

infix 3 _⊨_
_⊨_ : Cx Ty → Ty → Set₁
Γ ⊨ A = ∀ {{_ : Model}} {w : World} → w ⊩ Γ ⇒ A

infix 3 _⊨⋆_
_⊨⋆_ : Cx Ty → Cx Ty → Set₁
Γ ⊨⋆ Ξ = ∀ {{_ : Model}} {w : World} → w ⊩ Γ ⇒⋆ Ξ

-- Additional useful equipment, for sequents.

module _ {{_ : Model}} where
  lookup : ∀ {A Γ w} → A ∈ Γ → w ⊩ Γ ⇒ A
  lookup top     (γ , a) = a
  lookup (pop i) (γ , b) = lookup i γ


open import Prelude
open import core

module ground-decidable where
  ground-decidable : (τ : htyp) → (τ ground) + ((τ ground) → ⊥)
  ground-decidable b = Inl GBase
  ground-decidable ⦇-⦈ = Inr (λ ())
  ground-decidable (b ==> b) = Inr (λ ())
  ground-decidable (b ==> ⦇-⦈) = Inr (λ ())
  ground-decidable (b ==> τ' ==> τ'') = Inr (λ ())
  ground-decidable (⦇-⦈ ==> b) = Inr (λ ())
  ground-decidable (⦇-⦈ ==> ⦇-⦈) = Inl GHole
  ground-decidable (⦇-⦈ ==> τ' ==> τ'') = Inr (λ ())
  ground-decidable ((τ ==> τ₁) ==> b) = Inr (λ ())
  ground-decidable ((τ ==> τ₁) ==> ⦇-⦈) = Inr (λ ())
  ground-decidable ((τ ==> τ₁) ==> τ' ==> τ'') = Inr (λ ())

  ground-arr-lem : (τ : htyp) → ((τ ground) → ⊥) → (τ ≠  ⦇-⦈) → Σ[ τ1 ∈ htyp ] Σ[ τ2 ∈ htyp ] ((τ == (τ1 ==> τ2)) × ((τ1 ==> τ2) ≠ (⦇-⦈ ==> ⦇-⦈)))
  ground-arr-lem b ng nh = abort (ng GBase)
  ground-arr-lem ⦇-⦈ ng nh = abort (nh refl)
  ground-arr-lem (τ1 ==> τ2) ng nh = τ1 , τ2 , refl , (λ x → ng (lem' x))
    where
      lem' : ∀{τ1 τ2} → τ1 ==> τ2 == ⦇-⦈ ==> ⦇-⦈ → (τ1 ==> τ2) ground
      lem' refl = GHole


-- {-# OPTIONS -v tc.meta:50 #-}
-- Andreas 2012-03-27, record pattern unification
module Issue376 where

import Common.Level
open import Common.Equality
open import Common.Irrelevance

record Sigma (A : Set)(B : A -> Set) : Set where
  constructor _,_
  field
    fst : A
    snd : B fst
open Sigma public

record Unit : Set where
  constructor unit

bla1 : (A : Set) (a : A) ->
  let X : Unit -> A
      X = _
  in  X unit ≡ a
bla1 A a = refl

bla2 : (A : Set)(B : A -> Set) ->
  let X : Sigma A B -> Sigma A B
      X = _
  in  (x : A)(y : B x) -> X (x , y) ≡ (x , y)
bla2 A B x y = refl
-- _55 A B (x , y) := (x , y)

-- irrelevant records
bla3 : (A : Set)(B : A -> Set) ->
  let X : .(z : Sigma A B) -> (C : .(Sigma A B) -> Set) -> (.(z : Sigma A B) -> C z) -> C z
      X = _
  in  (x : A)(y : B x)(C : .(Sigma A B) -> Set)(k : .(z : Sigma A B) -> C z) ->
      X (x , y) C k ≡ k (x , y)
bla3 A B x y C k = refl

-- nested irrelevance
bla4 : (A : Set) ->
  let A' = Squash (Squash A) in
  let X : .(z : A') -> (C : .A' -> Set) -> (.(z : A') -> C z) -> C z
      X = _
  in  (a : A)(C : .A' -> Set)(k : .(z : A') -> C z) ->
      X (squash (squash a)) C k ≡ k (squash (squash a))
bla4 A a C k = refl

-- projected bound var
bla5 : (A : Set) (B : A -> Set) ->
  let X : (x : A) (y : B x) -> Sigma A B
      X = _
  in  (z : Sigma A B) -> X (fst z) (snd z) ≡ z
bla5 A B z = refl

-- projected bound var
bla6 : (A : Set) (B : A -> Set) ->
  let X : A -> A
      X = _
  in  (z : Sigma A B) -> X (fst z) ≡ fst z
bla6 A B z = refl


------------------------------------------------------------------------
-- M-types
------------------------------------------------------------------------

{-# OPTIONS --without-K --sized-types #-}

open import Equality

module M
  {reflexive} (eq : ∀ {a p} → Equality-with-J a p reflexive) where

open import Bijection eq as Bijection using (_↔_)
open Derived-definitions-and-properties eq
import Equivalence eq as Eq
open import Function-universe eq hiding (_∘_)
open import Function-universe.Size eq
open import H-level eq
open import H-level.Closure eq
open import Prelude
open import Prelude.Size

------------------------------------------------------------------------
-- M-types

mutual

  data M {a b} (A : Type a) (B : A → Type b) (i : Size) :
         Type (a ⊔ b) where
    dns : (x : A) (f : B x → M′ A B i) → M A B i

  record M′ {a b} (A : Type a) (B : A → Type b) (i : Size) :
            Type (a ⊔ b) where
    coinductive
    field
      force : {j : Size< i} → M A B j

open M′ public

-- Projections.

pɐǝɥ : ∀ {a b i} {A : Type a} {B : A → Type b} →
       M A B i → A
pɐǝɥ (dns x f) = x

lıɐʇ : ∀ {a b i} {j : Size< i} {A : Type a} {B : A → Type b} →
       (x : M A B i) → B (pɐǝɥ x) → M A B j
lıɐʇ (dns x f) y = force (f y)

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

-- M-types are isomorphic to Σ-types containing M-types (almost).

M-unfolding : ∀ {a b} {i} {A : Type a} {B : A → Type b} →
              M A B i ↔ ∃ λ (x : A) → B x → M′ A B i
M-unfolding = record
  { surjection = record
    { logical-equivalence = record
      { to   = λ { (dns x f) → x , f }
      ; from = uncurry dns
      }
    ; right-inverse-of = refl
    }
  ; left-inverse-of = λ { (dns x f) → refl (dns x f) }
  }

abstract

  -- Equality between elements of an M-type can be proved using a pair
  -- of equalities (assuming extensionality and a kind of η law).
  --
  -- Note that, because the equality type former is not sized, this
  -- lemma is perhaps not very useful.

  M-≡,≡↔≡ :
    ∀ {a b i} {A : Type a} {B : A → Type b} →
    Extensionality b (a ⊔ b) →
    (∀ {x} {f g : B x → M′ A B i} →
       _≡_ {A = B x → {j : Size< i} → M A B j}
           (force ∘ f) (force ∘ g) ↔
       f ≡ g) →
    ∀ {x y} {f : B x → M′ A B i} {g : B y → M′ A B i} →
    (∃ λ (p : x ≡ y) → ∀ b {j : Size< i} →
       force (f b) {j = j} ≡ force (g (subst B p b))) ↔
    _≡_ {A = M A B i} (dns x f) (dns y g)
  M-≡,≡↔≡ {a} {i = i} {A} {B} ext η {x} {y} {f} {g} =
    (∃ λ (p : x ≡ y) → ∀ b {j : Size< i} →
       force (f b) {j = j} ≡ force (g (subst B p b)))         ↝⟨ ∃-cong lemma ⟩
    (∃ λ (p : x ≡ y) → subst (λ x → B x → M′ A B i) p f ≡ g)  ↝⟨ Bijection.Σ-≡,≡↔≡ ⟩
    (_≡_ {A = ∃ λ (x : A) → B x → M′ A B i} (x , f) (y , g))  ↔⟨ Eq.≃-≡ (Eq.↔⇒≃ M-unfolding) ⟩□
    (dns x f ≡ dns y g)                                       □
    where
    lemma : (p : x ≡ y) →
            ((b : B x) {j : Size< i} →
             f b .force {j = j} ≡ g (subst B p b) .force) ↔
            (subst (λ x → B x → M′ A B i) p f ≡ g)
    lemma p = elim
      (λ {x y} p → (f : B x → M′ A B i) (g : B y → M′ A B i) →
                   (∀ b {j : Size< i} → f b .force {j = j} ≡
                                        g (subst B p b) .force) ↔
                   (subst (λ x → B x → M′ A B i) p f ≡ g))
      (λ x f g →
         (∀ b {j : Size< i} → f b .force {j = j} ≡
                              g (subst B (refl x) b) .force)            ↝⟨ subst (λ h → (∀ b {j : Size< i} → f b .force ≡ g (h b) .force) ↔
                                                                                (∀ b {j : Size< i} → f b .force ≡ g b .force))
                                                                                (sym (apply-ext (lower-extensionality lzero a ext)
                                                                                (subst-refl B)))
                                                                                Bijection.id ⟩

         (∀ b {j : Size< i} → f b .force {j = j} ≡ g b .force)          ↔⟨ ∀-cong ext (λ _ → implicit-Π-size-≃-Π-size) ⟩

         (∀ b (j : Size< i) → f b .force {j = j} ≡ g b .force)          ↔⟨ ∀-cong ext (λ _ → Π-size-≃) ⟩

         (∀ b (j : Size< i in-type) → f b .force {j = size j} ≡
                                      g b .force {j = size j})          ↔⟨ ∀-cong ext (λ _ → Eq.extensionality-isomorphism
                                                                                (lower-extensionality _ lzero ext)) ⟩
         ((b : B x) → _≡_ {A = (j : Size< i in-type) → M A B (size j)}
                          (λ j → f b .force {j = size j})
                          (λ j → g b .force {j = size j}))              ↔⟨ ∀-cong ext (λ _ → Eq.≃-≡ $ Eq.↔⇒≃ Bijection.implicit-Π↔Π) ⟩

         ((b : B x) → _≡_ {A = {j : Size< i in-type} → M A B (size j)}
                          (λ {j} → f b .force {j = size j})
                          (λ {j} → g b .force {j = size j}))            ↔⟨ ∀-cong ext (λ _ → Eq.≃-≡ implicit-Π-size-≃) ⟩

         ((b : B x) → _≡_ {A = {j : Size< i} → M A B j}
                          (λ { {j} → f b .force {j = j} })
                          (λ { {j} → g b .force {j = j} }))             ↔⟨ Eq.extensionality-isomorphism ext ⟩

         (force ∘ f ≡ force ∘ g)                                        ↝⟨ η ⟩

         (f ≡ g)                                                        ↝⟨ subst (λ h → (f ≡ g) ↔ (h ≡ g))
                                                                                (sym $ subst-refl (λ x' → B x' → M′ A B i) f)
                                                                                Bijection.id ⟩□
         (subst (λ x → B x → M′ A B i) (refl x) f ≡ g)                  □)
      p f g

------------------------------------------------------------------------
-- Bisimilarity and bisimilarity for bisimilarity

-- Bisimilarity.

mutual

  infix 4 [_]_≡M_ [_]_≡M′_

  data [_]_≡M_ {a b} {A : Type a} {B : A → Type b}
               (i : Size) (x y : M A B ∞) : Type (a ⊔ b) where
    dns : (p : pɐǝɥ x ≡ pɐǝɥ y) →
          (∀ b → [ i ] lıɐʇ x b ≡M′ lıɐʇ y (subst B p b)) →
          [ i ] x ≡M y

  record [_]_≡M′_ {a b} {A : Type a} {B : A → Type b}
                  (i : Size) (x y : M A B ∞) : Type (a ⊔ b) where
    coinductive
    field
      force : {j : Size< i} → [ j ] x ≡M y

open [_]_≡M′_ public

-- Projections.

pɐǝɥ≡ :
  ∀ {a b i} {A : Type a} {B : A → Type b} {x y : M A B ∞} →
  [ i ] x ≡M y → pɐǝɥ x ≡ pɐǝɥ y
pɐǝɥ≡ (dns p q) = p

lıɐʇ≡ :
  ∀ {a b i} {A : Type a} {B : A → Type b} {x y : M A B ∞} →
  (p : [ i ] x ≡M y) →
  ∀ b {j : Size< i} → [ j ] lıɐʇ x b ≡M lıɐʇ y (subst B (pɐǝɥ≡ p) b)
lıɐʇ≡ (dns p q) y = force (q y)

-- Equality implies bisimilarity.

≡⇒≡M : ∀ {a b i} {A : Type a} {B : A → Type b} {x y : M A B ∞} →
       x ≡ y → [ i ] x ≡M y
≡⇒≡M {i = i} {B = B} {dns x f} {dns y g} p =
  dns (proj₁ q) helper
  where
  q = elim (λ {m m′} m≡m′ →
              ∃ λ (x≡y : pɐǝɥ m ≡ pɐǝɥ m′) →
                  ∀ b → lıɐʇ m b ≡ lıɐʇ m′ (subst B x≡y b))
           (λ m → refl (pɐǝɥ m) , λ b →
              lıɐʇ m b                            ≡⟨ cong (lıɐʇ m) (sym $ subst-refl B _) ⟩∎
              lıɐʇ m (subst B (refl (pɐǝɥ m)) b)  ∎)
           p

  helper :
    ∀ b →
    [ i ] lıɐʇ (dns x f) b ≡M′ lıɐʇ (dns y g) (subst B (proj₁ q) b)
  force (helper b) = ≡⇒≡M (proj₂ q b)

-- Bisimilarity for the bisimilarity type.

mutual

  data [_]_≡≡M_ {a b} {A : Type a} {B : A → Type b} {x y : M A B ∞}
                (i : Size) (p q : [ ∞ ] x ≡M y) : Type (a ⊔ b) where
    dns : (r : pɐǝɥ≡ p ≡ pɐǝɥ≡ q) →
          (∀ b → [ i ] lıɐʇ≡ p b ≡≡M′
                       subst (λ p → [ ∞ ] lıɐʇ x b ≡M
                                          lıɐʇ y (subst B p b))
                             (sym r)
                             (lıɐʇ≡ q b)) →
          [ i ] p ≡≡M q

  record [_]_≡≡M′_ {a b} {A : Type a} {B : A → Type b} {x y : M A B ∞}
                   (i : Size) (p q : [ ∞ ] x ≡M y) : Type (a ⊔ b) where
    coinductive
    field
      force : {j : Size< i} → [ j ] p ≡≡M q

open [_]_≡≡M′_ public

------------------------------------------------------------------------
-- Closure under various h-levels

abstract

  -- If we assume a notion of extensionality (bisimilarity implies
  -- equality) then Contractible is closed under M.

  M-closure-contractible :
    ∀ {a b} {A : Type a} {B : A → Type b} →
    ({x y : M A B ∞} → [ ∞ ] x ≡M y → x ≡ y) →
    Contractible A → Contractible (M A B ∞)
  M-closure-contractible {A = A} {B} ext (z , irrA) = (x , ext ∘ irr)
    where
    x : ∀ {i} → M A B i
    x = dns z λ _ → λ { .force → x }

    irr : ∀ {i} y → [ i ] x ≡M y
    irr {i} (dns x′ f) = dns (irrA x′) helper
      where
      helper : ∀ y → [ i ] x ≡M′ force (f (subst B (irrA x′) y))
      force (helper _) = irr _

  -- The same applies to Is-proposition.

  M-closure-propositional :
    ∀ {a b} {A : Type a} {B : A → Type b} →
    ({x y : M A B ∞} → [ ∞ ] x ≡M y → x ≡ y) →
    Is-proposition A → Is-proposition (M A B ∞)
  M-closure-propositional {A = A} {B} ext p =
    λ x y → ext $ irrelevant x y
    where
    irrelevant : ∀ {i} (x y : M A B ∞) → [ i ] x ≡M y
    irrelevant {i} (dns x f) (dns y g) = dns (p x y) helper
      where
      helper :
        (y′ : B x) →
        [ i ] force (f y′) ≡M′ force (g (subst B (p x y) y′))
      force (helper _) = irrelevant _ _

  -- If we assume that we have another notion of extensionality, then
  -- Is-set is closed under M.

  M-closure-set :
    ∀ {a b} {A : Type a} {B : A → Type b} →
    ({x y : M A B ∞} {p q : x ≡ y} → [ ∞ ] ≡⇒≡M p ≡≡M ≡⇒≡M q → p ≡ q) →
    Is-set A → Is-set (M A B ∞)
  M-closure-set {A = A} {B} ext s =
    λ p q → ext $ uip (≡⇒≡M p) (≡⇒≡M q)
    where
    uip : ∀ {i} {x y : M A B ∞} (p q : [ ∞ ] x ≡M y) → [ i ] p ≡≡M q
    uip {i} {x} {y} (dns p f) (dns q g) = dns (s p q) helper
      where
      helper :
        (b : B (pɐǝɥ x)) →
        [ i ] force (f b) ≡≡M′
              subst (λ eq → [ ∞ ] lıɐʇ x b ≡M lıɐʇ y (subst B eq b))
                    (sym (s p q))
                    (force (g b))
      force (helper _) = uip _ _


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

open import HoTT

-- TODO Is it possible to have a more generic [→-group] construction?

module groups.ToOmega where

⊙→Ω-group-structure : ∀ {i j} (X : Ptd i) (Y : Ptd j)
  → GroupStructure (X ⊙→ ⊙Ω Y)
⊙→Ω-group-structure X Y = record {M} where
  module M where
    ident : (X ⊙→ ⊙Ω Y)
    ident = ⊙cst

    comp : (X ⊙→ ⊙Ω Y) → (X ⊙→ ⊙Ω Y) → (X ⊙→ ⊙Ω Y)
    comp F G = ⊙Ω-∙ ⊙∘ ⊙fanout F G

    inv : (X ⊙→ ⊙Ω Y) → (X ⊙→ ⊙Ω Y)
    inv F = (! ∘ fst F) , ap ! (snd F)
    abstract
      unit-l-lemma : ∀ {i} {A : Type i} {x : A} {p : x == x} (α : p == idp)
        → ⊙∘-pt (fst ⊙Ω-∙) (⊙fanout-pt {a₀ = idp} idp α) idp == α
      unit-l-lemma idp = idp

      unit-l : ∀ G → comp ident G == G
      unit-l G = ⊙λ=' (λ _ → idp) (unit-l-lemma (snd G))

      assoc-lemma : ∀ {i} {A : Type i} {x : A} {p q r : x == x}
        (α : p == idp) (β : q == idp) (γ : r == idp)
        →  ⊙∘-pt (fst ⊙Ω-∙) (⊙fanout-pt (⊙∘-pt (fst ⊙Ω-∙) (⊙fanout-pt α β) idp) γ) idp
        == ⊙∘-pt (fst ⊙Ω-∙) (⊙fanout-pt α (⊙∘-pt (fst ⊙Ω-∙) (⊙fanout-pt β γ) idp)) idp
         [ _== idp ↓ ∙-assoc p q r ]
      assoc-lemma idp idp idp = idp

      assoc : ∀ F G H → comp (comp F G) H == comp F (comp G H)
      assoc F G H = ⊙λ='
        (λ x → ∙-assoc (fst F x) (fst G x) (fst H x))
        (assoc-lemma (snd F) (snd G) (snd H))

      inv-l-lemma : ∀ {i} {A : Type i} {x : A} {p : x == x} (α : p == idp)
        → ⊙∘-pt (fst ⊙Ω-∙) (⊙fanout-pt (ap ! α) α) idp == idp [ _== idp ↓ !-inv-l p ]
      inv-l-lemma idp = idp

      inv-l : ∀ F → comp (inv F) F == ident
      inv-l F = ⊙λ=' (!-inv-l ∘ fst F) (inv-l-lemma (snd F))

Trunc-⊙→Ω-group : ∀ {i j} (X : Ptd i) (Y : Ptd j) → Group (lmax i j)
Trunc-⊙→Ω-group X Y = Trunc-group (⊙→Ω-group-structure X Y)

{- [Trunc-→Ω-group] is functorial in the first argument -}

⊙→Ω-group-structure-fmap-dom : ∀ {i j k} {X : Ptd i} {Y : Ptd j}
  (f : X ⊙→ Y) (Z : Ptd k)
  → (⊙→Ω-group-structure Y Z →ᴳˢ ⊙→Ω-group-structure X Z)
⊙→Ω-group-structure-fmap-dom F Z = group-structure-hom (_⊙∘ F)
  (λ g₁ g₂ → ⊙λ= (⊙∘-assoc ⊙Ω-∙ (⊙fanout g₁ g₂) F)
           ∙ ap (⊙Ω-∙ ⊙∘_) (⊙fanout-pre∘ g₁ g₂ F))

Trunc-⊙→Ω-group-fmap-dom : ∀ {i j k} {X : Ptd i} {Y : Ptd j}
  (f : X ⊙→ Y) (Z : Ptd k)
  → (Trunc-⊙→Ω-group Y Z →ᴳ Trunc-⊙→Ω-group X Z)
Trunc-⊙→Ω-group-fmap-dom F Z =
  Trunc-group-fmap $ ⊙→Ω-group-structure-fmap-dom F Z

⊙→Ω-group-structure-emap-dom : ∀ {i j k} {X : Ptd i} {Y : Ptd j}
  (e : X ⊙≃ Y) (Z : Ptd k)
  → (⊙→Ω-group-structure Y Z ≃ᴳˢ ⊙→Ω-group-structure X Z)
⊙→Ω-group-structure-emap-dom (F , F-is-equiv) Z =
  ⊙→Ω-group-structure-fmap-dom F Z , pre⊙∘-is-equiv (F , F-is-equiv)

Trunc-⊙→Ω-group-emap-dom : ∀ {i j k} {X : Ptd i} {Y : Ptd j}
  (e : X ⊙≃ Y) (Z : Ptd k)
  → (Trunc-⊙→Ω-group Y Z ≃ᴳ Trunc-⊙→Ω-group X Z)
Trunc-⊙→Ω-group-emap-dom F Z =
  Trunc-group-emap $ ⊙→Ω-group-structure-emap-dom F Z

Trunc-⊙→Ω-group-fmap-dom-idf : ∀ {i j} {X : Ptd i} (Y : Ptd j)
  → Trunc-⊙→Ω-group-fmap-dom (⊙idf X) Y == idhom (Trunc-⊙→Ω-group X Y)
Trunc-⊙→Ω-group-fmap-dom-idf Y = group-hom= $ λ= $ Trunc-elim (λ _ → idp)

Trunc-⊙→Ω-group-fmap-dom-∘ : ∀ {i j k l} {X : Ptd i} {Y : Ptd j} {Z : Ptd k}
  (g : Y ⊙→ Z) (f : X ⊙→ Y) (W : Ptd l)
  → Trunc-⊙→Ω-group-fmap-dom (g ⊙∘ f) W
    == Trunc-⊙→Ω-group-fmap-dom f W ∘ᴳ Trunc-⊙→Ω-group-fmap-dom g W
Trunc-⊙→Ω-group-fmap-dom-∘ g f W = group-hom= $ λ= $
  Trunc-elim (λ h → ap [_] (! (⊙λ= $ ⊙∘-assoc h g f)))

⊙→Ω-group-structure-fmap-codom : ∀ {i j k} (X : Ptd i) {Y : Ptd j} {Z : Ptd k}
  → Y ⊙→ Z → (⊙→Ω-group-structure X Y →ᴳˢ ⊙→Ω-group-structure X Z)
⊙→Ω-group-structure-fmap-codom X {Y} {Z} F = group-structure-hom
    (⊙Ω-fmap F ⊙∘_)
    (λ G H → ⊙λ=' (λ x → Ω-fmap-∙ F ((fst G) x) ((fst H) x))
                  (lemma (snd F) (snd G) (snd H)))
    where
      abstract
        lemma : ∀ {ptZ : de⊙ Z} (α : (fst F) (pt Y) == ptZ)
          {gpt hpt : Ω Y} (β : gpt == idp) (γ : hpt == idp)
          →  ⊙∘-pt (Ω-fmap (fst F , α)) (⊙∘-pt (fst ⊙Ω-∙) (⊙fanout-pt β γ) idp) (snd (⊙Ω-fmap (fst F , α)))
          == ⊙∘-pt (fst ⊙Ω-∙)
               (⊙fanout-pt
                 (⊙∘-pt (Ω-fmap (fst F , α)) β (snd (⊙Ω-fmap (fst F , α))))
                 (⊙∘-pt (Ω-fmap (fst F , α)) γ (snd (⊙Ω-fmap (fst F , α))))
               ) idp
           [ _== idp ↓ Ω-fmap-∙ (fst F , α) gpt hpt ]
        lemma idp idp idp = idp

Trunc-⊙→Ω-group-fmap-codom : ∀ {i j k} (X : Ptd i) {Y : Ptd j} {Z : Ptd k}
  → Y ⊙→ Z → Trunc-⊙→Ω-group X Y →ᴳ Trunc-⊙→Ω-group X Z
Trunc-⊙→Ω-group-fmap-codom X = Trunc-group-fmap ∘ ⊙→Ω-group-structure-fmap-codom X

⊙→Ω-group-structure-emap-codom : ∀ {i j k} (X : Ptd i) {Y : Ptd j} {Z : Ptd k}
  → Y ⊙≃ Z → ⊙→Ω-group-structure X Y ≃ᴳˢ ⊙→Ω-group-structure X Z
⊙→Ω-group-structure-emap-codom X (F , F-is-equiv) =
  ⊙→Ω-group-structure-fmap-codom X F , post⊙∘-is-equiv (⊙Ω-emap (F , F-is-equiv))

Trunc-⊙→Ω-group-emap-codom : ∀ {i j k} (X : Ptd i) {Y : Ptd j} {Z : Ptd k}
  → Y ⊙≃ Z → Trunc-⊙→Ω-group X Y ≃ᴳ Trunc-⊙→Ω-group X Z
Trunc-⊙→Ω-group-emap-codom X = Trunc-group-emap ∘ ⊙→Ω-group-structure-emap-codom X

-- TODO Check naming convensions.
-- TODO Use [CommSquareᴳ].
Trunc-⊙→Ω-group-fmap-nat : ∀ {i₀ i₁ j₀ j₁}
  {X₀ : Ptd i₀} {X₁ : Ptd i₁} {Y₀ : Ptd j₀} {Y₁ : Ptd j₁}
  (F : X₀ ⊙→ X₁) (G : Y₀ ⊙→ Y₁)
  →  Trunc-⊙→Ω-group-fmap-dom   F  Y₁ ∘ᴳ Trunc-⊙→Ω-group-fmap-codom X₁ G
  == Trunc-⊙→Ω-group-fmap-codom X₀ G  ∘ᴳ Trunc-⊙→Ω-group-fmap-dom   F  Y₀
Trunc-⊙→Ω-group-fmap-nat F G = group-hom= $ λ= $ Trunc-elim
  (λ k → ap [_] $ ⊙λ= $ ⊙∘-assoc (⊙Ω-fmap G) k F)

{- Not used.
Trunc-⊙→Ω-group-emap-nat : ∀ {i₀ i₁ j₀ j₁}
  {X₀ : Ptd i₀} {X₁ : Ptd i₁} {Y₀ : Ptd j₀} {Y₁ : Ptd j₁}
  (F : X₀ ⊙≃ X₁) (G : Y₀ ⊙≃ Y₁)
  →  Trunc-⊙→Ω-group-emap-dom   F  Y₁ ∘ᴳ Trunc-⊙→Ω-group-emap-codom X₁ G
  == Trunc-⊙→Ω-group-emap-codom X₀ G  ∘ᴳ Trunc-⊙→Ω-group-emap-dom   F  Y₀
Trunc-⊙→Ω-group-emap-nat F G = group-hom=-to-iso= $ Trunc-⊙→Ω-group-fmap-nat F G
-}

{- Pointed maps out of bool -}

Trunc-⊙Bool→Ω-iso-π₁ : ∀ {i} (X : Ptd i)
  → Trunc-⊙→Ω-group ⊙Bool X ≃ᴳ πS 0 X
Trunc-⊙Bool→Ω-iso-π₁ {i} X = Trunc-group-emap (≃-to-≃ᴳˢ (⊙Bool→-equiv-idf (⊙Ω X)) (λ _ _ → idp))


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

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

{- Typing Rules and basic syntactic properties for the type theory for globular sets -}
module GSeTT.Rules where
  {- Well-formedness statements ≡ inference rules -}
  data _⊢C : Pre-Ctx → Set₁
  data _⊢T_ : Pre-Ctx → Pre-Ty → Set₁
  data _⊢t_#_ : Pre-Ctx → Pre-Tm → Pre-Ty → Set₁
  data _⊢S_>_ : Pre-Ctx → Pre-Sub → Pre-Ctx → Set₁

  data _⊢C where
    ec : nil ⊢C
    cc : ∀ {Γ x A} → Γ ⊢C → Γ ⊢T A → x == length Γ → (Γ :: (x , A)) ⊢C

  data _⊢T_ where
    ob : ∀ {Γ} → Γ ⊢C → Γ ⊢T ∗
    ar : ∀ {Γ A t u} → Γ ⊢t t # A → Γ ⊢t u # A → Γ ⊢T ⇒ A t u

  data _⊢t_#_ where
    var : ∀ {Γ x A} → Γ ⊢C → x # A ∈ Γ → Γ ⊢t (Var x) # A

  data _⊢S_>_ where
    es : ∀ {Δ} → Δ ⊢C → Δ ⊢S nil > nil
    sc : ∀ {Δ Γ γ x y A t} → Δ ⊢S γ > Γ → (Γ :: (x , A)) ⊢C → (Δ ⊢t t # (A [ γ ]Pre-Ty)) → x == y → Δ ⊢S (γ :: (y , t)) > (Γ :: (x , A))

  {- Weakening admissibility -}

  wkT : ∀ {Γ A y B} → Γ ⊢T A → (Γ :: (y , B)) ⊢C → (Γ :: (y , B)) ⊢T A
  wkt : ∀ {Γ A t y B} → Γ ⊢t t # A → (Γ :: (y , B)) ⊢C → (Γ :: (y , B)) ⊢t t # A

  wkT (ob _) Γ,y:B⊢ = ob Γ,y:B⊢
  wkT (ar Γ⊢t:A Γ⊢u:A) Γ,y:B⊢ = ar (wkt Γ⊢t:A Γ,y:B⊢) (wkt Γ⊢u:A Γ,y:B⊢)
  wkt (var Γ⊢C x∈Γ) Γ,y:B⊢ = var Γ,y:B⊢ (inl x∈Γ)

  wkS : ∀ {Δ Γ γ y B} → Δ ⊢S γ > Γ → (Δ :: (y , B)) ⊢C → (Δ :: (y , B)) ⊢S γ > Γ
  wkS (es _) Δ,y:B⊢ = es Δ,y:B⊢
  wkS (sc Δ⊢γ:Γ Γ,x:A⊢ Δ⊢t:A[γ] idp) Δ,y:B⊢ = sc (wkS Δ⊢γ:Γ Δ,y:B⊢) Γ,x:A⊢ (wkt Δ⊢t:A[γ] Δ,y:B⊢) idp

  {- Consistency : all objects appearing in derivable judgments are derivable -}
  Γ⊢A→Γ⊢ : ∀ {Γ A} → Γ ⊢T A → Γ ⊢C
  Γ⊢t:A→Γ⊢ : ∀ {Γ A t} → Γ ⊢t t # A → Γ ⊢C

  Γ⊢A→Γ⊢ (ob Γ⊢) = Γ⊢
  Γ⊢A→Γ⊢ (ar Γ⊢t:A Γ⊢u:A) = Γ⊢t:A→Γ⊢ Γ⊢t:A
  Γ⊢t:A→Γ⊢ (var Γ⊢ _) = Γ⊢

  Δ⊢γ:Γ→Γ⊢ : ∀ {Δ Γ γ} → Δ ⊢S γ > Γ → Γ ⊢C
  Δ⊢γ:Γ→Γ⊢ (es Δ⊢) = ec
  Δ⊢γ:Γ→Γ⊢ (sc Δ⊢γ:Γ Γ,x:A⊢ Δ⊢t:A[γ] idp) = Γ,x:A⊢

  Δ⊢γ:Γ→Δ⊢ : ∀ {Δ Γ γ} → Δ ⊢S γ > Γ → Δ ⊢C
  Δ⊢γ:Γ→Δ⊢ (es Δ⊢) = Δ⊢
  Δ⊢γ:Γ→Δ⊢ (sc Δ⊢γ:Γ Γ,x:A⊢ Δ⊢t:A[γ] idp) = Δ⊢γ:Γ→Δ⊢ Δ⊢γ:Γ

  Γ,x:A⊢→Γ,x:A⊢A : ∀ {Γ x A} → (Γ :: (x , A)) ⊢C → (Γ :: (x , A)) ⊢T A
  Γ,x:A⊢→Γ,x:A⊢A Γ,x:A⊢@(cc Γ⊢ Γ⊢A idp) = wkT Γ⊢A Γ,x:A⊢

  Γ,x:A⊢→Γ,x:A⊢x:A : ∀ {Γ x A} → (Γ :: (x , A)) ⊢C → (Γ :: (x , A)) ⊢t (Var x) # A
  Γ,x:A⊢→Γ,x:A⊢x:A Γ,x:A⊢ = var Γ,x:A⊢ (inr (idp , idp))

  Γ,x:A⊢→Γ⊢ : ∀ {Γ x A} → (Γ :: (x , A)) ⊢C → Γ ⊢C
  Γ,x:A⊢→Γ⊢ (cc Γ⊢ _ _) = Γ⊢

  Γ⊢t:A→Γ⊢A : ∀ {Γ A t} → Γ ⊢t t # A → Γ ⊢T A
  Γ⊢t:A→Γ⊢A (var Γ,x:A⊢@(cc Γ⊢ Γ⊢A idp) (inl y∈Γ)) = wkT (Γ⊢t:A→Γ⊢A (var Γ⊢ y∈Γ)) Γ,x:A⊢
  Γ⊢t:A→Γ⊢A (var Γ,x:A⊢@(cc _ _ idp) (inr (idp , idp))) = Γ,x:A⊢→Γ,x:A⊢A Γ,x:A⊢

  Γ⊢src : ∀ {Γ A t u} → Γ ⊢T ⇒ A t u → Γ ⊢t t # A
  Γ⊢src (ar Γ⊢t Γ⊢u) = Γ⊢t

  Γ⊢tgt : ∀ {Γ A t u} → Γ ⊢T ⇒ A t u → Γ ⊢t u # A
  Γ⊢tgt (ar Γ⊢t Γ⊢u) = Γ⊢u

  {- Cut-admissibility -}
  -- notational shortcut : if A = B a term of type A is also of type B
  trT : ∀ {Γ A B t} → A == B → Γ ⊢t t # A → Γ ⊢t t # B
  trT idp Γ⊢t:A = Γ⊢t:A

  n∉Γ : ∀ {Γ A n} → Γ ⊢C → (length Γ ≤ n) → ¬ (n # A ∈ Γ)
  n∉Γ (cc Γ⊢ _ _) l+1≤n (inl n∈Γ) = n∉Γ Γ⊢ (Sn≤m→n≤m l+1≤n) n∈Γ
  n∉Γ (cc Γ⊢ _ idp) Sn≤n (inr (idp , idp)) = Sn≰n _ Sn≤n

  lΓ∉Γ : ∀ {Γ A} → Γ ⊢C → ¬ ((length Γ) # A ∈ Γ)
  lΓ∉Γ Γ⊢ = n∉Γ Γ⊢ (n≤n _)

  Γ+⊢l : ∀ {Γ x A} → (Γ :: (x , A)) ⊢C → x == length Γ
  Γ+⊢l (cc _ _ idp) = idp

  {- action on weakened types and terms -}
  wk[]T : ∀ {Γ Δ γ x u A B} → Γ ⊢T A → Δ ⊢S (γ :: (x , u)) > (Γ :: (x , B)) → (A [ (γ :: (x , u)) ]Pre-Ty) == (A [ γ ]Pre-Ty)
  wk[]t : ∀ {Γ Δ γ x u A t B} → Γ ⊢t t # A → Δ ⊢S (γ :: (x , u)) > (Γ :: (x , B)) → (t [ (γ :: (x , u)) ]Pre-Tm) == (t [ γ ]Pre-Tm)

  wk[]T (ob Γ⊢) _ = idp
  wk[]T (ar Γ⊢t:A Γ⊢u:A) Δ⊢γ+:Γ+ = ⇒= (wk[]T (Γ⊢t:A→Γ⊢A Γ⊢t:A) Δ⊢γ+:Γ+)  (wk[]t Γ⊢t:A Δ⊢γ+:Γ+) (wk[]t Γ⊢u:A Δ⊢γ+:Γ+)
  wk[]t {x = x} (var {x = y} Γ⊢ y∈Γ) Δ⊢γ+:Γ+ with (eqdecℕ y x)
  wk[]t {x = x} (var {x = y} Γ⊢ y∈Γ) Δ⊢γ+:Γ+ | inr _ = idp
  wk[]t (var {Γ = Γ} Γ⊢ x∈Γ) Δ⊢γ+:Γ+ | inl idp = ⊥-elim (lΓ∉Γ Γ⊢ (transport {B = λ n → n # _ ∈ Γ} (Γ+⊢l (Δ⊢γ:Γ→Γ⊢ Δ⊢γ+:Γ+)) x∈Γ))

  dim[] : ∀ (A : Pre-Ty) (γ : Pre-Sub) → dim (A [ γ ]Pre-Ty) == dim A
  dim[] ∗ γ = idp
  dim[] (⇒ A x x₁) γ = S= (dim[] A γ)


{- Cubical Agda with K

This file demonstrates the incompatibility of the --cubical
and --with-K flags, relying on the well-known incosistency of K with
univalence.

The --safe flag can be used to prevent accidentally mixing such
incompatible flags.

-}

{-# OPTIONS --cubical --no-import-sorts --with-K #-}

module Cubical.WithK where

open import Cubical.Data.Equality
open import Cubical.Data.Bool
open import Cubical.Data.Empty

private
 variable
  ℓ : Level
  A : Type ℓ
  x y : A

uip : (prf : x ≡p x) → prf ≡c reflp
uip reflp i = reflp

transport-uip : (prf : A ≡p A) → transport (ptoc prf) x ≡c x
transport-uip {x = x} prf =
  cong (λ m → transport (ptoc m) x) (uip prf) ∙ transportRefl x

transport-not : transport (ptoc (ctop notEq)) true ≡c false
transport-not = cong (λ a → transport a true) (ptoc-ctop notEq)

false-true : false ≡c true
false-true = sym transport-not ∙ transport-uip (ctop notEq)

absurd : (X : Type) → X
absurd X = transport (cong sel false-true) true
  where
    sel : Bool → Type
    sel false = Bool
    sel true = X

inconsistency : ⊥
inconsistency = absurd ⊥


------------------------------------------------------------------------
-- The two coinductive definitions of weak bisimilarity are pointwise
-- logically equivalent
------------------------------------------------------------------------

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

open import Labelled-transition-system

module Bisimilarity.Weak.Equivalent {ℓ} {lts : LTS ℓ} where

open import Equality.Propositional
open import Logical-equivalence using (_⇔_)
open import Prelude

import Bisimilarity.Weak lts as Std
import Bisimilarity.Weak.Alternative lts as Alt
import Bisimilarity.Weak.Alternative.Equational-reasoning-instances
import Bisimilarity.Weak.Equational-reasoning-instances
open import Equational-reasoning

open LTS lts

mutual

  -- The alternative definition of weak bisimilarity can be converted
  -- to the standard one, in a size-preserving way.

  alternative⇒ : ∀ {i p q} → Alt.[ i ] p ≈ q → Std.[ i ] p ≈ q
  alternative⇒ {i} p≈q =
    Std.⟨ lr p≈q
        , Σ-map id (Σ-map id symmetric) ∘ lr (symmetric p≈q)
        ⟩
    where
    lr : ∀ {p p′ q μ} →
         Alt.[ i ] p ≈ q → p [ μ ]⟶ p′ →
         ∃ λ q′ → q [ μ ]⇒̂ q′ × Std.[ i ] p′ ≈′ q′
    lr p≈q p⟶p′ =
      Σ-map id (Σ-map id alternative⇒′)
        (Alt.left-to-right p≈q (⟶→⇒̂ p⟶p′))

  alternative⇒′ : ∀ {i p q} → Alt.[ i ] p ≈′ q → Std.[ i ] p ≈′ q
  Std.force (alternative⇒′ p≈′q) = alternative⇒ (Alt.force p≈′q)

mutual

  -- One can also convert in the other direction. Note that this
  -- conversion is not guaranteed to be size-preserving. For at least
  -- one LTS it cannot (in general) be size-preserving, see
  -- Bisimilarity.Weak.Delay-monad.size-preserving-⇒alternative⇔uninhabited.

  ⇒alternative : ∀ {i p q} → p Std.≈ q → Alt.[ i ] p ≈ q
  ⇒alternative {i} p≈q =
    Alt.⟨ lr p≈q
        , Σ-map id (Σ-map id symmetric) ∘ lr (symmetric p≈q)
        ⟩
    where
    lr : ∀ {p p′ q μ} →
         p Std.≈ q → p [ μ ]⇒̂ p′ →
         ∃ λ q′ → q [ μ ]⇒̂ q′ × Alt.[ i ] p′ ≈′ q′
    lr p≈q p⇒̂p′ =
      Σ-map id (Σ-map id ⇒alternative′) (Std.weak-is-weak⇒̂ p≈q p⇒̂p′)

  ⇒alternative′ : ∀ {i p q} → p Std.≈ q → Alt.[ i ] p ≈′ q
  Alt.force (⇒alternative′ p≈q) = ⇒alternative p≈q

-- The two definitions of weak bisimilarity are logically equivalent.

alternative⇔ : ∀ {p q} → p Alt.≈ q ⇔ p Std.≈ q
alternative⇔ = record
  { to   = alternative⇒
  ; from = ⇒alternative
  }

-- TODO: I don't know if the two definitions of weak bisimilarity are
-- isomorphic.


------------------------------------------------------------------------
-- The Agda standard library
--
-- Record types with manifest fields and "with", based on Randy
-- Pollack's "Dependently Typed Records in Type Theory"
------------------------------------------------------------------------

-- For an example of how this module can be used, see README.Record.

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

open import Data.Bool.Base using (if_then_else_)
open import Data.Empty
open import Data.List.Base
open import Data.Product hiding (proj₁; proj₂)
open import Data.Unit
open import Function
open import Level
open import Relation.Binary
open import Relation.Binary.PropositionalEquality
open import Relation.Nullary
open import Relation.Nullary.Decidable

-- The module is parametrised by the type of labels, which should come
-- with decidable equality.

module Record {ℓ : Level} (Label : Set ℓ) (_≟_ : Decidable (_≡_ {A = Label})) where

------------------------------------------------------------------------
-- A Σ-type with a manifest field

-- A variant of Σ where the value of the second field is "manifest"
-- (given by the first).

infix 4 _,

record Manifest-Σ {a b} (A : Set a) {B : A → Set b}
                  (f : (x : A) → B x) : Set a where
  constructor _,
  field proj₁ : A

  proj₂ : B proj₁
  proj₂ = f proj₁

------------------------------------------------------------------------
-- Signatures and records

mutual

  infixl 5 _,_∶_ _,_≔_

  data Signature s : Set (suc s ⊔ ℓ) where
    ∅     : Signature s
    _,_∶_ : (Sig : Signature s)
            (ℓ : Label)
            (A : Record Sig → Set s) →
            Signature s
    _,_≔_ : (Sig : Signature s)
            (ℓ : Label)
            {A : Record Sig → Set s}
            (a : (r : Record Sig) → A r) →
            Signature s

  -- Record is a record type to ensure that the signature can be
  -- inferred from a value of type Record Sig.

  record Record {s} (Sig : Signature s) : Set s where
    eta-equality
    inductive
    constructor rec
    field fun : Record-fun Sig

  Record-fun : ∀ {s} → Signature s → Set s
  Record-fun ∅             = Lift _ ⊤
  Record-fun (Sig , ℓ ∶ A) =          Σ (Record Sig) A
  Record-fun (Sig , ℓ ≔ a) = Manifest-Σ (Record Sig) a

------------------------------------------------------------------------
-- Labels

-- A signature's labels, starting with the last one.

labels : ∀ {s} → Signature s → List Label
labels ∅             = []
labels (Sig , ℓ ∶ A) = ℓ ∷ labels Sig
labels (Sig , ℓ ≔ a) = ℓ ∷ labels Sig

-- Inhabited if the label is part of the signature.

infix 4 _∈_

_∈_ : ∀ {s} → Label → Signature s → Set
ℓ ∈ Sig =
  foldr (λ ℓ′ → if ⌊ ℓ ≟ ℓ′ ⌋ then (λ _ → ⊤) else id) ⊥ (labels Sig)

------------------------------------------------------------------------
-- Projections

-- Signature restriction and projection. (Restriction means removal of
-- a given field and all subsequent fields.)

Restrict : ∀ {s} (Sig : Signature s) (ℓ : Label) → ℓ ∈ Sig →
           Signature s
Restrict ∅              ℓ ()
Restrict (Sig , ℓ′ ∶ A) ℓ ℓ∈ with ℓ ≟ ℓ′
... | yes _ = Sig
... | no  _ = Restrict Sig ℓ ℓ∈
Restrict (Sig , ℓ′ ≔ a) ℓ ℓ∈ with ℓ ≟ ℓ′
... | yes _ = Sig
... | no  _ = Restrict Sig ℓ ℓ∈

Restricted : ∀ {s} (Sig : Signature s) (ℓ : Label) → ℓ ∈ Sig → Set s
Restricted Sig ℓ ℓ∈ = Record (Restrict Sig ℓ ℓ∈)

Proj : ∀ {s} (Sig : Signature s) (ℓ : Label) {ℓ∈ : ℓ ∈ Sig} →
       Restricted Sig ℓ ℓ∈ → Set s
Proj ∅              ℓ {}
Proj (Sig , ℓ′ ∶ A) ℓ {ℓ∈} with ℓ ≟ ℓ′
... | yes _ = A
... | no  _ = Proj Sig ℓ {ℓ∈}
Proj (_,_≔_ Sig ℓ′ {A = A} a) ℓ {ℓ∈} with ℓ ≟ ℓ′
... | yes _ = A
... | no  _ = Proj Sig ℓ {ℓ∈}

-- Record restriction and projection.

infixl 5 _∣_

_∣_ : ∀ {s} {Sig : Signature s} → Record Sig →
      (ℓ : Label) {ℓ∈ : ℓ ∈ Sig} → Restricted Sig ℓ ℓ∈
_∣_ {Sig = ∅}            r       ℓ {}
_∣_ {Sig = Sig , ℓ′ ∶ A} (rec r) ℓ {ℓ∈} with ℓ ≟ ℓ′
... | yes _ = Σ.proj₁ r
... | no  _ = _∣_ (Σ.proj₁ r) ℓ {ℓ∈}
_∣_ {Sig = Sig , ℓ′ ≔ a} (rec r) ℓ {ℓ∈} with ℓ ≟ ℓ′
... | yes _ = Manifest-Σ.proj₁ r
... | no  _ = _∣_ (Manifest-Σ.proj₁ r) ℓ {ℓ∈}

infixl 5 _·_

_·_ : ∀ {s} {Sig : Signature s} (r : Record Sig)
      (ℓ : Label) {ℓ∈ : ℓ ∈ Sig} →
      Proj Sig ℓ {ℓ∈} (r ∣ ℓ)
_·_ {Sig = ∅}            r       ℓ {}
_·_ {Sig = Sig , ℓ′ ∶ A} (rec r) ℓ {ℓ∈} with ℓ ≟ ℓ′
... | yes _ = Σ.proj₂ r
... | no  _ = _·_ (Σ.proj₁ r) ℓ {ℓ∈}
_·_ {Sig = Sig , ℓ′ ≔ a} (rec r) ℓ {ℓ∈} with ℓ ≟ ℓ′
... | yes _ = Manifest-Σ.proj₂ r
... | no  _ = _·_ (Manifest-Σ.proj₁ r) ℓ {ℓ∈}

------------------------------------------------------------------------
-- With

-- Sig With ℓ ≔ a is the signature Sig, but with the ℓ field set to a.

mutual

  infixl 5 _With_≔_

  _With_≔_ : ∀ {s} (Sig : Signature s) (ℓ : Label) {ℓ∈ : ℓ ∈ Sig} →
             ((r : Restricted Sig ℓ ℓ∈) → Proj Sig ℓ r) → Signature s
  _With_≔_ ∅ ℓ {} a
  _With_≔_ (Sig , ℓ′ ∶ A)   ℓ {ℓ∈} a with ℓ ≟ ℓ′
  ... | yes _ = Sig                   , ℓ′ ≔ a
  ... | no  _ = _With_≔_ Sig ℓ {ℓ∈} a , ℓ′ ∶ A ∘ drop-With
  _With_≔_  (Sig , ℓ′ ≔ a′) ℓ {ℓ∈} a with ℓ ≟ ℓ′
  ... | yes _ = Sig                   , ℓ′ ≔ a
  ... | no  _ = _With_≔_ Sig ℓ {ℓ∈} a , ℓ′ ≔ a′ ∘ drop-With

  drop-With : ∀ {s} {Sig : Signature s} {ℓ : Label} {ℓ∈ : ℓ ∈ Sig}
              {a : (r : Restricted Sig ℓ ℓ∈) → Proj Sig ℓ r} →
              Record (_With_≔_ Sig ℓ {ℓ∈} a) → Record Sig
  drop-With {Sig = ∅} {ℓ∈ = ()}      r
  drop-With {Sig = Sig , ℓ′ ∶ A} {ℓ} (rec r) with ℓ ≟ ℓ′
  ... | yes _ = rec (Manifest-Σ.proj₁ r , Manifest-Σ.proj₂ r)
  ... | no  _ = rec (drop-With (Σ.proj₁ r) , Σ.proj₂ r)
  drop-With {Sig = Sig , ℓ′ ≔ a} {ℓ} (rec r) with ℓ ≟ ℓ′
  ... | yes _ = rec (Manifest-Σ.proj₁ r ,)
  ... | no  _ = rec (drop-With (Manifest-Σ.proj₁ r) ,)


open import Prelude

module Implicits.Resolution.Finite.Expressiveness where

open import Coinduction
open import Data.Fin.Substitution
open import Implicits.Syntax
open import Implicits.Substitutions
open import Implicits.Resolution.Deterministic.Resolution as D
open import Implicits.Resolution.Ambiguous.Resolution as A
open import Implicits.Resolution.Finite.Resolution as F
open import Implicits.Resolution.Infinite.Resolution as ∞
open import Implicits.Resolution.Termination

module Finite⊆Infinite where

  p : ∀ {ν} {a} {Δ : ICtx ν} → Δ F.⊢ᵣ a → Δ ∞.⊢ᵣ a
  p (r-simp a a↓τ) = r-simp a (lem a↓τ)
    where
      lem : ∀ {ν} {a τ} {Δ : ICtx ν} → Δ F.⊢ a ↓ τ → Δ ∞.⊢ a ↓ τ
      lem (i-simp τ) = i-simp τ
      lem (i-iabs _ ⊢ᵣa b↓τ) = i-iabs (p ⊢ᵣa) (lem b↓τ)
      lem (i-tabs b a[/b]↓τ) = i-tabs b (lem a[/b]↓τ)
  p (r-iabs x) = r-iabs (p x)
  p (r-tabs x) = r-tabs (p x)

module Finite⊆Ambiguous where

  p : ∀ {ν} {a} {Δ : ICtx ν} → Δ F.⊢ᵣ a → Δ A.⊢ᵣ a
  p (r-simp a a↓τ) = lem a↓τ (r-ivar a)
    where
      lem : ∀ {ν} {a τ} {Δ : ICtx ν} → Δ F.⊢ a ↓ τ → Δ A.⊢ᵣ a → Δ A.⊢ᵣ simpl τ
      lem (i-simp τ) K⊢ᵣτ = K⊢ᵣτ
      lem (i-iabs _ ⊢ᵣa b↓τ) K⊢ᵣa⇒b = lem b↓τ (r-iapp K⊢ᵣa⇒b (p ⊢ᵣa))
      lem (i-tabs b a[/b]↓τ) K⊢ᵣ∀a = lem a[/b]↓τ (r-tapp b K⊢ᵣ∀a)
  p (r-iabs x) = r-iabs (p x)
  p (r-tabs x) = r-tabs (p x)

module Deterministic⊆Finite where

  open import Extensions.ListFirst

  -- Oliveira's termination condition is part of the well-formdness of types
  -- So we assume here that ⊢term x holds for all types x
  p : ∀ {ν} {a : Type ν} {Δ : ICtx ν} → (∀ {ν} (a : Type ν) → ⊢term a) → Δ D.⊢ᵣ a → Δ F.⊢ᵣ a
  p term (r-simp {ρ = r} x r↓a) =
    r-simp (proj₁ $ first⟶∈ x) (lem r↓a)
    where
      lem : ∀ {ν} {Δ : ICtx ν} {a r} → Δ D.⊢ r ↓ a → Δ F.⊢ r ↓ a
      lem (i-simp a) = i-simp a
      lem (i-iabs {ρ₁ = ρ₁} {ρ₂ = ρ₂} ⊢ᵣρ₁ ρ₂↓τ) with term (ρ₁ ⇒ ρ₂)
      lem (i-iabs ⊢ᵣρ₁ ρ₂↓τ) | term-iabs _ _ a-ρ<-b _ = i-iabs a-ρ<-b (p term ⊢ᵣρ₁) (lem ρ₂↓τ)
      lem (i-tabs b x₁) = i-tabs b (lem x₁)
  p term (r-iabs ρ₁ {ρ₂ = ρ₂} x) = r-iabs (p term x)
  p term (r-tabs x) = r-tabs (p term x)


------------------------------------------------------------------------
-- A variant of set quotients with erased higher constructors
------------------------------------------------------------------------

-- This module contains some basic definitions with few dependencies
-- (in particular, not Quotient). See Quotient.Erased for more
-- definitions. The definitions below are reexported from
-- Quotient.Erased.

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

-- Partly following the HoTT book, but adapted for erasure.
--
-- Unlike the HoTT book, but following the cubical library (in which
-- set quotients were implemented by Zesen Qian and Anders Mörtberg),
-- the quotienting relations are not (always) required to be
-- propositional.

-- The module is parametrised by a notion of equality. The higher
-- constructors of the HIT defining quotients use path equality, but
-- the supplied notion of equality is used for many other things.

import Equality.Path as P

module Quotient.Erased.Basics
  {e⁺} (eq : ∀ {a p} → P.Equality-with-paths a p e⁺) where

open P.Derived-definitions-and-properties eq hiding (elim)

open import Prelude

open import Bijection equality-with-J using (_↔_)
open import Equality.Path.Isomorphisms eq
open import Function-universe equality-with-J hiding (_∘_)
open import H-level equality-with-J
import H-level P.equality-with-J as PH
open import H-level.Closure equality-with-J

private
  variable
    a b p r : Level
    A B     : Type a
    R       : A → A → Type r
    x y     : A

------------------------------------------------------------------------
-- Quotients

-- The quotient type constructor.

infix 5 _/ᴱ_

data _/ᴱ_ (A : Type a) (@0 R : A → A → Type r) : Type (a ⊔ r) where
  [_]                      : A → A /ᴱ R
  @0 []-respects-relationᴾ : {x y : A} → R x y → [ x ] P.≡ [ y ]
  @0 /ᴱ-is-setᴾ            : P.Is-set (A /ᴱ R)

-- [_] respects the quotient relation (in erased contexts).

@0 []-respects-relation : R x y → _≡_ {A = A /ᴱ R} [ x ] [ y ]
[]-respects-relation = _↔_.from ≡↔≡ ∘ []-respects-relationᴾ

-- Quotients are sets (in erased contexts).

@0 /ᴱ-is-set : Is-set (A /ᴱ R)
/ᴱ-is-set = _↔_.from (H-level↔H-level 2) /ᴱ-is-setᴾ

------------------------------------------------------------------------
-- Eliminators

-- An eliminator, expressed using paths.

record Elimᴾ′ {A : Type a} {@0 R : A → A → Type r}
              (P : A /ᴱ R → Type p) :
              Type (a ⊔ r ⊔ p) where
  no-eta-equality
  field
    []ʳ : ∀ x → P [ x ]

    @0 []-respects-relationʳ :
      (r : R x y) →
      P.[ (λ i → P ([]-respects-relationᴾ r i)) ] []ʳ x ≡ []ʳ y

    @0 is-setʳ :
      {eq₁ eq₂ : x P.≡ y} {p : P x} {q : P y}
      (eq₃ : P.[ (λ i → P (eq₁ i)) ] p ≡ q)
      (eq₄ : P.[ (λ i → P (eq₂ i)) ] p ≡ q) →
      P.[ (λ i → P.[ (λ j → P (/ᴱ-is-setᴾ eq₁ eq₂ i j)) ] p ≡ q) ]
        eq₃ ≡ eq₄

open Elimᴾ′ public

elimᴾ′ :
  {@0 R : A → A → Type r}
  {P : A /ᴱ R → Type p} →
  Elimᴾ′ P → (x : A /ᴱ R) → P x
elimᴾ′ {A = A} {R = R} {P = P} e = helper
  where
  module E = Elimᴾ′ e

  helper : (x : A /ᴱ R) → P x
  helper [ x ]                       = E.[]ʳ x
  helper ([]-respects-relationᴾ r i) = E.[]-respects-relationʳ r i
  helper (/ᴱ-is-setᴾ p q i j)        =
    E.is-setʳ (λ i → helper (p i)) (λ i → helper (q i)) i j

-- A possibly more useful eliminator, expressed using paths.

record Elimᴾ {A : Type a} {@0 R : A → A → Type r}
             (P : A /ᴱ R → Type p) :
             Type (a ⊔ r ⊔ p) where
  no-eta-equality
  field
    []ʳ : ∀ x → P [ x ]

    @0 []-respects-relationʳ :
      (r : R x y) →
      P.[ (λ i → P ([]-respects-relationᴾ r i)) ] []ʳ x ≡ []ʳ y

    @0 is-setʳ : ∀ x → P.Is-set (P x)

open Elimᴾ public

elimᴾ :
  {@0 R : A → A → Type r}
  {P : A /ᴱ R → Type p} →
  Elimᴾ P → (x : A /ᴱ R) → P x
elimᴾ e = elimᴾ′ λ where
    .[]ʳ                   → E.[]ʳ
    .[]-respects-relationʳ → E.[]-respects-relationʳ
    .is-setʳ               → P.heterogeneous-UIP E.is-setʳ _
  where
  module E = Elimᴾ e

private

  -- One can define elimᴾ′ using elimᴾ.

  elimᴾ′₂ :
    {@0 R : A → A → Type r}
    {P : A /ᴱ R → Type p} →
    Elimᴾ′ P → (x : A /ᴱ R) → P x
  elimᴾ′₂ {P = P} e = elimᴾ λ where
      .[]ʳ                   → E.[]ʳ
      .[]-respects-relationʳ → E.[]-respects-relationʳ
      .is-setʳ x {y} {z} p q →                                          $⟨ E.is-setʳ p q ⟩
        P.[ (λ i →
               P.[ (λ j → P (/ᴱ-is-setᴾ P.refl P.refl i j)) ] y ≡ z) ]
          p ≡ q                                                         ↝⟨ P.subst (λ eq → P.[ (λ i → P.[ (λ j → P (eq i j)) ] y ≡ z) ] p ≡ q)
                                                                                (PH.mono₁ 2 /ᴱ-is-setᴾ _ _) ⟩
        P.[ (λ _ → P.[ (λ _ → P x) ] y ≡ z) ] p ≡ q                     ↔⟨⟩

        p P.≡ q                                                         □
    where
    module E = Elimᴾ′ e

-- A non-dependent eliminator, expressed using paths.

record Recᴾ {A : Type a} (@0 R : A → A → Type r) (B : Type b) :
            Type (a ⊔ r ⊔ b) where
  no-eta-equality
  field
    []ʳ                      : A → B
    @0 []-respects-relationʳ : (r : R x y) → []ʳ x P.≡ []ʳ y
    @0 is-setʳ               : P.Is-set B

open Recᴾ public

recᴾ :
  {@0 R : A → A → Type r} →
  Recᴾ R B → A /ᴱ R → B
recᴾ r = elimᴾ λ where
    .[]ʳ                   → R.[]ʳ
    .[]-respects-relationʳ → R.[]-respects-relationʳ
    .is-setʳ _             → R.is-setʳ
  where
  module R = Recᴾ r

-- An eliminator.

record Elim {A : Type a} {@0 R : A → A → Type r}
            (P : A /ᴱ R → Type p) :
            Type (a ⊔ r ⊔ p) where
  no-eta-equality
  field
    []ʳ : ∀ x → P [ x ]

    @0 []-respects-relationʳ :
      (r : R x y) →
      subst P ([]-respects-relation r) ([]ʳ x) ≡ []ʳ y

    @0 is-setʳ : ∀ x → Is-set (P x)

open Elim public

elim :
  {@0 R : A → A → Type r}
  {P : A /ᴱ R → Type p} →
  Elim P → (x : A /ᴱ R) → P x
elim e = elimᴾ λ where
    .[]ʳ                   → E.[]ʳ
    .[]-respects-relationʳ → subst≡→[]≡ ∘ E.[]-respects-relationʳ
    .is-setʳ               → _↔_.to (H-level↔H-level 2) ∘ E.is-setʳ
  where
  module E = Elim e

-- A non-dependent eliminator.

record Rec {A : Type a} (@0 R : A → A → Type r) (B : Type b) :
           Type (a ⊔ r ⊔ b) where
  no-eta-equality
  field
    []ʳ                      : A → B
    @0 []-respects-relationʳ : (r : R x y) → []ʳ x ≡ []ʳ y
    @0 is-setʳ               : Is-set B

open Rec public

rec :
  {@0 R : A → A → Type r} →
  Rec R B → A /ᴱ R → B
rec r = recᴾ λ where
    .[]ʳ                   → R.[]ʳ
    .[]-respects-relationʳ → _↔_.to ≡↔≡ ∘ R.[]-respects-relationʳ
    .is-setʳ               → _↔_.to (H-level↔H-level 2) R.is-setʳ
  where
  module R = Rec r

-- A variant of elim that can be used if the motive composed with [_]
-- is a family of propositions.
--
-- I took the idea for this eliminator from Nicolai Kraus.

record Elim-prop
         {A : Type a} {@0 R : A → A → Type r}
         (P : A /ᴱ R → Type p) :
         Type (a ⊔ r ⊔ p) where
  no-eta-equality
  field
    []ʳ                : ∀ x → P [ x ]
    @0 is-propositionʳ : ∀ x → Is-proposition (P [ x ])

open Elim-prop public

elim-prop :
  {@0 R : A → A → Type r}
  {P : A /ᴱ R → Type p} →
  Elim-prop P → (x : A /ᴱ R) → P x
elim-prop e = elim λ where
    .[]ʳ                     → E.[]ʳ
    .[]-respects-relationʳ _ → E.is-propositionʳ _ _ _
    .is-setʳ                 → elim λ @0 where
      .[]ʳ                     → mono₁ 1 ∘ E.is-propositionʳ
      .[]-respects-relationʳ _ → H-level-propositional ext 2 _ _
      .is-setʳ _               → mono₁ 1 (H-level-propositional ext 2)
  where
  module E = Elim-prop e

-- A variant of rec that can be used if the motive is a proposition.

record Rec-prop (A : Type a) (B : Type b) : Type (a ⊔ b) where
  no-eta-equality
  field
    []ʳ                : A → B
    @0 is-propositionʳ : Is-proposition B

open Rec-prop public

rec-prop :
  {@0 R : A → A → Type r} →
  Rec-prop A B → A /ᴱ R → B
rec-prop r = elim-prop λ where
    .[]ʳ               → R.[]ʳ
    .is-propositionʳ _ → R.is-propositionʳ
  where
  module R = Rec-prop r


postulate
  A : Set
  P : A → Set
  p : (a : A) → P a

record R : Set where
  field
    a : A
  b : P _
  b = p a

test : A
test = R.b


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

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

module homotopy.EilenbergMacLane where

-- EM(G,n) when G is π₁(A,a₀)
module EMImplicit {i} (A : Type i) (cA : is-connected ⟨0⟩ A)
  (gA : has-level ⟨ 1 ⟩ A) (A-H : HSS A)
  (μcoh : HSS.μe- A-H (HSS.e A-H) == HSS.μ-e A-H (HSS.e A-H)) where

  private
    a₀ = HSS.e A-H
    X = ⊙[ A , a₀ ]

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

  module _ (n : ℕ) where
    EM = fst (⊙EM n)
    embase = snd (⊙EM n)

  EM-level : (n : ℕ) → has-level ⟨ n ⟩ (fst (⊙EM n))
  EM-level O = gA a₀ a₀
  EM-level (S n) = Trunc-level

  EM-conn : (n : ℕ) → is-connected ⟨ n ⟩ (EM (S n))
  EM-conn n = Trunc-preserves-conn ⟨ S n ⟩
                  (transport (λ t → is-connected t (fst (⊙Susp^ n X)))
                    (nlemma n) (⊙Susp^-conn n cA))
    where nlemma : (n : ℕ) → (n -2) +2+ ⟨0⟩ == ⟨ n ⟩
          nlemma O = idp
          nlemma (S n) = ap S (nlemma n)

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

    private
      n : ℕ
      n = S (S n')

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

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

    private
      module SS = Susp^Stable X cA (S n') k tk tsk kle

    abstract
      stable : π (S k) tsk (⊙EM (S n))
             == π k tk (⊙EM n)
      stable =
        π (S k) tsk (⊙EM (S n))
          =⟨ π-below-trunc _ tsk _ _ (≤T-ap-S lte) ⟩
        π (S k) tsk (⊙Susp^ n X)
          =⟨ SS.stable ⟩
        π k tk (⊙Susp^ (S n') X)
          =⟨ ! (π-below-trunc _ tk _ _ lte) ⟩
        π k tk (⊙EM n) ∎

  module BelowDiagonal where

    π₁ : (n : ℕ) (t1 : 1 ≠ 0 )
      → π 1 t1 (⊙EM (S (S n))) == LiftUnit-Group
    π₁ n t1 =
      contr-is-0ᴳ (π 1 t1 (⊙EM (S (S n))))
        (connected-at-level-is-contr
          (raise-level-≤T (≤T-ap-S (≤T-ap-S (-2≤T (n -2))))
                          (Trunc-level {n = ⟨0⟩}))
          (Trunc-preserves-conn ⟨0⟩ (path-conn (EM-conn (S n)))))

    -- some clutter here arises from the definition of <;
    -- any simple way to avoid this?
    π-below : (k n : ℕ) (tk : k ≠ 0) → (k < n)
      → π k tk (⊙EM n) == LiftUnit-Group
    π-below 0 _ tk lt = ⊥-rec (tk idp)
    π-below 1 .2 tk ltS = π₁ 0 tk
    π-below 1 .3 tk (ltSR ltS) = π₁ 1 tk
    π-below 1 (S (S n)) tk (ltSR (ltSR _)) = π₁ n tk
    π-below (S (S k)) ._ tssk ltS =
      Stable.stable (S k) k (ℕ-S≠O k) tssk (inr ltS)
      ∙ π-below (S k) _ (ℕ-S≠O k) ltS
    π-below (S (S k)) ._ tssk (ltSR ltS) =
      Stable.stable (S k) (S k) (ℕ-S≠O k) tssk (inr (ltSR ltS))
      ∙ π-below (S k) _ (ℕ-S≠O k) (ltSR ltS)
    π-below (S (S k)) ._ tssk (ltSR (ltSR ltS)) =
      Stable.stable (S k) (S (S k)) (ℕ-S≠O k) tssk (inr (ltSR (ltSR ltS)))
      ∙ π-below (S k) _ (ℕ-S≠O k) (ltSR (ltSR ltS))
    π-below (S (S k)) (S (S (S n))) tssk (ltSR (ltSR (ltSR lt))) =
      Stable.stable (S k) n (ℕ-S≠O k) tssk
        (inr (<-cancel-S (ltSR (ltSR (ltSR lt)))))
      ∙ π-below (S k) _ (ℕ-S≠O k) (<-cancel-S (ltSR (ltSR (ltSR lt))))

  module OnDiagonal where

    π₁ : (tn : 1 ≠ O) (t1 : 1 ≠ O)
      → π 1 tn (⊙EM 1) == π 1 t1 X
    π₁ tn t1 =
      π-below-trunc 1 tn ⟨ 1 ⟩ X ≤T-refl
      ∙ ap (λ t → π 1 t X)
           (prop-has-all-paths (Π-level (λ _ → ⊥-is-prop)) tn t1)

    private
      module Π₂ = Pi2HSusp A gA cA A-H μcoh

    π₂ : (tn : 2 ≠ O) (t1 : 1 ≠ O)
      → π 2 tn (⊙EM 2) == π 1 t1 X
    π₂ tn t1 =
      π-below-trunc 2 tn ⟨ 2 ⟩ (⊙Susp X) ≤T-refl
      ∙ Π₂.π₂-Suspension t1 tn

    π-diag : (n : ℕ) (tn : n ≠ 0) (t1 : 1 ≠ O)
      → π n tn (⊙EM n) == π 1 t1 X
    π-diag 0 tn _ = ⊥-rec (tn idp)
    π-diag 1 tn t1 = π₁ tn t1
    π-diag 2 tn t1 = π₂ tn t1
    π-diag (S (S (S n))) tn t1 =
      Stable.stable (S (S n)) n (ℕ-S≠O _) tn ≤-refl
      ∙ π-diag (S (S n)) (ℕ-S≠O _) t1

  module AboveDiagonal where

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

  module Spectrum where

    private
      module Π₂ = Pi2HSusp A gA cA A-H μcoh

    spectrum0 : ⊙Ω (⊙EM 1) == ⊙EM 0
    spectrum0 =
      ⊙Ω (⊙EM 1)
        =⟨ ⊙ua (Trunc=-equiv _ _) idp ⟩
      ⊙Trunc ⟨ 0 ⟩ (⊙Ω X)
        =⟨ ⊙ua (unTrunc-equiv _ (gA a₀ a₀)) idp ⟩
      ⊙Ω X ∎

    spectrum1 : ⊙Ω (⊙EM 2) == ⊙EM 1
    spectrum1 =
      ⊙Ω (⊙EM 2)
        =⟨ ⊙ua (Trunc=-equiv _ _) idp ⟩
      ⊙Trunc ⟨ 1 ⟩ (⊙Ω (⊙Susp X))
        =⟨ Π₂.⊙main-lemma ⟩
      X
        =⟨ ! (⊙ua (unTrunc-equiv _ gA) idp) ⟩
      ⊙EM 1 ∎

    private
      nlemma : (n : ℕ) → Path {A = ℕ₋₂} (S ((n -2) +2+ ⟨0⟩)) (S (S (n -1)))
      nlemma O = idp
      nlemma (S n) = ap S (nlemma n)

      sconn : (n : ℕ) → is-connected (S (S (n -1))) (fst (⊙Susp^ (S n) X))
      sconn n = transport (λ t → is-connected t (fst (⊙Susp^ (S n) X)))
                          (nlemma n) (⊙Susp^-conn (S n) cA)

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

      module FS (n : ℕ) =
        FreudenthalEquiv (n -1) (⟨ S (S n) ⟩) (kle n)
          (fst (⊙Susp^ (S n) X)) (snd (⊙Susp^ (S n) X)) (sconn n)

    spectrumSS : (n : ℕ)
      → ⊙Ω (⊙EM (S (S (S n)))) == ⊙EM (S (S n))
    spectrumSS n =
      ⊙Ω (⊙EM (S (S (S n))))
        =⟨ ⊙ua (Trunc=-equiv _ _) idp ⟩
      ⊙Trunc ⟨ S (S n) ⟩ (⊙Ω (⊙Susp^ (S (S n)) X))
        =⟨ ! (FS.⊙path n) ⟩
      ⊙EM (S (S n)) ∎

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

module EMExplicit {i} (G : Group i) (G-abelian : is-abelian G) where
  module K₁ = EM₁ G
  module HSpace = EM₁HSpace G G-abelian
  open EMImplicit K₁.EM₁ K₁.EM₁-conn K₁.emlevel HSpace.H-EM₁ HSpace.μcoh public

  open BelowDiagonal public using (π-below)

  π-diag : (n : ℕ) (tn : n ≠ 0) → π n tn (⊙EM n) == G
  π-diag n tn =
    OnDiagonal.π-diag n tn (ℕ-S≠O 0) ∙ K₁.π₁.π₁-iso

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


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

open import Base
open import Homotopy.TruncatedHIT

{-
The idea is that if [n : ℕ] and [A : Set i], then [τ n A] is defined by the
following n-truncated higher inductive type:

    module Homotopy.TruncationHIT {i} (n : ℕ) (A : Set i) where

    (n)data τ : Set i where
      proj : A → τ

More explanations on the way this is done can be found in TruncatedHIT.agda.
This file contains nothing but stuff that could (should) be autogenerated from
the three lines above.
-}

module Homotopy.TruncationHIT {i} (n : ℕ₋₂) (A : Set i) where

private
  data #τ : Set i where
    #proj : A → #τ

    #top : (f : hSⁿ n → #τ) → #τ

τ : Set i
τ = #τ

proj : A → τ
proj = #proj

top : (f : hSⁿ n → τ) → τ
top = #top

postulate  -- HIT
  rays : (f : hSⁿ n → τ) (x : hSⁿ n) → top f ≡ f x
  hack-prop : (p : n ≡ ⟨-2⟩) (x y : τ) → (x ≡ y)

#τ-rec : ∀ {j} (P : τ → Set j)
  (proj* : (x : A) → P (proj x))
  {top* : (f : hSⁿ n → τ) (p : (x : hSⁿ n) → P (f x)) → P (top f)}
  {rays* : (f : hSⁿ n → τ) (p : (x : hSⁿ n) → P (f x)) (x : hSⁿ n)
    → transport P (rays f x) (top* f p) ≡ p x}
  {hack-prop* : (p : n ≡ ⟨-2⟩) (x y : τ) (x* : P x) (y* : P y)
    → (transport P (hack-prop p x y) x* ≡ y* )}
  → ((x : τ) → P x)
#τ-rec P proj* {top*} {rays*} {hack-prop*} (#proj u) = proj* u
#τ-rec P proj* {top*} {rays*} {hack-prop*} (#top f) =
  top* f (λ x → #τ-rec P proj* {top*} {rays*} {hack-prop*} (f x))

#τ-rec-nondep : ∀ {j} (C : Set j)
  (proj* : A → C)
  {top* : (f : hSⁿ n → τ) (p : hSⁿ n → C) → C}
  {rays* : (f : hSⁿ n → τ) (p : hSⁿ n → C) (x : hSⁿ n) → top* f p ≡ p x}
  {hack-prop* : (p : n ≡ ⟨-2⟩) (x y : τ) (x* y* : C) → x* ≡ y*}
  → (τ → C)
#τ-rec-nondep C proj* {top*} {rays*} {hack-prop*} (#proj u) = proj* u
#τ-rec-nondep C proj* {top*} {rays*} {hack-prop*} (#top f) =
  top* f (λ x → #τ-rec-nondep C proj* {top*} {rays*} {hack-prop*} (f x))

private
  contr : (n ≡ ⟨-2⟩) → is-contr τ
abstract
  contr p = inhab-prop-is-contr (top (λ x → abort-nondep (transport hSⁿ p x)))
                                (all-paths-is-prop (hack-prop p))

-- Computation rules for [rays] are not needed

τ-rec :  ∀ {j} (P : τ → Set j)
  (proj* : (x : A) → P (proj x))
  {hack-prop* : (p : n ≡ ⟨-2⟩) (x y : τ) (x* : P x) (y* : P y)
    → (transport P (hack-prop p x y) x* ≡ y* )}
  (trunc : (x : τ) → is-truncated n (P x))
  → ((x : τ) → P x)
τ-rec P proj* {hack-prop*} trunc =
  #τ-rec P proj* {x₁} {x₂} {hack-prop*} where
  abstract
    u : (f : hSⁿ n → τ) (p : (x : hSⁿ n) → P (f x))
      → filling-dep n P f (top f , rays f) p
    u = truncated-has-filling-dep τ P n contr (λ f → (top f , rays f))

    x₁ : (f : hSⁿ n → τ) (p : (x : hSⁿ n) → P (f x)) → P (top f)
    x₁ = λ f p → π₁ (u f p)

    x₂ : (f : hSⁿ n → τ) (p : (x : hSⁿ n) → P (f x)) (x : hSⁿ n)
      → transport P (rays f x) (x₁ f p) ≡ p x
    x₂ = λ f p → π₂ (u f p)

τ-rec-nondep : ∀ {j} (C : Set j)
  (proj* : A → C)
  {hack-prop* : (p : n ≡ ⟨-2⟩) (x y : τ) (x* y* : C) → x* ≡ y*}
  (trunc : is-truncated n C)
  → (τ → C)
τ-rec-nondep C proj* {hack-prop*} trunc =
  #τ-rec-nondep C proj* {λ _ p → π₁ (u p)}
                        {λ _ p → π₂ (u p)} {hack-prop*} where
  u : _
  u = truncated-has-spheres-filled n _ trunc

-- The nth truncation is of h-level [n]
abstract
  τ-is-truncated : is-truncated n τ
  τ-is-truncated =
    spheres-filled-is-truncated n τ contr (λ f → (top f , rays f))


module Generate where

open import Data.List
open import Data.Fin hiding (_+_)
open import Data.Nat
open import Data.Product
open import Data.Bool
open import Function using (_∘_ ; _$_ ; _∋_ ; id ; case_return_of_ ; flip )
open import Function.Injection hiding (_∘_)
open import Function.Surjection hiding (_∘_)
open import Function.Bijection hiding (_∘_)
open import Function.LeftInverse hiding (_∘_)
open import Relation.Binary using (Setoid ; Decidable)
open import Relation.Binary.PropositionalEquality hiding ( [_] )
open import Relation.Nullary.Negation
open import Relation.Nullary
open import Function.Equality using (_⟶_ ;  _⟨$⟩_ ; Π )
open import Reflection
open import Level

open import Serializer
open import Helper.Fin
open import Helper.CodeGeneration

open import Serializer.Bool
open import Serializer.Fin
open import With hiding (subst) --  ; _+_ ; A)

open Serializer.Serializer {{...}}
--open Serializer.Serializer {{Fin 2}}

-------------------------
-- Generic from/to term's.

data Test : Set where
  A : Bool -> Test
  B : Bool -> Test
  C : Bool -> Test
  D : Bool -> Test

conSize : (n : Name) -> Term
conSize n with (definition n)
conSize n | constructor′ with (type n)
conSize n | constructor′ | el (lit n₁) (pi (arg i (el s t)) t₂) = def (quote size) [ a1 t ]
conSize n | constructor′ | el _ _ = quoteTerm 0
conSize n | _ = quoteTerm 0

sumTerms : List Name -> Term
sumTerms [] = quoteTerm 0
sumTerms (x ∷ ls) = def (quote _+_) (a (conSize x) ∷ a (sumTerms ls) ∷ [])

cons`` : (n : Name) -> List (_×_ Name (_×_ (List Name) (List Name)))
cons`` n = zipWith _,_ (cons n) (zipWith _,_ (inits (cons n)) (drop 1 (tails (cons n))))

{-
fromTest : Test -> Fin 4
fromTest (A z) = +₁ 2 (2 + 0) (fromBool z)
fromTest (B z) = +₂ 2 2 (fromBool z)

toTest : Fin 4 -> Test
toTest x = (Helper.Fin.[ (λ z₁ → A (toBool z₁)) , (λ z₁ → B (toBool z₁)) ] (⨁ 2 2 x))
-}

{-
fromTest : Test -> Fin 6
fromTest (A x) = +₁ 4 2 (+₁ 2 2 (from x))
fromTest (B x) = +₁ 4 2 (+₂ 2 2 (from x))
fromTest (C x) = +₂ 4 2 (from x)

toTest : Fin 6 -> Test
toTest x = Helper.Fin.[ (\y -> Helper.Fin.[ (\z -> A $ to z) , (\z -> B $ to z) ] (⨁ 2 2 y) ) , (\y -> C $ to y) ] (⨁ 4 2 x)
-}

cons` : Name -> List (_×_ Name (_×_ (List Name) (List Name)))
cons` n = zipWith _,_ (reverse (cons n)) (zipWith _,_ (inits (reverse (cons n))) (Data.List.map reverse (drop 1 (tails (reverse (cons n))))))

{-# TERMINATING #-}
genFrom : (n : Name)  -> FunctionDef
genFrom n = fun-def (t0 $ fun_type) clauses
  where
    fun_type = pi (a ∘ t0 $ def n []) (abs "_" (t0 (def (quote Fin) [ a (sumTerms (cons n)) ])))

    construct : (_×_ Name (_×_ (List Name) (List Name))) -> Term
    construct (c , [] , []) = (def (quote from) [ a (var 0 []) ])
    construct (c , x ∷ xs , []) = def (quote +₂) ((a (sumTerms (x ∷ xs))) ∷ (a (conSize c)) ∷ (a (construct (c , ([] , [])))) ∷ [])
    construct (c , xs , y ∷ ys) = def (quote +₁) ((a (sumTerms (c ∷ (xs ++ ys)))) ∷ (a (conSize y)) ∷ (a (construct (c , (xs , ys)))) ∷ [])

    clauses = Data.List.map (\{ (c , xs) -> clause [ a $ con c [ a (var "_") ] ] (construct (c , xs))}) (cons` n)

unquoteDecl fromTest = genFrom (quote Test)

genTo : (n : Name)  -> FunctionDef
genTo n = fun-def (t0 $ fun_type) clauses
  where
    fun_type = pi (a (t0 (def (quote Fin) [ a (sumTerms (cons n)) ]))) (abs "_" (t0 $ def n []))

    match : List Name -> Term
    match [] = unknown
    match (x ∷ []) = con x [ a (def (quote to) [ a (var 0 []) ]) ]
    match (x ∷ x₁ ∷ cons) = def (quote [_,_])
          ( (a (lam visible (abs "_" (match (x₁ ∷ cons))))) -- (a (lam visible (abs "_" (con x [ a (def (quote to) [ a (var 0 []) ]) ]))))
          ∷ (a (lam visible (abs "_" (con x [ a (def (quote to) [ a (var 0 []) ]) ]))))
          ∷ (a (def (quote ⨁) ((a (sumTerms (x₁ ∷ cons))) ∷ (a (quoteTerm 2)) ∷ (a (var 0 [])) ∷ [])))
          ∷ [])

    clauses = [ clause [ a (var "_") ] (match (cons n)) ]

unquoteDecl toTest = genTo (quote Test)

makePresEq : {T : Set} {n : ℕ} -> (T -> (Fin n)) -> setoid T ⟶ setoid (Fin n)
makePresEq f = record { _⟨$⟩_ = f ; cong = (\{ {i} {.i} refl → refl }) }

genPresEq' : (n : Name) -> Term
genPresEq' n = def (quote makePresEq) [ a (def (quote fromTest) []) ] -- [(a (genFrom' n))]
  where
    setoidFrom = def (quote setoid) [(a (def n []))]
    setoidTo = def (quote setoid) [(a (def (quote Fin) [ a (sumTerms (cons n)) ]))]
    fun_type1 = t0 (def (quote _⟶_) ( (a setoidFrom) ∷ (a setoidTo) ∷ []))

genPresEq : (n : Name) -> FunctionDef
genPresEq n = fun-def fun_type1 [(clause [] (genPresEq' n) ) ]
  where
    setoidFrom = def (quote setoid) [(a (def n []))]
    setoidTo = def (quote setoid) [(a (def (quote Fin) [ a (sumTerms (cons n)) ]))]
    fun_type1 = t0 (def (quote _⟶_) ( (a setoidFrom) ∷ (a setoidTo) ∷ []))

unquoteDecl genTest = genPresEq (quote Test)

t2 : {x y : Bool} → fromBool x ≡ fromBool y → x ≡ y
t2 = Bijective.injective (Bijection.bijective (bijection {Bool}))

bijection' : Term
bijection' = quoteTerm t2

refl' : Term
refl' = pat-lam [ clause ( (a1 (var "d1")) ∷ (a1 (dot)) ∷ ( a (con (quote refl) []) ) ∷ []) (con (quote refl) []) ] []

x₁≡x₂' : Term
x₁≡x₂' = def (quote _≡_) ((a $ var 1 []) ∷ (a $ var 0 []) ∷ [])

+-eq₁' : Term -> Term
+-eq₁' t = (def (quote +-eq₁) [ a t ])

+-eq₂' : ℕ -> ℕ -> Term -> Term
+-eq₂' n1 n2 t = (def (quote +-eq₂) ( a1 (lit (nat n1)) ∷ a1 (lit (nat n2)) ∷ a t ∷ [] ) )

¬+-eq₁' : Term
¬+-eq₁' = def (quote ¬+-eq₁) []

¬+-eq₂' : Term
¬+-eq₂' = def (quote ¬+-eq₂) []

_$'_ : Term -> Term -> Term
x $' y = def (quote _$_) ((a x) ∷ (a y) ∷ [])

contradiction' : Term -> Term -> Term
contradiction' t1 t2 = def (quote contradiction) ( (a t1) ∷ (a t2) ∷ [] )

genInjective : (n : Name) -> FunctionDef
genInjective n = fun-def fun_type1 [ (clause [] ( pat-lam (clauses 0 (var 0 []) (indexed_cons)) [] )) ]
  where
    fun_type1 = t0 (def (quote Injective) [ (a (def (quote genTest) [])) ] )

    ncons = (length (cons n))
    indexed_cons = zipWith (_,_) (cons n) ((downFrom (length (cons n))))

    clause'' : Name -> Name -> Term -> Clause
    clause'' l r t = clause ( (a1 $ con l [ a (var "x") ]) ∷ (a1 $ con r [ a (var "y") ]) ∷ (a (var "p")) ∷ [] ) t

    clause' : ℕ -> Term -> (_×_ Name ℕ) -> (_×_ Name ℕ) -> Clause
    clause' n t (n1 , i1) (n2 , i2) with (Data.Nat.compare i1 i2)
    clause' n t (n1 , i1) (n2 , ._) | less .i1 k = clause'' n1 n2 (contradiction' (t) ¬+-eq₁')
    clause' n t (n1 , ℕ.zero) (n2 , .0) | equal .0 = clause'' n1 n2 (with' x₁≡x₂' (bijection' $' (t)) refl')
    clause' n t (n1 , ℕ.suc i1) (n2 , .(ℕ.suc i1)) | equal .(ℕ.suc i1) = clause'' n1 n2 (with' x₁≡x₂' (bijection' $' (+-eq₂' ((ncons ∸ n ∸ 1) * 2) 2 (t))) refl')
    clause' n t (n1 , ._) (n2 , i2) | greater .i2 k = clause'' n1 n2 (contradiction' (t) ¬+-eq₂')

    clauses : ℕ -> Term -> List (_×_ Name ℕ) -> List Clause
    clauses n t [] = []
    clauses n t (x ∷ cs) = (Data.List.map (clause' n t x) (x ∷ cs)) ++ (Data.List.map ((flip (clause' n t)) x) cs) ++ (clauses (ℕ.suc n) (+-eq₁' t) cs) -- Data.List.map clause' c

unquoteDecl testInjective = genInjective (quote Test)

makePresEqTo : {T : Set} {n : ℕ} -> ((Fin n) -> T) -> setoid (Fin n) ⟶ setoid T
makePresEqTo f = record { _⟨$⟩_ = f ; cong = (\{ {i} {.i} refl → refl }) }

genPresEqTo' : (n : Name) -> Term
genPresEqTo' n = def (quote makePresEqTo) [ a (def (quote toTest) []) ]
  where
    setoidTo = def (quote setoid) [(a (def n []))]
    setoidFrom = def (quote setoid) [(a (def (quote Fin) [ a (sumTerms (cons n)) ]))]
    fun_type1 = t0 (def (quote _⟶_) ( (a setoidFrom) ∷ (a setoidTo) ∷ []))

genPresEqTo : (n : Name) -> FunctionDef
genPresEqTo n = fun-def infer_type [ (clause [] (genPresEqTo' n) ) ]

unquoteDecl genTestTo = genPresEqTo (quote Test)

Fin+' : ℕ -> ℕ -> Term
Fin+' n1 n2 = def (quote Fin+) ((a (lit (nat n1))) ∷ (a (lit (nat n2))) ∷ (a $ var 0 []) ∷ [])

⨁' : ℕ -> ℕ -> Term
⨁' n1 n2 = def (quote ⨁) ((a (lit (nat n1))) ∷ (a (lit (nat n2))) ∷ (a $ var 0 []) ∷ [])

x₂≡x₁' : Term
x₂≡x₁' = def (quote _≡_) ( (a $ var 0 []) ∷ (a $ var 1 []) ∷ [])

right-inverse-of' : Term
right-inverse-of' = quoteTerm (Surjective.right-inverse-of (Bijective.surjective (Bijection.bijective (bijection {Bool}))))

fromToBool' : Term
fromToBool' = (def (quote fromBool) (arg (arg-info visible relevant) (def (quote toBool) (arg (arg-info visible relevant) (var 0 []) ∷ [])) ∷ []))

matchFin+ : Term -> Term -> Term
matchFin+ t1 t2 = pat-lam ((clause ( (a1 (dot)) ∷ (a (con (quote is+₁) [ a $ (var "u") ])) ∷ []) t1) ∷ (clause ( (a1 (dot)) ∷ (a (con (quote is+₂) [ a $ (var "u") ])) ∷ []) t2) ∷ []) []

step2 : Term
step2 = rewrite'' x₂≡x₁' (quoteTerm fromToBool') (quoteTerm (right-inverse-of' $' var 0 [])) refl'

⨁'' : ℕ -> ℕ -> Term
⨁'' m1 m2 = With.subst (quoteTerm (⨁' 1337 2)) (lit (nat 1337)) (lit (nat m1))

genRightInv' : (List Name) -> Term
genRightInv' [] = unknown
genRightInv' (x ∷ []) = step2
genRightInv' (x ∷ x₁ ∷ cons) = with'' (Fin+' l 2) (⨁'' l 2) (matchFin+ (genRightInv' (x₁ ∷ cons)) step2)
  where
    l = (length (x₁ ∷ cons) * 2)

right-inverse-of : genTestTo RightInverseOf genTest
right-inverse-of x = unquote (genRightInv' (cons (quote Test)))

{-
from-bijection : Bijection (setoid Test) (setoid (Fin 6))
from-bijection = record {
    to = genTest
  ; bijective = record {
      injective = testInjective
    ; surjective = record {
        from = genTestTo
      ; right-inverse-of = right-inverse-of
      }
    }
  }

instance
  serializerTest : Serializer Test
  serializerTest = record {
    size = 6 ;
    from = fromTest ;
    to = toTest ;
    bijection = from-bijection
    }
-}