Theory Cauchy_Davenport_Prime_Field

theory Cauchy_Davenport_Prime_Field
  imports
    Sumset_Basics
    "HOL-Computational_Algebra.Computational_Algebra"
    "HOL-Number_Theory.Number_Theory"
begin

section ‹Cauchy-Davenport over prime fields›

text ‹
  This theory proves the prime-field Cauchy-Davenport theorem by a
  polynomial-method argument. The core technical work packages the bivariate
  degree bookkeeping needed to show that a suitably chosen polynomial cannot
  vanish on too large a grid unless the expected lower bound holds.
›

subsection ‹Polynomial infrastructure›

definition total_degree_le :: "'a::comm_semiring_1 poly poly  nat  bool" where
  "total_degree_le P d 
    (i. if i  d then degree (poly.coeff P i)  d - i else poly.coeff P i = 0)"

definition sum_factor :: "'a::comm_ring_1  'a poly poly" where
  "sum_factor c = [: [:-c, 1:], [:1:] :]"

lemma coeff_poly_const:
  fixes P :: "'a::comm_semiring_1 poly poly"
  shows "poly.coeff (poly P [:a:]) j =
    (idegree P. poly.coeff (poly.coeff P i) j * a ^ i)"
proof -
  have "poly.coeff (poly P [:a:]) j =
      poly.coeff (idegree P. poly.coeff P i * [:a:] ^ i) j"
    by (simp add: poly_altdef)
  also have " =
      (idegree P. poly.coeff (poly.coeff P i * [:a:] ^ i) j)"
    by (simp add: coeff_sum)
  also have " =
      (idegree P. poly.coeff (poly.coeff P i) j * a ^ i)"
  proof (intro sum.cong refl)
    fix i
    assume "i  {..degree P}"
    have "[:a:] ^ i = [:a ^ i:]"
      by (induction i) (simp_all add: mult.commute)
    then show "poly.coeff (poly.coeff P i * [:a:] ^ i) j =
        poly.coeff (poly.coeff P i) j * a ^ i"
      by (simp add: coeff_mult mult.commute mult.left_commute mult.assoc)
  qed
  finally show ?thesis .
qed

lemma total_degree_le_eval_const:
  fixes P :: "'a::comm_semiring_1 poly poly"
  assumes td: "total_degree_le P d"
  shows "degree (poly P [:a:])  d"
proof -
  have zero_above: "poly.coeff (poly P [:a:]) j = 0" if j: "j > d" for j
  proof -
    have eval_coeff: "poly.coeff (poly P [:a:]) j =
        (idegree P. poly.coeff (poly.coeff P i) j * a ^ i)"
      by (rule coeff_poly_const)
    have sum_zero: "(idegree P. poly.coeff (poly.coeff P i) j * a ^ i) = 0"
    proof (intro sum.neutral ballI)
      fix i
      assume i: "i  {..degree P}"
      show "poly.coeff (poly.coeff P i) j * a ^ i = 0"
      proof (cases "i  d")
        case True
        with td have "degree (poly.coeff P i)  d - i"
          by (simp add: total_degree_le_def)
        moreover from True j have "d - i < j"
          by simp
        ultimately have "poly.coeff (poly.coeff P i) j = 0"
          by (meson coeff_eq_0 le_less_trans)
        then show ?thesis
          by simp
      next
        case False
        with td have "poly.coeff P i = 0"
          by (simp add: total_degree_le_def)
        then show ?thesis
          by simp
      qed
    qed
    show ?thesis
      using eval_coeff sum_zero by simp
  qed
  show ?thesis
    by (rule degree_le) (use zero_above in blast)
qed

lemma total_degree_le_eval_const_top:
  fixes P :: "'a::comm_semiring_1 poly poly"
  assumes td: "total_degree_le P d"
  shows "poly.coeff (poly P [:a:]) d = poly.coeff (poly.coeff P 0) d"
