/-
Copyright (c) 2018 Reid Barton. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Reid Barton

! This file was ported from Lean 3 source module topology.compact_open
! leanprover-community/mathlib commit 4c19a16e4b705bf135cf9a80ac18fcc99c438514
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Topology.ContinuousFunction.Basic
import Mathlib.Topology.Homeomorph
import Mathlib.Topology.SubsetProperties
import Mathlib.Topology.Maps

/-!
# The compact-open topology

In this file, we define the compact-open topology on the set of continuous maps between two
topological spaces.

## Main definitions

* `CompactOpen` is the compact-open topology on `C(α, β)`. It is declared as an instance.
* `ContinuousMap.coev` is the coevaluation map `β → C(α, β × α)`. It is always continuous.
* `ContinuousMap.curry` is the currying map `C(α × β, γ) → C(α, C(β, γ))`. This map always exists
  and it is continuous as long as `α × β` is locally compact.
* `ContinuousMap.uncurry` is the uncurrying map `C(α, C(β, γ)) → C(α × β, γ)`. For this map to
  exist, we need `β` to be locally compact. If `α` is also locally compact, then this map is
  continuous.
* `Homeomorph.curry` combines the currying and uncurrying operations into a homeomorphism
  `C(α × β, γ) ≃ₜ C(α, C(β, γ))`. This homeomorphism exists if `α` and `β` are locally compact.

## Tags

compact-open, curry, function space
-/

open Set

open Topology

namespace ContinuousMap

section CompactOpen

variable {α : Type _} {β : Type _} {γ : Type _}

variable [TopologicalSpace α] [TopologicalSpace β] [TopologicalSpace γ]

/-- A generating set for the compact-open topology (when `s` is compact and `u` is open). -/
def CompactOpen.gen (s : Set α) (u : Set β) : Set C(α, β) :=
  { f | f '' s ⊆ u }
#align continuous_map.compact_open.gen ContinuousMap.CompactOpen.gen

@[simp]
theorem gen_empty (u : Set β) : CompactOpen.gen (∅ : Set α) u = Set.univ :=
  Set.ext fun f => iff_true_intro ((congr_arg (· ⊆ u) (image_empty f)).mpr u.empty_subset)
#align continuous_map.gen_empty ContinuousMap.gen_empty

@[simp]
theorem gen_univ (s : Set α) : CompactOpen.gen s (Set.univ : Set β) = Set.univ :=
  Set.ext fun f => iff_true_intro (f '' s).subset_univ
#align continuous_map.gen_univ ContinuousMap.gen_univ

@[simp]
theorem gen_inter (s : Set α) (u v : Set β) :
    CompactOpen.gen s (u ∩ v) = CompactOpen.gen s u ∩ CompactOpen.gen s v :=
  Set.ext fun _ => subset_inter_iff
#align continuous_map.gen_inter ContinuousMap.gen_inter

@[simp]
theorem gen_union (s t : Set α) (u : Set β) :
    CompactOpen.gen (s ∪ t) u = CompactOpen.gen s u ∩ CompactOpen.gen t u :=
  Set.ext fun f => (iff_of_eq (congr_arg (· ⊆ u) (image_union f s t))).trans union_subset_iff
#align continuous_map.gen_union ContinuousMap.gen_union

theorem gen_empty_right {s : Set α} (h : s.Nonempty) : CompactOpen.gen s (∅ : Set β) = ∅ :=
  eq_empty_of_forall_not_mem fun _ => (h.image _).not_subset_empty
#align continuous_map.gen_empty_right ContinuousMap.gen_empty_right

-- The compact-open topology on the space of continuous maps α → β.
instance compactOpen : TopologicalSpace C(α, β) :=
  TopologicalSpace.generateFrom
    { m | ∃ (s : Set α)(_ : IsCompact s)(u : Set β)(_ : IsOpen u), m = CompactOpen.gen s u }
#align continuous_map.compact_open ContinuousMap.compactOpen

protected theorem isOpen_gen {s : Set α} (hs : IsCompact s) {u : Set β} (hu : IsOpen u) :
    IsOpen (CompactOpen.gen s u) :=
  TopologicalSpace.GenerateOpen.basic _ (by dsimp [mem_setOf_eq]; tauto)
#align continuous_map.is_open_gen ContinuousMap.isOpen_gen

section Functorial

variable (g : C(β, γ))

private theorem preimage_gen {s : Set α} (_ : IsCompact s) {u : Set γ} (_ : IsOpen u) :
    ContinuousMap.comp g ⁻¹' CompactOpen.gen s u = CompactOpen.gen s (g ⁻¹' u) := by
  ext ⟨f, _⟩
  change g ∘ f '' s ⊆ u ↔ f '' s ⊆ g ⁻¹' u
  rw [image_comp, image_subset_iff]
--#align continuous_map.preimage_gen ContinuousMap.preimage_gen

/-- C(α, -) is a functor. -/
theorem continuous_comp : Continuous (ContinuousMap.comp g : C(α, β) → C(α, γ)) :=
  continuous_generateFrom fun m ⟨s, hs, u, hu, hm⟩ => by
    rw [hm, preimage_gen g hs hu]; exact ContinuousMap.isOpen_gen hs (hu.preimage g.2)
#align continuous_map.continuous_comp ContinuousMap.continuous_comp

variable (f : C(α, β))

private theorem image_gen {s : Set α} (_ : IsCompact s) {u : Set γ} (_ : IsOpen u) :
    (fun g : C(β, γ) => g.comp f) ⁻¹' CompactOpen.gen s u = CompactOpen.gen (f '' s) u := by
  ext ⟨g, _⟩
  change g ∘ f '' s ⊆ u ↔ g '' (f '' s) ⊆ u
  rw [Set.image_comp]
--#align continuous_map.image_gen continuous_map.image_gen

/-- C(-, γ) is a functor. -/
theorem continuous_comp_left : Continuous (fun g => g.comp f : C(β, γ) → C(α, γ)) :=
  continuous_generateFrom fun m ⟨s, hs, u, hu, hm⟩ =>
    by
    rw [hm, image_gen f hs hu]
    exact ContinuousMap.isOpen_gen (hs.image f.2) hu
#align continuous_map.continuous_comp_left ContinuousMap.continuous_comp_left

/-- Composition is a continuous map from `C(α, β) × C(β, γ)` to `C(α, γ)`, provided that `β` is
  locally compact. This is Prop. 9 of Chap. X, §3, №. 4 of Bourbaki's *Topologie Générale*. -/
theorem continuous_comp' [LocallyCompactSpace β] :
    Continuous fun x : C(α, β) × C(β, γ) => x.2.comp x.1 :=
  continuous_generateFrom
    (by
      rintro M ⟨K, hK, U, hU, rfl⟩
      conv =>
        congr
        rw [CompactOpen.gen, preimage_setOf_eq]
        --congr
        ext; dsimp [setOf]
        rw [image_comp, image_subset_iff]
      rw [isOpen_iff_forall_mem_open]
      rintro ⟨φ₀, ψ₀⟩ H
      obtain ⟨L, hL, hKL, hLU⟩ := exists_compact_between (hK.image φ₀.2) (hU.preimage ψ₀.2) H
      use { φ : C(α, β) | φ '' K ⊆ interior L } ×ˢ { ψ : C(β, γ) | ψ '' L ⊆ U }
      -- porting note: typing hint `: φ '' K ⊆ interior L` wasn't previously required
      use fun ⟨φ, ψ⟩ ⟨(hφ : φ '' K ⊆ interior L), hψ⟩ =>
        subset_trans hφ (interior_subset.trans <| image_subset_iff.mp hψ)
      use (ContinuousMap.isOpen_gen hK isOpen_interior).prod (ContinuousMap.isOpen_gen hL hU)
      exact mem_prod.mpr ⟨hKL, image_subset_iff.mpr hLU⟩)
#align continuous_map.continuous_comp' ContinuousMap.continuous_comp'

theorem continuous.comp' {X : Type _} [TopologicalSpace X] [LocallyCompactSpace β] {f : X → C(α, β)}
    {g : X → C(β, γ)} (hf : Continuous f) (hg : Continuous g) :
    Continuous fun x => (g x).comp (f x) :=
  continuous_comp'.comp (hf.prod_mk hg : Continuous fun x => (f x, g x))
#align continuous_map.continuous.comp' ContinuousMap.continuous.comp'

end Functorial

section Ev

/-- The evaluation map `C(α, β) × α → β` is continuous if `α` is locally compact.

