module Test where

open import Algebra using (CommutativeRing; IsCommutativeRing)

open import Assume using (assume)
import Data.Nat as ℕ
open ℕ using (ℕ; zero; suc)

import Data.Fin as F
open F using (Fin)

open import Relation.Binary using (Rel; Setoid; _Preserves_⟶_)
open import Function using (Inverse; _on_; _∘_; _|>_; id; flip)

open import Algebra.Module using (Module)
module Mod = Module

open import Data.Product using (_,_; proj₂; _×_)

open import Algebra.Module.Construct.DirectProduct using () renaming (⟨module⟩ to ×-module)
open import Level using (_⊔_; Level; 0ℓ; Lift)
open import Data.Vec.Recursive
open import Algebra.Module.Vec.Recursive

module _
  {r ℓ} {CR : CommutativeRing r ℓ}
  where

  -- D B k is the solution to
  -- (A → X) → A ^ suc k → B
  -- i.e. D B k ~ A ^ k → B

  open Module

  D : ∀ {m ℓm} (M : Module CR m ℓm) → ℕ → Set _
  D {m = m} M k = ∀ {X : Set m} → X ^ suc k → Carrierᴹ M

  const
    : ∀ {m ℓm} {{M : Module CR m ℓm}}
    → ∀ {k} → Carrierᴹ M → D M k
  const {k = zero} c x = c
  const {{M}} {k = suc k} c v = 0ᴹ M

  double
    : ∀ {m ℓm} {{M : Module CR m ℓm}}
    → ∀ {k} → D M k → D M k
  double {{M}} x y = let x' = x y in _+ᴹ_ M x' x'

  -- D : Level → ∀ {n ℓn} (N : Module CR n ℓn) → ℕ → Set _
  -- D m N k = {M : Set m} → M → M ^ k → Carrierᴹ N

  -- compose
  --   : ∀ {x} {X : Set x}
  --   → ∀ {n ℓn} {N : Module CR n ℓn}
  --   → ∀ {o ℓo} {O : Module CR o ℓo}
  --   → ∀ {k} → (f : X → D N k) → (g : Carrierᴹ N → D O k)
  --   → X → D O k
  -- compose = ?

  -- _th-derivative_ : ∀ k → (D k → D k) → M.Carrierᴹ → N.Carrierᴹ

  -- k th-derivative f = {!   !}

  -- TODO
  -- can we use M → D N k everywhere?!
  -- data D : ℕ → Set (m ⊔ n) where
  --   z : N.Carrierᴹ → D zero
  --   d : ∀ {k} → (fx : N.Carrierᴹ) → (dfx : M.Carrierᴹ → D k) → D (suc k)

-- module _
--   {r ℓ} {CR : CommutativeRing r ℓ}
--   {m ℓm} {M : Module CR m ℓm}
--   {n ℓn} {N : Module CR n ℓn}
--   where

--   private
--     module CR = CommutativeRing CR
--     module M = Module M
--     module N = Module N

--   private
--     module dmod where

--       Carrierᴹ : ∀ {k : ℕ} → Set (m ⊔ n)
--       Carrierᴹ {k} = D M N k

--       _≈ᴹ_ : ∀ {k} → Rel (D M N k) (m ⊔ ℓn)
--       _≈ᴹ_ (z x) (z y) = Lift (m ⊔ ℓn) (x N.≈ᴹ y)
--       _≈ᴹ_ (d fx dfx) (d fy dfy) = (fx N.≈ᴹ fy) × (∀ v → dfx v ≈ᴹ dfy v)

--       _+ᴹ_ : ∀ {k} → (x y : D M N k) → D M N k
--       _+ᴹ_ (z x) (z y) = z (x N.+ᴹ y)
--       _+ᴹ_ (d fx dfx) (d fy dfy) = d (fx N.+ᴹ fy) λ v → dfx v +ᴹ dfy v

--       _*ₗ_ : ∀ {k} → (s : CR.Carrier) (x : D M N k) → D M N k
--       _*ₗ_ s (z x) = z (s N.*ₗ x)
--       _*ₗ_ s (d fx dfx) = d (s N.*ₗ fx) λ v → s *ₗ dfx v

--       _*ᵣ_ : ∀ {k} → (x : D M N k) (s : CR.Carrier) → D M N k
--       _*ᵣ_ = flip _*ₗ_

--       0ᴹ : ∀ {k} → D M N k
--       0ᴹ {zero} = z N.0ᴹ
--       0ᴹ {suc k} = d N.0ᴹ λ v → 0ᴹ

--       -ᴹ_ : ∀ {k} → D M N k → D M N k
--       -ᴹ_ (z x) = z (N.-ᴹ x)
--       -ᴹ_ (d fx dfx) = d (N.-ᴹ fx) λ v → -ᴹ dfx v

--   instance
--     D-module : ∀ {k : ℕ} → Module CR _ _
--     D-module {k} =
--       record
--       { Carrierᴹ = dmod.Carrierᴹ {k}
--       ; isModule = assume
--       ; dmod
--       }

--   private variable k : ℕ

--   run : D M N k → N.Carrierᴹ
--   run (z x) = x
--   run (d x _) = x

--   extract : D M N zero → N.Carrierᴹ
--   extract = run

--   diff : M.Carrierᴹ → D M N (suc k) → D M N k
--   diff v (d _ x) = x v

--   konst _# : ∀ (c : N.Carrierᴹ) → D M N k
--   konst {k = zero} c = z c
--   konst {k = suc k} c = d c (λ v → konst N.0ᴹ)
--   x # = konst x

--   jacobian : (df : D M N 1) (v : M.Carrierᴹ) → N.Carrierᴹ
--   jacobian df v = df |> diff v |> extract

