Theory Chevalley_Warning

theory Chevalley_Warning
  imports
    "HOL-Number_Theory.Residues"
    "HOL-Computational_Algebra.Polynomial"
    "HOL-Library.FuncSet"
    "Polynomials.Polynomials"
begin

section ‹The Chevalley-Warning theorem›

text ‹
  We reuse the concrete multivariate-polynomial representation from the AFP
  entry theoryPolynomials.Polynomials.  Thus a polynomial has type
  @{typ "('v, 'a) poly"} and consists of AFP monomials paired with
  coefficients.  The theorem takes an explicit finite set of variables, so it
  applies directly to the usual natural-number-indexed AFP polynomials.
›

definition variables_poly ::
    "'v::linorder set  ('v, 'a) poly  bool" where
  "variables_poly V p  poly_vars p  V"

definition total_degree_poly_le :: "('v::linorder, 'a) poly  nat  bool" where
  "total_degree_poly_le p d  poly_degree p  d"

definition total_degree_poly_less :: "('v::linorder, 'a) poly  nat  bool" where
  "total_degree_poly_less p d  poly_degree p < d"

definition common_zeros ::
    "'v::linorder set 
      ('v, 'a::comm_semiring_1) poly list  ('v  'a) set" where
  "common_zeros V ps =
    {α  PiE V (λ_. UNIV). list_all (λp. eval_poly α p = 0) ps}"

lemma variables_poly_UNIV [simp]: "variables_poly UNIV p"
  by (simp add: variables_poly_def)

lemma monom_vars_subset_poly_vars:
  assumes "(m, c)  set p"
  shows "monom_vars m  poly_vars p"
  using assms
  by (auto simp: monom_vars_def poly_vars_def)

lemma poly_degree_leI:
  assumes "m c. (m, c)  set p  monom_degree m  d"
  shows "poly_degree p  d"
  using assms
  by (induction p) (auto simp: poly_degree_def split: prod.splits)

lemma total_degree_poly_leD:
  assumes "total_degree_poly_le p d" "(m, c)  set p"
  shows "monom_degree m  d"
proof -
  have "monom_degree m  set (map (λ(m, c). monom_degree m) p)"
    using assms(2) by force
  then have "monom_degree m  max_list (map (λ(m, c). monom_degree m) p)"
    by (rule max_list)
  with assms(1) show ?thesis
    by (simp add: total_degree_poly_le_def poly_degree_def)
qed

lemma total_degree_poly_lessD:
  assumes "total_degree_poly_less p d" "(m, c)  set p"
  shows "monom_degree m < d"
  using total_degree_poly_leD[of p "poly_degree p" m c] assms
  by (simp add: total_degree_poly_le_def total_degree_poly_less_def)

lemma total_degree_poly_le_mono:
  assumes "total_degree_poly_le p d" "d  e"
  shows "total_degree_poly_le p e"
  using assms by (simp add: total_degree_poly_le_def)

lemma total_degree_poly_less_of_le:
  assumes "total_degree_poly_le p d" "d < e"
  shows "total_degree_poly_less p e"
  using assms
  by (simp add: total_degree_poly_le_def total_degree_poly_less_def)

subsection ‹Polynomial operations›

fun poly_prod :: "('v::linorder, 'a::comm_semiring_1) poly list  ('v, 'a) poly" where
  "poly_prod [] = one_poly"
| "poly_prod (p # ps) = poly_mult p (poly_prod ps)"

definition indicator_factor ::
    "nat  ('v::linorder, 'a::comm_ring_1) poly  ('v, 'a) poly" where
  "indicator_factor q p = poly_minus (poly_const 1) (poly_power p (q - 1))"

definition indicator_poly ::
    "nat  ('v::linorder, 'a::comm_ring_1) poly list  ('v, 'a) poly" where
  "indicator_poly q ps = poly_prod (map (indicator_factor q) ps)"

lemma monom_list_degree_mult:
  "monom_list_degree (monom_mult_list m n) =
    monom_list_degree m + monom_list_degree n"
  by (induction m n rule: monom_mult_list.induct)
    (auto simp: monom_list_degree_def monom_mult_list.simps
      split: list.splits prod.splits if_splits)

lemma monom_degree_mult [simp]:
  "monom_degree (m * n) = monom_degree m + monom_degree n"
  by transfer (rule monom_list_degree_mult)

lemma monom_degree_one [simp]:
  "monom_degree (1::'v::linorder monom) = 0"
  by transfer (simp add: monom_list_degree_def)

lemma monom_vars_mult [simp]:
  "monom_vars (m * n) = monom_vars m  monom_vars n"
  unfolding monom_vars_def
  by transfer (metis monom_list_mult_list_vars set_map)

lemma monom_mult_poly_monoms:
  "poly_monoms (monom_mult_poly (m, c) p)  (*) m ` poly_monoms p"
proof (induction p)
  case Nil
  then show ?case
    by (simp add: monom_mult_poly.simps)
