module Numeral.Natural.Relation.DivisibilityWithRemainder.Proofs where

import      Lvl
open import Data
open import Data.Boolean.Stmt
open import Functional
open import Logic.Predicate
open import Logic.Propositional
open import Numeral.Finite
import      Numeral.Finite.Proofs as 𝕟
open import Numeral.Natural
open import Numeral.Natural.Inductions
open import Numeral.Natural.Oper
open import Numeral.Natural.Oper.Comparisons
open import Numeral.Natural.Oper.Proofs
open import Numeral.Natural.Oper.Proofs.Order
open import Numeral.Natural.Relation.DivisibilityWithRemainder hiding (base₀ ; base₊ ; step)
open import Numeral.Natural.Relation.Order.Decidable
open import Numeral.Natural.Relation.Order.Proofs
open import Numeral.Natural.Relation.Order
open import Relator.Equals
open import Relator.Equals.Proofs
open import Structure.Function
open import Structure.Operator
open import Structure.Operator.Proofs.Util
open import Structure.Relator
open import Syntax.Transitivity
open import Type.Properties.Decidable.Proofs

-- 0 does not divide anything with any remainder.
[∣ᵣₑₘ]-0-divides : ∀{x}{r} → ¬(0 ∣ᵣₑₘ x)(r)
[∣ᵣₑₘ]-0-divides {r = ()}

-- 1 divides everything with the only possible remainder 0.
[∣ᵣₑₘ]-1-divides : ∀{x}{r} → (1 ∣ᵣₑₘ x)(r)
[∣ᵣₑₘ]-1-divides {𝟎}   {r = 𝟎} = DivRem𝟎
[∣ᵣₑₘ]-1-divides {𝐒 x} {r = 𝟎} = DivRem𝐒 [∣ᵣₑₘ]-1-divides

-- The quotient is the dividend when divided by 1.
[∣ᵣₑₘ]-quotient-of-1 : ∀{x}{r} → (p : (1 ∣ᵣₑₘ x)(r)) → ([∣ᵣₑₘ]-quotient p ≡ x)
[∣ᵣₑₘ]-quotient-of-1 {𝟎}  {𝟎} DivRem𝟎     = [≡]-intro
[∣ᵣₑₘ]-quotient-of-1 {𝐒 x}{𝟎} (DivRem𝐒 p) = [≡]-with(𝐒) ([∣ᵣₑₘ]-quotient-of-1 {x}{𝟎} p)

-- [∣ᵣₑₘ]-remainder-dividend : ∀{x y}{r : 𝕟(y)} → (x < y) → (y ∣ᵣₑₘ x)(r) → (x ≡ 𝕟-to-ℕ r)

-- How the arguments in the divisibility relation is related to each other by elementary functions.
-- Note: The division theorem is proven using this. By proving that [∣ᵣₑₘ]-quotient and [∣ᵣₑₘ]-remainder is equal to the algorithmic functions of floored division and modulo, the theorem follows directly from this.
[∣ᵣₑₘ]-is-division-with-remainder : ∀{x y}{r} → (p : (y ∣ᵣₑₘ x)(r)) → ((([∣ᵣₑₘ]-quotient p) ⋅ y) + (𝕟-to-ℕ ([∣ᵣₑₘ]-remainder p)) ≡ x)
[∣ᵣₑₘ]-is-division-with-remainder {𝟎}             {_}   {𝟎}   DivRem𝟎 = [≡]-intro
[∣ᵣₑₘ]-is-division-with-remainder {𝐒 .(x + y)}    {𝐒 y} {𝟎}   (DivRem𝐒 {x = x} p) =
  𝐒([∣ᵣₑₘ]-quotient p) ⋅ 𝐒(y)         🝖[ _≡_ ]-[ [⋅]-with-[𝐒]ₗ {[∣ᵣₑₘ]-quotient p}{𝐒(y)} ]
  (([∣ᵣₑₘ]-quotient p) ⋅ 𝐒(y)) + 𝐒(y) 🝖[ _≡_ ]-[]
  𝐒((([∣ᵣₑₘ]-quotient p) ⋅ 𝐒(y)) + y) 🝖[ _≡_ ]-[ congruence₁(𝐒) (congruence₂ₗ(_+_)(y) ([∣ᵣₑₘ]-is-division-with-remainder p)) ]
  𝐒(x + y)                            🝖-end
[∣ᵣₑₘ]-is-division-with-remainder {𝐒 .(𝕟-to-ℕ r)} {𝐒 y} {𝐒 r} DivRem𝟎 = [≡]-intro
[∣ᵣₑₘ]-is-division-with-remainder {𝐒 .(x + y)} {𝐒(y@(𝐒 _))} {r@(𝐒 _)} (DivRem𝐒 {x = x} p) =
  (([∣ᵣₑₘ]-quotient (DivRem𝐒 p)) ⋅ 𝐒(y)) + (𝕟-to-ℕ r) 🝖[ _≡_ ]-[]
  (𝐒([∣ᵣₑₘ]-quotient p) ⋅ 𝐒(y))          + (𝕟-to-ℕ r) 🝖[ _≡_ ]-[ congruence₂ₗ(_+_)(𝕟-to-ℕ r) ([⋅]-with-[𝐒]ₗ {[∣ᵣₑₘ]-quotient p}{𝐒(y)}) ]
  ((([∣ᵣₑₘ]-quotient p) ⋅ 𝐒(y)) + 𝐒(y))  + (𝕟-to-ℕ r) 🝖[ _≡_ ]-[ One.commuteᵣ-assocₗ {a = ([∣ᵣₑₘ]-quotient p) ⋅ 𝐒(y)}{b = 𝐒(y)}{c = 𝕟-to-ℕ r} ]
  ((([∣ᵣₑₘ]-quotient p) ⋅ 𝐒(y)) + (𝕟-to-ℕ r)) + 𝐒(y)  🝖[ _≡_ ]-[]
  𝐒(((([∣ᵣₑₘ]-quotient p) ⋅ 𝐒(y)) + (𝕟-to-ℕ r)) + y)  🝖[ _≡_ ]-[ congruence₁(𝐒) (congruence₂ₗ(_+_)(y) ([∣ᵣₑₘ]-is-division-with-remainder p)) ]
  𝐒(x + y)                                                 🝖-end

-- When the arguments in the divisibility relation are related to each other.
-- This also indicates that the divisibility relation actually states something about divisibility in the sense of the inverse of multiplication.
[∣ᵣₑₘ]-equivalence : ∀{x y}{r} → (y ∣ᵣₑₘ x)(r) ↔ ∃(q ↦ (q ⋅ y) + (𝕟-to-ℕ r) ≡ x)
[∣ᵣₑₘ]-equivalence = [↔]-intro (p ↦ l {q = [∃]-witness p} ([∃]-proof p)) (p ↦ [∃]-intro ([∣ᵣₑₘ]-quotient p) ⦃ [∣ᵣₑₘ]-is-division-with-remainder p ⦄) where
  l :  ∀{x y q}{r} → ((q ⋅ y) + (𝕟-to-ℕ r) ≡ x) → (y ∣ᵣₑₘ x)(r)
  l {_}{_}{𝟎}  {_} [≡]-intro = DivRem𝟎
  l {x}{y}{𝐒 q}{r} p = substitute₁(x ↦ (y ∣ᵣₑₘ x)(r)) eq (DivRem𝐒 (l{(q ⋅ y) + (𝕟-to-ℕ r)}{y}{q}{r} [≡]-intro)) where
    eq =
      ((q ⋅ y) + (𝕟-to-ℕ r)) + y 🝖[ _≡_ ]-[ One.commuteᵣ-assocₗ {a = q ⋅ y}{b = 𝕟-to-ℕ r}{c = y} ]
      ((q ⋅ y) + y) + (𝕟-to-ℕ r) 🝖[ _≡_ ]-[ congruence₂ₗ(_+_)(𝕟-to-ℕ r) ([⋅]-with-[𝐒]ₗ {q}{y}) ]-sym
      (𝐒(q) ⋅ y) + (𝕟-to-ℕ r)    🝖[ _≡_ ]-[ p ]
      x                          🝖-end

-- ⌊/⌋-when-zero : ∀{x y} → (x ⌊/⌋ 𝐒(y) ≡ 𝟎) → (x ≡ 0)
-- ⌊/⌋-when-positive : ∀{x y q} → (x ⌊/⌋ 𝐒(y) ≡ 𝐒(q)) → ∃(x₀ ↦ (x ≡ x₀ + 𝐒(y)))

{-
[∣ᵣₑₘ]-existence : ∀{x y} → ∃(𝐒(y) ∣ᵣₑₘ x)
∃.witness ([∣ᵣₑₘ]-existence {x} {y}) = ℕ-to-𝕟 (x mod 𝐒(y)) ⦃ {![↔]-to-[→] decide-is-true mod-maxᵣ!} ⦄
∃.proof   ([∣ᵣₑₘ]-existence {x} {y}) = [↔]-to-[←] [∣ᵣₑₘ]-equivalence ([∃]-intro (x ⌊/⌋ 𝐒(y)) ⦃ {!TODO: Insert division theorem here!} ⦄)
-}

DivRem𝟎Alt : ∀{x y} → (xy : (x < y)) → (y ∣ᵣₑₘ x)(ℕ-to-𝕟 x ⦃ [↔]-to-[→] decider-true xy ⦄)
DivRem𝟎Alt {x} {𝐒 y} (succ p) = [≡]-substitutionᵣ (𝕟.𝕟-ℕ-inverse) {expr ↦ (𝐒 y ∣ᵣₑₘ expr)(ℕ-to-𝕟 x)} ((DivRem𝟎{𝐒(y)}{ℕ-to-𝕟 x})) where
  instance
    x<𝐒y : IsTrue (x <? 𝐒(y))
    x<𝐒y = [↔]-to-[→] decider-true ([≤]-with-[𝐒] ⦃ p ⦄)

DivRem𝐒Alt : ∀{x y}{r : 𝕟(y)} → (x ≥ y) → (y ∣ᵣₑₘ x −₀ y)(r) → (y ∣ᵣₑₘ x)(r)
DivRem𝐒Alt{x}{𝟎}{}
DivRem𝐒Alt{x}{𝐒(y)}{r} xy = [≡]-substitutionᵣ ([↔]-to-[→] ([−₀][+]-nullify2ᵣ{𝐒(y)}{x}) xy) {\expr → (𝐒(y) ∣ᵣₑₘ expr) r} ∘ DivRem𝐒{𝐒(y)}{x −₀ 𝐒(y)}{r}

-- Every pair of numbers (positive divisor) when divided will yield a remainder and there is always a proof of it being the case.
-- This is an alternative way of constructing the modulo operator.
[∣ᵣₑₘ]-existence-alt : ∀{x y} → ∃(𝐒(y) ∣ᵣₑₘ x)
[∣ᵣₑₘ]-existence-alt {x} {y} = [ℕ]-strong-induction {φ = x ↦ ∃(𝐒(y) ∣ᵣₑₘ x)} base step {x} where
  base : ∃(𝐒(y) ∣ᵣₑₘ 𝟎)
  base = [∃]-intro 𝟎 ⦃ DivRem𝟎 ⦄

  step : ∀{i} → (∀{j} → (j ≤ i) → ∃(𝐒(y) ∣ᵣₑₘ j)) → ∃(𝐒(y) ∣ᵣₑₘ 𝐒(i))
  step{i} p with [≤][>]-dichotomy {y}{i}
  ... | [∨]-introₗ yi = [∃]-map-proof (DivRem𝐒Alt([≤]-with-[𝐒] ⦃ yi ⦄)) (p{𝐒(i) −₀ 𝐒(y)} ([−₀]-lesser {i}{y}))
  ... | [∨]-introᵣ 𝐒iy = [∃]-intro (ℕ-to-𝕟 (𝐒(i)) ⦃ [↔]-to-[→] decider-true 𝐒iy ⦄) ⦃ DivRem𝟎Alt ([≤]-with-[𝐒] ⦃ 𝐒iy ⦄) ⦄

