Theory Finite_Fields.Formal_Polynomial_Derivatives
section ‹Formal Derivatives\label{sec:pderiv}›
theory Formal_Polynomial_Derivatives
  imports "HOL-Algebra.Polynomial_Divisibility" "Ring_Characteristic"
begin
definition pderiv (‹pderivı›) where
  "pderiv⇘R⇙ x = ring.normalize R (
    map (λi. int_embed R i ⊗⇘R⇙ ring.coeff R x i) (rev [1..<length x]))"
context domain
begin
lemma coeff_range:
  assumes "subring K R"
  assumes "f ∈ carrier (K[X])"
  shows "coeff f i ∈ K"
proof -
  have "coeff f i ∈ set f ∪ {𝟬}"
    using coeff_img(3) by auto
  also have "... ⊆ K ∪ {𝟬}"
    using assms(2) univ_poly_carrier polynomial_incl by blast
  also have "... ⊆ K"
    using subringE[OF assms(1)] by simp
  finally show ?thesis by simp
qed
lemma pderiv_carr:
  assumes "subring K R"
  assumes "f ∈ carrier (K[X])"
  shows "pderiv f ∈ carrier (K[X])"
proof -
  have "int_embed R i ⊗ coeff f i ∈ K" for i
    using coeff_range[OF assms] int_embed_range[OF assms(1)]
    using subringE[OF assms(1)] by simp
  hence "polynomial K (pderiv f)"
    unfolding pderiv_def by (intro normalize_gives_polynomial, auto)
  thus ?thesis
    using univ_poly_carrier by auto
qed
lemma pderiv_coeff:
  assumes "subring K R"
  assumes "f ∈ carrier (K[X])"
  shows "coeff (pderiv f) k = int_embed R (Suc k) ⊗ coeff f (Suc k)"
    (is "?lhs = ?rhs")
proof (cases "k + 1  < length f")
  case True
  define j where "j = length f - k - 2"
  define d where
    "d = map (λi. int_embed R i ⊗ coeff f i) (rev [1..<length f])"
  have a: "j+1 < length f"
    using True unfolding j_def by simp
  hence b: "j < length [1..<length f]"
    by simp
  have c: "k < length d"
    unfolding d_def using True by simp
  have d: "degree d - k = j"
    unfolding d_def j_def by simp
  have e: "rev [Suc 0..<length f] ! j = length f - 1 - j"
    using b  by (subst rev_nth, auto)
  have f: "length f - j - 1 = k+1"
    unfolding j_def using True by simp
  have "coeff (pderiv f ) k = coeff (normalize d) k"
    unfolding pderiv_def d_def by simp
  also have "... = coeff d k"
    using normalize_coeff by simp
  also have "... = d ! j"
    using c d by (subst coeff_nth, auto)
  also have
    "... = int_embed R (length f - j - 1) ⊗ coeff f (length f - j - 1)"
    using b e unfolding d_def by simp
  also have "... = ?rhs"
    using f by simp
  finally show ?thesis by simp
next
  case False
  hence "Suc k ≥ length f"
    by simp
  hence a:"coeff f (Suc k) = 𝟬"
    using coeff_img by blast
  have b:"coeff (pderiv f) k = 𝟬"
    unfolding pderiv_def normalize_coeff[symmetric] using False
    by (intro coeff_length, simp)
  show ?thesis
    using int_embed_range[OF carrier_is_subring] by (simp add:a b)
qed
lemma pderiv_const:
  assumes "degree x = 0"
  shows "pderiv x = 𝟬⇘K[X]⇙"
proof (cases "length x = 0")
  case True
  then show ?thesis by (simp add:univ_poly_zero pderiv_def)
next
  case False
  hence "length x = 1" using assms by linarith
  then obtain y where "x = [y]" by (cases x, auto)
  then show ?thesis by (simp add:univ_poly_zero pderiv_def)
qed
lemma pderiv_var:
  shows "pderiv X = 𝟭⇘K[X]⇙"
  unfolding var_def pderiv_def
  by (simp add:univ_poly_one int_embed_def)
lemma pderiv_zero:
  shows "pderiv 𝟬⇘K[X]⇙ = 𝟬⇘K[X]⇙"
  unfolding pderiv_def univ_poly_zero by simp
