------------------------------------------------------------------------
-- Laws related to _>>=_
------------------------------------------------------------------------

module TotalParserCombinators.Laws.Monad where

open import Algebra
open import Category.Monad
open import Codata.Musical.Notation
open import Data.List
open import Data.List.Categorical
  using () renaming (module MonadProperties to ListMonad)
import Data.List.Relation.Binary.BagAndSetEquality as BSEq
open import Function
open import Level

private
  module BagMonoid {k} {A : Set} =
    CommutativeMonoid (BSEq.commutativeMonoid k A)
  open RawMonad {f = zero} Data.List.Categorical.monad
    using () renaming (_>>=_ to _>>=′_)

open import TotalParserCombinators.Derivative
open import TotalParserCombinators.Congruence as Eq
  hiding (return; fail) renaming (_∣_ to _∣′_)
import TotalParserCombinators.Laws.AdditiveMonoid as AdditiveMonoid
open import TotalParserCombinators.Laws.Derivative as Derivative
open import TotalParserCombinators.Laws.ReturnStar as Return⋆
open import TotalParserCombinators.Lib
open import TotalParserCombinators.Parser

------------------------------------------------------------------------
-- _>>=_, return, _∣_ and fail form a monad with a zero and a plus

-- If the laws below are combined with the additive monoid laws this
-- means that we have something which resembles an idempotent semiring
-- (if we restrict ourselves to language equivalence).

-- The zero laws are proved elsewhere.

open Derivative public
  using () renaming (left-zero->>=  to left-zero;
                     right-zero->>= to right-zero)

left-distributive : ∀ {Tok R₁ R₂ xs} {f g : R₁ → List R₂}
                    (p₁ : Parser Tok R₁ xs)
                    (p₂ : (x : R₁) → Parser Tok R₂ (f x))
                    (p₃ : (x : R₁) → Parser Tok R₂ (g x)) →
                    p₁ >>= (λ x → p₂ x ∣ p₃ x) ≅P p₁ >>= p₂ ∣ p₁ >>= p₃
left-distributive {xs = xs} {f} {g} p₁ p₂ p₃ =
  BSEq.>>=-left-distributive xs {f = f} ∷ λ t → ♯ (
    D t (p₁ >>= (λ x → p₂ x ∣ p₃ x))                       ≅⟨ D->>= p₁ (λ x → p₂ x ∣ p₃ x) ⟩

    D t p₁ >>= (λ x → p₂ x ∣ p₃ x) ∣
    return⋆ xs >>= (λ x → D t (p₂ x) ∣ D t (p₃ x))         ≅⟨ left-distributive (D t p₁) p₂ p₃ ∣′
                                                              left-distributive (return⋆ xs)
                                                                (λ x → D t (p₂ x)) (λ x → D t (p₃ x)) ⟩
    (D t p₁ >>= p₂ ∣ D t p₁ >>= p₃) ∣
    (return⋆ xs >>= (λ x → D t (p₂ x)) ∣
     return⋆ xs >>= (λ x → D t (p₃ x)))                    ≅⟨ AdditiveMonoid.swap
                                                               (D t p₁ >>= p₂) (D t p₁ >>= p₃)
                                                               (return⋆ xs >>= (λ x → D t (p₂ x)))
                                                               (return⋆ xs >>= (λ x → D t (p₃ x))) ⟩
    (D t p₁ >>= p₂ ∣ return⋆ xs >>= (λ x → D t (p₂ x))) ∣
    (D t p₁ >>= p₃ ∣ return⋆ xs >>= (λ x → D t (p₃ x)))    ≅⟨ sym (D->>= p₁ p₂ ∣′ D->>= p₁ p₃) ⟩

    D t (p₁ >>= p₂) ∣ D t (p₁ >>= p₃)                      ∎)

right-distributive : ∀ {Tok R₁ R₂ xs₁ xs₂} {f : R₁ → List R₂}
                     (p₁ : Parser Tok R₁ xs₁)
                     (p₂ : Parser Tok R₁ xs₂)
                     (p₃ : (x : R₁) → Parser Tok R₂ (f x)) →
                     (p₁ ∣ p₂) >>= p₃ ≅P p₁ >>= p₃ ∣ p₂ >>= p₃
right-distributive {xs₁ = xs₁} {xs₂} {f} p₁ p₂ p₃ =
  BagMonoid.reflexive (ListMonad.right-distributive xs₁ xs₂ f) ∷ λ t → ♯ (
    D t ((p₁ ∣ p₂) >>= p₃)                                  ≅⟨ D->>= (p₁ ∣ p₂) p₃ ⟩

    (D t p₁ ∣ D t p₂) >>= p₃ ∣
    return⋆ (xs₁ ++ xs₂) >>= (λ x → D t (p₃ x))             ≅⟨ ((D t p₁ ∣ D t p₂) >>= p₃ ∎) ∣′
                                                               [ ○ - ○ - ○ - ○ ] Return⋆.distrib-∣ xs₁ xs₂ >>=
                                                                                (λ x → D t (p₃ x) ∎) ⟩
    (D t p₁ ∣ D t p₂) >>= p₃ ∣
    (return⋆ xs₁ ∣ return⋆ xs₂) >>= (λ x → D t (p₃ x))      ≅⟨ right-distributive (D t p₁) (D t p₂) p₃ ∣′
                                                               right-distributive (return⋆ xs₁) (return⋆ xs₂)
                                                                                (λ x → D t (p₃ x)) ⟩
    ((D t p₁ >>= p₃) ∣ (D t p₂ >>= p₃)) ∣
    (return⋆ xs₁ >>= (λ x → D t (p₃ x)) ∣
     return⋆ xs₂ >>= (λ x → D t (p₃ x)))                    ≅⟨ AdditiveMonoid.swap
                                                                 (D t p₁ >>= p₃) (D t p₂ >>= p₃)
                                                                 (return⋆ xs₁ >>= (λ x → D t (p₃ x)))
                                                                 (return⋆ xs₂ >>= (λ x → D t (p₃ x))) ⟩
    (D t p₁ >>= p₃ ∣ return⋆ xs₁ >>= (λ x → D t (p₃ x))) ∣
    (D t p₂ >>= p₃ ∣ return⋆ xs₂ >>= (λ x → D t (p₃ x)))    ≅⟨ sym (D->>= p₁ p₃ ∣′ D->>= p₂ p₃) ⟩

    D t (p₁ >>= p₃) ∣ D t (p₂ >>= p₃)                       ∎)

left-identity : ∀ {Tok R₁ R₂} {f : R₁ → List R₂}
                (x : R₁) (p : (x : R₁) → Parser Tok R₂ (f x)) →
                return x >>= p ≅P p x
left-identity {f = f} x p =
  BagMonoid.reflexive (ListMonad.left-identity x f) ∷ λ t → ♯ (
    D t (return x >>= p)                              ≅⟨ D->>= (return x) p ⟩
    fail >>= p ∣ return⋆ [ x ] >>= (λ x → D t (p x))  ≅⟨ left-zero p ∣′
                                                         [ ○ - ○ - ○ - ○ ] AdditiveMonoid.right-identity (return x) >>=
                                                                           (λ x → D t (p x) ∎) ⟩
    fail ∣ return x >>= (λ x → D t (p x))             ≅⟨ AdditiveMonoid.left-identity (return x >>= (λ x → D t (p x))) ⟩
    return x >>= (λ x → D t (p x))                    ≅⟨ left-identity x (λ x → D t (p x)) ⟩
    D t (p x)                                         ∎)

right-identity : ∀ {Tok R xs}
                 (p : Parser Tok R xs) → p >>= return ≅P p
right-identity {xs = xs} p =
  BagMonoid.reflexive (ListMonad.right-identity xs) ∷ λ t → ♯ (
    D t (p >>= return)                              ≅⟨ D->>= p return ⟩
    D t p >>= return ∣ return⋆ xs >>= (λ _ → fail)  ≅⟨ right-identity (D t p) ∣′ right-zero (return⋆ xs) ⟩
    D t p            ∣ fail                         ≅⟨ AdditiveMonoid.right-identity (D t p) ⟩
    D t p                                           ∎)

associative : ∀ {Tok R₁ R₂ R₃ xs}
                {f : R₁ → List R₂} {g : R₂ → List R₃}
              (p₁ : Parser Tok R₁ xs)
              (p₂ : (x : R₁) → Parser Tok R₂ (f x))
              (p₃ : (x : R₂) → Parser Tok R₃ (g x)) →
              p₁ >>= (λ x → p₂ x >>= p₃) ≅P p₁ >>= p₂ >>= p₃
associative {xs = xs} {f} {g} p₁ p₂ p₃ =
  BagMonoid.reflexive (ListMonad.associative xs f g) ∷ λ t → ♯ (
    D t (p₁ >>= λ x → p₂ x >>= p₃)                               ≅⟨ D->>= p₁ (λ x → p₂ x >>= p₃) ⟩

    D t p₁ >>= (λ x → p₂ x >>= p₃) ∣
    return⋆ xs >>= (λ x → D t (p₂ x >>= p₃))                     ≅⟨ associative (D t p₁) p₂ p₃ ∣′
                                                                    [ ○ - ○ - ○ - ○ ] return⋆ xs ∎ >>= (λ x → D->>= (p₂ x) p₃) ⟩
    D t p₁ >>= p₂ >>= p₃ ∣
    return⋆ xs >>=
      (λ x → D t (p₂ x) >>= p₃ ∣
             return⋆ (f x) >>= λ x → D t (p₃ x))                 ≅⟨ (D t p₁ >>= p₂ >>= p₃ ∎) ∣′
                                                                    left-distributive (return⋆ xs)
                                                                                (λ x → D t (p₂ x) >>= p₃)
                                                                                (λ x → return⋆ (f x) >>= (λ x → D t (p₃ x))) ⟩
    D t p₁ >>= p₂ >>= p₃ ∣
    (return⋆ xs >>= (λ x → D t (p₂ x) >>= p₃) ∣
     return⋆ xs >>= (λ x → return⋆ (f x) >>= λ x → D t (p₃ x)))  ≅⟨ (D t p₁ >>= p₂ >>= p₃ ∎) ∣′
                                                                    (associative (return⋆ xs) (λ x → D t (p₂ x)) p₃ ∣′
                                                                     associative (return⋆ xs) (return⋆ ∘ f) (λ x → D t (p₃ x))) ⟩
    D t p₁ >>= p₂ >>= p₃ ∣
    (return⋆ xs >>= (λ x → D t (p₂ x)) >>= p₃ ∣
     return⋆ xs >>= (return⋆ ∘ f) >>= λ x → D t (p₃ x))          ≅⟨ sym $ AdditiveMonoid.associative
                                                                            (D t p₁ >>= p₂ >>= p₃)
                                                                            (return⋆ xs >>= (λ x → D t (p₂ x)) >>= p₃)
                                                                            (return⋆ xs >>= (return⋆ ∘ f) >>= (λ x → D t (p₃ x))) ⟩
    D t p₁ >>= p₂ >>= p₃ ∣
    return⋆ xs >>= (λ x → D t (p₂ x)) >>= p₃ ∣
    return⋆ xs >>= (return⋆ ∘ f) >>= (λ x → D t (p₃ x))          ≅⟨ sym (right-distributive
                                                                           (D t p₁ >>= p₂)
                                                                           (return⋆ xs >>= (λ x → D t (p₂ x)))
                                                                           p₃) ∣′
                                                                    [ ○ - ○ - ○ - ○ ] sym (Return⋆.distrib->>= xs f) >>=
                                                                                (λ x → D t (p₃ x) ∎) ⟩
    (D t p₁ >>= p₂ ∣
     return⋆ xs >>= (λ x → D t (p₂ x))) >>= p₃ ∣
    return⋆ (xs >>=′ f) >>= (λ x → D t (p₃ x))                   ≅⟨ [ ○ - ○ - ○ - ○ ] sym (D->>= p₁ p₂) >>= (λ x → p₃ x ∎) ∣′
                                                                    (return⋆ (xs >>=′ f) >>= (λ x → D t (p₃ x)) ∎) ⟩
    D t (p₁ >>= p₂) >>= p₃ ∣
    return⋆ (xs >>=′ f) >>= (λ x → D t (p₃ x))                   ≅⟨ sym $ D->>= (p₁ >>= p₂) p₃ ⟩

    D t (p₁ >>= p₂ >>= p₃)                                       ∎)


------------------------------------------------------------------------
-- The rec construction can be encoded using λ-terms
------------------------------------------------------------------------

module Recursion-without-rec where

open import Equality.Propositional.Cubical
open import Prelude hiding (id; swap)

import Finite-subset.Listed equality-with-paths as S

-- To simplify the development, let's work with actual natural numbers
-- as variables and constants (see
-- Atom.one-can-restrict-attention-to-χ-ℕ-atoms).

open import Atom

open import Alpha-equivalence χ-ℕ-atoms
open import Chi               χ-ℕ-atoms
open import Compatibility     χ-ℕ-atoms
open import Constants         χ-ℕ-atoms
open import Free-variables    χ-ℕ-atoms
open import Reasoning         χ-ℕ-atoms
open import Substitution      χ-ℕ-atoms
open import Values            χ-ℕ-atoms

open χ-atoms χ-ℕ-atoms

open import Combinators using (id; id-closed)

private
  variable
    A           : Type
    R           : A → A → Type
    x y z z₁ z₂ : Var
    e e′ v      : Exp

------------------------------------------------------------------------
-- "Plain" lambda terms

-- A predicate that holds for plain lambda terms, i.e. terms that are
-- built up using only var, lambda and apply.

Plain : Exp → Type
Plain (var _)       = ⊤
Plain (lambda _ e)  = Plain e
Plain (apply e₁ e₂) = Plain e₁ × Plain e₂
Plain (case _ _)    = ⊥
Plain (rec _ _)     = ⊥
Plain (const _ _)   = ⊥

-- Plain is preserved by substitutions.

plain-subst : ∀ e → Plain e → Plain e′ → Plain (e [ x ← e′ ])
plain-subst (apply e₁ e₂) (e₁-ok , e₂-ok) e′-ok =
  plain-subst e₁ e₁-ok e′-ok , plain-subst e₂ e₂-ok e′-ok
plain-subst {x = x} (lambda y e) e-ok e′-ok with x V.≟ y
… | yes _ = e-ok
… | no _  = plain-subst e e-ok e′-ok
plain-subst {x = x} (var y) _ e′-ok with x V.≟ y
… | yes _ = e′-ok
… | no _  = _

------------------------------------------------------------------------
-- A variant of a fixpoint combinator

