/-
Copyright (c) 2025 Junyu Guo and Hao Shen. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Junyu Guo, Hao Shen
-/
module

public import Mathlib
public import Groebner.MonomialOrder
public import Groebner.MonomialOrderEmbedding
public import Groebner.ToMathlib.Finsupp
public import Groebner.Ideal

/-! # Remainder

The following definition is abstracted from the "remainder" as in `MonomialOrder.div_set`. And more
properties of it is covered in this file.

* `MonomialOrder.IsRemainder m f B r`: Given a multivariate polynomial `f` and a "divisors" set `B`
  of with respect to a monomial order `m`. A polynomial `r` is called a remainder of `f` on
  division by `B` if there exists:

  1. Finite linear combination:
    `f = ∑ (g(b) * b) + r`;
  2. Degree condition:
     For each `b ∈ B`, the degree (with bot) of `g(b) * b` ≤ the degree (with bot) of `f` w.r.t.
     monomial order `m`;
  3. Remainder irreducibility:
    No term of `r` is divisible by the leading monomial of any non-zero `b ∈ B`.

And there're also some equivalent variants.

* `MonomialOrder.IsRemainder.isRemainder_def'`: A variant of `MonomialOrder.IsRemainder`
  without coercion of a `Set (MvPolynomial σ R)`.

* `MonomialOrder.IsRemainder.isRemainder_def''`: A variant of `MonomialOrder.IsRemainder` where
  `g : MvPolynomial σ R →₀ MvPolynomial σ R` is replaced with a
  function `g : MvPolynomial σ R → MvPolynomial σ R` without limitation on its support.

* `MonomialOrder.IsRemainder.isRemainder_iff_degree`: A variant where the degree condition is
  formalized with `m.degree`, which matches the statement of `MonomialOrder.div_set`.

* `MonomialOrder.IsRemainder.isRemainder_range`: A variant of `MonomialOrder.IsRemainder` where
  divisors are given as a family of polynomials.

There exists a remainder when the polynomial ring is communitive and any divisor either has an
invertible leading coefficient or is 0.

* `MonomialOrder.IsRemainder.exists_isRemainder`.

## Naming convention

Some theorems with an argument in type `Set (MvPolynomial σ R)` and a hypothesis that leading
coefficients of all polynomials in the set are invertible have 2 variants, named as following
respectively:

* without suffix `'` or `₀`: any polynomial `b` in the set has an invertible leading coefficient
  (`IsUnit (m.leadingCoeff p)`);
* with suffix `₀`: any polynomial `b` in the set either has an invertible leading coefficient or is
  equal to 0 (`IsUnit (m.leadingCoeff p) ∨ b = 0`);
* with suffix `'`: no hypotheses on leading coefficients, while requiring `R` to be a field
  (`Field k`, where the ring is denoted as `k` instead).

## Implementation notes

The definition of remainder makes sense when `R` is a communitive ring and any polynomial in `B`
either has an invertible leading coefficient or is 0, while for simplicity we try to formalize
it without applying these restrictions as its hypotheses.

We try to adjust its formal definition s.t. it corresponds with the informal definition well when
the above condition holds, while keeps simple and still shares some common properties without these
hypotheses.

* Degree condition is formalized as
  ```lean
  m.toWithBotSyn (m.withBotDegree b.val) + m.toWithBotSyn (m.withBotDegree (g b)) ≤
    m.toWithBotSyn (m.withBotDegree f)
  ```
  instead of `m.withBotDegree (b.val * g b) ≼'[m] m.withBotDegree f`.

  If `IsRemainder` was formalized with the latter one, some properties would require polynomials in
  `B` to have non-zero divisor leading coefficients since they need
  `m.withBotDegree (b.val * g b) = m.withBotDegree (b.val) + m.withBotDegree (g b)` with this
  formalization of `IsRemainder`. They no longer require this if `IsRemainder` is formalized with
  former one.

* Remainder irreducibility is formalized as `∀ c ∈ r.support, ∀ b ∈ B, b ≠ 0 → ¬ (m.degree b ≤ c)`,
  where `¬ (m.degree b ≤ c)` is sufficient but unnecessary for the indivisibility between the
  leading term of `b` (`m.leadingTerm b`) and the term of `r` with exponents `c`
  (`monomial c (b.coeff c)`).

## Reference : [Cox2015]

-/

@[expose] public section

namespace MonomialOrder

open MvPolynomial

/-- Given a multivariate polynomial `f` and a set `B` of multivariate polynomials over `R`.
A polynomial `r` is called a remainder of `f` on division by `B` with respect to a monomial order
`m`, if there exists `g : B →₀ R[X]` and a polynomial `r` s.t.

1. Finite linear combination:
  `f = ∑ (g(b) * b) + r`;
2. Degree condition:
  For eacho `b ∈ B`, the degree (with bot) of `g(b) * b` ≤ the degree (with bot) of `f` w.r.t. `m`;
3. Remainder irreducibility:
  No term of `r` is divisible by the leading monomial of any non-zero `b ∈ B`.

The definition of remainder makes sense when `R` is a communitive ring and any polynomial in `B`
either has an invertible leading coefficient or is 0. See the implementation note for the
formalization unaligned with the informal definition when not in this case.
-/
def IsRemainder {σ : Type*} (m : MonomialOrder σ) {R : Type*} [CommSemiring R]
    (f : MvPolynomial σ R) (B : Set (MvPolynomial σ R)) (r : MvPolynomial σ R) :=
  (∃ (g : B →₀ MvPolynomial σ R),
    f = Finsupp.linearCombination _ (fun (b : B) ↦ b.val) g + r ∧
    ∀ (b : B),
      m.toWithBotSyn (m.withBotDegree b.val) + m.toWithBotSyn (m.withBotDegree (g b)) ≤
        m.toWithBotSyn (m.withBotDegree f)) ∧
  ∀ c ∈ r.support, ∀ b ∈ B, b ≠ 0 → ¬ (m.degree b ≤ c)

namespace IsRemainder

section CommSemiring
variable {σ : Type*} {m : MonomialOrder σ}

variable {R : Type*} [CommSemiring R]
variable (f p : MvPolynomial σ R) (B : Set (MvPolynomial σ R)) (r : MvPolynomial σ R)

