# Theory Polynomials.More_MPoly_Type

```(* Author: Alexander Bentkamp, Universität des Saarlandes
*)

theory More_MPoly_Type
imports MPoly_Type
begin

abbreviation "lookup == Poly_Mapping.lookup"
abbreviation "keys == Poly_Mapping.keys"

section "MPpoly Mapping extenion"

lemma lookup_Abs_poly_mapping_when_finite:
assumes "finite S"
shows "lookup (Abs_poly_mapping (λx. f x when x∈S)) = (λx. f x when x∈S)"
proof -
have "finite {x. (f x when x∈S) ≠ 0}" using assms by auto
then show ?thesis using lookup_Abs_poly_mapping by fast
qed

definition remove_key::"'a ⇒ ('a ⇒⇩0 'b::monoid_add) ⇒ ('a ⇒⇩0 'b)" where
"remove_key k0 f = Abs_poly_mapping (λk. lookup f k when k ≠ k0)"

lemma remove_key_lookup:
"lookup (remove_key k0 f) k = (lookup f k when k ≠ k0)"
unfolding remove_key_def using finite_subset by (simp add: lookup_Abs_poly_mapping)

lemma remove_key_keys: "keys f - {k} = keys (remove_key k f)" (is "?A = ?B")
proof (rule antisym; rule subsetI)
fix x assume "x ∈ ?A"
then show "x ∈ ?B" using remove_key_lookup lookup_not_eq_zero_eq_in_keys DiffD1 DiffD2 insertCI
by (metis (mono_tags, lifting) when_def)
next
fix x assume "x ∈ ?B"
then have "lookup (remove_key k f) x ≠ 0"  by blast
then show "x ∈ ?A"
qed

lemma remove_key_sum: "remove_key k f + Poly_Mapping.single k (lookup f k) = f"
proof -
{
fix k'
have rem:"(lookup f k' when k' ≠ k) = lookup (remove_key k f) k'"
using when_def by (simp add: remove_key_lookup)
have sin:"(lookup f k when k'=k) =  lookup (Poly_Mapping.single k (lookup f k)) k'"
have "lookup f k' = (lookup f k' when k' ≠ k) + ((lookup f k) when k'=k)"
unfolding when_def by fastforce
with rem sin have "lookup f k' = lookup ((remove_key k f) + Poly_Mapping.single k (lookup f k)) k'"
}
then show ?thesis by (metis poly_mapping_eqI)
qed

lemma remove_key_single[simp]: "remove_key v (Poly_Mapping.single v n) = 0"
proof -
have 0:"⋀k. (lookup (Poly_Mapping.single v n) k when k ≠ v) = 0" by (simp add: lookup_single_not_eq when_def)
show ?thesis unfolding remove_key_def 0
by auto
qed

lemma remove_key_add: "remove_key v m + remove_key v m' = remove_key v (m + m')"

lemma poly_mapping_induct [case_names single sum]:
fixes P::"('a, 'b::monoid_add) poly_mapping ⇒ bool"
assumes single:"⋀k v. P (Poly_Mapping.single k v)"
and sum:"(⋀f g k v. P f ⟹ P g ⟹ g = (Poly_Mapping.single k v) ⟹ k ∉ keys f ⟹ P (f+g))"
shows "P f" using finite_keys[of f]
proof (induction "keys f" arbitrary: f rule: finite_induct)
case (empty)
then show ?case using single[of _ 0] by (metis (full_types) aux empty_iff not_in_keys_iff_lookup_eq_zero single_zero)
next
case (insert k K f)
obtain f1 f2 where f12_def: "f1 = remove_key k f" "f2 = Poly_Mapping.single k (lookup f k)" by blast
have "P f1"
proof -
have "Suc (card (keys f1)) = card (keys f)"
using remove_key_keys finite_keys f12_def(1) by (metis (no_types) Diff_insert_absorb card_insert_disjoint insert.hyps(2) insert.hyps(4))
then show ?thesis using insert lessI by (metis Diff_insert_absorb f12_def(1) remove_key_keys)
qed
have "P f2" by (simp add: single f12_def(2))
have "f1 + f2 = f" using remove_key_sum f12_def by auto
have "k ∉ keys f1" using remove_key_keys f12_def by fast
then show ?case using ‹P f1› ‹P f2› sum[of f1 f2 k "lookup f k"] ‹f1 + f2 = f› f12_def by auto
qed

lemma map_lookup:
assumes "g 0 = 0"
shows "lookup (Poly_Mapping.map g f) x = g ((lookup f) x)"
proof -
have "(g (lookup f x) when lookup f x ≠ 0) = g (lookup f x)"
by (metis (mono_tags, lifting) assms when_def)
then have "(g (lookup f x) when x ∈ keys f) = g (lookup f x)"
using lookup_not_eq_zero_eq_in_keys [of f] by simp
then show ?thesis
by (simp add: Poly_Mapping.map_def map_fun_def in_keys_iff)
qed

assumes "keys f ∩ keys g = {}"
shows "keys f ∪ keys g = keys (f+g)"
proof
have "keys f ⊆ keys (f+g)"
proof
fix x assume "x∈keys f"
then have "lookup (f+g) x = lookup f x " by (metis add.right_neutral assms disjoint_iff_not_equal not_in_keys_iff_lookup_eq_zero plus_poly_mapping.rep_eq)
then show "x∈keys (f+g)" using ‹x∈keys f› by (metis not_in_keys_iff_lookup_eq_zero)
qed
moreover have "keys g ⊆ keys (f+g)"
proof
fix x assume "x∈keys g"
then have "lookup (f+g) x = lookup g x "  by (metis IntI add.left_neutral assms empty_iff not_in_keys_iff_lookup_eq_zero plus_poly_mapping.rep_eq)
then show "x∈keys (f+g)" using ‹x∈keys g› by (metis not_in_keys_iff_lookup_eq_zero)
qed
ultimately show "keys f ∪ keys g ⊆ keys (f+g)" by simp
next
show "keys (f + g) ⊆ keys f ∪ keys g" by (simp add: keys_add)
qed

lemma fun_when:
"f 0 = 0 ⟹ f (a when P) = (f a when P)" by (simp add: when_def)

section "MPoly extension"

lemma coeff_all_0:"(⋀m. coeff p m = 0) ⟹ p=0"
by (metis aux coeff_def mapping_of_inject zero_mpoly.rep_eq)

definition vars::"'a::zero mpoly ⇒ nat set" where
"vars p = ⋃ (keys ` keys (mapping_of p))"

