module Syntax where

{-
open import Data.Nat hiding (_>_)
open import Data.Fin
open import Data.Product
open import Data.Bool
open import Relation.Binary.PropositionalEquality
-}

open import StdLibStuff

erase-subst :
 (X : Set) → (Y : X → Set) →
 (F : {x : X} → Y x) →
 (x₁ x₂ : X) →
 (eq : x₁ ≡ x₂) →
 (P : Y x₂ → Set) →
 P F →
 P (subst Y eq F)
erase-subst X Y F .x₂ x₂ refl P h = h

-- type, ctx

data Type (n : ℕ) : Set where
 $o : Type n
 $i : Fin n → Type n
 _>_ : Type n → Type n → Type n

data Ctx (n : ℕ) : Set where
 ε : Ctx n
 _∷_ : Type n → Ctx n → Ctx n

data Var : {n : ℕ} → Ctx n → Set where
 this : ∀ {n t} → {Γ : Ctx n} → Var (t ∷ Γ)
 next : ∀ {n t} → {Γ : Ctx n} → Var Γ → Var (t ∷ Γ)

lookup-Var : ∀ {n} → (Γ : Ctx n) → Var Γ → Type n
lookup-Var ε ()
lookup-Var (t ∷ _) this = t
lookup-Var (_ ∷ Γ) (next x) = lookup-Var Γ x

_++_ : ∀ {n} → Ctx n → Ctx n → Ctx n
ε ++ Γ = Γ
(t ∷ Γ₁) ++ Γ₂ = t ∷ (Γ₁ ++ Γ₂)

_r++_ : ∀ {n} → Ctx n → Ctx n → Ctx n
ε r++ Γ = Γ
(t ∷ Γ₁) r++ Γ₂ = Γ₁ r++ (t ∷ Γ₂)

-- stt formula

data Form : {n : ℕ} → Ctx n → Type n → Set where
 var : ∀ {n} → {Γ : Ctx n} {t : Type n} → (x : Var Γ) → lookup-Var Γ x ≡ t → Form Γ t
 N : ∀ {n} → {Γ : Ctx n} → Form Γ ($o > $o)
 A : ∀ {n} → {Γ : Ctx n} → Form Γ ($o > ($o > $o))
 Π : ∀ {n α} → {Γ : Ctx n} → Form Γ ((α > $o) > $o)
 i : ∀ {n α} → {Γ : Ctx n} → Form Γ ((α > $o) > α)
 app : ∀ {n α β} → {Γ : Ctx n} → Form Γ (α > β) → Form Γ α → Form Γ β
 lam : ∀ {n β} → {Γ : Ctx n} → (α : _) → Form (α ∷ Γ) β → Form Γ (α > β)

-- abbreviations (TPTP-like notation)

~ : ∀ {n} → {Γ : Ctx n} → Form Γ $o → Form Γ $o
~ F = app N F

_||_ : ∀ {n} → {Γ : Ctx n} → Form Γ $o → Form Γ $o → Form Γ $o
F || G = app (app A F) G

_&_ : ∀ {n} → {Γ : Ctx n} → Form Γ $o → Form Γ $o → Form Γ $o
F & G = ~ ((~ F) || (~ G))

_=>_ : ∀ {n} → {Γ : Ctx n} → Form Γ $o → Form Γ $o → Form Γ $o
F => G = (~ F) || G

![_]_ : ∀ {n} → {Γ : Ctx n} → (α : Type n) → Form (α ∷ Γ) $o → Form Γ $o
![ α ] F = app Π (lam α F)

?[_]_ : ∀ {n} → {Γ : Ctx n} → (α : Type n) → Form (α ∷ Γ) $o → Form Γ $o
?[ α ] F = ~ (![ α ] ~ F)

ι : ∀ {n} → {Γ : Ctx n} → (α : Type n) → Form (α ∷ Γ) $o → Form Γ α
ι α F = app i (lam α F)

Q : ∀ {n} → {Γ : Ctx n} → (α : Type n) → Form Γ (α > (α > $o))
Q α = lam α (lam α (![ α > $o ] (app (var this refl) (var (next (next this)) refl) => app (var this refl) (var (next this) refl))))

_==_ : ∀ {n} → {Γ : Ctx n} → {α : Type n} → Form Γ α → Form Γ α → Form Γ $o
F == G = app (app (Q _) F) G

_<=>_ : ∀ {n} → {Γ : Ctx n} → Form Γ $o → Form Γ $o → Form Γ $o
F <=> G = (F => G) & (G => F)

$true : ∀ {n} → {Γ : Ctx n} → Form Γ $o
$true = ![ $o ] (var this refl => var this refl)

$false : ∀ {n} → {Γ : Ctx n} → Form Γ $o
$false = ![ $o ] var this refl

^[_]_ : ∀ {n Γ t₂} → (t₁ : Type n) → Form (t₁ ∷ Γ) t₂ → Form Γ (t₁ > t₂)
^[ tp ] t = lam tp t

_·_ : ∀ {n} → {Γ : Ctx n} → ∀ {t₁ t₂} → Form Γ (t₁ > t₂) → Form Γ t₁ → Form Γ t₂
t₁ · t₂ = app t₁ t₂

$ : ∀ {n Γ} → {t : Type n} (v : Var Γ) → {eq : lookup-Var Γ v ≡ t} → Form Γ t
$ v {p} = var v p

-- occurs in

eq-Var : ∀ {n} {Γ : Ctx n} → Var Γ → Var Γ → Bool
eq-Var this this = true
eq-Var this (next y) = false
eq-Var (next x) this = false
eq-Var (next x) (next y) = eq-Var x y

occurs : ∀ {n Γ} → {t : Type n} → Var Γ → Form Γ t → Bool
occurs x (var x' p) = eq-Var x x'
occurs x N = false
occurs x A = false
occurs x Π = false
occurs x i = false
occurs x (app f₁ f₂) = occurs x f₁ ∨ occurs x f₂
occurs x (lam α f) = occurs (next x) f

-- weakening and substitution

weak-var : ∀ {n} → {β : Type n} (Γ₁ Γ₂ : Ctx n) → (x : Var (Γ₁ ++ Γ₂)) → Var (Γ₁ ++ (β ∷ Γ₂))
weak-var ε Γ₂ x = next x
weak-var (t ∷ Γ₁) Γ₂ this = this
weak-var (t ∷ Γ₁) Γ₂ (next x) = next (weak-var Γ₁ Γ₂ x)

weak-var-p : ∀ {n} → {β : Type n} (Γ₁ Γ₂ : Ctx n) → (x : Var (Γ₁ ++ Γ₂)) →
             lookup-Var (Γ₁ ++ (β ∷ Γ₂)) (weak-var Γ₁ Γ₂ x) ≡ lookup-Var (Γ₁ ++ Γ₂) x
weak-var-p ε Γ₂ x = refl
weak-var-p (t ∷ Γ₁) Γ₂ this = refl
weak-var-p (t ∷ Γ₁) Γ₂ (next x) = weak-var-p Γ₁ Γ₂ x

weak-i : ∀ {n} → {α β : Type n} (Γ₁ Γ₂ : Ctx n) → Form (Γ₁ ++ Γ₂) α → Form (Γ₁ ++ (β ∷ Γ₂)) α
weak-i Γ₁ Γ₂ (var x p) = var (weak-var Γ₁ Γ₂ x) (trans (weak-var-p Γ₁ Γ₂ x) p)
weak-i Γ₁ Γ₂ N = N
weak-i Γ₁ Γ₂ A = A
weak-i Γ₁ Γ₂ Π = Π
weak-i Γ₁ Γ₂ i = i
weak-i Γ₁ Γ₂ (app f₁ f₂) = app (weak-i Γ₁ Γ₂ f₁) (weak-i Γ₁ Γ₂ f₂)
weak-i Γ₁ Γ₂ (lam α f) = lam α (weak-i (α ∷ Γ₁) Γ₂ f)

weak : ∀ {n} → {Γ : Ctx n} {α β : Type n} → Form Γ α → Form (β ∷ Γ) α
weak {_} {Γ} = weak-i ε Γ

sub-var : ∀ {n} → {α β : Type n} (Γ₁ Γ₂ : Ctx n) → Form Γ₂ α → (x : Var (Γ₁ ++ (α ∷ Γ₂))) → lookup-Var (Γ₁ ++ (α ∷ Γ₂)) x ≡ β → Form (Γ₁ ++ Γ₂) β
sub-var ε Γ₂ g this p = subst (Form Γ₂) p g
sub-var ε Γ₂ g (next x) p = var x p
sub-var (t ∷ Γ₁) Γ₂ g this p = var this p
sub-var (t ∷ Γ₁) Γ₂ g (next x) p = weak (sub-var Γ₁ Γ₂ g x p)

sub-i : ∀ {n} → {α β : Type n} (Γ₁ Γ₂ : Ctx n) → Form Γ₂ α → Form (Γ₁ ++ (α ∷ Γ₂)) β → Form (Γ₁ ++ Γ₂) β
sub-i Γ₁ Γ₂ g (var x p) = sub-var Γ₁ Γ₂ g x p
sub-i Γ₁ Γ₂ g N = N
sub-i Γ₁ Γ₂ g A = A
sub-i Γ₁ Γ₂ g Π = Π
sub-i Γ₁ Γ₂ g i = i
sub-i Γ₁ Γ₂ g (app f₁ f₂) = app (sub-i Γ₁ Γ₂ g f₁) (sub-i Γ₁ Γ₂ g f₂)
sub-i Γ₁ Γ₂ g (lam α f) = lam α (sub-i (α ∷ Γ₁) Γ₂ g f)

sub : ∀ {n} → {Γ : Ctx n} {α β : Type n} → Form Γ α → Form (α ∷ Γ) β → Form Γ β
sub {_} {Γ} = sub-i ε Γ

-- properties about weak and sub

sub-weak-var-p-23-this-2 : ∀ {n} → {u v β : Type n} {Γ : Ctx n} (Γ' : Ctx n) (G : Form (v ∷ Γ) u) →
 (Γ'' : Ctx n)
 (h₁ : β ∷ Γ'' ≡ β ∷ (Γ' ++ (u ∷ (v ∷ Γ))))
 (p'' : lookup-Var ((β ∷ Γ') ++ (u ∷ (v ∷ Γ))) (subst Var h₁ this) ≡ lookup-Var (β ∷ (Γ' ++ (v ∷ Γ))) this) →
 var this refl ≡ sub-var (β ∷ Γ') (v ∷ Γ) G (subst Var h₁ this) p''
sub-weak-var-p-23-this-2 {n} {u} {v} {β} {Γ} Γ' G .(Γ' ++ (u ∷ (v ∷ Γ))) refl refl = refl

sub-weak-var-p-23-this-1 : ∀ {n} → {u v β : Type n} {Γ : Ctx n} (Γ' : Ctx n) (G : Form (v ∷ Γ) u) →
 (h₁ : β ∷ ((Γ' ++ (u ∷ ε)) ++ (v ∷ Γ)) ≡ β ∷ (Γ' ++ (u ∷ (v ∷ Γ))))
 (Γ'' : Ctx n)
 (h₂ : β ∷ Γ'' ≡ β ∷ ((Γ' ++ (u ∷ ε)) ++ Γ))
 (p'' : lookup-Var (β ∷ (Γ' ++ (u ∷ (v ∷ Γ)))) (subst Var h₁ (weak-var (β ∷ (Γ' ++ (u ∷ ε))) Γ (subst Var h₂ this))) ≡ β) →
 var this refl ≡
 sub-var (β ∷ Γ') (v ∷ Γ) G (subst Var h₁ (weak-var (β ∷ (Γ' ++ (u ∷ ε))) Γ (subst Var h₂ this))) p''
sub-weak-var-p-23-this-1 {n} {u} {v} {β} {Γ} Γ' G h₁ .((Γ' ++ (u ∷ ε)) ++ Γ) refl p'' = sub-weak-var-p-23-this-2 Γ' G ((Γ' ++ (u ∷ ε)) ++ (v ∷ Γ)) h₁ p''