lemma pderiv_add:
  assumes "subring K R"
  assumes [simp]: "f ∈ carrier (K[X])" "g ∈ carrier (K[X])"
  shows "pderiv (f ⊕⇘K[X]⇙ g) = pderiv f ⊕⇘K[X]⇙ pderiv g"
    (is "?lhs = ?rhs")
proof -
  interpret p: ring "(K[X])"
    using univ_poly_is_ring[OF assms(1)] by simp
  let ?n = "(λi. int_embed R i)"
  have a[simp]:"?n k ∈ carrier R" for k
    using int_embed_range[OF carrier_is_subring] by auto
  have b[simp]:"coeff f k ∈ carrier R" if "f ∈ carrier (K[X])" for k f
    using coeff_range[OF assms(1)] that
    using subringE(1)[OF assms(1)] by auto
  have "coeff ?lhs i = coeff ?rhs i" for i
  proof -
    have "coeff ?lhs i = ?n (i+1) ⊗ coeff (f ⊕⇘K [X]⇙ g) (i+1)"
      by (simp add: pderiv_coeff[OF assms(1)])
    also have "... = ?n (i+1) ⊗ (coeff f (i+1) ⊕ coeff g (i+1))"
      by (subst coeff_add[OF assms], simp)
    also have "... = ?n (i+1) ⊗ coeff f (i+1)
      ⊕ int_embed R (i+1) ⊗ coeff g (i+1)"
      by (subst r_distr, simp_all)
    also have "... = coeff (pderiv f) i ⊕ coeff (pderiv g) i"
      by (simp add: pderiv_coeff[OF assms(1)])
    also have "... = coeff (pderiv f ⊕⇘K [X]⇙ pderiv g) i"
      using pderiv_carr[OF assms(1)]
      by (subst coeff_add[OF assms(1)], auto)
    finally show ?thesis by simp
  qed
  hence "coeff ?lhs = coeff ?rhs" by auto
  thus "?lhs = ?rhs"
    using pderiv_carr[OF assms(1)]
    by (subst coeff_iff_polynomial_cond[where K="K"])
      (simp_all add:univ_poly_carrier)+
qed
lemma pderiv_inv:
  assumes "subring K R"
  assumes [simp]: "f ∈ carrier (K[X])"
  shows "pderiv (⊖⇘K[X]⇙ f) = ⊖⇘K[X]⇙ pderiv f" (is "?lhs = ?rhs")
proof -
  interpret p: cring "(K[X])"
    using univ_poly_is_cring[OF assms(1)] by simp
  have "pderiv (⊖⇘K[X]⇙ f) = pderiv (⊖⇘K[X]⇙ f) ⊕⇘K[X]⇙ 𝟬⇘K[X]⇙"
    using pderiv_carr[OF assms(1)]
    by (subst p.r_zero, simp_all)
  also have "... = pderiv (⊖⇘K[X]⇙ f) ⊕⇘K[X]⇙ (pderiv f ⊖⇘K[X]⇙ pderiv f)"
    using pderiv_carr[OF assms(1)] by simp
  also have "... = pderiv (⊖⇘K[X]⇙ f) ⊕⇘K[X]⇙ pderiv f ⊖⇘K[X]⇙ pderiv f"
    using pderiv_carr[OF assms(1)]
    unfolding a_minus_def by (simp add:p.a_assoc)
  also have "... = pderiv (⊖⇘K[X]⇙ f ⊕⇘K[X]⇙ f) ⊖⇘K[X]⇙ pderiv f"
    by (subst pderiv_add[OF assms(1)], simp_all)
  also have "... = pderiv 𝟬⇘K[X]⇙ ⊖⇘K[X]⇙ pderiv f"
    by (subst p.l_neg, simp_all)
  also have "... = 𝟬⇘K[X]⇙ ⊖⇘K[X]⇙ pderiv f"
    by (subst pderiv_zero, simp)
  also have "... = ⊖⇘K[X]⇙ pderiv f"
    unfolding a_minus_def using pderiv_carr[OF assms(1)]
    by (subst p.l_zero, simp_all)
  finally show "pderiv (⊖⇘K[X]⇙ f) = ⊖⇘K[X]⇙ pderiv f"
    by simp
qed
lemma coeff_mult:
  assumes "subring K R"
  assumes "f ∈ carrier (K[X])" "g ∈ carrier (K[X])"
  shows "coeff (f ⊗⇘K[X]⇙ g) i =
    (⨁ k ∈ {..i}. (coeff f) k ⊗ (coeff g) (i - k))"
