------------------------------------------------------------------------
-- Dependent lenses
------------------------------------------------------------------------

-- Some code below depends on UIP for Set ℓ (for some ℓ). This kind of
-- assumption is inconsistent in Cubical Agda. However, Andrea Vezzosi
-- and I have discussed whether UIP, the propositional truncation, and
-- extensionality are mutually consistent, and it seems plausible that
-- some form of extensional type theory with squash types would
-- provide a model for these things.
--
-- Note that the code that depends on UIP does not make use of
-- univalence, or at least it did not use to do this, library code may
-- have changed after the code was written. This development tracks
-- usage of univalence in types, but at the time of writing there is
-- some library code that does not do this.

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

module Lens.Dependent where

open import Equality.Propositional.Cubical
open import Logical-equivalence using (module _⇔_)
open import Prelude as P hiding (id; swap; Unit) renaming (_∘_ to _⊚_)

open import Bijection equality-with-J as Bij using (_↔_; module _↔_)
open import Bool equality-with-J
open import Equality.Decidable-UIP equality-with-J using (Constant)
open import Equality.Decision-procedures equality-with-J
import Equality.Groupoid equality-with-J as EG
open import Equality.Tactic equality-with-J as Tactic hiding (module Eq)
open import Equivalence equality-with-J as Eq using (_≃_; module _≃_)
open import Function-universe equality-with-J as F hiding (id; _∘_)
open import Groupoid equality-with-J
open import H-level equality-with-J as H-level
open import H-level.Closure equality-with-J
open import H-level.Truncation.Propositional equality-with-paths
  as Trunc
open import Surjection equality-with-J using (module _↠_)
open import Univalence-axiom equality-with-J

import Lens.Non-dependent.Higher equality-with-paths as ND

private
  variable
    a b b₁ b₂ b₃ c r r₁ r₂ r₃ : Level
    A                         : Set a

------------------------------------------------------------------------
-- Dependent lenses with "remainder types" visible in the type

-- If Lens₃ A R B is inhabited, then a value of type A can be split up
-- into a "remainder" r of type R and a value of type B r.

Lens₃ : Set a → (R : Set r) → (R → Set b) → Set _
Lens₃ A R B = A ≃ Σ R B

module Lens₃ {R : Set r} {B : R → Set b} (lens : Lens₃ A R B) where

  open _≃_

  -- The remainder.

  remainder : A → R
  remainder a = proj₁ (to lens a)

  -- Getter.

  get : (a : A) → B (remainder a)
  get a = proj₂ (to lens a)

  -- Setter.

  set : (a : A) → B (remainder a) → A
  set a b = from lens (remainder a , b)

  -- Modifier.

  modify : (a : A) → (B (remainder a) → B (remainder a)) → A
  modify a f = set a (f (get a))

  -- Setting leaves the remainder unchanged.

  remainder-set : ∀ a b → remainder (set a b) ≡ remainder a
  remainder-set a b =
    proj₁ (to lens (from lens (remainder a , b)))  ≡⟨ cong proj₁ (right-inverse-of lens _) ⟩∎
    remainder a                                    ∎

  -- A related isomorphism.

  codomain-set-≃ : ∀ {a b} → B (remainder (set a b)) ≃ B (remainder a)
  codomain-set-≃ = ≡⇒≃ (cong B (remainder-set _ _))

  -- Some lens laws.

  set-get : ∀ a → set a (get a) ≡ a
  set-get a =
    from lens (proj₁ (to lens a) , proj₂ (to lens a))  ≡⟨⟩
    from lens (to lens a)                              ≡⟨ left-inverse-of lens _ ⟩∎
    a                                                  ∎

  get-set₁ : ∀ a b → get (set a b) ≡ subst B (sym (remainder-set a b)) b
  get-set₁ a b =
    proj₂ (to lens (from lens (remainder a , b)))  ≡⟨ sym $ dcong proj₂ (sym lemma) ⟩

    subst (B ⊚ proj₁) (sym lemma)
          (proj₂ {B = B} (remainder a , b))        ≡⟨⟩

    subst (B ⊚ proj₁) (sym lemma) b                ≡⟨ subst-∘ _ _ (sym lemma) ⟩

    subst B (cong proj₁ (sym lemma)) b             ≡⟨ cong (λ x → subst B x b) (cong-sym _ lemma) ⟩∎

    subst B (sym (cong proj₁ lemma)) b             ∎
    where
    lemma = right-inverse-of lens _

  get-set₂ : ∀ a b → get (set a b) ≡ from codomain-set-≃ b
  get-set₂ a b =
    proj₂ (to lens (from lens (remainder a , b)))  ≡⟨ get-set₁ _ _ ⟩
    subst B (sym (cong proj₁ lemma)) b             ≡⟨ subst-in-terms-of-inverse∘≡⇒↝ equivalence (cong proj₁ lemma) _ _ ⟩∎
    ≡⇒← (cong B (cong proj₁ lemma)) b              ∎
    where
    lemma = right-inverse-of lens _

  set-set₁ : ∀ a b₁ b₂ →
             set (set a b₁) b₂ ≡ set a (subst B (remainder-set a b₁) b₂)
  set-set₁ a b₁ b₂ =
    from lens (remainder (set a b₁) , b₂)    ≡⟨ cong (from lens) (Σ-≡,≡→≡ eq refl) ⟩∎
    from lens (remainder a , subst B eq b₂)  ∎
    where
    eq = remainder-set a b₁

  set-set₂ : ∀ a b₁ b₂ →
             set (set a b₁) b₂ ≡ set a (to codomain-set-≃ b₂)
  set-set₂ a b₁ b₂ =
    set (set a b₁) b₂           ≡⟨ set-set₁ _ _ _ ⟩
    set a (subst B eq b₂)       ≡⟨ cong (set a) (subst-in-terms-of-≡⇒↝ equivalence eq _ _) ⟩∎
    set a (≡⇒→ (cong B eq) b₂)  ∎
    where
    eq = remainder-set a b₁

------------------------------------------------------------------------
-- Lens₃ combinators

-- Identity lens.

id₃ : Lens₃ A ⊤ (λ _ → A)
id₃ {A = A} =
  A      ↔⟨ inverse ×-left-identity ⟩□
  ⊤ × A  □

-- Composition of lenses.

infixr 9 _₃∘₃_

_₃∘₃_ : {R₁ : Set r₁} {B₁ : R₁ → Set b₁}
        {R₂ : R₁ → Set r₂} {B₂ : (r : R₁) → R₂ r → Set b₂} →
        (∀ {r} → Lens₃ (B₁ r) (R₂ r) (B₂ r)) → Lens₃ A R₁ B₁ →
        Lens₃ A (Σ R₁ R₂) (uncurry B₂)
_₃∘₃_ {A = A} {R₁ = R₁} {B₁ = B₁} {R₂ = R₂} {B₂ = B₂} l₁ l₂ =
  A                             ↔⟨ l₂ ⟩
  Σ R₁ B₁                       ↔⟨ ∃-cong (λ _ → l₁) ⟩
  Σ R₁ (λ r → Σ (R₂ r) (B₂ r))  ↔⟨ Σ-assoc ⟩□
  Σ (Σ R₁ R₂) (uncurry B₂)      □

------------------------------------------------------------------------
-- Some Lens₃ properties

-- id₃ and _₃∘₃_ form a kind of precategory.

left-identity₃ :
  {R : Set r} {B : R → Set b} →
  (l : Lens₃ A R B) →
  id₃ ₃∘₃ l
    ≡
  (A                      ↝⟨ l ⟩
   Σ R B                  ↝⟨ Σ-cong (inverse ×-right-identity) (λ _ → F.id) ⟩□
   Σ (R × ⊤) (B ⊚ proj₁)  □)
left-identity₃ _ = Eq.lift-equality ext refl

right-identity₃ :
  {R : Set r} {B : R → Set b} →
  (l : Lens₃ A R B) →
  l ₃∘₃ id₃
    ≡
  (A                      ↝⟨ l ⟩
   Σ R B                  ↝⟨ Σ-cong (inverse ×-left-identity) (λ _ → F.id) ⟩□
   Σ (⊤ × R) (B ⊚ proj₂)  □)
right-identity₃ _ = Eq.lift-equality ext refl

associativity₃ :
  {R₁ : Set r₁} {B₁ : R₁ → Set b₁}
  {R₂ : R₁ → Set r₂} {B₂ : (r₁ : R₁) → R₂ r₁ → Set b₂}
  {R₃ : (r₁ : R₁) → R₂ r₁ → Set r₃}
  {B₃ : (r₁ : R₁) (r₂ : R₂ r₁) → R₃ r₁ r₂ → Set b₃} →
  (l₁ : ∀ {r₁ r₂} → Lens₃ (B₂ r₁ r₂) (R₃ r₁ r₂) (B₃ r₁ r₂))
  (l₂ : ∀ {r} → Lens₃ (B₁ r) (R₂ r) (B₂ r))
  (l₃ : Lens₃ A R₁ B₁) →
  l₁ ₃∘₃ (l₂ ₃∘₃ l₃)
    ≡
  (A                                                             ↝⟨ (l₁ ₃∘₃ l₂) ₃∘₃ l₃ ⟩
   Σ (Σ R₁ (λ r₁ → Σ (R₂ r₁) (R₃ r₁))) (uncurry (uncurry ⊚ B₃))  ↝⟨ Σ-cong Σ-assoc (λ _ → F.id) ⟩□
   Σ (Σ (Σ R₁ R₂) (uncurry R₃)) (uncurry (uncurry B₃))           □)
associativity₃ _ _ _ = Eq.lift-equality ext refl

------------------------------------------------------------------------
-- Dependent lenses without "remainder types" visible in the type

-- One definition.

Lens : (A : Set a) → (A → Set b) → Set (lsuc (a ⊔ b))
Lens {a = a} {b = b} A B =
  ∃ λ (R : Set (a ⊔ b)) →
  ∃ λ (B′ : R → Set b) →
  ∃ λ (lens : Lens₃ A R B′) →
  ((r : R) → ∥ B′ r ∥)
    ×
  (∀ a → B′ (Lens₃.remainder lens a) ≡ B a)

-- An alternative definition. This definition is pointwise isomorphic
-- to the previous one, see Lens↔Lens′ below.

Lens′ : (A : Set a) → (A → Set b) → Set (lsuc (a ⊔ b))
Lens′ {a = a} {b = b} A B =
  ∃ λ (get : (a : A) → B a) →
  ∃ λ (R : Set (a ⊔ b)) →
  ∃ λ (remainder : A → R) →
  Surjective remainder
    ×
  ∃ λ (B′ : R → Set b) →
  ∃ λ (variant : ∀ {a} → B a ≡ B′ (remainder a)) →
  Eq.Is-equivalence {B = ∃ B′}
                    (λ a → remainder a , subst P.id variant (get a))