{-
open import Structure.Setoid.Uniqueness
{-[∣ᵣₑₘ]-uniqueness : ∀{x y}{p : ∃(𝐒(y) ∣ᵣₑₘ x)} → (p ≡ [∣ᵣₑₘ]-existence)
[∣ᵣₑₘ]-uniqueness {.(𝕟-to-ℕ r)} {y} {[∃]-intro r ⦃ DivRem𝟎 ⦄} = {![≡]-intro!}
[∣ᵣₑₘ]-uniqueness {.(x + 𝐒 y)} {y} {[∃]-intro r ⦃ DivRem𝐒 {x = x} p ⦄} = {!!}-}
{-[∣ᵣₑₘ]-unique-remainder : ∀{x y} → Unique(𝐒(y) ∣ᵣₑₘ x)
[∣ᵣₑₘ]-unique-remainder {.(𝕟-to-ℕ a)} {y} {a} {b} DivRem𝟎 q = {!!}
[∣ᵣₑₘ]-unique-remainder {.(x + 𝐒 y)} {y} {a} {b} (DivRem𝐒 {x = x} p) q = {!!}-}
open import Type.Properties.MereProposition
[∣ᵣₑₘ]-mereProposition : ∀{x y}{r : 𝕟(𝐒(y))} → MereProposition((𝐒(y) ∣ᵣₑₘ x)(r))
[∣ᵣₑₘ]-mereProposition = intro proof where
  proof : ∀{x y}{r : 𝕟(𝐒(y))}{p q : (𝐒(y) ∣ᵣₑₘ x)(r)} → (p ≡ q)
  proof {.(𝕟-to-ℕ r)} {y} {r} {DivRem𝟎} {q} = {!!}
  proof {.(x + 𝐒 y)} {y} {r} {DivRem𝐒 {x = x} p} {q} = {!p!}

  -- testor : ∀{y x}{r : 𝕟(𝐒 y)}{p : (𝐒(y) ∣ᵣₑₘ x)(r)} → (p ≡ DivRem𝟎) ∨ ∃(q ↦ (p ≡ DivRem𝐒 q))

  -- TODO: Maybe by injectivity of 𝕟-to-ℕ?
  test : ∀{y}{r : 𝕟(𝐒 y)}{p : (𝐒(y) ∣ᵣₑₘ (𝕟-to-ℕ r))(r)} → (p ≡ DivRem𝟎)
{-  test {y} {r} {p} with 𝕟-to-ℕ r
  test {_} {_} {_} | 𝟎 = {!!}-}
-}

{-
open import Data.Tuple using (_⨯_ ; _,_)
open import Type.Dependent
open import Type.Properties.MereProposition
[∣ᵣₑₘ]-mereProposition : ∀{x y}{r : 𝕟(y)} → MereProposition((y ∣ᵣₑₘ x)(r))
[∣ᵣₑₘ]-mereProposition = intro {!!} where
  proof : ∀{y}{r : 𝕟(y)} → (p q : Σ(ℕ) (x ↦ (y ∣ᵣₑₘ x)(r))) → (p ≡ q)
  proof {y} {r} (intro .(𝕟-to-ℕ r) DivRem𝟎) (intro .(𝕟-to-ℕ r) DivRem𝟎) = [≡]-intro
  proof {y} {r} (intro .(𝕟-to-ℕ r) DivRem𝟎) (intro .(x₂ + y) (DivRem𝐒{x = x₂} p₂)) = {!!}
  proof {y} {r} (intro .(x₁ + y) (DivRem𝐒{x = x₁} p₁)) (intro .(𝕟-to-ℕ r) DivRem𝟎) = {!!}
  proof {y} {r} (intro .(x₁ + y) (DivRem𝐒{x = x₁} p₁)) (intro .(x₂ + y) (DivRem𝐒{x = x₂} p₂)) = {!congruence₂ₗ(_+_)(y) (congruence₁(Σ.left) eq)!} where
    eq = proof (intro x₁ p₁) (intro x₂ p₂)
    test : ∀{a₁ a₂ : A}{b₁ : B(a₁)}{b₂ : B(a₂)} → (a₁ ≡ a₂) → (intro a₁ b₁ ≡ intro a₂ b₂)
-}


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

open import HoTT
open import cohomology.Theory

module cw.cohomology.InnerGrid {i} (OT : OrdinaryTheory i)
  (n : ℤ) {X Y Z W : Ptd i} (f : X ⊙→ Y) (g : Y ⊙→ Z) (h : Z ⊙→ W) where

  open OrdinaryTheory OT
  open import cohomology.PtdMapSequence cohomology-theory

{-

  X --> Y ---> Z ---> W
  |     |      |      |
  v     |      v      v
  1 ----+---> Z/X -> W/X
        |      | this |
        v      v  one v
        1 --> Z/Y -> W/Y

-}

  import cw.cohomology.GridPtdMap

  open cw.cohomology.GridPtdMap (g ⊙∘ f) h using ()
    renaming (Y/X-to-Z/X to Z/X-to-W/X; B/A-to-C/A to C/A-to-D/A;
      module B/AToC/A to C/AToD/A)

  open cw.cohomology.GridPtdMap f g
    using (Z/X-to-Z/Y; C/A-to-C/B; module C/AToC/B)

  open cw.cohomology.GridPtdMap f (h ⊙∘ g) using ()
    renaming (Z/X-to-Z/Y to W/X-to-W/Y; C/A-to-C/B to D/A-to-D/B;
      module C/AToC/B to D/AToD/B)

  open cw.cohomology.GridPtdMap g h using ()
    renaming (Y/X-to-Z/X to Z/Y-to-W/Y; B/A-to-C/A to C/B-to-D/B;
      module B/AToC/A to C/BToD/B)

  inner-grid-comm-sqr : CommSquare C/A-to-D/A C/B-to-D/B C/A-to-C/B D/A-to-D/B
  inner-grid-comm-sqr = comm-sqr $ Cofiber-elim idp (λ _ → idp)
    (λ a → ↓-='-in' $ ap-∘ C/B-to-D/B C/A-to-C/B (glue a)
                    ∙ ap (ap C/B-to-D/B) (C/AToC/B.glue-β a)
                    ∙ C/BToD/B.glue-β ((fst f) a)
                    ∙ ! (D/AToD/B.glue-β a)
                    ∙ ap (ap D/A-to-D/B) (! (C/AToD/A.glue-β a))
                    ∙ ∘-ap D/A-to-D/B C/A-to-D/A (glue a))

  C-inner-grid-commutes : CommSquareᴳ
    (C-fmap n Z/Y-to-W/Y) (C-fmap n Z/X-to-W/X) (C-fmap n W/X-to-W/Y) (C-fmap n Z/X-to-Z/Y)
  C-inner-grid-commutes = C-comm-square n inner-grid-comm-sqr