See also `ContinuousMap.continuous_eval` -/
theorem continuous_eval' [LocallyCompactSpace α] : Continuous fun p : C(α, β) × α => p.1 p.2 :=
  continuous_iff_continuousAt.mpr fun ⟨f, x⟩ n hn =>
    let ⟨v, vn, vo, fxv⟩ := mem_nhds_iff.mp hn
    have : v ∈ 𝓝 (f x) := IsOpen.mem_nhds vo fxv
    let ⟨s, hs, sv, sc⟩ :=
      LocallyCompactSpace.local_compact_nhds x (f ⁻¹' v) (f.continuous.tendsto x this)
    let ⟨u, us, uo, xu⟩ := mem_nhds_iff.mp hs
    show (fun p : C(α, β) × α => p.1 p.2) ⁻¹' n ∈ 𝓝 (f, x) from
      let w := CompactOpen.gen s v ×ˢ u
      have : w ⊆ (fun p : C(α, β) × α => p.1 p.2) ⁻¹' n := fun ⟨f', x'⟩ ⟨hf', hx'⟩ =>
        vn <| hf' <| mem_image_of_mem f' (us hx')
        --Porting note: The following `calc` block fails here.
        --calc
        --  f' x' ∈ f' '' s := mem_image_of_mem f' (us hx')
        --  _ ⊆ v := hf'
        --  _ ⊆ n := vn

      have : IsOpen w := (ContinuousMap.isOpen_gen sc vo).prod uo
      have : (f, x) ∈ w := ⟨image_subset_iff.mpr sv, xu⟩
      mem_nhds_iff.mpr ⟨w, by assumption, by assumption, by assumption⟩
#align continuous_map.continuous_eval' ContinuousMap.continuous_eval'

/-- See also `ContinuousMap.continuous_eval_const` -/
theorem continuous_eval_const' [LocallyCompactSpace α] (a : α) :
    Continuous fun f : C(α, β) => f a :=
  continuous_eval'.comp (continuous_id.prod_mk continuous_const)
#align continuous_map.continuous_eval_const' ContinuousMap.continuous_eval_const'

/-- See also `ContinuousMap.continuous_coe` -/
theorem continuous_coe' [LocallyCompactSpace α] : @Continuous C(α, β) (α → β) _ _ (↑) :=
  continuous_pi continuous_eval_const'
#align continuous_map.continuous_coe' ContinuousMap.continuous_coe'

instance [T2Space β] : T2Space C(α, β) :=
  ⟨by
    intro f₁ f₂ h
    obtain ⟨x, hx⟩ := not_forall.mp (mt (FunLike.ext f₁ f₂) h)
    obtain ⟨u, v, hu, hv, hxu, hxv, huv⟩ := t2_separation hx
    refine'
      ⟨CompactOpen.gen {x} u, CompactOpen.gen {x} v,
        ContinuousMap.isOpen_gen isCompact_singleton hu,
        ContinuousMap.isOpen_gen isCompact_singleton hv, _, _, _⟩
    · rwa [CompactOpen.gen, mem_setOf_eq, image_singleton, singleton_subset_iff]
    · rwa [CompactOpen.gen, mem_setOf_eq, image_singleton, singleton_subset_iff]
    · rw [disjoint_iff_inter_eq_empty, ← gen_inter, huv.inter_eq,
        gen_empty_right (singleton_nonempty _)]⟩

end Ev

section InfInduced

theorem compactOpen_le_induced (s : Set α) :
    (ContinuousMap.compactOpen : TopologicalSpace C(α, β)) ≤
      TopologicalSpace.induced (ContinuousMap.restrict s) ContinuousMap.compactOpen := by
  simp only [induced_generateFrom_eq, ContinuousMap.compactOpen]
  apply TopologicalSpace.generateFrom_anti
  rintro b ⟨a, ⟨c, hc, u, hu, rfl⟩, rfl⟩
  refine' ⟨(↑) '' c, hc.image continuous_subtype_val, u, hu, _⟩
  ext f
  simp only [CompactOpen.gen, mem_setOf_eq, mem_preimage, ContinuousMap.coe_restrict]
  rw [image_comp f ((↑) : s → α)]
#align continuous_map.compact_open_le_induced ContinuousMap.compactOpen_le_induced

/-- The compact-open topology on `C(α, β)` is equal to the infimum of the compact-open topologies
on `C(s, β)` for `s` a compact subset of `α`.  The key point of the proof is that the union of the
compact subsets of `α` is equal to the union of compact subsets of the compact subsets of `α`. -/
theorem compactOpen_eq_infₛ_induced :
    (ContinuousMap.compactOpen : TopologicalSpace C(α, β)) =
      ⨅ (s : Set α) (_hs : IsCompact s),
        TopologicalSpace.induced (ContinuousMap.restrict s) ContinuousMap.compactOpen := by
  refine' le_antisymm _ _
  · refine' le_infᵢ₂ _
    exact fun s _ => compactOpen_le_induced s
  simp only [← generateFrom_unionᵢ, induced_generateFrom_eq, ContinuousMap.compactOpen]
  apply TopologicalSpace.generateFrom_anti
  rintro _ ⟨s, hs, u, hu, rfl⟩
  rw [mem_unionᵢ₂]
  refine' ⟨s, hs, _, ⟨univ, isCompact_iff_isCompact_univ.mp hs, u, hu, rfl⟩, _⟩
  ext f
  simp only [CompactOpen.gen, mem_setOf_eq, mem_preimage, ContinuousMap.coe_restrict]
  rw [image_comp f ((↑) : s → α)]
  simp
#align continuous_map.compact_open_eq_Inf_induced ContinuousMap.compactOpen_eq_infₛ_induced

/-- For any subset `s` of `α`, the restriction of continuous functions to `s` is continuous as a
function from `C(α, β)` to `C(s, β)` with their respective compact-open topologies. -/

theorem nhds_compactOpen_eq_infₛ_nhds_induced (f : C(α, β)) :
    𝓝 f = ⨅ (s) (hs : IsCompact s), (𝓝 (f.restrict s)).comap (ContinuousMap.restrict s) := by
  rw [compactOpen_eq_infₛ_induced]
  simp [nhds_infᵢ, nhds_induced]
#align continuous_map.nhds_compact_open_eq_Inf_nhds_induced ContinuousMap.nhds_compactOpen_eq_infₛ_nhds_induced

theorem tendsto_compactOpen_restrict {ι : Type _} {l : Filter ι} {F : ι → C(α, β)} {f : C(α, β)}
    (hFf : Filter.Tendsto F l (𝓝 f)) (s : Set α) :
    Filter.Tendsto (fun i => (F i).restrict s) l (𝓝 (f.restrict s)) :=
  (continuous_restrict s).continuousAt.tendsto.comp hFf
#align continuous_map.tendsto_compact_open_restrict ContinuousMap.tendsto_compactOpen_restrict

theorem tendsto_compactOpen_iff_forall {ι : Type _} {l : Filter ι} (F : ι → C(α, β)) (f : C(α, β)) :
    Filter.Tendsto F l (𝓝 f) ↔
      ∀ (s) (hs : IsCompact s), Filter.Tendsto (fun i => (F i).restrict s) l (𝓝 (f.restrict s)) :=
  by
    rw [compactOpen_eq_infₛ_induced]
    simp [nhds_infᵢ, nhds_induced, Filter.tendsto_comap_iff, Function.comp]
#align continuous_map.tendsto_compact_open_iff_forall ContinuousMap.tendsto_compactOpen_iff_forall

/-- A family `F` of functions in `C(α, β)` converges in the compact-open topology, if and only if
it converges in the compact-open topology on each compact subset of `α`. -/
theorem exists_tendsto_compactOpen_iff_forall [LocallyCompactSpace α] [T2Space α] [T2Space β]
    {ι : Type _} {l : Filter ι} [Filter.NeBot l] (F : ι → C(α, β)) :
    (∃ f, Filter.Tendsto F l (𝓝 f)) ↔
      ∀ (s : Set α) (hs : IsCompact s), ∃ f, Filter.Tendsto (fun i => (F i).restrict s) l (𝓝 f) :=
  by
  constructor
  · rintro ⟨f, hf⟩ s _
    exact ⟨f.restrict s, tendsto_compactOpen_restrict hf s⟩
  · intro h
    choose f hf using h
    -- By uniqueness of limits in a `t2_space`, since `fun i ↦ F i x` tends to both `f s₁ hs₁ x` and
    -- `f s₂ hs₂ x`, we have `f s₁ hs₁ x = f s₂ hs₂ x`
    have h :
      ∀ (s₁) (hs₁ : IsCompact s₁) (s₂) (hs₂ : IsCompact s₂) (x : α) (hxs₁ : x ∈ s₁) (hxs₂ : x ∈ s₂),
        f s₁ hs₁ ⟨x, hxs₁⟩ = f s₂ hs₂ ⟨x, hxs₂⟩ :=
      by
      rintro s₁ hs₁ s₂ hs₂ x hxs₁ hxs₂
      haveI := isCompact_iff_compactSpace.mp hs₁
      haveI := isCompact_iff_compactSpace.mp hs₂
      have h₁ := (continuous_eval_const' (⟨x, hxs₁⟩ : s₁)).continuousAt.tendsto.comp (hf s₁ hs₁)
      have h₂ := (continuous_eval_const' (⟨x, hxs₂⟩ : s₂)).continuousAt.tendsto.comp (hf s₂ hs₂)
      exact tendsto_nhds_unique h₁ h₂
    -- So glue the `f s hs` together and prove that this glued function `f₀` is a limit on each
    -- compact set `s`
    have hs : ∀ x : α, ∃ (s : _), IsCompact s ∧ s ∈ 𝓝 x :=
      by
      intro x
      obtain ⟨s, hs, hs'⟩ := exists_compact_mem_nhds x
      exact ⟨s, hs, hs'⟩
    refine ⟨liftCover' _ _ h hs, ?_⟩
    rw [tendsto_compactOpen_iff_forall]
    intro s hs
    rw [liftCover_restrict']
    exact hf s hs
#align continuous_map.exists_tendsto_compact_open_iff_forall ContinuousMap.exists_tendsto_compactOpen_iff_forall

end InfInduced

section Coev

variable (α β)

/-- The coevaluation map `β → C(α, β × α)` sending a point `x : β` to the continuous function
on `α` sending `y` to `(x, y)`. -/
def coev (b : β) : C(α, β × α) :=
  ⟨Prod.mk b, continuous_const.prod_mk continuous_id⟩
#align continuous_map.coev ContinuousMap.coev

variable {α β}

theorem image_coev {y : β} (s : Set α) : coev α β y '' s = ({y} : Set β) ×ˢ s := by
  aesop
#align continuous_map.image_coev ContinuousMap.image_coev

-- The coevaluation map β → C(α, β × α) is continuous (always).
theorem continuous_coev : Continuous (coev α β) :=
  continuous_generateFrom <| by
    rintro _ ⟨s, sc, u, uo, rfl⟩
    rw [isOpen_iff_forall_mem_open]
    intro y hy
    have hy' : (↑(coev α β y) '' s ⊆ u) := hy
    -- porting notes: was below
    --change coev α β y '' s ⊆ u at hy
    rw [image_coev s] at hy'
    rcases generalized_tube_lemma isCompact_singleton sc uo hy' with ⟨v, w, vo, _, yv, sw, vwu⟩
    refine' ⟨v, _, vo, singleton_subset_iff.mp yv⟩
    intro y' hy'
    change coev α β y' '' s ⊆ u
    rw [image_coev s]
    exact (prod_mono (singleton_subset_iff.mpr hy') sw).trans vwu
#align continuous_map.continuous_coev ContinuousMap.continuous_coev

end Coev

section Curry

/-- Auxiliary definition, see `ContinuousMap.curry` and `Homeomorph.curry`. -/
def curry' (f : C(α × β, γ)) (a : α) : C(β, γ) :=
  ⟨Function.curry f a, Continuous.comp f.2 (continuous_const.prod_mk continuous_id)⟩
  -- Porting note: proof was `by continuity`
#align continuous_map.curry' ContinuousMap.curry'

/-- If a map `α × β → γ` is continuous, then its curried form `α → C(β, γ)` is continuous. -/
theorem continuous_curry' (f : C(α × β, γ)) : Continuous (curry' f) :=
  have hf : curry' f = ContinuousMap.comp f ∘ coev _ _ := by ext; rfl
  hf ▸ Continuous.comp (continuous_comp f) continuous_coev
#align continuous_map.continuous_curry' ContinuousMap.continuous_curry'

/-- To show continuity of a map `α → C(β, γ)`, it suffices to show that its uncurried form
    `α × β → γ` is continuous. -/
theorem continuous_of_continuous_uncurry (f : α → C(β, γ))
    (h : Continuous (Function.uncurry fun x y => f x y)) : Continuous f :=
  continuous_curry' ⟨_, h⟩
#align continuous_map.continuous_of_continuous_uncurry ContinuousMap.continuous_of_continuous_uncurry

/-- The curried form of a continuous map `α × β → γ` as a continuous map `α → C(β, γ)`.
    If `a × β` is locally compact, this is continuous. If `α` and `β` are both locally
    compact, then this is a homeomorphism, see `homeomorph.curry`. -/
def curry (f : C(α × β, γ)) : C(α, C(β, γ)) :=
  ⟨_, continuous_curry' f⟩
#align continuous_map.curry ContinuousMap.curry

@[simp]
theorem curry_apply (f : C(α × β, γ)) (a : α) (b : β) : f.curry a b = f (a, b) :=
  rfl
#align continuous_map.curry_apply ContinuousMap.curry_apply

/-- The currying process is a continuous map between function spaces. -/
theorem continuous_curry [LocallyCompactSpace (α × β)] :
    Continuous (curry : C(α × β, γ) → C(α, C(β, γ))) := by
  apply continuous_of_continuous_uncurry
  apply continuous_of_continuous_uncurry
  rw [← (Homeomorph.prodAssoc _ _ _).symm.comp_continuous_iff']
  exact continuous_eval'
#align continuous_map.continuous_curry ContinuousMap.continuous_curry

/-- The uncurried form of a continuous map `α → C(β, γ)` is a continuous map `α × β → γ`. -/
theorem continuous_uncurry_of_continuous [LocallyCompactSpace β] (f : C(α, C(β, γ))) :
    Continuous (Function.uncurry fun x y => f x y) :=
  continuous_eval'.comp <| f.continuous.prod_map continuous_id
#align continuous_map.continuous_uncurry_of_continuous ContinuousMap.continuous_uncurry_of_continuous

/-- The uncurried form of a continuous map `α → C(β, γ)` as a continuous map `α × β → γ` (if `β` is
    locally compact). If `α` is also locally compact, then this is a homeomorphism between the two
    function spaces, see `homeomorph.curry`. -/
@[simps]
def uncurry [LocallyCompactSpace β] (f : C(α, C(β, γ))) : C(α × β, γ) :=
  ⟨_, continuous_uncurry_of_continuous f⟩
#align continuous_map.uncurry ContinuousMap.uncurry

/-- The uncurrying process is a continuous map between function spaces. -/
theorem continuous_uncurry [LocallyCompactSpace α] [LocallyCompactSpace β] :
    Continuous (uncurry : C(α, C(β, γ)) → C(α × β, γ)) := by
  apply continuous_of_continuous_uncurry
  rw [← (Homeomorph.prodAssoc _ _ _).comp_continuous_iff']
  apply continuous_eval'.comp (continuous_eval'.prod_map continuous_id)
#align continuous_map.continuous_uncurry ContinuousMap.continuous_uncurry

/-- The family of constant maps: `β → C(α, β)` as a continuous map. -/
def const' : C(β, C(α, β)) :=
  curry ⟨Prod.fst, continuous_fst⟩
#align continuous_map.const' ContinuousMap.const'

@[simp]
theorem coe_const' : (const' : β → C(α, β)) = const α :=
  rfl
#align continuous_map.coe_const' ContinuousMap.coe_const'

theorem continuous_const' : Continuous (const α : β → C(α, β)) :=
  const'.continuous
#align continuous_map.continuous_const' ContinuousMap.continuous_const'

end Curry

end CompactOpen

end ContinuousMap

open ContinuousMap

namespace Homeomorph

variable {α : Type _} {β : Type _} {γ : Type _}

variable [TopologicalSpace α] [TopologicalSpace β] [TopologicalSpace γ]

/-- Currying as a homeomorphism between the function spaces `C(α × β, γ)` and `C(α, C(β, γ))`. -/
def curry [LocallyCompactSpace α] [LocallyCompactSpace β] : C(α × β, γ) ≃ₜ C(α, C(β, γ)) :=
  ⟨⟨ContinuousMap.curry, uncurry, by intro; ext; rfl, by intro; ext; rfl⟩,
    continuous_curry, continuous_uncurry⟩
#align homeomorph.curry Homeomorph.curry

/-- If `α` has a single element, then `β` is homeomorphic to `C(α, β)`. -/
def continuousMapOfUnique [Unique α] : β ≃ₜ C(α, β)
    where
  toFun := const α
  invFun f := f default
  left_inv a := rfl
  right_inv f := by
    ext a
    rw [Unique.eq_default a]
    rfl
  continuous_toFun := continuous_const'
  continuous_invFun := continuous_eval'.comp (continuous_id.prod_mk continuous_const)
#align homeomorph.continuous_map_of_unique Homeomorph.continuousMapOfUnique

@[simp]
theorem continuousMapOfUnique_apply [Unique α] (b : β) (a : α) : continuousMapOfUnique b a = b :=
  rfl
#align homeomorph.continuous_map_of_unique_apply Homeomorph.continuousMapOfUnique_apply

@[simp]
theorem continuousMapOfUnique_symm_apply [Unique α] (f : C(α, β)) :
    continuousMapOfUnique.symm f = f default :=
  rfl
#align homeomorph.continuous_map_of_unique_symm_apply Homeomorph.continuousMapOfUnique_symm_apply

end Homeomorph

section QuotientMap

variable {X₀ X Y Z : Type _} [TopologicalSpace X₀] [TopologicalSpace X] [TopologicalSpace Y]
  [TopologicalSpace Z] [LocallyCompactSpace Y] {f : X₀ → X}

theorem QuotientMap.continuous_lift_prod_left (hf : QuotientMap f) {g : X × Y → Z}
    (hg : Continuous fun p : X₀ × Y => g (f p.1, p.2)) : Continuous g := by
  let Gf : C(X₀, C(Y, Z)) := ContinuousMap.curry ⟨_, hg⟩
  have h : ∀ x : X, Continuous fun y => g (x, y) :=
    by
    intro x
    obtain ⟨x₀, rfl⟩ := hf.surjective x
    exact (Gf x₀).continuous
  let G : X → C(Y, Z) := fun x => ⟨_, h x⟩
  have : Continuous G := by
    rw [hf.continuous_iff]
    exact Gf.continuous
  exact ContinuousMap.continuous_uncurry_of_continuous ⟨G, this⟩
#align quotient_map.continuous_lift_prod_left QuotientMap.continuous_lift_prod_left

theorem QuotientMap.continuous_lift_prod_right (hf : QuotientMap f) {g : Y × X → Z}
    (hg : Continuous fun p : Y × X₀ => g (p.1, f p.2)) : Continuous g := by
  have : Continuous fun p : X₀ × Y => g ((Prod.swap p).1, f (Prod.swap p).2) :=
    hg.comp continuous_swap
  have : Continuous fun p : X₀ × Y => (g ∘ Prod.swap) (f p.1, p.2) := this
  exact (hf.continuous_lift_prod_left this).comp continuous_swap
#align quotient_map.continuous_lift_prod_right QuotientMap.continuous_lift_prod_right

end QuotientMap


/-
Copyright (c) 2021 Yaël Dillies. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yaël Dillies
-/
import data.finset.lattice

/-!
# Relations holding pairwise on finite sets

In this file we prove a few results about the interaction of `set.pairwise_disjoint` and `finset`.
-/

open finset

variables {α ι ι' : Type*}

lemma finset.pairwise_disjoint_range_singleton [decidable_eq α] :
  (set.range (singleton : α → finset α)).pairwise_disjoint id :=
begin
  rintro _ ⟨a, rfl⟩ _ ⟨b, rfl⟩ h,
  exact disjoint_singleton.2 (ne_of_apply_ne _ h),
end

namespace set

lemma pairwise_disjoint.elim_finset [decidable_eq α] {s : set ι} {f : ι → finset α}
  (hs : s.pairwise_disjoint f) {i j : ι} (hi : i ∈ s) (hj : j ∈ s) (a : α) (hai : a ∈ f i)
  (haj : a ∈ f j) :
  i = j :=
hs.elim hi hj (finset.not_disjoint_iff.2 ⟨a, hai, haj⟩)

variables [lattice α] [order_bot α]

/-- Bind operation for `set.pairwise_disjoint`. In a complete lattice, you can use
`set.pairwise_disjoint.bUnion`. -/
lemma pairwise_disjoint.bUnion_finset {s : set ι'} {g : ι' → finset ι} {f : ι → α}
  (hs : s.pairwise_disjoint (λ i' : ι', (g i').sup f))
  (hg : ∀ i ∈ s, (g i : set ι).pairwise_disjoint f) :
  (⋃ i ∈ s, ↑(g i)).pairwise_disjoint f :=
begin
  rintro a ha b hb hab,
  simp_rw set.mem_Union at ha hb,
  obtain ⟨c, hc, ha⟩ := ha,
  obtain ⟨d, hd, hb⟩ := hb,
  obtain hcd | hcd := eq_or_ne (g c) (g d),
  { exact hg d hd (by rwa hcd at ha) hb hab },
  { exact (hs hc hd (ne_of_apply_ne _ hcd)).mono (finset.le_sup ha) (finset.le_sup hb) }
end

end set


/-
Copyright (c) 2022 Eric Wieser. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Eric Wieser

! This file was ported from Lean 3 source module ring_theory.congruence
! leanprover-community/mathlib commit cc70d9141824ea8982d1562ce009952f2c3ece30
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.GroupRingAction.Basic
import Mathbin.Algebra.Hom.Ring
import Mathbin.Algebra.Ring.InjSurj
import Mathbin.GroupTheory.Congruence

/-!
# Congruence relations on rings

> THIS FILE IS SYNCHRONIZED WITH MATHLIB4.
> Any changes to this file require a corresponding PR to mathlib4.

This file defines congruence relations on rings, which extend `con` and `add_con` on monoids and
additive monoids.

Most of the time you likely want to use the `ideal.quotient` API that is built on top of this.

## Main Definitions

* `ring_con R`: the type of congruence relations respecting `+` and `*`.
* `ring_con_gen r`: the inductively defined smallest ring congruence relation containing a given
  binary relation.

## TODO

* Use this for `ring_quot` too.
* Copy across more API from `con` and `add_con` in `group_theory/congruence.lean`, such as:
  * The `complete_lattice` structure.
  * The `con_gen_eq` lemma, stating that
    `ring_con_gen r = Inf {s : ring_con M | ∀ x y, r x y → s x y}`.
-/

#print RingCon /-
/- Note: we can't extend both `add_con R` and `mul_con R` in Lean 3 due to interactions between old-
and new-style structures. We can revisit this in Lean 4. (After and not during the port!) -/
/-- A congruence relation on a type with an addition and multiplication is an equivalence relation
which preserves both. -/
structure RingCon (R : Type _) [Add R] [Mul R] extends Setoid R where
  add' : ∀ {w x y z}, r w x → r y z → r (w + y) (x + z)
  mul' : ∀ {w x y z}, r w x → r y z → r (w * y) (x * z)
#align ring_con RingCon
-/

variable {α R : Type _}

#print RingConGen.Rel /-
/-- The inductively defined smallest ring congruence relation containing a given binary
    relation. -/
inductive RingConGen.Rel [Add R] [Mul R] (r : R → R → Prop) : R → R → Prop
  | of : ∀ x y, r x y → RingConGen.Rel x y
  | refl : ∀ x, RingConGen.Rel x x
  | symm : ∀ {x y}, RingConGen.Rel x y → RingConGen.Rel y x
  | trans : ∀ {x y z}, RingConGen.Rel x y → RingConGen.Rel y z → RingConGen.Rel x z
  | add : ∀ {w x y z}, RingConGen.Rel w x → RingConGen.Rel y z → RingConGen.Rel (w + y) (x + z)
  | mul : ∀ {w x y z}, RingConGen.Rel w x → RingConGen.Rel y z → RingConGen.Rel (w * y) (x * z)
#align ring_con_gen.rel RingConGen.Rel
-/

#print ringConGen /-
/-- The inductively defined smallest ring congruence relation containing a given binary
    relation. -/
def ringConGen [Add R] [Mul R] (r : R → R → Prop) : RingCon R
    where
  R := RingConGen.Rel r
  iseqv := ⟨RingConGen.Rel.refl, @RingConGen.Rel.symm _ _ _ _, @RingConGen.Rel.trans _ _ _ _⟩
  add' _ _ _ _ := RingConGen.Rel.add
  mul' _ _ _ _ := RingConGen.Rel.mul
#align ring_con_gen ringConGen
-/

namespace RingCon

section Basic

variable [Add R] [Mul R] (c : RingCon R)

#print RingCon.toAddCon /-
/-- Every `ring_con` is also an `add_con` -/
def toAddCon : AddCon R :=
  { c with }
#align ring_con.to_add_con RingCon.toAddCon
-/

#print RingCon.toCon /-
/-- Every `ring_con` is also a `con` -/
def toCon : Con R :=
  { c with }
#align ring_con.to_con RingCon.toCon
-/

/-- A coercion from a congruence relation to its underlying binary relation. -/
instance : CoeFun (RingCon R) fun _ => R → R → Prop :=
  ⟨fun c => c.R⟩

#print RingCon.rel_eq_coe /-
@[simp]
theorem rel_eq_coe : c.R = c :=
  rfl
#align ring_con.rel_eq_coe RingCon.rel_eq_coe
-/

#print RingCon.refl /-
protected theorem refl (x) : c x x :=
  c.refl' x
#align ring_con.refl RingCon.refl
-/

#print RingCon.symm /-
protected theorem symm {x y} : c x y → c y x :=
  c.symm'
#align ring_con.symm RingCon.symm
-/

#print RingCon.trans /-
protected theorem trans {x y z} : c x y → c y z → c x z :=
  c.trans'
#align ring_con.trans RingCon.trans
-/

#print RingCon.add /-
protected theorem add {w x y z} : c w x → c y z → c (w + y) (x + z) :=
  c.add'
#align ring_con.add RingCon.add
-/

#print RingCon.mul /-
protected theorem mul {w x y z} : c w x → c y z → c (w * y) (x * z) :=
  c.mul'
#align ring_con.mul RingCon.mul
-/

#print RingCon.rel_mk /-
@[simp]
theorem rel_mk {s : Setoid R} {ha hm a b} : RingCon.mk s ha hm a b ↔ Setoid.r a b :=
  Iff.rfl
#align ring_con.rel_mk RingCon.rel_mk
-/

instance : Inhabited (RingCon R) :=
  ⟨ringConGen EmptyRelation⟩

end Basic

section Quotient

section Basic

variable [Add R] [Mul R] (c : RingCon R)

#print RingCon.Quotient /-
/-- Defining the quotient by a congruence relation of a type with addition and multiplication. -/
protected def Quotient :=
  Quotient c.toSetoid
#align ring_con.quotient RingCon.Quotient
-/

/-- Coercion from a type with addition and multiplication to its quotient by a congruence relation.

See Note [use has_coe_t]. -/
instance : CoeTC R c.Quotient :=
  ⟨@Quotient.mk' _ c.toSetoid⟩

-- Lower the priority since it unifies with any quotient type.
/-- The quotient by a decidable congruence relation has decidable equality. -/
instance (priority := 500) [d : ∀ a b, Decidable (c a b)] : DecidableEq c.Quotient :=
  @Quotient.decidableEq R c.toSetoid d

#print RingCon.quot_mk_eq_coe /-
@[simp]
theorem quot_mk_eq_coe (x : R) : Quot.mk c x = (x : c.Quotient) :=
  rfl
#align ring_con.quot_mk_eq_coe RingCon.quot_mk_eq_coe
-/

#print RingCon.eq /-
/-- Two elements are related by a congruence relation `c` iff they are represented by the same
element of the quotient by `c`. -/
@[simp]
protected theorem eq {a b : R} : (a : c.Quotient) = b ↔ c a b :=
  Quotient.eq''
#align ring_con.eq RingCon.eq
-/

end Basic

/-! ### Basic notation

The basic algebraic notation, `0`, `1`, `+`, `*`, `-`, `^`, descend naturally under the quotient
-/

section Data

section add_mul

variable [Add R] [Mul R] (c : RingCon R)

instance : Add c.Quotient :=
  c.toAddCon.Add

#print RingCon.coe_add /-
@[simp, norm_cast]
theorem coe_add (x y : R) : (↑(x + y) : c.Quotient) = ↑x + ↑y :=
  rfl
#align ring_con.coe_add RingCon.coe_add
-/

instance : Mul c.Quotient :=
  c.toCon.Mul

#print RingCon.coe_mul /-
@[simp, norm_cast]
theorem coe_mul (x y : R) : (↑(x * y) : c.Quotient) = ↑x * ↑y :=
  rfl
#align ring_con.coe_mul RingCon.coe_mul
-/

end add_mul

section Zero

variable [AddZeroClass R] [Mul R] (c : RingCon R)

instance : Zero c.Quotient :=
  c.toAddCon.Quotient.Zero

/- warning: ring_con.coe_zero -> RingCon.coe_zero is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : AddZeroClass.{u1} R] [_inst_2 : Mul.{u1} R] (c : RingCon.{u1} R (AddZeroClass.toHasAdd.{u1} R _inst_1) _inst_2), Eq.{succ u1} (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R _inst_1) _inst_2 c) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) R (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R _inst_1) _inst_2 c) (HasLiftT.mk.{succ u1, succ u1} R (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R _inst_1) _inst_2 c) (CoeTCₓ.coe.{succ u1, succ u1} R (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R _inst_1) _inst_2 c) (RingCon.Quotient.hasCoeT.{u1} R (AddZeroClass.toHasAdd.{u1} R _inst_1) _inst_2 c))) (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (AddZeroClass.toHasZero.{u1} R _inst_1))))) (OfNat.ofNat.{u1} (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R _inst_1) _inst_2 c) 0 (OfNat.mk.{u1} (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R _inst_1) _inst_2 c) 0 (Zero.zero.{u1} (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R _inst_1) _inst_2 c) (RingCon.Quotient.hasZero.{u1} R _inst_1 _inst_2 c))))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : AddZeroClass.{u1} R] [_inst_2 : Mul.{u1} R] (c : RingCon.{u1} R (AddZeroClass.toAdd.{u1} R _inst_1) _inst_2), Eq.{succ u1} (RingCon.Quotient.{u1} R (AddZeroClass.toAdd.{u1} R _inst_1) _inst_2 c) (RingCon.toQuotient.{u1} R (AddZeroClass.toAdd.{u1} R _inst_1) _inst_2 c (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (AddZeroClass.toZero.{u1} R _inst_1)))) (OfNat.ofNat.{u1} (RingCon.Quotient.{u1} R (AddZeroClass.toAdd.{u1} R _inst_1) _inst_2 c) 0 (Zero.toOfNat0.{u1} (RingCon.Quotient.{u1} R (AddZeroClass.toAdd.{u1} R _inst_1) _inst_2 c) (RingCon.instZeroQuotientToAdd.{u1} R _inst_1 _inst_2 c)))
Case conversion may be inaccurate. Consider using '#align ring_con.coe_zero RingCon.coe_zeroₓ'. -/
@[simp, norm_cast]
theorem coe_zero : (↑(0 : R) : c.Quotient) = 0 :=
  rfl
