/-
Copyright (c) 2019 Johan Commelin. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johan Commelin, Fabian Glöckle
-/
import linear_algebra.finite_dimensional
import linear_algebra.projection
import linear_algebra.sesquilinear_form

/-!
# Dual vector spaces

The dual space of an R-module M is the R-module of linear maps `M → R`.

## Main definitions

* `dual R M` defines the dual space of M over R.
* Given a basis for an `R`-module `M`, `basis.to_dual` produces a map from `M` to `dual R M`.
* Given families of vectors `e` and `ε`, `dual_pair e ε` states that these families have the
  characteristic properties of a basis and a dual.
* `dual_annihilator W` is the submodule of `dual R M` where every element annihilates `W`.

## Main results

* `to_dual_equiv` : the linear equivalence between the dual module and primal module,
  given a finite basis.
* `dual_pair.basis` and `dual_pair.eq_dual`: if `e` and `ε` form a dual pair, `e` is a basis and
  `ε` is its dual basis.
* `quot_equiv_annihilator`: the quotient by a subspace is isomorphic to its dual annihilator.

## Notation

We sometimes use `V'` as local notation for `dual K V`.

## TODO

Erdös-Kaplansky theorem about the dimension of a dual vector space in case of infinite dimension.
-/

noncomputable theory

namespace module

variables (R : Type*) (M : Type*)
variables [comm_semiring R] [add_comm_monoid M] [module R M]

/-- The dual space of an R-module M is the R-module of linear maps `M → R`. -/
@[derive [add_comm_monoid, module R]] def dual := M →ₗ[R] R

instance {S : Type*} [comm_ring S] {N : Type*} [add_comm_group N] [module S N] :
  add_comm_group (dual S N) := linear_map.add_comm_group

instance : add_monoid_hom_class (dual R M) M R :=
linear_map.add_monoid_hom_class

/-- The canonical pairing of a vector space and its algebraic dual. -/
def dual_pairing (R M) [comm_semiring R] [add_comm_monoid M] [module R M] :
  module.dual R M →ₗ[R] M →ₗ[R] R := linear_map.id

@[simp] lemma dual_pairing_apply (v x) : dual_pairing R M v x = v x := rfl

namespace dual

instance : inhabited (dual R M) := linear_map.inhabited

instance : has_coe_to_fun (dual R M) (λ _, M → R) := ⟨linear_map.to_fun⟩

/-- Maps a module M to the dual of the dual of M. See `module.erange_coe` and
`module.eval_equiv`. -/
def eval : M →ₗ[R] (dual R (dual R M)) := linear_map.flip linear_map.id

@[simp] lemma eval_apply (v : M) (a : dual R M) : eval R M v a = a v :=
begin
  dunfold eval,
  rw [linear_map.flip_apply, linear_map.id_apply]
end

