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

open import lib.Basics
open import lib.NType2
open import lib.types.Pi
open import lib.types.Group

{-
    The definition of G-sets.  Thanks to Daniel Grayson.
-}

module lib.types.GroupSet {i} where

  -- The right group action with respect to the group [grp].
  record GsetStructure (grp : Group i) {j} (El : Type j)
    (_ : is-set El) : Type (lmax i j) where
    constructor gset-structure
    private
      module G = Group grp
      module GS = GroupStructure G.group-struct
    field
      act : El → G.El → El
      unit-r : ∀ x → act x GS.ident == x
      assoc : ∀ x g₁ g₂ → act (act x g₁) g₂ == act x (GS.comp g₁ g₂)

  -- The definition of a G-set.  A set [El] equipped with
  -- a right group action with respect to [grp].
  record Gset (grp : Group i) j : Type (lsucc (lmax i j)) where
    constructor gset
    field
      El : Type j
      El-level : is-set El
      gset-struct : GsetStructure grp El El-level
    open GsetStructure gset-struct public
    El-is-set = El-level

  -- A helper function to establish equivalence between two G-sets.
  -- Many data are just props and this function do the coversion for them
  -- for you.  You only need to show the non-trivial parts.
  module _ {grp : Group i} {j} {El : Type j} {El-level : is-set El} where
    private
      module G = Group grp
      module GS = GroupStructure G.group-struct
    open GsetStructure
    private
      gset-structure=' : ∀ {gss₁ gss₂ : GsetStructure grp El El-level}
        → (act= : act gss₁ == act gss₂)
        → unit-r gss₁ == unit-r gss₂
          [ (λ act → ∀ x → act x GS.ident == x) ↓ act= ]
        → assoc gss₁ == assoc gss₂
          [ (λ act → ∀ x g₁ g₂ →
              act (act x g₁) g₂ == act x (GS.comp g₁ g₂)) ↓ act= ]
        → gss₁ == gss₂
      gset-structure=' {gset-structure _ _ _} {gset-structure ._ ._ ._}
        idp idp idp = idp

    gset-structure= : ∀ {gss₁ gss₂ : GsetStructure grp El El-level}
      → (∀ x g → act gss₁ x g == act gss₂ x g)
      → gss₁ == gss₂
    gset-structure= act= = gset-structure='
      (λ= λ x → λ= λ g → act= x g)
      (prop-has-all-paths-↓ (Π-level λ _ → El-level _ _))
      (prop-has-all-paths-↓
        (Π-level λ _ → Π-level λ _ → Π-level λ _ → El-level _ _))

  module _ {grp : Group i} {j : ULevel} where
    private
      module G = Group grp
      module GS = GroupStructure G.group-struct
    open Gset {grp} {j}
    private
      gset='' : ∀ {gs₁ gs₂ : Gset grp j}
        → (El= : El gs₁ == El gs₂)
        → (El-level : El-level gs₁ == El-level gs₂ [ is-set ↓ El= ])
        → gset-struct gs₁ == gset-struct gs₂
          [ uncurry (GsetStructure grp) ↓ pair= El= El-level ]
        → gs₁ == gs₂
      gset='' {gset _ _ _} {gset ._ ._ ._} idp idp idp = idp

      gset=' : ∀ {gs₁ gs₂ : Gset grp j}
        → (El= : El gs₁ == El gs₂)
        → (El-level : El-level gs₁ == El-level gs₂ [ is-set ↓ El= ])
        → (∀ {x₁} {x₂} (p : x₁ == x₂ [ idf _ ↓ El= ]) g
            → act gs₁ x₁ g == act gs₂ x₂ g [ idf _ ↓ El= ])
        → gs₁ == gs₂
      gset=' {gset _ _ _} {gset ._ ._ _} idp idp act= =
        gset='' idp idp (gset-structure= λ x g → act= idp g)

    gset= : ∀ {gs₁ gs₂ : Gset grp j}
      → (El≃ : El gs₁ ≃ El gs₂)
      → (∀ {x₁} {x₂}
          → –> El≃ x₁ == x₂
          → ∀ g → –> El≃ (act gs₁ x₁ g) == act gs₂ x₂ g)
      → gs₁ == gs₂
    gset= El≃ act= =
      gset='
        (ua El≃)
        (prop-has-all-paths-↓ is-set-is-prop)
        (λ x= g → ↓-idf-ua-in El≃ $ act= (↓-idf-ua-out El≃ x=) g)

  -- The Gset homomorphism.
  record GsetHom {grp : Group i} {j}
    (gset₁ gset₂ : Gset grp j) : Type (lmax i j) where
    constructor gset-hom
    open Gset
    field
      f : El gset₁ → El gset₂
      pres-act : ∀ g x → f (act gset₁ x g) == act gset₂ (f x) g

  private
    gset-hom=' : ∀ {grp : Group i} {j} {gset₁ gset₂ : Gset grp j}
      {gsh₁ gsh₂ : GsetHom gset₁ gset₂}
      → (f= : GsetHom.f gsh₁ == GsetHom.f gsh₂)
      → (GsetHom.pres-act gsh₁ == GsetHom.pres-act gsh₂
        [ (λ f → ∀ g x → f (Gset.act gset₁ x g) == Gset.act gset₂ (f x) g) ↓ f= ] )
      → gsh₁ == gsh₂
    gset-hom=' idp idp = idp

  gset-hom= : ∀ {grp : Group i} {j} {gset₁ gset₂ : Gset grp j}
    {gsh₁ gsh₂ : GsetHom gset₁ gset₂}
    → (∀ x → GsetHom.f gsh₁ x == GsetHom.f gsh₂ x)
    → gsh₁ == gsh₂
  gset-hom= {gset₂ = gset₂} f= =
    gset-hom='
      (λ= f=)
      (prop-has-all-paths-↓ $
        Π-level λ _ → Π-level λ _ → Gset.El-level gset₂ _ _)