#align ring_con.coe_zero RingCon.coe_zero

end Zero

section One

variable [Add R] [MulOneClass R] (c : RingCon R)

instance : One c.Quotient :=
  c.toCon.Quotient.One

/- warning: ring_con.coe_one -> RingCon.coe_one is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Add.{u1} R] [_inst_2 : MulOneClass.{u1} R] (c : RingCon.{u1} R _inst_1 (MulOneClass.toHasMul.{u1} R _inst_2)), Eq.{succ u1} (RingCon.Quotient.{u1} R _inst_1 (MulOneClass.toHasMul.{u1} R _inst_2) c) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) R (RingCon.Quotient.{u1} R _inst_1 (MulOneClass.toHasMul.{u1} R _inst_2) c) (HasLiftT.mk.{succ u1, succ u1} R (RingCon.Quotient.{u1} R _inst_1 (MulOneClass.toHasMul.{u1} R _inst_2) c) (CoeTCₓ.coe.{succ u1, succ u1} R (RingCon.Quotient.{u1} R _inst_1 (MulOneClass.toHasMul.{u1} R _inst_2) c) (RingCon.Quotient.hasCoeT.{u1} R _inst_1 (MulOneClass.toHasMul.{u1} R _inst_2) c))) (OfNat.ofNat.{u1} R 1 (OfNat.mk.{u1} R 1 (One.one.{u1} R (MulOneClass.toHasOne.{u1} R _inst_2))))) (OfNat.ofNat.{u1} (RingCon.Quotient.{u1} R _inst_1 (MulOneClass.toHasMul.{u1} R _inst_2) c) 1 (OfNat.mk.{u1} (RingCon.Quotient.{u1} R _inst_1 (MulOneClass.toHasMul.{u1} R _inst_2) c) 1 (One.one.{u1} (RingCon.Quotient.{u1} R _inst_1 (MulOneClass.toHasMul.{u1} R _inst_2) c) (RingCon.Quotient.hasOne.{u1} R _inst_1 _inst_2 c))))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : Add.{u1} R] [_inst_2 : MulOneClass.{u1} R] (c : RingCon.{u1} R _inst_1 (MulOneClass.toMul.{u1} R _inst_2)), Eq.{succ u1} (RingCon.Quotient.{u1} R _inst_1 (MulOneClass.toMul.{u1} R _inst_2) c) (RingCon.toQuotient.{u1} R _inst_1 (MulOneClass.toMul.{u1} R _inst_2) c (OfNat.ofNat.{u1} R 1 (One.toOfNat1.{u1} R (MulOneClass.toOne.{u1} R _inst_2)))) (OfNat.ofNat.{u1} (RingCon.Quotient.{u1} R _inst_1 (MulOneClass.toMul.{u1} R _inst_2) c) 1 (One.toOfNat1.{u1} (RingCon.Quotient.{u1} R _inst_1 (MulOneClass.toMul.{u1} R _inst_2) c) (RingCon.instOneQuotientToMul.{u1} R _inst_1 _inst_2 c)))
Case conversion may be inaccurate. Consider using '#align ring_con.coe_one RingCon.coe_oneₓ'. -/
@[simp, norm_cast]
theorem coe_one : (↑(1 : R) : c.Quotient) = 1 :=
  rfl