lemma vars_finite: "finite (vars p)" unfolding vars_def by auto

lemma vars_monom_single: "vars (monom (Poly_Mapping.single v k) a) ⊆ {v}"
proof
fix w assume "w ∈ vars (monom (Poly_Mapping.single v k) a)"
then have "w = v" using vars_def by (metis UN_E lookup_eq_zero_in_keys_contradict lookup_single_not_eq monom.rep_eq)
then show "w ∈ {v}" by auto
qed

lemma vars_monom_keys:
assumes "a≠0"
shows "vars (monom m a) = keys m"
proof (rule antisym; rule subsetI)
fix w assume "w ∈ vars (monom m a)"
then have "lookup m w ≠ 0" using vars_def by (metis UN_E lookup_eq_zero_in_keys_contradict lookup_single_not_eq monom.rep_eq)
then show "w ∈ keys m" by (meson lookup_not_eq_zero_eq_in_keys)
next
fix w assume "w ∈ keys m"
then have "lookup m w ≠ 0" by (meson lookup_not_eq_zero_eq_in_keys)
then show "w ∈ vars (monom m a)" unfolding vars_def using assms by (metis UN_iff lookup_not_eq_zero_eq_in_keys lookup_single_eq monom.rep_eq)
qed

lemma vars_monom_subset:
shows "vars (monom m a) ⊆ keys m"
by (cases "a=0"; simp add: vars_def vars_monom_keys)

lemma vars_monom_single_cases: "vars (monom (Poly_Mapping.single v k) a) = (if k=0 ∨ a=0 then {} else {v})"
proof(cases "k=0")
assume "k=0"
then have "(Poly_Mapping.single v k) = 0" by simp
then have "vars (monom (Poly_Mapping.single v k) a) = {}"
by (metis (mono_tags, lifting) single_zero singleton_inject subset_singletonD vars_monom_single zero_neq_one)
then show ?thesis using ‹k=0› by auto
next
assume "k≠0"
then show ?thesis
proof (cases "a=0")
assume "a=0"
then have "monom (Poly_Mapping.single v k) a = 0" by (metis monom.abs_eq monom_zero single_zero)
then show ?thesis by (metis (mono_tags, opaque_lifting) ‹k ≠ 0› ‹a=0› monom.abs_eq single_zero singleton_inject subset_singletonD vars_monom_single)
next
assume "a≠0"
then have "v ∈ vars (monom (Poly_Mapping.single v k) a)" by (simp add: ‹k ≠ 0› vars_def)
then show ?thesis using ‹a≠0› ‹k ≠ 0› vars_monom_single by fastforce
qed
qed

