import Mathlib.Data.Finsupp.Basic
import Mathlib.Data.MvPolynomial.Basic
import Mathlib.Data.MvPolynomial.CommRing
import Mathlib.RingTheory.Polynomial.Basic
import Mathlib.Tactic.LibrarySearch
import Basic
import TermOrder


namespace MvPolynomial

open BigOperators
open Classical
open Finset
variable {σ : Type _}

variable [LinearOrder (TermOrder (σ →₀ ℕ))]
variable [CovariantClass (TermOrder (σ→₀ℕ)) (TermOrder (σ→₀ℕ)) (·+·) (·≤·)]
variable [ZeroLEClass (TermOrder (σ→₀ℕ))]


variable [CommSemiring R]
variable (p q: MvPolynomial σ R)
variable (p_ne_zero: p ≠ 0)
variable (q_ne_zero: q ≠ 0)


def multideg: TermOrder (σ →₀ ℕ) :=
  p.support.sup (α:=TermOrder (σ→₀ℕ)) (β:=TermOrder (σ→₀ℕ)) id
-- #align mv_polynomial.multideg MvPolynomial.multideg

-- TODO: remove multideg'
def multideg' : TermOrder (σ →₀ ℕ) :=
p.support.max' (α:=TermOrder (σ→₀ℕ))
(Finset.nonempty_of_ne_empty (support_eq_empty.not.mpr p_ne_zero))
-- #align mv_polynomial.multideg' MvPolynomial.multideg'

def multideg'' : WithBot (TermOrder (σ→₀ℕ)) :=
  p.support.max (α:=TermOrder (σ→₀ℕ))

noncomputable def leading_coeff : R :=
  if p_ne_zero': p ≠ 0
  then coeff (multideg' p p_ne_zero') p
  else 0
#align mv_polynomial.leading_coeff MvPolynomial.leading_coeff


noncomputable def leading_term : MvPolynomial σ R :=
  if p_ne_zero': p ≠ 0
  then monomial (multideg' p p_ne_zero') (coeff (multideg' p p_ne_zero') p)
  else 0
#align mv_polynomial.leading_term MvPolynomial.leading_term

noncomputable def lm : MvPolynomial σ R :=
  if p_ne_zero': p ≠ 0
  then monomial (multideg' p p_ne_zero') (1 : R)
  else 0
#align mv_polynomial.lm MvPolynomial.lm

-- instance WithBot (TermOrder (σ→₀ℕ))

-- set_option trace.Meta.synthInstance true in
-- #check (inferInstance : WellFoundedRelation (WithBot (TermOrder (σ→₀ℕ))))

lemma multideg_apply :
multideg p = p.support.sup (α:=TermOrder (σ→₀ℕ)) (β:=TermOrder (σ→₀ℕ)) id
  := rfl

lemma multideg'_apply :
multideg' p p_ne_zero = p.support.max' (α:=TermOrder (σ→₀ℕ))
(Finset.nonempty_of_ne_empty (support_eq_empty.not.mpr p_ne_zero)) := rfl

lemma multideg''_apply :
  p.multideg'' = p.support.max (α:=TermOrder (σ→₀ℕ)) := rfl

lemma multideg'_eq_multideg {p: MvPolynomial σ R} (p_ne_zero: p ≠ 0):
  multideg' p p_ne_zero = multideg p := by
  unfold multideg multideg' Finset.max'
  -- library_search
  exact Finset.sup'_eq_sup (multideg'.proof_1 p p_ne_zero) id


lemma multideg''_def :
  p.multideg'' =
    if p = 0 then (⊥:  WithBot (TermOrder (σ →₀ ℕ))) else p.multideg :=