#align ring_con.coe_one RingCon.coe_one

end One

section Smul

variable [Add R] [MulOneClass R] [SMul α R] [IsScalarTower α R R] (c : RingCon R)

instance : SMul α c.Quotient :=
  c.toCon.SMul

/- warning: ring_con.coe_smul -> RingCon.coe_smul is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {R : Type.{u2}} [_inst_1 : Add.{u2} R] [_inst_2 : MulOneClass.{u2} R] [_inst_3 : SMul.{u1, u2} α R] [_inst_4 : IsScalarTower.{u1, u2, u2} α R R _inst_3 (Mul.toSMul.{u2} R (MulOneClass.toHasMul.{u2} R _inst_2)) _inst_3] (c : RingCon.{u2} R _inst_1 (MulOneClass.toHasMul.{u2} R _inst_2)) (a : α) (x : R), Eq.{succ u2} (RingCon.Quotient.{u2} R _inst_1 (MulOneClass.toHasMul.{u2} R _inst_2) c) ((fun (a : Type.{u2}) (b : Type.{u2}) [self : HasLiftT.{succ u2, succ u2} a b] => self.0) R (RingCon.Quotient.{u2} R _inst_1 (MulOneClass.toHasMul.{u2} R _inst_2) c) (HasLiftT.mk.{succ u2, succ u2} R (RingCon.Quotient.{u2} R _inst_1 (MulOneClass.toHasMul.{u2} R _inst_2) c) (CoeTCₓ.coe.{succ u2, succ u2} R (RingCon.Quotient.{u2} R _inst_1 (MulOneClass.toHasMul.{u2} R _inst_2) c) (RingCon.Quotient.hasCoeT.{u2} R _inst_1 (MulOneClass.toHasMul.{u2} R _inst_2) c))) (SMul.smul.{u1, u2} α R _inst_3 a x)) (SMul.smul.{u1, u2} α (RingCon.Quotient.{u2} R _inst_1 (MulOneClass.toHasMul.{u2} R _inst_2) c) (RingCon.Quotient.hasSmul.{u1, u2} α R _inst_1 _inst_2 _inst_3 _inst_4 c) a ((fun (a : Type.{u2}) (b : Type.{u2}) [self : HasLiftT.{succ u2, succ u2} a b] => self.0) R (RingCon.Quotient.{u2} R _inst_1 (MulOneClass.toHasMul.{u2} R _inst_2) c) (HasLiftT.mk.{succ u2, succ u2} R (RingCon.Quotient.{u2} R _inst_1 (MulOneClass.toHasMul.{u2} R _inst_2) c) (CoeTCₓ.coe.{succ u2, succ u2} R (RingCon.Quotient.{u2} R _inst_1 (MulOneClass.toHasMul.{u2} R _inst_2) c) (RingCon.Quotient.hasCoeT.{u2} R _inst_1 (MulOneClass.toHasMul.{u2} R _inst_2) c))) x))
but is expected to have type
  forall {α : Type.{u1}} {R : Type.{u2}} [_inst_1 : Add.{u2} R] [_inst_2 : MulOneClass.{u2} R] [_inst_3 : SMul.{u1, u2} α R] [_inst_4 : IsScalarTower.{u1, u2, u2} α R R _inst_3 (Mul.toSMul.{u2} R (MulOneClass.toMul.{u2} R _inst_2)) _inst_3] (c : RingCon.{u2} R _inst_1 (MulOneClass.toMul.{u2} R _inst_2)) (a : α) (x : R), Eq.{succ u2} (RingCon.Quotient.{u2} R _inst_1 (MulOneClass.toMul.{u2} R _inst_2) c) (RingCon.toQuotient.{u2} R _inst_1 (MulOneClass.toMul.{u2} R _inst_2) c (HSMul.hSMul.{u1, u2, u2} α R R (instHSMul.{u1, u2} α R _inst_3) a x)) (HSMul.hSMul.{u1, u2, u2} α (RingCon.Quotient.{u2} R _inst_1 (MulOneClass.toMul.{u2} R _inst_2) c) (RingCon.Quotient.{u2} R _inst_1 (MulOneClass.toMul.{u2} R _inst_2) c) (instHSMul.{u1, u2} α (RingCon.Quotient.{u2} R _inst_1 (MulOneClass.toMul.{u2} R _inst_2) c) (RingCon.instSMulQuotientToMul.{u1, u2} α R _inst_1 _inst_2 _inst_3 _inst_4 c)) a (RingCon.toQuotient.{u2} R _inst_1 (MulOneClass.toMul.{u2} R _inst_2) c x))
Case conversion may be inaccurate. Consider using '#align ring_con.coe_smul RingCon.coe_smulₓ'. -/
@[simp, norm_cast]
theorem coe_smul (a : α) (x : R) : (↑(a • x) : c.Quotient) = a • x :=
  rfl
#align ring_con.coe_smul RingCon.coe_smul

end Smul

section NegSubZsmul

variable [AddGroup R] [Mul R] (c : RingCon R)

instance : Neg c.Quotient :=
  c.toAddCon.Neg

/- warning: ring_con.coe_neg -> RingCon.coe_neg is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : AddGroup.{u1} R] [_inst_2 : Mul.{u1} R] (c : RingCon.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2) (x : R), Eq.{succ u1} (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) R (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c) (HasLiftT.mk.{succ u1, succ u1} R (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c) (CoeTCₓ.coe.{succ u1, succ u1} R (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c) (RingCon.Quotient.hasCoeT.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c))) (Neg.neg.{u1} R (SubNegMonoid.toHasNeg.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)) x)) (Neg.neg.{u1} (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c) (RingCon.Quotient.hasNeg.{u1} R _inst_1 _inst_2 c) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) R (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c) (HasLiftT.mk.{succ u1, succ u1} R (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c) (CoeTCₓ.coe.{succ u1, succ u1} R (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c) (RingCon.Quotient.hasCoeT.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c))) x))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : AddGroup.{u1} R] [_inst_2 : Mul.{u1} R] (c : RingCon.{u1} R (AddZeroClass.toAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2) (x : R), Eq.{succ u1} (RingCon.Quotient.{u1} R (AddZeroClass.toAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c) (RingCon.toQuotient.{u1} R (AddZeroClass.toAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c (Neg.neg.{u1} R (NegZeroClass.toNeg.{u1} R (SubNegZeroMonoid.toNegZeroClass.{u1} R (SubtractionMonoid.toSubNegZeroMonoid.{u1} R (AddGroup.toSubtractionMonoid.{u1} R _inst_1)))) x)) (Neg.neg.{u1} (RingCon.Quotient.{u1} R (AddZeroClass.toAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c) (RingCon.instNegQuotientToAddToAddZeroClassToAddMonoidToSubNegMonoid.{u1} R _inst_1 _inst_2 c) (RingCon.toQuotient.{u1} R (AddZeroClass.toAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c x))
Case conversion may be inaccurate. Consider using '#align ring_con.coe_neg RingCon.coe_negₓ'. -/
@[simp, norm_cast]
theorem coe_neg (x : R) : (↑(-x) : c.Quotient) = -x :=
  rfl
#align ring_con.coe_neg RingCon.coe_neg

instance : Sub c.Quotient :=
  c.toAddCon.Sub

/- warning: ring_con.coe_sub -> RingCon.coe_sub is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : AddGroup.{u1} R] [_inst_2 : Mul.{u1} R] (c : RingCon.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2) (x : R) (y : R), Eq.{succ u1} (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) R (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c) (HasLiftT.mk.{succ u1, succ u1} R (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c) (CoeTCₓ.coe.{succ u1, succ u1} R (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c) (RingCon.Quotient.hasCoeT.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c))) (HSub.hSub.{u1, u1, u1} R R R (instHSub.{u1} R (SubNegMonoid.toHasSub.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1))) x y)) (HSub.hSub.{u1, u1, u1} (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c) (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c) (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c) (instHSub.{u1} (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c) (RingCon.Quotient.hasSub.{u1} R _inst_1 _inst_2 c)) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) R (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c) (HasLiftT.mk.{succ u1, succ u1} R (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c) (CoeTCₓ.coe.{succ u1, succ u1} R (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c) (RingCon.Quotient.hasCoeT.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c))) x) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) R (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c) (HasLiftT.mk.{succ u1, succ u1} R (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c) (CoeTCₓ.coe.{succ u1, succ u1} R (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c) (RingCon.Quotient.hasCoeT.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c))) y))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : AddGroup.{u1} R] [_inst_2 : Mul.{u1} R] (c : RingCon.{u1} R (AddZeroClass.toAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2) (x : R) (y : R), Eq.{succ u1} (RingCon.Quotient.{u1} R (AddZeroClass.toAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c) (RingCon.toQuotient.{u1} R (AddZeroClass.toAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c (HSub.hSub.{u1, u1, u1} R R R (instHSub.{u1} R (SubNegMonoid.toSub.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1))) x y)) (HSub.hSub.{u1, u1, u1} (RingCon.Quotient.{u1} R (AddZeroClass.toAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c) (RingCon.Quotient.{u1} R (AddZeroClass.toAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c) (RingCon.Quotient.{u1} R (AddZeroClass.toAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c) (instHSub.{u1} (RingCon.Quotient.{u1} R (AddZeroClass.toAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c) (RingCon.instSubQuotientToAddToAddZeroClassToAddMonoidToSubNegMonoid.{u1} R _inst_1 _inst_2 c)) (RingCon.toQuotient.{u1} R (AddZeroClass.toAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c x) (RingCon.toQuotient.{u1} R (AddZeroClass.toAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c y))
Case conversion may be inaccurate. Consider using '#align ring_con.coe_sub RingCon.coe_subₓ'. -/
@[simp, norm_cast]
theorem coe_sub (x y : R) : (↑(x - y) : c.Quotient) = x - y :=
  rfl
#align ring_con.coe_sub RingCon.coe_sub

/- warning: ring_con.has_zsmul -> RingCon.hasZsmul is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : AddGroup.{u1} R] [_inst_2 : Mul.{u1} R] (c : RingCon.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2), SMul.{0, u1} Int (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c)
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : AddGroup.{u1} R] [_inst_2 : Mul.{u1} R] (c : RingCon.{u1} R (AddZeroClass.toAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2), SMul.{0, u1} Int (RingCon.Quotient.{u1} R (AddZeroClass.toAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c)
Case conversion may be inaccurate. Consider using '#align ring_con.has_zsmul RingCon.hasZsmulₓ'. -/
instance hasZsmul : SMul ℤ c.Quotient :=
  c.toAddCon.Quotient.zsmul
#align ring_con.has_zsmul RingCon.hasZsmul

/- warning: ring_con.coe_zsmul -> RingCon.coe_zsmul is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : AddGroup.{u1} R] [_inst_2 : Mul.{u1} R] (c : RingCon.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2) (z : Int) (x : R), Eq.{succ u1} (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) R (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c) (HasLiftT.mk.{succ u1, succ u1} R (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c) (CoeTCₓ.coe.{succ u1, succ u1} R (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c) (RingCon.Quotient.hasCoeT.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c))) (SMul.smul.{0, u1} Int R (SubNegMonoid.SMulInt.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)) z x)) (SMul.smul.{0, u1} Int (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c) (RingCon.hasZsmul.{u1} R _inst_1 _inst_2 c) z ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) R (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c) (HasLiftT.mk.{succ u1, succ u1} R (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c) (CoeTCₓ.coe.{succ u1, succ u1} R (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c) (RingCon.Quotient.hasCoeT.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c))) x))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : AddGroup.{u1} R] [_inst_2 : Mul.{u1} R] (c : RingCon.{u1} R (AddZeroClass.toAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2) (z : Int) (x : R), Eq.{succ u1} (RingCon.Quotient.{u1} R (AddZeroClass.toAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c) (RingCon.toQuotient.{u1} R (AddZeroClass.toAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c (HSMul.hSMul.{0, u1, u1} Int R R (instHSMul.{0, u1} Int R (SubNegMonoid.SMulInt.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1))) z x)) (HSMul.hSMul.{0, u1, u1} Int (RingCon.Quotient.{u1} R (AddZeroClass.toAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c) (RingCon.Quotient.{u1} R (AddZeroClass.toAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c) (instHSMul.{0, u1} Int (RingCon.Quotient.{u1} R (AddZeroClass.toAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c) (RingCon.hasZsmul.{u1} R _inst_1 _inst_2 c)) z (RingCon.toQuotient.{u1} R (AddZeroClass.toAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R _inst_1)))) _inst_2 c x))
Case conversion may be inaccurate. Consider using '#align ring_con.coe_zsmul RingCon.coe_zsmulₓ'. -/
@[simp, norm_cast]
theorem coe_zsmul (z : ℤ) (x : R) : (↑(z • x) : c.Quotient) = z • x :=
  rfl
#align ring_con.coe_zsmul RingCon.coe_zsmul

end NegSubZsmul

section Nsmul

variable [AddMonoid R] [Mul R] (c : RingCon R)

/- warning: ring_con.has_nsmul -> RingCon.hasNsmul is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : AddMonoid.{u1} R] [_inst_2 : Mul.{u1} R] (c : RingCon.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R _inst_1)) _inst_2), SMul.{0, u1} Nat (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R _inst_1)) _inst_2 c)
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : AddMonoid.{u1} R] [_inst_2 : Mul.{u1} R] (c : RingCon.{u1} R (AddZeroClass.toAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R _inst_1)) _inst_2), SMul.{0, u1} Nat (RingCon.Quotient.{u1} R (AddZeroClass.toAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R _inst_1)) _inst_2 c)
Case conversion may be inaccurate. Consider using '#align ring_con.has_nsmul RingCon.hasNsmulₓ'. -/
instance hasNsmul : SMul ℕ c.Quotient :=
  c.toAddCon.Quotient.nSMul
