/-
Copyright (c) 2018 Chris Hughes. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Chris Hughes, Johannes Hölzl, Scott Morrison, Jens Wagemaker
-/
import data.polynomial.inductions
import data.polynomial.monic
import ring_theory.multiplicity

/-!
# Division of univariate polynomials

The main defs are `div_by_monic` and `mod_by_monic`.
The compatibility between these is given by `mod_by_monic_add_div`.
We also define `root_multiplicity`.
-/

noncomputable theory
open_locale classical big_operators

open finset
namespace polynomial
universes u v w z
variables {R : Type u} {S : Type v} {T : Type w} {A : Type z} {a b : R} {n : ℕ}

section comm_semiring
variables [comm_semiring R]

theorem X_dvd_iff {α : Type u} [comm_semiring α] {f : polynomial α} : X ∣ f ↔ f.coeff 0 = 0 :=
⟨λ ⟨g, hfg⟩, by rw [hfg, mul_comm, coeff_mul_X_zero],
λ hf, ⟨f.div_X, by rw [mul_comm, ← add_zero (f.div_X * X), ← C_0, ← hf, div_X_mul_X_add]⟩⟩

end comm_semiring

section comm_semiring

variables [comm_semiring R] {p q : polynomial R}

lemma multiplicity_finite_of_degree_pos_of_monic (hp : (0 : with_bot ℕ) < degree p)
  (hmp : monic p) (hq : q ≠ 0) : multiplicity.finite p q :=
have zn0 : (0 : R) ≠ 1, from λ h, by haveI := subsingleton_of_zero_eq_one h;
  exact hq (subsingleton.elim _ _),