module Lens {A : Set a} {B : A → Set b} (l : Lens A B) where

  -- The remainder type: what remains of A when B is removed
  -- (roughly).

  R : Set (a ⊔ b)
  R = proj₁ l

  -- A variant of B, indexed by Rs instead of As.

  B′ : R → Set b
  B′ = proj₁ (proj₂ l)

  -- The main lens isomorphism.

  lens : Lens₃ A R B′
  lens = proj₁ (proj₂ (proj₂ l))

  -- If the remainder type is inhabited, then the corresponding view
  -- should also be (merely) inhabited.

  inhabited : (r : R) → ∥ B′ r ∥
  inhabited = proj₁ (proj₂ (proj₂ (proj₂ l)))

  private module L = Lens₃ lens
  open _≃_

  -- The "non-B" part of the A value.

  remainder : A → R
  remainder = L.remainder

  -- An equality that specifies in what sense B′ is a variant of B.
  --
  -- (This used to be a bijection, but it was turned into an
  -- equality in order to make it obvious that, if the K rule is
  -- enabled, then variant cannot do anything strange.)

  variant : ∀ a → B′ (remainder a) ≡ B a
  variant = proj₂ (proj₂ (proj₂ (proj₂ l)))

  -- A corresponding isomorphism.

  variant≃ : ∀ {a} → B′ (remainder a) ≃ B a
  variant≃ = ≡⇒≃ (variant _)

  -- A variant of variant.

  other-variant : ∀ r (b′ : B′ r) → B′ r ≡ B (from lens (r , b′))
  other-variant r b′ =
    B′ r                                       ≡⟨⟩
    B′ (proj₁ {B = B′} (r , b′))               ≡⟨ cong (B′ ⊚ proj₁) (sym $ right-inverse-of lens (r , b′)) ⟩
    B′ (proj₁ (to lens (from lens (r , b′))))  ≡⟨⟩
    B′ (remainder (from lens (r , b′)))        ≡⟨ variant (_≃_.from lens (r , b′)) ⟩∎
    B (from lens (r , b′))                     ∎

  -- Note that B ⊚ _≃_.from lens only depends on the "R part" of the
  -- argument.

  independent-of-B′ : (r : R) → Constant (B ⊚ _≃_.from lens ⊚ (r ,_))
  independent-of-B′ r b₁ b₂ =
    B (_≃_.from lens (r , b₁))  ≡⟨ sym $ other-variant _ _ ⟩
    B′ r                        ≡⟨ other-variant _ _ ⟩∎
    B (_≃_.from lens (r , b₂))  ∎

  -- Thus we can, assuming that Set b is a set, define a variant of B
  -- that only depends on R.

  module First-variant-of-B (uip : Is-set (Set b)) where

    private

      B̲′ : (r : R) → ∥ B′ r ∥ → Set b
      B̲′ r =
        to (constant-function≃∥inhabited∥⇒inhabited uip)
           (B ⊚ _≃_.from lens ⊚ (r ,_) , independent-of-B′ r)

    B̲ : R → Set b
    B̲ r = B̲′ r (inhabited r)

    -- This type family is pointwise equal to B′ (given the same
    -- assumptions).

    B′≡B̲ : ∀ r → B′ r ≡ B̲ r
    B′≡B̲ r = Trunc.elim
      (λ ∥b′∥ → B′ r ≡ B̲′ r ∥b′∥)
      (λ _ → uip)
      (other-variant r)
      (inhabited r)

  -- We can also use other assumptions:
  --
  -- * Univalence.
  -- * B should be a family of sets.

  module Second-variant-of-B
           (univ  : Univalence b)
           (B-set : ∀ a → Is-set (B a))
           where

    private
     abstract

      B̲-triple : (r : R) → ∃ λ (X : SET b) → B′ r ≡ proj₁ X
      B̲-triple r =
        to (coherently-constant-function≃∥inhabited∥⇒inhabited
              (Σ-closure 3
                 (∃-H-level-H-level-1+ ext univ 2)
                 (λ { (_ , X-set) → mono₁ 2 $
                      H-level-H-level-≡ʳ ext univ 1 X-set })))
           ( (λ b′ →   (B (_≃_.from lens (r , b′)) , B-set _)
                     , other-variant r b′)
           , (λ b′₁ b′₂ → Σ-≡,≡→≡
                (Σ-≡,≡→≡ (independent-of-B′ r b′₁ b′₂)
                         (Is-set-is-propositional _ _))
                (subst (λ X → B′ r ≡ proj₁ X)
                       (Σ-≡,≡→≡ (independent-of-B′ r b′₁ b′₂) _)
                       (other-variant r b′₁)                        ≡⟨ subst-∘ (B′ r ≡_) proj₁
                                                                               (Σ-≡,≡→≡ (independent-of-B′ r b′₁ b′₂) _) ⟩
                 subst (B′ r ≡_)
                       (cong proj₁ $
                          Σ-≡,≡→≡ (independent-of-B′ r b′₁ b′₂) _)
                       (other-variant r b′₁)                        ≡⟨⟩

                 trans (other-variant r b′₁)
                       (cong proj₁ $
                          Σ-≡,≡→≡ (independent-of-B′ r b′₁ b′₂) _)  ≡⟨ cong (trans (other-variant r b′₁)) $
                                                                         proj₁-Σ-≡,≡→≡ (independent-of-B′ r b′₁ b′₂) _ ⟩
                 trans (other-variant r b′₁)
                       (independent-of-B′ r b′₁ b′₂)                ≡⟨⟩

                 trans (other-variant r b′₁)
                       (trans (sym $ other-variant r b′₁)
                              (other-variant r b′₂))                ≡⟨ sym $ trans-assoc _ _ (other-variant r b′₂) ⟩

                 trans (trans (other-variant r b′₁)
                              (sym $ other-variant r b′₁))
                       (other-variant r b′₂)                        ≡⟨ cong (flip trans (other-variant r b′₂)) $
                                                                         trans-symʳ (other-variant r b′₁) ⟩
                 trans refl (other-variant r b′₂)                   ≡⟨ trans-reflˡ _ ⟩∎

                 other-variant r b′₂                                ∎))
           , (λ b′₁ b′₂ b′₃ →
                let lemma =
                      trans (independent-of-B′ r b′₁ b′₂)
                            (independent-of-B′ r b′₂ b′₃)              ≡⟨⟩

                      trans (trans (sym $ other-variant r b′₁)
                                   (other-variant r b′₂))
                            (trans (sym $ other-variant r b′₂)
                                   (other-variant r b′₃))              ≡⟨ sym $ trans-assoc (independent-of-B′ r b′₁ b′₂)
                                                                                (sym $ other-variant r b′₂)
                                                                                (other-variant r b′₃) ⟩
                      trans (trans (trans (sym $ other-variant r b′₁)
                                          (other-variant r b′₂))
                                   (sym $ other-variant r b′₂))
                            (other-variant r b′₃)                      ≡⟨ cong (flip trans (other-variant r b′₃)) $
                                                                            trans-[trans]-sym (sym $ other-variant r b′₁)
                                                                                (other-variant r b′₂) ⟩
                      trans (sym $ other-variant r b′₁)
                            (other-variant r b′₃)                      ≡⟨ refl ⟩∎

                      independent-of-B′ r b′₁ b′₃                      ∎
                in
                trans
                  (Σ-≡,≡→≡ (Σ-≡,≡→≡ (independent-of-B′ r b′₁ b′₂) _) _)
                  (Σ-≡,≡→≡ (Σ-≡,≡→≡ (independent-of-B′ r b′₂ b′₃) _) _)  ≡⟨ trans-Σ-≡,≡→≡ (Σ-≡,≡→≡ (independent-of-B′ r b′₁ b′₂) _)
                                                                                (Σ-≡,≡→≡ (independent-of-B′ r b′₂ b′₃) _)
                                                                                _ _ ⟩
                Σ-≡,≡→≡
                  (trans (Σ-≡,≡→≡ (independent-of-B′ r b′₁ b′₂) _)
                         (Σ-≡,≡→≡ (independent-of-B′ r b′₂ b′₃) _))
                  _                                                      ≡⟨ Σ-≡,≡→≡-cong (trans-Σ-≡,≡→≡ (independent-of-B′ r b′₁ b′₂)
                                                                                (independent-of-B′ r b′₂ b′₃)
                                                                                _ _)
                                                                                refl ⟩
                Σ-≡,≡→≡
                  (Σ-≡,≡→≡ (trans (independent-of-B′ r b′₁ b′₂)
                                  (independent-of-B′ r b′₂ b′₃))
                           _)
                  _                                                      ≡⟨ Σ-≡,≡→≡-cong
                                                                              (Σ-≡,≡→≡-cong lemma
                                                                                (mono₁ 0 (+⇒≡ Is-set-is-propositional) _ _))
                                                                              (H-level-H-level-≡ʳ ext univ 1 (B-set _) _ _) ⟩∎
                Σ-≡,≡→≡ (Σ-≡,≡→≡ (independent-of-B′ r b′₁ b′₃) _) _      ∎))
          (inhabited r)
        where
        Is-set-is-propositional :
          {B : Set b} → Is-proposition (Is-set B)
        Is-set-is-propositional =
          H-level-propositional ext 2

    B̲ : R → Set b
    B̲ r = proj₁ (proj₁ (B̲-triple r))

    B̲-set : ∀ r → Is-set (B̲ r)
    B̲-set r = proj₂ (proj₁ (B̲-triple r))

    B′≡B̲ : ∀ r → B′ r ≡ B̲ r
    B′≡B̲ r = proj₂ (B̲-triple r)

  -- Getter.

  get : (a : A) → B a
  get a = to variant≃ (L.get a)

  -- Setter.

  set : (a : A) → B a → A
  set a b = L.set a (from variant≃ b)

  -- Modifier.

  modify : (a : A) → (B a → B a) → A
  modify a f = set a (f (get a))

  -- Setting leaves the remainder unchanged.

  remainder-set : ∀ a b → remainder (set a b) ≡ remainder a
  remainder-set a b = L.remainder-set a (from variant≃ b)

  -- Hence the type of the gettable part stays unchanged after a set.

  codomain-set : ∀ {a b} → B (set a b) ≡ B a
  codomain-set {a} {b} =
    B (set a b)               ≡⟨ sym (variant _) ⟩
    B′ (remainder (set a b))  ≡⟨ cong B′ (remainder-set a b) ⟩
    B′ (remainder a)          ≡⟨ variant _ ⟩∎
    B a                       ∎

  -- A corresponding isomorphism.

  codomain-set-≃ : ∀ {a b} → B (set a b) ≃ B a
  codomain-set-≃ = ≡⇒≃ codomain-set

  -- Unfolding lemmas for codomain-set-≃.

  to-codomain-set-≃ :
    ∀ {a b} →
    to (codomain-set-≃ {a = a} {b = b}) ≡
    to variant≃ ⊚ to L.codomain-set-≃ ⊚ from variant≃
  to-codomain-set-≃ =
    ≡⇒→ (trans (sym (variant _)) (trans eq (variant _)))  ≡⟨ ≡⇒↝-trans equivalence {B≡C = trans eq (variant _)} ⟩
    ≡⇒→ (trans eq (variant _)) ⊚ ≡⇒→ (sym (variant _))    ≡⟨ cong (≡⇒→ (trans eq (variant _)) ⊚_)
                                                                  (≡⇒↝-sym equivalence {eq = variant _}) ⟩
    ≡⇒→ (trans eq (variant _)) ⊚ from variant≃            ≡⟨ cong (_⊚ from variant≃) (≡⇒↝-trans equivalence {B≡C = variant _}) ⟩∎
    to variant≃ ⊚ ≡⇒→ eq ⊚ from variant≃                  ∎
    where
    eq = cong B′ (remainder-set _ _)

  from-codomain-set-≃ :
    ∀ {a b} →
    from (codomain-set-≃ {a = a} {b = b}) ≡
    to variant≃ ⊚ from L.codomain-set-≃ ⊚ from variant≃
  from-codomain-set-≃ =
    ≡⇒← (trans (sym (variant _)) (trans eq (variant _)))        ≡⟨ sym $ ≡⇒↝-sym equivalence {eq = trans (sym (variant _))
                                                                                (trans eq (variant _))} ⟩
    ≡⇒→ (sym (trans (sym (variant _)) (trans eq (variant _))))  ≡⟨ cong ≡⇒→
                                                                        (Tactic.prove (Sym (Trans (Sym (Lift (variant _)))
                                                                                (Trans (Lift eq) (Lift (variant _)))))
                                                                                (Trans (Trans (Sym (Lift (variant _)))
                                                                                (Sym (Lift eq)))
                                                                                (Lift (variant _)))
                                                                                refl) ⟩
    ≡⇒→ (trans (trans (sym (variant _)) (sym eq)) (variant _))  ≡⟨ ≡⇒↝-trans equivalence {B≡C = variant _} ⟩
    to variant≃ ⊚ ≡⇒→ (trans (sym (variant _)) (sym eq))        ≡⟨ cong (to variant≃ ⊚_) (≡⇒↝-trans equivalence {B≡C = sym eq}) ⟩
    to variant≃ ⊚ ≡⇒→ (sym eq) ⊚ ≡⇒→ (sym (variant _))          ≡⟨ cong₂ (λ f g → to variant≃ ⊚ f ⊚ g)
                                                                         (≡⇒↝-sym equivalence {eq = eq})
                                                                         (≡⇒↝-sym equivalence {eq = variant _}) ⟩∎
    to variant≃ ⊚ ≡⇒← eq ⊚ from variant≃                        ∎
    where
    eq = cong B′ (remainder-set _ _)

  -- Some lens laws.

  set-get : ∀ a → set a (get a) ≡ a
  set-get a =
    L.set a (from variant≃ (to variant≃ (L.get a)))  ≡⟨ cong (L.set a) (left-inverse-of variant≃ _) ⟩
    L.set a (L.get a)                                ≡⟨ L.set-get a ⟩∎
    a                                                ∎

  get-set : ∀ a b → get (set a b) ≡ from codomain-set-≃ b
  get-set a b =
    to variant≃ (L.get (L.set a (from variant≃ b)))  ≡⟨ cong (to variant≃) $ L.get-set₂ _ _ ⟩
    to variant≃ (≡⇒← eq (from variant≃ b))           ≡⟨ cong (_$ b) (sym from-codomain-set-≃) ⟩∎
    from codomain-set-≃ b                            ∎
    where
    eq = cong B′ (remainder-set a b)

  set-set : ∀ a b₁ b₂ → set (set a b₁) b₂ ≡ set a (to codomain-set-≃ b₂)
  set-set a b₁ b₂ =
    L.set (L.set a (from variant≃ b₁)) (from variant≃ b₂)  ≡⟨ L.set-set₂ a (from variant≃ b₁) (from variant≃ b₂) ⟩
    L.set a (to L.codomain-set-≃ (from variant≃ b₂))       ≡⟨ cong (L.set a) lemma ⟩∎
    L.set a (from variant≃ (to codomain-set-≃ b₂))         ∎
    where
    lemma =
      to L.codomain-set-≃ (from variant≃ b₂)                    ≡⟨ sym $ left-inverse-of variant≃ _ ⟩

      from variant≃
        (to variant≃ (to L.codomain-set-≃ (from variant≃ b₂)))  ≡⟨ cong (from variant≃ ⊚ (_$ b₂)) $ sym to-codomain-set-≃ ⟩∎

      from variant≃ (to codomain-set-≃ b₂)                      ∎

-- For a non-dependent dependent lens, for which UIP holds for the
-- codomain's universe, codomain-set-≃ is equal to the identity.

codomain-set-≃≡id :
  {B : Set b} →
  Is-set (Set b) →
  (l : Lens A (λ _ → B)) →
  ∀ {a b} → Lens.codomain-set-≃ l {a = a} {b = b} ≡ Eq.id
codomain-set-≃≡id uip l =
  codomain-set-≃    ≡⟨⟩
  ≡⇒≃ codomain-set  ≡⟨ cong ≡⇒≃ (uip codomain-set refl) ⟩
  ≡⇒≃ refl          ≡⟨ refl ⟩∎
  Eq.id             ∎
  where
  open Lens l

------------------------------------------------------------------------
-- Some lens isomorphisms

-- Lens preserves level-preserving equivalences (assuming univalence).

Lens-cong :
  {A₁ A₂ : Set a} {B₁ : A₁ → Set b} {B₂ : A₂ → Set b} →
  Univalence b →
  (A₁≃A₂ : A₁ ≃ A₂) →
  (∀ a → B₁ a ≃ B₂ (_≃_.to A₁≃A₂ a)) →
  Lens A₁ B₁ ≃ Lens A₂ B₂
Lens-cong {A₁ = A₁} {A₂} {B₁} {B₂} univ A₁≃A₂ B₁≃B₂ =
  (∃ λ (R : Set _) →
   ∃ λ (B′ : R → Set _) →
   ∃ λ (lens : Lens₃ A₁ R B′) →
   ((r : R) → ∥ B′ r ∥)
     ×
   (∀ a → B′ (remainder lens a) ≡ B₁ a))              ↝⟨ (∃-cong λ R → ∃-cong λ B′ →
                                                          Σ-cong (Eq.≃-preserves ext A₁≃A₂ F.id) λ lens →
                                                          ∃-cong λ _ →
                                                          Π-cong ext A₁≃A₂ λ a →
                                                          ≡-preserves-≃ ext univ univ
                                                            (≡⇒≃ (
      B′ (proj₁ (to lens a))                                      ≡⟨ cong (λ a → B′ (proj₁ (to lens a))) $ sym $ left-inverse-of A₁≃A₂ _ ⟩∎
      B′ (proj₁ (to lens (from A₁≃A₂ (to A₁≃A₂ a))))              ∎))
                                                            (B₁≃B₂ a)) ⟩□
  (∃ λ (R : Set _) →
   ∃ λ (B′ : R → Set _) →
   ∃ λ (lens : Lens₃ A₂ R B′) →
   ((r : R) → ∥ B′ r ∥)
     ×
   (∀ a → B′ (remainder lens a) ≡ B₂ a))              □
  where
  open Lens₃
  open _≃_

-- If B x is a proposition for all x, then Lens A B is isomorphic to
-- (x : A) → B x (assuming univalence).

lens-to-proposition↔get :
  {A : Set a} {B : A → Set b} →
  Univalence (a ⊔ b) →
  Univalence b →
  (∀ x → Is-proposition (B x)) →
  Lens A B ↔ ((x : A) → B x)