lemma vars_monom:
assumes "a≠0"
shows "vars (monom m (1::'a::zero_neq_one)) = vars (monom m (a::'a))"
unfolding vars_monom_keys[OF assms] using vars_monom_keys[of 1] one_neq_zero by blast

lemma vars_add: "vars (p1 + p2) ⊆ vars p1 ∪ vars p2"
proof
fix w assume "w ∈ vars (p1 + p2)"
then obtain m where "w ∈ keys m" "m ∈ keys (mapping_of (p1 + p2))" by (metis UN_E vars_def)
then have "m ∈ keys (mapping_of (p1)) ∪ keys (mapping_of (p2))"
then show "w ∈ vars p1 ∪ vars p2" using vars_def ‹w ∈ keys m› by fastforce
qed

lemma vars_mult: "vars (p*q) ⊆ vars p ∪ vars q"
proof
fix x assume "x∈vars (p*q)"
then obtain m where "m∈keys (mapping_of (p*q))" "x∈keys m"
using vars_def  by blast
then have "m∈keys (mapping_of p * mapping_of q)"
then obtain a b where "m=a + b" "a ∈ keys (mapping_of p)" "b ∈ keys (mapping_of q)"
using keys_mult by blast
then have "x ∈ keys a ∪ keys b"
using Poly_Mapping.keys_add ‹x ∈ keys m› by force
then show "x ∈ vars p ∪ vars q" unfolding vars_def
using ‹a ∈ keys (mapping_of p)› ‹b ∈ keys (mapping_of q)› by blast
qed

assumes "p2 = monom m a" "m ∉ keys (mapping_of p1)"
shows "vars (p1 + p2) = vars p1 ∪ vars p2"
proof -
have "keys (mapping_of p2) ⊆ {m}" using monom_def keys_single assms by auto
have "keys (mapping_of (p1+p2)) = keys (mapping_of p1) ∪ keys (mapping_of p2)"
using keys_add by (metis Int_insert_right_if0 ‹keys (mapping_of p2) ⊆ {m}› assms(2) inf_bot_right plus_mpoly.rep_eq subset_singletonD)
then show ?thesis unfolding vars_def by simp
qed

lemma vars_setsum: "finite S ⟹ vars (∑m∈S. f m) ⊆ (⋃m∈S. vars (f m))"
proof (induction S rule:finite_induct)
case empty
then show ?case by (metis UN_empty eq_iff monom_zero sum.empty single_zero vars_monom_single_cases)
next
case (insert s S)
then have "vars (sum f (insert s S)) = vars (f s + sum f S)" by (metis sum.insert)
also have "... ⊆ vars (f s) ∪ vars (sum f S)" by (simp add: vars_add)
also have "... ⊆ (⋃m∈insert s S. vars (f m))" using insert.IH by auto
finally show ?case by metis
qed

lemma coeff_monom: "coeff (monom m a) m' = (a when m'=m)"
by (simp add: coeff_def lookup_single_not_eq when_def)

lemma coeff_add: "coeff p m + coeff q m = coeff (p+q) m"

lemma coeff_eq: "coeff p = coeff q ⟷ p=q" by (simp add: coeff_def lookup_inject mapping_of_inject)

lemma coeff_monom_mult: "coeff ((monom m' a)  * q) (m' + m)  = a * coeff q m"
unfolding coeff_def times_mpoly.rep_eq lookup_mult mapping_of_monom lookup_single when_mult

lemma one_term_is_monomial:
assumes "card (keys (mapping_of p)) ≤ 1"
obtains m where "p = monom m (coeff p m)"
proof (cases "keys (mapping_of p) = {}")
case True
then show ?thesis using aux coeff_def empty_iff mapping_of_inject mapping_of_monom not_in_keys_iff_lookup_eq_zero single_zero by (metis (no_types) that)
next
case False
then obtain m where "keys (mapping_of p) = {m}" using assms by (metis One_nat_def Suc_leI antisym card_0_eq card_eq_SucD finite_keys neq0_conv)
have "p = monom m (coeff p m)"
unfolding mapping_of_inject[symmetric]
by (rule poly_mapping_eqI, metis (no_types, lifting) ‹keys (mapping_of p) = {m}›
coeff_def keys_single lookup_single_eq  mapping_of_monom not_in_keys_iff_lookup_eq_zero
singletonD)
then show ?thesis ..
qed

(* remove_term is eventually unnessecary *)
definition remove_term::"(nat ⇒⇩0 nat) ⇒ 'a::zero mpoly ⇒ 'a mpoly" where
"remove_term m0 p = MPoly (Abs_poly_mapping (λm. coeff p m when m ≠ m0))"

