Theory Preliminaries_on_Polynomials_1

section ‹Preliminaries: Extending the Library on Multivariate Polynomials›

subsection ‹Part 1 -- Extensions Without Importing Univariate Polynomials›

theory Preliminaries_on_Polynomials_1
  imports 
    Polynomials.More_MPoly_Type
    Polynomials.MPoly_Type_Class_Finite_Map
begin

type_synonym var = nat
type_synonym monom = "var ⇒0 nat" 

definition substitute :: "(var ⇒ 'a mpoly) ⇒ 'a :: comm_semiring_1 mpoly ⇒ 'a mpoly" where
  "substitute σ p = insertion σ (replace_coeff Const p)" 

lemma Const_0: "Const 0 = 0"
  by (transfer, simp add: Const0_zero)

lemma Const_1: "Const 1 = 1"
  by (transfer, simp add: Const0_one)

lemma insertion_Var: "insertion α (Var x) = α x" 
  apply transfer
  by (metis One_nat_def Var0_def insertion.abs_eq insertion_single mapping_of_inverse monom.rep_eq mult.right_neutral mult_1 power.simps(2) power_0)

lemma insertion_Const: "insertion α (Const a) = a"
  by (metis Const.abs_eq Const0_def insertion_single monom.abs_eq mult.right_neutral power_0 single_zero)

lemma insertion_power: "insertion α (p^n) = (insertion α p)^n" 
  by (induct n, auto simp: insertion_mult)

lemma insertion_monom_add: "insertion α (monom (f + g) a) = insertion α (monom f 1) * insertion α (monom g a)" 
  by (metis insertion_mult mult_1 mult_monom)

lemma insertion_uminus: "insertion α (- p) = - insertion α p"
  by (metis add_eq_0_iff insertion_add insertion_zero)

lemma insertion_sum_list: "insertion α (sum_list ps) = sum_list (map (insertion α) ps)" 
  by (induct ps, auto simp: insertion_add)

lemma coeff_uminus: "coeff (- p) m = - coeff p m"
  by (simp add: coeff_def uminus_mpoly.rep_eq)

lemma insertion_substitute: "insertion α (substitute σ p) = insertion (λ x. insertion α (σ x)) p" 
  unfolding substitute_def
proof (induct p rule: mpoly_induct)
  case (monom m a)
  show ?case 
    apply (subst replace_coeff_monom)
    subgoal by (simp add: Const_0)
    subgoal proof (induct m arbitrary: a rule: poly_mapping_induct)
      case (single k v)
      show ?case by (simp add: insertion_mult insertion_Const insertion_power)  
    next
      case (sum f g k v a)
      from sum(1)[of 1] sum(2)[of a] show ?case 
        by (simp add: insertion_monom_add insertion_mult Const_1)
    qed
    done