proof -
  have "poly.coeff (poly P [:a:]) d =
      (idegree P. poly.coeff (poly.coeff P i) d * a ^ i)"
    by (rule coeff_poly_const)
  also have " =
      poly.coeff (poly.coeff P 0) d * a ^ 0 +
      (i{..degree P} - {0}. poly.coeff (poly.coeff P i) d * a ^ i)"
    by (subst sum.remove[of "{..degree P}" 0 "λi. poly.coeff (poly.coeff P i) d * a ^ i"]) simp_all
  also have " = poly.coeff (poly.coeff P 0) d * a ^ 0"
  proof -
    have "(i{..degree P} - {0}. poly.coeff (poly.coeff P i) d * a ^ i) = 0"
    proof (intro sum.neutral ballI)
      fix i
      assume i: "i  {..degree P} - {0}"
      then have i_pos: "0 < i"
        by simp
      show "poly.coeff (poly.coeff P i) d * a ^ i = 0"
      proof (cases "i  d")
        case True
        with td have deg_i: "degree (poly.coeff P i)  d - i"
        by (simp add: total_degree_le_def)
      from True i_pos have "d - i < d"
        by simp
      with deg_i have "poly.coeff (poly.coeff P i) d = 0"
        by (meson coeff_eq_0 le_less_trans)
        then show ?thesis
          by simp
      next
        case False
        with td have "poly.coeff P i = 0"
          by (simp add: total_degree_le_def)
        then show ?thesis
          by simp
      qed
    qed
    then show ?thesis
      by simp
  qed
  finally show ?thesis
    by simp
qed

lemma coeff_sum_factor_mult_0:
  "poly.coeff (sum_factor c * P) 0 = [:-c, 1:] * poly.coeff P 0"
  by (simp add: sum_factor_def coeff_mult)

lemma coeff_sum_factor_mult_Suc:
  "poly.coeff (sum_factor c * P) (Suc i) =
    [:-c, 1:] * poly.coeff P (Suc i) + poly.coeff P i"
  by (simp add: sum_factor_def coeff_mult)

lemma total_degree_le_sum_factor_mult:
  fixes P :: "'a::field poly poly"
  assumes td: "total_degree_le P d"
  shows "total_degree_le (sum_factor c * P) (Suc d)"
unfolding total_degree_le_def
proof
  fix i
  show "(if i  Suc d then degree (poly.coeff (sum_factor c * P) i)  Suc d - i
         else poly.coeff (sum_factor c * P) i = 0)"
  proof (cases i)
    case 0
    have "degree (poly.coeff (sum_factor c * P) 0) =
        degree ([:-c, 1:] * poly.coeff P 0)"
      by (simp add: coeff_sum_factor_mult_0)
    also have "  degree [:-c, 1:] + degree (poly.coeff P 0)"
      by (rule degree_mult_le)
    also have "  Suc d"
    proof -
      have "degree (poly.coeff P 0)  d"
      proof -
        from td have "if 0  d then degree (poly.coeff P 0)  d - 0 else poly.coeff P 0 = 0"
          unfolding total_degree_le_def by blast
        then show ?thesis
          by simp
      qed
      then show ?thesis
        by simp
    qed
    finally show ?thesis
      using 0 by simp
  next
    case (Suc k)
    show ?thesis
    proof (cases "k  d")
      case True
      have deg_Suc: "degree (poly.coeff P (Suc k))  d - Suc k"
      proof (cases "Suc k  d")
        case True
        with td show ?thesis
          by (simp add: total_degree_le_def)
      next
        case False
        with td have "poly.coeff P (Suc k) = 0"
          by (simp add: total_degree_le_def)
        then show ?thesis
          by simp
      qed
      from True td have deg_k: "degree (poly.coeff P k)  d - k"
        by (simp add: total_degree_le_def)
      have "degree (poly.coeff (sum_factor c * P) (Suc k)) =
          degree ([:-c, 1:] * poly.coeff P (Suc k) + poly.coeff P k)"
        by (simp add: coeff_sum_factor_mult_Suc)
      also have " 
          max (degree ([:-c, 1:] * poly.coeff P (Suc k))) (degree (poly.coeff P k))"
        by (rule degree_add_le_max)
      also have "  d - k"
      proof -
        have deg_lin: "degree ([:-c, 1:] * poly.coeff P (Suc k))  d - k"
        proof -
          show ?thesis
          proof (cases "poly.coeff P (Suc k) = 0")
            case True
            then show ?thesis
              by simp
          next
            case False
            have suc_le: "Suc k  d"
            proof (rule ccontr)
              assume "¬ Suc k  d"
              with td have "poly.coeff P (Suc k) = 0"
                by (simp add: total_degree_le_def)
              with False show False
                by contradiction
            qed
            have "degree ([:-c, 1:] * poly.coeff P (Suc k)) =
                degree [:-c, 1:] + degree (poly.coeff P (Suc k))"
              by (rule degree_mult_eq) (use False in simp_all)
            also have "  1 + (d - Suc k)"
              using deg_Suc by simp
            also have " = d - k"
              using suc_le by simp
            finally show ?thesis .
          qed
        qed
        show ?thesis
          using deg_lin deg_k by simp
      qed
      finally show ?thesis
        using Suc True by simp
    next
      case False
      with td have zero_k: "poly.coeff P k = 0"
        by (simp add: total_degree_le_def)
      from False have "d < k"
        by simp
      then have "d < Suc k"
        by simp
      with td have zero_Suc: "poly.coeff P (Suc k) = 0"
        by (simp add: total_degree_le_def)
      show ?thesis
        using Suc zero_k zero_Suc by (simp add: coeff_sum_factor_mult_Suc)
    qed
  qed
