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

{- This module serves to develop some basic theory about pointed types.
   After defining the usual notions of pointed types, pointed equivalences,
   and loop spaces, we derive a version of univalence for pointed equivalences
   and observe that univalence between identical types is a pointed equivalence.

   We define pointed versions of dependent sums and products using the usual
   notion of pointed families and observe that they commute in a certain sense
   with taking loop spaces.

   We finish by deriving a generalization to n-truncation and n-th loop spaces
   of the fact allowing us to 'forget' contractible components in a dependent
   sum type that will be needed in the Hierarchy module. -}
module Universe.Utility.Pointed where

open import lib.Basics
open import lib.NType2
open import lib.types.Nat hiding (_+_)
open import lib.types.Pi
open import lib.types.Sigma
open import lib.types.TLevel

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

{- Pointed types.
   A pointed type is a type (base) together with a chosen element (pt).
   The loop space construction Ω takes a pointed type and returns the type
   of all loops at the given basepoint. The trivial loop is taken as
   canonical element, thus forming a new pointed type. -}
Type• : (i : ULevel) → Type (lsucc i)
Type• i = Σ (Type i) (idf _)

module _ {i} (X : Type• i) where
  base = fst X
  pt   = snd X

-- Alternate definition: Σ (n -Type i) ⟦_⟧
_-Type•_ : (n : ℕ₋₂) (i : ULevel) → Type (lsucc i)
n -Type• i = Σ (Type• i) (has-level n ∘ base)

-- For alternate definition: Σ-≤ (n -Type-≤ i) raise
_-Type•-≤_ : (n : ℕ₋₂) (i : ULevel) → S n -Type lsucc i
n -Type•-≤ i = (n -Type• i
              , equiv-preserves-level Σ-comm-snd
                                      (snd (Σ-≤ (n -Type-≤ i) raise)))

{- Pointed equivalences.
   An equivalence of pointed types is an equivalences of the bases
   that preserves the points -}
_≃•_ : ∀ {i j} → Type• i → Type• j → Set (i ⊔ j)
A ≃• B = Σ (base A ≃ base B) (λ f → –> f (pt A) == pt B)

ide• : ∀ {i} (X : Type• i) → X ≃• X
ide• _ = ide _ , idp

_∘e•_ : ∀ {i j k} {X : Type• i} {Y : Type• j} {Z : Type• k}
       → Y ≃• Z → X ≃• Y → X ≃• Z
(u , p) ∘e• (v , q) = (u ∘e v , ap (–> u) q ∙ p)

_⁻¹• : ∀ {i j} {X : Type• i} {Y : Type• j} → X ≃• Y → Y ≃• X
(u , p) ⁻¹• = (u ⁻¹ , ap (<– u) (! p) ∙ <–-inv-l u _)

-- Equational reasoning for pointed equivalences.
infix  2 _≃•∎
infixr 2 _≃•⟨_⟩_

_≃•⟨_⟩_ : ∀ {i j k} (X : Type• i) {Y : Type• j} {Z : Type• k}
          → X ≃• Y → Y ≃• Z → X ≃• Z
_ ≃•⟨ u ⟩ v = v ∘e• u

_≃•∎ : ∀ {i} (X : Type• i) → X ≃• X
_≃•∎ = ide•

-- The loop space construction.
Ω : ∀ {i} → Type• i → Type• i
Ω (A , a) = (a == a , idp)

Ω-≤ : ∀ {i} {n : ℕ₋₂} → (S n) -Type• i → n -Type• i
Ω-≤ ((A , a) , t) = (Ω (A , a) , t a a)

-- Loop spaces are preserved by pointed equivalences.
equiv-Ω : ∀ {i j} {X : Type• i} {Y : Type• j} → X ≃• Y → Ω X ≃• Ω Y
equiv-Ω (u , p) = split u p where
  split : ∀ u {a} {b} → –> u a == b → Ω (_ , a) ≃• Ω (_ , b)
  split u idp = (equiv-ap u _ _ , idp)

equiv-Ω^ : ∀ {i j} {X : Type• i} {Y : Type• j} (n : ℕ)
           → X ≃• Y → (Ω ^ n) X ≃• (Ω ^ n) Y
equiv-Ω^ 0     e = e
equiv-Ω^ (S n) e = equiv-Ω^ n (equiv-Ω e)

{- We call a pointed type n-truncated if its base is.
   Constructing the loop space decrements the truncation level. -}
has-level• : ∀ {i} → ℕ₋₂ → Type• i → Type i
has-level• n = has-level n ∘ base

trunc-many : ∀ {i} {X : Type• i} {k : ℕ} (n : ℕ)
             → has-level• (k + n -2)          X
             → has-level• (k     -2) ((Ω ^ n) X)
trunc-many 0     t = t
trunc-many (S n) t = trunc-many n (t _ _)

--Ω^-≤ : ∀ {i} {k : ℕ} (n : ℕ) → (k + n -2) -Type• i → (k -2) -Type• i
--Ω^-≤ O     X = X
--Ω^-≤ (S n) X = Ω^-≤ n (Ω-≤ X)

Ω^-≤ : ∀ {i} {k : ℕ} (n : ℕ) → (k + n -2) -Type• i → (k -2) -Type• i
Ω^-≤ n (X , t) = ((Ω ^ n) X , trunc-many n t)

Ω^-≤' : ∀ {i} {k : ℕ} (n : ℕ) → (n + k -2) -Type• i → (k -2) -Type• i
Ω^-≤' n (X , t) =
  ((Ω ^ n) X , trunc-many n (transport (λ z → has-level• (z -2) X)
                                       (+-comm _ n) t))

{- Pointedness allows for a more direct notion of contractibility.
   Beware that is-contr• will be equivalent --- not definitionally equal ---
   to has-level∙ ⟨-2⟩. -}
module _ {i} (X : Type• i) where
  is-contr• : Type i
  is-contr• = ∀ a → pt X == a

{- Since pointed types are always inhabited,
   being contractible and propositional is equivalent. -}
module _ {i} {X : Type• i} where
  contr•-equiv-contr : is-contr• X ≃ is-contr (base X)
  contr•-equiv-contr = prop-equiv'
                         (λ c → Π-level (λ a → raise-level-<T (ltSR ltS) c _ _))
                         (cst is-contr-is-prop)
                         (λ x → (pt X , x))
                         (λ y _ → prop-has-all-paths (contr-is-prop y) _ _)

  is-contr•-is-prop : is-prop (is-contr• X)
  is-contr•-is-prop = equiv-preserves-level (contr•-equiv-contr ⁻¹)
                                            is-contr-is-prop

  prop-equiv-contr : is-prop (base X) ≃ is-contr (base X)
  prop-equiv-contr = prop-equiv is-prop-is-prop
                                is-contr-is-prop
                                (inhab-prop-is-contr (pt X))
                                contr-is-prop

  contr•-equiv-prop : is-contr• X ≃ is-prop (base X)
  contr•-equiv-prop = prop-equiv-contr ⁻¹ ∘e contr•-equiv-contr

-- ** Lemma 5.4 ***
{- The induction step for Lemma 7.2.9 in the HoTT Book is much
   more complicated than neccesarry. Associating iterated loop spaces in
   the reverse order, we can do away with the prerequisites 7.2.7 and 7.2.8
   as well as further auxiliary steps. -}
has-level-equiv-contr-loops : ∀ {i} {n : ℕ} {A : Type i}
                              →   has-level (n -1) A
                                ≃ ((a : A) → is-contr• ((Ω ^ n) (A , a)))
has-level-equiv-contr-loops {n = O} {A} =
    is-prop A                      ≃⟨ prop-equiv-inhab-to-contr ⟩
    (A → is-contr A)               ≃⟨ equiv-Π-r (λ _ → contr•-equiv-contr ⁻¹) ⟩
    ((a : A) → is-contr• (A , a))  ≃∎
has-level-equiv-contr-loops {n = S n} {A} = equiv-Π-r lem where
  lem = λ a →
      (((b : A) → has-level ( n -1) (a == b)))
    ≃⟨ equiv-Π-r (λ _ → has-level-equiv-contr-loops) ⟩
      (((b : A) (p : a == b) → is-contr• ((Ω ^ n) (a == b , p))))
    ≃⟨ Π₁-contr (pathfrom-is-contr _) ∘e curry-equiv ⁻¹ ⟩
      is-contr• ((Ω ^ n) (a == a , idp))
    ≃∎

-- Pointed equivalences preserve (pointed) contractibility.
equiv-is-contr• : ∀ {i j} {X : Type• i} {Y : Type• j}
                → X ≃• Y → is-contr• X ≃ is-contr• Y
equiv-is-contr• (u , p) = contr•-equiv-contr ⁻¹
                       ∘e equiv-level u
                       ∘e contr•-equiv-contr

-- Univalence for pointed equivalences.
module _ {i} {A B : Type• i} where
  ua•-equiv : (A ≃• B) ≃ (A == B)
  ua•-equiv =
      A ≃• B
    ≃⟨ ide _ ⟩
      Σ (base A ≃ base B) (λ f → –> f (pt A) == pt B)
    ≃⟨ equiv-Σ-fst _ (snd (ua-equiv ⁻¹)) ⁻¹ ⟩
      Σ (base A == base B) (λ q → coe q (pt A) == pt B)
    ≃⟨ equiv-Σ-snd (λ q → coe-equiv (ap (λ f → coe f (pt A) == pt B)
                                        (! (ap-idf q)))) ⟩
      Σ (base A == base B) (λ q → coe (ap (idf _) q) (pt A) == pt B)
    ≃⟨ equiv-Σ-snd (λ q → to-transp-equiv _ q ⁻¹) ⟩
      Σ (base A == base B) (λ q → pt A == pt B [ idf _ ↓ q ])
    ≃⟨ =Σ-eqv _ _ ⟩
      A == B
    ≃∎

  ua• : A ≃• B → A == B
  ua• = –> ua•-equiv

  coe•-equiv : A == B → A ≃• B
  coe•-equiv = <– ua•-equiv

-- Normal univalence can be made pointed in the endo-setting.
module _ {i} {A : Type i} where
  ua-equiv• : (A ≃ A , ide _) ≃• (A == A , idp)
  ua-equiv• = ((ua-equiv ⁻¹) , idp) ⁻¹•

-- *** Definition 4.1 ***
{- Pointed families.
   A pointed family over a pointed type is a family over the base
   together with a inhabitant of the family at the point.
   Note that pointed types are equivalent to pointed families
   over the pointed unit type (not shown here). -}
Fam• : ∀ {i} (X : Type• i) (j : ULevel) → Type (i ⊔ lsucc j)
Fam• X j = Σ (base X → Type j) (λ P → P (pt X))