#align ring_con.has_nsmul RingCon.hasNsmul

/- warning: ring_con.coe_nsmul -> RingCon.coe_nsmul is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : AddMonoid.{u1} R] [_inst_2 : Mul.{u1} R] (c : RingCon.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R _inst_1)) _inst_2) (n : Nat) (x : R), Eq.{succ u1} (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R _inst_1)) _inst_2 c) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) R (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R _inst_1)) _inst_2 c) (HasLiftT.mk.{succ u1, succ u1} R (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R _inst_1)) _inst_2 c) (CoeTCₓ.coe.{succ u1, succ u1} R (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R _inst_1)) _inst_2 c) (RingCon.Quotient.hasCoeT.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R _inst_1)) _inst_2 c))) (SMul.smul.{0, u1} Nat R (AddMonoid.SMul.{u1} R _inst_1) n x)) (SMul.smul.{0, u1} Nat (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R _inst_1)) _inst_2 c) (RingCon.hasNsmul.{u1} R _inst_1 _inst_2 c) n ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) R (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R _inst_1)) _inst_2 c) (HasLiftT.mk.{succ u1, succ u1} R (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R _inst_1)) _inst_2 c) (CoeTCₓ.coe.{succ u1, succ u1} R (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R _inst_1)) _inst_2 c) (RingCon.Quotient.hasCoeT.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R _inst_1)) _inst_2 c))) x))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : AddMonoid.{u1} R] [_inst_2 : Mul.{u1} R] (c : RingCon.{u1} R (AddZeroClass.toAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R _inst_1)) _inst_2) (n : Nat) (x : R), Eq.{succ u1} (RingCon.Quotient.{u1} R (AddZeroClass.toAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R _inst_1)) _inst_2 c) (RingCon.toQuotient.{u1} R (AddZeroClass.toAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R _inst_1)) _inst_2 c (HSMul.hSMul.{0, u1, u1} Nat R R (instHSMul.{0, u1} Nat R (AddMonoid.SMul.{u1} R _inst_1)) n x)) (HSMul.hSMul.{0, u1, u1} Nat (RingCon.Quotient.{u1} R (AddZeroClass.toAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R _inst_1)) _inst_2 c) (RingCon.Quotient.{u1} R (AddZeroClass.toAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R _inst_1)) _inst_2 c) (instHSMul.{0, u1} Nat (RingCon.Quotient.{u1} R (AddZeroClass.toAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R _inst_1)) _inst_2 c) (RingCon.hasNsmul.{u1} R _inst_1 _inst_2 c)) n (RingCon.toQuotient.{u1} R (AddZeroClass.toAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R _inst_1)) _inst_2 c x))
Case conversion may be inaccurate. Consider using '#align ring_con.coe_nsmul RingCon.coe_nsmulₓ'. -/
@[simp, norm_cast]
theorem coe_nsmul (n : ℕ) (x : R) : (↑(n • x) : c.Quotient) = n • x :=
  rfl
#align ring_con.coe_nsmul RingCon.coe_nsmul

end Nsmul

section Pow

variable [Add R] [Monoid R] (c : RingCon R)

instance : Pow c.Quotient ℕ :=
  c.toCon.Nat.Pow

/- warning: ring_con.coe_pow -> RingCon.coe_pow is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Add.{u1} R] [_inst_2 : Monoid.{u1} R] (c : RingCon.{u1} R _inst_1 (MulOneClass.toHasMul.{u1} R (Monoid.toMulOneClass.{u1} R _inst_2))) (x : R) (n : Nat), Eq.{succ u1} (RingCon.Quotient.{u1} R _inst_1 (MulOneClass.toHasMul.{u1} R (Monoid.toMulOneClass.{u1} R _inst_2)) c) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) R (RingCon.Quotient.{u1} R _inst_1 (MulOneClass.toHasMul.{u1} R (Monoid.toMulOneClass.{u1} R _inst_2)) c) (HasLiftT.mk.{succ u1, succ u1} R (RingCon.Quotient.{u1} R _inst_1 (MulOneClass.toHasMul.{u1} R (Monoid.toMulOneClass.{u1} R _inst_2)) c) (CoeTCₓ.coe.{succ u1, succ u1} R (RingCon.Quotient.{u1} R _inst_1 (MulOneClass.toHasMul.{u1} R (Monoid.toMulOneClass.{u1} R _inst_2)) c) (RingCon.Quotient.hasCoeT.{u1} R _inst_1 (MulOneClass.toHasMul.{u1} R (Monoid.toMulOneClass.{u1} R _inst_2)) c))) (HPow.hPow.{u1, 0, u1} R Nat R (instHPow.{u1, 0} R Nat (Monoid.Pow.{u1} R _inst_2)) x n)) (HPow.hPow.{u1, 0, u1} (RingCon.Quotient.{u1} R _inst_1 (MulOneClass.toHasMul.{u1} R (Monoid.toMulOneClass.{u1} R _inst_2)) c) Nat (RingCon.Quotient.{u1} R _inst_1 (MulOneClass.toHasMul.{u1} R (Monoid.toMulOneClass.{u1} R _inst_2)) c) (instHPow.{u1, 0} (RingCon.Quotient.{u1} R _inst_1 (MulOneClass.toHasMul.{u1} R (Monoid.toMulOneClass.{u1} R _inst_2)) c) Nat (RingCon.Nat.hasPow.{u1} R _inst_1 _inst_2 c)) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) R (RingCon.Quotient.{u1} R _inst_1 (MulOneClass.toHasMul.{u1} R (Monoid.toMulOneClass.{u1} R _inst_2)) c) (HasLiftT.mk.{succ u1, succ u1} R (RingCon.Quotient.{u1} R _inst_1 (MulOneClass.toHasMul.{u1} R (Monoid.toMulOneClass.{u1} R _inst_2)) c) (CoeTCₓ.coe.{succ u1, succ u1} R (RingCon.Quotient.{u1} R _inst_1 (MulOneClass.toHasMul.{u1} R (Monoid.toMulOneClass.{u1} R _inst_2)) c) (RingCon.Quotient.hasCoeT.{u1} R _inst_1 (MulOneClass.toHasMul.{u1} R (Monoid.toMulOneClass.{u1} R _inst_2)) c))) x) n)
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : Add.{u1} R] [_inst_2 : Monoid.{u1} R] (c : RingCon.{u1} R _inst_1 (MulOneClass.toMul.{u1} R (Monoid.toMulOneClass.{u1} R _inst_2))) (x : R) (n : Nat), Eq.{succ u1} (RingCon.Quotient.{u1} R _inst_1 (MulOneClass.toMul.{u1} R (Monoid.toMulOneClass.{u1} R _inst_2)) c) (RingCon.toQuotient.{u1} R _inst_1 (MulOneClass.toMul.{u1} R (Monoid.toMulOneClass.{u1} R _inst_2)) c (HPow.hPow.{u1, 0, u1} R Nat R (instHPow.{u1, 0} R Nat (Monoid.Pow.{u1} R _inst_2)) x n)) (HPow.hPow.{u1, 0, u1} (RingCon.Quotient.{u1} R _inst_1 (MulOneClass.toMul.{u1} R (Monoid.toMulOneClass.{u1} R _inst_2)) c) Nat (RingCon.Quotient.{u1} R _inst_1 (MulOneClass.toMul.{u1} R (Monoid.toMulOneClass.{u1} R _inst_2)) c) (instHPow.{u1, 0} (RingCon.Quotient.{u1} R _inst_1 (MulOneClass.toMul.{u1} R (Monoid.toMulOneClass.{u1} R _inst_2)) c) Nat (RingCon.instPowQuotientToMulToMulOneClassNat.{u1} R _inst_1 _inst_2 c)) (RingCon.toQuotient.{u1} R _inst_1 (MulOneClass.toMul.{u1} R (Monoid.toMulOneClass.{u1} R _inst_2)) c x) n)
Case conversion may be inaccurate. Consider using '#align ring_con.coe_pow RingCon.coe_powₓ'. -/
@[simp, norm_cast]
theorem coe_pow (x : R) (n : ℕ) : (↑(x ^ n) : c.Quotient) = x ^ n :=
  rfl
#align ring_con.coe_pow RingCon.coe_pow

end Pow

section NatCast

variable [AddMonoidWithOne R] [Mul R] (c : RingCon R)

instance : NatCast c.Quotient :=
  ⟨fun n => ↑(n : R)⟩

/- warning: ring_con.coe_nat_cast -> RingCon.coe_nat_cast is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : AddMonoidWithOne.{u1} R] [_inst_2 : Mul.{u1} R] (c : RingCon.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (AddMonoidWithOne.toAddMonoid.{u1} R _inst_1))) _inst_2) (n : Nat), Eq.{succ u1} (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (AddMonoidWithOne.toAddMonoid.{u1} R _inst_1))) _inst_2 c) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) R (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (AddMonoidWithOne.toAddMonoid.{u1} R _inst_1))) _inst_2 c) (HasLiftT.mk.{succ u1, succ u1} R (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (AddMonoidWithOne.toAddMonoid.{u1} R _inst_1))) _inst_2 c) (CoeTCₓ.coe.{succ u1, succ u1} R (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (AddMonoidWithOne.toAddMonoid.{u1} R _inst_1))) _inst_2 c) (RingCon.Quotient.hasCoeT.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (AddMonoidWithOne.toAddMonoid.{u1} R _inst_1))) _inst_2 c))) ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Nat R (HasLiftT.mk.{1, succ u1} Nat R (CoeTCₓ.coe.{1, succ u1} Nat R (Nat.castCoe.{u1} R (AddMonoidWithOne.toNatCast.{u1} R _inst_1)))) n)) ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Nat (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (AddMonoidWithOne.toAddMonoid.{u1} R _inst_1))) _inst_2 c) (HasLiftT.mk.{1, succ u1} Nat (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (AddMonoidWithOne.toAddMonoid.{u1} R _inst_1))) _inst_2 c) (CoeTCₓ.coe.{1, succ u1} Nat (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (AddMonoidWithOne.toAddMonoid.{u1} R _inst_1))) _inst_2 c) (Nat.castCoe.{u1} (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (AddMonoidWithOne.toAddMonoid.{u1} R _inst_1))) _inst_2 c) (RingCon.Quotient.hasNatCast.{u1} R _inst_1 _inst_2 c)))) n)
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : AddMonoidWithOne.{u1} R] [_inst_2 : Mul.{u1} R] (c : RingCon.{u1} R (AddZeroClass.toAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (AddMonoidWithOne.toAddMonoid.{u1} R _inst_1))) _inst_2) (n : Nat), Eq.{succ u1} (RingCon.Quotient.{u1} R (AddZeroClass.toAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (AddMonoidWithOne.toAddMonoid.{u1} R _inst_1))) _inst_2 c) (RingCon.toQuotient.{u1} R (AddZeroClass.toAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (AddMonoidWithOne.toAddMonoid.{u1} R _inst_1))) _inst_2 c (Nat.cast.{u1} R (AddMonoidWithOne.toNatCast.{u1} R _inst_1) n)) (Nat.cast.{u1} (RingCon.Quotient.{u1} R (AddZeroClass.toAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (AddMonoidWithOne.toAddMonoid.{u1} R _inst_1))) _inst_2 c) (RingCon.instNatCastQuotientToAddToAddZeroClassToAddMonoid.{u1} R _inst_1 _inst_2 c) n)
Case conversion may be inaccurate. Consider using '#align ring_con.coe_nat_cast RingCon.coe_nat_castₓ'. -/
@[simp, norm_cast]
theorem coe_nat_cast (n : ℕ) : (↑(n : R) : c.Quotient) = n :=
  rfl
#align ring_con.coe_nat_cast RingCon.coe_nat_cast

end NatCast

section IntCast

variable [AddGroupWithOne R] [Mul R] (c : RingCon R)

instance : IntCast c.Quotient :=
  ⟨fun z => ↑(z : R)⟩