qed

lemma total_degree_le_prod_sum_factor:
  fixes C :: "'a::field set"
  assumes "finite C"
  shows "total_degree_le (prod sum_factor C) (card C)"
  using assms
proof (induction rule: finite_induct)
  case empty
  show ?case
    by (simp add: total_degree_le_def)
next
  case (insert c C)
  then show ?case
    by (simp add: total_degree_le_sum_factor_mult)
qed

lemma coeff_coeff_sum_factor_mult_top:
  fixes P :: "'a::field poly poly"
  assumes td: "total_degree_le P d"
  assumes ij: "i + j = Suc d"
  shows "poly.coeff (poly.coeff (sum_factor c * P) i) j =
    (if i = 0 then 0 else poly.coeff (poly.coeff P (i - 1)) j) +
    (if j = 0 then 0 else poly.coeff (poly.coeff P i) (j - 1))"
proof (cases i)
  case 0
  with ij have j: "j = Suc d"
    by simp
  have "poly.coeff (poly.coeff (sum_factor c * P) 0) j =
      poly.coeff ([:-c, 1:] * poly.coeff P 0) j"
    by (simp add: coeff_sum_factor_mult_0)
  also have " =
      poly.coeff (poly.coeff P 0) (j - 1) - c * poly.coeff (poly.coeff P 0) j"
    using j by (cases j) (simp_all add: coeff_mult)
  also have "poly.coeff (poly.coeff P 0) j = 0"
  proof -
    have "degree (poly.coeff P 0)  d"
    proof -
      have "if 0  d then degree (poly.coeff P 0)  d - 0 else poly.coeff P 0 = 0"
        using td unfolding total_degree_le_def by blast
      then show ?thesis
        by simp
    qed
    with j show ?thesis
      by (meson coeff_eq_0 less_Suc_eq_le)
  qed
  finally show ?thesis
    using 0 j by simp
next
  case (Suc k)
  have rec: "poly.coeff (poly.coeff (sum_factor c * P) (Suc k)) j =
      (if j = 0 then 0 else poly.coeff (poly.coeff P (Suc k)) (j - 1)) -
      c * poly.coeff (poly.coeff P (Suc k)) j +
      poly.coeff (poly.coeff P k) j"
  proof -
    have "poly.coeff (poly.coeff (sum_factor c * P) (Suc k)) j =
        poly.coeff ([:-c, 1:] * poly.coeff P (Suc k) + poly.coeff P k) j"
      by (simp add: coeff_sum_factor_mult_Suc)
    also have " =
        poly.coeff ([:-c, 1:] * poly.coeff P (Suc k)) j +
        poly.coeff (poly.coeff P k) j"
      by simp
    also have "poly.coeff ([:-c, 1:] * poly.coeff P (Suc k)) j =
        (if j = 0 then 0 else poly.coeff (poly.coeff P (Suc k)) (j - 1)) -
        c * poly.coeff (poly.coeff P (Suc k)) j"
    proof (cases j)
      case 0
      then show ?thesis
        by (simp add: coeff_mult)
    next
      case (Suc l)
      then show ?thesis
        by (simp add: coeff_mult)
    qed
    finally show ?thesis
      by simp
  qed
  have zero_term: "poly.coeff (poly.coeff P (Suc k)) j = 0"
  proof (cases "Suc k  d")
    case True
    with td have "degree (poly.coeff P (Suc k))  d - Suc k"
      by (simp add: total_degree_le_def)
    moreover have "d - Suc k < j"
    proof -
      from ij Suc have "j = Suc d - Suc k"
        by simp
      with True show ?thesis
        by simp
    qed
    ultimately show ?thesis
      by (meson coeff_eq_0 le_less_trans)
  next
    case False
    with td show ?thesis
      by (simp add: total_degree_le_def)
  qed
  show ?thesis
    using Suc rec zero_term by (cases j) simp_all
qed

lemma coeff_coeff_prod_sum_factor_top:
  fixes C :: "'a::field set"
  assumes fin: "finite C"
  assumes ij: "i + j = card C"
  shows "poly.coeff (poly.coeff (prod sum_factor C) i) j = of_nat (card C choose i)"
  using fin ij
proof (induction C arbitrary: i j rule: finite_induct)
  case empty
  then have "i = 0" "j = 0"
    by simp_all
  then show ?case
    by simp
