------------------------------------------------------------------------
-- Small coinductive higher lenses with erased "proofs"
------------------------------------------------------------------------

{-# OPTIONS --guardedness #-}

import Equality.Path as P

module Lens.Non-dependent.Higher.Coinductive.Small.Erased
  {e⁺} (eq : ∀ {a p} → P.Equality-with-paths a p e⁺) where

open P.Derived-definitions-and-properties eq

open import Logical-equivalence using (_⇔_)
open import Prelude as P hiding (id)

open import Bijection equality-with-J as B using (_↔_)
open import Equality.Decidable-UIP equality-with-J using (Constant)
open import Equality.Path.Isomorphisms eq
open import Equivalence equality-with-J as Eq
  using (_≃_; Is-equivalence)
open import Equivalence.Erased.Cubical eq as EEq using (_≃ᴱ_)
open import Equivalence.Erased.Contractible-preimages.Cubical eq as ECP
  using (_⁻¹ᴱ_)
open import Erased.Cubical eq
open import Function-universe equality-with-J as F hiding (id; _∘_)
open import H-level equality-with-J
open import H-level.Closure equality-with-J
open import H-level.Truncation.Propositional eq using (∥_∥)
open import Preimage equality-with-J using (_⁻¹_)
open import Tactic.Sigma-cong equality-with-J
open import Univalence-axiom equality-with-J

open import Lens.Non-dependent eq
import Lens.Non-dependent.Higher.Capriotti.Variant.Erased.Variant eq
  as V
open import Lens.Non-dependent.Higher.Coherently.Coinductive eq
import Lens.Non-dependent.Higher.Coinductive eq as C
import Lens.Non-dependent.Higher.Coinductive.Erased eq as CE
import Lens.Non-dependent.Higher.Coinductive.Small eq as S
import Lens.Non-dependent.Higher.Erased eq as E

private
  variable
    a b c d : Level
    A B     : Type a
    l g p s : A
    P       : A → Type p
    f       : (x : A) → P x
    univ    : Univalence a

------------------------------------------------------------------------
-- Weakly constant functions

-- Weakly constant type-valued functions.
--
-- Note that the definition does not use _≃ᴱ_: the right-to-left
-- directions of the equivalences are erased.

Constantᴱ :
  {A : Type a} →
  (A → Type p) → Type (a ⊔ p)
Constantᴱ P = ∀ x y → ∃ λ (f : P x → P y) → Erased (Is-equivalence f)

-- In erased contexts Constantᴱ is pointwise equivalent to Constant
-- (assuming univalence).

@0 Constantᴱ≃Constant :
  {P : A → Type p} →
  Univalence p →
  Constantᴱ P ≃ Constant P
Constantᴱ≃Constant {P = P} univ =
  Constantᴱ P                                                ↔⟨⟩
  (∀ x y → ∃ λ (f : P x → P y) → Erased (Is-equivalence f))  ↔⟨ (∀-cong ext λ _ → ∀-cong ext λ _ → ∃-cong λ _ → erased Erased↔) ⟩
  (∀ x y → ∃ λ (f : P x → P y) → Is-equivalence f)           ↔⟨ (∀-cong ext λ _ → ∀-cong ext λ _ → inverse Eq.≃-as-Σ) ⟩
  (∀ x y → P x ≃ P y)                                        ↝⟨ (∀-cong ext λ _ → ∀-cong ext λ _ → inverse $ ≡≃≃ univ) ⟩
  (∀ x y → P x ≡ P y)                                        ↔⟨⟩
  Constant P                                                 □

-- In erased contexts Constantᴱ is pointwise equivalent to
-- S.Constant-≃.

@0 Constantᴱ≃Constant-≃ : Constantᴱ P ≃ S.Constant-≃ P
Constantᴱ≃Constant-≃ {P = P} =
  Constantᴱ P                                                ↔⟨⟩
  (∀ x y → ∃ λ (f : P x → P y) → Erased (Is-equivalence f))  ↔⟨ (∀-cong ext λ _ → ∀-cong ext λ _ → ∃-cong λ _ → erased Erased↔) ⟩
  (∀ x y → ∃ λ (f : P x → P y) → Is-equivalence f)           ↔⟨ (∀-cong ext λ _ → ∀-cong ext λ _ → inverse Eq.≃-as-Σ) ⟩
  (∀ x y → P x ≃ P y)                                        ↔⟨⟩
  S.Constant-≃ P                                             □

-- In erased contexts Constantᴱ (f ⁻¹ᴱ_) is pointwise equivalent to
-- S.Constant-≃ (f ⁻¹_).

@0 Constantᴱ-⁻¹ᴱ-≃-Constant-≃-⁻¹ :
  Constantᴱ (f ⁻¹ᴱ_) ≃ S.Constant-≃ (f ⁻¹_)
Constantᴱ-⁻¹ᴱ-≃-Constant-≃-⁻¹ {f = f} =
  Constantᴱ (f ⁻¹ᴱ_)               ↝⟨ Constantᴱ≃Constant-≃ ⟩
  S.Constant-≃ (f ⁻¹ᴱ_)            ↔⟨⟩
  (∀ b₁ b₂ → f ⁻¹ᴱ b₁ ≃ f ⁻¹ᴱ b₂)  ↝⟨ (∀-cong ext λ _ → ∀-cong ext λ _ →
                                       Eq.≃-preserves ext (inverse ECP.⁻¹≃⁻¹ᴱ) (inverse ECP.⁻¹≃⁻¹ᴱ)) ⟩
  (∀ b₁ b₂ → f ⁻¹ b₁ ≃ f ⁻¹ b₂)    ↔⟨⟩
  S.Constant-≃ (f ⁻¹_)             □

-- In erased contexts Constantᴱ (f ⁻¹ᴱ_) is pointwise equivalent to
-- Constant (f ⁻¹_) (assuming univalence).

@0 Constantᴱ-⁻¹ᴱ-≃-Constant-⁻¹ :
  Block "Constantᴱ-⁻¹ᴱ-≃-Constant-⁻¹" →
  {A : Type a} {B : Type b} {f : A → B} →
  Univalence (a ⊔ b) →
  Constantᴱ (f ⁻¹ᴱ_) ≃ Constant (f ⁻¹_)
Constantᴱ-⁻¹ᴱ-≃-Constant-⁻¹ ⊠ {f = f} univ =
  Constantᴱ (f ⁻¹ᴱ_)    ↝⟨ Constantᴱ-⁻¹ᴱ-≃-Constant-≃-⁻¹ ⟩
  S.Constant-≃ (f ⁻¹_)  ↝⟨ inverse $ S.Constant≃Constant-≃ univ ⟩□
  Constant (f ⁻¹_)      □

-- A "computation" rule for Constantᴱ-⁻¹ᴱ-≃-Constant-⁻¹.

@0 proj₁-from-Constantᴱ-⁻¹ᴱ-≃-Constant-⁻¹ :
  (bl : Block "Constantᴱ-⁻¹ᴱ-≃-Constant-⁻¹") →
  ∀ {A : Type a} {B : Type b} {f : A → B} {univ : Univalence (a ⊔ b)}
    {c : Constant (f ⁻¹_)} {b₁ b₂} →
  proj₁ (_≃_.from (Constantᴱ-⁻¹ᴱ-≃-Constant-⁻¹ bl univ) c b₁ b₂) ≡
  ECP.⁻¹→⁻¹ᴱ ∘ subst P.id (c b₁ b₂) ∘ ECP.⁻¹ᴱ→⁻¹
proj₁-from-Constantᴱ-⁻¹ᴱ-≃-Constant-⁻¹ ⊠ {c = c} {b₁ = b₁} {b₂ = b₂} =
  ⟨ext⟩ λ (a , [ eq ]) →

  ECP.⁻¹→⁻¹ᴱ (≡⇒→ (c b₁ b₂) (a , eq))         ≡⟨ cong ECP.⁻¹→⁻¹ᴱ $ sym $
                                                 subst-id-in-terms-of-≡⇒↝ equivalence ⟩∎
  ECP.⁻¹→⁻¹ᴱ (subst P.id (c b₁ b₂) (a , eq))  ∎

-- Constantᴱ (get ⁻¹ᴱ_) can be expressed (up to _≃ᴱ_) in terms of a
-- "setter" and an erased "get-set" law that, in a certain way, form
-- an erased family of equivalences.
--
-- This lemma is based on a lemma suggested by Andrea Vezzosi, see
-- S.Constant-≃-get-⁻¹-≃.

Constantᴱ-⁻¹ᴱ-≃ᴱ :
  {A : Type a} {B : Type b} {get : A → B} →
  Block "Constantᴱ-⁻¹ᴱ-≃ᴱ" →
  Constantᴱ (get ⁻¹ᴱ_) ≃ᴱ
  (∃ λ (set : A → B → A) →
   Erased (∃ λ (get-set : ∀ a b → get (set a b) ≡ b) →
           ∀ b₁ b₂ →
           let f : get ⁻¹ b₁ → get ⁻¹ b₂
               f = λ (a , _) → set a b₂ , get-set a b₂
           in
           Is-equivalence f))
Constantᴱ-⁻¹ᴱ-≃ᴱ {A = A} {B = B} {get = get} ⊠ =
  Constantᴱ (get ⁻¹ᴱ_)                                                    ↔⟨⟩

  (∀ b₁ b₂ →
   ∃ λ (f : get ⁻¹ᴱ b₁ → get ⁻¹ᴱ b₂) →
   Erased (Is-equivalence f))                                             ↔⟨ (∀-cong ext λ _ → ∀-cong ext λ _ → ∃-cong λ f → Erased-cong (
                                                                              Is-equivalence≃Is-equivalence-∘ˡ {k = equivalence} ext $
                                                                              _≃_.is-equivalence $ from-bijection $
                                                                              ∃-cong λ _ → erased Erased↔)) ⟩
  (∀ b₁ b₂ →
   ∃ λ (f : get ⁻¹ᴱ b₁ → get ⁻¹ᴱ b₂) →
   Erased (Is-equivalence (Σ-map P.id erased ∘ f)))                       ↔⟨ (∀-cong ext λ _ → ∀-cong ext λ _ → ∃-cong λ f → Erased-cong (
                                                                              Is-equivalence≃Is-equivalence-∘ʳ {k = equivalence} ext $
                                                                              _≃_.is-equivalence $ from-bijection $
                                                                              ∃-cong λ _ → inverse $ erased Erased↔)) ⟩
  (∀ b₁ b₂ →
   ∃ λ (f : get ⁻¹ᴱ b₁ → get ⁻¹ᴱ b₂) →
   Erased (Is-equivalence (Σ-map P.id erased ∘ f ∘ Σ-map P.id [_]→)))     ↔⟨ Π-comm ⟩

  (∀ b₂ b₁ →
   ∃ λ (f : get ⁻¹ᴱ b₁ → get ⁻¹ᴱ b₂) →
   Erased (Is-equivalence (Σ-map P.id erased ∘ f ∘ Σ-map P.id [_]→)))     ↔⟨ (∀-cong ext λ _ → ΠΣ-comm) ⟩

  (∀ b₂ →
   ∃ λ (f : ∀ b₁ → get ⁻¹ᴱ b₁ → get ⁻¹ᴱ b₂) →
   ∀ b₁ →
   Erased (Is-equivalence (Σ-map P.id erased ∘ f b₁ ∘ Σ-map P.id [_]→)))  ↔⟨ (∀-cong ext λ _ → ∃-cong λ _ → inverse Erased-Π↔Π) ⟩

  (∀ b₂ →
   ∃ λ (f : ∀ b₁ → get ⁻¹ᴱ b₁ → get ⁻¹ᴱ b₂) →
   Erased (∀ b₁ →
           Is-equivalence (Σ-map P.id erased ∘ f b₁ ∘ Σ-map P.id [_]→)))  ↔⟨ (∀-cong ext λ _ →
                                                                              Σ-cong {k₂ = equivalence}
                                                                                (inverse (∀-cong ext λ _ → currying) F.∘
                                                                                Π-comm F.∘
                                                                                (∀-cong ext λ _ → currying))
                                                                                (λ _ → Eq.id)) ⟩
  (∀ b₂ →
   ∃ λ (f : ∀ a → (∃ λ b₁ → Erased (get a ≡ b₁)) → get ⁻¹ᴱ b₂) →
   Erased (∀ b₁ →
           let g : get ⁻¹ b₁ → get ⁻¹ b₂
               g = λ (a , eq) → Σ-map P.id erased (f a (b₁ , [ eq ]))
           in
           Is-equivalence g))                                             ↝⟨ (∀-cong ext λ _ →
                                                                              EEq.Σ-cong-≃ᴱ-Erased
                                                                                (∀-cong ext λ _ →
                                                                                EEq.drop-⊤-left-Π-≃ᴱ ext
                                                                                Erased-other-singleton≃ᴱ⊤
                                                                                (λ _ _ → F.id)) λ f →
                                                                              Erased-cong (
                                                                              ∀-cong ext λ b₁ →
                                                                              Is-equivalence-cong ext λ (a , eq) →
      Σ-map P.id erased (f a (b₁ , [ eq ]))                                       ≡⟨ cong (Σ-map P.id erased ∘ f a) $ sym $
                                                                                erased (proj₂ Contractibleᴱ-Erased-other-singleton) _ ⟩

      Σ-map P.id erased (f a (get a , [ refl _ ]))                                ≡⟨ cong (Σ-map P.id erased) $ sym $ subst-refl _ _ ⟩∎

      Σ-map P.id erased
        (subst (const _) (refl _) (f a (get a , [ refl _ ])))                   ∎)) ⟩

  (∀ b₂ →
   ∃ λ (f : A → get ⁻¹ᴱ b₂) →
   Erased (∀ b₁ →
           let g : get ⁻¹ b₁ → get ⁻¹ b₂
               g = λ (a , _) → Σ-map P.id erased (f a)
           in
           Is-equivalence g))                                             ↔⟨ ΠΣ-comm ⟩

  (∃ λ (f : ∀ b → A → get ⁻¹ᴱ b) →
   ∀ b₂ →
   Erased (∀ b₁ →
           let g : get ⁻¹ b₁ → get ⁻¹ b₂
               g = λ (a , _) → Σ-map P.id erased (f b₂ a)
           in
           Is-equivalence g))                                             ↔⟨ (∃-cong λ _ → inverse Erased-Π↔Π) ⟩

  (∃ λ (f : ∀ b → A → get ⁻¹ᴱ b) →
   Erased (∀ b₂ b₁ →
           let g : get ⁻¹ b₁ → get ⁻¹ b₂
               g = λ (a , _) → Σ-map P.id erased (f b₂ a)
           in
           Is-equivalence g))                                             ↔⟨ Σ-cong Π-comm (λ _ → Erased-cong Π-comm) ⟩

  (∃ λ (f : A → ∀ b → get ⁻¹ᴱ b) →
   Erased (∀ b₁ b₂ →
           let g : get ⁻¹ b₁ → get ⁻¹ b₂
               g = λ (a , _) → Σ-map P.id erased (f a b₂)
           in
           Is-equivalence g))                                             ↔⟨ Σ-cong (∀-cong ext λ _ → ΠΣ-comm) (λ _ → Eq.id) ⟩

  (∃ λ (f : A → ∃ λ (set : B → A) →
                ∀ b → Erased (get (set b) ≡ b)) →
   Erased (∀ b₁ b₂ →
           let g : get ⁻¹ b₁ → get ⁻¹ b₂
               g = λ (a , _) → proj₁ (f a) b₂ , erased (proj₂ (f a) b₂)
           in
           Is-equivalence g))                                             ↔⟨ Σ-cong
                                                                               (∀-cong ext λ _ → ∃-cong λ _ → inverse Erased-Π↔Π)
                                                                               (λ _ → Eq.id) ⟩
  (∃ λ (f : A → ∃ λ (set : B → A) →
                Erased (∀ b → get (set b) ≡ b)) →
   Erased (∀ b₁ b₂ →
           let g : get ⁻¹ b₁ → get ⁻¹ b₂
               g = λ (a , _) → proj₁ (f a) b₂ , erased (proj₂ (f a)) b₂
           in
           Is-equivalence g))                                             ↔⟨ Σ-cong-id {k₂ = equivalence} ΠΣ-comm ⟩

  (∃ λ ((set , get-set) :
        ∃ λ (set : A → B → A) →
        ∀ a → Erased (∀ b → get (set a b) ≡ b)) →
   Erased (∀ b₁ b₂ →
           let f : get ⁻¹ b₁ → get ⁻¹ b₂
               f = λ (a , _) → set a b₂ , erased (get-set a) b₂
           in
           Is-equivalence f))                                             ↔⟨ Σ-cong (∃-cong λ _ → inverse Erased-Π↔Π) (λ _ → Eq.id) ⟩

  (∃ λ ((set , [ get-set ]) :
        ∃ λ (set : A → B → A) →
        Erased (∀ a b → get (set a b) ≡ b)) →
   Erased (∀ b₁ b₂ →
           let f : get ⁻¹ b₁ → get ⁻¹ b₂
               f = λ (a , _) → set a b₂ , get-set a b₂
           in
           Is-equivalence f))                                             ↔⟨ inverse Σ-assoc ⟩

  (∃ λ (set : A → B → A) →
   ∃ λ (([ get-set ]) : Erased (∀ a b → get (set a b) ≡ b)) →
   Erased (∀ b₁ b₂ →
           Is-equivalence λ (a , _) → set a b₂ , get-set a b₂))           ↔⟨ (∃-cong λ _ → inverse
                                                                              Erased-Σ↔Σ) ⟩□
  (∃ λ (set : A → B → A) →
   Erased (∃ λ (get-set : ∀ a b → get (set a b) ≡ b) →
           ∀ b₁ b₂ →
           Is-equivalence λ (a , _) → set a b₂ , get-set a b₂))           □

-- A lemma relating S.Constant-≃-get-⁻¹-≃,
-- Constantᴱ-⁻¹ᴱ-≃-Constant-≃-⁻¹ and Constantᴱ-⁻¹ᴱ-≃ᴱ.

@0 to-Constant-≃-get-⁻¹-≃-to-Constantᴱ-⁻¹ᴱ-≃-Constant-≃-⁻¹-≡ :
  (bl : Block "Constant-≃-get-⁻¹-≃")
  {A : Type a} {B : Type b} {get : A → B} →
  (c : Constantᴱ (get ⁻¹ᴱ_)) →
  _≃_.to (S.Constant-≃-get-⁻¹-≃ bl)
    (_≃_.to Constantᴱ-⁻¹ᴱ-≃-Constant-≃-⁻¹ c) ≡
  Σ-map P.id erased
    (_≃ᴱ_.to (Constantᴱ-⁻¹ᴱ-≃ᴱ bl) c)
to-Constant-≃-get-⁻¹-≃-to-Constantᴱ-⁻¹ᴱ-≃-Constant-≃-⁻¹-≡
  bl {get = get} c =
  _≃_.from (Eq.≃-≡ $ Eq.↔⇒≃ $ inverse Σ-assoc) $
  Σ-≡,≡→≡
    (lemma bl)
    ((Π-closure ext 1 λ _ →
      Π-closure ext 1 λ _ →
      Eq.propositional ext _)
       _ _)
  where
  lemma :
    (bl : Block "Constant-≃-get-⁻¹-≃") →
    (proj₁ $ _↔_.to Σ-assoc $
     _≃_.to (S.Constant-≃-get-⁻¹-≃ bl)
       (_≃_.to Constantᴱ-⁻¹ᴱ-≃-Constant-≃-⁻¹ c)) ≡
    (proj₁ $ _↔_.to Σ-assoc $
     Σ-map P.id erased (_≃ᴱ_.to (Constantᴱ-⁻¹ᴱ-≃ᴱ bl) c))
  lemma ⊠ =
    Σ-≡,≡→≡
      (⟨ext⟩ λ a → ⟨ext⟩ λ b →

       proj₁ (proj₁ (c (get a) b) (a , [ refl _ ]))      ≡⟨ cong proj₁ $ sym $ subst-refl _ _ ⟩∎
       proj₁ (subst (λ _ → get ⁻¹ᴱ b) (refl _)
                (proj₁ (c (get a) b) (a , [ refl _ ])))  ∎)
      (⟨ext⟩ λ a → ⟨ext⟩ λ b →

       subst (λ set → ∀ a b → get (set a b) ≡ b)
         (⟨ext⟩ λ a → ⟨ext⟩ λ b →
          cong {x = proj₁ (c (get a) b) (a , [ refl _ ])} proj₁ $ sym $
          subst-refl _ _)
         (λ a b → erased (proj₂ (proj₁ (c (get a) b) (a , [ refl _ ]))))
         a b                                                              ≡⟨ sym $
                                                                             trans (push-subst-application _ _) $
                                                                             cong (_$ b) $ push-subst-application _ _ ⟩
       subst (λ set → get (set a b) ≡ b)
         (⟨ext⟩ λ _ → ⟨ext⟩ λ _ → cong proj₁ $ sym $ subst-refl _ _)
         (erased (proj₂ (proj₁ (c (get a) b) (a , [ refl _ ]))))          ≡⟨ trans (subst-ext _ _) $
                                                                             subst-ext _ _ ⟩
       subst (λ s → get s ≡ b)
         (cong proj₁ $ sym $ subst-refl _ _)
         (erased (proj₂ (proj₁ (c (get a) b) (a , [ refl _ ]))))          ≡⟨ sym $ subst-∘ _ _ _ ⟩

       subst (λ (s , _) → get s ≡ b)
         (sym $ subst-refl _ _)
         (erased (proj₂ (proj₁ (c (get a) b) (a , [ refl _ ]))))          ≡⟨ elim¹
                                                                               (λ {p} eq →
                                                                                subst (λ (s , _) → get s ≡ b) eq
                                                                                (erased (proj₂ (proj₁ (c (get a) b) (a , [ refl _ ])))) ≡
                                                                                erased (proj₂ p))
                                                                               (subst-refl _ _)
                                                                               _ ⟩∎
       erased
         (proj₂ (subst (λ _ → get ⁻¹ᴱ b) (refl _)
                   (proj₁ (c (get a) b) (a , [ refl _ ]))))               ∎)

------------------------------------------------------------------------
-- Coherently constant families of fibres

-- Coherently constant families of fibres (with erased proofs),
-- defined using univalence.

Coherently-constant-fibres :
  {A : Type a} {B : Type b} →
  @0 Univalence (a ⊔ b) →
  (A → B) → Type (a ⊔ b)
Coherently-constant-fibres {A = A} {B = B} univ get =
  ∃ λ (set : A → B → A) →
    Erased
      (∃ λ (c : S.Coherently-constant univ (get ⁻¹_)) →
       set ≡
       S.Lens.set {univ = univ}
         (record { get⁻¹-coherently-constant = c }))

-- Coherently-constant-fibres univ get is equivalent (with erased
-- proofs) to CE.Coherently-constant (get ⁻¹ᴱ_) (assuming univalence).

Coherently-constant-fibres≃ᴱCoherently-constant-⁻¹ᴱ :
  {A : Type a} {B : Type b} {get : A → B} →
  @0 Univalence (lsuc (a ⊔ b)) →
  (@0 univ : Univalence (a ⊔ b)) →
  Coherently-constant-fibres univ get ≃ᴱ
  CE.Coherently-constant (get ⁻¹ᴱ_)
Coherently-constant-fibres≃ᴱCoherently-constant-⁻¹ᴱ
  {a = a} {b = b} {A = A} {B = B} {get = get} univ′ univ =
  block λ bl →

  Coherently-constant-fibres univ get                                     ↔⟨⟩

  (∃ λ (set : A → B → A) →
   Erased
     (∃ λ (c : S.Coherently-constant univ (get ⁻¹_)) →
      set ≡
      S.Lens.set {univ = univ}
        (record { get⁻¹-coherently-constant = c })))                      ↔⟨ (∃-cong λ _ → Erased-cong (∃-cong λ c →
                                                                              ≡⇒≃ $ cong (_ ≡_) $
                                                                              S.Lens.set≡ {univ = univ}
                                                                                (record { get⁻¹-coherently-constant = c })
                                                                                bl)) ⟩
  (∃ λ (set : A → B → A) →
   Erased
     (∃ λ (c : S.Coherently-constant univ (get ⁻¹_)) →
      set ≡ proj₁ (_≃_.to (S.Constant-≃-get-⁻¹-≃ bl) (c .property))))     ↔⟨ (∃-cong λ _ → Erased-cong (∃-cong λ _ → inverse $
                                                                              ≡-comm F.∘
                                                                              drop-⊤-right λ _ →
                                                                              _⇔_.to contractible⇔↔⊤ $
                                                                              other-singleton-contractible _)) ⟩
  (∃ λ (set : A → B → A) →
   Erased
     (∃ λ (c : S.Coherently-constant univ (get ⁻¹_)) →
      ∃ λ (p : proj₁ (_≃_.to (S.Constant-≃-get-⁻¹-≃ bl) (c .property)) ≡
               set) →
      ∃ λ (q : ∃ λ (get-set : (a : A) (b : B) → get (set a b) ≡ b) →
               ∀ b₁ b₂ →
               let f : get ⁻¹ b₁ → get ⁻¹ b₂
                   f = λ (a , _) → set a b₂ , get-set a b₂
               in Is-equivalence f) →
      subst
        (λ set →
           ∃ λ (get-set : (a : A) (b : B) → get (set a b) ≡ b) →
           ∀ b₁ b₂ →
           let f : get ⁻¹ b₁ → get ⁻¹ b₂
               f = λ (a , _) → set a b₂ , get-set a b₂
           in
           Is-equivalence f)
        p
        (proj₂ (_≃_.to (S.Constant-≃-get-⁻¹-≃ bl) (c .property))) ≡
        q))                                                               ↔⟨ (∃-cong λ _ → Erased-cong (∃-cong λ _ →
                                                                              (∃-cong λ _ → ∃-comm) F.∘
                                                                              ∃-comm F.∘
                                                                              (∃-cong λ _ → inverse Σ-assoc))) ⟩
  (∃ λ (set : A → B → A) →
   Erased
     (∃ λ (c : S.Coherently-constant univ (get ⁻¹_)) →
      ∃ λ (get-set : (a : A) (b : B) → get (set a b) ≡ b) →
      ∃ λ (eq : ∀ b₁ b₂ →
                let f : get ⁻¹ b₁ → get ⁻¹ b₂
                    f = λ (a , _) → set a b₂ , get-set a b₂
                in Is-equivalence f) →
      ∃ λ (p : proj₁ (_≃_.to (S.Constant-≃-get-⁻¹-≃ bl) (c .property)) ≡
               set) →
      subst
        (λ set →
           ∃ λ (get-set : (a : A) (b : B) → get (set a b) ≡ b) →
           ∀ b₁ b₂ →
           let f : get ⁻¹ b₁ → get ⁻¹ b₂
               f = λ (a , _) → set a b₂ , get-set a b₂
           in
           Is-equivalence f)
        p
        (proj₂ (_≃_.to (S.Constant-≃-get-⁻¹-≃ bl) (c .property))) ≡
        (get-set , eq)))                                                  ↔⟨ (∃-cong λ _ → Erased-cong (∃-cong λ _ → ∃-cong λ _ → ∃-cong λ _ →
                                                                              B.Σ-≡,≡↔≡)) ⟩
  (∃ λ (set : A → B → A) →
   Erased
     (∃ λ (c : S.Coherently-constant univ (get ⁻¹_)) →
      ∃ λ (get-set : (a : A) (b : B) → get (set a b) ≡ b) →
      ∃ λ (eq : ∀ b₁ b₂ →
                let f : get ⁻¹ b₁ → get ⁻¹ b₂
                    f = λ (a , _) → set a b₂ , get-set a b₂
                in Is-equivalence f) →
      _≃_.to (S.Constant-≃-get-⁻¹-≃ bl) (c .property) ≡
      (set , get-set , eq)))                                              ↔⟨ (∃-cong λ _ → Erased-cong (∃-cong λ _ → ∃-cong λ _ → ∃-cong λ _ →
                                                                              ≡⇒≃ (cong (_≡ _) $ _≃_.left-inverse-of (S.Constant-≃-get-⁻¹-≃ bl) _) F.∘
                                                                              inverse (Eq.≃-≡ $ inverse $ S.Constant-≃-get-⁻¹-≃ bl))) ⟩
  (∃ λ (set : A → B → A) →
   Erased
     (∃ λ (c : S.Coherently-constant univ (get ⁻¹_)) →
      ∃ λ (get-set : (a : A) (b : B) → get (set a b) ≡ b) →
      ∃ λ (eq : ∀ b₁ b₂ →
                let f : get ⁻¹ b₁ → get ⁻¹ b₂
                    f = λ (a , _) → set a b₂ , get-set a b₂
                in Is-equivalence f) →
      c .property ≡ S.Constant-≃-get-⁻¹-≃⁻¹ (set , get-set , eq)))        ↔⟨ (Σ-assoc F.∘
                                                                              (∃-cong λ _ →
                                                                               Erased-Σ↔Σ F.∘
                                                                               Erased-cong
                                                                                (Σ-assoc F.∘
                                                                                (∃-cong λ _ → ∃-comm) F.∘
                                                                                ∃-comm))) ⟩
  (∃ λ ((set , [ get-set , eq ]) :
        ∃ λ (set : A → B → A) →
        Erased
          (∃ λ (get-set : (a : A) (b : B) → get (set a b) ≡ b) →
           ∀ b₁ b₂ →
           let f : get ⁻¹ b₁ → get ⁻¹ b₂
               f = λ (a , _) → set a b₂ , get-set a b₂
           in
           Is-equivalence f)) →
   Erased
     (∃ λ (c : S.Coherently-constant univ (get ⁻¹_)) →
      c .property ≡ S.Constant-≃-get-⁻¹-≃⁻¹ (set , get-set , eq)))        ↝⟨ (inverse $
                                                                              EEq.Σ-cong-≃ᴱ-Erased (Constantᴱ-⁻¹ᴱ-≃ᴱ bl) λ c →
                                                                              Erased-cong (∃-cong λ c′ →
                                                                              ≡⇒↝ _ (cong (c′ .property ≡_) (
       _≃_.to Constantᴱ-⁻¹ᴱ-≃-Constant-≃-⁻¹ c                                   ≡⟨ sym $ _≃_.to-from (S.Constant-≃-get-⁻¹-≃ bl) $
                                                                                to-Constant-≃-get-⁻¹-≃-to-Constantᴱ-⁻¹ᴱ-≃-Constant-≃-⁻¹-≡
                                                                                bl c ⟩∎
       S.Constant-≃-get-⁻¹-≃⁻¹
         (Σ-map P.id erased $ _≃ᴱ_.to (Constantᴱ-⁻¹ᴱ-≃ᴱ bl) c)                  ∎)))) ⟩

  (∃ λ (c : Constantᴱ (get ⁻¹ᴱ_)) →
   Erased
     (∃ λ (c′ : S.Coherently-constant univ (get ⁻¹_)) →
      c′ .property ≡ _≃_.to Constantᴱ-⁻¹ᴱ-≃-Constant-≃-⁻¹ c))             ↔⟨ (∃-cong λ c → Erased-cong (inverse $
                                                                              Σ-cong (S.Coinductive-coherently-constant≃Coherently-constant
                                                                                univ′ univ) λ c′ →
                                                                              lemma₁ bl c (c′ .property))) ⟩
  (∃ λ (c : Constantᴱ (get ⁻¹ᴱ_)) →
   Erased
     (∃ λ (c′ : C.Coherently-constant (get ⁻¹_)) →
      c′ .property ≡ _≃_.to (Constantᴱ-⁻¹ᴱ-≃-Constant-⁻¹ bl univ) c))     ↔⟨ (∃-cong λ c → Erased-cong (lemma₃ bl c)) ⟩

  (∃ λ (c : Constantᴱ (get ⁻¹ᴱ_)) →
   Erased (
   ∃ λ (c′ : C.Coherently-constant (get ⁻¹ᴱ_)) →
   ∀ b₁ b₂ → proj₁ (c b₁ b₂) ≡ subst P.id (c′ .property b₁ b₂)))          ↔⟨ inverse Σ-assoc F.∘
                                                                             (Σ-cong (ΠΣ-comm F.∘ ∀-cong ext λ _ → ΠΣ-comm) λ _ → F.id) ⟩
  (∃ λ (get⁻¹ᴱ-const : ∀ b₁ b₂ → get ⁻¹ᴱ b₁ → get ⁻¹ᴱ b₂) →
   (∀ b₁ b₂ → Erased (Is-equivalence (get⁻¹ᴱ-const b₁ b₂))) ×
   Erased (
   ∃ λ (c′ : C.Coherently-constant (get ⁻¹ᴱ_)) →
   ∀ b₁ b₂ → get⁻¹ᴱ-const b₁ b₂ ≡ subst P.id (c′ .property b₁ b₂)))       ↔⟨ (∃-cong {k = bijection} λ _ →
                                                                              drop-⊤-left-× λ ([ _ , eq ]) →
                                                                              _⇔_.to contractible⇔↔⊤ $
                                                                              propositional⇒inhabited⇒contractible
                                                                                (Π-closure ext 1 λ _ →
                                                                                Π-closure ext 1 λ _ →
                                                                                H-level-Erased 1 (Eq.propositional ext _))
                                                                                (λ x y →
                                                                                [ Eq.respects-extensional-equality
                                                                                (ext⁻¹ (sym (eq x y)))
                                                                                (_≃_.is-equivalence $ Eq.subst-as-equivalence _ _)
                                                                                ])) ⟩
  (∃ λ (get⁻¹ᴱ-const : ∀ b₁ b₂ → get ⁻¹ᴱ b₁ → get ⁻¹ᴱ b₂) →
   Erased (
   ∃ λ (c′ : C.Coherently-constant (get ⁻¹ᴱ_)) →
   ∀ b₁ b₂ → get⁻¹ᴱ-const b₁ b₂ ≡ subst P.id (c′ .property b₁ b₂)))       ↔⟨⟩

  CE.Coherently-constant (get ⁻¹ᴱ_)                                       □
  where
  @0 lemma₁ : ∀ _ _ _ → _ ≃ _
  lemma₁ bl c c′ =
    c′ ≡ _≃_.to (Constantᴱ-⁻¹ᴱ-≃-Constant-⁻¹ bl univ) c  ↝⟨ ≡⇒≃ $ cong (c′ ≡_) $
                                                            unblock bl
                                                              (λ bl → _≃_.to (Constantᴱ-⁻¹ᴱ-≃-Constant-⁻¹ bl univ) c ≡
                                                                      _≃_.from (S.Constant≃Constant-≃ univ)
                                                                        (_≃_.to Constantᴱ-⁻¹ᴱ-≃-Constant-≃-⁻¹ c))
                                                              (refl _) ⟩
    c′ ≡
    _≃_.from (S.Constant≃Constant-≃ univ)
      (_≃_.to Constantᴱ-⁻¹ᴱ-≃-Constant-≃-⁻¹ c)           ↝⟨ inverse $ Eq.≃-≡ (S.Constant≃Constant-≃ univ) ⟩

    _≃_.to (S.Constant≃Constant-≃ univ) c′ ≡
    _≃_.to (S.Constant≃Constant-≃ univ)
      (_≃_.from (S.Constant≃Constant-≃ univ)
         (_≃_.to Constantᴱ-⁻¹ᴱ-≃-Constant-≃-⁻¹ c))       ↝⟨ ≡⇒≃ $
                                                            cong (_≃_.to (S.Constant≃Constant-≃ univ) c′ ≡_) $
                                                            _≃_.right-inverse-of (S.Constant≃Constant-≃ univ) _ ⟩□
    _≃_.to (S.Constant≃Constant-≃ univ) c′ ≡
    _≃_.to Constantᴱ-⁻¹ᴱ-≃-Constant-≃-⁻¹ c               □

  @0 lemma₂ : ∀ _ _ _ _ → _
  lemma₂ bl c′ b₁ b₂ =
    proj₁ (_≃_.from (Constantᴱ-⁻¹ᴱ-≃-Constant-⁻¹ bl univ)
             (c′ .property) b₁ b₂)                                      ≡⟨ proj₁-from-Constantᴱ-⁻¹ᴱ-≃-Constant-⁻¹ bl ⟩

    ECP.⁻¹→⁻¹ᴱ ∘ subst P.id (c′ .property b₁ b₂) ∘ ECP.⁻¹ᴱ→⁻¹           ≡⟨ sym $
                                                                           cong₂ (λ f g → f ∘ subst P.id (c′ .property b₁ b₂) ∘ g)
                                                                             (trans (⟨ext⟩ λ _ →
                                                                                subst-id-in-terms-of-≡⇒↝ equivalence) $
                                                                              cong _≃_.to $ _≃_.right-inverse-of (≡≃≃ univ) _)
                                                                             (trans (⟨ext⟩ λ _ →
                                                                                subst-id-in-terms-of-inverse∘≡⇒↝ equivalence) $
                                                                              cong _≃_.from $ _≃_.right-inverse-of (≡≃≃ univ) _) ⟩
    subst P.id (≃⇒≡ univ ECP.⁻¹≃⁻¹ᴱ) ∘
    subst P.id (c′ .property b₁ b₂) ∘
    subst P.id (sym (≃⇒≡ univ ECP.⁻¹≃⁻¹ᴱ))                              ≡⟨ (⟨ext⟩ λ _ →
                                                                            trans (subst-subst _ _ _ _) $
                                                                            subst-subst _ _ _ _) ⟩
    subst P.id
      (trans (sym (≃⇒≡ univ (ECP.⁻¹≃⁻¹ᴱ {y = b₁})))
         (trans (c′ .property b₁ b₂)
           (≃⇒≡ univ (ECP.⁻¹≃⁻¹ᴱ {y = b₂}))))                           ≡⟨ sym $
                                                                           cong (λ f → subst P.id
                                                                                (trans (sym (f b₁))
                                                                                (trans (c′ .property b₁ b₂) (f b₂)))) $
                                                                           _≃_.left-inverse-of (Eq.extensionality-isomorphism bad-ext) _ ⟩
    subst P.id
      (trans (sym (cong (_$ b₁) $ ⟨ext⟩ λ _ → ≃⇒≡ univ ECP.⁻¹≃⁻¹ᴱ))
         (trans (c′ .property b₁ b₂)
           (cong (_$ b₂) $ ⟨ext⟩ λ b₂ →
            ≃⇒≡ univ (ECP.⁻¹≃⁻¹ᴱ {y = b₂}))))                           ≡⟨ cong (subst P.id) $
                                                                           elim¹
                                                                             (λ eq →
                                                                                trans (sym (cong (_$ b₁) eq))
                                                                                (trans (c′ .property b₁ b₂) (cong (_$ b₂) eq)) ≡
                                                                                ≡⇒→ (cong C.Coherently-constant eq) c′ .property b₁ b₂)
                                                                             (
        trans (sym (cong (_$ b₁) (refl _)))
          (trans (c′ .property b₁ b₂) (cong (_$ b₂) (refl (get ⁻¹_))))        ≡⟨ trans (cong (flip trans _) $
                                                                                trans (cong sym $ cong-refl _) $
                                                                                sym-refl) $
                                                                                trans (trans-reflˡ _) $
                                                                                trans (cong (trans _) $ cong-refl _) $
                                                                                trans-reflʳ _ ⟩

        c′ .property b₁ b₂                                                    ≡⟨ cong (λ eq → _≃_.to eq c′ .property b₁ b₂) $ sym $
                                                                                trans (cong ≡⇒≃ $ cong-refl C.Coherently-constant)
                                                                                ≡⇒↝-refl ⟩∎
        ≡⇒→ (cong C.Coherently-constant (refl _)) c′ .property b₁ b₂          ∎)
                                                                             _ ⟩∎
    subst P.id (≡⇒→ (cong C.Coherently-constant $
                     ⟨ext⟩ λ _ → ≃⇒≡ univ ECP.⁻¹≃⁻¹ᴱ)
                c′ .property b₁ b₂)                                     ∎

  @0 lemma₃ : ∀ _ _ → _ ≃ _
  lemma₃ bl c =
    (∃ λ (c′ : C.Coherently-constant (get ⁻¹_)) →
     c′ .property ≡ _≃_.to (Constantᴱ-⁻¹ᴱ-≃-Constant-⁻¹ bl univ) c)  ↝⟨ (∃-cong λ _ →
                                                                         (∀-cong ext λ _ → ∀-cong ext λ _ → from-bijection $ inverse $
                                                                          ≡-comm F.∘
                                                                          ignore-propositional-component
                                                                            (H-level-Erased 1 (Eq.propositional ext _))) F.∘
                                                                         inverse
                                                                           (Eq.extensionality-isomorphism bad-ext F.∘
                                                                            (∀-cong ext λ _ → Eq.extensionality-isomorphism bad-ext)) F.∘
                                                                         (≡⇒≃ $ cong (_ ≡_) $
                                                                          _≃_.left-inverse-of (Constantᴱ-⁻¹ᴱ-≃-Constant-⁻¹ bl univ) _) F.∘
                                                                         (inverse $
                                                                          Eq.≃-≡ $ inverse $ Constantᴱ-⁻¹ᴱ-≃-Constant-⁻¹ bl univ)) ⟩
    (∃ λ (c′ : C.Coherently-constant (get ⁻¹_)) →
     ∀ b₁ b₂ →
     proj₁ (c b₁ b₂) ≡
     proj₁ (_≃_.from (Constantᴱ-⁻¹ᴱ-≃-Constant-⁻¹ bl univ)
              (c′ .property) b₁ b₂))                                 ↝⟨ (Σ-cong (≡⇒≃ $ cong C.Coherently-constant $ ⟨ext⟩ λ _ →
                                                                                ≃⇒≡ univ ECP.⁻¹≃⁻¹ᴱ) λ c′ →
                                                                         ∀-cong ext λ b₁ → ∀-cong ext λ b₂ →
                                                                         ≡⇒≃ $ cong (proj₁ (c b₁ b₂) ≡_) (lemma₂ bl c′ b₁ b₂)) ⟩□
    (∃ λ (c′ : C.Coherently-constant (get ⁻¹ᴱ_)) →
     ∀ b₁ b₂ → proj₁ (c b₁ b₂) ≡ subst P.id (c′ .property b₁ b₂))    □

-- In erased contexts Coherently-constant-fibres univ get is
-- equivalent to S.Coherently-constant univ (get ⁻¹_).

@0 Coherently-constant-fibres≃Coherently-constant-⁻¹ :
  (bl : Block "Constant-≃-get-⁻¹-≃")
  {A : Type a} {B : Type b} {get : A → B} →
  (univ : Univalence (a ⊔ b)) →
  Coherently-constant-fibres univ get ≃
  S.Coherently-constant univ (get ⁻¹_)
Coherently-constant-fibres≃Coherently-constant-⁻¹
  bl {A = A} {B = B} {get = get} univ =
  Coherently-constant-fibres univ get                               ↔⟨⟩

  (∃ λ (set : A → B → A) →
   Erased
     (∃ λ (c : S.Coherently-constant univ (get ⁻¹_)) →
      set ≡
      S.Lens.set {univ = univ}
        (record { get⁻¹-coherently-constant = c })))                ↔⟨ (∃-cong λ _ → erased Erased↔) ⟩

  (∃ λ (set : A → B → A) →
   ∃ λ (c : S.Coherently-constant univ (get ⁻¹_)) →
   set ≡
   S.Lens.set {univ = univ}
     (record { get⁻¹-coherently-constant = c }))                    ↔⟨ ∃-comm ⟩

  (∃ λ (c : S.Coherently-constant univ (get ⁻¹_)) →
   ∃ λ (set : A → B → A) →
   set ≡
   S.Lens.set {univ = univ}
     (record { get⁻¹-coherently-constant = c }))                    ↔⟨ (drop-⊤-right λ _ →
                                                                        _⇔_.to contractible⇔↔⊤ $
                                                                        singleton-contractible _) ⟩□
  S.Coherently-constant univ (get ⁻¹_)                              □

------------------------------------------------------------------------
-- The lens type family

-- Small coinductive lenses.
--
-- The lens type is defined using (erased) univalence. The use of a
-- record type makes it easier for Agda to infer the argument univ
-- from a value of type Lens univ A B.
--
-- Note that everything is erased, except for the getter and the
-- setter.

record Lens (@0 univ : Univalence (a ⊔ b))
            (A : Type a) (B : Type b) : Type (a ⊔ b) where
  field

    -- A getter.

    get : A → B

    -- A setter.

    set : A → B → A

    -- The family get ⁻¹_ is coherently constant (in erased contexts).

    @0 get⁻¹-coherently-constant : S.Coherently-constant univ (get ⁻¹_)

  -- Using get and get⁻¹-coherently-constant we can construct an
  -- erased lens.

  @0 erased-lens : S.Lens univ A B
  erased-lens = record
    { get                       = get
    ; get⁻¹-coherently-constant = get⁻¹-coherently-constant
    }

  field

    -- The setter that we get from this lens is equal to set.

    @0 set≡set : set ≡ S.Lens.set erased-lens

  -- The family of fibres of the getter is coherently constant.

  coherently-constant-fibres-get : Coherently-constant-fibres univ get
  coherently-constant-fibres-get =
    set , [ (get⁻¹-coherently-constant , set≡set) ]

instance

  -- The lenses defined above have getters and setters.

  has-getter-and-setter :
    {@0 univ : Univalence (a ⊔ b)} →
    Has-getter-and-setter (Lens {a = a} {b = b} univ)
  has-getter-and-setter = record
    { get = Lens.get
    ; set = Lens.set
    }

-- Lens can be expressed as a Σ-type.

Lens-as-Σ :
  {A : Type a} {B : Type b} {@0 univ : Univalence (a ⊔ b)} →
  Lens univ A B ≃
  ∃ λ (get : A → B) → Coherently-constant-fibres univ get
Lens-as-Σ =
  Eq.↔→≃
    (λ l → Lens.get l , Lens.coherently-constant-fibres-get l)
    _ refl refl

-- Lens univ A B is equivalent to CE.Lens A B (with erased proofs,
-- assuming univalence).

Lens≃ᴱLens :
  Block "Lens≃ᴱLens" →
  {A : Type a} {B : Type b} →
  @0 Univalence (lsuc (a ⊔ b)) →
  (@0 univ : Univalence (a ⊔ b)) →
  Lens univ A B ≃ᴱ CE.Lens A B
Lens≃ᴱLens ⊠ {A = A} {B = B} univ′ univ =
  Lens univ A B                                              ↔⟨ Lens-as-Σ ⟩
  (∃ λ (get : A → B) → Coherently-constant-fibres univ get)  ↝⟨ (∃-cong λ _ →
                                                                 Coherently-constant-fibres≃ᴱCoherently-constant-⁻¹ᴱ univ′ univ) ⟩□
  (∃ λ (get : A → B) → CE.Coherently-constant (get ⁻¹ᴱ_))    □

-- The equivalence preserves getters and setters.

Lens≃ᴱLens-preserves-getters-and-setters :
  (bl : Block "Lens≃ᴱLens")
  {A : Type a} {B : Type b}
  (@0 univ′ : Univalence (lsuc (a ⊔ b)))
  (@0 univ : Univalence (a ⊔ b)) →
  Preserves-getters-and-setters-⇔ A B
    (_≃ᴱ_.logical-equivalence (Lens≃ᴱLens bl univ′ univ))
Lens≃ᴱLens-preserves-getters-and-setters ⊠ univ′ univ =
    (λ _ → refl _ , refl _)
  , (λ l →
       let open CE.Lens l in
         refl _
       , ⟨ext⟩ λ a → ⟨ext⟩ λ b →
         proj₁
           (subst (λ _ → get ⁻¹ᴱ b) (refl tt)
              (get⁻¹ᴱ-const (get a) b (a , [ refl (get a) ])))  ≡⟨ cong proj₁ $ subst-refl _ _ ⟩∎

         proj₁ (get⁻¹ᴱ-const (get a) b (a , [ refl (get a) ]))  ∎)

-- Lens univ A B is equivalent to E.Lens A B (with erased proofs,
-- assuming univalence).

Lens≃ᴱLensᴱ :
  Block "Lens≃ᴱLensᴱ" →
  {A : Type a} {B : Type b}
  {@0 univ : Univalence (a ⊔ b)} →
  @0 Univalence (lsuc (a ⊔ b)) →
  Lens univ A B ≃ᴱ E.Lens A B
Lens≃ᴱLensᴱ bl {A = A} {B = B} {univ = univ} univ′ =
  Lens univ A B  ↝⟨ Lens≃ᴱLens bl univ′ univ ⟩
  CE.Lens A B    ↝⟨ CE.Lens≃ᴱLens bl univ′ univ ⟩
  V.Lens A B     ↝⟨ V.Lens≃ᴱHigher-lens bl univ ⟩□
  E.Lens A B     □

-- The equivalence preserves getters and setters (in erased contexts).

@0 Lens≃ᴱLensᴱ-preserves-getters-and-setters :
  (bl : Block "Lens→Lens")
  {A : Type a} {B : Type b}
  (@0 univ′ : Univalence (lsuc (a ⊔ b)))
  (@0 univ : Univalence (a ⊔ b)) →
  Preserves-getters-and-setters-⇔ A B
    (_≃ᴱ_.logical-equivalence (Lens≃ᴱLensᴱ bl {univ = univ} univ′))
Lens≃ᴱLensᴱ-preserves-getters-and-setters bl univ′ univ =
  Preserves-getters-and-setters-⇔-∘
    {f = _≃ᴱ_.logical-equivalence (V.Lens≃ᴱHigher-lens bl univ) F.∘
         _≃ᴱ_.logical-equivalence (CE.Lens≃ᴱLens bl univ′ univ)}
    {g = _≃ᴱ_.logical-equivalence (Lens≃ᴱLens bl univ′ univ)}
    (Preserves-getters-and-setters-⇔-∘
       {f = _≃ᴱ_.logical-equivalence (V.Lens≃ᴱHigher-lens bl univ)}
       {g = _≃ᴱ_.logical-equivalence (CE.Lens≃ᴱLens bl univ′ univ)}
       (V.Lens≃ᴱHigher-lens-preserves-getters-and-setters bl univ)
       (CE.Lens≃ᴱLens-preserves-getters-and-setters bl univ′ univ))
    (Lens≃ᴱLens-preserves-getters-and-setters bl univ′ univ)

-- In erased contexts Lens univ A B is equivalent to S.Lens univ A B.

@0 Lens≃Lens :
  (bl : Block "Constant-≃-get-⁻¹-≃") →
  Lens univ A B ≃ S.Lens univ A B
Lens≃Lens {univ = univ} {A = A} {B = B} bl =
  Lens univ A B                                               ↝⟨ Lens-as-Σ ⟩
  (∃ λ (get : A → B) → Coherently-constant-fibres univ get)   ↝⟨ (∃-cong λ _ → Coherently-constant-fibres≃Coherently-constant-⁻¹ bl univ) ⟩
  (∃ λ (get : A → B) → S.Coherently-constant univ (get ⁻¹_))  ↝⟨ inverse S.Lens-as-Σ ⟩□
  S.Lens univ A B                                             □

-- Lens≃Lens preserves getters and setters.

@0 Lens≃Lens-preserves-getters-and-setters :
  (bl : Block "Lens≃Lens")
  {A : Type a} {B : Type b}
  {univ : Univalence (a ⊔ b)} →
  Preserves-getters-and-setters-⇔ A B
    (_≃_.logical-equivalence (Lens≃Lens {univ = univ} bl))
Lens≃Lens-preserves-getters-and-setters ⊠ =
    (λ l →
       let open Lens l in
         refl _
       , (S.Lens.set erased-lens  ≡⟨ sym set≡set ⟩∎
          set                     ∎))
  , (λ _ → refl _ , refl _)

-- Lenses with stable view types are equal if their setters are equal
-- (in erased contexts, assuming univalence).

@0 lenses-equal-if-setters-equal :
  {A : Type a} {B : Type b} →
  Univalence (lsuc (a ⊔ b)) →
  (univ : Univalence (a ⊔ b)) →
  (l₁ l₂ : Lens univ A B) →
  (∥ B ∥ → B) →
  Lens.set l₁ ≡ Lens.set l₂ →
  l₁ ≡ l₂
lenses-equal-if-setters-equal univ′ univ l₁ l₂ stable =
  block λ bl →

  Lens.set l₁ ≡ Lens.set l₂                            ↔⟨ ≡⇒≃ $ sym $ cong₂ _≡_
                                                            (proj₂ $ proj₁ (Lens≃Lens-preserves-getters-and-setters bl) l₁)
                                                            (proj₂ $ proj₁ (Lens≃Lens-preserves-getters-and-setters bl) l₂) ⟩
  S.Lens.set (_≃_.to (Lens≃Lens bl) l₁) ≡
  S.Lens.set (_≃_.to (Lens≃Lens bl) l₂)                ↝⟨ S.lenses-equal-if-setters-equal univ′ _ _ _ stable ⟩

  _≃_.to (Lens≃Lens bl) l₁ ≡ _≃_.to (Lens≃Lens bl) l₂  ↔⟨ Eq.≃-≡ $ Lens≃Lens bl ⟩□

  l₁ ≡ l₂                                              □

------------------------------------------------------------------------
-- Changing the implementation of the getter and the setter

-- One can change the getter of a lens, provided that the new
-- implementation is extensionally equal to the old one.

with-other-getter :
  {A : Type a} {B : Type b}
  (@0 univ : Univalence (a ⊔ b))
  (l : Lens univ A B)
  (get : A → B) →
  @0 get ≡ Lens.get l →
  Lens univ A B
with-other-getter {A = A} {B = B} univ l get p = record l
  { get                       = get
  ; get⁻¹-coherently-constant =
      subst (S.Coherently-constant univ ∘ _⁻¹_)
        (sym p) L.get⁻¹-coherently-constant
  ; set≡set =
      L.set          ≡⟨ L.set≡set ⟩
      S.Lens.set l₁  ≡⟨ cong S.Lens.set $ sym l₂≡l₁ ⟩∎
      S.Lens.set l₂  ∎
  }
  where
  module L = Lens l

  @0 l₁ l₂ : S.Lens univ A B

  l₁ = record
    { get                       = L.get
    ; get⁻¹-coherently-constant = L.get⁻¹-coherently-constant
    }

  l₂ = record
    { get                       = get
    ; get⁻¹-coherently-constant =
        subst (S.Coherently-constant univ ∘ _⁻¹_) (sym p)
          L.get⁻¹-coherently-constant
    }

  @0 l₂≡l₁ : l₂ ≡ l₁
  l₂≡l₁ =
    _≃_.to (Eq.≃-≡ S.Lens-as-Σ) $ Σ-≡,≡→≡ p
      (subst (S.Coherently-constant univ ∘ _⁻¹_) p
         (subst (S.Coherently-constant univ ∘ _⁻¹_) (sym p)
            L.get⁻¹-coherently-constant)                     ≡⟨ subst-subst-sym _ _ _ ⟩∎

       L.get⁻¹-coherently-constant                           ∎)

_ : Lens.get (with-other-getter univ l g p) ≡ g
_ = refl _

_ : Lens.set (with-other-getter univ l g p) ≡ Lens.set l
_ = refl _

-- The resulting lens is equal to the old one (if the equality proof
-- is not erased).

with-other-getter≡ :
  {A : Type a} {B : Type b}
  (@0 univ : Univalence (a ⊔ b))
  (l : Lens univ A B)
  (get : A → B)
  (p : get ≡ Lens.get l) →
  with-other-getter univ l get p ≡ l
with-other-getter≡ {A = A} {B = B} univ l get p =
  _≃_.to (Eq.≃-≡ Lens-as-Σ) $ Σ-≡,≡→≡ p
    (subst (Coherently-constant-fibres univ) p
       L′.coherently-constant-fibres-get                             ≡⟨ push-subst-pair-× _ _ ⟩

     L.set ,
     subst
       (λ get →
          Erased
            (∃ λ (c : S.Coherently-constant univ (get ⁻¹_)) →
             L.set ≡
             S.Lens.set {univ = univ}
               (record { get⁻¹-coherently-constant = c })))
       p
       [ (L′.get⁻¹-coherently-constant , L′.set≡set) ]               ≡⟨ cong (L.set ,_) push-subst-[] ⟩

     L.set ,
     [ subst
         (λ get →
            ∃ λ (c : S.Coherently-constant univ (get ⁻¹_)) →
            L.set ≡
            S.Lens.set {univ = univ}
              (record { get⁻¹-coherently-constant = c }))
         p
         (L′.get⁻¹-coherently-constant , L′.set≡set)
     ]                                                               ≡⟨ cong (L.set ,_) $ []-cong [ push-subst-pair′ _ _ _ ] ⟩

     L.set ,
     [ ( L.get⁻¹-coherently-constant
       , subst
           (λ ((get , c) : ∃ (S.Coherently-constant univ ∘ _⁻¹_)) →
              L.set ≡
              S.Lens.set {univ = univ}
                (record { get⁻¹-coherently-constant = c }))
           (Σ-≡,≡→≡ p (subst-subst-sym _ _ _))
           L′.set≡set
       )
     ]                                                               ≡⟨⟩

     L.set ,
     [ ( L.get⁻¹-coherently-constant
       , let q = Σ-≡,≡→≡ {p₂ = _ , L.get⁻¹-coherently-constant}
                   p
                   (subst-subst-sym _ _ _)
         in
         subst
           (λ ((get , c) : ∃ (S.Coherently-constant univ ∘ _⁻¹_)) →
              L.set ≡
              S.Lens.set {univ = univ}
                (record { get⁻¹-coherently-constant = c }))
           q
           (trans L.set≡set
              (cong (S.Lens.set {univ = univ}) $ sym $
               _≃_.to (Eq.≃-≡ S.Lens-as-Σ) q))
       )
     ]                                                               ≡⟨ cong (L.set ,_) $ []-cong
                                                                        [ cong (L.get⁻¹-coherently-constant ,_) $
                                                                          elim₁
                                                                            (λ q →
                                                                               subst
                                                                                (λ ((get , c) : ∃ (S.Coherently-constant univ ∘ _⁻¹_)) →
                                                                                L.set ≡
                                                                                S.Lens.set {univ = univ}
                                                                                (record { get⁻¹-coherently-constant = c }))
                                                                                q
                                                                                (trans L.set≡set
                                                                                (cong (S.Lens.set {univ = univ}) $ sym $
                                                                                _≃_.to (Eq.≃-≡ S.Lens-as-Σ) q)) ≡
                                                                               L.set≡set)
                                                                            (trans (subst-refl _ _) $
                                                                             trans (cong (trans _) $
                                                                                trans (cong (cong _) $
                                                                                trans (cong sym (Eq.to-≃-≡-refl S.Lens-as-Σ))
                                                                                sym-refl) $
                                                                                cong-refl _) $
                                                                             trans-reflʳ _)
                                                                            _
                                                                        ] ⟩
     L.set , [ (L.get⁻¹-coherently-constant , L.set≡set) ]           ≡⟨⟩

     L.coherently-constant-fibres-get                                ∎)
  where
  module L = Lens l

  l′ : Lens univ A B
  l′ = record
    { get                       = get
    ; get⁻¹-coherently-constant =
        subst (S.Coherently-constant univ ∘ _⁻¹_) (sym p)
          L.get⁻¹-coherently-constant
    }

  module L′ = Lens l′

-- One can change the setter of a lens, provided that the new
-- implementation is extensionally equal to the old one.

with-other-setter :
  {A : Type a} {B : Type b}
  {@0 univ : Univalence (a ⊔ b)}
  (l : Lens univ A B)
  (set : A → B → A) →
  @0 set ≡ Lens.set l →
  Lens univ A B
with-other-setter l set p = record l
  { set     = set
  ; set≡set = trans p (Lens.set≡set l)
  }

_ : Lens.get (with-other-setter l s p) ≡ Lens.get l
_ = refl _

_ : Lens.set (with-other-setter l s p) ≡ s
_ = refl _

-- The resulting lens is equal to the old one (if the equality proof
-- is not erased).

with-other-setter≡ :
  {A : Type a} {B : Type b}
  {@0 univ : Univalence (a ⊔ b)}
  (l : Lens univ A B)
  (set : A → B → A)
  (p : set ≡ Lens.set l) →
  with-other-setter l set p ≡ l
with-other-setter≡ {univ = univ} l set p =
  _≃_.to (Eq.≃-≡ Lens-as-Σ) $ cong (get ,_) $ Σ-≡,≡→≡ p
    (subst
       (λ set → Erased
                  (∃ λ (c : S.Coherently-constant univ (get ⁻¹_)) →
                   set ≡
                   S.Lens.set {univ = univ}
                     (record { get⁻¹-coherently-constant = c })))
       p
       [ (get⁻¹-coherently-constant , trans p set≡set) ]             ≡⟨ push-subst-[] ⟩

     [ subst
         (λ set → ∃ λ (c : S.Coherently-constant univ (get ⁻¹_)) →
                  set ≡
                  S.Lens.set {univ = univ}
                    (record { get⁻¹-coherently-constant = c }))
         p
         (get⁻¹-coherently-constant , trans p set≡set)
     ]                                                               ≡⟨ []-cong [ push-subst-pair-× _ _ ] ⟩

     [ ( get⁻¹-coherently-constant
       , subst (_≡ S.Lens.set {univ = univ}
                     (record { get⁻¹-coherently-constant =
                                 get⁻¹-coherently-constant }))
           p
           (trans p set≡set)
       )
     ]                                                               ≡⟨ []-cong [ cong (_ ,_) subst-trans-sym ] ⟩

     [ ( get⁻¹-coherently-constant
       , trans (sym p) (trans p set≡set)
       )
     ]                                                               ≡⟨ []-cong [ cong (_ ,_) $ trans-sym-[trans] _ _ ] ⟩∎

     [ (get⁻¹-coherently-constant , set≡set) ]                       ∎)
  where
  open Lens l

------------------------------------------------------------------------
-- Code for converting from S.Lens to Lens

-- Data corresponding to the erased proofs of a lens.

record Erased-proofs
         {A : Type a} {B : Type b}
         (univ : Univalence (a ⊔ b))
         (get : A → B) (set : A → B → A) : Type (a ⊔ b) where
  field
    @0 get⁻¹-coherently-constant : S.Coherently-constant univ (get ⁻¹_)

  @0 erased-lens : S.Lens univ A B
  erased-lens = record
    { get                       = get
    ; get⁻¹-coherently-constant = get⁻¹-coherently-constant
    }

  field
    @0 set≡set : set ≡ S.Lens.set erased-lens

-- Extracts "erased proofs" from a lens (in erased contexts).

@0 Lens→Erased-proofs :
  (l : S.Lens univ A B) →
  Erased-proofs univ (S.Lens.get l) (S.Lens.set l)
Lens→Erased-proofs {univ = univ} l = proofs ⊠
  where
  module _ (bl : Unit) where

    open Erased-proofs

    l′ = _≃_.from (Lens≃Lens bl) l

    proofs : Erased-proofs univ (Lens.get l′) (Lens.set l′)
    proofs .get⁻¹-coherently-constant =
      Lens.get⁻¹-coherently-constant l′
    proofs .set≡set = Lens.set≡set l′

-- Converts two functions and some erased proofs to a lens.
--
-- Note that Agda can in many cases infer "get" and "set" from the
-- first explicit argument, see (for instance) id below.

Erased-proofs→Lens :
  {A : Type a} {B : Type b} {get : A → B} {set : A → B → A}
  {@0 univ : Univalence (a ⊔ b)} →
  @0 Erased-proofs univ get set →
  Lens univ A B
Erased-proofs→Lens {get = get} {set = set} ep = λ where
  .Lens.get                       → get
  .Lens.set                       → set
  .Lens.get⁻¹-coherently-constant →
    Erased-proofs.get⁻¹-coherently-constant ep
  .Lens.set≡set → Erased-proofs.set≡set ep

------------------------------------------------------------------------
-- Identity and composition

-- An identity lens.

id :
  {A : Type a}
  (@0 univ : Univalence a) →
  Lens univ A A
id univ = Erased-proofs→Lens (Lens→Erased-proofs (S.id univ))

-- Composition.

infix 9 ⟨_,_⟩_∘_

⟨_,_⟩_∘_ :
  {A : Type a} {B : Type b} {C : Type c}
  {@0 univ₁ : Univalence (b ⊔ c)}
  {@0 univ₂ : Univalence (a ⊔ b)}
  (@0 univ₃ : Univalence (a ⊔ c)) →
  @0 Univalence (a ⊔ b ⊔ c) →
  Lens univ₁ B C → Lens univ₂ A B → Lens univ₃ A C
⟨ univ₃ , univ ⟩ l₁ ∘ l₂ =
  Erased-proofs→Lens
    (subst (Erased-proofs _ _)
       (⟨ext⟩ λ a → ⟨ext⟩ λ c →
        S.Lens.set l₂′ a (S.Lens.set l₁′ (get l₂ a) c)  ≡⟨ cong (λ s → s a (S.Lens.set l₁′ (get l₂ a) c)) $
                                                           proj₂ $ proj₁ (Lens≃Lens-preserves-getters-and-setters ⊠) l₂ ⟩
        set l₂ a (S.Lens.set l₁′ (get l₂ a) c)          ≡⟨ cong (λ s → set l₂ a (s (get l₂ a) c)) $
                                                           proj₂ $ proj₁ (Lens≃Lens-preserves-getters-and-setters ⊠) l₁ ⟩∎
        set l₂ a (set l₁ (get l₂ a) c)                  ∎) $
     Lens→Erased-proofs (S.⟨ univ₃ , univ ⟩ l₁′ ∘ l₂′))
  where
  open Lens

  @0 l₁′ : _
  l₁′ = _≃_.to (Lens≃Lens ⊠) l₁

  @0 l₂′ : _
  l₂′ = _≃_.to (Lens≃Lens ⊠) l₂

-- The setter of a lens formed using composition is defined in the
-- "right" way.

set-∘ :
  ∀ {A : Type a} {B : Type b} {C : Type c}
    {@0 univ₁ : Univalence (b ⊔ c)}
    {@0 univ₂ : Univalence (a ⊔ b)}
  (@0 univ₃ : Univalence (a ⊔ c))
  (@0 univ₄ : Univalence (a ⊔ b ⊔ c))
  (l₁ : Lens univ₁ B C) (l₂ : Lens univ₂ A B) {a c} →
  Lens.set (⟨ univ₃ , univ₄ ⟩ l₁ ∘ l₂) a c ≡
  Lens.set l₂ a (Lens.set l₁ (Lens.get l₂ a) c)
set-∘ _ _ _ _ = refl _

-- Composition is associative if the view type of the resulting lens
-- is stable (in erased contexts, assuming univalence).

@0 associativity :
  {A : Type a} {B : Type b} {C : Type c} {D : Type d} →
  (∥ D ∥ → D) →
  (univ₁  : Univalence (c ⊔ d))
  (univ₂  : Univalence (b ⊔ c))
  (univ₃  : Univalence (a ⊔ b))
  (univ₄  : Univalence (a ⊔ d))
  (univ₅  : Univalence (a ⊔ c ⊔ d))
  (univ₆  : Univalence (a ⊔ c))
  (univ₇  : Univalence (a ⊔ b ⊔ c))
  (univ₈  : Univalence (a ⊔ b ⊔ d))
  (univ₉  : Univalence (b ⊔ d))
  (univ₁₀ : Univalence (b ⊔ c ⊔ d)) →
  Univalence (lsuc (a ⊔ d)) →
  (l₁ : Lens univ₁ C D) (l₂ : Lens univ₂ B C) (l₃ : Lens univ₃ A B) →
  ⟨ univ₄ , univ₅ ⟩ l₁ ∘ (⟨ univ₆ , univ₇ ⟩ l₂ ∘ l₃) ≡
  ⟨ univ₄ , univ₈ ⟩ (⟨ univ₉ , univ₁₀ ⟩ l₁ ∘ l₂) ∘ l₃
associativity stable _ _ _ univ₄ _ _ _ _ _ _ univ₁₁ l₁ l₂ l₃ =
  lenses-equal-if-setters-equal
    univ₁₁
    univ₄
    _
    _
    stable
    (refl _)

-- The identity lens is a left identity of composition if the view
-- type of the resulting lens is stable (assuming univalence).

@0 left-identity :
  {A : Type a} {B : Type b} →
  (∥ B ∥ → B) →
  (univ₁ : Univalence (a ⊔ b))
  (univ₂ : Univalence (a ⊔ b))
  (univ₃ : Univalence b) →
  Univalence (lsuc (a ⊔ b)) →
  (l : Lens univ₁ A B) →
  ⟨ univ₁ , univ₂ ⟩ id univ₃ ∘ l ≡ l
left-identity stable univ₁ univ₂ univ₃ univ₄ l =
  lenses-equal-if-setters-equal
    univ₄
    univ₁
    _
    _
    stable
    (refl _)

-- The identity lens is a right identity of composition if the view
-- type of the resulting lens is stable (assuming univalence).

@0 right-identity :
  {A : Type a} {B : Type b} →
  (∥ B ∥ → B) →
  (univ₁ : Univalence (a ⊔ b))
  (univ₂ : Univalence (a ⊔ b))
  (univ₃ : Univalence a) →
  Univalence (lsuc (a ⊔ b)) →
  (l : Lens univ₁ A B) →
  ⟨ univ₁ , univ₂ ⟩ l ∘ id univ₃ ≡ l
right-identity stable univ₁ univ₂ univ₃ univ₄ l =
  lenses-equal-if-setters-equal
    univ₄
    univ₁
    _
    _
    stable
    (refl _)


------------------------------------------------------------------------
-- Up-to techniques for CCS
------------------------------------------------------------------------

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

open import Prelude

module Bisimilarity.Up-to.CCS {ℓ} {Name : Type ℓ} where

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

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

open import Bisimilarity.CCS
import Bisimilarity.Equational-reasoning-instances
open import Equational-reasoning
open import Indexed-container
open import Labelled-transition-system.CCS Name
open import Relation

open import Bisimilarity CCS
open import Bisimilarity.Step CCS _[_]⟶_ using (Step; Step↔StepC)
open import Bisimilarity.Up-to CCS

-- Up to context for a very simple kind of context.

Up-to-! : Trans₂ ℓ (Proc ∞)
Up-to-! R (P , Q) =
  ∃ λ P′ → ∃ λ Q′ → P ≡ ! P′ × R (P′ , Q′) × ! Q′ ≡ Q

-- This transformer is size-preserving.

up-to-!-size-preserving : Size-preserving Up-to-!
up-to-!-size-preserving R⊆∼i (P′ , Q′ , refl , P′RQ′ , refl) =
  !-cong (R⊆∼i P′RQ′)

-- Up to context for CCS (for polyadic contexts).

Up-to-context : Trans₂ ℓ (Proc ∞)
Up-to-context R (P , Q) =
  ∃ λ n →
  ∃ λ (C : Context ∞ n) →
  ∃ λ Ps →
  ∃ λ Qs →
  Equal ∞ P (C [ Ps ])
    ×
  Equal ∞ Q (C [ Qs ])
    ×
  ∀ x → R (Ps x , Qs x)

-- Up to context is monotone.

up-to-context-monotone : Monotone Up-to-context
up-to-context-monotone R⊆S =
  Σ-map id $ Σ-map id $ Σ-map id $ Σ-map id $ Σ-map id $ Σ-map id
    (R⊆S ∘_)

-- Up to context is size-preserving.

up-to-context-size-preserving : Size-preserving Up-to-context
up-to-context-size-preserving
  R⊆∼i {P , Q} (_ , C , Ps , Qs , eq₁ , eq₂ , Ps∼Qs) =
    P         ∼⟨ ≡→∼ eq₁ ⟩
    C [ Ps ]  ∼⟨ C [ R⊆∼i ∘ Ps∼Qs ]-cong ⟩
    C [ Qs ]  ∼⟨ symmetric (≡→∼ eq₂) ⟩■
    Q

-- Note that up to context is not compatible (assuming that Name is
-- inhabited).
--
-- This counterexample is a minor variant of one due to Pous and
-- Sangiorgi, who state that up to context would have been compatible
-- if the Step function had been defined in a slightly different way
-- (see Remark 6.4.2 in "Enhancements of the bisimulation proof
-- method").

¬-up-to-context-compatible : Name → ¬ Compatible Up-to-context
¬-up-to-context-compatible x comp = contradiction
  where
  a = x , true
  b = x , false
  c = a
  d = a

  a≢b : ¬ a ≡ b
  a≢b ()

  data R : Rel₂ ℓ (Proc ∞) where
    base : R (! name a ∙ (b ∙) , ! name a ∙ (c ∙))

  data S₀ : Rel₂ ℓ (Proc ∞) where
    base : S₀ (! name a ∙ (b ∙) ∣ b ∙ , ! name a ∙ (c ∙) ∣ c ∙)

  S : Rel₂ ℓ (Proc ∞)
  S = Up-to-bisimilarity S₀

  d!ab[R]d!ac : Up-to-context R ( name d ∙ (! name a ∙ (b ∙))
                                , name d ∙ (! name a ∙ (c ∙))
                                )
  d!ab[R]d!ac =
      1
    , name d · (λ { .force → hole fzero })
    , (λ _ → ! name a ∙ (b ∙))
    , (λ _ → ! name a ∙ (c ∙))
    , refl · (λ { .force → Proc-refl _ })
    , refl · (λ { .force → Proc-refl _ })
    , (λ _ → base)

  ¬!ab[S]!ac : ¬ Up-to-context S (! name a ∙ (b ∙) , ! name a ∙ (c ∙))
  ¬!ab[S]!ac (n , C , Ps , Qs , !ab≡C[Ps] , !ac≡C[Qs] , PsSQs) =

                                    $⟨ Matches-[] C ⟩
    Matches ∞ (C [ Ps ]) C          ↝⟨ Matches-cong (Proc-sym !ab≡C[Ps]) ⟩
    Matches ∞ (! name a ∙ (b ∙)) C  ↝⟨ helper !ab≡C[Ps] !ac≡C[Qs] ⟩□
    ⊥                               □

    where

    helper : Equal ∞ (! name a ∙ (b ∙)) (C [ Ps ]) →
             Equal ∞ (! name a ∙ (c ∙)) (C [ Qs ]) →
             ¬ Matches ∞ (! name a ∙ (b ∙)) C

    helper !ab≡ _ (hole x) = case PsSQs x of λ where
      (_ , Ps[x]∼!ab∣b , _ , base , _) →
                                                    $⟨ Ps[x]∼!ab∣b ⟩
        Ps x ∼ ! name a ∙ (b ∙) ∣ b ∙               ↝⟨ transitive {a = ℓ} (≡→∼ !ab≡) ⟩
        ! name a ∙ (b ∙) ∼ ! name a ∙ (b ∙) ∣ b ∙   ↝⟨ (λ !ab∼!ab∣b → Σ-map id proj₁ $ StepC.right-to-left !ab∼!ab∣b (par-right action)) ⟩
        (∃ λ P′ → ! name a ∙ (b ∙) [ name b ]⟶ P′)  ↝⟨ cancel-name ∘ !-only ·-only ∘ proj₂ ⟩
        a ≡ b                                       ↝⟨ a≢b ⟩□
        ⊥                                           □

    helper (! ab≡) (! ac≡) (! hole x) = case PsSQs x of λ where
      (_ , Ps[x]∼!ab∣b , _ , base , _) →
                                                  $⟨ Ps[x]∼!ab∣b ⟩
        Ps x ∼ ! name a ∙ (b ∙) ∣ b ∙             ↝⟨ transitive {a = ℓ} (≡→∼ ab≡) ⟩
        name a ∙ (b ∙) ∼ ! name a ∙ (b ∙) ∣ b ∙   ↝⟨ (λ ab∼!ab∣b → Σ-map id proj₁ $ StepC.right-to-left ab∼!ab∣b (par-right action)) ⟩
        (∃ λ P′ → name a ∙ (b ∙) [ name b ]⟶ P′)  ↝⟨ cancel-name ∘ ·-only ∘ proj₂ ⟩
        a ≡ b                                     ↝⟨ a≢b ⟩□
        ⊥                                         □

    helper (! (_ · b≡′)) (! (_ · c≡′)) (! action {C = C} M) =
      case force C , force b≡′ , force c≡′ , force M {j = ∞} ⦂
             (∃ λ C → Equal ∞ _ (C [ Ps ]) ×
                      Equal ∞ _ (C [ Qs ]) × Matches ∞ _ C) of λ where

        (._ , b≡ , _ , hole x) → case PsSQs x of λ where
          (_ , Ps[x]∼!ab∣b , _ , base , _) →
                                           $⟨ Ps[x]∼!ab∣b ⟩
            Ps x ∼ ! name a ∙ (b ∙) ∣ b ∙  ↝⟨ transitive {a = ℓ} (≡→∼ b≡) ⟩
            b ∙ ∼ ! name a ∙ (b ∙) ∣ b ∙   ↝⟨ (λ b∼!ab∣b → Σ-map id proj₁ $
                                                             StepC.right-to-left b∼!ab∣b (par-left (replication (par-right action)))) ⟩
            (∃ λ P′ → b ∙ [ name a ]⟶ P′)  ↝⟨ cancel-name ∘ ·-only ∘ proj₂ ⟩
            b ≡ a                          ↝⟨ a≢b ∘ sym ⟩□
            ⊥                              □

        (._ , _ , c≡b · _ , action _) →
                           $⟨ c≡b ⟩
          name c ≡ name b  ↝⟨ cancel-name ⟩
          c ≡ b            ↝⟨ a≢b ⟩□
          ⊥                □

  R⊆StepS : R ⊆ ⟦ StepC ⟧ S
  R⊆StepS base = StepC.⟨ lr , rl ⟩
    where
    lr : ∀ {P′ μ} →
         ! name a ∙ (b ∙) [ μ ]⟶ P′ →
         ∃ λ Q′ → ! name a ∙ (c ∙) [ μ ]⟶ Q′ × S (P′ , Q′)
    lr {P′} {μ} !ab[μ]⟶P′ = case 6-1-3-2 !ab[μ]⟶P′ of λ where
      (inj₁ (.(b ∙) , action , P′∼!ab∣b)) →
          ! name a ∙ (c ∙) ∣ c ∙
        , replication (par-right action)
        , _
        , (P′                      ∼⟨ P′∼!ab∣b ⟩■
           ! name a ∙ (b ∙) ∣ b ∙)
        , _
        , base
        , (! name a ∙ (c ∙) ∣ c ∙  ■)

      (inj₂ (μ≡τ , _)) → ⊥-elim $ name≢τ (
        name a  ≡⟨ !-only ·-only !ab[μ]⟶P′ ⟩
        μ       ≡⟨ μ≡τ ⟩∎
        τ       ∎)

    rl : ∀ {Q′ μ} →
         ! name a ∙ (c ∙) [ μ ]⟶ Q′ →
         ∃ λ P′ → ! name a ∙ (b ∙) [ μ ]⟶ P′ × S (P′ , Q′)
    rl {Q′} {μ} !ac[μ]⟶Q′ = case 6-1-3-2 !ac[μ]⟶Q′ of λ where
      (inj₁ (.(c ∙) , action , Q′∼!ac∣c)) →
          ! name a ∙ (b ∙) ∣ b ∙
        , replication (par-right action)
        , _
        , (! name a ∙ (b ∙) ∣ b ∙  ■)
        , _
        , base
        , (! name a ∙ (c ∙) ∣ c ∙  ∼⟨ symmetric Q′∼!ac∣c ⟩■
           Q′)

      (inj₂ (μ≡τ , _)) → ⊥-elim $ name≢τ (
        name a  ≡⟨ !-only ·-only !ac[μ]⟶Q′ ⟩
        μ       ≡⟨ μ≡τ ⟩∎
        τ       ∎)

  -- Note the use of compatibility in [R]⊆Step[S].

  [R]⊆Step[S] : Up-to-context R ⊆ ⟦ StepC ⟧ (Up-to-context S)
  [R]⊆Step[S] =
    Up-to-context R              ⊆⟨ up-to-context-monotone (λ {x} → R⊆StepS {x}) ⟩
    Up-to-context (⟦ StepC ⟧ S)  ⊆⟨ comp ⟩∎
    ⟦ StepC ⟧ (Up-to-context S)  ∎

  contradiction : ⊥
  contradiction =
                                                               $⟨ d!ab[R]d!ac ⟩
    Up-to-context R ( name d ∙ (! name a ∙ (b ∙))
                    , name d ∙ (! name a ∙ (c ∙))
                    )                                          ↝⟨ [R]⊆Step[S] ⟩

    ⟦ StepC ⟧ (Up-to-context S) ( name d ∙ (! name a ∙ (b ∙))
                                , name d ∙ (! name a ∙ (c ∙))
                                )                              ↝⟨ (λ s → StepC.left-to-right s action) ⟩

    (∃ λ P → name d ∙ (! name a ∙ (c ∙)) [ name d ]⟶ P ×
             Up-to-context S (! name a ∙ (b ∙) , P))           ↝⟨ (λ { (P , d!ac[d]⟶P  , !ab[S]P) →
                                                                       subst (Up-to-context S ∘ (_ ,_)) (·-only⟶ d!ac[d]⟶P) !ab[S]P }) ⟩

    Up-to-context S (! name a ∙ (b ∙) , ! name a ∙ (c ∙))      ↝⟨ ¬!ab[S]!ac ⟩□

    ⊥                                                          □


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

open import HoTT

module experimental.CubicalTypes where

data access : ℕ → ℕ → Type₀ where
  [] : access O O
  _#up : ∀ {n} {k} → access n k → access (S n) k
  _#down : ∀ {n} {k} → access n k → access (S n) k
  _#keep : ∀ {n} {k} → access n k → access (S n) (S k)

_a∙_ : ∀ {n k l} → access n k → access k l → access n l
_a∙_ [] a₂ = a₂
_a∙_ (a₁ #up) a₂ = (a₁ a∙ a₂) #up
_a∙_ (a₁ #down) a₂ = (a₁ a∙ a₂) #down
_a∙_ (a₁ #keep) (a₂ #up) = (a₁ a∙ a₂) #up
_a∙_ (a₁ #keep) (a₂ #down) = (a₁ a∙ a₂) #down
_a∙_ (a₁ #keep) (a₂ #keep) = (a₁ a∙ a₂) #keep


module Cats.Bifunctor where

open import Data.Product using (_,_)
open import Level using (_⊔_)
open import Relation.Binary using (IsEquivalence ; _Preserves₂_⟶_⟶_)

open import Cats.Category
open import Cats.Category.Cat using (_∘_ ; ∘-resp ; _≈_ ; equiv)
open import Cats.Category.Cat.Facts.Product using (Swap ; hasBinaryProducts)
open import Cats.Category.Fun using (Fun ; Trans ; ≈-intro ; ≈-elim)
open import Cats.Category.Product.Binary using (_×_)
open import Cats.Category.Product.Binary.Facts using (iso-intro)
open import Cats.Functor using (Functor)
open import Cats.Trans.Iso using (NatIso)
open import Cats.Util.Simp using (simp!)

import Cats.Category.Constructions.Iso as Iso
import Cats.Category.Fun.Facts as Fun

open Functor
open Trans
open Iso.Build._≅_

module _ {lo la l≈ lo′ la′ l≈′ lo″ la″ l≈″} where

  Bifunctor
    : Category lo la l≈
    → Category lo′ la′ l≈′
    → Category lo″ la″ l≈″
    → Set (lo ⊔ la ⊔ l≈ ⊔ lo′ ⊔ la′ ⊔ l≈′ ⊔ lo″ ⊔ la″ ⊔ l≈″)
  Bifunctor C D E = Functor (C × D) E

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

    private
      module C = Category C
      module D = Category D
      module E = Category E

    biobj : Bifunctor C D E → C.Obj → D.Obj → E.Obj
    biobj F c d = fobj F (c , d)

    bimap
      : (F : Bifunctor C D E)
      → ∀ {A B A′ B′}
      → (A C.⇒ B)
      → (A′ D.⇒ B′)
      → biobj F A A′ E.⇒ biobj F B B′
    bimap F f g = fmap F (f , g)

    Bifunctor→Functor₁ : Bifunctor C D E → C.Obj → Functor D E
    Bifunctor→Functor₁ F x = record
        { fobj = λ d → fobj F (x , d)
        ; fmap = λ f → fmap F (C.id , f)
        ; fmap-resp = λ x≈y → fmap-resp F (C.≈.refl , x≈y)
        ; fmap-id = fmap-id F
        ; fmap-∘ = λ where
            {f = f} {g} →
              begin
                fmap F (C.id , f) E.∘ fmap F (C.id , g)
              ≈⟨ fmap-∘ F ⟩
                fmap F (C.id C.∘ C.id , f D.∘ g)
              ≈⟨ fmap-resp F (C.id-l , D.≈.refl) ⟩
                fmap F (C.id , f D.∘ g)
              ∎
        }
      where
        open E.≈-Reasoning

    Bifunctor→Functor₁-resp : ∀ {F G x y}
      → F ≈ G
      → x C.≅ y
      → Bifunctor→Functor₁ F x ≈ Bifunctor→Functor₁ G y
    Bifunctor→Functor₁-resp {F} {G} {x} {y}
      record { iso = Fx≅Gx ; forth-natural = fnat } x≅y
        = record
        { iso = λ {z} →
            let open E.≅-Reasoning in
            begin
              fobj F (x , z)
            ≈⟨ Fx≅Gx ⟩
              fobj G (x , z)
            ≈⟨ fobj-resp G (iso-intro x≅y D.≅.refl) ⟩
              fobj G (y , z)
            ∎
        ; forth-natural = λ {c} {d} {f} →
            let open E.≈-Reasoning in
            triangle (fmap G (forth x≅y , f) E.∘ forth Fx≅Gx)
            ( begin
                ((E.id E.∘ fmap G (forth x≅y , D.id)) E.∘ forth Fx≅Gx) E.∘ fmap F (C.id , f)
              ≈⟨ simp! E ⟩
                fmap G (forth x≅y , D.id) E.∘ forth Fx≅Gx E.∘ fmap F (C.id , f)
              ≈⟨ E.∘-resp-r fnat ⟩
                fmap G (forth x≅y , D.id) E.∘ (fmap G (C.id , f) E.∘ forth Fx≅Gx)
              ≈⟨ simp! E ⟩
                (fmap G (forth x≅y , D.id) E.∘ fmap G (C.id , f)) E.∘ forth Fx≅Gx
              ≈⟨ E.∘-resp-l (fmap-∘ G) ⟩
                fmap G (forth x≅y C.∘ C.id , D.id D.∘ f) E.∘ forth Fx≅Gx
              ≈⟨ E.∘-resp-l (fmap-resp G (C.id-r , D.id-l)) ⟩
                fmap G (forth x≅y , f) E.∘ forth Fx≅Gx
              ∎
            )
            ( begin
                fmap G (C.id , f) E.∘ (E.id E.∘ fmap G (forth x≅y , D.id)) E.∘ forth Fx≅Gx
              ≈⟨ E.∘-resp-r (E.∘-resp-l E.id-l) ⟩
                fmap G (C.id , f) E.∘ fmap G (forth x≅y , D.id) E.∘ forth Fx≅Gx
              ≈⟨ simp! E ⟩
                (fmap G (C.id , f) E.∘ fmap G (forth x≅y , D.id)) E.∘ forth Fx≅Gx
              ≈⟨ E.∘-resp-l (fmap-∘ G) ⟩
                fmap G (C.id C.∘ forth x≅y , f D.∘ D.id) E.∘ forth Fx≅Gx
              ≈⟨ E.∘-resp-l (fmap-resp G (C.id-l , D.id-r)) ⟩
                fmap G (forth x≅y , f) E.∘ forth Fx≅Gx
              ∎
            )
        }

    transposeBifunctor₁ : Bifunctor C D E → Functor C (Fun D E)
    transposeBifunctor₁ F = record
        { fobj = Bifunctor→Functor₁ F
        ; fmap = λ {a} {b} f → record
            { component = λ d → fmap F (f , D.id)
            ; natural = λ {a′} {b′} {g} →
                begin
                  fmap F (f , D.id) E.∘ fmap (Bifunctor→Functor₁ F a) g
                ≈⟨ fmap-∘ F ⟩
                  fmap F (f C.∘ C.id , D.id D.∘ g)
                ≈⟨ fmap-resp F
                    ( (C.≈.trans C.id-r (C.≈.sym C.id-l))
                    , D.≈.trans D.id-l (D.≈.sym D.id-r) ) ⟩
                  fmap F (C.id C.∘ f , g D.∘ D.id)
                ≈⟨ E.≈.sym (fmap-∘ F) ⟩
                  fmap (Bifunctor→Functor₁ F b) g E.∘ fmap F (f , D.id)
                ∎
            }
        ; fmap-resp = λ x≈y → ≈-intro (fmap-resp F (x≈y , D.≈.refl))
        ; fmap-id = ≈-intro (fmap-id F)
        ; fmap-∘ = λ where
            {f = f} {g} → ≈-intro (
              begin
                fmap F (f , D.id) E.∘ fmap F (g , D.id)
              ≈⟨ fmap-∘ F ⟩
                fmap F (f C.∘ g , D.id D.∘ D.id)
              ≈⟨ fmap-resp F (C.≈.refl , D.id-l) ⟩
                fmap F (f C.∘ g , D.id)
              ∎)
        }
      where
        open E.≈-Reasoning

    transposeBifunctor₁-resp : ∀ {F G}
      → F ≈ G
      → transposeBifunctor₁ F ≈ transposeBifunctor₁ G
    transposeBifunctor₁-resp {F} {G} F≈G = record
        { iso = Fun.≈→≅ (Bifunctor→Functor₁-resp F≈G C.≅.refl)
        ; forth-natural = λ {c} {d} {f} → ≈-intro (
            let open E.≈-Reasoning in
            triangle (fmap G (f , D.id) E.∘ forth (iso F≈G))
            ( begin
                ((E.id E.∘ fmap G (C.id , D.id)) E.∘ forth (iso F≈G)) E.∘ fmap F (f , D.id)
              ≈⟨ E.∘-resp-l (E.≈.trans (E.∘-resp-l (E.≈.trans E.id-l (fmap-id G))) E.id-l) ⟩
                forth (iso F≈G) E.∘ fmap F (f , D.id)
              ≈⟨ forth-natural F≈G ⟩
                (fmap G (f , D.id) E.∘ forth (iso F≈G))
              ∎
            )
            ( begin
                fmap G (f , D.id) E.∘ (E.id E.∘ fmap G (C.id , D.id)) E.∘ forth (iso F≈G)
              ≈⟨ E.∘-resp-r (E.≈.trans (E.∘-resp-l (E.≈.trans E.id-l (fmap-id G))) E.id-l) ⟩
                fmap G (f , D.id) E.∘ forth (iso F≈G)
              ∎
            ))
        }
      where
        open E.≈-Reasoning
        open NatIso using (iso ; forth-natural)

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

  private
    module C = Category C
    module D = Category D
    module E = Category E

  Bifunctor→Functor₂ : Bifunctor C D E → D.Obj → Functor C E
  Bifunctor→Functor₂ F x = Bifunctor→Functor₁ (F ∘ Swap) x

  Bifunctor→Functor₂-resp : ∀ {F G x y}
    → F ≈ G
    → x D.≅ y
    → Bifunctor→Functor₂ F x ≈ Bifunctor→Functor₂ G y
  Bifunctor→Functor₂-resp {F} {G} F≈G x≅y
      = Bifunctor→Functor₁-resp {F = F ∘ Swap} {G = G ∘ Swap} (∘-resp F≈G refl) x≅y
    where
      open IsEquivalence equiv

  transposeBifunctor₂ : Bifunctor C D E → Functor D (Fun C E)
  transposeBifunctor₂ F = transposeBifunctor₁ (F ∘ Swap)

  transposeBifunctor₂-resp : ∀ {F G}
    → F ≈ G → transposeBifunctor₂ F ≈ transposeBifunctor₂ G
  transposeBifunctor₂-resp {F} {G} F≈G
      = transposeBifunctor₁-resp {F = F ∘ Swap} {G = G ∘ Swap} (∘-resp F≈G refl)
    where
      open IsEquivalence equiv


open import Agda.Primitive using (lzero; lsuc; _⊔_)
open import Relation.Binary.PropositionalEquality using (_≡_; refl; subst)

open import Relation.Binary

import MultiSorted.Context as MSC
import MultiSorted.AlgebraicTheory as MST
import SingleSorted.AlgebraicTheory

module MultiSorted.FromSingleSorted
  (Σ : SingleSorted.AlgebraicTheory.Signature)
  (𝒯 : SingleSorted.AlgebraicTheory.Theory lzero Σ)
  where

module SS where
  open SingleSorted.AlgebraicTheory public
  open SingleSorted.AlgebraicTheory.Signature Σ public

data 𝒜 : Set where
  A : 𝒜

single-sort : ∀ (X : 𝒜) → X ≡ A
single-sort A = refl

-- We have to transform the following constructions from the single sorted to the multi sorted setting
-- contexts, variables, operations, terms, equations, theory

singleSortedToMultiSortedContext : ∀ {𝓈} {Sort : Set 𝓈} (X : Sort) → SS.Context → MSC.Context Sort
singleSortedToMultiSortedContext _ SS.ctx-empty = MSC.Context.ctx-empty
singleSortedToMultiSortedContext X SS.ctx-slot = MSC.Context.ctx-slot X
singleSortedToMultiSortedContext X (SS.ctx-concat Γ Δ) =
  MSC.Context.ctx-concat (singleSortedToMultiSortedContext X Γ) (singleSortedToMultiSortedContext X Δ)

multiSortedToSingleSortedContext : ∀ {𝓈} {Sort : Set 𝓈} (X : Sort) → MSC.Context Sort → SS.Context
multiSortedToSingleSortedContext X MSC.ctx-empty = SS.ctx-empty
multiSortedToSingleSortedContext X (MSC.ctx-slot x) = SS.ctx-slot
multiSortedToSingleSortedContext X (MSC.ctx-concat Γ Δ) =
  SS.ctx-concat (multiSortedToSingleSortedContext X Γ) (multiSortedToSingleSortedContext X Δ)

S : MST.Signature
S = record { sort = 𝒜
           ; oper = SS.Signature.oper Σ
           ; oper-arity = λ{ f → singleSortedToMultiSortedContext A (SS.Signature.oper-arity Σ f) }
           ; oper-sort = λ{ f → A }
           }

module MS where
  open MST.Signature S public

singleSortedToMultiSortedVar : ∀ {Γ : SS.Context}
  → SS.var Γ  → MS.var (singleSortedToMultiSortedContext A Γ)
singleSortedToMultiSortedVar SS.var-var = MS.var-var
singleSortedToMultiSortedVar (SS.var-inl x) = MS.var-inl (singleSortedToMultiSortedVar x)
singleSortedToMultiSortedVar (SS.var-inr x) = MS.var-inr (singleSortedToMultiSortedVar x)

coerce : ∀ {Γ : MS.Context} {X} {Y} → MS.Term Γ X → MS.Term Γ Y
coerce {Γ} {A} {A} t = subst (MS.Term Γ) refl t

argToArg : ∀ {Δ : SS.Context} → MS.arg (singleSortedToMultiSortedContext A Δ) → SS.arg Δ
argToArg {SS.ctx-slot} i = SS.var-var
argToArg {SS.ctx-concat Δ Γ} (MSC.var-inl i) = SS.var-inl (argToArg i)
argToArg {SS.ctx-concat Δ Γ} (MSC.var-inr i) = SS.var-inr (argToArg i)

singleSortedToMultiSortedTerm : ∀ {Γ : SS.Context}
  → SS.Term Γ
  → MS.Term (singleSortedToMultiSortedContext A Γ) A
singleSortedToMultiSortedTerm {Γ} (SS.tm-var x) = coerce (MS.tm-var (singleSortedToMultiSortedVar x))
singleSortedToMultiSortedTerm (SS.tm-oper f ts) = MS.tm-oper f
  (λ i → coerce (singleSortedToMultiSortedTerm (ts (argToArg i))))

singleSortedToMultiSortedEquation : SS.Equation → MST.Equation S
singleSortedToMultiSortedEquation eq =
  MST.make-eq
    (singleSortedToMultiSortedContext A (SS.Equation.eq-ctx eq))
    A
    (singleSortedToMultiSortedTerm (SS.Equation.eq-lhs eq))
    (singleSortedToMultiSortedTerm (SS.Equation.eq-rhs eq))

𝓣 : MST.Theory lzero S
𝓣 = record { ax = SS.Theory.ax 𝒯
            ; ax-eq = λ{ a → singleSortedToMultiSortedEquation (SS.Theory.ax-eq 𝒯 a)}
            }


open import Nat
open import Prelude
open import dynamics-core
open import contexts
open import lemmas-consistency
open import type-assignment-unicity
open import lemmas-progress-checks

-- taken together, the theorems in this file argue that for any expression
-- d, at most one summand of the labeled sum that results from progress may
-- be true at any time: that boxed values, indeterminates, and expressions
-- that step are pairwise disjoint.
--
-- note that as a consequence of currying and comutativity of products,
-- this means that there are three theorems to prove. in addition to those,
-- we also prove several convenince forms that combine theorems about
-- indeterminate and boxed value forms into the same statement about final
-- forms, which mirrors the mutual definition of indeterminate and final
-- and saves some redundant argumentation.
module progress-checks where
  -- boxed values are not indeterminates
  boxedval-not-indet : ∀{d} → d boxedval → d indet → ⊥
  boxedval-not-indet (BVVal VNum) ()
  boxedval-not-indet (BVVal VLam) ()
  boxedval-not-indet (BVArrCast x bv) (ICastArr x₁ ind) = boxedval-not-indet bv ind
  boxedval-not-indet (BVHoleCast x bv) (ICastGroundHole x₁ ind) = boxedval-not-indet bv ind
  boxedval-not-indet (BVHoleCast x bv) (ICastHoleGround x₁ ind x₂) = boxedval-not-indet bv ind
  boxedval-not-indet (BVVal (VInl x)) (IInl ind) = boxedval-not-indet (BVVal x) ind
  boxedval-not-indet (BVVal (VInr x)) (IInr ind) = boxedval-not-indet (BVVal x) ind
  boxedval-not-indet (BVInl bv) (IInl ind) = boxedval-not-indet bv ind
  boxedval-not-indet (BVInr bv) (IInr ind) = boxedval-not-indet bv ind
  boxedval-not-indet (BVSumCast x bv) (ICastSum x₁ ind) = boxedval-not-indet bv ind
  boxedval-not-indet (BVVal (VPair x x₂)) (IPair1 ind x₁) = boxedval-not-indet (BVVal x) ind
  boxedval-not-indet (BVVal (VPair x x₂)) (IPair2 x₁ ind) = boxedval-not-indet (BVVal x₂) ind
  boxedval-not-indet (BVPair bv bv₁) (IPair1 ind x) = boxedval-not-indet bv ind
  boxedval-not-indet (BVPair bv bv₁) (IPair2 x ind) = boxedval-not-indet bv₁ ind
  boxedval-not-indet (BVProdCast x bv) (ICastProd x₁ ind) = boxedval-not-indet bv ind

  -- boxed values don't step
  boxedval-not-step : ∀{d} → d boxedval → (Σ[ d' ∈ ihexp ] (d ↦ d')) → ⊥
  boxedval-not-step (BVVal VNum) (d' , Step FHOuter () x₃)
  boxedval-not-step (BVVal VLam) (d' , Step FHOuter () x₃)
  boxedval-not-step (BVArrCast x bv) (d0' , Step FHOuter (ITCastID) FHOuter) = x refl
  boxedval-not-step (BVArrCast x bv) (_ , Step (FHCast x₁) x₂ (FHCast x₃)) = boxedval-not-step bv (_ , Step x₁ x₂ x₃)
  boxedval-not-step (BVHoleCast () bv) (d' , Step FHOuter (ITCastID) FHOuter)
  boxedval-not-step (BVHoleCast x bv) (d' , Step FHOuter (ITCastSucceed ()) FHOuter)
  boxedval-not-step (BVHoleCast GArrHole bv) (_ , Step FHOuter (ITGround (MGArr x)) FHOuter) = x refl
  boxedval-not-step (BVHoleCast x bv) (_ , Step (FHCast x₁) x₂ (FHCast x₃)) = boxedval-not-step bv (_ , Step x₁ x₂ x₃)
  boxedval-not-step (BVHoleCast x x₁) (_ , Step FHOuter (ITExpand ()) FHOuter)
  boxedval-not-step (BVHoleCast x x₁) (_ , Step FHOuter (ITCastFail x₂ () x₄) FHOuter)
  boxedval-not-step (BVVal (VInl x)) (_ , Step FHOuter () x₃)
  boxedval-not-step (BVVal (VInr x)) (_ , Step FHOuter () x₃)
  boxedval-not-step (BVInl bv) (_ , Step FHOuter () x₂)
  boxedval-not-step (BVInr bv) (_ , Step FHOuter () x₂)
  boxedval-not-step (BVSumCast x bv) (_ , Step FHOuter ITCastID FHOuter) = x refl
  boxedval-not-step (BVSumCast x bv) (_ , Step (FHCast x₁) x₂ (FHCast x₃)) = boxedval-not-step bv (_ , Step x₁ x₂ x₃)
  boxedval-not-step (BVHoleCast GNum bv) (_ , Step FHOuter (ITGround ()) FHOuter)
  boxedval-not-step (BVHoleCast GSumHole bv) (_ , Step FHOuter (ITGround (MGSum x)) FHOuter) = x refl
  boxedval-not-step (BVVal (VInl x)) (_ , Step (FHInl x₁) x₂ (FHInl x₃)) = boxedval-not-step (BVVal x) (_ , Step x₁ x₂ x₃)
  boxedval-not-step (BVVal (VInr x)) (_ , Step (FHInr x₁) x₂ (FHInr x₃)) = boxedval-not-step (BVVal x) (_ , Step x₁ x₂ x₃)
  boxedval-not-step (BVInl bv) (_ , Step (FHInl x) x₁ (FHInl x₂)) = boxedval-not-step bv (_ , Step x x₁ x₂)
  boxedval-not-step (BVInr bv) (_ , Step (FHInr x) x₁ (FHInr x₂)) = boxedval-not-step bv (_ , Step x x₁ x₂)
  boxedval-not-step (BVVal (VPair x x₁)) (_ , Step (FHPair1 x₂) x₃ (FHPair1 x₄)) = boxedval-not-step (BVVal x) (_ , Step x₂ x₃ x₄)
  boxedval-not-step (BVVal (VPair x x₁)) (_ , Step (FHPair2 x₂) x₃ (FHPair2 x₄)) = boxedval-not-step (BVVal x₁) (_ , (Step x₂ x₃ x₄))
  boxedval-not-step (BVPair bv bv₁) (_ , Step (FHPair1 x) x₁ (FHPair1 x₂)) = boxedval-not-step bv (_ , Step x x₁ x₂)
  boxedval-not-step (BVPair bv bv₁) (_ , Step (FHPair2 x) x₁ (FHPair2 x₂)) = boxedval-not-step bv₁ (_ , Step x x₁ x₂)
  boxedval-not-step (BVProdCast x bv) (_ , Step FHOuter ITCastID FHOuter) = x refl
  boxedval-not-step (BVProdCast x bv) (_ , Step (FHCast x₁) x₂ (FHCast x₃)) = boxedval-not-step bv (_ , Step x₁ x₂ x₃)
  boxedval-not-step (BVHoleCast GProdHole bv) (_ , Step FHOuter (ITGround (MGProd x)) FHOut\er) = x refl

  mutual
    -- indeterminates don't step
    indet-not-step : ∀{d} → d indet → (Σ[ d' ∈ ihexp ] (d ↦ d')) → ⊥
    indet-not-step IEHole (d' , Step FHOuter () FHOuter)
    indet-not-step (INEHole x) (d' , Step FHOuter () FHOuter)
    indet-not-step (INEHole x) (_ , Step (FHNEHole x₁) x₂ (FHNEHole x₃)) = final-sub-not-trans x x₁ x₂
    indet-not-step (IAp x₁ () x₂) (_ , Step FHOuter (ITLam) FHOuter)
    indet-not-step (IAp x (ICastArr x₁ ind) x₂) (_ , Step FHOuter (ITApCast) FHOuter) = x _ _ _ _ _  refl
    indet-not-step (IAp x ind _) (_ , Step (FHAp1 x₂) x₃ (FHAp1 x₄)) = indet-not-step ind (_ , Step x₂ x₃ x₄)
    indet-not-step (IAp x ind f) (_ , Step (FHAp2 x₃) x₄ (FHAp2 x₆)) = final-not-step f (_ , Step x₃ x₄ x₆)
    indet-not-step (ICastArr x ind) (d0' , Step FHOuter (ITCastID) FHOuter) = x refl
    indet-not-step (ICastArr x ind) (_ , Step (FHCast x₁) x₂ (FHCast x₃)) = indet-not-step ind (_ , Step x₁ x₂ x₃)
    indet-not-step (ICastGroundHole () ind) (d' , Step FHOuter (ITCastID) FHOuter)
    indet-not-step (ICastGroundHole x ind) (d' , Step FHOuter (ITCastSucceed ()) FHOuter)
    indet-not-step (ICastGroundHole GArrHole ind) (_ , Step FHOuter (ITGround (MGArr x₁)) FHOuter) = x₁ refl
    indet-not-step (ICastGroundHole x ind) (_ , Step (FHCast x₁) x₂ (FHCast x₃)) = indet-not-step ind (_ , Step x₁ x₂ x₃)
    indet-not-step (ICastHoleGround x ind ()) (d' , Step FHOuter (ITCastID ) FHOuter)
    indet-not-step (ICastHoleGround x ind g) (d' , Step FHOuter (ITCastSucceed  x₂) FHOuter) = x _ _ refl
    indet-not-step (ICastHoleGround x ind GArrHole) (_ , Step FHOuter (ITExpand (MGArr x₂)) FHOuter) = x₂ refl
    indet-not-step (ICastHoleGround x ind g) (_ , Step (FHCast x₁) x₂ (FHCast x₃)) = indet-not-step ind (_ , Step x₁ x₂ x₃)
    indet-not-step (ICastGroundHole x x₁) (_ , Step FHOuter (ITExpand ()) FHOuter)
    indet-not-step (ICastHoleGround x x₁ x₂) (_ , Step FHOuter (ITGround ()) FHOuter)
    indet-not-step (ICastGroundHole x x₁) (_ , Step FHOuter (ITCastFail x₂ () x₄) FHOuter)
    indet-not-step (ICastHoleGround x x₁ x₂) (_ , Step FHOuter (ITCastFail x₃ x₄ x₅) FHOuter) = x _ _ refl
    indet-not-step (IFailedCast x x₁ x₂ x₃) (d' , Step FHOuter () FHOuter)
    indet-not-step (IFailedCast x x₁ x₂ x₃) (_ , Step (FHFailedCast x₄) x₅ (FHFailedCast x₆)) = final-not-step x (_ , Step x₄ x₅ x₆)
    indet-not-step (IPlus1 () x₄) (_ , Step FHOuter (ITPlus x₁) FHOuter)
    indet-not-step (IPlus1 x x₄) (_ , Step (FHPlus1 x₁) x₂ (FHPlus1 x₃)) = indet-not-step x (_ , Step x₁ x₂ x₃)
    indet-not-step (IPlus1 x x₄) (.(_ ·+ _) , Step (FHPlus2 x₁) x₂ (FHPlus2 x₃)) = final-not-step x₄ (_ , Step x₁ x₂ x₃)
    indet-not-step (IPlus2 x ()) (_ , Step FHOuter (ITPlus x₁) FHOuter)
    indet-not-step (IPlus2 x x₄) (_ , Step (FHPlus1 x₁) x₂ (FHPlus1 x₃)) = final-not-step x (_ , Step x₁ x₂ x₃)
    indet-not-step (IPlus2 x x₄) (_ , Step (FHPlus2 x₁) x₂ (FHPlus2 x₃)) = indet-not-step x₄ (_ , Step x₁ x₂ x₃)
    indet-not-step (IInl ind) (_ , Step FHOuter () x₂)
    indet-not-step (IInl ind) (_ , Step (FHInl x) x₁ (FHInl x₂)) = indet-not-step ind (_ , Step x x₁ x₂)
    indet-not-step (IInr ind) (_ , Step FHOuter () x₂)
    indet-not-step (IInr ind) (_ , Step (FHInr x) x₁ (FHInr x₂)) = indet-not-step ind (_ , Step x x₁ x₂)
    indet-not-step (ICase x x₁ x₂ ind) (_ , Step FHOuter ITCaseInl x₅) = x _ _ refl
    indet-not-step (ICase x x₁ x₂ ind) (_ , Step FHOuter ITCaseInr x₅) = x₁ _ _ refl
    indet-not-step (ICase x x₁ x₂ ind) (_ , Step FHOuter ITCaseCast x₅) = x₂ _ _ _ _ _ refl
    indet-not-step (ICase x x₁ x₂ ind) (_ , Step (FHCase x₃) x₄ (FHCase x₅)) = indet-not-step ind (_ , Step x₃ x₄ x₅)
    indet-not-step (ICastSum x ind) (_ , Step FHOuter ITCastID FHOuter) = x refl
    indet-not-step (ICastSum x ind) (_ , Step (FHCast x₁) x₂ (FHCast x₃)) = indet-not-step ind (_ , Step x₁ x₂ x₃)
    indet-not-step (ICastGroundHole GNum ind) (_ , Step FHOuter (ITGround ()) FHOuter)
    indet-not-step (ICastGroundHole GSumHole ind) (_ , Step FHOuter (ITGround (MGSum x)) FHOuter) = x refl
    indet-not-step (ICastHoleGround x ind GNum) (_ , Step FHOuter (ITExpand ()) FHOuter)
    indet-not-step (ICastHoleGround x ind GSumHole) (_ , Step FHOuter (ITExpand (MGSum x₁)) FHOuter) = x₁ refl
    indet-not-step (IPair1 ind x) (_ , Step (FHPair1 x₁) x₂ (FHPair1 x₃)) = indet-not-step ind (_ , Step x₁ x₂ x₃)
    indet-not-step (IPair1 ind x) (_ , Step (FHPair2 x₁) x₂ (FHPair2 x₃)) = final-not-step x (_ , Step x₁ x₂ x₃)
    indet-not-step (IPair2 x ind) (_ , Step (FHPair1 x₁) x₂ (FHPair1 x₃)) = final-not-step x (_ , Step x₁ x₂ x₃)
    indet-not-step (IPair2 x ind) (_ , Step (FHPair2 x₁) x₂ (FHPair2 x₃)) = indet-not-step ind (_ , Step x₁ x₂ x₃)
    indet-not-step (IFst x x₁ ind) (_ , Step FHOuter ITFstPair FHOuter) = x _ _ refl
    indet-not-step (IFst x x₁ ind) (_ , Step (FHFst x₂) x₃ (FHFst x₄)) = indet-not-step ind (_ , Step x₂ x₃ x₄)
    indet-not-step (ISnd x x₁ ind) (_ , Step FHOuter ITSndPair FHOuter) = x _ _ refl
    indet-not-step (ISnd x x₁ ind) (_ , Step (FHSnd x₂) x₃ (FHSnd x₄)) = indet-not-step ind (_ , Step x₂ x₃ x₄)
    indet-not-step (ICastGroundHole GProdHole ind) (_ , Step FHOuter (ITGround (MGProd x)) FHOuter) = x refl
    indet-not-step (ICastHoleGround x ind GProdHole) (_ , Step FHOuter (ITExpand (MGProd x₁)) FHOuter) = x₁ refl
    indet-not-step (IPair1 ind x) (_ , Step FHOuter () FHOuter)
    indet-not-step (IPair2 x ind) (_ , Step FHOuter () FHOuter)
    indet-not-step (IFst x x₁ ind) (_ , Step FHOuter ITFstCast FHOuter) = x₁ _ _ _ _ _ refl
    indet-not-step (ISnd x x₁ ind) (_ , Step FHOuter ITSndCast FHOuter) = x₁ _ _ _ _ _ refl
    indet-not-step (ICastProd x ind) (_ , Step FHOuter ITCastID FHOuter) = x refl
    indet-not-step (ICastProd x ind) (_ , Step (FHCast x₁) x₂ (FHCast x₃)) = indet-not-step ind (_ , Step x₁ x₂ x₃)

    -- final expressions don't step
    final-not-step : ∀{d} → d final → Σ[ d' ∈ ihexp ] (d ↦ d') → ⊥
    final-not-step (FBoxedVal x) stp = boxedval-not-step x stp
    final-not-step (FIndet x) stp = indet-not-step x stp


-- The bug documented below was exposed by the fix to issue 274.

module Issue274 where

open import Common.Level

record Q a : Set (a ⊔ a) where

record R a : Set a where
  field q : Q a

  A : Set₁
  A = Set

postulate
  ℓ : Level
  r : R (ℓ ⊔ ℓ)

foo : R ℓ
foo = r

-- Issue274.agda:32,7-8
-- ℓ ⊔ ℓ !=< ℓ of type Level
-- when checking that the expression r has type R ℓ


{-# OPTIONS --safe #-}

module Cubical.HITs.SetQuotients.EqClass where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Univalence

open import Cubical.HITs.PropositionalTruncation as Prop
open import Cubical.HITs.SetQuotients as SetQuot

open import Cubical.Relation.Nullary
open import Cubical.Relation.Binary

open import Cubical.Functions.Embedding
open import Cubical.Functions.Surjection

private
  variable
    ℓ ℓ' ℓ'' : Level

-- another definition using equivalence classes

open BinaryRelation
open isEquivRel

open Iso

module _
  {ℓ ℓ' : Level}
  (X : Type ℓ) where

  ℙ : Type (ℓ-max ℓ (ℓ-suc ℓ'))
  ℙ = X → hProp ℓ'

  ℙDec : Type (ℓ-max ℓ (ℓ-suc ℓ'))
  ℙDec = Σ[ P ∈ ℙ ] ((x : X) → Dec (P x .fst))

  isSetℙ : isSet ℙ
  isSetℙ = isSetΠ λ x → isSetHProp

  isSetℙDec : isSet ℙDec
  isSetℙDec = isOfHLevelΣ 2 isSetℙ (λ P → isSetΠ (λ x → isProp→isSet (isPropDec (P x .snd))))

  module _
    (R : X → X → Type ℓ'') where

    isEqClass : ℙ → Type (ℓ-max (ℓ-max ℓ ℓ') ℓ'')
    isEqClass P = ∥ Σ[ x ∈ X ] ((a : X) → P a .fst ≃ ∥ R a x ∥) ∥

    isPropIsEqClass : (P : ℙ) → isProp (isEqClass P)
    isPropIsEqClass P = isPropPropTrunc

    _∥_ : Type (ℓ-max (ℓ-max ℓ (ℓ-suc ℓ')) ℓ'')
    _∥_ = Σ[ P ∈ ℙ ] isEqClass P

    isSet∥ : isSet _∥_
    isSet∥ = isOfHLevelΣ 2 isSetℙ (λ _ → isProp→isSet isPropPropTrunc)

    module _
      (dec : (x x' : X) → Dec (R x x')) where

      _∥Dec_ : Type (ℓ-max (ℓ-max ℓ (ℓ-suc ℓ')) ℓ'')
      _∥Dec_ = Σ[ P ∈ ℙDec ] isEqClass (P .fst)

      isDecEqClass : (P : _∥_) → (x : X) → Dec (P .fst x .fst)
      isDecEqClass (P , h) a =
        Prop.rec (isPropDec (P a .snd)) (λ (x , p) → EquivPresDec (invEquiv (p a)) (Dec∥∥ (dec a x))) h

      Iso-∥Dec-∥ : Iso _∥Dec_ _∥_
      Iso-∥Dec-∥ .fun P = P .fst .fst , P .snd
      Iso-∥Dec-∥ .inv P = (P .fst , isDecEqClass P) , P .snd
      Iso-∥Dec-∥ .rightInv P = refl
      Iso-∥Dec-∥ .leftInv P i .fst .fst = P .fst .fst
      Iso-∥Dec-∥ .leftInv P i .fst .snd x =
        isProp→PathP {B = λ i → Dec (P .fst .fst x .fst)}
          (λ i → isPropDec (P .fst .fst x .snd))
          (isDecEqClass (Iso-∥Dec-∥ .fun P) x) (P .fst .snd x) i
      Iso-∥Dec-∥ .leftInv P i .snd = P .snd

      ∥Dec≃∥ : _∥Dec_ ≃ _∥_
      ∥Dec≃∥ = isoToEquiv Iso-∥Dec-∥

module _
  {ℓ ℓ' ℓ'' : Level}
  (X : Type ℓ)
  (R : X → X → Type ℓ'')
  (h : isEquivRel R) where

  ∥Rx∥Iso : (x x' : X)(r : R x x') → (a : X) → Iso ∥ R a x ∥ ∥ R a x' ∥
  ∥Rx∥Iso x x' r a .fun = Prop.rec isPropPropTrunc (λ r' → ∣ h .transitive _ _ _ r' r ∣)
  ∥Rx∥Iso x x' r a .inv = Prop.rec isPropPropTrunc (λ r' → ∣ h .transitive _ _ _ r' (h .symmetric _ _ r) ∣)
  ∥Rx∥Iso x x' r a .leftInv _ = isPropPropTrunc _ _
  ∥Rx∥Iso x x' r a .rightInv _ = isPropPropTrunc _ _

  isEqClass∥Rx∥ : (x : X) → isEqClass X R (λ a → ∥ R a x ∥ , isPropPropTrunc)
  isEqClass∥Rx∥ x = ∣ x , (λ _ → idEquiv _) ∣

  ∥R∥ : (x : X) → X ∥ R
  ∥R∥ x = (λ a → ∥ R a x ∥ , isPropPropTrunc) , isEqClass∥Rx∥ x

  ∥Rx∥Path : (x x' : X)(r : R x x') → ∥R∥ x ≡ ∥R∥ x'
  ∥Rx∥Path x x' r i .fst a .fst = ua (isoToEquiv (∥Rx∥Iso x x' r a)) i
  ∥Rx∥Path x x' r i .fst a .snd =
    isProp→PathP {B = λ i → isProp (∥Rx∥Path x x' r i .fst a .fst)}
                 (λ i → isPropIsProp) isPropPropTrunc isPropPropTrunc i
  ∥Rx∥Path x x' r i .snd =
    isProp→PathP {B = λ i → isEqClass X R (∥Rx∥Path x x' r i .fst)}
                 (λ i → isPropIsEqClass X R (∥Rx∥Path x x' r i .fst))
                 (isEqClass∥Rx∥ x) (isEqClass∥Rx∥ x') i

  /→∥ : X / R → X ∥ R
  /→∥ = SetQuot.rec (isSet∥ X R) ∥R∥ (λ x x' r → ∥Rx∥Path x x' r)

  inj/→∥' : (x x' : X) → ∥R∥ x ≡ ∥R∥ x' → ∥ R x x' ∥
  inj/→∥' x x' p = transport (λ i → p i .fst x .fst) ∣ h .reflexive x ∣

  inj/→∥ : (x y : X / R) → /→∥ x ≡ /→∥ y → x ≡ y
  inj/→∥ =
    SetQuot.elimProp2 {P = λ x y → /→∥ x ≡ /→∥ y → x ≡ y}
      (λ _ _ → isPropΠ (λ _ → squash/ _ _))
      (λ x y q → Prop.rec (squash/ _ _) (λ r → eq/ _ _ r) (inj/→∥' x y q))

  isEmbedding/→∥ : isEmbedding /→∥
  isEmbedding/→∥ = injEmbedding squash/ (isSet∥ X R) (λ {x} {y} → inj/→∥ x y)

  surj/→∥ : (P : X ∥ R) → ((x , _) : Σ[ x ∈ X ] ((a : X) → P .fst a .fst ≃ ∥ R a x ∥)) → ∥R∥ x ≡ P
  surj/→∥ P (x , p) i .fst a .fst = ua (p a) (~ i)
  surj/→∥ P (x , p) i .fst a .snd =
    isProp→PathP {B = λ i → isProp (surj/→∥ P (x , p) i .fst a .fst)}
                 (λ i → isPropIsProp) isPropPropTrunc (P .fst a .snd) i
  surj/→∥ P (x , p) i .snd =
    isProp→PathP {B = λ i → isEqClass X R (surj/→∥ P (x , p) i .fst)}
                 (λ i → isPropIsEqClass X R (surj/→∥ P (x , p) i .fst))
                 (isEqClass∥Rx∥ x) (P .snd) i

  isSurjection/→∥ : isSurjection /→∥
  isSurjection/→∥ P = Prop.rec isPropPropTrunc (λ p → ∣ [ p .fst ] , surj/→∥ P p ∣) (P .snd)

  -- both definitions are equivalent
  equivQuot : X / R ≃ X ∥ R
  equivQuot = /→∥ , isEmbedding×isSurjection→isEquiv (isEmbedding/→∥ , isSurjection/→∥)