Theory Poly_Expansions

theory Poly_Expansions
  imports Total_Degree
begin

subsection ‹Explicit expansions›

lemma mpoly_keys_subset: "keys (mapping_of P)  Abs_poly_mapping ` (!0) ` 
  {i. length i = max_vars P + 1  sum_list i  total_degree P}"
proof 
  fix k assume assm: "k  keys (mapping_of P)"
  define l::"nat list" where "l  map (lookup k  nat) [0..max_vars P]"

  have 0: "lookup k i = l!0i" for i
  proof (cases "i  max_vars P")
    case True
    have "l!0i = (lookup k  nat) ([0..max_vars P]!i)"
      by (smt (verit, best) Suc_as_int True l_def le_imp_less_Suc length_map length_upto nth0_nth
          nth_map)
    also have "... = lookup k i"
      by (simp add: True)
    finally show ?thesis 
      by simp
  next
    case False    
    hence "lookup k i = 0"
      unfolding max_vars_def vars_def
      by (metis Suc_eq_plus1 after_max_vars assm in_keys_iff max_vars_def not_less_eq_eq vars_def)
    thus ?thesis 
      using False by (simp add: l_def nth0_0)
  qed

  have 1: "k = Abs_poly_mapping ((!0) l)"
    by (simp add: 0 poly_mapping_eqI)

  have "sum_list l = (i=0..<length l. l!0i)"
    using sum_list_sum_nth by (metis nth0_nth sum.ivl_cong)
  also have "... = sum (lookup k) (keys k)"
    by (metis "1" calculation keys.rep_eq lookup_Abs_poly_mapping_nth0 nth0_sum_list)
  also have "...  total_degree P"
    unfolding total_degree_def using assm by simp
  finally have 2: "sum_list l  total_degree P" .

  show "k  Abs_poly_mapping ` (!0) ` {i. length i = max_vars P + 1  sum_list i  total_degree P}"
    using 1 2 l_def by simp
qed

lemma monom_single: "monom (Poly_Mapping.single v p) a = Const a * (Var v) ^ p"
proof (induction p)
  case 0
  show ?case by (simp add: Const.abs_eq Const0_def monom.abs_eq)
next
  case (Suc p)
  show ?case
    unfolding power_Suc2 semigroup_mult_class.mult.assoc[symmetric] Suc[symmetric]
    by (metis Suc_eq_plus1 Var.abs_eq Var0_def monom.abs_eq mult.right_neutral mult_monom single_add)
qed

lemma coeff_monom :
"coeff (monom m a) m' = (if m=m' then a else 0)"
  unfolding coeff_def monom_def using lookup_single_eq lookup_single_not_eq
  by (metis monom.rep_eq monom_def)

lemma monom_eq_var:
"monom (Abs_poly_mapping (λv'. (Suc 0) when v=v')) 1 = MPoly (Var0 v)"
proof -
  have 0: "Poly_Mapping.single (Abs_poly_mapping (λv'. Suc 0 when v=v')) 1 = Var0 v"
    unfolding Var0_def single_def by simp
  hence "MPoly (Poly_Mapping.single (Abs_poly_mapping (λv'. Suc 0 when v=v')) 1) = MPoly (Var0 v)"
    by metis
  thus ?thesis unfolding monom_def by simp
qed

lemma monom_eq_power_var:
  "monom (Abs_poly_mapping (λv'. n when v = v')) 1 = MPoly (Var0 v)^n"
proof (induction n)
  case 0
  then show ?case by simp
next
  case (Suc n)
  note t=this
  have 0: "lookup (Abs_poly_mapping (λv'. n when v = v')) = (λv'. n when v = v')
       lookup (Abs_poly_mapping (λv'. Suc 0 when v = v')) = (λv'. Suc 0 when v = v')"
    using lookup_Abs_poly_mapping by (simp add: when_def)
  hence "(λk. lookup (Abs_poly_mapping (λv'. n when v = v')) k
         + lookup (Abs_poly_mapping (λv'. Suc 0 when v = v')) k)
      = (λk. (λv'. n when v = v') k + (λv'. Suc 0 when v = v') k)"
    by simp
  also have "... = (λk. Suc n when v = k)"
    by (metis "when"(1) "when"(2) add_0_iff add_Suc_right)
  finally have 0: "(λk. lookup (Abs_poly_mapping (λv'. n when v = v')) k
         + lookup (Abs_poly_mapping (λv'. Suc 0 when v = v')) k)
      = (λk. Suc n when v = k)" by blast
  have "Abs_poly_mapping (λv'. n when v = v') + Abs_poly_mapping (λv'. Suc 0 when v = v')
      = Abs_poly_mapping (λv'. Suc n when v = v')"
    unfolding plus_poly_mapping_def apply simp using 0 by simp
  then show ?case using monom_eq_var[of v] mult_monom[of "Abs_poly_mapping (λv'. n when v = v')" 1
        "Abs_poly_mapping (λv'. Suc 0 when v = v')" 1] power_Suc2 lambda_one t by metis
