------------------------------------------------------------------------
-- The "circle"
------------------------------------------------------------------------

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

-- Partly following the HoTT book.

-- The module is parametrised by a notion of equality. The higher
-- constructor of the HIT defining the circle uses path equality, but
-- the supplied notion of equality is used for many other things.

import Equality.Path as P

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

open P.Derived-definitions-and-properties eq hiding (elim)

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

open import Bijection equality-with-J as Bijection using (_↔_)
import Bijection P.equality-with-J as PB
open import Equality.Groupoid equality-with-J
open import Equality.Path.Isomorphisms eq
open import Equality.Path.Isomorphisms.Univalence eq
import Equality.Path.Isomorphisms P.equality-with-paths as PI
open import Equality.Tactic equality-with-J hiding (module Eq)
open import Equivalence equality-with-J as Eq using (_≃_)
import Equivalence P.equality-with-J as PE
import Erased.Cubical eq as E
open import Function-universe equality-with-J as F hiding (id; _∘_)
open import Group equality-with-J as G using (_≃ᴳ_)
import Group.Cyclic eq as C
open import Groupoid equality-with-J
open import H-level equality-with-J as H-level
open import H-level.Closure equality-with-J
open import H-level.Truncation eq as T using (∥_∥[1+_])
open import H-level.Truncation.Propositional eq as Trunc
  using (∥_∥; ∣_∣)
open import H-level.Truncation.Propositional.One-step eq as O
  using (∥_∥¹)
open import Integer equality-with-J as Int
  using (ℤ; +_; -[1+_]; ℤ-group)
open import Nat equality-with-J
open import Pointed-type equality-with-J as PT using (_≃ᴮ_)
open import Pointed-type.Homotopy-group eq
open import Sphere eq as Sphere using (𝕊)
open import Suspension eq as Suspension
  using (Susp; north; south; meridian)
open import Univalence-axiom equality-with-J as Univ using (Univalence)

private
  variable
    a p   : Level
    A     : Type p
    P     : A → Type p
    f     : (x : A) → P x
    b ℓ x : A

------------------------------------------------------------------------
-- The type and some eliminators

-- The circle.

data 𝕊¹ : Type where
  base  : 𝕊¹
  loopᴾ : base P.≡ base

loop : base ≡ base
loop = _↔_.from ≡↔≡ loopᴾ

-- A dependent eliminator, expressed using paths.

elimᴾ :
  (P : 𝕊¹ → Type p)
  (b : P base) →
  P.[ (λ i → P (loopᴾ i)) ] b ≡ b →
  (x : 𝕊¹) → P x
elimᴾ P b ℓ base      = b
elimᴾ P b ℓ (loopᴾ i) = ℓ i

-- A non-dependent eliminator, expressed using paths.

recᴾ : (b : A) → b P.≡ b → 𝕊¹ → A
recᴾ = elimᴾ _

-- A dependent eliminator.

elim :
  (P : 𝕊¹ → Type p)
  (b : P base) →
  subst P loop b ≡ b →
  (x : 𝕊¹) → P x
elim P b ℓ = elimᴾ P b (subst≡→[]≡ ℓ)

-- A "computation" rule.

elim-loop : dcong (elim P b ℓ) loop ≡ ℓ
elim-loop = dcong-subst≡→[]≡ (refl _)

-- Every dependent function of type (x : 𝕊¹) → P x can be expressed
-- using elim.

η-elim :
  {f : (x : 𝕊¹) → P x} →
  f ≡ elim P (f base) (dcong f loop)
η-elim {P = P} {f = f} =
  ⟨ext⟩ $ elim _ (refl _)
    (subst (λ x → f x ≡ elim P (f base) (dcong f loop) x) loop (refl _)  ≡⟨ subst-in-terms-of-trans-and-dcong ⟩

     trans (sym (dcong f loop))
       (trans (cong (subst P loop) (refl _))
          (dcong (elim P (f base) (dcong f loop)) loop))                 ≡⟨ cong (trans (sym (dcong f loop))) $
                                                                            trans (cong (flip trans _) $ cong-refl _) $
                                                                            trans-reflˡ _ ⟩
     trans (sym (dcong f loop))
       (dcong (elim P (f base) (dcong f loop)) loop)                     ≡⟨ cong (trans (sym (dcong f loop))) elim-loop ⟩

     trans (sym (dcong f loop)) (dcong f loop)                           ≡⟨ trans-symˡ _ ⟩∎

     refl _                                                              ∎)

-- A non-dependent eliminator.

rec : (b : A) → b ≡ b → 𝕊¹ → A
rec b ℓ = recᴾ b (_↔_.to ≡↔≡ ℓ)

-- A "computation" rule.

rec-loop : cong (rec b ℓ) loop ≡ ℓ
rec-loop = cong-≡↔≡ (refl _)

-- Every function from 𝕊¹ to A can be expressed using rec.

η-rec : {f : 𝕊¹ → A} → f ≡ rec (f base) (cong f loop)
η-rec {f = f} =
  ⟨ext⟩ $ elim _ (refl _)
    (subst (λ x → f x ≡ rec (f base) (cong f loop) x) loop (refl _)      ≡⟨ subst-in-terms-of-trans-and-cong ⟩

     trans (sym (cong f loop))
       (trans (refl _) (cong (rec (f base) (cong f loop)) loop))         ≡⟨ cong (trans (sym (cong f loop))) $ trans-reflˡ _ ⟩

     trans (sym (cong f loop)) (cong (rec (f base) (cong f loop)) loop)  ≡⟨ cong (trans (sym (cong f loop))) rec-loop ⟩

     trans (sym (cong f loop)) (cong f loop)                             ≡⟨ trans-symˡ _ ⟩∎

     refl _                                                              ∎)

-- An alternative non-dependent eliminator.

rec′ : (b : A) → b ≡ b → 𝕊¹ → A
rec′ {A = A} b ℓ = elim
  (const A)
  b
  (subst (const A) loop b  ≡⟨ subst-const _ ⟩
   b                       ≡⟨ ℓ ⟩∎
   b                       ∎)

-- A "computation" rule.

rec′-loop : cong (rec′ b ℓ) loop ≡ ℓ
rec′-loop = dcong≡→cong≡ elim-loop

------------------------------------------------------------------------
-- Some equivalences

-- The circle can be expressed as a suspension.

𝕊¹≃Susp-Bool : 𝕊¹ ≃ Susp Bool
𝕊¹≃Susp-Bool = Eq.↔→≃ to from to∘from from∘to
  where
  north≡north =
    north  ≡⟨ meridian false ⟩
    south  ≡⟨ sym $ meridian true ⟩∎
    north  ∎

  to : 𝕊¹ → Susp Bool
  to = rec north north≡north

  module From = Suspension.Rec base base (if_then refl base else loop)

  from : Susp Bool → 𝕊¹
  from = From.rec

  to∘from : ∀ x → to (from x) ≡ x
  to∘from = Suspension.elim _
    (to (from north)  ≡⟨⟩
     north            ∎)
    (to (from south)  ≡⟨⟩
     north            ≡⟨ meridian true ⟩∎
     south            ∎)
    (λ b →
       subst (λ x → to (from x) ≡ x) (meridian b) (refl north)  ≡⟨ subst-in-terms-of-trans-and-cong ⟩

       trans (sym (cong (to ∘ from) (meridian b)))
         (trans (refl _) (cong id (meridian b)))                ≡⟨ cong₂ (trans ∘ sym)
                                                                     (trans (sym $ cong-∘ _ _ _) $
                                                                      cong (cong to) From.rec-meridian)
                                                                     (trans (trans-reflˡ _) $
                                                                      sym $ cong-id _) ⟩
       trans (sym (cong to (if b then refl base else loop)))
             (meridian b)                                       ≡⟨ lemma b ⟩∎

       meridian true                                            ∎)
    where
    lemma : (b : Bool) → _ ≡ _
    lemma true  =
      trans (sym (cong to (if true ⦂ Bool then refl base else loop)))
            (meridian true)                                            ≡⟨⟩

      trans (sym (cong to (refl base))) (meridian true)                ≡⟨ prove (Trans (Sym (Cong _ Refl)) (Lift _)) (Lift _) (refl _) ⟩∎

      meridian true                                                    ∎

    lemma false =
      trans (sym (cong to (if false ⦂ Bool then refl base else loop)))
            (meridian false)                                            ≡⟨⟩

      trans (sym (cong to loop)) (meridian false)                       ≡⟨ cong (λ p → trans (sym p) (meridian false)) rec-loop ⟩

      trans (sym north≡north) (meridian false)                          ≡⟨ prove (Trans (Sym (Trans (Lift _) (Sym (Lift _)))) (Lift _))
                                                                                (Trans (Trans (Lift _) (Sym (Lift _))) (Lift _))
                                                                                (refl _) ⟩
      trans (trans (meridian true) (sym $ meridian false))
            (meridian false)                                            ≡⟨ trans-[trans-sym]- _ _ ⟩∎

      meridian true                                                     ∎

  from∘to : ∀ x → from (to x) ≡ x
  from∘to = elim _
    (from (to base)  ≡⟨⟩
     base            ∎)
    (subst (λ x → from (to x) ≡ x) loop (refl base)                  ≡⟨ subst-in-terms-of-trans-and-cong ⟩

     trans (sym (cong (from ∘ to) loop))
           (trans (refl base) (cong id loop))                        ≡⟨ cong₂ (trans ∘ sym)
                                                                          (trans (sym $ cong-∘ _ to _) $
                                                                           cong (cong from) rec-loop)
                                                                          (trans (trans-reflˡ _) $
                                                                           sym $ cong-id _) ⟩

     trans (sym (cong from north≡north)) loop                        ≡⟨ prove (Trans (Sym (Cong _ (Trans (Lift _) (Sym (Lift _))))) (Lift _))
                                                                              (Trans (Trans (Cong from (Lift (meridian true)))
                                                                                (Sym (Cong from (Lift (meridian false)))))
                                                                                (Lift _))
                                                                              (refl _) ⟩
     trans (trans (cong from (meridian true))
                  (sym $ cong from (meridian false)))
           loop                                                      ≡⟨ cong₂ (λ p q → trans (trans p (sym q)) loop)
                                                                          From.rec-meridian
                                                                          From.rec-meridian ⟩
     trans (trans (if true ⦂ Bool then refl base else loop)
                  (sym $ if false ⦂ Bool then refl base else loop))
           loop                                                      ≡⟨⟩

     trans (trans (refl base) (sym loop)) loop                       ≡⟨ trans-[trans-sym]- _ _ ⟩∎

     refl base                                                       ∎)

-- The circle is equivalent to the 1-dimensional sphere.

𝕊¹≃𝕊¹ : 𝕊¹ ≃ 𝕊 1
𝕊¹≃𝕊¹ =
  𝕊¹          ↝⟨ 𝕊¹≃Susp-Bool ⟩
  Susp Bool   ↔⟨ Suspension.cong-↔ Sphere.Bool↔𝕊⁰ ⟩
  Susp (𝕊 0)  ↔⟨⟩
  𝕊 1         □

------------------------------------------------------------------------
-- The loop space of the circle

-- The function trans is commutative for the loop space of the circle.

trans-commutative : (p q : base ≡ base) → trans p q ≡ trans q p
trans-commutative =
  flip $ Transitivity-commutative.commutative base _∙_ ∙-base base-∙
  where
  _∙_ : 𝕊¹ → 𝕊¹ → 𝕊¹
  x ∙ y = rec x (elim (λ x → x ≡ x) loop lemma x) y
    where
    lemma : subst (λ x → x ≡ x) loop loop ≡ loop
    lemma = ≡⇒↝ _ (sym [subst≡]≡[trans≡trans]) (refl _)

  base-∙ : ∀ x → x ∙ base ≡ x
  base-∙ _ = refl _

  ∙-base : ∀ y → base ∙ y ≡ y
  ∙-base =
    elim _ (refl _)
      (subst (λ x → rec base loop x ≡ x) loop (refl _)         ≡⟨ subst-in-terms-of-trans-and-cong ⟩

       trans (sym (cong (rec base loop) loop))
         (trans (refl _) (cong id loop))                       ≡⟨ cong (trans _) $ trans-reflˡ _ ⟩

       trans (sym (cong (rec base loop) loop)) (cong id loop)  ≡⟨ cong₂ (trans ∘ sym)
                                                                    rec-loop
                                                                    (sym $ cong-id _) ⟩

       trans (sym loop) loop                                   ≡⟨ trans-symˡ _ ⟩∎

       refl _                                                  ∎)

-- The loop space is equivalent to x ≡ x, for any x : 𝕊¹.

base≡base≃≡ : {x : 𝕊¹} → (base ≡ base) ≃ (x ≡ x)
base≡base≃≡ = elim
  (λ x → (base ≡ base) ≃ (x ≡ x))
  Eq.id
  (Eq.lift-equality ext $ ⟨ext⟩ λ eq →
   _≃_.to (subst (λ x → (base ≡ base) ≃ (x ≡ x)) loop Eq.id) eq        ≡⟨ cong (_$ eq) Eq.to-subst ⟩
   subst (λ x → base ≡ base → x ≡ x) loop id eq                        ≡⟨ subst-→ ⟩
   subst (λ x → x ≡ x) loop (subst (λ _ → base ≡ base) (sym loop) eq)  ≡⟨ cong (subst (λ x → x ≡ x) loop) $ subst-const _ ⟩
   subst (λ x → x ≡ x) loop eq                                         ≡⟨ ≡⇒↝ _ (sym [subst≡]≡[trans≡trans]) (

       trans eq loop                                                        ≡⟨ trans-commutative _ _ ⟩∎
       trans loop eq                                                        ∎) ⟩∎

   eq                                                                  ∎)
  _

private

  -- Definitions used to define base≡base≃ℤ and Fundamental-group≃ℤ.

  module base≡base≃ℤ (univ : Univalence lzero) where

    -- The universal cover of the circle.

    Cover : 𝕊¹ → Type
    Cover = rec ℤ (Univ.≃⇒≡ univ Int.successor)

    to : base ≡ x → Cover x
    to = flip (subst Cover) (+ 0)

    ≡⇒≃-cong-Cover-loop : Univ.≡⇒≃ (cong Cover loop) ≡ Int.successor
    ≡⇒≃-cong-Cover-loop =
      Univ.≡⇒≃ (cong Cover loop)              ≡⟨ cong Univ.≡⇒≃ rec-loop ⟩
      Univ.≡⇒≃ (Univ.≃⇒≡ univ Int.successor)  ≡⟨ _≃_.right-inverse-of (Univ.≡≃≃ univ) _ ⟩∎
      Int.successor                           ∎

    subst-Cover-loop :
      ∀ i → subst Cover loop i ≡ Int.suc i
    subst-Cover-loop i =
      subst Cover loop i            ≡⟨ subst-in-terms-of-≡⇒↝ equivalence _ _ _ ⟩
      Univ.≡⇒→ (cong Cover loop) i  ≡⟨ cong (λ eq → _≃_.to eq _) ≡⇒≃-cong-Cover-loop ⟩∎
      _≃_.to Int.successor i        ∎

    subst-Cover-sym-loop :
      ∀ i → subst Cover (sym loop) i ≡ Int.pred i
    subst-Cover-sym-loop i =
      subst Cover (sym loop) i                 ≡⟨ subst-in-terms-of-inverse∘≡⇒↝ equivalence _ _ _ ⟩
      _≃_.from (Univ.≡⇒≃ (cong Cover loop)) i  ≡⟨ cong (λ eq → _≃_.from eq _) ≡⇒≃-cong-Cover-loop ⟩∎
      _≃_.from Int.successor i                 ∎

    module 𝕊¹-G = Groupoid (groupoid 𝕊¹)

    loops : ℤ → base ≡ base
    loops = loop 𝕊¹-G.^_

    to-loops : ∀ i → to (loops i) ≡ i
    to-loops (+ zero) =
      subst Cover (refl _) (+ 0)  ≡⟨ subst-refl _ _ ⟩∎
      + zero                      ∎
    to-loops (+ suc n) =
      subst Cover (trans (loops (+ n)) loop) (+ 0)        ≡⟨ sym $ subst-subst _ _ _ _ ⟩
      subst Cover loop (subst Cover (loops (+ n)) (+ 0))  ≡⟨⟩
      subst Cover loop (to (loops (+ n)))                 ≡⟨ cong (subst Cover loop) $ to-loops (+ n) ⟩
      subst Cover loop (+ n)                              ≡⟨ subst-Cover-loop _ ⟩∎
      + suc n                                             ∎
    to-loops -[1+ zero ] =
      subst Cover (trans (refl _) (sym loop)) (+ 0)  ≡⟨ cong (flip (subst Cover) _) $ trans-reflˡ _ ⟩
      subst Cover (sym loop) (+ 0)                   ≡⟨ subst-Cover-sym-loop _ ⟩∎
      -[1+ zero ]                                    ∎
    to-loops -[1+ suc n ] =
      subst Cover (trans (loops -[1+ n ]) (sym loop)) (+ 0)        ≡⟨ sym $ subst-subst _ _ _ _ ⟩
      subst Cover (sym loop) (subst Cover (loops -[1+ n ]) (+ 0))  ≡⟨⟩
      subst Cover (sym loop) (to (loops -[1+ n ]))                 ≡⟨ cong (subst Cover (sym loop)) $ to-loops -[1+ n ] ⟩
      subst Cover (sym loop) -[1+ n ]                              ≡⟨ subst-Cover-sym-loop _ ⟩∎
      -[1+ suc n ]                                                 ∎

    loops-pred-loop :
      ∀ i → trans (loops (Int.pred i)) loop ≡ loops i
    loops-pred-loop i =
      trans (loops (Int.pred i)) loop                           ≡⟨ cong (flip trans _ ∘ loops) $ Int.pred≡-1+ i ⟩
      trans (loops (Int.-[ 1 ] Int.+ i)) loop                   ≡⟨ cong (flip trans _) $ sym $ 𝕊¹-G.^∘^ {j = i} Int.-[ 1 ] ⟩
      trans (trans (loops i) (loops (Int.-[ 1 ]))) loop         ≡⟨⟩
      trans (trans (loops i) (trans (refl _) (sym loop))) loop  ≡⟨ cong (flip trans _) $ cong (trans _) $ trans-reflˡ _ ⟩
      trans (trans (loops i) (sym loop)) loop                   ≡⟨ trans-[trans-sym]- _ _ ⟩∎
      loops i                                                   ∎

    from : ∀ x → Cover x → base ≡ x
    from = elim _
      loops
      (⟨ext⟩ λ i →
       subst (λ x → Cover x → base ≡ x) loop loops i            ≡⟨ subst-→ ⟩
       subst (base ≡_) loop (loops (subst Cover (sym loop) i))  ≡⟨ sym trans-subst ⟩
       trans (loops (subst Cover (sym loop) i)) loop            ≡⟨ cong (flip trans _ ∘ loops) $ subst-Cover-sym-loop _ ⟩
       trans (loops (Int.pred i)) loop                          ≡⟨ loops-pred-loop i ⟩∎
       loops i                                                  ∎)

    from-to : (eq : base ≡ x) → from x (to eq) ≡ eq
    from-to = elim¹
      (λ {x} eq → from x (to eq) ≡ eq)
      (from base (to (refl base))             ≡⟨⟩
       loops (subst Cover (refl base) (+ 0))  ≡⟨ cong loops $ subst-refl _ _ ⟩
       loops (+ 0)                            ≡⟨⟩
       refl base                              ∎)

    loops-+ : ∀ i j → loops (i Int.+ j) ≡ trans (loops i) (loops j)
    loops-+ i j =
      loops (i Int.+ j)          ≡⟨ cong loops $ Int.+-comm i ⟩
      loops (j Int.+ i)          ≡⟨ sym $ 𝕊¹-G.^∘^ j ⟩∎
      trans (loops i) (loops j)  ∎