⟨nat_degree q, λ ⟨r, hr⟩,
  have hp0 : p ≠ 0, from λ hp0, by simp [hp0] at hp; contradiction,
  have hr0 : r ≠ 0, from λ hr0, by simp * at *,
  have hpn1 : leading_coeff p ^ (nat_degree q + 1) = 1,
    by simp [show _ = _, from hmp],
  have hpn0' : leading_coeff p ^ (nat_degree q + 1) ≠ 0,
    from hpn1.symm ▸ zn0.symm,
  have hpnr0 : leading_coeff (p ^ (nat_degree q + 1)) * leading_coeff r ≠ 0,
    by simp only [leading_coeff_pow' hpn0', leading_coeff_eq_zero, hpn1,
      one_pow, one_mul, ne.def, hr0]; simp,
  have hnp : 0 < nat_degree p,
    by rw [← with_bot.coe_lt_coe, ← degree_eq_nat_degree hp0];
    exact hp,
  begin
    have := congr_arg nat_degree hr,
    rw [nat_degree_mul' hpnr0,  nat_degree_pow' hpn0', add_mul, add_assoc] at this,
    exact ne_of_lt (lt_add_of_le_of_pos (le_mul_of_one_le_right (nat.zero_le _) hnp)
      (add_pos_of_pos_of_nonneg (by rwa one_mul) (nat.zero_le _))) this
  end⟩

end comm_semiring

section ring
variables [ring R] {p q : polynomial R}

lemma div_wf_lemma (h : degree q ≤ degree p ∧ p ≠ 0) (hq : monic q) :
  degree (p - C (leading_coeff p) * X ^ (nat_degree p - nat_degree q) * q) < degree p :=
have hp : leading_coeff p ≠ 0 := mt leading_coeff_eq_zero.1 h.2,
  have hq0 : q ≠ 0 := hq.ne_zero_of_polynomial_ne h.2,
  have hlt : nat_degree q ≤ nat_degree p := with_bot.coe_le_coe.1
    (by rw [← degree_eq_nat_degree h.2, ← degree_eq_nat_degree hq0];
    exact h.1),
  degree_sub_lt
  (by rw [hq.degree_mul, degree_C_mul_X_pow _ hp, degree_eq_nat_degree h.2,
      degree_eq_nat_degree hq0, ← with_bot.coe_add, tsub_add_cancel_of_le hlt])
  h.2
  (by rw [leading_coeff_mul_monic hq, leading_coeff_mul_X_pow, leading_coeff_C])

/-- See `div_by_monic`. -/
noncomputable def div_mod_by_monic_aux : Π (p : polynomial R) {q : polynomial R},
  monic q → polynomial R × polynomial R
| p := λ q hq, if h : degree q ≤ degree p ∧ p ≠ 0 then
  let z := C (leading_coeff p) * X^(nat_degree p - nat_degree q)  in
  have wf : _ := div_wf_lemma h hq,
  let dm := div_mod_by_monic_aux (p - z * q) hq in
  ⟨z + dm.1, dm.2⟩
  else ⟨0, p⟩
using_well_founded {dec_tac := tactic.assumption}

/-- `div_by_monic` gives the quotient of `p` by a monic polynomial `q`. -/
def div_by_monic (p q : polynomial R) : polynomial R :=
if hq : monic q then (div_mod_by_monic_aux p hq).1 else 0

/-- `mod_by_monic` gives the remainder of `p` by a monic polynomial `q`. -/
def mod_by_monic (p q : polynomial R) : polynomial R :=
if hq : monic q then (div_mod_by_monic_aux p hq).2 else p

infixl  ` /ₘ ` : 70 := div_by_monic

infixl ` %ₘ ` : 70 := mod_by_monic

lemma degree_mod_by_monic_lt [nontrivial R] : ∀ (p : polynomial R) {q : polynomial R}
  (hq : monic q), degree (p %ₘ q) < degree q
| p := λ q hq,
if h : degree q ≤ degree p ∧ p ≠ 0 then
  have wf : _ := div_wf_lemma ⟨h.1, h.2⟩ hq,
  have degree ((p - C (leading_coeff p) * X ^ (nat_degree p - nat_degree q) * q) %ₘ q) < degree q :=
      degree_mod_by_monic_lt (p - C (leading_coeff p) * X ^ (nat_degree p - nat_degree q) * q)
      hq,
  begin
    unfold mod_by_monic at this ⊢,
    unfold div_mod_by_monic_aux,
    rw dif_pos hq at this ⊢,
    rw if_pos h,
    exact this
  end
else
  or.cases_on (not_and_distrib.1 h) begin
    unfold mod_by_monic div_mod_by_monic_aux,
    rw [dif_pos hq, if_neg h],
    exact lt_of_not_ge,
  end
  begin
    assume hp,
    unfold mod_by_monic div_mod_by_monic_aux,
    rw [dif_pos hq, if_neg h, not_not.1 hp],
    exact lt_of_le_of_ne bot_le
      (ne.symm (mt degree_eq_bot.1 hq.ne_zero)),
  end
using_well_founded {dec_tac := tactic.assumption}

@[simp] lemma zero_mod_by_monic (p : polynomial R) : 0 %ₘ p = 0 :=
begin
  unfold mod_by_monic div_mod_by_monic_aux,
  by_cases hp : monic p,
  { rw [dif_pos hp, if_neg (mt and.right (not_not_intro rfl))] },
  { rw [dif_neg hp] }
end

@[simp] lemma zero_div_by_monic (p : polynomial R) : 0 /ₘ p = 0 :=
begin
  unfold div_by_monic div_mod_by_monic_aux,
  by_cases hp : monic p,
  { rw [dif_pos hp, if_neg (mt and.right (not_not_intro rfl))] },
  { rw [dif_neg hp] }
end

@[simp] lemma mod_by_monic_zero (p : polynomial R) : p %ₘ 0 = p :=
if h : monic (0 : polynomial R) then (subsingleton_of_monic_zero h).1 _ _ else
by unfold mod_by_monic div_mod_by_monic_aux; rw dif_neg h

@[simp] lemma div_by_monic_zero (p : polynomial R) : p /ₘ 0 = 0 :=
if h : monic (0 : polynomial R) then (subsingleton_of_monic_zero h).1 _ _ else
by unfold div_by_monic div_mod_by_monic_aux; rw dif_neg h

lemma div_by_monic_eq_of_not_monic (p : polynomial R) (hq : ¬monic q) : p /ₘ q = 0 := dif_neg hq

lemma mod_by_monic_eq_of_not_monic (p : polynomial R) (hq : ¬monic q) : p %ₘ q = p := dif_neg hq

lemma mod_by_monic_eq_self_iff [nontrivial R] (hq : monic q) : p %ₘ q = p ↔ degree p < degree q :=
⟨λ h, h ▸ degree_mod_by_monic_lt _ hq,
λ h, have ¬ degree q ≤ degree p := not_le_of_gt h,
  by unfold mod_by_monic div_mod_by_monic_aux; rw [dif_pos hq, if_neg (mt and.left this)]⟩

theorem degree_mod_by_monic_le (p : polynomial R) {q : polynomial R}
  (hq : monic q) : degree (p %ₘ q) ≤ degree q :=
by { nontriviality R, exact (degree_mod_by_monic_lt _ hq).le }

end ring

section comm_ring
variables [comm_ring R] {p q : polynomial R}

lemma mod_by_monic_eq_sub_mul_div : ∀ (p : polynomial R) {q : polynomial R} (hq : monic q),
  p %ₘ q = p - q * (p /ₘ q)
| p := λ q hq,
  if h : degree q ≤ degree p ∧ p ≠ 0 then
    have wf : _ := div_wf_lemma h hq,
    have ih : _ := mod_by_monic_eq_sub_mul_div
      (p - C (leading_coeff p) * X ^ (nat_degree p - nat_degree q) * q) hq,
    begin
      unfold mod_by_monic div_by_monic div_mod_by_monic_aux,
      rw [dif_pos hq, if_pos h],
      rw [mod_by_monic, dif_pos hq] at ih,
      refine ih.trans _,
      unfold div_by_monic,
      rw [dif_pos hq, dif_pos hq, if_pos h, mul_add, sub_add_eq_sub_sub, mul_comm]
    end
  else
    begin
      unfold mod_by_monic div_by_monic div_mod_by_monic_aux,
      rw [dif_pos hq, if_neg h, dif_pos hq, if_neg h, mul_zero, sub_zero]
    end
using_well_founded {dec_tac := tactic.assumption}

lemma mod_by_monic_add_div (p : polynomial R) {q : polynomial R} (hq : monic q) :
  p %ₘ q + q * (p /ₘ q) = p := eq_sub_iff_add_eq.1 (mod_by_monic_eq_sub_mul_div p hq)

lemma div_by_monic_eq_zero_iff [nontrivial R] (hq : monic q) : p /ₘ q = 0 ↔ degree p < degree q :=
⟨λ h, by have := mod_by_monic_add_div p hq;
  rwa [h, mul_zero, add_zero, mod_by_monic_eq_self_iff hq] at this,
λ h, have ¬ degree q ≤ degree p := not_le_of_gt h,
  by unfold div_by_monic div_mod_by_monic_aux; rw [dif_pos hq, if_neg (mt and.left this)]⟩

lemma degree_add_div_by_monic (hq : monic q) (h : degree q ≤ degree p) :
  degree q + degree (p /ₘ q) = degree p :=
begin
  nontriviality R,
  have hdiv0 : p /ₘ q ≠ 0 := by rwa [(≠), div_by_monic_eq_zero_iff hq, not_lt],
  have hlc : leading_coeff q * leading_coeff (p /ₘ q) ≠ 0 :=
    by rwa [monic.def.1 hq, one_mul, (≠), leading_coeff_eq_zero],
  have hmod : degree (p %ₘ q) < degree (q * (p /ₘ q)) :=
    calc degree (p %ₘ q) < degree q : degree_mod_by_monic_lt _ hq
    ... ≤ _ : by rw [degree_mul' hlc, degree_eq_nat_degree hq.ne_zero,
        degree_eq_nat_degree hdiv0, ← with_bot.coe_add, with_bot.coe_le_coe];
      exact nat.le_add_right _ _,
  calc degree q + degree (p /ₘ q) = degree (q * (p /ₘ q)) : eq.symm (degree_mul' hlc)
  ... = degree (p %ₘ q + q * (p /ₘ q)) : (degree_add_eq_right_of_degree_lt hmod).symm
  ... = _ : congr_arg _ (mod_by_monic_add_div _ hq)
end

lemma degree_div_by_monic_le (p q : polynomial R) : degree (p /ₘ q) ≤ degree p :=
if hp0 : p = 0 then by simp only [hp0, zero_div_by_monic, le_refl]
else if hq : monic q then
  if h : degree q ≤ degree p
  then by haveI := nontrivial.of_polynomial_ne hp0;
    rw [← degree_add_div_by_monic hq h, degree_eq_nat_degree hq.ne_zero,
      degree_eq_nat_degree (mt (div_by_monic_eq_zero_iff hq).1 (not_lt.2 h))];
    exact with_bot.coe_le_coe.2 (nat.le_add_left _ _)
  else
    by unfold div_by_monic div_mod_by_monic_aux;
      simp only [dif_pos hq, h, false_and, if_false, degree_zero, bot_le]
else (div_by_monic_eq_of_not_monic p hq).symm ▸ bot_le

lemma degree_div_by_monic_lt (p : polynomial R) {q : polynomial R} (hq : monic q)
  (hp0 : p ≠ 0) (h0q : 0 < degree q) : degree (p /ₘ q) < degree p :=
if hpq : degree p < degree q
then begin
  haveI := nontrivial.of_polynomial_ne hp0,
  rw [(div_by_monic_eq_zero_iff hq).2 hpq, degree_eq_nat_degree hp0],
  exact with_bot.bot_lt_coe _
end
else begin
  haveI := nontrivial.of_polynomial_ne hp0,
  rw [← degree_add_div_by_monic hq (not_lt.1 hpq), degree_eq_nat_degree hq.ne_zero,
        degree_eq_nat_degree (mt (div_by_monic_eq_zero_iff hq).1 hpq)],
  exact with_bot.coe_lt_coe.2 (nat.lt_add_of_pos_left
    (with_bot.coe_lt_coe.1 $ (degree_eq_nat_degree hq.ne_zero) ▸ h0q))
end

theorem nat_degree_div_by_monic {R : Type u} [comm_ring R] (f : polynomial R) {g : polynomial R}
  (hg : g.monic) : nat_degree (f /ₘ g) = nat_degree f - nat_degree g :=
begin
  by_cases h01 : (0 : R) = 1,
  { haveI := subsingleton_of_zero_eq_one h01,
    rw [subsingleton.elim (f /ₘ g) 0, subsingleton.elim f 0, subsingleton.elim g 0,
        nat_degree_zero] },
  haveI : nontrivial R := ⟨⟨0, 1, h01⟩⟩,
  by_cases hfg : f /ₘ g = 0,
  { rw [hfg, nat_degree_zero], rw div_by_monic_eq_zero_iff hg at hfg,
    rw tsub_eq_zero_iff_le.mpr (nat_degree_le_nat_degree $ le_of_lt hfg) },
  have hgf := hfg, rw div_by_monic_eq_zero_iff hg at hgf, push_neg at hgf,
  have := degree_add_div_by_monic hg hgf,
  have hf : f ≠ 0, { intro hf, apply hfg, rw [hf, zero_div_by_monic] },
  rw [degree_eq_nat_degree hf, degree_eq_nat_degree hg.ne_zero, degree_eq_nat_degree hfg,
      ← with_bot.coe_add, with_bot.coe_eq_coe] at this,
  rw [← this, add_tsub_cancel_left]
end

lemma div_mod_by_monic_unique {f g} (q r : polynomial R) (hg : monic g)
  (h : r + g * q = f ∧ degree r < degree g) : f /ₘ g = q ∧ f %ₘ g = r :=
begin
  nontriviality R,
  have h₁ : r - f %ₘ g = -g * (q - f /ₘ g),
    from eq_of_sub_eq_zero
      (by rw [← sub_eq_zero_of_eq (h.1.trans (mod_by_monic_add_div f hg).symm)];
        simp [mul_add, mul_comm, sub_eq_add_neg, add_comm, add_left_comm, add_assoc]),
  have h₂ : degree (r - f %ₘ g) = degree (g * (q - f /ₘ g)),
    by simp [h₁],
  have h₄ : degree (r - f %ₘ g) < degree g,
    from calc degree (r - f %ₘ g) ≤ max (degree r) (degree (f %ₘ g)) :
      degree_sub_le _ _
    ... < degree g : max_lt_iff.2 ⟨h.2, degree_mod_by_monic_lt _ hg⟩,
  have h₅ : q - (f /ₘ g) = 0,
    from by_contradiction
      (λ hqf, not_le_of_gt h₄ $
        calc degree g ≤ degree g + degree (q - f /ₘ g) :
          by erw [degree_eq_nat_degree hg.ne_zero, degree_eq_nat_degree hqf,
              with_bot.coe_le_coe];
            exact nat.le_add_right _ _
        ... = degree (r - f %ₘ g) :
          by rw [h₂, degree_mul']; simpa [monic.def.1 hg]),
  exact ⟨eq.symm $ eq_of_sub_eq_zero h₅,
    eq.symm $ eq_of_sub_eq_zero $ by simpa [h₅] using h₁⟩
end

lemma map_mod_div_by_monic [comm_ring S] (f : R →+* S) (hq : monic q) :
  (p /ₘ q).map f = p.map f /ₘ q.map f ∧ (p %ₘ q).map f = p.map f %ₘ q.map f :=
begin
  nontriviality S,
  haveI : nontrivial R := f.domain_nontrivial,
  have : map f p /ₘ map f q = map f (p /ₘ q) ∧ map f p %ₘ map f q = map f (p %ₘ q),
  { exact (div_mod_by_monic_unique ((p /ₘ q).map f) _ (monic_map f hq)
      ⟨eq.symm $ by rw [← map_mul, ← map_add, mod_by_monic_add_div _ hq],
      calc _ ≤ degree (p %ₘ q) : degree_map_le _ _
      ... < degree q : degree_mod_by_monic_lt _ hq
      ... = _ : eq.symm $ degree_map_eq_of_leading_coeff_ne_zero _
        (by rw [monic.def.1 hq, f.map_one]; exact one_ne_zero)⟩) },
  exact ⟨this.1.symm, this.2.symm⟩
end

lemma map_div_by_monic [comm_ring S] (f : R →+* S) (hq : monic q) :
  (p /ₘ q).map f = p.map f /ₘ q.map f :=
(map_mod_div_by_monic f hq).1

lemma map_mod_by_monic [comm_ring S] (f : R →+* S) (hq : monic q) :
  (p %ₘ q).map f = p.map f %ₘ q.map f :=
(map_mod_div_by_monic f hq).2

lemma dvd_iff_mod_by_monic_eq_zero (hq : monic q) : p %ₘ q = 0 ↔ q ∣ p :=
⟨λ h, by rw [← mod_by_monic_add_div p hq, h, zero_add];
  exact dvd_mul_right _ _,
λ h, begin
  nontriviality R,
  obtain ⟨r, hr⟩ := exists_eq_mul_right_of_dvd h,
  by_contradiction hpq0,
  have hmod : p %ₘ q = q * (r - p /ₘ q),
  { rw [mod_by_monic_eq_sub_mul_div _ hq, mul_sub, ← hr] },
  have : degree (q * (r - p /ₘ q)) < degree q :=
    hmod ▸ degree_mod_by_monic_lt _ hq,
  have hrpq0 : leading_coeff (r - p /ₘ q) ≠ 0 :=
    λ h, hpq0 $ leading_coeff_eq_zero.1
      (by rw [hmod, leading_coeff_eq_zero.1 h, mul_zero, leading_coeff_zero]),
  have hlc : leading_coeff q * leading_coeff (r - p /ₘ q) ≠ 0 :=
    by rwa [monic.def.1 hq, one_mul],
  rw [degree_mul' hlc, degree_eq_nat_degree hq.ne_zero,
      degree_eq_nat_degree (mt leading_coeff_eq_zero.2 hrpq0)] at this,
    exact not_lt_of_ge (nat.le_add_right _ _) (with_bot.some_lt_some.1 this)
end⟩

theorem map_dvd_map [comm_ring S] (f : R →+* S) (hf : function.injective f) {x y : polynomial R}
  (hx : x.monic) : x.map f ∣ y.map f ↔ x ∣ y :=
begin
  rw [← dvd_iff_mod_by_monic_eq_zero hx, ← dvd_iff_mod_by_monic_eq_zero (monic_map f hx),
    ← map_mod_by_monic f hx],
  exact ⟨λ H, map_injective f hf $ by rw [H, map_zero],
  λ H, by rw [H, map_zero]⟩
end

@[simp] lemma mod_by_monic_one (p : polynomial R) : p %ₘ 1 = 0 :=
(dvd_iff_mod_by_monic_eq_zero (by convert monic_one)).2 (one_dvd _)

@[simp] lemma div_by_monic_one (p : polynomial R) : p /ₘ 1 = p :=
by conv_rhs { rw [← mod_by_monic_add_div p monic_one] }; simp

@[simp] lemma mod_by_monic_X_sub_C_eq_C_eval (p : polynomial R) (a : R) :
  p %ₘ (X - C a) = C (p.eval a) :=
begin
  nontriviality R,
  have h : (p %ₘ (X - C a)).eval a = p.eval a,
  { rw [mod_by_monic_eq_sub_mul_div _ (monic_X_sub_C a), eval_sub, eval_mul,
        eval_sub, eval_X, eval_C, sub_self, zero_mul, sub_zero] },
  have : degree (p %ₘ (X - C a)) < 1 :=
    degree_X_sub_C a ▸ degree_mod_by_monic_lt p (monic_X_sub_C a),
  have : degree (p %ₘ (X - C a)) ≤ 0,
  { cases (degree (p %ₘ (X - C a))),
    { exact bot_le },
    { exact with_bot.some_le_some.2 (nat.le_of_lt_succ (with_bot.some_lt_some.1 this)) } },
  rw [eq_C_of_degree_le_zero this, eval_C] at h,
  rw [eq_C_of_degree_le_zero this, h]
end

lemma mul_div_by_monic_eq_iff_is_root : (X - C a) * (p /ₘ (X - C a)) = p ↔ is_root p a :=
⟨λ h, by rw [← h, is_root.def, eval_mul, eval_sub, eval_X, eval_C, sub_self, zero_mul],
λ h : p.eval a = 0,
  by conv {to_rhs, rw ← mod_by_monic_add_div p (monic_X_sub_C a)};
    rw [mod_by_monic_X_sub_C_eq_C_eval, h, C_0, zero_add]⟩

lemma dvd_iff_is_root : (X - C a) ∣ p ↔ is_root p a :=
⟨λ h, by rwa [← dvd_iff_mod_by_monic_eq_zero (monic_X_sub_C _),
    mod_by_monic_X_sub_C_eq_C_eval, ← C_0, C_inj] at h,
  λ h, ⟨(p /ₘ (X - C a)), by rw mul_div_by_monic_eq_iff_is_root.2 h⟩⟩

lemma mod_by_monic_X (p : polynomial R) : p %ₘ X = C (p.eval 0) :=
by rw [← mod_by_monic_X_sub_C_eq_C_eval, C_0, sub_zero]

lemma eval₂_mod_by_monic_eq_self_of_root [comm_ring S] {f : R →+* S}
  {p q : polynomial R} (hq : q.monic) {x : S} (hx : q.eval₂ f x = 0) :
  (p %ₘ q).eval₂ f x = p.eval₂ f x :=
by rw [mod_by_monic_eq_sub_mul_div p hq, eval₂_sub, eval₂_mul, hx, zero_mul, sub_zero]

lemma sum_fin [add_comm_monoid S] (f : ℕ → R → S) (hf : ∀ i, f i 0 = 0)
  {n : ℕ} (hn : p.degree < n) :
  ∑ (i : fin n), f i (p.coeff i) = p.sum f :=
begin
  by_cases hp : p = 0,
  { rw [hp, sum_zero_index, finset.sum_eq_zero], intros i _, exact hf i },
  rw [degree_eq_nat_degree hp, with_bot.coe_lt_coe] at hn,
  calc  ∑ (i : fin n), f i (p.coeff i)
      = ∑ i in finset.range n, f i (p.coeff i) : fin.sum_univ_eq_sum_range (λ i, f i (p.coeff i)) _
  ... = ∑ i in p.support, f i (p.coeff i) : (finset.sum_subset
    (supp_subset_range_nat_degree_succ.trans (finset.range_subset.mpr hn))
    (λ i _ hi, show f i (p.coeff i) = 0, by rw [not_mem_support_iff.mp hi, hf])).symm
  ... = p.sum f : p.sum_def _
end

lemma sum_mod_by_monic_coeff [nontrivial R] (hq : q.monic)
  {n : ℕ} (hn : q.degree ≤ n) :
  ∑ (i : fin n), monomial i ((p %ₘ q).coeff i) = p %ₘ q :=
(sum_fin (λ i c, monomial i c) (by simp)
  ((degree_mod_by_monic_lt _ hq).trans_le hn)).trans
  (sum_monomial_eq _)

section multiplicity
/-- An algorithm for deciding polynomial divisibility.
The algorithm is "compute `p %ₘ q` and compare to `0`". `
See `polynomial.mod_by_monic` for the algorithm that computes `%ₘ`.
 -/
def decidable_dvd_monic (p : polynomial R) (hq : monic q) : decidable (q ∣ p) :=
decidable_of_iff (p %ₘ q = 0) (dvd_iff_mod_by_monic_eq_zero hq)

open_locale classical

lemma multiplicity_X_sub_C_finite (a : R) (h0 : p ≠ 0) :
  multiplicity.finite (X - C a) p :=
multiplicity_finite_of_degree_pos_of_monic
  (have (0 : R) ≠ 1, from (λ h, by haveI := subsingleton_of_zero_eq_one h;
      exact h0 (subsingleton.elim _ _)),
    by haveI : nontrivial R := ⟨⟨0, 1, this⟩⟩; rw degree_X_sub_C; exact dec_trivial)
    (monic_X_sub_C _) h0
/-- The largest power of `X - C a` which divides `p`.
This is computable via the divisibility algorithm `decidable_dvd_monic`. -/
def root_multiplicity (a : R) (p : polynomial R) : ℕ :=
if h0 : p = 0 then 0
else let I : decidable_pred (λ n : ℕ, ¬(X - C a) ^ (n + 1) ∣ p) :=
  λ n, @not.decidable _ (decidable_dvd_monic p (monic_pow (monic_X_sub_C a) (n + 1))) in
by exactI nat.find (multiplicity_X_sub_C_finite a h0)

lemma root_multiplicity_eq_multiplicity (p : polynomial R) (a : R) :
  root_multiplicity a p = if h0 : p = 0 then 0 else
  (multiplicity (X - C a) p).get (multiplicity_X_sub_C_finite a h0) :=
by simp [multiplicity, root_multiplicity, part.dom];
  congr; funext; congr

@[simp] lemma root_multiplicity_zero {x : R} : root_multiplicity x 0 = 0 := dif_pos rfl

lemma root_multiplicity_eq_zero {p : polynomial R} {x : R} (h : ¬ is_root p x) :
  root_multiplicity x p = 0 :=
begin
  rw root_multiplicity_eq_multiplicity,
  split_ifs, { refl },
  rw [← enat.coe_inj, enat.coe_get, multiplicity.multiplicity_eq_zero_of_not_dvd, nat.cast_zero],
  intro hdvd,
  exact h (dvd_iff_is_root.mp hdvd)
end

lemma root_multiplicity_pos {p : polynomial R} (hp : p ≠ 0) {x : R} :
  0 < root_multiplicity x p ↔ is_root p x :=
begin
  rw [← dvd_iff_is_root, root_multiplicity_eq_multiplicity, dif_neg hp,
      ← enat.coe_lt_coe, enat.coe_get],
  exact multiplicity.dvd_iff_multiplicity_pos
end

lemma pow_root_multiplicity_dvd (p : polynomial R) (a : R) :
  (X - C a) ^ root_multiplicity a p ∣ p :=
if h : p = 0 then by simp [h]
else by rw [root_multiplicity_eq_multiplicity, dif_neg h];
  exact multiplicity.pow_multiplicity_dvd _

lemma div_by_monic_mul_pow_root_multiplicity_eq
  (p : polynomial R) (a : R) :
  p /ₘ ((X - C a) ^ root_multiplicity a p) *
  (X - C a) ^ root_multiplicity a p = p :=
have monic ((X - C a) ^ root_multiplicity a p),
  from monic_pow (monic_X_sub_C _) _,
by conv_rhs { rw [← mod_by_monic_add_div p this,
    (dvd_iff_mod_by_monic_eq_zero this).2 (pow_root_multiplicity_dvd _ _)] };
  simp [mul_comm]

lemma eval_div_by_monic_pow_root_multiplicity_ne_zero
  {p : polynomial R} (a : R) (hp : p ≠ 0) :
  eval a (p /ₘ ((X - C a) ^ root_multiplicity a p)) ≠ 0 :=
begin
  haveI : nontrivial R := nontrivial.of_polynomial_ne hp,
  rw [ne.def, ← is_root.def, ← dvd_iff_is_root],
  rintros ⟨q, hq⟩,
  have := div_by_monic_mul_pow_root_multiplicity_eq p a,
  rw [mul_comm, hq, ← mul_assoc, ← pow_succ',
    root_multiplicity_eq_multiplicity, dif_neg hp] at this,
  exact multiplicity.is_greatest'
    (multiplicity_finite_of_degree_pos_of_monic
    (show (0 : with_bot ℕ) < degree (X - C a),
      by rw degree_X_sub_C; exact dec_trivial) (monic_X_sub_C _) hp)
    (nat.lt_succ_self _) (dvd_of_mul_right_eq _ this)
end

end multiplicity
end comm_ring

end polynomial


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

! This file was ported from Lean 3 source module number_theory.liouville.residual
! leanprover-community/mathlib commit 32b08ef840dd25ca2e47e035c5da03ce16d2dc3c
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.NumberTheory.Liouville.Basic
import Mathbin.Topology.MetricSpace.Baire
import Mathbin.Topology.Instances.Irrational

/-!
# Density of Liouville numbers

In this file we prove that the set of Liouville numbers form a dense `Gδ` set. We also prove a
similar statement about irrational numbers.
-/

open Filter

open Filter Set Metric

/- ./././Mathport/Syntax/Translate/Expr.lean:107:6: warning: expanding binder group (a b) -/
theorem setOf_liouville_eq_interᵢ_unionᵢ :
    { x | Liouville x } =
      ⋂ n : ℕ, ⋃ (a : ℤ) (b : ℤ) (hb : 1 < b), ball (a / b) (1 / b ^ n) \ {a / b} :=
  by
  ext x
  simp only [mem_Inter, mem_Union, Liouville, mem_set_of_eq, exists_prop, mem_diff,
    mem_singleton_iff, mem_ball, Real.dist_eq, and_comm']
#align set_of_liouville_eq_Inter_Union setOf_liouville_eq_interᵢ_unionᵢ

theorem isGδ_setOf_liouville : IsGδ { x | Liouville x } :=
  by
  rw [setOf_liouville_eq_interᵢ_unionᵢ]
  refine' isGδ_interᵢ fun n => IsOpen.isGδ _
  refine' isOpen_unionᵢ fun a => isOpen_unionᵢ fun b => isOpen_unionᵢ fun hb => _
  exact is_open_ball.inter is_closed_singleton.is_open_compl
#align is_Gδ_set_of_liouville isGδ_setOf_liouville

/- ./././Mathport/Syntax/Translate/Expr.lean:107:6: warning: expanding binder group (a b) -/
theorem setOf_liouville_eq_irrational_inter_interᵢ_unionᵢ :
    { x | Liouville x } =
      { x | Irrational x } ∩ ⋂ n : ℕ, ⋃ (a : ℤ) (b : ℤ) (hb : 1 < b), ball (a / b) (1 / b ^ n) :=
  by
  refine' subset.antisymm _ _
  · refine' subset_inter (fun x hx => hx.Irrational) _
    rw [setOf_liouville_eq_interᵢ_unionᵢ]
    exact Inter_mono fun n => Union₂_mono fun a b => Union_mono fun hb => diff_subset _ _
  · simp only [inter_Inter, inter_Union, setOf_liouville_eq_interᵢ_unionᵢ]
    refine' Inter_mono fun n => Union₂_mono fun a b => Union_mono fun hb => _
    rw [inter_comm]
    refine' diff_subset_diff subset.rfl (singleton_subset_iff.2 ⟨a / b, _⟩)
    norm_cast
#align set_of_liouville_eq_irrational_inter_Inter_Union setOf_liouville_eq_irrational_inter_interᵢ_unionᵢ

/-- The set of Liouville numbers is a residual set. -/
theorem eventually_residual_liouville : ∀ᶠ x in residual ℝ, Liouville x :=
  by
  rw [Filter.Eventually, setOf_liouville_eq_irrational_inter_interᵢ_unionᵢ]
  refine' eventually_residual_irrational.and _
  refine' eventually_residual.2 ⟨_, _, rat.dense_embedding_coe_real.dense.mono _, subset.rfl⟩
  ·
    exact
      isGδ_interᵢ fun n =>
        IsOpen.isGδ <|
          isOpen_unionᵢ fun a => isOpen_unionᵢ fun b => isOpen_unionᵢ fun hb => is_open_ball
  · rintro _ ⟨r, rfl⟩
    simp only [mem_Inter, mem_Union]
    refine' fun n => ⟨r.num * 2, r.denom * 2, _, _⟩
    · have := Int.ofNat_le.2 r.pos
      rw [Int.ofNat_one] at this
      linarith
    · convert mem_ball_self _ using 2
      · push_cast
        norm_cast
        norm_num
      · refine' one_div_pos.2 (pow_pos (Int.cast_pos.2 _) _)
        exact mul_pos (Int.coe_nat_pos.2 r.pos) zero_lt_two
#align eventually_residual_liouville eventually_residual_liouville

/-- The set of Liouville numbers in dense. -/
theorem dense_liouville : Dense { x | Liouville x } :=
  dense_of_mem_residual eventually_residual_liouville
#align dense_liouville dense_liouville


/-
Copyright 2020 Google LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
 -/

import algebra.ordered_ring
import data.nat.basic
import formal_ml.core

/-
  In order to understand all of the results about natural numbers,
  it is helpful to look at the instances for nat that are defined.
  Specifically, the naturals are:
  1. a canonically ordered commutative semiring
  2. a decidable linear ordered semiring.
-/

lemma lt_antirefl (n:ℕ):(n < n)→ false :=
begin
  apply nat.lt_irrefl,
end

--While this does solve the problem, it is not identical.
lemma lt_succ (n:ℕ):(n < nat.succ n) :=
begin
  apply nat.le_refl,
end

lemma lt_succ_of_lt (m n:ℕ):(m < n) → (m < nat.succ n) :=
begin
  intros a,
  apply nat.lt_succ_of_le,
  apply ((@nat.lt_iff_le_not_le m n).mp a).left,
end

lemma nat_lt_def (n m:ℕ): n < m ↔  (nat.succ n) ≤  m :=
begin
  refl,
end

lemma lt_succ_imp_le (a b:ℕ):a < nat.succ b → a ≤ b :=
begin
  intros a_1,
  rw nat_lt_def at a_1,
  apply nat.le_of_succ_le_succ,
  assumption,
end

lemma nat_fact_pos {n:ℕ}:0 < nat.factorial n :=
begin
  induction n,
  {
    simp,
  },
  {
    simp,
    apply n_ih,
  }
end

lemma nat_minus_cancel_of_le {k n:ℕ}:k≤ n → (n - k) + k = n :=
begin
  rw add_comm,
  apply nat.add_sub_cancel',
end

lemma nat_lt_of_add_lt {a b c:ℕ}:a + b < c → b < c :=
begin
  induction a,
  {
    simp,
  },
  {
    rw nat.succ_add,
    intro A1,
    apply a_ih,
    apply nat.lt_of_succ_lt,
    apply A1,
  }
end

lemma nat_fact_nonzero {n:ℕ}:nat.factorial n ≠ 0 :=
begin
  intro A1,
  have A2:0 < nat.factorial n := nat_fact_pos,
  rw A1 at A2,
  apply lt_irrefl 0 A2,
end

lemma nat_lt_sub_of_add_lt {k n n':ℕ}:n + k < n' → n < n' - k :=
begin
  revert n',
  revert n,
  induction k,
  {
    intros n n' A1,
    rw add_zero at A1,
    simp,
    apply A1,
  },
  {
    intros n n' A1,
    cases n',
    {
      exfalso,
      apply nat.not_lt_zero,
      apply A1,
    },
    {
      rw nat.succ_sub_succ_eq_sub,
      apply k_ih,
      rw nat.add_succ at A1,
      apply nat.lt_of_succ_lt_succ,
      apply A1,
    }
  }
end

lemma nat_zero_lt_of_nonzero {n:ℕ}:(n≠ 0)→ (0 < n) :=
begin
  intro A1,
  cases n,
  {
    exfalso,
    apply A1,
    refl,
  },
  {
    apply nat.zero_lt_succ,
  }
end

--In an earlier version, this was solvable via simp.
lemma nat_succ_one_add  {n:ℕ}:nat.succ n = 1 + n :=
begin
  rw add_comm,
end

lemma nat.le_add {a b:ℕ}:a ≤ a + b :=
begin
  simp,
end

-- mul_left_cancel' is the inspiration here.
-- However, it doesn't exist for nat.
lemma nat.mul_left_cancel_trichotomy {x : ℕ}:x ≠ 0 → ∀ {y z : ℕ},
  (x * y = x * z ↔ y = z)
  ∧ (x * y < x * z ↔ y < z) :=
begin
  induction x,
  {
    intros A1 y z,
    exfalso,
    apply A1,
    refl,
  },
  {
    intros A1 y z,
    rw nat_succ_one_add,
    rw right_distrib,
    rw one_mul,
    rw right_distrib,
    rw one_mul,
    cases x_n,
    {
      rw zero_mul,
      rw zero_mul,
      rw add_zero,
      rw add_zero,
      simp,
    },
    {
      have B1:nat.succ x_n ≠ 0,
      {
        have A2A:0 < nat.succ x_n,
        {
          apply nat.zero_lt_succ,
        },
        intro A2B,
        rw A2B at A2A,
        apply lt_irrefl 0 A2A,
      },
      have A2 := @x_ih B1 y z,
      have B2 := @x_ih B1 z y,
      cases A2 with A3 A4,
      cases B2 with B3 B4,
      have A5:y < z ∨ y = z ∨ z < y := lt_trichotomy y z,
      split;split;intros A6,
      {
        cases A5,
        {
          exfalso,
          have A7:y + nat.succ x_n * y < z + nat.succ x_n * z,
          {
            apply add_lt_add A5 (A4.mpr A5),
          },
          rw A6 at A7,
          apply lt_irrefl _ A7,
        },
        cases A5,
        {
          exact A5,
        },
        {
          exfalso,
          have A7:z + nat.succ x_n * z < y + nat.succ x_n * y,
          {
            apply add_lt_add A5 (B4.mpr A5),
          },
          rw A6 at A7,
          apply lt_irrefl _ A7,
        },
      },
      {
        rw A6,
      },
      {
        cases A5,
        {
          apply A5,
        },
        cases A5,
        {
          rw A5 at A6,
          exfalso,
          apply lt_irrefl _ A6,
        },
        {
          exfalso,
          apply not_lt_of_lt A6,
          apply add_lt_add A5 (B4.mpr A5),
        },
      },
      {
        apply add_lt_add A6,
        apply A4.mpr A6,
      }
    }
  }
end

lemma nat.mul_left_cancel' {x : ℕ}:x ≠ 0 → ∀ {y z : ℕ},
  (x * y = x * z → y = z) :=
begin
  intros A1 y z A2,
  apply (@nat.mul_left_cancel_trichotomy x A1 y z).left.mp A2,
end

lemma nat.mul_eq_zero_iff_eq_zero_or_eq_zero {a b:ℕ}:
  a * b = 0 ↔ (a = 0 ∨ b = 0) :=
begin
  split;intros A1,
  {
    have A2:(a=0) ∨ (a ≠ (0:ℕ)) := eq_or_ne,
    cases A2,
    {
      left,
      apply A2,
    },
    {
      have A1:a * b = a * 0,
      {
        rw mul_zero,
        apply A1,
      },
      right,
      apply nat.mul_left_cancel' A2 A1,
    }
  },
  {
    cases A1;rw A1,
    {
      rw zero_mul,
    },
    {
      rw mul_zero,
    }
  }
end

lemma double_ne {m n:ℕ}:2 * m ≠ 1 + 2 * n :=
begin
  intro A1,
  have A2:=lt_trichotomy m n,
  cases A2,
  {
    have A3:0 + 2 * m < 1 + 2 * n,
    {
      apply add_lt_add,
      apply zero_lt_one,
      apply mul_lt_mul_of_pos_left A2 zero_lt_two,
    },
    rw zero_add at A3,
    rw A1 at A3,
    apply lt_irrefl _ A3,
  },
  cases A2,
  {
    rw A2 at A1,
    have A3:0 + 2 * n < 1 + 2 * n,
    {
      rw add_comm 0 _,
      rw add_comm 1 _,
      apply add_lt_add_left,
      apply zero_lt_one,
    },
    rw zero_add at A3,
    rw ← A1 at A3,
    apply lt_irrefl _ A3,
  },
  {
    have A4:nat.succ n ≤ m,
    {
      apply nat.succ_le_of_lt A2,
    },
    have A5 := lt_or_eq_of_le A4,
    cases A5,
    {
      have A6:2 * nat.succ n < 2 * m,
      {
         apply mul_lt_mul_of_pos_left A5 zero_lt_two,
      },
      rw nat_succ_one_add at A6,
      rw left_distrib at A6,
      rw mul_one at A6,
      have A7:1 + 2 * n < 2 * m,
      {
        apply lt_trans _ A6,
        apply add_lt_add_right,
        apply one_lt_two,
      },
      rw A1 at A7,
      apply lt_irrefl _ A7,
    },
    {
      subst m,
      rw nat_succ_one_add at A1,
      rw left_distrib at A1,
      rw mul_one at A1,
      simp at A1,
      have A2:1 < 2 := one_lt_two,
      rw A1 at A2,
      apply lt_irrefl _ A2,
    }
  },
end

lemma nat.one_le_of_zero_lt {x:ℕ}:0 < x → 1 ≤ x :=
begin
  intro A1,
  apply nat.succ_le_of_lt,
  apply A1,
end

lemma nat.succ_le_iff_lt {m n:ℕ}:m.succ ≤ n ↔ m < n :=
begin
  apply nat.succ_le_iff,
end

lemma nat.zero_of_lt_one {x:ℕ}:x < 1 → x  = 0 :=
begin
  intro A1,
  have B1 := nat.le_pred_of_lt A1,
  simp at B1,
  apply B1,
end

-------------------------------------- Move these to some pow.lean file, or into mathlib ---------------

lemma linear_ordered_semiring.one_le_pow {α:Type*} [linear_ordered_semiring α]
   {a:α} {b:ℕ}:1 ≤ a → 1≤ a^b :=
begin
  induction b,
  {
    simp,
    intro A1,
    apply le_refl _,
  },
  {
    rw pow_succ,
    intro A1,
    have A2 := b_ih A1,
    have A3:1 * a^b_n ≤ a * (a^b_n),
    {
      apply mul_le_mul_of_nonneg_right,
      apply A1,
      apply le_of_lt,
      apply lt_of_lt_of_le zero_lt_one A2,
    },
    apply le_trans _ A3,
    simp [A2],
  },
end

lemma linear_ordered_semiring.pow_monotone
    {α:Type*} [linear_ordered_semiring α] [N:nontrivial α] {a:α} {b c:ℕ}:1 ≤ a → b ≤ c →  a^b ≤ a^c :=
begin
  intros A1 A2,
  rw le_iff_exists_add at A2,
  cases A2 with d A2,
  rw A2,
  rw pow_add,
  rw le_mul_iff_one_le_right,
  apply linear_ordered_semiring.one_le_pow A1,
  apply lt_of_lt_of_le zero_lt_one,
  apply linear_ordered_semiring.one_le_pow A1,
  apply N
end


/-
Copyright (c) 2018 Kenny Lau. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kenny Lau, Chris Hughes, Mario Carneiro
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.algebra.associated
import Mathlib.linear_algebra.basic
import Mathlib.order.zorn
import Mathlib.order.atoms
import Mathlib.PostPort

universes u u_1 v w l u_2 u_3

namespace Mathlib

/-!

# Ideals over a ring

This file defines `ideal R`, the type of ideals over a commutative ring `R`.

## Implementation notes

`ideal R` is implemented using `submodule R R`, where `•` is interpreted as `*`.

## TODO

Support one-sided ideals, and ideals over non-commutative rings.

See `algebra.ring_quot` for quotients of non-commutative rings.
-/

/-- An ideal in a commutative semiring `R` is an additive submonoid `s` such that
`a * b ∈ s` whenever `b ∈ s`. If `R` is a ring, then `s` is an additive subgroup.  -/
def ideal (R : Type u) [comm_semiring R] := submodule R R

namespace ideal

protected theorem zero_mem {α : Type u} [comm_ring α] (I : ideal α) : 0 ∈ I := submodule.zero_mem I

protected theorem add_mem {α : Type u} [comm_ring α] (I : ideal α) {a : α} {b : α} :
    a ∈ I → b ∈ I → a + b ∈ I :=
  submodule.add_mem I

theorem neg_mem_iff {α : Type u} [comm_ring α] (I : ideal α) {a : α} : -a ∈ I ↔ a ∈ I :=
  submodule.neg_mem_iff I

theorem add_mem_iff_left {α : Type u} [comm_ring α] (I : ideal α) {a : α} {b : α} :
    b ∈ I → (a + b ∈ I ↔ a ∈ I) :=
  submodule.add_mem_iff_left I

theorem add_mem_iff_right {α : Type u} [comm_ring α] (I : ideal α) {a : α} {b : α} :
    a ∈ I → (a + b ∈ I ↔ b ∈ I) :=
  submodule.add_mem_iff_right I

protected theorem sub_mem {α : Type u} [comm_ring α] (I : ideal α) {a : α} {b : α} :
    a ∈ I → b ∈ I → a - b ∈ I :=
  submodule.sub_mem I

theorem mul_mem_left {α : Type u} [comm_ring α] (I : ideal α) (a : α) {b : α} : b ∈ I → a * b ∈ I :=
  submodule.smul_mem I a

theorem mul_mem_right {α : Type u} [comm_ring α] (I : ideal α) {a : α} (b : α) (h : a ∈ I) :
    a * b ∈ I :=
  mul_comm b a ▸ mul_mem_left I b h

end ideal

-- A separate namespace definition is needed because the variables were historically in a different order

namespace ideal

theorem ext {α : Type u} [comm_ring α] {I : ideal α} {J : ideal α} (h : ∀ (x : α), x ∈ I ↔ x ∈ J) :
    I = J :=
  submodule.ext h

theorem eq_top_of_unit_mem {α : Type u} [comm_ring α] (I : ideal α) (x : α) (y : α) (hx : x ∈ I)
    (h : y * x = 1) : I = ⊤ :=
  sorry

theorem eq_top_of_is_unit_mem {α : Type u} [comm_ring α] (I : ideal α) {x : α} (hx : x ∈ I)
    (h : is_unit x) : I = ⊤ :=
  (fun (_a : ∃ (b : α), b * x = 1) =>
      Exists.dcases_on _a
        fun (w : α) (h_1 : w * x = 1) => idRhs (I = ⊤) (eq_top_of_unit_mem I x w hx h_1))
    (iff.mp is_unit_iff_exists_inv' h)

theorem eq_top_iff_one {α : Type u} [comm_ring α] (I : ideal α) : I = ⊤ ↔ 1 ∈ I := sorry

theorem ne_top_iff_one {α : Type u} [comm_ring α] (I : ideal α) : I ≠ ⊤ ↔ ¬1 ∈ I :=
  not_congr (eq_top_iff_one I)

theorem exists_mem_ne_zero_iff_ne_bot {α : Type u} [comm_ring α] (I : ideal α) :
    (∃ (p : α), ∃ (H : p ∈ I), p ≠ 0) ↔ I ≠ ⊥ :=
  sorry

theorem exists_mem_ne_zero_of_ne_bot {α : Type u} [comm_ring α] (I : ideal α) (hI : I ≠ ⊥) :
    ∃ (p : α), ∃ (H : p ∈ I), p ≠ 0 :=
  iff.mpr (exists_mem_ne_zero_iff_ne_bot I) hI

@[simp] theorem unit_mul_mem_iff_mem {α : Type u} [comm_ring α] (I : ideal α) {x : α} {y : α}
    (hy : is_unit y) : y * x ∈ I ↔ x ∈ I :=
  sorry

@[simp] theorem mul_unit_mem_iff_mem {α : Type u} [comm_ring α] (I : ideal α) {x : α} {y : α}
    (hy : is_unit y) : x * y ∈ I ↔ x ∈ I :=
  mul_comm y x ▸ unit_mul_mem_iff_mem I hy

/-- The ideal generated by a subset of a ring -/
def span {α : Type u} [comm_ring α] (s : set α) : ideal α := submodule.span α s

theorem subset_span {α : Type u} [comm_ring α] {s : set α} : s ⊆ ↑(span s) := submodule.subset_span

theorem span_le {α : Type u} [comm_ring α] {s : set α} {I : ideal α} : span s ≤ I ↔ s ⊆ ↑I :=
  submodule.span_le

theorem span_mono {α : Type u} [comm_ring α] {s : set α} {t : set α} : s ⊆ t → span s ≤ span t :=
  submodule.span_mono

@[simp] theorem span_eq {α : Type u} [comm_ring α] (I : ideal α) : span ↑I = I :=
  submodule.span_eq I

@[simp] theorem span_singleton_one {α : Type u} [comm_ring α] : span 1 = ⊤ :=
  iff.mpr (eq_top_iff_one (span 1)) (subset_span (set.mem_singleton 1))

theorem mem_span_insert {α : Type u} [comm_ring α] {s : set α} {x : α} {y : α} :
    x ∈ span (insert y s) ↔ ∃ (a : α), ∃ (z : α), ∃ (H : z ∈ span s), x = a * y + z :=
  submodule.mem_span_insert

theorem mem_span_insert' {α : Type u} [comm_ring α] {s : set α} {x : α} {y : α} :
    x ∈ span (insert y s) ↔ ∃ (a : α), x + a * y ∈ span s :=
  submodule.mem_span_insert'

theorem mem_span_singleton' {α : Type u} [comm_ring α] {x : α} {y : α} :
    x ∈ span (singleton y) ↔ ∃ (a : α), a * y = x :=
  submodule.mem_span_singleton

theorem mem_span_singleton {α : Type u} [comm_ring α] {x : α} {y : α} :
    x ∈ span (singleton y) ↔ y ∣ x :=
  sorry

theorem span_singleton_le_span_singleton {α : Type u} [comm_ring α] {x : α} {y : α} :
    span (singleton x) ≤ span (singleton y) ↔ y ∣ x :=
  iff.trans span_le (iff.trans set.singleton_subset_iff mem_span_singleton)

theorem span_singleton_eq_span_singleton {α : Type u} [integral_domain α] {x : α} {y : α} :
    span (singleton x) = span (singleton y) ↔ associated x y :=
  sorry

theorem span_eq_bot {α : Type u} [comm_ring α] {s : set α} :
    span s = ⊥ ↔ ∀ (x : α), x ∈ s → x = 0 :=
  submodule.span_eq_bot

@[simp] theorem span_singleton_eq_bot {α : Type u} [comm_ring α] {x : α} :
    span (singleton x) = ⊥ ↔ x = 0 :=
  submodule.span_singleton_eq_bot

@[simp] theorem span_zero {α : Type u} [comm_ring α] : span 0 = ⊥ :=
  eq.mpr (id (Eq._oldrec (Eq.refl (span 0 = ⊥)) (Eq.symm set.singleton_zero)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (span (singleton 0) = ⊥)) (propext span_singleton_eq_bot)))
      (Eq.refl 0))

theorem span_singleton_eq_top {α : Type u} [comm_ring α] {x : α} :
    span (singleton x) = ⊤ ↔ is_unit x :=
  sorry

theorem span_singleton_mul_right_unit {α : Type u} [comm_ring α] {a : α} (h2 : is_unit a) (x : α) :
    span (singleton (x * a)) = span (singleton x) :=
  sorry

theorem span_singleton_mul_left_unit {α : Type u} [comm_ring α] {a : α} (h2 : is_unit a) (x : α) :
    span (singleton (a * x)) = span (singleton x) :=
  sorry

/--
The ideal generated by an arbitrary binary relation.
-/
def of_rel {α : Type u} [comm_ring α] (r : α → α → Prop) : ideal α :=
  submodule.span α (set_of fun (x : α) => ∃ (a : α), ∃ (b : α), ∃ (h : r a b), x = a - b)

/-- An ideal `P` of a ring `R` is prime if `P ≠ R` and `xy ∈ P → x ∈ P ∨ y ∈ P` -/
def is_prime {α : Type u} [comm_ring α] (I : ideal α) :=
  I ≠ ⊤ ∧ ∀ {x y : α}, x * y ∈ I → x ∈ I ∨ y ∈ I

theorem is_prime.mem_or_mem {α : Type u} [comm_ring α] {I : ideal α} (hI : is_prime I) {x : α}
    {y : α} : x * y ∈ I → x ∈ I ∨ y ∈ I :=
  and.right hI

theorem is_prime.mem_or_mem_of_mul_eq_zero {α : Type u} [comm_ring α] {I : ideal α}
    (hI : is_prime I) {x : α} {y : α} (h : x * y = 0) : x ∈ I ∨ y ∈ I :=
  and.right hI x y (Eq.symm h ▸ ideal.zero_mem I)

theorem is_prime.mem_of_pow_mem {α : Type u} [comm_ring α] {I : ideal α} (hI : is_prime I) {r : α}
    (n : ℕ) (H : r ^ n ∈ I) : r ∈ I :=
  Nat.rec (fun (H : r ^ 0 ∈ I) => not.elim (mt (iff.mpr (eq_top_iff_one I)) (and.left hI)) H)
    (fun (n : ℕ) (ih : r ^ n ∈ I → r ∈ I) (H : r ^ Nat.succ n ∈ I) =>
      or.cases_on (is_prime.mem_or_mem hI H) id ih)
    n H

theorem not_is_prime_iff {α : Type u} [comm_ring α] {I : ideal α} :
    ¬is_prime I ↔ I = ⊤ ∨ ∃ (x : α), ∃ (H : ¬x ∈ I), ∃ (y : α), ∃ (H : ¬y ∈ I), x * y ∈ I :=
  sorry

theorem zero_ne_one_of_proper {α : Type u} [comm_ring α] {I : ideal α} (h : I ≠ ⊤) : 0 ≠ 1 :=
  fun (hz : 0 = 1) => iff.mp (ne_top_iff_one I) h (hz ▸ ideal.zero_mem I)

theorem span_singleton_prime {α : Type u} [comm_ring α] {p : α} (hp : p ≠ 0) :
    is_prime (span (singleton p)) ↔ prime p :=
  sorry

theorem bot_prime {R : Type u_1} [integral_domain R] : is_prime ⊥ := sorry

/-- An ideal is maximal if it is maximal in the collection of proper ideals. -/
def is_maximal {α : Type u} [comm_ring α] (I : ideal α) := is_coatom I

theorem is_maximal_iff {α : Type u} [comm_ring α] {I : ideal α} :
    is_maximal I ↔ ¬1 ∈ I ∧ ∀ (J : ideal α) (x : α), I ≤ J → ¬x ∈ I → x ∈ J → 1 ∈ J :=
  sorry

theorem is_maximal.eq_of_le {α : Type u} [comm_ring α] {I : ideal α} {J : ideal α}
    (hI : is_maximal I) (hJ : J ≠ ⊤) (IJ : I ≤ J) : I = J :=
  iff.mpr eq_iff_le_not_lt { left := IJ, right := fun (h : I < J) => hJ (and.right hI J h) }

theorem is_maximal.exists_inv {α : Type u} [comm_ring α] {I : ideal α} (hI : is_maximal I) {x : α}
    (hx : ¬x ∈ I) : ∃ (y : α), y * x - 1 ∈ I :=
  sorry

theorem is_maximal.is_prime {α : Type u} [comm_ring α] {I : ideal α} (H : is_maximal I) :
    is_prime I :=
  sorry

protected instance is_maximal.is_prime' {α : Type u} [comm_ring α] (I : ideal α)
    [H : is_maximal I] : is_prime I :=
  is_maximal.is_prime

/-- Krull's theorem: if `I` is an ideal that is not the whole ring, then it is included in some
    maximal ideal. -/
theorem exists_le_maximal {α : Type u} [comm_ring α] (I : ideal α) (hI : I ≠ ⊤) :
    ∃ (M : ideal α), is_maximal M ∧ I ≤ M :=
  sorry

/-- Krull's theorem: a nontrivial ring has a maximal ideal. -/
theorem exists_maximal {α : Type u} [comm_ring α] [nontrivial α] : ∃ (M : ideal α), is_maximal M :=
  sorry

/-- If P is not properly contained in any maximal ideal then it is not properly contained
  in any proper ideal -/
theorem maximal_of_no_maximal {R : Type u} [comm_ring R] {P : ideal R}
    (hmax : ∀ (m : ideal R), P < m → ¬is_maximal m) (J : ideal R) (hPJ : P < J) : J = ⊤ :=
  sorry

theorem mem_span_pair {α : Type u} [comm_ring α] {x : α} {y : α} {z : α} :
    z ∈ span (insert x (singleton y)) ↔ ∃ (a : α), ∃ (b : α), a * x + b * y = z :=
  sorry

theorem span_singleton_lt_span_singleton {β : Type v} [integral_domain β] {x : β} {y : β} :
    span (singleton x) < span (singleton y) ↔ dvd_not_unit y x :=
  sorry

theorem factors_decreasing {β : Type v} [integral_domain β] (b₁ : β) (b₂ : β) (h₁ : b₁ ≠ 0)
    (h₂ : ¬is_unit b₂) : span (singleton (b₁ * b₂)) < span (singleton b₁) :=
  sorry

/-- The quotient `R/I` of a ring `R` by an ideal `I`. -/
def quotient {α : Type u} [comm_ring α] (I : ideal α) := submodule.quotient I

namespace quotient

protected instance has_one {α : Type u} [comm_ring α] (I : ideal α) : HasOne (quotient I) :=
  { one := submodule.quotient.mk 1 }

protected instance has_mul {α : Type u} [comm_ring α] (I : ideal α) : Mul (quotient I) :=
  { mul :=
      fun (a b : quotient I) =>
        quotient.lift_on₂' a b (fun (a b : α) => submodule.quotient.mk (a * b)) sorry }

protected instance comm_ring {α : Type u} [comm_ring α] (I : ideal α) : comm_ring (quotient I) :=
  comm_ring.mk add_comm_group.add sorry add_comm_group.zero sorry sorry add_comm_group.neg
    add_comm_group.sub sorry sorry Mul.mul sorry 1 sorry sorry sorry sorry sorry

/-- The ring homomorphism from a ring `R` to a quotient ring `R/I`. -/
def mk {α : Type u} [comm_ring α] (I : ideal α) : α →+* quotient I :=
  ring_hom.mk (fun (a : α) => submodule.quotient.mk a) sorry sorry sorry sorry

protected instance inhabited {α : Type u} [comm_ring α] {I : ideal α} : Inhabited (quotient I) :=
  { default := coe_fn (mk I) (bit1 (bit0 (bit1 (bit0 (bit0 1))))) }

protected theorem eq {α : Type u} [comm_ring α] {I : ideal α} {x : α} {y : α} :
    coe_fn (mk I) x = coe_fn (mk I) y ↔ x - y ∈ I :=
  submodule.quotient.eq I

@[simp] theorem mk_eq_mk {α : Type u} [comm_ring α] {I : ideal α} (x : α) :
    submodule.quotient.mk x = coe_fn (mk I) x :=
  rfl

theorem eq_zero_iff_mem {α : Type u} {a : α} [comm_ring α] {I : ideal α} :
    coe_fn (mk I) a = 0 ↔ a ∈ I :=
  sorry

theorem zero_eq_one_iff {α : Type u} [comm_ring α] {I : ideal α} : 0 = 1 ↔ I = ⊤ :=
  iff.trans eq_comm (iff.trans eq_zero_iff_mem (iff.symm (eq_top_iff_one I)))

theorem zero_ne_one_iff {α : Type u} [comm_ring α] {I : ideal α} : 0 ≠ 1 ↔ I ≠ ⊤ :=
  not_congr zero_eq_one_iff

protected theorem nontrivial {α : Type u} [comm_ring α] {I : ideal α} (hI : I ≠ ⊤) :
    nontrivial (quotient I) :=
  nontrivial.mk (Exists.intro 0 (Exists.intro 1 (iff.mpr zero_ne_one_iff hI)))

theorem mk_surjective {α : Type u} [comm_ring α] {I : ideal α} : function.surjective ⇑(mk I) :=
  fun (y : quotient I) => quotient.induction_on' y fun (x : α) => exists.intro x rfl

protected instance integral_domain {α : Type u} [comm_ring α] (I : ideal α) [hI : is_prime I] :
    integral_domain (quotient I) :=
  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

theorem is_integral_domain_iff_prime {α : Type u} [comm_ring α] (I : ideal α) :
    is_integral_domain (quotient I) ↔ is_prime I :=
  sorry

theorem exists_inv {α : Type u} [comm_ring α] {I : ideal α} [hI : is_maximal I] {a : quotient I} :
    a ≠ 0 → ∃ (b : quotient I), a * b = 1 :=
  sorry

/-- quotient by maximal ideal is a field. def rather than instance, since users will have
computable inverses in some applications -/
protected def field {α : Type u} [comm_ring α] (I : ideal α) [hI : is_maximal I] :
    field (quotient I) :=
  field.mk integral_domain.add sorry integral_domain.zero sorry sorry integral_domain.neg
    integral_domain.sub sorry sorry integral_domain.mul sorry integral_domain.one sorry sorry sorry
    sorry sorry
    (fun (a : quotient I) =>
      dite (a = 0) (fun (ha : a = 0) => 0) fun (ha : ¬a = 0) => classical.some (exists_inv ha))
    sorry sorry sorry

/-- If the quotient by an ideal is a field, then the ideal is maximal. -/
theorem maximal_of_is_field {α : Type u} [comm_ring α] (I : ideal α) (hqf : is_field (quotient I)) :
    is_maximal I :=
  sorry

/-- The quotient of a ring by an ideal is a field iff the ideal is maximal. -/
theorem maximal_ideal_iff_is_field_quotient {α : Type u} [comm_ring α] (I : ideal α) :
    is_maximal I ↔ is_field (quotient I) :=
  { mp := fun (h : is_maximal I) => field.to_is_field (quotient I),
    mpr := fun (h : is_field (quotient I)) => maximal_of_is_field I h }

/-- Given a ring homomorphism `f : α →+* β` sending all elements of an ideal to zero,
lift it to the quotient by this ideal. -/
def lift {α : Type u} {β : Type v} [comm_ring α] [comm_ring β] (S : ideal α) (f : α →+* β)
    (H : ∀ (a : α), a ∈ S → coe_fn f a = 0) : quotient S →+* β :=
  ring_hom.mk (fun (x : quotient S) => quotient.lift_on' x ⇑f sorry) sorry sorry sorry sorry

@[simp] theorem lift_mk {α : Type u} {β : Type v} {a : α} [comm_ring α] [comm_ring β] (S : ideal α)
    (f : α →+* β) (H : ∀ (a : α), a ∈ S → coe_fn f a = 0) :
    coe_fn (lift S f H) (coe_fn (mk S) a) = coe_fn f a :=
  rfl

@[simp] theorem lift_comp_mk {α : Type u} {β : Type v} [comm_ring α] [comm_ring β] (S : ideal α)
    (f : α →+* β) (H : ∀ (a : α), a ∈ S → coe_fn f a = 0) : ring_hom.comp (lift S f H) (mk S) = f :=
  ring_hom.ext fun (_x : α) => rfl

theorem lift_surjective {α : Type u} {β : Type v} [comm_ring α] [comm_ring β] (S : ideal α)
    (f : α →+* β) (H : ∀ (a : α), a ∈ S → coe_fn f a = 0) (hf : function.surjective ⇑f) :
    function.surjective ⇑(lift S f H) :=
  sorry

end quotient

theorem mem_sup_left {R : Type u} [comm_ring R] {S : ideal R} {T : ideal R} {x : R} :
    x ∈ S → x ∈ S ⊔ T :=
  (fun (this : S ≤ S ⊔ T) => this) le_sup_left

theorem mem_sup_right {R : Type u} [comm_ring R] {S : ideal R} {T : ideal R} {x : R} :
    x ∈ T → x ∈ S ⊔ T :=
  (fun (this : T ≤ S ⊔ T) => this) le_sup_right

theorem mem_supr_of_mem {R : Type u} [comm_ring R] {ι : Type u_1} {S : ι → ideal R} (i : ι)
    {x : R} : x ∈ S i → x ∈ supr S :=
  (fun (this : S i ≤ supr S) => this) (le_supr S i)

theorem mem_Sup_of_mem {R : Type u} [comm_ring R] {S : set (ideal R)} {s : ideal R} (hs : s ∈ S)
    {x : R} : x ∈ s → x ∈ Sup S :=
  (fun (this : s ≤ Sup S) => this) (le_Sup hs)

theorem mem_Inf {R : Type u} [comm_ring R] {s : set (ideal R)} {x : R} :
    x ∈ Inf s ↔ ∀ {I : ideal R}, I ∈ s → x ∈ I :=
  sorry

@[simp] theorem mem_inf {R : Type u} [comm_ring R] {I : ideal R} {J : ideal R} {x : R} :
    x ∈ I ⊓ J ↔ x ∈ I ∧ x ∈ J :=
  iff.rfl

@[simp] theorem mem_infi {R : Type u} [comm_ring R] {ι : Type u_1} {I : ι → ideal R} {x : R} :
    x ∈ infi I ↔ ∀ (i : ι), x ∈ I i :=
  submodule.mem_infi fun (i : ι) => I i

@[simp] theorem mem_bot {R : Type u} [comm_ring R] {x : R} : x ∈ ⊥ ↔ x = 0 := submodule.mem_bot R

/-- All ideals in a field are trivial. -/
theorem eq_bot_or_top {K : Type u} [field K] (I : ideal K) : I = ⊥ ∨ I = ⊤ := sorry

theorem eq_bot_of_prime {K : Type u} [field K] (I : ideal K) [h : is_prime I] : I = ⊥ :=
  iff.mp or_iff_not_imp_right (eq_bot_or_top I) (and.left h)

theorem bot_is_maximal {K : Type u} [field K] : is_maximal ⊥ := sorry

/-- `I^n` as an ideal of `R^n`. -/
def pi {α : Type u} [comm_ring α] (I : ideal α) (ι : Type v) : ideal (ι → α) :=
  submodule.mk (set_of fun (x : ι → α) => ∀ (i : ι), x i ∈ I) sorry sorry sorry

theorem mem_pi {α : Type u} [comm_ring α] (I : ideal α) (ι : Type v) (x : ι → α) :
    x ∈ pi I ι ↔ ∀ (i : ι), x i ∈ I :=
  iff.rfl

/-- `R^n/I^n` is a `R/I`-module. -/
protected instance module_pi {α : Type u} [comm_ring α] (I : ideal α) (ι : Type v) :
    module (quotient I) (quotient (pi I ι)) :=
  semimodule.mk sorry sorry

/-- `R^n/I^n` is isomorphic to `(R/I)^n` as an `R/I`-module. -/
def pi_quot_equiv {α : Type u} [comm_ring α] (I : ideal α) (ι : Type v) :
    linear_equiv (quotient I) (quotient (pi I ι)) (ι → quotient I) :=
  linear_equiv.mk
    (fun (x : quotient (pi I ι)) =>
      quotient.lift_on' x (fun (f : ι → α) (i : ι) => coe_fn (quotient.mk I) (f i)) sorry)
    sorry sorry
    (fun (x : ι → quotient I) => coe_fn (quotient.mk (pi I ι)) fun (i : ι) => quotient.out' (x i))
    sorry sorry

/-- If `f : R^n → R^m` is an `R`-linear map and `I ⊆ R` is an ideal, then the image of `I^n` is
    contained in `I^m`. -/
theorem map_pi {α : Type u} [comm_ring α] (I : ideal α) {ι : Type u_1} [fintype ι] {ι' : Type w}
    (x : ι → α) (hi : ∀ (i : ι), x i ∈ I) (f : linear_map α (ι → α) (ι' → α)) (i : ι') :
    coe_fn f x i ∈ I :=
  sorry

end ideal

namespace ring

theorem not_is_field_of_subsingleton {R : Type u_1} [ring R] [subsingleton R] : ¬is_field R := sorry

theorem exists_not_is_unit_of_not_is_field {R : Type u_1} [comm_ring R] [nontrivial R]
    (hf : ¬is_field R) : ∃ (x : R), ∃ (H : x ≠ 0), ¬is_unit x :=
  sorry

theorem not_is_field_iff_exists_ideal_bot_lt_and_lt_top {R : Type u_1} [comm_ring R]
    [nontrivial R] : ¬is_field R ↔ ∃ (I : ideal R), ⊥ < I ∧ I < ⊤ :=
  sorry

theorem not_is_field_iff_exists_prime {R : Type u_1} [comm_ring R] [nontrivial R] :
    ¬is_field R ↔ ∃ (p : ideal R), p ≠ ⊥ ∧ ideal.is_prime p :=
  sorry

/-- When a ring is not a field, the maximal ideals are nontrivial. -/
theorem ne_bot_of_is_maximal_of_not_is_field {R : Type u_1} [comm_ring R] [nontrivial R]
    {M : ideal R} (max : ideal.is_maximal M) (not_field : ¬is_field R) : M ≠ ⊥ :=
  sorry

end ring

namespace ideal

/-- Maximal ideals in a non-field are nontrivial. -/
theorem bot_lt_of_maximal {R : Type u} [comm_ring R] [nontrivial R] (M : ideal R)
    [hm : is_maximal M] (non_field : ¬is_field R) : ⊥ < M :=
  sorry

end ideal

/-- The set of non-invertible elements of a monoid. -/
def nonunits (α : Type u) [monoid α] : set α := set_of fun (a : α) => ¬is_unit a

@[simp] theorem mem_nonunits_iff {α : Type u} {a : α} [comm_monoid α] :
    a ∈ nonunits α ↔ ¬is_unit a :=
  iff.rfl

theorem mul_mem_nonunits_right {α : Type u} {a : α} {b : α} [comm_monoid α] :
    b ∈ nonunits α → a * b ∈ nonunits α :=
  mt is_unit_of_mul_is_unit_right

theorem mul_mem_nonunits_left {α : Type u} {a : α} {b : α} [comm_monoid α] :
    a ∈ nonunits α → a * b ∈ nonunits α :=
  mt is_unit_of_mul_is_unit_left

theorem zero_mem_nonunits {α : Type u} [semiring α] : 0 ∈ nonunits α ↔ 0 ≠ 1 :=
  not_congr is_unit_zero_iff

@[simp] theorem one_not_mem_nonunits {α : Type u} [monoid α] : ¬1 ∈ nonunits α :=
  not_not_intro is_unit_one

theorem coe_subset_nonunits {α : Type u} [comm_ring α] {I : ideal α} (h : I ≠ ⊤) :
    ↑I ⊆ nonunits α :=
  fun (x : α) (hx : x ∈ ↑I) (hu : is_unit x) => h (ideal.eq_top_of_is_unit_mem I hx hu)

theorem exists_max_ideal_of_mem_nonunits {α : Type u} {a : α} [comm_ring α] (h : a ∈ nonunits α) :
    ∃ (I : ideal α), ideal.is_maximal I ∧ a ∈ I :=
  sorry

/-- A commutative ring is local if it has a unique maximal ideal. Note that
  `local_ring` is a predicate. -/
class local_ring (α : Type u) [comm_ring α] extends nontrivial α where
  is_local : ∀ (a : α), is_unit a ∨ is_unit (1 - a)

namespace local_ring

theorem is_unit_or_is_unit_one_sub_self {α : Type u} [comm_ring α] [local_ring α] (a : α) :
    is_unit a ∨ is_unit (1 - a) :=
  is_local a

theorem is_unit_of_mem_nonunits_one_sub_self {α : Type u} [comm_ring α] [local_ring α] (a : α)
    (h : 1 - a ∈ nonunits α) : is_unit a :=
  iff.mp or_iff_not_imp_right (is_local a) h

theorem is_unit_one_sub_self_of_mem_nonunits {α : Type u} [comm_ring α] [local_ring α] (a : α)
    (h : a ∈ nonunits α) : is_unit (1 - a) :=
  iff.mp or_iff_not_imp_left (is_local a) h

theorem nonunits_add {α : Type u} [comm_ring α] [local_ring α] {x : α} {y : α} (hx : x ∈ nonunits α)
    (hy : y ∈ nonunits α) : x + y ∈ nonunits α :=
  sorry

/-- The ideal of elements that are not units. -/
def maximal_ideal (α : Type u) [comm_ring α] [local_ring α] : ideal α :=
  submodule.mk (nonunits α) sorry sorry sorry

protected instance maximal_ideal.is_maximal (α : Type u) [comm_ring α] [local_ring α] :
    ideal.is_maximal (maximal_ideal α) :=
  eq.mpr
    (id (Eq._oldrec (Eq.refl (ideal.is_maximal (maximal_ideal α))) (propext ideal.is_maximal_iff)))
    { left := id fun (h : 1 ∈ maximal_ideal α) => h is_unit_one,
      right :=
        fun (I : ideal α) (x : α) (hI : maximal_ideal α ≤ I) (hx : ¬x ∈ maximal_ideal α)
          (H : x ∈ I) =>
          Exists.dcases_on
            (eq.mp (Eq._oldrec (Eq.refl (¬x ∈ maximal_ideal α)) (propext not_not)) hx)
            fun (u : units α) (hx_h : ↑u = x) =>
              Eq._oldrec
                (fun (H : ↑u ∈ I) =>
                  eq.mpr (id (Eq.refl (1 ∈ I)))
                    (eq.mp
                      ((fun (ᾰ ᾰ_1 : α) (e_2 : ᾰ = ᾰ_1) (ᾰ_2 ᾰ_3 : ideal α) (e_3 : ᾰ_2 = ᾰ_3) =>
                          congr (congr_arg has_mem.mem e_2) e_3)
                        (↑(u⁻¹) * ↑u) 1 (units.inv_mul u) I I (Eq.refl I))
                      (ideal.mul_mem_left I (↑(u⁻¹)) H)))
                hx_h H }

theorem maximal_ideal_unique (α : Type u) [comm_ring α] [local_ring α] :
    exists_unique fun (I : ideal α) => ideal.is_maximal I :=
  sorry

theorem eq_maximal_ideal {α : Type u} [comm_ring α] [local_ring α] {I : ideal α}
    (hI : ideal.is_maximal I) : I = maximal_ideal α :=
  unique_of_exists_unique (maximal_ideal_unique α) hI (maximal_ideal.is_maximal α)

theorem le_maximal_ideal {α : Type u} [comm_ring α] [local_ring α] {J : ideal α} (hJ : J ≠ ⊤) :
    J ≤ maximal_ideal α :=
  sorry

@[simp] theorem mem_maximal_ideal {α : Type u} [comm_ring α] [local_ring α] (x : α) :
    x ∈ maximal_ideal α ↔ x ∈ nonunits α :=
  iff.rfl

end local_ring

theorem local_of_nonunits_ideal {α : Type u} [comm_ring α] (hnze : 0 ≠ 1)
    (h : ∀ (x y : α), x ∈ nonunits α → y ∈ nonunits α → x + y ∈ nonunits α) : local_ring α :=
  sorry

theorem local_of_unique_max_ideal {α : Type u} [comm_ring α]
    (h : exists_unique fun (I : ideal α) => ideal.is_maximal I) : local_ring α :=
  sorry

theorem local_of_unique_nonzero_prime (R : Type u) [comm_ring R]
    (h : exists_unique fun (P : ideal R) => P ≠ ⊥ ∧ ideal.is_prime P) : local_ring R :=
  sorry

theorem local_of_surjective {A : Type u_1} {B : Type u_2} [comm_ring A] [local_ring A] [comm_ring B]
    [nontrivial B] (f : A →+* B) (hf : function.surjective ⇑f) : local_ring B :=
  sorry

/-- A local ring homomorphism is a homomorphism between local rings
  such that the image of the maximal ideal of the source is contained within
  the maximal ideal of the target. -/
class is_local_ring_hom {α : Type u} {β : Type v} [semiring α] [semiring β] (f : α →+* β) where
  map_nonunit : ∀ (a : α), is_unit (coe_fn f a) → is_unit a

protected instance is_local_ring_hom_id (A : Type u_1) [semiring A] :
    is_local_ring_hom (ring_hom.id A) :=
  is_local_ring_hom.mk fun (a : A) => id

@[simp] theorem is_unit_map_iff {A : Type u_1} {B : Type u_2} [semiring A] [semiring B]
    (f : A →+* B) [is_local_ring_hom f] (a : A) : is_unit (coe_fn f a) ↔ is_unit a :=
  { mp := is_local_ring_hom.map_nonunit a, mpr := ring_hom.is_unit_map f }

protected instance is_local_ring_hom_comp {A : Type u_1} {B : Type u_2} {C : Type u_3} [semiring A]
    [semiring B] [semiring C] (g : B →+* C) (f : A →+* B) [is_local_ring_hom g]
    [is_local_ring_hom f] : is_local_ring_hom (ring_hom.comp g f) :=
  is_local_ring_hom.mk
    fun (a : A) => is_local_ring_hom.map_nonunit a ∘ is_local_ring_hom.map_nonunit (coe_fn f a)

@[simp] theorem is_unit_of_map_unit {α : Type u} {β : Type v} [semiring α] [semiring β]
    (f : α →+* β) [is_local_ring_hom f] (a : α) (h : is_unit (coe_fn f a)) : is_unit a :=
  is_local_ring_hom.map_nonunit a h

theorem of_irreducible_map {α : Type u} {β : Type v} [semiring α] [semiring β] (f : α →+* β)
    [h : is_local_ring_hom f] {x : α} (hfx : irreducible (coe_fn f x)) : irreducible x :=
  sorry

theorem map_nonunit {α : Type u} {β : Type v} [comm_ring α] [local_ring α] [comm_ring β]
    [local_ring β] (f : α →+* β) [is_local_ring_hom f] (a : α)
    (h : a ∈ local_ring.maximal_ideal α) : coe_fn f a ∈ local_ring.maximal_ideal β :=
  fun (H : is_unit (coe_fn f a)) => h (is_unit_of_map_unit f a H)

namespace local_ring

/-- The residue field of a local ring is the quotient of the ring by its maximal ideal. -/
def residue_field (α : Type u) [comm_ring α] [local_ring α] := ideal.quotient (maximal_ideal α)

protected instance residue_field.field (α : Type u) [comm_ring α] [local_ring α] :
    field (residue_field α) :=
  ideal.quotient.field (maximal_ideal α)

protected instance residue_field.inhabited (α : Type u) [comm_ring α] [local_ring α] :
    Inhabited (residue_field α) :=
  { default := bit1 (bit0 (bit1 (bit0 (bit0 1)))) }

/-- The quotient map from a local ring to its residue field. -/
def residue (α : Type u) [comm_ring α] [local_ring α] : α →+* residue_field α :=
  ideal.quotient.mk (maximal_ideal α)

namespace residue_field

/-- The map on residue fields induced by a local homomorphism between local rings -/
def map {α : Type u} {β : Type v} [comm_ring α] [local_ring α] [comm_ring β] [local_ring β]
    (f : α →+* β) [is_local_ring_hom f] : residue_field α →+* residue_field β :=
  ideal.quotient.lift (maximal_ideal α) (ring_hom.comp (ideal.quotient.mk (maximal_ideal β)) f)
    sorry

end residue_field

end local_ring

namespace field

protected instance local_ring {α : Type u} [field α] : local_ring α :=
  local_ring.mk
    fun (a : α) =>
      dite (a = 0)
        (fun (h : a = 0) =>
          Or.inr
            (eq.mpr (id (Eq._oldrec (Eq.refl (is_unit (1 - a))) h))
              (eq.mpr (id (Eq._oldrec (Eq.refl (is_unit (1 - 0))) (sub_zero 1))) is_unit_one)))
        fun (h : ¬a = 0) => Or.inl (is_unit_of_mul_eq_one a (a⁻¹) (div_self h))

end Mathlib


variable (p q r : Prop)

example : p ∧ q ↔ q ∧ p := Iff.intro
(fun hpq : p ∧ q => show q ∧ p from And.intro (And.right hpq) (And.left hpq))
(fun hqp : q ∧ p => show p ∧ q from And.intro (And.right hqp) (And.left hqp))

example : p ∨ q ↔ q ∨ p := Iff.intro
(fun hpq : p ∨ q => Or.elim hpq
   (fun hp : p => show q ∨ p from Or.intro_right q hp)
   (fun hq : q => show q ∨ p from Or.intro_left p hq))
(fun hqp : q ∨ p => Or.elim hqp
   (fun hq : q => show p ∨ q from Or.intro_right p hq)
   (fun hq : p => show p ∨ q from Or.intro_left q hq))

-- first version of a proof
example : (p ∧ q) ∧ r ↔ p ∧ (q ∧ r) :=
Iff.intro
(fun hpq : (p ∧ q) ∧ r => show p ∧ (q ∧ r) from And.intro (hpq.left.left) (And.intro hpq.left.right hpq.right))
(fun hqp : p ∧ (q ∧ r) => show (p ∧ q) ∧ r from And.intro (And.intro hqp.left hqp.right.left) hqp.right.right)

-- same proof using subgoals
example : (p ∧ q) ∧ r ↔ p ∧ (q ∧ r) := Iff.intro
(fun impl_left : (p ∧ q) ∧ r =>
   have hpq : (p ∧ q) := impl_left.left
   have hqr : (q ∧ r) := And.intro hpq.right impl_left.right
   show p ∧ (q ∧ r) from And.intro (hpq.left) hqr)
(fun impl_right : p ∧ (q ∧ r) =>
   have hqr : (q ∧ r) := impl_right.right
   have hpq : (p ∧ q) := And.intro impl_right.left hqr.left
   show (p ∧ q) ∧ r from And.intro hpq hqr.right)

example : (p ∨ q) ∨ r ↔ p ∨ (q ∨ r) := Iff.intro
(fun impl_left : (p ∨ q) ∨ r => Or.elim impl_left
   (fun hpq : (p ∨ q) => Or.elim hpq
      (fun hp : p => show p ∨ (q ∨ r) from Or.intro_left (q ∨ r) hp)
      (fun hq : q =>
         have hqr : q ∨ r := Or.intro_left r hq
         show p ∨ (q ∨ r) from Or.intro_right p hqr))
   (fun hqp : r =>
      have hqr : q ∨ r := Or.intro_right q hqp
      show p ∨ (q ∨ r) from Or.intro_right p hqr))
(fun impl_right : p ∨ (q ∨ r) => Or.elim impl_right
   (fun hp : p =>
      have hpq : p ∨ q := Or.intro_left q hp
      show (p ∨ q) ∨ r from Or.intro_left r hpq)
   (fun hqr : q ∨ r => Or.elim hqr
      (fun hq : q =>
         have hpq : p ∨ q := Or.intro_right p hq
         show (p ∨ q) ∨ r from Or.intro_left r hpq)
      (fun hqp : r => show (p ∨ q) ∨ r from Or.intro_right (p ∨ q) hqp)))

example : p ∧ (q ∨ r) ↔ (p ∧ q) ∨ (p ∧ r) := Iff.intro
(fun impl_left : p ∧ (q ∨ r) =>
   have hp : p := impl_left.left
   Or.elim impl_left.right
      (fun hq : q =>
         have hpq : p ∧ q := And.intro hp hq
         show (p ∧ q) ∨ (p ∧ r) from Or.intro_left (p ∧ r) hpq)
      (fun hqp : r =>
         have hpr : p ∧ r := And.intro hp hqp
         show (p ∧ q) ∨ (p ∧ r) from Or.intro_right (p ∧ q) hpr))
(fun impl_right : (p ∧ q) ∨ (p ∧ r) => Or.elim impl_right
   (fun hpq : p ∧ q =>
      have hqr : q ∨ r := Or.intro_left r hpq.right
      show p ∧ (q ∨ r) from And.intro hpq.left hqr)
   (fun hpr : p ∧ r =>
      have hqr : q ∨ r := Or.intro_right q hpr.right
      show p ∧ (q ∨ r) from And.intro hpr.left hqr))

example : p ∨ (q ∧ r) ↔ (p ∨ q) ∧ (p ∨ r) := Iff.intro
(fun impl_left : p ∨ (q ∧ r) => Or.elim impl_left
   (fun hp : p =>
      have hpq : p ∨ q := Or.intro_left q hp
      have hpr : p ∨ r := Or.intro_left r hp
      show (p ∨ q) ∧ (p ∨ r) from And.intro hpq hpr)
   (fun hqr : q ∧ r =>
      have hpq : p ∨ q := Or.intro_right p hqr.left
      have hpr : p ∨ r := Or.intro_right p hqr.right
      show (p ∨ q) ∧ (p ∨ r) from And.intro hpq hpr))
(fun impl_right : (p ∨ q) ∧ (p ∨ r) =>
   have hpq : (p ∨ q) := impl_right.left
   have hpr : (p ∨ r) := impl_right.right
   Or.elim hpq
      (fun hp : p => show p ∨ (q ∧ r) from Or.intro_left (q ∧ r) hp)
      (fun hq : q => Or.elim hpr
         (fun hp : p => show p ∨ (q ∧ r) from Or.intro_left (q ∧ r) hp)
         (fun hr : r =>
            have hqr : q ∧ r := And.intro hq hr
            show p ∨ (q ∧ r) from Or.intro_right p hqr)))

example : (p → (q → r)) ↔ (p ∧ q → r) := Iff.intro
(fun hpqr : p → (q → r) =>
   fun hpq : p ∧ q =>
   have hqr : q → r := hpqr hpq.left
   show r from hqr hpq.right)
(fun hpqr : p ∧ q → r =>
   fun hp : p =>
   fun hq : q =>
   have hpq : p ∧ q := And.intro hp hq
   show r from hpqr hpq)

example : ((p ∨ q) → r) ↔ (p → r) ∧ (q → r) := Iff.intro
(fun hpq : (p ∨ q) → r =>
   have hpr : p → r := fun hp : p => hpq (Or.intro_left q hp)
   have hqr : q → r := fun hq : q => hpq (Or.intro_right p hq)
   show (p → r) ∧ (q → r) from And.intro hpr hqr)
(fun h1 : (p → r) ∧ (q → r) =>
   have hpr : p → r := h1.left
   have hqr : q → r := h1.right
   fun hpq : p ∨ q => Or.elim hpq
      (fun hp : p => show r from hpr hp)
      (fun hq : q => show r from hqr hq))

example : ¬ (p ∨ q) ↔ ¬ p ∧ ¬ q := Iff.intro -- same as the previous, just replace r with False
(fun hpq : p ∨ q → False =>
   have hpf : p → False := fun hp : p => hpq (Or.intro_left q hp)
   have hqf : q → False := fun hq : q => hpq (Or.intro_right p hq)
   show ¬ p ∧ ¬ q from And.intro hpf hqf)
(fun h1 : ¬ p ∧ ¬ q =>
   have hpf : ¬ p := h1.left
   have hqf : ¬ q := h1.right
   fun hpq : p ∨ q => Or.elim hpq
      (fun hp : p => show False from hpf hp)
      (fun hq : q => show False from hqf hq))

example : ¬p ∨ ¬q → ¬(p ∧ q) :=
fun h1 : ¬p ∨ ¬q =>
   fun hpq : p ∧ q =>
      show False from Or.elim h1
         (fun hp : p → False => hp hpq.left)
         (fun hq : q → False => hq hpq.right)

example : ¬(p ∧ ¬p) :=
fun hpp : p ∧ ¬p => show False from hpp.right hpp.left

example : p ∧ ¬q → ¬(p → q) :=
fun h1 : p ∧ ¬q =>
   fun hpq : p → q =>
      have hq : q := hpq h1.left
      show False from h1.right hq

example : ¬p → (p → q) :=
fun h1 : p → False =>
   fun hp : p => False.elim (h1 hp)

example : (¬p ∨ q) → (p → q) :=
fun hpq : ¬p ∨ q =>
   fun hp : p =>
      show q from Or.elim hpq
         (fun hnp : p → False => False.elim (hnp hp))
         (fun hq : q => hq)

example : p ∨ False ↔ p := Iff.intro
(fun hpf : p ∨ False => Or.elim hpf
   (fun hp : p => hp)
   (fun hf : False => False.elim hf))
(fun hp : p => show p ∨ False from Or.intro_left False hp)

example : p ∧ False ↔ False := Iff.intro
(fun hpf : p ∧ False => show False from hpf.right)
(fun hf : False =>
   have hp : p := False.elim hf
   show p ∧ False from And.intro hp hf)

example : (p → q) → (¬q → ¬p) :=
fun hpq : p → q =>
   fun hqf : q → False =>
      fun hp : p => show False from (hqf (hpq hp))

open Classical

example : (p → q ∨ r) → ((p → q) ∨ (p → r)) :=
fun hpqr : (p → q ∨ r) => Or.elim (em q)
   (fun hq : q =>
      suffices hpq : p → q from Or.intro_left (p → r) hpq
      fun _ : p => show q from hq)
   (fun hqf : q → False =>
         suffices hpr : p → r from Or.intro_right (p → q) hpr
         fun hp : p => Or.elim (hpqr hp)
            (fun hq : q => show r from False.elim (hqf hq))
            (fun hr : r => hr))

example : ¬(p ∧ q) → ¬p ∨ ¬q :=
fun h1 : (p ∧ q) → False => Or.elim (em p)
  (fun hp : p => suffices hqf : q → False from Or.intro_right (¬p) hqf
    fun hq : q => show False from h1 (And.intro hp hq))
  (fun hpf : p → False => Or.intro_left (q → False) hpf)

example : ¬(p → q) → p ∧ ¬q :=
fun h1 : (p → q) → False =>
  have h2 : p := Or.elim (em p)
    (fun hp : p => hp)
    (fun hpf : p → False =>
      have hpq : p → q := fun hp : p => False.elim (hpf hp)
      False.elim (h1 hpq))
  have h3 : q → False := Or.elim (em q)
    (fun hq : q =>
      fun _ : q => h1 (fun _ : p => hq))
    (fun hqf : q → False => hqf)
  show p ∧ ¬q from And.intro h2 h3

example : (p → q) → (¬p ∨ q) :=
fun hpq : p → q => Or.elim (em p)
  (fun hp : p =>
    have hq : q := hpq hp
    show ¬p ∨ q from  Or.intro_right (p → False) hq)
  (fun hpf : p → False => show ¬p ∨ q from Or.intro_left q hpf)

example : (¬q → ¬p) → (p → q) :=
fun hqp : ¬q → ¬p => Or.elim (em q)
   (fun hq : q =>
      fun _ : p => show q from hq)
   (fun hqf : q → False =>
      have hpf : p → False := hqp hqf
      fun hp : p => show q from False.elim (hpf hp))

example : p ∨ ¬p := em p

example : ((p → q) → p) → p :=
fun h1 : (p → q) → p => Or.elim (em (p → q))
  (h1)
  (fun _ : (p → q) → False => Or.elim (em p)
    (fun hp : p => hp)
    (fun hpf : p → False =>
      have hpq : p → q := fun hp : p => show q from False.elim (hpf hp)
      show p from (h1 hpq)))

example : ¬(p ↔ ¬p) :=
fun h1 : p ↔ ¬p =>
  have hleft : p → ¬p := Iff.mp h1
  have hright : ¬p → p := Iff.mpr h1
  have hpf : ¬p := fun h2 : p => (hleft h2) h2
  have hp : p := hright hpf
  show False from (hpf hp)