--   hessian : (df : D M N 2) (v v' : M.Carrierᴹ) → N.Carrierᴹ
--   hessian df v v' = df |> diff v |> diff v' |> extract

--   hack : ∀ {l} → D M N (suc l) → D M N l
--   hack {zero} (d fx dfx) = z fx
--   hack {suc l} (d fx dfx) = d fx λ v → hack {l} (dfx v)

module _ where
  import Real as ℝ
  open ℝ using (ℝ)

  open import Data.Vec.Recursive

  ℝ-isCommutativeRing : IsCommutativeRing ℝ._≈_ ℝ._+_ ℝ._*_ ℝ.-_ 0.0 1.0
  ℝ-isCommutativeRing = assume

  instance
    ℝ-commutativeRing : CommutativeRing 0ℓ 0ℓ
    ℝ-commutativeRing = record { isCommutativeRing = ℝ-isCommutativeRing }

  ℝ-cr : CommutativeRing 0ℓ 0ℓ
  ℝ-cr = ℝ-commutativeRing

  private
    ℝ-mod' : Module ℝ-commutativeRing 0ℓ 0ℓ
    ℝ-mod' = U.⟨module⟩
      where import Algebra.Module.Construct.TensorUnit as U

  ℝ^ : ∀ n → Module ℝ-commutativeRing 0ℓ 0ℓ
  ℝ^ n = ℝ-mod' ^ᴹ n

  ℝ-mod : Module ℝ-commutativeRing 0ℓ 0ℓ
  ℝ-mod = ℝ^ 1

  exp : ∀ {k} → D ℝ-mod k → D ℝ-mod k
  exp {0} f x = ℝ.e^ f x
  exp {1} f' xdx = let (fx , f'x) = f xdx in {! (ℝ.e^ fx) ℝ.* f'x   !}
  exp {k} f y = {!   !}

--   -- TODO
--   -- is this really the identity?
--   var : ∀ {rank k} (c : Mod.Carrierᴹ (ℝ^ rank)) → D (ℝ^ rank) (ℝ^ rank) k
--   var {rank} {zero} c = z c
--   var {rank} {suc n} c = d c (λ v → konst (replicate rank 1.0))

--   instance
--     ℝ^n : ∀ {n} → Module ℝ-cr 0ℓ 0ℓ
--     ℝ^n {n} = ℝ^ n

--   infixl 6 _+_ _-_
--   infixl 7 _*_
--   -- linear and bilinear forms
--   _*_
--     : ∀ {m ℓm} {M : Module ℝ-cr m ℓm} {k rank}
--     → (x y : D M (ℝ^ rank) k)
--     → D M (ℝ^ rank) k

--   open Module {{...}}
--   _+_ _-_
--     : ∀ {r ℓ} {CR : CommutativeRing r ℓ} {m ℓm} {{M : Module CR m ℓm}} (x y : Mod.Carrierᴹ M)
--     → Mod.Carrierᴹ M
--   x + y = x +ᴹ y
--   x - y = x +ᴹ (-ᴹ y)

--   _*_ {rank = rank} (z x) (z y) = z (zipWith ℝ._*_ rank x y)
--   _*_ {rank = rank} dfx@(d fx f'x) dgx@(d gx g'x) =
--     d (zipWith ℝ._*_ rank fx gx) (λ v → hack dfx * g'x v + hack dgx * f'x v)

--   -- convenience function
--   _>-<_
--     : ∀ {k}
--     → (f : ℝ → ℝ) (f' : ∀ {l} → D ℝ-mod ℝ-mod l → D ℝ-mod ℝ-mod l)
--     → D ℝ-mod ℝ-mod k → D ℝ-mod ℝ-mod k
--   (f >-< _) (z x) = z (f x)
--   (f >-< f') dfx@(d x dx) = d (f x) λ v → dx v * f' (hack dfx)

--   ε : ∀ {rank} → Fin rank → ℝ ^ rank
--   ε {zero} n = _
--   ε {suc zero} _ = 1.0
--   ε {2+ rank} F.zero = 1.0 , Mod.0ᴹ (ℝ^ (suc rank))
--   ε {2+ rank} (F.suc n) = Mod.0ᴹ ℝ-mod , ε n

--   ∇_ grad : ∀ {rank} → D (ℝ^ rank) (ℝ^ 1) 1 → ℝ ^ rank
--   ∇_ {rank = rank} f = map (jacobian f) rank unitvecs
--     where
--       unitvecs : (ℝ ^ rank) ^ rank
--       unitvecs = map ε rank (tabulate rank id)

--   grad = ∇_

--   infixl 8 _**_
--   infixr 9 -_
--   infixr 9 e^_

--   -_
--     : ∀ {r ℓ} {CR : CommutativeRing r ℓ} {m ℓm} {{M : Module CR m ℓm}} (x : Mod.Carrierᴹ M)
--     → Mod.Carrierᴹ M
--   -_ x = -ᴹ x

--   ℝ-sgn : ℝ → ℝ
--   ℝ-sgn x = if does (0.0 ℝ.≤? x) then 1.0 else ℝ.- 1.0
--     where
--       open import Data.Bool using (if_then_else_)
--       open import Relation.Nullary using (does)

--   ℝ-abs : ℝ → ℝ
--   ℝ-abs x = x ℝ.* ℝ-sgn x

--   -- the first is the zero-order
--   poly : ∀ {rank k} → ℝ ^ suc rank → D ℝ-mod ℝ-mod k → D ℝ-mod ℝ-mod k
--   poly {rank = zero} cs x = konst cs
--   poly {rank = suc rank} (c , cs) x = konst c + x * poly cs x

--   pow : ℕ → ∀ {k} → D ℝ-mod ℝ-mod k → D ℝ-mod ℝ-mod k
--   pow 0 x = konst 1.0
--   pow 1 x = x
--   pow (suc n) dx = dx * pow n dx

--   log e^_ recip abs sgn : ∀ {k} → D ℝ-mod ℝ-mod k → D ℝ-mod ℝ-mod k
--   log = ℝ.log >-< recip ∘ abs

--   recip (z x) = z (1.0 ℝ.÷ x)
--   recip dfx@(d fx f'x) = d (1.0 ℝ.÷ fx) λ x → - (f'x x * tmp * tmp)
--     where
--       tmp = recip (hack dfx)

--   abs = ℝ-abs >-< sgn

--   sgn = ℝ-sgn >-< λ _ → 0ᴹ

--   e^ z x = z (ℝ.e^ x)
--   e^ dfx@(d fx f'x) = d (run tmp) λ v → f'x v * tmp
--     where
--       tmp = e^ hack dfx

--   infixl 7 _÷_
--   _÷_ _**_ : ∀ {k} (x y : D ℝ-mod ℝ-mod k) → D ℝ-mod ℝ-mod k
--   x ÷ y = x * recip x
--   x ** y = e^ y * log x

--   _! : ℕ → ℕ
--   0 ! = 1
--   1 ! = 1
--   sucn@(suc n) ! = sucn ℕ.* (n !)

--   sterling : ∀ {k} → ℕ → D ℝ-mod ℝ-mod k
--   sterling {k} m = m' * log m' - m'
--     where
--       m' : D ℝ-mod ℝ-mod k
--       m' = ℝ.fromℕ m #

--   -- without the constant denominator term
--   logPoisson' : ∀ {n} → ℕ → D ℝ-mod ℝ-mod n → D ℝ-mod ℝ-mod n
--   logPoisson' k λ' = k' * log λ' - λ'
--     where k' = ℝ.fromℕ k #

--   logPoisson : ∀ {n} → ℕ → D ℝ-mod ℝ-mod n → D ℝ-mod ℝ-mod n
--   logPoisson k λ' = logPoisson' k λ' - sterling k

--   -- descend : ∀ {rank} (f : D (ℝ^ rank) 1 → D ℝ-mod 1) (steps : ℕ) (start : ℝ ^ rank) → ℝ ^ rank
--   -- descend f zero start = start
--   -- descend f (suc steps) start = {!   !}

--   module _ where
--     open import Relation.Binary.PropositionalEquality using (refl)

--     _ : (∇ (var 1.0 * var 1.0)) ℝ.≈ 2.0
--     _ = refl

-- -- -- --   _ : run e^_ 2.0 ℝ.≈ (ℝ.e^ 2.0)
-- -- -- --   _ = refl

-- -- -- --   testpoly : ∀ {n} → D ℝ-mod n → D ℝ-mod n
-- -- -- --   testpoly = poly (1.0 , 2.0 , 3.0)

-- -- -- --   _ : run testpoly 2.0 ℝ.≈ (1.0 + 4.0 + 12.0)
-- -- -- --   _ = refl

-- -- -- --   _ : ∇ testpoly [ 2.0 ] ℝ.≈ (0.0 + 2.0 + 3.0 ℝ.* 2.0 ℝ.* 2.0)
-- -- -- --   _ = refl

-- -- -- --   _ : ∇ testpoly [ 7.0 ] ℝ.≈ (0.0 + 2.0 + 2.0 ℝ.* 3.0 ℝ.* 7.0)
-- -- -- --   _ = refl

-- -- -- --   _ : hessian e^_ 1.0 1.0 0.0 ℝ.≈ (ℝ.e^ 1.0)
-- -- -- --   _ = refl

-- -- -- --   asdf : D ℝ-mod 2
-- -- -- --   asdf = e^ var (2.0 , 1.0 , 0.0)

-- -- -- --   _ : hessian (poly (1.0 , 2.0 , 3.0)) 1.0 1.0 0.0 ℝ.≈ (2.0 ℝ.* 3.0)
-- -- -- --   _ = refl

-- -- -- --   _ : ∇ log [ 1.0 ] ℝ.≈ 1.0
-- -- -- --   _ = refl

-- -- -- --   _ : ∇ log [ -ᴹ 1.0 ] ℝ.≈ 1.0
-- -- -- --   _ = refl

-- -- -- --   _ : ∇ sgn [ 2.0 ] ℝ.≈ 0.0
-- -- -- --   _ = refl

-- -- -- --   _ : ∇ abs [ 2.0 ] ℝ.≈ 1.0
-- -- -- --   _ = refl

-- -- -- --   _ : run abs (- 2.0) ℝ.≈ 2.0
-- -- -- --   _ = refl

-- -- -- --   _ : ∇ abs [ - 1.0 ] ℝ.≈ (- 1.0)
-- -- -- --   _ = refl

-- -- -- --   _ : ∇ recip [ - 2.0 ] ℝ.≈ (- 0.25)
-- -- -- --   _ = refl

-- -- -- --   _ : ∇ log [ 2.0 ] ℝ.≈ 0.5
-- -- -- --   _ = refl


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

module Lecture6 where

import Lecture5
open Lecture5 public

-- Section 6.1 Contractible types

is-contr : {i : Level} → UU i → UU i
is-contr A = Sigma A (λ a → (x : A) → Id a x)

center : {i : Level} {A : UU i} → is-contr A → A
center (dpair c C) = c

-- We make sure that the contraction is coherent in a straightforward way
contraction : {i : Level} {A : UU i} (H : is-contr A) →
  (const A A (center H) ~ id)
contraction (dpair c C) x = concat _ (inv (C c)) (C x)

coh-contraction : {i : Level} {A : UU i} (H : is-contr A) →
  Id (contraction H (center H)) refl
coh-contraction (dpair c C) = left-inv (C c)

ev-pt : {i j : Level} (A : UU i) (a : A) (B : A → UU j) → ((x : A) → B x) → B a
ev-pt A a B f = f a

sing-ind-is-contr : {i j : Level} (A : UU i) (H : is-contr A) (B : A → UU j) →
  B (center H) → (x : A) → B x
sing-ind-is-contr A H B b x = tr B (contraction H x) b

sing-comp-is-contr : {i j : Level} (A : UU i) (H : is-contr A) (B : A → UU j) →
  (((ev-pt A (center H) B) ∘ (sing-ind-is-contr A H B)) ~ id)
sing-comp-is-contr A H B b =
  ap (λ (ω : Id (center H) (center H)) → tr B ω b) (coh-contraction H)

is-sing-is-contr : {i j : Level} (A : UU i) (H : is-contr A) (B : A → UU j) →
  sec (ev-pt A (center H) B)
is-sing-is-contr A H B =
  dpair (sing-ind-is-contr A H B) (sing-comp-is-contr A H B)

is-sing : {i : Level} (A : UU i) → A → UU (lsuc i)
is-sing {i} A a = (B : A → UU i) → sec (ev-pt A a B)

is-contr-is-sing : {i : Level} (A : UU i) (a : A) →
  is-sing A a → is-contr A
is-contr-is-sing A a S = dpair a (pr1 (S (λ y → Id a y)) refl)

is-sing-unit : is-sing unit star
is-sing-unit B = dpair ind-unit (λ b → refl)

is-contr-unit : is-contr unit
is-contr-unit = is-contr-is-sing unit star (is-sing-unit)

is-sing-total-path : {i : Level} (A : UU i) (a : A) →
  is-sing (Σ A (λ x → Id a x)) (dpair a refl)
is-sing-total-path A a B = dpair (ind-Σ ∘ (ind-Id _)) (λ b → refl)

is-contr-total-path : {i : Level} (A : UU i) (a : A) →
  is-contr (Σ A (λ x → Id a x))
is-contr-total-path A a = is-contr-is-sing _ _ (is-sing-total-path A a)

-- Section 6.2 Contractible maps

fib : {i j : Level} {A : UU i} {B : UU j} (f : A → B) (b : B) → UU (i ⊔ j)
fib f b = Σ _ (λ x → Id (f x) b)

is-contr-map : {i j : Level} {A : UU i} {B : UU j} (f : A → B) → UU (i ⊔ j)
is-contr-map f = (y : _) → is-contr (fib f y)

-- Our goal is to show that contractible maps are equivalences.
-- First we construct the inverse of a contractible map.
inv-is-contr-map : {i j : Level} {A : UU i} {B : UU j} {f : A → B} →
  is-contr-map f → B → A
inv-is-contr-map H y = pr1 (center (H y))

-- Then we show that the inverse is a section.
issec-is-contr-map : {i j : Level} {A : UU i} {B : UU j} {f : A → B}
  (H : is-contr-map f) → (f ∘ (inv-is-contr-map H)) ~ id
issec-is-contr-map H y = pr2 (center (H y))

-- Then we show that the inverse is also a retraction.
isretr-is-contr-map : {i j : Level} {A : UU i} {B : UU j} {f : A → B}
  (H : is-contr-map f) → ((inv-is-contr-map H) ∘ f) ~ id
isretr-is-contr-map {_} {_} {A} {B} {f} H x =
  ap {_} {_} {fib f (f x)} {A} pr1
    {dpair (inv-is-contr-map H (f x)) (issec-is-contr-map H (f x))}
    {dpair x refl}
    ( concat (center (H (f x)))
             (inv (contraction (H (f x))
               (dpair (inv-is-contr-map H (f x)) (issec-is-contr-map H (f x)))))
             (contraction (H (f x)) (dpair x refl)))

-- Finally we put it all together to show that contractible maps are equivalences.
is-equiv-is-contr-map : {i j : Level} {A : UU i} {B : UU j} {f : A → B} →
  is-contr-map f → is-equiv f
is-equiv-is-contr-map H =
  pair
    (dpair (inv-is-contr-map H) (issec-is-contr-map H))
    (dpair (inv-is-contr-map H) (isretr-is-contr-map H))

-- Section 6.3 Equivalences are contractible maps

htpy-nat : {i j : Level} {A : UU i} {B : UU j} {f g : A → B} (H : f ~ g)
  {x y : A} (p : Id x y) →
  Id (concat _ (H x) (ap g p)) (concat _ (ap f p) (H y))
htpy-nat H refl = right-unit (H _)

-- Should left-unwhisk and right-unwhisk be moved to Lecture 4? That's where they most naturally fit.
left-unwhisk : {i : Level} {A : UU i} {x y z : A} (p : Id x y) {q r : Id y z} →
  Id (concat _ p q) (concat _ p r) → Id q r
left-unwhisk refl s = concat _ (inv (left-unit _)) (concat _ s (left-unit _))

right-unwhisk : {i : Level} {A : UU i} {x y z : A} {p q : Id x y}
  (r : Id y z) → Id (concat _ p r) (concat _ q r) → Id p q
right-unwhisk refl s = concat _ (inv (right-unit _)) (concat _ s (right-unit _))

htpy-red : {i : Level} {A : UU i} {f : A → A} (H : f ~ id) →
  (x : A) → Id (H (f x)) (ap f (H x))
htpy-red {_} {A} {f} H x = right-unwhisk (H x)
  (concat (concat (f x) (H (f x)) (ap id (H x)))
    (ap (concat (f x) (H (f x))) (inv (ap-id (H x)))) (htpy-nat H (H x)))

center-is-invertible : {i j : Level} {A : UU i} {B : UU j} {f : A → B} →
  is-invertible f → (y : B) → fib f y
center-is-invertible {i} {j} {A} {B} {f}
  (dpair g (dpair issec isretr)) y =
    (dpair (g y)
      (concat _
        (inv (ap (f ∘ g) (issec y)))
        (concat _ (ap f (isretr (g y))) (issec y))))

mv-left : {i : Level} {A : UU i} {x y z : A}
  (p : Id x y) {q : Id y z} {r : Id x z} →
  Id (concat _ p q) r → Id q (concat _ (inv p) r)
mv-left refl s = s

square : {i : Level} {A : UU i} {x y1 y2 z : A}
  (p1 : Id x y1) (q1 : Id y1 z) (p2 : Id x y2) (q2 : Id y2 z) → UU i
square p q p' q' = Id (concat _ p q) (concat _ p' q')

sq-left-whisk : {i : Level} {A : UU i} {x y1 y2 z : A} {p1 p1' : Id x y1}
  (s : Id p1 p1') {q1 : Id y1 z} {p2 : Id x y2} {q2 : Id y2 z} →
  square p1 q1 p2 q2 → square p1' q1 p2 q2
sq-left-whisk refl sq = sq

sq-top-whisk : {i : Level} {A : UU i} {x y1 y2 z : A}
  {p1 : Id x y1} {q1 : Id y1 z}
  {p2 p2' : Id x y2} (s : Id p2 p2') {q2 : Id y2 z} →
  square p1 q1 p2 q2 → square p1 q1 p2' q2
sq-top-whisk refl sq = sq

contraction-is-invertible : {i j : Level} {A : UU i} {B : UU j} {f : A → B} →
  (I : is-invertible f) → (y : B) → (t : fib f y) →
  Id (center-is-invertible I y) t
contraction-is-invertible {i} {j} {A} {B} {f}
  (dpair g (dpair issec isretr)) y (dpair x refl) =
  eq-pair (dpair
    (isretr x)
    (concat _
      (tr-fib (isretr x) (f x)
        (pr2 (center-is-invertible
          (dpair g (dpair issec isretr))
          (f x))))
      (inv (mv-left (ap f (isretr x))
        (concat _ (right-unit (ap f (isretr x)))
        (mv-left (ap (f ∘ g) (issec y))
        (sq-left-whisk {_} {_} {f(g(f(g(f x))))} {f(g(f x))} {f(g(f x))} {f x}
          {issec (f(g(f x)))} {ap (f ∘ g) (issec (f x))}
          (htpy-red issec (f x)) {ap f (isretr x)} {ap f (isretr (g (f x)))}
          {issec (f x)}
          (sq-top-whisk {_} {_} {f(g(f(g(f x))))} {f(g(f x))} {f(g(f x))} {f x}
            {issec (f(g(f x)))} {_} {_} {_}
            (concat _ (ap-comp f (g ∘ f) (isretr x))
            (inv (ap (ap f) (htpy-red isretr x))))
            (htpy-nat (htpy-right-whisk issec f) (isretr x))))))))))

is-contr-map-is-invertible : {i j : Level} {A : UU i} {B : UU j} {f : A → B} → is-invertible f → is-contr-map f
is-contr-map-is-invertible {i} {j} {A} {B} {f} I y =
    dpair (center-is-invertible I y) (contraction-is-invertible I y)

is-contr-map-is-equiv : {i j : Level} {A : UU i} {B : UU j} {f : A → B} → is-equiv f → is-contr-map f
is-contr-map-is-equiv = is-contr-map-is-invertible ∘ is-invertible-is-equiv

is-contr-total-path' : {i : Level} (A : UU i) (a : A) →
  is-contr (Σ A (λ x → Id x a))
is-contr-total-path' A a = is-contr-map-is-equiv (is-equiv-id _) a

-- Exercises

-- Exercise 6.1
is-prop-is-contr : {i : Level} {A : UU i} → is-contr A → (x y : A) → is-contr (Id x y)
is-prop-is-contr {i} {A} C =
  sing-ind-is-contr A C
    (λ x → ((y : A) → is-contr (Id x y)))
    (λ y → dpair
           (contraction C y)
           (ind-Id
             (λ z (p : Id (center C) z) → Id (contraction C z) p)
             (coh-contraction C)
             y))

-- Exercise 6.2
is-contr-retract-of : {i j : Level} {A : UU i} (B : UU j) → A retract-of B → is-contr B → is-contr A
is-contr-retract-of B (dpair i (dpair r isretr)) C =
  dpair
    (r (center C))
    (λ x → concat (r (i x)) (ap r (contraction C (i x))) (isretr x))

-- Exercise 6.3

is-equiv-const-is-contr : {i : Level} {A : UU i} → is-contr A → is-equiv (const A unit star)
is-equiv-const-is-contr {i} {A} H = pair (dpair (ind-unit (center H)) (ind-unit refl)) (dpair (const unit A (center H)) (contraction H))

is-contr-is-equiv-const : {i : Level} {A : UU i} → is-equiv (const A unit star) → is-contr A
is-contr-is-equiv-const (dpair (dpair g issec) (dpair h isretr)) = dpair (h star) isretr

is-contr-is-equiv : {i j : Level} {A : UU i} {B : UU j} (f : A → B) → is-equiv f → is-contr B → is-contr A
is-contr-is-equiv f Ef C =
  is-contr-is-equiv-const
    (is-equiv-comp (λ x → refl) Ef (is-equiv-const-is-contr C))

is-contr-is-equiv' : {i j : Level} {A : UU i} {B : UU j} (f : A → B) → is-equiv f → is-contr A → is-contr B
is-contr-is-equiv' f Ef C = is-contr-is-equiv (inv-is-equiv Ef) (is-equiv-inv-is-equiv Ef) C

is-equiv-is-contr : {i j : Level} {A : UU i} {B : UU j} (f : A → B) →
  is-contr A → is-contr B → is-equiv f
is-equiv-is-contr {i} {j} {A} {B} f CA CB =
  pair
    (dpair
      (const B A (center CA))
      (sing-ind-is-contr B CB _ (inv (contraction CB (f (center CA))))))
    (dpair (const B A (center CA)) (contraction CA))


------------------------------------------------------------------------
-- The Agda standard library
--
-- Pointwise equality for containers
------------------------------------------------------------------------

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

module Data.Container.Relation.Binary.Pointwise where

open import Data.Product using (_,_; Σ-syntax; -,_; proj₁; proj₂)
open import Function
open import Level using (_⊔_)
open import Relation.Binary using (REL; _⇒_)
open import Relation.Binary.PropositionalEquality as P
  using (_≡_; subst; cong)

open import Data.Container.Core using (Container; ⟦_⟧)

-- Equality, parametrised on an underlying relation.

module _ {s p} (C : Container s p) where

  record Pointwise {x y e} {X : Set x} {Y : Set y} (R : REL X Y e)
                   (cx : ⟦ C ⟧ X) (cy : ⟦ C ⟧ Y) : Set (s ⊔ p ⊔ e) where
    constructor _,_
    field shape    : proj₁ cx ≡ proj₁ cy
          position : ∀ p → R (proj₂ cx p) (proj₂ cy (subst _ shape p))

module _ {s p} {C : Container s p} {x y} {X : Set x} {Y : Set y}
         {ℓ ℓ′} {R : REL X Y ℓ} {R′ : REL X Y ℓ′}
         where

  map : R ⇒ R′ → Pointwise C R ⇒ Pointwise C R′
  map R⇒R′ (s , f) = s , R⇒R′ ∘ f


module PiFrac.AuxLemmas where
open import Data.Empty
open import Data.Unit hiding (_≟_)
open import Data.Sum
open import Data.Product
open import Relation.Binary.Core
open import Relation.Binary
open import Relation.Nullary
open import Relation.Binary.PropositionalEquality
open import Function using (_∘_)
open import PiFrac.Syntax
open import PiFrac.Opsem

Lemma₁ : ∀ {A B C D v v' κ κ'} {c : A ↔ B} {c' : C ↔ D}
       → ⟨ c ∣ v ∣ κ ⟩ ↦ ［ c' ∣ v' ∣ κ' ］
       → (A ≡ C × B ≡ D)
Lemma₁ ↦₁ = refl , refl
Lemma₁ ↦₂ = refl , refl
Lemma₁ ↦η = refl , refl
Lemma₁ ↦ε₁ = refl , refl

Lemma₂ : ∀ {A B v v' κ κ'} {c c' : A ↔ B}
       → ⟨ c ∣ v ∣ κ ⟩ ↦ ［ c' ∣ v' ∣ κ' ］
       → c ≡ c' × κ ≡ κ'
Lemma₂ ↦₁ = refl , refl
Lemma₂ ↦₂ = refl , refl
Lemma₂ ↦η = refl , refl
Lemma₂ ↦ε₁ = refl , refl

Lemma₃ : ∀ {A B v v' κ} {c : A ↔ B}
       → (r : ⟨ c ∣ v ∣ κ ⟩ ↦ ［ c ∣ v' ∣ κ ］)
       → base c ⊎ dual c ⊎ A ≡ B
Lemma₃ (↦₁ {b = b}) = inj₁ b
Lemma₃ ↦₂ = inj₂ (inj₂ refl)
Lemma₃ ↦η = inj₂ (inj₁ tt)
Lemma₃ ↦ε₁ = inj₂ (inj₁ tt)

Lemma₄ : ∀ {A v v' κ} {c : A ↔ A}
       → (r : ⟨ c ∣ v ∣ κ ⟩ ↦ ［ c ∣ v' ∣ κ ］)
       → base c ⊎ c ≡ id↔
Lemma₄ {c = swap₊} ↦₁ = inj₁ tt
Lemma₄ {c = swap⋆} ↦₁ = inj₁ tt
Lemma₄ ↦₂ = inj₂ refl

Lemma₅ : ∀ {A B v v' b κ} {c : A ↔ B ×ᵤ 𝟙/ b}
        → (r : ⟨ c ∣ v ∣ κ ⟩ ↦ ［ c ∣ v' ∣ κ ］)
        → base c ⊎ (b , ↻) ≡ v' ⊎ ¬ (dual c)
Lemma₅ (↦₁ {b = b}) = inj₁ b
Lemma₅ ↦₂ = inj₂ (inj₂ (λ ()))
Lemma₅ ↦η = inj₂ (inj₁ refl)

Lemma₆ : ∀ {A v v' κ} → v ≢ v' → ∄[ st' ] (st' ↦ ［ ηₓ {A} v ∣ v' , ↻ ∣ κ ］)
Lemma₆ neq (⟨ _ ∣ v ∣ _ ⟩ , r) with Lemma₁ r
... | refl , refl with Lemma₂ r
... | refl , refl with Lemma₅ r
... | inj₂ (inj₁ refl) = neq refl
... | inj₂ (inj₂ nd) = nd tt


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

module Data.Sigma.Properties where

open import Prelude hiding (B; C)
open import Cubical.Foundations.HLevels using (isOfHLevelΣ) public
open import Cubical.Data.Sigma.Properties using (Σ≡Prop) public

private
  variable
    B : A → Type b
    C : Σ A B → Type c

reassoc : Σ (Σ A B) C ⇔ Σ[ x ⦂ A ] × Σ[ y ⦂ B x ] × C (x , y)
reassoc .fun       ((x , y) , z)    = x , (y , z)
reassoc .inv       (x , (y , z))    = (x , y) , z
reassoc .leftInv   ((x , y) , z) i  = ((x , y) , z)
reassoc .rightInv  (x , (y , z)) i  = (x , (y , z))

≃ΣProp≡ : ∀ {A : Type a} {u} {U : A → Type u} → ((x : A) → isProp (U x)) → {p q : Σ A U} → (p ≡ q) ≃ (fst p ≡ fst q)
≃ΣProp≡ {A = A} {U = U} pA {p} {q} = isoToEquiv (iso to fro (λ _ → refl) (J Jt Jp))
  where
  to : {p q : Σ A U} → p ≡ q → fst p ≡ fst q
  to = cong fst

  fro : ∀ {p q} → fst p ≡ fst q → p ≡ q
  fro = Σ≡Prop pA

  Jt : (q : Σ A U) → p ≡ q → Type _
  Jt q q≡ = fro (to q≡) ≡ q≡

  Jp : Jt p refl
  Jp i j .fst = p .fst
  Jp i j .snd = isProp→isSet (pA (p .fst)) (p .snd) (p .snd) (λ k → fro {p} {p} (to (refl {x = p})) k .snd) refl i j


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

open import HoTT
open import homotopy.elims.Lemmas

module homotopy.elims.SuspSmash {i j k} {X : Ptd i} {Y : Ptd j}
  {P : Susp (X ∧ Y) → Type k}
  (north* : P north) (south* : P south)
  (smin* : (x : de⊙ X) (y : de⊙ Y) → north* == south* [ P ↓ merid (smin x y) ])
  where

private
  smbase*-template : ∀ {s} (p : smin (pt X) (pt Y) == s)
    → north* == south* [ P ↓ merid s ]
  smbase*-template p = transport (λ κ → north* == south* [ P ↓ merid κ ])
    p (smin* (pt X) (pt Y))

  smbasel* = smbase*-template (smgluel (pt X))
  smbaser* = smbase*-template (smgluer (pt Y))

  -- note that [smin*] is adjusted.
  coh* : (s : X ∧ Y) → north* == south* [ P ↓ merid s ]
  coh* = Smash-elim
    (λ x y → fst (fill-l x) ◃ fst (fill-r y) ◃ fst fill-base ◃ smin* x y)
    smbasel* smbaser*
    (λ x → ↓↓-from-squareover $
           ap (λ p → fst (fill-l x) ◃ p ◃ fst fill-base ◃ smin* x (pt Y)) fill-r-β
      ∙h↓⊡ ap (fst (fill-l x) ◃_) (idp◃ _)
      ∙h↓⊡ snd (fill-l x))
    (λ y → ↓↓-from-squareover $
           ap (λ p → p ◃ fst (fill-r y) ◃ fst fill-base ◃ smin* (pt X) y) fill-l-β
      ∙h↓⊡ idp◃ _
      ∙h↓⊡ snd (fill-r y))
    where
    fill-template : ∀ {s₁ s₂} (p : s₁ == s₂)
      (α : north* == south* [ P ↓ merid s₁ ])
      (β : north* == south* [ P ↓ merid s₂ ])
      → Σ (north* == north*)
          (λ q → SquareOver P (natural-square merid p)
                   (q ◃ α)
                   (↓-ap-in _ _ (apd (λ _ → north*) p))
                   (↓-ap-in _ _ (apd (λ _ → south*) p))
                   β)
    fill-template p α β = fill-upper-left _ _ _ _ _

    fill-base = fill-template (smgluel (pt X)) (smin* (pt X) (pt Y)) smbasel*
    fill-l = λ x → fill-template (smgluel x) (fst fill-base ◃ smin* x (pt Y)) smbasel*
    fill-l-β : fst (fill-l (pt X)) == idp
    fill-l-β = ! $
      fill-upper-left-unique _ _ _ _ _ idp (idp◃ _ ∙h↓⊡ snd fill-base)

    fill-r = λ y → fill-template (smgluer y) (fst fill-base ◃ smin* (pt X) y) smbaser*
    fill-r-β : fst (fill-r (pt Y)) == idp
    fill-r-β = ! $
      fill-upper-left-unique _ _ _ _ _ idp (idp◃ _ ∙h↓⊡ snd fill-base)
      ∙ ap (λ sp → fst (fill-template (snd sp)
                                      (fst fill-base ◃ smin* (pt X) (pt Y))
                                      (smbase*-template (snd sp))))
           (contr-has-all-paths {{pathfrom-is-contr (smin (pt X) (pt Y))}}
                                (smbasel , smgluel (pt X))
                                (smbaser , smgluer (pt Y)))

SuspSmash-elim : Π (Susp (X ∧ Y)) P
SuspSmash-elim = Susp-elim north* south* coh*


{-# OPTIONS --allow-unsolved-metas #-}
module FLutil where

open import Level hiding ( suc ; zero )
open import Data.Fin hiding ( _<_  ; _≤_ ; _-_ ; _+_ ; _≟_)
open import Data.Fin.Properties hiding ( <-trans ; ≤-refl ; ≤-trans ; ≤-irrelevant ; _≟_ ) renaming ( <-cmp to <-fcmp )
open import Data.Fin.Permutation  -- hiding ([_,_])
open import Data.Nat -- using (ℕ; suc; zero; s≤s ; z≤n )
open import Data.Nat.Properties as DNP
open import Relation.Binary.PropositionalEquality hiding ( [_] )
open import Data.List using (List; []; _∷_ ; length ; _++_ ; tail ) renaming (reverse to rev )
open import Data.Product
open import Relation.Nullary
open import Data.Empty
open import  Relation.Binary.Core
open import  Relation.Binary.Definitions
open import logic
open import nat

infixr  100 _::_

data  FL : (n : ℕ )→ Set where
   f0 :  FL 0
   _::_ :  { n : ℕ } → Fin (suc n ) → FL n → FL (suc n)

data _f<_  : {n : ℕ } (x : FL n ) (y : FL n)  → Set  where
   f<n : {m : ℕ } {xn yn : Fin (suc m) } {xt yt : FL m} → xn Data.Fin.< yn →   (xn :: xt) f< ( yn :: yt )
   f<t : {m : ℕ } {xn : Fin (suc m) } {xt yt : FL m} → xt f< yt →   (xn :: xt) f< ( xn :: yt )

FLeq : {n : ℕ } {xn yn : Fin (suc n)} {x : FL n } {y : FL n}  → xn :: x ≡ yn :: y → ( xn ≡ yn )  × (x ≡ y )
FLeq refl = refl , refl

FLpos : {n : ℕ} → FL (suc n) → Fin (suc n)
FLpos (x :: _) = x

f-<> :  {n : ℕ } {x : FL n } {y : FL n}  → x f< y → y f< x → ⊥
f-<> (f<n x) (f<n x₁) = nat-<> x x₁
f-<> (f<n x) (f<t lt2) = nat-≡< refl x
f-<> (f<t lt) (f<n x) = nat-≡< refl x
f-<> (f<t lt) (f<t lt2) = f-<> lt lt2

f-≡< :  {n : ℕ } {x : FL n } {y : FL n}  → x ≡ y → y f< x → ⊥
f-≡< refl (f<n x) = nat-≡< refl x
f-≡< refl (f<t lt) = f-≡< refl lt

FLcmp : {n : ℕ } → Trichotomous {Level.zero} {FL n}  _≡_  _f<_
FLcmp f0 f0 = tri≈ (λ ()) refl (λ ())
FLcmp (xn :: xt) (yn :: yt) with <-fcmp xn yn
... | tri< a ¬b ¬c = tri< (f<n a) (λ eq → nat-≡< (cong toℕ (proj₁ (FLeq eq)) ) a) (λ lt  → f-<> lt (f<n a) )
... | tri> ¬a ¬b c = tri> (λ lt  → f-<> lt (f<n c) ) (λ eq → nat-≡< (cong toℕ (sym (proj₁ (FLeq eq)) )) c) (f<n c)
... | tri≈ ¬a refl ¬c with FLcmp xt yt
... | tri< a ¬b ¬c₁ = tri< (f<t a) (λ eq → ¬b (proj₂ (FLeq eq) )) (λ lt  → f-<> lt (f<t a) )
... | tri≈ ¬a₁ refl ¬c₁ = tri≈ (λ lt → f-≡< refl lt )  refl (λ lt → f-≡< refl lt )
... | tri> ¬a₁ ¬b c = tri> (λ lt  → f-<> lt (f<t c) ) (λ eq → ¬b (proj₂ (FLeq eq) )) (f<t c)

f<-trans : {n : ℕ } { x y z : FL n } → x f< y → y f< z → x f< z
f<-trans {suc n} (f<n x) (f<n x₁) = f<n ( Data.Fin.Properties.<-trans x x₁ )
f<-trans {suc n} (f<n x) (f<t y<z) = f<n x
f<-trans {suc n} (f<t x<y) (f<n x) = f<n x
f<-trans {suc n} (f<t x<y) (f<t y<z) = f<t (f<-trans x<y y<z)

infixr 250 _f<?_

_f<?_ : {n  : ℕ} → (x y : FL n ) → Dec (x f< y )
x f<? y with FLcmp x y
... | tri< a ¬b ¬c = yes a
... | tri≈ ¬a refl ¬c = no ( ¬a )
... | tri> ¬a ¬b c = no ( ¬a )

_f≤_ : {n : ℕ } (x : FL n ) (y : FL n)  → Set
_f≤_ x y = (x ≡ y ) ∨  (x f< y )

FL0 : {n : ℕ } → FL n
FL0 {zero} = f0
FL0 {suc n} = zero :: FL0

fmax : { n : ℕ } →  FL n
fmax {zero} = f0
fmax {suc n} = fromℕ< a<sa :: fmax {n}

fmax< : { n : ℕ } → {x : FL n } → ¬ (fmax f< x )
fmax< {suc n} {x :: y} (f<n lt) = nat-≤> (fmax1 x) lt where
    fmax1 : {n : ℕ } → (x : Fin (suc n)) → toℕ x ≤ toℕ (fromℕ< {n} a<sa)
    fmax1 {zero} zero = z≤n
    fmax1 {suc n} zero = z≤n
    fmax1 {suc n} (suc x) = s≤s (fmax1 x)
fmax< {suc n} {x :: y} (f<t lt) = fmax< {n} {y} lt

fmax¬ : { n : ℕ } → {x : FL n } → ¬ ( x ≡ fmax ) → x f< fmax
fmax¬ {zero} {f0} ne = ⊥-elim ( ne refl )
fmax¬ {suc n} {x} ne with FLcmp x fmax
... | tri< a ¬b ¬c = a
... | tri≈ ¬a b ¬c = ⊥-elim ( ne b)
... | tri> ¬a ¬b c = ⊥-elim (fmax< c)

x≤fmax : {n : ℕ } → {x : FL n} → x f≤ fmax
x≤fmax {n} {x} with FLcmp x fmax
... | tri< a ¬b ¬c = case2 a
... | tri≈ ¬a b ¬c = case1 b
... | tri> ¬a ¬b c = ⊥-elim ( fmax< c )

open import Data.Nat.Properties using ( ≤-trans ; <-trans )
fsuc : { n : ℕ } → (x : FL n ) → x f< fmax → FL n
fsuc {n} (x :: y) (f<n lt) = fromℕ< fsuc1 :: y where
    fsuc1 : suc (toℕ x) < n
    fsuc1 =  Data.Nat.Properties.≤-trans (s≤s lt) ( s≤s ( toℕ≤pred[n] (fromℕ< a<sa)) )
fsuc (x :: y) (f<t lt) = x :: fsuc y lt

open import fin

flist1 :  {n : ℕ } (i : ℕ) → i < suc n → List (FL n) → List (FL n) → List (FL (suc n))
flist1 zero i<n [] _ = []
flist1 zero i<n (a ∷ x ) z  = ( zero :: a ) ∷ flist1 zero i<n x z
flist1 (suc i) (s≤s i<n) [] z  = flist1 i (Data.Nat.Properties.<-trans i<n a<sa) z z
flist1 (suc i) i<n (a ∷ x ) z  = ((fromℕ< i<n ) :: a ) ∷ flist1 (suc i) i<n x z

flist : {n : ℕ } → FL n → List (FL n)
flist {zero} f0 = f0 ∷ []
flist {suc n} (x :: y)  = flist1 n a<sa (flist y) (flist y)

FL1 : List ℕ → List ℕ
FL1 [] = []
FL1 (x ∷ y) = suc x ∷ FL1 y

FL→plist : {n : ℕ} → FL n → List ℕ
FL→plist {0} f0 = []
FL→plist {suc n} (zero :: y) = zero ∷ FL1 (FL→plist y)
FL→plist {suc n} (suc x :: y) with FL→plist y
... | [] = zero ∷ []
... | x1 ∷ t = suc x1 ∷ FL2 x t where
  FL2 : {n : ℕ} → Fin n → List ℕ → List ℕ
  FL2 zero y = zero ∷ FL1 y
  FL2 (suc i) [] = zero ∷ []
  FL2 (suc i) (x ∷ y) = suc x ∷ FL2 i y

tt0 = (# 2) :: (# 1) :: (# 0) :: zero :: f0
tt1 = FL→plist tt0

open _∧_

find-zero : {n i : ℕ} → List ℕ → i < n  → Fin n ∧ List ℕ
find-zero  [] i<n = record { proj1 = fromℕ< i<n  ; proj2 = [] }
find-zero x (s≤s z≤n) = record { proj1 = fromℕ< (s≤s z≤n)  ; proj2 = x }
find-zero (zero ∷ y) (s≤s (s≤s i<n)) = record { proj1 = fromℕ< (s≤s (s≤s i<n)) ; proj2 = y }
find-zero (suc x ∷ y) (s≤s (s≤s i<n)) with find-zero y (s≤s i<n)
... | record { proj1 = i ; proj2 = y1 } = record { proj1 = suc i ; proj2 = suc x ∷ y1 }

plist→FL : {n : ℕ} → List ℕ → FL n -- wrong implementation
plist→FL {zero} [] = f0
plist→FL {suc n} [] = zero :: plist→FL {n} []
plist→FL {zero} x = f0
plist→FL {suc n} x with find-zero x a<sa
... | record { proj1 = i ; proj2 = y } = i :: plist→FL y

tt2 = 2 ∷ 1 ∷ 0 ∷ 3 ∷ []
tt3 : FL 4
tt3 = plist→FL tt2
tt4 = FL→plist tt3
tt5 = plist→FL {4} (FL→plist tt0)

-- maybe FL→iso can be easier using this ...
-- FL→plist-iso : {n : ℕ} → (f : FL n ) → plist→FL (FL→plist f ) ≡ f
-- FL→plist-iso = {!!}
-- FL→plist-inject : {n : ℕ} → (f g : FL n ) → FL→plist f ≡ FL→plist g → f ≡ g
-- FL→plist-inject = {!!}

open import Relation.Binary as B hiding (Decidable; _⇔_)
open import Data.Sum.Base as Sum --  inj₁
open import Relation.Nary using (⌊_⌋)
open import Data.List.Fresh hiding ([_])

FList : (n : ℕ ) → Set
FList n = List# (FL n) ⌊ _f<?_ ⌋

fr1 : FList 3
fr1 =
   ((# 0) :: ((# 0) :: ((# 0 ) :: f0))) ∷#
   ((# 0) :: ((# 1) :: ((# 0 ) :: f0))) ∷#
   ((# 1) :: ((# 0) :: ((# 0 ) :: f0))) ∷#
   ((# 2) :: ((# 0) :: ((# 0 ) :: f0))) ∷#
   ((# 2) :: ((# 1) :: ((# 0 ) :: f0))) ∷#
   []

open import Data.Product
open import Relation.Nullary.Decidable hiding (⌊_⌋)
-- open import Data.Bool hiding (_<_ ; _≤_ )
open import Data.Unit.Base using (⊤ ; tt)

--  fresh a []        = ⊤
--  fresh a (x ∷# xs) = R a x × fresh a xs

-- toWitness
-- ttf< :  {n : ℕ } → {x a : FL n } → x f< a  → T (isYes (x f<? a))
-- ttf< {n} {x} {a} x<a with x f<? a
-- ... | yes y = subst (λ k → Data.Bool.T k ) refl tt
-- ... | no nn = ⊥-elim ( nn x<a )

ttf : {n : ℕ } {x a : FL (n)} → x f< a → (y : FList (n)) →  fresh (FL (n)) ⌊ _f<?_ ⌋  a y  → fresh (FL (n)) ⌊ _f<?_ ⌋  x y
ttf _ [] fr = Level.lift tt
ttf {_} {x} {a} lt (cons a₁ y x1) (lift lt1 , x2 ) = (Level.lift (fromWitness (ttf1 lt1 lt ))) , ttf (ttf1 lt1 lt) y x1 where
       ttf1 : True (a f<? a₁) → x f< a  → x f< a₁
       ttf1 t x<a = f<-trans x<a (toWitness t)

-- by https://gist.github.com/aristidb/1684202

FLinsert : {n : ℕ } → FL n → FList n  → FList n
FLfresh : {n : ℕ } → (a x : FL (suc n) ) → (y : FList (suc n) ) → a f< x
     → fresh (FL (suc n)) ⌊ _f<?_ ⌋ a y → fresh (FL (suc n)) ⌊ _f<?_ ⌋ a (FLinsert x y)
FLinsert {zero} f0 y = f0 ∷# []
FLinsert {suc n} x [] = x ∷# []
FLinsert {suc n} x (cons a y x₁) with FLcmp x a
... | tri≈ ¬a b ¬c  = cons a y x₁
... | tri< lt ¬b ¬c  = cons x ( cons a y x₁) ( Level.lift (fromWitness lt ) , ttf lt y  x₁)
FLinsert {suc n} x (cons a [] x₁) | tri> ¬a ¬b lt  = cons a ( x  ∷# []  ) ( Level.lift (fromWitness lt) , Level.lift tt )
FLinsert {suc n} x (cons a y yr)  | tri> ¬a ¬b a<x = cons a (FLinsert x y) (FLfresh a x y a<x yr )

FLfresh a x [] a<x (Level.lift tt) = Level.lift (fromWitness a<x) , Level.lift tt
FLfresh a x (cons b [] (Level.lift tt)) a<x (Level.lift a<b , a<y) with FLcmp x b
... | tri< x<b ¬b ¬c  = Level.lift (fromWitness a<x) , Level.lift a<b , Level.lift tt
... | tri≈ ¬a refl ¬c = Level.lift (fromWitness a<x) , Level.lift tt
... | tri> ¬a ¬b b<x =  Level.lift a<b  ,  Level.lift (fromWitness  (f<-trans (toWitness a<b) b<x))  , Level.lift tt
FLfresh a x (cons b y br) a<x (Level.lift a<b , a<y) with FLcmp x b
... | tri< x<b ¬b ¬c =  Level.lift (fromWitness a<x) , Level.lift a<b , ttf (toWitness a<b) y br
... | tri≈ ¬a refl ¬c = Level.lift (fromWitness a<x) , ttf a<x y br
FLfresh a x (cons b [] br) a<x (Level.lift a<b , a<y) | tri> ¬a ¬b b<x =
    Level.lift a<b , Level.lift (fromWitness (f<-trans (toWitness a<b) b<x)) , Level.lift tt
FLfresh a x (cons b (cons a₁ y x₁) br) a<x (Level.lift a<b , a<y) | tri> ¬a ¬b b<x =
    Level.lift a<b , FLfresh a x (cons a₁ y x₁) a<x a<y

fr6 = FLinsert ((# 1) :: ((# 1) :: ((# 0 ) :: f0))) fr1

open import Data.List.Fresh.Relation.Unary.Any
open import Data.List.Fresh.Relation.Unary.All

x∈FLins : {n : ℕ} → (x : FL n ) → (xs : FList n)  → Any (x ≡_) (FLinsert x xs)
x∈FLins {zero} f0 [] = here refl
x∈FLins {zero} f0 (cons f0 xs x) = here refl
x∈FLins {suc n} x [] = here refl
x∈FLins {suc n} x (cons a xs x₁) with FLcmp x a
... | tri< x<a ¬b ¬c = here refl
... | tri≈ ¬a b ¬c   = here b
x∈FLins {suc n} x (cons a [] x₁)              | tri> ¬a ¬b a<x = there ( here refl )
x∈FLins {suc n} x (cons a (cons a₁ xs x₂) x₁) | tri> ¬a ¬b a<x = there ( x∈FLins x (cons a₁ xs x₂) )

nextAny : {n : ℕ} → {x h : FL n } → {L : FList n}  → {hr : fresh (FL n) ⌊ _f<?_ ⌋ h L } → Any (x ≡_) L → Any (x ≡_) (cons h L hr )
nextAny (here x₁) = there (here x₁)
nextAny (there any) = there (there any)

insAny : {n : ℕ} → {x h : FL n } → (xs : FList n)  → Any (x ≡_) xs → Any (x ≡_) (FLinsert h xs)
insAny {zero} {f0} {f0} (cons a L xr) (here refl) = here refl
insAny {zero} {f0} {f0} (cons a L xr) (there any) = insAny {zero} {f0} {f0} L any
insAny {suc n} {x} {h} (cons a L xr) any with FLcmp h a
... | tri< x<a ¬b ¬c = there any
... | tri≈ ¬a b ¬c = any
insAny {suc n} {a} {h} (cons a [] (Level.lift tt)) (here refl) | tri> ¬a ¬b c = here refl
insAny {suc n} {x} {h} (cons a (cons a₁ L x₁) xr) (here refl) | tri> ¬a ¬b c = here refl
insAny {suc n} {x} {h} (cons a (cons a₁ L x₁) xr) (there any) | tri> ¬a ¬b c = there (insAny (cons a₁ L x₁) any)

-- FLinsert membership

module FLMB { n : ℕ } where

  FL-Setoid : Setoid Level.zero Level.zero
  FL-Setoid  = record { Carrier = FL n ; _≈_ = _≡_ ; isEquivalence = record { sym = sym ; refl = refl ; trans = trans }}

  open import Data.List.Fresh.Membership.Setoid FL-Setoid

  FLinsert-mb :  (x : FL n ) → (xs : FList n)  → x ∈ FLinsert x xs
  FLinsert-mb x xs = x∈FLins {n} x xs


------------------------------------------------------------------------
-- Vectors parameterised on types in Set₁
------------------------------------------------------------------------

-- I want universe polymorphism.

module Data.Vec1 where

infixr 5 _∷_

open import Data.Nat
open import Data.Vec using (Vec; []; _∷_)
open import Data.Fin

------------------------------------------------------------------------
-- The type

data Vec₁ (a : Set₁) : ℕ → Set₁ where
  []  : Vec₁ a zero
  _∷_ : ∀ {n} (x : a) (xs : Vec₁ a n) → Vec₁ a (suc n)

------------------------------------------------------------------------
-- Some operations

map : ∀ {a b n} → (a → b) → Vec₁ a n → Vec₁ b n
map f []       = []
map f (x ∷ xs) = f x ∷ map f xs

map₀₁ : ∀ {a b n} → (a → b) → Vec a n → Vec₁ b n
map₀₁ f []       = []
map₀₁ f (x ∷ xs) = f x ∷ map₀₁ f xs

map₁₀ : ∀ {a b n} → (a → b) → Vec₁ a n → Vec b n
map₁₀ f []       = []
map₁₀ f (x ∷ xs) = f x ∷ map₁₀ f xs

lookup : ∀ {a n} → Fin n → Vec₁ a n → a
lookup zero    (x ∷ xs) = x
lookup (suc i) (x ∷ xs) = lookup i xs


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

open import Bundles

module Properties.ConstructiveField {ℓ ℓ'} (F : ConstructiveField {ℓ} {ℓ'}) where

open import Agda.Primitive renaming (_⊔_ to ℓ-max; lsuc to ℓ-suc; lzero to ℓ-zero)

private
  variable
    ℓ'' : Level

open import Cubical.Foundations.Everything renaming (_⁻¹ to _⁻¹ᵖ; assoc to ∙-assoc)
open import Cubical.Data.Sum.Base renaming (_⊎_ to infixr 4 _⊎_)
open import Cubical.Data.Sigma.Base renaming (_×_ to infixr 4 _×_)
open import Cubical.Data.Empty renaming (elim to ⊥-elim) -- `⊥` and `elim`

open import Function.Base using (it) -- instance search

open import MoreLogic
open MoreLogic.Reasoning
import MoreAlgebra

-- Lemma 4.1.6.
-- For a constructive field (F, 0, 1, +, ·, #), the following hold.
-- 1. 1 # 0.
-- 2. Addition + is #-compatible in the sense that for all x, y, z : F
--    x # y ⇔ x + z # y + z.
-- 3. Multiplication · is #-extensional in the sense that for all w, x, y, z : F
--    w · x # y · z ⇒ w # y ∨ x # z.

open ConstructiveField F

open import Cubical.Structures.Ring
R = (makeRing 0f 1f _+_ _·_ -_ is-set +-assoc +-rid +-rinv +-comm ·-assoc ·-rid ·-lid ·-rdist-+ ·-ldist-+)
open Cubical.Structures.Ring.Theory R
open MoreAlgebra.Properties.Ring R

-- Lemma 4.1.6.1
1f#0f : 1f # 0f
1f#0f with ·-identity 1f
1f#0f | 1·1≡1 , _ = fst (·-inv-back _ _ 1·1≡1)

-- Lemma 4.1.6.2
--   For #-compatibility of +, suppose x # y, that is, (x +z) −z # (y +z) −z.
--   Then #-extensionality gives (x + z # y + z) ∨ (−z # −z), where the latter case is excluded by irreflexivity of #.
+-#-compatible : ∀(x y z : Carrier) → x # y → x + z # y + z
+-#-compatible x y z x#y with
  let P = transport (λ i →  a+b-b≡a x z i # a+b-b≡a y z i ) x#y
  in  +-#-extensional _ _ _ _ P
... | inl x+z#y+z = x+z#y+z
... | inr  -z#-z  = ⊥-elim (#-irrefl _ -z#-z)

-- The other direction is similar.
+-#-compatible-inv : ∀(x y z : Carrier) → x + z # y + z → x # y
+-#-compatible-inv _ _ _ x+z#y+z with +-#-extensional _ _ _ _ x+z#y+z
... | inl x#y = x#y
... | inr z#z = ⊥-elim (#-irrefl _ z#z)

-- Lemma 4.1.6.3
·-#-extensional-case1 : ∀(w x y z : Carrier) → w · x # y · z → w · x # w · z → x # z
·-#-extensional-case1 w x y z w·x#y·z w·x#w·z =
  let
    instance -- this allows to use ⁻¹ᶠ without an instance argument
      w·[z-x]#0f =
        ( w · x         # w ·  z         ⇒⟨ +-#-compatible _ _ (- (w · x)) ⟩
          w · x - w · x # w ·  z - w · x ⇒⟨ transport (λ i →  (fst (+-inv (w · x)) i) # a·b-a·c≡a·[b-c] w z x i) ⟩
                     0f # w · (z - x)    ⇒⟨ #-sym _ _ ⟩
          w ·   (z - x) # 0f             ◼) w·x#w·z
  in  (    w  · (z - x) # 0f                      ⇒⟨ (λ _ → ·-rinv (w · (z - x)) it ) ⟩  -- NOTE: "plugging in" the instance did not work, ∴ `it`
           w  · (z - x) · (w · (z - x)) ⁻¹ᶠ  ≡ 1f ⇒⟨ transport (λ i → ·-comm w (z - x) i · (w · (z - x)) ⁻¹ᶠ ≡ 1f) ⟩
      (z - x) ·  w      · (w · (z - x)) ⁻¹ᶠ  ≡ 1f ⇒⟨ transport (λ i → ·-assoc (z - x) w ((w · (z - x)) ⁻¹ᶠ) (~ i) ≡ 1f) ⟩
      (z - x) · (w      · (w · (z - x)) ⁻¹ᶠ) ≡ 1f ⇒⟨ fst ∘ (·-inv-back _ _)  ⟩
         z    - x       # 0f                      ⇒⟨ +-#-compatible _ _ x ⟩
        (z    - x) + x  # 0f + x                  ⇒⟨ transport (λ i → +-assoc z (- x) x (~ i) # snd (+-identity x) i) ⟩
         z + (- x  + x) #      x                  ⇒⟨ transport (λ i → z + snd (+-inv x) i # x) ⟩
         z +      0f    # x                       ⇒⟨ transport (λ i → fst (+-identity z) i # x) ⟩
         z              # x                       ⇒⟨ #-sym _ _ ⟩
         x              # z                       ◼) it -- conceptually we would plug `w·[z-x]#0f` in, but this breaks the very first step

·-#-extensional : ∀(w x y z : Carrier) → w · x # y · z → (w # y) ⊎ (x # z)
·-#-extensional w x y z w·x#y·z with #-cotrans _ _ w·x#y·z (w · z)
... | inl w·x#w·z =    inr (·-#-extensional-case1 w x y z w·x#y·z w·x#w·z) -- first case
... | inr w·z#y·z = let z·w≡z·y = (transport (λ i → ·-comm w z i # ·-comm y z i) w·z#y·z)
                    in inl (·-#-extensional-case1 _ _ _ _ z·w≡z·y z·w≡z·y) -- second case reduced to first case


{-# OPTIONS --cubical --safe --no-import-sorts #-}
module Cubical.Algebra.CommAlgebra.Base where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.Equiv.HalfAdjoint
open import Cubical.Foundations.SIP

open import Cubical.Data.Sigma

open import Cubical.Reflection.StrictEquiv

open import Cubical.Structures.Axioms
open import Cubical.Algebra.Semigroup
open import Cubical.Algebra.Monoid
open import Cubical.Algebra.CommRing
open import Cubical.Algebra.Ring
open import Cubical.Algebra.Algebra hiding (⟨_⟩a)

private
  variable
    ℓ ℓ′ : Level

record IsCommAlgebra (R : CommRing {ℓ}) {A : Type ℓ}
                     (0a : A) (1a : A)
                     (_+_ : A → A → A) (_·_ : A → A → A) (-_ : A → A)
                     (_⋆_ : ⟨ R ⟩ → A → A) : Type ℓ where

  constructor iscommalgebra

  field
    isAlgebra : IsAlgebra (CommRing→Ring R) 0a 1a _+_ _·_ -_ _⋆_
    ·-comm    : (x y : A) → x · y ≡ y · x

  open IsAlgebra isAlgebra public

record CommAlgebra (R : CommRing {ℓ}) : Type (ℓ-suc ℓ) where

  constructor commalgebra

  field
    Carrier        : Type ℓ
    0a             : Carrier
    1a             : Carrier
    _+_            : Carrier → Carrier → Carrier
    _·_            : Carrier → Carrier → Carrier
    -_             : Carrier → Carrier
    _⋆_            : ⟨ R ⟩ → Carrier → Carrier
    isCommAlgebra  : IsCommAlgebra R 0a 1a _+_ _·_ -_ _⋆_

  open IsCommAlgebra isCommAlgebra public

module _ {R : CommRing {ℓ}} where
  open CommRingStr (snd R) using (1r) renaming (_+_ to _+r_; _·_ to _·s_)

  ⟨_⟩a : CommAlgebra R → Type ℓ
  ⟨_⟩a = CommAlgebra.Carrier

  CommAlgebra→Algebra : (A : CommAlgebra R) → Algebra (CommRing→Ring R)
  CommAlgebra→Algebra (commalgebra Carrier _ _ _ _ _ _ (iscommalgebra isAlgebra ·-comm)) =
    algebra Carrier _ _ _ _ _ _ isAlgebra

  CommAlgebra→CommRing : (A : CommAlgebra R) → CommRing {ℓ}
  CommAlgebra→CommRing (commalgebra Carrier _ _ _ _ _ _
                          (iscommalgebra isAlgebra ·-comm)) =
    _ , commringstr _ _ _ _ _ (iscommring (IsAlgebra.isRing isAlgebra) ·-comm)

  CommAlgebraEquiv : (R S : CommAlgebra R) → Type ℓ
  CommAlgebraEquiv R S = AlgebraEquiv (CommAlgebra→Algebra R) (CommAlgebra→Algebra S)

  makeIsCommAlgebra : {A : Type ℓ} {0a 1a : A}
                      {_+_ _·_ : A → A → A} { -_ : A → A} {_⋆_ : ⟨ R ⟩ → A → A}
                      (isSet-A : isSet A)
                      (+-assoc :  (x y z : A) → x + (y + z) ≡ (x + y) + z)
                      (+-rid : (x : A) → x + 0a ≡ x)
                      (+-rinv : (x : A) → x + (- x) ≡ 0a)
                      (+-comm : (x y : A) → x + y ≡ y + x)
                      (·-assoc :  (x y z : A) → x · (y · z) ≡ (x · y) · z)
                      (·-lid : (x : A) → 1a · x ≡ x)
                      (·-ldist-+ : (x y z : A) → (x + y) · z ≡ (x · z) + (y · z))
                      (·-comm : (x y : A) → x · y ≡ y · x)
                      (⋆-assoc : (r s : ⟨ R ⟩) (x : A) → (r ·s s) ⋆ x ≡ r ⋆ (s ⋆ x))
                      (⋆-ldist : (r s : ⟨ R ⟩) (x : A) → (r +r s) ⋆ x ≡ (r ⋆ x) + (s ⋆ x))
                      (⋆-rdist : (r : ⟨ R ⟩) (x y : A) → r ⋆ (x + y) ≡ (r ⋆ x) + (r ⋆ y))
                      (⋆-lid   : (x : A) → 1r ⋆ x ≡ x)
                      (⋆-lassoc : (r : ⟨ R ⟩) (x y : A) → (r ⋆ x) · y ≡ r ⋆ (x · y))
                    → IsCommAlgebra R 0a 1a _+_ _·_ -_ _⋆_
  makeIsCommAlgebra {A} {0a} {1a} {_+_} {_·_} { -_} {_⋆_} isSet-A
                    +-assoc +-rid +-rinv +-comm
                    ·-assoc ·-lid ·-ldist-+ ·-comm
                    ⋆-assoc ⋆-ldist ⋆-rdist ⋆-lid ⋆-lassoc
   = iscommalgebra
     (makeIsAlgebra
       isSet-A
       +-assoc +-rid +-rinv +-comm
       ·-assoc
         (λ x → x · 1a ≡⟨ ·-comm _ _ ⟩ 1a · x ≡⟨ ·-lid _ ⟩ x ∎)
         ·-lid
         (λ x y z → x · (y + z)       ≡⟨ ·-comm _ _ ⟩
                    (y + z) · x       ≡⟨ ·-ldist-+ _ _ _ ⟩
                    (y · x) + (z · x) ≡⟨ cong (λ u → (y · x) + u) (·-comm _ _) ⟩
                    (y · x) + (x · z) ≡⟨ cong (λ u → u + (x · z)) (·-comm _ _) ⟩
                    (x · y) + (x · z) ∎)
         ·-ldist-+
       ⋆-assoc
         ⋆-ldist
         ⋆-rdist
         ⋆-lid
         ⋆-lassoc
         λ r x y → r ⋆ (x · y) ≡⟨ cong (λ u → r ⋆ u) (·-comm _ _) ⟩
                   r ⋆ (y · x) ≡⟨ sym (⋆-lassoc _ _ _) ⟩
                   (r ⋆ y) · x ≡⟨ ·-comm _ _ ⟩
                   x · (r ⋆ y) ∎)
     ·-comm

module CommAlgebraΣTheory (R : CommRing {ℓ}) where

  open AlgebraΣTheory (CommRing→Ring R)

  CommAlgebraAxioms : (A : Type ℓ) (s : RawAlgebraStructure A) → Type ℓ
  CommAlgebraAxioms A (_+_ , _·_ , 1a , _⋆_) = AlgebraAxioms A (_+_ , _·_ , 1a , _⋆_)
                                      × ((x y : A) → x · y ≡ y · x)

  CommAlgebraStructure : Type ℓ → Type ℓ
  CommAlgebraStructure = AxiomsStructure RawAlgebraStructure CommAlgebraAxioms

  CommAlgebraΣ : Type (ℓ-suc ℓ)
  CommAlgebraΣ = TypeWithStr ℓ CommAlgebraStructure

  CommAlgebraEquivStr : StrEquiv CommAlgebraStructure ℓ
  CommAlgebraEquivStr = AxiomsEquivStr RawAlgebraEquivStr CommAlgebraAxioms

  isPropCommAlgebraAxioms : (A : Type ℓ) (s : RawAlgebraStructure A)
                       → isProp (CommAlgebraAxioms A s)
  isPropCommAlgebraAxioms A (_+_ , _·_ , 1a , _⋆_) =
    isPropΣ (isPropAlgebraAxioms A (_+_ , _·_ , 1a , _⋆_))
           λ isAlgebra → isPropΠ2 λ _ _ → (isSetAlgebraΣ (A , _ , isAlgebra)) _ _

  CommAlgebra→CommAlgebraΣ : CommAlgebra R → CommAlgebraΣ
  CommAlgebra→CommAlgebraΣ (commalgebra _ _ _ _ _ _ _ (iscommalgebra G C)) =
    _ , _ , Algebra→AlgebraΣ (algebra _ _ _ _ _ _ _ G) .snd .snd , C

  CommAlgebraΣ→CommAlgebra : CommAlgebraΣ → CommAlgebra R
  CommAlgebraΣ→CommAlgebra (_ , _ , G , C) =
    commalgebra _ _ _ _ _ _ _ (iscommalgebra (AlgebraΣ→Algebra (_ , _ , G) .Algebra.isAlgebra) C)

  CommAlgebraIsoCommAlgebraΣ : Iso (CommAlgebra R) CommAlgebraΣ
  CommAlgebraIsoCommAlgebraΣ =
    iso CommAlgebra→CommAlgebraΣ CommAlgebraΣ→CommAlgebra (λ _ → refl) (λ _ → refl)

  commAlgebraUnivalentStr : UnivalentStr CommAlgebraStructure CommAlgebraEquivStr
  commAlgebraUnivalentStr = axiomsUnivalentStr _ isPropCommAlgebraAxioms rawAlgebraUnivalentStr

  CommAlgebraΣPath : (A B : CommAlgebraΣ) → (A ≃[ CommAlgebraEquivStr ] B) ≃ (A ≡ B)
  CommAlgebraΣPath = SIP commAlgebraUnivalentStr

  CommAlgebraEquivΣ : (A B : CommAlgebra R) → Type ℓ
  CommAlgebraEquivΣ A B = CommAlgebra→CommAlgebraΣ A ≃[ CommAlgebraEquivStr ] CommAlgebra→CommAlgebraΣ B

  CommAlgebraPath : (A B : CommAlgebra R) → (CommAlgebraEquiv A B) ≃ (A ≡ B)
  CommAlgebraPath A B =
    CommAlgebraEquiv A B   ≃⟨ strictIsoToEquiv AlgebraEquivΣPath ⟩
    CommAlgebraEquivΣ A B  ≃⟨ CommAlgebraΣPath _ _ ⟩
    CommAlgebra→CommAlgebraΣ A ≡ CommAlgebra→CommAlgebraΣ B
      ≃⟨ isoToEquiv (invIso (congIso CommAlgebraIsoCommAlgebraΣ)) ⟩
    A ≡ B ■

CommAlgebraPath : (R : CommRing {ℓ}) → (A B : CommAlgebra R) → (CommAlgebraEquiv A B) ≃ (A ≡ B)
CommAlgebraPath = CommAlgebraΣTheory.CommAlgebraPath


module puzzle where

---- 仮定
-- 猫か犬を飼っている人は山羊を飼ってない
-- 猫を飼ってない人は、犬かウサギを飼っている
-- 猫も山羊も飼っていない人は、ウサギを飼っている
--
---- 問題
-- 山羊を飼っている人は、犬を飼っていない
-- 山羊を飼っている人は、ウサギを飼っている
-- ウサギを飼っていない人は、猫を飼っている

module pet-research where
  open import logic
  open import Relation.Nullary
  open import Data.Empty

  postulate
     lem : (a : Set) → a ∨ ( ¬ a )

  record PetResearch ( Cat Dog Goat Rabbit : Set ) : Set where
     field
        fact1 : ( Cat ∨ Dog ) → ¬ Goat
        fact2 : ¬ Cat →  ( Dog ∨ Rabbit )
        fact3 : ¬ ( Cat ∨ Goat ) →  Rabbit

  module tmp ( Cat Dog Goat Rabbit : Set ) (p :  PetResearch  Cat Dog Goat Rabbit ) where

    open PetResearch

    problem0 : Cat ∨ Dog ∨ Goat ∨ Rabbit
    problem0 with lem Cat | lem Goat
    ... | case1 c | g = case1 c
    ... | c | case1 g = case2 ( case2 ( case1 g ) )
    ... | case2 ¬c | case2 ¬g  = case2 ( case2 ( case2 ( fact3 p lemma1 ))) where
        lemma1 : ¬ ( Cat ∨ Goat )
        lemma1 (case1 c) = ¬c c
        lemma1 (case2 g) = ¬g g

    problem1 : Goat → ¬ Dog
    problem1 g d = fact1 p (case2 d) g

    problem2 : Goat → Rabbit
    problem2 g with lem Cat | lem Dog
    problem2 g | case1 c | d = ⊥-elim ( fact1 p (case1 c ) g )
    problem2 g | case2 ¬c | case1 d = ⊥-elim ( fact1 p (case2 d ) g )
    problem2 g | case2 ¬c | case2 ¬d with lem Rabbit
    ... | case1  r = r
    ... | case2 ¬r = fact3 p lemma2 where
        lemma2 : ¬ ( Cat ∨ Goat )
        lemma2 (case1 c) = ¬c c
        lemma2 (case2 g) with fact2 p ¬c
        lemma2 (case2 g) | case1 d = ¬d d
        lemma2 (case2 g) | case2 r = ¬r r

    problem3 : (¬ Rabbit ) → Cat
    problem3 ¬r with lem Cat | lem Goat
    problem3 ¬r | case1 c | g = c
    problem3 ¬r | case2 ¬c | g = ⊥-elim ( ¬r ( fact3 p lemma3 )) where
        lemma3 :  ¬ ( Cat ∨ Goat )
        lemma3 (case1 c) = ¬c c
        lemma3 (case2 g) with fact2 p ¬c
        lemma3 (case2 g) | case1 d = fact1 p (case2 d ) g
        lemma3 (case2 g) | case2 r = ¬r r

module pet-research1 ( Cat Dog Goat Rabbit : Set ) where

  open import Data.Bool
  open import Relation.Binary
  open import Relation.Binary.PropositionalEquality

  _=>_ :  Bool → Bool → Bool
  _ => true = true
  false => _ = true
  true => false = false

  ¬_ : Bool → Bool
  ¬ p = not p

  problem0 :  ( Cat Dog Goat Rabbit : Bool ) →
    ((( Cat ∨ Dog ) => (¬ Goat) ) ∧ ( (¬ Cat ) =>  ( Dog ∨ Rabbit ) ) ∧ ( ( ¬ ( Cat ∨ Goat ) ) =>  Rabbit ) )
    => (Cat ∨ Dog ∨ Goat ∨ Rabbit) ≡ true
  problem0 true d g r = refl
  problem0 false true g r = refl
  problem0 false false true r = refl
  problem0 false false false true = refl
  problem0 false false false false = refl

  problem1 :  ( Cat Dog Goat Rabbit : Bool ) →
    ((( Cat ∨ Dog ) => (¬ Goat) ) ∧ ( (¬ Cat ) =>  ( Dog ∨ Rabbit ) ) ∧ ( ( ¬ ( Cat ∨ Goat ) ) =>  Rabbit ) )
    => ( Goat => ( ¬ Dog )) ≡ true
  problem1 c false false r = refl
  problem1 c true false r = refl
  problem1 c false true r = refl
  problem1 false true true r = refl
  problem1 true true true r = refl

  problem2 :  ( Cat Dog Goat Rabbit : Bool ) →
    ((( Cat ∨ Dog ) => (¬ Goat) ) ∧ ( (¬ Cat ) =>  ( Dog ∨ Rabbit ) ) ∧ ( ( ¬ ( Cat ∨ Goat ) ) =>  Rabbit ) )
    => ( Goat => Rabbit ) ≡ true
  problem2 c d false false = refl
  problem2 c d false true = refl
  problem2 c d true true = refl
  problem2 true d true false = refl
  problem2 false false true false = refl
  problem2 false true true false = refl

  problem3 :  ( Cat Dog Goat Rabbit : Bool ) →
    ((( Cat ∨ Dog ) => (¬ Goat) ) ∧ ( (¬ Cat ) =>  ( Dog ∨ Rabbit ) ) ∧ ( ( ¬ ( Cat ∨ Goat ) ) =>  Rabbit ) )
    => ( (¬ Rabbit ) => Cat ) ≡ true
  problem3 false d g true = refl
  problem3 true d g true = refl
  problem3 true d g false = refl
  problem3 false false false false = refl
  problem3 false false true false = refl
  problem3 false true false false = refl
  problem3 false true true false = refl

-- module pet-research2 ( Cat Dog Goat Rabbit : Set ) where
--
--   open import Data.Bool hiding ( _∨_ )
--   open import Relation.Binary
--   open import Relation.Binary.PropositionalEquality
--
--   ¬_ : Bool → Bool
--   ¬ p = p xor true
--
--   infixr 5 _∨_
--   _∨_ :  Bool → Bool → Bool
--   a ∨ b = ¬ ( (¬ a) ∧ (¬ b ) )
--
--   _=>_ :  Bool → Bool → Bool
--   a => b = (¬ a ) ∨ b
--
--   open import Data.Bool.Solver using (module xor-∧-Solver)
--   open xor-∧-Solver
--
--   problem0' :  ( Cat : Bool ) → (Cat xor Cat ) ≡ false
--   problem0' = solve 1 (λ c → (c :+ c ) := con false ) refl
--
--   problem1' :  ( Cat : Bool ) → (Cat ∧ (Cat xor true ))  ≡ false
--   problem1' = solve 1 (λ c → ((c :* (c :+ con true )) ) := con false ) {!!}
--
--   open import Data.Nat
--   :¬_ : {n : ℕ} → Polynomial n → Polynomial n
--   :¬ p = p :+ con true
--
--   _:∨_ : {n : ℕ} → Polynomial n → Polynomial n → Polynomial n
--   a :∨ b = :¬ ( ( :¬ a ) :* ( :¬ b ))
--
--   _:=>_ : {n : ℕ} → Polynomial n → Polynomial n → Polynomial n
--   a :=> b = ( :¬ a ) :∨ b
--
--   _:∧_ = _:*_
--
--   infixr 6 _:∧_
--   infixr 5 _:∨_
--
--   problem0 :  ( Cat Dog Goat Rabbit : Bool ) →
--     ((( Cat ∨ Dog ) => (¬ Goat) ) ∧ ( (¬ Cat ) =>  ( Dog ∨ Rabbit ) ) ∧ ( ( ¬ ( Cat ∨ Goat ) ) =>  Rabbit ) )
--     => (Cat ∨ Dog ∨ Goat ∨ Rabbit) ≡ true
--   problem0 = solve 4 ( λ Cat Dog Goat Rabbit → (
--     ( ((Cat :∨ Dog ) :=> (:¬ Goat)) :∧ ( ((:¬ Cat ) :=>  ( Dog :∨ Rabbit )) :∧ (( :¬ ( Cat :∨ Goat ) ) :=>  Rabbit)  ))
--     :=> ( Cat :∨ (Dog :∨ ( Goat :∨ Rabbit))) ) := con true ) {!!}
--
--   problem1 :  ( Cat Dog Goat Rabbit : Bool ) →
--     ((( Cat ∨ Dog ) => (¬ Goat) ) ∧ ( (¬ Cat ) =>  ( Dog ∨ Rabbit ) ) ∧ ( ( ¬ ( Cat ∨ Goat ) ) =>  Rabbit ) )
--     => ( Goat => ( ¬ Dog )) ≡ true
--   problem1 c false false r = {!!}
--   problem1 c true false r = {!!}
--   problem1 c false true r = {!!}
--   problem1 false true true r = refl
--   problem1 true true true r = refl
--
--   problem2 :  ( Cat Dog Goat Rabbit : Bool ) →
--     ((( Cat ∨ Dog ) => (¬ Goat) ) ∧ ( (¬ Cat ) =>  ( Dog ∨ Rabbit ) ) ∧ ( ( ¬ ( Cat ∨ Goat ) ) =>  Rabbit ) )
--     => ( Goat => Rabbit ) ≡ true
--   problem2 c d false false = {!!}
--   problem2 c d false true = {!!}
--   problem2 c d true true = {!!}
--   problem2 true d true false = refl
--   problem2 false false true false = refl
--   problem2 false true true false = refl
--
--   problem3 :  ( Cat Dog Goat Rabbit : Bool ) →
--     ((( Cat ∨ Dog ) => (¬ Goat) ) ∧ ( (¬ Cat ) =>  ( Dog ∨ Rabbit ) ) ∧ ( ( ¬ ( Cat ∨ Goat ) ) =>  Rabbit ) )
--     => ( (¬ Rabbit ) => Cat ) ≡ true
--   problem3 false d g true = {!!}
--   problem3 true d g true = {!!}
--   problem3 true d g false = {!!}
--   problem3 false false false false = refl
--   problem3 false false true false = refl
--   problem3 false true false false = refl
--   problem3 false true true false = refl


{-# OPTIONS --safe --warning=error --without-K --guardedness #-}

open import Agda.Primitive using (Level; lzero; lsuc; _⊔_)
open import Setoids.Setoids
open import Rings.Definition
open import Rings.Orders.Partial.Definition
open import Rings.Orders.Total.Definition
open import Groups.Definition
open import Groups.Lemmas
open import Fields.Fields
open import Sets.EquivalenceRelations
open import Sequences
open import Setoids.Orders.Partial.Definition
open import Setoids.Orders.Total.Definition
open import Functions.Definition
open import LogicalFormulae
open import Numbers.Naturals.Semiring
open import Numbers.Naturals.Order
open import Numbers.Naturals.Order.Lemmas
open import Semirings.Definition

module Fields.CauchyCompletion.NearlyTotalOrder {m n o : _} {A : Set m} {S : Setoid {m} {n} A} {_+_ : A → A → A} {_*_ : A → A → A} {_<_ : Rel {m} {o} A} {pOrder : SetoidPartialOrder S _<_} {R : Ring S _+_ _*_} {pRing : PartiallyOrderedRing R pOrder} (order : TotallyOrderedRing pRing) (F : Field R) where

open Setoid S
open SetoidTotalOrder (TotallyOrderedRing.total order)
open SetoidPartialOrder pOrder
open Equivalence eq
open PartiallyOrderedRing pRing
open Ring R
open Group additiveGroup
open Field F
open import Fields.Orders.Lemmas {F = F} {pRing} (record { oRing = order })
open import Setoids.Orders.Partial.Sequences pOrder

open import Rings.InitialRing R
open import Rings.Orders.Partial.Lemmas pRing
open import Rings.Orders.Total.Lemmas order
open import Rings.Orders.Total.Cauchy order
open import Rings.Orders.Total.AbsoluteValue order
open import Fields.Lemmas F
open import Fields.CauchyCompletion.Definition order F
open import Fields.CauchyCompletion.Setoid order F
open import Fields.CauchyCompletion.Group order F
open import Fields.CauchyCompletion.Addition order F
open import Fields.CauchyCompletion.Approximation order F
open import Fields.CauchyCompletion.Comparison order F
open import Fields.CauchyCompletion.PartiallyOrderedRing order F
open import Fields.Orders.Total.Lemmas {F = F} (record { oRing = order })

makeIncreasingLemma : (a : A) (s : Sequence A) → Sequence A
Sequence.head (makeIncreasingLemma a s) with totality a (Sequence.head s)
... | inl (inl x) = Sequence.head s
... | inl (inr x) = a
... | inr x = a
Sequence.tail (makeIncreasingLemma a s) = makeIncreasingLemma (Sequence.head (makeIncreasingLemma a s)) (Sequence.tail s)

makeIncreasingLemmaIsIncreasing : (a : A) (s : Sequence A) → WeaklyIncreasing (makeIncreasingLemma a s)
makeIncreasingLemmaIsIncreasing a s zero with totality a (Sequence.head s)
makeIncreasingLemmaIsIncreasing a s zero | inl (inl x) with totality (Sequence.head s) (Sequence.head (Sequence.tail s))
makeIncreasingLemmaIsIncreasing a s zero | inl (inl x) | inl (inl y) = inl y
makeIncreasingLemmaIsIncreasing a s zero | inl (inl x) | inl (inr y) = inr reflexive
makeIncreasingLemmaIsIncreasing a s zero | inl (inl x) | inr y = inr reflexive
makeIncreasingLemmaIsIncreasing a s zero | inl (inr x) with totality a (Sequence.head (Sequence.tail s))
... | inl (inl y) = inl y
... | inl (inr y) = inr reflexive
... | inr y = inr reflexive
makeIncreasingLemmaIsIncreasing a s zero | inr x with totality a (Sequence.head (Sequence.tail s))
... | inl (inl y) = inl y
... | inl (inr y) = inr reflexive
... | inr y = inr reflexive
makeIncreasingLemmaIsIncreasing a s (succ m) = makeIncreasingLemmaIsIncreasing (Sequence.head (makeIncreasingLemma a s)) (Sequence.tail s) m

makeIncreasing : Sequence A → Sequence A
Sequence.head (makeIncreasing x) = Sequence.head x
Sequence.tail (makeIncreasing x) = makeIncreasingLemma (Sequence.head x) (Sequence.tail x)

makeIncreasingIsIncreasing : (a : Sequence A) → WeaklyIncreasing (makeIncreasing a)
makeIncreasingIsIncreasing a zero with totality (Sequence.head a) (Sequence.head (Sequence.tail a))
... | inl (inl x) = inl x
... | inl (inr x) = inr reflexive
... | inr x = inr reflexive
makeIncreasingIsIncreasing a (succ m) = makeIncreasingLemmaIsIncreasing _ _ m

approximateIncreasingSeqRaw : CauchyCompletion → Sequence A
approximateIncreasingSeqRaw a = funcToSequence f
  where
    f : ℕ → A
    f n with allInvertible (fromN (succ n)) λ n=0 → irreflexive (<WellDefined reflexive n=0 (fromNPreservesOrder (0<1 nontrivial) (succIsPositive n)))
    ... | 1/n , prN = underlying (approximateBelow a 1/n (reciprocalPositive' (fromN (succ n)) 1/n (fromNPreservesOrder (0<1 nontrivial) (succIsPositive n)) prN))

approximateIncreasingSeq : CauchyCompletion → Sequence A
approximateIncreasingSeq a = makeIncreasing (approximateIncreasingSeqRaw a)

approximateIncreasingConverges : (a : CauchyCompletion) → cauchy (approximateIncreasingSeq a)
approximateIncreasingConverges a e 0<e = {!!}

approximateIncreasingIncreases : (a : CauchyCompletion) → WeaklyIncreasing (approximateIncreasingSeq a)
approximateIncreasingIncreases a = makeIncreasingIsIncreasing (approximateIncreasingSeqRaw a)

approximateIncreasing : CauchyCompletion → CauchyCompletion
approximateIncreasing a = record { elts = approximateIncreasingSeq a ; converges = approximateIncreasingConverges a }

approximateIncreasingEqual : (a : CauchyCompletion) → Setoid._∼_ cauchyCompletionSetoid (approximateIncreasing a) a
approximateIncreasingEqual a e 0<e = {!!}

decideSign : (a : CauchyCompletion) → (Setoid._∼_ cauchyCompletionSetoid a (injection 0R) → False) → (a <Cr 0R) || (0R r<C a)
decideSign a a!=0 = {!!}
  where

private
  lemma : (a b : CauchyCompletion) → Setoid._∼_ cauchyCompletionSetoid ((b +C (-C a)) +C a) b
  lemma a b = Equivalence.transitive (Setoid.eq cauchyCompletionSetoid) {record { converges = CauchyCompletion.converges ((b +C (-C a)) +C a) }} {record { converges = CauchyCompletion.converges (b +C ((-C a) +C a)) }} {record { converges = CauchyCompletion.converges b }} (Group.+Associative' CGroup {record { converges = CauchyCompletion.converges b }} {record { converges = CauchyCompletion.converges (-C a) }} { record { converges = CauchyCompletion.converges a }}) (Equivalence.transitive (Setoid.eq cauchyCompletionSetoid) {record {converges = CauchyCompletion.converges (b +C ((-C a) +C a))}} {record { converges = CauchyCompletion.converges (b +C (injection 0R))}} {record {converges = CauchyCompletion.converges b}} (Group.+WellDefinedRight CGroup {record {converges = CauchyCompletion.converges b}} {record {converges = CauchyCompletion.converges ((-C a) +C a)}} {record { converges = CauchyCompletion.converges (injection 0R)}} (Group.invLeft CGroup {record { converges = CauchyCompletion.converges a }})) (Group.identRight CGroup {record { converges = CauchyCompletion.converges b }}))

nearlyTotal : (a b : CauchyCompletion) → (Setoid._∼_ cauchyCompletionSetoid a b → False) → (a <C b) || (b <C a)
nearlyTotal a b a!=b with decideSign (b +C (-C a)) λ bad → a!=b (Equivalence.symmetric (Setoid.eq cauchyCompletionSetoid) {record { converges = CauchyCompletion.converges b }} {record { converges = CauchyCompletion.converges a  }} (transferToRight CGroup {record { converges = CauchyCompletion.converges b }} {record { converges = CauchyCompletion.converges a }} bad))
... | inl x = inr (<CWellDefined (lemma a b) (Group.identLeft CGroup {record { converges = CauchyCompletion.converges a }}) (PartiallyOrderedRing.orderRespectsAddition CpOrderedRing (<CRelaxR x) a))
... | inr x = inl (<CWellDefined (Group.identLeft CGroup {record { converges = CauchyCompletion.converges a }}) (lemma a b) (PartiallyOrderedRing.orderRespectsAddition CpOrderedRing (<CRelaxL x) a))


open import Data.Nat
open import Data.Bool
open import Data.Product
open import Function
open import Data.List hiding (lookup)
open import Data.List.All
open import Data.List.Membership.Propositional
open import Data.Maybe.Categorical
open import Category.Monad
open import Agda.Primitive
open import Size
open import Codata.Thunk
open import Codata.Delay
open import Codata.Delay.Categorical

module Typed.STLC.Delay where

data Ty : Set where
  numt boolt : Ty
  _⟶_ : Ty → Ty → Ty

data Expr : List Ty → Ty → Set where
  add : ∀ {Γ} →
    Expr Γ numt → Expr Γ numt →
    Expr Γ numt
  num : ∀ {Γ} →
    ℕ →
    Expr Γ numt
  true′ false′ : ∀ {Γ} →
    Expr Γ boolt
  if′_then_else : ∀ {Γ} →
    ∀ {t} → Expr Γ boolt → Expr Γ t → Expr Γ t →
    Expr Γ t
  ƛ_ : ∀ {Γ t₁ t₂} →
    Expr (t₁ ∷ Γ) t₂ →
    Expr Γ (t₁ ⟶ t₂)
  _∙_ : ∀ {Γ t₁ t₂} →
    Expr Γ (t₁ ⟶ t₂) → Expr Γ t₁ →
    Expr Γ t₂
  var : ∀ {Γ t} →
    t ∈ Γ →
    Expr Γ t

mutual
  data Val : Ty → Set where
    numv : ℕ → Val numt
    boolv : Bool → Val boolt
    ⟨ƛ_,_⟩ : ∀ {Γ t₁ t₂} → Expr (t₁ ∷ Γ) t₂ → Env Γ → Val (t₁ ⟶ t₂)

  Env : List Ty → Set
  Env Γ = All Val Γ

ReaderT : ∀ {a} {M : Set a → Set a} (R : Set a) → RawMonad M → RawMonad (λ A → R → M A)
ReaderT _ M = record { return = λ a _ → return a ;
                       _>>=_ = λ f k r → f r >>= λ x → k x r }
  where open RawMonad M

module _ {Γ : List Ty} {i} where
  open RawMonad {lzero} (ReaderT (Env Γ) (Sequential.monad {i})) public

M : List Ty → Size → Set → Set
M Γ i A = Env Γ → Delay A i

getEnv : ∀ {Γ i} → M Γ i (Env Γ)
getEnv E = now E

withEnv : ∀ {Γ Γ' i A} → Env Γ → M Γ i A → M Γ' i A
withEnv E m _ = m E

get : ∀ {Γ t i} → t ∈ Γ → M Γ i (Val t)
get x E = now (lookup E x)

mutual
  eval : ∀ {Γ t} → Expr Γ t → ∀ {i} → M Γ i (Val t)
  eval (add e₁ e₂) = do
    numv n₁ ← eval e₁
    numv n₂ ← eval e₁
    return (numv (n₁ + n₂))
  eval (num n) = do
    return (numv n)
  eval true′ = do
    return (boolv true)
  eval false′ = do
    return (boolv false)
  eval (if′ c then t else e) = do
    (boolv b) ← eval c
    if b then eval t else eval e
  eval (ƛ e) = do
    E ← getEnv
    return ⟨ƛ e , E ⟩
  eval (f ∙ a) = do
    ⟨ƛ e , E ⟩ ← eval f
    v ← eval a
    withEnv (v ∷ E) (▹eval e)
  eval (var x) = do
    get x

  ▹eval : ∀ {Γ i t} → Expr Γ t → M Γ i (Val t)
  ▹eval e E = later λ where .force → eval e E


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

open import Agda.Primitive using (Level; lzero; lsuc; _⊔_)
open import Setoids.Setoids
open import Setoids.Functions.Definition
open import Sets.EquivalenceRelations

module Setoids.Functions.Extension where

ExtensionallyEqual : {a b c d : _} {A : Set a} {B : Set b} {S : Setoid {a} {c} A} {T : Setoid {b} {d} B} {f g : A → B} (fWd : WellDefined S T f) (gWd : WellDefined S T g) → Set (a ⊔ d)
ExtensionallyEqual {A = A} {T = T} {f = f} {g = g} fWD gWD = (∀ {x : A} → Setoid._∼_ T (f x) (g x))

extensionallyEqualReflexive : {a b c d : _} {A : Set a} {B : Set b} (S : Setoid {a} {c} A) (T : Setoid {b} {d} B) (f : A → B) (fWD1 fWD2 : WellDefined S T f) → ExtensionallyEqual {S = S} {T} fWD1 fWD2
extensionallyEqualReflexive S T f fWD1 _ = Equivalence.reflexive (Setoid.eq T)

extensionallyEqualSymmetric : {a b c d : _} {A : Set a} {B : Set b} (S : Setoid {a} {c} A) (T : Setoid {b} {d} B) (f g : A → B) (fWD : WellDefined S T f) (gWD : WellDefined S T g) → ExtensionallyEqual {S = S} {T = T} fWD gWD → ExtensionallyEqual {S = S} {T} gWD fWD
extensionallyEqualSymmetric S T f g fWD gWD pr = Equivalence.symmetric (Setoid.eq T) pr

extensionallyEqualTransitive : {a b c d : _} {A : Set a} {B : Set b} (S : Setoid {a} {c} A) (T : Setoid {b} {d} B) (f g h : A → B) (fWD : WellDefined S T f) (gWD : WellDefined S T g) (hWD : WellDefined S T h) → ExtensionallyEqual {S = S} {T} fWD gWD → ExtensionallyEqual {S = S} {T} gWD hWD → ExtensionallyEqual {S = S} {T} fWD hWD
extensionallyEqualTransitive S T f g h fWD gWD hWD pr1 pr2 = Equivalence.transitive (Setoid.eq T) pr1 pr2


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

open import Agda.Builtin.Equality
open import Agda.Builtin.Nat

single : {m n : Nat} → suc m ≡ suc n → n ≡ m
single p with refl ← p = refl

double : {m n p : Nat} → suc m ≡ n → suc n ≡ 2 + p → m ≡ p
double p q with refl ← p | refl ← q = refl

_∋_ : (A : Set) → A → A
A ∋ a = a

-- The second equality proof is only well-typed
-- after the first one has been used

tele : {m n : Nat} → suc m ≡ suc n → m ≡ n
tele {m} {n} p
  with refl ← p
       | refl ← (n ≡ m) ∋ refl
       = refl

tele' : {m n : Nat} → m ≡ n → m ≡ n
tele' {m} {n} p with refl ← p with (n ≡ m) ∋ refl
... | q = refl

-- Further splitting after a using & with

tele'' : {m n : Nat} → m ≡ n → Nat → Nat
tele'' {m} {n} p r with refl ← p | (n ≡ m) ∋ refl
tele'' {m} {m} p zero    | q = m
tele'' {m} {m} p (suc r) | q = r

data Vec {a} (A : Set a) : Nat → Set a where
  []  : Vec A 0
  _∷_ : ∀ {n} → A → Vec A n → Vec A (suc n)

module _ {a} {A : Set a} {n} (xs : Vec A (suc n)) where

  head : A
  head with (x ∷ _) ← xs = x

  tail : Vec A n
  tail with (_ ∷ xs) ← xs = xs -- pattern shadows variable with'd on

castVec : ∀ {m n} → m ≡ n → Vec Nat m → Vec Nat n
castVec eq ms with refl ← eq = ms

data All (P : Nat → Set) : ∀ {n} → Vec Nat n → Set where
  []  : All P []
  _∷_ : ∀ {n x xs} → P x → All P {n} xs → All P (x ∷ xs)

open import Agda.Builtin.Sigma

castAll : ∀ {P m n xs ys} → Σ (m ≡ n) (λ eq → castVec eq xs ≡ ys) →
          All P xs → All P ys
castAll (refl , refl) all = all


module my-bool-test where

open import bool
open import eq
open import level

~~tt : ~ ~ tt ≡ tt
~~tt = refl

~~ff : ~ ~ ff ≡ ff
~~ff = refl
{-
~~-elim : ∀ (b : 𝔹) → ~ ~ b ≡ b
~~-elim tt = refl
~~-elim ff = refl
-}
~~-elim2 : ∀ (b : 𝔹) → ~ ~ b ≡ b
~~-elim2 tt = ~~tt
~~-elim2 ff = ~~ff

~~tt' : ~ ~ tt ≡ tt
~~tt' = refl{lzero}{𝔹}{tt}

~~ff' : ~ ~ ff ≡ ff
~~ff' = refl{lzero}{𝔹}{ff}

~~-elim : ∀ (b : 𝔹) → ~ ~ b ≡ b
~~-elim tt = refl
~~-elim ff = refl

||≡ff₁ : ∀ {b1 b2} → b1 || b2 ≡ ff → b1 ≡ ff
||≡ff₁ {ff} _ = refl{lzero}{𝔹}{ff}
||≡ff₁ {tt} ()

||≡ff₂ : ∀ {b1 b2} → b1 || b2 ≡ ff → ff ≡ b1
||≡ff₂ {ff} _ = refl{lzero}{𝔹}{ff}
||≡ff₂ {tt} p = sym p

||-cong₁ : ∀ {b1 b1' b2} → b1 ≡ b1' → b1 || b2 ≡ b1' || b2
||-cong₁{b1}{.b1}{b2} refl = refl

||-cong₂ : ∀ {b1 b2 b2'} → b2 ≡ b2' → b1 || b2 ≡ b1 || b2'
||-cong₂ p rewrite p = refl

ite-same : ∀{ℓ}{A : Set ℓ} →
           ∀(b : 𝔹) (x : A) →
           (if b then x else x) ≡ x
ite-same tt x = refl
ite-same ff x = refl

𝔹-contra : ff ≡ tt → ∀ {P : Set} → P
𝔹-contra ()

p : ff && ff ≡ ~ tt
p = refl


module Generic.Lib.Data.Nat where

open import Data.Nat.Base hiding (_⊔_; _^_) public

open import Generic.Lib.Decidable

instance
  ℕEq : Eq ℕ
  ℕEq = viaBase Nat._≟_ where
    import Data.Nat as Nat

foldℕ : ∀ {α} {A : Set α} -> (A -> A) -> A -> ℕ -> A
foldℕ f x  0      = x
foldℕ f x (suc n) = f (foldℕ f x n)


{-# OPTIONS --without-K #-}
module Util.HoTT.Univalence.Axiom where

open import Util.HoTT.Equiv
open import Util.HoTT.Univalence.Statement
open import Util.Prelude
open import Util.Relation.Binary.PropositionalEquality using (Σ-≡⁻)

private
  variable
    α β γ : Level
    A B C : Set α

postulate
  univalence : ∀ {α} → Univalence α

≃→≡ : A ≃ B → A ≡ B
≃→≡ A≃B = univalence A≃B .proj₁ .proj₁

≡→≃∘≃→≡ : (p : A ≃ B) → ≡→≃ (≃→≡ p) ≡ p
≡→≃∘≃→≡ p = univalence p .proj₁ .proj₂

≃→≡∘≡→≃ : (p : A ≡ B) → ≃→≡ (≡→≃ p) ≡ p
≃→≡∘≡→≃ p = Σ-≡⁻ (univalence (≡→≃ p) .proj₂ (p , refl)) .proj₁

≃→≡-≡→≃-coh : (p : A ≡ B)
  → subst (λ q → ≡→≃ q ≡ ≡→≃ p) (≃→≡∘≡→≃ p) (≡→≃∘≃→≡ (≡→≃ p)) ≡ refl
≃→≡-≡→≃-coh p = Σ-≡⁻ (univalence (≡→≃ p) .proj₂ (p , refl)) .proj₂

≅→≡ : A ≅ B → A ≡ B
≅→≡ = ≃→≡ ∘ ≅→≃


module GTFL where

open import Data.Nat hiding (_⊓_; erase; _≟_; _≤_)
open import Data.Bool hiding (_≟_)
open import Data.Fin using (Fin; zero; suc; toℕ)
open import Data.Vec
open import Relation.Binary.PropositionalEquality
open import Relation.Nullary
open import Data.Empty
open import Function using (_∘_)

-- | Types
infixr 30 _⇒_
data GType : Set where
    nat  : GType
    bool : GType
    _⇒_  : GType → GType → GType
    ✭    : GType
    err  : GType    -- easier to model this as a type in Agda

-- | Untyped Expressions
data Expr : Set where
    litNat      : ℕ → Expr
    litBool     : Bool → Expr
    dyn         : Expr
    err         : Expr
    var         : ℕ → Expr
    lam         : GType → Expr → Expr
    _∙_         : Expr → Expr → Expr
    _⊕_         : Expr → Expr → Expr
    if_thn_els_ : Expr → Expr → Expr → Expr

Ctx : ℕ → Set
Ctx = Vec GType

infixr 10 _~_
data _~_ {A B : Set} (x : A) (y : B) : Set where
    cons : x ~ y

~dom : ∀ (t : GType) → GType
~dom (t ⇒ t₁) = t₁
~dom _ = err

~cod : ∀ (t : GType) → GType
~cod (t ⇒ t₁) = t₁
~cod _ = err

_⊓_ : ∀ (t₁ t₂ : GType) → GType
nat ⊓ nat = nat
bool ⊓ bool = bool
t₁ ⊓ ✭ = t₁
✭ ⊓ t₂ = t₂
(t₁ ⇒ t₂) ⊓ (t₃ ⇒ t₄) = (t₁ ⊓ t₃) ⇒ (t₂ ⊓ t₄)
_ ⊓ _ = err

-- | Typed Terms
data Term {n} (Γ : Ctx n) : GType → Set where
    Tx   : ∀ {t} (v : Fin n) → t ≡ lookup v Γ → Term Γ t
    Tn   : ℕ → Term Γ nat
    Tb   : Bool → Term Γ bool
    Tdy  : Term Γ ✭
    _T∙_ : ∀ {t₁ t₂} → Term Γ t₁ → Term Γ t₂ → t₂ ~ (~dom t₁) → Term Γ (~cod t₁)
    _T⊕_ : ∀ {t₁ t₂} → Term Γ t₁ → Term Γ t₂ → (t₁ ~ nat) → (t₂ ~ nat) → Term Γ (t₁ ⊓ t₂)
    Tif  : ∀ {t₁ t₂ t₃} → Term Γ t₁ → Term Γ t₂ → Term Γ t₃ → (t₁ ~ bool) → Term Γ (t₂ ⊓ t₃)
    Tlam : ∀ t₁ {t₂} → Term (t₁ ∷ Γ) t₂ → Term Γ (t₁ ⇒ t₂)

erase : ∀ {n} {Γ : Ctx n} {t} → Term Γ t → Expr
erase (Tx v x) = var (toℕ v)
erase (Tn x) = litNat x
erase (Tb x) = litBool x
erase Tdy    = dyn
erase ((term T∙ term₁) _) = (erase term) ∙ (erase term₁)
erase ((term T⊕ term₁) _ _) = (erase term) ⊕ (erase term₁)
erase (Tif b tt ff  _) = if erase b thn erase tt els erase ff
erase (Tlam t₁ term) = lam t₁ (erase term)

data Fromℕ (n : ℕ) : ℕ → Set where
    yes : (m : Fin n) → Fromℕ n (toℕ m)
    no  : (m : ℕ) → Fromℕ n (n + m)

fromℕ : ∀ n m → Fromℕ n m
fromℕ zero m = no m
fromℕ (suc n) zero = yes zero
fromℕ (suc n) (suc m) with fromℕ n m
fromℕ (suc n) (suc .(toℕ m)) | yes m = yes (suc m)
fromℕ (suc n) (suc .(n + m)) | no m = no m

data Check {n} (Γ : Ctx n) : Expr → Set where
    yes : (τ : GType) (t : Term Γ τ) → Check Γ (erase t)
    no  : {e : Expr} → Check Γ e

staticCheck : ∀ {n} (Γ : Ctx n) (t : Expr) → Check Γ t
-- | primitives
staticCheck Γ (litNat x)  = yes nat (Tn x)
staticCheck Γ (litBool x) = yes bool (Tb x)
staticCheck {n} Γ dyn     = yes ✭ Tdy
staticCheck Γ err         = no

-- | var lookup
staticCheck {n} Γ (var v) with fromℕ n v
staticCheck {n} Γ (var .(toℕ m)) | yes m = yes (lookup m Γ) (Tx m refl)
staticCheck {n} Γ (var .(n + m)) | no m  = no

-- | lambda abstraction
staticCheck Γ (lam x t) with staticCheck (x ∷ Γ) t
staticCheck Γ (lam x .(erase t)) | yes τ t = yes (x ⇒ τ) (Tlam x t) -- double check this
staticCheck Γ (lam x t)          | no      = no

-- | application
staticCheck Γ (t₁ ∙ t₂) with staticCheck Γ t₁ | staticCheck Γ t₂
staticCheck Γ (.(erase t₁) ∙ .(erase t)) | yes (τ₁ ⇒ τ₂) t₁ | (yes τ t) = yes τ₂ ((t₁ T∙ t) cons)
staticCheck Γ (.(erase t₁) ∙ .(erase t)) | yes _ t₁         | (yes τ t) = no -- not sure about this
staticCheck Γ (t₁ ∙ t₂)                  | _                | _         = no

-- | addition
staticCheck Γ (t₁ ⊕ t₂) with staticCheck Γ t₁ | staticCheck Γ t₂
staticCheck Γ (.(erase t₁) ⊕ .(erase t)) | yes nat t₁ | (yes nat t) = yes nat ((t₁ T⊕ t) cons cons)
staticCheck Γ (.(erase t₁) ⊕ .(erase t)) | yes ✭ t₁ | (yes nat t) = yes (✭ ⊓ nat) ((t₁ T⊕ t) cons cons)
staticCheck Γ (.(erase t₁) ⊕ .(erase t)) | yes nat t₁ | (yes ✭ t) = yes (nat ⊓ ✭) ((t₁ T⊕ t) cons cons)
staticCheck Γ (.(erase t₁) ⊕ .(erase t)) | yes ✭ t₁ | (yes ✭ t) = yes ✭ ((t₁ T⊕ t) cons cons)
staticCheck Γ (t₁ ⊕ t₂)                  | _ | _ = no

-- | if ... then ... else
staticCheck Γ (if t thn t₁ els t₂) with staticCheck Γ t
staticCheck Γ (if .(erase t) thn t₁ els t₂) | yes bool t with staticCheck Γ t₁ | staticCheck Γ t₂
staticCheck Γ (if .(erase t₂) thn .(erase t₁) els .(erase t)) | yes bool t₂ | (yes τ₁ t₁) | (yes τ₂ t) = yes (τ₁ ⊓ τ₂) (Tif t₂ t₁ t cons)
staticCheck Γ (if .(erase t₁) thn .(erase t) els t₂) | yes bool t₁ | (yes τ t) | no = no
staticCheck Γ (if .(erase t₂) thn t₁ els .(erase t)) | yes bool t₂ | no | (yes τ t) = no
staticCheck Γ (if .(erase t) thn t₁ els t₂) | yes bool t | no | _ = no

staticCheck Γ (if .(erase t) thn t₁ els t₂) | yes ✭ t with staticCheck Γ t₁ | staticCheck Γ t₂
staticCheck Γ (if .(erase t₂) thn .(erase t₁) els .(erase t)) | yes ✭ t₂ | (yes τ₁ t₁) | (yes τ₂ t) = yes (τ₁ ⊓ τ₂) (Tif t₂ t₁ t cons)
staticCheck Γ (if .(erase t₁) thn .(erase t) els t₂)          | yes ✭ t₁ | (yes τ t) | no = no
staticCheck Γ (if .(erase t₂) thn t₁ els .(erase t))          | yes ✭ t₂ | no | (yes τ t) = no
staticCheck Γ (if .(erase t) thn t₁ els t₂)                   | yes ✭ t | no | no = no

staticCheck Γ (if .(erase t) thn t₁ els t₂) | yes _ t = no
staticCheck Γ (if t thn t₁ els t₂) | no               = no

extractType : ∀ {n} {Γ : Ctx n} {t : Expr} → Check Γ t → GType
extractType (yes τ t) = τ
extractType no = err

-- Type Precision
data _⊑_ : GType → GType → Set where
    n⊑✭ : nat ⊑ ✭
    b⊑✭ : bool ⊑ ✭
    ⇒⊑ : ∀ (t₁ t₂ : GType) → (t₁ ⇒ t₂) ⊑ ✭
    n⊑n : nat ⊑ nat
    b⊑b : bool ⊑ bool
    ✭⊑✭ : ✭ ⊑ ✭
    app⊑ : ∀ (t₁ t₂ t₃ t₄ : GType) → t₁ ⊑ t₃ → t₂ ⊑ t₄ → (t₁ ⇒ t₃) ⊑ (t₃ ⇒ t₄)

-- Term Precision
data _≤_ : Expr → Expr → Set where
    n≤n : ∀ {n} → litNat n ≤ litNat n
    b≤b : ∀ {b} → litBool b ≤ litBool b
    n≤✭ : ∀ {n} → litNat n ≤ dyn
    b≤✭ : ∀ {b} → litBool b ≤ dyn
    d≤d : dyn ≤ dyn

ssG : ∀ {n} {Γ : Ctx n} {e₁ e₂ : Expr} → e₁ ≤ e₂ → extractType (staticCheck Γ e₁) ⊑ extractType (staticCheck Γ e₂)
ssG n≤n = n⊑n
ssG b≤b = b⊑b
ssG n≤✭ = n⊑✭
ssG b≤✭ = b⊑✭
ssG d≤d = ✭⊑✭


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

open import HoTT
open import homotopy.EilenbergMacLane

module cohomology.CupProduct.OnEM.InLowDegrees2 {i} {j} (G : AbGroup i) (H : AbGroup j) where

private
  module G = AbGroup G
  module H = AbGroup H
  module G⊗H = TensorProduct G H

open EMExplicit G⊗H.abgroup
open import cohomology.CupProduct.OnEM.InLowDegrees G H public
open import cohomology.CupProduct.OnEM.CommutativityInLowDegrees
open CP₁₁-comm G H

cp₁₁-embase-r : (x : EM₁ G.grp) → cp₁₁ x embase == [ north ]₂
cp₁₁-embase-r x = CP₁₁Comm.f x embase

module ∧-cp₁₁-Rec =
  SmashRec {X = ⊙EM₁ G.grp} {Y = ⊙EM₁ H.grp} {C = EM 2}
            cp₁₁
            [ north ]₂ [ north ]₂
            cp₁₁-embase-r
            (λ y → idp)

∧-cp₁₁ : ⊙EM₁ G.grp ∧ ⊙EM₁ H.grp → EM 2
∧-cp₁₁ = ∧-cp₁₁-Rec.f

⊙∧-cp₁₁ : ⊙EM₁ G.grp ⊙∧ ⊙EM₁ H.grp ⊙→ ⊙EM 2
⊙∧-cp₁₁ = ∧-cp₁₁-Rec.f , idp


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

open import Algebra
open import Relation.Unary
open import Relation.Binary hiding (Decidable)

module Data.FingerTree.Split.Intermediate
  {r m}
  (ℳ : Monoid r m)
  {s}
  {ℙ : Pred (Monoid.Carrier ℳ) s}
  (ℙ-resp : ℙ Respects (Monoid._≈_ ℳ))
  (ℙ? : Decidable ℙ)
  where

open import Relation.Nullary using (¬_; yes; no; Dec)
open import Level using (_⊔_)
open import Data.Product
open import Function
open import Data.List as List using (List; _∷_; [])

open import Data.FingerTree.Measures ℳ
open import Data.FingerTree.Structures ℳ
open import Data.FingerTree.Reasoning ℳ
open import Data.FingerTree.View ℳ using (deepₗ; deepᵣ)
open import Data.FingerTree.Cons ℳ using (listToTree)

open import Relation.Nullary using (Dec; yes; no)
open import Relation.Nullary.Decidable using (True; toWitness; False; toWitnessFalse)

open σ ⦃ ... ⦄

open Monoid ℳ renaming (Carrier to 𝓡)

open import Data.FingerTree.Relation.Binary.Reasoning.FasterInference.Setoid setoid

open import Data.FingerTree.Split.Point ℳ ℙ-resp ℙ?
open import Data.FingerTree.Split.StoredPredicate ℳ ℙ-resp ℙ?

open import Data.Empty.Irrelevant using (⊥-elim)

record Split′ (i : 𝓡) {a b} (Σ : Set a) (A : Set b) ⦃ _ : σ Σ ⦄ ⦃ _ : σ A ⦄ : Set (a ⊔ b ⊔ s) where
  constructor _∷⟨_⟩∷_[_]
  field
    left′  : Σ
    focus′ : A
    right′ : Σ
    .proof′ : i ∙ μ left′ ∣ μ focus′
open Split′ public
instance
  σ-Split′ : ∀  {a b} {Σ : Set a} {A : Set b} ⦃ _ : σ Σ ⦄ ⦃ _ : σ A ⦄ {i : 𝓡} → σ (Split′ i Σ A)
  μ ⦃ σ-Split′ {i = i} ⦄ (l ∷⟨ x ⟩∷ r [ _ ]) = i ∙ (μ l ∙ (μ x ∙ μ r))

infixl 2 _i≈[_]
_i≈[_] : ∀ {a b} {Σ : Set a} {A : Set b} ⦃ _ : σ Σ ⦄ ⦃ _ : σ A ⦄
          → ∀ {i xs}
          → μ⟨ Split′ i Σ A ⟩≈ (i ∙ xs)
          → ∀ {j}
          → i ≈ j → μ⟨ Split′ j Σ A ⟩≈ (j ∙ xs)
xs ∷⟨ x ⟩∷ ys [ p₁ ] ⇑[ p₂ ] i≈[ i≈ ] = xs ∷⟨ x ⟩∷ ys [ p₁ ≈◄⟅ ≪∙ i≈ ⟆ ] ⇑[ ≪∙ sym i≈ ⍮ p₂ ⍮ ≪∙ i≈ ]
{-# INLINE _i≈[_] #-}


------------------------------------------------------------------------
-- The Agda standard library
--
-- Code related to vector equality over propositional equality that
-- makes use of heterogeneous equality
------------------------------------------------------------------------

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

module Data.Vec.Relation.Binary.Equality.Propositional.WithK
  {a} {A : Set a} where

open import Data.Vec
open import Data.Vec.Relation.Binary.Equality.Propositional {A = A}
open import Relation.Binary.PropositionalEquality
open import Relation.Binary.HeterogeneousEquality as H using (_≅_)

≋⇒≅ : ∀ {m n} {xs : Vec A m} {ys : Vec A n} →
      xs ≋ ys → xs ≅ ys
≋⇒≅ p with length-equal p
... | refl = H.≡-to-≅ (≋⇒≡ p)


open import Common.Equality
open import Common.Prelude renaming (Nat to ℕ)

infixr 4 _,_
infix  4 ,_

record Σ (A : Set) (B : A → Set) : Set where
  constructor _,_
  field
    proj₁ : A
    proj₂ : B proj₁

open Σ

,_ : {A : Set} {B : A → Set} {x : A} → B x → Σ A B
, y = _ , y

should-be-accepted : Σ ℕ λ i → Σ ℕ λ j → Σ (i ≡ j) λ _ → Σ ℕ λ k → j ≡ k
should-be-accepted = 5 , , refl , , refl

_⊕_ : ℕ → ℕ → ℕ
_⊕_ = _+_

_↓ : ℕ → ℕ
_↓ = pred

infixl 6 _⊕_
infix  6 _↓

should-also-be-accepted : ℕ
should-also-be-accepted = 1 ⊕ 0 ↓ ⊕ 1 ↓ ⊕ 1 ⊕ 1 ↓

parses-correctly : should-also-be-accepted ≡ 1
parses-correctly = refl


open import Categories
open import Monads

module Monads.EM.Adjunction {a b}{C : Cat {a}{b}}(M : Monad C) where

open import Library

open import Functors
open import Adjunctions
open import Monads.EM M
open import Monads.EM.Functors M

open Cat C
open Fun
open Monad M
open Adj
open Alg
open AlgMorph

EMAdj : Adj C EM
EMAdj  = record {
  L = EML;
  R = EMR;
  left = λ f → comp (amor f) η;
  right = λ {X}{Y} f →
    record{amor = astr Y X f;
           ahom = λ {Z}{g} →
             proof
             comp (astr Y X f) (astr (OMap EML X) Z g)
             ≅⟨ sym (alaw2 Y) ⟩
             astr Y Z (comp (astr Y X f) g)
             ∎};
  lawa = λ {X}{Y}(f : AlgMorph (OMap EML X) Y) → AlgMorphEq (
    proof
    astr Y X (comp (amor f) η)
    ≅⟨ sym (ahom f) ⟩
    comp (amor f) (astr (OMap EML X) X η)
    ≡⟨⟩
    comp (amor f) (bind η)
    ≅⟨ cong (comp (amor f)) law1 ⟩
    comp (amor f) iden
    ≅⟨ idr ⟩
    amor f
    ∎);
  lawb = λ {X}{Y} f →
    proof
    comp (astr Y X f) η
    ≅⟨ sym (alaw1 Y) ⟩
    f
    ∎;
  natleft = λ{X}{X'}{Y}{Y'} f g h →
    proof
    comp (amor g) (comp (comp (amor h) η) f)
    ≅⟨ cong (comp (amor g)) ass ⟩
    comp (amor g) (comp (amor h) (comp η f))
    ≅⟨ cong (comp (amor g) ∘ comp (amor h)) (sym law2) ⟩
    comp (amor g) (comp (amor h) (comp (bind (comp η f)) η))
    ≅⟨ cong (comp (amor g)) (sym ass) ⟩
    comp (amor g) (comp (comp (amor h) (bind (comp η f))) η)
    ≅⟨ sym ass ⟩
    comp (comp (amor g) (comp (amor h) (bind (comp η f)))) η
    ∎;
  natright = λ{X}{X'}{Y}{Y'} f g h → AlgMorphEq (
    proof
    astr Y' X' (comp (amor g) (comp h f))
    ≅⟨ sym (ahom g) ⟩
    comp (amor g) (astr Y X' (comp h f))
    ≅⟨ cong (λ h → comp (amor g) (astr Y X' (comp h f))) (alaw1 Y) ⟩
    comp (amor g) (astr Y X' (comp (comp (astr Y X h) η) f))
    ≅⟨ cong (comp (amor g) ∘ astr Y X') ass ⟩
    comp (amor g) (astr Y X' (comp (astr Y X h) (comp η f)))
    ≅⟨ cong (comp (amor g)) (alaw2 Y) ⟩
    comp (amor g) (comp (astr Y X h) (bind (comp η f)))
    ∎)}


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

open import LogicalFormulae
open import Numbers.Naturals.Semiring
open import Numbers.Naturals.Order
open import Numbers.Naturals.Naturals
open import Numbers.Naturals.EuclideanAlgorithm
open import Semirings.Definition
open import Orders.Total.Definition

module Numbers.Modulo.ModuloFunction where

open TotalOrder ℕTotalOrder

private
  notBigger : (a : ℕ) {n : ℕ} → succ a +N n ≡ a → False
  notBigger (succ a) {n} pr = notBigger a {n} (succInjective pr)

  notBigger' : (a : ℕ) {n : ℕ} → succ a +N n ≡ n → False
  notBigger' (succ a) {succ n} pr rewrite Semiring.commutative ℕSemiring a (succ n) | Semiring.commutative ℕSemiring n a = notBigger' _ (succInjective pr)

mod : (n : ℕ) → .(pr : 0 <N n) → (a : ℕ) → ℕ
mod (succ n) 0<n a = divisionAlgResult.rem (divisionAlg (succ n) a)

abstract
  modIsMod : {n : ℕ} → .(pr : 0 <N n) → (a : ℕ) → a <N n → mod n pr a ≡ a
  modIsMod {succ n} 0<n a a<n with divisionAlg (succ n) a
  modIsMod {succ n} 0<n a a<n | record { quot = zero ; rem = rem ; pr = pr ; remIsSmall = remIsSmall ; quotSmall = quotSmall } rewrite multiplicationNIsCommutative n 0 = pr
  modIsMod {succ n} 0<n a (le x proof) | record { quot = succ quot ; rem = rem ; pr = pr ; remIsSmall = inl rem<sn ; quotSmall = quotSmall } = exFalso (notBigger a v)
    where
      t : ((succ (a +N x)) *N succ quot) +N rem ≡ a
      t rewrite Semiring.commutative ℕSemiring a x = transitivity (applyEquality (λ i → (i *N succ quot) +N rem) proof) pr
      u : (succ quot *N succ (a +N x)) +N rem ≡ a
      u = transitivity (applyEquality (_+N rem) (multiplicationNIsCommutative (succ quot) _)) t
      v : succ a +N ((x +N quot *N succ (a +N x)) +N rem) ≡ a
      v = transitivity (applyEquality succ (transitivity (Semiring.+Associative ℕSemiring a _ rem) (applyEquality (_+N rem) (Semiring.+Associative ℕSemiring a x _)))) u

  mod<N : {n : ℕ} → .(pr : 0 <N n) → (a : ℕ) → (mod n pr a) <N n
  mod<N {succ n} pr a with divisionAlg (succ n) a
  mod<N {succ n} 0<n a | record { quot = quot ; rem = rem ; pr = pr ; remIsSmall = inl x ; quotSmall = quotSmall } = x

  modIdempotent : {n : ℕ} → .(pr : 0 <N n) → (a : ℕ) → mod n pr (mod n pr a) ≡ mod n pr a
  modIdempotent 0<n a = modIsMod 0<n (mod _ 0<n a) (mod<N 0<n a)

  modAnd+n : {n : ℕ} → .(pr : 0 <N n) → (a : ℕ) → mod n pr (n +N a) ≡ mod n pr a
  modAnd+n {succ n} 0<sn a with divisionAlg (succ n) (succ n +N a)
  modAnd+n {succ n} 0<sn a | record { quot = 0 ; rem = rem ; pr = pr ; remIsSmall = inl remIsSmall ; quotSmall = inl q } rewrite multiplicationNIsCommutative n 0 | pr = exFalso (notBigger (succ n) (transitivity (applyEquality succ (transitivity (applyEquality succ (Semiring.+Associative ℕSemiring n a _)) (Semiring.commutative ℕSemiring (succ (n +N a)) _))) (_<N_.proof remIsSmall)))
  modAnd+n {succ n} 0<sn a | record { quot = succ quot ; rem = rem ; pr = pr ; remIsSmall = inl remIsSmall ; quotSmall = inl q } = modIsUnique (record { quot = quot ; rem = rem ; pr = t ; remIsSmall = inl remIsSmall ; quotSmall = inl q }) (divisionAlg (succ n) a)
    where
      g : succ n +N ((quot *N succ n) +N rem) ≡ succ n +N a
      g = transitivity (Semiring.+Associative ℕSemiring (succ n) _ rem) (transitivity (applyEquality (_+N rem) (multiplicationNIsCommutative (succ quot) _)) pr)
      h : (quot *N succ n) +N rem ≡ a
      h = canSubtractFromEqualityLeft g
      t : (succ n *N quot) +N rem ≡ a
      t = transitivity (applyEquality (_+N rem) (multiplicationNIsCommutative (succ n) quot)) h

  modExtracts : {n : ℕ} → .(pr : 0 <N n) → (a b : ℕ) → mod n pr (a +N b) ≡ mod n pr (mod n pr a +N mod n pr b)
  modExtracts {succ n} 0<sn a b with divisionAlg (succ n) a
  modExtracts {succ n} 0<sn a b | record { quot = sn/a ; rem = sn%a ; pr = prA ; remIsSmall = remIsSmallA ; quotSmall = quotSmallA } with divisionAlg (succ n) b
  modExtracts {succ n} 0<sn a b | record { quot = sn/a ; rem = sn%a ; pr = prA ; remIsSmall = remIsSmallA ; quotSmall = quotSmallA } | record { quot = sn/b ; rem = sn%b ; pr = prB ; remIsSmall = remIsSmallB ; quotSmall = quotSmallB } with divisionAlg (succ n) (a +N b)
  modExtracts {succ n} 0<sn a b | record { quot = sn/a ; rem = sn%a ; pr = prA ; remIsSmall = remIsSmallA ; quotSmall = quotSmallA } | record { quot = sn/b ; rem = sn%b ; pr = prB ; remIsSmall = remIsSmallB ; quotSmall = quotSmallB } | record { quot = sn/a+b ; rem = sn%a+b ; pr = prA+B ; remIsSmall = remIsSmallA+B ; quotSmall = quotSmallA+B } with divisionAlg (succ n) (sn%a +N sn%b)
  modExtracts {succ n} 0<sn a b | record { quot = sn/a ; rem = sn%a ; pr = prA ; remIsSmall = inl sn%a<sn ; quotSmall = inl _ } | record { quot = sn/b ; rem = sn%b ; pr = prB ; remIsSmall = inl sn%b<sn ; quotSmall = inl _ } | record { quot = sn/a+b ; rem = sn%a+b ; pr = prA+B ; remIsSmall = inl sn%a+b<sn ; quotSmall = inl _ } | record { quot = quot ; rem = rem ; pr = pr ; remIsSmall = inl remIsSmall ; quotSmall = inl _ } = equalityCommutative whoa
    where
      t : ((succ n *N sn/a) +N sn%a) +N ((succ n *N sn/b) +N sn%b) ≡ a +N b
      t = +NWellDefined prA prB
      t' : (succ n *N (sn/a +N sn/b)) +N (sn%a +N sn%b) ≡ a +N b
      t' = transitivity (transitivity (Semiring.+Associative ℕSemiring (succ n *N (sn/a +N sn/b)) sn%a sn%b) (transitivity (applyEquality (_+N sn%b) (transitivity (transitivity (applyEquality (_+N sn%a) (transitivity (applyEquality (succ n *N_) (Semiring.commutative ℕSemiring sn/a sn/b)) (Semiring.+DistributesOver* ℕSemiring (succ n) sn/b sn/a))) (equalityCommutative (Semiring.+Associative ℕSemiring (succ n *N sn/b) (succ n *N sn/a) sn%a))) (Semiring.commutative ℕSemiring (succ n *N sn/b) ((succ n *N sn/a) +N sn%a)))) (equalityCommutative (Semiring.+Associative ℕSemiring ((succ n *N sn/a) +N sn%a) (succ n *N sn/b) sn%b)))) t
      thing : ((succ n) *N quot) +N rem ≡ sn%a +N sn%b
      thing = pr
      t'' : (succ n *N ((sn/a +N sn/b) +N quot)) +N rem ≡ a +N b
      t'' rewrite Semiring.+DistributesOver* ℕSemiring (succ n) (sn/a +N sn/b) quot | equalityCommutative (Semiring.+Associative ℕSemiring (succ n *N (sn/a +N sn/b)) (succ n *N quot) rem) | thing = t'
      whoa : rem ≡ sn%a+b
      whoa = modUniqueLemma _ _ remIsSmall sn%a+b<sn (transitivity t'' (equalityCommutative prA+B))

  modAddition : {n : ℕ} → .(pr : 0 <N n) → {a b : ℕ} → (a <N n) → (b <N n) → (a +N b ≡ mod n pr (a +N b)) || (a +N b ≡ n +N mod n pr (a +N b))
  modAddition {succ n} 0<sn {a} {b} a<n b<n with divisionAlg (succ n) (a +N b)
  modAddition {succ n} 0<sn {a} {b} a<n b<n | record { quot = zero ; rem = rem ; pr = pr ; remIsSmall = inl rem<sn ; quotSmall = inl _ } = inl (equalityCommutative (transitivity (applyEquality (_+N rem) (multiplicationNIsCommutative 0 n)) pr))
  modAddition {succ n} 0<sn {a} {b} a<n b<n | record { quot = succ zero ; rem = rem ; pr = pr ; remIsSmall = inl rem<sn ; quotSmall = inl _ } = inr (transitivity (equalityCommutative pr) (applyEquality (λ i → succ i +N rem) (transitivity (multiplicationNIsCommutative n 1) (Semiring.sumZeroRight ℕSemiring n))))
  modAddition {succ n} 0<sn {a} {b} (le x prA) (le y prB) | record { quot = succ (succ quot) ; rem = 0 ; pr = pr ; remIsSmall = inl rem<sn ; quotSmall = inl _ } rewrite Semiring.sumZeroRight ℕSemiring (succ n *N succ (succ quot)) | multiplicationNIsCommutative (succ n) (succ (succ quot)) = exFalso (notBigger' ((x +N succ y) +N (quot *N succ n)) u)
    where
      t : ((succ x +N a) +N succ (y +N b)) +N (quot *N succ n) ≡ a +N b
      t = transitivity (transitivity (applyEquality (_+N quot *N succ n) (+NWellDefined prA prB)) (equalityCommutative (Semiring.+Associative ℕSemiring (succ n) (succ n) (quot *N succ n)))) pr
      u : ((succ x +N succ y) +N (quot *N succ n)) +N (a +N b) ≡ a +N b
      u rewrite Semiring.commutative ℕSemiring ((x +N succ y) +N quot *N succ n) (a +N b) | Semiring.+Associative ℕSemiring (succ a +N b) (x +N succ y) (quot *N succ n) = transitivity (applyEquality (λ i → succ i +N quot *N succ n) (transitivity (Semiring.commutative ℕSemiring (a +N b) _) (transitivity (equalityCommutative (Semiring.+Associative ℕSemiring x (succ y) _)) (transitivity (applyEquality (x +N_) (transitivity (Semiring.+Associative ℕSemiring (succ y) a b) (transitivity (applyEquality (_+N b) (Semiring.commutative ℕSemiring _ a)) (equalityCommutative (Semiring.+Associative ℕSemiring a (succ y) b))))) (Semiring.+Associative ℕSemiring x a _))))) t
  modAddition {succ n} 0<sn {a} {b} a<n b<n | record { quot = succ (succ quot) ; rem = succ rem ; pr = pr ; remIsSmall = inl rem<sn ; quotSmall = inl _ } = exFalso (irreflexive (<Transitive u t))
    where
      t : (succ n *N succ (succ quot) +N succ rem) <N succ n +N succ n
      t = identityOfIndiscernablesLeft _<N_ (addStrongInequalities a<n b<n) (equalityCommutative pr)
      lemm : {a c d : ℕ} → (a +N a) <N (a *N succ (succ c)) +N succ d
      lemm {a} {c} {d} = le (a *N c +N d) f
        where
          f : succ ((a *N c +N d) +N (a +N a)) ≡ a *N succ (succ c) +N succ d
          f rewrite multiplicationNIsCommutative a (succ (succ c)) | Semiring.+Associative ℕSemiring a a (c *N a) | multiplicationNIsCommutative c a | Semiring.commutative ℕSemiring (a *N c +N d) (a +N a) | Semiring.+Associative ℕSemiring (a +N a) (a *N c) d | Semiring.commutative ℕSemiring ((a +N a) +N a *N c) (succ d) = applyEquality succ (Semiring.commutative ℕSemiring _ d)
      u : (succ n +N succ n) <N (succ n *N succ (succ quot) +N succ rem)
      u = lemm {succ n}

  modN : {n : ℕ} → .(0<n : 0 <N n) → mod n 0<n n ≡ 0
  modN {succ n} 0<n = modIsUnique (divisionAlg (succ n) (succ n)) record { quot = 1 ; rem = zero ; pr = ans ; remIsSmall = inl (le n (Semiring.sumZeroRight ℕSemiring (succ n))) ; quotSmall = inl (le n (Semiring.sumZeroRight ℕSemiring (succ n))) }
    where
      ans : succ (n *N 1 +N 0) ≡ succ n
      ans rewrite multiplicationNIsCommutative n 1 | Semiring.sumZeroRight ℕSemiring n | Semiring.sumZeroRight ℕSemiring n = refl


module Data.BinaryTree where

import      Lvl
open import Type

private variable ℓ ℓᵢ ℓₗ ℓₙ ℓₒ : Lvl.Level
private variable N N₁ N₂ L T A B : Type{ℓ}

data BinaryTree (L : Type{ℓₗ}) (N : Type{ℓₙ}) : Type{ℓₗ Lvl.⊔ ℓₙ} where
  Leaf : L → BinaryTree L N
  Node : N → BinaryTree L N → BinaryTree L N → BinaryTree L N

elim : (P : BinaryTree L N → Type{ℓ}) → ((l : L) → P(Leaf l)) → (∀{tl tr} → (n : N) → (pl : P(tl)) → (pr : P(tr)) → P(Node n tl tr)) → ((t : BinaryTree L N) → P(t))
elim P pl pn (Leaf l)     = pl l
elim P pl pn (Node n l r) = pn n (elim P pl pn l) (elim P pl pn r)

open import Data using (Unit ; <>)
open import Data.Boolean
open import Data.Option
open import Data.Tuple using (_⨯_ ; _,_)
open import Functional as Fn using (_$_)
open import Numeral.Natural
open import Numeral.Natural.Function
open import Numeral.Natural.Oper hiding (_^_)

emptyTree : BinaryTree{ℓₗ} Unit N
emptyTree = Leaf <>

singleton : N → BinaryTree{ℓₗ} Unit N
singleton a = Node a emptyTree emptyTree

isLeaf : BinaryTree L N → Bool
isLeaf (Leaf _)     = 𝑇
isLeaf (Node _ _ _) = 𝐹

isNode : BinaryTree L N → Bool
isNode (Leaf _)     = 𝐹
isNode (Node _ _ _) = 𝑇

root : BinaryTree L N → Option(N)
root (Leaf _)     = None
root (Node a _ _) = Some a

flip : BinaryTree L N → BinaryTree L N
flip (Leaf l)     = Leaf l
flip (Node n l r) = Node n (flip r) (flip l)

foldPreOrderDepthFirst : (L → T → T) → (N → T → T) → T → BinaryTree L N → T
foldPreOrderDepthFirst(_▫ₗ_)(_▫ₙ_) id (Leaf a)     = a ▫ₗ id
foldPreOrderDepthFirst(_▫ₗ_)(_▫ₙ_) id (Node a l r) =
  a ▫ₙ_                                         $
  Fn.swap(foldPreOrderDepthFirst(_▫ₗ_)(_▫ₙ_)) l $
  Fn.swap(foldPreOrderDepthFirst(_▫ₗ_)(_▫ₙ_)) r $
  id

foldInOrderDepthFirst : (L → T → T) → (N → T → T) → T → BinaryTree L N → T
foldInOrderDepthFirst(_▫ₗ_)(_▫ₙ_) id (Leaf a)     = a ▫ₗ id
foldInOrderDepthFirst(_▫ₗ_)(_▫ₙ_) id (Node a l r) =
  Fn.swap(foldInOrderDepthFirst(_▫ₗ_)(_▫ₙ_)) l $
  a ▫ₙ_                                        $
  Fn.swap(foldInOrderDepthFirst(_▫ₗ_)(_▫ₙ_)) r $
  id

foldPostOrderDepthFirst : (L → T → T) → (N → T → T) → T → BinaryTree L N → T
foldPostOrderDepthFirst(_▫ₗ_)(_▫ₙ_) id (Leaf a)     = a ▫ₗ id
foldPostOrderDepthFirst(_▫ₗ_)(_▫ₙ_) id (Node a l r) =
  Fn.swap(foldPostOrderDepthFirst(_▫ₗ_)(_▫ₙ_)) l $
  Fn.swap(foldPostOrderDepthFirst(_▫ₗ_)(_▫ₙ_)) r $
  a ▫ₙ_                                          $
  id

foldReversedPreOrderDepthFirst : (L → T → T) → (N → T → T) → T → BinaryTree L N → T
foldReversedPreOrderDepthFirst(_▫ₗ_)(_▫ₙ_) id (Leaf a)     = a ▫ₗ id
foldReversedPreOrderDepthFirst(_▫ₗ_)(_▫ₙ_) id (Node a l r) =
  a ▫ₙ_                                                 $
  Fn.swap(foldReversedPreOrderDepthFirst(_▫ₗ_)(_▫ₙ_)) r $
  Fn.swap(foldReversedPreOrderDepthFirst(_▫ₗ_)(_▫ₙ_)) l $
  id

foldReversedInOrderDepthFirst : (L → T → T) → (N → T → T) → T → BinaryTree L N → T
foldReversedInOrderDepthFirst(_▫ₗ_)(_▫ₙ_) id (Leaf a)     = a ▫ₗ id
foldReversedInOrderDepthFirst(_▫ₗ_)(_▫ₙ_) id (Node a l r) =
  Fn.swap(foldReversedInOrderDepthFirst(_▫ₗ_)(_▫ₙ_)) r $
  a ▫ₙ_                                                $
  Fn.swap(foldReversedInOrderDepthFirst(_▫ₗ_)(_▫ₙ_)) l $
  id

foldReversedPostOrderDepthFirst : (L → T → T) → (N → T → T) → T → BinaryTree L N → T
foldReversedPostOrderDepthFirst(_▫ₗ_)(_▫ₙ_) id (Leaf a)     = a ▫ₗ id
foldReversedPostOrderDepthFirst(_▫ₗ_)(_▫ₙ_) id (Node a l r) =
  Fn.swap(foldReversedPostOrderDepthFirst(_▫ₗ_)(_▫ₙ_)) r $
  Fn.swap(foldReversedPostOrderDepthFirst(_▫ₗ_)(_▫ₙ_)) l $
  a ▫ₙ_                                                  $
  id

-- The size is the number of nodes in the tree.
size : BinaryTree L N → ℕ
size (Leaf _)     = 𝟎
size (Node _ l r) = 𝐒(size(l) + size(r))

-- The number of leaves in the tree.
numberOfLeaves : BinaryTree L N → ℕ
numberOfLeaves (Leaf _)     = 1
numberOfLeaves (Node _ l r) = numberOfLeaves(l) + numberOfLeaves(r)

-- The height is the length of the longest path from the root.
height : BinaryTree L N → ℕ
height (Leaf _)     = 𝟎
height (Node _ l r) = 𝐒(max (height l) (height r))

open import Data.Boolean.Operators
open        Data.Boolean.Operators.Programming

isFull : BinaryTree L N → Bool
isFull (Leaf _)                               = 𝑇
isFull (Node _ (Leaf _)        (Leaf _))      = 𝑇
isFull (Node _ (Node _ _ _)    (Leaf _))      = 𝐹
isFull (Node _ (Leaf _)        (Node _ _ _))  = 𝐹
isFull (Node _ l@(Node _ _ _) r@(Node _ _ _)) = isFull l && isFull r

open import Data.List
import      Data.List.Functions as List

treesOfDepth : ℕ → BinaryTree L N → List(BinaryTree L N)
treesOfDepth 𝟎      tree         = tree ⊰ ∅
treesOfDepth (𝐒(_)) (Leaf _)     = ∅
treesOfDepth (𝐒(n)) (Node _ l r) = (treesOfDepth n l) List.++ (treesOfDepth n r)


{-# OPTIONS --copatterns #-}

open import Common.Equality
open import Common.Product

id : {A : Set} → A → A
id x = x

record Functor (F : Set → Set) : Set₁ where
  field
    map : ∀ {A B} → (A → B) → F A → F B
    map-id : ∀ {A}{x : F A} → map id x ≡ x
open Functor

test : {C : Set} → Functor (_×_ C)
map    test f (c , a) = c , f a
map-id test           = refl -- : map test id x ≡ x
  -- needs to match against record constructor
  -- x/(c,a) = proj₁ x / c, proj₂ x / a