Theory Interpolation_Polynomials_HOL_Algebra.Interpolation_Polynomial_Cardinalities
section ‹Cardinalities of Interpolation Polynomials›
text ‹This section establishes the cardinalities of the set of polynomials with a degree bound
interpolating a given set of points.›
theory Interpolation_Polynomial_Cardinalities
  imports Bounded_Degree_Polynomials Lagrange_Interpolation
begin
lemma (in ring) poly_add_coeff:
  assumes "x ∈ carrier (poly_ring R)"
  assumes "y ∈ carrier (poly_ring R)"
  shows "coeff (x ⊕⇘poly_ring R⇙ y) k = coeff x k ⊕ coeff y k"
  by (metis assms univ_poly_carrier polynomial_incl univ_poly_add poly_add_coeff)
lemma (in domain) poly_neg_coeff:
  assumes "x ∈ carrier (poly_ring R)"
  shows "coeff (⊖⇘poly_ring R⇙ x) k = ⊖coeff x k"
proof -
  interpret x:cring "poly_ring R"
    using assms cring_def carrier_is_subring domain.univ_poly_is_cring domain_axioms by auto
  have a:"𝟬⇘poly_ring R⇙ = x ⊖⇘poly_ring R⇙ x"
    by (metis x.r_right_minus_eq assms(1))
  have "𝟬 = coeff (𝟬⇘poly_ring R⇙) k" by (simp add:univ_poly_zero)
  also have "... = coeff x k ⊕ coeff (⊖⇘poly_ring R⇙ x) k" using a assms
    by (simp add:a_minus_def poly_add_coeff)
  finally have "𝟬 = coeff x k ⊕ coeff (⊖⇘poly_ring R⇙ x) k" by simp
  thus ?thesis
    by (metis local.minus_minus x.a_inv_closed sum_zero_eq_neg coeff_in_carrier assms)
qed
lemma (in domain) poly_substract_coeff:
  assumes "x ∈ carrier (poly_ring R)"
  assumes "y ∈ carrier (poly_ring R)"
  shows "coeff (x ⊖⇘poly_ring R⇙ y) k = coeff x k ⊖ coeff y k"
proof -
  interpret x:cring "poly_ring R"
    using assms cring_def carrier_is_subring domain.univ_poly_is_cring domain_axioms by auto
  show ?thesis
    using assms by (simp add:a_minus_def poly_add_coeff poly_neg_coeff)
qed
text ‹A polynomial with more zeros than its degree is the zero polynomial.›
lemma (in field) max_roots:
  assumes "p ∈ carrier (poly_ring R)"
  assumes "K ⊆ carrier R"
  assumes "finite K"
  assumes "degree p < card K"
  assumes "⋀x. x ∈ K ⟹ eval p x = 𝟬"
  shows "p = 𝟬⇘poly_ring R⇙"
proof (rule ccontr)
  assume "p ≠ 𝟬⇘poly_ring R⇙"
  hence a:"p ≠ []" by (simp add: univ_poly_zero)
  have "⋀x. count (mset_set K) x ≤ count (roots p) x"
  proof -
    fix x
    show "count (mset_set K) x ≤ count (roots p) x"
    proof (cases "x ∈ K")
      case True
      hence "is_root p x"
        by (meson a assms(2,5) is_ring is_root_def subsetD)
      hence "x ∈ set_mset (roots p)"
        using assms(1) roots_mem_iff_is_root field_def by force
      hence "1 ≤ count (roots p) x" by simp
      moreover have "count (mset_set K) x = 1" using True assms(3) by simp
      ultimately show ?thesis by presburger
    next
      case False
      hence "count (mset_set K) x = 0" by simp
      then show ?thesis by presburger
    qed
  qed
  hence "mset_set K ⊆# roots p"
    by (simp add: subseteq_mset_def)
  hence "card K ≤ size (roots p)"
    by (metis size_mset_mono size_mset_set)
  moreover have "size (roots p) ≤ degree p"
    using a size_roots_le_degree assms by auto
  ultimately show "False" using assms(4)
    by (meson leD less_le_trans)