-- We have fibered notions of the loop space contruction and n-truncatedness.
module _ {i} {X : Type• i} {j} where
  -- *** Definition 4.3 ***
  {- Note that the definition of the family of path types differs slightly from
     that of the article, which would correspond to transport P p x == x.
     We use dependent paths since this follows the design of the HoTT
     community's Agda library. Clearly, both types are equivalent. -}
  Ω̃ : Fam• X j → Fam• (Ω X) j
  Ω̃ (P , x) = ((λ p → x == x [ P ↓ p ]) , idp)

  fam-has-level : ℕ₋₂ → Fam• X j → Type (i ⊔ j)
  fam-has-level n Q = (a : base X) → has-level n (fst Q a)

{- == Pointed dependent sums ==
   Pointed families as defined above enable us to introduce a Σ-connective
   for pointed types. Because of the abstract nature of some of our lemmata, we
   give Σ• in its uncurried form and first define the type of its parameter. -}
Σ•-param : ∀ i j → Type (lsucc (i ⊔ j))
Σ•-param i j = Σ (Type• i) (λ X → Fam• X j)

module _ {i j} where
  Ω-Σ•-param : Σ•-param i j → Σ•-param i j
  Ω-Σ•-param (X , W) = (Ω X , Ω̃ W)

  -- *** Definition 4.4 ***
  Σ• : Σ•-param i j → Type• (i ⊔ j)
  Σ• (X , Q) = (Σ (base X) (fst Q) , (pt X , snd Q))

  -- *** Lemma 4.5 ***
  {- Commutativity of pointed dependent sums and the loop space construction
     will become an important technical tool, enabling us to work at a more
     abstract level later on. -}
  Ω-Σ•-comm : (R : Σ•-param i j) → Ω (Σ• R) ≃• Σ• (Ω-Σ•-param R)
  Ω-Σ•-comm _ = (=Σ-eqv _ _ , idp) ⁻¹•

{- Pointed dependent products.
   This is not quite the equivalent of Π for pointed types: the domain parameter
   is just an ordinary non-pointed type. However, to enable our goal of
   abstractly working with pointed types, defining this notion is useful. -}
Π•-param : ∀ i j → Type (lsucc (i ⊔ j))
Π•-param i j = Σ (Type i) (λ A → A → Type• j)

module _ {i j} where
  Ω-Π•-param : Π•-param i j → Π•-param i j
  Ω-Π•-param (A , F) = (A , Ω ∘ F)

  -- *** Definition 4.6 ***
  Π• : Π•-param i j → Type• (i ⊔ j)
  Π• (A , Y) = (Π A (base ∘ Y) , pt ∘ Y)

  -- *** Lemma 4.7 ***
  {- Pointed dependent products and loop space construction on
     its codomain parameter commute as well. -}
  Ω-Π•-comm : (R : Π•-param i j) → Ω (Π• R) ≃• Π• (Ω-Π•-param R)
  Ω-Π•-comm _ = (app=-equiv , idp)

  Ω^-Π•-comm : (C : Type i) (F : C → Type• j) (n : ℕ)
             → (Ω ^ n) (Π• (C , F)) ≃• Π• (C , Ω ^ n ∘ F)
  Ω^-Π•-comm C F 0     = ide• _
  Ω^-Π•-comm C F (S n) = Ω^-Π•-comm C _ n ∘e• equiv-Ω^ n (Ω-Π•-comm _)

equiv-Π• : ∀ {i₀ i₁ j₀ j₁} {R₀ : Π•-param i₀ j₀} {R₁ : Π•-param i₁ j₁}
           → Σ (fst R₀ ≃ fst R₁) (λ u → ∀ a → snd R₀ (<– u a) ≃• snd R₁ a)
           → Π• R₀ ≃• Π• R₁
equiv-Π• (u , v) = (equiv-Π u (fst ∘ v) , λ= (snd ∘ v))

-- *** Lemma 5.1 ***
-- In an n-th loop space, we can forget components of truncation level n.
forget-Ω^-Σ•₂ : ∀ {i j} {X : Type• i} (Q : Fam• X j) (n : ℕ)
                → fam-has-level (n -2) Q → (Ω ^ n) (Σ• (X , Q)) ≃• (Ω ^ n) X
forget-Ω^-Σ•₂     {X = X} Q    O  h = (Σ₂-contr h , idp)
forget-Ω^-Σ•₂ {i} {X = X} Q (S n) h =
  (Ω ^ (S n)) (Σ• (X , Q))  ≃•⟨ equiv-Ω^ n (Ω-Σ•-comm _) ⟩
  (Ω ^ n) (Σ• (Ω X , Ω̃ Q))  ≃•⟨ forget-Ω^-Σ•₂ {i} _ n (λ _ → =-[-↓-]-level h) ⟩
  (Ω ^ (S n)) X             ≃•∎


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

open import Agda.Primitive
open import Prelude
import GSeTT.Rules
import GSeTT.Typed-Syntax
import Globular-TT.Syntax
import Globular-TT.Rules

{- Decidability of type cheking for the type theory for globular sets -}
module Globular-TT.Dec-Type-Checking {l} (index : Set l) (rule : index → GSeTT.Typed-Syntax.Ctx × (Globular-TT.Syntax.Pre-Ty index))
                                                   (assumption : Globular-TT.Rules.well-founded index rule)
                                                   (eqdec-index : eqdec index) where
  open import Globular-TT.Syntax index
  open import Globular-TT.Eqdec-syntax index eqdec-index
  open import Globular-TT.Rules index rule
  open import Globular-TT.CwF-Structure index rule

  dec-∈ : ∀ (n : ℕ) (A : Pre-Ty) (Γ : Pre-Ctx) → dec (n # A ∈ Γ)
  dec-∈ n A ⊘ = inr λ{()}
  dec-∈ n A (Γ ∙ x # B) with dec-∈ n A Γ
  ...                    | inl n∈Γ = inl (inl n∈Γ)
  ...                    | inr n∉Γ with eqdecℕ n x | eqdec-Ty A B
  ...                              | inl idp | inl idp = inl (inr (idp , idp))
  ...                              | inr n≠x | _ = inr λ{(inl n∈Γ) → n∉Γ n∈Γ; (inr (idp , idp)) → n≠x idp}
  ...                              | inl idp | inr A≠B_ = inr λ{(inl n∈Γ) → n∉Γ n∈Γ;  (inr (_ , A=B)) → A≠B A=B}

  {- Decidability in the fragment of the theory with only globular contexts -}
  -- termination is really tricky, and involves reasonning both on depth and dimension at the same time !
  dec-G⊢T : ∀ (Γ : GSeTT.Typed-Syntax.Ctx) n A → dim A ≤ n →  dec (GPre-Ctx (fst Γ) ⊢T A)
  dec-G⊢t : ∀ (Γ : GSeTT.Typed-Syntax.Ctx) n d A t → dim A ≤ n → depth t ≤ d →  dec (GPre-Ctx (fst Γ) ⊢t t # A)
  dec-G⊢S : ∀ (Δ Γ : GSeTT.Typed-Syntax.Ctx) n d γ → dimC (GPre-Ctx (fst Γ)) ≤ n → depthS γ ≤ d → dec (GPre-Ctx (fst Δ) ⊢S γ > GPre-Ctx (fst Γ))

  dec-G⊢T Γ _ ∗ _ = inl (ob (GCtx _ (snd Γ)))
  dec-G⊢T Γ (S n) (⇒ A t u) (S≤ i)  with dec-G⊢T Γ n A i | dec-G⊢t Γ n _ A t i (n≤n _) | dec-G⊢t Γ n _ A u i (n≤n _)
  ...                         | inl Γ⊢A | inl Γ⊢t:A | inl Γ⊢u:A = inl (ar Γ⊢A Γ⊢t:A Γ⊢u:A)
  ...                         | inr Γ⊬A | _ | _ = inr λ{(ar Γ⊢A _ _) → Γ⊬A Γ⊢A}
  ...                         | inl _ | inr Γ⊬t:A  | _ = inr λ{(ar _ Γ⊢t:A _) → Γ⊬t:A Γ⊢t:A}
  ...                         | inl _ | inl _  | inr Γ⊬u:A = inr λ{(ar _ _ Γ⊢u:A) → Γ⊬u:A Γ⊢u:A}

  dec-G⊢t Γ n _ A (Var x) _ (0≤ _) with dec-∈ x A (GPre-Ctx (fst Γ))
  ...                              | inl x∈Γ = inl (var (GCtx _ (snd Γ)) x∈Γ)
  ...                              | inr x∉Γ = inr λ {(var _ x∈Γ) → x∉Γ x∈Γ}
  dec-G⊢t Γ n (S d) A (Tm-constructor i γ) dimA≤n (S≤ dγ≤d) with eqdec-Ty A (Ti i [ γ ]Pre-Ty )
  ...                                         | inr A≠Ti = inr λ{(tm _ _ idp) → A≠Ti idp}
  ...                                         | inl idp
                                                    with dec-G⊢T (fst (rule i)) n (Ti i) (=-≤ (dim[] _ _ ^) dimA≤n)
  ...                                                 | inr Ci⊬Ti = inr λ t → Ci⊬Ti (Γ⊢tm→Ci⊢Ti t)
  ...                                                 | inl Ci⊢Ti with dec-G⊢S Γ (fst (rule i)) n d γ
                                                                               (≤T (≤-= (assumption i Ci⊢Ti) (dim[] _ _ ^)) dimA≤n)    -- dim Ci ≤ dim A
                                                                               dγ≤d                                                    -- depth γ ≤ d
  ...                                                             | inr Γ⊬γ = inr λ t → Γ⊬γ (Γ⊢tm→Γ⊢γ t)
  ...                                                             | inl Γ⊢γ = inl (tm Ci⊢Ti Γ⊢γ idp)

  dec-G⊢S Δ (nil , _) _ _ <> (0≤ _) _ = inl (es (GCtx _ (snd Δ)))
  dec-G⊢S Δ ((Γ :: _) , _) _ _ <> _ _ = inr λ {()}
  dec-G⊢S Δ (nil , _) _ _ < γ , x ↦ t > _ _ = inr λ {()}
  dec-G⊢S Δ ((Γ :: (y , A)) , Γ+⊢@(GSeTT.Rules.cc Γ⊢ Γ⊢A idp)) n d < γ , x ↦ t > dimΓ+≤n dγ+≤d with dec-G⊢S Δ (Γ , Γ⊢) n d γ
                                                                                (≤T (n≤max (dimC (GPre-Ctx Γ)) (dim (GPre-Ty A))) dimΓ+≤n) -- dim Γ ≤ n
                                                                                (≤T (n≤max (depthS γ) (depth t)) dγ+≤d)                    -- depth γ ≤ d
                                                                                | dec-G⊢t Δ n d ((GPre-Ty A) [ γ ]Pre-Ty) t
                                                                                ((≤T (=-≤ (dim[] _ _) (m≤max (dimC (GPre-Ctx Γ)) (dim (GPre-Ty A)))) dimΓ+≤n)) -- dim A[γ] ≤ n
                                                                                (≤T (m≤max (depthS γ) (depth t)) dγ+≤d)                                        -- depth t ≤ d
                                                                                | eqdecℕ y x
  ...                                                    | inl Δ⊢γ:Γ | inl Δ⊢t | inl idp = inl (sc Δ⊢γ:Γ (GCtx _ Γ+⊢) Δ⊢t idp)
  ...                                                    | inr Δ⊬γ:Γ | _ | _  = inr λ {(sc Δ⊢γ:Γ _ _ idp) → Δ⊬γ:Γ Δ⊢γ:Γ}
  ...                                                    | inl _ | inr Δ⊬t | _ = inr λ {(sc _ _ Δ⊢t idp) → Δ⊬t Δ⊢t}
  ...                                                    | inl _ | inl _ | inr n≠x = inr λ {(sc _ _ _ idp) → n≠x idp}

  {- Decidability of judgments for contexts, types, terms and substitution towards a glaobular context -}
  dec-⊢C : ∀ Γ → dec (Γ ⊢C)
  dec-⊢T : ∀ Γ A →  dec (Γ ⊢T A)
  dec-⊢t : ∀ Γ A t → dec (Γ ⊢t t # A)
  dec-⊢S:G : ∀ Δ (Γ : GSeTT.Typed-Syntax.Ctx) γ → dec (Δ ⊢S γ > GPre-Ctx (fst Γ))

  dec-⊢T Γ ∗ with dec-⊢C Γ
  ...        | inl Γ⊢ = inl (ob Γ⊢)
  ...        | inr Γ⊬ = inr λ {(ob Γ⊢) → Γ⊬ Γ⊢}

  dec-⊢T Γ (⇒ A t u) with dec-⊢T Γ A | dec-⊢t Γ A t | dec-⊢t Γ A u
  ...                 | inl Γ⊢A | inl Γ⊢t:A | inl Γ⊢u:A = inl (ar Γ⊢A Γ⊢t:A Γ⊢u:A)
  ...                 | inr Γ⊬A | _ | _ = inr λ {(ar Γ⊢A _ _) → Γ⊬A Γ⊢A}
  ...                 | inl _ | inr Γ⊬t:A | _ = inr λ {(ar _ Γ⊢t:A _) → Γ⊬t:A Γ⊢t:A}
  ...                 | inl _ | inl _ | inr Γ⊬u:A = inr λ {(ar _ _ Γ⊢u:A) → Γ⊬u:A Γ⊢u:A}

  dec-⊢t Γ A (Var x) with dec-⊢C Γ | dec-∈ x A Γ
  ...                | inl Γ⊢ | inl x∈Γ = inl (var Γ⊢ x∈Γ)
  ...                | inr Γ⊬ | _ = inr λ {(var Γ⊢ _) → Γ⊬ Γ⊢}
  ...                | inl _ | inr x∉Γ = inr λ {(var _ x∈Γ) → x∉Γ x∈Γ}
  dec-⊢t Γ A (Tm-constructor i γ) with eqdec-Ty A (Ti i [ γ ]Pre-Ty)
  ...                                   | inr A≠Ti = inr λ{(tm _ _ idp) → A≠Ti idp}
  ...                                   | inl idp
                                              with dec-G⊢T (fst (rule i)) _ (Ti i) (n≤n _) | dec-⊢S:G Γ (fst (rule i)) γ
  ...                                              | inl Ci⊢Ti | inl Γ⊢γ = inl (tm Ci⊢Ti Γ⊢γ idp)
  ...                                              | inr Ci⊬Ti | _ = inr λ t → Ci⊬Ti (Γ⊢tm→Ci⊢Ti t)
  ...                                              | inl _ | inr Γ⊬γ = inr λ t → Γ⊬γ (Γ⊢tm→Γ⊢γ t)

  dec-⊢C ⊘ = inl ec
  dec-⊢C (Γ ∙ n # A) with dec-⊢C Γ | dec-⊢T Γ A | eqdecℕ n (C-length Γ)
  ...                 | inl Γ⊢ | inl Γ⊢A | inl idp = inl (cc Γ⊢ Γ⊢A idp)
  ...                 | inr Γ⊬ | _ | _ = inr λ {(cc Γ⊢ _ idp) → Γ⊬ Γ⊢}
  ...                 | inl _ | inr Γ⊬A | _ = inr λ {(cc _ Γ⊢A idp) → Γ⊬A Γ⊢A}
  ...                 | inl _ | inl _ | inr n≠l = inr λ {(cc _ _ idp) → n≠l idp}

  dec-⊢S:G Δ (nil , _) <> with (dec-⊢C Δ)
  ...             | inl Δ⊢ = inl (es Δ⊢)
  ...             | inr Δ⊬ = inr λ{(es Δ⊢) → Δ⊬ Δ⊢}
  dec-⊢S:G Δ (nil , _) < γ , x ↦ x₁ > = inr λ{()}
  dec-⊢S:G Δ ((Γ :: _) , _) <> = inr λ{()}
  dec-⊢S:G Δ ((Γ :: (v , A)) , Γ+⊢@(GSeTT.Rules.cc Γ⊢ Γ⊢A idp)) < γ , x ↦ t > with dec-⊢S:G Δ (Γ , Γ⊢) γ | dec-⊢t Δ ((GPre-Ty A) [ γ ]Pre-Ty) t | eqdecℕ x (length Γ)
  ...                                                                | inl Δ⊢γ | inl Δ⊢t | inl idp = inl (sc Δ⊢γ (GCtx _ Γ+⊢) Δ⊢t idp)
  ...                                                                | inr Δ⊬γ | _ | _ = inr λ{(sc Δ⊢γ _ _ idp) → Δ⊬γ Δ⊢γ}
  ...                                                                | inl _ | inr Δ⊬t | _ = inr λ{(sc _ _ Δ⊢t idp) → Δ⊬t Δ⊢t}
  ...                                                                | inl _ | inl _ | inr x≠x = inr λ{(sc _ _ _ idp) → x≠x idp}

  {- Decidability of substitution -}
  dec-⊢S : ∀ Δ Γ γ → dec (Δ ⊢S γ > Γ)
  dec-⊢S Δ ⊘ <> with (dec-⊢C Δ)
  ...             | inl Δ⊢ = inl (es Δ⊢)
  ...             | inr Δ⊬ = inr λ{(es Δ⊢) → Δ⊬ Δ⊢}
  dec-⊢S Δ ⊘ < γ , x ↦ x₁ > = inr λ{()}
  dec-⊢S Δ (Γ ∙ _ # _) <> = inr λ{()}
  dec-⊢S Δ (Γ ∙ v # A) < γ , x ↦ t > with dec-⊢S Δ Γ γ | dec-⊢C (Γ ∙ v # A) | dec-⊢t Δ (A [ γ ]Pre-Ty) t | eqdecℕ x v
  ...                                                                | inl Δ⊢γ | inl Γ+⊢ | inl Δ⊢t | inl idp  = inl (sc Δ⊢γ Γ+⊢ Δ⊢t idp)
  ...                                                                | inr Δ⊬γ | _ | _ | _  = inr λ{(sc Δ⊢γ _ _ idp) → Δ⊬γ Δ⊢γ}
  ...                                                                | inl _ | inr Γ+⊬ | _ | _  = inr λ{(sc _ Γ⊢ _ idp) → Γ+⊬ Γ⊢}
  ...                                                                | inl _ | inl _ | inr Δ⊬t | _  = inr λ{(sc _ _ Δ⊢t idp) → Δ⊬t Δ⊢t}
  ...                                                                | inl _ | inl _ | inl _ | inr x≠x  = inr λ{(sc _ _ _ idp) → x≠x idp}


{-# OPTIONS --rewriting #-}

-- Syntax

open import Library

module Derivations (Base : Set) where

import Formulas; private open module Form = Formulas Base
-- open import Formulas Base public

-- Derivations

infix 2 _⊢_

data _⊢_ (Γ : Cxt) : (A : Form) → Set where
  hyp    : ∀{A} (x : Hyp A Γ) → Γ ⊢ A
  impI   : ∀{A B} (t : Γ ∙ A ⊢ B) → Γ ⊢ A ⇒ B
  impE   : ∀{A B} (t : Γ ⊢ A ⇒ B) (u : Γ ⊢ A) → Γ ⊢ B
  andI   : ∀{A B} (t : Γ ⊢ A) (u : Γ ⊢ B) → Γ ⊢ A ∧ B
  andE₁  : ∀{A B} (t : Γ ⊢ A ∧ B) → Γ ⊢ A
  andE₂  : ∀{A B} (t : Γ ⊢ A ∧ B) → Γ ⊢ B
  orI₁   : ∀{A B} (t : Γ ⊢ A) → Γ ⊢ A ∨ B
  orI₂   : ∀{A B} (t : Γ ⊢ B) → Γ ⊢ A ∨ B
  orE    : ∀{A B C} (t : Γ ⊢ A ∨ B) (u : Γ ∙ A ⊢ C) (v : Γ ∙ B ⊢ C) → Γ ⊢ C
  falseE : ∀{C} (t : Γ ⊢ False) → Γ ⊢ C
  trueI  : Γ ⊢ True

-- Example derivation

andComm : ∀{A B} → ε ⊢ A ∧ B ⇒ B ∧ A
andComm = impI (andI (andE₂ (hyp top)) (andE₁ (hyp top)))

-- Weakening

Tm = λ A Γ → Γ ⊢ A

monD : ∀{A} → Mon (Tm A)
monD τ (hyp x)     = hyp (monH τ x)
monD τ (impI t)    = impI (monD (lift τ) t)
monD τ (impE t u)  = impE (monD τ t) (monD τ u)
monD τ (andI t u)  = andI (monD τ t) (monD τ u)
monD τ (andE₁ t)   = andE₁ (monD τ t)
monD τ (andE₂ t)   = andE₂ (monD τ t)
monD τ (orI₁ t)    = orI₁ (monD τ t)
monD τ (orI₂ t)    = orI₂ (monD τ t)
monD τ (orE t u v) = orE (monD τ t) (monD (lift τ) u) (monD (lift τ) v)
monD τ (falseE t)  = falseE (monD τ t)
monD τ trueI       = trueI

monD-id : ∀{Γ A} (t : Γ ⊢ A) → monD id≤ t ≡ t
monD-id (hyp x) = refl
monD-id (impI t) = cong impI (monD-id t)  -- REWRITE lift-id≤
monD-id (impE t u) = cong₂ impE (monD-id t) (monD-id u)
monD-id (andI t u) = cong₂ andI (monD-id t) (monD-id u)
monD-id (andE₁ t) = cong andE₁ (monD-id t)
monD-id (andE₂ t) = cong andE₂ (monD-id t)
monD-id (orI₁ t) = cong orI₁ (monD-id t)
monD-id (orI₂ t) = cong orI₂ (monD-id t)
monD-id (orE t u v) = cong₃ orE (monD-id t) (monD-id u) (monD-id v)
monD-id (falseE t) = cong falseE (monD-id t)
monD-id trueI = refl

-- For normal forms, we use the standard presentation.
-- Normal forms are not unique.

-- Normality as predicate

mutual
  data Neutral {Γ} {A} : (t : Γ ⊢ A) → Set where
    hyp : (x : Hyp A Γ) → Neutral (hyp x)
    impE : ∀{B} {t : Γ ⊢ B ⇒ A} {u : Γ ⊢ B} (ne : Neutral t) (nf : Normal u) → Neutral (impE t u)
    andE₁ : ∀{B} {t : Γ ⊢ A ∧ B} (ne : Neutral t) → Neutral (andE₁ t)
    andE₂ : ∀{B} {t : Γ ⊢ B ∧ A} (ne : Neutral t) → Neutral (andE₂ t)

  data Normal {Γ} : ∀{A} (t : Γ ⊢ A) → Set where
    ne     : ∀{A} {t : Γ ⊢ A} (ne : Neutral t) → Normal t
    impI   : ∀{A B} (t : Γ ∙ A ⊢ B) (nf : Normal t) → Normal (impI t)
    andI   : ∀{A B} (t : Γ ⊢ A) (u : Γ ⊢ B) (nf₁ : Normal t) (nf₂ : Normal u) → Normal (andI t u)
    orI₁   : ∀{A B} {t : Γ ⊢ A} (nf : Normal t) → Normal (orI₁ {Γ} {A} {B} t)
    orI₂   : ∀{A B} {t : Γ ⊢ B} (nf : Normal t) → Normal (orI₂ {Γ} {A} {B} t)
    orE    : ∀{A B C} {t : Γ ⊢ A ∨ B} {u : Γ ∙ A ⊢ C} {v : Γ ∙ B ⊢ C}
             (ne : Neutral t) (nf₁ : Normal u) (nf₂ : Normal v) → Normal (orE t u v)
    falseE : ∀{A} {t : Γ ⊢ False} (ne : Neutral t) → Normal (falseE {Γ} {A} t)
    trueI  : Normal trueI

-- Intrinsic normality

mutual
  data Ne (Γ : Cxt) (A : Form) : Set where
    hyp    : ∀    (x : Hyp A Γ) → Ne Γ A
    impE   : ∀{B} (t : Ne Γ (B ⇒ A)) (u : Nf Γ B) → Ne Γ A
    andE₁  : ∀{B} (t : Ne Γ (A ∧ B)) → Ne Γ A
    andE₂  : ∀{B} (t : Ne Γ (B ∧ A)) → Ne Γ A

  data Nf (Γ : Cxt) : (A : Form) → Set where
    -- ne     : ∀{A} (t : Ne Γ A) → Nf Γ A   -- allows η-short nfs
    ne     : ∀{P} (t : Ne Γ (Atom P)) → Nf Γ (Atom P)
    impI   : ∀{A B} (t : Nf (Γ ∙ A) B) → Nf Γ (A ⇒ B)
    andI   : ∀{A B} (t : Nf Γ A) (u : Nf Γ B) → Nf Γ (A ∧ B)
    orI₁   : ∀{A B} (t : Nf Γ A) → Nf Γ (A ∨ B)
    orI₂   : ∀{A B} (t : Nf Γ B) → Nf Γ (A ∨ B)
    orE    : ∀{A B C} (t : Ne Γ (A ∨ B)) (u : Nf (Γ ∙ A) C) (v : Nf (Γ ∙ B) C) → Nf Γ C
    falseE : ∀{A} (t : Ne Γ False) → Nf Γ A
    trueI  : Nf Γ True

-- Presheaf-friendly argument order

Ne' = λ A Γ → Ne Γ A
Nf' = λ A Γ → Nf Γ A

-- Admissible false-Elimination from a normal proof of false (using case splits)

falseE! : ∀{A} → Nf' False →̇ Nf' A
-- falseE! (ne t)       = falseE t  -- only for η-short
falseE! (orE t t₁ t₂) = orE t (falseE! t₁) (falseE! t₂)
falseE! (falseE t)   = falseE t

-- Ne' and Nf' are presheaves over (Cxt, ≤)

mutual

  monNe : ∀{A} → Mon (Ne' A)
  monNe τ (hyp x)     = hyp (monH τ x)
  monNe τ (impE t u)  = impE (monNe τ t) (monNf τ u)
  monNe τ (andE₁ t)   = andE₁ (monNe τ t)
  monNe τ (andE₂ t)   = andE₂ (monNe τ t)

  monNf : ∀{A} → Mon (Nf' A)
  monNf τ (ne t)     = ne (monNe τ t)
  monNf τ (impI t)    = impI (monNf (lift τ) t)
  monNf τ (andI t u)  = andI (monNf τ t) (monNf τ u)
  monNf τ (orI₁ t)    = orI₁ (monNf τ t)
  monNf τ (orI₂ t)    = orI₂ (monNf τ t)
  monNf τ (orE t u v) = orE (monNe τ t) (monNf (lift τ) u) (monNf (lift τ) v)
  monNf τ (falseE t)  = falseE (monNe τ t)
  monNf τ trueI       = trueI

-- Forgetting normality

mutual
  ne[_] : ∀{Γ A} (t : Ne Γ A) → Γ ⊢ A
  ne[ hyp x ]     = hyp x
  ne[ impE t u ]  = impE ne[ t ] nf[ u ]
  ne[ andE₁ t ]   = andE₁ ne[ t ]
  ne[ andE₂ t ]   = andE₂ ne[ t ]

  nf[_] : ∀{Γ A} (t : Nf Γ A) → Γ ⊢ A
  nf[ ne t ]      = ne[ t ]
  nf[ impI t ]    = impI nf[ t ]
  nf[ andI t u ]  = andI nf[ t ] nf[ u ]
  nf[ orI₁ t ]    = orI₁ nf[ t ]
  nf[ orI₂ t ]    = orI₂ nf[ t ]
  nf[ orE t u v ] = orE ne[ t ] nf[ u ] nf[ v ]
  nf[ falseE t ]  = falseE ne[ t ]
  nf[ trueI ]     = trueI

mutual

  monD-ne : ∀{Γ Δ A} (τ : Δ ≤ Γ) (t : Ne Γ A) → ne[ monNe τ t ] ≡ monD τ ne[ t ]
  monD-ne τ (hyp x)     = refl
  monD-ne τ (impE t u)  = cong₂ impE (monD-ne τ t) (monD-nf τ u)
  monD-ne τ (andE₁ t)   = cong andE₁ (monD-ne τ t)
  monD-ne τ (andE₂ t)   = cong andE₂ (monD-ne τ t)

  monD-nf : ∀{Γ Δ A} (τ : Δ ≤ Γ) (t : Nf Γ A) → nf[ monNf τ t ] ≡ monD τ nf[ t ]
  monD-nf τ (ne t)      = monD-ne τ t
  monD-nf τ (impI t)    = cong impI (monD-nf (lift τ) t)
  monD-nf τ (andI t u)  = cong₂ andI (monD-nf τ t) (monD-nf τ u)
  monD-nf τ (orI₁ t)    = cong orI₁ (monD-nf τ t)
  monD-nf τ (orI₂ t)    = cong orI₂ (monD-nf τ t)
  monD-nf τ (orE t u v) = cong₃ orE (monD-ne τ t) (monD-nf (lift τ) u) (monD-nf (lift τ) v)
  monD-nf τ (falseE t)  = cong falseE (monD-ne τ t)
  monD-nf τ trueI       = refl

  -- monD-ne : ∀{Γ Δ A} (τ : Δ ≤ Γ) (t : Ne Γ A) → monD τ ne[ t ] ≡ ne[ monNe τ t ]
  -- monD-ne τ (hyp x)     = refl
  -- monD-ne τ (impE t u)  = cong₂ impE (monD-ne τ t) (monD-nf τ u)
  -- monD-ne τ (andE₁ t)   = cong andE₁ (monD-ne τ t)
  -- monD-ne τ (andE₂ t)   = cong andE₂ (monD-ne τ t)

  -- monD-nf : ∀{Γ Δ A} (τ : Δ ≤ Γ) (t : Nf Γ A) → monD τ nf[ t ] ≡ nf[ monNf τ t ]
  -- monD-nf τ (ne t)      = monD-ne τ t
  -- monD-nf τ (impI t)    = cong impI (monD-nf (lift τ) t)
  -- monD-nf τ (andI t u)  = cong₂ andI (monD-nf τ t) (monD-nf τ u)
  -- monD-nf τ (orI₁ t)    = cong orI₁ (monD-nf τ t)
  -- monD-nf τ (orI₂ t)    = cong orI₂ (monD-nf τ t)
  -- monD-nf τ (orE t u v) = cong₃ orE (monD-ne τ t) (monD-nf (lift τ) u) (monD-nf (lift τ) v)
  -- monD-nf τ (falseE t)  = cong falseE (monD-ne τ t)
  -- monD-nf τ trueI       = refl

{-# REWRITE monD-ne monD-nf #-}


{-# OPTIONS --cubical --no-import-sorts --safe #-}
module Cubical.DStructures.Structures.SplitEpi where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.Structure
open import Cubical.Functions.FunExtEquiv

open import Cubical.Homotopy.Base

open import Cubical.Data.Sigma

open import Cubical.Relation.Binary

open import Cubical.Algebra.Group
open import Cubical.Structures.LeftAction

open import Cubical.DStructures.Base
open import Cubical.DStructures.Meta.Properties

open import Cubical.DStructures.Structures.Type
open import Cubical.DStructures.Structures.Constant
open import Cubical.DStructures.Structures.Group

private
  variable
    ℓ ℓ' : Level

open URGStr

---------------------------------------------
-- URG structures on the type of split epis,
-- and displayed structures over that
--
--     B
--     |
--  isSplit
--     |
--   G²FB
---------------------------------------------

module _ (ℓ ℓ' : Level) where

  -- type of Split epimorphisms
  SplitEpi = Σ[ ((G , H) , f , b) ∈ G²FB ℓ ℓ' ] isGroupSplitEpi f b
  SplitEpi' = Σ[ G ∈ Group {ℓ} ] Σ[ H ∈ Group {ℓ'} ] Σ[ (f , b) ∈ (GroupHom G H) × (GroupHom H G) ] isGroupSplitEpi f b

  IsoSplitEpi' : Iso SplitEpi' SplitEpi
  IsoSplitEpi' = compIso (invIso Σ-assoc-Iso)
                         (invIso Σ-assoc-Iso)

  -- split epimorphisms + a map back
  SplitEpiB = Σ[ (((G , H) , f , b) , isRet) ∈ SplitEpi ] GroupHom H G

  -- split epimorphisms displayed over pairs of groups
  𝒮ᴰ-SplitEpi : URGStrᴰ (𝒮-G²FB ℓ ℓ')
                        (λ ((G , H) , (f , b)) → isGroupSplitEpi f b)
                        ℓ-zero
  𝒮ᴰ-SplitEpi =
    Subtype→Sub-𝒮ᴰ (λ ((G , H) , (f , b)) → isGroupSplitEpi f b , isPropIsGroupSplitEpi f b)
                   (𝒮-G²FB ℓ ℓ')

  -- URG structure on type of split epimorphisms
  𝒮-SplitEpi : URGStr SplitEpi (ℓ-max ℓ ℓ')
  𝒮-SplitEpi = ∫⟨ 𝒮-G²FB ℓ ℓ' ⟩ 𝒮ᴰ-SplitEpi

  -- morphisms back displayed over split epimorphisms,
  -- obtained by lifting the morphisms back over
  -- 𝒮-G² twice
  𝒮ᴰ-G²FBSplit\B : URGStrᴰ 𝒮-SplitEpi
                           (λ (((G , H) , _) , _) → GroupHom H G)
                           (ℓ-max ℓ ℓ')
  𝒮ᴰ-G²FBSplit\B =
    VerticalLift2-𝒮ᴰ (𝒮-group ℓ ×𝒮 𝒮-group ℓ')
                     (𝒮ᴰ-G²\B ℓ ℓ')
                     (𝒮ᴰ-G²\FB ℓ ℓ')
                     𝒮ᴰ-SplitEpi

  -- URG structure on split epis with an extra
  -- morphism back
  𝒮-SplitEpiB : URGStr SplitEpiB (ℓ-max ℓ ℓ')
  𝒮-SplitEpiB = ∫⟨ 𝒮-SplitEpi ⟩ 𝒮ᴰ-G²FBSplit\B

  𝒮ᴰ-G\GFBSplitEpi : URGStrᴰ (𝒮-group ℓ)
                             (λ G → Σ[ H ∈ Group {ℓ'} ] Σ[ (f , b) ∈ (GroupHom G H) × (GroupHom H G) ] isGroupSplitEpi f b )
                             (ℓ-max ℓ ℓ')
  𝒮ᴰ-G\GFBSplitEpi =
    splitTotal-𝒮ᴰ (𝒮-group ℓ)
                  (𝒮ᴰ-const (𝒮-group ℓ) (𝒮-group ℓ'))
                  (splitTotal-𝒮ᴰ (𝒮-group ℓ ×𝒮 𝒮-group ℓ')
                                 (𝒮ᴰ-G²\FB ℓ ℓ')
                                 𝒮ᴰ-SplitEpi)

--------------------------------------------------
-- This module introduces convenient notation
-- when working with a single split epimorphism
---------------------------------------------------

module SplitEpiNotation {G₀ : Group {ℓ}} {G₁ : Group {ℓ'}}
                        (ι : GroupHom G₀ G₁) (σ : GroupHom G₁ G₀)
                        (split : isGroupSplitEpi ι σ) where
  open GroupNotation₀ G₀
  open GroupNotation₁ G₁
  ι∘σ : GroupHom G₁ G₁
  ι∘σ = compGroupHom σ ι
  s = GroupHom.fun σ
  -- i is reserved for an interval variable (i : I) so we use 𝒾 instead
  𝒾 = GroupHom.fun ι
  -i = λ (x : ⟨ G₀ ⟩) → -₁ (𝒾 x)
  s- = λ (x : ⟨ G₁ ⟩) → s (-₁ x)
  si = λ (x : ⟨ G₀ ⟩) → s (𝒾 x)
  is = λ (x : ⟨ G₁ ⟩) → 𝒾 (s x)
  -si = λ (x : ⟨ G₀ ⟩) → -₀ (si x)
  -is = λ (x : ⟨ G₁ ⟩) → -₁ (is x)
  si- = λ (x : ⟨ G₀ ⟩) → si (-₀ x)
  is- = λ (x : ⟨ G₁ ⟩) → is (-₁ x)
  s-i = λ (x : ⟨ G₀ ⟩) → s (-₁ (𝒾 x))
  isi = λ (x : ⟨ G₀ ⟩) → 𝒾 (s (𝒾 x))


module Semantics where

open import Data.Nat hiding (_⊔_; _⊓_)
open import Data.Product
open import Data.Sum
open import Data.String using (String)
open import Data.Unit hiding (_≟_)
open import Data.Empty

open import Relation.Nullary
import Relation.Binary.PropositionalEquality as Eq
open Eq using (_≡_;_≢_; refl)
open Eq.≡-Reasoning
open import Level hiding (_⊔_) renaming (zero to lzero; suc to lsuc)

{- TODO:
* subtyping of refinement types
* union types
* intersection types
-}

Id = String

variable
  x y : Id
  ℓ : Level

data Expr : Set where
  Nat : ℕ → Expr
  Var : Id → Expr
  Lam : Id → Expr → Expr
  App : Expr → Expr → Expr
  Pair : Expr → Expr → Expr
  Fst Snd : Expr → Expr
  Inl Inr : Expr → Expr
  Case : Expr → Id → Expr → Id → Expr → Expr

data RawType : Set where
  Nat : RawType
  _⇒_ _⋆_ _⊹_ : RawType → RawType → RawType

ss⇒tt : ∀ {S S₁ T T₁ : RawType} → (S ⇒ S₁) ≡ (T ⇒ T₁) → (S ≡ T × S₁ ≡ T₁)
ss⇒tt refl = refl , refl

ss⋆tt : ∀ {S S₁ T T₁ : RawType} → (S ⋆ S₁) ≡ (T ⋆ T₁) → (S ≡ T × S₁ ≡ T₁)
ss⋆tt refl = refl , refl

ss⊹tt : ∀ {S S₁ T T₁ : RawType} → (S ⊹ S₁) ≡ (T ⊹ T₁) → (S ≡ T × S₁ ≡ T₁)
ss⊹tt refl = refl , refl

data Type : Set₁ where
  Base : (P : ℕ → Set) → Type -- refinement
  Nat : Type
  _⇒_ : Type → Type → Type
  _⋆_ : Type → Type → Type
  _⊹_ : Type → Type → Type

T-Nat = Base (λ n → ⊤) -- all natural numbers

data ne : Type → Set where
  ne-base : ∀ {P} → (∃P : Σ ℕ P) → ne (Base P)
  ne-nat : ne Nat
  ne-⇒ : ∀ {S T} → ne S → ne T → ne (S ⇒ T)
  ne-⋆ : ∀ {S T} → ne S → ne T → ne (S ⋆ T)
  ne-⊹L : ∀ {S T} → ne S → ne (S ⊹ T)
  ne-⊹R : ∀ {S T} → ne T → ne (S ⊹ T)

data Env (A : Set ℓ) : Set ℓ where
  · : Env A
  _,_⦂_ : Env A → (x : Id) → (a : A) → Env A

∥_∥ : Type → RawType
∥ Base P ∥ = Nat
∥ Nat ∥ = Nat
∥ S ⇒ S₁ ∥ = ∥ S ∥ ⇒ ∥ S₁ ∥
∥ S ⋆ S₁ ∥ = ∥ S ∥ ⋆ ∥ S₁ ∥
∥ S ⊹ S₁ ∥ = ∥ S ∥ ⊹ ∥ S₁ ∥

_∨_ : (P Q : ℕ → Set) → ℕ → Set
P ∨ Q = λ n → P n ⊎ Q n

_∧_ : (P Q : ℕ → Set) → ℕ → Set
P ∧ Q = λ n → P n × Q n

implies : ∀ {P Q : ℕ → Set} → (n : ℕ) → P n → (P n ⊎ Q n)
implies n Pn = inj₁ Pn

p*q->p : ∀ {P Q : ℕ → Set} → (n : ℕ) → (P n × Q n) → P n
p*q->p n (Pn , Qn) = Pn

_⊔_ _⊓_  : (S T : Type) {r : ∥ S ∥ ≡ ∥ T ∥} → Type

(Base P ⊔ Base P₁) {refl} = Base (P ∨ P₁)
(Base P ⊔ Nat) = Nat
(Nat ⊔ Base P) = Nat
(Nat ⊔ Nat) = Nat
((S ⇒ S₁) ⊔ (T ⇒ T₁)) {r} with ss⇒tt r
... | sss , ttt = (S ⊓ T){sss} ⇒ (S₁ ⊔ T₁){ttt}
((S ⋆ S₁) ⊔ (T ⋆ T₁)) {r} with ss⋆tt r
... | sss , ttt = (S ⊔ T){sss} ⋆ (S₁ ⊔ T₁){ttt}
((S ⊹ S₁) ⊔ (T ⊹ T₁)) {r} with ss⊹tt r
... | sss , ttt = (S ⊔ T){sss} ⊹ (S₁ ⊔ T₁){ttt}

Base P ⊓ Base P₁ = Base (P ∧ P₁)
Base P ⊓ Nat = Base P
Nat ⊓ Base P = Base P
Nat ⊓ Nat = Nat
((S ⇒ S₁) ⊓ (T ⇒ T₁)){r} with ss⇒tt r
... | sss , ttt = (S ⊔ T){sss} ⇒ (S₁ ⊓ T₁){ttt}
((S ⋆ S₁) ⊓ (T ⋆ T₁)){r} with ss⋆tt r
... | sss , ttt = (S ⊓ T){sss} ⋆ (S₁ ⊓ T₁){ttt}
((S ⊹ S₁) ⊓ (T ⊹ T₁)){r} with ss⊹tt r
... | sss , ttt = (S ⊓ T){sss} ⊹ (S₁ ⊓ T₁){ttt}

variable
  S T U S′ T′ U′ U″ : Type
  Γ Γ₁ Γ₂ : Env Type
  L M N : Expr
  n : ℕ
  P : ℕ → Set

data Split {A : Set ℓ} : Env A → Env A → Env A → Set ℓ where
  nil : Split · · ·
  lft : ∀ {a : A}{Γ Γ₁ Γ₂ : Env A} → Split Γ Γ₁ Γ₂ → Split (Γ , x ⦂ a) (Γ₁ , x ⦂ a) Γ₂
  rgt : ∀ {a : A}{Γ Γ₁ Γ₂ : Env A} → Split Γ Γ₁ Γ₂ → Split (Γ , x ⦂ a) Γ₁ (Γ₂ , x ⦂ a)

data _⦂_∈_ {A : Set ℓ} : Id → A → Env A → Set ℓ where

  found : ∀ {a : A}{E : Env A} →
    x ⦂ a ∈ (E , x ⦂ a)

  there : ∀ {a a' : A}{E : Env A} →
    x ⦂ a ∈ E →
      -- x ≢ y →
    x ⦂ a ∈ (E , y ⦂ a')

data _<:_ : Type → Type → Set where

  <:-refl :
    T <: T

  <:-base :
    (P Q : ℕ → Set) →
    (p→q : ∀ n → P n → Q n) →
    Base P <: Base Q

  <:-base-nat :
    Base P <: Nat

  <:-⇒ :
    S′ <: S →
    T <: T′ →
    (S ⇒ T) <: (S′ ⇒ T′)

  <:-⋆ :
    S <: S′ →
    T <: T′ →
    (S ⋆ T) <: (S′ ⋆ T′)

  <:-⊹ :
    S <: S′ →
    T <: T′ →
    (S ⊹ T) <: (S′ ⊹ T′)

-- subtyping is compatible with raw types

<:-raw : S <: T → ∥ S ∥ ≡ ∥ T ∥
<:-raw <:-refl = refl
<:-raw (<:-base P Q p→q) = refl
<:-raw <:-base-nat = refl
<:-raw (<:-⇒ s<:t s<:t₁) = Eq.cong₂ _⇒_ (Eq.sym (<:-raw s<:t)) (<:-raw s<:t₁)
<:-raw (<:-⋆ s<:t s<:t₁) = Eq.cong₂ _⋆_ (<:-raw s<:t) (<:-raw s<:t₁)
<:-raw (<:-⊹ s<:t s<:t₁) = Eq.cong₂ _⊹_ (<:-raw s<:t) (<:-raw s<:t₁)

<:-⊔ : ∀ S T → {c : ∥ S ∥ ≡ ∥ T ∥} → S <: (S ⊔ T){c}
<:-⊓ : ∀ S T → {c : ∥ S ∥ ≡ ∥ T ∥} → (S ⊓ T){c} <: S

<:-⊔ (Base P) (Base P₁) {refl} = <:-base P (P ∨ P₁) implies
<:-⊔ (Base P) Nat = <:-base-nat
<:-⊔ Nat (Base P) = <:-refl
<:-⊔ Nat Nat = <:-refl
<:-⊔ (S ⇒ S₁) (T ⇒ T₁) {c} with ss⇒tt c
... | c1 , c2 = <:-⇒ (<:-⊓ S T) (<:-⊔ S₁ T₁)
<:-⊔ (S ⋆ S₁) (T ⋆ T₁) {c} with ss⋆tt c
... | c1 , c2 = <:-⋆ (<:-⊔ S T) (<:-⊔ S₁ T₁)
<:-⊔ (S ⊹ S₁) (T ⊹ T₁) {c} with ss⊹tt c
... | c1 , c2 = <:-⊹ (<:-⊔ S T) (<:-⊔ S₁ T₁)

<:-⊓ (Base P) (Base P₁) {refl} = <:-base (P ∧ P₁) P p*q->p
<:-⊓ (Base P) Nat = <:-refl
<:-⊓ Nat (Base P) = <:-base-nat
<:-⊓ Nat Nat = <:-refl
<:-⊓ (S ⇒ S₁) (T ⇒ T₁) {c} with ss⇒tt c
... | c1 , c2 = <:-⇒ (<:-⊔ S T) (<:-⊓ S₁ T₁)
<:-⊓ (S ⋆ S₁) (T ⋆ T₁) {c} with ss⋆tt c
... | c1 , c2 = <:-⋆ (<:-⊓ S T) (<:-⊓ S₁ T₁)
<:-⊓ (S ⊹ S₁) (T ⊹ T₁) {c} with ss⊹tt c
... | c1 , c2 = <:-⊹ (<:-⊓ S T) (<:-⊓ S₁ T₁)

-- should be in terms of RawType for evaluation

data _⊢_⦂_ : Env Type → Expr → Type → Set₁ where

  nat' :
    Γ ⊢ Nat n ⦂ Base (_≡_ n)

  var :
    (x∈ : x ⦂ T ∈ Γ) →
    --------------------
    Γ ⊢ Var x ⦂ T

  lam :
    (Γ , x ⦂ S) ⊢ M ⦂ T →
    --------------------
    Γ ⊢ Lam x M ⦂ (S ⇒ T)

  app :
    Γ ⊢ M ⦂ (S ⇒ T) →
    Γ ⊢ N ⦂ S →
    --------------------
    Γ ⊢ App M N ⦂ T

  pair :
    Γ ⊢ M ⦂ S →
    Γ ⊢ N ⦂ T →
    --------------------
    Γ ⊢ Pair M N ⦂ (S ⋆ T)

  pair-E1 :
    Γ ⊢ M ⦂ (S ⋆ T) →
    --------------------
    Γ ⊢ Fst M ⦂ S

  pair-E2 :
    Γ ⊢ M ⦂ (S ⋆ T) →
    --------------------
    Γ ⊢ Snd M ⦂ T

  sum-I1 :
    Γ ⊢ M ⦂ S →
    --------------------
    Γ ⊢ Inl M ⦂ (S ⊹ T)

  sum-I2 :
    Γ ⊢ N ⦂ T →
    --------------------
    Γ ⊢ Inl N ⦂ (S ⊹ T)

  sum-E :
    Γ ⊢ L ⦂ (S ⊹ T) →
    (Γ , x ⦂ S) ⊢ M ⦂ U →
    (Γ , y ⦂ T) ⊢ N ⦂ U →
    --------------------
    Γ ⊢ Case L x M y N ⦂ U

split-sym : Split Γ Γ₁ Γ₂ → Split Γ Γ₂ Γ₁
split-sym nil = nil
split-sym (lft sp) = rgt (split-sym sp)
split-sym (rgt sp) = lft (split-sym sp)

weaken-∈ : Split Γ Γ₁ Γ₂ → x ⦂ T ∈ Γ₁ → x ⦂ T ∈ Γ
weaken-∈ (lft sp) found = found
weaken-∈ (rgt sp) found = there (weaken-∈ sp found)
weaken-∈ (lft sp) (there x∈) = there (weaken-∈ sp x∈)
weaken-∈ (rgt sp) (there x∈) = there (weaken-∈ sp (there x∈))

weaken : Split Γ Γ₁ Γ₂ → Γ₁ ⊢ M ⦂ T → Γ ⊢ M ⦂ T
weaken sp (nat') = nat'
weaken sp (var x∈) = var (weaken-∈ sp x∈)
weaken sp (lam ⊢M) = lam (weaken (lft sp) ⊢M)
weaken sp (app ⊢M ⊢N) = app (weaken sp ⊢M) (weaken sp ⊢N)
weaken sp (pair ⊢M ⊢N) = pair (weaken sp ⊢M) (weaken sp ⊢N)
weaken sp (pair-E1 ⊢M) = pair-E1 (weaken sp ⊢M)
weaken sp (pair-E2 ⊢M) = pair-E2 (weaken sp ⊢M)
weaken sp (sum-I1 ⊢M) = sum-I1 (weaken sp ⊢M)
weaken sp (sum-I2 ⊢N) = sum-I2 (weaken sp ⊢N)
weaken sp (sum-E ⊢L ⊢M ⊢N) = sum-E (weaken sp ⊢L) (weaken (lft sp) ⊢M) (weaken (lft sp) ⊢N)

-- incorrectness typing

P=n : ℕ → ℕ → Set
P=n = λ n x → n ≡ x

data _⊢_÷_ : Env Type → Expr → Type → Set₁ where

  nat' :
    --------------------
    · ⊢ Nat n ÷ Base (_≡_ n)

  var1 :
    ( · , x ⦂ T) ⊢ Var x ÷ T

{-
  var :
    x ⦂ T ∈ Γ →
    --------------------
    Γ ⊢ Var x ÷ T
-}

  lam :
    (· , x ⦂ S) ⊢ M ÷ T →
    --------------------
    · ⊢ Lam x M ÷ (S ⇒ T)

  pair :
    Split Γ Γ₁ Γ₂ →
    Γ₁ ⊢ M ÷ S →
    Γ₂ ⊢ N ÷ T →
    --------------------
    Γ ⊢ Pair M N ÷ (S ⋆ T)

  pair-E1 :
    Γ ⊢ M ÷ (S ⋆ T) →
    --------------------
    Γ ⊢ Fst M ÷ S

  pair-E2 :
    Γ ⊢ M ÷ (S ⋆ T) →
    --------------------
    Γ ⊢ Snd M ÷ T

  sum-E :
    Split Γ Γ₁ Γ₂ →
    Γ₁ ⊢ L ÷ (S ⊹ T) →
    (Γ₂ , x ⦂ S) ⊢ M ÷ U →
    (Γ₂ , y ⦂ T) ⊢ N ÷ U →
    --------------------
    Γ ⊢ Case L x M y N ÷ U

  sum-E′ : ∀ {ru′=ru″} →
    Split Γ Γ₁ Γ₂ →
    Γ₁ ⊢ L ÷ (S ⊹ T) →
    (Γ₂ , x ⦂ S) ⊢ M ÷ U′ →
    (Γ₂ , y ⦂ T) ⊢ N ÷ U″ →
    U ≡ (U′ ⊔ U″){ru′=ru″} →
    --------------------
    Γ ⊢ Case L x M y N ÷ U

{-
  `sub` :
    Γ ⊢ M ÷ S →
    T <: S →
    --------------------
    Γ ⊢ M ÷ T
-}

record _←_ (A B : Set) : Set where
  field
    func : A → B
    back : ∀ (b : B) → ∃ λ (a : A) → func a ≡ b

open _←_

T⟦_⟧ : Type → Set
T⟦ Base P ⟧ = Σ ℕ P
T⟦ Nat ⟧ = ℕ
T⟦ S ⇒ T ⟧ = T⟦ S ⟧ → T⟦ T ⟧
T⟦ S ⋆ T ⟧ = T⟦ S ⟧ × T⟦ T ⟧
T⟦ S ⊹ T ⟧ = T⟦ S ⟧ ⊎ T⟦ T ⟧

T'⟦_⟧ : Type → Set
T'⟦ Base P ⟧ = Σ ℕ P
T'⟦ Nat ⟧ = ℕ
T'⟦ S ⇒ T ⟧ = T'⟦ S ⟧ ← T'⟦ T ⟧
T'⟦ S ⋆ T ⟧ = T'⟦ S ⟧ × T'⟦ T ⟧
T'⟦ S ⊹ T ⟧ = T'⟦ S ⟧ ⊎ T'⟦ T ⟧

E⟦_⟧ : Env Type → Env Set
E⟦ · ⟧ = ·
E⟦ Γ , x ⦂ T ⟧ = E⟦ Γ ⟧ , x ⦂ T⟦ T ⟧

data iEnv : Env Set → Set where
  · : iEnv ·
  _,_⦂_ : ∀ {E}{A} → iEnv E → (x : Id) → (a : A) → iEnv (E , x ⦂ A)

lookup : (x ⦂ T ∈ Γ) → iEnv E⟦ Γ ⟧ → T⟦ T ⟧
lookup found (γ , _ ⦂ a) = a
lookup (there x∈) (γ , _ ⦂ a) = lookup x∈ γ

eval : Γ ⊢ M ⦂ T → iEnv E⟦ Γ ⟧ → T⟦ T ⟧
eval (nat'{n = n}) γ = n , refl
eval (var x∈) γ = lookup x∈ γ
eval (lam ⊢M) γ = λ s → eval ⊢M (γ , _ ⦂ s)
eval (app ⊢M ⊢N) γ = eval ⊢M γ (eval ⊢N γ)
eval (pair ⊢M ⊢N) γ = (eval ⊢M γ) , (eval ⊢N γ)
eval (pair-E1 ⊢M) γ = proj₁ (eval ⊢M γ)
eval (pair-E2 ⊢M) γ = proj₂ (eval ⊢M γ)
eval (sum-I1 ⊢M) γ = inj₁ (eval ⊢M γ)
eval (sum-I2 ⊢N) γ = inj₂ (eval ⊢N γ)
eval (sum-E{S = S}{T = T}{U = U} ⊢L ⊢M ⊢N) γ =
  [ (λ s → eval ⊢M (γ , _ ⦂ s)) , (λ t → eval ⊢N (γ , _ ⦂ t)) ] (eval ⊢L γ)

corr : Γ ⊢ M ÷ T → Γ ⊢ M ⦂ T
corr (nat') = nat'
corr var1 = var found
-- corr (var x) = var x
corr (lam ⊢M) = lam (corr ⊢M)
corr (pair-E1 ÷M) = pair-E1 (corr ÷M)
corr (pair-E2 ÷M) = pair-E2 (corr ÷M)
corr (pair sp ÷M ÷N) = pair (weaken sp (corr ÷M)) (weaken (split-sym sp) (corr ÷N))
corr (sum-E sp ÷L ÷M ÷N) = sum-E (weaken sp (corr  ÷L)) (weaken (lft (split-sym sp)) (corr ÷M)) (weaken (lft (split-sym sp)) (corr ÷N))
corr (sum-E′ sp ÷L ÷M ÷N U≡U′⊔U″) =
  sum-E (weaken sp (corr  ÷L)) (weaken (lft (split-sym sp)) (corr {!!})) (weaken (lft (split-sym sp)) (corr {!!}))
{-
corr (`sub` ÷M T<S) = {!÷M!}
-}

-- pick one element of a type to demonstrate non-emptiness
one : ∀ (T : Type) {net : ne T} → T⟦ T ⟧
one (Base P) {ne-base ∃P} = ∃P
one Nat = zero
one (T ⇒ T₁) {ne-⇒ ne-T ne-T₁} = λ x → one T₁ {ne-T₁}
one (T ⋆ T₁) {ne-⋆ ne-T ne-T₁} = (one T {ne-T}) , (one T₁ {ne-T₁})
one (T ⊹ T₁) {ne-⊹L ne-T} = inj₁ (one T {ne-T})
one (T ⊹ T₁) {ne-⊹R ne-T₁} = inj₂ (one T₁ {ne-T₁})

{- not needed
many : iEnv E⟦ Γ ⟧
many {·} = ·
many {Γ , x ⦂ T} = many , x ⦂ one T

gen : (x∈ : x ⦂ T ∈ Γ) (t  : T⟦ T ⟧) → iEnv E⟦ Γ ⟧
gen found t = many , _ ⦂ t
gen (there x∈) t = (gen x∈ t) , _ ⦂ one {!!}

lookup-gen : (x∈ : x ⦂ T ∈ Γ) (t  : T⟦ T ⟧) → lookup x∈ (gen x∈ t) ≡ t
lookup-gen found t = refl
lookup-gen (there x∈) t = lookup-gen x∈ t
-}

open Eq.≡-Reasoning

postulate
  ext : ∀ {A B : Set}{f g : A → B} → (∀ x → f x ≡ g x) → f ≡ g

unsplit-env : Split Γ Γ₁ Γ₂ → iEnv E⟦ Γ₁ ⟧ → iEnv E⟦ Γ₂ ⟧ → iEnv E⟦ Γ ⟧
unsplit-env nil γ₁ γ₂ = ·
unsplit-env (lft sp) (γ₁ , _ ⦂ a) γ₂ = (unsplit-env sp γ₁ γ₂) , _ ⦂ a
unsplit-env (rgt sp) γ₁ (γ₂ , _ ⦂ a) = (unsplit-env sp γ₁ γ₂) , _ ⦂ a

unsplit-split : (sp : Split Γ Γ₁ Γ₂) (γ₁ : iEnv E⟦ Γ₁ ⟧) (γ₂ : iEnv E⟦ Γ₂ ⟧) →
  unsplit-env sp γ₁ γ₂ ≡ unsplit-env (split-sym sp) γ₂ γ₁
unsplit-split nil γ₁ γ₂ = refl
unsplit-split (lft sp) (γ₁ , _ ⦂ a) γ₂ rewrite unsplit-split sp γ₁ γ₂ = refl
unsplit-split (rgt sp) γ₁ (γ₂ , _ ⦂ a) rewrite unsplit-split sp γ₁ γ₂ = refl

lookup-unsplit : (sp : Split Γ Γ₁ Γ₂) (γ₁ : iEnv E⟦ Γ₁ ⟧) (γ₂ : iEnv E⟦ Γ₂ ⟧) →
  (x∈ : x ⦂ T ∈ Γ₁) →
  lookup (weaken-∈ sp x∈) (unsplit-env sp γ₁ γ₂) ≡ lookup x∈ γ₁
lookup-unsplit (lft sp) (γ₁ , _ ⦂ a) γ₂ found = refl
lookup-unsplit (rgt sp) γ₁ (γ₂ , _ ⦂ a) found = lookup-unsplit sp γ₁ γ₂ found
lookup-unsplit (lft sp) (γ₁ , _ ⦂ a) γ₂ (there x∈) = lookup-unsplit sp γ₁ γ₂ x∈
lookup-unsplit (rgt sp) γ₁ (γ₂ , _ ⦂ a) (there x∈) = lookup-unsplit sp γ₁ γ₂ (there x∈)

eval-unsplit : (sp : Split Γ Γ₁ Γ₂) (γ₁ : iEnv E⟦ Γ₁ ⟧) (γ₂ : iEnv E⟦ Γ₂ ⟧) →
  (⊢M : Γ₁ ⊢ M ⦂ T) →
  eval (weaken sp ⊢M) (unsplit-env sp γ₁ γ₂) ≡ eval ⊢M γ₁
eval-unsplit sp γ₁ γ₂ (nat')= refl
eval-unsplit sp γ₁ γ₂ (var x∈) = lookup-unsplit sp γ₁ γ₂ x∈
eval-unsplit sp γ₁ γ₂ (lam ⊢M) = ext (λ s → eval-unsplit (lft sp) (γ₁ , _ ⦂ s) γ₂ ⊢M)
eval-unsplit sp γ₁ γ₂ (app ⊢M ⊢M₁)
  rewrite eval-unsplit sp γ₁ γ₂ ⊢M | eval-unsplit sp γ₁ γ₂ ⊢M₁ = refl
eval-unsplit sp γ₁ γ₂ (pair ⊢M ⊢M₁)
  rewrite eval-unsplit sp γ₁ γ₂ ⊢M | eval-unsplit sp γ₁ γ₂ ⊢M₁ = refl
eval-unsplit sp γ₁ γ₂ (pair-E1 ⊢M)
  rewrite eval-unsplit sp γ₁ γ₂ ⊢M = refl
eval-unsplit sp γ₁ γ₂ (pair-E2 ⊢M)
  rewrite eval-unsplit sp γ₁ γ₂ ⊢M = refl
eval-unsplit sp γ₁ γ₂ (sum-I1 ⊢M)
  rewrite eval-unsplit sp γ₁ γ₂ ⊢M = refl
eval-unsplit sp γ₁ γ₂ (sum-I2 ⊢N)
  rewrite eval-unsplit sp γ₁ γ₂ ⊢N = refl
eval-unsplit sp γ₁ γ₂ (sum-E ⊢L ⊢M ⊢N)
  rewrite eval-unsplit sp γ₁ γ₂ ⊢L
        | ext (λ s → eval-unsplit (lft sp) (γ₁ , _ ⦂ s) γ₂ ⊢M)
        | ext (λ t → eval-unsplit (lft sp) (γ₁ , _ ⦂ t) γ₂ ⊢N)
  = refl

-- soundness of the incorrectness rules
lave :
  (÷M : Γ ⊢ M ÷ T) →
  ∀ (t : T⟦ T ⟧) →
  ∃ λ (γ : iEnv E⟦ Γ ⟧) →
  eval (corr ÷M) γ ≡ t

lave nat' (n , refl) = · , refl

lave var1 t = (· , _ ⦂ t) , refl
-- lave (var x∈) t =  (gen x∈ t) , lookup-gen x∈ t
lave (lam{x = x}{S = S} ÷M) t = · , ext aux
  where
    aux : (s : T⟦ S ⟧) → eval (corr ÷M) (· , x ⦂ s) ≡ t s
    aux s with lave ÷M (t s)
    ... | (· , .x ⦂ a) , snd = {!!} -- impossible to complete!

lave (pair-E1 ÷M) t with lave ÷M (t , one {!!})
... | γ , ih = γ , Eq.cong proj₁ ih
lave (pair-E2 ÷M) t with lave ÷M (one {!!} , t)
... | γ , ih = γ , Eq.cong proj₂ ih

lave (pair sp ÷M ÷N) (s , t) with lave ÷M s | lave ÷N t
... | γ₁ , ih-M | γ₂ , ih-N =
  unsplit-env sp γ₁ γ₂ ,
  Eq.cong₂ _,_ (Eq.trans (eval-unsplit sp γ₁ γ₂ (corr ÷M)) ih-M)
               (begin eval (weaken (split-sym sp) (corr ÷N)) (unsplit-env sp γ₁ γ₂)
               ≡⟨ Eq.cong (eval (weaken (split-sym sp) (corr ÷N))) (unsplit-split sp γ₁ γ₂) ⟩
               eval (weaken (split-sym sp) (corr ÷N)) (unsplit-env (split-sym sp) γ₂ γ₁)
               ≡⟨ eval-unsplit (split-sym sp) γ₂ γ₁ (corr ÷N) ⟩
               ih-N)

-- works, but unsatisfactory!
-- this proof uses only one branch of the case
-- this choice is possible because both branches ÷M and ÷N have the same type
-- in general, U could be the union of the types of ÷M and ÷N
lave (sum-E{S = S}{T = T}{U = U} sp ÷L ÷M ÷N) u
  with lave ÷M u | lave ÷N u
... | (γ₁ , x ⦂ s) , ih-M | (γ₂ , y ⦂ t) , ih-N
  with lave ÷L (inj₁ s)
... | γ₀ , ih-L
  =
  unsplit-env sp γ₀ γ₁ ,
  (begin [
      (λ s₁ →
         eval (weaken (lft (split-sym sp)) (corr ÷M))
         (unsplit-env sp γ₀ γ₁ , x ⦂ s₁))
      ,
      (λ t₁ →
         eval (weaken (lft (split-sym sp)) (corr ÷N))
         (unsplit-env sp γ₀ γ₁ , y ⦂ t₁))
      ]
      (eval (weaken sp (corr ÷L)) (unsplit-env sp γ₀ γ₁))
  ≡⟨ Eq.cong [
      (λ s₁ →
         eval (weaken (lft (split-sym sp)) (corr ÷M))
         (unsplit-env sp γ₀ γ₁ , x ⦂ s₁))
      ,
      (λ t₁ →
         eval (weaken (lft (split-sym sp)) (corr ÷N))
         (unsplit-env sp γ₀ γ₁ , y ⦂ t₁))
      ] (eval-unsplit sp γ₀ γ₁ (corr ÷L)) ⟩
    [
      (λ s₁ →
         eval (weaken (lft (split-sym sp)) (corr ÷M))
         (unsplit-env sp γ₀ γ₁ , x ⦂ s₁))
      ,
      (λ t₁ →
         eval (weaken (lft (split-sym sp)) (corr ÷N))
         (unsplit-env sp γ₀ γ₁ , y ⦂ t₁))
      ]
      (eval (corr ÷L) γ₀)
  ≡⟨ Eq.cong
       [
       (λ s₁ →
          eval (weaken (lft (split-sym sp)) (corr ÷M))
          (unsplit-env sp γ₀ γ₁ , x ⦂ s₁))
       ,
       (λ t₁ →
          eval (weaken (lft (split-sym sp)) (corr ÷N))
          (unsplit-env sp γ₀ γ₁ , y ⦂ t₁))
       ]
       ih-L ⟩
    eval (weaken (lft (split-sym sp)) (corr ÷M)) (unsplit-env sp γ₀ γ₁ , x ⦂ s)
  ≡⟨  Eq.cong (λ γ → eval (weaken (lft (split-sym sp)) (corr ÷M)) (γ , x ⦂ s)) (unsplit-split sp γ₀ γ₁) ⟩
    eval (weaken (lft (split-sym sp)) (corr ÷M)) (unsplit-env (split-sym sp) γ₁ γ₀ , x ⦂ s)
  ≡⟨⟩
    eval (weaken (lft (split-sym sp)) (corr ÷M)) (unsplit-env (lft (split-sym sp)) (γ₁ , x ⦂ s) γ₀)
  ≡⟨ eval-unsplit (lft (split-sym sp)) (γ₁ , x ⦂ s) γ₀ (corr ÷M) ⟩
    ih-M)

lave (sum-E′{S = S}{T = T}{U = U} sp ÷L ÷M ÷N uuu) u = {!!}


------------------------------------------------------------------------
-- The delay monad is a monad up to strong bisimilarity
------------------------------------------------------------------------

{-# OPTIONS --sized-types #-}

module Delay-monad.Monad where

open import Equality.Propositional
open import Prelude
open import Prelude.Size

open import Conat equality-with-J as Conat using (zero; suc; force)
open import Monad equality-with-J

open import Delay-monad
open import Delay-monad.Bisimilarity as B

------------------------------------------------------------------------
-- Map, join and bind

-- A universe-polymorphic variant of map.

map′ : ∀ {i a b} {A : Type a} {B : Type b} →
       (A → B) → Delay A i → Delay B i
map′ f (now   x) = now (f x)
map′ f (later x) = later λ { .force → map′ f (force x) }

-- Join.

join : ∀ {i a} {A : Type a} →
       Delay (Delay A i) i → Delay A i
join (now   x) = x
join (later x) = later λ { .force → join (force x) }

-- A universe-polymorphic variant of bind.

infixl 5 _>>=′_

_>>=′_ : ∀ {i a b} {A : Type a} {B : Type b} →
         Delay A i → (A → Delay B i) → Delay B i
x >>=′ f = join (map′ f x)

instance

  -- A raw monad instance.

  delay-raw-monad : ∀ {a i} → Raw-monad (λ (A : Type a) → Delay A i)
  Raw-monad.return delay-raw-monad = now
  Raw-monad._>>=_  delay-raw-monad = _>>=′_

------------------------------------------------------------------------
-- Monad laws

left-identity′ :
  ∀ {a b} {A : Type a} {B : Type b} x (f : A → Delay B ∞) →
  return x >>=′ f ∼ f x
left-identity′ x f = reflexive (f x)

right-identity′ : ∀ {a i} {A : Type a} (x : Delay A ∞) →
                  [ i ] x >>= return ∼ x
right-identity′ (now   x) = now
right-identity′ (later x) = later λ { .force →
                              right-identity′ (force x) }

associativity′ :
  ∀ {a b c i} {A : Type a} {B : Type b} {C : Type c} →
  (x : Delay A ∞) (f : A → Delay B ∞) (g : B → Delay C ∞) →
  [ i ] x >>=′ (λ x → f x >>=′ g) ∼ x >>=′ f >>=′ g
associativity′ (now   x) f g = reflexive (f x >>=′ g)
associativity′ (later x) f g = later λ { .force →
                                 associativity′ (force x) f g }

-- The delay monad is a monad (assuming extensionality).

delay-monad :
  ∀ {a} → B.Extensionality a → Monad (λ (A : Type a) → Delay A ∞)
Monad.raw-monad      (delay-monad ext)       = delay-raw-monad
Monad.left-identity  (delay-monad ext) x f   = ext (left-identity′ x f)
Monad.right-identity (delay-monad ext) x     = ext (right-identity′ x)
Monad.associativity  (delay-monad ext) x f g = ext (associativity′ x f g)

------------------------------------------------------------------------
-- The functions map′, join and _>>=′_ preserve strong and weak
-- bisimilarity and expansion

map-cong : ∀ {k i a b} {A : Type a} {B : Type b}
           (f : A → B) {x y : Delay A ∞} →
           [ i ] x ⟨ k ⟩ y → [ i ] map′ f x ⟨ k ⟩ map′ f y
map-cong f now        = now
map-cong f (later  p) = later λ { .force → map-cong f (force p) }
map-cong f (laterˡ p) = laterˡ (map-cong f p)
map-cong f (laterʳ p) = laterʳ (map-cong f p)

join-cong : ∀ {k i a} {A : Type a} {x y : Delay (Delay A ∞) ∞} →
            [ i ] x ⟨ k ⟩ y → [ i ] join x ⟨ k ⟩ join y
join-cong now        = reflexive _
join-cong (later  p) = later λ { .force → join-cong (force p) }
join-cong (laterˡ p) = laterˡ (join-cong p)
join-cong (laterʳ p) = laterʳ (join-cong p)

infixl 5 _>>=-cong_

_>>=-cong_ :
  ∀ {k i a b} {A : Type a} {B : Type b}
    {x y : Delay A ∞} {f g : A → Delay B ∞} →
  [ i ] x ⟨ k ⟩ y → (∀ z → [ i ] f z ⟨ k ⟩ g z) →
  [ i ] x >>=′ f ⟨ k ⟩ y >>=′ g
now      >>=-cong  q = q _
later  p >>=-cong  q = later λ { .force → force p >>=-cong q }
laterˡ p >>=-cong  q = laterˡ (p >>=-cong q)
laterʳ p >>=-cong  q = laterʳ (p >>=-cong q)

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

-- The function map′ can be expressed using _>>=′_ and now.

map∼>>=-now :
  ∀ {i a b} {A : Type a} {B : Type b} {f : A → B} (x : Delay A ∞) →
  [ i ] map′ f x ∼ x >>=′ now ∘ f
map∼>>=-now (now x)   = now
map∼>>=-now (later x) = later λ { .force → map∼>>=-now (x .force) }

------------------------------------------------------------------------
-- Some lemmas relating monadic combinators to steps

-- Use of map′ does not affect the number of steps in the computation.

steps-map′ :
  ∀ {i a b} {A : Type a} {B : Type b} {f : A → B}
  (x : Delay A ∞) →
  Conat.[ i ] steps (map′ f x) ∼ steps x
steps-map′ (now x)   = zero
steps-map′ (later x) = suc λ { .force → steps-map′ (x .force) }

-- Use of _⟨$⟩_ does not affect the number of steps in the
-- computation.

steps-⟨$⟩ :
  ∀ {i ℓ} {A B : Type ℓ} {f : A → B}
  (x : Delay A ∞) →
  Conat.[ i ] steps (f ⟨$⟩ x) ∼ steps x
steps-⟨$⟩ (now x)   = zero
steps-⟨$⟩ (later x) = suc λ { .force → steps-⟨$⟩ (x .force) }


module BasicIPC.Metatheory.Hilbert-TarskiConcreteGluedImplicit where

open import BasicIPC.Syntax.Hilbert public
open import BasicIPC.Semantics.TarskiConcreteGluedImplicit public

open ImplicitSyntax (_⊢_) public

-- Completeness with respect to a particular model.

module _ {{_ : Model}} where
  reify : ∀ {A w} → w ⊩ A → unwrap w ⊢ A
  reify {α P}   s = syn s
  reify {A ▻ B} s = syn s
  reify {A ∧ B} s = pair (reify (π₁ s)) (reify (π₂ s))
  reify {⊤}    s = unit

  reify⋆ : ∀ {Ξ w} → w ⊩⋆ Ξ → unwrap w ⊢⋆ Ξ
  reify⋆ {∅}     ∙        = ∙
  reify⋆ {Ξ , A} (ts , t) = reify⋆ ts , reify t

-- Additional useful equipment.

module _ {{_ : Model}} where
  ⟪K⟫ : ∀ {A B w} → w ⊩ A → w ⊩ B ▻ A
  ⟪K⟫ {A} a = app ck (reify a) ⅋ K a

  ⟪S⟫′ : ∀ {A B C w} → w ⊩ A ▻ B ▻ C → w ⊩ (A ▻ B) ▻ A ▻ C
  ⟪S⟫′ {A} {B} {C} s₁ = app cs (syn s₁) ⅋ λ s₂ →
                          app (app cs (syn s₁)) (syn s₂) ⅋ ⟪S⟫ s₁ s₂

  _⟪,⟫′_ : ∀ {A B w} → w ⊩ A → w ⊩ B ▻ A ∧ B
  _⟪,⟫′_ {A} a = app cpair (reify a) ⅋ _,_ a

-- Soundness with respect to all models, or evaluation.

eval : ∀ {A Γ} → Γ ⊢ A → Γ ⊨ A
eval (var i)   γ = lookup i γ
eval (app t u) γ = eval t γ ⟪$⟫ eval u γ
eval ci        γ = ci ⅋ I
eval ck        γ = ck ⅋ ⟪K⟫
eval cs        γ = cs ⅋ ⟪S⟫′
eval cpair     γ = cpair ⅋ _⟪,⟫′_
eval cfst      γ = cfst ⅋ π₁
eval csnd      γ = csnd ⅋ π₂
eval unit      γ = ∙

-- TODO: Correctness of evaluation with respect to conversion.

-- The canonical model.

private
  instance
    canon : Model
    canon = record
      { _⊩ᵅ_ = λ w P → unwrap w ⊢ α P
      }

-- Soundness with respect to the canonical model.

reflectᶜ : ∀ {A w} → unwrap w ⊢ A → w ⊩ A
reflectᶜ {α P}   t = t ⅋ t
reflectᶜ {A ▻ B} t = t ⅋ λ a → reflectᶜ (app t (reify a))
reflectᶜ {A ∧ B} t = reflectᶜ (fst t) , reflectᶜ (snd t)
reflectᶜ {⊤}    t = ∙

reflectᶜ⋆ : ∀ {Ξ w} → unwrap w ⊢⋆ Ξ → w ⊩⋆ Ξ
reflectᶜ⋆ {∅}     ∙        = ∙
reflectᶜ⋆ {Ξ , A} (ts , t) = reflectᶜ⋆ ts , reflectᶜ t

-- Reflexivity and transitivity.

refl⊩⋆ : ∀ {w} → w ⊩⋆ unwrap w
refl⊩⋆ = reflectᶜ⋆ refl⊢⋆

trans⊩⋆ : ∀ {w w′ w″} → w ⊩⋆ unwrap w′ → w′ ⊩⋆ unwrap w″ → w ⊩⋆ unwrap w″
trans⊩⋆ ts us = reflectᶜ⋆ (trans⊢⋆ (reify⋆ ts) (reify⋆ us))

-- Completeness with respect to all models, or quotation.

quot : ∀ {A Γ} → Γ ⊨ A → Γ ⊢ A
quot s = reify (s refl⊩⋆)

-- Normalisation by evaluation.

norm : ∀ {A Γ} → Γ ⊢ A → Γ ⊢ A
norm = quot ∘ eval

-- TODO: Correctness of normalisation with respect to conversion.


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

open import lib.Basics
open import lib.types.Pi
open import lib.types.Paths
open import lib.types.Sigma

module lib.types.Flattening {i j k}
  (A : Type i) (B : Type j) (f g : B → A)
  (C : A → Type k) (D : (b : B) → C (f b) ≃ C (g b)) where

{- The base HIT -}

import lib.types.Generic1HIT as Generic1HIT

module W = Generic1HIT A B f g
open W public using (cc; pp)
              renaming (T to W)

module P = W.RecType C D
open P public -- using (↓-pp-in; ↓-pp-out; ↓-pp-β)
              renaming (f to P)

{- The flattened HIT -}

At : Type _
At = Σ A C

Bt : Type _
Bt = Σ B (C ∘ f)

ft : Bt → At
ft (b , d) = (f b , d)

gt : Bt → At
gt (b , d) = (g b , –> (D b) d)

module Wt = Generic1HIT At Bt ft gt
open Wt public using ()
               renaming (T to Wt)

cct = curry Wt.cc
ppt = curry Wt.pp

private
  {- Flattening -}

  module _ where
    paths-flatten :
      (b : B) → (cct (f b) == cct (g b) [ (λ w → (P w → Wt)) ↓ pp b ])
    paths-flatten b =
      ↓-app→cst-in (λ q → ppt b _ ∙' ap (cct (g b)) (↓-pp-out q))

  module FlattenCurried = W.Elim cct paths-flatten

  flatten-curried : (w : W) → (P w → Wt)
  flatten-curried = W.Elim.f cct paths-flatten

  flatten : Σ W P → Wt
  flatten (w , x) = flatten-curried w x

  {- Unflattening -}

  unflatten-cc : (ac : At) → Σ W P
  unflatten-cc (a , c) = (cc a , c)

  unflatten-pp : (bd : Bt) → unflatten-cc (ft bd) == unflatten-cc (gt bd)
  unflatten-pp (b , d) = pair= (pp b) (↓-pp-in idp)

  module Unflatten = Wt.Rec unflatten-cc unflatten-pp

  unflatten : Wt → Σ W P
  unflatten = Unflatten.f

  {- First composition -}

  flatten-unflatten : (w : Wt) → flatten (unflatten w) == w
  flatten-unflatten = Wt.elim
    (λ _ → idp)
    (λ bd → let (b , d) = bd in
      ↓-∘=idf-in flatten unflatten
      (ap flatten (ap unflatten (ppt b d))
                 =⟨ Unflatten.pp-β bd |in-ctx ap flatten ⟩
       ap flatten (pair= (pp b) (↓-pp-in idp))
                 =⟨ split-ap2 flatten (pp b) (↓-pp-in idp) ⟩
       ↓-app→cst-out (apd flatten-curried (pp b)) (↓-pp-in idp)
                 =⟨ FlattenCurried.pp-β b
                    |in-ctx (λ u → ↓-app→cst-out u (↓-pp-in idp)) ⟩
       ↓-app→cst-out (paths-flatten b) (↓-pp-in idp)
                 =⟨ idp ⟩
       ↓-app→cst-out (↓-app→cst-in
         (λ q → ppt b _ ∙' ap (cct (g b)) (↓-pp-out q))) (↓-pp-in idp)
                 =⟨ ↓-app→cst-β (λ q → ppt b _ ∙' ap (cct (g b)) (↓-pp-out q))
                                (↓-pp-in idp) ⟩
       ppt b d ∙' ap (cct (g b)) (↓-pp-out (↓-pp-in idp))
                 =⟨ ↓-pp-β idp |in-ctx (λ u → ppt b d ∙' ap (cct (g b)) u) ⟩
       ppt b d ∎))

  {- Second composition -}

  unflatten-flatten-curried : (w : W) (x : P w)
    →  unflatten (flatten-curried w x) == (w , x)
  unflatten-flatten-curried = W.elim
    (λ a x → idp)
    (λ b → ↓-Π-in
    (λ q → ↓-∘=idf-in unflatten flatten
      (ap unflatten (ap flatten (pair= (pp b) q))
                 =⟨ split-ap2 flatten (pp b) q |in-ctx ap unflatten ⟩
       ap unflatten (↓-app→cst-out (apd flatten-curried (pp b)) q)
                 =⟨ FlattenCurried.pp-β b
                    |in-ctx (λ u → ap unflatten (↓-app→cst-out u q)) ⟩
       ap unflatten (↓-app→cst-out (paths-flatten b) q)
                 =⟨ idp ⟩
       ap unflatten (↓-app→cst-out (↓-app→cst-in (λ qq → ppt b _ ∙' ap (cct (g b)) (↓-pp-out qq))) q)
                 =⟨ ↓-app→cst-β (λ qq → ppt b _ ∙' ap (cct (g b)) (↓-pp-out qq)) q |in-ctx ap unflatten ⟩
       ap unflatten (ppt b _ ∙' ap (cct (g b)) (↓-pp-out q))
                 =⟨ ap-∙' unflatten (ppt b _) (ap (cct (g b)) (↓-pp-out q)) ⟩
       ap unflatten (ppt b _) ∙' ap unflatten (ap (cct (g b)) (↓-pp-out q))
                 =⟨ Unflatten.pp-β (b , _) |in-ctx (λ u → u ∙' ap unflatten (ap (cct (g b)) (↓-pp-out q))) ⟩
       pair= (pp b) (↓-pp-in idp) ∙' ap unflatten (ap (cct (g b)) (↓-pp-out q))
                 =⟨ ∘-ap unflatten (cct (g b)) (↓-pp-out q) |in-ctx (λ u → (pair= (pp b) (↓-pp-in idp) ∙' u)) ⟩
       pair= (pp b) (↓-pp-in idp) ∙' ap (unflatten ∘ cct (g b)) (↓-pp-out q)
                 =⟨ idp ⟩
       pair= (pp b) (↓-pp-in idp) ∙' ap (λ x → (cc (g b), x)) (↓-pp-out q)
                 =⟨ ap-cst,id P (↓-pp-out q) |in-ctx (λ u → pair= (pp b) (↓-pp-in idp) ∙' u) ⟩
       pair= (pp b) (↓-pp-in idp) ∙' pair= idp (↓-pp-out q)
                 =⟨ Σ-∙' (↓-pp-in idp) (↓-pp-out q) ⟩
       pair= (pp b) (↓-pp-in idp ∙'ᵈ ↓-pp-out q)
                 =⟨ to-transp-weird q (coe-pp-β _ _) |in-ctx pair= (pp b) ⟩
       pair= (pp b) q ∎)))

  unflatten-flatten : (wx : Σ W P) → unflatten (flatten wx) == wx
  unflatten-flatten (w , x) = unflatten-flatten-curried w x

{- The equivalence -}

abstract
  flattening-equiv : Σ W P ≃ Wt
  flattening-equiv = equiv flatten unflatten flatten-unflatten unflatten-flatten


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

module Number.Instances.QuoQ.Instance where

open import Agda.Primitive renaming (_⊔_ to ℓ-max; lsuc to ℓ-suc; lzero to ℓ-zero)
open import Cubical.Foundations.Everything renaming (_⁻¹ to _⁻¹ᵖ; assoc to ∙-assoc)
open import Cubical.Foundations.Logic renaming (inl to inlᵖ; inr to inrᵖ)

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.Base renaming (_×_ to infixr 4 _×_)
open import Cubical.Data.Sigma
open import Cubical.Data.Bool as Bool using (Bool; not; true; false)
open import Cubical.Data.Empty renaming (elim to ⊥-elim; ⊥ to ⊥⊥) -- `⊥` and `elim`
open import Cubical.Foundations.Logic renaming (¬_ to ¬ᵖ_; inl to inlᵖ; inr to inrᵖ)
open import Function.Base using (it; _∋_; _$_)
open import Cubical.Foundations.Isomorphism

open import Cubical.HITs.PropositionalTruncation renaming (elim to ∣∣-elim)

open import Utils using (!_; !!_)
open import MoreLogic.Reasoning
open import MoreLogic.Definitions
open import MoreLogic.Properties
open import MorePropAlgebra.Definitions hiding (_≤''_)
open import MorePropAlgebra.Structures
open import MorePropAlgebra.Bundles
open import MorePropAlgebra.Consequences
open import Number.Structures2
open import Number.Bundles2

open import Cubical.Data.NatPlusOne using (HasFromNat; 1+_; ℕ₊₁; ℕ₊₁→ℕ) renaming
  ( _*₊₁_         to _·₊₁_
  ; *₊₁-comm      to ·₊₁-comm
  ; *₊₁-assoc     to ·₊₁-assoc
  ; *₊₁-identityˡ to ·₊₁-identityˡ
  ; *₊₁-identityʳ to ·₊₁-identityʳ
  )
open import Cubical.HITs.SetQuotients as SetQuotient using () renaming (_/_ to _//_)

open import Cubical.Data.Nat.Literals
open import Cubical.Data.Bool
open import Number.Prelude.Nat
open import Number.Prelude.QuoInt
open import Cubical.HITs.Ints.QuoInt using (HasFromNat)
open import Number.Instances.QuoQ.Definitions
open import Cubical.HITs.Rationals.QuoQ using
  ( ℚ
  ; HasFromNat
  ; isSetℚ
  ; onCommonDenom
  ; onCommonDenomSym
  ; eq/
  ; eq/⁻¹
  ; _//_
  ; _∼_
  ; [_/_]
  ) renaming
  ( [_] to [_]ᶠ
  ; ℕ₊₁→ℤ to [1+_ⁿ]ᶻ
  ; _*_ to _·_
  ; *-comm to ·-comm
  ; *-assoc to ·-assoc
  )

{-# DISPLAY [_/_] (pos 1) (1+ 0) = 1 #-}
{-# DISPLAY [_/_] (pos 0) (1+ 0) = 0 #-}

private
  ·ᶻ-commʳ : ∀ a b c → (a ·ᶻ b) ·ᶻ c ≡ (a ·ᶻ c) ·ᶻ b
  ·ᶻ-commʳ a b c = (a ·ᶻ  b) ·ᶻ c  ≡⟨ sym $ ·ᶻ-assoc a b c ⟩
                    a ·ᶻ (b  ·ᶻ c) ≡⟨ (λ i → a ·ᶻ ·ᶻ-comm b c i) ⟩
                    a ·ᶻ (c  ·ᶻ b) ≡⟨ ·ᶻ-assoc a c b ⟩
                   (a ·ᶻ  c) ·ᶻ b  ∎

  ·ᶻ-commˡ : ∀ a b c → a ·ᶻ (b ·ᶻ c) ≡ b ·ᶻ (a ·ᶻ c)
  ·ᶻ-commˡ a b c = ·ᶻ-comm a (b ·ᶻ c) ∙ sym (·ᶻ-commʳ b a c) ∙ sym (·ᶻ-assoc b a c)

  is-0<ⁿᶻ : ∀{aⁿ} → [ 0 <ᶻ [1+ aⁿ ⁿ]ᶻ ]
  is-0<ⁿᶻ {aⁿ} = ℕ₊₁.n aⁿ , +ⁿ-comm (ℕ₊₁.n aⁿ) 1

  is-0≤ⁿᶻ : ∀{aⁿ} → [ 0 ≤ᶻ [1+ aⁿ ⁿ]ᶻ ]
  is-0≤ⁿᶻ (k , p) = snotzⁿ $ sym (+ⁿ-suc k _) ∙ p

<-irrefl : ∀ a → [ ¬(a < a) ]
<-irrefl = SetQuotient.elimProp {R = _∼_} (λ a → isProp[] (¬(a < a))) γ where
  γ : (a : ℤ × ℕ₊₁) → [ ¬([ a ]ᶠ < [ a ]ᶠ) ]
  γ a@(aᶻ , aⁿ) = κ where
    aⁿᶻ = [1+ aⁿ ⁿ]ᶻ
    κ : [ ¬((aᶻ ·ᶻ aⁿᶻ) <ᶻ (aᶻ ·ᶻ aⁿᶻ)) ]
    κ = <ᶻ-irrefl (aᶻ ·ᶻ aⁿᶻ)

<-trans : (a b c : ℚ) → [ a < b ] → [ b < c ] → [ a < c ]
<-trans = SetQuotient.elimProp3 {R = _∼_} (λ a b c → isProp[] ((a < b) ⇒ (b < c) ⇒ (a < c))) γ where
  γ : (a b c : ℤ × ℕ₊₁) → [ [ a ]ᶠ < [ b ]ᶠ ] → [ [ b ]ᶠ < [ c ]ᶠ ] → [ [ a ]ᶠ < [ c ]ᶠ ]
  γ a@(aᶻ , aⁿ) b@(bᶻ , bⁿ) c@(cᶻ , cⁿ) = κ where
    aⁿᶻ   = [1+ aⁿ ⁿ]ᶻ
    bⁿᶻ   = [1+ bⁿ ⁿ]ᶻ
    cⁿᶻ   = [1+ cⁿ ⁿ]ᶻ
    0<aⁿᶻ = [ 0 <ᶻ aⁿᶻ ] ∋ is-0<ⁿᶻ
    0<bⁿᶻ = [ 0 <ᶻ bⁿᶻ ] ∋ is-0<ⁿᶻ
    0<cⁿᶻ = [ 0 <ᶻ cⁿᶻ ] ∋ is-0<ⁿᶻ
    κ : [ (aᶻ ·ᶻ bⁿᶻ) <ᶻ (bᶻ ·ᶻ aⁿᶻ) ] → [ (bᶻ ·ᶻ cⁿᶻ) <ᶻ (cᶻ ·ᶻ bⁿᶻ) ] → [ (aᶻ ·ᶻ cⁿᶻ) <ᶻ (cᶻ ·ᶻ aⁿᶻ) ]
    -- strategy: multiply with xⁿᶻ and then use <ᶻ-trans
    κ p₁ p₂ = ·ᶻ-reflects-<ᶻ (aᶻ ·ᶻ cⁿᶻ) (cᶻ ·ᶻ aⁿᶻ) bⁿᶻ 0<bⁿᶻ φ₃ where
      φ₁ = ( aᶻ ·ᶻ bⁿᶻ        <ᶻ bᶻ ·ᶻ aⁿᶻ        ⇒ᵖ⟨ ·ᶻ-preserves-<ᶻ (aᶻ ·ᶻ bⁿᶻ) (bᶻ ·ᶻ aⁿᶻ) cⁿᶻ 0<cⁿᶻ ⟩
             aᶻ ·ᶻ bⁿᶻ ·ᶻ cⁿᶻ <ᶻ bᶻ ·ᶻ aⁿᶻ ·ᶻ cⁿᶻ ⇒ᵖ⟨ transport (λ i → [ ·ᶻ-commʳ aᶻ bⁿᶻ cⁿᶻ i <ᶻ ·ᶻ-commʳ bᶻ aⁿᶻ cⁿᶻ i ]) ⟩
             aᶻ ·ᶻ cⁿᶻ ·ᶻ bⁿᶻ <ᶻ bᶻ ·ᶻ cⁿᶻ ·ᶻ aⁿᶻ ◼ᵖ) .snd p₁
      φ₂ = ( bᶻ ·ᶻ cⁿᶻ        <ᶻ cᶻ ·ᶻ bⁿᶻ        ⇒ᵖ⟨ ·ᶻ-preserves-<ᶻ (bᶻ ·ᶻ cⁿᶻ) (cᶻ ·ᶻ bⁿᶻ) aⁿᶻ 0<aⁿᶻ ⟩
             bᶻ ·ᶻ cⁿᶻ ·ᶻ aⁿᶻ <ᶻ cᶻ ·ᶻ bⁿᶻ ·ᶻ aⁿᶻ ⇒ᵖ⟨ transport (λ i → [ (bᶻ ·ᶻ cⁿᶻ ·ᶻ aⁿᶻ) <ᶻ ·ᶻ-commʳ cᶻ bⁿᶻ aⁿᶻ i ]) ⟩
             bᶻ ·ᶻ cⁿᶻ ·ᶻ aⁿᶻ <ᶻ cᶻ ·ᶻ aⁿᶻ ·ᶻ bⁿᶻ ◼ᵖ) .snd p₂
      φ₃ : [ aᶻ ·ᶻ cⁿᶻ ·ᶻ bⁿᶻ <ᶻ cᶻ ·ᶻ aⁿᶻ ·ᶻ bⁿᶻ ]
      φ₃ = <ᶻ-trans (aᶻ ·ᶻ cⁿᶻ ·ᶻ bⁿᶻ) (bᶻ ·ᶻ cⁿᶻ ·ᶻ aⁿᶻ) (cᶻ ·ᶻ aⁿᶻ ·ᶻ bⁿᶻ) φ₁ φ₂

<-asym : ∀ a b → [ a < b ] → [ ¬(b < a) ]
<-asym a b a<b b<a = <-irrefl a (<-trans a b a a<b b<a)

<-irrefl'' : ∀ a b → [ a < b ] ⊎ [ b < a ] → [ ¬(a ≡ₚ b) ]
<-irrefl'' a b (inl a<b) a≡b = <-irrefl b (substₚ (λ p → p < b) a≡b a<b)
<-irrefl'' a b (inr b<a) a≡b = <-irrefl b (substₚ (λ p → b < p) a≡b b<a)

<-tricho : (a b : ℚ) → ([ a < b ] ⊎ [ b < a ]) ⊎ [ a ≡ₚ b ] -- TODO: insert trichotomy definition here
<-tricho = SetQuotient.elimProp2 {R = _∼_} (λ a b → isProp[] ([ <-irrefl'' a b ] ([ <-asym a b ] (a < b) ⊎ᵖ (b < a)) ⊎ᵖ (a ≡ₚ b))) γ where
  γ : (a b : ℤ × ℕ₊₁) → ([ [ a ]ᶠ < [ b ]ᶠ ] ⊎ [ [ b ]ᶠ < [ a ]ᶠ ]) ⊎ [ [ a ]ᶠ ≡ₚ [ b ]ᶠ ]
  γ a@(aᶻ , aⁿ) b@(bᶻ , bⁿ) = κ where
    aⁿᶻ = [1+ aⁿ ⁿ]ᶻ
    bⁿᶻ = [1+ bⁿ ⁿ]ᶻ
    κ : ([ (aᶻ ·ᶻ bⁿᶻ) <ᶻ (bᶻ ·ᶻ aⁿᶻ) ] ⊎ [ (bᶻ ·ᶻ aⁿᶻ) <ᶻ (aᶻ ·ᶻ bⁿᶻ) ]) ⊎ [ [ aᶻ , aⁿ ]ᶠ ≡ₚ [ bᶻ , bⁿ ]ᶠ ]
    κ with <ᶻ-tricho (aᶻ ·ᶻ bⁿᶻ) (bᶻ ·ᶻ aⁿᶻ)
    ... | inl p = inl p
    ... | inr p = inr ∣ eq/ {R = _∼_} a b p ∣

<-StrictLinearOrder : [ isStrictLinearOrder _<_ ]
<-StrictLinearOrder .IsStrictLinearOrder.is-irrefl = <-irrefl
<-StrictLinearOrder .IsStrictLinearOrder.is-trans  = <-trans
<-StrictLinearOrder .IsStrictLinearOrder.is-tricho = <-tricho

<-StrictPartialOrder : [ isStrictPartialOrder _<_ ]
<-StrictPartialOrder = strictlinearorder⇒strictpartialorder _<_ <-StrictLinearOrder

_#_ : hPropRel ℚ ℚ ℓ-zero
x # y = [ <-asym x y ] (x < y) ⊎ᵖ (y < x)

#-ApartnessRel : [ isApartnessRel _#_ ]
#-ApartnessRel = #''-isApartnessRel <-StrictPartialOrder <-asym

open IsApartnessRel #-ApartnessRel public renaming
  ( is-irrefl  to #-irrefl
  ; is-sym     to #-sym
  ; is-cotrans to #-cotrans
  )

#-split' : ∀ z n → [ [ z , n ]ᶠ # 0 ] → [ z <ᶻ 0 ] ⊎ [ 0 <ᶻ z ]
#-split' (pos zero) _ p = p
#-split' (neg zero) _ p = p
#-split' (posneg i) _ p = p
#-split' (pos (suc n)) _ p = transport (λ i → [ suc (·ⁿ-identityʳ n i) <ⁿ 0 ] ⊎ [ 0 <ⁿ suc (·ⁿ-identityʳ n i) ]) p
#-split' (neg (suc n)) _ p = p

_⁻¹'  : (x : ℤ × ℕ₊₁) → [ [ x ]ᶠ # 0 ] → ℚ
(xᶻ , xⁿ) ⁻¹' = λ _ → [ signed (signᶻ xᶻ) (ℕ₊₁→ℕ xⁿ) , absᶻ⁺¹ xᶻ ]ᶠ

⁻¹'-respects-∼ : (a b : ℤ × ℕ₊₁) (p : a ∼ b)
                → PathP (λ i → [ eq/ a b p i # 0 ] → ℚ) (a ⁻¹') (b ⁻¹')
⁻¹'-respects-∼ a@(aᶻ , aⁿ) b@(bᶻ , bⁿ) p = κ where
  aⁿᶻ = [1+ aⁿ ⁿ]ᶻ; 0<aⁿᶻ : [ 0 <ᶻ aⁿᶻ ]; 0<aⁿᶻ = is-0<ⁿᶻ -- ℕ₊₁.n aⁿ , +ⁿ-comm (ℕ₊₁.n aⁿ) 1
  bⁿᶻ = [1+ bⁿ ⁿ]ᶻ; 0<bⁿᶻ : [ 0 <ᶻ bⁿᶻ ]; 0<bⁿᶻ = is-0<ⁿᶻ -- ℕ₊₁.n bⁿ , +ⁿ-comm (ℕ₊₁.n bⁿ) 1
  η : signᶻ aᶻ ≡ signᶻ bᶻ
  η i = sign (eq/ a b p i)
  s : aᶻ ·ᶻ bⁿᶻ ≡ bᶻ ·ᶻ aⁿᶻ
  s = p
  r : [ [ aᶻ , aⁿ ]ᶠ # 0 ] → (signed (signᶻ aᶻ) (ℕ₊₁→ℕ aⁿ) , absᶻ⁺¹ aᶻ) ∼ (signed (signᶻ bᶻ) (ℕ₊₁→ℕ bⁿ) , absᶻ⁺¹ bᶻ)
  r q = φ where
    φ : signed (signᶻ aᶻ) (ℕ₊₁→ℕ aⁿ) ·ᶻ [1+ absᶻ⁺¹ bᶻ ⁿ]ᶻ ≡ signed (signᶻ bᶻ) (ℕ₊₁→ℕ bⁿ) ·ᶻ [1+ absᶻ⁺¹ aᶻ ⁿ]ᶻ
    φ with #-split' aᶻ aⁿ q
    -- proof for aᶻ < 0
    ... | inl aᶻ<0 =
      signed (signᶻ aᶻ) (ℕ₊₁→ℕ aⁿ) ·ᶻ [1+ absᶻ⁺¹ bᶻ ⁿ]ᶻ ≡⟨ (λ i → signed (<ᶻ0-signᶻ aᶻ aᶻ<0 i) (ℕ₊₁→ℕ aⁿ) ·ᶻ absᶻ⁺¹-identity bᶻ (inl bᶻ<0) i) ⟩
      signed  sneg      (ℕ₊₁→ℕ aⁿ) ·ᶻ pos (absᶻ bᶻ)      ≡⟨ cong₂ signed (λ i → sneg ·ˢ q₁ i) q₂ ⟩
      signed  sneg      (ℕ₊₁→ℕ bⁿ) ·ᶻ pos (absᶻ aᶻ)      ≡⟨ (λ i → signed (<ᶻ0-signᶻ bᶻ bᶻ<0 (~ i)) (ℕ₊₁→ℕ bⁿ) ·ᶻ absᶻ⁺¹-identity aᶻ (inl aᶻ<0) (~ i)) ⟩
      signed (signᶻ bᶻ) (ℕ₊₁→ℕ bⁿ) ·ᶻ [1+ absᶻ⁺¹ aᶻ ⁿ]ᶻ ∎ where
      bᶻ<0 : [ bᶻ <ᶻ 0 ]
      bᶻ<0 = ∼-preserves-<ᶻ aᶻ aⁿ bᶻ bⁿ p .fst aᶻ<0
      abstract
        c      = suc (<ᶻ-split-neg aᶻ aᶻ<0 .fst)
        d      = suc (<ᶻ-split-neg bᶻ bᶻ<0 .fst)
        aᶻ≡-c  : aᶻ ≡ neg c
        aᶻ≡-c  = <ᶻ-split-neg aᶻ aᶻ<0 .snd
        bᶻ≡-d  : bᶻ ≡ neg d
        bᶻ≡-d  = <ᶻ-split-neg bᶻ bᶻ<0 .snd
        absa≡c : absᶻ aᶻ ≡ c
        absa≡c i = absᶻ (aᶻ≡-c i)
        absb≡d : absᶻ bᶻ ≡ d
        absb≡d i = absᶻ (bᶻ≡-d i)
      q₁ : signᶻ (pos (absᶻ bᶻ)) ≡ signᶻ (pos (absᶻ aᶻ))
      q₁ = transport (λ i → signᶻ-pos (absᶻ bᶻ) (~ i) ≡ signᶻ-pos (absᶻ aᶻ) (~ i)) refl
      s' = bᶻ ·ᶻ aⁿᶻ    ≡ aᶻ ·ᶻ bⁿᶻ                       ≡⟨ (λ i → ·ᶻ-comm bᶻ aⁿᶻ i ≡ ·ᶻ-comm aᶻ bⁿᶻ i) ⟩
           aⁿᶻ ·ᶻ bᶻ    ≡ bⁿᶻ ·ᶻ aᶻ                       ≡⟨ (λ i → aⁿᶻ ·ᶻ bᶻ≡-d i ≡ bⁿᶻ ·ᶻ aᶻ≡-c i) ⟩
           aⁿᶻ ·ᶻ neg d ≡ bⁿᶻ ·ᶻ neg c                    ≡⟨ refl ⟩
             signed (signᶻ (neg d)) (suc (ℕ₊₁.n aⁿ) ·ⁿ d)
           ≡ signed (signᶻ (neg c)) (suc (ℕ₊₁.n bⁿ) ·ⁿ c) ∎
      q₂ = (suc (ℕ₊₁.n aⁿ) ·ⁿ       d ≡ suc (ℕ₊₁.n bⁿ) ·ⁿ      c  ⇒⟨ transport (λ i → suc (ℕ₊₁.n aⁿ) ·ⁿ absb≡d (~ i) ≡ suc (ℕ₊₁.n bⁿ) ·ⁿ absa≡c (~ i)) ⟩
            suc (ℕ₊₁.n aⁿ) ·ⁿ absᶻ bᶻ ≡ suc (ℕ₊₁.n bⁿ) ·ⁿ absᶻ aᶻ ◼) (λ i → absᶻ (transport s' (sym s) i))
    -- proof for 0 < aᶻ
    ... | inr 0<aᶻ =
      signed (signᶻ aᶻ ⊕ spos) (suc (ℕ₊₁.n aⁿ) ·ⁿ suc (ℕ₊₁.n (absᶻ⁺¹ bᶻ))) ≡⟨ (λ i → signed (0<ᶻ-signᶻ aᶻ 0<aᶻ i ⊕ spos) (suc (ℕ₊₁.n aⁿ) ·ⁿ suc (ℕ₊₁.n (absᶻ⁺¹ bᶻ)))) ⟩
      signed             spos  (suc (ℕ₊₁.n aⁿ) ·ⁿ suc (ℕ₊₁.n (absᶻ⁺¹ bᶻ))) ≡⟨ transport s' (sym s) ⟩
      signed             spos  (suc (ℕ₊₁.n bⁿ) ·ⁿ suc (ℕ₊₁.n (absᶻ⁺¹ aᶻ))) ≡⟨ (λ i → signed (0<ᶻ-signᶻ bᶻ 0<bᶻ (~ i) ⊕ spos) (suc (ℕ₊₁.n bⁿ) ·ⁿ suc (ℕ₊₁.n (absᶻ⁺¹ aᶻ)))) ⟩
      signed (signᶻ bᶻ ⊕ spos) (suc (ℕ₊₁.n bⁿ) ·ⁿ suc (ℕ₊₁.n (absᶻ⁺¹ aᶻ))) ∎ where
      0<bᶻ : [ 0 <ᶻ bᶻ ]
      0<bᶻ = ∼-preserves-<ᶻ aᶻ aⁿ bᶻ bⁿ p .snd 0<aᶻ
      abstract
        c       = <ᶻ-split-pos aᶻ 0<aᶻ .fst
        d       = <ᶻ-split-pos bᶻ 0<bᶻ .fst
        aᶻ≡sc   : aᶻ ≡ pos (suc c)
        bᶻ≡sd   : bᶻ ≡ pos (suc d)
        absa≡sc : absᶻ aᶻ ≡ suc c
        absb≡sd : absᶻ bᶻ ≡ suc d
        aᶻ≡sc   = <ᶻ-split-pos aᶻ 0<aᶻ .snd
        bᶻ≡sd   = <ᶻ-split-pos bᶻ 0<bᶻ .snd
        absa≡sc i = absᶻ (aᶻ≡sc i)
        absb≡sd i = absᶻ (bᶻ≡sd i)
      s' = bᶻ  ·ᶻ aⁿᶻ         ≡ aᶻ  ·ᶻ bⁿᶻ         ≡⟨ (λ i → ·ᶻ-comm bᶻ aⁿᶻ i ≡ ·ᶻ-comm aᶻ bⁿᶻ i) ⟩
           aⁿᶻ ·ᶻ bᶻ          ≡ bⁿᶻ ·ᶻ aᶻ          ≡⟨ (λ i → aⁿᶻ ·ᶻ bᶻ≡sd i ≡ bⁿᶻ ·ᶻ aᶻ≡sc i) ⟩
           aⁿᶻ ·ᶻ pos (suc d) ≡ bⁿᶻ ·ᶻ pos (suc c) ≡⟨ refl ⟩
             pos (suc (ℕ₊₁.n aⁿ) ·ⁿ suc d)
           ≡ pos (suc (ℕ₊₁.n bⁿ) ·ⁿ suc c) ≡⟨ (λ i → pos (suc (ℕ₊₁.n aⁿ) ·ⁿ absb≡sd (~ i)) ≡ pos (suc (ℕ₊₁.n bⁿ) ·ⁿ absa≡sc (~ i))) ⟩
             pos (suc (ℕ₊₁.n aⁿ) ·ⁿ absᶻ bᶻ)
           ≡ pos (suc (ℕ₊₁.n bⁿ) ·ⁿ absᶻ aᶻ) ≡⟨ (λ i → pos (suc (ℕ₊₁.n aⁿ) ·ⁿ absᶻ⁺¹-identityⁿ bᶻ (inr 0<bᶻ) (~ i)) ≡ pos (suc (ℕ₊₁.n bⁿ) ·ⁿ absᶻ⁺¹-identityⁿ aᶻ (inr 0<aᶻ) (~ i))) ⟩
             pos (suc (ℕ₊₁.n aⁿ) ·ⁿ suc (ℕ₊₁.n (absᶻ⁺¹ bᶻ)))
           ≡ pos (suc (ℕ₊₁.n bⁿ) ·ⁿ suc (ℕ₊₁.n (absᶻ⁺¹ aᶻ))) ∎
  -- eq/ a b r : [ a ]ᶠ ≡ [ b ]ᶠ
  γ : [ [ a ]ᶠ # 0 ] ≡ [ [ b ]ᶠ # 0 ]
  γ i = [ eq/ a b p i # 0 ]
  κ : PathP _ (a ⁻¹') (b ⁻¹')
  κ i = λ(q : [ eq/ a b p i # 0 ]) → eq/ (signed (signᶻ aᶻ) (ℕ₊₁→ℕ aⁿ) , absᶻ⁺¹ aᶻ) (signed (signᶻ bᶻ) (ℕ₊₁→ℕ bⁿ) , absᶻ⁺¹ bᶻ) (r (ψ q)) i where
    ψ : [ eq/ a b p i # 0 ] → [ eq/ a b p i0 # 0 ]
    ψ p = transport (λ j → γ (i ∧ ~ j)) p

_⁻¹ : (x : ℚ) → {{ _ : [ x # 0 ]}} → ℚ
(x ⁻¹) {{p}} = SetQuotient.elim {R = _∼_} {B = λ x → [ x # 0 ] → ℚ} φ _⁻¹' ⁻¹'-respects-∼ x p where
  φ : ∀ x → isSet ([ x # 0 ] → ℚ)
  φ x = isSetΠ (λ _ → isSetℚ)
  -- φ' : ∀ x → isSet ({{_ : [ x # 0 ]}} → ℚ)
  -- φ' x = transport (λ i → ∀ x → isSet (instance≡ {A = [ x # 0 ]} {B = λ _ → ℚ} (~ i))) φ x

⊕-diagonal : ∀ s → s ⊕ s ≡ spos
⊕-diagonal spos = refl
⊕-diagonal sneg = refl

zeroᶠ : ∀ x → [ 0 / x ] ≡ 0
zeroᶠ x = eq/ (0 , x) (0 , 1) refl

#⇒#ᶻ : ∀ xᶻ xⁿ → [ [ xᶻ / xⁿ ] # 0 ] → [ xᶻ #ᶻ 0 ]
#⇒#ᶻ (pos zero)    xⁿ p = p
#⇒#ᶻ (neg zero)    xⁿ p = p
#⇒#ᶻ (posneg i)    xⁿ p = p
#⇒#ᶻ (pos (suc n)) xⁿ (inl p) = inl (transport (λ i → [ suc (·ⁿ-identityʳ n i) <ⁿ 0 ]) p)
#⇒#ᶻ (pos (suc n)) xⁿ (inr p) = inr (transport (λ i → [ 0 <ⁿ suc (·ⁿ-identityʳ n i) ]) p)
#⇒#ᶻ (neg (suc n)) xⁿ p = inl tt

#ᶻ⇒# : ∀ xᶻ xⁿ → [ xᶻ #ᶻ 0 ] → [ [ xᶻ / xⁿ ] # 0 ]
#ᶻ⇒# (pos zero) xⁿ p = p
#ᶻ⇒# (neg zero) xⁿ p = p
#ᶻ⇒# (posneg i) xⁿ p = p
#ᶻ⇒# (pos (suc n)) xⁿ (inl p) = inl (transport (λ i → [ suc (·ⁿ-identityʳ n (~ i)) <ⁿ 0 ]) p)
#ᶻ⇒# (pos (suc n)) xⁿ (inr p) = inr (transport (λ i → [ 0 <ⁿ suc (·ⁿ-identityʳ n (~ i)) ]) p)
#ᶻ⇒# (neg (suc n)) xⁿ p = inl tt

·-invʳ' : ∀ x → (p : [ [ x ]ᶠ # 0 ]) → [ x ]ᶠ · ([ x ]ᶠ ⁻¹) {{p}} ≡ 1
·-invʳ' x@(xᶻ , xⁿ) p = γ where
  aᶻ  : ℤ
  aⁿ  : ℕ₊₁
  aᶻ  = signed (signᶻ xᶻ ⊕ signᶻ xᶻ) (absᶻ xᶻ ·ⁿ suc (ℕ₊₁.n xⁿ))
  aⁿ  = 1+ (ℕ₊₁.n (absᶻ⁺¹ xᶻ) +ⁿ ℕ₊₁.n xⁿ ·ⁿ suc (ℕ₊₁.n (absᶻ⁺¹ xᶻ)))
  aⁿᶻ = [1+ aⁿ ⁿ]ᶻ
  η = absᶻ xᶻ ·ⁿ suc (ℕ₊₁.n xⁿ) ≡⟨ ·ⁿ-comm (absᶻ xᶻ) (suc (ℕ₊₁.n xⁿ)) ⟩
      suc (ℕ₊₁.n xⁿ) ·ⁿ absᶻ xᶻ ≡⟨ (λ i → suc (ℕ₊₁.n xⁿ) ·ⁿ absᶻ⁺¹-identityⁿ xᶻ (#⇒#ᶻ xᶻ xⁿ p) (~ i)) ⟩
      suc (ℕ₊₁.n xⁿ) ·ⁿ suc (ℕ₊₁.n (absᶻ⁺¹ xᶻ)) ∎
  ψ : aᶻ ≡ aⁿᶻ
  ψ = signed (signᶻ xᶻ ⊕ signᶻ xᶻ) (absᶻ xᶻ ·ⁿ suc (ℕ₊₁.n xⁿ)) ≡⟨ cong₂ signed (⊕-diagonal (signᶻ xᶻ)) refl ⟩
      signed  spos                 (absᶻ xᶻ ·ⁿ suc (ℕ₊₁.n xⁿ)) ≡⟨ cong pos η ⟩
      pos (suc (ℕ₊₁.n xⁿ) ·ⁿ suc (ℕ₊₁.n (absᶻ⁺¹ xᶻ))) ∎
  φ : aᶻ ·ᶻ 1 ≡ 1 ·ᶻ aⁿᶻ
  φ = ·ᶻ-identity aᶻ .fst ∙ ψ ∙ sym (·ᶻ-identity aⁿᶻ .snd)
  κ : (aᶻ , aⁿ) ∼ (pos 1 , (1+ 0))
  κ = φ
  γ : [ aᶻ , aⁿ ]ᶠ ≡ 1
  γ = eq/ (aᶻ , aⁿ) (pos 1 , (1+ 0)) κ

·-invʳ : ∀ x → (p : [ x # 0 ]) → x · (x ⁻¹) {{p}} ≡ 1
·-invʳ = let P : ℚ → hProp ℓ-zero
             P x = ∀ᵖ[ p ∶ x # 0 ] ([ isSetℚ ] x · (x ⁻¹) {{p}} ≡ˢ 1)
         in  SetQuotient.elimProp {R = _∼_} (λ x → isProp[] (P x)) ·-invʳ'

·-invˡ : ∀ x → (p : [ x # 0 ]) → (x ⁻¹) {{p}} · x ≡ 1
·-invˡ x p = ·-comm _ x ∙ ·-invʳ x p

-- NOTE: we already have
--   ℚ-cancelˡ : ∀ {a b} (c : ℕ₊₁) → [ ℕ₊₁→ℤ c ℤ.* a / c *₊₁ b ] ≡ [ a / b ]
--   ℚ-cancelʳ : ∀ {a b} (c : ℕ₊₁) → [ a ℤ.* ℕ₊₁→ℤ c / b *₊₁ c ] ≡ [ a / b ]

module _ (aᶻ : ℤ) (aⁿ bⁿ : ℕ₊₁) (let aⁿᶻ = [1+ aⁿ ⁿ]ᶻ) (let bⁿᶻ = [1+ bⁿ ⁿ]ᶻ) where
  private
    lem : ∀ a b → signᶻ a ≡ signᶻ (signed (signᶻ a ⊕ spos) (absᶻ a ·ⁿ (ℕ₊₁→ℕ b)))
    lem (pos zero)    b j = signᶻ (posneg (i0 ∧ ~ j))
    lem (neg zero)    b j = signᶻ (posneg (i1 ∧ ~ j))
    lem (posneg i)    b j = signᶻ (posneg (i  ∧ ~ j))
    lem (pos (suc n)) b   = refl
    lem (neg (suc n)) b   = refl

    -- one could write this as a one-liner, even without mentioning cᶻ and cⁿ, but this reduces the overall proof-readability
    expand-fraction' : [ aᶻ / aⁿ ] ≡ [ aᶻ ·ᶻ bⁿᶻ / aⁿ ·₊₁ bⁿ ]
    expand-fraction' = eq/ _ _ $ (λ i → signed (lem aᶻ bⁿ i ⊕ spos) (((λ i → absᶻ aᶻ ·ⁿ ·ⁿ-comm (ℕ₊₁→ℕ aⁿ) (ℕ₊₁→ℕ bⁿ) i) ∙ ·ⁿ-assoc (absᶻ aᶻ) (ℕ₊₁→ℕ bⁿ) (ℕ₊₁→ℕ aⁿ)) i))

  expand-fraction : [ aᶻ / aⁿ ] ≡ [ aᶻ ·ᶻ bⁿᶻ / aⁿ ·₊₁ bⁿ ]
  expand-fraction = eq/ _ _ γ where
    cᶻ : ℤ
    cᶻ = signed (signᶻ aᶻ ⊕ spos) (absᶻ aᶻ ·ⁿ (ℕ₊₁→ℕ bⁿ))
    cⁿ : ℕ₊₁
    cⁿ = 1+ (ℕ₊₁.n bⁿ +ⁿ ℕ₊₁.n aⁿ ·ⁿ (ℕ₊₁→ℕ bⁿ))
    κ =  absᶻ aᶻ ·ⁿ ((ℕ₊₁→ℕ aⁿ)  ·ⁿ (ℕ₊₁→ℕ bⁿ)) ≡[ i ]⟨ absᶻ aᶻ ·ⁿ ·ⁿ-comm (ℕ₊₁→ℕ aⁿ) (ℕ₊₁→ℕ bⁿ) i ⟩
         absᶻ aᶻ ·ⁿ ((ℕ₊₁→ℕ bⁿ)  ·ⁿ (ℕ₊₁→ℕ aⁿ)) ≡⟨ ·ⁿ-assoc (absᶻ aᶻ) (ℕ₊₁→ℕ bⁿ) (ℕ₊₁→ℕ aⁿ) ⟩
        (absᶻ aᶻ ·ⁿ  (ℕ₊₁→ℕ bⁿ)) ·ⁿ (ℕ₊₁→ℕ aⁿ)  ∎
    γ : (aᶻ , aⁿ) ∼ (cᶻ , cⁿ)
    γ i = signed (lem aᶻ bⁿ i ⊕ spos) (κ i)

·-≡' : ∀ aᶻ aⁿ bᶻ bⁿ → [ aᶻ / aⁿ ] · [ bᶻ / bⁿ ] ≡ [ aᶻ ·ᶻ bᶻ / aⁿ ·₊₁ bⁿ ]
·-≡' aᶻ aⁿ bᶻ bⁿ = refl

∼⁻¹ : ∀ aᶻ aⁿ bᶻ bⁿ → [ aᶻ / aⁿ ] ≡ [ bᶻ / bⁿ ] → (aᶻ , aⁿ) ∼ (bᶻ , bⁿ)
∼⁻¹ aᶻ aⁿ bᶻ bⁿ p = eq/⁻¹ _ _ p

-- already have this in ᶻ
signᶻ-absᶻ-identity : ∀ a → signed (signᶻ a) (absᶻ a) ≡ a
signᶻ-absᶻ-identity (pos zero) j  = posneg (i0 ∧ j)
signᶻ-absᶻ-identity (neg zero) j  = posneg (i1 ∧ j)
signᶻ-absᶻ-identity (posneg i) j  = posneg (i  ∧ j)
signᶻ-absᶻ-identity (pos (suc n)) = refl
signᶻ-absᶻ-identity (neg (suc n)) = refl

-- already have this in ᶻ
absᶻ-preserves-·ᶻ : ∀ a b → absᶻ (a ·ᶻ b) ≡ absᶻ a ·ⁿ absᶻ b
absᶻ-preserves-·ᶻ a b = refl

-- already have this in ᶻ
signᶻ-absᶻ-≡ : ∀ a b → signᶻ a ≡ signᶻ b → absᶻ a ≡ absᶻ b → a ≡ b
signᶻ-absᶻ-≡ a b p q = transport (λ i → signᶻ-absᶻ-identity a i ≡ signᶻ-absᶻ-identity b i) λ i → signed (p i) (q i)

ℚ-reflects-nom : ∀ aᶻ bᶻ n → [ aᶻ / n ] ≡ [ bᶻ / n ] → aᶻ ≡ bᶻ
ℚ-reflects-nom aᶻ bᶻ n p = signᶻ-absᶻ-≡ aᶻ bᶻ φ η where
  n' = suc (ℕ₊₁.n n)
  0<n' : [ 0 <ⁿ n' ]
  0<n' = 0<ⁿsuc (ℕ₊₁.n n)
  s = signed (signᶻ aᶻ       ) (absᶻ aᶻ ·ⁿ n') ≡⟨ cong₂ signed (sym (⊕-identityʳ (signᶻ aᶻ))) refl ⟩
      signed (signᶻ aᶻ ⊕ spos) (absᶻ aᶻ ·ⁿ n') ≡⟨ eq/⁻¹ _ _ p ⟩
      signed (signᶻ bᶻ ⊕ spos) (absᶻ bᶻ ·ⁿ n') ≡⟨ cong₂ signed (⊕-identityʳ (signᶻ bᶻ)) refl ⟩
      signed (signᶻ bᶻ       ) (absᶻ bᶻ ·ⁿ n') ∎
  φ : signᶻ aᶻ ≡ signᶻ bᶻ
  φ i = sign (p i)
  κ : absᶻ aᶻ ·ⁿ n' ≡ absᶻ bᶻ ·ⁿ n'
  κ i = absᶻ (s i)
  η : absᶻ aᶻ ≡ absᶻ bᶻ
  η = ·ⁿ-reflects-≡ʳ (absᶻ aᶻ) (absᶻ bᶻ) n' 0<n' κ

ℕ₊₁→ℕ-reflects-≡ : ∀ a b → (ℕ₊₁→ℕ a) ≡ (ℕ₊₁→ℕ b) → a ≡ b
ℕ₊₁→ℕ-reflects-≡ (1+ a) (1+ b) p i = 1+ +ⁿ-preserves-≡ˡ {1} {a} {b} p i

ℚ-reflects-denom : ∀ z aⁿ bⁿ → [ z #ᶻ 0 ] → [ z / aⁿ ] ≡ [ z / bⁿ ] → aⁿ ≡ bⁿ
ℚ-reflects-denom z aⁿ bⁿ z#0 p = ℕ₊₁→ℕ-reflects-≡ aⁿ bⁿ (sym γ) where
  κ : absᶻ z ·ⁿ (ℕ₊₁→ℕ bⁿ) ≡ absᶻ z ·ⁿ (ℕ₊₁→ℕ aⁿ)
  κ i = absᶻ (eq/⁻¹ _ _ p i)
  φ : (z#0 : [ z #ᶻ 0 ]) → [ 0 <ⁿ absᶻ z ]
  φ (inl z<0) = let (n , z≡-sn) = <ᶻ-split-neg z z<0 in transport (λ i → [ 0 <ⁿ absᶻ (z≡-sn (~ i)) ]) (0<ⁿsuc n)
  φ (inr 0<z) = let (n , z≡+sn) = <ᶻ-split-pos z 0<z in transport (λ i → [ 0 <ⁿ absᶻ (z≡+sn (~ i)) ]) (0<ⁿsuc n)
  γ : ℕ₊₁→ℕ bⁿ ≡ ℕ₊₁→ℕ aⁿ
  γ = ·ⁿ-reflects-≡ˡ (ℕ₊₁→ℕ bⁿ) (ℕ₊₁→ℕ aⁿ) (absᶻ z) (φ z#0) κ

-- already have this in QuoInt
pos-reflects-≡ : ∀ a b → pos a ≡ pos b → a ≡ b
pos-reflects-≡ a b p i = absᶻ (p i)

snotz' : ∀ n → ¬ᵗ (suc n ≡ 0)
snotz' n p = let caseNat = λ{ 0 → ⊥⊥ ; (suc n) → ℕ } in subst caseNat p 0

¬0≡1ⁿ : ¬ᵗ _≡_ {A = ℕ} 0 1
¬0≡1ⁿ p = snotzⁿ {0} (sym p)

¬0≡1ᶻ : ¬ᵗ _≡_ {A = ℤ} 0 1
¬0≡1ᶻ p = ¬0≡1ⁿ $ pos-reflects-≡ 0 1 p

¬0≡1ᶠ : ¬ᵗ _≡_ {A = ℚ} 0 1
¬0≡1ᶠ p = ¬0≡1ᶻ $ ℚ-reflects-nom 0 1 1 p

-- already have this in ᶻ
signed0≡0 : ∀ s → signed s 0 ≡ 0
signed0≡0 spos   = refl
signed0≡0 sneg i = posneg (~ i)

·-nullifiesˡ' : ∀ bᶻ bⁿ → 0 · [ bᶻ / bⁿ ] ≡ 0
·-nullifiesˡ' bᶻ bⁿ =
  [ signed (signᶻ bᶻ) 0 / (1+ (ℕ₊₁.n bⁿ +ⁿ 0)) ] ≡⟨ cong₂ [_/_] (signed0≡0 (signᶻ bᶻ)) refl ⟩
  [                   0 / (1+ (ℕ₊₁.n bⁿ +ⁿ 0)) ] ≡⟨ zeroᶠ (1+ (ℕ₊₁.n bⁿ +ⁿ 0)) ⟩
  [                   0 / (1+ 0)               ] ∎

·-nullifiesʳ' : ∀ bᶻ bⁿ → [ bᶻ / bⁿ ] · 0 ≡ 0
·-nullifiesʳ' bᶻ bⁿ = ·-comm [ bᶻ / bⁿ ] 0 ∙ ·-nullifiesˡ' bᶻ bⁿ

·-inv-<ᶻ' : (a@(aᶻ , aⁿ) b@(bᶻ , bⁿ) : ℤ × ℕ₊₁) → ([ a ]ᶠ · [ b ]ᶠ ≡ 1) → [ (0 <ᶻ aᶻ) ⊓ (0 <ᶻ bᶻ) ] ⊎ [ (aᶻ <ᶻ 0) ⊓ (bᶻ <ᶻ 0) ]
·-inv-<ᶻ' a@(aᶻ , aⁿ) b@(bᶻ , bⁿ) p = γ where
  aⁿᶻ = [1+ aⁿ ⁿ]ᶻ
  bⁿᶻ = [1+ bⁿ ⁿ]ᶻ
  κ = ([ aᶻ /  aⁿ ] · [ bᶻ /   bⁿ ] ≡ [     1      /   (1+ 0)  ] ⇒⟨ transport (cong₂ _≡_ refl (transport (λ i → 1 ≡ [ ·ᶻ-identity aⁿᶻ .snd i / ·₊₁-identityˡ aⁿ i ]) (expand-fraction 1 1 aⁿ) ∙ expand-fraction aⁿᶻ aⁿ bⁿ)) ⟩
       [ aᶻ ·ᶻ bᶻ   /   aⁿ ·₊₁ bⁿ ] ≡ [ aⁿᶻ ·ᶻ bⁿᶻ / aⁿ ·₊₁ bⁿ ] ⇒⟨ ℚ-reflects-nom _ _ (aⁿ ·₊₁ bⁿ) ⟩
         aᶻ ·ᶻ bᶻ                   ≡   aⁿᶻ ·ᶻ bⁿᶻ               ◼) p
  φ₀ : [ 0 <ⁿ ((ℕ₊₁→ℕ aⁿ) ·ⁿ (ℕ₊₁→ℕ bⁿ)) ]
  φ₀ = 0<ⁿsuc _
  φ₁ : [ 0 <ᶻ aⁿᶻ ·ᶻ bⁿᶻ ]
  φ₁ = φ₀
  φ₂ : [ 0 <ᶻ aᶻ  ·ᶻ bᶻ  ]
  φ₂ = subst (λ p → [ 0 <ᶻ p ]) (sym κ) φ₁
  γ : [ (0 <ᶻ aᶻ) ⊓ (0 <ᶻ bᶻ) ] ⊎ [ (aᶻ <ᶻ 0) ⊓ (bᶻ <ᶻ 0) ]
  γ with <ᶻ-tricho 0 aᶻ
  ... | inl (inl 0<aᶻ) = inl (0<aᶻ , ·ᶻ-reflects-0<ᶻ aᶻ bᶻ φ₂ .fst .fst 0<aᶻ)
  ... | inl (inr aᶻ<0) = inr (aᶻ<0 , ·ᶻ-reflects-0<ᶻ aᶻ bᶻ φ₂ .snd .fst aᶻ<0)
  ... | inr      0≡aᶻ  = ⊥-elim {A = λ _ → [ (0 <ᶻ aᶻ) ⊓ (0 <ᶻ bᶻ) ] ⊎ [ (aᶻ <ᶻ 0) ⊓ (bᶻ <ᶻ 0) ]} (¬0≡1ᶠ η) where
    η =   0                      ≡⟨ sym $ zeroᶠ (aⁿ ·₊₁ bⁿ) ⟩
        [ 0        / aⁿ ·₊₁ bⁿ ] ≡⟨ (λ i → [ ·ᶻ-nullifiesˡ bᶻ (~ i) / aⁿ ·₊₁ bⁿ ]) ⟩
        [ 0  ·ᶻ bᶻ / aⁿ ·₊₁ bⁿ ] ≡⟨ (λ i → [ 0≡aᶻ i ·ᶻ bᶻ / aⁿ ·₊₁ bⁿ ]) ⟩
        [ aᶻ ·ᶻ bᶻ / aⁿ ·₊₁ bⁿ ] ≡⟨ p ⟩
          1                      ∎

private
  lem0<ᶻ : ∀ aᶻ → [ 0 <ᶻ aᶻ ] → aᶻ ≡ signed (signᶻ aᶻ ⊕ spos) (absᶻ aᶻ ·ⁿ 1)
  lem0<ᶻ aᶻ 0<aᶻ =
    aᶻ                                                            ≡⟨ γ ⟩
    pos (suc n)                                                   ≡⟨ (λ i → pos (suc (·ⁿ-identityʳ n (~ i)))) ⟩
    pos (suc (n ·ⁿ 1))                                            ≡⟨ refl ⟩
    signed (signᶻ (pos (suc n)) ⊕ spos) (absᶻ (pos (suc n)) ·ⁿ 1) ≡⟨ (λ i → signed (signᶻ (γ (~ i)) ⊕ spos) (absᶻ (γ (~ i)) ·ⁿ 1)) ⟩
    signed (signᶻ  aᶻ           ⊕ spos) (absᶻ  aᶻ           ·ⁿ 1) ∎ where
    abstract
      n = <ᶻ-split-pos aᶻ 0<aᶻ .fst
      γ : aᶻ ≡ pos (suc n)
      γ = <ᶻ-split-pos aᶻ 0<aᶻ .snd

  lem<ᶻ0 : ∀ aᶻ → [ aᶻ <ᶻ 0  ] → aᶻ ≡ signed (signᶻ aᶻ ⊕ spos) (absᶻ aᶻ ·ⁿ 1)
  lem<ᶻ0 aᶻ aᶻ<0 =
    aᶻ                                                            ≡⟨ γ ⟩
    neg (suc n)                                                   ≡⟨ (λ i → neg (suc (·ⁿ-identityʳ n (~ i)))) ⟩
    neg (suc (n ·ⁿ 1))                                            ≡⟨ refl ⟩
    signed (signᶻ (neg (suc n)) ⊕ spos) (absᶻ (neg (suc n)) ·ⁿ 1) ≡⟨ (λ i → signed (signᶻ (γ (~ i)) ⊕ spos) (absᶻ (γ (~ i)) ·ⁿ 1)) ⟩
    signed (signᶻ  aᶻ           ⊕ spos) (absᶻ  aᶻ           ·ⁿ 1) ∎ where
    abstract
      n = <ᶻ-split-neg aᶻ aᶻ<0 .fst
      γ : aᶻ ≡ neg (suc n)
      γ = <ᶻ-split-neg aᶻ aᶻ<0 .snd

·-inv#0' : (a b : ℤ × ℕ₊₁) → ([ a ]ᶠ · [ b ]ᶠ ≡ 1) → [ [ a ]ᶠ # 0 ⊓ [ b ]ᶠ # 0 ]
·-inv#0' a@(aᶻ , aⁿ) b@(bᶻ , bⁿ) p with ·-inv-<ᶻ' a b p
... | inl (0<aᶻ , 0<bᶻ) = inr (subst (λ p → [ 0 <ᶻ p ]) (lem0<ᶻ aᶻ 0<aᶻ) 0<aᶻ)
                        , inr (subst (λ p → [ 0 <ᶻ p ]) (lem0<ᶻ bᶻ 0<bᶻ) 0<bᶻ)
... | inr (aᶻ<0 , bᶻ<0) = inl (subst (λ p → [ p <ᶻ 0 ]) (lem<ᶻ0 aᶻ aᶻ<0) aᶻ<0)
                        , inl (subst (λ p → [ p <ᶻ 0 ]) (lem<ᶻ0 bᶻ bᶻ<0) bᶻ<0)

·-inv#0 : ∀ x y → x · y ≡ 1 → [(x # 0) ⊓ (y # 0)]
·-inv#0 = let P : ℚ → ℚ → hProp ℓ-zero
              P x y = ([ isSetℚ ] (x · y) ≡ˢ 1) ⇒ ((x # 0) ⊓ (y # 0))
          in  SetQuotient.elimProp2 {R = _∼_} {C = λ x y → [ P x y ]} (λ x y → isProp[] (P x y)) ·-inv#0'

·-inv#0ˡ' : (a b : ℤ × ℕ₊₁) → ([ a ]ᶠ · [ b ]ᶠ ≡ 1) → [ [ a ]ᶠ # 0 ]
·-inv#0ˡ' a b p = ·-inv#0' a b p .fst

·-inv#0ˡ : ∀ x y → x · y ≡ 1 → [ x # 0 ]
·-inv#0ˡ x y p = ·-inv#0 x y p .fst

-- ·-reflects-signʳ : ∀ a b c → [ 0 < c ] → a · b ≡ c → [ ((0 < b) ⇒ (0 < a)) ⊓ ((b < 0) ⇒ (a < 0)) ]
-- ·-reflects-signʳ a b c p q .fst 0<b = {!   !}
-- ·-reflects-signʳ a b c p q .snd b<0 = {!   !}
-- ⁻¹-preserves-sign : ∀ z z⁻¹ → [ 0f < z ] → z · z⁻¹ ≡ 1f → [ 0f < z⁻¹ ]
-- ⁻¹-preserves-sign z z⁻¹

-- -- TODO: this is a plain copy from `MorePropAlgebra.Properties.AlmostPartiallyOrderedField`
-- --       we might put it into `MorePropAlgebra.Consequences`
-- -- uniqueness of inverses from `·-assoc` + `·-comm` + `·-lid` + `·-rid`
-- ·-rinv-unique'' : (x y z : F) → [ x · y ≡ˢ 1f ] → [ x · z ≡ˢ 1f ] → [ y ≡ˢ z ]
-- ·-rinv-unique'' x y z x·y≡1 x·z≡1 =
--   (      x  · y  ≡ˢ     1f ⇒ᵖ⟨ (λ x·y≡1 i → z · x·y≡1 i) ⟩
--     z · (x  · y) ≡ˢ z · 1f ⇒ᵖ⟨ pathTo⇒ (λ i → ·-assoc z x y i ≡ˢ ·-rid z i) ⟩
--    (z ·  x) · y  ≡ˢ z      ⇒ᵖ⟨ pathTo⇒ (λ i → (·-comm z x i) · y  ≡ˢ z) ⟩
--    (x ·  z) · y  ≡ˢ z      ⇒ᵖ⟨ pathTo⇒ (λ i → x·z≡1 i · y  ≡ˢ z) ⟩
--       1f    · y  ≡ˢ z      ⇒ᵖ⟨ pathTo⇒ (λ i → ·-lid y i ≡ˢ z) ⟩
--               y  ≡ˢ z      ◼ᵖ) .snd x·y≡1

·-inv'' : ∀ x → [ (∃[ y ] ([ isSetℚ ] (x · y) ≡ˢ 1)) ⇔ (x # 0) ]
·-inv'' x .fst p = ∣∣-elim (λ _ → φ') (λ{ (y , q) → γ y q } ) p where
  φ' = isProp[] (x # 0)
  γ : ∀ y → [ [ isSetℚ ] (x · y) ≡ˢ 1 ] → [ x # 0 ]
  γ y q = ·-inv#0ˡ x y q
·-inv'' x .snd p = ∣ (x ⁻¹) {{p}} , ·-invʳ x p ∣

-- -- note, that the following holds definitionally (TODO: put this at the definition of `min`)
-- _ =    min [ aᶻ , aⁿ ]ᶠ [ bᶻ , bⁿ ]ᶠ                 ≡⟨ refl ⟩
--     [ (minᶻ (aᶻ *ᶻ bⁿᶻ) (bᶻ *ᶻ aⁿᶻ) , aⁿ *₊₁ bⁿ) ]ᶠ  ∎
-- -- and we also have definitionally
-- _ : [1+ aⁿ *₊₁ bⁿ ⁿ]ᶻ ≡ aⁿᶻ *ᶻ bⁿᶻ
-- _ = refl
-- -- therefore, we have for the LHS:
-- _ = ([ cᶻ , cⁿ ]ᶠ ≤ min [ aᶻ , aⁿ ]ᶠ [ bᶻ , bⁿ ]ᶠ)                      ≡⟨ refl ⟩
--     ([ cᶻ , cⁿ ]ᶠ ≤ [ (minᶻ (aᶻ *ᶻ bⁿᶻ) (bᶻ *ᶻ aⁿᶻ) , aⁿ *₊₁ bⁿ) ]ᶠ)    ≡⟨ refl ⟩
--     (¬([ (minᶻ (aᶻ *ᶻ bⁿᶻ) (bᶻ *ᶻ aⁿᶻ) , aⁿ *₊₁ bⁿ) ]ᶠ < [ cᶻ , cⁿ ]ᶠ)) ≡⟨ refl ⟩
--     ¬((minᶻ (aᶻ *ᶻ bⁿᶻ) (bᶻ *ᶻ aⁿᶻ) *ᶻ cⁿᶻ) <ᶻ (cᶻ *ᶻ (aⁿᶻ *ᶻ bⁿᶻ)))    ≡⟨ refl ⟩
--     ((cᶻ *ᶻ (aⁿᶻ *ᶻ bⁿᶻ)) ≤ᶻ (minᶻ (aᶻ *ᶻ bⁿᶻ) (bᶻ *ᶻ aⁿᶻ) *ᶻ cⁿᶻ))     ∎
-- -- similar for the RHS:
-- _ =  ([ cᶻ , cⁿ ]ᶠ ≤ [ aᶻ , aⁿ ]ᶠ) ⊓  ([ cᶻ , cⁿ ]ᶠ ≤ [ bᶻ , bⁿ ]ᶠ) ≡⟨ refl ⟩
--     ¬([ aᶻ , aⁿ ]ᶠ < [ cᶻ , cⁿ ]ᶠ) ⊓ ¬([ bᶻ , bⁿ ]ᶠ < [ cᶻ , cⁿ ]ᶠ) ≡⟨ refl ⟩
--     ¬((aᶻ *ᶻ cⁿᶻ)  <ᶻ (cᶻ *ᶻ aⁿᶻ)) ⊓ ¬((bᶻ *ᶻ cⁿᶻ)  <ᶻ (cᶻ *ᶻ bⁿᶻ)) ≡⟨ refl ⟩
--      ((cᶻ *ᶻ aⁿᶻ)  ≤ᶻ (aᶻ *ᶻ cⁿᶻ)) ⊓  ((cᶻ *ᶻ bⁿᶻ)  ≤ᶻ (bᶻ *ᶻ cⁿᶻ)) ∎
-- -- therfore, only properties on ℤ remain
-- RHS = [ ((cᶻ *ᶻ aⁿᶻ)  ≤ᶻ (aᶻ *ᶻ cⁿᶻ)) ⊓  ((cᶻ *ᶻ bⁿᶻ)  ≤ᶻ (bᶻ *ᶻ cⁿᶻ)) ]
-- LHS = [ ((cᶻ *ᶻ (aⁿᶻ *ᶻ bⁿᶻ)) ≤ᶻ (minᶻ (aᶻ *ᶻ bⁿᶻ) (bᶻ *ᶻ aⁿᶻ) *ᶻ cⁿᶻ)) ]
-- strategy: multiply everything with aⁿᶻ, bⁿᶻ, cⁿᶻ

is-min : (x y z : ℚ) → [ (z ≤ min x y) ⇔ (z ≤ x) ⊓ (z ≤ y) ]
is-min = SetQuotient.elimProp3  {R = _∼_} (λ x y z → isProp[] ((z ≤ min x y) ⇔ (z ≤ x) ⊓ (z ≤ y))) γ where
  γ : (a b c : ℤ × ℕ₊₁) → [ ([ c ]ᶠ ≤ min [ a ]ᶠ [ b ]ᶠ) ⇔ ([ c ]ᶠ ≤ [ a ]ᶠ) ⊓ ([ c ]ᶠ ≤ [ b ]ᶠ) ]
  γ a@(aᶻ , aⁿ) b@(bᶻ , bⁿ) c@(cᶻ , cⁿ) = pathTo⇔ (sym κ) where
    aⁿᶻ = [1+ aⁿ ⁿ]ᶻ; 0≤aⁿᶻ : [ 0 ≤ᶻ aⁿᶻ ]; 0≤aⁿᶻ = is-0≤ⁿᶻ
    bⁿᶻ = [1+ bⁿ ⁿ]ᶻ; 0≤bⁿᶻ : [ 0 ≤ᶻ bⁿᶻ ]; 0≤bⁿᶻ = is-0≤ⁿᶻ
    cⁿᶻ = [1+ cⁿ ⁿ]ᶻ; 0≤cⁿᶻ : [ 0 ≤ᶻ cⁿᶻ ]; 0≤cⁿᶻ = is-0≤ⁿᶻ
    κ = (
      ((cᶻ ·ᶻ aⁿᶻ)         ≤ᶻ (aᶻ ·ᶻ cⁿᶻ)        ) ⊓ ((cᶻ ·ᶻ bⁿᶻ)         ≤ᶻ (bᶻ ·ᶻ cⁿᶻ)        ) ≡⟨ ⊓≡⊓ (·ᶻ-creates-≤ᶻ-≡ (cᶻ ·ᶻ aⁿᶻ) (aᶻ ·ᶻ cⁿᶻ) bⁿᶻ 0≤bⁿᶻ) (·ᶻ-creates-≤ᶻ-≡ (cᶻ ·ᶻ bⁿᶻ) (bᶻ ·ᶻ cⁿᶻ) aⁿᶻ 0≤aⁿᶻ) ⟩
      ((cᶻ ·ᶻ aⁿᶻ) ·ᶻ bⁿᶻ  ≤ᶻ (aᶻ ·ᶻ cⁿᶻ) ·ᶻ bⁿᶻ ) ⊓ ((cᶻ ·ᶻ bⁿᶻ) ·ᶻ aⁿᶻ  ≤ᶻ (bᶻ ·ᶻ cⁿᶻ) ·ᶻ aⁿᶻ ) ≡⟨ ⊓≡⊓ (λ i → ·ᶻ-assoc cᶻ aⁿᶻ bⁿᶻ (~ i) ≤ᶻ ·ᶻ-assoc aᶻ cⁿᶻ bⁿᶻ (~ i)) (λ i → ·ᶻ-assoc cᶻ bⁿᶻ aⁿᶻ (~ i) ≤ᶻ ·ᶻ-assoc bᶻ cⁿᶻ aⁿᶻ (~ i)) ⟩
      ( cᶻ ·ᶻ (aⁿᶻ ·ᶻ bⁿᶻ) ≤ᶻ  aᶻ ·ᶻ (cⁿᶻ ·ᶻ bⁿᶻ)) ⊓ ( cᶻ ·ᶻ (bⁿᶻ ·ᶻ aⁿᶻ) ≤ᶻ  bᶻ ·ᶻ (cⁿᶻ ·ᶻ aⁿᶻ)) ≡⟨ ⊓≡⊓ (λ i → cᶻ ·ᶻ (aⁿᶻ ·ᶻ bⁿᶻ) ≤ᶻ  aᶻ ·ᶻ (·ᶻ-comm cⁿᶻ bⁿᶻ i)) (λ i → cᶻ ·ᶻ (·ᶻ-comm bⁿᶻ aⁿᶻ i) ≤ᶻ  bᶻ ·ᶻ (·ᶻ-comm cⁿᶻ aⁿᶻ i)) ⟩
      ( cᶻ ·ᶻ (aⁿᶻ ·ᶻ bⁿᶻ) ≤ᶻ  aᶻ ·ᶻ (bⁿᶻ ·ᶻ cⁿᶻ)) ⊓ ( cᶻ ·ᶻ (aⁿᶻ ·ᶻ bⁿᶻ) ≤ᶻ  bᶻ ·ᶻ (aⁿᶻ ·ᶻ cⁿᶻ)) ≡⟨ sym $ ⇔toPath' $ is-minᶻ (aᶻ ·ᶻ (bⁿᶻ ·ᶻ cⁿᶻ)) (bᶻ ·ᶻ (aⁿᶻ ·ᶻ cⁿᶻ)) (cᶻ ·ᶻ (aⁿᶻ ·ᶻ bⁿᶻ)) ⟩
      ((cᶻ ·ᶻ (aⁿᶻ ·ᶻ bⁿᶻ)) ≤ᶻ  minᶻ ( aᶻ ·ᶻ (bⁿᶻ  ·ᶻ cⁿᶻ)) (bᶻ ·ᶻ (aⁿᶻ  ·ᶻ cⁿᶻ)))                ≡⟨ (λ i → ((cᶻ ·ᶻ (aⁿᶻ ·ᶻ bⁿᶻ)) ≤ᶻ minᶻ (·ᶻ-assoc aᶻ bⁿᶻ cⁿᶻ i) (·ᶻ-assoc bᶻ aⁿᶻ cⁿᶻ i))) ⟩
      ((cᶻ ·ᶻ (aⁿᶻ ·ᶻ bⁿᶻ)) ≤ᶻ  minᶻ ((aᶻ ·ᶻ  bⁿᶻ) ·ᶻ cⁿᶻ) ((bᶻ ·ᶻ  aⁿᶻ) ·ᶻ cⁿᶻ))                 ≡⟨ (λ i → (cᶻ ·ᶻ (aⁿᶻ ·ᶻ bⁿᶻ)) ≤ᶻ ·ᶻ-minᶻ-distribʳ (aᶻ ·ᶻ bⁿᶻ) (bᶻ ·ᶻ aⁿᶻ) cⁿᶻ 0≤cⁿᶻ (~ i)) ⟩
      ((cᶻ ·ᶻ (aⁿᶻ ·ᶻ bⁿᶻ)) ≤ᶻ (minᶻ ( aᶻ ·ᶻ  bⁿᶻ)          (bᶻ ·ᶻ  aⁿᶻ)           ·ᶻ cⁿᶻ))       ∎)

is-max : (x y z : ℚ) → [ (max x y ≤ z) ⇔ (x ≤ z) ⊓ (y ≤ z) ]
is-max = SetQuotient.elimProp3  {R = _∼_} (λ x y z → isProp[] ((max x y ≤ z) ⇔ (x ≤ z) ⊓ (y ≤ z))) γ where
  γ : (a b c : ℤ × ℕ₊₁) → [ (max [ a ]ᶠ [ b ]ᶠ ≤ [ c ]ᶠ) ⇔ ([ a ]ᶠ ≤ [ c ]ᶠ) ⊓ ([ b ]ᶠ ≤ [ c ]ᶠ) ]
  γ a@(aᶻ , aⁿ) b@(bᶻ , bⁿ) c@(cᶻ , cⁿ) = pathTo⇔ (sym κ) where
    aⁿᶻ = [1+ aⁿ ⁿ]ᶻ; 0≤aⁿᶻ : [ 0 ≤ᶻ aⁿᶻ ]; 0≤aⁿᶻ = is-0≤ⁿᶻ
    bⁿᶻ = [1+ bⁿ ⁿ]ᶻ; 0≤bⁿᶻ : [ 0 ≤ᶻ bⁿᶻ ]; 0≤bⁿᶻ = is-0≤ⁿᶻ
    cⁿᶻ = [1+ cⁿ ⁿ]ᶻ; 0≤cⁿᶻ : [ 0 ≤ᶻ cⁿᶻ ]; 0≤cⁿᶻ = is-0≤ⁿᶻ
    κ = ( aᶻ ·ᶻ cⁿᶻ         ≤ᶻ  cᶻ ·ᶻ aⁿᶻ        ) ⊓ ( bᶻ ·ᶻ cⁿᶻ         ≤ᶻ  cᶻ ·ᶻ bⁿᶻ        ) ≡⟨ ⊓≡⊓ (·ᶻ-creates-≤ᶻ-≡ (aᶻ ·ᶻ cⁿᶻ) (cᶻ ·ᶻ aⁿᶻ) bⁿᶻ 0≤bⁿᶻ) (·ᶻ-creates-≤ᶻ-≡ (bᶻ ·ᶻ cⁿᶻ) (cᶻ ·ᶻ bⁿᶻ) aⁿᶻ 0≤aⁿᶻ) ⟩
        ((aᶻ ·ᶻ cⁿᶻ) ·ᶻ bⁿᶻ ≤ᶻ (cᶻ ·ᶻ aⁿᶻ) ·ᶻ bⁿᶻ) ⊓ ((bᶻ ·ᶻ cⁿᶻ) ·ᶻ aⁿᶻ ≤ᶻ (cᶻ ·ᶻ bⁿᶻ) ·ᶻ aⁿᶻ) ≡⟨ ⊓≡⊓ (λ i → ·ᶻ-assoc aᶻ cⁿᶻ bⁿᶻ (~ i) ≤ᶻ ·ᶻ-assoc cᶻ aⁿᶻ bⁿᶻ (~ i)) (λ i → ·ᶻ-assoc bᶻ cⁿᶻ aⁿᶻ (~ i) ≤ᶻ ·ᶻ-assoc cᶻ bⁿᶻ aⁿᶻ (~ i)) ⟩
        (aᶻ ·ᶻ (cⁿᶻ ·ᶻ bⁿᶻ) ≤ᶻ cᶻ ·ᶻ (aⁿᶻ ·ᶻ bⁿᶻ)) ⊓ (bᶻ ·ᶻ (cⁿᶻ ·ᶻ aⁿᶻ) ≤ᶻ cᶻ ·ᶻ (bⁿᶻ ·ᶻ aⁿᶻ)) ≡⟨ ⊓≡⊓ (λ i → aᶻ ·ᶻ ·ᶻ-comm cⁿᶻ bⁿᶻ i ≤ᶻ cᶻ ·ᶻ (aⁿᶻ ·ᶻ bⁿᶻ)) (λ i → bᶻ ·ᶻ ·ᶻ-comm cⁿᶻ aⁿᶻ i ≤ᶻ cᶻ ·ᶻ ·ᶻ-comm bⁿᶻ aⁿᶻ i) ⟩
        (aᶻ ·ᶻ (bⁿᶻ ·ᶻ cⁿᶻ) ≤ᶻ cᶻ ·ᶻ (aⁿᶻ ·ᶻ bⁿᶻ)) ⊓ (bᶻ ·ᶻ (aⁿᶻ ·ᶻ cⁿᶻ) ≤ᶻ cᶻ ·ᶻ (aⁿᶻ ·ᶻ bⁿᶻ)) ≡⟨ sym $ ⇔toPath' $ is-maxᶻ (aᶻ ·ᶻ (bⁿᶻ ·ᶻ cⁿᶻ)) (bᶻ ·ᶻ (aⁿᶻ ·ᶻ cⁿᶻ)) (cᶻ ·ᶻ (aⁿᶻ ·ᶻ bⁿᶻ)) ⟩
        maxᶻ (aᶻ ·ᶻ (bⁿᶻ ·ᶻ cⁿᶻ)) (bᶻ ·ᶻ (aⁿᶻ ·ᶻ cⁿᶻ)) ≤ᶻ cᶻ ·ᶻ (aⁿᶻ ·ᶻ bⁿᶻ)                    ≡⟨ (λ i → maxᶻ (·ᶻ-assoc aᶻ bⁿᶻ cⁿᶻ i) (·ᶻ-assoc bᶻ aⁿᶻ cⁿᶻ i) ≤ᶻ cᶻ ·ᶻ (aⁿᶻ ·ᶻ bⁿᶻ)) ⟩
        maxᶻ ((aᶻ ·ᶻ bⁿᶻ) ·ᶻ cⁿᶻ) ((bᶻ ·ᶻ aⁿᶻ) ·ᶻ cⁿᶻ) ≤ᶻ cᶻ ·ᶻ (aⁿᶻ ·ᶻ bⁿᶻ)                    ≡⟨ (λ i → ·ᶻ-maxᶻ-distribʳ (aᶻ ·ᶻ bⁿᶻ) (bᶻ ·ᶻ aⁿᶻ) cⁿᶻ 0≤cⁿᶻ (~ i) ≤ᶻ cᶻ ·ᶻ (aⁿᶻ ·ᶻ bⁿᶻ)) ⟩
        maxᶻ (aᶻ ·ᶻ bⁿᶻ) (bᶻ ·ᶻ aⁿᶻ) ·ᶻ cⁿᶻ ≤ᶻ cᶻ ·ᶻ (aⁿᶻ ·ᶻ bⁿᶻ) ∎

private
  lem2  : ∀ a b c d → (a ·ᶻ c) ·ᶻ (b ·ᶻ d) ≡ (a ·ᶻ b) ·ᶻ (c ·ᶻ d)
  lem2' : ∀ a b c d → (a ·ᶻ c) ·ᶻ (b ·ᶻ d) ≡ (a ·ᶻ b) ·ᶻ (d ·ᶻ c)
  lem3  : ∀ a b c d → (a ·ᶻ c) ·ᶻ (b ·ᶻ d) ≡ (a ·ᶻ d) ·ᶻ (c ·ᶻ b)
  lem3' : ∀ a b c d → (a ·ᶻ c) ·ᶻ (b ·ᶻ d) ≡ (a ·ᶻ d) ·ᶻ (b ·ᶻ c)

  lem2  a b c d = ·ᶻ-commʳ a c (b ·ᶻ d) ∙ (λ i → ·ᶻ-assoc a b d i ·ᶻ c) ∙ sym (·ᶻ-assoc (a ·ᶻ b) d c) ∙ (λ i → (a ·ᶻ b) ·ᶻ ·ᶻ-comm d c i)
  lem2' a b c d = lem2 a b c d ∙ λ i → (a ·ᶻ b) ·ᶻ ·ᶻ-comm c d i
  lem3  a b c d = (λ i → a ·ᶻ c ·ᶻ ·ᶻ-comm b d i) ∙ sym (·ᶻ-assoc a c (d ·ᶻ b)) ∙ (λ i → a ·ᶻ ·ᶻ-commˡ c d b i) ∙ ·ᶻ-assoc a d (c ·ᶻ b)
  lem3' a b c d = lem3 a b c d ∙ λ i → (a ·ᶻ d) ·ᶻ ·ᶻ-comm c b i

·-preserves-< : (x y z : ℚ) → [ 0 < z ] → [ x < y ] → [ x · z < y · z ]
·-preserves-< = SetQuotient.elimProp3 {R = _∼_} (λ x y z → isProp[] ((0 < z) ⇒ (x < y) ⇒ (x · z < y · z))) γ where
  γ : (a b c : ℤ × ℕ₊₁) → [ 0 < [ c ]ᶠ ] → [ [ a ]ᶠ < [ b ]ᶠ ] → [ [ a ]ᶠ · [ c ]ᶠ < [ b ]ᶠ · [ c ]ᶠ ]
  γ a@(aᶻ , aⁿ) b@(bᶻ , bⁿ) c@(cᶻ , cⁿ) = κ where
    aⁿᶻ = [1+ aⁿ ⁿ]ᶻ
    bⁿᶻ = [1+ bⁿ ⁿ]ᶻ
    cⁿᶻ = [1+ cⁿ ⁿ]ᶻ; 0<cⁿᶻ : [ 0 <ᶻ cⁿᶻ ]; 0<cⁿᶻ = is-0<ⁿᶻ
    κ : [ 0 ·ᶻ cⁿᶻ <ᶻ cᶻ ·ᶻ 1 ] → [ aᶻ ·ᶻ bⁿᶻ <ᶻ bᶻ ·ᶻ aⁿᶻ ] → [ (aᶻ ·ᶻ cᶻ) ·ᶻ (bⁿᶻ ·ᶻ cⁿᶻ) <ᶻ (bᶻ ·ᶻ cᶻ) ·ᶻ (aⁿᶻ ·ᶻ cⁿᶻ) ]
    κ p q = transport (λ i → [ lem2 aᶻ bⁿᶻ cᶻ cⁿᶻ (~ i) <ᶻ lem2 bᶻ aⁿᶻ cᶻ cⁿᶻ (~ i) ]) $ ·ᶻ-preserves-<ᶻ (aᶻ ·ᶻ bⁿᶻ) (bᶻ ·ᶻ aⁿᶻ) (cᶻ ·ᶻ cⁿᶻ) 0<cᶻ·cⁿᶻ q where
      0<cᶻ : [ 0 <ᶻ cᶻ ]
      0<cᶻ = transport (λ i → [ ·ᶻ-nullifiesˡ cⁿᶻ i <ᶻ ·ᶻ-identity cᶻ .fst i ]) p
      0<cᶻ·cⁿᶻ : [ 0 <ᶻ (cᶻ ·ᶻ cⁿᶻ) ]
      0<cᶻ·cⁿᶻ = ·ᶻ-preserves-0<ᶻ cᶻ cⁿᶻ 0<cᶻ 0<cⁿᶻ

private
  -- continuing the pattern...
  elimProp4 : ∀{ℓ} {A : Type ℓ} → {R : A → A → Type ℓ}
            → {E : A SetQuotient./ R → A SetQuotient./ R → A SetQuotient./ R → A SetQuotient./ R → Type ℓ}
            → ((x y z w : A SetQuotient./ R ) → isProp (E x y z w))
            → ((a b c d : A) → E SetQuotient.[ a ] SetQuotient.[ b ] SetQuotient.[ c ] SetQuotient.[ d ])
            → (x y z w : A SetQuotient./ R)
            → E x y z w
  elimProp4 Eprop f = SetQuotient.elimProp (λ x → isPropΠ3 (λ y z w → Eprop x y z w))
                                           (λ x → SetQuotient.elimProp3 (λ y z w → Eprop SetQuotient.[ x ] y z w) (f x))

open import Cubical.HITs.Rationals.QuoQ using
  ( _+_
  ; -_
  ; +-assoc
  ; +-comm
  ; +-identityˡ
  ; +-identityʳ
  ; +-inverseˡ
  ; +-inverseʳ
  ) renaming
  ( *-identityˡ to ·-identityˡ
  ; *-identityʳ to ·-identityʳ
  ; *-distribˡ  to ·-distribˡ
  ; *-distribʳ  to ·-distribʳ
  )

-- the following hold definitionally:
--       [ aᶻ , aⁿ ]ᶠ · [ bᶻ , bⁿ ]ᶠ ≡ [ aᶻ · bᶻ , aⁿ ·₊₁ bⁿ ]ᶠ
--       [ aᶻ , aⁿ ]ᶠ < [ bᶻ , bⁿ ]ᶠ ≡ aᶻ ·ᶻ bⁿᶻ <ᶻ bᶻ ·ᶻ aⁿᶻ
--   min [ aᶻ , aⁿ ]ᶠ   [ bᶻ , bⁿ ]ᶠ ≡ [ (minᶻ (aᶻ ·ᶻ bⁿᶻ) (bᶻ ·ᶻ aⁿᶻ) , aⁿ ·₊₁ bⁿ) ]ᶠ
--       [ aᶻ , aⁿ ]ᶠ + [ bᶻ , bⁿ ]ᶠ ≡ [ aᶻ ·ᶻ bⁿᶻ +ᶻ bᶻ ·ᶻ aⁿᶻ , aⁿ ·₊₁ bⁿ ]ᶠ

+-<-ext : (w x y z : ℚ) → [ w + x < y + z ] → [ (w < y) ⊔ (x < z) ]
+-<-ext = elimProp4 {R = _∼_} (λ w x y z → isProp[] ((w + x < y + z) ⇒ ((w < y) ⊔ (x < z)))) γ where
  γ : (w x y z : ℤ × ℕ₊₁) → [ [ w ]ᶠ + [ x ]ᶠ < [ y ]ᶠ + [ z ]ᶠ ] → [ ([ w ]ᶠ < [ y ]ᶠ) ⊔ ([ x ]ᶠ < [ z ]ᶠ) ]
  γ w@(wᶻ , wⁿ) x@(xᶻ , xⁿ) y@(yᶻ , yⁿ) z@(zᶻ , zⁿ) = κ where
    wⁿᶻ = [1+ wⁿ ⁿ]ᶻ; 0<wⁿᶻ : [ 0 <ᶻ wⁿᶻ ]; 0<wⁿᶻ = is-0<ⁿᶻ
    xⁿᶻ = [1+ xⁿ ⁿ]ᶻ; 0<xⁿᶻ : [ 0 <ᶻ xⁿᶻ ]; 0<xⁿᶻ = is-0<ⁿᶻ
    yⁿᶻ = [1+ yⁿ ⁿ]ᶻ; 0<yⁿᶻ : [ 0 <ᶻ yⁿᶻ ]; 0<yⁿᶻ = is-0<ⁿᶻ
    zⁿᶻ = [1+ zⁿ ⁿ]ᶻ; 0<zⁿᶻ : [ 0 <ᶻ zⁿᶻ ]; 0<zⁿᶻ = is-0<ⁿᶻ
    0<xzⁿᶻ : [ 0 <ᶻ xⁿᶻ ·ᶻ zⁿᶻ ]; 0<xzⁿᶻ = ·ᶻ-preserves-0<ᶻ xⁿᶻ zⁿᶻ 0<xⁿᶻ 0<zⁿᶻ
    0<wyⁿᶻ : [ 0 <ᶻ wⁿᶻ ·ᶻ yⁿᶻ ]; 0<wyⁿᶻ = ·ᶻ-preserves-0<ᶻ wⁿᶻ yⁿᶻ 0<wⁿᶻ 0<yⁿᶻ
    φ₁ = wᶻ ·ᶻ xⁿᶻ ·ᶻ (yⁿᶻ ·ᶻ zⁿᶻ) ≡⟨ lem2  wᶻ yⁿᶻ xⁿᶻ zⁿᶻ ⟩ wᶻ ·ᶻ yⁿᶻ ·ᶻ (xⁿᶻ ·ᶻ zⁿᶻ) ∎
    φ₂ = xᶻ ·ᶻ wⁿᶻ ·ᶻ (yⁿᶻ ·ᶻ zⁿᶻ) ≡⟨ lem3  xᶻ yⁿᶻ wⁿᶻ zⁿᶻ ⟩ xᶻ ·ᶻ zⁿᶻ ·ᶻ (wⁿᶻ ·ᶻ yⁿᶻ) ∎
    φ₃ = yᶻ ·ᶻ zⁿᶻ ·ᶻ (wⁿᶻ ·ᶻ xⁿᶻ) ≡⟨ lem2' yᶻ wⁿᶻ zⁿᶻ xⁿᶻ ⟩ yᶻ ·ᶻ wⁿᶻ ·ᶻ (xⁿᶻ ·ᶻ zⁿᶻ) ∎
    φ₄ = zᶻ ·ᶻ yⁿᶻ ·ᶻ (wⁿᶻ ·ᶻ xⁿᶻ) ≡⟨ lem3' zᶻ wⁿᶻ yⁿᶻ xⁿᶻ ⟩ zᶻ ·ᶻ xⁿᶻ ·ᶻ (wⁿᶻ ·ᶻ yⁿᶻ) ∎
    φ = (    (wᶻ ·ᶻ xⁿᶻ +ᶻ xᶻ ·ᶻ wⁿᶻ) ·ᶻ (yⁿᶻ ·ᶻ zⁿᶻ)
          <ᶻ (yᶻ ·ᶻ zⁿᶻ +ᶻ zᶻ ·ᶻ yⁿᶻ) ·ᶻ (wⁿᶻ ·ᶻ xⁿᶻ) ⇒ᵖ⟨ transport (λ i → [ is-distᶻ (wᶻ ·ᶻ xⁿᶻ) (xᶻ ·ᶻ wⁿᶻ) (yⁿᶻ ·ᶻ zⁿᶻ) .snd i <ᶻ is-distᶻ (yᶻ ·ᶻ zⁿᶻ) (zᶻ ·ᶻ yⁿᶻ) (wⁿᶻ ·ᶻ xⁿᶻ) .snd i ]) ⟩
             wᶻ ·ᶻ xⁿᶻ ·ᶻ (yⁿᶻ ·ᶻ zⁿᶻ) +ᶻ xᶻ ·ᶻ wⁿᶻ ·ᶻ (yⁿᶻ ·ᶻ zⁿᶻ)
          <ᶻ yᶻ ·ᶻ zⁿᶻ ·ᶻ (wⁿᶻ ·ᶻ xⁿᶻ) +ᶻ zᶻ ·ᶻ yⁿᶻ ·ᶻ (wⁿᶻ ·ᶻ xⁿᶻ) ⇒ᵖ⟨ transport (λ i → [ φ₁ i +ᶻ φ₂ i <ᶻ φ₃ i +ᶻ φ₄ i ]) ⟩
             wᶻ ·ᶻ yⁿᶻ ·ᶻ (xⁿᶻ ·ᶻ zⁿᶻ) +ᶻ xᶻ ·ᶻ zⁿᶻ ·ᶻ (wⁿᶻ ·ᶻ yⁿᶻ)
          <ᶻ yᶻ ·ᶻ wⁿᶻ ·ᶻ (xⁿᶻ ·ᶻ zⁿᶻ) +ᶻ zᶻ ·ᶻ xⁿᶻ ·ᶻ (wⁿᶻ ·ᶻ yⁿᶻ) ◼ᵖ) .snd
    P₁ = wᶻ ·ᶻ yⁿᶻ ·ᶻ (xⁿᶻ ·ᶻ zⁿᶻ) <ᶻ yᶻ ·ᶻ wⁿᶻ ·ᶻ (xⁿᶻ ·ᶻ zⁿᶻ)
    P₂ = xᶻ ·ᶻ zⁿᶻ ·ᶻ (wⁿᶻ ·ᶻ yⁿᶻ) <ᶻ zᶻ ·ᶻ xⁿᶻ ·ᶻ (wⁿᶻ ·ᶻ yⁿᶻ)
    Q₁ = wᶻ ·ᶻ yⁿᶻ <ᶻ yᶻ ·ᶻ wⁿᶻ
    Q₂ = xᶻ ·ᶻ zⁿᶻ <ᶻ zᶻ ·ᶻ xⁿᶻ
    ψ = +ᶻ-<ᶻ-ext (wᶻ ·ᶻ yⁿᶻ ·ᶻ (xⁿᶻ ·ᶻ zⁿᶻ)) (xᶻ ·ᶻ zⁿᶻ ·ᶻ (wⁿᶻ ·ᶻ yⁿᶻ)) (yᶻ ·ᶻ wⁿᶻ ·ᶻ (xⁿᶻ ·ᶻ zⁿᶻ)) (zᶻ ·ᶻ xⁿᶻ ·ᶻ (wⁿᶻ ·ᶻ yⁿᶻ))
    κ : [ (wᶻ ·ᶻ xⁿᶻ +ᶻ xᶻ ·ᶻ wⁿᶻ) ·ᶻ (yⁿᶻ ·ᶻ zⁿᶻ) <ᶻ (yᶻ ·ᶻ zⁿᶻ +ᶻ zᶻ ·ᶻ yⁿᶻ) ·ᶻ (wⁿᶻ ·ᶻ xⁿᶻ) ]
      → [ Q₁ ⊔ Q₂ ]
    κ p =  case ψ (φ p) as P₁ ⊔ P₂ ⇒ (Q₁ ⊔ Q₂) of λ
      { (inl q) → inlᵖ (·ᶻ-reflects-<ᶻ (wᶻ ·ᶻ yⁿᶻ) (yᶻ ·ᶻ wⁿᶻ) (xⁿᶻ ·ᶻ zⁿᶻ) 0<xzⁿᶻ q)
      ; (inr q) → inrᵖ (·ᶻ-reflects-<ᶻ (xᶻ ·ᶻ zⁿᶻ) (zᶻ ·ᶻ xⁿᶻ) (wⁿᶻ ·ᶻ yⁿᶻ) 0<wyⁿᶻ q)
      }

+-Semigroup : [ isSemigroup _+_ ]
+-Semigroup .IsSemigroup.is-set   = isSetℚ
+-Semigroup .IsSemigroup.is-assoc = +-assoc

·-Semigroup : [ isSemigroup _·_ ]
·-Semigroup .IsSemigroup.is-set   = isSetℚ
·-Semigroup .IsSemigroup.is-assoc = ·-assoc

+-Monoid : [ isMonoid 0 _+_ ]
+-Monoid .IsMonoid.is-Semigroup  = +-Semigroup
+-Monoid .IsMonoid.is-identity x = +-identityʳ x , +-identityˡ x

·-Monoid : [ isMonoid 1 _·_ ]
·-Monoid .IsMonoid.is-Semigroup  = ·-Semigroup
·-Monoid .IsMonoid.is-identity x = ·-identityʳ x , ·-identityˡ x

is-Semiring : [ isSemiring 0 1 _+_ _·_ ]
is-Semiring .IsSemiring.+-Monoid = +-Monoid
is-Semiring .IsSemiring.·-Monoid = ·-Monoid
is-Semiring .IsSemiring.+-comm   = +-comm
is-Semiring .IsSemiring.is-dist x y z = sym (·-distribˡ x y z) , sym (·-distribʳ x y z)

is-CommSemiring : [ isCommSemiring 0 1 _+_ _·_ ]
is-CommSemiring .IsCommSemiring.is-Semiring = is-Semiring
is-CommSemiring .IsCommSemiring.·-comm      = ·-comm

≤-Lattice : [ isLattice _≤_ min max ]
≤-Lattice .IsLattice.≤-PartialOrder = linearorder⇒partialorder _ (≤'-isLinearOrder <-StrictLinearOrder)
≤-Lattice .IsLattice.is-min         = is-min
≤-Lattice .IsLattice.is-max         = is-max

is-LinearlyOrderedCommSemiring : [ isLinearlyOrderedCommSemiring 0 1 _+_ _·_ _<_ min max ]
is-LinearlyOrderedCommSemiring .IsLinearlyOrderedCommSemiring.is-CommSemiring     = is-CommSemiring
is-LinearlyOrderedCommSemiring .IsLinearlyOrderedCommSemiring.<-StrictLinearOrder = <-StrictLinearOrder
is-LinearlyOrderedCommSemiring .IsLinearlyOrderedCommSemiring.≤-Lattice           = ≤-Lattice
is-LinearlyOrderedCommSemiring .IsLinearlyOrderedCommSemiring.+-<-ext             = +-<-ext
is-LinearlyOrderedCommSemiring .IsLinearlyOrderedCommSemiring.·-preserves-<       = ·-preserves-<

+-inverse : (x : ℚ) → (x + (- x) ≡ 0) × ((- x) + x ≡ 0)
+-inverse x .fst = +-inverseʳ x
+-inverse x .snd = +-inverseˡ x

is-LinearlyOrderedCommRing : [ isLinearlyOrderedCommRing 0 1 _+_ _·_ -_ _<_ min max ]
is-LinearlyOrderedCommRing. IsLinearlyOrderedCommRing.is-LinearlyOrderedCommSemiring = is-LinearlyOrderedCommSemiring
is-LinearlyOrderedCommRing. IsLinearlyOrderedCommRing.+-inverse                      = +-inverse

is-LinearlyOrderedField : [ isLinearlyOrderedField 0 1 _+_ _·_ -_ _<_ min max ]
is-LinearlyOrderedField .IsLinearlyOrderedField.is-LinearlyOrderedCommRing = is-LinearlyOrderedCommRing
is-LinearlyOrderedField .IsLinearlyOrderedField.·-inv''                    = ·-inv''

ℚbundle : LinearlyOrderedField {ℓ-zero} {ℓ-zero}
ℚbundle .LinearlyOrderedField.Carrier                  = ℚ
ℚbundle .LinearlyOrderedField.0f                       = 0
ℚbundle .LinearlyOrderedField.1f                       = 1
ℚbundle .LinearlyOrderedField._+_                      = _+_
ℚbundle .LinearlyOrderedField.-_                       = -_
ℚbundle .LinearlyOrderedField._·_                      = _·_
ℚbundle .LinearlyOrderedField.min                      = min
ℚbundle .LinearlyOrderedField.max                      = max
ℚbundle .LinearlyOrderedField._<_                      = _<_
ℚbundle .LinearlyOrderedField.is-LinearlyOrderedField  = is-LinearlyOrderedField