-- A variant of the call-by-value fixpoint combinator Θᵥ that (at the
-- time of writing) is presented on the Wikipedia page about
-- fixed-point combinators
-- (https://en.wikipedia.org/wiki/Fixed-point_combinator). There Θᵥ is
-- defined to be the application of λxy.y(λz.xxyz) to itself. I have
-- dropped the final z, and made the choice of variable name for z
-- customisable.

mutual

  F : Var → Exp
  F z = apply (f z) (f z)

  f : Var → Exp
  f z =
    lambda v-x (lambda v-y (
      apply (var v-y) (
        lambda z (
          apply (apply (var v-x) (var v-x)) (var v-y)))))

-- The expressions f z, F z and id are closed.

f-closed : Closed (f z)
f-closed =
  Closed′-closed-under-lambda $
  Closed′-closed-under-lambda $
  Closed′-closed-under-apply
    (from-⊎ (closed′? (var v-y) (v-y ∷ v-x ∷ [])))
    (Closed′-closed-under-lambda $
     Closed′-closed-under-apply
       (Closed′-closed-under-apply
          (Closed′-closed-under-var (inj₂ (inj₂ (inj₁ refl))))
          (Closed′-closed-under-var (inj₂ (inj₂ (inj₁ refl)))))
       (Closed′-closed-under-var (inj₂ (inj₁ refl))))

F-closed : Closed (F z)
F-closed = Closed′-closed-under-apply f-closed f-closed

-- F z₁ and f z₁ are α-equivalent to F z₂ and f z₂, respectively,
-- assuming that z₁ and z₂ are distinct from v-x and v-y.

f≈αf :
  z₁ ≢ v-x → z₁ ≢ v-y → z₂ ≢ v-x → z₂ ≢ v-y →
  Alpha R (f z₁) (f z₂)
f≈αf {z₁ = z₁} {z₂ = z₂} {R = R} z₁≢x z₁≢y z₂≢x z₂≢y =
  lambda (
  lambda (
  apply (var y∼y₁) (
  lambda (
  apply (
  apply (var x∼x) (var x∼x)) (
  var y∼y₂)))))
  where
  x∼x : (R [ v-x ∼ v-x ] [ v-y ∼ v-y ] [ z₁ ∼ z₂ ]) v-x v-x
  x∼x = inj₂ (z₁≢x , z₂≢x , inj₂ ((λ ()) , (λ ()) , inj₁ (refl , refl)))

  y∼y₁ : (R [ v-x ∼ v-x ] [ v-y ∼ v-y ]) v-y v-y
  y∼y₁ = inj₁ (refl , refl)

  y∼y₂ : (R [ v-x ∼ v-x ] [ v-y ∼ v-y ] [ z₁ ∼ z₂ ]) v-y v-y
  y∼y₂ = inj₂ (z₁≢y , z₂≢y , y∼y₁)

F≈αF :
  z₁ ≢ v-x → z₁ ≢ v-y → z₂ ≢ v-x → z₂ ≢ v-y →
  Alpha R (F z₁) (F z₂)
F≈αF z₁≢x z₁≢y z₂≢x z₂≢y =
  apply (f≈αf z₁≢x z₁≢y z₂≢x z₂≢y) (f≈αf z₁≢x z₁≢y z₂≢x z₂≢y)

------------------------------------------------------------------------
-- A plain alternative to rec

-- An expression former that has the same semantics as rec, but that
-- takes plain expressions to plain expressions.
--
-- Note that substitution does not necessarily behave in the same way
-- for plain-rec as for rec (see below).

plain-rec : Var → Exp → Exp
plain-rec x e =
  let z , _ = fresh′ (S.from-List (v-x ∷ v-y ∷ [])) e in
  apply (lambda z (apply (F z) (lambda x (e [ x ← apply (var x) id ]))))
    id

-- If e is a plain lambda term, then plain-rec x e is a plain
-- lambda term.

plain-rec-plain : ∀ e → Plain e → Plain (plain-rec x e)
plain-rec-plain e ok = (_ , plain-subst e ok _) , _

-- The semantic rule given for rec is admissible for plain-rec.

plain-rec-⇓ :
  ∀ e → e [ x ← plain-rec x e ] ⇓ v → plain-rec x e ⇓ v
plain-rec-⇓ {x = x} {v = v} e ⇓v =
  plain-rec x e                                                         ⟶⟨⟩

  apply (lambda z′
           (apply (F z′) (lambda x (e [ x ← apply (var x) id ]))))
    id                                                                  ⟶⟨ apply lambda lambda ⟩

  apply (F z′) (lambda x (e [ x ← apply (var x) id ])) [ z′ ← id ]      ≡⟨ subst-∉ z′ _ z∉apply ⟩⟶

  apply (F z′) (lambda x (e [ x ← apply (var x) id ]))                  ⟶⟨ []⇓ (apply← ∙) F⇓ ⟩

  apply (lambda v-y
           (apply (var v-y) (lambda z′ (apply (F z′) (var v-y)))))
    (lambda x (e [ x ← apply (var x) id ]))                             ⟶⟨ apply lambda lambda ⟩

  apply (var v-y) (lambda z′ (apply (F z′) (var v-y)))
    [ v-y ← lambda x (e [ x ← apply (var x) id ]) ]                     ≡⟨ cong (apply _) (lambda-step-≢ y≢z) ⟩⟶

  apply (lambda x (e [ x ← apply (var x) id ]))
    (lambda z′ (apply (F z′) (lambda x (e [ x ← apply (var x) id ]))))  ⟶⟨ apply lambda lambda ⟩

  e [ x ← apply (var x) id ]
    [ x ← lambda z′
            (apply (F z′) (lambda x (e [ x ← apply (var x) id ]))) ]    ≡⟨ fusion e ⟩⟶

  e [ x ← apply (var x) id
            [ x ← lambda z′
                    (apply (F z′)
                       (lambda x (e [ x ← apply (var x) id ]))) ] ]     ≡⟨ cong₂ (λ e₁ e₂ → e [ x ← apply e₁ e₂ ])
                                                                             (var-step-≡ (refl {x = x}))
                                                                             (subst-closed x _ id-closed) ⟩⟶

  e [ x ← plain-rec x e ]                                               ⇓⟨ ⇓v ⟩■

  v
  where
  z,f = fresh′ (S.from-List (v-x ∷ v-y ∷ [])) e

  z′ : Var
  z′ = proj₁ z,f

  z∉e : ¬ z′ ∈FV e
  z∉e = proj₁ (proj₂ z,f)

  x≢z : v-x ≢ z′
  x≢z x≡z = proj₂ (proj₂ z,f) (S.≡→∈∷ (sym x≡z))

  y≢z : v-y ≢ z′
  y≢z y≡z = proj₂ (proj₂ z,f) (S.∈→∈∷ (S.≡→∈∷ (sym y≡z)))

  F⇓ :
    F z′ ⇓
    lambda v-y (apply (var v-y) (lambda z′ (apply (F z′) (var v-y))))
  F⇓ =
    apply lambda lambda
      (lambda v-y
         (apply (var v-y)
            (lambda z′ (apply (apply (var v-x) (var v-x)) (var v-y))))
         [ v-x ← f z′ ]                                                   ⟶⟨⟩

       lambda v-y
         (apply (var v-y)
            (lambda z′ (apply (apply (var v-x) (var v-x)) (var v-y))
               [ v-x ← f z′ ]))                                           ≡⟨ cong (lambda _) $ cong (apply _) $
                                                                             lambda-step-≢ x≢z ⟩⟶

       lambda v-y (apply (var v-y) (lambda z′ (apply (F z′) (var v-y))))  ■⟨ lambda _ _ ⟩)

  z∉apply :
    ¬ z′ ∈FV apply (F z′) (lambda x (e [ x ← apply (var x) id ]))
  z∉apply (apply-left z∈Fz) =
    F-closed z′ (λ ()) z∈Fz
  z∉apply (apply-right (lambda z≢x z∈)) with subst-∈FV x e z∈
  … | inj₁ (z∈e , _)              = z∉e z∈e
  … | inj₂ (apply-left (var z≡x)) = z≢x z≡x
  … | inj₂ (apply-right z∈id)     = id-closed z′ (λ ()) z∈id

-- Substitution of closed expressions is not in general defined in the
-- same way for plain-rec as for rec.

¬-plain-rec-subst :
  ¬ (∀ y e x e′ →
     Closed e′ →
     plain-rec y e [ x ← e′ ] ≡
     plain-rec y (if x V.≟ y then e else e [ x ← e′ ]))
¬-plain-rec-subst plain-rec-subst =
  not-equal (plain-rec-subst y′ e₁ x′ e₂ id-closed)
  where
  y′ = v-y
  x′ = v-z
  e₂ = id
  e₁ = var x′

  not-equal :
    plain-rec y′ e₁ [ x′ ← e₂ ] ≢
    plain-rec y′ (if x′ V.≟ y′ then e₁ else e₁ [ x′ ← e₂ ])
  not-equal ()

-- However, it is defined in the same way /up to α-equivalence/.

plain-rec-subst :
  ∀ x →
  Closed e′ →
  plain-rec y e [ x ← e′ ] ≈α
  plain-rec y (if x V.≟ y then e else e [ x ← e′ ])
plain-rec-subst {e′ = e′} {y = y} {e = e} x cl-e′ =
  apply (lambda z¹
           (apply (F z¹) (lambda y (e [ y ← apply (var y) id ]))))
    id [ x ← e′ ]                                                   ≡⟨ cong (apply _) $
                                                                       subst-closed x _ id-closed ⟩α
  apply (lambda z¹
           (apply (F z¹) (lambda y (e [ y ← apply (var y) id ])))
           [ x ← e′ ])
    id                                                              ≡⟨ cong (λ e → apply (lambda z¹ e) id) $
                                                                       lemma₁ (x V.≟ z¹) (x V.≟ y) ⟩α
  apply (lambda z¹
           (apply (F z¹)
              (lambda y ((if x V.≟ y then e else e [ x ← e′ ])
                           [ y ← apply (var y) id ]))))
    id                                                              ≈⟨ apply (lambda (apply (F≈αF z¹≢x z¹≢y z²≢x z²≢y)
                                                                                (refl-Alpha _ lemma₂)))
                                                                         refl-α ⟩α∎
  apply (lambda z²
           (apply (F z²)
              (lambda y ((if x V.≟ y then e else e [ x ← e′ ])
                           [ y ← apply (var y) id ]))))
    id                                                              ∎
  where
  z¹,f₁ = fresh′ (S.from-List (v-x ∷ v-y ∷ [])) e

  z¹ : Var
  z¹ = proj₁ z¹,f₁

  z¹∉e : ¬ z¹ ∈FV e
  z¹∉e = proj₁ (proj₂ z¹,f₁)

  z¹≢x : z¹ ≢ v-x
  z¹≢x z¹≡x = proj₂ (proj₂ z¹,f₁) (S.≡→∈∷ z¹≡x)

  z¹≢y : z¹ ≢ v-y
  z¹≢y z¹≡y = proj₂ (proj₂ z¹,f₁) (S.∈→∈∷ (S.≡→∈∷ z¹≡y))

  z²,f₂ =
    fresh′ (S.from-List (v-x ∷ v-y ∷ []))
      (if x V.≟ y then e else e [ x ← e′ ])

  z² : Var
  z² = proj₁ z²,f₂

  z²∉ : ¬ z² ∈FV if x V.≟ y then e else e [ x ← e′ ]
  z²∉ = proj₁ (proj₂ z²,f₂)

  z²≢x : z² ≢ v-x
  z²≢x z²≡x = proj₂ (proj₂ z²,f₂) (S.≡→∈∷ z²≡x)

  z²≢y : z² ≢ v-y
  z²≢y z²≡y = proj₂ (proj₂ z²,f₂) (S.∈→∈∷ (S.≡→∈∷ z²≡y))

  x∉y-id : x ≢ y → ¬ x ∈FV apply (var y) id
  x∉y-id x≢y (apply-left (var x≡y)) = x≢y x≡y
  x∉y-id _   (apply-right x∈id)     = id-closed x (λ ()) x∈id

  lemma₁ :
    (x≟z¹ : Dec (x ≡ z¹)) (x≟y : Dec (x ≡ y)) →
    if x≟z¹
    then apply (F z¹) (lambda y (e [ y ← apply (var y) id ]))
    else apply (F z¹) (lambda y (e [ y ← apply (var y) id ]))
           [ x ← e′ ] ≡
    apply (F z¹)
      (lambda y ((if x≟y then e else e [ x ← e′ ])
                   [ y ← apply (var y) id ]))
  lemma₁ (yes _) (yes _) = refl

  lemma₁ (yes x≡z¹) (no _) =
    apply (F z¹) (lambda y (e [ y ← apply (var y) id ]))             ≡⟨ cong (apply _) $ cong (lambda _) $ cong (_[ _ ← _ ]) $ sym $
                                                                        subst-∉ x e (z¹∉e ∘ subst (_∈FV _) x≡z¹) ⟩∎
    apply (F z¹) (lambda y (e [ x ← e′ ] [ y ← apply (var y) id ]))  ∎

  lemma₁ (no _) (yes x≡y) =
    apply (F z¹ [ x ← e′ ])
      (lambda y (e [ y ← apply (var y) id ]) [ x ← e′ ])             ≡⟨ cong (λ e″ → apply e″ (lambda y (e [ y ← apply (var y) id ])
                                                                                [ x ← e′ ])) $
                                                                        subst-closed x e′ F-closed ⟩

    apply (F z¹) (lambda y (e [ y ← apply (var y) id ]) [ x ← e′ ])  ≡⟨ cong (apply (F z¹)) $
                                                                        lambda-step-≡ x≡y ⟩∎
    apply (F z¹) (lambda y (e [ y ← apply (var y) id ]))             ∎

  lemma₁ (no _) (no x≢y) =
    apply (F z¹ [ x ← e′ ])
      (lambda y (e [ y ← apply (var y) id ]) [ x ← e′ ])             ≡⟨ cong (λ e″ → apply e″ (lambda y (e [ y ← apply (var y) id ])
                                                                                [ x ← e′ ])) $
                                                                        subst-closed x e′ F-closed ⟩

    apply (F z¹) (lambda y (e [ y ← apply (var y) id ]) [ x ← e′ ])  ≡⟨ cong (apply (F z¹)) $
                                                                        lambda-step-≢ x≢y ⟩

    apply (F z¹) (lambda y (e [ y ← apply (var y) id ] [ x ← e′ ]))  ≡⟨ cong (apply _) $ cong (lambda _) $ sym $
                                                                        swap x≢y (x∉y-id x≢y) (cl-e′ _ (λ ())) e ⟩∎
    apply (F z¹) (lambda y (e [ x ← e′ ] [ y ← apply (var y) id ]))  ∎

  lemma₂ :
    ∀ z →
    z ∈FV lambda y ((if x V.≟ y then e else e [ x ← e′ ])
                      [ y ← apply (var y) id ]) →
    (_≡_ [ z¹ ∼ z² ]) z z
  lemma₂ z (lambda z≢y z∈) =
    case subst-∈FV y _ z∈ of λ where
      (inj₁ (z∈ , _)) →
        inj₂ ( z¹≢ (x V.≟ y) z∈
             , z²∉ ∘ flip (subst (_∈FV _)) z∈ ∘ sym
             , refl
             )

      (inj₂ (apply-left (var z≡y))) →
        ⊥-elim $ z≢y z≡y

      (inj₂ (apply-right ∈id)) →
        ⊥-elim $ id-closed _ (λ ()) ∈id
    where
    z¹≢ :
      ∀ x≟y →
      z ∈FV if x≟y then e else e [ x ← e′ ] →
      z¹ ≢ z
    z¹≢ (yes _) z∈ = z¹∉e ∘ flip (subst (_∈FV _)) z∈ ∘ sym
    z¹≢ (no _)  z∈ = case subst-∈FV x e z∈ of λ where
      (inj₁ (z∈ , _)) →
        z¹∉e ∘ flip (subst (_∈FV _)) z∈ ∘ sym
      (inj₂ z∈e′) →
        ⊥-elim $ cl-e′ _ (λ ()) z∈e′


-- Andreas, 2017-08-25, issue #1611.
-- Fixed by Jesper Cockx as #2621.

open import Common.Prelude

data D : Bool → Set where
  dt : D true
  df : D false

Works : ∀{b} → D b → Set
Works dt = Bool
Works df = Bool

Fails : ∀{b : Bool} → D _ → Set
Fails dt = Bool
Fails df = Bool

-- WAS:
-- false != true of type Bool
-- when checking that the pattern df has type D true

-- SHOULD BE:
-- Don't know whether to split on dt

-- NOW:
-- I'm not sure if there should be a case for the constructor dt,
-- because I get stuck when trying to solve the following unification
-- problems (inferred index ≟ expected index):
--   true ≟ _9
-- when checking that the pattern dt has type D _9


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

open import HoTT

module homotopy.PushoutSplit where

--        g     h
--     D --> B --> C    K = A ⊔^D B / (f,g)       d₁ = A <- D -> B
--    f|     |     |    L = K ⊔^B C / (right,h)   d₂ = K <- B -> C
--     v     v     v                              d  = A <- D -> C
--     A --> K --> L
--
module PushoutRSplit {i j k l} {A : Type i} {B : Type j} {C : Type k}
  {D : Type l} (f : D → A) (g : D → B) (h : B → C) where

  private
    d₁ : Span
    d₁ = span A B D f g

    d₂ : Span
    d₂ = span (Pushout d₁) C B right h

    d : Span
    d = span A C D f (h ∘ g)

  split-span-map : SpanMap d d₂
  split-span-map = span-map left (idf C) g (comm-sqr λ a → glue a) (comm-sqr λ _ → idp)

  module Split = PushoutFmap split-span-map

  split : Pushout d → Pushout d₂
  split = Split.f

  inner-span-map : SpanMap d₁ d
  inner-span-map = span-map (idf A) h (idf D) (comm-sqr λ _ → idp) (comm-sqr λ _ → idp)

  module Inner = PushoutFmap inner-span-map

  inner : Pushout d₁ → Pushout d
  inner = Inner.f

  module Merge = PushoutRec {d = d₂} {D = Pushout d}
    inner right (λ _ → idp)

  merge : Pushout d₂ → Pushout d
  merge = Merge.f

  private
    square-extend-tr : ∀ {i} {A : Type i} {a₀₀ a₀₁ a₁₀ a₁₁ b : A}
      {p₀₋ : a₀₀ == a₀₁} {p₋₀ : a₀₀ == a₁₀}
      {p₋₁ : a₀₁ == a₁₁} {p₁₋ : a₁₀ == a₁₁} (q : a₁₀ == b)
      → Square p₀₋ p₋₀ p₋₁ p₁₋
      → Square p₀₋ (p₋₀ ∙ q) p₋₁ (! q ∙' p₁₋)
    square-extend-tr idp ids = ids

  split-inner : (k : Pushout d₁) → split (inner k) == left k
  split-inner = Pushout-elim
    (λ a → idp)
    (λ b → ! (glue b))
    (λ d → ↓-='-from-square $
      (ap-∘ split inner (glue d)
       ∙ ap (ap split) (Inner.glue-β d) ∙ Split.glue-β d)
      ∙v⊡ square-extend-tr (glue (g d)) vid-square)

  abstract
    split-merge : (l : Pushout d₂) → split (merge l) == l
    split-merge = Pushout-elim
      split-inner
      (λ c → idp)
      (λ b → ↓-∘=idf-from-square split merge $
        ap (ap split) (Merge.glue-β b) ∙v⊡ bl-square (glue b))

    merge-split : (l : Pushout d) → merge (split l) == l
    merge-split = Pushout-elim
      (λ a → idp)
      (λ c → idp)
      (λ d → ↓-∘=idf-in' merge split $
        ap merge (ap split (glue d))
          =⟨ ap (ap merge) (Split.glue-β d) ⟩
        ap merge (ap left (glue d) ∙ glue (g d))
          =⟨ ap-∙ merge (ap left (glue d)) (glue (g d)) ⟩
        ap merge (ap left (glue d)) ∙ ap merge (glue (g d))
          =⟨ ap2 _∙_ (∘-ap merge left (glue d)) (Merge.glue-β (g d)) ⟩
        ap inner (glue d) ∙ idp
          =⟨ ∙-unit-r _ ⟩
        ap inner (glue d)
          =⟨ Inner.glue-β d ⟩
        glue d ∎)

  split-equiv : Pushout d ≃ Pushout d₂
  split-equiv = equiv split merge split-merge merge-split

{-
  two-pushouts-left : lift ∘ left == left ∘ left
                      [ (λ E → (A → E)) ↓ two-pushouts ]
  two-pushouts-left = codomain-over-equiv _ _

  two-pushouts-right : lift ∘ right == right [ (λ E → (D → E)) ↓ two-pushouts ]
  two-pushouts-right = codomain-over-equiv _ _

  two-pushouts-inner : lift ∘ inner == left
                       [ (λ E → (Pushout d₁ → E)) ↓ two-pushouts ]
  two-pushouts-inner = codomain-over-equiv _ _ ▹ λ= split-inner
-}

rsplit-equiv = PushoutRSplit.split-equiv

--        h
--     D --> C    K = A ⊔^D C / (f,h)       d₁ = A <- D -> C
--    f|     |    L = B ⊔^A K / (g,left)    d₂ = B <- A -> K
--     v     v                              d  = B <- D -> C
--     A --> K
--    g|     |
--     v     v
--     B --> L

module PushoutLSplit {i j k l} {A : Type i} {B : Type j} {C : Type k}
  {D : Type l} (f : D → A) (g : A → B) (h : D → C) where

  private
    d₁ : Span
    d₁ = span A C D f h

    d₂ : Span
    d₂ = span B (Pushout d₁) A g left

    d : Span
    d = span B C D (g ∘ f) h

  split-span-map : SpanMap d d₂
  split-span-map = span-map (idf B) right f (comm-sqr λ _ → idp) (comm-sqr λ d → ! (glue d))

  module Split = PushoutFmap split-span-map

  split : Pushout d → Pushout d₂
  split = Split.f

  inner-span-map : SpanMap d₁ d
  inner-span-map = span-map g (idf C) (idf D) (comm-sqr λ _ → idp) (comm-sqr λ _ → idp)

  module Inner = PushoutFmap inner-span-map

  inner : Pushout d₁ → Pushout d
  inner = Inner.f

  module Merge = PushoutRec {d = d₂} {D = Pushout d}
    left inner (λ _ → idp)

  merge : Pushout d₂ → Pushout d
  merge = Merge.f

  private
    square-extend-tl : ∀ {i} {A : Type i} {a₀₀ a₀₁ a₁₀ a₁₁ b : A}
      {p₀₋ : a₀₀ == a₀₁} {p₋₀ : a₀₀ == a₁₀}
      {p₋₁ : a₀₁ == a₁₁} {p₁₋ : a₁₀ == a₁₁} (q : b == a₀₀)
      → Square p₀₋ p₋₀ p₋₁ p₁₋
      → Square (q ∙' p₀₋) (q ∙' p₋₀) p₋₁ p₁₋
    square-extend-tl idp ids = ids

  split-inner : (k : Pushout d₁) → split (inner k) == right k
  split-inner = Pushout-elim
    (λ a → glue a)
    (λ c → idp)
    (λ d → ↓-='-from-square $
      (ap-∘ split inner (glue d)
       ∙ ap (ap split) (Inner.glue-β d) ∙ Split.glue-β d
       ∙ ap (λ p → glue (f d) ∙' ap right p) (!-! (glue d)))
      ∙v⊡ square-extend-tl (glue (f d)) vid-square)

  abstract
    split-merge : (l : Pushout d₂) → split (merge l) == l
    split-merge = Pushout-elim
      (λ b → idp)
      split-inner
      (λ a → ↓-∘=idf-from-square split merge $
        ap (ap split) (Merge.glue-β a) ∙v⊡ br-square (glue a))

    merge-split : (l : Pushout d) → merge (split l) == l
    merge-split = Pushout-elim
      (λ b → idp)
      (λ c → idp)
      (λ d → ↓-∘=idf-in' merge split $
        ap merge (ap split (glue d))
          =⟨ ap (ap merge) (Split.glue-β d) ⟩
        ap merge (glue (f d) ∙' ap right (! (! (glue d))))
          =⟨ ap-∙' merge (glue (f d)) (ap right (! (! (glue d)))) ⟩
        ap merge (glue (f d)) ∙' ap merge (ap right (! (! (glue d))))
          =⟨ ap2 _∙'_ (Merge.glue-β (f d)) (∘-ap merge right (! (! (glue d)))) ⟩
        idp ∙' ap inner (! (! (glue d)))
          =⟨ ∙'-unit-l _ ⟩
        ap inner (! (! (glue d)))
          =⟨ ap (ap inner) (!-! (glue d)) ⟩
        ap inner (glue d)
          =⟨ Inner.glue-β d ⟩
        glue d ∎)

  split-equiv : Pushout d ≃ Pushout d₂
  split-equiv = equiv split merge split-merge merge-split

lsplit-equiv = PushoutLSplit.split-equiv

{- TODO Update this part

--        g     h
--     Y --> Z --> W    K = X ⊔^Y Y / (f,g)        ps₁ = X <- Y -> Z
--    f|     |     |    L = Z ⊔^Z W / (left,h)     ps₂ = K <- Z -> W
--     v     v     v                               ps = X <- Y -> W
--     X --> K --> L
--
module TwoPushoutsPtd {i j k l} {X : Ptd i} {Y : Ptd j} {Z : Ptd k} {W : Ptd l}
  (f : Y ⊙→ X) (g : Y ⊙→ Z) (h : Z ⊙→ W) where

  private
    ps₁ = ⊙span X Z Y f g
    ps₂ = ⊙span (⊙Pushout ps₁) W Z (⊙right ps₁) h
    ps = ⊙span X W Y f (h ⊙∘ g)

  open TwoPushoutsEquiv (fst f) (fst g) (fst h)

  two-pushouts-ptd :
    ⊙Lift {j = lmax l (lmax k (lmax j i))} (⊙Pushout ps)
    == ⊙Pushout ps₂
  two-pushouts-ptd = ⊙ua (two-pushouts-equiv ∘e lift-equiv) idp

  two-pushouts-⊙left :
    ⊙lift ⊙∘ ⊙left ps == ⊙left ps₂ ⊙∘ ⊙left ps₁
    [ (λ V → X ⊙→ V) ↓ two-pushouts-ptd ]
  two-pushouts-⊙left = codomain-over-⊙equiv _ _ _

  two-pushouts-⊙right :
    ⊙lift ⊙∘ ⊙right ps == ⊙right ps₂
    [ (λ V → W ⊙→ V) ↓ two-pushouts-ptd ]
  two-pushouts-⊙right =
    codomain-over-⊙equiv _ _ _ ▹ pair= idp (lemma f g h)
    where
    lemma : {X : Ptd i} {Y : Ptd j} {Z : Ptd k} {W : Ptd l}
      (f : Y ⊙→ X) (g : Y ⊙→ Z) (h : Z ⊙→ W)
      → ap (TwoPushoutsEquiv.split (fst f) (fst g) (fst h)
               ∘ lower {j = lmax l (lmax k (lmax j i))})
              (snd (⊙lift ⊙∘ ⊙right (⊙span X W Y f (h ⊙∘ g))))
        ∙ idp
        ==  ap right (! (snd h)) ∙ ! (glue (pt Z))
            ∙' ap left (snd (⊙right (⊙span X Z Y f g)))
    lemma {Y = Y} (f , idp) (g , idp) (h , idp) =
      ap (2P.split ∘ lower) (ap lift (! (glue (pt Y))) ∙ idp) ∙ idp
        =⟨ ∙-unit-r _ ⟩
      ap (2P.split ∘ lower) (ap lift (! (glue (pt Y))) ∙ idp)
        =⟨ ∙-unit-r _ |in-ctx (ap (2P.split ∘ lower)) ⟩
      ap (2P.split ∘ lower) (ap lift (! (glue (pt Y))))
        =⟨ ∘-ap (2P.split ∘ lower) lift _ ⟩
      ap 2P.split (! (glue (pt Y)))
        =⟨ ap-! 2P.split (glue (pt Y)) ⟩
      ! (ap 2P.split (glue (pt Y)))
        =⟨ 2P.Split.glue-β (pt Y) |in-ctx ! ⟩
      ! (ap left (glue (pt Y)) ∙ glue (g (pt Y)))
        =⟨ !-∙ (ap left (glue (pt Y))) (glue (g (pt Y))) ⟩
      ! (glue (g (pt Y))) ∙ ! (ap left (glue (pt Y)))
        =⟨ !-ap left (glue (pt Y)) |in-ctx (λ w → ! (glue (g (pt Y))) ∙ w) ⟩
      ! (glue (g (pt Y))) ∙ ap left (! (glue (pt Y)))
        =⟨ ∙=∙' (! (glue (g (pt Y)))) (ap left (! (glue (pt Y)))) ⟩
      ! (glue (g (pt Y))) ∙' ap left (! (glue (pt Y))) ∎
      where
      module 2P = TwoPushoutsEquiv f g h

  two-pushouts-⊙inner : ⊙lift ⊙∘ (inner , idp) == ⊙left ps₂
    [ (λ V → ⊙Pushout ps₁ ⊙→ V) ↓ two-pushouts-ptd ]
  two-pushouts-⊙inner =
    codomain-over-⊙equiv _ _ _ ▹ ⊙λ= split-inner idp
-}

{-
open TwoPushoutsEquiv using () renaming (split-equiv to vsplit-pushouts-equiv)
  -- two-pushouts; two-pushouts-left; two-pushouts-right; two-pushouts-inner

open TwoPushoutsPtd
  using (two-pushouts-ptd; two-pushouts-⊙left; two-pushouts-⊙right;
         two-pushouts-⊙inner)
-}


-- Internal hom in families
module SOAS.Abstract.Hom {T : Set} where

open import SOAS.Common
open import SOAS.Construction.Structure
open import SOAS.Context
open import SOAS.Variable
open import SOAS.Families.Core {T}
open import SOAS.Families.Isomorphism
open import SOAS.Families.BCCC

open import SOAS.Construction.Skew.SkewClosed

open import Categories.Functor.Bifunctor
open import Categories.NaturalTransformation.Dinatural using (dtHelper)

-- Heterogeneous action of a sorted family on a family
⟨_,_⟩ : Familyₛ → Family → Family
⟨ 𝒳 , Y ⟩ Γ = (Γ ~[ 𝒳 ]↝_) ⇾ Y

⟨-,-⟩F : Bifunctor 𝔽amₛ.op 𝔽amilies 𝔽amilies
⟨-,-⟩F = record
  { F₀ = λ{ (𝒳 , Y) → ⟨ 𝒳 , Y ⟩ }
  ; F₁ = λ{ (f , g) o {Δ} σ → g (o (f ∘ σ))  }
  ; identity = refl
  ; homomorphism = refl
  ; F-resp-≈ = λ{ {f = f , g} (p , p′) {Γ} {o} → dext′ (trans (cong (g ∘ o) (dext′ p)) p′) }
  }

-- Arrow mapping
⟨_,_⟩₁ : {𝒳 𝒳′ : Familyₛ} {Y Y′ : Family} → 𝒳′ ⇾̣ 𝒳 → Y ⇾ Y′ → (⟨ 𝒳 , Y ⟩ ⇾ ⟨ 𝒳′ , Y′ ⟩)
⟨ f , g ⟩₁ = Functor.₁ ⟨-,-⟩F (f , g)

-- Internal hom of sorted families
〖_,_〗 : Familyₛ → Familyₛ → Familyₛ
〖 X , Y 〗 τ = ⟨ X , Y τ ⟩

〖-,-〗F  : Bifunctor 𝔽amₛ.op 𝔽amiliesₛ 𝔽amiliesₛ
〖-,-〗F = record
  { F₀ = λ{ (X , Y) → 〖 X , Y 〗 }
  ; F₁ = λ{ (f , g) o {Δ} σ → g (o (f ∘ σ))  }
  ; identity = refl
  ; homomorphism = refl
  ; F-resp-≈ = λ{ {f = f , g} (p , p′) {x = h} → dext′ (trans (cong (g ∘ h) (dext′ p)) p′) }
  }

-- Arrow mapping
〖_,_〗₁ : {𝒳 𝒳′ 𝒴 𝒴′ : Familyₛ} → 𝒳′ ⇾̣ 𝒳 → 𝒴 ⇾̣ 𝒴′ → (〖 𝒳 , 𝒴 〗 ⇾̣ 〖 𝒳′ , 𝒴′ 〗)
〖 f , g 〗₁ h σ = g (h (f ∘ σ))

〖_,_〗ₗ : {𝒳 𝒳′ : Familyₛ} → 𝒳′ ⇾̣ 𝒳 → (𝒴 : Familyₛ) → (〖 𝒳 , 𝒴 〗 ⇾̣ 〖 𝒳′ , 𝒴 〗)
〖 f , Z 〗ₗ h σ = h (f ∘ σ)

〖_,_〗ᵣ : {𝒴 𝒴′ : Familyₛ} → (𝒳 : Familyₛ) → 𝒴 ⇾̣ 𝒴′ → (〖 𝒳 , 𝒴 〗 ⇾̣ 〖 𝒳 , 𝒴′ 〗)
〖 X , g 〗ᵣ h σ = g (h σ)

-- | Structure morphisms

i : (𝒳 : Familyₛ) → 〖 ℐ , 𝒳 〗 ⇾̣ 𝒳
i 𝒳 o = o id

i′ : (X : Family) → ⟨ ℐ , X ⟩ ⇾ X
i′ X o = o id

j : (𝒳 : Familyₛ) → ℐ ⇾̣ 〖 𝒳 , 𝒳 〗
j 𝒳 v σ = σ v

L : (𝒳 𝒴 𝒵 : Familyₛ) → 〖 𝒴 , 𝒵 〗 ⇾̣ 〖 〖 𝒳 , 𝒴 〗 , 〖 𝒳 , 𝒵 〗 〗
L 𝒳 Y Z o ς σ = o (λ v → ς v σ)

L′ : (𝒳 𝒴 : Familyₛ)(Z : Family) → ⟨ 𝒴 , Z ⟩ ⇾ ⟨ 〖 𝒳 , 𝒴 〗 , ⟨ 𝒳 , Z ⟩ ⟩
L′ 𝒳 𝒴 Z o ς σ = o (λ v → ς v σ)

-- Category of sorted families is skew-closed under the internal hom
𝔽amₛ:SkewClosed : SkewClosed 𝔽amiliesₛ
𝔽amₛ:SkewClosed = record
  { [-,-] = 〖-,-〗F
  ; unit = ℐ
  ; identity = ntHelper record { η = i  ; commute = λ f → refl }
  ; diagonal = dtHelper record { α = j ; commute = λ f → refl }
  ; L = L
  ; L-commute = refl
  ; Lj≈j = refl
  ; ijL≈id = refl
  ; iL≈i = refl
  ; ij≈id = refl
  ; pentagon = refl
  }

private
  variable
    𝒳 𝒴 𝒵 : Familyₛ
    Y Z : Family

-- ⟨X,-⟩ distributes over and factors out of products
⟨𝒳,Y×Z⟩≅⟨𝒳,Y⟩×⟨𝒳,Z⟩ : ⟨ 𝒳 , (Y ×ₘ Z) ⟩ ≅ₘ ⟨ 𝒳 , Y ⟩ ×ₘ ⟨ 𝒳 , Z ⟩
⟨𝒳,Y×Z⟩≅⟨𝒳,Y⟩×⟨𝒳,Z⟩  = record
  { from = λ h → (λ ρ → proj₁ (h ρ)) , λ ϱ → proj₂ (h ϱ)
  ; to = λ{ (bx , by) ρ → bx ρ , by ρ}
  ; iso = record { isoˡ = refl ; isoʳ = refl }
  }

-- ⟨X,-⟩ factors out of coproducts
⟨𝒳,Y⟩+⟨𝒳,Z⟩⇾⟨𝒳,Y+Z⟩ : ⟨ 𝒳 , Y ⟩ +ₘ ⟨ 𝒳 , Z ⟩ ⇾ ⟨ 𝒳 , (Y +ₘ Z) ⟩
⟨𝒳,Y⟩+⟨𝒳,Z⟩⇾⟨𝒳,Y+Z⟩ (inj₁ ox) σ = inj₁ (ox σ)
⟨𝒳,Y⟩+⟨𝒳,Z⟩⇾⟨𝒳,Y+Z⟩ (inj₂ oy) ς = inj₂ (oy ς)

-- Same properties for the hom
〖𝒳,𝒴×̣𝒵〗≅̣〖𝒳,𝒴〗×̣〖𝒳,𝒵〗 : 〖 𝒳 , 𝒴 ×̣ₘ 𝒵 〗 ≅̣ₘ 〖 𝒳 , 𝒴 〗 ×̣ₘ 〖 𝒳 , 𝒵 〗
〖𝒳,𝒴×̣𝒵〗≅̣〖𝒳,𝒴〗×̣〖𝒳,𝒵〗 = ≅ₘ→≅̣ₘ ⟨𝒳,Y×Z⟩≅⟨𝒳,Y⟩×⟨𝒳,Z⟩

〖𝒳,𝒴〗+̣〖𝒳,𝒵〗⇾̣〖𝒳,𝒴+̣𝒵〗 : 〖 𝒳 , 𝒴 〗 +̣ₘ 〖 𝒳 , 𝒵 〗 ⇾̣ 〖 𝒳 , (𝒴 +̣ₘ 𝒵) 〗
〖𝒳,𝒴〗+̣〖𝒳,𝒵〗⇾̣〖𝒳,𝒴+̣𝒵〗 = ⟨𝒳,Y⟩+⟨𝒳,Z⟩⇾⟨𝒳,Y+Z⟩


{-# OPTIONS --cubical --no-import-sorts --safe #-}
module Cubical.Data.Empty.Base where

open import Cubical.Core.Everything

data ⊥ : Type₀ where

rec : ∀ {ℓ} {A : Type ℓ} → ⊥ → A
rec ()

elim : ∀ {ℓ} {A : ⊥ → Type ℓ} → (x : ⊥) → A x
elim ()


open import Categories
open import Monads

module Monads.CatofAdj.TermAdjObj {a b}{C : Cat {a}{b}}(M : Monad C) where

open import Library
open import Functors
open import Naturals
open import Adjunctions
open import Monads.CatofAdj M
open import Categories.Terminal
open import Monads.EM M
open import Monads.EM.Adjunction M
open import Adjunctions.Adj2Mon

open Cat
open Fun
open Monad M
open NatT
open Adj

lemX : R EMAdj ○ L EMAdj ≅ TFun M
lemX = FunctorEq _ _ refl refl

EMObj : Obj CatofAdj
EMObj = record {
  D       = EM;
  adj     = EMAdj;
  law     = lemX;
  ηlaw    = idl C;
  bindlaw = λ{X Y f} →
    cong bind
         (stripsubst (Hom C X) f (fcong Y (cong OMap (sym lemX))))}

open ObjAdj
open Adj

alaw1lem : ∀{c d}{D : Cat {c}{d}}
  (T : Fun C C)(L : Fun C D)(R : Fun D C)
  (p : R ○ L ≅ T)
  (η : ∀ {X} → Hom C X (OMap T X)) →
  (right : ∀ {X Y} → Hom C X (OMap R Y) → Hom D (OMap L X) Y) →
  (left : ∀ {X Y} → Hom D (OMap L X) Y → Hom C X (OMap R Y)) →
  (ηlaw : ∀ {X} → left (iden D {OMap L X}) ≅ η {X}) →
  ∀ {X}{Z}{f : Hom C Z (OMap R X)} →
  (nat : comp C (HMap R (right f)) (comp C (left (iden D)) (iden C))
         ≅
         left (comp D (right f) (comp D (iden D) (HMap L (iden C))))) →
  (lawb : left (right f) ≅ f) →
  f
  ≅
  comp C (subst (λ Z₁ → Hom C Z₁ (OMap R X))
                (fcong Z (cong OMap p))
                (HMap R (right f)))
         η
alaw1lem {D = D} .(R ○ L) L R refl η right left ηlaw {X}{Z}{f} nat lawb =
  trans (trans (trans (sym lawb)
                      (cong left
                            (trans (sym (idr D))
                                   (cong (comp D (right f))
                                         (trans (sym (fid L))
                                                (sym (idl D)))))))
               (trans (sym nat)
                      (cong (comp C (HMap R (right f)))
                            (idr C))))
        (cong (comp C (HMap R (right f))) ηlaw)

alaw2lem : ∀{c d}{D : Cat {c}{d}}
  (T : Fun C C)(L : Fun C D)(R : Fun D C)
  (p : R ○ L ≅ T) →
  (right : ∀ {X Y} → Hom C X (OMap R Y) → Hom D (OMap L X) Y) →
  (bind : ∀ {X Y} → Hom C X (OMap T Y) → Hom C (OMap T X) (OMap T Y)) →
  (natright : {X₁ X' : Obj C} {Y Y' : Obj D} (f₁ : Hom C X' X₁)
    (g : Hom D Y Y') (h : Hom C X₁ (OMap R Y)) →
    right (comp C (HMap R g) (comp C h f₁))
    ≅
    comp D g (comp D (right h) (HMap L f₁))) →
  ∀ {X}{Z} {W} {k : Hom C Z (OMap T W)}{f : Hom C W (OMap R X)} →
  (bindlaw : HMap R (right (subst (Hom C Z) (fcong W (cong OMap (sym p))) k))
             ≅ bind k) →
  subst (λ Z → Hom C Z (OMap R X))
  (fcong Z (cong OMap p))
  (HMap R
   (right
    (comp C
     (subst (λ Z → Hom C Z (OMap R X))
      (fcong W (cong OMap p)) (HMap R (right f)))
     k)))
  ≅
  comp C
  (subst (λ Z → Hom C Z (OMap R X))
   (fcong W (cong OMap p)) (HMap R (right f)))
  (bind k)
alaw2lem {D = D} .(R ○ L) L R refl right bind natright {X}{Z}{W}{k}{f} bindlaw =
  trans (trans (cong (HMap R)
                     (trans (cong (λ k₁ → right (comp C (HMap R (right f)) k₁))
                                  (sym (idr C)))
                            (trans (natright (iden C) (right f) k)
                                   (trans (cong (λ h → comp D
                                                            (right f)
                                                            (comp D
                                                                  (right k)
                                                                  h))
                                                (fid L))
                                          (trans (sym (ass D))
                                                 (idr D))))))
               (fcomp R))
        (cong (comp C (HMap R (right f))) bindlaw)

ahomlem : ∀{c d}{D : Cat {c}{d}}
          (T : Fun C C)(L : Fun C D)(R : Fun D C)(p : R ○ L ≅ T) →
  (right : ∀ {X Y} → Hom C X (OMap R Y) → Hom D (OMap L X) Y) →
  (natright : {X₁ X' : Obj C} {Y Y' : Obj D} (f₁ : Hom C X' X₁)
    (g : Hom D Y Y') (h : Hom C X₁ (OMap R Y)) →
    right (comp C (HMap R g) (comp C h f₁))
    ≅
    comp D g (comp D (right h) (HMap L f₁))) →
  {X : Obj D}{Y : Obj D}{f : Hom D X Y} →
  {Z : Obj C} {f₁ : Hom C Z (OMap R X)} →
  comp C (HMap R f)
  (subst (λ Z₁ → Hom C Z₁ (OMap R X))
   (fcong Z (cong OMap p))
   (HMap R (right f₁)))
  ≅
  subst (λ Z₁ → Hom C Z₁ (OMap R Y))
  (fcong Z (cong OMap p))
  (HMap R (right (comp C (HMap R f) f₁)))
ahomlem {D = D} .(R ○ L) L R refl right natright {X}{Y}{f}{Z}{g} =
  trans (sym (fcomp R))
    (cong (HMap R)
     (sym
      (trans (cong (λ g₁ → right (comp C (HMap R f) g₁)) (sym (idr C)))
       (trans (natright (iden C) f g)
        (trans (cong (λ h → comp D f (comp D (right g) h)) (fid L))
         (trans (sym (ass D)) (idr D)))))))

Llawlem : ∀{c d}{D : Cat {c}{d}}
  (T : Fun C C)(L : Fun C D)(R : Fun D C)(p : R ○ L ≅ T) →
  (right : ∀ {X Y} → Hom C X (OMap R Y) → Hom D (OMap L X) Y) →
  (bind : ∀ {X Y} → Hom C X (OMap T Y) → Hom C (OMap T X) (OMap T Y)) →
  (bindlaw : {X Y : Obj C} {f : Hom C X (OMap T Y)} →
    HMap R
    (right
     (subst (Hom C X) (fcong Y (cong OMap (sym p))) f))
    ≅ bind f) →
  ∀{X Z} →
  {f  : Hom C Z (OMap R (OMap L X))}
  {f' : Hom C Z (OMap T X)} → (q  : f ≅ f') →
  subst (λ Z₁ → Hom C Z₁ (OMap R (OMap L X)))
  (fcong Z (cong OMap p)) (HMap R (right f))
  ≅ bind f'
Llawlem .(R ○ L) L R refl right bind bindlaw {X}{Z}{f}{.f} refl = bindlaw


{-# OPTIONS --rewriting --prop #-}

open import common
open import syntx

{- The sort corresponding to judgments -}

data JudgmentSort : Set where
  Ty  : JudgmentSort
  Tm  : JudgmentSort
  Ty= : JudgmentSort
  Tm= : JudgmentSort

JudgmentArityArgs = ArityArgs JudgmentSort
JudgmentArity = Arity JudgmentSort

{-
Judgments are indexed by the signature, their ambient context, the length of their local context,
and their sort.

We can see judgments as consisting of two contexts, one normal context (the ambient context) and
then one dependent context (the local context). The reason is that all typing rules occur in an
ambient context which never changes, and sometimes add new assumptions (to the local context).
Therefore we will never have to check that the ambient contexts are equal, it will be forced by the
typing.

Indexing judgments by sorts is very good to get rid of absurd cases, when giving typing rules and
that some judgments are supposed to have certain sorts.
-}

data Judgment (Σ : Signature) {m : ℕ} (Γ : Ctx Σ m) (n : ℕ) : JudgmentSort → Set where
  _⊢_       : (Δ : DepCtx Σ m n) → TyExpr Σ (m + n) → Judgment Σ Γ n Ty
  _⊢_:>_    : (Δ : DepCtx Σ m n) → TmExpr Σ (m + n) → TyExpr Σ (m + n) → Judgment Σ Γ n Tm
  _⊢_==_    : (Δ : DepCtx Σ m n) → TyExpr Σ (m + n) → TyExpr Σ (m + n) → Judgment Σ Γ n Ty=
  _⊢_==_:>_ : (Δ : DepCtx Σ m n) → TmExpr Σ (m + n) → TmExpr Σ (m + n) → TyExpr Σ (m + n)
            → Judgment Σ Γ n Tm=

{-
A derivation rule consists of a partial function taking a tuple of judgments (of the correct
arities) and returning another judgment. Moreover, a derivation rule is extendable to any other
signature the original signature maps to.

The type [DerivationRulePremises Σ Γ args] represents tuples of judgments of arities [args] (and in
signature [Σ] and with ambient context [Γ])

The type [DerivationRule Σ ar n] represents derivation rules in signature [Σ], of arity [ar] and in
scope [n]. It lives in [Set₁] because it quantifies over arbitrary signatures that [Σ] maps into.
-}

data DerivationRulePremises (Σ : Signature) {n : ℕ} (Γ : Ctx Σ n) : JudgmentArityArgs → Set where
  [] : DerivationRulePremises Σ Γ []
  _,_ : {m : ℕ} {k : JudgmentSort} {args : JudgmentArityArgs}
      → DerivationRulePremises Σ Γ args
      → Judgment Σ Γ m k
      → DerivationRulePremises Σ Γ (args , (m , k))

record DerivationRule (Σ : Signature) (ar : JudgmentArity) : Set₁ where
  field
    rule : {Σ' : Signature} {n : ℕ} → (Σ →Sig Σ') n → (Γ : Ctx Σ' n)
         → DerivationRulePremises Σ' Γ (args ar) → Partial (Judgment Σ' Γ 0 (sort ar))
open DerivationRule public

{- A derivability structure consists of a bunch of derivation rules, indexed by their arities -}

data Tag : Set where
  S T C Eq : Tag

record DerivabilityStructure (Σ : Signature) : Set₁ where
  field
    Rules : Tag → JudgmentArity → Set
    derivationRule : {t : Tag} {ar : JudgmentArity} (r : Rules t ar) → DerivationRule Σ ar
open DerivabilityStructure public

{- We can move the local context to the end of the ambient context -}

module _ {Σ : Signature} {m : ℕ} {Γ : Ctx Σ m} where

  Γ+ : {l : ℕ} (Δ : DepCtx Σ m l) → Ctx Σ (m + l)
  Γ+ ◇ = Γ
  Γ+ (Δ , A) = (Γ+ Δ , A)

  exchangeCtx : {n : ℕ} {k : JudgmentSort} → Judgment Σ Γ n k → Ctx Σ (m + n)
  exchangeCtx (Δ ⊢ A)           = Γ+ Δ
  exchangeCtx (Δ ⊢ u :> A)      = Γ+ Δ
  exchangeCtx (Δ ⊢ A == B)      = Γ+ Δ
  exchangeCtx (Δ ⊢ u == v :> A) = Γ+ Δ

  exchange : {n : ℕ} {k : JudgmentSort} → (j : Judgment Σ Γ n k) → Judgment Σ (exchangeCtx j) 0 k
  exchange (Δ ⊢ A)           = ◇ ⊢ A
  exchange (Δ ⊢ u :> A)      = ◇ ⊢ u :> A
  exchange (Δ ⊢ A == B)      = ◇ ⊢ A == B
  exchange (Δ ⊢ u == v :> A) = ◇ ⊢ u == v :> A

{-
A judgment can be derivable in one different way:
- if it has a trivial local context, then it should be obtained by applying a rule [r] from the
  derivability structure to a list of judgments [js] which are all derivable [js-der] and for which
  the rule is defined [def].

The type [DerivableArgs E js] represents the fact that all of the judgments in [js] are derivables.
The type [Derivable E j] represents the fact that the judgment [j] is derivable.
-}

data Derivable {Σ : Signature} (E : DerivabilityStructure Σ)
     : {m : ℕ} {Γ : Ctx Σ m} {k : JudgmentSort} → Judgment Σ Γ 0 k → Prop

data DerivableArgs {Σ : Signature} (E : DerivabilityStructure Σ) {m : ℕ} {Γ : Ctx Σ m}
     : {ar : JudgmentArityArgs} → DerivationRulePremises Σ Γ ar → Prop where
  []  : DerivableArgs E []
  _,_ : {n : ℕ} {k : JudgmentSort} {j : Judgment Σ Γ n k}
        {ar : JudgmentArityArgs} {js : DerivationRulePremises Σ Γ ar}
        → DerivableArgs E js
        → Derivable E (exchange j)
        → DerivableArgs E (js , j)

data Derivable {Σ} E where
  apr : (t : Tag) {ar : JudgmentArity} (r : Rules E t ar) {m : ℕ} {Γ : Ctx Σ m}
        {js : DerivationRulePremises Σ Γ (args ar)}
        (js-der : DerivableArgs E js) {{def : isDefined (rule (derivationRule E r) idSig Γ js)}}
          → Derivable E (rule (derivationRule E r) idSig Γ js $ def)

{- Special cases of [_,_], used to make Agda not blow up -}

_,0Ty_ : ∀ {Σ} {E} {m} {Γ : Ctx Σ m} {A : TyExpr Σ m}
        {ar : JudgmentArityArgs} {js : DerivationRulePremises Σ Γ ar}
        → DerivableArgs E js
        → Derivable E (◇ ⊢ A)
        → DerivableArgs E (js , ◇ ⊢ A)
djs ,0Ty dj = djs , dj

_,0Ty=_ : ∀ {Σ} {E} {m} {Γ : Ctx Σ m} {A B : _}
        {ar : JudgmentArityArgs} {js : DerivationRulePremises Σ Γ ar}
        → DerivableArgs E js
        → Derivable E (◇ ⊢ A == B)
        → DerivableArgs E (js , ◇ ⊢ A == B)
djs ,0Ty= dj = djs , dj

_,0Tm_ : ∀ {Σ} {E} {m} {Γ : Ctx Σ m} {u : _} {A : _}
        {ar : JudgmentArityArgs} {js : DerivationRulePremises Σ Γ ar}
        → DerivableArgs E js
        → Derivable E (◇ ⊢ u :> A)
        → DerivableArgs E (js , ◇ ⊢ u :> A)
djs ,0Tm dj = djs , dj

_,0Tm=_ : ∀ {Σ} {E} {m} {Γ : Ctx Σ m} {u v : _} {A : _}
        {ar : JudgmentArityArgs} {js : DerivationRulePremises Σ Γ ar}
        → DerivableArgs E js
        → Derivable E (◇ ⊢ u == v :> A)
        → DerivableArgs E (js , ◇ ⊢ u == v :> A)
djs ,0Tm= dj = djs , dj

_,1Ty_ : ∀ {Σ} {E} {m} {Γ : Ctx Σ m} {A} {B}
        {ar : JudgmentArityArgs} {js : DerivationRulePremises Σ Γ ar}
        → DerivableArgs E js
        → Derivable E (exchange ((◇ , A) ⊢ B))
        → DerivableArgs E (js , (◇ , A) ⊢ B)
djs ,1Ty dj = djs , dj

_,1Ty=_ : ∀ {Σ} {E} {m} {Γ : Ctx Σ m} {A} {B C}
        {ar : JudgmentArityArgs} {js : DerivationRulePremises Σ Γ ar}
        → DerivableArgs E js
        → Derivable E (exchange ((◇ , A) ⊢ B == C))
        → DerivableArgs E (js , (◇ , A) ⊢ B == C)
djs ,1Ty= dj = djs , dj

_,1Tm_ : ∀ {Σ} {E} {m} {Γ : Ctx Σ m} {u : _} {A : _} {B : _}
        {ar : JudgmentArityArgs} {js : DerivationRulePremises Σ Γ ar}
        → DerivableArgs E js
        → Derivable E (exchange ((◇ , B) ⊢ u :> A))
        → DerivableArgs E (js , (◇ , B) ⊢ u :> A)
djs ,1Tm dj = djs , dj

_,1Tm=_ : ∀ {Σ} {E} {m} {Γ : Ctx Σ m} {u v : _} {A : _} {B : _}
        {ar : JudgmentArityArgs} {js : DerivationRulePremises Σ Γ ar}
        → DerivableArgs E js
        → Derivable E (exchange ((◇ , B) ⊢ u == v :> A))
        → DerivableArgs E (js , (◇ , B) ⊢ u == v :> A)
djs ,1Tm= dj = djs , dj

_,2Tm_ : ∀ {Σ} {E} {m} {Γ : Ctx Σ m} {u : _} {A : _} {B : _} {C : _}
        {ar : JudgmentArityArgs} {js : DerivationRulePremises Σ Γ ar}
        → DerivableArgs E js
        → Derivable E (exchange ((◇ , B , C) ⊢ u :> A))
        → DerivableArgs E (js , (◇ , B , C) ⊢ u :> A)
djs ,2Tm dj = djs , dj

infixl 4 _,0Ty_ _,0Ty=_ _,0Tm_ _,0Tm=_
         _,1Ty_ _,1Ty=_ _,1Tm_ _,1Tm=_
                        _,2Tm_


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

open import lib.Basics
open import lib.two-semi-categories.Functor
open import lib.two-semi-categories.FundamentalCategory
open import lib.two-semi-categories.FunctorInverse
open import lib.types.Pi using ()

module lib.two-semi-categories.FunextFunctors where

module FunextFunctors {i j} (A : Type i) (B : Type j) {{B-level : has-level 2 B}} where

  open import lib.two-semi-categories.FunCategory

  private

    app=-pres-comp : ∀ {f g h : A → B} (α : f == g) (β : g == h) → app= (α ∙ β) == (λ a → app= α a ∙ app= β a)
    app=-pres-comp α β = λ= (λ a → ap-∙ (λ f → f a) α β)

    abstract
      app=-pres-comp-coh : ∀ {f g h i : A → B} (α : f == g) (β : g == h) (γ : h == i)
        → app=-pres-comp (α ∙ β) γ ◃∙
          ap (λ s a → s a ∙ app= γ a) (app=-pres-comp α β) ◃∙
          λ= (λ a → ∙-assoc (app= α a) (app= β a) (app= γ a)) ◃∎
          =ₛ
          ap app= (∙-assoc α β γ) ◃∙
          app=-pres-comp α (β ∙ γ) ◃∙
          ap (λ s a → app= α a ∙ s a) (app=-pres-comp β γ) ◃∎
      app=-pres-comp-coh {f} idp idp γ =
        app=-pres-comp idp γ ◃∙
        ap (λ s a → s a ∙ app= γ a) (app=-pres-comp idp idp) ◃∙
        λ= (λ a → idp) ◃∎
          =ₛ⟨ 2 & 1 & =ₛ-in {t = []} (! (λ=-η idp)) ⟩
        app=-pres-comp idp γ ◃∙
        ap (λ s a → s a ∙ app= γ a) (λ= (λ a → idp {a = idp {a = f a}})) ◃∎
          =ₛ₁⟨ 1 & 1 & ap (ap (λ s a → s a ∙ app= γ a)) (! (λ=-η idp)) ⟩
        app=-pres-comp idp γ ◃∙
        idp ◃∎
          =ₛ₁⟨ 1 & 1 & ap (ap (λ s → s)) (λ=-η idp) ⟩
        app=-pres-comp idp γ ◃∙
        ap (λ s → s) (λ= (λ a → idp {a = app= γ a})) ◃∎
          =ₛ⟨ 0 & 0 & contract ⟩
        idp ◃∙
        app=-pres-comp idp γ ◃∙
        ap (λ s → s) (λ= (λ a → idp {a = app= γ a})) ◃∎ ∎ₛ

  app=-functor : TwoSemiFunctor (2-type-fundamental-cat (A → B))
                                (fun-cat A (2-type-fundamental-cat B))
  app=-functor =
    record
    { F₀ = idf (A → B)
    ; F₁ = app=
    ; pres-comp = app=-pres-comp
    ; pres-comp-coh = app=-pres-comp-coh
    }

  private
    module app=-functor =
      TwoSemiFunctor app=-functor
    module app=-inverse =
      FunctorInverse
        app=-functor
        (idf-is-equiv _)
        (λ f g → snd app=-equiv)

  λ=-functor : TwoSemiFunctor (fun-cat A (2-type-fundamental-cat B))
                              (2-type-fundamental-cat (A → B))
  λ=-functor = app=-inverse.functor

  module λ=-functor = TwoSemiFunctor λ=-functor

  abstract
    λ=-functor-pres-comp=λ=-∙ : ∀ {f g h : A → B} (α : f ∼ g) (β : g ∼ h)
      → λ=-functor.pres-comp α β == =ₛ-out (λ=-∙ α β)
    λ=-functor-pres-comp=λ=-∙ α β = =ₛ-out {t = =ₛ-out (λ=-∙ α β) ◃∎} $
      λ=-functor.pres-comp α β ◃∎
        =ₛ⟨ app=-inverse.pres-comp-β α β ⟩
      idp ◃∙
      ap2 (λ s t → λ= (λ a → s a ∙ t a)) (! (λ= (app=-β α))) (! (λ= (app=-β β))) ◃∙
      ap λ= (! (λ= (λ a → ap-∙ (λ f → f a) (λ= α) (λ= β)))) ◃∙
      ! (λ=-η (λ= α ∙ λ= β)) ◃∎
        =ₛ⟨ 0 & 1 & expand [] ⟩
      ap2 (λ s t → λ= (λ a → s a ∙ t a)) (! (λ= (app=-β α))) (! (λ= (app=-β β))) ◃∙
      ap λ= (! (λ= (λ a → ap-∙ (λ f → f a) (λ= α) (λ= β)))) ◃∙
      ! (λ=-η (λ= α ∙ λ= β)) ◃∎
        =ₛ₁⟨ 0 & 1 & step₈ ⟩
      ap λ= (! (λ= (λ a' → ap2 _∙_ (app=-β α a') (app=-β β a')))) ◃∙
      ap λ= (! (λ= (λ a' → ap-∙ (λ γ → γ a') (λ= α) (λ= β)))) ◃∙
      ! (λ=-η (λ= α ∙ λ= β)) ◃∎
        =ₛ⟨ 0 & 2 &
            ap-seq-=ₛ λ= $ ∙-!-seq $
            λ= (λ a' → ap-∙ (λ γ → γ a') (λ= α) (λ= β)) ◃∙
            λ= (λ a' → ap2 _∙_ (app=-β α a') (app=-β β a')) ◃∎ ⟩
      ap λ= (! (λ= (λ a' → ap-∙ (λ γ → γ a') (λ= α) (λ= β)) ∙
                λ= (λ a' → ap2 _∙_ (app=-β α a') (app=-β β a')))) ◃∙
      ! (λ=-η (λ= α ∙ λ= β)) ◃∎
        =ₛ₁⟨ 0 & 1 & ap-! λ= (λ= (λ a' → ap-∙ (λ γ → γ a') (λ= α) (λ= β)) ∙
                              λ= (λ a' → ap2 _∙_ (app=-β α a') (app=-β β a'))) ⟩
      ! (ap λ= (λ= (λ a' → ap-∙ (λ γ → γ a') (λ= α) (λ= β)) ∙
                λ= (λ a' → ap2 _∙_ (app=-β α a') (app=-β β a')))) ◃∙
      ! (λ=-η (λ= α ∙ λ= β)) ◃∎
        =ₛ₁⟨ 0 & 1 &
            ap (! ∘ ap λ=) $ =ₛ-out $
            ∙-λ= (λ a' → ap-∙ (λ γ → γ a') (λ= α) (λ= β))
                  (λ a' → ap2 _∙_ (app=-β α a') (app=-β β a')) ⟩
      ! (ap λ= (λ= (λ a' → ap-∙ (λ γ → γ a') (λ= α) (λ= β) ∙
                          ap2 _∙_ (app=-β α a') (app=-β β a')))) ◃∙
      ! (λ=-η (λ= α ∙ λ= β)) ◃∎
        =ₛ⟨ =ₛ-in $
            ∙-! (ap λ= (λ= (λ a' → ap-∙ (λ γ → γ a') (λ= α) (λ= β) ∙
                                          ap2 _∙_ (app=-β α a') (app=-β β a'))))
                (λ=-η (λ= α ∙ λ= β)) ⟩
      ! (λ=-η (λ= α ∙ λ= β) ∙
        ap λ= (λ= (λ a' → ap-∙ (λ γ → γ a') (λ= α) (λ= β) ∙
                          ap2 _∙_ (app=-β α a') (app=-β β a')))) ◃∎ ∎ₛ
      where
      step₈' : ap2 (λ s t a → s a ∙ t a) (λ= (app=-β α)) (λ= (app=-β β)) ==
              λ= (λ a' → ap2 _∙_ (app=-β α a') (app=-β β a'))
      step₈' =
        –>-is-inj app=-equiv _ _ $ λ= $ λ a →
        app= (ap2 (λ s t a' → s a' ∙ t a') (λ= (app=-β α)) (λ= (app=-β β))) a
          =⟨ ap-ap2 (λ f → f a) (λ s t a' → s a' ∙ t a') (λ= (app=-β α)) (λ= (app=-β β)) ⟩
        ap2 (λ s t → s a ∙ t a) (λ= (app=-β α)) (λ= (app=-β β))
          =⟨ ! (ap2-ap-lr _∙_ (λ f → f a) (λ f → f a) (λ= (app=-β α)) (λ= (app=-β β))) ⟩
        ap2 _∙_ (app= (λ= (app=-β α)) a) (app= (λ= (app=-β β)) a)
          =⟨ ap2 (ap2 _∙_) (app=-β (app=-β α) a) (app=-β (app=-β β) a) ⟩
        ap2 _∙_ (app=-β α a) (app=-β β a)
          =⟨ ! (app=-β (λ a' → ap2 _∙_ (app=-β α a') (app=-β β a')) a) ⟩
        app= (λ= (λ a' → ap2 _∙_ (app=-β α a') (app=-β β a'))) a =∎
      step₈ : ap2 (λ s t → λ= (λ a → s a ∙ t a)) (! (λ= (app=-β α))) (! (λ= (app=-β β))) ==
              ap λ= (! (λ= (λ a' → ap2 _∙_ (app=-β α a') (app=-β β a'))))
      step₈ =
        ap2 (λ s t → λ= (λ a → s a ∙ t a)) (! (λ= (app=-β α))) (! (λ= (app=-β β)))
          =⟨ ! (ap-ap2 λ= (λ s t a → s a ∙ t a) (! (λ= (app=-β α))) (! (λ= (app=-β β)))) ⟩
        ap λ= (ap2 (λ s t a → s a ∙ t a) (! (λ= (app=-β α))) (! (λ= (app=-β β))))
          =⟨ ap (ap λ=) (ap2-! (λ s t a → s a ∙ t a) (λ= (app=-β α)) (λ= (app=-β β))) ⟩
        ap λ= (! (ap2 (λ s t a → s a ∙ t a) (λ= (app=-β α)) (λ= (app=-β β))))
          =⟨ ap (ap λ= ∘ !) step₈' ⟩
        ap λ= (! (λ= (λ a' → ap2 _∙_ (app=-β α a') (app=-β β a')))) =∎


open import Function using (case_of_; _∘_)
open import Data.List using (List; _++_; map) renaming (_∷_ to _,_; _∷ʳ_ to _,′_; [] to ∅)
open import Data.List.Properties using (map-++-commute)
open import Data.Product using () renaming (_×_ to _x'_)
open import Relation.Binary.PropositionalEquality as PropEq using (_≡_; refl; sym; cong)

module LinearLogic (U : Set) (R : U) (⟦_⟧ᵁ : U → Set) where

infixr 40 ¬_
infix  30 _⊗_
infixr 20 _⊸_
infix  4  _⊢_

data Type : Set where
  el   : (A : U) → Type
  ⊥    : Type
  _⊗_  : Type → Type → Type
  _⊸_  : Type → Type → Type

data _⊢_ : ∀ (X : List Type) (A : Type) → Set where
  var   : ∀ {A} → A , ∅ ⊢ A
  abs   : ∀ {X A B} → A , X ⊢ B → X ⊢ A ⊸ B
  app   : ∀ {X Y A B} → X ⊢ A ⊸ B → Y ⊢ A → X ++ Y ⊢ B
  pair  : ∀ {X Y A B} → X ⊢ A → Y ⊢ B → X ++ Y ⊢ A ⊗ B
  case  : ∀ {X Y A B C } → X ⊢ A ⊗ B → A , B , Y ⊢ C → X ++ Y ⊢ C
  exch  : ∀ {X Y Z W A} →  (X ++ Z) ++ (Y ++ W) ⊢ A
        →  (X ++ Y) ++ (Z ++ W) ⊢ A

¬_ : Type → Type
¬ A = A ⊸ ⊥

exch₀ : ∀ {A B C X} → A , B , X ⊢ C → B , A , X ⊢ C
exch₀ {A} {B} {X = X} t = exch {∅} {B , ∅} {A , ∅} {X} t

swap : ∀ {A B} → ∅ ⊢ A ⊗ B ⊸ B ⊗ A
swap {A} {B} = abs (case var (exch₀ (pair var var)))

raise : ∀ {A B X} → X ⊢ A → X ⊢ (A ⊸ B) ⊸ B
raise t = abs (app var t)

++-assoc : ∀ {a} {A : Set a} (X Y Z : List A) → X ++ (Y ++ Z) ≡ (X ++ Y) ++ Z
++-assoc ∅ Y Z = refl
++-assoc (x , X) Y Z = cong (_,_ x) (++-assoc X Y Z)

xs++[]=xs : ∀ {a} {A : Set a} (xs : List A) → xs ++ ∅ ≡ xs
xs++[]=xs ∅ = refl
xs++[]=xs (x , xs) = cong (_,_ x) (xs++[]=xs xs)

to-front : ∀ {X A B} → A , X ⊢ B → X ,′ A ⊢ B
to-front {X} {A} {B} t = lem1 lem2
  where
    lem1 : A , (X ++ ∅) ⊢ B → X ,′ A ⊢ B
    lem1 = exch {∅} {X} {A , ∅} {∅}
    lem2 : A , (X ++ ∅) ⊢ B
    lem2 rewrite xs++[]=xs X = t

to-back : ∀ {X A B} → X ,′ A ⊢ B → A , X ⊢ B
to-back {X} {A} {B} t = lem2
  where
    lem1 : A , X ++ ∅ ⊢ B
    lem1 = exch {∅} {A , ∅} {X} {∅} t
    lem2 : A , X ⊢ B
    lem2 rewrite sym (xs++[]=xs (A , X)) = lem1

YX↝XY : ∀ {A} X Y → Y ++ X ⊢ A → X ++ Y ⊢ A
YX↝XY {A} X Y t = lem₃
  where
    lem₁ : Y ++ X ++ ∅ ⊢ A
    lem₁ rewrite xs++[]=xs X = t
    lem₂ : X ++ Y ++ ∅ ⊢ A
    lem₂ = exch {∅} {X} {Y} {∅} lem₁
    lem₃ : X ++ Y ⊢ A
    lem₃ = PropEq.subst (λ Y → X ++ Y ⊢ A) (xs++[]=xs Y) lem₂

Y[XZ]↝X[YZ] : ∀ {A} X Y Z → Y ++ (X ++ Z) ⊢ A → X ++ (Y ++ Z) ⊢ A
Y[XZ]↝X[YZ] {A} X Y Z t = exch {∅} {X} {Y} {Z} t

[YX]Z↝[XY]Z : ∀ {A} X Y Z → (Y ++ X) ++ Z ⊢ A → (X ++ Y) ++ Z ⊢ A
[YX]Z↝[XY]Z {A} X Y Z t = lem₃
  where
    lem₁ : Y ++ (X ++ Z) ⊢ A
    lem₁ rewrite ++-assoc Y X Z = t
    lem₂ : X ++ (Y ++ Z) ⊢ A
    lem₂ = Y[XZ]↝X[YZ] X Y Z lem₁
    lem₃ : (X ++ Y) ++ Z ⊢ A
    lem₃ rewrite sym (++-assoc X Y Z) = lem₂

[XZ]Y↝[XY]Z : ∀ {A} X Y Z → (X ++ Z) ++ Y ⊢ A → (X ++ Y) ++ Z ⊢ A
[XZ]Y↝[XY]Z {A} X Y Z t = lem₃
  where
    lem₁ : (X ++ Z) ++ Y ++ ∅ ⊢ A
    lem₁ rewrite xs++[]=xs Y = t
    lem₂ : (X ++ Y) ++ Z ++ ∅ ⊢ A
    lem₂ = exch {X} {Y} {Z} {∅} lem₁
    lem₃ : (X ++ Y) ++ Z ⊢ A
    lem₃ = PropEq.subst (λ Z → (X ++ Y) ++ Z ⊢ A) (xs++[]=xs Z) lem₂

X[ZY]↝X[YZ] : ∀ {A} X Y Z → X ++ (Z ++ Y) ⊢ A → X ++ (Y ++ Z) ⊢ A
X[ZY]↝X[YZ] {A} X Y Z t = lem₃
  where
    lem₁ : (X ++ Z) ++ Y ⊢ A
    lem₁ rewrite sym (++-assoc X Z Y) = t
    lem₂ : (X ++ Y) ++ Z ⊢ A
    lem₂ = [XZ]Y↝[XY]Z X Y Z lem₁
    lem₃ : X ++ Y ++ Z ⊢ A
    lem₃ rewrite ++-assoc X Y Z = lem₂

XYZW↝XWZY : ∀ {A} X Y Z W → (X ++ Y) ++ (Z ++ W) ⊢ A → (X ++ W) ++ (Z ++ Y) ⊢ A
XYZW↝XWZY {A} X Y Z W t = lem₃
  where
    lem₁ : (X ++ Y) ++ (W ++ Z) ⊢ A
    lem₁ = X[ZY]↝X[YZ] (X ++ Y) W Z t
    lem₂ : (X ++ W) ++ (Y ++ Z) ⊢ A
    lem₂ = exch {X} {W} {Y} {Z} lem₁
    lem₃ : (X ++ W) ++ (Z ++ Y) ⊢ A
    lem₃ = X[ZY]↝X[YZ] (X ++ W) Z Y lem₂

XYZW↝YWXZ : ∀ {A} X Y Z W → (X ++ Y) ++ (Z ++ W) ⊢ A → (Y ++ W) ++ (X ++ Z) ⊢ A
XYZW↝YWXZ {A} X Y Z W t = lem₃
  where
    lem₁ : (Y ++ X) ++ (Z ++ W) ⊢ A
    lem₁ = [YX]Z↝[XY]Z Y X (Z ++ W) t
    lem₂ : (Y ++ X) ++ (W ++ Z) ⊢ A
    lem₂ = X[ZY]↝X[YZ] (Y ++ X) W Z lem₁
    lem₃ : (Y ++ W) ++ (X ++ Z) ⊢ A
    lem₃ = exch {Y} {W} {X} {Z} lem₂

XYZW↝ZXWY : ∀ {A} X Y Z W → (X ++ Y) ++ (Z ++ W) ⊢ A → (Z ++ X) ++ (W ++ Y) ⊢ A
XYZW↝ZXWY {A} X Y Z W t = lem₃
  where
    lem₁ : (X ++ Z) ++ (Y ++ W) ⊢ A
    lem₁ = exch {X} {Z} {Y} {W} t
    lem₂ : (Z ++ X) ++ (Y ++ W) ⊢ A
    lem₂ = [YX]Z↝[XY]Z Z X (Y ++ W) lem₁
    lem₃ : (Z ++ X) ++ (W ++ Y) ⊢ A
    lem₃ = X[ZY]↝X[YZ] (Z ++ X) W Y lem₂

XYZW↝ZYXW : ∀ {A} X Y Z W → (X ++ Y) ++ (Z ++ W) ⊢ A → (Z ++ Y) ++ (X ++ W) ⊢ A
XYZW↝ZYXW {A} X Y Z W t = lem₃
  where
    lem₁ : (Y ++ X) ++ (Z ++ W) ⊢ A
    lem₁ = [YX]Z↝[XY]Z Y X (Z ++ W) t
    lem₂ : (Y ++ Z) ++ (X ++ W) ⊢ A
    lem₂ = exch {Y} {Z} {X} {W} lem₁
    lem₃ : (Z ++ Y) ++ (X ++ W) ⊢ A
    lem₃ = [YX]Z↝[XY]Z Z Y (X ++ W) lem₂

pair-left : ∀ {X A B C} → A , B , X ⊢ C → A ⊗ B , X ⊢ C
pair-left t = case var t

pair-left′ : ∀ {X A B C} → X ++ (A , B , ∅) ⊢ C → X ,′ A ⊗ B ⊢ C
pair-left′ {X} {A} {B} {C} = lem₃
  where
    lem₁ : X ,′ A ,′ B ⊢ C → X ,′ A ⊗ B ⊢ C
    lem₁ t = to-front (pair-left (to-back {B , X} {A} (to-back {X ,′ A} {B} t)))
    lem₂ : ∀ {a} {A : Set a} xs (y z : A) → xs ,′ y ,′ z  ≡ xs ++ (y , z , ∅)
    lem₂ ∅ y z = refl
    lem₂ (x , xs) y z = cong (_,_ x) (lem₂ xs y z)
    lem₃ : X ++ (A , B , ∅) ⊢ C → X ,′ A ⊗ B ⊢ C
    lem₃ rewrite sym (lem₂ X A B) = lem₁

open import IntuitionisticLogic U ⟦_⟧ᵁ as IL renaming (Type to TypeIL; _⊗_ to _×_)
open IL.Explicit
  hiding (swap; swap′)
  renaming (_⊢_ to _⊢IL_; ReifyType to ReifyTypeIL; ReifyCtxt to ReiftCtxtIL; [_] to reifyIL)

instance
  ReifyType : Reify Type TypeIL
  ReifyType = record { ⟦_⟧ = ⟦_⟧ }
    where

      ⟦_⟧ : Type → TypeIL
      ⟦ ⊥      ⟧ = el R
      ⟦ el A   ⟧ = el A
      ⟦ A ⊗ B  ⟧ = ⟦ A ⟧ × ⟦ B ⟧
      ⟦ A ⊸ B  ⟧ = ⟦ A ⟧ ⇒ ⟦ B ⟧

open Reify {{...}} using (⟦_⟧)

instance
  ReifyCtxt : Reify (List Type) (List TypeIL)
  ReifyCtxt = record { ⟦_⟧ = map ⟦_⟧ }

⟦X++Y⟧=⟦X⟧++⟦Y⟧ : (X Y : List Type) → ⟦ X ++ Y ⟧ ≡ ⟦ X ⟧ ++ ⟦ Y ⟧
⟦X++Y⟧=⟦X⟧++⟦Y⟧ X Y = map-++-commute ⟦_⟧ X Y

toIL : ∀ {X A} → X ⊢ A → ⟦ X ⟧ ⊢IL ⟦ A ⟧
toIL var       = var
toIL (abs t)   = abs (toIL t)
toIL (app {X} {Y} s t)   rewrite ⟦X++Y⟧=⟦X⟧++⟦Y⟧ X Y  = app (toIL s) (toIL t)
toIL (pair {X} {Y} s t)  rewrite ⟦X++Y⟧=⟦X⟧++⟦Y⟧ X Y  = pair (toIL s) (toIL t)
toIL (case {X} {Y} s t)  rewrite ⟦X++Y⟧=⟦X⟧++⟦Y⟧ X Y  = case (toIL s) (toIL t)
toIL (exch {X} {Y} {Z} {W} {A} t)  = lem4
  where
    lem1 : ⟦ (X ++ Z) ++ (Y ++ W) ⟧ ⊢IL ⟦ A ⟧
    lem1 = toIL t
    lem2 : (⟦ X ⟧ ++ ⟦ Z ⟧) ++ (⟦ Y ⟧ ++ ⟦ W ⟧) ⊢IL ⟦ A ⟧
    lem2 rewrite  sym (⟦X++Y⟧=⟦X⟧++⟦Y⟧ X Z)
               |  sym (⟦X++Y⟧=⟦X⟧++⟦Y⟧ Y W)
               |  sym (⟦X++Y⟧=⟦X⟧++⟦Y⟧ (X ++ Z) (Y ++ W)) = lem1
    lem3 : (⟦ X ⟧ ++ ⟦ Y ⟧) ++ (⟦ Z ⟧ ++ ⟦ W ⟧) ⊢IL ⟦ A ⟧
    lem3 = exch {⟦ X ⟧} {⟦ Y ⟧} {⟦ Z ⟧} {⟦ W ⟧} lem2
    lem4 : ⟦ (X ++ Y) ++ (Z ++ W) ⟧ ⊢IL ⟦ A ⟧
    lem4 rewrite  ⟦X++Y⟧=⟦X⟧++⟦Y⟧ (X ++ Y) (Z ++ W)
               |  ⟦X++Y⟧=⟦X⟧++⟦Y⟧ X Y
               |  ⟦X++Y⟧=⟦X⟧++⟦Y⟧ Z W = lem3

[_] : {A : Type} {X : List Type} → X ⊢ A → (Ctxt ⟦ ⟦ X ⟧ ⟧ → ⟦ ⟦ A ⟧ ⟧)
[_] = reifyIL ∘ toIL

swap′ : {A B : Type} → ⟦ ⟦ A ⟧ ⟧ x' ⟦ ⟦ B ⟧ ⟧ → ⟦ ⟦ B ⟧ ⟧ x' ⟦ ⟦ A ⟧ ⟧
swap′ {A} {B} = [ swap {A} {B} ] ∅


open import Oscar.Prelude
open import Oscar.Class.Successor₀
open import Oscar.Class.Successor₁
open import Oscar.Class.Injectivity
open import Oscar.Class.Pure

module Oscar.Class.Thickandthin where

module _
  {x} {X : Ø x} {a} (A : X → Ø a) {b} (B : X → Ø b)
  where
  record [𝓣hin] : Ø₀ where
    no-eta-equality
    constructor ∁
  module _
    ⦃ _ : 𝓢uccessor₀ X ⦄
    where
    𝔱hin : ∀ (m : X) → A (⇑₀ m) → B m → Ø b
    𝔱hin m = λ _ _ → B (⇑₀ m)
    𝓽hin = ∀ {m : X} → A (⇑₀ m) → B m → B (⇑₀ m)
    record 𝓣hin ⦃ _ : [𝓣hin] ⦄ : Ø x ∙̂ a ∙̂ b where
      field
        thin : 𝓽hin
      instance `𝓘njection₂ : ∀ {m} → 𝓘njection₂ (𝔱hin m)
      `𝓘njection₂ = ∁ thin
open 𝓣hin ⦃ … ⦄ public

module _
  {x} {X : Ø x} {a} (A : X → Ø a) {b} (B : X → Ø b)
  where
  record [𝓣hick] : Ø₀ where
    no-eta-equality
    constructor ∁
  module _
    ⦃ _ : 𝓢uccessor₀ X ⦄
    where
    𝓽hick = ∀ {m} → A m → B (⇑₀ m) → B m
    record 𝓣hick ⦃ _ : [𝓣hick] ⦄ : Ø x ∙̂ a ∙̂ b where field thick : 𝓽hick
open 𝓣hick ⦃ … ⦄ public

module _
  {x} {X : Ø x}
  {a} (A : X → Ø a)
  {b} (B : X → Ø b)
  {ℓ} (_≈_ : ∀ {x} → B x → B x → Ø ℓ)
  where
  record [𝓣hick/thin=1] : Ø₀ where
    no-eta-equality
    constructor ∁
  module _
    ⦃ _ : 𝓢uccessor₀ X ⦄
    ⦃ _ : [𝓢uccessor₁] A ⦄
    ⦃ _ : 𝓢uccessor₁ A ⦄
    ⦃ _ : [𝓣hin] A B ⦄
    ⦃ _ : 𝓣hin A B ⦄
    ⦃ _ : [𝓣hick] A B ⦄
    ⦃ _ : 𝓣hick A B ⦄
    where
    𝓽hick/thin=1 = ∀ {m} (x : A m) (y : B m) → thick x (thin (⇑₁ x) y) ≈ y
    record 𝓣hick/thin=1 : Ø x ∙̂ a ∙̂ b ∙̂ ℓ where field thick/thin=1 : 𝓽hick/thin=1
open 𝓣hick/thin=1 ⦃ … ⦄ public

module _
  {x} {X : Ø x}
  {a} (A : X → Ø a)
  {b} (B : X → Ø b)
  {c} (C : Ø b → Ø c)
  where
  record [𝓒heck] : Ø₀ where
    no-eta-equality
    constructor ∁
  module _
    ⦃ _ : 𝓢uccessor₀ X ⦄
    where
    𝓬heck = ∀ {m} → A (⇑₀ m) → B (⇑₀ m) → C (B m)
    record 𝓒heck ⦃ _ : [𝓒heck] ⦄ : Ø x ∙̂ a ∙̂ b ∙̂ c where field check : 𝓬heck
open 𝓒heck ⦃ … ⦄ public

check[_] : ∀
  {x} {X : Ø x}
  {a} {A : X → Ø a}
  {b} {B : X → Ø b}
  {c} (C : Ø b → Ø c)
  ⦃ _ : [𝓒heck] A B C ⦄
  ⦃ _ : 𝓢uccessor₀ X ⦄
  ⦃ _ : 𝓒heck A B C ⦄
  → 𝓬heck A B C
check[ _ ] = check

module _
  {x} {X : Ø x}
  {a} (A : X → Ø a)
  {b} (B : X → Ø b)
  {c} (C : Ø b → Ø c)
  {ℓ} (_≈_ : ∀ {x} → C (B x) → C (B x) → Ø ℓ)
  where
  record [𝓒heck/thin=1] : Ø₀ where
    no-eta-equality
    constructor ∁
  module _
    ⦃ _ : 𝓢uccessor₀ X ⦄
    ⦃ _ : [𝓣hin] A B ⦄
    ⦃ _ : 𝓣hin A B ⦄
    ⦃ _ : [𝓒heck] A B C ⦄
    ⦃ _ : 𝓒heck A B C ⦄
    ⦃ _ : 𝓟ure C ⦄
    where
    𝓬heck/thin=1 = ∀ {n} (x : A (⇑₀ n)) (y : B n) → check x (thin x y) ≈ pure y
    record 𝓒heck/thin=1 ⦃ _ : [𝓒heck/thin=1] ⦄ : Ø x ∙̂ a ∙̂ b ∙̂ c ∙̂ ℓ where field check/thin=1 : 𝓬heck/thin=1
open 𝓒heck/thin=1 ⦃ … ⦄ public

check/thin=1[_] : ∀
  {x} {X : Ø x}
  {a} {A : X → Ø a}
  {b} {B : X → Ø b}
  {c} {C : Ø b → Ø c}
  {ℓ} (_≈_ : ∀ {x} → C (B x) → C (B x) → Ø ℓ)
  ⦃ _ : 𝓢uccessor₀ X ⦄
  ⦃ _ : [𝓣hin] A B ⦄
  ⦃ _ : 𝓣hin A B ⦄
  ⦃ _ : [𝓒heck] A B C ⦄
  ⦃ _ : 𝓒heck A B C ⦄
  ⦃ _ : 𝓟ure C ⦄
  ⦃ _ : [𝓒heck/thin=1] A B C _≈_ ⦄
  ⦃ _ : 𝓒heck/thin=1 A B C _≈_ ⦄
  → 𝓬heck/thin=1 A B C _≈_
check/thin=1[ _ ] = check/thin=1

record IsThickandthin
  {x a b c ℓb ℓc}
  {X : Ø x}
  (A : X → Ø a)
  (B : X → Ø b)
  (_≈B_ : ∀ {x} → B x → B x → Ø ℓb)
  (C : Ø b → Ø c)
  (_≈C_ : ∀ {x} → C (B x) → C (B x) → Ø ℓc)
  : Ø x ∙̂ a ∙̂ ↑̂ b ∙̂ ℓb ∙̂ c ∙̂ ℓc where
  constructor ∁
  field
    overlap ⦃ `𝓢uccessor₀ ⦄ : 𝓢uccessor₀ X
    overlap ⦃ `[𝓢uccessor₁] ⦄ : [𝓢uccessor₁] A
    overlap ⦃ `𝓢uccessor₁ ⦄ : 𝓢uccessor₁ A
    overlap ⦃ `[𝓣hick] ⦄ : [𝓣hick] A B
    overlap ⦃ `𝓣hick ⦄ : 𝓣hick A B
    overlap ⦃ `[𝓣hin] ⦄ : [𝓣hin] A B
    overlap ⦃ `𝓣hin ⦄ : 𝓣hin A B
    overlap ⦃ `[𝓘njectivity₂,₁] ⦄ : ∀ {m} → [𝓘njectivity₂,₁] (𝔱hin A B m) _≈B_ _≈B_
    overlap ⦃ `𝓘njectivity₂,₁ ⦄   : ∀ {m} → 𝓘njectivity₂,₁ (𝔱hin A B m) _≈B_ _≈B_
    overlap ⦃ `[𝓒heck] ⦄ : [𝓒heck] A B C
    overlap ⦃ `𝓒heck ⦄ : 𝓒heck A B C
    overlap ⦃ `[𝓣hick/thin=1] ⦄ : [𝓣hick/thin=1] A B _≈B_
    overlap ⦃ `𝓣hick/thin=1 ⦄ : 𝓣hick/thin=1 A B _≈B_
    overlap ⦃ `[𝓒heck/thin=1] ⦄ : [𝓒heck/thin=1] A B C _≈C_
    overlap ⦃ `𝓟ure ⦄ : 𝓟ure C
    overlap ⦃ `𝓒heck/thin=1 ⦄ : 𝓒heck/thin=1 A B C _≈C_

record Thickandthin x a b ℓb c ℓc : Ø ↑̂ (x ∙̂ a ∙̂ b ∙̂ ℓb ∙̂ c ∙̂ ℓc) where
  constructor ∁
  field
    {X} : Ø x
    A : X → Ø a
    B : X → Ø b
    _≈B_ : ∀ {x} → B x → B x → Ø ℓb
    C : Ø b → Ø c
    _≈C_ : ∀ {x} → C (B x) → C (B x) → Ø ℓc
    ⦃ `IsThickandthin ⦄ : IsThickandthin A B _≈B_ C _≈C_


open import Mockingbird.Forest using (Forest)

-- Aristocratic Birds
module Mockingbird.Problems.Chapter19 {ℓb ℓ} (forest : Forest {ℓb} {ℓ}) where

open import Data.Product using (_×_; _,_; ∃-syntax)
open import Function using (_$_)
open import Level using (_⊔_)
open import Data.Vec using ([]; _∷_)
open import Relation.Unary using (Pred; _∈_; _⊆_; _⊇_)

open Forest forest
open import Mockingbird.Forest.Birds forest
open import Mockingbird.Forest.Combination.Vec forest
open import Mockingbird.Forest.Combination.Vec.Properties forest
open import Mockingbird.Forest.Extensionality forest
import Mockingbird.Problems.Chapter11 forest as Chapter₁₁
import Mockingbird.Problems.Chapter12 forest as Chapter₁₂

problem₁″ : ⦃ _ : HasEagle ⦄ ⦃ _ : HasCardinalOnceRemoved ⦄ ⦃ _ : HasCardinalTwiceRemoved ⦄ ⦃ _ : HasWarbler ⦄ → HasJaybird
problem₁″ = record
  { J = C** ∙ (W ∙ (C* ∙ E))
  ; isJaybird = λ x y z w → begin
      C** ∙ (W ∙ (C* ∙ E)) ∙ x ∙ y ∙ z ∙ w  ≈⟨ isCardinalTwiceRemoved (W ∙ (C* ∙ E)) x y z w ⟩
      W ∙ (C* ∙ E) ∙ x ∙ y ∙ w ∙ z          ≈⟨ congʳ $ congʳ $ congʳ $ isWarbler (C* ∙ E) x ⟩
      C* ∙ E ∙ x ∙ x ∙ y ∙ w ∙ z            ≈⟨ congʳ $ congʳ $ isCardinalOnceRemoved E x x y ⟩
      E ∙ x ∙ y ∙ x ∙ w ∙ z                 ≈⟨ isEagle x y x w z ⟩
      x ∙ y ∙ (x ∙ w ∙ z)                   ∎
  }

problem₁′ : ⦃ _ : HasBluebird ⦄ ⦃ _ : HasCardinal ⦄ ⦃ _ : HasWarbler ⦄ → HasJaybird
problem₁′ = record
  { J = B ∙ (B ∙ C) ∙ (W ∙ (B ∙ C ∙ (B ∙ (B ∙ B ∙ B))))
  ; isJaybird = isJaybird ⦃ problem₁″ ⦄
  } where
  instance
    hasEagle = Chapter₁₁.problem₇
    hasCardinalOnceRemoved = Chapter₁₁.problem₃₁
    hasCardinalTwiceRemoved = Chapter₁₁.problem₃₅-C**

problem₁ : ⦃ _ : HasBluebird ⦄ ⦃ _ : HasThrush ⦄ ⦃ _ : HasMockingbird ⦄ → HasJaybird
problem₁ = problem₁′
  where
    instance
      hasCardinal = Chapter₁₁.problem₂₁′
      hasWarbler = Chapter₁₂.problem₇

problem₂ : ⦃ _ : HasJaybird ⦄ ⦃ _ : HasIdentity ⦄ → HasQuixoticBird
problem₂ = record
  { Q₁ = J ∙ I
  ; isQuixoticBird = λ x y z → begin
      J ∙ I ∙ x ∙ y ∙ z    ≈⟨ isJaybird I x y z ⟩
      I ∙ x ∙ (I ∙ z ∙ y)  ≈⟨ congʳ $ isIdentity x ⟩
      x ∙ (I ∙ z ∙ y)      ≈⟨ congˡ $ congʳ $ isIdentity z ⟩
      x ∙ (z ∙ y)          ∎
  }

problem₃ : ⦃ _ : HasQuixoticBird ⦄ ⦃ _ : HasIdentity ⦄ → HasThrush
problem₃ = record
  { T = Q₁ ∙ I
  ; isThrush = λ x y → begin
      Q₁ ∙ I ∙ x ∙ y  ≈⟨ isQuixoticBird I x y ⟩
      I ∙ (y ∙ x)     ≈⟨ isIdentity $ y ∙ x ⟩
      y ∙ x           ∎
  }

problem₄ : ⦃ _ : HasJaybird ⦄ ⦃ _ : HasThrush ⦄ → HasRobin
problem₄ = record
  { R = J ∙ T
  ; isRobin = λ x y z → begin
      J ∙ T ∙ x ∙ y ∙ z    ≈⟨ isJaybird T x y z ⟩
      T ∙ x ∙ (T ∙ z ∙ y)  ≈⟨ isThrush x $ T ∙ z ∙ y ⟩
      T ∙ z ∙ y ∙ x        ≈⟨ congʳ $ isThrush z y ⟩
      y ∙ z ∙ x            ∎
  }

problem₅-C* : ⦃ _ : HasCardinal ⦄ ⦃ _ : HasQuixoticBird ⦄ → HasCardinalOnceRemoved
problem₅-C* = record
  { C* = C ∙ (Q₁ ∙ C)
  ; isCardinalOnceRemoved = λ x y z w → begin
      C ∙ (Q₁ ∙ C) ∙ x ∙ y ∙ z ∙ w  ≈⟨ congʳ $ congʳ $ isCardinal (Q₁ ∙ C) x y ⟩
      Q₁ ∙ C ∙ y ∙ x ∙ z ∙ w        ≈⟨ congʳ $ congʳ $ isQuixoticBird C y x ⟩
      C ∙ (x ∙ y) ∙ z ∙ w           ≈⟨ isCardinal (x ∙ y) z w ⟩
      x ∙ y ∙ w ∙ z                 ∎
  }

problem₅ : ⦃ _ : HasRobin ⦄ ⦃ _ : HasQuixoticBird ⦄ → HasBluebird
problem₅ = record
  { B = C* ∙ Q₁
  ; isBluebird = λ x y z → begin
      C* ∙ Q₁ ∙ x ∙ y ∙ z  ≈⟨ isCardinalOnceRemoved Q₁ x y z ⟩
      Q₁ ∙ x ∙ z ∙ y       ≈⟨ isQuixoticBird x z y ⟩
      x ∙ (y ∙ z)          ∎
  } where
  instance
    hasCardinal = Chapter₁₁.problem₂₁
    hasCardinalOnceRemoved = problem₅-C*

problem₆ : ⦃ _ : HasJaybird ⦄ ⦃ _ : HasBluebird ⦄ ⦃ _ : HasThrush ⦄
  → ∃[ J₁ ] (∀ x y z w → J₁ ∙ x ∙ y ∙ z ∙ w ≈ y ∙ x ∙ (w ∙ x ∙ z))
problem₆ =
  ( B ∙ J ∙ T
  , λ x y z w → begin
      B ∙ J ∙ T ∙ x ∙ y ∙ z ∙ w    ≈⟨ (congʳ $ congʳ $ congʳ $ isBluebird J T x) ⟩
      J ∙ (T ∙ x) ∙ y ∙ z ∙ w      ≈⟨ isJaybird (T ∙ x) y z w ⟩
      T ∙ x ∙ y ∙ (T ∙ x ∙ w ∙ z)  ≈⟨ congʳ $ isThrush x y ⟩
      y ∙ x ∙ (T ∙ x ∙ w ∙ z)      ≈⟨ congˡ $ congʳ $ isThrush x w ⟩
      y ∙ x ∙ (w ∙ x ∙ z)          ∎
  )

problem₇ : ⦃ _ : HasCardinal ⦄ ⦃ _ : HasThrush ⦄
  → ∃[ J₁ ] (∀ x y z w → J₁ ∙ x ∙ y ∙ z ∙ w ≈ y ∙ x ∙ (w ∙ x ∙ z))
  → HasMockingbird
problem₇ (J₁ , isJ₁) = record
  { M = C ∙ (C ∙ (C ∙ J₁ ∙ T) ∙ T) ∙ T
  ; isMockingbird = λ x → begin
      C ∙ (C ∙ (C ∙ J₁ ∙ T) ∙ T) ∙ T ∙ x  ≈⟨ isCardinal _ T x ⟩
      C ∙ (C ∙ J₁ ∙ T) ∙ T ∙ x ∙ T        ≈⟨ congʳ $ isCardinal _ T x ⟩
      C ∙ J₁ ∙ T ∙ x ∙ T ∙ T              ≈⟨ congʳ $ congʳ $ isCardinal J₁ T x ⟩
      J₁ ∙ x ∙ T ∙ T ∙ T                  ≈⟨ isJ₁ x T T T ⟩
      T ∙ x ∙ (T ∙ x ∙ T)                 ≈⟨ isThrush x (T ∙ x ∙ T) ⟩
      T ∙ x ∙ T ∙ x                       ≈⟨ congʳ $ isThrush x T ⟩
      T ∙ x ∙ x                           ≈⟨ isThrush x x ⟩
      x ∙ x                               ∎
  }

module _  ⦃ _ : HasJaybird ⦄ ⦃ hasIdentity′ : HasIdentity ⦄ where
  private
    instance
      hasQuixoticBird = problem₂

  hasThrush : HasThrush
  hasThrush = problem₃

  private
    instance
      _ = hasThrush
      hasRobin = problem₄

  hasBluebird : HasBluebird
  hasBluebird = problem₅

  private
    instance
      _ = hasBluebird
      hasCardinal = Chapter₁₁.problem₂₁′

  hasMockingbird : HasMockingbird
  hasMockingbird = problem₇ problem₆

  hasIdentity : HasIdentity
  hasIdentity = hasIdentity′

-- Equality of predicates.
infix 4 _≐_
_≐_ : ∀ {a ℓ} {A : Set a} (P Q : Pred A ℓ) → Set (a ⊔ ℓ)
P ≐ Q = P ⊆ Q × Q ⊆ P

module _ ⦃ _ : Extensional ⦄
         ⦃ _ : HasBluebird ⦄ ⦃ _ : HasMockingbird ⦄ ⦃ _ : HasThrush ⦄
         ⦃ _ : HasIdentity ⦄ ⦃ _ : HasJaybird ⦄ where
  private
    ⟨B,M,T,I⟩ = ⟨ B ∷ M ∷ T ∷ I ∷ [] ⟩
    ⟨J,I⟩ = ⟨ J ∷ I ∷ [] ⟩

  module _ where
    private
      instance
        hasCardinal = Chapter₁₁.problem₂₁′
        hasWarbler = Chapter₁₂.problem₇

      b : B ∈ ⟨B,M,T,I⟩
      b = [ here refl ]

      m : M ∈ ⟨B,M,T,I⟩
      m = [ there (here refl) ]

      t : T ∈ ⟨B,M,T,I⟩
      t = [ there (there (here refl)) ]

      c : C ∈ ⟨B,M,T,I⟩
      c = b ⟨∙⟩ b ⟨∙⟩ t ⟨∙⟩ (b ⟨∙⟩ b ⟨∙⟩ t) ⟨∙⟩ (b ⟨∙⟩ b ⟨∙⟩ t)

      w : W ∈ ⟨B,M,T,I⟩
      w = b ⟨∙⟩ (t ⟨∙⟩ (b ⟨∙⟩ m ⟨∙⟩ (b ⟨∙⟩ b ⟨∙⟩ t))) ⟨∙⟩ (b ⟨∙⟩ b ⟨∙⟩ t)

    ⟨J,I⟩⊆⟨B,M,T,I⟩ : ⟨J,I⟩ ⊆ ⟨B,M,T,I⟩
    ⟨J,I⟩⊆⟨B,M,T,I⟩ [ here x≈J ] = subst′
      (trans x≈J $ ext′ $ ext′ $ ext′ $ ext′ $ trans
        (isJaybird _ _ _ _)
        (sym $ isJaybird ⦃ problem₁′ ⦄ _ _ _ _))
      (b ⟨∙⟩ (b ⟨∙⟩ c) ⟨∙⟩ (w ⟨∙⟩ (b ⟨∙⟩ c ⟨∙⟩ (b ⟨∙⟩ (b ⟨∙⟩ b ⟨∙⟩ b)))))
    ⟨J,I⟩⊆⟨B,M,T,I⟩ [ there (here x≈I) ] = [ there (there (there (here x≈I))) ]
    ⟨J,I⟩⊆⟨B,M,T,I⟩ (x∈⟨J,I⟩ ⟨∙⟩ y∈⟨J,I⟩ ∣ xy≈z) = ⟨J,I⟩⊆⟨B,M,T,I⟩ x∈⟨J,I⟩ ⟨∙⟩ ⟨J,I⟩⊆⟨B,M,T,I⟩ y∈⟨J,I⟩ ∣ xy≈z

  module _ where
    private
      instance
        hasQuixoticBird = problem₂
        hasRobin = problem₄
        hasCardinal = Chapter₁₁.problem₂₁
        hasCardinalOnceRemoved = problem₅-C*

      j : J ∈ ⟨J,I⟩
      j = [ here refl ]

      i : I ∈ ⟨J,I⟩
      i = [ there (here refl) ]

      q₁ : Q₁ ∈ ⟨J,I⟩
      q₁ = j ⟨∙⟩ i

      t : T ∈ ⟨J,I⟩
      t = subst′ (ext′ (ext′ (trans (isThrush _ _) (sym (isThrush ⦃ problem₃ ⦄ _ _))))) $
        j ⟨∙⟩ i ⟨∙⟩ i

      r : R ∈ ⟨J,I⟩
      r = j ⟨∙⟩ t

      c : C ∈ ⟨J,I⟩
      c = r ⟨∙⟩ r ⟨∙⟩ r

      c* : C* ∈ ⟨J,I⟩
      c* = c ⟨∙⟩ (q₁ ⟨∙⟩ c)

      b : B ∈ ⟨J,I⟩
      b = subst′ (ext′ (ext′ (ext′ (trans (isBluebird _ _ _) (sym (isBluebird ⦃ problem₅ ⦄ _ _ _)))))) $
        c* ⟨∙⟩ q₁

      m : M ∈ ⟨J,I⟩
      m = subst′ (ext′ (trans (isMockingbird _) (sym (isMockingbird ⦃ problem₇ problem₆ ⦄ _)))) $
        c ⟨∙⟩ (c ⟨∙⟩ (c ⟨∙⟩ (b ⟨∙⟩ j ⟨∙⟩ t) ⟨∙⟩ t) ⟨∙⟩ t) ⟨∙⟩ t

    ⟨J,I⟩⊇⟨B,M,T,I⟩ : ⟨J,I⟩ ⊇ ⟨B,M,T,I⟩
    ⟨J,I⟩⊇⟨B,M,T,I⟩ [ here x≈B ] = subst′ x≈B b
    ⟨J,I⟩⊇⟨B,M,T,I⟩ [ there (here x≈M) ] = subst′ x≈M m
    ⟨J,I⟩⊇⟨B,M,T,I⟩ [ there (there (here x≈T)) ] = subst′ x≈T t
    ⟨J,I⟩⊇⟨B,M,T,I⟩ [ there (there (there (here x≈I))) ] = [ there (here x≈I) ]
    ⟨J,I⟩⊇⟨B,M,T,I⟩ (x∈⟨B,M,T,I⟩ ⟨∙⟩ y∈⟨B,M,T,I⟩ ∣ xy≈z) = ⟨J,I⟩⊇⟨B,M,T,I⟩ x∈⟨B,M,T,I⟩ ⟨∙⟩ ⟨J,I⟩⊇⟨B,M,T,I⟩ y∈⟨B,M,T,I⟩ ∣ xy≈z

  ⟨J,I⟩≐⟨B,M,T,I⟩ : ⟨ J ∷ I ∷ [] ⟩ ≐ ⟨ B ∷ M ∷ T ∷ I ∷ [] ⟩
  ⟨J,I⟩≐⟨B,M,T,I⟩ = (⟨J,I⟩⊆⟨B,M,T,I⟩ , ⟨J,I⟩⊇⟨B,M,T,I⟩)


module nodcap.Base where

open import Data.Nat as ℕ using (ℕ; suc; zero)
open import Data.Pos as ℕ⁺ using (ℕ⁺; suc; _+_)
open import Data.List as L using (List; []; _∷_; _++_)
open import Data.List.Any using (Any; here; there)
open import Data.List.Any.Membership.Propositional using (_∈_; _∼[_]_; bag)
open import Data.Product using (_×_; _,_; uncurry; map)
open import Data.Sum using (_⊎_; inj₁; inj₂)
open import Function using (id; _$_)
open import Relation.Binary.PropositionalEquality as P using (_≡_)

-- Types.

data Type : Set where
  𝟏 : Type
  ⊥ : Type
  𝟎 : Type
  ⊤ : Type
  _⊗_ : (A B : Type) → Type
  _⅋_ : (A B : Type) → Type
  _⊕_ : (A B : Type) → Type
  _&_ : (A B : Type) → Type
  ![_]_ : (n : ℕ⁺) (A : Type) → Type
  ?[_]_ : (n : ℕ⁺) (A : Type) → Type

-- Duality.

_^ : Type → Type
𝟏 ^ = ⊥
⊥ ^ = 𝟏
𝟎 ^ = ⊤
⊤ ^ = 𝟎
(A ⊗ B) ^ = (A ^) ⅋ (B ^)
(A ⅋ B) ^ = (A ^) ⊗ (B ^)
(A ⊕ B) ^ = (A ^) & (B ^)
(A & B) ^ = (A ^) ⊕ (B ^)
(![ n ] A) ^ = ?[ n ] (A ^)
(?[ n ] A) ^ = ![ n ] (A ^)

^-inv : (A : Type) → A ^ ^ ≡ A
^-inv 𝟏 = P.refl
^-inv ⊥ = P.refl
^-inv 𝟎 = P.refl
^-inv ⊤ = P.refl
^-inv (A ⊗ B) = P.cong₂ _⊗_ (^-inv A) (^-inv B)
^-inv (A ⅋ B) = P.cong₂ _⅋_ (^-inv A) (^-inv B)
^-inv (A ⊕ B) = P.cong₂ _⊕_ (^-inv A) (^-inv B)
^-inv (A & B) = P.cong₂ _&_ (^-inv A) (^-inv B)
^-inv (![ n ] A) = P.cong ![ n ]_ (^-inv A)
^-inv (?[ n ] A) = P.cong ?[ n ]_ (^-inv A)

-- Lollipop.

_⊸_ : (A B : Type) → Type
A ⊸ B = (A ^) ⅋ B

-- Polarity.

data Pos : (A : Type) → Set where
  𝟎 : Pos 𝟎
  𝟏 : Pos 𝟏
  _⊗_ : (A B : Type) → Pos (A ⊗ B)
  _⊕_ : (A B : Type) → Pos (A ⊕ B)
  ![_]_ : (n : ℕ⁺) (A : Type) → Pos (![ n ] A)

data Neg : (A : Type) → Set where
  ⊥ : Neg ⊥
  ⊤ : Neg ⊤
  _⅋_ : (A B : Type) → Neg (A ⅋ B)
  _&_ : (A B : Type) → Neg (A & B)
  ?[_]_ : (n : ℕ⁺) (A : Type) → Neg (?[ n ] A)

pol? : (A : Type) → Pos A ⊎ Neg A
pol? 𝟏 = inj₁ 𝟏
pol? ⊥ = inj₂ ⊥
pol? 𝟎 = inj₁ 𝟎
pol? ⊤ = inj₂ ⊤
pol? (A ⊗ B) = inj₁ (A ⊗ B)
pol? (A ⅋ B) = inj₂ (A ⅋ B)
pol? (A ⊕ B) = inj₁ (A ⊕ B)
pol? (A & B) = inj₂ (A & B)
pol? (![ n ] A) = inj₁ (![ n ] A)
pol? (?[ n ] A) = inj₂ (?[ n ] A)

^-posneg : {A : Type} (P : Pos A) → Neg (A ^)
^-posneg 𝟎 = ⊤
^-posneg 𝟏 = ⊥
^-posneg (A ⊗ B) = (A ^) ⅋ (B ^)
^-posneg (A ⊕ B) = (A ^) & (B ^)
^-posneg (![ n ] A) = ?[ n ] (A ^)

^-negpos : {A : Type} (N : Neg A) → Pos (A ^)
^-negpos ⊥ = 𝟏
^-negpos ⊤ = 𝟎
^-negpos (A ⅋ B) = (A ^) ⊗ (B ^)
^-negpos (A & B) = (A ^) ⊕ (B ^)
^-negpos (?[ n ] A) = ![ n ] (A ^)

-- Environments.

Environment : Set
Environment = List Type

-- Injectivity.

private
  infix 10 _≈_

  _≈_ : Type → Type → Set
  A ≈ B = A ≡ B

⊗-inj : {A B C D : Type} → A ⊗ B ≈ C ⊗ D → A ≈ C × B ≈ D
⊗-inj P.refl = P.refl , P.refl

⅋-inj : {A B C D : Type} → A ⅋ B ≈ C ⅋ D → A ≈ C × B ≈ D
⅋-inj P.refl = P.refl , P.refl

⊕-inj : {A B C D : Type} → A ⊕ B ≈ C ⊕ D → A ≈ C × B ≈ D
⊕-inj P.refl = P.refl , P.refl

&-inj : {A B C D : Type} → A & B ≈ C & D → A ≈ C × B ≈ D
&-inj P.refl = P.refl , P.refl

!-inj : {A B : Type} {m n : ℕ⁺} → ![ m ] A ≈ ![ n ] B → m ≡ n × A ≈ B
!-inj P.refl = P.refl , P.refl

?-inj : {A B : Type} {m n : ℕ⁺} → ?[ m ] A ≈ ?[ n ] B → m ≡ n × A ≈ B
?-inj P.refl = P.refl , P.refl

-- This is one of those proofs for which I wish Agda had tactics.

^-inj : {A B : Type} → A ^ ≈ B ^ → A ≈ B
^-inj {A = 𝟏}        {B = 𝟏}         p = P.refl
^-inj {A = 𝟏}        {B = ⊥}         ()
^-inj {A = 𝟏}        {B = 𝟎}         ()
^-inj {A = 𝟏}        {B = ⊤}         ()
^-inj {A = 𝟏}        {B = C ⊗ D}     ()
^-inj {A = 𝟏}        {B = C ⅋ D}     ()
^-inj {A = 𝟏}        {B = C ⊕ D}     ()
^-inj {A = 𝟏}        {B = C & D}     ()
^-inj {A = 𝟏}        {B = ![ n ] C}  ()
^-inj {A = 𝟏}        {B = ?[ n ] C}  ()
^-inj {A = ⊥}        {B = 𝟏}         ()
^-inj {A = ⊥}        {B = ⊥}         p = P.refl
^-inj {A = ⊥}        {B = 𝟎}         ()
^-inj {A = ⊥}        {B = ⊤}         ()
^-inj {A = ⊥}        {B = C ⊗ D}     ()
^-inj {A = ⊥}        {B = C ⅋ D}     ()
^-inj {A = ⊥}        {B = C ⊕ D}     ()
^-inj {A = ⊥}        {B = C & D}     ()
^-inj {A = ⊥}        {B = ![ n ] C}  ()
^-inj {A = ⊥}        {B = ?[ n ] C}  ()
^-inj {A = 𝟎}        {B = 𝟏}         ()
^-inj {A = 𝟎}        {B = ⊥}         ()
^-inj {A = 𝟎}        {B = 𝟎}         p = P.refl
^-inj {A = 𝟎}        {B = ⊤}         ()
^-inj {A = 𝟎}        {B = C ⊗ D}     ()
^-inj {A = 𝟎}        {B = C ⅋ D}     ()
^-inj {A = 𝟎}        {B = C ⊕ D}     ()
^-inj {A = 𝟎}        {B = C & D}     ()
^-inj {A = 𝟎}        {B = ![ n ] C}  ()
^-inj {A = 𝟎}        {B = ?[ n ] C}  ()
^-inj {A = ⊤}        {B = 𝟏}         ()
^-inj {A = ⊤}        {B = ⊥}         ()
^-inj {A = ⊤}        {B = 𝟎}         ()
^-inj {A = ⊤}        {B = ⊤}         p = P.refl
^-inj {A = ⊤}        {B = C ⊗ D}     ()
^-inj {A = ⊤}        {B = C ⅋ D}     ()
^-inj {A = ⊤}        {B = C ⊕ D}     ()
^-inj {A = ⊤}        {B = C & D}     ()
^-inj {A = ⊤}        {B = ![ n ] C}  ()
^-inj {A = ⊤}        {B = ?[ n ] C}  ()
^-inj {A = A ⊗ B}    {B = 𝟏}         ()
^-inj {A = A ⊗ B}    {B = ⊥}         ()
^-inj {A = A ⊗ B}    {B = 𝟎}         ()
^-inj {A = A ⊗ B}    {B = ⊤}         ()
^-inj {A = A ⊗ B}    {B = C ⊗ D}     p = uncurry (P.cong₂ _⊗_) (map ^-inj ^-inj (⅋-inj p))
^-inj {A = A ⊗ B}    {B = C ⅋ D}     ()
^-inj {A = A ⊗ B}    {B = C ⊕ D}     ()
^-inj {A = A ⊗ B}    {B = C & D}     ()
^-inj {A = A ⊗ B}    {B = ![ n ] C}  ()
^-inj {A = A ⊗ B}    {B = ?[ n ] C}  ()
^-inj {A = A ⅋ B}    {B = 𝟏}         ()
^-inj {A = A ⅋ B}    {B = ⊥}         ()
^-inj {A = A ⅋ B}    {B = 𝟎}         ()
^-inj {A = A ⅋ B}    {B = ⊤}         ()
^-inj {A = A ⅋ B}    {B = C ⊗ D}     ()
^-inj {A = A ⅋ B}    {B = C ⅋ D}     p = uncurry (P.cong₂ _⅋_) (map ^-inj ^-inj (⊗-inj p))
^-inj {A = A ⅋ B}    {B = C ⊕ D}     ()
^-inj {A = A ⅋ B}    {B = C & D}     ()
^-inj {A = A ⅋ B}    {B = ![ n ] C}  ()
^-inj {A = A ⅋ B}    {B = ?[ n ] C}  ()
^-inj {A = A ⊕ B}    {B = 𝟏}         ()
^-inj {A = A ⊕ B}    {B = ⊥}         ()
^-inj {A = A ⊕ B}    {B = 𝟎}         ()
^-inj {A = A ⊕ B}    {B = ⊤}         ()
^-inj {A = A ⊕ B}    {B = C ⊗ D}     ()
^-inj {A = A ⊕ B}    {B = C ⅋ D}     ()
^-inj {A = A ⊕ B}    {B = C ⊕ D}     p = uncurry (P.cong₂ _⊕_) (map ^-inj ^-inj (&-inj p))
^-inj {A = A ⊕ B}    {B = C & D}     ()
^-inj {A = A ⊕ B}    {B = ![ n ] C}  ()
^-inj {A = A ⊕ B}    {B = ?[ n ] C}  ()
^-inj {A = A & B}    {B = 𝟏}         ()
^-inj {A = A & B}    {B = ⊥}         ()
^-inj {A = A & B}    {B = 𝟎}         ()
^-inj {A = A & B}    {B = ⊤}         ()
^-inj {A = A & B}    {B = C ⊗ D}     ()
^-inj {A = A & B}    {B = C ⅋ D}     ()
^-inj {A = A & B}    {B = C ⊕ D}     ()
^-inj {A = A & B}    {B = C & D}     p = uncurry (P.cong₂ _&_) (map ^-inj ^-inj (⊕-inj p))
^-inj {A = A & B}    {B = ![ n ] C}  ()
^-inj {A = A & B}    {B = ?[ n ] C}  ()
^-inj {A = ![ m ] A} {B = 𝟏}         ()
^-inj {A = ![ m ] A} {B = ⊥}         ()
^-inj {A = ![ m ] A} {B = 𝟎}         ()
^-inj {A = ![ m ] A} {B = ⊤}         ()
^-inj {A = ![ m ] A} {B = C ⊗ D}     ()
^-inj {A = ![ m ] A} {B = C ⅋ D}     ()
^-inj {A = ![ m ] A} {B = C ⊕ D}     ()
^-inj {A = ![ m ] A} {B = C & D}     ()
^-inj {A = ![ m ] A} {B = ![ n ] C}  p = uncurry (P.cong₂ ![_]_) (map id ^-inj (?-inj p))
^-inj {A = ![ m ] A} {B = ?[ n ] C}  ()
^-inj {A = ?[ m ] A} {B = 𝟏}         ()
^-inj {A = ?[ m ] A} {B = ⊥}         ()
^-inj {A = ?[ m ] A} {B = 𝟎}         ()
^-inj {A = ?[ m ] A} {B = ⊤}         ()
^-inj {A = ?[ m ] A} {B = C ⊗ D}     ()
^-inj {A = ?[ m ] A} {B = C ⅋ D}     ()
^-inj {A = ?[ m ] A} {B = C ⊕ D}     ()
^-inj {A = ?[ m ] A} {B = C & D}     ()
^-inj {A = ?[ m ] A} {B = ![ n ] C}  ()
^-inj {A = ?[ m ] A} {B = ?[ n ] C}  p = uncurry (P.cong₂ ?[_]_) (map id ^-inj (!-inj p))


{-# OPTIONS --safe #-}

module Definition.Typed.Consequences.NeTypeEq where

open import Definition.Untyped
open import Definition.Typed
open import Definition.Typed.Properties
open import Definition.Typed.Weakening
open import Definition.Typed.Consequences.Syntactic
open import Definition.Typed.Consequences.Injectivity
open import Definition.Typed.Consequences.Substitution

open import Tools.Product
import Tools.PropositionalEquality as PE

-- to be moved in Untyped

typelevel-injectivity : ∀ {r r' l l'} → [ r , l ] PE.≡ [ r' , l' ] → r PE.≡ r' × l PE.≡ l'
typelevel-injectivity PE.refl = PE.refl , PE.refl

-- Helper function for the same variable instance of a context have equal types.
varTypeEq′ : ∀ {n R rR T rT Γ} → n ∷ R ^ rR ∈ Γ → n ∷ T ^ rT ∈ Γ → R PE.≡ T × rR PE.≡ rT
varTypeEq′ here here = PE.refl , PE.refl
varTypeEq′ (there n∷R) (there n∷T) with varTypeEq′ n∷R n∷T
... | PE.refl , PE.refl = PE.refl , PE.refl

-- The same variable instance of a context have equal types.
varTypeEq : ∀ {x A B rA rB Γ} → Γ ⊢ A ^ rA → Γ ⊢ B ^ rB
          → x ∷ A ^ rA ∈ Γ
          → x ∷ B ^ rB ∈ Γ
          → Γ ⊢ A ≡ B ^ rA × rA PE.≡ rB
varTypeEq A B x∷A x∷B with varTypeEq′ x∷A x∷B
... | PE.refl , PE.refl = refl A , PE.refl

-- The same neutral term have equal types.
-- to use this with different relevances rA rB we need unicity of relevance for types
neTypeEq : ∀ {t A B rA lA lA' Γ} → Neutral t → Γ ⊢ t ∷ A ^ [ rA , lA ] → Γ ⊢ t ∷ B ^ [ rA , lA' ] →
  lA PE.≡ lA' × Γ ⊢ A ≡ B ^ [ rA , lA ]
neTypeEq (var x) (var x₁ x₂) (var x₃ x₄) =
  let V , e = varTypeEq (syntacticTerm (var x₃ x₂)) (syntacticTerm (var x₃ x₄)) x₂ x₄
      _ , el = typelevel-injectivity e
  in el , V
neTypeEq (∘ₙ neT) (t∷A ∘ⱼ t∷A₁) (t∷B ∘ⱼ t∷B₁) with neTypeEq neT t∷A t∷B
... | e , q = let _ , _ , _ , elG , w = injectivity q
              in PE.cong _ elG , substTypeEq w (genRefl t∷A₁)
neTypeEq (natrecₙ neT) (natrecⱼ x t∷A t∷A₁ t∷A₂) (natrecⱼ x₁ t∷B t∷B₁ t∷B₂) =
  PE.refl , refl (substType x₁ t∷B₂)
neTypeEq Emptyrecₙ (Emptyrecⱼ x t∷A) (Emptyrecⱼ x₁ t∷B) =
  PE.refl , refl x₁
neTypeEq (Idₙ X) (Idⱼ Y Y₁ Y₂) (Idⱼ Z Z₁ Z₂) =
  let e , q = neTypeEq X Y Z
      el = next-inj e
  in e , PE.subst (λ l → _ ⊢ _ ≡ SProp l ^ _) el (refl (Ugenⱼ (wfTerm Y) ) )
neTypeEq (Idℕₙ X) (Idⱼ Y Y₁ Y₂) (Idⱼ Z Z₁ Z₂) =
  let e , q = neTypeEq X Y₁ Z₁
      el = ιinj e
  in PE.cong next el , PE.subst (λ l → _ ⊢ _ ≡ SProp l ^ _) el (refl (Ugenⱼ (wfTerm Y) ) )
neTypeEq (Idℕ0ₙ X) (Idⱼ Y Y₁ Y₂) (Idⱼ Z Z₁ Z₂) =
  let e , q = neTypeEq X Y₂ Z₂
      el = ιinj e
  in PE.cong next el , PE.subst (λ l → _ ⊢ _ ≡ SProp l ^ _) el (refl (Ugenⱼ (wfTerm Y) ) )
neTypeEq (IdℕSₙ X) (Idⱼ Y Y₁ Y₂) (Idⱼ Z Z₁ Z₂) =
  let e , q = neTypeEq X Y₂ Z₂
      el = ιinj e
  in PE.cong next el , PE.subst (λ l → _ ⊢ _ ≡ SProp l ^ _) el (refl (Ugenⱼ (wfTerm Y) ) )
neTypeEq (IdUₙ X) (Idⱼ Y Y₁ Y₂) (Idⱼ Z Z₁ Z₂) =
  let e , q = neTypeEq X Y₁ Z₁
      el = ιinj e
  in PE.cong next el , PE.subst (λ l → _ ⊢ _ ≡ SProp l ^ _) el (refl (Ugenⱼ (wfTerm Y) ) )
neTypeEq (IdUℕₙ X) (Idⱼ Y Y₁ Y₂) (Idⱼ Z Z₁ Z₂) =
  let e , q = neTypeEq X Y₂ Z₂
      el = ιinj e
  in PE.cong next el , PE.subst (λ l → _ ⊢ _ ≡ SProp l ^ _) el (refl (Ugenⱼ (wfTerm Y) ) )
neTypeEq (IdUΠₙ X) (Idⱼ Y Y₁ Y₂) (Idⱼ Z Z₁ Z₂) =
  let e , q = neTypeEq X Y₂ Z₂
      el = ιinj e
  in PE.cong next el , PE.subst (λ l → _ ⊢ _ ≡ SProp l ^ _) el (refl (Ugenⱼ (wfTerm Y) ) )
neTypeEq X (castⱼ Y Y₁ Y₂ Y₃)  (castⱼ Z Z₁ Z₂ Z₃) = PE.refl , refl (univ Y₁)
neTypeEq x (conv t∷A x₁) t∷B =
  let e , q = neTypeEq x t∷A t∷B
  in e , trans (sym x₁) q
neTypeEq x t∷A (conv t∷B x₃) =
  let e , q = neTypeEq x t∷A t∷B
  in e , trans q (PE.subst (λ l → _ ⊢ _ ≡ _ ^ [ _ , l ]) (PE.sym e) x₃)

natTypeEq : ∀ {A rA lA Γ} → Γ ⊢ ℕ ∷ A ^ [ rA , lA ] → rA PE.≡ ! × lA PE.≡ ι ¹ × Γ ⊢ A ≡ U ⁰ ^ [ ! , ι ¹ ]
natTypeEq (ℕⱼ x) = PE.refl , PE.refl , refl (univ (univ 0<1 x))
natTypeEq (conv X x) = let eqrA , eqlA , eqAU = natTypeEq X in eqrA , eqlA ,
  trans (sym (PE.subst (λ l → _ ⊢ _ ≡ _ ^ [ _ , l ] ) eqlA (PE.subst (λ r → _ ⊢ _ ≡ _ ^ [ r , _ ]) eqrA x))) eqAU

emptyTypeEq : ∀ {A rA lA Γ l} → Γ ⊢ Empty l ∷ A ^ [ rA , lA ] →
  rA PE.≡ ! × lA PE.≡ next l × Γ ⊢ A ≡ SProp l ^ [ ! , next l ]
emptyTypeEq (Emptyⱼ x) = PE.refl , PE.refl , refl (Ugenⱼ x)
emptyTypeEq (conv X x) = let eqrA , eqlA , eqAU = emptyTypeEq X in eqrA , eqlA ,
 trans (sym (PE.subst (λ l → _ ⊢ _ ≡ _ ^ [ _ , l ] ) eqlA (PE.subst (λ r → _ ⊢ _ ≡ _ ^ [ r , _ ]) eqrA x))) eqAU


-- Andreas, 2017-10-04, issue #2752, report and test case by nad
--
-- Problem was: instance does not distribute into mutual blocks.

open import Agda.Builtin.List
open import Agda.Builtin.Size

mutual

  data Rose (i : Size) (A : Set) : Set where
    node : List (Rose′ i A) → Rose i A

  data Rose′ (i : Size) (A : Set) : Set where
    delay : {j : Size< i} → Rose j A → Rose′ i A

record Map (F : Set → Set) : Set₁ where
  field
    map : {A B : Set} → (A → B) → F A → F B

open Map ⦃ … ⦄ public

instance

  Map-List : Map List
  Map.map Map-List = λ where
    f []       → []
    f (x ∷ xs) → f x ∷ map f xs

instance

  mutual

    Map-Rose : ∀ {i} → Map (Rose i)
    Map.map Map-Rose f (node xs) = node (map (map f) xs)

    Map-Rose′ : ∀ {i} → Map (Rose′ i)
    Map.map Map-Rose′ f (delay t) = delay (map f t)

-- Was: unresolved instance arguments.

-- Should succeed.


module empty where

open import level

----------------------------------------------------------------------
-- datatypes
----------------------------------------------------------------------

data ⊥ {ℓ : Level} : Set ℓ where

----------------------------------------------------------------------
-- syntax
----------------------------------------------------------------------

----------------------------------------------------------------------
-- theorems
----------------------------------------------------------------------
⊥-elim : ∀{ℓ} → ⊥ {ℓ} → ∀{ℓ'}{P : Set ℓ'} → P
⊥-elim ()


------------------------------------------------------------------------
-- The Agda standard library
--
-- An irrelevant version of ⊥-elim
------------------------------------------------------------------------

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

module Data.Empty.Irrelevant where

open import Data.Empty hiding (⊥-elim)

⊥-elim : ∀ {w} {Whatever : Set w} → .⊥ → Whatever
⊥-elim ()


{-# OPTIONS --safe --warning=error #-}

open import Numbers.Naturals.Semiring
open import Numbers.Naturals.Order
open import Sets.FinSet.Definition
open import LogicalFormulae
open import Numbers.Naturals.WithK
open import Sets.FinSet.Lemmas

module Sets.FinSetWithK where

private
  sgEq : {l m : _} {L : Set l} → {pr : L → Set m} → {a b : Sg L pr} → (underlying a ≡ underlying b) → ({c : L} → (r s : pr c) → r ≡ s) → (a ≡ b)
  sgEq {l} {m} {L} {prop} {(a , b1)} {(.a , b)} refl pr2 with pr2 {a} b b1
  sgEq {l} {m} {L} {prop} {(a , b1)} {(.a , .b1)} refl pr2 | refl = refl

finNotEqualsRefl : {n : ℕ} {a b : FinSet (succ n)} → (p1 p2 : FinNotEquals a b) → p1 ≡ p2
finNotEqualsRefl {.1} {.fzero} {.(fsucc fzero)} (fne2 .fzero .(fsucc fzero) (inl (refl ,, refl))) (fne2 .fzero .(fsucc fzero) (inl (refl ,, refl))) = refl
finNotEqualsRefl {.1} {.fzero} {.(fsucc fzero)} (fne2 .fzero .(fsucc fzero) (inl (refl ,, refl))) (fne2 .fzero .(fsucc fzero) (inr (() ,, snd)))
finNotEqualsRefl {.1} {.(fsucc fzero)} {.fzero} (fne2 .(fsucc fzero) .fzero (inr (refl ,, refl))) (fne2 .(fsucc fzero) .fzero (inl (() ,, snd)))
finNotEqualsRefl {.1} {.(fsucc fzero)} {.fzero} (fne2 .(fsucc fzero) .fzero (inr (refl ,, refl))) (fne2 .(fsucc fzero) .fzero (inr (refl ,, refl))) = refl
finNotEqualsRefl {.(succ (succ _))} {.fzero} {.(fsucc a)} (fneN .fzero .(fsucc a) (inl (inl (refl ,, (a , refl))))) (fneN .fzero .(fsucc a) (inl (inl (refl ,, (.a , refl))))) = refl
finNotEqualsRefl {.(succ (succ _))} {.fzero} {.(fsucc a)} (fneN .fzero .(fsucc a) (inl (inl (refl ,, (a , refl))))) (fneN .fzero .(fsucc a) (inl (inr ((a₁ , ()) ,, snd))))
finNotEqualsRefl {.(succ (succ _))} {.fzero} {.(fsucc a)} (fneN .fzero .(fsucc a) (inl (inl (refl ,, (a , refl))))) (fneN .fzero .(fsucc a) (inr ((fst ,, snd) , b))) = exFalso q
  where
    p : fzero ≡ fsucc fst
    p = _&_&_.one b
    q : False
    q with p
    ... | ()
finNotEqualsRefl {.(succ (succ _))} {.(fsucc a)} {.fzero} (fneN .(fsucc a) .fzero (inl (inr ((a , refl) ,, refl)))) (fneN .(fsucc a) .fzero (inl (inl (() ,, snd))))
finNotEqualsRefl {.(succ (succ _))} {.(fsucc a)} {.fzero} (fneN .(fsucc a) .fzero (inl (inr ((a , refl) ,, refl)))) (fneN .(fsucc a) .fzero (inl (inr ((.a , refl) ,, refl)))) = refl
finNotEqualsRefl {.(succ (succ _))} {.(fsucc a)} {.fzero} (fneN .(fsucc a) .fzero (inl (inr ((a , refl) ,, refl)))) (fneN .(fsucc a) .fzero (inr ((fst ,, snd) , b))) = exFalso q
  where
    p : fzero ≡ fsucc snd
    p = _&_&_.two b
    q : False
    q with p
    ... | ()
finNotEqualsRefl {.(succ (succ _))} {a} {b} (fneN a b (inr (record { fst = fst ; snd = snd₁ } , snd))) (fneN .a .b (inl (inl (fst1 ,, snd₂)))) = exFalso q
  where
    p : fzero ≡ fsucc fst
    p = transitivity (equalityCommutative fst1) (_&_&_.one snd)
    q : False
    q with p
    ... | ()
finNotEqualsRefl {.(succ (succ _))} {a} {b} (fneN a b (inr (record { fst = fst ; snd = snd1 } , snd))) (fneN .a .b (inl (inr (fst₁ ,, snd2)))) = exFalso q
  where
    p : fzero ≡ fsucc snd1
    p = transitivity (equalityCommutative snd2) (_&_&_.two snd)
    q : False
    q with p
    ... | ()
finNotEqualsRefl {.(succ (succ _))} {.fzero} {b} (fneN fzero b (inr (record { fst = fst ; snd = snd₁ } , snd))) (fneN .fzero .b (inr ((fst1 ,, snd₂) , b1))) = exFalso q
  where
    p : fzero ≡ fsucc fst1
    p = _&_&_.one b1
    q : False
    q with p
    ... | ()
finNotEqualsRefl {.(succ (succ _))} {.(fsucc a)} {.fzero} (fneN (fsucc a) fzero (inr (record { fst = fst ; snd = snd₁ } , snd))) (fneN .(fsucc a) .fzero (inr ((fst₁ ,, snd2) , b1))) = exFalso q
  where
    p : fzero ≡ fsucc snd2
    p = _&_&_.two b1
    q : False
    q with p
    ... | ()
finNotEqualsRefl {.(succ (succ _))} {.(fsucc a)} {.(fsucc b)} (fneN (fsucc a) (fsucc b) (inr (record { fst = fst ; snd = snd1 } , snd))) (fneN .(fsucc a) .(fsucc b) (inr ((fst1 ,, snd2) , b1))) = ans
  where
    t : a ≡ fst1
    t = fsuccInjective (_&_&_.one b1)
    t' : a ≡ fst
    t' = fsuccInjective (_&_&_.one snd)
    u : b ≡ snd1
    u = fsuccInjective (_&_&_.two snd)
    u' : b ≡ snd2
    u' = fsuccInjective (_&_&_.two b1)
    equality : {c : FinSet (succ (succ _)) && FinSet (succ (succ _))} → (r1 s : (fsucc a ≡ fsucc (_&&_.fst c)) & (fsucc b ≡ fsucc (_&&_.snd c)) & FinNotEquals (_&&_.fst c) (_&&_.snd c)) → r1 ≡ s
    equality record { one = refl ; two = refl ; three = q } record { one = refl ; two = refl ; three = q' } = applyEquality (λ t → record { one = refl ; two = refl ; three = t }) (finNotEqualsRefl q q')
    r : (fst ,, snd1) ≡ (fst1 ,, snd2)
    r rewrite equalityCommutative t | equalityCommutative t' | equalityCommutative u | equalityCommutative u' = refl
    ans : fneN (fsucc a) (fsucc b) (inr ((fst ,, snd1) , snd)) ≡ fneN (fsucc a) (fsucc b) (inr ((fst1 ,, snd2) , b1))
    ans = applyEquality (λ t → fneN (fsucc a) (fsucc b) (inr t)) (sgEq r equality)