module Pi-1 where

open import Data.Empty
open import Data.Unit
open import Data.Product
open import Relation.Binary.PropositionalEquality

open import Groupoid

import Pi-2 as P

--infixr 10 _◎_
--infixr 30 _⟷_

------------------------------------------------------------------------------
-- Level -1:
-- Types are -1 groupoids or equivalently all types are mere propositions
--
-- Types and values are defined without references to programs
-- Types include PATHs from the previous level in addition to the usual types

data U : Set where
  ZERO  : U
  ONE   : U
  TIMES : U → U → U
  PATH  : {t₁ t₂ : P.U•} → (t₁ P.⟷ t₂) → U

data Path {t₁ t₂ : P.U•} : P.Space t₁ → P.Space t₂ → Set where
  path : (c : t₁ P.⟷ t₂) → Path (P.point t₁) (P.point t₂)

⟦_⟧ : U → Set
⟦ ZERO ⟧             = ⊥
⟦ ONE ⟧              = ⊤
⟦ TIMES t₁ t₂ ⟧      = ⟦ t₁ ⟧ × ⟦ t₂ ⟧
⟦ PATH {t₁} {t₂} c ⟧ = Path {t₁} {t₂} (P.point t₁) (P.point t₂)

-- Proof that all types are mere propositions. Def. 3.3.1 in the HoTT book

isMereProposition : (t : U) → (v v' : ⟦ t ⟧) → (v ≡ v')
isMereProposition ZERO ()
isMereProposition ONE tt tt = refl
isMereProposition (TIMES t₁ t₂) (v₁ , v₂) (v₁' , v₂') =
 cong₂ _,′_ (isMereProposition t₁ v₁ v₁') (isMereProposition t₂ v₂ v₂')
isMereProposition (PATH {t₁} {t₂} c) (path c₁) (path c₂) = {!!}

-- c₁ : t₁ ⟷ t₂ in Pi-2
-- c₂ : t₁ ⟷ t₂ in Pi-2

-- Proof that every type is a groupoid; this does not depend on the
-- definition of programs

isGroupoid : U → 1Groupoid
isGroupoid ZERO               = discrete ⊥
isGroupoid ONE                = discrete ⊤
isGroupoid (TIMES t₁ t₂)      = isGroupoid t₁ ×G isGroupoid t₂
isGroupoid (PATH {t₁} {t₂} c) = {!!}