{-# OPTIONS --without-K --safe #-}
module PiFracMemSem where

open import Relation.Binary.Core
open import Data.Empty
open import Function
open import Data.Nat
open import Data.Nat.Properties
open import Data.Fin as Fin using (Fin; zero; suc)
open import Data.Vec
open import Data.Vec.Relation.Unary.Any.Properties
open import Data.Vec.Any hiding (map)
open import Data.Empty using (⊥)
open import Data.Unit using (⊤; tt)
open import Data.Sum using (_⊎_; inj₁; inj₂)
open import Data.Product using (_×_; _,_; proj₁; proj₂; Σ-syntax)
open import Relation.Nullary
open import Relation.Binary.PropositionalEquality
  renaming ([_] to R[_])
open import PiFracDyn

infix  80 ∣_∣

∣_∣ : (A : 𝕌) → ℕ
∣ 𝟘 ∣ = 0
∣ 𝟙 ∣ = 1
∣ A₁ +ᵤ A₂ ∣ = ∣ A₁ ∣ + ∣ A₂ ∣
∣ A₁ ×ᵤ A₂ ∣ = ∣ A₁ ∣ * ∣ A₂ ∣
∣ 𝟙/ A ∣ = 1

Vec× : ∀ {n m} {A B : Set} → Vec A n → Vec B m → Vec (A × B) (n * m)
Vec× va vb = concat (map (λ a₁ → map (a₁ ,_) vb) va)

∈map : ∀ {ℓ₁ ℓ₂} {n} {A : Set ℓ₁} {B : Set ℓ₂} {v : Vec A n} → (f : A → B) → (a : A)
     → Any (a ≡_) v → Any (f a ≡_) (map f v)
∈map f a (here refl) = here refl
∈map f a (there a∈v) = there (∈map f a a∈v)

inVec× : ∀ {n m} {A B : Set} → (va : Vec A n) → (vb : Vec B m)
       → (a : A) (b : B)
       → Any (a ≡_) va → Any (b ≡_) vb
       → Any ((a , b) ≡_) (Vec× va vb)
inVec× (a ∷ va) vb .a b (here refl) b∈vb = ++⁺ˡ {xs = map (a ,_) vb} (∈map _ _ b∈vb)
inVec× (x ∷ va) vb a b (there a∈va) b∈vb = ++⁺ʳ (map (x ,_) vb) (inVec× va vb a b a∈va b∈vb)

any≡← : ∀ {ℓ} {A : Set ℓ} {n} {a} → (v : Vec A n) → (i : Fin n) → a ≡ lookup v i → Any (a ≡_) v
any≡← (_ ∷ _)  Fin.0F refl = here refl
any≡← (_ ∷ v) (suc i) refl = there (any≡← v i refl)

Enum : (A : 𝕌) → Vec ⟦ A ⟧ ∣ A ∣
Enum 𝟘         = []
Enum 𝟙          = tt ∷ []
Enum (A₁ +ᵤ A₂) = map inj₁ (Enum A₁) ++ map inj₂ (Enum A₂)
Enum (A₁ ×ᵤ A₂) = Vec× (Enum A₁) (Enum A₂)
Enum (𝟙/ A) = ○ ∷ []

Find : {A : 𝕌} (x : ⟦ A ⟧) → Σ[ i ∈ Fin ∣ A ∣ ] (x ≡ lookup (Enum A) i)
Find {𝟘} ()
Find {𝟙} tt = index tt∈𝟙 , lookup-index tt∈𝟙
  where
    tt∈𝟙 : Any (tt ≡_) (Enum 𝟙)
    tt∈𝟙 = here refl
Find {A₁ +ᵤ A₂} (inj₁ x) =
  let i , p₁ = Find x in
  let x∈A₁ : Any ((inj₁ x) ≡_) (Enum (A₁ +ᵤ A₂))
      x∈A₁ = ++⁺ˡ {xs = map inj₁ (Enum A₁)} (∈map inj₁ x (any≡← _ i p₁)) in
  index x∈A₁ , lookup-index x∈A₁
Find {A₁ +ᵤ A₂} (inj₂ y) =
  let j , p₂ = Find y in
  let y∈A₂ : Any ((inj₂ y) ≡_) (Enum (A₁ +ᵤ A₂))
      y∈A₂ = ++⁺ʳ (map inj₁ (Enum A₁)) (∈map inj₂ y (any≡← _ j p₂)) in
  index y∈A₂ , lookup-index y∈A₂
Find {A₁ ×ᵤ A₂} (x , y) =
  let i , p₁ = Find x
      j , p₂ = Find y in
  let xy∈A₁×A₂ : Any ((x , y) ≡_) (Enum (A₁ ×ᵤ A₂))
      xy∈A₁×A₂ = inVec× (Enum A₁) (Enum A₂) x y (any≡← (Enum A₁) i p₁) (any≡← (Enum A₂) j p₂) in
  index xy∈A₁×A₂ , lookup-index xy∈A₁×A₂
Find {𝟙/ A} ○ = index ○∈𝟙/A , lookup-index ○∈𝟙/A
  where
    ○∈𝟙/A : Any (○ ≡_) (Enum (𝟙/ A))
    ○∈𝟙/A = here refl

Find' : {A : 𝕌} (x : ⟦ A ⟧) → Fin ∣ A ∣
Find' = proj₁ ∘ Find

data State (A : 𝕌) : Set where
  ⟪_[_]⟫ : Vec ⟦ A ⟧ ∣ A ∣ → Fin ∣ A ∣ → State A

resolve : {A : 𝕌} → State A → ⟦ A ⟧
resolve ⟪ v [ i ]⟫ = lookup v i

st : {A B : 𝕌} → ⟦ A ⟧ → (c : A ↔ B) → Σ[ C ∈ 𝕌 ] (C ↔ B × State C)
st (inj₂ y) (unite₊l {t})                   = _ , id↔ , ⟪ Enum t [ Find' y ]⟫
st a (uniti₊l {t})                          = _ , id↔ , ⟪ (Enum (𝟘 +ᵤ t)) [ Find' a ]⟫
st (inj₁ x) (unite₊r {t})                   = _ , id↔ , ⟪ Enum t [ Find' x ]⟫
st a (uniti₊r {t})                          = _ , id↔ , ⟪ (Enum (t +ᵤ 𝟘)) [ Find' {t +ᵤ 𝟘} (inj₁ a) ]⟫
st (inj₁ x) (swap₊ {t₁} {t₂})               = _ , id↔ , ⟪ Enum _ [ Find' {t₂ +ᵤ t₁} (inj₂ x) ]⟫
st (inj₂ y) (swap₊ {t₁} {t₂})               = _ , id↔ , ⟪ Enum _ [ Find' {t₂ +ᵤ t₁} (inj₁ y) ]⟫
st (inj₁ x) (assocl₊ {t₁} {t₂} {t₃})        = _ , id↔ , ⟪ Enum _ [ Find' {(t₁ +ᵤ t₂) +ᵤ t₃} (inj₁ (inj₁ x)) ]⟫
st (inj₂ (inj₁ x)) (assocl₊ {t₁} {t₂} {t₃}) = _ , id↔ , ⟪ Enum _ [ Find' {(t₁ +ᵤ t₂) +ᵤ t₃} (inj₁ (inj₂ x)) ]⟫
st (inj₂ (inj₂ y)) (assocl₊ {t₁} {t₂} {t₃}) = _ , id↔ , ⟪ Enum _ [ Find' {(t₁ +ᵤ t₂) +ᵤ t₃} (inj₂ y) ]⟫
st (inj₁ (inj₁ x)) (assocr₊ {t₁} {t₂} {t₃}) = _ , id↔ , ⟪ Enum _ [ Find' {t₁ +ᵤ t₂ +ᵤ t₃} (inj₁ x) ]⟫
st (inj₁ (inj₂ y)) (assocr₊ {t₁} {t₂} {t₃}) = _ , id↔ , ⟪ Enum _ [ Find' {t₁ +ᵤ t₂ +ᵤ t₃} (inj₂ (inj₁ y)) ]⟫
st (inj₂ y) (assocr₊ {t₁} {t₂} {t₃})        = _ , id↔ , ⟪ Enum _ [ Find' {t₁ +ᵤ t₂ +ᵤ t₃} (inj₂ (inj₂ y)) ]⟫
st (tt , y) unite⋆l                         = _ , id↔ , ⟪ Enum _ [ Find' y ]⟫
st a uniti⋆l                                = _ , id↔ , ⟪ Enum _ [ Find' (tt , a) ]⟫
st (x , tt) unite⋆r                         = _ , id↔ , ⟪ Enum _ [ Find' x ]⟫
st a uniti⋆r                                = _ , id↔ , ⟪ Enum _ [ Find' (a , tt) ]⟫
st (x , y) swap⋆                            = _ , id↔ , ⟪ Enum _ [ Find' (y , x) ]⟫
st (x , y , z) assocl⋆                      = _ , id↔ , ⟪ Enum _ [ Find' ((x , y) , z) ]⟫
st ((x , y) , z) assocr⋆                    = _ , id↔ , ⟪ Enum _ [ Find' (x , y , z) ]⟫
st (inj₁ x , y) (dist {t₁} {t₂} {t₃})       = _ , id↔ , ⟪ Enum _ [ Find' {t₁ ×ᵤ t₃ +ᵤ t₂ ×ᵤ t₃} (inj₁ (x , y)) ]⟫
st (inj₂ x , y) (dist {t₁} {t₂} {t₃})       = _ , id↔ , ⟪ Enum _ [ Find' {t₁ ×ᵤ t₃ +ᵤ t₂ ×ᵤ t₃} (inj₂ (x , y)) ]⟫
st (inj₁ (x , y)) (factor {t₁} {t₂} {t₃})   = _ , id↔ , ⟪ Enum _ [ Find' {(t₁ +ᵤ t₂) ×ᵤ t₃} (inj₁ x , y) ]⟫
st (inj₂ (y , z)) (factor {t₁} {t₂} {t₃})   = _ , id↔ , ⟪ Enum _ [ Find' {(t₁ +ᵤ t₂) ×ᵤ t₃} (inj₂ y , z) ]⟫
st (x , inj₁ y) (distl {t₁} {t₂} {t₃})      = _ , id↔ , ⟪ Enum _ [ Find' {(t₁ ×ᵤ t₂) +ᵤ (t₁ ×ᵤ t₃)} (inj₁ (x , y)) ]⟫
st (x , inj₂ y) (distl {t₁} {t₂} {t₃})      = _ , id↔ , ⟪ Enum _ [ Find' {(t₁ ×ᵤ t₂) +ᵤ (t₁ ×ᵤ t₃)} (inj₂ (x , y)) ]⟫
st (inj₁ (x , y)) (factorl {t₁} {t₂} {t₃})  = _ , id↔ , ⟪ Enum _ [ Find' {t₁ ×ᵤ (t₂ +ᵤ t₃)} (x , inj₁ y) ]⟫
st (inj₂ (x , z)) (factorl {t₁} {t₂} {t₃})  = _ , id↔ , ⟪ Enum _ [ Find' {t₁ ×ᵤ (t₂ +ᵤ t₃)} (x , inj₂ z) ]⟫
st tt (η {t} v)                           = _ , id↔ , ⟪ Enum (t ×ᵤ (𝟙/ v)) [ Find' {t ×ᵤ (𝟙/ v)} (v , ○) ]⟫
st (x , ○) (ε {t} v) with 𝕌dec t v x
st (x , ○) (ε {t} v) | yes _ = _ , id↔ , ⟪ Enum _ [ Find' tt ]⟫
st (x , ○) (ε {t} v) | no  _ = _ , (ε {t} v) , ⟪ Enum (t ×ᵤ (𝟙/ v)) [ Find' {t ×ᵤ (𝟙/ v)} (x , ○) ]⟫
st a id↔                                    = _ , id↔ , ⟪ Enum _ [ Find' a ]⟫
st a (id↔ ⊚ c)                              = _ , c , ⟪ Enum _ [ Find' a ]⟫
st a (c₁ ⊚ c₂)                              = let _ , c , st' = st a c₁ in
                                              _ , c ⊚ c₂ , st'
st (inj₁ x) (_⊕_ {t₁} {t₂} {_} {t₄} id↔ c₂) = _ , id↔ , ⟪ Enum _ [ Find' {_ +ᵤ t₄} (inj₁ x) ]⟫
st (inj₁ x) (_⊕_ {t₁} {t₂} c₁ c₂)           = let _ , c , st' = st x c₁ in
                                              _ , c ⊕ c₂ , ⟪ Enum _ [ Find' {_ +ᵤ t₂} (inj₁ $ resolve st') ]⟫
st (inj₂ y) (_⊕_ {t₁} {t₂} {t₃} {_} c₁ id↔) = _ , id↔ , ⟪ Enum _ [ Find' {t₃ +ᵤ _} (inj₂ y) ]⟫
st (inj₂ y) (_⊕_ {t₁} c₁ c₂)                = let _ , c , st' = st y c₂ in
                                              _ , c₁ ⊕ c , ⟪ Enum _ [ Find' {t₁ +ᵤ _} (inj₂ $ resolve st') ]⟫
st (x , y) (id↔ ⊗ id↔)                      = _ , id↔ , ⟪ Enum _ [ Find' (x , y) ]⟫
st (x , y) (id↔ ⊗ c₂)                       = let _ , c , st' = st y c₂ in
                                               _ , id↔ ⊗ c , ⟪ Enum _ [ Find' (x , resolve st') ]⟫
st (x , y) (c₁ ⊗ c₂)                        = let _ , c , st' = st x c₁ in
                                              _ , c ⊗ c₂ , ⟪ Enum _ [ Find' (resolve st' , y) ]⟫

step : {A B : 𝕌} (c : A ↔ B) → State A → Σ[ C ∈ 𝕌 ] (C ↔ B × State C)
step c ⟪ v [ i ]⟫ = st (lookup v i) c

data State' : Set where
  ⟪_∥_[_]⟫ : {A B : 𝕌} → A ↔ B → Vec ⟦ A ⟧ ∣ A ∣ → Fin ∣ A ∣ → State'

step' : State' → State'
step' ⟪ c ∥ v [ i ]⟫ with step c ⟪ v [ i ]⟫
step' ⟪ c ∥ v [ i ]⟫ | _ , c' , ⟪ v' [ i' ]⟫ = ⟪ c' ∥ v' [ i' ]⟫

run : (n : ℕ) → State' → Vec State' (suc n)
run 0 st = [ st ]
run (suc n) st with run n st
... | sts with last sts
... | ⟪ cx ∥ vx [ ix ]⟫ rewrite +-comm 1 (suc n) = sts ++ [ step' ⟪ cx ∥ vx [ ix ]⟫ ]

ex₁ : Vec State' 33
ex₁ = run 32 ⟪ id' ∥ Enum 𝟚 [ Fin.fromℕ 1 ]⟫


module Common.Context where

open import Common public

-- Contexts.

data Cx (U : Set) : Set where
  ∅   : Cx U
  _,_ : Cx U → U → Cx U

-- Vector contexts.

data VCx (U : Set) : ℕ → Set where
  ∅   : VCx U zero
  _,_ : ∀ {n} → VCx U n → U → VCx U (suc n)

-- Inversion principles for contexts.

module _ {U : Set} where
  inv,₁ : ∀ {Γ Γ′ : Cx U} {A A′ : U} → (Γ Cx., A) ≡ (Γ′ , A′) → Γ ≡ Γ′
  inv,₁ refl = refl

  inv,₂ : ∀ {Γ Γ′ : Cx U} {A A′ : U} → (Γ Cx., A) ≡ (Γ′ , A′) → A ≡ A′
  inv,₂ refl = refl

-- Decidable equality for contexts.

module ContextEquality {U : Set} (_≟ᵁ_ : (A A′ : U) → Dec (A ≡ A′)) where
  _≟ᵀ⋆_ : (Γ Γ′ : Cx U) → Dec (Γ ≡ Γ′)
  ∅       ≟ᵀ⋆ ∅         = yes refl
  ∅       ≟ᵀ⋆ (Γ′ , A′) = no λ ()
  (Γ , A) ≟ᵀ⋆ ∅         = no λ ()
  (Γ , A) ≟ᵀ⋆ (Γ′ , A′) with Γ ≟ᵀ⋆ Γ′ | A ≟ᵁ A′
  (Γ , A) ≟ᵀ⋆ (.Γ , .A) | yes refl | yes refl = yes refl
  (Γ , A) ≟ᵀ⋆ (Γ′ , A′) | no  Γ≢Γ′ | _        = no (Γ≢Γ′ ∘ inv,₁)
  (Γ , A) ≟ᵀ⋆ (Γ′ , A′) | _        | no  A≢A′ = no (A≢A′ ∘ inv,₂)

-- Context membership, or nameless typed de Bruijn indices.

module _ {U : Set} where
  infix 3 _∈_
  data _∈_ (A : U) : Cx U → Set where
    instance
      top : ∀ {Γ} → A ∈ Γ , A
      pop : ∀ {B Γ} → A ∈ Γ → A ∈ Γ , B

  pop² : ∀ {A B C Γ} → A ∈ Γ → A ∈ Γ , B , C
  pop² = pop ∘ pop

  i₀ : ∀ {A Γ} → A ∈ Γ , A
  i₀ = top

  i₁ : ∀ {A B Γ} → A ∈ Γ , A , B
  i₁ = pop top

  i₂ : ∀ {A B C Γ} → A ∈ Γ , A , B , C
  i₂ = pop² top

  [_]ⁱ : ∀ {A Γ} → A ∈ Γ → ℕ
  [ top ]ⁱ   = zero
  [ pop i ]ⁱ = suc [ i ]ⁱ

-- Context inclusion, or order-preserving embedding.

module _ {U : Set} where
  infix 3 _⊆_
  data _⊆_ : Cx U → Cx U → Set where
    instance
      done : ∅ ⊆ ∅
      skip : ∀ {A Γ Γ′} → Γ ⊆ Γ′ → Γ ⊆ Γ′ , A
      keep : ∀ {A Γ Γ′} → Γ ⊆ Γ′ → Γ , A ⊆ Γ′ , A

  skip² : ∀ {A B Γ Γ′} → Γ ⊆ Γ′ → Γ ⊆ Γ′ , B , A
  skip² = skip ∘ skip

  keep² : ∀ {A B Γ Γ′} → Γ ⊆ Γ′ → Γ , B , A ⊆ Γ′ , B , A
  keep² = keep ∘ keep

  instance
    refl⊆ : ∀ {Γ} → Γ ⊆ Γ
    refl⊆ {∅}     = done
    refl⊆ {Γ , A} = keep refl⊆

  trans⊆ : ∀ {Γ Γ′ Γ″} → Γ ⊆ Γ′ → Γ′ ⊆ Γ″ → Γ ⊆ Γ″
  trans⊆ η        done      = η
  trans⊆ η        (skip η′) = skip (trans⊆ η η′)
  trans⊆ (skip η) (keep η′) = skip (trans⊆ η η′)
  trans⊆ (keep η) (keep η′) = keep (trans⊆ η η′)

  unskip⊆ : ∀ {A Γ Γ′} → Γ , A ⊆ Γ′ → Γ ⊆ Γ′
  unskip⊆ (skip η) = skip (unskip⊆ η)
  unskip⊆ (keep η) = skip η

  unkeep⊆ : ∀ {A Γ Γ′} → Γ , A ⊆ Γ′ , A → Γ ⊆ Γ′
  unkeep⊆ (skip η) = unskip⊆ η
  unkeep⊆ (keep η) = η

  weak⊆ : ∀ {A Γ} → Γ ⊆ Γ , A
  weak⊆ = skip refl⊆

  weak²⊆ : ∀ {A B Γ} → Γ ⊆ Γ , A , B
  weak²⊆ = skip² refl⊆

  bot⊆ : ∀ {Γ} → ∅ ⊆ Γ
  bot⊆ {∅}     = done
  bot⊆ {Γ , A} = skip bot⊆

-- Monotonicity of context membership with respect to context inclusion.

module _ {U : Set} where
  mono∈ : ∀ {A : U} {Γ Γ′} → Γ ⊆ Γ′ → A ∈ Γ → A ∈ Γ′
  mono∈ done     ()
  mono∈ (skip η) i       = pop (mono∈ η i)
  mono∈ (keep η) top     = top
  mono∈ (keep η) (pop i) = pop (mono∈ η i)

  reflmono∈ : ∀ {A Γ} → (i : A ∈ Γ) → i ≡ mono∈ refl⊆ i
  reflmono∈ top     = refl
  reflmono∈ (pop i) = cong pop (reflmono∈ i)

  transmono∈ : ∀ {A Γ Γ′ Γ″} → (η : Γ ⊆ Γ′) (η′ : Γ′ ⊆ Γ″) (i : A ∈ Γ)
               → mono∈ η′ (mono∈ η i) ≡ mono∈ (trans⊆ η η′) i
  transmono∈ done     η′        ()
  transmono∈ η        (skip η′) i       = cong pop (transmono∈ η η′ i)
  transmono∈ (skip η) (keep η′) i       = cong pop (transmono∈ η η′ i)
  transmono∈ (keep η) (keep η′) top     = refl
  transmono∈ (keep η) (keep η′) (pop i) = cong pop (transmono∈ η η′ i)

-- Concatenation of contexts.

module _ {U : Set} where
  _⧺_ : Cx U → Cx U → Cx U
  Γ ⧺ ∅        = Γ
  Γ ⧺ (Γ′ , A) = (Γ ⧺ Γ′) , A

  id⧺₁ : ∀ {Γ} → Γ ⧺ ∅ ≡ Γ
  id⧺₁ = refl

  id⧺₂ : ∀ {Γ} → ∅ ⧺ Γ ≡ Γ
  id⧺₂ {∅}     = refl
  id⧺₂ {Γ , A} = cong² _,_ id⧺₂ refl

  weak⊆⧺₁ : ∀ {Γ} Γ′ → Γ ⊆ Γ ⧺ Γ′
  weak⊆⧺₁ ∅        = refl⊆
  weak⊆⧺₁ (Γ′ , A) = skip (weak⊆⧺₁ Γ′)

  weak⊆⧺₂ : ∀ {Γ Γ′} → Γ′ ⊆ Γ ⧺ Γ′
  weak⊆⧺₂ {Γ} {∅}      = bot⊆
  weak⊆⧺₂ {Γ} {Γ′ , A} = keep weak⊆⧺₂

-- Thinning of contexts.

module _ {U : Set} where
  _∖_ : ∀ {A} → (Γ : Cx U) → A ∈ Γ → Cx U
  ∅       ∖ ()
  (Γ , A) ∖ top   = Γ
  (Γ , B) ∖ pop i = (Γ ∖ i) , B

  thin⊆ : ∀ {A Γ} → (i : A ∈ Γ) → Γ ∖ i ⊆ Γ
  thin⊆ top     = weak⊆
  thin⊆ (pop i) = keep (thin⊆ i)

-- Decidable equality of context membership.

module _ {U : Set} where
  data _=∈_ {A : U} {Γ} (i : A ∈ Γ) : ∀ {B} → B ∈ Γ → Set where
    same : i =∈ i
    diff : ∀ {B} → (j : B ∈ Γ ∖ i) → i =∈ mono∈ (thin⊆ i) j

  _≟∈_ : ∀ {A B : U} {Γ} → (i : A ∈ Γ) (j : B ∈ Γ) → i =∈ j
  top   ≟∈ top    = same
  top   ≟∈ pop j  rewrite reflmono∈ j = diff j
  pop i ≟∈ top    = diff top
  pop i ≟∈ pop j  with i ≟∈ j
  pop i ≟∈ pop .i | same   = same
  pop i ≟∈ pop ._ | diff j = diff (pop j)


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

{- Remember to keep CodeAP.agda in sync. -}

open import HoTT
import homotopy.RelativelyConstantToSetExtendsViaSurjection as SurjExt

module homotopy.vankampen.CodeBP {i j k l}
  (span : Span {i} {j} {k})
  {D : Type l} (h : D → Span.C span) (h-is-surj : is-surj h) where

  open Span span

  data precodeBB (b₀ : B) : B → Type (lmax (lmax (lmax i j) k) l)
  data precodeBA (b₀ : B) (a₁ : A) : Type (lmax (lmax (lmax i j) k) l)

  data precodeBB b₀ where
    pc-b : ∀ {b₁} (pB : b₀ =₀ b₁) → precodeBB b₀ b₁
    pc-bab : ∀ d {b₁} (pc : precodeBA b₀ (f (h d))) (pB : g (h d) =₀ b₁) → precodeBB b₀ b₁

  infix 66 pc-b
  syntax pc-b p = ⟧b p
  infixl 65 pc-bab
  syntax pc-bab d pcBA pB = pcBA ba⟦ d ⟧b pB

  data precodeBA b₀ a₁ where
    pc-bba : ∀ d (pc : precodeBB b₀ (g (h d))) (pA : f (h d) =₀ a₁) → precodeBA b₀ a₁

  infixl 65 pc-bba
  syntax pc-bba d pcBB pA = pcBB bb⟦ d ⟧a pA

  data precodeBB-rel {b₀ : B} : {b₁ : B}
    → precodeBB b₀ b₁ → precodeBB b₀ b₁ → Type (lmax (lmax (lmax i j) k) l)
  data precodeBA-rel {b₀ : B} : {a₁ : A}
    → precodeBA b₀ a₁ → precodeBA b₀ a₁ → Type (lmax (lmax (lmax i j) k) l)
  data precodeBB-rel {b₀} where
    pcBBr-idp₀-idp₀ : ∀ {d} pcBB → precodeBB-rel (pcBB bb⟦ d ⟧a idp₀ ba⟦ d ⟧b idp₀) pcBB
    pcBBr-switch : ∀ {d₀ d₁ : D} pcBB (pC : h d₀ =₀ h d₁)
      → precodeBB-rel (pcBB bb⟦ d₀ ⟧a ap₀ f pC ba⟦ d₁ ⟧b idp₀) (pcBB bb⟦ d₀ ⟧a idp₀ ba⟦ d₀ ⟧b ap₀ g pC)
    pcBBr-cong : ∀ {d b₁ pcBA₁ pcBA₂} (r : precodeBA-rel pcBA₁ pcBA₂) (pB : g (h d) =₀ b₁)
      → precodeBB-rel (pcBA₁ ba⟦ d ⟧b pB) (pcBA₂ ba⟦ d ⟧b pB)
  data precodeBA-rel {b₀} where
    pcBAr-idp₀-idp₀ : ∀ {d} pcBA → precodeBA-rel (pcBA ba⟦ d ⟧b idp₀ bb⟦ d ⟧a idp₀) pcBA
    pcBAr-cong : ∀ {d a₁ pcBB₁ pcBB₂} (r : precodeBB-rel pcBB₁ pcBB₂) (pA : f (h d) =₀ a₁)
      → precodeBA-rel (pcBB₁ bb⟦ d ⟧a pA) (pcBB₂ bb⟦ d ⟧a pA)

  codeBB : B → B → Type (lmax (lmax (lmax i j) k) l)
  codeBB b₀ b₁ = SetQuot (precodeBB-rel {b₀} {b₁})

  codeBA : B → A → Type (lmax (lmax (lmax i j) k) l)
  codeBA b₀ a₁ = SetQuot (precodeBA-rel {b₀} {a₁})

  c-bba : ∀ {a₀} d {a₁} (pc : codeBB a₀ (g (h d))) (pA : f (h d) =₀ a₁) → codeBA a₀ a₁
  c-bba d {a₁} c pA = SetQuot-rec SetQuot-is-set
    (λ pc → q[ pc-bba d pc pA ])
    (λ r → quot-rel $ pcBAr-cong r pA) c

  c-bab : ∀ {a₀} d {b₁} (pc : codeBA a₀ (f (h d))) (pB : g (h d) =₀ b₁) → codeBB a₀ b₁
  c-bab d {a₁} c pB = SetQuot-rec SetQuot-is-set
    (λ pc → q[ pc-bab d pc pB ])
    (λ r → quot-rel $ pcBBr-cong r pB) c

-- codeBP

  abstract
    pcBB-idp₀-idp₀-head : ∀ {d₀ b} (pB : g (h d₀) =₀ b)
      → q[ ⟧b idp₀ bb⟦ d₀ ⟧a idp₀ ba⟦ d₀ ⟧b pB ] == q[ ⟧b pB ] :> codeBB _ b
    pcBB-idp₀-idp₀-head {d₀} = Trunc-elim (λ _ → =-preserves-set SetQuot-is-set) lemma where
      lemma : ∀ {b} (pB : g (h d₀) == b)
        → q[ ⟧b idp₀ bb⟦ d₀ ⟧a idp₀ ba⟦ d₀ ⟧b [ pB ] ] == q[ ⟧b [ pB ] ] :> codeBB _ b
      lemma idp = quot-rel $ pcBBr-idp₀-idp₀ (⟧b idp₀)

  pcBA-prepend : ∀ {b₀} d₁ {b₂} → b₀ =₀ g (h d₁) → precodeBA (g (h d₁)) b₂ → precodeBA b₀ b₂
  pcBB-prepend : ∀ {b₀} d₁ {a₂} → b₀ =₀ g (h d₁) → precodeBB (g (h d₁)) a₂ → precodeBB b₀ a₂
  pcBA-prepend d₁ pB (pc-bba d pc pA) = pc-bba d (pcBB-prepend d₁ pB pc) pA
  pcBB-prepend d₁ pB (pc-b pB₁) = pc-bab d₁ (pc-bba d₁ (pc-b pB) idp₀) pB₁
  pcBB-prepend d₁ pB (pc-bab d pc pB₁) = pc-bab d (pcBA-prepend d₁ pB pc) pB₁

  abstract
    pcBA-prepend-idp₀ : ∀ {d₀ b₁} (pcBA : precodeBA (g (h d₀)) b₁)
      → q[ pcBA-prepend d₀ idp₀ pcBA ] == q[ pcBA ] :> codeBA (g (h d₀)) b₁
    pcBB-prepend-idp₀ : ∀ {d₀ a₁} (pcBB : precodeBB (g (h d₀)) a₁)
      → q[ pcBB-prepend d₀ idp₀ pcBB ] == q[ pcBB ] :> codeBB (g (h d₀)) a₁
    pcBA-prepend-idp₀ (pc-bba d pc pB) = pcBB-prepend-idp₀ pc |in-ctx λ c → c-bba d c pB
    pcBB-prepend-idp₀ (pc-b pB) = pcBB-idp₀-idp₀-head pB
    pcBB-prepend-idp₀ (pc-bab d pc pB) = pcBA-prepend-idp₀ pc |in-ctx λ c → c-bab d c pB

    transp-cBA-l : ∀ d {b₀ a₁} (p : g (h d) == b₀) (pcBA : precodeBA (g (h d)) a₁)
      → transport (λ x → codeBA x a₁) p q[ pcBA ] == q[ pcBA-prepend d [ ! p ] pcBA ]
    transp-cBA-l d idp pcBA = ! $ pcBA-prepend-idp₀ pcBA

    transp-cBB-l : ∀ d {b₀ b₁} (p : g (h d) == b₀) (pcBB : precodeBB (g (h d)) b₁)
      → transport (λ x → codeBB x b₁) p q[ pcBB ] == q[ pcBB-prepend d [ ! p ] pcBB ]
    transp-cBB-l d idp pcBB = ! $ pcBB-prepend-idp₀ pcBB

    transp-cBA-r : ∀ d {b₀ a₁} (p : f (h d) == a₁) (pcBA : precodeBA b₀ (f (h d)))
      → transport (λ x → codeBA b₀ x) p q[ pcBA ] == q[ pcBA ba⟦ d ⟧b idp₀ bb⟦ d ⟧a [ p ] ]
    transp-cBA-r d idp pcBA = ! $ quot-rel $ pcBAr-idp₀-idp₀ pcBA

    transp-cBB-r : ∀ d {b₀ b₁} (p : g (h d) == b₁) (pcBB : precodeBB b₀ (g (h d)))
      → transport (λ x → codeBB b₀ x) p q[ pcBB ] == q[ pcBB bb⟦ d ⟧a idp₀ ba⟦ d ⟧b [ p ] ]
    transp-cBB-r d idp pcBB = ! $ quot-rel $ pcBBr-idp₀-idp₀ pcBB

  module CodeBAEquivCodeBB (b₀ : B) where

    eqv-on-image : (d : D) → codeBA b₀ (f (h d)) ≃ codeBB b₀ (g (h d))
    eqv-on-image d = equiv to from to-from from-to where
      to = λ c → c-bab d c idp₀
      from = λ c → c-bba d c idp₀

      abstract
        from-to : ∀ cBA → from (to cBA) == cBA
        from-to = SetQuot-elim
          (λ _ → =-preserves-set SetQuot-is-set)
          (λ pcBA → quot-rel (pcBAr-idp₀-idp₀ pcBA))
          (λ _ → prop-has-all-paths-↓ (SetQuot-is-set _ _))

        to-from : ∀ cBB → to (from cBB) == cBB
        to-from = SetQuot-elim
          (λ _ → =-preserves-set SetQuot-is-set)
          (λ pcBB → quot-rel (pcBBr-idp₀-idp₀ pcBB))
          (λ _ → prop-has-all-paths-↓ (SetQuot-is-set _ _))

    abstract
      eqv-is-const : ∀ d₁ d₂ (p : h d₁ == h d₂)
        → eqv-on-image d₁ == eqv-on-image d₂
        [ (λ c → codeBA b₀ (f c) ≃ codeBB b₀ (g c)) ↓ p ]
      eqv-is-const d₁ d₂ p = ↓-Subtype-in (λ d → is-equiv-prop) $
        ↓-→-from-transp $ λ= $
          SetQuot-elim (λ _ → =-preserves-set SetQuot-is-set)
            (λ pcBA →
              transport (λ c → codeBB b₀ (g c)) p q[ pcBA ba⟦ d₁ ⟧b idp₀ ]
                =⟨ ap-∘ (codeBB b₀) g p |in-ctx (λ p → coe p q[ pcBA ba⟦ d₁ ⟧b idp₀ ]) ⟩
              transport (codeBB b₀) (ap g p) q[ pcBA ba⟦ d₁ ⟧b idp₀ ]
                =⟨ transp-cBB-r d₁ (ap g p) (pcBA ba⟦ d₁ ⟧b idp₀) ⟩
              q[ pcBA ba⟦ d₁ ⟧b idp₀ bb⟦ d₁ ⟧a idp₀ ba⟦ d₁ ⟧b [ ap g p ] ]
                =⟨ ! $ quot-rel $ pcBBr-switch (pcBA ba⟦ d₁ ⟧b idp₀) [ p ] ⟩
              q[ pcBA ba⟦ d₁ ⟧b idp₀ bb⟦ d₁ ⟧a [ ap f p ] ba⟦ d₂ ⟧b idp₀ ]
                =⟨ ! $ transp-cBA-r d₁ (ap f p) pcBA |in-ctx (λ c → c-bab d₂ c idp₀) ⟩
              c-bab d₂ (transport (codeBA b₀) (ap f p) q[ pcBA ]) idp₀
                =⟨ ∘-ap (codeBA b₀) f p |in-ctx (λ p → coe p q[ pcBA ]) |in-ctx (λ c → c-bab d₂ c idp₀) ⟩
              c-bab d₂ (transport (λ c → codeBA b₀ (f c)) p q[ pcBA ]) idp₀
                =∎)
            (λ _ → prop-has-all-paths-↓ (SetQuot-is-set _ _))

    module SE = SurjExt
      (λ c → ≃-is-set SetQuot-is-set SetQuot-is-set)
      h h-is-surj
      eqv-on-image
      eqv-is-const

    abstract
      eqv : ∀ c → codeBA b₀ (f c) ≃ codeBB b₀ (g c)
      eqv = SE.ext

      eqv-β : ∀ d → eqv (h d) == eqv-on-image d
      eqv-β = SE.β

  module CodeBP (b₀ : B) = PushoutRec (codeBA b₀) (codeBB b₀)
    (ua ∘ CodeBAEquivCodeBB.eqv b₀)

  codeBP : B → Pushout span → Type (lmax (lmax (lmax i j) k) l)
  codeBP = CodeBP.f

  abstract
    codeBP-level : ∀ {a₀ p₁} → is-set (codeBP a₀ p₁)
    codeBP-level {a₀} {p₁} = Pushout-elim
      {P = λ p₁ → is-set (codeBP a₀ p₁)}
      (λ a₁ → SetQuot-is-set)
      (λ b₁ → SetQuot-is-set)
      (λ c₁ → prop-has-all-paths-↓ is-set-is-prop)
      p₁
  codeBP-is-set = codeBP-level

  abstract
    transp-cBP-glue : ∀ {b₀} d₁ (pcBA : precodeBA b₀ (f (h d₁)))
      → transport (codeBP b₀) (glue (h d₁)) q[ pcBA ] == q[ pcBA ba⟦ d₁ ⟧b idp₀ ]
    transp-cBP-glue {b₀} d₁ pcBA =
      transport (codeBP b₀) (glue (h d₁)) q[ pcBA ]
        =⟨ ap (λ e → coe e q[ pcBA ]) (CodeBP.glue-β b₀ (h d₁) ∙ ap ua (CodeBAEquivCodeBB.eqv-β b₀ d₁)) ⟩
      coe (ua (CodeBAEquivCodeBB.eqv-on-image b₀ d₁)) q[ pcBA ]
        =⟨ coe-β (CodeBAEquivCodeBB.eqv-on-image b₀ d₁) q[ pcBA ] ⟩
      q[ pcBA ba⟦ d₁ ⟧b idp₀ ]
        =∎

    transp-cBP-!glue : ∀ {b₀} d₁ (pcBB : precodeBB b₀ (g (h d₁)))
      → transport (codeBP b₀) (! (glue (h d₁))) q[ pcBB ] == q[ pcBB bb⟦ d₁ ⟧a idp₀ ]
    transp-cBP-!glue {b₀} d₁ pcBB =
      transport (codeBP b₀) (! (glue (h d₁))) q[ pcBB ]
        =⟨ ap (λ e → coe e q[ pcBB ]) (ap-! (codeBP b₀) (glue (h d₁)))
         ∙ coe-! (ap (codeBP b₀) (glue (h d₁))) q[ pcBB ] ⟩
      transport! (codeBP b₀) (glue (h d₁)) q[ pcBB ]
        =⟨ ap (λ e → coe! e q[ pcBB ]) (CodeBP.glue-β b₀ (h d₁) ∙ ap ua (CodeBAEquivCodeBB.eqv-β b₀ d₁)) ⟩
      coe! (ua (CodeBAEquivCodeBB.eqv-on-image b₀ d₁)) q[ pcBB ]
        =⟨ coe!-β (CodeBAEquivCodeBB.eqv-on-image b₀ d₁) q[ pcBB ] ⟩
      q[ pcBB bb⟦ d₁ ⟧a idp₀ ]
        =∎

  -- code to path
  pcBA-to-path : ∀ {b₀ a₁} → precodeBA b₀ a₁ → right b₀ =₀ left a₁ :> Pushout span
  pcBB-to-path : ∀ {b₀ b₁} → precodeBB b₀ b₁ → right b₀ =₀ right b₁ :> Pushout span
  pcBA-to-path (pc-bba d pc pA) = pcBB-to-path pc ∙₀' !₀ [ glue (h d) ] ∙₀' ap₀ left pA
  pcBB-to-path (pc-b pB) = ap₀ right pB
  pcBB-to-path (pc-bab d pc pB) = pcBA-to-path pc ∙₀' [ glue (h d) ] ∙₀' ap₀ right pB

  abstract
    pcBA-to-path-rel : ∀ {b₀ a₁} {pcBA₀ pcBA₁ : precodeBA b₀ a₁}
      → precodeBA-rel pcBA₀ pcBA₁ → pcBA-to-path pcBA₀ == pcBA-to-path pcBA₁
    pcBB-to-path-rel : ∀ {b₀ b₁} {pcBB₀ pcBB₁ : precodeBB b₀ b₁}
      → precodeBB-rel pcBB₀ pcBB₁ → pcBB-to-path pcBB₀ == pcBB-to-path pcBB₁
    pcBA-to-path-rel (pcBAr-idp₀-idp₀ pcBA) =
        ∙₀'-assoc (pcBA-to-path pcBA) [ glue (h _) ] [ ! (glue (h _)) ]
      ∙ ap (λ p → pcBA-to-path pcBA ∙₀' [ p ]) (!-inv'-r (glue (h _)))
      ∙ ∙₀'-unit-r (pcBA-to-path pcBA)
    pcBA-to-path-rel (pcBAr-cong pcBB pA) = pcBB-to-path-rel pcBB |in-ctx _∙₀' !₀ [ glue (h _) ] ∙₀' ap₀ left pA
    pcBB-to-path-rel (pcBBr-idp₀-idp₀ pcBB) =
        ∙₀'-assoc (pcBB-to-path pcBB) [ ! (glue (h _)) ] [ glue (h _) ]
      ∙ ap (λ p → pcBB-to-path pcBB ∙₀' [ p ]) (!-inv'-l (glue (h _)))
      ∙ ∙₀'-unit-r (pcBB-to-path pcBB)
    pcBB-to-path-rel (pcBBr-switch pcBB pC) =
        ap (_∙₀' [ glue (h _) ]) (! (∙₀'-assoc (pcBB-to-path pcBB) [ ! (glue (h _)) ] (ap₀ left (ap₀ f pC))))
      ∙ ∙₀'-assoc (pcBB-to-path pcBB ∙₀' [ ! (glue (h _)) ]) (ap₀ left (ap₀ f pC)) [ glue (h _) ]
      ∙ ap ((pcBB-to-path pcBB ∙₀' [ ! (glue (h _)) ]) ∙₀'_) (natural₀ pC)
      where
        natural : ∀ {c₀ c₁} (p : c₀ == c₁)
          → (ap left (ap f p) ∙' glue c₁) == (glue c₀ ∙' ap right (ap g p))
          :> (left (f c₀) == right (g c₁) :> Pushout span)
        natural idp = ∙'-unit-l (glue _)

        natural₀ : ∀ {c₀ c₁} (p : c₀ =₀ c₁)
          → (ap₀ left (ap₀ f p) ∙₀' [ glue c₁ ]) == ([ glue c₀ ] ∙₀' ap₀ right (ap₀ g p))
          :> (left (f c₀) =₀ right (g c₁) :> Pushout span)
        natural₀ = Trunc-elim (λ _ → =-preserves-set Trunc-level) (ap [_] ∘ natural)
    pcBB-to-path-rel (pcBBr-cong pcBA pB) = pcBA-to-path-rel pcBA |in-ctx _∙₀' [ glue (h _) ] ∙₀' ap₀ right pB

  decodeBA : ∀ {b₀ a₁} → codeBA b₀ a₁ → right b₀ =₀ left a₁ :> Pushout span
  decodeBB : ∀ {b₀ b₁} → codeBB b₀ b₁ → right b₀ =₀ right b₁ :> Pushout span
  decodeBA = SetQuot-rec Trunc-level pcBA-to-path pcBA-to-path-rel
  decodeBB = SetQuot-rec Trunc-level pcBB-to-path pcBB-to-path-rel

  abstract
    decodeBA-is-decodeBB : ∀ {b₀} c₁ →
      decodeBA {b₀} {f c₁} == decodeBB {b₀} {g c₁}
      [ (λ p₁ → codeBP b₀ p₁ → right b₀ =₀ p₁) ↓ glue c₁ ]
    decodeBA-is-decodeBB {b₀ = b₀} = SurjExt.ext
      (λ _ → ↓-preserves-level $ Π-is-set λ _ → Trunc-level) h h-is-surj
      (λ d₁ → ↓-→-from-transp $ λ= $ SetQuot-elim
        {P = λ cBA → transport (right b₀ =₀_) (glue (h d₁)) (decodeBA cBA)
                  == decodeBB (transport (codeBP b₀) (glue (h d₁)) cBA)}
        (λ _ → =-preserves-set Trunc-level)
        (λ pcBA →
          transport (right b₀ =₀_) (glue (h d₁)) (pcBA-to-path pcBA)
            =⟨ transp₀-cst=₀idf [ glue (h d₁) ] (pcBA-to-path pcBA) ⟩
          pcBA-to-path pcBA ∙₀' [ glue (h d₁) ]
            =⟨ ! $ ap (λ e → decodeBB (–> e q[ pcBA ])) (CodeBAEquivCodeBB.eqv-β b₀ d₁) ⟩
          decodeBB (–> (CodeBAEquivCodeBB.eqv b₀ (h d₁)) q[ pcBA ])
            =⟨ ! $ ap decodeBB (coe-β (CodeBAEquivCodeBB.eqv b₀ (h d₁)) q[ pcBA ]) ⟩
          decodeBB (coe (ua (CodeBAEquivCodeBB.eqv b₀ (h d₁))) q[ pcBA ])
            =⟨ ! $ ap (λ p → decodeBB (coe p q[ pcBA ])) (CodeBP.glue-β b₀ (h d₁)) ⟩
          decodeBB (transport (codeBP b₀) (glue (h d₁)) q[ pcBA ])
            =∎)
        (λ _ → prop-has-all-paths-↓ $ Trunc-level {n = 0} _ _))
      (λ _ _ _ → prop-has-all-paths-↓ $ ↓-level $ Π-is-set λ _ → Trunc-level)

  decodeBP : ∀ {b₀ p₁} → codeBP b₀ p₁ → right b₀ =₀ p₁
  decodeBP {p₁ = p₁} = Pushout-elim
    (λ a₁ → decodeBA) (λ b₁ → decodeBB)
    decodeBA-is-decodeBB
    p₁


-- Internal monoid in a strict monoidal category
{-# OPTIONS --safe #-}

open import Cubical.Categories.Category.Base
open import Cubical.Categories.Monoidal.Base
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Path
open import Cubical.Categories.Functor.Base
open import Cubical.Reflection.RecordEquiv hiding (unit)

module Cubical.Categories.Monoidal.Strict.Monoid {ℓ ℓ' : Level} (C : StrictMonCategory ℓ ℓ') where

module _ where
    open StrictMonCategory C

    record Monoid : Type (ℓ-max ℓ ℓ') where
      constructor monoid
      field
        carrier : ob
        η : Hom[ unit , carrier ]
        μ : Hom[ carrier ⊗ carrier , carrier ]
        assoc-μ : PathP (λ i → Hom[ assoc carrier carrier carrier i , carrier ]) (μ ∘ (id ⊗ₕ μ)) (μ ∘ (μ ⊗ₕ id))
        idl-μ : PathP (λ i → Hom[ idl carrier i , carrier ]) (μ ∘ (η ⊗ₕ id)) id
        idr-μ : PathP (λ i → Hom[ idr carrier i , carrier ]) (μ ∘ (id ⊗ₕ η)) id

    open Monoid

    record Monoid[_,_] (monA monB : Monoid) : Type ℓ' where
      constructor monoidHom
      field
        carrierHom : Hom[ carrier monA , carrier monB ]
        nat-η : carrierHom ∘ η monA ≡ η monB
        nat-μ : carrierHom ∘ μ monA ≡ μ monB ∘ (carrierHom ⊗ₕ carrierHom)

    open Monoid[_,_]

    monoidHomExt : {monA monB : Monoid} (f g : Monoid[ monA , monB ]) → carrierHom f ≡ carrierHom g → f ≡ g
    carrierHom (monoidHomExt f g p i) = p i
    nat-η (monoidHomExt {monA} {monB} f g p i) = lemma i
      where lemma : PathP (λ i₁ → p i₁ ∘ η monA ≡ η monB) (nat-η f) (nat-η g)
            lemma = toPathP (isSetHom _ _ _ _)
    nat-μ (monoidHomExt {monA} {monB} f g p i) = lemma i
      where lemma : PathP (λ i₁ → p i₁ ∘ μ monA ≡ μ monB ∘ (p i₁ ⊗ₕ p i₁)) (nat-μ f) (nat-μ g)
            lemma = toPathP (isSetHom _ _ _ _)

module C = StrictMonCategory C
open Category
open Monoid
open Monoid[_,_]
open Functor

monoidCategory : Category (ℓ-max ℓ ℓ') ℓ'
ob monoidCategory = Monoid
Hom[_,_] monoidCategory = Monoid[_,_]
carrierHom (id monoidCategory) = C.id
nat-η (id monoidCategory {monA}) = C.⋆IdR (η monA)
nat-μ (id monoidCategory {monA}) =
  C.id C.∘ μ monA
    ≡⟨ C.⋆IdR (μ monA) ⟩
  μ monA
    ≡⟨ sym (C.⋆IdL (μ monA)) ⟩
  μ monA C.∘ id C.C
    ≡⟨ cong (μ monA C.∘_) (sym (F-id C.─⊗─)) ⟩
  μ monA C.∘ (C.id C.⊗ₕ C.id) ∎
carrierHom (_⋆_ monoidCategory f g) = carrierHom g C.∘ carrierHom f
nat-η (_⋆_ monoidCategory {monA} {monB} {monC} f g) =
  (carrierHom g C.∘ carrierHom f) C.∘ η monA
    ≡⟨ sym (C.⋆Assoc (η monA) (carrierHom f) (carrierHom g)) ⟩
  carrierHom g C.∘ (carrierHom f C.∘ η monA)
    ≡⟨ cong (carrierHom g C.∘_) (nat-η f) ⟩
  carrierHom g C.∘ η monB
    ≡⟨ nat-η g ⟩
  η monC ∎
nat-μ (_⋆_ monoidCategory {monA} {monB} {monC} f g) =
  (carrierHom g C.∘ carrierHom f) C.∘ μ monA
    ≡⟨ sym (C.⋆Assoc (μ monA) (carrierHom f) (carrierHom g)) ⟩
  carrierHom g C.∘ (carrierHom f C.∘ μ monA)
    ≡⟨ cong (carrierHom g C.∘_) (nat-μ f) ⟩
  carrierHom g C.∘ (μ monB C.∘ (carrierHom f C.⊗ₕ carrierHom f))
    ≡⟨ C.⋆Assoc (carrierHom f C.⊗ₕ carrierHom f) (μ monB) (carrierHom g) ⟩
  (carrierHom g C.∘ μ monB) C.∘ (carrierHom f C.⊗ₕ carrierHom f)
    ≡⟨ cong (C._∘ (carrierHom f C.⊗ₕ carrierHom f)) (nat-μ g) ⟩
  (μ monC C.∘ (carrierHom g C.⊗ₕ carrierHom g)) C.∘ (carrierHom f C.⊗ₕ carrierHom f)
    ≡⟨ sym (C.⋆Assoc (carrierHom f C.⊗ₕ carrierHom f) (carrierHom g C.⊗ₕ carrierHom g) (μ monC)) ⟩
  μ monC C.∘ ((carrierHom g C.⊗ₕ carrierHom g) C.∘ (carrierHom f C.⊗ₕ carrierHom f))
    ≡⟨ cong (μ monC C.∘_) (sym (F-seq C.─⊗─ (carrierHom f , carrierHom f) (carrierHom g , carrierHom g))) ⟩
  μ monC C.∘ ((carrierHom g C.∘ carrierHom f) C.⊗ₕ (carrierHom g C.∘ carrierHom f)) ∎
⋆IdL monoidCategory {monA} f = monoidHomExt _ _ (C.⋆IdL (carrierHom f))
⋆IdR monoidCategory {monA} f = monoidHomExt _ _ (C.⋆IdR (carrierHom f))
⋆Assoc monoidCategory {monA} {monB} {monC} {monD} f g h =
  monoidHomExt _ _ (C.⋆Assoc (carrierHom f) (carrierHom g) (carrierHom h))
isSetHom monoidCategory {monA} {monB} =
  isOfHLevelRetractFromIso 2 isoToΣ
    (isSetΣ C.isSetHom λ _ → isProp→isSet (isProp× (C.isSetHom _ _) (C.isSetHom _ _)))
  where
  unquoteDecl isoToΣ = declareRecordIsoΣ isoToΣ (quote Monoid[_,_])


-- Andreas, 2022-06-10
-- A failed attempt to break Prop ≤ Set.
-- See https://github.com/agda/agda/issues/5761#issuecomment-1151336715

{-# OPTIONS --prop --cumulativity #-}

data   ⊥ : Set where
record ⊤ : Set where
  constructor tt

data Fool : Prop where
  true false : Fool

Bool : Set
Bool = Fool

True : Bool → Set
True true  = ⊤
True false = ⊥

X : Fool → Set

true≡false : X true → X false
true≡false x = x

X = True

-- Now we have  True true → True false  which looks dangerous...
-- But we cannot exploit it:

fails : ⊥
fails = true≡false tt

-- ERROR:
-- ⊤ !=< (True _)
-- when checking that the expression tt has type X _


open import Nat
open import Prelude
open import core
open import contexts

open import progress
open import preservation
open import elaborability
open import typed-elaboration

module continuity where
  -- we take the sensibilty theorem as a postulate; for a proof, refer to
  -- the POPL17 mechanization. we also postulate some glue that allows us
  -- to use our theorems here on the shape of results from that work.
  postulate
    action : Set
    zexp : Set
    _◆ : zexp → hexp
    _⊢_=>_~_~>_=>_ : (Γ : tctx) → (e1 : zexp) → (t1 : htyp)
                        → (α : action) → (e2 : zexp) → (t2 : htyp) → Set
    sensibility : {Γ : tctx} {e e' : zexp} {τ τ' : htyp} {α : action} →
                  Γ ⊢ (e ◆) => τ →
                  Γ ⊢ e => τ ~ α ~> e' => τ' →
                  Γ ⊢ (e' ◆) => τ'
    binders-unique-h : hexp → Set
    binders-unique-z : zexp → Set
    binders-unique-cursor1 : ∀{e} → binders-unique-z e → binders-unique-h (e ◆)
    binders-unique-cursor2 : ∀{e} → binders-unique-h (e ◆) → binders-unique-z e
    binders-unique-sensibility : {Γ : tctx} {e e' : zexp} {τ τ' : htyp} {α : action} →
                  binders-unique-z e →
                  Γ ⊢ e => τ ~ α ~> e' => τ' →
                  binders-unique-z e'
    expansion-unique : ∀{Γ e τ d Δ} →
                       binders-unique-h e →
                       Γ ⊢ e ⇒ τ ~> d ⊣ Δ →
                       binders-unique d

  continuity : ∀{ e τ α e' τ' }
             → binders-unique-z e
             → ∅ ⊢ (e ◆) => τ
             → ∅ ⊢ e => τ ~ α ~> e' => τ'
             → Σ[ Δ ∈ hctx ] Σ[ d ∈ ihexp ]
                (   ∅ ⊢ (e' ◆) ⇒ τ' ~> d ⊣ Δ
                  × Δ , ∅ ⊢ d :: τ'
                  × ( (Σ[ d' ∈ ihexp ]( d ↦ d' × Δ , ∅ ⊢ d' :: τ' ))
                     + d boxedval
                     + d indet
                    )
                )
  continuity bu wt action with sensibility wt action
  ... | sense with elaborability-synth sense
  ... | d , Δ , exp with typed-elaboration-synth exp
  ... | d::τ' with progress d::τ'
  ... | (S (d' , stp)) =  Δ , d , exp , d::τ' , Inl (d' , stp , preservation (expansion-unique (binders-unique-cursor1 (binders-unique-sensibility bu action)) exp) d::τ' stp)
  ... | (I ind) = Δ , d , exp , d::τ' , Inr (Inr ind)
  ... | (BV boxed) = Δ , d , exp , d::τ' , Inr (Inl boxed)


-- Andreas, 2017-08-10, issue #2667, reported by xekoukou,
-- test case by gallais

-- {-# OPTIONS -v tc.with.split:40 #-}

data Bot : Set where

data A : Set where
  s  : A → A

data P : A → Set where
  p : ∀ {a} → P (s a)

poo : ∀{b} → P b → Set
poo p with Bot
poo {b = s c} p | _ = P c

-- Error WAS (2.5.3): Panic, unbound variable c

-- Expected (as in 2.5.2):
--
-- Inaccessible (dotted) patterns from the parent clause must also be
-- inaccessible in the with clause, when checking the pattern {b = s c},
-- when checking that the clause
-- poo p with Bot
-- poo {b = s c} p | _ = P c
-- has type {b : A} → P b → Set

-- Ideally: succeed.

-- 2018-01-05, Jesper: We're one step closer to an ideal world.


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

open import HoTT

module homotopy.FiberOfWedgeToProduct {i j} (X : Ptd i) (Y : Ptd j) where

  private
    X⊙×Y = X ⊙× Y

  private
    to-glue-template : ∀ {x y}
      {p p' : (pt X , pt Y) == (x , y)}
      (q : p == p' [ (λ w → ∨-to-× w == (x , y)) ↓ wglue ])
      → jright (snd×= p) == jleft (fst×= p') :> (pt X == x) * (pt Y == y)
    to-glue-template {p = p} {p' = p'} q =
      ! (jglue (fst×= p) (snd×= p)) ∙' ap (jleft ∘ fst×=) (↓-∨to×=cst-out q)

    module To (x : de⊙ X) (y : de⊙ Y) = WedgeElim
      {P = λ x∨y → ∨-to-× x∨y == (x , y) → (pt X == x) * (pt Y == y)}
      (λ x p → jright (snd×= p)) (λ y p → jleft (fst×= p))
      (↓-app→cst-in to-glue-template)

  to : ∀ x y → hfiber ∨-to-× (x , y) → (pt X == x) * (pt Y == y)
  to x y = uncurry (To.f x y)

  private
    -- the only needed β-rule for [to] applied to [wglue].
    abstract
      to-glue-idp-β : ap (to (pt X) (pt Y)) (pair= wglue (↓-∨to×=cst-in idp))
        == ! (jglue idp idp)
      to-glue-idp-β =
          split-ap2 (to _ _) wglue (↓-∨to×=cst-in idp)
        ∙ ap (λ p → ↓-app→cst-out p (↓-∨to×=cst-in idp)) (To.glue-β _ _)
        ∙ ↓-app→cst-β to-glue-template (↓-∨to×=cst-in idp)
        ∙ ap (λ p → ! (jglue idp idp) ∙' ap (left ∘ fst×=) p) (↓-∨to×=cst-β idp)

  private
    from-glue-template : ∀ {x y} (x-path : pt X == x) (y-path : pt Y == y)
      → (winr y , pair×= x-path idp) == (winl x , pair×= idp y-path)
          :> hfiber ∨-to-× (x , y)
    from-glue-template idp idp = ! $ pair= wglue (↓-∨to×=cst-in idp)

    module From (x : de⊙ X) (y : de⊙ Y) = JoinRec
      {C = hfiber ∨-to-× (x , y)}
      (λ x-path → winr y , pair×= x-path idp)
      (λ y-path → winl x , pair×= idp y-path)
      from-glue-template

  from : ∀ x y → (pt X == x) * (pt Y == y) → hfiber ∨-to-× (x , y)
  from = From.f

  private
    abstract
      to-from-glue-template : ∀ {x y} (x-path : pt X == x) (y-path : pt Y == y)
        → ap left (fst×=-β x-path idp) == ap right (snd×=-β idp y-path)
            [ (λ j → to x y (from x y j) == j) ↓ jglue x-path y-path ]
      to-from-glue-template idp idp = ↓-∘=idf-in' (to _ _) (from _ _) $
          ap (ap (to _ _)) (From.glue-β _ _ idp idp)
        ∙ ap-! (to _ _) (pair= wglue (↓-∨to×=cst-in idp))
        ∙ ap ! to-glue-idp-β
        ∙ !-! (jglue idp idp)

    to-from : ∀ x y j → to x y (from x y j) == j
    to-from x y = Join-elim
      {P = λ j → to x y (from x y j) == j}
      (λ x-path → ap left (fst×=-β x-path idp))
      (λ y-path → ap right (snd×=-β idp y-path))
      to-from-glue-template

  private
    abstract
      from-to-winl-template : ∀ {x x' y} (xy-path : (x' , pt Y) == (x , y))
        → (winl x , pair×= idp (snd×= xy-path)) == (winl x' , xy-path)
            :> hfiber (∨-to-× {X = X} {Y = Y}) (x , y)
      from-to-winl-template idp = idp

      from-to-winr-template : ∀ {x y y'} (xy-path : (pt X , y') == (x , y))
        → (winr y , pair×= (fst×= xy-path) idp) == (winr y' , xy-path)
            :> hfiber (∨-to-× {X = X} {Y = Y}) (x , y)
      from-to-winr-template idp = idp

      -- this version enables path induction on [q] which
      -- turns *both* [p] and [p'] into [idp]. yay!
      from-to-glue-template' : ∀ {x y}
        (p p' : (pt X , pt Y) == (x , y)) (q : p == p')
        → from-to-winl-template p == from-to-winr-template p'
          [ (λ xy → from x y (to x y xy) == xy) ↓ pair= wglue (↓-∨to×=cst-in q) ]
      from-to-glue-template' idp .idp idp =
        ↓-∘=idf-in' (from (pt X) (pt Y)) (to (pt X) (pt Y)) $
            ap (ap (from _ _)) to-glue-idp-β
          ∙ ap-! (from _ _) (jglue idp idp)
          ∙ ap ! (From.glue-β _ _ idp idp)
          ∙ !-! (pair= wglue (↓-∨to×=cst-in idp))

      from-to-glue-template : ∀ {x y}
        (p p' : (pt X , pt Y) == (x , y))
        (q : p == p' [ (λ w → ∨-to-× w == (x , y)) ↓ wglue ])
        → from-to-winl-template p == from-to-winr-template p'
          [ (λ xy → from x y (to x y xy) == xy) ↓ pair= wglue q ]
      from-to-glue-template {x} {y} p p' q =
        transport
          (λ q →
            from-to-winl-template p == from-to-winr-template p'
            [ (λ xy → from x y (to x y xy) == xy) ↓ pair= wglue q ])
          (↓-∨to×=cst-η q)
          (from-to-glue-template' p p' (↓-∨to×=cst-out q))

    from-to : ∀ x y hf → from x y (to x y hf) == hf
    from-to x y = uncurry $ Wedge-elim
      {P = λ w → ∀ p → from x y (To.f x y w p) == (w , p)}
      (λ x → from-to-winl-template)
      (λ y → from-to-winr-template)
      (↓-Π-in λ {p} {p'} → from-to-glue-template p p')

  fiber-thm : ∀ x y → hfiber ∨-to-× (x , y) ≃ (pt X == x) * (pt Y == y)
  fiber-thm x y = equiv (to x y) (from x y) (to-from x y) (from-to x y)


module TestsTransport where

open import Function
open import Data.Product
open import Relation.Binary.PropositionalEquality as P
open ≡-Reasoning

open import Isomorphisms
open import Tests

Foo : (A : Set) → (T : Testable A) →
      (x y : A) → (φ : Test T) → Set
Foo A T x y ⊤ =
  _⊨_ {A} {T} x ⊤ ≡ _⊨_ {A} {T} y ⊤
Foo A T x y ⊥ =
  _⊨_ {A} {T} x ⊥ ≡ _⊨_ {A} {T} y ⊥
Foo A T x y (nonTriv nt) = sat≡ (kind T) (subT nt) (obs T)
  where
    sat≡ : (k : Kind) → SubTests T k → (A → ObsTy (index T) (parts T) k) → Set
    sat≡ ind φs o =
      cotuple (λ i y → y ⊨ app φs i) (o x)
      ≡ cotuple (λ i y → y ⊨ app φs i) (o y)
    sat≡ coind (i , φ) o =
      app (o x) i ⊨ φ ≡ app (o y) i ⊨ φ

-- Note: Maybe Reveal_is_ can help to match on (kind T)??

{-
≃-transport : {A B : Set} → {T : Testable A} → (I : Iso A B) →
              (x y : A) → x ≃〈 T 〉 y →
              (Iso.iso I x) ≃〈 iso-testable T I 〉 (Iso.iso I y)
≃-transport {A} {B} {T₁} I x y p = record { eqProof = q }
    where
    T₂ =  iso-testable T₁ I
    f : A → B
    f = Iso.iso I
    g : B → A
    g = IsIso.inv (Iso.indeedIso I)
    o₁ = obs T₁
    o₂ = obs T₂
    lem : (a : A) → (o₂ ∘ f) a ≡ o₁ a
    lem a =
      begin
        (o₂ ∘ f) a
      ≡⟨ refl ⟩
        (o₁ ∘ g ∘ f) a
      ≡⟨ cong (λ x₁ → o₁ x₁) (IsIso.isLeftInv (Iso.indeedIso I) a) ⟩
        o₁ a
      ∎
    q : (φ : Test T₂) → f x ⊨ φ ≡ f y ⊨ φ
    q ⊤ = refl
    q ⊥ = refl
    q (nonTriv (nonTrivTest t)) =
      begin
        sat (kind T₂) t (o₂ (f x))
      ≡⟨ cong (λ u → sat (kind T₂) t u) (lem x) ⟩
        sat (kind T₂) t (o₁ x)
      ≡⟨ sat≡ (index T₁) (parts T₁) (kind T₁)
              o₂ (obsIso T₂) (partsTestable T₁) o₁ (obsIso T₁) lem p t ⟩
        sat (kind T₂) t (o₁ y)
      ≡⟨ cong (λ u → sat (kind T₂) t u) (sym $ lem y) ⟩
        sat (kind T₂) t (o₂ (f y))
      ∎
      where
        -- Agda complains at this point that
        -- T₁ != record { index = index T₁ ; parts = parts T₁ ; kind = kind T₁
        --              ; obs = obs T₁ ; obsIso = obsIso T₁
        --              ; partsTestable = partsTestable T₁}
        -- ...
        -- Is it possible to circumvent this issue?

        sat≡ : (Idx : Set) → (P : Idx → Set) → (k : Kind) →
               (o₂ : B → ObsTy Idx P k) → (isoT₂ : IsIso o₂) →
               (pTestable : (i : Idx) → Testable (P i)) →
               (o₁ : A → ObsTy Idx P k) → (isoT₁ : IsIso o₁) →
               ((a : A) → (o₂ ∘ f) a ≡ o₁ a) →
               x ≃〈 record
                  { index = Idx
                  ; parts = P
                  ; kind = k
                  ; obs = o₁
                  ; obsIso = isoT₁
                  ; partsTestable = pTestable
                  } 〉 y →
               (t' : SubTests
                     ( record
                      { index = Idx ; parts = P ; kind = k ; obs = o₂
                      ; obsIso = isoT₂ ; partsTestable = pTestable }) k ) →
               sat k t' (o₁ x) ≡ sat k t' (o₁ y)
        sat≡ = {!!}
-}

{-
≃-transport {A} {B} {T₁} I x y p =
  q (index T₁) (parts T₁) (kind T₁) (obs T₂) (obsIso T₂) (partsTestable T₁)
    (obs T₁) (obsIso T₁) lem {!!}
    -- Agda complains at this point that
    -- T₁ != record { index = index T₁ ; parts = parts T₁ ; kind = kind T₁
    --              ; obs = obs T₁ ; obsIso = obsIso T₁
    --              ; partsTestable = partsTestable T₁}
    -- ...
    -- Is it possible to circumvent this issue?
  where
    T₂ =  iso-testable T₁ I
    f : A → B
    f = Iso.iso I
    g : B → A
    g = IsIso.inv (Iso.indeedIso I)
    o₁ = obs T₁
    o₂ = obs T₂
    lem : (a : A) → (o₂ ∘ f) a ≡ o₁ a
    lem a =
      begin
        (o₂ ∘ f) a
      ≡⟨ refl ⟩
        (o₁ ∘ g ∘ f) a
      ≡⟨ cong (λ x₁ → o₁ x₁) (IsIso.isLeftInv (Iso.indeedIso I) a) ⟩
        o₁ a
      ∎

    q : (Idx : Set) → (P : Idx → Set) → (k : Kind) →
        (o₂ : B → ObsTy Idx P k) → (isoT₂ : IsIso o₂) →
        (pTestable : (i : Idx) → Testable (P i)) →
        (o₁ : A → ObsTy Idx P k) → (isoT₁ : IsIso o₁) →
        ((a : A) → (o₂ ∘ f) a ≡ o₁ a) →
        x ≃〈 record
                  { index = Idx
                  ; parts = P
                  ; kind = k
                  ; obs = o₁
                  ; obsIso = isoT₁
                  ; partsTestable = pTestable
                  } 〉 y →
        f x ≃〈 record
                  { index = Idx
                  ; parts = P
                  ; kind = k
                  ; obs = o₂
                  ; obsIso = isoT₂
                  ; partsTestable = pTestable
                  } 〉
        f y
    q Idx P ind o₂ isoT₂ pTestable o₁ isoT₁ lem p = record { eqProof = qi }
      where
        T₁' = record
          { index = Idx ; parts = P ; kind = ind
          ; obs = o₁ ; obsIso = isoT₁ ; partsTestable = pTestable }
        T₂' = record
          { index = Idx ; parts = P ; kind = ind
          ; obs = o₂ ; obsIso = isoT₂ ; partsTestable = pTestable }
        qi : (φ : Test T₂') → f x ⊨ φ ≡ f y ⊨ φ
        qi ⊤ = refl
        qi ⊥ = refl
        qi (nonTriv (nonTrivTest φs)) =
          begin
            cotuple (λ i z → z ⊨ app φs i) (o₂ (f x))
          ≡⟨ cong (λ u → cotuple (λ i z → z ⊨ app φs i) u) (lem x) ⟩
            cotuple (λ i z → z ⊨ app φs i) (o₁ x)
          ≡⟨ refl ⟩
            _⊨_ {A} {T₁'} x (nonTriv (nonTrivTest φs))
          ≡⟨ eqProof p (nonTriv (nonTrivTest φs)) ⟩
            _⊨_ {A} {T₁'} y (nonTriv (nonTrivTest φs))
          ≡⟨ refl ⟩
            cotuple (λ i z → z ⊨ app φs i) (o₁ y)
          ≡⟨ cong (λ u → cotuple (λ i z → z ⊨ app φs i) u) (sym $ lem y) ⟩
            cotuple (λ i z → z ⊨ app φs i) (o₂ (f y))
          ∎
    q Idx P coind o₂ isoT₂ pTestable o₁ isoT₁ lem p = record { eqProof = qc }
      where
        T₁' = record
          { index = Idx ; parts = P ; kind = coind
          ; obs = o₁ ; obsIso = isoT₁ ; partsTestable = pTestable }
        T₂' = record
          { index = Idx ; parts = P ; kind = coind
          ; obs = o₂ ; obsIso = isoT₂ ; partsTestable = pTestable }
        qc : (φ : Test T₂') → f x ⊨ φ ≡ f y ⊨ φ
        qc ⊤ = refl
        qc ⊥ = refl
        qc (nonTriv (nonTrivTest (i , φ))) =
          begin
            app (o₂ (f x)) i ⊨ φ
          ≡⟨ cong (λ u → app u i ⊨ φ) (lem x) ⟩
            app (o₁ x) i ⊨ φ
          ≡⟨ {!!} ⟩
            app (o₁ y) i ⊨ φ
          ≡⟨ cong (λ u → app u i ⊨ φ) (sym $ lem y) ⟩
            app (o₂ (f y)) i ⊨ φ
          ∎
-}


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

module FreeReader where

open import Cubical.Core.Everything
open import Cubical.Foundations.Prelude
open import Function.Base using (id; _$_)

open import Class
open Functor

variable
  A B C : Set

-- The Reader monad, as a free monad
data FreeReader (R : Set) : Set → Set₁ where
  Pure : A → FreeReader R A
  Bind : FreeReader R A → (A → FreeReader R B) → FreeReader R B
  Ask : FreeReader R R

  -- Monad laws
  LeftId : ∀ {A B}
    → (a : A)
    → (f : A → FreeReader R B)
    → Bind (Pure a) f ≡ f a
  RightId : ∀ {A}
    → (m : FreeReader R A)
    → Bind m Pure ≡ m
  Assoc : ∀ {A B C}
    → (m : FreeReader R A)
    → (f : A → FreeReader R B)
    → (g : B → FreeReader R C)
    → Bind (Bind m f) g ≡ Bind m (λ x → Bind (f x) g)

freereader-functor : ∀ {R} → Functor (FreeReader R)
freereader-functor .fmap f m = Bind m (Pure ∘ f)
freereader-functor .fmap-id-legit m i = RightId m i
freereader-functor .fmap-compose-legit m f g i
  = hcomp (λ j → λ { (i = i0) → Bind m (Pure ∘ f ∘ g)
                   ; (i = i1) → Assoc m (Pure ∘ g) (Pure ∘ f) (~ j)
                   })
          (Bind m (λ x → LeftId (g x) (Pure ∘ f) (~ i)))

freereader-ap : ∀ {R} → Applicative (FreeReader R)
freereader-ap .Applicative.functor = freereader-functor
freereader-ap .Applicative.pure = Pure
freereader-ap .Applicative._<*>_ mf m = Bind mf (λ f → Bind m (Pure ∘ f))
freereader-ap .Applicative.app-identity v i
  = hcomp (λ j → λ { (i = i0) → LeftId id (λ f → Bind v (Pure ∘ f)) (~ j)
                   ; (i = i1) → v
                   })
          (RightId v i)
freereader-ap .Applicative.app-compose u v w
  -- For some reason, Agda doesn't like wildcards in cubical proofs. Oh well.
        = Pure _∘_ <*> u <*> v <*> w
  ≡[ i ]⟨ LeftId _∘_ (λ f → Bind u (Pure ∘ f)) i <*> v <*> w ⟩
          Bind u (λ u' → Pure (u' ∘_)) <*> v <*> w
  ≡[ i ]⟨ Assoc u (λ u' → Pure (u' ∘_)) (λ f → Bind v (Pure ∘ f)) i <*> w ⟩
          Bind u (λ u' → Bind (Pure (u' ∘_)) (λ c → Bind v (Pure ∘ c))) <*> w
  ≡[ i ]⟨ Bind u (λ u' → LeftId (u' ∘_) (λ c → Bind v (Pure ∘ c)) i) <*> w ⟩
          Bind u (λ u' → Bind v (λ v' → Pure (u' ∘ v'))) <*> w
  ≡[ i ]⟨ Assoc u (λ u' → Bind v (λ v' → Pure (u' ∘ v'))) (λ f → Bind w (Pure ∘ f)) i ⟩
          Bind u (λ u' → Bind (Bind v (λ v' → Pure (u' ∘ v'))) (λ f → Bind w (Pure ∘ f)))
  ≡[ i ]⟨ Bind u (λ u' → Assoc v (λ v' → Pure (u' ∘ v')) (λ f → Bind w (Pure ∘ f)) i) ⟩
          Bind u (λ u' → Bind v (λ v' → Bind (Pure (u' ∘ v')) (λ f → Bind w (Pure ∘ f))))
  ≡[ i ]⟨ Bind u (λ u' → Bind v (λ v' → LeftId (u' ∘ v') (λ f → Bind w (Pure ∘ f)) i)) ⟩
          Bind u (λ u' → Bind v (λ v' → Bind w (Pure ∘ u' ∘ v')))
  ≡[ i ]⟨ Bind u (λ u' → Bind v (λ v' → Bind w (λ w' → LeftId (v' w') (Pure ∘ u') (~ i)))) ⟩
          Bind u (λ u' → Bind v (λ v' → Bind w (λ w' → Bind (Pure (v' w')) (Pure ∘ u'))))
  ≡[ i ]⟨ Bind u (λ u' → Bind v (λ v' → Assoc w (Pure ∘ v') (Pure ∘ u') (~ i))) ⟩
          Bind u (λ u' → Bind v (λ v' → Bind (Bind w (Pure ∘ v')) (Pure ∘ u')))
  ≡[ i ]⟨ Bind u (λ u' → Assoc v (λ f → Bind w (Pure ∘ f)) (Pure ∘ u') (~ i)) ⟩
          Bind u (λ u' → Bind (Bind v (λ v' → Bind w (Pure ∘ v'))) (Pure ∘ u'))
          ∎
  where open Applicative freereader-ap
freereader-ap .Applicative.app-homo f x
  = LeftId f (λ f' → Bind (Pure x) (λ x' → Pure (f' x')))
  ∙ LeftId x (λ x' → Pure (f x'))
freereader-ap .Applicative.app-intchg u y
          -- u <*> pure y
        = Bind u (λ u' → Bind (Pure y) (Pure ∘ u'))
  ≡[ i ]⟨ Bind u (λ u' → LeftId y (Pure ∘ u') i) ⟩
          Bind u (λ u' → Pure (u' y))
  ≡[ i ]⟨ LeftId (_$ y) (λ f → Bind u (Pure ∘ f)) (~ i) ⟩
          Bind (Pure (_$ y)) (λ _y' → Bind u (λ u' → Pure (u' y')))
          -- pure (_$ y) <*> u
          ∎

freereader-monad : ∀ {R} → Monad (FreeReader R)
freereader-monad .Monad.applicative    = freereader-ap
freereader-monad .Monad._>>=_          = Bind
freereader-monad .Monad.monad-left-id  = LeftId
freereader-monad .Monad.monad-right-id = RightId
freereader-monad .Monad.monad-assoc    = Assoc


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

open import HoTT
open import homotopy.HSpace renaming (HSpaceStructure to HSS)
import homotopy.WedgeExtension as WedgeExt

module homotopy.Pi2HSusp {i} {X : Ptd i} {{_ : has-level 1 (de⊙ X)}}
  {{_ : is-connected 0 (de⊙ X)}} (H-X : HSS X)
  where

  {- TODO this belongs somewhere else, but where? -}
  private
    Type=-ext : ∀ {i} {A B : Type i} (p q : A == B)
      → (coe p ∼ coe q) → p == q
    Type=-ext p q α =
      ! (ua-η p)
      ∙ ap ua (Subtype=-out is-equiv-prop (λ= α))
      ∙ ua-η q

  module μ = ConnectedHSpace H-X
  μ = μ.μ
  private
    A = de⊙ X
    e = pt X

  P : Susp A → Type i
  P x = Trunc 1 (north == x)

  module Codes = SuspRec A A (λ a → ua (μ.r-equiv a))

  Codes : Susp A → Type i
  Codes = Codes.f

  Codes-level : (x : Susp A) → has-level 1 (Codes x)
  Codes-level = Susp-elim ⟨⟩ ⟨⟩
    (λ _ → prop-has-all-paths-↓)

  encode'₀ : {x : Susp A} → (north == x) → Codes x
  encode'₀ α = transport Codes α e

  encode' : {x : Susp A} → P x → Codes x
  encode' {x} = Trunc-rec {{Codes-level x}} encode'₀

  import homotopy.SuspAdjointLoop as SAL
  {- This should be [[_] ∘ η] where [η] is the functor in SuspAdjointLoop.agda -}
  decodeN' : A → P north
  decodeN' = [_] ∘ fst (SAL.η X)

  abstract
    transport-Codes-mer : (a a' : A)
      → transport Codes (merid a) a' == μ a a'
    transport-Codes-mer a a' =
      coe (ap Codes (merid a)) a'
        =⟨ Codes.merid-β a |in-ctx (λ w → coe w a') ⟩
      coe (ua (μ.r-equiv a)) a'
        =⟨ coe-β (μ.r-equiv a) a' ⟩
      μ a a' ∎

    transport-Codes-mer-e-! : (a : A)
      → transport Codes (! (merid e)) a == a
    transport-Codes-mer-e-! a =
      coe (ap Codes (! (merid e))) a
        =⟨ ap-! Codes (merid e) |in-ctx (λ w → coe w a) ⟩
      coe (! (ap Codes (merid e))) a
        =⟨ Codes.merid-β e |in-ctx (λ w → coe (! w) a) ⟩
      coe (! (ua (μ.r-equiv e))) a
        =⟨ Type=-ext (ua (μ.r-equiv e)) idp (λ x → coe-β _ x ∙ μ.unit-l x)
          |in-ctx (λ w → coe (! w) a) ⟩
      coe (! idp) a ∎

  abstract
    encode'-decodeN' : (a : A) → encode' (decodeN' a) == a
    encode'-decodeN' a =
      transport Codes (merid a ∙ ! (merid e)) e
        =⟨ transp-∙ {B = Codes} (merid a) (! (merid e)) e ⟩
      transport Codes (! (merid e)) (transport Codes (merid a) e)
        =⟨ transport-Codes-mer a e ∙ μ.unit-r a
          |in-ctx (λ w → transport Codes (! (merid e)) w) ⟩
      transport Codes (! (merid e)) a
        =⟨ transport-Codes-mer-e-! a ⟩
      a ∎

  abstract
    homomorphism : (a a' : A)
      → Path {A = Trunc 1 (north == south)}
        [ merid (μ a a' ) ] [ merid a' ∙ ! (merid e) ∙ merid a ]
    homomorphism = WedgeExt.ext args
      where
      args : WedgeExt.args {a₀ = e} {b₀ = e}
      args = record {m = -1; n = -1;
        P = λ a a' → (_ , ⟨⟩);
        f = λ a →  ap [_] $
              merid (μ a e)
                =⟨ ap merid (μ.unit-r a) ⟩
              merid a
                =⟨ ap (λ w → w ∙ merid a) (! (!-inv-r (merid e)))
                   ∙ ∙-assoc (merid e) (! (merid e)) (merid a)  ⟩
              merid e ∙ ! (merid e) ∙ merid a ∎;
        g = λ a' → ap [_] $
              merid (μ e a')
                =⟨ ap merid (μ.unit-l a') ⟩
              merid a'
                =⟨ ! (∙-unit-r (merid a'))
                   ∙ ap (λ w → merid a' ∙ w) (! (!-inv-l (merid e))) ⟩
              merid a' ∙ ! (merid e) ∙ merid e ∎ ;
        p = ap (λ {(p₁ , p₂) → ap [_] $
              merid (μ e e) =⟨ p₁ ⟩
              merid e       =⟨ p₂ ⟩
              merid e ∙ ! (merid e) ∙ merid e ∎})
             (pair×= (ap (λ x → ap merid x) (! μ.coh)) (coh (merid e)))}
        where coh : {B : Type i} {b b' : B} (p : b == b')
                → ap (λ w → w ∙ p) (! (!-inv-r p)) ∙ ∙-assoc p (! p) p
                  == ! (∙-unit-r p) ∙ ap (λ w → p ∙ w) (! (!-inv-l p))
              coh idp = idp

  decode' : {x : Susp A} → Codes x → P x
  decode' {x} = Susp-elim {P = λ x → Codes x → P x}
                 decodeN'
                 (λ a → [ merid a ])
                 (λ a → ↓-→-from-transp (λ= $ STS a))
                 x
    where
    abstract
      STS : (a a' : A) → transport P (merid a) (decodeN' a')
                         == [ merid (transport Codes (merid a) a') ]
      STS a a' =
        transport P (merid a) [ merid a' ∙ ! (merid e) ]
          =⟨ transport-Trunc (north ==_) (merid a) _ ⟩
        [ transport (north ==_) (merid a) (merid a' ∙ ! (merid e)) ]
          =⟨ ap [_] (transp-cst=idf {A = Susp A} (merid a) _) ⟩
        [ (merid a' ∙ ! (merid e)) ∙ merid a ]
          =⟨ ap [_] (∙-assoc (merid a') (! (merid e)) (merid a)) ⟩
        [ merid a' ∙ ! (merid e) ∙ merid a ]
          =⟨ ! (homomorphism a a') ⟩
        [ merid (μ a a') ]
          =⟨ ap ([_] ∘ merid) (! (transport-Codes-mer a a')) ⟩
        [ merid (transport Codes (merid a) a') ] ∎

  abstract
    decode'-encode' : {x : Susp A} (tα : P x)
      → decode' {x} (encode' {x} tα) == tα
    decode'-encode' {x} = Trunc-elim
      {P = λ tα → decode' {x} (encode' {x} tα) == tα}
      -- FIXME: Agda very slow (looping?) when omitting the next line
      {{λ _ → =-preserves-level Trunc-level}}
      (J (λ y p → decode' {y} (encode' {y} [ p ]) == [ p ])
         (ap [_] (!-inv-r (merid e))))

  eq' : Trunc 1 (Ω (⊙Susp X)) ≃ A
  eq' = equiv encode' decodeN' encode'-decodeN' decode'-encode'

  ⊙decodeN : ⊙Trunc 1 X ⊙→ ⊙Trunc 1 (⊙Ω (⊙Susp X))
  ⊙decodeN = ⊙Trunc-fmap (SAL.η X)

  decodeN : Trunc 1 A → Trunc 1 (Ω (⊙Susp X))
  decodeN = fst ⊙decodeN

  encodeN : Trunc 1 (Ω (⊙Susp X)) → Trunc 1 A
  encodeN = [_] ∘ encode' {x = north}

  eq : Trunc 1 (Ω (⊙Susp X)) ≃ Trunc 1 A
  eq = encodeN ,
    replace-inverse (snd ((unTrunc-equiv A)⁻¹ ∘e eq'))
      {decodeN}
      (Trunc-elim (λ _ → idp))

  ⊙eq : ⊙Trunc 1 (⊙Ω (⊙Susp X)) ⊙≃ ⊙Trunc 1 X
  ⊙eq = ≃-to-⊙≃ eq idp

  ⊙eq⁻¹ : ⊙Trunc 1 X ⊙≃ ⊙Trunc 1 (⊙Ω (⊙Susp X))
  ⊙eq⁻¹ = ⊙decodeN , snd (eq ⁻¹)

  iso : Ω^S-group 0 (⊙Trunc 1 (⊙Ω (⊙Susp X)))
      ≃ᴳ Ω^S-group 0 (⊙Trunc 1 X)
  iso = Ω^S-group-emap 0 ⊙eq

  abstract
    π₂-Susp : πS 1 (⊙Susp X) ≃ᴳ πS 0 X
    π₂-Susp =
      πS 1 (⊙Susp X)
        ≃ᴳ⟨ πS-Ω-split-iso 0 (⊙Susp X) ⟩
      πS 0 (⊙Ω (⊙Susp X))
        ≃ᴳ⟨ Ω^S-group-Trunc-fuse-diag-iso 0 (⊙Ω (⊙Susp X)) ⁻¹ᴳ ⟩
      Ω^S-group 0 (⊙Trunc 1 (⊙Ω (⊙Susp X)))
        ≃ᴳ⟨ iso ⟩
      Ω^S-group 0 (⊙Trunc 1 X)
        ≃ᴳ⟨ Ω^S-group-Trunc-fuse-diag-iso 0 X ⟩
      πS 0 X ≃ᴳ∎


{-# OPTIONS --cubical #-}

open import Agda.Builtin.Cubical.Path
open import Agda.Primitive
open import Agda.Primitive.Cubical
open import Agda.Builtin.Bool

variable
  a p         : Level
  A           : Set a
  P           : A → Set p
  x y : A

refl : x ≡ x
refl {x = x} = λ _ → x

data 𝕊¹ : Set where
  base : 𝕊¹
  loop : base ≡ base

g : Bool → I → 𝕊¹
g true i = base
g false i = loop i

_ : g false i0 ≡ base
_ = refl

_ : g true i0 ≡ base
_ = refl

-- non-unique solutions, should not solve either of the metas.
_ : g _ _ ≡ base
_ = refl

h : Bool → base ≡ base
h true = \ _ → base
h false = loop

_ : h false i0 ≡ base
_ = refl

_ : h true i0 ≡ base
_ = refl

-- non-unique solutions, should not solve either of the metas.
_ : h _ _ ≡ base
_ = refl


------------------------------------------------------------------------
-- The Agda standard library
--
-- A categorical view of Delay
------------------------------------------------------------------------

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

module Codata.Delay.Categorical where

open import Codata.Delay
open import Function
open import Category.Functor
open import Category.Applicative
open import Category.Monad
open import Data.These using (leftMost)

functor : ∀ {i ℓ} → RawFunctor {ℓ} (λ A → Delay A i)
functor = record { _<$>_ = λ f → map f }

module Sequential where

  applicative : ∀ {i ℓ} → RawApplicative {ℓ} (λ A → Delay A i)
  applicative = record
    { pure = now
    ; _⊛_  = λ df da → bind df (λ f → map f da)
    }

  applicativeZero : ∀ {i ℓ} → RawApplicativeZero {ℓ} (λ A → Delay A i)
  applicativeZero = record
    { applicative = applicative
    ; ∅           = never
    }

  monad : ∀ {i ℓ} → RawMonad {ℓ} (λ A → Delay A i)
  monad = record
    { return = now
    ; _>>=_  = bind
    }

  monadZero : ∀ {i ℓ} → RawMonadZero {ℓ} (λ A → Delay A i)
  monadZero = record
    { monad           = monad
    ; applicativeZero = applicativeZero
    }

module Zippy where

  applicative : ∀ {i ℓ} → RawApplicative {ℓ} (λ A → Delay A i)
  applicative = record
    { pure = now
    ; _⊛_  = zipWith id
    }

  applicativeZero : ∀ {i ℓ} → RawApplicativeZero {ℓ} (λ A → Delay A i)
  applicativeZero = record
    { applicative = applicative
    ; ∅           = never
    }

  alternative : ∀ {i ℓ} → RawAlternative {ℓ} (λ A → Delay A i)
  alternative = record
    { applicativeZero = applicativeZero
    ; _∣_             = alignWith leftMost
    }


module Context where

open import Level using (_⊔_)
open import Function
open import Data.List.Base

-- | `ifix`. Check `docs/fomega/deep-isorecursive/README.md` for details.
{-# NO_POSITIVITY_CHECK #-}
data IFix {ι φ} {I : Set ι} (F : (I -> Set φ) -> I -> Set φ) (i : I) : Set φ where
  Wrap : F (IFix F) i -> IFix F i

infix  3 _∈_ _⊆_
infixl 5 _▻_ _▻▻_ _▶_ _▷_

-- | A context is a snoc-list.
data Con {α} (A : Set α) : Set α where
  ε   : Con A
  _▻_ : (Γ : Con A) -> A -> Con A

-- | The left fold over a context. Note that for contexts, the left fold is the lazy one,
-- i.e. it's preferable.
foldlᶜ : ∀ {α ρ} {A : Set α} {R : Set ρ} -> (R -> A -> R) -> R -> Con A -> R
foldlᶜ f z  ε      = z
foldlᶜ f z (Γ ▻ σ) = f (foldlᶜ f z Γ) σ

-- | Concatenate two contexts.
_▻▻_ : ∀ {α} {A : Set α} -> Con A -> Con A -> Con A
Γ ▻▻ Δ = foldlᶜ _▻_ Γ Δ
{-# DISPLAY foldlᶜ _▻_ Γ Δ = Γ ▻▻ Δ #-}

-- | The right fold over a context. Note that for contexts, the right fold is the strict one,
-- i.e. it's not preferable.
foldrᶜ : ∀ {α ρ} {A : Set α} {R : Set ρ} -> (A -> R -> R) -> R -> Con A -> R
foldrᶜ f z  ε      = z
foldrᶜ f z (Γ ▻ σ) = foldrᶜ f (f σ z) Γ

-- | Mapping over a context.
mapᶜ : ∀ {α β} {A : Set α} {B : Set β} -> (A -> B) -> Con A -> Con B
mapᶜ f = foldlᶜ (λ Ξ x -> Ξ ▻ f x) ε

joinᶜ : ∀ {α} {A : Set α} -> Con (Con A) -> Con A
joinᶜ = foldlᶜ _▻▻_ ε

listToCon : ∀ {α} {A : Set α} -> List A -> Con A
listToCon = foldl _▻_ ε
{-# DISPLAY foldl _▻_ ε = listToCon #-}

-- | A variable is a point in a context.
data _∈_ {α} {A : Set α} σ : Con A -> Set where
  vz  : ∀ {Γ}   -> σ ∈ Γ ▻ σ
  vs_ : ∀ {Γ τ} -> (v : σ ∈ Γ) -> σ ∈ Γ ▻ τ

-- | Expand the context that a variable is defined in, but do not change the variable.
wkᵛ : ∀ {α} {A : Set α} {Γ Δ : Con A} {σ} -> σ ∈ Δ -> σ ∈ Γ ▻▻ Δ
wkᵛ  vz    = vz
wkᵛ (vs v) = vs (wkᵛ v)

-- | A variable in the middle of a context.
vsⁿ : ∀ {α} {A : Set α} {Θ : Con A} {σ} Ξ -> σ ∈ Θ ▻ σ ▻▻ Ξ
vsⁿ  ε      = vz
vsⁿ (Ξ ▻ ν) = vs (vsⁿ Ξ)

mapᵛ : ∀ {α β} {A : Set α} {B : Set β} {Γ σ} {f : A -> B} -> σ ∈ Γ -> f σ ∈ mapᶜ f Γ
mapᵛ  vz    = vz
mapᵛ (vs v) = vs (mapᵛ v)

-- -- | Order-preserving embedding. I.e. in `Γ ⊆ Δ` `Δ` contains the same elements as `Γ` and
-- -- they're in the same order, but there can be additional elements between them.
-- -- See https://fplab.bitbucket.io/posts/2008-03-07-order-preserving-embeddin.html
-- data _⊆_ {α} {A : Set α} : Con A -> Con A -> Set where
--   -- We use this instead of `ε ⊆ ε`, because we this definition we have `idᵒ ∘ᵒ idᵒ ≡ idᵒ`
--   -- definitionally and this is needed in order to type check some terms with open types.
--   stop : ∀ {Γ}     ->                Γ         ⊆ Γ
--   skip : ∀ {Γ Δ σ} -> (ι : Γ ⊆ Δ) -> Γ         ⊆ Δ ▻ σ
--   keep : ∀ {Γ Δ σ} -> (ι : Γ ⊆ Δ) -> Γ ▻ σ     ⊆ Δ ▻ σ

_⊆_ : ∀ {α} {A : Set α} -> Con A -> Con A -> Set α
Γ ⊆ Δ = ∀ {σ} -> σ ∈ Γ -> σ ∈ Δ

stop : ∀ {α} {A : Set α} {Γ : Con A} -> Γ ⊆ Γ
stop = id

dupl : ∀ {α} {A : Set α} {Γ : Con A} {σ} -> Γ ▻ σ ▻ σ ⊆ Γ ▻ σ
dupl  vz    = vz
dupl (vs v) = v

skip : ∀ {α} {A : Set α} {Γ Δ : Con A} {σ} -> Γ ⊆ Δ -> Γ ⊆ Δ ▻ σ
skip ι = vs_ ∘ ι

keep : ∀ {α} {A : Set α} {Γ Δ : Con A} {σ} -> Γ ⊆ Δ -> Γ ▻ σ ⊆ Δ ▻ σ
keep ι  vz    = vz
keep ι (vs v) = vs (ι v)

move : ∀ {α} {A : Set α} {Γ Δ : Con A} {σ} -> Γ ⊆ Δ -> Γ ▻ σ ▻ σ ⊆ Δ ▻ σ
move ι  vz    = vz
move ι (vs v) = keep ι v

skipⁿ : ∀ {α} {A : Set α} {Γ Δ : Con A} Ξ -> Γ ⊆ Δ -> Γ ⊆ Δ ▻▻ Ξ
skipⁿ  ε      ι = ι
skipⁿ (Ξ ▻ ν) ι = skip (skipⁿ Ξ ι)

keepⁿ : ∀ {α} {A : Set α} {Γ Δ : Con A} Ξ -> Γ ⊆ Δ -> Γ ▻▻ Ξ ⊆ Δ ▻▻ Ξ
keepⁿ  ε      ι = ι
keepⁿ (Ξ ▻ ν) ι = keep (keepⁿ Ξ ι)

empᵒ : ∀ {α} {A : Set α} {Γ : Con A} -> ε ⊆ Γ
empᵒ {Γ = ε}     = stop
empᵒ {Γ = Γ ▻ σ} = skip empᵒ

-- | The identity embedding.
idᵒ : ∀ {α} {A : Set α} {Γ : Con A} -> Γ ⊆ Γ
idᵒ = stop

-- | The left extension embedding.
extˡ : ∀ {α} {A : Set α} {Γ Δ : Con A} -> Γ ⊆ Δ ▻▻ Γ
extˡ {Γ = ε}     = empᵒ
extˡ {Γ = Γ ▻ σ} = keep extˡ

-- | The right extension embedding.
extʳ : ∀ {α} {A : Set α} {Γ : Con A} Δ -> Γ ⊆ Γ ▻▻ Δ
extʳ Δ = skipⁿ Δ idᵒ

-- | The one element extension embedding.
topᵒ : ∀ {α} {A : Set α} {Γ : Con A} {σ} -> Γ ⊆ Γ ▻ σ
topᵒ = extʳ (ε ▻ _)

mapᶜ-⊆ : ∀ {α β} {A : Set α} {B : Set β} {Γ Δ} {f : A -> B} -> Γ ⊆ Δ -> mapᶜ f Γ ⊆ mapᶜ f Δ
mapᶜ-⊆ {Γ = ε}     ι  ()
mapᶜ-⊆ {Γ = Γ ▻ σ} ι  vz    = mapᵛ (ι vz)
mapᶜ-⊆ {Γ = Γ ▻ σ} ι (vs v) = mapᶜ-⊆ (ι ∘ vs_) v

-- | Rename a variable.
renᵛ : ∀ {α} {A : Set α} {Γ Δ : Con A} {σ} -> Γ ⊆ Δ -> σ ∈ Γ -> σ ∈ Δ
renᵛ ι = ι

shiftᵛⁿ : ∀ {α} {A : Set α} {Γ : Con A} {σ} Δ -> σ ∈ Γ -> σ ∈ Γ ▻▻ Δ
shiftᵛⁿ Δ = renᵛ (extʳ Δ)

shiftᵛ : ∀ {α} {A : Set α} {Γ : Con A} {σ τ} -> σ ∈ Γ -> σ ∈ Γ ▻ τ
shiftᵛ = shiftᵛⁿ (ε ▻ _)

-- We need both first-order and higher-order environments, because the former are more convenient
-- for compilation and the latter are more convenient for evaluation at the type level.

-- | In `Seq P Γ` there a `P σ` for each `σ` from `Γ`.
data Seq {α π} {A : Set α} (P : A -> Set π) : Con A -> Set π where
  ø   : Seq P ε
  _▶_ : ∀ {σ Γ} -> Seq P Γ -> P σ -> Seq P (Γ ▻ σ)

-- We wrap this in `record` in order to get good inference.
-- | In `Env P Γ` there a `P σ` for each `σ` from `Γ`.
record Env {α π} {A : Set α} (P : A -> Set π) (Γ : Con A) : Set (α ⊔ π) where
  constructor PackEnv
  field unpackEnv : ∀ {σ} -> σ ∈ Γ -> P σ
open Env public

foldlˢ
  : ∀ {α π ρ} {A : Set α} {P : A -> Set π} {Γ}
  -> (R : Con A -> Set ρ) -> (∀ {Ξ σ} -> R Ξ -> P σ -> R (Ξ ▻ σ)) -> R ε -> Seq P Γ -> R Γ
foldlˢ R f z  ø      = z
foldlˢ R f z (ρ ▶ α) = f (foldlˢ R f z ρ) α

-- | Apply a natural transformation to each element of a `Seq`.
mapˢ
  : ∀ {α π₁ π₂} {A : Set α} {P₁ : A -> Set π₁} {P₂ : A -> Set π₂} {Γ}
  -> (∀ {σ} -> P₁ σ -> P₂ σ) -> Seq P₁ Γ -> Seq P₂ Γ
mapˢ f = foldlˢ (Seq _) (λ ρ x -> ρ ▶ f x) ø

mapElˢ
  : ∀ {α π₁ π₂} {A : Set α} {P₁ : A -> Set π₁} {P₂ : A -> Set π₂} {Γ}
  -> (∀ {σ} -> σ ∈ Γ -> P₁ σ -> P₂ σ) -> Seq P₁ Γ -> Seq P₂ Γ
mapElˢ f  ø      = ø
mapElˢ f (ρ ▶ α) = mapElˢ (f ∘ vs_) ρ ▶ f vz α

eraseˢ : ∀ {α π} {A : Set α} {P : Set π} {Γ : Con A} -> Seq (λ _ -> P) Γ -> Con P
eraseˢ = foldlˢ _ _▻_ ε

∅ : ∀ {α π} {A : Set α} {P : A -> Set π} -> Env P ε
∅ = PackEnv λ()

_▷_ : ∀ {α π} {A : Set α} {Γ σ} {P : A -> Set π} -> Env P Γ -> P σ -> Env P (Γ ▻ σ)
PackEnv ρ ▷ α = PackEnv λ
  {  vz    -> α
  ; (vs v) -> ρ v
  }

_∘ᵉ_ : ∀ {α π} {A : Set α} {Γ Δ} {P : A -> Set π} -> Env P Δ -> Γ ⊆ Δ -> Env P Γ
PackEnv ρ ∘ᵉ ι = PackEnv (ρ ∘ ι)

withSplitᵉ
  : ∀ {α π ρ} {A : Set α} {Γ σ} {P : A -> Set π} {R : Set ρ}
  -> Env P (Γ ▻ σ) -> (Env P Γ -> P σ -> R) -> R
withSplitᵉ (PackEnv ρ) k = k (PackEnv (ρ ∘ vs_)) (ρ vz)

foldlᵉ
  : ∀ {α π ρ} {A : Set α} {P : A -> Set π} {Γ}
  -> (R : Con A -> Set ρ) -> (∀ {Ξ σ} -> R Ξ -> P σ -> R (Ξ ▻ σ)) -> R ε -> Env P Γ -> R Γ
foldlᵉ {Γ = ε}     R f z ρ = z
foldlᵉ {Γ = Γ ▻ σ} R f z ρ = withSplitᵉ ρ λ ρ′ α -> f (foldlᵉ R f z ρ′) α

-- | Apply a natural transformation to each element of a `Env`.
mapᵉ
  : ∀ {α π₁ π₂} {A : Set α} {P₁ : A -> Set π₁} {P₂ : A -> Set π₂} {Γ}
  -> (∀ {σ} -> P₁ σ -> P₂ σ) -> Env P₁ Γ -> Env P₂ Γ
mapᵉ f = foldlᵉ (Env _) (λ ρ x -> ρ ▷ f x) ∅

mapElᵉ
  : ∀ {α π₁ π₂} {A : Set α} {P₁ : A -> Set π₁} {P₂ : A -> Set π₂} {Γ}
  -> (∀ {σ} -> σ ∈ Γ -> P₁ σ -> P₂ σ) -> Env P₁ Γ -> Env P₂ Γ
mapElᵉ f (PackEnv ρ) = PackEnv λ v -> f v (ρ v)

-- | Look up a variable in an environment.
lookupᵉ : ∀ {α π} {A : Set α} {P : A -> Set π} {Γ σ} -> σ ∈ Γ -> Env P Γ -> P σ
lookupᵉ v (PackEnv ρ) = ρ v

eraseᵉ : ∀ {α π} {A : Set α} {P : Set π} {Γ : Con A} -> Env (λ _ -> P) Γ -> Con P
eraseᵉ = foldlᵉ _ _▻_ ε

record Environment {α β} {A : Set α} (_◆_ : Con A -> A -> Set β) : Set (α ⊔ β) where
  infix 3 _⊢ᵉ_

  field
    varᵈ : ∀ {Γ σ} -> σ ∈ Γ -> Γ ◆ σ
    renᵈ : ∀ {Γ Δ σ} -> Γ ⊆ Δ -> Γ ◆ σ -> Δ ◆ σ

  idᵉ : ∀ {Γ} -> Env (Γ ◆_) Γ
  idᵉ = PackEnv varᵈ

  freshᵉ : ∀ {Γ σ} -> (Γ ▻ σ) ◆ σ
  freshᵉ = varᵈ vz

  _⊢ᵉ_ : Con A -> Con A -> Set (α ⊔ β)
  Ξ ⊢ᵉ Θ = Env (λ σ -> Ξ ◆ σ) Θ

  renᵉ : ∀ {Δ Ξ Γ} -> Δ ⊆ Ξ -> Δ ⊢ᵉ Γ -> Ξ ⊢ᵉ Γ
  renᵉ ι (PackEnv ρ) = PackEnv $ renᵈ ι ∘ ρ

  keepᵉ : ∀ {Δ Γ σ} -> Δ ⊢ᵉ Γ -> Δ ▻ σ ⊢ᵉ Γ ▻ σ
  keepᵉ ρ = renᵉ topᵒ ρ ▷ freshᵉ

  topᵉ : ∀ {Γ σ} -> Γ ◆ σ -> Γ ⊢ᵉ Γ ▻ σ
  topᵉ t = idᵉ ▷ t

  shiftᵉ : ∀ {Δ Γ τ} -> Δ ⊢ᵉ Γ -> Δ ▻ τ ⊢ᵉ Γ
  shiftᵉ = renᵉ topᵒ


------------------------------------------------------------------------
-- Parallel reduction in pure type systems (PTS)
------------------------------------------------------------------------

module Pts.Reduction.Parallel where

open import Data.Fin using (Fin)
open import Data.Fin.Substitution
open import Data.Fin.Substitution.ExtraLemmas
open import Data.Fin.Substitution.Relation
open import Data.Star using (Star; ε; _◅_; _◅◅_)
open import Data.Product using (_,_; ∃; _×_)
open import Data.Nat using (ℕ; _+_)
open import Data.Vec.All using (lookup₂)
import Function as Fun
open import Relation.Binary
open import Relation.Binary.EquivalenceClosure using (EqClosure)
open import Relation.Binary.SymmetricClosure using (fwd; bwd)
open import Relation.Binary.Reduction
import Relation.Binary.PropositionalEquality as P

open import Pts.Syntax

-- All remaining submodules are parametrized by a given set of sorts.
module _ {Sort : Set} where
  open Syntax       Sort
  open Substitution Sort using (_[_])

  ----------------------------------------------------------------------
  -- Parallel reduction

  infixl 9 _·_
  infix  5 _⇛_

  -- One-step parallel reduction.
  data _⇛_ {n : ℕ} : Term n → Term n → Set where
    refl : ∀ {a}                                 →       a ⇛ a
    Π    : ∀ {a₁ a₂ b₁ b₂}   → a₁ ⇛ a₂ → b₁ ⇛ b₂ → Π a₁ b₁ ⇛ Π a₂ b₂
    ƛ    : ∀ {a₁ a₂ b₁ b₂}   → a₁ ⇛ a₂ → b₁ ⇛ b₂ → ƛ a₁ b₁ ⇛ ƛ a₂ b₂
    _·_  : ∀ {a₁ a₂ b₁ b₂}   → a₁ ⇛ a₂ → b₁ ⇛ b₂ → a₁ · b₁ ⇛ a₂ · b₂
    cont : ∀ {a b₁ b₂ c₁ c₂} → b₁ ⇛ b₂ → c₁ ⇛ c₂ →
           (ƛ a b₁) · c₁ ⇛ b₂ [ c₂ ]

  reduction : Reduction Term
  reduction = record { _→1_ = _⇛_ }

  -- Parallel reduction and equivalence.
  open Reduction reduction public renaming (_→*_ to _⇛*_; _↔_ to _≡p_)

  ----------------------------------------------------------------------
  -- Simple properties of parallel reduction

  -- Inclusions.
  ⇛⇒⇛*  = →1⇒→* reduction
  ⇛*⇒≡p = →*⇒↔  reduction
  ⇛⇒≡p  = →1⇒↔  reduction

  -- parallel reduction is a preorder.
  ⇛*-predorder = →*-predorder reduction

  -- Terms together with parallel equivalence form a setoid.
  ≡p-setoid = ↔-setoid reduction

  open P using (_≡_)

  -- Shapes are preserved by one-step parallel reduction.

  sort-⇛ : ∀ {n s} {a : Term n} → sort s ⇛ a → sort s ≡ a
  sort-⇛ refl = P.refl

  ƛ-⇛ : ∀ {n} {a : Term n} {b c} → ƛ a b ⇛ c →
        ∃ λ a′ → ∃ λ b′ → a ⇛ a′ × b ⇛ b′ × ƛ a′ b′ ≡ c
  ƛ-⇛ refl          = _ , _ , refl , refl , P.refl
  ƛ-⇛ (ƛ a⇛a′ b⇛b′) = _ , _ , a⇛a′ , b⇛b′ , P.refl

  Π-⇛ : ∀ {n} {a : Term n} {b c} → Π a b ⇛ c →
        ∃ λ a′ → ∃ λ b′ → a ⇛ a′ × b ⇛ b′ × Π a′ b′ ≡ c
  Π-⇛ refl          = _ , _ , refl , refl , P.refl
  Π-⇛ (Π a⇛a′ b⇛b′) = _ , _ , a⇛a′ , b⇛b′ , P.refl

  -- Shapes are preserved by parallel reduction.

  sort-⇛* : ∀ {n s} {a : Term n} → sort s ⇛* a → sort s ≡ a
  sort-⇛* ε             = P.refl
  sort-⇛* (refl ◅ s⇛*b) = sort-⇛* s⇛*b

  ƛ-⇛* : ∀ {n} {a : Term n} {b c} → ƛ a b ⇛* c →
         ∃ λ a′ → ∃ λ b′ → a ⇛* a′ × b ⇛* b′ × ƛ a′ b′ ≡ c
  ƛ-⇛* ε                        = _ , _ , ε , ε , P.refl
  ƛ-⇛* (refl        ◅ λab⇛*d)   = ƛ-⇛* λab⇛*d
  ƛ-⇛* (ƛ a⇛a₁ b⇛b₁ ◅ λa₁b₁⇛*d) =
    let a₂ , b₂ , a₁⇛*a₂ , b₁⇛*b₂ , λa₂b₂≡d = ƛ-⇛* λa₁b₁⇛*d
    in a₂ , b₂ , a⇛a₁ ◅ a₁⇛*a₂ , b⇛b₁ ◅ b₁⇛*b₂ , λa₂b₂≡d

  Π-⇛* : ∀ {n} {a : Term n} {b c} → Π a b ⇛* c →
         ∃ λ a′ → ∃ λ b′ → a ⇛* a′ × b ⇛* b′ × Π a′ b′ ≡ c
  Π-⇛* ε                        = _ , _ , ε , ε , P.refl
  Π-⇛* (refl        ◅ Πab⇛*d)   = Π-⇛* Πab⇛*d
  Π-⇛* (Π a⇛a₁ b⇛b₁ ◅ Πa₁b₁⇛*d) =
    let a₂ , b₂ , a₁⇛*a₂ , b₁⇛*b₂ , Πa₂b₂≡d = Π-⇛* Πa₁b₁⇛*d
    in a₂ , b₂ , a⇛a₁ ◅ a₁⇛*a₂ , b⇛b₁ ◅ b₁⇛*b₂ , Πa₂b₂≡d

  ----------------------------------------------------------------------
  -- Substitutions lifted to parallel reduction
  --
  -- The application _/⇛_ below may be considered a substitution
  -- lemma, i.e. it establishes that substitutions of terms preserve
  -- parallel reduction:
  --
  --                                ⇛
  --                           a ------→ b
  --                           |         |
  --                       -/σ |         | -/σ
  --                           ↓    ⇛    ↓
  --                          a/σ ····→ b/σ
  --
  -- ∀ (a b : Term n) (σ : Sub Term m n).

  -- Application of generic substitutions lifted to reduction.
  record ParSubstApp {T₁ T₂} (R : TermRel T₁ T₂)
                     (l₁ : Lift T₁ Term) (l₂ : Lift T₂ Term)
                     (rl : RelLift R _⇛_ l₁ l₂) : Set where
    infix  10 _↑₂
    infixl 8  _/₁_ _/₂_ _/_ _/⇛_

    open Substitution Sort using (termSubst)
    private
      _/₁_ = TermSubst.app termSubst l₁
      _/₂_ = TermSubst.app termSubst l₂
      _↑₂  = Lift._↑ l₂
    open LiftTermRel T₁ T₂ using (_⟨_⟩_)
    open RelLift rl
    open P using (sym; subst)

    -- T₂-substitutions commute.
    field /₂-sub-↑ : ∀ {m n b} {σ : Sub T₂ m n} a →
                     (a [ b ]) /₂ σ ≡ (a /₂ (σ ↑₂)) [ b /₂ σ ]

    _/_ : ∀ {m n} {σ₁ : Sub T₁ m n} {σ₂ : Sub T₂ m n} →
          ∀ a → σ₁ ⟨ R ⟩ σ₂ → a /₁ σ₁ ⇛ a /₂ σ₂
    var x   / σ₁∼σ₂ = lift (lookup₂ x σ₁∼σ₂)
    sort s  / σ₁∼σ₂ = refl
    Π a b   / σ₁∼σ₂ = Π (a / σ₁∼σ₂) (b / σ₁∼σ₂ ↑)
    ƛ a b   / σ₁∼σ₂ = ƛ (a / σ₁∼σ₂) (b / σ₁∼σ₂ ↑)
    (a · b) / σ₁∼σ₂ = (a / σ₁∼σ₂) · (b / σ₁∼σ₂)

    _/⇛_ : ∀ {m n a₁ a₂} {σ₁ : Sub T₁ m n} {σ₂ : Sub T₂ m n} →
           a₁ ⇛ a₂ → σ₁ ⟨ R ⟩ σ₂ → a₁ /₁ σ₁ ⇛ a₂ /₂ σ₂
    refl {a}      /⇛ σ₁∼σ₂ = a / σ₁∼σ₂
    Π a₁⇛a₂ b₁⇛b₂ /⇛ σ₁∼σ₂ = Π (a₁⇛a₂ /⇛ σ₁∼σ₂) (b₁⇛b₂ /⇛ σ₁∼σ₂ ↑)
    ƛ a₁⇛a₂ b₁⇛b₂ /⇛ σ₁∼σ₂ = ƛ (a₁⇛a₂ /⇛ σ₁∼σ₂) (b₁⇛b₂ /⇛ σ₁∼σ₂ ↑)
    a₁⇛a₂ · b₁⇛b₂ /⇛ σ₁∼σ₂ = (a₁⇛a₂ /⇛ σ₁∼σ₂) · (b₁⇛b₂ /⇛ σ₁∼σ₂)
    cont {b₂ = b₂} a₁⇛a₂ b₁⇛b₂ /⇛ σ₁∼σ₂ =
      P.subst (_⇛_ _) (sym (/₂-sub-↑ b₂))
                   (cont (a₁⇛a₂ /⇛ σ₁∼σ₂ ↑) (b₁⇛b₂ /⇛ σ₁∼σ₂))

  -- Term substitutions lifted to parallel reduction.
  module ParSubstitution where
    open Substitution Sort using (termSubst; sub-commutes; varLiftSubLemmas)
    open P using (_≡_; refl)
    private
      module S = TermSubst termSubst
      module V = VarEqSubst

    varLift : RelLift _≡_ _⇛_ S.varLift S.varLift
    varLift = record { simple = V.simple; lift = lift }
      where
        lift : ∀ {n} {x₁ x₂ : Fin n} → x₁ ≡ x₂ → S.var x₁ ⇛ S.var x₂
        lift {x₁ = x} refl = refl

    varSubstApp : ParSubstApp _≡_ S.varLift S.varLift varLift
    varSubstApp = record { /₂-sub-↑ = λ a → /-sub-↑ a _ _ }
      where open LiftSubLemmas varLiftSubLemmas

    infix 8 _/Var-⇛_

    _/Var-⇛_ : ∀ {m n a₁ a₂} {σ₁ σ₂ : Sub Fin m n} →
               a₁ ⇛ a₂ → σ₁ V.⟨≡⟩ σ₂ → (a₁ S./Var σ₁) ⇛ (a₂ S./Var σ₂)
    _/Var-⇛_ = ParSubstApp._/⇛_ varSubstApp

    simple : RelSimple _⇛_ S.simple S.simple
    simple = record
      { extension = record { weaken = λ t₁⇛t₂ → t₁⇛t₂ /Var-⇛ V.wk }
      ; var       = λ x → refl {a = var x}
      }

    termLift : RelLift _⇛_ _⇛_ S.termLift S.termLift
    termLift = record { simple = simple; lift = Fun.id }

    termSubstApp : ParSubstApp _⇛_ S.termLift S.termLift termLift
    termSubstApp = record { /₂-sub-↑ = sub-commutes }

    open ParSubstApp termSubstApp public

    subst : RelSubst _⇛_ S.subst S.subst
    subst = record
      { simple      = simple
      ; application = record { _/_ = _/⇛_ }
      }

    open LiftTermRel Term Term public using (_⟨_⟩_)
    open RelSubst subst public hiding (var; simple; _/_)

    infix 10 _[⇛_]

    -- Shorthand for single-variable substitutions lifted to redcution.
    _[⇛_] : ∀ {n} {a₁ : Term (1 + n)} {a₂} {b₁ : Term n} {b₂} →
            a₁ ⇛ a₂ → b₁ ⇛ b₂ → a₁ S./ S.sub b₁ ⇛ a₂ S./ S.sub b₂
    a₁⇛a₂ [⇛ b₁⇛b₂ ] = a₁⇛a₂ /⇛ sub b₁⇛b₂

  open ParSubstitution

  ----------------------------------------------------------------------
  -- Confluence of parallel reduction
  --
  -- Parallel reduction enjoys the single-step diamond property,
  -- i.e. for any pair a ⇛ b₁, a ⇛ b₂ of parallel reductions, there is
  -- a term c, such that
  --
  --                                ⇛
  --                           a ------→ b₂
  --                           |         :
  --                         ⇛ |         : ⇛
  --                           ↓    ⇛    ↓
  --                           b₁ ·····→ c
  --
  -- commutes.  Confluence (aka the Church-Rosser property) then
  -- follows by pasting of diagrams.

  infixl 4 _⋄_

  -- Diamond property of one-step reduction.
  _⋄_ : ∀ {n} {a b₁ b₂ : Term n} → a ⇛ b₁ → a ⇛ b₂ → ∃ λ c → b₁ ⇛ c × b₂ ⇛ c
  refl ⋄ a⇛b  = _ , a⇛b  , refl
  a⇛b  ⋄ refl = _ , refl , a⇛b
  Π a₁⇛b₁₁ a₂⇛b₁₂ ⋄ Π a₁⇛b₂₁ a₂⇛b₂₂ =
    let c₁ , b₁₁⇛c₁ , b₂₁⇛c₁ = a₁⇛b₁₁ ⋄ a₁⇛b₂₁
        c₂ , b₁₂⇛c₂ , b₂₂⇛c₂ = a₂⇛b₁₂ ⋄ a₂⇛b₂₂
    in Π c₁ c₂ , Π b₁₁⇛c₁ b₁₂⇛c₂ , Π b₂₁⇛c₁ b₂₂⇛c₂
  ƛ a₁⇛b₁₁ a₂⇛b₁₂ ⋄ ƛ a₁⇛b₂₁ a₂⇛b₂₂ =
    let c₁ , b₁₁⇛c₁ , b₂₁⇛c₁ = a₁⇛b₁₁ ⋄ a₁⇛b₂₁
        c₂ , b₁₂⇛c₂ , b₂₂⇛c₂ = a₂⇛b₁₂ ⋄ a₂⇛b₂₂
    in ƛ c₁ c₂ , ƛ b₁₁⇛c₁ b₁₂⇛c₂ , ƛ b₂₁⇛c₁ b₂₂⇛c₂
  a₁⇛b₁₁ · a₂⇛b₁₂ ⋄ a₁⇛b₂₁ · a₂⇛b₂₂ =
    let c₁ , b₁₁⇛c₁ , b₂₁⇛c₁ = a₁⇛b₁₁ ⋄ a₁⇛b₂₁
        c₂ , b₁₂⇛c₂ , b₂₂⇛c₂ = a₂⇛b₁₂ ⋄ a₂⇛b₂₂
    in c₁ · c₂ , b₁₁⇛c₁ · b₁₂⇛c₂ , b₂₁⇛c₁ · b₂₂⇛c₂
  refl · a₂⇛b₁₂ ⋄ cont {b₂ = b₂₁} a₁⇛b₂₁ a₂⇛b₂₂ =
    let c₂ , b₁₂⇛c₂ , b₂₂⇛c₂ = a₂⇛b₁₂ ⋄ a₂⇛b₂₂
    in b₂₁ [ c₂ ] , cont a₁⇛b₂₁ b₁₂⇛c₂ , refl {a = b₂₁} [⇛ b₂₂⇛c₂ ]
  ƛ _ a₁⇛b₁₁ · a₂⇛b₁₂ ⋄ cont a₁⇛b₂₁ a₂⇛b₂₂ =
    let c₁ , b₁₁⇛c₁ , b₂₁⇛c₁ = a₁⇛b₁₁ ⋄ a₁⇛b₂₁
        c₂ , b₁₂⇛c₂ , b₂₂⇛c₂ = a₂⇛b₁₂ ⋄ a₂⇛b₂₂
    in c₁ [ c₂ ] , cont b₁₁⇛c₁ b₁₂⇛c₂ , b₂₁⇛c₁ [⇛ b₂₂⇛c₂ ]
  cont {b₂ = b₁₁} a₁⇛b₁₁ a₂⇛b₁₂ ⋄ refl · a₂⇛b₂₂ =
    let c₂ , b₁₂⇛c₂ , b₂₂⇛c₂ = a₂⇛b₁₂ ⋄ a₂⇛b₂₂
    in b₁₁ [ c₂ ] , refl {a = b₁₁} [⇛ b₁₂⇛c₂ ] , cont a₁⇛b₁₁ b₂₂⇛c₂
  cont a₁⇛b₁₁ a₂⇛b₁₂ ⋄ ƛ _ a₁⇛b₂₁ · a₂⇛b₂₂ =
    let c₁ , b₁₁⇛c₁ , b₂₁⇛c₁ = a₁⇛b₁₁ ⋄ a₁⇛b₂₁
        c₂ , b₁₂⇛c₂ , b₂₂⇛c₂ = a₂⇛b₁₂ ⋄ a₂⇛b₂₂
    in c₁ [ c₂ ] , b₁₁⇛c₁ [⇛ b₁₂⇛c₂ ] , cont b₂₁⇛c₁ b₂₂⇛c₂
  cont a₁⇛b₁₁ a₂⇛b₁₂ ⋄ cont a₁⇛b₂₁ a₂⇛b₂₂ =
    let c₁ , b₁₁⇛c₁ , b₂₁⇛c₁ = a₁⇛b₁₁ ⋄ a₁⇛b₂₁
        c₂ , b₁₂⇛c₂ , b₂₂⇛c₂ = a₂⇛b₁₂ ⋄ a₂⇛b₂₂
    in c₁ [ c₂ ] , b₁₁⇛c₁ [⇛ b₁₂⇛c₂ ] , b₂₁⇛c₁ [⇛ b₂₂⇛c₂ ]

  -- A strip lemma.
  _⋄′_ : ∀ {n} {a b₁ b₂ : Term n} →
         a ⇛ b₁ → a ⇛* b₂ → ∃ λ c → b₁ ⇛* c × b₂ ⇛ c
  a⇛b  ⋄′ ε               = _ , ε , a⇛b
  a⇛b₁ ⋄′ (a⇛b₂ ◅ b₂⇛*c₂) =
    let c₁ , b₁⇛c₁ , b₂⇛c₁ = a⇛b₁ ⋄ a⇛b₂
        d  , c₁⇛*d , c₂⇛d  = b₂⇛c₁ ⋄′ b₂⇛*c₂
    in d , b₁⇛c₁ ◅ c₁⇛*d , c₂⇛d

  -- Confluence (aka the Church-Rosser property) of _⇛*_
  _⋄*_ : ∀ {n} {a b₁ b₂ : Term n} →
         a ⇛* b₁ → a ⇛* b₂ → ∃ λ c → b₁ ⇛* c × b₂ ⇛* c
  ε               ⋄* a⇛*b  = _ , a⇛*b , ε
  (a⇛b₁ ◅ b₁⇛*c₁) ⋄* a⇛*b₂ =
    let c₂ , b₁⇛*c₂ , b₂⇛c₂ = a⇛b₁ ⋄′ a⇛*b₂
        d  , c₁⇛*d  , c₂⇛*d = b₁⇛*c₁ ⋄* b₁⇛*c₂
    in d , c₁⇛*d , b₂⇛c₂ ◅ c₂⇛*d

  -- Factorization of equivalence into parallel reductions.
  ≡p⇒⇛* : ∀ {n} {a b : Term n} → a ≡p b → ∃ λ c → a ⇛* c × b ⇛* c
  ≡p⇒⇛* ε               = _ , ε , ε
  ≡p⇒⇛* (fwd a⇛b ◅ b≡c) =
    let d , b⇛*d , c⇛*d = ≡p⇒⇛* b≡c in d , a⇛b ◅ b⇛*d , c⇛*d
  ≡p⇒⇛* (bwd a⇚b ◅ b≡c) =
    let d , b⇛*d , c⇛*d = ≡p⇒⇛* b≡c
        e , a⇛*e , d⇛e  = a⇚b ⋄′ b⇛*d
    in e , a⇛*e , c⇛*d ◅◅ (d⇛e ◅ ε)

  -- Π-injectivity (with respect to ≡p).
  Π-inj : ∀ {n} {a₁ a₂ : Term n} {b₁ b₂} → Π a₁ b₁ ≡p Π a₂ b₂ →
          a₁ ≡p a₂ × b₁ ≡p b₂
  Π-inj Πa₁b₁≡Πa₂b₂ with ≡p⇒⇛* Πa₁b₁≡Πa₂b₂
  Π-inj Πa₁b₁≡Πa₂b₂ | c , Πa₁b₁⇛*c , Πa₂b₂⇛*c with
    Π-⇛* Πa₁b₁⇛*c | Π-⇛* Πa₂b₂⇛*c
  Π-inj Πa₁b₁≡Πa₂b₂ | Π a₃ b₃ , Πa₁b₁⇛*c , Πa₂b₂⇛*c |
    ._ , ._ , a₁⇛*a₃ , b₁⇛*b₃ , P.refl | ._ , ._ , a₂⇛*a₃ , b₂⇛*b₃ , P.refl =
    ⇛*⇒≡p a₁⇛*a₃ ◅◅ sym (⇛*⇒≡p a₂⇛*a₃) , ⇛*⇒≡p b₁⇛*b₃ ◅◅ sym (⇛*⇒≡p b₂⇛*b₃)
    where
      module ParSetoid {n} where
        open Setoid (≡p-setoid {n}) public
      open ParSetoid

  -- Parallel equivalence on sorts implies syntactic equivalence.
  sort-≡p : ∀ {n s₁ s₂} → sort {n} s₁ ≡p sort s₂ → s₁ ≡ s₂
  sort-≡p s₁≡s₂ with ≡p⇒⇛* s₁≡s₂
  sort-≡p s₁≡s₂ | c , s₁⇛*c , s₂⇛*c with sort-⇛* s₁⇛*c | sort-⇛* s₂⇛*c
  sort-≡p s₁≡s₂ | ._ , s₁⇛*c , s₂⇛*c | P.refl | P.refl = P.refl


{-# OPTIONS --without-K #-}
module hott.equivalence.coind where

open import level
open import sum
open import equality.core
open import equality.calculus
open import equality.reasoning
open import function.core
open import function.extensionality
open import function.fibration
open import function.isomorphism
open import function.overloading
open import container.core
open import container.fixpoint
open import container.m
open import hott.equivalence.core
open import hott.equivalence.alternative
open import hott.level
open import sets.unit

apply≅' : ∀ {i j}{X : Set i}{Y : Set j}
        → X ≅' Y → X → Y
apply≅' (i , _) = _≅_.to i

is-≅' : ∀ {i j}{X : Set i}{Y : Set j} → (X → Y) → Set _
is-≅' {X = X}{Y = Y} f = apply≅' ⁻¹ f

abstract
  ≅'-≈-fib-comm : ∀ {i j}{X : Set i}{Y : Set j}(eq : X ≅' Y)
                → proj₁ (≅'⇒≈ eq) ≡ apply≅' eq
  ≅'-≈-fib-comm eq = refl

  ≅'⇒≈-we : ∀ {i j}{X : Set i}{Y : Set j}
           → weak-equiv (≅'⇒≈ {X = X}{Y = Y})
  ≅'⇒≈-we = proj₂ (≅⇒≈ (sym≅ ≈⇔≅'))

  is-≅'-≈-iso : ∀ {i j}{X : Set i}{Y : Set j}(f : X → Y)
              → is-≅' f ≅ weak-equiv f
  is-≅'-≈-iso {X = X}{Y = Y} f = begin
      is-≅' f
    ≡⟨ refl ⟩
      (fib weak-equiv ∘' ≅'⇒≈) ⁻¹ f
    ≅⟨ fib-compose ≅'⇒≈ (fib weak-equiv) f ⟩
      ( Σ (fib weak-equiv ⁻¹ f) λ { (we , _) → ≅'⇒≈ ⁻¹ we } )
    ≅⟨ ( Σ-ap-iso refl≅ λ { (we , _)
        → contr-⊤-iso (≅'⇒≈-we we) } ) ⟩
      ( fib weak-equiv ⁻¹ f × ⊤ )
    ≅⟨ ×-right-unit ⟩
      fib weak-equiv ⁻¹ f
    ≅⟨ fib-iso f ⟩
      weak-equiv f
    ∎
    where open ≅-Reasoning

is-≅'-h1 : ∀ {i j}{X : Set i}{Y : Set j}
         → (f : X → Y) → h 1 (is-≅' f)
is-≅'-h1 f = iso-level (sym≅ (is-≅'-≈-iso f)) (weak-equiv-h1 f)

≅'-Σ-iso : ∀ {i j}{X : Set i}{Y : Set j}
         → (X ≅' Y) ≅ (Σ (X → Y) λ f → is-≅' f)
≅'-Σ-iso {X = X}{Y = Y} = sym≅ (total-iso apply≅')

≅'-equality : ∀ {i j}{X : Set i}{Y : Set j}
            → {isom₁ isom₂ : X ≅' Y}
            → (apply isom₁ ≡ apply isom₂)
            → isom₁ ≡ isom₂
≅'-equality {X = X}{Y = Y} {isom₁}{isom₂} p = iso⇒inj ≅'-Σ-iso q
  where
    q : apply ≅'-Σ-iso isom₁ ≡ apply ≅'-Σ-iso isom₂
    q = unapΣ (p , h1⇒prop (is-≅'-h1 _) _ _)

record F {i j}(Z : ∀ {i j} → Set i → Set j → Set (i ⊔ j))
         (X : Set i)(Y : Set j) : Set (i ⊔ j) where
  constructor mk-F
  field
    f : X → Y
    g : Y → X
    φ : (x : X)(y : Y) → Z (f x ≡ y) (x ≡ g y)

~-container : ∀ i → Container _ _ _
~-container i = record
  { I = Set i × Set i
  ; A = λ { (X , Y) → (X → Y) × (Y → X) }
  ; B = λ { {X , Y} _ → X × Y }
  ; r = λ { {a = f , g} (x , y) → (f x ≡ y) , (x ≡ g y) } }

module D {i} where
  open Definition (~-container i) public
  open Fixpoint (fix M fixpoint) public
    hiding (fixpoint)

unfold≅' : ∀ {i}{X Y : Set i}
         → X ≅' Y → D.F (λ { (X , Y) → X ≅' Y }) (X , Y)
unfold≅' (isom , _)
  = (to , from)
  , λ { (x , y) → ≅⇒≅' (iso-adjunction isom x y) }
  where
    open _≅_ isom

--unfold≅' : ∀ {i}{X Y : Set i}
--         → X ≅' Y → D.F (λ { (X , Y) → X ≅' Y }) (X , Y)
--unfold≅' {X = X}{Y = Y} (iso f g α β , δ) =
--    ((f , g) , λ {(x , y) → ≅⇒≅' (φ x y)})
--  where
--    open ≡-Reasoning
--
--    δ' = co-coherence (iso f g α β) δ
--
--    iso₁ : {x : X}{y : Y}(p : f x ≡ y)
--         → ap f (sym (α x) · ap g p) · β y ≡ p
--    iso₁ {x} .{f x} refl = begin
--        ap f (sym (α x) · refl) · β (f x)
--      ≡⟨ ap (λ z → ap f z · β (f x)) (left-unit (sym (α x)))  ⟩
--        ap f (sym (α x)) · β (f x)
--      ≡⟨ ap (λ z → z · β (f x)) (ap-inv f (α x)) ⟩
--        sym (ap f (α x)) · β (f x)
--      ≡⟨ ap (λ z → sym z · β (f x)) (δ x) ⟩
--        sym (β (f x)) · β (f x)
--      ≡⟨ right-inverse (β (f x)) ⟩
--        refl
--      ∎
--
--    iso₂' : {x : X}{y : Y}(q : g y ≡ x)
--         → sym (α x) · ap g (ap f (sym q) · β y) ≡ sym q
--    iso₂' .{g y}{y} refl = begin
--        sym (α (g y)) · ap g (refl · β y)
--      ≡⟨ ap (λ z → sym (α (g y)) · ap g z) (right-unit (β y)) ⟩
--        sym (α (g y)) · ap g (β y)
--      ≡⟨ ap (λ z → sym (α (g y)) · z) (δ' y) ⟩
--        sym (α (g y)) · α (g y)
--      ≡⟨ right-inverse (α (g y)) ⟩
--        refl
--      ∎
--
--    iso₂ : {x : X}{y : Y}(q : x ≡ g y)
--         → sym (α x) · ap g (ap f q · β y) ≡ q
--    iso₂ {x}{y} q =
--      subst (λ z → sym (α x) · ap g (ap f z · β y) ≡ z)
--            (double-inverse q)
--            (iso₂' (sym q))
--
--    φ : (x : X)(y : Y) → (f x ≡ y) ≅ (x ≡ g y)
--    φ x y = record
--      { to = λ p → sym (α x) · ap g p
--      ; from = λ q → ap f q · β y
--      ; iso₁ = iso₁
--      ; iso₂ = iso₂ }

Iso' : ∀ {i} → Set i × Set i → Set _
Iso' (X , Y) = X ≅' Y

_~_ : ∀ {i} → Set i → Set i → Set i
_~_ {i} X Y = D.M (X , Y)

apply~ : ∀ {i}{X Y : Set i} → X ~ Y → X → Y
apply~ eq = proj₁ (D.head eq)

invert~ : ∀ {i}{X Y : Set i} → X ~ Y → Y → X
invert~ eq = proj₂ (D.head eq)

private
  u : ∀ {i}(XY : Set i × Set i)
    → let (X , Y) = XY in X ~ Y → X ≅' Y
  u (X , Y) eq = ≅⇒≅' (iso f g α β)
    where
      f : X → Y
      f = apply~ eq

      g : Y → X
      g = invert~ eq

      φ : (x : X)(y : Y) → (f x ≡ y) ~ (x ≡ g y)
      φ x y = D.tail eq (x , y)

      α : (x : X) → g (f x) ≡ x
      α x = sym (apply~ (φ x (f x)) refl)

      β : (y : Y) → f (g y) ≡ y
      β y = invert~ (φ (g y) y) refl

--   u-morphism : ∀ {i}{X Y : Set i}
--              → (eq : X ~ Y)
--              → unfold≅' (u _ eq)
--              ≡ D.imap u _ (D.out _ eq)
--   u-morphism {i}{X}{Y} eq = unapΣ (refl , funext λ {(x , y) → lem₂ x y})
--     where
--       f : X → Y
--       f = apply~ eq
--
--       g : Y → X
--       g = invert~ eq
--
--       φ : (x : X)(y : Y) → (f x ≡ y) ~ (x ≡ g y)
--       φ x y = D.tail eq (x , y)
--
--       σ τ : (x : X)(y : Y) → (f x ≡ y) ≅' (x ≡ g y)
--       σ x y = proj₂ (unfold≅' (u _ eq)) (x , y)
--       τ x y = u _ (φ x y)
--
--       lem : (x : X)(y : Y)(p : f x ≡ y)
--            → apply≅' (σ x y) p ≡ apply≅' (τ x y) p
--       lem x .(f x) refl = ?
--
--       lem₂ : (x : X)(y : Y) → proj₂ (unfold≅' (u (X , Y) eq)) (x , y)
--                             ≡ u _ (D.tail eq (x , y))
--       lem₂ x y = ≅'-equality (funext (lem x y))
--
--   v : ∀ {i}(XY : Set i × Set i) → let (X , Y) = XY in X ≅' Y → X ~ Y
--   v = D.unfold (λ _ → unfold≅')
--
--   vu-morphism : ∀ {i}{X Y : Set i}
--               → (eq : X ~ Y)
--               → D.out _ (v _ (u _ eq))
--               ≡ D.imap (v D.∘ⁱ u) _ (D.out _ eq)
--   vu-morphism {X = X}{Y = Y} eq = ap (D.imap v _) (u-morphism eq)
--
--   vu-id : ∀ {i}{X Y : Set i} (eq : X ~ Y) → v _ (u _ eq) ≡ eq
--   vu-id eq = D.unfold-η D.out (v D.∘ⁱ u) vu-morphism eq · D.unfold-id eq
--
--   uv-id : ∀ {i}{X Y : Set i} (i : X ≅' Y) → u _ (v _ i) ≡ i
--   uv-id {X = X}{Y = Y} i = ≅'-equality refl
--
-- ~⇔≅' : ∀ {i}{X Y : Set i} → (X ~ Y) ≅ (X ≅' Y)
-- ~⇔≅' = iso (u _) (v _) vu-id uv-id


module Reduction where

open import Data.Sum
open import Library
open import Terms
open import Substitution
open import TermShape
open import SN

data βECxt (Γ : Cxt) : (Δ : Cxt) (a b : Ty) → Set where
  appl  : ∀ {a b} (u : Tm Γ a)          → βECxt Γ Γ (a →̂ b) b
  appr  : ∀ {a b} (t : Tm Γ (a →̂  b))   → βECxt Γ Γ a b
  abs   : ∀ {a b}                       → βECxt Γ (a ∷ Γ) b (a →̂  b)

data βEhole {Γ : Cxt} : {Δ : Cxt} {b a : Ty} → Tm Γ b → βECxt Γ Δ a b → Tm Δ a → Set where
  appl  : ∀ {a b} {t : Tm Γ (a →̂ b)} (u : Tm Γ a)  → βEhole (app t u) (appl u) t
  appr  : ∀ {a b u} (t : Tm Γ (a →̂  b))            → βEhole (app t u) (appr t) u
  abs   : ∀ {a b} {t : Tm (a ∷ Γ) b}               → βEhole (abs t) abs t

-- Plugging a hole
mkHole : ∀ {Γ Δ} {a b} (E : βECxt Γ Δ a b) {t} → ∃ λ E[t] → βEhole E[t] E t
mkHole (appl u)  = _ , appl u
mkHole (appr t)  = _ , appr t
mkHole abs       = _ , abs

data _⇒β_ {Γ} : ∀ {a} → Tm Γ a → Tm Γ a → Set where

  β     : ∀ {a b}{t : Tm (a ∷ Γ) b}{u}
          → (app (abs t) u) ⇒β subst0 u t

  cong  : ∀ {Δ a b t t' Et Et'}{E : βECxt Γ Δ a b}
          → (𝑬𝒕 : βEhole Et E t)
          → (𝑬𝒕' : βEhole Et' E t')
          → (t⇒β : t ⇒β t')
          → Et ⇒β Et'

subst⇒β : ∀ {m vt a Γ} {t t' : Tm Γ a} {Δ}
           (σ : RenSub {m} vt Γ Δ) → t ⇒β t' → subst σ t ⇒β subst σ t'
subst⇒β σ (β {t = t} {u = u})          = ≡.subst (λ t' → app (abs (subst (lifts σ) t)) (subst σ u) ⇒β t')
                                                   (sgs-lifts-term {σ = σ} {u} {t})
                                           β
subst⇒β σ (cong (appl u) (appl .u) t⇒) = cong (appl _) (appl _) (subst⇒β σ t⇒)
subst⇒β σ (cong (appr t) (appr .t) t⇒) = cong (appr _) (appr _) (subst⇒β σ t⇒)
subst⇒β σ (cong abs abs t⇒)            = cong abs abs (subst⇒β (lifts σ) t⇒)

-- Multi-step β-reduction (β-reduction sequences).
-- Reflexive-transitive closure of single-step β-reduction.

infix 7 _⇒β_ _⇒β*_

data _⇒β*_ {Γ} {a} : Tm Γ a → Tm Γ a → Set where
  []  : ∀ {t} → t ⇒β* t
  _∷_ : ∀ {ti tm to} → ti ⇒β tm → tm ⇒β* to → ti ⇒β* to

-- Concatenating β-reduction sequences

_++β_ : ∀ {Γ} {a} {t₀ t₁ t₂ : Tm Γ a} → t₀ ⇒β* t₁ → t₁ ⇒β* t₂ → t₀ ⇒β* t₂
[]       ++β ys = ys
(x ∷ xs) ++β ys = x ∷ (xs ++β ys)

-- Multi-step β-reduction is compatible with term constructors.

cong* : ∀ {a Γ Δ} {b} {t tβ* : Tm Γ a} {E : βECxt Δ Γ a b} {E[t] E[tβ*]}
  → βEhole E[t] E t
  → βEhole E[tβ*] E tβ*
  → t    ⇒β* tβ*
  → E[t] ⇒β* E[tβ*]
cong* E1         E2          (x ∷ t⇒) = cong E1 (proj₂ (mkHole _)) x ∷ cong* (proj₂ (mkHole _)) E2 t⇒
cong* (appl u)   (appl .u)   []       = []
cong* (appr t₁)  (appr .t₁)  []       = []
cong* abs        abs         []       = []

EC→βEC : ∀ {Γ} {a b} (E : ECxt Γ a b) → βECxt Γ Γ a b
EC→βEC (appl u) = appl u

mkHole4 : ∀ {Γ} {a b} (E : ECxt Γ a b) {t : Tm Γ a} → βEhole (E [ t ]) (EC→βEC E) t
mkHole4 (appl u) = appl u

cong*3 : ∀ {Γ a b t t'}(E : ECxt* Γ a b)
          → (t⇒ : t ⇒β t')
          → E [ t ]* ⇒β E [ t' ]*
cong*3 [] t⇒ = t⇒
cong*3 (x ∷ E) t⇒ = cong*3 E (cong (mkHole4 x) (mkHole4 x) t⇒)

cong*4 : ∀ {Γ a b t t'}(E : ECxt* Γ a b)
          → (t⇒ : t ⇒β* t')
          → E [ t ]* ⇒β* E [ t' ]*
cong*4 E [] = []
cong*4 E (x ∷ xs) = cong*3 E x ∷ cong*4 E xs

subst⇒β*₀ : ∀ {m vt a Γ} {t t' : Tm Γ a} {Δ} (σ : RenSub {m} vt Γ Δ) → t ⇒β* t' → subst σ t ⇒β* subst σ t'
subst⇒β*₀ σ [] = []
subst⇒β*₀ σ (x ∷ xs) = (subst⇒β σ x) ∷ (subst⇒β*₀ σ xs)

mutual
  subst⇒β* : ∀ {m vt a Γ} {Δ} {σ ρ : RenSub {m} vt Γ Δ}
    → (∀ {b} (x : Var Γ b) → vt2tm _ (σ x) ⇒β* vt2tm _ (ρ x))
    → (t : Tm Γ a)
    → subst σ t ⇒β* subst ρ t
  subst⇒β* σ₁ (var x) = σ₁ x
  subst⇒β* {vt = vt} σ₁ (abs t) = cong* abs abs (subst⇒β* (lifts⇒β* {vt = vt} σ₁) t)
  subst⇒β* σ₁ (app t t₁) = cong* (appl _) (appl _) (subst⇒β* σ₁ t) ++β cong* (appr _) (appr _) (subst⇒β* σ₁ t₁)

  lifts⇒β* : ∀ {m vt a Γ} {Δ} {σ ρ : RenSub {m} vt Γ Δ}
    → (∀ {b} (x : Var Γ b) → vt2tm _ (σ x) ⇒β* vt2tm _ (ρ x))
    →  ∀ {b} (x : Var (a ∷ Γ) b)
    → vt2tm _ (lifts {a = a} σ x) ⇒β* vt2tm _ (lifts {a = a} ρ x)
  lifts⇒β* {vt = `Var} σ (zero) = []
  lifts⇒β* {vt = `Tm}  σ (zero) = []
  lifts⇒β* {vt = `Var} σ (suc x)   = subst⇒β*₀ {vt = `Var} suc (σ x)
  lifts⇒β* {vt = `Tm}  σ (suc x)   = subst⇒β*₀ {vt = `Var} suc (σ x)

-- Altenkirch's weak standardization lemma.

mutual
  beta-shr : ∀ {i a Γ} {t tβ th : Tm Γ a}
    → t ⇒β tβ
    → t ⟨ i ⟩⇒ th
    → (tβ ≡ th) ⊎ ∃ λ t' → tβ ⟨ i ⟩⇒ t' × th ⇒β* t'
  beta-shr β (β 𝒖)                                                   = inj₁ ≡.refl
  beta-shr (cong (appl u) (appl .u) (cong abs abs tβ⇒)) (β 𝒖)        = inj₂ (_ , β 𝒖 , (subst⇒β (sgs u) tβ⇒ ∷ []))
  beta-shr (cong (appr ._) (appr ._) tβ⇒) (β {t = t} 𝒖)
    = inj₂ (_ , β (mapβSN tβ⇒ 𝒖) , subst⇒β* {vt = `Tm} (λ { {._} (zero) → tβ⇒ ∷ [] ; (suc x) → [] }) t)
  beta-shr β (cong (appl u) (appl .u) (cong () 𝑬𝒕' th⇒))
  beta-shr (cong E1 E2 t⇒) (cong E0 E3 th⇒)                          = helper E1 E2 t⇒ E0 E3 th⇒

    where
      helper : ∀ {i}{a Γ} {t tβ th : Tm Γ a} {Δ a₁} {t₁ ta : Tm Δ a₁}
           {E : βECxt Γ Δ a₁ a} {a₂} {t₂ tb : Tm Γ a₂} {E₁ : ECxt Γ a₂ a}
        → βEhole t E t₁
        → βEhole tβ E ta
        → t₁ ⇒β ta
        → Ehole t E₁ t₂
        → Ehole th E₁ tb
        → t₂ ⟨ i ⟩⇒ tb
        → tβ ≡ th ⊎ ∃ λ t' → tβ ⟨ i ⟩⇒ t' × th ⇒β* t'
      helper (appl u) (appl .u) t⇒₁ (appl .u) (appl .u) th⇒₁ with beta-shr t⇒₁ th⇒₁
      helper (appl u) (appl .u) t⇒₁ (appl .u) (appl .u) th⇒₁ | inj₁ ≡.refl = inj₁ ≡.refl
      helper (appl u) (appl .u) t⇒₁ (appl .u) (appl .u) th⇒₁ | inj₂ (tm , h⇒tm , tm⇒β)
             = inj₂ (_ , cong (appl _) (appl _) h⇒tm , cong* (appl _) (appl _) tm⇒β)
      helper (appr t₂) (appr .t₂) t⇒₁ (appl t₁) (appl .t₁) th⇒₁ = inj₂ (_ , cong (appl _) (appl _) th⇒₁ , (cong (appr _) (appr _) t⇒₁ ∷ []))

  -- SNe is preserved under reduction.

  mapβSNe : ∀ {i a Γ} {t t' : Tm Γ a}
   → t ⇒β t'
   → SNe {i} t
   → SNe {i} t'
  mapβSNe (cong (appl u) (appl .u) t⇒)  (elim 𝒏 (appl 𝒖))  = elim (mapβSNe t⇒ 𝒏) (appl 𝒖)
  mapβSNe (cong (appr u) (appr .u) t⇒)  (elim 𝒏 (appl 𝒖))  = elim 𝒏 (appl (mapβSN t⇒ 𝒖))
  -- The other cases are impossible
  mapβSNe (cong abs abs t⇒)             (elim 𝒏 ())
  mapβSNe β                             (elim (elim 𝒏 ()) (appl 𝒖))

  -- SN is preserved under reduction.

  mapβSN : ∀ {i a Γ} {t t' : Tm Γ a}
    → t ⇒β t'
    → SN {i} t
    → SN {i} t'
  mapβSN t⇒                (ne 𝒏)                = ne (mapβSNe t⇒ 𝒏)
  mapβSN (cong abs abs t⇒) (abs 𝒕)               = abs (mapβSN t⇒ 𝒕)
  mapβSN t⇒                (exp t⇒₁ 𝒕)           with beta-shr t⇒ t⇒₁
  mapβSN t⇒ (exp t⇒₁ 𝒕) | inj₁ ≡.refl = 𝒕
  mapβSN t⇒ (exp t⇒₁ 𝒕) | inj₂ (_ , t⇒h , t⇒β*)  = exp t⇒h (mapβ*SN t⇒β* 𝒕)

  -- SN is preserved under multi-step reduction.

  mapβ*SN : ∀ {i a Γ} {t t' : Tm Γ a}
   → t ⇒β* t'
   → SN {i} t
   → SN {i} t'
  mapβ*SN []          𝒕 = 𝒕
  mapβ*SN (t⇒ ∷ t⇒β*) 𝒕 = mapβ*SN t⇒β* (mapβSN t⇒ 𝒕)


postulate
  C : Set
  anything : C

record I : Set where
  constructor c
  field
    f : C

data Wrap : (j : I) → Set where
  wrap : ∀ {j} → Wrap j

works1 : ∀ {j} → Wrap j → C
works1 {c ._} (wrap {j = c _}) with anything
... | z = z