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

open import lib.Basics
open import lib.NType2
open import lib.types.TLevel
open import lib.types.Sigma
open import lib.types.Pi

module lib.types.Groupoid where

record GroupoidStructure {i j} {El : Type i} (Arr : El → El → Type j)
  (_ : ∀ x y → has-level 0 (Arr x y)) : Type (lmax i j) where
  field
    ident : ∀ {x} → Arr x x
    inv : ∀ {x y} → Arr x y → Arr y x
    comp : ∀ {x y z} → Arr x y → Arr y z → Arr x z
    unit-l : ∀ {x y} (a : Arr x y) → comp ident a == a
    assoc : ∀ {x y z w} (a : Arr x y) (b : Arr y z) (c : Arr z w)
              → comp (comp a b) c == comp a (comp b c)
    inv-l : ∀ {x y } (a : Arr x y) → (comp (inv a) a) == ident

  private
    infix 80 _⊙_
    _⊙_ = comp

  abstract
    inv-r  : ∀ {x y} (a : Arr x y) → a ⊙ inv a == ident
    inv-r a =
      a ⊙ inv a                           =⟨ ! $ unit-l (a ⊙ inv a) ⟩
      ident ⊙ (a ⊙ inv a)                 =⟨ ! $ inv-l (inv a) |in-ctx _⊙ (a ⊙ inv a) ⟩
      (inv (inv a) ⊙ inv a) ⊙ (a ⊙ inv a) =⟨ assoc (inv (inv a)) (inv a) (a ⊙ inv a) ⟩
      inv (inv a) ⊙ (inv a ⊙ (a ⊙ inv a)) =⟨ ! $ assoc (inv a) a (inv a) |in-ctx inv (inv a) ⊙_ ⟩
      inv (inv a) ⊙ ((inv a ⊙ a) ⊙ inv a) =⟨ inv-l a |in-ctx (λ h → inv (inv a) ⊙ (h ⊙ inv a)) ⟩
      inv (inv a) ⊙ (ident ⊙ inv a)       =⟨ unit-l (inv a) |in-ctx inv (inv a) ⊙_ ⟩
      inv (inv a) ⊙ inv a                 =⟨ inv-l (inv a) ⟩
      ident                               =∎

    unit-r : ∀ {x y} (a : Arr x y) → a ⊙ ident == a
    unit-r a =
      a ⊙ ident          =⟨ ! (inv-l a) |in-ctx a ⊙_ ⟩
      a ⊙ (inv a ⊙ a)    =⟨ ! $ assoc a (inv a) a ⟩
      (a ⊙ inv a) ⊙ a    =⟨ inv-r a |in-ctx _⊙ a ⟩
      ident ⊙ a          =⟨ unit-l a ⟩
      a                  =∎

record PreGroupoid i j : Type (lsucc (lmax i j)) where
  constructor groupoid
  field
    El : Type i
    Arr : El → El → Type j
    Arr-level : ∀ x y → has-level 0 (Arr x y)
    groupoid-struct : GroupoidStructure Arr Arr-level


module Issue475 where

data Box (A : Set) : Set where
  box : A → Box A

postulate
  T : {A : Set} → A → Set

unbox : {A : Set} → Box A → A

data BoxT {A : Set}(b : Box A) : Set where
  boxT : T (unbox b) → BoxT b

-- Can't be projection-like since we've already used it in BoxT
unbox (box x) = x

postulate
  A : Set
  b : Box A

unboxT : BoxT b → T (unbox b)
unboxT (boxT p) = p


{-

A possible implementation of the following HIT arr. Note that this type is
mutually defined with App, that is to say, they are defined by
induction-recursion!

Inductive arr (A,B:Set) : Set :=
| base : B -> arr A B
| step : (A -> arr A B) -> arr A B.
| path : (forall x : A, App f x = App g x) -> f = g.

where App is defined by

Fixpoint App (f : arr A B)(a : A) :=
match f with
| base b => b
| step g => App (g a) a
end.

Due to the fact that arr and App are defined by induction-recuriosn, the
recursion principle for arr needs to make extra assumptions, see the parameters
of the arr-Rec module.
Effectively, we see the function space A → B as algebra for the functor (-) × A
and App is an algebra homomorphism from arr A B to A → B.
This is reflected in the extra assumption in the recursion principle.

-}

