------------------------------------------------------------------------
-- The Agda standard library
--
-- The type for booleans and some operations
------------------------------------------------------------------------

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

module Data.Bool.Base where

open import Data.Unit.Base using (⊤)
open import Data.Empty
open import Relation.Nullary

infixr 6 _∧_
infixr 5 _∨_ _xor_
infix  0 if_then_else_

------------------------------------------------------------------------
-- The boolean type

open import Agda.Builtin.Bool public

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

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

-- A function mapping true to an inhabited type and false to an empty
-- type.

T : Bool → Set
T true  = ⊤
T false = ⊥

if_then_else_ : ∀ {a} {A : Set a} → Bool → A → A → A
if true  then t else f = t
if false then t else f = f

_∧_ : Bool → Bool → Bool
true  ∧ b = b
false ∧ b = false

_∨_ : Bool → Bool → Bool
true  ∨ b = true
false ∨ b = b

_xor_ : Bool → Bool → Bool
true  xor b = not b
false xor b = b


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

{- Here, we derive our main theorem: there is a type in the n-th universe
   that is not an n-type, implying the n-th universe is not n-truncated.
   The n-th universe restricted to n-types is hence a 'strict' n-type.
   For this, we first derive local-global looping in a modular way.

   A technical point worth noting is that Agda does not implement
   cumulative universes. Since that means the crucial steps in our
   transformations (where we pass between universes uning univalence)
   can not be expressed using equality without resorting to explicit lifting,
   we decide to explicitely uses equivalences (and pointed equivalences,
   respectively) instead where possible.
   As a drawback, we have to use lemmata showing preservation of
   (pointed) equivalences of various (pointed) type constructions,
   a parametricity property derived for free
   from univalence-induced equalities. -}
module Universe.Hierarchy where

open import lib.Basics
open import lib.Equivalences2
open import lib.NType2
open import lib.types.Bool
open import lib.types.Nat hiding (_+_)
open import lib.types.Paths
open import lib.types.Sigma
open import lib.types.TLevel

open import Universe.Utility.General
open import Universe.Utility.Pointed
open import Universe.Utility.TruncUniverse

-- *** Lemma 5.2 ***
{- Our Local-global looping principle.
   We would like to state this principle in the form of
     Ωⁿ⁺¹ (Type i , A) == ∀• a → Ωⁿ (A , a)
   for n ≥ 1. Unfortunately, the two sides have different universe
   levels since (Type i , A) lives in Type• (suc i) instead of Type• i.
   Morally, this is outbalanced by the extra Ω on the left-hand side,
   which might help explain on an intuitive level why
   the n-th universe ends up being not an n-type.

   The reason why the univalence principle (A ≡ B) ≃ (A ≃ B)
   cannot be written as (A ≡ B) ≡ (A ≃ B) is precisely the same. -}
module _ {i} {A : Type i} where
  -- The degenerate pre-base case carries around a propositional component.
  Ω-Type : Ω (Type i , A) ≃• Σ• (Π• (A , λ a → (A , a))
                               , (is-equiv , idf-is-equiv _))
  Ω-Type =
      Ω (Type i , A)
    ≃•⟨ ide• _ ⟩
      (A == A , idp)
    ≃•⟨ ua-equiv• ⁻¹• ⟩
      (A ≃ A , ide _)
    ≃•⟨ ide• _ ⟩
      ((Σ (A → A) is-equiv) , (idf _ , idf-is-equiv _))
    ≃•⟨ ide• _ ⟩
      Σ• (Π• (A , λ a → (A , a)) , (is-equiv , idf-is-equiv _))
    ≃•∎

  -- The base case.
  Ω²-Type : (Ω ^ 2) (Type i , A) ≃• Π• (A , λ a → Ω (A , a))
  Ω²-Type =
      (Ω ^ 2) (Type i , A)
    ≃•⟨ equiv-Ω Ω-Type ⟩
      Ω (Σ• (Π• (A , λ a → (A , a)) , (is-equiv , idf-is-equiv _)))
    ≃•⟨ forget-Ω^-Σ•₂ {i} _ 1 (is-equiv-is-prop ∘ _) ⟩
      Ω (Π• (A , λ a → (A , a)))
    ≃•⟨ Ω-Π•-comm _ ⟩
      Π• (A , λ a → Ω (A , a))
    ≃•∎

  -- The general case follows by permuting Ω and Π• repeatedly.
  Ω^-Type : (n : ℕ) → (Ω ^ (n + 2)) (Type i , A)
                   ≃• Π• (A , λ a → (Ω ^ (n + 1)) (A , a))
  Ω^-Type n = Ω^-Π•-comm _ _ n ∘e• equiv-Ω^ n Ω²-Type

{- The pointed family P from the article.
   It takes an n-type A and returns the space of (n+1)-loops
   with basepoint A in U_n^n (the n-th universe restricted to n-types).
   This crucial restriction to n-types implies it is just a set. -}
module _ (n : ℕ) (A : ⟨ n ⟩ -Type ｢ n ｣) where
  -- *** Definition of P and Corollary 5.6 ***
  P : ⟨0⟩ -Type• ｢ n + 1 ｣
  P = Ω^-≤' (n + 1) q where
    q : ⟨ n + 1 ⟩ -Type• ｢ n + 1 ｣
    q = –> Σ-comm-snd ((⟨ n ⟩ -Type-≤ ｢ n ｣ , A))

  -- Forgetting about the truncation level, we may present P as follows:
  Q : Type• ｢ n + 1 ｣
  Q = (Ω ^ (n + 1)) (Type ｢ n ｣ , fst A)

  P-is-Q : fst P ≃• Q
  P-is-Q = equiv-Ω^ n (forget-Ω^-Σ•₂ _ 1 (λ _ → has-level-is-prop))

-- *** Definition of Loop and Lemma 5.7 ***
{- The type Loop of (images of) n-loops in U_(n-1)^(n-1) is
   just the dependent sum over P except for the special case n ≡ 0,
   where we take U_(-1)^(-1) (and hence Loop) to be the booleans.

   The boilerplate with raise-≤T is just to verify Loop n is n-truncated.
   The bulk of the rest of this module consists of showing
   the n-th universe is not n-truncated at basepoint Loop n,
   i.e. that Q n (Loop n) is not contractible.

   Warning: The indexing of Loop starts at -1 in the article,
            but we use natural numbers here (starting at 0),
            thus everything is shifted by one. -}
Loop : (n : ℕ) → ⟨ n ⟩ -Type ｢ n ｣
Loop 0     = (Bool , Bool-is-set)
Loop (S n) = Σ-≤ (⟨ n ⟩ -Type-≤ ｢ n ｣) (λ A →
               raise-≤T {n = ⟨ n + 1 ⟩} (≤T-+2+-l ⟨0⟩ (-2≤T _))
                                        (fst (<– Σ-comm-snd (P n A))))

-- *** Section 2: the base case ***
-- The base case is as usual (there is a non-trivial automorphism on booleans).
base-case : ¬ (is-contr• (Q 0 (Loop 0)))
base-case c = Bool-false≠true false-is-true where
  -- Negation.
  ~ : Bool → Bool
  ~ = λ {true → false; false → true}

  -- Negation is an equivalence.
  e : Bool ≃ Bool
  e = equiv ~ ~ inv inv where
    inv = λ {true → idp; false → idp}

  -- Negation being equal to the identity yields a contradiction.
  false-is-true =
    false             =⟨ ! (coe-β e true) ⟩
    coe (ua e) true   =⟨ ap (λ p → coe p true) (! (c (ua e))) ⟩
    coe idp true      =⟨ idp ⟩
    true              ∎

-- Let us now turn towards the successor case.
module _ (m : ℕ) where

  -- We expand the type we are later going to assume contractible.
  Q-L-is-… =
      Q (m + 1) (Loop (m + 1))
    ≃•⟨ ide• _ ⟩
      (Ω ^ (m + 2)) (_ , ⟦ Loop (m + 1) ⟧)
    ≃•⟨ (Ω^-Type m) ⟩
      Π• (_ , λ {(A , q) → Ω ^ (m + 1) $ (⟦ Loop (m + 1) ⟧ , (A , q))})
    ≃•⟨ ide• _ ⟩
      Π• (_ , λ {(A , q) → Ω ^ (m + 1) $ Σ• ((_ , A) , (base ∘ fst ∘ P m , q))})
    ≃•∎

  -- What we really want is to arrive at contractibility of E (m ≥ 1)...
  E = Π• (⟦ Loop (m + 1) ⟧ , λ {(A , q) → Ω ^ (m + 1) $ (⟨ m ⟩ -Type ｢ m ｣ , A)})

  -- ...or at least show that the following element f of E is trivial (m ≡ 0).
  f : base E
  f (_ , q) = q

{- We want to use our assumption of contractibility of Q (n + 1) (Loop (n + 1))
   to show that f is trivial, i.e. constant with value the basepoint. -}
f-is-trivial : (m : ℕ) → is-contr• (Q (m + 1) (Loop (m + 1))) → f m == pt (E m)

-- m ≡ 0
f-is-trivial 0 c = ap (λ f' → fst ∘ f')
                      (! (–> (equiv-is-contr• …-is-E') c f')) where
  {- This is almost E, except for the additional component
      specifying that the first component p should commute with q. -}
  E' = Π• (_ , λ {(A , q) → (Σ (A == A) (λ p → q == q [ (λ x → x == x) ↓ p ])
                                       , (idp , idp))})

  -- This "almost" E comes from Q 1 (Loop 1), hence can be shown contractible.
  …-is-E' : Q 1 (Loop 1) ≃• E'
  …-is-E' = equiv-Π• (ide _ , Ω-Σ•-comm ∘ _) ∘e• Q-L-is-… 0

  {- Fortunately, f can be 'extended' to an element f' of E',
     and triviality of f' implies triviality of f. -}
  f' = λ {(_ , q) → (q , ↓-idf=idf-in (∙=∙' q q))}

{- m ≥ 1: We can show Q (k + 2) (Loop (k + 2)) ≃ E (k + 1),
          thus E is contractible, hence f trivial. -}
f-is-trivial (S k) c = ! (–> (equiv-is-contr• (…-is-E ∘e• Q-L-is-… (k + 1)))
                             c
                             (f (k + 1))) where
  …-is-E : _ ≃• E (k + 1)
  …-is-E = equiv-Π• (ide _ , equiv-Ω^ k ∘ (λ {(A , q) → forget-Ω^-Σ•₂
             {｢ k + 2 ｣} (base ∘ fst ∘ P (k + 1) , q) 2
             (snd ∘ P (k + 1))}))

-- *** Lemma 5.8, part one ***
-- Our main lemma: like in the article, but in negative form.
main : (n : ℕ) → ¬ (is-contr• (Q n (Loop n)))
main 0       = base-case
main (S m) c = main m step where
  {- We know Q (m + 1) (Loop (m + 1)) is contractible,
     use that to show that the above f is trivial,
     deduce f (Loop m , p) ≡ p is trivial for all p in P m (Loop m),
     which implies P m (Loop m) is contractible.
     But this is just another form of Q m (Loop m),
     so the conclusion follows by induction hypothesis. -}
  step : is-contr• (Q m (Loop m))
  step = –> (equiv-is-contr• (P-is-Q m (Loop m)))
            (λ q → app= (! (f-is-trivial m c)) (Loop m , q))

-- *** Lemma 5.8, part two ***
-- Alternate form of the main lemma
main' : (n : ℕ) → ¬ (is-contr• ((Ω ^ (n + 1)) (⟨ n ⟩ -Type ｢ n ｣ , Loop n )))
main' n = main n ∘ –> (equiv-is-contr• (P-is-Q n (Loop n)))

-- Small helper thingy
helpy : ∀ {i} {n : ℕ} {X : Type• i}
        → has-level• (n -1) X → is-contr• ((Ω ^ n) X)
helpy {n = n} {X} = <– contr•-equiv-prop
                  ∘ trunc-many n
                  ∘ transport (λ k → has-level• (k -2) X)
                              (+-comm 1 n)

-- Main theorems now fall out as corollaries.
module _ (n : ℕ) where
  {- Recall that L n is n-truncated.
     We also know it is not (n-1)-truncated, it is thus a 'strict' n-type. -}
  Loop-is-not-trunc : ¬ (has-level (n -1) ⟦ Loop n ⟧)
  Loop-is-not-trunc = main n ∘ helpy ∘ (λ t → universe-=-level t t)

  -- *** Theorem 5.9 ***
  -- The n-th universe is not n-truncated.
  Type-is-not-trunc : ¬ (has-level ⟨ n ⟩ (Type ｢ n ｣))
  Type-is-not-trunc = main n ∘ helpy

  -- *** Theorem 5.11 ***
  {- MAIN RESULT:
     The n-th universe restricted to n-types is a 'strict' (n+1)-type. -}
  Type-≤-is-not-trunc : ¬ (has-level ⟨ n ⟩ (⟨ n ⟩ -Type ｢ n ｣))
  Type-≤-is-not-trunc = main' n ∘ helpy


module Syntax where

data S
  (A₁ : Set)
  (A₂ : A₁ → Set)
  : Set
  where

  _,_
    : (x₁ : A₁)
    → A₂ x₁
    → S A₁ A₂

syntax S A₁ (λ x → A₂)
  = x ∈ A₁ × A₂

module M where

  data S'
    (A₁ : Set)
    (A₂ : A₁ → Set)
    : Set
    where

    _,'_
      : (x₁ : A₁)
      → A₂ x₁
      → S' A₁ A₂

  syntax S' A₁ (λ x → A₂)
    = x ∈' A₁ ×' A₂

open M
  using (S')

postulate

  p1
    : {A₁ : Set}
    → {A₂ : A₁ → Set}
    → x₁ ∈ A₁ × A₂ x₁
    → A₁

  p1'
    : {A₁ : Set}
    → {A₂ : A₁ → Set}
    → x₁ ∈' A₁ ×' A₂ x₁
    → A₁


open import Agda.Primitive

record Functor {a b} (F : Set a → Set b) : Set (lsuc a ⊔ b) where
  field
    fmap : ∀ {A B} → (A → B) → F A → F B

open Functor {{...}} public

module _ {a b} (F : Set a → Set b) where
  record FunctorZero : Set (lsuc a ⊔ b) where
    field
      empty : ∀ {A} → F A
      overlap {{super}} : Functor F

  record Alternative : Set (lsuc a ⊔ b) where
    infixl 3 _<|>_
    field
      _<|>_ : ∀ {A} → F A → F A → F A
      overlap {{super}} : FunctorZero

open FunctorZero {{...}} public

open Alternative {{...}} public

record StateT {a} (S : Set a) (M : Set a → Set a) (A : Set a) : Set a where
  no-eta-equality
  constructor stateT
  field runStateT : S → M A

open StateT public

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

  instance
    FunctorStateT : {{_ : Functor M}} → Functor {a = a} (StateT S M)
    runStateT (fmap {{FunctorStateT}} f m) s = fmap f (runStateT m s)

    FunctorZeroStateT : {{_ : FunctorZero M}} → FunctorZero {a = a} (StateT S M)
    runStateT (empty {{FunctorZeroStateT}}) s = empty

    AlternativeStateT : {{_ : Alternative M}} → Alternative {a = a} (StateT S M)
    runStateT (_<|>_ {{AlternativeStateT}} x y) s = runStateT x s <|> runStateT y s

--- Example with some module parameter instantiations

open import Agda.Builtin.Nat
open import Agda.Builtin.Unit

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

postulate
  C₁ : Set → Set

record C₂ (A : Set) : Set where
  field
    f : A → A
    overlap {{super}} : C₁ A

open C₂ {{...}}

postulate
  T : (A : Set) → A → Set

module M (A : Set) (n : Nat) (xs : Vec A n) where

  unpackT : Vec A n → Set
  unpackT (x ∷ xs) = C₂ (T A x)
  unpackT [] = ⊤

  postulate instance C₁T : ∀ {x} {{C₁A : C₁ A}} → C₁ (T A x)

  C₂T : {{C₂A : C₂ A}} (ys : Vec A n) → unpackT ys
  C₂T [] = _
  f {{C₂T (y ∷ ys)}} x = x


------------------------------------------------------------------------
-- The Agda standard library
--
-- Properties, related to reflexive transitive closures, that rely on
-- the K rule
------------------------------------------------------------------------

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

module
  Relation.Binary.Construct.Closure.ReflexiveTransitive.Properties.WithK
  where

open import Function
open import Relation.Binary
open import Relation.Binary.Construct.Closure.ReflexiveTransitive
open import Relation.Binary.PropositionalEquality

------------------------------------------------------------------------
-- Equality

module _ {i t} {I : Set i} {T : Rel I t} {i j k} {x y : T i j} {xs ys}
  where

  ◅-injectiveˡ : (Star T i k ∋ x ◅ xs) ≡ y ◅ ys → x ≡ y
  ◅-injectiveˡ refl = refl

  ◅-injectiveʳ : (Star T i k ∋ x ◅ xs) ≡ y ◅ ys → xs ≡ ys
  ◅-injectiveʳ refl = refl


{-# OPTIONS --safe #-}
module Cubical.Categories.Category.Base where

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

private
  variable
    ℓ ℓ' : Level

-- Categories with hom-sets
record Category ℓ ℓ' : Type (ℓ-suc (ℓ-max ℓ ℓ')) where
  -- no-eta-equality ; NOTE: need eta equality for `opop`
  field
    ob : Type ℓ
    Hom[_,_] : ob → ob → Type ℓ'
    id   : ∀ {x} → Hom[ x , x ]
    _⋆_  : ∀ {x y z} (f : Hom[ x , y ]) (g : Hom[ y , z ]) → Hom[ x , z ]
    ⋆IdL : ∀ {x y} (f : Hom[ x , y ]) → id ⋆ f ≡ f
    ⋆IdR : ∀ {x y} (f : Hom[ x , y ]) → f ⋆ id ≡ f
    ⋆Assoc : ∀ {x y z w} (f : Hom[ x , y ]) (g : Hom[ y , z ]) (h : Hom[ z , w ])
           → (f ⋆ g) ⋆ h ≡ f ⋆ (g ⋆ h)
    isSetHom : ∀ {x y} → isSet Hom[ x , y ]

  -- composition: alternative to diagramatic order
  _∘_ : ∀ {x y z} (g : Hom[ y , z ]) (f : Hom[ x , y ]) → Hom[ x , z ]
  g ∘ f = f ⋆ g

  infixr 9 _⋆_
  infixr 9 _∘_

open Category

-- Helpful syntax/notation
_[_,_] : (C : Category ℓ ℓ') → (x y : C .ob) → Type ℓ'
_[_,_] = Hom[_,_]

-- Needed to define this in order to be able to make the subsequence syntax declaration
seq' : ∀ (C : Category ℓ ℓ') {x y z} (f : C [ x , y ]) (g : C [ y , z ]) → C [ x , z ]
seq' = _⋆_

infixl 15 seq'
syntax seq' C f g = f ⋆⟨ C ⟩ g

-- composition
comp' : ∀ (C : Category ℓ ℓ') {x y z} (g : C [ y , z ]) (f : C [ x , y ]) → C [ x , z ]
comp' = _∘_

infixr 16 comp'
syntax comp' C g f = g ∘⟨ C ⟩ f