qed

lemma coeff_prod_monom_not_enough:
  fixes m m' a
  assumes "k. lookup m k < lookup m' k"
  shows "coeff (monom m' a * Q) m = 0"
proof -
  from assms obtain k where "lookup m k < lookup m' k" by auto
  hence 0: "lookup m k  lookup m' k + v" for v
    by simp
  have "m  m' + m''" for m''
    unfolding lookup_inject[symmetric]
    using 0[of "lookup m'' k"]
    by (auto simp: lookup_add)
  thus ?thesis
    unfolding coeff_def times_mpoly.rep_eq times_poly_mapping.rep_eq prod_fun_def
    unfolding coeff_def[symmetric] coeff_monom
    unfolding when_def[symmetric] when_mult Sum_any_when_equal'
    apply (subst zero_class.when(2))
    by auto
qed

lemma finite_sum_mpoly_commute:
  "finite S  (mS. MPoly (f m)) = MPoly (mS. f m)"
  by (simp add: add_to_finite_sum plus_mpoly.abs_eq zero_mpoly_def)

lemma finite_prod_mpoly_commute:
  "finite S  (mS. MPoly (f m)) = MPoly (mS. f m)"
  by (simp add: mult_to_finite_prod one_mpoly_def times_mpoly.abs_eq)

lemma power_mpoly_commute: "MPoly a ^ p = MPoly (a ^ p)"
  by (transfer; auto)

lemma finite_sum_poly_mapping_commute :
"finite S  (m. finite {x. f m x0}) 
(mS. Abs_poly_mapping (f m)) = Abs_poly_mapping (λx. mS. f m x)"
proof (induction "card S" arbitrary:S)
  case 0
  then show ?case by force
