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

module Lecture5 where

open import Lecture4 public

-- Section 5.1 Homotopies
_~_ : {i j : Level} {A : UU i} {B : A → UU j} (f g : (x : A) → B x) → UU (i ⊔ j)
f ~ g = (x : _) → Id (f x) (g x)

htpy-refl : {i j : Level} {A : UU i} {B : A → UU j} (f : (x : A) → B x) → f ~ f
htpy-refl f x = refl

htpy-inv : {i j : Level} {A : UU i} {B : A → UU j} {f g : (x : A) → B x} → (f ~ g) → (g ~ f)
htpy-inv H x = inv (H x)

htpy-concat : {i j : Level} {A : UU i} {B : A → UU j} {f : (x : A) → B x} (g : (x : A) → B x) {h : (x : A) → B x} → (f ~ g) → (g ~ h) → (f ~ h)
htpy-concat g H K x = concat _ (H x) (K x)

htpy-assoc : {i j : Level} {A : UU i} {B : A → UU j} {f g h k : (x : A) → B x} → (H : f ~ g) → (K : g ~ h) → (L : h ~ k) → htpy-concat _ H (htpy-concat _ K L) ~ htpy-concat _ (htpy-concat _ H K) L
htpy-assoc H K L x = assoc (H x) (K x) (L x)

htpy-left-unit : {i j : Level} {A : UU i} {B : A → UU j} {f g : (x : A) → B x} (H : f ~ g) → (htpy-concat _ (htpy-refl f) H) ~ H
htpy-left-unit H x = left-unit (H x)

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

htpy-left-inv : {i j : Level} {A : UU i} {B : A → UU j} {f g : (x : A) → B x} (H : f ~ g) → htpy-concat _ (htpy-inv H) H ~ htpy-refl g
htpy-left-inv H x = left-inv (H x)

htpy-right-inv : {i j : Level} {A : UU i} {B : A → UU j} {f g : (x : A) → B x} (H : f ~ g) → htpy-concat _ H (htpy-inv H) ~ htpy-refl f
htpy-right-inv H x = right-inv (H x)

htpy-left-whisk : {i j k : Level} {A : UU i} {B : UU j} {C : UU k} (h : B → C) {f g : A → B} → (f ~ g) → ((h ∘ f) ~ (h ∘ g))
htpy-left-whisk h H x = ap h (H x)

htpy-right-whisk : {i j k : Level} {A : UU i} {B : UU j} {C : UU k} {g h : B → C} (H : g ~ h) (f : A → B) → ((g ∘ f) ~ (h ∘ f))
htpy-right-whisk H f x = H (f x)

-- Section 5.2 Bi-invertible maps
sec : {i j : Level} {A : UU i} {B : UU j} (f : A → B) → UU (i ⊔ j)
sec {_} {_} {A} {B} f = Σ (B → A) (λ g → (f ∘ g) ~ id)

retr : {i j : Level} {A : UU i} {B : UU j} (f : A → B) → UU (i ⊔ j)
retr {_} {_} {A} {B} f = Σ (B → A) (λ g → (g ∘ f) ~ id)

_retract-of_ : {i j : Level} → UU i → UU j → UU (i ⊔ j)
A retract-of B = Σ (A → B) retr

is-equiv : {i j : Level} {A : UU i} {B : UU j} (f : A → B) → UU (i ⊔ j)
is-equiv f = sec f × retr f

-- this equivalence symbol is \simeq
_≃_ : {i j : Level} (A : UU i) (B : UU j) → UU (i ⊔ j)
A ≃ B = Σ (A → B) (λ f → is-equiv f)

eqv-sec : {i j : Level} {A : UU i} {B : UU j} {f : A → B} → is-equiv f → sec f
eqv-sec e = pr1 e

eqv-secf : {i j : Level} {A : UU i} {B : UU j} {f : A → B} → is-equiv f → B → A
eqv-secf e = pr1 (eqv-sec e)

eqv-sech : {i j : Level} {A : UU i} {B : UU j} {f : A → B} → (e : is-equiv f) → ((f ∘ eqv-secf e) ~ id)
eqv-sech e = pr2 (eqv-sec e)

eqv-retr : {i j : Level} {A : UU i} {B : UU j} {f : A → B} → is-equiv f → retr f
eqv-retr e = pr2 e

eqv-retrf : {i j : Level} {A : UU i} {B : UU j} {f : A → B} → is-equiv f → B → A
eqv-retrf e = pr1 (eqv-retr e)

eqv-retrh : {i j : Level} {A : UU i} {B : UU j} {f : A → B} → (e : is-equiv f) → (((eqv-retrf e) ∘ f) ~ id)
eqv-retrh e = pr2 (eqv-retr e)

is-invertible : {i j : Level} {A : UU i} {B : UU j} (f : A → B) → UU (i ⊔ j)
is-invertible {_} {_} {A} {B} f = Σ (B → A) (λ g → ((f ∘ g) ~ id) × ((g ∘ f) ~ id))

is-equiv-is-invertible : {i j : Level} {A : UU i} {B : UU j} {f : A → B} → is-invertible f → is-equiv f
is-equiv-is-invertible (dpair g (dpair H K)) = pair (dpair g H) (dpair g K)

htpy-secf-retrf : {i j : Level} {A : UU i} {B : UU j} {f : A → B} (e : is-equiv f) → (eqv-secf e ~ eqv-retrf e)
htpy-secf-retrf {_} {_} {_} {_} {f} (dpair (dpair g issec) (dpair h isretr)) =
  htpy-concat (h ∘ (f ∘ g)) (htpy-inv (htpy-right-whisk isretr g)) (htpy-left-whisk h issec)

is-invertible-is-equiv : {i j : Level} {A : UU i} {B : UU j} {f : A → B} → is-equiv f → is-invertible f
is-invertible-is-equiv {_} {_} {_} {_} {f} (dpair (dpair g issec) (dpair h isretr)) = dpair g (pair issec (htpy-concat (h ∘ f) (htpy-right-whisk (htpy-secf-retrf (dpair (dpair g issec) (dpair h isretr))) f) isretr))

inv-is-equiv : {i j : Level} {A : UU i} {B : UU j} {f : A → B} → is-equiv f → B → A
inv-is-equiv E = pr1 (is-invertible-is-equiv E)

issec-inv-is-equiv : {i j : Level} {A : UU i} {B : UU j} {f : A → B} → (E : is-equiv f) → (f ∘ (inv-is-equiv E)) ~ id
issec-inv-is-equiv E = pr1 (pr2 (is-invertible-is-equiv E))

isretr-inv-is-equiv : {i j : Level} {A : UU i} {B : UU j} {f : A → B} → (E : is-equiv f) → ((inv-is-equiv E) ∘ f) ~ id
isretr-inv-is-equiv E = pr2 (pr2 (is-invertible-is-equiv E))

is-equiv-inv-is-equiv : {i j : Level} {A : UU i} {B : UU j} {f : A → B} → (E : is-equiv f) → is-equiv (inv-is-equiv E)
is-equiv-inv-is-equiv {_} {_} {_} {_} {f} E =
  pair (dpair f (isretr-inv-is-equiv E)) (dpair f (issec-inv-is-equiv E))

is-equiv-id : {i : Level} (A : UU i) → is-equiv (id {_} {A})
is-equiv-id A = pair (dpair id (htpy-refl id)) (dpair id (htpy-refl id))

inv-Pi-swap : {i j k : Level} {A : UU i} {B : UU j} (C : A → B → UU k) →
  ((y : B) (x : A) → C x y) → ((x : A) (y : B) → C x y)
inv-Pi-swap C g x y = g y x

is-equiv-swap : {i j k : Level} {A : UU i} {B : UU j} (C : A → B → UU k) → is-equiv (Pi-swap {_} {_} {_} {A} {B} {C})
is-equiv-swap C = pair (dpair (inv-Pi-swap C) (htpy-refl id)) (dpair (inv-Pi-swap C) (htpy-refl id))

-- Section 5.3 The identity type of a Σ-type

eq-pair' : {i j : Level} {A : UU i} {B : A → UU j} (s t : Σ A B) → (Σ (Id (pr1 s) (pr1 t)) (λ α → Id (tr B α (pr2 s)) (pr2 t))) → Id s t
eq-pair' (dpair x y) (dpair x' y') (dpair refl refl) = refl

eq-pair : {i j : Level} {A : UU i} {B : A → UU j} {s t : Σ A B} → (Σ (Id (pr1 s) (pr1 t)) (λ α → Id (tr B α (pr2 s)) (pr2 t))) → Id s t
eq-pair {i} {j} {A} {B} {s} {t} = eq-pair' s t

pair-eq' : {i j : Level} {A : UU i} {B : A → UU j} (s t : Σ A B) → (Id s t) → Σ (Id (pr1 s) (pr1 t)) (λ α → Id (tr B α (pr2 s)) (pr2 t))
pair-eq' s t refl = dpair refl refl

pair-eq  : {i j : Level} {A : UU i} {B : A → UU j} {s t : Σ A B} → (Id s t) → Σ (Id (pr1 s) (pr1 t)) (λ α → Id (tr B α (pr2 s)) (pr2 t))
pair-eq {i} {j} {A} {B} {s} {t} = pair-eq' s t

isretr-pair-eq : {i j : Level} {A : UU i} {B : A → UU j} (s t : Σ A B) → (((pair-eq' s t) ∘ (eq-pair' s t)) ~ id)
isretr-pair-eq (dpair x y) (dpair x' y') (dpair refl refl) = refl

issec-pair-eq : {i j : Level} {A : UU i} {B : A → UU j} (s t : Σ A B) → (((eq-pair' s t) ∘ (pair-eq' s t)) ~ id)
issec-pair-eq (dpair x y) .(dpair x y) refl = refl

