------------------------------------------------------------------------
-- The Agda standard library
--
-- Containers, based on the work of Abbott and others
------------------------------------------------------------------------

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

module Data.Container where

open import Level using (_⊔_)
open import Codata.M hiding (map)
open import Data.W
open import Size

------------------------------------------------------------------------
-- Re-exporting content to maintain backwards compatibility

open import Data.Container.Core public
open import Data.Container.Relation.Unary.Any
  using (◇) renaming (map to ◇-map) public
open import Data.Container.Relation.Unary.All
  using (□) renaming (map to □-map) public
open import Data.Container.Membership
  using (_∈_) public
open import Data.Container.Relation.Binary.Pointwise
  using () renaming (Pointwise to Eq) public
open import Data.Container.Relation.Binary.Pointwise.Properties
  using (refl; sym; trans) public
open import Data.Container.Relation.Binary.Equality.Setoid
  using (isEquivalence; setoid) public
open import Data.Container.Properties
  using () renaming (map-identity to identity; map-compose to composition) public
open import Data.Container.Related public

module Morphism where

  open import Data.Container.Morphism.Properties
    using (Natural; NT; natural; complete; id-correct; ∘-correct) public

  open import Data.Container.Morphism
    using (id; _∘_) public

-- The least and greatest fixpoints of a container.

μ : ∀ {s p} → Container s p → Set (s ⊔ p)
μ = W

ν : ∀ {s p} → Container s p → Set (s ⊔ p)
ν C = M C ∞


---------------------------------------------------------------------------
-- Normalization by Evaluation for Intuitionistic Propositional Logic
--
-- We employ a monadic interpreter for the soundness part,
-- and a special class of monads, called cover monads,
-- for the completeness part.
--
-- Normalization is completeness after soundness.
--
-- We give two instances of a cover monad: the free cover monad
-- and the continuation monad with normal forms as answer type.
---------------------------------------------------------------------------

{-# OPTIONS --postfix-projections #-}

open import Library

module NfModelMonad (Base : Set) where

import Formulas   ; open module Form = Formulas    Base
import Derivations; open module Der  = Derivations Base

---------------------------------------------------------------------------
-- Specification of a strong monad on presheaves.
-- Devoid of monad laws.

record Monad : Set₁ where

  -- Presheaf transformer.

  field
    C     : (P : Cxt → Set) (Γ : Cxt) → Set
    monC  : ∀{P} (monP : Mon P) → Mon (C P)

  -- Strong functor.

  field
    mapC' : ∀{P Q} → ⟨ P ⇒̂ Q ⊙ C P ⟩→̇ C Q

  -- Ordinary functor: an instance of mapC'.

  mapC : ∀{P Q} → (P →̇ Q) → (C P →̇ C Q)
  mapC f = mapC' λ τ → f

  -- Monad.

  field
    return : ∀{P} (monP : Mon P) → P →̇ C P
    joinC  : ∀{P} → C (C P) →̇ C P

  -- Kleisli extension.

  extC : ∀{P Q} → (P →̇ C Q) → C P →̇ C Q
  extC f = joinC ∘ mapC f

  -- Strong bind.

  bindC' : ∀{P Q} (monP : Mon P) → C P →̇ (P ⇒̂ C Q) ⇒̂ C Q
  bindC' monP c τ k = joinC (mapC' k (monC monP τ c))

---------------------------------------------------------------------------
-- A model of IPL parametrized by a strong monad.