qed
definition (in ring) split_poly
  where "split_poly K p = (restrict (eval p) K, λk. coeff p (k+card K))"
text ‹To establish the count of the number of polynomials of degree less than
$n$ interpolating a function $f$ on $K$ where $\lvert K \rvert \leq n$, the function
@{term "split_poly K"} establishes a bijection between the polynomials of degree less than
$n$ and the values of the polynomials on $K$ in combination with the coefficients of order
$\lvert K \rvert$ and greater.
For the injectivity: Note that the difference of two polynomials whose coefficients of order
$\lvert K \rvert$ and larger agree must have a degree less than $\lvert K \rvert$ and because
their values agree on $k$ points, it must have $\lvert K \rvert$ zeros and hence is the zero
polynomial.
For the surjectivty: Let $p$ be a polynomial whose coefficients larger than $\lvert K \rvert$ are
chosen, and all other coefficients be $0$. Now it is possible to find a polynomial $q$ interpolating
$f - p$ on $K$ using Lagrange interpolation. Then $p + q$ will interpolate $f$ on $K$ and because
the degree of $q$ is less than $\lvert K \rvert$ its coefficients of order $\lvert K \rvert$ will
be the same as those of $p$.
A tempting question is whether it would be easier to instead establish a bijection between the
polynomials of degree less than $n$ and its values on $K \cup K'$ where $K'$ are arbitrarily chosen
$n-\lvert K \rvert$ points in the field. This approach is indeed easier, however, it fails for
the case where the size of the field is less than $n$.›
lemma (in field) split_poly_inj:
  assumes "finite K"
  assumes "K ⊆ carrier R"
  shows "inj_on (split_poly K) (carrier (poly_ring R))"
proof
  fix x
  fix y
  assume a1:"x ∈ carrier (poly_ring R)"
  assume a2:"y ∈ carrier (poly_ring R)"
  assume a3:"split_poly K x = split_poly K y"
  interpret x:cring "poly_ring R"
    using carrier_is_subring domain.univ_poly_is_cring domain_axioms by auto
  have x_y_carrier: "x ⊖⇘poly_ring R⇙ y ∈ carrier (poly_ring R)" using a1 a2 by simp
  have "⋀k. coeff x (k+card K) = coeff y (k+card K)"
    using a3 by (simp add:split_poly_def, meson)
  hence "⋀k. coeff (x ⊖⇘poly_ring R⇙ y) (k+card K) = 𝟬"
    using coeff_in_carrier a1 a2 by (simp add:poly_substract_coeff)
  hence "degree (x ⊖⇘poly_ring R⇙ y) < card K ∨ (x ⊖⇘poly_ring R⇙ y) = 𝟬⇘poly_ring R⇙"
    by (metis poly_degree_bound_from_coeff add.commute le_iff_add x_y_carrier)
  moreover have "⋀k. k ∈ K ⟹ eval x k = eval y k"
    using a3 by (simp add:split_poly_def restrict_def, meson)
  hence "⋀k. k ∈ K ⟹ eval x k ⊖ eval y k = 𝟬"
    by (metis eval_in_carrier univ_poly_carrier polynomial_incl a1 assms(2) in_mono r_right_minus_eq)
  hence "⋀k. k ∈ K ⟹ eval (x ⊖⇘poly_ring R⇙ y) k = 𝟬"
    using a1 a2 subsetD[OF assms(2)] carrier_is_subring
    by (simp add: ring_hom_cring.hom_sub[OF eval_cring_hom])
  ultimately have "x ⊖⇘poly_ring R⇙ y = 𝟬⇘poly_ring R⇙"
    using max_roots x_y_carrier assms by blast
  then show "x = y"
    using x.r_right_minus_eq[OF a1 a2] by simp