lens-to-proposition↔get {b = b} {A = A} {B = B} univ₁ univ₂ B-prop =
  (∃ λ (R : Set _) →
   ∃ λ (B′ : R → Set _) →
   ∃ λ (lens : A ≃ Σ R B′) →
   ((r : R) → ∥ B′ r ∥)
     ×
   (∀ a → B′ (Lens₃.remainder lens a) ≡ B a))                ↝⟨ (∃-cong λ _ → ∃-cong λ B′ → ∃-cong λ l → ∃-cong λ _ →
                                                                 Π-cong ext l λ _ →
                                                                 ≡⇒↝ _ $ cong (λ x → B′ _ ≡ B x) $ sym $ _≃_.left-inverse-of l _) ⟩
  (∃ λ (R : Set _) →
   ∃ λ (B′ : R → Set _) →
   ∃ λ (lens : A ≃ Σ R B′) →
   ((r : R) → ∥ B′ r ∥)
     ×
   (∀ p → B′ (proj₁ p) ≡ B (_≃_.from lens p)))               ↝⟨ (∃-cong λ _ → ∃-cong λ _ → ∃-cong λ _ → ∃-cong λ _ → currying) ⟩

  (∃ λ (R : Set _) →
   ∃ λ (B′ : R → Set _) →
   ∃ λ (lens : A ≃ Σ R B′) →
   ((r : R) → ∥ B′ r ∥)
     ×
   ((r : R) (b : B′ r) → B′ r ≡ B (_≃_.from lens (r , b))))  ↝⟨ (∃-cong λ _ → ∃-cong λ _ → ∃-cong λ _ → inverse ΠΣ-comm) ⟩

  (∃ λ (R : Set _) →
   ∃ λ (B′ : R → Set _) →
   ∃ λ (lens : A ≃ Σ R B′) →
   ((r : R) →
      ∥ B′ r ∥
        ×
      ((b : B′ r) → B′ r ≡ B (_≃_.from lens (r , b)))))      ↔⟨ (∃-cong λ _ → ∃-cong λ B′ → ∃-cong λ lens → ∀-cong ext $
                                                                 lemma₁ B′ lens) ⟩
  (∃ λ (R : Set _) →
   ∃ λ (B′ : R → Set _) →
   ∃ λ (lens : A ≃ Σ R B′) →
   ((r : R) → ∃ λ (⊤≃B′ : ⊤ ≃ B′ r) →
                B (_≃_.from lens (r , _≃_.to ⊤≃B′ _))))      ↝⟨ (∃-cong λ _ → ∃-cong λ _ → ∃-cong λ _ → ΠΣ-comm) ⟩

  (∃ λ (R : Set _) →
   ∃ λ (B′ : R → Set _) →
   ∃ λ (lens : A ≃ Σ R B′) →
   ∃ λ (⊤≃B′ : ∀ r → ⊤ ≃ B′ r) →
   ∀ r → B (_≃_.from lens (r , _≃_.to (⊤≃B′ r) _)))          ↝⟨ (∃-cong λ _ → ∃-cong λ _ → ∃-comm) ⟩

  (∃ λ (R : Set _) →
   ∃ λ (B′ : R → Set _) →
   ∃ λ (⊤≃B′ : ∀ r → ⊤ ≃ B′ r) →
   ∃ λ (lens : A ≃ Σ R B′) →
   ∀ r → B (_≃_.from lens (r , _≃_.to (⊤≃B′ r) _)))          ↝⟨ (∃-cong λ _ → ∃-cong λ _ → ∃-cong λ ⊤≃B′ →
                                                                 Σ-cong (Eq.≃-preserves ext F.id
                                                                           (drop-⊤-right (λ r → inverse (⊤≃B′ r)))) λ _ →
                                                                 F.id) ⟩
  (∃ λ (R : Set _) →
   ∃ λ (B′ : R → Set _) →
   (∀ r → ⊤ ≃ B′ r)
     ×
   ∃ λ (lens : A ≃ R) →
   ∀ r → B (_≃_.from lens r))                                ↝⟨ (∃-cong λ _ → ∃-cong λ _ → ∃-comm) ⟩

  (∃ λ (R : Set _) →
   ∃ λ (B′ : R → Set _) →
   ∃ λ (lens : A ≃ R) →
   (∀ r → ⊤ ≃ B′ r)
     ×
   ∀ r → B (_≃_.from lens r))                                ↝⟨ (∃-cong λ _ → ∃-comm) ⟩

  (∃ λ (R : Set _) →
   ∃ λ (lens : A ≃ R) →
   ∃ λ (B′ : R → Set _) →
   (∀ r → ⊤ ≃ B′ r)
     ×
   ∀ r → B (_≃_.from lens r))                                ↝⟨ Σ-assoc ⟩

  (Σ (∃ λ (R : Set _) → A ≃ R) λ { (R , lens) →
   ∃ λ (B′ : R → Set _) →
   (∀ r → ⊤ ≃ B′ r)
     ×
   ∀ r → B (_≃_.from lens r) })                              ↝⟨ drop-⊤-left-Σ (other-singleton-with-≃-↔-⊤ {b = b} ext univ₁) ⟩

  (∃ λ (B′ : ↑ _ A → Set _) →
   (∀ a → ⊤ ≃ B′ a)
     ×
   (∀ a → B (lower a)))                                      ↝⟨ Σ-assoc ⟩

  (∃ λ (B′ : ↑ _ A → Set _) → ∀ a → ⊤ ≃ B′ a)
    ×
  (∀ a → B (lower a))                                        ↔⟨ ((∃-cong λ _ → ∀-cong ext λ _ →
                                                                  Eq.≃-preserves ext (inverse $ Eq.↔⇒≃ Bij.↑↔) F.id)
                                                                   ×-cong
                                                                 Π-cong ext (Eq.↔⇒≃ Bij.↑↔) (λ _ → F.id)) ⟩
  (∃ λ (B′ : ↑ _ A → Set _) → ∀ a → ↑ _ ⊤ ≃ B′ a)
    ×
  (∀ a → B a)                                                ↔⟨ (∃-cong λ B′ → ∀-cong ext λ _ →
                                                                 inverse $ ≡≃≃ univ₂)
                                                                  ×-cong
                                                                F.id ⟩
  (∃ λ (B′ : ↑ _ A → Set _) → ∀ a → ↑ _ ⊤ ≡ B′ a)
    ×
  (∀ a → B a)                                                ↔⟨ (∃-cong λ _ →
                                                                 Eq.extensionality-isomorphism ext)
                                                                  ×-cong
                                                                F.id ⟩
  (∃ λ (B′ : ↑ _ A → Set _) → const (↑ _ ⊤) ≡ B′)
    ×
  (∀ a → B a)                                                ↝⟨ drop-⊤-left-× (λ _ →
                                                                _⇔_.to contractible⇔↔⊤ (other-singleton-contractible _)) ⟩□
  (∀ a → B a)                                                □
  where
  lemma₂ : {R : Set _} (B′ : R → Set _) (r : R) → _
  lemma₂ B′ r =
    (∥ B′ r ∥ × Is-proposition (B′ r))  ↝⟨ ×-comm ⟩

    (Is-proposition (B′ r) × ∥ B′ r ∥)  ↝⟨ (∃-cong λ B′-prop → ∥∥↔ B′-prop) ⟩

    (Is-proposition (B′ r) × B′ r)      ↔⟨ _↠_.from (Eq.≃↠⇔ (Σ-closure 1 (H-level-propositional ext 1) λ B′-prop →
                                                             B′-prop)
                                                            (H-level-propositional ext 0))
                                                    (record { to   = uncurry propositional⇒inhabited⇒contractible
                                                            ; from = λ B′-contr → mono₁ 0 B′-contr , proj₁ B′-contr
                                                            }) ⟩
    Contractible (B′ r)                 ↝⟨ contractible↔≃⊤ ext ⟩

    B′ r ≃ ⊤                            ↝⟨ Eq.inverse-isomorphism ext ⟩□

    ⊤ ≃ B′ r                            □

  lemma₁ : {R : Set _} (B′ : R → Set _) (lens : A ≃ Σ R B′) (r : R) → _
  lemma₁ B′ lens r =
    ∥ B′ r ∥
      ×
    ((b′ : B′ r) → B′ r ≡ B (_≃_.from lens (r , b′)))  ↝⟨ (∃-cong λ _ → ∀-cong ext λ _ →
                                                           ≡≃≃ univ₂) ⟩
    ∥ B′ r ∥
      ×
    ((b′ : B′ r) → B′ r ≃ B (_≃_.from lens (r , b′)))  ↝⟨ (∃-cong λ _ → ∀-cong ext λ b′ →
                                                           _↠_.from (Eq.≃↠⇔ (Eq.right-closure ext 0 (B-prop _))
                                                                            (×-closure 1
                                                                                (H-level-propositional ext 1)
                                                                                (B-prop _)))
                                                                    (record
                                                                       { to   = λ eq →   H-level.respects-surjection
                                                                                (_≃_.surjection (inverse eq))
                                                                                1
                                                                                (B-prop _)
                                                                                , _≃_.to eq b′
                                                                       ; from = λ { (B′-prop , b) → _↠_.from (Eq.≃↠⇔ B′-prop (B-prop _))
                                                                                (record { to   = const b
                                                                                ; from = const b′
                                                                                }) }
                                                                       })) ⟩
    ∥ B′ r ∥
      ×
    ((b′ : B′ r) → Is-proposition (B′ r)
                     ×
                   B (_≃_.from lens (r , b′)))         ↔⟨ (∃-cong λ _ → ΠΣ-comm) ⟩

    ∥ B′ r ∥
      ×
    (B′ r → Is-proposition (B′ r))
      ×
    ((b′ : B′ r) → B (_≃_.from lens (r , b′)))         ↝⟨ (∃-cong λ _ →
                                                           _↠_.from (Eq.≃↠⇔ (Π-closure ext 1 λ _ →
                                                                             H-level-propositional ext 1)
                                                                            (H-level-propositional ext 1))
                                                                    (record { to   = λ B′-prop → [inhabited⇒+]⇒+ 0 B′-prop
                                                                            ; from = λ B′-prop _ → B′-prop
                                                                            })
                                                             ×-cong
                                                           F.id) ⟩
    ∥ B′ r ∥
      ×
    Is-proposition (B′ r)
      ×
    ((b′ : B′ r) → B (_≃_.from lens (r , b′)))         ↔⟨ ×-assoc ⟩

    (∥ B′ r ∥
      ×
     Is-proposition (B′ r))
      ×
    ((b′ : B′ r) → B (_≃_.from lens (r , b′)))         ↔⟨ lemma₂ B′ r ×-cong F.id ⟩

    (⊤ ≃ B′ r)
      ×
    ((b′ : B′ r) → B (_≃_.from lens (r , b′)))         ↝⟨ (∃-cong λ ⊤≃B′ → drop-⊤-left-Π ext (_≃_.bijection $ inverse ⊤≃B′)) ⟩□

    (∃ λ (⊤≃B′ : ⊤ ≃ B′ r) →
     B (_≃_.from lens (r , _≃_.to ⊤≃B′ _)))            □

-- If B x is contractible for all x, then Lens A B is isomorphic to ⊤
-- (assuming univalence).

lens-to-contractible↔⊤ :
  {A : Set a} {B : A → Set b} →
  Univalence (a ⊔ b) →
  Univalence b →
  (∀ x → Contractible (B x)) →
  Lens A B ↔ ⊤
lens-to-contractible↔⊤ {A = A} {B} univ₁ univ₂ cB =
  Lens A B         ↝⟨ lens-to-proposition↔get univ₁ univ₂ (mono₁ 0 ⊚ cB) ⟩
  ((x : A) → B x)  ↝⟨ (∀-cong ext λ _ →
                       _⇔_.to contractible⇔↔⊤ (cB _)) ⟩
  (A → ⊤)          ↝⟨ →-right-zero ⟩□
  ⊤                □

-- Lens A (const ⊥) is isomorphic to ¬ A (assuming univalence).

lens-to-⊥↔¬ :
  {A : Set a} →
  Univalence (a ⊔ b) →
  Univalence b →
  Lens A (const (⊥ {ℓ = b})) ↔ ¬ A
lens-to-⊥↔¬ {A = A} univ₁ univ₂ =
  Lens A (const ⊥)  ↝⟨ lens-to-proposition↔get univ₁ univ₂ (λ _ → ⊥-propositional) ⟩
  (A → ⊥)           ↝⟨ inverse $ ¬↔→⊥ ext ⟩□
  ¬ A               □

------------------------------------------------------------------------
-- Results relating different kinds of lenses

-- If we assume that equality with the codomain type is propositional,
-- then non-dependent dependent lenses are isomorphic to non-dependent
-- lenses.
--
-- TODO: Can this be proved without assuming that equality with the
-- codomain type is propositional? If not, can the definition of Lens
-- be changed so that it can be proved?

non-dependent-lenses-isomorphic :
  {B : Set b} →
  (∀ {B′} → Is-proposition (B′ ≡ B)) →
  ∃ λ (iso : Lens A (const B) ↔ ND.Lens A B) →
    ∀ {l a} → Lens.get l a ≡ ND.Lens.get (_↔_.to iso l) a
non-dependent-lenses-isomorphic {A = A} {B = B} ≡B-prop =
  (Lens A (const B)  ↝⟨ inverse ND.Lens-as-Σ F.∘ ∃-cong lemma ⟩□
   ND.Lens A B       □)
  , λ {l a} →
    let p = variant l a
        q = Trunc.rec
              ≡B-prop
              (λ b → subst (λ { (r , _) → B′ l r ≡ B })
                           (_≃_.right-inverse-of (lens l)
                              (remainder l a , b))
                           (variant l (Lens₃.set (lens l) a b)))
              (inhabited l (remainder l a))
    in
    ≡⇒→ p (proj₂ (_≃_.to (lens l) a))  ≡⟨ cong (λ eq → ≡⇒→ eq (proj₂ (_≃_.to (lens l) a)))
                                               (≡B-prop p q) ⟩∎
    ≡⇒→ q (proj₂ (_≃_.to (lens l) a))  ∎
  where
  open Lens

  lemma = λ R →
    (∃ λ (B′ : R → Set _) →
     ∃ λ (lens : A ≃ Σ R B′) →
     ((r : R) → ∥ B′ r ∥)
       ×
     (∀ a → B′ (Lens₃.remainder lens a) ≡ B))     ↝⟨ (∃-cong λ _ → ∃-cong λ l → ∃-cong λ _ →
                                                      Π-cong ext l (λ _ → F.id)) ⟩
    (∃ λ (B′ : R → Set _) →
     (A ≃ Σ R B′)
       ×
     ((r : R) → ∥ B′ r ∥)
       ×
     (∀ p → B′ (proj₁ p) ≡ B))                    ↝⟨ (∃-cong λ _ → ∃-cong λ _ → ∃-cong λ _ → currying) ⟩

    (∃ λ (B′ : R → Set _) →
     (A ≃ Σ R B′)
       ×
     ((r : R) → ∥ B′ r ∥)
       ×
     ((r : R) → B′ r → B′ r ≡ B))                 ↔⟨ (∃-cong λ _ → ∃-cong λ _ → ∃-cong λ inh →
                                                      ∀-cong ext λ r →
                                                      _↠_.from (Eq.≃↠⇔ (Π-closure ext 1 λ _ →
                                                                        ≡B-prop)
                                                                       ≡B-prop)
                                                        (record { from = λ B′r≡B     → const B′r≡B
                                                                ; to   = λ B′r→B′r≡B → Trunc.rec ≡B-prop B′r→B′r≡B (inh r)
                                                                })) ⟩
    (∃ λ (B′ : R → Set _) →
     (A ≃ Σ R B′)
       ×
     ((r : R) → ∥ B′ r ∥)
       ×
     ((r : R) → B′ r ≡ B))                        ↝⟨ (∃-cong λ _ → ∃-cong λ _ → ×-comm) ⟩

    (∃ λ (B′ : R → Set _) →
     (A ≃ Σ R B′)
       ×
     ((r : R) → B′ r ≡ B)
       ×
     ((r : R) → ∥ B′ r ∥))                        ↝⟨ (∃-cong λ _ → ∃-comm) ⟩

    (∃ λ (B′ : R → Set _) →
     ((r : R) → B′ r ≡ B)
       ×
     (A ≃ Σ R B′)
       ×
     ((r : R) → ∥ B′ r ∥))                        ↔⟨ (∃-cong λ _ →
                                                      Σ-cong (Eq.extensionality-isomorphism ext) λ B′≡B →
                                                      Eq.≃-preserves ext F.id (∃-cong λ _ → ≡⇒↝ _ (B′≡B _))
                                                        ×-cong
                                                      ∀-cong ext (λ _ → Eq.↔⇒≃ $ ∥∥-cong (≡⇒↝ _ (B′≡B _)))) ⟩
    (∃ λ (B′ : R → Set _) →
     B′ ≡ const B
       ×
     (A ≃ (R × B))
       ×
     (R → ∥ B ∥))                                 ↝⟨ Σ-assoc ⟩

    ((∃ λ (B′ : R → Set _) → B′ ≡ const B)
       ×
     (A ≃ (R × B))
       ×
     (R → ∥ B ∥))                                 ↝⟨ drop-⊤-left-× (λ _ →
                                                     _⇔_.to contractible⇔↔⊤ (singleton-contractible _)) ⟩□
    ((A ≃ (R × B))
       ×
     (R → ∥ B ∥))                                 □

-- The type of non-dependent dependent lenses from A to B is
-- isomorphic to the type of non-dependent lenses from A to B if UIP
-- holds for B.

non-dependent-lenses-isomorphic-UIP :
  {B : Set b} →
  Is-set (Set b) →
  ∃ λ (iso : Lens A (const B) ↔ ND.Lens A B) →
    ∀ {l a} → Lens.get l a ≡ ND.Lens.get (_↔_.to iso l) a
non-dependent-lenses-isomorphic-UIP uip =
  non-dependent-lenses-isomorphic uip

-- Lens and Lens′ are pointwise isomorphic.

Lens↔Lens′ : {A : Set a} {B : A → Set b} →
             Lens A B ↔ Lens′ A B