variables {R M} {M' : Type*} [add_comm_monoid M'] [module R M']

/-- The transposition of linear maps, as a linear map from `M →ₗ[R] M'` to
`dual R M' →ₗ[R] dual R M`. -/
def transpose : (M →ₗ[R] M') →ₗ[R] (dual R M' →ₗ[R] dual R M) :=
(linear_map.llcomp R M M' R).flip

lemma transpose_apply (u : M →ₗ[R] M') (l : dual R M') : transpose u l = l.comp u := rfl

variables {M'' : Type*} [add_comm_monoid M''] [module R M'']

lemma transpose_comp (u : M' →ₗ[R] M'') (v : M →ₗ[R] M') :
  transpose (u.comp v) = (transpose v).comp (transpose u) := rfl

end dual

end module

namespace basis

universes u v w

open module module.dual submodule linear_map cardinal function

variables {R M K V ι : Type*}

section comm_semiring

variables [comm_semiring R] [add_comm_monoid M] [module R M] [decidable_eq ι]
variables (b : basis ι R M)

/-- The linear map from a vector space equipped with basis to its dual vector space,
taking basis elements to corresponding dual basis elements. -/
def to_dual : M →ₗ[R] module.dual R M :=
b.constr ℕ $ λ v, b.constr ℕ $ λ w, if w = v then (1 : R) else 0

lemma to_dual_apply (i j : ι) :
  b.to_dual (b i) (b j) = if i = j then 1 else 0 :=
by { erw [constr_basis b, constr_basis b], ac_refl }

@[simp] lemma to_dual_total_left (f : ι →₀ R) (i : ι) :
  b.to_dual (finsupp.total ι M R b f) (b i) = f i :=
begin
  rw [finsupp.total_apply, finsupp.sum, linear_map.map_sum, linear_map.sum_apply],
  simp_rw [linear_map.map_smul, linear_map.smul_apply, to_dual_apply, smul_eq_mul,
           mul_boole, finset.sum_ite_eq'],
  split_ifs with h,
  { refl },
  { rw finsupp.not_mem_support_iff.mp h }
end

@[simp] lemma to_dual_total_right (f : ι →₀ R) (i : ι) :
  b.to_dual (b i) (finsupp.total ι M R b f) = f i :=
begin
  rw [finsupp.total_apply, finsupp.sum, linear_map.map_sum],
  simp_rw [linear_map.map_smul, to_dual_apply, smul_eq_mul, mul_boole, finset.sum_ite_eq],
  split_ifs with h,
  { refl },
  { rw finsupp.not_mem_support_iff.mp h }
end

lemma to_dual_apply_left (m : M) (i : ι) : b.to_dual m (b i) = b.repr m i :=
by rw [← b.to_dual_total_left, b.total_repr]

lemma to_dual_apply_right (i : ι) (m : M) : b.to_dual (b i) m = b.repr m i :=
by rw [← b.to_dual_total_right, b.total_repr]

lemma coe_to_dual_self (i : ι) : b.to_dual (b i) = b.coord i :=
by { ext, apply to_dual_apply_right }

/-- `h.to_dual_flip v` is the linear map sending `w` to `h.to_dual w v`. -/
def to_dual_flip (m : M) : (M →ₗ[R] R) := b.to_dual.flip m

lemma to_dual_flip_apply (m₁ m₂ : M) : b.to_dual_flip m₁ m₂ = b.to_dual m₂ m₁ := rfl

lemma to_dual_eq_repr (m : M) (i : ι) : b.to_dual m (b i) = b.repr m i :=
b.to_dual_apply_left m i

lemma to_dual_eq_equiv_fun [fintype ι] (m : M) (i : ι) : b.to_dual m (b i) = b.equiv_fun m i :=
by rw [b.equiv_fun_apply, to_dual_eq_repr]

lemma to_dual_inj (m : M) (a : b.to_dual m = 0) : m = 0 :=
begin
  rw [← mem_bot R, ← b.repr.ker, mem_ker, linear_equiv.coe_coe],
  apply finsupp.ext,
  intro b,
  rw [← to_dual_eq_repr, a],
  refl
end

theorem to_dual_ker : b.to_dual.ker = ⊥ :=
ker_eq_bot'.mpr b.to_dual_inj

theorem to_dual_range [fin : fintype ι] : b.to_dual.range = ⊤ :=
begin
  rw eq_top_iff',
  intro f,
  rw linear_map.mem_range,
  let lin_comb : ι →₀ R := finsupp.on_finset fin.elems (λ i, f.to_fun (b i)) _,
  { use finsupp.total ι M R b lin_comb,
    apply b.ext,
    { intros i,
      rw [b.to_dual_eq_repr _ i, repr_total b],
      { refl } } },
  { intros a _,
    apply fin.complete }
end

end comm_semiring

section comm_ring

variables [comm_ring R] [add_comm_group M] [module R M] [decidable_eq ι]
variables (b : basis ι R M)

/-- A vector space is linearly equivalent to its dual space. -/
@[simps]
def to_dual_equiv [fintype ι] : M ≃ₗ[R] (dual R M) :=
linear_equiv.of_bijective b.to_dual
  (ker_eq_bot.mp b.to_dual_ker) (range_eq_top.mp b.to_dual_range)

/-- Maps a basis for `V` to a basis for the dual space. -/
def dual_basis [fintype ι] : basis ι R (dual R M) :=
b.map b.to_dual_equiv

-- We use `j = i` to match `basis.repr_self`
lemma dual_basis_apply_self [fintype ι] (i j : ι) :
  b.dual_basis i (b j) = if j = i then 1 else 0 :=
by { convert b.to_dual_apply i j using 2, rw @eq_comm _ j i }

lemma total_dual_basis [fintype ι] (f : ι →₀ R) (i : ι) :
  finsupp.total ι (dual R M) R b.dual_basis f (b i) = f i :=
begin
  rw [finsupp.total_apply, finsupp.sum_fintype, linear_map.sum_apply],
  { simp_rw [linear_map.smul_apply, smul_eq_mul, dual_basis_apply_self, mul_boole,
      finset.sum_ite_eq, if_pos (finset.mem_univ i)] },
  { intro, rw zero_smul },
end

lemma dual_basis_repr [fintype ι] (l : dual R M) (i : ι) :
  b.dual_basis.repr l i = l (b i) :=
by rw [← total_dual_basis b, basis.total_repr b.dual_basis l]

lemma dual_basis_equiv_fun [fintype ι] (l : dual R M) (i : ι) :
  b.dual_basis.equiv_fun l i = l (b i) :=
by rw [basis.equiv_fun_apply, dual_basis_repr]

lemma dual_basis_apply [fintype ι] (i : ι) (m : M) : b.dual_basis i m = b.repr m i :=
b.to_dual_apply_right i m

@[simp] lemma coe_dual_basis [fintype ι] :
  ⇑b.dual_basis = b.coord :=
by { ext i x, apply dual_basis_apply }

@[simp] lemma to_dual_to_dual [fintype ι] :
  b.dual_basis.to_dual.comp b.to_dual = dual.eval R M :=
begin
  refine b.ext (λ i, b.dual_basis.ext (λ j, _)),
  rw [linear_map.comp_apply, to_dual_apply_left, coe_to_dual_self, ← coe_dual_basis,
      dual.eval_apply, basis.repr_self, finsupp.single_apply, dual_basis_apply_self]
end

theorem eval_ker {ι : Type*} (b : basis ι R M) :
  (dual.eval R M).ker = ⊥ :=
begin
  rw ker_eq_bot',
  intros m hm,
  simp_rw [linear_map.ext_iff, dual.eval_apply, zero_apply] at hm,
  exact (basis.forall_coord_eq_zero_iff _).mp (λ i, hm (b.coord i))
end

lemma eval_range {ι : Type*} [fintype ι] (b : basis ι R M) :
  (eval R M).range = ⊤ :=
begin
  classical,
  rw [← b.to_dual_to_dual, range_comp, b.to_dual_range, map_top, to_dual_range _],
  apply_instance
end

/-- A module with a basis is linearly equivalent to the dual of its dual space. -/
def eval_equiv  {ι : Type*} [fintype ι] (b : basis ι R M) : M ≃ₗ[R] dual R (dual R M) :=
linear_equiv.of_bijective (eval R M)
  (ker_eq_bot.mp b.eval_ker) (range_eq_top.mp b.eval_range)

@[simp] lemma eval_equiv_to_linear_map {ι : Type*} [fintype ι] (b : basis ι R M) :
  (b.eval_equiv).to_linear_map = dual.eval R M := rfl

end comm_ring

/-- `simp` normal form version of `total_dual_basis` -/
@[simp] lemma total_coord [comm_ring R] [add_comm_group M] [module R M] [fintype ι]
  (b : basis ι R M) (f : ι →₀ R) (i : ι) :
  finsupp.total ι (dual R M) R b.coord f (b i) = f i :=
by { haveI := classical.dec_eq ι, rw [← coe_dual_basis, total_dual_basis] }

-- TODO(jmc): generalize to rings, once `module.rank` is generalized
theorem dual_dim_eq [field K] [add_comm_group V] [module K V] [fintype ι] (b : basis ι K V) :
  cardinal.lift (module.rank K V) = module.rank K (dual K V) :=
begin
  classical,
  have := linear_equiv.lift_dim_eq b.to_dual_equiv,
  simp only [cardinal.lift_umax] at this,
  rw [this, ← cardinal.lift_umax],
  apply cardinal.lift_id,
end

end basis

namespace module

variables {K V : Type*}
variables [field K] [add_comm_group V] [module K V]
open module module.dual submodule linear_map cardinal basis finite_dimensional

theorem eval_ker : (eval K V).ker = ⊥ :=
by { classical, exact (basis.of_vector_space K V).eval_ker }

-- TODO(jmc): generalize to rings, once `module.rank` is generalized
theorem dual_dim_eq [finite_dimensional K V] :
  cardinal.lift (module.rank K V) = module.rank K (dual K V) :=
(basis.of_vector_space K V).dual_dim_eq

lemma erange_coe [finite_dimensional K V] : (eval K V).range = ⊤ :=
begin
  letI : is_noetherian K V := is_noetherian.iff_fg.2 infer_instance,
  exact (basis.of_vector_space K V).eval_range
end

variables (K V)

/-- A vector space is linearly equivalent to the dual of its dual space. -/
def eval_equiv [finite_dimensional K V] : V ≃ₗ[K] dual K (dual K V) :=
linear_equiv.of_bijective (eval K V)
  (ker_eq_bot.mp eval_ker) (range_eq_top.mp erange_coe)

variables {K V}

@[simp] lemma eval_equiv_to_linear_map [finite_dimensional K V] :
  (eval_equiv K V).to_linear_map = dual.eval K V := rfl

end module

section dual_pair

open module

variables {R M ι : Type*}
variables [comm_ring R] [add_comm_group M] [module R M] [decidable_eq ι]

/-- `e` and `ε` have characteristic properties of a basis and its dual -/
@[nolint has_inhabited_instance]
structure dual_pair (e : ι → M) (ε : ι → (dual R M)) :=
(eval : ∀ i j : ι, ε i (e j) = if i = j then 1 else 0)
(total : ∀ {m : M}, (∀ i, ε i m = 0) → m = 0)
[finite : ∀ m : M, fintype {i | ε i m ≠ 0}]

end dual_pair

namespace dual_pair

open module module.dual linear_map function

variables {R M ι : Type*}
variables [comm_ring R] [add_comm_group M] [module R M]
variables {e : ι → M} {ε : ι → dual R M}

/-- The coefficients of `v` on the basis `e` -/
def coeffs [decidable_eq ι] (h : dual_pair e ε) (m : M) : ι →₀ R :=
{ to_fun := λ i, ε i m,
  support := by { haveI := h.finite m, exact {i : ι | ε i m ≠ 0}.to_finset },
  mem_support_to_fun := by {intro i, rw set.mem_to_finset, exact iff.rfl } }

@[simp]

/-- linear combinations of elements of `e`.
This is a convenient abbreviation for `finsupp.total _ M R e l` -/
def lc {ι} (e : ι → M) (l : ι →₀ R) : M := l.sum (λ (i : ι) (a : R), a • (e i))

lemma lc_def (e : ι → M) (l : ι →₀ R) : lc e l = finsupp.total _ _ _ e l := rfl

variables [decidable_eq ι] (h : dual_pair e ε)
include h

lemma dual_lc (l : ι →₀ R) (i : ι) : ε i (dual_pair.lc e l) = l i :=
begin
  erw linear_map.map_sum,
  simp only [h.eval, map_smul, smul_eq_mul],
  rw finset.sum_eq_single i,
  { simp },
  { intros q q_in q_ne,
    simp [q_ne.symm] },
  { intro p_not_in,
    simp [finsupp.not_mem_support_iff.1 p_not_in] },
end

@[simp]
lemma coeffs_lc (l : ι →₀ R) : h.coeffs (dual_pair.lc e l) = l :=
by { ext i, rw [h.coeffs_apply, h.dual_lc] }

/-- For any m : M n, \sum_{p ∈ Q n} (ε p m) • e p = m -/
@[simp]
lemma lc_coeffs (m : M) : dual_pair.lc e (h.coeffs m) = m :=
begin
  refine eq_of_sub_eq_zero (h.total _),
  intros i,
  simp [-sub_eq_add_neg, linear_map.map_sub, h.dual_lc, sub_eq_zero]
end

/-- `(h : dual_pair e ε).basis` shows the family of vectors `e` forms a basis. -/
@[simps]
def basis : basis ι R M :=
basis.of_repr
{ to_fun := coeffs h,
  inv_fun := lc e,
  left_inv := lc_coeffs h,
  right_inv := coeffs_lc h,
  map_add' := λ v w, by { ext i, exact (ε i).map_add v w },
  map_smul' := λ c v, by { ext i, exact (ε i).map_smul c v } }

@[simp] lemma coe_basis : ⇑h.basis = e :=
by { ext i, rw basis.apply_eq_iff, ext j,
     rw [h.basis_repr_apply, coeffs_apply, h.eval, finsupp.single_apply],
     convert if_congr eq_comm rfl rfl } -- `convert` to get rid of a `decidable_eq` mismatch

lemma mem_of_mem_span {H : set ι} {x : M} (hmem : x ∈ submodule.span R (e '' H)) :
  ∀ i : ι, ε i x ≠ 0 → i ∈ H :=
begin
  intros i hi,
  rcases (finsupp.mem_span_image_iff_total _).mp hmem with ⟨l, supp_l, rfl⟩,
  apply not_imp_comm.mp ((finsupp.mem_supported' _ _).mp supp_l i),
  rwa [← lc_def, h.dual_lc] at hi
end

lemma coe_dual_basis [fintype ι] : ⇑h.basis.dual_basis = ε :=
funext (λ i, h.basis.ext (λ j, by rw [h.basis.dual_basis_apply_self, h.coe_basis, h.eval,
                                      if_congr eq_comm rfl rfl]))

end dual_pair

namespace submodule

universes u v w

variables {R : Type u} {M : Type v} [comm_ring R] [add_comm_group M] [module R M]
variable {W : submodule R M}

/-- The `dual_restrict` of a submodule `W` of `M` is the linear map from the
  dual of `M` to the dual of `W` such that the domain of each linear map is
  restricted to `W`. -/
def dual_restrict (W : submodule R M) :
  module.dual R M →ₗ[R] module.dual R W :=
linear_map.dom_restrict' W

@[simp] lemma dual_restrict_apply
  (W : submodule R M) (φ : module.dual R M) (x : W) :
  W.dual_restrict φ x = φ (x : M) := rfl

/-- The `dual_annihilator` of a submodule `W` is the set of linear maps `φ` such
  that `φ w = 0` for all `w ∈ W`. -/
def dual_annihilator {R : Type u} {M : Type v} [comm_ring R] [add_comm_group M]
  [module R M] (W : submodule R M) : submodule R $ module.dual R M :=
W.dual_restrict.ker

@[simp] lemma mem_dual_annihilator (φ : module.dual R M) :
  φ ∈ W.dual_annihilator ↔ ∀ w ∈ W, φ w = 0 :=
begin
  refine linear_map.mem_ker.trans _,
  simp_rw [linear_map.ext_iff, dual_restrict_apply],
  exact ⟨λ h w hw, h ⟨w, hw⟩, λ h w, h w.1 w.2⟩
end

lemma dual_restrict_ker_eq_dual_annihilator (W : submodule R M) :
  W.dual_restrict.ker = W.dual_annihilator :=
rfl

lemma dual_annihilator_sup_eq_inf_dual_annihilator (U V : submodule R M) :
  (U ⊔ V).dual_annihilator = U.dual_annihilator ⊓ V.dual_annihilator :=
begin
  ext φ,
  rw [mem_inf, mem_dual_annihilator, mem_dual_annihilator, mem_dual_annihilator],
  split; intro h,
  { refine ⟨_, _⟩;
    intros x hx,
    exact h x (mem_sup.2 ⟨x, hx, 0, zero_mem _, add_zero _⟩),
    exact h x (mem_sup.2 ⟨0, zero_mem _, x, hx, zero_add _⟩) },
  { simp_rw mem_sup,
    rintro _ ⟨x, hx, y, hy, rfl⟩,
    rw [linear_map.map_add, h.1 _ hx, h.2 _ hy, add_zero] }
end

/-- The pullback of a submodule in the dual space along the evaluation map. -/
def dual_annihilator_comap (Φ : submodule R (module.dual R M)) : submodule R M :=
Φ.dual_annihilator.comap (module.dual.eval R M)

lemma mem_dual_annihilator_comap_iff {Φ : submodule R (module.dual R M)} (x : M) :
  x ∈ Φ.dual_annihilator_comap ↔ ∀ φ ∈ Φ, (φ x : R) = 0 :=
by simp_rw [dual_annihilator_comap, mem_comap, mem_dual_annihilator, module.dual.eval_apply]

end submodule

namespace subspace

open submodule linear_map

universes u v w

-- We work in vector spaces because `exists_is_compl` only hold for vector spaces
variables {K : Type u} {V : Type v} [field K] [add_comm_group V] [module K V]

/-- Given a subspace `W` of `V` and an element of its dual `φ`, `dual_lift W φ` is
the natural extension of `φ` to an element of the dual of `V`.
That is, `dual_lift W φ` sends `w ∈ W` to `φ x` and `x` in the complement of `W` to `0`. -/
noncomputable def dual_lift (W : subspace K V) :
  module.dual K W →ₗ[K] module.dual K V :=
let h := classical.indefinite_description _ W.exists_is_compl in
  (linear_map.of_is_compl_prod h.2).comp (linear_map.inl _ _ _)

variable {W : subspace K V}

@[simp] lemma dual_lift_of_subtype {φ : module.dual K W} (w : W) :
  W.dual_lift φ (w : V) = φ w :=
by { erw of_is_compl_left_apply _ w, refl }

lemma dual_lift_of_mem {φ : module.dual K W} {w : V} (hw : w ∈ W) :
  W.dual_lift φ w = φ ⟨w, hw⟩ :=
dual_lift_of_subtype ⟨w, hw⟩

@[simp] lemma dual_restrict_comp_dual_lift (W : subspace K V) :
  W.dual_restrict.comp W.dual_lift = 1 :=
by { ext φ x, simp }

lemma dual_restrict_left_inverse (W : subspace K V) :
  function.left_inverse W.dual_restrict W.dual_lift :=
λ x, show W.dual_restrict.comp W.dual_lift x = x,
  by { rw [dual_restrict_comp_dual_lift], refl }

lemma dual_lift_right_inverse (W : subspace K V) :
  function.right_inverse W.dual_lift W.dual_restrict :=
W.dual_restrict_left_inverse

lemma dual_restrict_surjective :
  function.surjective W.dual_restrict :=
W.dual_lift_right_inverse.surjective

lemma dual_lift_injective : function.injective W.dual_lift :=
W.dual_restrict_left_inverse.injective

/-- The quotient by the `dual_annihilator` of a subspace is isomorphic to the
  dual of that subspace. -/
noncomputable def quot_annihilator_equiv (W : subspace K V) :
  (module.dual K V ⧸ W.dual_annihilator) ≃ₗ[K] module.dual K W :=
(quot_equiv_of_eq _ _ W.dual_restrict_ker_eq_dual_annihilator).symm.trans $
  W.dual_restrict.quot_ker_equiv_of_surjective dual_restrict_surjective

/-- The natural isomorphism forom the dual of a subspace `W` to `W.dual_lift.range`. -/
noncomputable def dual_equiv_dual (W : subspace K V) :
  module.dual K W ≃ₗ[K] W.dual_lift.range :=
linear_equiv.of_injective _ dual_lift_injective

lemma dual_equiv_dual_def (W : subspace K V) :
  W.dual_equiv_dual.to_linear_map = W.dual_lift.range_restrict := rfl

@[simp] lemma dual_equiv_dual_apply (φ : module.dual K W) :
  W.dual_equiv_dual φ = ⟨W.dual_lift φ, mem_range.2 ⟨φ, rfl⟩⟩ := rfl

section

open_locale classical

open finite_dimensional

variables {V₁ : Type*} [add_comm_group V₁] [module K V₁]

instance [H : finite_dimensional K V] : finite_dimensional K (module.dual K V) :=
linear_equiv.finite_dimensional (basis.of_vector_space K V).to_dual_equiv

variables [finite_dimensional K V] [finite_dimensional K V₁]

@[simp] lemma dual_finrank_eq :
  finrank K (module.dual K V) = finrank K V :=
linear_equiv.finrank_eq (basis.of_vector_space K V).to_dual_equiv.symm

/-- The quotient by the dual is isomorphic to its dual annihilator.  -/
noncomputable def quot_dual_equiv_annihilator (W : subspace K V) :
  (module.dual K V ⧸ W.dual_lift.range) ≃ₗ[K] W.dual_annihilator :=
linear_equiv.quot_equiv_of_quot_equiv $
  linear_equiv.trans W.quot_annihilator_equiv W.dual_equiv_dual

/-- The quotient by a subspace is isomorphic to its dual annihilator. -/
noncomputable def quot_equiv_annihilator (W : subspace K V) :
  (V ⧸ W) ≃ₗ[K] W.dual_annihilator :=
begin
  refine _ ≪≫ₗ W.quot_dual_equiv_annihilator,
  refine linear_equiv.quot_equiv_of_equiv _ (basis.of_vector_space K V).to_dual_equiv,
  exact (basis.of_vector_space K W).to_dual_equiv.trans W.dual_equiv_dual
end

open finite_dimensional

@[simp]
lemma finrank_dual_annihilator_comap_eq {Φ : subspace K (module.dual K V)} :
  finrank K Φ.dual_annihilator_comap = finrank K Φ.dual_annihilator :=
begin
  rw [submodule.dual_annihilator_comap, ← module.eval_equiv_to_linear_map],
  exact linear_equiv.finrank_eq (linear_equiv.of_submodule' _ _),
end

lemma finrank_add_finrank_dual_annihilator_comap_eq
  (W : subspace K (module.dual K V)) :
  finrank K W + finrank K W.dual_annihilator_comap = finrank K V :=
begin
  rw [finrank_dual_annihilator_comap_eq, W.quot_equiv_annihilator.finrank_eq.symm, add_comm,
      submodule.finrank_quotient_add_finrank, subspace.dual_finrank_eq],
end

end

end subspace

variables {R : Type*} [comm_ring R] {M₁ : Type*} {M₂ : Type*}
variables [add_comm_group M₁] [module R M₁] [add_comm_group M₂] [module R M₂]

open module

/-- Given a linear map `f : M₁ →ₗ[R] M₂`, `f.dual_map` is the linear map between the dual of
`M₂` and `M₁` such that it maps the functional `φ` to `φ ∘ f`. -/
def linear_map.dual_map (f : M₁ →ₗ[R] M₂) : dual R M₂ →ₗ[R] dual R M₁ :=
linear_map.lcomp R R f

@[simp] lemma linear_map.dual_map_apply (f : M₁ →ₗ[R] M₂) (g : dual R M₂) (x : M₁) :
  f.dual_map g x = g (f x) :=
linear_map.lcomp_apply f g x

@[simp] lemma linear_map.dual_map_id :
  (linear_map.id : M₁ →ₗ[R] M₁).dual_map = linear_map.id :=
by { ext, refl }

lemma linear_map.dual_map_comp_dual_map {M₃ : Type*} [add_comm_group M₃] [module R M₃]
  (f : M₁ →ₗ[R] M₂) (g : M₂ →ₗ[R] M₃) :
  f.dual_map.comp g.dual_map = (g.comp f).dual_map :=
rfl

/-- The `linear_equiv` version of `linear_map.dual_map`. -/
def linear_equiv.dual_map (f : M₁ ≃ₗ[R] M₂) : dual R M₂ ≃ₗ[R] dual R M₁ :=
{ inv_fun := f.symm.to_linear_map.dual_map,
  left_inv :=
    begin
      intro φ, ext x,
      simp only [linear_map.dual_map_apply, linear_equiv.coe_to_linear_map,
                 linear_map.to_fun_eq_coe, linear_equiv.apply_symm_apply]
    end,
  right_inv :=
    begin
      intro φ, ext x,
      simp only [linear_map.dual_map_apply, linear_equiv.coe_to_linear_map,
                 linear_map.to_fun_eq_coe, linear_equiv.symm_apply_apply]
    end,
  .. f.to_linear_map.dual_map }

@[simp] lemma linear_equiv.dual_map_apply (f : M₁ ≃ₗ[R] M₂) (g : dual R M₂) (x : M₁) :
  f.dual_map g x = g (f x) :=
linear_map.lcomp_apply f g x

@[simp] lemma linear_equiv.dual_map_refl :
  (linear_equiv.refl R M₁).dual_map = linear_equiv.refl R (dual R M₁) :=
by { ext, refl }

@[simp] lemma linear_equiv.dual_map_symm {f : M₁ ≃ₗ[R] M₂} :
  (linear_equiv.dual_map f).symm = linear_equiv.dual_map f.symm := rfl

lemma linear_equiv.dual_map_trans {M₃ : Type*} [add_comm_group M₃] [module R M₃]
  (f : M₁ ≃ₗ[R] M₂) (g : M₂ ≃ₗ[R] M₃) :
  g.dual_map.trans f.dual_map = (f.trans g).dual_map :=
rfl

namespace linear_map

variable (f : M₁ →ₗ[R] M₂)

lemma ker_dual_map_eq_dual_annihilator_range :
  f.dual_map.ker = f.range.dual_annihilator :=
begin
  ext φ, split; intro hφ,
  { rw mem_ker at hφ,
    rw submodule.mem_dual_annihilator,
    rintro y ⟨x, rfl⟩,
    rw [← dual_map_apply, hφ, zero_apply] },
  { ext x,
    rw dual_map_apply,
    rw submodule.mem_dual_annihilator at hφ,
    exact hφ (f x) ⟨x, rfl⟩ }
end

lemma range_dual_map_le_dual_annihilator_ker :
  f.dual_map.range ≤ f.ker.dual_annihilator :=
begin
  rintro _ ⟨ψ, rfl⟩,
  simp_rw [submodule.mem_dual_annihilator, mem_ker],
  rintro x hx,
  rw [dual_map_apply, hx, map_zero]
end

section finite_dimensional

variables {K : Type*} [field K] {V₁ : Type*} {V₂ : Type*}
variables [add_comm_group V₁] [module K V₁] [add_comm_group V₂] [module K V₂]

open finite_dimensional

variable [finite_dimensional K V₂]

@[simp] lemma finrank_range_dual_map_eq_finrank_range (f : V₁ →ₗ[K] V₂) :
  finrank K f.dual_map.range = finrank K f.range :=
begin
  have := submodule.finrank_quotient_add_finrank f.range,
  rw [(subspace.quot_equiv_annihilator f.range).finrank_eq,
      ← ker_dual_map_eq_dual_annihilator_range] at this,
  conv_rhs at this { rw ← subspace.dual_finrank_eq },
  refine add_left_injective (finrank K f.dual_map.ker) _,
  change _ + _ = _ + _,
  rw [finrank_range_add_finrank_ker f.dual_map, add_comm, this],
end

lemma range_dual_map_eq_dual_annihilator_ker [finite_dimensional K V₁] (f : V₁ →ₗ[K] V₂) :
  f.dual_map.range = f.ker.dual_annihilator :=
begin
  refine eq_of_le_of_finrank_eq f.range_dual_map_le_dual_annihilator_ker _,
  have := submodule.finrank_quotient_add_finrank f.ker,
  rw (subspace.quot_equiv_annihilator f.ker).finrank_eq at this,
  refine add_left_injective (finrank K f.ker) _,
  simp_rw [this, finrank_range_dual_map_eq_finrank_range],
  exact finrank_range_add_finrank_ker f,
end

end finite_dimensional

section field

variables {K V : Type*}
variables [field K] [add_comm_group V] [module K V]

lemma dual_pairing_nondegenerate : (dual_pairing K V).nondegenerate :=
begin
  refine ⟨separating_left_iff_ker_eq_bot.mpr ker_id, _⟩,
  intros x,
  contrapose,
  rintros hx : x ≠ 0,
  rw [not_forall],
  let f : V →ₗ[K] K := classical.some (linear_pmap.mk_span_singleton x 1 hx).to_fun.exists_extend,
  use [f],
  refine ne_zero_of_eq_one _,
  have h : f.comp (K ∙ x).subtype = (linear_pmap.mk_span_singleton x 1 hx).to_fun :=
    classical.some_spec (linear_pmap.mk_span_singleton x (1 : K) hx).to_fun.exists_extend,
  rw linear_map.ext_iff at h,
  convert h ⟨x, submodule.mem_span_singleton_self x⟩,
  exact (linear_pmap.mk_span_singleton_apply' K hx 1).symm,
end

end field

end linear_map


/-
Copyright (c) 2019 Amelia Livingston. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Amelia Livingston
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.group_theory.congruence
import Mathlib.group_theory.submonoid.default
import Mathlib.algebra.group.units
import Mathlib.algebra.punit_instances
import Mathlib.PostPort

universes u_1 u_2 l u_3 u_4 u_5 u_6

namespace Mathlib

/-!
# Localizations of commutative monoids

Localizing a commutative ring at one of its submonoids does not rely on the ring's addition, so
we can generalize localizations to commutative monoids.

We characterize the localization of a commutative monoid `M` at a submonoid `S` up to
isomorphism; that is, a commutative monoid `N` is the localization of `M` at `S` iff we can find a
monoid homomorphism `f : M →* N` satisfying 3 properties:
1. For all `y ∈ S`, `f y` is a unit;
2. For all `z : N`, there exists `(x, y) : M × S` such that `z * f y = f x`;
3. For all `x, y : M`, `f x = f y` iff there exists `c ∈ S` such that `x * c = y * c`.

Given such a localization map `f : M →* N`, we can define the surjection
`localization_map.mk'` sending `(x, y) : M × S` to `f x * (f y)⁻¹`, and
`localization_map.lift`, the homomorphism from `N` induced by a homomorphism from `M` which maps
elements of `S` to invertible elements of the codomain. Similarly, given commutative monoids
`P, Q`, a submonoid `T` of `P` and a localization map for `T` from `P` to `Q`, then a homomorphism
`g : M →* P` such that `g(S) ⊆ T` induces a homomorphism of localizations,
`localization_map.map`, from `N` to `Q`.
We treat the special case of localizing away from an element in the sections `away_map` and `away`.

We also define the quotient of `M × S` by the unique congruence relation (equivalence relation
preserving a binary operation) `r` such that for any other congruence relation `s` on `M × S`
satisfying '`∀ y ∈ S`, `(1, 1) ∼ (y, y)` under `s`', we have that `(x₁, y₁) ∼ (x₂, y₂)` by `s`
whenever `(x₁, y₁) ∼ (x₂, y₂)` by `r`. We show this relation is equivalent to the standard
localization relation.
This defines the localization as a quotient type, `localization`, but the majority of
subsequent lemmas in the file are given in terms of localizations up to isomorphism, using maps
which satisfy the characteristic predicate.

## Implementation notes

In maths it is natural to reason up to isomorphism, but in Lean we cannot naturally `rewrite` one
structure with an isomorphic one; one way around this is to isolate a predicate characterizing
a structure up to isomorphism, and reason about things that satisfy the predicate.

The infimum form of the localization congruence relation is chosen as 'canonical' here, since it
shortens some proofs.

To apply a localization map `f` as a function, we use `f.to_map`, as coercions don't work well for
this structure.

To reason about the localization as a quotient type, use `mk_eq_monoid_of_mk'` and associated
lemmas. These show the quotient map `mk : M → S → localization S` equals the
surjection `localization_map.mk'` induced by the map
`monoid_of : localization_map S (localization S)` (where `of` establishes the
localization as a quotient type satisfies the characteristic predicate). The lemma
`mk_eq_monoid_of_mk'` hence gives you access to the results in the rest of the file, which are
about the `localization_map.mk'` induced by any localization map.

## Tags
localization, monoid localization, quotient monoid, congruence relation, characteristic predicate,
commutative monoid
-/

namespace add_submonoid

/-- The type of add_monoid homomorphisms satisfying the characteristic predicate: if `f : M →+ N`
satisfies this predicate, then `N` is isomorphic to the localization of `M` at `S`. -/
structure localization_map {M : Type u_1} [add_comm_monoid M] (S : add_submonoid M) (N : Type u_2)
    [add_comm_monoid N]
    extends M →+ N where
  map_add_units' : ∀ (y : ↥S), is_add_unit (to_fun ↑y)
  surj' : ∀ (z : N), ∃ (x : M × ↥S), z + to_fun ↑(prod.snd x) = to_fun (prod.fst x)
  eq_iff_exists' : ∀ (x y : M), to_fun x = to_fun y ↔ ∃ (c : ↥S), x + ↑c = y + ↑c

/-- The add_monoid hom underlying a `localization_map` of `add_comm_monoid`s. -/
end add_submonoid

namespace submonoid

/-- The type of monoid homomorphisms satisfying the characteristic predicate: if `f : M →* N`
satisfies this predicate, then `N` is isomorphic to the localization of `M` at `S`. -/
structure localization_map {M : Type u_1} [comm_monoid M] (S : submonoid M) (N : Type u_2)
    [comm_monoid N]
    extends M →* N where
  map_units' : ∀ (y : ↥S), is_unit (to_fun ↑y)
  surj' : ∀ (z : N), ∃ (x : M × ↥S), z * to_fun ↑(prod.snd x) = to_fun (prod.fst x)
  eq_iff_exists' : ∀ (x y : M), to_fun x = to_fun y ↔ ∃ (c : ↥S), x * ↑c = y * ↑c

/-- The monoid hom underlying a `localization_map`. -/
end submonoid

namespace localization

/-- The congruence relation on `M × S`, `M` a `comm_monoid` and `S` a submonoid of `M`, whose
quotient is the localization of `M` at `S`, defined as the unique congruence relation on
`M × S` such that for any other congruence relation `s` on `M × S` where for all `y ∈ S`,
`(1, 1) ∼ (y, y)` under `s`, we have that `(x₁, y₁) ∼ (x₂, y₂)` by `r` implies
`(x₁, y₁) ∼ (x₂, y₂)` by `s`. -/
def Mathlib.add_localization.r {M : Type u_1} [add_comm_monoid M] (S : add_submonoid M) :
    add_con (M × ↥S) :=
  Inf (set_of fun (c : add_con (M × ↥S)) => ∀ (y : ↥S), coe_fn c 0 (↑y, y))

/-- An alternate form of the congruence relation on `M × S`, `M` a `comm_monoid` and `S` a
submonoid of `M`, whose quotient is the localization of `M` at `S`. -/
def Mathlib.add_localization.r' {M : Type u_1} [add_comm_monoid M] (S : add_submonoid M) :
    add_con (M × ↥S) :=
  add_con.mk
    (fun (a b : M × ↥S) =>
      ∃ (c : ↥S), prod.fst a + ↑(prod.snd b) + ↑c = prod.fst b + ↑(prod.snd a) + ↑c)
    sorry sorry

/-- The congruence relation used to localize a `comm_monoid` at a submonoid can be expressed
equivalently as an infimum (see `localization.r`) or explicitly
(see `localization.r'`). -/
theorem r_eq_r' {M : Type u_1} [comm_monoid M] (S : submonoid M) : r S = r' S := sorry

theorem Mathlib.add_localization.r_iff_exists {M : Type u_1} [add_comm_monoid M]
    {S : add_submonoid M} {x : M × ↥S} {y : M × ↥S} :
    coe_fn (add_localization.r S) x y ↔
        ∃ (c : ↥S), prod.fst x + ↑(prod.snd y) + ↑c = prod.fst y + ↑(prod.snd x) + ↑c :=
  sorry

end localization

/-- The localization of a `comm_monoid` at one of its submonoids (as a quotient type). -/
def localization {M : Type u_1} [comm_monoid M] (S : submonoid M) := con.quotient sorry

namespace localization

protected instance inhabited {M : Type u_1} [comm_monoid M] (S : submonoid M) :
    Inhabited (localization S) :=
  con.quotient.inhabited

protected instance comm_monoid {M : Type u_1} [comm_monoid M] (S : submonoid M) :
    comm_monoid (localization S) :=
  con.comm_monoid (r S)

/-- Given a `comm_monoid` `M` and submonoid `S`, `mk` sends `x : M`, `y ∈ S` to the equivalence
class of `(x, y)` in the localization of `M` at `S`. -/
def Mathlib.add_localization.mk {M : Type u_1} [add_comm_monoid M] {S : add_submonoid M} (x : M)
    (y : ↥S) : add_localization S :=
  coe_fn (add_con.mk' (add_localization.r S)) (x, y)

theorem Mathlib.add_localization.ind {M : Type u_1} [add_comm_monoid M] {S : add_submonoid M}
    {p : add_localization S → Prop}
    (H : ∀ (y : M × ↥S), p (add_localization.mk (prod.fst y) (prod.snd y)))
    (x : add_localization S) : p x :=
  sorry

theorem Mathlib.add_localization.induction_on {M : Type u_1} [add_comm_monoid M]
    {S : add_submonoid M} {p : add_localization S → Prop} (x : add_localization S)
    (H : ∀ (y : M × ↥S), p (add_localization.mk (prod.fst y) (prod.snd y))) : p x :=
  add_localization.ind H x

theorem induction_on₂ {M : Type u_1} [comm_monoid M] {S : submonoid M}
    {p : localization S → localization S → Prop} (x : localization S) (y : localization S)
    (H : ∀ (x y : M × ↥S), p (mk (prod.fst x) (prod.snd x)) (mk (prod.fst y) (prod.snd y))) :
    p x y :=
  induction_on x fun (x : M × ↥S) => induction_on y (H x)

theorem induction_on₃ {M : Type u_1} [comm_monoid M] {S : submonoid M}
    {p : localization S → localization S → localization S → Prop} (x : localization S)
    (y : localization S) (z : localization S)
    (H :
      ∀ (x y z : M × ↥S),
        p (mk (prod.fst x) (prod.snd x)) (mk (prod.fst y) (prod.snd y))
          (mk (prod.fst z) (prod.snd z))) :
    p x y z :=
  induction_on₂ x y fun (x y : M × ↥S) => induction_on z (H x y)

theorem one_rel {M : Type u_1} [comm_monoid M] {S : submonoid M} (y : ↥S) :
    coe_fn (r S) 1 (↑y, y) :=
  fun (b : con (M × ↥S))
    (hb : b ∈ set_of fun (c : con (M × ↥S)) => ∀ (y : ↥S), coe_fn c 1 (↑y, y)) => hb y

theorem Mathlib.add_localization.r_of_eq {M : Type u_1} [add_comm_monoid M] {S : add_submonoid M}
    {x : M × ↥S} {y : M × ↥S} (h : prod.fst y + ↑(prod.snd x) = prod.fst x + ↑(prod.snd y)) :
    coe_fn (add_localization.r S) x y :=
  sorry

end localization

namespace monoid_hom

/-- Makes a localization map from a `comm_monoid` hom satisfying the characteristic predicate. -/
def Mathlib.add_monoid_hom.to_localization_map {M : Type u_1} [add_comm_monoid M]
    {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N] (f : M →+ N)
    (H1 : ∀ (y : ↥S), is_add_unit (coe_fn f ↑y))
    (H2 : ∀ (z : N), ∃ (x : M × ↥S), z + coe_fn f ↑(prod.snd x) = coe_fn f (prod.fst x))
    (H3 : ∀ (x y : M), coe_fn f x = coe_fn f y ↔ ∃ (c : ↥S), x + ↑c = y + ↑c) :
    add_submonoid.localization_map S N :=
  add_submonoid.localization_map.mk (add_monoid_hom.to_fun f) sorry sorry H1 H2 H3

end monoid_hom

namespace submonoid

namespace localization_map

/-- Short for `to_monoid_hom`; used to apply a localization map as a function. -/
def to_map {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2} [comm_monoid N]
    (f : localization_map S N) : M →* N :=
  to_monoid_hom f

theorem Mathlib.add_submonoid.localization_map.ext {M : Type u_1} [add_comm_monoid M]
    {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N]
    {f : add_submonoid.localization_map S N} {g : add_submonoid.localization_map S N}
    (h :
      ∀ (x : M),
        coe_fn (add_submonoid.localization_map.to_map f) x =
          coe_fn (add_submonoid.localization_map.to_map g) x) :
    f = g :=
  sorry

theorem ext_iff {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2} [comm_monoid N]
    {f : localization_map S N} {g : localization_map S N} :
    f = g ↔ ∀ (x : M), coe_fn (to_map f) x = coe_fn (to_map g) x :=
  { mp := fun (h : f = g) (x : M) => h ▸ rfl, mpr := ext }

theorem to_map_injective {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2}
    [comm_monoid N] : function.injective to_map :=
  fun (_x _x_1 : localization_map S N) (h : to_map _x = to_map _x_1) =>
    ext (iff.mp monoid_hom.ext_iff h)

theorem map_units {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2} [comm_monoid N]
    (f : localization_map S N) (y : ↥S) : is_unit (coe_fn (to_map f) ↑y) :=
  map_units' f y

theorem surj {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2} [comm_monoid N]
    (f : localization_map S N) (z : N) :
    ∃ (x : M × ↥S), z * coe_fn (to_map f) ↑(prod.snd x) = coe_fn (to_map f) (prod.fst x) :=
  surj' f z

theorem eq_iff_exists {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2}
    [comm_monoid N] (f : localization_map S N) {x : M} {y : M} :
    coe_fn (to_map f) x = coe_fn (to_map f) y ↔ ∃ (c : ↥S), x * ↑c = y * ↑c :=
  eq_iff_exists' f x y

/-- Given a localization map `f : M →* N`, a section function sending `z : N` to some
`(x, y) : M × S` such that `f x * (f y)⁻¹ = z`. -/
def sec {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2} [comm_monoid N]
    (f : localization_map S N) (z : N) : M × ↥S :=
  classical.some (surj f z)

theorem Mathlib.add_submonoid.localization_map.sec_spec {M : Type u_1} [add_comm_monoid M]
    {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N]
    {f : add_submonoid.localization_map S N} (z : N) :
    z +
          coe_fn (add_submonoid.localization_map.to_map f)
            ↑(prod.snd (add_submonoid.localization_map.sec f z)) =
        coe_fn (add_submonoid.localization_map.to_map f)
          (prod.fst (add_submonoid.localization_map.sec f z)) :=
  classical.some_spec (add_submonoid.localization_map.surj f z)

theorem Mathlib.add_submonoid.localization_map.sec_spec' {M : Type u_1} [add_comm_monoid M]
    {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N]
    {f : add_submonoid.localization_map S N} (z : N) :
    coe_fn (add_submonoid.localization_map.to_map f)
          (prod.fst (add_submonoid.localization_map.sec f z)) =
        coe_fn (add_submonoid.localization_map.to_map f)
            ↑(prod.snd (add_submonoid.localization_map.sec f z)) +
          z :=
  sorry

/-- Given a monoid hom `f : M →* N` and submonoid `S ⊆ M` such that `f(S) ⊆ units N`, for all
`w : M, z : N` and `y ∈ S`, we have `w * (f y)⁻¹ = z ↔ w = f y * z`. -/
theorem Mathlib.add_submonoid.localization_map.add_neg_left {M : Type u_1} [add_comm_monoid M]
    {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N] {f : M →+ N}
    (h : ∀ (y : ↥S), is_add_unit (coe_fn f ↑y)) (y : ↥S) (w : N) (z : N) :
    w + ↑(-coe_fn (is_add_unit.lift_right (add_monoid_hom.mrestrict f S) h) y) = z ↔
        w = coe_fn f ↑y + z :=
  sorry

/-- Given a monoid hom `f : M →* N` and submonoid `S ⊆ M` such that `f(S) ⊆ units N`, for all
`w : M, z : N` and `y ∈ S`, we have `z = w * (f y)⁻¹ ↔ z * f y = w`. -/
theorem mul_inv_right {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2}
    [comm_monoid N] {f : M →* N} (h : ∀ (y : ↥S), is_unit (coe_fn f ↑y)) (y : ↥S) (w : N) (z : N) :
    z = w * ↑(coe_fn (is_unit.lift_right (monoid_hom.mrestrict f S) h) y⁻¹) ↔ z * coe_fn f ↑y = w :=
  sorry

/-- Given a monoid hom `f : M →* N` and submonoid `S ⊆ M` such that
`f(S) ⊆ units N`, for all `x₁ x₂ : M` and `y₁, y₂ ∈ S`, we have
`f x₁ * (f y₁)⁻¹ = f x₂ * (f y₂)⁻¹ ↔ f (x₁ * y₂) = f (x₂ * y₁)`. -/
@[simp] theorem mul_inv {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2}
    [comm_monoid N] {f : M →* N} (h : ∀ (y : ↥S), is_unit (coe_fn f ↑y)) {x₁ : M} {x₂ : M} {y₁ : ↥S}
    {y₂ : ↥S} :
    coe_fn f x₁ * ↑(coe_fn (is_unit.lift_right (monoid_hom.mrestrict f S) h) y₁⁻¹) =
          coe_fn f x₂ * ↑(coe_fn (is_unit.lift_right (monoid_hom.mrestrict f S) h) y₂⁻¹) ↔
        coe_fn f (x₁ * ↑y₂) = coe_fn f (x₂ * ↑y₁) :=
  sorry

/-- Given a monoid hom `f : M →* N` and submonoid `S ⊆ M` such that `f(S) ⊆ units N`, for all
`y, z ∈ S`, we have `(f y)⁻¹ = (f z)⁻¹ → f y = f z`. -/
theorem Mathlib.add_submonoid.localization_map.neg_inj {M : Type u_1} [add_comm_monoid M]
    {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N] {f : M →+ N}
    (hf : ∀ (y : ↥S), is_add_unit (coe_fn f ↑y)) {y : ↥S} {z : ↥S}
    (h :
      -coe_fn (is_add_unit.lift_right (add_monoid_hom.mrestrict f S) hf) y =
        -coe_fn (is_add_unit.lift_right (add_monoid_hom.mrestrict f S) hf) z) :
    coe_fn f ↑y = coe_fn f ↑z :=
  sorry

/-- Given a monoid hom `f : M →* N` and submonoid `S ⊆ M` such that `f(S) ⊆ units N`, for all
`y ∈ S`, `(f y)⁻¹` is unique. -/
theorem Mathlib.add_submonoid.localization_map.neg_unique {M : Type u_1} [add_comm_monoid M]
    {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N] {f : M →+ N}
    (h : ∀ (y : ↥S), is_add_unit (coe_fn f ↑y)) {y : ↥S} {z : N} (H : coe_fn f ↑y + z = 0) :
    ↑(-coe_fn (is_add_unit.lift_right (add_monoid_hom.mrestrict f S) h) y) = z :=
  sorry

theorem map_right_cancel {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2}
    [comm_monoid N] (f : localization_map S N) {x : M} {y : M} {c : ↥S}
    (h : coe_fn (to_map f) (↑c * x) = coe_fn (to_map f) (↑c * y)) :
    coe_fn (to_map f) x = coe_fn (to_map f) y :=
  sorry

theorem Mathlib.add_submonoid.localization_map.map_left_cancel {M : Type u_1} [add_comm_monoid M]
    {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N]
    (f : add_submonoid.localization_map S N) {x : M} {y : M} {c : ↥S}
    (h :
      coe_fn (add_submonoid.localization_map.to_map f) (x + ↑c) =
        coe_fn (add_submonoid.localization_map.to_map f) (y + ↑c)) :
    coe_fn (add_submonoid.localization_map.to_map f) x =
        coe_fn (add_submonoid.localization_map.to_map f) y :=
  sorry

/-- Given a localization map `f : M →* N`, the surjection sending `(x, y) : M × S` to
`f x * (f y)⁻¹`. -/
def mk' {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2} [comm_monoid N]
    (f : localization_map S N) (x : M) (y : ↥S) : N :=
  coe_fn (to_map f) x *
    ↑(coe_fn (is_unit.lift_right (monoid_hom.mrestrict (to_map f) S) (map_units f)) y⁻¹)

theorem Mathlib.add_submonoid.localization_map.mk'_add {M : Type u_1} [add_comm_monoid M]
    {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N]
    (f : add_submonoid.localization_map S N) (x₁ : M) (x₂ : M) (y₁ : ↥S) (y₂ : ↥S) :
    add_submonoid.localization_map.mk' f (x₁ + x₂) (y₁ + y₂) =
        add_submonoid.localization_map.mk' f x₁ y₁ + add_submonoid.localization_map.mk' f x₂ y₂ :=
  sorry

theorem mk'_one {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2} [comm_monoid N]
    (f : localization_map S N) (x : M) : mk' f x 1 = coe_fn (to_map f) x :=
  sorry

/-- Given a localization map `f : M →* N` for a submonoid `S ⊆ M`, for all `z : N` we have that if
`x : M, y ∈ S` are such that `z * f y = f x`, then `f x * (f y)⁻¹ = z`. -/
@[simp] theorem Mathlib.add_submonoid.localization_map.mk'_sec {M : Type u_1} [add_comm_monoid M]
    {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N]
    (f : add_submonoid.localization_map S N) (z : N) :
    add_submonoid.localization_map.mk' f (prod.fst (add_submonoid.localization_map.sec f z))
          (prod.snd (add_submonoid.localization_map.sec f z)) =
        z :=
  sorry

theorem mk'_surjective {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2}
    [comm_monoid N] (f : localization_map S N) (z : N) : ∃ (x : M), ∃ (y : ↥S), mk' f x y = z :=
  Exists.intro (prod.fst (sec f z)) (Exists.intro (prod.snd (sec f z)) (mk'_sec f z))

theorem mk'_spec {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2} [comm_monoid N]
    (f : localization_map S N) (x : M) (y : ↥S) :
    mk' f x y * coe_fn (to_map f) ↑y = coe_fn (to_map f) x :=
  sorry

theorem mk'_spec' {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2} [comm_monoid N]
    (f : localization_map S N) (x : M) (y : ↥S) :
    coe_fn (to_map f) ↑y * mk' f x y = coe_fn (to_map f) x :=
  sorry

theorem eq_mk'_iff_mul_eq {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2}
    [comm_monoid N] (f : localization_map S N) {x : M} {y : ↥S} {z : N} :
    z = mk' f x y ↔ z * coe_fn (to_map f) ↑y = coe_fn (to_map f) x :=
  sorry

theorem Mathlib.add_submonoid.localization_map.mk'_eq_iff_eq_add {M : Type u_1} [add_comm_monoid M]
    {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N]
    (f : add_submonoid.localization_map S N) {x : M} {y : ↥S} {z : N} :
    add_submonoid.localization_map.mk' f x y = z ↔
        coe_fn (add_submonoid.localization_map.to_map f) x =
          z + coe_fn (add_submonoid.localization_map.to_map f) ↑y :=
  sorry

theorem Mathlib.add_submonoid.localization_map.mk'_eq_iff_eq {M : Type u_1} [add_comm_monoid M]
    {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N]
    (f : add_submonoid.localization_map S N) {x₁ : M} {x₂ : M} {y₁ : ↥S} {y₂ : ↥S} :
    add_submonoid.localization_map.mk' f x₁ y₁ = add_submonoid.localization_map.mk' f x₂ y₂ ↔
        coe_fn (add_submonoid.localization_map.to_map f) (x₁ + ↑y₂) =
          coe_fn (add_submonoid.localization_map.to_map f) (x₂ + ↑y₁) :=
  sorry

protected theorem eq {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2} [comm_monoid N]
    (f : localization_map S N) {a₁ : M} {b₁ : M} {a₂ : ↥S} {b₂ : ↥S} :
    mk' f a₁ a₂ = mk' f b₁ b₂ ↔ ∃ (c : ↥S), a₁ * ↑b₂ * ↑c = b₁ * ↑a₂ * ↑c :=
  iff.trans (mk'_eq_iff_eq f) (eq_iff_exists f)

protected theorem eq' {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2}
    [comm_monoid N] (f : localization_map S N) {a₁ : M} {b₁ : M} {a₂ : ↥S} {b₂ : ↥S} :
    mk' f a₁ a₂ = mk' f b₁ b₂ ↔ coe_fn (localization.r S) (a₁, a₂) (b₁, b₂) :=
  sorry

theorem Mathlib.add_submonoid.localization_map.eq_iff_eq {M : Type u_1} [add_comm_monoid M]
    {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N] {P : Type u_3} [add_comm_monoid P]
    (f : add_submonoid.localization_map S N) (g : add_submonoid.localization_map S P) {x : M}
    {y : M} :
    coe_fn (add_submonoid.localization_map.to_map f) x =
          coe_fn (add_submonoid.localization_map.to_map f) y ↔
        coe_fn (add_submonoid.localization_map.to_map g) x =
          coe_fn (add_submonoid.localization_map.to_map g) y :=
  iff.trans (add_submonoid.localization_map.eq_iff_exists f)
    (iff.symm (add_submonoid.localization_map.eq_iff_exists g))

theorem mk'_eq_iff_mk'_eq {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2}
    [comm_monoid N] {P : Type u_3} [comm_monoid P] (f : localization_map S N)
    (g : localization_map S P) {x₁ : M} {x₂ : M} {y₁ : ↥S} {y₂ : ↥S} :
    mk' f x₁ y₁ = mk' f x₂ y₂ ↔ mk' g x₁ y₁ = mk' g x₂ y₂ :=
  iff.trans (localization_map.eq' f) (iff.symm (localization_map.eq' g))

/-- Given a localization map `f : M →* N` for a submonoid `S ⊆ M`, for all `x₁ : M` and `y₁ ∈ S`,
if `x₂ : M, y₂ ∈ S` are such that `f x₁ * (f y₁)⁻¹ * f y₂ = f x₂`, then there exists `c ∈ S`
such that `x₁ * y₂ * c = x₂ * y₁ * c`. -/
theorem Mathlib.add_submonoid.localization_map.exists_of_sec_mk' {M : Type u_1} [add_comm_monoid M]
    {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N]
    (f : add_submonoid.localization_map S N) (x : M) (y : ↥S) :
    ∃ (c : ↥S),
        x +
              ↑(prod.snd
                  (add_submonoid.localization_map.sec f
                    (add_submonoid.localization_map.mk' f x y))) +
            ↑c =
          prod.fst
                (add_submonoid.localization_map.sec f (add_submonoid.localization_map.mk' f x y)) +
              ↑y +
            ↑c :=
  iff.mp (add_submonoid.localization_map.eq_iff_exists f)
    (iff.mp (add_submonoid.localization_map.mk'_eq_iff_eq f)
      (Eq.symm
        (add_submonoid.localization_map.mk'_sec f (add_submonoid.localization_map.mk' f x y))))

theorem mk'_eq_of_eq {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2} [comm_monoid N]
    (f : localization_map S N) {a₁ : M} {b₁ : M} {a₂ : ↥S} {b₂ : ↥S} (H : b₁ * ↑a₂ = a₁ * ↑b₂) :
    mk' f a₁ a₂ = mk' f b₁ b₂ :=
  iff.mpr (mk'_eq_iff_eq f) (H ▸ rfl)

@[simp] theorem Mathlib.add_submonoid.localization_map.mk'_self' {M : Type u_1} [add_comm_monoid M]
    {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N]
    (f : add_submonoid.localization_map S N) (y : ↥S) :
    add_submonoid.localization_map.mk' f (↑y) y = 0 :=
  sorry

@[simp] theorem mk'_self {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2}
    [comm_monoid N] (f : localization_map S N) (x : M) (H : x ∈ S) :
    mk' f x { val := x, property := H } = 1 :=
  sorry

theorem mul_mk'_eq_mk'_of_mul {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2}
    [comm_monoid N] (f : localization_map S N) (x₁ : M) (x₂ : M) (y : ↥S) :
    coe_fn (to_map f) x₁ * mk' f x₂ y = mk' f (x₁ * x₂) y :=
  sorry

theorem Mathlib.add_submonoid.localization_map.mk'_add_eq_mk'_of_add {M : Type u_1}
    [add_comm_monoid M] {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N]
    (f : add_submonoid.localization_map S N) (x₁ : M) (x₂ : M) (y : ↥S) :
    add_submonoid.localization_map.mk' f x₂ y +
          coe_fn (add_submonoid.localization_map.to_map f) x₁ =
        add_submonoid.localization_map.mk' f (x₁ + x₂) y :=
  sorry

theorem Mathlib.add_submonoid.localization_map.add_mk'_zero_eq_mk' {M : Type u_1}
    [add_comm_monoid M] {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N]
    (f : add_submonoid.localization_map S N) (x : M) (y : ↥S) :
    coe_fn (add_submonoid.localization_map.to_map f) x + add_submonoid.localization_map.mk' f 0 y =
        add_submonoid.localization_map.mk' f x y :=
  sorry

@[simp] theorem mk'_mul_cancel_right {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2}
    [comm_monoid N] (f : localization_map S N) (x : M) (y : ↥S) :
    mk' f (x * ↑y) y = coe_fn (to_map f) x :=
  sorry

theorem Mathlib.add_submonoid.localization_map.mk'_add_cancel_left {M : Type u_1}
    [add_comm_monoid M] {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N]
    (f : add_submonoid.localization_map S N) (x : M) (y : ↥S) :
    add_submonoid.localization_map.mk' f (↑y + x) y =
        coe_fn (add_submonoid.localization_map.to_map f) x :=
  sorry

theorem is_unit_comp {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2} [comm_monoid N]
    {P : Type u_3} [comm_monoid P] (f : localization_map S N) (j : N →* P) (y : ↥S) :
    is_unit (coe_fn (monoid_hom.comp j (to_map f)) ↑y) :=
  sorry

/-- Given a localization map `f : M →* N` for a submonoid `S ⊆ M` and a map of `comm_monoid`s
`g : M →* P` such that `g(S) ⊆ units P`, `f x = f y → g x = g y` for all `x y : M`. -/
theorem eq_of_eq {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2} [comm_monoid N]
    {P : Type u_3} [comm_monoid P] (f : localization_map S N) {g : M →* P}
    (hg : ∀ (y : ↥S), is_unit (coe_fn g ↑y)) {x : M} {y : M}
    (h : coe_fn (to_map f) x = coe_fn (to_map f) y) : coe_fn g x = coe_fn g y :=
  sorry

/-- Given `comm_monoid`s `M, P`, localization maps `f : M →* N, k : P →* Q` for submonoids
`S, T` respectively, and `g : M →* P` such that `g(S) ⊆ T`, `f x = f y` implies
`k (g x) = k (g y)`. -/
theorem comp_eq_of_eq {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2}
    [comm_monoid N] {P : Type u_3} [comm_monoid P] (f : localization_map S N) {g : M →* P}
    {T : submonoid P} {Q : Type u_4} [comm_monoid Q] (hg : ∀ (y : ↥S), coe_fn g ↑y ∈ T)
    (k : localization_map T Q) {x : M} {y : M} (h : coe_fn (to_map f) x = coe_fn (to_map f) y) :
    coe_fn (to_map k) (coe_fn g x) = coe_fn (to_map k) (coe_fn g y) :=
  sorry

/-- Given a localization map `f : M →* N` for a submonoid `S ⊆ M` and a map of `comm_monoid`s
`g : M →* P` such that `g y` is invertible for all `y : S`, the homomorphism induced from
`N` to `P` sending `z : N` to `g x * (g y)⁻¹`, where `(x, y) : M × S` are such that
`z = f x * (f y)⁻¹`. -/
def lift {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2} [comm_monoid N]
    {P : Type u_3} [comm_monoid P] (f : localization_map S N) {g : M →* P}
    (hg : ∀ (y : ↥S), is_unit (coe_fn g ↑y)) : N →* P :=
  monoid_hom.mk
    (fun (z : N) =>
      coe_fn g (prod.fst (sec f z)) *
        ↑(coe_fn (is_unit.lift_right (monoid_hom.mrestrict g S) hg) (prod.snd (sec f z))⁻¹))
    sorry sorry

/-- Given a localization map `f : M →* N` for a submonoid `S ⊆ M` and a map of `comm_monoid`s
`g : M →* P` such that `g y` is invertible for all `y : S`, the homomorphism induced from
`N` to `P` maps `f x * (f y)⁻¹` to `g x * (g y)⁻¹` for all `x : M, y ∈ S`. -/
theorem lift_mk' {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2} [comm_monoid N]
    {P : Type u_3} [comm_monoid P] (f : localization_map S N) {g : M →* P}
    (hg : ∀ (y : ↥S), is_unit (coe_fn g ↑y)) (x : M) (y : ↥S) :
    coe_fn (lift f hg) (mk' f x y) =
        coe_fn g x * ↑(coe_fn (is_unit.lift_right (monoid_hom.mrestrict g S) hg) y⁻¹) :=
  sorry

/-- Given a localization map `f : M →* N` for a submonoid `S ⊆ M`, if a `comm_monoid` map
`g : M →* P` induces a map `f.lift hg : N →* P` then for all `z : N, v : P`, we have
`f.lift hg z = v ↔ g x = g y * v`, where `x : M, y ∈ S` are such that `z * f y = f x`. -/
theorem Mathlib.add_submonoid.localization_map.lift_spec {M : Type u_1} [add_comm_monoid M]
    {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N] {P : Type u_3} [add_comm_monoid P]
    (f : add_submonoid.localization_map S N) {g : M →+ P}
    (hg : ∀ (y : ↥S), is_add_unit (coe_fn g ↑y)) (z : N) (v : P) :
    coe_fn (add_submonoid.localization_map.lift f hg) z = v ↔
        coe_fn g (prod.fst (add_submonoid.localization_map.sec f z)) =
          coe_fn g ↑(prod.snd (add_submonoid.localization_map.sec f z)) + v :=
  add_submonoid.localization_map.add_neg_left hg (prod.snd (add_submonoid.localization_map.sec f z))
    (coe_fn g (prod.fst (add_submonoid.localization_map.sec f z))) v

/-- Given a localization map `f : M →* N` for a submonoid `S ⊆ M`, if a `comm_monoid` map
`g : M →* P` induces a map `f.lift hg : N →* P` then for all `z : N, v w : P`, we have
`f.lift hg z * w = v ↔ g x * w = g y * v`, where `x : M, y ∈ S` are such that
`z * f y = f x`. -/
theorem Mathlib.add_submonoid.localization_map.lift_spec_add {M : Type u_1} [add_comm_monoid M]
    {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N] {P : Type u_3} [add_comm_monoid P]
    (f : add_submonoid.localization_map S N) {g : M →+ P}
    (hg : ∀ (y : ↥S), is_add_unit (coe_fn g ↑y)) (z : N) (w : P) (v : P) :
    coe_fn (add_submonoid.localization_map.lift f hg) z + w = v ↔
        coe_fn g (prod.fst (add_submonoid.localization_map.sec f z)) + w =
          coe_fn g ↑(prod.snd (add_submonoid.localization_map.sec f z)) + v :=
  sorry

theorem lift_mk'_spec {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2}
    [comm_monoid N] {P : Type u_3} [comm_monoid P] (f : localization_map S N) {g : M →* P}
    (hg : ∀ (y : ↥S), is_unit (coe_fn g ↑y)) (x : M) (v : P) (y : ↥S) :
    coe_fn (lift f hg) (mk' f x y) = v ↔ coe_fn g x = coe_fn g ↑y * v :=
  eq.mpr
    (id
      (Eq._oldrec (Eq.refl (coe_fn (lift f hg) (mk' f x y) = v ↔ coe_fn g x = coe_fn g ↑y * v))
        (lift_mk' f hg x y)))
    (mul_inv_left hg y (coe_fn g x) v)

/-- Given a localization map `f : M →* N` for a submonoid `S ⊆ M`, if a `comm_monoid` map
`g : M →* P` induces a map `f.lift hg : N →* P` then for all `z : N`, we have
`f.lift hg z * g y = g x`, where `x : M, y ∈ S` are such that `z * f y = f x`. -/
theorem lift_mul_right {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2}
    [comm_monoid N] {P : Type u_3} [comm_monoid P] (f : localization_map S N) {g : M →* P}
    (hg : ∀ (y : ↥S), is_unit (coe_fn g ↑y)) (z : N) :
    coe_fn (lift f hg) z * coe_fn g ↑(prod.snd (sec f z)) = coe_fn g (prod.fst (sec f z)) :=
  sorry

/-- Given a localization map `f : M →* N` for a submonoid `S ⊆ M`, if a `comm_monoid` map
`g : M →* P` induces a map `f.lift hg : N →* P` then for all `z : N`, we have
`g y * f.lift hg z = g x`, where `x : M, y ∈ S` are such that `z * f y = f x`. -/
theorem Mathlib.add_submonoid.localization_map.lift_add_left {M : Type u_1} [add_comm_monoid M]
    {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N] {P : Type u_3} [add_comm_monoid P]
    (f : add_submonoid.localization_map S N) {g : M →+ P}
    (hg : ∀ (y : ↥S), is_add_unit (coe_fn g ↑y)) (z : N) :
    coe_fn g ↑(prod.snd (add_submonoid.localization_map.sec f z)) +
          coe_fn (add_submonoid.localization_map.lift f hg) z =
        coe_fn g (prod.fst (add_submonoid.localization_map.sec f z)) :=
  sorry

@[simp] theorem lift_eq {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2}
    [comm_monoid N] {P : Type u_3} [comm_monoid P] (f : localization_map S N) {g : M →* P}
    (hg : ∀ (y : ↥S), is_unit (coe_fn g ↑y)) (x : M) :
    coe_fn (lift f hg) (coe_fn (to_map f) x) = coe_fn g x :=
  sorry

theorem Mathlib.add_submonoid.localization_map.lift_eq_iff {M : Type u_1} [add_comm_monoid M]
    {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N] {P : Type u_3} [add_comm_monoid P]
    (f : add_submonoid.localization_map S N) {g : M →+ P}
    (hg : ∀ (y : ↥S), is_add_unit (coe_fn g ↑y)) {x : M × ↥S} {y : M × ↥S} :
    coe_fn (add_submonoid.localization_map.lift f hg)
            (add_submonoid.localization_map.mk' f (prod.fst x) (prod.snd x)) =
          coe_fn (add_submonoid.localization_map.lift f hg)
            (add_submonoid.localization_map.mk' f (prod.fst y) (prod.snd y)) ↔
        coe_fn g (prod.fst x + ↑(prod.snd y)) = coe_fn g (prod.fst y + ↑(prod.snd x)) :=
  sorry

@[simp] theorem Mathlib.add_submonoid.localization_map.lift_comp {M : Type u_1} [add_comm_monoid M]
    {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N] {P : Type u_3} [add_comm_monoid P]
    (f : add_submonoid.localization_map S N) {g : M →+ P}
    (hg : ∀ (y : ↥S), is_add_unit (coe_fn g ↑y)) :
    add_monoid_hom.comp (add_submonoid.localization_map.lift f hg)
          (add_submonoid.localization_map.to_map f) =
        g :=
  add_monoid_hom.ext fun (x : M) => add_submonoid.localization_map.lift_eq f hg x

@[simp] theorem lift_of_comp {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2}
    [comm_monoid N] {P : Type u_3} [comm_monoid P] (f : localization_map S N) (j : N →* P) :
    lift f (is_unit_comp f j) = j :=
  sorry

theorem epic_of_localization_map {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2}
    [comm_monoid N] {P : Type u_3} [comm_monoid P] (f : localization_map S N) {j : N →* P}
    {k : N →* P}
    (h :
      ∀ (a : M),
        coe_fn (monoid_hom.comp j (to_map f)) a = coe_fn (monoid_hom.comp k (to_map f)) a) :
    j = k :=
  sorry

theorem Mathlib.add_submonoid.localization_map.lift_unique {M : Type u_1} [add_comm_monoid M]
    {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N] {P : Type u_3} [add_comm_monoid P]
    (f : add_submonoid.localization_map S N) {g : M →+ P}
    (hg : ∀ (y : ↥S), is_add_unit (coe_fn g ↑y)) {j : N →+ P}
    (hj : ∀ (x : M), coe_fn j (coe_fn (add_submonoid.localization_map.to_map f) x) = coe_fn g x) :
    add_submonoid.localization_map.lift f hg = j :=
  sorry

@[simp] theorem Mathlib.add_submonoid.localization_map.lift_id {M : Type u_1} [add_comm_monoid M]
    {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N]
    (f : add_submonoid.localization_map S N) (x : N) :
    coe_fn (add_submonoid.localization_map.lift f (add_submonoid.localization_map.map_units f)) x =
        x :=
  iff.mp add_monoid_hom.ext_iff
    (add_submonoid.localization_map.lift_of_comp f (add_monoid_hom.id N)) x

/-- Given two localization maps `f : M →* N, k : M →* P` for a submonoid `S ⊆ M`,
the hom from `P` to `N` induced by `f` is left inverse to the hom from `N` to `P`
induced by `k`. -/
@[simp] theorem lift_left_inverse {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2}
    [comm_monoid N] {P : Type u_3} [comm_monoid P] (f : localization_map S N)
    {k : localization_map S P} (z : N) :
    coe_fn (lift k (map_units f)) (coe_fn (lift f (map_units k)) z) = z :=
  sorry

theorem lift_surjective_iff {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2}
    [comm_monoid N] {P : Type u_3} [comm_monoid P] (f : localization_map S N) {g : M →* P}
    (hg : ∀ (y : ↥S), is_unit (coe_fn g ↑y)) :
    function.surjective ⇑(lift f hg) ↔
        ∀ (v : P), ∃ (x : M × ↥S), v * coe_fn g ↑(prod.snd x) = coe_fn g (prod.fst x) :=
  sorry

theorem lift_injective_iff {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2}
    [comm_monoid N] {P : Type u_3} [comm_monoid P] (f : localization_map S N) {g : M →* P}
    (hg : ∀ (y : ↥S), is_unit (coe_fn g ↑y)) :
    function.injective ⇑(lift f hg) ↔
        ∀ (x y : M), coe_fn (to_map f) x = coe_fn (to_map f) y ↔ coe_fn g x = coe_fn g y :=
  sorry

/-- Given a `comm_monoid` homomorphism `g : M →* P` where for submonoids `S ⊆ M, T ⊆ P` we have
`g(S) ⊆ T`, the induced monoid homomorphism from the localization of `M` at `S` to the
localization of `P` at `T`: if `f : M →* N` and `k : P →* Q` are localization maps for `S` and
`T` respectively, we send `z : N` to `k (g x) * (k (g y))⁻¹`, where `(x, y) : M × S` are such
that `z = f x * (f y)⁻¹`. -/
def map {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2} [comm_monoid N]
    {P : Type u_3} [comm_monoid P] (f : localization_map S N) {g : M →* P} {T : submonoid P}
    (hy : ∀ (y : ↥S), coe_fn g ↑y ∈ T) {Q : Type u_4} [comm_monoid Q] (k : localization_map T Q) :
    N →* Q :=
  lift f sorry

theorem Mathlib.add_submonoid.localization_map.map_eq {M : Type u_1} [add_comm_monoid M]
    {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N] {P : Type u_3} [add_comm_monoid P]
    (f : add_submonoid.localization_map S N) {g : M →+ P} {T : add_submonoid P}
    (hy : ∀ (y : ↥S), coe_fn g ↑y ∈ T) {Q : Type u_4} [add_comm_monoid Q]
    {k : add_submonoid.localization_map T Q} (x : M) :
    coe_fn (add_submonoid.localization_map.map f hy k)
          (coe_fn (add_submonoid.localization_map.to_map f) x) =
        coe_fn (add_submonoid.localization_map.to_map k) (coe_fn g x) :=
  add_submonoid.localization_map.lift_eq f
    (fun (y : ↥S) =>
      add_submonoid.localization_map.map_units k { val := coe_fn g ↑y, property := hy y })
    x

@[simp] theorem Mathlib.add_submonoid.localization_map.map_comp {M : Type u_1} [add_comm_monoid M]
    {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N] {P : Type u_3} [add_comm_monoid P]
    (f : add_submonoid.localization_map S N) {g : M →+ P} {T : add_submonoid P}
    (hy : ∀ (y : ↥S), coe_fn g ↑y ∈ T) {Q : Type u_4} [add_comm_monoid Q]
    {k : add_submonoid.localization_map T Q} :
    add_monoid_hom.comp (add_submonoid.localization_map.map f hy k)
          (add_submonoid.localization_map.to_map f) =
        add_monoid_hom.comp (add_submonoid.localization_map.to_map k) g :=
  add_submonoid.localization_map.lift_comp f
    fun (y : ↥S) =>
      add_submonoid.localization_map.map_units k { val := coe_fn g ↑y, property := hy y }

theorem map_mk' {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2} [comm_monoid N]
    {P : Type u_3} [comm_monoid P] (f : localization_map S N) {g : M →* P} {T : submonoid P}
    (hy : ∀ (y : ↥S), coe_fn g ↑y ∈ T) {Q : Type u_4} [comm_monoid Q] {k : localization_map T Q}
    (x : M) (y : ↥S) :
    coe_fn (map f hy k) (mk' f x y) = mk' k (coe_fn g x) { val := coe_fn g ↑y, property := hy y } :=
  sorry

/-- Given localization maps `f : M →* N, k : P →* Q` for submonoids `S, T` respectively, if a
`comm_monoid` homomorphism `g : M →* P` induces a `f.map hy k : N →* Q`, then for all `z : N`,
`u : Q`, we have `f.map hy k z = u ↔ k (g x) = k (g y) * u` where `x : M, y ∈ S` are such that
`z * f y = f x`. -/
theorem map_spec {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2} [comm_monoid N]
    {P : Type u_3} [comm_monoid P] (f : localization_map S N) {g : M →* P} {T : submonoid P}
    (hy : ∀ (y : ↥S), coe_fn g ↑y ∈ T) {Q : Type u_4} [comm_monoid Q] {k : localization_map T Q}
    (z : N) (u : Q) :
    coe_fn (map f hy k) z = u ↔
        coe_fn (to_map k) (coe_fn g (prod.fst (sec f z))) =
          coe_fn (to_map k) (coe_fn g ↑(prod.snd (sec f z))) * u :=
  lift_spec f (fun (y : ↥S) => map_units k { val := coe_fn g ↑y, property := hy y }) z u

/-- Given localization maps `f : M →* N, k : P →* Q` for submonoids `S, T` respectively, if a
`comm_monoid` homomorphism `g : M →* P` induces a `f.map hy k : N →* Q`, then for all `z : N`,
we have `f.map hy k z * k (g y) = k (g x)` where `x : M, y ∈ S` are such that
`z * f y = f x`. -/
theorem map_mul_right {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2}
    [comm_monoid N] {P : Type u_3} [comm_monoid P] (f : localization_map S N) {g : M →* P}
    {T : submonoid P} (hy : ∀ (y : ↥S), coe_fn g ↑y ∈ T) {Q : Type u_4} [comm_monoid Q]
    {k : localization_map T Q} (z : N) :
    coe_fn (map f hy k) z * coe_fn (to_map k) (coe_fn g ↑(prod.snd (sec f z))) =
        coe_fn (to_map k) (coe_fn g (prod.fst (sec f z))) :=
  lift_mul_right f (fun (y : ↥S) => map_units k { val := coe_fn g ↑y, property := hy y }) z

/-- Given localization maps `f : M →* N, k : P →* Q` for submonoids `S, T` respectively, if a
`comm_monoid` homomorphism `g : M →* P` induces a `f.map hy k : N →* Q`, then for all `z : N`,
we have `k (g y) * f.map hy k z = k (g x)` where `x : M, y ∈ S` are such that
`z * f y = f x`. -/
theorem Mathlib.add_submonoid.localization_map.map_add_left {M : Type u_1} [add_comm_monoid M]
    {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N] {P : Type u_3} [add_comm_monoid P]
    (f : add_submonoid.localization_map S N) {g : M →+ P} {T : add_submonoid P}
    (hy : ∀ (y : ↥S), coe_fn g ↑y ∈ T) {Q : Type u_4} [add_comm_monoid Q]
    {k : add_submonoid.localization_map T Q} (z : N) :
    coe_fn (add_submonoid.localization_map.to_map k)
            (coe_fn g ↑(prod.snd (add_submonoid.localization_map.sec f z))) +
          coe_fn (add_submonoid.localization_map.map f hy k) z =
        coe_fn (add_submonoid.localization_map.to_map k)
          (coe_fn g (prod.fst (add_submonoid.localization_map.sec f z))) :=
  sorry

@[simp] theorem Mathlib.add_submonoid.localization_map.map_id {M : Type u_1} [add_comm_monoid M]
    {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N]
    (f : add_submonoid.localization_map S N) (z : N) :
    coe_fn
          (add_submonoid.localization_map.map f
            (fun (y : ↥S) =>
              (fun (this : coe_fn (add_monoid_hom.id M) ↑y ∈ S) => this) (subtype.property y))
            f)
          z =
        z :=
  add_submonoid.localization_map.lift_id f z

/-- If `comm_monoid` homs `g : M →* P, l : P →* A` induce maps of localizations, the composition
of the induced maps equals the map of localizations induced by `l ∘ g`. -/
theorem map_comp_map {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2} [comm_monoid N]
    {P : Type u_3} [comm_monoid P] (f : localization_map S N) {g : M →* P} {T : submonoid P}
    (hy : ∀ (y : ↥S), coe_fn g ↑y ∈ T) {Q : Type u_4} [comm_monoid Q] {k : localization_map T Q}
    {A : Type u_5} [comm_monoid A] {U : submonoid A} {R : Type u_6} [comm_monoid R]
    (j : localization_map U R) {l : P →* A} (hl : ∀ (w : ↥T), coe_fn l ↑w ∈ U) :
    monoid_hom.comp (map k hl j) (map f hy k) =
        map f
          (fun (x : ↥S) =>
            (fun (this : coe_fn (monoid_hom.comp l g) ↑x ∈ U) => this)
              (hl { val := coe_fn g ↑x, property := hy x }))
          j :=
  sorry

/-- If `comm_monoid` homs `g : M →* P, l : P →* A` induce maps of localizations, the composition
of the induced maps equals the map of localizations induced by `l ∘ g`. -/
theorem Mathlib.add_submonoid.localization_map.map_map {M : Type u_1} [add_comm_monoid M]
    {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N] {P : Type u_3} [add_comm_monoid P]
    (f : add_submonoid.localization_map S N) {g : M →+ P} {T : add_submonoid P}
    (hy : ∀ (y : ↥S), coe_fn g ↑y ∈ T) {Q : Type u_4} [add_comm_monoid Q]
    {k : add_submonoid.localization_map T Q} {A : Type u_5} [add_comm_monoid A]
    {U : add_submonoid A} {R : Type u_6} [add_comm_monoid R]
    (j : add_submonoid.localization_map U R) {l : P →+ A} (hl : ∀ (w : ↥T), coe_fn l ↑w ∈ U)
    (x : N) :
    coe_fn (add_submonoid.localization_map.map k hl j)
          (coe_fn (add_submonoid.localization_map.map f hy k) x) =
        coe_fn
          (add_submonoid.localization_map.map f
            (fun (x : ↥S) =>
              (fun (this : coe_fn (add_monoid_hom.comp l g) ↑x ∈ U) => this)
                (hl { val := coe_fn g ↑x, property := hy x }))
            j)
          x :=
  sorry

/-- Given `x : M`, the type of `comm_monoid` homomorphisms `f : M →* N` such that `N`
is isomorphic to the localization of `M` at the submonoid generated by `x`. -/
def away_map {M : Type u_1} [comm_monoid M] (x : M) (N' : Type u_2) [comm_monoid N'] :=
  localization_map (powers x) N'

/-- Given `x : M` and a localization map `F : M →* N` away from `x`, `inv_self` is `(F x)⁻¹`. -/
def away_map.inv_self {M : Type u_1} [comm_monoid M] {N : Type u_2} [comm_monoid N] (x : M)
    (F : away_map x N) : N :=
  mk' F 1 { val := x, property := sorry }

/-- Given `x : M`, a localization map `F : M →* N` away from `x`, and a map of `comm_monoid`s
`g : M →* P` such that `g x` is invertible, the homomorphism induced from `N` to `P` sending
`z : N` to `g y * (g x)⁻ⁿ`, where `y : M, n : ℕ` are such that `z = F y * (F x)⁻ⁿ`. -/
def away_map.lift {M : Type u_1} [comm_monoid M] {N : Type u_2} [comm_monoid N] {P : Type u_3}
    [comm_monoid P] {g : M →* P} (x : M) (F : away_map x N) (hg : is_unit (coe_fn g x)) : N →* P :=
  lift F sorry

@[simp] theorem away_map.lift_eq {M : Type u_1} [comm_monoid M] {N : Type u_2} [comm_monoid N]
    {P : Type u_3} [comm_monoid P] {g : M →* P} (x : M) (F : away_map x N)
    (hg : is_unit (coe_fn g x)) (a : M) :
    coe_fn (away_map.lift x F hg) (coe_fn (to_map F) a) = coe_fn g a :=
  lift_eq F (away_map.lift._proof_1 x hg) a

@[simp] theorem away_map.lift_comp {M : Type u_1} [comm_monoid M] {N : Type u_2} [comm_monoid N]
    {P : Type u_3} [comm_monoid P] {g : M →* P} (x : M) (F : away_map x N)
    (hg : is_unit (coe_fn g x)) : monoid_hom.comp (away_map.lift x F hg) (to_map F) = g :=
  lift_comp F (away_map.lift._proof_1 x hg)

/-- Given `x y : M` and localization maps `F : M →* N, G : M →* P` away from `x` and `x * y`
respectively, the homomorphism induced from `N` to `P`. -/
def away_to_away_right {M : Type u_1} [comm_monoid M] {N : Type u_2} [comm_monoid N] {P : Type u_3}
    [comm_monoid P] (x : M) (F : away_map x N) (y : M) (G : away_map (x * y) P) : N →* P :=
  away_map.lift x F sorry

end localization_map

end submonoid

namespace add_submonoid

namespace localization_map

/-- Given `x : A` and a localization map `F : A →+ B` away from `x`, `neg_self` is `- (F x)`. -/
def away_map.neg_self {A : Type u_4} [add_comm_monoid A] (x : A) {B : Type u_5} [add_comm_monoid B]
    (F : away_map x B) : B :=
  mk' F 0 { val := x, property := sorry }

/-- Given `x : A`, a localization map `F : A →+ B` away from `x`, and a map of `add_comm_monoid`s
`g : A →+ C` such that `g x` is invertible, the homomorphism induced from `B` to `C` sending
`z : B` to `g y - n • g x`, where `y : A, n : ℕ` are such that `z = F y - n • F x`. -/
def away_map.lift {A : Type u_4} [add_comm_monoid A] (x : A) {B : Type u_5} [add_comm_monoid B]
    (F : away_map x B) {C : Type u_6} [add_comm_monoid C] {g : A →+ C}
    (hg : is_add_unit (coe_fn g x)) : B →+ C :=
  lift F sorry

@[simp] theorem away_map.lift_eq {A : Type u_4} [add_comm_monoid A] (x : A) {B : Type u_5}
    [add_comm_monoid B] (F : away_map x B) {C : Type u_6} [add_comm_monoid C] {g : A →+ C}
    (hg : is_add_unit (coe_fn g x)) (a : A) :
    coe_fn (away_map.lift x F hg) (coe_fn (to_map F) a) = coe_fn g a :=
  lift_eq F (away_map.lift._proof_1 x hg) a

@[simp] theorem away_map.lift_comp {A : Type u_4} [add_comm_monoid A] (x : A) {B : Type u_5}
    [add_comm_monoid B] (F : away_map x B) {C : Type u_6} [add_comm_monoid C] {g : A →+ C}
    (hg : is_add_unit (coe_fn g x)) : add_monoid_hom.comp (away_map.lift x F hg) (to_map F) = g :=
  lift_comp F (away_map.lift._proof_1 x hg)

/-- Given `x y : A` and localization maps `F : A →+ B, G : A →+ C` away from `x` and `x + y`
respectively, the homomorphism induced from `B` to `C`. -/
def away_to_away_right {A : Type u_4} [add_comm_monoid A] (x : A) {B : Type u_5} [add_comm_monoid B]
    (F : away_map x B) {C : Type u_6} [add_comm_monoid C] (y : A) (G : away_map (x + y) C) :
    B →+ C :=
  away_map.lift x F sorry

end localization_map

end add_submonoid

namespace submonoid

namespace localization_map

/-- If `f : M →* N` and `k : M →* P` are localization maps for a submonoid `S`, we get an
isomorphism of `N` and `P`. -/
def mul_equiv_of_localizations {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2}
    [comm_monoid N] {P : Type u_3} [comm_monoid P] (f : localization_map S N)
    (k : localization_map S P) : N ≃* P :=
  mul_equiv.mk (⇑(lift f (map_units k))) (⇑(lift k (map_units f))) (lift_left_inverse f)
    (lift_left_inverse k) sorry

@[simp] theorem Mathlib.add_submonoid.localization_map.add_equiv_of_localizations_apply
    {M : Type u_1} [add_comm_monoid M] {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N]
    {P : Type u_3} [add_comm_monoid P] (f : add_submonoid.localization_map S N)
    {k : add_submonoid.localization_map S P} {x : N} :
    coe_fn (add_submonoid.localization_map.add_equiv_of_localizations f k) x =
        coe_fn (add_submonoid.localization_map.lift f (add_submonoid.localization_map.map_units k))
          x :=
  rfl

@[simp] theorem Mathlib.add_submonoid.localization_map.add_equiv_of_localizations_symm_apply
    {M : Type u_1} [add_comm_monoid M] {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N]
    {P : Type u_3} [add_comm_monoid P] (f : add_submonoid.localization_map S N)
    {k : add_submonoid.localization_map S P} {x : P} :
    coe_fn (add_equiv.symm (add_submonoid.localization_map.add_equiv_of_localizations f k)) x =
        coe_fn (add_submonoid.localization_map.lift k (add_submonoid.localization_map.map_units f))
          x :=
  rfl

theorem Mathlib.add_submonoid.localization_map.add_equiv_of_localizations_symm_eq_add_equiv_of_localizations
    {M : Type u_1} [add_comm_monoid M] {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N]
    {P : Type u_3} [add_comm_monoid P] (f : add_submonoid.localization_map S N)
    {k : add_submonoid.localization_map S P} :
    add_equiv.symm (add_submonoid.localization_map.add_equiv_of_localizations k f) =
        add_submonoid.localization_map.add_equiv_of_localizations f k :=
  rfl

/-- If `f : M →* N` is a localization map for a submonoid `S` and `k : N ≃* P` is an isomorphism
of `comm_monoid`s, `k ∘ f` is a localization map for `M` at `S`. -/
def of_mul_equiv_of_localizations {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2}
    [comm_monoid N] {P : Type u_3} [comm_monoid P] (f : localization_map S N) (k : N ≃* P) :
    localization_map S P :=
  monoid_hom.to_localization_map (monoid_hom.comp (mul_equiv.to_monoid_hom k) (to_map f)) sorry
    sorry sorry

@[simp] theorem Mathlib.add_submonoid.localization_map.of_add_equiv_of_localizations_apply
    {M : Type u_1} [add_comm_monoid M] {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N]
    {P : Type u_3} [add_comm_monoid P] (f : add_submonoid.localization_map S N) {k : N ≃+ P}
    (x : M) :
    coe_fn
          (add_submonoid.localization_map.to_map
            (add_submonoid.localization_map.of_add_equiv_of_localizations f k))
          x =
        coe_fn k (coe_fn (add_submonoid.localization_map.to_map f) x) :=
  rfl

theorem of_mul_equiv_of_localizations_eq {M : Type u_1} [comm_monoid M] {S : submonoid M}
    {N : Type u_2} [comm_monoid N] {P : Type u_3} [comm_monoid P] (f : localization_map S N)
    {k : N ≃* P} :
    to_map (of_mul_equiv_of_localizations f k) =
        monoid_hom.comp (mul_equiv.to_monoid_hom k) (to_map f) :=
  rfl

theorem symm_comp_of_mul_equiv_of_localizations_apply {M : Type u_1} [comm_monoid M]
    {S : submonoid M} {N : Type u_2} [comm_monoid N] {P : Type u_3} [comm_monoid P]
    (f : localization_map S N) {k : N ≃* P} (x : M) :
    coe_fn (mul_equiv.symm k) (coe_fn (to_map (of_mul_equiv_of_localizations f k)) x) =
        coe_fn (to_map f) x :=
  mul_equiv.symm_apply_apply k (coe_fn (to_map f) x)

theorem symm_comp_of_mul_equiv_of_localizations_apply' {M : Type u_1} [comm_monoid M]
    {S : submonoid M} {N : Type u_2} [comm_monoid N] {P : Type u_3} [comm_monoid P]
    (f : localization_map S N) {k : P ≃* N} (x : M) :
    coe_fn k (coe_fn (to_map (of_mul_equiv_of_localizations f (mul_equiv.symm k))) x) =
        coe_fn (to_map f) x :=
  mul_equiv.apply_symm_apply k (coe_fn (to_map f) x)

theorem of_mul_equiv_of_localizations_eq_iff_eq {M : Type u_1} [comm_monoid M] {S : submonoid M}
    {N : Type u_2} [comm_monoid N] {P : Type u_3} [comm_monoid P] (f : localization_map S N)
    {k : N ≃* P} {x : M} {y : P} :
    coe_fn (to_map (of_mul_equiv_of_localizations f k)) x = y ↔
        coe_fn (to_map f) x = coe_fn (mul_equiv.symm k) y :=
  iff.symm (equiv.eq_symm_apply (mul_equiv.to_equiv k))

theorem mul_equiv_of_localizations_right_inv {M : Type u_1} [comm_monoid M] {S : submonoid M}
    {N : Type u_2} [comm_monoid N] {P : Type u_3} [comm_monoid P] (f : localization_map S N)
    (k : localization_map S P) :
    of_mul_equiv_of_localizations f (mul_equiv_of_localizations f k) = k :=
  to_map_injective (lift_comp f (map_units k))

theorem Mathlib.add_submonoid.localization_map.add_equiv_of_localizations_right_inv_apply
    {M : Type u_1} [add_comm_monoid M] {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N]
    {P : Type u_3} [add_comm_monoid P] (f : add_submonoid.localization_map S N)
    {k : add_submonoid.localization_map S P} {x : M} :
    coe_fn
          (add_submonoid.localization_map.to_map
            (add_submonoid.localization_map.of_add_equiv_of_localizations f
              (add_submonoid.localization_map.add_equiv_of_localizations f k)))
          x =
        coe_fn (add_submonoid.localization_map.to_map k) x :=
  iff.mp add_submonoid.localization_map.ext_iff
    (add_submonoid.localization_map.add_equiv_of_localizations_right_inv f k) x

theorem Mathlib.add_submonoid.localization_map.add_equiv_of_localizations_left_neg {M : Type u_1}
    [add_comm_monoid M] {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N] {P : Type u_3}
    [add_comm_monoid P] (f : add_submonoid.localization_map S N) (k : N ≃+ P) :
    add_submonoid.localization_map.add_equiv_of_localizations f
          (add_submonoid.localization_map.of_add_equiv_of_localizations f k) =
        k :=
  add_equiv.ext
    (iff.mp add_monoid_hom.ext_iff
      (add_submonoid.localization_map.lift_of_comp f (add_equiv.to_add_monoid_hom k)))

@[simp] theorem mul_equiv_of_localizations_left_inv_apply {M : Type u_1} [comm_monoid M]
    {S : submonoid M} {N : Type u_2} [comm_monoid N] {P : Type u_3} [comm_monoid P]
    (f : localization_map S N) {k : N ≃* P} (x : N) :
    coe_fn (mul_equiv_of_localizations f (of_mul_equiv_of_localizations f k)) x = coe_fn k x :=
  sorry

@[simp] theorem Mathlib.add_submonoid.localization_map.of_add_equiv_of_localizations_id
    {M : Type u_1} [add_comm_monoid M] {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N]
    (f : add_submonoid.localization_map S N) :
    add_submonoid.localization_map.of_add_equiv_of_localizations f (add_equiv.refl N) = f :=
  sorry

theorem Mathlib.add_submonoid.localization_map.of_add_equiv_of_localizations_comp {M : Type u_1}
    [add_comm_monoid M] {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N] {P : Type u_3}
    [add_comm_monoid P] (f : add_submonoid.localization_map S N) {Q : Type u_4} [add_comm_monoid Q]
    {k : N ≃+ P} {j : P ≃+ Q} :
    add_submonoid.localization_map.to_map
          (add_submonoid.localization_map.of_add_equiv_of_localizations f (add_equiv.trans k j)) =
        add_monoid_hom.comp (add_equiv.to_add_monoid_hom j)
          (add_submonoid.localization_map.to_map
            (add_submonoid.localization_map.of_add_equiv_of_localizations f k)) :=
  sorry

/-- Given `comm_monoid`s `M, P` and submonoids `S ⊆ M, T ⊆ P`, if `f : M →* N` is a localization
map for `S` and `k : P ≃* M` is an isomorphism of `comm_monoid`s such that `k(T) = S`, `f ∘ k`
is a localization map for `T`. -/
def Mathlib.add_submonoid.localization_map.of_add_equiv_of_dom {M : Type u_1} [add_comm_monoid M]
    {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N] {P : Type u_3} [add_comm_monoid P]
    (f : add_submonoid.localization_map S N) {T : add_submonoid P} {k : P ≃+ M}
    (H : add_submonoid.map (add_equiv.to_add_monoid_hom k) T = S) :
    add_submonoid.localization_map T N :=
  add_monoid_hom.to_localization_map
    (add_monoid_hom.comp (add_submonoid.localization_map.to_map f) (add_equiv.to_add_monoid_hom k))
    sorry sorry sorry

@[simp] theorem of_mul_equiv_of_dom_apply {M : Type u_1} [comm_monoid M] {S : submonoid M}
    {N : Type u_2} [comm_monoid N] {P : Type u_3} [comm_monoid P] (f : localization_map S N)
    {T : submonoid P} {k : P ≃* M} (H : map (mul_equiv.to_monoid_hom k) T = S) (x : P) :
    coe_fn (to_map (of_mul_equiv_of_dom f H)) x = coe_fn (to_map f) (coe_fn k x) :=
  rfl

theorem Mathlib.add_submonoid.localization_map.of_add_equiv_of_dom_eq {M : Type u_1}
    [add_comm_monoid M] {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N] {P : Type u_3}
    [add_comm_monoid P] (f : add_submonoid.localization_map S N) {T : add_submonoid P} {k : P ≃+ M}
    (H : add_submonoid.map (add_equiv.to_add_monoid_hom k) T = S) :
    add_submonoid.localization_map.to_map (add_submonoid.localization_map.of_add_equiv_of_dom f H) =
        add_monoid_hom.comp (add_submonoid.localization_map.to_map f)
          (add_equiv.to_add_monoid_hom k) :=
  rfl

theorem of_mul_equiv_of_dom_comp_symm {M : Type u_1} [comm_monoid M] {S : submonoid M}
    {N : Type u_2} [comm_monoid N] {P : Type u_3} [comm_monoid P] (f : localization_map S N)
    {T : submonoid P} {k : P ≃* M} (H : map (mul_equiv.to_monoid_hom k) T = S) (x : M) :
    coe_fn (to_map (of_mul_equiv_of_dom f H)) (coe_fn (mul_equiv.symm k) x) = coe_fn (to_map f) x :=
  congr_arg (⇑(to_map f)) (mul_equiv.apply_symm_apply k x)

theorem Mathlib.add_submonoid.localization_map.of_add_equiv_of_dom_comp {M : Type u_1}
    [add_comm_monoid M] {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N] {P : Type u_3}
    [add_comm_monoid P] (f : add_submonoid.localization_map S N) {T : add_submonoid P} {k : M ≃+ P}
    (H : add_submonoid.map (add_equiv.to_add_monoid_hom (add_equiv.symm k)) T = S) (x : M) :
    coe_fn
          (add_submonoid.localization_map.to_map
            (add_submonoid.localization_map.of_add_equiv_of_dom f H))
          (coe_fn k x) =
        coe_fn (add_submonoid.localization_map.to_map f) x :=
  congr_arg (⇑(add_submonoid.localization_map.to_map f)) (add_equiv.symm_apply_apply k x)

/-- A special case of `f ∘ id = f`, `f` a localization map. -/
@[simp] theorem of_mul_equiv_of_dom_id {M : Type u_1} [comm_monoid M] {S : submonoid M}
    {N : Type u_2} [comm_monoid N] (f : localization_map S N) :
    of_mul_equiv_of_dom f
          ((fun (this : map (mul_equiv.to_monoid_hom (mul_equiv.refl M)) S = S) => this)
            (ext
              fun (x : M) =>
                { mp :=
                    fun (_x : x ∈ map (mul_equiv.to_monoid_hom (mul_equiv.refl M)) S) =>
                      (fun (_a : x ∈ map (mul_equiv.to_monoid_hom (mul_equiv.refl M)) S) =>
                          Exists.dcases_on _a
                            fun (w : M)
                              (h :
                              w ∈ ↑S ∧ coe_fn (mul_equiv.to_monoid_hom (mul_equiv.refl M)) w = x) =>
                              and.dcases_on h
                                fun (h_left : w ∈ ↑S)
                                  (h_right :
                                  coe_fn (mul_equiv.to_monoid_hom (mul_equiv.refl M)) w = x) =>
                                  idRhs ((fun (_x : M) => _x ∈ S) x) (h_right ▸ h_left))
                        _x,
                  mpr := fun (h : x ∈ S) => Exists.intro x { left := h, right := rfl } })) =
        f :=
  sorry

/-- Given localization maps `f : M →* N, k : P →* U` for submonoids `S, T` respectively, an
isomorphism `j : M ≃* P` such that `j(S) = T` induces an isomorphism of localizations
`N ≃* U`. -/
def Mathlib.add_submonoid.localization_map.add_equiv_of_add_equiv {M : Type u_1} [add_comm_monoid M]
    {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N] {P : Type u_3} [add_comm_monoid P]
    (f : add_submonoid.localization_map S N) {T : add_submonoid P} {Q : Type u_4}
    [add_comm_monoid Q] (k : add_submonoid.localization_map T Q) {j : M ≃+ P}
    (H : add_submonoid.map (add_equiv.to_add_monoid_hom j) S = T) : N ≃+ Q :=
  add_submonoid.localization_map.add_equiv_of_localizations f
    (add_submonoid.localization_map.of_add_equiv_of_dom k H)

@[simp] theorem Mathlib.add_submonoid.localization_map.add_equiv_of_add_equiv_eq_map_apply
    {M : Type u_1} [add_comm_monoid M] {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N]
    {P : Type u_3} [add_comm_monoid P] (f : add_submonoid.localization_map S N)
    {T : add_submonoid P} {Q : Type u_4} [add_comm_monoid Q]
    {k : add_submonoid.localization_map T Q} {j : M ≃+ P}
    (H : add_submonoid.map (add_equiv.to_add_monoid_hom j) S = T) (x : N) :
    coe_fn (add_submonoid.localization_map.add_equiv_of_add_equiv f k H) x =
        coe_fn
          (add_submonoid.localization_map.map f
            (fun (y : ↥S) =>
              (fun (this : coe_fn (add_equiv.to_add_monoid_hom j) ↑y ∈ T) => this)
                (H ▸ set.mem_image_of_mem (⇑j) (subtype.property y)))
            k)
          x :=
  rfl

theorem mul_equiv_of_mul_equiv_eq_map {M : Type u_1} [comm_monoid M] {S : submonoid M}
    {N : Type u_2} [comm_monoid N] {P : Type u_3} [comm_monoid P] (f : localization_map S N)
    {T : submonoid P} {Q : Type u_4} [comm_monoid Q] {k : localization_map T Q} {j : M ≃* P}
    (H : map (mul_equiv.to_monoid_hom j) S = T) :
    mul_equiv.to_monoid_hom (mul_equiv_of_mul_equiv f k H) =
        map f
          (fun (y : ↥S) =>
            (fun (this : coe_fn (mul_equiv.to_monoid_hom j) ↑y ∈ T) => this)
              (H ▸ set.mem_image_of_mem (⇑j) (subtype.property y)))
          k :=
  rfl

@[simp] theorem mul_equiv_of_mul_equiv_eq {M : Type u_1} [comm_monoid M] {S : submonoid M}
    {N : Type u_2} [comm_monoid N] {P : Type u_3} [comm_monoid P] (f : localization_map S N)
    {T : submonoid P} {Q : Type u_4} [comm_monoid Q] {k : localization_map T Q} {j : M ≃* P}
    (H : map (mul_equiv.to_monoid_hom j) S = T) (x : M) :
    coe_fn (mul_equiv_of_mul_equiv f k H) (coe_fn (to_map f) x) = coe_fn (to_map k) (coe_fn j x) :=
  map_eq f (fun (y : ↥S) => H ▸ set.mem_image_of_mem (⇑j) (subtype.property y)) x

@[simp] theorem Mathlib.add_submonoid.localization_map.add_equiv_of_add_equiv_mk' {M : Type u_1}
    [add_comm_monoid M] {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N] {P : Type u_3}
    [add_comm_monoid P] (f : add_submonoid.localization_map S N) {T : add_submonoid P}
    {Q : Type u_4} [add_comm_monoid Q] {k : add_submonoid.localization_map T Q} {j : M ≃+ P}
    (H : add_submonoid.map (add_equiv.to_add_monoid_hom j) S = T) (x : M) (y : ↥S) :
    coe_fn (add_submonoid.localization_map.add_equiv_of_add_equiv f k H)
          (add_submonoid.localization_map.mk' f x y) =
        add_submonoid.localization_map.mk' k (coe_fn j x)
          { val := coe_fn j ↑y, property := H ▸ set.mem_image_of_mem (⇑j) (subtype.property y) } :=
  add_submonoid.localization_map.map_mk' f
    (fun (y : ↥S) => H ▸ set.mem_image_of_mem (⇑j) (subtype.property y)) x y

@[simp] theorem Mathlib.add_submonoid.localization_map.of_add_equiv_of_add_equiv_apply
    {M : Type u_1} [add_comm_monoid M] {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N]
    {P : Type u_3} [add_comm_monoid P] (f : add_submonoid.localization_map S N)
    {T : add_submonoid P} {Q : Type u_4} [add_comm_monoid Q]
    {k : add_submonoid.localization_map T Q} {j : M ≃+ P}
    (H : add_submonoid.map (add_equiv.to_add_monoid_hom j) S = T) (x : M) :
    coe_fn
          (add_submonoid.localization_map.to_map
            (add_submonoid.localization_map.of_add_equiv_of_localizations f
              (add_submonoid.localization_map.add_equiv_of_add_equiv f k H)))
          x =
        coe_fn (add_submonoid.localization_map.to_map k) (coe_fn j x) :=
  sorry

theorem Mathlib.add_submonoid.localization_map.of_add_equiv_of_add_equiv {M : Type u_1}
    [add_comm_monoid M] {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N] {P : Type u_3}
    [add_comm_monoid P] (f : add_submonoid.localization_map S N) {T : add_submonoid P}
    {Q : Type u_4} [add_comm_monoid Q] {k : add_submonoid.localization_map T Q} {j : M ≃+ P}
    (H : add_submonoid.map (add_equiv.to_add_monoid_hom j) S = T) :
    add_submonoid.localization_map.to_map
          (add_submonoid.localization_map.of_add_equiv_of_localizations f
            (add_submonoid.localization_map.add_equiv_of_add_equiv f k H)) =
        add_monoid_hom.comp (add_submonoid.localization_map.to_map k)
          (add_equiv.to_add_monoid_hom j) :=
  add_monoid_hom.ext (add_submonoid.localization_map.of_add_equiv_of_add_equiv_apply f H)

end localization_map

end submonoid

namespace localization

/-- Natural hom sending `x : M`, `M` a `comm_monoid`, to the equivalence class of
`(x, 1)` in the localization of `M` at a submonoid. -/
def monoid_of {M : Type u_1} [comm_monoid M] (S : submonoid M) :
    submonoid.localization_map S (localization S) :=
  submonoid.localization_map.mk
    (monoid_hom.to_fun (monoid_hom.comp (con.mk' (r S)) (monoid_hom.inl M ↥S))) sorry sorry sorry
    sorry sorry

theorem Mathlib.add_localization.mk_zero_eq_add_monoid_of_mk {M : Type u_1} [add_comm_monoid M]
    {S : add_submonoid M} (x : M) :
    add_localization.mk x 0 =
        coe_fn (add_submonoid.localization_map.to_map (add_localization.add_monoid_of S)) x :=
  rfl

theorem Mathlib.add_localization.mk_eq_add_monoid_of_mk'_apply {M : Type u_1} [add_comm_monoid M]
    {S : add_submonoid M} (x : M) (y : ↥S) :
    add_localization.mk x y =
        add_submonoid.localization_map.mk' (add_localization.add_monoid_of S) x y :=
  sorry

@[simp] theorem Mathlib.add_localization.mk_eq_add_monoid_of_mk' {M : Type u_1} [add_comm_monoid M]
    {S : add_submonoid M} :
    add_localization.mk = add_submonoid.localization_map.mk' (add_localization.add_monoid_of S) :=
  funext
    fun (_x : M) => funext fun (_x_1 : ↥S) => add_localization.mk_eq_add_monoid_of_mk'_apply _x _x_1

/-- Given a localization map `f : M →* N` for a submonoid `S`, we get an isomorphism between
the localization of `M` at `S` as a quotient type and `N`. -/
def mul_equiv_of_quotient {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2}
    [comm_monoid N] (f : submonoid.localization_map S N) : localization S ≃* N :=
  submonoid.localization_map.mul_equiv_of_localizations (monoid_of S) f

@[simp] theorem mul_equiv_of_quotient_apply {M : Type u_1} [comm_monoid M] {S : submonoid M}
    {N : Type u_2} [comm_monoid N] {f : submonoid.localization_map S N} (x : localization S) :
    coe_fn (mul_equiv_of_quotient f) x =
        coe_fn
          (submonoid.localization_map.lift (monoid_of S) (submonoid.localization_map.map_units f))
          x :=
  rfl

@[simp] theorem mul_equiv_of_quotient_mk' {M : Type u_1} [comm_monoid M] {S : submonoid M}
    {N : Type u_2} [comm_monoid N] {f : submonoid.localization_map S N} (x : M) (y : ↥S) :
    coe_fn (mul_equiv_of_quotient f) (submonoid.localization_map.mk' (monoid_of S) x y) =
        submonoid.localization_map.mk' f x y :=
  submonoid.localization_map.lift_mk' (monoid_of S) (submonoid.localization_map.map_units f) x y

theorem mul_equiv_of_quotient_mk {M : Type u_1} [comm_monoid M] {S : submonoid M} {N : Type u_2}
    [comm_monoid N] {f : submonoid.localization_map S N} (x : M) (y : ↥S) :
    coe_fn (mul_equiv_of_quotient f) (mk x y) = submonoid.localization_map.mk' f x y :=
  sorry

@[simp] theorem Mathlib.add_localization.add_equiv_of_quotient_add_monoid_of {M : Type u_1}
    [add_comm_monoid M] {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N]
    {f : add_submonoid.localization_map S N} (x : M) :
    coe_fn (add_localization.add_equiv_of_quotient f)
          (coe_fn (add_submonoid.localization_map.to_map (add_localization.add_monoid_of S)) x) =
        coe_fn (add_submonoid.localization_map.to_map f) x :=
  add_submonoid.localization_map.lift_eq (add_localization.add_monoid_of S)
    (add_submonoid.localization_map.map_units f) x

@[simp] theorem Mathlib.add_localization.add_equiv_of_quotient_symm_mk' {M : Type u_1}
    [add_comm_monoid M] {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N]
    {f : add_submonoid.localization_map S N} (x : M) (y : ↥S) :
    coe_fn (add_equiv.symm (add_localization.add_equiv_of_quotient f))
          (add_submonoid.localization_map.mk' f x y) =
        add_submonoid.localization_map.mk' (add_localization.add_monoid_of S) x y :=
  add_submonoid.localization_map.lift_mk' f
    (add_submonoid.localization_map.map_units (add_localization.add_monoid_of S)) x y

theorem Mathlib.add_localization.add_equiv_of_quotient_symm_mk {M : Type u_1} [add_comm_monoid M]
    {S : add_submonoid M} {N : Type u_2} [add_comm_monoid N]
    {f : add_submonoid.localization_map S N} (x : M) (y : ↥S) :
    coe_fn (add_equiv.symm (add_localization.add_equiv_of_quotient f))
          (add_submonoid.localization_map.mk' f x y) =
        add_localization.mk x y :=
  sorry

@[simp] theorem mul_equiv_of_quotient_symm_monoid_of {M : Type u_1} [comm_monoid M]
    {S : submonoid M} {N : Type u_2} [comm_monoid N] {f : submonoid.localization_map S N} (x : M) :
    coe_fn (mul_equiv.symm (mul_equiv_of_quotient f))
          (coe_fn (submonoid.localization_map.to_map f) x) =
        coe_fn (submonoid.localization_map.to_map (monoid_of S)) x :=
  submonoid.localization_map.lift_eq f (submonoid.localization_map.map_units (monoid_of S)) x

/-- Given `x : M`, the localization of `M` at the submonoid generated by `x`, as a quotient. -/
def Mathlib.add_localization.away {M : Type u_1} [add_comm_monoid M] (x : M) :=
  add_localization (add_submonoid.multiples x)

/-- Given `x : M`, `inv_self` is `x⁻¹` in the localization (as a quotient type) of `M` at the
submonoid generated by `x`. -/
def away.inv_self {M : Type u_1} [comm_monoid M] (x : M) : away x :=
  mk 1 { val := x, property := sorry }

/-- Given `x : M`, the natural hom sending `y : M`, `M` a `comm_monoid`, to the equivalence class
of `(y, 1)` in the localization of `M` at the submonoid generated by `x`. -/
def away.monoid_of {M : Type u_1} [comm_monoid M] (x : M) :
    submonoid.localization_map.away_map x (away x) :=
  monoid_of (submonoid.powers x)

@[simp] theorem Mathlib.add_localization.away.mk_eq_add_monoid_of_mk' {M : Type u_1}
    [add_comm_monoid M] (x : M) :
    add_localization.mk =
        add_submonoid.localization_map.mk' (add_localization.away.add_monoid_of x) :=
  add_localization.mk_eq_add_monoid_of_mk'

/-- Given `x : M` and a localization map `f : M →* N` away from `x`, we get an isomorphism between
the localization of `M` at the submonoid generated by `x` as a quotient type and `N`. -/
def away.mul_equiv_of_quotient {M : Type u_1} [comm_monoid M] {N : Type u_2} [comm_monoid N] (x : M)
    (f : submonoid.localization_map.away_map x N) : away x ≃* N :=
  mul_equiv_of_quotient f

end Mathlib


/-
Copyright (c) 2020 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
import topology.sheaves.sheaf_condition.sites
import category_theory.sites.limits
import category_theory.adjunction
import category_theory.limits.functor_category

/-!
# Presheaves in `C` have limits and colimits when `C` does.
-/

noncomputable theory

universes v u

open category_theory
open category_theory.limits

variables {C : Type u} [category.{v} C] {J : Type v} [small_category J]

namespace Top

instance [has_limits C] (X : Top) : has_limits (presheaf C X) :=
limits.functor_category_has_limits_of_size

instance [has_colimits C] (X : Top) : has_colimits (presheaf C X) :=
limits.functor_category_has_colimits_of_size

instance [has_limits C] (X : Top) : creates_limits (sheaf.forget C X) :=
(@@creates_limits_of_nat_iso _ _
  (presheaf.Sheaf_spaces_equiv_sheaf_sites_inverse_forget C X))
  (@@category_theory.comp_creates_limits _ _ _ _ _ _
    Sheaf.category_theory.Sheaf_to_presheaf.category_theory.creates_limits.{u v v})

instance [has_limits C] (X : Top) : has_limits (sheaf C X) :=
has_limits_of_has_limits_creates_limits (sheaf.forget C X)

lemma is_sheaf_of_is_limit [has_limits C] {X : Top} (F : J ⥤ presheaf C X)
  (H : ∀ j, (F.obj j).is_sheaf) {c : cone F} (hc : is_limit c) : c.X.is_sheaf :=
begin
  let F' : J ⥤ sheaf C X := { obj := λ j, ⟨F.obj j, H j⟩, map := F.map },
  let e : F' ⋙ sheaf.forget C X ≅ F := nat_iso.of_components (λ _, iso.refl _) (by tidy),
  exact presheaf.is_sheaf_of_iso ((is_limit_of_preserves (sheaf.forget C X)
      (limit.is_limit F')).cone_points_iso_of_nat_iso hc e) (limit F').2
end

lemma limit_is_sheaf [has_limits C] {X : Top} (F : J ⥤ presheaf C X)
  (H : ∀ j, (F.obj j).is_sheaf) : (limit F).is_sheaf :=
is_sheaf_of_is_limit F H (limit.is_limit F)

end Top