{-# OPTIONS --without-K #-}
open import lib.Basics
open import lib.PathGroupoid
open import lib.types.Paths
open import lib.Funext

module _ where
  private
    data #arr-aux (A B : Set) : Set where
      #base : B → #arr-aux A B
      #step : (A → #arr-aux A B) → #arr-aux A B

    #App : ∀{A B} → #arr-aux A B → A → B
    #App (#base b) a = b
    #App (#step g) a = #App (g a) a

  _~>_ : Set → Set → Set
  _~>_ = #arr-aux

  base : ∀{A B} → B → A ~> B
  base = #base

  step : ∀{A B} → (A → A ~> B) → A ~> B
  step = #step

  App : ∀{A B} → A ~> B → A → B
  App = #App

  Abstr : ∀{A B} → (A → B) → A ~> B
  Abstr f = step (λ x → base (f x))

  postulate
    path : ∀{A B} (f g : A ~> B) (a : A) → App f a == App g a → f == g

  module arr-Rec {A B X : Set}
    (base* : B → X)
    (step* : (A → X) → X)
    (App* : X → A → B)
    -- Here we need to make the assumption that App* is an algebra homomorphism
    -- from X into the function space.
    (App*-β₂ : (a : A) (f : A → X) → App* (step* f) a == App* (f a) a)
    (path* : (x y : X) (a : A) → App* x a == App* y a → x == y)
    where
    rec : A ~> B → X
    rec = rec-aux phantom where
      rec-aux : Phantom path* → A ~> B → X
      rec-aux ph (#base b) = base* b
      rec-aux ph (#step f) = step* (λ a → rec-aux ph (f a))

    lem : (f g : A ~> B) (a : A) →
      App f a == App g a → App* (rec f) a == App* (rec g) a
    lem (#base b) (#base .b) a idp = idp
    lem (#base b) (#step g) a p =
      let β-red* = App*-β₂ a (rec ∘ g)
          IH = lem (#base b) (g a) a p
      in  IH ∙ ! β-red*
    lem (#step f) (#base b) a p =
      let β-red* = App*-β₂ a (rec ∘ f)
          IH = lem (f a) (#base b) a p
      in  β-red* ∙ IH
    lem (#step f) (#step g) a p =
      let IH = lem (f a) (g a) a p
          β-red-f = App*-β₂ a (rec ∘ f)
          β-red-g = App*-β₂ a (rec ∘ g)
      in β-red-f ∙ IH ∙ ! β-red-g

    postulate
      path-β : (f g : A ~> B) (a : A) (p : App f a == App g a) →
               ap rec (path f g a p)
               == path* (rec f) (rec g) a (lem f g a p)


open import Level using () renaming (zero to ℓ₀)
open import Relation.Binary using (DecSetoid)

module CheckInsert (A : DecSetoid ℓ₀ ℓ₀) where

open import Data.Nat using (ℕ)
open import Data.Fin using (Fin)
open import Data.Fin.Properties using (_≟_)
open import Data.Maybe using (Maybe ; nothing ; just) renaming (setoid to MaybeSetoid ; Eq to MaybeEq)
open import Data.Vec using (Vec) renaming (_∷_ to _∷V_)
open import Data.Vec.Equality using () renaming (module Equality to VecEq)
open import Relation.Nullary using (Dec ; yes ; no ; ¬_)
open import Relation.Nullary.Negation using (contradiction)
open import Relation.Binary using (Setoid ; module DecSetoid)
open import Relation.Binary.Core using (refl ; _≡_ ; _≢_)
import Relation.Binary.EqReasoning as EqR
open import Relation.Binary.PropositionalEquality using (cong ; sym ; inspect ; [_] ; trans)

open import FinMap

private
  open module A = DecSetoid A using (Carrier ; _≈_) renaming (_≟_ to deq)

checkInsert : {n : ℕ} → Fin n → Carrier → FinMapMaybe n Carrier → Maybe (FinMapMaybe n Carrier)
checkInsert i b m with lookupM i m
...               | nothing = just (insert i b m)
...               | just c with deq b c
...                         | yes b≈c = just m
...                         | no b≉c  = nothing

data InsertionResult {n : ℕ} (i : Fin n) (x : Carrier) (h : FinMapMaybe n Carrier) : Maybe (FinMapMaybe n Carrier) → Set where
  same : (x' : Carrier) → x ≈ x' → lookupM i h ≡ just x' → InsertionResult i x h (just h)
  new : lookupM i h ≡ nothing → InsertionResult i x h (just (insert i x h))
  wrong : (x' : Carrier) → ¬ (x ≈ x') → lookupM i h ≡ just x' → InsertionResult i x h nothing

insertionresult : {n : ℕ} → (i : Fin n) → (x : Carrier) → (h : FinMapMaybe n Carrier) → InsertionResult i x h (checkInsert i x h)
insertionresult i x h with lookupM i h | inspect (lookupM i) h
insertionresult i x h | just x' | _ with deq x x'
insertionresult i x h | just x' | [ il ] | yes x≈x' = same x' x≈x' il
insertionresult i x h | just x' | [ il ] | no x≉x' = wrong x' x≉x' il
insertionresult i x h | nothing | [ il ] = new il

lemma-checkInsert-same : {n : ℕ} → (i : Fin n) → (x : Carrier) → (m : FinMapMaybe n Carrier) → lookupM i m ≡ just x → checkInsert i x m ≡ just m
lemma-checkInsert-same i x m p with lookupM i m
lemma-checkInsert-same i x m refl | .(just x) with deq x x
lemma-checkInsert-same i x m refl | .(just x) | yes x≈x = refl
lemma-checkInsert-same i x m refl | .(just x) | no x≉x = contradiction A.refl x≉x

lemma-checkInsert-new : {n : ℕ} → (i : Fin n) → (x : Carrier) → (m : FinMapMaybe n Carrier) → lookupM i m ≡ nothing → checkInsert i x m ≡ just (insert i x m)
lemma-checkInsert-new i x m p with lookupM i m
lemma-checkInsert-new i x m refl | .nothing = refl

lemma-checkInsert-wrong : {n : ℕ} → (i : Fin n) → (x : Carrier) → (m : FinMapMaybe n Carrier) → (x' : Carrier) → ¬ (x ≈ x') → lookupM i m ≡ just x' → checkInsert i x m ≡ nothing
lemma-checkInsert-wrong i x m x' d p with lookupM i m
lemma-checkInsert-wrong i x m x' d refl | .(just x') with deq x x'
lemma-checkInsert-wrong i x m x' d refl | .(just x') | yes q = contradiction q d
lemma-checkInsert-wrong i x m x' d refl | .(just x') | no ¬q = refl

lemma-checkInsert-restrict : {n m : ℕ} → (f : Fin n → Carrier) → (i : Fin n) → (is : Vec (Fin n) m) → checkInsert i (f i) (restrict f is) ≡ just (restrict f (i ∷V is))
lemma-checkInsert-restrict f i is with checkInsert i (f i) (restrict f is) | insertionresult i (f i) (restrict f is)
lemma-checkInsert-restrict f i is | ._ | same x fi≈x p = cong just (lemma-insert-same _ i (trans p (cong just (sym (lemma-lookupM-restrict i f is p)))))
lemma-checkInsert-restrict f i is | ._ | new _ = refl
lemma-checkInsert-restrict f i is | ._ | wrong x fi≉x p = contradiction (Setoid.reflexive A.setoid (lemma-lookupM-restrict i f is p)) fi≉x

lemma-lookupM-checkInsert : {n : ℕ} → (i j : Fin n) → (h : FinMapMaybe n Carrier) → {x : Carrier} → lookupM i h ≡ just x → (y : Carrier) → {h' : FinMapMaybe n Carrier} → checkInsert j y h ≡ just h' → lookupM i h' ≡ just x
lemma-lookupM-checkInsert i j h pl y ph' with checkInsert j y h | insertionresult j y h
lemma-lookupM-checkInsert i j h pl y refl | ._ | same _ _ _ = pl
lemma-lookupM-checkInsert i j h pl y ph'  | ._ | new _ with i ≟ j
lemma-lookupM-checkInsert i .i h pl y ph' | ._ | new pl' | yes refl = contradiction (trans (sym pl) pl') (λ ())
lemma-lookupM-checkInsert i j h {x} pl y refl | ._ | new _ | no i≢j = begin
  lookupM i (insert j y h)
    ≡⟨ lemma-lookupM-insert-other i j y h i≢j ⟩
  lookupM i h
    ≡⟨ pl ⟩
  just x ∎
  where open Relation.Binary.PropositionalEquality.≡-Reasoning

lemma-lookupM-checkInsert i j h pl y () | ._ | wrong _ _ _

lemma-lookupM-checkInsert-other : {n : ℕ} → (i j : Fin n) → i ≢ j → (x : Carrier) → (h : FinMapMaybe n Carrier) → {h' : FinMapMaybe n Carrier} → checkInsert j x h ≡ just h' → lookupM i h' ≡ lookupM i h
lemma-lookupM-checkInsert-other i j i≢j x h ph' with lookupM j h
lemma-lookupM-checkInsert-other i j i≢j x h ph' | just y with deq x y
lemma-lookupM-checkInsert-other i j i≢j x h refl | just y | yes x≈y = refl
lemma-lookupM-checkInsert-other i j i≢j x h () | just y | no x≉y
lemma-lookupM-checkInsert-other i j i≢j x h refl | nothing = lemma-lookupM-insert-other i j x h i≢j


{-

Matrix with coefficients in a commutative ring

-}
{-# OPTIONS --safe #-}
module Cubical.Algebra.Matrix.CommRingCoefficient where

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

open import Cubical.Data.Nat hiding (_+_ ; _·_)
open import Cubical.Data.FinData

open import Cubical.Data.Sigma
open import Cubical.Data.Empty as Empty

open import Cubical.Relation.Nullary

open import Cubical.Algebra.Ring
open import Cubical.Algebra.Ring.BigOps
open import Cubical.Algebra.CommRing
open import Cubical.Algebra.RingSolver.Reflection

open import Cubical.Algebra.Matrix

private
  variable
    ℓ : Level
    m n k l : ℕ

module Coefficient (𝓡 : CommRing ℓ) where

  private
    R = 𝓡 .fst
    𝑹 = CommRing→Ring 𝓡
    AbR = Ring→AbGroup 𝑹

  open CommRingStr       (𝓡 .snd) renaming ( is-set to isSetR )

  open Sum                𝑹
  open FinMatrixAbGroup

  -- Convenient renaming

  Mat : (m n : ℕ) → Type ℓ
  Mat m n = FinMatrix R m n

  isSetMat : isSet (Mat m n)
  isSetMat = isSetΠ2 (λ _ _ → isSetR)

  isContrEmpty : {n : ℕ} → isContr (Mat 0 n)
  isContrEmpty =
    isOfHLevelRespectEquiv _ (equiv→ (uninhabEquiv (λ ()) ¬Fin0) (idEquiv _)) isContr⊥→A

  isContrEmptyᵗ : {m : ℕ} → isContr (Mat m 0)
  isContrEmptyᵗ =
    isContrΠ (λ _ → isOfHLevelRespectEquiv _ (equiv→ (uninhabEquiv (λ ()) ¬Fin0) (idEquiv _)) isContr⊥→A)

  𝟙 : Mat n n
  𝟙 = oneFinMatrix 𝑹

  𝟘 : Mat m n
  𝟘 = zeroFinMatrix AbR

  _⋆_ : Mat m n → Mat n k → Mat m k
  M ⋆ N = mulFinMatrix 𝑹 M N

  infixl 8 _⋆_

  ⋆lUnit : (M : Mat m n) → 𝟙 ⋆ M ≡ M
  ⋆lUnit = mulFinMatrix1r 𝑹

  ⋆rUnit : (M : Mat m n) → M ⋆ 𝟙 ≡ M
  ⋆rUnit = mulFinMatrixr1 𝑹

  ⋆Assoc : (M : Mat m n)(N : Mat n k)(K : Mat k l) → M ⋆ (N ⋆ K) ≡ (M ⋆ N) ⋆ K
  ⋆Assoc = mulFinMatrixAssoc 𝑹

  -- Transposition

  _ᵗ : Mat m n → Mat n m
  (M ᵗ) i j = M j i

  idemᵗ : (M : Mat m n) → (M ᵗ)ᵗ ≡ M
  idemᵗ M t i j = M i j

  compᵗ : (M : Mat m n)(N : Mat n k) → (M ⋆ N)ᵗ ≡ N ᵗ ⋆ M ᵗ
  compᵗ M N t i j = ∑ (λ l → ·Comm (M j l) (N l i) t)

  𝟙ᵗ : 𝟙 ᵗ ≡ 𝟙 {n = n}
  𝟙ᵗ t zero    zero    = 1r
  𝟙ᵗ t (suc i) zero    = 0r
  𝟙ᵗ t zero    (suc j) = 0r
  𝟙ᵗ t (suc i) (suc j) = 𝟙ᵗ t i j

  -- Invertible matrices

  isInv' : Mat n n → Mat n n → Type ℓ
  isInv' {n = n} M N = (M ⋆ N ≡ 𝟙) × (N ⋆ M ≡ 𝟙)

  isPropIsInv' : (M N : Mat n n) → isProp (isInv' M N)
  isPropIsInv' M N = isProp× (isSetMat _ _) (isSetMat _ _)

  invUniq : (M N N' : Mat n n) → isInv' M N → isInv' M N' → N ≡ N'
  invUniq M N N' p q =
      sym (⋆lUnit N)
    ∙ (λ i → q .snd (~ i) ⋆ N)
    ∙ sym (⋆Assoc N' M N)
    ∙ (λ i → N' ⋆ p .fst i)
    ∙ ⋆rUnit N'

  isInv : Mat n n → Type ℓ
  isInv {n = n} M = Σ[ N ∈ Mat n n ] isInv' M N

  isPropIsInv : (M : Mat n n) → isProp (isInv M)
  isPropIsInv M p q = Σ≡Prop (λ _ → isPropIsInv' M _) (invUniq M _ _ (p .snd) (q .snd))

  isInv⋆ : {M M' : Mat n n} → isInv M → isInv M' → isInv (M ⋆ M')
  isInv⋆ (N , p) (N' , q) .fst = N' ⋆ N
  isInv⋆ {M = M} {M' = M'} (N , p) (N' , q) .snd .fst =
      sym (⋆Assoc M M' (N' ⋆ N))
    ∙ (λ i → M ⋆ ⋆Assoc M' N' N i)
    ∙ (λ i → M ⋆ (q .fst i ⋆ N))
    ∙ (λ i → M ⋆ ⋆lUnit N i)
    ∙ p .fst
  isInv⋆ {M = M} {M' = M'} (N , p) (N' , q) .snd .snd =
      sym (⋆Assoc N' N (M ⋆ M'))
    ∙ (λ i → N' ⋆ ⋆Assoc N M M' i)
    ∙ (λ i → N' ⋆ (p .snd i ⋆ M'))
    ∙ (λ i → N' ⋆ ⋆lUnit M' i)
    ∙ q .snd

  InvMat : (n : ℕ) → Type ℓ
  InvMat n = Σ[ M ∈ Mat n n ] isInv M

  isInv𝟙 : isInv {n = n} 𝟙
  isInv𝟙 .fst = 𝟙
  isInv𝟙 .snd .fst = ⋆lUnit _
  isInv𝟙 .snd .snd = ⋆lUnit _

  isInvᵗ : {M : Mat n n} → isInv M → isInv (M ᵗ)
  isInvᵗ {M = M} isInvM .fst = (isInvM .fst)ᵗ
  isInvᵗ {M = M} isInvM .snd .fst = (sym (compᵗ _ M)) ∙ (λ t → (isInvM .snd .snd t)ᵗ) ∙ 𝟙ᵗ
  isInvᵗ {M = M} isInvM .snd .snd = (sym (compᵗ M _)) ∙ (λ t → (isInvM .snd .fst t)ᵗ) ∙ 𝟙ᵗ

  -- Inversion formula for 2 × 2 matrices

  dot2 : (V W : FinVec R 2) → (∑ λ i → V i · W i) ≡ V zero · W zero + V one · W one
  dot2 V W i = V zero · W zero + (+Rid (V one · W one) i)

  mul2 :
      (M : Mat m 2)(N : Mat 2 n)
    → (i : Fin m)(j : Fin n)
    → (M ⋆ N) i j ≡ M i zero · N zero j + M i one · N one j
  mul2 M N i j = dot2 (M i) (λ k → N k j)

  open Units 𝓡

  det2×2 : Mat 2 2 → R
  det2×2 M = M zero zero · M one one - M zero one · M one zero

  module _
    (M : Mat 2 2)(p : det2×2 M ∈ Rˣ) where

    private
      Δ   = det2×2 M
      Δ⁻¹ = (_⁻¹) Δ ⦃ p ⦄

      ·rInv : Δ · Δ⁻¹ ≡ 1r
      ·rInv = ·-rinv _ ⦃ p ⦄

      M⁻¹ : Mat 2 2
      M⁻¹ zero zero =   M one  one  · Δ⁻¹
      M⁻¹ zero one  = - M zero one  · Δ⁻¹
      M⁻¹ one  zero = - M one  zero · Δ⁻¹
      M⁻¹ one  one  =   M zero zero · Δ⁻¹

    isInvMat2x2 : isInv M
    isInvMat2x2 .fst = M⁻¹
    isInvMat2x2 .snd .fst i zero zero =
      (mul2 M M⁻¹ zero zero ∙ helper _ _ _ _ _ ∙ ·rInv) i
      where helper : (x y z w d : R) → x · (w · d) + y · (- z · d) ≡  (x · w - y · z) · d
            helper = solve 𝓡
    isInvMat2x2 .snd .fst i zero one  =
      (mul2 M M⁻¹ zero one  ∙ helper _ _ _) i
      where helper : (x y d : R) → x · (- y · d) + y · (x · d) ≡ 0r
            helper = solve 𝓡
    isInvMat2x2 .snd .fst i one  zero =
      (mul2 M M⁻¹ one  zero ∙ helper _ _ _) i
      where helper : (z w d : R) → z · (w · d) + w · (- z · d) ≡ 0r
            helper = solve 𝓡
    isInvMat2x2 .snd .fst i one  one  =
      (mul2 M M⁻¹ one  one  ∙ helper _ _ _ _ _ ∙ ·rInv) i
      where helper : (x y z w d : R) → z · (- y · d) + w · (x · d) ≡  (x · w - y · z) · d
            helper = solve 𝓡
    isInvMat2x2 .snd .snd i zero zero =
      (mul2 M⁻¹ M zero zero ∙ helper _ _ _ _ _ ∙ ·rInv) i
      where helper : (x y z w d : R) → (w · d) · x + (- y · d) · z ≡  (x · w - y · z) · d
            helper = solve 𝓡
    isInvMat2x2 .snd .snd i zero one  =
      (mul2 M⁻¹ M zero one  ∙ helper _ _ _) i
      where helper : (y w d : R) → (w · d) · y + (- y · d) · w ≡ 0r
            helper = solve 𝓡
    isInvMat2x2 .snd .snd i one  zero =
      (mul2 M⁻¹ M one  zero ∙ helper _ _ _) i
      where helper : (x z d : R) → (- z · d) · x + (x · d) · z ≡ 0r
            helper = solve 𝓡
    isInvMat2x2 .snd .snd i one  one  =
      (mul2 M⁻¹ M one  one  ∙ helper _ _ _ _ _ ∙ ·rInv) i
      where helper : (x y z w d : R) → (- z · d) · y + (x · d) · w ≡  (x · w - y · z) · d
            helper = solve 𝓡

  -- Similarity of matrices

  record SimRel (M N : Mat m n) : Type ℓ where
    field
      transMatL : Mat m m
      transMatR : Mat n n
      transEq : N ≡ transMatL ⋆ M ⋆ transMatR

      isInvTransL : isInv transMatL
      isInvTransR : isInv transMatR

  open SimRel

  record Sim (M : Mat m n) : Type ℓ where
    field
      result : Mat m n
      simrel : SimRel M result

  open Sim

  idSimRel : (M : Mat m n) → SimRel M M
  idSimRel _ .transMatL = 𝟙
  idSimRel _ .transMatR = 𝟙
  idSimRel M .transEq = sym ((λ t → ⋆lUnit M t ⋆ 𝟙) ∙ ⋆rUnit _)
  idSimRel _ .isInvTransL = isInv𝟙
  idSimRel _ .isInvTransR = isInv𝟙

  idSim : (M : Mat m n) → Sim M
  idSim M .result = M
  idSim M .simrel = idSimRel M

  ≡SimRel : {M N : Mat m n} → M ≡ N → SimRel M N
  ≡SimRel p .transMatL = 𝟙
  ≡SimRel p .transMatR = 𝟙
  ≡SimRel {M = M} p .transEq = sym p ∙ sym ((λ t → ⋆lUnit M t ⋆ 𝟙) ∙ ⋆rUnit _)
  ≡SimRel p .isInvTransL = isInv𝟙
  ≡SimRel p .isInvTransR = isInv𝟙

  ≡Sim : {M N : Mat m n} → M ≡ N → Sim M
  ≡Sim _ .result = _
  ≡Sim p .simrel = ≡SimRel p

  compSimRel : {M N K : Mat m n} → SimRel M N → SimRel N K → SimRel M K
  compSimRel p q .transMatL = q .transMatL ⋆ p .transMatL
  compSimRel p q .transMatR = p .transMatR ⋆ q .transMatR
  compSimRel {M = M} p q .transEq =
      let L  = p .transMatL
          R  = p .transMatR
          L' = q .transMatL
          R' = q .transMatR in
      q .transEq
    ∙ (λ t → L' ⋆ p .transEq t ⋆ R')
    ∙ (λ t → L' ⋆ ⋆Assoc L M R (~ t) ⋆ R')
    ∙ (λ t → ⋆Assoc L' (L ⋆ (M ⋆ R)) R' (~ t))
    ∙ (λ t → L' ⋆ ⋆Assoc L (M ⋆ R) R' (~ t))
    ∙ (λ t → L' ⋆ (L ⋆ ⋆Assoc M R R' (~ t)))
    ∙ (λ t → L' ⋆ ⋆Assoc L M (R ⋆ R') t)
    ∙ (λ t → ⋆Assoc L' (L ⋆ M) (R ⋆ R') t)
    ∙ (λ t → ⋆Assoc L' L M t ⋆ (R ⋆ R'))
  compSimRel p q .isInvTransL = isInv⋆ (q .isInvTransL) (p .isInvTransL)
  compSimRel p q .isInvTransR = isInv⋆ (p .isInvTransR) (q .isInvTransR)

  compSim : {M : Mat m n}(p : Sim M)(q : Sim (p .result)) → Sim M
  compSim p q .result = q .result
  compSim p q .simrel = compSimRel (p .simrel) (q .simrel)

  -- Add a new element at upper-left corner

  _⊕_ : R → Mat m n → Mat (suc m) (suc n)
  (a ⊕ M) zero    zero    = a
  (a ⊕ M) (suc i) zero    = 0r
  (a ⊕ M) zero    (suc j) = 0r
  (a ⊕ M) (suc i) (suc j) = M i j

  infixr 5 _⊕_

  sucMat : (M : Mat (suc m) (suc n)) → Mat m n
  sucMat M i j = M (suc i) (suc j)

  𝟙suc : (i j : Fin m) → 𝟙 i j ≡ sucMat 𝟙 i j
  𝟙suc zero    zero    = refl
  𝟙suc (suc i) zero    = refl
  𝟙suc zero    (suc j) = refl
  𝟙suc (suc i) (suc j) = refl

  1⊕𝟙 : 1r ⊕ 𝟙 {n = n} ≡ 𝟙
  1⊕𝟙 t zero    zero    = 1r
  1⊕𝟙 t (suc i) zero    = 0r
  1⊕𝟙 t zero    (suc j) = 0r
  1⊕𝟙 t (suc i) (suc j) = 𝟙suc i j t

  ⊕-⋆ : (a b : R)(M : Mat m n)(N : Mat n k) → (a ⊕ M) ⋆ (b ⊕ N) ≡ (a · b) ⊕ (M ⋆ N)
  ⊕-⋆ {n = n} a b M N t zero zero =
    ((λ t → a · b + ∑Mul0r {n = n} (λ i → 0r) t) ∙ helper _ _) t
    where helper : (a b : R) → a · b + 0r ≡ a · b
          helper = solve 𝓡
  ⊕-⋆ a b M N t zero (suc j) = (helper a _ ∙ ∑Mul0r (λ i → N i j)) t
    where helper : (a c : R) → a · 0r + c ≡ c
          helper = solve 𝓡
  ⊕-⋆ a b M N t (suc i) zero = (helper b _ ∙ ∑Mulr0 (λ j → M i j)) t
    where helper : (b c : R) → 0r · b + c ≡ c
          helper = solve 𝓡
  ⊕-⋆ _ _ M N t (suc i) (suc j) = helper ((M ⋆ N) i j) t
    where helper : (c : R) → 0r · 0r + c ≡ c
          helper = solve 𝓡

  isInv⊕ : (M : Mat m m) → isInv M → (isInv (1r ⊕ M))
  isInv⊕ M isInvM .fst = 1r ⊕ isInvM .fst
  isInv⊕ M isInvM .snd .fst = ⊕-⋆ _ _ _ _ ∙ (λ t → ·Lid 1r t ⊕ isInvM .snd .fst t) ∙ 1⊕𝟙
  isInv⊕ M isInvM .snd .snd = ⊕-⋆ _ _ _ _ ∙ (λ t → ·Rid 1r t ⊕ isInvM .snd .snd t) ∙ 1⊕𝟙

  ⊕SimRel : (a : R){M N : Mat m n} → (sim : SimRel M N) → SimRel (a ⊕ M) (a ⊕ N)
  ⊕SimRel _ sim .transMatL = 1r ⊕ sim .transMatL
  ⊕SimRel _ sim .transMatR = 1r ⊕ sim .transMatR
  ⊕SimRel a {M = M} sim .transEq =
    let P = sim .transMatL
        Q = sim .transMatR in
      (λ t → helper a t ⊕ sim .transEq t)
    ∙ sym (⊕-⋆ _ _ _ Q)
    ∙ (λ t → ⊕-⋆ 1r a P M (~ t) ⋆ (1r ⊕ Q))
    where helper : (a : R) → a ≡ (1r · a) · 1r
          helper = solve 𝓡
  ⊕SimRel _ sim .isInvTransL = isInv⊕ _ (sim .isInvTransL)
  ⊕SimRel _ sim .isInvTransR = isInv⊕ _ (sim .isInvTransR)

  ⊕Sim : (a : R){M : Mat m n} → (sim : Sim M) → Sim (a ⊕ M)
  ⊕Sim a sim .result = a ⊕ sim .result
  ⊕Sim _ sim .simrel = ⊕SimRel _ (sim .simrel)


{-# OPTIONS --type-in-type #-}

open import Function
open import Data.Nat.Base
open import Data.Bool.Base
open import Data.Product

{-# NO_POSITIVITY_CHECK #-}
record IFix {I : Set} (F : (I -> Set) -> I -> Set) (i : I) : Set where
  constructor wrap
  field unwrap : F (IFix F) i
open IFix

data Expr : Set -> Set where
  Lit  : ℕ -> Expr ℕ
  Inc  : Expr ℕ -> Expr ℕ
  IsZ  : Expr ℕ -> Expr Bool
  Pair : ∀ {A B} -> Expr A -> Expr B -> Expr (A × B)
  Fst  : ∀ {A B} -> Expr (A × B) -> Expr A
  Snd  : ∀ {A B} -> Expr (A × B) -> Expr B

-- As usually the pattern vector of a Scott-encoded data type encodes pattern-matching.
ExprF′ : (Set -> Set) -> Set -> Set
ExprF′
  = λ Rec I
  -> (R : Set -> Set)
  -> (ℕ -> R ℕ)
  -> (Rec ℕ -> R ℕ)
  -> (Rec ℕ -> R Bool)
  -> (∀ {A B} -> Rec A -> Rec B -> R (A × B))
  -> (∀ {A B} -> Rec (A × B) -> R A)
  -> (∀ {A B} -> Rec (A × B) -> R B)
  -> R I

Expr′ : Set -> Set
Expr′ = IFix ExprF′

expr-to-expr′ : ∀ {A} -> Expr A -> Expr′ A
expr-to-expr′ (Lit x)    = wrap λ R lit inc isZ pair fst snd -> lit x
expr-to-expr′ (Inc x)    = wrap λ R lit inc isZ pair fst snd -> inc (expr-to-expr′ x)
expr-to-expr′ (IsZ x)    = wrap λ R lit inc isZ pair fst snd -> isZ (expr-to-expr′ x)
expr-to-expr′ (Pair x y) = wrap λ R lit inc isZ pair fst snd -> pair (expr-to-expr′ x) (expr-to-expr′ y)
expr-to-expr′ (Fst p)    = wrap λ R lit inc isZ pair fst snd -> fst (expr-to-expr′ p)
expr-to-expr′ (Snd p)    = wrap λ R lit inc isZ pair fst snd -> snd (expr-to-expr′ p)

{-# TERMINATING #-}
expr′-to-expr : ∀ {A} -> Expr′ A -> Expr A
expr′-to-expr e =
  unwrap
    e
    Expr
    Lit
    (Inc ∘ expr′-to-expr)
    (IsZ ∘ expr′-to-expr)
    (λ x y -> Pair (expr′-to-expr x) (expr′-to-expr y))
    (Fst ∘ expr′-to-expr)
    (Snd ∘ expr′-to-expr)

isZero : ℕ -> Bool
isZero zero    = true
isZero (suc _) = false

{-# TERMINATING #-}
evalExpr′ : ∀ {A} -> Expr′ A -> A
evalExpr′ e =
  unwrap
    e
    id
    id
    (suc ∘ evalExpr′)
    (isZero ∘ evalExpr′)
    (λ x y -> (evalExpr′ x , evalExpr′ y))
    (proj₁ ∘ evalExpr′)
    (proj₂ ∘ evalExpr′)


-- Modified: Andreas, 2011-04-11 freezing Metas
module HereditarilySingletonRecord where

data _≡_ {A : Set} (x : A) : A → Set where
  refl : x ≡ x

-- * trivial unit type

record Singleton : Set where

foo : Singleton
foo = _

-- * product of unit types

record HereditarilySingleton : Set where
  field
    singleton      : Singleton
    also-singleton : Singleton

bar : HereditarilySingleton
bar = _

-- * hiding the unit types behind a type case

data ℕ : Set where
  zero : ℕ
  suc  : (n : ℕ) → ℕ

Unit : ℕ → Set
Unit zero    = Singleton
Unit (suc n) = Unit n

mutual -- needed to avoid freezing

  one : ℕ
  one = _

  record HereditarilySingleton₂ : Set where
    field
      singleton : Unit one

  baz : HereditarilySingleton₂
  baz = _

  force : one ≡ suc zero
  force = refl


-- In this file we consider the special of localising at a single
-- element f : R (or rather the set of powers of f). This is also
-- known as inverting f.
-- We then prove that localising first at an element f and at an element
-- g (or rather the image g/1) is the same as localising at the product f·g
-- This fact has an important application in algebraic geometry where it's
-- used to define the structure sheaf of a commutative ring.

{-# OPTIONS --safe --experimental-lossy-unification #-}
module Cubical.Algebra.CommRing.Localisation.InvertingElements where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Function
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.Univalence
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Powerset
open import Cubical.Foundations.Transport
open import Cubical.Functions.FunExtEquiv

import Cubical.Data.Empty as ⊥
open import Cubical.Data.Bool
open import Cubical.Data.Nat renaming ( _+_ to _+ℕ_ ; _·_ to _·ℕ_
                                      ; +-comm to +ℕ-comm ; +-assoc to +ℕ-assoc
                                      ; ·-assoc to ·ℕ-assoc ; ·-comm to ·ℕ-comm)
open import Cubical.Data.Nat.Order
open import Cubical.Data.Vec
open import Cubical.Data.Sigma.Base
open import Cubical.Data.Sigma.Properties
open import Cubical.Data.FinData
open import Cubical.Relation.Nullary
open import Cubical.Relation.Binary

open import Cubical.Algebra.Group
open import Cubical.Algebra.AbGroup
open import Cubical.Algebra.Monoid
open import Cubical.Algebra.Ring
open import Cubical.Algebra.CommRing
open import Cubical.Algebra.CommRing.Localisation.Base
open import Cubical.Algebra.CommRing.Localisation.UniversalProperty
open import Cubical.Algebra.RingSolver.ReflectionSolving

open import Cubical.HITs.SetQuotients as SQ
open import Cubical.HITs.PropositionalTruncation as PT

open Iso

private
  variable
    ℓ ℓ' : Level
    A : Type ℓ

module _(R' : CommRing ℓ) where
 open isMultClosedSubset
 private R = fst R'
 open CommRingStr (snd R')
 open Exponentiation R'

 [_ⁿ|n≥0] : R → ℙ R
 [ f ⁿ|n≥0] g = (∃[ n ∈ ℕ ] g ≡ f ^ n) , isPropPropTrunc
 -- Σ[ n ∈ ℕ ] (s ≡ f ^ n) × (∀ m → s ≡ f ^ m → n ≤ m) maybe better, this isProp:
 -- (n,s≡fⁿ,p) (m,s≡fᵐ,q) then n≤m by p and  m≤n by q => n≡m

 powersFormMultClosedSubset : (f : R) → isMultClosedSubset R' [ f ⁿ|n≥0]
 powersFormMultClosedSubset f .containsOne = PT.∣ zero , refl ∣
 powersFormMultClosedSubset f .multClosed =
             PT.map2 λ (m , p) (n , q) → (m +ℕ n) , (λ i → (p i) · (q i)) ∙ ·-of-^-is-^-of-+ f m n

 R[1/_] : R → Type ℓ
 R[1/ f ] = Loc.S⁻¹R R' [ f ⁿ|n≥0] (powersFormMultClosedSubset f)

 R[1/_]AsCommRing : R → CommRing ℓ
 R[1/ f ]AsCommRing = Loc.S⁻¹RAsCommRing R' [ f ⁿ|n≥0] (powersFormMultClosedSubset f)

 -- A useful lemma: (gⁿ/1)≡(g/1)ⁿ in R[1/f]
 ^-respects-/1 : {f g : R} (n : ℕ) → [ (g ^ n) , 1r , PT.∣ 0 , (λ _ → 1r) ∣ ] ≡
     Exponentiation._^_ R[1/ f ]AsCommRing [ g , 1r , powersFormMultClosedSubset _ .containsOne ] n
 ^-respects-/1 zero = refl
 ^-respects-/1 {f} {g} (suc n) = eq/ _ _ ( (1r , powersFormMultClosedSubset f .containsOne)
                                         , cong (1r · (g · (g ^ n)) ·_) (·Lid 1r))
                           ∙ cong (CommRingStr._·_ (R[1/ f ]AsCommRing .snd)
                           [ g , 1r , powersFormMultClosedSubset f .containsOne ]) (^-respects-/1 n)

 -- A slight improvement for eliminating into propositions
 InvElPropElim : {f : R} {P : R[1/ f ] → Type ℓ'}
               → (∀ x →  isProp (P x))
               → (∀ (r : R) (n : ℕ) → P [ r , (f ^ n) , PT.∣ n , refl ∣ ])    -- ∀ r n → P (r/fⁿ)
              ----------------------------------------------------------
               → (∀ x → P x)
 InvElPropElim {f = f} {P = P} PisProp base = elimProp (λ _ → PisProp _) []-case
  where
  S[f] = Loc.S R' [ f ⁿ|n≥0] (powersFormMultClosedSubset f)
  []-case : (a : R × S[f]) → P [ a ]
  []-case (r , s , s∈S[f]) = PT.rec (PisProp _) Σhelper s∈S[f]
   where
   Σhelper : Σ[ n ∈ ℕ ] s ≡ f ^ n → P [ r , s , s∈S[f] ]
   Σhelper (n , p) = subst P (cong [_] (≡-× refl (Σ≡Prop (λ _ → isPropPropTrunc) (sym p)))) (base r n)

 -- For predicates over the set of powers
 powersPropElim : {f : R} {P : R → Type ℓ'}
                → (∀ x →  isProp (P x))
                → (∀ n → P (f ^ n))
               ------------------------------
                → ∀ s → s ∈ [ f ⁿ|n≥0] → P s
 powersPropElim {f = f} {P = P} PisProp base s =
                PT.rec (PisProp s) λ (n , p) → subst P (sym p) (base n)

module DoubleLoc (R' : CommRing ℓ) (f g : (fst R')) where
 open isMultClosedSubset
 open CommRingStr (snd R')
 open CommRingTheory R'
 open Exponentiation R'
 open RingTheory (CommRing→Ring R')
 open CommRingStr (snd (R[1/_]AsCommRing R' f)) renaming ( _·_ to _·ᶠ_ ; 1r to 1ᶠ
                                                         ; _+_ to _+ᶠ_ ; 0r to 0ᶠ
                                                         ; ·Lid to ·ᶠ-lid ; ·Rid to ·ᶠ-rid
                                                         ; ·Assoc to ·ᶠ-assoc ; ·-comm to ·ᶠ-comm)
 open IsRingHom

 private
  R = fst R'
  R[1/f] = R[1/_] R' f
  R[1/f]AsCommRing = R[1/_]AsCommRing R' f
  R[1/fg] = R[1/_] R' (f · g)
  R[1/fg]AsCommRing = R[1/_]AsCommRing R' (f · g)
  R[1/f][1/g] = R[1/_] (R[1/_]AsCommRing R' f)
                                [ g , 1r , powersFormMultClosedSubset R' f .containsOne ]
  R[1/f][1/g]AsCommRing = R[1/_]AsCommRing (R[1/_]AsCommRing R' f)
                                [ g , 1r , powersFormMultClosedSubset R' f .containsOne ]
  R[1/f][1/g]ˣ = R[1/f][1/g]AsCommRing ˣ

 _/1/1 : R → R[1/f][1/g]
 r /1/1 = [ [ r , 1r , PT.∣ 0 , refl ∣ ] , 1ᶠ , PT.∣ 0 , refl ∣ ]

 /1/1AsCommRingHom : CommRingHom R' R[1/f][1/g]AsCommRing
 fst /1/1AsCommRingHom = _/1/1
 snd /1/1AsCommRingHom = makeIsRingHom refl lem+ lem·
   where
   lem+ : _
   lem+ r r' =
     cong [_]
       (≡-×
         (cong [_]
           (≡-×
             (cong₂ _+_
               (sym (·Rid _) ∙ (λ i → (·Rid r (~ i)) · (·Rid 1r (~ i))))
               (sym (·Rid _) ∙ (λ i → (·Rid r' (~ i)) · (·Rid 1r (~ i)))))
             (Σ≡Prop (λ _ → isPropPropTrunc)
               (sym (·Lid _) ∙ (λ i → (·Lid 1r (~ i)) · (·Lid 1r (~ i)))))))
         (Σ≡Prop (λ _ → isPropPropTrunc) (sym (·ᶠ-lid 1ᶠ))))

   lem· : _
   lem· r r' =
     cong [_]
       (≡-×
         (cong [_] (≡-× refl (Σ≡Prop (λ _ → isPropPropTrunc) (sym (·Lid _)))))
         (Σ≡Prop (λ _ → isPropPropTrunc) (sym (·ᶠ-lid 1ᶠ))))

 -- this will give us a map R[1/fg] → R[1/f][1/g] by the universal property of localisation
 fⁿgⁿ/1/1∈R[1/f][1/g]ˣ : (s : R) → s ∈ ([_ⁿ|n≥0] R' (f · g)) → s /1/1 ∈ R[1/f][1/g]ˣ
 fⁿgⁿ/1/1∈R[1/f][1/g]ˣ = powersPropElim R' (λ s → R[1/f][1/g]ˣ (s /1/1) .snd) ℕcase
  where
  ℕcase : (n : ℕ) → ((f · g) ^ n) /1/1 ∈ R[1/f][1/g]ˣ
  ℕcase n = [ [ 1r , (f ^ n) , PT.∣ n , refl ∣ ]
            , [ (g ^ n) , 1r , PT.∣ 0 , refl ∣ ] --denominator
            , PT.∣ n , ^-respects-/1 _ n ∣ ]
            , eq/ _ _ ((1ᶠ , powersFormMultClosedSubset _ _ .containsOne)
            , eq/ _ _ ((1r , powersFormMultClosedSubset _ _ .containsOne) , path))
   where
   eq1 : ∀ x → 1r · (1r · (x · 1r) · 1r) · (1r · 1r · (1r · 1r)) ≡ x
   eq1 = solve R'

   eq2 : ∀ x y → x · y ≡ 1r · (1r · 1r · (1r · y)) · (1r · (1r · x) · 1r)
   eq2 = solve R'

   path : 1r · (1r · ((f · g) ^ n · 1r) · 1r) · (1r · 1r · (1r · 1r))
        ≡ 1r · (1r · 1r · (1r · g ^ n)) · (1r · (1r · f ^ n) · 1r)
   path = 1r · (1r · ((f · g) ^ n · 1r) · 1r) · (1r · 1r · (1r · 1r)) ≡⟨ eq1 ((f · g) ^ n) ⟩
          (f · g) ^ n                                                 ≡⟨ ^-ldist-· _ _ _ ⟩
          f ^ n · g ^ n                                               ≡⟨ eq2 (f ^ n) (g ^ n) ⟩
          1r · (1r · 1r · (1r · g ^ n)) · (1r · (1r · f ^ n) · 1r)    ∎

 -- the main result: localising at one element and then at another is
 -- the same as localising at the product.
 -- takes forever to compute without experimental lossy unification
 R[1/fg]≡R[1/f][1/g] : R[1/fg]AsCommRing ≡ R[1/f][1/g]AsCommRing
 R[1/fg]≡R[1/f][1/g] = S⁻¹RChar R' ([_ⁿ|n≥0] R' (f · g))
                         (powersFormMultClosedSubset R' (f · g)) _ /1/1AsCommRingHom pathtoR[1/fg]
  where
  open PathToS⁻¹R
  pathtoR[1/fg] : PathToS⁻¹R R' ([_ⁿ|n≥0] R' (f · g)) (powersFormMultClosedSubset R' (f · g))
                             R[1/f][1/g]AsCommRing /1/1AsCommRingHom
  φS⊆Aˣ pathtoR[1/fg] = fⁿgⁿ/1/1∈R[1/f][1/g]ˣ

  kerφ⊆annS pathtoR[1/fg] r p = toGoal helperR[1/f]
   where
   open RingTheory (CommRing→Ring R[1/f]AsCommRing) renaming ( 0RightAnnihilates to 0ᶠRightAnnihilates
                                                         ; 0LeftAnnihilates to 0ᶠ-leftNullifies)
   open Exponentiation R[1/f]AsCommRing renaming (_^_ to _^ᶠ_)
                                        hiding (·-of-^-is-^-of-+ ; ^-ldist-·)

   S[f] = Loc.S R' ([_ⁿ|n≥0] R' f) (powersFormMultClosedSubset R' f)
   S[fg] = Loc.S R' ([_ⁿ|n≥0] R' (f · g)) (powersFormMultClosedSubset R' (f · g))
   g/1 : R[1/_] R' f
   g/1 = [ g , 1r , powersFormMultClosedSubset R' f .containsOne ]
   S[g/1] = Loc.S R[1/f]AsCommRing
                  ([_ⁿ|n≥0] R[1/f]AsCommRing g/1)
                  (powersFormMultClosedSubset R[1/f]AsCommRing g/1)
   r/1 : R[1/_] R' f
   r/1 = [ r , 1r , powersFormMultClosedSubset R' f .containsOne ]

   -- this is the crucial step, modulo truncation we can take p to be generated
   -- by the quotienting relation of localisation. Note that we wouldn't be able
   -- to prove our goal if kerφ⊆annS was formulated with a Σ instead of a ∃
   ∥r/1,1/1≈0/1,1/1∥ : ∃[ u ∈ S[g/1] ] fst u ·ᶠ r/1 ·ᶠ 1ᶠ ≡ fst u ·ᶠ 0ᶠ ·ᶠ 1ᶠ
   ∥r/1,1/1≈0/1,1/1∥ = Iso.fun (SQ.isEquivRel→TruncIso (Loc.locIsEquivRel _ _ _) _ _) p

   helperR[1/f] : ∃[ n ∈ ℕ ] [ g ^ n · r , 1r , PT.∣ 0 , refl ∣ ] ≡ 0ᶠ
   helperR[1/f] = PT.rec isPropPropTrunc
                  (uncurry (uncurry (powersPropElim R[1/f]AsCommRing
                                    (λ _ → isPropΠ (λ _ → isPropPropTrunc)) baseCase)))
                  ∥r/1,1/1≈0/1,1/1∥
    where
    baseCase : ∀ n → g/1 ^ᶠ n ·ᶠ r/1 ·ᶠ 1ᶠ ≡ g/1 ^ᶠ n ·ᶠ 0ᶠ ·ᶠ 1ᶠ
                   → ∃[ n ∈ ℕ ] [ g ^ n · r , 1r , PT.∣ 0 , refl ∣ ] ≡ 0ᶠ
    baseCase n q = PT.∣ n , path ∣
     where
     path : [ g ^ n · r , 1r , PT.∣ 0 , refl ∣ ] ≡ 0ᶠ
     path = [ g ^ n · r , 1r , PT.∣ 0 , refl ∣ ]

          ≡⟨ cong [_] (≡-× refl (Σ≡Prop (λ _ → isPropPropTrunc) (sym (·Rid _)))) ⟩

            [ g ^ n , 1r , PT.∣ 0 , refl ∣ ] ·ᶠ r/1

          ≡⟨ cong (_·ᶠ r/1) (^-respects-/1 _ n) ⟩

            g/1 ^ᶠ n ·ᶠ r/1

          ≡⟨ sym (·ᶠ-rid _) ⟩

            g/1 ^ᶠ n ·ᶠ r/1 ·ᶠ 1ᶠ

          ≡⟨ q ⟩

            g/1 ^ᶠ n ·ᶠ 0ᶠ ·ᶠ 1ᶠ

          ≡⟨ cong (_·ᶠ 1ᶠ) (0ᶠRightAnnihilates _) ∙ 0ᶠ-leftNullifies 1ᶠ ⟩

            0ᶠ ∎

   toGoal : ∃[ n ∈ ℕ ] [ g ^ n · r , 1r , PT.∣ 0 , refl ∣ ] ≡ 0ᶠ
          → ∃[ u ∈ S[fg] ] fst u · r ≡ 0r
   toGoal = PT.rec isPropPropTrunc Σhelper
    where
    Σhelper : Σ[ n ∈ ℕ ] [ g ^ n · r , 1r , PT.∣ 0 , refl ∣ ] ≡ 0ᶠ
            → ∃[ u ∈ S[fg] ] fst u · r ≡ 0r
    Σhelper (n , q) = PT.map Σhelper2 helperR
     where
     -- now, repeat the above strategy with q
     ∥gⁿr≈0∥ : ∃[ u ∈ S[f] ] fst u · (g ^ n · r) · 1r ≡ fst u · 0r · 1r
     ∥gⁿr≈0∥ = Iso.fun (SQ.isEquivRel→TruncIso (Loc.locIsEquivRel _ _ _) _ _) q

     helperR : ∃[ m ∈ ℕ ] f ^ m · g ^ n · r ≡ 0r
     helperR = PT.rec isPropPropTrunc
               (uncurry (uncurry (powersPropElim R'
                                 (λ _ → isPropΠ (λ _ → isPropPropTrunc)) baseCase)))
               ∥gⁿr≈0∥
      where
      baseCase : (m : ℕ) → f ^ m · (g ^ n · r) · 1r ≡ f ^ m · 0r · 1r
               → ∃[ m ∈ ℕ ] f ^ m · g ^ n · r ≡ 0r
      baseCase m q' = PT.∣ m , path ∣
       where
       path : f ^ m · g ^ n · r ≡ 0r
       path = (λ i → ·Rid (·Assoc (f ^ m) (g ^ n) r (~ i)) (~ i))
            ∙∙ q' ∙∙ (λ i → ·Rid (0RightAnnihilates (f ^ m) i) i)

     Σhelper2 : Σ[ m ∈ ℕ ] f ^ m · g ^ n · r ≡ 0r
              → Σ[ u ∈ S[fg] ] fst u · r ≡ 0r
     Σhelper2 (m , q') = (((f · g) ^ l) , PT.∣ l , refl ∣) , path
      where
      l = max m n

      path : (f · g) ^ l · r ≡ 0r
      path = (f · g) ^ l · r

           ≡⟨ cong (_· r) (^-ldist-· _ _ _) ⟩

             f ^ l · g ^ l · r

           ≡⟨ cong₂ (λ x y → f ^ x · g ^ y · r) (sym (≤-∸-+-cancel {m = m} left-≤-max))
                                                (sym (≤-∸-+-cancel {m = n} right-≤-max)) ⟩

             f ^ (l ∸ m +ℕ m) · g ^ (l ∸ n +ℕ n) · r

           ≡⟨ cong₂ (λ x y → x · y · r) (sym (·-of-^-is-^-of-+ _ _ _))
                                        (sym (·-of-^-is-^-of-+ _ _ _)) ⟩

             f ^ (l ∸ m) · f ^ m · (g ^ (l ∸ n) · g ^ n) · r

           ≡⟨ cong (_· r) (·-commAssocSwap _ _ _ _) ⟩

             f ^ (l ∸ m) · g ^ (l ∸ n) · (f ^ m · g ^ n) · r

           ≡⟨ sym (·Assoc _ _ _) ⟩

             f ^ (l ∸ m) · g ^ (l ∸ n) · (f ^ m · g ^ n · r)

           ≡⟨ cong (f ^ (l ∸ m) · g ^ (l ∸ n) ·_) q' ⟩

             f ^ (l ∸ m) · g ^ (l ∸ n) · 0r

           ≡⟨ 0RightAnnihilates _ ⟩

             0r ∎

  surχ pathtoR[1/fg] = InvElPropElim _ (λ _ → isPropPropTrunc) toGoal
   where
   open Exponentiation R[1/f]AsCommRing renaming (_^_ to _^ᶠ_)
                                               hiding (·-of-^-is-^-of-+ ; ^-ldist-·)
   open CommRingStr (snd R[1/f][1/g]AsCommRing) renaming (_·_ to _·R[1/f][1/g]_)
                    hiding (1r ; ·Lid ; ·Rid ; ·Assoc)
   open Units R[1/f][1/g]AsCommRing
   g/1 : R[1/_] R' f
   g/1 = [ g , 1r , powersFormMultClosedSubset R' f .containsOne ]
   S[fg] = Loc.S R' ([_ⁿ|n≥0] R' (f · g)) (powersFormMultClosedSubset R' (f · g))

   baseCase : (r : R) (m n : ℕ) → ∃[ x ∈ R × S[fg] ] (x .fst /1/1)
       ≡ [ [ r , f ^ m , PT.∣ m , refl ∣ ]
         , [ g ^ n , 1r , PT.∣ 0 , refl ∣ ] , PT.∣ n , ^-respects-/1 _ n ∣ ]
       ·R[1/f][1/g] (x .snd .fst /1/1)
   baseCase r m n = PT.∣ ((r · f ^ (l ∸ m) · g ^ (l ∸ n)) -- x .fst
               , (f · g) ^ l , PT.∣ l , refl ∣)      -- x .snd
               , eq/ _ _ ((1ᶠ , PT.∣ 0 , refl ∣) , eq/ _ _ ((1r , PT.∣ 0 , refl ∣) , path)) ∣
               -- reduce equality of double fractions into equality in R
    where
    eq1 : ∀ r flm gln gn fm
        → 1r · (1r · (r · flm · gln) · (gn · 1r)) · (1r · (fm · 1r) · 1r)
        ≡ r · flm · (gln · gn) · fm
    eq1 = solve R'

    eq2 : ∀ r flm gl fm → r · flm · gl · fm ≡  r · (flm · fm ) · gl
    eq2 = solve R'

    eq3 : ∀ r fgl → r · fgl ≡ 1r · (1r · (r · fgl) · 1r) · (1r · 1r · (1r · 1r))
    eq3 = solve R'

    l = max m n

    path : 1r · (1r · (r · f ^ (l ∸ m) · g ^ (l ∸ n)) · (g ^ n · 1r)) · (1r · (f ^ m · 1r) · 1r)
         ≡ 1r · (1r · (r · (f · g) ^ l) · 1r) · (1r · 1r · (1r · 1r))
    path = 1r · (1r · (r · f ^ (l ∸ m) · g ^ (l ∸ n)) · (g ^ n · 1r)) · (1r · (f ^ m · 1r) · 1r)

         ≡⟨ eq1 r  (f ^ (l ∸ m)) (g ^ (l ∸ n)) (g ^ n) (f ^ m) ⟩

           r · f ^ (l ∸ m) · (g ^ (l ∸ n) · g ^ n) · f ^ m

         ≡⟨ cong (λ x → r · f ^ (l ∸ m) · x · f ^ m) (·-of-^-is-^-of-+ _ _ _) ⟩

           r · f ^ (l ∸ m) · g ^ (l ∸ n +ℕ n) · f ^ m

         ≡⟨ cong (λ x → r · f ^ (l ∸ m) · g ^ x · f ^ m) (≤-∸-+-cancel right-≤-max) ⟩

           r · f ^ (l ∸ m) · g ^ l · f ^ m

         ≡⟨ eq2 r (f ^ (l ∸ m)) (g ^ l) (f ^ m) ⟩

           r · (f ^ (l ∸ m) · f ^ m) · g ^ l

         ≡⟨ cong (λ x → r · x · g ^ l) (·-of-^-is-^-of-+ _ _ _) ⟩

           r · f ^ (l ∸ m +ℕ m) · g ^ l

         ≡⟨ cong (λ x → r · f ^ x · g ^ l) (≤-∸-+-cancel left-≤-max) ⟩

           r · f ^ l · g ^ l

         ≡⟨ sym (·Assoc _ _ _) ⟩

           r · (f ^ l · g ^ l)

         ≡⟨ cong (r ·_) (sym (^-ldist-· _ _ _)) ⟩

           r · (f · g) ^ l

         ≡⟨ eq3 r ((f · g) ^ l) ⟩

           1r · (1r · (r · (f · g) ^ l) · 1r) · (1r · 1r · (1r · 1r)) ∎

   base-^ᶠ-helper : (r : R) (m n : ℕ) → ∃[ x ∈ R × S[fg] ] (x .fst /1/1)
       ≡ [ [ r , f ^ m , PT.∣ m , refl ∣ ]
         , g/1 ^ᶠ n , PT.∣ n , refl ∣ ] ·R[1/f][1/g] (x .snd .fst /1/1)
   base-^ᶠ-helper r m n = subst (λ y →  ∃[ x ∈ R × S[fg] ] (x .fst /1/1)
                          ≡ [ [ r , f ^ m , PT.∣ m , refl ∣ ] , y ] ·R[1/f][1/g] (x .snd .fst /1/1))
                     (Σ≡Prop (λ _ → isPropPropTrunc) (^-respects-/1 _ n)) (baseCase r m n)

   indStep : (r : R[1/_] R' f) (n : ℕ) → ∃[ x ∈ R × S[fg] ]
         (x .fst /1/1) ≡ [ r , g/1 ^ᶠ n , PT.∣ n , refl ∣ ] ·R[1/f][1/g] (x .snd .fst /1/1)
   indStep = InvElPropElim _ (λ _ → isPropΠ λ _ → isPropPropTrunc) base-^ᶠ-helper

   toGoal : (r : R[1/_] R' f) (n : ℕ) → ∃[ x ∈ R × S[fg] ]
            (x .fst /1/1) ·R[1/f][1/g]
            ((x .snd .fst /1/1) ⁻¹) ⦃ φS⊆Aˣ pathtoR[1/fg] (x .snd .fst) (x .snd .snd) ⦄
          ≡ [ r , g/1 ^ᶠ n , PT.∣ n , refl ∣ ]
   toGoal r n = PT.map Σhelper (indStep r n)
    where
    Σhelper : Σ[ x ∈ R × S[fg] ]
               (x .fst /1/1) ≡ [ r , g/1 ^ᶠ n , PT.∣ n , refl ∣ ] ·R[1/f][1/g] (x .snd .fst /1/1)
            → Σ[ x ∈ R × S[fg] ]
               (x .fst /1/1) ·R[1/f][1/g] ((x .snd .fst /1/1) ⁻¹)
               ⦃ φS⊆Aˣ pathtoR[1/fg] (x .snd .fst) (x .snd .snd) ⦄
               ≡ [ r , g/1 ^ᶠ n , PT.∣ n , refl ∣ ]
    Σhelper ((r' , s , s∈S[fg]) , p) = (r' , s , s∈S[fg])
                                     , ⁻¹-eq-elim ⦃ φS⊆Aˣ pathtoR[1/fg] s s∈S[fg] ⦄ p

 -- In this module we construct the map R[1/fg]→R[1/f][1/g] directly
 -- and show that it is equal (although not judgementally) to the map induced
 -- by the universal property of localisation, i.e. transporting along the path
 -- constructed above. Given that this is the easier direction, we can see that
 -- it is pretty cumbersome to prove R[1/fg]≡R[1/f][1/g] directly,
 -- which illustrates the usefulness of S⁻¹RChar quite well.
 private
  module check where
   φ : R[1/fg] → R[1/f][1/g]
   φ = SQ.rec squash/ ϕ ϕcoh
     where
     S[fg] = Loc.S R' ([_ⁿ|n≥0] R' (f · g)) (powersFormMultClosedSubset R' (f · g))

     curriedϕΣ : (r s : R) → Σ[ n ∈ ℕ ] s ≡ (f · g) ^ n → R[1/f][1/g]
     curriedϕΣ r s (n , s≡fg^n) = [ [ r , (f ^ n) , PT.∣ n , refl ∣ ]
                                  , [ (g ^ n) , 1r , PT.∣ 0 , refl ∣ ] --denominator
                                  , PT.∣ n , ^-respects-/1 R' n ∣ ]

     curriedϕ : (r s : R) → ∃[ n ∈ ℕ ] s ≡ (f · g) ^ n → R[1/f][1/g]
     curriedϕ r s = elim→Set (λ _ → squash/) (curriedϕΣ r s) coh
      where
      coh : (x y : Σ[ n ∈ ℕ ] s ≡ (f · g) ^ n) → curriedϕΣ r s x ≡ curriedϕΣ r s y
      coh (n , s≡fg^n) (m , s≡fg^m) = eq/ _ _ ((1ᶠ , PT.∣ 0 , refl ∣) ,
                                      eq/ _ _ ( (1r , powersFormMultClosedSubset R' f .containsOne)
                                              , path))
       where
       eq1 : ∀ r gm  fm → 1r · (1r · r · gm) · (1r · fm · 1r) ≡ r · (gm · fm)
       eq1 = solve R'

       path : 1r · (1r · r · (g ^ m)) · (1r · (f ^ m) · 1r)
            ≡ 1r · (1r · r · (g ^ n)) · (1r · (f ^ n) · 1r)
       path = 1r · (1r · r · (g ^ m)) · (1r · (f ^ m) · 1r)

            ≡⟨ eq1 r (g ^ m) (f ^ m) ⟩

              r · (g ^ m · f ^ m)

            ≡⟨ cong (r ·_) (sym (^-ldist-· g f m)) ⟩

              r · ((g · f) ^ m)

            ≡⟨ cong (λ x → r · (x ^ m)) (·-comm _ _) ⟩

              r · ((f · g) ^ m)

            ≡⟨ cong (r ·_) ((sym s≡fg^m) ∙ s≡fg^n) ⟩

              r · ((f · g) ^ n)

            ≡⟨ cong (λ x → r · (x ^ n)) (·-comm _ _) ⟩

              r · ((g · f) ^ n)

            ≡⟨ cong (r ·_) (^-ldist-· g f n) ⟩

              r · (g ^ n · f ^ n)

            ≡⟨ sym (eq1 r (g ^ n) (f ^ n)) ⟩

              1r · (1r · r · (g ^ n)) · (1r · (f ^ n) · 1r) ∎

     ϕ : R × S[fg] → R[1/f][1/g]
     ϕ = uncurry2 curriedϕ -- λ (r / (fg)ⁿ) → ((r / fⁿ) / gⁿ)

     curriedϕcohΣ : (r s r' s' u : R) → (p : u · r · s' ≡ u · r' · s)
                                      → (α : Σ[ n ∈ ℕ ] s ≡ (f · g) ^ n)
                                      → (β : Σ[ m ∈ ℕ ] s' ≡ (f · g) ^ m)
                                      → (γ : Σ[ l ∈ ℕ ] u ≡ (f · g) ^ l)
                                      → ϕ (r , s , PT.∣ α ∣) ≡ ϕ (r' , s' , PT.∣ β ∣)
     curriedϕcohΣ r s r' s' u p (n , s≡fgⁿ) (m , s'≡fgᵐ) (l , u≡fgˡ) =
      eq/ _ _ ( ( [ (g ^ l) , 1r , powersFormMultClosedSubset R' f .containsOne ]
                , PT.∣ l , ^-respects-/1 R' l ∣)
              , eq/ _ _ ((f ^ l , PT.∣ l , refl ∣) , path))
      where
      eq1 : ∀ fl gl r gm fm
          → fl · (gl · r · gm) · (1r · fm · 1r) ≡ fl · gl · r · (gm · fm)
      eq1 = solve R'

      path : f ^ l · (g ^ l · transp (λ i → R) i0 r · transp (λ i → R) i0 (g ^ m))
                   · (1r · transp (λ i → R) i0 (f ^ m) · transp (λ i → R) i0 1r)
           ≡ f ^ l · (g ^ l · transp (λ i → R) i0 r' · transp (λ i → R) i0 (g ^ n))
                   · (1r · transp (λ i → R) i0 (f ^ n) · transp (λ i → R) i0 1r)
      path = f ^ l · (g ^ l · transp (λ i → R) i0 r · transp (λ i → R) i0 (g ^ m))
                   · (1r · transp (λ i → R) i0 (f ^ m) · transp (λ i → R) i0 1r)

           ≡⟨ (λ i → f ^ l · (g ^ l · transportRefl r i · transportRefl (g ^ m) i)
                           · (1r · transportRefl (f ^ m) i · transportRefl 1r i)) ⟩

             f ^ l · (g ^ l · r · g ^ m) · (1r · f ^ m · 1r)

           ≡⟨ eq1 (f ^ l) (g ^ l) r (g ^ m) (f ^ m) ⟩

             f ^ l · g ^ l · r · (g ^ m · f ^ m)

           ≡⟨ (λ i → ^-ldist-· f g l (~ i) · r · ^-ldist-· g f m (~ i)) ⟩

             (f · g) ^ l · r · (g · f) ^ m

           ≡⟨ cong (λ x → (f · g) ^ l · r · x ^ m) (·-comm _ _) ⟩

             (f · g) ^ l · r · (f · g) ^ m

           ≡⟨ (λ i → u≡fgˡ (~ i) · r · s'≡fgᵐ (~ i)) ⟩

             u · r · s'

           ≡⟨ p ⟩

             u · r' · s

           ≡⟨ (λ i → u≡fgˡ i · r' · s≡fgⁿ i) ⟩

             (f · g) ^ l · r' · (f · g) ^ n

           ≡⟨ cong (λ x → (f · g) ^ l · r' · x ^ n) (·-comm _ _) ⟩

             (f · g) ^ l · r' · (g · f) ^ n

           ≡⟨ (λ i → ^-ldist-· f g l i · r' · ^-ldist-· g f n i) ⟩

             f ^ l · g ^ l · r' · (g ^ n · f ^ n)

           ≡⟨ sym (eq1 (f ^ l) (g ^ l) r' (g ^ n) (f ^ n)) ⟩

             f ^ l · (g ^ l · r' · g ^ n) · (1r · f ^ n · 1r)

           ≡⟨ (λ i → f ^ l · (g ^ l · transportRefl r' (~ i) · transportRefl (g ^ n) (~ i))
                           · (1r · transportRefl (f ^ n) (~ i) · transportRefl 1r (~ i))) ⟩

             f ^ l · (g ^ l · transp (λ i → R) i0 r' · transp (λ i → R) i0 (g ^ n))
                   · (1r · transp (λ i → R) i0 (f ^ n) · transp (λ i → R) i0 1r) ∎

     curriedϕcoh : (r s r' s' u : R) → (p : u · r · s' ≡ u · r' · s)
                                     → (α : ∃[ n ∈ ℕ ] s ≡ (f · g) ^ n)
                                     → (β : ∃[ m ∈ ℕ ] s' ≡ (f · g) ^ m)
                                     → (γ : ∃[ l ∈ ℕ ] u ≡ (f · g) ^ l)
                                     → ϕ (r , s , α) ≡ ϕ (r' , s' , β)
     curriedϕcoh r s r' s' u p = PT.elim (λ _ → isPropΠ2 (λ _ _ → squash/ _ _))
                           λ α → PT.elim (λ _ → isPropΠ (λ _ → squash/ _ _))
                           λ β → PT.rec (squash/ _ _)
                           λ γ →  curriedϕcohΣ r s r' s' u p α β γ

     ϕcoh : (a b : R × S[fg])
          → Loc._≈_ R' ([_ⁿ|n≥0] R' (f · g)) (powersFormMultClosedSubset R' (f · g)) a b
          → ϕ a ≡ ϕ b
     ϕcoh (r , s , α) (r' , s' , β) ((u , γ) , p) =  curriedϕcoh r s r' s' u p α β γ

   -- the map induced by the universal property
   open S⁻¹RUniversalProp R' ([_ⁿ|n≥0] R' (f · g)) (powersFormMultClosedSubset R' (f · g))
   χ : R[1/fg] → R[1/f][1/g]
   χ = S⁻¹RHasUniversalProp R[1/f][1/g]AsCommRing /1/1AsCommRingHom fⁿgⁿ/1/1∈R[1/f][1/g]ˣ .fst .fst .fst

   -- the sanity check:
   -- both maps send a fraction r/(fg)ⁿ to a double fraction,
   -- where numerator and denominator are almost the same fraction respectively.
   -- unfortunately the proofs that the denominators are powers are quite different for
   -- the two maps, but of course we can ignore them.
   -- The definition of χ introduces a lot of (1r ·_). Perhaps most surprisingly,
   -- we have to give the path eq1 for the equality of the numerator of the numerator.
   φ≡χ : ∀ r → φ r ≡ χ r
   φ≡χ = InvElPropElim _ (λ _ → squash/ _ _) ℕcase
    where
    ℕcase : (r : R) (n : ℕ)
          → φ [ r , (f · g) ^ n , PT.∣ n , refl ∣ ] ≡ χ [ r , (f · g) ^ n , PT.∣ n , refl ∣ ]
    ℕcase r n = cong [_] (ΣPathP --look into the components of the double-fractions
              ( cong [_] (ΣPathP (eq1 , Σ≡Prop (λ x → S'[f] x .snd) (sym (·Lid _))))
              , Σ≡Prop (λ x → S'[f][g] x .snd) --ignore proof that denominator is power of g/1
              ( cong [_] (ΣPathP (sym (·Lid _) , Σ≡Prop (λ x → S'[f] x .snd) (sym (·Lid _)))))))
     where
     S'[f] = ([_ⁿ|n≥0] R' f)
     S'[f][g] = ([_ⁿ|n≥0] R[1/f]AsCommRing [ g , 1r , powersFormMultClosedSubset R' f .containsOne ])

     eq1 : transp (λ i → fst R') i0 r ≡ r · transp (λ i → fst R') i0 1r
     eq1 = transportRefl r ∙∙ sym (·Rid r) ∙∙ cong (r ·_) (sym (transportRefl 1r))


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

module Pi0Semiring where

import Level

open import PiU
open import PiLevel0

open import Algebra using (CommutativeSemiring)
open import Algebra.Structures
  using (IsSemigroup; IsCommutativeMonoid; IsCommutativeSemiring)

------------------------------------------------------------------------------
-- Commutative semiring structure of U

typesPlusIsSG : IsSemigroup _⟷_ PLUS
typesPlusIsSG = record {
  isEquivalence = record { refl = id⟷ ; sym = ! ; trans = _◎_ } ;
  assoc = λ t₁ t₂ t₃ → assocr₊ {t₁} {t₂} {t₃} ;
  ∙-cong = _⊕_
  }

typesTimesIsSG : IsSemigroup _⟷_ TIMES
typesTimesIsSG = record {
  isEquivalence = record { refl = id⟷ ; sym = ! ; trans = _◎_ } ;
  assoc = λ t₁ t₂ t₃ → assocr⋆ {t₁} {t₂} {t₃} ;
  ∙-cong = _⊗_
  }

typesPlusIsCM : IsCommutativeMonoid _⟷_ PLUS ZERO
typesPlusIsCM = record {
  isSemigroup = typesPlusIsSG ;
  identityˡ = λ t → unite₊l {t} ;
  comm = λ t₁ t₂ → swap₊ {t₁} {t₂}
  }

typesTimesIsCM : IsCommutativeMonoid _⟷_ TIMES ONE
typesTimesIsCM = record {
  isSemigroup = typesTimesIsSG ;
  identityˡ = λ t → unite⋆l {t} ;
  comm = λ t₁ t₂ → swap⋆ {t₁} {t₂}
  }

typesIsCSR : IsCommutativeSemiring _⟷_ PLUS TIMES ZERO ONE
typesIsCSR = record {
  +-isCommutativeMonoid = typesPlusIsCM ;
  *-isCommutativeMonoid = typesTimesIsCM ;
  distribʳ = λ t₁ t₂ t₃ → dist {t₂} {t₃} {t₁} ;
  zeroˡ = λ t → absorbr {t}
  }

typesCSR : CommutativeSemiring Level.zero Level.zero
typesCSR = record {
  Carrier = U ;
  _≈_ = _⟷_ ;
  _+_ = PLUS ;
  _*_ = TIMES ;
  0# = ZERO ;
  1# = ONE ;
  isCommutativeSemiring = typesIsCSR
  }

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


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

module Homogenous.Equality where
import Homogenous.Base
import EqBase
import PolyDepPrelude

open PolyDepPrelude using ( Datoid
                   ; Bool; true; false; _&&_
                   ; Pair; pair
                   ; Either; left; right
                   ; suc; zero
                   ; _::_; nil
                   ; cmp; Unit; unit
                   )
open Homogenous.Base using (Arity; Sig; Fa; F; T; It; out)

eq_step_ar : (n : Arity){X : Set}(fs : Fa n (X -> Bool))(xs : Fa n X) -> Bool
eq_step_ar zero    unit         unit         = true
eq_step_ar (suc m) (pair f fs') (pair x xs') = f x && eq_step_ar m fs' xs'

-- We write left as This (as in "This constructor" and right as Other
-- (as in "one of the Other constructors") to help the reader

eq_step' : (fi : Sig){X : Set} -> F fi (X -> Bool) -> F fi X -> Bool
eq_step' (nil)   ()         ()  -- empty
eq_step' (n :: ns) (left fs ) (left xs ) = eq_step_ar n fs xs
eq_step' (n :: ns) (left fs ) (right y') = false
eq_step' (n :: ns) (right x') (left xs ) = false
eq_step' (n :: ns) (right x') (right y') = eq_step' ns x' y'

eq_step : (fi : Sig)(x : F fi (T fi -> Bool)) -> T fi -> Bool
eq_step fi x = \t -> eq_step' fi x (out fi t)

equal' : (fi : Sig) -> T fi -> (T fi -> Bool)
equal' fi = It fi (eq_step fi)

equal : (fi : Sig) -> T fi -> T fi -> Bool
equal fi x y = equal' fi x y


module Structure.Numeral.Integer where

import      Lvl
open import Structure.Setoid
open import Structure.Operator.Properties
open import Structure.Operator.Ring
open import Structure.OrderedField
open import Structure.Relator
open import Type

private variable ℓₒ ℓₗ ℓₑ ℓₗ₁ ℓₗ₂ : Lvl.Level
private variable Z : Type{ℓₒ}

record Integer ⦃ equiv : Equiv{ℓₑ}(Z) ⦄ (_+_ : Z → Z → Z) (_⋅_ : Z → Z → Z) (_≤_ : Z → Z → Type{ℓₗ}) : Typeω where
  constructor intro
  field
    ⦃ ring ⦄              : Ring(_+_)(_⋅_)
    ⦃ ordered ⦄           : Ordered(_+_)(_⋅_)(_≤_)
  open Ring(ring) public
  open Ordered(ordered) public

  𝐒 : Z → Z
  𝐒 = _+ 𝟏

  𝐏 : Z → Z
  𝐏 = _− 𝟏

  field
    ⦃ distinct-identities ⦄ : DistinctIdentities
    positive-induction : ∀{ℓ}{P : Z → Type{ℓ}} ⦃ rel-p : UnaryRelator(P) ⦄ → P(𝟎) → (∀{n} → (𝟎 ≤ n) → P(n) → P(𝐒(n))) → (∀{n} → (𝟎 ≤ n) → P(n))


open import x1-Base

module x2-Sort
         {X    : Set}
         {_≈_  : Rel X}
         {_≤_  : Rel X}
         (_≤?_ : Decidable _≤_)
         (ord  : TotalOrder _≈_ _≤_)
  where
open TotalOrder  ord         using (total; equivalence)
open Equivalence equivalence using (refl)

-- represent bounded lists, but want bounds to be open
-- so lift X in a type that contains a top and bottom elements
data ⊥X⊤ : Set where
  ⊤ ⊥ : ⊥X⊤
  ⟦_⟧ : X → ⊥X⊤

-- lift ordering to work with ⊥X⊤
data _≤̂_ : Rel ⊥X⊤ where
  ⊥≤̂     : ∀ {x} → ⊥ ≤̂ x
  ≤̂⊤     : ∀ {x} → x ≤̂ ⊤
  ≤-lift : ∀ {x y} → x ≤ y → ⟦ x ⟧ ≤̂ ⟦ y ⟧

-- bounded, ordered lists
-- elements ordered according to ≤ relation
data OList (l u : ⊥X⊤) : Set where
  -- nil works with any bounds l ≤ u
  nil  : l ≤̂ u
       → OList l u
  -- cons x to a list with x as a lower bound,
  -- return a list with lower bound l, l ≤̂ ⟦ x ⟧
  cons : ∀ x
         (xs : OList ⟦ x ⟧ u)
       → l ≤̂ ⟦ x ⟧
       → OList l u

toList : ∀ {l u} → OList l u → List X
toList (nil _)       = []
toList (cons x xs _) = x ∷ toList xs

insert : ∀ {l u} x
       → OList l           u
       →       l ≤̂ ⟦ x ⟧
       →           ⟦ x ⟧ ≤̂ u
       → OList l           u
insert x   (nil _)             l≤̂⟦x⟧ ⟦x⟧≤̂u =
  cons x  (nil ⟦x⟧≤̂u)
          l≤̂⟦x⟧

insert x L@(cons x' xs l≤̂⟦x'⟧) l≤̂⟦x⟧ ⟦x⟧≤̂u with x ≤? x'
... | left   x≤x'        =
  cons x  (cons   x' xs (≤-lift x≤x'))
          l≤̂⟦x⟧
... | (right x≤x'→Empty) =
  cons x' (insert x  xs ([ ≤-lift
                         , (λ y≤x → absurd (x≤x'→Empty y≤x)) ]
                         (total x' x))
                        ⟦x⟧≤̂u)
          l≤̂⟦x'⟧

-- Insertion sort uses OList ⊥ ⊤ to represent a sorted list with open bounds:
isort' : List X → OList ⊥ ⊤
isort' = foldr (λ x xs → insert x xs ⊥≤̂ ≤̂⊤) (nil ⊥≤̂)

isort : List X → List X
isort xs = toList (isort' xs)

------------------------------------------------------------------------------
-- Tree sort (more efficient)

-- bounded, ordered binary tree
data Tree (l u : ⊥X⊤) : Set where
  leaf : l ≤̂ u
       → Tree l       u
  node : (x : X)
       → Tree l ⟦ x ⟧
       → Tree   ⟦ x ⟧ u
       → Tree l       u

t-insert : ∀ {l u}
         → (x : X)
         → Tree l       u
         →      l   ≤̂ ⟦ x ⟧
         →    ⟦ x ⟧ ≤̂   u
         → Tree l       u
t-insert x (leaf _)       l≤x x≤u =
  node x    (leaf l≤x) (leaf x≤u)
t-insert x (node y ly yu) l≤x x≤u with x ≤? y
... | left x≤y  =
  node y    (t-insert x ly l≤x (≤-lift x≤y)) yu
... | right x>y =
  node y ly (t-insert x yu ([ (λ x≤y → absurd (x>y x≤y)) , ≤-lift ] (total x y)) x≤u)

t-fromList : List X → Tree ⊥ ⊤
t-fromList = foldr (λ x xs → t-insert x xs ⊥≤̂ ≤̂⊤) (leaf ⊥≤̂)

-- OList concatenation, including inserting a new element in the middle
_⇒_++_ : ∀ {l u} x
       → OList l ⟦ x ⟧
       → OList   ⟦ x ⟧ u
       → OList l      u
x ⇒ nil l≤u       ++ xu = cons x            xu  l≤u
x ⇒ cons y yx l≤y ++ xu = cons y (x ⇒ yx ++ xu) l≤y

t-flatten : ∀ {l u} → Tree l u → OList l u
t-flatten (leaf l≤u)     = (nil l≤u)
t-flatten (node x lx xu) = x ⇒ t-flatten lx ++ t-flatten xu

t-sort' : List X → OList ⊥ ⊤
t-sort' xs = t-flatten (foldr (λ x xs → t-insert x xs ⊥≤̂ ≤̂⊤) (leaf ⊥≤̂) xs)

t-sort : List X → List X
t-sort xs = toList (t-sort' xs)


module NatProps where
open import Data.Nat using (ℕ ; _≟_ ; _<_ ; zero ; suc ; s≤s)
open import Relation.Nullary using (Dec ; yes ; no ; ¬_)
open import Relation.Binary.PropositionalEquality using (_≡_ ; refl ; sym)

<-¬≡ : ∀ {n m} → n < m → ¬ n ≡ m
<-¬≡ {zero} {zero} ()
<-¬≡ {zero} {suc m} p = λ ()
<-¬≡ {suc n} {zero} p = λ ()
<-¬≡ {suc n} {suc m} (Data.Nat.s≤s p) with (<-¬≡ p)
... | q = λ { refl → q refl }


{-# OPTIONS --copatterns #-}
-- {-# OPTIONS -v tc.lhs.split:50 -v tc.cover:20 -v tc.cc:15 -v tc.lhs.top:10 #-}
-- {-# OPTIONS -v term:20 #-}
-- {-# OPTIONS --no-positivity-check #-}
-- {-# OPTIONS -v tc.def.fun:50  #-}
-- {-# OPTIONS -v 100  #-}
module CoPatStream where

import Common.Level
open import Common.Prelude hiding (map)
open import Common.Equality

record Stream (A : Set) : Set where
  coinductive
  constructor cons
  field
    head : A
    tail : Stream A
module S = Stream

record _≈_ {A : Set}(s t : Stream A) : Set where
  coinductive
  field
    head : S.head s ≡ S.head t
    tail : S.tail s ≈ S.tail t
module B = _≈_

repeat : {A : Set}(a : A) → Stream A
-- repeat a = cons a (repeat a)
S.head (repeat a) = a
S.tail (repeat a) = repeat a

module Cycle where

  cycle : Nat -> Stream Nat
  cycle n = cycle' n where
    cycle' : Nat -> Stream Nat
    S.head (cycle' n) = n
    S.tail (cycle' zero    ) = cycle n
    S.tail (cycle' (suc n')) = cycle' n'

module CoPat where

  map : {A B : Set} → (A → B) → Stream A → Stream B
  S.head (map f s) = f (S.head s)
  S.tail (map f s) = map f (S.tail s)

  map_id : {A : Set}(s : Stream A) → map (λ x → x) s ≈ s
  B.head (map_id s) = refl
  B.tail (map_id s) = map_id (S.tail s)

module HandTranslated where

  {-# NON_TERMINATING #-}
  map : {A B : Set} → (A → B) → Stream A → Stream B
  map f s = record
    { head = f (S.head s)
    ; tail = map f (S.tail s)
    }

  {- rejected because map is not unfolded
  {-# NON_TERMINATING #-}
  map_id : {A : Set}(s : Stream A) → map (λ x → x) s ≈ s
  map_id s = record
    { head = refl
    ; tail = map_id (S.tail s)
    }
  -}

module DeepCoPat where

  repeat₂ : {A : Set}(a₁ a₂ : A) → Stream A
  (       (S.head (repeat₂ a₁ a₂))) = a₁
  (S.head (S.tail (repeat₂ a₁ a₂))) = a₂
  (S.tail (S.tail (repeat₂ a₁ a₂))) = repeat₂ a₁ a₂

  repeat≈repeat₂ : {A : Set}(a : A) → repeat a ≈ repeat₂ a a
  (       (B.head (repeat≈repeat₂ a))) = refl
  (B.head (B.tail (repeat≈repeat₂ a))) = refl
  (B.tail (B.tail (repeat≈repeat₂ a))) = repeat≈repeat₂ a

module ProjectionRHS where

  repeat′ : {A : Set}(a : A) → Stream A
  (       (S.head (repeat′ a))) = a
  (S.head (S.tail (repeat′ a))) = a
  (S.tail (S.tail (repeat′ a))) = S.tail (repeat′ a)

-- -- Type error:
--   repeat≈repeat′ : {A : Set}(a : A) → repeat a ≈ repeat′ a
--   (       (B.head (repeat≈repeat′ a))) = refl
--   (B.head (B.tail (repeat≈repeat′ a))) = refl
--   (B.tail (B.tail (repeat≈repeat′ a))) = repeat≈repeat′ a


-- Intuitionistic propositional logic, vector-based de Bruijn approach, initial encoding

module Vi.Ip where

open import Lib using (Nat; suc; _+_; Fin; fin; Vec; _,_; proj; VMem; mem)

-- Types

infixl 2 _&&_
infixl 1 _||_
infixr 0 _=>_
data Ty : Set where
  UNIT  :             Ty
  _=>_  : Ty -> Ty -> Ty
  _&&_  : Ty -> Ty -> Ty
  _||_  : Ty -> Ty -> Ty
  FALSE :             Ty

infixr 0 _<=>_
_<=>_ : Ty -> Ty -> Ty
a <=> b = (a => b) && (b => a)

NOT : Ty -> Ty
NOT a = a => FALSE

TRUE : Ty
TRUE = FALSE => FALSE

-- Context and truth judgement

Cx : Nat -> Set
Cx n = Vec Ty n

isTrue : forall {tn} -> Ty -> Fin tn -> Cx tn -> Set
isTrue a i tc = VMem a i tc

-- Terms

module Ip where
  infixl 1 _$_
  infixr 0 lam=>_
  data Tm {tn} (tc : Cx tn) : Ty -> Set where
    var    : forall {a i}   -> isTrue a i tc             -> Tm tc a
    lam=>_ : forall {a b}   -> Tm (tc , a) b             -> Tm tc (a => b)
    _$_    : forall {a b}   -> Tm tc (a => b) -> Tm tc a -> Tm tc b
    pair'  : forall {a b}   -> Tm tc a        -> Tm tc b -> Tm tc (a && b)
    fst    : forall {a b}   -> Tm tc (a && b)            -> Tm tc a
    snd    : forall {a b}   -> Tm tc (a && b)            -> Tm tc b
    left   : forall {a b}   -> Tm tc a                   -> Tm tc (a || b)
    right  : forall {a b}   -> Tm tc b                   -> Tm tc (a || b)
    case'  : forall {a b c} -> Tm tc (a || b) -> Tm (tc , a) c -> Tm (tc , b) c -> Tm tc c
    abort  : forall {a}     -> Tm tc FALSE               -> Tm tc a

  syntax pair' x y    = [ x , y ]
  syntax case' xy x y = case xy => x => y

  v : forall {tn} (k : Nat) {tc : Cx (suc (k + tn))} -> Tm tc (proj tc (fin k))
  v i = var (mem i)

  Thm : Ty -> Set
  Thm a = forall {tn} {tc : Cx tn} -> Tm tc a
open Ip public

-- Example theorems

t1 : forall {a b} -> Thm (a => NOT a => b)
t1 =
  lam=>
    lam=> abort (v 0 $ v 1)

t2 : forall {a b} -> Thm (NOT a => a => b)
t2 =
  lam=>
    lam=> abort (v 1 $ v 0)

t3 : forall {a} -> Thm (a => NOT (NOT a))
t3 =
  lam=>
    lam=> v 0 $ v 1

t4 : forall {a} -> Thm (NOT a <=> NOT (NOT (NOT a)))
t4 =
  [ lam=>
      lam=> v 0 $ v 1
  , lam=>
      lam=> v 1 $ (lam=> v 0 $ v 1)
  ]


open import Relation.Binary.Core

module BBHeap.Equality.Properties {A : Set}(_≤_ : A → A → Set)  where

open import BBHeap _≤_
open import BBHeap.Equality _≤_
open import Bound.Lower A

refl≈ : {b : Bound}{h : BBHeap b} → h ≈ h
refl≈ {b} {leaf} = ≈leaf
refl≈ {b} {left b≤x l⋘r} = ≈left b≤x b≤x l⋘r l⋘r refl≈ refl≈
refl≈ {b} {right b≤x l⋙r} = ≈right b≤x b≤x l⋙r l⋙r refl≈ refl≈

sym≈ : {b b' : Bound}{h : BBHeap b}{h' : BBHeap b'} → h ≈ h' → h' ≈ h
sym≈ ≈leaf = ≈leaf
sym≈ (≈left b≤x b'≤x' l⋘r l'⋘r' l≈l' r≈r') = ≈left b'≤x' b≤x l'⋘r' l⋘r (sym≈ l≈l') (sym≈ r≈r')
sym≈ (≈right b≤x b'≤x' l⋙r l'⋙r' l≈l' r≈r') = ≈right b'≤x' b≤x l'⋙r' l⋙r (sym≈ l≈l') (sym≈ r≈r')

trans≈ : {b b' b'' : Bound}{h : BBHeap b}{h' : BBHeap b'}{h'' : BBHeap b''} → h ≈ h' → h' ≈ h'' → h ≈ h''
trans≈ ≈leaf ≈leaf = ≈leaf
trans≈ (≈left b≤x b'≤x' l⋘r l'⋘r' l≈l' r≈r') (≈left .b'≤x' b''≤x'' .l'⋘r' l''⋘r'' l'≈l'' r'≈r'') = ≈left b≤x b''≤x'' l⋘r l''⋘r'' (trans≈ l≈l' l'≈l'') (trans≈ r≈r' r'≈r'')
trans≈ (≈right b≤x b'≤x' l⋙r l'⋙r' l≈l' r≈r') (≈right .b'≤x' b''≤x'' .l'⋙r' l''⋙r'' l'≈l'' r'≈r'') = ≈right b≤x b''≤x'' l⋙r l''⋙r'' (trans≈ l≈l' l'≈l'') (trans≈ r≈r' r'≈r'')

mutual
  lemma≈≃ : {b b' b'' : Bound}{h : BBHeap b}{h' : BBHeap b'}{h'' : BBHeap b''} → h ≈ h' → h' ≃ h'' → h ≃ h''
  lemma≈≃ ≈leaf ≃lf = ≃lf
  lemma≈≃ (≈left b≤x b'≤x' l⋘r l'⋘r' l≈l' r≈r') (≃nd .b'≤x' b''≤x'' .l'⋘r' l''⋘r'' l'≃r' l''≃r'' l'≃l'') = ≃nd b≤x b''≤x'' l⋘r l''⋘r'' (lemma≈≃ l≈l' (lemma≃≈ l'≃r' (sym≈ r≈r'))) l''≃r'' (lemma≈≃ l≈l' l'≃l'')

  lemma≃≈ : {b b' b'' : Bound}{h : BBHeap b}{h' : BBHeap b'}{h'' : BBHeap b''} → h ≃ h' → h' ≈ h'' → h ≃ h''
  lemma≃≈ ≃lf ≈leaf = ≃lf
  lemma≃≈ (≃nd b≤x b'≤x' l⋘r l'⋘r' l≃r l'≃r' l≃l') (≈left .b'≤x' b''≤x'' .l'⋘r' l''⋘r'' l'≈l'' r'≈r'') = ≃nd b≤x b''≤x'' l⋘r l''⋘r'' l≃r (lemma≈≃ (sym≈ l'≈l'') (lemma≃≈ l'≃r' r'≈r'')) (lemma≃≈ l≃l' l'≈l'')

lemma≈⋗ : {b b' b'' : Bound}{h : BBHeap b}{h' : BBHeap b'}{h'' : BBHeap b''} → h ≈ h' → h' ⋗ h'' → h ⋗ h''
lemma≈⋗ (≈left b≤x .b'≤x' lf⋘ .lf⋘ ≈leaf ≈leaf) (⋗lf b'≤x') = ⋗lf b≤x
lemma≈⋗ (≈left b≤x .b'≤x' l⋘r .l'⋘r' l≈l' r≈r') (⋗nd b'≤x' b''≤x'' l'⋘r' l''⋘r'' l'≃r' l''≃r'' l'⋗l'') = ⋗nd b≤x b''≤x'' l⋘r l''⋘r'' (lemma≈≃ l≈l' (lemma≃≈ l'≃r' (sym≈ r≈r'))) l''≃r'' (lemma≈⋗ l≈l' l'⋗l'')

lemma⋗≈ : {b b' b'' : Bound}{h : BBHeap b}{h' : BBHeap b'}{h'' : BBHeap b''} → h ⋗ h' → h' ≈ h'' → h ⋗ h''
lemma⋗≈ (⋗lf b≤x) ≈leaf = ⋗lf b≤x
lemma⋗≈ (⋗nd b≤x b'≤x' l⋘r l'⋘r' l≃r l'≃r' l⋗l') (≈left .b'≤x' b''≤x'' .l'⋘r' l''⋘r'' l'≈l'' r'≈r'') = ⋗nd b≤x b''≤x'' l⋘r l''⋘r'' l≃r (lemma≈≃ (sym≈ l'≈l'') (lemma≃≈ l'≃r' r'≈r'')) (lemma⋗≈ l⋗l' l'≈l'')

lemma≈⋘ : {b b' b'' : Bound}{h : BBHeap b}{h' : BBHeap b'}{h'' : BBHeap b''} → h ≈ h' → h' ⋘ h'' → h ⋘ h''
lemma≈⋘ ≈leaf lf⋘ = lf⋘
lemma≈⋘ (≈left b≤x b'≤x' l⋘r l'⋘r' l≈l' r≈r') (ll⋘ .b'≤x' b''≤x'' .l'⋘r' l''⋘r'' l''≃r'' r'≃l'') = ll⋘ b≤x b''≤x'' l⋘r l''⋘r'' l''≃r'' (lemma≈≃ r≈r' r'≃l'')
lemma≈⋘ (≈right b≤x b'≤x' l⋙r l'⋙r' l≈l' r≈r') (lr⋘ .b'≤x' b''≤x'' .l'⋙r' l''⋘r'' l''≃r'' l'⋗l'') = lr⋘ b≤x b''≤x'' l⋙r l''⋘r'' l''≃r'' (lemma≈⋗ l≈l' l'⋗l'')

lemma⋘≈ : {b b' b'' : Bound}{h : BBHeap b}{h' : BBHeap b'}{h'' : BBHeap b''} → h ⋘ h' → h' ≈ h'' → h ⋘ h''
lemma⋘≈ lf⋘ ≈leaf = lf⋘
lemma⋘≈ (ll⋘ b≤x b'≤x' l⋘r l'⋘r' l'≃r' r≃l') (≈left .b'≤x' b''≤x'' .l'⋘r' l''⋘r'' l'≈l'' r'≈r'') = ll⋘ b≤x b''≤x'' l⋘r l''⋘r'' (lemma≈≃ (sym≈ l'≈l'') (lemma≃≈ l'≃r' r'≈r'')) (lemma≃≈ r≃l' l'≈l'')
lemma⋘≈ (lr⋘ b≤x b'≤x' l⋙r l'⋘r' l'≃r' l⋗l') (≈left .b'≤x' b''≤x'' .l'⋘r' l''⋘r'' l'≈l'' r'≈r'') = lr⋘ b≤x b''≤x'' l⋙r l''⋘r'' (lemma≈≃ (sym≈ l'≈l'') (lemma≃≈ l'≃r' r'≈r'')) (lemma⋗≈ l⋗l' l'≈l'')

lemma≈⋙ : {b b' b'' : Bound}{h : BBHeap b}{h' : BBHeap b'}{h'' : BBHeap b''} → h ≈ h' → h' ⋙ h'' → h ⋙ h''
lemma≈⋙ (≈left b≤x b'≤x' lf⋘ lf⋘ ≈leaf ≈leaf) (⋙lf .b'≤x') = ⋙lf b≤x
lemma≈⋙ (≈left b≤x b'≤x' l⋘r l'⋘r' l≈l' r≈r') (⋙rl .b'≤x' b'≤x'' .l'⋘r' l'≃r' l''⋘r'' l'⋗r'') = ⋙rl b≤x b'≤x'' l⋘r (lemma≈≃ l≈l' (lemma≃≈ l'≃r' (sym≈ r≈r'))) l''⋘r'' (lemma≈⋗ l≈l' l'⋗r'')
lemma≈⋙ (≈left b≤x b'≤x' l⋘r l'⋘r' l≈l' r≈r') (⋙rr .b'≤x' b''≤x'' .l'⋘r' l'≃r' l''⋙r'' l'≃l'') = ⋙rr b≤x b''≤x'' l⋘r (lemma≈≃ l≈l' (lemma≃≈ l'≃r' (sym≈ r≈r'))) l''⋙r'' (lemma≈≃ l≈l' l'≃l'')

lemma⋙≈ : {b b' b'' : Bound}{h : BBHeap b}{h' : BBHeap b'}{h'' : BBHeap b''} → h ⋙ h' → h' ≈ h'' → h ⋙ h''
lemma⋙≈ (⋙lf b≤x) ≈leaf = ⋙lf b≤x
lemma⋙≈ (⋙rl b≤x b'≤x' l⋘r l≃r l'⋘r' l⋗r') (≈left .b'≤x' b'≤x'' .l'⋘r' l''⋘r'' l'≈l'' r'≈r'') = ⋙rl b≤x b'≤x'' l⋘r l≃r l''⋘r'' (lemma⋗≈ l⋗r' r'≈r'')
lemma⋙≈ (⋙rr b≤x b'≤x' l⋘r l≃r l'⋙r' l≃l') (≈right .b'≤x' b''≤x'' .l'⋙r' l'⋙r'' l'≈l'' r'≈r'') = ⋙rr b≤x b''≤x'' l⋘r l≃r l'⋙r'' (lemma≃≈ l≃l' l'≈l'')


module Oscar.Class.ThickAndThin where

open import Oscar.Data.Fin
open import Oscar.Data.Equality
open import Oscar.Data.Nat
open import Oscar.Data.Maybe

record ThickAndThin {a} (A : Nat → Set a) : Set a where
  field
    thin : ∀ {m} → Fin (suc m) → A m → A (suc m)
    thin-injective : ∀ {m} (x : Fin (suc m)) {y₁ y₂ : A m} → thin x y₁ ≡ thin x y₂ → y₁ ≡ y₂
    thick : ∀ {m} → A (suc m) → Fin m → A m
    thick∘thin=id : ∀ {m} (x : Fin m) (y : A m) → thick (thin (suc x) y) x ≡ y
    check : ∀ {m} → Fin (suc m) → A (suc m) → Maybe (A m)
    thin-check-id : ∀ {m} (x : Fin (suc m)) y → ∀ y' → thin x y' ≡ y → check x y ≡ just y'

open ThickAndThin ⦃ … ⦄ public

-- open import Oscar.Level

-- record ThickAndThin' {a} {A : Set a} (f : A → A) {b} (B : A → Set b) (g : ∀ {x} → B x → B (f x)) {c} (C : A → Set c) : Set (a ⊔ b ⊔ c) where
--   field
--     thin : ∀ {n} → B (f n) → C n → C (f n)
--     thick : ∀ {n} → C (f n) → B n → C n
--     thin-injective : ∀ {n} (z : B (f n)) {x y : C n} → thin z x ≡ thin z y → x ≡ y
--     thick∘thin=id : ∀ {n} (x : B n) (y : C n) → thick (thin (g x) y) x ≡ y
--     check : ∀ {n} → B (f n) → C (f n) → Maybe (C n)
--     thin-check-id : ∀ {n} (x : B (f n)) y → ∀ y' → thin x y' ≡ y → check x y ≡ just y'

-- --open ThickAndThin' ⦃ … ⦄ public


------------------------------------------------------------------------------
-- FOTC natural numbers
------------------------------------------------------------------------------

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

module FOTC.Data.Nat where

open import FOTC.Base
open import FOTC.Data.Nat.Type public

infixl 7 _*_
infixl 6 _+_ _∸_

------------------------------------------------------------------------------
-- Arithmetic operations

postulate
  _+_  : D → D → D
  +-0x : ∀ n → zero + n      ≡ n
  +-Sx : ∀ m n → succ₁ m + n ≡ succ₁ (m + n)
{-# ATP axioms +-0x +-Sx #-}

postulate
  _∸_  : D → D → D
  ∸-x0 : ∀ n → n ∸ zero      ≡ n
  ∸-xS : ∀ m n → m ∸ succ₁ n ≡ pred₁ (m ∸ n)
{-# ATP axioms ∸-x0 ∸-xS #-}

postulate
  _*_  : D → D → D
  *-0x : ∀ n → zero * n      ≡ zero
  *-Sx : ∀ m n → succ₁ m * n ≡ n + m * n
{-# ATP axioms *-0x *-Sx #-}


module Data.Num.Digit where

open import Data.Nat
open import Data.Nat.Properties
open import Data.Nat.Properties.Simple
open import Data.Nat.Properties.Extra
open import Data.Fin as Fin
    using (Fin; fromℕ≤; inject≤; toℕ; zero; suc)
open import Data.Fin.Properties as FinProps using (bounded; toℕ-fromℕ≤)

open import Function
open import Relation.Binary.PropositionalEquality
open import Relation.Nullary
open import Relation.Nullary.Negation
open import Relation.Binary

open ≡-Reasoning
open ≤-Reasoning renaming (begin_ to start_; _∎ to _□; _≡⟨_⟩_ to _≈⟨_⟩_)
open DecTotalOrder decTotalOrder using (reflexive) renaming (refl to ≤-refl)

--------------------------------------------------------------------------------
-- Digits
--------------------------------------------------------------------------------

Digit : ℕ → Set
Digit = Fin

-- converting to and from ℕ
Digit-toℕ : ∀ {d} → Digit d → ℕ → ℕ
Digit-toℕ x o = toℕ x + o

Digit-fromℕ : ∀ {d} → (n o : ℕ) → d + o ≥ n → Digit (suc d)
Digit-fromℕ {d} n o upper-bound with n ∸ o ≤? d
Digit-fromℕ {d} n o upper-bound | yes p = fromℕ≤ (s≤s p)
Digit-fromℕ {d} n o upper-bound | no ¬p = contradiction p ¬p
    where   p : n ∸ o ≤ d
            p = start
                    n ∸ o
                ≤⟨ ∸n-mono o upper-bound ⟩
                    (d + o) ∸ o
                ≈⟨ m+n∸n≡m d o ⟩
                    d
                □

Digit-fromℕ-toℕ : ∀ {d o}
    → (n : ℕ)
    → (lower-bound :     o ≤ n)
    → (upper-bound : d + o ≥ n)
    → Digit-toℕ (Digit-fromℕ {d} n o upper-bound) o ≡ n
Digit-fromℕ-toℕ {d} {o} n lb ub with n ∸ o ≤? d
Digit-fromℕ-toℕ {d} {o} n lb ub | yes q =
    begin
        toℕ (fromℕ≤ (s≤s q)) + o
    ≡⟨ cong (λ x → x + o) (toℕ-fromℕ≤ (s≤s q)) ⟩
        n ∸ o + o
    ≡⟨ m∸n+n≡m lb ⟩
        n
    ∎
Digit-fromℕ-toℕ {d} {o} n lb ub | no ¬q = contradiction q ¬q
    where   q : n ∸ o ≤ d
            q = +n-mono-inverse o $
                start
                    n ∸ o + o
                ≈⟨ m∸n+n≡m lb ⟩
                    n
                ≤⟨ ub ⟩
                    d + o
                □

--------------------------------------------------------------------------------
-- Properties of all Digits
--------------------------------------------------------------------------------

Digit-upper-bound : ∀ {d} → (o : ℕ) → (x : Digit d) → Digit-toℕ x o < d + o
Digit-upper-bound {d} o x = +n-mono o (bounded x)

Digit-lower-bound : ∀ {d} → (o : ℕ) → (x : Digit d) → Digit-toℕ x o ≥ o
Digit-lower-bound {d} o x = n≤m+n (toℕ x) o

--------------------------------------------------------------------------------
-- Special Digits
--------------------------------------------------------------------------------

-- A digit at its greatest
Greatest : ∀ {d} (x : Digit d) → Set
Greatest {d} x = suc (toℕ x) ≡ d

Greatest? : ∀ {d} (x : Digit d) → Dec (Greatest x)
Greatest? {d} x = suc (toℕ x) ≟ d

greatest-digit : ∀ d → Digit (suc d)
greatest-digit d = Fin.fromℕ d

greatest-digit-toℕ : ∀ {d o}
    → (x : Digit (suc d))
    → Greatest x
    → Digit-toℕ x o ≡ d + o
greatest-digit-toℕ {d} {o} x greatest = cancel-suc $
    begin
        suc (Digit-toℕ x o)
    ≡⟨ cong (λ w → w + o) greatest ⟩
        suc (d + o)
    ∎

greatest-of-all : ∀ {d} (o : ℕ) → (x y : Digit d) → Greatest x → Digit-toℕ x o ≥ Digit-toℕ y o
greatest-of-all o zero    zero     refl = ≤-refl
greatest-of-all o zero    (suc ()) refl
greatest-of-all o (suc x) zero     greatest = +n-mono o {zero} {suc (toℕ x)} z≤n
greatest-of-all o (suc x) (suc y)  greatest = s≤s (greatest-of-all o x y (cancel-suc greatest))

greatest-digit-is-the-Greatest : ∀ d → Greatest (greatest-digit d)
greatest-digit-is-the-Greatest d = cong suc (FinProps.to-from d)

-- carry, 1 `max` o, in case that the least digit "o" is 0
carry : ℕ → ℕ
carry o = 1 ⊔ o

carry-lower-bound : ∀ {o} → carry o ≥ o
carry-lower-bound {o} = m≤n⊔m o 1

carry-upper-bound : ∀ {d o} → 2 ≤ suc d + o → carry o ≤ d + o
carry-upper-bound {d} {zero}  proper = ≤-pred proper
carry-upper-bound {d} {suc o} proper = n≤m+n d (suc o)

carry-digit : ∀ d o → 2 ≤ suc d + o → Digit (suc d)
carry-digit d o proper = Digit-fromℕ (carry o) o (carry-upper-bound {d} proper)

carry-digit-toℕ : ∀ d o
    → (proper : 2 ≤ suc (d + o))
    → Digit-toℕ (carry-digit d o proper) o ≡ carry o
carry-digit-toℕ d o proper = Digit-fromℕ-toℕ (carry o) (m≤n⊔m o 1) (carry-upper-bound {d} proper)

-- LCD-upper-bound-prim d zero    proper = ≤-pred proper
-- LCD-upper-bound-prim d (suc o) proper = n≤m+n (suc o) d
--
-- LCD : ∀ d o → 2 ≤ suc d + o → Digit (suc d)
-- LCD d o proper = Digit-fromℕ (1 ⊔ o) o (LCD-upper-bound-prim d o proper)
--
-- LCD-toℕ : ∀ d o
--     → (proper : 2 ≤ suc (d + o))
--     → Digit-toℕ (LCD d o proper) o ≡ 1 ⊔ o
-- LCD-toℕ d o proper = Digit-fromℕ-toℕ (1 ⊔ o) (m≤n⊔m o 1) (LCD-upper-bound-prim d o proper)
--
-- LCD-upper-bound : ∀ {d o}
--     → (proper : 2 ≤ suc (d + o))
--     → Digit-toℕ (LCD d o proper) o ≤ d + o
-- LCD-upper-bound {d} {o} proper =
--     start
--         Digit-toℕ (LCD d o proper) o
--     ≈⟨ LCD-toℕ d o proper ⟩
--         1 ⊔ o
--     ≤⟨ LCD-upper-bound-prim d o proper ⟩
--         d + o
--     □

--------------------------------------------------------------------------------
-- Functions on Digits
--------------------------------------------------------------------------------

-- +1
digit+1 : ∀ {d} → (x : Digit d) → (¬greatest : ¬ (Greatest x)) → Fin d
digit+1 x ¬greatest = fromℕ≤ {suc (toℕ x)} (≤∧≢⇒< (bounded x) ¬greatest)

digit+1-toℕ : ∀ {d o}
    → (x : Digit d)
    → (¬greatest : ¬ (Greatest x))
    → Digit-toℕ (digit+1 x ¬greatest) o ≡ suc (Digit-toℕ x o)
digit+1-toℕ {d} {o} x ¬greatest =
    begin
        Digit-toℕ (digit+1 x ¬greatest) o
    ≡⟨ cong (λ w → w + o) (toℕ-fromℕ≤ (≤∧≢⇒< (bounded x) ¬greatest)) ⟩
        suc (Digit-toℕ x o)
    ∎

digit+1-n-lemma : ∀ {d}
    → (x : Digit d)
    → Greatest x
    → (n : ℕ)
    → n > 0
    → suc (toℕ x) ∸ n < d
digit+1-n-lemma {zero } () greatest n n>0
digit+1-n-lemma {suc d} x greatest n n>0 = s≤s $ start
        suc (toℕ x) ∸ n
    ≤⟨ ∸n-mono n (bounded x) ⟩
        suc d ∸ n
    ≤⟨ n∸-mono (suc d) n>0 ⟩
        suc d ∸ 1
    ≤⟨ ≤-refl ⟩
        d
    □

-- +1 and then -n, useful for handling carrying on increment
digit+1-n : ∀ {d}
    → (x : Digit d)
    → Greatest x
    → (n : ℕ)
    → n > 0
    → Digit d
digit+1-n x greatest n n>0 = fromℕ≤ (digit+1-n-lemma x greatest n n>0)

digit+1-n-toℕ : ∀ {d o}
    → (x : Digit d)
    → (greatest : Greatest x)
    → (n : ℕ)
    → (n>0 : n > 0)
    → n ≤ d
    → Digit-toℕ (digit+1-n x greatest n n>0) o ≡ suc (Digit-toℕ x o) ∸ n
digit+1-n-toℕ {zero}  {o} () greatest n n>0 n≤d
digit+1-n-toℕ {suc d} {o} x greatest n n>0  n≤d =
    begin
        toℕ (digit+1-n x greatest n n>0) + o
    ≡⟨ cong (λ w → w + o) (toℕ-fromℕ≤ (digit+1-n-lemma x greatest n n>0)) ⟩
        suc (toℕ x) ∸ n + o
    ≡⟨ +-comm (suc (toℕ x) ∸ n) o ⟩
        o + (suc (toℕ x) ∸ n)
    ≡⟨ sym (+-∸-assoc o {suc (toℕ x)} {n} $
        start
            n
        ≤⟨ n≤d ⟩
            suc d
        ≈⟨ sym greatest ⟩
            suc (toℕ x)
        □)
    ⟩
        (o + suc (toℕ x)) ∸ n
    ≡⟨ cong (λ w → w ∸ n) (+-comm o (suc (toℕ x))) ⟩
        suc (toℕ x) + o ∸ n
    ∎


-- Isomorphism of indexed families
module SOAS.Families.Isomorphism {T} where

open import SOAS.Common
open import SOAS.Context {T}
open import SOAS.Families.Core {T}

open import Categories.Morphism 𝔽amilies public using ()
        renaming ( _≅_ to _≅ₘ_ ; module ≅ to ≅ₘ ; ≅-setoid to ≅ₘ-setoid)

-- Isomorphism between two families
record FamIso (X Y : Family) : Set where

  -- Prove isomorphism of the families X and Y in the category 𝔽am from
  -- a proof of isomorphism of the sets X Γ and Y Γ for all contexts Γ.
  field iso : (Γ : Ctx) → X Γ ≅ₛ Y Γ

  -- Two directions of the isomorphism.
  iso⇒ : X ⇾ Y
  iso⇒ {Γ} = _≅ₛ_.from (iso Γ)
  iso⇐ : Y ⇾ X
  iso⇐ {Γ} = _≅ₛ_.to (iso Γ)

  -- Construct the isomorphism of families
  ≅ₘ : X ≅ₘ Y
  ≅ₘ = record
    { from = iso⇒
    ; to = iso⇐
    ; iso = record
      { isoˡ = λ {Γ}{x} → _≅ₛ_.isoˡ (iso Γ)
      ; isoʳ = λ {Γ}{x} → _≅ₛ_.isoʳ (iso Γ)
      }
    }

≅ₘ→FamIso : {X Y : Family} → X ≅ₘ Y → FamIso X Y
≅ₘ→FamIso p = record { iso = λ Γ → record
  { from = _≅ₘ_.from p
  ; to = _≅ₘ_.to p
  ; iso = record { isoˡ = _≅ₘ_.isoˡ p ; isoʳ = _≅ₘ_.isoʳ p }
  } }

-- | Isomorphism of sorted families

open import Categories.Morphism 𝔽amiliesₛ public
  using () renaming ( _≅_ to _≅̣ₘ_ ; module ≅ to ≅̣ₘ)

-- Sorted family isomorphism gives a family isomorphism at each sort
≅̣ₘ→≅ₘ : {τ : T}{𝒳 𝒴 : Familyₛ} → 𝒳 ≅̣ₘ 𝒴 → 𝒳 τ ≅ₘ 𝒴 τ
≅̣ₘ→≅ₘ {τ} p = record { from = _≅̣ₘ_.from p ; to = _≅̣ₘ_.to p
                     ; iso = record { isoˡ = _≅̣ₘ_.isoˡ p ; isoʳ = _≅̣ₘ_.isoʳ p } }

-- Family isomorphism at each sort gives sorted family isomorphism
≅ₘ→≅̣ₘ : {𝒳 𝒴 : Familyₛ} → ({τ : T} → 𝒳 τ ≅ₘ 𝒴 τ) → 𝒳 ≅̣ₘ 𝒴
≅ₘ→≅̣ₘ p = record { from = _≅ₘ_.from p ; to = _≅ₘ_.to p
                 ; iso = record { isoˡ = _≅ₘ_.isoˡ p ; isoʳ = _≅ₘ_.isoʳ p } }


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

module sets.int.core where

open import sum
open import equality
open import function
import sets.nat as N
open N using (ℕ; suc)
open import sets.int.definition
open import sets.int.utils
open import sets.vec
open import hott.level.closure

infixl 7 _*_
infixl 6 _+_

from-nat : ℕ → ℤ
from-nat n = n [-] 0

zero : ℤ
zero = from-nat 0

one : ℤ
one = from-nat 1

private
  module _ where
    open N

    add : ℕ → ℕ → ℕ → ℕ → ℤ
    add n n' m m' = (n + m) [-] (n' + m')

    add-sound : (n n' d m m' e : ℕ)
              → add n n' m m'
              ≡ add (d + n) (d + n') (e + m) (e + m')
    add-sound n n' d m m' e
      = eq-ℤ (n + m) (n' + m') (d + e)
      · ap₂ _[-]_ (solve 4 (exp λ d e n m → d :+ e :+ (n :+ m))
                           (exp λ d e n m → d :+ n :+ (e :+ m))
                           (d ∷∷ e ∷∷ n ∷∷ m ∷∷ ⟦⟧))
                  (solve 4 (exp λ d e n m → d :+ e :+ (n :+ m))
                           (exp λ d e n m → d :+ n :+ (e :+ m))
                           (d ∷∷ e ∷∷ n' ∷∷ m' ∷∷ ⟦⟧))

    mul : ℕ → ℕ → ℕ → ℕ → ℤ
    mul n n' m m' = (n * m + n' * m') [-] (n' * m + n * m')

    mul-sound : (n n' d m m' e : ℕ)
              → mul n n' m m'
              ≡ mul (d + n) (d + n') (e + m) (e + m')
    mul-sound n n' d m m' e
      = eq-ℤ (n * m + n' * m') (n' * m + n * m')
             (d * e + d * m + n * e + d * e + d * m' + n' * e)
      · ap₂ _[-]_ lem₁ lem₂
      where
        distr₂ : ∀ a b c d
               → (a + b) * (c + d)
               ≡ a * c + a * d + b * c + b * d
        distr₂ a b c d = right-distr a b (c + d)
                       · ap₂ _+_ (left-distr a c d) (left-distr b c d)
                       · sym (+-associativity (a * c + a * d) (b * c) (b * d))

        lem₁ : d * e + d * m + n * e + d * e + d * m' + n' * e + (n * m + n' * m')
             ≡ (d + n) * (e + m) + (d + n') * (e + m')
        lem₁ = sym
             $ ap₂ _+_ (distr₂ d n e m) (distr₂ d n' e m')
             · solve 7 (exp λ a b c d e f g → a :+ b :+ c :+ d :+ (a :+ e :+ f :+ g))
                       (exp λ a b c d e f g → a :+ b :+ c :+ a :+ e :+ f :+ (d :+ g))
                       ((d * e) ∷∷ (d * m) ∷∷ (n * e) ∷∷ (n * m) ∷∷
                        (d * m') ∷∷ (n' * e) ∷∷ (n' * m') ∷∷ ⟦⟧)

        lem₂ : d * e + d * m + n * e + d * e + d * m' + n' * e + (n' * m + n * m')
             ≡ (d + n') * (e + m) + (d + n) * (e + m')
        lem₂ = sym
             $ ap₂ _+_ (distr₂ d n' e m) (distr₂ d n e m')
             · solve 7 (exp λ a b c d e f g → a :+ b :+ c :+ d :+ (a :+ e :+ f :+ g))
                         (exp λ a b c d e f g → a :+ b :+ f :+ a :+ e :+ c :+ (d :+ g))
                         ((d * e) ∷∷ (d * m) ∷∷ (n' * e) ∷∷ (n' * m) ∷∷
                          (d * m') ∷∷ (n * e) ∷∷ (n * m') ∷∷ ⟦⟧)

    neg : ℕ → ℕ → ℤ
    neg n n' = n' [-] n

    neg-sound : (n n' d : ℕ) → neg n n' ≡ neg (d + n) (d + n')
    neg-sound n n' d = eq-ℤ n' n d

_+_ : ℤ → ℤ → ℤ
_+_ = elim₂-ℤ hℤ add add-sound

negate : ℤ → ℤ
negate = elim-ℤ hℤ (neg , neg-sound)

inc : ℤ → ℤ
inc n = one + n

dec : ℤ → ℤ
dec n = negate one + n

_*_ : ℤ → ℤ → ℤ
_*_ = elim₂-ℤ hℤ mul mul-sound


{-# OPTIONS --cubical --safe #-}
module Cubical.HITs.FiniteMultiset.Properties where

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

open import Cubical.HITs.FiniteMultiset.Base

private
  variable
    A : Type₀

infixr 30 _++_

_++_ : ∀ (xs ys : FMSet A) → FMSet A
[] ++ ys = ys
(x ∷ xs) ++ ys = x ∷ xs ++ ys
comm x y xs i ++ ys = comm x y (xs ++ ys) i
trunc xs zs p q i j ++ ys =
  trunc (xs ++ ys) (zs ++ ys) (cong (_++ ys) p) (cong (_++ ys) q) i j

unitl-++ : ∀ (xs : FMSet A) → [] ++ xs ≡ xs
unitl-++ xs = refl

unitr-++ : ∀ (xs : FMSet A) → xs ++ [] ≡ xs
unitr-++ = FMSetElimProp.f (trunc _ _)
  refl
  (λ x p → cong (_∷_ x) p)

assoc-++ : ∀ (xs ys zs : FMSet A) → xs ++ (ys ++ zs) ≡ (xs ++ ys) ++ zs
assoc-++ = FMSetElimProp.f (propPi (λ _ → propPi (λ _ → trunc _ _)))
  (λ ys zs → refl)
  (λ x p ys zs → cong (_∷_ x) (p ys zs))

cons-++ : ∀ (x : A) (xs : FMSet A) → x ∷ xs ≡ xs ++ [ x ]
cons-++ x = FMSetElimProp.f (trunc _ _)
  refl
  (λ y {xs} p → comm x y xs ∙ cong (_∷_ y) p)

comm-++ : ∀ (xs ys : FMSet A) → xs ++ ys ≡ ys ++ xs
comm-++ = FMSetElimProp.f (propPi (λ _ → trunc _ _))
  (λ ys → sym (unitr-++ ys))
  (λ x {xs} p ys → cong (x ∷_) (p ys)
                 ∙ cong (_++ xs) (cons-++ x ys)
                 ∙ sym (assoc-++ ys [ x ] xs))

module FMSetUniversal {ℓ} {M : Type ℓ} (MSet : isSet M)
  (e : M) (_⊗_ : M → M → M)
  (comm-⊗ : ∀ x y → x ⊗ y ≡ y ⊗ x) (assoc-⊗ : ∀ x y z → x ⊗ (y ⊗ z) ≡ (x ⊗ y) ⊗ z)
  (unit-⊗ : ∀ x → e ⊗ x ≡ x)
  (f : A → M) where

  f-extend : FMSet A → M
  f-extend = FMSetRec.f MSet e (λ x m → f x ⊗ m)
         (λ x y m → comm-⊗ (f x) (f y ⊗ m) ∙ sym (assoc-⊗ (f y) m (f x)) ∙ cong (f y ⊗_) (comm-⊗ m (f x)))

  f-extend-nil : f-extend [] ≡ e
  f-extend-nil = refl

  f-extend-cons : ∀ x xs → f-extend (x ∷ xs) ≡ f x ⊗ f-extend xs
  f-extend-cons x xs = refl

  f-extend-sing : ∀ x → f-extend [ x ] ≡ f x
  f-extend-sing x = comm-⊗ (f x) e ∙ unit-⊗ (f x)

  f-extend-++ : ∀ xs ys → f-extend (xs ++ ys) ≡ f-extend xs ⊗ f-extend ys
  f-extend-++ = FMSetElimProp.f (propPi λ _ → MSet _ _)
    (λ ys → sym (unit-⊗ (f-extend ys)))
    (λ x {xs} p ys → cong (f x ⊗_) (p ys) ∙ assoc-⊗ (f x) (f-extend xs) (f-extend ys))

  module _ (h : FMSet A → M) (h-nil : h [] ≡ e) (h-sing : ∀ x → h [ x ] ≡ f x)
           (h-++ : ∀ xs ys → h (xs ++ ys) ≡ h xs ⊗ h ys) where

    f-extend-unique : h ≡ f-extend
    f-extend-unique = funExt (FMSetElimProp.f (MSet _ _)
                              h-nil
                              (λ x {xs} p → (h-++ [ x ] xs) ∙ cong (_⊗ h xs) (h-sing x) ∙ cong (f x ⊗_) p))


module GGT.Bundles where

open import Level
open import Relation.Unary
open import Relation.Binary -- using (Rel)
open import Algebra.Core
open import Algebra.Bundles
open import GGT.Structures
open import Data.Product
-- do we need a left action definition?
-- parametrize over Op r/l?

record Action a b ℓ₁ ℓ₂ : Set (suc (a ⊔ b ⊔ ℓ₁ ⊔ ℓ₂))  where
  infixl 6 _·_
  infix 3 _≋_
  open Group hiding (setoid)
  field
    G : Group a ℓ₁
    Ω : Set b
    _≋_ : Rel Ω ℓ₂
    _·_ : Opᵣ (Carrier G) Ω
    isAction : IsAction (_≈_ G) _≋_ _·_ (_∙_ G) (ε G) (_⁻¹ G)

  open IsAction isAction public

  -- the (raw) pointwise stabilizer
  stab : Ω → Pred (Carrier G) ℓ₂
  stab o = λ (g : Carrier G) → o · g ≋ o

  -- Orbital relation
  _ω_  : Rel Ω (a ⊔ ℓ₂)
  o ω o' = ∃[ g ] (o · g ≋ o')

  _·G : Ω → Pred Ω (a ⊔ ℓ₂)
  o ·G = o ω_

  -- Orbits
  open import GGT.Setoid setoid (a ⊔ ℓ₂)
  Orbit : Ω → Setoid (b ⊔ (a ⊔ ℓ₂)) ℓ₂
  Orbit o = subSetoid (o ·G)


{-

Definition of finite sets

A set is finite if it is merely equivalent to `Fin n` for some `n`. We
can translate this to code in two ways: a truncated sigma of a nat and
an equivalence, or a sigma of a nat and a truncated equivalence. We
prove that both formulations are equivalent.

-}

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

module Cubical.Data.FinSet where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Logic
open import Cubical.Foundations.Function
open import Cubical.Foundations.Structure
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.Univalence
open import Cubical.Foundations.Equiv
open import Cubical.HITs.PropositionalTruncation
open import Cubical.Data.Unit
open import Cubical.Data.Nat
open import Cubical.Data.Fin
open import Cubical.Data.Sigma

private
  variable
    ℓ : Level
    A : Type ℓ

isFinSet : Type ℓ → Type ℓ
isFinSet A = ∃[ n ∈ ℕ ] A ≃ Fin n

isProp-isFinSet : isProp (isFinSet A)
isProp-isFinSet = propTruncIsProp

FinSet : Type (ℓ-suc ℓ)
FinSet = TypeWithStr _ isFinSet

isFinSetFin : ∀ {n} → isFinSet (Fin n)
isFinSetFin = ∣ _ , pathToEquiv refl ∣

isFinSetUnit : isFinSet Unit
isFinSetUnit = ∣ 1 , Unit≃Fin1 ∣

isFinSetΣ : Type ℓ → Type ℓ
isFinSetΣ A = Σ[ n ∈ ℕ ] ∥ A ≃ Fin n ∥

FinSetΣ : Type (ℓ-suc ℓ)
FinSetΣ = TypeWithStr _ isFinSetΣ

isProp-isFinSetΣ : isProp (isFinSetΣ A)
isProp-isFinSetΣ {A = A} (n , equivn) (m , equivm) =
  Σ≡Prop (λ _ → propTruncIsProp) n≡m
  where
    Fin-n≃Fin-m : ∥ Fin n ≃ Fin m ∥
    Fin-n≃Fin-m = rec
      propTruncIsProp
      (rec
        (isPropΠ λ _ → propTruncIsProp)
        (λ hm hn → ∣ Fin n ≃⟨ invEquiv hn ⟩ A ≃⟨ hm ⟩ Fin m ■ ∣)
        equivm
      )
      equivn

    Fin-n≡Fin-m : ∥ Fin n ≡ Fin m ∥
    Fin-n≡Fin-m = rec propTruncIsProp (∣_∣ ∘ ua) Fin-n≃Fin-m

    ∥n≡m∥ : ∥ n ≡ m ∥
    ∥n≡m∥ = rec propTruncIsProp (∣_∣ ∘ Fin-inj n m) Fin-n≡Fin-m

    n≡m : n ≡ m
    n≡m = rec (isSetℕ n m) (λ p → p) ∥n≡m∥

isFinSet≡isFinSetΣ : isFinSet A ≡ isFinSetΣ A
isFinSet≡isFinSetΣ {A = A} = hPropExt isProp-isFinSet isProp-isFinSetΣ to from
  where
    to : isFinSet A → isFinSetΣ A
    to ∣ n , equiv ∣ = n , ∣ equiv ∣
    to (squash p q i) = isProp-isFinSetΣ (to p) (to q) i

    from : isFinSetΣ A → isFinSet A
    from (n , ∣ isFinSet-A ∣) = ∣ n , isFinSet-A ∣
    from (n , squash p q i) = isProp-isFinSet (from (n , p)) (from (n , q)) i

FinSet≡FinSetΣ : FinSet {ℓ} ≡ FinSetΣ
FinSet≡FinSetΣ = ua (isoToEquiv (iso to from to-from from-to))
  where
    to : FinSet → FinSetΣ
    to (A , isFinSetA) = A , transport isFinSet≡isFinSetΣ isFinSetA

    from : FinSetΣ → FinSet
    from (A , isFinSetΣA) = A , transport (sym isFinSet≡isFinSetΣ) isFinSetΣA

    to-from : ∀ A → to (from A) ≡ A
    to-from A = Σ≡Prop (λ _ → isProp-isFinSetΣ) refl

    from-to : ∀ A → from (to A) ≡ A
    from-to A = Σ≡Prop (λ _ → isProp-isFinSet) refl

card : FinSet {ℓ} → ℕ
card = fst ∘ snd ∘ transport FinSet≡FinSetΣ


------------------------------------------------------------------------
-- Some derivable properties
------------------------------------------------------------------------

open import Algebra

module Algebra.Props.Ring (r : Ring) where

open Ring r
import Relation.Binary.EqReasoning as EqR; open EqR setoid
open import Data.Function
open import Data.Product

-‿*-distribˡ : ∀ x y → - x * y ≈ - (x * y)
-‿*-distribˡ x y = begin
  - x * y                        ≈⟨ sym $ proj₂ +-identity _ ⟩
  - x * y + 0#                   ≈⟨ refl ⟨ +-pres-≈ ⟩ sym (proj₂ -‿inverse _) ⟩
  - x * y + (x * y + - (x * y))  ≈⟨ sym $ +-assoc _ _ _  ⟩
  - x * y + x * y + - (x * y)    ≈⟨ sym (proj₂ distrib _ _ _) ⟨ +-pres-≈ ⟩ refl ⟩
  (- x + x) * y + - (x * y)      ≈⟨ (proj₁ -‿inverse _ ⟨ *-pres-≈ ⟩ refl)
                                      ⟨ +-pres-≈ ⟩
                                    refl ⟩
  0# * y + - (x * y)             ≈⟨ proj₁ zero _ ⟨ +-pres-≈ ⟩ refl ⟩
  0# + - (x * y)                 ≈⟨ proj₁ +-identity _ ⟩
  - (x * y)                      ∎

-‿*-distribʳ : ∀ x y → x * - y ≈ - (x * y)
-‿*-distribʳ x y = begin
  x * - y                        ≈⟨ sym $ proj₁ +-identity _ ⟩
  0# + x * - y                   ≈⟨ sym (proj₁ -‿inverse _) ⟨ +-pres-≈ ⟩ refl ⟩
  - (x * y) + x * y + x * - y    ≈⟨ +-assoc _ _ _  ⟩
  - (x * y) + (x * y + x * - y)  ≈⟨ refl ⟨ +-pres-≈ ⟩ sym (proj₁ distrib _ _ _)  ⟩
  - (x * y) + x * (y + - y)      ≈⟨ refl ⟨ +-pres-≈ ⟩ (refl ⟨ *-pres-≈ ⟩ proj₂ -‿inverse _) ⟩
  - (x * y) + x * 0#             ≈⟨ refl ⟨ +-pres-≈ ⟩ proj₂ zero _ ⟩
  - (x * y) + 0#                 ≈⟨ proj₂ +-identity _ ⟩
  - (x * y)                      ∎


open import Prelude

module Nat where

  -- definitions

  data Nat : Set where
    Z : Nat
    1+ : Nat → Nat

  {-# BUILTIN NATURAL Nat #-}

  _+_ : Nat → Nat → Nat
  Z + m = m
  1+ n + m = 1+ (n + m)

  infixl 60 _+_

  data _≤_ : Nat → Nat → Set where
    ≤refl  : ∀{n} → n ≤ n
    ≤1+    : ∀{n m} → n ≤ m → n ≤ 1+ m

  infix 40 _≤_

  _<_ : Nat → Nat → Set
  n < m = n ≤ m ∧ n ≠ m
  infix 40 _<_

  -- The `difference` operation accepts two numbers n and m and a proof that n ≤ m.
  -- Alternative definitions could return an option, returning None if n > m,
  -- or truncation, returning 0 if n > m. The latter may make some of the work easier,
  -- if it allows us to avoid issues with proof relevance. Even so, many proofs would
  -- still need to make use of the proof that n ≤ m to establish that the difference is
  -- not arbitrarily 0. As such, we believe that refactoring to use the perhaps more
  -- conventional truncation approach would not necessarily go far enough in cleaning up
  -- proofs as to be worth the refactoring effort.
  difference : ∀{n m} → n ≤ m → Nat
  difference {n} {.n} ≤refl = Z
  difference {n} {.(1+ _)} (≤1+ n≤m-1) = 1+ (difference n≤m-1)

  -- basic theorems

  -- the succ operation is injective
  1+inj : ∀{n m} → 1+ n == 1+ m → n == m
  1+inj refl = refl

  1+ap : ∀{n m} → n == m → 1+ n == 1+ m
  1+ap {n} {.n} refl = refl

  1+ap-cp : ∀{n m} → 1+ n ≠ 1+ m → n ≠ m
  1+ap-cp h1 h2 = h1 (1+ap h2)

  1+inj-cp : ∀{n m} → n ≠ m → 1+ n ≠ 1+ m
  1+inj-cp h1 h2 = h1 (1+inj h2)

  -- equality of naturals is decidable. we represent this as computing a
  -- choice of units, with inl <> meaning that the naturals are indeed the
  -- same and inr <> that they are not.
  natEQ : (x y : Nat) → ((x == y) ∨ ((x == y) → ⊥))
  natEQ Z Z = Inl refl
  natEQ Z (1+ y) = Inr (λ ())
  natEQ (1+ x) Z = Inr (λ ())
  natEQ (1+ x) (1+ y) with natEQ x y
  natEQ (1+ x) (1+ .x) | Inl refl = Inl refl
  ... | Inr b = Inr (λ x₁ → b (1+inj x₁))

  0≠1+n : ∀{n} → 0 ≠ 1+ n
  0≠1+n = λ ()

  -- _+_ theorems

  n+Z==n : ∀{n} → n + Z == n
  n+Z==n {Z} = refl
  n+Z==n {1+ n} = 1+ap n+Z==n

  n+1+m==1+n+m : ∀{n m} → n + 1+ m == 1+ (n + m)
  n+1+m==1+n+m {Z} = refl
  n+1+m==1+n+m {1+ n} = 1+ap n+1+m==1+n+m

  n≠n+1+m : ∀{n m} → n ≠ n + 1+ m
  n≠n+1+m {Z} {m} ()
  n≠n+1+m {1+ n} {m} h = 1+inj-cp n≠n+1+m h

  +comm : ∀{a b} → a + b == b + a
  +comm {Z} {b} = ! n+Z==n
  +comm {1+ a} {Z} = 1+ap n+Z==n
  +comm {1+ a} {1+ b} with a + 1+ b | b + 1+ a | n+1+m==1+n+m {a} {b} | n+1+m==1+n+m {b} {a}
  +comm {1+ a} {1+ b} | _ | _ | refl | refl = 1+ap (1+ap (+comm {a}))

  +assc : ∀{a b c} → (a + b) + c == a + (b + c)
  +assc {Z} = refl
  +assc {1+ a} = 1+ap (+assc {a})

  a+n==a+m→n==m : ∀{a n m} → a + n == a + m → n == m
  a+n==a+m→n==m {Z} refl = refl
  a+n==a+m→n==m {1+ a} a+n==a+m = a+n==a+m→n==m (1+inj a+n==a+m)

  n+a==m+a→n==m : ∀{n m a} → n + a == m + a → n == m
  n+a==m+a→n==m {n} {m} {a} n+a==m+a rewrite +comm {n} {a} | +comm {m} {a} = a+n==a+m→n==m n+a==m+a

  +inj : ∀{a1 a2 b} → a1 + b == a2 + b → a1 == a2
  +inj {a1} {a2} {Z} h
    rewrite (n+Z==n {a1}) | (n+Z==n {a2}) = h
  +inj {a1} {a2} {1+ b} h
    rewrite n+1+m==1+n+m {a1} {b} | n+1+m==1+n+m {a2} {b} = +inj (1+inj h)

  +inj-cp : ∀{a1 a2 b} → a1 ≠ a2 → a1 + b ≠ a2 + b
  +inj-cp ne f = ne (+inj f)

  -- even/odd theorems

  even-inj : ∀{m n} → m + m == n + n → m == n
  even-inj {Z} {Z} eq = refl
  even-inj {1+ m} {1+ n} eq rewrite (n+1+m==1+n+m {n} {n}) | (n+1+m==1+n+m {m} {m})
    = 1+ap (even-inj (1+inj (1+inj eq)))

  even-not-odd : ∀{m n} → 1+ (m + m) ≠ (n + n)
  even-not-odd {Z} {1+ n} eq rewrite (n+1+m==1+n+m {n} {n}) = 0≠1+n (1+inj eq)
  even-not-odd {1+ m} {1+ n} eq rewrite (n+1+m==1+n+m {n} {n}) | (n+1+m==1+n+m {m} {m})
    = even-not-odd {m} {n} (1+inj (1+inj eq))

  -- _≤_ theorems

  0≤n : ∀{n} → Z ≤ n
  0≤n {Z} = ≤refl
  0≤n {1+ n} = ≤1+ 0≤n

  1+n≰0 : ∀{n} → 1+ n ≤ Z → ⊥
  1+n≰0 = λ ()

  n≤m→1+n≤1+m : ∀{n m} → n ≤ m → 1+ n ≤ 1+ m
  n≤m→1+n≤1+m {n} {.n} ≤refl = ≤refl
  n≤m→1+n≤1+m {n} {.(1+ _)} (≤1+ h) = ≤1+ (n≤m→1+n≤1+m h)

  n≤m→s+n=m : ∀{n m} → n ≤ m → Σ[ s ∈ Nat ] (s + n == m)
  n≤m→s+n=m ≤refl = Z , refl
  n≤m→s+n=m (≤1+ n≤m)
    with n≤m→s+n=m n≤m
  ... | _ , refl = _ , refl

  1+n≤1+m→n≤m : ∀{n m} → 1+ n ≤ 1+ m → n ≤ m
  1+n≤1+m→n≤m {n} {.n} ≤refl = ≤refl
  1+n≤1+m→n≤m {n} {Z} (≤1+ h) = abort (1+n≰0 h)
  1+n≤1+m→n≤m {n} {1+ m} (≤1+ h) = ≤1+ (1+n≤1+m→n≤m h)

  n+s≤m+s→n≤m : ∀{n m s} → n + s ≤ m + s → n ≤ m
  n+s≤m+s→n≤m {n} {m} {s = Z} n+s≤m+s
    rewrite n+Z==n {n} | n+Z==n {m} = n+s≤m+s
  n+s≤m+s→n≤m {n} {m} {s = 1+ s} n+s≤m+s
    rewrite n+1+m==1+n+m {n} {s} | n+1+m==1+n+m {m} {s}
      = n+s≤m+s→n≤m (1+n≤1+m→n≤m n+s≤m+s)

  1+n≰n : ∀{n} → 1+ n ≤ n → ⊥
  1+n≰n {Z} h = abort (1+n≰0 h)
  1+n≰n {1+ n} h = 1+n≰n (1+n≤1+m→n≤m h)

  ≤total : ∀{n m} → n ≤ m ∨ m ≤ n
  ≤total {Z} {m} = Inl 0≤n
  ≤total {1+ n} {Z} = Inr (≤1+ 0≤n)
  ≤total {1+ n} {1+ m} with ≤total {n} {m}
  ≤total {1+ n} {1+ m} | Inl h = Inl (n≤m→1+n≤1+m h)
  ≤total {1+ n} {1+ m} | Inr h = Inr (n≤m→1+n≤1+m h)

  ≤trans : ∀{a b c} → a ≤ b → b ≤ c → a ≤ c
  ≤trans ≤refl b≤c = b≤c
  ≤trans (≤1+ a≤b) ≤refl = ≤1+ a≤b
  ≤trans (≤1+ a≤b) (≤1+ b≤c) = ≤1+ (≤trans (≤1+ a≤b) b≤c)

  ≤antisym : ∀{n m} → n ≤ m → m ≤ n → n == m
  ≤antisym {n} {.n} ≤refl m≤n = refl
  ≤antisym {n} {.(1+ _)} (≤1+ h1) h2 = abort (1+n≰n (≤trans h2 h1))

  n≤n+m : ∀{n m} → n ≤ n + m
  n≤n+m {n} {Z} with n + Z | n+Z==n {n}
  n≤n+m {_} {Z} | _ | refl = ≤refl
  n≤n+m {n} {1+ m} with n + 1+ m | ! (n+1+m==1+n+m {n} {m})
  n≤n+m {n} {1+ m} | _ | refl = ≤trans n≤n+m (≤1+ ≤refl)

  n≤m+n : ∀{n m} → n ≤ m + n
  n≤m+n {n} {m} rewrite +comm {m} {n} = n≤n+m

  -- _<_ theorems

  n≮0 : ∀{n} → n < Z → ⊥
  n≮0 {Z} (π3 , π4) = π4 refl
  n≮0 {1+ n} (π3 , π4) = 1+n≰0 π3

  n<1+n : ∀{n} → n < 1+ n
  π1 n<1+n = ≤1+ ≤refl
  π2 n<1+n ()

  1+n<1+m→n<m : ∀{n m} → 1+ n < 1+ m → n < m
  π1 (1+n<1+m→n<m (π3 , π4)) = 1+n≤1+m→n≤m π3
  π2 (1+n<1+m→n<m (π3 , π4)) = 1+ap-cp π4

  <trans : ∀{a b c} → a < b → b < c → a < c
  π1 (<trans (π3 , π4) (π5 , π6)) = ≤trans π3 π5
  π2 (<trans (π3 , π4) (≤refl , π6)) = abort (π6 refl)
  π2 (<trans (π3 , π4) (≤1+ π5 , π6)) refl = 1+n≰n (≤trans π3 π5)

  <antisym : ∀{n m} → n < m → m < n → ⊥
  <antisym (n≤m , n≠m) (m≤n , _) = n≠m (≤antisym n≤m m≤n)

  <antirefl : ∀{n} → n < n → ⊥
  <antirefl (_ , ne) = abort (ne refl)

  n<m→n<s+m : ∀{n m s} → n < m → n < s + m
  n<m→n<s+m {s = Z} n<m = n<m
  n<m→n<s+m {s = 1+ s} n<m =
    <trans (n<m→n<s+m {s = s} n<m) n<1+n

  n<m→1+n<1+m : ∀{n m} → n < m → 1+ n < 1+ m
  n<m→1+n<1+m (π3 , π4) = n≤m→1+n≤1+m π3 , 1+inj-cp π4

  n<m→s+1+n=m : ∀{n m} → n < m → Σ[ s ∈ Nat ] (s + 1+ n == m)
  n<m→s+1+n=m (≤refl , ne) = abort (ne refl)
  n<m→s+1+n=m {n = n} (≤1+ n≤m , _)
    with n≤m→s+n=m n≤m
  ... | _ , refl = _ , (n+1+m==1+n+m {m = n})

  0<1+n : ∀{n} → 0 < 1+ n
  0<1+n {Z} = ≤1+ ≤refl , (λ ())
  0<1+n {1+ n} = 0≤n , (λ ())

  1+n≤m→n<m : ∀{n m} → 1+ n ≤ m → n < m
  1+n≤m→n<m ≤refl = n<1+n
  1+n≤m→n<m (≤1+ 1+n≤m) = <trans (1+n≤m→n<m 1+n≤m) n<1+n

  n≤m→n<1+m : ∀{n m} → n ≤ m → n < 1+ m
  n≤m→n<1+m {Z} n≤m = 0<1+n
  n≤m→n<1+m {1+ n} n≤m = n<m→1+n<1+m (1+n≤m→n<m n≤m)

  n<m→1+n≤m : ∀{n m} → n < m → 1+ n ≤ m
  n<m→1+n≤m (≤refl , ne) = abort (ne refl)
  n<m→1+n≤m (≤1+ n≤m , _) = n≤m→1+n≤1+m n≤m

  n<m→n≤m : ∀{n m} → n < m → n ≤ m
  n<m→n≤m n<m = 1+n≤1+m→n≤m (≤1+ (n<m→1+n≤m n<m))

  <dec : (n m : Nat) → n < m ∨ n == m ∨ m < n
  <dec n m with natEQ n m
  ... | Inl refl = Inr (Inl refl)
  ... | Inr ne   with ≤total {n} {m}
  ... | Inl ≤refl     = abort (ne refl)
  ... | Inl (≤1+ n≤m) = Inl (n≤m→n<1+m n≤m)
  ... | Inr ≤refl     = abort (ne refl)
  ... | Inr (≤1+ m≤n) = Inr (Inr (n≤m→n<1+m m≤n))

  <dec-refl : (n : Nat) → <dec n n == Inr (Inl refl)
  <dec-refl n with <dec n n
  <dec-refl n | Inl (_ , ne)       = abort (ne refl)
  <dec-refl n | Inr (Inl refl)     = refl
  <dec-refl n | Inr (Inr (_ , ne)) = abort (ne refl)

  -- difference theorems

  m-n+n==m : ∀{n m} → (n≤m : n ≤ m) → difference n≤m + n == m
  m-n+n==m ≤refl = refl
  m-n+n==m (≤1+ n≤m) = 1+ap (m-n+n==m n≤m)

  n+m-n==m : ∀{n m} → (n≤n+m : n ≤ n + m) → difference n≤n+m == m
  n+m-n==m {n} n≤n+m =
    n+a==m+a→n==m (m-n+n==m n≤n+m · +comm {n})

  a+b==c→a==c-b : ∀{a b c} → a + b == c → (b≤c : b ≤ c) → a == difference b≤c
  a+b==c→a==c-b a+b==c b≤c
    = n+a==m+a→n==m (a+b==c · ! (m-n+n==m b≤c))

  diff-proof-irrelevance : ∀{n m} →
                             (n≤m1 n≤m2 : n ≤ m) →
                             difference n≤m1 == difference n≤m2
  diff-proof-irrelevance ≤refl ≤refl = refl
  diff-proof-irrelevance ≤refl (≤1+ n≤m2) = abort (1+n≰n n≤m2)
  diff-proof-irrelevance (≤1+ n≤m1) ≤refl = abort (1+n≰n n≤m1)
  diff-proof-irrelevance (≤1+ n≤m1) (≤1+ n≤m2) = 1+ap (diff-proof-irrelevance n≤m1 n≤m2)

  m-n==1+m-1+n : ∀{n m} →
                   (n≤m : n ≤ m) →
                   (1+n≤1+m : 1+ n ≤ 1+ m) →
                   difference n≤m == difference 1+n≤1+m
  m-n==1+m-1+n {n} {.n} ≤refl ≤refl = refl
  m-n==1+m-1+n {n} {.n} ≤refl (≤1+ 1+n≤n) = abort (1+n≰n 1+n≤n)
  m-n==1+m-1+n {.(1+ _)} {.(1+ _)} (≤1+ 1+m≤m) ≤refl = abort (1+n≰n 1+m≤m)
  m-n==1+m-1+n {n} {.(1+ _)} (≤1+ n≤m) (≤1+ 1+n≤1+m) = 1+ap (m-n==1+m-1+n n≤m 1+n≤1+m)

  m-n==m+s-n+s : ∀{n m s} →
                   (n≤m : n ≤ m) →
                   (n+s≤m+s : n + s ≤ m + s) →
                   difference n≤m == difference n+s≤m+s
  m-n==m+s-n+s {n} {m} {s = Z} n≤m n+s≤m+s
    rewrite n+Z==n {n} | n+Z==n {m}
      = diff-proof-irrelevance n≤m n+s≤m+s
  m-n==m+s-n+s {n} {m} {s = 1+ s} n≤m n+s≤m+s
    rewrite n+1+m==1+n+m {n} {s} | n+1+m==1+n+m {m} {s}
      = (m-n==m+s-n+s n≤m (1+n≤1+m→n≤m n+s≤m+s)) · (m-n==1+m-1+n (1+n≤1+m→n≤m n+s≤m+s) n+s≤m+s)


{-# OPTIONS --rewriting #-}

open import FFI.Data.Either using (Either; Left; Right)
open import Luau.Type using (Type; nil; number; string; boolean; never; unknown; _⇒_; _∪_; _∩_)
open import Luau.TypeNormalization using (normalize)

module Luau.FunctionTypes where

-- The domain of a normalized type
srcⁿ : Type → Type
srcⁿ (S ⇒ T) = S
srcⁿ (S ∩ T) = srcⁿ S ∪ srcⁿ T
srcⁿ never = unknown
srcⁿ T = never

-- To get the domain of a type, we normalize it first We need to do
-- this, since if we try to use it on non-normalized types, we get
--
-- src(number ∩ string) = src(number) ∪ src(string) = never ∪ never
-- src(never) = unknown
--
-- so src doesn't respect type equivalence.
src : Type → Type
src (S ⇒ T) = S
src T = srcⁿ(normalize T)

-- The codomain of a type
tgt : Type → Type
tgt nil = never
tgt (S ⇒ T) = T
tgt never = never
tgt unknown = unknown
tgt number = never
tgt boolean = never
tgt string = never
tgt (S ∪ T) = (tgt S) ∪ (tgt T)
tgt (S ∩ T) = (tgt S) ∩ (tgt T)