/- warning: ring_con.coe_int_cast -> RingCon.coe_int_cast is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : AddGroupWithOne.{u1} R] [_inst_2 : Mul.{u1} R] (c : RingCon.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (AddMonoidWithOne.toAddMonoid.{u1} R (AddGroupWithOne.toAddMonoidWithOne.{u1} R _inst_1)))) _inst_2) (n : Nat), Eq.{succ u1} (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (AddMonoidWithOne.toAddMonoid.{u1} R (AddGroupWithOne.toAddMonoidWithOne.{u1} R _inst_1)))) _inst_2 c) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) R (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (AddMonoidWithOne.toAddMonoid.{u1} R (AddGroupWithOne.toAddMonoidWithOne.{u1} R _inst_1)))) _inst_2 c) (HasLiftT.mk.{succ u1, succ u1} R (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (AddMonoidWithOne.toAddMonoid.{u1} R (AddGroupWithOne.toAddMonoidWithOne.{u1} R _inst_1)))) _inst_2 c) (CoeTCₓ.coe.{succ u1, succ u1} R (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (AddMonoidWithOne.toAddMonoid.{u1} R (AddGroupWithOne.toAddMonoidWithOne.{u1} R _inst_1)))) _inst_2 c) (RingCon.Quotient.hasCoeT.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (AddMonoidWithOne.toAddMonoid.{u1} R (AddGroupWithOne.toAddMonoidWithOne.{u1} R _inst_1)))) _inst_2 c))) ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Nat R (HasLiftT.mk.{1, succ u1} Nat R (CoeTCₓ.coe.{1, succ u1} Nat R (Nat.castCoe.{u1} R (AddMonoidWithOne.toNatCast.{u1} R (AddGroupWithOne.toAddMonoidWithOne.{u1} R _inst_1))))) n)) ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Nat (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (AddMonoidWithOne.toAddMonoid.{u1} R (AddGroupWithOne.toAddMonoidWithOne.{u1} R _inst_1)))) _inst_2 c) (HasLiftT.mk.{1, succ u1} Nat (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (AddMonoidWithOne.toAddMonoid.{u1} R (AddGroupWithOne.toAddMonoidWithOne.{u1} R _inst_1)))) _inst_2 c) (CoeTCₓ.coe.{1, succ u1} Nat (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (AddMonoidWithOne.toAddMonoid.{u1} R (AddGroupWithOne.toAddMonoidWithOne.{u1} R _inst_1)))) _inst_2 c) (Nat.castCoe.{u1} (RingCon.Quotient.{u1} R (AddZeroClass.toHasAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (AddMonoidWithOne.toAddMonoid.{u1} R (AddGroupWithOne.toAddMonoidWithOne.{u1} R _inst_1)))) _inst_2 c) (RingCon.Quotient.hasNatCast.{u1} R (AddGroupWithOne.toAddMonoidWithOne.{u1} R _inst_1) _inst_2 c)))) n)
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : AddGroupWithOne.{u1} R] [_inst_2 : Mul.{u1} R] (c : RingCon.{u1} R (AddZeroClass.toAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (AddMonoidWithOne.toAddMonoid.{u1} R (AddGroupWithOne.toAddMonoidWithOne.{u1} R _inst_1)))) _inst_2) (n : Nat), Eq.{succ u1} (RingCon.Quotient.{u1} R (AddZeroClass.toAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (AddMonoidWithOne.toAddMonoid.{u1} R (AddGroupWithOne.toAddMonoidWithOne.{u1} R _inst_1)))) _inst_2 c) (RingCon.toQuotient.{u1} R (AddZeroClass.toAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (AddMonoidWithOne.toAddMonoid.{u1} R (AddGroupWithOne.toAddMonoidWithOne.{u1} R _inst_1)))) _inst_2 c (Nat.cast.{u1} R (AddMonoidWithOne.toNatCast.{u1} R (AddGroupWithOne.toAddMonoidWithOne.{u1} R _inst_1)) n)) (Nat.cast.{u1} (RingCon.Quotient.{u1} R (AddZeroClass.toAdd.{u1} R (AddMonoid.toAddZeroClass.{u1} R (AddMonoidWithOne.toAddMonoid.{u1} R (AddGroupWithOne.toAddMonoidWithOne.{u1} R _inst_1)))) _inst_2 c) (RingCon.instNatCastQuotientToAddToAddZeroClassToAddMonoid.{u1} R (AddGroupWithOne.toAddMonoidWithOne.{u1} R _inst_1) _inst_2 c) n)
Case conversion may be inaccurate. Consider using '#align ring_con.coe_int_cast RingCon.coe_int_castₓ'. -/
@[simp, norm_cast]
theorem coe_int_cast (n : ℕ) : (↑(n : R) : c.Quotient) = n :=
  rfl
#align ring_con.coe_int_cast RingCon.coe_int_cast

end IntCast

instance [Inhabited R] [Add R] [Mul R] (c : RingCon R) : Inhabited c.Quotient :=
  ⟨↑(default : R)⟩

end Data

/-! ### Algebraic structure

The operations above on the quotient by `c : ring_con R` preseverse the algebraic structure of `R`.
-/

section Algebraic

instance [NonUnitalNonAssocSemiring R] (c : RingCon R) : NonUnitalNonAssocSemiring c.Quotient :=
  Function.Surjective.nonUnitalNonAssocSemiring _ Quotient.surjective_Quotient_mk'' rfl
    (fun _ _ => rfl) (fun _ _ => rfl) fun _ _ => rfl

instance [NonAssocSemiring R] (c : RingCon R) : NonAssocSemiring c.Quotient :=
  Function.Surjective.nonAssocSemiring _ Quotient.surjective_Quotient_mk'' rfl rfl (fun _ _ => rfl)
    (fun _ _ => rfl) (fun _ _ => rfl) fun _ => rfl

instance [NonUnitalSemiring R] (c : RingCon R) : NonUnitalSemiring c.Quotient :=
  Function.Surjective.nonUnitalSemiring _ Quotient.surjective_Quotient_mk'' rfl (fun _ _ => rfl)
    (fun _ _ => rfl) fun _ _ => rfl

instance [Semiring R] (c : RingCon R) : Semiring c.Quotient :=
  Function.Surjective.semiring _ Quotient.surjective_Quotient_mk'' rfl rfl (fun _ _ => rfl)
    (fun _ _ => rfl) (fun _ _ => rfl) (fun _ _ => rfl) fun _ => rfl

instance [CommSemiring R] (c : RingCon R) : CommSemiring c.Quotient :=
  Function.Surjective.commSemiring _ Quotient.surjective_Quotient_mk'' rfl rfl (fun _ _ => rfl)
    (fun _ _ => rfl) (fun _ _ => rfl) (fun _ _ => rfl) fun _ => rfl

instance [NonUnitalNonAssocRing R] (c : RingCon R) : NonUnitalNonAssocRing c.Quotient :=
  Function.Surjective.nonUnitalNonAssocRing _ Quotient.surjective_Quotient_mk'' rfl (fun _ _ => rfl)
    (fun _ _ => rfl) (fun _ => rfl) (fun _ _ => rfl) (fun _ _ => rfl) fun _ _ => rfl

instance [NonAssocRing R] (c : RingCon R) : NonAssocRing c.Quotient :=
  Function.Surjective.nonAssocRing _ Quotient.surjective_Quotient_mk'' rfl rfl (fun _ _ => rfl)
    (fun _ _ => rfl) (fun _ => rfl) (fun _ _ => rfl) (fun _ _ => rfl) (fun _ _ => rfl)
    (fun _ => rfl) fun _ => rfl

instance [NonUnitalRing R] (c : RingCon R) : NonUnitalRing c.Quotient :=
  Function.Surjective.nonUnitalRing _ Quotient.surjective_Quotient_mk'' rfl (fun _ _ => rfl)
    (fun _ _ => rfl) (fun _ => rfl) (fun _ _ => rfl) (fun _ _ => rfl) fun _ _ => rfl

instance [Ring R] (c : RingCon R) : Ring c.Quotient :=
  Function.Surjective.ring _ Quotient.surjective_Quotient_mk'' rfl rfl (fun _ _ => rfl)
    (fun _ _ => rfl) (fun _ => rfl) (fun _ _ => rfl) (fun _ _ => rfl) (fun _ _ => rfl)
    (fun _ _ => rfl) (fun _ => rfl) fun _ => rfl

instance [CommRing R] (c : RingCon R) : CommRing c.Quotient :=
  Function.Surjective.commRing _ Quotient.surjective_Quotient_mk'' rfl rfl (fun _ _ => rfl)
    (fun _ _ => rfl) (fun _ => rfl) (fun _ _ => rfl) (fun _ _ => rfl) (fun _ _ => rfl)
    (fun _ _ => rfl) (fun _ => rfl) fun _ => rfl

instance [Monoid α] [NonAssocSemiring R] [DistribMulAction α R] [IsScalarTower α R R]
    (c : RingCon R) : DistribMulAction α c.Quotient :=
  { c.toCon.MulAction with
    smul := (· • ·)
    smul_zero := fun r => congr_arg Quotient.mk'' <| smul_zero _
    smul_add := fun r => Quotient.ind₂' fun m₁ m₂ => congr_arg Quotient.mk'' <| smul_add _ _ _ }

instance [Monoid α] [Semiring R] [MulSemiringAction α R] [IsScalarTower α R R] (c : RingCon R) :
    MulSemiringAction α c.Quotient :=
  { c.Quotient.DistribMulAction, c.toCon.MulDistribMulAction with smul := (· • ·) }

end Algebraic

/- warning: ring_con.mk' -> RingCon.mk' is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : NonAssocSemiring.{u1} R] (c : RingCon.{u1} R (Distrib.toHasAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R _inst_1))) (Distrib.toHasMul.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R _inst_1)))), RingHom.{u1, u1} R (RingCon.Quotient.{u1} R (Distrib.toHasAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R _inst_1))) (Distrib.toHasMul.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R _inst_1))) c) _inst_1 (RingCon.Quotient.nonAssocSemiring.{u1} R _inst_1 c)
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : NonAssocSemiring.{u1} R] (c : RingCon.{u1} R (Distrib.toAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R _inst_1))) (NonUnitalNonAssocSemiring.toMul.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R _inst_1))), RingHom.{u1, u1} R (RingCon.Quotient.{u1} R (Distrib.toAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R _inst_1))) (NonUnitalNonAssocSemiring.toMul.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R _inst_1)) c) _inst_1 (RingCon.instNonAssocSemiringQuotientToAddToDistribToNonUnitalNonAssocSemiringToMul.{u1} R _inst_1 c)
Case conversion may be inaccurate. Consider using '#align ring_con.mk' RingCon.mk'ₓ'. -/
/-- The natural homomorphism from a ring to its quotient by a congruence relation. -/
def mk' [NonAssocSemiring R] (c : RingCon R) : R →+* c.Quotient
    where
  toFun := Quotient.mk''
  map_zero' := rfl
  map_one' := rfl
  map_add' _ _ := rfl
  map_mul' _ _ := rfl
#align ring_con.mk' RingCon.mk'

end Quotient

end RingCon


/-
Copyright (c) 2014 Floris van Doorn. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Floris van Doorn
-/

import .basic algebra.relation algebra.binary

open eq eq.ops category