next
  case (insert c C)
  let ?P = "prod sum_factor C"
  have td: "total_degree_le ?P (card C)"
    using insert.hyps by (simp add: total_degree_le_prod_sum_factor)
  have ij': "i + j = Suc (card C)"
    using insert.prems insert.hyps by simp
  have top_rec:
    "poly.coeff (poly.coeff (prod sum_factor (insert c C)) i) j =
      (if i = 0 then 0 else poly.coeff (poly.coeff ?P (i - 1)) j) +
      (if j = 0 then 0 else poly.coeff (poly.coeff ?P i) (j - 1))"
  proof -
    have "poly.coeff (poly.coeff (prod sum_factor (insert c C)) i) j =
        poly.coeff (poly.coeff (sum_factor c * ?P) i) j"
      using insert.hyps by simp
    also have " =
        (if i = 0 then 0 else poly.coeff (poly.coeff ?P (i - 1)) j) +
        (if j = 0 then 0 else poly.coeff (poly.coeff ?P i) (j - 1))"
      using coeff_coeff_sum_factor_mult_top[OF td ij'] .
    finally show ?thesis .
  qed
  show ?case
  proof (cases "i = 0  j = 0")
    case True
    then show ?thesis
    proof
      assume "i = 0"
      with ij' have j: "j = Suc (card C)"
        by simp
      have "poly.coeff (poly.coeff (prod sum_factor (insert c C)) i) j =
          poly.coeff (poly.coeff ?P 0) (card C)"
        using top_rec i = 0 j by simp
      also have " = of_nat (card C choose 0)"
        using insert.IH[of 0 "card C"] by simp
      finally show ?thesis
        using i = 0 by simp
    next
      assume "j = 0"
      with ij' have i: "i = Suc (card C)"
        by simp
      have "poly.coeff (poly.coeff (prod sum_factor (insert c C)) i) j =
          poly.coeff (poly.coeff ?P (card C)) 0"
        using top_rec j = 0 i by simp
      also have " = of_nat (card C choose card C)"
        using insert.IH[of "card C" 0] by simp
      finally show ?thesis
        using i j = 0 insert.hyps by simp
    qed
  next
    case False
    then have i_pos: "i > 0" and j_pos: "j > 0"
      by auto
    have split1: "i - 1 + j = card C" and split2: "i + (j - 1) = card C"
      using ij' i_pos j_pos by simp_all
    have "poly.coeff (poly.coeff (prod sum_factor (insert c C)) i) j =
        poly.coeff (poly.coeff ?P (i - 1)) j +
        poly.coeff (poly.coeff ?P i) (j - 1)"
      using top_rec False by simp
    also have " = of_nat (card C choose (i - 1)) + of_nat (card C choose i)"
      using insert.IH[of "i - 1" j] insert.IH[of i "j - 1"] split1 split2 by simp
    also have " = of_nat (Suc (card C) choose i)"
      using i_pos by (cases i) simp_all
    finally show ?thesis
      using insert.hyps by simp
  qed
qed

lemma poly_sum_factor:
  "poly (poly (sum_factor c) [:x:]) y = x + y - c"
  by (simp add: sum_factor_def)

lemma prime_not_dvd_binomial:
  assumes "prime p" "k  n" "n < p"
  shows "¬ p dvd (n choose k)"
proof
  assume dvd_choose: "p dvd (n choose k)"
  have fact_expand: "fact n = fact k * fact (n - k) * (n choose k)"
    using binomial_fact_lemma[OF assms(2)] by (simp add: mult_ac)
  have "p dvd fact n"
    using dvd_choose fact_expand by simp
  with assms show False
    by (simp add: prime_dvd_fact_iff)
qed

lemma finite_subset_with_card:
  assumes "finite U" "n  card U"
  shows "V. V  U  card V = n"
  using assms
proof (induction U arbitrary: n rule: finite_induct)
  case empty
  then show ?case
    by auto
next
  case (insert x U)
  show ?case
  proof (cases "n  card U")
    case True
    then obtain V where "V  U" "card V = n"
      using insert.IH by blast
    then show ?thesis
      using insert.hyps by auto
  next
    case False
    have n_le: "n  Suc (card U)"
      using insert.prems insert.hyps by simp
    from False have "Suc (card U)  n"
      by simp
    with n_le have "n = Suc (card U)"
      by simp
    then show ?thesis
      using insert.hyps by auto
  qed
qed

lemma prime_card_eq_char:
  assumes prime_card: "prime (card (UNIV :: 'a::finite_field set))"
  shows "CHAR('a) = card (UNIV :: 'a set)"
proof -
  have "CHAR('a) dvd card (UNIV :: 'a set)"
    by (rule CHAR_dvd_CARD)
  moreover from prime_card have "m. m dvd card (UNIV :: 'a set)  m = 1  m = card (UNIV :: 'a set)"
    by (simp add: prime_nat_iff)
  ultimately have "CHAR('a) = 1  CHAR('a) = card (UNIV :: 'a set)"
    by blast
  with CHAR_prime show ?thesis
    by auto
qed

lemma of_nat_binomial_ne_zero:
  assumes prime_card: "prime (card (UNIV :: 'a::finite_field set))"
  assumes "k  n" "n < card (UNIV :: 'a set)"
  shows "of_nat (n choose k)  (0 :: 'a)"
proof -
  have char_eq: "CHAR('a) = card (UNIV :: 'a set)"
    by (rule prime_card_eq_char[OF prime_card])
  have "¬ CHAR('a) dvd (n choose k)"
    using assms by (simp add: char_eq prime_not_dvd_binomial prime_card_eq_char)
  then show ?thesis
    using of_nat_eq_0_iff_char_dvd by blast
qed

lemma coeff_x_factor_plus_const_0:
  "poly.coeff ([:-[:a:], 1:] * Q + [:R:]) 0 = - [:a:] * poly.coeff Q 0 + R"
  by (simp add: coeff_mult)

lemma coeff_x_factor_plus_const_Suc:
  "poly.coeff ([:-[:a:], 1:] * Q + [:R:]) (Suc i) =
    - [:a:] * poly.coeff Q (Suc i) + poly.coeff Q i"
  by (simp add: coeff_mult)

lemma total_degree_le_factor_out:
  fixes P Q :: "'a::field poly poly"
  assumes td: "total_degree_le P (Suc d)"
  assumes decomp: "P = [:-[:a:], 1:] * Q + [:R:]"
  shows "total_degree_le Q d"
proof -
  have outer_zero: "poly.coeff Q i = 0" if "d < i" for i
  proof (rule ccontr)
    assume nz: "poly.coeff Q i  0"
    let ?I = "{k. poly.coeff Q k  0}"
    have finI: "finite ?I"
    proof (rule finite_subset[of _ "{..degree Q}"])
      show "?I  {..degree Q}"
      proof
        fix k
        assume k_in: "k  ?I"
        have nz_k: "poly.coeff Q k  0"
          using k_in by simp
        show "k  {..degree Q}"
        proof (rule ccontr)
          assume "k  {..degree Q}"
          then have "degree Q < k"
            by simp
          then have "poly.coeff Q k = 0"
            by (rule coeff_eq_0)
          with nz_k show False
            by contradiction
        qed
      qed
    qed simp
    have i_in: "i  ?I"
      using nz by simp
    let ?k = "Max ?I"
    have k_in: "?k  ?I"
      by (rule Max_in[OF finI]) (use i_in in auto)
    have i_le_k: "i  ?k"
      using finI i_in by (intro Max_ge) auto
    have q_suc_zero: "poly.coeff Q (Suc ?k) = 0"
    proof (rule ccontr)
      assume nz_suc: "poly.coeff Q (Suc ?k)  0"
      then have suc_in: "Suc ?k  ?I"
        by simp
      have "Suc ?k  ?k"
        using finI suc_in by (intro Max_ge) auto
      then show False
        by simp
    qed
    have "poly.coeff P (Suc ?k) = poly.coeff Q ?k"
      using decomp q_suc_zero by (simp add: coeff_x_factor_plus_const_Suc)
    moreover have "poly.coeff P (Suc ?k) = 0"
      using td that i_le_k by (simp add: total_degree_le_def)
    ultimately show False
      using k_in by simp
  qed
  have degree_bound: "degree (poly.coeff Q i)  d - i" if "i  d" for i
  proof (rule ccontr)
    assume bad: "¬ degree (poly.coeff Q i)  d - i"
    let ?I = "{k. k  d  degree (poly.coeff Q k) > d - k}"
    have finI: "finite ?I"
      by simp
    have i_in: "i  ?I"
      using that bad by simp
    let ?k = "Max ?I"
    have k_in: "?k  ?I"
      by (rule Max_in[OF finI]) (use i_in in auto)
    have k_le_d: "?k  d" and deg_k: "degree (poly.coeff Q ?k) > d - ?k"
      using k_in by auto
    have next_bound: "degree (poly.coeff Q (Suc ?k))  d - Suc ?k"
    proof (cases "Suc ?k  d")
      case True
      have "Suc ?k  ?I"
      proof
        assume suc_in: "Suc ?k  ?I"
        have "Suc ?k  ?k"
          using finI suc_in by (intro Max_ge) auto
        then show False
          by simp
      qed
      then show ?thesis
        using True by simp
    next
      case False
      have "poly.coeff Q (Suc ?k) = 0"
        using outer_zero False by simp
      then show ?thesis
        by simp
    qed
    have deg_tail: "degree (- [:a:] * poly.coeff Q (Suc ?k)) < degree (poly.coeff Q ?k)"
      using next_bound deg_k by (auto intro: le_less_trans simp: degree_mult_le)
    have "poly.coeff P (Suc ?k) =
        poly.coeff Q ?k + (- [:a:] * poly.coeff Q (Suc ?k))"
      using decomp by (simp add: coeff_x_factor_plus_const_Suc algebra_simps)
    then have eq_p: "poly.coeff P (Suc ?k) =
        poly.coeff Q ?k + (- [:a:] * poly.coeff Q (Suc ?k))" .
    have deg_sum: "degree (poly.coeff Q ?k + (- [:a:] * poly.coeff Q (Suc ?k))) =
        degree (poly.coeff Q ?k)"
      by (rule degree_add_eq_left[OF deg_tail])
    have deg_p: "degree (poly.coeff P (Suc ?k)) = degree (poly.coeff Q ?k)"
      using eq_p deg_sum by simp
    have "degree (poly.coeff P (Suc ?k))  Suc d - Suc ?k"
    proof -
      have "Suc ?k  Suc d"
        using k_le_d by simp
      from td have step:
        "if Suc ?k  Suc d
         then degree (poly.coeff P (Suc ?k))  Suc d - Suc ?k
         else poly.coeff P (Suc ?k) = 0"
        unfolding total_degree_le_def by blast
      with Suc ?k  Suc d show ?thesis
        by simp
    qed
    then show False
      using deg_k deg_p by simp
  qed
  show ?thesis
    by (simp add: total_degree_le_def degree_bound outer_zero)
qed

lemma coeff_coeff_x_factor_top:
  fixes Q :: "'a::field poly poly"
  assumes td: "total_degree_le Q d"
  assumes mn: "m + n = d"
  shows "poly.coeff (poly.coeff ([:-[:a:], 1:] * Q + [:R:]) (Suc m)) n =
    poly.coeff (poly.coeff Q m) n"
proof -
  have "poly.coeff (poly.coeff ([:-[:a:], 1:] * Q + [:R:]) (Suc m)) n =
      poly.coeff (poly.coeff Q m) n - a * poly.coeff (poly.coeff Q (Suc m)) n"
    by (simp add: coeff_x_factor_plus_const_Suc coeff_mult)
  moreover have "poly.coeff (poly.coeff Q (Suc m)) n = 0"
  proof -
    show ?thesis
    proof (cases "Suc m  d")
      case True
      have deg_bound: "degree (poly.coeff Q (Suc m))  d - Suc m"
        using td True by (simp add: total_degree_le_def)
      have "d - Suc m < n"
      proof -
        from mn have "n = d - m"
          by simp
        with True show ?thesis
          by simp
      qed
      with deg_bound show ?thesis
        by (meson coeff_eq_0 le_less_trans)
    next
      case False
      with td show ?thesis
        by (simp add: total_degree_le_def)
    qed
  qed
  ultimately show ?thesis
    by simp
qed

lemma grid_nonzero_from_top_coeff:
  fixes P :: "'a::field poly poly"
  assumes cardA: "card A = Suc m"
  assumes cardB: "card B = Suc n"
  assumes td: "total_degree_le P (m + n)"
  assumes top: "poly.coeff (poly.coeff P m) n  0"
  shows "aA. bB. poly (poly P [:a:]) b  0"
  using cardA td top cardB
proof (induction m arbitrary: A P)
  case 0
  show ?case
  proof -
    from "0.prems"(1) have "a. A = {a}"
      by (simp add: card_1_singleton_iff)
    then obtain a where A: "A = {a}"
      by blast
    note td0 = "0.prems"(2)
    note top0 = "0.prems"(3)
    define R where "R = poly P [:a:]"
    have coeff_R: "poly.coeff R (0 + n) = poly.coeff (poly.coeff P 0) (0 + n)"
      unfolding R_def by (rule total_degree_le_eval_const_top[OF td0])
    have coeff_R_nz: "poly.coeff R n  0"
      using coeff_R top0 by simp
    then have nz_R: "R  0"
      by auto
    show ?thesis
    proof (rule ccontr)
      assume "¬ ?thesis"
      then have vanish_B: "bB. poly R b = 0"
        using A by (simp add: R_def)
      have "B  {b. poly R b = 0}"
        using vanish_B by auto
      then have "card B  card {b. poly R b = 0}"
        by (intro card_mono poly_roots_finite[OF nz_R]) auto
      also have "  degree R"
        by (rule card_poly_roots_bound[OF nz_R])
      finally have cardB_le_degR: "card B  degree R" .
      have degR': "degree R  0 + n"
        unfolding R_def by (rule total_degree_le_eval_const[OF td0])
      have degR: "degree R  n"
        using degR' by simp
      show False
        using cardB cardB_le_degR degR by linarith
    qed
  qed
next
  case (Suc m)
  have "0 < card A"
    using Suc.prems(1) by simp
  then have "A  {}"
    by auto
  then obtain a where a_in: "a  A"
    by auto
  show ?case
  proof (cases "bB. poly (poly P [:a:]) b  0")
    case True
    then show ?thesis
      using a_in by blast
  next
    case False
    define R where "R = poly P [:a:]"
    have vanish_R: "poly R b = 0" if "b  B" for b
      using False that by (simp add: R_def)
    have dvd: "[:-[:a:], 1:] dvd (P - [:R:])"
    proof -
      have eval0: "poly (P - [:poly P [:a:]:]) [:a:] = 0"
        by simp
      have dvd0: "[:-[:a:], 1:] dvd (P - [:poly P [:a:]:])"
        using eval0 by (rule iffD1[OF poly_eq_0_iff_dvd])
      show ?thesis
        using dvd0 R_def by simp
    qed
    then obtain Q where Q: "P = [:-[:a:], 1:] * Q + [:R:]"
      by (auto simp: dvd_def algebra_simps)
    let ?A' = "A - {a}"
    have cardA': "card ?A' = Suc m"
      using Suc.prems(1) a_in by (simp add: card_Diff_singleton_if)
    have tdQ: "total_degree_le Q (m + n)"
    proof -
      have tdP': "total_degree_le P (Suc (m + n))"
        using Suc.prems(2) by simp
      show ?thesis
        by (rule total_degree_le_factor_out[OF tdP' Q])
    qed
    have topQ: "poly.coeff (poly.coeff Q m) n  0"
    proof -
      have coeff_eq:
        "poly.coeff (poly.coeff P (Suc m)) n = poly.coeff (poly.coeff Q m) n"
      proof -
        have "poly.coeff (poly.coeff ([:-[:a:], 1:] * Q + [:R:]) (Suc m)) n =
            poly.coeff (poly.coeff Q m) n"
          by (rule coeff_coeff_x_factor_top[OF tdQ]) simp
        then show ?thesis
          by (simp add: Q)
      qed
      with Suc.prems(3) show ?thesis
        by simp
    qed
    obtain x b where xb: "x  ?A'" "b  B" "poly (poly Q [:x:]) b  0"
      using Suc.IH[OF cardA' tdQ topQ cardB] by blast
    have x_neq_a: "x  a"
      using xb by simp
    have "poly (poly P [:x:]) b = (x - a) * poly (poly Q [:x:]) b + poly R b"
    proof -
      have "poly (poly P [:x:]) b =
          poly (poly ([:-[:a:], 1:] * Q + [:R:]) [:x:]) b"
        using Q by simp
      also have " = poly (poly ([:-[:a:], 1:] * Q) [:x:]) b + poly R b"
        by simp
      also have "poly (poly ([:-[:a:], 1:] * Q) [:x:]) b =
          (x - a) * poly (poly Q [:x:]) b"
        by (simp add: algebra_simps)
      finally show ?thesis .
    qed
    also have " = (x - a) * poly (poly Q [:x:]) b"
      using xb vanish_R by simp
    also have "  0"
      using xb x_neq_a by simp
    finally have "poly (poly P [:x:]) b  0" .
    then show ?thesis
      using xb by blast
  qed
qed

lemma sumset_eq_UNIV_if_large:
  fixes A B :: "'a::finite_field set"
  assumes "card (UNIV :: 'a set) < card A + card B - 1"
  assumes "A  {}" "B  {}"
  shows "sumset A B = UNIV"
proof
  show "sumset A B  UNIV"
    by simp
next
  show "UNIV  sumset A B"
  proof
    fix x :: 'a
    let ?T = "translate x ((uminus) ` B)"
    have card_negB: "card ((uminus) ` B) = card B"
      by (intro card_image inj_onI) auto
    have card_T: "card ?T = card B"
      by (simp add: card_negB card_translate_eq)
    have "card A + card ?T = card (A  ?T) + card (A  ?T)"
      using card_Un_Int[of A ?T] by simp
    moreover have "card (A  ?T)  card (UNIV :: 'a set)"
      by (rule card_mono) auto
    ultimately have "card (A  ?T) > 0"
      using assms card_T by linarith
    then have "A  ?T  {}"
      by auto
    then obtain a where a: "a  A  ?T"
      by auto
    then obtain b where b: "b  B" "a = x + (- b)"
      by (auto simp: translate_iff)
    have a_in: "a  A"
      using a by simp
    have "x = a + b"
      using b by simp
    moreover have "a + b  sumset A B"
      using a_in b(1) by (rule sumsetI)
    ultimately show "x  sumset A B"
      by simp
  qed
qed

subsection ‹The main lower bound›

text ‹
  The final statement is the abstract prime-field version of Cauchy-Davenport:
  for nonempty finite subsets of a field whose cardinality is prime, the sumset
  has size at least the expected truncated lower bound.
›

theorem cauchy_davenport_prime_field:
  fixes A B :: "'a::finite_field set"
  assumes prime_card: "prime (card (UNIV :: 'a set))"
  assumes "A  {}" "B  {}"
  shows "card (sumset A B)  min (card (UNIV :: 'a set)) (card A + card B - 1)"
proof (cases "card (UNIV :: 'a set) < card A + card B - 1")
  case True
  have "sumset A B = UNIV"
  proof (rule sumset_eq_UNIV_if_large)
    show "card (UNIV :: 'a set) < card A + card B - 1"
      using True by assumption
    show "A  {}"
      using assms(2) .
    show "B  {}"
      using assms(3) .
  qed
  then show ?thesis
    by simp
next
  case False
  let ?m = "card A - 1"
  let ?n = "card B - 1"
  have cardA_pos: "0 < card A" and cardB_pos: "0 < card B"
    using assms(2,3) by auto
  have cardA: "card A = Suc ?m" and cardB: "card B = Suc ?n"
    using cardA_pos cardB_pos by simp_all
  have mn_lt: "?m + ?n < card (UNIV :: 'a set)"
  proof -
    have le_card: "card A + card B - 1  card (UNIV :: 'a set)"
      using False by simp
    have "Suc (?m + ?n) = card A + card B - 1"
      using cardA cardB by simp
    with le_card have "Suc (?m + ?n)  card (UNIV :: 'a set)"
      by simp
    then show ?thesis
      by simp
  qed
  show ?thesis
  proof (rule ccontr)
    assume bad: "¬ card (sumset A B)  min (card (UNIV :: 'a set)) (card A + card B - 1)"
    have sumset_small: "card (sumset A B)  ?m + ?n"
      using False bad assms by simp
    obtain C where C: "sumset A B  C" "C  UNIV" "card C = ?m + ?n"
      using exists_subset_between[of "sumset A B" "?m + ?n" UNIV] sumset_small mn_lt by auto
    define F where "F = prod sum_factor C"
    have tdF: "total_degree_le F (?m + ?n)"
    proof -
      have finC: "finite C"
        using C by auto
      have "total_degree_le F (card C)"
        unfolding F_def by (rule total_degree_le_prod_sum_factor[OF finC])
      with C show ?thesis
        by simp
    qed
    have topF: "poly.coeff (poly.coeff F ?m) ?n = of_nat ((?m + ?n) choose ?m)"
    proof -
      have finC: "finite C"
        using C by auto
      have mn_card: "?m + ?n = card C"
        using C by simp
      have "poly.coeff (poly.coeff F ?m) ?n = of_nat (card C choose ?m)"
        unfolding F_def by (rule coeff_coeff_prod_sum_factor_top[OF finC mn_card])
      with C show ?thesis
        by simp
    qed
    have topF_nz: "poly.coeff (poly.coeff F ?m) ?n  0"
      using of_nat_binomial_ne_zero[OF prime_card, of ?m "?m + ?n"] topF mn_lt by simp
    obtain a b where ab: "a  A" "b  B" "poly (poly F [:a:]) b  0"
      using grid_nonzero_from_top_coeff[OF cardA cardB tdF topF_nz] by blast
    have ab_sum: "a + b  sumset A B"
      using ab(1,2) by (rule sumsetI)
    have "a + b  C"
      using ab_sum C(1) by auto
    then have "poly (poly F [:a:]) b = 0"
    proof -
      have finC: "finite C"
        using C by auto
      have "poly (poly F [:a:]) b = (cC. (a + b - c))"
        using finC by (simp add: F_def poly_prod poly_sum_factor)
      also have " = 0"
        using finC a + b  C by (simp add: prod_zero)
      finally show ?thesis .
    qed
    with ab show False
      by simp
  qed
qed

end