module

public import Mathlib
public import Groebner.ToMathlib.AddEquiv
public import Groebner.ToMathlib.PropLemma
public import Groebner.ToMathlib.MvPolynomial

open MvPolynomial
namespace MonomialOrder
variable {σ : Type*} {m : MonomialOrder σ}

/- S-polynomial merged:
- https://github.com/leanprover-community/mathlib4/pull/32344,
- https://github.com/leanprover-community/mathlib4/pull/32788. -/

@[expose] public section

section misc

variable {R : Type*} [CommSemiring R] (f g : MvPolynomial σ R)

-- submitted: https://github.com/leanprover-community/mathlib4/pull/34758
@[simp]
lemma monic_leadingTerm (p : MvPolynomial σ R) :
    m.Monic (m.leadingTerm p) ↔ m.Monic p := by simp [leadingTerm, Monic]

-- submitted: https://github.com/leanprover-community/mathlib4/pull/34758
lemma support_leadingTerm (p : MvPolynomial σ R) [Decidable (p = 0)] :
    support (m.leadingTerm p) = if p = 0 then ∅ else {m.degree p} := by
  classical
  simp [leadingTerm, support_monomial]

-- submitted: https://github.com/leanprover-community/mathlib4/pull/34758
lemma support_leadingTerm' {p : MvPolynomial σ R} (hp : p ≠ 0) :
    support (m.leadingTerm p) = {m.degree p} := by
  classical
  simp [leadingTerm, support_monomial, hp]

-- submitted: https://github.com/leanprover-community/mathlib4/pull/34758
lemma le_degree_of_mem_support {p : MvPolynomial σ R} {a : σ →₀ ℕ}
    (ha : a ∈ p.support) : a ≼[m] m.degree p := by
  simp [degree, Finset.le_sup ha]

-- submitted: https://github.com/leanprover-community/mathlib4/pull/34758
lemma leadingTerm_eq_leadingTerm_iff {p q : MvPolynomial σ R} :
    m.leadingTerm p = m.leadingTerm q ↔
    m.leadingCoeff p = m.leadingCoeff q ∧ m.degree p = m.degree q := by
  rw [leadingTerm, leadingTerm, monomial_eq_monomial_iff]
  aesop

-- submitted: https://github.com/leanprover-community/mathlib4/pull/34758
@[simp]
lemma monic_one' : m.Monic (1 : MvPolynomial σ R) := monic_one

-- submitted: https://github.com/leanprover-community/mathlib4/pull/34758
@[simp, nontriviality]
lemma monic_of_subsingleton [Subsingleton (MvPolynomial σ R)] (p : MvPolynomial σ R) :
    m.Monic p := by
  simp [Subsingleton.eq_one (α := MvPolynomial σ R)]

-- submitted: https://github.com/leanprover-community/mathlib4/pull/34758
lemma degree_le_degree_of_support_subset {p q : MvPolynomial σ R} (h : p.support ⊆ q.support) :
    m.degree p ≼[m] m.degree q := by
  simp_rw [degree, m.toSyn.apply_symm_apply]
  exact Finset.sup_mono h

-- submitted: https://github.com/leanprover-community/mathlib4/pull/34758
theorem degree_mul_le' {f g : MvPolynomial σ R} :
    m.toSyn (m.degree (f * g)) ≤ m.toSyn (m.degree f) + m.toSyn (m.degree g) :=
  map_add m.toSyn _ _ ▸ degree_mul_le

-- submitted: https://github.com/leanprover-community/mathlib4/pull/34758
variable {f} in
lemma mem_nonZeroDivisors_of_leadingCoeff_mem_nonZeroDivisors
    (hf : m.leadingCoeff f ∈ nonZeroDivisors _) : f ∈ nonZeroDivisors _ := by
  rw [← nonZeroDivisorsLeft_eq_nonZeroDivisors, mem_nonZeroDivisorsLeft_iff]
  intro g
  rw [← not_imp_not, ← m.leadingCoeff_eq_zero_iff (f := f * g)]
  intro h
  rwa [m.leadingCoeff_mul_of_left_mem_nonZeroDivisors hf h,
    mul_left_mem_nonZeroDivisors_eq_zero_iff hf, m.leadingCoeff_eq_zero_iff]