-- The loop space of the circle is equivalent to the type of integers
-- (assuming univalence).
--
-- The proof is based on the one presented by Licata and Shulman in
-- "Calculating the Fundamental Group of the Circle in Homotopy Type
-- Theory".

base≡base≃ℤ :
  Univalence lzero →
  (base ≡ base) ≃ ℤ
base≡base≃ℤ univ = Eq.↔→≃ to loops to-loops from-to
  where
  open base≡base≃ℤ univ

-- The circle's fundamental group is equivalent to the group of
-- integers (assuming univalence).

Fundamental-group≃ℤ :
  Univalence lzero →
  Fundamental-group (𝕊¹ , base) ≃ᴳ ℤ-group
Fundamental-group≃ℤ univ = G.≃ᴳ-sym λ where
    .G.Homomorphic.related → inverse
      (∥ base ≡ base ∥[1+ 1 ]  ↝⟨ T.∥∥-cong $ base≡base≃ℤ univ ⟩
       ∥ ℤ ∥[1+ 1 ]            ↔⟨ _⇔_.to (T.+⇔∥∥↔ {n = 1}) Int.ℤ-set ⟩□
       ℤ                       □)
    .G.Homomorphic.homomorphic i j → cong T.∣_∣ (loops-+ i j)
  where
  open base≡base≃ℤ univ

-- The circle is a groupoid (assuming univalence).

𝕊¹-groupoid :
  Univalence lzero →
  H-level 3 𝕊¹
𝕊¹-groupoid univ {x = x} {y = y} =
                        $⟨ (λ {_ _} → Int.ℤ-set) ⟩
  Is-set ℤ              ↝⟨ H-level-cong _ 2 (inverse $ base≡base≃ℤ univ) ⦂ (_ → _) ⟩
  Is-set (base ≡ base)  ↝⟨ (λ s →
                              elim
                                (λ x → ∀ y → Is-set (x ≡ y))
                                (elim _ s (H-level-propositional ext 2 _ _))
                                ((Π-closure ext 1 λ _ →
                                  H-level-propositional ext 2)
                                   _ _)
                                x y) ⟩□
  Is-set (x ≡ y)        □

-- The type of endofunctions on 𝕊¹ is equivalent to
-- ∃ λ (x : 𝕊¹) → x ≡ x.

𝕊¹→𝕊¹≃Σ𝕊¹≡ : (𝕊¹ → 𝕊¹) ≃ ∃ λ (x : 𝕊¹) → x ≡ x
𝕊¹→𝕊¹≃Σ𝕊¹≡ = Eq.↔→≃ to from to-from from-to
  where
  to : (𝕊¹ → 𝕊¹) → ∃ λ (x : 𝕊¹) → x ≡ x
  to f = f base , cong f loop

  from : (∃ λ (x : 𝕊¹) → x ≡ x) → (𝕊¹ → 𝕊¹)
  from = uncurry rec

  to-from : ∀ p → to (from p) ≡ p
  to-from (x , eq) = cong (x ,_)
    (cong (rec x eq) loop  ≡⟨ rec-loop ⟩∎
     eq                    ∎)

  from-to : ∀ f → from (to f) ≡ f
  from-to f =
    rec (f base) (cong f loop)  ≡⟨ sym η-rec ⟩∎
    f                           ∎

-- The type of endofunctions on 𝕊¹ is equivalent to 𝕊¹ × ℤ (assuming
-- univalence).
--
-- This result was pointed out to me by Paolo Capriotti.

𝕊¹→𝕊¹≃𝕊¹×ℤ :
  Univalence lzero →
  (𝕊¹ → 𝕊¹) ≃ (𝕊¹ × ℤ)
𝕊¹→𝕊¹≃𝕊¹×ℤ univ =
  (𝕊¹ → 𝕊¹)               ↝⟨ 𝕊¹→𝕊¹≃Σ𝕊¹≡ ⟩
  (∃ λ (x : 𝕊¹) → x ≡ x)  ↝⟨ (∃-cong λ _ → inverse base≡base≃≡) ⟩
  𝕊¹ × base ≡ base        ↝⟨ (∃-cong λ _ → base≡base≃ℤ univ) ⟩□
  𝕊¹ × ℤ                  □

-- The forward direction of 𝕊¹→𝕊¹≃𝕊¹×ℤ maps the identity function to
-- base , + 1.

𝕊¹→𝕊¹≃𝕊¹×ℤ-id :
  (univ : Univalence lzero) →
  _≃_.to (𝕊¹→𝕊¹≃𝕊¹×ℤ univ) id ≡ (base , + 1)
𝕊¹→𝕊¹≃𝕊¹×ℤ-id univ = _≃_.from-to (𝕊¹→𝕊¹≃𝕊¹×ℤ univ)
  (rec base (trans (refl base) loop)  ≡⟨ cong (rec base) $ trans-reflˡ _ ⟩
   rec base loop                      ≡⟨ cong (rec base) $ cong-id _ ⟩
   rec base (cong id loop)            ≡⟨ sym η-rec ⟩∎
   id                                 ∎)

-- The forward direction of 𝕊¹→𝕊¹≃𝕊¹×ℤ maps the constant function
-- returning base to base , + 0.

𝕊¹→𝕊¹≃𝕊¹×ℤ-const :
  (univ : Univalence lzero) →
  _≃_.to (𝕊¹→𝕊¹≃𝕊¹×ℤ univ) (const base) ≡ (base , + 0)
𝕊¹→𝕊¹≃𝕊¹×ℤ-const univ = _≃_.from-to (𝕊¹→𝕊¹≃𝕊¹×ℤ univ)
  (rec base (refl base)               ≡⟨ cong (rec base) $ sym $ cong-const _ ⟩
   rec base (cong (const base) loop)  ≡⟨ sym η-rec ⟩∎
   const base                         ∎)

------------------------------------------------------------------------
-- A conversion function

-- The one-step truncation of the unit type is equivalent to the
-- circle.
--
-- Paolo Capriotti informed me about this result.

∥⊤∥¹≃𝕊¹ : ∥ ⊤ ∥¹ ≃ 𝕊¹
∥⊤∥¹≃𝕊¹ = _↔_.from ≃↔≃ $ PE.↔→≃
  (O.recᴾ λ where
     .O.∣∣ʳ _            → base
     .O.∣∣-constantʳ _ _ → loopᴾ)
  (recᴾ O.∣ _ ∣ (O.∣∣-constantᴾ _ _))
  (elimᴾ _ P.refl (λ _ → P.refl))
  (O.elimᴾ λ where
     .O.∣∣ʳ _              → P.refl
     .O.∣∣-constantʳ _ _ _ → P.refl)

------------------------------------------------------------------------
-- Some negative results

-- The equality loop is not equal to refl base.

loop≢refl : loop ≢ refl base
loop≢refl =
  E.Stable-¬
    E.[ loop ≡ refl base  →⟨ Type-set ⟩
        Is-set Type       →⟨ Univ.¬-Type-set univ ⟩□
        ⊥                 □
      ]
  where
  module _ (loop≡refl : loop ≡ refl base) where

    refl≡ : (A : Type) (A≡A : A ≡ A) → refl A ≡ A≡A
    refl≡ A A≡A =
      refl A                        ≡⟨⟩
      refl (rec A A≡A base)         ≡⟨ sym $ cong-refl _ ⟩
      cong (rec A A≡A) (refl base)  ≡⟨ cong (cong (rec A A≡A)) $ sym loop≡refl ⟩
      cong (rec A A≡A) loop         ≡⟨ rec-loop ⟩∎
      A≡A                           ∎

    Type-set : Is-set Type
    Type-set {x = A} {y = B} =
      elim¹ (λ p → ∀ q → p ≡ q)
            (refl≡ A)

-- Thus the circle is not a set.

¬-𝕊¹-set : ¬ Is-set 𝕊¹
¬-𝕊¹-set =
  Is-set 𝕊¹                     ↝⟨ (λ h → h) ⟩
  Is-proposition (base ≡ base)  ↝⟨ (λ h → h _ _) ⟩
  loop ≡ refl base              ↝⟨ loop≢refl ⟩□
  ⊥                             □

-- It is not necessarily the case that the one-step truncation of a
-- proposition is a proposition.

¬-Is-proposition-∥∥¹ :
  ¬ ({A : Type a} → Is-proposition A → Is-proposition ∥ A ∥¹)
¬-Is-proposition-∥∥¹ {a = a} =
  ({A : Type a} → Is-proposition A → Is-proposition ∥ A ∥¹)  ↝⟨ _$ H-level.mono₁ 0 (↑-closure 0 ⊤-contractible) ⟩
  Is-proposition ∥ ↑ a ⊤ ∥¹                                  ↝⟨ H-level-cong _ 1 (O.∥∥¹-cong-↔ Bijection.↑↔) ⟩
  Is-proposition ∥ ⊤ ∥¹                                      ↝⟨ H-level-cong _ 1 ∥⊤∥¹≃𝕊¹ ⟩
  Is-proposition 𝕊¹                                          ↝⟨ ¬-𝕊¹-set ∘ H-level.mono₁ 1 ⟩□
  ⊥                                                          □

-- A function with the type of refl (for 𝕊¹) that is not equal to
-- refl.

not-refl : (x : 𝕊¹) → x ≡ x
not-refl = elim _
  loop
  (subst (λ z → z ≡ z) loop loop  ≡⟨ ≡⇒↝ _ (sym [subst≡]≡[trans≡trans]) (refl _) ⟩∎
   loop                           ∎)

-- The function not-refl is not equal to refl.

not-refl≢refl : not-refl ≢ refl
not-refl≢refl =
  not-refl ≡ refl   ↝⟨ cong (_$ _) ⟩
  loop ≡ refl base  ↝⟨ loop≢refl ⟩□
  ⊥                 □

-- There is a value with the type of refl that is not equal to refl.

∃≢refl : ∃ λ (f : (x : 𝕊¹) → x ≡ x) → f ≢ refl
∃≢refl = not-refl , not-refl≢refl

-- For every universe level there is a type A such that
-- (x : A) → x ≡ x is not a proposition.

¬-type-of-refl-propositional :
  ∃ λ (A : Type a) → ¬ Is-proposition ((x : A) → x ≡ x)
¬-type-of-refl-propositional {a = a} =
    ↑ _ 𝕊¹
  , (Is-proposition (∀ x → x ≡ x)                                 ↝⟨ (λ prop → prop _ _) ⟩

     cong lift ∘ proj₁ ∃≢refl ∘ lower ≡ cong lift ∘ refl ∘ lower  ↝⟨ cong (_∘ lift) ⟩

     cong lift ∘ proj₁ ∃≢refl ≡ cong lift ∘ refl                  ↝⟨ cong (cong lower ∘_) ⟩

     cong lower ∘ cong lift ∘ proj₁ ∃≢refl ≡
     cong lower ∘ cong lift ∘ refl                                ↝⟨ ≡⇒↝ _ (cong₂ _≡_ (⟨ext⟩ λ _ → cong-∘ _ _ _) (⟨ext⟩ λ _ → cong-∘ _ _ _)) ⟩

     cong id ∘ proj₁ ∃≢refl ≡ cong id ∘ refl                      ↝⟨ ≡⇒↝ _ (sym $ cong₂ _≡_ (⟨ext⟩ λ _ → cong-id _) (⟨ext⟩ λ _ → cong-id _)) ⟩

     proj₁ ∃≢refl ≡ refl                                          ↝⟨ proj₂ ∃≢refl ⟩□

     ⊥                                                            □)

