{-# OPTIONS --safe --lossy-unification #-}
module Cubical.Algebra.CommRing.Quotient.Base where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Structure
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Function
open import Cubical.Foundations.Powerset
open import Cubical.Functions.Surjection

open import Cubical.Data.Nat
open import Cubical.Data.FinData
open import Cubical.Data.Sigma using (Σ≡Prop)

open import Cubical.HITs.SetQuotients as SQ renaming (_/_ to _/ₛ_)
open import Cubical.HITs.PropositionalTruncation as PT

open import Cubical.Algebra.CommRing
open import Cubical.Algebra.CommRing.Ideal
open import Cubical.Algebra.CommRing.FGIdeal
open import Cubical.Algebra.CommRing.Kernel
open import Cubical.Algebra.Ring
import Cubical.Algebra.Ring.Quotient as Ring

private
  variable
     ℓ' ℓ'' : Level

module _ (R : CommRing ) (I : IdealsIn R) where
  open CommRingStr (snd R)
  R/I =  R  /ₛ  x y  x - y  (fst I))

  quotientCommRingStr : CommRingStr R/I
  quotientCommRingStr = snd
    (Ring→CommRing
        ((CommRing→Ring R) Ring./ (CommIdeal→Ideal I))
        (elimProp2  _ _  squash/ _ _)
                   λ x y i  [ CommRingStr.·Comm (snd R) x y i ]))

_/_ : (R : CommRing ) (I : IdealsIn R)  CommRing 
fst (R / I) = R/I R I
snd (R / I) = quotientCommRingStr R I

[_]/ : {R : CommRing } {I : IdealsIn R}  (a : fst R)  fst (R / I)
[ a ]/ = SQ.[ a ]

module Coherence (R : CommRing ) (I : IdealsIn R) where
  opaque
    isRingHomCoh : IsRingHom (snd (CommRing→Ring (R / I)))
                              x  x)
                             (snd ((CommRing→Ring R) Ring./ (CommIdeal→Ideal I)))
    IsRingHom.pres0 isRingHomCoh = refl
    IsRingHom.pres1 isRingHomCoh = refl
    IsRingHom.pres+ isRingHomCoh = λ _ _  refl
    IsRingHom.pres· isRingHomCoh = λ _ _  refl
    IsRingHom.pres- isRingHomCoh = λ _  refl
    isRingHomCohInv : IsRingHom (snd ((CommRing→Ring R) Ring./ (CommIdeal→Ideal I)))
                              x  x)
                             (snd (CommRing→Ring (R / I)))
    IsRingHom.pres0 isRingHomCohInv = refl
    IsRingHom.pres1 isRingHomCohInv = refl
    IsRingHom.pres+ isRingHomCohInv = λ _ _  refl
    IsRingHom.pres· isRingHomCohInv = λ _ _  refl
    IsRingHom.pres- isRingHomCohInv = λ _  refl

    0r≡ : RingStr.0r (CommRing→Ring (R / I) .snd)  RingStr.0r ((CommRing→Ring R Ring./ CommIdeal→Ideal I) .snd)
    0r≡ = refl

  ringStr : RingHom (CommRing→Ring (R / I))
                ((CommRing→Ring R) Ring./ (CommIdeal→Ideal I))

  fst ringStr x = x
  (snd ringStr) = isRingHomCoh

  ringStrInv : RingHom ((CommRing→Ring R) Ring./ (CommIdeal→Ideal I))
                   (CommRing→Ring (R / I))

  fst ringStrInv x = x
  (snd ringStrInv) = isRingHomCohInv


open RingHoms