-- submitted: https://github.com/leanprover-community/mathlib4/pull/34765
@[simp]
theorem leadingCoeff_mul' [NoZeroDivisors R] {f g : MvPolynomial σ R} :
    m.leadingCoeff (f * g) = m.leadingCoeff f * m.leadingCoeff g := by
  -- improved version of `MonomialOrder.leadingCoeff_mul`
  wlog! +distrib h : f ≠ 0 ∧ g ≠ 0
  · cases h <;> simp [*]
  obtain ⟨hf, hg⟩ := h
  rw [leadingCoeff, degree_mul hf hg, ← coeff_mul_of_degree_add]

lemma sPolynomial_decomposition_of_degree_sum_smul_le₀ {R} [CommRing R] {d : m.syn} {ι : Type*}
    {B : Finset ι} {c : ι → R} {g : ι → MvPolynomial σ R}
    (hd : ∀ b ∈ B,
      (m.toSyn <| m.degree <| g b) = d ∧ IsUnit (m.leadingCoeff <| g b) ∨ g b = 0)
    (hfd : (m.toSyn <| m.degree <| ∑ b ∈ B, c b • g b) < d) :
    ∃ (c' : ι → ι → R),
      ∑ b ∈ B, c b • g b = ∑ b₁ ∈ B, ∑ b₂ ∈ B, (c' b₁ b₂) • m.sPolynomial (g b₁) (g b₂) := by
  classical
  classical
  induction B using Finset.induction_on with
  | empty => simp
  | insert b B hb h =>
    by_cases hb0 : g b = 0
    · simp_all
    simp? [Finset.sum_insert hb, hb0] at hfd hd says
      simp only [Finset.sum_insert hb, Finset.mem_insert, forall_eq_or_imp, hb0, or_false] at hfd hd
    obtain ⟨⟨rfl, isunit_gb⟩, hd⟩ := hd
    use fun b₁ b₂ ↦ if b₂ = b then c b₁ * ↑isunit_gb.unit⁻¹ else 0
    simp? [Finset.sum_insert hb, hb] says
      simp only [Finset.sum_insert hb, ite_smul, zero_smul, ↓reduceIte, Finset.sum_ite_eq', hb,
        add_zero, sPolynomial_self, smul_zero, zero_add]
    simp only [m.toSyn.injective.eq_iff] at *
    trans ∑ b' ∈ B, (c b' • g b' - (c b' * m.leadingCoeff (g b') * ↑isunit_gb.unit⁻¹) • g b)
    · suffices (-(∑ i ∈ B, c i * m.leadingCoeff (g i))) = c b * m.leadingCoeff (g b) by
        rw [add_comm, Finset.sum_sub_distrib, sub_eq_add_neg, ← Finset.sum_smul, ← Finset.sum_mul,
          ← neg_smul, ← neg_mul, this, mul_assoc, isunit_gb.mul_val_inv, mul_one]
      rw [← add_eq_zero_iff_neg_eq']
      trans c b * (g b).coeff (m.degree <| g b) + ∑ i ∈ B, c i * (g i).coeff (m.degree <| g b)
      · unfold leadingCoeff
        congr 1
        apply Finset.sum_congr rfl
        intro b' hb'
        rcases hd b' hb' with h | h <;> simp [h]
      · simp_rw [← MvPolynomial.coeff_C_mul, ← smul_eq_C_mul]
        rw [← coeff_sum, ← coeff_add, ← notMem_support_iff]
        exact m.notMem_support_of_degree_lt hfd
    · apply Finset.sum_congr rfl
      intro b' hb'
      rw [sPolynomial]
      obtain (⟨h, -⟩ | h) := hd b' hb'
      · simp [h, ← smul_eq_C_mul, smul_sub, ← mul_smul,
          (mul_assoc ..).trans (congrArg (c b' * ·) isunit_gb.val_inv_mul),
          mul_right_comm (b := m.leadingCoeff (g b'))]
      · simp [h]

lemma sPolynomial_decomposition_of_degree_sum_smul_le {R} [CommRing R] {d : m.syn} {ι : Type*}
    {B : Finset ι} {c : ι → R} {g : ι → MvPolynomial σ R}
    (hB : ∀ b ∈ B, IsUnit (m.leadingCoeff <| g b))
    (hd : ∀ b ∈ B, (m.toSyn <| m.degree <| g b) = d)
    (hfd : (m.toSyn <| m.degree <| ∑ b ∈ B, c b • g b) < d) :
    ∃ (c' : ι → ι → R),
      ∑ b ∈ B, c b • g b = ∑ b₁ ∈ B, ∑ b₂ ∈ B, (c' b₁ b₂) • m.sPolynomial (g b₁) (g b₂) :=
  sPolynomial_decomposition_of_degree_sum_smul_le₀ (fun _ h ↦ Or.intro_left _ ⟨hd _ h, hB _ h⟩) hfd

lemma sPolynomial_decomposition_of_degree_sum_le {R} [CommRing R] {d : m.syn} {ι : Type*}
    {B : Finset ι} {g : ι → MvPolynomial σ R}
    (hd : ∀ b ∈ B,
      (m.toSyn <| m.degree <| g b) = d ∧ IsUnit (m.leadingCoeff <| g b) ∨ g b = 0)
    (hfd : (m.toSyn <| m.degree <| ∑ b ∈ B, g b) < d) :
    ∃ (c : ι → ι → R),
      ∑ b ∈ B, g b = ∑ b₁ ∈ B, ∑ b₂ ∈ B, (c b₁ b₂) • m.sPolynomial (g b₁) (g b₂) := by
  simpa using
    sPolynomial_decomposition_of_degree_sum_smul_le₀ (c := fun _ ↦ 1) hd (by simpa using hfd)

lemma sPolynomial_monomial_mul_of_mem_nonZeroDivisors {R} [CommRing R]
    {p₁ p₂ : MvPolynomial σ R}
    (hp₁ : m.leadingCoeff p₁ ∈ nonZeroDivisors _)
    (hp₂ : m.leadingCoeff p₂ ∈ nonZeroDivisors _)
    (d₁ d₂ : σ →₀ ℕ)
    (c₁ c₂ : R) :
    m.sPolynomial ((monomial d₁ c₁) * p₁) ((monomial d₂ c₂) * p₂) =
      monomial ((d₁ + m.degree p₁) ⊔ (d₂ + m.degree p₂) - m.degree p₁ ⊔ m.degree p₂) (c₁ * c₂) *
      m.sPolynomial p₁ p₂ := by
  classical
  simp only [sPolynomial_def]
  wlog! +distrib H : c₁ ≠ 0 ∧ c₂ ≠ 0
  · (obtain rfl | rfl := H) <;> simp
  rcases H with ⟨hc1, hc2⟩
  have hm1 := (monomial_eq_zero (s := d₁)).not.mpr hc1
  have hm2 := (monomial_eq_zero (s := d₂)).not.mpr hc2
  simp_rw [m.degree_mul_of_right_mem_nonZeroDivisors hm1 hp₁,
    m.degree_mul_of_right_mem_nonZeroDivisors hm2 hp₂,
    mul_sub, ← mul_assoc _ _ p₁, ← mul_assoc _ _ p₂, monomial_mul,
    m.leadingCoeff_mul_of_right_mem_nonZeroDivisors hm1 hp₁,
    m.leadingCoeff_mul_of_right_mem_nonZeroDivisors hm2 hp₂,
    m.leadingCoeff_monomial, degree_monomial, hc1, hc2, reduceIte, mul_right_comm, mul_comm c₂ c₁]
  rw [tsub_add_tsub_cancel (sup_le_sup (self_le_add_left _ _) (self_le_add_left _ _)) (by simp),
    tsub_add_tsub_cancel (sup_le_sup (self_le_add_left _ _) (self_le_add_left _ _)) (by simp),
    tsub_add_eq_add_tsub le_sup_left, tsub_add_eq_add_tsub le_sup_right,
    add_comm d₁, add_comm d₂, add_tsub_add_eq_tsub_right, add_tsub_add_eq_tsub_right]

lemma sPolynomial_monomial_mul_of_mem_nonZeroDivisors' {R} [CommRing R]
    {p₁ p₂ : MvPolynomial σ R}
    (hp₁ : m.leadingCoeff p₁ ∈ nonZeroDivisors _)
    (hp₂ : m.leadingCoeff p₂ ∈ nonZeroDivisors _)
    (d₁ d₂ : σ →₀ ℕ)
    (c₁ c₂ : R) :
    m.sPolynomial (monomial d₁ c₁ * p₁) (monomial d₂ c₂ * p₂) =
      monomial (m.degree (monomial d₁ c₁ * p₁) ⊔ m.degree (monomial d₂ c₂ * p₂) -
          m.degree p₁ ⊔ m.degree p₂) (c₁ * c₂) *
      m.sPolynomial p₁ p₂ := by
  classical
  wlog! +distrib H : c₁ ≠ 0 ∧ c₂ ≠ 0
  · (obtain rfl | rfl := H) <;> simp
  simp [H, hp₁, hp₂, degree_mul_of_right_mem_nonZeroDivisors,
    sPolynomial_monomial_mul_of_mem_nonZeroDivisors, degree_monomial]


theorem leadingCoeff_mul_of_left_mem_nonZeroDivisors' {f g : MvPolynomial σ R}
    (hf : m.leadingCoeff f ∈ nonZeroDivisors _) :
    m.leadingCoeff (f * g) = m.leadingCoeff f * m.leadingCoeff g := by
  by_cases hg : g = 0
  · simp [hg]
  simp only [leadingCoeff, degree_mul_of_left_mem_nonZeroDivisors hf hg, coeff_mul_of_degree_add]

theorem leadingCoeff_mul_of_right_mem_nonZeroDivisors' {f g : MvPolynomial σ R}
    (hg : m.leadingCoeff g ∈ nonZeroDivisors _) :
    m.leadingCoeff (f * g) = m.leadingCoeff f * m.leadingCoeff g := by
  by_cases hf : f = 0
  · simp [hf]
  simp only [leadingCoeff, degree_mul_of_right_mem_nonZeroDivisors hf hg, coeff_mul_of_degree_add]

lemma support_add_of_leadingTerm_add_leadingTerm_eq_zero
    {p q : MvPolynomial σ R}
    (h : m.leadingTerm p + m.leadingTerm q = 0) :
    ∀ a ∈ (p + q).support,
      (a ∈ p.support ∧ a ≺[m] m.degree p) ∨
      (a ∈ q.support ∧ a ≺[m] m.degree q) := by
  classical
  intro a ha
  wlog! +distrib hpq : p ≠ 0 ∧ q ≠ 0
  · rcases hpq with h' | h'
    · simp [h'] at h
      simp [h, h'] at ha
    · simp [h'] at h
      simp [h, h'] at ha
  unfold leadingTerm at h
  by_cases! hpq' : m.degree p ≠ m.degree q
  · apply congrArg (coeff (m.degree q)) at h
    simp [hpq', hpq] at h
  rw [hpq', ← map_add, monomial_eq_zero] at h
  unfold leadingCoeff at h
  rcases Finset.mem_union.mp <| support_add (p := p) (q := q) ha with h' | h'
  on_goal 1 => left
  on_goal 2 => right
  all_goals
    exists h'
    apply lt_of_le_of_ne (m.le_degree_of_mem_support h')
    by_contra!
    rw [m.toSyn.apply_eq_iff_eq] at this
    subst this
    simp [hpq' ▸ h, hpq'] at ha

@[simp]
lemma leadingTerm_neg {R} [CommRing R] (p : MvPolynomial σ R) :
    m.leadingTerm (-p) = - m.leadingTerm p := by
  -- todo: why
  -- https://leanprover.zulipchat.com/#narrow/channel/113488-general/topic/backward.2EisDefEq.2ErespectTransparency/with/576908571
  set_option backward.isDefEq.respectTransparency false in
  simp [leadingTerm]

lemma support_sub_of_leadingTerm_eq_leadingTerm {R} [CommRing R]
    {p q : MvPolynomial σ R}
    (h : m.leadingTerm p = m.leadingTerm q) :
    ∀ a ∈ (p - q).support,
      (a ∈ p.support ∧ a ≺[m] m.degree p) ∨
      (a ∈ q.support ∧ a ≺[m] m.degree q) := by
  classical
  convert m.support_add_of_leadingTerm_add_leadingTerm_eq_zero (p := p) (q := -q) ?_
  · simp [-MvPolynomial.mem_support_iff, ← sub_eq_add_neg, degree_neg, support_neg]
  simp [h]

end misc

section killCompl

lemma degree_rename_killCompl_le_degree {σ' R : Type*} [CommSemiring R]
    {f : σ' → σ} (hf : f.Injective) {p : MvPolynomial σ R} :
    m.degree ((p.killCompl hf).rename f) ≼[m] m.degree p :=
  m.degree_le_degree_of_support_subset (support_rename_killCompl_subset hf)

end killCompl

section WithBotDegree

section Semiring

variable {R : Type*} [CommSemiring R] (f g : MvPolynomial σ R)

variable (m) in
/-- the degree of a multivariate polynomial with respect to a monomial ordering -/
noncomputable def withBotDegree : WithBot (σ →₀ ℕ) :=
  f.support.image m.toSyn |>.max.map m.toSyn.symm

variable (m) in
noncomputable def toWithBotSyn : WithBot (σ →₀ ℕ) ≃+ WithBot m.syn := m.toSyn.withBotCongr

@[simp]
lemma toWithBotSyn_apply_bot : m.toWithBotSyn ⊥ = ⊥ := rfl

@[simp]
lemma toWithBotSyn_symm_apply_bot : m.toWithBotSyn.symm ⊥ = ⊥ := rfl

@[simp]
lemma toWithBotSyn_apply_eq_bot_iff (a) : m.toWithBotSyn a = ⊥ ↔ a = ⊥ := by
  simp [← m.toWithBotSyn.eq_symm_apply]

@[simp]
lemma toWithBotSyn_apply_le_bot_iff (a) : m.toWithBotSyn a ≤ ⊥ ↔ a = ⊥ := by
  simp [← m.toWithBotSyn.eq_symm_apply]

@[simp]
lemma toWithBotSyn_apply_coe (a : σ →₀ ℕ) : m.toWithBotSyn a = m.toSyn a := rfl

@[simp]
lemma bot_lt_toWithBotSyn_apply_iff (a) : ⊥ < m.toWithBotSyn a ↔ a ≠ ⊥ := by
  simp [bot_lt_iff_ne_bot]

@[simp]
lemma toWithBotSyn_symm_apply_eq_bot (a) : m.toWithBotSyn.symm a = ⊥ ↔ a = ⊥ := by
  simp [m.toWithBotSyn.symm_apply_eq]

lemma toWithBotSyn_apply (a : WithBot (σ →₀ ℕ)) : m.toWithBotSyn a = a.map m.toSyn := rfl

/-- Given a monomial order with bot, notation for the corresponding strict order relation on
`WithBot (σ →₀ ℕ)` -/
scoped
notation:50 c " ≺'[" m:25 "] " d:50 =>
  (MonomialOrder.toWithBotSyn m c < MonomialOrder.toWithBotSyn m d)

/-- Given a monomial order with bot, notation for the corresponding order relation on
`WithBot (σ →₀ ℕ)` -/
scoped
notation:50 c " ≼'[" m:25 "] " d:50 =>
  (MonomialOrder.toWithBotSyn m c ≤ MonomialOrder.toWithBotSyn m d)

lemma withBotDegree_eq [Decidable (f = 0)] :
    m.withBotDegree f = if f = 0 then ⊥ else ↑(m.degree f) := by
  simp [withBotDegree, degree]
  by_cases hf : f = 0
  · simp [hf]
  · simp [hf, Finset.max_eq_sup_coe, ← Finset.coe_sup_of_nonempty _ (⇑m.toSyn)]

@[simp]
lemma withBotDegree_eq_coe_degree_iff : m.withBotDegree f = m.degree f ↔ f ≠ 0 := by
  classical
  simp [withBotDegree_eq]

@[simp]
lemma withBotDegree_eq_bot_iff : m.withBotDegree f = ⊥ ↔ f = 0 := by
  classical
  simp [withBotDegree_eq]

lemma degree_eq_unbotD_withBotDegree : m.degree f = (m.withBotDegree f).unbotD 0 := by
  classical
  by_cases h : f = 0 <;> simp [withBotDegree_eq, h]

@[simp]
lemma withBotDegree_zero : m.withBotDegree (R := R) 0 = ⊥ := rfl

lemma withBotDegree_monomial (d) (c) [Decidable (c = 0)] :
    m.withBotDegree (R := R) (monomial d c) = if c = 0 then ⊥ else ↑d := by
  classical
  split_ifs <;> simp [withBotDegree_eq, *, m.degree_monomial]

lemma withBotDegree_C (c) [Decidable (c = 0)] :
    m.withBotDegree (R := R) (C c) = if c = 0 then ⊥ else 0 := by
  classical
  rw [← monomial_zero', withBotDegree_monomial]
  rfl

@[simp]
lemma withBotDegree_leadingTerm : m.withBotDegree (m.leadingTerm f) = m.withBotDegree f := by
  classical
  simp [withBotDegree_eq]

@[simp]
lemma withBotDegree_neg {R} [CommRing R] (f : MvPolynomial σ R) :
    m.withBotDegree (-f) = m.withBotDegree f := by
  classical
  simp [m.withBotDegree_eq]

@[simp]
lemma withBotDegree_one [Nontrivial R] : m.withBotDegree (R := R) 1 = 0 := by
  classical
  simp [withBotDegree_eq]

variable {f g} in
lemma withBotDegree_mul_of_left_mem_nonZeroDivisors (hf : m.leadingCoeff f ∈ nonZeroDivisors _) :
    m.withBotDegree (f * g) = m.withBotDegree f + m.withBotDegree g := by
  classical
  wlog! +distrib h0 : f ≠ 0 ∧ g ≠ 0
  · rcases h0 with h0 | h0 <;> simp [h0]
  have : f * g ≠ 0 := not_imp_not.mpr
    ((mem_nonZeroDivisors_iff.mp (mem_nonZeroDivisors_of_leadingCoeff_mem_nonZeroDivisors hf)).1 _)
    h0.2
  simp [m.withBotDegree_eq, h0, m.degree_mul_of_left_mem_nonZeroDivisors hf, this]

variable {f g} in
lemma withBotDegree_mul_of_right_mem_nonZeroDivisors (hf : m.leadingCoeff g ∈ nonZeroDivisors _) :
    m.withBotDegree (f * g) = m.withBotDegree f + m.withBotDegree g := by
  rw [mul_comm, add_comm, withBotDegree_mul_of_left_mem_nonZeroDivisors (hf := hf)]

@[simp]
lemma withBotDegree_mul [NoZeroDivisors R] :
    m.withBotDegree (f * g) = m.withBotDegree f + m.withBotDegree g := by
  wlog! trivial : Nontrivial R
  · simp [Subsingleton.eq_zero (α := MvPolynomial σ R)]
  wlog! hf : f ≠ 0
  · simp [hf]
  rw [← m.leadingCoeff_ne_zero_iff] at hf
  exact m.withBotDegree_mul_of_left_mem_nonZeroDivisors (mem_nonZeroDivisors_iff_ne_zero.mpr hf)

lemma withBotDegree_mul_le :
    m.withBotDegree (f * g) ≼'[m] m.withBotDegree f + m.withBotDegree g := by
  wlog! +distrib h0 : f * g ≠ 0
  · simp [h0]
  simp [m.withBotDegree_eq_coe_degree_iff f |>.mpr (by aesop),
    m.withBotDegree_eq_coe_degree_iff g |>.mpr (by aesop),
    m.withBotDegree_eq_coe_degree_iff _ |>.mpr h0, ← WithBot.coe_add,
    ← map_add, -- the warning here is a bug?
    m.degree_mul_le]

lemma withBotDegree_mul_le' :
    m.toWithBotSyn (m.withBotDegree (f * g)) ≤
      m.toWithBotSyn (m.withBotDegree f) + m.toWithBotSyn (m.withBotDegree g) := by
  wlog! +distrib h0 : f * g ≠ 0
  · simp [h0]
  simp [m.withBotDegree_eq_coe_degree_iff f |>.mpr (by aesop),
    m.withBotDegree_eq_coe_degree_iff g |>.mpr (by aesop),
    m.withBotDegree_eq_coe_degree_iff _ |>.mpr h0, ← WithBot.coe_add,
    m.degree_mul_le']

-- lemma le_withBotDegree_iff (a) :
--     a ≤ m.toWithBotSyn (m.withBotDegree g) ↔
--       (a ≤ m.toSyn (m.degree g) ∧ (g = 0 → a = ⊥)) := by
--   classical
--   wlog! +distrib h : a ≠ ⊥ ∧ g ≠ 0
--   · rcases h with h | h
--     · simp [h, m.toWithBotSyn_apply]
--     · simp_rw [m.toWithBotSyn_apply]
--       aesop
--   simp [m.withBotDegree_eq, h, m.toWithBotSyn_apply]

lemma withBotDegree_le_withBotDegree_iff :
    m.withBotDegree f ≼'[m] m.withBotDegree g ↔
      (m.degree f ≼[m] m.degree g ∧ (g = 0 → f = 0)) := by
  classical
  wlog! +distrib h : f ≠ 0 ∧ g ≠ 0
  · rcases h with h | h
    · simp [h, m.toWithBotSyn_apply]
    · simp_rw [m.toWithBotSyn_apply]
      aesop
  simp [m.withBotDegree_eq, h, m.toWithBotSyn_apply]

lemma withBotDegree_le_withBotDegree_iff' :
    m.withBotDegree f ≤ m.withBotDegree g ↔
      (m.degree f ≤ m.degree g ∧ (g = 0 → f = 0)) := by
  classical
  wlog! +distrib h : f ≠ 0 ∧ g ≠ 0
  · rcases h with h | h
    · simp [h]
    · simp_rw [h]
      aesop
  simp [m.withBotDegree_eq, h]

-- lemma le_withBotDegree_iff_of_ne_zero (a) (hg : g ≠ 0) :
--     a ≤ m.toWithBotSyn (m.withBotDegree g) ↔ a.unbotD 0 ≤ m.toSyn (m.degree g) := by
--   rw [le_withBotDegree_iff]
--   wlog! ha : a ≠ ⊥
--   · simp [ha]
--   rw [← a.coe_unbot ha, WithBot.coe_le_coe, WithBot.unbotD_coe]
--   simp [hg, ha]

variable {g} in
lemma withBotDegree_le_withBotDegree_iff_of_ne_zero (hg : g ≠ 0) :
    m.withBotDegree f ≼'[m] m.withBotDegree g ↔ m.degree f ≼[m] m.degree g := by
  simp [withBotDegree_le_withBotDegree_iff, hg]

lemma withBotDegree_lt_withBotDegree_iff :
    m.withBotDegree f ≺'[m] m.withBotDegree g ↔
      (m.degree f ≺[m] m.degree g ∨ (f = 0 ∧ g ≠ 0)) := by
  classical
  by_cases! hg : g = 0
  · simp_rw [toWithBotSyn_apply]
    aesop
  by_cases! hf : f = 0
  · simp [hg, hf, bot_lt_iff_ne_bot, toWithBotSyn_apply]
  simp [m.withBotDegree_eq, hf, hg, m.toWithBotSyn_apply]

lemma withBotDegree_lt_withBotDegree_iff_of_ne_zero (hf : f ≠ 0) :
    m.withBotDegree f ≺'[m] m.withBotDegree g ↔ m.degree f ≺[m] m.degree g := by
  simp [withBotDegree_lt_withBotDegree_iff, hf]

lemma withBotDegree_eq_withBotDegree_iff :
    m.withBotDegree f = m.withBotDegree g ↔ (m.degree f = m.degree g ∧ (f = 0 ↔ g = 0)) := by
  classical
  wlog! +distrib h : f ≠ 0 ∧ g ≠ 0
  · rcases h with h | h
    all_goals
      simp_rw [h]
      revert f g
      simp [m.withBotDegree_eq, m.degree_zero]
  simp [h, m.withBotDegree_eq]

lemma withBotDegree_add_le :
    (m.toWithBotSyn <| m.withBotDegree (f + g)) ≤
      (m.toWithBotSyn <| m.withBotDegree f) ⊔ (m.toWithBotSyn <| m.withBotDegree g) := by
  wlog! +distrib h : f ≠ 0 ∧ g ≠ 0
  · rcases h with h | h <;> simp [h, m.toWithBotSyn_apply]
  simpa [withBotDegree_le_withBotDegree_iff, h] using degree_add_le (R := R)

lemma withBotDegree_add_of_lt (h : m.withBotDegree g ≺'[m] m.withBotDegree f) :
    m.withBotDegree (f + g) = m.withBotDegree f := by
  wlog! hg : g ≠ 0
  · simp [hg]
  simp? [withBotDegree_lt_withBotDegree_iff, hg] at h says
    simp only [withBotDegree_lt_withBotDegree_iff, hg, ne_eq, false_and, or_false] at h
  simp? [withBotDegree_eq_withBotDegree_iff, show f ≠ 0 by contrapose h; simp [h]] says
    simp only [withBotDegree_eq_withBotDegree_iff, show f ≠ 0 by contrapose h; simp [h], iff_false]
  rw [← and_imp_iff_and]
  refine ⟨m.degree_add_of_lt h, ?_⟩
  intro h'
  contrapose! h
  simp [← h ▸ h']

lemma withBotDegree_add_of_right_lt (h : m.withBotDegree f ≺'[m] m.withBotDegree g) :
    m.withBotDegree (f + g) = m.withBotDegree g := by
  rw [add_comm]
  exact m.withBotDegree_add_of_lt _ _ h

lemma withBotDegree_sum_le {α : Type*} {s : Finset α} {f : α → MvPolynomial σ R} :
    (m.toWithBotSyn <| m.withBotDegree <| ∑ x ∈ s, f x) ≤
      s.sup fun x ↦ (m.toWithBotSyn <| m.withBotDegree <| f x) := by
  induction s using Finset.cons_induction_on with
  | empty => simp
  | cons a s haA h =>
    rw [Finset.sum_cons, Finset.sup_cons]
    exact le_trans (m.withBotDegree_add_le _ _) (max_le_max le_rfl h)

lemma le_withBotDegree {f : MvPolynomial σ R} {d : σ →₀ ℕ} (hd : d ∈ f.support) :
    d ≼'[m] m.withBotDegree f := by
  classical
  simp [withBotDegree_eq, toWithBotSyn_apply, ne_zero_iff.mpr ⟨d, by simpa using hd⟩, le_degree hd]

lemma withBotDegree_le_withBotDegree_of_support_subset
    {p q : MvPolynomial σ R} (h : p.support ⊆ q.support) :
    m.withBotDegree p ≼'[m] m.withBotDegree q := by
  wlog! hq : q ≠ 0
  · simpa [hq] using h
  rw [m.withBotDegree_le_withBotDegree_iff_of_ne_zero _ hq]
  exact m.degree_le_degree_of_support_subset h

lemma withBotDegree_rename_killCompl_le_withBotDegree {σ'} {f : σ' → σ}
    (hf : f.Injective) (p : MvPolynomial σ R) :
    m.withBotDegree ((p.killCompl hf).rename f) ≼'[m] m.withBotDegree p :=
  m.withBotDegree_le_withBotDegree_of_support_subset (support_rename_killCompl_subset hf)

end Semiring

end WithBotDegree

end

end MonomialOrder