lemma remove_term_coeff: "coeff (remove_term m0 p) m = (coeff p m when m ≠ m0)"
proof -
have "{m. (coeff p m when m ≠ m0) ≠ 0} ⊆ {m. coeff p m ≠ 0}" by auto
then have "finite {m. (coeff p m when m ≠ m0) ≠ 0}" unfolding coeff_def using finite_subset by auto
then have "lookup (Abs_poly_mapping (λm. coeff p m when m ≠ m0)) m = (coeff p m when m ≠ m0)" using lookup_Abs_poly_mapping by fastforce
then show ?thesis unfolding remove_term_def using coeff_def by (metis (mono_tags, lifting) Quotient_mpoly Quotient_rep_abs_fold_unmap)
qed

lemma coeff_keys: "m ∈ keys (mapping_of p) ⟷ coeff p m ≠ 0"

lemma remove_term_keys:
shows "keys (mapping_of p) - {m} = keys (mapping_of (remove_term m p))" (is "?A = ?B")
proof
show "?A ⊆ ?B"
proof
fix m' assume "m'∈?A"
then show "m' ∈ ?B" by (simp add: coeff_keys remove_term_coeff)
qed
show "?B ⊆ ?A"
proof
fix m' assume "m'∈ ?B"
then show "m' ∈ ?A" by (simp add: coeff_keys remove_term_coeff)
qed
qed

lemma remove_term_sum: "remove_term m p + monom m (coeff p m) = p"
proof -
have "coeff p = (λm'. (coeff p m' when m' ≠ m) + ((coeff p m) when m'=m))" unfolding when_def by fastforce
moreover have "coeff (remove_term m p + monom m (coeff p m)) = ..."
using remove_term_coeff coeff_monom coeff_add by (metis (no_types))
ultimately show ?thesis using coeff_eq by auto
qed

lemma mpoly_induct [case_names monom sum]:
assumes monom:"⋀m a. P (monom m a)"
and sum:"(⋀p1 p2 m a. P p1 ⟹ P p2 ⟹ p2 = (monom m a) ⟹ m ∉ keys (mapping_of p1) ⟹ P (p1+p2))"
shows "P p" using assms
using poly_mapping_induct[of "λp :: (nat ⇒⇩0 nat) ⇒⇩0 'a. P (MPoly p)"] MPoly_induct monom.abs_eq plus_mpoly.abs_eq
by (metis (no_types) MPoly_inverse UNIV_I)

lemma monom_pow:"monom (Poly_Mapping.single v n0) a ^ n = monom (Poly_Mapping.single v (n0*n)) (a ^ n)"
apply (induction n)
apply auto
by (metis (no_types, lifting) mult_monom single_add)

lemma insertion_fun_single: "insertion_fun f (λm. (a when (Poly_Mapping.single (v::nat) (n::nat)) = m)) = a * f v ^ n" (is "?i = _")
proof -
have setsum_single:"⋀ a f. (∑m∈{a}. f m) = f a"
by (metis add.right_neutral empty_Diff finite.emptyI sum.empty sum.insert_remove)

have 1:"?i = (∑m. (a when Poly_Mapping.single v n = m) * (∏v. f v ^ lookup m v))"
unfolding insertion_fun_def by metis
have "∀m. m ≠ Poly_Mapping.single v n ⟶ (a when Poly_Mapping.single v n = m) = 0" by simp

have "(∑m∈{Poly_Mapping.single v n}. (a when Poly_Mapping.single v n = m) * (∏v. f v ^ lookup m v)) = ?i"
unfolding 1 when_mult unfolding when_def by auto
then have 2:"?i = a * (∏va. f va ^ lookup (Poly_Mapping.single v n) va)"
unfolding setsum_single[of "λm. (a when Poly_Mapping.single v n = m) * (∏v. f v ^ lookup m v)" "Poly_Mapping.single k v"]
by auto
have "∀v0. v0≠v ⟶ lookup (Poly_Mapping.single v n) v0 = 0" by (simp add: lookup_single_not_eq)
then have "∀va. va≠v ⟶ f va ^ lookup (Poly_Mapping.single v n) va = 1"  by simp
then have "a * (∏va∈{v}. f va ^ lookup (Poly_Mapping.single v n) va) = ?i" unfolding 2
using Prod_any.expand_superset[of "{v}" "λva. f va ^ lookup (Poly_Mapping.single v n) va", simplified]
by fastforce
then show ?thesis by simp
qed

lemma insertion_single[simp]: "insertion f (monom (Poly_Mapping.single (v::nat) (n::nat)) a) = a * f v ^ n"
using insertion_fun_single  Sum_any.cong insertion.rep_eq insertion_aux.rep_eq insertion_fun_def
mapping_of_monom single.rep_eq by (metis (no_types, lifting))