Lens↔Lens′ {a = a} {b = b} {A = A} {B = B} =
  (∃ λ (R : Set (a ⊔ b)) →
   ∃ λ (B′ : R → Set b) →
   ∃ λ (lens : Lens₃ A R B′) →
   ((r : R) → ∥ B′ r ∥)
     ×
   (∀ a → B′ (Lens₃.remainder lens a) ≡ B a))                           ↝⟨ (∃-cong λ _ → ∃-cong λ _ →
                                                                            Σ-cong (Eq.≃-as-Σ) λ _ → F.id) ⟩
  (∃ λ (R : Set (a ⊔ b)) →
   ∃ λ (B′ : R → Set b) →
   ∃ λ (lens : ∃ λ (rg : A → Σ R B′) → Eq.Is-equivalence rg) →
   ((r : R) → ∥ B′ r ∥)
     ×
   (∀ a → B′ (proj₁ (proj₁ lens a)) ≡ B a))                             ↝⟨ (∃-cong λ _ → ∃-cong λ _ →
                                                                            inverse Σ-assoc) ⟩
  (∃ λ (R : Set (a ⊔ b)) →
   ∃ λ (B′ : R → Set b) →
   ∃ λ (rg : A → Σ R B′) →
   Eq.Is-equivalence rg
     ×
   ((r : R) → ∥ B′ r ∥)
     ×
   (∀ a → B′ (proj₁ (rg a)) ≡ B a))                                     ↝⟨ (∃-cong λ _ → ∃-cong λ _ →
                                                                            Σ-cong ΠΣ-comm λ _ → F.id) ⟩
  (∃ λ (R : Set (a ⊔ b)) →
   ∃ λ (B′ : R → Set b) →
   ∃ λ (rg : ∃ λ (remainder : A → R) →
             (a : A) → B′ (remainder a)) →
   Eq.Is-equivalence {B = ∃ B′} (λ a → proj₁ rg a , proj₂ rg a)
     ×
   ((r : R) → ∥ B′ r ∥)
     ×
   (∀ a → B′ (proj₁ rg a) ≡ B a))                                       ↝⟨ (∃-cong λ _ → ∃-cong λ _ →
                                                                            inverse Σ-assoc) ⟩
  (∃ λ (R : Set (a ⊔ b)) →
   ∃ λ (B′ : R → Set b) →
   ∃ λ (remainder : A → R) →
   ∃ λ (get′ : (a : A) → B′ (remainder a)) →
   Eq.Is-equivalence {B = ∃ B′} (λ a → remainder a , get′ a)
     ×
   ((r : R) → ∥ B′ r ∥)
     ×
   (∀ a → B′ (remainder a) ≡ B a))                                      ↝⟨ (∃-cong λ R → ∃-cong λ B′ → ∃-cong λ rem → ∃-cong λ get′ → ∃-cong λ eq →
                                                                            ∀-cong ext (λ r → ∥∥-cong (lemma R B′ rem get′ eq r))
                                                                              ×-cong
                                                                            F.id) ⟩
  (∃ λ (R : Set (a ⊔ b)) →
   ∃ λ (B′ : R → Set b) →
   ∃ λ (remainder : A → R) →
   ∃ λ (get′ : (a : A) → B′ (remainder a)) →
   Eq.Is-equivalence {B = ∃ B′} (λ a → remainder a , get′ a)
     ×
   Surjective remainder
     ×
   (∀ a → B′ (remainder a) ≡ B a))                                      ↝⟨ (∃-cong λ _ → ∃-cong λ _ → ∃-cong λ _ → ∃-cong λ _ → ∃-cong λ _ →
                                                                            ×-comm) ⟩
  (∃ λ (R : Set (a ⊔ b)) →
   ∃ λ (B′ : R → Set b) →
   ∃ λ (remainder : A → R) →
   ∃ λ (get′ : (a : A) → B′ (remainder a)) →
   Eq.Is-equivalence {B = ∃ B′} (λ a → remainder a , get′ a)
     ×
   (∀ a → B′ (remainder a) ≡ B a)
     ×
   Surjective remainder)                                                ↝⟨ (∃-cong λ _ → ∃-cong λ _ → ∃-cong λ _ → ∃-cong λ _ →
                                                                            ∃-comm) ⟩
  (∃ λ (R : Set (a ⊔ b)) →
   ∃ λ (B′ : R → Set b) →
   ∃ λ (remainder : A → R) →
   ∃ λ (get′ : (a : A) → B′ (remainder a)) →
   (∀ a → B′ (remainder a) ≡ B a)
     ×
   Eq.Is-equivalence {B = ∃ B′} (λ a → remainder a , get′ a)
     ×
   Surjective remainder)                                                ↝⟨ (∃-cong λ _ → ∃-cong λ _ → ∃-cong λ _ →
                                                                            ∃-comm) ⟩
  (∃ λ (R : Set (a ⊔ b)) →
   ∃ λ (B′ : R → Set b) →
   ∃ λ (remainder : A → R) →
   (∀ a → B′ (remainder a) ≡ B a)
     ×
   ∃ λ (get′ : (a : A) → B′ (remainder a)) →
   Eq.Is-equivalence {B = ∃ B′} (λ a → remainder a , get′ a)
     ×
   Surjective remainder)                                                ↝⟨ (∃-cong λ _ → ∃-cong λ _ → ∃-cong λ _ → ×-cong₁ λ _ →
                                                                            ∀-cong ext λ _ →
                                                                            Groupoid.⁻¹-bijection (EG.groupoid _)) ⟩
  (∃ λ (R : Set (a ⊔ b)) →
   ∃ λ (B′ : R → Set b) →
   ∃ λ (remainder : A → R) →
   (∀ a → B a ≡ B′ (remainder a))
     ×
   ∃ λ (get′ : (a : A) → B′ (remainder a)) →
   Eq.Is-equivalence {B = ∃ B′} (λ a → remainder a , get′ a)
     ×
   Surjective remainder)                                                ↝⟨ (∃-cong λ _ → ∃-cong λ _ → ∃-cong λ _ → ×-cong₁ λ _ →
                                                                            inverse Bij.implicit-Π↔Π) ⟩
  (∃ λ (R : Set (a ⊔ b)) →
   ∃ λ (B′ : R → Set b) →
   ∃ λ (remainder : A → R) →
   (∀ {a} → B a ≡ B′ (remainder a))
     ×
   ∃ λ (get′ : (a : A) → B′ (remainder a)) →
   Eq.Is-equivalence {B = ∃ B′} (λ a → remainder a , get′ a)
     ×
   Surjective remainder)                                                ↝⟨ (∃-cong λ _ → ∃-cong λ _ → ∃-cong λ _ → ∃-cong λ variant → inverse $
                                                                            Σ-cong (∀-cong ext λ _ →
                                                                                Eq.subst-as-equivalence P.id (variant {_})) λ _ →
                                                                            F.id) ⟩
  (∃ λ (R : Set (a ⊔ b)) →
   ∃ λ (B′ : R → Set b) →
   ∃ λ (remainder : A → R) →
   ∃ λ (variant : ∀ {a} → B a ≡ B′ (remainder a)) →
   ∃ λ (get : (a : A) → B a) →
   Eq.Is-equivalence {B = ∃ B′}
                     (λ a → remainder a , subst P.id variant (get a))
     ×
   Surjective remainder)                                                ↝⟨ (∃-cong λ _ → ∃-cong λ _ → ∃-cong λ _ →
                                                                            ∃-comm) ⟩
  (∃ λ (R : Set (a ⊔ b)) →
   ∃ λ (B′ : R → Set b) →
   ∃ λ (remainder : A → R) →
   ∃ λ (get : (a : A) → B a) →
   ∃ λ (variant : ∀ {a} → B a ≡ B′ (remainder a)) →
   Eq.Is-equivalence {B = ∃ B′}
                     (λ a → remainder a , subst P.id variant (get a))
     ×
   Surjective remainder)                                                ↝⟨ (∃-cong λ _ → ∃-cong λ _ →
                                                                            ∃-comm) ⟩
  (∃ λ (R : Set (a ⊔ b)) →
   ∃ λ (B′ : R → Set b) →
   ∃ λ (get : (a : A) → B a) →
   ∃ λ (remainder : A → R) →
   ∃ λ (variant : ∀ {a} → B a ≡ B′ (remainder a)) →
   Eq.Is-equivalence {B = ∃ B′}
                     (λ a → remainder a , subst P.id variant (get a))
     ×
   Surjective remainder)                                                ↝⟨ (∃-cong λ _ →
                                                                            ∃-comm) ⟩
  (∃ λ (R : Set (a ⊔ b)) →
   ∃ λ (get : (a : A) → B a) →
   ∃ λ (B′ : R → Set b) →
   ∃ λ (remainder : A → R) →
   ∃ λ (variant : ∀ {a} → B a ≡ B′ (remainder a)) →
   Eq.Is-equivalence {B = ∃ B′}
                     (λ a → remainder a , subst P.id variant (get a))
     ×
   Surjective remainder)                                                ↝⟨ ∃-comm ⟩

  (∃ λ (get : (a : A) → B a) →
   ∃ λ (R : Set (a ⊔ b)) →
   ∃ λ (B′ : R → Set b) →
   ∃ λ (remainder : A → R) →
   ∃ λ (variant : ∀ {a} → B a ≡ B′ (remainder a)) →
   Eq.Is-equivalence {B = ∃ B′}
                     (λ a → remainder a , subst P.id variant (get a))
     ×
   Surjective remainder)                                                ↝⟨ (∃-cong λ _ → ∃-cong λ _ → ∃-cong λ _ → ∃-cong λ _ → ∃-cong λ _ →
                                                                            ×-comm) ⟩
  (∃ λ (get : (a : A) → B a) →
   ∃ λ (R : Set (a ⊔ b)) →
   ∃ λ (B′ : R → Set b) →
   ∃ λ (remainder : A → R) →
   ∃ λ (variant : ∀ {a} → B a ≡ B′ (remainder a)) →
   Surjective remainder
     ×
   Eq.Is-equivalence {B = ∃ B′}
                     (λ a → remainder a , subst P.id variant (get a)))  ↝⟨ (∃-cong λ _ → ∃-cong λ _ → ∃-cong λ _ → ∃-cong λ _ →
                                                                            ∃-comm) ⟩
  (∃ λ (get : (a : A) → B a) →
   ∃ λ (R : Set (a ⊔ b)) →
   ∃ λ (B′ : R → Set b) →
   ∃ λ (remainder : A → R) →
   Surjective remainder
     ×
   ∃ λ (variant : ∀ {a} → B a ≡ B′ (remainder a)) →
   Eq.Is-equivalence {B = ∃ B′}
                     (λ a → remainder a , subst P.id variant (get a)))  ↝⟨ (∃-cong λ _ → ∃-cong λ _ →
                                                                            ∃-comm) ⟩
  (∃ λ (get : (a : A) → B a) →
   ∃ λ (R : Set (a ⊔ b)) →
   ∃ λ (remainder : A → R) →
   ∃ λ (B′ : R → Set b) →
   Surjective remainder
     ×
   ∃ λ (variant : ∀ {a} → B a ≡ B′ (remainder a)) →
   Eq.Is-equivalence {B = ∃ B′}
                     (λ a → remainder a , subst P.id variant (get a)))  ↝⟨ (∃-cong λ _ → ∃-cong λ _ → ∃-cong λ _ →
                                                                            ∃-comm) ⟩□
  (∃ λ (get : (a : A) → B a) →
   ∃ λ (R : Set (a ⊔ b)) →
   ∃ λ (remainder : A → R) →
   Surjective remainder
     ×
   ∃ λ (B′ : R → Set b) →
   ∃ λ (variant : ∀ {a} → B a ≡ B′ (remainder a)) →
   Eq.Is-equivalence {B = ∃ B′}
                     (λ a → remainder a , subst P.id variant (get a)))  □
  where
  lemma : ∀ _ _ _ _ _ _ → _
  lemma = λ _ B′ remainder _ eq r →
    B′ r                            ↝⟨ (inverse $ drop-⊤-right λ _ →
                                        _⇔_.to contractible⇔↔⊤ $
                                        singleton-contractible _) ⟩
    B′ r × Singleton r              ↝⟨ ∃-comm ⟩
    (∃ λ r′ → B′ r × r′ ≡ r)        ↝⟨ ∃-cong (λ _ → ×-cong₁ λ r′≡r → ≡⇒↝ _ (cong B′ (sym r′≡r))) ⟩
    (∃ λ r′ → B′ r′ × r′ ≡ r)       ↝⟨ Σ-assoc ⟩
    (∃ λ (p : ∃ B′) → proj₁ p ≡ r)  ↝⟨ inverse $ Σ-cong Eq.⟨ _ , eq ⟩ (λ _ → F.id) ⟩□
    (∃ λ a → remainder a ≡ r)       □

------------------------------------------------------------------------
-- Lens combinators

-- Conversion from Lens₃ to Lens.

Lens₃-to-Lens :
  {A : Set a} {R : Set (a ⊔ b)} {B : R → Set b} →
  (l : Lens₃ A R B) →
  Lens A (B ⊚ Lens₃.remainder l)
Lens₃-to-Lens {A = A} {R} {B} l =
  _ ,
  _ ,
  (A                                              ↝⟨ l ⟩
   Σ R B                                          ↔⟨ ∃-cong (λ _ → inverse ∥∥×↔) ⟩
   Σ R (λ r → ∥ B r ∥ × B r)                      ↔⟨ Σ-assoc ⟩□
   Σ (Σ R (λ r → ∥ B r ∥)) (λ { (r , _) → B r })  □) ,
  proj₂ ,
  λ _ → refl

-- A variant of Lens₃-to-Lens.

Lens₃-to-Lens′ :
  {A : Set (a ⊔ r)} {R : Set r} {B : R → Set b} →
  (l : Lens₃ A R B) →
  Lens A (B ⊚ Lens₃.remainder l)
Lens₃-to-Lens′ {a = a} {b = b} {A = A} {R = R} {B = B} l =
  Lens₃-to-Lens
    (A                                       ↝⟨ l ⟩
     Σ R B                                   ↝⟨ Σ-cong (inverse Bij.↑↔) (λ _ → F.id) ⟩□
     Σ (↑ (a ⊔ b) R) (λ { (lift r) → B r })  □)

-- Identity lens.

id : Lens A (λ _ → A)
id {A = A} = Lens₃-to-Lens′
  (A      ↔⟨ inverse ×-left-identity ⟩□
   ⊤ × A  □)

-- Composition of lenses.
--
-- Note that this function combines a family of Lenses and a Lens₃.

infixr 9 _∘₃_

_∘₃_ :
  {A : Set (a ⊔ b ⊔ c)} {R : Set (a ⊔ b ⊔ c)}
  {B : R → Set (b ⊔ c)} {C : {r : R} → B r → Set c} →
  (∀ {r} → Lens (B r) C) → (l₂ : Lens₃ A R B) →
  Lens A (C ⊚ Lens₃.get l₂)
_∘₃_ {R = R} l₁ l₂ =
  (∃ λ (r : R) → Lens.R (l₁ {r = r})) ,
  (λ { (r₁ , r₂) → Lens.B′ (l₁ {r = r₁}) r₂ }) ,
  Lens.lens l₁ ₃∘₃ l₂ ,
  Lens.inhabited l₁ ⊚ proj₂ ,
  Lens.variant l₁ ⊚ Lens₃.get l₂

-- /Forward/ composition of lenses.
--
-- This function combines /Lens/es, but has a type which is arguably
-- more complicated. The type is also somewhat restricted: C is only
-- indexed by As, not Bs.

infixr 9 _⨾_

_⨾_ : {A : Set (a ⊔ b ⊔ c)} {B : A → Set (b ⊔ c)} {C : A → Set c} →
      (l₁ : Lens A B) →
      let open Lens l₁; open _≃_ lens in
      (∀ {r} → Lens (B′ r) (λ b′ → C (from (r , b′)))) →
      Lens A C
_⨾_ {C = C} l₁ l₂ =
  (∃ λ (r₁ : R l₁) → Lens.R (l₂ {r = r₁})) ,
  (λ { (r₁ , r₂) → B′ (l₂ {r = r₁}) r₂ }) ,
  lens l₂ ₃∘₃ lens l₁ ,
  (λ { (_ , r₂) → inhabited l₂ r₂ }) ,
  λ a →
    B′ l₂ (remainder l₂ (Lens₃.get (lens l₁) a))  ≡⟨ variant l₂ _ ⟩
    C (from (lens l₁) (to (lens l₁) a))           ≡⟨ cong C (left-inverse-of (lens l₁) a) ⟩∎
    C a                                           ∎
  where
  open _≃_
  open Lens

-- Can the composition operation be generalised?

-- The argument below does not depend on the details of the
-- implementation of Lens.