mutual
 sub-weak-var-p-23-next-2 : ∀ {n} → {u v β t : Type n} {Γ : Ctx n} (Γ' : Ctx n) (x : Var (Γ' ++ Γ)) (G : Form (v ∷ Γ) u) →
  (p : lookup-Var (Γ' ++ (v ∷ Γ)) (weak-var Γ' Γ x) ≡ β)
  (Γ'' : Ctx n)
  (h₁₁ : t ∷ Γ'' ≡ t ∷ (Γ' ++ (u ∷ (v ∷ Γ))))
  (h₁₂ : (Γ' ++ (u ∷ ε)) ++ (v ∷ Γ) ≡ Γ'')
  (h₂ : Γ' ++ (u ∷ Γ) ≡ (Γ' ++ (u ∷ ε)) ++ Γ)
  (p'' : lookup-Var ((t ∷ Γ') ++ (u ∷ (v ∷ Γ))) (subst Var h₁₁ (next (subst Var h₁₂ (weak-var (Γ' ++ (u ∷ ε)) Γ (subst Var h₂ (weak-var Γ' Γ x)))))) ≡ β) →
  var (next (weak-var Γ' Γ x)) p ≡
  sub-var (t ∷ Γ') (v ∷ Γ) G (subst Var h₁₁ (next (subst Var h₁₂ (weak-var (Γ' ++ (u ∷ ε)) Γ (subst Var h₂ (weak-var Γ' Γ x)))))) p''
 sub-weak-var-p-23-next-2 {n} {u} {v} {β} {t} {Γ} Γ' x G p .(Γ' ++ (u ∷ (v ∷ Γ))) refl h₁₂ h₂ p'' = subst (λ z → var (next {_} {t} (weak-var Γ' Γ x)) p ≡ weak-i ε (Γ' ++ (v ∷ Γ)) z) {-{var (weak-var Γ' Γ x) p} {sub-var Γ' (v ∷ Γ) G (subst Var h₁₂ (weak-var (Γ' ++ (u ∷ ε)) Γ (subst Var h₂ (weak-var Γ' Γ x)))) p''}-} (sub-weak-var-p-23 {n} {u} {v} {β} {Γ} Γ' x G p h₁₂ h₂ p'') refl

 sub-weak-var-p-23-next-1 : ∀ {n} → {u v β t : Type n} {Γ : Ctx n} (Γ' : Ctx n) (x : Var (Γ' ++ Γ)) (G : Form (v ∷ Γ) u) →
  (p : lookup-Var (Γ' ++ (v ∷ Γ)) (weak-var Γ' Γ x) ≡ β)
  (h₁ : t ∷ ((Γ' ++ (u ∷ ε)) ++ (v ∷ Γ)) ≡ t ∷ (Γ' ++ (u ∷ (v ∷ Γ))))
  (Γ'' : Ctx n)
  (h₂₁ : t ∷ Γ'' ≡ t ∷ ((Γ' ++ (u ∷ ε)) ++ Γ))
  (h₂₂ : Γ' ++ (u ∷ Γ) ≡ Γ'')
  (p'' : lookup-Var ((t ∷ Γ') ++ (u ∷ (v ∷ Γ))) (subst Var h₁ (weak-var (t ∷ (Γ' ++ (u ∷ ε))) Γ (subst Var h₂₁ (next (subst Var h₂₂ (weak-var Γ' Γ x)))))) ≡ β) →
  var (next (weak-var Γ' Γ x)) p ≡
  sub-var (t ∷ Γ') (v ∷ Γ) G (subst Var h₁ (weak-var (t ∷ (Γ' ++ (u ∷ ε))) Γ (subst Var h₂₁ (next (subst Var h₂₂ (weak-var Γ' Γ x)))))) p''
 sub-weak-var-p-23-next-1 {n} {u} {v} {β} {t} {Γ} Γ' x G p h₁ .((Γ' ++ (u ∷ ε)) ++ Γ) refl h₂₂ p'' = sub-weak-var-p-23-next-2 Γ' x G p ((Γ' ++ (u ∷ ε)) ++ (v ∷ Γ)) h₁ refl h₂₂ p''

 sub-weak-var-p-23 : ∀ {n} → {u v β : Type n} {Γ : Ctx n} (Γ' : Ctx n) (x : Var (Γ' ++ Γ)) (G : Form (v ∷ Γ) u) →
  (p : lookup-Var (Γ' ++ (v ∷ Γ)) (weak-var Γ' Γ x) ≡ β) →
  (h₁ : (Γ' ++ (u ∷ ε)) ++ (v ∷ Γ) ≡ Γ' ++ (u ∷ (v ∷ Γ))) →
  (h₂ : Γ' ++ (u ∷ Γ) ≡ (Γ' ++ (u ∷ ε)) ++ Γ) →
  (p'' : lookup-Var (Γ' ++ (u ∷ (v ∷ Γ))) (subst Var h₁ (weak-var (Γ' ++ (u ∷ ε)) Γ (subst Var h₂ (weak-var Γ' Γ x)))) ≡ β) →
  var (weak-var Γ' Γ x) p ≡
  sub-var Γ' (v ∷ Γ) G (subst Var h₁ (weak-var (Γ' ++ (u ∷ ε)) Γ (subst Var h₂ (weak-var Γ' Γ x)))) p''
 sub-weak-var-p-23 ε x G refl refl refl refl = refl
 sub-weak-var-p-23 {n} {u} {v} {β} {Γ} (.β ∷ Γ') this G refl h₁ h₂ p'' = sub-weak-var-p-23-this-1 Γ' G h₁ (Γ' ++ (u ∷ Γ)) h₂ p''
 sub-weak-var-p-23 {n} {u} {v} {β} {Γ} (t ∷ Γ') (next x) G p h₁ h₂ p'' = sub-weak-var-p-23-next-1 Γ' x G p h₁ (Γ' ++ (u ∷ Γ)) h₂ refl p''

sub-weak-p-23-var-2 : ∀ {n} → {u v β : Type n} {Γ : Ctx n} (Γ' : Ctx n) (x : Var (Γ' ++ Γ)) (G : Form (v ∷ Γ) u) →
 (p : lookup-Var (Γ' ++ Γ) x ≡ β) →
 (Γ'' : Ctx n) →
 (h₁₁ : Γ'' ≡ Γ' ++ (u ∷ (v ∷ Γ))) →
 (h₁₂ : (Γ' ++ (u ∷ ε)) ++ (v ∷ Γ) ≡ Γ'') →
 (h₂ : Γ' ++ (u ∷ Γ) ≡ (Γ' ++ (u ∷ ε)) ++ Γ) →
 (p'' : lookup-Var Γ'' (subst Var h₁₂ (weak-var (Γ' ++ (u ∷ ε)) Γ (subst Var h₂ (weak-var Γ' Γ x)))) ≡ β) →
 var (weak-var Γ' Γ x) (trans (weak-var-p Γ' Γ x) p) ≡
 sub-i Γ' (v ∷ Γ) G (subst (λ z → Form z β) h₁₁ (var (subst Var h₁₂ (weak-var (Γ' ++ (u ∷ ε)) Γ (subst Var h₂ (weak-var Γ' Γ x)))) p''))
sub-weak-p-23-var-2 {n} {u} {v} {β} {Γ} Γ' x G p .(Γ' ++ (u ∷ (v ∷ Γ))) refl h₁₂ h₂ p'' = sub-weak-var-p-23 Γ' x G (trans (weak-var-p Γ' Γ x) p) h₁₂ h₂ p''

sub-weak-p-23-var-1 : ∀ {n} → {u v β : Type n} {Γ : Ctx n} (Γ' : Ctx n) (x : Var (Γ' ++ Γ)) (G : Form (v ∷ Γ) u) →
 (p : lookup-Var (Γ' ++ Γ) x ≡ β) →
 (h₁ : (Γ' ++ (u ∷ ε)) ++ (v ∷ Γ) ≡ Γ' ++ (u ∷ (v ∷ Γ))) →
 (Γ'' : Ctx n) →
 (h₂₁ : Γ'' ≡ (Γ' ++ (u ∷ ε)) ++ Γ) →
 (h₂₂ : Γ' ++ (u ∷ Γ) ≡ Γ'') →
 (p' : lookup-Var Γ'' (subst Var h₂₂ (weak-var Γ' Γ x)) ≡ β) →
 var (weak-var Γ' Γ x) (trans (weak-var-p Γ' Γ x) p) ≡
 sub-i Γ' (v ∷ Γ) G (subst (λ z → Form z β) h₁ (weak-i (Γ' ++ (u ∷ ε)) Γ (subst (λ z → Form z β) h₂₁ (var (subst Var h₂₂ (weak-var Γ' Γ x)) p'))))
sub-weak-p-23-var-1 {n} {u} {v} {β} {Γ} Γ' x G p h₁ .((Γ' ++ (u ∷ ε)) ++ Γ) refl h₂₂ p' = sub-weak-p-23-var-2 Γ' x G p ((Γ' ++ (u ∷ ε)) ++ (v ∷ Γ)) h₁ refl h₂₂ (trans (weak-var-p (Γ' ++ (u ∷ ε)) Γ (subst Var h₂₂ (weak-var Γ' Γ x))) p')

mutual
 sub-weak-p-23-app-2 : ∀ {n} → {t u v w : Type n} {Γ : Ctx n} (Γ' : Ctx n) (f₁ : Form (Γ' ++ Γ) (w > t)) (f₂ : Form (Γ' ++ Γ) w) (G : Form (v ∷ Γ) u) →
  (Γ'' : Ctx n) →
  (h₁₁ : Γ'' ≡ Γ' ++ (u ∷ (v ∷ Γ))) →
  (h₁₂ : (Γ' ++ (u ∷ ε)) ++ (v ∷ Γ) ≡ Γ'') →
  (h₂ : Γ' ++ (u ∷ Γ) ≡ (Γ' ++ (u ∷ ε)) ++ Γ) →
  app (weak-i Γ' Γ f₁) (weak-i Γ' Γ f₂) ≡
  sub-i Γ' (v ∷ Γ) G (subst (λ z → Form z t) h₁₁ (app (subst (λ z → Form z (w > t)) h₁₂ (weak-i (Γ' ++ (u ∷ ε)) Γ (subst (λ z → Form z (w > t)) h₂ (weak-i Γ' Γ f₁)))) (subst (λ z → Form z w) h₁₂ (weak-i (Γ' ++ (u ∷ ε)) Γ (subst (λ z → Form z w) h₂ (weak-i Γ' Γ f₂))))))
 sub-weak-p-23-app-2 {n} {t} {u} {v} {w} {Γ} Γ' f₁ f₂ G .(Γ' ++ (u ∷ (v ∷ Γ))) refl h₁₂ h₂ =
  trans (cong (λ z → app z (weak-i Γ' Γ f₂)) (
    sub-weak-p-23-i Γ' f₁ G h₁₂ h₂
   ))
   (cong (λ z → app (sub-i Γ' (v ∷ Γ) G (subst (λ z → Form z (w > t)) h₁₂ (weak-i (Γ' ++ (u ∷ ε)) Γ (subst (λ z → Form z (w > t)) h₂ (weak-i Γ' Γ f₁))))) z) (
    sub-weak-p-23-i Γ' f₂ G h₁₂ h₂
   ))

 sub-weak-p-23-app-1 : ∀ {n} → {t u v w : Type n} {Γ : Ctx n} (Γ' : Ctx n) (f₁ : Form (Γ' ++ Γ) (w > t)) (f₂ : Form (Γ' ++ Γ) w) (G : Form (v ∷ Γ) u) →
  (h₁ : (Γ' ++ (u ∷ ε)) ++ (v ∷ Γ) ≡ Γ' ++ (u ∷ (v ∷ Γ))) →
  (Γ'' : Ctx n) →
  (h₂₁ : Γ'' ≡ (Γ' ++ (u ∷ ε)) ++ Γ) →
  (h₂₂ : Γ' ++ (u ∷ Γ) ≡ Γ'') →
  app (weak-i Γ' Γ f₁) (weak-i Γ' Γ f₂) ≡
  sub-i Γ' (v ∷ Γ) G (subst (λ z → Form z t) h₁ (weak-i (Γ' ++ (u ∷ ε)) Γ (subst (λ z → Form z t) h₂₁ (app (subst (λ z → Form z (w > t)) h₂₂ (weak-i Γ' Γ f₁)) (subst (λ z → Form z w) h₂₂ (weak-i Γ' Γ f₂))))))
 sub-weak-p-23-app-1 {n} {t} {u} {v} {w} {Γ} Γ' f₁ f₂ G h₁ .((Γ' ++ (u ∷ ε)) ++ Γ) refl h₂₂ = sub-weak-p-23-app-2 Γ' f₁ f₂ G ((Γ' ++ (u ∷ ε)) ++ (v ∷ Γ)) h₁ refl h₂₂

 sub-weak-p-23-lam-2 : ∀ {n} → {u v α β : Type n} {Γ : Ctx n} (Γ' : Ctx n) (f : Form ((α ∷ Γ') ++ Γ) β) (G : Form (v ∷ Γ) u) →
  (Γ'' : Ctx n) →
  (h₁₁ : Γ'' ≡ Γ' ++ (u ∷ (v ∷ Γ))) →
  (h₁₂ : (Γ' ++ (u ∷ ε)) ++ (v ∷ Γ) ≡ Γ'') →
  (h₂ : α ∷ (Γ' ++ (u ∷ Γ)) ≡ α ∷ ((Γ' ++ (u ∷ ε)) ++ Γ)) →
  lam α (weak-i (α ∷ Γ') Γ f) ≡
  sub-i Γ' (v ∷ Γ) G (subst (λ z → Form z (α > β)) h₁₁ (lam α (subst (λ z → Form z β) (cong (_∷_ α) h₁₂) (weak-i (α ∷ (Γ' ++ (u ∷ ε))) Γ (subst (λ z → Form z β) h₂ (weak-i (α ∷ Γ') Γ f))))))
 sub-weak-p-23-lam-2 {n} {u} {v} {α} {β} {Γ} Γ' f G .(Γ' ++ (u ∷ (v ∷ Γ))) refl h₁₂ h₂ = cong (lam α) (
  sub-weak-p-23-i (α ∷ Γ') f G (cong (_∷_ α) h₁₂) h₂)

 sub-weak-p-23-lam-1 : ∀ {n} → {u v α β : Type n} {Γ : Ctx n} (Γ' : Ctx n) (f : Form ((α ∷ Γ') ++ Γ) β) (G : Form (v ∷ Γ) u) →
  (h₁ : (Γ' ++ (u ∷ ε)) ++ (v ∷ Γ) ≡ Γ' ++ (u ∷ (v ∷ Γ))) →
  (Γ'' : Ctx n) →
  (h₂₁ : Γ'' ≡ (Γ' ++ (u ∷ ε)) ++ Γ) →
  (h₂₂ : Γ' ++ (u ∷ Γ) ≡ Γ'') →
  lam α (weak-i (α ∷ Γ') Γ f) ≡
  sub-i Γ' (v ∷ Γ) G (subst (λ z → Form z (α > β)) h₁ (weak-i (Γ' ++ (u ∷ ε)) Γ (subst (λ z → Form z (α > β)) h₂₁ (lam α (subst (λ z → Form z β) (cong (λ z → α ∷ z) h₂₂) (weak-i (α ∷ Γ') Γ f))))))
 sub-weak-p-23-lam-1 {n} {u} {v} {α} {β} {Γ} Γ' f G h₁ .((Γ' ++ (u ∷ ε)) ++ Γ) refl h₂₂ = sub-weak-p-23-lam-2 Γ' f G ((Γ' ++ (u ∷ ε)) ++ (v ∷ Γ)) h₁ refl (cong (_∷_ α) h₂₂)

 sub-weak-p-23-i : ∀ {n} → {t u v : Type n} {Γ : Ctx n} (Γ' : Ctx n) (F : Form (Γ' ++ Γ) t) (G : Form (v ∷ Γ) u) →
  (h₁ : (Γ' ++ (u ∷ ε)) ++ (v ∷ Γ) ≡ Γ' ++ (u ∷ (v ∷ Γ))) →
  (h₂ : Γ' ++ (u ∷ Γ) ≡ (Γ' ++ (u ∷ ε)) ++ Γ) →
  weak-i Γ' Γ F ≡ sub-i Γ' (v ∷ Γ) G (subst (λ z → Form z t) h₁ (weak-i (Γ' ++ (u ∷ ε)) Γ (subst (λ z → Form z t) h₂ (weak-i Γ' Γ F))))
-- sub-weak-p-23-i {_} {.(lookup-Var (Γ' ++ Γ) x)} {u} {v} {Γ} Γ' (var x) G h₁ h₂ = {!!}
 sub-weak-p-23-i {_} {β} {u} {v} {Γ} Γ' (var x p) G h₁ h₂ = sub-weak-p-23-var-1 Γ' x G p h₁ (Γ' ++ (u ∷ Γ)) h₂ refl (trans (weak-var-p Γ' Γ x) p)
 sub-weak-p-23-i {_} {.($o > $o)} {u} {v} {Γ} Γ' N G h₁ h₂ = erase-subst (Ctx _) (λ z → Form z ($o > $o)) N (Γ' ++ (u ∷ Γ)) ((Γ' ++ (u ∷ ε)) ++ Γ) h₂ (λ z → N ≡ sub-i Γ' (v ∷ Γ) G (subst (λ z → Form z ($o > $o)) h₁ (weak-i (Γ' ++ (u ∷ ε)) Γ z))) (erase-subst (Ctx _) (λ z → Form z ($o > $o)) N ((Γ' ++ (u ∷ ε)) ++ (v ∷ Γ)) (Γ' ++ (u ∷ (v ∷ Γ))) h₁ (λ z → N ≡ sub-i Γ' (v ∷ Γ) G z) refl)  -- rewrite h₂ | h₁ = refl
 sub-weak-p-23-i {_} {.($o > ($o > $o))} {u} {v} {Γ} Γ' A G h₁ h₂ = erase-subst (Ctx _) (λ z → Form z ($o > ($o > $o))) A (Γ' ++ (u ∷ Γ)) ((Γ' ++ (u ∷ ε)) ++ Γ) h₂ (λ z → A ≡ sub-i Γ' (v ∷ Γ) G (subst (λ z → Form z ($o > ($o > $o))) h₁ (weak-i (Γ' ++ (u ∷ ε)) Γ z))) (erase-subst (Ctx _) (λ z → Form z ($o > ($o > $o))) A ((Γ' ++ (u ∷ ε)) ++ (v ∷ Γ)) (Γ' ++ (u ∷ (v ∷ Γ))) h₁ (λ z → A ≡ sub-i Γ' (v ∷ Γ) G z) refl)  -- rewrite h₂ | h₁ = refl
 sub-weak-p-23-i {_} {((t > $o) > $o)} {u} {v} {Γ} Γ' Π G h₁ h₂ = erase-subst (Ctx _) (λ z → Form z ((t > $o) > $o)) Π (Γ' ++ (u ∷ Γ)) ((Γ' ++ (u ∷ ε)) ++ Γ) h₂ (λ z → Π ≡ sub-i Γ' (v ∷ Γ) G (subst (λ z → Form z ((t > $o) > $o)) h₁ (weak-i (Γ' ++ (u ∷ ε)) Γ z))) (erase-subst (Ctx _) (λ z → Form z ((t > $o) > $o)) Π ((Γ' ++ (u ∷ ε)) ++ (v ∷ Γ)) (Γ' ++ (u ∷ (v ∷ Γ))) h₁ (λ z → Π ≡ sub-i Γ' (v ∷ Γ) G z) refl)  -- rewrite h₂ | h₁ = refl
 sub-weak-p-23-i {_} {((t > $o) > .t)} {u} {v} {Γ} Γ' i G h₁ h₂ = erase-subst (Ctx _) (λ z → Form z ((t > $o) > t)) i (Γ' ++ (u ∷ Γ)) ((Γ' ++ (u ∷ ε)) ++ Γ) h₂ (λ z → i ≡ sub-i Γ' (v ∷ Γ) G (subst (λ z → Form z ((t > $o) > t)) h₁ (weak-i (Γ' ++ (u ∷ ε)) Γ z))) (erase-subst (Ctx _) (λ z → Form z ((t > $o) > t)) i ((Γ' ++ (u ∷ ε)) ++ (v ∷ Γ)) (Γ' ++ (u ∷ (v ∷ Γ))) h₁ (λ z → i ≡ sub-i Γ' (v ∷ Γ) G z) refl)  -- rewrite h₂ | h₁ = refl
 sub-weak-p-23-i {_} {t} {u} {v} {Γ} Γ' (app f₁ f₂) G h₁ h₂ = sub-weak-p-23-app-1 Γ' f₁ f₂ G h₁ (Γ' ++ (u ∷ Γ)) h₂ refl
 sub-weak-p-23-i {_} {α > β} {u} {v} {Γ} Γ' (lam .α f) G h₁ h₂ = sub-weak-p-23-lam-1 Γ' f G h₁ (Γ' ++ (u ∷ Γ)) h₂ refl

sub-weak-var-p-1-this-2 : ∀ {n} → {u v β : Type n} {Γ : Ctx n} (Γ' : Ctx n) (G : Form Γ u) →
 (Γ'' : Ctx n)
 (h₁ : β ∷ Γ'' ≡ β ∷ (Γ' ++ (v ∷ Γ)))
 (p' : β ≡ β) →
 var this refl ≡
 subst (λ z → Form z β) h₁ (var this p')
sub-weak-var-p-1-this-2 {n} {u} {v} {β} {Γ} Γ' G ._ refl refl = refl

sub-weak-var-p-1-this-1 : ∀ {n} → {u v β : Type n} {Γ : Ctx n} (Γ' : Ctx n) (G : Form Γ u) →
 (h₁ : β ∷ ((Γ' ++ (v ∷ ε)) ++ Γ) ≡ β ∷ (Γ' ++ (v ∷ Γ)))
 (Γ'' : Ctx n)
 (h₂ : β ∷ Γ'' ≡ β ∷ ((Γ' ++ (v ∷ ε)) ++ (u ∷ Γ)))
 (p' : lookup-Var ((β ∷ (Γ' ++ (v ∷ ε))) ++ (u ∷ Γ)) (subst Var h₂ this) ≡ β) →
 var this refl ≡
 subst (λ z → Form z β) h₁ (sub-var (β ∷ (Γ' ++ (v ∷ ε))) Γ G (subst Var h₂ this) p')
sub-weak-var-p-1-this-1 {n} {u} {v} {β} {Γ} Γ' G h₁ ._ refl p' = sub-weak-var-p-1-this-2 Γ' G _ h₁ p'

mutual
 sub-weak-var-p-1-next-2 : ∀ {n} → {u v β t : Type n} {Γ : Ctx n} (Γ' : Ctx n) (x : Var (Γ' ++ Γ)) (G : Form Γ u) →
  (p : lookup-Var (Γ' ++ (v ∷ Γ)) (weak-var Γ' Γ x) ≡ β)
  (Γ'' : Ctx n)
  (h₁₁ : t ∷ Γ'' ≡ t ∷ (Γ' ++ (v ∷ Γ)))
  (h₁₂ : ((Γ' ++ (v ∷ ε)) ++ Γ) ≡ Γ'')
  (h₂ : (Γ' ++ (v ∷ (u ∷ Γ))) ≡ ((Γ' ++ (v ∷ ε)) ++ (u ∷ Γ)))
  (p' : lookup-Var ((Γ' ++ (v ∷ ε)) ++ (u ∷ Γ)) (subst Var h₂ (weak-var Γ' (u ∷ Γ) (weak-var Γ' Γ x))) ≡ β) →
  var (next (weak-var Γ' Γ x)) p ≡
  subst (λ z → Form z β) h₁₁ (weak-i ε Γ'' (subst (λ z → Form z β) h₁₂ (sub-var (Γ' ++ (v ∷ ε)) Γ G (subst Var h₂ (weak-var Γ' (u ∷ Γ) (weak-var Γ' Γ x))) p')))
 sub-weak-var-p-1-next-2 {n} {u} {v} {β} {t} {Γ} Γ' x G p ._ refl h₁₂ h₂ p' =
  subst (λ z → var (next {_} {t} (weak-var Γ' Γ x)) p ≡ weak-i ε (Γ' ++ (v ∷ Γ)) z)
   (sub-weak-var-p-1 Γ' x G p h₁₂ h₂ p')
   refl

 sub-weak-var-p-1-next-1 : ∀ {n} → {u v β t : Type n} {Γ : Ctx n} (Γ' : Ctx n) (x : Var (Γ' ++ Γ)) (G : Form Γ u) →
  (p : lookup-Var (Γ' ++ (v ∷ Γ)) (weak-var Γ' Γ x) ≡ β)
  (h₁ : t ∷ ((Γ' ++ (v ∷ ε)) ++ Γ) ≡ t ∷ (Γ' ++ (v ∷ Γ)))
  (Γ'' : Ctx n)
  (h₂₁ : t ∷ Γ'' ≡ t ∷ ((Γ' ++ (v ∷ ε)) ++ (u ∷ Γ)))
  (h₂₂ : (Γ' ++ (v ∷ (u ∷ Γ))) ≡ Γ'')
  (p' : lookup-Var (t ∷ ((Γ' ++ (v ∷ ε)) ++ (u ∷ Γ))) (subst Var h₂₁ (next (subst Var h₂₂ (weak-var Γ' (u ∷ Γ) (weak-var Γ' Γ x))))) ≡ β) →
  var (next (weak-var Γ' Γ x)) p ≡
  subst (λ z → Form z β) h₁ (sub-var (t ∷ (Γ' ++ (v ∷ ε))) Γ G (subst Var h₂₁ (next (subst Var h₂₂ (weak-var Γ' (u ∷ Γ) (weak-var Γ' Γ x))))) p')
 sub-weak-var-p-1-next-1 {n} {u} {v} {β} {t} {Γ} Γ' x G p h₁ ._ refl h₂₂ p' = sub-weak-var-p-1-next-2 Γ' x G p _ h₁ refl h₂₂ p'

 sub-weak-var-p-1 : ∀ {n} → {u v β : Type n} {Γ : Ctx n} (Γ' : Ctx n) (x : Var (Γ' ++ Γ)) (G : Form Γ u) →
  (p : lookup-Var (Γ' ++ (v ∷ Γ)) (weak-var Γ' Γ x) ≡ β) →
  (h₁ : (Γ' ++ (v ∷ ε)) ++ Γ ≡ Γ' ++ (v ∷ Γ)) →
  (h₂ : Γ' ++ (v ∷ (u ∷ Γ)) ≡ (Γ' ++ (v ∷ ε)) ++ (u ∷ Γ)) →
  (p' : lookup-Var ((Γ' ++ (v ∷ ε)) ++ (u ∷ Γ)) (subst Var h₂ (weak-var Γ' (u ∷ Γ) (weak-var Γ' Γ x))) ≡ β) →
  var (weak-var Γ' Γ x) p ≡
  subst (λ z → Form z β) h₁ (sub-var (Γ' ++ (v ∷ ε)) Γ G (subst Var h₂ (weak-var Γ' (u ∷ Γ) (weak-var Γ' Γ x))) p')
 sub-weak-var-p-1 ε x G refl refl refl refl = refl
 sub-weak-var-p-1 {n} {u} {v} {β} {Γ} (.β ∷ Γ') this G refl h₁ h₂ p' = sub-weak-var-p-1-this-1 Γ' G h₁ _ h₂ p'
 sub-weak-var-p-1 {n} {u} {v} {β} {Γ} (t ∷ Γ') (next x) G p h₁ h₂ p' = sub-weak-var-p-1-next-1 Γ' x G p h₁ _ h₂ refl p'

sub-weak-p-1-var : ∀ {n} → {u v β : Type n} {Γ : Ctx n} (Γ' : Ctx n) (x : Var (Γ' ++ Γ)) (G : Form Γ u) →
 (p : lookup-Var (Γ' ++ Γ) x ≡ β)
 (h₁ : (Γ' ++ (v ∷ ε)) ++ Γ ≡ Γ' ++ (v ∷ Γ))
 (Γ'' : Ctx n)
 (h₂₁ : Γ'' ≡ (Γ' ++ (v ∷ ε)) ++ (u ∷ Γ))
 (h₂₂ : Γ' ++ (v ∷ (u ∷ Γ)) ≡ Γ'')
 (p' : lookup-Var Γ'' (subst Var h₂₂ (weak-var Γ' (u ∷ Γ) (weak-var Γ' Γ x))) ≡ β) →
 var (weak-var Γ' Γ x) (trans (weak-var-p Γ' Γ x) p) ≡
 subst (λ z → Form z β) h₁ (sub-i (Γ' ++ (v ∷ ε)) Γ G (subst (λ z → Form z β) h₂₁ (var (subst Var h₂₂ (weak-var Γ' (u ∷ Γ) (weak-var Γ' Γ x))) p')))
sub-weak-p-1-var {n} {u} {v} {β} {Γ} Γ' x G p h₁ ._ refl h₂₂ p' = sub-weak-var-p-1 Γ' x G (trans (weak-var-p Γ' Γ x) p) h₁ h₂₂ p'

mutual
 sub-weak-p-1-app-2 : ∀ {n} → {t u v w : Type n} {Γ : Ctx n} (Γ' : Ctx n) (f₁ : Form (Γ' ++ Γ) (w > t)) (f₂ : Form (Γ' ++ Γ) w) (G : Form Γ u) →
  (Γ'' : Ctx n) →
  (h₁₁ : Γ'' ≡ Γ' ++ (v ∷ Γ)) →
  (h₁₂ : (Γ' ++ (v ∷ ε)) ++ Γ ≡ Γ'') →
  (h₂ : Γ' ++ (v ∷ (u ∷ Γ)) ≡ (Γ' ++ (v ∷ ε)) ++ (u ∷ Γ)) →
  app (weak-i Γ' Γ f₁) (weak-i Γ' Γ f₂) ≡
  subst (λ z → Form z t) h₁₁ (app (subst (λ z → Form z (w > t)) h₁₂ (sub-i (Γ' ++ (v ∷ ε)) Γ G (subst (λ z → Form z (w > t)) h₂ (weak-i Γ' (u ∷ Γ) (weak-i Γ' Γ f₁))))) (subst (λ z → Form z w) h₁₂ (sub-i (Γ' ++ (v ∷ ε)) Γ G (subst (λ z → Form z w) h₂ (weak-i Γ' (u ∷ Γ) (weak-i Γ' Γ f₂))))))
 sub-weak-p-1-app-2 {n} {t} {u} {v} {w} {Γ} Γ' f₁ f₂ G .(Γ' ++ (v ∷ Γ)) refl h₁₂ h₂ =
  trans (cong (λ z → app z (weak-i Γ' Γ f₂)) (
    sub-weak-p-1-i Γ' f₁ G h₁₂ h₂
   ))
   (cong (λ z → app (subst (λ z → Form z (w > t)) h₁₂ (sub-i (Γ' ++ (v ∷ ε)) Γ G (subst (λ z → Form z (w > t)) h₂ (weak-i Γ' (u ∷ Γ) (weak-i Γ' Γ f₁))))) z) (
    sub-weak-p-1-i Γ' f₂ G h₁₂ h₂
   ))

 sub-weak-p-1-app-1 : ∀ {n} → {t u v w : Type n} {Γ : Ctx n} (Γ' : Ctx n) (f₁ : Form (Γ' ++ Γ) (w > t)) (f₂ : Form (Γ' ++ Γ) w) (G : Form Γ u) →
  (h₁ : (Γ' ++ (v ∷ ε)) ++ Γ ≡ Γ' ++ (v ∷ Γ)) →
  (Γ'' : Ctx n) →
  (h₂₁ : Γ'' ≡ (Γ' ++ (v ∷ ε)) ++ (u ∷ Γ)) →
  (h₂₂ : Γ' ++ (v ∷ (u ∷ Γ)) ≡ Γ'') →
  app (weak-i Γ' Γ f₁) (weak-i Γ' Γ f₂) ≡
  subst (λ z → Form z t) h₁ (sub-i (Γ' ++ (v ∷ ε)) Γ G (subst (λ z → Form z t) h₂₁ (app (subst (λ z → Form z (w > t)) h₂₂ (weak-i Γ' (u ∷ Γ) (weak-i Γ' Γ f₁))) (subst (λ z → Form z w) h₂₂ (weak-i Γ' (u ∷ Γ) (weak-i Γ' Γ f₂))))))
 sub-weak-p-1-app-1 {n} {t} {u} {v} {w} {Γ} Γ' f₁ f₂ G h₁ .((Γ' ++ (v ∷ ε)) ++ (u ∷ Γ)) refl h₂₂ = sub-weak-p-1-app-2 Γ' f₁ f₂ G ((Γ' ++ (v ∷ ε)) ++ Γ) h₁ refl h₂₂

 sub-weak-p-1-lam-2 : ∀ {n} → {u v α β : Type n} {Γ : Ctx n} (Γ' : Ctx n) (f : Form ((α ∷ Γ') ++ Γ) β) (G : Form Γ u) →
  (Γ'' : Ctx n) →
  (h₁₁ : Γ'' ≡ Γ' ++ (v ∷ Γ)) →
  (h₁₂ : (Γ' ++ (v ∷ ε)) ++ Γ ≡ Γ'') →
  (h₂ : Γ' ++ (v ∷ (u ∷ Γ)) ≡ (Γ' ++ (v ∷ ε)) ++ (u ∷ Γ)) →
  lam α (weak-i (α ∷ Γ') Γ f) ≡
  subst (λ z → Form z (α > β)) h₁₁ (lam α (subst (λ z → Form z β) (cong (_∷_ α) h₁₂) (sub-i (α ∷ (Γ' ++ (v ∷ ε))) Γ G (subst (λ z → Form z β) (cong (_∷_ α) h₂) (weak-i (α ∷ Γ') (u ∷ Γ) (weak-i (α ∷ Γ') Γ f))))))
 sub-weak-p-1-lam-2 {n} {u} {v} {α} {β} {Γ} Γ' f G .(Γ' ++ (v ∷ Γ)) refl h₁₂ h₂ = cong (lam α) (
  sub-weak-p-1-i (α ∷ Γ') f G (cong (_∷_ α) h₁₂) (cong (_∷_ α) h₂))

 sub-weak-p-1-lam-1 : ∀ {n} → {u v α β : Type n} {Γ : Ctx n} (Γ' : Ctx n) (f : Form ((α ∷ Γ') ++ Γ) β) (G : Form Γ u) →
  (h₁ : (Γ' ++ (v ∷ ε)) ++ Γ ≡ Γ' ++ (v ∷ Γ)) →
  (Γ'' : Ctx n) →
  (h₂₁ : Γ'' ≡ (Γ' ++ (v ∷ ε)) ++ (u ∷ Γ)) →
  (h₂₂ : Γ' ++ (v ∷ (u ∷ Γ)) ≡ Γ'') →
  lam α (weak-i (α ∷ Γ') Γ f) ≡
  subst (λ z → Form z (α > β)) h₁ (sub-i (Γ' ++ (v ∷ ε)) Γ G (subst (λ z → Form z (α > β)) h₂₁ (lam α (subst (λ z → Form z β) (cong (λ z → α ∷ z) h₂₂) (weak-i (α ∷ Γ') (u ∷ Γ) (weak-i (α ∷ Γ') Γ f))))))
 sub-weak-p-1-lam-1 {n} {u} {v} {α} {β} {Γ} Γ' f G h₁ .((Γ' ++ (v ∷ ε)) ++ (u ∷ Γ)) refl h₂₂ = sub-weak-p-1-lam-2 Γ' f G ((Γ' ++ (v ∷ ε)) ++ Γ) h₁ refl h₂₂

 sub-weak-p-1-i : ∀ {n} → {t u v : Type n} {Γ : Ctx n} (Γ' : Ctx n) (F : Form (Γ' ++ Γ) t) (G : Form Γ u) →
   (h₁ : (Γ' ++ (v ∷ ε)) ++ Γ ≡ Γ' ++ (v ∷ Γ)) →
   (h₂ : Γ' ++ (v ∷ (u ∷ Γ)) ≡ (Γ' ++ (v ∷ ε)) ++ (u ∷ Γ)) →
   weak-i Γ' Γ F ≡ subst (λ z → Form z t) h₁ (sub-i (Γ' ++ (v ∷ ε)) Γ G (subst (λ z → Form z t) h₂ (weak-i Γ' (u ∷ Γ) (weak-i Γ' Γ F))))
 sub-weak-p-1-i {_} {β} {u} {v} {Γ} Γ' (var x p) G h₁ h₂ = sub-weak-p-1-var Γ' x G p h₁ _ h₂ refl (trans (weak-var-p Γ' (u ∷ Γ) (weak-var Γ' Γ x)) (trans (weak-var-p Γ' Γ x) p))
 sub-weak-p-1-i {_} {$o > $o} {u} {v} {Γ} Γ' N G h₁ h₂ = erase-subst (Ctx _) (λ z → Form z ($o > $o)) N (Γ' ++ (v ∷ (u ∷ Γ))) ((Γ' ++ (v ∷ ε)) ++ (u ∷ Γ)) h₂ (λ z → N ≡ subst (λ z → Form z ($o > $o)) h₁ (sub-i (Γ' ++ (v ∷ ε)) Γ G z)) (erase-subst (Ctx _) (λ z → Form z ($o > $o)) N ((Γ' ++ (v ∷ ε)) ++ Γ) (Γ' ++ (v ∷ Γ)) h₁ (λ z → N ≡ z) refl)  -- rewrite h₂ | h₁ = refl
 sub-weak-p-1-i {_} {$o > ($o > $o)} {u} {v} {Γ} Γ' A G h₁ h₂ = erase-subst (Ctx _) (λ z → Form z ($o > ($o > $o))) A (Γ' ++ (v ∷ (u ∷ Γ))) ((Γ' ++ (v ∷ ε)) ++ (u ∷ Γ)) h₂ (λ z → A ≡ subst (λ z → Form z ($o > ($o > $o))) h₁ (sub-i (Γ' ++ (v ∷ ε)) Γ G z)) (erase-subst (Ctx _) (λ z → Form z ($o > ($o > $o))) A ((Γ' ++ (v ∷ ε)) ++ Γ) (Γ' ++ (v ∷ Γ)) h₁ (λ z → A ≡ z) refl)  -- rewrite h₂ | h₁ = refl
 sub-weak-p-1-i {_} {(t > $o) > $o} {u} {v} {Γ} Γ' Π G h₁ h₂ = erase-subst (Ctx _) (λ z → Form z ((t > $o) > $o)) Π (Γ' ++ (v ∷ (u ∷ Γ))) ((Γ' ++ (v ∷ ε)) ++ (u ∷ Γ)) h₂ (λ z → Π ≡ subst (λ z → Form z ((t > $o) > $o)) h₁ (sub-i (Γ' ++ (v ∷ ε)) Γ G z)) (erase-subst (Ctx _) (λ z → Form z ((t > $o) > $o)) Π ((Γ' ++ (v ∷ ε)) ++ Γ) (Γ' ++ (v ∷ Γ)) h₁ (λ z → Π ≡ z) refl)  -- rewrite h₂ | h₁ = refl
 sub-weak-p-1-i {_} {(t > $o) > .t} {u} {v} {Γ} Γ' i G h₁ h₂ = erase-subst (Ctx _) (λ z → Form z ((t > $o) > t)) i (Γ' ++ (v ∷ (u ∷ Γ))) ((Γ' ++ (v ∷ ε)) ++ (u ∷ Γ)) h₂ (λ z → i ≡ subst (λ z → Form z ((t > $o) > t)) h₁ (sub-i (Γ' ++ (v ∷ ε)) Γ G z)) (erase-subst (Ctx _) (λ z → Form z ((t > $o) > t)) i ((Γ' ++ (v ∷ ε)) ++ Γ) (Γ' ++ (v ∷ Γ)) h₁ (λ z → i ≡ z) refl)  -- rewrite h₂ | h₁ = refl
 sub-weak-p-1-i {_} {t} {u} {v} {Γ} Γ' (app f₁ f₂) G h₁ h₂ = sub-weak-p-1-app-1 Γ' f₁ f₂ G h₁ (Γ' ++ (v ∷ (u ∷ Γ))) h₂ refl
 sub-weak-p-1-i {_} {α > β} {u} {v} {Γ} Γ' (lam .α f) G h₁ h₂ = sub-weak-p-1-lam-1 Γ' f G h₁ (Γ' ++ (v ∷ (u ∷ Γ))) h₂ refl

sub-weak-p-1'-var : ∀ {n} → {t u : Type n} {Γ : Ctx n} (Γ' : Ctx n) (x : Var (Γ' ++ Γ)) (G : Form Γ u)
 (p : lookup-Var (Γ' ++ Γ) x ≡ t) →
 var x p ≡ sub-var Γ' Γ G (weak-var Γ' Γ x) (trans (weak-var-p Γ' Γ x) p)
sub-weak-p-1'-var ε x G p = refl
sub-weak-p-1'-var (v ∷ Γ') this G p = refl
sub-weak-p-1'-var {_} {_} {_} {Γ} (v ∷ Γ') (next x) G p =
 subst (λ z → var (next {_} {v} x) p ≡ weak-i ε (Γ' ++ Γ) z)
  (sub-weak-p-1'-var Γ' x G p)
  refl

sub-weak-p-1'-i : ∀ {n} → {t u : Type n} {Γ : Ctx n} (Γ' : Ctx n) (F : Form (Γ' ++ Γ) t) (G : Form Γ u) →
  F ≡ sub-i Γ' Γ G (weak-i Γ' Γ F)
sub-weak-p-1'-i Γ' (var x p) G = sub-weak-p-1'-var Γ' x G p
sub-weak-p-1'-i Γ' N G = refl
sub-weak-p-1'-i Γ' A G = refl
sub-weak-p-1'-i Γ' Π G = refl
sub-weak-p-1'-i Γ' i G = refl
sub-weak-p-1'-i {_} {_} {_} {Γ} Γ' (app f₁ f₂) G = trans (cong (λ z → app z f₂) (sub-weak-p-1'-i Γ' f₁ G)) ((cong (λ z → app (sub-i Γ' Γ G (weak-i Γ' Γ f₁)) z) (sub-weak-p-1'-i Γ' f₂ G)))
sub-weak-p-1'-i Γ' (lam α f) G = cong (lam α) (sub-weak-p-1'-i (α ∷ Γ') f G)

sub-weak-p-1 : ∀ {n} → {t u v : Type n} {Γ : Ctx n} (F : Form Γ t) (G : Form Γ u) →
  weak-i ε Γ F ≡ sub-i (v ∷ ε) Γ G (weak-i ε (u ∷ Γ) (weak-i ε Γ F))
sub-weak-p-1 F G = sub-weak-p-1-i ε F G refl refl

sub-weak-p-23 : ∀ {n} → {t u v : Type n} {Γ : Ctx n} (F : Form Γ t) (G : Form (v ∷ Γ) u) →
 weak-i ε Γ F ≡ sub-i ε (v ∷ Γ) G (weak-i (u ∷ ε) Γ (weak-i ε Γ F))
sub-weak-p-23 F G = sub-weak-p-23-i ε F G refl refl

sub-weak-p-1' : ∀ {n} → {t u : Type n} {Γ : Ctx n} (F : Form Γ t) (G : Form Γ u) →
  F ≡ sub G (weak F)
sub-weak-p-1' F G = sub-weak-p-1'-i ε F G

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

weak-var-irr-proof-2 : ∀ {n} {Γ : Ctx n} (t : Type n) (x : Var Γ)
 (p₁ : lookup-Var Γ x ≡ t)
 (p₂ : lookup-Var Γ x ≡ t) →
 var x p₁ ≡ var x p₂
weak-var-irr-proof-2 {n} {Γ} .(lookup-Var Γ x) x refl refl = refl

weak-var-irr-proof : ∀ {n} {Γ : Ctx n} (t : Type n) (x₁ x₂ : Var Γ)
 (p₁ : lookup-Var Γ x₁ ≡ t)
 (p₂ : lookup-Var Γ x₂ ≡ t) →
 x₁ ≡ x₂ →
 var x₁ p₁ ≡ var x₂ p₂
weak-var-irr-proof {n} {Γ} t .x₂ x₂ p₁ p₂ refl = weak-var-irr-proof-2 t x₂ p₁ p₂  -- rewrite h = weak-var-irr-proof-2 t x₂ p₁ p₂

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

weak-weak-var-p-1-this : ∀ {n} Γ₁ Γ₂ → (t u v w : Type n)
 (Γ'₁ Γ'₂ : Ctx n) →
 (h₁ : w ∷ Γ'₁ ≡ w ∷ ((Γ₂ ++ (t ∷ ε)) ++ Γ₁))
 (h₂ : w ∷ Γ'₂ ≡ w ∷ ((Γ₂ ++ (t ∷ ε)) ++ (u ∷ Γ₁))) →
 weak-var (w ∷ (Γ₂ ++ (t ∷ ε))) Γ₁ (subst Var h₁ this) ≡
 subst Var h₂ this
weak-weak-var-p-1-this Γ₁ Γ₂ t u v w ._ ._ refl refl = refl

mutual
 weak-weak-var-p-1-next : ∀ {n} Γ₁ Γ₂ → (t u v w : Type n) (x : Var (Γ₂ ++ Γ₁))
  (Γ'₁ Γ'₂ : Ctx n) →
  (h₁₁ : w ∷ Γ'₁ ≡ w ∷ ((Γ₂ ++ (t ∷ ε)) ++ Γ₁))
  (h₁₂ : Γ₂ ++ (t ∷ Γ₁) ≡ Γ'₁)
  (h₂₁ : w ∷ Γ'₂ ≡ w ∷ ((Γ₂ ++ (t ∷ ε)) ++ (u ∷ Γ₁)))
  (h₂₂ : Γ₂ ++ (t ∷ (u ∷ Γ₁)) ≡ Γ'₂) →
  weak-var (w ∷ (Γ₂ ++ (t ∷ ε))) Γ₁ (subst Var h₁₁ (next (subst Var h₁₂ (weak-var Γ₂ Γ₁ x)))) ≡
  subst Var h₂₁ (next (subst Var h₂₂ (weak-var Γ₂ (u ∷ Γ₁) (weak-var Γ₂ Γ₁ x))))
 weak-weak-var-p-1-next Γ₁ Γ₂ t u v w x ._ ._ refl h₁₂ refl h₂₂ = cong next (weak-weak-var-p-1 Γ₁ Γ₂ t u v x h₁₂ h₂₂)

 weak-weak-var-p-1 : ∀ {n} Γ₁ Γ₂ → (t u v : Type n) (x : Var (Γ₂ ++ Γ₁))
  (h₁ : Γ₂ ++ (t ∷ Γ₁) ≡ (Γ₂ ++ (t ∷ ε)) ++ Γ₁)
  (h₂ : Γ₂ ++ (t ∷ (u ∷ Γ₁)) ≡ (Γ₂ ++ (t ∷ ε)) ++ (u ∷ Γ₁)) →
  weak-var (Γ₂ ++ (t ∷ ε)) Γ₁ (subst Var h₁ (weak-var Γ₂ Γ₁ x)) ≡
  subst Var h₂ (weak-var Γ₂ (u ∷ Γ₁) (weak-var Γ₂ Γ₁ x))
 weak-weak-var-p-1 Γ₂ ε t u v x refl refl = refl
 weak-weak-var-p-1 Γ₁ (w ∷ Γ₂) t u v this h₁ h₂ = weak-weak-var-p-1-this Γ₁ Γ₂ t u v w _ _ h₁ h₂
 weak-weak-var-p-1 Γ₁ (w ∷ Γ₂) t u v (next x) h₁ h₂ = weak-weak-var-p-1-next Γ₁ Γ₂ t u v w x _ _ h₁ refl h₂ refl

weak-weak-p-1-var : ∀ {n} Γ₁ Γ₂ → (t u v : Type n) (x : Var (Γ₂ ++ Γ₁))
 (Γ'₁ Γ'₂ : Ctx n) →
 (h₁₁ : Γ'₁ ≡ (Γ₂ ++ (t ∷ ε)) ++ Γ₁) →
 (h₁₂ : Γ₂ ++ (t ∷ Γ₁) ≡ Γ'₁) →
 (h₂₁ : Γ'₂ ≡ (Γ₂ ++ (t ∷ ε)) ++ (u ∷ Γ₁)) →
 (h₂₂ : Γ₂ ++ (t ∷ (u ∷ Γ₁)) ≡ Γ'₂) →
 (p₁ : lookup-Var Γ'₁ (subst Var h₁₂ (weak-var Γ₂ Γ₁ x)) ≡ v)
 (p₂ : lookup-Var Γ'₂ (subst Var h₂₂ (weak-var Γ₂ (u ∷ Γ₁) (weak-var Γ₂ Γ₁ x))) ≡ v) →
 weak-i (Γ₂ ++ (t ∷ ε)) Γ₁ (subst (λ z → Form z v) h₁₁ (var (subst Var h₁₂ (weak-var Γ₂ Γ₁ x)) p₁)) ≡
 subst (λ z → Form z v) h₂₁ (var (subst Var h₂₂ (weak-var Γ₂ (u ∷ Γ₁) (weak-var Γ₂ Γ₁ x))) p₂)
weak-weak-p-1-var Γ₁ Γ₂ t u v x .((Γ₂ ++ (t ∷ ε)) ++ Γ₁) .((Γ₂ ++ (t ∷ ε)) ++ (u ∷ Γ₁)) refl h₁₂ refl h₂₂ p₁ p₂ = weak-var-irr-proof _ (weak-var (Γ₂ ++ (t ∷ ε)) Γ₁ (subst Var h₁₂ (weak-var Γ₂ Γ₁ x))) (subst Var h₂₂ (weak-var Γ₂ (u ∷ Γ₁) (weak-var Γ₂ Γ₁ x))) (trans (weak-var-p (Γ₂ ++ (t ∷ ε)) Γ₁ (subst Var h₁₂ (weak-var Γ₂ Γ₁ x))) p₁) p₂ (weak-weak-var-p-1 Γ₁ Γ₂ t u v x h₁₂ h₂₂)

mutual
 weak-weak-p-1-app : ∀ {n} Γ₁ Γ₂ → (t u v α : Type n) (f₁ : Form (Γ₂ ++ Γ₁) (α > v)) (f₂ : Form (Γ₂ ++ Γ₁) α)
  (Γ'₁ Γ'₂ : Ctx n) →
  (h₁₁ : Γ'₁ ≡ (Γ₂ ++ (t ∷ ε)) ++ Γ₁) →
  (h₁₂ : Γ₂ ++ (t ∷ Γ₁) ≡ Γ'₁) →
  (h₂₁ : Γ'₂ ≡ (Γ₂ ++ (t ∷ ε)) ++ (u ∷ Γ₁)) →
  (h₂₂ : Γ₂ ++ (t ∷ (u ∷ Γ₁)) ≡ Γ'₂) →
  weak-i (Γ₂ ++ (t ∷ ε)) Γ₁ (subst (λ z → Form z v) h₁₁ (app (subst (λ z → Form z (α > v)) h₁₂ (weak-i Γ₂ Γ₁ f₁)) (subst (λ z → Form z α) h₁₂ (weak-i Γ₂ Γ₁ f₂)))) ≡
  subst (λ z → Form z v) h₂₁ (app (subst (λ z → Form z (α > v)) h₂₂ (weak-i Γ₂ (u ∷ Γ₁) (weak-i Γ₂ Γ₁ f₁))) (subst (λ z → Form z α) h₂₂ (weak-i Γ₂ (u ∷ Γ₁) (weak-i Γ₂ Γ₁ f₂))))
 weak-weak-p-1-app Γ₁ Γ₂ t u v α f₁ f₂ ._ ._ refl h₁₂ refl h₂₂ =
  trans (cong (λ z → app z (weak-i (Γ₂ ++ (t ∷ ε)) Γ₁ (subst (λ z → Form z α) h₁₂ (weak-i Γ₂ Γ₁ f₂)))) (weak-weak-p-1-i Γ₁ Γ₂ t u (α > v) f₁ h₁₂ h₂₂)) (cong (app (subst (λ z → Form z (α > v)) h₂₂ (weak-i Γ₂ (u ∷ Γ₁) (weak-i Γ₂ Γ₁ f₁)))) (weak-weak-p-1-i Γ₁ Γ₂ t u α f₂ h₁₂ h₂₂))

 weak-weak-p-1-lam : ∀ {n} Γ₁ Γ₂ → (t u α β : Type n) (X : Form (α ∷ (Γ₂ ++ Γ₁)) β)
  (Γ'₁ Γ'₂ : Ctx n) →
  (h₁₁ : Γ'₁ ≡ (Γ₂ ++ (t ∷ ε)) ++ Γ₁) →
  (h₁₂ : Γ₂ ++ (t ∷ Γ₁) ≡ Γ'₁) →
  (h₂₁ : Γ'₂ ≡ (Γ₂ ++ (t ∷ ε)) ++ (u ∷ Γ₁)) →
  (h₂₂ : Γ₂ ++ (t ∷ (u ∷ Γ₁)) ≡ Γ'₂) →
  weak-i (Γ₂ ++ (t ∷ ε)) Γ₁ (subst (λ z → Form z (α > β)) h₁₁ (lam α (subst (λ z → Form z β) (cong (_∷_ α) h₁₂) (weak-i (α ∷ Γ₂) Γ₁ X)))) ≡
  subst (λ z → Form z (α > β)) h₂₁ (lam α (subst (λ z → Form z β) (cong (_∷_ α) h₂₂) (weak-i (α ∷ Γ₂) (u ∷ Γ₁) (weak-i (α ∷ Γ₂) Γ₁ X))))
 weak-weak-p-1-lam Γ₁ Γ₂ t u α β X .((Γ₂ ++ (t ∷ ε)) ++ Γ₁) .((Γ₂ ++ (t ∷ ε)) ++ (u ∷ Γ₁)) refl h₁₂ refl h₂₂ = cong (lam α) (weak-weak-p-1-i Γ₁ (α ∷ Γ₂) t u β X (cong (_∷_ α) h₁₂) (cong (_∷_ α) h₂₂))

 weak-weak-p-1-i : ∀ {n} Γ₁ Γ₂ → (t u v : Type n) (X : Form (Γ₂ ++ Γ₁) v) →
  (h₁ : Γ₂ ++ (t ∷ Γ₁) ≡ (Γ₂ ++ (t ∷ ε)) ++ Γ₁) →
  (h₂ : Γ₂ ++ (t ∷ (u ∷ Γ₁)) ≡ (Γ₂ ++ (t ∷ ε)) ++ (u ∷ Γ₁)) →
  weak-i {n} {v} {u} (Γ₂ ++ (t ∷ ε)) Γ₁ (subst (λ z → Form z v) h₁ (weak-i {n} {v} {t} Γ₂ Γ₁ X)) ≡ subst (λ z → Form z v) h₂ (weak-i {n} {v} {t} Γ₂ (u ∷ Γ₁) (weak-i {n} {v} {u} Γ₂ Γ₁ X))
 weak-weak-p-1-i Γ₁ Γ₂ t u v (var x p) h₁ h₂ = weak-weak-p-1-var Γ₁ Γ₂ t u v x _ _ h₁ refl h₂ refl (trans (weak-var-p Γ₂ Γ₁ x) p) (trans (weak-var-p Γ₂ (u ∷ Γ₁) (weak-var Γ₂ Γ₁ x)) (trans (weak-var-p Γ₂ Γ₁ x) p))
 weak-weak-p-1-i Γ₁ Γ₂ t u .($o > $o) N h₁ h₂ = erase-subst (Ctx _) (λ z → Form z ($o > $o)) N (Γ₂ ++ (t ∷ (u ∷ Γ₁))) ((Γ₂ ++ (t ∷ ε)) ++ (u ∷ Γ₁)) h₂ (λ z → weak-i (Γ₂ ++ (t ∷ ε)) Γ₁ (subst (λ z → Form z ($o > $o)) h₁ N) ≡ z) (erase-subst (Ctx _) (λ z → Form z ($o > $o)) N (Γ₂ ++ (t ∷ Γ₁)) ((Γ₂ ++ (t ∷ ε)) ++ Γ₁) h₁ (λ z → weak-i {_} {_} {u} (Γ₂ ++ (t ∷ ε)) Γ₁ z ≡ N) refl)  -- rewrite h₂ | h₁ = refl
 weak-weak-p-1-i Γ₁ Γ₂ t u .($o > ($o > $o)) A h₁ h₂ = erase-subst (Ctx _) (λ z → Form z ($o > ($o > $o))) A (Γ₂ ++ (t ∷ (u ∷ Γ₁))) ((Γ₂ ++ (t ∷ ε)) ++ (u ∷ Γ₁)) h₂ (λ z → weak-i (Γ₂ ++ (t ∷ ε)) Γ₁ (subst (λ z → Form z ($o > ($o > $o))) h₁ A) ≡ z) (erase-subst (Ctx _) (λ z → Form z ($o > ($o > $o))) A (Γ₂ ++ (t ∷ Γ₁)) ((Γ₂ ++ (t ∷ ε)) ++ Γ₁) h₁ (λ z → weak-i {_} {_} {u} (Γ₂ ++ (t ∷ ε)) Γ₁ z ≡ A) refl)  -- rewrite h₂ | h₁ = refl
 weak-weak-p-1-i Γ₁ Γ₂ t u .((α > $o) > $o) (Π {._} {α}) h₁ h₂ = erase-subst (Ctx _) (λ z → Form z ((α > $o) > $o)) Π (Γ₂ ++ (t ∷ (u ∷ Γ₁))) ((Γ₂ ++ (t ∷ ε)) ++ (u ∷ Γ₁)) h₂ (λ z → weak-i (Γ₂ ++ (t ∷ ε)) Γ₁ (subst (λ z → Form z ((α > $o) > $o)) h₁ Π) ≡ z) (erase-subst (Ctx _) (λ z → Form z ((α > $o) > $o)) Π (Γ₂ ++ (t ∷ Γ₁)) ((Γ₂ ++ (t ∷ ε)) ++ Γ₁) h₁ (λ z → weak-i {_} {_} {u} (Γ₂ ++ (t ∷ ε)) Γ₁ z ≡ Π) refl)  -- rewrite h₂ | h₁ = refl
 weak-weak-p-1-i Γ₁ Γ₂ t u .((α > $o) > α) (i {._} {α}) h₁ h₂ = erase-subst (Ctx _) (λ z → Form z ((α > $o) > α)) i (Γ₂ ++ (t ∷ (u ∷ Γ₁))) ((Γ₂ ++ (t ∷ ε)) ++ (u ∷ Γ₁)) h₂ (λ z → weak-i (Γ₂ ++ (t ∷ ε)) Γ₁ (subst (λ z → Form z ((α > $o) > α)) h₁ i) ≡ z) (erase-subst (Ctx _) (λ z → Form z ((α > $o) > α)) i (Γ₂ ++ (t ∷ Γ₁)) ((Γ₂ ++ (t ∷ ε)) ++ Γ₁) h₁ (λ z → weak-i {_} {_} {u} (Γ₂ ++ (t ∷ ε)) Γ₁ z ≡ i) refl)  -- rewrite h₂ | h₁ = refl
 weak-weak-p-1-i Γ₁ Γ₂ t u v (app f₁ f₂) h₁ h₂ = weak-weak-p-1-app Γ₁ Γ₂ t u v _ f₁ f₂ _ _ h₁ refl h₂ refl
 weak-weak-p-1-i Γ₁ Γ₂ t u .(α > β) (lam {._} {β} α f) h₁ h₂ = weak-weak-p-1-lam Γ₁ Γ₂ t u α β f _ _ h₁ refl h₂ refl

weak-weak-p-1 : ∀ {n} Γ → (t u v : Type n) (X : Form Γ v) →
  weak-i {n} {v} {u} (t ∷ ε) Γ (weak-i {n} {v} {t} ε Γ X) ≡ weak-i {n} {v} {t} ε (u ∷ Γ) (weak-i {n} {v} {u} ε Γ X)
weak-weak-p-1 Γ t u v X = weak-weak-p-1-i Γ ε t u v X refl refl

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

thevar : ∀ {n Γ} → (Γ' : Ctx n) (α : Type n) → Var (Γ' ++ (α ∷ Γ))
thevar ε α = this
thevar (β ∷ Γ') α = next (thevar Γ' α)

occurs-p-2-i-var : ∀ {n} {Γ : Ctx n} {α β : Type n} (Γ' : Ctx n)
 (v : Var (Γ' ++ Γ))
 (p : lookup-Var (Γ' ++ Γ) v ≡ β) →
 eq-Var (thevar Γ' α) (weak-var Γ' Γ v) ≡ false
occurs-p-2-i-var ε v p = refl
occurs-p-2-i-var (γ ∷ Γ') this p = refl
occurs-p-2-i-var (γ ∷ Γ') (next v) p = occurs-p-2-i-var Γ' v p

occurs-p-2-i : ∀ {n} {Γ : Ctx n} {α β : Type n} (Γ' : Ctx n) (F : Form (Γ' ++ Γ) β) →
 occurs {n} {Γ' ++ (α ∷ Γ)} (thevar Γ' α) (weak-i Γ' Γ F) ≡ false
occurs-p-2-i Γ' (var v p) = occurs-p-2-i-var Γ' v p
occurs-p-2-i Γ' N = refl
occurs-p-2-i Γ' A = refl
occurs-p-2-i Γ' Π = refl
occurs-p-2-i Γ' i = refl
occurs-p-2-i {n} {Γ} {α} {β} Γ' (app f₁ f₂) = subst (λ z → z ∨ occurs (thevar Γ' α) (weak-i Γ' Γ f₂) ≡ false) (sym (occurs-p-2-i {n} {Γ} {α} {_ > β} Γ' f₁)) (occurs-p-2-i {n} {Γ} {α} Γ' f₂)  -- rewrite occurs-p-2-i {n} {Γ} {α} {_ > β} Γ' f₁ | occurs-p-2-i {n} {Γ} {α} Γ' f₂ = refl
occurs-p-2-i Γ' (lam γ f) = occurs-p-2-i (γ ∷ Γ') f

occurs-p-2 : ∀ {n} {Γ : Ctx n} {α β : Type n} (F : Form Γ β) →
 occurs {n} {α ∷ Γ} this (weak F) ≡ false
occurs-p-2 F = occurs-p-2-i ε F

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

sub-sub-weak-weak-p-3-i-var-2-this : ∀ {n} → {Γ Γ' : Ctx n} {α β γ : Type n} →
 (Γ1 Γ2 : Ctx n) →
 (eq1 : γ ∷ Γ1 ≡ γ ∷ (Γ' ++ (α ∷ (α ∷ Γ)))) →
 (eq2 : γ ∷ Γ2 ≡ γ ∷ (Γ' ++ (α ∷ Γ))) →
 (p1 : lookup-Var (γ ∷ (Γ' ++ (α ∷ (α ∷ Γ)))) (subst Var eq1 this) ≡ β) →
 (p2 : lookup-Var (γ ∷ (Γ' ++ (α ∷ Γ))) (subst Var eq2 this) ≡ β) →
 sub-var (γ ∷ Γ') (α ∷ Γ) (var this refl) (subst Var eq1 this) p1 ≡ var (subst Var eq2 this) p2
sub-sub-weak-weak-p-3-i-var-2-this {n} {Γ} {Γ'} {α} .(Γ' ++ (α ∷ (α ∷ Γ))) .(Γ' ++ (α ∷ Γ)) refl refl refl refl = refl

mutual
 sub-sub-weak-weak-p-3-i-var-2-next : ∀ {n} → {Γ Γ' : Ctx n} {α β γ : Type n}
  (x : Var ((Γ' ++ (α ∷ ε)) ++ Γ)) →
  (Γ1 Γ2 : Ctx n) →
  (eq1 : γ ∷ Γ1 ≡ γ ∷ (Γ' ++ (α ∷ (α ∷ Γ)))) →
  (eq12 : (Γ' ++ (α ∷ ε)) ++ (α ∷ Γ) ≡ Γ1) →
  (eq2 : γ ∷ Γ2 ≡ γ ∷ (Γ' ++ (α ∷ Γ))) →
  (eq22 : (Γ' ++ (α ∷ ε)) ++ Γ ≡ Γ2) →
  (p1 : lookup-Var ((γ ∷ Γ') ++ (lookup-Var (α ∷ Γ) this ∷ (α ∷ Γ))) (subst Var eq1 (next (subst Var eq12 (weak-var (Γ' ++ (α ∷ ε)) Γ x)))) ≡ β) →
  (p2 : lookup-Var (γ ∷ (Γ' ++ (α ∷ Γ))) (subst Var eq2 (next (subst Var eq22 x))) ≡ β) →
  sub-var (γ ∷ Γ') (α ∷ Γ) (var this refl) (subst Var eq1 (next (subst Var eq12 (weak-var (Γ' ++ (α ∷ ε)) Γ x)))) p1
  ≡ var (subst Var eq2 (next (subst Var eq22 x))) p2
 sub-sub-weak-weak-p-3-i-var-2-next {n} {Γ} {Γ'} {α} x .(Γ' ++ (α ∷ (α ∷ Γ))) .(Γ' ++ (α ∷ Γ)) refl eq12 refl eq22 p1 p2 = cong (weak-i ε (Γ' ++ (α ∷ Γ))) (sub-sub-weak-weak-p-3-i-var-2 {n} {Γ} {Γ'} {α} x eq12 eq22 p1 p2)

 sub-sub-weak-weak-p-3-i-var-2 : ∀ {n} → {Γ Γ' : Ctx n} {α β : Type n} (x : Var ((Γ' ++ (α ∷ ε)) ++ Γ)) →
  (eq1 : (Γ' ++ (α ∷ ε)) ++ (α ∷ Γ) ≡ Γ' ++ (α ∷ (α ∷ Γ))) →
  (eq2 : (Γ' ++ (α ∷ ε)) ++ Γ ≡ Γ' ++ (α ∷ Γ)) →
  (p1 : lookup-Var (Γ' ++ (α ∷ (α ∷ Γ))) (subst Var eq1 (weak-var (Γ' ++ (α ∷ ε)) Γ x)) ≡ β) →
  (p2 : lookup-Var (Γ' ++ (α ∷ Γ)) (subst Var eq2 x) ≡ β) →
  sub-var Γ' (α ∷ Γ) (var this refl) (subst Var eq1 (weak-var (Γ' ++ (α ∷ ε)) Γ x)) p1
  ≡ var (subst Var eq2 x) p2
 sub-sub-weak-weak-p-3-i-var-2 {n} {Γ} {ε} this refl refl refl refl = refl
 sub-sub-weak-weak-p-3-i-var-2 {n} {Γ} {ε} (next x) refl refl refl refl = refl
 sub-sub-weak-weak-p-3-i-var-2 {n} {Γ} {γ ∷ Γ'} this eq1 eq2 p1 p2 = sub-sub-weak-weak-p-3-i-var-2-this {n} {Γ} {Γ'} {_} {_} {γ} ((Γ' ++ (_ ∷ ε)) ++ (_ ∷ Γ)) ((Γ' ++ (_ ∷ ε)) ++ Γ) eq1 eq2 p1 p2
 sub-sub-weak-weak-p-3-i-var-2 {n} {Γ} {γ ∷ Γ'} (next x) eq1 eq2 p1 p2 = sub-sub-weak-weak-p-3-i-var-2-next {n} {Γ} {Γ'} {_} {_} {γ} x ((Γ' ++ (_ ∷ ε)) ++ (_ ∷ Γ)) ((Γ' ++ (_ ∷ ε)) ++ Γ) eq1 refl eq2 refl p1 p2

sub-sub-weak-weak-p-3-i-var : ∀ {n} → {Γ Γ' : Ctx n} {α β : Type n} (x : Var ((Γ' ++ (α ∷ ε)) ++ Γ)) →
 (Γ1 Γ2 : Ctx n) →
 (eq11 : Γ1 ≡ Γ' ++ (α ∷ (α ∷ Γ))) →
 (eq12 : (Γ' ++ (α ∷ ε)) ++ (α ∷ Γ) ≡ Γ1) →
 (eq21 : Γ2 ≡ Γ' ++ (α ∷ Γ)) →
 (eq22 : (Γ' ++ (α ∷ ε)) ++ Γ ≡ Γ2) →
 (p1 : lookup-Var Γ1 (subst Var eq12 (weak-var (Γ' ++ (α ∷ ε)) Γ x)) ≡ β) →
 (p2 : lookup-Var Γ2 (subst Var eq22 x) ≡ β) →
 sub-i Γ' (α ∷ Γ) (var this refl) (subst (λ z → Form z β) eq11 (var (subst Var eq12 (weak-var (Γ' ++ (α ∷ ε)) Γ x)) p1))
 ≡ subst (λ z → Form z β) eq21 (var (subst Var eq22 x) p2)
sub-sub-weak-weak-p-3-i-var {n} {Γ} {Γ'} {α} x .(Γ' ++ (α ∷ (α ∷ Γ))) .(Γ' ++ (α ∷ Γ)) refl eq12 refl eq22 p1 p2 = sub-sub-weak-weak-p-3-i-var-2 {n} {Γ} {Γ'} {α} x eq12 eq22 p1 p2

mutual
 sub-sub-weak-weak-p-3-i-app : ∀ {n} → {Γ Γ' : Ctx n} {α β γ : Type n}
  (f₁ : Form ((Γ' ++ (α ∷ ε)) ++ Γ) (γ > β)) →
  (f₂ : Form ((Γ' ++ (α ∷ ε)) ++ Γ) γ) →
  (Γ1 Γ2 : Ctx n) →
  (eq1 : Γ1 ≡ Γ' ++ (α ∷ (α ∷ Γ))) →
  (eq12 : (Γ' ++ (α ∷ ε)) ++ (α ∷ Γ) ≡ Γ1) →
  (eq2 : Γ2 ≡ Γ' ++ (α ∷ Γ)) →
  (eq22 : (Γ' ++ (α ∷ ε)) ++ Γ ≡ Γ2) →
  sub-i Γ' (α ∷ Γ) (var this refl) (subst (λ z → Form z β) eq1 (app (subst (λ z → Form z (γ > β)) eq12 (weak-i (Γ' ++ (α ∷ ε)) Γ f₁)) (subst (λ z → Form z γ) eq12 (weak-i (Γ' ++ (α ∷ ε)) Γ f₂))))
  ≡ subst (λ z → Form z β) eq2 (app (subst (λ z → Form z (γ > β)) eq22 f₁) (subst (λ z → Form z γ) eq22 f₂))
 sub-sub-weak-weak-p-3-i-app {n} {Γ} {Γ'} {α} {β} {γ} f₁ f₂ .(Γ' ++ (α ∷ (α ∷ Γ))) .(Γ' ++ (α ∷ Γ)) refl eq12 refl eq22 = trans (cong (λ z → app z (sub-i Γ' (α ∷ Γ) (var this refl) (subst (λ z → Form z γ) eq12 (weak-i (Γ' ++ (α ∷ ε)) Γ f₂)))) (sub-sub-weak-weak-p-3-i {n} {Γ} {Γ'} {α} {γ > β} f₁ eq12 eq22)) (cong (app (subst (λ z → Form z (γ > β)) eq22 f₁)) (sub-sub-weak-weak-p-3-i {n} {Γ} {Γ'} {α} {γ} f₂ eq12 eq22))

 sub-sub-weak-weak-p-3-i-lam : ∀ {n} → {Γ Γ' : Ctx n} {α β γ : Type n}
  (f : Form (γ ∷ ((Γ' ++ (α ∷ ε)) ++ Γ)) β)
  (Γ1 Γ2 : Ctx n)
  (eq1 : Γ1 ≡ Γ' ++ (α ∷ (α ∷ Γ)))
  (eq12 : γ ∷ ((Γ' ++ (α ∷ ε)) ++ (α ∷ Γ)) ≡ γ ∷ Γ1)
  (eq2 : Γ2 ≡ Γ' ++ (α ∷ Γ))
  (eq22 : γ ∷ ((Γ' ++ (α ∷ ε)) ++ Γ) ≡ γ ∷ Γ2) →
  sub-i Γ' (α ∷ Γ) (var this refl) (subst (λ z → Form z (γ > β)) eq1 (lam γ (subst (λ z → Form z β) eq12 (weak-i (γ ∷ (Γ' ++ (α ∷ ε))) Γ f))))
  ≡ subst (λ z → Form z (γ > β)) eq2 (lam γ (subst (λ z → Form z β) eq22 f))
 sub-sub-weak-weak-p-3-i-lam {n} {Γ} {Γ'} {α} {β} {γ} f .(Γ' ++ (α ∷ (α ∷ Γ))) .(Γ' ++ (α ∷ Γ)) refl eq12 refl eq22 = cong (lam γ) (sub-sub-weak-weak-p-3-i {n} {Γ} {γ ∷ Γ'} {α} {β} f eq12 eq22)

 sub-sub-weak-weak-p-3-i : ∀ {n} → {Γ Γ' : Ctx n} {α β : Type n} (G : Form ((Γ' ++ (α ∷ ε)) ++ Γ) β) →
  (eq1 : (Γ' ++ (α ∷ ε)) ++ (α ∷ Γ) ≡ Γ' ++ (α ∷ (α ∷ Γ))) →
  (eq2 : (Γ' ++ (α ∷ ε)) ++ Γ ≡ Γ' ++ (α ∷ Γ)) →
  sub-i Γ' (α ∷ Γ) (var this refl) (subst (λ z → Form z β) eq1 (weak-i (Γ' ++ (α ∷ ε)) Γ G)) ≡ subst (λ z → Form z β) eq2 G
 sub-sub-weak-weak-p-3-i {n} {Γ} {Γ'} {α} {β} (var x p) eq1 eq2 = sub-sub-weak-weak-p-3-i-var {n} {Γ} {Γ'} {α} {β} x ((Γ' ++ (α ∷ ε)) ++ (α ∷ Γ)) ((Γ' ++ (α ∷ ε)) ++ Γ) eq1 refl eq2 refl (trans (weak-var-p (Γ' ++ (α ∷ ε)) Γ x) p) p
 sub-sub-weak-weak-p-3-i {_} {Γ} {Γ'} {α} {$o > $o} N eq1 eq2 = erase-subst (Ctx _) (λ z → Form z ($o > $o)) N ((Γ' ++ (α ∷ ε)) ++ Γ) (Γ' ++ (α ∷ Γ)) eq2 (λ z → sub-i Γ' (α ∷ Γ) (var this refl) (subst (λ z → Form z ($o > $o)) eq1 N) ≡ z) (erase-subst (Ctx _) (λ z → Form z ($o > $o)) N ((Γ' ++ (α ∷ ε)) ++ (α ∷ Γ)) (Γ' ++ (α ∷ (α ∷ Γ))) eq1 (λ z → sub-i Γ' (α ∷ Γ) (var this refl) z ≡ N) refl)  -- rewrite eq2 | eq1 = refl
 sub-sub-weak-weak-p-3-i {_} {Γ} {Γ'} {α} {$o > ($o > $o)} A eq1 eq2 = erase-subst (Ctx _) (λ z → Form z ($o > ($o > $o))) A ((Γ' ++ (α ∷ ε)) ++ Γ) (Γ' ++ (α ∷ Γ)) eq2 (λ z → sub-i Γ' (α ∷ Γ) (var this refl) (subst (λ z → Form z ($o > ($o > $o))) eq1 A) ≡ z) (erase-subst (Ctx _) (λ z → Form z ($o > ($o > $o))) A ((Γ' ++ (α ∷ ε)) ++ (α ∷ Γ)) (Γ' ++ (α ∷ (α ∷ Γ))) eq1 (λ z → sub-i Γ' (α ∷ Γ) (var this refl) z ≡ A) refl)  -- rewrite eq2 | eq1 = refl
 sub-sub-weak-weak-p-3-i {_} {Γ} {Γ'} {α} {(β > $o) > $o} Π eq1 eq2 = erase-subst (Ctx _) (λ z → Form z ((β > $o) > $o)) Π ((Γ' ++ (α ∷ ε)) ++ Γ) (Γ' ++ (α ∷ Γ)) eq2 (λ z → sub-i Γ' (α ∷ Γ) (var this refl) (subst (λ z → Form z ((β > $o) > $o)) eq1 Π) ≡ z) (erase-subst (Ctx _) (λ z → Form z ((β > $o) > $o)) Π ((Γ' ++ (α ∷ ε)) ++ (α ∷ Γ)) (Γ' ++ (α ∷ (α ∷ Γ))) eq1 (λ z → sub-i Γ' (α ∷ Γ) (var this refl) z ≡ Π) refl)  -- rewrite eq2 | eq1 = refl
 sub-sub-weak-weak-p-3-i {_} {Γ} {Γ'} {α} {(β > $o) > .β} i eq1 eq2 = erase-subst (Ctx _) (λ z → Form z ((β > $o) > β)) i ((Γ' ++ (α ∷ ε)) ++ Γ) (Γ' ++ (α ∷ Γ)) eq2 (λ z → sub-i Γ' (α ∷ Γ) (var this refl) (subst (λ z → Form z ((β > $o) > β)) eq1 i) ≡ z) (erase-subst (Ctx _) (λ z → Form z ((β > $o) > β)) i ((Γ' ++ (α ∷ ε)) ++ (α ∷ Γ)) (Γ' ++ (α ∷ (α ∷ Γ))) eq1 (λ z → sub-i Γ' (α ∷ Γ) (var this refl) z ≡ i) refl)  -- rewrite eq2 | eq1 = refl
 sub-sub-weak-weak-p-3-i {_} {Γ} {Γ'} {α} (app f₁ f₂) eq1 eq2 = sub-sub-weak-weak-p-3-i-app {_} {Γ} {Γ'} {α} f₁ f₂ ((Γ' ++ (α ∷ ε)) ++ (α ∷ Γ)) ((Γ' ++ (α ∷ ε)) ++ Γ) eq1 refl eq2 refl
 sub-sub-weak-weak-p-3-i {_} {Γ} {Γ'} {α} (lam γ f) eq1 eq2 = sub-sub-weak-weak-p-3-i-lam {_} {Γ} {Γ'} {α} f ((Γ' ++ (α ∷ ε)) ++ (α ∷ Γ)) ((Γ' ++ (α ∷ ε)) ++ Γ) eq1 refl eq2 refl

sub-sub-weak-weak-p-3 : ∀ {n} → {Γ : Ctx n} {α β : Type n} (G : Form (α ∷ Γ) β) →
 sub (var this refl) (weak-i (α ∷ ε) Γ G) ≡ G
sub-sub-weak-weak-p-3 G = sub-sub-weak-weak-p-3-i {_} {_} {ε} G refl refl

sub-sub-weak-weak-p : ∀ {n} → {Γ : Ctx n} {α β : Type n} (F : Form Γ β) (G : Form (α ∷ Γ) β) →
 sub (var this refl) (sub-i (α ∷ (α ∷ ε)) Γ F (weak-i (α ∷ ε) (β ∷ Γ) (weak-i (α ∷ ε) Γ G))) ≡ G
sub-sub-weak-weak-p {_} {Γ} {α} {β} F G = subst (λ z → sub (var this refl) z ≡ G) (sub-weak-p-1-i (α ∷ ε) G F refl refl) (sub-sub-weak-weak-p-3 G)

sub-sub-weak-weak-p-2 : ∀ {n} → {Γ : Ctx n} {α β : Type n} (F G : Form (α ∷ Γ) β) →
 sub (var this refl) (sub-i (α ∷ (α ∷ ε)) Γ (lam α G) (weak-i (α ∷ ε) ((α > β) ∷ Γ) (weak-i (α ∷ ε) Γ F))) ≡ F
sub-sub-weak-weak-p-2 {_} {Γ} {α} {β} F G = subst (λ z → sub (var this refl) z ≡ F) (sub-weak-p-1-i (α ∷ ε) F (lam α G) refl refl) (sub-sub-weak-weak-p-3 F)

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

mutual
 headNorm : {n : ℕ} {Γ : Ctx n} {α : Type n} → ℕ → Form Γ α → Form Γ α
-- headNorm m (app (app (lam α (lam _ (app Π (lam (_ > $o) (app (app A (app N (app (var this _) (var (next (next this)) _)))) (app (var this _) (var (next this) _))))))) F) G) = ?
 headNorm m (app F G) = headNorm' m (headNorm m F) G
 headNorm _ F = F

 headNorm' : {n : ℕ} {Γ : Ctx n} {α β : Type n} → ℕ → Form Γ (α > β) → Form Γ α → Form Γ β
 headNorm' (suc m) (lam _ F) G = headNorm m (sub G F)
 headNorm' 0 (lam _ F) G = app (lam _ F) G
 headNorm' _ F G = app F G

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

!'[_]_ : ∀ {n} → {Γ : Ctx n} → (α : Type n) → Form Γ (α > $o) → Form Γ $o
!'[ α ] F = app Π F

?'[_]_ : ∀ {n} → {Γ : Ctx n} → (α : Type n) → Form Γ (α > $o) → Form Γ $o
?'[ α ] F = ?[ α ] (weak F · $ this {refl})

ι' : ∀ {n} → {Γ : Ctx n} → (α : Type n) → Form Γ (α > $o) → Form Γ α
ι' α F = app i F


------------------------------------------------------------------------------
-- Totality properties respect to OrdList
------------------------------------------------------------------------------

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

module FOTC.Program.SortList.Properties.Totality.OrdListI where

open import Common.FOL.Relation.Binary.EqReasoning

open import FOTC.Base
open import FOTC.Base.List
open import FOTC.Data.Bool
open import FOTC.Data.Bool.PropertiesI
open import FOTC.Data.Nat.Inequalities
open import FOTC.Data.Nat.List.Type
open import FOTC.Data.Nat.Type
open import FOTC.Data.List
open import FOTC.Data.List.PropertiesI
open import FOTC.Program.SortList.Properties.Totality.BoolI
open import FOTC.Program.SortList.SortList

------------------------------------------------------------------------------
-- If (i ∷ is) is ordered then 'is' is ordered.
subList-OrdList : ∀ {i is} → N i → ListN is → OrdList (i ∷ is) → OrdList is
subList-OrdList {i} Ni lnnil LOi∷is = ordList-[]

subList-OrdList {i} Ni (lncons {j} {js} Nj Ljs) LOi∷j∷js =
  &&-list₂-t₂ (le-ItemList-Bool Ni (lncons Nj Ljs))
              (ordList-Bool (lncons Nj Ljs))
              (trans (sym (ordList-∷ i (j ∷ js))) LOi∷j∷js)

++-OrdList-helper : ∀ {item is js} → N item → ListN is → ListN js →
                    ≤-ItemList item is →
                    ≤-ItemList item js →
                    ≤-Lists is js →
                    ≤-ItemList item (is ++ js)
++-OrdList-helper {item} {js = js} _ lnnil _ _ item≤js _ =
  subst (≤-ItemList item) (sym (++-leftIdentity js)) item≤js

++-OrdList-helper {item} {js = js} Nitem
                  (lncons {i} {is} Ni LNis) LNjs item≤i∷is item≤js i∷is≤js =
  le-ItemList item ((i ∷ is) ++ js)
    ≡⟨ subst (λ t → le-ItemList item ((i ∷ is) ++ js) ≡ le-ItemList item t)
             (++-∷ i is js)
             refl
    ⟩
  le-ItemList item (i ∷ (is ++ js))
    ≡⟨ le-ItemList-∷ item i (is ++ js) ⟩
  le item i && le-ItemList item (is ++ js)
    ≡⟨ subst (λ t → le item i && le-ItemList item (is ++ js) ≡
                    t && le-ItemList item (is ++ js))
             (&&-list₂-t₁ (le-Bool Nitem Ni)
                          (le-ItemList-Bool Nitem LNis)
                          (trans (sym (le-ItemList-∷ item i is)) item≤i∷is))
             refl
    ⟩
  true && le-ItemList item (is ++ js)
    ≡⟨ subst (λ t → true && le-ItemList item (is ++ js) ≡ true && t)
             (++-OrdList-helper Nitem LNis LNjs lemma₁ item≤js lemma₂)
             refl
    ⟩
  true && true
    ≡⟨ t&&x≡x true ⟩
  true ∎
    where
    lemma₁ : le-ItemList item is ≡ true
    lemma₁ = &&-list₂-t₂ (le-Bool Nitem Ni)
                         (le-ItemList-Bool Nitem LNis)
                         (trans (sym (le-ItemList-∷ item i is)) item≤i∷is)

    lemma₂ : le-Lists is js ≡ true
    lemma₂ = &&-list₂-t₂ (le-ItemList-Bool Ni LNjs)
                         (le-Lists-Bool LNis LNjs)
                         (trans (sym (le-Lists-∷ i is js)) i∷is≤js)


-- Kapitel 1, Abschnitt D (Die Eigenschaften der Gleichheit)
module CombinatoryLogic.Equality where

open import Algebra.Definitions using (Congruent₂; LeftCongruent; RightCongruent)
open import Data.Vec using (Vec; []; _∷_; foldr₁; lookup)
open import Function using (_$_)
open import Relation.Binary using (IsEquivalence; Setoid)
open import Relation.Binary.PropositionalEquality using (_≡_; refl)

open import CombinatoryLogic.Semantics
open import CombinatoryLogic.Syntax

private
  -- A lemma used in the proofs of Propositions 1 and 2.
  lemma-CQXX : ∀ {X} → ⊢ C ∙ Q ∙ X ∙ X
  lemma-CQXX {X} =
    let s₁ = ⊢ Π ∙ (W ∙ (C ∙ Q))                        [ ax Q ]
        s₂ = ⊢ W ∙ (C ∙ Q) ∙ X                          [ Π s₁ ]
        s₃ = ⊢ Q ∙ (W ∙ (C ∙ Q) ∙ X) ∙ (C ∙ Q ∙ X ∙ X)  [ W ]
        s₄ = ⊢ C ∙ Q ∙ X ∙ X                            [ Q₁ s₂ s₃ ]
    in s₄

-- Satz 1
prop₁ : ∀ {X} → ⊢ Q ∙ X ∙ X
prop₁ {X} = s₆ where
  s₄ = ⊢ C ∙ Q ∙ X ∙ X                            [ lemma-CQXX ]
  s₅ = ⊢ Q ∙ (C ∙ Q ∙ X ∙ X) ∙ (Q ∙ X ∙ X)        [ C ]
  s₆ = ⊢ Q ∙ X ∙ X                                [ Q₁ s₄ s₅ ]

-- Satz 2
prop₂ : ∀ {X Y} → ⊢ Q ∙ X ∙ Y → ⊢ Q ∙ Y ∙ X
prop₂ {X} {Y} ⊢QXY = s₅ where
  s₁ = ⊢ Q ∙ X ∙ Y                             [ ⊢QXY ]
  -- NOTE: Curry's proof contains a mistake: CQXX should be CQXY.
  s₂ = ⊢ Q ∙ (C ∙ Q ∙ X ∙ X) ∙ (C ∙ Q ∙ X ∙ Y) [ Q₂ {Z = C ∙ Q ∙ X} s₁ ]
  s₃ = ⊢ C ∙ Q ∙ X ∙ X                         [ lemma-CQXX ]
  -- NOTE: Curry's proof uses rule Q, but there is no such rule.
  s₄ = ⊢ C ∙ Q ∙ X ∙ Y                         [ Q₁ s₃ s₂ ]
  s₅ = ⊢ Q ∙ Y ∙ X                             [ Q₁ s₄ C ]

-- Satz 3
prop₃ : ∀ {X Y Z} → ⊢ Q ∙ X ∙ Y → ⊢ Q ∙ Y ∙ Z → ⊢ Q ∙ X ∙ Z
prop₃ {X} {Y} {Z} ⊢QXY ⊢QYZ = s₃ where
  s₁ = ⊢ Q ∙ Y ∙ Z                     [ ⊢QYZ ]
  s₂ = ⊢ Q ∙ (Q ∙ X ∙ Y) ∙ (Q ∙ X ∙ Z) [ Q₂ {Z = Q ∙ X} s₁ ]
  s₃ = ⊢ Q ∙ X ∙ Z                     [ Q₁ ⊢QXY s₂ ]

-- Satz 4
prop₄ : ∀ {X Y Z} → ⊢ Q ∙ X ∙ Y → ⊢ Q ∙ (X ∙ Z) ∙ (Y ∙ Z)
prop₄ {X} {Y} {Z} Hp = s₅ where
  p₁ = λ {X} → ⊢ Q ∙ (C ∙ (B ∙ (W ∙ K)) ∙ Z ∙ X) ∙ (B ∙ (W ∙ K) ∙ X ∙ Z)  [ C ]
  p₂ = λ {X} → ⊢ Q ∙ (B ∙ (W ∙ K) ∙ X ∙ Z) ∙ (W ∙ K ∙ (X ∙ Z))            [ B ]
  p₃ = λ {X} → ⊢ Q ∙ (W ∙ K ∙ (X ∙ Z)) ∙ (K ∙ (X ∙ Z) ∙ (X ∙ Z))          [ W ]
  p₄ = λ {X} → ⊢ Q ∙ (K ∙ (X ∙ Z) ∙ (X ∙ Z)) ∙ (X ∙ Z)                    [ K ]
  p₅ = λ {X} → ⊢ Q ∙ (C ∙ (B ∙ (W ∙ K)) ∙ Z ∙ X) ∙ (X ∙ Z)                [ prop₃ p₁ $ prop₃ p₂ $ prop₃ p₃ p₄ ]
  s₁ = ⊢ Q ∙ (C ∙ (B ∙ (W ∙ K)) ∙ Z ∙ X) ∙ (C ∙ (B ∙ (W ∙ K)) ∙ Z ∙ Y)  [ Q₂ Hp ]
  s₂ = ⊢ Q ∙ (C ∙ (B ∙ (W ∙ K)) ∙ Z ∙ X) ∙ (X ∙ Z)                      [ p₅ ]
  s₃ = ⊢ Q ∙ (C ∙ (B ∙ (W ∙ K)) ∙ Z ∙ Y) ∙ (Y ∙ Z)                      [ p₅ ]
  s₄ = ⊢ Q ∙ (X ∙ Z) ∙ (C ∙ (B ∙ (W ∙ K)) ∙ Z ∙ X)                      [ prop₂ s₂ ]
  s₅ = ⊢ Q ∙ (X ∙ Z) ∙ (Y ∙ Z)                                          [ prop₃ s₄ $ prop₃ s₁ s₃ ]

-- Satz 5
prop₅ : ∀ {X Y} → X ≡ Y → ⊢ Q ∙ X ∙ Y
prop₅ {X} {Y} refl = prop₁

-- Satz 6 (first part)
isEquivalence : IsEquivalence _≈_
isEquivalence = record
  { refl = prop₁
  ; sym = prop₂
  ; trans = prop₃
  }

setoid : Setoid _ _
setoid = record
  { Carrier = Combinator
  ; _≈_ = _≈_
  ; isEquivalence = isEquivalence
  }

module Reasoning where
  module ≈ = IsEquivalence isEquivalence
  import Relation.Binary.Reasoning.Base.Single _≈_ ≈.refl ≈.trans as Base
  open Base using (begin_; _∎) public

  infixr 2 _≈⟨⟩_ step-≈ step-≈˘

  _≈⟨⟩_ : ∀ x {y} → x Base.IsRelatedTo y → x Base.IsRelatedTo y
  _ ≈⟨⟩ x≈y = x≈y

  step-≈ = Base.step-∼
  syntax step-≈ x y≈z x≈y = x ≈⟨ x≈y ⟩ y≈z

  step-≈˘ : ∀ x {y z} → y Base.IsRelatedTo z → y ≈ x → x Base.IsRelatedTo z
  step-≈˘ x y∼z y≈x = x ≈⟨ ≈.sym y≈x ⟩ y∼z
  syntax step-≈˘ x y≈z y≈x = x ≈˘⟨ y≈x ⟩ y≈z

-- Satz 6 (second part)
cong : Congruent₂ _≈_ _∙_
cong {X} {X′} {Y} {Y′} ⊢X==X′ ⊢Y==Y′ = s₃ where
  s₁ = ⊢ X ∙ Y == X ∙ Y′    [ Q₂ ⊢Y==Y′ ]
  s₂ = ⊢ X ∙ Y′ == X′ ∙ Y′  [ prop₄ ⊢X==X′ ]
  s₃ = ⊢ X ∙ Y == X′ ∙ Y′   [ prop₃ s₁ s₂ ]

-- congˡ and congʳ follow from cong, but are also just different names for
-- Q₂ and prop₄, respectively.
congˡ : LeftCongruent _≈_ _∙_
congˡ = Q₂

congʳ : RightCongruent _≈_ _∙_
congʳ = prop₄

-- TODO: Satz 7, 8

prop₉ : ∀ {X} → ⊢ I ∙ X == X
prop₉ {X} = begin
  I ∙ X      ≈⟨⟩
  W ∙ K ∙ X  ≈⟨ W ⟩
  K ∙ X ∙ X  ≈⟨ K ⟩
  X          ∎
  where open Reasoning

-- Note after the proof of Satz 9.
S : Combinator
S = B ∙ (B ∙ W) ∙ (B ∙ B ∙ C)

prop-S : ∀ {X} {Y} {Z} → ⊢ S ∙ X ∙ Y ∙ Z == X ∙ Z ∙ (Y ∙ Z)
prop-S {X} {Y} {Z} = begin
  S ∙ X ∙ Y ∙ Z                          ≈⟨⟩
  B ∙ (B ∙ W) ∙ (B ∙ B ∙ C) ∙ X ∙ Y ∙ Z  ≈⟨ congʳ $ congʳ B ⟩
  B ∙ W ∙ (B ∙ B ∙ C ∙ X) ∙ Y ∙ Z        ≈⟨ congʳ B ⟩
  W ∙ (B ∙ B ∙ C ∙ X ∙ Y) ∙ Z            ≈⟨ W ⟩
  B ∙ B ∙ C ∙ X ∙ Y ∙ Z ∙ Z              ≈⟨ congʳ $ congʳ $ congʳ B ⟩
  B ∙ (C ∙ X) ∙ Y ∙ Z ∙ Z                ≈⟨ congʳ B ⟩
  C ∙ X ∙ (Y ∙ Z) ∙ Z                    ≈⟨ C ⟩
  X ∙ Z ∙ (Y ∙ Z)                        ∎
  where open Reasoning

-- TODO: ⊢ B == S(KS)K, ⊢ C == S(BBS)(KK), ⊢ W == SS(SK), ⊢ I == SKK


------------------------------------------------------------------------
-- Integers, defined using a quotient type
------------------------------------------------------------------------

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

import Equality.Path as P

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

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

open import Prelude as P hiding (suc; _*_; _^_) renaming (_+_ to _⊕_)

open import Bijection equality-with-J using (_↔_)
open import Equality.Path.Isomorphisms eq
open import Equality.Path.Isomorphisms.Univalence eq
open import Equivalence equality-with-J as Eq using (_≃_)
open import Equivalence-relation equality-with-J
open import Erased.Cubical eq
open import Function-universe equality-with-J hiding (id; _∘_)
open import Group equality-with-J as G using (Group; Abelian; _≃ᴳ_)
open import Group.Cyclic eq as C using (Generated-by; Cyclic)
open import H-level equality-with-J
open import H-level.Closure equality-with-J
open import H-level.Truncation.Propositional eq using (∣_∣)
import Integer equality-with-J as Data
import Nat equality-with-J as Nat
open import Quotient eq as Q hiding (elim; rec)
open import Univalence-axiom equality-with-J

private
  variable
    m m₁ m₂ n n₁ n₂ : ℕ
    a               : Level
    A               : Type a
    i j p q         : A

------------------------------------------------------------------------
-- The relation used to define the integers.

-- Two pairs of natural numbers are related if they have the same
-- difference.

Same-difference : ℕ × ℕ → ℕ × ℕ → Type
Same-difference (m₁ , n₁) (m₂ , n₂) = m₁ ⊕ n₂ ≡ n₁ ⊕ m₂

-- The relation is pointwise propositional.

Same-difference-propositional :
  Is-proposition (Same-difference p q)
Same-difference-propositional = ℕ-set

-- The relation is an equivalence relation.

Same-difference-is-equivalence-relation :
  Is-equivalence-relation Same-difference
Same-difference-is-equivalence-relation = record
  { reflexive  = λ { {m , n} →
                     m ⊕ n  ≡⟨ Nat.+-comm m ⟩∎
                     n ⊕ m  ∎
                   }
  ; symmetric  = λ { {m₁ , n₁} {m₂ , n₂} hyp →
                     m₂ ⊕ n₁  ≡⟨ Nat.+-comm m₂ ⟩
                     n₁ ⊕ m₂  ≡⟨ sym hyp ⟩
                     m₁ ⊕ n₂  ≡⟨ Nat.+-comm m₁ ⟩∎
                     n₂ ⊕ m₁  ∎
                   }
  ; transitive = λ { {m₁ , n₁} {m₂ , n₂} {m₃ , n₃} hyp₁ hyp₂ →
                     Nat.+-cancellativeʳ (
                       m₁ ⊕ n₃ ⊕ m₂    ≡⟨ sym $ Nat.+-assoc m₁ ⟩
                       m₁ ⊕ (n₃ ⊕ m₂)  ≡⟨ cong (m₁ ⊕_) $ Nat.+-comm n₃ ⟩
                       m₁ ⊕ (m₂ ⊕ n₃)  ≡⟨ cong (m₁ ⊕_) hyp₂ ⟩
                       m₁ ⊕ (n₂ ⊕ m₃)  ≡⟨ Nat.+-assoc m₁ ⟩
                       m₁ ⊕ n₂ ⊕ m₃    ≡⟨ cong (_⊕ m₃) hyp₁ ⟩
                       n₁ ⊕ m₂ ⊕ m₃    ≡⟨ sym $ Nat.+-assoc n₁ ⟩
                       n₁ ⊕ (m₂ ⊕ m₃)  ≡⟨ cong (n₁ ⊕_) $ Nat.+-comm m₂ ⟩
                       n₁ ⊕ (m₃ ⊕ m₂)  ≡⟨ Nat.+-assoc n₁ ⟩∎
                       n₁ ⊕ m₃ ⊕ m₂    ∎)
                   }
  }

-- It is decidable whether the relation holds.

Same-difference-decidable : ∀ p → Dec (Same-difference p q)
Same-difference-decidable _ = _ Nat.≟ _

------------------------------------------------------------------------
-- Integers

-- Integers.

ℤ : Type
ℤ = (ℕ × ℕ) / Same-difference

-- The integers form a set.

ℤ-set : Is-set ℤ
ℤ-set = /-is-set

-- Turns natural numbers into the corresponding integers.

infix 8 +_

+_ : ℕ → ℤ
+ n = [ (n , 0) ]

-- Turns natural numbers into the corresponding negative integers.

-[_] : ℕ → ℤ
-[ n ] = [ (0 , n) ]

------------------------------------------------------------------------
-- A lemma

-- Increasing both sides of a pair by one does not affect the value of
-- the corresponding integer.

[]≡[suc,suc] : _≡_ {A = ℤ} [ (m , n) ] [ (P.suc m , P.suc n) ]
[]≡[suc,suc] {m = m} {n = n} = []-respects-relation
  (m ⊕ P.suc n  ≡⟨ sym $ Nat.suc+≡+suc m ⟩
   P.suc m ⊕ n  ≡⟨ Nat.+-comm (P.suc m) ⟩∎
   n ⊕ P.suc m  ∎)

------------------------------------------------------------------------
-- A one-to-one correspondence between two definitions of integers

-- There is a bijection between this variant of integers and the one
-- in Integer.

ℤ↔ℤ : ℤ ↔ Data.ℤ
ℤ↔ℤ = record
  { surjection = record
    { logical-equivalence = record
      { to   = to
      ; from = from
      }
    ; right-inverse-of = to∘from
    }
  ; left-inverse-of = from∘to
  }
  where
  to-lemma₁ : m₁ ⊕ P.suc n₂ ≡ m₂ →
              (Data.+ m₁) ≡ Data.+ m₂ +-[1+ n₂ ]
  to-lemma₁ {m₁ = m₁} {n₂ = n₂} {m₂ = zero} hyp =
    ⊥-elim $
    Nat.0≢+
      (zero             ≡⟨ sym hyp ⟩
       m₁ ⊕ P.suc n₂    ≡⟨ sym $ Nat.suc+≡+suc m₁ ⟩∎
       P.suc (m₁ ⊕ n₂)  ∎)
  to-lemma₁ {m₁ = m₁} {n₂ = zero} {m₂ = P.suc m₂} hyp =
    cong (Data.+_) $
    Nat.cancel-suc
      (P.suc m₁  ≡⟨ Nat.+-comm 1 ⟩
       m₁ ⊕ 1    ≡⟨ hyp ⟩∎
       P.suc m₂  ∎)
  to-lemma₁ {m₁ = m₁} {n₂ = P.suc n₂} {m₂ = P.suc m₂} hyp =
    to-lemma₁ $
    Nat.cancel-suc
      (P.suc (m₁ ⊕ P.suc n₂)  ≡⟨ Nat.suc+≡+suc m₁ ⟩
       m₁ ⊕ P.suc (P.suc n₂)  ≡⟨ hyp ⟩∎
       P.suc m₂               ∎)

  to-lemma₂ : m₁ ⊕ zero ≡ P.suc n₁ ⊕ m₂ →
              Data.+ m₁ +-[1+ n₁ ] ≡ Data.+ m₂
  to-lemma₂ {m₁ = zero} hyp =
    ⊥-elim $ Nat.0≢+ hyp
  to-lemma₂ {m₁ = P.suc m₁} {n₁ = zero} {m₂ = m₂} hyp =
    cong (Data.+_) $
    Nat.cancel-suc
      (P.suc m₁      ≡⟨ sym Nat.+-right-identity ⟩
       P.suc m₁ ⊕ 0  ≡⟨ hyp ⟩∎
       P.suc m₂      ∎)
  to-lemma₂ {m₁ = P.suc m₁} {n₁ = P.suc n₁} hyp =
    to-lemma₂ (Nat.cancel-suc hyp)

  to-lemma₃ :
    ∀ m₁ n₁ m₂ n₂ →
    m₁ ⊕ P.suc n₂ ≡ P.suc n₁ ⊕ m₂ →
    Data.+ m₁ +-[1+ n₁ ] ≡ Data.+ m₂ +-[1+ n₂ ]
  to-lemma₃ (P.suc m₁) (P.suc n₁) m₂ n₂ hyp =
    to-lemma₃ m₁ n₁ m₂ n₂ (Nat.cancel-suc hyp)
  to-lemma₃ m₁ n₁ (P.suc m₂) (P.suc n₂) hyp =
    to-lemma₃ m₁ n₁ m₂ n₂ $
    Nat.cancel-suc
      (P.suc (m₁ ⊕ P.suc n₂)  ≡⟨ Nat.suc+≡+suc m₁ ⟩
       m₁ ⊕ P.suc (P.suc n₂)  ≡⟨ hyp ⟩
       P.suc n₁ ⊕ P.suc m₂    ≡⟨ cong P.suc $ sym $ Nat.suc+≡+suc n₁ ⟩∎
       P.suc (P.suc n₁ ⊕ m₂)  ∎)
  to-lemma₃ zero n₁ zero n₂ hyp =
    cong Data.-[1+_] $
    Nat.cancel-suc
      (P.suc n₁      ≡⟨ sym Nat.+-right-identity ⟩
       P.suc n₁ ⊕ 0  ≡⟨ sym hyp ⟩∎
       P.suc n₂      ∎)
  to-lemma₃ (P.suc m₁) zero (P.suc m₂) zero hyp =
    cong (Data.+_) $
    Nat.cancel-suc $
      (P.suc m₁  ≡⟨ Nat.+-comm 1 ⟩
       m₁ ⊕ 1    ≡⟨ Nat.cancel-suc hyp ⟩∎
       P.suc m₂  ∎)
  to-lemma₃ (P.suc m₁) zero zero n₂ hyp =
    ⊥-elim $ Nat.0≢+
      (0                ≡⟨ sym $ Nat.cancel-suc hyp ⟩
       m₁ ⊕ P.suc n₂    ≡⟨ sym $ Nat.suc+≡+suc m₁ ⟩∎
       P.suc (m₁ ⊕ n₂)  ∎)
  to-lemma₃ zero n₁ (P.suc m₂) zero hyp =
    ⊥-elim $ Nat.0≢+
      (0                ≡⟨ Nat.cancel-suc hyp ⟩
       n₁ ⊕ P.suc m₂    ≡⟨ sym $ Nat.suc+≡+suc n₁ ⟩∎
       P.suc (n₁ ⊕ m₂)  ∎)

  to-lemma :
    ∀ m₁ n₁ m₂ n₂ →
    Same-difference (m₁ , n₁) (m₂ , n₂) →
    Data.+ m₁ Data.- Data.+ n₁ ≡
    Data.+ m₂ Data.- Data.+ n₂
  to-lemma m₁ zero m₂ zero hyp =
    Data.+ (m₁ ⊕ 0)  ≡⟨ cong Data.+_ hyp ⟩
    Data.+ m₂        ≡⟨ cong Data.+_ (sym Nat.+-right-identity) ⟩∎
    Data.+ (m₂ ⊕ 0)  ∎
  to-lemma m₁ zero m₂ (P.suc n₂) hyp =
    Data.+ (m₁ ⊕ 0)       ≡⟨ cong Data.+_ Nat.+-right-identity ⟩
    Data.+ m₁             ≡⟨ to-lemma₁ hyp ⟩∎
    Data.+ m₂ +-[1+ n₂ ]  ∎
  to-lemma m₁ (P.suc n₁) m₂ zero hyp =
    Data.+ m₁ +-[1+ n₁ ]  ≡⟨ to-lemma₂ hyp ⟩
    Data.+ m₂             ≡⟨ cong Data.+_ (sym Nat.+-right-identity) ⟩∎
    Data.+ (m₂ ⊕ 0)       ∎
  to-lemma m₁ (P.suc n₁) m₂ (P.suc n₂) hyp =
    Data.+ m₁ +-[1+ n₁ ]  ≡⟨ to-lemma₃ _ _ _ _ hyp ⟩∎
    Data.+ m₂ +-[1+ n₂ ]  ∎

  to : ℤ → Data.ℤ
  to = Q.rec λ where
    .[]ʳ (m , n) → Data.+ m Data.- Data.+ n

    .[]-respects-relationʳ {x = m₁ , n₁} {y = m₂ , n₂} →
      to-lemma m₁ n₁ m₂ n₂

    .is-setʳ → Data.ℤ-set

  from : Data.ℤ → ℤ
  from (Data.+ n)    = + n
  from Data.-[1+ n ] = [ (0 , P.suc n) ]

  to∘from : ∀ i → to (from i) ≡ i
  to∘from (Data.+ n) =
    to (from (Data.+ n))      ≡⟨⟩
    Data.+ (n ⊕ 0)            ≡⟨ cong Data.+_ Nat.+-right-identity ⟩∎
    Data.+ n                  ∎
  to∘from Data.-[1+ n ] =
    to (from Data.-[1+ n ])  ≡⟨⟩
    Data.-[1+ n ]            ∎

  from-+_+-[1+_] :
    ∀ m n → from (Data.+ m +-[1+ n ]) ≡ [ (m , P.suc n) ]
  from-+ zero    +-[1+ n ]       = refl _
  from-+ P.suc m +-[1+ zero ]    = []≡[suc,suc]
  from-+ P.suc m +-[1+ P.suc n ] =
    from (Data.+ P.suc m +-[1+ P.suc n ])  ≡⟨⟩
    from (Data.+ m +-[1+ n ])              ≡⟨ from-+ m +-[1+ n ] ⟩
    [ (m , P.suc n) ]                      ≡⟨ []≡[suc,suc] ⟩∎
    [ (P.suc m , P.suc (P.suc n)) ]        ∎

  from∘to : ∀ i → from (to i) ≡ i
  from∘to = Q.elim-prop λ where
    .[]ʳ (m , zero) →
      from (to (+ m))  ≡⟨⟩
      + (m ⊕ 0)        ≡⟨ cong +_ Nat.+-right-identity ⟩∎
      + m              ∎
    .[]ʳ (m , P.suc n) →
      from (to [ (m , P.suc n) ])  ≡⟨⟩
      from (Data.+ m +-[1+ n ])    ≡⟨ from-+ m +-[1+ n ] ⟩∎
      [ (m , P.suc n) ]            ∎
    .is-propositionʳ _ → ℤ-set

-- The bijection is homomorphic with respect to +_/Data.+_.

ℤ↔ℤ-+ : _↔_.to ℤ↔ℤ (+ n) ≡ Data.+ n
ℤ↔ℤ-+ {n = n} =
  Data.+ (n ⊕ 0)  ≡⟨ cong Data.+_ Nat.+-right-identity ⟩∎
  Data.+ n        ∎

-- The bijection is homomorphic with respect to -[_]/Data.-[_].

ℤ↔ℤ--[] : _↔_.to ℤ↔ℤ -[ n ] ≡ Data.-[ n ]
ℤ↔ℤ--[] {n = zero}    = Data.+ 0  ∎
ℤ↔ℤ--[] {n = P.suc n} = Data.-[1+ n ]  ∎

------------------------------------------------------------------------
-- An eliminator and a recursor with poor computational behaviour

module _ where

  private

    -- An eliminator for integers. This eliminator is not exported,
    -- because it is basically just the eliminator for quotients.

    elim :
      (P : ℤ → Type p) →
      (∀ i → Is-set (P i)) →
      (f : ∀ m n → P [ (m , n) ]) →
      (∀ {p q} (s : Same-difference p q) →
       subst P ([]-respects-relation s) (uncurry f p) ≡ uncurry f q) →
      ∀ i → P i
    elim _ P-set f resp = Q.elim λ where
      .[]ʳ                   → uncurry f
      .[]-respects-relationʳ → resp
      .is-setʳ               → P-set

    -- The following computation rule holds by definition.

    elim-[] :
      (P : ℤ → Type p) →
      (P-set : ∀ i → Is-set (P i)) →
      (f : ∀ m n → P [ (m , n) ]) →
      (resp : ∀ {p q} (s : Same-difference p q) →
              subst P ([]-respects-relation s) (uncurry f p) ≡
              uncurry f q) →
      elim P P-set f resp [ (m , n) ] ≡ f m n
    elim-[] _ _ _ _ = refl _

private

  -- A helper function used in the implementation of elim.

  elim′ :
    (P : ℤ → Type p) →
    (∀ n → P (_↔_.from ℤ↔ℤ (Data.+ n))) →
    (∀ n → P (_↔_.from ℤ↔ℤ Data.-[1+ n ])) →
    ∀ i → P (_↔_.from ℤ↔ℤ i)
  elim′ _ p n (Data.+ m)    = p m
  elim′ _ p n Data.-[1+ m ] = n m

-- An eliminator for integers.
--
-- Note that the motive does not have to be a family of sets, and
-- that the function does not need to respect the relation
-- Same-difference.

elim :
  (P : ℤ → Type p) →
  (∀ m n → P [ (m , n) ]) →
  ∀ i → P i
elim P f i =                       $⟨ elim′ P (λ n → f n 0) (λ n → f 0 (P.suc n)) (_↔_.to ℤ↔ℤ i) ⟩
  P (_↔_.from ℤ↔ℤ (_↔_.to ℤ↔ℤ i))  ↝⟨ subst P (_↔_.left-inverse-of ℤ↔ℤ i) ⟩□
  P i                              □

mutual

  -- A "computation" rule for elim.
  --
  -- Here the function is required to respect Same-difference. Note that
  -- this "computation" rule does not (at the time of writing) in
  -- general hold by definition.

  elim-[] :
    {P : ℤ → Type p}
    (f : ∀ m n → P [ (m , n) ]) →
    (∀ {p q} (s : Same-difference p q) →
     subst P ([]-respects-relation s) (uncurry f p) ≡ uncurry f q) →
    elim P f [ (m , n) ] ≡ f m n
  elim-[] f hyp = elim-[]′ f (λ _ _ → hyp _)

  -- A variant of the computation rule in which the requirement to
  -- respect Same-difference has been replaced by a more specific
  -- condition.

  elim-[]′ :
    {P : ℤ → Type p}
    (f : ∀ m n → P [ (m , n) ]) →
    (∀ m n → subst P []≡[suc,suc] (f m n) ≡ f (P.suc m) (P.suc n)) →
    elim P f [ (m , n) ] ≡ f m n
  elim-[]′ {m = m} {n = zero} {P = P} f hyp =
    elim P f [ (m , 0) ]                                  ≡⟨⟩
    subst P (cong +_ Nat.+-right-identity) (f (m ⊕ 0) 0)  ≡⟨ sym $ subst-∘ _ _ _ ⟩
    subst (P ∘ +_) Nat.+-right-identity (f (m ⊕ 0) 0)     ≡⟨ elim¹ (λ eq → subst (P ∘ +_) eq (f _ _) ≡ f _ _) (subst-refl _ _) _ ⟩∎
    f m 0                                                 ∎

  elim-[]′ {m = m} {n = P.suc n} {P = P} f hyp =
    elim P f [ (m , P.suc n) ]                                          ≡⟨⟩

    subst P (_↔_.left-inverse-of ℤ↔ℤ [ (m , P.suc n) ]) $
      elim′ P (λ n → f n 0) (λ n → f 0 (P.suc n)) (Data.+ m +-[1+ n ])  ≡⟨ lemma m n ⟩∎

    f m (P.suc n)                                                       ∎
    where
    lemma :
      ∀ m n →
      subst P (_↔_.left-inverse-of ℤ↔ℤ [ (m , P.suc n) ])
        (elim′ P (λ n → f n 0) (λ n → f 0 (P.suc n))
           (Data.+ m +-[1+ n ])) ≡
      f m (P.suc n)
    lemma zero n =
      subst P (refl _) (f 0 (P.suc n))  ≡⟨ subst-refl _ _ ⟩∎
      f 0 (P.suc n)                     ∎
    lemma (P.suc m) zero =
      subst P []≡[suc,suc] (f m 0)  ≡⟨ hyp _ _ ⟩∎
      f (P.suc m) 1                 ∎
    lemma (P.suc m) (P.suc n) =
      subst P
        (trans (_↔_.left-inverse-of ℤ↔ℤ [ (m , P.suc n) ])
           []≡[suc,suc])
        (elim′ P (λ n → f n 0) (λ n → f 0 (P.suc n))
           (Data.+ m +-[1+ n ]))                              ≡⟨ sym $ subst-subst _ _ _ _ ⟩

      subst P []≡[suc,suc]
        (subst P (_↔_.left-inverse-of ℤ↔ℤ [ (m , P.suc n) ])
           (elim′ P (λ n → f n 0) (λ n → f 0 (P.suc n))
              (Data.+ m +-[1+ n ])))                          ≡⟨ cong (subst P ([]-respects-relation _)) $
                                                                 lemma m n ⟩

      subst P []≡[suc,suc] (f m (P.suc n))                    ≡⟨ hyp _ _ ⟩∎

      f (P.suc m) (P.suc (P.suc n))                           ∎

private

  -- A helper function used in the implementation of rec.

  rec′ : (ℕ → A) → (ℕ → A) → Data.ℤ → A
  rec′ p n (Data.+ m)    = p m
  rec′ p n Data.-[1+ m ] = n m

-- A recursor for integers.

rec : (ℕ → ℕ → A) → ℤ → A
rec {A = A} f =
  ℤ       ↔⟨ ℤ↔ℤ ⟩
  Data.ℤ  ↝⟨ rec′ (λ n → f n 0) (λ n → f 0 (P.suc n)) ⟩□
  A       □

-- The recursor could have been defined in terms of the eliminator.
--
-- The recursor is not defined in terms of the eliminator in this way
-- because (in at least some cases) this would lead to different
-- computational behaviour.

rec≡elim : (f : ℕ → ℕ → A) → rec f i ≡ elim (const _) f i
rec≡elim {i = i} f =
  rec f i                                                               ≡⟨⟩

  rec′ (λ n → f n 0) (λ n → f 0 (P.suc n)) (_↔_.to ℤ↔ℤ i)               ≡⟨ lemma (_↔_.to ℤ↔ℤ i) ⟩

  elim′ (const _) (λ n → f n 0) (λ n → f 0 (P.suc n)) (_↔_.to ℤ↔ℤ i)    ≡⟨ sym $ subst-const _ ⟩

  subst (const _) (_↔_.left-inverse-of ℤ↔ℤ i) $
    elim′ (const _) (λ n → f n 0) (λ n → f 0 (P.suc n)) (_↔_.to ℤ↔ℤ i)  ≡⟨⟩

  elim (const _) f i                                                    ∎
  where
  lemma :
    ∀ i →
    rec′ (λ n → f n 0) (λ n → f 0 (P.suc n)) i ≡
    elim′ (const _) (λ n → f n 0) (λ n → f 0 (P.suc n)) i
  lemma (Data.+ _)    = refl _
  lemma Data.-[1+ _ ] = refl _

-- A "computation" rule for rec.
--
-- Note that this "computation" rule does not (at the time of writing)
-- in general hold by definition.

rec-[] :
  (f : ℕ → ℕ → A) →
  (∀ {p q} → Same-difference p q → uncurry f p ≡ uncurry f q) →
  rec f [ (m , n) ] ≡ f m n
rec-[] {m = m} {n = n} f hyp =
  rec f [ (m , n) ]                                         ≡⟨ rec≡elim f ⟩
  elim (const _) f [ (m , n) ]                              ≡⟨ elim-[] f (λ {p q} s →
    subst (const _) ([]-respects-relation s) (uncurry f p)       ≡⟨ subst-const _ ⟩
    uncurry f p                                                  ≡⟨ hyp s ⟩∎
    uncurry f q                                                  ∎) ⟩∎
  f m n                                                     ∎

------------------------------------------------------------------------
-- Negation, addition and subtraction

-- A helper function that can be used to define unary operators on
-- integers.

unary-operator :
  (f : ℕ × ℕ → ℕ × ℕ) →
  (∀ {i j} →
   Same-difference i j →
   Same-difference (f i) (f j)) →
  ℤ → ℤ
unary-operator f resp = Q.rec λ where
  .[]ʳ i                   → [ f i ]
  .[]-respects-relationʳ s → []-respects-relation (resp s)
  .is-setʳ                 → ℤ-set

private

  -- A computation rule for unary-operator.
  --
  -- The function is not defined using the recursor above, but rather
  -- the quotient eliminator, to ensure that this computation rule
  -- holds by definition.

  unary-operator-[] :
    (f : ℕ × ℕ → ℕ × ℕ) →
    (resp : ∀ {i j} →
            Same-difference i j →
            Same-difference (f i) (f j)) →
    ∀ i → unary-operator f resp [ i ] ≡ [ f i ]
  unary-operator-[] _ _ _ = refl _

-- A helper function that can be used to define binary operators on
-- integers.

binary-operator :
  (f : ℕ × ℕ → ℕ × ℕ → ℕ × ℕ) →
  (∀ {i₁ i₂ j₁ j₂} →
   Same-difference i₁ i₂ →
   Same-difference j₁ j₂ →
   Same-difference (f i₁ j₁) (f i₂ j₂)) →
  ℤ → ℤ → ℤ
binary-operator f resp = Q.rec λ where
  .[]ʳ i → Q.rec λ where
    .[]ʳ j → [ f i j ]

    .[]-respects-relationʳ s →
      []-respects-relation (resp (Nat.+-comm (proj₁ i)) s)

    .is-setʳ → ℤ-set

  .[]-respects-relationʳ s → ⟨ext⟩ $ Q.elim-prop λ where
    .[]ʳ i → []-respects-relation (resp s (Nat.+-comm (proj₁ i)))

    .is-propositionʳ _ → +⇒≡ {n = 1} ℤ-set

  .is-setʳ →
    Π-closure ext 2 λ _ →
    ℤ-set

private

  -- A computation rule for binary-operator.
  --
  -- The function is not defined using the recursor above, but rather
  -- the quotient eliminator, to ensure that this computation rule
  -- holds by definition.

  binary-operator-[] :
    (f : ℕ × ℕ → ℕ × ℕ → ℕ × ℕ) →
    (resp : ∀ {i₁ i₂ j₁ j₂} →
            Same-difference i₁ i₂ →
            Same-difference j₁ j₂ →
            Same-difference (f i₁ j₁) (f i₂ j₂)) →
    ∀ i j → binary-operator f resp [ i ] [ j ] ≡ [ f i j ]
  binary-operator-[] _ _ _ _ = refl _

-- Negation.

infix 8 -_

-_ : ℤ → ℤ
-_ = unary-operator swap sym

-- Addition.

infixl 6 _+_

_+_ : ℤ → ℤ → ℤ
_+_ = binary-operator
  (Σ-zip _⊕_ _⊕_)
  (λ { {k₁ , k₂} {ℓ₁ , ℓ₂} {m₁ , m₂} {n₁ , n₂} hyp₁ hyp₂ →
       (k₁ ⊕ m₁) ⊕ (ℓ₂ ⊕ n₂)  ≡⟨ lemma k₁ ⟩
       (k₁ ⊕ ℓ₂) ⊕ (m₁ ⊕ n₂)  ≡⟨ cong₂ _⊕_ hyp₁ hyp₂ ⟩
       (k₂ ⊕ ℓ₁) ⊕ (m₂ ⊕ n₁)  ≡⟨ lemma k₂ ⟩∎
       (k₂ ⊕ m₂) ⊕ (ℓ₁ ⊕ n₁)  ∎
     })
  where
  lemma : ∀ _ {_ _ _} → _
  lemma a {b} {c} {d} =
    (a ⊕ b) ⊕ (c ⊕ d)  ≡⟨ sym $ Nat.+-assoc a ⟩
    a ⊕ (b ⊕ (c ⊕ d))  ≡⟨ cong (a ⊕_) $ Nat.+-assoc b ⟩
    a ⊕ ((b ⊕ c) ⊕ d)  ≡⟨ cong ((a ⊕_) ∘ (_⊕ d)) $ Nat.+-comm b ⟩
    a ⊕ ((c ⊕ b) ⊕ d)  ≡⟨ cong (a ⊕_) $ sym $ Nat.+-assoc c ⟩
    a ⊕ (c ⊕ (b ⊕ d))  ≡⟨ Nat.+-assoc a ⟩∎
    (a ⊕ c) ⊕ (b ⊕ d)  ∎

-- Subtraction.

infixl 6 _-_

_-_ : ℤ → ℤ → ℤ
i - j = i + - j

-- The implementation of negation given here matches the one in
-- Integer.

-₁≡-₁ : ∀ i → - (_↔_.from ℤ↔ℤ i) ≡ _↔_.from ℤ↔ℤ (Data.- i)
-₁≡-₁ (Data.+ zero)      = -[ 0 ]        ∎
-₁≡-₁ (Data.+ (P.suc n)) = -[ P.suc n ]  ∎
-₁≡-₁ Data.-[1+ n ]      = + P.suc n     ∎

-- A lemma used in the implementation of +≡+.

++-[1+]≡++-[1+] : + m + -[ P.suc n ] ≡ _↔_.from ℤ↔ℤ (Data.+ m +-[1+ n ])
++-[1+]≡++-[1+] {m = zero}    {n = n}       = refl _
++-[1+]≡++-[1+] {m = P.suc m} {n = zero}    =
  [ (P.suc (m ⊕ 0) , 1) ]  ≡⟨ cong (Q.[_] ∘ (_, 1) ∘ P.suc) Nat.+-right-identity ⟩
  [ (P.suc m , 1) ]        ≡⟨ sym []≡[suc,suc] ⟩∎
  [ (m , 0) ]              ∎
++-[1+]≡++-[1+] {m = P.suc m} {n = P.suc n} =
  + P.suc m + -[ P.suc (P.suc n) ]               ≡⟨ sym []≡[suc,suc] ⟩
  + m + -[ P.suc n ]                             ≡⟨ ++-[1+]≡++-[1+] ⟩
  _↔_.from ℤ↔ℤ (Data.+ m +-[1+ n ])              ≡⟨⟩
  _↔_.from ℤ↔ℤ (Data.+ P.suc m +-[1+ P.suc n ])  ∎

-- The implementation of addition given here matches the one in
-- Integer.

+≡+ :
  ∀ i →
  (_↔_.from ℤ↔ℤ i) + (_↔_.from ℤ↔ℤ j) ≡ _↔_.from ℤ↔ℤ (i Data.+ j)
+≡+ {j = Data.+ n}      (Data.+ m) = + (m ⊕ n)  ∎
+≡+ {j = Data.-[1+ n ]} (Data.+ m) = ++-[1+]≡++-[1+]

+≡+ {j = Data.+ n} Data.-[1+ m ] =
  -[ P.suc m ] + + n                 ≡⟨ []-respects-relation (
      n ⊕ P.suc m                         ≡⟨ Nat.+-comm n ⟩
      P.suc m ⊕ n                         ≡⟨ sym $ cong₂ _⊕_ (Nat.+-right-identity {n = P.suc m}) Nat.+-right-identity ⟩∎
      P.suc m ⊕ 0 ⊕ (n ⊕ 0)               ∎) ⟩
  + n + -[ P.suc m ]                 ≡⟨ ++-[1+]≡++-[1+] ⟩∎
  _↔_.from ℤ↔ℤ (Data.+ n +-[1+ m ])  ∎

+≡+ {j = Data.-[1+ n ]} Data.-[1+ m ] =
  -[ P.suc m ⊕ P.suc n ]      ≡⟨ cong (-[_] ∘ P.suc) $ sym $ Nat.suc+≡+suc _ ⟩∎
  -[ P.suc (P.suc (m ⊕ n)) ]  ∎

-- The implementation of subtraction given here matches the one in
-- Integer.

-≡- :
  ∀ i j →
  (_↔_.from ℤ↔ℤ i) - (_↔_.from ℤ↔ℤ j) ≡ _↔_.from ℤ↔ℤ (i Data.- j)
-≡- i j =
  (_↔_.from ℤ↔ℤ i) - (_↔_.from ℤ↔ℤ j)       ≡⟨⟩
  (_↔_.from ℤ↔ℤ i) + - (_↔_.from ℤ↔ℤ j)     ≡⟨ cong (λ j → _↔_.from ℤ↔ℤ i + j) $ -₁≡-₁ j ⟩
  _↔_.from ℤ↔ℤ i + _↔_.from ℤ↔ℤ (Data.- j)  ≡⟨ +≡+ i ⟩
  _↔_.from ℤ↔ℤ (i Data.+ Data.- j)          ≡⟨⟩
  _↔_.from ℤ↔ℤ (i Data.- j)                 ∎

------------------------------------------------------------------------
-- Some lemmas related to equality

-- The Same-difference relation is pointwise equivalent to equality
-- under [_] (assuming propositional extensionality).

Same-difference≃[]≡[] :
  Propositional-extensionality lzero →
  Same-difference i j ≃ ([ i ] ≡ [ j ])
Same-difference≃[]≡[] prop-ext =
  related≃[equal]
    prop-ext
    Same-difference-is-equivalence-relation
    (λ {p} → Same-difference-propositional {p = p})

-- Non-negative integers are not equal to negative integers.

+≢-[1+] : + m ≢ -[ P.suc n ]
+≢-[1+] {m = m} {n = n} =
  Stable-¬
    [ + m ≡ -[ P.suc n ]                     ↔⟨⟩
      [ (m , 0) ] ≡ [ (0 , P.suc n) ]        ↔⟨ inverse $ Same-difference≃[]≡[] prop-ext ⟩
      Same-difference (m , 0) (0 , P.suc n)  ↔⟨⟩
      m ⊕ P.suc n ≡ 0                        ↝⟨ trans (Nat.suc+≡+suc m) ⟩
      P.suc (m ⊕ n) ≡ 0                      ↝⟨ Nat.0≢+ ∘ sym ⟩□
      ⊥                                      □
    ]

-- Non-positive integers are not equal to positive integers.

+[1+]≢- : + P.suc m ≢ -[ n ]
+[1+]≢- {m = m} {n = n} =
  Stable-¬
    [ + P.suc m ≡ -[ n ]                     ↔⟨⟩
      [ (P.suc m , 0) ] ≡ [ (0 , n) ]        ↔⟨ inverse $ Same-difference≃[]≡[] prop-ext ⟩
      Same-difference (P.suc m , 0) (0 , n)  ↔⟨⟩
      P.suc m ⊕ n ≡ 0                        ↝⟨ Nat.0≢+ ∘ sym ⟩□
      ⊥                                      □
    ]

-- The +_ "constructor" is cancellative (assuming propositional
-- extensionality).

+-cancellative :
  Propositional-extensionality lzero →
  + m ≡ + n → m ≡ n
+-cancellative {m = m} {n = n} prop-ext =
  + m ≡ + n                  ↔⟨⟩
  [ (m , 0) ] ≡ [ (n , 0) ]  ↔⟨ inverse $ Same-difference≃[]≡[] prop-ext ⟩
  m ⊕ 0 ≡ 0 ⊕ n              ↝⟨ trans (sym Nat.+-right-identity) ⟩□
  m ≡ n                      □

-- The -[_] "constructor" is cancellative (assuming propositional
-- extensionality).

-[]-cancellative :
  Propositional-extensionality lzero →
  -[ m ] ≡ -[ n ] → m ≡ n
-[]-cancellative {m = m} {n = n} prop-ext =
  -[ m ] ≡ -[ n ]  ↝⟨ cong (-_) ⟩
  + m ≡ + n        ↝⟨ +-cancellative prop-ext ⟩□
  m ≡ n            □

-- Equality of integers is decidable (assuming propositional
-- extensionality).

infix 4 [_]_≟_

[_]_≟_ :
  Propositional-extensionality lzero →
  Decidable-equality ℤ
[_]_≟_ prop-ext = Q.elim-prop λ where
  .[]ʳ i → Q.elim-prop λ where
     .[]ʳ _ →
       ⊎-map (_≃_.to $ Same-difference≃[]≡[] prop-ext)
             (_∘ _≃_.from (Same-difference≃[]≡[] prop-ext))
             (Same-difference-decidable i)
     .is-propositionʳ _ →
       Dec-closure-propositional ext ℤ-set
  .is-propositionʳ _ →
    Π-closure ext 1 λ _ →
    Dec-closure-propositional ext ℤ-set

-- Erased equality of integers is decidable.

infix 4 _≟_

_≟_ : Decidable-erased-equality ℤ
_≟_ = Q.elim-prop λ where
  .[]ʳ i → Q.elim-prop λ where
     .[]ʳ _ →
       Dec-Erased-map
         (from-equivalence $ Same-difference≃[]≡[] prop-ext) $
       Dec→Dec-Erased $
       Same-difference-decidable i
     .is-propositionʳ _ →
       Is-proposition-Dec-Erased ext ℤ-set
  .is-propositionʳ _ →
    Π-closure ext 1 λ _ →
    Is-proposition-Dec-Erased ext ℤ-set

------------------------------------------------------------------------
-- The successor and predecessor functions

-- The successor function.

suc : ℤ → ℤ
suc = Q.rec λ where
  .[]ʳ (m , n) → [ (P.suc m , n) ]

  .[]-respects-relationʳ {x = m₁ , m₂} {y = n₁ , n₂} hyp →
    []-respects-relation
      (P.suc (m₁ ⊕ n₂)  ≡⟨ cong P.suc hyp ⟩
       P.suc (m₂ ⊕ n₁)  ≡⟨ Nat.suc+≡+suc _ ⟩∎
       m₂ ⊕ P.suc n₁    ∎ )

  .is-setʳ → ℤ-set

-- The function suc adds one to its input.

suc≡1+ : ∀ i → suc i ≡ + 1 + i
suc≡1+ = elim _ λ _ _ → refl _

-- The predecessor function.

pred : ℤ → ℤ
pred = Q.rec λ where
  .[]ʳ (m , n) → [ (m , P.suc n) ]

  .[]-respects-relationʳ {x = m₁ , m₂} {y = n₁ , n₂} hyp →
    []-respects-relation
      (m₁ ⊕ P.suc n₂    ≡⟨ sym $ Nat.suc+≡+suc _ ⟩
       P.suc (m₁ ⊕ n₂)  ≡⟨ cong P.suc hyp ⟩∎
       P.suc (m₂ ⊕ n₁)  ∎)

  .is-setʳ → ℤ-set

-- The function pred subtracts one from its input.

pred≡-1+ : ∀ i → pred i ≡ -[ 1 ] + i
pred≡-1+ = elim _ λ _ _ → refl _

-- An equivalence between ℤ and ℤ corresponding to the successor
-- function.

successor : ℤ ≃ ℤ
successor = Eq.↔→≃
  suc
  pred
  (elim _ λ m _ → []-respects-relation (cong P.suc (Nat.+-comm m)))
  (elim _ λ m _ → []-respects-relation (cong P.suc (Nat.+-comm m)))

------------------------------------------------------------------------
-- Positive, negative

-- The property of being positive.

Positive : ℤ → Type
Positive = Data.Positive ∘ _↔_.to ℤ↔ℤ

-- Positive is propositional.

Positive-propositional : ∀ i → Is-proposition (Positive i)
Positive-propositional _ = Data.Positive-propositional

-- The property of being negative.

Negative : ℤ → Type
Negative = Data.Negative ∘ _↔_.to ℤ↔ℤ

-- Negative is propositional.

Negative-propositional : ∀ i → Is-proposition (Negative i)
Negative-propositional _ = Data.Negative-propositional

-- No integer is both positive and negative.

¬+- : ∀ i → Positive i → Negative i → ⊥₀
¬+- _ = Data.¬+-

-- No integer is both positive and equal to zero.

¬+0 : Positive i → i ≡ + 0 → ⊥₀
¬+0 pos ≡0 = Data.¬+0 pos (_↔_.from-to ℤ↔ℤ (sym ≡0))

-- No integer is both negative and equal to zero.

¬-0 : Negative i → i ≡ + 0 → ⊥₀
¬-0 neg ≡0 = Data.¬-0 neg (_↔_.from-to ℤ↔ℤ (sym ≡0))

-- One can decide if an integer is negative, zero or positive.

-⊎0⊎+ : ∀ i → Negative i ⊎ i ≡ + 0 ⊎ Positive i
-⊎0⊎+ i =
  ⊎-map id (⊎-map (sym ∘ _↔_.to-from ℤ↔ℤ) id)
    (Data.-⊎0⊎+ $ _↔_.to ℤ↔ℤ i)

-- If i and j are positive, then i + j is positive.

>0→>0→+>0 : ∀ i j → Positive i → Positive j → Positive (i + j)
>0→>0→+>0 i j i+ j+ =                                                   $⟨ Data.>0→>0→+>0 (_↔_.to ℤ↔ℤ i) (_↔_.to ℤ↔ℤ j) i+ j+ ⟩

  Data.Positive (_↔_.to ℤ↔ℤ i Data.+ _↔_.to ℤ↔ℤ j)                      ↝⟨ subst Data.Positive $
                                                                           sym $ _↔_.from-to ℤ↔ℤ $ sym $ +≡+ (_↔_.to ℤ↔ℤ i) ⟩
  Data.Positive (_↔_.to ℤ↔ℤ (_↔_.from ℤ↔ℤ (_↔_.to ℤ↔ℤ i) +
                             _↔_.from ℤ↔ℤ (_↔_.to ℤ↔ℤ j)))              ↔⟨⟩

  Positive (_↔_.from ℤ↔ℤ (_↔_.to ℤ↔ℤ i) + _↔_.from ℤ↔ℤ (_↔_.to ℤ↔ℤ j))  ↝⟨ subst Positive $
                                                                           cong₂ _+_
                                                                             (_↔_.left-inverse-of ℤ↔ℤ i)
                                                                             (_↔_.left-inverse-of ℤ↔ℤ j) ⟩□
  Positive (i + j)                                                      □

-- If i and j are negative, then i + j is negative.

<0→<0→+<0 : ∀ i j → Negative i → Negative j → Negative (i + j)
<0→<0→+<0 i j i- j- =                                                   $⟨ Data.<0→<0→+<0 (_↔_.to ℤ↔ℤ i) (_↔_.to ℤ↔ℤ j) i- j- ⟩

  Data.Negative (_↔_.to ℤ↔ℤ i Data.+ _↔_.to ℤ↔ℤ j)                      ↝⟨ subst Data.Negative $
                                                                           sym $ _↔_.from-to ℤ↔ℤ $ sym $ +≡+ (_↔_.to ℤ↔ℤ i) ⟩
  Data.Negative (_↔_.to ℤ↔ℤ (_↔_.from ℤ↔ℤ (_↔_.to ℤ↔ℤ i) +
                             _↔_.from ℤ↔ℤ (_↔_.to ℤ↔ℤ j)))              ↔⟨⟩

  Negative (_↔_.from ℤ↔ℤ (_↔_.to ℤ↔ℤ i) + _↔_.from ℤ↔ℤ (_↔_.to ℤ↔ℤ j))  ↝⟨ subst Negative $
                                                                           cong₂ _+_
                                                                             (_↔_.left-inverse-of ℤ↔ℤ i)
                                                                             (_↔_.left-inverse-of ℤ↔ℤ j) ⟩□
  Negative (i + j)                                                      □

------------------------------------------------------------------------
-- The group of integers

-- The group of integers.

ℤ-group : Group lzero
ℤ-group .Group.Carrier        = ℤ
ℤ-group .Group.Carrier-is-set = ℤ-set
ℤ-group .Group._∘_            = _+_
ℤ-group .Group.id             = + 0
ℤ-group .Group._⁻¹            = -_
ℤ-group .Group.assoc          =
  elim _ λ m₁ n₁ → elim _ λ _ _ → elim _ λ _ _ →
  cong [_] $ cong₂ _,_
    (Nat.+-assoc m₁)
    (Nat.+-assoc n₁)
ℤ-group .Group.left-identity =
  elim _ λ _ _ → refl _
ℤ-group .Group.right-identity =
  elim _ λ _ _ →
  cong [_] $ cong₂ _,_
        Nat.+-right-identity
        Nat.+-right-identity
ℤ-group .Group.left-inverse =
  elim _ λ _ n →
  []-respects-relation (cong (_⊕ 0) $ Nat.+-comm n)
ℤ-group .Group.right-inverse =
  elim _ λ m _ →
  []-respects-relation (cong (_⊕ 0) $ Nat.+-comm m)

-- ℤ-group is isomorphic to Data.ℤ-group.

ℤ≃ᴳℤ : ℤ-group ≃ᴳ Data.ℤ-group
ℤ≃ᴳℤ = G.≃ᴳ-sym λ where
  .G.Homomorphic.related         → Eq.↔⇒≃ (inverse ℤ↔ℤ)
  .G.Homomorphic.homomorphic i _ → sym (+≡+ i)

-- ℤ-group is equal to Data.ℤ-group (assuming univalence).

ℤ≡ℤ :
  Univalence lzero →
  ℤ-group ≡ Data.ℤ-group
ℤ≡ℤ univ = _≃_.to (G.≃ᴳ≃≡ ext univ) ℤ≃ᴳℤ

-- The group of integers is generated by + 1.

ℤ-generated-by-1 : Generated-by ℤ-group (+ 1)
ℤ-generated-by-1 =
  C.≃ᴳ→Generated-by→Generated-by
    (G.≃ᴳ-sym ℤ≃ᴳℤ)
    C.ℤ-generated-by-1

-- The group of integers is cyclic.

ℤ-cyclic : Cyclic ℤ-group
ℤ-cyclic = ∣ _ , ℤ-generated-by-1 ∣

-- The group of integers is abelian.

ℤ-abelian : Abelian ℤ-group
ℤ-abelian = C.Cyclic→Abelian ℤ-group ℤ-cyclic

-- The direct product of the group of integers and the group of
-- integers is not cyclic.

ℤ×ℤ-not-cyclic : ¬ Cyclic (ℤ-group G.× ℤ-group)
ℤ×ℤ-not-cyclic =
  Cyclic (ℤ-group G.× ℤ-group)            ↝⟨ C.≃ᴳ→Cyclic→Cyclic (G.↝-× ℤ≃ᴳℤ ℤ≃ᴳℤ) ⟩
  Cyclic (Data.ℤ-group G.× Data.ℤ-group)  ↝⟨ C.ℤ×ℤ-not-cyclic ⟩□
  ⊥                                       □

-- The group of integers is not isomorphic to the direct product of
-- the group of integers and the group of integers.

ℤ≄ᴳℤ×ℤ : ¬ ℤ-group ≃ᴳ (ℤ-group G.× ℤ-group)
ℤ≄ᴳℤ×ℤ =
  ℤ-group ≃ᴳ (ℤ-group G.× ℤ-group)                 ↝⟨ G.↝ᴳ-trans (G.≃ᴳ-sym ℤ≃ᴳℤ) ∘ flip G.↝ᴳ-trans (G.↝-× ℤ≃ᴳℤ ℤ≃ᴳℤ) ⟩
  Data.ℤ-group ≃ᴳ (Data.ℤ-group G.× Data.ℤ-group)  ↝⟨ C.ℤ≄ᴳℤ×ℤ ⟩□
  ⊥                                                □

-- The group of integers is not equal to the direct product of the
-- group of integers and the group of integers.

ℤ≢ℤ×ℤ : ℤ-group ≢ (ℤ-group G.× ℤ-group)
ℤ≢ℤ×ℤ =
  ℤ-group ≡ (ℤ-group G.× ℤ-group)   ↝⟨ flip (subst (ℤ-group ≃ᴳ_)) G.↝ᴳ-refl ⟩
  ℤ-group ≃ᴳ (ℤ-group G.× ℤ-group)  ↝⟨ ℤ≄ᴳℤ×ℤ ⟩□
  ⊥                                 □


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

open import Categories.Category using () renaming (Category to Setoid-Category)
open import Categories.Category.Monoidal

module Categories.Enriched.NaturalTransformation.NaturalIsomorphism
  {o ℓ e} {V : Setoid-Category o ℓ e} (M : Monoidal V) where

open import Level

open import Categories.Category.Construction.EnrichedFunctors M
open import Categories.Enriched.Category M
open import Categories.Enriched.Functor M renaming (id to idF)
open import Categories.Enriched.NaturalTransformation M renaming (id to idNT)
open import Categories.Functor.Properties using ([_]-resp-Iso)
open import Categories.Morphism as Morphism using (Iso)
open import Categories.Category.Monoidal.Reasoning M
open import Relation.Binary using (IsEquivalence)

open Setoid-Category V renaming (Obj to ObjV; id to idV)
module M = Monoidal M
open M hiding (unitorˡ; unitorʳ; associator)
open NaturalTransformation

module NaturalIsomorphism = Morphism._≅_
open NaturalIsomorphism

-- A natural isomorphism |α : F ≅ G : C → D| is an isomorphism of F
-- and G in the functor category [C , D] between C and D.

module _ {c d} {C : Category c} {D : Category d} where

  NaturalIsomorphism : (F G : Functor C D) → Set (ℓ ⊔ e ⊔ c)
  NaturalIsomorphism F G = F ≅ G
    where open Morphism (EnrichedFunctors C D) using (_≅_)

  -- A commonly used shorthand for NaturalIsomorphism

  infix 4 _≃_

  _≃_ = NaturalIsomorphism

  module _ {F G : Functor C D} where
    private
      module F = Functor F
      module G = Functor G
    open Morphism (Underlying D) using (_≅_)

    -- Natural isomorphisms are pointwise isomorphisms: each component
    -- |α [ X ]| is an isomorphism |F X ≅ G X|.

    infixl 16 _ᵢ[_]

    _ᵢ[_] : NaturalIsomorphism F G → ∀ X → F.₀ X ≅ G.₀ X
    α ᵢ[ X ] = record
      { from = from α [ X ]
      ; to   = to α   [ X ]
      ; iso  = record { isoˡ = isoˡ α ; isoʳ = isoʳ α }
      }

  ≃-isEquivalence : IsEquivalence _≃_
  ≃-isEquivalence = Morphism.≅-isEquivalence (EnrichedFunctors C D)

  module ≃ = IsEquivalence ≃-isEquivalence

  id : {F : Functor C D} → F ≃ F
  id = ≃.refl

  _⁻¹ : {F G : Functor C D} → F ≃ G → G ≃ F
  α ⁻¹ = ≃.sym α

  infixr 9 _ⓘᵥ_

  _ⓘᵥ_ : {F G H : Functor C D} → G ≃ H → F ≃ G → F ≃ H
  α ⓘᵥ β = ≃.trans β α

  private
    module D = Underlying D

  -- Left and right unitors

  unitorˡ : {F : Functor C D} → idF ∘F F ≃ F
  unitorˡ {F} = record
    { from = record
      { comp    = λ _ → D.id
      ; commute = (refl⟩∘⟨ refl⟩⊗⟨ identityˡ ⟩∘⟨refl) ○ comm
      }
    ; to = record
      { comp    = λ _ → D.id
      ; commute = comm ○ (refl⟩∘⟨ ⟺ identityˡ ⟩⊗⟨refl ⟩∘⟨refl)
      }
    ; iso = record { isoˡ = D.identity² ; isoʳ = D.identity² }
    }
    where comm = commute (idNT {F = F})

  unitorʳ : {F : Functor C D} → F ∘F idF ≃ F
  unitorʳ {F} = record
    { from = record
      { comp    = λ _ → D.id
      ; commute = (refl⟩∘⟨ refl⟩⊗⟨ identityʳ ⟩∘⟨refl) ○ comm
      }
    ; to = record
      { comp    = λ _ → D.id
      ; commute = comm ○ (refl⟩∘⟨ ⟺ identityʳ ⟩⊗⟨refl ⟩∘⟨refl)
      }
    ; iso = record { isoˡ = D.identity² ; isoʳ = D.identity² }
    }
    where comm = commute (idNT {F = F})

module _ {c d e} {C : Category c} {D : Category d} {E : Category e} where
  open NaturalIsomorphism

  -- Left- and right-hand composition with a functor

  infixr 9 _ⓘₕ_ _ⓘˡ_ _ⓘʳ_

  _ⓘˡ_ : {F G : Functor C D} (H : Functor D E) → F ≃ G → H ∘F F ≃ H ∘F G
  H ⓘˡ α = record
    { from = H ∘ˡ from α
    ; to   = H ∘ˡ to α
    ; iso  = record { isoˡ = iso.isoˡ ; isoʳ = iso.isoʳ }
    }
    where
      module iso {X} = Iso ([ UnderlyingFunctor H ]-resp-Iso (iso (α ᵢ[ X ])))

  _ⓘʳ_ : {G H : Functor D E} → G ≃ H → (F : Functor C D) → G ∘F F ≃ H ∘F F
  α ⓘʳ F = record
    { from = from α ∘ʳ F
    ; to   = to α   ∘ʳ F
    ; iso  = record { isoˡ = isoˡ (α ᵢ[ F.₀ _ ]) ; isoʳ = isoʳ (α ᵢ[ F.₀ _ ]) }
    }
    where module F = Functor F

  -- Horizontal composition

  _ⓘₕ_ : {H I : Functor D E} {F G : Functor C D} →
           H ≃ I → F ≃ G → (H ∘F F) ≃ (I ∘F G)
  _ⓘₕ_ {_} {I} {F} {_} α β = (I ⓘˡ β) ⓘᵥ (α ⓘʳ F)

module _ {b c d e} {B : Category b} {C : Category c}
                   {D : Category d} {E : Category e} where
  open NaturalIsomorphism
  private
    module E  = Category E
    module UE = Underlying E

  -- Associator

  associator : {F : Functor D E} {G : Functor C D} {H : Functor B C} →
               (F ∘F G) ∘F H ≃ F ∘F (G ∘F H)
  associator {F} {G} {H} = record
    { from = record
      { comp    = λ _ → E.id
      ; commute = (refl⟩∘⟨ refl⟩⊗⟨ assoc ⟩∘⟨refl) ○ comm
      }
    ; to = record
      { comp    = λ _ → E.id
      ; commute = comm ○ (refl⟩∘⟨ ⟺ assoc ⟩⊗⟨refl ⟩∘⟨refl)
      }
    ; iso = record { isoˡ = UE.identity² ; isoʳ = UE.identity² }
    }
    where comm = commute (idNT {F = F ∘F (G ∘F H)})


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

module Cubical.Structures.Rng where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.HLevels
open import Cubical.Data.Sigma

open import Cubical.Foundations.SIP renaming (SNS-PathP to SNS)

open import Cubical.Structures.NAryOp
open import Cubical.Structures.Semigroup hiding (⟨_⟩)
open import Cubical.Structures.AbGroup

private
  variable
    ℓ ℓ' : Level

raw-rng-structure : Type ℓ → Type ℓ
raw-rng-structure X = (X → X → X) × (X → X → X)

raw-rng-is-SNS : SNS {ℓ} raw-rng-structure _
raw-rng-is-SNS = join-SNS (nAryFunIso 2) (nAryFunSNS 2) (nAryFunIso 2) (nAryFunSNS 2)

rng-axioms : (X : Type ℓ) (s : raw-rng-structure X) → Type ℓ
rng-axioms X (_·_ , _+_) = abelian-group-axioms X _·_ ×
                           semigroup-axioms X _+_ ×
                           ((x y z : X) → x · (y + z) ≡ (x · y) + (x · z)) ×
                           ((x y z : X) → (x + y) · z ≡ (x · z) + (y · z))

rng-structure : Type ℓ → Type ℓ
rng-structure = add-to-structure raw-rng-structure rng-axioms

Rngs : Type (ℓ-suc ℓ)
Rngs {ℓ} = TypeWithStr ℓ rng-structure

rng-iso : StrIso rng-structure ℓ
rng-iso = add-to-iso (join-iso (nAryFunIso 2) (nAryFunIso 2)) rng-axioms

rng-axioms-isProp : (X : Type ℓ) (s : raw-rng-structure X) → isProp (rng-axioms X s)
rng-axioms-isProp X (_·_ , _+_) = isPropΣ (abelian-group-axioms-isProp X _·_)
                                  λ _ → isPropΣ (semigroup-axiom-isProp X _+_)
                                  λ { (isSetX , _) → isPropΣ (isPropΠ3 (λ _ _ _ → isSetX _ _))
                                  λ _ → isPropΠ3 (λ _ _ _ → isSetX _ _)}

rng-is-SNS : SNS {ℓ} rng-structure rng-iso
rng-is-SNS = add-axioms-SNS _ rng-axioms-isProp raw-rng-is-SNS

RngPath : (M N : Rngs {ℓ}) → (M ≃[ rng-iso ] N) ≃ (M ≡ N)
RngPath = SIP rng-is-SNS


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

module Cubical.Induction.WellFounded where

open import Cubical.Foundations.Everything

Rel : ∀{ℓ} → Type ℓ → ∀ ℓ' → Type _
Rel A ℓ = A → A → Type ℓ

module _ {ℓ ℓ'} {A : Type ℓ} (_<_ : A → A → Type ℓ') where
  WFRec : ∀{ℓ''} → (A → Type ℓ'') → A → Type _
  WFRec P x = ∀ y → y < x → P y

  data Acc (x : A) : Type (ℓ-max ℓ ℓ') where
    acc : WFRec Acc x → Acc x

  WellFounded : Type _
  WellFounded = ∀ x → Acc x

module _ {ℓ ℓ'} {A : Type ℓ} {_<_ : A → A → Type ℓ'} where
  isPropAcc : ∀ x → isProp (Acc _<_ x)
  isPropAcc x (acc p) (acc q)
    = λ i → acc (λ y y<x → isPropAcc y (p y y<x) (q y y<x) i)

  access : ∀{x} → Acc _<_ x → WFRec _<_ (Acc _<_) x
  access (acc r) = r

  private
    wfi : ∀{ℓ''} {P : A → Type ℓ''}
        → ∀ x → (wf : Acc _<_ x)
        → (∀ x → (∀ y → y < x → P y) → P x)
        → P x
    wfi x (acc p) e = e x λ y y<x → wfi y (p y y<x) e

  module WFI (wf : WellFounded _<_) where
    module _ {ℓ''} {P : A → Type ℓ''} (e : ∀ x → (∀ y → y < x → P y) → P x) where
      private
        wfi-compute : ∀ x ax → wfi x ax e ≡ e x (λ y _ → wfi y (wf y) e)
        wfi-compute x (acc p)
          = λ i → e x (λ y y<x → wfi y (isPropAcc y (p y y<x) (wf y) i) e)

      induction :  ∀ x → P x
      induction x = wfi x (wf x) e

      induction-compute : ∀ x → induction x ≡ (e x λ y _ → induction y)
      induction-compute x = wfi-compute x (wf x)


-- {-# OPTIONS --verbose tc.records.ifs:15 #-}
-- {-# OPTIONS --verbose tc.rec.proj:15 #-}
-- {-# OPTIONS --verbose tc.rec:15 #-}
-- {-# OPTIONS --verbose tc.constr.findInScope:15 #-}
-- {-# OPTIONS --verbose tc.term.args.ifs:15 #-}
-- {-# OPTIONS --verbose tc.section.apply:15 #-}
-- {-# OPTIONS --verbose tc.mod.apply:15 #-}

module 02-classes-indep where

data T : Set where
  tt : T

data Bool : Set where
  true : Bool
  false : Bool

module testMod (a : Bool) where
  testModEntry : Bool
  testModEntry = a

record Monoid (t : Set) : Set where
  field
    zeroT : t
    plusT : t → t → t
  test : Bool
  test = false

or : Bool → Bool → Bool
or true _ = true
or _ true = true
or false false = false

aT : Monoid T
aT = record { zeroT = tt; plusT = λ _ _ → tt }

testMonoid : {t : Set} → {{tM : Monoid t}} → t → t
testMonoid {{tM}} t = let open Monoid tM in plusT t zeroT

aBool : Monoid Bool
aBool = record { zeroT = false; plusT = or }

test : Bool
test = testMonoid false

open Monoid {{...}}

test2 : {t : Set} → {{tM : Monoid t}} → t
test2 = zeroT

test3 : T
test3 = zeroT

test4 : Bool
test4 = zeroT

⋯ : {A : Set} → {{v : A}} → A
⋯ {{v}} = v

test5 : Bool
test5 = Monoid.zeroT ⋯


module int-tests where

open import int
open import eq
open import product

three : ℤ
three = , next next unit{pos}

-two : ℤ
-two = , next unit{neg}

one = -two +ℤ three

one-lem : one ≡ ,_ { a = nonzero pos } unit
one-lem = refl

six = three +ℤ three

-four = -two +ℤ -two


module NotStrictlyPositive where

data False : Set where

data Not (A : Set) : Set where
  not : (A -> False) -> Not A

data Neg (A : Set) : Set where
  neg : Not A -> Neg A

data Bad : Set where
  bad : Neg Bad -> Bad