lemma insertion_fun_irrelevant_vars:
fixes p::"((nat ⇒⇩0 nat) ⇒ 'a::comm_ring_1)"
assumes "⋀m v. p m ≠ 0 ⟹ lookup m v ≠ 0 ⟹ f v = g v"
shows "insertion_fun f p = insertion_fun g p"
proof -
{
fix m::"nat⇒⇩0nat"
assume "p m ≠ 0"
then have "(∏v. f v ^ lookup m v) = (∏v. g v ^ lookup m v)"
using assms by (metis power_0)
}
then show ?thesis unfolding insertion_fun_def by (metis (no_types, lifting) mult_not_zero)
qed

lemma insertion_aux_irrelevant_vars:
fixes p::"((nat ⇒⇩0 nat) ⇒⇩0 'a::comm_ring_1)"
assumes "⋀m v. lookup p m ≠ 0 ⟹ lookup m v ≠ 0 ⟹ f v = g v"
shows "insertion_aux f p = insertion_aux g p"
using insertion_fun_irrelevant_vars[of "lookup p" f g] assms
by (metis insertion_aux.rep_eq)

lemma insertion_irrelevant_vars:
fixes p::"'a::comm_ring_1 mpoly"
assumes "⋀v. v∈vars p ⟹ f v = g v"
shows "insertion f p = insertion g p"
proof -
{
fix m v assume "lookup (mapping_of p) m ≠ 0" "lookup m v ≠ 0"
then have "v ∈ vars p" unfolding vars_def by (meson UN_I lookup_not_eq_zero_eq_in_keys)
then have "f v = g v" using assms by auto
}
then show ?thesis
unfolding insertion_def using insertion_aux_irrelevant_vars[of "mapping_of p"]
by (metis insertion.rep_eq insertion_def)
qed

section "Nested MPoly"

definition reduce_nested_mpoly::"'a::comm_ring_1 mpoly mpoly ⇒ 'a mpoly" where
"reduce_nested_mpoly pp = insertion (λv. monom (Poly_Mapping.single v 1) 1) pp"

lemma reduce_nested_mpoly_sum:
fixes p1::"'a::comm_ring_1 mpoly mpoly"
shows "reduce_nested_mpoly (p1 + p2) = reduce_nested_mpoly p1 + reduce_nested_mpoly p2"

lemma reduce_nested_mpoly_prod:
fixes p1::"'a::comm_ring_1 mpoly mpoly"
shows "reduce_nested_mpoly (p1 * p2) = reduce_nested_mpoly p1 * reduce_nested_mpoly p2"

lemma reduce_nested_mpoly_0:
shows "reduce_nested_mpoly 0 = 0" by (simp add: reduce_nested_mpoly_def)

lemma insertion_nested_poly:
fixes pp::"'a::comm_ring_1 mpoly mpoly"
shows "insertion f (insertion (λv. monom 0 (f v)) pp) = insertion f (reduce_nested_mpoly pp)"
proof (induction pp rule:mpoly_induct)
case (monom m a)
then show ?case
proof (induction m arbitrary:a rule:poly_mapping_induct)
case (single v n)
show ?case unfolding reduce_nested_mpoly_def
using monom_pow[of 0 0 "f v" n] apply simp
using insertion_single[of f 0 0] by auto
next
case (sum m1 m2 k v)
then have "insertion f (insertion (λv. monom 0 (f v)) (monom m1 a * monom m2 1))
= insertion f (reduce_nested_mpoly (monom m1 a * monom m2 1))" unfolding reduce_nested_mpoly_prod insertion_mult by metis
then show ?case using mult_monom[of m1 a m2 1] by auto
qed
next
case (sum p1 p2 m a)
qed

definition extract_var::"'a::comm_ring_1 mpoly ⇒ nat ⇒ 'a::comm_ring_1 mpoly mpoly" where
"extract_var p v = (∑m. monom (remove_key v m) (monom (Poly_Mapping.single v (lookup m v)) (coeff p m)))"

lemma extract_var_finite_set:
assumes "{m'. coeff p m' ≠ 0} ⊆ S"
assumes "finite S"
shows "extract_var p v = (∑m∈S. monom (remove_key v m) (monom (Poly_Mapping.single v (lookup m v)) (coeff p m)))"
proof-
{
fix m' assume "coeff p m' = 0"
then have "monom (remove_key v m') (monom (Poly_Mapping.single v (lookup m' v)) (coeff p m')) = 0"
using monom.abs_eq monom_zero single_zero by metis
}
then have 0:"{a. monom (remove_key v a) (monom (Poly_Mapping.single v (lookup a v)) (coeff p a)) ≠ 0} ⊆ S"
using ‹{m'. coeff p m' ≠ 0} ⊆ S› by fastforce
then show ?thesis
unfolding extract_var_def using Sum_any.expand_superset [OF ‹finite S› 0] by metis
qed

