{-# OPTIONS --without-K #-}
open import HoTT.Base
open import HoTT.HLevel
open import HoTT.HLevel.Truncate
open import HoTT.Logic
open import HoTT.Identity
open import HoTT.Identity.Identity
open import HoTT.Identity.Coproduct
open import HoTT.Identity.Sigma
open import HoTT.Identity.Pi
open import HoTT.Identity.Universe
open import HoTT.Equivalence
open import HoTT.Equivalence.Lift
open import HoTT.Sigma.Transport

module HoTT.Exercises.Chapter3 where

module Exercise1 {i} {A B : 𝒰 i} (e : A ≃ B) (A-set : isSet A)
  where
  □ : isSet B
  □ {x} {y} p q = ap⁻¹ (ap≃ (e ⁻¹ₑ) x y) (A-set (ap g p) (ap g q))
    where open Iso (eqv→iso e)

module Exercise2 {i} {A B : 𝒰 i} (A-set : isSet A) (B-set : isSet B)
  where
  □ : isSet (A + B)
  □ {inl x} {inl y} p q = ap⁻¹ =+-equiv
    (ap lift (A-set (lower (=+-elim p)) (lower (=+-elim q))))
  □ {inl x} {inr y} p q = 𝟎-rec (=+-elim p)
  □ {inr x} {inl y} p q = 𝟎-rec (=+-elim p)
  □ {inr x} {inr y} p q = ap⁻¹ =+-equiv
    (ap lift (B-set (lower (=+-elim p)) (lower (=+-elim q))))

module Exercise3
  {i} {A : 𝒰 i} (A-set : isSet A)
  {j} {B : A → 𝒰 j} (B-set : {x : A} → isSet (B x))
  where
  □ : isSet (Σ A B)
  □ {x = x@(x₁ , x₂)} {y = y@(y₁ , y₂)} p q =
    ap⁻¹ =Σ-equiv (lemma (pr₁ =Σ-equiv p) (pr₁ =Σ-equiv q))
    where
    lemma : (p q : Σ (x₁ == y₁) λ p₁ → (transport B p₁ x₂ == y₂)) → p == q
    lemma (p₁ , p₂) (q₁ , q₂) = pair⁼ (r₁ , r₂)
      where
      r₁ = A-set p₁ q₁
      r₂ = B-set (transport _ r₁ p₂) q₂

module Exercise4 {i} {A : 𝒰 i} where
  _ : isProp A → isContr (A → A)
  _ = λ A-prop → id , λ f → funext λ x → A-prop x (f x)

  _ : isContr (A → A) → isProp A
  _ = λ where
      (f , contr) x y → happly (contr (const x) ⁻¹ ∙ contr (const y)) x

module Exercise5 {i} {A : 𝒰 i} where
  open import HoTT.Pi.Transport
  open import HoTT.Sigma.Transport

  _ : isProp A ≃ (A → isContr A)
  _ = f , qinv→isequiv (g , η , ε)
    where
    f : isProp A → (A → isContr A)
    f A-prop x = x , A-prop x
    g : (A → isContr A) → isProp A
    g h x y = let contr = pr₂ (h x) in contr x ⁻¹ ∙ contr y
    η : g ∘ f ~ id
    η _ = isProp-prop _ _
    ε : f ∘ g ~ id
    ε h = funext λ _ → isContr-prop _ _

module Exercise6 {i} {A : 𝒰 i} where
  instance
    LEM-prop : ⦃ hlevel 1 A ⦄ → hlevel 1 (A + ¬ A)
    LEM-prop = isProp→hlevel1 λ where
      (inl a) (inl a') → ap inl center
      (inl a) (inr f) → 𝟎-rec (f a)
      (inr f) (inl b') → 𝟎-rec (f b')
      (inr f) (inr f') → ap inr center

module Exercise7
  {i} {A : 𝒰 i} {A-prop : isProp A}
  {j} {B : 𝒰 j} {B-prop : isProp B}
  where
  □ : ¬ (A × B) → isProp (A + B)
  □ f = λ where
      (inl x) (inl y) → ap inl (A-prop _ _)
      (inl x) (inr y) → 𝟎-rec (f (x , y))
      (inr x) (inl y) → 𝟎-rec (f (y , x))
      (inr x) (inr y) → ap inr (B-prop _ _)

module Exercise8 {i j} {A : 𝒰 i} {B : 𝒰 j} {f : A → B} where
  open import HoTT.Equivalence.Proposition
  prop₁ : qinv f → ∥ qinv f ∥
  prop₁ e = ∣ e ∣
  prop₂ : ∥ qinv f ∥ → qinv f
  prop₂ e = isequiv→qinv (∥-rec ⦃ isProp→hlevel1 isequiv-prop ⦄ qinv→isequiv e)
  prop₃ : isProp ∥ qinv f ∥
  prop₃ = hlevel1→isProp

module Exercise9 {i} (lem : LEM {i}) where
  open import HoTT.Logic
  open import HoTT.Equivalence.Lift

  _ : Prop𝒰 i ≃ 𝟐
  _ = f , qinv→isequiv (g , η , ε)
    where
    f : Prop𝒰 i → 𝟐
    f P with lem P
    ... | inl _ = 0₂
    ... | inr _ = 1₂
    g : 𝟐 → Prop𝒰 i
    g 0₂ = ⊤
    g 1₂ = ⊥
    η : g ∘ f ~ id
    η P with lem P
    ... | inl t = hlevel⁼ (ua (prop-equiv (const t) (const ★)))
    ... | inr f = hlevel⁼ (ua (prop-equiv 𝟎-rec (𝟎-rec ∘ f)))
    ε : f ∘ g ~ id
    ε 0₂ with lem (g 0₂)
    ... | inl _ = refl
    ... | inr f = 𝟎-rec (f ★)
    ε 1₂ with lem (g 1₂)
    ... | inl ()
    ... | inr _ = refl

import HoTT.Exercises.Chapter3.Exercise10

module Exercise11 where
  open variables
  open import HoTT.Pi.Transport
  open import HoTT.Identity.Boolean

  prop : ¬ ((A : 𝒰₀) → ∥ A ∥ → A)
  prop f = 𝟎-rec (g (f 𝟐 ∣ 0₂ ∣) p)
    where
    not = 𝟐-rec 1₂ 0₂
    e : 𝟐 ≃ 𝟐
    e = not , qinv→isequiv (not , 𝟐-ind _ refl refl , 𝟐-ind _ refl refl)
    g : (x : 𝟐) → ¬ (not x == x)
    g 0₂ = 𝟎-rec ∘ pr₁ =𝟐-equiv
    g 1₂ = 𝟎-rec ∘ pr₁ =𝟐-equiv
    p : not (f 𝟐 ∣ 0₂ ∣) == f 𝟐 ∣ 0₂ ∣
    p =
      not (f 𝟐 ∣ 0₂ ∣)
        =⟨ ap (λ e → pr₁ e (f 𝟐 ∣ 0₂ ∣)) (=𝒰-β e) ⁻¹ ⟩
      transport id (ua e) (f 𝟐 ∣ 0₂ ∣)
        =⟨ ap (λ x → transport id (ua e) (f 𝟐 x)) center ⟩
      transport id (ua e) (f 𝟐 (transport ∥_∥ (ua e ⁻¹) ∣ 0₂ ∣))
        =⟨ happly (transport-→ ∥_∥ id (ua e) (f 𝟐) ⁻¹) ∣ 0₂ ∣ ⟩
      transport (λ A → ∥ A ∥ → A) (ua e) (f 𝟐) ∣ 0₂ ∣
        =⟨ happly (apd f (ua e)) ∣ 0₂ ∣ ⟩
      f 𝟐 ∣ 0₂ ∣
        ∎
      where open =-Reasoning

module Exercise12 {i} {A : 𝒰 i} (lem : LEM {i}) where
  open variables using (B ; j)

  □ : ∥ (∥ A ∥ → A) ∥
  □ with lem (type ∥ A ∥)
  ... | inl x = swap ∥-rec x λ x → ∣ const x ∣
  ... | inr f = ∣ 𝟎-rec ∘ f ∣

module Exercise13 {i} (lem : LEM∞ {i}) where
  □ : AC {i} {i} {i}
  □ {X = X} {A = A} {P = P} f = ∣ pr₁ ∘ g , pr₂ ∘ g ∣
    where
    g : (x : X) → Σ (A x) (P x)
    g x with lem (Σ (A x) (P x))
    ... | inl t = t
    ... | inr b = ∥-rec ⦃ hlevel-in (λ {x} → 𝟎-rec (b x)) ⦄ id (f x)

module Exercise14 (lem : ∀ {i} → LEM {i}) where
  open import HoTT.Sigma.Universal

  open variables

  ∥_∥' : 𝒰 i → 𝒰 i
  ∥ A ∥' = ¬ ¬ A

  ∣_∣' : A → ∥ A ∥'
  ∣ a ∣' f = f a

  ∥'-hlevel : hlevel 1 ∥ A ∥'
  ∥'-hlevel = ⟨⟩

  ∥'-rec : ⦃ hlevel 1 B ⦄ → (f : A → B) →
           Σ[ g ∶ (∥ A ∥' → B) ] Π[ a ∶ A ] g ∣ a ∣' == f a
  ∥'-rec f = λ where
    .pr₁ a → +-rec id (λ b → 𝟎-rec (a (𝟎-rec ∘ b ∘ f))) (lem (type _))
    .pr₂ _ → center

  _ : ∥ A ∥' ≃ ∥ A ∥
  _ = let open Iso in iso→eqv λ where
    .f → pr₁ (∥'-rec ∣_∣)
    .g → ∥-rec ∣_∣'
    .η _ → center
    .ε _ → center

module Exercise15
  (LiftProp-isequiv : ∀ {i j} → isequiv (LiftProp {i} {j}))
  where
  open import HoTT.Equivalence.Transport
  open variables

  open module LiftProp-qinv {i} {j} = qinv (isequiv→qinv (LiftProp-isequiv {i} {j}))
    renaming (g to LowerProp ; η to LiftProp-η ; ε to LiftProp-ε)

  ∥_∥' : 𝒰 (lsuc i) → 𝒰 (lsuc i)
  ∥_∥' {i} A = (P : Prop𝒰 i) → (A → P ty) → P ty

  ∣_∣' : A → ∥ A ∥'
  ∣ a ∣' _ f = f a

  ∥'-hlevel : hlevel 1 ∥ A ∥'
  ∥'-hlevel = ⟨⟩

  ∥'-rec : {A : 𝒰 (lsuc i)} {(type B) : Prop𝒰 (lsuc i)} → (f : A → B) →
           Σ (∥ A ∥' → B) λ g → (a : A) → g ∣ a ∣' == f a
  ∥'-rec {_} {_} {B} f = let p = ap _ty (LiftProp-ε B) in λ where
    .pr₁ a → transport id p (lift (a (LowerProp B) (lower ∘ transport id (p ⁻¹) ∘ f)))
    -- We do not get a definitional equality since our propositional
    -- resizing equivalence does not give us definitionally equal
    -- types, i.e. LowerProp B ≢ B. If it did, then we could write
    --
    --   ∥'-rec f a :≡ a (LowerProp B) f
    --
    -- and then we'd have ∥'-rec f ∣a∣' ≡ f a.
    .pr₂ a → Eqv.ε (transport-equiv p) (f a)

module Exercise16
  {i} {(type X) : Set𝒰 i}
  {j} {Y : X → Prop𝒰 j}
  (lem : ∀ {i} → LEM {i})
  where
  _ : Π[ x ∶ X ] ¬ ¬ Y x ty ≃ ¬ ¬ (Π[ x ∶ X ] Y x ty)
  _ = let open Iso in iso→eqv λ where
    .f s t → t λ x → +-rec id (𝟎-rec ∘ s x) (lem (Y x))
    .g s x y → 𝟎-rec (s λ f → 𝟎-rec (y (f x)))
    .η _ → center
    .ε _ → center

module Exercise17
  {i} {A : 𝒰 i}
  {j} {B : ∥ A ∥ → Prop𝒰 j}
  where
  ∥-ind : ((a : A) → B ∣ a ∣ ty) → (x : ∥ A ∥) → B x ty
  ∥-ind f x = ∥-rec (transport (_ty ∘ B) center ∘ f) x
    where instance _ = λ {x} → hlevel𝒰.h (B x)

module Exercise18 {i} where
  open Exercise6

  _ : LEM {i} → LDN {i}
  _ = λ lem P f → +-rec id (𝟎-rec ∘ f) (lem P)

  _ : LDN {i} → LEM {i}
  _ = λ ldn P → ldn (type (P ty + ¬ P ty)) λ f → f (inr (f ∘ inl))

module Exercise19
  {i} {P : ℕ → 𝒰 i}
  (P-lem : (n : ℕ) → P n + ¬ P n)
  -- Do not assume that all P n are mere propositions so we can
  -- reuse this solution for exercise 23.
  where
  open import HoTT.NaturalNumber renaming (_+_ to _+ₙ_ ; +-comm to +ₙ-comm)
  open import HoTT.Identity.NaturalNumber
  open import HoTT.Transport.Identity
  open import HoTT.Base.Inspect
  open import HoTT.Identity.Product
  open import HoTT.Equivalence.Sigma
  open import HoTT.Equivalence.Coproduct
  open import HoTT.Equivalence.Empty

  -- P n does not hold for any m < n
  ℕ* : ℕ → 𝒰 i
  ℕ* zero = 𝟏
  ℕ* (succ n) = ¬ P n × ℕ* n

  P* : {n : ℕ} → P n → 𝒰 i
  P* {n} p = inl p == P-lem n

  -- ℕ* is the product of 𝟏 and some ¬ P n, all of which are
  -- propositions, so it is a proposition as well.
  instance ℕ*-hlevel : {n : ℕ} → hlevel 1 (ℕ* n)
  ℕ*-hlevel {zero} = 𝟏-hlevel
  ℕ*-hlevel {succ n} = ×-hlevel

  P*-hlevel : {n : ℕ} → hlevel 1 (Σ (P n) P*)
  hlevel-out (P*-hlevel {n}) {p , _} = ⟨⟩
    where
    e : P n ≃ P n + ¬ P n
    e = +-empty₂ ⁻¹ₑ ∙ₑ +-equiv reflₑ (𝟎-equiv (_$ p))
    instance
      _ = =-contrᵣ (P-lem n)
      _ = raise ⦃ equiv-hlevel (Σ-equiv₁ e ⁻¹ₑ) ⦄
  instance _ = P*-hlevel

  -- Extract evidence that ¬ P m for some m < n
  extract : {m n : ℕ} → m < n → ℕ* n → ¬ P m
  extract {m} (k , p) = pr₁ ∘ weaken ∘ transport ℕ* p'
    where
    p' = p ⁻¹ ∙ +ₙ-comm (succ m) k
    weaken : {k : ℕ} → ℕ* (k +ₙ succ m) → ℕ* (succ m)
    weaken {zero} = id
    weaken {succ k} = weaken ∘ pr₂

  -- The smallest n such that P n holds
  Pₒ = Σ (Σ ℕ λ n → Σ (P n) P*) (ℕ* ∘ pr₁)

  Pₒ-prop : isProp Pₒ
  Pₒ-prop ((n , p , _) , n*) ((m , q , _) , m*) =
    pair⁼ (pair⁼ (n=m , center) , center)
    where
    n=m : n == m
    n=m with n <=> m
    -- If n = m, then there is nothing to do.
    ... | inl n=m = n=m
    -- If n < m, we have P n and we know ℕ* m contains ¬ P n
    -- somewhere inside, so we can just extract it to find a
    -- contradiction.
    ... | inr (inl n<m) = 𝟎-rec (extract n<m m* p)
    -- The m < n case is symmetrical.
    ... | inr (inr m<n) = 𝟎-rec (extract m<n n* q)
  instance _ = isProp→hlevel1 Pₒ-prop

  -- Use the decidability of P to search for an instance of P in
  -- some finite range of natural numbers, keeping track of evidence
  -- that P does not hold for lower n.
  find-P : (n : ℕ) → Pₒ + ℕ* n
  find-P zero = inr ★
  find-P (succ n) with find-P n | P-lem n | inspect P-lem n
  ... | inl x  | _      | _      = inl x
  ... | inr n* | inl p  | [ p* ] = inl ((n , p , p*) , n*)
  ... | inr n* | inr ¬p | _      = inr (¬p , n*)

  -- If we know P holds for some n, then we have an upper bound for
  -- which to search for the smallest n. If we do not find any other
  -- n' ≤ n such that P n', then we have a contradiction.
  to-Pₒ : Σ ℕ P → Pₒ
  to-Pₒ (n , p) with find-P (succ n)
  ... | inl x = x
  ... | inr (¬p , _) = 𝟎-rec (¬p p)

  from-Pₒ : Pₒ → Σ ℕ P
  from-Pₒ ((n , p , p*) , n*) = n , p

  _ : ∥ Σ ℕ P ∥ → Σ ℕ P
  _ = from-Pₒ ∘ ∥-rec to-Pₒ

module Exercise20 where
  open variables

  -- See HoTT.HLevel
  _ : ⦃ _ : hlevel 0 A ⦄ → Σ A P ≃ P center
  _ = Σ-contr₁

module Exercise21 {i} {P : 𝒰 i} where
  open import HoTT.Equivalence.Proposition
  _ : isProp P ≃ (P ≃ ∥ P ∥)
  _ = prop-equiv ⦃ isProp-hlevel1 ⦄ f g
    where
    f : isProp P → P ≃ ∥ P ∥
    f p = prop-equiv ∣_∣ (∥-rec id)
      where instance _ = isProp→hlevel1 p
    g : P ≃ ∥ P ∥ → isProp P
    g e = hlevel1→isProp ⦃ equiv-hlevel (e ⁻¹ₑ) ⦄
    instance
      _ = Σ-hlevel ⦃ Π-hlevel ⦄ ⦃ isProp→hlevel1 isequiv-prop ⦄

module Exercise22 where
  Fin : ℕ → 𝒰₀
  Fin zero = 𝟎
  Fin (succ n) = Fin n + 𝟏

  □ : ∀ {i} (n : ℕ) {A : Fin n → 𝒰 i} {P : (x : Fin n) → A x → 𝒰 i} →
      ((x : Fin n) → ∥ Σ (A x) (P x) ∥) →
      ∥ Σ ((x : Fin n) → A x) (λ g → ((x : Fin n) → P x (g x))) ∥
  □ zero _ = ∣ 𝟎-ind , 𝟎-ind ∣
  □ (succ n) {A} {P} f =
    swap ∥-rec (f (inr ★))     λ zₛ →
    swap ∥-rec (□ n (f ∘ inl)) λ zₙ →
    let f = f' zₛ zₙ in ∣ pr₁ ∘ f , pr₂ ∘ f ∣
    where
    f' : _ → _ → (x : Fin (succ n)) → Σ (A x) (P x)
    f' (_ , _) (g , h) (inl m) = g m , h m
    f' (x , y) (_ , _) (inr ★) = x , y

module Exercise23 where
  -- The solution for Exercise19 covers the case where P is not
  -- necessarily a mere proposition.
  open Exercise19


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

module Pitch where

open import Cubical.Core.Everything using (_≡_; Level; Type; Σ; _,_; fst; snd; _≃_; ~_)

open import Cubical.Foundations.Prelude     using (refl; sym; _∙_; cong; transport; subst; funExt; transp)
--open import Cubical.Foundations.Function    using (_∘_)
open import Cubical.Foundations.Univalence  using (ua)
open import Cubical.Foundations.Isomorphism using (iso; Iso; isoToPath; section; retract; isoToEquiv)

open import Data.Bool       using (Bool; false; true)
open import Data.Integer    using (ℤ; +_; -[1+_])
open import Data.Fin        using (Fin; toℕ; #_; _≟_; fromℕ<) renaming (zero to fz; suc to fs)
open import Data.List       using (List; []; _∷_; foldr; map)
open import Data.Maybe      using (Maybe; just; nothing) renaming (map to mmap)
open import Data.Nat        using (ℕ; zero; suc; _+_; _*_; _∸_; _≡ᵇ_; _>_)
open import Data.Nat.DivMod using (_mod_; _div_)
open import Data.Product    using (_×_; _,_; proj₁)
open import Data.String     using (String; intersperse) renaming (_++_ to _++s_)
open import Data.Vec        using (Vec; []; _∷_; lookup; replicate; _[_]%=_; toList) renaming (map to vmap)

open import Relation.Nullary using (yes; no)

open import BitVec          using (BitVec; insert; empty; show)
open import Util            using (n∸k<n; _+N_; opposite; _∘_)

-- Position of a pitch on an absolute scale
-- 0 is C(-1) on the international scale (where C4 is middle C)
-- or C0 on the Midi scale (where C5 is middle C)
-- Pitch is intentially set to match Midi pitch.
-- However it is fine to let 0 represent some other note and
-- translate appropriately at the end.
Pitch : Type
Pitch = ℕ

-- Number of notes in the chromatic scale.
s12 : ℕ
s12 = 12

-- Number of notes in the diatonic scale.
s7 : ℕ
s7 = 7

-- Pitch Class: position of a pitch within an octave, in the range [0..s12-1].
-- Pitch class 0 corresponds to C (assuming pitch 0 is Midi C0), which is standard.
PC : Type
PC = Fin s12

showPC : PC → String
showPC fz = "0"
showPC (fs fz) = "1"
showPC (fs (fs fz)) = "2"
showPC (fs (fs (fs fz))) = "3"
showPC (fs (fs (fs (fs fz)))) = "4"
showPC (fs (fs (fs (fs (fs fz))))) = "5"
showPC (fs (fs (fs (fs (fs (fs fz)))))) = "6"
showPC (fs (fs (fs (fs (fs (fs (fs fz))))))) = "7"
showPC (fs (fs (fs (fs (fs (fs (fs (fs fz)))))))) = "8"
showPC (fs (fs (fs (fs (fs (fs (fs (fs (fs fz))))))))) = "9"
showPC (fs (fs (fs (fs (fs (fs (fs (fs (fs (fs fz)))))))))) = "a"
showPC (fs (fs (fs (fs (fs (fs (fs (fs (fs (fs (fs fz))))))))))) = "b"

showPCs : List PC → String
showPCs pcs = intersperse " " (map showPC pcs)

toPC : Pitch → PC
toPC = _mod s12

Scale : ℕ → Type
Scale = Vec PC

-- Which octave one is in.
Octave : Type
Octave = ℕ

PitchOctave : Type
PitchOctave = PC × Octave

relativeToAbsolute : PitchOctave → Pitch
relativeToAbsolute (n , o) = (o * s12 + (toℕ n))

absoluteToRelative : Pitch → PitchOctave
absoluteToRelative n = (toPC n , n div s12)

pitchToClass : Pitch → PC
pitchToClass = proj₁ ∘ absoluteToRelative

majorScale harmonicMinorScale : Scale s7
majorScale         = # 0 ∷ # 2 ∷ # 4 ∷ # 5 ∷ # 7 ∷ # 9 ∷ # 11 ∷ []
harmonicMinorScale = # 0 ∷ # 2 ∷ # 3 ∷ # 5 ∷ # 7 ∷ # 8 ∷ # 11 ∷ []

wholeTone0Scale wholeTone1Scale : Scale 6
wholeTone0Scale    = # 0 ∷ # 2 ∷ # 4 ∷ # 6 ∷ # 8 ∷ # 10 ∷ []
wholeTone1Scale    = # 1 ∷ # 3 ∷ # 5 ∷ # 7 ∷ # 9 ∷ # 11 ∷ []

octatonic01Scale octatonic02Scale octatonic12Scale : Scale 8
octatonic01Scale   = # 0 ∷ # 1 ∷ # 3 ∷ # 4 ∷ # 6 ∷ # 7 ∷ # 9 ∷ # 10 ∷ []
octatonic02Scale   = # 0 ∷ # 2 ∷ # 3 ∷ # 5 ∷ # 6 ∷ # 8 ∷ # 9 ∷ # 11 ∷ []
octatonic12Scale   = # 1 ∷ # 2 ∷ # 4 ∷ # 5 ∷ # 7 ∷ # 8 ∷ # 10 ∷ # 11 ∷ []

majorPentatonicScale minorPentatonicScale ryukyuScale : Scale 5
majorPentatonicScale = # 0 ∷ # 2 ∷ # 4 ∷ # 7 ∷ # 9 ∷ []
minorPentatonicScale = # 0 ∷ # 2 ∷ # 4 ∷ # 7 ∷ # 9 ∷ []
ryukyuScale          = # 0 ∷ # 4 ∷ # 5 ∷ # 7 ∷ # 11 ∷ []

indexInScale : {n : ℕ} → Vec PC n → Fin s12 → Maybe (Fin n)
indexInScale []         p = nothing
indexInScale (pc ∷ pcs) p with pc ≟ p
... | yes _ = just fz
... | no  _ = mmap fs (indexInScale pcs p)

scaleSize : {n : ℕ} → Scale n → ℕ
scaleSize {n} _ = n

transposePitch : ℤ → Pitch → Pitch
transposePitch (+_     k) n = n + k
transposePitch (-[1+_] k) n = n ∸ suc k

-- transpose pitch class
Tp : ℕ  → PC → PC
Tp n pc = pc +N n

-- invert pitch class
Ip : PC → PC
Ip = opposite

-- Set of pitch classes represented as a bit vector.
PCSet : Type
PCSet = BitVec s12

toPCSet : List PC → PCSet
toPCSet = foldr insert empty

fromPCSet : PCSet → List PC
fromPCSet pcs = fromPCS s12 pcs
  where fromPCS : (n : ℕ) → BitVec n → List PC
        fromPCS zero []              = []
        fromPCS (suc n) (false ∷ xs) = fromPCS n xs
        fromPCS (suc n) (true  ∷ xs) = fromℕ< (n∸k<n 11 n) ∷ fromPCS n xs

-- transpose pitch class set
T : ℕ → PCSet → PCSet
T n = toPCSet ∘ map (Tp n) ∘ fromPCSet

-- invert pitch class set
I : PCSet → PCSet
I = toPCSet ∘ map Ip ∘ fromPCSet

-- Standard Midi pitches

-- first argument is relative pitch within octave
-- second argument is octave (C5 = middle C for Midi)
standardMidiPitch : Fin s12 → ℕ → Pitch
standardMidiPitch p o = relativeToAbsolute (p , o)

c c♯ d d♯ e f f♯ g g♯ a b♭ b : ℕ → Pitch
c  = standardMidiPitch (# 0)
c♯ = standardMidiPitch (# 1)
d  = standardMidiPitch (# 2)
d♯ = standardMidiPitch (# 3)
e  = standardMidiPitch (# 4)
f  = standardMidiPitch (# 5)
f♯ = standardMidiPitch (# 6)
g  = standardMidiPitch (# 7)
g♯ = standardMidiPitch (# 8)
a  = standardMidiPitch (# 9)
b♭ = standardMidiPitch (# 10)
b  = standardMidiPitch (# 11)

-- Equivalences

rel→abs : PitchOctave → Pitch
rel→abs = relativeToAbsolute

abs→rel : Pitch → PitchOctave
abs→rel = absoluteToRelative

{-
rel→abs→rel : (p : PitchOctave) → (abs→rel ∘ rel→abs) p ≡ p
rel→abs→rel (pitchClass p , octave o) i =
  let a = cong pitchClass (modUnique s12 o p)
      b = cong octave     (divUnique s12 o p)
  in a i , b i

abs→rel→abs : (p : Pitch) → (rel→abs ∘ abs→rel) p ≡ p
abs→rel→abs (pitch p) = cong pitch (sym (n≡divmod p s12))

abs≃rel : Iso Pitch PitchOctave
abs≃rel = iso abs→rel rel→abs rel→abs→rel abs→rel→abs

abs≡rel : Pitch ≡ PitchOctave
abs≡rel = isoToPath abs≃rel
-}


------------------------------------------------------------------------
-- Squashing
------------------------------------------------------------------------

{-# OPTIONS --erased-cubical --prop --safe #-}

import Equality.Path as P

module Squash {e⁺} (eq : ∀ {a p} → P.Equality-with-paths a p e⁺) where

open P.Derived-definitions-and-properties eq

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

open import Bijection equality-with-J using (_↔_)
open import Equality.Decidable-UIP equality-with-J
open import Double-negation equality-with-J as DN using (¬¬_)
open import Embedding equality-with-J using (Embedding; Is-embedding)
open import Equality.Decision-procedures equality-with-J
open import Equivalence equality-with-J as Eq
  using (_≃_; Is-equivalence)
open import Equivalence.Erased equality-with-J using (_≃ᴱ_)
open import For-iterated-equality equality-with-J
open import Function-universe equality-with-J hiding (id; _∘_)
open import H-level equality-with-J as H-level
open import H-level.Closure equality-with-J
open import H-level.Truncation.Propositional eq as Trunc using (∥_∥)
open import Injection equality-with-J using (_↣_)
open import Monad equality-with-J
open import Surjection equality-with-J using (_↠_; Split-surjective)

private
  variable
    a b ℓ p : Level
    A B     : Type a
    P       : A → Type p
    k x y   : A
    n       : ℕ

------------------------------------------------------------------------
-- The squash type

-- Any two elements of type Squash′ A are definitionally equal.

data Squash′ (A : Type a) : Prop a where
  squash′ : A → Squash′ A

-- However, Squash′ A does not have type Type a. The following wrapper
-- makes it possible to use squashed types in, say, lists.

record Squash (A : Type a) : Type a where
  constructor squash
  field
    squashed : Squash′ A

pattern [_] x = squash (squash′ x)

private

  -- A unit test.

  test : [ 4 ] ∷ [ 5 ] ∷ [] ≡ [ 3 ] ∷ [ 9 ] ∷ []
  test = refl _

-- Squashed types are propositions.

Squash-propositional : Is-proposition (Squash A)
Squash-propositional = λ _ _ → refl _

------------------------------------------------------------------------
-- Squash is a monad

-- A universe-polymorphic variant of bind.

infixl 5 _>>=′_

_>>=′_ : Squash A → (A → Squash B) → Squash B
_>>=′_ {A = A} {B = B} (squash x) f = squash (lemma x)
  where
  lemma : Squash′ A → Squash′ B
  lemma (squash′ x) = Squash.squashed (f x)

instance

  -- Squash is a monad.

  raw-monad : Raw-monad {c = ℓ} Squash
  Raw-monad.return raw-monad = [_]
  Raw-monad._>>=_  raw-monad = _>>=′_

  monad : Monad {c = ℓ} Squash
  Monad.raw-monad      monad = raw-monad
  Monad.left-identity  monad = λ _ _ → refl _
  Monad.right-identity monad = λ _ → refl _
  Monad.associativity  monad = λ _ _ _ → refl _

------------------------------------------------------------------------
-- Squash preserves all kinds of functions

private

  -- Squash preserves functions.

  Squash-cong-→ : (A → B) → Squash A → Squash B
  Squash-cong-→ A→B x = x >>=′ return ∘ A→B

-- If A and B are logically equivalent, then Squash A and Squash B are
-- equivalent.

Squash-cong-⇔ : A ⇔ B → Squash A ≃ Squash B
Squash-cong-⇔ A⇔B = Eq.↔⇒≃ (record
  { surjection = record
    { logical-equivalence = record
      { to   = Squash-cong-→ (_⇔_.to   A⇔B)
      ; from = Squash-cong-→ (_⇔_.from A⇔B)
      }
    ; right-inverse-of = λ _ → refl _
    }
  ; left-inverse-of = λ _ → refl _
  })

-- If there is a split surjection from A to B, then Squash A and
-- Squash B are equivalent.

Squash-cong-↠ : A ↠ B → Squash A ≃ Squash B
Squash-cong-↠ = Squash-cong-⇔ ∘ _↠_.logical-equivalence

private

  -- Some lemmas used in Squash-cong.

  Squash-cong-↔ : A ↔ B → Squash A ↔ Squash B
  Squash-cong-↔ =
    from-isomorphism ∘ Squash-cong-⇔ ∘ _↔_.logical-equivalence

  Squash-cong-≃ : A ≃ B → Squash A ≃ Squash B
  Squash-cong-≃ = Squash-cong-⇔ ∘ _≃_.logical-equivalence

  Squash-cong-≃ᴱ : A ≃ᴱ B → Squash A ≃ᴱ Squash B
  Squash-cong-≃ᴱ =
    from-isomorphism ∘ Squash-cong-⇔ ∘ _≃ᴱ_.logical-equivalence

  Squash-cong-↣ : A ↣ B → Squash A ↣ Squash B
  Squash-cong-↣ A↣B = record
    { to        = Squash-cong-→ (_↣_.to A↣B)
    ; injective = λ _ → refl _
    }

  Squash-cong-Embedding :
    Embedding A B → Embedding (Squash A) (Squash B)
  Squash-cong-Embedding A↣B = record
    { to           = Squash-cong-→ (Embedding.to A↣B)
    ; is-embedding = λ x y →
        _≃_.is-equivalence $
        _↠_.from (Eq.≃↠⇔ (⇒≡ 1 Squash-propositional)
                         (⇒≡ 1 Squash-propositional))
          (record { from = λ _ → refl _ })
    }

Squash-cong : A ↝[ k ] B → Squash A ↝[ k ] Squash B
Squash-cong {k = implication}         = Squash-cong-→
Squash-cong {k = logical-equivalence} = from-isomorphism ∘ Squash-cong-⇔
Squash-cong {k = injection}           = Squash-cong-↣
Squash-cong {k = embedding}           = Squash-cong-Embedding
Squash-cong {k = surjection}          = from-isomorphism ∘ Squash-cong-↠
Squash-cong {k = bijection}           = Squash-cong-↔
Squash-cong {k = equivalence}         = Squash-cong-≃
Squash-cong {k = equivalenceᴱ}        = Squash-cong-≃ᴱ

------------------------------------------------------------------------
-- Some isomorphisms

-- Squash ⊤ is isomorphic to ⊤.

Squash-⊤↔⊤ : Squash ⊤ ↔ ⊤
Squash-⊤↔⊤ = record
  { surjection = record
    { logical-equivalence = record
      { from = λ _ → [ _ ]
      }
    ; right-inverse-of = λ _ → refl _
    }
  ; left-inverse-of = λ _ → refl _
  }

-- Squash ⊥ is isomorphic to ⊥.

Squash-⊥↔⊥ : Squash (⊥ {ℓ = ℓ}) ↔ ⊥ {ℓ = ℓ}
Squash-⊥↔⊥ = record
  { surjection = record
    { logical-equivalence = record
      { to   = λ x → ⊥-in-prop→⊥ (Squash-⊥→⊥-in-prop x)
      ; from = λ ()
      }
    ; right-inverse-of = λ ()
    }
  ; left-inverse-of = λ _ → refl _
  }
  where

  -- An empty type in Prop.

  data ⊥-in-prop : Prop where

  -- Squash ⊥ implies ⊥-in-prop.

  Squash-⊥→⊥-in-prop : Squash (⊥ {ℓ = ℓ}) → ⊥-in-prop
  Squash-⊥→⊥-in-prop [ () ]

  -- ⊥-in-prop implies ⊥.

  ⊥-in-prop→⊥ : ⊥-in-prop → ⊥ {ℓ = ℓ}
  ⊥-in-prop→⊥ ()

-- Squash commutes with _×_.
--
-- This lemma was suggested by Jesper Cockx.

Squash-×↔× : Squash (A × B) ↔ Squash A × Squash B
Squash-×↔× = record
  { surjection = record
    { logical-equivalence = record
      { to   = λ p → Squash-cong proj₁ p , Squash-cong proj₂ p
      ; from = λ (x , y) → x >>=′ λ x → y >>=′ λ y → return (x , y)
      }
    ; right-inverse-of = refl
    }
  ; left-inverse-of = refl
  }

-- Squash commutes with ↑ ℓ.

Squash-↑↔↑ : Squash (↑ ℓ A) ↔ ↑ ℓ (Squash A)
Squash-↑↔↑ = record
  { surjection = record
    { logical-equivalence = record
      { to   = λ { x .lower → Squash-cong lower x }
      ; from = λ (lift x) → Squash-cong lift x
      }
    ; right-inverse-of = λ _ → refl _
    }
  ; left-inverse-of = λ _ → refl _
  }

------------------------------------------------------------------------
-- Some properties

-- ∥ A ∥ implies Squash A.

∥∥→Squash : ∥ A ∥ → Squash A
∥∥→Squash = Trunc.rec Squash-propositional [_]

-- Squash A implies ¬ ¬ A.

Squash→¬¬ : Squash A → ¬ ¬ A
Squash→¬¬ {A = A} = curry (
  Squash A × ¬ A  ↝⟨ uncurry (flip Squash-cong) ⟩
  Squash ⊥        ↔⟨ Squash-⊥↔⊥ ⟩□
  ⊥               □)

-- It is not in general the case that [ x ] ≡ [ y ] implies
-- Squash (x ≡ y).

¬-[]≡[]→Squash-≡ :
  ¬ ({A : Type a} {x y : A} → [ x ] ≡ [ y ] → Squash (x ≡ y))
¬-[]≡[]→Squash-≡ {a = a} =
  ({A : Type a} {x y : A} → [ x ] ≡ [ y ] → Squash (x ≡ y))  ↝⟨ _$ refl _ ⟩
  Squash (lift true ≡ lift false)                            ↝⟨ Squash-cong (cong lower) ⟩
  Squash (true ≡ false)                                      ↝⟨ Squash-cong Bool.true≢false ⟩
  Squash ⊥                                                   ↔⟨ Squash-⊥↔⊥ ⟩□
  ⊥                                                          □

-- [_] is split surjective for decided types.

Split-surjective-[] : Dec A → Split-surjective {A = A} [_]
Split-surjective-[] (yes x) _ = x , refl _
Split-surjective-[] (no ¬x) x =
  ⊥-elim (_↔_.to Squash-⊥↔⊥ (Squash-cong ¬x x))

------------------------------------------------------------------------
-- Stability

-- A type A is stable if Squash A implies A.

Stable : Type a → Type a
Stable A = Squash A → A

-- A type A is very stable if Squash A is equivalent to A.

Very-stable : Type a → Type a
Very-stable A = Squash A ≃ A

-- Variants of the definitions above for equality.

Stable-≡ : Type a → Type a
Stable-≡ = For-iterated-equality 1 Stable

Very-stable-≡ : Type a → Type a
Very-stable-≡ = For-iterated-equality 1 Very-stable

------------------------------------------------------------------------
-- Some lemmas related to stability

-- Very stable types are stable.

Very-stable→Stable :
  ∀ n →
  For-iterated-equality n Very-stable A →
  For-iterated-equality n Stable A
Very-stable→Stable n =
  For-iterated-equality-cong₁ _ n _≃_.to

-- Very-stable is propositional (assuming extensionality).

Very-stable-propositional :
  {A : Type a} →
  Extensionality a a →
  Is-proposition (Very-stable A)
Very-stable-propositional ext =
  Eq.left-closure ext 0 Squash-propositional

private

  -- The previous result can be generalised.

  For-iterated-equality-Very-stable-propositional :
    {A : Type a} →
    Extensionality a a →
    ∀ n → Is-proposition (For-iterated-equality n Very-stable A)
  For-iterated-equality-Very-stable-propositional ext n =
    H-level-For-iterated-equality ext 1 n
      (Very-stable-propositional ext)

-- A type is very stable if and only if [_] is an equivalence for that
-- type.

Very-stable↔Is-equivalence-[] :
  {A : Type a} →
  Very-stable A ↝[ a ∣ a ] Is-equivalence {A = A} [_]
Very-stable↔Is-equivalence-[] =
  generalise-ext?-prop
    (record { from = inverse ∘ Eq.⟨ _ ,_⟩
            ; to   = λ s →
                _≃_.is-equivalence $
                Eq.with-other-function
                  (inverse s)
                  [_]
                  (λ _ → refl _)
            })
    Very-stable-propositional
    (λ ext → Eq.propositional ext _)

-- A type is very stable if and only if it is a stable proposition.

Very-stable↔Stable×Is-proposition :
  {A : Type a} →
  Very-stable A ↝[ a ∣ a ] Stable A × Is-proposition A
Very-stable↔Stable×Is-proposition =
  generalise-ext?-prop
    (record { to   = λ s → _≃_.to s
                         , H-level-cong _ 1 s Squash-propositional
            ; from = λ (s , prop) →
                _↠_.from (Eq.≃↠⇔ Squash-propositional prop)
                  (record { to = s; from = [_] })
            })
    Very-stable-propositional
    (λ ext →
       [inhabited⇒+]⇒+ 0 λ (_ , prop) →
       ×-closure 1
         (Π-closure ext 1 (λ _ → prop))
         (H-level-propositional ext 1))

-- If A has h-level 1 + n and is stable "n levels up", then A is very
-- stable "n levels up".

Stable→H-level-suc→Very-stable :
  ∀ n →
  For-iterated-equality n Stable A → H-level (suc n) A →
  For-iterated-equality n Very-stable A
Stable→H-level-suc→Very-stable {A = A} n = curry (
  For-iterated-equality n Stable A × H-level (suc n) A            ↝⟨ (∃-cong λ _ → lemma) ⟩

  For-iterated-equality n Stable A ×
  For-iterated-equality n Is-proposition A                        ↝⟨ For-iterated-equality-commutes-× n _ ⟩

  For-iterated-equality n (λ A → Stable A × Is-proposition A) A   ↝⟨ For-iterated-equality-cong₁ _ n $
                                                                     uncurry (curry $ _⇔_.from (Very-stable↔Stable×Is-proposition _)) ⟩
  For-iterated-equality n Very-stable A                           □)
  where
  lemma =
    H-level (suc n) A                         ↝⟨ _⇔_.to H-level⇔H-level′ ⟩
    H-level′ (suc n) A                        ↝⟨ inverse-ext? (For-iterated-equality-For-iterated-equality n 1) _ ⟩
    For-iterated-equality n (H-level′ 1) A    ↝⟨ For-iterated-equality-cong₁ _ n $
                                                 _⇔_.from (H-level⇔H-level′ {n = 1}) ⟩□
    For-iterated-equality n Is-proposition A  □

-- Types with h-level n are very stable "n levels up".

H-level→Very-stable :
  ∀ n → H-level n A → For-iterated-equality n Very-stable A
H-level→Very-stable {A = A} n =
  H-level n A                            ↝⟨ _⇔_.to H-level⇔H-level′ ⟩
  H-level′ n A                           ↝⟨ For-iterated-equality-cong₁ _ n Contractible→Very-stable ⟩□
  For-iterated-equality n Very-stable A  □
  where
  Contractible→Very-stable :
    ∀ {A} → Contractible A → Very-stable A
  Contractible→Very-stable c =
    _⇔_.from (Very-stable↔Stable×Is-proposition _)
      ( (λ _ → proj₁ c)
      , mono₁ 0 c
      )

-- If A is very stable, then [_] {A = A} is an embedding.

Very-stable→Is-embedding-[] :
  Very-stable A → Is-embedding {A = A} [_]
Very-stable→Is-embedding-[] s x y =
  _≃_.is-equivalence (
    x ≡ y          ↝⟨ inverse $ Eq.≃-≡ $ Eq.⟨ _ , Very-stable↔Is-equivalence-[] _ s ⟩ ⟩□
    [ x ] ≡ [ y ]  □)

-- If A is very stable, then [_] {A = A} is split surjective.

Very-stable→Split-surjective-[] :
  Very-stable A → Split-surjective {A = A} [_]
Very-stable→Split-surjective-[] {A = A} =
  Very-stable A         ↝⟨ Very-stable↔Is-equivalence-[] _ ⟩
  Is-equivalence [_]    ↝⟨ (λ hyp → _↠_.split-surjective $ _≃_.surjection $ Eq.⟨ _ , hyp ⟩) ⟩
  Split-surjective [_]  □

-- Types that are stable for double negation are stable for Squash.

¬¬-Stable→Stable : (¬ ¬ A → A) → Stable A
¬¬-Stable→Stable ¬¬-Stable x = ¬¬-Stable (Squash→¬¬ x)

-- Types for which it is known whether or not they are inhabited are
-- stable.

Dec→Stable : Dec A → Stable A
Dec→Stable (yes x) _ = x
Dec→Stable (no ¬x) x with () ← Squash→¬¬ x ¬x

-- Every type is stable in the double negation monad.

¬¬-Stable : ¬¬ Stable A
¬¬-Stable = DN.map′ Dec→Stable DN.excluded-middle

-- Bool is not very stable. (But it is stable, see Dec→Stable above.)

¬-Very-stable-Bool : ¬ Very-stable Bool
¬-Very-stable-Bool =
  Very-stable Bool     ↝⟨ proj₂ ∘ Very-stable↔Stable×Is-proposition _ ⟩
  Is-proposition Bool  ↝⟨ ¬-Bool-propositional ⟩□
  ⊥                    □

-- If equality is decidable for A, then equality is very stable for A.

Decidable-equality→Very-stable-≡ :
  Decidable-equality A → Very-stable-≡ A
Decidable-equality→Very-stable-≡ dec _ _ =
  _⇔_.from (Very-stable↔Stable×Is-proposition _)
    ( Dec→Stable (dec _ _)
    , decidable⇒set dec
    )

----------------------------------------------------------------------
-- Preservation lemmas

-- Stable preserves some kinds of functions (those that are
-- "symmetric"), possibly assuming extensionality.

Stable-cong :
  {A : Type a} {B : Type b} →
  Extensionality? ⌊ k ⌋-sym (a ⊔ b) (a ⊔ b) →
  A ↝[ ⌊ k ⌋-sym ] B → Stable A ↝[ ⌊ k ⌋-sym ] Stable B
Stable-cong {k = k} {A = A} {B = B} ext A↝B =
  Stable A        ↔⟨⟩
  (Squash A → A)  ↝⟨ →-cong ext (Squash-cong A↝B) A↝B ⟩
  (Squash B → B)  ↔⟨⟩
  Stable B        □

-- A kind of map function for Stable.

Stable-map : A ⇔ B → Stable A → Stable B
Stable-map = _⇔_.to ∘ Stable-cong _

-- A kind of map function for Very-stable.

Very-stable-map :
  A ↠ B → Very-stable A → Very-stable B
Very-stable-map {A = A} {B = B} A↠B s =
  _↠_.from (Eq.≃↠⇔ Squash-propositional B-prop)
    (record { from = [_]
            ; to   = Squash B  ↝⟨ Squash-cong (_↠_.from A↠B) ⟩
                     Squash A  ↔⟨ s ⟩
                     A         ↝⟨ _↠_.to A↠B ⟩□
                     B         □
            })
  where
  A-prop : Is-proposition A
  A-prop = proj₂ $ Very-stable↔Stable×Is-proposition _ s

  B-prop : Is-proposition B
  B-prop = H-level.respects-surjection A↠B 1 A-prop

-- Very-stable preserves equivalences (assuming extensionality).

Very-stable-cong :
  {A : Type a} {B : Type b} →
  Extensionality? k (a ⊔ b) (a ⊔ b) →
  A ≃ B → Very-stable A ↝[ k ] Very-stable B
Very-stable-cong {A = A} {B = B} ext A≃B =
  Very-stable A  ↔⟨⟩
  Squash A ≃ A   ↝⟨ generalise-ext?
                      (Eq.≃-preserves-⇔ (Squash-cong A≃B) A≃B)
                      (λ ext →
                         let eq = Eq.≃-preserves ext (Squash-cong-≃ A≃B) A≃B in
                         _≃_.right-inverse-of eq , _≃_.left-inverse-of eq)
                      ext ⟩
  Squash B ≃ B   ↔⟨⟩
  Very-stable B  □

------------------------------------------------------------------------
-- Closure properties

-- Squash A is very stable.

Very-stable-Squash : Very-stable (Squash A)
Very-stable-Squash =
  _⇔_.from (Very-stable↔Stable×Is-proposition _)
    ( _>>= id
    , Squash-propositional
    )

-- ⊤ is very stable.

Very-stable-⊤ : Very-stable ⊤
Very-stable-⊤ = Eq.↔⇒≃ Squash-⊤↔⊤

-- ⊥ is very stable.

Very-stable-⊥ : Very-stable (⊥ {ℓ = ℓ})
Very-stable-⊥ = Eq.↔⇒≃ Squash-⊥↔⊥

-- Stable is closed under Π A.

Stable-Π :
  (∀ x → Stable (P x)) →
  Stable ((x : A) → P x)
Stable-Π {P = P} s =
  Squash (∀ x → P x)    ↝⟨ (λ s x → Squash-cong (_$ x) s) ⟩
  (∀ x → Squash (P x))  ↝⟨ ∀-cong _ s ⟩□
  (∀ x → P x)           □

-- Very-stable is closed under Π A (assuming extensionality).

Very-stable-Π :
  {A : Type a} {P : A → Type p} →
  Extensionality a p →
  (∀ x → Very-stable (P x)) →
  Very-stable ((x : A) → P x)
Very-stable-Π {P = P} ext s =
  _⇔_.from (Very-stable↔Stable×Is-proposition _)
    ( Stable-Π (Very-stable→Stable 0 ∘ s)
    , (Π-closure ext 1 λ x →
       H-level-cong _ 1 (s x) Squash-propositional)
    )

-- Stable is closed under _×_.

Stable-× : Stable A → Stable B → Stable (A × B)
Stable-× {A = A} {B = B} s₁ s₂ =
  Squash (A × B)       ↔⟨ Squash-×↔× ⟩
  Squash A × Squash B  ↝⟨ s₁ ×-cong s₂ ⟩□
  A × B                □

-- Very-stable is closed under _×_.

Very-stable-× : Very-stable A → Very-stable B → Very-stable (A × B)
Very-stable-× s₁ s₂ =
  _⇔_.from (Very-stable↔Stable×Is-proposition _)
    ( Stable-× (Very-stable→Stable 0 s₁) (Very-stable→Stable 0 s₂)
    , ×-closure 1 (H-level-cong _ 1 s₁ Squash-propositional)
                  (H-level-cong _ 1 s₂ Squash-propositional)
    )

-- Stable is closed under ↑ ℓ.

Stable-↑ : Stable A → Stable (↑ ℓ A)
Stable-↑ {A = A} s =
  Squash (↑ _ A)  ↔⟨ Squash-↑↔↑ ⟩
  ↑ _ (Squash A)  ↝⟨ ↑-cong s ⟩□
  ↑ _ A           □

-- Very-stable is closed under ↑ ℓ.

Very-stable-↑ : Very-stable A → Very-stable (↑ ℓ A)
Very-stable-↑ {A = A} s =
  Squash (↑ _ A)  ↔⟨ Squash-↑↔↑ ⟩
  ↑ _ (Squash A)  ↝⟨ ↑-cong s ⟩□
  ↑ _ A           □

-- If A is very stable, then equality is very stable for A.

Very-stable→Very-stable-≡ :
  ∀ n →
  For-iterated-equality n       Very-stable A →
  For-iterated-equality (suc n) Very-stable A
Very-stable→Very-stable-≡ {A = A} n =
  For-iterated-equality n Very-stable A        ↝⟨ For-iterated-equality-cong₁ _ n lemma ⟩
  For-iterated-equality n Very-stable-≡ A      ↝⟨ For-iterated-equality-For-iterated-equality n 1 _ ⟩□
  For-iterated-equality (suc n) Very-stable A  □
  where
  lemma : ∀ {A} → Very-stable A → Very-stable-≡ A
  lemma {A = A} s x y =
    _⇔_.from (Very-stable↔Stable×Is-proposition _)
      ( (Squash (x ≡ y)        ↝⟨ const prop ⟩
         Is-proposition A      ↝⟨ +⇒≡ ⟩
         Contractible (x ≡ y)  ↝⟨ proj₁ ⟩□
         x ≡ y                 □)
      , ⇒≡ 1 prop
      )
    where
    prop : Is-proposition A
    prop = proj₂ $ _⇔_.to (Very-stable↔Stable×Is-proposition _) s

private

  -- Some examples showing how Very-stable→Very-stable-≡ can be
  -- used.

  -- Equalities between erased values are very stable.

  Very-stable-≡₀ : Very-stable-≡ (Squash A)
  Very-stable-≡₀ = Very-stable→Very-stable-≡ 0 Very-stable-Squash

  -- Equalities between equalities between erased values are very
  -- stable.

  Very-stable-≡₁ : For-iterated-equality 2 Very-stable (Squash A)
  Very-stable-≡₁ = Very-stable→Very-stable-≡ 1 Very-stable-≡₀

  -- And so on…

private

  -- A lemma.

  For-iterated-equality-Is-proposition↔H-level′-suc :
    {A : Type a} →
    ∀ n →
    For-iterated-equality n Is-proposition A ↝[ a ∣ a ]
    H-level′ (suc n) A
  For-iterated-equality-Is-proposition↔H-level′-suc {A = A} n ext =
    For-iterated-equality n Is-proposition A  ↝⟨ For-iterated-equality-cong₁ ext n (H-level↔H-level′ {n = 1} ext) ⟩
    For-iterated-equality n (H-level′ 1) A    ↝⟨ For-iterated-equality-For-iterated-equality n 1 ext ⟩□
    H-level′ (suc n) A                        □

-- If A is "stable 1 + n levels up", then H-level′ (suc n) A is
-- stable.

Stable-H-level′ :
  ∀ n →
  For-iterated-equality (suc n) Stable A →
  Stable (H-level′ (suc n) A)
Stable-H-level′ {A = A} n =
  For-iterated-equality (suc n) Stable A               ↝⟨ inverse-ext? (For-iterated-equality-For-iterated-equality n 1) _ ⟩
  For-iterated-equality n Stable-≡ A                   ↝⟨ For-iterated-equality-cong₁ _ n lemma ⟩
  For-iterated-equality n (Stable ∘ Is-proposition) A  ↝⟨ For-iterated-equality-commutes-← _ Stable n Stable-Π ⟩
  Stable (For-iterated-equality n Is-proposition A)    ↝⟨ Stable-map (For-iterated-equality-Is-proposition↔H-level′-suc n _) ⟩□
  Stable (H-level′ (suc n) A)                          □
  where
  lemma : ∀ {A} → Stable-≡ A → Stable (Is-proposition A)
  lemma s =
    Stable-Π λ _ →
    Stable-Π λ _ →
    s _ _

-- If A is "stable 1 + n levels up", then H-level (suc n) A is
-- stable.

Stable-H-level :
  ∀ n →
  For-iterated-equality (suc n) Stable A →
  Stable (H-level (suc n) A)
Stable-H-level {A = A} n =
  For-iterated-equality (suc n) Stable A  ↝⟨ Stable-H-level′ n ⟩
  Stable (H-level′ (suc n) A)             ↝⟨ Stable-map (record { to = inverse-ext? H-level↔H-level′ _; from = H-level↔H-level′ _ }) ⟩□
  Stable (H-level (suc n) A)              □

-- If A is "very stable 1 + n levels up", then H-level′ (suc n) A is
-- very stable (assuming extensionality).

Very-stable-H-level′ :
  {A : Type a} →
  Extensionality a a →
  ∀ n →
  For-iterated-equality (suc n) Very-stable A →
  Very-stable (H-level′ (suc n) A)
Very-stable-H-level′ {A = A} ext n =
  For-iterated-equality (suc n) Very-stable A               ↝⟨ inverse-ext? (For-iterated-equality-For-iterated-equality n 1) _ ⟩
  For-iterated-equality n Very-stable-≡ A                   ↝⟨ For-iterated-equality-cong₁ _ n lemma ⟩
  For-iterated-equality n (Very-stable ∘ Is-proposition) A  ↝⟨ For-iterated-equality-commutes-← _ Very-stable n (Very-stable-Π ext) ⟩
  Very-stable (For-iterated-equality n Is-proposition A)    ↝⟨ Very-stable-map (For-iterated-equality-Is-proposition↔H-level′-suc n ext) ⟩□
  Very-stable (H-level′ (suc n) A)                          □
  where
  lemma : ∀ {A} → Very-stable-≡ A → Very-stable (Is-proposition A)
  lemma s =
    Very-stable-Π ext λ _ →
    Very-stable-Π ext λ _ →
    s _ _

-- If A is "very stable 1 + n levels up", then H-level (suc n) A is
-- very stable (assuming extensionality).

Very-stable-H-level :
  {A : Type a} →
  Extensionality a a →
  ∀ n →
  For-iterated-equality (suc n) Very-stable A →
  Very-stable (H-level (suc n) A)
Very-stable-H-level {A = A} ext n =
  For-iterated-equality (suc n) Very-stable A  ↝⟨ Very-stable-H-level′ ext n ⟩
  Very-stable (H-level′ (suc n) A)             ↝⟨ Very-stable-cong _ (inverse $ H-level↔H-level′ ext) ⟩□
  Very-stable (H-level (suc n) A)              □

-- If equality is stable for A and B, then it is stable for A ⊎ B.

Stable-≡-⊎ :
  ∀ n →
  For-iterated-equality (suc n) Stable A →
  For-iterated-equality (suc n) Stable B →
  For-iterated-equality (suc n) Stable (A ⊎ B)
Stable-≡-⊎ n sA sB =
  For-iterated-equality-⊎-suc
    n
    (Stable-map ∘ from-isomorphism)
    (Very-stable→Stable 0 Very-stable-⊥)
    (For-iterated-equality-↑ _ (suc n) (Stable-map ∘ from-isomorphism) sA)
    (For-iterated-equality-↑ _ (suc n) (Stable-map ∘ from-isomorphism) sB)

-- If equality is very stable for A and B, then it is very stable
-- for A ⊎ B.

Very-stable-≡-⊎ :
  ∀ n →
  For-iterated-equality (suc n) Very-stable A →
  For-iterated-equality (suc n) Very-stable B →
  For-iterated-equality (suc n) Very-stable (A ⊎ B)
Very-stable-≡-⊎ n sA sB =
  For-iterated-equality-⊎-suc
    n
    lemma
    Very-stable-⊥
    (For-iterated-equality-↑ _ (suc n) lemma sA)
    (For-iterated-equality-↑ _ (suc n) lemma sB)
  where
  lemma : A ↔ B → Very-stable A → Very-stable B
  lemma = Very-stable-cong _ ∘ from-isomorphism

-- If equality is stable for A, then it is stable for List A.

Stable-≡-List :
  ∀ n →
  For-iterated-equality (suc n) Stable A →
  For-iterated-equality (suc n) Stable (List A)
Stable-≡-List n =
  For-iterated-equality-List-suc
    n
    (Stable-map ∘ from-isomorphism)
    (Very-stable→Stable 0 $ Very-stable-↑ Very-stable-⊤)
    (Very-stable→Stable 0 Very-stable-⊥)
    Stable-×

-- If equality is very stable for A, then it is very stable for
-- List A.

Very-stable-≡-List :
  ∀ n →
  For-iterated-equality (suc n) Very-stable A →
  For-iterated-equality (suc n) Very-stable (List A)
Very-stable-≡-List n =
  For-iterated-equality-List-suc
    n
    (Very-stable-cong _ ∘ from-isomorphism)
    (Very-stable-↑ Very-stable-⊤)
    Very-stable-⊥
    Very-stable-×

----------------------------------------------------------------------
-- Simple corollaries or variants of results above

-- A generalisation of Stable-Π.

Stable-Πⁿ :
  {A : Type a} {P : A → Type p} →
  Extensionality a p →
  ∀ n →
  (∀ x → For-iterated-equality n Stable (P x)) →
  For-iterated-equality n Stable ((x : A) → P x)
Stable-Πⁿ ext n =
  For-iterated-equality-Π
    ext
    n
    (Stable-map ∘ from-isomorphism)
    Stable-Π

-- A generalisation of Very-stable-Π.

Very-stable-Πⁿ :
  {A : Type a} {P : A → Type p} →
  Extensionality a p →
  ∀ n →
  (∀ x → For-iterated-equality n Very-stable (P x)) →
  For-iterated-equality n Very-stable ((x : A) → P x)
Very-stable-Πⁿ ext n =
  For-iterated-equality-Π
    ext
    n
    (Very-stable-cong _ ∘ from-isomorphism)
    (Very-stable-Π ext)

-- A generalisation of Stable-×.

Stable-×ⁿ :
  ∀ n →
  For-iterated-equality n Stable A →
  For-iterated-equality n Stable B →
  For-iterated-equality n Stable (A × B)
Stable-×ⁿ n =
  For-iterated-equality-×
    n
    (Stable-map ∘ from-isomorphism)
    Stable-×

-- A generalisation of Very-stable-×.

Very-stable-×ⁿ :
  ∀ n →
  For-iterated-equality n Very-stable A →
  For-iterated-equality n Very-stable B →
  For-iterated-equality n Very-stable (A × B)
Very-stable-×ⁿ n =
  For-iterated-equality-×
    n
    (Very-stable-cong _ ∘ from-isomorphism)
    Very-stable-×

-- A generalisation of Stable-↑.

Stable-↑ⁿ :
  ∀ n →
  For-iterated-equality n Stable A →
  For-iterated-equality n Stable (↑ ℓ A)
Stable-↑ⁿ n =
  For-iterated-equality-↑ _ n (Stable-map ∘ from-isomorphism)

-- A generalisation of Very-stable-↑.

Very-stable-↑ⁿ :
  ∀ n →
  For-iterated-equality n Very-stable A →
  For-iterated-equality n Very-stable (↑ ℓ A)
Very-stable-↑ⁿ n =
  For-iterated-equality-↑
    _
    n
    (Very-stable-cong _ ∘ from-isomorphism)


-- Andreas, 2014-05-20 Triggered by Andrea Vezzosi & NAD
{-# OPTIONS --copatterns #-}
-- {-# OPTIONS -v tc.conv.coerce:10 #-}

open import Common.Size

-- Andreas, 2015-03-16: currently forbidden
-- Size≤ : Size → SizeUniv
-- Size≤ i = Size< ↑ i

postulate
  Dom    : Size → Set
  mapDom : ∀ i (j : Size< (↑ i)) → Dom i → Dom j

record ∞Dom i : Set where
  field
    force : ∀ (j : Size< i) → Dom j

∞mapDom : ∀ i (j : Size< (↑ i)) → ∞Dom i → ∞Dom j
∞Dom.force (∞mapDom i j x) k = mapDom k k (∞Dom.force x k)

-- The second k on the rhs has type
--   k : Size< j
-- and should have type
--   k : Size≤ k = Size< ↑ k
-- Since j <= ↑ k does not hold (we have only k < j),
-- we cannot do the usual subtyping  Size< j <= Size≤ k,
-- but we have to use the "singleton type property"
--   k : Size< ↑ k


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

open import common
open import common using (_===_)
open import syntx
open import derivability
open import structuralrules
open import typingrules

{- Type theories -}

Σ₀ : Signature
Symbols Σ₀ ar = Empty

E₀ : DerivabilityStructure Σ₀
E₀ = StructuralRules Σ₀

data TypeTheory : Set
TTSig : TypeTheory → Signature
TTDer : (t : TypeTheory) → DerivabilityStructure (TTSig t)

data TypeTheory where
  ◇ : TypeTheory
  _,_ : (t : TypeTheory) {ar : SyntaxArity} (r : TypingRule (TTDer t) (args ar) (sort ar)) → TypeTheory
  _,=_ : (t : TypeTheory) {ar : SyntaxArityArgs} {k : SyntaxSort} (r : EqualityRule (TTDer t) ar k) → TypeTheory

TTSig ◇ = Σ₀
TTSig (_,_ t {ar} r) = ExtSig (TTSig t) ar
TTSig (t ,= _) = TTSig t

TTDer ◇ = E₀
TTDer (t , r) = extend (TTDer t) (TRules r)
TTDer (t ,= r) = extendE (TTDer t) (ERule r)

{- Instances to make it possible to use numeric literals to refer to symbols and typing rules -}

instance
  NumΣ₀ : Number Empty
  Number.Constraint NumΣ₀ n = Empty

  NumExtSig : {A : SyntaxArity → Set} {ar ar' : SyntaxArity} {{_ : Number (A ar')}} → Number (ExtSigSymbols A ar ar')
  Number.Constraint (NumExtSig {ar = ar} {ar'}) zero = ar === ar'
  Number.Constraint (NumExtSig {{r}}) (suc n) = Number.Constraint r n
  Number.fromNat NumExtSig zero {{refl}} = new
  Number.fromNat (NumExtSig {{r}}) (suc n) = prev (Number.fromNat r n)

instance
  NumExtT : {A : JudgmentArity → Set} {ar : SyntaxArity} {ar' : JudgmentArity} {{_ : Number (A ar')}} → Number (ExtT A ar ar')
  Number.Constraint (NumExtT {ar = ar} {ar'}) zero = TArity ar === ar'
  Number.Constraint (NumExtT {{r}}) (suc n) = Number.Constraint r n
  Number.fromNat NumExtT zero {{refl}} = typingrule
  Number.fromNat (NumExtT {{r}}) (suc n) = prev (Number.fromNat r n)

  NumExtC : {A : JudgmentArity → Set} {ar : SyntaxArity} {ar' : JudgmentArity} {{_ : Number (A ar')}} → Number (ExtC A ar ar')
  Number.Constraint (NumExtC {ar = ar} {ar'}) zero = CArity ar === ar'
  Number.Constraint (NumExtC {{r}}) (suc n) = Number.Constraint r n
  Number.fromNat NumExtC zero {{refl}} = congruencerule
  Number.fromNat (NumExtC {{r}}) (suc n) = prev (Number.fromNat r n)

  NumExtE : {A : JudgmentArity → Set} {nar ar' : JudgmentArity} {{_ : Number (A ar')}} → Number (ExtE A nar ar')
  Number.Constraint (NumExtE {nar = nar} {ar'}) zero = nar === ar'
  Number.Constraint (NumExtE {{r}}) (suc n) = Number.Constraint r n
  Number.fromNat NumExtE zero {{refl}} = equalityrule
  Number.fromNat (NumExtE {{r}}) (suc n) = prev (Number.fromNat r n)


open import Data.Sum using ( _⊎_ ; inj₁ ; inj₂ )
open import Relation.Binary.PropositionalEquality using ( refl )
open import Web.Semantic.DL.ABox.Interp using ( ⌊_⌋ ; ind ; _*_ )
open import Web.Semantic.DL.ABox.Model using
  ( _⊨a_ ; ⊨a-resp-≡³ ; on-bnode ; bnodes ; _,_ )
open import Web.Semantic.DL.Category.Object using ( Object ; IN )
open import Web.Semantic.DL.Category.Morphism using
  ( _⇒_ ; BN ; impl ; _⊑_ ; _≣_ ; _,_ )
open import Web.Semantic.DL.Category.Composition using ( _∙_ )
open import Web.Semantic.DL.Category.Tensor using ( _⊗_ ; _⟨⊗⟩_ )
open import Web.Semantic.DL.Category.Wiring using ( identity ; id✓ )
open import Web.Semantic.DL.Category.Properties.Composition.Lemmas using
  ( compose-left ; compose-right ; compose-resp-⊨a )
open import Web.Semantic.DL.Category.Properties.Tensor.Lemmas using
  ( tensor-up ; tensor-down ; tensor-resp-⊨a )
open import Web.Semantic.DL.Category.Properties.Tensor.RespectsWiring using
  ( tensor-resp-wiring )
open import Web.Semantic.DL.Signature using ( Signature )
open import Web.Semantic.DL.TBox using ( TBox )
open import Web.Semantic.DL.TBox.Interp using ( Δ )
open import Web.Semantic.Util using
  ( id ; _∘_ ; _⊕_⊕_ ; inode ; bnode ; enode ; left ; right ; up ; down )

module Web.Semantic.DL.Category.Properties.Tensor.Functor
  {Σ : Signature} {S T : TBox Σ} where

tensor-resp-id : ∀ (A₁ A₂ : Object S T) →
  ((identity A₁ ⟨⊗⟩ identity A₂) ≣ identity (A₁ ⊗ A₂))
tensor-resp-id A₁ A₂ =
  tensor-resp-wiring A₁ A₂ A₁ A₂
    id (id✓ A₁) id (id✓ A₂) id (id✓ (A₁ ⊗ A₂))
    (λ x → refl) (λ x → refl)

tensor-resp-compose :  ∀ {A₁ A₂ B₁ B₂ C₁ C₂ : Object S T}
  (F₁ : A₁ ⇒ B₁) (F₂ : A₂ ⇒ B₂) (G₁ : B₁ ⇒ C₁) (G₂ : B₂ ⇒ C₂) →
    (((F₁ ∙ G₁) ⟨⊗⟩ (F₂ ∙ G₂)) ≣ ((F₁ ⟨⊗⟩ F₂) ∙ (G₁ ⟨⊗⟩ G₂)))
tensor-resp-compose {A₁} {A₂} {B₁} {B₂} {C₁} {C₂} F₁ F₂ G₁ G₂ =
  (LHS⊑RHS , RHS⊑LHS) where

  LHS⊑RHS : (F₁ ∙ G₁) ⟨⊗⟩ (F₂ ∙ G₂) ⊑ (F₁ ⟨⊗⟩ F₂) ∙ (G₁ ⟨⊗⟩ G₂)
  LHS⊑RHS I I⊨STA₁A₂ I⊨LHS = (f , I⊨RHS) where

    f : ((BN F₁ ⊎ BN F₂) ⊕ (IN B₁ ⊎ IN B₂) ⊕ (BN G₁ ⊎ BN G₂)) → Δ ⌊ I ⌋
    f (inode (inj₁ v)) = ind I (bnode (inj₁ (inode v)))
    f (inode (inj₂ v)) = ind I (bnode (inj₂ (inode v)))
    f (bnode (inj₁ y)) = ind I (bnode (inj₁ (bnode y)))
    f (bnode (inj₂ y)) = ind I (bnode (inj₂ (bnode y)))
    f (enode (inj₁ w)) = ind I (bnode (inj₁ (enode w)))
    f (enode (inj₂ w)) = ind I (bnode (inj₂ (enode w)))

    Iˡ₁⊨F₁ : up * left * bnodes I f ⊨a impl F₁
    Iˡ₁⊨F₁ = ⊨a-resp-≡³
      (left * up * I) (on-bnode f (ind I) ∘ left ∘ up) refl (impl F₁)
      (compose-left F₁ G₁ (up * I) (tensor-up (F₁ ∙ G₁) (F₂ ∙ G₂) I I⊨LHS))

    Iˡ₂⊨F₂ : down * (left * bnodes I f) ⊨a impl F₂
    Iˡ₂⊨F₂ = ⊨a-resp-≡³
      (left * down * I) (on-bnode f (ind I) ∘ left ∘ down) refl (impl F₂)
      (compose-left F₂ G₂ (down * I) (tensor-down (F₁ ∙ G₁) (F₂ ∙ G₂) I I⊨LHS))

    Iʳ₁⊨G₁ : up * (right * bnodes I f) ⊨a impl G₁
    Iʳ₁⊨G₁ = ⊨a-resp-≡³
      (right * up * I) (on-bnode f (ind I) ∘ right ∘ up) refl (impl G₁)
      (compose-right F₁ G₁ (up * I) (tensor-up (F₁ ∙ G₁) (F₂ ∙ G₂) I I⊨LHS))

    Iʳ₂⊨G₂ : down * (right * bnodes I f) ⊨a impl G₂
    Iʳ₂⊨G₂ = ⊨a-resp-≡³
      (right * down * I) (on-bnode f (ind I) ∘ right ∘ down) refl (impl G₂)
      (compose-right F₂ G₂ (down * I) (tensor-down (F₁ ∙ G₁) (F₂ ∙ G₂) I I⊨LHS))

    I⊨RHS : bnodes I f ⊨a impl ((F₁ ⟨⊗⟩ F₂) ∙ (G₁ ⟨⊗⟩ G₂))
    I⊨RHS = compose-resp-⊨a (F₁ ⟨⊗⟩ F₂) (G₁ ⟨⊗⟩ G₂) (bnodes I f)
      (tensor-resp-⊨a F₁ F₂ (left * bnodes I f) Iˡ₁⊨F₁ Iˡ₂⊨F₂)
      (tensor-resp-⊨a G₁ G₂ (right * bnodes I f) Iʳ₁⊨G₁ Iʳ₂⊨G₂)

  RHS⊑LHS : (F₁ ⟨⊗⟩ F₂) ∙ (G₁ ⟨⊗⟩ G₂) ⊑ (F₁ ∙ G₁) ⟨⊗⟩ (F₂ ∙ G₂)
  RHS⊑LHS I I⊨STA₁A₂ I⊨RHS = (f , I⊨LHS) where

    f : ((BN F₁ ⊕ IN B₁ ⊕ BN G₁) ⊎ (BN F₂ ⊕ IN B₂ ⊕ BN G₂)) → Δ ⌊ I ⌋
    f (inj₁ (inode v)) = ind I (bnode (inode (inj₁ v)))
    f (inj₁ (bnode y)) = ind I (bnode (bnode (inj₁ y)))
    f (inj₁ (enode w)) = ind I (bnode (enode (inj₁ w)))
    f (inj₂ (inode v)) = ind I (bnode (inode (inj₂ v)))
    f (inj₂ (bnode y)) = ind I (bnode (bnode (inj₂ y)))
    f (inj₂ (enode w)) = ind I (bnode (enode (inj₂ w)))

    I₁ˡ⊨F₁ : left * up * bnodes I f ⊨a impl F₁
    I₁ˡ⊨F₁ = ⊨a-resp-≡³
      (up * left * I) (on-bnode f (ind I) ∘ up ∘ left) refl (impl F₁)
      (tensor-up F₁ F₂ (left * I) (compose-left (F₁ ⟨⊗⟩ F₂) (G₁ ⟨⊗⟩ G₂) I I⊨RHS))

    I₁ʳ⊨G₁ : right * up * bnodes I f ⊨a impl G₁
    I₁ʳ⊨G₁ = ⊨a-resp-≡³
      (up * right * I) (on-bnode f (ind I) ∘ up ∘ right) refl (impl G₁)
      (tensor-up G₁ G₂ (right * I) (compose-right (F₁ ⟨⊗⟩ F₂) (G₁ ⟨⊗⟩ G₂) I I⊨RHS))

    I₂ˡ⊨F₂ : left * down * bnodes I f ⊨a impl F₂
    I₂ˡ⊨F₂ = ⊨a-resp-≡³
      (down * left * I) (on-bnode f (ind I) ∘ down ∘ left) refl (impl F₂)
      (tensor-down F₁ F₂ (left * I) (compose-left (F₁ ⟨⊗⟩ F₂) (G₁ ⟨⊗⟩ G₂) I I⊨RHS))

    I₂ʳ⊨G₂ : right * down * bnodes I f ⊨a impl G₂
    I₂ʳ⊨G₂ = ⊨a-resp-≡³
      (down * right * I) (on-bnode f (ind I) ∘ down ∘ right) refl (impl G₂)
      (tensor-down G₁ G₂ (right * I) (compose-right (F₁ ⟨⊗⟩ F₂) (G₁ ⟨⊗⟩ G₂) I I⊨RHS))

    I⊨LHS : bnodes I f ⊨a impl ((F₁ ∙ G₁) ⟨⊗⟩ (F₂ ∙ G₂))
    I⊨LHS = tensor-resp-⊨a (F₁ ∙ G₁) (F₂ ∙ G₂) (bnodes I f)
      (compose-resp-⊨a F₁ G₁ (up * bnodes I f) I₁ˡ⊨F₁ I₁ʳ⊨G₁)
      (compose-resp-⊨a F₂ G₂ (down * bnodes I f) I₂ˡ⊨F₂ I₂ʳ⊨G₂)


------------------------------------------------------------------------
-- The Agda standard library
--
-- Empty type
------------------------------------------------------------------------

module Data.Empty where

open import Level

data ⊥ : Set where

{-# IMPORT Data.FFI #-}
{-# COMPILED_DATA ⊥ Data.FFI.AgdaEmpty #-}

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


------------------------------------------------------------------------------
-- Properties for the bisimilarity relation
------------------------------------------------------------------------------

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

module FOTC.Relation.Binary.Bisimilarity.PropertiesI where

open import FOTC.Base
open import FOTC.Base.List
open import FOTC.Base.List.PropertiesI
open import FOTC.Base.PropertiesI
open import FOTC.Data.Stream.Type
open import FOTC.Relation.Binary.Bisimilarity.Type

------------------------------------------------------------------------------
-- Because a greatest post-fixed point is a fixed-point, the
-- bisimilarity relation _≈_ on unbounded lists is also a pre-fixed
-- point of the bisimulation functional (see
-- FOTC.Relation.Binary.Bisimulation).
≈-in : ∀ {xs ys} →
       ∃[ x' ]  ∃[ xs' ] ∃[ ys' ]
         xs ≡ x' ∷ xs' ∧ ys ≡ x' ∷ ys' ∧ xs' ≈ ys' →
       xs ≈ ys
≈-in h = ≈-coind B h' h
  where
  B : D → D → Set
  B xs ys = ∃[ x' ]  ∃[ xs' ] ∃[ ys' ]
              xs ≡ x' ∷ xs' ∧ ys ≡ x' ∷ ys' ∧ xs' ≈ ys'

  h' : ∀ {xs} {ys} → B xs ys →
       ∃[ x' ] ∃[ xs' ] ∃[ ys' ] xs ≡ x' ∷ xs' ∧ ys ≡ x' ∷ ys' ∧ B xs' ys'
  h' (x' , xs' , ys' , prf₁ , prf₂ , xs'≈ys') =
    x' , xs' , ys' , prf₁ , prf₂ , ≈-out xs'≈ys'

≈-refl : ∀ {xs} → Stream xs → xs ≈ xs
≈-refl {xs} Sxs = ≈-coind B h₁ h₂
  where
  B : D → D → Set
  B xs ys = Stream xs ∧ xs ≡ ys

  h₁ : ∀ {xs ys} → B xs ys →
       ∃[ x' ] ∃[ xs' ] ∃[ ys' ] xs ≡ x' ∷ xs' ∧ ys ≡ x' ∷ ys' ∧ B xs' ys'
  h₁ (Sxs , refl) with Stream-out Sxs
  ... | x' , xs' , prf , Sxs' =
    x' , xs' , xs' , prf , prf , (Sxs' , refl)

  h₂ : B xs xs
  h₂ = Sxs , refl

≈-sym : ∀ {xs ys} → xs ≈ ys → ys ≈ xs
≈-sym {xs} {ys} xs≈ys = ≈-coind B h₁ h₂
  where
  B : D → D → Set
  B xs ys = ys ≈ xs

  h₁ : ∀ {ys xs} → B ys xs →
       ∃[ y' ] ∃[ ys' ] ∃[ xs' ] ys ≡ y' ∷ ys' ∧ xs ≡ y' ∷ xs' ∧ B ys' xs'
  h₁ Bxsys with ≈-out Bxsys
  ... | y' , ys' , xs' , prf₁ , prf₂ , ys'≈xs' =
    y' , xs' , ys' , prf₂ , prf₁ , ys'≈xs'

  h₂ : B ys xs
  h₂ = xs≈ys

≈-trans : ∀ {xs ys zs} → xs ≈ ys → ys ≈ zs → xs ≈ zs
≈-trans {xs} {ys} {zs} xs≈ys ys≈zs = ≈-coind B h₁ h₂
  where
  B : D → D → Set
  B xs zs = ∃[ ys ] xs ≈ ys ∧ ys ≈ zs

  h₁ : ∀ {as} {cs} → B as cs →
       ∃[ a' ] ∃[ as' ] ∃[ cs' ] as ≡ a' ∷ as' ∧ cs ≡ a' ∷ cs' ∧ B as' cs'
  h₁ {cs = cs} (bs , as≈bs , bs≈cs) with ≈-out as≈bs
  ... | a' , as' , bs' , prf₁ , prf₂ , as'≈bs' with ≈-out bs≈cs
  ... | b' , bs'' , cs' , prf₃ , prf₄ , bs''≈cs' =
    a'
    , as'
    , cs'
    , prf₁
    , subst (λ t → cs ≡ t ∷ cs') b'≡a' prf₄
    , (bs' , as'≈bs' , subst (λ t → t ≈ cs') bs''≡bs' bs''≈cs')
    where
    b'≡a' : b' ≡ a'
    b'≡a' = ∧-proj₁ (∷-injective (trans (sym prf₃) prf₂))

    bs''≡bs' : bs'' ≡ bs'
    bs''≡bs' = ∧-proj₂ (∷-injective (trans (sym prf₃) prf₂))

  h₂ : B xs zs
  h₂ = ys , (xs≈ys , ys≈zs)

∷-injective≈ : ∀ {x xs ys} → x ∷ xs ≈ x ∷ ys → xs ≈ ys
∷-injective≈ {x} {xs} {ys} h with ≈-out h
... | x' , xs' , ys' , prf₁ , prf₂ , prf₃ = xs≈ys
  where
  xs≡xs' : xs ≡ xs'
  xs≡xs' = ∧-proj₂ (∷-injective prf₁)

  ys≡ys' : ys ≡ ys'
  ys≡ys' = ∧-proj₂ (∷-injective prf₂)

  xs≈ys : xs ≈ ys
  xs≈ys = subst (λ t → t ≈ ys)
                (sym xs≡xs')
                (subst (_≈_ xs') (sym ys≡ys') prf₃)

∷-rightCong≈ : ∀ {x xs ys} → xs ≈ ys → x ∷ xs ≈ x ∷ ys
∷-rightCong≈ {x} {xs} {ys} h = ≈-in (x , xs , ys , refl , refl , h)


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

open import lib.Base

module lib.PathGroupoid where

module _ {i} {A : Type i} where

  {- Concatenation of paths

  There are two different definitions of concatenation of paths, [_∙_] and [_∙'_],
  with different definitionnal behaviour. Maybe we should have only one but it’s
  sometimes useful to have both (in particular in lib.types.Paths).
  -}

  infixr 80 _∙_ _∙'_

  _∙_ : {x y z : A}
    → (x == y → y == z → x == z)
  idp ∙ q = q

  _∙'_ : {x y z : A}
    → (x == y → y == z → x == z)
  q ∙' idp = q

  ∙=∙' : {x y z : A} (p : x == y) (q : y == z)
    → p ∙ q == p ∙' q
  ∙=∙' idp idp = idp

  ∙'=∙ : {x y z : A} (p : x == y) (q : y == z)
    → p ∙' q == p ∙ q
  ∙'=∙ idp idp = idp

  ∙-assoc : {x y z t : A} (p : x == y) (q : y == z) (r : z == t)
    → (p ∙ q) ∙ r == p ∙ (q ∙ r)
  ∙-assoc idp _ _ = idp

  ∙'-assoc : {x y z t : A} (p : x == y) (q : y == z) (r : z == t)
    → (p ∙' q) ∙' r == p ∙' (q ∙' r)
  ∙'-assoc _ _ idp = idp

  -- [∙-unit-l] and [∙'-unit-r] are definitional

  ∙-unit-r : {x y : A} (q : x == y) → q ∙ idp == q
  ∙-unit-r idp = idp

  ∙'-unit-l : {x y : A} (q : x == y) → idp ∙' q == q
  ∙'-unit-l idp = idp

  {- Reversal of paths -}

  ! : {x y : A} → (x == y → y == x)
  ! idp = idp

  !-inv-l : {x y : A} (p : x == y) → (! p) ∙ p == idp
  !-inv-l idp = idp

  !-inv'-l : {x y : A} (p : x == y) → (! p) ∙' p == idp
  !-inv'-l idp = idp

  !-inv-r : {x y : A} (p : x == y) → p ∙ (! p) == idp
  !-inv-r idp = idp

  !-inv'-r : {x y : A} (p : x == y) → p ∙' (! p) == idp
  !-inv'-r idp = idp

  {- Interactions between operations

  A lemma of the form [!-∙ …] gives a result of the form [! (_∙_ …) == …],
  and so on.
  -}

  !-∙ : {x y z : A} (p : x == y) (q : y == z) → ! (p ∙ q) == ! q ∙ ! p
  !-∙ idp idp = idp

  ∙-! : {x y z : A} (q : y == z) (p : x == y) → ! q ∙ ! p == ! (p ∙ q)
  ∙-! idp idp = idp

  !-∙' : {x y z : A} (p : x == y) (q : y == z) → ! (p ∙' q) == ! q ∙' ! p
  !-∙' idp idp = idp

  ∙'-! : {x y z : A} (q : y == z) (p : x == y) → ! q ∙' ! p == ! (p ∙' q)
  ∙'-! idp idp = idp

  !-! : {x y : A} (p : x == y) → ! (! p) == p
  !-! idp = idp

  {- Horizontal compositions -}

  infixr 80 _∙2_ _∙'2_

  _∙2_ : {x y z : A} {p p' : x == y} {q q' : y == z} (α : p == p') (β : q == q')
    → p ∙ q == p' ∙ q'
  _∙2_ {p = idp} idp β = β

  _∙'2_ : {x y z : A} {p p' : x == y} {q q' : y == z} (α : p == p') (β : q == q')
    → p ∙' q == p' ∙' q'
  _∙'2_ {q = idp} α idp = α

  idp∙2idp : {x y z : A} (p : x == y) (q : y == z)
    → (idp {a = p}) ∙2 (idp {a = q}) == idp
  idp∙2idp idp idp = idp

  idp∙'2idp : {x y z : A} (p : x == y) (q : y == z)
    → (idp {a = p}) ∙'2 (idp {a = q}) == idp
  idp∙'2idp idp idp = idp

{-
Sometimes we need to restart a new section in order to have everything in the
previous one generalized.
-}
module _ {i} {A : Type i} where
  {- Whisker and horizontal composition for Eckmann-Hilton argument -}

  infixr 80 _∙ᵣ_ _⋆2_ _⋆'2_
  infixl 80 _∙ₗ_

  _∙ᵣ_ : {x y z : A} {p p' : x == y} (α : p == p') (q : y == z)
    → p ∙ q == p' ∙ q
  _∙ᵣ_ {p = p} {p' = p'} α idp = ∙-unit-r p ∙ α ∙ ! (∙-unit-r p')

  _∙ₗ_ : {x y z : A} {q q' : y == z} (p : x == y) (β : q == q')
    → p ∙ q == p ∙ q'
  _∙ₗ_ {q = q} {q' = q'} idp β = β

  _⋆2_ : {x y z : A} {p p' : x == y} {q q' : y == z}
         (α : p == p') (β : q == q')
    → p ∙ q == p' ∙ q'
  _⋆2_ {p' = p'} {q = q} α β = (α ∙ᵣ q) ∙ (p' ∙ₗ β)

  _⋆'2_ : {x y z : A} {p p' : x == y} {q q' : y == z}
          (α : p == p') (β : q == q')
    → p ∙ q == p' ∙ q'
  _⋆'2_ {p = p} {q' = q'} α β = (p ∙ₗ β) ∙ (α ∙ᵣ q')

  ⋆2=⋆'2 : {x y z : A} {p p' : x == y} {q q' : y == z}
           (α : p == p') (β : q == q')
    → α ⋆2 β == α ⋆'2 β
  ⋆2=⋆'2 {p = idp} {q = idp} idp idp = idp

module _ {i} {A : Type i} where

  anti-whisker-right : {x y z : A} (p : y == z) {q r : x == y}
    → (q ∙ p == r ∙ p → q == r)
  anti-whisker-right idp {q} {r} h =
    ! (∙-unit-r q) ∙ (h ∙ ∙-unit-r r)

  anti-whisker-left : {x y z : A} (p : x == y) {q r : y == z}
    → (p ∙ q == p ∙ r → q == r)
  anti-whisker-left idp h = h

{- Dependent stuff -}
module _ {i j} {A : Type i} {B : A → Type j} where

  {- Dependent constant path -}

  idpᵈ : {x : A} {u : B x} → u == u [ B ↓ idp ]
  idpᵈ = idp

  {- Dependent opposite path -}

  !ᵈ : {x y : A} {p : x == y} {u : B x} {v : B y}
    → (u == v [ B ↓ p ] → v == u [ B ↓ (! p)])
  !ᵈ {p = idp} = !

  !ᵈ' : {x y : A} {p : x == y} {u : B y} {v : B x}
    → (u == v [ B ↓ (! p) ] → v == u [ B ↓ p ])
  !ᵈ' {p = idp} = !

  !ᵈ-!ᵈ' : {x y : A} {p : x == y} {u : B y} {v : B x}
    → (q : u == v [ B ↓ (! p) ])
    → !ᵈ (!ᵈ' q) == q
  !ᵈ-!ᵈ' {p = idp} idp = idp

  {- Dependent concatenation -}

  infixr 80 _∙ᵈ_ _∙'ᵈ_ _◃_ _▹_ _!◃_ _▹!_

  _∙ᵈ_ : {x y z : A} {p : x == y} {p' : y == z}
    {u : B x} {v : B y} {w : B z}
    → (u == v [ B ↓ p ]
    → v == w [ B ↓ p' ]
    → u == w [ B ↓ (p ∙ p') ])
  _∙ᵈ_ {p = idp} {p' = idp} q r = q ∙ r

  _◃_ = _∙ᵈ_

  ◃idp : {x : A} {v w : B x} (q : w == v)
    → q ◃ idp == q
  ◃idp idp = idp

  idp◃ : {x y : A} {p : x == y}
    {u : B x} {v : B y} (r : u == v [ B ↓ p ])
    → idp ◃ r == r
  idp◃ {p = idp} r = idp

  _∙'ᵈ_ : {x y z : A} {p : x == y} {p' : y == z}
    {u : B x} {v : B y} {w : B z}
    → (u == v [ B ↓ p ]
    → v == w [ B ↓ p' ]
    → u == w [ B ↓ (p ∙' p') ])
  _∙'ᵈ_ {p = idp} {p' = idp} q r = q ∙' r

  _▹_ = _∙'ᵈ_

  {- That’s not perfect, because [q] could be a dependent path. But in that case
     this is not well typed… -}
  idp▹ : {x : A} {v w : B x} (q : v == w)
    → idp ▹ q == q
  idp▹ idp = idp

  ▹idp : {x y : A} {p : x == y}
    {u : B x} {v : B y} (q : u == v [ B ↓ p ])
    → q ▹ idp == q
  ▹idp {p = idp} idp = idp

  _▹!_ : {x y z : A} {p : x == y} {p' : z == y}
    {u : B x} {v : B y} {w : B z}
    → u == v [ B ↓ p ]
    → w == v [ B ↓ p' ]
    → u == w [ B ↓ p ∙' (! p') ]
  _▹!_ {p' = idp} q idp = q

  idp▹! : {x : A} {v w : B x} (q : w == v)
    → idp ▹! q == ! q
  idp▹! idp = idp

  _!◃_ : {x y z : A} {p : y == x} {p' : y == z}
    {u : B x} {v : B y} {w : B z}
    → v == u [ B ↓ p ]
    → v == w [ B ↓ p' ]
    → u == w [ B ↓ (! p) ∙ p' ]
  _!◃_ {p = idp} idp q = q

  !◃idp :{x : A} {v w : B x} (q : v == w)
    → q !◃ idp == ! q
  !◃idp idp = idp

  {-
  This is some kind of dependent horizontal composition (used in [apd∙]).
  -}

  infixr 80 _∙2ᵈ_ _∙'2ᵈ_

  _∙2ᵈ_ : {x y z : Π A B}
    {a a' : A} {p : a == a'} {q : x a == y a} {q' : x a' == y a'}
    {r : y a == z a} {r' : y a' == z a'}
    → (q == q'            [ (λ a → x a == y a) ↓ p ])
    → (r == r'            [ (λ a → y a == z a) ↓ p ])
    → (q ∙ r == q' ∙ r' [ (λ a → x a == z a) ↓ p ])
  _∙2ᵈ_ {p = idp} α β = α ∙2 β

  _∙'2ᵈ_ : {x y z : Π A B}
    {a a' : A} {p : a == a'} {q : x a == y a} {q' : x a' == y a'}
    {r : y a == z a} {r' : y a' == z a'}
    → (q == q'            [ (λ a → x a == y a) ↓ p ])
    → (r == r'            [ (λ a → y a == z a) ↓ p ])
    → (q ∙' r == q' ∙' r' [ (λ a → x a == z a) ↓ p ])
  _∙'2ᵈ_ {p = idp} α β = α ∙'2 β

  {-
  [apd∙] reduces a term of the form [apd (λ a → q a ∙ r a) p], do not confuse it
  with [apd-∙] which reduces a term of the form [apd f (p ∙ q)].
  -}

  apd∙ : {a a' : A} {x y z : Π A B}
    (q : (a : A) → x a == y a) (r : (a : A) → y a == z a)
    (p : a == a')
    → apd (λ a → q a ∙ r a) p == apd q p ∙2ᵈ apd r p
  apd∙ q r idp = ! (idp∙2idp (q _) (r _))

  apd∙' : {a a' : A} {x y z : Π A B}
    (q : (a : A) → x a == y a) (r : (a : A) → y a == z a)
    (p : a == a')
    → apd (λ a → q a ∙' r a) p == apd q p ∙'2ᵈ apd r p
  apd∙' q r idp = ! (idp∙'2idp (q _) (r _))

module _ {i j} {A : Type i} {B : A → Type j} where

  {- Exchange -}

  ▹-∙'2ᵈ : {x y z : Π A B}
    {a a' a'' : A} {p : a == a'} {p' : a' == a''}
    {q0 : x a == y a} {q0' : x a' == y a'}
    {r0 : y a == z a} {r0' : y a' == z a'}
    {q0'' : x a'' == y a''} {r0'' : y a'' == z a''}
    (q : q0 == q0' [ (λ a → x a == y a) ↓ p ])
    (r : r0 == r0' [ (λ a → y a == z a) ↓ p ])
    (s : q0' == q0'' [ (λ a → x a == y a) ↓ p' ])
    (t : r0' == r0'' [ (λ a → y a == z a) ↓ p' ])
    → (q ∙'2ᵈ r) ▹ (s ∙'2ᵈ t) == (q ▹ s) ∙'2ᵈ (r ▹ t)
  ▹-∙'2ᵈ {p = idp} {p' = idp} {q0} {.q0} {r0} {.r0} idp idp idp idp =
    ap (λ u → (idp {a = q0} ∙'2 idp {a = r0}) ∙' u) (idp∙'2idp q0 r0)


{-# OPTIONS --type-in-type --no-termination-check
 #-}

module Data where

_-_ : {A : Set}{B : A -> Set}{C : (a : A) -> B a -> Set} ->
      ({a : A}(b : B a) -> C a b) -> (f : (a : A) -> B a) ->
      (a : A) -> C a (f a)
f - g = \ x -> f (g x)

id : {X : Set} -> X -> X
id x = x

konst : {S : Set}{T : S -> Set} -> ({x : S} -> T x) -> (x : S) -> T x
konst c x = c

data Zero : Set where
record One : Set where

record Sig (S : Set)(T : S -> Set) : Set where
  field
    fst : S
    snd : T fst

open module Sig' {S : Set}{T : S -> Set} = Sig {S}{T}

_,_ : forall {S T}(s : S) -> T s -> Sig S T
s , t = record {fst = s; snd = t}

infixr 40 _,_

_*_ : Set -> Set -> Set
S * T = Sig S \ _ -> T

sig : forall {S T}{P : Sig S T -> Set} ->
      ((s : S)(t : T s) -> P (s , t)) -> (st : Sig S T) -> P st
sig f st = f (fst st) (snd st)

data Two : Set where
  tt : Two
  ff : Two

cond : {S : Two -> Set} -> S tt -> S ff -> (b : Two) -> S b
cond t f tt = t
cond t f ff = f

_+_ : Set -> Set -> Set
S + T = Sig Two (cond S T)

[_,_] : {S T : Set}{P : S + T -> Set} ->
        ((s : S) -> P (tt , s)) -> ((t : T) -> P (ff , t)) ->
        (x : S + T) -> P x
[ f , g ] = sig (cond f g)

data PROP : Set where
  Absu : PROP
  Triv : PROP
  _/\_ : PROP -> PROP -> PROP
  All : (S : Set) -> (S -> PROP) -> PROP

Prf : PROP -> Set
Prf Absu = Zero
Prf Triv = One
Prf (P /\ Q) = Prf P * Prf Q
Prf (All S P) = (x : S) -> Prf (P x)

data CON (I : Set) : Set where
  ?? : I -> CON I
  PrfC : PROP -> CON I
  _*C_ : CON I -> CON I -> CON I
  PiC : (S : Set) -> (S -> CON I) -> CON I
  SiC : (S : Set) -> (S -> CON I) -> CON I
  MuC : (O : Set) -> (O -> CON (I + O)) -> O -> CON I
  NuC : (O : Set) -> (O -> CON (I + O)) -> O -> CON I

mutual
  [|_|] : {I : Set} -> CON I -> (I -> Set) -> Set
  [| ?? i |]       X = X i
  [| PrfC P |]     X = Prf P
  [| C *C D |]     X = [| C |] X * [| D |] X
  [| PiC S C |]    X = (s : S) -> [| C s |] X
  [| SiC S C |]    X = Sig S \ s -> [| C s |] X
  [| MuC O C o |]  X = Mu C X o
  [| NuC O C o |]  X = Nu C X o

  data Mu {I O : Set}(C : O -> CON (I + O))(X : I -> Set)(o : O) : Set where
    con : [| C o |] [ X , Mu C X ] -> Mu C X o
  codata Nu {I O : Set}(C : O -> CON (I + O))(X : I -> Set)(o : O) : Set where
    con : [| C o |] [ X , Nu C X ] -> Nu C X o

noc :  {I O : Set}{C : O -> CON (I + O)}{X : I -> Set}{o : O} ->
       Nu C X o -> [| C o |] [ X , Nu C X ]
noc (con xs) = xs

mutual
  map : {I : Set}{X Y : I -> Set} -> (C : CON I) -> ((i : I) -> X i -> Y i) ->
        [| C |] X -> [| C |] Y
  map (?? i)       f = f i
  map (PrfC P)     f = id
  map (C *C D)     f = sig \ c d -> map C f c , map D f d
  map (PiC S C)    f = \ c s -> map (C s) f (c s)
  map (SiC S C)    f = sig \ s c -> s , map (C s) f c
  map (MuC O C o)  f = fold C (Mu C _) (\ o xs -> con (map (C o) [ f , konst id ] xs)) o
  map (NuC O C o)  f = unfold C (Nu C _) (\ o ss -> map (C o) [ f , konst id ] (noc ss)) o

  fold :  {I O : Set}(C : O -> CON (I + O)){X : I -> Set}(Y : O -> Set) ->
          ((o : O) -> [| C o |] [ X , Y ] -> Y o) ->
          (o : O) -> Mu C X o -> Y o
  fold C Y f o (con xs) = f o (map (C o) [ konst id , fold C Y f ] xs)

  unfold :  {I O : Set}(C : O -> CON (I + O)){X : I -> Set}(Y : O -> Set) ->
            ((o : O) -> Y o -> [| C o |] [ X , Y ]) ->
            (o : O) -> Y o -> Nu C X o
  unfold C Y f o y = con (map (C o) [ konst id , unfold C Y f ] (f o y))

induction : {I O : Set}(C : O -> CON (I + O)){X : I -> Set}
            (P : (o : O) -> Mu C X o -> Set) ->
            ((o : O)(xyps : [| C o |] [ X , (\ o -> Sig (Mu C X o) (P o)) ]) ->
             P o (con (map (C o) [ konst id , konst fst ] xyps))) ->
            (o : O)(y : Mu C X o) -> P o y
induction C P p o (con xys) =
  p o (map (C o) [ konst id , (\ o y -> (y , induction C P p o y)) ] xys)

moo : {I O : Set}(C : O -> CON (I + O)){X : I -> Set}(o : O)
      (W : (o : O) -> Mu C X o -> Set)(w : (o : O)(y : Mu C X o) -> W o y)
      (P : (io : I + O) -> [| ?? io |] [ X , Mu C X ] -> Set)
      (io : I + O)(xy : [| ?? io |] [ X , Mu C X ]) -> P io xy ->
      let g : (io : I + O) ->
              [| ?? io |] [ X , Mu C X ] -> [| ?? io |] [ X , (\ o -> Sig (Mu C X o) (W o)) ]
          g = [ konst id , (\ o y -> (y , w o y)) ]
          f : (io : I + O) ->
              [| ?? io |] [ X , (\ o -> Sig (Mu C X o) (W o)) ] -> [| ?? io |] [ X , Mu C X ]
          f = [ konst id , konst fst ]
      in  P io (f io (g io xy))
moo C o W w P io xy p = {!p!}


module dynamic where

open import LSsyntax
open import static
open import Relation.Nullary
open import Data.Nat using (ℕ ; _+_)
open import Data.Fin using (Fin; toℕ)
open import Data.Vec using (Vec ; lookup; _∷_; [])
open import Data.Product
open import Relation.Binary.PropositionalEquality
open import Relation.Nullary
open import Data.Bool using (Bool; true ; false)

data _⊓ˡ_ : (ℓ₁ ℓ₂ : Label) → Set where
    ℓ⊓✭ : ∀ ℓ → ℓ ⊓ˡ ✭
    ✭⊓ℓ : ∀ ℓ → ✭ ⊓ˡ ℓ
    idℓ : ∀ ℓ → ℓ ⊓ˡ ℓ

_⊓ᵣ_ : ∀ (ℓ₁ ℓ₂ : Label) → Dec (ℓ₁ ⊓ˡ ℓ₂)
⊤ ⊓ᵣ ⊤ = yes (idℓ ⊤)
⊤ ⊓ᵣ ⊥ = no (λ ())
⊤ ⊓ᵣ ✭ = yes (ℓ⊓✭ ⊤)
⊥ ⊓ᵣ ⊤ = no (λ ())
⊥ ⊓ᵣ ⊥ = yes (idℓ ⊥)
⊥ ⊓ᵣ ✭ = yes (ℓ⊓✭ ⊥)
✭ ⊓ᵣ ⊤ = yes (✭⊓ℓ ⊤)
✭ ⊓ᵣ ⊥ = yes (✭⊓ℓ ⊥)
✭ ⊓ᵣ ✭ = yes (ℓ⊓✭ ✭)

-- note that this is not actually correct. The meet of ⊤ and bottom for example should be undefined,
-- but that isn't easy to model directly in Agda. However, if we only call this after induction over
-- the proof of _⊓ᵣ_ it (should) be impossible to call one of the incorrect cases.
recMeet : ∀ (ℓ₁ ℓ₂ : Label) → Label
recMeet ⊤ ⊤ = ⊤
recMeet ⊤ ✭ = ⊤
recMeet ⊥ ⊥ = ⊥
recMeet ⊥ ✭ = ⊥
recMeet ✭ ⊤ = ⊤
recMeet ✭ ⊥ = ⊥
recMeet ✭ ✭ = ✭
recMeet _ _ = ✭     -- fake case

_⊓_ : ∀ (t₁ t₂ : GType) → GType
bool x ⊓ bool x₁ with x ⊓ᵣ x₁
bool x ⊓ bool x₁ | yes p = bool (recMeet x x₁)
bool x ⊓ bool x₁ | no ¬p = err
bool x ⊓ _ = err
(t₁ ⇒ x) t₂ ⊓ (t₃ ⇒ x₁) t₄ with x ⊓ᵣ x₁
(t₁ ⇒ x) t₂ ⊓ (t₃ ⇒ x₁) t₄ | yes p with (t₁ ⊓ t₃) | (t₂ ⊓ t₄)
(t₁ ⇒ x₂) t₂ ⊓ (t₃ ⇒ x₃) t₄ | yes p | (bool x) | (bool x₁) = ((bool x) ⇒ (recMeet x₂ x₃)) (bool x)
-- this is probably not correct, but it keeps things simple.
(t₁ ⇒ x₂) t₂ ⊓ (t₃ ⇒ x₃) t₄ | yes p | (bool x) | ((d ⇒ x₁) d₁) = err
(t₁ ⇒ x₂) t₂ ⊓ (t₃ ⇒ x₃) t₄ | yes p | ((c ⇒ x) c₁) | (bool x₁) = err
(t₁ ⇒ x₂) t₂ ⊓ (t₃ ⇒ x₃) t₄ | yes p | ((c ⇒ x) c₁) | ((d ⇒ x₁) d₁) = err
(t₁ ⇒ x₁) t₂ ⊓ (t₃ ⇒ x₂) t₄ | yes p | (bool x) | err = err
(t₁ ⇒ x₁) t₂ ⊓ (t₃ ⇒ x₂) t₄ | yes p | ((c ⇒ x) c₁) | err = err
(t₁ ⇒ x₁) t₂ ⊓ (t₃ ⇒ x₂) t₄ | yes p | err | (bool x) = err
(t₁ ⇒ x₁) t₂ ⊓ (t₃ ⇒ x₂) t₄ | yes p | err | ((d ⇒ x) d₁) = err
(t₁ ⇒ x) t₂ ⊓ (t₃ ⇒ x₁) t₄ | yes p | err | err = err
(t₁ ⇒ x) t₂ ⊓ (t₃ ⇒ x₁) t₄ | no ¬p = err
_ ⊓ _ = err

I≼ : ∀ (ℓ₁ ℓ₂ : Label) → (Label × Label)
I≼ ⊤ ✭   = ⊤ , ⊤
I≼ ✭ ⊤   = ✭ , ⊤
I≼ ✭ ⊥   = ⊥ , ⊥
I≼ ⊥ ✭   = ⊥ , ✭
I≼ ℓ₁ ℓ₂ = ℓ₁ , ℓ₂

Δ≼ : ∀ (triple : Label × Label × Label) → (Label × Label)
Δ≼ (ℓ₁ , ⊤ , ⊤) = ℓ₁ , ⊤
Δ≼ (ℓ₁ , ⊤ , ✭) = ℓ₁ , ⊤
Δ≼ (⊥ , ⊥ , ℓ₃) = ⊥ , ℓ₃
Δ≼ (⊥ , ✭ , ℓ₃) = ⊥ , ℓ₃
Δ≼ (ℓ₁ , ✭ , ℓ₃) = ℓ₁ , ℓ₃
Δ≼ (ℓ₁ , ℓ₂ , ℓ₃) = ℓ₁ , ℓ₃

Δ< : ∀ (triple : GType × GType × GType) → (GType × GType)
Δ< (bool ℓ₁ , bool ℓ₂ , bool ℓ₃) =
    let new = Δ≼ (ℓ₁ , ℓ₂ , ℓ₃) in
    bool (proj₁ new) , bool (proj₂ new)
Δ< ((t₁ ⇒ ℓ₁) t′₁ , (t₂ ⇒ ℓ₂) t′₂ , (t₃ ⇒ ℓ₃) t′₃) =
    let new = Δ≼ (ℓ₁ , ℓ₂ , ℓ₃) in
    ((t₁ ⇒ (proj₁ new)) t′₁) , ((t₃ ⇒ (proj₂ new)) t′₃)
Δ< (_ , _ , _) = err , err

interior : ∀ (t : GType) → (GType × GType)
interior (bool ℓ) =  (bool ℓ) , (bool ℓ)
interior ((t ⇒ ℓ) t₁) =
    let (ℓ₁ , ℓ₂) = I≼ (getLabel t) ℓ in
    ((setLabel t ℓ₁) ⇒ ℓ₂) t₁ , ((setLabel t ℓ₁) ⇒ ℓ₂) t₁
interior err = err , err

_∘<_ : ∀ (t₁ t₂ : (GType × GType)) → (GType × GType)
(s₁ , s₂₁) ∘< (s₂₂ , s₃) = Δ< (s₁ , (s₂₁ ⊓ s₂₂) , s₃)

dynamicCheck : ∀ {n} (Γ : Ctx n) (t : Term) → Check Γ t
-- variables
dynamicCheck {n} Γ (var v) with fromℕ n v
dynamicCheck {n} Γ (var .(toℕ m)) | yes m = yes (lookup m Γ) (Sx m refl)
dynamicCheck {n} Γ (var .(n + m)) | no m = no

-- literals
dynamicCheck Γ (litBool x ℓ) = yes (bool ℓ) (Sb x ℓ)

-- lambda abstraction
dynamicCheck Γ (lam x t x₁) with dynamicCheck (x ∷ Γ) t
dynamicCheck Γ (lam x .(erase t) ℓ) | yes τ t = yes ((x ⇒ ℓ) τ) (Sλ x ℓ t)
dynamicCheck Γ (lam x t x₁) | no = no

-- logical and
dynamicCheck Γ (t ∧ t₁) with dynamicCheck Γ t | dynamicCheck Γ t₁
dynamicCheck Γ (.(erase t) ∧ .(erase t₁)) | yes τ t | (yes τ₁ t₁) with (interior τ) ∘< (interior τ₁)
dynamicCheck Γ (.(erase t) ∧ .(erase t₁)) | yes τ t | (yes τ₁ t₁) | (bool x , bool x₁) = yes (bool (getLabel τ ~⋎~ getLabel τ₁)) (t S∧ t₁)
dynamicCheck Γ (.(erase t) ∧ .(erase t₁)) | yes τ t | (yes τ₁ t₁) | (_ , _) = no
dynamicCheck Γ (t ∧ t₁) | _ | _ = no

-- logical or
dynamicCheck Γ (t ∨ t₁) with dynamicCheck Γ t | dynamicCheck Γ t₁
dynamicCheck Γ (.(erase t) ∨ .(erase t₁)) | yes τ t | (yes τ₁ t₁) with (interior τ) ∘< (interior τ₁)
dynamicCheck Γ (.(erase t) ∨ .(erase t₁)) | yes τ t | (yes τ₁ t₁) | (bool x , bool x₁) = yes (bool (getLabel τ ~⋎~ getLabel τ₁)) (t S∨ t₁)
dynamicCheck Γ (.(erase t) ∨ .(erase t₁)) | yes τ t | (yes τ₁ t₁) | (_ , _) = no
dynamicCheck Γ (t ∨ t₁) | _ | _ = no

-- application
-- this needs to be doublechecked!
dynamicCheck Γ (t ∙ t₁) with dynamicCheck Γ t | dynamicCheck Γ t₁
dynamicCheck Γ (.(erase t) ∙ .(erase t₁)) | yes ((τ ⇒ ℓ₁) τ₁) t | yes τ₂ t₁ with (interior ((τ ⇒ ℓ₁) τ₁)) ∘< (interior τ₂)
dynamicCheck Γ (.(erase t) ∙ .(erase t₁)) | yes ((τ ⇒ ℓ₁) τ₁) t | (yes τ₂ t₁) | (bool x , bool x₁) = yes (bool (getLabel τ₁ ~⋎~ ℓ₁)) (S∙ t t₁ (yes τ₂ τ))
dynamicCheck Γ (.(erase t) ∙ .(erase t₁)) | yes ((τ ⇒ ℓ₁) τ₁) t | (yes τ₂ t₁) | (bool x , (proj₄ ⇒ x₁) proj₅) = no
dynamicCheck Γ (.(erase t) ∙ .(erase t₁)) | yes ((τ ⇒ ℓ₁) τ₁) t | (yes τ₂ t₁) | ((proj₃ ⇒ x) proj₄ , bool x₁) = no
dynamicCheck Γ (.(erase t) ∙ .(erase t₁)) | yes ((τ ⇒ ℓ₁) τ₁) t | (yes τ₂ t₁) | ((proj₃ ⇒ x) proj₄ , (proj₅ ⇒ x₁) proj₆) = no
dynamicCheck Γ (.(erase t) ∙ .(erase t₁)) | yes ((τ ⇒ ℓ₁) τ₁) t | (yes τ₂ t₁) | (_ , _) = no
dynamicCheck Γ (.(erase t) ∙ .(erase t₁)) | yes _ t | (yes _ t₁) = no
dynamicCheck Γ (.(erase t) ∙ t₁) | yes τ t | no = no
dynamicCheck Γ (t₁ ∙ .(erase t)) | no | yes τ t = no
dynamicCheck Γ (t ∙ t₁) | no | no = no

-- if then else
dynamicCheck Γ (if b then t₁ else t₂) with dynamicCheck Γ b
dynamicCheck Γ (if .(erase b) then t₁ else t₂) | yes τ b with dynamicCheck Γ t₁ | dynamicCheck Γ t₂
dynamicCheck Γ (if .(erase b) then .(erase t₁) else .(erase t₂)) | yes τ b | (yes τ₁ t₁) | (yes τ₂ t₂) with (interior τ₁) ∘< (interior τ₂)
dynamicCheck Γ (if .(erase b) then .(erase t₁) else .(erase t₂)) | yes τ b | (yes τ₁ t₁) | (yes τ₂ t₂) | (bool ℓ , bool ℓ₁) = yes (bool (getLabel (τ₁ :∨: τ₂) ~⋎~ getLabel τ)) (Sif b t₁ t₂)
dynamicCheck Γ (if .(erase b) then .(erase t₁) else .(erase t₂)) | yes τ b | (yes τ₁ t₁) | (yes τ₂ t₂) | (_ , _) = no
dynamicCheck Γ (if .(erase b) then t₁ else t₂) | yes τ b | _ | _ = no
dynamicCheck Γ (if b then t₁ else t₂) | no = no

-- error
dynamicCheck Γ error = no


module trie where

open import bool
open import char
open import list
open import maybe
open import product
open import string
open import unit

cal : Set → Set
cal A = 𝕃 (char × A)

empty-cal : ∀{A : Set} → cal A
empty-cal = []

cal-lookup : ∀ {A : Set} → cal A → char → maybe A
cal-lookup [] _ = nothing
cal-lookup ((c , a) :: l) c' with c =char c'
... | tt = just a
... | ff = cal-lookup l c'

cal-insert : ∀ {A : Set} → cal A → char → A → cal A
cal-insert [] c a = (c , a) :: []
cal-insert ((c' , a') :: l) c a with c =char c'
... | tt = (c , a) :: l
... | ff = (c' , a') :: (cal-insert l c a)

cal-remove : ∀ {A : Set} → cal A → char → cal A
cal-remove [] _ = []
cal-remove ((c , a) :: l) c' with c =char c'
... | tt = cal-remove l c'
... | ff = (c , a) :: cal-remove l c'

cal-add : ∀{A : Set} → cal A → char → A → cal A
cal-add l c a = (c , a) :: l

test-cal-insert = cal-insert (('a' , 1) :: ('b' , 2) :: []) 'b' 20

data trie (A : Set) : Set where
  Node : maybe A → cal (trie A) → trie A

empty-trie : ∀{A : Set} → trie A
empty-trie = (Node nothing empty-cal)

trie-lookup-h : ∀{A : Set} → trie A → 𝕃 char → maybe A
trie-lookup-h (Node odata ts) (c :: cs) with cal-lookup ts c
trie-lookup-h (Node odata ts) (c :: cs) | nothing = nothing
trie-lookup-h (Node odata ts) (c :: cs) | just t = trie-lookup-h t cs
trie-lookup-h (Node odata ts) [] = odata

trie-lookup : ∀{A : Set} → trie A → string → maybe A
trie-lookup t s = trie-lookup-h t (string-to-𝕃char s)

trie-contains : ∀{A : Set} → trie A → string → 𝔹
trie-contains t s with trie-lookup t s
trie-contains t s | nothing = ff
trie-contains t s | just _ = tt

trie-insert-h : ∀{A : Set} → trie A → 𝕃 char → A → trie A
trie-insert-h (Node odata ts) [] x = (Node (just x) ts)
trie-insert-h (Node odata ts) (c :: cs) x with cal-lookup ts c
trie-insert-h (Node odata ts) (c :: cs) x | just t =
  (Node odata (cal-insert ts c (trie-insert-h t cs x)))
trie-insert-h (Node odata ts) (c :: cs) x | nothing =
  (Node odata (cal-add ts c (trie-insert-h empty-trie cs x)))

trie-insert : ∀{A : Set} → trie A → string → A → trie A
trie-insert t s x = trie-insert-h t (string-to-𝕃char s) x

trie-remove-h : ∀{A : Set} → trie A → 𝕃 char → trie A
trie-remove-h (Node odata ts) (c :: cs) with cal-lookup ts c
trie-remove-h (Node odata ts) (c :: cs) | nothing = Node odata ts
trie-remove-h (Node odata ts) (c :: cs) | just t = Node odata (cal-insert ts c (trie-remove-h t cs))
trie-remove-h (Node odata ts) [] = Node nothing ts

trie-remove : ∀{A : Set} → trie A → string → trie A
trie-remove t s = trie-remove-h t (string-to-𝕃char s)

trie-map : ∀{A B : Set} → (A → B) → trie A → trie B
trie-cal-map : ∀{A B : Set} → (A → B) → cal (trie A) → cal (trie B)
trie-map f (Node x x₁) = Node (maybe-map f x) (trie-cal-map f x₁)
trie-cal-map f [] = []
trie-cal-map f ((c , t) :: cs) =
  (c , trie-map f t) :: trie-cal-map f cs

trie-to-string-h : ∀{A : Set} → string → (A → string) → trie A → 𝕃 char → string
trie-cal-to-string-h : ∀{A : Set} → string → (A → string) → cal (trie A) → 𝕃 char → string
trie-to-string-h sep d (Node (just x) c) prev-str =
  (𝕃char-to-string (reverse prev-str)) ^ sep ^ (d x) ^ "\n" ^ (trie-cal-to-string-h sep d c prev-str)
trie-to-string-h sep d (Node nothing c) prev-str = trie-cal-to-string-h sep d c prev-str
trie-cal-to-string-h sep d [] prev-str = ""
trie-cal-to-string-h sep d ((c , t) :: cs) prev-str =
  (trie-to-string-h sep d t (c :: prev-str)) ^ (trie-cal-to-string-h sep d cs prev-str)

{- trie-to-string sep d t returns a string representation of the trie t,
   where each mapping from string s to data x is printed as
     s sep d x
   where sep is a string and d returns a string for any element A of the trie. -}
trie-to-string : ∀{A : Set} → string → (A → string) → trie A → string
trie-to-string sep d t = trie-to-string-h sep d t []

trie-mappings-h : ∀{A : Set} → trie A → 𝕃 char → 𝕃 (string × A)
trie-cal-mappings-h : ∀{A : Set} → cal (trie A) → 𝕃 char → 𝕃 (string × A)
trie-mappings-h (Node (just x) c) prev-str = (𝕃char-to-string (reverse prev-str) , x) :: (trie-cal-mappings-h c prev-str)
trie-mappings-h (Node nothing c) prev-str = (trie-cal-mappings-h c prev-str)
trie-cal-mappings-h [] prev-str = []
trie-cal-mappings-h ((c , t) :: cs) prev-str = trie-mappings-h t (c :: prev-str) ++ (trie-cal-mappings-h cs prev-str)

trie-mappings : ∀{A : Set} → trie A → 𝕃 (string × A)
trie-mappings t = trie-mappings-h t []

-- return a list of all the strings which have associated data in the trie
trie-strings : ∀{A : Set} → trie A → 𝕃 string
trie-strings t = map fst (trie-mappings t)

trie-nonempty : ∀{A : Set} → trie A → 𝔹
trie-cal-nonempty : ∀{A : Set} → cal (trie A) → 𝔹
trie-nonempty (Node (just x) t) = tt
trie-nonempty (Node nothing c) = trie-cal-nonempty c
trie-cal-nonempty [] = ff
trie-cal-nonempty ((a , t) :: c) = trie-nonempty t || trie-cal-nonempty c

----------------------------------------------------------------------
-- stringset
----------------------------------------------------------------------

stringset : Set
stringset = trie ⊤

stringset-contains : stringset → string → 𝔹
stringset-contains ss s = trie-contains ss s

stringset-insert : stringset → string → stringset
stringset-insert ss s = trie-insert ss s triv

stringset-remove : stringset → string → stringset
stringset-remove ss s = trie-remove ss s

stringset-insert𝕃 : stringset → 𝕃 char → stringset
stringset-insert𝕃 ss s = trie-insert-h ss s triv

empty-stringset : stringset
empty-stringset = empty-trie

stringset-insert* : stringset → 𝕃 string → stringset
stringset-insert* s [] = s
stringset-insert* s (x :: xs) = stringset-insert (stringset-insert* s xs) x

stringset-strings : ∀{A : Set} → trie A → 𝕃 string
stringset-strings t = map fst (trie-mappings t)


open import Categories
open import Monads

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

open import Library
open import Functors
open import Naturals hiding (Iso; module Iso)
open import Adjunctions
open import Monads.CatofAdj M
open import Categories.Initial
open import Monads.Kleisli M
open import Monads.Kleisli.Functors M
open import Monads.Kleisli.Adjunction M
open import Adjunctions.Adj2Mon

open Cat
open Fun
open Monad M
open NatT
open Adj

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

KlObj : Obj CatofAdj
KlObj = record {
  D   = Kl;
  adj = KlAdj;
  law = lemX;
  ηlaw = refl;
  bindlaw = λ{X}{Y}{f} →
    cong bind
         (stripsubst (Hom C X) f (fcong Y (cong OMap (sym lemX))))}

open ObjAdj
open import Isomorphism
open Iso

lemZ : {D : Cat {a}{b}}{F G G' : Fun C D} →
       G ≅ G' → {α : NatT F G}{β : NatT F G'} → α ≅ β →
       ∀ {X} → cmp α {X} ≅ cmp β {X}
lemZ refl refl = refl

lemZ' : {D : Cat {a}{b}}{F F' G G' : Fun C D} →
        F ≅ F' → G ≅ G' → {α : NatT F G}{β : NatT F' G'} → α ≅ β →
        ∀ {X} → cmp α {X} ≅ cmp β {X}
lemZ' refl refl refl = refl

lemfid : {D : Cat {a}{b}}(L : Fun C D)(R : Fun D C)(T : Fun C C)
  (p : T ≅ R ○ L) (η : ∀ {X} → Hom C X (OMap T X))
  (right : {X : Obj C}{Y : Obj D} → Hom C X (OMap R Y) → Hom D (OMap L X) Y) →
  (left : {X : Obj C}{Y : Obj D} → Hom D (OMap L X) Y → Hom C X (OMap R Y)) →
  (q :  {X : Obj C}{Y : Obj D}(f : Hom D (OMap L X) Y) → right (left f) ≅ f) →
  (r : {X : Obj C} → left (iden D {OMap L X}) ≅ η {X}) →
  {X : Obj C} →
    right (subst (Hom C X) (fcong X (cong OMap p)) η)
    ≅
    iden D {OMap L X}
lemfid {D = D} L R .(R ○ L) refl η right left q r {X} =
  trans (cong right (sym r)) (q (iden D))

lemfcomp : {D : Cat {a}{b}}(L : Fun C D)(R : Fun D C)(T : Fun C C)
  (p : T ≅ R ○ L)
  (bind : ∀{X Y} → Hom C X (OMap T Y) → Hom C (OMap T X) (OMap T Y)) →
  (right : {X : Obj C} {Y : Obj D} → Hom C X (OMap R Y) → Hom D (OMap L X) Y) →
  (q : {X Y : Obj C} {f : Hom C X (OMap T Y)} →
       HMap R (right (subst (Hom C X) (fcong Y (cong OMap p)) f)) ≅ bind f) →
  (r : {X X' : Obj C}{Y Y' : Obj D}
       (f : Hom C X' X)(g : Hom D Y Y')
       (h : Hom C X (OMap R Y)) →
       right (comp C (HMap R g) (comp C h f))
       ≅
       comp D g (comp D (right h) (HMap L f))) →
  ∀{X Y Z : Obj C} {f : Hom C Y (OMap T Z)} {g : Hom C X (OMap T Y)} →
    right (subst (Hom C X) (fcong Z (cong OMap p)) (comp C (bind f) g))
    ≅
    comp D (right (subst (Hom C Y) (fcong Z (cong OMap p)) f))
           (right (subst (Hom C X) (fcong Y (cong OMap p)) g))
lemfcomp {D = D} L R .(R ○ L) refl bind right q r {X}{Y}{Z}{f}{g} =
  trans (cong₂ (λ f g → right (comp C f g)) (sym (q {f = f})) (sym (idr C)))
        (trans (r (iden C) (right f) g)
               (cong (comp D (right f))
                     (trans (cong (comp D (right g)) (fid L)) (idr D))))

lemLlaw : {D : Cat {a}{b}}(L : Fun C D)(R : Fun D C)(T : Fun C C)
          (p : T ≅ R ○ L) (η : ∀ {X} → Hom C X (OMap T X)) →
          (right : {X : Obj C} {Y : Obj D} →
                   Hom C X (OMap R Y) → Hom D (OMap L X) Y) →
          (left : {X : Obj C} {Y : Obj D} →
                  Hom D (OMap L X) Y → Hom C X (OMap R Y)) →
          (q : {X : Obj C} {Y : Obj D} (f : Hom D (OMap L X) Y) →
               right (left f) ≅ f) →
          (r : {X : Obj C} → left (iden D {OMap L X}) ≅ η {X}) →
          (t : {X X' : Obj C}{Y Y' : Obj D}
                   (f : Hom C X' X)(g : Hom D Y Y')
                   (h : Hom C X (OMap R Y)) →
                   right (comp C (HMap R g) (comp C h f))
                   ≅
                   comp D g (comp D (right h) (HMap L f)) ) →
          {X Y : Obj C} (f : Hom C X Y) →
          right (subst (Hom C X) (fcong Y (cong OMap p))  (comp C η f))
          ≅
          HMap L f
lemLlaw {D = D} L R .(R ○ L) refl η right left q r t {X}{Y} f =
  trans (cong (λ g → right (comp C g f)) (sym r))
        (trans (trans (trans (cong right
                                   (trans (sym (idl C))
                                          (cong (λ g → comp C g
                                                   (comp C (left (iden D)) f))
                                                (sym (fid R)))))
                             (t f (iden D) (left (iden D))))
                      (cong (comp D (iden D))
                            (trans (cong (λ g → comp D g (HMap L f))
                                         (q (iden D)))
                                   (idl D))))
               (idl D))

K' : (A : Obj CatofAdj) → Fun (D KlObj) (D A)
K' A = record {
                 OMap = OMap (L (adj A));
                 HMap =
                   λ {X} {Y} f →
                     right (adj A)
                     (subst (Hom C X) (fcong Y (cong OMap (sym (law A)))) f);
                 fid =
                   lemfid (L (adj A)) (R (adj A)) (TFun M) (sym (law A)) η
                   (right (adj A)) (left (adj A)) (lawa (adj A)) (ηlaw A);
                 fcomp =
                   lemfcomp (L (adj A))
                            (R (adj A))
                            (TFun M)
                            (sym (law A))
                            bind
                            (right (adj A))
                            (bindlaw A)
                            (natright (adj A)) }

Llaw' : (A : Obj CatofAdj) → K' A ○ L (adj KlObj) ≅ L (adj A)
Llaw' A = FunctorEq
  _
  _
  refl
  (iext λ _ → iext λ _ → ext $ lemLlaw
    (L (adj A))
    (R (adj A))
    (TFun M)
    (sym (law A))
    η
    (right (adj A))
    (left (adj A))
    (lawa (adj A))
    (ηlaw A)
    (natright (adj A)))

Rlaw' : (A : Obj CatofAdj) → R (adj KlObj) ≅ R (adj A) ○ K' A
Rlaw' A = FunctorEq
  _
  _
  (cong OMap (sym (law A)))
             (iext λ _ → iext λ _ → ext λ _ → sym (bindlaw A))

rightlaw' : (A : Obj CatofAdj) →
            {X : Obj C} {Y : Obj (D KlObj)}
            {f : Hom C X (OMap (R (adj KlObj)) Y)} →
            HMap (K' A) (right (adj KlObj) f) ≅
            right (adj A) (subst (Hom C X) (fcong Y (cong OMap (Rlaw' A))) f)
rightlaw' A {X}{Y}{f} =
  cong (right (adj A))
       (trans (stripsubst (Hom C X) f (fcong Y (cong OMap (sym (law A)))))
              (sym (stripsubst (Hom C X) f (fcong Y (cong OMap (Rlaw' A))))))

KlHom : {A : Obj CatofAdj} → Hom CatofAdj KlObj A
KlHom {A} = record {
  K        = K' A;
  Llaw     = Llaw' A;
  Rlaw     = Rlaw' A;
  rightlaw = rightlaw' A }

uniq : {X : Obj CatofAdj}{f : Hom CatofAdj KlObj X} → KlHom ≅ f
uniq {X}{V} = HomAdjEq
  _
  _
  (FunctorEq
    _
    _
    (cong OMap (sym (HomAdj.Llaw V)))
    (iext λ A → iext λ B → ext λ f →
                          trans (cong₂ (λ B₁ → right (adj X) {A} {B₁})
                                (sym (fcong B (cong OMap (HomAdj.Llaw V))))
                                (trans (stripsubst
                                         (Hom C A)
                                         f
                                         (fcong B (cong OMap (sym (law X)))))
                                       (sym (stripsubst
                                              (Hom C A)
                                              f
                                              (fcong
                                                B
                                                (cong OMap (HomAdj.Rlaw V)))))))
                         (sym (HomAdj.rightlaw V))))

KlIsInit : Init CatofAdj KlObj
KlIsInit = record {
  i   = KlHom;
  law = uniq}
-- -}


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

open import lib.Base
open import lib.PathGroupoid
open import lib.PathOver
open import lib.cubical.Square

module lib.cubical.SquareOver where

SquareOver : ∀ {i j} {A : Type i} (B : A → Type j) {a₀₀ a₀₁ a₁₀ a₁₁ : A}
  {p₀₋ : a₀₀ == a₀₁} {p₋₀ : a₀₀ == a₁₀} {p₋₁ : a₀₁ == a₁₁} {p₁₋ : a₁₀ == a₁₁}
  (sq : Square p₀₋ p₋₀ p₋₁ p₁₋)
  {b₀₀ : B a₀₀} {b₀₁ : B a₀₁} {b₁₀ : B a₁₀} {b₁₁ : B a₁₁}
  (q₀₋ : b₀₀ == b₀₁ [ B ↓ p₀₋ ]) (q₋₀ : b₀₀ == b₁₀ [ B ↓ p₋₀ ])
  (q₋₁ : b₀₁ == b₁₁ [ B ↓ p₋₁ ]) (q₁₋ : b₁₀ == b₁₁ [ B ↓ p₁₋ ])
  → Type j
SquareOver B ids = Square

apd-square : ∀ {i j} {A : Type i} {B : A → Type j} (f : Π A B)
  {a₀₀ a₀₁ a₁₀ a₁₁ : A}
  {p₀₋ : a₀₀ == a₀₁} {p₋₀ : a₀₀ == a₁₀} {p₋₁ : a₀₁ == a₁₁} {p₁₋ : a₁₀ == a₁₁}
  (sq : Square p₀₋ p₋₀ p₋₁ p₁₋)
  → SquareOver B sq (apd f p₀₋) (apd f p₋₀) (apd f p₋₁) (apd f p₁₋)
apd-square f ids = ids

↓-=-to-squareover : ∀ {i j} {A : Type i} {B : A → Type j}
  {f g : Π A B} {x y : A} {p : x == y}
  {u : f x == g x} {v : f y == g y}
  → u == v [ (λ z → f z == g z) ↓ p ]
  → SquareOver B vid-square u (apd f p) (apd g p) v
↓-=-to-squareover {p = idp} idp = hid-square

↓-=-from-squareover : ∀ {i j} {A : Type i} {B : A → Type j}
  {f g : Π A B} {x y : A} {p : x == y}
  {u : f x == g x} {v : f y == g y}
  → SquareOver B vid-square u (apd f p) (apd g p) v
  → u == v [ (λ z → f z == g z) ↓ p ]
↓-=-from-squareover {p = idp} sq = horiz-degen-path sq

squareover-cst-in : ∀ {i j} {A : Type i} {B : Type j} {a₀₀ a₀₁ a₁₀ a₁₁ : A}
  {p₀₋ : a₀₀ == a₀₁} {p₋₀ : a₀₀ == a₁₀} {p₋₁ : a₀₁ == a₁₁} {p₁₋ : a₁₀ == a₁₁}
  {sq : Square p₀₋ p₋₀ p₋₁ p₁₋}
  {b₀₀ b₀₁ b₁₀ b₁₁ : B}
  {q₀₋ : b₀₀ == b₀₁} {q₋₀ : b₀₀ == b₁₀} {q₋₁ : b₀₁ == b₁₁} {q₁₋ : b₁₀ == b₁₁}
  (sq' : Square q₀₋ q₋₀ q₋₁ q₁₋)
  → SquareOver (λ _ → B) sq
      (↓-cst-in q₀₋) (↓-cst-in q₋₀) (↓-cst-in q₋₁) (↓-cst-in q₁₋)
squareover-cst-in {sq = ids} sq' = sq'

↓↓-from-squareover : ∀ {i j k} {A : Type i} {B : A → Type j} {C : Type k}
  {x y : C → A} {u : (c : C) → B (x c)} {v : (c : C) → B (y c)}
  {p : (c : C) → x c == y c} {c₁ c₂ : C} {q : c₁ == c₂}
  {α : u c₁ == v c₁ [ B ↓ p c₁ ]} {β : u c₂ == v c₂ [ B ↓ p c₂ ]}
  → SquareOver B (natural-square p q)
               α (↓-ap-in _ _ (apd u q)) (↓-ap-in _ _ (apd v q)) β
  → α == β [ (λ c → u c == v c [ B ↓ p c ]) ↓ q ]
↓↓-from-squareover {q = idp} sq = lemma sq
  where
  lemma : ∀ {i j} {A : Type i} {B : A → Type j}
    {x y : A} {u : B x} {v : B y} {p : x == y}
    {α β : u == v [ B ↓ p ]}
    → SquareOver B hid-square α idp idp β
    → α == β
  lemma {p = idp} sq = horiz-degen-path sq

↓↓-to-squareover : ∀ {i j k} {A : Type i} {B : A → Type j} {C : Type k}
  {x y : C → A} {u : (c : C) → B (x c)} {v : (c : C) → B (y c)}
  {p : (c : C) → x c == y c} {c₁ c₂ : C} {q : c₁ == c₂}
  {α : u c₁ == v c₁ [ B ↓ p c₁ ]} {β : u c₂ == v c₂ [ B ↓ p c₂ ]}
  → α == β [ (λ c → u c == v c [ B ↓ p c ]) ↓ q ]
  → SquareOver B (natural-square p q)
               α (↓-ap-in _ _ (apd u q)) (↓-ap-in _ _ (apd v q)) β
↓↓-to-squareover {q = idp} r = lemma r
  where
  lemma : ∀ {i j} {A : Type i} {B : A → Type j}
    {x y : A} {u : B x} {v : B y} {p : x == y}
    {α β : u == v [ B ↓ p ]}
    → α == β
    → SquareOver B hid-square α idp idp β
  lemma {p = idp} r = horiz-degen-square r

infixr 80 _∙v↓⊡_ _∙h↓⊡_ _↓⊡v∙_ _↓⊡h∙_

_∙h↓⊡_ : ∀ {i j} {A : Type i} {B : A → Type j} {a₀₀ a₀₁ a₁₀ a₁₁ : A}
  {p₀₋ : a₀₀ == a₀₁} {p₋₀ : a₀₀ == a₁₀} {p₋₁ : a₀₁ == a₁₁} {p₁₋ : a₁₀ == a₁₁}
  {sq : Square p₀₋ p₋₀ p₋₁ p₁₋}
  {b₀₀ : B a₀₀} {b₀₁ : B a₀₁} {b₁₀ : B a₁₀} {b₁₁ : B a₁₁}
  {q₀₋ q₀₋' : b₀₀ == b₀₁ [ B ↓ p₀₋ ]} {q₋₀ : b₀₀ == b₁₀ [ B ↓ p₋₀ ]}
  {q₋₁ : b₀₁ == b₁₁ [ B ↓ p₋₁ ]} {q₁₋ : b₁₀ == b₁₁ [ B ↓ p₁₋ ]}
  → q₀₋ == q₀₋'
  → SquareOver B sq q₀₋' q₋₀ q₋₁ q₁₋
  → SquareOver B sq q₀₋ q₋₀ q₋₁ q₁₋
_∙h↓⊡_ {sq = ids} = _∙h⊡_

_∙v↓⊡_ : ∀ {i j} {A : Type i} {B : A → Type j} {a₀₀ a₀₁ a₁₀ a₁₁ : A}
  {p₀₋ : a₀₀ == a₀₁} {p₋₀ : a₀₀ == a₁₀} {p₋₁ : a₀₁ == a₁₁} {p₁₋ : a₁₀ == a₁₁}
  {sq : Square p₀₋ p₋₀ p₋₁ p₁₋}
  {b₀₀ : B a₀₀} {b₀₁ : B a₀₁} {b₁₀ : B a₁₀} {b₁₁ : B a₁₁}
  {q₀₋ : b₀₀ == b₀₁ [ B ↓ p₀₋ ]} {q₋₀ q₋₀' : b₀₀ == b₁₀ [ B ↓ p₋₀ ]}
  {q₋₁ : b₀₁ == b₁₁ [ B ↓ p₋₁ ]} {q₁₋ : b₁₀ == b₁₁ [ B ↓ p₁₋ ]}
  → q₋₀ == q₋₀'
  → SquareOver B sq q₀₋ q₋₀' q₋₁ q₁₋
  → SquareOver B sq q₀₋ q₋₀ q₋₁ q₁₋
_∙v↓⊡_ {sq = ids} = _∙v⊡_

_↓⊡v∙_ : ∀ {i j} {A : Type i} {B : A → Type j} {a₀₀ a₀₁ a₁₀ a₁₁ : A}
  {p₀₋ : a₀₀ == a₀₁} {p₋₀ : a₀₀ == a₁₀} {p₋₁ : a₀₁ == a₁₁} {p₁₋ : a₁₀ == a₁₁}
  {sq : Square p₀₋ p₋₀ p₋₁ p₁₋}
  {b₀₀ : B a₀₀} {b₀₁ : B a₀₁} {b₁₀ : B a₁₀} {b₁₁ : B a₁₁}
  {q₀₋ : b₀₀ == b₀₁ [ B ↓ p₀₋ ]} {q₋₀ : b₀₀ == b₁₀ [ B ↓ p₋₀ ]}
  {q₋₁ q₋₁' : b₀₁ == b₁₁ [ B ↓ p₋₁ ]} {q₁₋ : b₁₀ == b₁₁ [ B ↓ p₁₋ ]}
  → SquareOver B sq q₀₋ q₋₀ q₋₁ q₁₋
  → q₋₁ == q₋₁'
  → SquareOver B sq q₀₋ q₋₀ q₋₁' q₁₋
_↓⊡v∙_ {sq = ids} = _⊡v∙_

_↓⊡h∙_ : ∀ {i j} {A : Type i} {B : A → Type j} {a₀₀ a₀₁ a₁₀ a₁₁ : A}
  {p₀₋ : a₀₀ == a₀₁} {p₋₀ : a₀₀ == a₁₀} {p₋₁ : a₀₁ == a₁₁} {p₁₋ : a₁₀ == a₁₁}
  {sq : Square p₀₋ p₋₀ p₋₁ p₁₋}
  {b₀₀ : B a₀₀} {b₀₁ : B a₀₁} {b₁₀ : B a₁₀} {b₁₁ : B a₁₁}
  {q₀₋ : b₀₀ == b₀₁ [ B ↓ p₀₋ ]} {q₋₀ : b₀₀ == b₁₀ [ B ↓ p₋₀ ]}
  {q₋₁ : b₀₁ == b₁₁ [ B ↓ p₋₁ ]} {q₁₋ q₁₋' : b₁₀ == b₁₁ [ B ↓ p₁₋ ]}
  → SquareOver B sq q₀₋ q₋₀ q₋₁ q₁₋
  → q₁₋ == q₁₋'
  → SquareOver B sq q₀₋ q₋₀ q₋₁ q₁₋'
_↓⊡h∙_ {sq = ids} = _⊡h∙_