module No-fully-general-composition-operator
  (Lens′ : (A : Set) → (A → Set) → Set₁)
  (get′  : ∀ {A B} → Lens′ A B → (a : A) → B a)
  where

  -- The following type signature—and partial specification—might seem
  -- like a reasonable goal (if we restrict attention to Set₀).

  Type-of-composition : Set₁
  Type-of-composition =
    {A : Set} {B : A → Set} {C : (a : A) → B a → Set}
    (l₁ : Lens′ A B)
    (l₂ : ∀ a → Lens′ (B a) (C a)) →
    ∃ λ (l₃ : Lens′ A (λ a → C a (get′ l₁ a))) →
      ∀ a → get′ l₃ a ≡ get′ (l₂ a) (get′ l₁ a)

  -- However, this specification is unsatisfiable in the non-dependent
  -- case (for ND.Lens).

  no-corresponding-non-dependent-composition-operator :
    let open ND.Lens in
    ¬ ({A B C : Set}
       (l₁ : ND.Lens A B)
       (l₂ : A → ND.Lens B C) →
       ∃ λ (l₃ : ND.Lens A C) →
         ∀ a → get l₃ a ≡ get (l₂ a) (get l₁ a))
  no-corresponding-non-dependent-composition-operator comp =
    contradiction
    where
    open ND.Lens
    open _≃_

    idL : ND.Lens Bool Bool
    idL = ND.Lens-combinators.id ⊠

    swapL : ND.Lens Bool Bool
    swapL = ND.isomorphism-to-lens
      (Bool      ↔⟨ swap ⟩
       Bool      ↔⟨ inverse ×-left-identity ⟩□
       ⊤ × Bool  □)

    l₁ : ND.Lens Bool Bool
    l₁ = idL

    l₂ : Bool → ND.Lens Bool Bool
    l₂ = if_then idL else swapL

    l₃ : ND.Lens Bool Bool
    l₃ = proj₁ (comp l₁ l₂)

    get-constant : ∀ b → get l₃ b ≡ true
    get-constant true  = proj₂ (comp l₁ l₂) _
    get-constant false = proj₂ (comp l₁ l₂) _

    contradiction : ⊥
    contradiction = Bool.true≢false (
      true                        ≡⟨ sym $ get-constant (set l₃ true false) ⟩
      get l₃ (set l₃ true false)  ≡⟨ get-set l₃ true false ⟩∎
      false                       ∎)

  -- Thus it is also unsatisfiable if non-dependent dependent lenses
  -- are isomorphic (in a certain sense) to the corresponding
  -- non-dependent lenses.

  no-composition-operator :
    ({A B : Set} →
     ∃ λ (iso : Lens′ A (λ _ → B) ↔ ND.Lens A B) →
       ∀ {l a} → get′ l a ≡ ND.Lens.get (_↔_.to iso l) a) →
    ¬ Type-of-composition
  no-composition-operator Lens↔Lens comp =
    no-corresponding-non-dependent-composition-operator
      (λ l₁ l₂ →
         let l₃ , get-l₃ = comp (from l₁) (λ a → from (l₂ a))
         in
         to l₃ , λ a →
           get (to l₃) a                                  ≡⟨ sym $ proj₂ Lens↔Lens ⟩
           get′ l₃ a                                      ≡⟨ get-l₃ a ⟩
           get′ (from (l₂ a)) (get′ (from l₁) a)          ≡⟨ cong (get′ (from (l₂ a))) (proj₂ Lens↔Lens) ⟩
           get′ (from (l₂ a)) (get (to (from l₁)) a)      ≡⟨ proj₂ Lens↔Lens ⟩
           get (to (from (l₂ a))) (get (to (from l₁)) a)  ≡⟨ cong₂ (λ l₁ l₂ → get l₁ (get l₂ a))
                                                                   (right-inverse-of _)
                                                                   (right-inverse-of _) ⟩∎
           get (l₂ a) (get l₁ a)                          ∎)
    where
    open ND.Lens
    open module Lens↔Lens {A B : Set} =
      _↔_ (proj₁ (Lens↔Lens {A = A} {B = B}))

-- In the presence of UIP for Set it is impossible to define a fully
-- general composition operator.

no-fully-general-composition-operator-UIP :
  let open Lens in
  Is-set Set →
  ¬ ({A : Set} {B : A → Set} {C : (a : A) → B a → Set}
     (l₁ : Lens A B)
     (l₂ : ∀ a → Lens (B a) (C a)) →
     ∃ λ (l₃ : Lens A (λ a → C a (Lens.get l₁ a))) →
       ∀ a → get l₃ a ≡ get (l₂ a) (get l₁ a))
no-fully-general-composition-operator-UIP uip =
  No-fully-general-composition-operator.no-composition-operator
    Lens Lens.get
    (non-dependent-lenses-isomorphic-UIP uip)

------------------------------------------------------------------------
-- An observation

-- Lens₃ lenses cannot (easily) be used to replace ordinary
-- projections: one cannot, in general, define lenses with the type of
-- the first projection from a Σ-type. For Lens lenses the situation
-- is unclear.

module Observation where

  -- A Σ-type which is isomorphic to the unit type.

  Unit = Σ Bool λ b → b ≡ true

  -- All its inhabitants are equal.

  equal : (u₁ u₂ : Unit) → u₁ ≡ u₂
  equal (.true , refl) (.true , refl) = refl

  -- Its only inhabitant.

  u : Unit
  u = (true , refl)

  -- The first projection Lens₃ cannot be defined for Unit.

  not-proj₁₃ : ∀ {r} {R : Set r} → ¬ Lens₃ Unit R (λ _ → Bool)
  not-proj₁₃ l = Bool.true≢false (
    true                                                    ≡⟨ sym $ subst-const (sym $ remainder-set u true) ⟩
    subst (λ _ → Bool) (sym $ remainder-set u true) true    ≡⟨ sym $ get-set₁ u true ⟩
    get (set u true)                                        ≡⟨ cong get (equal (set u true) (set u false)) ⟩
    get (set u false)                                       ≡⟨ get-set₁ u false ⟩
    subst (λ _ → Bool) (sym $ remainder-set u false) false  ≡⟨ subst-const (sym $ remainder-set u false) ⟩∎
    false                                                   ∎)
    where
    open Lens₃ l

  -- The first projection Lens cannot be defined for Unit /if/ we
  -- assume that UIP holds for Set.

  not-proj₁ : Is-set Set → ¬ Lens Unit (λ _ → Bool)
  not-proj₁ uip l = Bool.true≢false (
    true                       ≡⟨ sym $ cong (λ eq → from eq true) $ codomain-set-≃≡id uip l ⟩
    from codomain-set-≃ true   ≡⟨ sym $ get-set u true ⟩
    get (set u true)           ≡⟨ cong get (equal (set u true) (set u false)) ⟩
    get (set u false)          ≡⟨ get-set u false ⟩
    from codomain-set-≃ false  ≡⟨ cong (λ eq → from eq false) $ codomain-set-≃≡id uip l ⟩∎
    false                      ∎)
    where
    open _≃_
    open Lens l

  -- TODO: What is the situation in the presence of univalence?


module New.Unused where

open import New.Changes

module _ {ℓ₁} {ℓ₂}
  {A : Set ℓ₁} {B : Set ℓ₂} {{CA : ChAlg A}} {{CB : ChAlg B}} where
  open ≡-Reasoning
  open import Postulate.Extensionality
  module _ (f : A → B) where
    nil-is-derivative : IsDerivative f (nil f)
    nil-is-derivative a da v =
      begin
        f (a ⊕ da)
      ≡⟨ sym (cong (λ □ → □ (_⊕_ a da)) (update-nil f)) ⟩
        (f ⊕ nil f) (a ⊕ da)
      ≡⟨ proj₂ (nil-valid f a da v) ⟩
        f a ⊕ (nil f a da)
      ∎

    derivative-is-nil : ∀ df → IsDerivative f df → f ⊕ df ≡ f
    derivative-is-nil df f-df =
      ext (λ v →
        begin
          f v ⊕ df v (nil v)
        ≡⟨ sym (f-df v (nil v) (nil-valid v)) ⟩
          f (v ⊕ nil v)
        ≡⟨ cong f (update-nil v) ⟩
          f v
        ∎)

    derivative-is-valid : ∀ df
      (IsDerivative-f-df : IsDerivative f df) →
      WellDefinedFunChange f df
    derivative-is-valid df IsDerivative-f-df a da ada
      rewrite sym (IsDerivative-f-df (a ⊕ da) (nil (a ⊕ da)) (nil-valid (a ⊕ da)))
      | update-nil (a ⊕ da)
      | IsDerivative-f-df a da ada
      = refl

    equal-future-expand-derivative : ∀ df → IsDerivative f df →
      ∀ v1 dv1 → valid v1 dv1 →
      ∀ v2 →
      v2 ≡ v1 ⊕ dv1 →
      f v2 ≡ f v1 ⊕ df v1 dv1
    equal-future-expand-derivative df is-derivative-f-df v1 dv1 v1dv1 v2 eq-fut =
      begin
        f v2
      ≡⟨ cong f eq-fut ⟩
        f (v1 ⊕ dv1)
      ≡⟨ is-derivative-f-df v1 dv1 v1dv1 ⟩
        f v1 ⊕ df v1 dv1
      ∎
    -- -- equal-future-expand-derivative df is-derivative-f-df v1 dv1 v1dv1 v2 dv2 v2dv2 eq-fut =
    -- --   begin
    -- --     (f ⊕ df) (v1 ⊕ dv1)
    -- --   ≡⟨ cong (f ⊕ df) eq-fut ⟩
    -- --     (f ⊕ df) (v2 ⊕ dv2)
    -- --   ≡⟨ well-defined-f-df v2 dv2 v2dv2 ⟩
    -- --     f v2 ⊕ df v2 dv2
    -- --   ∎
    --   -- equal-future-expand-base df (derivative-is-valid df is-derivative-f-df)

    equal-future-base : ∀ df → WellDefinedFunChange f df →
      ∀ v1 dv1 → valid v1 dv1 →
      ∀ v2 dv2 → valid v2 dv2 →
      v1 ⊕ dv1 ≡ v2 ⊕ dv2 →
      f v1 ⊕ df v1 dv1 ≡ f v2 ⊕ df v2 dv2
    equal-future-base df well-defined-f-df v1 dv1 v1dv1 v2 dv2 v2dv2 eq-fut =
      begin
        f v1 ⊕ df v1 dv1
      ≡⟨ sym (well-defined-f-df v1 dv1 v1dv1) ⟩
        (f ⊕ df) (v1 ⊕ dv1)
      ≡⟨ cong (f ⊕ df) eq-fut ⟩
        (f ⊕ df) (v2 ⊕ dv2)
      ≡⟨ well-defined-f-df v2 dv2 v2dv2 ⟩
        f v2 ⊕ df v2 dv2
      ∎

    equal-future-change : ∀ df → valid f df →
      ∀ v1 dv1 → valid v1 dv1 →
      ∀ v2 dv2 → valid v2 dv2 →
      v1 ⊕ dv1 ≡ v2 ⊕ dv2 →
      f v1 ⊕ df v1 dv1 ≡ f v2 ⊕ df v2 dv2
    equal-future-change df fdf =
      equal-future-base df
        (λ a da ada → proj₂ (fdf a da ada))

    equal-future-derivative : ∀ df → IsDerivative f df →
      ∀ v1 dv1 → valid v1 dv1 →
      ∀ v2 dv2 → valid v2 dv2 →
      v1 ⊕ dv1 ≡ v2 ⊕ dv2 →
      f v1 ⊕ df v1 dv1 ≡ f v2 ⊕ df v2 dv2
    equal-future-derivative df is-derivative-f-df =
      equal-future-base df (derivative-is-valid df is-derivative-f-df)


{- Definition of join for ◇ and associated lemmas. -}
module TemporalOps.Diamond.Join where

open import CategoryTheory.Categories
open import CategoryTheory.Instances.Reactive
open import CategoryTheory.Functor
open import CategoryTheory.NatTrans
open import CategoryTheory.Monad
open import TemporalOps.Common
open import TemporalOps.Next
open import TemporalOps.Delay
open import TemporalOps.Diamond.Functor

import Relation.Binary.PropositionalEquality as ≡
open import Data.Product
open import Relation.Binary.HeterogeneousEquality as ≅
            using (_≅_ ; ≅-to-≡ ; ≡-to-≅ ; cong₂)
open import Data.Nat.Properties
            using (+-identityʳ ; +-comm ; +-suc ; +-assoc)

open import Holes.Term using (⌞_⌟)
open import Holes.Cong.Propositional

-- | Auxiliary definitions for μ

-- Shifting an event by some interval
μ-shift : {A : τ} -> (k n : ℕ) -> ◇ A at n -> ◇ A at (k + n)
μ-shift {A} k n (l , v) = k + l , rew (sym (delay-+ k l n)) v

-- Auxiliary function for μ, taking the comparison of the delay and
-- time index as an argument
μ-compare : (A : τ) -> (n k : ℕ) -> (v : delay ◇ A by k at n)
         -> LeqOrdering k n -> ◇ A at n
μ-compare A .(k + l) k  v snd==[ .k + l ] =
        μ-shift k l (rew (delay-+-left0 k l) v)
μ-compare A n .(n + suc l) v fst==suc[ .n + l ] =
        n + suc l , rew (delay-⊤ n l) top.tt

private module F-◇ = Functor F-◇

-- Join for ◇
μ-◇ : (F-◇ ²) ⟹ F-◇
μ-◇ = record
    { at = μ-◇-at
    ; nat-cond = λ {A} {B} {f} {n} {a}
                -> μ-◇-nat-cond {A} {B} {f} {n} {a}
    }
    where
    open Functor
    private module F-▹ = Functor F-▹
    open ≡.≡-Reasoning

    -- The order of mapping and shifting of events can be interchanged
    μ-shift-fmap : {A B : τ} {f : A ⇴ B} {k l : ℕ} {a : ◇ A at l}
                -> F-◇.fmap f (k + l) (μ-shift {A} k l a)
                 ≡ μ-shift {B} k l (F-◇.fmap f l a)
    μ-shift-fmap {A} {B} {f} {zero} {l} {j , y} = refl
    μ-shift-fmap {A} {B} {f} {suc k} {l} {j , y}
        rewrite μ-shift-fmap {A} {B} {f} {k} {l} {j , y}
        = cong (λ x → suc k + j , x) (fmap-delay-+-k k j l y)

    -- Join for ◇
    μ-◇-at : (A : τ) -> ◇ ◇ A ⇴ ◇ A
    μ-◇-at A n (k , v) = μ-compare A n k v (compareLeq k n)

    -- Naturality proof
    μ-◇-nat-cond : ∀{A B : τ} {f : A ⇴ B} {n : ℕ} {a : ◇ ◇ A at n}
                -> F-◇.fmap f n (μ-◇-at A n a)
                 ≡ μ-◇-at B n (Functor.fmap (F-◇ ²) f n a)
    μ-◇-nat-cond {A} {B} {f} {n} {k , v} with inspect (compareLeq k n)
    -- n = k + l
    μ-◇-nat-cond {A} {B} {f} {.(k + l)} {k , v} | snd==[ .k + l ] with≡ pf =
        begin
            F-◇.fmap f (k + l) (μ-◇-at A (k + l) (k , v))
        ≡⟨⟩ -- Def. of μ-◇-at
            F-◇.fmap f (k + l) (μ-compare A (k + l) k v ⌞ compareLeq k (k + l) ⌟)
        ≡⟨ cong! pf ⟩
            F-◇.fmap f (k + l) (μ-shift k l (rew (delay-+-left0 k l) v))
        ≡⟨ μ-shift-fmap {_}{_}{_}{k}{l}{(rew (delay-+-left0 k l) v)} ⟩
            μ-shift k l (F-◇.fmap f l ⌞ rew (delay-+-left0 k l) v ⌟)
        ≡⟨ cong! (delay-+-left0-eq k l v v′ v≅v′) ⟩
            μ-shift k l (F-◇.fmap f l (rew (delay-+ k 0 l) v′))
        ≡⟨⟩ -- Def. of (F-delay 0).fmap
            μ-shift k l ((Functor.fmap (F-delay 0) (F-◇.fmap f) at l) (rew (delay-+ k 0 l) v′))
        ≡⟨ cong (λ x → μ-shift k l x) (sym (fmap-delay-+-n+k k 0 l v′)) ⟩
            μ-shift k l ⌞ rew (delay-+ k 0 l) ((Functor.fmap (F-delay (k + 0)) (F-◇.fmap f) at (k + l)) v′) ⌟
        ≡⟨ cong!
            (sym (delay-+-left0-eq k l ((Functor.fmap (F-delay k) (F-◇.fmap f) at (k + l)) v)
                                       ((Functor.fmap (F-delay (k + 0)) (F-◇.fmap f) at (k + l)) v′) pr))
         ⟩
            μ-shift k l (rew (delay-+-left0 k l) ((Functor.fmap (F-delay k) (F-◇.fmap f) at (k + l)) v))
        ≡⟨⟩ -- Def. of μ-compare
            μ-compare B (k + l) k ((Functor.fmap (F-delay k) (F-◇.fmap f) at (k + l)) v) (snd==[ k + l ])
        ≡⟨ cong (λ x → μ-compare B (k + l) k ((Functor.fmap (F-delay k) (F-◇.fmap f) at (k + l)) v) x) (sym pf) ⟩
            μ-compare B (k + l) k ((Functor.fmap (F-delay k) (F-◇.fmap f) at (k + l)) v) (compareLeq k (k + l))
        ≡⟨⟩ -- Folding definitions
            μ-◇-at B (k + l) (fmap (F-◇ ²) f (k + l) (k , v))
        ∎
        where
        v′ : delay (◇ A) by (k + 0) at (k + l)
        v′ = rew (delay-+0-left k (k + l)) v
        v≅v′ : v ≅ v′
        v≅v′ = rew-to-≅ (delay-+0-left k (k + l))
        pr : (Functor.fmap (F-delay k) (F-◇.fmap f) at (k + l)) v
           ≅ (Functor.fmap (F-delay (k + 0)) (F-◇.fmap f) at (k + l)) v′
        pr = cong₂ (λ x y → (Functor.fmap (F-delay x) (F-◇.fmap f) at (k + l)) y)
                (≡-to-≅ (sym (+-identityʳ k))) v≅v′

    -- k = suc n + l
    μ-◇-nat-cond {A} {B} {f} {.n} {.(n + suc l) , v} | fst==suc[ n + l ] with≡ pf =
        begin
            F-◇.fmap f n (μ-◇-at A n (n + suc l , v))
        ≡⟨⟩ -- Def. of μ-◇-at
            F-◇.fmap f n (μ-compare A n (n + suc l) v ⌞ compareLeq (n + suc l) n ⌟)
        ≡⟨ cong! pf ⟩
            F-◇.fmap f n (n + suc l , rew (delay-⊤ n l) top.tt)
        ≡⟨⟩ -- Def. of F-◇.fmap
            n + suc l , (Functor.fmap (F-delay (n + suc l)) f at n) (rew (delay-⊤ n l) top.tt)
        ≡⟨ cong (λ x → n + suc l , x) (eq n l) ⟩
            n + suc l , rew (delay-⊤ n l) top.tt
        ≡⟨⟩ -- Def. of μ-compare
            μ-compare B n (n + suc l) ((Functor.fmap (F-delay ((n + suc l))) (F-◇.fmap f) at n) v) (fst==suc[ n + l ])
        ≡⟨ cong (λ x → μ-compare B n (n + suc l) ((Functor.fmap (F-delay (n + suc l)) (F-◇.fmap f) at n) v) x) (sym pf) ⟩
            μ-compare B n (n + suc l) ((Functor.fmap (F-delay (n + suc l)) (F-◇.fmap f) at n) v) (compareLeq (n + suc l) n)
        ≡⟨⟩ -- Def. of μ-◇-at
            μ-◇-at B n (Functor.fmap (F-◇ ²) f n (n + suc l , v))
        ∎
        where
        eq : ∀ (n l : ℕ)
          -> (Functor.fmap (F-delay (n + suc l)) f at n) (rew (delay-⊤ n l) top.tt)
           ≡ rew (delay-⊤ n l) top.tt
        eq zero l = refl
        eq (suc n) l = eq n l


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