lemma extract_var_non_zero_coeff: "extract_var p v = (∑m∈{m'. coeff p m' ≠ 0}. monom (remove_key v m) (monom (Poly_Mapping.single v (lookup m v)) (coeff p m)))"
using extract_var_finite_set  coeff_def finite_lookup order_refl by (metis (no_types, lifting) Collect_cong sum.cong)

lemma extract_var_sum: "extract_var (p+p') v = extract_var p v + extract_var p' v"
proof -
define S where "S = {m. coeff p m ≠ 0} ∪ {m. coeff p' m ≠ 0} ∪ {m. coeff (p+p') m ≠ 0}"
have subsets:"{m. coeff p m ≠ 0} ⊆ S" "{m. coeff p' m ≠ 0} ⊆ S" "{m. coeff (p+p') m ≠ 0} ⊆ S"
unfolding S_def by auto
have "finite S" unfolding S_def using coeff_def finite_lookup
by (metis (mono_tags) Collect_disj_eq finite_Collect_disjI)
then show ?thesis  unfolding
extract_var_finite_set[OF subsets(1) ‹finite S›]
extract_var_finite_set[OF subsets(2) ‹finite S›]
extract_var_finite_set[OF subsets(3) ‹finite S›]
by metis
qed

lemma extract_var_monom:
shows "extract_var (monom m a) v = monom (remove_key v m) (monom (Poly_Mapping.single v (lookup m v)) a)"
proof (cases "a = 0")
assume "a ≠ 0"
have 0:"{m'. coeff (monom m a) m' ≠ 0} = {m}"
unfolding coeff_monom using ‹a ≠ 0› by auto
show ?thesis
unfolding extract_var_non_zero_coeff unfolding 0 unfolding coeff_monom
using sum.insert[OF finite.emptyI, unfolded sum.empty add.right_neutral] when_def
by auto
next
assume "a = 0"
have 0:"{m'. coeff (monom m a) m' ≠ 0} = {}"
unfolding coeff_monom using ‹a = 0› by auto
show ?thesis unfolding extract_var_non_zero_coeff 0
using ‹a = 0› monom.abs_eq monom_zero sum.empty single_zero by (metis (no_types, lifting))
qed

lemma extract_var_monom_mult:
shows "extract_var (monom (m+m') (a*b)) v = extract_var (monom m a) v * extract_var (monom m' b) v"

lemma extract_var_single: "extract_var (monom (Poly_Mapping.single v n) a) v = monom 0 (monom (Poly_Mapping.single v n) a)"
unfolding extract_var_monom by simp

lemma extract_var_single':
assumes "v ≠ v'"
shows "extract_var (monom (Poly_Mapping.single v n) a) v' = monom (Poly_Mapping.single v n) (monom 0 a)"
unfolding extract_var_monom using assms by (metis add.right_neutral lookup_single_not_eq remove_key_sum single_zero)