next
  case (sum p1 p2 m a)
  then show ?case 
    apply (subst replace_coeff_add)
    subgoal by (simp add: Const_0)
    subgoal by (transfer', simp add: Const0_def single_add)
    by (simp add: insertion_add)
qed

lemma Const_add: "Const (x + y) = Const x + Const y" 
  by (transfer, auto simp: Const0_def single_add)

lemma substitute_add[simp]: "substitute σ (p + q) = substitute σ p + substitute σ q" 
  unfolding substitute_def insertion_add[symmetric] 
  by (subst replace_coeff_add, auto simp: Const_0 Const_add)

lemma Const_sum: "Const (sum f A) = sum (Const o f) A"
  by (metis Const_0 Const_add sum_comp_morphism)

lemma Const_sum_list: "Const (sum_list (map f xs)) = sum_list (map (Const o f) xs)"
  by (induct xs, auto simp: Const_0 Const_add)

lemma Const_0_eq[simp]: "Const x = 0 ⟷ x = 0" 
  by (smt (verit) Const.abs_eq Const0_def coeff_monom monom.abs_eq single_zero when_def zero_mpoly_def)

lemma Const_sum_any: "Const (Sum_any f) = Sum_any (Const o f)"
  unfolding Sum_any.expand_set Const_sum o_def
  by (intro sum.cong[OF _ refl], auto simp: Const_0)

lemma Const_mult: "Const (x * y) = Const x * Const y"
  by (metis Const.abs_eq Const0_def monom.abs_eq smult_conv_mult smult_monom)

lemma Const_power: "Const (x ^ e) = Const x ^ e" 
  by (induct e, auto simp: Const_1 Const_mult)

lemma lookup_replace_Const: "lookup (mapping_of (replace_coeff Const p)) l = Const (lookup (mapping_of p) l)" 
  by (metis Const_0 coeff_def coeff_replace_coeff)

lemma replace_coeff_mult: "replace_coeff Const (p * q) = replace_coeff Const p * replace_coeff Const q" 
  apply (subst coeff_eq[symmetric], intro ext, subst coeff_replace_coeff, rule Const_0)
  apply (unfold coeff_def)
  apply (unfold times_mpoly.rep_eq)
  apply (unfold Poly_Mapping.lookup_mult)
  apply (unfold Const_sum_any o_def Const_mult lookup_replace_Const)
  apply (unfold when_def if_distrib Const_0)
  by auto


lemma substitute_mult[simp]: "substitute σ (p * q) = substitute σ p * substitute σ q" 
  unfolding substitute_def insertion_mult[symmetric] replace_coeff_mult ..

lemma replace_coeff_Var[simp]: "replace_coeff Const (Var x) = Var x"
  by (metis Const_0 Const_1 Var.abs_eq Var0_def monom.abs_eq replace_coeff_monom)

lemma replace_coeff_Const[simp]: "replace_coeff Const (Const c) = Const (Const c)" 
  by (metis Const.abs_eq Const0_def Const_0 monom.abs_eq replace_coeff_monom)

lemma substitute_Var[simp]: "substitute σ (Var x) = σ x" 
  unfolding substitute_def by (simp add: insertion_Var)

lemma substitute_Const[simp]: "substitute σ (Const c) = Const c" 
  unfolding substitute_def by (simp add: insertion_Const)

lemma substitute_0[simp]: "substitute σ 0 = 0" 
  using substitute_Const[of σ 0, unfolded Const_0] .

lemma substitute_1[simp]: "substitute σ 1 = 1" 
  using substitute_Const[of σ 1, unfolded Const_1] .

lemma substitute_power[simp]: "substitute σ (p^e) = (substitute σ p)^e" 
  by (induct e, auto)

lemma substitute_monom[simp]: "substitute σ (monom (monomial e x) c) = Const c * (σ x)^e" 
  by (simp add: replace_coeff_monom substitute_def)

lemma substitute_sum_list: "substitute σ (sum_list (map f xs)) = sum_list (map (substitute σ o f) xs)"
  by (induct xs, auto)

lemma substitute_sum: "substitute σ (sum f xs) = sum (substitute σ o f) xs"
  by (induct xs rule: infinite_finite_induct, auto)

lemma substitute_prod: "substitute σ (prod f xs) = prod (substitute σ o f) xs"
  by (induct xs rule: infinite_finite_induct, auto)

definition vars_list where "vars_list = sorted_list_of_set o vars" 

lemma set_vars_list[simp]: "set (vars_list p) = vars p" 
  unfolding vars_list_def o_def using vars_finite[of p] by auto

lift_definition mpoly_coeff_filter :: "('a :: zero ⇒ bool) ⇒ 'a mpoly ⇒ 'a mpoly" is
  "λ f p. Poly_Mapping.mapp (λ m c. c when f c) p" .

lemma mpoly_coeff_filter: "coeff (mpoly_coeff_filter f p) m = (coeff p m when f (coeff p m))" 
  unfolding coeff_def by transfer (simp add: in_keys_iff mapp.rep_eq)

lemma total_degree_add: assumes "total_degree p ≤ d" "total_degree q ≤ d" 
  shows "total_degree (p + q) ≤ d" 
  using assms
proof transfer
  fix d and p q :: "(nat ⇒0 nat) ⇒0 'a" 
  let ?exp = "λ p. Max (insert (0 :: nat) ((λm. sum (lookup m) (keys m)) ` keys p))" 
  assume d: "?exp p ≤ d" "?exp q ≤ d" 
  have "?exp (p + q) ≤ Max (insert (0 :: nat) ((λm. sum (lookup m) (keys m)) ` (keys p ∪ keys q)))" 
    using Poly_Mapping.keys_add[of p q]
    by (intro Max_mono, auto)
  also have "… = max (?exp p) (?exp q)" 
    by (subst Max_Un[symmetric], auto simp: image_Un)
  also have "… ≤ d" using d by auto
  finally show "?exp (p + q) ≤ d" .
qed

lemma total_degree_Var[simp]: "total_degree (Var x :: 'a :: comm_semiring_1 mpoly) = Suc 0" 
  by (transfer, auto simp: Var0_def)

lemma total_degree_Const[simp]: "total_degree (Const x) = 0" 
  by (transfer, auto simp: Const0_def)

lemma total_degree_Const_mult: assumes "total_degree p ≤ d"
  shows "total_degree (Const x * p) ≤ d" 
  using assms
proof (transfer, goal_cases)
  case (1 p d x)
  have sub: "keys (Const0 x * p) ⊆ keys p" 
    by (rule order.trans[OF keys_mult], auto simp: Const0_def)
  show ?case
    by (rule order.trans[OF _ 1], rule Max_mono, insert sub, auto)
qed

lemma vars_0[simp]: "vars 0 = {}" 
  unfolding vars_def by (simp add: zero_mpoly.rep_eq)

lemma vars_1[simp]: "vars 1 = {}"
  unfolding vars_def by (simp add: one_mpoly.rep_eq)

lemma vars_Var[simp]: "vars (Var x :: 'a :: comm_semiring_1 mpoly) = {x}" 
  unfolding vars_def by (transfer, auto simp: Var0_def)

lemma vars_Const[simp]: "vars (Const c) = {}" 
  unfolding vars_def by (transfer, auto simp: Const0_def)

lemma coeff_sum_list: "coeff (sum_list ps) m = (∑p←ps. coeff p m)"  
  by (induct ps, auto simp: coeff_add[symmetric]) 
    (metis coeff_monom monom_zero zero_when)

lemma coeff_Const_mult: "coeff (Const c * p) m = c * coeff p m" 
  by (metis Const.abs_eq Const0_def add_0 coeff_monom_mult monom.abs_eq)

lemma coeff_Const: "coeff (Const c) m = (if m = 0 then (c :: 'a :: comm_semiring_1) else 0)"
  by (simp add: Const.rep_eq Const0_def coeff_def lookup_single_not_eq)

lemma coeff_Var: "coeff (Var x) m = (if m = monomial 1 x then 1 :: 'a :: comm_semiring_1 else 0)"
  by (simp add: Var.rep_eq Var0_def coeff_def lookup_single_not_eq)



text ‹list-based representations, so that polynomials can be converted to first-order terms›

lift_definition monom_list :: "'a :: comm_semiring_1 mpoly ⇒ (monom × 'a) list"
  is "λ p. map (λ m. (m, lookup p m)) (sorted_list_of_set (keys p))" .

lift_definition var_list :: "monom ⇒ (var × nat) list"
  is "λ m. map (λ x. (x, lookup m x)) (sorted_list_of_set (keys m))" .

lemma monom_list: "p = (∑ (m, c) ← monom_list p. monom m c)" 
  apply transfer
  subgoal for p
    apply (subst poly_mapping_sum_monomials[symmetric])
    apply (subst distinct_sum_list_conv_Sum)
     apply (unfold distinct_map, simp add: inj_on_def)
     apply (meson in_keys_iff monomial_inj)
    apply (unfold set_map image_comp o_def split)
    apply (subst set_sorted_list_of_set, force)
    by (smt (verit, best) finite_keys lookup_eq_zero_in_keys_contradict monomial_inj o_def sum.cong sum.reindex_nontrivial)
  done

lemma monom_list_coeff: "(m,c) ∈ set (monom_list p) ⟹ coeff p m = c" 
  unfolding coeff_def by (transfer, auto)

lemma monom_list_keys: "(m,c) ∈ set (monom_list p) ⟹ keys m ⊆ vars p" 
  unfolding vars_def by (transfer, auto)


lemma var_list: "monom m c = Const (c :: 'a :: comm_semiring_1) * (∏ (x, e) ← var_list m. (Var x)^e)" 
proof transfer
  fix m :: monom and c :: 'a
  have set: "set (sorted_list_of_set (keys m)) = keys m" 
    by (subst set_sorted_list_of_set, force+)
  have id: "(∏(x, y)←map (λx. (x, lookup m x)) (sorted_list_of_set (keys m)). Var0 x ^ y)
    = (∏ x ∈ keys m. Var0 x ^ lookup m x)" (is "?r1 = ?r2")
    apply (unfold map_map o_def split)
    apply (subst prod.distinct_set_conv_list[symmetric])
    by auto
  have "monomial c m = Const0 c * monomial 1 m"
    by (simp add: Const0_one monomial_mp)
  also have "monomial (1 :: 'a) m = ?r1" unfolding id 
  proof (induction m rule: poly_mapping_induct)
    case (single k v)
    then show ?case by (auto simp: Var0_power mult_single)    
  next
    case (sum f g k v)
    have id: "monomial (1 :: 'a) (f + g) = monomial 1 f * monomial 1 g" 
      by (simp add: mult_single)
    have keys: "keys (f + g) = keys f ∪ keys g" "keys f ∩ keys g = {}" 
       apply (intro keys_plus_ninv_comm_monoid_add) 
      using sum(3-4) by simp
    show ?case unfolding id sum(1-2) unfolding keys(1)
      apply (subst prod.union_disjoint, force, force, rule keys)
      apply (intro arg_cong2[of _ _ _ _ "(*)"] prod.cong refl)
       apply (insert keys(2), simp add: disjoint_iff in_keys_iff lookup_add)
      by (metis add_cancel_left_left disjoint_iff_not_equal in_keys_iff plus_poly_mapping.rep_eq)
  qed
  finally show "monomial c m = Const0 c * ?r1" .
qed

lemma var_list_keys: "(x,e) ∈ set (var_list m) ⟹ x ∈ keys m" 
  by (transfer, auto)

lemma vars_substitute: assumes "⋀ x. vars (σ x) ⊆ V" 
  shows "vars (substitute σ p) ⊆ V" 
proof -
  define mcs where "mcs = monom_list p" 
  show ?thesis unfolding monom_list[of p, folded mcs_def]
  proof (induct mcs)
    case (Cons mc mcs)
    obtain m c where mc: "mc = (m,c)" by force
    define xes where "xes = var_list m" 
    have monom: "vars (substitute σ (monom m c)) ⊆ V" unfolding var_list[of m, folded xes_def]
    proof (induct xes)
      case (Cons xe xes)
      obtain x e where xe: "xe = (x,e)" by force
      from assms have "vars (σ x) ⊆ V" .
      hence x: "vars ((σ x)^e) ⊆ V" 
      proof (induct e)
        case (Suc e)
        then show ?case 
          by (simp, intro order.trans[OF vars_mult], auto)
      qed force
      have id: "substitute σ (Const c * (∏a←xe # xes. case a of (x, a) ⇒ Var x ^ a))
        = σ x ^ e * (Const c * substitute σ (∏(x, y)←xes. Var x ^ y))" unfolding xe
        by (simp add: ac_simps)
      show ?case unfolding id 
        apply (rule order.trans[OF vars_mult])
        using Cons x by auto
    qed force
    show ?case unfolding mc 
      apply simp
      apply (rule order.trans[OF vars_add])
      using monom Cons by auto
  qed force
qed

lemma insertion_monom_nonneg: assumes "⋀ x. α x ≥ 0" and c: "(c :: 'a :: {linordered_nonzero_semiring,ordered_semiring_0}) ≥ 0" 
  shows "insertion α (monom m c) ≥ 0" 
proof -
  define xes where "xes = var_list m" 
  show ?thesis unfolding var_list[of m c, folded xes_def]
  proof (induct xes)
    case Nil
    thus ?case using c by (auto simp: insertion_Const)
  next
    case (Cons xe xes)
    obtain x e where xe: "xe = (x,e)" by force
    have id: "insertion α (Const c * (∏a←xe # xes. case a of (x, a) ⇒ Var x ^ a))
      = α x ^ e * insertion α (Const c * (∏a←xes. case a of (x, a) ⇒ Var x ^ a))" 
      unfolding xe
      by (simp add: insertion_mult insertion_power insertion_Var algebra_simps)
    show ?case unfolding id
    proof (intro mult_nonneg_nonneg Cons)
      show "0 ≤ α x ^ e" using assms(1)[of x]
        by (induct e, auto)
    qed
  qed
qed 

lemma insertion_nonneg: assumes "⋀ x. α x ≥ (0 :: 'a :: linordered_idom)" 
  and "⋀ m. coeff p m ≥ 0"  
shows "insertion α p ≥ 0" 
proof -
  define mcs where "mcs = monom_list p" 
  from monom_list[of p] have p: "p = (∑(m, c)← mcs. monom m c)" unfolding mcs_def by auto
  have mcs: "(m,c) ∈ set mcs ⟹ c ≥ 0" for m c 
    using monom_list_coeff assms(2) unfolding mcs_def by auto
  show ?thesis using mcs unfolding p
  proof (induct mcs)
    case Nil
    thus ?case by (auto simp: insertion_Const)
  next
    case (Cons mc mcs)
    obtain m c where mc: "mc = (m,c)" by force
    with Cons have "c ≥ 0" by auto
    from insertion_monom_nonneg[OF assms(1) this]
    have m: "0 ≤ insertion α (monom m c)" by auto
    from Cons(1)[OF Cons(2)] 
    have IH: "0 ≤ insertion α (∑a←mcs. case a of (a, b) ⇒ monom a b)" by force
    show ?case unfolding mc using IH m
      by (auto simp: insertion_add)
  qed
qed 

lemma vars_sumlist: "vars (sum_list ps) ⊆ ⋃ (vars ` set ps)" 
  by (induct ps, insert vars_add, auto)

lemma coefficients_of_linear_poly: assumes linear: "total_degree (p :: 'a :: comm_semiring_1 mpoly) ≤ 1"  
  shows "∃ c a vs. p = Const c + (∑i←vs. Const (a i) * Var i)
     ∧ distinct vs ∧ set vs = vars p ∧ sorted_list_of_set (vars p) = vs ∧ (∀ v ∈ set vs. a v ≠ 0)
     ∧ (∀ i. a i = coeff p (monomial 1 i)) ∧ (c = coeff p 0)" 
proof -
  have sum_zero: "(⋀ x. x ∈ set xs ⟹ x = 0) ⟹ sum_list (xs :: 'a list) = 0" for xs by (induct xs, auto)
  define a :: "var ⇒ 'a" where "a i = coeff p (monomial 1 i)" for i
  define vs where "vs = sorted_list_of_set (vars p)" 
  define c where "c = coeff p 0" 
  define q where "q = Const c + (∑i← vs. Const (a i) * Var i)" 
  show ?thesis
  proof (intro exI[of _ vs] exI[of _ a] exI[of _ c] conjI ballI vs_def[symmetric] c_def allI a_def, 
      unfold q_def[symmetric])
    show "set vs = vars p" and dist: "distinct vs" 
      using sorted_list_of_set[of "vars p", folded vs_def] vars_finite[of p] by auto    
    show "p = q" 
      unfolding coeff_eq[symmetric]
    proof (intro ext)
      fix m
      have "coeff q m = coeff (Const c) m + (∑x←vs. a x * coeff (Var x) m)" 
        unfolding q_def coeff_add[symmetric] coeff_sum_list map_map o_def coeff_Const_mult ..
      also have "… = coeff p m" 
      proof (cases "m = 0")
        case True
        thus ?thesis by (simp add: coeff_Const coeff_Var monomial_0_iff c_def)
      next
        case False
        from False have "coeff (Const (coeff p 0)) m + (∑x←vs. a x * coeff (Var x) m) 
        = (∑x←vs. a x * coeff (Var x) m)" unfolding coeff_Const by simp
        also have "… = coeff p m" 
        proof (cases "∃ i ∈ set vs. m = monomial 1 i")
          case True
          then obtain i where i: "i ∈ set vs" and m: "m = monomial 1 i" by auto
          from split_list[OF i] obtain bef aft where id: "vs = bef @ i # aft" by auto
          from id dist have i: "i ∉ set bef" "i ∉ set aft" by auto
          have [simp]: "(monomial (Suc 0) i = monomial (Suc 0) j) = (i = j)" for i j :: var
            using monomial_inj by fastforce
          show ?thesis 
            apply (subst id, unfold coeff_Var m, simp)
            apply (subst sum_zero, use i in force)
            apply (subst sum_zero, use i in force)
            by (simp add: a_def)
        next
          case mon: False
          hence one: "(∑x←vs. a x * coeff (Var x) m) = 0" 
            by (intro sum_zero, auto simp: coeff_Var)
          have two: "coeff p m = 0" 
          proof (rule ccontr)
            assume n0: "coeff p m ≠ 0" 
            show False
            proof (cases "∃ i. m = monomial 1 i")
              case True
              with mon obtain i where i: "i ∉ set vs" and m: "m = monomial 1 i" by auto
              from n0 m have "i ∈ vars p" unfolding vars_def coeff_def 
                by (metis UN_I in_keys_iff lookup_single_eq one_neq_zero)
              with i ‹set vs = vars p› show False by auto
            next
              case False
              have "sum (lookup m) (keys m) ≤ total_degree p" using n0 unfolding coeff_def
                apply transfer
                by transfer (metis (no_types, lifting) Max_ge finite.insertI finite_imageI finite_keys image_eqI in_keys_iff insertCI)
              also have "… ≤ 1" using linear .
              finally have linear: "sum (lookup m) (keys m) ≤ 1" by auto
              consider (single) x where "keys m = {x}" | (null) "keys m = {}" | 
                (two) x y k where "keys m = {x,y} ∪ k" and "x ≠ y" by blast
              thus False
              proof cases
                case null
                hence "m = 0" by simp
                with ‹m ≠ 0› show False by simp
              next
                case (single x)
                with linear have "lookup m x ≤ 1" by auto
                moreover from single have nz: "lookup m x ≠ 0"
                  by (metis in_keys_iff insertI1)
                ultimately have "lookup m x = 1" by auto
                with single have "m = monomial 1 x"
                  by (metis Diff_cancel Diff_eq_empty_iff keys_subset_singleton_imp_monomial)
                with False show False by auto
              next
                case (two x y k)
                define k' where "k' = k - {x,y}" 
                have "keys m = insert x (insert y k')" "x ≠ y" "x ∉ k'" "y ∉ k'" "finite k'" 
                  unfolding k'_def using two finite_keys[of m] by auto
                hence "lookup m x + lookup m y ≤ sum (lookup m) (keys m)" by simp
                also have "… ≤ 1" by fact
                finally have "lookup m x = 0 ∨ lookup m y = 0" by auto
                with two show False by blast
              qed
            qed
          qed
          from one two show ?thesis by simp
        qed
        finally show ?thesis by (simp add: c_def)
      qed
      finally show "coeff p m = coeff q m" ..
    qed
  
    fix v
    assume v: "v ∈ set vs" 
    hence "v ∈ vars p" using ‹set vs = vars p› by auto
    hence vq: "v ∈ vars q" unfolding ‹p = q› .
    from split_list[OF v] obtain bef aft where vs: "vs = bef @ v # aft" by auto
    with dist have vba: "v ∉ set bef" "v ∉ set aft" by auto
    show "a v ≠ 0" 
    proof
      assume a0: "a v = 0" 
      have "v ∈ vars p" by fact
      also have "p = q" by fact
      also have "vars q ⊆ vars (sum_list (map (λ x. Const (a x) * Var x) bef)) ∪ 
         vars (Const (a v) * Var v)
         ∪ vars (sum_list (map (λ x. Const (a x) * Var x) aft))" 
        unfolding q_def vs apply simp
        apply (rule order.trans[OF vars_add], simp)
        apply (rule order.trans[OF vars_add])
        by (insert vars_add, blast)
      also have "vars (Const (a v) * Var v) = {}" unfolding a0 Const_0 by simp
      finally obtain list where v: "v ∈ vars (sum_list (map (λ x. Const (a x) * Var x) list))"   
        and not_v: "v ∉ set list" using vba by auto
      from set_mp[OF vars_sumlist v] obtain x where "x ∈ set list" and "v ∈ vars (Const (a x) * Var x)" 
        by auto
      with vars_mult[of "Const (a x)" "Var x"] not_v show False by auto
    qed
  qed
qed

text ‹Introduce notion for degree of monom›
definition degree_monom :: "(var ⇒0 nat) ⇒ nat" where
  "degree_monom m = sum (lookup m) (keys m)" 

lemma total_degree_alt_def: "total_degree p = Max (insert 0 (degree_monom ` keys (mapping_of p)))"
  unfolding degree_monom_def
  by transfer' simp

lemma degree_monon_le_total_degree: assumes "coeff p m ≠ 0" 
  shows "degree_monom m ≤ total_degree p"
  using assms unfolding total_degree_alt_def by (simp add: coeff_keys)

lemma degree_monom_eq_total_degree: assumes "p ≠ 0" 
  shows "∃ m. coeff p m ≠ 0 ∧ degree_monom m = total_degree p" 
proof (cases "total_degree p = 0")
  case False
  thus ?thesis unfolding total_degree_alt_def
    by (metis (full_types) Max_in coeff_keys empty_not_insert finite_imageI finite_insert finite_keys image_iff insertE)
next  
  case True
  from assms obtain m where "coeff p m ≠ 0"
    using coeff_all_0 by auto
  with degree_monon_le_total_degree[OF this] True show ?thesis by auto
qed

lemma degree_add_leI: "degree p x ≤ d ⟹ degree q x ≤ d ⟹ degree (p + q) x ≤ d"  
  apply transfer
  subgoal for p x d q using Poly_Mapping.keys_add[of p q]
    by (intro Max.boundedI, auto)
  done

lemma degree_sum_leI: assumes "⋀ i. i ∈ A ⟹ degree (p i) x ≤ d" 
  shows "degree (sum p A) x ≤ d" 
  using assms 
  by (induct A rule: infinite_finite_induct, auto intro: degree_add_leI)

lemma total_degree_sum_leI: assumes "⋀ i. i ∈ A ⟹ total_degree (p i) ≤ d" 
  shows "total_degree (sum p A) ≤ d" 
  using assms 
  by (induct A rule: infinite_finite_induct, auto intro: total_degree_add)

lemma total_degree_monom: assumes "c ≠ 0"  
  shows "total_degree (monom m c) = degree_monom m"
  unfolding total_degree_alt_def using assms by auto

lemma degree_Var[simp]: "degree (Var x :: 'a :: comm_semiring_1 mpoly) x = 1" 
  by (transfer, unfold Var0_def, simp)

lemma Var_neq_0[simp]: "Var x ≠ (0 :: 'a :: comm_semiring_1 mpoly)" 
proof
  assume "Var x = (0 :: 'a mpoly)" 
  from arg_cong[OF this, of "λ p. degree p x"]
  show False by simp
qed

lemma degree_Const[simp]: "degree (Const c) x = 0" 
  by transfer (auto simp: Const0_def)

lemma vars_add_subI: "vars p ⊆ A ⟹ vars q ⊆ A ⟹ vars (p + q) ⊆ A" 
  by (metis le_supI subset_trans vars_add)

lemma vars_mult_subI: "vars p ⊆ A ⟹ vars q ⊆ A ⟹ vars (p * q) ⊆ A" 
  by (metis le_supI subset_trans vars_mult)

lemma vars_eqI: assumes "vars (p :: 'a :: comm_ring_1 mpoly) ⊆ V"
  "⋀ v. v ∈ V ⟹ ∃ a b. insertion a p ≠ insertion (a(v := b)) p" 
shows "vars p = V" 
proof (rule ccontr)
  assume "¬ ?thesis" 
  with assms obtain v where "v ∈ V" and not: "v ∉ vars p" by auto
  from assms(2)[OF this(1)] obtain a b where "insertion a p ≠ insertion (a(v := b)) p" by auto
  moreover have "insertion a p = insertion (a(v := b)) p" 
    by (rule insertion_irrelevant_vars, insert not, auto) 
  ultimately show False by auto
qed


end