open import lib.Basics
open import lib.types.Bool
open import lib.types.FunctionSeq
open import lib.types.Pointed
open import lib.types.Suspension.Core
open import lib.types.Suspension.Flip
open import lib.types.Suspension.Iterated
open import lib.types.Suspension.IteratedEquivs
open import lib.types.Suspension.IteratedTrunc
open import lib.types.Suspension.Trunc
open import lib.types.TLevel
open import lib.types.Truncation

module lib.types.Suspension.IteratedFlip where

module _ {i} {A : Type i} where
  maybe-Susp^-flip : ∀ (n : ℕ)
    → Bool
    → Susp^ n A → Susp^ n A
  maybe-Susp^-flip 0 _ = idf _
  maybe-Susp^-flip (S _) b = maybe-Susp-flip b

  Susp-fmap-maybe-Susp^-flip : ∀ (n : ℕ) (b : Bool)
    → (n == 0 → b == false)
    → maybe-Susp^-flip (S n) b ∼ Susp-fmap (maybe-Susp^-flip n b)
  Susp-fmap-maybe-Susp^-flip O b h x =
    maybe-Susp^-flip 1 b x
      =⟨ ap (λ c → maybe-Susp^-flip 1 c x) (h idp) ⟩
    x
      =⟨ ! (Susp-fmap-idf A x) ⟩
    Susp-fmap (idf A) x =∎
  Susp-fmap-maybe-Susp^-flip (S n) true h x = ! (Susp-fmap-flip x)
  Susp-fmap-maybe-Susp^-flip (S n) false h x = ! (Susp-fmap-idf (Susp^ (S n) A) x)

module _ {i} {X : Ptd i} where

  ⊙maybe-Susp^-flip : ∀ (n : ℕ)
    → Bool
    → ⊙Susp^ n X ⊙→ ⊙Susp^ n X
  ⊙maybe-Susp^-flip 0 _ = ⊙idf _
  ⊙maybe-Susp^-flip (S n) b = ⊙maybe-Susp-flip (⊙Susp^ n X) b

  de⊙-⊙maybe-Susp^-flip : ∀ (n : ℕ) (b : Bool)
    → fst (⊙maybe-Susp^-flip n b) == maybe-Susp^-flip n b
  de⊙-⊙maybe-Susp^-flip O b = idp
  de⊙-⊙maybe-Susp^-flip (S n) b = de⊙-⊙maybe-Susp-flip _ b

  ⊙maybe-Susp^-flip-false : ∀ (n : ℕ)
    → ⊙maybe-Susp^-flip n false == ⊙idf _
  ⊙maybe-Susp^-flip-false O = idp
  ⊙maybe-Susp^-flip-false (S n) = idp

  ⊙maybe-Susp^-flip-flip : ∀ (n : ℕ) (b c : Bool)
    → ⊙maybe-Susp^-flip n b ◃⊙∘ ⊙maybe-Susp^-flip n c ◃⊙idf
      =⊙∘
      ⊙maybe-Susp^-flip n (xor b c) ◃⊙idf
  ⊙maybe-Susp^-flip-flip 0     _ _ = =⊙∘-in idp
  ⊙maybe-Susp^-flip-flip (S n) b c = ⊙maybe-Susp-flip-flip (⊙Susp^ n X) b c

  ⊙Susp-fmap-maybe-Susp^-flip : ∀ (n : ℕ) (b : Bool)
    → (n == 0 → b == false)
    → ⊙Susp-fmap (maybe-Susp^-flip n b) == ⊙maybe-Susp^-flip (S n) b
  ⊙Susp-fmap-maybe-Susp^-flip 0 b h =
    ⊙Susp-fmap (idf (de⊙ X))
      =⟨ =⊙∘-out (⊙Susp-fmap-idf _) ⟩
    ⊙idf _
      =⟨ ap (⊙maybe-Susp^-flip 1) (! (h idp)) ⟩
    ⊙maybe-Susp^-flip 1 b =∎
  ⊙Susp-fmap-maybe-Susp^-flip (S n) b h = ⊙Susp-fmap-maybe-Susp-flip b

Susp^-fmap-Susp-flip : ∀ {i} (m : ℕ) {A : Type i} (s : Susp^ (S m) (Susp A))
  → Susp^-fmap (S m) Susp-flip s == Susp-flip s
Susp^-fmap-Susp-flip O {A} s = Susp-fmap-flip s
Susp^-fmap-Susp-flip (S m) {A} s =
  Susp^-fmap (S (S m)) Susp-flip s
    =⟨ ap (λ f → Susp-fmap f s) (λ= (Susp^-fmap-Susp-flip m)) ⟩
  Susp-fmap Susp-flip s
    =⟨ Susp-fmap-flip s ⟩
  Susp-flip s =∎

abstract
  ⊙Susp^-fmap-⊙Susp-flip : ∀ {i} (m : ℕ) {X : Ptd i}
    → ⊙Susp^-fmap (S m) (⊙Susp-flip X) == ⊙Susp-flip (⊙Susp^ m (⊙Susp (de⊙ X)))
  ⊙Susp^-fmap-⊙Susp-flip 0 {X} = ⊙Susp-fmap-Susp-flip
  ⊙Susp^-fmap-⊙Susp-flip (S m) {X} =
    ⊙Susp^-fmap (S (S m)) (⊙Susp-flip X)
      =⟨ ap ⊙Susp-fmap (λ= (Susp^-fmap-Susp-flip m)) ⟩
    ⊙Susp-fmap Susp-flip
      =⟨ ⊙Susp-fmap-Susp-flip ⟩
    ⊙Susp-flip (⊙Susp^ (S m) (⊙Susp (de⊙ X))) =∎

  ⊙maybe-Susp^-flip-natural : ∀ {i} {j} {X : Ptd i} {Y : Ptd j} (f : X ⊙→ Y)
    (n : ℕ) (b : Bool)
    → ⊙Susp^-fmap n f ⊙∘ ⊙maybe-Susp^-flip n b ==
      ⊙maybe-Susp^-flip n b ⊙∘ ⊙Susp^-fmap n f
  ⊙maybe-Susp^-flip-natural f O b = ! (⊙λ= (⊙∘-unit-l f))
  ⊙maybe-Susp^-flip-natural f (S n) b = ⊙maybe-Susp-flip-natural (⊙Susp^-fmap n f) b

  ⊙maybe-Susp^-flip-natural-=⊙∘ : ∀ {i} {X Y : Ptd i} (f : X ⊙→ Y)
    (n : ℕ) (b : Bool)
    → ⊙Susp^-fmap n f ◃⊙∘
      ⊙maybe-Susp^-flip n b ◃⊙idf
      =⊙∘
      ⊙maybe-Susp^-flip n b ◃⊙∘
      ⊙Susp^-fmap n f ◃⊙idf
  ⊙maybe-Susp^-flip-natural-=⊙∘ f n b =
    =⊙∘-in (⊙maybe-Susp^-flip-natural f n b)

abstract
  ⊙Susp^-comm-flip : ∀ {i} (m n : ℕ) (X : Ptd i)
    → ⊙Susp^-fmap n (⊙Susp-flip (⊙Susp^ m X)) ◃⊙∘
      ⊙coe (⊙Susp^-comm (S m) n {X}) ◃⊙idf
      =⊙∘
      ⊙coe (⊙Susp^-comm (S m) n {X}) ◃⊙∘
      ⊙Susp-flip (⊙Susp^ m (⊙Susp^ n X)) ◃⊙idf
  ⊙Susp^-comm-flip m O X =
    ⊙Susp-flip (⊙Susp^ m X) ◃⊙∘
    ⊙coe (⊙Susp^-comm (S m) 0) ◃⊙idf
      =⊙∘⟨ 1 & 1 & =⊙∘-in {gs = ⊙idf-seq} $ ap ⊙coe (⊙Susp^-comm-0-r (S m) X) ⟩
    ⊙Susp-flip (⊙Susp^ m X) ◃⊙idf
      =⊙∘⟨ 0 & 0 & =⊙∘-in {gs = ⊙coe (⊙Susp^-comm (S m) O) ◃⊙idf} $
                   ! $ ap ⊙coe (⊙Susp^-comm-0-r (S m) X) ⟩
    ⊙coe (⊙Susp^-comm (S m) O) ◃⊙∘
    ⊙Susp-flip (⊙Susp^ m (⊙Susp^ O X)) ◃⊙idf ∎⊙∘
  ⊙Susp^-comm-flip m (S n) X =
    ⊙Susp^-fmap (S n) (⊙Susp-flip (⊙Susp^ m X)) ◃⊙∘
    ⊙coe (⊙Susp^-comm (S m) (S n)) ◃⊙idf
      =⊙∘₁⟨ 0 & 1 & ⊙Susp^-fmap-⊙Susp-flip n {⊙Susp^ m X} ⟩
    ⊙Susp-flip (⊙Susp^ n (⊙Susp (Susp^ m (de⊙ X)))) ◃⊙∘
    ⊙coe (⊙Susp^-comm (S m) (S n)) ◃⊙idf
      =⊙∘₁⟨ 1 & 1 & p ⟩
    ⊙Susp-flip (⊙Susp^ n (⊙Susp (Susp^ m (de⊙ X)))) ◃⊙∘
    ⊙Susp-fmap (fst (⊙coe (⊙Susp^-comm m (S n) ∙ ⊙Susp^-comm 1 n))) ◃⊙idf
      =⊙∘⟨ =⊙∘-in {gs = ⊙Susp-fmap (fst (⊙coe (⊙Susp^-comm m (S n) {X} ∙ ⊙Susp^-comm 1 n {⊙Susp^ m X}))) ◃⊙∘
                        ⊙Susp-flip (⊙Susp^ m (⊙Susp (Susp^ n (de⊙ X)))) ◃⊙idf} $
           ⊙Susp-flip-natural (⊙coe (⊙Susp^-comm m (S n) ∙ ⊙Susp^-comm 1 n)) ⟩
    ⊙Susp-fmap (fst (⊙coe (⊙Susp^-comm m (S n) {X} ∙ ⊙Susp^-comm 1 n {⊙Susp^ m X}))) ◃⊙∘
    ⊙Susp-flip (⊙Susp^ m (⊙Susp (Susp^ n (de⊙ X)))) ◃⊙idf
      =⊙∘₁⟨ 0 & 1 & ! p ⟩
    ⊙coe (⊙Susp^-comm (S m) (S n)) ◃⊙∘
    ⊙Susp-flip (⊙Susp^ m (⊙Susp^ (S n) X)) ◃⊙idf ∎⊙∘
    where
    p : ⊙coe (⊙Susp^-comm (S m) (S n) {X}) ==
        ⊙Susp-fmap (fst (⊙coe (⊙Susp^-comm m (S n) {X} ∙ ⊙Susp^-comm 1 n {⊙Susp^ m X})))
    p =
      ap ⊙coe (⊙Susp^-comm-S-S m n) ∙
      ! (⊙transport-⊙coe ⊙Susp (Susp^-comm m (S n) ∙ Susp^-comm 1 n)) ∙
      ⊙transport-⊙Susp (Susp^-comm m (S n) ∙ Susp^-comm 1 n) ∙
      ap (⊙Susp-fmap ∘ coe) (ap2 _∙_ (! (de⊙-⊙Susp^-comm m (S n) {X}))
                                     (! (de⊙-⊙Susp^-comm 1 n {⊙Susp^ m X}))) ∙
      ap (⊙Susp-fmap ∘ coe) (∙-ap de⊙ (⊙Susp^-comm m (S n)) (⊙Susp^-comm 1 n))

  ⊙maybe-Susp^-flip-⊙Susp^-comm : ∀ {i} (X : Ptd i) (m n : ℕ) (b : Bool)
    → ⊙Susp^-fmap n (⊙maybe-Susp^-flip m b) ◃⊙∘
      ⊙coe (⊙Susp^-comm m n {X}) ◃⊙idf
      =⊙∘
      ⊙coe (⊙Susp^-comm m n {X}) ◃⊙∘
      ⊙maybe-Susp^-flip m b ◃⊙idf
  ⊙maybe-Susp^-flip-⊙Susp^-comm X O n b =
    ⊙Susp^-fmap n (⊙idf X) ◃⊙∘
    ⊙coe (⊙Susp^-comm 0 n) ◃⊙idf
      =⊙∘⟨ 0 & 1 & ⊙Susp^-fmap-idf n X ⟩
    ⊙coe (⊙Susp^-comm 0 n) ◃⊙idf
      =⊙∘⟨ 1 & 0 & ⊙contract ⟩
    ⊙coe (⊙Susp^-comm 0 n) ◃⊙∘
    ⊙idf (⊙Susp^ n X) ◃⊙idf ∎⊙∘
  ⊙maybe-Susp^-flip-⊙Susp^-comm X (S m) n true = ⊙Susp^-comm-flip m n X
  ⊙maybe-Susp^-flip-⊙Susp^-comm X (S m) n false =
    ⊙Susp^-fmap n (⊙idf (⊙Susp^ (S m) X)) ◃⊙∘
    ⊙coe (⊙Susp^-comm (S m) n) ◃⊙idf
      =⊙∘⟨ 0 & 1 & ⊙Susp^-fmap-idf n (⊙Susp^ (S m) X) ⟩
    ⊙coe (⊙Susp^-comm (S m) n) ◃⊙idf
      =⊙∘⟨ 1 & 0 & ⊙contract ⟩
    ⊙coe (⊙Susp^-comm (S m) n) ◃⊙∘
    ⊙idf (⊙Susp^ (S m) (⊙Susp^ n X)) ◃⊙idf ∎⊙∘