qed
lemma (in field) split_poly_image:
  assumes "finite K"
  assumes "K ⊆ carrier R"
  shows "split_poly K ` carrier (poly_ring R) ⊇
        (K →⇩E carrier R) × {f. range f ⊆ carrier R ∧ (∃n. ∀k ≥ n. f k = 𝟬⇘R⇙)}"
proof (rule subsetI)
  fix x
  assume a:"x ∈ (K →⇩E carrier R) × {f. range f ⊆ carrier R ∧ (∃(n::nat). ∀k ≥ n. f k = 𝟬)}"
  have a1: "fst x ∈ (K →⇩E carrier R)"
    using a by (simp add:mem_Times_iff)
  obtain n where a2: "snd x ∈ {f. range f ⊆ carrier R ∧ (∀k ≥ n. f k = 𝟬)}"
    using a mem_Times_iff by force
  have a3: "⋀y. snd x y ∈ carrier R" using a2 by blast
  define w where "w = build_poly (λi. if i ≥ card K then (snd x (i - card K)) else 𝟬) (card K + n)"
  have w_carr: "w ∈ carrier (poly_ring R)"
    unfolding w_def by (rule build_poly_poly, simp add:a3)
  have w_eval_range: "⋀x. x ∈ carrier R ⟹ local.eval w x ∈ carrier R"
  proof -
    fix x
    assume w_eval_range_1:"x ∈ carrier R"
    interpret x:ring_hom_cring "poly_ring R" "R" "(λp. eval p x)"
      using eval_cring_hom[OF carrier_is_subring] assms w_eval_range_1 by blast
    show "eval w x ∈ carrier R"
      by (rule x.hom_closed[OF w_carr])
  qed
  interpret r:cring "poly_ring R"
    using carrier_is_subring domain.univ_poly_is_cring domain_axioms by auto
  define y where "y = interpolate K (λk. fst x k ⊖ eval w k)"
  define r where "r = y ⊕⇘poly_ring R⇙ w"
  have x_minus_w_in_carrier: "⋀z. z ∈ K ⟹ fst x z ⊖ eval w z ∈ carrier R"
    using a1 PiE_def Pi_def minus_closed subsetD[OF assms(2)] w_eval_range by auto
  have y_poly: "y ∈ carrier (poly_ring R)" unfolding y_def
    using x_minus_w_in_carrier interpolate_poly[OF assms(1) assms(2)] image_subsetI by force
  have y_degree: "degree y ≤ card K - 1"
    unfolding y_def
    using x_minus_w_in_carrier interpolate_degree[OF assms(1) assms(2)] image_subsetI by force
  have y_len: "length y ≤ card K"
  proof (cases "K={}")
    case True
    then show ?thesis
      by (simp add:y_def interpolate_def univ_poly_zero)
  next
    case False
    then show ?thesis
      by (metis y_degree Suc_le_D assms(1) card_gt_0_iff diff_Suc_1 not_less_eq_eq order.strict_iff_not)
  qed
  have r_poly: "r ∈ carrier (poly_ring R)"
    using r_def y_poly w_carr by simp
  have coeff_r: "⋀k. coeff r (k + card K) = snd x k"
  proof -
    fix k :: nat
    have y_len': "length y ≤ k + card K" using y_len trans_le_add2 by blast
    have "coeff r (k + card K) = coeff y (k + card K) ⊕ coeff w (k+card K)"
      by (simp add:r_def  poly_add_coeff[OF y_poly w_carr])
    also have "... = 𝟬 ⊕ coeff w (k+card K)"
      using coeff_length[OF y_len'] by simp
    also have "... = coeff w (k+card K)"
      using coeff_in_carrier[OF w_carr] by simp
    also have "... = snd x k"
      using a2 by (simp add:w_def build_poly_coeff not_less)
    finally show "coeff r (k + card K) = snd x k" by simp
  qed
  have eval_r: "⋀k. k ∈ K ⟹ eval r k = fst x k"
  proof -
    fix k
    assume b:"k ∈ K"
    interpret s:ring_hom_cring "poly_ring R" "R" "(λp. eval p k)"
      using eval_cring_hom[OF carrier_is_subring] assms b by blast
    have k_carr: "k ∈ carrier R" using assms(2) b by blast
    have fst_x_k_carr: "⋀k. k ∈ K ⟹ fst x k ∈ carrier R"
      using  a1 PiE_def Pi_def by blast
    have "eval r k = eval y k ⊕ eval w k"
      using y_poly w_carr by (simp add:r_def)
    also have "... = fst x k ⊖ local.eval w k ⊕ local.eval w k"
      using assms b x_minus_w_in_carrier
      by (simp add:y_def interpolate_eval[OF _ _ image_subsetI])
    also have "... = fst x k ⊕ (⊖ local.eval w k ⊕ local.eval w k)"
      using fst_x_k_carr[OF b] w_eval_range[OF k_carr]
      by (simp add:a_minus_def a_assoc)
    also have "... = fst x k"
      using fst_x_k_carr[OF b] w_eval_range[OF k_carr]
      by (simp add:a_comm r_neg)
    finally show "eval r k = fst x k" by simp
  qed
  have "r ∈ (carrier (poly_ring R))"
    by (metis r_poly)
  moreover have "⋀y. (if y ∈ K then eval r y else undefined) = fst x y"
    using a1 eval_r PiE_E by auto
  hence "split_poly K r = x"
    by (simp add:split_poly_def prod_eq_iff coeff_r restrict_def)
  ultimately show "x ∈ split_poly K ` (carrier (poly_ring R))"
    by blast
qed
text ‹This is like @{thm [source] card_vimage_inj} but supports @{term "inj_on"} instead.›
lemma card_vimage_inj_on:
  assumes "inj_on f B"
  assumes "A ⊆ f ` B"
  shows "card (f -` A ∩ B) = card A"
proof -
  have "A = f ` (f -` A ∩ B)" using assms(2) by auto
  thus ?thesis using assms card_image
    by (metis inf_le2 inj_on_subset)
qed
lemma inv_subsetI:
  assumes "⋀x. x ∈ A ⟹ f x ∈ B ⟹ x ∈ C"
  shows "f -` B ∩ A ⊆ C"
  using assms by force
text ‹The following establishes the main result of this section: There are $\lvert F \rvert^{n-k}$
polynomials of degree less than $n$ interpolating $k \leq n$ points.›
lemma restrict_eq_imp:
  assumes "restrict f A = restrict g A"
  assumes "x ∈ A"
  shows "f x = g x"
  by (metis restrict_def assms)
theorem (in field) interpolating_polynomials_card:
  assumes "finite K"
  assumes "K ⊆ carrier R"
  assumes "f ` K ⊆ carrier R"
  shows "card {ω ∈ bounded_degree_polynomials R (card K + n). (∀k ∈ K. eval ω k = f k)} = card (carrier R)^n"
    (is "card ?A = ?B")
proof -
  define z where "z = restrict f K"
  define M where "M = {f. range f ⊆ carrier R ∧ (∀k ≥ n. f k = 𝟬)}"
  hence inj_on_bounded: "inj_on (split_poly K) (carrier (poly_ring R))"
    using split_poly_inj[OF assms(1) assms(2)] by blast
  have "?A ⊆ split_poly K -` ({z} × M)"
    unfolding split_poly_def z_def M_def bounded_degree_polynomials_length
    by (rule subsetI, auto intro!:coeff_in_carrier coeff_length)
  moreover have "?A ⊆ carrier (poly_ring R)"
    unfolding bounded_degree_polynomials_length by blast
  ultimately have a:"?A ⊆ split_poly K -` ({z} × M) ∩ carrier (poly_ring R)"
    by blast
  have "⋀x k . (λk. coeff x (k + card K)) ∈ M ⟹ k ≥ n + card K ⟹ coeff x k = 𝟬"
    by (simp add:M_def, metis Nat.le_diff_conv2 Nat.le_imp_diff_is_add add_leD2)
  hence "split_poly K -` ({z} × M) ∩ carrier (poly_ring R) ⊆ bounded_degree_polynomials R (card K + n)"
    unfolding split_poly_def z_def using poly_degree_bound_from_coeff_1 inv_subsetI by force
  moreover have "⋀ω k. ω ∈ split_poly K -` ({z} × M) ∩ carrier (poly_ring R) ⟹ k ∈ K ⟹ eval ω k = f k"
    unfolding split_poly_def z_def using restrict_eq_imp by fastforce
  ultimately have b:"split_poly K -` ({z} × M) ∩ carrier (poly_ring R) ⊆ ?A"
    by blast
  have "z ∈ K →⇩E carrier R"
    unfolding z_def using assms(3) by auto
  moreover have "M ⊆ {f. range f ⊆ carrier R ∧ (∃n. (∀k ≥ n. f k = 𝟬))}"
    unfolding M_def by blast
  ultimately have c:"{z} × M ⊆ split_poly K ` carrier (poly_ring R)"
    using split_poly_image[OF assms(1) assms(2)] by fast
  have "card ?A = card (split_poly K -` ({z} × M) ∩ carrier (poly_ring R))"
    using order_antisym[OF a b] by simp
  also have "... = card ({z} × M)"
    using card_vimage_inj_on[OF inj_on_bounded] c by blast
  also have "... = card (carrier R)^n"
    by (simp add:card_cartesian_product M_def card_mostly_constant_maps)
  finally show ?thesis by simp
qed
text ‹A corollary is the classic result~\<^cite>‹‹Theorem 7.15› in "shoup2009computational"› that there is
exactly one polynomial of degree less than $n$ interpolating $n$ points:›
corollary (in field) interpolating_polynomial_one:
  assumes "finite K"
  assumes "K ⊆ carrier R"
  assumes "f ` K ⊆ carrier R"
  shows "card {ω ∈ bounded_degree_polynomials R (card K). (∀k ∈ K. eval ω k = f k)} = 1"
  using interpolating_polynomials_card[OF assms(1) assms(2) assms(3), where n="0"]
  by simp
text ‹In the case of fields with infinite carriers, it is possible to conclude that there are
infinitely many polynomials of degree less than $n$ interpolating $k < n$ points.›
corollary (in field) interpolating_polynomial_inf:
  assumes "infinite (carrier R)"
  assumes "finite K" "K ⊆ carrier R" "f ` K ⊆ carrier R"
  assumes "n > 0"
  shows "infinite {ω ∈ bounded_degree_polynomials R (card K + n). (∀k ∈ K. eval ω k = f k)}"
    (is "infinite ?A")
proof -
  have "{} ⊂ {ω ∈ bounded_degree_polynomials R (card K). (∀k ∈ K. eval ω k = f k)}"
    using interpolating_polynomial_one[OF assms(2) assms(3) assms(4)] by fastforce
  also have "... ⊆ ?A"
    unfolding bounded_degree_polynomials_def by auto
  finally have a:"?A ≠ {}" by auto
  have "card ?A = card (carrier R)^n"
    using interpolating_polynomials_card[OF assms(2) assms(3) assms(4), where n="n"] by simp
  also have "... = 0"
    using assms(1) assms(5) by simp
  finally have b:"card ?A = 0" by simp
  show ?thesis using a b card_0_eq by blast
qed
text ‹The following is an additional independent result: The evaluation homomorphism is injective
for degree one polynomials.›
lemma  (in field) eval_inj_if_degree_1:
  assumes "p ∈ carrier (poly_ring R)" "degree p = 1"
  shows "inj_on (eval p) (carrier R)"
proof -
  obtain u v where p_def: "p = [u,v]" using assms
    by (cases p, cases "(tl p)", auto)
  have "u ∈ carrier R - {𝟬}" using p_def assms by blast
  moreover have "v ∈ carrier R" using p_def assms by blast
  ultimately show ?thesis by (simp add:p_def field_Units inj_on_def)
qed
end