{--
------------------------------------------------------------------------------
-- Level -1:
-- if types are non-empty they must contain one point only like above
-- types may however be empty
--
-- We can prove that all types are either empty or equivalent to the unit
-- type (i.e., they are mere propositions)

module Pi-1 where

  data U : Set where
    ZERO : U
    ONE : U
    TIMES : U → U → U

  ⟦_⟧ : U → Set
  ⟦ ZERO ⟧ = ⊥
  ⟦ ONE ⟧ = ⊤
  ⟦ TIMES t₁ t₂ ⟧ = ⟦ t₁ ⟧ × ⟦ t₂ ⟧

------------------------------------------------------------------------------
-- Level 0:
-- types may contain any number of points (including none)
-- they are sets
--
-- We define isomorphisms between these sets that are reified to paths in the
-- next level (level 1). These isomorphisms are the combinators for a
-- commutative semiring structure which is sound and complete for sets.

module Pi0 where

  infixr 10 _◎_
  infixr 30 _⟷_

  -- types

  data U : Set where
    ZERO  : U
    ONE   : U
    PLUS  : U → U → U
    TIMES : U → U → U

  -- values

  ⟦_⟧ : U → Set
  ⟦ ZERO ⟧        = ⊥
  ⟦ ONE ⟧         = ⊤
  ⟦ PLUS t₁ t₂ ⟧  = ⟦ t₁ ⟧ ⊎ ⟦ t₂ ⟧
  ⟦ TIMES t₁ t₂ ⟧ = ⟦ t₁ ⟧ × ⟦ t₂ ⟧

  -- pointed types

  record U• : Set where
    constructor •[_,_]
    field
      ∣_∣ : U
      • : ⟦ ∣_∣ ⟧

  open U•

  Space : (t• : U•) → Set
  Space •[ t , v ] = ⟦ t ⟧

  point : (t• : U•) → Space t•
  point •[ t , v ] = v

  -- examples

  ONE• : U•
  ONE• = •[ ONE , tt ]

  BOOL : U
  BOOL = PLUS ONE ONE

  BOOL² : U
  BOOL² = TIMES BOOL BOOL

  TRUE : ⟦ BOOL ⟧
  TRUE = inj₁ tt

  FALSE : ⟦ BOOL ⟧
  FALSE = inj₂ tt

  BOOL•F : U•
  BOOL•F = •[ BOOL , FALSE ]

  BOOL•T : U•
  BOOL•T = •[ BOOL , TRUE ]

  -- isomorphisms between pointed types

  data _⟷_ : U• → U• → Set where
    unite₊  : {t : U•} → •[ PLUS ZERO ∣ t ∣ , inj₂ (• t) ] ⟷ t
    uniti₊  : {t : U•} → t ⟷ •[ PLUS ZERO ∣ t ∣ , inj₂ (• t) ]
    swap1₊  : {t₁ t₂ : U•} → •[ PLUS ∣ t₁ ∣ ∣ t₂ ∣ , inj₁ (• t₁) ] ⟷
                             •[ PLUS ∣ t₂ ∣ ∣ t₁ ∣ , inj₂ (• t₁) ]
    swap2₊  : {t₁ t₂ : U•} → •[ PLUS ∣ t₁ ∣ ∣ t₂ ∣ , inj₂ (• t₂) ] ⟷
                             •[ PLUS ∣ t₂ ∣ ∣ t₁ ∣ , inj₁ (• t₂) ]
    assocl1₊ : {t₁ t₂ t₃ : U•} →
               •[ PLUS ∣ t₁ ∣ (PLUS ∣ t₂ ∣ ∣ t₃ ∣) , inj₁ (• t₁) ] ⟷
               •[ PLUS (PLUS ∣ t₁ ∣ ∣ t₂ ∣) ∣ t₃ ∣ , inj₁ (inj₁ (• t₁)) ]
    assocl2₊ : {t₁ t₂ t₃ : U•} →
               •[ PLUS ∣ t₁ ∣ (PLUS ∣ t₂ ∣ ∣ t₃ ∣) , inj₂ (inj₁ (• t₂)) ] ⟷
               •[ PLUS (PLUS ∣ t₁ ∣ ∣ t₂ ∣) ∣ t₃ ∣ , inj₁ (inj₂ (• t₂)) ]
    assocl3₊ : {t₁ t₂ t₃ : U•} →
               •[ PLUS ∣ t₁ ∣ (PLUS ∣ t₂ ∣ ∣ t₃ ∣) , inj₂ (inj₂ (• t₃)) ] ⟷
               •[ PLUS (PLUS ∣ t₁ ∣ ∣ t₂ ∣) ∣ t₃ ∣ , inj₂ (• t₃) ]
    assocr1₊ : {t₁ t₂ t₃ : U•} →
               •[ PLUS (PLUS ∣ t₁ ∣ ∣ t₂ ∣) ∣ t₃ ∣ , inj₁ (inj₁ (• t₁)) ] ⟷
               •[ PLUS ∣ t₁ ∣ (PLUS ∣ t₂ ∣ ∣ t₃ ∣) , inj₁ (• t₁) ]
    assocr2₊ : {t₁ t₂ t₃ : U•} →
               •[ PLUS (PLUS ∣ t₁ ∣ ∣ t₂ ∣) ∣ t₃ ∣ , inj₁ (inj₂ (• t₂)) ] ⟷
               •[ PLUS ∣ t₁ ∣ (PLUS ∣ t₂ ∣ ∣ t₃ ∣) , inj₂ (inj₁ (• t₂)) ]
    assocr3₊ : {t₁ t₂ t₃ : U•} →
               •[ PLUS (PLUS ∣ t₁ ∣ ∣ t₂ ∣) ∣ t₃ ∣ , inj₂ (• t₃) ] ⟷
               •[ PLUS ∣ t₁ ∣ (PLUS ∣ t₂ ∣ ∣ t₃ ∣) , inj₂ (inj₂ (• t₃)) ]
    unite⋆  : {t : U•} → •[ TIMES ONE ∣ t ∣ , (tt , • t) ] ⟷ t
    uniti⋆  : {t : U•} → t ⟷ •[ TIMES ONE ∣ t ∣ , (tt , • t) ]
    swap⋆   : {t₁ t₂ : U•} → •[ TIMES ∣ t₁ ∣ ∣ t₂ ∣ , (• t₁ , • t₂) ] ⟷
                             •[ TIMES ∣ t₂ ∣ ∣ t₁ ∣ , (• t₂ , • t₁) ]
    assocl⋆ : {t₁ t₂ t₃ : U•} →
           •[ TIMES ∣ t₁ ∣ (TIMES ∣ t₂ ∣ ∣ t₃ ∣) , (• t₁ , (• t₂ , • t₃)) ] ⟷
           •[ TIMES (TIMES ∣ t₁ ∣ ∣ t₂ ∣) ∣ t₃ ∣ , ((• t₁ , • t₂) , • t₃) ]
    assocr⋆ : {t₁ t₂ t₃ : U•} →
           •[ TIMES (TIMES ∣ t₁ ∣ ∣ t₂ ∣) ∣ t₃ ∣ , ((• t₁ , • t₂) , • t₃) ] ⟷
           •[ TIMES ∣ t₁ ∣ (TIMES ∣ t₂ ∣ ∣ t₃ ∣) , (• t₁ , (• t₂ , • t₃)) ]
    distz : {t : U•} {absurd : ⟦ ZERO ⟧} →
            •[ TIMES ZERO ∣ t ∣ , (absurd , • t) ] ⟷ •[ ZERO , absurd ]
    factorz : {t : U•} {absurd : ⟦ ZERO ⟧} →
            •[ ZERO , absurd ] ⟷ •[ TIMES ZERO ∣ t ∣ , (absurd , • t) ]
    dist1   : {t₁ t₂ t₃ : U•} →
              •[ TIMES (PLUS ∣ t₁ ∣ ∣ t₂ ∣) ∣ t₃ ∣ , (inj₁ (• t₁) , • t₃) ] ⟷
              •[ PLUS (TIMES ∣ t₁ ∣ ∣ t₃ ∣) (TIMES ∣ t₂ ∣ ∣ t₃ ∣) ,
                 inj₁ (• t₁ , • t₃) ]
    dist2   : {t₁ t₂ t₃ : U•} →
              •[ TIMES (PLUS ∣ t₁ ∣ ∣ t₂ ∣) ∣ t₃ ∣ , (inj₂ (• t₂) , • t₃) ] ⟷
              •[ PLUS (TIMES ∣ t₁ ∣ ∣ t₃ ∣) (TIMES ∣ t₂ ∣ ∣ t₃ ∣) ,
                 inj₂ (• t₂ , • t₃) ]
    factor1   : {t₁ t₂ t₃ : U•} →
              •[ PLUS (TIMES ∣ t₁ ∣ ∣ t₃ ∣) (TIMES ∣ t₂ ∣ ∣ t₃ ∣) ,
                 inj₁ (• t₁ , • t₃) ] ⟷
              •[ TIMES (PLUS ∣ t₁ ∣ ∣ t₂ ∣) ∣ t₃ ∣ , (inj₁ (• t₁) , • t₃) ]
    factor2   : {t₁ t₂ t₃ : U•} →
              •[ PLUS (TIMES ∣ t₁ ∣ ∣ t₃ ∣) (TIMES ∣ t₂ ∣ ∣ t₃ ∣) ,
                 inj₂ (• t₂ , • t₃) ] ⟷
              •[ TIMES (PLUS ∣ t₁ ∣ ∣ t₂ ∣) ∣ t₃ ∣ , (inj₂ (• t₂) , • t₃) ]
    id⟷    : {t : U•} → t ⟷ t
    sym⟷   : {t₁ t₂ : U•} → (t₁ ⟷ t₂) → (t₂ ⟷ t₁)
    _◎_    : {t₁ t₂ t₃ : U•} → (t₁ ⟷ t₂) → (t₂ ⟷ t₃) → (t₁ ⟷ t₃)
    _⊕1_   : {t₁ t₂ t₃ t₄ : U•} → (t₁ ⟷ t₃) → (t₂ ⟷ t₄) →
             (•[ PLUS ∣ t₁ ∣ ∣ t₂ ∣ , inj₁ (• t₁) ] ⟷
              •[ PLUS ∣ t₃ ∣ ∣ t₄ ∣ , inj₁ (• t₃) ])
    _⊕2_   : {t₁ t₂ t₃ t₄ : U•} → (t₁ ⟷ t₃) → (t₂ ⟷ t₄) →
             (•[ PLUS ∣ t₁ ∣ ∣ t₂ ∣ , inj₂ (• t₂) ] ⟷
              •[ PLUS ∣ t₃ ∣ ∣ t₄ ∣ , inj₂ (• t₄) ])
    _⊗_     : {t₁ t₂ t₃ t₄ : U•} → (t₁ ⟷ t₃) → (t₂ ⟷ t₄) →
            (•[ TIMES ∣ t₁ ∣ ∣ t₂ ∣ , (• t₁ , • t₂ ) ] ⟷
             •[ TIMES ∣ t₃ ∣ ∣ t₄ ∣ , (• t₃ , • t₄ ) ])

  -- examples

  NOT•T : •[ BOOL , TRUE ] ⟷ •[ BOOL , FALSE ]
  NOT•T = swap1₊

  NOT•F : •[ BOOL , FALSE ] ⟷ •[ BOOL , TRUE ]
  NOT•F = swap2₊

  CNOT•Fx : {b : ⟦ BOOL ⟧} →
            •[ BOOL² , (FALSE , b) ] ⟷ •[ BOOL² , (FALSE , b) ]
  CNOT•Fx = dist2 ◎ ((id⟷ ⊗ NOT•F) ⊕2 id⟷) ◎ factor2

  CNOT•TF : •[ BOOL² , (TRUE , FALSE) ] ⟷ •[ BOOL² , (TRUE , TRUE) ]
  CNOT•TF = dist1 ◎
            ((id⟷ ⊗ NOT•F) ⊕1 (id⟷ {•[ TIMES ONE BOOL , (tt , FALSE) ]})) ◎
            factor1

  CNOT•TT : •[ BOOL² , (TRUE , TRUE) ] ⟷ •[ BOOL² , (TRUE , FALSE) ]
  CNOT•TT = dist1 ◎
            ((id⟷ ⊗ NOT•T) ⊕1 (id⟷ {•[ TIMES ONE BOOL , (tt , TRUE) ]})) ◎
            factor1

------------------------------------------------------------------------------
-- Level 1
-- types are 1groupoids; they consist of points with collections of
-- non-trivial paths between them; the paths are defined at the previous
-- level (level 0).
--
-- We define isomorphisms between these level 1 paths that are reified as
-- 2paths in the next level (level 2). These isomorphisms include groupoid
-- combinators and commutative semiring combinators

module Pi1 where

  infixr 10 _◎_
  infixr 30 _⟷_

  -- types

  data U : Set where
    ZERO  : U
    ONE   : U
    PLUS  : U → U → U
    TIMES : U → U → U
    PATH  : {t₁ t₂ : Pi0.U•} → (t₁ Pi0.⟷ t₂) → U

  -- values

  data Path {t₁ t₂ : Pi0.U•} : (Pi0.Space t₁) → (Pi0.Space t₂) → Set where
    path : (c : t₁ Pi0.⟷ t₂) → Path (Pi0.point t₁) (Pi0.point t₂)

  ⟦_⟧ : U → Set
  ⟦ ZERO ⟧             = ⊥
  ⟦ ONE ⟧              = ⊤
  ⟦ PLUS t₁ t₂ ⟧       = ⟦ t₁ ⟧ ⊎ ⟦ t₂ ⟧
  ⟦ TIMES t₁ t₂ ⟧      = ⟦ t₁ ⟧ × ⟦ t₂ ⟧
  ⟦ PATH {t₁} {t₂} c ⟧ = Path {t₁} {t₂} (Pi0.point t₁) (Pi0.point t₂)

  -- pointed types

  record U• : Set where
    constructor •[_,_]
    field
      ∣_∣ : U
      • : ⟦ ∣_∣ ⟧

  open U•

  Space : (t• : U•) → Set
  Space •[ t , p ] = ⟦ t ⟧

  point : (t• : U•) → Space t•
  point •[ t , p ] = p

  _∘_ : {t₁ t₂ t₃ : Pi0.U•} →
        Path (Pi0.point t₁) (Pi0.point t₂) →
        Path (Pi0.point t₂) (Pi0.point t₃) →
        Path (Pi0.point t₁) (Pi0.point t₃)
  path c₁ ∘ path c₂ = path (c₁ Pi0.◎ c₂)

  PathSpace : {t₁ t₂ : Pi0.U•} → (c : t₁ Pi0.⟷ t₂) → U•
  PathSpace c = •[ PATH c , path c ]

  -- examples
  -- several paths between T and F; some of these will be equivalent

  p₁ p₂ p₃ p₄ : Path Pi0.TRUE Pi0.FALSE
  p₁ = path Pi0.swap1₊
  p₂ = path (Pi0.swap1₊ Pi0.◎ Pi0.id⟷)
  p₃ = path (Pi0.swap1₊ Pi0.◎ Pi0.swap2₊ Pi0.◎ Pi0.swap1₊)
  p₄ = path (Pi0.uniti⋆ Pi0.◎
             Pi0.swap⋆ Pi0.◎
             (Pi0.swap1₊ Pi0.⊗ Pi0.id⟷) Pi0.◎
             Pi0.swap⋆ Pi0.◎
             Pi0.unite⋆)

  -- groupoid combinators to reason about id, sym, and trans
  -- commutative semiring combinators for 0, 1, +, *

  data _⟷_ : U• → U• → Set where

    -- common combinators

    id⟷    : {t : U•} → t ⟷ t
    sym⟷   : {t₁ t₂ : U•} → (t₁ ⟷ t₂) → (t₂ ⟷ t₁)
    _◎_     : {t₁ t₂ t₃ : U•} → (t₁ ⟷ t₂) → (t₂ ⟷ t₃) → (t₁ ⟷ t₃)

    -- groupoid combinators

    lidl    : {t₁ t₂ : Pi0.U•} {c : t₁ Pi0.⟷ t₂} →
              PathSpace (Pi0.id⟷ Pi0.◎ c) ⟷ PathSpace c
    lidr    : {t₁ t₂ : Pi0.U•} {c : t₁ Pi0.⟷ t₂} →
              PathSpace c ⟷ PathSpace (Pi0.id⟷ Pi0.◎ c)
    ridl    : {t₁ t₂ : Pi0.U•} {c : t₁ Pi0.⟷ t₂} →
              PathSpace (c Pi0.◎ Pi0.id⟷) ⟷ PathSpace c
    ridr    : {t₁ t₂ : Pi0.U•} {c : t₁ Pi0.⟷ t₂} →
              PathSpace c ⟷ PathSpace (c Pi0.◎ Pi0.id⟷)
    invll   : {t₁ t₂ : Pi0.U•} {c : t₁ Pi0.⟷ t₂} →
              PathSpace (Pi0.sym⟷ c Pi0.◎ c) ⟷ PathSpace {t₂} {t₂} Pi0.id⟷
    invlr   : {t₁ t₂ : Pi0.U•} {c : t₁ Pi0.⟷ t₂} →
              PathSpace {t₂} {t₂} Pi0.id⟷ ⟷ PathSpace (Pi0.sym⟷ c Pi0.◎ c)
    invrl   : {t₁ t₂ : Pi0.U•} {c : t₁ Pi0.⟷ t₂} →
              PathSpace (c Pi0.◎ Pi0.sym⟷ c) ⟷ PathSpace {t₁} {t₁} Pi0.id⟷
    invrr   : {t₁ t₂ : Pi0.U•} {c : t₁ Pi0.⟷ t₂} →
              PathSpace {t₁} {t₁} Pi0.id⟷ ⟷ PathSpace (c Pi0.◎ Pi0.sym⟷ c)
    invinvl : {t₁ t₂ : Pi0.U•} {c : t₁ Pi0.⟷ t₂} →
              PathSpace (Pi0.sym⟷ (Pi0.sym⟷ c)) ⟷ PathSpace c
    invinvr : {t₁ t₂ : Pi0.U•} {c : t₁ Pi0.⟷ t₂} →
              PathSpace c ⟷ PathSpace (Pi0.sym⟷ (Pi0.sym⟷ c))
    tassocl : {t₁ t₂ t₃ t₄ : Pi0.U•}
              {c₁ : t₁ Pi0.⟷ t₂} {c₂ : t₂ Pi0.⟷ t₃} {c₃ : t₃ Pi0.⟷ t₄} →
              PathSpace (c₁ Pi0.◎ (c₂ Pi0.◎ c₃)) ⟷
              PathSpace ((c₁ Pi0.◎ c₂) Pi0.◎ c₃)
    tassocr : {t₁ t₂ t₃ t₄ : Pi0.U•}
              {c₁ : t₁ Pi0.⟷ t₂} {c₂ : t₂ Pi0.⟷ t₃} {c₃ : t₃ Pi0.⟷ t₄} →
              PathSpace ((c₁ Pi0.◎ c₂) Pi0.◎ c₃) ⟷
              PathSpace (c₁ Pi0.◎ (c₂ Pi0.◎ c₃))
    -- resp◎ is closely related to Eckmann-Hilton
    resp◎   : {t₁ t₂ t₃ : Pi0.U•}
              {c₁ : t₁ Pi0.⟷ t₂} {c₂ : t₂ Pi0.⟷ t₃}
              {c₃ : t₁ Pi0.⟷ t₂} {c₄ : t₂ Pi0.⟷ t₃} →
              (PathSpace c₁ ⟷ PathSpace c₃) →
              (PathSpace c₂ ⟷ PathSpace c₄) →
              PathSpace (c₁ Pi0.◎ c₂) ⟷ PathSpace (c₃ Pi0.◎ c₄)

    -- commutative semiring combinators

    unite₊  : {t : U•} → •[ PLUS ZERO ∣ t ∣ , inj₂ (• t) ] ⟷ t
    uniti₊  : {t : U•} → t ⟷ •[ PLUS ZERO ∣ t ∣ , inj₂ (• t) ]
    swap1₊  : {t₁ t₂ : U•} → •[ PLUS ∣ t₁ ∣ ∣ t₂ ∣ , inj₁ (• t₁) ] ⟷
                             •[ PLUS ∣ t₂ ∣ ∣ t₁ ∣ , inj₂ (• t₁) ]
    swap2₊  : {t₁ t₂ : U•} → •[ PLUS ∣ t₁ ∣ ∣ t₂ ∣ , inj₂ (• t₂) ] ⟷
                             •[ PLUS ∣ t₂ ∣ ∣ t₁ ∣ , inj₁ (• t₂) ]
    assocl1₊ : {t₁ t₂ t₃ : U•} →
               •[ PLUS ∣ t₁ ∣ (PLUS ∣ t₂ ∣ ∣ t₃ ∣) , inj₁ (• t₁) ] ⟷
               •[ PLUS (PLUS ∣ t₁ ∣ ∣ t₂ ∣) ∣ t₃ ∣ , inj₁ (inj₁ (• t₁)) ]
    assocl2₊ : {t₁ t₂ t₃ : U•} →
               •[ PLUS ∣ t₁ ∣ (PLUS ∣ t₂ ∣ ∣ t₃ ∣) , inj₂ (inj₁ (• t₂)) ] ⟷
               •[ PLUS (PLUS ∣ t₁ ∣ ∣ t₂ ∣) ∣ t₃ ∣ , inj₁ (inj₂ (• t₂)) ]
    assocl3₊ : {t₁ t₂ t₃ : U•} →
               •[ PLUS ∣ t₁ ∣ (PLUS ∣ t₂ ∣ ∣ t₃ ∣) , inj₂ (inj₂ (• t₃)) ] ⟷
               •[ PLUS (PLUS ∣ t₁ ∣ ∣ t₂ ∣) ∣ t₃ ∣ , inj₂ (• t₃) ]
    assocr1₊ : {t₁ t₂ t₃ : U•} →
               •[ PLUS (PLUS ∣ t₁ ∣ ∣ t₂ ∣) ∣ t₃ ∣ , inj₁ (inj₁ (• t₁)) ] ⟷
               •[ PLUS ∣ t₁ ∣ (PLUS ∣ t₂ ∣ ∣ t₃ ∣) , inj₁ (• t₁) ]
    assocr2₊ : {t₁ t₂ t₃ : U•} →
               •[ PLUS (PLUS ∣ t₁ ∣ ∣ t₂ ∣) ∣ t₃ ∣ , inj₁ (inj₂ (• t₂)) ] ⟷
               •[ PLUS ∣ t₁ ∣ (PLUS ∣ t₂ ∣ ∣ t₃ ∣) , inj₂ (inj₁ (• t₂)) ]
    assocr3₊ : {t₁ t₂ t₃ : U•} →
               •[ PLUS (PLUS ∣ t₁ ∣ ∣ t₂ ∣) ∣ t₃ ∣ , inj₂ (• t₃) ] ⟷
               •[ PLUS ∣ t₁ ∣ (PLUS ∣ t₂ ∣ ∣ t₃ ∣) , inj₂ (inj₂ (• t₃)) ]
    unite⋆  : {t : U•} → •[ TIMES ONE ∣ t ∣ , (tt , • t) ] ⟷ t
    uniti⋆  : {t : U•} → t ⟷ •[ TIMES ONE ∣ t ∣ , (tt , • t) ]
    swap⋆   : {t₁ t₂ : U•} → •[ TIMES ∣ t₁ ∣ ∣ t₂ ∣ , (• t₁ , • t₂) ] ⟷
                             •[ TIMES ∣ t₂ ∣ ∣ t₁ ∣ , (• t₂ , • t₁) ]
    assocl⋆ : {t₁ t₂ t₃ : U•} →
           •[ TIMES ∣ t₁ ∣ (TIMES ∣ t₂ ∣ ∣ t₃ ∣) , (• t₁ , (• t₂ , • t₃)) ] ⟷
           •[ TIMES (TIMES ∣ t₁ ∣ ∣ t₂ ∣) ∣ t₃ ∣ , ((• t₁ , • t₂) , • t₃) ]
    assocr⋆ : {t₁ t₂ t₃ : U•} →
           •[ TIMES (TIMES ∣ t₁ ∣ ∣ t₂ ∣) ∣ t₃ ∣ , ((• t₁ , • t₂) , • t₃) ] ⟷
           •[ TIMES ∣ t₁ ∣ (TIMES ∣ t₂ ∣ ∣ t₃ ∣) , (• t₁ , (• t₂ , • t₃)) ]
    distz : {t : U•} {absurd : ⟦ ZERO ⟧} →
            •[ TIMES ZERO ∣ t ∣ , (absurd , • t) ] ⟷ •[ ZERO , absurd ]
    factorz : {t : U•} {absurd : ⟦ ZERO ⟧} →
            •[ ZERO , absurd ] ⟷ •[ TIMES ZERO ∣ t ∣ , (absurd , • t) ]
    dist1   : {t₁ t₂ t₃ : U•} →
              •[ TIMES (PLUS ∣ t₁ ∣ ∣ t₂ ∣) ∣ t₃ ∣ , (inj₁ (• t₁) , • t₃) ] ⟷
              •[ PLUS (TIMES ∣ t₁ ∣ ∣ t₃ ∣) (TIMES ∣ t₂ ∣ ∣ t₃ ∣) ,
                 inj₁ (• t₁ , • t₃) ]
    dist2   : {t₁ t₂ t₃ : U•} →
              •[ TIMES (PLUS ∣ t₁ ∣ ∣ t₂ ∣) ∣ t₃ ∣ , (inj₂ (• t₂) , • t₃) ] ⟷
              •[ PLUS (TIMES ∣ t₁ ∣ ∣ t₃ ∣) (TIMES ∣ t₂ ∣ ∣ t₃ ∣) ,
                 inj₂ (• t₂ , • t₃) ]
    factor1   : {t₁ t₂ t₃ : U•} →
              •[ PLUS (TIMES ∣ t₁ ∣ ∣ t₃ ∣) (TIMES ∣ t₂ ∣ ∣ t₃ ∣) ,
                 inj₁ (• t₁ , • t₃) ] ⟷
              •[ TIMES (PLUS ∣ t₁ ∣ ∣ t₂ ∣) ∣ t₃ ∣ , (inj₁ (• t₁) , • t₃) ]
    factor2   : {t₁ t₂ t₃ : U•} →
              •[ PLUS (TIMES ∣ t₁ ∣ ∣ t₃ ∣) (TIMES ∣ t₂ ∣ ∣ t₃ ∣) ,
                 inj₂ (• t₂ , • t₃) ] ⟷
              •[ TIMES (PLUS ∣ t₁ ∣ ∣ t₂ ∣) ∣ t₃ ∣ , (inj₂ (• t₂) , • t₃) ]
    _⊕1_   : {t₁ t₂ t₃ t₄ : U•} → (t₁ ⟷ t₃) → (t₂ ⟷ t₄) →
             (•[ PLUS ∣ t₁ ∣ ∣ t₂ ∣ , inj₁ (• t₁) ] ⟷
              •[ PLUS ∣ t₃ ∣ ∣ t₄ ∣ , inj₁ (• t₃) ])
    _⊕2_   : {t₁ t₂ t₃ t₄ : U•} → (t₁ ⟷ t₃) → (t₂ ⟷ t₄) →
             (•[ PLUS ∣ t₁ ∣ ∣ t₂ ∣ , inj₂ (• t₂) ] ⟷
              •[ PLUS ∣ t₃ ∣ ∣ t₄ ∣ , inj₂ (• t₄) ])
    _⊗_     : {t₁ t₂ t₃ t₄ : U•} → (t₁ ⟷ t₃) → (t₂ ⟷ t₄) →
            (•[ TIMES ∣ t₁ ∣ ∣ t₂ ∣ , (• t₁ , • t₂ ) ] ⟷
             •[ TIMES ∣ t₃ ∣ ∣ t₄ ∣ , (• t₃ , • t₄ ) ])

  -- proof that we have a 1Groupoid structure

  G : 1Groupoid
  G = record
        { set = Pi0.U•
        ; _↝_ = Pi0._⟷_
        ; _≈_ = λ c₀ c₁ → PathSpace c₀ ⟷ PathSpace c₁
        ; id = Pi0.id⟷
        ; _∘_ = λ c₀ c₁ → c₁ Pi0.◎ c₀
        ; _⁻¹ = Pi0.sym⟷
        ; lneutr = λ _ → ridl
        ; rneutr = λ _ → lidl
        ; assoc = λ _ _ _ → tassocl
        ; equiv = record { refl = id⟷
                                ; sym = λ c → sym⟷ c
                                ; trans = λ c₀ c₁ → c₀ ◎ c₁ }
        ; linv = λ _ → invrl
        ; rinv = λ _ → invll
        ; ∘-resp-≈ = λ f⟷h g⟷i → resp◎ g⟷i f⟷h
        }

------------------------------------------------------------------------------
-- Level 2
-- types are 2groupoids; they are points with non-trivial paths between them
-- and non-trivial 2paths between the paths

module Pi2 where

  infixr 10 _◎_
  infixr 30 _⟷_

  -- types

  data U : Set where
    ZERO   : U
    ONE    : U
    PLUS   : U → U → U
    TIMES  : U → U → U
    PATH   : {t₁ t₂ : Pi0.U•} → (t₁ Pi0.⟷ t₂) → U
    2PATH  : {t₁ t₂ : Pi1.U•} → (t₁ Pi1.⟷ t₂) → U

  -- values

  data Path {t₁ t₂ : Pi0.U•} : (Pi0.Space t₁) → (Pi0.Space t₂) → Set where
    path : (c : t₁ Pi0.⟷ t₂) → Path (Pi0.point t₁) (Pi0.point t₂)

  data 2Path {t₁ t₂ : Pi1.U•} : Pi1.Space t₁ → Pi1.Space t₂ → Set where
    2path : (c : t₁ Pi1.⟷ t₂) → 2Path (Pi1.point t₁) (Pi1.point t₂)

  ⟦_⟧ : U → Set
  ⟦ ZERO ⟧             = ⊥
  ⟦ ONE ⟧              = ⊤
  ⟦ PLUS t₁ t₂ ⟧       = ⟦ t₁ ⟧ ⊎ ⟦ t₂ ⟧
  ⟦ TIMES t₁ t₂ ⟧      = ⟦ t₁ ⟧ × ⟦ t₂ ⟧
  ⟦ PATH {t₁} {t₂} c ⟧ = Path {t₁} {t₂} (Pi0.point t₁) (Pi0.point t₂)
  ⟦ 2PATH {t₁} {t₂} c ⟧ = 2Path {t₁} {t₂} (Pi1.point t₁) (Pi1.point t₂)

  -- pointed types

  record U• : Set where
    constructor •[_,_]
    field
      ∣_∣ : U
      • : ⟦ ∣_∣ ⟧

  open U•

  Space : (t• : U•) → Set
  Space •[ t , p ] = ⟦ t ⟧

  point : (t• : U•) → Space t•
  point •[ t , p ] = p

  _∘1_ : {t₁ t₂ t₃ : Pi0.U•} →
        Path (Pi0.point t₁) (Pi0.point t₂) →
        Path (Pi0.point t₂) (Pi0.point t₃) →
        Path (Pi0.point t₁) (Pi0.point t₃)
  path c₁ ∘1 path c₂ = path (c₁ Pi0.◎ c₂)

  _∘2_ : {t₁ t₂ t₃ : Pi1.U•} →
        2Path (Pi1.point t₁) (Pi1.point t₂) →
        2Path (Pi1.point t₂) (Pi1.point t₃) →
        2Path (Pi1.point t₁) (Pi1.point t₃)
  2path c₁ ∘2 2path c₂ = 2path (c₁ Pi1.◎ c₂)

  PathSpace : {t₁ t₂ : Pi0.U•} → (c : t₁ Pi0.⟷ t₂) → U•
  PathSpace c = •[ PATH c , path c ]

  2PathSpace : {t₁ t₂ : Pi1.U•} → (c : t₁ Pi1.⟷ t₂) → U•
  2PathSpace c = •[ 2PATH c , 2path c ]

  -- examples

  -- groupoid combinators to reason about id, sym, and trans
  -- commutative semiring combinators for 0, 1, +, *

  data _⟷_ : U• → U• → Set where

    -- common combinators

    id⟷    : {t : U•} → t ⟷ t
    sym⟷   : {t₁ t₂ : U•} → (t₁ ⟷ t₂) → (t₂ ⟷ t₁)
    _◎_     : {t₁ t₂ t₃ : U•} → (t₁ ⟷ t₂) → (t₂ ⟷ t₃) → (t₁ ⟷ t₃)

    -- 1groupoid combinators

    1lidl    : {t₁ t₂ : Pi0.U•} {c : t₁ Pi0.⟷ t₂} →
              PathSpace (Pi0.id⟷ Pi0.◎ c) ⟷ PathSpace c
    1lidr    : {t₁ t₂ : Pi0.U•} {c : t₁ Pi0.⟷ t₂} →
              PathSpace c ⟷ PathSpace (Pi0.id⟷ Pi0.◎ c)
    1ridl    : {t₁ t₂ : Pi0.U•} {c : t₁ Pi0.⟷ t₂} →
              PathSpace (c Pi0.◎ Pi0.id⟷) ⟷ PathSpace c
    1ridr    : {t₁ t₂ : Pi0.U•} {c : t₁ Pi0.⟷ t₂} →
              PathSpace c ⟷ PathSpace (c Pi0.◎ Pi0.id⟷)
    1invll   : {t₁ t₂ : Pi0.U•} {c : t₁ Pi0.⟷ t₂} →
              PathSpace (Pi0.sym⟷ c Pi0.◎ c) ⟷ PathSpace {t₂} {t₂} Pi0.id⟷
    1invlr   : {t₁ t₂ : Pi0.U•} {c : t₁ Pi0.⟷ t₂} →
              PathSpace {t₂} {t₂} Pi0.id⟷ ⟷ PathSpace (Pi0.sym⟷ c Pi0.◎ c)
    1invrl   : {t₁ t₂ : Pi0.U•} {c : t₁ Pi0.⟷ t₂} →
              PathSpace (c Pi0.◎ Pi0.sym⟷ c) ⟷ PathSpace {t₁} {t₁} Pi0.id⟷
    1invrr   : {t₁ t₂ : Pi0.U•} {c : t₁ Pi0.⟷ t₂} →
              PathSpace {t₁} {t₁} Pi0.id⟷ ⟷ PathSpace (c Pi0.◎ Pi0.sym⟷ c)
    1invinvl : {t₁ t₂ : Pi0.U•} {c : t₁ Pi0.⟷ t₂} →
              PathSpace (Pi0.sym⟷ (Pi0.sym⟷ c)) ⟷ PathSpace c
    1invinvr : {t₁ t₂ : Pi0.U•} {c : t₁ Pi0.⟷ t₂} →
              PathSpace c ⟷ PathSpace (Pi0.sym⟷ (Pi0.sym⟷ c))
    1tassocl : {t₁ t₂ t₃ t₄ : Pi0.U•}
              {c₁ : t₁ Pi0.⟷ t₂} {c₂ : t₂ Pi0.⟷ t₃} {c₃ : t₃ Pi0.⟷ t₄} →
              PathSpace (c₁ Pi0.◎ (c₂ Pi0.◎ c₃)) ⟷
              PathSpace ((c₁ Pi0.◎ c₂) Pi0.◎ c₃)
    1tassocr : {t₁ t₂ t₃ t₄ : Pi0.U•}
              {c₁ : t₁ Pi0.⟷ t₂} {c₂ : t₂ Pi0.⟷ t₃} {c₃ : t₃ Pi0.⟷ t₄} →
              PathSpace ((c₁ Pi0.◎ c₂) Pi0.◎ c₃) ⟷
              PathSpace (c₁ Pi0.◎ (c₂ Pi0.◎ c₃))
    -- resp◎ is closely related to Eckmann-Hilton
    1resp◎   : {t₁ t₂ t₃ : Pi0.U•}
              {c₁ : t₁ Pi0.⟷ t₂} {c₂ : t₂ Pi0.⟷ t₃}
              {c₃ : t₁ Pi0.⟷ t₂} {c₄ : t₂ Pi0.⟷ t₃} →
              (PathSpace c₁ ⟷ PathSpace c₃) →
              (PathSpace c₂ ⟷ PathSpace c₄) →
              PathSpace (c₁ Pi0.◎ c₂) ⟷ PathSpace (c₃ Pi0.◎ c₄)

    -- 2groupoid combinators

    lidl    : {t₁ t₂ : Pi1.U•} {c : t₁ Pi1.⟷ t₂} →
              2PathSpace (Pi1.id⟷ Pi1.◎ c) ⟷ 2PathSpace c
    lidr    : {t₁ t₂ : Pi1.U•} {c : t₁ Pi1.⟷ t₂} →
              2PathSpace c ⟷ 2PathSpace (Pi1.id⟷ Pi1.◎ c)
    ridl    : {t₁ t₂ : Pi1.U•} {c : t₁ Pi1.⟷ t₂} →
              2PathSpace (c Pi1.◎ Pi1.id⟷) ⟷ 2PathSpace c
    ridr    : {t₁ t₂ : Pi1.U•} {c : t₁ Pi1.⟷ t₂} →
              2PathSpace c ⟷ 2PathSpace (c Pi1.◎ Pi1.id⟷)
    invll   : {t₁ t₂ : Pi1.U•} {c : t₁ Pi1.⟷ t₂} →
              2PathSpace (Pi1.sym⟷ c Pi1.◎ c) ⟷ 2PathSpace {t₂} {t₂} Pi1.id⟷
    invlr   : {t₁ t₂ : Pi1.U•} {c : t₁ Pi1.⟷ t₂} →
              2PathSpace {t₂} {t₂} Pi1.id⟷ ⟷ 2PathSpace (Pi1.sym⟷ c Pi1.◎ c)
    invrl   : {t₁ t₂ : Pi1.U•} {c : t₁ Pi1.⟷ t₂} →
              2PathSpace (c Pi1.◎ Pi1.sym⟷ c) ⟷ 2PathSpace {t₁} {t₁} Pi1.id⟷
    invrr   : {t₁ t₂ : Pi1.U•} {c : t₁ Pi1.⟷ t₂} →
              2PathSpace {t₁} {t₁} Pi1.id⟷ ⟷ 2PathSpace (c Pi1.◎ Pi1.sym⟷ c)
    invinvl : {t₁ t₂ : Pi1.U•} {c : t₁ Pi1.⟷ t₂} →
              2PathSpace (Pi1.sym⟷ (Pi1.sym⟷ c)) ⟷ 2PathSpace c
    invinvr : {t₁ t₂ : Pi1.U•} {c : t₁ Pi1.⟷ t₂} →
              2PathSpace c ⟷ 2PathSpace (Pi1.sym⟷ (Pi1.sym⟷ c))
    tassocl : {t₁ t₂ t₃ t₄ : Pi1.U•}
              {c₁ : t₁ Pi1.⟷ t₂} {c₂ : t₂ Pi1.⟷ t₃} {c₃ : t₃ Pi1.⟷ t₄} →
              2PathSpace (c₁ Pi1.◎ (c₂ Pi1.◎ c₃)) ⟷
              2PathSpace ((c₁ Pi1.◎ c₂) Pi1.◎ c₃)
    tassocr : {t₁ t₂ t₃ t₄ : Pi1.U•}
              {c₁ : t₁ Pi1.⟷ t₂} {c₂ : t₂ Pi1.⟷ t₃} {c₃ : t₃ Pi1.⟷ t₄} →
              2PathSpace ((c₁ Pi1.◎ c₂) Pi1.◎ c₃) ⟷
              2PathSpace (c₁ Pi1.◎ (c₂ Pi1.◎ c₃))
    -- resp◎ is closely related to Eckmann-Hilton
    resp◎   : {t₁ t₂ t₃ : Pi1.U•}
              {c₁ : t₁ Pi1.⟷ t₂} {c₂ : t₂ Pi1.⟷ t₃}
              {c₃ : t₁ Pi1.⟷ t₂} {c₄ : t₂ Pi1.⟷ t₃} →
              (2PathSpace c₁ ⟷ 2PathSpace c₃) →
              (2PathSpace c₂ ⟷ 2PathSpace c₄) →
              2PathSpace (c₁ Pi1.◎ c₂) ⟷ 2PathSpace (c₃ Pi1.◎ c₄)

    -- commutative semiring combinators

    unite₊  : {t : U•} → •[ PLUS ZERO ∣ t ∣ , inj₂ (• t) ] ⟷ t
    uniti₊  : {t : U•} → t ⟷ •[ PLUS ZERO ∣ t ∣ , inj₂ (• t) ]
    swap1₊  : {t₁ t₂ : U•} → •[ PLUS ∣ t₁ ∣ ∣ t₂ ∣ , inj₁ (• t₁) ] ⟷
                             •[ PLUS ∣ t₂ ∣ ∣ t₁ ∣ , inj₂ (• t₁) ]
    swap2₊  : {t₁ t₂ : U•} → •[ PLUS ∣ t₁ ∣ ∣ t₂ ∣ , inj₂ (• t₂) ] ⟷
                             •[ PLUS ∣ t₂ ∣ ∣ t₁ ∣ , inj₁ (• t₂) ]
    assocl1₊ : {t₁ t₂ t₃ : U•} →
               •[ PLUS ∣ t₁ ∣ (PLUS ∣ t₂ ∣ ∣ t₃ ∣) , inj₁ (• t₁) ] ⟷
               •[ PLUS (PLUS ∣ t₁ ∣ ∣ t₂ ∣) ∣ t₃ ∣ , inj₁ (inj₁ (• t₁)) ]
    assocl2₊ : {t₁ t₂ t₃ : U•} →
               •[ PLUS ∣ t₁ ∣ (PLUS ∣ t₂ ∣ ∣ t₃ ∣) , inj₂ (inj₁ (• t₂)) ] ⟷
               •[ PLUS (PLUS ∣ t₁ ∣ ∣ t₂ ∣) ∣ t₃ ∣ , inj₁ (inj₂ (• t₂)) ]
    assocl3₊ : {t₁ t₂ t₃ : U•} →
               •[ PLUS ∣ t₁ ∣ (PLUS ∣ t₂ ∣ ∣ t₃ ∣) , inj₂ (inj₂ (• t₃)) ] ⟷
               •[ PLUS (PLUS ∣ t₁ ∣ ∣ t₂ ∣) ∣ t₃ ∣ , inj₂ (• t₃) ]
    assocr1₊ : {t₁ t₂ t₃ : U•} →
               •[ PLUS (PLUS ∣ t₁ ∣ ∣ t₂ ∣) ∣ t₃ ∣ , inj₁ (inj₁ (• t₁)) ] ⟷
               •[ PLUS ∣ t₁ ∣ (PLUS ∣ t₂ ∣ ∣ t₃ ∣) , inj₁ (• t₁) ]
    assocr2₊ : {t₁ t₂ t₃ : U•} →
               •[ PLUS (PLUS ∣ t₁ ∣ ∣ t₂ ∣) ∣ t₃ ∣ , inj₁ (inj₂ (• t₂)) ] ⟷
               •[ PLUS ∣ t₁ ∣ (PLUS ∣ t₂ ∣ ∣ t₃ ∣) , inj₂ (inj₁ (• t₂)) ]
    assocr3₊ : {t₁ t₂ t₃ : U•} →
               •[ PLUS (PLUS ∣ t₁ ∣ ∣ t₂ ∣) ∣ t₃ ∣ , inj₂ (• t₃) ] ⟷
               •[ PLUS ∣ t₁ ∣ (PLUS ∣ t₂ ∣ ∣ t₃ ∣) , inj₂ (inj₂ (• t₃)) ]
    unite⋆  : {t : U•} → •[ TIMES ONE ∣ t ∣ , (tt , • t) ] ⟷ t
    uniti⋆  : {t : U•} → t ⟷ •[ TIMES ONE ∣ t ∣ , (tt , • t) ]
    swap⋆   : {t₁ t₂ : U•} → •[ TIMES ∣ t₁ ∣ ∣ t₂ ∣ , (• t₁ , • t₂) ] ⟷
                             •[ TIMES ∣ t₂ ∣ ∣ t₁ ∣ , (• t₂ , • t₁) ]
    assocl⋆ : {t₁ t₂ t₃ : U•} →
           •[ TIMES ∣ t₁ ∣ (TIMES ∣ t₂ ∣ ∣ t₃ ∣) , (• t₁ , (• t₂ , • t₃)) ] ⟷
           •[ TIMES (TIMES ∣ t₁ ∣ ∣ t₂ ∣) ∣ t₃ ∣ , ((• t₁ , • t₂) , • t₃) ]
    assocr⋆ : {t₁ t₂ t₃ : U•} →
           •[ TIMES (TIMES ∣ t₁ ∣ ∣ t₂ ∣) ∣ t₃ ∣ , ((• t₁ , • t₂) , • t₃) ] ⟷
           •[ TIMES ∣ t₁ ∣ (TIMES ∣ t₂ ∣ ∣ t₃ ∣) , (• t₁ , (• t₂ , • t₃)) ]
    distz : {t : U•} {absurd : ⟦ ZERO ⟧} →
            •[ TIMES ZERO ∣ t ∣ , (absurd , • t) ] ⟷ •[ ZERO , absurd ]
    factorz : {t : U•} {absurd : ⟦ ZERO ⟧} →
            •[ ZERO , absurd ] ⟷ •[ TIMES ZERO ∣ t ∣ , (absurd , • t) ]
    dist1   : {t₁ t₂ t₃ : U•} →
              •[ TIMES (PLUS ∣ t₁ ∣ ∣ t₂ ∣) ∣ t₃ ∣ , (inj₁ (• t₁) , • t₃) ] ⟷
              •[ PLUS (TIMES ∣ t₁ ∣ ∣ t₃ ∣) (TIMES ∣ t₂ ∣ ∣ t₃ ∣) ,
                 inj₁ (• t₁ , • t₃) ]
    dist2   : {t₁ t₂ t₃ : U•} →
              •[ TIMES (PLUS ∣ t₁ ∣ ∣ t₂ ∣) ∣ t₃ ∣ , (inj₂ (• t₂) , • t₃) ] ⟷
              •[ PLUS (TIMES ∣ t₁ ∣ ∣ t₃ ∣) (TIMES ∣ t₂ ∣ ∣ t₃ ∣) ,
                 inj₂ (• t₂ , • t₃) ]
    factor1   : {t₁ t₂ t₃ : U•} →
              •[ PLUS (TIMES ∣ t₁ ∣ ∣ t₃ ∣) (TIMES ∣ t₂ ∣ ∣ t₃ ∣) ,
                 inj₁ (• t₁ , • t₃) ] ⟷
              •[ TIMES (PLUS ∣ t₁ ∣ ∣ t₂ ∣) ∣ t₃ ∣ , (inj₁ (• t₁) , • t₃) ]
    factor2   : {t₁ t₂ t₃ : U•} →
              •[ PLUS (TIMES ∣ t₁ ∣ ∣ t₃ ∣) (TIMES ∣ t₂ ∣ ∣ t₃ ∣) ,
                 inj₂ (• t₂ , • t₃) ] ⟷
              •[ TIMES (PLUS ∣ t₁ ∣ ∣ t₂ ∣) ∣ t₃ ∣ , (inj₂ (• t₂) , • t₃) ]
    _⊕1_   : {t₁ t₂ t₃ t₄ : U•} → (t₁ ⟷ t₃) → (t₂ ⟷ t₄) →
             (•[ PLUS ∣ t₁ ∣ ∣ t₂ ∣ , inj₁ (• t₁) ] ⟷
              •[ PLUS ∣ t₃ ∣ ∣ t₄ ∣ , inj₁ (• t₃) ])
    _⊕2_   : {t₁ t₂ t₃ t₄ : U•} → (t₁ ⟷ t₃) → (t₂ ⟷ t₄) →
             (•[ PLUS ∣ t₁ ∣ ∣ t₂ ∣ , inj₂ (• t₂) ] ⟷
              •[ PLUS ∣ t₃ ∣ ∣ t₄ ∣ , inj₂ (• t₄) ])
    _⊗_     : {t₁ t₂ t₃ t₄ : U•} → (t₁ ⟷ t₃) → (t₂ ⟷ t₄) →
            (•[ TIMES ∣ t₁ ∣ ∣ t₂ ∣ , (• t₁ , • t₂ ) ] ⟷
             •[ TIMES ∣ t₃ ∣ ∣ t₄ ∣ , (• t₃ , • t₄ ) ])

  -- proof that we have a 2Groupoid structure; this is 1Groupoid whose
  -- equivalences are themselves a 1Groupoid

  G : 1Groupoid
  G = record
        { set = Pi1.U•
        ; _↝_ = Pi1._⟷_
        ; _≈_ = λ c₀ c₁ → 2PathSpace c₀ ⟷ 2PathSpace c₁
        ; id = Pi1.id⟷
        ; _∘_ = λ c₀ c₁ → c₁ Pi1.◎ c₀
        ; _⁻¹ = Pi1.sym⟷
        ; lneutr = λ _ → ridl
        ; rneutr = λ _ → lidl
        ; assoc = λ _ _ _ → tassocl
        ; equiv = record { refl = id⟷
                                ; sym = λ c → sym⟷ c
                                ; trans = λ c₀ c₁ → c₀ ◎ c₁ }
        ; linv = λ _ → invrl
        ; rinv = λ _ → invll
        ; ∘-resp-≈ = λ f⟷h g⟷i → resp◎ g⟷i f⟷h
        }

  -- examples

------------------------------------------------------------------------------

--}