proof -
  have a:"set f ⊆ carrier R"
    using assms(1,2) univ_poly_carrier
    using subringE(1)[OF assms(1)] polynomial_incl by blast
  have b:"set g ⊆ carrier R"
    using assms(1,3) univ_poly_carrier
    using subringE(1)[OF assms(1)] polynomial_incl by blast
  show ?thesis
    unfolding univ_poly_mult poly_mult_coeff[OF a b] by simp
qed
lemma pderiv_mult:
  assumes "subring K R"
  assumes [simp]: "f ∈ carrier (K[X])" "g ∈ carrier (K[X])"
  shows "pderiv (f ⊗⇘K[X]⇙ g) =
    pderiv f ⊗⇘K[X]⇙ g ⊕⇘K[X]⇙ f ⊗⇘K[X]⇙ pderiv g"
    (is "?lhs = ?rhs")
proof -
  interpret p: cring "(K[X])"
    using univ_poly_is_cring[OF assms(1)] by simp
  let ?n = "(λi. int_embed R i)"
  have a[simp]:"?n k ∈ carrier R" for k
    using int_embed_range[OF carrier_is_subring] by auto
  have b[simp]:"coeff f k ∈ carrier R" if "f ∈ carrier (K[X])" for k f
    using coeff_range[OF assms(1)]
    using subringE(1)[OF assms(1)] that by auto
  have "coeff ?lhs i = coeff ?rhs i" for i
  proof -
    have "coeff ?lhs i = ?n (i+1) ⊗ coeff (f ⊗⇘K [X]⇙ g) (i+1)"
      using assms(2,3) by (simp add: pderiv_coeff[OF assms(1)])
    also have "... = ?n (i+1) ⊗
      (⨁k ∈ {..i+1}. coeff f k ⊗ (coeff g (i + 1 - k)))"
      by (subst coeff_mult[OF assms], simp)
    also have "... =
      (⨁k ∈ {..i+1}. ?n (i+1) ⊗ (coeff f k ⊗ coeff g (i + 1 - k)))"
      by (intro finsum_rdistr, simp_all add:Pi_def)
    also have "... =
      (⨁k ∈ {..i+1}. ?n k ⊗ (coeff f k ⊗ coeff g (i + 1 - k)) ⊕
      ?n (i+1-k) ⊗ (coeff f k ⊗ coeff g (i + 1 - k)))"
      using int_embed_add[symmetric] of_nat_diff
      by (intro finsum_cong')
        (simp_all add:l_distr[symmetric] of_nat_diff)
    also have "... =
      (⨁k ∈ {..i+1}. ?n k ⊗ coeff f k ⊗ coeff g (i + 1 - k) ⊕
      coeff f k ⊗ (?n (i+1-k) ⊗ coeff g (i + 1 - k)))"
      using Pi_def a b m_assoc m_comm
      by (intro finsum_cong' arg_cong2[where f="(⊕)"], simp_all)
    also have "... =
      (⨁k ∈ {..i+1}. ?n k ⊗ coeff f k ⊗ coeff g (i+1-k)) ⊕
      (⨁k ∈ {..i+1}. coeff f k ⊗ (?n (i+1-k) ⊗ coeff g (i+1-k)))"
      by (subst finsum_addf[symmetric], simp_all add:Pi_def)
    also have "... =
      (⨁k∈insert 0 {1..i+1}. ?n k ⊗ coeff f k ⊗ coeff g (i+1-k)) ⊕
      (⨁k∈insert (i+1) {..i}. coeff f k ⊗ (?n (i+1-k) ⊗ coeff g (i+1-k)))"
      using subringE(1)[OF assms(1)]
      by (intro arg_cong2[where f="(⊕)"] finsum_cong')
        (auto simp:set_eq_iff)
    also have "... =
      (⨁k ∈ {1..i+1}. ?n k ⊗ coeff f k ⊗ coeff g (i+1-k)) ⊕
      (⨁k ∈ {..i}. coeff f k ⊗ (?n (i+1-k) ⊗ coeff g (i+1-k)))"
      by (subst (1 2) finsum_insert, auto simp add:int_embed_zero)
    also have "... =
      (⨁k ∈ Suc ` {..i}. ?n k ⊗ coeff f (k) ⊗ coeff g (i+1-k)) ⊕
      (⨁k ∈ {..i}. coeff f k ⊗ (?n (i+1-k) ⊗ coeff g (i+1-k)))"
      by (intro arg_cong2[where f="(⊕)"] finsum_cong')
        (simp_all add:Pi_def atMost_atLeast0)
    also have "... =
      (⨁k ∈ {..i}. ?n (k+1) ⊗ coeff f (k+1) ⊗ coeff g (i-k)) ⊕
      (⨁k ∈ {..i}. coeff f k ⊗ (?n (i+1-k) ⊗ coeff g (i+1-k)))"
      by (subst finsum_reindex, auto)
    also have "... =
      (⨁k ∈ {..i}. coeff (pderiv f) k ⊗ coeff g (i-k)) ⊕
      (⨁k ∈ {..i}. coeff f k ⊗ coeff (pderiv g) (i-k))"
      using Suc_diff_le
      by (subst (1 2) pderiv_coeff[OF assms(1)])
        (auto intro!: finsum_cong')
    also have "... =
      coeff (pderiv f ⊗⇘K[X]⇙ g) i ⊕ coeff (f ⊗⇘K[X]⇙ pderiv g) i"
      using pderiv_carr[OF assms(1)]
      by (subst (1 2) coeff_mult[OF assms(1)], auto)
    also have "... = coeff ?rhs i"
      using pderiv_carr[OF assms(1)]
      by (subst coeff_add[OF assms(1)], auto)
    finally show ?thesis by simp
  qed
  hence "coeff ?lhs = coeff ?rhs" by auto
  thus "?lhs = ?rhs"
    using pderiv_carr[OF assms(1)]
    by (subst coeff_iff_polynomial_cond[where K="K"])
     (simp_all add:univ_poly_carrier)
qed
lemma pderiv_pow:
  assumes "n > (0 :: nat)"
  assumes "subring K R"
  assumes [simp]: "f ∈ carrier (K[X])"
  shows "pderiv (f [^]⇘K[X]⇙ n) =
    int_embed (K[X]) n ⊗⇘K[X]⇙ f [^]⇘K[X]⇙ (n-1) ⊗⇘K[X]⇙ pderiv f"
    (is "?lhs = ?rhs")
proof -
  interpret p: cring "(K[X])"
    using univ_poly_is_cring[OF assms(2)] by simp
  let ?n = "λn. int_embed (K[X]) n"
  have [simp]: "?n i ∈ carrier (K[X])" for i
    using p.int_embed_range[OF p.carrier_is_subring] by simp
  obtain m where n_def: "n = Suc m" using assms(1) lessE by blast
  have "pderiv (f [^]⇘K[X]⇙ (m+1)) =
    ?n (m+1) ⊗⇘K[X]⇙ f [^]⇘K[X]⇙ m ⊗⇘K[X]⇙ pderiv f"
  proof (induction m)
    case 0
    then show ?case
      using pderiv_carr[OF assms(2)] assms(3)
      using p.int_embed_one by simp
  next
    case (Suc m)
    have "pderiv (f [^]⇘K [X]⇙ (Suc m + 1)) =
      pderiv (f [^]⇘K [X]⇙ (m+1) ⊗⇘K[X]⇙ f) "
      by simp
    also have "... =
      pderiv (f [^]⇘K [X]⇙ (m+1)) ⊗⇘K[X]⇙ f ⊕⇘K[X]⇙
      f [^]⇘K [X]⇙ (m+1) ⊗⇘K[X]⇙ pderiv f"
      using assms(3) by (subst pderiv_mult[OF assms(2)], auto)
    also have "... =
      (?n (m+1) ⊗⇘K [X]⇙ f [^]⇘K [X]⇙ m ⊗⇘K [X]⇙ pderiv f) ⊗⇘K[X]⇙ f
      ⊕⇘K[X]⇙ f [^]⇘K [X]⇙ (m+1) ⊗⇘K[X]⇙ pderiv f"
      by (subst Suc(1), simp)
    also have
      "... = ?n (m+1) ⊗⇘K[X]⇙ (f [^]⇘K [X]⇙ (m+1) ⊗⇘K[X]⇙ pderiv f)
      ⊕⇘K[X]⇙ 𝟭⇘K [X]⇙ ⊗⇘K[X]⇙ (f [^]⇘K [X]⇙ (m+1) ⊗⇘K[X]⇙ pderiv f)"
      using assms(3) pderiv_carr[OF assms(2)]
      apply (intro arg_cong2[where f="(⊕⇘K[X]⇙)"])
      apply (simp add:p.m_assoc)
       apply (simp add:p.m_comm)
      by simp
    also have
      "... = (?n (m+1) ⊕⇘K[X]⇙ 𝟭⇘K [X]⇙) ⊗⇘K [X]⇙
      (f [^]⇘K [X]⇙ (m+1) ⊗⇘K [X]⇙ pderiv f)"
      using assms(3) pderiv_carr[OF assms(2)]
      by (subst p.l_distr[symmetric], simp_all)
    also have "... =
      (𝟭⇘K [X]⇙ ⊕⇘K[X]⇙ ?n (m+1)) ⊗⇘K [X]⇙
      (f [^]⇘K [X]⇙ (m+1) ⊗⇘K [X]⇙ pderiv f)"
      using assms(3) pderiv_carr[OF assms(2)]
      by (subst p.a_comm, simp_all)
    also have "... = ?n (1+ Suc m)
      ⊗⇘K [X]⇙ f [^]⇘K [X]⇙ (Suc m) ⊗⇘K [X]⇙ pderiv f"
      using assms(3) pderiv_carr[OF assms(2)] of_nat_add
      apply (subst (2) of_nat_add, subst p.int_embed_add)
      by (simp add:p.m_assoc p.int_embed_one)
    finally show ?case by simp
  qed
  thus "?thesis" using n_def by auto
qed
lemma pderiv_var_pow:
  assumes "n > (0::nat)"
  assumes "subring K R"
  shows "pderiv (X [^]⇘K[X]⇙ n) =
    int_embed (K[X]) n ⊗⇘K[X]⇙ X [^]⇘K[X]⇙ (n-1)"
proof -
  interpret p: cring "(K[X])"
    using univ_poly_is_cring[OF assms(2)] by simp
  have [simp]: "int_embed (K[X]) i ∈ carrier (K[X])" for i
    using p.int_embed_range[OF p.carrier_is_subring] by simp
  show ?thesis
    using var_closed[OF assms(2)]
    using pderiv_var[where K="K"] pderiv_carr[OF assms(2)]
    by (subst pderiv_pow[OF assms(1,2)], simp_all)
qed
lemma int_embed_consistent_with_poly_of_const:
  assumes "subring K R"
  shows "int_embed (K[X]) m = poly_of_const (int_embed R m)"
proof -
  define K' where "K' = R ⦇ carrier := K ⦈"
  interpret p: cring "(K[X])"
    using univ_poly_is_cring[OF assms] by simp
  interpret d: domain "K'"
    unfolding K'_def
    using assms(1) subdomainI' subdomain_is_domain by simp
  interpret h: ring_hom_ring  "K'" "K[X]" "poly_of_const"
    unfolding K'_def
    using canonical_embedding_ring_hom[OF assms(1)] by simp
  define n where "n=nat (abs m)"
  have a1: "int_embed (K[X]) (int n) = poly_of_const (int_embed K' n)"
  proof (induction n)
    case 0
    then show ?case by (simp add:d.int_embed_zero p.int_embed_zero)
  next
    case (Suc n)
    then show ?case
      using d.int_embed_closed d.int_embed_add d.int_embed_one
      by (simp add:p.int_embed_add p.int_embed_one)
  qed
  also have "... = poly_of_const (int_embed R n)"
    unfolding K'_def using int_embed_consistent[OF assms] by simp
  finally have a:
    "int_embed (K[X]) (int n) = poly_of_const (int_embed R (int n))"
    by simp
  have "int_embed (K[X]) (-(int n)) =
    poly_of_const (int_embed K' (- (int n)))"
    using d.int_embed_closed a1 by (simp add: p.int_embed_inv d.int_embed_inv)
  also have "... = poly_of_const (int_embed R (- (int n)))"
    unfolding K'_def using int_embed_consistent[OF assms] by simp
  finally have b:
    "int_embed (K[X]) (-int n) = poly_of_const (int_embed R (-int n))"
    by simp
  show ?thesis
    using a b n_def by (cases "m ≥ 0", simp, simp)
qed
end
end