module _ {i} (X : Ptd i) (m : ℕ₋₂) where

  ⊙Susp^-Trunc-swap-⊙maybe-Susp^-flip : ∀ (n : ℕ) (b : Bool)
    → ⊙Susp^-Trunc-swap X m n ◃⊙∘
      ⊙maybe-Susp^-flip n b ◃⊙idf
      =⊙∘
      ⊙Trunc-fmap (⊙maybe-Susp^-flip n b) ◃⊙∘
      ⊙Susp^-Trunc-swap X m n ◃⊙idf
  ⊙Susp^-Trunc-swap-⊙maybe-Susp^-flip O b =
    =⊙∘-in (! (⊙λ= ⊙Trunc-fmap-⊙idf))
  ⊙Susp^-Trunc-swap-⊙maybe-Susp^-flip (S n) true =
    ⊙Susp^-Trunc-swap X m (S n) ◃⊙∘
    ⊙Susp-flip (⊙Susp^ n (⊙Trunc m X)) ◃⊙idf
      =⊙∘⟨ 0 & 1 & ⊙expand $
           ⊙Susp-Trunc-swap (Susp^ n (de⊙ X)) (⟨ n ⟩₋₂ +2+ m) ◃⊙∘
           ⊙Susp-fmap (Susp^-Trunc-swap (de⊙ X) m n) ◃⊙idf ⟩
    ⊙Susp-Trunc-swap (Susp^ n (de⊙ X)) (⟨ n ⟩₋₂ +2+ m) ◃⊙∘
    ⊙Susp-fmap (Susp^-Trunc-swap (de⊙ X) m n) ◃⊙∘
    ⊙Susp-flip (⊙Susp^ n (⊙Trunc m X)) ◃⊙idf
      =⊙∘⟨ 1 & 2 & =⊙∘-in
           {gs = ⊙Susp-flip (⊙Trunc (⟨ n ⟩₋₂ +2+ m) (⊙Susp^ n X)) ◃⊙∘
                 ⊙Susp-fmap (Susp^-Trunc-swap (de⊙ X) m n) ◃⊙idf} $
           ! $ ⊙Susp-flip-natural (⊙Susp^-Trunc-swap X m n) ⟩
    ⊙Susp-Trunc-swap (Susp^ n (de⊙ X)) (⟨ n ⟩₋₂ +2+ m) ◃⊙∘
    ⊙Susp-flip (⊙Trunc (⟨ n ⟩₋₂ +2+ m) (⊙Susp^ n X)) ◃⊙∘
    ⊙Susp-fmap (Susp^-Trunc-swap (de⊙ X) m n) ◃⊙idf
      =⊙∘⟨ 0 & 2 & ⊙Susp-Trunc-swap-⊙Susp-flip (⟨ n ⟩₋₂ +2+ m) ⟩
    ⊙Trunc-fmap (⊙Susp-flip (⊙Susp^ n X)) ◃⊙∘
    ⊙Susp-Trunc-swap (Susp^ n (de⊙ X)) (⟨ n ⟩₋₂ +2+ m) ◃⊙∘
    ⊙Susp-fmap (Susp^-Trunc-swap (de⊙ X) m n) ◃⊙idf
      =⊙∘⟨ 1 & 2 & ⊙contract ⟩
    ⊙Trunc-fmap (⊙Susp-flip (⊙Susp^ n X)) ◃⊙∘
    ⊙Susp^-Trunc-swap X m (S n) ◃⊙idf ∎⊙∘
  ⊙Susp^-Trunc-swap-⊙maybe-Susp^-flip (S n) false =
    ⊙Susp^-Trunc-swap X m (S n) ◃⊙∘
    ⊙idf _ ◃⊙idf
      =⊙∘⟨ 1 & 1 & ⊙expand ⊙idf-seq ⟩
    ⊙Susp^-Trunc-swap X m (S n) ◃⊙idf
      =⊙∘₁⟨ 0 & 0 & ! (⊙λ= ⊙Trunc-fmap-⊙idf) ⟩
    ⊙Trunc-fmap (⊙idf (⊙Susp (de⊙ (⊙Susp^ n X)))) ◃⊙∘
    ⊙Susp^-Trunc-swap X m (S n) ◃⊙idf ∎⊙∘


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

module BasicIS4.Syntax.DyadicHilbertSequential where

open import BasicIS4.Syntax.Common public

-- Derivations.

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

infix 3 _⊢_
_⊢_ : Cx² Ty Ty → Ty → Set
Γ ⁏ Δ ⊢ A = ∃ (λ Ξ → Γ ⁏ Δ ⊦⊢ Ξ , A)

-- Monotonicity with respect to context inclusion.

mono⊦⊢ : ∀ {Ξ Γ Γ′ Δ} → Γ ⊆ Γ′ → Γ ⁏ Δ ⊦⊢ Ξ → Γ′ ⁏ Δ ⊦⊢ Ξ
mono⊦⊢ η nil         = nil
mono⊦⊢ η (var i ts)  = var (mono∈ η i) (mono⊦⊢ η ts)
mono⊦⊢ η (mp i j ts) = mp i j (mono⊦⊢ η ts)
mono⊦⊢ η (ci ts)     = ci (mono⊦⊢ η ts)
mono⊦⊢ η (ck ts)     = ck (mono⊦⊢ η ts)
mono⊦⊢ η (cs ts)     = cs (mono⊦⊢ η ts)
mono⊦⊢ η (mvar i ts) = mvar i (mono⊦⊢ η ts)
mono⊦⊢ η (nec ss ts) = nec ss (mono⊦⊢ η ts)
mono⊦⊢ η (cdist ts)  = cdist (mono⊦⊢ η ts)
mono⊦⊢ η (cup ts)    = cup (mono⊦⊢ η ts)
mono⊦⊢ η (cdown ts)  = cdown (mono⊦⊢ η ts)
mono⊦⊢ η (cpair ts)  = cpair (mono⊦⊢ η ts)
mono⊦⊢ η (cfst ts)   = cfst (mono⊦⊢ η ts)
mono⊦⊢ η (csnd ts)   = csnd (mono⊦⊢ η ts)
mono⊦⊢ η (unit ts)   = unit (mono⊦⊢ η ts)

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

-- Monotonicity with respect to modal context inclusion.

mmono⊦⊢ : ∀ {Ξ Γ Δ Δ′} → Δ ⊆ Δ′ → Γ ⁏ Δ ⊦⊢ Ξ → Γ ⁏ Δ′ ⊦⊢ Ξ
mmono⊦⊢ θ nil         = nil
mmono⊦⊢ θ (var i ts)  = var i (mmono⊦⊢ θ ts)
mmono⊦⊢ θ (mp i j ts) = mp i j (mmono⊦⊢ θ ts)
mmono⊦⊢ θ (ci ts)     = ci (mmono⊦⊢ θ ts)
mmono⊦⊢ θ (ck ts)     = ck (mmono⊦⊢ θ ts)
mmono⊦⊢ θ (cs ts)     = cs (mmono⊦⊢ θ ts)
mmono⊦⊢ θ (mvar i ts) = mvar (mono∈ θ i) (mmono⊦⊢ θ ts)
mmono⊦⊢ θ (nec ss ts) = nec (mmono⊦⊢ θ ss) (mmono⊦⊢ θ ts)
mmono⊦⊢ θ (cdist ts)  = cdist (mmono⊦⊢ θ ts)
mmono⊦⊢ θ (cup ts)    = cup (mmono⊦⊢ θ ts)
mmono⊦⊢ θ (cdown ts)  = cdown (mmono⊦⊢ θ ts)
mmono⊦⊢ θ (cpair ts)  = cpair (mmono⊦⊢ θ ts)
mmono⊦⊢ θ (cfst ts)   = cfst (mmono⊦⊢ θ ts)
mmono⊦⊢ θ (csnd ts)   = csnd (mmono⊦⊢ θ ts)
mmono⊦⊢ θ (unit ts)   = unit (mmono⊦⊢ θ ts)

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

-- Concatenation of derivations.

_⧺⊦_ : ∀ {Γ Δ Ξ Ξ′} → Γ ⁏ Δ ⊦⊢ Ξ → Γ ⁏ Δ ⊦⊢ Ξ′ → Γ ⁏ Δ ⊦⊢ Ξ ⧺ Ξ′
us ⧺⊦ nil       = us
us ⧺⊦ var i ts  = var i (us ⧺⊦ ts)
us ⧺⊦ mp i j ts = mp (mono∈ weak⊆⧺₂ i) (mono∈ weak⊆⧺₂ j) (us ⧺⊦ ts)
us ⧺⊦ ci ts     = ci (us ⧺⊦ ts)
us ⧺⊦ ck ts     = ck (us ⧺⊦ ts)
us ⧺⊦ cs ts     = cs (us ⧺⊦ ts)
us ⧺⊦ mvar i ts = mvar i (us ⧺⊦ ts)
us ⧺⊦ nec ss ts = nec ss (us ⧺⊦ ts)
us ⧺⊦ cdist ts  = cdist (us ⧺⊦ ts)
us ⧺⊦ cup ts    = cup (us ⧺⊦ ts)
us ⧺⊦ cdown ts  = cdown (us ⧺⊦ ts)
us ⧺⊦ cpair ts  = cpair (us ⧺⊦ ts)
us ⧺⊦ cfst ts   = cfst (us ⧺⊦ ts)
us ⧺⊦ csnd ts   = csnd (us ⧺⊦ ts)
us ⧺⊦ unit ts   = unit (us ⧺⊦ ts)

-- Modus ponens and necessitation in expanded form.

app : ∀ {A B Γ Δ} → Γ ⁏ Δ ⊢ A ▻ B → Γ ⁏ Δ ⊢ A → Γ ⁏ Δ ⊢ B
app {A} {B} (Ξ , ts) (Ξ′ , us) = Ξ″ , vs
  where Ξ″ = (Ξ′ , A) ⧺ (Ξ , A ▻ B)
        vs = mp top (mono∈ (weak⊆⧺₁ (Ξ , A ▻ B)) top) (us ⧺⊦ ts)

box : ∀ {A Γ Δ} → ∅ ⁏ Δ ⊢ A → Γ ⁏ Δ ⊢ □ A
box (Ξ , ts) = ∅ , nec ts nil


open import Relation.Binary.Core

module PLRTree.DropLast.Complete {A : Set}
                     (_≤_ : A → A → Set)
                     (tot≤ : Total _≤_)  where

open import Data.Sum renaming (_⊎_ to _∨_)
open import PLRTree {A}
open import PLRTree.Drop _≤_ tot≤
open import PLRTree.Complete {A}
open import PLRTree.Complete.Properties {A}
open import PLRTree.Compound {A}
open import PLRTree.Equality {A}
open import PLRTree.Equality.Properties {A}