module _ (R : CommRing ) (I : IdealsIn R) where

  quotientHom : CommRingHom R (R / I)
  quotientHom =
    withOpaqueStr $
    RingHom→CommRingHom $
        Coherence.ringStrInv R I
     ∘r Ring.quotientHom (CommRing→Ring R) (CommIdeal→Ideal I)

  quotientHomSurjective : isSurjection (quotientHom .fst)
  quotientHomSurjective = Ring.quotientHomSurjective (CommRing→Ring R) (CommIdeal→Ideal I)

  quotientHomEpi : (S : hSet ℓ')
                    (f g :  R / I    S )
                    f  quotientHom .fst  g  quotientHom .fst
                    f  g
  quotientHomEpi S f g p =
      (Ring.quotientHomEpi
         (CommRing→Ring R) (CommIdeal→Ideal I) S
         f g p)

module Quotient-FGideal-CommRing-Ring
  (R : CommRing )
  (S : Ring ℓ')
  (f : RingHom (CommRing→Ring R) S)
  {n : }
  (v : FinVec  R  n)
  (fnull : (k : Fin n)  f $r v k  RingStr.0r (snd S))
  where

  open RingStr (snd S) using (0r; is-set)

  Iv = generatedIdeal R v

  zeroOnGeneratedIdeal : (x :  R )  x  fst Iv  f $r x  0r
  zeroOnGeneratedIdeal x x∈FGIdeal =
    PT.elim
       _  is-set (f $r x) 0r)
       {(α , isLC)  subst _ (sym isLC) (cancelLinearCombination R S f _ α v fnull)})
      x∈FGIdeal

  inducedHom : RingHom (CommRing→Ring (R / (generatedIdeal _ v))) S
  inducedHom = Ring.UniversalProperty.inducedHom
                 (CommRing→Ring R) (CommIdeal→Ideal Iv) f zeroOnGeneratedIdeal
               ∘r (Coherence.ringStr R Iv)

module Quotient-FGideal-CommRing-CommRing
  (R : CommRing )
  (S : CommRing ℓ')
  (f : CommRingHom R S)
  {n : }
  (v : FinVec  R  n)
  (fnull : (k : Fin n)  f $cr v k  CommRingStr.0r (snd S))
  where

  inducedHom : CommRingHom (R / (generatedIdeal _ v)) S
  inducedHom = RingHom→CommRingHom $
               Quotient-FGideal-CommRing-Ring.inducedHom R (CommRing→Ring S) (CommRingHom→RingHom f) v fnull

module UniversalProperty
  (R S : CommRing )
  (I : IdealsIn R)
  (f : CommRingHom R S)
  (I⊆ker : (x :  R )  x  fst I  fst f x  CommRingStr.0r (snd S))
  where

  inducedHom : CommRingHom (R / I) S
  inducedHom =
    withOpaqueStr $
    RingHom→CommRingHom $
       Ring.UniversalProperty.inducedHom
         (CommRing→Ring R)
         (CommIdeal→Ideal I)
         (CommRingHom→RingHom f)
         I⊆ker
      ∘r Coherence.ringStr R I

  opaque
    isSolution : inducedHom ∘cr quotientHom R I  f
    isSolution = Σ≡Prop  _  isPropIsCommRingHom _ _ _)
                       (cong fst (Ring.UniversalProperty.solution
                                        (CommRing→Ring R)
                                        (CommIdeal→Ideal I)
                                        (CommRingHom→RingHom f)
                                        I⊆ker))

  opaque
    isUnique : (ψ : CommRingHom (R / I) S)  (ψIsSolution : ψ .fst  quotientHom R I .fst  f .fst)
               ψ  inducedHom
    isUnique ψ ψIsSolution =
      Σ≡Prop  _  isPropIsCommRingHom _ _ _)
             (cong fst
                   (Ring.UniversalProperty.unique'
                       (CommRing→Ring R)
                       (CommIdeal→Ideal I)
                       (CommRingHom→RingHom f)
                       I⊆ker
                       (CommRingHom→RingHom ψ)
                       ψIsSolution))


module _ {R : CommRing } (I : IdealsIn R) where
  open CommRingStr ⦃...⦄
  private
    π = quotientHom R I
    instance _ = snd R
             _ = snd (R / I)

  opaque
    kernel≡I : kernelIdeal R (R / I) π  I
    kernel≡I =
      Σ≡Prop (CommIdeal.isPropIsCommIdeal _)
            (funExt
             λ x  Σ≡Prop  _  isPropIsProp)
                     let reason = cong  y  π .fst x  y) (Coherence.0r≡ R I)
                     in (π .fst x  RingStr.0r (CommRing→Ring (R / I) .snd)                       ≡⟨ reason 
                         π .fst x  RingStr.0r ((CommRing→Ring R Ring./ CommIdeal→Ideal I) .snd) ))
      cong Ideal→CommIdeal (Ring.kernel≡I (CommIdeal→Ideal I))

  zeroOnIdeal : (x :  R )  x  fst I  fst π x  0r
  zeroOnIdeal x x∈I = subst  P  fst ((fst P) x)) (sym kernel≡I) x∈I