-- Every element of the circle is /merely/ equal to the base point.
--
-- This lemma was mentioned by Mike Shulman in a blog post
-- (http://homotopytypetheory.org/2013/07/24/cohomology/).

all-points-on-the-circle-are-merely-equal :
  (x : 𝕊¹) → ∥ x ≡ base ∥
all-points-on-the-circle-are-merely-equal =
  elim _
       ∣ refl base ∣
       (Trunc.truncation-is-proposition _ _)

-- Thus every element of the circle is not not equal to the base
-- point.

all-points-on-the-circle-are-¬¬-equal :
  (x : 𝕊¹) → ¬ ¬ x ≡ base
all-points-on-the-circle-are-¬¬-equal x =
  x ≢ base        ↝⟨ Trunc.rec ⊥-propositional ⟩
  ¬ ∥ x ≡ base ∥  ↝⟨ _$ all-points-on-the-circle-are-merely-equal x ⟩□
  ⊥               □

-- It is not the case that every point on the circle is equal to the
-- base point.

¬-all-points-on-the-circle-are-equal :
  ¬ ((x : 𝕊¹) → x ≡ base)
¬-all-points-on-the-circle-are-equal =
  ((x : 𝕊¹) → x ≡ base)  ↝⟨ (λ hyp x y → x     ≡⟨ hyp x ⟩
                                         base  ≡⟨ sym (hyp y) ⟩∎
                                         y     ∎) ⟩
  Is-proposition 𝕊¹      ↝⟨ mono₁ 1 ⟩
  Is-set 𝕊¹              ↝⟨ ¬-𝕊¹-set ⟩□
  ⊥                      □

-- Thus double-negation shift for Type-valued predicates over 𝕊¹ does
-- not hold in general.

¬-double-negation-shift :
  ¬ ({P : 𝕊¹ → Type} → ((x : 𝕊¹) → ¬ ¬ P x) → ¬ ¬ ((x : 𝕊¹) → P x))
¬-double-negation-shift =
  ({P : 𝕊¹ → Type} → ((x : 𝕊¹) → ¬ ¬ P x) → ¬ ¬ ((x : 𝕊¹) → P x))  ↝⟨ _$ all-points-on-the-circle-are-¬¬-equal ⟩
  ¬ ¬ ((x : 𝕊¹) → x ≡ base)                                        ↝⟨ _$ ¬-all-points-on-the-circle-are-equal ⟩□
  ⊥                                                                □

-- Furthermore excluded middle for arbitrary types (in Type) does not
-- hold.

¬-excluded-middle : ¬ ({A : Type} → Dec A)
¬-excluded-middle =
  ({A : Type} → Dec A)                                             ↝⟨ (λ em ¬¬a → [ id , ⊥-elim ∘ ¬¬a ] em) ⟩
  ({A : Type} → ¬ ¬ A → A)                                         ↝⟨ (λ dne → flip _$_ ∘ (dne ∘_)) ⟩
  ({P : 𝕊¹ → Type} → ((x : 𝕊¹) → ¬ ¬ P x) → ¬ ¬ ((x : 𝕊¹) → P x))  ↝⟨ ¬-double-negation-shift ⟩□
  ⊥                                                                □

-- H-level.Closure.proj₁-closure cannot be generalised by replacing
-- the assumption ∀ a → B a with ∀ a → ∥ B a ∥.
--
-- This observation is due to Andrea Vezzosi.

¬-generalised-proj₁-closure :
  ¬ ({A : Type} {B : A → Type} →
     (∀ a → ∥ B a ∥) →
     ∀ n → H-level n (Σ A B) → H-level n A)
¬-generalised-proj₁-closure generalised-proj₁-closure =
                                 $⟨ singleton-contractible _ ⟩
  Contractible (Σ 𝕊¹ (_≡ base))  ↝⟨ generalised-proj₁-closure
                                      all-points-on-the-circle-are-merely-equal
                                      0 ⟩
  Contractible 𝕊¹                ↝⟨ mono (zero≤ 2) ⟩
  Is-set 𝕊¹                      ↝⟨ ¬-𝕊¹-set ⟩□
  ⊥                              □

-- There is no based equivalence between the circle and the product of
-- the circle with itself.
--
-- This result was pointed out to me by Paolo Capriotti.

𝕊¹≄ᴮ𝕊¹×𝕊¹ : ¬ (𝕊¹ , base) ≃ᴮ ((𝕊¹ , base) PT.× (𝕊¹ , base))
𝕊¹≄ᴮ𝕊¹×𝕊¹ =
  E.Stable-¬
    E.[ (𝕊¹ , base) ≃ᴮ ((𝕊¹ , base) PT.× (𝕊¹ , base))                      ↝⟨ ≃ᴮ→≃ᴳ (𝕊¹ , base) ((𝕊¹ , base) PT.× (𝕊¹ , base)) 0 ⟩

        Fundamental-group (𝕊¹ , base) ≃ᴳ
        Fundamental-group ((𝕊¹ , base) PT.× (𝕊¹ , base))                   ↝⟨ flip G.↝ᴳ-trans (Homotopy-group-[1+ 0 ]-× (𝕊¹ , base) (𝕊¹ , base)) ⟩

        Fundamental-group (𝕊¹ , base) ≃ᴳ
        (Fundamental-group (𝕊¹ , base) G.× Fundamental-group (𝕊¹ , base))  ↝⟨ flip G.↝ᴳ-trans
                                                                                (G.↝-× (Fundamental-group≃ℤ univ) (Fundamental-group≃ℤ univ)) ∘
                                                                              G.↝ᴳ-trans (G.≃ᴳ-sym (Fundamental-group≃ℤ univ)) ⟩

        ℤ-group ≃ᴳ (ℤ-group G.× ℤ-group)                                   ↝⟨ C.ℤ≄ᴳℤ×ℤ ⟩□

        ⊥                                                                  □
      ]

-- 𝕊¹ is not equivalent to 𝕊¹ × 𝕊¹.
--
-- This result was pointed out to me by Paolo Capriotti.

𝕊¹≄𝕊¹×𝕊¹ : ¬ 𝕊¹ ≃ (𝕊¹ × 𝕊¹)
𝕊¹≄𝕊¹×𝕊¹ hyp =
  let x , y = _≃_.to hyp base in
  all-points-on-the-circle-are-¬¬-equal x λ x≡base →
  all-points-on-the-circle-are-¬¬-equal y λ y≡base →
  𝕊¹≄ᴮ𝕊¹×𝕊¹ (hyp , cong₂ _,_ x≡base y≡base)

------------------------------------------------------------------------
-- An alternative approach to defining eliminators and proving
-- computation rules for arbitrary notions of equality, based on an
-- anonymous reviewer's suggestion

-- Circle eq p is an axiomatisation of the circle, for the given
-- notion of equality eq, eliminating into Type p.
--
-- Note that the statement of the computation rule for "loop" is more
-- complicated than above (elim-loop). The reason is that the
-- computation rule for "base" does not hold definitionally.

Circle :
  ∀ {e⁺} →
  (∀ {a p} → P.Equality-with-paths a p e⁺) →
  (p : Level) → Type (lsuc p)
Circle eq p =
  ∃ λ (𝕊¹ : Type) →
  ∃ λ (base : 𝕊¹) →
  ∃ λ (loop : base ≡.≡ base) →
    (P : 𝕊¹ → Type p)
    (b : P base)
    (ℓ : ≡.subst P loop b ≡.≡ b) →
    ∃ λ (elim : (x : 𝕊¹) → P x) →
    ∃ λ (elim-base : elim base ≡.≡ b) →
      ≡.subst (λ b → ≡.subst P loop b ≡.≡ b)
              elim-base
              (≡.dcong elim loop)
        ≡.≡
      ℓ
  where
  module ≡ = P.Derived-definitions-and-properties eq

-- A circle defined for paths (P.equality-with-J) is equivalent to one
-- defined for eq.

Circle≃Circle : Circle P.equality-with-paths p ≃ Circle eq p
Circle≃Circle =
  ∃-cong λ _ →
  ∃-cong λ _ →
  Σ-cong (inverse ≡↔≡) λ loop →
  ∀-cong ext λ P →
  ∀-cong ext λ b →
  Π-cong-contra ext subst≡↔subst≡ λ ℓ →
  ∃-cong λ f →
  Σ-cong (inverse ≡↔≡) λ f-base →
  let lemma = P.elim¹
        (λ eq → _↔_.from subst≡↔subst≡
                  (P.subst
                     (λ b → P.subst P loop b P.≡ b)
                     eq
                     (P.dcong f loop)) ≡
                P.subst
                  (λ b → subst P (_↔_.from ≡↔≡ loop) b ≡ b)
                  eq
                  (_↔_.from subst≡↔subst≡ (P.dcong f loop)))
        (_↔_.from subst≡↔subst≡
           (P.subst
              (λ b → P.subst P loop b P.≡ b)
              P.refl
              (P.dcong f loop))                       ≡⟨ cong (_↔_.from subst≡↔subst≡) $ _↔_.from ≡↔≡ $
                                                         P.subst-refl (λ b → P.subst P loop b P.≡ b) _ ⟩

         _↔_.from subst≡↔subst≡ (P.dcong f loop)      ≡⟨ sym $ _↔_.from ≡↔≡ $
                                                         P.subst-refl (λ b → subst P (_↔_.from ≡↔≡ loop) b ≡ b) _ ⟩∎
         P.subst
           (λ b → subst P (_↔_.from ≡↔≡ loop) b ≡ b)
           P.refl
           (_↔_.from subst≡↔subst≡ (P.dcong f loop))  ∎)
        _
  in
  P.subst
    (λ b → P.subst P loop b P.≡ b)
    f-base
    (P.dcong f loop) P.≡
  _↔_.to subst≡↔subst≡ ℓ                           ↔⟨ ≡↔≡ F.∘ inverse (from≡↔≡to (Eq.↔⇒≃ subst≡↔subst≡)) F.∘ inverse ≡↔≡ ⟩

  _↔_.from subst≡↔subst≡
    (P.subst
       (λ b → P.subst P loop b P.≡ b)
       f-base
       (P.dcong f loop)) P.≡
  ℓ                                                ↝⟨ ≡⇒↝ _ (cong (P._≡ _) lemma) ⟩

  P.subst
    (λ b → subst P (_↔_.from ≡↔≡ loop) b ≡ b)
    f-base
    (_↔_.from subst≡↔subst≡ (P.dcong f loop)) P.≡
  ℓ                                                ↝⟨ ≡⇒↝ _ $ cong (λ eq → P.subst (λ b → subst P (_↔_.from ≡↔≡ loop) b ≡ b) f-base eq P.≡ ℓ) $
                                                      _↔_.from-to (inverse subst≡↔subst≡) dcong≡dcong ⟩
  P.subst
    (λ b → subst P (_↔_.from ≡↔≡ loop) b ≡ b)
    f-base
    (dcong f (_↔_.from ≡↔≡ loop)) P.≡
  ℓ                                                ↔⟨ inverse subst≡↔subst≡ ⟩□

  subst
    (λ b → subst P (_↔_.from ≡↔≡ loop) b ≡ b)
    (_↔_.from ≡↔≡ f-base)
    (dcong f (_↔_.from ≡↔≡ loop)) ≡
  ℓ                                                □

-- An implemention of the circle for paths (P.equality-with-paths).

circleᴾ : Circle P.equality-with-paths p
circleᴾ =
    𝕊¹
  , base
  , loopᴾ
  , λ P b ℓ →
      let elim = elimᴾ P b (PI.subst≡→[]≡ {B = P} ℓ)
      in
        elim
      , P.refl
      , (P.subst (λ b → P.subst P loopᴾ b P.≡ b) P.refl
           (P.dcong elim loopᴾ)                          P.≡⟨ P.subst-refl (λ b → P.subst P loopᴾ b P.≡ b) _ ⟩

         P.dcong elim loopᴾ                              P.≡⟨ PI.dcong-subst≡→[]≡ {f = elim} {eq₂ = ℓ} P.refl ⟩∎

         ℓ                                               ∎)

-- An implementation of the circle for eq.

circle : Circle eq p
circle = _≃_.to Circle≃Circle circleᴾ

-- The latter implementation computes in the right way for "base".

_ :
  let _ , base′ , _ , elim′ = circle {p = p} in
  ∀ {P b ℓ} →
  proj₁ (elim′ P b ℓ) base′ ≡ b
_ = refl _

-- The usual computation rule for "loop" can be derived.

elim-loop-circle :
  let _ , _ , loop′ , elim′ = circle {p = p} in
  ∀ {P b ℓ} →
  dcong (proj₁ (elim′ P b ℓ)) loop′ ≡ ℓ
elim-loop-circle {P = P} {b = b} {ℓ = ℓ} =
  let _ , _ , loop′ , elim′           = circle
      elim″ , elim″-base , elim″-loop = elim′ P b ℓ

      lemma =
        refl _               ≡⟨ sym from-≡↔≡-refl ⟩
        _↔_.from ≡↔≡ P.refl  ≡⟨⟩
        elim″-base           ∎
  in
  dcong elim″ loop′                                                 ≡⟨ sym $ subst-refl _ _ ⟩
  subst (λ b → subst P loop′ b ≡ b) (refl _) (dcong elim″ loop′)    ≡⟨ cong (λ eq → subst (λ b → subst P loop′ b ≡ b) eq (dcong elim″ loop′)) lemma ⟩
  subst (λ b → subst P loop′ b ≡ b) elim″-base (dcong elim″ loop′)  ≡⟨ elim″-loop ⟩∎
  ℓ                                                                 ∎

-- An alternative to Circle≃Circle that does not give the "right"
-- computational behaviour for circle′ below.

Circle≃Circle′ : Circle P.equality-with-paths p ≃ Circle eq p
Circle≃Circle′ =
  ∃-cong λ _ →
  ∃-cong λ _ →
  Σ-cong (inverse ≡↔≡) λ loop →
  ∀-cong ext λ P →
  ∀-cong ext λ b →
  Π-cong ext (inverse subst≡↔subst≡) λ ℓ →
  ∃-cong λ f →
  Σ-cong (inverse ≡↔≡) λ f-base →
  let lemma = P.elim¹
        (λ eq → _↔_.from subst≡↔subst≡
                  (P.subst
                     (λ b → P.subst P loop b P.≡ b)
                     eq
                     (P.dcong f loop)) ≡
                P.subst
                  (λ b → subst P (_↔_.from ≡↔≡ loop) b ≡ b)
                  eq
                  (_↔_.from subst≡↔subst≡ (P.dcong f loop)))
        (_↔_.from subst≡↔subst≡
           (P.subst
              (λ b → P.subst P loop b P.≡ b)
              P.refl
              (P.dcong f loop))                       ≡⟨ cong (_↔_.from subst≡↔subst≡) $ _↔_.from ≡↔≡ $
                                                         P.subst-refl (λ b → P.subst P loop b P.≡ b) _ ⟩

         _↔_.from subst≡↔subst≡ (P.dcong f loop)      ≡⟨ sym $ _↔_.from ≡↔≡ $
                                                         P.subst-refl (λ b → subst P (_↔_.from ≡↔≡ loop) b ≡ b) _ ⟩∎
         P.subst
           (λ b → subst P (_↔_.from ≡↔≡ loop) b ≡ b)
           P.refl
           (_↔_.from subst≡↔subst≡ (P.dcong f loop))  ∎)
        _
  in
  P.subst
    (λ b → P.subst P loop b P.≡ b)
    f-base
    (P.dcong f loop) P.≡
  ℓ                                                ↔⟨ ≡↔≡ F.∘ from-isomorphism (inverse $ Eq.≃-≡ $ Eq.↔⇒≃ $ inverse subst≡↔subst≡) F.∘ inverse ≡↔≡ ⟩

  _↔_.from subst≡↔subst≡
    (P.subst
       (λ b → P.subst P loop b P.≡ b)
       f-base
       (P.dcong f loop)) P.≡
  _↔_.from subst≡↔subst≡ ℓ                         ↝⟨ ≡⇒↝ _ (cong (P._≡ _↔_.from subst≡↔subst≡ ℓ) lemma) ⟩

  P.subst
    (λ b → subst P (_↔_.from ≡↔≡ loop) b ≡ b)
    f-base
    (_↔_.from subst≡↔subst≡ (P.dcong f loop)) P.≡
  _↔_.from subst≡↔subst≡ ℓ                         ↝⟨ ≡⇒↝ _ $ cong (λ eq → P.subst (λ b → subst P (_↔_.from ≡↔≡ loop) b ≡ b) f-base eq P.≡ _↔_.from subst≡↔subst≡ ℓ) $
                                                      _↔_.from-to (inverse subst≡↔subst≡) dcong≡dcong ⟩
  P.subst
    (λ b → subst P (_↔_.from ≡↔≡ loop) b ≡ b)
    f-base
    (dcong f (_↔_.from ≡↔≡ loop)) P.≡
  _↔_.from subst≡↔subst≡ ℓ                         ↔⟨ inverse subst≡↔subst≡ ⟩□

  subst
    (λ b → subst P (_↔_.from ≡↔≡ loop) b ≡ b)
    (_↔_.from ≡↔≡ f-base)
    (dcong f (_↔_.from ≡↔≡ loop)) ≡
  _↔_.from subst≡↔subst≡ ℓ                         □

-- An alternative implementation of the circle for eq.

circle′ : Circle eq p
circle′ = _≃_.to Circle≃Circle′ circleᴾ

-- This implementation does not compute in the right way for "base".
-- The following code is (at the time of writing) rejected by Agda.

-- _ :
--   let _ , base′ , _ , elim′ = circle′ {p = p} in
--   ∀ {P b ℓ} →
--   proj₁ (elim′ P b ℓ) base′ ≡ b
-- _ = refl _


module Issue826-2 where

open import Common.Coinduction

data _≡_ {A : Set} (x y : A) : Set where

data D : Set where
  c : ∞ D → D

delay : D → ∞ D
delay x = ♯ x

data P : D → Set where
  o : (x : ∞ D) → P (♭ x) → P (c x)

postulate
  h : (x : D) → P x → P x

f : (x : D) → P (c (delay x)) → P x
f x (o .(delay x) p) = h x p

g : (x : D) → P x → P (c (delay x))
g x p = h (c (delay x)) (o (delay x) p)

postulate
  bar : (x : ∞ D) (p : P (♭ x)) →
        h (c x) (o x (h (♭ x) p)) ≡ o x p

foo : (x : D) (p : P (c (delay x))) → g x (f x p) ≡ p
foo x (o .(delay x) p) = goal
  where
  x′ = _

  goal : _ ≡ o x′ p
  goal = bar x′ p

-- The following error message seems to indicate that an expression is
-- not forced properly:
--
--   Bug.agda:30,26-30
--   ♭ (.Bug.♯-0 x) != x of type D
--   when checking that the expression goal has type
--   g x (f x (o (.Bug.♯-0 x) p)) ≡ o (.Bug.♯-0 x) p
--
-- Thus it seems as if this problem affects plain type-checking as
-- well.


-- Andreas, 2012-02-24 example by Ramana Kumar
{-# OPTIONS --sized-types #-}
-- {-# OPTIONS --show-implicit -v tc.size.solve:20 -v tc.conv.size:15 #-}
module SizeInconsistentMeta4 where

open import Data.Nat using (ℕ;zero;suc) renaming (_<_ to _N<_)
open import Data.Product using (_,_;_×_)
open import Data.List using (List)
open import Relation.Binary using (Rel;_Respects₂_;Transitive;IsEquivalence)
open import Relation.Binary.Product.StrictLex using (×-Lex;×-transitive)
open import Relation.Binary.List.StrictLex using (Lex-<) renaming (transitive to Lex<-trans)

open import Relation.Binary.PropositionalEquality as PropEq using (_≡_)

import Level
open import Size using (Size;↑_)

-- keeping the definition of Vec for the positivity check
infixr 5 _∷_
data Vec {a} (A : Set a) : ℕ → Set a where
  []  : Vec A zero
  _∷_ : ∀ {n} (x : A) (xs : Vec A n) → Vec A (suc n)

{- produces different error
data Lex-< {A : _} (_≈_ _<_ : Rel A Level.zero) : Rel (List A) Level.zero where
postulate
  Lex<-trans : ∀ {P _≈_ _<_} →
               IsEquivalence _≈_ → _<_ Respects₂ _≈_ → Transitive _<_ →
               Transitive (Lex-< _≈_ _<_)
-}

postulate
  N<-trans : Transitive _N<_
  -- Vec : ∀ {a} (A : Set a) → ℕ → Set a
  Vec→List : ∀ {a n} {A : Set a} → Vec A n → List A

data Type : {z : Size} → Set where
  TyApp : {z : Size} (n : ℕ) → (as : Vec (Type {z}) n) → Type {↑ z}

infix 4 _<_
data _<_ : {z : Size} → Rel (Type {z}) Level.zero where
  TyApp<TyApp : ∀ {z} {n₁} {as₁} {n₂} {as₂} → ×-Lex _≡_ _N<_ (Lex-< _≡_ (_<_ {z})) (n₁ , Vec→List as₁) (n₂ , Vec→List as₂) → _<_ {↑ z} (TyApp n₁ as₁) (TyApp n₂ as₂)

<-trans : {z : Size} → Transitive (_<_ {z})
<-trans (TyApp<TyApp p) (TyApp<TyApp q) = TyApp<TyApp (×-transitive PropEq.isEquivalence (PropEq.resp₂ _N<_) N<-trans {_≤₂_ = Lex-< _≡_ _<_ } (Lex<-trans PropEq.isEquivalence (PropEq.resp₂ _<_) <-trans) p q)


{-# OPTIONS --cubical --no-import-sorts --safe #-}

module Cubical.Categories.Functor.Base where

open import Cubical.Foundations.Prelude
open import Cubical.Data.Sigma
open import Cubical.Categories.Category

private
  variable
    ℓC ℓC' ℓD ℓD' : Level

record Functor (C : Precategory ℓC ℓC') (D : Precategory ℓD ℓD') : Type (ℓ-max (ℓ-max ℓC ℓC') (ℓ-max ℓD ℓD')) where
  no-eta-equality
  open Precategory

  field
    F-ob : C .ob → D .ob
    F-hom : {x y : C .ob} → C [ x , y ] → D [(F-ob x) , (F-ob y)]
    F-id : {x : C .ob} → F-hom (C .id x) ≡ D .id (F-ob x)
    F-seq : {x y z : C .ob} (f : C [ x , y ]) (g : C [ y , z ]) → F-hom (f ⋆⟨ C ⟩ g) ≡ (F-hom f) ⋆⟨ D ⟩ (F-hom g)

  isFull = (x y : _) (F[f] : D [(F-ob x) , (F-ob y)]) → ∃ (C [ x , y ]) (λ f → F-hom f ≡ F[f])
  isFaithful = (x y : _) (f g : C [ x , y ]) → F-hom f ≡ F-hom g → f ≡ g
  isEssentiallySurj = ∀ (d : D .ob) → Σ[ c ∈ C .ob ] CatIso {C = D} (F-ob c) d

private
  variable
    ℓ ℓ' : Level
    C D E : Precategory ℓ ℓ'

open Precategory
open Functor

-- Helpful notation

-- action on objects
infix 30 _⟅_⟆
_⟅_⟆ : (F : Functor C D)
     → C .ob
     → D .ob
_⟅_⟆ = F-ob

-- action on morphisms
infix 30 _⟪_⟫ -- same infix level as on objects since these will never be used in the same context
_⟪_⟫ : (F : Functor C D)
     → ∀ {x y}
     → C [ x , y ]
     → D [(F ⟅ x ⟆) , (F ⟅ y ⟆)]
_⟪_⟫ = F-hom

-- Functor constructions

𝟙⟨_⟩ : ∀ (C : Precategory ℓ ℓ') → Functor C C
𝟙⟨ C ⟩ .F-ob x = x
𝟙⟨ C ⟩ .F-hom f = f
𝟙⟨ C ⟩ .F-id = refl
𝟙⟨ C ⟩ .F-seq _ _ = refl

-- functor composition
funcComp : ∀ (G : Functor D E) (F : Functor C D) → Functor C E
(funcComp G F) .F-ob c = G ⟅ F ⟅ c ⟆ ⟆
(funcComp G F) .F-hom f = G ⟪ F ⟪ f ⟫ ⟫
(funcComp {D = D} {E = E} {C = C} G F) .F-id {c}
  = (G ⟪ F ⟪ C .id c ⟫ ⟫)
  ≡⟨ cong (G ⟪_⟫) (F .F-id) ⟩
    G .F-id
  --   (G ⟪ D .id (F ⟅ c ⟆) ⟫) -- deleted this cause the extra refl composition was annoying
  -- ≡⟨ G .F-id ⟩
  --   E .id (G ⟅ F ⟅ c ⟆ ⟆)
  -- ∎
(funcComp {D = D} {E = E} {C = C} G F) .F-seq {x} {y} {z} f g
  = (G ⟪ F ⟪ f ⋆⟨ C ⟩ g ⟫ ⟫)
  ≡⟨ cong (G ⟪_⟫) (F .F-seq _ _) ⟩
    G .F-seq _ _
  --   (G ⟪ (F ⟪ f ⟫) ⋆⟨ D ⟩ (F ⟪ g ⟫) ⟫) -- deleted for same reason as above
  -- ≡⟨ G .F-seq _ _ ⟩
  --   (G ⟪ F ⟪ f ⟫ ⟫) ⋆⟨ E ⟩ (G ⟪ F ⟪ g ⟫ ⟫)
  -- ∎

infixr 30 funcComp
syntax funcComp G F = G ∘F F

_^opF : Functor C D → Functor (C ^op) (D ^op)
(F ^opF) .F-ob = F .F-ob
(F ^opF) .F-hom = F .F-hom
(F ^opF) .F-id = F .F-id
(F ^opF) .F-seq f g = F .F-seq g f


module Structure.Operator.Vector where

open import Functional using (swap)
import      Lvl
open import Logic
open import Logic.Propositional
open import Structure.Setoid
open import Structure.Operator.Field
open import Structure.Operator.Group
open import Structure.Operator.Monoid
import      Structure.Operator.Names as Names
open import Structure.Operator.Properties
open import Structure.Operator
open import Type

record VectorSpace {ℓᵥ ℓₛ ℓᵥₑ ℓₛₑ}
                   {V : Type{ℓᵥ}} ⦃ _ : Equiv{ℓᵥₑ}(V) ⦄
                   {S : Type{ℓₛ}} ⦃ _ : Equiv{ℓₛₑ}(S) ⦄
                   (_+ᵥ_ : V → V → V)
                   (_⋅ₛᵥ_ : S → V → V)
                   (_+ₛ_ : S → S → S)
                   (_⋅ₛ_ : S → S → S)
                   : Stmt{ℓₛₑ Lvl.⊔ ℓₛ Lvl.⊔ ℓᵥₑ Lvl.⊔ ℓᵥ} where
  constructor intro
  field
    ⦃ scalarField ⦄            : Field(_+ₛ_)(_⋅ₛ_)
    ⦃ vectorCommutativeGroup ⦄ : CommutativeGroup(_+ᵥ_)

  open Field(scalarField)
    renaming (
      𝟎 to 𝟎ₛ ;
      𝟏 to 𝟏ₛ ;
      _−_ to _−ₛ_ ;
      _/_ to _/ₛ_ ;
      −_ to −ₛ_ ;
      ⅟ to ⅟ₛ ;
      [+]-group              to [+ₛ]-group ;
      [+]-commutativity      to [+ₛ]-commutativity ;
      [+]-monoid             to [+ₛ]-monoid ;
      [+]-binary-operator    to [+ₛ]-binary-operator ;
      [+]-associativity      to [+ₛ]-associativity ;
      [+]-identity-existence to [+ₛ]-identity-existence ;
      [+]-identity           to [+ₛ]-identity ;
      [+]-identityₗ          to [+ₛ]-identityₗ ;
      [+]-identityᵣ          to [+ₛ]-identityᵣ ;
      [+]-inverse-existence  to [+ₛ]-inverse-existence ;
      [+]-inverse            to [+ₛ]-inverse ;
      [+]-inverseₗ           to [+ₛ]-inverseₗ ;
      [+]-inverseᵣ           to [+ₛ]-inverseᵣ ;
      [−]-function           to [−ₛ]-function ;
      [⋅]-binary-operator    to [⋅ₛ]-binary-operator ;
      [⋅]-associativity      to [⋅ₛ]-associativity ;
      [⋅]-identity-existence to [⋅ₛ]-identity-existence ;
      [⋅]-identity           to [⋅ₛ]-identity ;
      [⋅]-identityₗ          to [⋅ₛ]-identityₗ ;
      [⋅]-identityᵣ          to [⋅ₛ]-identityᵣ ;
      [⋅]-inverseₗ           to [⋅ₛ]-inverseₗ ;
      [⋅]-inverseᵣ           to [⋅ₛ]-inverseᵣ ;
      distinct-identities to distinct-identitiesₛ
    ) public
  open CommutativeGroup(vectorCommutativeGroup)
    renaming (
      id to 𝟎ᵥ ;
      inv to −ᵥ_ ;
      inv-op to _−ᵥ_ ;
      group               to [+ᵥ]-group;
      commutativity       to [+ᵥ]-commutativity;
      monoid              to [+ᵥ]-monoid ;
      binary-operator     to [+ᵥ]-binary-operator ;
      associativity       to [+ᵥ]-associativity ;
      identity-existence  to [+ᵥ]-identity-existence ;
      identity            to [+ᵥ]-identity ;
      identityₗ           to [+ᵥ]-identityₗ ;
      identityᵣ           to [+ᵥ]-identityᵣ ;
      inverse-existence   to [+ᵥ]-inverse-existence ;
      inverse             to [+ᵥ]-inverse ;
      inverseₗ            to [+ᵥ]-inverseₗ ;
      inverseᵣ            to [+ᵥ]-inverseᵣ ;
      inv-function        to [−ᵥ]-function
    ) public

  field
    ⦃ [⋅ₛᵥ]-binaryOperator ⦄      : BinaryOperator(_⋅ₛᵥ_)
    [⋅ₛ][⋅ₛᵥ]-compatibility       : Names.Compatibility(_⋅ₛ_)(_⋅ₛᵥ_) -- TODO: This is semigroup action
    ⦃ [⋅ₛᵥ]-identity ⦄            : Identityₗ(_⋅ₛᵥ_)(𝟏ₛ)
    ⦃ [⋅ₛᵥ][+ᵥ]-distributivityₗ ⦄ : Distributivityₗ(_⋅ₛᵥ_)(_+ᵥ_)
    [⋅ₛᵥ][+ₛ][+ᵥ]-distributivityᵣ : Names.DistributivityPatternᵣ(_⋅ₛᵥ_)(_+ₛ_)(_+ᵥ_) -- TODO: This is ∀? → Preserving₂

  _⋅ᵥₛ_ = swap(_⋅ₛᵥ_)

record VectorSpaceVObject {ℓᵥ ℓₛ ℓᵥₑ ℓₛₑ}
  {S : Type{ℓₛ}} ⦃ equiv-S : Equiv{ℓₛₑ}(S) ⦄
  (_+ₛ_ : S → S → S)
  (_⋅ₛ_ : S → S → S)
  : Stmt{ℓₛₑ Lvl.⊔ ℓₛ Lvl.⊔ Lvl.𝐒(ℓᵥₑ Lvl.⊔ ℓᵥ)}
  where

  constructor intro
  field
    {Vector} : Type{ℓᵥ}
    ⦃ Vector-equiv ⦄ : Equiv{ℓᵥₑ}(Vector)
    _+ᵥ_ : Vector → Vector → Vector
    _⋅ₛᵥ_ : S → Vector → Vector
    ⦃ vectorSpace ⦄ : VectorSpace(_+ᵥ_)(_⋅ₛᵥ_)(_+ₛ_)(_⋅ₛ_)
  open VectorSpace(vectorSpace) public

record VectorSpaceObject {ℓᵥ ℓₛ ℓᵥₑ ℓₛₑ} : Stmt{Lvl.𝐒(ℓₛₑ Lvl.⊔ ℓₛ Lvl.⊔ ℓᵥₑ Lvl.⊔ ℓᵥ)} where
  constructor intro
  field
    {Vector} : Type{ℓᵥ}
    ⦃ equiv-Vector ⦄ : Equiv{ℓᵥₑ}(Vector)
    {Scalar} : Type{ℓₛ}
    ⦃ equiv-Scalar ⦄ : Equiv{ℓₛₑ}(Scalar)
    _+ᵥ_  : Vector → Vector → Vector
    _⋅ₛᵥ_ : Scalar → Vector → Vector
    _+ₛ_  : Scalar → Scalar → Scalar
    _⋅ₛ_  : Scalar → Scalar → Scalar
    ⦃ vectorSpace ⦄ : VectorSpace(_+ᵥ_)(_⋅ₛᵥ_)(_+ₛ_)(_⋅ₛ_)
  open VectorSpace(vectorSpace) public


{-

This file contains:

Properties of the FreeGroup:
- FreeGroup A is Set, SemiGroup, Monoid, Group
- Recursion principle for the FreeGroup
- Induction principle for the FreeGroup on hProps
- Condition for the equality of Group Homomorphisms from FreeGroup
- Equivalence of the types (A → Group .fst) (GroupHom (freeGroupGroup A) Group)

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

module Cubical.HITs.FreeGroup.Properties where

open import Cubical.HITs.FreeGroup.Base

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Path
open import Cubical.Foundations.Function
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Equiv.BiInvertible

open import Cubical.Algebra.Group
open import Cubical.Algebra.Group.Morphisms
open import Cubical.Algebra.Group.MorphismProperties
open import Cubical.Algebra.Monoid.Base
open import Cubical.Algebra.Semigroup.Base

private
  variable
    ℓ ℓ' : Level
    A : Type ℓ

freeGroupIsSet : isSet (FreeGroup A)
freeGroupIsSet = trunc

freeGroupIsSemiGroup : IsSemigroup {A = FreeGroup A} _·_
freeGroupIsSemiGroup = issemigroup freeGroupIsSet assoc

freeGroupIsMonoid : IsMonoid {A = FreeGroup A} ε _·_
freeGroupIsMonoid = ismonoid freeGroupIsSemiGroup (λ x → sym (idr x)) (λ x → sym (idl x))

freeGroupIsGroup : IsGroup {G = FreeGroup A} ε _·_ inv
freeGroupIsGroup = isgroup freeGroupIsMonoid invr invl

freeGroupGroupStr : GroupStr (FreeGroup A)
freeGroupGroupStr = groupstr ε _·_ inv freeGroupIsGroup

-- FreeGroup is indeed a group
freeGroupGroup : Type ℓ → Group ℓ
freeGroupGroup A = FreeGroup A , freeGroupGroupStr

-- The recursion principle for the FreeGroup
rec : {Group : Group ℓ'} → (A → Group .fst) → GroupHom (freeGroupGroup A) Group
rec {Group = G , groupstr 1g _·g_ invg (isgroup (ismonoid isSemigroupg ·gIdR ·gIdL) ·gInvR ·gInvL)} f = f' , isHom
  where
  f' : FreeGroup _ → G
  f' (η a)                  = f a
  f' (g1 · g2)              = (f' g1) ·g (f' g2)
  f' ε                      = 1g
  f' (inv g)                = invg (f' g)
  f' (assoc g1 g2 g3 i)     = IsSemigroup.·Assoc isSemigroupg (f' g1) (f' g2) (f' g3) i
  f' (idr g i)              = sym (·gIdR (f' g)) i
  f' (idl g i)              = sym (·gIdL (f' g)) i
  f' (invr g i)             = ·gInvR (f' g) i
  f' (invl g i)             = ·gInvL (f' g) i
  f' (trunc g1 g2 p q i i₁) = IsSemigroup.is-set isSemigroupg (f' g1) (f' g2) (cong f' p) (cong f' q) i i₁

  isHom : IsGroupHom freeGroupGroupStr f' _
  IsGroupHom.pres·   isHom = λ g1 g2 → refl
  IsGroupHom.pres1   isHom = refl
  IsGroupHom.presinv isHom = λ g → refl

-- The induction principle for the FreeGroup for hProps
elimProp : {B : FreeGroup A → Type ℓ'}
         → ((x : FreeGroup A) → isProp (B x))
         → ((a : A) → B (η a))
         → ((g1 g2 : FreeGroup A) → B g1 → B g2 → B (g1 · g2))
         → (B ε)
         → ((g : FreeGroup A) → B g → B (inv g))
         → (x : FreeGroup A)
         → B x
elimProp {B = B} Bprop η-ind ·-ind ε-ind inv-ind = induction where
  induction : ∀ g → B g
  induction (η a) = η-ind a
  induction (g1 · g2) = ·-ind g1 g2 (induction g1) (induction g2)
  induction ε = ε-ind
  induction (inv g) = inv-ind g (induction g)
  induction (assoc g1 g2 g3 i) = path i where
    p1 : B g1
    p1 = induction g1
    p2 : B g2
    p2 = induction g2
    p3 : B g3
    p3 = induction g3
    path : PathP (λ i → B (assoc g1 g2 g3 i)) (·-ind g1 (g2 · g3) p1 (·-ind g2 g3 p2 p3))
                                              (·-ind (g1 · g2) g3 (·-ind g1 g2 p1 p2) p3)
    path = isProp→PathP (λ i → Bprop (assoc g1 g2 g3 i)) (·-ind g1 (g2 · g3) p1 (·-ind g2 g3 p2 p3))
                                                         (·-ind (g1 · g2) g3 (·-ind g1 g2 p1 p2) p3)
  induction (idr g i) = path i where
    p : B g
    p = induction g
    pε : B ε
    pε = induction ε
    path : PathP (λ i → B (idr g i)) p (·-ind g ε p pε)
    path = isProp→PathP (λ i → Bprop (idr g i)) p (·-ind g ε p pε)
  induction (idl g i) = path i where
    p : B g
    p = induction g
    pε : B ε
    pε = induction ε
    path : PathP (λ i → B (idl g i)) p (·-ind ε g pε p)
    path = isProp→PathP (λ i → Bprop (idl g i)) p (·-ind ε g pε p)
  induction (invr g i) = path i where
    p : B g
    p = induction g
    pinv : B (inv g)
    pinv = inv-ind g p
    pε : B ε
    pε = induction ε
    path : PathP (λ i → B (invr g i)) (·-ind g (inv g) p pinv) pε
    path = isProp→PathP (λ i → Bprop (invr g i)) (·-ind g (inv g) p pinv) pε
  induction (invl g i) = path i where
    p : B g
    p = induction g
    pinv : B (inv g)
    pinv = inv-ind g p
    pε : B ε
    pε = induction ε
    path : PathP (λ i → B (invl g i)) (·-ind (inv g) g pinv p) pε
    path = isProp→PathP (λ i → Bprop (invl g i)) (·-ind (inv g) g pinv p) pε
  induction (trunc g1 g2 q1 q2 i i₁) = square i i₁ where
    p1 : B g1
    p1 = induction g1
    p2 : B g2
    p2 = induction g2
    dq1 : PathP (λ i → B (q1 i)) p1 p2
    dq1 = cong induction q1
    dq2 : PathP (λ i → B (q2 i)) p1 p2
    dq2 = cong induction q2
    square : SquareP (λ i i₁ → B (trunc g1 g2 q1 q2 i i₁))
                {a₀₀ = p1} {a₀₁ = p2} dq1
                {a₁₀ = p1} {a₁₁ = p2} dq2
                (cong induction (refl {x = g1})) (cong induction (refl {x = g2}))
    square = isProp→SquareP (λ i i₁ → Bprop (trunc g1 g2 q1 q2 i i₁))
             (cong induction (refl {x = g1})) (cong induction (refl {x = g2}))
             dq1 dq2

-- Two group homomorphisms from FreeGroup to G are the same if they agree on every a : A
freeGroupHom≡ : {Group : Group ℓ'}{f g : GroupHom (freeGroupGroup A) Group}
               → (∀ a → (fst f) (η a) ≡ (fst g) (η a)) → f ≡ g
freeGroupHom≡ {Group = G , (groupstr 1g _·g_ invg isGrp)} {f} {g} eqOnA = GroupHom≡ (funExt pointwise) where
  B : ∀ x → Type _
  B x = (fst f) x ≡ (fst g) x
  Bprop : ∀ x → isProp (B x)
  Bprop x = (isSetGroup (G , groupstr 1g _·g_ invg isGrp)) ((fst f) x) ((fst g) x)
  η-ind : ∀ a → B (η a)
  η-ind = eqOnA
  ·-ind : ∀ g1 g2 → B g1 → B g2 → B (g1 · g2)
  ·-ind g1 g2 ind1 ind2 =
    (fst f) (g1 · g2)
    ≡⟨ IsGroupHom.pres· (f .snd) g1 g2 ⟩
    ((fst f) g1) ·g ((fst f) g2)
    ≡⟨ cong (λ x → x ·g ((fst f) g2)) ind1 ⟩
    ((fst g) g1) ·g ((fst f) g2)
    ≡⟨ cong (λ x → ((fst g) g1) ·g x) ind2 ⟩
    ((fst g) g1) ·g ((fst g) g2)
    ≡⟨ sym (IsGroupHom.pres· (g .snd) g1 g2) ⟩
    (fst g) (g1 · g2) ∎
  ε-ind : B ε
  ε-ind =
    (fst f) ε
    ≡⟨ IsGroupHom.pres1 (f .snd) ⟩
    1g
    ≡⟨ sym (IsGroupHom.pres1 (g .snd)) ⟩
    (fst g) ε ∎
  inv-ind : ∀ x → B x → B (inv x)
  inv-ind x ind =
    (fst f) (inv x)
    ≡⟨ IsGroupHom.presinv (f .snd) x ⟩
    invg ((fst f) x)
    ≡⟨ cong invg ind ⟩
    invg ((fst g) x)
    ≡⟨ sym (IsGroupHom.presinv (g .snd) x) ⟩
    (fst g) (inv x) ∎
  pointwise : ∀ x → (fst f) x ≡ (fst g) x
  pointwise = elimProp Bprop η-ind ·-ind ε-ind inv-ind

-- The type of Group Homomorphisms from the FreeGroup A into G
-- is equivalent to the type of functions from A into G .fst
A→Group≃GroupHom : {Group : Group ℓ'} → (A → Group .fst) ≃ GroupHom (freeGroupGroup A) Group
A→Group≃GroupHom {Group = Group} = biInvEquiv→Equiv-right biInv where
  biInv : BiInvEquiv (A → Group .fst) (GroupHom (freeGroupGroup A) Group)
  BiInvEquiv.fun  biInv              = rec
  BiInvEquiv.invr biInv (hom , _) a  = hom (η a)
  BiInvEquiv.invr-rightInv biInv hom = freeGroupHom≡ (λ a → refl)
  BiInvEquiv.invl biInv (hom ,  _) a = hom (η a)
  BiInvEquiv.invl-leftInv biInv f    = funExt (λ a → refl)


open import MLib.Algebra.PropertyCode
open import MLib.Algebra.PropertyCode.Structures

module MLib.Matrix.SemiTensor.Core {c ℓ} (struct : Struct bimonoidCode c ℓ) where

open import MLib.Prelude
open import MLib.Matrix.Core
open import MLib.Matrix.Equality struct
open import MLib.Matrix.Mul struct
open import MLib.Matrix.Tensor struct
open import MLib.Algebra.Operations struct

open Nat using () renaming (_+_ to _+ℕ_; _*_ to _*ℕ_)

open import MLib.Fin.Parts.Simple

open import Data.Nat.LCM
open import Data.Nat.Divisibility

chunkVec : ∀ {m n} → Table S (m *ℕ n) → Table (Table S n) m
chunkVec {m} {n} t .lookup i .lookup j = lookup t (fromParts (i , j))

-- Case 1 of semi-tensor inner product of vectors

_⋉ᵥ₁_ : ∀ {n t} → Table S (t *ℕ n) → Table S t → Table S n
(_⋉ᵥ₁_ {n} {t} X Y) .lookup i = ∑[ k < t ] (X′ .lookup k .lookup i *′ Y .lookup k)
  where X′ = chunkVec {t} X

-- Case 2 of semi-tensor inner product of vector

_⋉ᵥ₂_ : ∀ {n s} → Table S s → Table S (s *ℕ n) → Table S n
(_⋉ᵥ₂_ {n} {s} X Y) .lookup i = ∑[ k < s ] (X .lookup k *′ Y′ .lookup k .lookup i)
  where Y′ = chunkVec {s} Y

module Defn {n p t : ℕ} (lcm : LCM n p t) where
  -- Left semi-Tensor product

  n∣t = LCM.commonMultiple lcm .proj₁
  p∣t = LCM.commonMultiple lcm .proj₂

  t/n = quotient n∣t
  t/p = quotient p∣t

  Iₜₙ = 1● {t/n}
  Iₜₚ = 1● {t/p}

  module _ where
    open ≡.Reasoning

    abstract
      lem₁ : n *ℕ t/n ≡ t
      lem₁ = begin
        n *ℕ t/n ≡⟨ Nat.*-comm n _ ⟩
        t/n *ℕ n ≡⟨ ≡.sym (_∣_.equality n∣t) ⟩
        t ∎

      lem₂ : p *ℕ t/p ≡ t
      lem₂ = begin
        p *ℕ t/p ≡⟨ Nat.*-comm p _ ⟩
        t/p *ℕ p ≡⟨ ≡.sym (_∣_.equality p∣t) ⟩
        t ∎

  module _ {m q} (A : Matrix S m n) (B : Matrix S p q) where
    A′ = A ⊠ Iₜₙ
    B′ = B ⊠ Iₜₚ
    A′′ = ≡.subst (Matrix S (m *ℕ t/n)) {y = t} lem₁ A′
    B′′ = ≡.subst (λ h → Matrix S h (q *ℕ t/p)) {y = t} lem₂ B′

    stp : Matrix S (m *ℕ t/n) (q *ℕ t/p)
    stp = A′′ ⊗ B′′

infixl 7 _⋉_

_⋉_ : ∀ {m n p q} → Matrix S m n → Matrix S p q → Matrix S _ _
_⋉_ {m} {n} {p} {q} = Defn.stp {n} {p} (lcm n p .proj₂)


------------------------------------------------------------------------
-- The Agda standard library
--
-- Some properties of reflexive closures which rely on the K rule
------------------------------------------------------------------------

{-# OPTIONS --safe --with-K #-}

module Relation.Binary.Construct.Closure.Reflexive.Properties.WithK where

open import Data.Empty.Irrelevant using (⊥-elim)
open import Data.Product as Prod
open import Data.Sum.Base as Sum
open import Relation.Binary
open import Relation.Binary.Construct.Closure.Reflexive
open import Relation.Binary.Construct.Closure.Reflexive.Properties public
open import Relation.Binary.PropositionalEquality as PropEq using (_≡_; refl; cong)
open import Relation.Nullary.Negation using (contradiction)

module _ {a ℓ} {A : Set a} {_∼_ : Rel A ℓ} where

  irrel : Irrelevant _∼_ → Irreflexive _≡_ _∼_ → Irrelevant (Refl _∼_)
  irrel irrel irrefl [ x∼y₁ ] [ x∼y₂ ] = cong [_] (irrel x∼y₁ x∼y₂)
  irrel irrel irrefl [ x∼y ]  refl     = contradiction x∼y (irrefl refl)
  irrel irrel irrefl refl     [ x∼y ]  = contradiction x∼y (irrefl refl)
  irrel irrel irrefl refl     refl     = refl


module Int where

open import Data.Product
open import Data.Sum as S
open import Relation.Binary.PropositionalEquality as Eq
open import Relation.Binary.Core
open import Relation.Binary
open import Function.Equality hiding (cong)
open import Data.Empty
import Level
open import Nat

open Eq.≡-Reasoning

ℤb : Set
ℤb = ℕ × ℕ

_≡ℤ_ : ℤb → ℤb → Set
(a , b) ≡ℤ (c , d) = a + d ≡ b + c

reflexive : {x : ℤb} → x ≡ℤ x
reflexive {a , b} = +-comm a b
symmetric : (x y : ℤb) → x ≡ℤ y → y ≡ℤ x
symmetric (a , b) (c , d) a+d≡b+c = begin
        c + b
    ≡⟨ +-comm c b ⟩
        b + c
    ≡⟨ sym a+d≡b+c ⟩
        a + d
    ≡⟨ +-comm a d ⟩
        d + a
    ∎
transitive : ∀ {x y z : ℤb} → x ≡ℤ y → y ≡ℤ z → x ≡ℤ z
transitive {a , b} {c , d} {e , f} a+d≡b+c c+f≡d+e = lem (begin
        a + ((d + c) + f)
    ≡⟨ cong (λ x → a + x) (sym (+-assoc d c f)) ⟩
        a + (d + (c + f))
    ≡⟨ +-assoc a d (c + f) ⟩
        (a + d) + (c + f)
    ≡⟨ cong (λ x → x + (c + f)) a+d≡b+c ⟩
        (b + c) + (c + f)
    ≡⟨ cong (λ x → (b + c) + x) c+f≡d+e ⟩
        (b + c) + (d + e)
    ≡⟨ sym (+-assoc b c (d + e)) ⟩
        b + (c + (d + e))
    ≡⟨ cong (λ x → b + x) (+-assoc c d e) ⟩
        b + ((c + d) + e)
    ∎)
    where
    lem : (a + ((d + c) + f)) ≡ (b + ((c + d) + e)) → (a + f) ≡ (b + e)
    lem p = +-rCancel (a + f) (b + e) (d + c) (begin
            (a + f) + (d + c)
        ≡⟨ sym (+-assoc a f (d + c)) ⟩
            a + (f + (d + c))
        ≡⟨ cong (λ x → a + x) (+-comm f (d + c)) ⟩
            a + ((d + c) + f)
        ≡⟨ p ⟩
            b + ((c + d) + e)
        ≡⟨ cong (λ x → b + x) (cong (λ x → x + e) (+-comm c d)) ⟩
            b + ((d + c) + e)
        ≡⟨ cong (λ x → b + x) (+-comm (d + c) e) ⟩
            b + (e + (d + c))
        ≡⟨ +-assoc b e (d + c) ⟩
            (b + e) + (d + c)
        ∎)

≡ℤ-eq : IsEquivalence _≡ℤ_
≡ℤ-eq = record
        { refl = λ {x} → reflexive {x}
        ; sym = λ {x y} → symmetric x y
        ; trans = λ {x y z} → transitive {x} {y} {z} }

ℤ : Setoid (Level.zero) (Level.zero)
ℤ = record { Carrier = ℤb ; _≈_ = _≡ℤ_ ; isEquivalence = ≡ℤ-eq }

≡ℤ-≡ : {a b : ℤb} → a ≡ b → a ≡ℤ b
≡ℤ-≡ {x , y} {.(x , y)} refl = +-comm x y

import Relation.Binary.EqReasoning as EqR
open EqR ℤ renaming
    ( begin_ to beginℤ_
    ; _≡⟨_⟩_ to _≡ℤ⟨_⟩_
    ; _∎ to _ℤ∎
    )

_+ℤ_ : ℤb → ℤb → ℤb
(a , b) +ℤ (c , d) = (a + c , b + d)

_*ℤ_ : ℤb → ℤb → ℤb
(a , b) *ℤ (c , d) = ((a * c) + (b * d) , (a * d) + (b * c))

+ℤ-lUniv : (a b c : ℤb) → a ≡ℤ b → (a +ℤ c) ≡ℤ (b +ℤ c)
+ℤ-lUniv (a₁ , a₂) (b₁ , b₂) (c₁ , c₂) a₁+b₂≡a₂+b₁ = lem
    where
        lem : (a₁ + c₁) + (b₂ + c₂) ≡ (a₂ + c₂) + (b₁ + c₁)
        lem = begin
                (a₁ + c₁) + (b₂ + c₂)
            ≡⟨ sym (+-assoc a₁ c₁ (b₂ + c₂)) ⟩
                a₁ + (c₁ + (b₂ + c₂))
            ≡⟨ cong (λ x → a₁ + x) (+-assoc c₁ b₂ c₂) ⟩
                a₁ + ((c₁ + b₂) + c₂)
            ≡⟨ cong (λ x → a₁ + x) (cong (λ x → x + c₂) (+-comm c₁ b₂)) ⟩
                a₁ + ((b₂ + c₁) + c₂)
            ≡⟨ cong (λ x → a₁ + x) (sym (+-assoc b₂ c₁ c₂)) ⟩
                a₁ + (b₂ + (c₁ + c₂))
            ≡⟨ +-assoc a₁ b₂ (c₁ + c₂) ⟩
                (a₁ + b₂) + (c₁ + c₂)
            ≡⟨ cong (λ x → x + (c₁ + c₂)) a₁+b₂≡a₂+b₁ ⟩
                (a₂ + b₁) + (c₁ + c₂)
            ≡⟨ sym (+-assoc a₂ b₁ (c₁ + c₂)) ⟩
                a₂ + (b₁ + (c₁ + c₂))
            ≡⟨ cong (λ x → a₂ + x) (+-assoc b₁ c₁ c₂) ⟩
                a₂ + ((b₁ + c₁) + c₂)
            ≡⟨ cong (λ x → a₂ + x) (+-comm (b₁ + c₁) c₂) ⟩
                a₂ + (c₂ + (b₁ + c₁))
            ≡⟨ +-assoc a₂ c₂ (b₁ + c₁) ⟩
                (a₂ + c₂) + (b₁ + c₁)
            ∎

-- c +ℤ a = (c₁ + a₁ , c₂ + a₂)
-- c +ℤ b = (c₁ + b₁ , c₂ + b₂)
-- (c₁ + a₁) + (c₂ + b₂) ≡ (c₂ + a₂) + (c₁ + b₁)
+ℤ-rUniv : (a b c : ℤb) → a ≡ℤ b → (c +ℤ a) ≡ℤ (c +ℤ b)
+ℤ-rUniv (a₁ , a₂) (b₁ , b₂) (c₁ , c₂) a₁+b₂≡a₂+b₁ = begin
        (c₁ + a₁) + (c₂ + b₂)
    ≡⟨ cong (λ x → (c₁ + a₁) + x) (+-comm c₂ b₂) ⟩
        (c₁ + a₁) + (b₂ + c₂)
    ≡⟨ sym (+-assoc c₁ a₁ (b₂ + c₂)) ⟩
        c₁ + (a₁ + (b₂ + c₂))
    ≡⟨ cong (λ x → c₁ + x) (+-assoc a₁ b₂ c₂) ⟩
        c₁ + ((a₁ + b₂) + c₂)
    ≡⟨ cong (λ x → c₁ + (x + c₂)) a₁+b₂≡a₂+b₁ ⟩
        c₁ + ((a₂ + b₁) + c₂)
    ≡⟨ +-comm c₁ ((a₂ + b₁) + c₂) ⟩
        ((a₂ + b₁) + c₂) + c₁
    ≡⟨ cong (λ x → x + c₁) (+-comm (a₂ + b₁) c₂) ⟩
        (c₂ + (a₂ + b₁)) + c₁
    ≡⟨ sym (+-assoc c₂ (a₂ + b₁) c₁) ⟩
        c₂ + ((a₂ + b₁) + c₁)
    ≡⟨ cong (λ x → c₂ + x) (sym (+-assoc a₂ b₁ c₁)) ⟩
        c₂ + (a₂ + (b₁ + c₁))
    ≡⟨ cong (λ x → c₂ + (a₂ + x)) (+-comm b₁ c₁) ⟩
        c₂ + (a₂ + (c₁ + b₁))
    ≡⟨ +-assoc c₂ a₂ (c₁ + b₁) ⟩
        (c₂ + a₂) + (c₁ + b₁)
    ∎

*ℤ-lUniv : (a b c : ℤb) → a ≡ℤ b → (a *ℤ c) ≡ℤ (b *ℤ c)
*ℤ-lUniv (a₁ , a₂) (b₁ , b₂) (c₁ , c₂) a₁+b₂≡a₂+b₁ = lem
    where
        -- a *ℤ c = (a₁ * c₁ + a₂ * c₂ , a₁ * c₂ + a₂ * c₁)
        -- b *ℤ c = (b₁ * c₁ + b₂ * c₂ , b₁ * c₂ + b₂ * c₁)
        lem : ((a₁ * c₁) + (a₂ * c₂)) + ((b₁ * c₂) + (b₂ * c₁))
                ≡ ((a₁ * c₂) + (a₂ * c₁)) + ((b₁ * c₁) + (b₂ * c₂))
        lem = begin
                ((a₁ * c₁) + (a₂ * c₂)) + ((b₁ * c₂) + (b₂ * c₁))
            ≡⟨ cong (λ x → ((a₁ * c₁) + (a₂ * c₂)) + x) (+-comm (b₁ * c₂) (b₂ * c₁)) ⟩
                ((a₁ * c₁) + (a₂ * c₂)) + ((b₂ * c₁) + (b₁ * c₂))
            ≡⟨ sym (+-assoc (a₁ * c₁) (a₂ * c₂) ((b₂ * c₁) + (b₁ * c₂))) ⟩
                (a₁ * c₁) + ((a₂ * c₂) + ((b₂ * c₁) + (b₁ * c₂)))
            ≡⟨ cong (λ x → (a₁ * c₁) + x) (+-assoc (a₂ * c₂) (b₂ * c₁) (b₁ * c₂)) ⟩
                (a₁ * c₁) + (((a₂ * c₂) + (b₂ * c₁)) + (b₁ * c₂))
            ≡⟨ cong (λ x → (a₁ * c₁) + x) (cong (λ x → x + (b₁ * c₂)) (+-comm (a₂ * c₂) (b₂ * c₁))) ⟩
                (a₁ * c₁) + (((b₂ * c₁) + (a₂ * c₂)) + (b₁ * c₂))
            ≡⟨ cong (λ x → (a₁ * c₁) + x) (sym (+-assoc (b₂ * c₁) (a₂ * c₂) (b₁ * c₂))) ⟩
                (a₁ * c₁) + ((b₂ * c₁) + ((a₂ * c₂) + (b₁ * c₂)))
            ≡⟨ +-assoc (a₁ * c₁) (b₂ * c₁) ((a₂ * c₂) + (b₁ * c₂)) ⟩
                ((a₁ * c₁) + (b₂ * c₁)) + ((a₂ * c₂) + (b₁ * c₂))
            ≡⟨ cong (λ x → x + ((a₂ * c₂) + (b₁ * c₂))) (sym (+*-rDist a₁ b₂ c₁))  ⟩
                ((a₁ + b₂) * c₁) + ((a₂ * c₂) + (b₁ * c₂))
            ≡⟨ cong (λ x → ((a₁ + b₂) * c₁) + x) (sym (+*-rDist a₂ b₁ c₂)) ⟩
                ((a₁ + b₂) * c₁) + ((a₂ + b₁) * c₂)
            ≡⟨ cong (λ x → x + ((a₂ + b₁) * c₂)) (cong (λ x → x * c₁) a₁+b₂≡a₂+b₁) ⟩
                ((a₂ + b₁) * c₁) + ((a₂ + b₁) * c₂)
            ≡⟨ cong (λ x → ((a₂ + b₁) * c₁) + x) (cong (λ x → x * c₂) (sym a₁+b₂≡a₂+b₁)) ⟩
                ((a₂ + b₁) * c₁) + ((a₁ + b₂) * c₂)
            ≡⟨ cong (λ x → x + ((a₁ + b₂) * c₂)) (+*-rDist a₂ b₁ c₁) ⟩
                ((a₂ * c₁) + (b₁ * c₁)) + ((a₁ + b₂) * c₂)
            ≡⟨ cong (λ x → ((a₂ * c₁) + (b₁ * c₁)) + x) (+*-rDist a₁ b₂ c₂) ⟩
                ((a₂ * c₁) + (b₁ * c₁)) + ((a₁ * c₂) + (b₂ * c₂))
            ≡⟨ sym (+-assoc (a₂ * c₁) (b₁ * c₁) ((a₁ * c₂) + (b₂ * c₂))) ⟩
                (a₂ * c₁) + ((b₁ * c₁) + ((a₁ * c₂) + (b₂ * c₂)))
            ≡⟨ cong (λ x → (a₂ * c₁) + x) (+-assoc (b₁ * c₁) (a₁ * c₂) (b₂ * c₂)) ⟩
                (a₂ * c₁) + (((b₁ * c₁) + (a₁ * c₂)) + (b₂ * c₂))
            ≡⟨ cong (λ x → (a₂ * c₁) + x) (cong (λ x → x + (b₂ * c₂)) (+-comm (b₁ * c₁) (a₁ * c₂))) ⟩
                (a₂ * c₁) + (((a₁ * c₂) + (b₁ * c₁)) + (b₂ * c₂))
            ≡⟨ cong (λ x → (a₂ * c₁) + x) (sym (+-assoc (a₁ * c₂) (b₁ * c₁) (b₂ * c₂))) ⟩
                (a₂ * c₁) + ((a₁ * c₂) + ((b₁ * c₁) + (b₂ * c₂)))
            ≡⟨ +-assoc (a₂ * c₁) (a₁ * c₂) ((b₁ * c₁) + (b₂ * c₂)) ⟩
                ((a₂ * c₁) + (a₁ * c₂)) + ((b₁ * c₁) + (b₂ * c₂))
            ≡⟨ cong (λ x → x + ((b₁ * c₁) + (b₂ * c₂))) (+-comm (a₂ * c₁) (a₁ * c₂)) ⟩
                ((a₁ * c₂) + (a₂ * c₁)) + ((b₁ * c₁) + (b₂ * c₂))
            ∎

-- c *ℤ a = (c₁ * a₁) + (c₂ * a₂) , (c₁ * a₂) + (c₂ * a₁)
-- c *ℤ b = (c₁ * b₁) + (c₂ * b₂) , (c₁ * b₂) + (c₂ * b₁)
*ℤ-rUniv : (a b c : ℤb) → a ≡ℤ b → (c *ℤ a) ≡ℤ (c *ℤ b)
*ℤ-rUniv (a₁ , a₂) (b₁ , b₂) (c₁ , c₂) a₁+b₂≡a₂+b₁ = begin
        ((c₁ * a₁) + (c₂ * a₂)) + ((c₁ * b₂) + (c₂ * b₁))
    ≡⟨ +-assoc ((c₁ * a₁) + (c₂ * a₂)) (c₁ * b₂) (c₂ * b₁) ⟩
        (((c₁ * a₁) + (c₂ * a₂)) + (c₁ * b₂)) + (c₂ * b₁)
    ≡⟨ cong (λ x → x + (c₂ * b₁)) (sym (+-assoc (c₁ * a₁) (c₂ * a₂) (c₁ * b₂))) ⟩
        ((c₁ * a₁) + ((c₂ * a₂) + (c₁ * b₂))) + (c₂ * b₁)
    ≡⟨ cong (λ z → ((c₁ * a₁) + z) + (c₂ * b₁)) (+-comm (c₂ * a₂) (c₁ * b₂)) ⟩
        ((c₁ * a₁) + ((c₁ * b₂) + (c₂ * a₂))) + (c₂ * b₁)
    ≡⟨ cong (λ z → z + (c₂ * b₁)) (+-assoc (c₁ * a₁) (c₁ * b₂) (c₂ * a₂)) ⟩
        (((c₁ * a₁) + (c₁ * b₂)) + (c₂ * a₂)) + (c₂ * b₁)
    ≡⟨ sym (+-assoc ((c₁ * a₁) + (c₁ * b₂)) (c₂ * a₂) (c₂ * b₁)) ⟩
        ((c₁ * a₁) + (c₁ * b₂)) + ((c₂ * a₂) + (c₂ * b₁))
    ≡⟨ cong (λ z → z + ((c₂ * a₂) + (c₂ * b₁))) (sym (+*-lDist a₁ b₂ c₁)) ⟩
        (c₁ * (a₁ + b₂)) + ((c₂ * a₂) + (c₂ * b₁))
    ≡⟨ cong (λ z → (c₁ * (a₁ + b₂)) + z) (sym (+*-lDist a₂ b₁ c₂)) ⟩
        (c₁ * (a₁ + b₂)) + (c₂ * (a₂ + b₁))
    ≡⟨ cong (λ z → (c₁ * z) + (c₂ * (a₂ + b₁))) a₁+b₂≡a₂+b₁ ⟩
        (c₁ * (a₂ + b₁)) + (c₂ * (a₂ + b₁))
    ≡⟨ cong (λ z → (c₁ * (a₂ + b₁)) + (c₂ * z)) (sym a₁+b₂≡a₂+b₁) ⟩
        (c₁ * (a₂ + b₁)) + (c₂ * (a₁ + b₂))
    ≡⟨ cong (λ z → z + (c₂ * (a₁ + b₂))) (+*-lDist a₂ b₁ c₁) ⟩
        ((c₁ * a₂) + (c₁ * b₁)) + (c₂ * (a₁ + b₂))
    ≡⟨ cong (λ z → ((c₁ * a₂) + (c₁ * b₁)) + z) (+*-lDist a₁ b₂ c₂) ⟩
        ((c₁ * a₂) + (c₁ * b₁)) + ((c₂ * a₁) + (c₂ * b₂))
    ≡⟨ sym (+-assoc (c₁ * a₂) (c₁ * b₁) ((c₂ * a₁) + (c₂ * b₂))) ⟩
        (c₁ * a₂) + ((c₁ * b₁) + ((c₂ * a₁) + (c₂ * b₂)))
    ≡⟨ cong (λ z → (c₁ * a₂) + z) (+-assoc (c₁ * b₁) (c₂ * a₁) (c₂ * b₂)) ⟩
        (c₁ * a₂) + (((c₁ * b₁) + (c₂ * a₁)) + (c₂ * b₂))
    ≡⟨ cong (λ z → (c₁ * a₂) + (z + (c₂ * b₂))) (+-comm (c₁ * b₁) (c₂ * a₁)) ⟩
        (c₁ * a₂) + (((c₂ * a₁) + (c₁ * b₁)) + (c₂ * b₂))
    ≡⟨ cong (λ z → (c₁ * a₂) + z) (sym (+-assoc (c₂ * a₁) (c₁ * b₁) (c₂ * b₂))) ⟩
        (c₁ * a₂) + ((c₂ * a₁) + ((c₁ * b₁) + (c₂ * b₂)))
    ≡⟨ +-assoc (c₁ * a₂) (c₂ * a₁) ((c₁ * b₁) + (c₂ * b₂)) ⟩
        ((c₁ * a₂) + (c₂ * a₁)) + ((c₁ * b₁) + (c₂ * b₂))
    ∎

+ℤ-rUnit : (a b : ℕ) → ((a , b) +ℤ (zero , zero)) ≡ (a , b)
+ℤ-rUnit a b = begin
        (a , b) +ℤ (zero , zero)
    ≡⟨ refl ⟩
        (a + zero , b + zero)
    ≡⟨ cong (λ x → x , (b + zero)) (+-rUnit a) ⟩
        (a , b + zero)
    ≡⟨ cong (λ x → a , x) (+-rUnit b) ⟩
        (a , b)
    ∎

+ℤ-lUnit : (a b : ℕ) → ((zero , zero) +ℤ (a , b)) ≡ (a , b)
+ℤ-lUnit a b = begin
        (zero , zero) +ℤ (a , b)
    ≡⟨ refl ⟩
        (zero + a , zero + b)
    ≡⟨ refl ⟩
        (a , b)
    ∎

-- (a , b) *ℤ (succ zero , zero) = (a * succ zero + b * zero , a * zero + b * succ zero)
-- a + (a * zero + b * succ zero) = b + (a * succ zero + b * zero)
*ℤ-rUnit : (a b : ℕ) → ((a , b) *ℤ (succ zero , zero)) ≡ (a , b)
*ℤ-rUnit a b = begin
        ((a * succ zero) + (b * zero) , ((a * zero) + (b * succ zero)))
    ≡⟨ cong (λ x → x , ((a * zero) + (b * succ zero))) lem1 ⟩
        (a , ((a * zero) + (b * succ zero)))
    ≡⟨ cong (λ x → a , x) lem2 ⟩
        (a , b)
    ∎
    where
        lem1 : (a * succ zero) + (b * zero) ≡ a
        lem1 = begin
                (a * succ zero) + (b * zero)
            ≡⟨ cong (λ x → x + (b * zero)) (*-rUnit a) ⟩
                a + (b * zero)
            ≡⟨ cong (λ x → a + x) (*-rAbsorp b) ⟩
                a + zero
            ≡⟨ +-rUnit a ⟩
                a
            ∎
        lem2 : (a * zero) + (b * succ zero) ≡ b
        lem2 = begin
                (a * zero) + (b * succ zero)
            ≡⟨ cong (λ x → (a * zero) + x) (*-rUnit b) ⟩
                (a * zero) + b
            ≡⟨ cong (λ x → x + b) (*-rAbsorp a) ⟩
                zero + b
            ≡⟨ refl ⟩
                b
            ∎

-- ((succ zero * a) + (zero * b) , (succ zero * b) + (zero * a))
*ℤ-lUnit : (a b : ℕ) → ((succ zero , zero) *ℤ (a , b)) ≡ (a , b)
*ℤ-lUnit a b = begin
        ((succ zero * a) + (zero * b) , (succ zero * b) + (zero * a))
    ≡⟨ cong (λ x → x , ((succ zero * b) + (zero * a))) lem1 ⟩
        (a , (succ zero * b) + (zero * a))
    ≡⟨ cong (λ x → a , x) lem2 ⟩
        (a , b)
    ∎
    where
        lem1 : (succ zero * a) + (zero * b) ≡ a
        lem1 = begin
                (succ zero * a) + (zero * b)
            ≡⟨ cong (λ x → x + (zero * b)) (*-lUnit a) ⟩
                a + (zero * b)
            ≡⟨ cong (λ x → a + x) refl ⟩
                a + zero
            ≡⟨ +-rUnit a ⟩
                a
            ∎
        lem2 : (succ zero * b) + (zero * a) ≡ b
        lem2 = begin
                (succ zero * b) + (zero * a)
            ≡⟨ cong (λ x → x + (zero * a)) (*-lUnit b) ⟩
                b + (zero * a)
            ≡⟨ cong (λ x → b + x) refl ⟩
                b + zero
            ≡⟨ +-rUnit b ⟩
                b
            ∎

-- (b +ℤ c) = (b₁ + c₁) , (b₂ + c₂)
-- a +ℤ (b +ℤ c) = a₁ + (b₁ + c₁) , a₂ + (b₂ + c₂)
-- (a +ℤ b) = a₁ + b₁ , a₂ + b₂
-- (a +ℤ b) + c = (a₁ + b₁) + c₁ , (a₂ + b₂) + c₂
-- (a₁ + (b₁ + c₁)) + ((a₂ + b₂) + c₂) ≡ (a₂ + (b₂ + c₂)) + ((a₁ + b₁) + c₁)
+ℤ-assoc : (a b c : ℤb) → (a +ℤ (b +ℤ c)) ≡ℤ ((a +ℤ b) +ℤ c)
+ℤ-assoc (a₁ , a₂) (b₁ , b₂) (c₁ , c₂) = begin
        (a₁ + (b₁ + c₁)) + ((a₂ + b₂) + c₂)
    ≡⟨ +-comm (a₁ + (b₁ + c₁)) ((a₂ + b₂) + c₂) ⟩
        ((a₂ + b₂) + c₂) + (a₁ + (b₁ + c₁))
    ≡⟨ cong (λ x → x + (a₁ + (b₁ + c₁))) (sym (+-assoc a₂ b₂ c₂)) ⟩
        (a₂ + (b₂ + c₂)) + (a₁ + (b₁ + c₁))
    ≡⟨ cong (λ x → (a₂ + (b₂ + c₂)) + x) (+-assoc a₁ b₁ c₁) ⟩
        (a₂ + (b₂ + c₂)) + ((a₁ + b₁) + c₁)
    ∎

-- a +ℤ b = a + c , b + d
-- b +ℤ a = c + a , d + b
-- (a + c) + (d + b) = (b + d) + (c + a)
+ℤ-comm : (a b : ℤb) → (a +ℤ b) ≡ℤ (b +ℤ a)
+ℤ-comm (a , b) (c , d) = begin
        (a + c) + (d + b)
    ≡⟨ +-comm (a + c) (d + b) ⟩
        (d + b) + (a + c)
    ≡⟨ cong (λ x → x + (a + c)) (+-comm d b) ⟩
        (b + d) + (a + c)
    ≡⟨ cong (λ x → (b + d) + x) (+-comm a c) ⟩
        (b + d) + (c + a)
    ∎

-- b *ℤ c = (b₁ * c₁) + (b₂ * c₂) , (b₁ * c₂) + (b₂ * c₁)
-- a *ℤ (b *ℤ c) = (a₁ * ((b₁ * c₁) + (b₂ * c₂))) + (a₂ * ((b₁ * c₂) + (b₂ * c₁)))
--               , (a₁ * ((b₁ * c₂) + (b₂ * c₁))) + (a₂ * ((b₁ * c₁) + (b₂ * c₂)))
-- a *ℤ b = (a₁ * b₁) + (a₂ * b₂) , (a₁ * b₂) + (a₂ * b₁)
-- (a *ℤ b) *ℤ c = (((a₁ * b₁) + (a₂ * b₂)) * c₁) + (((a₁ * b₂) + (a₂ * b₁)) * c₂)
--               , (((a₁ * b₂) + (a₂ * b₁)) * c₁) + (((a₁ * b₁) + (a₂ * b₂)) * c₂)
-- (a₁ * ((b₁ * c₁) + (b₂ * c₂))) + (a₂ * ((b₁ * c₂) + (b₂ * c₁)))
-- + (((a₁ * b₂) + (a₂ * b₁)) * c₁) + (((a₁ * b₁) + (a₂ * b₂)) * c₂)
-- ≡
-- (a₁ * ((b₁ * c₂) + (b₂ * c₁))) + (a₂ * ((b₁ * c₁) + (b₂ * c₂)))
-- + (((a₁ * b₁) + (a₂ * b₂)) * c₁) + (((a₁ * b₂) + (a₂ * b₁)) * c₂)
*ℤ-assoc : (a b c : ℤb) → (a *ℤ (b *ℤ c)) ≡ℤ ((a *ℤ b) *ℤ c)
*ℤ-assoc a@(a₁ , a₂) b@(b₁ , b₂) c@(c₁ , c₂) = ≡ℤ-≡ (begin
        a *ℤ (b *ℤ c)
    ≡⟨ cong (λ x → a *ℤ x) refl ⟩
        a *ℤ ((b₁ * c₁) + (b₂ * c₂) , (b₁ * c₂) + (b₂ * c₁))
    ≡⟨ refl ⟩
        ((a₁ * ((b₁ * c₁) + (b₂ * c₂))) + (a₂ * ((b₁ * c₂) + (b₂ * c₁))) ,
            ((a₁ * ((b₁ * c₂) + (b₂ * c₁))) + (a₂ * ((b₁ * c₁) + (b₂ * c₂)))))
    ≡⟨ cong (λ z → z , (((a₁ * ((b₁ * c₂) + (b₂ * c₁))) + (a₂ * ((b₁ * c₁) + (b₂ * c₂)))))) lem1 ⟩
        ((((a₁ * b₁) + (a₂ * b₂)) * c₁) + (((a₁ * b₂) + (a₂ * b₁)) * c₂)) ,
            ((a₁ * ((b₁ * c₂) + (b₂ * c₁))) + (a₂ * ((b₁ * c₁) + (b₂ * c₂))))
    ≡⟨ cong (λ z → (((((a₁ * b₁) + (a₂ * b₂)) * c₁) + (((a₁ * b₂) + (a₂ * b₁)) * c₂))) , z) lem2 ⟩
        ((((a₁ * b₁) + (a₂ * b₂)) * c₁) + (((a₁ * b₂) + (a₂ * b₁)) * c₂)) ,
         ((((a₁ * b₁) + (a₂ * b₂)) * c₂) + (((a₁ * b₂) + (a₂ * b₁)) * c₁))
    ≡⟨ refl ⟩
        ((a₁ * b₁) + (a₂ * b₂) , (a₁ * b₂) + (a₂ * b₁)) *ℤ c
    ≡⟨ cong (λ z → z *ℤ c) t-lem ⟩
        (a *ℤ b) *ℤ c
    ∎)
    where
        t-lem : ((a₁ * b₁) + (a₂ * b₂) , (a₁ * b₂) + (a₂ * b₁)) ≡ (a *ℤ b)
        t-lem = refl
        lem1 : ((a₁ * ((b₁ * c₁) + (b₂ * c₂))) + (a₂ * ((b₁ * c₂) + (b₂ * c₁)))) ≡
                ((((a₁ * b₁) + (a₂ * b₂)) * c₁) + (((a₁ * b₂) + (a₂ * b₁)) * c₂))
        lem1 = begin
                ((a₁ * ((b₁ * c₁) + (b₂ * c₂))) + (a₂ * ((b₁ * c₂) + (b₂ * c₁))))
            ≡⟨ cong (λ z → z + (a₂ * ((b₁ * c₂) + (b₂ * c₁)))) (+*-lDist (b₁ * c₁) (b₂ * c₂) a₁) ⟩
                ((a₁ * (b₁ * c₁)) + (a₁ * (b₂ * c₂))) + (a₂ * ((b₁ * c₂) + (b₂ * c₁)))
            ≡⟨ cong (λ z → (((a₁ * (b₁ * c₁)) + (a₁ * (b₂ * c₂)))) + z) (+*-lDist (b₁ * c₂) (b₂ * c₁) a₂) ⟩
                ((a₁ * (b₁ * c₁)) + (a₁ * (b₂ * c₂))) + ((a₂ * (b₁ * c₂)) + (a₂ * (b₂ * c₁)))
            ≡⟨ cong (λ z → (((a₁ * (b₁ * c₁)) + (a₁ * (b₂ * c₂)))) + z) (+-comm (a₂ * (b₁ * c₂)) (a₂ * (b₂ * c₁))) ⟩
                ((a₁ * (b₁ * c₁)) + (a₁ * (b₂ * c₂))) + ((a₂ * (b₂ * c₁)) + (a₂ * (b₁ * c₂)))
            ≡⟨ sym (+-assoc (a₁ * (b₁ * c₁)) (a₁ * (b₂ * c₂)) ((a₂ * (b₂ * c₁)) + (a₂ * (b₁ * c₂)))) ⟩
                (a₁ * (b₁ * c₁)) + ((a₁ * (b₂ * c₂)) + (((a₂ * (b₂ * c₁)) + (a₂ * (b₁ * c₂)))))
            ≡⟨ cong (λ z → (a₁ * (b₁ * c₁)) + z) (+-assoc (a₁ * (b₂ * c₂)) (a₂ * (b₂ * c₁)) (a₂ * (b₁ * c₂))) ⟩
                (a₁ * (b₁ * c₁)) + (((a₁ * (b₂ * c₂)) + (a₂ * (b₂ * c₁))) + (a₂ * (b₁ * c₂)))
            ≡⟨ cong (λ z → (a₁ * (b₁ * c₁)) + (z + (a₂ * (b₁ * c₂)))) (+-comm (a₁ * (b₂ * c₂)) (a₂ * (b₂ * c₁))) ⟩
                (a₁ * (b₁ * c₁)) + (((a₂ * (b₂ * c₁)) + (a₁ * (b₂ * c₂))) + (a₂ * (b₁ * c₂)))
            ≡⟨ cong (λ z → (a₁ * (b₁ * c₁)) + z) (sym (+-assoc (a₂ * (b₂ * c₁)) (a₁ * (b₂ * c₂)) (a₂ * (b₁ * c₂)))) ⟩
                (a₁ * (b₁ * c₁)) + ((a₂ * (b₂ * c₁)) + ((a₁ * (b₂ * c₂)) + (a₂ * (b₁ * c₂))))
            ≡⟨ +-assoc (a₁ * (b₁ * c₁)) (a₂ * (b₂ * c₁)) ((a₁ * (b₂ * c₂)) + (a₂ * (b₁ * c₂))) ⟩
                ((a₁ * (b₁ * c₁)) + (a₂ * (b₂ * c₁))) + ((a₁ * (b₂ * c₂)) + (a₂ * (b₁ * c₂)))
            ≡⟨ cong (λ z → (z + (a₂ * (b₂ * c₁))) + ((a₁ * (b₂ * c₂)) + (a₂ * (b₁ * c₂)))) (*-assoc a₁ b₁ c₁) ⟩
                (((a₁ * b₁) * c₁) + (a₂ * (b₂ * c₁))) + ((a₁ * (b₂ * c₂)) + (a₂ * (b₁ * c₂)))
            ≡⟨ cong (λ z → (((a₁ * b₁) * c₁) + z) + ((a₁ * (b₂ * c₂)) + (a₂ * (b₁ * c₂)))) (*-assoc a₂ b₂ c₁) ⟩
                (((a₁ * b₁) * c₁) + ((a₂ * b₂) * c₁)) + ((a₁ * (b₂ * c₂)) + (a₂ * (b₁ * c₂)))
            ≡⟨ cong (λ z → z + ((a₁ * (b₂ * c₂)) + (a₂ * (b₁ * c₂)))) (sym (+*-rDist (a₁ * b₁) (a₂ * b₂) c₁)) ⟩
                (((a₁ * b₁) + (a₂ * b₂)) * c₁) + ((a₁ * (b₂ * c₂)) + (a₂ * (b₁ * c₂)))
            ≡⟨ cong (λ z → ((((a₁ * b₁) + (a₂ * b₂)) * c₁)) + (z + (a₂ * (b₁ * c₂)))) (*-assoc a₁ b₂ c₂) ⟩
                (((a₁ * b₁) + (a₂ * b₂)) * c₁) + (((a₁ * b₂) * c₂) + (a₂ * (b₁ * c₂)))
            ≡⟨ cong (λ z → (((a₁ * b₁) + (a₂ * b₂)) * c₁) + (((a₁ * b₂) * c₂) + z)) (*-assoc a₂ b₁ c₂) ⟩
                (((a₁ * b₁) + (a₂ * b₂)) * c₁) + (((a₁ * b₂) * c₂) + ((a₂ * b₁) * c₂))
            ≡⟨ cong (λ z → ((((a₁ * b₁) + (a₂ * b₂)) * c₁)) + z) (sym (+*-rDist (a₁ * b₂) (a₂ * b₁) c₂)) ⟩
                (((a₁ * b₁) + (a₂ * b₂)) * c₁) + (((a₁ * b₂) + (a₂ * b₁)) * c₂)
            ∎
        lem2 : ((a₁ * ((b₁ * c₂) + (b₂ * c₁))) + (a₂ * ((b₁ * c₁) + (b₂ * c₂)))) ≡
                ((((a₁ * b₁) + (a₂ * b₂)) * c₂) + (((a₁ * b₂) + (a₂ * b₁)) * c₁))
        lem2 = begin
                (a₁ * ((b₁ * c₂) + (b₂ * c₁))) + (a₂ * ((b₁ * c₁) + (b₂ * c₂)))
            ≡⟨ cong (λ x → x + (a₂ * ((b₁ * c₁) + (b₂ * c₂)))) (+*-lDist (b₁ * c₂) (b₂ * c₁) a₁) ⟩
                ((a₁ * (b₁ * c₂)) + (a₁ * (b₂ * c₁))) + (a₂ * ((b₁ * c₁) + (b₂ * c₂)))
            ≡⟨ cong (λ x → (((a₁ * (b₁ * c₂)) + (a₁ * (b₂ * c₁)))) + x) (+*-lDist (b₁ * c₁) (b₂ * c₂) a₂) ⟩
                ((a₁ * (b₁ * c₂)) + (a₁ * (b₂ * c₁))) + ((a₂ * (b₁ * c₁)) + (a₂ * (b₂ * c₂)))
            ≡⟨ cong (λ x → (x + ((a₁ * (b₂ * c₁)))) + (((a₂ * (b₁ * c₁)) + (a₂ * (b₂ * c₂))))) (*-assoc a₁ b₁ c₂) ⟩
                (((a₁ * b₁) * c₂) + (a₁ * (b₂ * c₁))) + ((a₂ * (b₁ * c₁)) + (a₂ * (b₂ * c₂)))
            ≡⟨ cong (λ x → ((((a₁ * b₁) * c₂)) + x) + (((a₂ * (b₁ * c₁)) + (a₂ * (b₂ * c₂))))) (*-assoc a₁ b₂ c₁) ⟩
                (((a₁ * b₁) * c₂) + ((a₁ * b₂) * c₁)) + ((a₂ * (b₁ * c₁)) + (a₂ * (b₂ * c₂)))
            ≡⟨ cong (λ x → ((((a₁ * b₁) * c₂) + ((a₁ * b₂) * c₁))) + (x + (a₂ * (b₂ * c₂)))) (*-assoc a₂ b₁ c₁) ⟩
                (((a₁ * b₁) * c₂) + ((a₁ * b₂) * c₁)) + (((a₂ * b₁) * c₁) + (a₂ * (b₂ * c₂)))
            ≡⟨ cong (λ x → ((((a₁ * b₁) * c₂) + ((a₁ * b₂) * c₁))) + (((a₂ * b₁) * c₁) + x)) (*-assoc a₂ b₂ c₂) ⟩
                (((a₁ * b₁) * c₂) + ((a₁ * b₂) * c₁)) + (((a₂ * b₁) * c₁) + ((a₂ * b₂) * c₂))
            ≡⟨ sym (+-assoc ((a₁ * b₁) * c₂) ((a₁ * b₂) * c₁) (((a₂ * b₁) * c₁) + ((a₂ * b₂) * c₂))) ⟩
                ((a₁ * b₁) * c₂) + (((a₁ * b₂) * c₁) + (((a₂ * b₁) * c₁) + ((a₂ * b₂) * c₂)))
            ≡⟨ cong (λ x → (((a₁ * b₁) * c₂)) + x) (+-assoc ((a₁ * b₂) * c₁) ((a₂ * b₁) * c₁) ((a₂ * b₂) * c₂)) ⟩
                ((a₁ * b₁) * c₂) + ((((a₁ * b₂) * c₁) + ((a₂ * b₁) * c₁)) + ((a₂ * b₂) * c₂))
            ≡⟨ cong (λ x → (((a₁ * b₁) * c₂)) + x) (+-comm (((a₁ * b₂) * c₁) + ((a₂ * b₁) * c₁)) ((a₂ * b₂) * c₂)) ⟩
                ((a₁ * b₁) * c₂) + (((a₂ * b₂) * c₂) + (((a₁ * b₂) * c₁) + ((a₂ * b₁) * c₁)))
            ≡⟨ +-assoc ((a₁ * b₁) * c₂) ((a₂ * b₂) * c₂) (((a₁ * b₂) * c₁) + ((a₂ * b₁) * c₁)) ⟩
                (((a₁ * b₁) * c₂) + ((a₂ * b₂) * c₂)) + (((a₁ * b₂) * c₁) + ((a₂ * b₁) * c₁))
            ≡⟨ cong (λ x → x + (((a₁ * b₂) * c₁) + ((a₂ * b₁) * c₁))) (sym (+*-rDist (a₁ * b₁) (a₂ * b₂) c₂)) ⟩
                (((a₁ * b₁) + (a₂ * b₂)) * c₂) + (((a₁ * b₂) * c₁) + ((a₂ * b₁) * c₁))
            ≡⟨ cong (λ x → ((((a₁ * b₁) + (a₂ * b₂)) * c₂)) + x) (sym (+*-rDist (a₁ * b₂) (a₂ * b₁) c₁)) ⟩
                (((a₁ * b₁) + (a₂ * b₂)) * c₂) + (((a₁ * b₂) + (a₂ * b₁)) * c₁)
            ∎

-- a *ℤ b = a₁ * b₁ + a₂ * b₂ , a₁ * b₂ + a₂ * b₁
-- b *ℤ a = b₁ * a₁ + b₂ * a₂ , b₁ * a₂ + b₂ * a₁
*ℤ-comm : (a b : ℤb) → (a *ℤ b) ≡ℤ (b *ℤ a)
*ℤ-comm (a₁ , a₂) (b₁ , b₂) = begin
        ((a₁ * b₁) + (a₂ * b₂)) + ((b₁ * a₂) + (b₂ * a₁))
    ≡⟨ +-comm ((a₁ * b₁) + (a₂ * b₂)) ((b₁ * a₂) + (b₂ * a₁)) ⟩
        ((b₁ * a₂) + (b₂ * a₁)) + ((a₁ * b₁) + (a₂ * b₂))
    ≡⟨ cong (λ z → z + ((a₁ * b₁) + (a₂ * b₂))) (+-comm (b₁ * a₂) (b₂ * a₁)) ⟩
        ((b₂ * a₁) + (b₁ * a₂)) + ((a₁ * b₁) + (a₂ * b₂))
    ≡⟨ cong (λ z → (z + (b₁ * a₂)) + ((a₁ * b₁) + (a₂ * b₂))) (*-comm b₂ a₁) ⟩
        ((a₁ * b₂) + (b₁ * a₂)) + ((a₁ * b₁) + (a₂ * b₂))
    ≡⟨ cong (λ z → ((a₁ * b₂) + z) + ((a₁ * b₁) + (a₂ * b₂))) (*-comm b₁ a₂) ⟩
        ((a₁ * b₂) + (a₂ * b₁)) + ((a₁ * b₁) + (a₂ * b₂))
    ≡⟨ cong (λ z → ((a₁ * b₂) + (a₂ * b₁)) + (z + (a₂ * b₂))) (*-comm a₁ b₁) ⟩
        ((a₁ * b₂) + (a₂ * b₁)) + ((b₁ * a₁) + (a₂ * b₂))
    ≡⟨ cong (λ z → ((a₁ * b₂) + (a₂ * b₁)) + ((b₁ * a₁) + z)) (*-comm a₂ b₂) ⟩
        ((a₁ * b₂) + (a₂ * b₁)) + ((b₁ * a₁) + (b₂ * a₂))
    ∎

ℕ-ℤ : ℕ → ℤb
ℕ-ℤ n = (n , zero)

ℕ-ℤ-monic : (m n : ℕ) → ℕ-ℤ m ≡ℤ ℕ-ℤ n → m ≡ n
ℕ-ℤ-monic m n m+0≡0+n = begin
        m
    ≡⟨ sym (+-rUnit m) ⟩
        m + zero
    ≡⟨ m+0≡0+n ⟩
        zero + n
    ≡⟨ refl ⟩
        n
    ∎

-- (n , zero) +ℤ (zero , n) = n + zero , zero + n
-- (n + zero) + zero = (zero + n) + zero
+ℤ-invℕ : (n : ℕ) → ((n , zero) +ℤ (zero , n)) ≡ℤ (zero , zero)
+ℤ-invℕ n = begin
        (n + zero) + zero
    ≡⟨ cong (λ x → x + zero) (+-comm n zero) ⟩
        (zero + n) + zero
    ∎

-- (a , b) +ℤ (b , a) = a + b , b + a
-- (a + b) + zero = (b + a) + zero
+ℤ-inv : (a : ℤb) → ∃ λ b → (a +ℤ b) ≡ℤ (zero , zero)
+ℤ-inv (a , b) = (b , a) , (begin
        (a + b) + zero
    ≡⟨ cong (λ x → x + zero) (+-comm a b) ⟩
        (b + a) + zero
    ∎)

-ℤ_ : ℤb → ℤb
-ℤ (a , b) = (b , a)

_-ℤ_ : ℤb → ℤb → ℤb
a -ℤ b = a +ℤ (-ℤ b)

-ℤ-inv : (a : ℤb) → (a -ℤ a) ≡ℤ (zero , zero)
-ℤ-inv (a , b) = begin
        (a + b) + zero
    ≡⟨ cong (λ x → x + zero) (+-comm a b) ⟩
        (b + a) + zero
    ∎

_≤ℤ_ : ℤb → ℤb → Set
a ≤ℤ b = ∃ λ l → (a +ℤ (l , zero)) ≡ℤ b

≤ℤ-refl : (a : ℤb) → a ≤ℤ a
≤ℤ-refl (a , b) = zero , ≡ℤ-≡ (+ℤ-rUnit a b)

-- p : (a₁ , a₂) +ℤ (x , zero) ≡ℤ (b₁ , b₂)
-- (a₁ + x , a₂ + zero) ≡ℤ (b₁ , b₂)
-- (a₁ + x) + b₂ = (a₂ + zero) + b₁
-- q : (b₁ , b₂) +ℤ (x , zero) ≡ℤ (c₁ , c₂)
-- (b₁ + y , b₂ + zero) ≡ℤ (c₁ , c₂)
-- (b₁ + y) + c₂ = (b₂ + zero) + c₁
-- goal : (a₁ , a₂) +ℤ (z , zero) ≡ℤ (c₁ , c₂)
-- (a₁ + z , a₂ + zero) ≡ℤ (c₁ , c₂)
-- (a₁ + z) + c₂ ≡ (a₂ + zero) + c₁
≤ℤ-trans : (a b c : ℤb) → a ≤ℤ b → b ≤ℤ c → a ≤ℤ c
≤ℤ-trans (a₁ , a₂) (b₁ , b₂) (c₁ , c₂) (x , p) (y , q) = (x + y) ,
    +-rCancel ((a₁ + (x + y)) + c₂) ((a₂ + zero) + c₁) b₂ (begin
            ((a₁ + (x + y)) + c₂) + b₂
        ≡⟨ cong (λ z → z + b₂) (sym (+-assoc a₁ (x + y) c₂)) ⟩
            (a₁ + ((x + y) + c₂)) + b₂
        ≡⟨ sym (+-assoc a₁ ((x + y) + c₂) b₂) ⟩
            a₁ + (((x + y) + c₂) + b₂)
        ≡⟨ cong (λ z → a₁ + z) (sym (+-assoc (x + y) c₂ b₂)) ⟩
            a₁ + ((x + y) + (c₂ + b₂))
        ≡⟨ cong (λ z → a₁ + ((x + y) + z)) (+-comm c₂ b₂) ⟩
            a₁ + ((x + y) + (b₂ + c₂))
        ≡⟨ cong (λ z → a₁ + z) (sym (+-assoc x y (b₂ + c₂))) ⟩
            a₁ + (x + (y + (b₂ + c₂)))
        ≡⟨ cong (λ z → a₁ + (x + z)) (+-assoc y b₂ c₂) ⟩
            a₁ + (x + ((y + b₂) + c₂))
        ≡⟨ cong (λ z → a₁ + (x + (z + c₂))) (+-comm y b₂) ⟩
            a₁ + (x + ((b₂ + y) + c₂))
        ≡⟨ cong (λ z → a₁ + (x + z)) (sym (+-assoc b₂ y c₂)) ⟩
            a₁ + (x + (b₂ + (y + c₂)))
        ≡⟨ +-assoc a₁ x (b₂ + (y + c₂)) ⟩
            ((a₁ + x) + (b₂ + (y + c₂)))
        ≡⟨ +-assoc (a₁ + x) b₂ (y + c₂) ⟩
            ((a₁ + x) + b₂) + (y + c₂)
        ≡⟨ cong (λ z → z + (y + c₂)) p ⟩
            ((a₂ + zero) + b₁) + (y + c₂)
        ≡⟨ sym (+-assoc (a₂ + zero) b₁ (y + c₂)) ⟩
            (a₂ + zero) + (b₁ + (y + c₂))
        ≡⟨ cong (λ z → (a₂ + zero) + z) (+-assoc b₁ y c₂) ⟩
            (a₂ + zero) + ((b₁ + y) + c₂)
        ≡⟨ cong (λ z → (a₂ + zero) + z) q ⟩
            (a₂ + zero) + ((b₂ + zero) + c₁)
        ≡⟨ cong (λ z → (a₂ + zero) + z) (+-comm (b₂ + zero) c₁) ⟩
            (a₂ + zero) + (c₁ + (b₂ + zero))
        ≡⟨ cong (λ z → (a₂ + zero) + (c₁ + z)) (+-rUnit b₂) ⟩
            (a₂ + zero) + (c₁ + b₂)
        ≡⟨ +-assoc (a₂ + zero) c₁ b₂ ⟩
            ((a₂ + zero) + c₁) + b₂
        ∎)

-- p : (a₁ + x , a₂ + zero) ≡ℤ (b₁ , b₂)
-- (a₁ + x) + b₂ ≡ (a₂ + zero) + b₁
-- q : (b₁ + y , b₂ + zero) ≡ℤ (a₁ , a₂)
-- (b₁ + y) + a₂ ≡ (b₂ + zero) + a₁
≤ℤ-antisym : (a b : ℤb) → a ≤ℤ b → b ≤ℤ a → a ≡ℤ b
≤ℤ-antisym (a₁ , a₂) (b₁ , b₂) (x , p) (y , q) = begin
        a₁ + b₂
    ≡⟨ sym (+-rUnit (a₁ + b₂)) ⟩
        (a₁ + b₂) + zero
    ≡⟨ cong (λ z → (a₁ + b₂) + z) (sym x≡0) ⟩
        (a₁ + b₂) + x
    ≡⟨ sym (+-assoc a₁ b₂ x) ⟩
        a₁ + (b₂ + x)
    ≡⟨ cong (λ z → a₁ + z) (+-comm b₂ x) ⟩
        a₁ + (x + b₂)
    ≡⟨ +-assoc a₁ x b₂ ⟩
        (a₁ + x) + b₂
    ≡⟨ p ⟩
        (a₂ + zero) + b₁
    ≡⟨ cong (λ z → z + b₁) (+-rUnit a₂) ⟩
        a₂ + b₁
    ∎
    where
        x≡0 : x ≡ zero
        x≡0 = +-lZero x y (+-lCancel (x + y) zero ((a₁ + b₂) + a₂) (begin
                ((a₁ + b₂) + a₂) + (x + y)
            ≡⟨ sym (+-assoc (a₁ + b₂) a₂ (x + y)) ⟩
                (a₁ + b₂) + (a₂ + (x + y))
            ≡⟨ cong (λ x₁ → (a₁ + b₂) + x₁) (+-assoc a₂ x y) ⟩
                (a₁ + b₂) + ((a₂ + x) + y)
            ≡⟨ cong (λ z → (a₁ + b₂) + (z + y)) (+-comm a₂ x) ⟩
                (a₁ + b₂) + ((x + a₂) + y)
            ≡⟨ cong (λ z → (a₁ + b₂) + z) (sym (+-assoc x a₂ y)) ⟩
                (a₁ + b₂) + (x + (a₂ + y))
            ≡⟨ +-assoc (a₁ + b₂) x (a₂ + y) ⟩
                ((a₁ + b₂) + x) + (a₂ + y)
            ≡⟨ cong (λ z → z + (a₂ + y)) (sym (+-assoc a₁ b₂ x)) ⟩
                (a₁ + (b₂ + x)) + (a₂ + y)
            ≡⟨ cong (λ z → (a₁ + z) + (a₂ + y)) (+-comm b₂ x) ⟩
                (a₁ + (x + b₂)) + (a₂ + y)
            ≡⟨ cong (λ z → z + (a₂ + y)) (+-assoc a₁ x b₂) ⟩
                ((a₁ + x) + b₂) + (a₂ + y)
            ≡⟨ cong (λ z → z + (a₂ + y)) p ⟩
                ((a₂ + zero) + b₁) + (a₂ + y)
            ≡⟨ sym (+-assoc (a₂ + zero) b₁ (a₂ + y)) ⟩
                (a₂ + zero) + (b₁ + (a₂ + y))
            ≡⟨ cong (λ z → (a₂ + zero) + (b₁ + z)) (+-comm a₂ y) ⟩
                (a₂ + zero) + (b₁ + (y + a₂))
            ≡⟨ cong (λ z → (a₂ + zero) + z) (+-assoc b₁ y a₂) ⟩
                (a₂ + zero) + ((b₁ + y) + a₂)
            ≡⟨ cong (λ z → (a₂ + zero) + z) q ⟩
                (a₂ + zero) + ((b₂ + zero) + a₁)
            ≡⟨ +-comm (a₂ + zero) ((b₂ + zero) + a₁) ⟩
                ((b₂ + zero) + a₁) + (a₂ + zero)
            ≡⟨ cong (λ z → z + (a₂ + zero)) (+-comm (b₂ + zero) a₁) ⟩
                (a₁ + (b₂ + zero)) + (a₂ + zero)
            ≡⟨ cong (λ z → (a₁ + z) + (a₂ + zero)) (+-rUnit b₂) ⟩
                (a₁ + b₂) + (a₂ + zero)
            ≡⟨ +-assoc (a₁ + b₂) a₂ zero ⟩
                ((a₁ + b₂) + a₂) + zero
            ∎))

-- (a₁ , a₂) ≤ℤ (b₁ , b₂)
-- (a₁ + l , a₂ + zero) ≡ℤ (b₁ , b₂)
-- (a₁ + l) + b₂ ≡ (a₂ + zero) + b₁
≤ℤ-all : (a b : ℤb) → a ≤ℤ b ⊎ b ≤ℤ a
≤ℤ-all a@(a₁ , a₂) b@(b₁ , b₂) = S.map lem1 lem2 (≤-all (a₁ + b₂) (a₂ + b₁))
    where
        lem1 : (a₁ + b₂) ≤ (a₂ + b₁) → a ≤ℤ b
        lem1 (x , p) = x , (begin
                (a₁ + x) + b₂
            ≡⟨ sym (+-assoc a₁ x b₂) ⟩
                a₁ + (x + b₂)
            ≡⟨ cong (λ z → a₁ + z) (+-comm x b₂) ⟩
                a₁ + (b₂ + x)
            ≡⟨ +-assoc a₁ b₂ x ⟩
                (a₁ + b₂) + x
            ≡⟨ p ⟩
                a₂ + b₁
            ≡⟨ cong (λ z → z + b₁) (sym (+-rUnit a₂)) ⟩
                (a₂ + zero) + b₁
            ∎)
        lem2 : (a₂ + b₁) ≤ (a₁ + b₂) → b ≤ℤ a
        lem2 (x , p) = x , (begin
                (b₁ + x) + a₂
            ≡⟨ sym (+-assoc b₁ x a₂) ⟩
                b₁ + (x + a₂)
            ≡⟨ cong (λ z → b₁ + z) (+-comm x a₂) ⟩
                b₁ + (a₂ + x)
            ≡⟨ +-assoc b₁ a₂ x ⟩
                (b₁ + a₂) + x
            ≡⟨ cong (λ z → z + x) (+-comm b₁ a₂) ⟩
                (a₂ + b₁) + x
            ≡⟨ p ⟩
                a₁ + b₂
            ≡⟨ +-comm a₁ b₂ ⟩
                b₂ + a₁
            ≡⟨ cong (λ z → z + a₁) (sym (+-rUnit b₂)) ⟩
                (b₂ + zero) + a₁
            ∎)


open import Data.Product using ( _×_ )
open import FRP.LTL.ISet.Core using ( ISet ; ⌈_⌉ ; M⟦_⟧ )
open import FRP.LTL.Time using ( Time )
open import FRP.LTL.Time.Bound using ( fin ; _≺_ )
open import FRP.LTL.Time.Interval using ( [_⟩ ; sing )

module FRP.LTL.ISet.Until where

data _Until_ (A B : ISet) (t : Time) : Set where
  now : M⟦ B ⟧ (sing t) → (A Until B) t
  later : ∀ {u} → .(t≺u : fin t ≺ fin u) → M⟦ A ⟧ [ t≺u ⟩ → M⟦ B ⟧ (sing u) → (A Until B) t

_U_ : ISet → ISet → ISet
A U B = ⌈ A Until B ⌉


-- Andreas, 2011-10-06
module Issue483c where

data _≡_ {A : Set}(a : A) : A → Set where
  refl : a ≡ a

record _×_ (A B : Set) : Set where
  constructor _,_
  field fst : A
        snd : B

postulate
  A : Set
  f : .A → A

-- this succeeds
test : let X : .A → A
           X = _
       in .(x : A) → (X ≡ f) × (X (f x) ≡ f x)
test x = refl , refl

-- so this should also succeed
test2 : let X : .A → A
            X = _
        in .(x : A) → (X (f x) ≡ f x) × (X ≡ f)
test2 x = refl , refl


open import Relation.Nullary using (Dec; yes; no)
open import Relation.Binary.PropositionalEquality using (_≡_; refl; sym)

open import Data.Sum using (_⊎_)
open _⊎_

module AKS.Nat.Roots where

open import AKS.Nat using (ℕ; _≤_; _<_; n≤m⇒n<m⊎n≡m; <-irrefl; *-1-commutativeMonoid)
open ℕ
open import AKS.Nat using ([_,_]; binary-search; acc)
open import AKS.Exponentiation *-1-commutativeMonoid using (_^_)

record Root (n : ℕ) (b : ℕ) : Set where
  constructor Root✓
  field
    root : ℕ
    root^b≤n : root ^ b ≤ n
    n<root^[1+b] : n < (suc root) ^ b

open import AKS.Unsafe using (TODO)

find-root : ∀ n b → Root n b
find-root n b = loop 0 n binary-search
  where
  loop : ∀ l h → [ l , h ] → Root n b
  loop l h (acc downward upward) = TODO

-- root-unique : root ^ b < n → n < (suc root) ^ b

record Power (n : ℕ) (b : ℕ) : Set where
  constructor Power✓
  field
    root : ℕ
    n≡root^b : n ≡ root ^ b

power? : ∀ n b → Dec (Power n b)
power? n b with find-root n b
... | Root✓ root₁ root₁^b≤n n<root₁^[1+b] with n≤m⇒n<m⊎n≡m root₁^b≤n
...   | inj₁ root₁^b<n = no λ { (Power✓ root₂ n≡root₂^b) → TODO }
...   | inj₂ root₁^b≡n = yes (Power✓ root₁ (sym root₁^b≡n))

record Perfect (n : ℕ) : Set where
  constructor Perfect✓
  field
    b : ℕ
    power : Power n b

perfect? : ∀ n → Dec (Perfect n)
perfect? = TODO


{-# OPTIONS --without-K #-}
module equality.groupoid where

open import equality.core

_⁻¹ : ∀ {i}{X : Set i}{x y : X} → x ≡ y → y ≡ x
_⁻¹ = sym

left-unit : ∀ {i}{X : Set i}{x y : X}
          → (p : x ≡ y)
          → p · refl ≡ p
left-unit refl = refl

right-unit : ∀ {i}{X : Set i}{x y : X}
           → (p : x ≡ y)
           → refl · p ≡ p
right-unit refl = refl

associativity : ∀ {i}{X : Set i}{x y z w : X}
              → (p : x ≡ y)(q : y ≡ z)(r : z ≡ w)
              → p · q · r ≡ p · (q · r)
associativity refl _ _ = refl

left-inverse : ∀ {i}{X : Set i}{x y : X}
             → (p : x ≡ y)
             → p · p ⁻¹ ≡ refl
left-inverse refl = refl

right-inverse : ∀ {i}{X : Set i}{x y : X}
              → (p : x ≡ y)
              → p ⁻¹ · p ≡ refl
right-inverse refl = refl


module Class.Equality where

open import Data.Bool using (Bool; if_then_else_)
open import Data.List
open import Data.Product
open import Relation.Nullary
open import Relation.Nullary.Decidable
open import Relation.Binary.PropositionalEquality

record Eq (A : Set) : Set where
  field
    _≟_ : (x y : A) -> Dec (x ≡ y)

open Eq {{...}} public

record EqB (A : Set) : Set where
  field
    _≣_ : (x y : A) -> Bool

  decCase_of_default_ : ∀ {b} {B : Set b} -> A -> List (A × B) -> B -> B
  decCase a of [] default d = d
  decCase a of x ∷ xs default d = if a ≣ proj₁ x then proj₂ x else (decCase a of xs default d)

open EqB {{...}} public

Eq→EqB : ∀ {A} -> {{_ : Eq A}} -> EqB A
Eq→EqB = record { _≣_ = λ x y -> ⌊ x ≟ y ⌋ }


module Inductive.Examples.Product where

open import Inductive
open import Tuple

open import Data.Fin
open import Data.Product hiding (_×_; <_,_>)
open import Data.List
open import Data.Vec

_×_ : Set → Set → Set
A × B = Inductive (((A ∷ (B ∷ [])) , []) ∷ [])

<_,_> : {A B : Set} → A → B → A × B
< a , b > = construct zero (a ∷ (b ∷ [])) []

fst : {A B : Set} → A × B → A
fst = rec ((λ a b → a) ∷ [])

snd : {A B : Set} → A × B → B
snd = rec ((λ a b → b) ∷ [])


{-# OPTIONS --without-K #-}
module NTypes.Coproduct where

open import NTypes
open import PathOperations
open import PathStructure.Coproduct
open import Types

⊎-isSet : ∀ {A : Set} {B : Set} →
  isSet A → isSet B → isSet (A ⊎ B)
⊎-isSet {A = A} {B = B} A-set B-set x y p q =
  case (λ x → (y : A ⊎ B) (p q : x ≡ y) → p ≡ q)
  (λ a y p q → case
    (λ y → (p q : inl a ≡ y) → p ≡ q)
    (λ a′ p q
      → split-eq p ⁻¹
      · ap (ap inl)
        (A-set _ _
          (lower (tr (F (inl a)) p (lift refl)))
          (lower (tr (F (inl a)) q (lift refl)))
        )
      · split-eq q
      )
    (λ _  p q → 0-elim
      (lower (split-path p)))
    y p q)
  (λ b y p q → case
    (λ y → (p q : inr b ≡ y) → p ≡ q)
    (λ _  p q → 0-elim
      (lower (split-path p)))
    (λ b′ p q
      → split-eq p ⁻¹
      · ap (ap inr)
        (B-set _ _
          (lower (tr (F (inr b)) p (lift refl)))
          (lower (tr (F (inr b)) q (lift refl)))
        )
      · split-eq q
      )
    y p q)
  x y p q
  where
  split-eq : {x y : A ⊎ B} → _
  split-eq {x = x} {y = y} =
    π₂ (π₂ (π₂ (split-merge-eq {x = x} {y = y})))


module _ where

open import Agda.Builtin.Equality

postulate
  A : Set
  P : A → Set

data Id (A : Set) : Set where
  id : A → Id A

data Flat (@♭ A : Set) : Set where
  con : (@♭ x : A) → Flat A

counit : {@♭ A : Set} → Flat A → A
counit (con x) = x

test2 : (@♭ x : Id A) → Flat A
test2 (id x) = con x

test3 : (@♭ x : A) (@♭ y : A) (@♭ p : x ≡ y) → P x → P y
test3 x y refl p = p

test4 : (@♭ x : A) (@♭ y : A) → Flat (x ≡ y) → P x → P y
test4 x y (con refl) p = p

test6 : (@♭ x y : A) → (eq : con x ≡ con y) → P x → P y
test6 x .x refl p = p