is-equiv-eq-pair' : {i j : Level} {A : UU i} {B : A → UU j} (s t : Σ A B) → is-equiv (eq-pair' s t)
is-equiv-eq-pair' s t = pair (dpair (pair-eq' s t) (issec-pair-eq s t)) (dpair (pair-eq' s t) (isretr-pair-eq s t))

is-equiv-eq-pair : {i j : Level} {A : UU i} {B : A → UU j} (s t : Σ A B) → is-equiv (eq-pair {i} {j} {A} {B} {s} {t})
is-equiv-eq-pair = is-equiv-eq-pair'

-- Exercises below

-- Exercise 5.1

singleton-ind-const-htpy : {i : Level} {A : UU i} (a : A) → (const 𝟙 A a) ~ (ind-unit a)
singleton-ind-const-htpy a star = refl

-- Exercise 5.2
ap-const-is-const-refl : {i j : Level} {A : UU i} {B : UU j} (b : B) {x y : A} → (ap (const A B b)) ~ (const (Id x y) (Id b b) refl)
ap-const-is-const-refl b refl = refl

-- Exercise 5.3
inv-inv-htpy : {i : Level} {A : UU i} {x y : A} (p : Id x y) → Id (inv (inv p)) p
inv-inv-htpy refl = refl

is-equiv-inv : {i : Level} {A : UU i} (x y : A) →
  is-equiv (λ (p : Id x y) → inv p)
is-equiv-inv x y = pair (dpair inv inv-inv-htpy) (dpair inv inv-inv-htpy)

concat-inv-left-htpy : {i : Level} {A : UU i} {x y z : A} (p : Id x y) → (q : Id y z) → Id ((inv p) · (p · q)) q
concat-inv-left-htpy refl q = refl

concat-inv-right-htpy : {i : Level} {A : UU i} {x y z : A} (p : Id x y) → (r : Id x z) → Id (p · ((inv p) · r)) r
concat-inv-right-htpy refl r = refl

is-equiv-concat : {i : Level} {A : UU i} (x y z : A) (p : Id x y) → (is-equiv λ (q : Id y z) → p · q)
is-equiv-concat x y z p = pair (dpair (λ (r : Id x z) → ((inv p) · r)) (λ (r : Id x z) → concat-inv-right-htpy p r)) (dpair (λ (r : Id x z) → (inv p) · r) (λ (q : Id y z) → concat-inv-left-htpy p q))

tr-inv-htpy : {i j : Level} {A : UU i} {B : A → UU j} {x y : A} (p : Id x y) (b : B x) → Id (tr B (inv p) (tr B p b)) b
tr-inv-htpy refl b = refl

tr-inv-htpy' : {i j : Level} {A : UU i} {B : A → UU j} {x y : A} (p : Id x y) (b : B y) → Id (tr B p (tr B (inv p) b)) b
tr-inv-htpy' refl b = refl

is-equiv-tr : {i j : Level} {A : UU i} (B : A → UU j) {x y : A} (p : Id x y) → is-equiv (λ b → (tr B p b))
is-equiv-tr B p = pair (dpair (tr B (inv p)) (tr-inv-htpy' p)) (dpair (tr B (inv p)) (tr-inv-htpy p))

-- Exercise 5.4
is-equiv-htpy : {i j : Level} {A : UU i} {B : UU j} {f g : A → B} →
  f ~ g → is-equiv g → is-equiv f
is-equiv-htpy H (dpair (dpair gs issec) (dpair gr isretr)) =
  pair
    (dpair gs (htpy-concat _ (htpy-right-whisk H gs) issec))
    (dpair gr (htpy-concat (gr ∘ _) (htpy-left-whisk gr H) isretr))

-- Exercise 5.5
is-equiv-comp : {i j k : Level} {A : UU i} {B : UU j} {X : UU k} {f : A → X} {g : B → X} {h : A → B} (H : f ~ (g ∘ h)) → is-equiv h → is-equiv g → is-equiv f
is-equiv-comp {i} {j} {k} {A} {B} {X} {f} {g} {h} H (dpair (dpair hs hs-issec) (dpair hr hr-isretr))
  (dpair (dpair gs gs-issec) (dpair gr gr-isretr)) =
  is-equiv-htpy H
    (pair
      (dpair (hs ∘ gs)
        (htpy-concat (g ∘ gs)
          (htpy-left-whisk g (htpy-right-whisk hs-issec gs)) gs-issec))
      (dpair (hr ∘ gr)
        (htpy-concat (hr ∘ h)
          (htpy-left-whisk hr (htpy-right-whisk gr-isretr h)) hr-isretr)))

-- Exercise 5.6
not-not-x-is-x : (x : bool) → Id (not (not x)) x
not-not-x-is-x true = refl
not-not-x-is-x false = refl

is-equiv-not : is-equiv not
is-equiv-not = pair (dpair not not-not-x-is-x) (dpair not not-not-x-is-x)

path-true-to-false-is-contra : (Id true false) → 𝟘
path-true-to-false-is-contra p = tr (ind-bool 𝟙 𝟘) p star

same-image-not-equiv : (f : bool → bool) → (p : Id (f true) (f false)) → (is-equiv f) → 𝟘
same-image-not-equiv f p (dpair f-is-sec (dpair g htpy)) = path-true-to-false-is-contra (true ==⟨ inv (htpy true) ⟩ g(f(true)) ==⟨ (ap g p) ⟩ g(f(false)) ==⟨ htpy false ⟩ false ==∎)
-- NOTE: ap g p is a path from g(f(true)) to g(f(false))

-- Exercise 5.7
zpred-zsucc-x-is-x : (x : ℤ) → Id (Zpred (Zsucc x)) x
zpred-zsucc-x-is-x (inl Nzero) = refl
zpred-zsucc-x-is-x (inl (Nsucc x)) = refl
zpred-zsucc-x-is-x (inr (inl star)) = refl
zpred-zsucc-x-is-x (inr (inr x)) = refl

zsucc-zpred-x-is-x : (x : ℤ) → Id (Zsucc (Zpred x)) x
zsucc-zpred-x-is-x (inl x) = refl
zsucc-zpred-x-is-x (inr (inl star)) = refl
zsucc-zpred-x-is-x (inr (inr Nzero)) = refl
zsucc-zpred-x-is-x (inr (inr (Nsucc x))) = refl

is-equiv-zsucc : is-equiv Zsucc
is-equiv-zsucc = dpair (dpair Zpred zsucc-zpred-x-is-x) (dpair Zpred zpred-zsucc-x-is-x)

-- Exercise 5.8
-- construct equivalences A + B <-> B + A and A x B <-> B x A
coprod-rev : {i j : Level} (A : UU i) (B : UU j) → (coprod A B) → (coprod B A)
coprod-rev A B (inl a) = inr a
coprod-rev A B (inr b) = inl b

coprod-rev-squared-is-id : {i j : Level} (A : UU i) (B : UU j) → (coprod-rev B A ∘ coprod-rev A B) ~ id
coprod-rev-squared-is-id A B (inl a) = refl
coprod-rev-squared-is-id A B (inr b) = refl

is-equiv-coprod-rev : {i j : Level} (A : UU i) (B : UU j) → is-equiv (coprod-rev A B)
is-equiv-coprod-rev A B = dpair (dpair (coprod-rev B A) (coprod-rev-squared-is-id B A)) (dpair (coprod-rev B A) (coprod-rev-squared-is-id A B))

prod-rev : {i j : Level} (A : UU i) (B : UU j) → (prod A B) → (prod B A)
prod-rev A B x = pair (pr2 x) (pr1 x)

prod-rev-squared-is-id : {i j : Level} (A : UU i) (B : UU j) → (prod-rev B A ∘ prod-rev A B) ~ id
prod-rev-squared-is-id A B (dpair a b) = refl

is-equiv-prod-rev : {i j : Level} (A : UU i) (B : UU j) → is-equiv (prod-rev A B)
is-equiv-prod-rev A B = dpair (dpair (prod-rev B A) (prod-rev-squared-is-id B A)) (dpair (prod-rev B A) (prod-rev-squared-is-id A B))

-- Exercise 5.9
--                             i      r
-- Consider a sec/retr pair A ---> B ---> A with H : r ∘ i ~ id. Show that x = y is a retr of i(x) = i(y)
-- path-is-retr-of-apsec : {j k : Level} {A : UU j} {B : UU k} (x : A) (y : A) → (i : A → B) → (r : B → A) → ((r ∘ i) ~ id) → ((Id x y) retract-of (Id (i x) (i y)))
-- path-is-retr-of-apsec x y i r H = dpair (ap i) ((dpair (( λ p → x ==⟨ inv (H x) ⟩ r (i x) ==⟨ (ap r p) ⟩ r (i y) ==⟨ (H y) ⟩ y ==∎ )) λ p → {!   !})

-- Exercise 5.10
-- Exercise 5.11
-- Exercise 5.12
-- Exercise 5.13


------------------------------------------------------------------------
-- An abstraction of various forms of recursion/induction
------------------------------------------------------------------------

-- Note: The types in this module can perhaps be easier to understand
-- if they are normalised. Note also that Agda can do the
-- normalisation for you.

module Induction where

open import Relation.Unary

-- A RecStruct describes the allowed structure of recursion. The
-- examples in Induction.Nat should explain what this is all about.

RecStruct : Set → Set₁
RecStruct a = Pred a → Pred a

-- A recursor builder constructs an instance of a recursion structure
-- for a given input.

RecursorBuilder : ∀ {a} → RecStruct a → Set₁
RecursorBuilder {a} Rec = (P : Pred a) → Rec P ⊆′ P → Universal (Rec P)

-- A recursor can be used to actually compute/prove something useful.

Recursor : ∀ {a} → RecStruct a → Set₁
Recursor {a} Rec = (P : Pred a) → Rec P ⊆′ P → Universal P

-- And recursors can be constructed from recursor builders.

build : ∀ {a} {Rec : RecStruct a} →
        RecursorBuilder Rec →
        Recursor Rec
build builder P f x = f x (builder P f x)

-- We can repeat the exercise above for subsets of the type we are
-- recursing over.

SubsetRecursorBuilder : ∀ {a} → Pred a → RecStruct a → Set₁
SubsetRecursorBuilder {a} Q Rec = (P : Pred a) → Rec P ⊆′ P → Q ⊆′ Rec P

SubsetRecursor : ∀ {a} → Pred a → RecStruct a → Set₁
SubsetRecursor {a} Q Rec = (P : Pred a) → Rec P ⊆′ P → Q ⊆′ P

subsetBuild : ∀ {a} {Q : Pred a} {Rec : RecStruct a} →
              SubsetRecursorBuilder Q Rec →
              SubsetRecursor Q Rec
subsetBuild builder P f x q = f x (builder P f x q)


-- simply-typed λ-calculus w/ DeBruijn indices

module LC where

open import Agda.Primitive
open import Agda.Builtin.Bool
open import Data.Bool.Properties hiding (≤-trans ; <-trans ; ≤-refl ; <-irrefl)
open import Data.Empty
open import Data.Nat renaming (_+_ to _+ᴺ_ ; _≤_ to _≤ᴺ_ ; _≥_ to _≥ᴺ_ ; _<_ to _<ᴺ_ ; _>_ to _>ᴺ_)
open import Data.Nat.Properties renaming (_<?_ to _<ᴺ?_)
open import Data.Integer renaming (_+_ to _+ᶻ_ ; _≤_ to _≤ᶻ_ ; _≥_ to _≥ᶻ_ ; _<_ to _<ᶻ_ ; _>_ to _>ᶻ_)
open import Data.Integer.Properties using (⊖-≥ ; 0≤n⇒+∣n∣≡n ; +-monoˡ-≤)
open import Data.List
open import Data.List.Relation.Unary.All
open import Relation.Binary.PropositionalEquality
open import Relation.Nullary
open import Relation.Nullary.Decidable
open import Relation.Nullary.Negation

open import Auxiliary

module defs where
  data Exp : Set where
    Var : ℕ → Exp
    Abs : Exp → Exp
    App : Exp → Exp → Exp

  data Ty : Set where
    Fun : Ty → Ty → Ty

  -- typing

  Env = List Ty

  data _∶_∈_ : ℕ → Ty → Env → Set where
    here : {T : Ty} {Γ : Env} → 0 ∶ T ∈ (T ∷ Γ)
    there : {n : ℕ} {T₁ T₂ : Ty} {Γ : Env} → n ∶ T₁ ∈ Γ → (ℕ.suc n) ∶ T₁ ∈ (T₂ ∷ Γ)

  data _⊢_∶_ : Env → Exp → Ty → Set where
    TVar : {n : ℕ} {Γ : Env} {T : Ty} → n ∶ T ∈ Γ → Γ ⊢ (Var n) ∶ T
    TAbs : {Γ : Env} {T₁ T₂ : Ty} {e : Exp} → (T₁ ∷ Γ) ⊢ e ∶ T₂ → Γ ⊢ (Abs e) ∶ (Fun T₁ T₂)
    TApp : {Γ : Env} {T₁ T₂ : Ty} {e₁ e₂ : Exp} → Γ ⊢ e₁ ∶ (Fun T₁ T₂) → Γ ⊢ e₂ ∶ T₁ → Γ ⊢ (App e₁ e₂) ∶ T₂

-- denotational semantics
module denotational where
  open defs

  Valᵈ : Ty → Set
  Valᵈ (Fun Ty₁ Ty₂) = (Valᵈ Ty₁) → (Valᵈ Ty₂)

  access : {n : ℕ} {Γ : Env} {T : Ty} → n ∶ T ∈ Γ → All Valᵈ Γ → Valᵈ T
  access here (V ∷ Γ) = V
  access (there J) (V ∷ Γ) = access J Γ

  eval : {Γ : Env} {T : Ty} {e : Exp} → Γ ⊢ e ∶ T → All Valᵈ Γ → Valᵈ T
  eval (TVar c) Val-Γ = access c Val-Γ
  eval (TAbs TJ) Val-Γ = λ V → eval TJ (V ∷ Val-Γ)
  eval (TApp TJ TJ₁) Val-Γ = (eval TJ Val-Γ) (eval TJ₁ Val-Γ)

-- operational semantics (call-by-value)
module operational where
  open defs

  -- shifting, required to avoid variable-capturing in substitution
  -- see Pierce 2002, pg. 78/79
  ↑_,_[_] : ℤ → ℕ → Exp → Exp
  ↑ d , c [ Var x ]
    with (x <ᴺ? c)
  ...  | yes p = Var x
  ...  | no ¬p = Var (∣ (ℤ.pos x) +ᶻ d ∣)  -- should always be positive anyway
  ↑ d , c [ Abs t ] = Abs (↑ d , (ℕ.suc c) [ t ])
  ↑ d , c [ App t t₁ ] = App (↑ d , c [ t ]) (↑ d , c [ t₁ ])

  -- shifting in range [n, m]; by def. m < n implies no shift
  ↑[_,_]_[_] : ℕ → ℕ → ℤ → Exp → Exp
  ↑[ n , m ] d [ Var x ]
    with (x <ᴺ? n)
  ...  | yes p = Var x
  ...  | no ¬p
       with (m <ᴺ? x)
  ...     | yes p' = Var x
  ...     | no ¬p' = Var (∣ (ℤ.pos x) +ᶻ d ∣)
  ↑[ n , m ] d [ Abs e ] = Abs (↑[ ℕ.suc n , ℕ.suc m ] d [ e ])
  ↑[ n , m ] d [ App e e₁ ] = App (↑[ n , m ] d [ e ]) (↑[ n , m ] d [ e₁ ])

  -- shorthands
  ↑¹[_] : Exp → Exp
  ↑¹[ e ] = ↑ (ℤ.pos 1) , 0 [ e ]

  ↑⁻¹[_] : Exp → Exp
  ↑⁻¹[ e ] = ↑ (ℤ.negsuc 0) , 0 [ e ]

  -- substitution
  -- see Pierce 2002, pg. 80
  [_↦_]_ : ℕ → Exp → Exp → Exp
  [ k ↦ s ] Var x
    with (Data.Nat._≟_ x k)
  ...  | yes p = s
  ...  | no ¬p = Var x
  [ k ↦ s ] Abs t = Abs ([ ℕ.suc k ↦ ↑¹[ s ] ] t)
  [ k ↦ s ] App t t₁ = App ([ k ↦ s ] t) ([ k ↦ s ] t₁)

  data Val : Exp → Set where
    VFun : {e : Exp} → Val (Abs e)

  -- reduction relation
  data _⇒_ : Exp → Exp → Set where
    ξ-App1 : {e₁ e₁' e₂ : Exp} → e₁ ⇒ e₁' → App e₁ e₂ ⇒ App e₁' e₂
    ξ-App2 : {e e' v : Exp} → Val v → e ⇒ e' → App v e ⇒ App v e'
    β-App : {e v : Exp} → Val v → (App (Abs e) v) ⇒ (↑⁻¹[ ([ 0 ↦ ↑¹[ v ] ] e) ])

  ---- properties & lemmas

  --- properties of shifting

  ↑-var-refl : {d : ℤ} {c : ℕ} {x : ℕ} {le : ℕ.suc x ≤ᴺ c} → ↑ d , c [ Var x ] ≡ Var x
  ↑-var-refl {d} {c} {x} {le}
    with (x <ᴺ? c)
  ...  | no ¬p = contradiction le ¬p
  ...  | yes p = refl

  ↑[]-var-refl-< : {n m x : ℕ} {d : ℤ} {le : x <ᴺ n} → ↑[ n , m ] d [ Var x ] ≡ Var x
  ↑[]-var-refl-< {n} {m} {x} {d} {le}
    with (x <ᴺ? n)
  ...  | yes p = refl
  ...  | no ¬p = contradiction le ¬p

  ↑[]-var-refl-> : {n m x : ℕ} {d : ℤ} {le : m <ᴺ x} → ↑[ n , m ] d [ Var x ] ≡ Var x
  ↑[]-var-refl-> {n} {m} {x} {d} {le}
    with (x <ᴺ? n)
  ...  | yes p = refl
  ...  | no p
      with (m <ᴺ? x)
  ...    | no ¬q = contradiction le ¬q
  ...    | yes q = refl

  ↑[]-var-shift : {n m x : ℕ} {d : ℤ} (le1 : n ≤ᴺ x) (le2 : x ≤ᴺ m) → ↑[ n , m ] d [ Var x ] ≡  Var (∣ (ℤ.pos x) +ᶻ d ∣)
  ↑[]-var-shift {n} {m} {x} {d} le1 le2
    with x <ᴺ? n
  ...  | yes p = contradiction p (<⇒≱ (s≤s le1))
  ...  | no ¬p
      with m <ᴺ? x
  ...    | yes p' = contradiction p' (<⇒≱ (s≤s le2))
  ...    | no ¬p' = refl

  ↑¹-var : {x : ℕ} → ↑¹[ Var x ] ≡ Var (ℕ.suc x)
  ↑¹-var {zero} = refl
  ↑¹-var {ℕ.suc x}
    rewrite (sym (n+1≡sucn{x +ᴺ 1}))
          | (sym (n+1≡sucn{x}))
          = cong ↑¹[_] (↑¹-var{x})

  ↑⁻¹ₖ[↑¹ₖ[s]]≡s : {e : Exp} {k : ℕ} → ↑ -[1+ 0 ] , k [ ↑ + 1 , k [ e ] ] ≡ e
  ↑⁻¹ₖ[↑¹ₖ[s]]≡s {Var x} {k}
    with (x <ᴺ? k)
  -- x < k
  --  => ↑⁻¹ₖ(↑¹ₖ(Var n)) = ↑⁻¹ₖ(Var n) = Var n
  ...  | yes p = ↑-var-refl{ -[1+ 0 ]}{k}{x}{p}
  -- x ≥ k
  --  => ↑⁻¹ₖ(↑¹ₖ(Var n)) = ↑⁻¹ₖ(Var |n + 1|) = Var (||n + 1| - 1|) = Var n
  ...  | no ¬p
       with (¬[x≤k]⇒¬[sucx≤k] ¬p)
  ...     | ¬p'
          with (x +ᴺ 1) <ᴺ? k
  ...        | yes pp = contradiction pp ¬p'
  ...        | no ¬pp
             rewrite (∣nℕ+1⊖1∣≡n{x})
                   = refl
  ↑⁻¹ₖ[↑¹ₖ[s]]≡s {Abs e} {k} = cong Abs ↑⁻¹ₖ[↑¹ₖ[s]]≡s
  ↑⁻¹ₖ[↑¹ₖ[s]]≡s {App e e₁} = cong₂ App ↑⁻¹ₖ[↑¹ₖ[s]]≡s ↑⁻¹ₖ[↑¹ₖ[s]]≡s

  ↑ᵏ[↑ˡ[s]]≡↑ᵏ⁺ˡ[s] : {k l : ℤ} {c : ℕ} {s : Exp} → l ≥ᶻ +0 → ↑ k , c [ ↑ l , c [ s ] ] ≡ ↑ (l +ᶻ k) , c [ s ]
  ↑ᵏ[↑ˡ[s]]≡↑ᵏ⁺ˡ[s] {k} {l} {c} {Var x} ge
    with x <ᴺ? c
  ↑ᵏ[↑ˡ[s]]≡↑ᵏ⁺ˡ[s] {k} {l} {c} {Var x} ge | no ¬p
      with ∣ + x +ᶻ l ∣ <ᴺ? c
  ...    | yes q = contradiction q (<⇒≱ (n≤m⇒n<sucm (≤-trans (≮⇒≥ ¬p) (m≥0⇒∣n+m∣≥n ge))))
  ...    | no ¬q
         rewrite (0≤n⇒+∣n∣≡n{+ x +ᶻ l} (m≥0⇒n+m≥0 ge))
               | (Data.Integer.Properties.+-assoc (+_ x) l k)
               = refl
  ↑ᵏ[↑ˡ[s]]≡↑ᵏ⁺ˡ[s] {k} {l} {c} {Var x} ge | yes p
      with x <ᴺ? c
  ...    | yes p' = refl
  ...    | no ¬p' = contradiction p ¬p'
  ↑ᵏ[↑ˡ[s]]≡↑ᵏ⁺ˡ[s] {k} {l} {c} {Abs s} le = cong Abs (↑ᵏ[↑ˡ[s]]≡↑ᵏ⁺ˡ[s]{k}{l}{ℕ.suc c}{s} le)
  ↑ᵏ[↑ˡ[s]]≡↑ᵏ⁺ˡ[s] {k} {l} {c} {App s s₁} le = cong₂ App  (↑ᵏ[↑ˡ[s]]≡↑ᵏ⁺ˡ[s]{k}{l}{c}{s} le)  (↑ᵏ[↑ˡ[s]]≡↑ᵏ⁺ˡ[s]{k}{l}{c}{s₁} le)

  ↑k,q[↑l,c[s]]≡↑l+k,c[s] : {k l : ℤ} {q c : ℕ} {s : Exp} →  + q ≤ᶻ + c +ᶻ l → c ≤ᴺ q → ↑ k , q [ ↑ l , c [ s ] ] ≡ ↑ (l +ᶻ k) , c [ s ]
  ↑k,q[↑l,c[s]]≡↑l+k,c[s] {k} {l} {q} {c} {Var x} ge₁ ge₂
    with x <ᴺ? c
  ... | yes p
      with x <ᴺ? q
  ...    | yes p' = refl
  ...    | no ¬p' = contradiction (≤-trans p ge₂) ¬p'
  ↑k,q[↑l,c[s]]≡↑l+k,c[s] {k} {l} {q} {c} {Var x} ge₁ ge₂
      | no ¬p
      with ∣ + x +ᶻ l ∣ <ᴺ? q
  ...    | yes p' = contradiction p' (≤⇒≯ (+a≤b⇒a≤∣b∣{q}{+ x +ᶻ l} (Data.Integer.Properties.≤-trans ge₁ ((Data.Integer.Properties.+-monoˡ-≤ l (+≤+ (≮⇒≥ ¬p)))))))
  ...    | no ¬p'
         rewrite (0≤n⇒+∣n∣≡n{+ x +ᶻ l} (Data.Integer.Properties.≤-trans (+≤+ z≤n) ((Data.Integer.Properties.≤-trans ge₁ ((Data.Integer.Properties.+-monoˡ-≤ l (+≤+ (≮⇒≥ ¬p))))))))
               | (Data.Integer.Properties.+-assoc (+_ x) l k)
               = refl
  ↑k,q[↑l,c[s]]≡↑l+k,c[s] {k} {l} {q} {c} {Abs s} ge₁ ge₂ = cong Abs (↑k,q[↑l,c[s]]≡↑l+k,c[s] {k} {l} {ℕ.suc q} {ℕ.suc c} {s} (+q≤+c+l⇒+1q≤+1c+l{q}{c}{l} ge₁) (s≤s ge₂))
  ↑k,q[↑l,c[s]]≡↑l+k,c[s] {k} {l} {q} {c} {App s s₁} ge₁ ge₂ = cong₂ App (↑k,q[↑l,c[s]]≡↑l+k,c[s] {k} {l} {q} {c} {s} ge₁ ge₂) (↑k,q[↑l,c[s]]≡↑l+k,c[s] {k} {l} {q} {c} {s₁} ge₁ ge₂)

  --- properties of substitution

  subst-trivial : {x : ℕ} {s : Exp} → [ x ↦ s ] Var x ≡ s
  subst-trivial {x} {s}
    with x Data.Nat.≟ x
  ...  | no ¬p = contradiction refl ¬p
  ...  | yes p = refl

  var-subst-refl : {n m : ℕ} {neq : n ≢ m} {e : Exp} → [ n ↦ e ] (Var m) ≡ (Var m)
  var-subst-refl {n} {m} {neq} {e}
    with Data.Nat._≟_ n m
       | map′ (≡ᵇ⇒≡ m n) (≡⇒≡ᵇ m n) (Data.Bool.Properties.T? (m ≡ᵇ n))
  ...  | yes p | _ = contradiction p neq
  ...  | no ¬p | yes q = contradiction q (≢-sym ¬p)
  ...  | no ¬p | no ¬q = refl

  --- properties and manipulation of environments

  var-env-< : {Γ : Env} {T : Ty} {n : ℕ} (j : n ∶ T ∈ Γ) → n <ᴺ (length Γ)
  var-env-< {.(T ∷ _)} {T} {.0} here = s≤s z≤n
  var-env-< {.(_ ∷ _)} {T} {.(ℕ.suc _)} (there j) = s≤s (var-env-< j)

  -- type to determine whether var type judgement in env. (Δ ++ Γ) is in Δ or Γ
  data extract-env-or {Δ Γ : Env} {T : Ty} {x : ℕ} : Set where
    in-Δ : x ∶ T ∈ Δ → extract-env-or
    -- x ≥ length Δ makes sure that x really is in Γ; e.g.
    -- x = 1, Δ = (S ∷ T), Γ = (T ∷ Γ'); here 1 ∶ T ∈ Δ as well as (1 ∸ 2) ≡ 0 ∶ T ∈ Γ
    in-Γ : (x ≥ᴺ length Δ) → (x ∸ length Δ) ∶ T ∈ Γ → extract-env-or

  extract : {Δ Γ : Env} {T : Ty} {x : ℕ} (j : x ∶ T ∈ (Δ ++ Γ)) → extract-env-or{Δ}{Γ}{T}{x}
  extract {[]} {Γ} {T} {x} j = in-Γ z≤n j
  extract {x₁ ∷ Δ} {Γ} {.x₁} {.0} here = in-Δ here
  extract {x₁ ∷ Δ} {Γ} {T} {ℕ.suc x} (there j)
    with extract {Δ} {Γ} {T} {x} j
  ...  | in-Δ j'  = in-Δ (there j')
  ...  | in-Γ ge j'' = in-Γ (s≤s ge) j''

  ext-behind : {Δ Γ : Env} {T : Ty} {x : ℕ} → x ∶ T ∈ Δ → x ∶ T ∈ (Δ ++ Γ)
  ext-behind here = here
  ext-behind (there j) = there (ext-behind j)

  ext-front : {n : ℕ} {Γ Δ : Env} {S : Ty} → n ∶ S ∈ Γ → (n +ᴺ (length Δ)) ∶ S ∈ (Δ ++ Γ)
  ext-front {n} {Γ} {[]} {S} j
    rewrite (n+length[]≡n{A = Ty}{n = n})
          = j
  ext-front {n} {Γ} {T ∷ Δ} {S} j
    rewrite (+-suc n (foldr (λ _ → ℕ.suc) 0 Δ))
          = there (ext-front j)

  swap-env-behind : {Γ Δ : Env} {T : Ty} → 0 ∶ T ∈ (T ∷ Γ) → 0 ∶ T ∈ (T ∷ Δ)
  swap-env-behind {Γ} {Δ} {T} j = here

  swap-type : {Δ ∇ Γ : Env} {T : Ty} → (length Δ) ∶ T ∈ (Δ ++ T ∷ ∇ ++ Γ) → (length Δ +ᴺ length ∇) ∶ T ∈ (Δ ++ ∇ ++ T ∷ Γ)
  swap-type {Δ} {∇} {Γ} {T} j
    with extract{Δ}{T ∷ ∇ ++ Γ} j
  ...  | in-Δ x = contradiction (var-env-< {Δ} {T} x) (<-irrefl refl)
  ...  | in-Γ le j'
       with extract{T ∷ ∇}{Γ} j'
  ...     | in-Δ j''
          rewrite (n∸n≡0 (length Δ))
                | (sym (length[A++B]≡length[A]+length[B]{lzero}{Ty}{Δ}{∇}))
                | (sym (++-assoc{lzero}{Ty}{Δ}{∇}{T ∷ Γ}))
                = ext-front{0}{T ∷ Γ}{Δ ++ ∇}{T} (swap-env-behind{∇}{Γ}{T} j'')
  ...     | in-Γ le' j''
          rewrite (length[A∷B]≡suc[length[B]]{lzero}{Ty}{T}{∇})
                | (n∸n≡0 (length Δ))
                = contradiction le' (<⇒≱ (s≤s z≤n))

  env-pred : {Γ : Env} {S T : Ty} {y : ℕ} {gt : y ≢ 0} → y ∶ T ∈ (S ∷ Γ) → ∣ y ⊖ 1 ∣ ∶ T ∈ Γ
  env-pred {Γ} {S} {.S} {.0} {gt} here = contradiction refl gt
  env-pred {Γ} {S} {T} {.(ℕ.suc _)} {gt} (there j) = j

  env-type-equiv-here : {Γ : Env} {S T : Ty} → 0 ∶ T ∈ (S ∷ Γ) → T ≡ S
  env-type-equiv-here {Γ} {S} {.S} here = refl

  env-type-equiv : {Δ ∇ : Env} {S T : Ty} → length Δ ∶ T ∈ (Δ ++ S ∷ ∇) → T ≡ S
  env-type-equiv {Δ} {∇} {S} {T} j
    with extract{Δ}{S ∷ ∇} j
  ...  | in-Δ x = contradiction (var-env-< x) (≤⇒≯ ≤-refl)
  ...  | in-Γ x j'
       rewrite (n∸n≡0 (length Δ))
             = env-type-equiv-here {∇} {S} {T} j'

  env-type-equiv-j : {Γ : Env} {S T : Ty} {n : ℕ} → T ≡ S → n ∶ T ∈ Γ → n ∶ S ∈ Γ
  env-type-equiv-j {Γ} {S} {T} {n} eq j
    rewrite eq
          = j

  -- extension of environment

  ext : {Γ Δ ∇ : Env} {S : Ty} {s : Exp} → (∇ ++ Γ) ⊢ s ∶ S → (∇ ++ Δ ++ Γ) ⊢ ↑ (ℤ.pos (length Δ)) , length ∇ [ s ] ∶ S
  ext {Γ} {Δ} {∇} (TVar {n} x)
    with extract{∇}{Γ} x
  ... | in-Δ x₁
      with n <ᴺ? length ∇
  ...    | yes p = TVar (ext-behind x₁)
  ...    | no ¬p = contradiction (var-env-< x₁) ¬p
  ext {Γ} {Δ} {∇} (TVar {n} x)
      | in-Γ x₁ x₂
      with n <ᴺ? length ∇
  ...    | yes p = contradiction x₁ (<⇒≱ p)
  ...    | no ¬p
         with (ext-front{n ∸ length ∇}{Γ}{∇ ++ Δ} x₂)
  ...       | w
            rewrite (length[A++B]≡length[A]+length[B]{lzero}{Ty}{∇}{Δ})
                  | (sym (+-assoc (n ∸ length ∇) (length ∇) (length Δ)))
                  | (m∸n+n≡m{n}{length ∇} (≮⇒≥ ¬p))
                  | (++-assoc{lzero}{Ty}{∇}{Δ}{Γ})
                  = TVar w
  ext {Γ} {Δ} {∇} {Fun T₁ T₂} {Abs e} (TAbs j) = TAbs (ext{Γ}{Δ}{T₁ ∷ ∇} j)
  ext {Γ} {Δ} {∇} {S} {App s₁ s₂} (TApp{T₁ = T₁} j₁ j₂) = TApp (ext{Γ}{Δ}{∇}{Fun T₁ S} j₁) (ext{Γ}{Δ}{∇}{T₁} j₂)

  ---- progress and preservation

  -- progress theorem, i.e. a well-typed closed expression is either a value
  -- or can be reduced further
  data Progress (e : Exp) {T : Ty} {j : [] ⊢ e ∶ T}  : Set where
    step : {e' : Exp} → e ⇒ e' → Progress e
    value : Val e → Progress e

  progress : (e : Exp) {T : Ty} {j : [] ⊢ e ∶ T} → Progress e {T} {j}
  progress (Var x) {T} {TVar ()}
  progress (Abs e) = value VFun
  progress (App e e₁) {T} {TApp{T₁ = T₁}{T₂ = .T} j j₁} with progress e {Fun T₁ T} {j}
  ... | step x = step (ξ-App1 x)
  ... | value VFun with progress e₁ {T₁} {j₁}
  ...    | step x₁ = step (ξ-App2 VFun x₁)
  ...    | value x₁ = step (β-App x₁)

  ---

  -- preservation under substitution
  preserve-subst : {T S : Ty} {Γ Δ : Env} {e s : Exp} (j : (Δ ++ (S ∷ Γ)) ⊢ e ∶ T) (j' : Γ ⊢ s ∶ S) → (Δ ++ Γ) ⊢  ↑ -[1+ 0 ] , length Δ [ [ length Δ ↦ ↑ (ℤ.pos (ℕ.suc (length Δ))) , 0 [ s ] ] e ] ∶ T
  preserve-subst {T} {S} {Γ} {Δ} {e} {s} (TVar{n} x) j'
    with extract{Δ}{S ∷ Γ}{T}{n} x
  ...  | in-Δ x₁
       with n Data.Nat.≟ length Δ
  ...     | yes p = contradiction p (<⇒≢ (var-env-< x₁))
  ...     | no ¬p
          with n <ᴺ? length Δ
  ...        | yes q = TVar (ext-behind{Δ}{Γ} x₁)
  ...        | no ¬q = contradiction (var-env-< x₁) ¬q
  preserve-subst {T} {S} {Γ} {Δ} {e} {s} (TVar{n} x) j'
       | in-Γ x₁ x₂
       with n Data.Nat.≟ length Δ
  ...     | yes p
          rewrite (↑k,q[↑l,c[s]]≡↑l+k,c[s]{ -[1+ 0 ]}{+[1+ length Δ ]}{length Δ}{0}{s} (+≤+ n≤sucn) z≤n)
                | p
                | (env-type-equiv x)
                = ext{Γ}{Δ}{[]} j'
  ...     | no ¬p
          with n <ᴺ? length Δ
  ...        | yes q = contradiction x₁ (<⇒≱ q)
  ...        | no ¬q rewrite (sym (minus-1{n}{length Δ}{≤∧≢⇒< x₁ (≢-sym ¬p)}))
                           = TVar (ext-front{Δ = Δ} (env-pred{Γ}{S}{T}{gt = ≢-sym (<⇒≢ (m>n⇒m∸n≥1 (≤∧≢⇒< x₁ (≢-sym ¬p))))} x₂))
  preserve-subst {T} {S} {Γ} {Δ} {Abs e'} {s} (TAbs{T₁ = T₁}{T₂} j) j'
    with preserve-subst{T₂}{S}{Γ}{T₁ ∷ Δ}{e'}{s} j j'
  ...  | w
       rewrite (↑ᵏ[↑ˡ[s]]≡↑ᵏ⁺ˡ[s]{+ 1}{+[1+ length Δ ]}{0}{s} (+≤+ z≤n))
             | (length[A∷B]≡suc[length[B]]{lzero}{Ty}{T₁}{Δ})
             | (n+1≡sucn{length Δ})
             = TAbs w
  preserve-subst (TApp j j₁) j' = TApp (preserve-subst j j') (preserve-subst j₁ j')

  -- preservation theorem, i.e. a well-typed expression reduces to a well-typed expression
  preserve : {T : Ty} {Γ : Env} (e e' : Exp) (j : Γ ⊢ e ∶ T) (r : e ⇒ e') → Γ ⊢ e' ∶ T
  preserve (App s₁ s₂) .(App _ s₂) (TApp j j') (ξ-App1{e₁' = s₁'} r) = TApp (preserve s₁ s₁' j r) j' -- IH on inner reduction
  preserve (App s₁ s₂) .(App s₁ _) (TApp j j') (ξ-App2{e' = s₂'} x r) = TApp j (preserve s₂ s₂' j' r)
  preserve (App (Abs e) s')  .(↑⁻¹[ [ 0 ↦ ↑¹[ s' ] ] e ]) (TApp (TAbs j) j') (β-App x) = preserve-subst{Δ = []} j j'

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

  -- swap-subst lemma: swap the position of a type in an environment (did not need this after all)

  -- aux. calculations
  length-≡ : {Δ ∇ Γ : Env} {S : Ty} → ℕ.suc (length Δ +ᴺ (length Γ +ᴺ length ∇)) ≡ length (Δ ++ S ∷ ∇ ++ Γ)
  length-≡ {Δ} {∇} {Γ} {S}
    rewrite (length[A++B]≡length[A]+length[B]{lzero}{Ty}{Δ}{S ∷ ∇ ++ Γ})
          | (length[A++B]≡length[A]+length[B]{lzero}{Ty}{∇}{Γ})
          | (+-suc (length Δ) (length ∇ +ᴺ length Γ))
          | (+-comm (length ∇) (length Γ))
          = refl

  length-≡' : {Δ ∇ : Env} {S : Ty} → length (Δ ++ ∇ ++ S ∷ []) ≡ length Δ +ᴺ ℕ.suc (length ∇)
  length-≡' {Δ} {∇} {S}
    rewrite (length[A++B]≡length[A]+length[B]{lzero}{Ty}{Δ}{∇ ++ S ∷ []})
          | (length[A++B]≡length[A]+length[B]{lzero}{Ty}{∇}{S ∷ []})
          | (+-suc (length ∇) (0))
          | (+-identityʳ (length ∇))
          = refl

  length-≡'' : {Δ ∇ : Env} {S : Ty} → length (Δ ++ ∇ ++ S ∷ []) ≡ ℕ.suc (length ∇ +ᴺ length Δ)
  length-≡'' {Δ} {∇} {S}
    rewrite (cong (ℕ.suc) (+-comm (length ∇) (length Δ)))
          | (sym (+-suc (length Δ) (length ∇)))
          = length-≡'{Δ}{∇}{S}

  --- why the "r"? we have to "remember" where S was
  --- cannot substitute its position for zero, since ↑ would increase that
  --- cannot do ↑ and then subtitute its position for zero, since (position - 1) would be affected aswell
  --- fix: cache in unreachable variable r
  swap-subst : {T S : Ty} {Γ Δ ∇ : Env} {e : Exp} {r : ℕ} {gt : r >ᴺ ℕ.suc (length Δ +ᴺ (length Γ +ᴺ length ∇))}
               → (Δ ++ (S ∷ ∇) ++ Γ) ⊢ e ∶ T
               → (Δ ++ ∇ ++ (S ∷ Γ)) ⊢ [ r ↦ Var (length Δ +ᴺ length ∇) ] ↑[ ℕ.suc (length Δ) , length Δ +ᴺ length ∇ ] -[1+ 0 ] [ [ length Δ ↦ Var r ] e ] ∶ T
  swap-subst {(Fun T₁ T₂)} {S} {Γ} {Δ} {∇} {(Abs e)} {r} {gt} (TAbs j)
    rewrite (↑¹-var{length Δ +ᴺ length ∇})
          | (n+1≡sucn{r})
          = TAbs (swap-subst{T₂}{S}{Γ}{T₁ ∷ Δ}{∇}{e}{ℕ.suc r}{s≤s gt} j)
  swap-subst {T} {S} {Γ} {Δ} {∇} {(App e e₁)} {r} {gt} (TApp{T₁ = T₁}{T₂} j j₁) = TApp (swap-subst{Fun T₁ T₂}{S}{Γ}{Δ}{∇}{e}{r}{gt} j) (swap-subst{T₁}{S}{Γ}{Δ}{∇}{e₁}{r}{gt} j₁)
  swap-subst {T} {S} {Γ} {Δ} {∇} {Var y} {r} {gt} (TVar j)
    with extract{Δ} {(S ∷ ∇) ++ Γ} {T} {y} j
       | y <ᴺ? (foldr (λ _ → ℕ.suc) 0 Δ)
       | Data.Nat._≟_ y (length Δ)
  -- y ∈ Δ
  ... | in-Δ x | yes p | yes q = contradiction q (<⇒≢ p)
  ... | in-Δ x | yes p | no ¬q
    rewrite (↑[]-var-refl-<{ℕ.suc (length Δ)}{length Δ +ᴺ length ∇}{y}{ -[1+ 0 ] }{[k<x]⇒[k<sucx] p})
          | (var-subst-refl{r}{y}{≢-sym (<⇒≢ (a<b≤c⇒a<c p (≤-trans (n≤m⇒n≤sucm (m≤m+n (length Δ) (length Γ +ᴺ length ∇))) (<-trans (s≤s ≤-refl) gt))) )}
            {Var (foldr (λ _ → ℕ.suc) 0 Δ +ᴺ foldr (λ _ → ℕ.suc) 0 ∇)})
          = TVar (ext-behind x)
  ... | in-Δ x | no ¬p | _ = contradiction (var-env-< x) ¬p
  -- y ∈ (S ∷ ∇ ++ Γ)
  ... | in-Γ ge x | p | q
    with extract{S ∷ ∇} {Γ} {T} {y ∸ length Δ} x
  --- y ∈ (S ∷ ∇)
  ---- y @ S
  swap-subst {T} {S} {Γ} {Δ} {∇} {Var y} {r} {gt} (TVar j) | in-Γ ge x | p | yes q' | in-Δ x'
    rewrite (↑[]-var-refl->{ℕ.suc (length Δ)}{length Δ +ᴺ length ∇}{r}{ -[1+ 0 ] }{<-trans (s≤s (n+m≤n+q+m {length Δ} {length ∇} {length Γ})) gt})
          | (subst-trivial{r} {Var (length Δ +ᴺ length ∇)})
          | q'
          | (env-type-equiv j)
          = TVar (swap-type{Δ}{∇}{Γ}{S} (env-type-equiv-j (env-type-equiv j) j))
  ---- y ∈ ∇
  swap-subst {T} {S} {Γ} {Δ} {∇} {Var y} {r} {gt} (TVar j) | in-Γ ge x | p | no ¬q' | in-Δ x'
    rewrite (↑[]-var-shift{ℕ.suc (length Δ)}{length Δ +ᴺ length ∇}{y}{ -[1+ 0 ]} (≤∧≢⇒< ge (≢-sym ¬q')) (≤-trans (≤-pred (m≤n∧m≡q⇒q≤n (m≤n⇒m+o≤n+o{o = length Δ} (var-env-< x'))
            (cong ℕ.suc (m∸n+n≡m ge)))) (≤-refl-+-comm{length ∇}{length Δ})))
          | length-≡ {Δ} {∇} {Γ} {S}
          | (var-subst-refl {r} {∣ y ⊖ 1 ∣} {≢-sym (<⇒≢ (<-trans (n>0⇒n>∣n⊖1∣ (≤-trans (s≤s z≤n) ((≤∧≢⇒< ge (≢-sym ¬q'))))) (<-trans (var-env-< j) gt)))} {Var (length Δ +ᴺ length ∇)})
          | sym (minus-1{y}{length Δ}{≤∧≢⇒< ge (≢-sym ¬q')} )
          = TVar (ext-front{Δ = Δ}(ext-behind{∇}{S ∷ Γ}{T} (env-pred{gt = ≢-sym (<⇒≢ (m>n⇒m∸n≥1{y}{length Δ} ((≤∧≢⇒< ge (≢-sym ¬q')))))} x')))
  --- y ∈ Γ
  swap-subst {T} {S} {Γ} {Δ} {∇} {Var y} {r} {gt} (TVar j) | in-Γ ge x | no ¬p | yes q | in-Γ ge' x'
          = contradiction q (≢-sym (<⇒≢ (m∸n≢0⇒n<m{y}{length Δ} (≢-sym (<⇒≢ (a<b≤c⇒a<c (s≤s z≤n) (≤-trans (length[A∷B]≥1{lzero}{Ty}{S}{∇}) ge')))))))
  swap-subst {T} {S} {Γ} {Δ} {∇} {Var y} {r} {gt} (TVar j) | in-Γ ge x | yes p | yes q | in-Γ ge' x' = contradiction q (<⇒≢ p)
  swap-subst {T} {S} {Γ} {Δ} {∇} {Var y} {r} {gt} (TVar j) | in-Γ ge x | p | no ¬q' | in-Γ ge' x'
    rewrite (↑[]-var-refl->{ℕ.suc (length Δ)}{length Δ +ᴺ length ∇}{y}{ -[1+ 0 ]}{a<b≤c⇒a<c (s≤s (≤-refl-+-comm{length Δ}{length ∇})) (m≤n∧n≡q⇒m≤q (m≤n⇒m+o≤n+o{o = length Δ} ge') (m∸n+n≡m ge))})
          | (var-subst-refl{r}{y}{≢-sym (<⇒≢ (<-trans (var-env-< j) (m≤n∧m≡q⇒q≤n gt (cong ℕ.suc (length-≡{Δ}{∇}{Γ}{S})))))}{ Var (foldr (λ _ → ℕ.suc) 0 Δ +ᴺ foldr (λ _ → ℕ.suc) 0 ∇)})
      with ext-front{((y ∸ (length Δ)) ∸ ℕ.suc (length ∇))}{Γ}{Δ ++ ∇ ++ (S ∷ [])}{T}
  ...     | w rewrite (∸-+-assoc y (length Δ) (ℕ.suc (length ∇)))
                    | (sym (length-≡'{Δ}{∇}{S}))
                    | (m∸n+n≡m{y}{length (Δ ++ ∇ ++ S ∷ [])}  (m≤n∧m≡q⇒q≤n (m≤n∧n≡q⇒m≤q (m≤n⇒m+o≤n+o{o = length Δ} ge') (m∸n+n≡m ge)) (sym (length-≡''{Δ}{∇}{S}))))
                    | (A++B++D∷[]++C≡A++B++D∷C{lzero}{Ty}{Δ}{∇}{Γ}{S})
                    = TVar (w x')


open import Categories
open import Monads

module Monads.Kleisli.Functors {a b}{C : Cat {a}{b}}(M : Monad C) where

open import Library
open import Functors
open import Monads.Kleisli M

open Cat C
open Fun
open Monad M

KlL : Fun C Kl
KlL = record{
  OMap  = id;
  HMap  = comp η;
  fid   = idr;
  fcomp = λ{_}{_}{_}{f}{g} →
    proof
    comp η (comp f g)
    ≅⟨ sym ass ⟩
    comp (comp η f) g
    ≅⟨ cong (λ f → comp f g) (sym law2) ⟩
    comp (comp (bind (comp η f)) η) g
    ≅⟨ ass ⟩
    comp (bind (comp η f)) (comp η g)
    ∎}

KlR : Fun Kl C
KlR = record{
  OMap  = T;
  HMap  = bind;
  fid   = law1;
  fcomp = law3}


module Issue348 where

import Common.Irrelevance

data _==_ {A : Set1}(a : A) : A -> Set where
  refl : a == a

record R : Set1 where
  constructor mkR
  field
    .fromR : Set

reflR : (r : R) -> r == r
reflR r = refl {a = _}
-- issue: unsolved metavars resolved 2010-10-15 by making eta-expansion
-- more lazy (do not eta expand all meta variable listeners, see MetaVars.hs


-- Andreas and James, Nov 2011 and Oct 2012
-- function with flexible arity
-- {-# OPTIONS -v tc.cover:20 #-}
module FlexibleInterpreter where

open import Common.Equality
open import Common.IO
open import Common.Nat renaming (zero to Z; suc to S) hiding (pred)

data Ty : Set where
  nat : Ty
  arr : Ty -> Ty -> Ty

data Exp : Ty -> Set where
  zero : Exp nat
  suc  : Exp (arr nat nat)
  pred : Exp (arr nat nat)
  app  : {a b : Ty} -> Exp (arr a b) -> Exp a -> Exp b

Sem : Ty -> Set
Sem nat = Nat
Sem (arr a b) = Sem a -> Sem b

module Flex where
  -- Haskell does not seem to handle this

  eval' : (a : Ty) -> Exp a -> Sem a
  eval' ._ zero = Z
  eval' ._ suc  = S
  eval' b (app f e) = eval' _ f (eval' _ e)
  eval' .(arr nat nat) pred Z = Z
  eval' ._ pred (S n) = n

  eval : {a : Ty} -> Exp a -> Sem a
  eval zero         = Z
  eval suc          = S
  eval pred Z       = Z
  eval pred (S n)   = n
  eval (app f e)    = eval f (eval e)

  testEvalSuc : ∀ {n} → eval suc n ≡ S n
  testEvalSuc = refl

  testEvalPredZero : eval pred Z ≡ Z
  testEvalPredZero = refl

  testEvalPredSuc : ∀ {n} → eval pred (S n) ≡ n
  testEvalPredSuc = refl

module Rigid where
  -- This executes fine

  eval : {a : Ty} -> Exp a -> Sem a
  eval zero      = Z
  eval suc       = S
  eval pred      = λ { Z    -> Z
                     ; (S n) -> n
                     }
  eval (app f e) = eval f (eval e)

open Flex

expr = (app pred (app suc (app suc zero)))
test = eval expr
main = printNat test
-- should print 1


{-# OPTIONS --cubical --safe #-}
module Cubical.Relation.Binary.Base where

open import Cubical.Core.Everything

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.HLevels

open import Cubical.HITs.SetQuotients.Base

module BinaryRelation {ℓ ℓ' : Level} {A : Type ℓ} (R : A → A → Type ℓ') where
  isRefl : Type (ℓ-max ℓ ℓ')
  isRefl = (a : A) → R a a

  isSym : Type (ℓ-max ℓ ℓ')
  isSym = (a b : A) → R a b → R b a

  isTrans : Type (ℓ-max ℓ ℓ')
  isTrans = (a b c : A)  → R a b → R b c → R a c

  record isEquivRel : Type (ℓ-max ℓ ℓ') where
    constructor EquivRel
    field
      reflexive : isRefl
      symmetric : isSym
      transitive : isTrans

  isPropValued : Type (ℓ-max ℓ ℓ')
  isPropValued = (a b : A) → isProp (R a b)

  isEffective : Type (ℓ-max ℓ ℓ')
  isEffective = (a b : A) →
    let x : A / R
        x = [ a ]
        y : A / R
        y = [ b ]
    in (x ≡ y) ≃ R a b


------------------------------------------------------------------------
-- INCREMENTAL λ-CALCULUS
--
-- Incrementalization as term-to-term transformation (Fig. 4g).
------------------------------------------------------------------------

import Parametric.Syntax.Type as Type
import Parametric.Syntax.Term as Term
import Parametric.Change.Type as ChangeType

module Parametric.Change.Derive
    {Base : Type.Structure}
    (Const : Term.Structure Base)
    (ΔBase : ChangeType.Structure Base)
  where

open Type.Structure Base
open Term.Structure Base Const
open ChangeType.Structure Base ΔBase

-- Extension point: Incrementalization of primitives.
Structure : Set
Structure = ∀ {τ} →
  Const τ →
  Term ∅ (ΔType τ)

module Structure (derive-const : Structure) where
  fit : ∀ {τ Γ} → Term Γ τ → Term (ΔContext Γ) τ
  fit = weaken Γ≼ΔΓ

  -- In the paper, we transform "x" to "dx". Here, we work with
  -- de Bruijn indices, so we have to manipulate the indices to
  -- account for a bigger context after transformation.
  deriveVar : ∀ {τ Γ} → Var Γ τ → Var (ΔContext Γ) (ΔType τ)
  deriveVar this = this
  deriveVar (that x) = that (that (deriveVar x))

  -- We provide: Incrementalization of arbitrary terms.
  derive : ∀ {τ Γ} → Term Γ τ → Term (ΔContext Γ) (ΔType τ)
  derive (var x) = var (deriveVar x)
  derive (app s t) = app (app (derive s) (fit t)) (derive t)
  derive (abs t) = abs (abs (derive t))
  derive {Γ = Γ} (const c) = weaken (∅≼Γ {ΔContext Γ}) (derive-const c)


------------------------------------------------------------------------
-- The Agda standard library
--
-- Instantiates the ring solver with two copies of the same ring with
-- decidable equality
------------------------------------------------------------------------

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

open import Algebra.Solver.Ring.AlmostCommutativeRing
open import Relation.Binary
open import Relation.Binary.Consequences using (dec⟶weaklyDec)

module Algebra.Solver.Ring.Simple
         {r₁ r₂} (R : AlmostCommutativeRing r₁ r₂)
         (_≟_ : Decidable (AlmostCommutativeRing._≈_ R))
         where

open AlmostCommutativeRing R
import Algebra.Solver.Ring as RS
open RS rawRing R (-raw-almostCommutative⟶ R) (dec⟶weaklyDec _≟_) public


open import FRP.JS.Bool using ( Bool ; _∨_ ; not )
open import FRP.JS.True using ( True )
open import FRP.JS.Maybe using ( Maybe )
open import FRP.JS.Float using ( ℝ ) renaming ( _/?_ to _/?r_ )
open import FRP.JS.Primitive using ( ℕ ; String )

module FRP.JS.Int where

infixr 4 _≤_ _<_ _≟_
infixl 6 _+_ _-_
infixl 7 _*_ _/_ _/?_

open import FRP.JS.Primitive public using ( ℤ )

private
 primitive
  primIntegerPlus     : ℤ -> ℤ -> ℤ
  primIntegerMinus    : ℤ -> ℤ -> ℤ
  primIntegerTimes    : ℤ -> ℤ -> ℤ
  primIntegerDiv      : ℤ -> ℤ -> ℤ
  primIntegerMod      : ℤ -> ℤ -> ℤ
  primIntegerEquality : ℤ -> ℤ -> Bool
  primIntegerLess     : ℤ -> ℤ -> Bool
  primIntegerToFloat  : ℤ -> ℝ
  primIntegerAbs      : ℤ -> ℕ
  primShowInteger     : ℤ -> String
  primNatToInteger    : ℕ → ℤ
  primFloatDiv        : ℝ → ℝ → ℝ

_+_   = primIntegerPlus
_-_   = primIntegerMinus
_*_   = primIntegerTimes
_%_   = primIntegerMod
_≟_  = primIntegerEquality
_<_   = primIntegerLess
float = primIntegerToFloat
abs   = primIntegerAbs
show  = primShowInteger

{-# COMPILED_JS _+_   function(x) { return function(y) { return x + y; }; } #-}
{-# COMPILED_JS _-_   function(x) { return function(y) { return x - y; }; } #-}
{-# COMPILED_JS _*_   function(x) { return function(y) { return x * y; }; } #-}
{-# COMPILED_JS _%_   function(x) { return function(y) { if (x < 0) { return (x % y) + x; } else { return x % y; } }; } #-}
{-# COMPILED_JS _≟_  function(x) { return function(y) { return x === y; }; } #-}
{-# COMPILED_JS _<_   function(x) { return function(y) { return x < y; }; } #-}
{-# COMPILED_JS float function(x) { return x; } #-}
{-# COMPILED_JS abs   function(x) { return Math.abs(x); } #-}
{-# COMPILED_JS show  function(x) { return x.toString(); } #-}

+0 = primNatToInteger 0
+1 = primNatToInteger 1
-1 = +0 - +1

-_ : ℤ → ℤ
- x = +0 - x

_≤_ : ℤ → ℤ → Bool
x ≤ y = (x < y) ∨ (x ≟ y)

_≠_ : ℤ → ℤ → Bool
x ≠ y = not (x ≟ y)

_/_ : (x y : ℤ) → {y≠0 : True (y ≠ +0)} → ℝ
x / y = primFloatDiv (float x) (float y)

_/?_ : ℤ → ℤ → Maybe ℝ
x /? y = (float x) /?r (float y)

{-# COMPILED_JS _/_ function(x) { return function(y) { return x / y; }; } #-}
{-# COMPILED_JS _≤_ function(x) { return function(y) { return x <= y; }; } #-}
{-# COMPILED_JS _≠_ function(x) { return function(y) { return x !== y; }; } #-}
{-# COMPILED_JS _/_  function(x) { return function(y) { return function() { return x / y; }; }; } #-}
{-# COMPILED_JS _/?_  function(x) { return function(y) { if (y === 0) { return null; } else { return x / y; } }; } #-}


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

open import lib.Basics
open import lib.types.Coproduct
open import lib.types.Sigma

{- Some miscellaneous lemmas for types with decidable equality. -}
module Util.DecEq where

×-has-dec-eq : {i j : ULevel} {A : Type i} {B : Type j} → (has-dec-eq A) → (has-dec-eq B) →
  (has-dec-eq (A × B))

×-has-dec-eq Adec Bdec (a , b) (a' , b') =
  ⊔-rec (⊔-rec (λ p q → inl (pair×= q p)) (λ f _ → inr (λ p → f (ap snd p))) (Bdec b b'))
        (λ f → inr (λ p → f (ap fst p))) (Adec a a')

equiv-preserves-dec-eq : {i j : ULevel} {A : Type i} {B : Type j} → (A ≃ B) → (has-dec-eq B) →
  has-dec-eq A

equiv-preserves-dec-eq e Adec a a' = ⊔-rec (λ p → inl (–>-is-inj e a a' p))
  (λ f → inr (λ p → f (ap (–> e) p))) (Adec (–> e a) (–> e a'))


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

open import Level
open import Categories.Category using (Category; _[_,_])

-- Various conclusions that can be drawn from Yoneda
-- over a particular Category C
module Categories.Yoneda.Properties {o ℓ e : Level} (C : Category o ℓ e) where

open import Function.Base using (_$_)
open import Function.Bundles using (Inverse)
open import Function.Equality using (Π; _⟨$⟩_; cong)
open import Relation.Binary.Bundles using (module Setoid)
import Relation.Binary.Reasoning.Setoid as SetoidR
open import Data.Product using (_,_; Σ)

open import Categories.Category.Product
open import Categories.Category.Construction.Presheaves
open import Categories.Category.Construction.Functors
open import Categories.Category.Instance.Setoids
open import Categories.Functor renaming (id to idF)
open import Categories.Functor.Properties using (Full; Faithful; FullyFaithful)
open import Categories.Functor.Hom using (module Hom; Hom[_][-,_]; Hom[_][-,-])
open import Categories.Functor.Bifunctor
open import Categories.Functor.Presheaf
open import Categories.Functor.Construction.LiftSetoids
open import Categories.NaturalTransformation using (NaturalTransformation; ntHelper) renaming (id to idN)
open import Categories.NaturalTransformation.NaturalIsomorphism using (NaturalIsomorphism)
open import Categories.Yoneda

import Categories.Morphism as Mor
import Categories.Morphism.Reasoning as MR
import Categories.NaturalTransformation.Hom as NT-Hom

open Category C using (module HomReasoning; id; _∘_; identityˡ; identityʳ; Obj; ∘-resp-≈ˡ; ∘-resp-≈ʳ)
open HomReasoning
open NaturalTransformation using (η; commute)
open Yoneda C using (embed; yoneda-inverse; module yoneda)
private
  module CE = Category.Equiv C using (refl)
  module C = Category C using (op; id)

YoFull : Full embed
YoFull {X} {Y} = record
  { from             = record { _⟨$⟩_ = λ ε → η ε X ⟨$⟩ id ; cong = λ i≈j → i≈j CE.refl }
  ; right-inverse-of = λ ε {x} {z} {y} z≈y →
    begin
      (η ε X ⟨$⟩ id) ∘ z      ≈˘⟨ identityˡ ⟩
      id ∘ (η ε X ⟨$⟩ id) ∘ z ≈˘⟨ commute ε z CE.refl ⟩
      η ε x ⟨$⟩ id ∘ id ∘ z   ≈⟨ cong (η ε x) (identityˡ ○ identityˡ ○ z≈y) ⟩
      η ε x ⟨$⟩ y             ∎
  }

YoFaithful : Faithful embed
YoFaithful f g pres-≈ = begin
  f      ≈˘⟨ identityʳ ⟩
  f ∘ id ≈⟨ pres-≈ CE.refl ⟩
  g ∘ id ≈⟨ identityʳ ⟩
  g      ∎

YoFullyFaithful : FullyFaithful embed
YoFullyFaithful = YoFull , YoFaithful

open Mor C using (_≅_)

yoneda-iso : ∀ {A B : Obj} → NaturalIsomorphism Hom[ C ][-, A ] Hom[ C ][-, B ] → A ≅ B
yoneda-iso {A} {B} niso = record
  { from = ⇒.η A ⟨$⟩ id
  ; to   = ⇐.η B ⟨$⟩ id
  ; iso  = record
    { isoˡ = begin
      (⇐.η B ⟨$⟩ id) ∘ (⇒.η A ⟨$⟩ id)      ≈˘⟨ identityˡ ⟩
      id ∘ (⇐.η B ⟨$⟩ id) ∘ (⇒.η A ⟨$⟩ id) ≈⟨  B⇒A.inverseʳ F⇐G CE.refl  ⟩
      ⇐.η A ⟨$⟩ (⇒.η A ⟨$⟩ id)             ≈⟨ isoX.isoˡ CE.refl ⟩
      id                                   ∎
    ; isoʳ = begin
      (⇒.η A ⟨$⟩ id) ∘ (⇐.η B ⟨$⟩ id)      ≈˘⟨ identityˡ ⟩
      id ∘ (⇒.η A ⟨$⟩ id) ∘ (⇐.η B ⟨$⟩ id) ≈⟨  A⇒B.inverseʳ F⇒G CE.refl ⟩
      ⇒.η B ⟨$⟩ (⇐.η B ⟨$⟩ id)             ≈⟨ isoX.isoʳ CE.refl ⟩
      id                                   ∎
    }
  }
  where
  open NaturalIsomorphism niso
  A⇒B = yoneda-inverse A (Functor.F₀ embed B)
  B⇒A = yoneda-inverse B (Functor.F₀ embed A)
  module A⇒B = Inverse A⇒B using (inverseʳ)
  module B⇒A = Inverse B⇒A using (inverseʳ)
  module isoX {X} = Mor.Iso (iso X) using (isoʳ; isoˡ)

module _ {o′ ℓ′ e′} {D : Category o′ ℓ′ e′} where
  private
    module D = Category D using (id)

    module _ {F G : Functor D C} where
      private
        module F = Functor F using (₀; ₁; identity)
        module G = Functor G using (₀; ₁; identity)
        Hom[-,F-] : Bifunctor C.op D (Setoids ℓ e)
        Hom[-,F-] = Hom[ C ][-,-] ∘F (idF ⁂ F)

        Hom[-,G-] : Bifunctor C.op D (Setoids ℓ e)
        Hom[-,G-] = Hom[ C ][-,-] ∘F (idF ⁂ G)

      nat-appʳ : ∀ X → NaturalTransformation Hom[-,F-] Hom[-,G-] →
                       NaturalTransformation Hom[ C ][-, F.₀ X ] Hom[ C ][-, G.₀ X ]
      nat-appʳ X α = ntHelper record
        { η       = λ Y → η α (Y , X)
        ; commute = λ {_ Y} f eq → cong (η α (Y , X)) (∘-resp-≈ˡ (⟺ F.identity)) ○ commute α (f , D.id) eq ○ ∘-resp-≈ˡ G.identity
        }

      transform : NaturalTransformation Hom[-,F-] Hom[-,G-] → NaturalTransformation F G
      transform α = ntHelper record
        { η       = λ X → η α (F.₀ X , X) ⟨$⟩ id
        ; commute = λ {X Y} f → begin
          (η α (F.₀ Y , Y) ⟨$⟩ id) ∘ F.₁ f       ≈˘⟨ identityˡ ⟩
          id ∘ (η α (F.₀ Y , Y) ⟨$⟩ id) ∘ F.₁ f  ≈˘⟨ lower (yoneda.⇒.commute {Y = Hom[ C ][-, G.₀ Y ] , _} (idN , F.₁ f) {nat-appʳ Y α} {nat-appʳ Y α} (cong (η α _))) ⟩
          η α (F.₀ X , Y) ⟨$⟩ F.₁ f ∘ id         ≈⟨ cong (η α (F.₀ X , Y)) (∘-resp-≈ʳ (⟺ identityˡ)) ⟩
          η α (F.₀ X , Y) ⟨$⟩ F.₁ f ∘ id ∘ id    ≈⟨ commute α (id , f) CE.refl ⟩
          G.₁ f ∘ (η α (F.₀ X , X) ⟨$⟩ id) ∘ id  ≈⟨ refl⟩∘⟨ identityʳ ⟩
          G.₁ f ∘ (η α (F.₀ X , X) ⟨$⟩ id)      ∎
        }

  module _ (F G : Functor D C) where
    private
      module F = Functor F using (₀)
      module G = Functor G using (₀)
      Hom[-,F-] : Bifunctor C.op D (Setoids ℓ e)
      Hom[-,F-] = Hom[ C ][-,-] ∘F (idF ⁂ F)

      Hom[-,G-] : Bifunctor C.op D (Setoids ℓ e)
      Hom[-,G-] = Hom[ C ][-,-] ∘F (idF ⁂ G)

    -- The implicits given below are sometimes needed (yellow), sometimes make an enormous difference in
    -- typechecking time. For example, in yoneda.⇒.commute, "nat-appʳ X F⇐G" is needed, while
    -- nat-appʳ {F = G} {F} X F⇐G makes a huge difference in checking time.
    yoneda-NI : NaturalIsomorphism Hom[-,F-] Hom[-,G-] → NaturalIsomorphism F G
    yoneda-NI ni = record
      { F⇒G = transform F⇒G
      ; F⇐G = transform F⇐G
      ; iso = λ X → record
        { isoˡ = begin
          (⇐.η (G.₀ X , X) ⟨$⟩ id) ∘ (⇒.η (F.₀ X , X) ⟨$⟩ id)      ≈⟨ introˡ CE.refl ⟩
          id ∘ (⇐.η (G.₀ X , X) ⟨$⟩ id) ∘ (⇒.η (F.₀ X , X) ⟨$⟩ id) ≈˘⟨ lower {e} (yoneda.⇒.commute {Y = Hom[ C ][-, F.₀ X ] , _}
                                                                                (idN , (⇒.η (F.₀ X , X) ⟨$⟩ id))
                                                                                {nat-appʳ {F = G} {F} X F⇐G}
                                                                                {nat-appʳ {F = G} {F} X F⇐G}
                                                                                (cong (⇐.η (_ , X) ))) ⟩
          ⇐.η (F.₀ X , X) ⟨$⟩ (⇒.η (F.₀ X , X) ⟨$⟩ id) ∘ id        ≈⟨ cong (⇐.η _) identityʳ ⟩
          ⇐.η (F.₀ X , X) ⟨$⟩ (⇒.η (F.₀ X , X) ⟨$⟩ id)             ≈⟨ iso.isoˡ _ CE.refl ⟩
          id                                                  ∎
        ; isoʳ = begin
          (⇒.η (F.₀ X , X) ⟨$⟩ id) ∘ (⇐.η (G.₀ X , X) ⟨$⟩ id)      ≈⟨ introˡ CE.refl ⟩
          id ∘ (⇒.η (F.₀ X , X) ⟨$⟩ id) ∘ (⇐.η (G.₀ X , X) ⟨$⟩ id) ≈˘⟨ lower {e} (yoneda.⇒.commute {Y = Hom[ C ][-, G.₀ X ] , _}
                                                                                (idN , (⇐.η (G.₀ X , X) ⟨$⟩ C.id))
                                                                                {nat-appʳ {F = F} {G} X F⇒G}
                                                                                {nat-appʳ {F = F} {G} X F⇒G}
                                                                                (cong (⇒.η _))) ⟩
          ⇒.η (G.₀ X , X) ⟨$⟩ (⇐.η (G.₀ X , X) ⟨$⟩ id) ∘ id        ≈⟨ cong (⇒.η _) identityʳ ⟩
          ⇒.η (G.₀ X , X) ⟨$⟩ (⇐.η (G.₀ X , X) ⟨$⟩ id)             ≈⟨ iso.isoʳ _ CE.refl ⟩
          id                                                      ∎
        }
      }
      where
      open NaturalIsomorphism ni using (F⇒G; F⇐G; module ⇐; module ⇒; module iso)
      open MR C using (introˡ)


module DFA where

open import Level
open import Data.Unit
open import Data.List using (List; []; _∷_)
open import Data.Bool using (Bool; true; false; _∧_; _∨_; T; not)
open import Function
open import Relation.Nullary using (Dec; yes; no; ¬_)
open import Relation.Nullary.Negation using (contradiction)
open import Relation.Binary

open DecSetoid

String : (Σ : Set) → Set
String Σ = List Σ

-- A deterministic finite automaton is a 5-tuple, (Q, Σ, δ, q0, F)
record DFA (Q : DecSetoid zero zero) (Σ : Set): Set₁ where
    constructor dfa
    field
        δ : Carrier Q → Σ → Carrier Q
        initial : Carrier Q
        accepts : Carrier Q → Bool

open DFA

-- eats a character
step : ∀ {Q Σ} → DFA Q Σ → Σ → DFA Q Σ
step (dfa δ initial accept) char = dfa δ (δ initial char) accept

-- eats a lot of characters
steps : ∀ {Q Σ} → DFA Q Σ → String Σ → DFA Q Σ
steps machine []       = machine
steps machine (x ∷ xs) = steps (step machine x) xs

-- a machine M is acceptable if its initial state is in the accept states
acceptable : ∀ {Q Σ} → DFA Q Σ → Bool
acceptable (dfa δ initial accepts) = accepts initial

--------------------------------------------------------------------------------
--  language and automaton
--------------------------------------------------------------------------------

open import Relation.Unary hiding (Decidable)

Language : (Σ : Set) → Set _
Language Σ = Pred (String Σ) zero

-- machine ⇒ language
⟦_⟧ : ∀ {P Σ} → DFA P Σ → Language Σ
⟦ machine ⟧ = λ string → T (acceptable (steps machine string))

_∈?_ : ∀ {P Σ} → (string : String Σ) → (M : DFA P Σ) → Dec (string ∈ ⟦ M ⟧)
string ∈? machine with acceptable (steps machine string)
string ∈? machine | false = no id
string ∈? machine | true = yes tt

--------------------------------------------------------------------------------
--  product and sum
--------------------------------------------------------------------------------

open import Data.Product
open import Relation.Binary.Product.Pointwise

-- a helper function for constructing a transition function with product states
product-δ : ∀ {P Q Σ} → DFA P Σ → DFA Q Σ → Carrier P × Carrier Q → Σ → Carrier P × Carrier Q
product-δ A B (p , q) c = δ A p c , δ B q c

-- intersection
infixl 30 _∩ᴹ_
_∩ᴹ_ : ∀ {P Q Σ} → DFA P Σ → DFA Q Σ → DFA (P ×-decSetoid Q) Σ
A ∩ᴹ B = dfa
    (product-δ A B)
    (initial A , initial B)
    (uncurry (λ p q → accepts A p ∧ accepts B q))

-- union
infixl 29 _∪ᴹ_
_∪ᴹ_ : ∀ {P Q Σ} → DFA P Σ → DFA Q Σ → DFA (P ×-decSetoid Q) Σ
A ∪ᴹ B = dfa
    (product-δ A B)
    (initial A , initial B)
    (uncurry (λ p q → accepts A p ∨ accepts B q))

-- negation
infixl 31 Cᴹ_
Cᴹ_ : ∀ {P Σ} → DFA P Σ → DFA P Σ
Cᴹ dfa δ initial accepts = dfa δ initial (not ∘ accepts)

--------------------------------------------------------------------------------
--  concatenation
--------------------------------------------------------------------------------

open import Data.Sum
open import Relation.Binary.Sum
open import Data.Empty

module Pointwise where
    -- _⊎-decSetoid_ : ∀ {d₁ d₂ d₃ d₄}
    --     → DecSetoid d₁ d₂
    --     → DecSetoid d₃ d₄
    --     → DecSetoid _ _
    -- _⊎-decSetoid_ {d₁} {d₂} {d₃} {d₄} s₁ s₂ = record
    --     { Carrier = Carrier s₁ ⊎ Carrier s₂
    --     ; _≈_ = _⊎-≈_
    --     ; isDecEquivalence = record
    --         { isEquivalence = ⊎-≈-isEquivalence
    --         ; _≟_ = ⊎-≈-decidable
    --         }
    --     }
    --     where
    --
    --         open DecSetoid
    --         _⊎-≈_ : Carrier s₁ ⊎ Carrier s₂
    --             → Carrier s₁ ⊎ Carrier s₂
    --             → Set (d₂ ⊔ d₄)
    --         _⊎-≈_ (inj₁ x) (inj₁ y) = Lift {d₂} {d₄} (_≈_ s₁ x y)  -- (_≈_ s₁ x y)
    --         _⊎-≈_ (inj₁ x) (inj₂ y) = Lift ⊥
    --         _⊎-≈_ (inj₂ x) (inj₁ y) = Lift ⊥
    --         _⊎-≈_ (inj₂ x) (inj₂ y) = Lift {d₄} {d₂} (_≈_ s₂ x y)
    --
    --         open IsEquivalence
    --         module S₁ = IsDecEquivalence (isDecEquivalence s₁)
    --         module S₂ = IsDecEquivalence (isDecEquivalence s₂)
    --
    --         ⊎-≈-refl : Reflexive _⊎-≈_
    --         ⊎-≈-refl {inj₁ x} = lift (refl S₁.isEquivalence)
    --         ⊎-≈-refl {inj₂ y} = lift (refl S₂.isEquivalence)
    --
    --         ⊎-≈-sym : Symmetric _⊎-≈_
    --         ⊎-≈-sym {inj₁ x} {inj₁ y} (lift eq) = lift (sym S₁.isEquivalence eq)
    --         ⊎-≈-sym {inj₁ x} {inj₂ y} (lift ())
    --         ⊎-≈-sym {inj₂ x} {inj₁ y} (lift ())
    --         ⊎-≈-sym {inj₂ x} {inj₂ y} (lift eq) = lift (sym S₂.isEquivalence eq)
    --
    --         ⊎-≈-trans : Transitive _⊎-≈_
    --         ⊎-≈-trans {inj₁ x} {inj₁ y} {inj₁ z} (lift p) (lift q) = lift (trans S₁.isEquivalence p q)
    --         ⊎-≈-trans {inj₁ x} {inj₁ y} {inj₂ z} p        (lift ())
    --         ⊎-≈-trans {inj₁ x} {inj₂ y} {_}      (lift ()) q
    --         ⊎-≈-trans {inj₂ x} {inj₁ y} {_}      (lift ()) q
    --         ⊎-≈-trans {inj₂ x} {inj₂ y} {inj₁ z} p        (lift ())
    --         ⊎-≈-trans {inj₂ x} {inj₂ y} {inj₂ z} (lift p) (lift q) = lift (trans S₂.isEquivalence p q)
    --
    --         ⊎-≈-isEquivalence : IsEquivalence _⊎-≈_
    --         ⊎-≈-isEquivalence = record
    --             { refl = λ {x} → ⊎-≈-refl {x}
    --             ; sym = λ {x} {y} eq → ⊎-≈-sym {x} {y} eq
    --             ; trans = λ {x} {y} {z} p q → ⊎-≈-trans {x} {y} {z} p q
    --             }
    --
    --         lift-dec : ∀ {a} {b} {P : Set a} → Dec P → Dec (Lift {a} {b} P)
    --         lift-dec (yes p) = yes (lift p)
    --         lift-dec (no ¬p) = no (λ p → contradiction (lower p) ¬p)
    --
    --         ⊎-≈-decidable : Decidable _⊎-≈_
    --         ⊎-≈-decidable (inj₁ x) (inj₁ y) = lift-dec (S₁._≟_ x y)
    --         ⊎-≈-decidable (inj₁ x) (inj₂ y) = no lower
    --         ⊎-≈-decidable (inj₂ x) (inj₁ y) = no lower
    --         ⊎-≈-decidable (inj₂ x) (inj₂ y) = lift-dec (S₂._≟_ x y)

infixl 28 _++_
_++_ : ∀ {P Q Σ} → DFA P Σ → DFA Q Σ → DFA (P ⊎-decSetoid Q) Σ
_++_ {P} {Q} {Σ} A B = dfa
    δ'
    (inj₁ (initial A))
    accepts'
    where
        δ' : Carrier (P ⊎-decSetoid Q) → Σ → Carrier (P ⊎-decSetoid Q)
        δ' (inj₁ state) character with acceptable (step A character)
        δ' (inj₁ state) character | false = inj₁ (δ A state character)
        δ' (inj₁ state) character | true = inj₂ (initial B)
        δ' (inj₂ state) character = inj₂ (δ B state character)

        accepts' : Carrier (P ⊎-decSetoid Q) → Bool
        accepts' (inj₁ state) = false
        accepts' (inj₂ state) = accepts B state

-- -- ++-right-identity : {P Q Σ : Set}
-- --     → (A : DFA P Σ) → (B : DFA Q Σ)
-- --     → Empty B
-- --     → DFA (P ⊎ Q) Σ
--
--------------------------------------------------------------------------------
--  Kleene closure
--------------------------------------------------------------------------------

-- 1. make the initial state the accept state
-- 2. wire all of the transitions that would've gone to the original accept
--    states to the initial state (which is the new accept state)
infixl 32 _*
_* : ∀ {Q Σ} → DFA Q Σ → DFA Q Σ
_* {Q} {Σ} M = dfa δ' (initial M) accepts'
    where

        δ' : Carrier Q → Σ → Carrier Q
        δ' state character with acceptable (step M character)
        δ' state character | false = δ M state character
        δ' state character | true = initial M

        open IsDecEquivalence (isDecEquivalence Q) renaming (_≟_ to _≟s_)
        open import Relation.Nullary.Decidable
        accepts' : Carrier Q → Bool
        accepts' state = ⌊ state ≟s initial M ⌋

--------------------------------------------------------------------------------
--  Properties
--------------------------------------------------------------------------------

module Properties (Σ : Set) where

    open import Function.Equality using (Π)
    open Π
    open import Function.Equivalence using (_⇔_; Equivalence)
    open import Data.Bool.Properties as BoolProp
    open import Relation.Nullary.Negation
    -- extracting one direction of a function equivalence
    ⟦_⟧→ : {A B : Set} → A ⇔ B → A → B
    ⟦ A⇔B ⟧→ A = Equivalence.to A⇔B ⟨$⟩ A

    ←⟦_⟧ : {A B : Set} → A ⇔ B → B → A
    ←⟦ A⇔B ⟧ B = Equivalence.from A⇔B ⟨$⟩ B

    open import Relation.Unary.Membership (String Σ)
    open ⊆-Reasoning

    ∩-homo⇒ : ∀ {P Q} → (A : DFA P Σ) → (B : DFA Q Σ)
        → ⟦ A ∩ᴹ B ⟧ ⊆′ ⟦ A ⟧ ∩ ⟦ B ⟧
    ∩-homo⇒ A B []       = ⟦ BoolProp.T-∧ ⟧→
    ∩-homo⇒ A B (x ∷ xs) = ∩-homo⇒ (step A x) (step B x) xs

    ∩-homo⇐ : ∀ {P Q} → (A : DFA P Σ) → (B : DFA Q Σ)
        → ⟦ A ⟧ ∩ ⟦ B ⟧ ⊆′ ⟦ A ∩ᴹ B ⟧
    ∩-homo⇐ A B []       = ←⟦ BoolProp.T-∧ ⟧
    ∩-homo⇐ A B (x ∷ xs) = ∩-homo⇐ (step A x) (step B x) xs

    ∩-homo : ∀ {P Q} → (A : DFA P Σ) → (B : DFA Q Σ)
        → ⟦ A ∩ᴹ B ⟧ ≋ ⟦ A ⟧ ∩ ⟦ B ⟧
    ∩-homo A B = (∩-homo⇒ A B) , (∩-homo⇐ A B)

    ∩-sym⇒ : ∀ {P Q} → (A : DFA P Σ) → (B : DFA Q Σ)
        → ⟦ A ∩ᴹ B ⟧ ⊆′ ⟦ B ∩ᴹ A ⟧
    ∩-sym⇒ A B xs P =
            xs
        ∈⟨ P ⟩
            ⟦ A ∩ᴹ B ⟧
        ⊆⟨ ∩-homo⇒ A B ⟩
            ⟦ A ⟧ ∩ ⟦ B ⟧
        →⟨ swap ⟩
            ⟦ B ⟧ ∩ ⟦ A ⟧
        ⊆⟨ ∩-homo⇐ B A ⟩
            ⟦ B ∩ᴹ A ⟧
        ∎

    ∩-sym : ∀ {P Q} → (A : DFA P Σ) → (B : DFA Q Σ)
        → ⟦ A ∩ᴹ B ⟧ ≋ ⟦ B ∩ᴹ A ⟧
    ∩-sym A B = (∩-sym⇒ A B) , (∩-sym⇒ B A)

    ∪-homo⇒ : ∀ {P Q} → (A : DFA P Σ) → (B : DFA Q Σ)
        → ⟦ A ∪ᴹ B ⟧ ⊆′ ⟦ A ⟧ ∪ ⟦ B ⟧
    ∪-homo⇒ A B []       P = ⟦ BoolProp.T-∨ ⟧→ P
    ∪-homo⇒ A B (x ∷ xs) P = ∪-homo⇒ (step A x) (step B x) xs P

    ∪-homo⇐ : ∀ {P Q} → (A : DFA P Σ) → (B : DFA Q Σ)
        → ⟦ A ⟧ ∪ ⟦ B ⟧ ⊆′ ⟦ A ∪ᴹ B ⟧
    ∪-homo⇐ A B []       P = ←⟦ BoolProp.T-∨ ⟧ P
    ∪-homo⇐ A B (x ∷ xs) P = ∪-homo⇐ (step A x) (step B x) xs P

    ∪-homo : ∀ {P Q} → (A : DFA P Σ) → (B : DFA Q Σ)
        → ⟦ A ∪ᴹ B ⟧ ≋ ⟦ A ⟧ ∪ ⟦ B ⟧
    ∪-homo A B = (∪-homo⇒ A B) , (∪-homo⇐ A B)

    ∪-sym⇒ : ∀ {P Q} → (A : DFA P Σ) → (B : DFA Q Σ)
        → ⟦ A ∪ᴹ B ⟧ ⊆′ ⟦ B ∪ᴹ A ⟧
    ∪-sym⇒ A B xs P =
            xs
        ∈⟨ P ⟩
            ⟦ A ∪ᴹ B ⟧
        ⊆⟨ ∪-homo⇒ A B ⟩
            ⟦ A ⟧ ∪ ⟦ B ⟧
        →⟨ ⊎-swap ⟩
            ⟦ B ⟧ ∪ ⟦ A ⟧
        ⊆⟨ ∪-homo⇐ B A ⟩
            ⟦ B ∪ᴹ A ⟧
        ∎
        where
            ⊎-swap : ∀ {a b} {A : Set a} {B : Set b} → A ⊎ B → B ⊎ A
            ⊎-swap (inj₁ x) = inj₂ x
            ⊎-swap (inj₂ y) = inj₁ y

    ∪-sym : ∀ {P Q} → (A : DFA P Σ) → (B : DFA Q Σ)
        → ⟦ A ∪ᴹ B ⟧ ≋ ⟦ B ∪ᴹ A ⟧
    ∪-sym A B = (∪-sym⇒ A B) , (∪-sym⇒ B A)

    T-not-¬ : {b : Bool} → T (not b) → ¬ (T b)
    T-not-¬ {false} P Q = Q
    T-not-¬ {true}  P Q = P

    T-¬-not : {b : Bool} → ¬ (T b) → T (not b)
    T-¬-not {false} P = tt
    T-¬-not {true}  P = P tt

    C⇒ : ∀ {P} → (M : DFA P Σ)
        → ∁ ⟦ M ⟧ ⊆ ⟦ Cᴹ M ⟧
    C⇒ M {[]}     = T-¬-not
    C⇒ M {x ∷ xs} = C⇒ (step M x) {xs}

    C⇐ : ∀ {P} → (M : DFA P Σ)
        → ⟦ Cᴹ M ⟧ ⊆ ∁ ⟦ M ⟧
    C⇐ M {[]}     = T-not-¬
    C⇐ M {x ∷ xs} = C⇐ (step M x) {xs}

    C-∩-∅⇒ : ∀ {P} → (M : DFA P Σ) → ⟦ M ∩ᴹ Cᴹ M ⟧ ⊆ ∅
    C-∩-∅⇒ M {[]} P =
        let
            Q , ¬Q = ∩-homo⇒ M (Cᴹ M) [] P
        in contradiction Q (C⇐ M {[]} ¬Q)
    C-∩-∅⇒ M {x ∷ xs} = C-∩-∅⇒ (step M x) {xs}

    C-∩-∅⇐ : ∀ {P} → (M : DFA P Σ) → ∅ ⊆ ⟦ M ∩ᴹ Cᴹ M ⟧
    C-∩-∅⇐ M {[]} ()
    C-∩-∅⇐ M {x ∷ xs} = C-∩-∅⇐ (step M x) {xs}

    C-∪-U⇒ : ∀ {P} → (M : DFA P Σ) → ⟦ M ∪ᴹ Cᴹ M ⟧ ⊆ U
    C-∪-U⇒ M {[]} P = tt
    C-∪-U⇒ M {x ∷ xs} = C-∪-U⇒ (step M x) {xs}

    C-∪-U⇐ : ∀ {P} → (M : DFA P Σ) → U ⊆ ⟦ M ∪ᴹ Cᴹ M ⟧
    C-∪-U⇐ M {[]} P with [] ∈? M
    C-∪-U⇐ M {[]} P | yes p = ←⟦ T-∨ ⟧ (inj₁ p)
    C-∪-U⇐ M {[]} P | no ¬p = ←⟦ T-∨
            {accepts M (initial M)}
            {not (accepts M (initial M))}
        ⟧ (inj₂ (T-¬-not ¬p))
    C-∪-U⇐ M {x ∷ xs} = C-∪-U⇐ (step M x) {xs}


{-# OPTIONS --safe --experimental-lossy-unification #-}
module Cubical.Algebra.Ring.Properties where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Function
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Equiv.HalfAdjoint
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.Univalence
open import Cubical.Foundations.Transport
open import Cubical.Foundations.SIP
open import Cubical.Foundations.GroupoidLaws
open import Cubical.Foundations.Path

open import Cubical.Data.Sigma
open import Cubical.Relation.Binary.Poset

open import Cubical.Structures.Axioms
open import Cubical.Structures.Auto
open import Cubical.Structures.Macro
open import Cubical.Algebra.Semigroup
open import Cubical.Algebra.Monoid
open import Cubical.Algebra.AbGroup
open import Cubical.Algebra.Ring.Base

open import Cubical.HITs.PropositionalTruncation

private
  variable
    ℓ ℓ' ℓ'' ℓ''' ℓ'''' : Level

{-
  some basic calculations (used for example in QuotientRing.agda),
  that should become obsolete or subject to change once we have a
  ring solver (see https://github.com/agda/cubical/issues/297)
-}
module RingTheory (R' : Ring ℓ) where

  open RingStr (snd R')
  private R = ⟨ R' ⟩
  implicitInverse : (x y : R)
                 → x + y ≡ 0r
                 → y ≡ - x
  implicitInverse x y p =
    y               ≡⟨ sym (+Lid y) ⟩
    0r + y          ≡⟨ cong (λ u → u + y) (sym (+Linv x)) ⟩
    (- x + x) + y   ≡⟨ sym (+Assoc _ _ _) ⟩
    (- x) + (x + y) ≡⟨ cong (λ u → (- x) + u) p ⟩
    (- x) + 0r      ≡⟨ +Rid _ ⟩
    - x             ∎

  equalByDifference : (x y : R)
                      → x - y ≡ 0r
                      → x ≡ y
  equalByDifference x y p =
    x               ≡⟨ sym (+Rid _) ⟩
    x + 0r          ≡⟨ cong (λ u → x + u) (sym (+Linv y)) ⟩
    x + ((- y) + y) ≡⟨ +Assoc _ _ _ ⟩
    (x - y) + y     ≡⟨ cong (λ u → u + y) p ⟩
    0r + y          ≡⟨ +Lid _ ⟩
    y               ∎

  0Selfinverse : - 0r ≡ 0r
  0Selfinverse = sym (implicitInverse _ _ (+Rid 0r))

  0Idempotent : 0r + 0r ≡ 0r
  0Idempotent = +Lid 0r

  +Idempotency→0 : (x : R) → x ≡ x + x → x ≡ 0r
  +Idempotency→0 x p =
    x               ≡⟨ sym (+Rid x) ⟩
    x + 0r          ≡⟨ cong (λ u → x + u) (sym (+Rinv _)) ⟩
    x + (x + (- x)) ≡⟨ +Assoc _ _ _ ⟩
    (x + x) + (- x) ≡⟨ cong (λ u → u + (- x)) (sym p) ⟩
    x + (- x)       ≡⟨ +Rinv _ ⟩
    0r              ∎

  -Idempotent : (x : R) → -(- x) ≡ x
  -Idempotent x =  - (- x)   ≡⟨ sym (implicitInverse (- x) x (+Linv _)) ⟩
                   x ∎

  0RightAnnihilates : (x : R) → x · 0r ≡ 0r
  0RightAnnihilates x =
              let x·0-is-idempotent : x · 0r ≡ x · 0r + x · 0r
                  x·0-is-idempotent =
                    x · 0r               ≡⟨ cong (λ u → x · u) (sym 0Idempotent) ⟩
                    x · (0r + 0r)        ≡⟨ ·Rdist+ _ _ _ ⟩
                    (x · 0r) + (x · 0r)  ∎
              in (+Idempotency→0 _ x·0-is-idempotent)

  0LeftAnnihilates : (x : R) → 0r · x ≡ 0r
  0LeftAnnihilates x =
              let 0·x-is-idempotent : 0r · x ≡ 0r · x + 0r · x
                  0·x-is-idempotent =
                    0r · x               ≡⟨ cong (λ u → u · x) (sym 0Idempotent) ⟩
                    (0r + 0r) · x        ≡⟨ ·Ldist+ _ _ _ ⟩
                    (0r · x) + (0r · x)  ∎
              in +Idempotency→0 _ 0·x-is-idempotent

  -DistR· : (x y : R) →  x · (- y) ≡ - (x · y)
  -DistR· x y = implicitInverse (x · y) (x · (- y))

                               (x · y + x · (- y)     ≡⟨ sym (·Rdist+ _ _ _) ⟩
                               x · (y + (- y))        ≡⟨ cong (λ u → x · u) (+Rinv y) ⟩
                               x · 0r                 ≡⟨ 0RightAnnihilates x ⟩
                               0r ∎)

  -DistL· : (x y : R) →  (- x) · y ≡ - (x · y)
  -DistL· x y = implicitInverse (x · y) ((- x) · y)

                              (x · y + (- x) · y     ≡⟨ sym (·Ldist+ _ _ _) ⟩
                              (x - x) · y            ≡⟨ cong (λ u → u · y) (+Rinv x) ⟩
                              0r · y                 ≡⟨ 0LeftAnnihilates y ⟩
                              0r ∎)

  -Swap· : (x y : R) → (- x) · y ≡ x · (- y)
  -Swap· _ _ = -DistL· _ _ ∙ sym (-DistR· _ _)

  -IsMult-1 : (x : R) → - x ≡ (- 1r) · x
  -IsMult-1 _ = sym (·Lid _) ∙ sym (-Swap· _ _)

  -Dist : (x y : R) → (- x) + (- y) ≡ - (x + y)
  -Dist x y =
    implicitInverse _ _
         ((x + y) + ((- x) + (- y)) ≡⟨ sym (+Assoc _ _ _) ⟩
          x + (y + ((- x) + (- y))) ≡⟨ cong
                                         (λ u → x + (y + u))
                                         (+Comm _ _) ⟩
          x + (y + ((- y) + (- x))) ≡⟨ cong (λ u → x + u) (+Assoc _ _ _) ⟩
          x + ((y + (- y)) + (- x)) ≡⟨ cong (λ u → x + (u + (- x)))
                                            (+Rinv _) ⟩
          x + (0r + (- x))           ≡⟨ cong (λ u → x + u) (+Lid _) ⟩
          x + (- x)                 ≡⟨ +Rinv _ ⟩
          0r ∎)

  translatedDifference : (x a b : R) → a - b ≡ (x + a) - (x + b)
  translatedDifference x a b =
              a - b                       ≡⟨ cong (λ u → a + u)
                                                  (sym (+Lid _)) ⟩
              (a + (0r + (- b)))          ≡⟨ cong (λ u → a + (u + (- b)))
                                                  (sym (+Rinv _)) ⟩
              (a + ((x + (- x)) + (- b))) ≡⟨ cong (λ u → a + u)
                                                  (sym (+Assoc _ _ _)) ⟩
              (a + (x + ((- x) + (- b)))) ≡⟨ (+Assoc _ _ _) ⟩
              ((a + x) + ((- x) + (- b))) ≡⟨ cong (λ u → u + ((- x) + (- b)))
                                                  (+Comm _ _) ⟩
              ((x + a) + ((- x) + (- b))) ≡⟨ cong (λ u → (x + a) + u)
                                                  (-Dist _ _) ⟩
              ((x + a) - (x + b)) ∎

  +Assoc-comm1 : (x y z : R) → x + (y + z) ≡ y + (x + z)
  +Assoc-comm1 x y z = +Assoc x y z ∙∙ cong (λ x → x + z) (+Comm x y) ∙∙ sym (+Assoc y x z)

  +Assoc-comm2 : (x y z : R) → x + (y + z) ≡ z + (y + x)
  +Assoc-comm2 x y z = +Assoc-comm1 x y z ∙∙ cong (λ x → y + x) (+Comm x z) ∙∙ +Assoc-comm1 y z x

  +ShufflePairs : (a b c d : R)
                → (a + b) + (c + d) ≡ (a + c) + (b + d)
  +ShufflePairs a b c d =
    (a + b) + (c + d) ≡⟨ +Assoc _ _ _ ⟩
    ((a + b) + c) + d ≡⟨ cong (λ u → u + d) (sym (+Assoc _ _ _)) ⟩
    (a + (b + c)) + d ≡⟨ cong (λ u → (a + u) + d) (+Comm _ _) ⟩
    (a + (c + b)) + d ≡⟨ cong (λ u → u + d) (+Assoc _ _ _) ⟩
    ((a + c) + b) + d ≡⟨ sym (+Assoc _ _ _) ⟩
    (a + c) + (b + d) ∎

  ·-assoc2 : (x y z w : R) → (x · y) · (z · w) ≡ x · (y · z) · w
  ·-assoc2 x y z w = ·Assoc (x · y) z w ∙ cong (_· w) (sym (·Assoc x y z))

module RingHoms where
  open IsRingHom

  idRingHom : (R : Ring ℓ) → RingHom R R
  fst (idRingHom R) = idfun (fst R)
  snd (idRingHom R) = makeIsRingHom refl (λ _ _ → refl) (λ _ _ → refl)

  compIsRingHom : {A : Ring ℓ} {B : Ring ℓ'} {C : Ring ℓ''}
    {g : ⟨ B ⟩ → ⟨ C ⟩} {f : ⟨ A ⟩ → ⟨ B ⟩}
    → IsRingHom (B .snd) g (C .snd)
    → IsRingHom (A .snd) f (B .snd)
    → IsRingHom (A .snd) (g ∘ f) (C .snd)
  compIsRingHom {g = g} {f} gh fh .pres0 = cong g (fh .pres0) ∙ gh .pres0
  compIsRingHom {g = g} {f} gh fh .pres1 = cong g (fh .pres1) ∙ gh .pres1
  compIsRingHom {g = g} {f} gh fh .pres+ x y = cong g (fh .pres+ x y) ∙ gh .pres+ (f x) (f y)
  compIsRingHom {g = g} {f} gh fh .pres· x y = cong g (fh .pres· x y) ∙ gh .pres· (f x) (f y)
  compIsRingHom {g = g} {f} gh fh .pres- x = cong g (fh .pres- x) ∙ gh .pres- (f x)

  compRingHom : {R : Ring ℓ} {S : Ring ℓ'} {T : Ring ℓ''}
              → RingHom R S → RingHom S T → RingHom R T
  fst (compRingHom f g) x = g .fst (f .fst x)
  snd (compRingHom f g) = compIsRingHom (g .snd) (f .snd)

  syntax compRingHom f g = g ∘r f

  compIdRingHom : {R S : Ring ℓ} (φ : RingHom R S) → compRingHom (idRingHom R) φ ≡ φ
  compIdRingHom φ = RingHom≡ refl

  idCompRingHom : {R S : Ring ℓ} (φ : RingHom R S) → compRingHom φ (idRingHom S) ≡ φ
  idCompRingHom φ = RingHom≡ refl

  compAssocRingHom : {R S T U : Ring ℓ} (φ : RingHom R S) (ψ : RingHom S T) (χ : RingHom T U) →
                     compRingHom (compRingHom φ ψ) χ ≡ compRingHom φ (compRingHom ψ χ)
  compAssocRingHom _ _ _ = RingHom≡ refl

module RingEquivs where
  open RingStr
  open IsRingHom
  open RingHoms

  compIsRingEquiv : {A : Ring ℓ} {B : Ring ℓ'} {C : Ring ℓ''}
    {g : ⟨ B ⟩ ≃ ⟨ C ⟩} {f : ⟨ A ⟩ ≃ ⟨ B ⟩}
    → IsRingEquiv (B .snd) g (C .snd)
    → IsRingEquiv (A .snd) f (B .snd)
    → IsRingEquiv (A .snd) (compEquiv f g) (C .snd)
  compIsRingEquiv {g = g} {f} gh fh = compIsRingHom {g = g .fst} {f .fst} gh fh

  compRingEquiv : {A : Ring ℓ} {B : Ring ℓ'} {C : Ring ℓ''}
                → RingEquiv A B → RingEquiv B C → RingEquiv A C
  fst (compRingEquiv f g) = compEquiv (f .fst) (g .fst)
  snd (compRingEquiv f g) = compIsRingEquiv {g = g .fst} {f = f .fst} (g .snd) (f .snd)

  isRingHomInv : {A : Ring ℓ} → {B : Ring ℓ'} → (e : RingEquiv A B) → IsRingHom (snd B) (invEq (fst e)) (snd A)
  isRingHomInv {A = A} {B = B} e = makeIsRingHom
                         ((cong g (sym (pres1 fcrh))) ∙ retEq et (1r (snd A)))
                         (λ x y → g (snd B ._+_ x y)                 ≡⟨ cong g (sym (cong₂ (snd B ._+_) (secEq et x) (secEq et y))) ⟩
                                   g (snd B ._+_ (f (g x)) (f (g y))) ≡⟨ cong g (sym (pres+ fcrh (g x) (g y))) ⟩
                                   g (f (snd A ._+_ (g x) (g y)))     ≡⟨ retEq et (snd A ._+_ (g x) (g y)) ⟩
                                   snd A ._+_ (g x) (g y)  ∎)
                         (λ x y → g (snd B ._·_ x y)                 ≡⟨ cong g (sym (cong₂ (snd B ._·_) (secEq et x) (secEq et y))) ⟩
                                   g (snd B ._·_ (f (g x)) (f (g y))) ≡⟨ cong g (sym (pres· fcrh (g x) (g y))) ⟩
                                   g (f (snd A ._·_ (g x) (g y)))     ≡⟨ retEq et (snd A ._·_ (g x) (g y)) ⟩
                                   snd A ._·_ (g x) (g y)  ∎)
               where
               et = fst e
               f = fst et
               fcrh = snd e
               g = invEq et

  invEquivRing : {A : Ring ℓ} → {B : Ring ℓ'} → RingEquiv A B → RingEquiv B A
  fst (invEquivRing e) = invEquiv (fst e)
  snd (invEquivRing e) = isRingHomInv e

module RingHomTheory {R S : Ring ℓ} (φ : RingHom R S) where
  open RingTheory ⦃...⦄
  open RingStr ⦃...⦄
  open IsRingHom (φ .snd)
  private
    instance
      _ = R
      _ = S
      _ = snd R
      _ = snd S
    f = fst φ

  ker≡0→inj : ({x : ⟨ R ⟩} → f x ≡ 0r → x ≡ 0r)
            → ({x y : ⟨ R ⟩} → f x ≡ f y → x ≡ y)
  ker≡0→inj ker≡0 {x} {y} p = equalByDifference _ _ (ker≡0 path)
   where
   path : f (x - y) ≡ 0r
   path = f (x - y)     ≡⟨ pres+ _ _ ⟩
          f x + f (- y) ≡⟨ cong (f x +_) (pres- _) ⟩
          f x - f y     ≡⟨ cong (_- f y) p ⟩
          f y - f y     ≡⟨ +Rinv _ ⟩
          0r            ∎

-- the Ring version of uaCompEquiv
module RingUAFunctoriality where
 open RingStr
 open RingEquivs

 Ring≡ : (A B : Ring ℓ) → (
   Σ[ p ∈ ⟨ A ⟩ ≡ ⟨ B ⟩ ]
   Σ[ q0 ∈ PathP (λ i → p i) (0r (snd A)) (0r (snd B)) ]
   Σ[ q1 ∈ PathP (λ i → p i) (1r (snd A)) (1r (snd B)) ]
   Σ[ r+ ∈ PathP (λ i → p i → p i → p i) (_+_ (snd A)) (_+_ (snd B)) ]
   Σ[ r· ∈ PathP (λ i → p i → p i → p i) (_·_ (snd A)) (_·_ (snd B)) ]
   Σ[ s ∈ PathP (λ i → p i → p i) (-_ (snd A)) (-_ (snd B)) ]
   PathP (λ i → IsRing (q0 i) (q1 i) (r+ i) (r· i) (s i)) (isRing (snd A)) (isRing (snd B)))
   ≃ (A ≡ B)
 Ring≡ A B = isoToEquiv theIso
   where
   open Iso
   theIso : Iso _ _
   fun theIso (p , q0 , q1 , r+ , r· , s , t) i = p i
                                                , ringstr (q0 i) (q1 i) (r+ i) (r· i) (s i) (t i)
   inv theIso x = cong ⟨_⟩ x , cong (0r ∘ snd) x , cong (1r ∘ snd) x
                , cong (_+_ ∘ snd) x , cong (_·_ ∘ snd) x , cong (-_ ∘ snd) x , cong (isRing ∘ snd) x
   rightInv theIso _ = refl
   leftInv theIso _ = refl

 caracRing≡ : {A B : Ring ℓ} (p q : A ≡ B) → cong ⟨_⟩ p ≡ cong ⟨_⟩ q → p ≡ q
 caracRing≡ {A = A} {B = B} p q P =
   sym (transportTransport⁻ (ua (Ring≡ A B)) p)
                                    ∙∙ cong (transport (ua (Ring≡ A B))) helper
                                    ∙∙ transportTransport⁻ (ua (Ring≡ A B)) q
     where
     helper : transport (sym (ua (Ring≡ A B))) p ≡ transport (sym (ua (Ring≡ A B))) q
     helper = Σ≡Prop
                (λ _ → isPropΣ
                          (isOfHLevelPathP' 1 (is-set (snd B)) _ _)
                          λ _ → isPropΣ (isOfHLevelPathP' 1 (is-set (snd B)) _ _)
                          λ _ → isPropΣ (isOfHLevelPathP' 1 (isSetΠ2 λ _ _ → is-set (snd B)) _ _)
                          λ _ → isPropΣ (isOfHLevelPathP' 1 (isSetΠ2 λ _ _ → is-set (snd B)) _ _)
                          λ _ → isPropΣ (isOfHLevelPathP' 1 (isSetΠ λ _ → is-set (snd B)) _ _)
                          λ _ → isOfHLevelPathP 1 (isPropIsRing _ _ _ _ _) _ _)
               (transportRefl (cong ⟨_⟩ p) ∙ P ∙ sym (transportRefl (cong ⟨_⟩ q)))

 uaCompRingEquiv : {A B C : Ring ℓ} (f : RingEquiv A B) (g : RingEquiv B C)
                  → uaRing (compRingEquiv f g) ≡ uaRing f ∙ uaRing g
 uaCompRingEquiv f g = caracRing≡ _ _ (
   cong ⟨_⟩ (uaRing (compRingEquiv f g))
     ≡⟨ uaCompEquiv _ _ ⟩
   cong ⟨_⟩ (uaRing f) ∙ cong ⟨_⟩ (uaRing g)
     ≡⟨ sym (cong-∙ ⟨_⟩ (uaRing f) (uaRing g)) ⟩
   cong ⟨_⟩ (uaRing f ∙ uaRing g) ∎)

open RingEquivs
open RingUAFunctoriality
recPT→Ring : {A : Type ℓ'} (𝓕  : A → Ring ℓ)
           → (σ : ∀ x y → RingEquiv (𝓕 x) (𝓕 y))
           → (∀ x y z → σ x z ≡ compRingEquiv (σ x y) (σ y z))
          ------------------------------------------------------
           → ∥ A ∥ → Ring ℓ
recPT→Ring 𝓕 σ compCoh = rec→Gpd isGroupoidRing 𝓕
  (3-ConstantCompChar 𝓕 (λ x y → uaRing (σ x y))
                          λ x y z → sym (  cong uaRing (compCoh x y z)
                                         ∙ uaCompRingEquiv (σ x y) (σ y z)))