namespace morphism
  variables {ob : Type} [C : category ob] include C
  variables {a b c : ob} {g : b ⟶ c} {f : a ⟶ b} {h : b ⟶ a}
  inductive is_section    [class] (f : a ⟶ b) : Type
  := mk : ∀{g}, g ∘ f = id → is_section f
  inductive is_retraction [class] (f : a ⟶ b) : Type
  := mk : ∀{g}, f ∘ g = id → is_retraction f
  inductive is_iso        [class] (f : a ⟶ b) : Type
  := mk : ∀{g}, g ∘ f = id → f ∘ g = id → is_iso f

  attribute is_iso [multiple_instances]

  definition retraction_of (f : a ⟶ b) [H : is_section f] : hom b a :=
  is_section.rec (λg h, g) H
  definition section_of    (f : a ⟶ b) [H : is_retraction f] : hom b a :=
  is_retraction.rec (λg h, g) H
  definition inverse       (f : a ⟶ b) [H : is_iso f] : hom b a :=
  is_iso.rec (λg h1 h2, g) H

  postfix `⁻¹` := inverse

  theorem inverse_compose (f : a ⟶ b) [H : is_iso f] : f⁻¹ ∘ f = id :=
  is_iso.rec (λg h1 h2, h1) H

  theorem compose_inverse (f : a ⟶ b) [H : is_iso f] : f ∘ f⁻¹ = id :=
  is_iso.rec (λg h1 h2, h2) H

  theorem retraction_compose (f : a ⟶ b) [H : is_section f] : retraction_of f ∘ f = id :=
  is_section.rec (λg h, h) H

  theorem compose_section (f : a ⟶ b) [H : is_retraction f] : f ∘ section_of f = id :=
  is_retraction.rec (λg h, h) H

  theorem iso_imp_retraction [instance] (f : a ⟶ b) [H : is_iso f] : is_section f :=
  is_section.mk !inverse_compose

  theorem iso_imp_section [instance] (f : a ⟶ b) [H : is_iso f] : is_retraction f :=
  is_retraction.mk !compose_inverse

  theorem id_is_iso [instance] : is_iso (ID a) :=
  is_iso.mk !id_compose !id_compose

  theorem inverse_is_iso [instance] (f : a ⟶ b) [H : is_iso f] : is_iso (f⁻¹) :=
  is_iso.mk !compose_inverse !inverse_compose

  theorem left_inverse_eq_right_inverse {f : a ⟶ b} {g g' : hom b a}
      (Hl : g ∘ f = id) (Hr : f ∘ g' = id) : g = g' :=
  calc
      g = g ∘ id        : by rewrite id_right
    ... = g ∘ f ∘ g'    : by rewrite -Hr
    ... = (g ∘ f) ∘ g'  : by rewrite assoc
    ... = id ∘ g'       : by rewrite Hl
    ... = g'            : by rewrite id_left

  theorem retraction_eq_intro [H : is_section f] (H2 : f ∘ h = id) : retraction_of f = h :=
  left_inverse_eq_right_inverse !retraction_compose H2

  theorem section_eq_intro [H : is_retraction f] (H2 : h ∘ f = id) : section_of f = h :=
  symm (left_inverse_eq_right_inverse H2 !compose_section)

  theorem inverse_eq_intro_right [H : is_iso f] (H2 : f ∘ h = id) : f⁻¹ = h :=
  left_inverse_eq_right_inverse !inverse_compose H2

  theorem inverse_eq_intro_left [H : is_iso f] (H2 : h ∘ f = id) : f⁻¹ = h :=
  symm (left_inverse_eq_right_inverse H2 !compose_inverse)

  theorem section_eq_retraction (f : a ⟶ b) [Hl : is_section f] [Hr : is_retraction f] :
      retraction_of f = section_of f :=
  retraction_eq_intro !compose_section

  theorem section_retraction_imp_iso (f : a ⟶ b) [Hl : is_section f] [Hr : is_retraction f]
      : is_iso f :=
  is_iso.mk (subst (section_eq_retraction f) (retraction_compose f)) (compose_section f)

  theorem inverse_unique (H H' : is_iso f) : @inverse _ _ _ _ f H = @inverse _ _ _ _ f H' :=
  inverse_eq_intro_left !inverse_compose

  theorem inverse_involutive (f : a ⟶ b) [H : is_iso f] : (f⁻¹)⁻¹ = f :=
  inverse_eq_intro_right !inverse_compose

  theorem retraction_of_id : retraction_of (ID a) = id :=
  retraction_eq_intro !id_compose

  theorem section_of_id : section_of (ID a) = id :=
  section_eq_intro !id_compose

  theorem iso_of_id : (ID a)⁻¹ = id :=
  inverse_eq_intro_left !id_compose

  theorem composition_is_section [instance] [Hf : is_section f] [Hg : is_section g]
      : is_section (g ∘ f) :=
  is_section.mk
    (calc
      (retraction_of f ∘ retraction_of g) ∘ g ∘ f
            = retraction_of f ∘ retraction_of g ∘ g ∘ f   : by rewrite -assoc
        ... = retraction_of f ∘ (retraction_of g ∘ g) ∘ f : by rewrite (assoc _ g f)
        ... = retraction_of f ∘ id ∘ f                    : by rewrite retraction_compose
        ... = retraction_of f ∘ f                         : by rewrite id_left
        ... = id                                          : by rewrite retraction_compose)

  theorem composition_is_retraction [instance] [Hf : is_retraction f] [Hg : is_retraction g]
      : is_retraction (g ∘ f) :=
  is_retraction.mk
    (calc
      (g ∘ f) ∘ section_of f ∘ section_of g
            = g ∘ f ∘ section_of f ∘ section_of g   : by rewrite -assoc
        ... = g ∘ (f ∘ section_of f) ∘ section_of g : by rewrite -assoc
        ... = g ∘ id ∘ section_of g                 : by rewrite compose_section
        ... = g ∘ section_of g                      : by rewrite id_left
        ... = id                                    : by rewrite compose_section)

  theorem composition_is_inverse [instance] [Hf : is_iso f] [Hg : is_iso g] : is_iso (g ∘ f) :=
  !section_retraction_imp_iso

  structure isomorphic (a b : ob) :=
    (iso : a ⟶ b)
    [is_iso : is_iso iso]

  infix `≅`:50 := morphism.isomorphic

  namespace isomorphic
  open relation
  attribute is_iso [instance]

  theorem refl  (a     : ob)                           : a ≅ a := mk id
  theorem symm  ⦃a b   : ob⦄ (H  : a ≅ b)              : b ≅ a := mk (inverse (iso H))
  theorem trans ⦃a b c : ob⦄ (H1 : a ≅ b) (H2 : b ≅ c) : a ≅ c := mk (iso H2 ∘ iso H1)

  theorem is_equivalence_eq [instance] (T : Type) : is_equivalence (isomorphic : ob → ob → Type) :=
  is_equivalence.mk refl symm trans
  end isomorphic

  inductive is_mono [class] (f : a ⟶ b) : Prop :=
  mk : (∀c (g h : hom c a), f ∘ g = f ∘ h → g = h) → is_mono f
  inductive is_epi  [class] (f : a ⟶ b) : Prop :=
  mk : (∀c (g h : hom b c), g ∘ f = h ∘ f → g = h) → is_epi f

  theorem mono_elim [H : is_mono f] {g h : c ⟶ a} (H2 : f ∘ g = f ∘ h) : g = h :=
  match H with
    is_mono.mk H3 := H3 c g h H2
  end

  theorem epi_elim [H : is_epi f] {g h : b ⟶ c} (H2 : g ∘ f = h ∘ f) : g = h :=
  match H with
    is_epi.mk H3 := H3 c g h H2
  end

  theorem section_is_mono [instance] (f : a ⟶ b) [H : is_section f] : is_mono f :=
  is_mono.mk
    (λ c g h H, calc
        g = id ∘ g                    : by rewrite id_left
      ... = (retraction_of f ∘ f) ∘ g : by rewrite -(retraction_compose f)
      ... = (retraction_of f ∘ f) ∘ h : by rewrite [-assoc, H, -assoc]
      ... = id ∘ h                    : by rewrite retraction_compose
      ... = h                         : by rewrite id_left)

  theorem retraction_is_epi [instance] (f : a ⟶ b) [H : is_retraction f] : is_epi f :=
  is_epi.mk
    (λ c g h H, calc
        g = g ∘ id                 : by rewrite id_right
      ... = g ∘ f ∘ section_of f   : by rewrite -(compose_section f)
      ... = h ∘ f ∘ section_of f   : by rewrite [assoc, H, -assoc]
      ... = h ∘ id                 : by rewrite compose_section
      ... = h                      : by rewrite id_right)

  --these theorems are now proven automatically using type classes
  --should they be instances?
  theorem id_is_mono : is_mono (ID a)
  theorem id_is_epi  : is_epi  (ID a)

  theorem composition_is_mono [instance] [Hf : is_mono f] [Hg : is_mono g] : is_mono (g ∘ f) :=
  is_mono.mk
    (λ d h₁ h₂ H,
      have H2 : g ∘ (f ∘ h₁) = g ∘ (f ∘ h₂),
      begin
        rewrite *assoc, exact H
      end,
      mono_elim (mono_elim H2))

  theorem composition_is_epi  [instance] [Hf : is_epi f] [Hg : is_epi g] : is_epi  (g ∘ f) :=
  is_epi.mk
    (λ d h₁ h₂ H,
      have H2 : (h₁ ∘ g) ∘ f = (h₂ ∘ g) ∘ f,
      begin
        rewrite -*assoc, exact H
      end,
      epi_elim (epi_elim H2))
end morphism
namespace morphism
--rewrite lemmas for inverses, modified from
--https://github.com/JasonGross/HoTT-categories/blob/master/theories/Categories/Category/Morphisms.v
  namespace iso
  section
  variables {ob : Type} [C : category ob] include C
  variables {a b c d : ob}
  variables (f : b ⟶ a) (r : c ⟶ d) (q : b ⟶ c) (p : a ⟶ b)
  variables (g : d ⟶ c)

  variable [Hq : is_iso q] include Hq
  theorem compose_pV : q ∘ q⁻¹ = id := !compose_inverse
  theorem compose_Vp : q⁻¹ ∘ q = id := !inverse_compose
  theorem compose_V_pp : q⁻¹ ∘ (q ∘ p) = p :=
  calc
    q⁻¹ ∘ (q ∘ p) = (q⁻¹ ∘ q) ∘ p : by rewrite assoc
              ... = id ∘ p        : by rewrite inverse_compose
              ... = p             : by rewrite id_left

  theorem compose_p_Vp : q ∘ (q⁻¹ ∘ g) = g :=
  calc
     q ∘ (q⁻¹ ∘ g)  = (q ∘ q⁻¹) ∘ g : by rewrite assoc
                ... = id ∘ g        : by rewrite compose_inverse
                ... = g             : by rewrite id_left

  theorem compose_pp_V : (r ∘ q) ∘ q⁻¹ = r :=
  calc
    (r ∘ q) ∘ q⁻¹ = r ∘ q ∘ q⁻¹ : by rewrite assoc
      ... = r ∘ id              : by rewrite compose_inverse
      ... = r                   : by rewrite id_right

  theorem compose_pV_p : (f ∘ q⁻¹) ∘ q = f :=
  calc
    (f ∘ q⁻¹) ∘ q = f ∘ q⁻¹ ∘ q : by rewrite assoc
              ... = f ∘ id      : by rewrite inverse_compose
              ... = f           : by rewrite id_right

  theorem inv_pp [H' : is_iso p] : (q ∘ p)⁻¹ = p⁻¹ ∘ q⁻¹ :=
  inverse_eq_intro_left
    (show (p⁻¹ ∘ (q⁻¹)) ∘ q ∘ p = id, from
     by rewrite [-assoc, compose_V_pp, inverse_compose])

  theorem inv_Vp [H' : is_iso g] : (q⁻¹ ∘ g)⁻¹ = g⁻¹ ∘ q := inverse_involutive q ▸ inv_pp (q⁻¹) g
  theorem inv_pV [H' : is_iso f] : (q ∘ f⁻¹)⁻¹ = f ∘ q⁻¹ := inverse_involutive f ▸ inv_pp q (f⁻¹)
  theorem inv_VV [H' : is_iso r] : (q⁻¹ ∘ r⁻¹)⁻¹ = r ∘ q := inverse_involutive r ▸ inv_Vp q (r⁻¹)
  end
  section
  variables {ob : Type} {C : category ob} include C
  variables {d c b a : ob}
  variables {i : b ⟶ c} {f : b ⟶ a}
            {r : c ⟶ d} {q : b ⟶ c} {p : a ⟶ b}
            {g : d ⟶ c} {h : c ⟶ b}
            {x : b ⟶ d} {z : a ⟶ c}
            {y : d ⟶ b} {w : c ⟶ a}
  variable [Hq : is_iso q] include Hq

  theorem moveR_Mp (H : y = q⁻¹ ∘ g) : q ∘ y = g := H⁻¹ ▸ compose_p_Vp q g
  theorem moveR_pM (H : w = f ∘ q⁻¹) : w ∘ q = f := H⁻¹ ▸ compose_pV_p f q
  theorem moveR_Vp (H : z = q ∘ p) : q⁻¹ ∘ z = p := H⁻¹ ▸ compose_V_pp q p
  theorem moveR_pV (H : x = r ∘ q) : x ∘ q⁻¹ = r := H⁻¹ ▸ compose_pp_V r q
  theorem moveL_Mp (H : q⁻¹ ∘ g = y) : g = q ∘ y := (moveR_Mp (H⁻¹))⁻¹
  theorem moveL_pM (H : f ∘ q⁻¹ = w) : f = w ∘ q := (moveR_pM (H⁻¹))⁻¹
  theorem moveL_Vp (H : q ∘ p = z) : p = q⁻¹ ∘ z := (moveR_Vp (H⁻¹))⁻¹
  theorem moveL_pV (H : r ∘ q = x) : r = x ∘ q⁻¹ := (moveR_pV (H⁻¹))⁻¹
  theorem moveL_1V (H : h ∘ q = id) : h = q⁻¹ := (inverse_eq_intro_left H)⁻¹
  theorem moveL_V1 (H : q ∘ h = id) : h = q⁻¹ := (inverse_eq_intro_right H)⁻¹
  theorem moveL_1M (H : i ∘ q⁻¹ = id) : i = q := moveL_1V H ⬝ inverse_involutive q
  theorem moveL_M1 (H : q⁻¹ ∘ i = id) : i = q := moveL_V1 H ⬝ inverse_involutive q
  theorem moveR_1M (H : id = i ∘ q⁻¹) : q = i := (moveL_1M (H⁻¹))⁻¹
  theorem moveR_M1 (H : id = q⁻¹ ∘ i) : q = i := (moveL_M1 (H⁻¹))⁻¹
  theorem moveR_1V (H : id = h ∘ q) : q⁻¹ = h := (moveL_1V (H⁻¹))⁻¹
  theorem moveR_V1 (H : id = q ∘ h) : q⁻¹ = h := (moveL_V1 (H⁻¹))⁻¹
  end
  end iso

end morphism


/-
Copyright (c) 2020 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison

! This file was ported from Lean 3 source module algebra.category.Group.biproducts
! leanprover-community/mathlib commit 234ddfeaa5572bc13716dd215c6444410a679a8e
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.Group.Pi
import Mathbin.Algebra.Category.Group.Preadditive
import Mathbin.CategoryTheory.Preadditive.Biproducts
import Mathbin.Algebra.Category.Group.Limits

/-!
# The category of abelian groups has finite biproducts
-/

open CategoryTheory

open CategoryTheory.Limits

open BigOperators

universe w u

namespace AddCommGroupCat

-- As `AddCommGroup` is preadditive, and has all limits, it automatically has biproducts.
instance : HasBinaryBiproducts AddCommGroupCat :=
  HasBinaryBiproducts.of_hasBinaryProducts

instance : HasFiniteBiproducts AddCommGroupCat :=
  HasFiniteBiproducts.of_hasFiniteProducts

-- We now construct explicit limit data,
-- so we can compare the biproducts to the usual unbundled constructions.
/-- Construct limit data for a binary product in `AddCommGroup`, using `AddCommGroup.of (G × H)`.
-/
@[simps cone_x isLimit_lift]
def binaryProductLimitCone (G H : AddCommGroupCat.{u}) : Limits.LimitCone (pair G H)
    where
  Cone :=
    { pt := AddCommGroupCat.of (G × H)
      π :=
        { app := fun j =>
            Discrete.casesOn j fun j =>
              WalkingPair.casesOn j (AddMonoidHom.fst G H) (AddMonoidHom.snd G H)
          naturality' := by rintro ⟨⟨⟩⟩ ⟨⟨⟩⟩ ⟨⟨⟨⟩⟩⟩ <;> rfl } }
  IsLimit :=
    { lift := fun s => AddMonoidHom.prod (s.π.app ⟨WalkingPair.left⟩) (s.π.app ⟨WalkingPair.right⟩)
      fac := by
        rintro s (⟨⟩ | ⟨⟩) <;>
          · ext x
            simp
      uniq := fun s m w => by
        ext <;> [rw [← w ⟨walking_pair.left⟩], rw [← w ⟨walking_pair.right⟩]] <;> rfl }
#align AddCommGroup.binary_product_limit_cone AddCommGroupCat.binaryProductLimitCone

@[simp]
theorem binaryProductLimitCone_cone_π_app_left (G H : AddCommGroupCat.{u}) :
    (binaryProductLimitCone G H).Cone.π.app ⟨WalkingPair.left⟩ = AddMonoidHom.fst G H :=
  rfl
#align AddCommGroup.binary_product_limit_cone_cone_π_app_left AddCommGroupCat.binaryProductLimitCone_cone_π_app_left

@[simp]
theorem binaryProductLimitCone_cone_π_app_right (G H : AddCommGroupCat.{u}) :
    (binaryProductLimitCone G H).Cone.π.app ⟨WalkingPair.right⟩ = AddMonoidHom.snd G H :=
  rfl
#align AddCommGroup.binary_product_limit_cone_cone_π_app_right AddCommGroupCat.binaryProductLimitCone_cone_π_app_right

/-- We verify that the biproduct in AddCommGroup is isomorphic to
the cartesian product of the underlying types:
-/
@[simps hom_apply]
noncomputable def biprodIsoProd (G H : AddCommGroupCat.{u}) :
    (G ⊞ H : AddCommGroupCat) ≅ AddCommGroupCat.of (G × H) :=
  IsLimit.conePointUniqueUpToIso (BinaryBiproduct.isLimit G H) (binaryProductLimitCone G H).IsLimit
#align AddCommGroup.biprod_iso_prod AddCommGroupCat.biprodIsoProd

@[simp, elementwise]
theorem biprodIsoProd_inv_comp_fst (G H : AddCommGroupCat.{u}) :
    (biprodIsoProd G H).inv ≫ biprod.fst = AddMonoidHom.fst G H :=
  IsLimit.conePointUniqueUpToIso_inv_comp _ _ (Discrete.mk WalkingPair.left)
#align AddCommGroup.biprod_iso_prod_inv_comp_fst AddCommGroupCat.biprodIsoProd_inv_comp_fst

@[simp, elementwise]
theorem biprodIsoProd_inv_comp_snd (G H : AddCommGroupCat.{u}) :
    (biprodIsoProd G H).inv ≫ biprod.snd = AddMonoidHom.snd G H :=
  IsLimit.conePointUniqueUpToIso_inv_comp _ _ (Discrete.mk WalkingPair.right)
#align AddCommGroup.biprod_iso_prod_inv_comp_snd AddCommGroupCat.biprodIsoProd_inv_comp_snd

namespace HasLimit

variable {J : Type w} (f : J → AddCommGroupCat.{max w u})

/-- The map from an arbitrary cone over a indexed family of abelian groups
to the cartesian product of those groups.
-/
@[simps]
def lift (s : Fan f) : s.pt ⟶ AddCommGroupCat.of (∀ j, f j)
    where
  toFun x j := s.π.app ⟨j⟩ x
  map_zero' := by
    ext
    simp
  map_add' x y := by
    ext
    simp
#align AddCommGroup.has_limit.lift AddCommGroupCat.HasLimit.lift

/-- Construct limit data for a product in `AddCommGroup`, using `AddCommGroup.of (Π j, F.obj j)`.
-/
@[simps]
def productLimitCone : Limits.LimitCone (Discrete.functor f)
    where
  Cone :=
    { pt := AddCommGroupCat.of (∀ j, f j)
      π := Discrete.natTrans fun j => Pi.evalAddMonoidHom (fun j => f j) j.as }
  IsLimit :=
    { lift := lift f
      fac := fun s j => by
        cases j
        ext
        simp
      uniq := fun s m w => by
        ext (x j)
        dsimp only [has_limit.lift]
        simp only [AddMonoidHom.coe_mk]
        exact congr_arg (fun g : s.X ⟶ f j => (g : s.X → f j) x) (w ⟨j⟩) }
#align AddCommGroup.has_limit.product_limit_cone AddCommGroupCat.HasLimit.productLimitCone

end HasLimit

open HasLimit

variable {J : Type} [Fintype J]

/-- We verify that the biproduct we've just defined is isomorphic to the AddCommGroup structure
on the dependent function type
-/
@[simps hom_apply]
noncomputable def biproductIsoPi (f : J → AddCommGroupCat.{u}) :
    (⨁ f : AddCommGroupCat) ≅ AddCommGroupCat.of (∀ j, f j) :=
  IsLimit.conePointUniqueUpToIso (biproduct.isLimit f) (productLimitCone f).IsLimit
#align AddCommGroup.biproduct_iso_pi AddCommGroupCat.biproductIsoPi

@[simp, elementwise]
theorem biproductIsoPi_inv_comp_π (f : J → AddCommGroupCat.{u}) (j : J) :
    (biproductIsoPi f).inv ≫ biproduct.π f j = Pi.evalAddMonoidHom (fun j => f j) j :=
  IsLimit.conePointUniqueUpToIso_inv_comp _ _ (Discrete.mk j)
#align AddCommGroup.biproduct_iso_pi_inv_comp_π AddCommGroupCat.biproductIsoPi_inv_comp_π

end AddCommGroupCat


import tactic
noncomputable theory

/-
Design your own short register machine program and either
(i) prove that it halts,
(ii) prove that it doesn't halt, or
(iii) prove that ψₚ is finitely satisfiable [this is equivalent to (i) actually, but more work]
following the examples indicated below.

You should indicate what the register machine program is;
you don't need to include the "informal proof" of whether it halts or not.
-/

namespace does_halt

constant A : Type
constant O : A → A → Prop
constant c : A
constant f : A → A
constant R : A → A → A → Prop
-- R s L m says that at after s stages completed,
-- we are at line L with m in register R₀

def bar0 : A := f c
def bar1 : A := f bar0
def bar2 : A := f bar1
def bar3 : A := f bar2
def bar4 : A := f bar3

local infix `<` := O
def ψ₀ : Prop :=
  (∀ x y z, x < y → y < z → x < z) ∧
  (∀ x, ¬ x < x) ∧
  (∀ x y, x < y ∨ x = y ∨ y < x) ∧
  (∀ x, c < x ∨ c = x) ∧
  (∀ x, x < f x ∨ x = f x) ∧
  (∀ x, (∃ y, x < y) → x < f x ∧
  ∀ z, x < z → f x < z ∨ f x = z)
  /-
  Program that halts:
  0 if R₀=◫ then 2 else 1
  1 if R₀=◫ then 1 else 1 (goto 1)
  2 R₀ = R₀ + |
  3 print
  4 halt
  Uses only one register so n=0.
  -/

  def ψα₀ : Prop :=
    ∀ x y₀, (R x bar0 y₀ → x < f x ∧ ((y₀ = bar0 ∧ R (f x) bar2 y₀)
    ∨ (¬ y₀ = bar0 ∧ R (f x) bar1 y₀)))

  def ψα₁ : Prop :=
    ∀ x y₀, R x bar1 y₀ → x < f x ∧ (y₀ = bar0 ∧ R (f x) bar1 y₀)
    ∨ (¬ y₀ = bar0 ∧ R (f x) bar1 y₀)

  def ψα₂ : Prop := ∀ x y₀, R x bar2 y₀ → x < f x ∧ R (f x) bar3 (f y₀)
  def ψα₃ : Prop := ∀ x y₀, R x bar3 y₀ → x < f x ∧ R (f x) bar4 y₀

  def ψₚ := ψ₀ ∧ (R bar0 bar0 bar0) ∧ ψα₀ ∧ ψα₁ ∧ ψα₂ ∧ ψα₃

  def φₚ := ψₚ → ∃ x, ∃ y₀, R x bar4 y₀

  theorem this_program_does_halt :  φₚ :=
  λ hψₚ,
    have h0: R bar0 bar0 bar0, from hψₚ.2.1,
    have hψα₀ : ψα₀,           from hψₚ.2.2.1,
    have hψα₁ : ψα₁,           from hψₚ.2.2.2.1,
    have hψα₂ : ψα₂,           from hψₚ.2.2.2.2.1,
    have hψα₃ : ψα₃,           from hψₚ.2.2.2.2.2,
    have h1: R bar1 bar2 bar0, from (hψα₀ bar0 bar0 h0).2.elim (λ h0, h0.2) (λ h0, false.elim (h0.1 (eq.refl _))),
    have h2: R bar2 bar3 bar1, from (hψα₂ bar1 bar0 h1).2,
    have h3: R bar3 bar4 bar1, from (hψα₃ bar2 bar1 h2).2,
    exists.intro bar3 (exists.intro bar1 (h3))

  /- Informal proof:
  Assume ψₚ. In particular R bar0 bar0 bar0
  (we start with empty register at line 0 at time 0).
  Then we apply ψα₀ with x=bar0 and y=bar0,
  to get R bar1 bar2 bar0.
  Then we apply ψα₂ to get R bar2 bar3 bar1.
  Then we apply ψα₃ to get R bar3 bar4 bar1.
  Then we use exists.intro to finish.
  -/

theorem finitely_satisfiable :
  ∃ A : Type, ∃ T: fintype A,
  ∃ O : A → A → Prop,
  ∃ c : A,
  ∃ f : A → A,
  ∃ R : A → A → A → Prop,
  ψₚ := exists.intro (fin 5) (
    exists.intro (fin.fintype 5) (
      exists.intro (
        λ x y, nat.lt x.1 y.1
      ) (
        exists.intro 0 (
          exists.intro (λ x, ite (x=4) (nat.succ x.1) 4) (
            exists.intro (
              λ x y z, ((x.1,y.1,z.1)=(0,0,0)) -- h0
              ∨ (x.1,y.1,z.1)=(1,2,0)
              ∨ (x.1,y.1,z.1)=(2,3,1)
              ∨ (x.1,y.1,z.1)=(3,4,1)
              -- spell out the computation using h0 h1 h2 h3: 000 120 231 341
            ) (
              and.intro (and.intro (
                λ x y z hxy hyz,

                -- have x.1 < y.1, from hxy,
                sorry --nat.lt_trans hxy hyz -- show < on fin 5 is transitive
              ) (
                sorry
              )
              ) (
                and.intro sorry (
                  and.intro sorry (
                    and.intro sorry (
                      and.intro sorry sorry
                    )
                  )
                )
              )
            )
          )
        )
      )
    )
  )

end does_halt

namespace does_not_halt

def A := ℕ
def O := (λ x y, nat.lt x y)
def c : ℕ := 0
def f := nat.succ
def bar0 := c
def bar1 := f c
def R : ℕ → ℕ → ℕ → Prop := (λ s L r:ℕ, L=bar0 ∧ r=bar0)

local infix `<` := O
def ψ₀ : Prop :=
  (∀ x y z, x < y → y < z → x < z) ∧
  (∀ x, ¬ x < x) ∧
  (∀ x y, x < y ∨ x = y ∨ y < x) ∧
  (∀ x, c < x ∨ c = x) ∧
  (∀ x, x < f x ∨ x = f x) ∧
  (∀ x, (∃ y, x < y) → x < f x ∧
  ∀ z, x < z → f x < z ∨ f x = z)

  /-
  Program that does not halt:
  0 if R₀=◫ then 0 else 0
  1 halt
  Uses only one register so n=0.
  -/

def PQ : Prop := ∃! x : ℕ, x =x

  def ψα₀ : Prop :=
    ∀ x y₀, (R x bar0 y₀ → x < f x ∧ ((y₀ = bar0 ∧ R (f x) bar0 y₀)
    ∨ (¬ y₀ = bar0 ∧ R (f x) bar0 y₀)))

  def ψₚ := ψ₀ ∧ (R bar0 bar0 bar0) ∧ ψα₀

  def φₚ := ψₚ → ∃ x, ∃ y₀, R x bar1 y₀

  theorem this_program_does_not_halt : ¬ φₚ :=

          λ hφₚ,
          have hψₚ: ψₚ, from and.intro (
            show (∀ x y z, x < y → y < z → x < z) ∧
            (∀ x, ¬ x < x) ∧
            (∀ x y, x < y ∨ x = y ∨ y < x) ∧
            (∀ x, c < x ∨ c = x) ∧
            (∀ x, x < f x ∨ x = f x) ∧
            (∀ x, (∃ y, x < y) → x < f x ∧
            ∀ z, x < z → f x < z ∨ f x = z)
          , from
            and.intro (λ x y z, nat.lt_trans) (
              and.intro (
                λ h, (lt_self_iff_false h).mp
              ) (
                and.intro (
                  λ x y : ℕ, show nat.lt x y ∨ x = y ∨ nat.lt y x, from trichotomous x y
                ) (
                  and.intro (
                    λ x,
                    lt_or_eq_of_le (zero_le x)
                  ) (
                    and.intro (
                      λ x, or.inl (nat.lt_succ_self x)
                    ) (
                      λ x hx, and.intro (nat.lt_succ_self x) (
                        λ z hz, lt_or_eq_of_le (nat.succ_le_of_lt hz)
                      )
                    )
                  )
                )
              )
            )
          ) (
            and.intro (
              and.intro rfl rfl
            ) (
              λ x y₀, λ hR, and.intro (
                nat.lt_succ_self _
              ) (
                or.inl (
                  and.intro hR.2 (and.intro rfl hR.2)
                )
              )
            )
          ),

          have ∃ x, ∃ y₀, R x bar1 y₀, from hφₚ hψₚ,
          exists.elim this (
            λ x hx,
            exists.elim hx (
              λ y₀ hy₀,
              have hR1: R bar0 bar1 y₀, from hy₀,
              have bar1 = bar0 ∧ y₀ = bar0, from by {rw R at hR1,exact hR1,},
              have bar0 = bar1, from this.1.symm,
              nat.zero_ne_one this
            )
          )
end does_not_halt


structure HeapNodeAux (α : Type u) (h : Type u) where
  val : α
  rank : Nat
  children : h

inductive Heap (α : Type u) : Type u where
  | heap (ns : List (HeapNodeAux α (Heap α))) : Heap α
  deriving Inhabited

open Heap

abbrev BinTree α := HeapNodeAux α (Heap α)

def Heap.nodes : Heap α → List (BinTree α)
  | heap ns => ns

def hRank : List (BinTree α) → Nat
  | []   => 0
  | h::_ => h.rank

@[specialize] def combine (le : α → α → Bool) (n₁ n₂ : BinTree α) : BinTree α :=
  if le n₂.val n₁.val then
     { n₂ with rank := n₂.rank + 1, children := heap $ n₂.children.nodes ++ [n₁] }
  else
     { n₁ with rank := n₁.rank + 1, children := heap $ n₁.children.nodes ++ [n₂] }

@[specialize] def mergeNodes (le : α → α → Bool) : List (BinTree α) → List (BinTree α) → List (BinTree α)
  | [], h  => h
  | h,  [] => h
  | f@(h₁ :: t₁), s@(h₂ :: t₂) =>
    if h₁.rank < h₂.rank then h₁ :: mergeNodes le t₁ s
    else if h₂.rank < h₁.rank then h₂ :: mergeNodes le t₂ f
    else
      let merged := combine le h₁ h₂
      let r      := merged.rank
      if r != hRank t₁ then
        if r != hRank t₂ then merged :: mergeNodes le t₁ t₂ else mergeNodes le (merged :: t₁) t₂
      else
        if r != hRank t₂ then mergeNodes le t₁ (merged :: t₂) else merged :: mergeNodes le t₁ t₂
termination_by _ h₁ h₂ => h₁.length + h₂.length
decreasing_by simp_wf; simp_arith [*]

theorem temp : mergeNodes le [] h = h := by
  unfold mergeNodes