{-# OPTIONS --copatterns #-}
module MixingCoPatternsAndCoConstructors where

data ℕ : Set where
  zero : ℕ
  suc  : (n : ℕ) → ℕ

module MStream where

  record Stream (A : Set) : Set where
    coinductive
    constructor _∷_
    field
      head : A
      tail : Stream A
  open Stream

  weird : (n : ℕ) → Stream ℕ
  head (weird zero)    = zero
  tail (weird zero)    = weird zero
  head (weird (suc n)) = n
  tail (weird (suc n)) = tail (weird n)

  weird' : (n : ℕ) → Stream ℕ
  head (weird' zero) = zero
  tail (weird' zero) = weird' zero
  weird' (suc n) = n ∷ tail (weird' n)

  -- BAD:
  weird'' : (n : ℕ) → Stream ℕ
  weird'' zero    = zero ∷ weird'' zero
  weird'' (suc n) = n ∷ tail (weird'' n)

  -- productive, but not strongly normalizing,
  -- should be rejected by termination checker:

  repeat : {A : Set}(a : A) → Stream A
  head (repeat a) = a
  tail (repeat a) = a ∷ tail (repeat a)

module Coinduction where

  import Common.Level
  open import Common.Coinduction

  record Stream (A : Set) : Set where
    inductive
    constructor _∷_
    field
      head : A
      tail : ∞ (Stream A)
  open Stream

  weird'' : (n : ℕ) → Stream ℕ
  weird'' zero    = zero ∷ (♯ weird'' zero)
  weird'' (suc n) = n ∷ tail (weird'' n)

module CoList where

  mutual

    data CoList (A : Set) : Set where
      []  : CoList A
      _∷_ : (x : A)(xs : CoList∞ A) → CoList A

    record CoList∞ (A : Set) : Set where
      coinductive
      constructor delay
      field
        force : CoList A

  open CoList∞

  downFrom : (n : ℕ) → CoList ℕ
  downFrom zero    = []
  downFrom (suc n) = n ∷ delay (downFrom n)

  down : (n : ℕ) → CoList∞ ℕ
  force (down zero)    = []
  force (down (suc n)) = n ∷ delay (force (down n))
    -- weird detour: delay (force ... to test termination checker


module Luau.Var where

open import Agda.Builtin.Bool using (true; false)
open import Agda.Builtin.Equality using (_≡_)
open import Agda.Builtin.String using (String; primStringEquality)
open import Agda.Builtin.TrustMe using (primTrustMe)
open import Properties.Dec using (Dec; yes; no)
open import Properties.Equality using (_≢_)

Var : Set
Var = String

_≡ⱽ_ : (a b : Var) → Dec (a ≡ b)
a ≡ⱽ b with primStringEquality a b
a ≡ⱽ b | false = no p where postulate p : (a ≢ b)
a ≡ⱽ b | true = yes primTrustMe


------------------------------------------------------------------------------
-- The gcd is divisible by any common divisor (using equational reasoning)
------------------------------------------------------------------------------

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

module FOTC.Program.GCD.Partial.DivisibleI where

open import FOTC.Base
open import FOTC.Base.PropertiesI
open import FOTC.Data.Nat
open import FOTC.Data.Nat.Divisibility.NotBy0
open import FOTC.Data.Nat.Divisibility.NotBy0.PropertiesI
open import FOTC.Data.Nat.Induction.NonAcc.LexicographicI
open import FOTC.Data.Nat.Inequalities
open import FOTC.Data.Nat.Inequalities.EliminationPropertiesI
open import FOTC.Data.Nat.Inequalities.PropertiesI
open import FOTC.Data.Nat.PropertiesI
open import FOTC.Program.GCD.Partial.ConversionRulesI
open import FOTC.Program.GCD.Partial.Definitions
open import FOTC.Program.GCD.Partial.GCD

------------------------------------------------------------------------------
-- The gcd 0 (succ n) is Divisible.
gcd-0S-Divisible : ∀ {n} → N n → Divisible zero (succ₁ n) (gcd zero (succ₁ n))
gcd-0S-Divisible {n} _ c _ (c∣0 , c∣Sn) = subst (_∣_ c) (sym (gcd-0S n)) c∣Sn

------------------------------------------------------------------------------
-- The gcd (succ₁ n) 0 is Divisible.
gcd-S0-Divisible : ∀ {n} → N n → Divisible (succ₁ n) zero (gcd (succ₁ n) zero)
gcd-S0-Divisible {n} _ c _ (c∣Sn , c∣0) = subst (_∣_ c) (sym (gcd-S0 n)) c∣Sn

------------------------------------------------------------------------------
-- The gcd (succ₁ m) (succ₁ n) when succ₁ m > succ₁ n is Divisible.
gcd-S>S-Divisible :
  ∀ {m n} → N m → N n →
  (Divisible (succ₁ m ∸ succ₁ n) (succ₁ n) (gcd (succ₁ m ∸ succ₁ n) (succ₁ n))) →
  succ₁ m > succ₁ n →
  Divisible (succ₁ m) (succ₁ n) (gcd (succ₁ m) (succ₁ n))
gcd-S>S-Divisible {m} {n} Nm Nn acc Sm>Sn c Nc (c∣Sm , c∣Sn) =
{-
Proof:
   ----------------- (Hip.)
     c | m    c | n
   ---------------------- (Thm.)   -------- (Hip.)
       c | (m ∸ n)                   c | n
     ------------------------------------------ (IH)
              c | gcd m (n ∸ m)                          m > n
             --------------------------------------------------- (gcd def.)
                             c | gcd m n
-}
 subst (_∣_ c) (sym (gcd-S>S m n Sm>Sn)) (acc c Nc (c|Sm-Sn , c∣Sn))
 where
 c|Sm-Sn : c ∣ succ₁ m ∸ succ₁ n
 c|Sm-Sn = x∣y→x∣z→x∣y∸z Nc (nsucc Nm) (nsucc Nn) c∣Sm c∣Sn

------------------------------------------------------------------------------
-- The gcd (succ₁ m) (succ₁ n) when succ₁ m ≯ succ₁ n is Divisible.
gcd-S≯S-Divisible :
  ∀ {m n} → N m → N n →
  (Divisible (succ₁ m) (succ₁ n ∸ succ₁ m) (gcd (succ₁ m) (succ₁ n ∸ succ₁ m))) →
  succ₁ m ≯ succ₁ n →
  Divisible (succ₁ m) (succ₁ n) (gcd (succ₁ m) (succ₁ n))
gcd-S≯S-Divisible {m} {n} Nm Nn acc Sm≯Sn c Nc (c∣Sm , c∣Sn) =
{-
Proof
                            ----------------- (Hip.)
                                c | m    c | n
        -------- (Hip.)       ---------------------- (Thm.)
         c | m                      c | n ∸ m
     ------------------------------------------ (IH)
              c | gcd m (n ∸ m)                          m ≯ n
             --------------------------------------------------- (gcd def.)
                             c | gcd m n
-}

  subst (_∣_ c) (sym (gcd-S≯S m n Sm≯Sn)) (acc c Nc (c∣Sm , c|Sn-Sm))
  where
  c|Sn-Sm : c ∣ succ₁ n ∸ succ₁ m
  c|Sn-Sm = x∣y→x∣z→x∣y∸z Nc (nsucc Nn) (nsucc Nm) c∣Sn c∣Sm

------------------------------------------------------------------------------
-- The gcd m n when m > n is Divisible.
gcd-x>y-Divisible :
  ∀ {m n} → N m → N n →
  (∀ {o p} → N o → N p → Lexi o p m n → x≢0≢y o p →
             Divisible o p (gcd o p)) →
  m > n →
  x≢0≢y m n →
  Divisible m n (gcd m n)
gcd-x>y-Divisible nzero nzero _ _ ¬0≡0∧0≡0 _ _  = ⊥-elim (¬0≡0∧0≡0 (refl , refl))
gcd-x>y-Divisible nzero (nsucc Nn) _ 0>Sn _ _ _ = ⊥-elim (0>x→⊥ (nsucc Nn) 0>Sn)
gcd-x>y-Divisible (nsucc Nm) nzero _ _ _  c Nc  = gcd-S0-Divisible Nm c Nc
gcd-x>y-Divisible (nsucc {m} Nm) (nsucc {n} Nn) ah Sm>Sn _ c Nc =
  gcd-S>S-Divisible Nm Nn ih Sm>Sn c Nc
  where
  -- Inductive hypothesis.
  ih : Divisible (succ₁ m ∸ succ₁ n) (succ₁ n) (gcd (succ₁ m ∸ succ₁ n) (succ₁ n))
  ih = ah {succ₁ m ∸ succ₁ n}
          {succ₁ n}
          (∸-N (nsucc Nm) (nsucc Nn))
          (nsucc Nn)
          ([Sx∸Sy,Sy]<[Sx,Sy] Nm Nn)
          (λ p → ⊥-elim (S≢0 (∧-proj₂ p)))

------------------------------------------------------------------------------
-- The gcd m n when m ≯ n is Divisible.
gcd-x≯y-Divisible :
  ∀ {m n} → N m → N n →
  (∀ {o p} → N o → N p → Lexi o p m n → x≢0≢y o p →
             Divisible o p (gcd o p)) →
  m ≯ n →
  x≢0≢y m n →
  Divisible m n (gcd m n)
gcd-x≯y-Divisible nzero nzero _ _ h _ _   = ⊥-elim (h (refl , refl))
gcd-x≯y-Divisible nzero (nsucc Nn) _ _  _  c Nc  = gcd-0S-Divisible Nn c Nc
gcd-x≯y-Divisible (nsucc _) nzero _ Sm≯0 _ _ _  = ⊥-elim (S≯0→⊥ Sm≯0)
gcd-x≯y-Divisible (nsucc {m} Nm) (nsucc {n} Nn) ah Sm≯Sn _ c Nc =
  gcd-S≯S-Divisible Nm Nn ih Sm≯Sn c Nc
  where
  -- Inductive hypothesis.
  ih : Divisible (succ₁ m) (succ₁ n ∸ succ₁ m) (gcd (succ₁ m) (succ₁ n ∸ succ₁ m))
  ih = ah {succ₁ m}
          {succ₁ n ∸ succ₁ m}
          (nsucc Nm)
          (∸-N (nsucc Nn) (nsucc Nm))
          ([Sx,Sy∸Sx]<[Sx,Sy] Nm Nn)
          (λ p → ⊥-elim (S≢0 (∧-proj₁ p)))

------------------------------------------------------------------------------
-- The gcd is Divisible.
gcdDivisible : ∀ {m n} → N m → N n → x≢0≢y m n → Divisible m n (gcd m n)
gcdDivisible = Lexi-wfind A h
  where
  A : D → D → Set
  A i j = x≢0≢y i j → Divisible i j (gcd i j)

  h : ∀ {i j} → N i → N j → (∀ {k l} → N k → N l → Lexi k l i j → A k l) →
      A i j
  h Ni Nj ah = case (gcd-x>y-Divisible Ni Nj ah)
                    (gcd-x≯y-Divisible Ni Nj ah)
                    (x>y∨x≯y Ni Nj)


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

{-

This file defines

sucPred : ∀ (i : Int) → sucInt (predInt i) ≡ i
predSuc : ∀ (i : Int) → predInt (sucInt i) ≡ i

discreteInt : discrete Int
isSetInt : isSet Int

addition of Int is defined _+_ : Int → Int → Int

as well as its commutativity and associativity
+-comm : ∀ (m n : Int) → m + n ≡ n + m
+-assoc : ∀ (m n o : Int) → m + (n + o) ≡ (m + n) + o

An alternate definition of _+_ is defined via ua,
namely _+'_, which helps us to easily prove

isEquivAddInt : (m : Int) → isEquiv (λ n → n + m)

-}

module Cubical.Data.Int.Properties where

open import Cubical.Core.Everything

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Transport
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.Univalence

open import Cubical.Data.Empty as ⊥
open import Cubical.Data.Unit renaming (Unit to ⊤)
open import Cubical.Data.Nat as ℕ using (ℕ; zero; suc; discreteℕ) renaming
  ( _·_ to _·ⁿ_
  ; _+_ to _+ⁿ_
  ; _⊓_ to _⊓ⁿ_
  ; _⊔_ to _⊔ⁿ_
  )
open import Cubical.Data.Nat.Order as ℕᵒ using () renaming ( _<_ to _<ⁿ_ )
open import Cubical.Data.Bool hiding (_≟_)
open import Cubical.Data.Sum
open import Cubical.Data.Int.Base
open import Cubical.Functions.Logic using (pathTo⇒; ⊔-comm)
open import Cubical.HITs.PropositionalTruncation as PropTrunc using (propTruncIsProp)
open import Cubical.Foundations.Function using (_∘_)
open import Cubical.Reflection.Base using (_$_) -- TODO: add this to Foundation.Function

open import Cubical.Relation.Nullary
open import Cubical.Relation.Nullary.DecidableEq

abs : Int → ℕ
abs (pos n) = n
abs (negsuc n) = suc n

Sign : Type₀
Sign = Bool

pattern spos = Bool.false
pattern sneg = Bool.true

_·ˢ_ : Sign → Sign → Sign
_·ˢ_ = _⊕_

sgn : Int → Bool
sgn (pos    n) = spos
sgn (negsuc n) = sneg

signed : Sign → ℕ → Int
signed spos      x  = pos x
signed sneg  zero   = pos 0
signed sneg (suc x) = neg (suc x)

sgn-signed : ∀ s n → sgn (signed s (suc n)) ≡ s
sgn-signed spos n = refl
sgn-signed sneg n = refl

abs-signed : ∀ s n → abs (signed s n) ≡ n
abs-signed spos      n  = refl
abs-signed sneg  zero   = refl
abs-signed sneg (suc n) = refl

signed-inv : ∀ a → signed (sgn a) (abs a) ≡ a
signed-inv (pos    n) = refl
signed-inv (negsuc n) = refl

signed-zero : ∀ s → signed s 0 ≡ 0
signed-zero spos = refl
signed-zero sneg = refl

signed-reflects-sgn : ∀ s₁ s₂ n m → signed s₁ (suc n) ≡ signed s₂ (suc m) → s₁ ≡ s₂
signed-reflects-sgn s₁ s₂ n m p = sym (sgn-signed s₁ n) ∙ (λ i → sgn (p i)) ∙ sgn-signed s₂ m

signed-reflects-abs : ∀ s₁ s₂ n m → signed s₁ n ≡ signed s₂ m → n ≡ m
signed-reflects-abs s₁ s₂ n m p = sym (abs-signed s₁ n) ∙ (λ i → abs (p i)) ∙ abs-signed s₂ m

sign-abs-≡ : ∀ a b → sgn a ≡ sgn b → abs a ≡ abs b → a ≡ b
sign-abs-≡ a b p q = transport (λ i → signed-inv a i ≡ signed-inv b i) λ i → signed (p i) (q i)

sucPred : ∀ i → sucInt (predInt i) ≡ i
sucPred (pos zero)    = refl
sucPred (pos (suc n)) = refl
sucPred (negsuc n)    = refl

predSuc : ∀ i → predInt (sucInt i) ≡ i
predSuc (pos n)          = refl
predSuc (negsuc zero)    = refl
predSuc (negsuc (suc n)) = refl

predInt-inj : ∀ a b → predInt a ≡ predInt b → a ≡ b
predInt-inj a b p = sym (sucPred a) ∙ cong sucInt p ∙ sucPred b

injPos : ∀ {a b : ℕ} → pos a ≡ pos b → a ≡ b
injPos {a} h = subst T h refl
  where
  T : Int → Type₀
  T (pos x)    = a ≡ x
  T (negsuc _) = ⊥

injNegsuc : ∀ {a b : ℕ} → negsuc a ≡ negsuc b → a ≡ b
injNegsuc {a} h = subst T h refl
  where
  T : Int → Type₀
  T (pos _) = ⊥
  T (negsuc x) = a ≡ x

posNotnegsuc : ∀ (a b : ℕ) → ¬ (pos a ≡ negsuc b)
posNotnegsuc a b h = subst T h 0
  where
  T : Int → Type₀
  T (pos _)    = ℕ
  T (negsuc _) = ⊥

negsucNotpos : ∀ (a b : ℕ) → ¬ (negsuc a ≡ pos b)
negsucNotpos a b h = subst T h 0
  where
  T : Int → Type₀
  T (pos _)    = ⊥
  T (negsuc _) = ℕ

discreteInt : Discrete Int
discreteInt (pos n) (pos m) with discreteℕ n m
... | yes p = yes (cong pos p)
... | no p  = no (λ x → p (injPos x))
discreteInt (pos n) (negsuc m) = no (posNotnegsuc n m)
discreteInt (negsuc n) (pos m) = no (negsucNotpos n m)
discreteInt (negsuc n) (negsuc m) with discreteℕ n m
... | yes p = yes (cong negsuc p)
... | no p  = no (λ x → p (injNegsuc x))

isSetInt : isSet Int
isSetInt = Discrete→isSet discreteInt

_ℕ-_ : ℕ → ℕ → Int
a ℕ- 0 = pos a
0 ℕ- suc b = negsuc b
suc a ℕ- suc b = a ℕ- b

_+pos_ : Int → ℕ  → Int
z +pos 0 = z
z +pos (suc n) = sucInt (z +pos n)

_+negsuc_ : Int → ℕ → Int
z +negsuc 0 = predInt z
z +negsuc (suc n) = predInt (z +negsuc n)

infix  8 -_
infixl 7 _·_ _⊓_
infixl 6 _+_ _-_ _⊔_
infix  4 _<_ -- _≤_

_+_ : Int → Int → Int
m + pos n = m +pos n
m + negsuc n = m +negsuc n

-- TODO: in `Cubical.Papers.RepresentationIndependence` we have
--   -_ : ℤ → ℤ
--   - x = 0 - x
-- there might be an advantage of defining -_ by _-_ and not the other way around
-- e.g. in Cubical.HITs.QuoInt we have
--   -_ : ℤ → ℤ
--   - signed s n = signed (not s) n
--   - posneg i   = posneg (~ i)

-_ : Int → Int
- pos  zero   = pos zero
- pos (suc n) = negsuc n
- negsuc   n  = pos (suc n)

_-_ : Int → Int → Int
m - n = m + (- n)

_·_ : Int → Int → Int
x · y  = signed (sgn x ⊕ sgn y) (abs x ·ⁿ abs y)

_<_ : ∀(x y : Int) → Type₀
pos    x < pos    y = x <ⁿ y
pos    x < negsuc y = ⊥
negsuc x < pos    y = ⊤
negsuc x < negsuc y = y <ⁿ x

_⊓_ : Int → Int → Int
(pos    x) ⊓ (pos    y) = pos (x ⊓ⁿ y)
(pos    x) ⊓ (negsuc y) = negsuc y
(negsuc x) ⊓ (pos    y) = negsuc x
(negsuc x) ⊓ (negsuc y) = negsuc (x ⊔ⁿ y)

_⊔_ : Int → Int → Int
(pos    x) ⊔ (pos    y) = pos (x ⊔ⁿ y)
(pos    x) ⊔ (negsuc y) = pos x
(negsuc x) ⊔ (pos    y) = pos y
(negsuc x) ⊔ (negsuc y) = negsuc (x ⊓ⁿ y)

neg-involutive : ∀ n → - - n ≡ n
neg-involutive (pos  zero  ) = refl
neg-involutive (pos (suc n)) = refl
neg-involutive (negsuc   n ) = refl

neg-signed-not : ∀ s n → - signed s n ≡ signed (not s) n
neg-signed-not spos  zero   = refl
neg-signed-not sneg  zero   = refl
neg-signed-not spos (suc n) = refl
neg-signed-not sneg (suc n) = refl

neg-abs : ∀ a → abs (- a) ≡ abs a
neg-abs (pos zero) = refl
neg-abs (pos (suc n)) = refl
neg-abs (negsuc n) = refl

neg-reflects-≡ : ∀ a b → - a ≡ - b → a ≡ b
neg-reflects-≡ a b p = sym (neg-involutive a) ∙ (λ i → - p i) ∙ neg-involutive b

abs-preserves-· : ∀ a b → abs (a · b) ≡ abs a ℕ.· abs b
abs-preserves-· a b = abs-signed (sgn a ⊕ sgn b) _

sucInt+pos : ∀ n m → sucInt (m +pos n) ≡ (sucInt m) +pos n
sucInt+pos zero m = refl
sucInt+pos (suc n) m = cong sucInt (sucInt+pos n m)

predInt+negsuc : ∀ n m → predInt (m +negsuc n) ≡ (predInt m) +negsuc n
predInt+negsuc zero m = refl
predInt+negsuc (suc n) m = cong predInt (predInt+negsuc n m)

sucInt+negsuc : ∀ n m → sucInt (m +negsuc n) ≡ (sucInt m) +negsuc n
sucInt+negsuc zero m = (sucPred _) ∙ (sym (predSuc _))
sucInt+negsuc (suc n) m =      _ ≡⟨ sucPred _ ⟩
  m +negsuc n                    ≡[ i ]⟨ predSuc m (~ i) +negsuc n ⟩
  (predInt (sucInt m)) +negsuc n ≡⟨ sym (predInt+negsuc n (sucInt m)) ⟩
  predInt (sucInt m +negsuc n) ∎

predInt+pos : ∀ n m → predInt (m +pos n) ≡ (predInt m) +pos n
predInt+pos zero m = refl
predInt+pos (suc n) m =     _ ≡⟨ predSuc _ ⟩
  m +pos n                    ≡[ i ]⟨ sucPred m (~ i) + pos n ⟩
  (sucInt (predInt m)) +pos n ≡⟨ sym (sucInt+pos n (predInt m))⟩
  (predInt m) +pos (suc n)    ∎

predInt+ : ∀ m n → predInt (m + n) ≡ (predInt m) + n
predInt+ m (pos n) = predInt+pos n m
predInt+ m (negsuc n) = predInt+negsuc n m

+predInt : ∀ m n → predInt (m + n) ≡ m + (predInt n)
+predInt m (pos zero) = refl
+predInt m (pos (suc n)) = (predSuc (m + pos n)) ∙ (cong (_+_ m) (sym (predSuc (pos n))))
+predInt m (negsuc n) = refl

sucInt+ : ∀ m n → sucInt (m + n) ≡ (sucInt m) + n
sucInt+ m (pos n) = sucInt+pos n m
sucInt+ m (negsuc n) = sucInt+negsuc n m

+sucInt : ∀ m n → sucInt (m + n) ≡  m + (sucInt n)
+sucInt m (pos n) = refl
+sucInt m (negsuc zero) = sucPred _
+sucInt m (negsuc (suc n)) = (sucPred (m +negsuc n)) ∙ (cong (_+_ m) (sym (sucPred (negsuc n))))

pos0+ : ∀ z → z ≡ pos 0 + z
pos0+ (pos zero) = refl
pos0+ (pos (suc n)) = cong sucInt (pos0+ (pos n))
pos0+ (negsuc zero) = refl
pos0+ (negsuc (suc n)) = cong predInt (pos0+ (negsuc n))

negsuc0+ : ∀ z → predInt z ≡ negsuc 0 + z
negsuc0+ (pos zero) = refl
negsuc0+ (pos (suc n)) = (sym (sucPred (pos n))) ∙ (cong sucInt (negsuc0+ _))
negsuc0+ (negsuc zero) = refl
negsuc0+ (negsuc (suc n)) = cong predInt (negsuc0+ (negsuc n))

ind-comm : {A : Type₀} (_∙_ : A → A → A) (f : ℕ → A) (g : A → A)
           (p : ∀ {n} → f (suc n) ≡ g (f n))
           (g∙ : ∀ a b → g (a ∙ b) ≡ g a ∙ b)
           (∙g : ∀ a b → g (a ∙ b) ≡ a ∙ g b)
           (base : ∀ z → z ∙ f 0 ≡ f 0 ∙ z)
         → ∀ z n → z ∙ f n ≡ f n ∙ z
ind-comm _∙_ f g p g∙ ∙g base z 0 = base z
ind-comm _∙_ f g p g∙ ∙g base z (suc n) =
  z ∙ f (suc n) ≡[ i ]⟨ z ∙ p {n} i ⟩
  z ∙ g (f n)   ≡⟨ sym ( ∙g z (f n)) ⟩
  g (z ∙ f n)   ≡⟨ cong g IH ⟩
  g (f n ∙ z)   ≡⟨ g∙ (f n) z ⟩
  g (f n) ∙ z   ≡[ i ]⟨ p {n} (~ i) ∙ z ⟩
  f (suc n) ∙ z ∎
  where
  IH = ind-comm _∙_ f g p g∙ ∙g base z n

ind-assoc : {A : Type₀} (_·_ : A → A → A) (f : ℕ → A)
        (g : A → A) (p : ∀ a b → g (a · b) ≡ a · (g b))
        (q : ∀ {c} → f (suc c) ≡ g (f c))
        (base : ∀ m n → (m · n) · (f 0) ≡ m · (n · (f 0)))
        (m n : A) (o : ℕ)
      → m · (n · (f o)) ≡ (m · n) · (f o)
ind-assoc _·_ f g p q base m n 0 = sym (base m n)
ind-assoc _·_ f g p q base m n (suc o) =
    m · (n · (f (suc o))) ≡[ i ]⟨ m · (n · q {o} i) ⟩
    m · (n · (g (f o)))   ≡[ i ]⟨ m · (p n (f o) (~ i)) ⟩
    m · (g (n · (f o)))   ≡⟨ sym (p m (n · (f o)))⟩
    g (m · (n · (f o)))   ≡⟨ cong g IH ⟩
    g ((m · n) · (f o))   ≡⟨ p (m · n) (f o) ⟩
    (m · n) · (g (f o))   ≡[ i ]⟨ (m · n) · q {o} (~ i) ⟩
    (m · n) · (f (suc o)) ∎
    where
    IH = ind-assoc _·_ f g p q base m n o

+-comm : ∀ m n → m + n ≡ n + m
+-comm m (pos n) = ind-comm _+_ pos sucInt refl sucInt+ +sucInt pos0+ m n
+-comm m (negsuc n) = ind-comm _+_ negsuc predInt refl predInt+ +predInt negsuc0+ m n

+-assoc : ∀ m n o → m + (n + o) ≡ (m + n) + o
+-assoc m n (pos o) = ind-assoc _+_ pos sucInt +sucInt refl (λ _ _ → refl) m n o
+-assoc m n (negsuc o) = ind-assoc _+_ negsuc predInt +predInt refl +predInt m n o

·-nullifiesʳ : ∀ x → x · 0 ≡ 0
·-nullifiesʳ (pos    n) i = signed spos (ℕ.·-nullifiesʳ n i)
·-nullifiesʳ (negsuc n) i = signed sneg (ℕ.·-nullifiesʳ n i)

·-nullifiesˡ : ∀ x → 0 · x ≡ 0
·-nullifiesˡ (pos    n) i = pos (ℕ.·-nullifiesˡ n i)
·-nullifiesˡ (negsuc n)   = refl

+-identityʳ : ∀ x → x + 0 ≡ x
+-identityʳ x = refl

+-identityˡ : ∀ x → 0 + x ≡ x
+-identityˡ x = +-comm 0 x

·-identityʳ : ∀ x → x · 1 ≡ x
·-identityʳ x = cong₂ signed (⊕-comm (sgn x) spos) (ℕ.·-identityʳ (abs x)) ∙ signed-inv x

·-identityˡ : ∀ x → 1 · x ≡ x
·-identityˡ x = (λ i → signed (sgn x) (ℕ.+-comm (abs x) 0 i)) ∙ signed-inv x

·-comm : ∀ m n → m · n ≡ n · m
·-comm (pos m) n = cong₂ signed (⊕-comm spos (sgn n)) (ℕ.·-comm m (abs n))
·-comm (negsuc m) n = cong₂ signed (⊕-comm sneg (sgn n)) (ℕ.·-comm (suc m) (abs n))

predInt-neg : ∀ a → predInt (- a) ≡ - (sucInt a)
predInt-neg (pos     zero  ) = refl
predInt-neg (pos    (suc n)) = refl
predInt-neg (negsuc  zero  ) = refl
predInt-neg (negsuc (suc n)) = refl

sucInt-neg : ∀ a → sucInt (- a) ≡ - (predInt a)
sucInt-neg (pos     zero        ) = refl
sucInt-neg (pos    (suc zero)   ) = refl
sucInt-neg (pos    (suc (suc n))) = refl
sucInt-neg (negsuc  zero        ) = refl
sucInt-neg (negsuc (suc n)      ) = refl

private
  lem : ∀ n → -(0 + n) ≡ 0 - n
  lem n i = +-identityˡ (- +-identityˡ n i) (~ i)

negate-+ : ∀ m n → - (m + n) ≡  (- m) + (- n)
negate-+ (pos zero) = lem
negate-+ (pos (suc m)) n = (λ i → - sucInt+ (pos m) n (~ i)) ∙ sym (predInt-neg (pos m + n)) ∙ (λ i → predInt (negate-+ (pos m) n i)) ∙ predInt+ (- pos m) (- n) ∙ λ i → predInt-neg (pos m) i + - n
negate-+ (negsuc zero) n = predInt-inj (- (negsuc zero + n)) (pos 1 + - n) $ predInt-neg (negsuc 0 + n) ∙ (λ i → - sucInt+ (negsuc 0) n i) ∙ lem n ∙ sym (predInt+ (pos 1) (- n))
negate-+ (negsuc (suc m)) n = (λ i → - predInt+ (negsuc m) n (~ i)) ∙ sym (sucInt-neg (negsuc m + n)) ∙ (λ i → sucInt (negate-+ (negsuc m) n i)) ∙ sucInt+ (pos (suc m)) (- n)

negate-·ˡ : ∀ a b → -(a · b) ≡ (- a) · b
negate-·ˡ (pos zero) b = neg-signed-not (sgn b) 0 ∙ signed-zero (not (sgn b)) ∙ sym (signed-zero (sgn b))
negate-·ˡ (pos (suc n)) b = neg-signed-not (sgn b) _
negate-·ˡ (negsuc n) b = neg-signed-not (not (sgn b)) _ ∙ cong₂ signed (notnot (sgn b)) refl

negate-·ʳ : ∀ a b → -(a · b) ≡ a · (- b)
negate-·ʳ a b = (λ i → - ·-comm a b i) ∙ negate-·ˡ b a ∙ ·-comm (- b) a

negsuc+negsuc : ∀ a x → (negsuc a +negsuc x) ≡ negsuc (suc (a +ⁿ x))
negsuc+negsuc a zero = λ i → negsuc $ suc $ ℕ.+-comm 0 a i
negsuc+negsuc a (suc x) = let r = negsuc+negsuc a x in
  predInt (negsuc a +negsuc x)    ≡⟨ (λ i → predInt (r i)) ⟩
  predInt (negsuc (suc (a +ⁿ x))) ≡⟨ refl ⟩
  negsuc (suc (suc (a +ⁿ x)))     ≡⟨ (λ i → negsuc $ suc $ ℕ.+-suc a x (~ i)) ⟩
  negsuc (suc (a +ⁿ suc x))       ∎

signed-distrib : ∀ s m n → signed s (m +ⁿ n) ≡ signed s m + signed s n
signed-distrib s zero n = (sym $ +-identityˡ (signed s n)) ∙ λ i → signed-zero s (~ i) + signed s n
signed-distrib spos (suc m) n = cong sucInt (signed-distrib spos m n) ∙ sucInt+pos n (pos m)
signed-distrib sneg (suc m) zero i = negsuc (ℕ.+-comm m 0 i)
signed-distrib sneg (suc m) (suc n) = (λ i → negsuc (ℕ.+-suc m n i)) ∙  sym (negsuc+negsuc m n)

·-pos-suc : ∀ m n → pos (suc m) · n ≡ n + pos m · n
·-pos-suc m n = signed-distrib (sgn n) (abs n) (m ℕ.· abs n) ∙ λ i → signed-inv n i + signed (sgn n) (m ·ⁿ abs n)

·-negsuc-suc : ∀ m n → negsuc (suc m) · n ≡ - n + negsuc m · n
·-negsuc-suc m n = signed-distrib (not (sgn n)) (abs n) (suc m ℕ.· abs n) ∙ λ i → γ i + negsuc m · n
  where γ = sym (neg-signed-not (sgn n) (abs n)) ∙ cong -_ (signed-inv n)

-- this is a similar proof to the one in Cubical.HITs.QuoInt.Properties
--   but we have less definitional equalities
-- ·-assoc : ∀ m n o → m · (n · o) ≡ m · n · o
-- ·-assoc m n o     with abs m | signed-inv m
-- ... | zero   | pm = {!   !}
-- ... | suc mⁿ | pm with abs n | signed-inv n
-- ... | zero   | pn = (λ i → signed s₁ (γ₁ i)) ∙ signed-zero s₁ ∙ sym (signed-zero s₂) ∙ (λ i → signed s₂ (γ₂ i)) where
--   s₁ = sgn m ⊕ sgn (signed (sgn n ⊕ sgn o) 0)
--   s₂ = sgn (signed (sgn m ⊕ sgn n) (mⁿ ·ⁿ 0)) ⊕ sgn o
--   γ₁ = suc mⁿ ·ⁿ abs (signed (sgn n ⊕ sgn o) 0)        ≡⟨ {!   !} ⟩
--        suc mⁿ ·ⁿ 0                                     ≡⟨ {!   !} ⟩
--        0                                               ∎
--   γ₂ = 0                                               ≡⟨ {!   !} ⟩
--        0 ·ⁿ abs o                                      ≡⟨ {!   !} ⟩
--        (mⁿ ·ⁿ 0) ·ⁿ abs o                              ≡⟨ {!   !} ⟩
--        abs (signed (sgn m ⊕ sgn n) (mⁿ ·ⁿ 0)) ·ⁿ abs o ∎
-- ... | suc nⁿ | pn with abs o | signed-inv o
-- ... | zero   | po = (λ i → signed s₁ (γ₁ i)) ∙ signed-zero s₁ ∙ sym (signed-zero s₂) ∙ (λ i → signed s₂ (γ₂ i)) where
--   s₁ = sgn m ⊕ sgn (signed (sgn n ⊕ sgn o) (nⁿ ·ⁿ zero))
--   s₂ = sgn (signed (sgn m ⊕ sgn n) (suc mⁿ ·ⁿ suc nⁿ)) ⊕ sgn o
--   γ₁ = suc mⁿ ·ⁿ abs (signed (sgn n ⊕ sgn o) (nⁿ ·ⁿ 0)) ≡[ i ]⟨ suc mⁿ ·ⁿ abs-signed (sgn n ⊕ sgn o) (nⁿ ·ⁿ 0) i ⟩
--        suc mⁿ ·ⁿ                             (nⁿ ·ⁿ 0)  ≡[ i ]⟨ suc mⁿ ·ⁿ (ℕ.·-nullifiesʳ nⁿ i) ⟩
--        suc mⁿ ·ⁿ                                    0   ≡⟨ ℕ.·-nullifiesʳ (suc mⁿ) ⟩
--                                                     0 ∎
--   γ₂ = 0 ≡⟨ sym $ ℕ.·-nullifiesʳ (abs (signed (sgn m ⊕ sgn n) (suc mⁿ ·ⁿ suc nⁿ))) ⟩
--        abs (signed (sgn m ⊕ sgn n) (suc mⁿ ·ⁿ suc nⁿ)) ·ⁿ 0 ∎
-- ... | suc oⁿ | po = cong₂ signed γ₁ γ₂ where
--   γ₁ = sgn m ⊕ sgn (signed (sgn n ⊕ sgn o) (suc nⁿ ·ⁿ suc oⁿ)) ≡[ i ]⟨ sgn m ⊕ sgn-signed (sgn n ⊕ sgn o) (oⁿ +ⁿ nⁿ ·ⁿ suc oⁿ) i ⟩
--        sgn m ⊕ (sgn n ⊕ sgn o)                                 ≡⟨ ⊕-assoc (sgn m) (sgn n) (sgn o) ⟩
--        (sgn m ⊕ sgn n) ⊕ sgn o                                 ≡[ i ]⟨ sgn-signed (sgn m ⊕ sgn n) (nⁿ +ⁿ mⁿ ·ⁿ suc nⁿ) (~ i) ⊕ sgn o ⟩
--        sgn (signed (sgn m ⊕ sgn n) (suc mⁿ ·ⁿ suc nⁿ)) ⊕ sgn o ∎
--   γ₂ = suc mⁿ ·ⁿ abs (signed (sgn n ⊕ sgn o) (suc nⁿ ·ⁿ suc oⁿ)) ≡[ i ]⟨ suc mⁿ ·ⁿ abs-signed (sgn n ⊕ sgn o) (suc nⁿ ·ⁿ suc oⁿ) i ⟩
--        suc mⁿ ·ⁿ (suc nⁿ ·ⁿ suc oⁿ)                              ≡⟨ ℕ.·-assoc (suc mⁿ) (suc nⁿ) (suc oⁿ) ⟩
--        (suc mⁿ ·ⁿ suc nⁿ) ·ⁿ suc oⁿ                              ≡[ i ]⟨ abs-signed (sgn m ⊕ sgn n) (suc mⁿ ·ⁿ suc nⁿ) (~ i) ·ⁿ suc oⁿ ⟩
--        abs (signed (sgn m ⊕ sgn n) (suc mⁿ ·ⁿ suc nⁿ)) ·ⁿ suc oⁿ ∎

private
  lemma2 : ∀ a b c →  c +ⁿ (b +ⁿ a ·ⁿ suc b) ·ⁿ suc c
                   ≡ (c +ⁿ b ·ⁿ suc c) +ⁿ a ·ⁿ suc (c +ⁿ b ·ⁿ suc c)
  lemma2 a b c =
    c +ⁿ (b +ⁿ a ·ⁿ suc b) ·ⁿ suc c                 ≡⟨ (λ i → c +ⁿ ℕ.·-distribʳ b (a ·ⁿ suc b) (suc c) (~ i)) ⟩
    c +ⁿ (b ·ⁿ suc c +ⁿ (a ·ⁿ suc b) ·ⁿ suc c)      ≡⟨ ℕ.+-assoc c _ _ ⟩
    (c +ⁿ b ·ⁿ suc c) +ⁿ (a ·ⁿ suc b) ·ⁿ suc c      ≡⟨ (λ i → (c +ⁿ b ·ⁿ suc c) +ⁿ ℕ.·-assoc a (suc b) (suc c) (~ i)) ⟩
    (c +ⁿ b ·ⁿ suc c) +ⁿ a ·ⁿ (suc b ·ⁿ suc c)      ≡⟨ refl ⟩
    (c +ⁿ b ·ⁿ suc c) +ⁿ a ·ⁿ suc (c +ⁿ b ·ⁿ suc c) ∎

-- this is a proof ported from the non-cubical standard library
·-assoc : ∀ x y z → (x · y) · z ≡ x · (y · z)
·-assoc (pos 0) y z = (λ i → ·-nullifiesˡ y i · z) ∙ ·-nullifiesˡ z ∙ sym (·-nullifiesˡ (y · z))
·-assoc x (pos 0) z = (λ i → ·-nullifiesʳ x i · z) ∙ ·-nullifiesˡ z ∙ sym (·-nullifiesʳ x)  ∙ (λ i → x · ·-nullifiesˡ z (~ i))
·-assoc x y (pos 0) = ·-nullifiesʳ (x · y) ∙ sym (·-nullifiesʳ x) ∙ (λ i → x · ·-nullifiesʳ y (~ i))
·-assoc (negsuc   a ) (negsuc   b ) (pos (suc c)) i = (pos (suc (lemma2 a b c i)))
·-assoc (negsuc   a ) (pos (suc b)) (negsuc   c ) i = (pos (suc (lemma2 a b c i)))
·-assoc (pos (suc a)) (pos (suc b)) (pos (suc c)) i = (pos (suc (lemma2 a b c i)))
·-assoc (pos (suc a)) (negsuc   b ) (negsuc   c ) i = (pos (suc (lemma2 a b c i)))
·-assoc (negsuc   a ) (negsuc   b ) (negsuc   c ) i = (negsuc   (lemma2 a b c i) )
·-assoc (negsuc   a ) (pos (suc b)) (pos (suc c)) i = (negsuc   (lemma2 a b c i) )
·-assoc (pos (suc a)) (negsuc   b ) (pos (suc c)) i = (negsuc   (lemma2 a b c i) )
·-assoc (pos (suc a)) (pos (suc b)) (negsuc   c ) i = (negsuc   (lemma2 a b c i) )

-- this is a similar proof to the one in Cubical.HITs.QuoInt.Properties
·-distribˡ-pos : ∀ o m n → (pos o · m) + (pos o · n) ≡ pos o · (m + n)
·-distribˡ-pos zero m n = (λ i → ·-nullifiesˡ m i + ·-nullifiesˡ n i) ∙ sym (·-nullifiesˡ (m + n))
·-distribˡ-pos (suc o) m n =
  pos (suc o) · m + pos (suc o) · n ≡[ i ]⟨ ·-pos-suc o m i + ·-pos-suc o n i ⟩
  m + pos o · m + (n + pos o · n)   ≡⟨ +-assoc (m + pos o · m) n (pos o · n) ⟩
  m + pos o · m + n + pos o · n     ≡[ i ]⟨ +-assoc m (pos o · m) n (~ i) + pos o · n ⟩
  m + (pos o · m + n) + pos o · n   ≡[ i ]⟨ m + +-comm (pos o · m) n i + pos o · n ⟩
  m + (n + pos o · m) + pos o · n   ≡[ i ]⟨ +-assoc m n (pos o · m) i + pos o · n ⟩
  m + n + pos o · m + pos o · n     ≡⟨ sym (+-assoc (m + n) (pos o · m) (pos o · n)) ⟩
  m + n + (pos o · m + pos o · n)   ≡⟨ cong ((m + n) +_) (·-distribˡ-pos o m n) ⟩
  m + n + pos o · (m + n)           ≡⟨ sym (·-pos-suc o (m + n)) ⟩
  pos (suc o) · (m + n) ∎

neg-pos-neg : ∀ x → - pos x ≡ neg x
neg-pos-neg zero = refl
neg-pos-neg (suc x) = refl

·-distribˡ-neg : ∀ o m n → (neg o · m) + (neg o · n) ≡ neg o · (m + n)
·-distribˡ-neg o m n =
     neg o · m  +    neg o · n  ≡[ i ]⟨ neg-pos-neg o (~ i) · m + neg-pos-neg o (~ i) · n ⟩
  -  pos o · m  + -  pos o · n  ≡[ i ]⟨ negate-·ˡ (pos o) m (~ i) + negate-·ˡ (pos o) n (~ i) ⟩
  - (pos o · m) + - (pos o · n) ≡⟨ sym (negate-+ (pos o · m) (pos o · n)) ⟩
  - (pos o · m + pos o · n)     ≡⟨ cong -_ (·-distribˡ-pos o m n) ⟩
  - (pos o · (m + n))           ≡⟨ negate-·ˡ (pos o) (m + n) ⟩
  -  pos o · (m + n)            ≡[ i ]⟨ neg-pos-neg o i · (m + n) ⟩
     neg o · (m + n)            ∎

·-distribˡ : ∀ o m n → (o · m) + (o · n) ≡ o · (m + n)
·-distribˡ (pos    o) m n = ·-distribˡ-pos o m n
·-distribˡ (negsuc o) m n = ·-distribˡ-neg (suc o) m n

·-distribʳ : ∀ m n o → (m · o) + (n · o) ≡ (m + n) · o
·-distribʳ m n o = (λ i → ·-comm m o i + ·-comm n o i) ∙ ·-distribˡ o m n ∙ ·-comm o (m + n)

·-neg1 : ∀ x → -1 · x ≡ - x
·-neg1 x = sym (neg-signed-not (sgn x) (abs x +ⁿ 0)) ∙ (λ i → - signed (sgn x) (ℕ.+-comm (abs x) 0 i)) ∙ cong -_ (signed-inv x)

private
  possuc+negsuc≡0 : ∀ n → (pos (suc n) +negsuc n) ≡ pos 0
  possuc+negsuc≡0 zero = refl
  possuc+negsuc≡0 (suc n) = sym ind ∙ possuc+negsuc≡0 n where
    ind =           pos      (suc n)   +negsuc n  ≡⟨ refl ⟩
           predInt (pos (suc (suc n))) +negsuc n  ≡⟨ sym $ predInt+negsuc n (pos (suc (suc n))) ⟩
           predInt (pos (suc (suc n))  +negsuc n) ∎

  sucInt[negsuc+pos]≡0 : ∀ n → sucInt (negsuc n +pos n) ≡ pos 0
  sucInt[negsuc+pos]≡0 zero = refl
  sucInt[negsuc+pos]≡0 (suc n) = let r = sucInt[negsuc+pos]≡0 n in sym ind ∙ r where
    ind = sucInt (        negsuc n        +pos n ) ≡⟨ refl ⟩
          sucInt (sucInt (negsuc (suc n)) +pos n ) ≡⟨ (λ i → sucInt $ sucInt+pos n (negsuc (suc n)) (~ i)) ⟩
          sucInt (sucInt (negsuc (suc n)  +pos n)) ∎

+-inverseʳ : ∀ a → a + (- a) ≡ 0
+-inverseʳ (pos  zero  ) = refl
+-inverseʳ (pos (suc n)) = possuc+negsuc≡0 n
+-inverseʳ (negsuc   n ) = sucInt[negsuc+pos]≡0 n

+-inverseˡ : ∀ a → (- a) + a ≡ 0
+-inverseˡ a = +-comm (- a) a ∙ +-inverseʳ a

-- Compose sucPathInt with itself n times. Transporting along this
-- will be addition, transporting with it backwards will be subtraction.
-- Use this to define _+'_ for which is easier to prove
-- isEquiv (λ n → n +' m) since _+'_ is defined by transport

sucPathInt : Int ≡ Int
sucPathInt = ua (sucInt , isoToIsEquiv (iso sucInt predInt sucPred predSuc))

addEq : ℕ → Int ≡ Int
addEq zero = refl
addEq (suc n) = (addEq n) ∙ sucPathInt

predPathInt : Int ≡ Int
predPathInt = ua (predInt , isoToIsEquiv (iso predInt sucInt predSuc sucPred))

subEq : ℕ → Int ≡ Int
subEq zero = refl
subEq (suc n) = (subEq n) ∙ predPathInt

_+'_ : Int → Int → Int
m +' pos n    = transport (addEq n) m
m +' negsuc n = transport (subEq (suc n)) m

+'≡+ : _+'_ ≡ _+_
+'≡+ i m (pos zero) = m
+'≡+ i m (pos (suc n)) = sucInt (+'≡+ i m (pos n))
+'≡+ i m (negsuc zero) = predInt m
+'≡+ i m (negsuc (suc n)) = predInt (+'≡+ i m (negsuc n)) --
  -- compPath (λ i → (+'≡+ i (predInt m) (negsuc n))) (sym (predInt+negsuc n m)) i

isEquivAddInt' : (m : Int) → isEquiv (λ n → n +' m)
isEquivAddInt' (pos n)    = isEquivTransport (addEq n)
isEquivAddInt' (negsuc n) = isEquivTransport (subEq (suc n))

isEquivAddInt : (m : Int) → isEquiv (λ n → n + m)
isEquivAddInt = subst (λ add → (m : Int) → isEquiv (λ n → add n m)) +'≡+ isEquivAddInt'

-- below is an alternate proof of isEquivAddInt for comparison
-- We also have two useful lemma here.

minusPlus : ∀ m n → (n - m) + m ≡ n
minusPlus (pos zero) n = refl
minusPlus (pos 1) = sucPred
minusPlus (pos (suc (suc m))) n =
  sucInt ((n +negsuc (suc m)) +pos (suc m)) ≡⟨ sucInt+pos (suc m) _ ⟩
  sucInt (n +negsuc (suc m)) +pos (suc m)   ≡[ i ]⟨ sucPred (n +negsuc m) i +pos (suc m) ⟩
  (n - pos (suc m)) +pos (suc m)            ≡⟨ minusPlus (pos (suc m)) n ⟩
  n ∎
minusPlus (negsuc zero) = predSuc
minusPlus (negsuc (suc m)) n =
  predInt (sucInt (sucInt (n +pos m)) +negsuc m) ≡⟨ predInt+negsuc m _ ⟩
  predInt (sucInt (sucInt (n +pos m))) +negsuc m ≡[ i ]⟨ predSuc (sucInt (n +pos m)) i +negsuc m ⟩
  sucInt (n +pos m) +negsuc m                    ≡⟨ minusPlus (negsuc m) n ⟩
  n ∎

plusMinus : ∀ m n → (n + m) - m ≡ n
plusMinus (pos zero) n = refl
plusMinus (pos (suc m)) = minusPlus (negsuc m)
plusMinus (negsuc m) = minusPlus (pos (suc m))

private
  alternateProof : (m : Int) → isEquiv (λ n → n + m)
  alternateProof m = isoToIsEquiv (iso (λ n → n + m)
                                       (λ n → n - m)
                                       (minusPlus m)
                                       (plusMinus m))

<-irrefl : ∀ a → ¬ (a < a)
<-irrefl (pos  zero  ) = ℕᵒ.<-irrefl 0
<-irrefl (pos (suc n)) = ℕᵒ.<-irrefl (suc n)
<-irrefl (negsuc   n ) = ℕᵒ.<-irrefl n

<-trans : ∀ a b c → a < b → b < c → a < c
<-trans (pos    a) (pos    b) (pos    c) a<b b<c = ℕᵒ.<-trans a<b b<c
<-trans (negsuc a) (pos    b) (pos    c) a<b b<c = tt
<-trans (negsuc a) (negsuc b) (pos    c) a<b b<c = tt
<-trans (negsuc a) (negsuc b) (negsuc c) a<b b<c = ℕᵒ.<-trans b<c a<b

<-asym : ∀ a b → a < b → ¬(b < a)
<-asym a b p q = <-irrefl _ (<-trans _ _ _ p q)

private
  swap-∥⊎∥ : ∀{ℓ ℓ'} (a : Type ℓ) (b : Type ℓ') → ∥ a ⊎ b ∥ → ∥ b ⊎ a ∥
  swap-∥⊎∥ a b = PropTrunc.elim (λ _ → propTruncIsProp) λ{ (inl p) → ∣ inr p ∣ ; (inr p) → ∣ inl p ∣ }

<-cotrans : ∀ a b x → a < b → ∥ (a < x) ⊎ (x < b) ∥
<-cotrans (pos    a) (pos    b) (pos    x) a<b = ℕᵒ.<-cotrans _ _ x a<b
<-cotrans (pos    a) (pos    b) (negsuc x) a<b = ∣ inr tt ∣
<-cotrans (negsuc a) (pos    b) (pos    x) a<b = ∣ inl tt ∣
<-cotrans (negsuc a) (pos    b) (negsuc x) a<b = ∣ inr tt ∣
<-cotrans (negsuc a) (negsuc b) (pos    x) a<b = ∣ inl tt ∣
<-cotrans (negsuc a) (negsuc b) (negsuc x) a<b = swap-∥⊎∥ _ _ (ℕᵒ.<-cotrans _ _ x a<b)

data Trichotomy (m n : Int) : Type₀ where
  lt : m < n → Trichotomy m n
  eq : m ≡ n → Trichotomy m n
  gt : n < m → Trichotomy m n

_≟_ : ∀ m n → Trichotomy m n
pos    a ≟ pos    b with a ℕᵒ.≟ b
... | ℕᵒ.lt p = lt p
... | ℕᵒ.eq p = eq λ i → pos (p i)
... | ℕᵒ.gt p = gt p
pos    a ≟ negsuc b = gt tt
negsuc a ≟ pos    b = lt tt
negsuc a ≟ negsuc b with a ℕᵒ.≟ b
... | ℕᵒ.lt p = gt p
... | ℕᵒ.eq p = eq λ i → negsuc (p i)
... | ℕᵒ.gt p = lt p

sucInt-reflects-< : ∀ x y → sucInt x < sucInt y → x < y
sucInt-reflects-< (pos         x ) (pos         y ) p = ℕᵒ.suc-reflects-< x y p
sucInt-reflects-< (pos         x ) (negsuc  zero  ) p = ℕᵒ.¬-<-zero p
sucInt-reflects-< (negsuc      x ) (pos         y ) p = tt
sucInt-reflects-< (negsuc  zero  ) (negsuc  zero  ) p = p
sucInt-reflects-< (negsuc (suc x)) (negsuc  zero  ) p = ℕᵒ.zero-<-suc x
sucInt-reflects-< (negsuc (suc x)) (negsuc (suc y)) p = ℕᵒ.suc-preserves-< y x p

predInt-reflects-< : ∀ x y → predInt x < predInt y → x < y
predInt-reflects-< (pos  zero  ) (pos  zero  ) p = p
predInt-reflects-< (pos  zero  ) (pos (suc y)) p = ℕᵒ.zero-<-suc y
predInt-reflects-< (pos (suc x)) (pos (suc y)) p = ℕᵒ.suc-preserves-< x y p
predInt-reflects-< (pos  zero  ) (negsuc   y ) p = ℕᵒ.¬-<-zero p
predInt-reflects-< (negsuc   x ) (pos      y ) p = tt
predInt-reflects-< (negsuc   x ) (negsuc   y ) p = ℕᵒ.suc-reflects-< y x p

sucInt-preserves-< : ∀ x y → x < y → sucInt x < sucInt y
sucInt-preserves-< (pos         x ) (pos         y ) p = ℕᵒ.suc-preserves-< x y p
sucInt-preserves-< (negsuc  zero  ) (pos         y ) p = ℕᵒ.zero-<-suc y
sucInt-preserves-< (negsuc (suc x)) (pos         y ) p = tt
sucInt-preserves-< (negsuc  zero  ) (negsuc  zero  ) p = p
sucInt-preserves-< (negsuc  zero  ) (negsuc (suc y)) p = ℕᵒ.¬-<-zero p
sucInt-preserves-< (negsuc (suc x)) (negsuc  zero  ) p = tt
sucInt-preserves-< (negsuc (suc x)) (negsuc (suc y)) p = ℕᵒ.suc-reflects-< y x p

predInt-preserves-< : ∀ x y → x < y → predInt x < predInt y
predInt-preserves-< (pos  zero  ) (pos  zero  ) p = p
predInt-preserves-< (pos  zero  ) (pos (suc y)) p = tt
predInt-preserves-< (pos (suc x)) (pos  zero  ) p = ℕᵒ.¬-<-zero p
predInt-preserves-< (pos (suc x)) (pos (suc y)) p = ℕᵒ.suc-reflects-< x y p
predInt-preserves-< (negsuc   x ) (pos  zero  ) p = ℕᵒ.zero-<-suc x
predInt-preserves-< (negsuc   x ) (pos (suc y)) p = tt
predInt-preserves-< (negsuc   x ) (negsuc   y ) p = ℕᵒ.suc-preserves-< y x p

+-preserves-<ʳ : ∀ a b x → a < b → (a + x) < (b + x)
+-preserves-<ʳ a b (pos     zero  ) a<b = a<b
+-preserves-<ʳ a b (pos    (suc n)) a<b =  sucInt-preserves-< (a +pos n) (b +pos n) (+-preserves-<ʳ a b (pos n) a<b)
+-preserves-<ʳ a b (negsuc  zero  ) a<b = predInt-preserves-< a b a<b
+-preserves-<ʳ a b (negsuc (suc n)) a<b = predInt-preserves-< (a +negsuc n) (b +negsuc n) (+-preserves-<ʳ a b (negsuc n) a<b)

+-reflects-<ʳ : ∀ a b x → (a + x) < (b + x) → a < b
+-reflects-<ʳ a b x =
  a +  x      < b +  x      →⟨ +-preserves-<ʳ (a + x) (b + x) (- x) ⟩
  a +  x - x  < b +  x - x  →⟨ transp (λ i → +-assoc a x (- x) (~ i) < +-assoc b x (- x) (~ i)) i0 ⟩
  a + (x - x) < b + (x - x) →⟨ transp (λ i → (a + +-inverseʳ x i) < (b + +-inverseʳ x i)) i0 ⟩
  a +  0      < b +  0      →⟨⟩
  a           < b           ◼

+-reflects-<ˡ : ∀ a b x → (x + a) < (x + b) → a < b
+-reflects-<ˡ a b x p = +-reflects-<ʳ a b x (transport (λ i → +-comm x a i < +-comm x b i) p)

+-<-extensional : ∀ w x y z → (w + x) < (y + z) → ∥ (w < y) ⊎ (x < z) ∥
+-<-extensional w x y z r with w ≟ y | x ≟ z
... | lt w<y | q = ∣ inl w<y ∣
... | eq w≡y | q = ∣ inr $ +-reflects-<ˡ x z y (transport (λ i → (w≡y i + x) < (y + z)) r) ∣
... | gt y<w | q = PropTrunc.elim (λ _ → propTruncIsProp) γ (<-cotrans (w + x) (y + z) (y + x) r)
  where γ : _ ⊎ _ → _
        γ (inl p) = ∣ inr (⊥.elim {A = λ _ → x < z} (<-asym y w y<w (+-reflects-<ʳ w y x p))) ∣
        γ (inr p) = ∣ inr (+-reflects-<ˡ x z y p) ∣


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

-- The Simplex category Δ
module Categories.Category.Instance.Simplex where

open import Level using (0ℓ)
open import Data.Product
open import Data.Fin.Base using (Fin; zero; suc; _≤_; _<_; inject₁; punchIn)
open import Data.Nat.Base using (ℕ; zero; suc; z≤n; s≤s)
open import Function renaming (id to idF; _∘_ to _∙_)

open import Relation.Binary.PropositionalEquality using (_≡_; refl; sym; trans; cong)

open import Categories.Category.Core using (Category)

--------------------------------------------------------------------------------
-- The Simplex Category
--
-- Classically, the category Δ is defined as follows:
--   * Objects are Natural Numbers
--   * Morphisms 'm ⇒ n' are order-preserving maps 'Fin m → Fin n'
--
-- We _can_ define this version of Δ, but it is generally /not/ the version we want.
-- Most of the time we work with Δ, we only care about 2 classes of maps:
--   * Face maps 'δᵢ : n ⇒ (1 + n)' whose image omits 'i'
--   * Boundary maps 'σᵢ : (1 + n) ⇒ n' such that 'σᵢ i = σᵢ (i + 1) = i'
-- It turns out that all of the maps of Δ can be decomposed into compositions
-- of these 2 classes of maps[1], so most texts will define Functors out of Δ
-- based solely how they act on these morphisms.
--
-- Therefore, we want our definition of Δ to put these morphisms front and center,
-- by encoding our homs as follows:

infix 4 _Δ⇒_
infixr 9 _⊚_

data _Δ⇒_ : ℕ → ℕ → Set where
  ε  : ∀ {n} → n Δ⇒ n
  δ  : ∀ {n} → (i : Fin (suc n)) → n Δ⇒ (suc n)
  σ  : ∀ {n} → (j : Fin n) → (suc n) Δ⇒ n
  _⊚_ : ∀ {l m n} → m Δ⇒ n → l Δ⇒ m → l Δ⇒ n

-- However, this raises some tricky questions about equality of morphisms. It is tempting
-- to use the Simplical Identities[2] directly as our notion of equality:
--   * δᵢ ∘ δⱼ = δⱼ₊₁ ∘ δᵢ if i ≤ j
--   * σⱼ ∘ σᵢ = σᵢ ∘ σⱼ₊₁ if i ≤ j
--   * σⱼ ∘ δᵢ = δᵢ ∘ σⱼ₋₁ if i < j
--   * σⱼ ∘ δᵢ = id        if i = j or i = j + 1
--   * σⱼ ∘ δᵢ = δᵢ₋₁ ∘ σⱼ if j + 1 < i
--
-- However, working with these isn't exactly pleasant and will prove these
-- as theorems later. Instead of quotienting by these identities,
-- we appeal to the denotational semantics of our formal chains of morphisms.
-- In this case we interpret our morphisms as maps between finite ordinals:

face : ∀ {n} → Fin (ℕ.suc n) → Fin n → Fin (ℕ.suc n)
face = punchIn

degen : ∀ {n} → Fin n → Fin (ℕ.suc n) → Fin n
degen Fin.zero    Fin.zero    = Fin.zero
degen Fin.zero    (Fin.suc k) = k
degen (Fin.suc i) Fin.zero    = Fin.zero
degen (Fin.suc i) (Fin.suc k) = Fin.suc (degen i k)

⟦_⟧ : ∀ {m n} → m Δ⇒ n → (Fin m → Fin n)
⟦ ε ⟧ x = x
⟦ δ i ⟧ x = face i x
⟦ σ j ⟧ x = degen j x
⟦ f ⊚ g ⟧ x = ⟦ f ⟧ (⟦ g ⟧ x)

-- Now, we can define equality of morphisms by appealing to our semantics!
infix  4 _≗_

-- We wrap this in a record so that we don't compute away what the original morphsims were.
record _≗_ {m n} (f g : m Δ⇒ n) : Set where
  constructor Δ-eq
  field
    Δ-pointwise : ∀ {x} → ⟦ f ⟧ x ≡ ⟦ g ⟧ x

open _≗_ public

-- Now, we get the same benefits of being able to do induction on our input
-- arguments when trying to prove equalities, as well as being able to define functors
-- out of Δ via their action on face/degeneracy maps.

Δ : Category 0ℓ 0ℓ 0ℓ
Δ = record
  { Obj = ℕ
  ; _⇒_ = _Δ⇒_
  ; _≈_ = _≗_
  ; id = ε
  ; _∘_ = _⊚_
  ; assoc = Δ-eq refl
  ; sym-assoc = Δ-eq refl
  ; identityˡ = Δ-eq refl
  ; identityʳ = Δ-eq refl
  ; identity² = Δ-eq refl
  ; equiv = record
    { refl = Δ-eq refl
    ; sym = λ eq → Δ-eq (sym (Δ-pointwise eq))
    ; trans = λ eq₁ eq₂ → Δ-eq (trans (Δ-pointwise eq₁) (Δ-pointwise eq₂))
    }
  ; ∘-resp-≈ = λ {_ _ _ f g h i} eq₁ eq₂ → Δ-eq (trans (cong ⟦ f ⟧ (Δ-pointwise eq₂)) (Δ-pointwise eq₁))
  }

-- For completeness, here are the aforementioned simplical identities. These may seem /slightly/
-- different than the ones presented before, but it's just re-indexing to avoid having to use 'pred'.
open Category Δ

-- δᵢ ∘ δⱼ = δⱼ₊₁ ∘ δᵢ if i ≤ j
face-comm : ∀ {n} (i j : Fin (suc n))  → i ≤ j → δ (inject₁ i) ∘ δ j ≈ δ (suc j) ∘ δ i
face-comm zero    _       z≤n      = Δ-eq refl
face-comm (suc i) (suc j) (s≤s le) = Δ-eq (λ { {zero} → refl ; {suc x} → cong suc (Δ-pointwise (face-comm i j le)) })

-- σⱼ ∘ σᵢ = σᵢ ∘ σⱼ₊₁ if i ≤ j
degen-comm : ∀ {n} (i j : Fin n) → i ≤ j → σ j ∘ σ (inject₁ i) ≈ σ i ∘ σ (suc j)
degen-comm zero    zero    z≤n      = Δ-eq λ { {zero} → refl ; {suc x} → refl }
degen-comm zero    (suc _) z≤n      = Δ-eq λ { {zero} → refl ; {suc x} → refl }
degen-comm (suc i) (suc j) (s≤s le) = Δ-eq (λ { {zero} → refl ; {suc x} → cong suc (Δ-pointwise (degen-comm i j le)) })

-- σⱼ ∘ δᵢ = δᵢ ∘ σⱼ₋₁ if i < j
degen-face-comm : ∀ {n} (i : Fin (suc n)) (j : Fin n) → i < suc j → σ (suc j) ∘ δ (inject₁ i) ≈ δ i ∘ σ j
degen-face-comm zero    _       (s≤s _)  = Δ-eq refl
degen-face-comm (suc i) (suc j) (s≤s le) = Δ-eq (λ { {zero} → refl ; {suc x} → cong suc (Δ-pointwise (degen-face-comm i j le)) })

-- σⱼ ∘ δᵢ = id        if i = j
degen-face-id : ∀ {n} (i j : Fin n) → i ≡ j → σ j ∘ δ (inject₁ i) ≈ id
degen-face-id zero    zero    refl = Δ-eq refl
degen-face-id (suc i) (suc i) refl = Δ-eq (λ { {zero} → refl ; {suc x} → cong suc (Δ-pointwise (degen-face-id i i refl)) })

-- σⱼ ∘ δᵢ = id        if i = j + 1
degen-face-suc-id : ∀ {n} (i : Fin (suc n)) (j : Fin n) → i ≡ suc j → σ j ∘ δ i ≈ id
degen-face-suc-id (suc zero)    zero    refl = Δ-eq λ { {zero} → refl ; {suc x} → refl }
degen-face-suc-id (suc (suc i)) (suc i) refl = Δ-eq λ { {zero} → refl ; {suc x} → cong suc (Δ-pointwise (degen-face-suc-id (suc i) i refl)) }

-- σⱼ ∘ δᵢ = δᵢ₋₁ ∘ σⱼ if j + 1 < i
degen-face-suc-comm : ∀ {n} (i : Fin (suc n)) (j : Fin n) → suc j < i → σ (inject₁ j) ∘ δ (suc i) ≈ δ i ∘ σ j
degen-face-suc-comm (suc (suc i)) zero  (s≤s (s≤s z≤n))   = Δ-eq (λ { {zero} → refl ; {suc x} → refl })
degen-face-suc-comm (suc (suc i)) (suc j) (s≤s le)        = Δ-eq (λ { {zero} → refl ; {suc x} → cong suc (Δ-pointwise (degen-face-suc-comm (suc i) j le)) })

-- Further Work:
-- If we had a means of decomposing any monotone map 'Fin n ⇒ Fin m' into a series of face/boundary
-- maps, we would be able to compute normal forms in Δ by using normalization by evaluation. This is
-- very promising, as it would let us define a reflection tactic to automatically perform
-- proofs about chains of morphisms in Δ.

-- Footnotes:
-- 1. For a proof of this, see Categories for The Working Mathematician VII.5
-- 2. See https://ncatlab.org/nlab/show/simplex+category


-- Module for BCCs, CCCs and BCCCs
module CategoryTheory.BCCCs where

open import CategoryTheory.Categories
open import CategoryTheory.BCCCs.Cartesian public
open import CategoryTheory.BCCCs.Cocartesian public
open import CategoryTheory.BCCCs.Closed public

-- Bicartesian categories
record Bicartesian {n} (ℂ : Category n) : Set (lsuc n) where
    field
        cart   : Cartesian ℂ
        cocart : Cocartesian ℂ

-- Cartesian closed categories
record CartesianClosed {n} (ℂ : Category n) : Set (lsuc n) where
    field
        cart   : Cartesian ℂ
        closed : Closed cart

-- Bicartesian closed categories
record BicartesianClosed {n} (ℂ : Category n) : Set (lsuc n) where
    field
        cart   : Cartesian ℂ
        cocart : Cocartesian ℂ
        closed : Closed cart
    open Cartesian cart public
    open Cocartesian cocart public
    open Closed closed public


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

open import HoTT

module homotopy.HopfJunior where

-- We define the negation function and prove that it’s an equivalence

not : Bool → Bool
not true = false
not false = true

not-not : (b : Bool) → not (not b) == b
not-not true = idp
not-not false = idp

not-equiv : Bool ≃ Bool
not-equiv = equiv not not not-not not-not

-- Definition of the junior Hopf fibration
module HopfJunior = S¹RecType Bool not-equiv
open HopfJunior using (module Wt; Wt; cct; ppt; flattening-S¹)

HopfJunior : S¹ → Type₀
HopfJunior = HopfJunior.f

-- We get that the total space is the type Wt defined in the flattening lemma.
tot-is-Wt : Σ S¹ HopfJunior == Wt
tot-is-Wt = flattening-S¹

-- Now we prove that Wt is equivalent to S¹

private
  -- Map from [Wt] to [S¹]
  to-cct : Unit × Bool → S¹
  to-cct _ = base

  to-ppt : (x : Unit × Bool) → to-cct x == to-cct x
  to-ppt (_ , true) = loop
  to-ppt (_ , false) = idp

  module To = Wt.Rec to-cct to-ppt

  to : Wt → S¹
  to = To.f

  -- Map from [S¹] to [Wt]
  from-base : Wt
  from-base = cct tt true

  from-loop : from-base == from-base
  from-loop = ppt tt true ∙ ppt tt false

  module From = S¹Rec from-base from-loop

  from : S¹ → Wt
  from = From.f

  -- First composite
  abstract
    to-from : (x : S¹) → to (from x) == x
    to-from = S¹-elim idp (↓-∘=idf-in to from
      (ap to (ap from loop)                       =⟨ From.loop-β |in-ctx ap to ⟩
      ap to (ppt tt true ∙ ppt tt false)         =⟨ ap-∙ to (ppt tt true) (ppt tt false) ⟩
      ap to (ppt tt true) ∙ ap to (ppt tt false) =⟨ To.pp-β (tt , true) |in-ctx (λ u → u ∙ ap to (ppt tt false)) ⟩
      loop ∙ ap to (ppt tt false)                =⟨ To.pp-β (tt , false) |in-ctx (λ u → loop ∙ u) ⟩
      loop ∙ idp                                 =⟨ ∙-unit-r loop ⟩
      loop ∎))

  -- Second composite
  from-to-cct : (b : Bool) → from (to (cct tt b)) == cct tt b
  from-to-cct true = idp
  from-to-cct false = ! (ppt tt false)

  abstract
    from-to-ppt : (b : Bool) → ap from (ap to (ppt tt b)) ∙' from-to-cct (not b) == from-to-cct b ∙ ppt tt b
    from-to-ppt true =
      ap from (ap to (ppt tt true)) ∙' ! (ppt tt false) =⟨ To.pp-β (tt , true) |in-ctx (λ u → ap from u ∙' ! (ppt tt false)) ⟩
      ap from loop ∙' ! (ppt tt false)                  =⟨ From.loop-β |in-ctx (λ u → u ∙' ! (ppt tt false)) ⟩
      (ppt tt true ∙ ppt tt false) ∙' ! (ppt tt false)  =⟨ lemma (ppt tt true) (ppt tt false) ⟩
      ppt tt true ∎  where
        lemma : ∀ {i} {A : Type i} {a b c : A} (p : a == b) (q : b == c) → (p ∙ q) ∙' (! q) == p
        lemma idp idp = idp
    from-to-ppt false =
      ap from (ap to (ppt tt false))                    =⟨ To.pp-β (tt , false) |in-ctx ap from ⟩
      ap from (idp {a = base})                          =⟨ ! (!-inv-l (ppt tt false)) ⟩
      ! (ppt tt false) ∙ ppt tt false ∎

    from-to : (x : Wt) → from (to x) == x
    from-to = Wt.elim (from-to-cct ∘ snd) (λ b → ↓-∘=idf-in from to (from-to-ppt (snd b)))

  -- Conclusion
  subresult : Wt ≃ S¹
  subresult = equiv to from to-from from-to

-- Conclusion
result : Σ S¹ HopfJunior ≃ S¹
result = subresult ∘e (coe-equiv tot-is-Wt)


------------------------------------------------------------------------
-- The Agda standard library
--
-- The basic code for equational reasoning with a single relation
------------------------------------------------------------------------

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

open import Relation.Binary

module Relation.Binary.Reasoning.Base.Single
  {a ℓ} {A : Set a} (_∼_ : Rel A ℓ)
  (refl : Reflexive _∼_) (trans : Transitive _∼_)
  where

open import Level using (_⊔_)
open import Relation.Binary.PropositionalEquality as P using (_≡_)

infix  4 _IsRelatedTo_
infix  3 _∎
infixr 2 _∼⟨_⟩_ _≡⟨_⟩_ _≡˘⟨_⟩_ _≡⟨⟩_
infix  1 begin_

-- This seemingly unnecessary type is used to make it possible to
-- infer arguments even if the underlying equality evaluates.

data _IsRelatedTo_ (x y : A) : Set (a ⊔ ℓ) where
  relTo : (x∼y : x ∼ y) → x IsRelatedTo y

begin_ : ∀ {x y} → x IsRelatedTo y → x ∼ y
begin relTo x∼y = x∼y

_∼⟨_⟩_ : ∀ x {y z} → x ∼ y → y IsRelatedTo z → x IsRelatedTo z
_ ∼⟨ x∼y ⟩ relTo y∼z = relTo (trans x∼y y∼z)

_≡⟨_⟩_ : ∀ x {y z} → x ≡ y → y IsRelatedTo z → x IsRelatedTo z
_ ≡⟨ P.refl ⟩ x∼z = x∼z

_≡˘⟨_⟩_ : ∀ x {y z} → y ≡ x → y IsRelatedTo z → x IsRelatedTo z
_ ≡˘⟨ P.refl ⟩ x∼z = x∼z

_≡⟨⟩_ : ∀ x {y} → x IsRelatedTo y → x IsRelatedTo y
_ ≡⟨⟩ x∼y = _ ≡⟨ P.refl ⟩ x∼y

_∎ : ∀ x → x IsRelatedTo x
_∎ _ = relTo refl


module Oscar.Category.Functor where

open import Oscar.Category.Setoid
open import Oscar.Category.Category
open import Oscar.Category.Semifunctor
open import Oscar.Level

record Categories 𝔬₁ 𝔪₁ 𝔮₁ 𝔬₂ 𝔪₂ 𝔮₂ : Set (lsuc (𝔬₁ ⊔ 𝔪₁ ⊔ 𝔮₁ ⊔ 𝔬₂ ⊔ 𝔪₂ ⊔ 𝔮₂)) where
  constructor _,_
  field
    category₁ : Category 𝔬₁ 𝔪₁ 𝔮₁
    category₂ : Category 𝔬₂ 𝔪₂ 𝔮₂

  module 𝔊₁ = Category category₁
  module 𝔊₂ = Category category₂

  semigroupoids : Semigroupoids _ _ _ _ _ _
  Semigroupoids.semigroupoid₁ semigroupoids = 𝔊₁.semigroupoid
  Semigroupoids.semigroupoid₂ semigroupoids = 𝔊₂.semigroupoid

module _
  {𝔬₁ 𝔪₁ 𝔮₁ 𝔬₂ 𝔪₂ 𝔮₂} (categories : Categories 𝔬₁ 𝔪₁ 𝔮₁ 𝔬₂ 𝔪₂ 𝔮₂)
  where
  open Categories categories

  record IsFunctor
    {μ : 𝔊₁.⋆ → 𝔊₂.⋆}
    (𝔣 : ∀ {x y} → x 𝔊₁.↦ y → μ x 𝔊₂.↦ μ y)
    : Set (𝔬₁ ⊔ 𝔪₁ ⊔ 𝔮₁ ⊔ 𝔬₂ ⊔ 𝔪₂ ⊔ 𝔮₂) where
    instance _ = 𝔊₁.IsSetoid↦
    instance _ = 𝔊₂.IsSetoid↦
    field
      ⦃ isSemifunctor ⦄ : IsSemifunctor semigroupoids 𝔣
    field
      identity : (x : 𝔊₁.⋆) → 𝔣 (𝔊₁.ε {x = x}) ≋ 𝔊₂.ε
    open IsSemifunctor isSemifunctor public

open IsFunctor ⦃ … ⦄ public

open import Oscar.Category.Morphism

record Functor 𝔬₁ 𝔪₁ 𝔮₁ 𝔬₂ 𝔪₂ 𝔮₂ : Set (lsuc (𝔬₁ ⊔ 𝔪₁ ⊔ 𝔮₁ ⊔ 𝔬₂ ⊔ 𝔪₂ ⊔ 𝔮₂)) where
  constructor _,_
  field
    categories : Categories 𝔬₁ 𝔪₁ 𝔮₁ 𝔬₂ 𝔪₂ 𝔮₂
  open Categories categories public

  field
    {μ} : 𝔊₁.⋆ → 𝔊₂.⋆
    𝔣 : ∀ {x y} → x 𝔊₁.↦ y → μ x 𝔊₂.↦ μ y
    ⦃ isFunctor ⦄ : IsFunctor categories 𝔣
  open IsFunctor isFunctor public

-- module _
--   {𝔬₁ 𝔪₁ 𝔮₁} (category₁ : Category 𝔬₁ 𝔪₁ 𝔮₁)
--   {𝔬₂ 𝔪₂ 𝔮₂} (category₂ : Category 𝔬₂ 𝔪₂ 𝔮₂)
--   where

--   private module 𝔊₁ = Category category₁
--   private module 𝔊₂ = Category category₂

--   record IsFunctor
--     (μ : 𝔊₁.⋆ → 𝔊₂.⋆)
--     (𝔣 : ∀ {x y} → x 𝔊₁.↦ y → μ x 𝔊₂.↦ μ y)
--     : Set (𝔬₁ ⊔ 𝔪₁ ⊔ 𝔮₁ ⊔ 𝔬₂ ⊔ 𝔪₂ ⊔ 𝔮₂) where
--     field
--       ⦃ isSemifunctor ⦄ : IsSemifunctor 𝔊₁.semigroupoid 𝔊₂.semigroupoid μ 𝔣
--     field
--       identity : (x : 𝔊₁.⋆) → 𝔣 (𝔊₁.ε {x = x}) ≋ 𝔊₂.ε

-- open IsFunctor ⦃ … ⦄ public

-- record Functor 𝔬₁ 𝔪₁ 𝔮₁ 𝔬₂ 𝔪₂ 𝔮₂ : Set (lsuc (𝔬₁ ⊔ 𝔪₁ ⊔ 𝔮₁ ⊔ 𝔬₂ ⊔ 𝔪₂ ⊔ 𝔮₂)) where
--   field
--     category₁ : Category 𝔬₁ 𝔪₁ 𝔮₁
--     category₂ : Category 𝔬₂ 𝔪₂ 𝔮₂

--   module 𝔊₁ = Category category₁
--   module 𝔊₂ = Category category₂

--   field
--     μ : 𝔊₁.⋆ → 𝔊₂.⋆
--     𝔣 : ∀ {x y} → x 𝔊₁.↦ y → μ x 𝔊₂.↦ μ y
--     ⦃ isFunctor ⦄ : IsFunctor category₁ category₂ μ 𝔣