------------------------------------------------------------------------
-- Some alternative definitions of the concept of being an equivalence
------------------------------------------------------------------------

-- Partly based on the blog post "Universal properties without
-- function extensionality" by Mike Shulman
-- (https://homotopytypetheory.org/2014/11/02/universal-properties-without-function-extensionality/),
-- and the corresponding code in the Coq HoTT library
-- (https://github.com/HoTT/HoTT).

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

open import Equality

module Equivalence.Path-split
  {e⁺} (eq : ∀ {a p} → Equality-with-J a p e⁺) where

open Derived-definitions-and-properties eq

open import Logical-equivalence using (_⇔_)
open import Prelude

open import Bijection eq as B using (_↔_)
open import Embedding eq using (Embedding)
open import Equality.Decision-procedures eq
open import Equivalence eq as Eq using (_≃_; Is-equivalence)
open import Equivalence.Erased.Basics eq using (Is-equivalenceᴱ)
open import Function-universe eq as F hiding (id; _∘_)
open import H-level eq as H-level
open import H-level.Closure eq
open import Surjection eq using (Split-surjective; _↠_)

private
  variable
    a b c d p : Level
    A B       : Type a
    x y       : A
    f         : A → B
    k         : Kind
    n         : ℕ

------------------------------------------------------------------------
-- Path-split

-- An alternative definition of "Is-equivalence".

Path-split : {A : Type a} {B : Type b} → ℕ → (A → B) → Type (a ⊔ b)
Path-split zero    f = ↑ _ ⊤
Path-split (suc n) f =
  Split-surjective f ×
  (∀ x y → Path-split n (cong {x = x} {y = y} f))

private

  -- A lemma.

  eq→emb : Is-equivalence f → Is-equivalence (cong {x = x} {y = y} f)
  eq→emb eq =
    Embedding.is-embedding (from-isomorphism Eq.⟨ _ , eq ⟩) _ _

-- Equivalences are path-split.

Is-equivalence→Path-split : Is-equivalence f → Path-split n f
Is-equivalence→Path-split {n = zero}  eq = _
Is-equivalence→Path-split {n = suc n} eq =
    _≃_.split-surjective Eq.⟨ _ , eq ⟩
  , λ x y → Is-equivalence→Path-split (eq→emb eq)

private

  -- Path-split n f holds, for n ≥ 2, iff f is an equivalence.

  Path-split⇔Is-equivalence :
    Path-split (2 + n) f ⇔ Is-equivalence f
  Path-split⇔Is-equivalence {f = f} = record
    { to   = λ (s , p) →
               let inv    = proj₁ ∘ s
                   is-inv = proj₂ ∘ s
               in _≃_.is-equivalence $ Eq.↔⇒≃ (record
                    { surjection = record
                      { right-inverse-of = is-inv
                      }
                    ; left-inverse-of = λ x →  $⟨ is-inv (f x) ⟩
                        f (inv (f x)) ≡ f x    ↝⟨ proj₁ ∘ proj₁ (p _ _) ⟩
                        inv (f x) ≡ x          □
                    })
    ; from = Is-equivalence→Path-split
    }

-- If f is an equivalence, then Split-surjective f is contractible
-- (assuming extensionality).

Split-surjective-contractible-for-equivalences :
  ∀ {a b} {A : Type a} {B : Type b} {f : A → B} →
  Extensionality b (a ⊔ b) →
  Is-equivalence f →
  Contractible (Split-surjective f)
Split-surjective-contractible-for-equivalences
  {A = A} {B = B} {f = f} ext eq =

  propositional⇒inhabited⇒contractible
    (Π-closure ext 1 λ y →
     let surj : (∃ λ x → x ≡ _≃_.from A≃B y) ↠ (∃ λ x → f x ≡ y)
         surj = ∃-cong λ x →
           x ≡ _≃_.from A≃B y        ↔⟨ inverse $ Eq.≃-≡ A≃B ⟩
           f x ≡ f (_≃_.from A≃B y)  ↝⟨ ≡⇒↝ _ $ cong (_ ≡_) $ _≃_.right-inverse-of A≃B _ ⟩□
           f x ≡ y                   □
     in
     H-level.respects-surjection surj 1 $
     mono₁ 0 $
     singleton-contractible _)
    (_≃_.split-surjective A≃B)
  where
  A≃B : A ≃ B
  A≃B = Eq.⟨ _ , eq ⟩

-- If f is an equivalence, then Path-split n f is contractible
-- (assuming extensionality).

Path-split-contractible-for-equivalences :
  {A : Type a} {B : Type b} {f : A → B} →
  Extensionality (a ⊔ b) (a ⊔ b) →
  Is-equivalence f →
  Contractible (Path-split n f)
Path-split-contractible-for-equivalences {n = zero} _ _ =
  ↑-closure 0 $
  ⊤-contractible

Path-split-contractible-for-equivalences
  {a = a} {b = b} {n = suc n} {A = A} {B = B} {f = f} ext eq =

  ×-closure 0
    (Split-surjective-contractible-for-equivalences
       (lower-extensionality a lzero ext) eq)
    (Π-closure (lower-extensionality b lzero ext) 0 λ _ →
     Π-closure (lower-extensionality b lzero ext) 0 λ _ →
     Path-split-contractible-for-equivalences
        ext (eq→emb eq))

-- Path-split n is pointwise propositional for n ≥ 2 (assuming
-- extensionality).

Path-split-propositional :
  {A : Type a} {B : Type b} {f : A → B} →
  Extensionality (a ⊔ b) (a ⊔ b) →
  Is-proposition (Path-split (2 + n) f)
Path-split-propositional ext =
  [inhabited⇒contractible]⇒propositional λ p →
  Path-split-contractible-for-equivalences ext $
    _⇔_.to Path-split⇔Is-equivalence p

-- There is a bijection between Path-split n f, for n ≥ 2, and
-- Is-equivalence f (assuming extensionality).

Path-split↔Is-equivalence :
  {A : Type a} {B : Type b} {f : A → B} →
  Path-split (2 + n) f ↝[ a ⊔ b ∣ a ⊔ b ] Is-equivalence f
Path-split↔Is-equivalence =
  generalise-ext?-prop
    Path-split⇔Is-equivalence
    Path-split-propositional
    (λ ext → Eq.propositional ext _)

-- Another alternative definition of "Is-equivalence".

Path-split-∞ : {A : Type a} {B : Type b} → (A → B) → Type (a ⊔ b)
Path-split-∞ f = ∀ n → Path-split n f

-- Path-split-∞ is pointwise propositional (assuming extensionality).

Path-split-∞-propositional :
  {A : Type a} {B : Type b} {f : A → B} →
  Extensionality (a ⊔ b) (a ⊔ b) →
  Is-proposition (Path-split-∞ f)
Path-split-∞-propositional ext =
  [inhabited⇒contractible]⇒propositional λ p →
  Π-closure (lower-extensionality _ lzero ext) 0 λ _ →
  Path-split-contractible-for-equivalences ext $
    _⇔_.to Path-split⇔Is-equivalence (p 2)

-- There is a bijection between Path-split-∞ f and Is-equivalence f
-- (assuming extensionality).

Path-split-∞↔Is-equivalence :
  {A : Type a} {B : Type b} {f : A → B} →
  Path-split-∞ f ↝[ a ⊔ b ∣ a ⊔ b ] Is-equivalence f
Path-split-∞↔Is-equivalence =
  generalise-ext?-prop
    (record
       { to   = λ p → _⇔_.to Path-split⇔Is-equivalence (p 2)
       ; from = λ eq _ → Is-equivalence→Path-split eq
       })
    Path-split-∞-propositional
    (λ ext → Eq.propositional ext _)

-- A preservation lemma for Path-split.

Path-split-cong :
  {A : Type a} {B : Type b} {C : Type c} {D : Type d}
  {f : A → B} {g : C → D} →
  Extensionality? k (a ⊔ b ⊔ c ⊔ d) (a ⊔ b ⊔ c ⊔ d) →
  (A≃C : A ≃ C) (B≃D : B ≃ D) →
  (∀ x → g (_≃_.to A≃C x) ≡ _≃_.to B≃D (f x)) →
  ∀ n → Path-split n f ↝[ k ] Path-split n g
Path-split-cong
  {a = a} {b = b} {c = c} {d = d} {k = k} {f = f} {g = g}
  ext A≃C B≃D hyp = λ where

  zero →
    ↑ _ ⊤  ↔⟨ B.↑↔ ⟩
    ⊤      ↔⟨ inverse B.↑↔ ⟩□
    ↑ _ ⊤  □

  (suc n) →

    (Split-surjective f                   ↔⟨⟩

     (∀ y → ∃ λ x → f x ≡ y)              ↝⟨ (Π-cong (lower-extensionality? k (a ⊔ c) lzero ext) B≃D λ y →
                                              Σ-cong A≃C λ x →

       (f x ≡ y)                                ↔⟨ inverse $ Eq.≃-≡ B≃D ⟩
       (_≃_.to B≃D (f x) ≡ _≃_.to B≃D y)        ↝⟨ ≡⇒↝ _ $ cong (_≡ _) $ sym $ hyp x ⟩□
       (g (_≃_.to A≃C x) ≡ _≃_.to B≃D y)        □) ⟩

     (∀ y → ∃ λ x → g x ≡ y)              ↔⟨⟩

     Split-surjective g                   □)

      ×-cong

    (Π-cong (lower-extensionality? k (b ⊔ d) lzero ext) A≃C λ x →
     Π-cong (lower-extensionality? k (b ⊔ d) lzero ext) A≃C λ y →
     Path-split-cong ext

       (x ≡ y                        ↝⟨ inverse $ Eq.≃-≡ A≃C ⟩□
        _≃_.to A≃C x ≡ _≃_.to A≃C y  □)

       (f x ≡ f y                            ↝⟨ inverse $ Eq.≃-≡ B≃D ⟩
        _≃_.to B≃D (f x) ≡ _≃_.to B≃D (f y)  ↝⟨ ≡⇒↝ _ $ cong₂ _≡_ (sym $ hyp x) (sym $ hyp y) ⟩□
        g (_≃_.to A≃C x) ≡ g (_≃_.to A≃C y)  □)

       (λ x≡y →
          cong g (cong (_≃_.to A≃C) x≡y)                            ≡⟨ cong-∘ _ _ _ ⟩

          cong (g ∘ _≃_.to A≃C) x≡y                                 ≡⟨ elim¹
                                                                         (λ {y} x≡y →
                                                                            cong (g ∘ _≃_.to A≃C) x≡y ≡
                                                                            trans (trans (hyp x) (cong (_≃_.to B≃D ∘ f) x≡y))
                                                                              (sym $ hyp y))
                                                                         (
            cong (g ∘ _≃_.to A≃C) (refl _)                                ≡⟨ cong-refl _ ⟩

            refl _                                                        ≡⟨ sym $ trans-symʳ _ ⟩

            trans (hyp x) (sym $ hyp x)                                   ≡⟨ cong (flip trans _) $
                                                                             trans (sym $ trans-reflʳ _) $
                                                                             cong (trans _) $ sym $ cong-refl _ ⟩∎
            trans (trans (hyp x) (cong (_≃_.to B≃D ∘ f) (refl _)))
              (sym $ hyp x)                                               ∎)
                                                                         _ ⟩
          trans (trans (hyp x) (cong (_≃_.to B≃D ∘ f) x≡y))
            (sym $ hyp y)                                           ≡⟨ trans (cong (flip trans _) $ sym $
                                                                              subst-trans _) $
                                                                       trans-subst ⟩
          subst (_ ≡_) (sym $ hyp y)
            (subst (_≡ _) (sym $ hyp x)
               (cong (_≃_.to B≃D ∘ f) x≡y))                         ≡⟨ trans (cong (subst _ _) $
                                                                              subst-in-terms-of-≡⇒↝ equivalence _ _ _) $
                                                                       subst-in-terms-of-≡⇒↝ equivalence _ _ _ ⟩
          _≃_.to (≡⇒↝ _ (cong (_ ≡_) (sym $ hyp y)))
            (_≃_.to (≡⇒↝ _ (cong (_≡ _) (sym $ hyp x)))
               (cong (_≃_.to B≃D ∘ f) x≡y))                         ≡⟨ cong (_$ cong (_≃_.to B≃D ∘ f) x≡y) $ sym $
                                                                       ≡⇒↝-trans equivalence ⟩
          _≃_.to
            (≡⇒↝ _ $
             trans (cong (_≡ _) (sym $ hyp x))
               (cong (_ ≡_) (sym $ hyp y)))
            (cong (_≃_.to B≃D ∘ f) x≡y)                             ≡⟨⟩

          _≃_.to (≡⇒↝ _ $ cong₂ _≡_ (sym $ hyp x) (sym $ hyp y))
            (cong (_≃_.to B≃D ∘ f) x≡y)                             ≡⟨ cong (_≃_.to (≡⇒↝ _ _)) $ sym $
                                                                       cong-∘ _ _ _ ⟩∎
          _≃_.to (≡⇒↝ _ $ cong₂ _≡_ (sym $ hyp x) (sym $ hyp y))
            (cong (_≃_.to B≃D) (cong f x≡y))                        ∎)

       n)

------------------------------------------------------------------------
-- Extendable along

-- Is-[ n ]-extendable-along-[ f ] P means that P is n-extendable
-- along f.

Is-[_]-extendable-along-[_] :
  {A : Type a} {B : Type b} →
  ℕ → (A → B) → (B → Type c) → Type (a ⊔ b ⊔ c)
Is-[ zero  ]-extendable-along-[ f ] P = ↑ _ ⊤
Is-[ suc n ]-extendable-along-[ f ] P =
  ((g : ∀ x → P (f x)) →
     ∃ λ (h : ∀ x → P x) → ∀ x → h (f x) ≡ g x) ×
  ((g h : ∀ x → P x) →
     Is-[ n ]-extendable-along-[ f ] (λ x → g x ≡ h x))

-- Is-∞-extendable-along-[ f ] P means that P is ∞-extendable along f.

Is-∞-extendable-along-[_] :
  {A : Type a} {B : Type b} →
  (A → B) → (B → Type c) → Type (a ⊔ b ⊔ c)
Is-∞-extendable-along-[ f ] P =
  ∀ n → Is-[ n ]-extendable-along-[ f ] P

-- In the presence of extensionality Is-[_]-extendable-along-[_] can
-- be expressed using Path-split.

Is-extendable-along≃Path-split :
  {A : Type a} {B : Type b} {P : B → Type p} {f : A → B} →
  Extensionality (a ⊔ b ⊔ p) (a ⊔ b ⊔ p) →
  ∀ n →
  Is-[ n ]-extendable-along-[ f ] P ≃
  Path-split n (λ (g : ∀ x → P x) → g ∘ f)
Is-extendable-along≃Path-split {a = a} {b = b} {p = p} {f = f} ext =
  λ where
    zero    → Eq.id
    (suc n) →
      (∀-cong (lower-extensionality b lzero ext) λ g →
       ∃-cong λ h →
         (∀ x → h (f x) ≡ g x)  ↝⟨ Eq.extensionality-isomorphism
                                     (lower-extensionality (b ⊔ p) (a ⊔ b) ext) ⟩□
         h ∘ f ≡ g              □)
        ×-cong
      (∀-cong (lower-extensionality a lzero ext) λ g →
       ∀-cong (lower-extensionality a lzero ext) λ h →
         Is-[ n ]-extendable-along-[ f ] (λ x → g x ≡ h x)  ↝⟨ Is-extendable-along≃Path-split ext n ⟩
         Path-split n (_∘ f)                                ↝⟨ Path-split-cong ext
                                                                 (Eq.extensionality-isomorphism ext₁)
                                                                 (Eq.extensionality-isomorphism ext₂)
                                                                 (λ eq →
          cong (_∘ f) (apply-ext (Eq.good-ext ext₁) eq)             ≡⟨ Eq.cong-pre-∘-good-ext ext₂ ext₁ _ ⟩∎
          apply-ext (Eq.good-ext ext₂) (eq ∘ f)                     ∎)
                                                                 n ⟩□
         Path-split n (cong (_∘ f))                         □)
  where
  ext₁ = lower-extensionality (a ⊔ p) (a ⊔ b) ext
  ext₂ = lower-extensionality (b ⊔ p) (a ⊔ b) ext

-- In the presence of extensionality Is-∞-extendable-along-[_] can
-- be expressed using Path-split-∞.

Is-∞-extendable-along≃Path-split-∞ :
  {A : Type a} {B : Type b} {P : B → Type p} {f : A → B} →
  Extensionality (a ⊔ b ⊔ p) (a ⊔ b ⊔ p) →
  Is-∞-extendable-along-[ f ] P ≃
  Path-split-∞ (λ (g : ∀ x → P x) → g ∘ f)
Is-∞-extendable-along≃Path-split-∞ ext =
  ∀-cong (lower-extensionality _ lzero ext) $
  Is-extendable-along≃Path-split ext

-- Is-[ 2 + n ]-extendable-along-[ f ] P is propositional (assuming
-- extensionality).

Is-extendable-along-propositional :
  {A : Type a} {B : Type b} {P : B → Type p} {f : A → B} →
  Extensionality (a ⊔ b ⊔ p) (a ⊔ b ⊔ p) →
  Is-proposition (Is-[ 2 + n ]-extendable-along-[ f ] P)
Is-extendable-along-propositional ext =
  H-level-cong _ 1 (inverse $ Is-extendable-along≃Path-split ext _) $
  Path-split-propositional ext

-- Is-∞-extendable-along-[ f ] P is propositional (assuming
-- extensionality).

Is-∞-extendable-along-propositional :
  {A : Type a} {B : Type b} {P : B → Type p} {f : A → B} →
  Extensionality (a ⊔ b ⊔ p) (a ⊔ b ⊔ p) →
  Is-proposition (Is-∞-extendable-along-[ f ] P)
Is-∞-extendable-along-propositional ext =
  H-level-cong _ 1 (inverse $ Is-∞-extendable-along≃Path-split-∞ ext) $
  Path-split-∞-propositional ext

-- In the presence of extensionality Is-∞-extendable-along-[_] can be
-- expressed using Is-equivalence.

Is-∞-extendable-along≃Is-equivalence :
  {A : Type a} {B : Type b} {P : B → Type p} {f : A → B} →
  Extensionality (a ⊔ b ⊔ p) (a ⊔ b ⊔ p) →
  Is-∞-extendable-along-[ f ] P ≃
  Is-equivalence (λ (g : ∀ x → P x) → g ∘ f)
Is-∞-extendable-along≃Is-equivalence {P = P} {f = f} ext =
  Is-∞-extendable-along-[ f ] P  ↝⟨ Is-∞-extendable-along≃Path-split-∞ ext ⟩
  Path-split-∞ (_∘ f)            ↝⟨ Path-split-∞↔Is-equivalence ext ⟩□
  Is-equivalence (_∘ f)          □

-- The definitions below are not taken directly from "Universal
-- properties without function extensionality".

-- A type B is P-null for a predicate P of type A → Type p if the
-- function const of type B → P x → B is an equivalence for each x.
--
-- This definition is based on one from "Modalities in Homotopy Type
-- Theory" by Rijke, Shulman and Spitters.

_-Null_ : {A : Type a} → (A → Type p) → Type b → Type (a ⊔ b ⊔ p)
P -Null B = ∀ x → Is-equivalence (const ⦂ (B → P x → B))

-- A variant of _-Null_ with erased proofs.

_-Nullᴱ_ : {A : Type a} → (A → Type p) → Type b → Type (a ⊔ b ⊔ p)
P -Nullᴱ B = ∀ x → Is-equivalenceᴱ (const ⦂ (B → P x → B))

-- Is-∞-extendable-along-[_] can sometimes be replaced by
-- Is-equivalence const.

Is-∞-extendable-along≃Is-equivalence-const :
  {A : Type a} {B : Type b} →
  Extensionality (a ⊔ b) (a ⊔ b) →
  Is-∞-extendable-along-[ (λ (_ : A) → lift tt) ] (λ (_ : ↑ a ⊤) → B) ≃
  Is-equivalence (const ⦂ (B → A → B))
Is-∞-extendable-along≃Is-equivalence-const {a = a} {A = A} {B = B} ext =
  Is-∞-extendable-along-[ (λ _ → lift tt) ] (λ (_ : ↑ a ⊤) → B)  ↝⟨ Is-∞-extendable-along≃Is-equivalence ext ⟩

  Is-equivalence (_∘ (λ _ → lift tt) ⦂ ((↑ a ⊤ → B) → (A → B)))  ↝⟨ inverse $
                                                                    Is-equivalence≃Is-equivalence-∘ʳ
                                                                      (_≃_.is-equivalence $ Eq.↔→≃ (_$ lift tt) const refl refl) ext ⟩□
  Is-equivalence (const ⦂ (B → A → B))                           □

-- A corollary of Is-∞-extendable-along≃Is-equivalence-const.

Π-Is-∞-extendable-along≃Null :
  {A : Type a} {P : A → Type p} {B : Type b} →
  Extensionality (a ⊔ b ⊔ p) (b ⊔ p) →
  (∀ x → Is-∞-extendable-along-[ (λ (_ : P x) → lift tt) ]
           (λ (_ : ↑ p ⊤) → B)) ≃
  P -Null B
Π-Is-∞-extendable-along≃Null {a = a} {p = p} {b = b} ext =
  ∀-cong (lower-extensionality (b ⊔ p) lzero ext) λ _ →
  Is-∞-extendable-along≃Is-equivalence-const
    (lower-extensionality a lzero ext)

private

  -- If const is an equivalence from Bool to B → Bool, then B is not
  -- not inhabited (assuming extensionality).

  Is-equivalence-const→¬¬ :
    {B : Type b} →
    Extensionality b lzero →
    Is-equivalence (const ⦂ (Bool → B → Bool)) →
    ¬ ¬ B
  Is-equivalence-const→¬¬ {B = B} ext =
    curry
      (Is-equivalence (const ⦂ (Bool → B → Bool)) × ¬ B  →⟨ Σ-map Eq.⟨ _ ,_⟩ (Eq.↔⇒≃ ∘ inverse ∘ B.⊥↔uninhabited) ⟩
       Bool ≃ (B → Bool) × B ≃ ⊥                         →⟨ (λ (≃B→ , B≃) → →-cong ext B≃ F.id F.∘ ≃B→) ⟩
       Bool ≃ (⊥ → Bool)                                 →⟨ Π⊥↔⊤ ext F.∘_ ⟩
       Bool ≃ ⊤                                          →⟨ (λ eq → _≃_.to (Eq.≃-≡ eq) (refl _)) ⟩
       true ≡ false                                      →⟨ Bool.true≢false ⟩□
       ⊥                                                 □)

  -- If const is an equivalence from Bool to B → Bool, and equality is
  -- decidable for B, then B is a proposition.

  Is-equivalence-const→Decidable-equality→Is-proposition :
    Is-equivalence (const ⦂ (Bool → B → Bool)) →
    Decidable-equality B →
    Is-proposition B
  Is-equivalence-const→Decidable-equality→Is-proposition
    {B = B} eq _≟_ x y = x≡y
    where
    lemma :
      (f g : B → Bool) → f ≢ g →
      (h : B → Bool) → f ≡ h ⊎ g ≡ h
    lemma =                                                        $⟨ helper ⟩
      ((x y : Bool) → x ≢ y → (z : Bool) → x ≡ z ⊎ y ≡ z)          →⟨ (Π-cong _ equiv λ x → Π-cong _ equiv λ y →
                                                                       →-cong-→ (→-cong-→ (_≃_.from (Eq.≃-≡ equiv)) id) $
                                                                       Π-cong _ equiv λ z →
                                                                       _≃_.from (Eq.≃-≡ equiv ⊎-cong Eq.≃-≡ equiv)) ⟩□
      ((f g : B → Bool) → f ≢ g → (h : B → Bool) → f ≡ h ⊎ g ≡ h)  □
      where
      equiv : Bool ≃ (B → Bool)
      equiv = Eq.⟨ _ , eq ⟩

      true≡⊎false≡ : (b : Bool) → true ≡ b ⊎ false ≡ b
      true≡⊎false≡ true  = inj₁ (refl _)
      true≡⊎false≡ false = inj₂ (refl _)

      helper : (x y : Bool) → x ≢ y → (z : Bool) → x ≡ z ⊎ y ≡ z
      helper true  true  t≢t = ⊥-elim $ t≢t (refl _)
      helper true  false _   = true≡⊎false≡
      helper false true  _   = _↔_.to ⊎-comm ∘ true≡⊎false≡
      helper false false f≢f = ⊥-elim $ f≢f (refl _)

    f₁ f₂ f₃ : B → Bool
    f₁ _ = true
    f₂ _ = false
    f₃ z = if x ≟ z then true else false

    f₁≢f₂ : f₁ ≢ f₂
    f₁≢f₂ f₁≡f₂ = Bool.true≢false $ cong (_$ x) f₁≡f₂

    f₁≡f₃→x≡y : f₁ ≡ f₃ → x ≡ y
    f₁≡f₃→x≡y f₁≡f₃ = helper (x ≟ y) (cong (_$ y) f₁≡f₃)
      where
      helper :
        (d : Dec (x ≡ y)) →
        true ≡ if d then true else false →
        x ≡ y
      helper (yes x≡y) _          = x≡y
      helper (no  _)   true≡false =
        ⊥-elim $ Bool.true≢false true≡false

    f₂≢f₃ : f₂ ≢ f₃
    f₂≢f₃ =
      f₂ ≡ f₃                                →⟨ cong (_$ x) ⟩
      false ≡ if x ≟ x then true else false  →⟨ flip trans (helper (x ≟ x)) ⟩
      false ≡ true                           →⟨ Bool.true≢false ∘ sym ⟩□
      ⊥                                      □
      where
      helper :
        (d : Dec (x ≡ x)) →
        if d then true else false ≡ true
      helper (yes _)  = refl _
      helper (no x≢x) = ⊥-elim $ x≢x $ refl _

    f₁≡⊎f₂≡ : (f : B → Bool) → f₁ ≡ f ⊎ f₂ ≡ f
    f₁≡⊎f₂≡ = lemma f₁ f₂ f₁≢f₂

    x≡y : x ≡ y
    x≡y with f₁≡⊎f₂≡ f₃
    … | inj₁ f₁≡f₃ = f₁≡f₃→x≡y f₁≡f₃
    … | inj₂ f₂≡f₃ = ⊥-elim $ f₂≢f₃ f₂≡f₃

-- If f is an equivalence, then n-extendability along f is
-- contractible (assuming extensionality).

Is-extendable-along-contractible-for-equivalences :
  {A : Type a} {B : Type b} {f : A → B} {P : B → Type p} →
  Extensionality (a ⊔ b ⊔ p) (a ⊔ b ⊔ p) →
  Is-equivalence f →
  ∀ n → Contractible (Is-[ n ]-extendable-along-[ f ] P)
Is-extendable-along-contractible-for-equivalences _ _ zero =
  ↑-closure 0 ⊤-contractible

Is-extendable-along-contractible-for-equivalences
  {a = a} {b = b} {p = p} {f = f} {P = P} ext eq (suc n) =

  ×-closure 0
    (Π-closure (lower-extensionality b lzero ext) 0 λ g →
                                                             $⟨ singleton-contractible _ ⟩
       Contractible (∃ λ h → h ≡ subst P (inv _) ∘ g ∘ f⁻¹)  ↝⟨ H-level-cong _ 0 (lemma g) ⦂ (_ → _) ⟩□
       Contractible (∃ λ h → ∀ x → h (f x) ≡ g x)            □)
    (Π-closure (lower-extensionality a lzero ext) 0 λ _ →
     Π-closure (lower-extensionality a lzero ext) 0 λ _ →
     Is-extendable-along-contractible-for-equivalences ext eq n)
  where
  f⁻¹ = _≃_.from Eq.⟨ _ , eq ⟩
  inv = _≃_.left-inverse-of (inverse Eq.⟨ _ , eq ⟩)

  lemma : ∀ _ → _ ≃ _
  lemma g =
    (∃ λ h → h ≡ subst P (inv _) ∘ g ∘ f⁻¹)  ↔⟨ (∃-cong λ h → inverse $
                                                 ∘from≡↔≡∘to′ (lower-extensionality p (a ⊔ b) ext) (inverse Eq.⟨ _ , eq ⟩)) ⟩
    (∃ λ h → h ∘ f ≡ g)                      ↝⟨ (∃-cong λ _ → inverse $
                                                 Eq.extensionality-isomorphism (lower-extensionality (b ⊔ p) (a ⊔ b) ext)) ⟩□
    (∃ λ h → ∀ x → h (f x) ≡ g x)            □

-- If f is an equivalence, then ∞-extendability along f is
-- contractible (assuming extensionality).

Is-∞-extendable-along-contractible-for-equivalences :
  {A : Type a} {B : Type b} {f : A → B} {P : B → Type p} →
  Extensionality (a ⊔ b ⊔ p) (a ⊔ b ⊔ p) →
  Is-equivalence f →
  Contractible (Is-∞-extendable-along-[ f ] P)
Is-∞-extendable-along-contractible-for-equivalences ext eq =
  Π-closure (lower-extensionality _ lzero ext) 0 λ n →
  Is-extendable-along-contractible-for-equivalences ext eq n

------------------------------------------------------------------------
-- Alternatives to Is-[_]-extendable-along-[_] and
-- Is-∞-extendable-along-[_]

-- A variant of Is-[_]-extendable-along-[_].

Is-[_]-extendable-along-const-tt-[_] :
  ℕ → Type a → Type b → Type (a ⊔ b)
Is-[ zero  ]-extendable-along-const-tt-[ A ] B = ↑ _ ⊤
Is-[ suc n ]-extendable-along-const-tt-[ A ] B =
  ((g : A → B) → ∃ λ (x : B) → ∀ y → x ≡ g y) ×
  ((x y : B) → Is-[ n ]-extendable-along-const-tt-[ A ] (x ≡ y))

-- A variant of Is-∞-extendable-along-[_].

Is-∞-extendable-along-const-tt-[_] :
  Type a → Type b → Type (a ⊔ b)
Is-∞-extendable-along-const-tt-[ A ] B =
  ∀ n → Is-[ n ]-extendable-along-const-tt-[ A ] B

-- In some cases Is-[_]-extendable-along-[_] and
-- Is-∞-extendable-along-[_] can be replaced by the variants.

≃Is-extendable-along-const-tt :
  {A : Type a} {B : Type b} →
  Extensionality? k (a ⊔ b) (a ⊔ b) →
  ∀ n →
  Is-[ n ]-extendable-along-[ (λ (_ : A) → lift tt) ]
    (λ (_ : ↑ a ⊤) → B) ↝[ k ]
  Is-[ n ]-extendable-along-const-tt-[ A ] B
≃Is-extendable-along-const-tt                 _   zero    = F.id
≃Is-extendable-along-const-tt {a = a} {k = k} ext (suc n) =
  (∀-cong ext λ _ →
   Σ-cong (Eq.↔→≃ (_$ lift tt) const refl refl) λ _ →
   F.id)
    ×-cong
  Π-cong ext′ (Eq.↔→≃ (_$ lift tt) const refl refl) λ _ →
  Π-cong ext′ (Eq.↔→≃ (_$ lift tt) const refl refl) λ _ →
  ≃Is-extendable-along-const-tt ext n
  where
  ext′ = lower-extensionality? k a lzero ext

≃Is-∞-extendable-along-const-tt :
  {A : Type a} {B : Type b} →
  Extensionality? k (a ⊔ b) (a ⊔ b) →
  Is-∞-extendable-along-[ (λ (_ : A) → lift tt) ] (λ (_ : ↑ a ⊤) → B)
    ↝[ k ]
  Is-∞-extendable-along-const-tt-[ A ] B
≃Is-∞-extendable-along-const-tt {k = k} ext =
  ∀-cong (lower-extensionality? k _ lzero ext) λ n →
  ≃Is-extendable-along-const-tt ext n

-- Preservation lemmas for Is-[_]-extendable-along-[_] and
-- Is-∞-extendable-along-[_].

Is-extendable-along-const-tt-cong :
  {A : Type a} {B : Type b} {C : Type c} →
  Extensionality? k (a ⊔ b ⊔ c) (a ⊔ b ⊔ c) →
  (A≃B : A ≃ B)
  (A→≃B→ : {C : Type c} → (A → C) ≃ (B → C)) →
  ({C : Type c} (f : A → C) (x : A) →
   _≃_.to A→≃B→ f (_≃_.to A≃B x) ≡ f x) →
  ∀ n →
  Is-[ n ]-extendable-along-const-tt-[ A ] C ↝[ k ]
  Is-[ n ]-extendable-along-const-tt-[ B ] C
Is-extendable-along-const-tt-cong _ _ _ _ zero =
  ↑ _ ⊤  ↔⟨ B.↑↔ ⟩
  ⊤      ↔⟨ inverse B.↑↔ ⟩□
  ↑ _ ⊤  □
Is-extendable-along-const-tt-cong
  {a = a} {b = b} {c = c} {k = k} ext A≃B A→≃B→ hyp (suc n) =

  (Π-cong ext A→≃B→ λ g →
   ∃-cong λ x →
   Π-cong (lower-extensionality? k c (a ⊔ b) ext) A≃B λ y →
     x ≡ g y                            ↝⟨ ≡⇒↝ _ $ cong (_ ≡_) $ sym $ hyp g y ⟩□
     x ≡ _≃_.to A→≃B→ g (_≃_.to A≃B y)  □)
    ×-cong
  (∀-cong (lower-extensionality? k (a ⊔ b) lzero ext) λ x →
   ∀-cong (lower-extensionality? k (a ⊔ b) lzero ext) λ y →
   Is-extendable-along-const-tt-cong ext A≃B A→≃B→ hyp n)

Is-∞-extendable-along-const-tt-cong :
  {A : Type a} {B : Type b} {C : Type c} →
  Extensionality? k (a ⊔ b ⊔ c) (a ⊔ b ⊔ c) →
  (A≃B : A ≃ B)
  (A→≃B→ : {C : Type c} → (A → C) ≃ (B → C)) →
  ({C : Type c} (f : A → C) (x : A) →
   _≃_.to A→≃B→ f (_≃_.to A≃B x) ≡ f x) →
  Is-∞-extendable-along-const-tt-[ A ] C ↝[ k ]
  Is-∞-extendable-along-const-tt-[ B ] C
Is-∞-extendable-along-const-tt-cong {k = k} ext A≃B A→≃B→ hyp =
  ∀-cong (lower-extensionality? k _ lzero ext) λ n →
  Is-extendable-along-const-tt-cong ext A≃B A→≃B→ hyp n


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

open import Base

module Spaces.Circle where

{-
Idea :

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

I’m using Dan Licata’s trick to have a higher inductive type with definitional
reduction rule for [base]
-}

private
  data #S¹ : Set where
    #base : #S¹

S¹ : Set
S¹ = #S¹

base : S¹
base = #base

postulate  -- HIT
  loop : base ≡ base

S¹-rec : ∀ {i} (P : S¹ → Set i) (x : P base) (p : transport P loop x ≡ x)
  → ((t : S¹) → P t)
S¹-rec P x p #base = x

postulate  -- HIT
  S¹-β-loop : ∀ {i} (P : S¹ → Set i) (x : P base) (p : transport P loop x ≡ x)
      → apd (S¹-rec P x p) loop ≡ p

S¹-rec-nondep : ∀ {i} (A : Set i) (x : A) (p : x ≡ x) → (S¹ → A)
S¹-rec-nondep A x p #base = x

postulate  -- HIT
  S¹-β-loop-nondep : ∀ {i} (A : Set i) (x : A) (p : x ≡ x)
    → ap (S¹-rec-nondep A x p) loop ≡ p

-- S¹-rec-nondep : ∀ {i} (A : Set i) (x : A) (p : x ≡ x) → (S¹ → A)
-- S¹-rec-nondep A x p = S¹-rec (λ _ → A) x (trans-cst loop x ∘ p)

-- β-nondep : ∀ {i} (A : Set i) (x : A) (p : x ≡ x)
--   → ap (S¹-rec-nondep A x p) loop ≡ p
-- β-nondep A x p =
--   apd-trivial (S¹-rec-nondep A x p) loop ∘
--   (whisker-left (! (trans-cst loop _)) (β (λ _ → A) x (trans-cst loop _ ∘ p))
--   ∘ (! (concat-assoc (! (trans-cst loop _)) (trans-cst loop _) p)
--   ∘ whisker-right p (opposite-left-inverse (trans-cst loop _))))


{-# OPTIONS --sized-types #-}
module BinTree where

open import Data.Nat
open import Data.List
open import Data.List.Properties
open import Size

data Bin {T : Set} {i : Size} : Set where
  Leaf : Bin {T} {i}
  Node : T → Bin {T} {i} → Bin {T} {i} → Bin {T} {i}

data Rose {T : Set} {i : Size} : Set where
  Empty : Rose {T} {i}
  R : T → List (Rose {T} {i}) → Rose {T} {i}

BinListToBin : {T : Set} → List (Bin {T}) → Bin {T}
BinListToBin [] = Leaf
BinListToBin (Leaf ∷ bs) = BinListToBin bs
BinListToBin (Node x b b₁ ∷ bs) = Node x b (BinListToBin bs)

Seperate : {T : Set} → Bin {T} → List (Bin {T})
Seperate Leaf = []
Seperate (Node x b b₁) = Node x b Leaf ∷ Seperate b₁

RoseToBin : {i : Size} → {T : Set} → Rose {T} {∞} → Bin {T} {∞}
RoseToBin Empty = Leaf
RoseToBin (R x x₁) = Node x (BinListToBin (map RoseToBin x₁)) Leaf

BinToRose : {T : Set} → Bin {T} → Rose {T}
BinToRose Leaf = Empty
BinToRose (Node x b b₁) = R x (map BinToRose (Seperate b₁))


module plfa-code.Bin where

-- I collect code about Bin there. other definitions I use the std-lib version

import Relation.Binary.PropositionalEquality as Eq
open Eq using (_≡_; refl; cong; sym; trans)
open Eq.≡-Reasoning using (begin_; _≡⟨⟩_; _∎)
open import Data.Nat using (ℕ; zero; suc; _+_; _*_; _∸_; _≤_; z≤n; s≤s)
open import Data.Nat.Properties using (+-comm; +-suc; +-mono-≤)

infixr 2 _≡⟨_⟩_

_≡⟨_⟩_ : ∀ {A : Set} (x : A) {y z : A}
  → x ≡ y
  → y ≡ z
    -------
  → x ≡ z
x ≡⟨ x≡y ⟩ y≡z  =  trans x≡y y≡z

data Bin : Set where
  nil : Bin
  x0_ : Bin → Bin
  x1_ : Bin → Bin

inc : Bin → Bin
inc nil    = x1 nil
inc (x0 t) = x1 t
inc (x1 t) = x0 (inc t)

to : ℕ → Bin
to zero    = x0 nil
to (suc n) = inc (to n)

from : Bin → ℕ
from nil      = 0
from (x0 t)   = 2 * (from t)
from (x1 t)   = suc (2 * (from t))

+1≡suc : ∀ {n : ℕ} → n + 1 ≡ suc n
+1≡suc {zero}   = refl
+1≡suc {suc n}  = cong suc +1≡suc

suc-from-inc : ∀ (x : Bin) → from (inc x) ≡ suc (from x)
suc-from-inc nil                                        = refl
suc-from-inc (x0 x) rewrite +1≡suc {from x * 2}         = refl
suc-from-inc (x1 x) rewrite suc-from-inc x
                          | +-suc (from x) (from x + 0) = refl

-- t4 is ⊥ , because `to (from nil) ≡ x0 nil ≢ nil`
-- t4 : ∀ (x : Bin) → to (from x) ≡ x

from-to-const : ∀ (n : ℕ) → from (to n) ≡ n
from-to-const zero                               =  refl
from-to-const (suc n) rewrite suc-from-inc (to n)
                            | from-to-const n    =  refl

data Can : Bin → Set
data One : Bin → Set

data One where
  one  : One (x1 nil)
  x0_ : ∀ {b : Bin} → One b → One(x0 b)
  x1_ : ∀ {b : Bin} → One b → One(x1 b)

data Can where
  zero     : Can (x0 nil)
  can-one  : ∀ {b : Bin} → One b → Can b

one-inc : ∀ {x : Bin} → One x → One (inc x)
one-inc one = x0 one
one-inc (x0 x) = x1 x
one-inc (x1 x) = x0 (one-inc x)

can-inc : ∀ {x : Bin} → Can x → Can (inc x)
can-inc zero        =  can-one one
can-inc (can-one x) =  can-one (one-inc x)

one-to-n : ∀ (n : ℕ) → One (to (suc n))
one-to-n zero = one
one-to-n (suc n) = one-inc (one-to-n n)

can-to-n : ∀ (n : ℕ) → Can (to n)
can-to-n zero = zero
can-to-n (suc n) = can-one (one-to-n n)

open Data.Nat.Properties using (+-identityʳ; +-suc)

l0 : ∀ (n) → to (suc n) ≡ inc (to n)
l0 zero    = refl
l0 (suc n) = refl

2n-eq-x0 : ∀ (n) → 1 ≤ n → to (n + n) ≡ x0 (to n)
2n-eq-x0 zero ()
2n-eq-x0 (suc zero) (s≤s z≤n) = refl
2n-eq-x0 (suc (suc n)) (s≤s z≤n) =
  begin to (suc (suc n) + suc (suc n))
  ≡⟨ cong (λ x → to x) (+-suc (suc (suc n)) (suc n)) ⟩  to (suc (suc (suc n + suc n)))
  ≡⟨⟩  inc (inc (to (suc n + suc n)))
  ≡⟨ cong (λ x → (inc (inc x))) (2n-eq-x0 (suc n) (s≤s z≤n)) ⟩  inc (inc (x0 (to (suc n))))
  ≡⟨⟩  x0 (inc (to (suc n)))
  ≡⟨⟩  x0 (to (suc (suc n)))
  ∎

one-b-iff-1≤b : ∀ (b) → One b → 1 ≤ from b
one-b-iff-1≤b (x0 b) (x0 ob)
              rewrite +-identityʳ (from b) = +-mono-≤ (one-b-iff-1≤b b ob) z≤n
              where n = from b
one-b-iff-1≤b (x1 .nil) one     = s≤s z≤n
one-b-iff-1≤b (x1 b)    (x1 ob) = s≤s z≤n

one-tf-eq : ∀ {b} → One b → to (from b) ≡ b
one-tf-eq {_}    one    = refl
one-tf-eq {x0 b} (x0 x) =
  begin
    to (from (x0 b))
  ≡⟨⟩
    to (from b + (from b + zero))
  ≡⟨ cong (λ n → to (from b + n)) (+-identityʳ (from b)) ⟩
    to (from b + from b)
  ≡⟨ 2n-eq-x0 (from b) (one-b-iff-1≤b b x) ⟩
    x0 (to (from b))
  ≡⟨ cong x0_ (one-tf-eq x) ⟩
    x0 b
  ∎
one-tf-eq {x1 b} (x1 x) =
  begin
    to (from (x1 b))
  ≡⟨⟩
    inc (to (from b + (from b + zero)))
  ≡⟨ cong (λ n → inc (to (from b + n))) (+-identityʳ (from b)) ⟩
    inc (to (from b + from b))
  ≡⟨ cong inc (2n-eq-x0 (from b) (one-b-iff-1≤b b x)) ⟩
    inc (x0 (to (from b)))
  ≡⟨⟩
    x1 (to (from b))
  ≡⟨ cong x1_ (one-tf-eq x) ⟩
    x1 b
  ∎

can-tf-eq : ∀ {x} → Can x → to (from x) ≡ x
can-tf-eq {_}  zero        = refl
can-tf-eq {b}  (can-one x) = one-tf-eq x

open import Function.LeftInverse using (_↞_)

ℕ-embedding-Bin : ℕ ↞ Bin
ℕ-embedding-Bin =
  record
  { to              = record { _⟨$⟩_ = to   ; cong = cong to }
  ; from            = record { _⟨$⟩_ = from ; cong = cong from }
  ; left-inverse-of = from-to-const
  }

open import Function.Inverse using (_↔_)
open import Data.Product
  using (Σ; ∃; Σ-syntax; ∃-syntax) renaming (_,_ to ⟨_,_⟩)
open import Function using (_∘_)

one-assim : ∀ {b : Bin} → (x : One b) → (y : One b) → x ≡ y
one-assim one one = refl
one-assim (x0 x) (x0 y) = sym (cong x0_ (one-assim y x))
one-assim (x1 x) (x1 y) = sym (cong x1_ (one-assim y x))

can-assim : ∀ {b : Bin} → (x : Can b) → (y : Can b) → x ≡ y
can-assim zero zero = refl
can-assim zero (can-one (x0 ()))
can-assim (can-one (x0 ())) zero
can-assim (can-one one) (can-one one) = refl
can-assim (can-one (x0 x)) (can-one (x0 y)) = cong (can-one ∘ x0_) (one-assim x y)
can-assim (can-one (x1 x)) (can-one (x1 y)) = cong (can-one ∘ x1_) (one-assim x y)

∃-proj₁ : {A : Set} {B : A → Set} → ∃[ x ] B x → A
∃-proj₁ ⟨ x , _ ⟩ = x

∃≡ : ∀ {A : Set} {B : A → Set} (p₁ : ∃[ x ](B x)) (p₂ : ∃[ y ](B y))
  → ∃-proj₁ p₁ ≡ ∃-proj₁ p₂ → (∀ (x) → (b₁ : B x) → (b₂ : B x) → b₁ ≡ b₂)
  → p₁ ≡ p₂
∃≡ ⟨ x , bx ⟩ ⟨ .x , by ⟩ refl f = sym (cong (⟨_,_⟩ x) (f x by bx))

Bin-isomorphism : ℕ ↔ ∃[ x ](Can x)
Bin-isomorphism =
  record
  { to         = record { _⟨$⟩_ = to′; cong = cong to′}
  ; from       = record { _⟨$⟩_ = from′; cong = cong from′}
  ; inverse-of = record
                 { left-inverse-of = from-to-const
                 ; right-inverse-of = to∘from′
                 }
  }
  where
  to′ : ℕ → ∃[ x ](Can x)
  to′ n = ⟨ to n , can-to-n n ⟩
  from′ : ∃[ x ](Can x) → ℕ
  from′ ⟨ b , can-b ⟩ = from b
  to∘from′ : ∀ y → to′ (from′ y) ≡ y
  to∘from′ ⟨ b , can-b ⟩  = ∃≡ ⟨ to (from b) , can-to-n (from b) ⟩ ⟨ b , can-b ⟩ (can-tf-eq can-b) (λ x → can-assim {x})


------------------------------------------------------------------------
-- Results relating different instances of certain axioms related to
-- equality
------------------------------------------------------------------------

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

module Equality.Instances-related where

import Bijection
open import Equality
import Equivalence
import Function-universe
import H-level
import H-level.Closure
open import Logical-equivalence using (_⇔_)
open import Prelude
import Surjection

private
  module E (e⁺ : ∀ e → Equivalence-relation⁺ e) where
    open Reflexive-relation′
           (λ e → Equivalence-relation⁺.reflexive-relation (e⁺ e))
         public

-- All families of equality types that satisfy certain axioms are
-- pointwise isomorphic. One of the families are used to define what
-- "isomorphic" means.
--
-- The isomorphisms map reflexivity to reflexivity in both directions.
--
-- Hofmann and Streicher prove something similar in "The groupoid
-- interpretation of type theory".

all-equality-types-isomorphic :
  ∀ {e₁ e₂}
  (eq₁ : ∀ {a p} → Equality-with-J a p e₁)
  (eq₂ : ∀ {a p} → Equality-with-J a p e₂) →
  let open Bijection eq₁ in
  ∀ {a} {A : Type a} →
  ∃ λ (iso : {x y : A} → E._≡_ e₁ x y ↔ E._≡_ e₂ x y) →
    (∀ {x} → E._≡_ e₂ (_↔_.to   iso (E.refl e₁ x)) (E.refl e₂ x)) ×
    (∀ {x} → E._≡_ e₁ (_↔_.from iso (E.refl e₂ x)) (E.refl e₁ x))
all-equality-types-isomorphic {c₁} {c₂} eq₁ eq₂ =
    record
      { surjection = record
        { logical-equivalence = record
          { to   = to c₂ eq₁
          ; from = to c₁ eq₂
          }
        ; right-inverse-of = to c₁ eq₂ ∘ to∘to _ _ eq₂ eq₁
        }
      ; left-inverse-of = to∘to _ _ eq₁ eq₂
      }
  , to-refl c₂ eq₁
  , to-refl c₁ eq₂
  where
  open E
  open module Eq {c} (eq : ∀ {a p} → Equality-with-J a p c) =
    Equality-with-J′ eq hiding (_≡_; refl)

  to : ∀ {c₁} c₂ (eq₁ : ∀ {a p} → Equality-with-J a p c₁)
       {a} {A : Type a} {x y : A} →
       _≡_ c₁ x y → _≡_ c₂ x y
  to c₂ eq₁ {x = x} x≡y = subst eq₁ (_≡_ c₂ x) x≡y (refl c₂ x)

  to-refl :
    ∀ {c₁} c₂ (eq₁ : ∀ {a p} → Equality-with-J a p c₁)
    {a} {A : Type a} {x : A} →
    _≡_ c₂ (to c₂ eq₁ (refl c₁ x)) (refl c₂ x)
  to-refl c₂ eq₁ = to c₂ eq₁ $ subst-refl eq₁ (_≡_ c₂ _) _

  to∘to : ∀ c₁ c₂
          (eq₁ : ∀ {a p} → Equality-with-J a p c₁)
          (eq₂ : ∀ {a p} → Equality-with-J a p c₂) →
          ∀ {a} {A : Type a} {x y : A} (x≡y : _≡_ c₁ x y) →
          _≡_ c₁ (to c₁ eq₂ (to c₂ eq₁ x≡y)) x≡y
  to∘to c₁ c₂ eq₁ eq₂ = elim eq₁
    (λ {x y} x≡y → _≡_ c₁ (to c₁ eq₂ (to c₂ eq₁ x≡y)) x≡y)
    (λ x → to c₁ eq₂ (to c₂ eq₁ (refl c₁ x))  ≡⟨ cong eq₁ (to c₁ eq₂) (subst-refl eq₁ (_≡_ c₂ x) (refl c₂ x)) ⟩
           to c₁ eq₂ (refl c₂ x)              ≡⟨ to c₁ eq₂ $ subst-refl eq₂ (_≡_ c₁ x) (refl c₁ x) ⟩∎
           refl c₁ x                          ∎)
    where
    open Derived-definitions-and-properties eq₁
      using (step-≡; finally)

module _ {congruence⁺}
         (eq : ∀ {a p} → Equality-with-J a p congruence⁺)
         where

  open Derived-definitions-and-properties eq
  open Function-universe eq hiding (_∘_) renaming (id to ⟨id⟩)
  open H-level.Closure eq
  open Surjection eq using (_↠_)

  private
    open module Eq = Equivalence eq using (_≃_)

  abstract

    -- The type Equality-with-J₀ a p reflexive-relation is
    -- contractible (given the assumptions in the telescope above, and
    -- assuming extensionality). A slight variant of this observation
    -- came up in a discussion between Thierry Coquand, Simon Huber
    -- and Nicolai Kraus. The proof is based on one due to Nicolai
    -- Kraus.

    Equality-with-J-contractible :
      ∀ {a p} →
      Extensionality (lsuc (a ⊔ p)) (a ⊔ lsuc p) →
      Contractible (Equality-with-J₀ a p (λ _ → reflexive-relation))
    Equality-with-J-contractible {a} {p} ext =                        $⟨ contr ⟩
      Contractible ((A : Type a) (P : I A → Type p)
                    (d : ∀ x → P (to x)) → Singleton d)               ↝⟨ H-level.respects-surjection eq surj 0 ⟩

      Contractible (Equality-with-J₀ a p (λ _ → reflexive-relation))  □
      where
      I : Type a → Type a
      I A = ∃ λ (x : A) → ∃ λ (y : A) → x ≡ y

      to : ∀ {A} → A → I A
      to x = x , x , refl x

      ≃I : ∀ {A} → A ≃ I A
      ≃I = Eq.↔⇒≃ (record
        { surjection = record
          { logical-equivalence = record
            { to   = to
            ; from = proj₁
            }
          ; right-inverse-of = λ q →
              let x , y , x≡y = q in
              cong (x ,_) $
                Σ-≡,≡→≡ x≡y
                        (subst (x ≡_) x≡y (refl x)  ≡⟨ sym trans-subst ⟩
                         trans (refl x) x≡y         ≡⟨ trans-reflˡ x≡y ⟩∎
                         x≡y                        ∎)
          }
        ; left-inverse-of = refl
        })

      contr :
        Contractible ((A : Type a) (P : I A → Type p)
                      (d : ∀ x → P (to x)) → Singleton d)
      contr =
        Π-closure          (lower-extensionality (lsuc p) lzero    ext) 0 λ _ →
        Π-closure          (lower-extensionality (lsuc a) (lsuc p) ext) 0 λ _ →
        Π-closure          (lower-extensionality _        (lsuc p) ext) 0 λ _ →
        singleton-contractible _

      surj :
        ((A : Type a) (P : I A → Type p)
         (d : ∀ x → P (to x)) → Singleton d) ↠
        Equality-with-J₀ a p (λ _ → reflexive-relation)
      surj =
        ((A : Type a) (P : I A → Type p)
         (d : ∀ x → P (to x)) → Singleton d)                    ↔⟨⟩

        ((A : Type a) (P : I A → Type p)
         (d : ∀ x → P (to x)) →
         ∃ λ (j : (x : A) → P (to x)) → j ≡ d)                  ↔⟨ (∀-cong (lower-extensionality (lsuc p) lzero ext) λ _ →
                                                                              ∀-cong (lower-extensionality (lsuc a) (lsuc p) ext) λ _ →
                                                                              ∀-cong (lower-extensionality _ (lsuc p) ext) λ _ →
                                                                              ∃-cong λ _ → inverse $
                                                                              Eq.extensionality-isomorphism (lower-extensionality _ _ ext)) ⟩
        ((A : Type a) (P : I A → Type p)
         (d : ∀ x → P (to x)) →
         ∃ λ (j : (x : A) → P (to x)) → (x : A) → j x ≡ d x)    ↔⟨ (∀-cong (lower-extensionality (lsuc p) lzero ext) λ _ →
                                                                              ∀-cong (lower-extensionality (lsuc a) (lsuc p) ext) λ _ →
                                                                              ∀-cong (lower-extensionality _ (lsuc p) ext) λ _ → inverse $
                                                                              Σ-cong (inverse $ Π-cong (lower-extensionality _ _ ext)
                                                                                ≃I λ _ → ⟨id⟩ {k = equivalence}) λ _ →
                                                                              ⟨id⟩ {k = equivalence}) ⟩
        ((A : Type a) (P : I A → Type p)
         (d : ∀ x → P (to x)) →
         ∃ λ (j : (q : I A) → P q) → (x : A) → j (to x) ≡ d x)  ↔⟨ (∀-cong (lower-extensionality (lsuc p) lzero ext) λ _ →
                                                                              ∀-cong (lower-extensionality (lsuc a) (lsuc p) ext) λ _ →
                                                                              ΠΣ-comm) ⟩
        ((A : Type a) (P : I A → Type p) →
         ∃ λ (j : (d : ∀ x → P (to x)) →
                  (q : I A) → P q) →
             (d : ∀ x → P (to x))
             (x : A) → j d (to x) ≡ d x)                        ↔⟨ (∀-cong (lower-extensionality (lsuc p) lzero ext) λ _ →
                                                                              ΠΣ-comm) ⟩
        ((A : Type a) →
         ∃ λ (j : (P : I A → Type p) (d : ∀ x → P (to x)) →
                  (q : I A) → P q) →
             (P : I A → Type p) (d : ∀ x → P (to x))
             (x : A) → j P d (to x) ≡ d x)                      ↔⟨ ΠΣ-comm ⟩

        (∃ λ (J : (A : Type a) (P : I A → Type p)
                  (d : ∀ x → P (to x)) →
                  (q : I A) → P q) →
             (A : Type a) (P : I A → Type p)
             (d : ∀ x → P (to x))
             (x : A) → J A P d (to x) ≡ d x)                    ↝⟨ record
                                                                     { logical-equivalence = record
                                                                       { to   = uncurry λ J Jβ → record
                                                                                { elim      = λ P r x≡y →
                                                                                J _ (P ∘ proj₂ ∘ proj₂) r (_ , _ , x≡y)
                                                                                ; elim-refl = λ P r → Jβ _ (P ∘ proj₂ ∘ proj₂) r _
                                                                                }
                                                                       ; from = λ eq →
                                                                             (λ A P d q → Equality-with-J₀.elim eq
                                                                                (λ x≡y → P (_ , _ , x≡y)) d (proj₂ (proj₂ q)))
                                                                           , (λ A P d x → Equality-with-J₀.elim-refl eq
                                                                                (λ x≡y → P (_ , _ , x≡y)) d)
                                                                       }
                                                                     ; right-inverse-of = refl
                                                                     } ⟩□
        Equality-with-J₀ a p (λ _ → reflexive-relation)         □

  private

    -- A related example. It had been suggested that the two proofs
    -- proof₁ and proof₂ below might not be provably equal, but Simon
    -- Huber managed to prove that they are (using a slightly
    -- different type for elim-refl).

    module _ {a p} {A : Type a}
             (P : A → Type p)
             {x : A} (y : P x) where

      subst′ : {x y : A} (P : A → Type p) → x ≡ y → P x → P y
      subst′ P = elim (λ {u v} _ → P u → P v) (λ _ p → p)

      subst′-refl≡id : {x : A} (P : A → Type p) → subst′ P (refl x) ≡ id
      subst′-refl≡id P = elim-refl (λ {u v} _ → P u → P v) (λ _ p → p)

      proof₁ proof₂ :
        subst′ P (refl x) (subst′ P (refl x) y) ≡ subst′ P (refl x) y

      proof₁ = cong (_$ subst′ P (refl x) y) (subst′-refl≡id P)

      proof₂ = cong (subst′ P (refl x)) (cong (_$ y) (subst′-refl≡id P))

      proof₁≡proof₂ : proof₁ ≡ proof₂
      proof₁≡proof₂ =
        subst (λ (s : Singleton id) →
                 ∀ y → cong (_$ proj₁ s y) (proj₂ s) ≡
                       cong (proj₁ s) (cong (_$ y) (proj₂ s)))

              (id                , refl id           ≡⟨ proj₂ (singleton-contractible _) _ ⟩∎
               subst′ P (refl x) , subst′-refl≡id P  ∎)

              (λ y →
                 cong (_$ y) (refl id)            ≡⟨ cong-id _ ⟩∎
                 cong id (cong (_$ y) (refl id))  ∎)

              y


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

open import Categories.Category
open import Categories.Functor

module Categories.Adjoint.AFT.SolutionSet {o ℓ e o′ ℓ′ e′} {C : Category o ℓ e} {D : Category o′ ℓ′ e′}
                                          (F : Functor C D) where

open import Level

private
  module C = Category C
  module D = Category D
  module F = Functor F
  open D
  open F

record SolutionSet i : Set (o ⊔ ℓ ⊔ o′ ⊔ ℓ′ ⊔ e′ ⊔ (suc i)) where
  field
    I       : Set i
    S       : I → C.Obj
    S₀      : ∀ {A X} → X ⇒ F₀ A → I
    S₁      : ∀ {A X} (f : X ⇒ F₀ A) → S (S₀ f) C.⇒ A
    ϕ       : ∀ {A X} (f : X ⇒ F₀ A) → X ⇒ F₀ (S (S₀ f))
    commute : ∀ {A X} (f : X ⇒ F₀ A) → F₁ (S₁ f) ∘ ϕ f ≈ f

record SolutionSet′ : Set (o ⊔ ℓ ⊔ o′ ⊔ ℓ′ ⊔ e′) where
  field
    S₀      : ∀ {A X} → X ⇒ F₀ A → C.Obj
    S₁      : ∀ {A X} (f : X ⇒ F₀ A) → S₀ f C.⇒ A
    ϕ       : ∀ {A X} (f : X ⇒ F₀ A) → X ⇒ F₀ (S₀ f)
    commute : ∀ {A X} (f : X ⇒ F₀ A) → F₁ (S₁ f) ∘ ϕ f ≈ f

SolutionSet⇒SolutionSet′ : ∀ {i} → SolutionSet i → SolutionSet′
SolutionSet⇒SolutionSet′ s = record
  { S₀      = λ f → S (S₀ f)
  ; S₁      = S₁
  ; ϕ       = ϕ
  ; commute = commute
  }
  where open SolutionSet s

SolutionSet′⇒SolutionSet : ∀ i → SolutionSet′ → SolutionSet (o ⊔ i)
SolutionSet′⇒SolutionSet i s = record
  { I       = Lift i C.Obj
  ; S       = lower
  ; S₀      = λ f → lift (S₀ f)
  ; S₁      = S₁
  ; ϕ       = ϕ
  ; commute = commute
  }
  where open SolutionSet′ s


module Thesis.SIRelBigStep.Normalization where

open import Thesis.SIRelBigStep.Lang

open import Data.Unit.Base hiding (_≤_)
open import Data.Product
open import Relation.Binary.PropositionalEquality

-- Define logical relation for normalization. Adapted from TAPL Ch. 12.
mutual
  normT : ∀ {Γ} τ (t : Term Γ τ) (ρ : ⟦ Γ ⟧Context) → Set
  normT τ t ρ = Σ[ v ∈ Val τ ] ρ ⊢ t ↓[ no ] v × normV τ v

  normV : ∀ τ (v : Val τ) → Set
  normV (σ ⇒ τ) (closure t ρ) = ∀ (v : Val σ) → (vv : normV _ v) → normT τ t (v • ρ)
  normV (pair τ1 τ2) (pairV v1 v2) = normV _ v1 × normV _ v2
  normV nat (natV n) = ⊤

normρ : ∀ Γ (ρ : ⟦ Γ ⟧Context) → Set
normρ ∅ ∅ = ⊤
normρ (τ • Γ) (v • ρ) = normV τ v × normρ Γ ρ

⟦_⟧VarNorm : ∀ {Γ τ} (x : Var Γ τ) {ρ} →
  normρ Γ ρ →
  normV τ (⟦ x ⟧Var ρ)
⟦ this ⟧VarNorm   {v • ρ} (vv , ρρ) = vv
⟦ that x ⟧VarNorm {v • ρ} (vv , ρρ) = ⟦ x ⟧VarNorm ρρ

-- Prove fundamental property.
normT-fund : ∀ {τ Γ} (t : Term Γ τ) ρ (ρρ : normρ Γ ρ) →
  normT τ t ρ

normV-fund-const : ∀ {τ} (c : Const τ) → normV τ (eval-const c)
normV-fund-const (lit n) = tt

normV-fund-sval : ∀ {τ Γ} (sv : SVal Γ τ) ρ (ρρ : normρ Γ ρ) → normV τ (eval sv ρ)
normV-fund-sval (var x) ρ ρρ = ⟦ x ⟧VarNorm ρρ
normV-fund-sval (abs t) ρ ρρ v vv = normT-fund t (v • ρ) (vv , ρρ)
normV-fund-sval (cons sv1 sv2) ρ ρρ = normV-fund-sval sv1 ρ ρρ , normV-fund-sval sv2 ρ ρρ
normV-fund-sval (const c) ρ ρρ = normV-fund-const c

-- Not inlined because it gives the right type ascription to the derivation `val sv`.
normT-fund-sval : ∀ {τ Γ} (sv : SVal Γ τ) ρ (ρρ : normρ Γ ρ) → normT τ (val sv) ρ
normT-fund-sval sv ρ ρρ = eval sv ρ , val sv , normV-fund-sval sv ρ ρρ

normV-fund-primitive : ∀ {σ τ} p →
  ∀ v → (vv : normV σ v) →
  normV τ (eval-primitive p v)
normV-fund-primitive succ (natV n) vv = tt
normV-fund-primitive add (pairV (natV m) (natV n)) vv = tt

open import Data.Nat
normT-fund (val sv) ρ ρρ = normT-fund-sval sv ρ ρρ
normT-fund (primapp p sv) ρ ρρ = eval-primitive p (eval sv ρ) , primapp p sv , normV-fund-primitive p (eval sv ρ) (normV-fund-sval sv ρ ρρ)
normT-fund (app vs vt) ρ ρρ with normT-fund-sval vs ρ ρρ | normT-fund-sval vt ρ ρρ
... | closure t ρ₁ , ↓fv , fvv | av , ↓av , avv with fvv av avv
... | v , ↓v , vv = v , app zero _ ↓fv ↓av ↓v , vv
normT-fund (lett s t) ρ ρρ with normT-fund s ρ ρρ
... | sv , ↓s , svv with normT-fund t (sv • ρ) (svv , ρρ)
... | tv , ↓t , tvv = tv , lett zero zero sv s t ↓s ↓t , tvv

-- Deduce from fundamental property that all terms indeed normalize.
normalize : ∀ {τ} (t : Term ∅ τ) → Σ[ v ∈ Val τ ] ∅ ⊢ t ↓[ no ] v
normalize t with normT-fund t ∅ tt
... | v , ↓ , _ = v , ↓


open import Relation.Binary.PropositionalEquality

record Group : Set₁ where
  field
    X : Set
    _·_ : X → X → X
    e : X
    i : X → X
    assoc  : (x y z : X) → (x · y) · z ≡ x · (y · z)
    unit-l : (x : X) → e · x ≡ x
    unit-r : (x : X) → x · e ≡ x
    inv-l  : (x : X) → i x · x ≡ e
    inv-r  : (x : X) → x · i x ≡ e

inv-u-l : {G : Group} → (x x' y : Group.X) → (x Group.· y) ≡ Group.e → (x' Group.· y) ≡ Group.e → x ≡ x'
inv-u-l x x' y p q = ?


module Golden where

open import Basics
open import Vec
open import Ix
open import Cutting
open import Interior
open import Tensor
open RECTANGLE
open INTERIOR RectCut

ind : (n : Nat)(P : Nat -> Set) -> P zero ->
         ((k : Nat) -> P k -> P (suc k)) -> P n
ind zero P pz ps = pz
ind (suc n) P pz ps = ps n (ind n P pz ps)

+N-zero : (x : Nat) -> (x +N zero) == x
+N-zero x = ind x (\ x -> (x +N zero) == x) (refl _) (\ _ h -> refl suc =$= h)

+N-suc : (x y : Nat) -> (x +N suc y) == suc (x +N y)
+N-suc x y = ind x (\ x -> (x +N suc y) == suc (x +N y))
             (refl _) (\ _ h -> refl suc =$= h)

comm-+N : (x y : Nat) -> (x +N y) == (y +N x)
comm-+N x zero = +N-zero x
comm-+N x (suc y) rewrite +N-suc x y = refl suc =$= comm-+N x y

clockwise90 : {P : Nat * Nat -> Set}{w h : Nat} ->
  ({w h : Nat} -> P (w , h) -> P (h , w)) ->
  Interior P (w , h) -> Interior P (h , w)
clockwise90 pc90 (tile p) = tile (pc90 p)
clockwise90 pc90 < inl (l , r , refl _) 8>< (il , ir , <>) >
  = < inr (l , r , refl _) 8><
      clockwise90 pc90 il , clockwise90 pc90 ir , <> >
clockwise90 pc90 < inr (t , b , refl _) 8>< it , ib , <> >
  = < (inl (b , t , comm-+N b t)) 8><
      clockwise90 pc90 ib , clockwise90 pc90 it , <> >

Square : Nat * Nat -> Set
Square (w , h) = w == h

c90Sq : {w h : Nat} -> Square (w , h) -> Square (h , w)
c90Sq (refl _) = refl _

golden : (n : Nat) -> Sg Nat \ w -> Sg Nat \ h -> Interior Square (w , h)
golden zero = 1 , 1 , tile (refl 1)
golden (suc n) with golden n
... | w , h , i = (w +N h) , w , < (inl (w , h , refl _)) 8><
                  tile (refl w) , clockwise90 c90Sq i , <> >

fill : {n : Nat}{X : Set} -> X -> X -> X -> Vec X (suc (suc n))
fill {n} first midst last rewrite comm-+N 1 (suc n) = first ,- (pure midst +V (last ,- []))
  where open Applicative (VecAppl n)

border : {w h : Nat} -> Matrix Char (w , h)
border {w} {zero} = []
border {zero} {h} = pure [] where open Applicative (VecAppl _)
border {suc zero} {suc zero} = ('O' ,- []) ,- []
border {suc (suc w)} {suc zero} = fill '<' '-' '>' ,- []
border {suc zero} {suc (suc h)} = fill ('^' ,- []) ('|' ,- []) ('v' ,- [])
border {suc (suc w)} {suc (suc h)} =
  fill (fill '/' '-' '\\')
       (fill '|' ' ' '|')
       (fill '\\' '-' '/')

goldenInterior :  (n : Nat) -> Sg Nat \ w -> Sg Nat \ h -> Interior (Matrix Char) (w , h)
goldenInterior n with golden n
... | w , h , sqi = w , h , ifold (\ _ _ -> tile border) (\ _ -> <_>) (w , h) sqi

picture : [ Interior (Matrix Char) -:> Matrix Char ]
picture = ifold (\ _ -> id) NatCut2DMatAlg

goldenPicture : (n : Nat) -> Sg Nat \ w -> Sg Nat \ h -> Matrix Char (w , h)
goldenPicture n with goldenInterior n
... | w , h , mi = w , h , picture _ mi


module MultiSorted.Context {s} (Sort : Set s)  where

-- An attempt to define more structured context
-- that directly support the cartesian structure

data Context : Set s where
  ctx-empty : Context
  ctx-slot : Sort → Context
  ctx-concat : Context → Context → Context

-- the variables in a context
data var : Context → Set where
  var-var  : ∀ {A} → var (ctx-slot A)
  var-inl  : ∀ {Γ Δ} → var Γ → var (ctx-concat Γ Δ)
  var-inr  : ∀ {Γ Δ} → var Δ → var (ctx-concat Γ Δ)

sort-of : ∀ (Γ : Context) → var Γ → Sort
sort-of (ctx-slot A) _ = A
sort-of (ctx-concat Γ Δ) (var-inl x) = sort-of Γ x
sort-of (ctx-concat Γ Δ) (var-inr x) = sort-of Δ x

-- It is absurd to have a variable in the empty context
ctx-empty-absurd : ∀ {ℓ} {P : var ctx-empty → Set ℓ} (x : var ctx-empty) → P x
ctx-empty-absurd ()


open import Common.Prelude hiding (tt)

instance
  tt : ⊤
  tt = record{}

NonZero : Nat → Set
NonZero zero    = ⊥
NonZero (suc _) = ⊤

pred′ : (n : Nat) {{_ : NonZero n}} → Nat
pred′ zero {{}}
pred′ (suc n) = n

test : (n : Nat) {{x y : NonZero n}} → Nat
test n = pred′ n


open import Relation.Binary.Core

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

open import PLRTree {A}
open import PLRTree.Drop _≤_ tot≤
open import PLRTree.Heap _≤_

lemma-dropLast-≤* : {x : A}{t : PLRTree} → x ≤* t → x ≤* dropLast t
lemma-dropLast-≤* {x} (lf≤* .x) = lf≤* x
lemma-dropLast-≤* (nd≤* {perfect} {x} {y} {l} {r} x≤y x≤*l x≤*r)
    with r
... | leaf = x≤*r
... | node _ _ _ _ = nd≤* x≤y x≤*l (lemma-dropLast-≤* x≤*r)
lemma-dropLast-≤* (nd≤* {left} {x} {y} {l} {r} x≤y x≤*l x≤*r)
    with l | x≤*l | dropLast l | lemma-dropLast-≤* x≤*l
... | leaf | _ | _ | _ = lf≤* x
... | node perfect _ _ _ | _ | node perfect y' l' r' | x≤*dll = nd≤* x≤y x≤*dll x≤*r
... | node perfect _ _ _ | _ | leaf | (lf≤* .x) = nd≤* x≤y (lf≤* x) x≤*r
... | node perfect _ _ _ | _ | node left _ _ _ | x≤*dll = nd≤* x≤y x≤*dll x≤*r
... | node perfect _ _ _ | _ | node right _ _ _ | x≤*dll = nd≤* x≤y x≤*dll x≤*r
... | node left _ _ _ | _ | node perfect y' l' r' | x≤*dll = nd≤* x≤y x≤*dll x≤*r
... | node left _ _ _ | _ | leaf | (lf≤* .x) = nd≤* x≤y (lf≤* x) x≤*r
... | node left _ _ _ | _ | node left _ _ _ | x≤*dll = nd≤* x≤y x≤*dll x≤*r
... | node left _ _ _ | _ | node right _ _ _ | x≤*dll = nd≤* x≤y x≤*dll x≤*r
... | node right _ _ _ | _ | node perfect y' l' r' | x≤*dll = nd≤* x≤y x≤*dll x≤*r
... | node right _ _ _ | _ | leaf | (lf≤* .x) = nd≤* x≤y (lf≤* x) x≤*r
... | node right _ _ _ | _ | node left _ _ _ | x≤*dll = nd≤* x≤y x≤*dll x≤*r
... | node right _ _ _ | _ | node right _ _ _ | x≤*dll = nd≤* x≤y x≤*dll x≤*r
lemma-dropLast-≤* (nd≤* {right} {x} {y} {l} {r} x≤y x≤*l x≤*r)
    with r
... | leaf = nd≤* x≤y x≤*r x≤*r
... | node perfect y' l' r' = nd≤* x≤y (lemma-dropLast-≤* x≤*l) x≤*r
... | node left _ _ _ = nd≤* x≤y x≤*l  (lemma-dropLast-≤* x≤*r)
... | node right _ _ _ = nd≤* x≤y x≤*l  (lemma-dropLast-≤* x≤*r)

lemma-dropLast-heap : {t : PLRTree} → Heap t → Heap (dropLast t)
lemma-dropLast-heap leaf = leaf
lemma-dropLast-heap (node {perfect} {x} {l} {r} x≤*l x≤*r hl hr)
    with r
... | leaf = hr
... | node _ _ _ _ = node x≤*l (lemma-dropLast-≤* x≤*r) hl (lemma-dropLast-heap hr)
lemma-dropLast-heap (node {left} {x} {l} {r} x≤*l x≤*r hl hr)
    with l | x≤*l | hl | dropLast l | lemma-dropLast-≤* x≤*l | lemma-dropLast-heap hl
... | leaf | _ | _ | _ | _ | _ = leaf
... | node perfect _ _ _ |  _ | _ | node perfect y' l' r' | x≤*dll | hdll = node x≤*dll x≤*r hdll hr
... | node perfect _ _ _ |  _ | _ | leaf | (lf≤* .x) | leaf = node (lf≤* x) x≤*r leaf hr
... | node perfect _ _ _ |  _ | _ | node left _ _ _ | x≤*dll | hdll = node x≤*dll x≤*r hdll hr
... | node perfect _ _ _ |  _ | _ | node right _ _ _ | x≤*dll | hdll = node x≤*dll x≤*r hdll hr
... | node left _ _ _ | _ | _ | node perfect y' l' r' | x≤*dll | hdll = node x≤*dll x≤*r hdll hr
... | node left _ _ _  |  _ | _ | leaf | (lf≤* .x) | leaf = node (lf≤* x) x≤*r leaf hr
... | node left _ _ _  |  _ | _ | node left _ _ _ | x≤*dll | hdll = node x≤*dll x≤*r hdll hr
... | node left _ _ _  |  _ | _ | node right _ _ _ | x≤*dll | hdll = node x≤*dll x≤*r hdll hr
... | node right _ _ _ |  _ | _ | node perfect y' l' r' | x≤*dll | hdll = node x≤*dll x≤*r hdll hr
... | node right _ _ _ |  _ | _ | leaf | (lf≤* .x) | leaf = node (lf≤* x) x≤*r leaf hr
... | node right _ _ _ |  _ | _ | node left _ _ _ | x≤*dll | hdll = node x≤*dll x≤*r hdll hr
... | node right _ _ _ |  _ | _ | node right _ _ _ | x≤*dll | hdll = node x≤*dll x≤*r hdll hr
lemma-dropLast-heap (node {right} {x} {l} {r} x≤*l x≤*r hl hr)
    with r
... | leaf = node x≤*r x≤*r hr hr
... | node perfect y' l' r' = node (lemma-dropLast-≤* x≤*l) x≤*r (lemma-dropLast-heap hl) hr
... | node left _ _ _ = node x≤*l (lemma-dropLast-≤* x≤*r) hl (lemma-dropLast-heap hr)
... | node right _ _ _ = node x≤*l (lemma-dropLast-≤* x≤*r) hl (lemma-dropLast-heap hr)


open import Oscar.Prelude
open import Oscar.Class

module Oscar.Class.Transitivity where

module Transitivity'
  {𝔬} {𝔒 : Ø 𝔬}
  {𝔯} (_∼_ : 𝔒 → 𝔒 → Ø 𝔯)
  x y z
  = ℭLASS (x ,, y ,, z ,, _∼_) (x ∼ y → y ∼ z → x ∼ z)

module Transitivity
  {𝔬} {𝔒 : Ø 𝔬}
  {𝔯} (_∼_ : 𝔒 → 𝔒 → Ø 𝔯)
  where
  class = ∀ {x y z} → Transitivity'.class _∼_ x y z
  type = ∀ {x y z} → Transitivity'.type _∼_ x y z
  method : ⦃ _ : class ⦄ → type
  method {x = x} {y} {z} = Transitivity'.method _∼_ x y z

module _
  {𝔬} {𝔒 : Ø 𝔬}
  {𝔯} {_∼_ : 𝔒 → 𝔒 → Ø 𝔯}
  where
  transitivity = Transitivity.method _∼_

module _
  {𝔬} {𝔒 : Ø 𝔬}
  {𝔯} (_∼_ : 𝔒 → 𝔒 → Ø 𝔯)
  ⦃ _ : Transitivity.class _∼_ ⦄
  where
  transitivity[_] = λ {x y z} (x∼y : x ∼ y) (y∼z : y ∼ z) → Transitivity.method _∼_ x∼y y∼z
  infixr 9 ∙[]-syntax
  ∙[]-syntax = transitivity[_]
  syntax ∙[]-syntax _⊸_ f g = g ∙[ _⊸_ ] f

module FlipTransitivity
  {𝔬} {𝔒 : Ø 𝔬}
  {𝔯} (_∼_ : 𝔒 → 𝔒 → Ø 𝔯)
  where
  class = Transitivity.class _∼_
  type = ∀ {x y z} → y ∼ z → x ∼ y → x ∼ z
  method : ⦃ _ : class ⦄ → type
  method = flip transitivity

module _
  {𝔬} {𝔒 : Ø 𝔬}
  {𝔯} {_∼_ : 𝔒 → 𝔒 → Ø 𝔯}
  ⦃ _ : Transitivity.class _∼_ ⦄
  where
  infixr 9 _∙_
  _∙_ : ∀ {x y z} (y∼z : y ∼ z) (x∼y : x ∼ y) → x ∼ z
  g ∙ f = transitivity f g

module _
  {𝔬} {𝔒 : Ø 𝔬}
  {𝔭} {𝔓 : 𝔒 → Ø 𝔭}
  where

  open import Oscar.Data.Proposequality

  ≡̇-transitivity = transitivity[ Proposextensequality⟦ 𝔓 ⟧ ]

  infixr 9 ≡̇-transitivity
  syntax ≡̇-transitivity f g = g ≡̇-∙ f

  infixr 9 ≡̇-transitivity-syntax
  ≡̇-transitivity-syntax = ≡̇-transitivity
  syntax ≡̇-transitivity-syntax f g = g ⟨≡̇⟩ f


module _ where

  data Nat : Set where
    zero : Nat
    suc : Nat → Nat
  {-# BUILTIN NATURAL Nat #-}

  _+_ : (m n : Nat) → Nat
  zero + n = n
  suc m + n = suc (m + n)

  data Th : (m n : Nat) → Set where
    os : ∀ {m n} → Th m n → Th (suc m) (suc n)

  Fin : Nat → Set
  Fin = Th (suc zero)

  infixl 6 _++_
  infix 4 _≈M_

  postulate
    U : Set
    RCtx : Nat → Set
    _++_ : ∀ {m n} → RCtx m → RCtx n → RCtx (n + m)
    El : U → RCtx (suc zero)
    _≈M_ : ∀ {n} (Δ0 Δ1 : RCtx n) → Set

  infix 4 _⊢l-var_

  data _⊢l-var_ : ∀ {n} (Δi : RCtx n) (i : Fin n) → Set where
    os : ∀ {n} {e : Th zero n} {Δ Δπ π} (iq : Δπ ≈M (Δ ++ El π)) →
         Δπ ⊢l-var os e

  ⊢l-var-sub : ∀ {n Δ} {i : Fin n} → Δ ⊢l-var i → Set
  ⊢l-var-sub (os iq) = Nat


{-# OPTIONS --exact-split              #-}
{-# OPTIONS --no-sized-types           #-}
{-# OPTIONS --no-universe-polymorphism #-}
{-# OPTIONS --without-K                #-}

module LogicalFramework.Disjunction where

module LF where
  postulate
    _∨_  : Set → Set → Set
    inj₁ : {A B : Set} → A → A ∨ B
    inj₂ : {A B : Set} → B → A ∨ B
    case : {A B C : Set} → (A → C) → (B → C) → A ∨ B → C

  ∨-comm : {A B : Set} → A ∨ B → B ∨ A
  ∨-comm = case inj₂ inj₁

module Inductive where

  open import Common.FOL.FOL

  ∨-comm-el : {A B : Set} → A ∨ B → B ∨ A
  ∨-comm-el = case inj₂ inj₁

  ∨-comm : {A B : Set} → A ∨ B → B ∨ A
  ∨-comm (inj₁ a) = inj₂ a
  ∨-comm (inj₂ b) = inj₁ b


data HasUniqueValues (A : Set) : List A → Set
 where
  [] : HasUniqueValues A []
  _∷_ : {x : A} → {xs : List A} → x ∉ xs → (uxs : HasUniqueValues A xs) → HasUniqueValues A (x ∷ xs)

record AList (A : Set) (B : Set) : Set
 where
  field
    domain : List A
    uniquedomain : HasUniqueValues A domain
    range : ∀ {x : A} → x ∈ domain → B

open AList

record Unifiable (F : Set) (T : Set) (U₁ U₂ : Set) (σ : (T → F) → F → F) : Set₁ where
  field
    _≈u≈_ : (φ₁ φ₂ : F) → Set
    unifier : (φ₁ φ₂ : F) → φ₁ ≈u≈ φ₂ → (F → F) × (F → F)
    unifier-law : (φ₁ φ₂ : F) → (=u= : φ₁ ≈u≈ φ₂) → (let u = unifier φ₁ φ₂ =u=) → (fst u) φ₁ ≡ (snd u) φ₂

mutual
  data FTerm : 𝕃 VariableName → Set
   where
    variable : (𝑥 : VariableName) → FTerm (𝕃⟦ 𝑥 ⟧)
    function : (𝑓 : FunctionName) → ..{𝑥s : 𝕃 VariableName} {arity : Nat} → (τs : FTerms 𝑥s arity) → FTerm 𝑥s

  data FTerms : 𝕃 VariableName → Nat → Set
   where
    [] : FTerms ∅ zero
    _∷_ : ∀ ..{𝑥s' 𝑥s : 𝕃 VariableName} → FTerm 𝑥s' → {n : Nat} → FTerms 𝑥s n → FTerms (union {m = VariableName} 𝑥s' 𝑥s) (⊹ n)

instance MembershipVariableNameFTerm : ∀ {𝑥s} → Membership VariableName (FTerm 𝑥s)
MembershipVariableNameFTerm = {!!}

record TotalIntersection {ℓ} (m : Set ℓ) (M : Set ℓ) ⦃ _ : Membership m M ⦄ : Set ℓ
 where
  field
    intersection : M → M → M
    intersectionLaw1 : ∀ {x : m} {X₁ X₂ : M} → x ∈ intersection X₁ X₂ → x ∈ X₁
    intersectionLaw2 : ∀ {x : m} {X₁ X₂ : M} → x ∈ intersection X₁ X₂ → x ∈ X₂
    intersectionLaw3 : ∀ {x : m} {X₁ X₂ : M} → x ∈ X₁ × x ∈ X₂ → x ∈ intersection X₁ X₂

open TotalIntersection ⦃ … ⦄

{-# DISPLAY TotalIntersection.intersection _ = intersection #-}

instance Intersection𝕃 : ∀ {ℓ} {A : Set ℓ} ⦃ _ : Eq A ⦄ → TotalIntersection A (𝕃 A)
Intersection𝕃 = {!!}

mutual
  subst : AList VariableName (∃ FTerm) → ∃ FTerm → ∃ FTerm
  subst x t@(.(✓ ∅) , variable 𝑥) with 𝑥 ∈? domain x
  … | yes x∈D = range x x∈D
  … | no x∉D = t
  subst x (fst₁ , function 𝑓 {𝑥s = 𝑥s} {arity = a} τs) with substs x a (𝑥s , τs)
  subst x (fst₁ , function 𝑓 {.fst₁} {arity₁} τs) | fst₂ , snd₁ = fst₂ , (function 𝑓 snd₁)

  substs : AList VariableName (∃ FTerm) → (a : Nat) → ∃ (flip FTerms a) → ∃ (flip FTerms a)
  substs x .0 (.∅ , []) = ∅ , []
  substs x .(suc _) (._ , (x₁ ∷ snd₁)) with {!subst x (_ , x₁)!}
  substs x .(suc _) (._ , (x₁ ∷ snd₁)) | sb = {!!}

-- indexed by the number of function symbols contained
data DTerm : Nat → Set
 where
  variable : (𝑥 : VariableName) → DTerm zero
  function : (𝑓 : FunctionName) → {arity : Nat} → (τs : Vec (∃ DTerm) arity) → DTerm (suc (sum (fst <$> vecToList τs)))

mutual
  substD : VariableName → ∃ DTerm → {n : Nat} → DTerm n → ∃ DTerm
  substD x x₁ (variable 𝑥) = ifYes 𝑥 ≟ x then x₁ else _ , variable 𝑥
  substD x x₁ (function 𝑓 τs) with substsD x x₁ τs
  substD x x₁ (function 𝑓 τs) | ss = suc (sum (fst <$> vecToList ss)) , function 𝑓 {_} ss

  substsD : VariableName → ∃ DTerm → {n : Nat} → Vec (Σ Nat DTerm) n → Vec (Σ Nat DTerm) n
  substsD x x₁ [] = []
  substsD x x₁ (x₂ ∷ x₃) with substD x x₁ (snd x₂) | substsD x x₁ x₃
  substsD x x₁ (x₂ ∷ x₃) | fst₁ , snd₁ | sss = (fst₁ , snd₁) ∷ sss

data HDTerm : Set where
  ⟨_⟩ : {n : Nat} → DTerm n → HDTerm

substituteD : (AList VariableName HDTerm) → HDTerm → HDTerm
substituteD = {!!}

amgu : HDTerm → HDTerm → (AList VariableName HDTerm) → Maybe (AList VariableName HDTerm)
amgu ⟨ variable 𝑥 ⟩ ⟨ variable 𝑥₁ ⟩ f = {!!}
amgu ⟨ variable 𝑥 ⟩ ⟨ function 𝑓 τs ⟩ f = {!!}
amgu ⟨ function 𝑓 τs ⟩ ⟨ variable 𝑥 ⟩ f = {!!}
amgu ⟨ function 𝑓 τs₁ ⟩ ⟨ function 𝑓₁ τs ⟩ f = {!!}

{-
data AList : 𝕃 VariableName → Set
 where
  [] : AList ∅
  _∷_ :
-}
record JohnUnification {𝑥s₁} (τ₁ : FTerm 𝑥s₁) {𝑥s₂} (τ₂ : FTerm 𝑥s₂) (_ : intersection {m = VariableName} 𝑥s₁ 𝑥s₂ ≡ ∅) : Set where
  field
    u₁ u₂ : AList VariableName (∃ FTerm)
    unification-law₁ : fst (subst u₁ (𝑥s₁ , τ₁)) ≡ fst (subst u₂ (𝑥s₂ , τ₂))
    unification-law₂ : snd (subst u₁ (𝑥s₁ , τ₁)) ≡ transport FTerm (sym unification-law₁) (snd (subst u₂ (𝑥s₂ , τ₂)))

record UnificationEquation (𝑥s : 𝕃 VariableName) : Set
 where
  field
    {lhs-terms} : 𝕃 VariableName
    lhs : FTerm lhs-terms
    {rhs-terms} : 𝕃 VariableName
    rhs : FTerm rhs-terms
    lhs∪rhs-terms : 𝑥s ≡ union {m = VariableName} lhs-terms rhs-terms

open UnificationEquation

number-of-variables-that-occur-more-than-once : ∀ {n-eqn} → Vec (∃ λ 𝑥s → UnificationEquation 𝑥s) n-eqn → Nat
number-of-variables-that-occur-more-than-once {zero} [] = 0
number-of-variables-that-occur-more-than-once {suc n-eqn} x = {!!}

number-of-function-symbols : ∀ {𝑥s} → FTerm 𝑥s → Nat
number-of-function-symbols = {!!}

record UnificationProblem (n-var n-lhs n-eqn : Nat) : Set
 where
  field
    equations : Vec (∃ λ 𝑥s → UnificationEquation 𝑥s) n-eqn
    n-var-law : number-of-variables-that-occur-more-than-once equations ≤ n-var
    n-lhs-law : (sum ∘ vecToList $ number-of-function-symbols ∘ lhs ∘ snd <$> equations) ≤ n-lhs

instance MembershipUnificationEquationUnificationProblem : ∀ {n-var n-lhs n-eqn 𝑥s} → Membership (UnificationEquation 𝑥s) (UnificationProblem n-var n-lhs n-eqn)
MembershipUnificationEquationUnificationProblem = {!!}

instance MembershipVariableNameUnificationProblem : ∀ {n-var n-lhs n-eqn} → Membership VariableName (UnificationProblem n-var n-lhs n-eqn)
MembershipVariableNameUnificationProblem = {!!}

deletable : ∀ {𝑥s} → UnificationEquation 𝑥s → Set
deletable = {!!}

deletable? : ∀ {𝑥s} → (eq : UnificationEquation 𝑥s) → Dec (deletable eq)
deletable? = {!!}

u-deletable? : ∀ {n-var n-lhs n-eqn} (up : UnificationProblem n-var n-lhs n-eqn) → Dec (∃ λ 𝑥s → ∃ λ (εq : UnificationEquation 𝑥s) → deletable εq × εq ∈ up)
u-deletable? {n-var} {n-lhs} {zero} up = no {!!}
u-deletable? {n-var} {n-lhs} {suc n-eqn} up = {!!}

deleteRule : ∀ {n-var n-lhs n-eqn} {up : UnificationProblem n-var n-lhs (suc n-eqn)} {𝑥s} {εq : UnificationEquation 𝑥s} → deletable εq → εq ∈ up → UnificationProblem n-var n-lhs n-eqn
deleteRule = {!!}

decomposable : ∀ {𝑥s} → UnificationEquation 𝑥s → Set
decomposable = {!!}

decomposable? : ∀ {𝑥s} → (eq : UnificationEquation 𝑥s) → Dec (decomposable eq)
decomposable? = {!!}

u-decomposable? : ∀ {n-var n-lhs n-eqn} (up : UnificationProblem n-var (suc n-lhs) n-eqn) → Dec (∃ λ 𝑥s → ∃ λ (εq : UnificationEquation 𝑥s) → decomposable εq × εq ∈ up)
u-decomposable? = {!!}

decomposeRule : ∀ {n-var n-lhs n-eqn} {up : UnificationProblem n-var (suc n-lhs) n-eqn} {𝑥s} {εq : UnificationEquation 𝑥s} → decomposable εq → εq ∈ up → UnificationProblem n-var n-lhs n-eqn
decomposeRule = {!!}

swapable : ∀ {𝑥s} → UnificationEquation 𝑥s → Set
swapable = {!!}

swapable? : ∀ {𝑥s} → (eq : UnificationEquation 𝑥s) → Dec (swapable eq)
swapable? = {!!}

u-swapable? : ∀ {n-var n-lhs n-eqn} (up : UnificationProblem n-var (suc n-lhs) n-eqn) → Dec (∃ λ 𝑥s → ∃ λ (εq : UnificationEquation 𝑥s) → swapable εq × εq ∈ up)
u-swapable? = {!!}

swapRule : ∀ {n-var n-lhs n-eqn} {up : UnificationProblem n-var (suc n-lhs) n-eqn} {𝑥s} {εq : UnificationEquation 𝑥s} → swapable εq → εq ∈ up → UnificationProblem n-var n-lhs n-eqn
swapRule = {!!}

eliminatable : ∀ {n-var n-lhs n-eqn} {up : UnificationProblem n-var n-lhs n-eqn} {𝑥s} {εq : UnificationEquation 𝑥s} → (εq∈up : εq ∈ up) → Set
eliminatable = {!!}

u-eliminatable? : ∀ {n-var n-lhs n-eqn} (up : UnificationProblem (suc n-var) n-lhs n-eqn) → Dec (∃ λ 𝑥s → ∃ λ (εq : UnificationEquation 𝑥s) → ∃ λ (εq∈up : εq ∈ up) → eliminatable {up = up} {εq = εq} εq∈up)
u-eliminatable? = {!!}

eliminateRule : ∀ {n-var n-lhs n-eqn} {up : UnificationProblem (suc n-var) n-lhs n-eqn} {𝑥s} {εq : UnificationEquation 𝑥s} → {εq∈up : εq ∈ up} → eliminatable {up = up} {εq = εq} εq∈up → UnificationProblem n-var n-lhs n-eqn
eliminateRule = {!!}

conflictable : ∀ {𝑥s} → UnificationEquation 𝑥s → Set
conflictable = {!!}

conflictable? : ∀ {𝑥s} → (εq : UnificationEquation 𝑥s) → Dec (conflictable εq)
conflictable? = {!!}

u-conflictable? : ∀ {n-var n-lhs n-eqn} (up : UnificationProblem n-var n-lhs n-eqn) → Dec (∃ λ 𝑥s → ∃ λ (εq : UnificationEquation 𝑥s) → conflictable εq × εq ∈ up)
u-conflictable? = {!!}

checkable : ∀ {𝑥s} → UnificationEquation 𝑥s → Set
checkable = {!!}

checkable? : ∀ {𝑥s} → (εq : UnificationEquation 𝑥s) → Dec (checkable εq)
checkable? = {!!}

u-checkable? : ∀ {n-var n-lhs n-eqn} (up : UnificationProblem n-var n-lhs n-eqn) → Dec (∃ λ 𝑥s → ∃ λ (εq : UnificationEquation 𝑥s) → checkable εq × εq ∈ up)
u-checkable? = {!!}

postulate
  substituteFormula : (VariableName → Term) → Formula → Formula

record Unifier' : Set
 where
  field
    unifier-left unifier-right : VariableName → Term

open Unifier'

record _Unifies_and_ (υ : Unifier') (φ₁ φ₂ : Formula) : Set
 where
  field
    unification-law : substituteFormula (unifier-left υ) φ₁ ≡ substituteFormula (unifier-right υ) φ₂


{-# OPTIONS -v tc.cover.splittree:50 #-}

open import Agda.Builtin.Nat

data Vec (A : Set) : Nat → Set where
  nil  : Vec A zero
  cons : (n : Nat) → A → Vec A n → Vec A (suc n)

append : {A : Set} (m n : Nat) → Vec A m → Vec A n → Vec A (m + n)
append .zero    n nil            ys = ys
append (.suc m) n (cons .m x xs) ys = cons (m + n) x (append m n xs ys)

open import Agda.Builtin.Equality

data _×_ (A B : Set) : Set where
  pair : A → B → A × B

test : (p q : Nat × Nat) → p ≡ q → Set₁
test (.(pair x) y) (pair x .y) refl = Set