theorem isRemainder_def (p : MvPolynomial σ R) (B : Set (MvPolynomial σ R))
    (r : MvPolynomial σ R) : m.IsRemainder p B r ↔
      (∃ (g : B → MvPolynomial σ R) (B' : Finset B),
        p = B'.sum (fun x => g x * x) + r  ∧
        ∀ b ∈ B',
          m.toWithBotSyn (m.withBotDegree b.val) + m.toWithBotSyn (m.withBotDegree (g b)) ≤
            m.toWithBotSyn (m.withBotDegree p)) ∧
      ∀ c ∈ r.support, ∀ g' ∈ B, g' ≠ 0 → ¬ (m.degree g' ≤ c) := by
  classical
  apply and_congr_left
  rintro -
  constructor
  · intro ⟨g, h₂, h₃⟩
    use g.toFun, g.support
    refine ⟨by rwa [Finsupp.linearCombination_apply, Finsupp.sum] at h₂, ?_⟩
    intro g' hg'
    exact h₃ g'
  · intro ⟨g, B', h₂, h₃⟩
    use Finsupp.onFinset B' (fun b' => if b' ∈ B' then g b' else 0) (by simp_intro ..)
    split_ands
    · rw [Finsupp.linearCombination_apply, Finsupp.sum, h₂, Finsupp.support_onFinset]
      congr 1
      simp? [Finset.filter_and, Finset.filter_mem_eq_inter, Finset.inter_self, Finset.inter_filter,
          Finset.filter_inter] says
        simp only [ne_eq, ite_eq_right_iff, Classical.not_imp, Finset.filter_and,
          Finset.filter_mem_eq_inter, Finset.inter_self, Finset.inter_filter,
          Finsupp.onFinset_apply, smul_eq_mul, ite_mul, zero_mul, Finset.sum_ite_mem,
          Finset.filter_inter]
      rw [Finset.sum_filter]
      apply Finset.sum_congr rfl
      intro b' _
      by_cases hb' : g b' = 0 <;> simp [hb']
    · intro b
      by_cases hbB' : b ∈ B' <;> simp [hbB', h₃]

/--
A variant of `MonomialOrder.IsRemainder` without coercion of a `Set (MvPolynomial σ R)`.
-/
theorem isRemainder_def' (p : MvPolynomial σ R) (B : Set (MvPolynomial σ R))
    (r : MvPolynomial σ R) : m.IsRemainder p B r ↔
      (∃ (g : MvPolynomial σ R →₀ MvPolynomial σ R),
        ↑g.support ⊆ B ∧
        p = Finsupp.linearCombination _ id g + r ∧
        ∀ b ∈ B,
          m.toWithBotSyn (m.withBotDegree b) + m.toWithBotSyn (m.withBotDegree (g b)) ≤
            m.toWithBotSyn (m.withBotDegree p)) ∧
      ∀ c ∈ r.support, ∀ g' ∈ B, g' ≠ 0 → ¬ (m.degree g' ≤ c) := by
  apply and_congr_left'
  rw [Function.Surjective.exists (Finsupp.restrictSupportEquiv B (MvPolynomial σ R)).surjective]
  conv in Finsupp.linearCombination _ _ _ =>
    -- simp? [Finsupp.linearCombination,
    --   Finsupp.sum_subtypeDomain_index (p := (· ∈ B)) (h := fun a b ↦ b * a) x.2] says
    simp only [Finsupp.linearCombination, Finsupp.restrictSupportEquiv_apply, Finsupp.coe_lsum,
      LinearMap.coe_smulRight, LinearMap.id_coe, id_eq, smul_eq_mul,
      Finsupp.sum_subtypeDomain_index (p := (· ∈ B)) (h := fun a b ↦ b * a) x.2]
  simp [Subtype.exists, exists_prop, -exists_and_left, and_assoc, Finsupp.linearCombination]

/--
A variant of `MonomialOrder.IsRemainder` where `g : MvPolynomial σ R →₀ MvPolynomial σ R` is
replaced with a function `g : MvPolynomial σ R → MvPolynomial σ R` without limitation on its
support.
-/
theorem isRemainder_def'' (p : MvPolynomial σ R) (B : Set (MvPolynomial σ R))
    (r : MvPolynomial σ R) :
    m.IsRemainder p B r ↔
      (∃ (g : MvPolynomial σ R → MvPolynomial σ R) (B' : Finset (MvPolynomial σ R)),
        ↑B' ⊆ B ∧
        p = B'.sum (fun x => g x * x) + r ∧
        ∀ b' ∈ B',
          m.toWithBotSyn (m.withBotDegree (b' : MvPolynomial σ R)) +
          m.toWithBotSyn (m.withBotDegree (g b')) ≤
            m.toWithBotSyn (m.withBotDegree p)) ∧
      ∀ c ∈ r.support, ∀ b ∈ B, b ≠ 0 → ¬ (m.degree b ≤ c) := by
  classical
  rw [isRemainder_def']
  apply and_congr_left
  rintro -
  constructor
  · intro ⟨g, h₁, h₂, h₃⟩
    use g.toFun, g.support
    refine ⟨h₁, by rwa [Finsupp.linearCombination_apply, Finsupp.sum] at h₂, ?_⟩
    intro g' hg'
    exact h₃ g' (Set.mem_of_mem_of_subset hg' h₁)
  · intro ⟨g, B', h₁, h₂, h₃⟩
    use Finsupp.onFinset B' (fun b' => if b' ∈ B' then g b' else 0) (by simp_intro ..)
    split_ands
    · simp_intro b' hb'
      exact Set.mem_of_mem_of_subset hb'.1 h₁
    · rw [Finsupp.linearCombination_apply, Finsupp.sum, h₂, Finsupp.support_onFinset]
      congr 1
      simp? [Finset.filter_and, Finset.filter_mem_eq_inter, Finset.inter_self, Finset.inter_filter,
          Finset.filter_inter] says
        simp only [ne_eq, ite_eq_right_iff, Classical.not_imp, Finset.filter_and,
          Finset.filter_mem_eq_inter, Finset.inter_self, Finset.inter_filter,
          Finsupp.onFinset_apply, id_eq, smul_eq_mul, ite_mul, zero_mul, Finset.sum_ite_mem,
          Finset.filter_inter]
      rw [Finset.sum_filter]
      apply Finset.sum_congr rfl
      intro b' _
      by_cases hb' : g b' = 0 <;> simp [hb']
    · intro b hb
      by_cases hbB' : b ∈ B' <;> simp [hbB', h₃]

/--
A variant of `MonomialOrder.IsRemainder_def'` where `B` is `Finset (MvPolynomial σ R)`.
-/
theorem isRemainder_finset (p : MvPolynomial σ R) (B' : Finset (MvPolynomial σ R))
    (r : MvPolynomial σ R) : m.IsRemainder p B' r ↔
      (∃ (g : MvPolynomial σ R → MvPolynomial σ R),
        p = B'.sum (fun x => g x * x) + r ∧
        ∀ b' ∈ B',
          m.toWithBotSyn (m.withBotDegree (b' : MvPolynomial σ R)) +
          m.toWithBotSyn (m.withBotDegree (g b')) ≤
            m.toWithBotSyn (m.withBotDegree p)) ∧
      ∀ c ∈ r.support, ∀ b ∈ B', b ≠ 0 → ¬ (m.degree b ≤ c) := by
  classical
  constructor
  · rw [isRemainder_def']
    intro ⟨⟨g, hgsup, hsum, hg⟩, hr⟩
    refine ⟨?_, hr⟩
    use g.toFun
    refine ⟨?_, hg⟩
    simp? [Finsupp.linearCombination_apply, Finsupp.sum]  at hsum says
      simp only [Finsupp.linearCombination_apply, Finsupp.sum, id_eq, smul_eq_mul] at hsum
    rw [hsum]
    congr 1
    apply Finset.sum_subset hgsup
    simp_intro ..
  · rw [isRemainder_def'']
    intro ⟨⟨g, hsum, hg⟩, hr⟩
    refine ⟨?_, hr⟩
    use fun b' ↦ if b' ∈ B' then g b' else 0
    use B'
    split_ands
    · rfl
    · simp [hsum]
    · simp_intro .. [hg]

theorem isRemainder_iff_exists_isRemainder_finset (p : MvPolynomial σ R)
    (B : Set (MvPolynomial σ R)) (r : MvPolynomial σ R) :
    m.IsRemainder p B r ↔
      (∃ (B' : Finset (MvPolynomial σ R)), ↑B' ⊆ B ∧ m.IsRemainder p B' r) ∧
        ∀ c ∈ r.support, ∀ b ∈ B, b ≠ 0 → ¬ (m.degree b ≤ c) := by
  simp_rw [isRemainder_def'' (B := B), isRemainder_finset]
  constructor
  · rintro ⟨⟨g, B', hB'B, hsum, hdeg⟩, hdeg'⟩
    refine ⟨⟨↑B', hB'B, ?_⟩ , hdeg'⟩
    refine ⟨⟨g, hsum, hdeg⟩, fun _ h _ hb ↦ hdeg' _ h _ (hB'B hb)⟩
  · rintro ⟨⟨B', hB'B, ⟨⟨g, hsum, hdeg⟩, -⟩⟩, hdeg'⟩
    exact ⟨⟨g, B', hB'B, hsum, hdeg⟩, hdeg'⟩

/-- `r` is a remainder of a family of polynomials, if and only if it is a remainder with properities
defined in `MonomialOrder.div` with this family of polynomials given as a map from indexes to them.

It is a variant of `MonomialOrder.IsRemainder` where divisors are given as a map from indexes to
polynomials. -/
theorem isRemainder_range {ι : Type*} (f : MvPolynomial σ R)
    (b : ι → MvPolynomial σ R) (r : MvPolynomial σ R) :
    m.IsRemainder f (Set.range b) r ↔
      (∃ g : ι →₀ MvPolynomial σ R,
        f = Finsupp.linearCombination _ b g + r ∧
        ∀ i : ι,
          m.toWithBotSyn (m.withBotDegree (b i : MvPolynomial σ R)) +
          m.toWithBotSyn (m.withBotDegree (g i)) ≤
            m.toWithBotSyn (m.withBotDegree f)) ∧
      ∀ c ∈ r.support, ∀ i : ι, b i ≠ 0 → ¬ (m.degree (b i) ≤ c) := by
  classical
  constructor
  · rintro ⟨⟨g, h₁, h₂⟩, h₃⟩
    let idx : Set.range b ↪ ι := {
      toFun := Set.rangeSplitting b,
      inj' := Set.rangeSplitting_injective b
    }
    split_ands
    · use Finsupp.embDomain idx g
      split_ands
      · simp [h₁, idx]
      · intro i
        specialize h₂ ⟨b i, Set.mem_range_self i⟩
        wlog! h' : (Finsupp.embDomain idx g) i ≠ 0
        · simp [h']
        convert h₂
        generalize_proofs hbi
        rw [Finsupp.embDomain_apply, Ne.dite_ne_right_iff fun h ↦ by simpa [h] using h'] at h'
        rw [Finsupp.embDomain_apply, dite_cond_eq_true (by simp [h'])]
        generalize_proofs h'
        rw! (occs := [2, 3]) [← h'.choose_spec]
        simp [idx, Set.apply_rangeSplitting]
    · intro i hi b hb
      aesop
  · rintro ⟨⟨g, h₁, h₂⟩, h₃⟩
    constructor
    · letI b'_range : Finset (Set.range b) := g.support.image (Set.rangeFactorization b)
      letI g' (x : Set.range b) : MvPolynomial σ R := (g.support.filter (b · = x)).sum g
      have mem_support (x) (hx : g' x ≠ 0) : x ∈ b'_range := by
        contrapose! hx
        simp? [b'_range, Set.rangeFactorization_eq_iff]  at hx says
          simp only [Finset.mem_image, Finsupp.mem_support_iff, ne_eq,
            Set.rangeFactorization_eq_iff, not_exists, not_and, b'_range] at hx
        apply Finset.sum_eq_zero
        simp? says simp only [Finset.mem_filter, Finsupp.mem_support_iff, ne_eq, and_imp]
        tauto
      use Finsupp.onFinset b'_range g' mem_support
      split_ands
      · simp? [h₁, Finsupp.linearCombination_apply, Finsupp.sum] says
          simp only [h₁, Finsupp.linearCombination_apply, Finsupp.sum, smul_eq_mul,
            Finsupp.onFinset_apply]
        congr
        rw [eq_comm, Finset.sum_subset (s₂ := b'_range) (by simp) (by simp_intro ..)]
        unfold b'_range g'
        apply Finset.sum_image'
        simp? [Finset.sum_mul] says
          simp only [Finsupp.mem_support_iff, ne_eq, Set.rangeFactorization_coe, Finset.sum_mul,
            Set.rangeFactorization_eq_rangeFactorization_iff]
        intro _ _
        apply Finset.sum_congr rfl
        simp_intro ..
      · intro b1
        rw [Finsupp.onFinset]
        apply le_trans (add_le_add_right m.withBotDegree_sum_le _)
        have := (m.toWithBotSyn (m.withBotDegree b1.val) + ·)
        rw [Finset.comp_sup_eq_sup_comp_of_is_total (m.toWithBotSyn (m.withBotDegree b1.val) + ·)]
        · simp_rw [Finset.sup_le_iff, Finset.mem_filter]
          rintro _ ⟨-, h⟩
          simp [← h, h₂]
        · simp_intro _ _ _ [add_le_add]
        simp
    · aesop

theorem isRemainder_range_fintype {ι : Type*} [Fintype ι] (b : ι → MvPolynomial σ R)
    (r : MvPolynomial σ R) :
      m.IsRemainder p (Set.range b) r ↔
      (∃ g : ι → MvPolynomial σ R,
          p = ∑ i : ι, (b i * g i) + r ∧
          ∀ i : ι,
            m.toWithBotSyn (m.withBotDegree (b i)) + m.toWithBotSyn (m.withBotDegree (g i)) ≤
            m.toWithBotSyn (m.withBotDegree p)) ∧
        ∀ c ∈ r.support, ∀ i : ι, b i ≠ 0 → ¬ (m.degree (b i) ≤ c) := by
  simp [IsRemainder.isRemainder_range,
    Function.Surjective.exists (Finsupp.equivFunOnFinite.surjective),
    Finsupp.linearCombination, Finsupp.sum_fintype, mul_comm]

/--
Remainders are preserved on insertion of the zero polynomial into the set of divisors.
-/
@[simp]
theorem isRemainder_insert_zero_iff_isRemainder (p : MvPolynomial σ R)
    (B : Set (MvPolynomial σ R)) (r : MvPolynomial σ R) :
    m.IsRemainder p (insert 0 B) r ↔ m.IsRemainder p B r := by
  unfold IsRemainder
  convert and_congr_left' ?_
  · aesop
  rw [(Finsupp.comapDomain_surjective (f := (⟨·.val, by simp⟩ : B → ↑(insert 0 B))) ?_).exists]
  on_goal 2 => simp [Function.Injective]
  congr! with g
  on_goal 2 => aesop
  rw [Finsupp.linearCombination_comapDomain, Finsupp.linearCombination_apply, Finsupp.sum]
  convert (g.support.sum_preimage ..).symm
  intro x hx hx'
  simp [or_iff_not_imp_right.mp x.prop (by simpa using hx')]

/--
Remainders are preserved with the zero polynomial removed from the set of divisors.
-/
@[simp]
theorem isRemainder_sdiff_singleton_zero_iff_isRemainder (p : MvPolynomial σ R)
  (B : Set (MvPolynomial σ R)) (r : MvPolynomial σ R) :
  m.IsRemainder p (B \ {0}) r ↔ m.IsRemainder p B r := by
  by_cases h : 0 ∈ B
  · rw [←isRemainder_insert_zero_iff_isRemainder, show insert 0 (B \ {0}) = B by simp [h]]
  · simp [h]

variable {B} in
theorem isRemainder_zero {r : MvPolynomial σ R}
    (h : m.IsRemainder 0 B r) : r = 0 := by
  classical
  unfold IsRemainder at h
  obtain ⟨⟨g, h0sumg, hg⟩, hr⟩ := h
  rw [h0sumg, eq_comm]
  convert zero_add r
  apply Finset.sum_eq_zero (f := fun x ↦ g x * x.val)
  intro x _
  specialize hg x
  simp? at hg says
    simp only [withBotDegree_zero, toWithBotSyn_apply_bot, le_bot_iff, WithBot.add_eq_bot,
      toWithBotSyn_apply_eq_bot_iff, withBotDegree_eq_bot_iff] at hg
  rcases hg with hg | hg <;> simp [hg]

@[simp]
theorem isRemainder_zero_iff :
    m.IsRemainder 0 B r ↔ r = 0 := by
  refine ⟨isRemainder_zero, fun h ↦ ?_⟩
  exact ⟨⟨0, by simp [h]⟩, by simp [h]⟩

theorem isRemainder_iff_degree (hB : ∀ b ∈ B, m.leadingCoeff b ∈ nonZeroDivisors _) :
    m.IsRemainder f B r ↔
    (∃ (g : B →₀ MvPolynomial σ R),
      f = Finsupp.linearCombination _ (fun (b : B) ↦ b.val) g + r ∧
      ∀ (b : B), m.degree (b.val * (g b)) ≼[m] m.degree f) ∧
    ∀ c ∈ r.support, ∀ b ∈ B, b ≠ 0 → ¬ (m.degree b ≤ c) := by
  classical
  wlog! hf : f = 0
  · simp_rw [IsRemainder, ← map_add,
        ← m.withBotDegree_mul_of_left_mem_nonZeroDivisors (hB _ (Subtype.prop _)),
       withBotDegree_le_withBotDegree_iff]
    simp? [hf, -mem_support_iff] says
      simp only [hf, IsEmpty.forall_iff, and_true, Subtype.forall, ne_eq]
  simp_rw [hf, isRemainder_zero_iff]
  constructor
  · intro h
    exact ⟨⟨0, by simp [h]⟩, by simp [h]⟩
  rintro ⟨⟨g, h0sumg, hg⟩, hr⟩
  simp_rw [m.degree_zero, m.toSyn.map_zero, ← m.eq_zero_iff, AddEquiv.map_eq_zero_iff,
    mul_comm _ (g _)] at hg
  simp_rw [Finsupp.linearCombination_apply, Finsupp.sum, smul_eq_mul] at h0sumg
  have h_deg_r_eq_0 : m.degree r = 0 := by
    apply congrArg m.degree at h0sumg
    contrapose! h0sumg
    rw [degree_zero, ne_comm, ← AddEquiv.map_ne_zero_iff m.toSyn, ← m.toSyn_lt_iff_ne_zero]
    rw [← AddEquiv.map_ne_zero_iff m.toSyn, ← m.toSyn_lt_iff_ne_zero] at h0sumg
    rwa [degree_add_eq_right_of_lt]
    apply lt_of_le_of_lt m.degree_sum_le
    simpa [hg] using lt_of_le_of_lt Finset.sup_const_le h0sumg
  by_contra! hr0
  suffices ∀ b, g b * b = 0 by simp [this, hr0.symm] at h0sumg
  intro b
  by_contra! hgb
  obtain ⟨h_gb_ne_0, h_b_ne_0⟩ := ne_zero_and_ne_zero_of_mul hgb
  rw [m.degree_eq_zero_iff.mp h_deg_r_eq_0, support_C, ite_cond_eq_false _ _ (by simp [hr0])] at hr
  specialize hr 0 (by simp) b b.2 h_b_ne_0
  specialize hg b
  rw [m.degree_mul_of_right_mem_nonZeroDivisors h_gb_ne_0 (hB b b.2)] at hg
  rw [← hg] at hr
  simp at hr

theorem isRemainder_iff_degree₀
    (hB : ∀ b ∈ B, m.leadingCoeff b ∈ nonZeroDivisors _ ∨ b = 0) :
    m.IsRemainder f B r ↔
    (∃ (g : B →₀ MvPolynomial σ R),
      f = Finsupp.linearCombination _ (fun (b : B) ↦ b.val) g + r ∧
      ∀ (b : B), m.degree (b.val * (g b)) ≼[m] m.degree f) ∧
    ∀ c ∈ r.support, ∀ b ∈ B, b ≠ 0 → ¬ (m.degree b ≤ c) := by
  rw [← isRemainder_sdiff_singleton_zero_iff_isRemainder,
    isRemainder_iff_degree (hB := by simp_intro _ _ [or_iff_not_imp_right.mp (hB _ _)]),
    Function.Surjective.exists <|
      Finsupp.comapDomain_surjective (f := (⟨·.val, by aesop⟩ : ↑(B \ {0}) → ↑B)) ?_]
  on_goal 2 => simp [Function.Injective]
  congr! with x
  · rw [Finsupp.linearCombination_comapDomain, Finsupp.linearCombination_apply, Finsupp.sum]
    convert (x.support.sum_preimage ..)
    · simp
    intro x hx hx'
    simp? at hx' says
      simp only [Set.diff_singleton_subset_iff, Set.subset_insert, Set.range_inclusion,
        Set.mem_diff, Subtype.coe_prop, Set.mem_singleton_iff, ne_eq, true_and, Set.mem_setOf_eq,
        not_not] at hx'
    simp [hx']
  · constructor
    · intro h b
      by_cases hb : b.val = 0
      · simp [hb]
      simpa using h ⟨b.val, by simpa⟩
    · simp_intro ..
  simp

lemma mem_ideal_of_mem_ideal {B : Set (MvPolynomial σ R)} {r : MvPolynomial σ R}
    {I : Ideal (MvPolynomial σ R)} {p : MvPolynomial σ R}
    (hBI : B ⊆ I) (hpBr : m.IsRemainder p B r) (hr : r ∈ I) :
    p ∈ I := by
  obtain ⟨⟨f, h_eq, h_deg⟩, h_remain⟩ := hpBr
  rw [h_eq]
  refine Ideal.add_mem _ ?_ hr
  rw [Finsupp.linearCombination_apply]
  apply Ideal.sum_mem
  exact fun _ _ ↦ Ideal.mul_mem_left _ _ (Set.mem_of_mem_of_subset (by simp) hBI)

lemma term_notMem_span_span_monomial {p r : MvPolynomial σ R}
    {B : Set (MvPolynomial σ R)} (h : m.IsRemainder p B r) :
    ∀ s ∈ r.support, monomial s (r.coeff s) ∉
      Ideal.span ((fun p ↦ monomial (m.degree p) 1) '' (B \ {0})) := by
  classical
  intro s hs
  rw [← Set.image_image (monomial · 1) _ _, mem_ideal_span_monomial_image]
  simp? [MvPolynomial.mem_support_iff.mp hs] says
    simp only [mem_support_iff, coeff_monomial, ne_eq, ite_eq_right_iff,
      MvPolynomial.mem_support_iff.mp hs, imp_false, Decidable.not_not, Set.mem_image, Set.mem_diff,
      Set.mem_singleton_iff, exists_exists_and_eq_and, forall_eq', not_exists, not_and, and_imp]
  intro b hb
  unfold MonomialOrder.IsRemainder at h
  exact h.2 _ hs b hb

lemma term_notMem_span_leadingTerm {p r : MvPolynomial σ R}
    {B : Set (MvPolynomial σ R)} (h : m.IsRemainder p B r) :
    ∀ s ∈ r.support, monomial s (r.coeff s) ∉ Ideal.span (m.leadingTerm '' B) := by
  classical
  intro s hs
  apply not_imp_not.mpr ((m.span_leadingTerm_le_span_monomial (B := B)) ·)
  exact term_notMem_span_span_monomial h s hs

lemma _root_.MvPolynomial.monomial_notMem_span_monomial {r : MvPolynomial σ R}
    {B : Set (MvPolynomial σ R)}
    (h : ∀ c ∈ r.support, ∀ b ∈ B, ¬ (m.degree b ≤ c)) :
    ∀ s ∈ r.support, monomial s (1 : R) ∉ Ideal.span ((fun p ↦ monomial (m.degree p) 1) '' B) := by
  classical
  wlog! _ : Nontrivial R
  · simp [Subsingleton.eq_zero (α := R)]
  intro s hs
  rw [← Set.image_image (monomial · 1) _ _, mem_ideal_span_monomial_image]
  simpa using h s hs

lemma _root_.MvPolynomial.monomial_notMem_span_leadingTerm {r : MvPolynomial σ R}
    {B : Set (MvPolynomial σ R)}
    (h : ∀ c ∈ r.support, ∀ b ∈ B, ¬ (m.degree b ≤ c)) :
    ∀ s ∈ r.support, monomial s 1 ∉ Ideal.span (m.leadingTerm '' B) := by
  classical
  intro s hs
  apply not_imp_not.mpr ((m.span_leadingTerm_le_span_monomial (B := B)) ·)
  apply not_imp_not.mpr (Ideal.span_mono (Set.image_mono <| Set.diff_subset (t := {0})) ·)
  exact monomial_notMem_span_monomial h s hs

variable {f r B} in
lemma withBotDegree_remainder_le (h : m.IsRemainder f B r) :
    m.withBotDegree r ≼'[m] m.withBotDegree f := by
  wlog! hf : f ≠ 0
  · -- simp [hf, isRemainder_zero_iff] at h; simp [hf, h] -- "flexible" linter doesn't work here?
    simp [hf, isRemainder_zero_iff .. |>.mp <| hf ▸ h]
  wlog! hr : r ≠ 0
  · simp [hr]
  obtain ⟨g, hsum, h⟩ := h.1
  apply congrArg (m.toWithBotSyn <| m.withBotDegree ·) at hsum
  contrapose! hsum
  apply ne_of_lt
  rw [withBotDegree_add_of_right_lt]
  · exact hsum
  apply lt_of_le_of_lt withBotDegree_sum_le
  rw [Finset.sup_lt_iff (bot_lt_iff_ne_bot.mpr (by simpa))]
  simp? [-mem_support_iff, -Subtype.forall] says
    simp only [Finsupp.mem_support_iff, ne_eq, LinearMap.coe_smulRight, LinearMap.id_coe, id_eq,
      smul_eq_mul]
  rintro b -
  apply lt_of_le_of_lt <| m.withBotDegree_mul_le (g b) b
  simpa [add_comm] using lt_of_le_of_lt (h b) hsum

variable {p B r} in
lemma exists_withBotDegree_le_withBotDegree
    (h : m.IsRemainder p B r) (hfr : m.withBotDegree p ≠ m.withBotDegree r) :
    ∃ b ∈ B, m.withBotDegree b ≤ m.withBotDegree p := by
  classical
  rw [ne_eq, ← m.toWithBotSyn.apply_eq_iff_eq, eq_comm,
    ← ne_eq, ne_iff_lt_iff_le.mpr <| withBotDegree_remainder_le h] at hfr
  wlog! hp : p ≠ 0
  · rw [hp, isRemainder_zero_iff] at h
    simp [h, hp] at hfr
  rw [isRemainder_def''] at h
  rcases h with ⟨⟨g, B', h₁, hsum, h₃⟩, h₄⟩
  have : m.degree p ∈ p.support := m.degree_mem_support hp
  nth_rw 1 [hsum] at this
  apply Finset.mem_of_subset support_add at this
  rw [Finset.mem_union] at this
  rcases this with this | this
  on_goal 2 =>
    apply m.le_withBotDegree at this
    rw [← withBotDegree_eq_coe_degree_iff _ |>.mpr hp, ← not_lt] at this
    contradiction
  obtain ⟨b, hb⟩ := Finset.mem_biUnion.mp <| Finset.mem_of_subset support_sum this
  use b
  refine ⟨h₁ hb.1, ?_⟩
  rcases hb with ⟨hb₁, hb₂⟩
  obtain hgbne0 : g b ≠ 0 := by
    contrapose! hb₂
    simp [hb₂]
  apply le_withBotDegree (m:=m) at hb₂
  rw [← withBotDegree_eq_coe_degree_iff _ |>.mpr hp] at hb₂
  apply le_trans' (m.withBotDegree_mul_le' ..) at hb₂
  rw [add_comm] at hb₂
  apply le_antisymm (add_comm (m.toWithBotSyn <| m.withBotDegree b) _ ▸ h₃ b hb₁) at hb₂
  simp only [← map_add, EmbeddingLike.apply_eq_iff_eq] at hb₂
  rw [← hb₂]
  exact WithBot.le_self_add (m.withBotDegree_eq_bot_iff _ |>.not.mpr hgbne0) _

variable {p B r} in
lemma exists_withBotDegree_le_withBotDegree₀
    (h : m.IsRemainder p B r) (hfr : m.withBotDegree p ≠ m.withBotDegree r) :
    ∃ b ∈ B, b ≠ 0 ∧ m.withBotDegree b ≤ m.withBotDegree p := by
  rw [← isRemainder_sdiff_singleton_zero_iff_isRemainder] at h
  simpa only [← and_assoc] using h.exists_withBotDegree_le_withBotDegree hfr

variable {p B r} in
lemma withBotDegree_eq_withBotDegree_iff_leadingTerm_eq_leadingTerm (h : m.IsRemainder p B r) :
    m.withBotDegree r = m.withBotDegree p ↔ m.leadingTerm r = m.leadingTerm p := by
  wlog! hB0 : 0 ∉ B
  · apply this (h := isRemainder_sdiff_singleton_zero_iff_isRemainder .. |>.mpr h)
    simp
  wlog! hp : p ≠ 0
  · simp [hp]
  constructor
  on_goal 2 =>
    intro h
    rw [← m.withBotDegree_leadingTerm r, ← m.withBotDegree_leadingTerm p, h]
  intro deg_eq
  have deg_eq' := m.withBotDegree_eq_withBotDegree_iff .. |>.mp deg_eq |>.1
  simp_rw [m.leadingTerm_eq_leadingTerm_iff, leadingCoeff, deg_eq', and_true]
  obtain ⟨⟨g, hg, hg'⟩, h⟩ := h
  nth_rw 3 [hg]
  rw [coeff_add]
  convert eq_comm.mp <| zero_add (coeff (m.degree p) r)
  rw [Finsupp.linearCombination_apply, Finsupp.sum, coeff_sum]
  apply Finset.sum_eq_zero
  rintro g' -
  wlog! hgg' : g g' * g'.val ≠ 0
  · simp [hgg']
  apply m.coeff_eq_zero_of_lt
  rw [smul_eq_mul, ← m.withBotDegree_lt_withBotDegree_iff_of_ne_zero _ _ hgg']
  apply lt_of_le_of_lt (m.withBotDegree_mul_le' ..)
  rw [add_comm]
  apply lt_of_le_of_ne (hg' g')
  specialize h (m.degree r)
    (by simpa [← m.withBotDegree_eq_bot_iff .. |>.not, ← deg_eq, withBotDegree_eq_bot_iff] using hp)
    g' g'.2 (by contrapose! hgg'; simp [hgg'])
  contrapose! h
  rw [← m.toWithBotSyn.map_add, m.toWithBotSyn.apply_eq_iff_eq,
    m.withBotDegree_eq_coe_degree_iff .. |>.mpr hp,
    m.withBotDegree_eq_coe_degree_iff g'.val |>.mpr (by contrapose! hgg'; simp [hgg']),
    m.withBotDegree_eq_coe_degree_iff (g g') |>.mpr (by contrapose! hgg'; simp [hgg']),
    ← WithBot.coe_add, WithBot.coe_eq_coe] at h
  simp [deg_eq', ← h]

variable {p B} in
lemma exists_degree_le_degree_of_zero (hp : p ≠ 0) (h : m.IsRemainder p B 0) :
    ∃ b ∈ B, m.degree b ≤ m.degree p := by
  obtain ⟨b, hbB, hb⟩ := exists_withBotDegree_le_withBotDegree (r := 0) h (by simpa)
  use b, hbB
  wlog! hb0 : b ≠ 0
  · simp [hb0]
  simpa [(withBotDegree_eq_coe_degree_iff _).mpr _, hb0, hp] using hb

@[simp, nontriviality]
lemma of_subsingleton [Subsingleton (MvPolynomial σ R)]
    {p r : MvPolynomial σ R} {s : Set (MvPolynomial σ R)} :
    m.IsRemainder p s r := by
  simp [IsRemainder, Subsingleton.eq_zero (α := MvPolynomial σ R)]

lemma _root_.MonomialOrder.Embedding.isRemainder_killCompl_of_isRemainder_rename
    {σ' σ} {m' : MonomialOrder σ'} {m : MonomialOrder σ}
    (e : Embedding m' m) {p : MvPolynomial σ' R} {B : Set (MvPolynomial σ' R)}
    {r : MvPolynomial σ R} (h : m.IsRemainder (p.rename e) (rename e '' B) r) :
    m'.IsRemainder p B (r.killCompl e.coe_injective) := by
  classical
  rw [isRemainder_def'] at h ⊢
  obtain ⟨⟨g, hg, hsum, hdeg⟩, hdeg'⟩ := h
  constructor
  · use g.mapDomain (killCompl e.coe_injective) |>.mapRange (killCompl e.coe_injective) (by simp)
    split_ands
    · apply subset_trans (SetLike.coe_subset_coe.mpr Finsupp.support_mapRange)
      apply subset_trans (SetLike.coe_subset_coe.mpr Finsupp.mapDomain_support)
      apply Set.image_mono (f := killCompl e.coe_injective) at hg
      simp_rw [← Set.image_comp, ← AlgHom.coe_comp,
        MvPolynomial.killCompl_comp_rename e.coe_injective, AlgHom.coe_id, Set.image_id] at hg
      rwa [Finset.coe_image]
    · apply congrArg (killCompl e.coe_injective) at hsum
      rw [← Function.comp_apply (f := killCompl e.coe_injective), ← AlgHom.coe_comp,
        killCompl_comp_rename, AlgHom.coe_id, id] at hsum
      simp [hsum, Finsupp.linearCombination]
      rw [Finsupp.sum_mapRange_index (by simp), g.sum_mapDomain_index (by simp) (by simp [add_mul]),
        map_finsuppSum]
      simp
    · intro b hb
      specialize hdeg (b.rename e) (Set.mem_image_of_mem _ hb)
      rw [← e.withBotDegree_add_withBotDegree_le_withBotDegree_iff']
      apply le_trans' hdeg
      nth_rw 2 [← b.killCompl_rename_app e.coe_injective]
      rw [Finsupp.mapRange_apply, g.mapDomain_apply' (rename e '' B) hg]
      · apply add_le_add_right
        exact withBotDegree_rename_killCompl_le_withBotDegree ..
      · apply Set.InjOn.image_of_comp
        simp
      · exact Set.mem_image_of_mem _ hb
  intro c hc g' hg' hg'0
  rw [support_killCompl] at hc
  specialize hdeg' (c.mapDomain e) (by simpa using hc) (g'.rename e) (Set.mem_image_of_mem _ hg')
    (by simpa)
  rwa [e.degree_rename, Finsupp.mapDomain_le_mapDomain_iff_le e.coe_injective] at hdeg'

lemma _root_.MonomialOrder.Embedding.isRemainder_rename_of_isRemainder {σ' σ}
    {m' : MonomialOrder σ'} {m : MonomialOrder σ}
    (e : Embedding m' m) {p : MvPolynomial σ' R} {B : Set (MvPolynomial σ' R)}
    {r : MvPolynomial σ' R} (h : m'.IsRemainder p B r) :
    m.IsRemainder (p.rename e) (rename e '' B) (r.rename e) := by
  classical
  rw [isRemainder_def'] at h ⊢
  obtain ⟨⟨g, hg, hsum, hdeg⟩, hdeg'⟩ := h
  constructor
  · use g.mapDomain (rename e) |>.mapRange (rename e) (by simp)
    split_ands
    · apply subset_trans (SetLike.coe_subset_coe.mpr Finsupp.support_mapRange)
      apply subset_trans (SetLike.coe_subset_coe.mpr Finsupp.mapDomain_support)
      rw [Finset.coe_image]
      exact Set.image_mono hg
    · simp [hsum, Finsupp.linearCombination]
      rw [Finsupp.sum_mapRange_index (by simp),
        Finsupp.sum_mapDomain_index_inj <| rename_injective _ e.coe_injective]
      simp_rw [← map_mul, map_finsuppSum]
    · simpa [← map_mul, Finsupp.mapDomain_apply (rename_injective _ e.coe_injective)] using hdeg
  · simpa [e.coe_injective, MvPolynomial.support_rename_of_injective, Embedding.degree_rename,
      Finsupp.mapDomain_le_mapDomain_iff_le] using hdeg'

lemma _root_.MonomialOrder.Embedding.isRemainder_iff_isRemainder_rename {σ' σ}
    {m' : MonomialOrder σ'} {m : MonomialOrder σ}
    (e : Embedding m' m) (p : MvPolynomial σ' R) {B : Set (MvPolynomial σ' R)}
    (r : MvPolynomial σ' R) :
    m'.IsRemainder p B r ↔
      m.IsRemainder (p.rename e) (rename e '' B) (r.rename e) :=
  ⟨e.isRemainder_rename_of_isRemainder,
    (by simpa using e.isRemainder_killCompl_of_isRemainder_rename ·)⟩

end CommSemiring

section CommRing

variable {σ : Type*} {m : MonomialOrder σ} {R : Type*} [CommRing R]

theorem exists_isRemainder {B : Set (MvPolynomial σ R)}
    (hB : ∀ b ∈ B, IsUnit <| m.leadingCoeff b) (p : MvPolynomial σ R) :
    ∃ (r : MvPolynomial σ R), m.IsRemainder p B r := by
  classical
  obtain ⟨g, r, h⟩ := MonomialOrder.div_set hB p
  use r
  rw [isRemainder_iff_degree (hB := fun _ h ↦ (hB _ h).mem_nonZeroDivisors)]
  split_ands
  · use g
    exact ⟨h.1, h.2.1⟩
  · intro c hc b hb _
    exact h.2.2 c hc b hb

/-- A variant of `div_set'` including `0` -/
theorem exists_isRemainder₀ {B : Set (MvPolynomial σ R)}
    (hB : ∀ b ∈ B, (IsUnit (m.leadingCoeff b) ∨ b = 0)) (p : MvPolynomial σ R) :
    ∃ (r : MvPolynomial σ R), m.IsRemainder p B r := by
  have hB₁ : ∀ b ∈ B \ {0}, IsUnit (m.leadingCoeff b) := by
    simp_intro .. [or_iff_not_imp_right.mp (hB _ _)]
  obtain ⟨r, h⟩ := exists_isRemainder hB₁ p
  exists r
  rwa [← isRemainder_sdiff_singleton_zero_iff_isRemainder]

lemma mem_ideal_iff {B : Set (MvPolynomial σ R)}
    {r : MvPolynomial σ R} {I : Ideal (MvPolynomial σ R)} {p : MvPolynomial σ R}
    (hBI : B ⊆ I) (hpBr : m.IsRemainder p B r) : r ∈ I ↔ p ∈ I := by
  refine ⟨mem_ideal_of_mem_ideal hBI hpBr, ?_⟩
  obtain ⟨⟨f, h_eq, h_deg⟩, h_remain⟩ := hpBr
  intro hp
  rw [← sub_eq_of_eq_add' h_eq]
  apply Ideal.sub_mem I hp
  apply Ideal.sum_mem
  exact fun _ _ ↦ Ideal.mul_mem_left I _ (Set.mem_of_mem_of_subset (by simp) hBI)

lemma sub_mem_ideal_of_isRemainder_of_subset_ideal
    {B : Set (MvPolynomial σ R)} {I : Ideal (MvPolynomial σ R)} {p r₁ r₂ : MvPolynomial σ R}
    (hBI : B ⊆ I) (hr₁ : m.IsRemainder p B r₁) (hr₂ : m.IsRemainder p B r₂) :
    r₁ - r₂ ∈ I := by
  obtain ⟨⟨f₁, h_eq₁, h_deg₁⟩, h_remain₁⟩ := hr₁
  obtain ⟨⟨f₂, h_eq₂, h_deg₂⟩, h_remain₂⟩ := hr₂
  rw [← sub_eq_of_eq_add' h_eq₁, ← sub_eq_of_eq_add' h_eq₂]
  simp? says simp only [sub_sub_sub_cancel_left]
  apply Ideal.sub_mem I
  all_goals
    apply Ideal.sum_mem
    intro g hg
    exact Ideal.mul_mem_left I _ (Set.mem_of_mem_of_subset (by simp) hBI)

lemma sub_monomial_notMem_span_leadingTerm
    {B : Set (MvPolynomial σ R)} {p r₁ r₂ : MvPolynomial σ R}
    (hr₁ : m.IsRemainder p B r₁) (hr₂ : m.IsRemainder p B r₂) :
    ∀ s ∈ (r₁ - r₂).support, monomial s 1 ∉ Ideal.span (m.leadingTerm '' B) := by
  classical
  wlog! hB0 : 0 ∉ B
  · rw [← isRemainder_sdiff_singleton_zero_iff_isRemainder] at hr₁ hr₂
    specialize this hr₁ hr₂ (by simp)
    rwa [← span_leadingTerm_sdiff_singleton_zero]
  apply monomial_notMem_span_leadingTerm
  intro c hc
  apply MvPolynomial.support_sub at hc
  rw [Finset.mem_union] at hc
  rcases hc with hc | hc
  · intro b hb
    exact hr₁.2 c hc b hb (hB0 <| · ▸ hb)
  · intro b hb
    exact hr₂.2 c hc b hb (hB0 <| · ▸ hb)

end CommRing

section Field

variable {k : Type*} [Field k] {σ : Type*} {m : MonomialOrder σ}

/-- A variant of `div_set'` in field -/
theorem exists_isRemainder' (B : Set (MvPolynomial σ k))
    (p : MvPolynomial σ k) :
    ∃ (r : MvPolynomial σ k), m.IsRemainder p B r := by
  apply exists_isRemainder₀
  simp [em']

theorem isRemainder_iff_degree' (f : MvPolynomial σ k) (B) (r) :
    m.IsRemainder f B r ↔
    (∃ (g : B →₀ MvPolynomial σ k),
      f = Finsupp.linearCombination _ (fun (b : B) ↦ b.val) g + r ∧
      ∀ (b : B), m.degree (b.val * (g b)) ≼[m] m.degree f) ∧
    ∀ c ∈ r.support, ∀ b ∈ B, b ≠ 0 → ¬ (m.degree b ≤ c) :=
  isRemainder_iff_degree₀ f B r (by simp [em'])

end Field

end IsRemainder

end MonomialOrder