next
  case (Cons t p)
  obtain n d where t: "t = (n, d)"
    by force
  show ?case
    using Cons.IH
    by (cases "c * d = 0")
      (auto simp: monom_mult_poly.simps t)
qed

lemma variables_poly_const [simp]:
  "variables_poly V (poly_const c :: ('v::linorder, 'a::zero) poly)"
  by (auto simp: variables_poly_def poly_vars_def poly_const_def monom_vars_def)

lemma total_degree_poly_le_const [simp]:
  "total_degree_poly_le (poly_const c :: ('v::linorder, 'a::zero) poly) 0"
  by (auto simp: total_degree_poly_le_def poly_degree_def poly_const_def)

lemma variables_poly_add:
  assumes p: "variables_poly V p" and q: "variables_poly V q"
  shows "variables_poly V (poly_add p q)"
proof (unfold variables_poly_def, rule subsetI)
  fix x
  assume "x  poly_vars (poly_add p q)"
  then obtain m c where
    mc: "(m, c)  set (poly_add p q)" and x: "x  monom_vars m"
    by (auto simp: poly_vars_def monom_vars_def)
  have "m  poly_monoms p  poly_monoms q"
    using poly_add_monoms[of p q] mc by force
  then have "monom_vars m  V"
  proof
    assume "m  poly_monoms p"
    then obtain c' where "(m, c')  set p"
      by auto
    then show ?thesis
      using monom_vars_subset_poly_vars[of m c' p] p
      by (auto simp: variables_poly_def)
  next
    assume "m  poly_monoms q"
    then obtain c' where "(m, c')  set q"
      by auto
    then show ?thesis
      using monom_vars_subset_poly_vars[of m c' q] q
      by (auto simp: variables_poly_def)
  qed
  with x show "x  V"
    by blast
qed

lemma total_degree_poly_le_add:
  assumes p: "total_degree_poly_le p d" and q: "total_degree_poly_le q d"
  shows "total_degree_poly_le (poly_add p q) d"
proof (unfold total_degree_poly_le_def, rule poly_degree_leI)
  fix m c
  assume "(m, c)  set (poly_add p q)"
  then have "m  poly_monoms (poly_add p q)"
    by force
  then have "m  poly_monoms p  poly_monoms q"
    using poly_add_monoms[of p q] by blast
  then show "monom_degree m  d"
    using total_degree_poly_leD[OF p] total_degree_poly_leD[OF q]
    by auto
qed

lemma total_degree_monom_mult_poly:
  assumes m: "monom_degree m  d" and p: "total_degree_poly_le p e"
  shows "total_degree_poly_le (monom_mult_poly (m, c) p) (d + e)"
proof (unfold total_degree_poly_le_def, rule poly_degree_leI)
  fix r a
  assume "(r, a)  set (monom_mult_poly (m, c) p)"
  then have "r  poly_monoms (monom_mult_poly (m, c) p)"
    by force
  then obtain r' where r': "r = m * r'" and "r'  poly_monoms p"
    using monom_mult_poly_monoms[of m c p] by blast
  then obtain b where "(r', b)  set p"
    by auto
  then have "monom_degree r'  e"
    by (rule total_degree_poly_leD[OF p])
  with m show "monom_degree r  d + e"
    by (simp add: r')
qed

lemma variables_monom_mult_poly:
  assumes m: "monom_vars m  V" and p: "variables_poly V p"
  shows "variables_poly V (monom_mult_poly (m, c) p)"
proof (unfold variables_poly_def, rule subsetI)
  fix x
  assume "x  poly_vars (monom_mult_poly (m, c) p)"
  then obtain r a where
    ra: "(r, a)  set (monom_mult_poly (m, c) p)"
    and x: "x  monom_vars r"
    by (auto simp: poly_vars_def monom_vars_def)
  then obtain r' where r': "r = m * r'" and "r'  poly_monoms p"
    using monom_mult_poly_monoms[of m c p] by force
  then obtain b where r'p: "(r', b)  set p"
    by auto
  have "monom_vars r'  V"
    using monom_vars_subset_poly_vars[OF r'p] p
    by (auto simp: variables_poly_def)
  with m x show "x  V"
    by (auto simp: r')
qed

lemma variables_poly_mult:
  assumes p: "variables_poly V p" and q: "variables_poly V q"
  shows "variables_poly V (poly_mult p q)"
  using p
proof (induction p)
  case Nil
  then show ?case
    by (simp add: poly_mult.simps variables_poly_def poly_vars_def)
next
  case (Cons t p)
  obtain m c where t: "t = (m, c)"
    by force
  have m_vars: "monom_vars m  V"
    using monom_vars_subset_poly_vars[of m c "t # p"] Cons.prems t
    by (auto simp: variables_poly_def)
  have p_vars: "variables_poly V p"
    using Cons.prems
    by (auto simp: variables_poly_def poly_vars_def)
  have head: "variables_poly V (monom_mult_poly (m, c) q)"
    by (rule variables_monom_mult_poly[OF m_vars q])
  have tail: "variables_poly V (poly_mult p q)"
    by (rule Cons.IH[OF p_vars])
  show ?case
    by (simp add: poly_mult.simps t variables_poly_add[OF head tail])
qed

lemma total_degree_poly_le_mult:
  assumes p: "total_degree_poly_le p d" and q: "total_degree_poly_le q e"
  shows "total_degree_poly_le (poly_mult p q) (d + e)"
  using p
proof (induction p)
  case Nil
  then show ?case
    by (simp add: poly_mult.simps total_degree_poly_le_def poly_degree_def)
next
  case (Cons t p)
  obtain m c where t: "t = (m, c)"
    by force
  have mc: "(m, c)  set (t # p)"
    by (simp add: t)
  have m_degree: "monom_degree m  d"
    by (rule total_degree_poly_leD[OF Cons.prems mc])
  have p_degree: "total_degree_poly_le p d"
    using Cons.prems t
    by (auto simp: total_degree_poly_le_def poly_degree_def)
  have head:
    "total_degree_poly_le (monom_mult_poly (m, c) q) (d + e)"
    by (rule total_degree_monom_mult_poly[OF m_degree q])
  have tail: "total_degree_poly_le (poly_mult p q) (d + e)"
    by (rule Cons.IH[OF p_degree])
  have "total_degree_poly_le
      (poly_add (monom_mult_poly (m, c) q) (poly_mult p q)) (d + e)"
    by (rule total_degree_poly_le_add[OF head tail])
  then show ?case
    by (simp add: poly_mult.simps t)
qed

lemma variables_poly_minus:
  assumes p: "variables_poly V p" and q: "variables_poly V q"
  shows "variables_poly V (poly_minus p q)"
proof -
  have neg: "variables_poly V (monom_mult_poly (1, -1) q)"
    by (rule variables_monom_mult_poly[OF _ q])
      (simp add: monom_vars_def)
  show ?thesis
    unfolding poly_minus_def
    by (rule variables_poly_add[OF p neg])
qed

lemma total_degree_poly_le_minus:
  assumes p: "total_degree_poly_le p d" and q: "total_degree_poly_le q d"
  shows "total_degree_poly_le (poly_minus p q) d"
proof -
  have neg': "total_degree_poly_le (monom_mult_poly (1, -1) q) (0 + d)"
    by (rule total_degree_monom_mult_poly[OF _ q]) simp
  have neg: "total_degree_poly_le (monom_mult_poly (1, -1) q) d"
    using neg' by simp
  show ?thesis
    unfolding poly_minus_def
    by (rule total_degree_poly_le_add[OF p neg])
qed

lemma variables_poly_power:
  assumes p: "variables_poly V p"
  shows "variables_poly V (poly_power p k)"
proof (induction k)
  case 0
  then show ?case
    by (simp add: poly_power.simps one_poly_def variables_poly_def poly_vars_def)
next
  case (Suc k)
  show ?case
    unfolding poly_power.simps
    by (rule variables_poly_mult[OF p Suc.IH])
qed

lemma total_degree_poly_le_power:
  assumes p: "total_degree_poly_le p d"
  shows "total_degree_poly_le (poly_power p k) (k * d)"
proof (induction k)
  case 0
  then show ?case
    by (simp add: poly_power.simps one_poly_def
      total_degree_poly_le_def poly_degree_def)
next
  case (Suc k)
  have "total_degree_poly_le (poly_mult p (poly_power p k)) (d + k * d)"
    by (rule total_degree_poly_le_mult[OF p Suc.IH])
  then show ?case
    by (simp add: poly_power.simps algebra_simps)
qed

lemma eval_poly_prod:
  "eval_poly α (poly_prod ps) = prod_list (map (eval_poly α) ps)"
  by (induction ps) simp_all

lemma variables_poly_prod:
  assumes "list_all (variables_poly V) ps"
  shows "variables_poly V (poly_prod ps)"
  using assms
proof (induction ps)
  case Nil
  then show ?case
    by (simp add: one_poly_def variables_poly_def poly_vars_def)
next
  case (Cons p ps)
  have p: "variables_poly V p" and ps: "list_all (variables_poly V) ps"
    using Cons.prems by simp_all
  show ?case
    by (simp add: variables_poly_mult[OF p Cons.IH[OF ps]])
qed

lemma total_degree_poly_prod:
  assumes "list_all2 total_degree_poly_le ps ds"
  shows "total_degree_poly_le (poly_prod ps) (sum_list ds)"
  using assms
proof (induction ps arbitrary: ds)
  case Nil
  then show ?case
    by (simp add: one_poly_def total_degree_poly_le_def poly_degree_def)
next
  case (Cons p ps)
  then obtain d ds' where ds: "ds = d # ds'"
    by (cases ds) auto
  have p: "total_degree_poly_le p d"
    using Cons.prems ds by simp
  have rest: "list_all2 total_degree_poly_le ps ds'"
    using Cons.prems ds by simp
  have "total_degree_poly_le (poly_mult p (poly_prod ps)) (d + sum_list ds')"
    by (rule total_degree_poly_le_mult[OF p Cons.IH[OF rest]])
  then show ?case
    by (simp add: ds)
qed

lemma eval_indicator_factor:
  "eval_poly α (indicator_factor q p) = 1 - eval_poly α p ^ (q - 1)"
  by (simp add: indicator_factor_def)

lemma eval_indicator_poly:
  "eval_poly α (indicator_poly q ps) =
    prod_list (map (λp. 1 - eval_poly α p ^ (q - 1)) ps)"
  unfolding indicator_poly_def
  by (simp add: eval_poly_prod eval_indicator_factor comp_def)

lemma variables_indicator_factor:
  assumes "variables_poly V p"
  shows "variables_poly V (indicator_factor q p)"
  using assms
  by (simp add: indicator_factor_def variables_poly_minus variables_poly_power)

lemma variables_indicator_poly:
  assumes "list_all (variables_poly V) ps"
  shows "variables_poly V (indicator_poly q ps)"
proof -
  have "list_all (variables_poly V) (map (indicator_factor q) ps)"
    using assms
    by (auto simp: list_all_iff intro: variables_indicator_factor)
  then show ?thesis
    unfolding indicator_poly_def
    by (rule variables_poly_prod)
qed

lemma total_degree_indicator_factor:
  fixes p :: "('v::linorder, 'a::comm_ring_1) poly"
  assumes p: "total_degree_poly_le p d"
  shows "total_degree_poly_le (indicator_factor q p) ((q - 1) * d)"
proof -
  have pow: "total_degree_poly_le (poly_power p (q - 1)) ((q - 1) * d)"
    by (rule total_degree_poly_le_power[OF p])
  have const:
    "total_degree_poly_le (poly_const 1 :: ('v, 'a) poly)
      ((q - 1) * d)"
    by (rule total_degree_poly_le_mono[OF total_degree_poly_le_const]) simp
  show ?thesis
    unfolding indicator_factor_def
    by (rule total_degree_poly_le_minus[OF const pow])
qed

lemma total_degree_indicator_poly:
  assumes "list_all2 total_degree_poly_le ps ds"
  shows "total_degree_poly_le (indicator_poly q ps) ((q - 1) * sum_list ds)"
  using assms
proof (induction ps arbitrary: ds)
  case Nil
  then show ?case
    by (simp add: indicator_poly_def one_poly_def
      total_degree_poly_le_def poly_degree_def)
next
  case (Cons p ps)
  then obtain d ds' where ds: "ds = d # ds'"
    by (cases ds) auto
  have p: "total_degree_poly_le p d"
    using Cons.prems ds by simp
  have rest: "list_all2 total_degree_poly_le ps ds'"
    using Cons.prems ds by simp
  have factor:
    "total_degree_poly_le (indicator_factor q p) ((q - 1) * d)"
    by (rule total_degree_indicator_factor[OF p])
  have tail:
    "total_degree_poly_le (indicator_poly q ps) ((q - 1) * sum_list ds')"
    by (rule Cons.IH[OF rest])
  have "total_degree_poly_le
      (poly_mult (indicator_factor q p) (indicator_poly q ps))
      ((q - 1) * d + (q - 1) * sum_list ds')"
    by (rule total_degree_poly_le_mult[OF factor tail])
  then show ?case
    by (simp add: indicator_poly_def ds algebra_simps)
qed

subsection ‹Finite-field power sums›

lemma finite_field_card_gt_one:
  "1 < card (UNIV :: 'a::finite_field set)"
proof -
  have "card ({0, 1} :: 'a set)  card (UNIV :: 'a set)"
    by (rule card_mono) auto
  then show ?thesis
    by simp
qed

lemma finite_field_power_card_minus_one:
  fixes x :: "'a::finite_field"
  assumes "x  0"
  shows "x ^ (card (UNIV :: 'a set) - 1) = 1"
proof -
  let ?q = "card (UNIV :: 'a set)"
  have q_gt: "1 < ?q"
    by (rule finite_field_card_gt_one)
  have "x ^ ?q = x"
    by (rule finite_field_power_card_eq_same)
  moreover have "x ^ ?q = x ^ (?q - 1) * x"
  proof -
    have q_suc: "?q = Suc (?q - 1)"
      using q_gt by simp
    have "x ^ ?q = x ^ Suc (?q - 1)"
      using q_suc by simp
    also have " = x ^ (?q - 1) * x"
      by (rule power_Suc2)
    finally show ?thesis .
  qed
  ultimately have "x ^ (?q - 1) * x = 1 * x"
    by simp
  then show ?thesis
    using assms by simp
qed

lemma finite_field_exists_power_ne_one:
  fixes k :: nat
  assumes k_pos: "0 < k"
  assumes k_lt: "k < card (UNIV :: 'a::finite_field set) - 1"
  shows "a::'a. a  0  a ^ k  1"
proof -
  let ?P = "monom (1::'a) k - 1"
  have P_nonzero: "?P  0"
  proof
    assume "?P = 0"
    then have "poly.coeff ?P k = 0"
      by simp
    moreover have "poly.coeff ?P k = 1"
      using k_pos by simp
    ultimately show False
      by simp
  qed
  have deg_P: "degree ?P  k"
  proof -
    have deg_monom: "degree (monom (1::'a) k)  k"
      by (rule degree_monom_le)
    have deg_one: "degree (1::'a Polynomial.poly)  k"
      using k_pos by simp
    have "degree ?P 
        max (degree (monom (1::'a) k)) (degree (1::'a Polynomial.poly))"
      by (rule degree_diff_le_max)
    also have "  k"
      using deg_monom deg_one by simp
    finally show ?thesis .
  qed
  have roots_bound: "card {x::'a. poly ?P x = 0}  k"
  proof -
    have "card {x::'a. poly ?P x = 0}  degree ?P"
      by (rule card_poly_roots_bound[OF P_nonzero])
    also have "  k"
      by (rule deg_P)
    finally show ?thesis .
  qed
  let ?NZ = "{x::'a. x  0}"
  have card_NZ: "card ?NZ = card (UNIV :: 'a set) - 1"
  proof -
    have "?NZ = (UNIV :: 'a set) - {0}"
      by auto
    also have "card  = card (UNIV :: 'a set) - card ({0} :: 'a set)"
      by (rule card_Diff_subset) auto
    finally show ?thesis
      by simp
  qed
  show ?thesis
  proof (rule ccontr)
    assume "¬ ?thesis"
    then have all_roots: "x::'a. x  0  x ^ k = 1"
      by blast
    have NZ_roots: "?NZ  {x::'a. poly ?P x = 0}"
      using all_roots by (auto simp: poly_monom)
    have "card ?NZ  card {x::'a. poly ?P x = 0}"
      by (rule card_mono[OF poly_roots_finite[OF P_nonzero] NZ_roots])
    also have "  k"
      by (rule roots_bound)
    finally show False
      using k_lt card_NZ by simp
  qed
qed

lemma finite_field_power_sum_less:
  assumes k_lt: "k < card (UNIV :: 'a::finite_field set) - 1"
  shows "(xUNIV. x ^ k :: 'a) = 0"
proof (cases "k = 0")
  case True
  have "(of_nat (card (UNIV :: 'a set)) :: 'a) = 0"
    by (simp add: of_nat_eq_0_iff_char_dvd CHAR_dvd_CARD)
  with True show ?thesis
    by simp
next
  case False
  then obtain a :: 'a where a0: "a  0" and ak: "a ^ k  1"
    using finite_field_exists_power_ne_one[of k] k_lt by auto
  let ?S = "(xUNIV. x ^ k :: 'a)"
  have "?S = (xUNIV. (a * x) ^ k)"
    by (rule sum.reindex_bij_witness[where i = "λx. a * x" and j = "λx. x / a"])
      (use a0 in auto)
  also have " = a ^ k * ?S"
    by (simp add: power_mult_distrib sum_distrib_left)
  finally have "(a ^ k - 1) * ?S = 0"
    by (simp add: algebra_simps)
  with ak show ?thesis
    by simp
qed

lemma eval_monom_list_prod_sum_var:
  fixes α :: "'v::linorder  'a::comm_semiring_1"
  assumes finite: "finite V" and vars: "fst ` set m  V"
  shows "eval_monom_list α m =
    (xV. α x ^ sum_var_list m x)"
  using assms
proof (induction m)
  case Nil
  then show ?case
    by (simp add: sum_var_list_def)
next
  case (Cons ve m)
  obtain v e where ve: "ve = (v, e)"
    by force
  have vV: "v  V" and mV: "fst ` set m  V"
    using Cons.prems by (auto simp: ve)
  have delta_prod:
    "(xV. α x ^ (if x = v then e else 0)) = α v ^ e"
  proof -
    have "(xV. α x ^ (if x = v then e else 0)) =
        (xV. if x = v then α x ^ e else 1)"
      by (intro prod.cong) auto
    also have " = α v ^ e"
      using Cons.prems(1) vV
      by (simp add: prod.delta)
    finally show ?thesis .
  qed
  have "(xV. α x ^ sum_var_list (ve # m) x) =
      (xV.
        α x ^ (if x = v then e else 0) * α x ^ sum_var_list m x)"
    by (intro prod.cong)
      (simp_all add: ve sum_var_list_def power_add)
  also have " =
      (xV. α x ^ (if x = v then e else 0)) *
      (xV. α x ^ sum_var_list m x)"
    by (rule prod.distrib)
  also have " = α v ^ e * eval_monom_list α m"
    by (simp add: delta_prod Cons.IH[OF Cons.prems(1) mV])
  also have " = eval_monom_list α (ve # m)"
    by (simp add: ve algebra_simps)
  finally show ?case
    by simp
qed

lemma eval_monom_prod_sum_var:
  fixes α :: "'v::linorder  'a::comm_semiring_1"
  assumes "finite V" "monom_vars m  V"
  shows "eval_monom α m = (xV. α x ^ sum_var m x)"
  using assms
  unfolding monom_vars_def
  by transfer (auto intro: eval_monom_list_prod_sum_var)

lemma monom_list_degree_sum_var:
  fixes m :: "'v::linorder monom_list"
  assumes "finite V" "fst ` set m  V"
  shows "monom_list_degree m = (xV. sum_var_list m x)"
  using assms
proof (induction m)
  case Nil
  then show ?case
    by (simp add: monom_list_degree_def sum_var_list_def)
next
  case (Cons ve m)
  obtain v e where ve: "ve = (v, e)"
    by force
  have vV: "v  V" and mV: "fst ` set m  V"
    using Cons.prems by (auto simp: ve)
  have IH:
    "monom_list_degree m = (xV. sum_var_list m x)"
    by (rule Cons.IH[OF Cons.prems(1) mV])
  have delta: "(xV. if x = v then e else 0) = e"
    using Cons.prems(1) vV
    by (simp add: sum.delta)
  have "monom_list_degree (ve # m) = e + monom_list_degree m"
    by (simp add: ve monom_list_degree_def)
  also have " = e + (xV. sum_var_list m x)"
    using IH by simp
  also have " =
      (xV. if x = v then e else 0) +
      (xV. sum_var_list m x)"
    using delta by simp
  also have " =
      (xV. (if x = v then e else 0) + sum_var_list m x)"
    by (simp add: sum.distrib)
  also have " = (xV. sum_var_list (ve # m) x)"
    by (intro sum.cong) (simp_all add: ve sum_var_list_def)
  finally show ?case .
qed

lemma monom_degree_sum_var:
  fixes m :: "'v::linorder monom"
  assumes "finite V" "monom_vars m  V"
  shows "monom_degree m = (xV. sum_var m x)"
  using assms
  unfolding monom_vars_def
  by transfer (auto intro: monom_list_degree_sum_var)

lemma sum_monom_assignments:
  fixes m :: "'v::linorder monom"
  assumes finite: "finite V" and vars: "monom_vars m  V"
  shows "(αPiE V (λ_. UNIV :: 'a::finite_field set).
      eval_monom α m) =
    (xV. y(UNIV :: 'a set). y ^ sum_var m x)"
proof -
  have "(xV. y(UNIV :: 'a set). y ^ sum_var m x) =
      (αPiE V (λ_. UNIV).
        xV. α x ^ sum_var m x)"
    by (rule prod_sum_PiE[OF finite]) simp
  then show ?thesis
    by (simp add: eval_monom_prod_sum_var[OF finite vars])
qed

lemma exists_variable_degree_lt:
  assumes finite: "finite V"
  assumes "(xV. f x) < card V * k"
  shows "xV. f x < k"
proof (rule ccontr)
  assume "¬ ?thesis"
  then have ge: "x. x  V  k  f x"
    by force
  have "card V * k = (xV. k)"
    using finite by simp
  also have "  (xV. f x)"
    by (rule sum_mono) (use ge in auto)
  finally show False
    using assms by simp
qed

lemma monomial_sum_zero_if_degree_lt:
  fixes m :: "'v::linorder monom"
  assumes finite: "finite V" and vars: "monom_vars m  V"
  assumes deg:
    "monom_degree m <
      card V * (card (UNIV :: 'a::finite_field set) - 1)"
  shows "(αPiE V (λ_. UNIV :: 'a set). eval_monom α m) = 0"
proof -
  have "xV. sum_var m x < card (UNIV :: 'a set) - 1"
  proof (rule exists_variable_degree_lt[OF finite])
    show "(xV. sum_var m x) <
        card V * (card (UNIV :: 'a set) - 1)"
      using deg monom_degree_sum_var[OF finite vars] by simp
  qed
  then obtain x where xV: "x  V"
    and x_lt: "sum_var m x < card (UNIV :: 'a set) - 1"
    by blast
  have factor_zero:
    "(yUNIV. (y::'a) ^ sum_var m x) = 0"
    by (rule finite_field_power_sum_less[OF x_lt])
  have "(xV. y(UNIV :: 'a set). y ^ sum_var m x) = 0"
  proof (rule prod_zero[OF finite])
    show "xV. (y(UNIV :: 'a set). y ^ sum_var m x) = 0"
      using xV factor_zero by blast
  qed
  then show ?thesis
    using sum_monom_assignments[OF finite vars, where 'a='a] by simp
qed

lemma finite_field_sum_poly_zero:
  assumes finite: "finite V" and vars: "variables_poly V p"
  assumes deg:
    "total_degree_poly_less p
      (card V * (card (UNIV :: 'a::finite_field set) - 1))"
  shows "(αPiE V (λ_. UNIV :: 'a set). eval_poly α p) = 0"
  using vars deg
proof (induction p)
  case Nil
  then show ?case
    by simp
next
  case (Cons t p)
  obtain m c where t: "t = (m, c)"
    by force
  have m_vars: "monom_vars m  V"
    using monom_vars_subset_poly_vars[of m c "t # p"] Cons.prems(1) t
    by (auto simp: variables_poly_def)
  have mc: "(m, c)  set (t # p)"
    by (simp add: t)
  have m_deg:
    "monom_degree m <
      card V * (card (UNIV :: 'a set) - 1)"
    by (rule total_degree_poly_lessD[OF Cons.prems(2) mc])
  have p_vars: "variables_poly V p"
    using Cons.prems(1)
    by (auto simp: variables_poly_def poly_vars_def)
  have p_deg:
    "total_degree_poly_less p
      (card V * (card (UNIV :: 'a set) - 1))"
    using Cons.prems(2) t
    by (auto simp: total_degree_poly_less_def poly_degree_def)
  have head:
    "(αPiE V (λ_. UNIV :: 'a set). eval_monom α m * c) = 0"
  proof -
    have "(αPiE V (λ_. UNIV :: 'a set).
        eval_monom α m * c) =
        (αPiE V (λ_. UNIV :: 'a set). eval_monom α m) * c"
      by (simp add: sum_distrib_right)
    also have " = 0"
      by (simp add: monomial_sum_zero_if_degree_lt[OF finite m_vars m_deg])
    finally show ?thesis .
  qed
  have "(αPiE V (λ_. UNIV :: 'a set).
      eval_poly α (t # p)) =
      (αPiE V (λ_. UNIV :: 'a set).
        eval_monom α m * c + eval_poly α p)"
    by (intro sum.cong) (simp_all add: t)
  also have " =
      (αPiE V (λ_. UNIV :: 'a set).
        eval_monom α m * c) +
      (αPiE V (λ_. UNIV :: 'a set). eval_poly α p)"
    by (simp add: sum.distrib)
  also have " = 0"
    using head Cons.IH[OF p_vars p_deg] by simp
  finally show ?case .
qed

subsection ‹The theorem›

lemma finite_field_indicator_factor_value:
  fixes y :: "'a::finite_field"
  shows "1 - y ^ (card (UNIV :: 'a set) - 1) = of_bool (y = 0)"
proof (cases "y = 0")
  case True
  have "0 < card (UNIV :: 'a set) - 1"
    using finite_field_card_gt_one[where 'a='a] by simp
  with True show ?thesis
    by (simp add: of_bool_def)
next
  case False
  then have "y ^ (card (UNIV :: 'a set) - 1) = 1"
    by (rule finite_field_power_card_minus_one)
  with False show ?thesis
    by (simp add: of_bool_def)
qed

lemma prod_list_of_bool_list_all:
  "prod_list (map (λx. of_bool (P x) :: 'a::comm_semiring_1) xs) =
    of_bool (list_all P xs)"
  by (induction xs) (simp_all add: of_bool_def)

lemma eval_indicator_poly_value:
  fixes ps :: "('v::linorder, 'a::finite_field) poly list"
  shows "eval_poly α (indicator_poly (card (UNIV :: 'a set)) ps) =
    of_bool (list_all (λp. eval_poly α p = 0) ps)"
proof -
  have "eval_poly α (indicator_poly (card (UNIV :: 'a set)) ps) =
      prod_list
        (map (λp. 1 - eval_poly α p ^ (card (UNIV :: 'a set) - 1)) ps)"
    by (rule eval_indicator_poly)
  also have " =
      prod_list (map (λp. of_bool (eval_poly α p = 0) :: 'a) ps)"
  proof (rule arg_cong[where f=prod_list])
    show "map (λp. 1 - eval_poly α p ^ (card (UNIV :: 'a set) - 1)) ps =
      map (λp. of_bool (eval_poly α p = 0) :: 'a) ps"
      by (intro map_cong refl finite_field_indicator_factor_value)
  qed
  also have " = of_bool (list_all (λp. eval_poly α p = 0) ps)"
    by (rule prod_list_of_bool_list_all)
  finally show ?thesis .
qed

lemma list_all2_variables:
  assumes
    "list_all2 (λp d. variables_poly V p  total_degree_poly_le p d) ps ds"
  shows "list_all (variables_poly V) ps"
  using assms
proof (induction ps arbitrary: ds)
  case Nil
  then show ?case
    by simp
next
  case (Cons p ps)
  then obtain d ds' where ds: "ds = d # ds'"
    by (cases ds) auto
  then show ?case
    using Cons.IH[of ds'] Cons.prems by auto
qed

lemma list_all2_degrees:
  assumes
    "list_all2 (λp d. variables_poly V p  total_degree_poly_le p d) ps ds"
  shows "list_all2 total_degree_poly_le ps ds"
  using assms
proof (induction ps arbitrary: ds)
  case Nil
  then show ?case
    by (cases ds) auto
next
  case (Cons p ps)
  then obtain d ds' where
    ds: "ds = d # ds'"
    and p: "total_degree_poly_le p d"
    and rest:
      "list_all2
        (λp d. variables_poly V p  total_degree_poly_le p d) ps ds'"
    by (cases ds) auto
  have "list_all2 total_degree_poly_le ps ds'"
    by (rule Cons.IH[OF rest])
  with p show ?case
    by (simp add: ds)
qed

theorem chevalley_warning:
  fixes V :: "'v::linorder set"
    and ps :: "('v, 'a::finite_field) poly list"
  assumes finite: "finite V"
  assumes polys:
    "list_all2 (λp d. variables_poly V p  total_degree_poly_le p d) ps ds"
  assumes degree_sum: "sum_list ds < card V"
  shows "CHAR('a) dvd card (common_zeros V ps)"
proof -
  let ?q = "card (UNIV :: 'a set)"
  let ?I = "indicator_poly ?q ps"
  have q_minus_pos: "0 < ?q - 1"
    using finite_field_card_gt_one[where 'a='a] by simp
  have variables: "list_all (variables_poly V) ps"
    by (rule list_all2_variables[OF polys])
  have I_variables: "variables_poly V ?I"
    by (rule variables_indicator_poly[OF variables])
  have degrees: "list_all2 total_degree_poly_le ps ds"
    by (rule list_all2_degrees[OF polys])
  have I_degree_le:
    "total_degree_poly_le ?I ((?q - 1) * sum_list ds)"
    by (rule total_degree_indicator_poly[OF degrees])
  have degree_bound:
    "(?q - 1) * sum_list ds < card V * (?q - 1)"
    using degree_sum q_minus_pos
    by (simp add: mult_less_cancel1)
  have I_degree_less:
    "total_degree_poly_less ?I (card V * (?q - 1))"
    by (rule total_degree_poly_less_of_le[OF I_degree_le degree_bound])
  have sum_zero:
    "(αPiE V (λ_. UNIV :: 'a set). eval_poly α ?I) = 0"
    by (rule finite_field_sum_poly_zero[OF finite I_variables I_degree_less])
  have finite_assignments: "finite (PiE V (λ_. UNIV :: 'a set))"
    by (rule finite_PiE[OF finite]) simp
  have sum_card:
    "(αPiE V (λ_. UNIV :: 'a set). eval_poly α ?I) =
      of_nat (card (common_zeros V ps))"
  proof -
    have "(αPiE V (λ_. UNIV :: 'a set). eval_poly α ?I) =
        (αPiE V (λ_. UNIV :: 'a set).
          of_bool (list_all (λp. eval_poly α p = 0) ps) :: 'a)"
      by (intro sum.cong)
        (simp_all add: eval_indicator_poly_value)
    also have " =
        of_nat (card {α  PiE V (λ_. UNIV :: 'a set).
          list_all (λp. eval_poly α p = 0) ps})"
    proof -
      have "(PiE V (λ_. UNIV :: 'a set) 
          {α. list_all (λp. eval_poly α p = 0) ps}) =
          {α  PiE V (λ_. UNIV :: 'a set).
            list_all (λp. eval_poly α p = 0) ps}"
        by auto
      with finite_assignments show ?thesis
        by simp
    qed
    also have "{α  PiE V (λ_. UNIV :: 'a set).
          list_all (λp. eval_poly α p = 0) ps} = common_zeros V ps"
      by (auto simp: common_zeros_def)
    finally show ?thesis .
  qed
  have "(of_nat (card (common_zeros V ps)) :: 'a) = 0"
    using sum_zero sum_card by simp
  then show ?thesis
    by (simp add: of_nat_eq_0_iff_char_dvd)
qed

corollary chevalley_warning_finite_type:
  fixes ps :: "('v::{finite,linorder}, 'a::finite_field) poly list"
  assumes polys: "list_all2 total_degree_poly_le ps ds"
  assumes degree_sum: "sum_list ds < card (UNIV :: 'v set)"
  shows "CHAR('a) dvd card (common_zeros UNIV ps)"
proof (rule chevalley_warning[where ds=ds])
  show "finite (UNIV :: 'v set)"
    by simp
  show "list_all2
      (λp d. variables_poly UNIV p  total_degree_poly_le p d) ps ds"
    using polys
    by (rule list_all2_mono) simp
  show "sum_list ds < card (UNIV :: 'v set)"
    using degree_sum by simp
qed

end