by
  by_cases hp : p = 0
  ·
    simp [multideg'', hp]
  ·
    -- simp? [multideg'', hp, ←multideg'_eq_multideg hp, Finset.max, multideg']
    simp only [multideg'', Finset.max, ← multideg'_eq_multideg hp,
              multideg', ne_eq, hp, ite_false]
    rw [Finset.max'_eq_sup', coe_sup']
    rfl


lemma multideg'_eq_multideg'' {p : MvPolynomial σ R} (p_ne_zero: p ≠ 0):
  p.multideg' p_ne_zero = p.multideg'' := by
  simp [p_ne_zero, multideg''_def, multideg'_eq_multideg]


@[simp]
lemma multideg_zero: multideg (0 : MvPolynomial σ R) = 0 := rfl

@[simp]
lemma multideg''_zero : multideg'' (0 : MvPolynomial σ R) = ⊥ := by
  unfold multideg''
  rw [support_eq_empty.mpr (rfl), max_empty]

@[simp]
lemma multideg_C: multideg (C a: MvPolynomial σ R) = 0 := by
  unfold multideg
  rw [C_apply, support_monomial]
  -- simp?
  simp only
  by_cases ha: a = 0
  · -- simp? [ha]
    simp only [ha, ite_true, sup_empty]
    rfl
  · -- simp? [ha]
    simp only [ha, ite_false, sup_singleton, id_eq]

@[simp]
lemma multideg''_C :
  multideg'' (C a : MvPolynomial σ R) = if a = 0 then ⊥ else 0 := by
  rw [multideg'', C_apply, support_monomial]
  by_cases ha : a = 0
  ·simp [ha]
  · simp [ha]
    rfl

lemma multideg_0: multideg (0: MvPolynomial σ R) = 0 := multideg_zero
lemma multideg''_0: multideg'' (0: MvPolynomial σ R) = ⊥ := multideg''_zero

@[simp]
lemma multideg_1: multideg (1: MvPolynomial σ R) = 0 := by
  rw [←C_1]
  exact multideg_C

@[simp]
lemma multideg''_1 [Nontrivial R] [Inhabited σ] : multideg'' (1 : MvPolynomial σ R) = 0 := by
  rw [multideg''_def]
  simp
  rfl

@[simp]
lemma multideg'_1 [Nontrivial R] [Inhabited σ]:
  multideg' (1 : MvPolynomial σ R) (one_ne_zero) = 0 := by
  rw [multideg'_eq_multideg]
  simp

@[simp]
lemma multideg'_1' (h1 : (1 : MvPolynomial σ R) ≠ 0):
  multideg' (1 : MvPolynomial σ R) h1 = 0 := by
  rw [multideg'_eq_multideg]
  simp

@[simp]
lemma ne_zero_of_multideg_ne_zero
  {p: MvPolynomial σ R} (h: multideg p ≠ 0):
  p ≠ 0 := by
  by_contra hp
  simp only [hp, multideg_zero, ne_eq, not_true] at h

lemma   coeff_eq_zero_of_multideg_lt {p: MvPolynomial σ R} (h: multideg p < s):
  coeff s p = 0 := by
  rw [←not_mem_support_iff]
  by_contra hs
  unfold multideg at h
  exact not_le_of_lt h (Finset.le_sup (f:=@id (TermOrder (σ→₀ℕ))) hs)

lemma coeff_eq_zero_of_multideg'_lt
    {p: MvPolynomial σ R} {p_ne_zero: p ≠ 0} (h: multideg' p p_ne_zero < s):
  coeff s p = 0 :=
    coeff_eq_zero_of_multideg_lt (multideg'_eq_multideg p_ne_zero ▸ h)

lemma le_multideg_of_coeff_ne_zero
    {p: MvPolynomial σ R} {s: TermOrder (σ→₀ℕ)} (h: coeff s p ≠ 0):
  s ≤ multideg p := by
  revert h
  rw [←not_imp_not, ←lt_iff_not_le]
  -- simp?
  simp only [ne_eq, not_not]
  exact coeff_eq_zero_of_multideg_lt


lemma le_multideg'_of_coeff_ne_zero
    {p: MvPolynomial σ R} {p_ne_zero: p ≠ 0} {s: TermOrder (σ→₀ℕ)}
    (h: coeff s p ≠ 0):
  s ≤ multideg' p p_ne_zero :=
  multideg'_eq_multideg p_ne_zero ▸ le_multideg_of_coeff_ne_zero h

lemma multideg'_in_support: multideg' p p_ne_zero ∈ p.support :=
  (show Finset (TermOrder (σ→₀ℕ)) from p.support).max'_mem _
#align mv_polynomial.multideg'_in_support MvPolynomial.multideg'_in_support

lemma leading_coeff_def_of_p_ne_zero {p: MvPolynomial σ R} (p_ne_zero: p≠0):
  leading_coeff p = coeff (multideg' p p_ne_zero) p := by
  unfold leading_coeff
  -- simp? [p_ne_zero]
  simp only [ne_eq, p_ne_zero, not_false_iff, multideg'_eq_multideg,
            dite_eq_ite, ite_true]

lemma multideg_mem_support_iff_p_ne_zero :
  p.multideg ∈ p.support ↔ p ≠ 0 := by
  constructor
  ·
    intro h
    exact support_eq_empty.not.mp (Finset.ne_empty_of_mem h)
  ·
    intro h
    rw [←multideg'_eq_multideg h]
    exact multideg'_in_support p h

lemma lm_def_of_ne_zero {p : MvPolynomial σ R} (h : p ≠ 0) :
  p.lm = monomial p.multideg 1 := by simp [lm, h, multideg'_eq_multideg]

@[simp]
lemma leading_coeff_eq_zero_iff: leading_coeff p = 0 ↔ p = 0 := by
  constructor
  ·
    intro hlc
    by_contra h
    -- simp? [h, lc] at hlc
    simp only [leading_coeff, ne_eq, h, not_false_iff, dite_true] at hlc
    exact (mem_support_iff.mp (multideg'_in_support p h)) hlc
  ·
    intro hp
    rw [hp]
    rfl

lemma leading_coeff_def: leading_coeff p = coeff (multideg p) p := by
  by_cases hp: p = 0
  ·
    rw [hp]
    -- simp? [leading_coeff_eq_zero_iff]
    simp only [multideg_zero, coeff_zero, leading_coeff_eq_zero_iff]
  ·
    unfold leading_coeff
    -- simp? [hp, multideg'_eq_multideg]
    simp only [ne_eq, hp, not_false_iff, multideg'_eq_multideg,
              dite_eq_ite, ite_true]

lemma multideg'_iff {p: MvPolynomial σ R} (p_ne_zero: p ≠ 0):
  s = multideg' p p_ne_zero ↔ (p.coeff s ≠ 0 ∧ ∀ s' > s, p.coeff s' = 0)
  := by
  constructor
  ·
    intros hs
    rw [hs, ←leading_coeff_def_of_p_ne_zero p_ne_zero]
    constructor
    ·exact (leading_coeff_eq_zero_iff p).not.mpr p_ne_zero
    ·
      intros s' hs'
      exact coeff_eq_zero_of_multideg'_lt hs'
  ·
    rintro ⟨hsp, hs'⟩
    rw [eq_iff_le_not_lt]
    constructor
    ·
      exact le_multideg'_of_coeff_ne_zero hsp
    ·
      by_contra hs
      specialize hs' (p.multideg' p_ne_zero) hs
      rw [←leading_coeff_def_of_p_ne_zero p_ne_zero] at hs'
      exact p_ne_zero ((leading_coeff_eq_zero_iff p).mp hs')

@[simp]
lemma leading_term_eq_zero_iff: leading_term p = 0 ↔ p = 0 := by
  constructor
  ·
    intro hp
    -- simp? [leading_term] at hp
    simp only [leading_term, ne_eq, dite_not, dite_eq_left_iff,
                monomial_eq_zero] at hp
    by_contra hp'
    exact mem_support_iff.mp (multideg'_in_support p hp') (hp hp')
  ·
    intro hp
    rw [hp]
    rfl

@[simp]
lemma leading_term_0 : leading_term (0 : MvPolynomial σ R) = 0 := by
  -- simp?
  simp only [ne_eq, leading_term_eq_zero_iff]


lemma leading_term_def:
  leading_term p = monomial (multideg p) p.leading_coeff := by
  by_cases hp: p = 0
  ·
    -- simp? [leading_term_eq_zero_iff, leading_coeff, hp]
    simp only [multideg_zero, leading_coeff, ne_eq, not_true, coeff_zero, hp,
                dite_eq_ite, ite_self, map_zero, leading_term_eq_zero_iff]
  ·
    -- simp? [hp, multideg'_eq_multideg, leading_term, leading_coeff]
    simp only [leading_term, ne_eq, hp, not_false_eq_true,
                multideg'_eq_multideg, dite_eq_ite, ite_true, leading_coeff]

lemma leading_term_def' :
  p.leading_term = p.leading_coeff • p.lm := by
  rw [leading_term_def, lm]
  by_cases h : p = 0
  ·simp [h]
  ·simp [h, multideg'_eq_multideg]


lemma leading_coeff_smul_lm_eq_leading_term:
  leading_coeff p • lm p =leading_term p := by
  unfold leading_coeff lm leading_term
  by_cases hp: p = 0
  ·
    -- simp? [hp]
    simp only [hp, ne_eq, coeff_zero, dite_eq_ite, dite_false, smul_zero]
  ·
    -- simp? [hp]
    simp only [ne_eq, hp, not_false_iff, dite_true]
    rw [smul_monomial, smul_eq_mul, mul_one]


lemma lm_eq_zero_iff: lm p = 0 ↔ p = 0 := by
  unfold lm
  constructor
  ·
    intro hlm
    -- simp? at hlm
    simp only [ne_eq, dite_not, dite_eq_left_iff, monomial_eq_zero] at hlm
    by_cases if_trivial: Nontrivial R
    ·
      by_contra hp
      exact one_ne_zero (hlm hp)
    ·
      have all_mem_eq := nontrivial_iff.not.mp if_trivial
      push_neg at all_mem_eq
      specialize all_mem_eq 1 0
      rw [←mul_one p, ←C_1, ←C_0, all_mem_eq]
      simp only [map_zero, mul_zero]
  ·
    intro hp
    simp only [hp, ne_eq, not_true, dite_false]


variable {p q: MvPolynomial σ R} (p_ne_zero: p≠0) (q_ne_zero: q≠0)

lemma multideg_le_iff_multideg''_le {s : TermOrder (σ→₀ℕ)} :
  p.multideg'' ≤ s ↔ p.multideg ≤ s := by
  by_cases hp : p = 0
  ·simp [hp]
  ·simp [←multideg'_eq_multideg'' hp, multideg'_eq_multideg]

lemma le_multideg_of_le_multideg'' {s : TermOrder (σ→₀ℕ)}
  (h : s ≤ p.multideg'') : s ≤ p.multideg := by
  by_cases hp : p = 0
  ·simp [hp] at h
  · simp [←multideg'_eq_multideg'' hp, multideg'_eq_multideg] at h
    exact h

lemma multideg_le_multideg_of_multideg''_le_multideg''
  (h : p.multideg'' ≤ q.multideg'') : p.multideg ≤ q.multideg := by
  by_cases hp : p = 0
  ·simp [hp]
  · by_cases hq : q = 0
    ·simp [hq, multideg''_def, hp] at h
    · simp [←multideg'_eq_multideg'', hp, hq, multideg'_eq_multideg] at h
      exact h


lemma le_multideg' {i: TermOrder (σ→₀ℕ)} (h: i ∈ p.support):
  i ≤ p.multideg' p_ne_zero
  := Finset.le_max' (α:=TermOrder (σ→₀ℕ)) p.support i h

lemma le_multideg'' {i: TermOrder (σ→₀ℕ)} (h: i ∈ p.support):
  i ≤ p.multideg'' := by
  by_cases hp : p = 0
  · simp [hp] at h
  · simp [←multideg'_eq_multideg'' hp, le_multideg' hp h]

lemma le_multideg {i: TermOrder (σ→₀ℕ)} (h: i ∈ p.support): i ≤ p.multideg
  := by
  by_cases hp: p = 0
  ·
    rw [←support_eq_empty] at hp
    -- simp? [hp] at h
    simp only [hp, Finset.not_mem_empty] at h
  ·
    exact multideg'_eq_multideg hp ▸ le_multideg' hp h

theorem multideg'_mul_le (pq_ne_zero: p * q ≠ 0):
  multideg' (p * q) pq_ne_zero ≤
      (multideg' p (ne_zero_and_ne_zero_of_mul pq_ne_zero).1) +
      (multideg' q (ne_zero_and_ne_zero_of_mul pq_ne_zero).2) := by
  have pqsup_nonempty :=
    Finset.nonempty_of_ne_empty (support_eq_empty.not.mpr pq_ne_zero)
  unfold multideg'
  have mul_sup := support_mul p q
  have h :=
    mem_of_subset mul_sup (max'_mem (α:=TermOrder (σ→₀ℕ)) _ pqsup_nonempty)
  rw [Finset.mem_biUnion] at h
  rcases h with ⟨a, ha, h⟩
  rw [Finset.mem_biUnion] at h
  rcases h with ⟨b, hb, h⟩
  rw [Finset.mem_singleton] at h
  rw [h]
  apply add_le_add
  ·exact Finset.le_max' (α:=TermOrder (σ→₀ℕ)) _ _ ha
  ·exact Finset.le_max' (α:=TermOrder (σ→₀ℕ)) _ _ hb

lemma multideg_mul_le: multideg (p * q) ≤ multideg p + multideg q := by
  by_cases hpq: p * q = 0
  ·exact hpq.symm ▸ ZeroLEClass.zero_le (multideg p + multideg q)
  ·
    rw [←multideg'_eq_multideg hpq,
        ←multideg'_eq_multideg (ne_zero_and_ne_zero_of_mul hpq).1,
        ←multideg'_eq_multideg (ne_zero_and_ne_zero_of_mul hpq).2]
    exact multideg'_mul_le hpq


lemma coeff_multideg'_add_mul:
  (p * q).coeff (multideg' p p_ne_zero + multideg' q q_ne_zero) =
  leading_coeff p * leading_coeff q :=
by
  rw [coeff_mul]
  unfold leading_coeff
  -- simp? [p_ne_zero, q_ne_zero]
  simp only [ne_eq, p_ne_zero, not_false_iff, dite_true, q_ne_zero]
  let deg'p: σ→₀ℕ := multideg' p p_ne_zero
  let deg'q: σ→₀ℕ := multideg' q q_ne_zero
  rw [Finset.sum_eq_add_sum_diff_singleton
        (i:=(deg'p, deg'q))
        (by rw [Finsupp.mem_antidiagonal])]
  rw [(_:
      ((deg'p + deg'q).antidiagonal \ {(deg'p, deg'q)}).sum
        (fun x => p.coeff x.1 * q.coeff x.2) = 0
      )]
  exact add_zero _
  rw [←Finset.sum_coe_sort]
  let s := (deg'p + deg'q).antidiagonal \ {(deg'p, deg'q)}
  have: ∀(i: s),
          p.coeff (i:(σ→₀ℕ)×(σ→₀ℕ)).1 * q.coeff (i:(σ→₀ℕ)×(σ→₀ℕ)).2 = 0
  := by
    -- simp? [-not_and]
    simp only [Subtype.forall, Finsupp.mem_antidiagonal, not_true,
                Finset.mem_sdiff, Finset.mem_singleton, and_imp, Prod.forall,
                Prod.mk.injEq]
    rintro (a: TermOrder (σ→₀ℕ)) (b: TermOrder (σ→₀ℕ)) hab hab'
    by_contra'
    let ⟨ha, hb⟩ := ne_zero_and_ne_zero_of_mul this
    rw [←mem_support_iff] at ha
    rw [←mem_support_iff] at hb
    have ha' := le_multideg' p_ne_zero ha
    have hb' := le_multideg' q_ne_zero hb
    by_cases ha: a = deg'p
    ·
      rw [ha] at hab
      have hb:= add_left_cancel hab
      exact hab' ⟨ha, hb⟩
    ·
      have key :=
        calc
          deg'p + deg'q
          _ = a + b := hab.symm
          _ < multideg' p _ + b := add_lt_add_right (lt_of_le_of_ne ha' ha) b
      exact ((lt_iff_not_ge _ _).mp (lt_of_add_lt_add_left key)) hb'
  -- simp? [this]
  simp only [Finset.sum_const_zero, this]


lemma multideg'_mul [NoZeroDivisors R]:
  multideg' (p*q) (mul_ne_zero_iff.mpr ⟨p_ne_zero, q_ne_zero⟩) =
    p.multideg' p_ne_zero + q.multideg' q_ne_zero
  := by
  have pq_ne_zero := mul_ne_zero_iff.mpr ⟨p_ne_zero, q_ne_zero⟩
  rw [le_antisymm_iff]
  constructor
  ·exact multideg'_mul_le pq_ne_zero
  ·
    -- conv_rhs => rw [multideg']
    apply Finset.le_max'
    refine mem_support_iff.mpr ?_
    rw [coeff_multideg'_add_mul p_ne_zero q_ne_zero]
    -- simp? [leading_coeff_eq_zero_iff, p_ne_zero, q_ne_zero]
    simp only [ne_eq, mul_eq_zero, leading_coeff_eq_zero_iff, p_ne_zero,
                q_ne_zero, or_self, not_false_eq_true]

lemma multideg''_mul [NoZeroDivisors R] :
  (p*q).multideg''  = p.multideg'' + q.multideg'' := by
  by_cases hpq : p = 0 ∨ q = 0
  ·cases' hpq with h h <;> simp [h]
  ·
    push_neg at hpq
    simp_rw [←multideg'_eq_multideg'' hpq.1, ←multideg'_eq_multideg'' hpq.2,
              ←multideg'_eq_multideg'' (mul_ne_zero_iff.mpr hpq)]
    rw [←WithBot.coe_add]
    simp [multideg'_mul hpq.1 hpq.2]

@[simp]
lemma multideg_eq_zero_iff: multideg q = 0 ↔ ∃ c : R, q = C c := by
  constructor
  ·
    intros hq
    rw [multideg_apply, ] at hq
    -- rw [zero_le'''] at this
    have := (Finset.sup_eq_bot_iff (α:=TermOrder (σ→₀ℕ)) id q.support).mp hq
    by_cases hq' : q.support.Nonempty
    ·
      have : q.support = {0} :=
          eq_singleton_iff_nonempty_unique_mem.mpr ⟨hq', this⟩
      use q.coeff 0
      rw [as_sum q]
      -- simp? [this]
      simp only [this, sum_singleton, monomial_zero', coeff_C, ite_true]
    ·
      use 0
      -- simp? [hq, ←support_eq_empty, not_nonempty_iff_eq_empty.mp hq']
      simp only [map_zero, ne_eq, ←support_eq_empty,
                  not_nonempty_iff_eq_empty.mp hq']
  ·
    rintro ⟨c, hq⟩
    -- simp? [hq]
    simp only [hq, multideg_C]

@[simp]
lemma leading_coeff_mul [NoZeroDivisors R]:
  leading_coeff (p * q) = leading_coeff p * leading_coeff q := by
  by_cases hpq: p * q = 0
  ·
    cases' (mul_eq_zero.mp hpq) with h h
    <;> simp [h, (leading_coeff_eq_zero_iff _).mpr]
  ·
    let ⟨p_ne_zero, q_ne_zero⟩ := mul_ne_zero_iff.mp hpq
    rw [←coeff_multideg'_add_mul]
    unfold leading_coeff
    -- simp? [p_ne_zero, q_ne_zero, multideg'_mul]
    simp only [ne_eq, mul_eq_zero, p_ne_zero, q_ne_zero, or_self,
              not_false_iff, multideg'_mul, dite_eq_ite, ite_true]
    exact p_ne_zero
    exact q_ne_zero
@[simp]
lemma leading_coeff_C: leading_coeff (C a: MvPolynomial σ R) = a := by
  rw [leading_coeff_def, multideg_C]
  -- simp?
  simp only [coeff_C, ite_true]

@[simp]
lemma leading_coeff_0: leading_coeff (0: MvPolynomial σ R) = 0 := rfl

@[simp]
lemma leading_coeff_1: leading_coeff (1: MvPolynomial σ R) = 1 :=
(C_1 (R:=R) (σ:=σ)).symm ▸ leading_coeff_C

@[simp]
lemma leading_term_1 : leading_term (1 : MvPolynomial σ R) = 1 := by
  simp [leading_term_def]
  rfl

lemma lm_mul [NoZeroDivisors R]: lm (p * q) = lm p * lm q := by
  by_cases hpq: p * q = 0
  ·
    cases' (mul_eq_zero.mp hpq) with h h <;> simp [h, (lm_eq_zero_iff _).mpr]
  ·
    let ⟨p_ne_zero, q_ne_zero⟩ := mul_ne_zero_iff.mp hpq
    simp [lm, p_ne_zero, q_ne_zero, multideg'_mul p_ne_zero q_ne_zero]

@[simp]
lemma lm_0 : lm (0 : MvPolynomial σ R) = 0 := by simp [lm]

@[simp]
lemma lm_1 : lm (1 : MvPolynomial σ R) = 1 := by
  rw [lm]
  by_cases h : 1 = (0 : MvPolynomial σ R)
  · simp [h]
  · simp [h]
    rfl

lemma leading_term_mul [NoZeroDivisors R]:
  leading_term (p * q) = leading_term p * leading_term q := by
  repeat rw [←leading_coeff_smul_lm_eq_leading_term]
  repeat rw [lm_mul, leading_coeff_mul]
  rw [smul_mul_smul]

noncomputable def MultiplicativeWithBotTermOrderMulZeroOneClass :
  MulZeroOneClass (Multiplicative (WithBot (TermOrder (σ →₀ ℕ)))) := {
  one := 1
  one_mul := by simp
  mul_one := by simp
  zero := ⊥,
  zero_mul := by
    intro x
    let x : WithBot (TermOrder (σ →₀ ℕ)) := x
    show ⊥ + x = ⊥
    simp [WithBot.map_bot (·+x)]
  mul_zero := by
    intro x
    let x : WithBot (TermOrder (σ →₀ ℕ)) := x
    show x + ⊥ = ⊥
    simp [WithBot.map_bot (x+·)]
}

private noncomputable instance :
  MulZeroOneClass (Multiplicative (WithBot (TermOrder (σ →₀ ℕ)))) :=
MultiplicativeWithBotTermOrderMulZeroOneClass

noncomputable def multideg''MulHomo'
  [Nontrivial R] [Inhabited σ] [NoZeroDivisors R]:
  MvPolynomial σ R →*₀ Multiplicative (WithBot (TermOrder (σ→₀ℕ))) := {
    toFun := multideg'',
    map_zero' := multideg''_zero,
    map_one' := multideg''_1,
    map_mul' := fun x y => multideg''_mul (p:=x) (q:=y)
  }
noncomputable def leading_coeffMulHomo [NoZeroDivisors R]:
  MvPolynomial σ R →*₀ R :=
{
  toFun := leading_coeff
  map_one' := C_1 (R:=R) (σ:=σ) ▸ leading_coeff_C (a:=1)
  map_mul' := by simp only [leading_coeff_mul, forall_const]
  map_zero' := by simp [leading_coeff]
}

noncomputable def lmMulHomo [NoZeroDivisors R]:
  MvPolynomial σ R →*₀ MvPolynomial σ R :=
{
  toFun := lm,
  map_one' := lm_1,
  map_zero' := lm_0,
  map_mul' := fun p q => lm_mul (p:=p) (q:=q)
}

noncomputable def leading_termMulHomo [NoZeroDivisors R] :
  MvPolynomial σ R →*₀ MvPolynomial σ R :=
{
  toFun := leading_term,
  map_zero' := leading_term_0,
  map_one' := leading_term_1,
  map_mul' := fun x y => leading_term_mul (p:=x) (q:=y)
}

lemma multideg_add_le_right
    {p q: MvPolynomial σ R} (h: multideg p <= multideg q):
  multideg (p + q) ≤ multideg q := by
  -- conv_lhs => unfold multideg
  nth_rewrite 1 [multideg]
  -- simp?
  simp only [Finset.sup_le_iff, id_eq]
  intros b hb
  rw [mem_support_iff, coeff_add] at hb
  by_contra hq
  -- simp? at hq
  simp only [ne_eq, not_le] at hq
  apply hb
  rw [coeff_eq_zero_of_multideg_lt hq,
      coeff_eq_zero_of_multideg_lt (lt_of_le_of_lt h hq), add_zero]

lemma multideg_add_le_left
    {p q: MvPolynomial σ R} (h: multideg q <= multideg p):
  multideg (p + q) ≤ multideg p := add_comm p q ▸ multideg_add_le_right h

theorem multideg_add_le: multideg (p+q) ≤ max p.multideg q.multideg := by
  -- simp?
  simp only [ne_eq, ge_iff_le, le_max_iff]
  by_cases h: multideg p <= multideg q
  ·
    right
    exact multideg_add_le_right h
  ·
    left
    -- simp? at h
    simp only [ne_eq, not_le] at h
    exact multideg_add_le_left (le_of_lt h)

theorem multideg''_add_le : multideg'' (p+q) ≤ max p.multideg'' q.multideg''
:= by
  by_cases h : p = 0 ∨ q = 0
  ·cases' h with h h <;> simp [h]
  ·
    by_cases hpq : p + q = 0
    ·simp [hpq]
    push_neg at h
    cases' h with hp hq
    rw [←multideg'_eq_multideg'' hp, ←multideg'_eq_multideg'' hq,
        ←multideg'_eq_multideg'' hpq, multideg'_eq_multideg,
          multideg'_eq_multideg, multideg'_eq_multideg]
    have := multideg_add_le (p:=p) (q:=q)
    simp at this
    simp [this]

lemma multideg_add_eq_right
    {p q: MvPolynomial σ R} (h: multideg p < multideg q):
  multideg (p+q) = multideg q := by
  have q_ne_zero :=
    ne_zero_of_multideg_ne_zero $ ne_bot_of_gt h
  have h₁: coeff (multideg q) (p + q) ≠ 0 := by
    rw [coeff_add, coeff_eq_zero_of_multideg_lt h,
        zero_add, ←leading_coeff_def]
    exact (leading_coeff_eq_zero_iff q).not.mpr q_ne_zero
  have pq_ne_zero : p + q ≠ 0 := ne_zero_iff.mpr ⟨q.multideg, h₁⟩
  apply Eq.symm
  rw [←multideg'_eq_multideg pq_ne_zero, multideg'_iff]
  constructor
  ·exact h₁
  ·
    intros s' hs'
    rw [coeff_add,
        coeff_eq_zero_of_multideg_lt hs',
        coeff_eq_zero_of_multideg_lt (lt_trans h hs'),
        zero_add]

lemma multideg_add_eq_left
    {p q: MvPolynomial σ R} (h: multideg q < multideg p):
  multideg (p+q) = multideg p :=
  add_comm p q ▸ multideg_add_eq_right h

-- TODO: mathlib
theorem monomial_add {s : σ →₀ ℕ} {a b : R} :
    monomial s (a+b) = monomial s a + monomial s b :=
AddMonoidAlgebra.single_add s a b
lemma multideg_add_lt_left_iff (h : q.multideg ≤ p.multideg)
  (h₂: p + q ≠ 0):
  multideg (p+q) < p.multideg ↔ leading_term p + leading_term q = 0 := by
  rw [leading_term_def, leading_term_def]
  constructor
  · intro hpq
    have hcoeff : (p+q).coeff p.multideg = 0 := by
      rw [coeff_eq_zero_of_multideg_lt]
      exact hpq
    have p_ne_zero : p ≠ 0 := by
      apply ne_zero_of_multideg_ne_zero
      exact (ne_of_lt (lt_of_le_of_lt (zero_le''' (p+q).multideg) hpq)).symm
    have hdeg : p.multideg = q.multideg := by
      by_contra hdeg
      rw [coeff_add] at hcoeff
      rw [coeff_eq_zero_of_multideg_lt (lt_of_le_of_ne' h hdeg)] at hcoeff
      simp [←leading_coeff_def, p_ne_zero] at hcoeff
    rw [hdeg, ←monomial_add]
    rw [monomial_eq_zero, leading_coeff_def, leading_coeff_def,
        ←hdeg, ←coeff_add]
    exact hcoeff
  · intro hpq
    apply lt_of_le_of_ne (multideg_add_le_left h)
    by_contra hpq'
    by_cases hpq'' : p.multideg = q.multideg
    · rw [←hpq'', ←hpq', ←monomial_add, monomial_eq_zero] at hpq
      rw [leading_coeff_def, leading_coeff_def, ←hpq'', ←hpq'] at hpq
      rw [←coeff_add, ←leading_coeff_def, leading_coeff_eq_zero_iff] at hpq
      exact h₂ hpq
    · change @Ne (σ →₀ ℕ) p.multideg q.multideg at hpq''
      have hp := hpq.symm ▸ coeff_zero p.multideg
      simp [hpq''.symm] at hp
      have hq := hpq.symm ▸ coeff_zero q.multideg
      simp [hpq''] at hq
      simp [hp, hq] at h₂

lemma coeff_multideg'_ne_zero : p.coeff (p.multideg' p_ne_zero) ≠ 0 :=
mem_support_iff.mp (multideg'_in_support p p_ne_zero)

variable (p q : MvPolynomial σ R) (s : σ→₀ℕ) (a : R)

@[simp]
lemma multideg_monomial : multideg (monomial s a) = if a = 0 then 0 else s :=
by
  by_cases ha : a = 0
  ·-- simp? [ha]
    simp only [ha, map_zero, multideg_zero, ite_true]
  ·-- simp? [ha, multideg, support_monomial]
    simp only [multideg, support_monomial, ha, ite_false, sup_singleton, id_eq]

@[simp]
lemma multideg''_monomial :
  multideg'' (monomial s a) =
    if a = 0 then ⊥ else (s : WithBot (σ→₀ℕ)) :=
by
  by_cases ha : a = 0
  · simp [ha]
  · simp [ha, multideg'', support_monomial]

@[simp]
lemma leading_coeff_monomial : leading_coeff (monomial s a) = a := by
  by_cases ha : a = 0 <;> simp [leading_coeff_def, multideg_monomial, ha]


@[simp] lemma multideg_leading_term : p.leading_term.multideg = p.multideg :=
by
  rw [leading_term_def]
  -- simp?
  simp only [ne_eq, multideg_monomial, leading_coeff_eq_zero_iff,
            ite_eq_left_iff, not_forall, exists_prop, monomial_eq_zero,
            ite_eq_right_iff]
  intro hp
  -- simp? [hp]
  simp only [hp, multideg_zero]

@[simp] lemma multideg''_leading_term : p.leading_term.multideg'' = p.multideg'' :=
by
  rw [leading_term_def]
  simp [multideg''_def]
  by_cases h : p = 0 <;> simp [h]

@[simp] lemma multideg_lm : p.lm.multideg = p.multideg := by
  rw [lm]
  by_cases h : p = 0
  ·-- simp? [h]
    simp only [h, ne_eq, not_true, dite_false, multideg_zero]
  ·
    -- simp? [h]
    simp only [ne_eq, h, not_false_eq_true, dite_true, multideg_monomial]
    by_cases if_trivial: Nontrivial R
    ·-- simp? [multideg'_eq_multideg]
      simp only [one_ne_zero, multideg'_eq_multideg, ite_false]
    ·
      have all_mem_eq := nontrivial_iff.not.mp if_trivial
      push_neg at all_mem_eq
      -- simp?[all_mem_eq 1 0,p.leading_coeff_eq_zero_iff.mp (all_mem_eq _ _)]
      simp only [all_mem_eq 1 0, ite_true, multideg_zero,
                  p.leading_coeff_eq_zero_iff.mp (all_mem_eq _ _)]

@[simp] lemma leading_term_leading_term :
  p.leading_term.leading_term = p.leading_term := by
  rw [leading_term_def]
  nth_rewrite 3 [leading_term_def]
  -- simp? [monomial_eq_monomial_iff]
  simp only [multideg_leading_term, monomial_eq_monomial_iff, true_and]
  left
  rw [leading_term_def]
  -- simp?
  simp only [leading_coeff_monomial]

lemma leading_term_mul'_right [NoZeroDivisors R]:
  (p * q).leading_term = (p.leading_term * q).leading_term := by
  rw [leading_term_mul, leading_term_mul, leading_term_leading_term]

lemma leading_term_mul'_left [NoZeroDivisors R]:
  (p * q).leading_term = (p * q.leading_term).leading_term := by
  rw [leading_term_mul, leading_term_mul, leading_term_leading_term]

variable {R : Type _} [CommRing R] (p q : MvPolynomial σ R)

@[simp] lemma multideg_neg : (-p).multideg = p.multideg := by
  rw [multideg, multideg]
  simp only [support_neg]

@[simp] lemma multideg''_neg : (-p).multideg'' = p.multideg'' := by
  rw [multideg'', multideg'']
  simp only [support_neg]

@[simp] lemma leading_coeff_neg : (-p).leading_coeff = -p.leading_coeff := by
  -- simp? [leading_coeff_def]
  simp only [leading_coeff_def, multideg_neg, coeff_neg]

@[simp] lemma leading_term_neg : (-p).leading_term = -p.leading_term := by
  rw [leading_term_def, leading_term_def]
  -- simp?
  simp only [multideg_neg, leading_coeff_neg, map_neg]

lemma multideg_sub_lt_left_iff
  {p q : MvPolynomial σ R} (h : q.multideg ≤ p.multideg) (h₂ : p - q≠0) :
  multideg (p - q) < p.multideg ↔ p.leading_term = q.leading_term := by

  rw [sub_eq_add_neg] at h₂
  rw [←multideg_neg (p:=q)] at h
  rw [sub_eq_add_neg, multideg_add_lt_left_iff h h₂, leading_term_neg]
  rw [add_neg_eq_zero]

@[simp] lemma coeff_lm_of_ne_zero (h : p ≠ 0):
  coeff p.multideg p.lm = 1 := by
  -- simp? [lm, h, multideg'_eq_multideg]
  simp only [lm._eq_1, ne_eq, multideg_eq_zero_iff, not_exists, h,
              not_false_eq_true, multideg'_eq_multideg, dite_eq_ite,
              ite_true, coeff_monomial]

@[simp]
lemma sub_multideg_le [CommRing R₁] {p q: MvPolynomial σ R₁}
  (h: multideg q ≤ multideg p) :
  multideg (q - q.coeff p.multideg • p.lm) ≤ multideg p := by
  rw [smul_eq_C_mul, sub_eq_add_neg, ←neg_mul (C (coeff (multideg p) q)) p.lm]
  refine le_trans multideg_add_le ?_
  by_cases hc : q.coeff p.multideg = 0
  ·-- simp? [h, hc]
    simp only [hc, map_zero, neg_zero, zero_mul, multideg_zero,
                ge_iff_le, zero_le''', max_eq_left, h]
  ·
    -- simp? [h]
    simp only [neg_mul, multideg_neg, ge_iff_le, max_le_iff, h, true_and]
    refine le_trans multideg_mul_le ?_
    -- simp? [multideg_lm]
    simp only [multideg_C, multideg_lm, zero_add, le_refl]

@[simp]
lemma sub_multideg_lt [CommRing R₁] {p q: MvPolynomial σ R₁} (hp: p ≠ 0)
  (h: multideg q ≤ multideg p) :
  q - q.coeff p.multideg • lm p = 0 ∨
  multideg (q - q.coeff p.multideg • lm p) < multideg p := by
  by_cases h' : q - q.coeff p.multideg • lm p = 0
  ·
    left
    exact h'
  ·
    right
    apply lt_of_le_of_ne (sub_multideg_le h)
    by_contra hp'
    rw [←(leading_coeff_eq_zero_iff _).not, leading_coeff_def, hp'] at h'
    simp [hp] at h'


@[simp]
lemma sub_multideg''_lt [CommRing R₁] {p q: MvPolynomial σ R₁} (hp: p ≠ 0)
  (h: multideg q ≤ multideg p) :
  multideg'' (q - q.coeff p.multideg • lm p) < multideg'' p := by
  -- simp? [multideg''_def, hp]
  simp only [multideg''_def, ne_eq, multideg_eq_zero_iff,
              not_exists, hp, ite_false]
  by_cases h' : q - q.coeff p.multideg • lm p = 0
  ·
    -- simp? [h', WithBot.bot_lt_coe]
    simp only [h', multideg_zero, ite_true, WithBot.bot_lt_coe]
  ·
    -- simp? [hp, h']
    simp only [ne_eq, multideg_eq_zero_iff, not_exists, h',
                ite_false, WithBot.coe_lt_coe]
    exact (or_iff_right h').mp (sub_multideg_lt hp h)
