/-
Copyright (c) 2018 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro
-/
import algebra.group_power.lemmas
import algebra.order.absolute_value
import algebra.order.group.min_max
import algebra.order.field.basic
import algebra.ring.pi
import group_theory.group_action.pi

/-!
# Cauchy sequences

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

A basic theory of Cauchy sequences, used in the construction of the reals and p-adic numbers. Where
applicable, lemmas that will be reused in other contexts have been stated in extra generality.

There are other "versions" of Cauchyness in the library, in particular Cauchy filters in topology.
This is a concrete implementation that is useful for simplicity and computability reasons.

## Important definitions

* `is_cau_seq`: a predicate that says `f : ℕ → β` is Cauchy.
* `cau_seq`: the type of Cauchy sequences valued in type `β` with respect to an absolute value
  function `abv`.

## Tags

sequence, cauchy, abs val, absolute value
-/

open is_absolute_value

variables {G α β : Type*}

theorem exists_forall_ge_and {α} [linear_order α] {P Q : α → Prop} :
  (∃ i, ∀ j ≥ i, P j) → (∃ i, ∀ j ≥ i, Q j) →
  ∃ i, ∀ j ≥ i, P j ∧ Q j
| ⟨a, h₁⟩ ⟨b, h₂⟩ := let ⟨c, ac, bc⟩ := exists_ge_of_linear a b in
  ⟨c, λ j hj, ⟨h₁ _ (le_trans ac hj), h₂ _ (le_trans bc hj)⟩⟩

section
variables [linear_ordered_field α] [ring β] (abv : β → α) [is_absolute_value abv]

theorem rat_add_continuous_lemma
  {ε : α} (ε0 : 0 < ε) : ∃ δ > 0, ∀ {a₁ a₂ b₁ b₂ : β},
  abv (a₁ - b₁) < δ → abv (a₂ - b₂) < δ → abv (a₁ + a₂ - (b₁ + b₂)) < ε :=
⟨ε / 2, half_pos ε0, λ a₁ a₂ b₁ b₂ h₁ h₂,
  by simpa [add_halves, sub_eq_add_neg, add_comm, add_left_comm, add_assoc]
    using lt_of_le_of_lt (abv_add abv _ _) (add_lt_add h₁ h₂)⟩

theorem rat_mul_continuous_lemma
  {ε K₁ K₂ : α} (ε0 : 0 < ε) :
  ∃ δ > 0, ∀ {a₁ a₂ b₁ b₂ : β}, abv a₁ < K₁ → abv b₂ < K₂ →
  abv (a₁ - b₁) < δ → abv (a₂ - b₂) < δ → abv (a₁ * a₂ - b₁ * b₂) < ε :=