lemma-dropLast-≃ : {l r : PLRTree} → l ≃ r → Compound l → l ⋙ dropLast r
lemma-dropLast-≃ (≃nd x x' ≃lf ≃lf ≃lf) compound = ⋙p (⋗lf x)
lemma-dropLast-≃ (≃nd x x' (≃nd _ _ _ _ _) ≃lf ()) compound
lemma-dropLast-≃ (≃nd x x' l≃r (≃nd x'₁ x'₂ l'₁≃r'₁ l'₂≃r'₂ l'₁≃l'₂) (≃nd x₁ .x'₁ l₁≃r₁ _ l₁≃l'₁)) compound =
               let x'₁l'₁r'₁⋙dl-x'₂l'₂r'₂ = lemma-dropLast-≃ (≃nd x'₁ x'₂ l'₁≃r'₁ l'₂≃r'₂ l'₁≃l'₂) compound
               in ⋙r x x' l≃r x'₁l'₁r'₁⋙dl-x'₂l'₂r'₂ (≃nd x₁ x'₁ l₁≃r₁ l'₁≃r'₁ l₁≃l'₁)

lemma-dropLast-⋗ : {l r : PLRTree} → l ⋗ r → Compound r → dropLast l ⋘ r
lemma-dropLast-⋗ (⋗nd _ _ ≃lf _ ()) compound
lemma-dropLast-⋗ (⋗nd x x' (≃nd x₁ x₂ ≃lf ≃lf ≃lf) ≃lf (⋗lf .x₁)) compound  = x⋘ x x₁ x'
lemma-dropLast-⋗ (⋗nd _ _ (≃nd x₁ _ ≃lf (≃nd _ _ _ _ _) ()) ≃lf (⋗lf .x₁)) compound
lemma-dropLast-⋗ (⋗nd x x' (≃nd {l₁} {r₁} x₁ x₂ l₁≃r₁ l₂≃r₂ l₁≃l₂) l'≃r' (⋗nd .x₁ x'₁ _ l'₁≃r'₁ l₁⋗l'₁)) compound =
               let x₁l₁r₁⋙dl-x₂l₂r₂ = lemma-dropLast-≃ (≃nd x₁ x₂ l₁≃r₁ l₂≃r₂ l₁≃l₂) compound
               in r⋘ x x' x₁l₁r₁⋙dl-x₂l₂r₂ l'≃r' (⋗nd x₁ x'₁ l₁≃r₁ l'₁≃r'₁ l₁⋗l'₁)

mutual
  lemma-dropLast-⋘ : {l r : PLRTree} → l ⋘ r → dropLast l ⋘ r ∨ dropLast l ≃ r
  lemma-dropLast-⋘ (x⋘ x _ z) = inj₂ (≃nd x z ≃lf ≃lf ≃lf)
  lemma-dropLast-⋘ (l⋘ x x' (x⋘ x₁ x₂ x₃) x'₁≃x'₂ (≃nd .x₃ x'₁ ≃lf ≃lf ≃lf)) = inj₂ (≃nd x x' (≃nd x₁ x₃ ≃lf ≃lf ≃lf) x'₁≃x'₂ (≃nd x₁ x'₁ ≃lf ≃lf ≃lf))
  lemma-dropLast-⋘ (l⋘ x x' (l⋘ {l₁} {r₁} x₁ x₂ l₁⋘r₁ l₂≃r₂ r₁≃l₂) l'≃r' (≃nd .x₂ x'₁ _ l'₁≃r'₁ l₂≃l'₁))
      with dropLast (node left x₁ l₁ r₁) | lemma-dropLast-⋘ (l⋘ x₁ x₂ l₁⋘r₁ l₂≃r₂ r₁≃l₂)
  ... | leaf | inj₁ ()
  ... | node perfect _ _ _ | inj₁ ()
  ... | node left _ _ _ | inj₁ dl-x₁l₁r₁⋘x₂l₂r₂ = inj₁ (l⋘ x x' dl-x₁l₁r₁⋘x₂l₂r₂ l'≃r' (≃nd x₂ x'₁ l₂≃r₂ l'₁≃r'₁ l₂≃l'₁))
  ... | node right _ _ _ | inj₁ dl-x₁l₁r₁⋘x₂l₂r₂ = inj₁ (l⋘ x x' dl-x₁l₁r₁⋘x₂l₂r₂ l'≃r' (≃nd x₂ x'₁ l₂≃r₂ l'₁≃r'₁ l₂≃l'₁))
  ... | leaf | inj₂ ()
  ... | node perfect x'' l'' r'' | inj₂ (≃nd .x'' .x₂ l''≃r'' _ l''≃l₂) =
               let l''≃l'₁ = trans≃ l''≃l₂ l₂≃l'₁
               in inj₂ (≃nd x x' (≃nd x'' x₂ l''≃r'' l₂≃r₂ l''≃l₂) l'≃r' (≃nd x'' x'₁ l''≃r'' l'₁≃r'₁ l''≃l'₁))
  ... | node left _ _ _ | inj₂ ()
  ... | node right _ _ _ | inj₂ ()
  lemma-dropLast-⋘ (l⋘ x x' (r⋘ {l₁} {r₁} x₁ x₂ l₁⋙r₁ l₂≃r₂ l₁⋗l₂) l'≃r' (≃nd .x₂ x'₁ _ l'₁≃r'₁ l₂≃l'₁))
      with dropLast (node right x₁ l₁ r₁) | lemma-dropLast-⋘ (r⋘ x₁ x₂ l₁⋙r₁ l₂≃r₂ l₁⋗l₂)
  ... | leaf | inj₁ ()
  ... | node perfect _ _ _ | inj₁ ()
  ... | node left _ _ _ | inj₁ dl-x₁l₁r₁⋘x₂l₂r₂ = inj₁ (l⋘ x x' dl-x₁l₁r₁⋘x₂l₂r₂ l'≃r' (≃nd x₂ x'₁ l₂≃r₂ l'₁≃r'₁ l₂≃l'₁))
  ... | node right _ _ _ | inj₁ dl-x₁l₁r₁⋘x₂l₂r₂ = inj₁ (l⋘ x x' dl-x₁l₁r₁⋘x₂l₂r₂ l'≃r' (≃nd x₂ x'₁ l₂≃r₂ l'₁≃r'₁ l₂≃l'₁))
  ... | leaf | inj₂ ()
  ... | node perfect x'' l'' r'' | inj₂ (≃nd .x'' .x₂ l''≃r'' _ l''≃l₂) =
               let l''≃l'₁ = trans≃ l''≃l₂ l₂≃l'₁
               in inj₂ (≃nd x x' (≃nd x'' x₂ l''≃r'' l₂≃r₂ l''≃l₂) l'≃r' (≃nd x'' x'₁ l''≃r'' l'₁≃r'₁ l''≃l'₁))
  ... | node left _ _ _ | inj₂ ()
  ... | node right _ _ _ | inj₂ ()
  lemma-dropLast-⋘ (r⋘ x x' (⋙p (⋗lf x₁)) ≃lf (⋗lf .x₁))  = inj₂ (≃nd x x' ≃lf ≃lf ≃lf)
  lemma-dropLast-⋘ (r⋘ _ _ (⋙p (⋗lf x₁)) l'≃r' (⋗nd .x₁ _ _ _ ()))
  lemma-dropLast-⋘ (r⋘ _ _ (⋙p (⋗nd x₁ _ ≃lf _ ())) ≃lf (⋗lf .x₁))
  lemma-dropLast-⋘ (r⋘ x x' (⋙p (⋗nd x₁ x₂ l₁≃r₁ l₂≃r₂ l₁⋗l₂)) l'≃r' (⋗nd .x₁ x'₁ _ l'₁≃r'₁ l₁⋗l'₁)) =
               let dl-x₁l₁r₁⋘x₂l₂r₂ = lemma-dropLast-⋗ (⋗nd x₁ x₂ l₁≃r₁ l₂≃r₂ l₁⋗l₂) compound ;
                    l₂≃l'₁ = lemma-⋗* l₁⋗l₂ l₁⋗l'₁
               in inj₁ (l⋘ x x' dl-x₁l₁r₁⋘x₂l₂r₂ l'≃r' (≃nd x₂ x'₁ l₂≃r₂ l'₁≃r'₁ l₂≃l'₁))
  lemma-dropLast-⋘ (r⋘ _ _ (⋙l x₁ _ ≃lf _ ()) ≃lf (⋗lf .x₁))
  lemma-dropLast-⋘ (r⋘ x x' (⋙l x₁ x₂ l₁≃r₁ l₂⋘r₂ l₁⋗r₂) l'≃r' (⋗nd .x₁ x'₁ _ l'₁≃r'₁ l₁⋗l'₁))
      with lemma-dropLast-⋙ (⋙l x₁ x₂ l₁≃r₁ l₂⋘r₂ l₁⋗r₂)
  ... | inj₁ dl-x₁l₁r₁⋘x₂l₂r₂ = inj₁ (r⋘ x x' dl-x₁l₁r₁⋘x₂l₂r₂ l'≃r' (⋗nd x₁ x'₁ l₁≃r₁ l'₁≃r'₁ l₁⋗l'₁))
  ... | inj₂ ()
  lemma-dropLast-⋘ (r⋘ x x' (⋙r x₁ x₂ ≃lf (⋙p ()) ≃lf) ≃lf (⋗lf .x₁))
  lemma-dropLast-⋘ (r⋘ _ _ (⋙r x₁ _ ≃lf (⋙l _ _ _ _ _) ()) ≃lf (⋗lf .x₁))
  lemma-dropLast-⋘ (r⋘ _ _ (⋙r x₁ _ ≃lf (⋙r _ _ _ _ _) ()) ≃lf (⋗lf .x₁))
  lemma-dropLast-⋘ (r⋘ x x' (⋙r x₁ x₂ l₁≃r₁ l₂⋙r₂ l₁≃l₂) l'≃r' (⋗nd .x₁ x'₁ _ l'₁≃r'₁ l₁⋗l'₁))
      with lemma-dropLast-⋙ (⋙r x₁ x₂ l₁≃r₁ l₂⋙r₂ l₁≃l₂)
  ... | inj₁ dl-x₁l₁r₁⋘x₂l₂r₂ = inj₁ (r⋘ x x' dl-x₁l₁r₁⋘x₂l₂r₂ l'≃r' (⋗nd x₁ x'₁ l₁≃r₁ l'₁≃r'₁ l₁⋗l'₁))
  ... | inj₂ ()

  lemma-dropLast-⋙ : {l r : PLRTree} → l ⋙ r → l ⋙ dropLast r ∨ l ⋗ r
  lemma-dropLast-⋙ (⋙p l⋗r) = inj₂ l⋗r
  lemma-dropLast-⋙ (⋙l x x' l≃r (x⋘ x'₁ x'₂ x'₃) (⋗nd x₁ .x'₃ l₁≃r₁ ≃lf l₁⋗x'₂)) = inj₁ (⋙p (⋗nd x x' l≃r (≃nd x'₁ x'₃ ≃lf ≃lf ≃lf) (⋗nd x₁ x'₁ l₁≃r₁ ≃lf l₁⋗x'₂)))
  lemma-dropLast-⋙ (⋙l x x' l≃r (l⋘ {l'₁} {r'₁} x'₁ x'₂ l'₁⋘r'₁ l'₂≃r'₂ r'₁≃l'₂) (⋗nd x₁ .x'₂ l₁≃r₁ _ l₁⋗l'₂))
      with dropLast (node left x'₁ l'₁ r'₁) | lemma-dropLast-⋘ (l⋘ x'₁ x'₂ l'₁⋘r'₁ l'₂≃r'₂ r'₁≃l'₂)
  ... | leaf | inj₁ ()
  ... | node perfect _ _ _ | inj₁ ()
  ... | node left _ _ _ | inj₁ dl-x'₁l'₁r'₁⋘x'₂l'₂r'₂ = inj₁ (⋙l x x' l≃r dl-x'₁l'₁r'₁⋘x'₂l'₂r'₂ (⋗nd x₁ x'₂ l₁≃r₁ l'₂≃r'₂ l₁⋗l'₂))
  ... | node right _ _ _ | inj₁ dl-x'₁l'₁r'₁⋘x'₂l'₂r'₂ = inj₁ (⋙l x x' l≃r dl-x'₁l'₁r'₁⋘x'₂l'₂r'₂ (⋗nd x₁ x'₂ l₁≃r₁ l'₂≃r'₂ l₁⋗l'₂))
  ... | leaf | inj₂ ()
  ... | node perfect x'' l'' r'' | inj₂ (≃nd .x'' .x'₂ l''≃r'' _ l''≃l'₂) =
               let l₁⋗l'' = lemma-⋗-≃ l₁⋗l'₂ (sym≃ l''≃l'₂)
               in inj₁ (⋙p (⋗nd x x' l≃r (≃nd x'' x'₂ l''≃r'' l'₂≃r'₂ l''≃l'₂) (⋗nd x₁ x'' l₁≃r₁ l''≃r'' l₁⋗l'')))
  ... | node left _ _ _ | inj₂ ()
  ... | node right _ _ _ | inj₂ ()
  lemma-dropLast-⋙ (⋙l x x' l≃r (r⋘ {l'₁} {r'₁} x'₁ x'₂ l'₁⋙r'₁ l'₂≃r'₂ l'₁⋗l'₂) (⋗nd x₁ .x'₂ l₁≃r₁ _ l₁⋗l'₂))
      with dropLast (node right x'₁ l'₁ r'₁) | lemma-dropLast-⋘ (r⋘ x'₁ x'₂ l'₁⋙r'₁ l'₂≃r'₂ l'₁⋗l'₂)
  ... | leaf | inj₁ ()
  ... | node perfect _ _ _ | inj₁ ()
  ... | node left _ _ _ | inj₁ dl-x'₁l'₁r'₁⋘x'₂l'₂r'₂ = inj₁ (⋙l x x' l≃r dl-x'₁l'₁r'₁⋘x'₂l'₂r'₂ (⋗nd x₁ x'₂ l₁≃r₁ l'₂≃r'₂ l₁⋗l'₂))
  ... | node right _ _ _ | inj₁ dl-x'₁l'₁r'₁⋘x'₂l'₂r'₂ = inj₁ (⋙l x x' l≃r dl-x'₁l'₁r'₁⋘x'₂l'₂r'₂ (⋗nd x₁ x'₂ l₁≃r₁ l'₂≃r'₂ l₁⋗l'₂))
  ... | leaf | inj₂ ()
  ... | node perfect x'' l'' r'' | inj₂ (≃nd .x'' .x'₂ l''≃r'' _ l''≃l'₂) =
               let l₁⋗l'' = lemma-⋗-≃ l₁⋗l'₂ (sym≃ l''≃l'₂)
               in inj₁ (⋙p (⋗nd x x' l≃r (≃nd x'' x'₂ l''≃r'' l'₂≃r'₂ l''≃l'₂) (⋗nd x₁ x'' l₁≃r₁ l''≃r'' l₁⋗l'')))
  ... | node left _ _ _ | inj₂ ()
  ... | node right _ _ _ | inj₂ ()
  lemma-dropLast-⋙ (⋙r x x' l≃r (⋙p ()) ≃lf)
  lemma-dropLast-⋙ (⋙r x x' l≃r (⋙p (⋗lf x'₁)) (≃nd x₁ .x'₁ ≃lf ≃lf ≃lf)) = inj₁ (⋙p (⋗nd x x' l≃r ≃lf (⋗lf x₁)))
  lemma-dropLast-⋙ (⋙r x x' l≃r (⋙p (⋗nd x'₁ x'₂ l'₁≃r'₁ l'₂≃r'₂ l'₁⋗l'₂)) (≃nd x₁ .x'₁ l₁≃r₁ _ l₁≃l'₁)) =
               let dl-x'₁l'₁r'₁⋘x'₂l'₂r'₂ =  lemma-dropLast-⋗ (⋗nd x'₁ x'₂ l'₁≃r'₁ l'₂≃r'₂ l'₁⋗l'₂) compound ;
                    l₁⋗l'₂ = lemma-≃-⋗ (sym≃ l₁≃l'₁) l'₁⋗l'₂
               in inj₁ (⋙l x x' l≃r dl-x'₁l'₁r'₁⋘x'₂l'₂r'₂ (⋗nd x₁ x'₂ l₁≃r₁ l'₂≃r'₂ l₁⋗l'₂))
  lemma-dropLast-⋙ (⋙r x x' l≃r (⋙l x'₁ x'₂ l'₁≃r'₁ l'₂⋘r'₂ l'₁⋗r'₂) (≃nd x₁ .x'₁ l₁≃r₁ _ l₁≃l'₁))
      with lemma-dropLast-⋙ (⋙l x'₁ x'₂ l'₁≃r'₁ l'₂⋘r'₂ l'₁⋗r'₂)
  ... | inj₁ x'₁l'₁r'₁⋙dl-x'₂l'₂r'₂ = inj₁ (⋙r x x' l≃r x'₁l'₁r'₁⋙dl-x'₂l'₂r'₂ (≃nd x₁ x'₁ l₁≃r₁ l'₁≃r'₁ l₁≃l'₁))
  ... | inj₂ ()
  lemma-dropLast-⋙ (⋙r x x' l≃r (⋙r x'₁ x'₂ l'₁≃r'₁ l'₂⋙r'₂ l'₁≃l'₂) (≃nd x₁ .x'₁ l₁≃r₁ _ l₁≃l'₁))
      with lemma-dropLast-⋙ (⋙r x'₁ x'₂ l'₁≃r'₁ l'₂⋙r'₂ l'₁≃l'₂)
  ... | inj₁ x'₁l'₁r'₁⋙dl-x'₂l'₂r'₂ = inj₁ (⋙r x x' l≃r x'₁l'₁r'₁⋙dl-x'₂l'₂r'₂ (≃nd x₁ x'₁ l₁≃r₁ l'₁≃r'₁ l₁≃l'₁))
  ... | inj₂ ()

lemma-dropLast-complete : {t : PLRTree} → Complete t → Complete (dropLast t)
lemma-dropLast-complete leaf = leaf
lemma-dropLast-complete (perfect {l} {r} x cl cr l≃r)
     with l | r | l≃r
... | leaf | leaf | ≃lf = leaf
... | node perfect x' l' r' | node perfect x'' l'' r'' | ≃nd .x' .x'' l'≃r' l''≃r'' l'≃l'' = right x cl (lemma-dropLast-complete cr) (lemma-dropLast-≃ (≃nd x' x'' l'≃r' l''≃r'' l'≃l'') compound)
lemma-dropLast-complete (left {l} {r} x cl cr l⋘r)
    with l | r | l⋘r | lemma-dropLast-⋘ l⋘r
... | leaf | _ | () | _
... | node perfect x' l' r' | _ | () | _
... | node left x' l' r' | node perfect x'' l'' r'' | l⋘ .x' .x'' l'⋘r' l''≃r'' r'≃l'' | inj₁ ld⋘r
    with dropLast (node left x' l' r') | ld⋘r | lemma-dropLast-complete cl
... | leaf | () | _
... | node perfect _ _ _ | () | _
... | node left x''' l''' r''' | ld⋘r' | cld = left x cld cr ld⋘r'
... | node right x''' l''' r''' | ld⋘r' | cld = left x cld cr ld⋘r'
lemma-dropLast-complete (left x cl cr l⋘r) | node left x' l' r' | node perfect x'' l'' r'' | l⋘ .x' .x'' l'⋘r' l''≃r'' r'≃l'' | inj₂ ld≃r
    with dropLast (node left x' l' r') | ld≃r | lemma-dropLast-complete cl
... | leaf | () | _
... | node perfect x''' l''' r''' | ld≃r' | cld = perfect x cld cr ld≃r'
... | node left _ _ _ | () | _
... | node right _ _ _ | () | _
lemma-dropLast-complete (left x cl cr l⋘r) | node right x' l' r' | leaf | () | _
lemma-dropLast-complete (left x cl cr l⋘r) | node right x' (node perfect x'' leaf leaf) leaf | node perfect x''' leaf leaf | x⋘ .x' .x'' .x''' | inj₁ ()
lemma-dropLast-complete (left x cl cr l⋘r) | node right x' (node perfect x'' leaf leaf) leaf | node perfect x''' leaf leaf | x⋘ .x' .x'' .x''' | inj₂ x'≃x''' =
           perfect x (perfect x' leaf leaf ≃lf) cr x'≃x'''
lemma-dropLast-complete (left x cl cr l⋘r) | node right x' l' r' | node perfect x'' l'' r'' | r⋘ .x' .x'' l⋙r l''≃r'' l'⋗l'' | inj₁ ld⋘r
    with dropLast (node right x' l' r') | ld⋘r | lemma-dropLast-complete cl
... | leaf | () | _
... | node perfect _ _ _ | () | _
... | node left x''' l''' r''' | ld⋘r' | cld = left x cld cr ld⋘r'
... | node right x''' l''' r''' | ld⋘r' | cld = left x cld cr ld⋘r'
lemma-dropLast-complete (left x cl cr l⋘r) | node right x' l' r' | node perfect x'' l'' r'' | r⋘ .x' .x'' l⋙r l''≃r'' l'⋗l'' | inj₂ ld≃r
    with dropLast (node right x' l' r') | ld≃r | lemma-dropLast-complete cl
... | leaf | () | _
... | node perfect x''' l''' r''' | ld≃r' | cld = perfect x cld cr ld≃r'
... | node left _ _ _ | () | _
... | node right _ _ _ | () | _
lemma-dropLast-complete (left x cl cr l⋘r) | node right x' l' r' | node left x'' l'' r'' | () | _
lemma-dropLast-complete (left x cl cr l⋘r) | node right x' l' r' | node right x'' l'' r'' | () | _
lemma-dropLast-complete (right {l} {r} x cl cr l⋙r)
    with l | r | l⋙r
... | leaf | _ | ⋙p ()
... | node perfect x' leaf leaf | leaf | ⋙p (⋗lf .x') = perfect x leaf leaf ≃lf
... | node perfect x' leaf leaf | node left _ _ _ | ⋙p ()
... | node perfect x' leaf leaf | node right _ _ _ | ⋙p ()
... | node perfect x' l' r' | node perfect x'' l'' r'' | ⋙p (⋗nd .x' .x'' l'≃r' l''≃r'' l'⋗l'') = left x (lemma-dropLast-complete cl) cr (lemma-dropLast-⋗ (⋗nd x' x'' l'≃r' l''≃r'' l'⋗l'') compound)
... | node perfect x' l' r' | node left x'' l'' r'' | ⋙l .x' .x'' l'≃r' l''⋘r'' l'⋗r''
    with lemma-dropLast-⋙ (⋙l x' x'' l'≃r' l''⋘r'' l'⋗r'')
... | inj₁ l⋙rd = right x cl (lemma-dropLast-complete  cr) l⋙rd
... | inj₂ ()
lemma-dropLast-complete (right x cl cr l⋙r) | node perfect x' l' r' | node right x'' l'' r'' | ⋙r .x' .x'' l'≃r' l''⋙r'' l'≃l''
    with lemma-dropLast-⋙ (⋙r x' x'' l'≃r' l''⋙r'' l'≃l'')
... | inj₁ l⋙rd = right x cl (lemma-dropLast-complete cr) l⋙rd
... | inj₂ ()
lemma-dropLast-complete (right x cl cr l⋙r) | node left _ _ _ | _ | ⋙p ()
lemma-dropLast-complete (right x cl cr l⋙r) | node right _ _ _ | _ | ⋙p ()