-- Isomorphisms and paths in categories
record CatIso (C : Category ℓ ℓ') (x y : C .ob) : Type ℓ' where
  constructor catiso
  field
    mor : C [ x , y ]
    inv : C [ y , x ]
    sec : inv ⋆⟨ C ⟩ mor ≡ C .id
    ret : mor ⋆⟨ C ⟩ inv ≡ C .id

pathToIso : {C : Category ℓ ℓ'} {x y : C .ob} (p : x ≡ y) → CatIso C x y
pathToIso {C = C} p = J (λ z _ → CatIso _ _ z) (catiso idx idx (C .⋆IdL idx) (C .⋆IdL idx)) p
  where
    idx = C .id

-- Univalent Categories
record isUnivalent (C : Category ℓ ℓ') : Type (ℓ-max ℓ ℓ') where
  field
    univ : (x y : C .ob) → isEquiv (pathToIso {C = C} {x = x} {y = y})

  -- package up the univalence equivalence
  univEquiv : ∀ (x y : C .ob) → (x ≡ y) ≃ (CatIso _ x y)
  univEquiv x y = pathToIso , univ x y

  -- The function extracting paths from category-theoretic isomorphisms.
  CatIsoToPath : {x y : C .ob} (p : CatIso _ x y) → x ≡ y
  CatIsoToPath {x = x} {y = y} p =
    equivFun (invEquiv (univEquiv x y)) p

-- Opposite category
_^op : Category ℓ ℓ' → Category ℓ ℓ'
ob (C ^op)           = ob C
Hom[_,_] (C ^op) x y = C [ y , x ]
id (C ^op)           = id C
_⋆_ (C ^op) f g      = g ⋆⟨ C ⟩ f
⋆IdL (C ^op)         = C .⋆IdR
⋆IdR (C ^op)         = C .⋆IdL
⋆Assoc (C ^op) f g h = sym (C .⋆Assoc _ _ _)
isSetHom (C ^op)     = C .isSetHom


open import Agda.Primitive using (_⊔_)

import Categories.Category as Category
import Categories.Category.Cartesian as Cartesian

open import SingleSorted.Context

module SingleSorted.Power
       {o ℓ e}
       (𝒞 : Category.Category o ℓ e) where

  open Category.Category 𝒞
  open HomReasoning

  record Powered (A : Obj) : Set (o ⊔ ℓ ⊔ e) where

    field
      pow : Context → Obj
      π : ∀ {Γ} → var Γ → pow Γ ⇒ A
      tuple : ∀ Γ {B} → (var Γ → B ⇒ A) → B ⇒ pow Γ
      project : ∀ {Γ} {B} {x : var Γ} {fs : var Γ → B ⇒ A} → π x ∘ tuple Γ fs ≈ fs x
      unique : ∀ {Γ} {B} {fs : var Γ → B ⇒ A} {g : B ⇒ pow Γ} → (∀ i → π i ∘ g ≈ fs i) → tuple Γ fs ≈ g

    η : ∀ {Γ} → tuple Γ π ≈ id
    η = unique (λ i → identityʳ)

    ! : ∀ {B : Obj} → B ⇒ pow ctx-empty
    ! = tuple ctx-empty ctx-empty-absurd

    !-unique : ∀ {B : Obj} {f : B ⇒ pow ctx-empty} → ! ≈ f
    !-unique = unique λ i → ctx-empty-absurd i

    !-unique₂ : ∀ {B : Obj} {f g : B ⇒ pow ctx-empty} → f ≈ g
    !-unique₂ = (⟺ !-unique) ○ !-unique

    tuple-cong : ∀ {B : Obj} {Γ} {fs gs : var Γ → B ⇒ A} → (∀ i → fs i ≈ gs i) →
                 tuple Γ fs ≈ tuple Γ gs
    tuple-cong ξ = unique (λ i →  project ○ ⟺ (ξ i))

    ∘-distribʳ-tuple : ∀ {B C} {Γ} {fs : var Γ → B ⇒ A} {g : C ⇒ B}  →
                       tuple Γ (λ x → fs x ∘ g) ≈ tuple Γ fs ∘ g
    ∘-distribʳ-tuple = unique (λ i → ⟺ assoc ○ ∘-resp-≈ˡ project)

  -- A cartesian category has a standard power structure (which we need not use)
  module _ (cartesian-𝒞 : Cartesian.Cartesian 𝒞) (A : Obj) where
    open Cartesian.Cartesian cartesian-𝒞

    standard-pow : Context → Obj
    standard-pow ctx-empty = ⊤
    standard-pow ctx-slot = A
    standard-pow (ctx-concat Γ Δ) = standard-pow Γ × standard-pow Δ

    standard-π : ∀ {Γ} → var Γ → standard-pow Γ ⇒ A
    standard-π var-var = id
    standard-π (var-inl i) = standard-π i ∘ π₁
    standard-π (var-inr i) = standard-π i ∘ π₂

    standard-tuple : ∀ Γ {B} → (var Γ → B ⇒ A) → B ⇒ standard-pow Γ
    standard-tuple ctx-empty fs = !
    standard-tuple ctx-slot fs = fs var-var
    standard-tuple (ctx-concat Γ Δ) fs = ⟨ standard-tuple Γ (λ i → fs (var-inl i)) , standard-tuple Δ (λ j → fs (var-inr j)) ⟩

    standard-project : ∀ {Γ} {B} {x : var Γ} {fs : var Γ → B ⇒ A} →
                       standard-π x ∘ standard-tuple Γ fs ≈ fs x
    standard-project {x = var-var} = identityˡ
    standard-project {x = var-inl x} = assoc ○ ((∘-resp-≈ʳ project₁) ○ standard-project {x = x})
    standard-project {x = var-inr x} = assoc ○ ((∘-resp-≈ʳ project₂) ○ standard-project {x = x})

    standard-unique : ∀ {Γ} {B} {fs : var Γ → B ⇒ A} {g : B ⇒ standard-pow Γ} → (∀ i → standard-π i ∘ g ≈ fs i) →
                      standard-tuple Γ fs ≈ g
    standard-unique {ctx-empty} ξ = !-unique _
    standard-unique {ctx-slot} ξ = ⟺ (ξ var-var) ○ identityˡ
    standard-unique {ctx-concat Γ Δ} {fs = fs} ξ =
      unique
         (⟺ (standard-unique (λ x → sym-assoc ○ (ξ (var-inl x)))))
         (⟺ (standard-unique (λ y → sym-assoc ○ (ξ (var-inr y)))))

    StandardPowered : Powered A
    StandardPowered =
      record
      { pow = standard-pow
      ; π = standard-π
      ; tuple = standard-tuple
      ; project = λ {Γ} → standard-project {Γ}
      ; unique = standard-unique }


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

open import Categories.Category

module Categories.Category.Closed {o ℓ e} (C : Category o ℓ e) where

private
  module C = Category C
  open C
  variable
    A B X X′ Y Y′ Z Z′ U V : Obj
    f g : A ⇒ B

  open Commutation

open import Level
open import Data.Product using (Σ; _,_)
open import Function.Equality using (_⟶_)
open import Function.Inverse using (_InverseOf_; Inverse)

open import Categories.Category.Product
open import Categories.Functor renaming (id to idF)
open import Categories.Functor.Bifunctor
open import Categories.Functor.Construction.Constant
open import Categories.NaturalTransformation
open import Categories.NaturalTransformation.NaturalIsomorphism
open import Categories.NaturalTransformation.Dinatural

record Closed : Set (levelOfTerm C) where
  field
    -- internal hom
    [-,-] : Bifunctor C.op C C
    unit  : Obj

  [_,-] : Obj → Functor C C
  [_,-] = appˡ [-,-]

  [-,_] : Obj → Functor C.op C
  [-,_] = appʳ [-,-]

  module [-,-] = Functor [-,-]

  [_,_]₀ : Obj → Obj → Obj
  [ X , Y ]₀ = [-,-].F₀ (X , Y)

  [_,_]₁ : A ⇒ B → X ⇒ Y → [ B , X ]₀ ⇒ [ A , Y ]₀
  [ f , g ]₁ = [-,-].F₁ (f , g)

  field
    -- i
    identity : NaturalIsomorphism idF [ unit ,-]
    -- j
    diagonal : Extranaturalʳ unit [-,-]

  module identity = NaturalIsomorphism identity
  module diagonal = DinaturalTransformation diagonal

  [[X,-],[X,-]] : Obj → Bifunctor C.op C C
  [[X,-],[X,-]] X = [-,-] ∘F (Functor.op [ X ,-] ⁂ [ X ,-])

  [[-,Y],[-,Z]] : Obj → Obj → Bifunctor C C.op C
  [[-,Y],[-,Z]] Y Z = [-,-] ∘F ((Functor.op [-, Y ]) ⁂ [-, Z ])

  -- L needs to be natural in Y and Z while extranatural in Z.
  -- it is better to spell out the conditions and then prove that indeed
  -- the naturality conditions hold.
  field
    L : ∀ X Y Z → [ Y , Z ]₀ ⇒ [ [ X , Y ]₀ , [ X , Z ]₀ ]₀
    L-natural-comm : [ [ Y , Z ]₀ ⇒  [ [ X , Y′ ]₀ , [ X , Z′ ]₀ ]₀ ]⟨
                       [ f , g ]₁                                   ⇒⟨ [ Y′ , Z′ ]₀ ⟩
                       L X Y′ Z′
                     ≈ L X Y Z                                      ⇒⟨ [ [ X , Y ]₀ , [ X , Z ]₀ ]₀ ⟩
                       [ [ C.id , f ]₁ , [ C.id , g ]₁ ]₁
                     ⟩
    L-dinatural-comm : [ [ Y , Z ]₀ ⇒  [ [ X , Y ]₀ , [ X′ , Z ]₀ ]₀ ]⟨
                         L X′ Y Z                                      ⇒⟨ [ [ X′ , Y ]₀ , [ X′ , Z ]₀ ]₀ ⟩
                         [ [ f , C.id ]₁ , [ C.id , C.id ]₁ ]₁
                       ≈ L X Y Z                                       ⇒⟨ [ [ X , Y ]₀ , [ X , Z ]₀ ]₀ ⟩
                         [ [ C.id , C.id ]₁ , [ f , C.id ]₁ ]₁
                       ⟩

  L-natural : NaturalTransformation [-,-] ([[X,-],[X,-]] X)
  L-natural {X} = ntHelper record
    { η       = λ where
      (Y , Z) → L X Y Z
    ; commute = λ _ → L-natural-comm
    }

  L-dinatural : Extranaturalʳ [ Y , Z ]₀ (flip-bifunctor ([[-,Y],[-,Z]] Y Z))
  L-dinatural {Y} {Z} = extranaturalʳ (λ X → L X Y Z) L-dinatural-comm

  module L-natural {X}     = NaturalTransformation (L-natural {X})
  module L-dinatural {Y Z} = DinaturalTransformation (L-dinatural {Y} {Z})

  -- other required diagrams
  field
    Lj≈j : [ unit ⇒ [ [ X , Y ]₀ , [ X , Y ]₀ ]₀ ]⟨
             diagonal.α Y                        ⇒⟨ [ Y , Y ]₀ ⟩
             L X Y Y
           ≈ diagonal.α [ X , Y ]₀
           ⟩
    jL≈i : [ [ X , Y ]₀ ⇒ [ unit , [ X , Y ]₀ ]₀ ]⟨
             L X X Y                             ⇒⟨ [ [ X , X ]₀ , [ X , Y ]₀ ]₀ ⟩
             [ diagonal.α X , C.id ]₁
           ≈ identity.⇒.η [ X , Y ]₀
           ⟩
    iL≈i : [ [ Y , Z ]₀ ⇒ [ Y , [ unit , Z ]₀ ]₀ ]⟨
             L unit Y Z                          ⇒⟨ [ [ unit , Y ]₀ , [ unit , Z ]₀ ]₀ ⟩
             [ identity.⇒.η Y , C.id ]₁
           ≈ [ C.id , identity.⇒.η Z ]₁
           ⟩

    pentagon : [ [ U , V ]₀ ⇒ [ [ Y , U ]₀ , [ [ X , Y ]₀ , [ X , V ]₀ ]₀ ]₀ ]⟨
                 L X U V                            ⇒⟨ [ [ X , U ]₀ , [ X , V ]₀ ]₀ ⟩
                 L [ X , Y ]₀ [ X , U ]₀ [ X , V ]₀ ⇒⟨ [ [ [ X , Y ]₀ , [ X , U ]₀ ]₀ , [ [ X , Y ]₀ , [ X , V ]₀ ]₀ ]₀ ⟩
                 [ L X Y U , C.id ]₁
               ≈ L Y U V                            ⇒⟨ [ [ Y , U ]₀ , [ Y , V ]₀ ]₀ ⟩
                 [ C.id , L X Y V ]₁
               ⟩

  open Functor

  γ : hom-setoid {X} {Y} ⟶ hom-setoid {unit} {[ X , Y ]₀}
  γ {X} = record
    { _⟨$⟩_ = λ f → [ C.id , f ]₁ ∘ diagonal.α _
    ; cong  = λ eq → ∘-resp-≈ˡ (F-resp-≈ [ X ,-] eq)
    }

  field
    γ⁻¹             : hom-setoid {unit} {[ X , Y ]₀} ⟶ hom-setoid {X} {Y}
    γ-inverseOf-γ⁻¹ : γ {X} {Y} InverseOf γ⁻¹

  γ-inverse : Inverse (hom-setoid {unit} {[ X , Y ]₀}) (hom-setoid {X} {Y})
  γ-inverse = record
    { to         = γ⁻¹
    ; from       = γ
    ; inverse-of = γ-inverseOf-γ⁻¹
    }


module Cats.Category.Constructions.Equalizer where

open import Level

open import Cats.Category.Base
open import Cats.Util.Conv

import Cats.Category.Constructions.Mono as Mono
import Cats.Category.Constructions.Unique as Unique

module Build {lo la l≈} (Cat : Category lo la l≈) where

  private open module Cat = Category Cat
  open Cat.≈-Reasoning
  open Mono.Build Cat
  open Unique.Build Cat

  record IsEqualizer {A B} (f g : A ⇒ B) {E} (e : E ⇒ A)
    : Set (lo ⊔ la ⊔ l≈)
    where
    field
      equalizes : f ∘ e ≈ g ∘ e
      universal : ∀ {Z} (z : Z ⇒ A)
        → f ∘ z ≈ g ∘ z
        → ∃![ u ] (e ∘ u ≈ z)

  record Equalizer {A B} (f g : A ⇒ B) : Set (lo ⊔ la ⊔ l≈) where
    field
      {E} : Obj
      e : E ⇒ A
      isEqualizer : IsEqualizer f g e

    open IsEqualizer isEqualizer public

  instance
    HasObj-Equalizer : ∀ {A B} {f g : A ⇒ B}
      → HasObj (Equalizer f g) lo la l≈
    HasObj-Equalizer = record { Cat = Cat ; _ᴼ = Equalizer.E }

    HasArrow-Equalizer : ∀ {A B} {f g : A ⇒ B}
      → HasArrow (Equalizer f g) lo la l≈
    HasArrow-Equalizer = record { Cat = Cat ; _⃗ = Equalizer.e }

  equalizer→mono : ∀ {A B} {f g : A ⇒ B} {E} {e : E ⇒ A}
    → IsEqualizer f g e
    → IsMono e
  equalizer→mono {f = f} {g} {E} {e} eql {Z} {i} {j} e∘i≈e∘j
      = ∃!→≈ (universal (e ∘ j) lemma) e∘i≈e∘j ≈.refl
    where
      open IsEqualizer eql

      lemma : f ∘ e ∘ j ≈ g ∘ e ∘ j
      lemma
          = begin
              f ∘ e ∘ j
            ≈⟨ unassoc ⟩
              (f ∘ e) ∘ j
            ≈⟨ ∘-resp-l equalizes ⟩
              (g ∘ e) ∘ j
            ≈⟨ assoc ⟩
              g ∘ e ∘ j
            ∎


-- Trying to define ≤ as a datatype in Prop doesn't work very well:

{-# OPTIONS --enable-prop #-}

open import Agda.Builtin.Nat

data _≤'_ : Nat → Nat → Prop where
  zero : (y : Nat) → zero ≤' y
  suc  : (x y : Nat) → x ≤' y → suc x ≤' suc y

≤'-ind : (P : (m n : Nat) → Set)
       → (pzy : (y : Nat) → P zero y)
       → (pss : (x y : Nat) → P x y → P (suc x) (suc y))
       → (m n : Nat) → m ≤' n → P m n
≤'-ind P pzy pss .0 y (zero .y) = ?
≤'-ind P pzy pss .(suc x) .(suc y) (suc x y pf) = ?


{-# OPTIONS --without-K #-}
module Transport where

open import GroupoidStructure
open import PathOperations
open import Types

tr-post : ∀ {a} {A : Set a} (a : A) {x y : A}
  (p : x ≡ y) (q : a ≡ x) →
  tr (λ x → a ≡ x) p q ≡ q · p
tr-post a = J
  (λ x _ p → (q : a ≡ x) → tr (λ x → a ≡ x) p q ≡ q · p)
  (λ _ q → p·id q ⁻¹)
  _ _

tr-pre : ∀ {a} {A : Set a} (a : A) {x y : A}
  (p : x ≡ y) (q : x ≡ a) →
  tr (λ x → x ≡ a) p q ≡ p ⁻¹ · q
tr-pre a = J
  (λ x _ p → (q : x ≡ a) → tr (λ x → x ≡ a) p q ≡ p ⁻¹ · q)
  (λ _ q → id·p q ⁻¹)
  _ _

tr-both : ∀ {a} {A : Set a} (a : A) {x y : A}
  (p : x ≡ y) (q : x ≡ x) →
  tr (λ x → x ≡ x) p q ≡ p ⁻¹ · q · p
tr-both a = J
  (λ x _ p → (q : x ≡ x) → tr (λ x → x ≡ x) p q ≡ p ⁻¹ · q · p)
  (λ _ q → p·id q ⁻¹ · id·p (q · refl) ⁻¹)
  _ _

tr-∘ : ∀ {a b p} {A : Set a} {B : Set b} {P : B → Set p}
  (f : A → B) {x y : A} (p : x ≡ y) (u : P (f x)) →
  tr (P ∘ f) p u ≡ tr P (ap f p) u
tr-∘ {P = P} f = J
  (λ x _ p → (u : P (f x)) → tr (P ∘ f) p u ≡ tr P (ap f p) u)
  (λ _ _ → refl) _ _


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

module Polynomial.Simple.AlmostCommutativeRing.Instances where

open import Polynomial.Simple.AlmostCommutativeRing
open import Level using (0ℓ)
open import Agda.Builtin.Reflection

module Nat where
  open import Data.Nat using (zero; suc)
  open import Relation.Binary.PropositionalEquality using (refl)
  open import Data.Nat.Properties using (*-+-commutativeSemiring)
  open import Data.Maybe using (just; nothing)

  ring : AlmostCommutativeRing 0ℓ 0ℓ
  ring =
    fromCommutativeSemiring
      *-+-commutativeSemiring
      λ { zero → just refl; _ → nothing }

  module Reflection where
    open import Polynomial.Simple.Reflection using (solveOver-macro)
    open import Data.Unit using (⊤)

    macro
      ∀⟨_⟩ : Term → Term → TC ⊤
      ∀⟨ n ⟩ = solveOver-macro n (quote ring)

module Int where
  open import Data.Nat using (zero)
  open import Data.Integer using (+_)
  open import Data.Integer.Properties using(+-*-commutativeRing)
  open import Data.Maybe using (just; nothing)
  open import Relation.Binary.PropositionalEquality using (refl)

  ring : AlmostCommutativeRing 0ℓ 0ℓ
  ring =
    fromCommutativeRing
      +-*-commutativeRing
      λ { (+ zero) → just refl; _ → nothing }

  module Reflection where
    open import Polynomial.Simple.Reflection using (solveOver-macro)
    open import Data.Unit using (⊤)

    macro
      ∀⟨_⟩ : Term → Term → TC ⊤
      ∀⟨ n ⟩ = solveOver-macro n (quote ring)


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

open import Cubical.Foundations.Everything renaming (_⁻¹ to _⁻¹ᵖ; assoc to ∙-assoc)
open import Cubical.Relation.Nullary.Base renaming (¬_ to ¬ᵗ_)-- ¬ᵗ_
open import Cubical.Relation.Binary.Base
open import Cubical.Data.Sum.Base renaming (_⊎_ to infixr 4 _⊎_)
open import Cubical.Data.Sigma renaming (_×_ to infixr 4 _×_)
open import Cubical.Data.Empty renaming (elim to ⊥-elim; ⊥ to ⊥⊥) -- `⊥` and `elim`
open import Function.Base using (_∋_; _$_; it; typeOf)
open import Cubical.Foundations.Logic renaming
  ( inl to inlᵖ
  ; inr to inrᵖ
  ; _⇒_ to infixr 0 _⇒_                  -- shifting by -6
  ; _⇔_ to infixr -2 _⇔_                 --
  ; ∃[]-syntax  to infix -4 ∃[]-syntax   --
  ; ∃[∶]-syntax to infix -4 ∃[∶]-syntax  --
  ; ∀[∶]-syntax to infix -4 ∀[∶]-syntax  --
  ; ∀[]-syntax  to infix -4 ∀[]-syntax   --
  )
-- open import Cubical.Data.Unit.Base using (Unit)
open import Cubical.HITs.PropositionalTruncation.Base -- ∣_∣

open import Utils
open import MoreLogic.Reasoning
open import MoreLogic.Definitions renaming
  ( _ᵗ⇒_ to infixr 0 _ᵗ⇒_
  ; ∀ᵖ[∶]-syntax  to infix -4 ∀ᵖ[∶]-syntax
  ; ∀ᵖ〚∶〛-syntax  to infix -4 ∀ᵖ〚∶〛-syntax
  ; ∀ᵖ!〚∶〛-syntax to infix -4 ∀ᵖ!〚∶〛-syntax
  ; ∀〚∶〛-syntax   to infix -4 ∀〚∶〛-syntax
  ; Σᵖ[]-syntax   to infix -4 Σᵖ[]-syntax
  ; Σᵖ[∶]-syntax  to infix -4 Σᵖ[∶]-syntax
  ) hiding (≡ˢ-syntax)
open import MoreLogic.Properties
open import MorePropAlgebra.Definitions hiding (_≤''_)
open import MorePropAlgebra.Consequences
open import MorePropAlgebra.Bundles

module MorePropAlgebra.Booij2020 where

module Chapter4 {ℓ ℓ'} (assumptions : AlmostPartiallyOrderedField {ℓ} {ℓ'}) where
  open import MorePropAlgebra.Properties.AlmostPartiallyOrderedField assumptions
  open AlmostPartiallyOrderedField assumptions renaming (Carrier to F; _-_ to _-_) -- atlernative to module telescope

  Item-1  = ∀[ x ] ∀[ y ]                                 x ≤ y ⇔ ¬(y < x)                          -- (definition of _≤_)
  Item-2  = ∀[ x ] ∀[ y ]                                 x # y ⇔ [ <-asym x y ] (x < y) ⊎ᵖ (y < x) -- (definition of _#_)
  Item-3  = ∀[ x ] ∀[ y ] ∀[ z ]                          x ≤ y ⇔ x + z ≤ y + z                     -- +-creates-≤
  Item-4  = ∀[ x ] ∀[ y ] ∀[ z ]                          x < y ⇔ x + z < y + z                     -- +-creates-<
  Item-5  = ∀[ x ] ∀[ y ]        0f < x + y          ⇒ (0f < x) ⊔ (0f < y)                          -- 0<-reflects-+
  Item-6  = ∀[ x ] ∀[ y ] ∀[ z ]  x < y     ⇒  y ≤ z ⇒    x     <     z                             -- <-≤-trans
  Item-7  = ∀[ x ] ∀[ y ] ∀[ z ]  x ≤ y     ⇒  y < z ⇒    x     <     z                             -- ≤-<-trans
  Item-8  = ∀[ x ] ∀[ y ] ∀[ z ]  x ≤ y     ⇒ 0f ≤ z ⇒    x · z ≤ y · z                             -- ·-preserves-≤
  Item-9  = ∀[ x ] ∀[ y ] ∀[ z ] 0f < z     ⇒            (x < y ⇔ x · z < y · z)                    -- ·-creates-<
  Item-10 =                      0f < 1f                                                            -- 0<1

  item-1 : ∀ x y   → [ x ≤ y ⇔                          ¬(y < x) ]; _ = [ Item-1 ] ∋ item-1
  item-2 : ∀ x y   → [ x # y ⇔ [ <-asym x y ] (x < y) ⊎ᵖ (y < x) ]; _ = [ Item-2 ] ∋ item-2

  item-6 : ∀ x y z → [ x < y ⇒ y ≤ z ⇒ x < z ]; _ : [ Item-6 ]; _ = item-6
  item-7 : ∀ x y z → [ x ≤ y ⇒ y < z ⇒ x < z ]; _ : [ Item-7 ]; _ = item-7

  --  1. x ≤ y ⇔ ¬(y < x),
  item-1 _ _ .fst x≤y = x≤y -- holds definitionally
  item-1 _ _ .snd x≤y = x≤y

  --  2. x # y ⇔ (x < y) ∨ (y < x)
  item-2 _ _ .fst  x#y        =  x#y -- holds definitionally
  item-2 _ _ .snd [x<y]⊎[y<x] = [x<y]⊎[y<x]

  item-6 = <-≤-trans
  item-7 = ≤-<-trans

  module +-<-ext+·-preserves-<⇒
    (+-<-ext       : [ is-+-<-Extensional _+_ _<_ ])
    (·-preserves-< : [ operation _·_ preserves _<_ when (λ z → 0f < z) ])
    where
    abstract
      +-reflects-<      : ∀ x y z → [           x + z < y + z ⇒     x < y      ]
      +-preserves-<     : ∀ x y z → [               x < y     ⇒ x + z < y + z  ]
      +-creates-<       : ∀ x y z → [               x < y     ⇔ x + z < y + z  ]; _ : [ Item-4  ]; _ = +-creates-<
      +-creates-≤       : ∀ x y z → [               x ≤ y     ⇔ x + z ≤ y + z  ]; _ : [ Item-3  ]; _ = +-creates-≤
      ·-reflects-<      : ∀ x y z → [ 0f < z ⇒ (x · z < y · z ⇒ x < y        ) ]
      ·-creates-<       : ∀ x y z → [ 0f < z ⇒ (    x < y     ⇔ x · z < y · z) ]; _ : [ Item-9  ]; _ = ·-creates-<
      ·-preserves-≤     : ∀ x y z → [  x ≤ y ⇒     0f ≤ z     ⇒ x · z ≤ y · z  ]; _ : [ Item-8  ]; _ = ·-preserves-≤

      0<-reflects-+     : ∀ x y   → [ 0f < x + y ⇒ (0f < x) ⊔ (0f < y)     ]; _ : [ Item-5  ]; _ = 0<-reflects-+

      ⁻¹-preserves-sign : ∀ z z⁻¹ → [ 0f < z ] → z · z⁻¹ ≡ 1f → [ 0f < z⁻¹ ]

      is-0<1            :           [ 0f < 1f ]                             ; _ : [ Item-10 ]; _ = is-0<1

      item-3            : [ Item-3 ]
      item-4            : [ Item-4 ]
      item-5            : [ Item-5 ]
      item-8            : [ Item-8 ]
      item-9            : [ Item-9 ]
      item-10           : [ Item-10 ]

      -- NOTE: just a plain copy of the previous proof
      +-preserves-< a b x = snd (
         a            <  b            ⇒ᵖ⟨ transport (λ i → [ sym (fst (+-identity a)) i < sym (fst (+-identity b)) i ]) ⟩
         a +    0f    <  b +    0f    ⇒ᵖ⟨ transport (λ i → [ a + sym (+-rinv x) i < b + sym (+-rinv x) i ]) ⟩
         a + (x  - x) <  b + (x  - x) ⇒ᵖ⟨ transport (λ i → [ +-assoc a x (- x) i < +-assoc b x (- x) i ]) ⟩
        (a +  x) - x  < (b +  x) - x  ⇒ᵖ⟨ +-<-ext (a + x) (- x) (b + x) (- x) ⟩
        (a + x < b + x) ⊔ (- x < - x) ⇒ᵖ⟨ (λ q → case q as (a + x < b + x) ⊔ (- x < - x) ⇒ a + x < b + x of λ
                                          { (inl a+x<b+x) → a+x<b+x -- somehow ⊥-elim needs a hint in the next line
                                          ; (inr  -x<-x ) → ⊥-elim {A = λ _ → [ a + x < b + x ]} (<-irrefl (- x) -x<-x)
                                          }) ⟩
         a + x < b + x ◼ᵖ)

      +-reflects-< x y z = snd
        ( x + z < y + z              ⇒ᵖ⟨ +-preserves-< _ _ (- z) ⟩
          (x + z) - z  < (y + z) - z ⇒ᵖ⟨ transport (λ i → [ +-assoc x z (- z) (~ i) < +-assoc y z (- z) (~ i) ]) ⟩
          x + (z - z) < y + (z - z)  ⇒ᵖ⟨ transport (λ i → [ x + +-rinv z i < y + +-rinv z i ]) ⟩
          x + 0f < y + 0f            ⇒ᵖ⟨ transport (λ i → [ fst (+-identity x) i < fst (+-identity y) i ]) ⟩
          x < y                      ◼ᵖ)

      --  3. x ≤ y ⇔ x + z ≤ y + z,
      +-creates-≤ x y z .fst = snd ( -- unfold the definition
         x     ≤ y          ⇒ᵖ⟨ (λ z → z) ⟩
        (y     < x     ⇒ ⊥) ⇒ᵖ⟨ (λ f → f ∘ (+-reflects-< y x z) ) ⟩
        (y + z < x + z ⇒ ⊥) ⇒ᵖ⟨ (λ z → z) ⟩
         x + z ≤ y + z      ◼ᵖ) -- refold the definition
      +-creates-≤ x y z .snd = snd (
         x + z ≤ y + z      ⇒ᵖ⟨ (λ z → z) ⟩ -- unfold the definition
        (y + z < x + z ⇒ ⊥) ⇒ᵖ⟨ (λ f p → f (+-preserves-< y x z p)) ⟩ -- just a variant of the above
        (y     < x     ⇒ ⊥) ⇒ᵖ⟨ (λ z → z) ⟩ -- refold the definition
         x     ≤ y          ◼ᵖ)

      item-3 = +-creates-≤

      --  4. x < y ⇔ x + z < y + z,
      +-creates-< x y z .fst = +-preserves-< x y z
      +-creates-< x y z .snd = +-reflects-<  x y z

      item-4 = +-creates-<

      --  5. 0 < x + y ⇒ 0 < x ∨ 0 < y,
      0<-reflects-+ x y = snd (
        (0f      < x + y)   ⇒ᵖ⟨ transport (λ i → [ fst (+-identity 0f) (~ i) < x + y ]) ⟩
        (0f + 0f < x + y)   ⇒ᵖ⟨ +-<-ext 0f 0f x y ⟩
        (0f < x) ⊔ (0f < y) ◼ᵖ)

      item-5 = 0<-reflects-+

      -- --  6. x < y ≤ z ⇒ x < z,
      -- item-6 x y z x<y y≤z = snd (
      --    x      <  y      ⇒ᵖ⟨ +-preserves-< _ _ _ ⟩
      --    x + z  <  y + z  ⇒ᵖ⟨ pathTo⇒ (λ i → x + z < +-comm y z i) ⟩
      --    x + z  <  z + y  ⇒ᵖ⟨ +-<-ext x z z y ⟩
      --   (x < z) ⊔ (z < y) ⇒ᵖ⟨ (λ q → case q as (x < z) ⊔ (z < y) ⇒ (x < z) of λ
      --                         { (inl x<z) → x<z
      --                         ; (inr z<y) → ⊥-elim (y≤z z<y)
      --                         }) ⟩
      --    x < z            ◼ᵖ) x<y
      --
      -- --  7. x ≤ y < z ⇒ x < z,
      -- item-7 x y z x≤y = snd ( -- very similar to the previous one
      --    y      <  z      ⇒ᵖ⟨ +-preserves-< y z x ⟩
      --    y + x  <  z + x  ⇒ᵖ⟨ pathTo⇒ (λ i → +-comm y x i < z + x) ⟩
      --    x + y  <  z + x  ⇒ᵖ⟨ +-<-ext x y z x ⟩
      --   (x < z) ⊔ (y < x) ⇒ᵖ⟨ (λ q → case q as (x < z) ⊔ (y < x) ⇒ (x < z) of λ
      --                         { (inl x<z) → x<z
      --                         ; (inr y<x) → ⊥-elim (x≤y y<x)
      --                         }) ⟩
      --    x < z  ◼ᵖ)

      -- 10. 0f < 1f
      item-10 = is-0<1

      ⁻¹-preserves-sign z z⁻¹ 0<z z·z⁻¹≡1 with snd (·-inv#0 z z⁻¹ z·z⁻¹≡1)
      ... | inl z⁻¹<0 = snd (
        z⁻¹     < 0f     ⇒ᵖ⟨ ·-preserves-< _ _ z 0<z ⟩
        z⁻¹ · z < 0f · z ⇒ᵖ⟨ transport (λ i → [ (·-comm _ _ ∙ z·z⁻¹≡1) i < RingTheory'.0-leftNullifies z i ]) ⟩
        1f      < 0f     ⇒ᵖ⟨ <-trans _ _ _ item-10 ⟩
        0f      < 0f     ⇒ᵖ⟨ <-irrefl _ ⟩
                ⊥        ⇒ᵖ⟨ ⊥-elim ⟩
             0f < z⁻¹    ◼ᵖ) z⁻¹<0
      ... | inr 0<z⁻¹ = 0<z⁻¹

      --  8. x ≤ y ∧ 0 ≤ z ⇒ x z ≤ y z,
      -- For item 8, suppose x ≤ y and 0 ≤ z and yz < xz.
      ·-preserves-≤ x y z x≤y 0≤z y·z<x·z = let
        -- Then 0 < z (x − y) by (†),
        i   = (  y · z            <  x · z                ⇒ᵖ⟨ pathTo⇒ (λ i → ·-comm y z i < ·-comm x z i) ⟩
                 z · y            <  z · x                ⇒ᵖ⟨ +-preserves-< _ _ _ ⟩
                (z · y) - (z · y) < (z · x) - (z ·    y ) ⇒ᵖ⟨ pathTo⇒ (cong₂ _<_ (+-rinv (z · y))
                                                               ( λ i → (z · x) + sym (RingTheory'.-commutesWithRight-· z y) i )) ⟩
                               0f < (z · x) + (z · (- y)) ⇒ᵖ⟨ pathTo⇒ (cong₂ _<_ refl (sym (fst (is-dist z x (- y))))) ⟩ -- [XX]
                               0f <  z · (x - y)          ◼ᵖ) .snd y·z<x·z
        instance z·[x-y]#0 = [ z · (x - y) # 0f ] ∋ inr i
        -- and so, being apart from 0, z (x − y) has a multiplicative inverse w.
        w   = (z · (x - y)) ⁻¹
        ii  : 1f ≡ (z · (x - y)) · w
        ii  = sym (·-rinv _ _)
        -- Hence z itself has a multiplicative inverse w (x − y),
        iii : 1f ≡ z · ((x - y) · w)
        iii = transport (λ i → 1f ≡ ·-assoc z (x - y) w (~ i)) ii
        instance z#0f = [ z # 0f ] ∋ fst (·-inv#0 _ _ (sym iii))
        -- and so 0 < z ∨ z < 0, where the latter case contradicts the assumption 0 ≤ z, so that we have 0 < z.
        instance _    = [ 0f < z ] ∋ case z#0f of λ where
                        (inl z<0) → ⊥-elim (0≤z z<0)
                        (inr 0<z) → 0<z
        -- Now w (x − y) has multiplicative inverse z, so it is apart from 0,
        iv  : [ (x - y) · w # 0f ]
        iv  = snd (·-inv#0 _ _ (sym iii))
        -- that is (0 < w (x − y)) ∨ (w (x − y) < 0).
        in case iv of λ where
          -- By (∗), from 0 < w (x − y) and yz < xz we get yzw (x − y) < xzw (x − y), so y < x, contradicting our assumption that x ≤ y.
          (inr 0<[x-y]·w) → (
             y ·  z                   <  x ·  z                    ⇒ᵖ⟨ ·-preserves-< _ _ _ 0<[x-y]·w ⟩
            (y ·  z) · ((x - y) · w)  < (x ·  z) · ((x - y) · w)   ⇒ᵖ⟨ pathTo⇒ (λ i →
                                                                          (·-assoc y z ((x - y) · w)) (~ i)
                                                                        < (·-assoc x z ((x - y) · w)) (~ i)) ⟩
             y · (z  · ((x - y) · w)) <  x · (z  · ((x - y) · w))  ⇒ᵖ⟨ pathTo⇒ (λ i →
                                                                         y · (iii (~ i)) < x · (iii (~ i))) ⟩
             y · 1f                   <  x · 1f                    ⇒ᵖ⟨ pathTo⇒ (cong₂ _<_
                                                                        (fst (·-identity y)) (fst (·-identity x))) ⟩
             y                        <  x                         ⇒ᵖ⟨ x≤y ⟩
                                      ⊥                            ◼ᵖ) .snd y·z<x·z
          -- In the latter case, from (∗) we get zw (x − y) < 0, i.e.
          -- 1 < 0 which contradicts item 10, so that we have 0 < w (x − y).
          (inl p) → (
                 (x - y) · w      < 0f     ⇒ᵖ⟨ ·-preserves-< _ _ _ it ⟩
                ((x - y) · w) · z < 0f · z ⇒ᵖ⟨ pathTo⇒ (cong₂ _<_ (·-comm _ _) (RingTheory'.0-leftNullifies z)) ⟩
            z · ((x - y) · w)     < 0f     ⇒ᵖ⟨ pathTo⇒ (λ i → iii (~ i) < 0f) ⟩
                               1f < 0f     ⇒ᵖ⟨ <-trans _ _ _  item-10 ⟩
                               0f < 0f     ⇒ᵖ⟨ <-irrefl _ ⟩
                                  ⊥        ◼ᵖ) .snd p

      item-8 = ·-preserves-≤

      --  9. 0 < z ⇒ (x < y ⇔ x z < y z),
      ·-creates-< x y z 0<z .fst = ·-preserves-< x y z 0<z
      -- For the other direction of item 9, assume 0 < z and xz < yz,
      ·-creates-< x y z 0<z .snd x·z<y·z = let
        instance _ = (          x · z  <  y · z            ⇒ᵖ⟨ +-preserves-< _ _ _ ⟩
                     (x · z) - (x · z) < (y · z) - (x · z) ⇒ᵖ⟨ pathTo⇒ (cong₂ _<_ (+-rinv (x · z)) refl) ⟩
                                    0f < (y · z) - (x · z) ◼ᵖ) .snd x·z<y·z
                 _ = [ (y · z) - (x · z) # 0f ] ∋ inr it
        -- so that yz − xz has a multiplicative inverse w,
        w = ((y · z) - (x · z)) ⁻¹
        o = ( (y · z) - (   x  · z) ≡⟨ ( λ i → (y · z) + (RingTheory'.-commutesWithLeft-· x z) (~ i)) ⟩
              (y · z) + ((- x) · z) ≡⟨ sym (snd (is-dist y (- x) z)) ⟩
              (y - x) · z           ∎)
        instance _ = [ (y - x) · z # 0f ] ∋ pathTo⇒ (λ i → o i # 0f) it
        -- and so z itself has multiplicative inverse w (y − x).
        instance _ = (          x · z  <  y · z            ⇒ᵖ⟨ +-preserves-< _ _ _ ⟩
                     (x · z) - (x · z) < (y · z) - (x · z) ⇒ᵖ⟨ pathTo⇒ (cong₂ _<_ (+-rinv (x · z)) refl) ⟩
                                    0f < (y · z) - (x · z) ◼ᵖ) .snd x·z<y·z
                 _ = [ (y · z) - (x · z) # 0f ] ∋ inr it
        1≡z·[w·[y-x]] = (
          1f                      ≡⟨ (λ i → ·-linv ((y · z) - (x · z)) it (~ i)) ⟩
          w · ((y · z) - (x · z)) ≡⟨ (λ i → w · o i) ⟩
          w · ((y - x) · z)       ≡⟨ (λ i → w · ·-comm (y - x) z i ) ⟩
          w · (z · (y - x))       ≡⟨ (λ i → ·-assoc w z (y - x) i) ⟩
          (w · z) · (y - x)       ≡⟨ (λ i → ·-comm w z i · (y - x)) ⟩
          (z · w) · (y - x)       ≡⟨ (λ i → ·-assoc z w (y - x) (~ i)) ⟩
          z · (w · (y - x))       ∎)
        1≡[w·[y-x]]·z : 1f ≡ (w · (y - x)) · z
        1≡[w·[y-x]]·z = transport (λ i → 1f ≡ ·-comm z (w · (y - x)) i) 1≡z·[w·[y-x]]
        -- Then since 0 < z and xz < yz, by (∗), we get xzw (y − x) < yzw (y − x), and hence x < y.
        instance _ = [ z # 0f ] ∋ inr 0<z
        z⁻¹ = w · (y - x)
        z⁻¹≡w·[y-x] : z ⁻¹ ≡ (w · (y - x))
        z⁻¹≡w·[y-x] = sym ((·-linv-unique (w · (y - x)) z (sym 1≡[w·[y-x]]·z)) it)
        instance _ = 0<z
        0<z⁻¹ : [ 0f < z ⁻¹ ]
        0<z⁻¹ = ⁻¹-preserves-sign z (z ⁻¹) it (·-rinv z it)
        instance _ = [ 0f < w · (y - x) ] ∋ pathTo⇒ (λ i → 0f < z⁻¹≡w·[y-x] i) 0<z⁻¹
        in (  x ·  z         <  y ·  z         ⇒ᵖ⟨ ·-preserves-< _ _ z⁻¹ it ⟩
             (x ·  z) · z⁻¹  < (y ·  z) · z⁻¹  ⇒ᵖ⟨ pathTo⇒ (λ i → ·-assoc x z z⁻¹ (~ i) < ·-assoc y z z⁻¹ (~ i)) ⟩
              x · (z  · z⁻¹) <  y · (z  · z⁻¹) ⇒ᵖ⟨ pathTo⇒ (λ i → x · 1≡z·[w·[y-x]] (~ i) < y · 1≡z·[w·[y-x]] (~ i)) ⟩
              x · 1f         <  y · 1f         ⇒ᵖ⟨ pathTo⇒ (cong₂ _<_ (fst (·-identity x)) (fst (·-identity y))) ⟩
              x              <  y              ◼ᵖ) .snd x·z<y·z

      ·-reflects-< x y z 0<z = ·-creates-< x y z 0<z .snd

      item-9 = ·-creates-<

      -- 10. 0 < 1.
      is-0<1 with ·-inv#0 _ _ (·-identity 1f .fst) .fst
      -- For item 10, since 1 has multiplicative inverse 1, it is apart from 0, hence 0 < 1 ∨ 1 < 0.
      ... | inl 1<0 = snd (
        -- If 1 < 0 then by item 4 we have 0 < −1 and so by (∗) we get 0 < (−1) · (−1), that is, 0 < 1, so by transitivity 1 < 1, contradicting irreflexivity of <.
          1f          < 0f                ⇒ᵖ⟨ +-preserves-< 1f 0f (- 1f) ⟩
          1f    - 1f  < 0f - 1f           ⇒ᵖ⟨ transport (λ i → [ +-rinv 1f i < snd (+-identity (- 1f)) i ]) ⟩
          0f          <    - 1f           ⇒ᵖ⟨ ( λ 0<-1 → ·-preserves-< 0f (- 1f) (- 1f) 0<-1 0<-1) ⟩
          0f · (- 1f) <   (- 1f) · (- 1f) ⇒ᵖ⟨ pathTo⇒ (cong₂ _<_ (RingTheory'.0-leftNullifies (- 1f)) refl) ⟩
          0f          <   (- 1f) · (- 1f) ⇒ᵖ⟨ transport (λ i → [ 0f < RingTheory'.-commutesWithRight-· (- 1f) (1f)   i  ]) ⟩
          0f          < -((- 1f) ·    1f )⇒ᵖ⟨ transport (λ i → [ 0f < RingTheory'.-commutesWithLeft-·  (- 1f)  1f (~ i) ]) ⟩
          0f          < (-(- 1f))·    1f  ⇒ᵖ⟨ transport (λ i → [ 0f < (Group'Properties.-involutive 1f i · 1f) ]) ⟩
          0f          <      1f  ·    1f  ⇒ᵖ⟨ transport (λ i → [ 0f < fst (·-identity 1f) i ]) ⟩
          0f          <      1f           ⇒ᵖ⟨ <-trans _ _ _ 1<0 ⟩
          1f          <      1f           ⇒ᵖ⟨ <-irrefl 1f ⟩
                      ⊥                   ⇒ᵖ⟨ ⊥-elim ⟩
          0f          <      1f           ◼ᵖ) 1<0
      ... | inr 0<1 = 0<1

  -- Conversely, assume the 10 listed items—in particular, items 4, 5 and 9.
  module item459⇒ -- 4. 5. 9. ⇒ 6.
    (item-4 : [ Item-4 ]) -- ∀[ x ] ∀[ y ] ∀[ z ]                          x < y ⇔ x + z < y + z
    (item-5 : [ Item-5 ]) -- ∀[ x ] ∀[ y ]        0f < x + y          ⇒ (0f < x) ⊔ (0f < y)
    (item-9 : [ Item-9 ]) -- ∀[ x ] ∀[ y ] ∀[ z ] 0f < z              ⇒   (x < y ⇔ x · z < y · z)
    where
    abstract
      +-<-ext       : ∀ x y z w → [ (x + y) < (z + w) ⇒ (x < z) ⊔ (y < w) ]; _ : [ is-+-<-Extensional _+_ _<_ ]; _ = +-<-ext
      ·-preserves-< : ∀ x y z   → [  0f < z ⇒  x < y  ⇒ (x · z) < (y · z) ]; _ : [ operation _·_ preserves _<_ when (λ z → 0f < z) ]; _ = ·-preserves-<
      private
        item-4'     : ∀ x y     → [      0f <  x - y  ⇒  y < x            ]
        lemma       : ∀ x y z w → (z + w) + ((- x) + (- y)) ≡ (z - x) + (w - y)

      item-4' x y = snd (
        0f     <  x - y         ⇒ᵖ⟨ fst (item-4 0f (x + (- y)) y) ⟩
        0f + y < (x - y) + y    ⇒ᵖ⟨ transport (λ i → [ snd (+-identity y) i < sym (+-assoc x (- y) y) i ]) ⟩
             y <  x + (- y + y) ⇒ᵖ⟨ transport (λ i → [ y < x + snd (+-inv y) i ]) ⟩
             y <  x + 0f        ⇒ᵖ⟨ transport (λ i → [ y < fst (+-identity x) i ]) ⟩
             y <  x ◼ᵖ)

      lemma x y z w = (
        -- NOTE: there has to be a shorter way to to this kind of calculations ...
        --       also I got not much introspection while creating the paths
        (z +   w) + ((- x)  + (- y))   ≡⟨ ( λ i → +-assoc z w ((- x)  + (- y)) (~ i)) ⟩
        (z + ( w  + ((- x)  + (- y)))) ≡⟨ ( λ i → z + (+-assoc w (- x) (- y) i)) ⟩
        (z + ((w  +  (- x)) + (- y)))  ≡⟨ ( λ i → z + ((+-comm w (- x) i) + (- y)) ) ⟩
        (z + (((- x) +  w)  + (- y)))  ≡⟨ ( λ i → z + (+-assoc (- x) w (- y) (~ i))) ⟩
        (z + (( - x) + (w - y)))       ≡⟨ ( λ i → +-assoc z (- x) (w  - y) i ) ⟩
        (z - x) + (w - y) ∎)

      -- 6. (†)
      -- In order to show (†), suppose x + y < z + w.
      -- So, by item 4, we get (x + y) − (x + y) < (z + w) − (x + y), that is, 0 < (z − x) + (w − y).
      -- By item 5, (0 < z − x) ∨ (0 < w − y), and so by item 4 in either case, we get x < z ∨ y < w.
      +-<-ext x y z w = snd (
        (x + y)           < (z + w)                     ⇒ᵖ⟨ fst (item-4 (x + y) (z + w) (- (x + y))) ⟩
        (x + y) - (x + y) < (z + w) - (x + y)           ⇒ᵖ⟨ transport (λ i → [ +-rinv (x + y) i < (z + w) + (-dist x y) i ]) ⟩
                       0f < (z +   w) + ((- x) + (- y)) ⇒ᵖ⟨ transport (λ i → [ 0f < lemma x y z w i ]) ⟩
                       0f < (z - x) + (w - y)           ⇒ᵖ⟨ item-5 (z - x) (w - y) ⟩
        (0f < z - x) ⊔ (0f < w - y)                     ⇒ᵖ⟨ (λ q → case q as (0f < z - x) ⊔ (0f < w - y) ⇒ ((x < z) ⊔ (y < w)) of λ
                                                            { (inl p) → inlᵖ (item-4' z x p)
                                                            ; (inr p) → inrᵖ (item-4' w y p)
                                                            }) ⟩
        ( x < z    ) ⊔ ( y < w    ) ◼ᵖ)

      -- 6. (∗)
      ·-preserves-< x y z 0<z = item-9 x y z 0<z .fst


module STLC.Examples where

-- This file contains a few example programs for the definitional
-- interpreter for STLC in Section 2.

open import Data.Maybe.Base
open import Data.List.Base
open import Data.List.Relation.Unary.Any
open import Data.List.Relation.Unary.All
open import Data.List.Membership.Propositional
open import Data.Integer hiding (suc)
open import Relation.Binary.PropositionalEquality -- for de Bruijn numerals

open import STLC.Semantics

-- The identity function: λ x . x
--
-- Variables are represented as de Bruijn indices, assumed to have
-- been elaborated from a surface language with names to the nameless
-- representation used in the abstract syntax in `STLC.Semantics`.
idexpr : Expr [] (unit ⇒ unit)
idexpr = ƛ (var (here refl))

-- id () = ()
test-idexpr : eval 2 (idexpr · unit) [] ≡ just unit
test-idexpr = refl

-- curried addition: λ x . λ y . y + x
curry+ : Expr [] (int ⇒ (int ⇒ int))
curry+ = ƛ (ƛ (iop _+_ (var (here refl)) (var (there (here refl)))))

-- 1 + 1 = 2
test-curry+ : eval 3 ((curry+ · (num (+ 1))) · (num (+ 1))) [] ≡ just (num (+ 2))
test-curry+ = refl


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

data AB : Set where
  A B : AB

foo : Nat → AB → AB
foo 0       t = A
foo (suc n) t = foo n A -- NB tail-recursive

test : foo 100000 A ≡ A
test = refl -- memory blows up here

data N : Set where
  z : N
  s : N → N

produce : Nat → AB → N
produce 0       t = z
produce (suc n) t = s (produce n A)

consume : N → AB → AB
consume z     t = t
consume (s n) t = consume n A

test₁ : consume (produce 100000 B) A ≡ A
test₁ = refl


module Structure.Category.Functor.Contravariant where

open import Logic.Predicate
import      Lvl
open import Structure.Category.Functor
open import Structure.Category.Dual
open import Structure.Category
open import Structure.Setoid
open import Type

private variable ℓ ℓₒ ℓₘ ℓₗₒ ℓₗₘ ℓᵣₒ ℓᵣₘ ℓₑ ℓₗₑ ℓᵣₑ : Lvl.Level
private variable Obj Objₗ Objᵣ : Type{ℓ}
private variable Morphism Morphismₗ Morphismᵣ : Objₗ → Objᵣ → Type{ℓ}

module _
  ⦃ morphism-equivₗ : ∀{x y : Objₗ} → Equiv{ℓₗₑ}(Morphismₗ x y) ⦄
  ⦃ morphism-equivᵣ : ∀{x y : Objᵣ} → Equiv{ℓᵣₑ}(Morphismᵣ x y) ⦄
  (Categoryₗ : Category(Morphismₗ))
  (Categoryᵣ : Category(Morphismᵣ))
  where

  ContravariantFunctor : (Objₗ → Objᵣ) → Type
  ContravariantFunctor = Functor (dualCategory Categoryₗ) Categoryᵣ
  module ContravariantFunctor = Functor{Categoryₗ = dualCategory Categoryₗ}{Categoryᵣ = Categoryᵣ}

module _
  ⦃ morphism-equiv : ∀{x y : Obj} → Equiv{ℓₑ}(Morphism x y) ⦄
  (Category : Category(Morphism))
  where

  ContravariantEndofunctor = ContravariantFunctor(Category)(Category)
  module ContravariantEndofunctor = ContravariantFunctor(Category)(Category)

_→ᶜᵒⁿᵗʳᵃᵛᵃʳⁱᵃⁿᵗᶠᵘⁿᶜᵗᵒʳ_ : CategoryObject{ℓₗₒ}{ℓₗₘ}{ℓₗₑ} → CategoryObject{ℓᵣₒ}{ℓᵣₘ}{ℓᵣₑ} → Type
catₗ →ᶜᵒⁿᵗʳᵃᵛᵃʳⁱᵃⁿᵗᶠᵘⁿᶜᵗᵒʳ catᵣ = ∃(ContravariantFunctor (CategoryObject.category(catₗ)) ((CategoryObject.category(catᵣ))))

⟲ᶜᵒⁿᵗʳᵃᵛᵃʳⁱᵃⁿᵗᶠᵘⁿᶜᵗᵒʳ_ : CategoryObject{ℓₒ}{ℓₘ}{ℓₑ} → Type
⟲ᶜᵒⁿᵗʳᵃᵛᵃʳⁱᵃⁿᵗᶠᵘⁿᶜᵗᵒʳ cat = cat →ᶜᵒⁿᵗʳᵃᵛᵃʳⁱᵃⁿᵗᶠᵘⁿᶜᵗᵒʳ cat

module _ (C₁ C₂ : CategoryObject{ℓₒ}{ℓₘ}{ℓₑ}) where
  open import Logic.Propositional
  open import Structure.Relator.Equivalence

  open CategoryObject ⦃ … ⦄
  open Category ⦃ … ⦄
  open ArrowNotation
  open Functor ⦃ … ⦄
  private open module MorphismEquiv₁ {x}{y} = Equivalence (Equiv-equivalence ⦃ morphism-equiv ⦃ C₁ ⦄ {x}{y} ⦄) using ()
  private open module MorphismEquiv₂ {x}{y} = Equivalence (Equiv-equivalence ⦃ morphism-equiv ⦃ C₂ ⦄ {x}{y} ⦄) using ()

  instance _ = C₁
  instance _ = C₂

  contravariant-functor-def-dual : (dual(C₁) →ᶠᵘⁿᶜᵗᵒʳ C₂) ↔ (C₁ →ᶠᵘⁿᶜᵗᵒʳ dual(C₂))
  contravariant-functor-def-dual = [↔]-intro l r where
    l : (dual(C₁) →ᶠᵘⁿᶜᵗᵒʳ C₂) ← (C₁ →ᶠᵘⁿᶜᵗᵒʳ dual(C₂))
    ∃.witness (l ([∃]-intro F)) = F
    Functor.map           (∃.proof (l _)) = map
    Functor.map-function  (∃.proof (l _)) = map-function
    Functor.op-preserving (∃.proof (l _)) = op-preserving
    Functor.id-preserving (∃.proof (l _)) = id-preserving

    r : (dual(C₁) →ᶠᵘⁿᶜᵗᵒʳ C₂) → (C₁ →ᶠᵘⁿᶜᵗᵒʳ dual(C₂))
    ∃.witness (r ([∃]-intro F)) = F
    Functor.map           (∃.proof (r _)) = map
    Functor.map-function  (∃.proof (r _)) = map-function
    Functor.op-preserving (∃.proof (r _)) = op-preserving
    Functor.id-preserving (∃.proof (r _)) = id-preserving


module Adjoint where

open import Level
open import Data.Product

open import Basic
open import Category
import Functor
import Nat

open Category.Category
open Functor.Functor
open Nat.Nat
open Nat.Export

record Adjoint {C₀ C₁ ℓ D₀ D₁ ℓ′} {C : Category C₀ C₁ ℓ} {D : Category D₀ D₁ ℓ′} (F : Functor.Functor C D) (G : Functor.Functor D C) : Set (suc ℓ ⊔ suc C₁ ⊔ C₀ ⊔ suc ℓ′ ⊔ suc D₁ ⊔ D₀) where
  field
    adjunction : {x : Obj C} {a : Obj D} → Setoids [ LiftSetoid {b = C₁ ⊔ D₁} {ℓ′ = ℓ ⊔ ℓ′} (Homsetoid D (fobj F x) a) ≅ LiftSetoid {b = C₁ ⊔ D₁} {ℓ′ = ℓ ⊔ ℓ′} (Homsetoid C x (fobj G a)) ]

  adjunct-→-Map : {x : Obj C} {a : Obj D} → Map.Map (Homsetoid D (fobj F x) a) (Homsetoid C x (fobj G a))
  adjunct-→-Map {x} {a} = lowerMap {d = C₁ ⊔ D₁} {ℓ″ = ℓ ⊔ ℓ′} (_[_≅_].map-→ (adjunction {x} {a}))

  adjunct-←-Map : {x : Obj C} {a : Obj D} → Map.Map (Homsetoid C x (fobj G a)) (Homsetoid D (fobj F x) a)
  adjunct-←-Map {x} {a} = lowerMap {d = C₁ ⊔ D₁} {ℓ″ = ℓ ⊔ ℓ′} (_[_≅_].map-← (adjunction {x} {a}))

  adjunct-→ : {x : Obj C} {a : Obj D} → Hom D (fobj F x) a → Hom C x (fobj G a)
  adjunct-→ f = Map.mapping (lowerMap {d = C₁ ⊔ D₁} {ℓ″  = ℓ ⊔ ℓ′} (_[_≅_].map-→ adjunction)) f

  adjunct-← : {x : Obj C} {a : Obj D} → Hom C x (fobj G a) → Hom D (fobj F x) a
  adjunct-← f = Map.mapping (lowerMap {d = C₁ ⊔ D₁} {ℓ″  = ℓ ⊔ ℓ′} (_[_≅_].map-← adjunction)) f

  adjunct-→←≈id : {x : Obj C} {a : Obj D} {f : Hom C x (fobj G a)} → C [ adjunct-→ (adjunct-← f) ≈ f ]
  adjunct-→←≈id {x} {a} {f} = lower (proj₁ (_[_≅_].proof adjunction) (lift f))

  adjunct-←→≈id : {x : Obj C} {a : Obj D} {f : Hom D (fobj F x) a} → D [ adjunct-← (adjunct-→ f) ≈ f ]
  adjunct-←→≈id {x} {a} {f} = lower (proj₂ (_[_≅_].proof adjunction) (lift f))

  field
    natural-in-→-C : {x y : Obj C} {a : Obj D} {f : Hom C y x} →
      Setoids [ Setoids [ liftMap {d = C₁ ⊔ D₁} {ℓ″  = ℓ ⊔ ℓ′} (component HomNat[ C ][ f ,-] (fobj G a)) ∘ _[_≅_].map-→ adjunction ] ≈ Setoids [ _[_≅_].map-→ adjunction ∘ liftMap {d = C₁ ⊔ D₁} {ℓ″  = ℓ ⊔ ℓ′} (component HomNat[ D ][ fmap F f ,-] a) ] ]
    natural-in-→-D : {x : Obj C} {a b : Obj D} {f : Hom D a b} →
      Setoids [ Setoids [ liftMap {d = C₁ ⊔ D₁} {ℓ″  = ℓ ⊔ ℓ′} (component HomNat[ C ][-, fmap G f ] x) ∘ _[_≅_].map-→ adjunction ] ≈ Setoids [ _[_≅_].map-→ adjunction ∘ liftMap {d = C₁ ⊔ D₁} {ℓ″  = ℓ ⊔ ℓ′} (component HomNat[ D ][-, f ] (fobj F x)) ] ]

  natural-in-←-C : {x y : Obj C} {a : Obj D} {f : Hom C y x} →
    Setoids [ Setoids [ liftMap {d = C₁ ⊔ D₁} {ℓ″  = ℓ ⊔ ℓ′} (component HomNat[ D ][ fmap F f ,-] a) ∘ _[_≅_].map-← adjunction ] ≈ Setoids [ _[_≅_].map-← adjunction ∘ liftMap {d = C₁ ⊔ D₁} {ℓ″  = ℓ ⊔ ℓ′} (component HomNat[ C ][ f ,-] (fobj G a)) ] ]
  natural-in-←-C {x} {y} {a} {f} = begin⟨ Setoids ⟩
    Setoids [ Ffa ∘ _[_≅_].map-← adjunction ] ≈⟨ ≈-composite Setoids {f = Ffa} {g = Setoids [ Setoids [ adj← ∘ fGa ] ∘ _[_≅_].map-→ adjunction ]} {h = adj→₂} {i = adj→₂} lem-1 (refl-hom Setoids {f = _[_≅_].map-← adjunction}) ⟩
    Setoids [ Setoids [ adj← ∘ fGa ] ∘ Setoids [ _[_≅_].map-→ adjunction ∘ _[_≅_].map-← adjunction ] ] ≈⟨ ≈-composite Setoids {f = Setoids [ adj← ∘ fGa ]} {g = Setoids [ adj← ∘ fGa ]} {h = Setoids [ _[_≅_].map-→ adjunction ∘ _[_≅_].map-← adjunction ]} {i = id Setoids} (refl-hom Setoids {f = Setoids [ adj← ∘ fGa ]}) (proj₁ (_[_≅_].proof adjunction)) ⟩
    Setoids [ adj← ∘ fGa ]
    ∎
    where
      Ffa = liftMap {d = C₁ ⊔ D₁} {ℓ″  = ℓ ⊔ ℓ′} (component HomNat[ D ][ fmap F f ,-] a)
      fGa = liftMap {d = C₁ ⊔ D₁} {ℓ″  = ℓ ⊔ ℓ′} (component HomNat[ C ][ f ,-] (fobj G a))
      adj← = _[_≅_].map-← adjunction
      adj→₂ = _[_≅_].map-← adjunction
      adj→ = _[_≅_].map-→ adjunction

      lem-1 : Setoids [ Ffa ≈ Setoids [ Setoids [ adj← ∘ fGa ] ∘ _[_≅_].map-→ adjunction ] ]
      lem-1 = begin⟨ Setoids ⟩
        Ffa ≈⟨ leftId Setoids {f = Ffa} ⟩
        Setoids [ id Setoids ∘ Ffa ] ≈⟨ ≈-composite Setoids {f = id Setoids} {g = Setoids [ adj← ∘ adj→ ]} {h = Ffa} {i = Ffa} (sym-hom Setoids {f = Setoids [ adj← ∘ adj→ ]} {g = id Setoids} (proj₂ (_[_≅_].proof adjunction))) (refl-hom Setoids {f = Ffa}) ⟩
        Setoids [ adj← ∘ Setoids [ adj→ ∘ Ffa ] ] ≈⟨ ≈-composite Setoids {f = adj←} {g = adj←} {h = Setoids [ adj→ ∘ Ffa ]} {i = Setoids [ fGa ∘ _[_≅_].map-→ adjunction ]} (refl-hom Setoids {f = adj←}) (sym-hom Setoids {f = Setoids [ fGa ∘ _[_≅_].map-→ adjunction ]} {g = Setoids [ adj→ ∘ Ffa ]} natural-in-→-C) ⟩
        Setoids [ Setoids [ _[_≅_].map-← adjunction ∘ liftMap {d = C₁ ⊔ D₁} {ℓ″  = ℓ ⊔ ℓ′} (component HomNat[ C ][ f ,-] (fobj G a)) ] ∘ _[_≅_].map-→ adjunction ]
        ∎

  natural-in-←-D : {x : Obj C} {a b : Obj D} {f : Hom D a b} →
    Setoids [ Setoids [ liftMap {d = C₁ ⊔ D₁} {ℓ″  = ℓ ⊔ ℓ′} (component HomNat[ D ][-, f ] (fobj F x)) ∘ _[_≅_].map-← adjunction ] ≈ Setoids [ _[_≅_].map-← adjunction ∘ liftMap {d = C₁ ⊔ D₁} {ℓ″  = ℓ ⊔ ℓ′} (component HomNat[ C ][-, fmap G f ] x) ] ]
  natural-in-←-D {x} {a} {b} {f} = begin⟨ Setoids ⟩
    Setoids [ fFx ∘ adj←a ] ≈⟨ ≈-composite Setoids {f = fFx} {g = Setoids [ Setoids [ adj←b ∘ Gfx ] ∘ adj→a ]} {h = adj←a} {i = adj←a} lem-1 (refl-hom Setoids {f = adj←a}) ⟩
    Setoids [ Setoids [ adj←b ∘ Gfx ] ∘ Setoids [ adj→a ∘ adj←a ] ] ≈⟨ ≈-composite Setoids {f = Setoids [ adj←b ∘ Gfx ]} {g = Setoids [ adj←b ∘ Gfx ]} {h = Setoids [ adj→a ∘ adj←a ]} {i = id Setoids} (refl-hom Setoids {f = Setoids [ adj←b ∘ Gfx ]}) (proj₁ (_[_≅_].proof adjunction)) ⟩
    Setoids [ adj←b ∘ Gfx ]
    ∎
    where
      fFx = liftMap {d = C₁ ⊔ D₁} {ℓ″  = ℓ ⊔ ℓ′} (component HomNat[ D ][-, f ] (fobj F x))
      Gfx = liftMap {d = C₁ ⊔ D₁} {ℓ″  = ℓ ⊔ ℓ′} (component HomNat[ C ][-, fmap G f ] x)
      adj←a = _[_≅_].map-← adjunction
      adj←b = _[_≅_].map-← adjunction
      adj→a = _[_≅_].map-→ adjunction
      adj→b = _[_≅_].map-→ adjunction

      lem-1 : Setoids [ fFx ≈ Setoids [ Setoids [ adj←b ∘ Gfx ] ∘ adj→a ] ]
      lem-1 = begin⟨ Setoids ⟩
        fFx ≈⟨ ≈-composite Setoids {f = id Setoids} {g = Setoids [ adj←b ∘ adj→b ]} {h = fFx} {i = fFx} (sym-hom Setoids {f = Setoids [ adj←b ∘ adj→b ]} {g = id Setoids} (proj₂ (_[_≅_].proof adjunction))) (refl-hom Setoids {f = fFx}) ⟩
        Setoids [ adj←b ∘ Setoids [ adj→b ∘ fFx ] ] ≈⟨ ≈-composite Setoids {f = adj←b} {g = adj←b} {h = Setoids [ adj→b ∘ fFx ]} {i = Setoids [ Gfx ∘ adj→a ]} (refl-hom Setoids {f = adj←b}) (sym-hom Setoids {f = Setoids [ Gfx ∘ adj→a ]} {g = Setoids [ adj→b ∘ fFx ]} natural-in-→-D) ⟩
        Setoids [ Setoids [ adj←b ∘ Gfx ] ∘ adj→a ]
        ∎

  naturality-point-←-C : {x y : Obj C} {a : Obj D} {f : Hom C y x} {k : Hom C x (fobj G a)} → D [ D [ adjunct-← k ∘ fmap F f ] ≈ adjunct-← (C [ k ∘ f ]) ]
  naturality-point-←-C = λ {x} {y} {a} {f} {k} → lower (natural-in-←-C (lift k))

  naturality-point-→-C : {x y : Obj C} {a : Obj D} {f : Hom C y x} {k : Hom D (fobj F x) a} → C [ C [ adjunct-→ k ∘ f ] ≈ adjunct-→ (D [ k ∘ fmap F f ]) ]
  naturality-point-→-C = λ {x} {y} {a} {f} {k} → lower (natural-in-→-C (lift k))

  naturality-point-←-D : {x : Obj C} {a b : Obj D} {f : Hom D a b} {k : Hom C x (fobj G a)} → D [ D [ f ∘ adjunct-← k ] ≈ adjunct-← (C [ fmap G f ∘ k ]) ]
  naturality-point-←-D = λ {x} {y} {a} {f} {k} → lower (natural-in-←-D (lift k))

  naturality-point-→-D : {x : Obj C} {a b : Obj D} {f : Hom D a b} {k : Hom D (fobj F x) a} → C [ C [ fmap G f ∘ adjunct-→ k ] ≈ adjunct-→ (D [ f ∘ k ]) ]
  naturality-point-→-D = λ {x} {y} {a} {f} {k} → lower (natural-in-→-D (lift k))

open Adjoint

unit : ∀ {C₀ C₁ ℓ D₀ D₁ ℓ′} {C : Category C₀ C₁ ℓ} {D : Category D₀ D₁ ℓ′} {F : Functor.Functor C D} {G : Functor.Functor D C} → Adjoint F G → Nat.Nat (Functor.identity C) (Functor.compose G F)
unit {C₀} {C₁} {ℓ} {D₀} {D₁} {ℓ′} {C} {D} {F} {G} F⊣G = record {
  component = λ X → adjunct-→ F⊣G (id D) ;
  naturality = λ {a} {b} {f} → begin⟨ C ⟩
    C [ adjunct-→ F⊣G (id D) ∘ fmap (Functor.identity C) f ] ≈⟨ naturality-point-→-C F⊣G ⟩
    adjunct-→ F⊣G (D [ id D ∘ fmap F (fmap (Functor.identity C) f) ]) ≈⟨ Map.preserveEq (adjunct-→-Map F⊣G) (leftId D) ⟩
    adjunct-→ F⊣G (fmap F f) ≈⟨ Map.preserveEq (adjunct-→-Map F⊣G) (sym-hom D (rightId D)) ⟩
    adjunct-→ F⊣G (D [ fmap F f ∘ id D ]) ≈⟨ sym-hom C (naturality-point-→-D F⊣G) ⟩
    C [ fmap (Functor.compose G F) f ∘ adjunct-→ F⊣G (id D) ]
    ∎ }

counit : ∀ {C₀ C₁ ℓ D₀ D₁ ℓ′} {C : Category C₀ C₁ ℓ} {D : Category D₀ D₁ ℓ′} {F : Functor.Functor C D} {G : Functor.Functor D C} → Adjoint F G → Nat.Nat (Functor.compose F G) (Functor.identity D)
counit {C₀} {C₁} {ℓ} {D₀} {D₁} {ℓ′} {C} {D} {F} {G} F⊣G = record {
  component = λ X → adjunct-← F⊣G (id C) ;
  naturality = λ {a} {b} {f} → begin⟨ D ⟩
    D [ adjunct-← F⊣G (id C) ∘ fmap (Functor.compose F G) f ] ≈⟨ naturality-point-←-C F⊣G ⟩
    adjunct-← F⊣G (C [ id C ∘ fmap G f ]) ≈⟨ Map.preserveEq (adjunct-←-Map F⊣G) (leftId C) ⟩
    adjunct-← F⊣G (fmap G f) ≈⟨ Map.preserveEq (adjunct-←-Map F⊣G) (sym-hom C (rightId C)) ⟩
    adjunct-← F⊣G (C [ fmap G f ∘ id C ]) ≈⟨ sym-hom D (naturality-point-←-D F⊣G) ⟩
    D [ fmap (Functor.identity D) f ∘ adjunct-← F⊣G (id C) ]
    ∎}

Unit-universal : ∀ {C₀ C₁ ℓ D₀ D₁ ℓ′} {C : Category C₀ C₁ ℓ} {D : Category D₀ D₁ ℓ′} {F : Functor.Functor C D} {G : Functor.Functor D C} → Adjoint F G → Set _
Unit-universal {C = C} {D} {F} {G} adj = ∀ {X : Obj C} {A : Obj D} (f : Hom C X (fobj G A)) → ∃! (λ x y → D [ x ≈ y ]) (λ (h : Hom D (fobj F X) A) → C [ C [ fmap G h ∘ component (unit adj) X ] ≈ f ])

unit-universality : ∀ {C₀ C₁ ℓ D₀ D₁ ℓ′} {C : Category C₀ C₁ ℓ} {D : Category D₀ D₁ ℓ′} {F : Functor.Functor C D} {G : Functor.Functor D C} → (adj : Adjoint F G) → Unit-universal adj
unit-universality {C = C} {D} {F} {G} F⊣G {X} {A} f =
  adjunct-← F⊣G f , lem-1 , lem-2
  where
    lem-1 = begin⟨ C ⟩
      C [ fmap G (adjunct-← F⊣G f) ∘ component (unit F⊣G) X ] ≈⟨ naturality-point-→-D F⊣G ⟩
      adjunct-→ F⊣G (D [ adjunct-← F⊣G f ∘ id D ]) ≈⟨ Map.preserveEq (adjunct-→-Map F⊣G) (≈-composite D (refl-hom D) (sym-hom D (preserveId F))) ⟩
      adjunct-→ F⊣G (D [ adjunct-← F⊣G f ∘ fmap F (id C) ]) ≈⟨ sym-hom C (naturality-point-→-C F⊣G) ⟩
      C [ adjunct-→ F⊣G (adjunct-← F⊣G f) ∘ id C ] ≈⟨ rightId C ⟩
      adjunct-→ F⊣G (adjunct-← F⊣G f) ≈⟨ adjunct-→←≈id F⊣G ⟩
      f
      ∎

    lem-2 = λ {y} eq → begin⟨ D ⟩
      adjunct-← F⊣G f ≈⟨ Map.preserveEq (adjunct-←-Map F⊣G) (sym-hom C eq) ⟩
      adjunct-← F⊣G (C [ fmap G y ∘ component (unit F⊣G) X ]) ≈⟨ sym-hom D (naturality-point-←-D (F⊣G)) ⟩
      D [ y ∘ adjunct-← F⊣G (adjunct-→ F⊣G (id D)) ] ≈⟨ ≈-composite D (refl-hom D) (adjunct-←→≈id F⊣G) ⟩
      D [ y ∘ id D ] ≈⟨ rightId D ⟩
      y
      ∎

Counit-universal : ∀ {C₀ C₁ ℓ D₀ D₁ ℓ′} {C : Category C₀ C₁ ℓ} {D : Category D₀ D₁ ℓ′} {F : Functor.Functor C D} {G : Functor.Functor D C} → Adjoint F G → Set _
Counit-universal {C = C} {D} {F} {G} adj = ∀ {X : Obj C} {A : Obj D} (h : Hom D (fobj F X) A) → ∃! (λ x y → C [ x ≈ y ]) (λ (f : Hom C X (fobj G A)) → D [ D [ component (counit adj) A ∘ fmap F f ] ≈ h ])

counit-universality : ∀ {C₀ C₁ ℓ D₀ D₁ ℓ′} {C : Category C₀ C₁ ℓ} {D : Category D₀ D₁ ℓ′} {F : Functor.Functor C D} {G : Functor.Functor D C} → (adj : Adjoint F G) → Counit-universal adj
counit-universality {C = C} {D} {F} {G} F⊣G = λ {X} {A} h →
  adjunct-→ F⊣G h , lem-1 , lem-2
  where
    lem-1 = λ {A} {h} → begin⟨ D ⟩
      D [ component (counit F⊣G) A ∘ fmap F (adjunct-→ F⊣G h) ] ≈⟨ naturality-point-←-C F⊣G ⟩
      adjunct-← F⊣G (C [ id C ∘ adjunct-→ F⊣G h ]) ≈⟨ Map.preserveEq (adjunct-←-Map F⊣G) (leftId C) ⟩
      adjunct-← F⊣G (adjunct-→ F⊣G h) ≈⟨ adjunct-←→≈id F⊣G ⟩
      h
      ∎

    lem-2 = λ {A} {h} {y} eq → begin⟨ C ⟩
      adjunct-→ F⊣G h ≈⟨ Map.preserveEq (adjunct-→-Map F⊣G) (sym-hom D eq) ⟩
      adjunct-→ F⊣G (D [ component (counit F⊣G) A ∘ fmap F y ]) ≈⟨ sym-hom C (naturality-point-→-C F⊣G) ⟩
      C [ adjunct-→ F⊣G (component (counit F⊣G) A) ∘ y ] ≈⟨ ≈-composite C (adjunct-→←≈id F⊣G) (refl-hom C) ⟩
      C [ id C ∘ y ] ≈⟨ leftId C ⟩
      y
      ∎

TriangularL : ∀ {C₀ C₁ ℓ D₀ D₁ ℓ′} {C : Category C₀ C₁ ℓ} {D : Category D₀ D₁ ℓ′} {F : Functor.Functor C D} {G : Functor.Functor D C} → Nat.Nat (Functor.identity C) (Functor.compose G F) → Nat.Nat (Functor.compose F G) (Functor.identity D) → Set _
TriangularL {C = C} {D} {F} {G} η ε = [ C , D ] [ Nat.compose (Nat.compose Nat.leftIdNat→ (ε N∘F F)) (Nat.compose (Nat.assocNat← {F = F} {G} {F}) (Nat.compose (F F∘N η) Nat.rightIdNat←)) ≈ id [ C , D ] ]

triangularL-point : ∀ {C₀ C₁ ℓ D₀ D₁ ℓ′} {C : Category C₀ C₁ ℓ} {D : Category D₀ D₁ ℓ′} {F : Functor.Functor C D} {G : Functor.Functor D C} → (η : Nat.Nat (Functor.identity C) (Functor.compose G F)) → (ε : Nat.Nat (Functor.compose F G) (Functor.identity D)) → {a : Obj C} → (triL : TriangularL {F = F} {G} η ε) → D [ D [ component (ε N∘F F) a ∘ component (F F∘N η) a ] ≈ id D {fobj F a} ]
triangularL-point {C = C} {D} {F} {G} η ε {a} triL = begin⟨ D ⟩
  D [ component (ε N∘F F) a ∘ component (F F∘N η) a ] ≈⟨ sym-hom D (≈-composite D (leftId D) (trans-hom D (leftId D) (rightId D))) ⟩
  D [ D [ id D ∘ component (ε N∘F F) a ] ∘ D [ id D ∘ D [ component (F F∘N η) a ∘ id D ] ] ] ≈⟨ triL {a} ⟩
  id D
  ∎

triangularL : ∀ {C₀ C₁ ℓ D₀ D₁ ℓ′} {C : Category C₀ C₁ ℓ} {D : Category D₀ D₁ ℓ′} {F : Functor.Functor C D} {G : Functor.Functor D C} → (adj : Adjoint F G) → TriangularL {F = F} {G} (unit adj) (counit adj)
triangularL {C = C} {D} {F} {G} adj = λ {a} →
  begin⟨ D ⟩
    component (Nat.compose εF (Nat.compose (Nat.assocNat← {F = F} {G} {F}) Fη)) a
  ≈⟨ ≈-composite D (leftId D) (trans-hom D (leftId D) (rightId D)) ⟩
    D [ component ε (fobj F a) ∘ fmap F (component η a) ]
  ≈⟨ proj₁ (proj₂ (counit-universality adj (id D))) ⟩
    id D
  ≈⟨ refl-hom D ⟩
    component (id [ C , D ] {F}) a
  ∎
  where
    ε = counit adj
    η = unit adj
    εF = Nat.compose Nat.leftIdNat→ (counit adj N∘F F)
    Fη = Nat.compose {F = F} (F F∘N unit adj) Nat.rightIdNat←

TriangularR : ∀ {C₀ C₁ ℓ D₀ D₁ ℓ′} {C : Category C₀ C₁ ℓ} {D : Category D₀ D₁ ℓ′} {F : Functor.Functor C D} {G : Functor.Functor D C} → Nat.Nat (Functor.identity C) (Functor.compose G F) → Nat.Nat (Functor.compose F G) (Functor.identity D) → Set _
TriangularR {C = C} {D} {F} {G} η ε = [ D , C ] [ Nat.compose (Nat.compose Nat.rightIdNat→ (G F∘N ε)) (Nat.compose (Nat.assocNat→ {F = G} {F} {G}) (Nat.compose (η N∘F G) Nat.leftIdNat←)) ≈ id [ D , C ] ]

triangularR : ∀ {C₀ C₁ ℓ D₀ D₁ ℓ′} {C : Category C₀ C₁ ℓ} {D : Category D₀ D₁ ℓ′} {F : Functor.Functor C D} {G : Functor.Functor D C} → (adj : Adjoint F G) → TriangularR {F = F} (unit adj) (counit adj)
triangularR {C = C} {D} {F} {G} adj = λ {a} → begin⟨ C ⟩
  component (Nat.compose Gε (Nat.compose (Nat.assocNat→ {F = G} {F} {G}) ηG)) a ≈⟨ ≈-composite C (leftId C) (trans-hom C (leftId C) (rightId C)) ⟩
  C [ fmap G (component ε a) ∘ component η (fobj G a) ] ≈⟨ proj₁ (proj₂ (unit-universality adj (id C))) ⟩
  id C ≈⟨ refl-hom C ⟩
  component (id [ D , C ] {G}) a
  ∎
  where
    ε = counit adj
    η = unit adj
    Gε = Nat.compose {H = G} Nat.rightIdNat→ (G F∘N counit adj)
    ηG = Nat.compose (unit adj N∘F G) Nat.leftIdNat←

triangularR-point : ∀ {C₀ C₁ ℓ D₀ D₁ ℓ′} {C : Category C₀ C₁ ℓ} {D : Category D₀ D₁ ℓ′} {F : Functor.Functor C D} {G : Functor.Functor D C} → (η : Nat.Nat (Functor.identity C) (Functor.compose G F)) → (ε : Nat.Nat (Functor.compose F G) (Functor.identity D)) → {a : Obj D} → (triR : TriangularR {F = F} {G} η ε) → C [ C [ component (G F∘N ε) a ∘ component (η N∘F G) a ] ≈ id C {fobj G a} ]
triangularR-point {C = C} {D} {F} {G} η ε {a} triR = begin⟨ C ⟩
  C [ component (G F∘N ε) a ∘ component (η N∘F G) a ] ≈⟨ sym-hom C (≈-composite C (leftId C) (trans-hom C (leftId C) (rightId C))) ⟩
  C [ C [ id C ∘ component (G F∘N ε) a ] ∘ C [ id C ∘ C [ component (η N∘F G) a ∘ id C ] ] ] ≈⟨ triR {a = a} ⟩
  id C
  ∎

unit-triangular-holds-adjoint : ∀ {C₀ C₁ ℓ D₀ D₁ ℓ′} {C : Category C₀ C₁ ℓ} {D : Category D₀ D₁ ℓ′} {F : Functor.Functor C D} {G : Functor.Functor D C} → (η : Nat.Nat (Functor.identity C) (Functor.compose G F)) → (ε : Nat.Nat (Functor.compose F G) (Functor.identity D)) → TriangularL {F = F} η ε → TriangularR {F = F} η ε → Adjoint F G
unit-triangular-holds-adjoint {C = C} {D} {F} {G} η ε triL triR = record {
  adjunction = λ {x} {a} → record {
    map-→ = record {
      mapping = λ Fx→a → lift (C [ fmap G (lower Fx→a) ∘ component η x ]) ;
      preserveEq = λ {x′} {y} x₂ → lift (≈-composite C (Functor.preserveEq G (lower x₂)) (refl-hom C)) } ;
    map-← = record {
      mapping = λ x→Ga → lift (D [ component ε a ∘ fmap F (lower x→Ga) ]) ;
      preserveEq = λ {x′} {y} x₂ → lift (≈-composite D (refl-hom D) (Functor.preserveEq F (lower x₂))) } ;
    proof = p1 , p2 } ;
  natural-in-→-C = λ {x} {y} {a} {f} Fx→a → lift (begin⟨ C ⟩
    C [ C [ fmap G (lower Fx→a) ∘ component η x ] ∘ f ] ≈⟨ assoc C ⟩
    C [ fmap G (lower Fx→a) ∘ C [ component η x ∘ f ] ] ≈⟨ ≈-composite C (refl-hom C) (naturality η) ⟩
    C [ fmap G (lower Fx→a) ∘ C [ fmap G (fmap F f) ∘ component η y ] ] ≈⟨ sym-hom C (assoc C) ⟩
    C [ C [ fmap G (lower Fx→a) ∘ fmap G (fmap F f) ] ∘ component η y ] ≈⟨ ≈-composite C (sym-hom C (preserveComp G)) (refl-hom C) ⟩
    C [ fmap G (D [ lower Fx→a ∘ fmap F f ]) ∘ component η y ]
    ∎) ;
  natural-in-→-D = λ {x} {a} {b} {f} Fx→a → lift (begin⟨ C ⟩
    C [ fmap G f ∘ C [ fmap G (lower Fx→a) ∘ component η x ] ] ≈⟨ sym-hom C (assoc C) ⟩
    C [ C [ fmap G f ∘ fmap G (lower Fx→a) ] ∘ component η x ] ≈⟨ ≈-composite C (sym-hom C (preserveComp G)) (refl-hom C) ⟩
    C [ fmap G (D [ f ∘ lower Fx→a ]) ∘ component η x ]
    ∎) }

  where
    p1 = λ {x} {a} x→Ga → lift (begin⟨ C ⟩
      C [ fmap G (D [ component ε a ∘ fmap F (lower x→Ga) ]) ∘ component η x ] ≈⟨ ≈-composite C (preserveComp G) (refl-hom C) ⟩
      C [ C [ fmap G (component ε a) ∘ fmap G (fmap F (lower x→Ga)) ] ∘ component η x ] ≈⟨ assoc C ⟩
      C [ fmap G (component ε a) ∘ C [ fmap G (fmap F (lower x→Ga)) ∘ component η x ] ] ≈⟨ ≈-composite C (refl-hom C) (sym-hom C (naturality η)) ⟩
      C [ fmap G (component ε a) ∘ C [ component η (fobj G a) ∘ (lower x→Ga) ] ] ≈⟨ sym-hom C (assoc C) ⟩
      C [ C [ fmap G (component ε a) ∘ component η (fobj G a) ] ∘ (lower x→Ga) ] ≈⟨ ≈-composite C (triangularR-point {F = F} η ε {a = a} triR) (refl-hom C) ⟩
      C [ id C ∘ (lower x→Ga) ] ≈⟨ leftId C ⟩
      lower x→Ga
      ∎)

    p2 = λ {x} {a} Fx→a → lift (begin⟨ D ⟩
      D [ component ε a ∘ fmap F (C [ fmap G (lower Fx→a) ∘ component η x ]) ] ≈⟨ ≈-composite D (refl-hom D) (preserveComp F) ⟩
      D [ component ε a ∘ D [ fmap F (fmap G (lower Fx→a)) ∘ fmap F (component η x) ] ] ≈⟨ sym-hom D (assoc D) ⟩
      D [ D [ component ε a ∘ fmap F (fmap G (lower Fx→a)) ] ∘ fmap F (component η x) ] ≈⟨ ≈-composite D (naturality ε) (refl-hom D) ⟩
      D [ D [ lower Fx→a ∘ component ε (fobj F x) ] ∘ fmap F (component η x) ] ≈⟨ assoc D ⟩
      D [ lower Fx→a ∘ D [ component ε (fobj F x) ∘ fmap F (component η x) ] ] ≈⟨ ≈-composite D (refl-hom D) (triangularL-point {F = F} η ε {a = x} triL) ⟩
      D [ lower Fx→a ∘ id D ] ≈⟨ rightId D ⟩
      lower Fx→a
      ∎)

module Export where
  _⊣_ = Adjoint


-- Fast exponentiation

module Numeric.Nat.Pow where

open import Prelude
open import Numeric.Nat.DivMod
open import Control.WellFounded
open import Tactic.Nat
open import Tactic.Cong

module _ {a} {A : Set a} {{_ : Semiring A}} where
  private
    expAcc : A → (n : Nat) → Acc _<_ n → A
    expAcc a  zero    wf = one
    expAcc a (suc n) (acc wf) with n divmod 2
    ... | qr q 0 _ eq = force (a * a) λ a² → expAcc a² q (wf q (by eq)) * a
    ... | qr q 1 _ eq = force (a * a) λ a² → expAcc a² (suc q) (wf (suc q) (by eq))
    ... | qr q (suc (suc _)) lt _ = refute lt

  infixr 8 _^′_
  _^′_ : A → Nat → A
  a ^′ n = expAcc a n (wfNat n)

  module _ (assoc : (a b c : A) → a * (b * c) ≡ (a * b) * c)
           (idr   : (a : A) → a * one ≡ a)
           (idl   : (a : A) → one * a ≡ a) where

    private
      force-eq : ∀ {a b} {A : Set a} {B : A → Set b} (x : A) {f : ∀ x → B x} {y : B x} →
                   f x ≡ y → force x f ≡ y
      force-eq x eq = forceLemma x _ ⟨≡⟩ eq

      on-assoc-l : {a b c a₁ b₁ c₁ : A} → (a * b) * c ≡ (a₁ * b₁) * c₁ → a * (b * c) ≡ a₁ * (b₁ * c₁)
      on-assoc-l eq = assoc _ _ _ ⟨≡⟩ eq ⟨≡⟩ʳ assoc _ _ _

      on-assoc-r : {a b c a₁ b₁ c₁ : A} → a * (b * c) ≡ a₁ * (b₁ * c₁) → (a * b) * c ≡ (a₁ * b₁) * c₁
      on-assoc-r eq = assoc _ _ _ ʳ⟨≡⟩ eq ⟨≡⟩ assoc _ _ _

      lem-pow-mul-l : (a : A) (n : Nat) → a * a ^ n ≡ a ^ n * a
      lem-pow-mul-l a zero    = idr _ ⟨≡⟩ʳ idl _
      lem-pow-mul-l a (suc n) = assoc _ _ _ ⟨≡⟩ (_* a) $≡ lem-pow-mul-l a n

      lem-pow-mul-distr : (a : A) (n : Nat) → (a * a) ^ n ≡ a ^ n * a ^ n
      lem-pow-mul-distr a  zero   = sym (idl _)
      lem-pow-mul-distr a (suc n) =
        (_* (a * a)) $≡ lem-pow-mul-distr a n ⟨≡⟩ʳ
        on-assoc-r (((a ^ n) *_) $≡ (on-assoc-l ((_* a) $≡ lem-pow-mul-l a n)))

      lem-pow-add-distr : (a : A) (n m : Nat) → a ^ (n + m) ≡ a ^ n * a ^ m
      lem-pow-add-distr a  zero   m = sym (idl _)
      lem-pow-add-distr a (suc n) m =
        (_* a) $≡ lem-pow-add-distr a n m ⟨≡⟩ʳ
        on-assoc-r (((a ^ n) *_) $≡ lem-pow-mul-l a m)

      lem : ∀ a n (wf : Acc _<_ n) → expAcc a n wf ≡ a ^ n
      lem a  zero   wf = refl
      lem a (suc n) (acc wf) with n divmod 2
      lem a (suc ._) (acc wf) | qr q 0 _ refl =
        force-eq (a * a) (cong (_* a) (
          lem (a * a) q (wf q (diff _ auto)) ⟨≡⟩
          lem-pow-mul-distr a q ⟨≡⟩
          lem-pow-add-distr a q q ʳ⟨≡⟩
          cong (_^_ a) {x = q + q} {y = q * 2 + 0} auto))
      lem a (suc ._) (acc wf) | qr q 1 _ refl =
        force-eq (a * a) $
          lem (a * a) (suc q) (wf (suc q) (diff _ auto)) ⟨≡⟩
          (a * a) ^ q * (a * a)
            ≡⟨ by-cong (lem-pow-mul-distr a q ⟨≡⟩ʳ lem-pow-add-distr a q q) ⟩
          a ^ (q + q) * (a * a)
            ≡⟨ by-cong {x = q + q} {y = q * 2} auto ⟩
          a ^ (q * 2) * (a * a)
            ≡⟨ by-cong (idl a) ⟩
          a ^ (q * 2) * (a ^ 1 * a)
            ≡⟨ assoc _ _ _ ⟩
          a ^ (q * 2) * a ^ 1 * a
            ≡⟨ by-cong (lem-pow-add-distr a (q * 2) 1) ⟩
          a ^ (q * 2 + 1) * a ∎
      ... | qr _ (suc (suc _)) lt _ = refute lt

    lem-^′-sound : ∀ a n → a ^′ n ≡ a ^ n
    lem-^′-sound a n = lem a n (wfNat n)

  {-# DISPLAY expAcc a n _ = a ^′ n #-}


module Numeral.Natural.Oper.Proofs.Order where

open import Functional
open import Logic
open import Logic.Propositional
open import Logic.Propositional.Theorems
open import Numeral.Natural
open import Numeral.Natural.Oper
open import Numeral.Natural.Oper.Proofs
open import Numeral.Natural.Relation.Order
open import Numeral.Natural.Relation.Order.Classical
open import Numeral.Natural.Relation.Order.Proofs
open import Relator.Equals
open import Relator.Equals.Proofs
open import Relator.Ordering.Proofs
open import Structure.Function.Domain
open import Structure.Operator
open import Structure.Operator.Properties
open import Structure.Relator.Properties
open import Syntax.Transitivity

[≤]ₗ[+] : ∀{x y : ℕ} → (x + y ≤ x) → (y ≡ 𝟎)
[≤]ₗ[+] {𝟎}               = [≤][0]ᵣ
[≤]ₗ[+] {𝐒(x)}{y} (proof) = [≤]ₗ[+] {x} ([≤]-without-[𝐒] {x + y} {x} (proof))

[≤]-with-[+]ᵣ : ∀{x y z : ℕ} → (x ≤ y) → (x + z ≤ y + z)
[≤]-with-[+]ᵣ {_}{_}{𝟎}    (proof)    = proof
[≤]-with-[+]ᵣ {_}{_}{𝐒(z)} (proof) = [≤]-with-[𝐒] ⦃ [≤]-with-[+]ᵣ {_}{_}{z} (proof) ⦄

[≤]-with-[+]ₗ : ∀{x y z : ℕ} → (x ≤ y) → (z + x ≤ z + y)
[≤]-with-[+]ₗ {.0} {𝟎}   {z } min        = reflexivity(_≤_)
[≤]-with-[+]ₗ {.0} {𝐒 y} {z}  min        = [≤]-successor([≤]-with-[+]ₗ {0}{y}{z} [≤]-minimum)
[≤]-with-[+]ₗ {𝐒 x} {𝐒 y} {z} (succ xy ) = [≤]-with-[𝐒] ⦃ [≤]-with-[+]ₗ {x} {y} {z} xy ⦄

[≤]-of-[+]ᵣ : ∀{x y : ℕ} → (y ≤ x + y)
[≤]-of-[+]ᵣ {x}   {𝟎}   = [≤]-minimum
[≤]-of-[+]ᵣ {𝟎}   {𝐒 x} = reflexivity(_≤_)
[≤]-of-[+]ᵣ {𝐒 x} {𝐒 y} = [≤]-with-[𝐒] ⦃ [≤]-of-[+]ᵣ {𝐒 x}{y} ⦄

[≤]-of-[+]ₗ : ∀{x y : ℕ} → (x ≤ x + y)
[≤]-of-[+]ₗ {𝟎}   {y}   = [≤]-minimum
[≤]-of-[+]ₗ {𝐒 x} {𝟎}   = reflexivity(_≤_)
[≤]-of-[+]ₗ {𝐒 x} {𝐒 y} = [≤]-with-[𝐒] ⦃ [≤]-of-[+]ₗ {x}{𝐒 y} ⦄

[≤]-with-[+] : ∀{x₁ y₁ : ℕ} → ⦃ _ : (x₁ ≤ y₁)⦄ → ∀{x₂ y₂ : ℕ} → ⦃ _ : (x₂ ≤ y₂)⦄ → (x₁ + x₂ ≤ y₁ + y₂)
[≤]-with-[+] {x₁} {y₁} ⦃ x1y1 ⦄ {.0}   {y₂}   ⦃ min ⦄   = transitivity(_≤_) x1y1 [≤]-of-[+]ₗ
[≤]-with-[+] {x₁} {y₁} ⦃ x1y1 ⦄ {𝐒 x₂} {𝐒 y₂} ⦃ succ p ⦄ = succ ([≤]-with-[+] {x₁} {y₁} {x₂} {y₂} ⦃ p ⦄)

[≤]-from-[+] : ∀{ℓ}{P : ℕ → Stmt{ℓ}}{x} → (∀{n} → P(x + n)) → (∀{y} → ⦃ _ : (x ≤ y) ⦄ → P(y))
[≤]-from-[+] {ℓ} {P} {𝟎}   anpxn {y}   ⦃ [≤]-minimum ⦄        = anpxn{y}
[≤]-from-[+] {ℓ} {P} {𝐒 x} anpxn {𝐒 y} ⦃ succ xy ⦄ = [≤]-from-[+] {ℓ} {P ∘ 𝐒} {x} anpxn {y} ⦃ xy ⦄

[−₀][+]-nullify2 : ∀{x y} → (x ≤ y) ↔ (x + (y −₀ x) ≡ y)
[−₀][+]-nullify2 {x}{y} = [↔]-intro (l{x}{y}) (r{x}{y}) where
  l : ∀{x y} → (x ≤ y) ← (x + (y −₀ x) ≡ y)
  l {𝟎}   {_}    _     = [≤]-minimum
  l {𝐒(_)}{𝟎}    ()
  l {𝐒(x)}{𝐒(y)} proof = [≤]-with-[𝐒] ⦃ l{x}{y} (injective(𝐒) proof) ⦄

  r : ∀{x y} → (x ≤ y) → (x + (y −₀ x) ≡ y)
  r {𝟎}   {𝟎}    proof = [≡]-intro
  r {𝟎}   {𝐒(_)} proof = [≡]-intro
  r {𝐒(_)}{𝟎}    ()
  r {𝐒(x)}{𝐒(y)} (succ proof) = [≡]-with(𝐒) (r{x}{y} (proof))

[−₀][+]-nullify2ᵣ : ∀{x y} → (x ≤ y) ↔ ((y −₀ x) + x ≡ y)
[−₀][+]-nullify2ᵣ {x}{y} = [↔]-transitivity [−₀][+]-nullify2 ([≡]-substitution (commutativity(_+_) {x}{y −₀ x}) {_≡ y})

[−₀]-when-0 : ∀{x y} → (x ≤ y) ↔ (x −₀ y ≡ 𝟎)
[−₀]-when-0 {x}{y} = [↔]-intro (l{x}{y}) (r{x}{y}) where
  l : ∀{x y} → (x ≤ y) ← (x −₀ y ≡ 𝟎)
  l {𝟎}   {_}    _     = [≤]-minimum
  l {𝐒(_)}{𝟎}    ()
  l {𝐒(x)}{𝐒(y)} proof = [≤]-with-[𝐒] ⦃ l{x}{y} proof ⦄

  r : ∀{x y} → (x ≤ y) → (x −₀ y ≡ 𝟎)
  r {𝟎}   {_}    proof = [≡]-intro
  r {𝐒(_)}{𝟎}    ()
  r {𝐒(x)}{𝐒(y)} (succ proof) = r{x}{y} (proof)

[−₀]-lesser-[𝐒]ₗ : ∀{x y} → ((x −₀ 𝐒(y)) ≤ (x −₀ y))
[−₀]-lesser-[𝐒]ᵣ : ∀{x y} → ((x −₀ y) ≤ (𝐒(x) −₀ y))

[−₀]-lesser-[𝐒]ₗ {𝟎}   {_}    = [≤]-minimum
[−₀]-lesser-[𝐒]ₗ {𝐒(_)}{𝟎}    = [≤]-of-[𝐒]
[−₀]-lesser-[𝐒]ₗ {𝐒(x)}{𝐒(y)} = [−₀]-lesser-[𝐒]ᵣ {x}{𝐒(y)}

[−₀]-lesser-[𝐒]ᵣ {𝟎}   {_}    = [≤]-minimum
[−₀]-lesser-[𝐒]ᵣ {𝐒(x)}{𝟎}    = [≤]-of-[𝐒]
[−₀]-lesser-[𝐒]ᵣ {𝐒(x)}{𝐒(y)} = [−₀]-lesser-[𝐒]ₗ {𝐒(x)}{y}

[≤][−₀][𝐒]ₗ : ∀{x y} → ((𝐒(x) −₀ y) ≤ 𝐒(x −₀ y))
[≤][−₀][𝐒]ₗ {x}   {𝟎}    = reflexivity(_≤_)
[≤][−₀][𝐒]ₗ {𝟎}   {𝐒(y)} = [≤]-minimum
[≤][−₀][𝐒]ₗ {𝐒(x)}{𝐒(y)} = [≤][−₀][𝐒]ₗ {x}{y}

[−₀][𝐒]ₗ-equality : ∀{x y} → (x ≥ y) ↔ ((𝐒(x) −₀ y) ≡ 𝐒(x −₀ y))
[−₀][𝐒]ₗ-equality = [↔]-intro l r where
  l : ∀{x y} → (x ≥ y) ← ((𝐒(x) −₀ y) ≡ 𝐒(x −₀ y))
  l {𝟎}   {𝟎}   p = [≤]-minimum
  l {𝐒 x} {𝟎}   p = [≤]-minimum
  l {𝐒 x} {𝐒 y} p = [≤]-with-[𝐒] ⦃ l{x}{y} p ⦄

  r : ∀{x y} → (x ≥ y) → ((𝐒(x) −₀ y) ≡ 𝐒(x −₀ y))
  r {x}   {.𝟎}  min       = [≡]-intro
  r {𝐒 x} {𝐒 y} (succ xy) = r xy

[−₀]-lesser : ∀{x y} → ((x −₀ y) ≤ x)
[−₀]-lesser {𝟎}   {_}    = [≤]-minimum
[−₀]-lesser {𝐒(x)}{𝟎}    = reflexivity(_≤_)
[−₀]-lesser {𝐒(x)}{𝐒(y)} = ([−₀]-lesser-[𝐒]ₗ {𝐒(x)}{y}) 🝖 ([−₀]-lesser {𝐒(x)}{y})

-- TODO: Converse is probably also true. One way to prove the equivalence is contraposition of [−₀]-comparison. Another is by [≤]-with-[+]ᵣ and some other stuff, but it seems to require more work
[−₀]-positive : ∀{x y} → (y > x) → (y −₀ x > 0)
[−₀]-positive {𝟎}   {𝐒(y)} _        = [≤]-with-[𝐒] ⦃ [≤]-minimum ⦄
[−₀]-positive {𝐒(x)}{𝐒(y)} (succ p) = [−₀]-positive {x}{y} p

[−₀]-nested-sameₗ : ∀{x y} → (x ≥ y) ↔ (x −₀ (x −₀ y) ≡ y)
[−₀]-nested-sameₗ {x}{y} = [↔]-intro (l{x}{y}) (r{x}{y}) where
  l : ∀{x y} → (x ≥ y) ← (x −₀ (x −₀ y) ≡ y)
  l {x}{y} proof =
    y             🝖[ _≤_ ]-[ [≡]-to-[≤] (symmetry(_≡_) proof) ]
    x −₀ (x −₀ y) 🝖[ _≤_ ]-[ [−₀]-lesser {x}{x −₀ y} ]
    x             🝖[ _≤_ ]-end

  r : ∀{x y} → (x ≥ y) → (x −₀ (x −₀ y) ≡ y)
  r{x}{y} x≥y =
    x −₀ (x −₀ y)              🝖[ _≡_ ]-[ [≡]-with(_−₀ (x −₀ y)) (symmetry(_≡_) ([↔]-to-[→] ([−₀][+]-nullify2 {y}{x}) (x≥y)) 🝖 commutativity(_+_) {y}{x −₀ y}) ]
    ((x −₀ y) + y) −₀ (x −₀ y) 🝖[ _≡_ ]-[ [−₀]ₗ[+]ₗ-nullify {x −₀ y}{y} ]
    y                          🝖-end

[+][−₀]-almost-associativity : ∀{x y z} → (y ≥ z) → ((x + y) −₀ z ≡ x + (y −₀ z))
[+][−₀]-almost-associativity {x} {y}   {.𝟎}  min      = [≡]-intro
[+][−₀]-almost-associativity {x} {𝐒 y} {𝐒 z} (succ p) = [+][−₀]-almost-associativity {x}{y}{z} p

[−₀][𝄩]-equality-condition : ∀{x y} → (x ≥ y) ↔ (x −₀ y ≡ x 𝄩 y)
[−₀][𝄩]-equality-condition = [↔]-intro l r where
  l : ∀{x y} → (x ≥ y) ← (x −₀ y ≡ x 𝄩 y)
  l {_}   {𝟎}   _ = min
  l {𝐒 x} {𝐒 y} p = succ(l p)

  r : ∀{x y} → (x ≥ y) → (x −₀ y ≡ x 𝄩 y)
  r min = [≡]-intro
  r (succ p) = r p

[𝄩]-intro-by[−₀] : ∀{ℓ}{P : ℕ → TYPE(ℓ)} → ∀{x y} → P(x −₀ y) → P(y −₀ x) → P(x 𝄩 y)
[𝄩]-intro-by[−₀] {x = x}{y = y} p1 p2 with [≤][>]-dichotomy {x}{y}
... | [∨]-introₗ le
  rewrite [↔]-to-[→] [−₀][𝄩]-equality-condition le
  rewrite commutativity(_𝄩_) {x}{y}
  = p2
... | [∨]-introᵣ gt
  rewrite [↔]-to-[→] [−₀][𝄩]-equality-condition ([≤]-predecessor gt)
  = p1

[𝄩]-of-𝐒ₗ : ∀{x y} → (x ≥ y) → (𝐒(x) 𝄩 y ≡ 𝐒(x 𝄩 y))
[𝄩]-of-𝐒ₗ {𝟎}   {𝟎}   = const [≡]-intro
[𝄩]-of-𝐒ₗ {𝐒 x} {𝟎}   = const [≡]-intro
[𝄩]-of-𝐒ₗ {𝐒 x} {𝐒 y} = [𝄩]-of-𝐒ₗ {x} {y} ∘ [≤]-without-[𝐒]

[𝄩]-of-𝐒ᵣ : ∀{x y} → (x ≤ y) → (x 𝄩 𝐒(y) ≡ 𝐒(x 𝄩 y))
[𝄩]-of-𝐒ᵣ {𝟎}   {𝟎}   = const [≡]-intro
[𝄩]-of-𝐒ᵣ {𝟎}   {𝐒 y} = const [≡]-intro
[𝄩]-of-𝐒ᵣ {𝐒 x} {𝐒 y} = [𝄩]-of-𝐒ᵣ {x} {y} ∘ [≤]-without-[𝐒]

[<]-with-[+]ᵣ : ∀{x y z} → (x < y) → (x + z < y + z)
[<]-with-[+]ᵣ = [≤]-with-[+]ᵣ

[<]-with-[+]ₗ : ∀{x y z} → (y < z) → (x + y < x + z)
[<]-with-[+]ₗ {x}{y}{z} = [≤]-with-[+]ₗ {𝐒 y}{z}{x}

[<]-with-[+]-weak : ∀{x₁ x₂ y₁ y₂} → ((x₁ ≤ x₂) ∧ (y₁ < y₂)) ∨ ((x₁ < x₂) ∧ (y₁ ≤ y₂)) → (x₁ + y₁ < x₂ + y₂)
[<]-with-[+]-weak ([∨]-introₗ ([∧]-intro x12 y12)) = [≤]-with-[+] ⦃ x12 ⦄ ⦃ y12 ⦄
[<]-with-[+]-weak ([∨]-introᵣ ([∧]-intro x12 y12)) = [≤]-with-[+] ⦃ x12 ⦄ ⦃ y12 ⦄

[<]-with-[+] : ∀{x₁ x₂ y₁ y₂} → (x₁ < x₂) → (y₁ < y₂) → (x₁ + y₁ < x₂ + y₂)
[<]-with-[+] x12 y12 = [≤]-predecessor ([≤]-with-[+] ⦃ x12 ⦄ ⦃ y12 ⦄)

[≤]-with-[⋅]ᵣ : ∀{a b c} → (a ≤ b) → ((a ⋅ c) ≤ (b ⋅ c))
[≤]-with-[⋅]ᵣ {c = 𝟎}   _  = [≤]-minimum
[≤]-with-[⋅]ᵣ {c = 𝐒 c} ab = [≤]-with-[+] ⦃ ab ⦄ ⦃ [≤]-with-[⋅]ᵣ {c = c} ab ⦄

[≤]-with-[⋅]ₗ : ∀{a b c} → (b ≤ c) → ((a ⋅ b) ≤ (a ⋅ c))
[≤]-with-[⋅]ₗ {a}{b}{c}
  rewrite commutativity(_⋅_) {a}{b}
  rewrite commutativity(_⋅_) {a}{c}
  = [≤]-with-[⋅]ᵣ {c = a}

[<]-with-[⋅]ᵣ : ∀{a b c} → (a < b) → ((a ⋅ 𝐒(c)) < (b ⋅ 𝐒(c)))
[<]-with-[⋅]ᵣ {c = 𝟎}   = id
[<]-with-[⋅]ᵣ {c = 𝐒 c} = [<]-with-[+] ∘ₛ [<]-with-[⋅]ᵣ {c = c}

[<]-with-[⋅]ₗ : ∀{a b c} → (b < c) → ((𝐒(a) ⋅ b) < (𝐒(a) ⋅ c))
[<]-with-[⋅]ₗ {a}{b}{c}
  rewrite commutativity(_⋅_) {𝐒(a)}{b}
  rewrite commutativity(_⋅_) {𝐒(a)}{c}
  = [<]-with-[⋅]ᵣ {c = a}

[⋅]ᵣ-growing : ∀{n c} → (1 ≤ c) → (n ≤ (c ⋅ n))
[⋅]ᵣ-growing {n}{𝐒 c} = [≤]-with-[⋅]ᵣ {1}{𝐒(c)}{n}

[⋅]ᵣ-strictly-growing : ∀{n c} → (2 ≤ c) → (𝐒(n) < (c ⋅ 𝐒(n)))
[⋅]ᵣ-strictly-growing {n} {1} (succ())
[⋅]ᵣ-strictly-growing {n} {𝐒(𝐒 c)} = [<]-with-[⋅]ᵣ {1}{𝐒(𝐒(c))}{n}

[^]-positive : ∀{a b} → ((𝐒(a) ^ b) > 0)
[^]-positive {a}{𝟎} = reflexivity(_≤_)
[^]-positive {a}{𝐒 b} =
  𝐒(a) ^ 𝐒(b)       🝖[ _≥_ ]-[]
  𝐒(a) ⋅ (𝐒(a) ^ b) 🝖[ _≥_ ]-[ [<]-with-[⋅]ₗ {a} ([^]-positive {a}{b}) ]
  𝐒(𝐒(a) ⋅ 0)       🝖[ _≥_ ]-[ succ min ]
  1                 🝖[ _≥_ ]-end

[^]ₗ-strictly-growing : ∀{n a b} → (a < b) → ((𝐒(𝐒(n)) ^ a) < (𝐒(𝐒(n)) ^ b))
[^]ₗ-strictly-growing {n} {𝟎}   {.(𝐒 b)} (succ {y = b} p) = [≤]-with-[+]ᵣ [≤]-minimum 🝖 [≤]-with-[⋅]ₗ {𝐒(𝐒(n))}{1}{𝐒(𝐒(n)) ^ b} ([^]-positive {𝐒(n)}{b})
[^]ₗ-strictly-growing {n} {𝐒 a} {.(𝐒 b)} (succ {y = b} p) = [<]-with-[⋅]ₗ {𝐒(n)} ([^]ₗ-strictly-growing {n}{a}{b} p)

[^]ₗ-growing : ∀{n a b} → ¬((n ≡ 𝟎) ∧ (a ≡ 𝟎)) → (a ≤ b) → ((n ^ a) ≤ (n ^ b))
[^]ₗ-growing {𝟎}  {𝟎}   {_}   p _ with () ← p([∧]-intro [≡]-intro [≡]-intro)
[^]ₗ-growing {𝟎}  {𝐒 a} {𝐒 b} _ _ = min
[^]ₗ-growing {𝐒 𝟎}{a}   {b}   _ _
  rewrite [^]-of-𝟏ₗ {a}
  rewrite [^]-of-𝟏ₗ {b}
  = succ min
[^]ₗ-growing {𝐒 (𝐒 n)}{a}{b} _ ab with [≤]-to-[<][≡] ab
... | [∨]-introₗ p         = sub₂(_<_)(_≤_) ([^]ₗ-strictly-growing {n}{a}{b} p)
... | [∨]-introᵣ [≡]-intro = reflexivity(_≤_)


module MLib.Algebra.PropertyCode.RawStruct where

open import MLib.Prelude
open import MLib.Fin.Parts
open import MLib.Finite

open import Relation.Binary as B using (Setoid)

open import Data.List.Any using (Any; here; there)
open import Data.List.Membership.Propositional using (_∈_)

open import Data.Vec.N-ary

open import Data.Product.Relation.SigmaPropositional as OverΣ using (OverΣ)

open import Data.Bool using (T)

open import Category.Applicative

module _ {c ℓ} {A : Set c} (_≈_ : Rel A ℓ) where
  open Algebra.FunctionProperties _≈_

  Congruentₙ : ∀ {n} → (Vec A n → A) → Set (c ⊔ˡ ℓ)
  Congruentₙ f = ∀ {xs ys} → Vec.Pointwise _≈_ xs ys → f xs ≈ f ys

  fromRefl : Reflexive _≈_ → (f : Vec A 0 → A) → Congruentₙ f
  fromRefl refl f [] = refl

  fromCongruent₂ : (f : Vec A 2 → A) → Congruent₂ (curryⁿ f) → Congruentₙ f
  fromCongruent₂ f cong₂ (x≈u ∷ y≈v ∷ []) = cong₂ x≈u y≈v

PointwiseFunc : ∀ {n} {a b ℓ r} {A : Set a} {B : Set b} (_∼_ : A → B → Set ℓ) (xs : Vec A n) (ys : Vec B n) (R : Set r) → Set (N-ary-level ℓ r n)
PointwiseFunc _∼_ [] [] R = R
PointwiseFunc _∼_ (x ∷ xs) (y ∷ ys) R = x ∼ y → PointwiseFunc _∼_ xs ys R

appPW : ∀ {n} {a b ℓ r} {A : Set a} {B : Set b} {_∼_ : A → B → Set ℓ} {xs : Vec A n} {ys : Vec B n} {R : Set r} → PointwiseFunc _∼_ xs ys R → Vec.Pointwise _∼_ xs ys → R
appPW f [] = f
appPW f (x∼y ∷ pw) = appPW (f x∼y) pw

curryPW : ∀ {n} {a b ℓ r} {A : Set a} {B : Set b} {_∼_ : A → B → Set ℓ} {xs : Vec A n} {ys : Vec B n} {R : Set r} → (Vec.Pointwise _∼_ xs ys → R) → PointwiseFunc _∼_ xs ys R
curryPW {xs = []} {[]} f = f []
curryPW {xs = x ∷ xs} {y ∷ ys} f x∼y = curryPW {xs = xs} {ys} λ pw → f (x∼y ∷ pw)

--------------------------------------------------------------------------------
--  Raw structures implementing a set of operators, i.e. interpretations of the
--  operators which are each congruent under an equivalence, but do not
--  necessarily have any other properties.
--------------------------------------------------------------------------------

record IsRawStruct {c ℓ} {A : Set c} (_≈_ : Rel A ℓ) {k} {K : ℕ → Set k} (appOp : ∀ {n} → K n → Vec A n → A) : Set (c ⊔ˡ ℓ ⊔ˡ k) where
  open Algebra.FunctionProperties _≈_
  field
    isEquivalence : IsEquivalence _≈_
    congⁿ : ∀ {n} (κ : K n) → Congruentₙ _≈_ (appOp κ)

  cong : ∀ {n} (κ : K n) {xs ys} → PointwiseFunc _≈_ xs ys (appOp κ xs ≈ appOp κ ys)
  cong κ {xs} = curryPW {xs = xs} (congⁿ κ)

  setoid : Setoid _ _
  setoid = record { isEquivalence = isEquivalence }

  ⟦_⟧ : ∀ {n} → K n → N-ary n A A
  ⟦ κ ⟧ = curryⁿ (appOp κ)

  _⟨_⟩_ : A → K 2 → A → A
  x ⟨ κ ⟩ y = ⟦ κ ⟧ x y

  open IsEquivalence isEquivalence public

record RawStruct {k} (K : ℕ → Set k) c ℓ : Set (sucˡ (c ⊔ˡ ℓ ⊔ˡ k)) where
  infix 4 _≈_

  field
    Carrier : Set c
    _≈_ : Rel Carrier ℓ
    appOp : ∀ {n} → K n → Vec Carrier n → Carrier
    isRawStruct : IsRawStruct _≈_ appOp

  open IsRawStruct isRawStruct public

  -- Form a structure over a new kind of operator by injecting from it.
  subRawStruct : ∀ {k′} {K′ : ℕ → Set k′} → (∀ {n} → K′ n → K n) → RawStruct K′ c ℓ
  subRawStruct liftK = record
    { Carrier = Carrier
    ; _≈_ = _≈_
    ; appOp = appOp ∘ liftK
    ; isRawStruct = record
      { isEquivalence = isEquivalence
      ; congⁿ = congⁿ ∘ liftK
      }
    }


{-# OPTIONS --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
import Tools.PropositionalEquality as PE

-- Congurence of the type of the successor case in natrec.
sucCong : ∀ {F G rF lF Γ} → Γ ∙ ℕ ^ [ ! , ι ⁰ ] ⊢ F ≡ G ^ [ rF , ι lF ]
        → Γ ⊢ Π ℕ ^ ! ° ⁰ ▹ (F ^ rF ° lF ▹▹ F [ suc (var 0) ]↑ ° lF ° lF) ° lF ° lF
            ≡ Π ℕ ^ ! ° ⁰ ▹ (G ^ rF ° lF ▹▹ G [ suc (var 0) ]↑ ° lF ° lF) ° lF ° lF ^ [ rF , ι lF ]
sucCong F≡G with wfEq F≡G
sucCong {lF = lF} F≡G | ⊢Γ ∙ ⊢ℕ =
  let ⊢F , _ = syntacticEq F≡G
  in  univ (Π-cong (⁰min lF) (≡is≤ PE.refl) ⊢ℕ (refl (un-univ ⊢ℕ))
             (Π-cong (≡is≤ PE.refl) (≡is≤ PE.refl) ⊢F (un-univ≡ F≡G)
                     (wkEqTerm (step id) (⊢Γ ∙ ⊢ℕ ∙ ⊢F)
                               (un-univ≡ (subst↑TypeEq F≡G
                                                       (refl (sucⱼ (var (⊢Γ ∙ ⊢ℕ) here))))))))


open import Stlc hiding (⟹*-Preorder; _⟹*⟪_⟫_; example₀; example₁)
open import Relation.Binary.PropositionalEquality as P using (_≡_; _≢_; refl)
open import Relation.Binary using (Preorder)
import Relation.Binary.PreorderReasoning as PreorderReasoning

⟹*-Preorder : Preorder _ _ _
⟹*-Preorder = record
  { Carrier    = Term
  ; _≈_        = _≡_
  ; _∼_        = _⟹*_
  ; isPreorder = record
    { isEquivalence = P.isEquivalence
    ; reflexive     = λ {refl → ⟨⟩}
    ; trans         = _>>_
    }
  }

open PreorderReasoning ⟹*-Preorder
     using (_IsRelatedTo_; begin_; _∎) renaming (_≈⟨_⟩_ to _≡⟨_⟩_; _∼⟨_⟩_ to _⟹*⟨_⟩_)

infixr 2 _⟹*⟪_⟫_

_⟹*⟪_⟫_ : ∀ x {y z} → x ⟹ y → y IsRelatedTo z → x IsRelatedTo z
x ⟹*⟪ x⟹y ⟫ yz  =  x ⟹*⟨ ⟨ x⟹y ⟩ ⟩ yz

example₀ : not · true ⟹* false
example₀ =
  begin
    not · true
  ⟹*⟪ β⇒ value-true ⟫
    if true then false else true
  ⟹*⟪ β𝔹₁ ⟫
    false
  ∎

example₁ : I² · I · (not · false) ⟹* true
example₁ =
  begin
    I² · I · (not · false)
  ⟹*⟪ γ⇒₁ (β⇒ value-λ) ⟫
    (λ[ x ∶ 𝔹 ] I · var x) · (not · false)
  ⟹*⟪ γ⇒₂ value-λ (β⇒ value-false) ⟫
    (λ[ x ∶ 𝔹 ] I · var x) · (if false then false else true)
  ⟹*⟪ γ⇒₂ value-λ β𝔹₂ ⟫
    (λ[ x ∶ 𝔹 ] I · var x) · true
  ⟹*⟪ β⇒ value-true ⟫
    I · true
  ⟹*⟪ β⇒ value-true ⟫
    true
  ∎


open import Prelude
open import core

module lemmas-matching where
  -- matching produces unique answers for arrows, sums, and products
  ▸arr-unicity : ∀{ t t2 t3 } →
                 t ▸arr t2 →
                 t ▸arr t3 →
                 t2 == t3
  ▸arr-unicity MAHole MAHole = refl
  ▸arr-unicity MAArr MAArr = refl

  -- if an arrow matches, then it's consistent with the least restrictive
  -- function type
  matchconsisthole : ∀{t t'} →
                 t ▸arr t' →
                 t ~ (⦇-⦈ ==> ⦇-⦈)
  matchconsisthole MAHole = TCHole2
  matchconsisthole MAArr = TCArr TCHole1 TCHole1

  match-consist : ∀{τ1 τ2} → τ1 ▸arr τ2 → (τ2 ~ τ1)
  match-consist MAHole = TCHole1
  match-consist MAArr = TCRefl

  match-unicity : ∀{ τ τ1 τ2} → τ ▸arr τ1 → τ ▸arr τ2 → τ1 == τ2
  match-unicity MAHole MAHole = refl
  match-unicity MAArr MAArr = refl


open import Relation.Binary.Core

module PLRTree.Push.Heap {A : Set}
                     (_≤_ : A → A → Set)
                     (tot≤ : Total _≤_)
                     (trans≤ : Transitive _≤_) where

open import Data.Sum
open import Induction.WellFounded
open import Order.Total _≤_ tot≤
open import PLRTree {A}
open import PLRTree.Drop _≤_ tot≤
open import PLRTree.Heap _≤_
open import PLRTree.Heap.Properties _≤_ trans≤
open import PLRTree.Order {A}
open import PLRTree.Order.Properties {A}

lemma-push-≤* : {x y : A}{l r : PLRTree}(t : Tag) → x ≤ y → x ≤* l → x ≤* r → (acc : Acc _≺_ (node t y l r)) → x ≤* (push (node t y l r) acc)
lemma-push-≤* {x} t x≤y (lf≤* .x) x≤*r (acc rs) = nd≤* x≤y (lf≤* x) (lf≤* x)
lemma-push-≤* {x} {y} t x≤y (nd≤* {t'} .{x} {x'} x≤x' x≤*l' x≤*r') (lf≤* .x) (acc rs)
    with tot≤ y x'
... | inj₁ y≤x' = nd≤* x≤y (nd≤* x≤x' (lf≤* x) (lf≤* x)) (lf≤* x)
... | inj₂ x'≤y = nd≤* x≤x' (nd≤* x≤y (lf≤* x) (lf≤* x)) (lf≤* x)
lemma-push-≤* {x} {y} t x≤y (nd≤* {t'} .{x} {x'} {l'} {r'} x≤x' x≤*l' x≤*r') (nd≤* {t''} .{x} {x''} {l''} {r''} x≤x'' x≤*l'' x≤*r'') (acc rs)
    with tot≤ y x' | tot≤ y x'' | tot≤ x' x''
... | inj₁ y≤x' | inj₁ y≤x'' | _ = nd≤* x≤y (nd≤* x≤x' x≤*l' x≤*r') (nd≤* x≤x'' x≤*l'' x≤*r'')
... | inj₁ y≤x' | inj₂ x''≤y | _ rewrite lemma-≡-height t'' y x'' l'' r'' =
           let acc-t''yl''r'' = rs (node t'' y l'' r'') (lemma-≺-right t y (node t' x' l' r') (node t'' x'' l'' r''))
           in nd≤* x≤x'' (nd≤* x≤x' x≤*l' x≤*r') (lemma-push-≤* t'' x≤y x≤*l'' x≤*r'' acc-t''yl''r'')
... | inj₂ x'≤y | inj₁ y≤x'' | _ rewrite lemma-≡-height t' y x' l' r' =
           let acc-t'yl'r' = rs (node t' y l' r') (lemma-≺-left t y (node t' x' l' r') (node t'' x'' l'' r''))
           in nd≤* x≤x' (lemma-push-≤* t' x≤y x≤*l' x≤*r' acc-t'yl'r') (nd≤* x≤x'' x≤*l'' x≤*r'')
... | inj₂ x'≤y | inj₂ x''≤y | inj₁ x'≤x'' rewrite lemma-≡-height t' y x' l' r' =
           let acc-t'yl'r' = rs (node t' y l' r') (lemma-≺-left t y (node t' x' l' r') (node t'' x'' l'' r''))
           in nd≤* x≤x' (lemma-push-≤* t' x≤y x≤*l' x≤*r' acc-t'yl'r') (nd≤* x≤x'' x≤*l'' x≤*r'')
... | inj₂ x'≤y | inj₂ x''≤y | inj₂ x''≤x' rewrite lemma-≡-height t'' y x'' l'' r'' =
           let acc-t''yl''r'' = rs (node t'' y l'' r'') (lemma-≺-right t y (node t' x' l' r') (node t'' x'' l'' r''))
           in nd≤* x≤x'' (nd≤* x≤x' x≤*l' x≤*r') (lemma-push-≤* t'' x≤y x≤*l'' x≤*r'' acc-t''yl''r'')

lemma-push-heap : {l r : PLRTree}(t : Tag)(x : A) → Heap l → Heap r → (acc : Acc _≺_ (node t x l r)) → Heap (push (node t x l r) acc)
lemma-push-heap t x leaf _ _ = node (lf≤* x) (lf≤* x) leaf leaf
lemma-push-heap t x (node {t'} {x'} x'≤*l' x'≤*r' hl' hr') leaf (acc rs)
    with tot≤ x x'
... | inj₁ x≤x' = node (nd≤* x≤x' (lf≤* x) (lf≤* x)) (lf≤* x) (node (lf≤* x') (lf≤* x') leaf leaf) leaf
... | inj₂ x'≤x = node (nd≤* x'≤x (lf≤* x') (lf≤* x')) (lf≤* x') (node (lf≤* x) (lf≤* x) leaf leaf) leaf
lemma-push-heap t x (node {t'} {x'} {l'} {r'} x'≤*l' x'≤*r' hl' hr') (node {t''} {x''} {l''} {r''} x''≤*l'' x''≤*r'' hl'' hr'') (acc rs)
    with tot≤ x x' | tot≤ x x'' | tot≤ x' x''
... | inj₁ x≤x' | inj₁ x≤x'' | _ =
           let x≤*t'x'l'r' = nd≤* x≤x' (lemma-≤-≤* x≤x' x'≤*l') (lemma-≤-≤* x≤x' x'≤*r') ;
                x≤*t''x''l''r'' = nd≤* x≤x'' (lemma-≤-≤* x≤x'' x''≤*l'') (lemma-≤-≤* x≤x'' x''≤*r'')
           in node x≤*t'x'l'r' x≤*t''x''l''r'' (node x'≤*l' x'≤*r' hl' hr') (node x''≤*l'' x''≤*r'' hl'' hr'')
... | inj₁ x≤x' | inj₂ x''≤x | _ rewrite lemma-≡-height t'' x x'' l'' r'' =
           let acc-t''xl''r'' = rs (node t'' x l'' r'') (lemma-≺-right t x (node t' x' l' r') (node t'' x'' l'' r'')) ;
                x''≤*t'x'l'r' = lemma-≤-≤* x''≤x (nd≤* x≤x' (lemma-≤-≤* x≤x' x'≤*l') (lemma-≤-≤* x≤x' x'≤*r')) ;
                x''≤*push-t''xl''r'' = lemma-push-≤* t'' x''≤x x''≤*l'' x''≤*r'' acc-t''xl''r''
           in node x''≤*t'x'l'r' x''≤*push-t''xl''r'' (node x'≤*l' x'≤*r' hl' hr') (lemma-push-heap t'' x hl'' hr'' acc-t''xl''r'')
... | inj₂ x'≤x | inj₁ x≤x'' | _ rewrite lemma-≡-height t' x x' l' r' =
           let acc-t'xl'r' = rs (node t' x l' r') (lemma-≺-left t x (node t' x' l' r') (node t'' x'' l'' r'')) ;
                x'≤*t''x''l''r'' = lemma-≤-≤* x'≤x (nd≤* x≤x'' (lemma-≤-≤* x≤x'' x''≤*l'') (lemma-≤-≤* x≤x'' x''≤*r'')) ;
                x'≤*push-t'xl'r' = lemma-push-≤* t' x'≤x x'≤*l' x'≤*r' acc-t'xl'r'
           in node x'≤*push-t'xl'r' x'≤*t''x''l''r'' (lemma-push-heap t' x hl' hr' acc-t'xl'r') (node x''≤*l'' x''≤*r'' hl'' hr'')
... | inj₂ x'≤x | inj₂ x''≤x | inj₁ x'≤x'' rewrite lemma-≡-height t' x x' l' r' =
           let acc-t'xl'r' = rs (node t' x l' r') (lemma-≺-left t x (node t' x' l' r') (node t'' x'' l'' r'')) ;
                x'≤*t''x''l''r'' = lemma-≤-≤* x'≤x'' (nd≤* refl≤ x''≤*l'' x''≤*r'') ;
                x'≤*push-t'xl'r' = lemma-push-≤* t' x'≤x x'≤*l' x'≤*r' acc-t'xl'r'
           in node x'≤*push-t'xl'r' x'≤*t''x''l''r'' (lemma-push-heap t' x hl' hr' acc-t'xl'r') (node x''≤*l'' x''≤*r'' hl'' hr'')
... | inj₂ x'≤x | inj₂ x''≤x | inj₂ x''≤x' rewrite lemma-≡-height t'' x x'' l'' r'' =
           let acc-t''xl''r'' = rs (node t'' x l'' r'') (lemma-≺-right t x (node t' x' l' r') (node t'' x'' l'' r'')) ;
                x''≤*t'x'l'r' = lemma-≤-≤* x''≤x' (nd≤* refl≤ x'≤*l' x'≤*r') ;
                x''≤*push-t''xl''r'' = lemma-push-≤* t'' x''≤x x''≤*l'' x''≤*r'' acc-t''xl''r''
           in node x''≤*t'x'l'r' x''≤*push-t''xl''r'' (node x'≤*l' x'≤*r' hl' hr') (lemma-push-heap t'' x hl'' hr'' acc-t''xl''r'')


module DaraisPhoas where

open import Agda.Primitive using (_⊔_)

module Prelude where

  infixr 3 ∃𝑠𝑡
  infixl 5 _∨_
  infixr 20 _∷_

  data 𝔹 : Set where
    T : 𝔹
    F : 𝔹

  data _∨_ {ℓ₁ ℓ₂} (A : Set ℓ₁) (B : Set ℓ₂) : Set (ℓ₁ ⊔ ℓ₂) where
    Inl : A → A ∨ B
    Inr : B → A ∨ B

  syntax ∃𝑠𝑡 A (λ x → B) = ∃ x ⦂ A 𝑠𝑡 B
  data ∃𝑠𝑡 {ℓ₁ ℓ₂} (A : Set ℓ₁) (B : A → Set ℓ₂) : Set (ℓ₁ ⊔ ℓ₂) where
    ⟨∃_,_⟩ : ∀ (x : A) → B x → ∃ x ⦂ A 𝑠𝑡 B x

  data ⟬_⟭ {ℓ} (A : Set ℓ) : Set ℓ where
    [] : ⟬ A ⟭
    _∷_ : A → ⟬ A ⟭ → ⟬ A ⟭

open Prelude

infixr 15 _⟨→⟩_
data type : Set where
  ⟨ℕ⟩   : type
  _⟨→⟩_ : type → type → type

infixl 15 _⟨∙⟩_
data exp-phoas (var : type → ⟬ type ⟭ → Set) : ∀ (Γ : ⟬ type ⟭) (τ : type) → Set where
  Var : ∀ {Γ τ}
    (x : var τ Γ)
    → exp-phoas var Γ τ
  ⟨λ⟩ : ∀ {Γ τ₁ τ₂}
    (e : var τ₁ (τ₁ ∷ Γ) → exp-phoas var (τ₁ ∷ Γ) τ₂)
    → exp-phoas var Γ (τ₁ ⟨→⟩ τ₂)
  _⟨∙⟩_ : ∀ {Γ τ₁ τ₂}
    (e₁ : exp-phoas var Γ (τ₁ ⟨→⟩ τ₂))
    (e₂ : exp-phoas var Γ τ₁)
    → exp-phoas var Γ τ₂

infix 10 _∈_
data _∈_ {ℓ} {A : Set ℓ} (x : A) : ∀ (xs : ⟬ A ⟭) → Set ℓ where
  Z : ∀ {xs} → x ∈ x ∷ xs
  S : ∀ {x′ xs} → x ∈ xs → x ∈ x′ ∷ xs

infix 10 _⊢_
data _⊢_ : ∀ (Γ : ⟬ type ⟭) (τ : type) → Set where
  Var : ∀ {Γ τ}
    (x : τ ∈ Γ)
    → Γ ⊢ τ
  ⟨λ⟩ : ∀ {Γ τ₁ τ₂}
    (e : τ₁ ∷ Γ ⊢ τ₂)
    → Γ ⊢ τ₁ ⟨→⟩ τ₂
  _⟨∙⟩_ : ∀ {Γ τ₁ τ₂}
    (e₁ : Γ ⊢ τ₁ ⟨→⟩ τ₂)
    (e₂ : Γ ⊢ τ₁)
    → Γ ⊢ τ₂

⟦_⟧₁ : ∀ {Γ τ} (e : exp-phoas _∈_ Γ τ) → Γ ⊢ τ
⟦ Var x ⟧₁ = Var x
⟦ ⟨λ⟩ e ⟧₁ = ⟨λ⟩ ⟦ e Z ⟧₁
⟦ e₁ ⟨∙⟩ e₂ ⟧₁ = ⟦ e₁ ⟧₁ ⟨∙⟩ ⟦ e₂ ⟧₁

⟦_⟧₂ : ∀ {Γ τ} (e : Γ ⊢ τ) → exp-phoas _∈_ Γ τ
⟦ Var x ⟧₂ = Var x
⟦ ⟨λ⟩ e ⟧₂ = ⟨λ⟩ (λ _ → ⟦ e ⟧₂)
⟦ e₁ ⟨∙⟩ e₂ ⟧₂ = ⟦ e₁ ⟧₂ ⟨∙⟩ ⟦ e₂ ⟧₂

Ch : type
Ch =  (⟨ℕ⟩ ⟨→⟩ ⟨ℕ⟩) ⟨→⟩ ⟨ℕ⟩ ⟨→⟩ ⟨ℕ⟩

twoDB : [] ⊢ Ch
twoDB = ⟨λ⟩ (⟨λ⟩ (Var (S Z) ⟨∙⟩ (Var (S Z) ⟨∙⟩ Var Z)))

twoPH : exp-phoas _∈_ [] Ch
twoPH = ⟨λ⟩ (λ f → ⟨λ⟩ (λ x → Var f ⟨∙⟩ (Var f ⟨∙⟩ Var x)))

{-
/Users/wadler/sf/src/extra/DaraisPhoas.agda:82,9-60
⟨ℕ⟩ ⟨→⟩ ⟨ℕ⟩ != ⟨ℕ⟩ of type type
when checking that the expression
⟨λ⟩ (λ f → ⟨λ⟩ (λ x → Var f ⟨∙⟩ (Var f ⟨∙⟩ Var x))) has type
exp-phoas _∈_ [] Ch
-}