next
  case (Suc n)
  then obtain y where y_prop: "yS" by fastforce
  define S' where "S' = S - {y}"
  hence card_S':"card S' = n" using Suc by (simp add: y_prop)
  hence IH: "(mS'. Abs_poly_mapping (f m)) = Abs_poly_mapping (λx. mS'. f m x)"
    using Suc by force
  have sum_disj: "(mS'. f m x) + f y x = (mS. f m x)" for x using S'_def
    by (metis Suc.prems(1) add.commute sum.remove y_prop)
  have "finite S'" by (simp add: S'_def Suc.prems(1))
  hence "(mS'. f m x = 0)  (mS'. f m x) = 0" for x by simp
  hence "{x. (mS'. f m x)  0}  (mS'. {x. f m x  0})"
    by (smt (verit, best) UN_I mem_Collect_eq subsetI)
  hence fin_sum: "finite {x. (mS'. f m x)  0}" using Suc(4) finite S' finite_subset by blast
  have "(mS. Abs_poly_mapping (f m)) = (mS'. Abs_poly_mapping (f m)) + Abs_poly_mapping (f y)"
    using S'_def by (metis Suc.prems(1) add.commute sum.remove y_prop)
  also have "... = Abs_poly_mapping (λx. mS'. f m x) + Abs_poly_mapping (f y)" using IH by argo
  also have "... = Abs_poly_mapping (λx. (mS'. f m x) + f y x)"
    unfolding plus_poly_mapping_def apply simp
    using lookup_Abs_poly_mapping fin_sum Suc(4)[of y] by force
  also have "... = Abs_poly_mapping (λx. (mS. f m x))" using sum_disj by presburger
  finally show ?case by blast
qed

lemma coeff_sum_monom:
  assumes "finite {m. f m0}"
  shows "coeff (Sum_any (λm. monom m (f m))) = f"
proof -
  define S where "S = {m. f m0}"
  have fin_S: "finite S" unfolding S_def using assms by simp
  have "Sum_any (λm. monom m (f m)) = Sum_any (λm. MPoly (Poly_Mapping.single m (f m)))"
    unfolding monom_def by simp
  also have "... = (mS. MPoly (Poly_Mapping.single m (f m)))"
    unfolding S_def                   
    by (metis (mono_tags, lifting) Collect_mono Sum_any.expand_superset assms single_zero zero_mpoly_def)
  also have "... = MPoly (mS. (Poly_Mapping.single m (f m)))"
    using finite_sum_mpoly_commute fin_S by fastforce
  finally have 0: "Sum_any (λm. monom m (f m)) = (MPoly (aS. (Poly_Mapping.single a (f a))))"
    by simp

  have 1:"(aS. (Poly_Mapping.single a (f a))) = Abs_poly_mapping (λm. aS. f a when a = m)"
    unfolding single_def apply simp
    using finite_sum_poly_mapping_commute[of S "(λa. (λk'. f a when a = k'))"] fin_S by fastforce
  have "(aS. f a when a = m) = f m" for m
  proof (cases "mS")
    case True
    hence "finite S  S = (S-{m}){m}  (S-{m}){m}={}" using fin_S by blast
    hence "(aS. f a when a = m) = (a(S-{m}). f a when a = m) + (a{m}. f a when a = m)"
      by (meson True bot.extremum insert_subset sum.subset_diff)
    also have "... = f m" by auto
    finally show ?thesis using mS by presburger
  next
    case False
    hence "aS  (f a when a = m) = 0" for a unfolding when_def by force
    hence "(aS. f a when a = m) = 0" by simp
    then show ?thesis using mS S_def by force
  qed
  hence "(aS. (Poly_Mapping.single a (f a))) = Abs_poly_mapping f"
    using 1 by force
  hence "coeff (Sum_any (λm. monom m (f m))) = lookup (mapping_of (MPoly (Abs_poly_mapping f)))"
    using 0 coeff_def by force
  thus ?thesis using MPoly_inverse UNIV_I lookup_Abs_poly_mapping[of f] assms by metis
qed

lemma coeff_sum_monom_bis:
  assumes "finite {m. f m0}" and "finite S"
  shows "coeff (mS. monom m (f m)) m'= (if m' S then f m' else 0)"
proof -
  define g where "g = (λm. if mS then f m else 0)"
  hence "{m. g m  0}  {m. f m  0}" by auto
  hence fin_g: "finite {m. g m  0}" using assms finite_subset by blast
  hence "(mS. monom m (f m)) = Sum_any (λm. monom m (g m))" using assms(2)
    by (smt (verit, best) Sum_any.conditionalize Sum_any.cong g_def monom.abs_eq monom_zero 
        single_zero)
  hence "coeff (mS. monom m (f m)) m' = coeff (Sum_any (λm. monom m (g m))) m'" by simp
  also have "... = g m'" using fin_g coeff_sum_monom by fastforce
  finally show ?thesis unfolding g_def by blast
qed


lemma cst_poly_times_monom: "MPoly (Const0 (a::('a::semiring_1))) * monom m b = monom m (a*b)"
proof -
  have "MPoly (Const0 a) * monom m b = monom 0 a * monom m b"
    using Const0_def by (metis monom.abs_eq)
  also have "... = monom m (a*b)"
    using mult_monom[of 0 a m b] by simp
  finally show ?thesis by simp
qed

lemma cst_poly_times_monom_one: "MPoly (Const0 (a::('a::semiring_1))) * monom m 1 = monom m a"
  using cst_poly_times_monom[of a m 1] by simp

lemma poly_eq_sum_monom: "P = Sum_any (λm. monom m (coeff P m))"
proof -
  have "m'. coeff P m' = coeff (Sum_any (λm. monom m (coeff P m))) m'"
    using coeff_sum_monom coeff_def
    by (metis (full_types) finite_lookup)
  thus ?thesis using coeff_eq by blast
qed

lemma poly_eq_sum_monom_alt: "P = (m(keys (mapping_of P)). monom m (coeff P m))"
  using poly_eq_sum_monom[of P]
  by (smt (verit) Sum_any.conditionalize Sum_any.cong coeff_keys finite_keys monom.abs_eq monom_zero single_zero)

lemma coeff_sum:
  fixes P::"_  'a::comm_monoid_add mpoly"
  assumes S_fin: "finite S"
  shows "coeff (sum P S) m = (iS. coeff (P i) m)"
proof (induct rule: finite_induct[OF S_fin])
  case 1 thus ?case by (simp add: coeff_def zero_mpoly.rep_eq)
next
  case (2 x S)
  have "coeff (sum P (insert x S)) m = coeff (sum P S + P x) m"
    by (simp add: "2.hyps"(1) "2.hyps"(2) add.commute)
  also have "... = coeff (sum P S) m + coeff (P x) m"
    by (simp add: coeff_add)
  also have "... = (iS. coeff (P i) m) + coeff (P x) m"
    by (simp add: "2")
  finally show ?case
    by (simp add: "2.hyps"(1) "2.hyps"(2) add.commute)
qed

lemma coeff_var_power_le:
  "j  i  MPoly_Type.coeff (Var v ^ j * P) (Poly_Mapping.single v i)
             = MPoly_Type.coeff P (Poly_Mapping.single v (i - j))"
proof -
  assume "j  i"
  then have eqi: "i = j + (i - j)"
    by auto

  show ?thesis
    apply (subst eqi)
    unfolding Var.abs_eq monom_eq_power_var[symmetric] Poly_Mapping.single_add
      Poly_Mapping.single.abs_eq[symmetric]
    apply (subst More_MPoly_Type.coeff_monom_mult)
    by simp
qed

lemma coeff_var_power_eq: "MPoly_Type.coeff (Var v ^ i) (Poly_Mapping.single v i) = 1"
  using coeff_var_power_le[of i i v 1]
  unfolding insertion_trivial[symmetric] MPoly_Type.coeff_def one_mpoly.rep_eq
  by auto

lemma coeff_const: "i > 0  MPoly_Type.coeff (Const c) (Poly_Mapping.single v i) = 0"
  unfolding MPoly_Type.coeff_def Const.rep_eq Const0_def lookup_single
  unfolding single_zero[of v, symmetric]
  apply (rule "when"(2))
  by (metis lookup_single_eq nat_less_le)

lemma mpoly_univariate_expansion:
  fixes P::"'a::comm_semiring_1 mpoly" and v::nat
  assumes univariate: "vars P  {v}"
  shows "P = Sum_any (λi. monom (Poly_Mapping.single v i) (coeff P (Poly_Mapping.single v i)))"
proof -
  define K where "K = keys (mapping_of P)"
  define f where "f = (λm::nat0nat. lookup m v)"

  have rewrite_monom: "mK  m = Poly_Mapping.single v (f m)" for m
  proof -
    assume "mK" 
    hence "keys m  vars P" 
      unfolding vars_def K_def by auto
    hence "keys m  {v}" 
      using univariate by auto
    thus ?thesis 
      unfolding f_def
      by (metis diff_shunt_var keys_eq_empty lookup_single_eq remove_key_keys 
          remove_key_single remove_key_sum)
  qed

  have f_inj: "inj_on f K" 
    using rewrite_monom by (metis inj_onI) 

  have coeff_null: "i  f ` K  
    monom (Poly_Mapping.single v i) (coeff P (Poly_Mapping.single v i)) = 0" for i
  proof -
    assume "i  f ` K"
    hence "Poly_Mapping.single v i  K" 
      unfolding f_def by force
    hence "coeff P (Poly_Mapping.single v i) = 0" 
      unfolding K_def coeff_def by (simp add: in_keys_iff)
    thus ?thesis by (metis mult_zero_left smult_0 smult_monom)
  qed

  have "P = (mK. monom m (coeff P m))"
    by (simp add: K_def poly_eq_sum_monom_alt)
  also have "... = (mK. monom (Poly_Mapping.single v (f m)) (coeff P (Poly_Mapping.single v (f m))))"
    using rewrite_monom by simp
  also have "... = (if ` K. monom (Poly_Mapping.single v i) (coeff P (Poly_Mapping.single v i)))"
    by (metis (mono_tags, lifting) f_inj sum.reindex_cong)
  also have "... = Sum_any (λi. monom (Poly_Mapping.single v i) (coeff P (Poly_Mapping.single v i)))"
    using coeff_null
    by (smt (verit) K_def Sum_any.conditionalize Sum_any.cong finite_imageI finite_keys)
  finally show ?thesis .
qed


lemma term_expansion_lemma_1: "i  [] 
  Poly_Mapping.single (Abs_poly_mapping ((!0) i)) (1 :: 'a::comm_semiring_1) =
  (s = 0..(length i - 1). Var0 s ^ (i ! s))"
proof (induction i rule: length_induct)
  case (1 i)
  show ?case proof (cases i rule: rev_exhaust)
    case Nil
    thus ?thesis using 1 by auto
  next
    case (snoc i' x)
    show ?thesis proof cases
      assume i'_def: "i' = []"
      have "Poly_Mapping.single (Abs_poly_mapping ((!0) [x])) 1 = Var0 0 ^ x"
        unfolding MPoly_Type.Var0_power by auto
      thus ?thesis unfolding snoc i'_def by auto
    next
      assume i'_non_empty: "i'  []"
      have "(s = 0..length i'. Var0 s ^ ((i' @ [x]) ! s)) =
        Poly_Mapping.single (Abs_poly_mapping ((!0) (i' @ [x]))) (1 :: 'a)" proof -
        have "(s = 0..length i'. Var0 s ^ ((i' @ [x]) ! s)) =
          (s = 0..Suc (length i' - 1). Var0 s ^ ((i' @ [x]) ! s))"
          using i'_non_empty by auto
        also have "... = (s = 0..length i' - 1. Var0 s ^ ((i' @ [x]) ! s)) *
          Var0 (Suc (length i' - 1)) ^ ((i' @ [x]) ! (Suc (length i' - 1)))"
          using prod.atLeast0_atMost_Suc by auto
        also have "... = (s = 0..length i' - 1. Var0 s ^ ((i' @ [x]) ! s)) *
          Var0 (length i') ^ ((i' @ [x]) ! (length i'))"
          using i'_non_empty by auto
        also have "... = (s = 0..length i' - 1. Var0 s ^ ((i' @ [x]) ! s)) *
          Var0 (length i') ^ x" by auto
        also have "... = (s = 0..length i' - 1. Var0 s ^ (i' ! s)) *
          Var0 (length i') ^ x"
          by (auto simp add: List.nth_append i'_non_empty order_le_less_trans)
        also have "... = Poly_Mapping.single (Abs_poly_mapping ((!0) i'))
          (1 :: 'a) * Var0 (length i') ^ x" using 1 snoc i'_non_empty by auto
        also have "... = Poly_Mapping.single (Abs_poly_mapping ((!0) i')) 1 *
          Poly_Mapping.single (Poly_Mapping.single (length i') x) 1"
          unfolding MPoly_Type.Var0_power by simp
        also have "... = Poly_Mapping.single (Abs_poly_mapping ((!0) i))
          (1 :: 'a)" proof -
          have "Abs_poly_mapping ((!0) i') + Poly_Mapping.single (length i') x =
            Abs_poly_mapping ((!0) i)" unfolding snoc by auto
          thus ?thesis by (simp add: Poly_Mapping.mult_single)
        qed
        finally show ?thesis unfolding snoc .
      qed
      thus ?thesis unfolding snoc by auto
    qed
  qed
qed

lemma term_expansion_lemma_2: "i  [] 
  monom (Abs_poly_mapping ((!0) i)) c =
  MPoly (Const0 c) * MPoly (s = 0..length i - 1. Var0 s ^ (i ! s))"
  unfolding term_expansion_lemma_1[symmetric] monom.abs_eq[symmetric]
    cst_poly_times_monom_one by simp

lemma term_expansion: "i  [] 
  monom (Abs_poly_mapping ((!0) i)) c =
  Const c * (s = 0..length i - 1. Var s ^ (i ! s))"
  unfolding MPoly_Type.Const.abs_eq MPoly_Type.Var.abs_eq term_expansion_lemma_2
  by (simp add: finite_prod_mpoly_commute power_mpoly_commute)

fun sample_prefix :: "nat  (nat  'a)  'a list" where
  "sample_prefix 0 f = []" |
  "sample_prefix (Suc l) f = sample_prefix l f @ [f l]"

lemma sample_prefix_length[simp]: "length (sample_prefix l f) = l"
  by (induction l; auto)

lemma sample_prefix_cong:
  "(x<n. f x = g x)  sample_prefix n f = sample_prefix n g"
proof -
  assume "x<n. f x = g x"
  hence "l  n  sample_prefix l f = sample_prefix l g" for l
    by (induction l; auto)
  thus ?thesis by auto
qed

lemma sample_prefix_inv_nth0: "(in. f i = 0)  f = (!0) (sample_prefix n f)"
proof (induction n arbitrary: f)
  case 0
  thus ?case unfolding nth0_def by simp
next
  case (Suc n)
  let ?g = "λx. f x when x < n"
  have "?g = (!0) (sample_prefix n ?g)" using Suc by auto
  also have "... = (!0) (sample_prefix n f)"
    using sample_prefix_cong[of n f ?g] by auto
  finally have 0: "?g = (!0) (sample_prefix n f)" .
  have "f = (λx. if x = n then f n else ?g x)" unfolding when_def using Suc by auto
  also have "... = (λx. if x = n then f n else (!0) (sample_prefix n f) x)"
    unfolding 0[symmetric] by simp
  also have "... = (!0) (sample_prefix (Suc n) f)"
    apply (rule ext)
    subgoal for x
      apply (cases "x = n")
      unfolding nth0_def apply simp_all
      unfolding List.nth_append apply simp_all
      done
    done
  finally show ?case .
qed

lemma sample_prefix_inj:
  "inj_on (λf. sample_prefix n f) {f. in. (f i :: 'a::zero) = 0}"
proof
  fix g h
  assume "g  {f. in. (f i :: 'a::zero) = 0}"
  hence 0: "in. g i = 0" by auto
  assume "h  {f. in. (f i :: 'a::zero) = 0}"
  hence 1: "in. h i = 0" by auto
  assume "sample_prefix n g = sample_prefix n h"
  hence "(!0) (sample_prefix n g) = (!0) (sample_prefix n h)" by auto
  thus "g = h" using 0 1 by (simp add: sample_prefix_inv_nth0[symmetric])
qed

lemma lookup_nth0_total_degree:
  "lookup (mapping_of P) (Abs_poly_mapping ((!0) i))  0 
  sum_list i  total_degree P"
  apply transfer
  apply (rule Max_ge)
  subgoal for P i
    apply simp
    done
  subgoal for P i
    unfolding image_def keys_def apply simp
    using nth0_sum_list[of i] apply auto
    done
  done

lemma prod_monom: "finite S  prod (λs. monom (x s) (a s)) S = monom (sum x S) (prod a S)"
proof (induction "card S" arbitrary:S)
  case 0
  then show ?case by auto
next
  case (Suc n)
  then obtain y where y_prop: "yS" by fastforce
  define S' where "S' = S - {y}"
  with Suc(3) have "finite S'" by auto
  from S'_def have card_S': "card S' = n" using Suc by (simp add: y_prop)
  have disj_un_S: "S = S'{y}  S'{y}={}finite S" using y_prop S'_def Suc by force
  hence "(sS. monom (x s) (a s)) = (sS'. monom (x s) (a s)) * (s{y}. monom (x s) (a s))"
    by (meson finite_Un prod.union_disjoint)
  also have "... = monom (sum x S') (prod a S') * monom (x y) (a y)"
    using Suc(1)[of "S'"] card_S' finite S' by auto
  also have "... = monom (sum x S' + x y) (prod a S' * a y)"
    using mult_monom by auto
  finally show "(sS. monom (x s) (a s)) = monom (sum x S) (prod a S)"
    using S'_def Suc
    by (smt (verit, ccfv_threshold) add.commute mult.commute prod.remove sum.remove y_prop)
qed

lemma poly_mapping_expansion: "x = (skeys x. Abs_poly_mapping (λv'. lookup x s when s = v'))"
proof - 
  have h0: "(xakeys x. lookup (Abs_poly_mapping (λv'. lookup x xa when xa = v')) i)
         = (xakeys x. (λv'. lookup x xa when xa = v') i)" for i
    by (rule sum.cong, auto)
  define x_fun where "x_fun  lookup x"
  have h1: "lookup x i = (xakeys x. lookup x xa when xa = i)" for i
    apply (cases "i  keys x", simp add: sum.remove[OF finite_keys])
    unfolding keys.rep_eq x_fun_def[symmetric]
    by (smt (verit, ccfv_SIG) mem_Collect_eq sum.neutral when_simps(2))

  thus ?thesis
    unfolding lookup_inject[symmetric] lookup_sum h0 by blast
qed

lemma monom_expansion:
  shows "monom x c = Const c * (skeys x. Var s ^ (lookup x s))"
  unfolding Var.abs_eq monom_eq_power_var[symmetric]
  apply (subst prod_monom[OF finite_keys, where ?a = "λ_. 1"])
  apply (simp add: Const.abs_eq cst_poly_times_monom)
  by (subst poly_mapping_expansion) simp

lemma monom_expansion':
  fixes P :: "'a::{ring_no_zero_divisors,comm_semiring_1} mpoly"
  assumes "x  keys (mapping_of P)"
  shows "monom x (coeff P x) = Const (coeff P x) * (s = 0..max_vars P. Var s ^ (lookup x s))"
proof (cases "vars P = {}")
  case True
  then show ?thesis 
    by (metis (no_types, lifting) Poly_Expansions.coeff_monom assms
        atLeastAtMost_singleton coeff_keys empty_iff finite.emptyI keys_zero
        lookup_zero max_vars_Const monom_expansion mult.commute mult_1 power_0
        prod.empty prod.insert vars_empty)
next
  case False
  then show ?thesis 
  unfolding max_vars_of_nonempty[OF False]
  apply (subst monom_expansion)
  apply (subst mult_cancel_left, rule disjI2)
  apply (rule prod.mono_neutral_cong_left)
  using assms apply simp_all
  subgoal 
    by (metis Max_ge UN_I atLeastAtMost_iff le0 subsetI vars_def
        vars_finite)
  subgoal
    by (simp add: in_keys_iff)
  done
qed

lemma mpoly_multivariate_expansion':
  fixes P::"'a::{ring_no_zero_divisors,comm_semiring_1} mpoly"
  shows "P = (mkeys (mapping_of P). 
                  Const (coeff P m) * (s = 0..max_vars P. (Var s) ^ (lookup m s)))"
  apply (subst poly_eq_sum_monom_alt)
  by (rule sum.cong, simp_all add: monom_expansion')

lemma mpoly_multivariate_expansion:
  fixes P::"'a::comm_semiring_1 mpoly"
  shows "P = (i | length i = max_vars P + 1  sum_list i  total_degree P. 
    Const (coeff P (Abs_poly_mapping ((!0) i))) *
    (s = 0..max_vars P. (Var s) ^ (i ! s)))"
proof -
  let ?t = "λm. (sample_prefix (max_vars P + 1) (lookup m))"
  have "P = (mkeys (mapping_of P). monom m (coeff P m))"
    by (rule poly_eq_sum_monom_alt)
  also have "... = (m | lookup (mapping_of P) m  0. monom m (coeff P m))"
    unfolding Poly_Mapping.in_keys_iff[symmetric] by auto
  also have "... = (m | lookup (mapping_of P) m  0.
    monom (Abs_poly_mapping (lookup m)) (coeff P (Abs_poly_mapping (lookup m))))"
    by auto
  also have "... = (i | length i = max_vars P + 1 
    lookup (mapping_of P) (Abs_poly_mapping ((!0) i))  0.
    monom (Abs_poly_mapping ((!0) i)) (coeff P (Abs_poly_mapping ((!0) i))))"
  proof (rule sum.reindex_cong[symmetric, of ?t])
    let ?S = "{m. lookup (mapping_of P) m  0}"
    let ?T = "{i. length i = max_vars P + 1 
      lookup (mapping_of P) (Abs_poly_mapping ((!0) i))  0}"
    show "inj_on ?t ?S" 
      proof
        fix m1 m2
        assume "m1  {m. lookup (mapping_of P) m  0}"
        hence 1: "v(max_vars P + 1). lookup m1 v = 0" using after_max_vars by auto
        assume "m2  {m. lookup (mapping_of P) m  0}"
        hence 2: "v(max_vars P + 1). lookup m2 v = 0" using after_max_vars by auto
        assume "sample_prefix (max_vars P + 1) (lookup m1) =
          sample_prefix (max_vars P + 1) (lookup m2)"
        hence "lookup m1 = lookup m2"
          using sample_prefix_inj[of "max_vars P + 1"] 1 2
          unfolding inj_on_def by blast
        thus "m1 = m2" by auto
      qed
    show "?T = ?t ` ?S" 
    proof (rule set_eqI; rule iffI)
      fix i
      assume 3: "i  ?T"
      let ?m = "Abs_poly_mapping ((!0) i)"
      from 3 have 4: "lookup (mapping_of P) ?m  0" by auto
      have "lookup ?m = (!0) (sample_prefix (max_vars P + 1) (lookup ?m))"
        apply (rule sample_prefix_inv_nth0)
        using after_max_vars[of P ?m] 4 by auto
      hence "(!0) i = (!0) (sample_prefix (max_vars P + 1) (lookup ?m))" by auto
      hence 5: "i = (sample_prefix (max_vars P + 1) (lookup ?m))"
        using nth0_inj[of i] 3 by auto
      from 4 5 have "m. lookup (mapping_of P) m  0 
        i = sample_prefix (max_vars P + 1) (lookup m)" by blast
      thus "i  ?t ` ?S" unfolding image_def by auto
    next
      fix i
      assume "i  ?t ` ?S"
      then obtain m where 6: "lookup (mapping_of P) m  0"
        "i = sample_prefix (max_vars P + 1) (lookup m)" by blast
      have 7: "length i = max_vars P + 1" using 6 by auto
      have "lookup m = (!0) (sample_prefix (max_vars P + 1) (lookup m))"
        apply (rule sample_prefix_inv_nth0)
        using after_max_vars[of P m] 6 apply auto
        done
      hence 8: "lookup (mapping_of P) (Abs_poly_mapping ((!0) i))  0"
        using 6 by auto
      from 7 8 show "i  ?T" by blast
    qed
    show "m  ?S 
      monom (Abs_poly_mapping ((!0) (?t m)))
        (coeff P (Abs_poly_mapping ((!0) (?t m)))) =
      monom (Abs_poly_mapping (lookup m))
        (coeff P (Abs_poly_mapping (lookup m)))" for m
    proof -
      assume 9: "m  ?S"
      have "lookup m = (!0) (sample_prefix (max_vars P + 1) (lookup m))"
        apply (rule sample_prefix_inv_nth0)
        using after_max_vars[of P m] 9 apply auto
        done
      thus ?thesis by auto
    qed
  qed
  also have "... = (i | length i = max_vars P + 1  sum_list i  total_degree P.
    monom (Abs_poly_mapping ((!0) i)) (coeff P (Abs_poly_mapping ((!0) i))))"
  proof (rule sum.mono_neutral_left)
    let ?A = "{i. length i = max_vars P + 1 
     lookup (mapping_of P) (Abs_poly_mapping ((!0) i))  0}"
    let ?B = "{i. length i = max_vars P + 1  sum_list i  total_degree P}"
    have "?B  {i. set i  {0..total_degree P}  length i = max_vars P + 1}"
      using member_le_sum_list by fastforce
    also have "finite ..."  using List.finite_lists_length_eq by auto
    ultimately show "finite ?B" using Finite_Set.finite_subset by auto
    show "?A  ?B" using lookup_nth0_total_degree by auto
    show "i?B - ?A. monom (Abs_poly_mapping ((!0) i))
      (coeff P (Abs_poly_mapping ((!0) i))) = 0"
      by (simp add: coeff_all_0 coeff_def)
  qed
  also have "... = (i | length i = max_vars P + 1  sum_list i  total_degree P.
    Const (coeff P (Abs_poly_mapping ((!0) i))) *
    (s = 0..max_vars P. (Var s) ^ (i ! s)))"
  proof (rule sum.cong[OF refl])
    fix i
    assume i_porp:
      "i  {i. length i = max_vars P + 1  sum_list i  total_degree P}"
    hence "i  []" "length i - 1 = max_vars P" by auto
    thus "monom (Abs_poly_mapping ((!0) i)) (coeff P (Abs_poly_mapping ((!0) i))) =
      Const (coeff P (Abs_poly_mapping ((!0) i))) *
      (s = 0..max_vars P. Var s ^ i ! s)"
      by (simp add: term_expansion)
  qed
  finally show ?thesis .
qed


lemma mpoly_univariate_expansion_sum:
  fixes P :: "('a::comm_ring_1) mpoly"
  assumes "vars P  {v}"
  defines "q  MPoly_Type.degree P v"
  defines "coeff_P  (λd. coeff P (Poly_Mapping.single v d))"
  shows "P = (d = 0..q. Const (coeff_P d) * (Var v) ^ d)"
  unfolding coeff_P_def
  apply (subst mpoly_univariate_expansion[of P v, OF assms(1)])
  unfolding monom_single
  unfolding Sum_any.expand_set
  apply (rule sum.mono_neutral_cong_left)
  subgoal by auto
  subgoal unfolding q_def degree.rep_eq
  proof -
    have "Const (coeff P (Poly_Mapping.single v p)) * Var v ^ p  0 
      p  {0..Max (insert 0 ((λm. lookup m v) ` keys (mapping_of P)))}" for p
    proof -
      assume "Const (coeff P (Poly_Mapping.single v p)) * Var v ^ p  0"
      hence "coeff P (Poly_Mapping.single v p)  0"
        by (metis Const_zero lambda_zero)
      hence "p  (λm. lookup m v) ` keys (mapping_of P)"
        by (metis coeff_def imageI in_keys_iff lookup_single_eq)
      thus "p  {0..Max (insert 0 ((λm. lookup m v) ` keys (mapping_of P)))}"
        by simp
    qed
    thus "{a. Const (coeff P (Poly_Mapping.single v a)) * Var v ^ a  0}
       {0..Max (insert 0 ((λm. lookup m v) ` keys (mapping_of P)))}"
      by blast
  qed
  subgoal by auto
  subgoal by auto
  done

end