begin
  have K0 : (0 : α) < max 1 (max K₁ K₂) := lt_of_lt_of_le zero_lt_one (le_max_left _ _),
  have εK := div_pos (half_pos ε0) K0,
  refine ⟨_, εK, λ a₁ a₂ b₁ b₂ ha₁ hb₂ h₁ h₂, _⟩,
  replace ha₁ := lt_of_lt_of_le ha₁ (le_trans (le_max_left _ K₂) (le_max_right 1 _)),
  replace hb₂ := lt_of_lt_of_le hb₂ (le_trans (le_max_right K₁ _) (le_max_right 1 _)),
  have := add_lt_add
    (mul_lt_mul' (le_of_lt h₁) hb₂ (abv_nonneg abv _) εK)
    (mul_lt_mul' (le_of_lt h₂) ha₁ (abv_nonneg abv _) εK),
  rw [← abv_mul abv, mul_comm, div_mul_cancel _ (ne_of_gt K0), ← abv_mul abv, add_halves] at this,
  simpa [mul_add, add_mul, sub_eq_add_neg, add_comm, add_left_comm]
    using lt_of_le_of_lt (abv_add abv _ _) this
end

theorem rat_inv_continuous_lemma
  {β : Type*} [division_ring β] (abv : β → α) [is_absolute_value abv]
  {ε K : α} (ε0 : 0 < ε) (K0 : 0 < K) :
  ∃ δ > 0, ∀ {a b : β}, K ≤ abv a → K ≤ abv b →
  abv (a - b) < δ → abv (a⁻¹ - b⁻¹) < ε :=
begin
  refine ⟨K * ε * K, mul_pos (mul_pos K0 ε0) K0, λ a b ha hb h, _⟩,
  have a0 := K0.trans_le ha,
  have b0 := K0.trans_le hb,
  rw [inv_sub_inv' ((abv_pos abv).1 a0) ((abv_pos abv).1 b0), abv_mul abv, abv_mul abv,
    abv_inv abv, abv_inv abv,  abv_sub abv],
  refine lt_of_mul_lt_mul_left (lt_of_mul_lt_mul_right _ b0.le) a0.le,
  rw [mul_assoc, inv_mul_cancel_right₀ b0.ne', ←mul_assoc, mul_inv_cancel a0.ne', one_mul],
  refine h.trans_le _,
  exact mul_le_mul (mul_le_mul ha le_rfl ε0.le a0.le) hb K0.le (mul_nonneg a0.le ε0.le),
end
end

/-- A sequence is Cauchy if the distance between its entries tends to zero. -/
def is_cau_seq {α : Type*} [linear_ordered_field α]
  {β : Type*} [ring β] (abv : β → α) (f : ℕ → β) : Prop :=
∀ ε > 0, ∃ i, ∀ j ≥ i, abv (f j - f i) < ε

namespace is_cau_seq
variables [linear_ordered_field α] [ring β] {abv : β → α} [is_absolute_value abv] {f g : ℕ → β}

@[nolint ge_or_gt] -- see Note [nolint_ge]
theorem cauchy₂ (hf : is_cau_seq abv f) {ε : α} (ε0 : 0 < ε) :
  ∃ i, ∀ j k ≥ i, abv (f j - f k) < ε :=
begin
  refine (hf _ (half_pos ε0)).imp (λ i hi j ij k ik, _),
  rw ← add_halves ε,
  refine lt_of_le_of_lt (abv_sub_le abv _ _ _) (add_lt_add (hi _ ij) _),
  rw abv_sub abv, exact hi _ ik
end

theorem cauchy₃ (hf : is_cau_seq abv f) {ε : α} (ε0 : 0 < ε) :
  ∃ i, ∀ j ≥ i, ∀ k ≥ j, abv (f k - f j) < ε :=
let ⟨i, H⟩ := hf.cauchy₂ ε0 in ⟨i, λ j ij k jk, H _ (le_trans ij jk) _ ij⟩

lemma add (hf : is_cau_seq abv f) (hg : is_cau_seq abv g) : is_cau_seq abv (f + g) :=
λ ε ε0,
  let ⟨δ, δ0, Hδ⟩ := rat_add_continuous_lemma abv ε0,
      ⟨i, H⟩ := exists_forall_ge_and (hf.cauchy₃ δ0) (hg.cauchy₃ δ0) in
  ⟨i, λ j ij, let ⟨H₁, H₂⟩ := H _ le_rfl in Hδ (H₁ _ ij) (H₂ _ ij)⟩

end is_cau_seq

/-- `cau_seq β abv` is the type of `β`-valued Cauchy sequences, with respect to the absolute value
function `abv`. -/
def cau_seq {α : Type*} [linear_ordered_field α]
  (β : Type*) [ring β] (abv : β → α) : Type* :=
{f : ℕ → β // is_cau_seq abv f}

namespace cau_seq
variables [linear_ordered_field α]

section ring
variables [ring β] {abv : β → α}

instance : has_coe_to_fun (cau_seq β abv) (λ _, ℕ → β) := ⟨subtype.val⟩

@[simp] theorem mk_to_fun (f) (hf : is_cau_seq abv f) :
  @coe_fn (cau_seq β abv) _ _ ⟨f, hf⟩ = f := rfl

theorem ext {f g : cau_seq β abv} (h : ∀ i, f i = g i) : f = g :=
subtype.eq (funext h)

theorem is_cau (f : cau_seq β abv) : is_cau_seq abv f := f.2

theorem cauchy (f : cau_seq β abv) :
  ∀ {ε}, 0 < ε → ∃ i, ∀ j ≥ i, abv (f j - f i) < ε := f.2

/-- Given a Cauchy sequence `f`, create a Cauchy sequence from a sequence `g` with
the same values as `f`. -/
def of_eq (f : cau_seq β abv) (g : ℕ → β) (e : ∀ i, f i = g i) : cau_seq β abv :=
⟨g, λ ε, by rw [show g = f, from (funext e).symm]; exact f.cauchy⟩

variable [is_absolute_value abv]

@[nolint ge_or_gt] -- see Note [nolint_ge]
theorem cauchy₂ (f : cau_seq β abv) {ε} : 0 < ε →
  ∃ i, ∀ j k ≥ i, abv (f j - f k) < ε := f.2.cauchy₂

theorem cauchy₃ (f : cau_seq β abv) {ε} : 0 < ε →
  ∃ i, ∀ j ≥ i, ∀ k ≥ j, abv (f k - f j) < ε := f.2.cauchy₃

theorem bounded (f : cau_seq β abv) : ∃ r, ∀ i, abv (f i) < r :=
begin
  cases f.cauchy zero_lt_one with i h,
  set R : ℕ → α := @nat.rec (λ n, α) (abv (f 0)) (λ i c, max c (abv (f i.succ))) with hR,
  have : ∀ i, ∀ j ≤ i, abv (f j) ≤ R i,
  { refine nat.rec (by simp [hR]) _,
    rintros i hi j (rfl | hj),
    { simp },
    exact (hi j hj).trans (le_max_left _ _) },
  refine ⟨R i + 1, λ j, _⟩,
  cases lt_or_le j i with ij ij,
  { exact lt_of_le_of_lt (this i _ (le_of_lt ij)) (lt_add_one _) },
  { have := lt_of_le_of_lt (abv_add abv _ _)
      (add_lt_add_of_le_of_lt (this i _ le_rfl) (h _ ij)),
    rw [add_sub, add_comm] at this, simpa }
end

theorem bounded' (f : cau_seq β abv) (x : α) : ∃ r > x, ∀ i, abv (f i) < r :=
let ⟨r, h⟩ := f.bounded in
⟨max r (x+1), lt_of_lt_of_le (lt_add_one _) (le_max_right _ _),
  λ i, lt_of_lt_of_le (h i) (le_max_left _ _)⟩

instance : has_add (cau_seq β abv) := ⟨λ f g, ⟨f + g, f.2.add g.2⟩⟩

@[simp, norm_cast] lemma coe_add (f g : cau_seq β abv) : ⇑(f + g) = f + g := rfl
@[simp, norm_cast] theorem add_apply (f g : cau_seq β abv) (i : ℕ) : (f + g) i = f i + g i := rfl

variable (abv)

/-- The constant Cauchy sequence. -/
def const (x : β) : cau_seq β abv :=
⟨λ i, x, λ ε ε0, ⟨0, λ j ij, by simpa [abv_zero abv] using ε0⟩⟩

variable {abv}

local notation `const` := const abv

@[simp, norm_cast] lemma coe_const (x : β) : ⇑(const x) = function.const _ x := rfl
@[simp, norm_cast] theorem const_apply (x : β) (i : ℕ) : (const x : ℕ → β) i = x := rfl

theorem const_inj {x y : β} : (const x : cau_seq β abv) = const y ↔ x = y :=
⟨λ h, congr_arg (λ f:cau_seq β abv, (f:ℕ→β) 0) h, congr_arg _⟩

instance : has_zero (cau_seq β abv) := ⟨const 0⟩
instance : has_one (cau_seq β abv) := ⟨const 1⟩
instance : inhabited (cau_seq β abv) := ⟨0⟩

@[simp, norm_cast] lemma coe_zero : ⇑(0 : cau_seq β abv) = 0 := rfl
@[simp, norm_cast] lemma coe_one : ⇑(1 : cau_seq β abv) = 1 := rfl
@[simp, norm_cast] lemma zero_apply (i) : (0 : cau_seq β abv) i = 0 := rfl
@[simp, norm_cast] lemma one_apply (i) : (1 : cau_seq β abv) i = 1 := rfl
@[simp] lemma const_zero : const 0 = 0 := rfl
@[simp] lemma const_one : const 1 = 1 := rfl

theorem const_add (x y : β) : const (x + y) = const x + const y :=
rfl

instance : has_mul (cau_seq β abv) :=
⟨λ f g, ⟨f * g, λ ε ε0,
  let ⟨F, F0, hF⟩ := f.bounded' 0, ⟨G, G0, hG⟩ := g.bounded' 0,
      ⟨δ, δ0, Hδ⟩ := rat_mul_continuous_lemma abv ε0,
      ⟨i, H⟩ := exists_forall_ge_and (f.cauchy₃ δ0) (g.cauchy₃ δ0) in
  ⟨i, λ j ij, let ⟨H₁, H₂⟩ := H _ le_rfl in
    Hδ (hF j) (hG i) (H₁ _ ij) (H₂ _ ij)⟩⟩⟩

@[simp, norm_cast] lemma coe_mul (f g : cau_seq β abv) : ⇑(f * g) = f * g := rfl
@[simp, norm_cast] theorem mul_apply (f g : cau_seq β abv) (i : ℕ) : (f * g) i = f i * g i := rfl

theorem const_mul (x y : β) : const (x * y) = const x * const y := rfl

instance : has_neg (cau_seq β abv) :=
⟨λ f, of_eq (const (-1) * f) (λ x, -f x) (λ i, by simp)⟩

@[simp, norm_cast] lemma coe_neg (f : cau_seq β abv) : ⇑(-f) = -f := rfl
@[simp, norm_cast] theorem neg_apply (f : cau_seq β abv) (i) : (-f) i = -f i := rfl

theorem const_neg (x : β) : const (-x) = -const x := rfl

instance : has_sub (cau_seq β abv) :=
⟨λ f g, of_eq (f + -g) (λ x, f x - g x) (λ i, by simp [sub_eq_add_neg])⟩

@[simp, norm_cast] lemma coe_sub (f g : cau_seq β abv) : ⇑(f - g) = f - g := rfl
@[simp, norm_cast] theorem sub_apply (f g : cau_seq β abv) (i : ℕ) : (f - g) i = f i - g i := rfl

theorem const_sub (x y : β) : const (x - y) = const x - const y := rfl

section has_smul
variables [has_smul G β] [is_scalar_tower G β β]

instance : has_smul G (cau_seq β abv) :=
⟨λ a f, of_eq (const (a • 1) * f) (a • f) $ λ i, smul_one_mul _ _⟩

@[simp, norm_cast] lemma coe_smul (a : G) (f : cau_seq β abv) : ⇑(a • f) = a • f := rfl
@[simp, norm_cast] lemma smul_apply (a : G) (f : cau_seq β abv) (i : ℕ) : (a • f) i = a • f i := rfl
lemma const_smul (a : G) (x : β) : const (a • x) = a • const x := rfl

instance : is_scalar_tower G (cau_seq β abv) (cau_seq β abv) :=
⟨λ a f g, subtype.ext $ smul_assoc a ⇑f ⇑g⟩

end has_smul

instance : add_group (cau_seq β abv) :=
function.injective.add_group _ subtype.coe_injective
  rfl coe_add coe_neg coe_sub (λ _ _, coe_smul _ _) (λ _ _, coe_smul _ _)

instance : add_group_with_one (cau_seq β abv) :=
{ one := 1,
  nat_cast := λ n, const n,
  nat_cast_zero := congr_arg const nat.cast_zero,
  nat_cast_succ := λ n, congr_arg const (nat.cast_succ n),
  int_cast := λ n, const n,
  int_cast_of_nat := λ n, congr_arg const (int.cast_of_nat n),
  int_cast_neg_succ_of_nat := λ n, congr_arg const (int.cast_neg_succ_of_nat n),
  .. cau_seq.add_group }

instance : has_pow (cau_seq β abv) ℕ :=
⟨λ f n, of_eq (npow_rec n f) (λ i, f i ^ n) $ by induction n; simp [*, npow_rec, pow_succ]⟩

@[simp, norm_cast] lemma coe_pow (f : cau_seq β abv) (n : ℕ) : ⇑(f ^ n) = f ^ n := rfl
@[simp, norm_cast] lemma pow_apply (f : cau_seq β abv) (n i : ℕ) : (f ^ n) i = f i ^ n := rfl
lemma const_pow (x : β) (n : ℕ) : const (x ^ n) = const x ^ n := rfl

instance : ring (cau_seq β abv) :=
function.injective.ring _ subtype.coe_injective
  rfl rfl coe_add coe_mul coe_neg coe_sub (λ _ _, coe_smul _ _) (λ _ _, coe_smul _ _) coe_pow
  (λ _, rfl) (λ _, rfl)

instance {β : Type*} [comm_ring β] {abv : β → α} [is_absolute_value abv] :
  comm_ring (cau_seq β abv) :=
{ mul_comm := by intros; apply ext; simp [mul_left_comm, mul_comm],
  ..cau_seq.ring }

/-- `lim_zero f` holds when `f` approaches 0. -/
def lim_zero {abv : β → α} (f : cau_seq β abv) : Prop := ∀ ε > 0, ∃ i, ∀ j ≥ i, abv (f j) < ε

theorem add_lim_zero {f g : cau_seq β abv}
  (hf : lim_zero f) (hg : lim_zero g) : lim_zero (f + g)
| ε ε0 := (exists_forall_ge_and
    (hf _ $ half_pos ε0) (hg _ $ half_pos ε0)).imp $
  λ i H j ij, let ⟨H₁, H₂⟩ := H _ ij in
    by simpa [add_halves ε] using lt_of_le_of_lt (abv_add abv _ _) (add_lt_add H₁ H₂)

theorem mul_lim_zero_right (f : cau_seq β abv) {g}
  (hg : lim_zero g) : lim_zero (f * g)
| ε ε0 := let ⟨F, F0, hF⟩ := f.bounded' 0 in
  (hg _ $ div_pos ε0 F0).imp $ λ i H j ij,
  by have := mul_lt_mul' (le_of_lt $ hF j) (H _ ij) (abv_nonneg abv _) F0;
     rwa [mul_comm F, div_mul_cancel _ (ne_of_gt F0), ← abv_mul abv] at this

theorem mul_lim_zero_left {f} (g : cau_seq β abv)
  (hg : lim_zero f) : lim_zero (f * g)
| ε ε0 := let ⟨G, G0, hG⟩ := g.bounded' 0 in
  (hg _ $ div_pos ε0 G0).imp $ λ i H j ij,
  by have := mul_lt_mul'' (H _ ij) (hG j) (abv_nonneg abv _) (abv_nonneg abv _);
     rwa [div_mul_cancel _ (ne_of_gt G0), ← abv_mul abv] at this

theorem neg_lim_zero {f : cau_seq β abv} (hf : lim_zero f) : lim_zero (-f) :=
by rw ← neg_one_mul; exact mul_lim_zero_right _ hf

theorem sub_lim_zero {f g : cau_seq β abv}
  (hf : lim_zero f) (hg : lim_zero g) : lim_zero (f - g) :=
by simpa only [sub_eq_add_neg] using add_lim_zero hf (neg_lim_zero hg)

theorem lim_zero_sub_rev {f g : cau_seq β abv} (hfg : lim_zero (f - g)) : lim_zero (g - f) :=
by simpa using neg_lim_zero hfg

theorem zero_lim_zero : lim_zero (0 : cau_seq β abv)
| ε ε0 := ⟨0, λ j ij, by simpa [abv_zero abv] using ε0⟩

theorem const_lim_zero {x : β} : lim_zero (const x) ↔ x = 0 :=
⟨λ H, (abv_eq_zero abv).1 $
  eq_of_le_of_forall_le_of_dense (abv_nonneg abv _) $
  λ ε ε0, let ⟨i, hi⟩ := H _ ε0 in le_of_lt $ hi _ le_rfl,
λ e, e.symm ▸ zero_lim_zero⟩

instance equiv : setoid (cau_seq β abv) :=
⟨λ f g, lim_zero (f - g),
⟨λ f, by simp [zero_lim_zero],
 λ f g h, by simpa using neg_lim_zero h,
 λ f g h fg gh, by simpa [sub_eq_add_neg, add_assoc] using add_lim_zero fg gh⟩⟩

lemma add_equiv_add {f1 f2 g1 g2 : cau_seq β abv} (hf : f1 ≈ f2) (hg : g1 ≈ g2) :
  f1 + g1 ≈ f2 + g2 :=
by simpa only [←add_sub_add_comm] using add_lim_zero hf hg

lemma neg_equiv_neg {f g : cau_seq β abv} (hf : f ≈ g) : -f ≈ -g :=
by simpa only [neg_sub'] using neg_lim_zero hf

lemma sub_equiv_sub {f1 f2 g1 g2 : cau_seq β abv} (hf : f1 ≈ f2) (hg : g1 ≈ g2) :
  f1 - g1 ≈ f2 - g2 :=
by simpa only [sub_eq_add_neg] using add_equiv_add hf (neg_equiv_neg hg)

theorem equiv_def₃ {f g : cau_seq β abv} (h : f ≈ g) {ε : α} (ε0 : 0 < ε) :
  ∃ i, ∀ j ≥ i, ∀ k ≥ j, abv (f k - g j) < ε :=
(exists_forall_ge_and (h _ $ half_pos ε0) (f.cauchy₃ $ half_pos ε0)).imp $
λ i H j ij k jk, let ⟨h₁, h₂⟩ := H _ ij in
by have := lt_of_le_of_lt (abv_add abv (f j - g j) _) (add_lt_add h₁ (h₂ _ jk));
   rwa [sub_add_sub_cancel', add_halves] at this

theorem lim_zero_congr {f g : cau_seq β abv} (h : f ≈ g) : lim_zero f ↔ lim_zero g :=
⟨λ l, by simpa using add_lim_zero (setoid.symm h) l,
 λ l, by simpa using add_lim_zero h l⟩

theorem abv_pos_of_not_lim_zero {f : cau_seq β abv} (hf : ¬ lim_zero f) :
  ∃ K > 0, ∃ i, ∀ j ≥ i, K ≤ abv (f j) :=
begin
  haveI := classical.prop_decidable,
  by_contra nk,
  refine hf (λ ε ε0, _),
  simp [not_forall] at nk,
  cases f.cauchy₃ (half_pos ε0) with i hi,
  rcases nk _ (half_pos ε0) i with ⟨j, ij, hj⟩,
  refine ⟨j, λ k jk, _⟩,
  have := lt_of_le_of_lt (abv_add abv _ _) (add_lt_add (hi j ij k jk) hj),
  rwa [sub_add_cancel, add_halves] at this
end

theorem of_near (f : ℕ → β) (g : cau_seq β abv)
  (h : ∀ ε > 0, ∃ i, ∀ j ≥ i, abv (f j - g j) < ε) : is_cau_seq abv f
| ε ε0 :=
  let ⟨i, hi⟩ := exists_forall_ge_and
    (h _ (half_pos $ half_pos ε0)) (g.cauchy₃ $ half_pos ε0) in
  ⟨i, λ j ij, begin
    cases hi _ le_rfl with h₁ h₂, rw abv_sub abv at h₁,
    have := lt_of_le_of_lt (abv_add abv _ _) (add_lt_add (hi _ ij).1 h₁),
    have := lt_of_le_of_lt (abv_add abv _ _) (add_lt_add this (h₂ _ ij)),
    rwa [add_halves, add_halves, add_right_comm,
         sub_add_sub_cancel, sub_add_sub_cancel] at this
  end⟩

lemma not_lim_zero_of_not_congr_zero {f : cau_seq _ abv} (hf : ¬ f ≈ 0) : ¬ lim_zero f :=
assume : lim_zero f,
have lim_zero (f - 0), by simpa,
hf this

lemma mul_equiv_zero  (g : cau_seq _ abv) {f : cau_seq _ abv} (hf : f ≈ 0) : g * f ≈ 0 :=
have lim_zero (f - 0), from hf,
have lim_zero (g*f), from mul_lim_zero_right _ $ by simpa,
show lim_zero (g*f - 0), by simpa

lemma mul_equiv_zero' (g : cau_seq _ abv) {f : cau_seq _ abv} (hf : f ≈ 0) : f * g ≈ 0 :=
have lim_zero (f - 0), from hf,
have lim_zero (f*g), from mul_lim_zero_left _ $ by simpa,
show lim_zero (f*g - 0), by simpa

lemma mul_not_equiv_zero {f g : cau_seq _ abv} (hf : ¬ f ≈ 0) (hg : ¬ g ≈ 0) : ¬ (f * g) ≈ 0 :=
assume : lim_zero (f*g - 0),
have hlz : lim_zero (f*g), by simpa,
have hf' : ¬ lim_zero f, by simpa using (show ¬ lim_zero (f - 0), from hf),
have hg' : ¬ lim_zero g, by simpa using (show ¬ lim_zero (g - 0), from hg),
begin
  rcases abv_pos_of_not_lim_zero hf' with ⟨a1, ha1, N1, hN1⟩,
  rcases abv_pos_of_not_lim_zero hg' with ⟨a2, ha2, N2, hN2⟩,
  have : 0 < a1 * a2, from mul_pos ha1 ha2,
  cases hlz _ this with N hN,
  let i := max N (max N1 N2),
  have hN' := hN i (le_max_left _ _),
  have hN1' := hN1 i (le_trans (le_max_left _ _) (le_max_right _ _)),
  have hN1' := hN2 i (le_trans (le_max_right _ _) (le_max_right _ _)),
  apply not_le_of_lt hN',
  change _ ≤ abv (_ * _),
  rw is_absolute_value.abv_mul abv,
  apply mul_le_mul; try { assumption },
  { apply le_of_lt ha2 },
  { apply is_absolute_value.abv_nonneg abv }
end

theorem const_equiv {x y : β} : const x ≈ const y ↔ x = y :=
show lim_zero _ ↔ _, by rw [← const_sub, const_lim_zero, sub_eq_zero]

lemma mul_equiv_mul {f1 f2 g1 g2 : cau_seq β abv} (hf : f1 ≈ f2) (hg : g1 ≈ g2) :
  f1 * g1 ≈ f2 * g2 :=
by simpa only [mul_sub, sub_mul, sub_add_sub_cancel]
  using add_lim_zero (mul_lim_zero_left g1 hf) (mul_lim_zero_right f2 hg)

lemma smul_equiv_smul [has_smul G β] [is_scalar_tower G β β] {f1 f2 : cau_seq β abv}
  (c : G) (hf : f1 ≈ f2) :
  c • f1 ≈ c • f2 :=
by simpa [const_smul, smul_one_mul _ _]
  using mul_equiv_mul (const_equiv.mpr $ eq.refl $ c • 1) hf

lemma pow_equiv_pow {f1 f2 : cau_seq β abv} (hf : f1 ≈ f2) (n : ℕ) :
  f1 ^ n ≈ f2 ^ n :=
begin
  induction n with n ih,
  { simp only [pow_zero, setoid.refl] },
  { simpa only [pow_succ] using mul_equiv_mul hf ih, },
end

end ring

section is_domain
variables [ring β] [is_domain β] (abv : β → α) [is_absolute_value abv]

lemma one_not_equiv_zero : ¬ (const abv 1) ≈ (const abv 0) :=
assume h,
have ∀ ε > 0, ∃ i, ∀ k, i ≤ k → abv (1 - 0) < ε, from h,
have h1 : abv 1 ≤ 0, from le_of_not_gt $
  assume h2 : 0 < abv 1,
  exists.elim (this _ h2) $ λ i hi,
    lt_irrefl (abv 1) $ by simpa using hi _ le_rfl,
have h2 : 0 ≤ abv 1, from is_absolute_value.abv_nonneg _ _,
have abv 1 = 0, from le_antisymm h1 h2,
have (1 : β) = 0, from (is_absolute_value.abv_eq_zero abv).1 this,
absurd this one_ne_zero

end is_domain

section division_ring
variables [division_ring β] {abv : β → α} [is_absolute_value abv]

theorem inv_aux {f : cau_seq β abv} (hf : ¬ lim_zero f) :
  ∀ ε > 0, ∃ i, ∀ j ≥ i, abv ((f j)⁻¹ - (f i)⁻¹) < ε | ε ε0 :=
let ⟨K, K0, HK⟩ := abv_pos_of_not_lim_zero hf,
    ⟨δ, δ0, Hδ⟩ := rat_inv_continuous_lemma abv ε0 K0,
    ⟨i, H⟩ := exists_forall_ge_and HK (f.cauchy₃ δ0) in
⟨i, λ j ij, let ⟨iK, H'⟩ := H _ le_rfl in Hδ (H _ ij).1 iK (H' _ ij)⟩

/-- Given a Cauchy sequence `f` with nonzero limit, create a Cauchy sequence with values equal to
the inverses of the values of `f`. -/
def inv (f : cau_seq β abv) (hf : ¬ lim_zero f) : cau_seq β abv := ⟨_, inv_aux hf⟩

@[simp, norm_cast] lemma coe_inv {f : cau_seq β abv} (hf) : ⇑(inv f hf) = f⁻¹ := rfl
@[simp, norm_cast] theorem inv_apply {f : cau_seq β abv} (hf i) : inv f hf i = (f i)⁻¹ := rfl

theorem inv_mul_cancel {f : cau_seq β abv} (hf) : inv f hf * f ≈ 1 :=
λ ε ε0, let ⟨K, K0, i, H⟩ := abv_pos_of_not_lim_zero hf in
⟨i, λ j ij,
  by simpa [(abv_pos abv).1 (lt_of_lt_of_le K0 (H _ ij)),
    abv_zero abv] using ε0⟩

theorem mul_inv_cancel {f : cau_seq β abv} (hf) : f * inv f hf ≈ 1 :=
λ ε ε0, let ⟨K, K0, i, H⟩ := abv_pos_of_not_lim_zero hf in
⟨i, λ j ij,
  by simpa [(abv_pos abv).1 (lt_of_lt_of_le K0 (H _ ij)),
    abv_zero abv] using ε0⟩

theorem const_inv {x : β} (hx : x ≠ 0) :
  const abv (x⁻¹) = inv (const abv x) (by rwa const_lim_zero) := rfl

end division_ring

section abs
local notation `const` := const abs

/-- The entries of a positive Cauchy sequence eventually have a positive lower bound. -/
def pos (f : cau_seq α abs) : Prop := ∃ K > 0, ∃ i, ∀ j ≥ i, K ≤ f j

theorem not_lim_zero_of_pos {f : cau_seq α abs} : pos f → ¬ lim_zero f
| ⟨F, F0, hF⟩ H :=
  let ⟨i, h⟩ := exists_forall_ge_and hF (H _ F0),
      ⟨h₁, h₂⟩ := h _ le_rfl in
  not_lt_of_le h₁ (abs_lt.1 h₂).2

theorem const_pos {x : α} : pos (const x) ↔ 0 < x :=
⟨λ ⟨K, K0, i, h⟩, lt_of_lt_of_le K0 (h _ le_rfl),
 λ h, ⟨x, h, 0, λ j _, le_rfl⟩⟩

theorem add_pos {f g : cau_seq α abs} : pos f → pos g → pos (f + g)
| ⟨F, F0, hF⟩ ⟨G, G0, hG⟩ :=
  let ⟨i, h⟩ := exists_forall_ge_and hF hG in
  ⟨_, _root_.add_pos F0 G0, i,
    λ j ij, let ⟨h₁, h₂⟩ := h _ ij in add_le_add h₁ h₂⟩

theorem pos_add_lim_zero {f g : cau_seq α abs} : pos f → lim_zero g → pos (f + g)
| ⟨F, F0, hF⟩ H :=
  let ⟨i, h⟩ := exists_forall_ge_and hF (H _ (half_pos F0)) in
  ⟨_, half_pos F0, i, λ j ij, begin
    cases h j ij with h₁ h₂,
    have := add_le_add h₁ (le_of_lt (abs_lt.1 h₂).1),
    rwa [← sub_eq_add_neg, sub_self_div_two] at this
  end⟩

protected theorem mul_pos {f g : cau_seq α abs} : pos f → pos g → pos (f * g)
| ⟨F, F0, hF⟩ ⟨G, G0, hG⟩ :=
  let ⟨i, h⟩ := exists_forall_ge_and hF hG in
  ⟨_, _root_.mul_pos F0 G0, i,
    λ j ij, let ⟨h₁, h₂⟩ := h _ ij in
    mul_le_mul h₁ h₂ (le_of_lt G0) (le_trans (le_of_lt F0) h₁)⟩

theorem trichotomy (f : cau_seq α abs) : pos f ∨ lim_zero f ∨ pos (-f) :=
begin
  cases classical.em (lim_zero f); simp *,
  rcases abv_pos_of_not_lim_zero h with ⟨K, K0, hK⟩,
  rcases exists_forall_ge_and hK (f.cauchy₃ K0) with ⟨i, hi⟩,
  refine (le_total 0 (f i)).imp _ _;
    refine (λ h, ⟨K, K0, i, λ j ij, _⟩);
    have := (hi _ ij).1;
    cases hi _ le_rfl with h₁ h₂,
  { rwa abs_of_nonneg at this,
    rw abs_of_nonneg h at h₁,
    exact (le_add_iff_nonneg_right _).1
      (le_trans h₁ $ neg_le_sub_iff_le_add'.1 $
        le_of_lt (abs_lt.1 $ h₂ _ ij).1) },
  { rwa abs_of_nonpos at this,
    rw abs_of_nonpos h at h₁,
    rw [← sub_le_sub_iff_right, zero_sub],
    exact le_trans (le_of_lt (abs_lt.1 $ h₂ _ ij).2) h₁ }
end

instance : has_lt (cau_seq α abs) := ⟨λ f g, pos (g - f)⟩
instance : has_le (cau_seq α abs) := ⟨λ f g, f < g ∨ f ≈ g⟩

theorem lt_of_lt_of_eq {f g h : cau_seq α abs}
  (fg : f < g) (gh : g ≈ h) : f < h :=
show pos (h - f),
by simpa [sub_eq_add_neg, add_comm, add_left_comm] using pos_add_lim_zero fg (neg_lim_zero gh)

theorem lt_of_eq_of_lt {f g h : cau_seq α abs}
  (fg : f ≈ g) (gh : g < h) : f < h :=
by have := pos_add_lim_zero gh (neg_lim_zero fg);
   rwa [← sub_eq_add_neg, sub_sub_sub_cancel_right] at this

theorem lt_trans {f g h : cau_seq α abs} (fg : f < g) (gh : g < h) : f < h :=
show pos (h - f),
by simpa [sub_eq_add_neg, add_comm, add_left_comm] using add_pos fg gh

theorem lt_irrefl {f : cau_seq α abs} : ¬ f < f
| h := not_lim_zero_of_pos h (by simp [zero_lim_zero])

lemma le_of_eq_of_le {f g h : cau_seq α abs}
  (hfg : f ≈ g) (hgh : g ≤ h) : f ≤ h :=
hgh.elim (or.inl ∘ cau_seq.lt_of_eq_of_lt hfg)
  (or.inr ∘ setoid.trans hfg)

lemma le_of_le_of_eq {f g h : cau_seq α abs}
  (hfg : f ≤ g) (hgh : g ≈ h) : f ≤ h :=
hfg.elim (λ h, or.inl (cau_seq.lt_of_lt_of_eq h hgh))
  (λ h, or.inr (setoid.trans h hgh))

instance : preorder (cau_seq α abs) :=
{ lt := (<),
  le := λ f g, f < g ∨ f ≈ g,
  le_refl := λ f, or.inr (setoid.refl _),
  le_trans := λ f g h fg, match fg with
    | or.inl fg, or.inl gh := or.inl $ lt_trans fg gh
    | or.inl fg, or.inr gh := or.inl $ lt_of_lt_of_eq fg gh
    | or.inr fg, or.inl gh := or.inl $ lt_of_eq_of_lt fg gh
    | or.inr fg, or.inr gh := or.inr $ setoid.trans fg gh
    end,
  lt_iff_le_not_le := λ f g,
    ⟨λ h, ⟨or.inl h,
      not_or (mt (lt_trans h) lt_irrefl) (not_lim_zero_of_pos h)⟩,
    λ ⟨h₁, h₂⟩, h₁.resolve_right
      (mt (λ h, or.inr (setoid.symm h)) h₂)⟩ }

theorem le_antisymm {f g : cau_seq α abs} (fg : f ≤ g) (gf : g ≤ f) : f ≈ g :=
fg.resolve_left (not_lt_of_le gf)

theorem lt_total (f g : cau_seq α abs) : f < g ∨ f ≈ g ∨ g < f :=
(trichotomy (g - f)).imp_right
  (λ h, h.imp (λ h, setoid.symm h) (λ h, by rwa neg_sub at h))

theorem le_total (f g : cau_seq α abs) : f ≤ g ∨ g ≤ f :=
(or.assoc.2 (lt_total f g)).imp_right or.inl

theorem const_lt {x y : α} : const x < const y ↔ x < y :=
show pos _ ↔ _, by rw [← const_sub, const_pos, sub_pos]

theorem const_le {x y : α} : const x ≤ const y ↔ x ≤ y :=
by rw le_iff_lt_or_eq; exact or_congr const_lt const_equiv

lemma le_of_exists {f g : cau_seq α abs}
  (h : ∃ i, ∀ j ≥ i, f j ≤ g j) : f ≤ g :=
let ⟨i, hi⟩ := h in
(or.assoc.2 (cau_seq.lt_total f g)).elim
  id
  (λ hgf, false.elim (let ⟨K, hK0, j, hKj⟩ := hgf in
    not_lt_of_ge (hi (max i j) (le_max_left _ _))
      (sub_pos.1 (lt_of_lt_of_le hK0 (hKj _ (le_max_right _ _))))))

theorem exists_gt (f : cau_seq α abs) : ∃ a : α, f < const a :=
let ⟨K, H⟩ := f.bounded in
⟨K + 1, 1, zero_lt_one, 0, λ i _, begin
  rw [sub_apply, const_apply, le_sub_iff_add_le', add_le_add_iff_right],
  exact le_of_lt (abs_lt.1 (H _)).2
end⟩

theorem exists_lt (f : cau_seq α abs) : ∃ a : α, const a < f :=
let ⟨a, h⟩ := (-f).exists_gt in ⟨-a, show pos _,
  by rwa [const_neg, sub_neg_eq_add, add_comm, ← sub_neg_eq_add]⟩

-- so named to match `rat_add_continuous_lemma`
theorem _root_.rat_sup_continuous_lemma {ε : α} {a₁ a₂ b₁ b₂ : α} :
  abs (a₁ - b₁) < ε → abs (a₂ - b₂) < ε → abs (a₁ ⊔ a₂ - (b₁ ⊔ b₂)) < ε :=
λ h₁ h₂, (abs_max_sub_max_le_max _ _ _ _).trans_lt (max_lt h₁ h₂)

-- so named to match `rat_add_continuous_lemma`
theorem _root_.rat_inf_continuous_lemma {ε : α} {a₁ a₂ b₁ b₂ : α} :
  abs (a₁ - b₁) < ε → abs (a₂ - b₂) < ε → abs (a₁ ⊓ a₂ - (b₁ ⊓ b₂)) < ε :=
λ h₁ h₂, (abs_min_sub_min_le_max _ _ _ _).trans_lt (max_lt h₁ h₂)

instance : has_sup (cau_seq α abs) :=
⟨λ f g, ⟨f ⊔ g, λ ε ε0,
  (exists_forall_ge_and (f.cauchy₃ ε0) (g.cauchy₃ ε0)).imp $ λ i H j ij,
    let ⟨H₁, H₂⟩ := H _ le_rfl in rat_sup_continuous_lemma (H₁ _ ij) (H₂ _ ij)⟩⟩

instance : has_inf (cau_seq α abs) :=
⟨λ f g, ⟨f ⊓ g, λ ε ε0,
  (exists_forall_ge_and (f.cauchy₃ ε0) (g.cauchy₃ ε0)).imp $ λ i H j ij,
    let ⟨H₁, H₂⟩ := H _ le_rfl in rat_inf_continuous_lemma (H₁ _ ij) (H₂ _ ij)⟩⟩

@[simp, norm_cast]

@[simp, norm_cast] lemma coe_inf (f g : cau_seq α abs) : ⇑(f ⊓ g) = f ⊓ g := rfl

theorem sup_lim_zero {f g : cau_seq α abs}
  (hf : lim_zero f) (hg : lim_zero g) : lim_zero (f ⊔ g)
| ε ε0 := (exists_forall_ge_and (hf _ ε0) (hg _ ε0)).imp $
  λ i H j ij, let ⟨H₁, H₂⟩ := H _ ij in begin
    rw abs_lt at H₁ H₂ ⊢,
    exact ⟨lt_sup_iff.mpr (or.inl H₁.1), sup_lt_iff.mpr ⟨H₁.2, H₂.2⟩⟩
  end

theorem inf_lim_zero {f g : cau_seq α abs}
  (hf : lim_zero f) (hg : lim_zero g) : lim_zero (f ⊓ g)
| ε ε0 := (exists_forall_ge_and (hf _ ε0) (hg _ ε0)).imp $
  λ i H j ij, let ⟨H₁, H₂⟩ := H _ ij in begin
    rw abs_lt at H₁ H₂ ⊢,
    exact ⟨lt_inf_iff.mpr ⟨H₁.1, H₂.1⟩, inf_lt_iff.mpr (or.inl H₁.2), ⟩
  end

lemma sup_equiv_sup {a₁ b₁ a₂ b₂ : cau_seq α abs} (ha : a₁ ≈ a₂) (hb : b₁ ≈ b₂) :
  a₁ ⊔ b₁ ≈ a₂ ⊔ b₂ :=
begin
  intros ε ε0,
  obtain ⟨ai, hai⟩ := ha ε ε0,
  obtain ⟨bi, hbi⟩ := hb ε ε0,
  exact ⟨ai ⊔ bi, λ i hi,
    (abs_max_sub_max_le_max (a₁ i) (b₁ i) (a₂ i) (b₂ i)).trans_lt
    (max_lt (hai i (sup_le_iff.mp hi).1) (hbi i (sup_le_iff.mp hi).2))⟩,
end

lemma inf_equiv_inf {a₁ b₁ a₂ b₂ : cau_seq α abs} (ha : a₁ ≈ a₂) (hb : b₁ ≈ b₂) :
  a₁ ⊓ b₁ ≈ a₂ ⊓ b₂ :=
begin
  intros ε ε0,
  obtain ⟨ai, hai⟩ := ha ε ε0,
  obtain ⟨bi, hbi⟩ := hb ε ε0,
  exact ⟨ai ⊔ bi, λ i hi,
    (abs_min_sub_min_le_max (a₁ i) (b₁ i) (a₂ i) (b₂ i)).trans_lt
    (max_lt (hai i (sup_le_iff.mp hi).1) (hbi i (sup_le_iff.mp hi).2))⟩,
end

protected lemma sup_lt {a b c : cau_seq α abs} (ha : a < c) (hb : b < c) : a ⊔ b < c :=
begin
  obtain ⟨⟨εa, εa0, ia, ha⟩, ⟨εb, εb0, ib, hb⟩⟩ := ⟨ha, hb⟩,
  refine ⟨εa ⊓ εb, lt_inf_iff.mpr ⟨εa0, εb0⟩, ia ⊔ ib, λ i hi, _⟩,
  have := min_le_min (ha _ (sup_le_iff.mp hi).1) (hb _ (sup_le_iff.mp hi).2),
  exact this.trans_eq (min_sub_sub_left _ _ _)
end

protected lemma lt_inf {a b c : cau_seq α abs} (hb : a < b) (hc : a < c) : a < b ⊓ c :=
begin
  obtain ⟨⟨εb, εb0, ib, hb⟩, ⟨εc, εc0, ic, hc⟩⟩ := ⟨hb, hc⟩,
  refine ⟨εb ⊓ εc, lt_inf_iff.mpr ⟨εb0, εc0⟩, ib ⊔ ic, λ i hi, _⟩,
  have := min_le_min (hb _ (sup_le_iff.mp hi).1) (hc _ (sup_le_iff.mp hi).2),
  exact this.trans_eq (min_sub_sub_right _ _ _),
end

@[simp] protected lemma sup_idem (a : cau_seq α abs) : a ⊔ a = a := subtype.ext sup_idem

@[simp] protected lemma inf_idem (a : cau_seq α abs) : a ⊓ a = a := subtype.ext inf_idem

protected lemma sup_comm (a b : cau_seq α abs) : a ⊔ b = b ⊔ a := subtype.ext sup_comm

protected lemma inf_comm (a b : cau_seq α abs) : a ⊓ b = b ⊓ a := subtype.ext inf_comm

protected lemma sup_eq_right {a b : cau_seq α abs} (h : a ≤ b) :
  a ⊔ b ≈ b :=
begin
  obtain ⟨ε, ε0 : _ < _, i, h⟩ | h := h,
  { intros _ _,
    refine ⟨i, λ j hj, _⟩,
    dsimp,
    erw ←max_sub_sub_right,
    rwa [sub_self, max_eq_right, abs_zero],
    rw [sub_nonpos, ←sub_nonneg],
    exact ε0.le.trans (h _ hj) },
  { refine setoid.trans (sup_equiv_sup h (setoid.refl _)) _,
    rw cau_seq.sup_idem,
    exact setoid.refl _ },
end

protected lemma inf_eq_right {a b : cau_seq α abs} (h : b ≤ a) :
  a ⊓ b ≈ b :=
begin
  obtain ⟨ε, ε0 : _ < _, i, h⟩ | h := h,
  { intros _ _,
    refine ⟨i, λ j hj, _⟩,
    dsimp,
    erw ←min_sub_sub_right,
    rwa [sub_self, min_eq_right, abs_zero],
    exact ε0.le.trans (h _ hj) },
  { refine setoid.trans (inf_equiv_inf (setoid.symm h) (setoid.refl _)) _,
    rw cau_seq.inf_idem,
    exact setoid.refl _ },
end

protected lemma sup_eq_left {a b : cau_seq α abs} (h : b ≤ a) :
  a ⊔ b ≈ a :=
by simpa only [cau_seq.sup_comm] using cau_seq.sup_eq_right h

protected lemma inf_eq_left {a b : cau_seq α abs} (h : a ≤ b) :
  a ⊓ b ≈ a :=
by simpa only [cau_seq.inf_comm] using cau_seq.inf_eq_right h

protected lemma le_sup_left {a b : cau_seq α abs} : a ≤ a ⊔ b :=
le_of_exists ⟨0, λ j hj, le_sup_left⟩

protected lemma inf_le_left {a b : cau_seq α abs} : a ⊓ b ≤ a :=
le_of_exists ⟨0, λ j hj, inf_le_left⟩

protected lemma le_sup_right {a b : cau_seq α abs} : b ≤ a ⊔ b :=
le_of_exists ⟨0, λ j hj, le_sup_right⟩

protected lemma inf_le_right {a b : cau_seq α abs} : a ⊓ b ≤ b :=
le_of_exists ⟨0, λ j hj, inf_le_right⟩

protected lemma sup_le {a b c : cau_seq α abs} (ha : a ≤ c) (hb : b ≤ c) : a ⊔ b ≤ c :=
begin
  cases ha with ha ha,
  { cases hb with hb hb,
    { exact or.inl (cau_seq.sup_lt ha hb) },
    { replace ha := le_of_le_of_eq ha.le (setoid.symm hb),
      refine le_of_le_of_eq (or.inr _) hb,
      exact cau_seq.sup_eq_right ha }, },
  { replace hb := le_of_le_of_eq hb (setoid.symm ha),
    refine le_of_le_of_eq (or.inr _) ha,
    exact cau_seq.sup_eq_left hb }
end

protected lemma le_inf {a b c : cau_seq α abs} (hb : a ≤ b) (hc : a ≤ c) : a ≤ b ⊓ c :=
begin
  cases hb with hb hb,
  { cases hc with hc hc,
    { exact or.inl (cau_seq.lt_inf hb hc) },
    { replace hb := le_of_eq_of_le (setoid.symm hc) hb.le,
      refine le_of_eq_of_le hc (or.inr _),
      exact setoid.symm (cau_seq.inf_eq_right hb) }, },
  { replace hc := le_of_eq_of_le (setoid.symm hb) hc,
    refine le_of_eq_of_le hb (or.inr _),
    exact setoid.symm (cau_seq.inf_eq_left hc) }
end

/-! Note that `distrib_lattice (cau_seq α abs)` is not true because there is no `partial_order`. -/

protected lemma sup_inf_distrib_left (a b c : cau_seq α abs) : a ⊔ (b ⊓ c) = (a ⊔ b) ⊓ (a ⊔ c) :=
subtype.ext $ funext $ λ i, max_min_distrib_left

protected lemma sup_inf_distrib_right (a b c : cau_seq α abs) : (a ⊓ b) ⊔ c = (a ⊔ c) ⊓ (b ⊔ c) :=
subtype.ext $ funext $ λ i, max_min_distrib_right

end abs

end cau_seq


/-
Copyright (c) 2021 Andrew Yang. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Andrew Yang

! This file was ported from Lean 3 source module ring_theory.local_properties
! leanprover-community/mathlib commit 831c494092374cfe9f50591ed0ac81a25efc5b86
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.RingTheory.FiniteType
import Mathbin.RingTheory.Localization.AtPrime
import Mathbin.RingTheory.Localization.Away
import Mathbin.RingTheory.Localization.Integer
import Mathbin.RingTheory.Localization.Submodule
import Mathbin.RingTheory.Nilpotent
import Mathbin.RingTheory.RingHomProperties

/-!
# Local properties of commutative rings

In this file, we provide the proofs of various local properties.

## Naming Conventions

* `localization_P` : `P` holds for `S⁻¹R` if `P` holds for `R`.
* `P_of_localization_maximal` : `P` holds for `R` if `P` holds for `Rₘ` for all maximal `m`.
* `P_of_localization_prime` : `P` holds for `R` if `P` holds for `Rₘ` for all prime `m`.
* `P_of_localization_span` : `P` holds for `R` if given a spanning set `{fᵢ}`, `P` holds for all
  `R_{fᵢ}`.

## Main results

The following properties are covered:

* The triviality of an ideal or an element:
  `ideal_eq_bot_of_localization`, `eq_zero_of_localization`
* `is_reduced` : `localization_is_reduced`, `is_reduced_of_localization_maximal`.
* `finite`: `localization_finite`, `finite_of_localization_span`
* `finite_type`: `localization_finite_type`, `finite_type_of_localization_span`

-/

open Pointwise Classical BigOperators

universe u

variable {R S : Type u} [CommRing R] [CommRing S] (M : Submonoid R)

variable (N : Submonoid S) (R' S' : Type u) [CommRing R'] [CommRing S'] (f : R →+* S)

variable [Algebra R R'] [Algebra S S']

section Properties

section CommRing

variable (P : ∀ (R : Type u) [CommRing R], Prop)

include P

/-- A property `P` of comm rings is said to be preserved by localization
  if `P` holds for `M⁻¹R` whenever `P` holds for `R`. -/
def LocalizationPreserves : Prop :=
  ∀ {R : Type u} [hR : CommRing R] (M : Submonoid R) (S : Type u) [hS : CommRing S] [Algebra R S]
    [IsLocalization M S], @P R hR → @P S hS
#align localization_preserves LocalizationPreserves

/-- A property `P` of comm rings satisfies `of_localization_maximal` if
  if `P` holds for `R` whenever `P` holds for `Rₘ` for all maximal ideal `m`. -/
def OfLocalizationMaximal : Prop :=
  ∀ (R : Type u) [CommRing R],
    (∀ (J : Ideal R) (hJ : J.IsMaximal), P (Localization.AtPrime J)) → P R
#align of_localization_maximal OfLocalizationMaximal

end CommRing

section RingHom

variable (P : ∀ {R S : Type u} [CommRing R] [CommRing S] (f : R →+* S), Prop)

include P

/-- A property `P` of ring homs is said to be preserved by localization
 if `P` holds for `M⁻¹R →+* M⁻¹S` whenever `P` holds for `R →+* S`. -/
def RingHom.LocalizationPreserves :=
  ∀ ⦃R S : Type u⦄ [CommRing R] [CommRing S] (f : R →+* S) (M : Submonoid R) (R' S' : Type u)
    [CommRing R'] [CommRing S'] [Algebra R R'] [Algebra S S'] [IsLocalization M R']
    [IsLocalization (M.map f) S'],
    P f → P (IsLocalization.map S' f (Submonoid.le_comap_map M) : R' →+* S')
#align ring_hom.localization_preserves RingHom.LocalizationPreserves

/-- A property `P` of ring homs satisfies `ring_hom.of_localization_finite_span`
if `P` holds for `R →+* S` whenever there exists a finite set `{ r }` that spans `R` such that
`P` holds for `Rᵣ →+* Sᵣ`.

Note that this is equivalent to `ring_hom.of_localization_span` via
`ring_hom.of_localization_span_iff_finite`, but this is easier to prove. -/
def RingHom.OfLocalizationFiniteSpan :=
  ∀ ⦃R S : Type u⦄ [CommRing R] [CommRing S] (f : R →+* S) (s : Finset R)
    (hs : Ideal.span (s : Set R) = ⊤) (H : ∀ r : s, P (Localization.awayMap f r)), P f
#align ring_hom.of_localization_finite_span RingHom.OfLocalizationFiniteSpan

/-- A property `P` of ring homs satisfies `ring_hom.of_localization_finite_span`
if `P` holds for `R →+* S` whenever there exists a set `{ r }` that spans `R` such that
`P` holds for `Rᵣ →+* Sᵣ`.

Note that this is equivalent to `ring_hom.of_localization_finite_span` via
`ring_hom.of_localization_span_iff_finite`, but this has less restrictions when applying. -/
def RingHom.OfLocalizationSpan :=
  ∀ ⦃R S : Type u⦄ [CommRing R] [CommRing S] (f : R →+* S) (s : Set R) (hs : Ideal.span s = ⊤)
    (H : ∀ r : s, P (Localization.awayMap f r)), P f
#align ring_hom.of_localization_span RingHom.OfLocalizationSpan

/-- A property `P` of ring homs satisfies `ring_hom.holds_for_localization_away`
 if `P` holds for each localization map `R →+* Rᵣ`. -/
def RingHom.HoldsForLocalizationAway : Prop :=
  ∀ ⦃R : Type u⦄ (S : Type u) [CommRing R] [CommRing S] [Algebra R S] (r : R)
    [IsLocalization.Away r S], P (algebraMap R S)
#align ring_hom.holds_for_localization_away RingHom.HoldsForLocalizationAway

/-- A property `P` of ring homs satisfies `ring_hom.of_localization_finite_span_target`
if `P` holds for `R →+* S` whenever there exists a finite set `{ r }` that spans `S` such that
`P` holds for `R →+* Sᵣ`.

Note that this is equivalent to `ring_hom.of_localization_span_target` via
`ring_hom.of_localization_span_target_iff_finite`, but this is easier to prove. -/
def RingHom.OfLocalizationFiniteSpanTarget : Prop :=
  ∀ ⦃R S : Type u⦄ [CommRing R] [CommRing S] (f : R →+* S) (s : Finset S)
    (hs : Ideal.span (s : Set S) = ⊤)
    (H : ∀ r : s, P ((algebraMap S (Localization.Away (r : S))).comp f)), P f
#align ring_hom.of_localization_finite_span_target RingHom.OfLocalizationFiniteSpanTarget

/-- A property `P` of ring homs satisfies `ring_hom.of_localization_span_target`
if `P` holds for `R →+* S` whenever there exists a set `{ r }` that spans `S` such that
`P` holds for `R →+* Sᵣ`.

Note that this is equivalent to `ring_hom.of_localization_finite_span_target` via
`ring_hom.of_localization_span_target_iff_finite`, but this has less restrictions when applying. -/
def RingHom.OfLocalizationSpanTarget : Prop :=
  ∀ ⦃R S : Type u⦄ [CommRing R] [CommRing S] (f : R →+* S) (s : Set S) (hs : Ideal.span s = ⊤)
    (H : ∀ r : s, P ((algebraMap S (Localization.Away (r : S))).comp f)), P f
#align ring_hom.of_localization_span_target RingHom.OfLocalizationSpanTarget

/-- A property `P` of ring homs satisfies `of_localization_prime` if
  if `P` holds for `R` whenever `P` holds for `Rₘ` for all prime ideals `p`. -/
def RingHom.OfLocalizationPrime : Prop :=
  ∀ ⦃R S : Type u⦄ [CommRing R] [CommRing S] (f : R →+* S),
    (∀ (J : Ideal S) (hJ : J.IsPrime), P (Localization.localRingHom _ J f rfl)) → P f
#align ring_hom.of_localization_prime RingHom.OfLocalizationPrime

/-- A property of ring homs is local if it is preserved by localizations and compositions, and for
each `{ r }` that spans `S`, we have `P (R →+* S) ↔ ∀ r, P (R →+* Sᵣ)`. -/
structure RingHom.PropertyIsLocal : Prop where
  LocalizationPreserves : RingHom.LocalizationPreserves @P
  OfLocalizationSpanTarget : RingHom.OfLocalizationSpanTarget @P
  StableUnderComposition : RingHom.StableUnderComposition @P
  HoldsForLocalizationAway : RingHom.HoldsForLocalizationAway @P
#align ring_hom.property_is_local RingHom.PropertyIsLocal

theorem RingHom.ofLocalizationSpan_iff_finite :
    RingHom.OfLocalizationSpan @P ↔ RingHom.OfLocalizationFiniteSpan @P :=
  by
  delta RingHom.OfLocalizationSpan RingHom.OfLocalizationFiniteSpan
  apply forall₅_congr
  -- TODO: Using `refine` here breaks `resetI`.
  intros
  constructor
  · intro h s
    exact h s
  · intro h s hs hs'
    obtain ⟨s', h₁, h₂⟩ := (Ideal.span_eq_top_iff_finite s).mp hs
    exact h s' h₂ fun x => hs' ⟨_, h₁ x.Prop⟩
#align ring_hom.of_localization_span_iff_finite RingHom.ofLocalizationSpan_iff_finite

theorem RingHom.ofLocalizationSpanTarget_iff_finite :
    RingHom.OfLocalizationSpanTarget @P ↔ RingHom.OfLocalizationFiniteSpanTarget @P :=
  by
  delta RingHom.OfLocalizationSpanTarget RingHom.OfLocalizationFiniteSpanTarget
  apply forall₅_congr
  -- TODO: Using `refine` here breaks `resetI`.
  intros
  constructor
  · intro h s
    exact h s
  · intro h s hs hs'
    obtain ⟨s', h₁, h₂⟩ := (Ideal.span_eq_top_iff_finite s).mp hs
    exact h s' h₂ fun x => hs' ⟨_, h₁ x.Prop⟩
#align ring_hom.of_localization_span_target_iff_finite RingHom.ofLocalizationSpanTarget_iff_finite

variable {P f R' S'}

theorem RingHom.PropertyIsLocal.respectsIso (hP : RingHom.PropertyIsLocal @P) :
    RingHom.RespectsIso @P :=
  by
  apply hP.stable_under_composition.respects_iso
  introv
  skip
  letI := e.to_ring_hom.to_algebra
  apply (config := { instances := false }) hP.holds_for_localization_away
  apply IsLocalization.away_of_isUnit_of_bijective _ isUnit_one
  exact e.bijective
#align ring_hom.property_is_local.respects_iso RingHom.PropertyIsLocal.respectsIso

-- Almost all arguments are implicit since this is not intended to use mid-proof.
theorem RingHom.LocalizationPreserves.away (H : RingHom.LocalizationPreserves @P) (r : R)
    [IsLocalization.Away r R'] [IsLocalization.Away (f r) S'] (hf : P f) :
    P (IsLocalization.Away.map R' S' f r) := by
  skip
  have : IsLocalization ((Submonoid.powers r).map f) S' :=
    by
    rw [Submonoid.map_powers]
    assumption
  exact H f (Submonoid.powers r) R' S' hf
#align ring_hom.localization_preserves.away RingHom.LocalizationPreserves.away

theorem RingHom.PropertyIsLocal.ofLocalizationSpan (hP : RingHom.PropertyIsLocal @P) :
    RingHom.OfLocalizationSpan @P := by
  introv R hs hs'
  skip
  apply_fun Ideal.map f  at hs
  rw [Ideal.map_span, Ideal.map_top] at hs
  apply hP.of_localization_span_target _ _ hs
  rintro ⟨_, r, hr, rfl⟩
  have := hs' ⟨r, hr⟩
  convert hP.stable_under_composition _ _ (hP.holds_for_localization_away (Localization.Away r) r)
      (hs' ⟨r, hr⟩) using
    1
  exact (IsLocalization.map_comp _).symm
#align ring_hom.property_is_local.of_localization_span RingHom.PropertyIsLocal.ofLocalizationSpan

end RingHom

end Properties

section Ideal

open nonZeroDivisors

/-- Let `I J : ideal R`. If the localization of `I` at each maximal ideal `P` is included in
the localization of `J` at `P`, then `I ≤ J`. -/
theorem Ideal.le_of_localization_maximal {I J : Ideal R}
    (h :
      ∀ (P : Ideal R) (hP : P.IsMaximal),
        Ideal.map (algebraMap R (Localization.AtPrime P)) I ≤
          Ideal.map (algebraMap R (Localization.AtPrime P)) J) :
    I ≤ J := by
  intro x hx
  suffices J.colon (Ideal.span {x}) = ⊤ by
    simpa using
      submodule.mem_colon.mp
        (show (1 : R) ∈ J.colon (Ideal.span {x}) from this.symm ▸ Submodule.mem_top) x
        (Ideal.mem_span_singleton_self x)
  refine' Not.imp_symm (J.colon (Ideal.span {x})).exists_le_maximal _
  push_neg
  intro P hP le
  obtain ⟨⟨⟨a, ha⟩, ⟨s, hs⟩⟩, eq⟩ :=
    (IsLocalization.mem_map_algebraMap_iff P.prime_compl _).mp (h P hP (Ideal.mem_map_of_mem _ hx))
  rw [← _root_.map_mul, ← sub_eq_zero, ← map_sub] at eq
  obtain ⟨⟨m, hm⟩, eq⟩ := (IsLocalization.map_eq_zero_iff P.prime_compl _ _).mp Eq
  refine' hs ((hP.is_prime.mem_or_mem (le (ideal.mem_colon_singleton.mpr _))).resolve_right hm)
  simp only [Subtype.coe_mk, mul_sub, sub_eq_zero, mul_comm x s, mul_left_comm] at eq
  simpa only [mul_assoc, Eq] using J.mul_mem_left m ha
#align ideal.le_of_localization_maximal Ideal.le_of_localization_maximal

/-- Let `I J : ideal R`. If the localization of `I` at each maximal ideal `P` is equal to
the localization of `J` at `P`, then `I = J`. -/
theorem Ideal.eq_of_localization_maximal {I J : Ideal R}
    (h :
      ∀ (P : Ideal R) (hP : P.IsMaximal),
        Ideal.map (algebraMap R (Localization.AtPrime P)) I =
          Ideal.map (algebraMap R (Localization.AtPrime P)) J) :
    I = J :=
  le_antisymm (Ideal.le_of_localization_maximal fun P hP => (h P hP).le)
    (Ideal.le_of_localization_maximal fun P hP => (h P hP).ge)
#align ideal.eq_of_localization_maximal Ideal.eq_of_localization_maximal

/-- An ideal is trivial if its localization at every maximal ideal is trivial. -/
theorem ideal_eq_bot_of_localization' (I : Ideal R)
    (h :
      ∀ (J : Ideal R) (hJ : J.IsMaximal), Ideal.map (algebraMap R (Localization.AtPrime J)) I = ⊥) :
    I = ⊥ :=
  Ideal.eq_of_localization_maximal fun P hP => by simpa using h P hP
#align ideal_eq_bot_of_localization' ideal_eq_bot_of_localization'

-- TODO: This proof should work for all modules, once we have enough material on submodules of
-- localized modules.
/-- An ideal is trivial if its localization at every maximal ideal is trivial. -/
theorem ideal_eq_bot_of_localization (I : Ideal R)
    (h :
      ∀ (J : Ideal R) (hJ : J.IsMaximal),
        IsLocalization.coeSubmodule (Localization.AtPrime J) I = ⊥) :
    I = ⊥ :=
  ideal_eq_bot_of_localization' _ fun P hP =>
    (Ideal.map_eq_bot_iff_le_ker _).mpr fun x hx =>
      by
      rw [RingHom.mem_ker, ← Submodule.mem_bot R, ← h P hP, IsLocalization.mem_coeSubmodule]
      exact ⟨x, hx, rfl⟩
#align ideal_eq_bot_of_localization ideal_eq_bot_of_localization

theorem eq_zero_of_localization (r : R)
    (h : ∀ (J : Ideal R) (hJ : J.IsMaximal), algebraMap R (Localization.AtPrime J) r = 0) : r = 0 :=
  by
  rw [← Ideal.span_singleton_eq_bot]
  apply ideal_eq_bot_of_localization
  intro J hJ
  delta IsLocalization.coeSubmodule
  erw [Submodule.map_span, Submodule.span_eq_bot]
  rintro _ ⟨_, h', rfl⟩
  cases set.mem_singleton_iff.mpr h'
  exact h J hJ
#align eq_zero_of_localization eq_zero_of_localization

end Ideal

section Reduced

theorem localization_isReduced : LocalizationPreserves fun R hR => IsReduced R :=
  by
  introv R _ _
  skip
  constructor
  rintro x ⟨_ | n, e⟩
  · simpa using congr_arg (· * x) e
  obtain ⟨⟨y, m⟩, hx⟩ := IsLocalization.surj M x
  dsimp only at hx
  let hx' := congr_arg (· ^ n.succ) hx
  simp only [mul_pow, e, MulZeroClass.zero_mul, ← RingHom.map_pow] at hx'
  rw [← (algebraMap R S).map_zero] at hx'
  obtain ⟨m', hm'⟩ := (IsLocalization.eq_iff_exists M S).mp hx'
  apply_fun (· * m' ^ n)  at hm'
  simp only [mul_assoc, MulZeroClass.zero_mul, MulZeroClass.mul_zero] at hm'
  rw [← mul_left_comm, ← pow_succ, ← mul_pow] at hm'
  replace hm' := IsNilpotent.eq_zero ⟨_, hm'.symm⟩
  rw [← (IsLocalization.map_units S m).mul_left_inj, hx, MulZeroClass.zero_mul,
    IsLocalization.map_eq_zero_iff M]
  exact ⟨m', by rw [← hm', mul_comm]⟩
#align localization_is_reduced localization_isReduced

instance [IsReduced R] : IsReduced (Localization M) :=
  localization_isReduced M _ inferInstance

theorem isReduced_ofLocalizationMaximal : OfLocalizationMaximal fun R hR => IsReduced R :=
  by
  introv R h
  constructor
  intro x hx
  apply eq_zero_of_localization
  intro J hJ
  specialize h J hJ
  skip
  exact (hx.map <| algebraMap R <| Localization.AtPrime J).eq_zero
#align is_reduced_of_localization_maximal isReduced_ofLocalizationMaximal

end Reduced

section Surjective

theorem localizationPreserves_surjective :
    RingHom.LocalizationPreserves fun R S _ _ f => Function.Surjective f :=
  by
  introv R H x
  skip
  obtain ⟨x, ⟨_, s, hs, rfl⟩, rfl⟩ := IsLocalization.mk'_surjective (M.map f) x
  obtain ⟨y, rfl⟩ := H x
  use IsLocalization.mk' R' y ⟨s, hs⟩
  rw [IsLocalization.map_mk']
  rfl
#align localization_preserves_surjective localizationPreserves_surjective

theorem surjective_ofLocalizationSpan :
    RingHom.OfLocalizationSpan fun R S _ _ f => Function.Surjective f :=
  by
  introv R e H
  rw [← Set.range_iff_surjective, Set.eq_univ_iff_forall]
  skip
  letI := f.to_algebra
  intro x
  apply Submodule.mem_of_span_eq_top_of_smul_pow_mem (Algebra.ofId R S).toLinearMap.range s e
  intro r
  obtain ⟨a, e'⟩ := H r (algebraMap _ _ x)
  obtain ⟨b, ⟨_, n, rfl⟩, rfl⟩ := IsLocalization.mk'_surjective (Submonoid.powers (r : R)) a
  erw [IsLocalization.map_mk'] at e'
  rw [eq_comm, IsLocalization.eq_mk'_iff_mul_eq, Subtype.coe_mk, Subtype.coe_mk, ← map_mul] at e'
  obtain ⟨⟨_, n', rfl⟩, e''⟩ := (IsLocalization.eq_iff_exists (Submonoid.powers (f r)) _).mp e'
  rw [Subtype.coe_mk, mul_comm x, ← mul_assoc, ← map_pow, ← map_mul, ← map_mul, ← pow_add] at e''
  exact ⟨n' + n, _, e''.symm⟩
#align surjective_of_localization_span surjective_ofLocalizationSpan

end Surjective

section Finite

/-- If `S` is a finite `R`-algebra, then `S' = M⁻¹S` is a finite `R' = M⁻¹R`-algebra. -/
theorem localization_finite : RingHom.LocalizationPreserves @RingHom.Finite :=
  by
  introv R hf
  -- Setting up the `algebra` and `is_scalar_tower` instances needed
  skip
  letI := f.to_algebra
  letI := ((algebraMap S S').comp f).toAlgebra
  let f' : R' →+* S' := IsLocalization.map S' f (Submonoid.le_comap_map M)
  letI := f'.to_algebra
  haveI : IsScalarTower R R' S' := IsScalarTower.of_algebraMap_eq' (IsLocalization.map_comp _).symm
  let fₐ : S →ₐ[R] S' := AlgHom.mk' (algebraMap S S') fun c x => RingHom.map_mul _ _ _
  -- We claim that if `S` is generated by `T` as an `R`-module,
  -- then `S'` is generated by `T` as an `R'`-module.
  obtain ⟨T, hT⟩ := hf
  use T.image (algebraMap S S')
  rw [eq_top_iff]
  rintro x -
  -- By the hypotheses, for each `x : S'`, we have `x = y / (f r)` for some `y : S` and `r : M`.
  -- Since `S` is generated by `T`, the image of `y` should fall in the span of the image of `T`.
  obtain ⟨y, ⟨_, ⟨r, hr, rfl⟩⟩, rfl⟩ := IsLocalization.mk'_surjective (M.map f) x
  rw [IsLocalization.mk'_eq_mul_mk'_one, mul_comm, Finset.coe_image]
  have hy : y ∈ Submodule.span R ↑T := by
    rw [hT]
    trivial
  replace hy : algebraMap S S' y ∈ Submodule.map fₐ.to_linear_map (Submodule.span R T) :=
    Submodule.mem_map_of_mem hy
  rw [Submodule.map_span fₐ.to_linear_map T] at hy
  have H :
    Submodule.span R (algebraMap S S' '' T) ≤
      (Submodule.span R' (algebraMap S S' '' T)).restrictScalars R :=
    by
    rw [Submodule.span_le]
    exact Submodule.subset_span
  -- Now, since `y ∈ span T`, and `(f r)⁻¹ ∈ R'`, `x / (f r)` is in `span T` as well.
  convert(Submodule.span R' (algebraMap S S' '' T)).smul_mem (IsLocalization.mk' R' (1 : R) ⟨r, hr⟩)
      (H hy) using
    1
  rw [Algebra.smul_def]
  erw [IsLocalization.map_mk']
  rw [map_one]
  rfl
#align localization_finite localization_finite

theorem localization_away_map_finite (r : R) [IsLocalization.Away r R']
    [IsLocalization.Away (f r) S'] (hf : f.Finite) : (IsLocalization.Away.map R' S' f r).Finite :=
  localization_finite.Away r hf
#align localization_away_map_finite localization_away_map_finite

/-- Let `S` be an `R`-algebra, `M` an submonoid of `R`, and `S' = M⁻¹S`.
If the image of some `x : S` falls in the span of some finite `s ⊆ S'` over `R`,
then there exists some `m : M` such that `m • x` falls in the
span of `finset_integer_multiple _ s` over `R`.
-/
theorem IsLocalization.smul_mem_finsetIntegerMultiple_span [Algebra R S] [Algebra R S']
    [IsScalarTower R S S'] [IsLocalization (M.map (algebraMap R S)) S'] (x : S) (s : Finset S')
    (hx : algebraMap S S' x ∈ Submodule.span R (s : Set S')) :
    ∃ m : M,
      m • x ∈
        Submodule.span R
          (IsLocalization.finsetIntegerMultiple (M.map (algebraMap R S)) s : Set S) :=
  by
  let g : S →ₐ[R] S' :=
    AlgHom.mk' (algebraMap S S') fun c x => by simp [Algebra.algebraMap_eq_smul_one]
  -- We first obtain the `y' ∈ M` such that `s' = y' • s` is falls in the image of `S` in `S'`.
  let y := IsLocalization.commonDenomOfFinset (M.map (algebraMap R S)) s
  have hx₁ : (y : S) • ↑s = g '' _ := (IsLocalization.finsetIntegerMultiple_image _ s).symm
  obtain ⟨y', hy', e : algebraMap R S y' = y⟩ := y.prop
  have : algebraMap R S y' • (s : Set S') = y' • s := by
    simp_rw [Algebra.algebraMap_eq_smul_one, smul_assoc, one_smul]
  rw [← e, this] at hx₁
  replace hx₁ := congr_arg (Submodule.span R) hx₁
  rw [Submodule.span_smul] at hx₁
  replace hx : _ ∈ y' • Submodule.span R (s : Set S') := Set.smul_mem_smul_set hx
  rw [hx₁] at hx
  erw [← g.map_smul, ← Submodule.map_span (g : S →ₗ[R] S')] at hx
  -- Since `x` falls in the span of `s` in `S'`, `y' • x : S` falls in the span of `s'` in `S'`.
  -- That is, there exists some `x' : S` in the span of `s'` in `S` and `x' = y' • x` in `S'`.
  -- Thus `a • (y' • x) = a • x' ∈ span s'` in `S` for some `a ∈ M`.
  obtain ⟨x', hx', hx'' : algebraMap _ _ _ = _⟩ := hx
  obtain ⟨⟨_, a, ha₁, rfl⟩, ha₂⟩ :=
    (IsLocalization.eq_iff_exists (M.map (algebraMap R S)) S').mp hx''
  use (⟨a, ha₁⟩ : M) * (⟨y', hy'⟩ : M)
  convert(Submodule.span R
          (IsLocalization.finsetIntegerMultiple (Submonoid.map (algebraMap R S) M) s :
            Set S)).smul_mem
      a hx' using
    1
  convert ha₂.symm
  · rw [Subtype.coe_mk, Submonoid.smul_def, Submonoid.coe_mul, ← smul_smul]
    exact Algebra.smul_def _ _
  · exact Algebra.smul_def _ _
#align is_localization.smul_mem_finset_integer_multiple_span IsLocalization.smul_mem_finsetIntegerMultiple_span

/-- If `S` is an `R' = M⁻¹R` algebra, and `x ∈ span R' s`,
then `t • x ∈ span R s` for some `t : M`.-/
theorem multiple_mem_span_of_mem_localization_span [Algebra R' S] [Algebra R S]
    [IsScalarTower R R' S] [IsLocalization M R'] (s : Set S) (x : S)
    (hx : x ∈ Submodule.span R' s) : ∃ t : M, t • x ∈ Submodule.span R s := by
  classical
    obtain ⟨s', hss', hs'⟩ := Submodule.mem_span_finite_of_mem_span hx
    rsuffices ⟨t, ht⟩ : ∃ t : M, t • x ∈ Submodule.span R (s' : Set S)
    · exact ⟨t, Submodule.span_mono hss' ht⟩
    clear hx hss' s
    revert x
    apply s'.induction_on
    · intro x hx
      use 1
      simpa using hx
    rintro a s ha hs x hx
    simp only [Finset.coe_insert, Finset.image_insert, Finset.coe_image, Subtype.coe_mk,
      Submodule.mem_span_insert] at hx⊢
    rcases hx with ⟨y, z, hz, rfl⟩
    rcases IsLocalization.surj M y with ⟨⟨y', s'⟩, e⟩
    replace e : _ * a = _ * a := (congr_arg (fun x => algebraMap R' S x * a) e : _)
    simp_rw [RingHom.map_mul, ← IsScalarTower.algebraMap_apply, mul_comm (algebraMap R' S y),
      mul_assoc, ← Algebra.smul_def] at e
    rcases hs _ hz with ⟨t, ht⟩
    refine' ⟨t * s', t * y', _, (Submodule.span R (s : Set S)).smul_mem s' ht, _⟩
    rw [smul_add, ← smul_smul, mul_comm, ← smul_smul, ← smul_smul, ← e]
    rfl
#align multiple_mem_span_of_mem_localization_span multiple_mem_span_of_mem_localization_span

/-- If `S` is an `R' = M⁻¹R` algebra, and `x ∈ adjoin R' s`,
then `t • x ∈ adjoin R s` for some `t : M`.-/
theorem multiple_mem_adjoin_of_mem_localization_adjoin [Algebra R' S] [Algebra R S]
    [IsScalarTower R R' S] [IsLocalization M R'] (s : Set S) (x : S)
    (hx : x ∈ Algebra.adjoin R' s) : ∃ t : M, t • x ∈ Algebra.adjoin R s :=
  by
  change ∃ t : M, t • x ∈ (Algebra.adjoin R s).toSubmodule
  change x ∈ (Algebra.adjoin R' s).toSubmodule at hx
  simp_rw [Algebra.adjoin_eq_span] at hx⊢
  exact multiple_mem_span_of_mem_localization_span M R' _ _ hx
#align multiple_mem_adjoin_of_mem_localization_adjoin multiple_mem_adjoin_of_mem_localization_adjoin

theorem finite_ofLocalizationSpan : RingHom.OfLocalizationSpan @RingHom.Finite :=
  by
  rw [RingHom.ofLocalizationSpan_iff_finite]
  introv R hs H
  -- We first setup the instances
  skip
  letI := f.to_algebra
  letI := fun r : s => (Localization.awayMap f r).toAlgebra
  have :
    ∀ r : s,
      IsLocalization ((Submonoid.powers (r : R)).map (algebraMap R S)) (Localization.Away (f r)) :=
    by
    intro r
    rw [Submonoid.map_powers]
    exact Localization.isLocalization
  haveI : ∀ r : s, IsScalarTower R (Localization.Away (r : R)) (Localization.Away (f r)) := fun r =>
    IsScalarTower.of_algebraMap_eq' (IsLocalization.map_comp _).symm
  -- By the hypothesis, we may find a finite generating set for each `Sᵣ`. This set can then be
  -- lifted into `R` by multiplying a sufficiently large power of `r`. I claim that the union of
  -- these generates `S`.
  constructor
  replace H := fun r => (H r).1
  choose s₁ s₂ using H
  let sf := fun x : s => IsLocalization.finsetIntegerMultiple (Submonoid.powers (f x)) (s₁ x)
  use s.attach.bUnion sf
  rw [Submodule.span_attach_bunionᵢ, eq_top_iff]
  -- It suffices to show that `r ^ n • x ∈ span T` for each `r : s`, since `{ r ^ n }` spans `R`.
  -- This then follows from the fact that each `x : R` is a linear combination of the generating set
  -- of `Sᵣ`. By multiplying a sufficiently large power of `r`, we can cancel out the `r`s in the
  -- denominators of both the generating set and the coefficients.
  rintro x -
  apply Submodule.mem_of_span_eq_top_of_smul_pow_mem _ (s : Set R) hs _ _
  intro r
  obtain ⟨⟨_, n₁, rfl⟩, hn₁⟩ :=
    multiple_mem_span_of_mem_localization_span (Submonoid.powers (r : R))
      (Localization.Away (r : R)) (s₁ r : Set (Localization.Away (f r))) (algebraMap S _ x)
      (by
        rw [s₂ r]
        trivial)
  rw [Submonoid.smul_def, Algebra.smul_def, IsScalarTower.algebraMap_apply R S, Subtype.coe_mk, ←
    map_mul] at hn₁
  obtain ⟨⟨_, n₂, rfl⟩, hn₂⟩ :=
    IsLocalization.smul_mem_finsetIntegerMultiple_span (Submonoid.powers (r : R))
      (Localization.Away (f r)) _ (s₁ r) hn₁
  rw [Submonoid.smul_def, ← Algebra.smul_def, smul_smul, Subtype.coe_mk, ← pow_add] at hn₂
  simp_rw [Submonoid.map_powers] at hn₂
  use n₂ + n₁
  exact le_supᵢ (fun x : s => Submodule.span R (sf x : Set S)) r hn₂
#align finite_of_localization_span finite_ofLocalizationSpan

end Finite

section FiniteType

theorem localization_finiteType : RingHom.LocalizationPreserves @RingHom.FiniteType :=
  by
  introv R hf
  -- mirrors the proof of `localization_map_finite`
  skip
  letI := f.to_algebra
  letI := ((algebraMap S S').comp f).toAlgebra
  let f' : R' →+* S' := IsLocalization.map S' f (Submonoid.le_comap_map M)
  letI := f'.to_algebra
  haveI : IsScalarTower R R' S' := IsScalarTower.of_algebraMap_eq' (IsLocalization.map_comp _).symm
  let fₐ : S →ₐ[R] S' := AlgHom.mk' (algebraMap S S') fun c x => RingHom.map_mul _ _ _
  obtain ⟨T, hT⟩ := id hf
  use T.image (algebraMap S S')
  rw [eq_top_iff]
  rintro x -
  obtain ⟨y, ⟨_, ⟨r, hr, rfl⟩⟩, rfl⟩ := IsLocalization.mk'_surjective (M.map f) x
  rw [IsLocalization.mk'_eq_mul_mk'_one, mul_comm, Finset.coe_image]
  have hy : y ∈ Algebra.adjoin R (T : Set S) :=
    by
    rw [hT]
    trivial
  replace hy : algebraMap S S' y ∈ (Algebra.adjoin R (T : Set S)).map fₐ :=
    subalgebra.mem_map.mpr ⟨_, hy, rfl⟩
  rw [fₐ.map_adjoin T] at hy
  have H :
    Algebra.adjoin R (algebraMap S S' '' T) ≤
      (Algebra.adjoin R' (algebraMap S S' '' T)).restrictScalars R :=
    by
    rw [Algebra.adjoin_le_iff]
    exact Algebra.subset_adjoin
  convert(Algebra.adjoin R' (algebraMap S S' '' T)).smul_mem (H hy)
      (IsLocalization.mk' R' (1 : R) ⟨r, hr⟩) using
    1
  rw [Algebra.smul_def]
  erw [IsLocalization.map_mk']
  rw [map_one]
  rfl
#align localization_finite_type localization_finiteType

theorem localization_away_map_finiteType (r : R) [IsLocalization.Away r R']
    [IsLocalization.Away (f r) S'] (hf : f.FiniteType) :
    (IsLocalization.Away.map R' S' f r).FiniteType :=
  localization_finiteType.Away r hf
#align localization_away_map_finite_type localization_away_map_finiteType

variable {S'}

/-- Let `S` be an `R`-algebra, `M` a submonoid of `S`, `S' = M⁻¹S`.
Suppose the image of some `x : S` falls in the adjoin of some finite `s ⊆ S'` over `R`,
and `A` is an `R`-subalgebra of `S` containing both `M` and the numerators of `s`.
Then, there exists some `m : M` such that `m • x` falls in `A`.
-/
theorem IsLocalization.exists_smul_mem_of_mem_adjoin [Algebra R S] [Algebra R S']
    [IsScalarTower R S S'] (M : Submonoid S) [IsLocalization M S'] (x : S) (s : Finset S')
    (A : Subalgebra R S) (hA₁ : (IsLocalization.finsetIntegerMultiple M s : Set S) ⊆ A)
    (hA₂ : M ≤ A.toSubmonoid) (hx : algebraMap S S' x ∈ Algebra.adjoin R (s : Set S')) :
    ∃ m : M, m • x ∈ A :=
  by
  let g : S →ₐ[R] S' := IsScalarTower.toAlgHom R S S'
  let y := IsLocalization.commonDenomOfFinset M s
  have hx₁ : (y : S) • ↑s = g '' _ := (IsLocalization.finsetIntegerMultiple_image _ s).symm
  obtain ⟨n, hn⟩ :=
    Algebra.pow_smul_mem_of_smul_subset_of_mem_adjoin (y : S) (s : Set S') (A.map g)
      (by
        rw [hx₁]
        exact Set.image_subset _ hA₁)
      hx (Set.mem_image_of_mem _ (hA₂ y.2))
  obtain ⟨x', hx', hx''⟩ := hn n (le_of_eq rfl)
  rw [Algebra.smul_def, ← _root_.map_mul] at hx''
  obtain ⟨a, ha₂⟩ := (IsLocalization.eq_iff_exists M S').mp hx''
  use a * y ^ n
  convert A.mul_mem hx' (hA₂ a.prop)
  rw [Submonoid.smul_def, smul_eq_mul, Submonoid.coe_mul, [anonymous], mul_assoc, ← ha₂, mul_comm]
#align is_localization.exists_smul_mem_of_mem_adjoin IsLocalization.exists_smul_mem_of_mem_adjoin

/-- Let `S` be an `R`-algebra, `M` an submonoid of `R`, and `S' = M⁻¹S`.
If the image of some `x : S` falls in the adjoin of some finite `s ⊆ S'` over `R`,
then there exists some `m : M` such that `m • x` falls in the
adjoin of `finset_integer_multiple _ s` over `R`.
-/
theorem IsLocalization.lift_mem_adjoin_finsetIntegerMultiple [Algebra R S] [Algebra R S']
    [IsScalarTower R S S'] [IsLocalization (M.map (algebraMap R S)) S'] (x : S) (s : Finset S')
    (hx : algebraMap S S' x ∈ Algebra.adjoin R (s : Set S')) :
    ∃ m : M,
      m • x ∈
        Algebra.adjoin R
          (IsLocalization.finsetIntegerMultiple (M.map (algebraMap R S)) s : Set S) :=
  by
  obtain ⟨⟨_, a, ha, rfl⟩, e⟩ :=
    IsLocalization.exists_smul_mem_of_mem_adjoin (M.map (algebraMap R S)) x s (Algebra.adjoin R _)
      Algebra.subset_adjoin _ hx
  · exact ⟨⟨a, ha⟩, by simpa [Submonoid.smul_def] using e⟩
  · rintro _ ⟨a, ha, rfl⟩
    exact Subalgebra.algebraMap_mem _ a
#align is_localization.lift_mem_adjoin_finset_integer_multiple IsLocalization.lift_mem_adjoin_finsetIntegerMultiple

theorem finiteType_ofLocalizationSpan : RingHom.OfLocalizationSpan @RingHom.FiniteType :=
  by
  rw [RingHom.ofLocalizationSpan_iff_finite]
  introv R hs H
  -- mirrors the proof of `finite_of_localization_span`
  skip
  letI := f.to_algebra
  letI := fun r : s => (Localization.awayMap f r).toAlgebra
  have :
    ∀ r : s,
      IsLocalization ((Submonoid.powers (r : R)).map (algebraMap R S)) (Localization.Away (f r)) :=
    by
    intro r
    rw [Submonoid.map_powers]
    exact Localization.isLocalization
  haveI : ∀ r : s, IsScalarTower R (Localization.Away (r : R)) (Localization.Away (f r)) := fun r =>
    IsScalarTower.of_algebraMap_eq' (IsLocalization.map_comp _).symm
  constructor
  replace H := fun r => (H r).1
  choose s₁ s₂ using H
  let sf := fun x : s => IsLocalization.finsetIntegerMultiple (Submonoid.powers (f x)) (s₁ x)
  use s.attach.bUnion sf
  convert(Algebra.adjoin_attach_bunionᵢ sf).trans _
  rw [eq_top_iff]
  rintro x -
  apply
    (⨆ x : s, Algebra.adjoin R (sf x : Set S)).toSubmodule.mem_of_span_eq_top_of_smul_pow_mem _ hs _
      _
  intro r
  obtain ⟨⟨_, n₁, rfl⟩, hn₁⟩ :=
    multiple_mem_adjoin_of_mem_localization_adjoin (Submonoid.powers (r : R))
      (Localization.Away (r : R)) (s₁ r : Set (Localization.Away (f r)))
      (algebraMap S (Localization.Away (f r)) x)
      (by
        rw [s₂ r]
        trivial)
  rw [Submonoid.smul_def, Algebra.smul_def, IsScalarTower.algebraMap_apply R S, Subtype.coe_mk, ←
    map_mul] at hn₁
  obtain ⟨⟨_, n₂, rfl⟩, hn₂⟩ :=
    IsLocalization.lift_mem_adjoin_finsetIntegerMultiple (Submonoid.powers (r : R)) _ (s₁ r) hn₁
  rw [Submonoid.smul_def, ← Algebra.smul_def, smul_smul, Subtype.coe_mk, ← pow_add] at hn₂
  simp_rw [Submonoid.map_powers] at hn₂
  use n₂ + n₁
  exact le_supᵢ (fun x : s => Algebra.adjoin R (sf x : Set S)) r hn₂
#align finite_type_of_localization_span finiteType_ofLocalizationSpan

end FiniteType


/-
Copyright (c) 2019 Kenny Lau. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kenny Lau
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.ring_theory.algebra_tower
import Mathlib.ring_theory.polynomial.scale_roots
import Mathlib.PostPort

universes u_1 u_3 u_2 u_4 u_5

namespace Mathlib

/-!
# Integral closure of a subring.

If A is an R-algebra then `a : A` is integral over R if it is a root of a monic polynomial
with coefficients in R. Enough theory is developed to prove that integral elements
form a sub-R-algebra of A.

## Main definitions

Let `R` be a `comm_ring` and let `A` be an R-algebra.

* `ring_hom.is_integral_elem (f : R →+* A) (x : A)` : `x` is integral with respect to the map `f`,

* `is_integral (x : A)`  : `x` is integral over `R`, i.e., is a root of a monic polynomial with
                           coefficients in `R`.
* `integral_closure R A` : the integral closure of `R` in `A`, regarded as a sub-`R`-algebra of `A`.
-/

/-- An element `x` of `A` is said to be integral over `R` with respect to `f`
if it is a root of a monic polynomial `p : polynomial R` evaluated under `f` -/
def ring_hom.is_integral_elem {R : Type u_1} {A : Type u_3} [comm_ring R] [ring A] (f : R →+* A)
    (x : A) :=
  ∃ (p : polynomial R), polynomial.monic p ∧ polynomial.eval₂ f x p = 0

/-- A ring homomorphism `f : R →+* A` is said to be integral
if every element `A` is integral with respect to the map `f` -/
def ring_hom.is_integral {R : Type u_1} {A : Type u_3} [comm_ring R] [ring A] (f : R →+* A) :=
  ∀ (x : A), ring_hom.is_integral_elem f x

/-- An element `x` of an algebra `A` over a commutative ring `R` is said to be *integral*,
if it is a root of some monic polynomial `p : polynomial R`.
Equivalently, the element is integral over `R` with respect to the induced `algebra_map` -/
def is_integral (R : Type u_1) {A : Type u_3} [comm_ring R] [ring A] [algebra R A] (x : A) :=
  ring_hom.is_integral_elem (algebra_map R A) x

/-- An algebra is integral if every element of the extension is integral over the base ring -/
def algebra.is_integral (R : Type u_1) (A : Type u_3) [comm_ring R] [ring A] [algebra R A] :=
  ring_hom.is_integral (algebra_map R A)

theorem ring_hom.is_integral_map {R : Type u_1} {S : Type u_2} [comm_ring R] [ring S] (f : R →+* S)
    {x : R} : ring_hom.is_integral_elem f (coe_fn f x) :=
  sorry

theorem is_integral_algebra_map {R : Type u_1} {A : Type u_3} [comm_ring R] [ring A] [algebra R A]
    {x : R} : is_integral R (coe_fn (algebra_map R A) x) :=
  ring_hom.is_integral_map (algebra_map R A)

theorem is_integral_of_noetherian {R : Type u_1} {A : Type u_3} [comm_ring R] [ring A] [algebra R A]
    (H : is_noetherian R A) (x : A) : is_integral R x :=
  sorry

theorem is_integral_of_submodule_noetherian {R : Type u_1} {A : Type u_3} [comm_ring R] [ring A]
    [algebra R A] (S : subalgebra R A) (H : is_noetherian R ↥↑S) (x : A) (hx : x ∈ S) :
    is_integral R x :=
  sorry

theorem is_integral_alg_hom {R : Type u_1} {A : Type u_2} {B : Type u_3} [comm_ring R] [comm_ring A]
    [comm_ring B] [algebra R A] [algebra R B] (f : alg_hom R A B) {x : A} (hx : is_integral R x) :
    is_integral R (coe_fn f x) :=
  sorry

theorem is_integral_of_is_scalar_tower {R : Type u_1} {A : Type u_2} {B : Type u_3} [comm_ring R]
    [comm_ring A] [comm_ring B] [algebra R A] [algebra R B] [algebra A B] [is_scalar_tower R A B]
    (x : B) (hx : is_integral R x) : is_integral A x :=
  sorry

theorem is_integral_of_subring {R : Type u_1} {A : Type u_2} [comm_ring R] [comm_ring A]
    [algebra R A] {x : A} (T : set R) [is_subring T] (hx : is_integral (↥T) x) : is_integral R x :=
  is_integral_of_is_scalar_tower x hx

theorem is_integral_algebra_map_iff {R : Type u_1} {A : Type u_2} {B : Type u_3} [comm_ring R]
    [comm_ring A] [comm_ring B] [algebra R A] [algebra R B] [algebra A B] [is_scalar_tower R A B]
    {x : A} (hAB : function.injective ⇑(algebra_map A B)) :
    is_integral R (coe_fn (algebra_map A B) x) ↔ is_integral R x :=
  sorry

theorem is_integral_iff_is_integral_closure_finite {R : Type u_1} {A : Type u_2} [comm_ring R]
    [comm_ring A] [algebra R A] {r : A} :
    is_integral R r ↔ ∃ (s : set R), set.finite s ∧ is_integral (↥(ring.closure s)) r :=
  sorry

theorem fg_adjoin_singleton_of_integral {R : Type u_1} {A : Type u_2} [comm_ring R] [comm_ring A]
    [algebra R A] (x : A) (hx : is_integral R x) : submodule.fg ↑(algebra.adjoin R (singleton x)) :=
  sorry

theorem fg_adjoin_of_finite {R : Type u_1} {A : Type u_2} [comm_ring R] [comm_ring A] [algebra R A]
    {s : set A} (hfs : set.finite s) (his : ∀ (x : A), x ∈ s → is_integral R x) :
    submodule.fg ↑(algebra.adjoin R s) :=
  sorry

theorem is_integral_of_mem_of_fg {R : Type u_1} {A : Type u_2} [comm_ring R] [comm_ring A]
    [algebra R A] (S : subalgebra R A) (HS : submodule.fg ↑S) (x : A) (hx : x ∈ S) :
    is_integral R x :=
  sorry

theorem ring_hom.is_integral_of_mem_closure {R : Type u_1} {S : Type u_4} [comm_ring R]
    [comm_ring S] (f : R →+* S) {x : S} {y : S} {z : S} (hx : ring_hom.is_integral_elem f x)
    (hy : ring_hom.is_integral_elem f y) (hz : z ∈ ring.closure (insert x (singleton y))) :
    ring_hom.is_integral_elem f z :=
  sorry

theorem is_integral_of_mem_closure {R : Type u_1} {A : Type u_2} [comm_ring R] [comm_ring A]
    [algebra R A] {x : A} {y : A} {z : A} (hx : is_integral R x) (hy : is_integral R y)
    (hz : z ∈ ring.closure (insert x (singleton y))) : is_integral R z :=
  ring_hom.is_integral_of_mem_closure (algebra_map R A) hx hy hz

theorem ring_hom.is_integral_zero {R : Type u_1} {S : Type u_4} [comm_ring R] [comm_ring S]
    (f : R →+* S) : ring_hom.is_integral_elem f 0 :=
  ring_hom.map_zero f ▸ ring_hom.is_integral_map f

theorem is_integral_zero {R : Type u_1} {A : Type u_2} [comm_ring R] [comm_ring A] [algebra R A] :
    is_integral R 0 :=
  ring_hom.is_integral_zero (algebra_map R A)

theorem ring_hom.is_integral_one {R : Type u_1} {S : Type u_4} [comm_ring R] [comm_ring S]
    (f : R →+* S) : ring_hom.is_integral_elem f 1 :=
  ring_hom.map_one f ▸ ring_hom.is_integral_map f

theorem is_integral_one {R : Type u_1} {A : Type u_2} [comm_ring R] [comm_ring A] [algebra R A] :
    is_integral R 1 :=
  ring_hom.is_integral_one (algebra_map R A)

theorem ring_hom.is_integral_add {R : Type u_1} {S : Type u_4} [comm_ring R] [comm_ring S]
    (f : R →+* S) {x : S} {y : S} (hx : ring_hom.is_integral_elem f x)
    (hy : ring_hom.is_integral_elem f y) : ring_hom.is_integral_elem f (x + y) :=
  ring_hom.is_integral_of_mem_closure f hx hy
    (is_add_submonoid.add_mem (ring.subset_closure (Or.inl rfl)) (ring.subset_closure (Or.inr rfl)))

theorem is_integral_add {R : Type u_1} {A : Type u_2} [comm_ring R] [comm_ring A] [algebra R A]
    {x : A} {y : A} (hx : is_integral R x) (hy : is_integral R y) : is_integral R (x + y) :=
  ring_hom.is_integral_add (algebra_map R A) hx hy

theorem ring_hom.is_integral_neg {R : Type u_1} {S : Type u_4} [comm_ring R] [comm_ring S]
    (f : R →+* S) {x : S} (hx : ring_hom.is_integral_elem f x) : ring_hom.is_integral_elem f (-x) :=
  ring_hom.is_integral_of_mem_closure f hx hx
    (is_add_subgroup.neg_mem (ring.subset_closure (Or.inl rfl)))

theorem is_integral_neg {R : Type u_1} {A : Type u_2} [comm_ring R] [comm_ring A] [algebra R A]
    {x : A} (hx : is_integral R x) : is_integral R (-x) :=
  ring_hom.is_integral_neg (algebra_map R A) hx

theorem ring_hom.is_integral_sub {R : Type u_1} {S : Type u_4} [comm_ring R] [comm_ring S]
    (f : R →+* S) {x : S} {y : S} (hx : ring_hom.is_integral_elem f x)
    (hy : ring_hom.is_integral_elem f y) : ring_hom.is_integral_elem f (x - y) :=
  sorry

theorem is_integral_sub {R : Type u_1} {A : Type u_2} [comm_ring R] [comm_ring A] [algebra R A]
    {x : A} {y : A} (hx : is_integral R x) (hy : is_integral R y) : is_integral R (x - y) :=
  ring_hom.is_integral_sub (algebra_map R A) hx hy

theorem ring_hom.is_integral_mul {R : Type u_1} {S : Type u_4} [comm_ring R] [comm_ring S]
    (f : R →+* S) {x : S} {y : S} (hx : ring_hom.is_integral_elem f x)
    (hy : ring_hom.is_integral_elem f y) : ring_hom.is_integral_elem f (x * y) :=
  ring_hom.is_integral_of_mem_closure f hx hy
    (is_submonoid.mul_mem (ring.subset_closure (Or.inl rfl)) (ring.subset_closure (Or.inr rfl)))

theorem is_integral_mul {R : Type u_1} {A : Type u_2} [comm_ring R] [comm_ring A] [algebra R A]
    {x : A} {y : A} (hx : is_integral R x) (hy : is_integral R y) : is_integral R (x * y) :=
  ring_hom.is_integral_mul (algebra_map R A) hx hy

/-- The integral closure of R in an R-algebra A. -/
def integral_closure (R : Type u_1) (A : Type u_2) [comm_ring R] [comm_ring A] [algebra R A] :
    subalgebra R A :=
  subalgebra.mk (set_of fun (r : A) => is_integral R r) is_integral_one sorry is_integral_zero sorry
    sorry

theorem mem_integral_closure_iff_mem_fg (R : Type u_1) (A : Type u_2) [comm_ring R] [comm_ring A]
    [algebra R A] {r : A} :
    r ∈ integral_closure R A ↔ ∃ (M : subalgebra R A), submodule.fg ↑M ∧ r ∈ M :=
  sorry

/-- Mapping an integral closure along an `alg_equiv` gives the integral closure. -/
theorem integral_closure_map_alg_equiv {R : Type u_1} {A : Type u_2} {B : Type u_3} [comm_ring R]
    [comm_ring A] [comm_ring B] [algebra R A] [algebra R B] (f : alg_equiv R A B) :
    subalgebra.map (integral_closure R A) ↑f = integral_closure R B :=
  sorry

theorem integral_closure.is_integral {R : Type u_1} {A : Type u_2} [comm_ring R] [comm_ring A]
    [algebra R A] (x : ↥(integral_closure R A)) : is_integral R x :=
  sorry

theorem ring_hom.is_integral_of_is_integral_mul_unit {R : Type u_1} {S : Type u_4} [comm_ring R]
    [comm_ring S] (f : R →+* S) (x : S) (y : S) (r : R) (hr : coe_fn f r * y = 1)
    (hx : ring_hom.is_integral_elem f (x * y)) : ring_hom.is_integral_elem f x :=
  sorry

theorem is_integral_of_is_integral_mul_unit {R : Type u_1} {A : Type u_2} [comm_ring R]
    [comm_ring A] [algebra R A] {x : A} {y : A} {r : R} (hr : coe_fn (algebra_map R A) r * y = 1)
    (hx : is_integral R (x * y)) : is_integral R x :=
  ring_hom.is_integral_of_is_integral_mul_unit (algebra_map R A) x y r hr hx

/-- Generalization of `is_integral_of_mem_closure` bootstrapped up from that lemma -/
theorem is_integral_of_mem_closure' {R : Type u_1} {A : Type u_2} [comm_ring R] [comm_ring A]
    [algebra R A] (G : set A) (hG : ∀ (x : A), x ∈ G → is_integral R x) (x : A)
    (H : x ∈ subring.closure G) : is_integral R x :=
  subring.closure_induction hx hG is_integral_zero is_integral_one
    (fun (_x _x_1 : A) => is_integral_add) (fun (_x : A) => is_integral_neg)
    fun (_x _x_1 : A) => is_integral_mul

theorem is_integral_of_mem_closure'' {R : Type u_1} [comm_ring R] {S : Type u_2} [comm_ring S]
    {f : R →+* S} (G : set S) (hG : ∀ (x : S), x ∈ G → ring_hom.is_integral_elem f x) (x : S)
    (H : x ∈ subring.closure G) : ring_hom.is_integral_elem f x :=
  is_integral_of_mem_closure' G hG x hx

theorem is_integral_trans_aux {R : Type u_1} {A : Type u_2} {B : Type u_3} [comm_ring R]
    [comm_ring A] [comm_ring B] [algebra A B] [algebra R B] (x : B) {p : polynomial A}
    (pmonic : polynomial.monic p) (hp : coe_fn (polynomial.aeval x) p = 0) :
    is_integral (↥(algebra.adjoin R ↑(finsupp.frange (polynomial.map (algebra_map A B) p)))) x :=
  sorry

/-- If A is an R-algebra all of whose elements are integral over R,
and x is an element of an A-algebra that is integral over A, then x is integral over R.-/
theorem is_integral_trans {R : Type u_1} {A : Type u_2} {B : Type u_3} [comm_ring R] [comm_ring A]
    [comm_ring B] [algebra A B] [algebra R B] [algebra R A] [is_scalar_tower R A B]
    (A_int : algebra.is_integral R A) (x : B) (hx : is_integral A x) : is_integral R x :=
  sorry

/-- If A is an R-algebra all of whose elements are integral over R,
and B is an A-algebra all of whose elements are integral over A,
then all elements of B are integral over R.-/
theorem algebra.is_integral_trans {R : Type u_1} {A : Type u_2} {B : Type u_3} [comm_ring R]
    [comm_ring A] [comm_ring B] [algebra A B] [algebra R B] [algebra R A] [is_scalar_tower R A B]
    (hA : algebra.is_integral R A) (hB : algebra.is_integral A B) : algebra.is_integral R B :=
  fun (x : B) => is_integral_trans hA x (hB x)

theorem ring_hom.is_integral_trans {R : Type u_1} {S : Type u_4} {T : Type u_5} [comm_ring R]
    [comm_ring S] [comm_ring T] (f : R →+* S) (g : S →+* T) (hf : ring_hom.is_integral f)
    (hg : ring_hom.is_integral g) : ring_hom.is_integral (ring_hom.comp g f) :=
  algebra.is_integral_trans hf hg

theorem ring_hom.is_integral_of_surjective {R : Type u_1} {S : Type u_4} [comm_ring R] [comm_ring S]
    (f : R →+* S) (hf : function.surjective ⇑f) : ring_hom.is_integral f :=
  fun (x : S) =>
    Exists.rec_on (hf x) fun (y : R) (hy : coe_fn f y = x) => hy ▸ ring_hom.is_integral_map f

theorem is_integral_of_surjective {R : Type u_1} {A : Type u_2} [comm_ring R] [comm_ring A]
    [algebra R A] (h : function.surjective ⇑(algebra_map R A)) : algebra.is_integral R A :=
  ring_hom.is_integral_of_surjective (algebra_map R A) h

/-- If `R → A → B` is an algebra tower with `A → B` injective,
then if the entire tower is an integral extension so is `R → A` -/
theorem is_integral_tower_bot_of_is_integral {R : Type u_1} {A : Type u_2} {B : Type u_3}
    [comm_ring R] [comm_ring A] [comm_ring B] [algebra A B] [algebra R B] [algebra R A]
    [is_scalar_tower R A B] (H : function.injective ⇑(algebra_map A B)) {x : A}
    (h : is_integral R (coe_fn (algebra_map A B) x)) : is_integral R x :=
  sorry

theorem ring_hom.is_integral_tower_bot_of_is_integral {R : Type u_1} {S : Type u_4} {T : Type u_5}
    [comm_ring R] [comm_ring S] [comm_ring T] (f : R →+* S) (g : S →+* T)
    (hg : function.injective ⇑g) (hfg : ring_hom.is_integral (ring_hom.comp g f)) :
    ring_hom.is_integral f :=
  fun (x : S) => is_integral_tower_bot_of_is_integral hg (hfg (coe_fn g x))

theorem is_integral_tower_bot_of_is_integral_field {R : Type u_1} {A : Type u_2} {B : Type u_3}
    [comm_ring R] [field A] [comm_ring B] [nontrivial B] [algebra R A] [algebra A B] [algebra R B]
    [is_scalar_tower R A B] {x : A} (h : is_integral R (coe_fn (algebra_map A B) x)) :
    is_integral R x :=
  is_integral_tower_bot_of_is_integral (ring_hom.injective (algebra_map A B)) h

theorem ring_hom.is_integral_elem_of_is_integral_elem_comp {R : Type u_1} {S : Type u_4}
    {T : Type u_5} [comm_ring R] [comm_ring S] [comm_ring T] (f : R →+* S) (g : S →+* T) {x : T}
    (h : ring_hom.is_integral_elem (ring_hom.comp g f) x) : ring_hom.is_integral_elem g x :=
  sorry

theorem ring_hom.is_integral_tower_top_of_is_integral {R : Type u_1} {S : Type u_4} {T : Type u_5}
    [comm_ring R] [comm_ring S] [comm_ring T] (f : R →+* S) (g : S →+* T)
    (h : ring_hom.is_integral (ring_hom.comp g f)) : ring_hom.is_integral g :=
  fun (x : T) => ring_hom.is_integral_elem_of_is_integral_elem_comp f g (h x)

/-- If `R → A → B` is an algebra tower,
then if the entire tower is an integral extension so is `A → B`. -/
theorem is_integral_tower_top_of_is_integral {R : Type u_1} {A : Type u_2} {B : Type u_3}
    [comm_ring R] [comm_ring A] [comm_ring B] [algebra A B] [algebra R B] [algebra R A]
    [is_scalar_tower R A B] {x : B} (h : is_integral R x) : is_integral A x :=
  sorry

theorem ring_hom.is_integral_quotient_of_is_integral {R : Type u_1} {S : Type u_4} [comm_ring R]
    [comm_ring S] (f : R →+* S) {I : ideal S} (hf : ring_hom.is_integral f) :
    ring_hom.is_integral (ideal.quotient_map I f le_rfl) :=
  sorry

theorem is_integral_quotient_of_is_integral {R : Type u_1} {A : Type u_2} [comm_ring R]
    [comm_ring A] [algebra R A] {I : ideal A} (hRA : algebra.is_integral R A) :
    algebra.is_integral (ideal.quotient (ideal.comap (algebra_map R A) I)) (ideal.quotient I) :=
  ring_hom.is_integral_quotient_of_is_integral (algebra_map R A) hRA

theorem is_integral_quotient_map_iff {R : Type u_1} {S : Type u_4} [comm_ring R] [comm_ring S]
    (f : R →+* S) {I : ideal S} :
    ring_hom.is_integral (ideal.quotient_map I f le_rfl) ↔
        ring_hom.is_integral (ring_hom.comp (ideal.quotient.mk I) f) :=
  sorry

/-- If the integral extension `R → S` is injective, and `S` is a field, then `R` is also a field. -/
theorem is_field_of_is_integral_of_is_field {R : Type u_1} {S : Type u_2} [integral_domain R]
    [integral_domain S] [algebra R S] (H : algebra.is_integral R S)
    (hRS : function.injective ⇑(algebra_map R S)) (hS : is_field S) : is_field R :=
  sorry

theorem integral_closure_idem {R : Type u_1} {A : Type u_2} [comm_ring R] [comm_ring A]
    [algebra R A] : integral_closure (↥↑(integral_closure R A)) A = ⊥ :=
  sorry

protected instance integral_closure.integral_domain {R : Type u_1} {S : Type u_2} [comm_ring R]
    [integral_domain S] [algebra R S] : integral_domain ↥(integral_closure R S) :=
  integral_domain.mk comm_ring.add sorry comm_ring.zero sorry sorry comm_ring.neg comm_ring.sub
    sorry sorry comm_ring.mul sorry comm_ring.one sorry sorry sorry sorry sorry sorry sorry

end Mathlib