lemma reduce_nested_mpoly_extract_var:
fixes p::"'a::comm_ring_1 mpoly"
shows "reduce_nested_mpoly (extract_var p v) = p"
proof (induction p rule:mpoly_induct)
case (monom m a)
then show ?case
proof (induction m arbitrary:a rule:poly_mapping_induct)
case (single v' n)
show ?case
proof (cases "v' = v")
case True
then show ?thesis
by (metis (no_types, lifting) insertion_single mult.right_neutral power_0
reduce_nested_mpoly_def single_zero extract_var_single)
next
case False
then show ?thesis unfolding extract_var_single'[OF False] reduce_nested_mpoly_def insertion_single
qed
next
case (sum m m' v n a)
then show ?case
using extract_var_monom_mult[of m m' a 1] reduce_nested_mpoly_prod by (metis mult.right_neutral mult_monom)
qed
next
case (sum p1 p2 m a)
then show ?case unfolding extract_var_sum reduce_nested_mpoly_sum by auto
qed

lemma vars_extract_var_subset: "vars (extract_var p v) ⊆ vars p"
proof
have "finite {m'. coeff p m' ≠ 0}" by (simp add: coeff_def)
fix x assume "x ∈ vars (extract_var p v)"
then have "x ∈ vars (∑m∈{m'. coeff p m' ≠ 0}. monom (remove_key v m) (monom (Poly_Mapping.single v (lookup m v)) (coeff p m)))"
unfolding extract_var_non_zero_coeff by metis
then have "x ∈ (⋃m∈{m'. coeff p m' ≠ 0}. vars (monom (remove_key v m) (monom (Poly_Mapping.single v (lookup m v)) (coeff p m))))"
using vars_setsum[OF ‹finite {m'. coeff p m' ≠ 0}›] by auto
then obtain m where "m∈{m'. coeff p m' ≠ 0}" "x ∈ vars (monom (remove_key v m) (monom (Poly_Mapping.single v (lookup m v)) (coeff p m)))"
by blast
show "x ∈ vars p" by (metis (mono_tags, lifting) DiffD1 UN_I ‹m ∈ {m'. coeff p m' ≠ 0}›
‹x ∈ vars (monom (remove_key v m) (monom (Poly_Mapping.single v (lookup m v)) (coeff p m)))›
coeff_keys mem_Collect_eq remove_key_keys subsetCE vars_def vars_monom_subset)
qed

lemma v_not_in_vars_extract_var: "v ∉ vars (extract_var p v)"
proof -
have "finite {m'. coeff p m' ≠ 0}" by (simp add: coeff_def)
have "⋀m. m∈{m'. coeff p m' ≠ 0} ⟹ v ∉ vars (monom (remove_key v m) (monom (Poly_Mapping.single v (lookup m v)) (coeff p m)))"
by (metis Diff_iff remove_key_keys singletonI subsetCE vars_monom_subset)
then have "v ∉ (⋃m∈{m'. coeff p m' ≠ 0}. vars (monom (remove_key v m) (monom (Poly_Mapping.single v (lookup m v)) (coeff p m))))"
by simp
then show ?thesis
unfolding extract_var_non_zero_coeff using vars_setsum[OF ‹finite {m'. coeff p m' ≠ 0}›] by blast
qed

lemma vars_coeff_extract_var: "vars (coeff (extract_var p v) j) ⊆ {v}"
proof (induction p rule:mpoly_induct)
case (monom m a)
then show ?case unfolding extract_var_monom coeff_monom vars_monom_single_cases
by (metis monom_zero single_zero vars_monom_single when_def)
next
case (sum p1 p2 m a)
then show ?case unfolding extract_var_sum coeff_add[symmetric]
by (metis (no_types, lifting) Un_insert_right insert_absorb2 subset_insertI2 subset_singletonD sup_bot.right_neutral vars_add)
qed

definition replace_coeff
where "replace_coeff f p = MPoly (Abs_poly_mapping (λm. f (lookup (mapping_of p) m)))"

lemma coeff_replace_coeff:
assumes "f 0 = 0"
shows "coeff (replace_coeff f p) m = f (coeff p m)"
proof -
have 0:"finite {m. f (lookup (mapping_of p) m) ≠ 0}"
unfolding coeff_def[symmetric] by (metis (mono_tags, lifting) Collect_mono assms(1) coeff_def finite_lookup finite_subset)+
then show ?thesis unfolding replace_coeff_def coeff_def using lookup_Abs_poly_mapping[OF 0]
by (metis (mono_tags, lifting) Quotient_mpoly Quotient_rep_abs_fold_unmap)
qed

lemma replace_coeff_monom:
assumes "f 0 = 0"
shows "replace_coeff f (monom m a) = monom m (f a)"
unfolding replace_coeff_def
unfolding  mapping_of_inject[symmetric] lookup_inject[symmetric] apply (rule HOL.ext)
unfolding lookup_single  mapping_of_monom fun_when[of f, OF ‹f 0 = 0›]
by (metis coeff_def coeff_monom lookup_single lookup_single_not_eq monom.abs_eq single.abs_eq)

assumes "f 0 = 0"
assumes "⋀a b. f (a+b) = f a + f b"
shows "replace_coeff f (p1 + p2) = replace_coeff f p1 + replace_coeff f p2"
proof -
have "finite {m. f (lookup (mapping_of p1) m) ≠ 0}"
"finite {m. f (lookup (mapping_of p2) m) ≠ 0}"
unfolding coeff_def[symmetric] by (metis (mono_tags, lifting) Collect_mono assms(1) coeff_def finite_lookup finite_subset)+
then show ?thesis
unfolding replace_coeff_def plus_mpoly.rep_eq unfolding Poly_Mapping.plus_poly_mapping.rep_eq
unfolding assms(2) plus_mpoly.abs_eq using Poly_Mapping.plus_poly_mapping.abs_eq[unfolded eq_onp_def] by fastforce
qed

lemma insertion_replace_coeff:
fixes pp::"'a::comm_ring_1 mpoly mpoly"
shows "insertion f (replace_coeff (insertion f) pp) = insertion f (reduce_nested_mpoly pp)"
proof (induction pp rule:mpoly_induct)
case (monom m a)
then show ?case
proof (induction m arbitrary:a rule:poly_mapping_induct)
case (single v n)
show ?case unfolding reduce_nested_mpoly_def  unfolding replace_coeff_monom[of "insertion f", OF insertion_zero]
insertion_single insertion_mult using insertion_single by (simp add: monom_pow)
next
case (sum m1 m2 k v)
have "replace_coeff (insertion f) (monom m1 a * monom m2 1) = replace_coeff (insertion f) (monom m1 a) * replace_coeff (insertion f) (monom m2 1)"
then have "insertion f (replace_coeff (insertion f) (monom m1 a * monom m2 1)) = insertion f (reduce_nested_mpoly (monom m1 a * monom m2 1))"
unfolding reduce_nested_mpoly_prod insertion_mult
by (simp add: insertion_mult sum.IH(1) sum.IH(2))
then show ?case using mult_monom[of m1 a m2 1] by auto
qed
next
case (sum p1 p2 m a)
then show ?case using reduce_nested_mpoly_sum insertion_add
qed

lemma replace_coeff_extract_var_cong:
assumes "f v = g v"
shows "replace_coeff (insertion f) (extract_var p v) = replace_coeff (insertion g) (extract_var p v)"
by (induction p rule:mpoly_induct;simp add: assms extract_var_monom replace_coeff_monom

lemma vars_replace_coeff:
assumes "f 0 = 0"
shows "vars (replace_coeff f p) ⊆ vars p"
unfolding vars_def apply (rule subsetI) unfolding mem_simps(8) coeff_keys
using assms coeff_replace_coeff by (metis coeff_keys)

(* Polynomial functions *)

definition polyfun :: "nat set ⇒ ((nat ⇒ 'a::comm_semiring_1) ⇒ 'a) ⇒ bool"
where "polyfun N f = (∃p. vars p ⊆ N ∧ (∀x. insertion x p = f x))"

lemma polyfunI: "(⋀P. (⋀p. vars p ⊆ N ⟹ (⋀x. insertion x p = f x) ⟹ P) ⟹ P) ⟹ polyfun N f"
unfolding polyfun_def by metis

lemma polyfun_subset: "N⊆N' ⟹ polyfun N f ⟹ polyfun N' f"
unfolding polyfun_def by blast

lemma polyfun_const: "polyfun N (λ_. c)"
proof -
have "⋀x. insertion x (monom 0 c) = c" using insertion_single by (metis insertion_one monom_one mult.commute mult.right_neutral single_zero)
then show ?thesis unfolding polyfun_def by (metis (full_types) empty_iff keys_single single_zero subsetI subset_antisym vars_monom_subset)
qed

assumes "polyfun N f" "polyfun N g"
shows "polyfun N (λx. f x + g x)"
proof -
obtain p1 p2 where "vars p1 ⊆ N" "∀x. insertion x p1 = f x"
"vars p2 ⊆ N" "∀x. insertion x p2 = g x"
using polyfun_def assms by metis
then have "vars (p1 + p2) ⊆ N" "∀x. insertion x (p1 + p2) = f x + g x"
using vars_add using Un_iff subsetCE subsetI apply blast
by (simp add: ‹∀x. insertion x p1 = f x› ‹∀x. insertion x p2 = g x› insertion_add)
then show ?thesis using polyfun_def by blast
qed

lemma polyfun_mult:
assumes "polyfun N f" "polyfun N g"
shows "polyfun N (λx. f x * g x)"
proof -
obtain p1 p2 where "vars p1 ⊆ N" "∀x. insertion x p1 = f x"
"vars p2 ⊆ N" "∀x. insertion x p2 = g x"
using polyfun_def assms by metis
then have "vars (p1 * p2) ⊆ N" "∀x. insertion x (p1 * p2) = f x * g x"
using vars_mult using Un_iff subsetCE subsetI apply blast
by (simp add: ‹∀x. insertion x p1 = f x› ‹∀x. insertion x p2 = g x› insertion_mult)
then show ?thesis using polyfun_def by blast
qed

lemma polyfun_Sum:
assumes "finite I"
assumes "⋀i. i∈I ⟹ polyfun N (f i)"
shows "polyfun N (λx. ∑i∈I. f i x)"
using assms
apply (induction I rule:finite_induct)

lemma polyfun_Prod:
assumes "finite I"
assumes "⋀i. i∈I ⟹ polyfun N (f i)"
shows "polyfun N (λx. ∏i∈I. f i x)"
using assms
apply (induction I rule:finite_induct)