module Soundness (monad : Monad) (open Monad monad) where

  -- The negative connectives True, ∧, and ⇒ are explained as usual by η-expansion
  -- and the meta-level connective.

  -- The positive connectives False and ∨ are inhabited by case trees.
  -- In case False, the tree has no leaves.
  -- In case A ∨ B, each leaf must be in the semantics of either A or B.

  T⟦_⟧ : (A : Form) (Γ : Cxt) → Set
  T⟦ Atom P ⟧   = C (Ne' (Atom P))
  T⟦ False  ⟧   = C λ Δ → ⊥
  T⟦ A ∨ B  ⟧   = C λ Δ → T⟦ A ⟧ Δ ⊎ T⟦ B ⟧ Δ
  T⟦ True   ⟧ Γ = ⊤
  T⟦ A ∧ B  ⟧ Γ = T⟦ A ⟧ Γ × T⟦ B ⟧ Γ
  T⟦ A ⇒ B  ⟧ Γ = ∀{Δ} (τ : Δ ≤ Γ) → T⟦ A ⟧ Δ → T⟦ B ⟧ Δ

  -- Monotonicity of the model is proven by induction on the proposition,
  -- using monotonicity of covers and the built-in monotonicity at implication.

  -- monT : ∀ A {Γ Δ} (τ : Δ ≤ Γ) → T⟦ A ⟧ Γ → T⟦ A ⟧ Δ

  monFalse : Mon (λ Γ → ⊥)
  monFalse τ ()

  mutual
    monT : ∀ A → Mon T⟦ A ⟧
    monT (Atom P)          = monC monNe
    monT False             = monC monFalse
    monT (A ∨ B)           = monC (monOr A B)
    monT True              = _
    monT (A ∧ B) τ (a , b) = monT A τ a , monT B τ b
    monT (A ⇒ B) τ f σ     = f (σ • τ)

    monOr : ∀ A B → Mon (λ Γ → T⟦ A ⟧ Γ ⊎ T⟦ B ⟧ Γ)
    monOr A B τ = map-⊎ (monT A τ) (monT B τ)

  returnOr : ∀ A B {Γ} → T⟦ A ⟧ Γ ⊎ T⟦ B ⟧ Γ → T⟦ A ∨ B ⟧ Γ
  returnOr A B = return (monOr A B)

  -- We can run computations of semantic values.
  -- This replaces the paste (weak sheaf condition) of Beth models.

  run : ∀ A → C T⟦ A ⟧ →̇ T⟦ A ⟧
  run (Atom P)      = joinC
  run False         = joinC
  run (A ∨ B)       = joinC
  run True          = _
  run (A ∧ B)       = < run A ∘ mapC proj₁ , run B ∘ mapC proj₂  >
  run (A ⇒ B) c τ a = run B $ mapC' (λ δ f → f id≤ (monT A δ a)) $ monC (monT (A ⇒ B)) τ c

  -- Remark: A variant of run in the style of bind.

  run' : ∀ {P} (monP : Mon P) A → C P →̇ (P ⇒̂ T⟦ A ⟧) ⇒̂ T⟦ A ⟧
  run' monP (Atom p)          = bindC' monP
  run' monP False             = bindC' monP
  run' monP (A ∨ B)           = bindC' monP
  run' monP True              = _
  run' monP (A ∧ B) c τ k     = run' monP A c τ (λ σ → proj₁ ∘ k σ) , run' monP B c τ (λ σ → proj₂ ∘ k σ)
  run' monP (A ⇒ B) c τ k σ a = run' monP B c (σ • τ) λ σ' p → k (σ' • σ) p id≤ (monT A σ' a)

  -- Fundamental theorem (interpretation / soundness).

  -- Pointwise extension of T⟦_⟧ to contexts.

  G⟦_⟧ : ∀ (Γ Δ : Cxt) → Set
  G⟦ ε     ⟧ Δ = ⊤
  G⟦ Γ ∙ A ⟧ Δ = G⟦ Γ ⟧ Δ × T⟦ A ⟧ Δ

  -- monG : ∀{Γ Δ Φ} (τ : Φ ≤ Δ) → G⟦ Γ ⟧ Δ → G⟦ Γ ⟧ Φ

  monG : ∀{Γ} → Mon G⟦ Γ ⟧
  monG {ε}     τ _       = _
  monG {Γ ∙ A} τ (γ , a) = monG τ γ , monT A τ a

  -- Variable case.

  lookup : ∀{Γ A} (x : Hyp A Γ) → G⟦ Γ ⟧ →̇ T⟦ A ⟧
  lookup top     = proj₂
  lookup (pop x) = lookup x ∘ proj₁

  -- A lemma for the orE case.

  orElim : ∀ A B C → ⟨ T⟦ A ∨ B ⟧ ⊙ T⟦ A ⇒ C ⟧ ⊙ T⟦ B ⇒ C ⟧ ⟩→̇ T⟦ C ⟧
  orElim A B C c g h = run C (mapC' (λ τ → [ g τ , h τ ]) c)

  orElim' : ∀ A B C → ⟨ T⟦ A ∨ B ⟧ ⊙ T⟦ A ⇒ C ⟧ ⊙ T⟦ B ⇒ C ⟧ ⟩→̇ T⟦ C ⟧
  orElim' A B C c g h = run' (monOr A B) C c id≤ λ τ → [ g τ , h τ ]

  -- A lemma for the falseE case.
  -- Casts an empty cover into any semantic value (by contradiction).

  falseElim : ∀ C → T⟦ False ⟧ →̇ T⟦ C ⟧
  falseElim C = run C ∘ mapC ⊥-elim

  -- The fundamental theorem

  eval :  ∀{A Γ} (t : Γ ⊢ A) → G⟦ Γ ⟧ →̇ T⟦ A ⟧
  eval (hyp x)           = lookup x
  eval (impI t)    γ τ a = eval t (monG τ γ , a)
  eval (impE t u)  γ     = eval t γ id≤ (eval u γ)
  eval (andI t u)  γ     = eval t γ , eval u γ
  eval (andE₁ t)         = proj₁ ∘ eval t
  eval (andE₂ t)         = proj₂ ∘ eval t
  eval (orI₁ {A} {B} t) γ = returnOr A B (inj₁ (eval t γ))
  eval (orI₂ {A} {B} t) γ = returnOr A B (inj₂ (eval t γ))
  eval (orE {A = A} {B} {C} t u v) γ = orElim A B C (eval t γ)
    (λ τ a → eval u (monG τ γ , a))
    (λ τ b → eval v (monG τ γ , b))
  eval {C} (falseE t) γ  = falseElim C (eval t γ)
  eval trueI       γ     = _

---------------------------------------------------------------------------
-- A strong monad with services to provide a cover.
-- Devoid of laws.

record CoverMonad : Set₁ where
  field
    monad : Monad
  open Monad monad public renaming (C to Cover)

  -- Services for case distinction.

  field
    falseC  : ∀{P} → Ne' False →̇ Cover P
    orC     : ∀{P Γ C D} (t : Ne Γ (C ∨ D))
                (c : Cover P (Γ ∙ C))
                (d : Cover P (Γ ∙ D)) → Cover P Γ

  -- Service for reification of into case trees.

  field
    runNf : ∀{A} → Cover (Nf' A) →̇ Nf' A

  -- A continuation version of runNf.

  runNf' : ∀ {A P} (monP : Mon P) → Cover P →̇ (P ⇒̂ Nf' A) ⇒̂ Nf' A
  runNf' monP c τ k = runNf (mapC' k (monC monP τ c))

---------------------------------------------------------------------------
-- Completeness of IPL via a cover monad.

module Completeness (covM : CoverMonad) (open CoverMonad covM) where
  open Soundness monad

  -- Reflection / reification, proven simultaneously by induction on the proposition.

  -- Reflection is η-expansion (and recursively reflection);
  -- at positive connections we build a case tree with a single scrutinee: the neutral
  -- we are reflecting.

  -- At implication, we need reification, which produces introductions
  -- and reifies the stored case trees.

  mutual

    fresh : ∀ {Γ} A → T⟦ A ⟧ (Γ ∙ A)
    fresh A = reflect A (hyp top)

    reflect : ∀ A → Ne' A →̇ T⟦ A ⟧
    reflect (Atom P) t     = return monNe t
    reflect False    t     = falseC t
    reflect (A ∨ B)  t     = orC t (returnOr A B (inj₁ (fresh A)))
                                   (returnOr A B (inj₂ (fresh B)))
    reflect True     t     = _
    reflect (A ∧ B)  t     = reflect A (andE₁ t) , reflect B (andE₂ t)
    reflect (A ⇒ B)  t τ a = reflect B (impE (monNe τ t) (reify A a))

    reify : ∀ A → T⟦ A ⟧ →̇ Nf' A
    reify (Atom P)        = runNf ∘ mapC ne
    reify False           = runNf ∘ mapC ⊥-elim
    reify (A ∨ B)         = runNf ∘ mapC [ orI₁ ∘ reify A , orI₂ ∘ reify B ]
    reify True _          = trueI
    reify (A ∧ B) (a , b) = andI (reify A a) (reify B b)
    reify (A ⇒ B) ⟦f⟧     = impI (reify B (⟦f⟧ (weak id≤) (fresh A)))

---------------------------------------------------------------------------
-- Normalization is completeness (reification) after soundness (evaluation)

module Normalization (covM : CoverMonad) (open CoverMonad covM) where
  open Soundness monad
  open Completeness covM

  -- Identity environment, constructed by reflection.

  freshG : ∀ Γ → G⟦ Γ ⟧ Γ
  freshG ε       = _
  freshG (Γ ∙ A) = monG (weak id≤) (freshG Γ) , fresh A

  -- A variant (no improvement).

  freshG' : ∀ Γ {Δ} (τ : Δ ≤ Γ) → G⟦ Γ ⟧ Δ
  freshG' ε       τ = _
  freshG' (Γ ∙ A) τ = freshG' Γ (τ • weak id≤) , monT A τ (fresh A)

  -- Normalization

  norm : ∀{A} → Tm A →̇ Nf' A
  norm t = reify _ (eval t (freshG _))

---------------------------------------------------------------------------
-- Case tree instance of cover monad

module CaseTree where

  data Cover (P : Cxt → Set) (Γ : Cxt) : Set where
    returnC : (p : P Γ) → Cover P Γ
    falseC  : (t : Ne Γ False) → Cover P Γ
    orC     : ∀{C D} (t : Ne Γ (C ∨ D))
                (c : Cover P (Γ ∙ C))
                (d : Cover P (Γ ∙ D)) → Cover P Γ

  return : ∀{P} (monP : Mon P) → P →̇ Cover P
  return monP p = returnC p

  -- Syntactic paste.

  runNf : ∀{A} → Cover (Nf' A) →̇ Nf' A
  runNf (returnC p) = p
  runNf (falseC t)  = falseE t
  runNf (orC t c d) = orE t (runNf c) (runNf d)

  -- Weakening covers:  A case tree in Γ can be transported to a thinning Δ
  -- by weakening all the scrutinees.

  monC : ∀{P} → (monP : Mon P) → Mon (Cover P)
  monC monP τ (returnC p) = returnC (monP τ p)
  monC monP τ (falseC t)  = falseC (monNe τ t)
  monC monP τ (orC t c d) = orC (monNe τ t) (monC monP (lift τ) c)
                                            (monC monP (lift τ) d)
  -- Monad.

  mapC : ∀{P Q} → (P →̇ Q) → (Cover P →̇ Cover Q)
  mapC f (returnC p) = returnC (f p)
  mapC f (falseC t)  = falseC t
  mapC f (orC t c d) = orC t (mapC f c) (mapC f d)

  joinC : ∀{P} → Cover (Cover P) →̇ Cover P
  joinC (returnC p) = p
  joinC (falseC t)  = falseC t
  joinC (orC t c d) = orC t (joinC c) (joinC d)

  -- Strong functoriality.

  mapC' : ∀{P Q Γ} → KFun P Q Γ → Cover P Γ → Cover Q Γ
  mapC' f (returnC p) = returnC (f id≤ p)
  mapC' f (falseC t)  = falseC t
  mapC' f (orC t c d) = orC t (mapC' (λ τ → f (τ • weak id≤)) c)
                              (mapC' (λ τ → f (τ • weak id≤)) d)

caseTreeMonad : CoverMonad
caseTreeMonad = record {monad = record{CaseTree}; CaseTree}

-- A normalization function using case trees.

open Normalization caseTreeMonad using () renaming (norm to normCaseTree)

---------------------------------------------------------------------------
-- Continuation instance of cover monad

module Continuation where

  record Cover (P : Cxt → Set) (Γ : Cxt) : Set where
    field runNf' : ∀ {A} → ((P ⇒̂ Nf' A) ⇒̂ Nf' A) Γ
  open Cover

  -- Services.

  runNf : ∀{A} → Cover (Nf' A) →̇ Nf' A
  runNf {A} {Γ} c = c .runNf' id≤ (λ τ → id)

  falseC : ∀{P} → Ne' False →̇ Cover P
  falseC t .runNf' τ k = falseE (monNe τ t)

  orC : ∀{P Γ C D} (t : Ne Γ (C ∨ D))
          (c : Cover P (Γ ∙ C))
          (d : Cover P (Γ ∙ D)) → Cover P Γ

  orC t c d .runNf' τ k = orE (monNe τ t)
    (c .runNf' (lift τ) (λ δ → k (δ • weak id≤)))
    (d .runNf' (lift τ) (λ δ → k (δ • weak id≤)))

  -- Monad infrastructure.

  monC : ∀{P} → (monP : Mon P) → Mon (Cover P)
  monC monP τ c .runNf' τ₁ k = c .runNf' (τ₁ • τ) k

  mapC' : ∀{P Q} → ⟨ P ⇒̂ Q ⊙ Cover P ⟩→̇ Cover Q
  mapC' k c .runNf' τ l = c .runNf' τ λ δ p → l δ (k (δ • τ) p)

  mapC : ∀{P Q} → (P →̇ Q) → (Cover P →̇ Cover Q)
  mapC f = mapC' λ τ → f

  return : ∀{P} (monP : Mon P) → P →̇ Cover P
  return monP p .runNf' τ k = k id≤ (monP τ p)

  joinC : ∀{P} → Cover (Cover P) →̇ Cover P
  joinC c .runNf' τ k = c .runNf' τ λ δ c' → c' .runNf' id≤ λ δ' → k (δ' • δ)

  extC : ∀{P Q} → (P →̇ Cover Q) → Cover P →̇ Cover Q
  extC f = joinC ∘ mapC f

  bindC' : ∀{P Q} → Cover P →̇ (P ⇒̂ Cover Q) ⇒̂ Cover Q
  bindC' c τ k .runNf' τ' k' = c .runNf' (τ' • τ) λ σ p → k (σ • τ') p .runNf' id≤ λ σ' → k' (σ' • σ)

continuationMonad : CoverMonad
continuationMonad = record{monad = record{Continuation}; Continuation}

-- A normalization function using continuations.

open Normalization continuationMonad using () renaming (norm to normContinuation)

-- Q.E.D.

-- -}
-- -}
-- -}


module InterpretationEqualityExceptAtVariableName where

open import OscarPrelude
open import VariableName
open import FunctionName
open import PredicateName
open import Elements
open import Interpretation

record _≞_/_ (𝓘 : Interpretation) (I : Interpretation) (𝑥 : VariableName) : Set
 where
  field
    μEquality : {𝑥′ : VariableName} → 𝑥′ ≢ 𝑥 → μ⟦ 𝓘 ⟧ 𝑥 ≡ μ⟦ I ⟧ 𝑥′
    𝑓Equality : (𝑓 : FunctionName) (μs : Elements) → 𝑓⟦ 𝓘 ⟧ 𝑓 μs ≡ 𝑓⟦ I ⟧ 𝑓 μs
    𝑃Equality : (𝑃 : PredicateName) → (μs : Elements) → 𝑃⟦ 𝓘 ⟧ 𝑃 μs ≡ 𝑃⟦ I ⟧ 𝑃 μs


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

module Definition.Typed.Consequences.SucCong where

open import Definition.Untyped
open import Definition.Typed
open import Definition.Typed.Weakening
open import Definition.Typed.Properties
open import Definition.Typed.Consequences.Syntactic
open import Definition.Typed.Consequences.Substitution

open import Tools.Product

-- Congurence of the type of the successor case in natrec.
sucCong : ∀ {F G Γ} → Γ ∙ ℕ ⊢ F ≡ G
        → Γ ⊢ Π ℕ ▹ (F ▹▹ F [ suc (var 0) ]↑)
            ≡ Π ℕ ▹ (G ▹▹ G [ suc (var 0) ]↑)
sucCong F≡G with wfEq F≡G
sucCong F≡G | ⊢Γ ∙ ⊢ℕ =
  let ⊢F , _ = syntacticEq F≡G
  in  Π-cong ⊢ℕ (refl ⊢ℕ)
             (Π-cong ⊢F F≡G
                     (wkEq (step id) (⊢Γ ∙ ⊢ℕ ∙ ⊢F)
                           (subst↑TypeEq F≡G
                                         (refl (sucⱼ (var (⊢Γ ∙ ⊢ℕ) here))))))


module Numeral.Ordinal where

import      Lvl
open import Numeral.Natural
open import Syntax.Function
open import Type

private variable ℓ : Lvl.Level
private variable T : Type{ℓ}

-- Ordinal numbers up to a certain level as a type.
-- 𝟎 is the zero ordinal, the smallest ordinal number.
-- 𝐒 is the successor ordinal function, the smallest ordinal greater than the given argument.
-- lim is the limit ordinal function, the "limit" of the given sequence. The smallest ordinal greater than all values of the given sequence. Note that this is not the usual definition of a "limit ordinal" because `lim` allows arbitrary sequences, specifically those with a maximum, resulting in an equivalent to the successor ordinal `𝐒` (TODO: If this is the case, then 𝐒 is actually unnecessary? Just define it by (lim ∘ const)?
-- Note: Usually the indexing in a limit ordinal should be the class of ordinals before it, so this definition is probably unable to express all ordinals.
data Ordinal(T : Type{ℓ}) : Type{ℓ} where
  𝟎   : Ordinal(T)
  𝐒   : Ordinal(T) → Ordinal(T)
  lim : (T → Ordinal(T)) → Ordinal(T)

{-
data Ordinal' : (ℓ : Lvl.Level) → Typeω where
  𝟎   : Ordinal'(ℓ)
  𝐒   : Ordinal'(ℓ) → Ordinal'(ℓ)
  lim : (Ordinal'(ℓ) → Ordinal'(Lvl.𝐒(ℓ))) → Ordinal'(Lvl.𝐒(ℓ)) -- When the next ordinal have the current ordinal as its index

data Ordinal(T : Type{ℓ}) : Type{ℓ} where
  𝟎   : Ordinal(T)
  lim : (T → Ordinal(T)) → Ordinal(T) -- 𝐒 is expressable through lim

data Ordinal(T : Type{ℓ}) : Type{ℓ} where
  𝟎   : Ordinal(T)
  𝐒   : Ordinal(T) → Ordinal(T)
  lim : (f : T → Ordinal(T)) → ∃(IncreasingSubsequence(_<_)(f)) → Ordinal(T) -- This excludes the cases where f have a maximum, resulting in no intersection between 𝐒 and lim
-}

_+_ : Ordinal(T) → Ordinal(T) → Ordinal(T)
x + 𝟎     = x
x + 𝐒(y)  = 𝐒(x + y)
x + lim f = lim(y ↦ (x + f(y)))

_⋅_ : Ordinal(T) → Ordinal(T) → Ordinal(T)
x ⋅ 𝟎      = 𝟎
x ⋅ 𝐒(y)   = (x ⋅ y) + x
x ⋅ lim f  = lim(y ↦ (x ⋅ f(y)))

_^_ : Ordinal(T) → Ordinal(T) → Ordinal(T)
x ^ 𝟎     = x
x ^ 𝐒(y)  = (x ^ y) ⋅ x
x ^ lim f = lim(y ↦ (x ^ f(y)))

open import Logic.Propositional

private variable x y z x₁ x₂ y₁ y₂ z₁ z₂ : Ordinal(T)

module _ {T : Type{ℓ}} where
  open import Functional
  open import Logic.Propositional

  -- TODO: Not sure if this is correct
  data _<_ : Ordinal(T) → Ordinal(T) → Type{ℓ} where
    minimal : ∀{x} → (𝟎 < 𝐒(x))
    step    : ∀{x y} → (x < y) → (𝐒(x) < 𝐒(y))
    limitₗ  : ∀{f}{x} → (∀{i} → (f(i) < x)) → (lim f < 𝐒(x))
    limitᵣ  : ∀{i}{f}{x} → (x < 𝐒(f(i))) → (x < lim f)

  _>_ = swap(_<_)

  _≤_ : Ordinal(T) → Ordinal(T) → Type
  x ≤ y = x < 𝐒(y)

  _≥_ = swap(_≤_)

  _≡_ : Ordinal(T) → Ordinal(T) → Type
  x ≡ y = (y ≤ x) ∧ (x ≤ y)

  open import Numeral.Natural.Induction
  from-ℕ : ℕ → Ordinal(T)
  from-ℕ = ℕ-elim 𝟎 (const 𝐒)

  _+ₙ_ : Ordinal(T) → ℕ → Ordinal(T)
  _+ₙ_ x = ℕ-elim x (const 𝐒)

  private variable f g : T → Ordinal(T)
  private variable n : T

  [<]-limitₗ-inv : (lim f < 𝐒(x)) → (∀{n} → (f(n) < x))
  [<]-limitₗ-inv (limitₗ p) = p

  open import Logic.Predicate
  open import Type.Properties.Inhabited
  [<]-limitᵣ-inv : (x < lim f) → ∃(n ↦ (x < 𝐒(f(n))))
  [<]-limitᵣ-inv (limitᵣ{n} p) = [∃]-intro n ⦃ p ⦄

  [<]-lim-minimal : ⦃ ◊ T ⦄ → (𝟎 < lim f)
  [<]-lim-minimal = limitᵣ{i = [◊]-existence} minimal

  [<]-lim-maximum : (f(n) < lim f)
  [<]-of-step : (x < 𝐒(x))

  [<]-lim-maximum{f}{n} = limitᵣ{n} [<]-of-step

  [<]-without-step : (𝐒(x) < 𝐒(y)) → (x < y)
  [<]-without-step (step p) = p

  [<]-of-step {𝟎}     = minimal
  [<]-of-step {𝐒(x)}  = step [<]-of-step
  [<]-of-step {lim f} = limitₗ [<]-lim-maximum

  [<]-transitivity : (x < y) → (y < z) → (x < z)
  [<]-transitivity minimal     (step yz)          = minimal
  [<]-transitivity minimal     (limitᵣ {n}{f} yz) = limitᵣ {n}{f} minimal
  [<]-transitivity (step xy)   (step yz)          = step ([<]-transitivity xy yz)
  [<]-transitivity (step xy)   (limitᵣ yz)        = limitᵣ ([<]-transitivity (step xy) yz)
  [<]-transitivity (limitₗ xy) (step yz)          = limitₗ ([<]-transitivity xy yz)
  [<]-transitivity (limitₗ xy) (limitᵣ yz)        = limitᵣ (limitₗ ([<]-without-step([<]-transitivity (step xy) yz)))
  [<]-transitivity (limitᵣ xy) (limitₗ yz)        = [<]-transitivity xy (step yz)
  [<]-transitivity (limitᵣ xy) (limitᵣ yz)        = limitᵣ ([<]-transitivity (limitᵣ xy) yz)

  [<]-stepᵣ : (x < y) → (x < 𝐒(y))
  [<]-stepᵣ minimal    = minimal
  [<]-stepᵣ (step p)   = step ([<]-stepᵣ p)
  [<]-stepᵣ (limitₗ p) = limitₗ ([<]-stepᵣ p)
  [<]-stepᵣ (limitᵣ p) = [<]-transitivity p (step [<]-lim-maximum)

  [<]-to-[≤] : (x < y) → (x ≤ y)
  [<]-to-[≤] = [<]-stepᵣ

  [<]-irreflexivity : ¬(x < x)
  [<]-irreflexivity (step p)            = [<]-irreflexivity p
  [<]-irreflexivity (limitᵣ (limitₗ x)) = [<]-irreflexivity x

  [<]-asymmetry : (x < y) → (y < x) → ⊥
  [<]-asymmetry xy yx = [<]-irreflexivity([<]-transitivity xy yx)

  [<]-without-stepₗ : (𝐒(x) < y) → (x < y)
  [<]-without-stepₗ (step p)   = [<]-stepᵣ p
  [<]-without-stepₗ (limitᵣ p) = limitᵣ ([<]-without-stepₗ p)

  [<]-no-less-than-minimum : ¬(x < 𝟎)
  [<]-no-less-than-minimum ()

  [≤]-minimal : (𝟎 ≤ x)
  [≤]-minimal = minimal

  [≤]-step : (x ≤ y) → (𝐒(x) ≤ 𝐒(y))
  [≤]-step = step

  [≤]-reflexivity : (x ≤ x)
  [≤]-reflexivity = [<]-of-step

  [≡]-reflexivity : (x ≡ x)
  [≡]-reflexivity = [∧]-intro [≤]-reflexivity [≤]-reflexivity

  open import Logic.Propositional.Theorems
  [≡]-symmetry : (x ≡ y) → (y ≡ x)
  [≡]-symmetry = [∧]-symmetry

  [≡]-step : (x ≡ y) → (𝐒(x) ≡ 𝐒(y))
  [≡]-step = [∧]-map step step

  open import Relator.Equals renaming (_≡_ to _≡ₑ_)
  [<]-less-than-one : ⦃ ◊ T ⦄ → (x < 𝐒(𝟎)) → (x ≡ₑ 𝟎)
  [<]-less-than-one minimal = [≡]-intro
  [<]-less-than-one (limitₗ p) with () ← p{[◊]-existence}

  [<][≤]-semitransitivityₗ : ⦃ ◊ T ⦄ → (x ≤ y) → (y < z) → (x < z)
  [<][≤]-semitransitivityₗ {_}        {y}        {.(lim _)} xy                 (limitᵣ yz)  = limitᵣ ([<][≤]-semitransitivityₗ xy yz)
  [<][≤]-semitransitivityₗ {.𝟎}       {.𝟎}       {.(𝐒 _)}    minimal            minimal     = minimal
  [<][≤]-semitransitivityₗ {.𝟎}       {.(𝐒 _)}   {.(𝐒 _)}    minimal            (step yz)   = minimal
  [<][≤]-semitransitivityₗ {.𝟎}       {.(lim _)} {.(𝐒 _)}    minimal            (limitₗ x)  = minimal
  [<][≤]-semitransitivityₗ {.(𝐒 _)}   {.(𝐒 _)}   {.(𝐒 _)}    (step xy)          (step yz)   = step ([<][≤]-semitransitivityₗ xy yz)
  [<][≤]-semitransitivityₗ {.(𝐒 _)}   {.(lim _)} {.(𝐒 _)}    (step (limitᵣ xy)) (limitₗ yz) = step ([<][≤]-semitransitivityₗ xy yz)
  [<][≤]-semitransitivityₗ {.(lim _)} {.𝟎}       {.(𝐒 _)}    (limitₗ xy)        minimal     with () ← xy{[◊]-existence}
  [<][≤]-semitransitivityₗ {.(lim _)} {.(𝐒 _)}   {.(𝐒 _)}    (limitₗ xy)        (step yz)   = limitₗ ([<][≤]-semitransitivityₗ xy yz)
  [<][≤]-semitransitivityₗ {.(lim _)} {.(lim _)} {.(𝐒 _)}    (limitₗ xy)        (limitₗ yz) = limitₗ ([<][≤]-semitransitivityₗ ([∃]-proof([<]-limitᵣ-inv xy)) yz)

  [≤]-transitivity : ⦃ ◊ T ⦄ → (x ≤ y) → (y ≤ z) → (x ≤ z)
  [≤]-transitivity = [<][≤]-semitransitivityₗ

  [≡]-transitivity : ⦃ ◊ T ⦄ → (x ≡ y) → (y ≡ z) → (x ≡ z)
  [≡]-transitivity ([∧]-intro yx xy) ([∧]-intro zy yz) = [∧]-intro ([≤]-transitivity zy yx) ([≤]-transitivity xy yz)

  [<][≤]-semitransitivityᵣ : ⦃ ◊ T ⦄ → (x < y) → (y ≤ z) → (x < z)
  [<][≤]-semitransitivityᵣ {.𝟎}       {.(𝐒 𝟎)}       {.(𝐒 _)}   minimal (step minimal)         = minimal
  [<][≤]-semitransitivityᵣ {.𝟎}       {.(𝐒 (𝐒 _))}   {.(𝐒 _)}   minimal (step (step yz))       = minimal
  [<][≤]-semitransitivityᵣ {.𝟎}       {.(𝐒 (lim _))} {.(𝐒 _)}   minimal (step (limitₗ yz))     = minimal
  [<][≤]-semitransitivityᵣ {.𝟎}       {.(𝐒 _)}       {.(lim _)} minimal (step (limitᵣ yz))     = limitᵣ{[◊]-existence} minimal
  [<][≤]-semitransitivityᵣ {.(𝐒 _)}   {.(𝐒 _)}       {𝐒 z}      (step xy) (step yz)            = step ([<][≤]-semitransitivityᵣ xy yz)
  [<][≤]-semitransitivityᵣ {.(𝐒 _)}   {.(𝐒 _)}       {lim z}    (step xy) (step (limitᵣ yz))   = limitᵣ (step ([<][≤]-semitransitivityᵣ xy yz))
  [<][≤]-semitransitivityᵣ {.(lim _)} {.(𝐒 _)}       {𝐒 z}      (limitₗ xy) (step yz)          = limitₗ ([<][≤]-semitransitivityᵣ xy yz)
  [<][≤]-semitransitivityᵣ {.(lim _)} {.(𝐒 _)}       {lim z}    (limitₗ xy) (step (limitᵣ yz)) = limitᵣ ([<][≤]-semitransitivityᵣ (limitₗ xy) (step yz))
  [<][≤]-semitransitivityᵣ {x}        {.(lim _)}     {z}        (limitᵣ xy) (limitₗ yz)        = [<][≤]-semitransitivityᵣ xy (step yz)

  {-
  StrictlyIncreasingSubsequenceExistence : ∀{ℓ}{I : Type{ℓ}} → (I → Ordinal(T)) → Type
  StrictlyIncreasingSubsequenceExistence{I = I} (f) = ∃{Obj = ℕ → I}(g ↦ ∀{n} → (f(g(n)) < f(g(𝐒(n)))))

  -- TODO: Requires some kind of search for T
  StrictlyIncreasingSubsequenceExistence-to-no-maximum : StrictlyIncreasingSubsequenceExistence(f) → ¬ ∃(max ↦ ∀{x} → (f(x) < f(max)))
  StrictlyIncreasingSubsequenceExistence-to-no-maximum ([∃]-intro witness) ([∃]-intro witness₁) = [<]-asymmetry {!!} {!!}
  -}

  {- TODO: Maybe unprovable constructively
  [<]-classical : (x < y) ∨ ¬(x < y)
  [<]-classical {𝟎}     {𝟎}     = [∨]-introᵣ \()
  [<]-classical {𝟎}     {𝐒 y}   = [∨]-introₗ minimal
  [<]-classical {𝟎}     {lim y} = [∨]-introₗ (limitᵣ{𝟎} minimal)
  [<]-classical {𝐒 x}   {𝟎}     = [∨]-introᵣ \()
  [<]-classical {𝐒 x}   {𝐒 y}   = [∨]-elim2 step (_∘ [<]-without-step) ([<]-classical {x} {y})
  [<]-classical {𝐒 x}   {lim y} with [<]-classical {x} {lim y}
  ... | [∨]-introₗ p = {!!}
  ... | [∨]-introᵣ p = {!!}
  [<]-classical {lim x} {𝟎}     = [∨]-introᵣ \()
  [<]-classical {lim x} {𝐒 y}   with [<]-classical {lim x} {y}
  ... | [∨]-introₗ (limitₗ p)          = [∨]-introₗ (limitₗ ([<]-stepᵣ p))
  ... | [∨]-introₗ (limitᵣ (limitₗ p)) = [∨]-introₗ (limitₗ (limitᵣ ([<]-stepᵣ p)))
  ... | [∨]-introᵣ p = [∨]-introᵣ (p ∘ (q ↦ {![<]-limitₗ-inv q!}))
  [<]-classical {lim x} {lim y} = {!!}
  -}

  {- TODO: This is true when there is no maximum for f
  ∀{x} → ∃(y ↦ (x < y) ∧ (f(x) < f(y)))
  test : (x < lim f) → (𝐒(x) < lim f)
  test {𝟎} (limitᵣ minimal) = limitᵣ (step {!!})
  test {𝐒 x} (limitᵣ (step p)) = limitᵣ (step {!!})
  test {lim x} (limitᵣ (limitₗ x₁)) = {!!}
  -}

  {- TODO: Also unprovable constructively?
  f either have a maximum value or is unbounded (so, this would require some kind of search?)

  [<]-sequence-limit : ∀{n} → (f(n) < lim g) → ((∀{n} → (f(n) < lim g)) ∨ (f(n) ≡ x))

  [≤]-total : ⦃ ◊ T ⦄ → ((x ≤ y) ∨ (y ≤ x))
  [≤]-total {𝟎} {_} = [∨]-introₗ minimal
  [≤]-total {_} {𝟎} = [∨]-introᵣ minimal
  [≤]-total {𝐒 x} {𝐒 y} = [∨]-elim2 step step ([≤]-total {x} {y})
  [≤]-total {𝐒 x} {lim y} with [≤]-total {x} {lim y}
  ... | [∨]-introₗ minimal = [∨]-introₗ (step [<]-lim-minimal)
  ... | [∨]-introₗ (step (limitᵣ p)) = [∨]-introₗ (step (limitᵣ (step {!!})))
  ... | [∨]-introₗ (limitₗ p) = [∨]-introₗ (step (limitᵣ (limitₗ (\{n} → {![<]-limitᵣ-inv (p{n})!}))))
  ... | [∨]-introᵣ p = [∨]-introᵣ ([<]-stepᵣ p)
  [≤]-total {lim x} {𝐒 y} = {!!} -- [∨]-symmetry ([≤]-total {𝐒 y} {lim x})
  [≤]-total {lim x} {lim y} = {!!}
  -}

  {-
  [≤]-to-[<][≡] : ⦃ ◊ T ⦄ → (x ≤ y)  → ((x < y) ∨ (x ≡ y))
  [≤]-to-[<][≡] {𝟎}     {𝟎}     minimal    = [∨]-introᵣ ([∧]-intro minimal minimal)
  [≤]-to-[<][≡] {𝟎}     {𝐒 y}   minimal    = [∨]-introₗ minimal
  [≤]-to-[<][≡] {𝟎}     {lim y} minimal    = [∨]-introₗ [<]-lim-minimal
  [≤]-to-[<][≡] {𝐒 x}   {𝐒 y}   (step p)   = [∨]-elim2 step [≡]-step ([≤]-to-[<][≡] {x}{y} p)
  [≤]-to-[<][≡] {𝐒 x} {lim y} (step (limitᵣ p)) with [≤]-to-[<][≡] p
  ... | [∨]-introₗ pp = [∨]-introₗ (limitᵣ (step pp))
  ... | [∨]-introᵣ ([∧]-intro l r) = {!!}
  -- [∨]-introᵣ ([∧]-intro (limitₗ (\{n} → {!!})) (step (limitᵣ r)))
  -- [∨]-elim2 (limitᵣ ∘ step) (\([∧]-intro l r) → [∧]-intro (limitₗ {![<]-stepᵣ l!}) (step (limitᵣ r))) ([≤]-to-[<][≡] p)
  [≤]-to-[<][≡] {lim x} {𝟎}     (limitₗ p) = [∨]-introᵣ ([∧]-intro minimal (limitₗ p))
  [≤]-to-[<][≡] {lim x} {𝐒 y}   (limitₗ p) = {!!}
  [≤]-to-[<][≡] {lim x} {lim y} (limitₗ p) = {!!}

  [<]-trichotomy : ⦃ ◊ T ⦄ → ((x < y) ∨ (x ≡ y) ∨ (x > y))
  [<]-trichotomy {𝟎}     {𝟎}     = [∨]-introₗ ([∨]-introᵣ [≡]-reflexivity)
  [<]-trichotomy {𝟎}     {𝐒 y}   = [∨]-introₗ ([∨]-introₗ minimal)
  [<]-trichotomy {𝟎}     {lim y} = [∨]-introₗ ([∨]-introₗ [<]-lim-minimal)
  [<]-trichotomy {𝐒 x}   {𝟎}     = [∨]-introᵣ minimal
  [<]-trichotomy {lim x} {𝟎}     = [∨]-introᵣ [<]-lim-minimal
  [<]-trichotomy {𝐒 x}   {𝐒 y}   = [∨]-elim2 ([∨]-elim2 step [≡]-step) step ([<]-trichotomy {x}{y})
  [<]-trichotomy {𝐒 x}   {lim y} with [<]-trichotomy {x} {lim y}
  ... | [∨]-introₗ ([∨]-introᵣ ([∧]-intro p _)) = [∨]-introᵣ p
  ... | [∨]-introᵣ p                            = [∨]-introᵣ ([<]-stepᵣ p)
  ... | [∨]-introₗ ([∨]-introₗ (limitᵣ{n} p))   = {!!}
  [<]-trichotomy {lim x} {𝐒 y}   = {!!}
  [<]-trichotomy {lim x} {lim y} = {!!}
  -}

  lim-of-constant : ⦃ ◊ T ⦄ → (lim(const x) ≡ 𝐒(x))
  lim-of-constant = [∧]-intro (step (limitᵣ{[◊]-existence} [<]-of-step)) (limitₗ [<]-of-step)

  lim-of-sequence-with-maximum : ∀{max} → (∀{n} → (f(n) ≤ f(max))) → (lim f ≡ 𝐒(f(max)))
  lim-of-sequence-with-maximum{max = max} p = [∧]-intro (step (limitᵣ{max} [<]-of-step)) (limitₗ p)

  -- ∃(max ↦ (∀{n} → (f(n) ≤ f(max))) ∧ (lim f ≡ 𝐒(f(max)))) ∨ (∀{x} → (𝐒(f(x)) < lim f)) -- or maybe (∀{x y} → (f(x) + f(y) < lim f))

  lim-[≤]-function : (∀{x} → (f(x) ≤ g(x))) → (lim f ≤ lim g)
  lim-[≤]-function p = limitₗ(limitᵣ p)

  lim-function : (∀{x} → (f(x) ≡ g(x))) → (lim f ≡ lim g)
  lim-function p = [∧]-intro (lim-[≤]-function([∧]-elimₗ p)) (lim-[≤]-function([∧]-elimᵣ p))

  [+]-identityᵣ : ((x + 𝟎) ≡ x)
  [+]-identityᵣ = [≡]-reflexivity

  [+]-identityₗ : ((𝟎 + x) ≡ x)
  [+]-identityₗ {𝟎}     = [+]-identityᵣ
  [+]-identityₗ {𝐒 x}   = [≡]-step ([+]-identityₗ {x})
  [+]-identityₗ {lim x} = lim-function [+]-identityₗ

  [+]-associativity : (((x + y) + z) ≡ (x + (y + z)))
  [+]-associativity {x}{y}{𝟎}     = [≡]-reflexivity
  [+]-associativity {x}{y}{𝐒 z}   = [≡]-step ([+]-associativity {x}{y}{z})
  [+]-associativity {x}{y}{lim z} = lim-function ([+]-associativity {x}{y}{_})

  [+]ₗ-[<][≤]-semifunction : (x < y) → ((x + z) ≤ (y + z))
  [+]ₗ-[<][≤]-semifunction {z = 𝟎}     p = [<]-to-[≤] p
  [+]ₗ-[<][≤]-semifunction {z = 𝐒 z}   p = [≤]-step([+]ₗ-[<][≤]-semifunction {z = z} p)
  [+]ₗ-[<][≤]-semifunction {z = lim x} p = limitₗ (limitᵣ ([+]ₗ-[<][≤]-semifunction p))

  [+]ᵣ-[<]-function : ⦃ ◊ T ⦄ → (y < z) → ((x + y) < (x + z))
  [+]ᵣ-[<]-function (minimal {𝟎})     = [<]-of-step
  [+]ᵣ-[<]-function (minimal {𝐒 x})   = [<]-stepᵣ ([+]ᵣ-[<]-function minimal)
  [+]ᵣ-[<]-function (minimal {lim x}) = [<]-stepᵣ (limitᵣ{[◊]-existence} ([+]ᵣ-[<]-function minimal))
  [+]ᵣ-[<]-function (step p)   = step ([+]ᵣ-[<]-function p)
  [+]ᵣ-[<]-function (limitₗ p) = limitₗ ([+]ᵣ-[<]-function p)
  [+]ᵣ-[<]-function (limitᵣ p) = limitᵣ ([+]ᵣ-[<]-function p)

  {-
  [+]-operator : ⦃ ◊ T ⦄ → (x₁ ≡ x₂) → (y₁ ≡ y₂) → ((x₁ + y₁) ≡ (x₂ + y₂))
  [+]-operator ([∧]-intro pxl pxr) ([∧]-intro pyl pyr) = [∧]-intro {!!} {!!} where
    l : (x₁ ≡ x₂) → ((x₁ + y) ≡ (x₂ + y))
    l {𝟎}      {x₂} ([∧]-intro pl pr) = [∧]-intro {!!} {!!}
    l {𝐒 x₁}   {x₂} ([∧]-intro pl pr) = [∧]-intro {!!} {!!}
    l {lim x₁} {x₂} ([∧]-intro pl pr) = [∧]-intro {!!} {!!}

    r : (y₁ ≡ y₂) → ((x + y₁) ≡ (x + y₂))
    r ([∧]-intro pl pr) = [∧]-intro ([+]ᵣ-[<]-function pl) ([+]ᵣ-[<]-function pr)
  -}

  open import Structure.Relator.Ordering
  open        Structure.Relator.Ordering.Strict.Properties using (intro)

  Ordinal-accessibleₗ : ⦃ ◊ T ⦄ → Strict.Properties.Accessibleₗ(_<_)(x)
  Ordinal-accessibleₗ {n} = intro ⦃ proof{n} ⦄ where
    proof : ∀{y x} → ⦃ _ : (x < y) ⦄ → Strict.Properties.Accessibleₗ(_<_)(x)
    proof {_}        {𝟎}                          = intro ⦃ \ ⦃ ⦄ ⦄
    proof {𝐒 𝟎}      {lim y} ⦃ limitₗ p ⦄         with () ← p{[◊]-existence}
    proof {𝐒 x}      {𝐒 y}   ⦃ step p ⦄           = intro ⦃ \{z} ⦃ pz ⦄ → Strict.Properties.Accessibleₗ.proof (Ordinal-accessibleₗ {x}) ⦃ [<][≤]-semitransitivityₗ pz p ⦄ ⦄
    proof {𝐒(𝐒 x)}   {lim y} ⦃ limitₗ p ⦄         = intro ⦃ \{z} ⦃ pz ⦄ → Strict.Properties.Accessibleₗ.proof (Ordinal-accessibleₗ {𝐒 x}) ⦃ [<][≤]-semitransitivityₗ ([∃]-proof([<]-limitᵣ-inv pz)) p ⦄ ⦄
    proof {𝐒(lim x)} {lim y} ⦃ limitₗ p ⦄         = intro ⦃ \{z} ⦃ pz ⦄ → Strict.Properties.Accessibleₗ.proof (Ordinal-accessibleₗ {lim x}) ⦃ [<][≤]-semitransitivityₗ ([∃]-proof([<]-limitᵣ-inv pz)) p ⦄ ⦄
    proof {lim x}    {𝐒 y}   ⦃ limitᵣ(step p) ⦄   = intro ⦃ \{z} ⦃ pz ⦄ → Strict.Properties.Accessibleₗ.proof Ordinal-accessibleₗ ⦃ [<][≤]-semitransitivityₗ pz p ⦄ ⦄
    proof {lim x}    {lim y} ⦃ limitᵣ(limitₗ p) ⦄ = intro ⦃ \{z} ⦃ pz ⦄ → Strict.Properties.Accessibleₗ.proof Ordinal-accessibleₗ ⦃ [<][≤]-semitransitivityₗ ([∃]-proof([<]-limitᵣ-inv pz)) p ⦄ ⦄

module _ where
  open import Functional
  open import Function.Iteration
  open import Numeral.Natural.Induction

  private variable n : ℕ

  _⋅ₙ_ : Ordinal(ℕ) → ℕ → Ordinal(ℕ)
  _⋅ₙ_ x = ℕ-elim 𝟎 (const(lim ∘ (_+ₙ_)))

  ω : Ordinal(ℕ)
  ω = lim from-ℕ

  ω² : Ordinal(ℕ)
  ω² = lim(ω ⋅ₙ_)

  ω-[<]-correctness : from-ℕ(n) < ω
  ω-[<]-correctness {𝟎}    = limitᵣ{i = 𝟎}    minimal
  ω-[<]-correctness {𝐒(n)} = limitᵣ{i = 𝐒(n)} (step [<]-of-step)

  open import Relator.Equals renaming (_≡_ to _≡ₑ_)
  open import Relator.Equals.Proofs
  open import Type.Properties.Inhabited
  open import Structure.Relator

  private variable f : ℕ → Ordinal(ℕ)

  instance
    ℕ-inhabited : ◊ ℕ
    ℕ-inhabited = intro ⦃ 𝟎 ⦄

  strictly-increasing-sequence-when-zero : (∀{n} → (f(n) < f(𝐒(n)))) → (f(n) ≡ₑ 𝟎) → (n ≡ₑ 𝟎)
  strictly-increasing-sequence-when-zero {f = f}{n = 𝟎} ord p = [≡]-intro
  strictly-increasing-sequence-when-zero {f = f}{n = 𝐒 n} ord p with f(n) | ord{n}
  ... | 𝟎     | ord' with () ← [<]-irreflexivity (substitute₂ᵣ(_<_) p ord')
  ... | 𝐒 fn  | ord' with () ← [<]-asymmetry minimal (substitute₂ᵣ(_<_) p ord')
  ... | lim x | ord' with () ← [<]-asymmetry [<]-lim-minimal (substitute₂ᵣ(_<_) p ord')


module Pi-2 where

open import Data.Unit
open import Data.Product
open import Relation.Binary.PropositionalEquality

open import Groupoid

infixr 10 _◎_
infixr 30 _⟷_

------------------------------------------------------------------------------
-- Level -2
-- Types are -2 groupoids or equivalently all types are contractible or
-- equivalently all types have exactly one point

-- There is no interesting path structure but we have the trivial paths
-- explicitly below.

-- Types and values are defined mutually without reference to programs Types
-- always include PATHs in addition to the usual types.

mutual

  data U : Set where
    ONE   : U
    TIMES : U → U → U
    PATH  : {t : U} → ⟦ t ⟧ → ⟦ t ⟧ → U

  ⟦_⟧ : U → Set
  ⟦ ONE ⟧         = ⊤
  ⟦ TIMES t₁ t₂ ⟧ = ⟦ t₁ ⟧ × ⟦ t₂ ⟧
  ⟦ PATH v₁ v₂ ⟧  = (v₁ ≡ v₂)

-- Proof that all types are contractible. Def. 3.11.1 in the HoTT book

isContr : (t : U) → Σ[ v ∈ ⟦ t ⟧ ] ((v' : ⟦ t ⟧) → (v ≡ v'))
isContr ONE = (tt , λ v → refl)
isContr (TIMES t₁ t₂) with isContr t₁ | isContr t₂
... | (v₁ , f₁) | (v₂ , f₂) = ((v₁ , v₂) , f)
  where f : (vv : ⟦ t₁ ⟧ × ⟦ t₂ ⟧) → (v₁ , v₂) ≡ vv
        f (v₁' , v₂') = cong₂ _,′_ (f₁ v₁') (f₂ v₂')
isContr (PATH {t} v₁ v₂) with isContr t
isContr (PATH {t} v₁ v₂) | (v' , f') = (p , λ q → proof-irrelevance p q)
  where p = trans (sym (f' v₁))  (f' v₂)

-- Proof that every type is a groupoid; this does not depend on the
-- definition of programs

isGroupoid : U → 1Groupoid
isGroupoid ONE              = discrete ⊤
isGroupoid (TIMES t₁ t₂)    = isGroupoid t₁ ×G isGroupoid t₂
isGroupoid (PATH {t} v₁ v₂) = discrete (v₁ ≡ v₂)

-- Programs
-- We use pointed types; programs map a pointed type to another
-- In other words, each program takes one particular value to another; if we
-- want to work on another value, we generally use another program

record U• : Set where
  constructor •[_,_]
  field
    ∣_∣ : U
    • : ⟦ ∣_∣ ⟧

open U•

Space : (t• : U•) → Set
Space •[ t , v ] = ⟦ t ⟧

point : (t• : U•) → Space t•
point •[ t , v ] = v

data _⟷_ : U• → U• → Set where
  id⟷    : {t : U•} → t ⟷ t
  sym⟷   : {t₁ t₂ : U•} → (t₁ ⟷ t₂) → (t₂ ⟷ t₁)
  _◎_     : {t₁ t₂ t₃ : U•} → (t₁ ⟷ t₂) → (t₂ ⟷ t₃) → (t₁ ⟷ t₃)
  unite⋆  : {t : U•} → •[ TIMES ONE ∣ t ∣ , (tt , • t) ] ⟷ t
  uniti⋆  : {t : U•} → t ⟷ •[ TIMES ONE ∣ t ∣ , (tt , • t) ]
  swap⋆   : {t₁ t₂ : U•} → •[ TIMES ∣ t₁ ∣ ∣ t₂ ∣ , (• t₁ , • t₂) ] ⟷
                           •[ TIMES ∣ t₂ ∣ ∣ t₁ ∣ , (• t₂ , • t₁) ]
  assocl⋆ : {t₁ t₂ t₃ : U•} →
           •[ TIMES ∣ t₁ ∣ (TIMES ∣ t₂ ∣ ∣ t₃ ∣) , (• t₁ , (• t₂ , • t₃)) ] ⟷
           •[ TIMES (TIMES ∣ t₁ ∣ ∣ t₂ ∣) ∣ t₃ ∣ , ((• t₁ , • t₂) , • t₃) ]
  assocr⋆ : {t₁ t₂ t₃ : U•} →
           •[ TIMES (TIMES ∣ t₁ ∣ ∣ t₂ ∣) ∣ t₃ ∣ , ((• t₁ , • t₂) , • t₃) ] ⟷
           •[ TIMES ∣ t₁ ∣ (TIMES ∣ t₂ ∣ ∣ t₃ ∣) , (• t₁ , (• t₂ , • t₃)) ]
  _⊗_     : {t₁ t₂ t₃ t₄ : U•} → (t₁ ⟷ t₃) → (t₂ ⟷ t₄) →
            (•[ TIMES ∣ t₁ ∣ ∣ t₂ ∣ , (• t₁ , • t₂ ) ] ⟷
             •[ TIMES ∣ t₃ ∣ ∣ t₄ ∣ , (• t₃ , • t₄ ) ])

-- Proof that the universe is itself a groupoid; this DOES depend on the
-- definition of programs; the morphisms between types ARE the programs; all
-- programs between the same types are forced to be the same using a
-- degenerate observational equivalence relation.

_obs≅_ : {t₁ t₂ : U•} → (c₁ c₂ : t₁ ⟷ t₂) → Set
c₁ obs≅ c₂ = ⊤

UG : 1Groupoid
UG = record
       { set = U•
       ; _↝_ = _⟷_
       ; _≈_ = _obs≅_
       ; id = id⟷
       ; _∘_ = λ y⟷z x⟷y → x⟷y ◎ y⟷z
       ; _⁻¹ = sym⟷
       ; lneutr = λ _ →  tt
       ; rneutr = λ _ → tt
       ; assoc = λ _ _ _ → tt
       ; equiv = record { refl = tt
                        ; sym = λ _ → tt
                        ; trans = λ _ _ → tt
                        }
       ; linv = λ _ → tt
       ; rinv = λ _ → tt
       ; ∘-resp-≈ = λ _ _ → tt
       }

-- Examples

ONE• ONE×ONE• : U•
ONE•     = •[ ONE , tt ]
ONE×ONE• = •[ TIMES ONE ONE , (tt , tt) ]

PathSpace : {t : U•} → ⟦ ∣ t ∣ ⟧ → U•
PathSpace v = •[ PATH v v , refl ]

c₁ c₂ c₃ : ONE• ⟷ ONE•
c₁ = id⟷
c₂ = uniti⋆ ◎ swap⋆ ◎ unite⋆
c₃ = uniti⋆ ◎ uniti⋆ ◎ assocl⋆ ◎ (swap⋆ ⊗ (sym⟷ id⟷)) ◎
     assocr⋆ ◎ unite⋆ ◎ unite⋆

-- The evaluation of a program like c₁, c₂, c₃ above is not done in order to
-- figure out the output value. Both the input and output values are encoded
-- in the type of the program; what the evaluation does is follow the path to
-- constructively reach the ouput value from the input value. Even though the
-- programs are observationally equivalent, they follow different paths. The
-- next level will allow us to reason about which of these paths are
-- connected by 2paths etc. In general, one write programs at level i, and
-- reasons about their (observational) equivalence at levels i+1 and
-- higher. It is always possible to stop this game and use extensional
-- equality at some level, i.e., to equate all programs that connect the same
-- input and output values.

c₄ : ONE×ONE• ⟷ ONE•
c₄ = unite⋆

c₅ : PathSpace tt ⟷ PathSpace tt
c₅ = id⟷

c₁≅c₂ : c₁ obs≅ c₂
c₁≅c₂ = tt

-- the following does not typecheck
-- c₁≅c₄ : c₁ obs≅ c₄
-- it is not possible to equate programs that go between different types/values.

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


module Prelude.Data.Natural where

open import Prelude.Core
open import Prelude.Classes
open import Prelude.Orders

----------------
-- Conversion --
----------------

record ToNatural {ℓ} (A : Set ℓ) : Set ℓ where
  constructor mk[ToNatural]
  field
    𝕟 : A → ℕ
open ToNatural {{…}}

-----------
-- Order --
-----------

data _≤ⁿ_ : ℕ → ℕ → Set where
  Zero : ∀ {n} → Zero ≤ⁿ n
  Succ : ∀ {n₁ n₂} → n₁ ≤ⁿ n₂ → Succ n₁ ≤ⁿ Succ n₂

xRx⸢≤ⁿ⸣ : reflexive _≤ⁿ_
xRx⸢≤ⁿ⸣ {Zero} = Zero
xRx⸢≤ⁿ⸣ {Succ n} = Succ xRx⸢≤ⁿ⸣

⋈⸢≤ⁿ⸣ : antisymmetric _≤ⁿ_
⋈⸢≤ⁿ⸣ Zero Zero = ↯
⋈⸢≤ⁿ⸣ (Succ n₁≤n₂) (Succ n₂≤n₁) = res[•x] $ ⋈⸢≤ⁿ⸣ n₁≤n₂ n₂≤n₁

_⊚⸢≤ⁿ⸣_ : transitive _≤ⁿ_
n₁≤n₂ ⊚⸢≤ⁿ⸣ Zero = Zero
Succ n₂≤n₃ ⊚⸢≤ⁿ⸣ Succ n₁≤n₂ = Succ (n₂≤n₃ ⊚⸢≤ⁿ⸣ n₁≤n₂)

data _<ⁿ_ : ℕ → ℕ → Set where
  Zero : ∀ {n} → Zero <ⁿ Succ n
  Succ : ∀ {n₁ n₂} → n₁ <ⁿ n₂ → Succ n₁ <ⁿ Succ n₂

weaken[<]ⁿ : ∀ {n₁ n₂} → n₁ <ⁿ n₂ → n₁ ≤ⁿ n₂
weaken[<]ⁿ Zero = Zero
weaken[<]ⁿ (Succ <₁) = Succ (weaken[<]ⁿ <₁)

strict[<]ⁿ : ∀ {n₁ n₂} → n₁ <ⁿ n₂ → ¬ (n₂ ≤ⁿ n₁)
strict[<]ⁿ Zero ()
strict[<]ⁿ (Succ <₁) (Succ ≤₁) = strict[<]ⁿ <₁ ≤₁

complete[<]ⁿ : ∀ {n₁ n₂} → n₁ ≤ⁿ n₂ → ¬ (n₂ ≤ⁿ n₁) → n₁ <ⁿ n₂
complete[<]ⁿ {n₂ = Zero} Zero ¬≤ = exfalso (¬≤ Zero)
complete[<]ⁿ {n₂ = Succ n₂} Zero ¬≤ = Zero
complete[<]ⁿ (Succ ≤₁) ¬≤ = Succ (complete[<]ⁿ ≤₁ (λ ≤₂ → ¬≤ (Succ ≤₂)))

_⋚ⁿ_ : ℕ → ℕ → ⪥!
Zero ⋚ⁿ Zero = [≡]
Zero ⋚ⁿ Succ n₂ = [<]
Succ n₁ ⋚ⁿ Zero = [>]
Succ n₁ ⋚ⁿ Succ n₂ = n₁ ⋚ⁿ n₂

_⋚ⁿᴾ_ : ∀ n₁ n₂ → n₁ ⪥!ᴾ[ _<ⁿ_ ] n₂
Zero ⋚ⁿᴾ Zero = [≡] ↯
Zero ⋚ⁿᴾ Succ n₂ = [<] Zero
Succ n₁ ⋚ⁿᴾ Zero = [>] Zero
Succ n₁ ⋚ⁿᴾ Succ n₂ with n₁ ⋚ⁿᴾ n₂
… | [<] <₀ = [<] (Succ <₀)
… | [≡] ≡₀ rewrite ≡₀ = [≡] ↯
… | [>] >₀ = [>] (Succ >₀)

_⋚ⁿᴸ_ : ∀ n₁ n₂ → n₁ ⪥!ᴸ[ _<ⁿ_ ] n₂ ‖[ n₁ ⋚ⁿ n₂ , n₁ ⋚ⁿᴾ n₂ ]
Zero ⋚ⁿᴸ Zero = [≡]
Zero ⋚ⁿᴸ Succ n₂ = [<]
Succ n₁ ⋚ⁿᴸ Zero = [>]
Succ n₁ ⋚ⁿᴸ Succ n₂ with n₁ ⋚ⁿ n₂ | n₁ ⋚ⁿᴾ n₂ | n₁ ⋚ⁿᴸ n₂
… | [<] | [<] <₀ | [<] = [<]
… | [≡] | [≡] ≡₀ | [≡] rewrite ≡₀ = [≡]
… | [>] | [>] >₀ | [>] = [>]

instance
  Reflexive[≤]ⁿ : Reflexive _≤ⁿ_
  Reflexive[≤]ⁿ = record { xRx = xRx⸢≤ⁿ⸣ }
  Antisymmetric[≤]ⁿ : Antisymmetric _≤ⁿ_
  Antisymmetric[≤]ⁿ = record { ⋈ = ⋈⸢≤ⁿ⸣ }
  Transitive[≤]ⁿ : Transitive _≤ⁿ_
  Transitive[≤]ⁿ = record { _⊚_ = _⊚⸢≤ⁿ⸣_ }
  Strict[<]ⁿ : Strict _≤ⁿ_ _<ⁿ_
  Strict[<]ⁿ = record
    { weaken[≺] = weaken[<]ⁿ
    ; strict[≺] = strict[<]ⁿ
    ; complete[≺] = complete[<]ⁿ
    }
  Irreflexive[<]ⁿ : Irreflexive _<ⁿ_
  Irreflexive[<]ⁿ = Irreflexive[<]/≤ _≤ⁿ_ _<ⁿ_
  Asymmetric[<]ⁿ : Asymmetric _<ⁿ_
  Asymmetric[<]ⁿ = Asymmetric[<]/≤ _≤ⁿ_ _<ⁿ_
  Transitive[<]ⁿ : Transitive _<ⁿ_
  Transitive[<]ⁿ = Transitive[<]/≤ _≤ⁿ_ _<ⁿ_
  Totally[<]ⁿ : Totally _<ⁿ_
  Totally[<]ⁿ = record
    { _⪥_ = _⋚ⁿ_
    ; _⪥ᴾ_ = _⋚ⁿᴾ_
    ; _⪥ᴸ_ = _⋚ⁿᴸ_
    }
  Order[ℕ] : Order 0ᴸ ℕ
  Order[ℕ] = mk[Order] _≤ⁿ_ _<ⁿ_

weaken[≤]ⁿ : ∀ {n₁ n₂} → n₁ ≤ⁿ n₂ → n₁ ≤ⁿ Succ n₂
weaken[≤]ⁿ Zero = Zero
weaken[≤]ⁿ (Succ n₁≤n₂) = Succ (weaken[≤]ⁿ n₁≤n₂)

pred[≤]ⁿ : ∀ {n₁ n₂} → Succ n₁ ≤ⁿ n₂ → n₁ ≤ⁿ n₂
pred[≤]ⁿ (Succ x) = weaken[≤]ⁿ x

n≤succ : ∀ {n : ℕ} → n ≤ⁿ Succ n
n≤succ = weaken[≤]ⁿ xRx

rel[</≤]ⁿ : ∀ {n₁ n₂} → n₁ <ⁿ n₂ ↔ Succ n₁ ≤ⁿ n₂
rel[</≤]ⁿ = ⟨ LHS , RHS ⟩
  where
    LHS : ∀ {n₁ n₂} → n₁ <ⁿ n₂ → Succ n₁ ≤ⁿ n₂
    LHS Zero = Succ Zero
    LHS (Succ <₁) = Succ (LHS <₁)
    RHS : ∀ {n₁ n₂} → Succ n₁ ≤ⁿ n₂ → n₁ <ⁿ n₂
    RHS {Zero} (Succ ≤₁) = Zero
    RHS {Succ n₃} (Succ ≤₁) = Succ (RHS ≤₁)

------------------
-- ≤ⁿ Decidable --
------------------

instance
  DecRel[≡]ⁿ : DecRel (_≡_ {A = ℕ})
  DecRel[≡]ⁿ = record
    { _⁇_ = ⁇[≡]/⪥[] _≤_ _<_
    ; _⁇ᴾ_ = ⁇ᴾ[≡]/⪥[] _≤_ _<_
    ; _⁇ᴸ_ = ⁇ᴸ[≡]/⪥[] _≤_ _<_
    }
  DecRel[≤]ⁿ : DecRel _≤ⁿ_
  DecRel[≤]ⁿ = record
    { _⁇_ = ⁇[≤]/⪥[] _≤_ _<_
    ; _⁇ᴾ_ = ⁇ᴾ[≤]/⪥[] _≤_ _<_
    ; _⁇ᴸ_ = ⁇ᴸ[≤]/⪥[] _≤_ _<_
    }
  DecRel[<]ⁿ : DecRel _<ⁿ_
  DecRel[<]ⁿ = record
    { _⁇_ = ⁇[<]/⪥[] _≤_ _<_
    ; _⁇ᴾ_ = ⁇ᴾ[<]/⪥[] _≤_ _<_
    ; _⁇ᴸ_ = ⁇ᴸ[<]/⪥[] _≤_ _<_
    }

--------------
-- Addition --
--------------

_+ⁿ_ : ℕ → ℕ → ℕ
Zero +ⁿ n₂ = n₂
Succ n₁ +ⁿ n₂ = Succ (n₁ +ⁿ n₂)

left-unit[+]ⁿ : ∀ (n : ℕ) → 0 +ⁿ n ≡ n
left-unit[+]ⁿ n = ↯

right-unit[+]ⁿ : ∀ (n : ℕ) → n +ⁿ 0 ≡ n
right-unit[+]ⁿ Zero = ↯
right-unit[+]ⁿ (Succ n) rewrite right-unit[+]ⁿ n = ↯

associative[+]ⁿ : ∀ (n₁ n₂ n₃ : ℕ) → (n₁ +ⁿ n₂) +ⁿ n₃ ≡ n₁ +ⁿ (n₂ +ⁿ n₃)
associative[+]ⁿ Zero n₂ n₃ = ↯
associative[+]ⁿ (Succ n₁) n₂ n₃ rewrite associative[+]ⁿ n₁ n₂ n₃ = ↯

m+suc[n]=suc[m+n] : ∀ (n₁ n₂ : ℕ) → n₁ +ⁿ Succ n₂ ≡ Succ (n₁ +ⁿ n₂)
m+suc[n]=suc[m+n] Zero n₂ = ↯
m+suc[n]=suc[m+n] (Succ n₁) n₂ rewrite m+suc[n]=suc[m+n] n₁ n₂ = ↯

suc[m+n]=m+suc[n] : ∀ (n₁ n₂ : ℕ) → Succ (n₁ +ⁿ n₂) ≡ n₁ +ⁿ Succ n₂
suc[m+n]=m+suc[n] n₁ n₂ = ◇ $ m+suc[n]=suc[m+n] n₁ n₂

commutative[+]ⁿ : ∀ (n₁ n₂ : ℕ) → n₁ +ⁿ n₂ ≡ n₂ +ⁿ n₁
commutative[+]ⁿ Zero n₂ rewrite right-unit[+]ⁿ n₂ = ↯
commutative[+]ⁿ (Succ n₁) n₂ rewrite commutative[+]ⁿ n₁ n₂ | suc[m+n]=m+suc[n] n₂ n₁ = ↯

instance
  Additive[ℕ] : Additive ℕ
  Additive[ℕ] = record
    { zero = Zero
    ; _+_ = _+ⁿ_
    ; left-unit[+] = left-unit[+]ⁿ
    ; right-unit[+] = right-unit[+]ⁿ
    ; associative[+] = associative[+]ⁿ
    ; commutative[+] = commutative[+]ⁿ
    }

--------------------
-- Multiplication --
--------------------

oneⁿ : ℕ
oneⁿ = Succ Zero

_×ⁿ_ : ℕ → ℕ → ℕ
Zero ×ⁿ n₂ = Zero
Succ n₁ ×ⁿ n₂ = n₂ +ⁿ (n₁ ×ⁿ n₂)

left-zero[×]ⁿ : ∀ n → zero ×ⁿ n ≡ zero
left-zero[×]ⁿ n = ↯

right-zero[×]ⁿ : ∀ n → n ×ⁿ zero ≡ zero
right-zero[×]ⁿ Zero = ↯
right-zero[×]ⁿ (Succ n) rewrite right-zero[×]ⁿ n = ↯

left-unit[×]ⁿ : ∀ n → oneⁿ ×ⁿ n ≡ n
left-unit[×]ⁿ n = right-unit[+]ⁿ n

right-unit[×]ⁿ : ∀ n → n ×ⁿ oneⁿ ≡ n
right-unit[×]ⁿ Zero = ↯
right-unit[×]ⁿ (Succ n) rewrite right-unit[×]ⁿ n = ↯

distributive[×]ⁿ : ∀ n₁ n₂ n₃ → (n₁ +ⁿ n₂) ×ⁿ n₃ ≡ (n₁ ×ⁿ n₃) +ⁿ (n₂ ×ⁿ n₃)
distributive[×]ⁿ Zero n₂ n₃ = ↯
distributive[×]ⁿ (Succ n₁) n₂ n₃ rewrite distributive[×]ⁿ n₁ n₂ n₃ | associative[+]ⁿ n₃ (n₁ ×ⁿ n₃) (n₂ ×ⁿ n₃) = ↯

associative[×]ⁿ : ∀ n₁ n₂ n₃ → (n₁ ×ⁿ n₂) ×ⁿ n₃ ≡ n₁ ×ⁿ (n₂ ×ⁿ n₃)
associative[×]ⁿ Zero n₂ n₃ = ↯
associative[×]ⁿ (Succ n₁) n₂ n₃ rewrite distributive[×]ⁿ n₂ (n₁ ×ⁿ n₂) n₃ | associative[×]ⁿ n₁ n₂ n₃ = ↯

commutative[×]ⁿ : ∀ n₁ n₂ → n₁ ×ⁿ n₂ ≡ n₂ ×ⁿ n₁
commutative[×]ⁿ Zero n₂ rewrite right-zero[×]ⁿ n₂ = ↯
commutative[×]ⁿ n₁ Zero rewrite right-zero[×]ⁿ n₁ = ↯
commutative[×]ⁿ (Succ n₁) (Succ n₂)
  rewrite commutative[×]ⁿ n₁ (Succ n₂)
        | commutative[×]ⁿ n₂ (Succ n₁)
        | commutative[×]ⁿ n₁ n₂
        | ◇ $ associative[+]ⁿ n₁ n₂ (n₂ ×ⁿ n₁)
        | commutative[+]ⁿ n₁ n₂
        | associative[+]ⁿ n₂ n₁ (n₂ ×ⁿ n₁)
        = ↯

instance
  Multiplicative[ℕ] : Multiplicative ℕ
  Multiplicative[ℕ] = record
    { one = oneⁿ
    ; _×_ = _×ⁿ_
    ; left-zero[×] = left-zero[×]ⁿ
    ; right-zero[×] = right-zero[×]ⁿ
    ; left-unit[×] = left-unit[×]ⁿ
    ; right-unit[×] = right-unit[×]ⁿ
    ; associative[×] = associative[×]ⁿ
    ; commutative[×] = commutative[×]ⁿ
    ; distributive[×] = distributive[×]ⁿ
    }

-----------------
-- Subtraction --
-----------------

_-ⁿ_‖_ : ∀ (n₁ n₂ : ℕ) → n₂ ≤ n₁ → ℕ
n₁ -ⁿ Zero ‖ Zero = n₁
Succ n₁ -ⁿ Succ n₂ ‖ Succ n₁≤n₂ = n₁ -ⁿ n₂ ‖ n₁≤n₂

correct[-ⁿ‖] : ∀ n₁ n₂ (n₂≤n₁ : n₂ ≤ n₁) → n₂ + (n₁ -ⁿ n₂ ‖ n₂≤n₁) ≡ n₁
correct[-ⁿ‖] n₁ Zero Zero = ↯
correct[-ⁿ‖] (Succ n₁) (Succ n₂) (Succ n₂≤n₁) rewrite correct[-ⁿ‖] n₁ n₂ n₂≤n₁ = ↯

n-n=0 : ∀ n → n -ⁿ n ‖ xRx ≡ Zero
n-n=0 Zero = ↯
n-n=0 (Succ n) rewrite n-n=0 n = ↯

n-0=n : ∀ n → n -ⁿ 0 ‖ Zero ≡ n
n-0=n n = ↯

suc[m-n]=suc[m]-n : ∀ n₁ n₂ (n₂≤n₁ : n₂ ≤ n₁) → Succ (n₁ -ⁿ n₂ ‖ n₂≤n₁) ≡ Succ n₁ -ⁿ n₂ ‖ weaken[≤]ⁿ n₂≤n₁
suc[m-n]=suc[m]-n n₁ Zero Zero = ↯
suc[m-n]=suc[m]-n (Succ n₁) (Succ n₂) (Succ n₂≤n₁) rewrite suc[m-n]=suc[m]-n n₁ n₂ n₂≤n₁ = ↯

instance
  Subtractive/OK[ℕ] : Subtractive/OK ℕ
  Subtractive/OK[ℕ] = record { ok[_-_] = _≥_ }
  Subtractive[ℕ] : Subtractive/P ℕ
  Subtractive[ℕ] = record
    { _-_‖_ = _-ⁿ_‖_
    ; correct[-‖] = correct[-ⁿ‖]
    }

_-%ⁿ_ : ∀ (n₁ n₂ : ℕ) → ℕ ∨ ℕ
Zero -%ⁿ n₂ = Inr n₂
n₁ -%ⁿ Zero = Inl n₁
Succ n₁ -%ⁿ Succ n₂ = n₁ -%ⁿ n₂

correct[-%ⁿ]/spec : ℕ → ℕ → Set
correct[-%ⁿ]/spec n₁ n₂ with n₁ -%ⁿ n₂
… | Inl n = n₂ + n ≡ n₁
… | Inr n = n₁ + n ≡ n₂

correct[-%ⁿ] : ∀ n₁ n₂ → correct[-%ⁿ]/spec n₁ n₂
correct[-%ⁿ] Zero Zero = ↯
correct[-%ⁿ] Zero (Succ n₂) = ↯
correct[-%ⁿ] (Succ n₁) Zero = ↯
correct[-%ⁿ] (Succ n₁) (Succ n₂) with n₁ -%ⁿ n₂ | correct[-%ⁿ] n₁ n₂
… | Inl x | IH rewrite IH = ↯
… | Inr x | IH rewrite IH = ↯

--------------------------
-- Division and Modulus --
--------------------------

divmod-loop : ∀ (n₁ n₂ quo n₂-rem : ℕ) → n₂-rem ≤ n₂ → ∃ quo,n₂-rem ⦂ ℕ ∧ ℕ 𝑠𝑡 let ⟨ quo' , n₂-rem' ⟩ = quo,n₂-rem in n₂-rem' ≤ n₂
divmod-loop Zero n₂ quo n₂-rem n₂-rem≤n₂ = ⟨∃ ⟨ quo , n₂-rem ⟩ , n₂-rem≤n₂ ⟩
divmod-loop (Succ n₁) n₂ quo Zero n₂-rem≤n₂ = divmod-loop n₁ n₂ (Succ quo) n₂ xRx
divmod-loop (Succ n₁) n₂ quo (Succ n₂-rem) n₂-rem≤n₂ = divmod-loop n₁ n₂ quo n₂-rem (pred[≤]ⁿ n₂-rem≤n₂)

correct[divmod-loop] :
  ∀ (n₁ n₂ quo n₂-rem : ℕ) (n₂-rem≤n₂ : n₂-rem ≤ n₂)
  → let ⟨∃ ⟨ quo' , n₂-rem' ⟩ , n₂-rem'≤n₂ ⟩ = divmod-loop n₁ n₂ quo n₂-rem n₂-rem≤n₂ in
    quo' × Succ n₂ + (n₂ - n₂-rem' ‖ n₂-rem'≤n₂) ≡ n₁ + (quo × Succ n₂) + (n₂ - n₂-rem ‖ n₂-rem≤n₂)
correct[divmod-loop] Zero n₂ quo n₂-rem n₂-rem≤n₂ = ↯
correct[divmod-loop] (Succ n₁) n₂ quo Zero Zero
  with divmod-loop n₁ n₂ (Succ quo) n₂ xRx
     | correct[divmod-loop] n₁ n₂ (Succ quo) n₂ xRx
… | ⟨∃ ⟨ quo' , n₂-rem' ⟩ , n₂-rem'≤n₂ ⟩ | IH
  rewrite suc[m+n]=m+suc[n] n₁ (quo × Succ n₂ + n₂ - Zero ‖ Zero)
        | n-n=0 n₂
        | commutative[+] (quo × Succ n₂) n₂
        | right-unit[+] (n₂ + quo × Succ n₂)
        = IH
correct[divmod-loop] (Succ n₁) (Succ n₂) quo (Succ n₂-rem) (Succ n₂-rem≤n₂)
  with divmod-loop n₁ (Succ n₂) quo n₂-rem (weaken[≤]ⁿ n₂-rem≤n₂)
     | correct[divmod-loop] n₁ (Succ n₂) quo n₂-rem (weaken[≤]ⁿ n₂-rem≤n₂)
… | ⟨∃ ⟨ quo' , n₂-rem' ⟩ , n₂-rem'≤n₂ ⟩ | IH
  rewrite suc[m+n]=m+suc[n] n₁ (quo × Succ (Succ n₂) + n₂ - n₂-rem ‖ n₂-rem≤n₂)
        | suc[m+n]=m+suc[n] (quo × Succ (Succ n₂)) (n₂ - n₂-rem ‖ n₂-rem≤n₂)
        | suc[m-n]=suc[m]-n n₂ n₂-rem n₂-rem≤n₂
        = IH

_/%ⁿ_‖_ : ∀ (n₁ n₂ : ℕ) (0<n₂ : 0 < n₂) → ℕ ∧ ℕ
n₁ /%ⁿ Succ n₂ ‖ Zero with divmod-loop n₁ n₂ 0 n₂ xRx⸢≤ⁿ⸣
… | ⟨∃ ⟨ quo , n₂-rem ⟩ , n₂-rem≤n₂ ⟩ = ⟨ quo , n₂ - n₂-rem ‖ n₂-rem≤n₂ ⟩

correct[/%‖]ⁿ : ∀ n₁ n₂ (0<n₂ : 0 < n₂) → let ⟨ quo , rem ⟩ = n₁ /%ⁿ n₂ ‖ 0<n₂ in n₂ × quo + rem ≡ n₁
correct[/%‖]ⁿ n₁ (Succ n₂) Zero with divmod-loop n₁ n₂ 0 n₂ xRx | correct[divmod-loop] n₁ n₂ 0 n₂ xRx
… | ⟨∃ ⟨ quo , n₂-rem ⟩ , n₂-rem≤n₂ ⟩ | H rewrite n-n=0 n₂ | right-unit[+] n₁ | commutative[×] quo (Succ n₂) = H

instance
  DivMod/OK[ℕ] : DivMod/OK ℕ
  DivMod/OK[ℕ] = record
    { ok[_/%_] = (λ n₁ n₂ → 0 < n₂) }
  DivMod[ℕ] : DivMod/P ℕ
  DivMod[ℕ] = record
    { _/%_‖_ = _/%ⁿ_‖_
    ; correct[/%‖] = correct[/%‖]ⁿ
    }

-------------
-- Bounded --
-------------

record ℕ≤[_] (nᵇ : ℕ) : Set where
  constructor mk[ℕ≤]
  field
    n : ℕ
    n≤nᵇ : n ≤ nᵇ

-- mk↯[ℕ≤] : ∀ {nᵇ} n → {{_ : ✓‼ (n ⁇[ _≤_ ] nᵇ)}} → ℕ≤[ nᵇ ]
-- mk↯[ℕ≤] {nᵇ} n {{_}} with n ⁇[ _≤_ ] nᵇ |
-- mk↯[ℕ≤] {nᵇ} n {{↯Rel}} | Rel n≤nᵇ = mk[ℕ≤] n n≤nᵇ

weaken[ℕ≤] : ∀ {nᵇ₁ nᵇ₂} → nᵇ₁ ≤ nᵇ₂ → ℕ≤[ nᵇ₁ ] → ℕ≤[ nᵇ₂ ]
weaken[ℕ≤] nᵇ₁≤nᵇ₂ (mk[ℕ≤] n n≤nᵇ₁) = mk[ℕ≤] n (nᵇ₁≤nᵇ₂ ⊚ n≤nᵇ₁)

instance
  ToNatural[ℕ≤] : ∀ {nᵇ} → ToNatural ℕ≤[ nᵇ ]
  ToNatural[ℕ≤] = mk[ToNatural] ℕ≤[_].n

record ℕ<[_] (nᵇ : ℕ) : Set where
  constructor mk[ℕ<]
  field
    n : ℕ
    n<nᵇ : n < nᵇ

-- mk↯[ℕ<] : ∀ {nᵇ} n → {{_ : rel (dec-rel[ _<_ ] n nᵇ)}} → ℕ<[ nᵇ ]
-- mk↯[ℕ<] {nᵇ} n {{_}} with dec-rel[ _<_ ] n nᵇ
-- mk↯[ℕ<] {nᵇ} n {{↯Rel}} | Rel n<nᵇ = mk[ℕ<] n n<nᵇ

weaken[ℕ<] : ∀ {nᵇ₁ nᵇ₂} → nᵇ₁ ≤ nᵇ₂ → ℕ<[ nᵇ₁ ] → ℕ<[ nᵇ₂ ]
weaken[ℕ<] nᵇ₁≤nᵇ₂ (mk[ℕ<] n n<nᵇ₁) = mk[ℕ<] n $ π₂ rel[</≤]ⁿ $ nᵇ₁≤nᵇ₂ ⊚ π₁ rel[</≤]ⁿ n<nᵇ₁

instance
  ToNatural[ℕ<] : ∀ {nᵇ} → ToNatural ℕ<[ nᵇ ]
  ToNatural[ℕ<] = mk[ToNatural] ℕ<[_].n

data fin : ℕ → Set where
  Zero : ∀ {n} → fin (Succ n)
  Succ : ∀ {n} → fin n → fin (Succ n)

mk[fin/<] : ∀ {nᵇ} n → n < nᵇ → fin nᵇ
mk[fin/<] Zero Zero = Zero
mk[fin/<] (Succ n) (Succ <₁) = Succ (mk[fin/<] n <₁)

mk[fin/ℕ<] : ∀ {nᵇ} → ℕ<[ nᵇ ] → fin nᵇ
mk[fin/ℕ<] (mk[ℕ<] n n<nᵇ) = mk[fin/<] n n<nᵇ

record ℕ⁺ : Set where
  constructor mk[ℕ⁺]
  field
    n : ℕ
    0<n : 0 < n

-- mk↯[ℕ⁺] : ∀ n {{_ : rel (dec-rel[ _<_ ] 0 n)}} → ℕ⁺
-- mk↯[ℕ⁺] n {{_}} with dec-rel[ _<_ ] 0 n
-- mk↯[ℕ⁺] n {{↯Rel}} | Rel 0<n = mk[ℕ⁺] n 0<n

instance
  ToNatural[ℕ⁺] : ToNatural ℕ⁺
  ToNatural[ℕ⁺] = mk[ToNatural] ℕ⁺.n


------------------------------------------------------------------------
-- Alpha-equivalence
------------------------------------------------------------------------

open import Atom

module Alpha-equivalence (atoms : χ-atoms) where

open import Equality.Propositional
open import Prelude hiding (const)

open import Bag-equivalence equality-with-J using (_∈_)
import Nat equality-with-J as Nat

open import Chi            atoms
open import Free-variables atoms

open χ-atoms atoms

private
  variable
    A                          : Type
    b₁ b₂                      : Br
    bs₁ bs₂                    : List Br
    c c₁ c₂                    : Const
    e e₁ e₂ e₃ e₁₁ e₁₂ e₂₁ e₂₂ : Exp
    es₁ es₂                    : List Exp
    R R₁ R₂                    : A → A → Type
    x x₁ x₁′ x₂ x₂′ y          : A
    xs xs₁ xs₂                 : List A

------------------------------------------------------------------------
-- The definition of α-equivalence

-- R [ x ∼ y ] relates x to y, and for pairs of variables x′, y′ such
-- that x is distinct from x′ and y is distinct from y′ it behaves
-- like R.

infix 5 _[_∼_]

_[_∼_] : (Var → Var → Type) → Var → Var → (Var → Var → Type)
(R [ x ∼ y ]) x′ y′ =
  x ≡ x′ × y ≡ y′
    ⊎
  x ≢ x′ × y ≢ y′ × R x′ y′

-- Alpha-⋆ lifts a binary relation to lists.

infixr 5 _∷_

data Alpha-⋆ (R : A → A → Type) : List A → List A → Type where
  []  : Alpha-⋆ R [] []
  _∷_ : R x₁ x₂ →
        Alpha-⋆ R xs₁ xs₂ →
        Alpha-⋆ R (x₁ ∷ xs₁) (x₂ ∷ xs₂)

-- Alpha R is α-equivalence up to R: free variables are related if
-- they are related by R.

mutual

  data Alpha (R : Var → Var → Type) : Exp → Exp → Type where
    apply :
      Alpha R e₁₁ e₁₂ → Alpha R e₂₁ e₂₂ →
      Alpha R (apply e₁₁ e₂₁) (apply e₁₂ e₂₂)

    lambda :
      Alpha (R [ x₁ ∼ x₂ ]) e₁ e₂ →
      Alpha R (lambda x₁ e₁) (lambda x₂ e₂)

    case :
      Alpha R e₁ e₂ →
      Alpha-⋆ (Alpha-Br R) bs₁ bs₂ →
      Alpha R (case e₁ bs₁) (case e₂ bs₂)

    rec :
      Alpha (R [ x₁ ∼ x₂ ]) e₁ e₂ →
      Alpha R (rec x₁ e₁) (rec x₂ e₂)

    var : R x₁ x₂ → Alpha R (var x₁) (var x₂)

    const :
      Alpha-⋆ (Alpha R) es₁ es₂ →
      Alpha R (const c es₁) (const c es₂)

  data Alpha-Br (R : Var → Var → Type) : Br → Br → Type where
    nil :
      Alpha R e₁ e₂ →
      Alpha-Br R (branch c [] e₁) (branch c [] e₂)
    cons :
      Alpha-Br (R [ x₁ ∼ x₂ ]) (branch c xs₁ e₁) (branch c xs₂ e₂) →
      Alpha-Br R (branch c (x₁ ∷ xs₁) e₁) (branch c (x₂ ∷ xs₂) e₂)

-- The α-equivalence relation.

infix 4 _≈α_

_≈α_ : Exp → Exp → Type
_≈α_ = Alpha _≡_

------------------------------------------------------------------------
-- Some properties related to reflexivity

-- If R y y holds assuming that y is distinct from x, then
-- (R [ x ∼ x ]) y y holds.

[≢→[∼]]→[∼] :
  ∀ R →
  (y ≢ x → R y y) →
  (R [ x ∼ x ]) y y
[≢→[∼]]→[∼] {y = y} {x = x} _ r = case x V.≟ y of λ where
  (yes x≡y) → inj₁ (x≡y , x≡y)
  (no  x≢y) → inj₂ ( x≢y , x≢y
                   , r (x≢y ∘ sym)
                   )

-- Alpha R is reflexive for expressions for which R x x holds for
-- every free variable x.

mutual

  refl-Alpha :
    ∀ e → (∀ x → x ∈FV e → R x x) → Alpha R e e

  refl-Alpha (apply e₁ e₂) r =
    apply (refl-Alpha e₁ λ x x∈e₁ → r x (apply-left  x∈e₁))
          (refl-Alpha e₂ λ x x∈e₂ → r x (apply-right x∈e₂))

  refl-Alpha {R = R} (lambda x e) r =
    lambda (refl-Alpha e λ y y∈e → [≢→[∼]]→[∼] R λ y≢x →
              r y (lambda y≢x y∈e))

  refl-Alpha (case e bs) r =
    case
      (refl-Alpha e λ x x∈e →
         r x (case-head x∈e))
      (refl-Alpha-B⋆ bs λ x ∈bs x∉xs x∈ →
         r x (case-body x∈ ∈bs x∉xs))

  refl-Alpha {R = R} (rec x e) r =
    rec (refl-Alpha e λ y y∈e → [≢→[∼]]→[∼] R λ y≢x →
           r y (rec y≢x y∈e))

  refl-Alpha (var x) r =
    var (r x (var refl))

  refl-Alpha (const c es) r =
    const (refl-Alpha-⋆ es λ x e e∈es x∈e →
             r x (const x∈e e∈es))

  refl-Alpha-B :
    ∀ c xs e →
    (∀ x → ¬ x ∈ xs → x ∈FV e → R x x) →
    Alpha-Br R (branch c xs e) (branch c xs e)
  refl-Alpha-B c [] e r =
    nil (refl-Alpha e λ x x∈e →
           r x (λ ()) x∈e)
  refl-Alpha-B {R = R} c (x ∷ xs) e r =
    cons (refl-Alpha-B c xs e λ y y∉xs y∈e → [≢→[∼]]→[∼] R λ y≢x →
            r y [ y≢x , y∉xs ] y∈e)

  refl-Alpha-⋆ :
    ∀ es → (∀ x e → e ∈ es → x ∈FV e → R x x) →
    Alpha-⋆ (Alpha R) es es
  refl-Alpha-⋆ []       _ = []
  refl-Alpha-⋆ (e ∷ es) r =
    refl-Alpha e (λ x x∈e → r x e (inj₁ refl) x∈e) ∷
    refl-Alpha-⋆ es (λ x e e∈es x∈e → r x e (inj₂ e∈es) x∈e)

  refl-Alpha-B⋆ :
    ∀ bs →
    (∀ x {c xs e} → branch c xs e ∈ bs → ¬ x ∈ xs → x ∈FV e → R x x) →
    Alpha-⋆ (Alpha-Br R) bs bs
  refl-Alpha-B⋆ []                   r = []
  refl-Alpha-B⋆ (branch c xs e ∷ bs) r =
    refl-Alpha-B c xs e (λ x x∉xs x∈e → r x (inj₁ refl) x∉xs x∈e) ∷
    refl-Alpha-B⋆ bs (λ x ∈bs x∉xs x∈ → r x (inj₂ ∈bs) x∉xs x∈)

-- The α-equivalence relation is reflexive.

refl-α : e ≈α e
refl-α = refl-Alpha _ (λ _ _ → refl)

-- Equational reasoning combinators.

infix  -1 finally-α _∎α
infixr -2 step-≡-≈α step-≈α-≡ _≡⟨⟩α_

_∎α : ∀ e → e ≈α e
_ ∎α = refl-α

step-≡-≈α : ∀ e₁ → e₂ ≈α e₃ → e₁ ≡ e₂ → e₁ ≈α e₃
step-≡-≈α _ e₂≈e₃ e₁≡e₂ = subst (_≈α _) (sym e₁≡e₂) e₂≈e₃

syntax step-≡-≈α e₁ e₂≈e₃ e₁≡e₂ = e₁ ≡⟨ e₁≡e₂ ⟩α e₂≈e₃

step-≈α-≡ : ∀ e₁ → e₂ ≡ e₃ → e₁ ≈α e₂ → e₁ ≈α e₃
step-≈α-≡ _ e₂≡e₃ e₁≈e₂ = subst (_ ≈α_) e₂≡e₃ e₁≈e₂

syntax step-≈α-≡ e₁ e₂≡e₃ e₁≈e₂ = e₁ ≈⟨ e₁≈e₂ ⟩α e₂≡e₃

_≡⟨⟩α_ : ∀ e₁ → e₁ ≈α e₂ → e₁ ≈α e₂
_ ≡⟨⟩α e₁≈e₂ = e₁≈e₂

finally-α : ∀ e₁ e₂ → e₁ ≈α e₂ → e₁ ≈α e₂
finally-α _ _ e₁≈e₂ = e₁≈e₂

syntax finally-α e₁ e₂ e₁≈e₂ = e₁ ≈⟨ e₁≈e₂ ⟩α∎ e₂ ∎

------------------------------------------------------------------------
-- A map function

-- A kind of map function for _[_∼_].

map-[∼] :
  ∀ R₁ → (∀ {x₁ x₂} → R₁ x₁ x₂ → R₂ x₁ x₂) →
  (R₁ [ x₁ ∼ x₂ ]) x₁′ x₂′ →
  (R₂ [ x₁ ∼ x₂ ]) x₁′ x₂′
map-[∼] _ r = ⊎-map id (Σ-map id (Σ-map id r))

-- A kind of map function for Alpha.

mutual

  map-Alpha :
    (∀ {x₁ x₂} → R₁ x₁ x₂ → R₂ x₁ x₂) →
    Alpha R₁ e₁ e₂ → Alpha R₂ e₁ e₂

  map-Alpha r (var Rx₁x₂) = var (r Rx₁x₂)

  map-Alpha {R₁ = R₁} r (lambda e₁≈e₂) =
    lambda (map-Alpha (map-[∼] R₁ r) e₁≈e₂)

  map-Alpha {R₁ = R₁} r (rec e₁≈e₂) =
    rec (map-Alpha (map-[∼] R₁ r) e₁≈e₂)

  map-Alpha r (apply e₁₁≈e₁₂ e₂₁≈e₂₂) =
    apply (map-Alpha r e₁₁≈e₁₂) (map-Alpha r e₂₁≈e₂₂)

  map-Alpha r (const es₁≈es₂) =
    const (map-Alpha-⋆ r es₁≈es₂)

  map-Alpha r (case e₁≈e₂ bs₁≈bs₂) =
    case (map-Alpha r e₁≈e₂)
         (map-Alpha-Br-⋆ r bs₁≈bs₂)

  map-Alpha-Br :
    (∀ {x₁ x₂} → R₁ x₁ x₂ → R₂ x₁ x₂) →
    Alpha-Br R₁ b₁ b₂ → Alpha-Br R₂ b₁ b₂
  map-Alpha-Br r (nil e₁≈e₂) =
    nil (map-Alpha r e₁≈e₂)
  map-Alpha-Br {R₁ = R₁} r (cons b₁≈b₂) =
    cons (map-Alpha-Br (map-[∼] R₁ r) b₁≈b₂)

  map-Alpha-⋆ :
    (∀ {x₁ x₂} → R₁ x₁ x₂ → R₂ x₁ x₂) →
    Alpha-⋆ (Alpha R₁) es₁ es₂ → Alpha-⋆ (Alpha R₂) es₁ es₂
  map-Alpha-⋆ _ []                = []
  map-Alpha-⋆ r (e₁≈e₂ ∷ es₁≈es₂) =
    map-Alpha r e₁≈e₂ ∷ map-Alpha-⋆ r es₁≈es₂

  map-Alpha-Br-⋆ :
    (∀ {x₁ x₂} → R₁ x₁ x₂ → R₂ x₁ x₂) →
    Alpha-⋆ (Alpha-Br R₁) bs₁ bs₂ → Alpha-⋆ (Alpha-Br R₂) bs₁ bs₂
  map-Alpha-Br-⋆ _ []                = []
  map-Alpha-Br-⋆ r (b₁≈b₂ ∷ bs₁≈bs₂) =
    map-Alpha-Br r b₁≈b₂ ∷ map-Alpha-Br-⋆ r bs₁≈bs₂

------------------------------------------------------------------------
-- Symmetry

-- A kind of symmetry holds for _[_∼_].

sym-[∼] :
  ∀ R →
  (R [ x₁ ∼ x₂ ]) x₁′ x₂′ →
  (flip R [ x₂ ∼ x₁ ]) x₂′ x₁′
sym-[∼] _ =
  ⊎-map Prelude.swap
        (λ (x₁≢x₁′ , x₂≢x₂′ , R₁x₁′x₂′) →
           x₂≢x₂′ , x₁≢x₁′ , R₁x₁′x₂′)

-- A kind of symmetry holds for Alpha.

mutual

  sym-Alpha : Alpha R e₁ e₂ → Alpha (flip R) e₂ e₁

  sym-Alpha (var Rx₁x₂) = var Rx₁x₂

  sym-Alpha {R = R} (lambda e₁≈e₂) =
    lambda (map-Alpha (sym-[∼] R) (sym-Alpha e₁≈e₂))

  sym-Alpha {R = R} (rec e₁≈e₂) =
    rec (map-Alpha (sym-[∼] R) (sym-Alpha e₁≈e₂))

  sym-Alpha (apply e₁₁≈e₁₂ e₂₁≈e₂₂) =
    apply (sym-Alpha e₁₁≈e₁₂) (sym-Alpha e₂₁≈e₂₂)

  sym-Alpha (const es₁≈es₂) =
    const (sym-Alpha-⋆ es₁≈es₂)

  sym-Alpha (case e₁≈e₂ bs₁≈bs₂) =
    case (sym-Alpha e₁≈e₂)
         (sym-Alpha-Br-⋆ bs₁≈bs₂)

  sym-Alpha-Br :
    Alpha-Br R b₁ b₂ → Alpha-Br (flip R) b₂ b₁
  sym-Alpha-Br (nil e₁≈e₂) =
    nil (sym-Alpha e₁≈e₂)
  sym-Alpha-Br {R = R} (cons b₁≈b₂) =
    cons (map-Alpha-Br (sym-[∼] R) (sym-Alpha-Br b₁≈b₂))

  sym-Alpha-⋆ :
    Alpha-⋆ (Alpha R) es₁ es₂ → Alpha-⋆ (Alpha (flip R)) es₂ es₁
  sym-Alpha-⋆ []                = []
  sym-Alpha-⋆ (e₁≈e₂ ∷ es₁≈es₂) =
    sym-Alpha e₁≈e₂ ∷ sym-Alpha-⋆ es₁≈es₂

  sym-Alpha-Br-⋆ :
    Alpha-⋆ (Alpha-Br R) bs₁ bs₂ → Alpha-⋆ (Alpha-Br (flip R)) bs₂ bs₁
  sym-Alpha-Br-⋆ []                = []
  sym-Alpha-Br-⋆ (b₁≈b₂ ∷ bs₁≈bs₂) =
    sym-Alpha-Br b₁≈b₂ ∷ sym-Alpha-Br-⋆ bs₁≈bs₂

-- The α-equivalence relation is symmetric.

sym-α : e₁ ≈α e₂ → e₂ ≈α e₁
sym-α = map-Alpha sym ∘ sym-Alpha

------------------------------------------------------------------------
-- Several things respect α-equivalence

-- The free variable relation respects α-equivalence.

mutual

  Alpha-∈ :
    Alpha R e₁ e₂ → x₁ ∈FV e₁ → ∃ λ x₂ → R x₁ x₂ × x₂ ∈FV e₂
  Alpha-∈ {R = R} (var Ry₁y₂) (var x₁≡y₁) =
    _ , subst (flip R _) (sym x₁≡y₁) Ry₁y₂ , var refl

  Alpha-∈ (lambda e₁≈e₂) (lambda x₁≢y₁ x₁∈)
    with Alpha-∈ e₁≈e₂ x₁∈
  … | x₂ , inj₂ (_ , y₂≢x₂ , Rx₁x₂) , x₂∈ =
    x₂ , Rx₁x₂ , lambda (y₂≢x₂ ∘ sym) x₂∈
  … | _ , inj₁ (y₁≡x₁ , _) , _ =
    ⊥-elim $ x₁≢y₁ (sym y₁≡x₁)

  Alpha-∈ (rec e₁≈e₂) (rec x₁≢y₁ x₁∈)
    with Alpha-∈ e₁≈e₂ x₁∈
  … | x₂ , inj₂ (_ , y₂≢x₂ , Rx₁x₂) , x₂∈ =
    x₂ , Rx₁x₂ , rec (y₂≢x₂ ∘ sym) x₂∈
  … | _ , inj₁ (y₁≡x₁ , _) , _ =
    ⊥-elim $ x₁≢y₁ (sym y₁≡x₁)

  Alpha-∈ (apply e₁₁≈e₁₂ e₂₁≈e₂₂) (apply-left x₁∈) =
    Σ-map id (Σ-map id apply-left) $ Alpha-∈ e₁₁≈e₁₂ x₁∈
  Alpha-∈ (apply e₁₁≈e₁₂ e₂₁≈e₂₂) (apply-right x₁∈) =
    Σ-map id (Σ-map id apply-right) $ Alpha-∈ e₂₁≈e₂₂ x₁∈

  Alpha-∈ (const es₁≈es₂) (const x₁∈ ∈es₁) =
    Σ-map id (Σ-map id $ uncurry λ _ → uncurry const) $
    Alpha-⋆-∈ es₁≈es₂ x₁∈ ∈es₁

  Alpha-∈ (case e₁≈e₂ bs₁≈bs₂) (case-head x₁∈) =
    Σ-map id (Σ-map id case-head) $
    Alpha-∈ e₁≈e₂ x₁∈

  Alpha-∈ (case e₁≈e₂ bs₁≈bs₂) (case-body x₁∈ ∈bs₁ ∉xs₁) =
    Σ-map id (Σ-map id λ (_ , _ , _ , x₂∈ , ∈bs₂ , ∉xs₂) →
                case-body x₂∈ ∈bs₂ ∉xs₂) $
    Alpha-Br-⋆-∈ bs₁≈bs₂ x₁∈ ∈bs₁ ∉xs₁

  Alpha-Br-∈ :
    Alpha-Br R (branch c₁ xs₁ e₁) (branch c₂ xs₂ e₂) →
    x₁ ∈FV e₁ → ¬ x₁ ∈ xs₁ →
    ∃ λ x₂ → R x₁ x₂ × x₂ ∈FV e₂ × ¬ x₂ ∈ xs₂
  Alpha-Br-∈ (nil e₁≈e₂) x₁∈ _ =
    Σ-map id (Σ-map id (_, λ ())) $
    Alpha-∈ e₁≈e₂ x₁∈
  Alpha-Br-∈ (cons {x₁ = y₁} {x₂ = y₂} bs₁≈bs₂) x₁∈ x₁∉
    with Alpha-Br-∈ bs₁≈bs₂ x₁∈ (x₁∉ ∘ inj₂)
  … | x₂ , inj₂ (_ , y₂≢x₂ , Rx₁x₂) , x₂∈ , x₂∉ =
    x₂ , Rx₁x₂ , x₂∈ , [ y₂≢x₂ ∘ sym , x₂∉ ]
  … | _ , inj₁ (y₁≡x₁ , _) , _ =
    ⊥-elim $ x₁∉ (inj₁ (sym y₁≡x₁))

  Alpha-⋆-∈ :
    Alpha-⋆ (Alpha R) es₁ es₂ →
    x₁ ∈FV e₁ → e₁ ∈ es₁ →
    ∃ λ x₂ → R x₁ x₂ × ∃ λ e₂ → x₂ ∈FV e₂ × e₂ ∈ es₂
  Alpha-⋆-∈ (e₁≈e₂ ∷ es₁≈es₂) x₁∈ (inj₁ ≡e₁) =
    Σ-map id (Σ-map id λ x₂∈ → _ , x₂∈ , inj₁ refl) $
    Alpha-∈ e₁≈e₂ (subst (_ ∈FV_) ≡e₁ x₁∈)
  Alpha-⋆-∈ (e₁≈e₂ ∷ es₁≈es₂) x₁∈ (inj₂ ∈es₁) =
    Σ-map id (Σ-map id (Σ-map id (Σ-map id inj₂))) $
    Alpha-⋆-∈ es₁≈es₂ x₁∈ ∈es₁

  Alpha-Br-⋆-∈ :
    Alpha-⋆ (Alpha-Br R) bs₁ bs₂ →
    x₁ ∈FV e₁ → branch c₁ xs₁ e₁ ∈ bs₁ → ¬ x₁ ∈ xs₁ →
    ∃ λ x₂ → R x₁ x₂ × ∃ λ c₂ → ∃ λ xs₂ → ∃ λ e₂ →
      x₂ ∈FV e₂ × branch c₂ xs₂ e₂ ∈ bs₂ × ¬ x₂ ∈ xs₂
  Alpha-Br-⋆-∈
    (_∷_ {x₁ = branch _ _ _} {x₂ = branch _ _ _} b₁≈b₂ bs₁≈bs₂)
    x₁∈ (inj₁ ≡b₁) x₁∉
    with
      Alpha-Br-∈
        b₁≈b₂
        (subst (_ ∈FV_) (cong (λ { (branch _ _ e) → e }) ≡b₁) x₁∈)
        (x₁∉ ∘
         subst (_ ∈_) (cong (λ { (branch _ xs _) → xs }) (sym ≡b₁)))
  … | x₂ , Rx₁x₂ , x₂∈ , x₂∉ =
    x₂ , Rx₁x₂ , _ , _ , _ , x₂∈ , inj₁ refl , x₂∉
  Alpha-Br-⋆-∈ (b₁≈b₂ ∷ bs₁≈bs₂) x₁∈ (inj₂ ∈bs₁) x₁∉ =
    (Σ-map id $ Σ-map id $ Σ-map id $ Σ-map id $ Σ-map id $ Σ-map id $
     Σ-map inj₂ id) $
    Alpha-Br-⋆-∈ bs₁≈bs₂ x₁∈ ∈bs₁ x₁∉

α-∈ : e₁ ≈α e₂ → x ∈FV e₁ → x ∈FV e₂
α-∈ e₁≈e₂ x∈ with Alpha-∈ e₁≈e₂ x∈
… | x′ , x≡x′ , x′∈ = subst (_∈FV _) (sym x≡x′) x′∈

-- The predicate Closed′ respects α-equivalence.

α-closed′ : e₁ ≈α e₂ → Closed′ xs e₁ → Closed′ xs e₂
α-closed′ e₁≈e₂ cl x x∉ x∈ =
  cl x x∉ (α-∈ (sym-α e₁≈e₂) x∈)

-- The predicate Closed respects α-equivalence.

α-closed : e₁ ≈α e₂ → Closed e₁ → Closed e₂
α-closed = α-closed′

-- Substitution does not necessarily respect α-equivalence.

¬-subst-α :
  ¬ (∀ {x₁ x₂ e₁ e₂ e₁′ e₂′} →
     Alpha (_≡_ [ x₁ ∼ x₂ ]) e₁ e₂ →
     e₁′ ≈α e₂′ →
     e₁ [ x₁ ← e₁′ ] ≈α e₂ [ x₂ ← e₂′ ])
¬-subst-α subst-α =
  not-equal (subst-α e¹≈e² e′≈e′)
  where
  x¹ = V.name 0
  x² = V.name 1
  z  = V.name 2

  e¹ = lambda x¹ (var z)
  e² = lambda x² (var z)

  e′ = var x¹

  e¹≈e² : Alpha (_≡_ [ z ∼ z ]) e¹ e²
  e¹≈e² =
    lambda (var (inj₂ ( V.distinct-codes→distinct-names (λ ())
                      , V.distinct-codes→distinct-names (λ ())
                      , inj₁ (refl , refl)
                      )))

  e′≈e′ : e′ ≈α e′
  e′≈e′ = refl-α

  not-equal : ¬ e¹ [ z ← e′ ] ≈α e² [ z ← e′ ]
  not-equal _ with z V.≟ x¹ | z V.≟ x² | z V.≟ z
  not-equal (lambda (var (inj₁ (_ , x²≡x¹)))) | no _ | no _ | yes _ =
    from-⊎ (1 Nat.≟ 0) (V.name-injective x²≡x¹)
  not-equal (lambda (var (inj₂ (x¹≢x¹ , _)))) | no _ | no _ | yes _ =
    x¹≢x¹ refl
  not-equal _ | yes z≡x¹ | _ | _ =
    from-⊎ (2 Nat.≟ 0) (V.name-injective z≡x¹)
  not-equal _ | _ | yes z≡x² | _ =
    from-⊎ (2 Nat.≟ 1) (V.name-injective z≡x²)
  not-equal _ | _ | _ | no z≢z = z≢z refl

-- TODO: Does substitution of closed terms respect α-equivalence? Does
-- the semantics respect α-equivalence for closed terms?


------------------------------------------------------------------------
-- The Agda standard library
--
-- Natural numbers represented in binary.
------------------------------------------------------------------------

-- This module aims to create an alternative formulation of ℕ that is
-- still reasonably computationally efficient without having to call out
-- to Haskell.

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

module Data.Nat.Binary.Base where

open import Algebra.Core using (Op₂)
open import Data.Nat.Base as ℕ using (ℕ)
open import Data.Sum.Base using (_⊎_)
open import Function using (_on_)
open import Level using (0ℓ)
open import Relation.Binary using (Rel)
open import Relation.Binary.PropositionalEquality using (_≡_)
open import Relation.Nullary using (¬_)

------------------------------------------------------------------------
-- Definition

data ℕᵇ : Set where
  zero   : ℕᵇ
  2[1+_] : ℕᵇ → ℕᵇ    -- n → 2*(1+n) = nonzero even numbers
  1+[2_] : ℕᵇ → ℕᵇ    -- n → 1 + 2*n = odd numbers

------------------------------------------------------------------------
-- Ordering relations

infix 4 _<_ _>_ _≤_ _≮_ _≯_ _≰_ _≱_

data _<_ : Rel ℕᵇ 0ℓ  where
  0<even    : ∀ {x} → zero < 2[1+ x ]
  0<odd     : ∀ {x} → zero < 1+[2 x ]
  even<even : ∀ {x y} → x < y → 2[1+ x ] < 2[1+ y ]
  even<odd  : ∀ {x y} → x < y → 2[1+ x ] < 1+[2 y ]
  odd<even  : ∀ {x y} → x < y ⊎ x ≡ y → 1+[2 x ] < 2[1+ y ]
  odd<odd   : ∀ {x y} → x < y → 1+[2 x ] < 1+[2 y ]
  -- In these constructors "even" stands for nonzero even.

_>_ : Rel ℕᵇ 0ℓ
x > y = y < x

_≤_ : Rel ℕᵇ 0ℓ
x ≤ y = x < y ⊎ x ≡ y

_≥_ : Rel ℕᵇ 0ℓ
x ≥ y = y ≤ x

_≮_ : Rel ℕᵇ 0ℓ
x ≮ y = ¬ (x < y)

_≯_ : Rel ℕᵇ 0ℓ
x ≯ y = ¬ (x > y)

_≰_ : Rel ℕᵇ 0ℓ
x ≰ y = ¬ (x ≤ y)

_≱_ : Rel ℕᵇ 0ℓ
x ≱ y = ¬ (x ≥ y)

------------------------------------------------------------------------
-- Basic operations

double : ℕᵇ → ℕᵇ
double zero     = zero
double 2[1+ x ] = 2[1+ 1+[2 x ] ]
double 1+[2 x ] = 2[1+ (double x) ]

suc : ℕᵇ → ℕᵇ
suc zero     =  1+[2 zero ]
suc 2[1+ x ] =  1+[2 (suc x) ]
suc 1+[2 x ] =  2[1+ x ]

pred : ℕᵇ → ℕᵇ
pred zero     = zero
pred 2[1+ x ] = 1+[2 x ]
pred 1+[2 x ] = double x

------------------------------------------------------------------------
-- Addition, multiplication and certain related functions

infixl 6 _+_
infixl 7 _*_

_+_ : Op₂ ℕᵇ
zero     + y        =  y
x        + zero     =  x
2[1+ x ] + 2[1+ y ] =  2[1+ suc (x + y) ]
2[1+ x ] + 1+[2 y ] =  suc 2[1+ (x + y) ]
1+[2 x ] + 2[1+ y ] =  suc 2[1+ (x + y) ]
1+[2 x ] + 1+[2 y ] =  suc 1+[2 (x + y) ]

_*_ : Op₂ ℕᵇ
zero     * _        =  zero
_        * zero     =  zero
2[1+ x ] * 2[1+ y ] =  double 2[1+ x + (y + x * y) ]
2[1+ x ] * 1+[2 y ] =  2[1+ x + y * 2[1+ x ] ]
1+[2 x ] * 2[1+ y ] =  2[1+ y + x * 2[1+ y ] ]
1+[2 x ] * 1+[2 y ] =  1+[2 x + y * 1+[2 x ] ]

------------------------------------------------------------------------
-- Conversion between ℕᵇ and ℕ

toℕ : ℕᵇ → ℕ
toℕ zero     =  0
toℕ 2[1+ x ] =  2 ℕ.* (ℕ.suc (toℕ x))
toℕ 1+[2 x ] =  ℕ.suc (2 ℕ.* (toℕ x))

-- Costs O(n), could be improved using `_/_` and `_%_`
fromℕ : ℕ → ℕᵇ
fromℕ 0         = zero
fromℕ (ℕ.suc n) = suc (fromℕ n)

-- An alternative ordering lifted from ℕ

infix 4 _<ℕ_

_<ℕ_ :  Rel ℕᵇ 0ℓ
_<ℕ_ =  ℕ._<_ on toℕ

------------------------------------------------------------------------
-- Other functions

-- Useful in some termination proofs.
size : ℕᵇ → ℕ
size zero     = 0
size 2[1+ x ] = ℕ.suc (size x)
size 1+[2 x ] = ℕ.suc (size x)

------------------------------------------------------------------------
-- Constants

0ᵇ = zero
1ᵇ = suc 0ᵇ
2ᵇ = suc 1ᵇ
3ᵇ = suc 2ᵇ
4ᵇ = suc 3ᵇ
5ᵇ = suc 4ᵇ
6ᵇ = suc 5ᵇ
7ᵇ = suc 6ᵇ
8ᵇ = suc 7ᵇ
9ᵇ = suc 8ᵇ


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

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

open import Equality

module Integer {e⁺} (eq : ∀ {a p} → Equality-with-J a p e⁺) where

open Derived-definitions-and-properties eq

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

open import Bijection eq using (_↔_)
open import Equivalence eq as Eq using (_≃_)
open import Function-universe eq hiding (_∘_)
open import Group eq using (Group; Abelian)
open import H-level eq
open import H-level.Closure eq
import Nat eq as Nat

open import Integer.Basics eq public

private
  variable
    i j : ℤ

------------------------------------------------------------------------
-- Some lemmas

-- The sum of i and - i is zero.

+-right-inverse : ∀ i → i + - i ≡ + 0
+-right-inverse = λ where
    (+ zero)    → refl _
    (+ P.suc n) → lemma n
    -[1+ n ]    → lemma n
  where
  lemma : ∀ n → + P.suc n +-[1+ n ] ≡ + 0
  lemma n
    with P.suc n Nat.<= n | T[<=]↔≤ {m = P.suc n} {n = n}
  … | false | _  = cong (+_) $ Nat.∸≡0 n
  … | true  | eq = ⊥-elim $ Nat.<-irreflexive (_↔_.to eq _)

-- The sum of - i and i is zero.

+-left-inverse : ∀ i → - i + i ≡ + 0
+-left-inverse i =
  - i + i  ≡⟨ +-comm (- i) ⟩
  i + - i  ≡⟨ +-right-inverse i ⟩∎
  + 0      ∎

-- + 0 is a left identity for addition.

+-left-identity : + 0 + i ≡ i
+-left-identity {i = + _}      = refl _
+-left-identity {i = -[1+ _ ]} = refl _

-- + 0 is a right identity for addition.

+-right-identity : i + + 0 ≡ i
+-right-identity {i = + _}      = cong (+_) Nat.+-right-identity
+-right-identity {i = -[1+ _ ]} = refl _

-- Addition is associative.

+-assoc : ∀ i {j k} → i + (j + k) ≡ (i + j) + k
+-assoc = λ where
    (+ m) {j = + _} {k = + _} →
      cong (+_) $ Nat.+-assoc m
    -[1+ m ] {j = -[1+ n ]} {k = -[1+ o ]} →
      cong (-[1+_] ∘ P.suc)
        (m ⊕ P.suc (n ⊕ o)  ≡⟨ sym $ Nat.suc+≡+suc m ⟩
         P.suc m ⊕ (n ⊕ o)  ≡⟨ Nat.+-assoc (P.suc m) ⟩∎
         (P.suc m ⊕ n) ⊕ o  ∎)
    -[1+ m ] {j = + n} {k = + o} →
      sym $ ++-[1+]++ _ n _
    (+ m) {j = -[1+ n ]} {k = -[1+ o ]} →
      sym $ ++-[1+]+-[1+] m _ _
    (+ m) {j = + n} {k = -[1+ o ]} →
      lemma₁ m _ _
    (+ m) {j = -[1+ n ]} {k = + o} →
      lemma₂ m _ _
    -[1+ m ] {j = -[1+ n ]} {k = + o} →
      lemma₃ _ o _
    -[1+ m ] {j = + n} {k = -[1+ o ]} →
      lemma₄ _ n _
  where
  ++-[1+]++ : ∀ m n o → + n +-[1+ m ] + + o ≡ (+ (n ⊕ o) +-[1+ m ])
  ++-[1+]++ m         zero      o = refl _
  ++-[1+]++ zero      (P.suc n) o = refl _
  ++-[1+]++ (P.suc m) (P.suc n) o = ++-[1+]++ m n o

  ++-[1+]+-[1+] :
    ∀ m n o → + m +-[1+ n ] + -[1+ o ] ≡ (+ m +-[1+ 1 ⊕ n ⊕ o ])
  ++-[1+]+-[1+] zero      n         o = refl _
  ++-[1+]+-[1+] (P.suc m) zero      o = refl _
  ++-[1+]+-[1+] (P.suc m) (P.suc n) o = ++-[1+]+-[1+] m n o

  lemma₁ : ∀ m n o → + m + (+ n +-[1+ o ]) ≡ (+ m ⊕ n +-[1+ o ])
  lemma₁ m n o =
    + m + (+ n +-[1+ o ])  ≡⟨ +-comm (+ m) {j = + n +-[1+ o ]} ⟩
    (+ n +-[1+ o ]) + + m  ≡⟨ ++-[1+]++ _ n _ ⟩
    (+ n ⊕ m +-[1+ o ])    ≡⟨ cong +_+-[1+ o ] $ Nat.+-comm n ⟩∎
    (+ m ⊕ n +-[1+ o ])    ∎

  lemma₂ : ∀ m n o → + m + (+ n +-[1+ o ]) ≡ (+ m +-[1+ o ]) + + n
  lemma₂ m n o =
    + m + (+ n +-[1+ o ])  ≡⟨ lemma₁ m n o ⟩
    (+ m ⊕ n +-[1+ o ])    ≡⟨ sym $ ++-[1+]++ _ m _ ⟩∎
    (+ m +-[1+ o ]) + + n  ∎

  lemma₃ :
    ∀ m n o → -[1+ m ] + (+ n +-[1+ o ]) ≡ (+ n +-[1+ 1 ⊕ m ⊕ o ])
  lemma₃ m n o =
    -[1+ m ] + (+ n +-[1+ o ])  ≡⟨ +-comm -[1+ m ] {j = + n +-[1+ o ]} ⟩
    (+ n +-[1+ o ]) + -[1+ m ]  ≡⟨ ++-[1+]+-[1+] n o m ⟩
    (+ n +-[1+ 1 ⊕ o ⊕ m ])     ≡⟨ cong + n +-[1+_] $ cong (1 ⊕_) $ Nat.+-comm o ⟩∎
    (+ n +-[1+ 1 ⊕ m ⊕ o ])     ∎

  lemma₄ :
    ∀ m n o → -[1+ m ] + (+ n +-[1+ o ]) ≡ (+ n +-[1+ m ]) + -[1+ o ]
  lemma₄ m n o =
    -[1+ m ] + (+ n +-[1+ o ])  ≡⟨ lemma₃ m n o ⟩
    (+ n +-[1+ 1 ⊕ m ⊕ o ])     ≡⟨ sym $ ++-[1+]+-[1+] n _ _ ⟩∎
    (+ n +-[1+ m ]) + -[1+ o ]  ∎

------------------------------------------------------------------------
-- Successor and predecessor

-- The successor function.

suc : ℤ → ℤ
suc (+ n)          = + P.suc n
suc -[1+ zero ]    = + zero
suc -[1+ P.suc n ] = -[1+ n ]

-- The successor function adds one to its input.

suc≡1+ : ∀ i → suc i ≡ + 1 + i
suc≡1+ (+ _)          = refl _
suc≡1+ -[1+ zero ]    = refl _
suc≡1+ -[1+ P.suc _ ] = refl _

-- The predecessor function.

pred : ℤ → ℤ
pred (+ zero)    = -[1+ zero ]
pred (+ P.suc n) = + n
pred -[1+ n ]    = -[1+ P.suc n ]

-- The predecessor function adds minus one to its input.

pred≡-1+ : ∀ i → pred i ≡ -[ 1 ] + i
pred≡-1+ (+ zero)    = refl _
pred≡-1+ (+ P.suc _) = refl _
pred≡-1+ -[1+ _ ]    = refl _

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

successor : ℤ ≃ ℤ
successor = Eq.↔→≃ suc pred suc-pred pred-suc
  where
  suc-pred : ∀ i → suc (pred i) ≡ i
  suc-pred (+ zero)    = refl _
  suc-pred (+ P.suc _) = refl _
  suc-pred -[1+ _ ]    = refl _

  pred-suc : ∀ i → pred (suc i) ≡ i
  pred-suc (+ _)          = refl _
  pred-suc -[1+ zero ]    = refl _
  pred-suc -[1+ P.suc _ ] = refl _

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

-- The property of being positive.

Positive : ℤ → Type
Positive (+ zero)    = ⊥
Positive (+ P.suc _) = ⊤
Positive -[1+ _ ]    = ⊥

-- Positive is propositional.

Positive-propositional : Is-proposition (Positive i)
Positive-propositional {i = + zero}    = ⊥-propositional
Positive-propositional {i = + P.suc _} = mono₁ 0 ⊤-contractible
Positive-propositional {i = -[1+ _ ]}  = ⊥-propositional

-- The property of being negative.

Negative : ℤ → Type
Negative (+ _)    = ⊥
Negative -[1+ _ ] = ⊤

-- Negative is propositional.

Negative-propositional : Is-proposition (Negative i)
Negative-propositional {i = + _}      = ⊥-propositional
Negative-propositional {i = -[1+ _ ]} = mono₁ 0 ⊤-contractible

-- No integer is both positive and negative.

¬+- : Positive i → Negative i → ⊥₀
¬+- {i = + _}      _   neg = neg
¬+- {i = -[1+ _ ]} pos _   = pos

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

¬+0 : Positive i → i ≡ + 0 → ⊥₀
¬+0 {i = + zero}    pos _  = pos
¬+0 {i = + P.suc _} _   ≡0 = Nat.0≢+ $ sym $ +-cancellative ≡0
¬+0 {i = -[1+ _ ]}  pos _  = pos

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

¬-0 : Negative i → i ≡ + 0 → ⊥₀
¬-0 {i = + _}      neg _  = neg
¬-0 {i = -[1+ _ ]} _   ≡0 = +≢-[1+] $ sym ≡0

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

-⊎0⊎+ : ∀ i → Negative i ⊎ i ≡ + 0 ⊎ Positive i
-⊎0⊎+ (+ zero)    = inj₂ (inj₁ (refl _))
-⊎0⊎+ (+ P.suc _) = inj₂ (inj₂ _)
-⊎0⊎+ -[1+ _ ]    = inj₁ _

-- 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 (+ P.suc _) (+ P.suc _) _ _ = _

-- 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 -[1+ _ ] -[1+ _ ] _ _ = _

------------------------------------------------------------------------
-- 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.left-identity _  = +-left-identity
ℤ-group .Group.right-identity _ = +-right-identity
ℤ-group .Group.assoc i _ _      = +-assoc i
ℤ-group .Group.right-inverse    = +-right-inverse
ℤ-group .Group.left-inverse     = +-left-inverse

-- The group of integers is abelian.

ℤ-abelian : Abelian ℤ-group
ℤ-abelian i _ = +-comm i

private
  module ℤG = Group ℤ-group
open ℤG public
  using ()
  renaming (_^+_ to infixl 7 _*+_;
            _^_  to infixl 7 _*_)

-- + 1 is a left identity for multiplication.

*-left-identity : ∀ i → + 1 * i ≡ i
*-left-identity = lemma
  where
  +lemma : ∀ n → + 1 *+ n ≡ + n
  +lemma zero      = refl _
  +lemma (P.suc n) =
    + 1 + (+ 1) *+ n  ≡⟨ cong (λ i → + 1 + i) $ +lemma n ⟩
    + 1 + + n         ≡⟨⟩
    + P.suc n         ∎

  -lemma : ∀ n → -[ 1 ] *+ n ≡ -[ n ]
  -lemma zero              = refl _
  -lemma (P.suc zero)      = refl _
  -lemma (P.suc (P.suc n)) =
    -[ 1 ] + -[ 1 ] *+ P.suc n  ≡⟨ cong (λ i → -[ 1 ] + i) $ -lemma (P.suc n) ⟩
    -[ 1 ] + -[ P.suc n ]       ≡⟨⟩
    -[ P.suc (P.suc n) ]        ∎

  lemma : ∀ i → + 1 * i ≡ i
  lemma (+ n)    = +lemma n
  lemma -[1+ n ] = -lemma (P.suc n)

-- _*+ n distributes over addition.

*+-distrib-+ : ∀ n → (i + j) *+ n ≡ i *+ n + j *+ n
*+-distrib-+ {i = i} n = ℤG.∘^+≡^+∘^+ (+-comm i) n

-- If a positive number is multiplied by a positive number, then
-- the result is positive.

>0→*+suc> : ∀ i m → Positive i → Positive (i *+ P.suc m)
>0→*+suc> i zero =
  Positive i          ↝⟨ subst Positive (sym $ ℤG.right-identity i) ⟩
  Positive (i + + 0)  ↔⟨⟩
  Positive (i *+ 1)   □
>0→*+suc> i (P.suc m) =
  Positive i                            ↝⟨ (λ p → p , >0→*+suc> i m p) ⟩
  Positive i × Positive (i *+ P.suc m)  ↝⟨ uncurry (>0→>0→+>0 i (i *+ P.suc m)) ⟩
  Positive (i + i *+ P.suc m)           ↔⟨⟩
  Positive (i *+ P.suc (P.suc m))       □

-- If a negative number is multiplied by a positive number, then
-- the result is negative.

<0→*+suc<0 : ∀ i m → Negative i → Negative (i *+ P.suc m)
<0→*+suc<0 i zero =
  Negative i          ↝⟨ subst Negative (sym $ ℤG.right-identity i) ⟩
  Negative (i + + 0)  ↔⟨⟩
  Negative (i *+ 1)   □
<0→*+suc<0 i (P.suc m) =
  Negative i                            ↝⟨ (λ p → p , <0→*+suc<0 i m p) ⟩
  Negative i × Negative (i *+ P.suc m)  ↝⟨ uncurry (<0→<0→+<0 i (i *+ P.suc m)) ⟩
  Negative (i + i *+ P.suc m)           ↔⟨⟩
  Negative (i *+ P.suc (P.suc m))       □

------------------------------------------------------------------------
-- Integer division by two

-- Division by two, rounded downwards.

⌊_/2⌋ : ℤ → ℤ
⌊ + n      /2⌋ = + Nat.⌊ n /2⌋
⌊ -[1+ n ] /2⌋ = -[ Nat.⌈ P.suc n /2⌉ ]

-- A kind of distributivity property for ⌊_/2⌋ and _+_.

⌊+*+2/2⌋≡ : ∀ i {j} → ⌊ i + j *+ 2 /2⌋ ≡ ⌊ i /2⌋ + j
⌊+*+2/2⌋≡ = λ where
    (+ m) {j = + n} → cong +_
      (Nat.⌊ m ⊕ 2 ⊛ n /2⌋  ≡⟨ cong Nat.⌊_/2⌋ $ Nat.+-comm m ⟩
       Nat.⌊ 2 ⊛ n ⊕ m /2⌋  ≡⟨ Nat.⌊2*+/2⌋≡ n ⟩
       n ⊕ Nat.⌊ m /2⌋      ≡⟨ Nat.+-comm n ⟩∎
       Nat.⌊ m /2⌋ ⊕ n      ∎)

    -[1+ zero ] {j = -[1+ n ]} → cong -[1+_]
      (Nat.⌈ P.suc (n ⊕ n) /2⌉            ≡⟨ cong (Nat.⌈_/2⌉ ∘ P.suc) $ ⊕-lemma n ⟩
       Nat.⌈ 1 ⊕ 2 ⊛ n /2⌉                ≡⟨ cong Nat.⌈_/2⌉ $ Nat.+-comm 1 ⟩
       Nat.⌈ 2 ⊛ n ⊕ 1 /2⌉                ≡⟨ Nat.⌈2*+/2⌉≡ n ⟩
       n ⊕ Nat.⌈ 1 /2⌉                    ≡⟨ Nat.+-comm n ⟩∎
       P.suc n                            ∎)

    -[1+ P.suc m ] {j = -[1+ n ]} → cong -[1+_]
      (Nat.⌈ P.suc m ⊕ P.suc (n ⊕ n) /2⌉  ≡⟨ cong (Nat.⌈_/2⌉ ∘ P.suc) $ sym $ Nat.suc+≡+suc m ⟩
       P.suc Nat.⌈ m ⊕ (n ⊕ n) /2⌉        ≡⟨ cong (P.suc ∘ Nat.⌈_/2⌉ ∘ (m ⊕_)) $ ⊕-lemma n ⟩
       P.suc Nat.⌈ m ⊕ 2 ⊛ n /2⌉          ≡⟨ cong (P.suc ∘ Nat.⌈_/2⌉) $ Nat.+-comm m ⟩
       P.suc Nat.⌈ 2 ⊛ n ⊕ m /2⌉          ≡⟨ cong P.suc $ Nat.⌈2*+/2⌉≡ n ⟩
       P.suc (n ⊕ Nat.⌈ m /2⌉)            ≡⟨ cong P.suc $ Nat.+-comm n ⟩∎
       P.suc (Nat.⌈ m /2⌉ ⊕ n)            ∎)

    (+ m) {j = -[1+ n ]} →
      ⌊ + m +-[1+ P.suc (n ⊕ n) ] /2⌋  ≡⟨ cong (λ n → ⌊ + m +-[1+ P.suc n ] /2⌋) $ ⊕-lemma n ⟩
      ⌊ + m +-[1+ P.suc (2 ⊛ n) ] /2⌋  ≡⟨ lemma₁ m n ⟩∎
      + Nat.⌊ m /2⌋ +-[1+ n ]          ∎

    -[1+ 0 ] {j = + 0} →
      -[ 1 ]  ≡⟨⟩
      -[ 1 ]  ∎

    -[1+ 0 ] {j = + P.suc n} → cong +_
      (Nat.⌊ n ⊕ P.suc (n ⊕ 0) /2⌋  ≡⟨ cong Nat.⌊_/2⌋ $ sym $ Nat.suc+≡+suc n ⟩
       Nat.⌊ 1 ⊕ 2 ⊛ n /2⌋          ≡⟨ Nat.⌊1+2*/2⌋≡ n ⟩∎
       n                            ∎)

    -[1+ P.suc m ] {j = + n} →
      ⌊ + 2 ⊛ n +-[1+ P.suc m ] /2⌋  ≡⟨ lemma₂ m n ⟩∎
      + n +-[1+ Nat.⌈ m /2⌉ ]        ∎
  where
  ⊕-lemma : ∀ n → n ⊕ n ≡ 2 ⊛ n
  ⊕-lemma n = cong (n ⊕_) $ sym Nat.+-right-identity

  lemma₁ :
    ∀ m n →
    ⌊ + m +-[1+ P.suc (2 ⊛ n) ] /2⌋ ≡
    + Nat.⌊ m /2⌋ +-[1+ n ]
  lemma₁ zero n = cong -[1+_]
    (Nat.⌈ 2 ⊛ n /2⌉  ≡⟨ Nat.⌈2*/2⌉≡ n ⟩∎
     n                ∎)
  lemma₁ (P.suc zero) n =
    -[ Nat.⌈ 1 ⊕ 2 ⊛ n /2⌉ ]  ≡⟨ cong -[_] $ Nat.⌈1+2*/2⌉≡ n ⟩∎
    -[1+ n ]                  ∎
  lemma₁ (P.suc (P.suc m)) zero =
    ⌊ + m /2⌋      ≡⟨⟩
    + Nat.⌊ m /2⌋  ∎
  lemma₁ (P.suc (P.suc m)) (P.suc n) =
    ⌊ + m +-[1+ n ⊕ P.suc (n ⊕ 0) ] /2⌋  ≡⟨ cong (⌊_/2⌋ ∘ + m +-[1+_]) $ sym $ Nat.suc+≡+suc n ⟩
    ⌊ + m +-[1+ P.suc (2 ⊛ n) ] /2⌋      ≡⟨ lemma₁ m n ⟩∎
    + Nat.⌊ m /2⌋ +-[1+ n ]              ∎

  mutual

    lemma₂ :
      ∀ m n →
      ⌊ + 2 ⊛ n +-[1+ P.suc m ] /2⌋ ≡
      + n +-[1+ Nat.⌈ m /2⌉ ]
    lemma₂ m zero =
      ⌊ -[1+ P.suc m ] /2⌋  ≡⟨⟩
      -[1+ Nat.⌈ m /2⌉ ]    ∎
    lemma₂ m (P.suc n) =
      ⌊ + n ⊕ P.suc (n ⊕ 0) +-[1+ m ] /2⌋  ≡⟨ cong (⌊_/2⌋ ∘ +_+-[1+ m ]) $ sym $ Nat.suc+≡+suc n ⟩
      ⌊ + P.suc (2 ⊛ n) +-[1+ m ] /2⌋      ≡⟨ lemma₃ m n ⟩∎
      + P.suc n +-[1+ Nat.⌈ m /2⌉ ]        ∎

    lemma₃ :
      ∀ m n →
      ⌊ + P.suc (2 ⊛ n) +-[1+ m ] /2⌋ ≡
      + P.suc n +-[1+ Nat.⌈ m /2⌉ ]
    lemma₃ zero n = cong +_
      (Nat.⌊ 2 ⊛ n /2⌋  ≡⟨ Nat.⌊2*/2⌋≡ n ⟩∎
       n                ∎)
    lemma₃ (P.suc zero) zero =
      -[ 1 ]  ≡⟨⟩
      -[ 1 ]  ∎
    lemma₃ (P.suc zero) (P.suc n) = cong +_
      (Nat.⌊ n ⊕ P.suc (n ⊕ zero) /2⌋  ≡⟨ cong Nat.⌊_/2⌋ $ sym $ Nat.suc+≡+suc n ⟩
       Nat.⌊ 1 ⊕ 2 ⊛ n /2⌋             ≡⟨ Nat.⌊1+2*/2⌋≡ n ⟩∎
       n                               ∎)
    lemma₃ (P.suc (P.suc m)) n =
      ⌊ + 2 ⊛ n +-[1+ P.suc m ] /2⌋  ≡⟨ lemma₂ m n ⟩∎
      + n +-[1+ Nat.⌈ m /2⌉ ]        ∎

-- If you double and then halve an integer, then you get back what you
-- started with.

⌊*+2/2⌋≡ : ∀ i → ⌊ i *+ 2 /2⌋ ≡ i
⌊*+2/2⌋≡ i =
  ⌊ i *+ 2 /2⌋        ≡⟨ cong ⌊_/2⌋ $ sym $ +-left-identity {i = i *+ 2} ⟩
  ⌊ + 0 + i *+ 2 /2⌋  ≡⟨ ⌊+*+2/2⌋≡ (+ 0) {j = i} ⟩
  ⌊ + 0 /2⌋ + i       ≡⟨⟩
  + 0 + i             ≡⟨ +-left-identity ⟩∎
  i                   ∎


-- Andreas, 2018-10-16, runtime erasure
--
-- Should not be able to extract erased constructor field.

open import Agda.Builtin.Nat

data Vec (A : Set) : Nat → Set where
  [] : Vec A zero
  _∷_ : {@0 n : Nat} (x : A) (xs : Vec A n) → Vec A (suc n)

length : ∀{A} {@0 n} (x : Vec A n) → Nat
length [] = zero
length (_∷_ {n} _ _) = suc n

-- Expected error:
--
-- Variable n is declared erased, so it cannot be used here
-- when checking that the expression n has type Nat


{-# OPTIONS --without-K --exact-split --allow-unsolved-metas #-}

module SAT-solver where

import 14-univalence
open 14-univalence public

{- The literates are atomic propositions or negations thereof. We simply use
   the natural numbers to encode the atomic propositions, and we use the
   booleans to tell whether it should be negated or not. -}

Literate = ℕ × bool

neg-Literate : Literate → Literate
neg-Literate (pair n b) = pair n (neg-𝟚 b)

zero-true-Literate : Literate
zero-true-Literate = pair zero-ℕ true

truth-value-Literate : (ℕ → bool) → Literate → bool
truth-value-Literate f (pair n true) = f n
truth-value-Literate f (pair n false) = neg-𝟚 (f n)

bool-Eq-ℕ : ℕ → ℕ → bool
bool-Eq-ℕ zero-ℕ zero-ℕ = true
bool-Eq-ℕ zero-ℕ (succ-ℕ n) = false
bool-Eq-ℕ (succ-ℕ m) zero-ℕ = false
bool-Eq-ℕ (succ-ℕ m) (succ-ℕ n) = bool-Eq-ℕ m n

bool-Eq-𝟚 : bool → bool → bool
bool-Eq-𝟚 true true = true
bool-Eq-𝟚 true false = false
bool-Eq-𝟚 false true = false
bool-Eq-𝟚 false false = true

bool-Eq-Literate : Literate → Literate → bool
bool-Eq-Literate (pair m x) (pair n y) =
  conjunction-𝟚 (bool-Eq-ℕ m n) (bool-Eq-𝟚 x y)

{- Clauses are finite disjunctions of literates. We simply encode them using
   lists of literates. -}

Clause = list Literate

clause-Literate : Literate → Clause
clause-Literate l = cons l nil

is-literate-Clause : Clause → UU lzero
is-literate-Clause c = Σ Literate (λ l → Id (clause-Literate l) c)

zero-true-Clause = clause-Literate zero-true-Literate

truth-value-Clause : (ℕ → bool) → Clause → bool
truth-value-Clause f nil = false
truth-value-Clause f (cons l c) =
  disjunction-𝟚 (truth-value-Literate f l) (truth-value-Clause f c)

contains-Clause : Literate → Clause → bool
contains-Clause l nil = false
contains-Clause l (cons l' c) =
  disjunction-𝟚 (bool-Eq-Literate l l') (contains-Clause l c)

if-then-else : {l : Level} {A : UU l} → bool → A → A → A
if-then-else true x y = x
if-then-else false x y = y

if-then-else-dep :
  {l1 l2 : Level} {A : UU l1} (B : A → UU l2)
  (b : bool) {x y : A} (u : Id b true → B x) (v : Id b false → B y) →
  B (if-then-else b x y)
if-then-else-dep B true u v = u refl
if-then-else-dep B false u v = v refl

{- We write a function that simplifies a clause in the following way:
    * simplified clauses do not contain any literate more than once.
    * if a simplified clause contains a literate and its negation, then it is
      of the form l ∨ ¬l. -}

simplify-Clause : Clause → Clause
simplify-Clause nil = nil
simplify-Clause (cons l c) =
  if-then-else
    ( contains-Clause (neg-Literate l) c)
    ( cons l (cons (neg-Literate l) nil))
    ( if-then-else
      ( contains-Clause l c)
      ( simplify-Clause c)
      ( cons l (simplify-Clause c)))

is-satisfiable-Clause : Clause → UU lzero
is-satisfiable-Clause c = Σ (ℕ → bool) (λ f → Id (truth-value-Clause f c) true)

{- We show that any non-empty disjunctive clause is satisfiable. -}

right-true-law-disjunction-𝟚 : (b : bool) → Id (disjunction-𝟚 b true) true
right-true-law-disjunction-𝟚 true = refl
right-true-law-disjunction-𝟚 false = refl

left-true-law-disjunction-𝟚 : (b : bool) → Id (disjunction-𝟚 true b) true
left-true-law-disjunction-𝟚 true = refl
left-true-law-disjunction-𝟚 false = refl

is-satisfiable-cons-Clause :
  ( l : Literate) (c : Clause) → is-satisfiable-Clause (cons l c)
is-satisfiable-cons-Clause (pair n true) c =
  pair
    ( const ℕ bool true)
    ( left-true-law-disjunction-𝟚 (truth-value-Clause (const ℕ bool true) c))
is-satisfiable-cons-Clause (pair n false) c =
  pair
    ( const ℕ bool false)
    ( left-true-law-disjunction-𝟚 (truth-value-Clause (const ℕ bool false) c))

{- Formulas in conjunctive normal form are finite conjunctions of clauses, as
   defined above. Therefore we encode them as lists of clauses. -}

CNF = list Clause

truth-value-CNF : (ℕ → bool) → CNF → bool
truth-value-CNF f nil = true
truth-value-CNF f (cons c φ) =
  conjunction-𝟚 (truth-value-Clause f c) (truth-value-CNF f φ)

{- A formula φ is in conjunctive normal form is said to be satisfiable if there
   is a map ℕ → bool which evaluates φ to true. -}

is-satisfiable-CNF : CNF → UU lzero
is-satisfiable-CNF φ = Σ (ℕ → bool) (λ f → Id (truth-value-CNF f φ) true)

contains-CNF : (l : Literate) (φ : CNF) → bool
contains-CNF l nil = false
contains-CNF l (cons c φ) =
  disjunction-𝟚 (contains-Clause l c) (contains-CNF l φ)

contains

simplify-CNF : CNF → CNF
simplify-CNF nil = nil
simplify-CNF (cons c φ) = cons (simplify-Clause c) (simplify-CNF φ)


import      Lvl
open import Type

module Structure.Logic.Constructive.Propositional {ℓₗ} {Formula : Type{ℓₗ}} {ℓₘₗ} (Proof : Formula → Type{ℓₘₗ}) where

open import Logic.Predicate

private variable X Y Z : Formula

-- Rules of bottom (falsity).
record Bottom(⊥ : Formula) : Type{ℓₘₗ Lvl.⊔ ℓₗ} where
  field
    elim : Proof(⊥) → Proof(X)
⊥ = \ ⦃([∃]-intro ▫) : ∃(Bottom)⦄ → ▫
module ⊥ {▫} ⦃ p ⦄ = Bottom {▫} p

-- Rules of top (truth).
record Top(⊤ : Formula) : Type{ℓₘₗ Lvl.⊔ ℓₗ} where
  field
    intro : Proof(⊤)
⊤ = \ ⦃([∃]-intro ▫) : ∃(Top)⦄ → ▫
module ⊤ {▫} ⦃ p ⦄ = Top {▫} p

-- Rules of conjunction (and).
record Conjunction(_∧_ : Formula → Formula → Formula) : Type{ℓₘₗ Lvl.⊔ ℓₗ} where
  field
    intro : Proof(X) → Proof(Y) → Proof(X ∧ Y)
    elimₗ : Proof(X ∧ Y) → Proof(X)
    elimᵣ : Proof(X ∧ Y) → Proof(Y)
_∧_ = \ ⦃([∃]-intro ▫) : ∃(Conjunction)⦄ → ▫
module ∧ {▫} ⦃ p ⦄ = Conjunction {▫} p

-- Rules of implication.
record Implication(_⟶_ : Formula → Formula → Formula) : Type{ℓₘₗ Lvl.⊔ ℓₗ} where
  field
    intro : (Proof(X) → Proof(Y)) → Proof(X ⟶ Y)
    elim  : Proof(X ⟶ Y) → Proof(X) → Proof(Y)
_⟶_ = \ ⦃([∃]-intro ▫) : ∃(Implication)⦄ → ▫
module ⟶ {▫} ⦃ p ⦄ = Implication {▫} p

-- Rules of reversed implication.
record Consequence(_⟵_ : Formula → Formula → Formula) : Type{ℓₘₗ Lvl.⊔ ℓₗ} where
  field
    intro : (Proof(X) → Proof(Y)) → Proof(Y ⟵ X)
    elim  : Proof(Y ⟵ X) → Proof(X) → Proof(Y)
_⟵_ = \ ⦃([∃]-intro ▫) : ∃(Consequence)⦄ → ▫
module ⟵ {▫} ⦃ p ⦄ = Consequence {▫} p

-- Rules of equivalence.
record Equivalence(_⟷_ : Formula → Formula → Formula) : Type{ℓₘₗ Lvl.⊔ ℓₗ} where
  field
    intro  : let _ = X in (Proof(Y) → Proof(X)) → (Proof(X) → Proof(Y)) → Proof(X ⟷ Y)
    elimₗ  : Proof(X ⟷ Y) → Proof(Y) → Proof(X)
    elimᵣ  : Proof(X ⟷ Y) → Proof(X) → Proof(Y)
_⟷_ = \ ⦃([∃]-intro ▫) : ∃(Equivalence)⦄ → ▫
module ⟷ {▫} ⦃ p ⦄ = Equivalence {▫} p

-- Rules of disjunction (or).
record Disjunction(_∨_  : Formula → Formula → Formula) : Type{ℓₘₗ Lvl.⊔ ℓₗ} where
  field
    introₗ : Proof(X) → Proof(X ∨ Y)
    introᵣ : Proof(Y) → Proof(X ∨ Y)
    elim   : (Proof(X) → Proof(Z)) → (Proof(Y) → Proof(Z)) → Proof(X ∨ Y) → Proof(Z)
_∨_ = \ ⦃([∃]-intro ▫) : ∃(Disjunction)⦄ → ▫
module ∨ {▫} ⦃ p ⦄ = Disjunction {▫} p

-- Rules of negation (not).
record Negation(¬_ : Formula → Formula) : Type{ℓₘₗ Lvl.⊔ ℓₗ} where
  field
    intro : (Proof(X) → Proof(Y)) → (Proof(X) → Proof(¬ Y)) → Proof(¬ X)
    elim  : Proof(X) → Proof(¬ X) → Proof(Y)
¬_ = \ ⦃([∃]-intro ▫) : ∃(Negation)⦄ → ▫
module ¬ {▫} ⦃ p ⦄ = Negation {▫} p

record Logic : Type{ℓₘₗ Lvl.⊔ ℓₗ} where
  field
    ⦃ bottom ⦄      : ∃(Bottom)
    ⦃ top ⦄         : ∃(Top)
    ⦃ conjunction ⦄ : ∃(Conjunction)
    ⦃ implication ⦄ : ∃(Implication)
    ⦃ consequence ⦄ : ∃(Consequence)
    ⦃ equivalence ⦄ : ∃(Equivalence)
    ⦃ disjunction ⦄ : ∃(Disjunction)
    ⦃ negation ⦄    : ∃(Negation)


module Control.Monad.Writer where

open import Prelude
open import Control.Monad.Zero
open import Control.Monad.Identity
open import Control.Monad.Transformer

record WriterT {a} (W : Set a) (M : Set a → Set a) (A : Set a) : Set a where
  no-eta-equality
  constructor writerT
  field runWriterT : M (A × W)

open WriterT public

module _ {a} {W : Set a} {M : Set a → Set a} where

  instance
    FunctorWriterT : {{_ : Functor M}} → Functor {a = a} (WriterT W M)
    runWriterT (fmap {{ FunctorWriterT }} f m) = first f <$> runWriterT m

    FunctorZeroWriterT : {{_ : FunctorZero M}} → FunctorZero {a = a} (WriterT W M)
    runWriterT (empty {{FunctorZeroWriterT}}) = empty

    AlternativeWriterT : {{_ : Alternative M}} → Alternative {a = a} (WriterT W M)
    runWriterT (_<|>_ {{AlternativeWriterT}} x y) = runWriterT x <|> runWriterT y

  module _ {{_ : Monoid W}} {{_ : Monad M}} where

    private
      bindWriterT : ∀ {A B} → WriterT W M A → (A → WriterT W M B) → WriterT W M B
      runWriterT (bindWriterT m f) = do
        (x , w₁) ← runWriterT m
        (y , w₂) ← (runWriterT (f x))
        return (y , w₁ <> w₂)

    instance
      ApplicativeWriterT : Applicative {a = a} (WriterT W M)
      runWriterT (pure {{ApplicativeWriterT}} x) = return (x , mempty)
      _<*>_ {{ApplicativeWriterT}} = monadAp bindWriterT

      MonadWriterT : Monad {a = a} (WriterT W M)
      _>>=_  {{MonadWriterT}} = bindWriterT

    liftWriterT : {A : Set a} → M A → WriterT W M A
    runWriterT (liftWriterT m) = (_, mempty) <$> m

    writer : {A : Set a} → A × W → WriterT W M A
    runWriterT (writer (x , w)) = return (x , w)

    tell : W → WriterT W M ⊤′
    runWriterT (tell w) = return (tt , w)

    listens : {A B : Set a} → (W → B) → WriterT W M A → WriterT W M (A × B)
    runWriterT (listens f m) = do
      (x , w) ← runWriterT m
      return ((x , f w) , w)

    listen : {A : Set a} → WriterT W M A → WriterT W M (A × W)
    listen = listens id

    pass : {A : Set a} → WriterT W M (A × (W → W)) → WriterT W M A
    runWriterT (pass m) = do
      ((x , f) , w) ← runWriterT m
      return (x , f w)

    censor : {A : Set a} → (W → W) → WriterT W M A → WriterT W M A
    censor f m = pass ((_, f) <$> m)

instance
  TransformerWriterT : ∀ {a} {W : Set a} {{_ : Monoid W}} → Transformer (WriterT W)
  lift {{TransformerWriterT}} = liftWriterT

Writer : ∀ {a} (W : Set a) (A : Set a) → Set a
Writer W = WriterT W Identity

runWriter : ∀ {a} {W : Set a} {A : Set a} → Writer W A → A × W
runWriter m = runIdentity (runWriterT m)

execWriter : ∀ {a} {W : Set a} {A : Set a} → Writer W A → W
execWriter = snd ∘ runWriter


module Cats.Category.Fun.Facts where

open import Cats.Category
open import Cats.Category.Cat using (_≈_)
open import Cats.Category.Fun using (Trans ; Fun ; ≈-intro ; ≈-elim)
open import Cats.Functor using (Functor)
open import Cats.Trans.Iso using (NatIso ; iso ; forth-natural ; back-natural)

open import Level using (_⊔_)

open Functor
open Trans
open Category._≅_

module _ {lo la l≈ lo′ la′ l≈′}
  {C : Category lo la l≈}
  {D : Category lo′ la′ l≈′}
  {F G : Functor C D}
  where

  private
    module C = Category C
    module D = Category D
    open D.≈-Reasoning
    open Category (Fun C D) using (_≅_)

  NatIso→≅ : NatIso F G → F ≅ G
  NatIso→≅ i = record
      { forth = Forth i
      ; back = Back i
      ; back-forth = ≈-intro (back-forth (iso i))
      ; forth-back = ≈-intro (forth-back (iso i))
      }
    where
      open NatIso

  ≅→NatIso : F ≅ G → NatIso F G
  ≅→NatIso i = record
      { iso = λ {c} → record
          { forth = component (forth i) c
          ; back = component (back i) c
          ; back-forth = ≈-elim (back-forth i)
          ; forth-back = ≈-elim (forth-back i)
          }
      ; forth-natural = natural (forth i)
      }

  ≈→≅ : F ≈ G → F ≅ G
  ≈→≅ eq = NatIso→≅ eq

  ≅→≈ : F ≅ G → F ≈ G
  ≅→≈ i = ≅→NatIso i