[isabelle-dev] Polynomial theory

Brian Huffman brianh at cs.pdx.edu
Sun Jan 11 00:53:26 CET 2009


Hi all,

Recently I was inspired by Mark Janney's comments on the Isabelle  
users list to finish up the polynomial theory that started working on  
a while ago. Basically, it is a heavily-reworked version of Clemens  
Ballarin's theories from HOL/Abstract/poly in the distribution. It  
includes most of the same operations: coefficients, monomials, degree,  
scalar multiplication, and all the ring operations. It also includes  
definitions for div and mod, and is fully integrated with the HOL  
algebraic type class hierarchy.

If there are no objections, I would like to add this theory to the  
main Isabelle/HOL source directory. Since the main HOL image already  
includes a list-based formalization of polynomials (Univ_Poly.thy), I  
think that a formalization using a real type constructor would be an  
improvement; it probably wouldn't be too hard to adapt the other  
theories to use the new polynomial type instead.

The source file is attached (it will probably only work with the  
development snapshot).

- Brian

-------------- next part --------------
header {* Univariate Polynomials *}

theory Polynomial
imports Plain SetInterval
begin

subsection {* Definition of type @{text poly} *}

typedef (Poly) 'a poly = "{f::nat \<Rightarrow> 'a::zero. \<exists>n. \<forall>i>n. f i = 0}"
  morphisms coeff Abs_poly
  by auto

lemma expand_poly_eq: "p = q \<longleftrightarrow> (\<forall>n. coeff p n = coeff q n)"
by (simp add: coeff_inject [symmetric] expand_fun_eq)

lemma poly_ext: "(\<And>n. coeff p n = coeff q n) \<Longrightarrow> p = q"
by (simp add: expand_poly_eq)


subsection {* Degree of a polynomial *}

definition
  degree :: "'a::zero poly \<Rightarrow> nat" where
  "degree p = (LEAST n. \<forall>i>n. coeff p i = 0)"

lemma coeff_eq_0: "degree p < n \<Longrightarrow> coeff p n = 0"
proof -
  have "coeff p \<in> Poly"
    by (rule coeff)
  hence "\<exists>n. \<forall>i>n. coeff p i = 0"
    unfolding Poly_def by simp
  hence "\<forall>i>degree p. coeff p i = 0"
    unfolding degree_def by (rule LeastI_ex)
  moreover assume "degree p < n"
  ultimately show ?thesis by simp
qed

lemma le_degree: "coeff p n \<noteq> 0 \<Longrightarrow> n \<le> degree p"
  by (erule contrapos_np, rule coeff_eq_0, simp)

lemma degree_le: "\<forall>i>n. coeff p i = 0 \<Longrightarrow> degree p \<le> n"
  unfolding degree_def by (erule Least_le)

lemma less_degree_imp: "n < degree p \<Longrightarrow> \<exists>i>n. coeff p i \<noteq> 0"
  unfolding degree_def by (drule not_less_Least, simp)


subsection {* The zero polynomial *}

instantiation poly :: (zero) zero
begin

definition
  zero_poly_def: "0 = Abs_poly (\<lambda>n. 0)"

instance ..
end

lemma coeff_0 [simp]: "coeff 0 n = 0"
  unfolding zero_poly_def
  by (simp add: Abs_poly_inverse Poly_def)

lemma degree_0 [simp]: "degree 0 = 0"
  by (rule order_antisym [OF degree_le le0]) simp

lemma leading_coeff_neq_0:
  assumes "p \<noteq> 0" shows "coeff p (degree p) \<noteq> 0"
proof (cases "degree p")
  case 0
  from `p \<noteq> 0` have "\<exists>n. coeff p n \<noteq> 0"
    by (simp add: expand_poly_eq)
  then obtain n where "coeff p n \<noteq> 0" ..
  hence "n \<le> degree p" by (rule le_degree)
  with `coeff p n \<noteq> 0` and `degree p = 0`
  show "coeff p (degree p) \<noteq> 0" by simp
next
  case (Suc n)
  from `degree p = Suc n` have "n < degree p" by simp
  hence "\<exists>i>n. coeff p i \<noteq> 0" by (rule less_degree_imp)
  then obtain i where "n < i" and "coeff p i \<noteq> 0" by fast
  from `degree p = Suc n` and `n < i` have "degree p \<le> i" by simp
  also from `coeff p i \<noteq> 0` have "i \<le> degree p" by (rule le_degree)
  finally have "degree p = i" .
  with `coeff p i \<noteq> 0` show "coeff p (degree p) \<noteq> 0" by simp
qed

lemma leading_coeff_0_iff [simp]: "coeff p (degree p) = 0 \<longleftrightarrow> p = 0"
  by (cases "p = 0", simp, simp add: leading_coeff_neq_0)


subsection {* List-style constructor for polynomials *}

definition
  pCons :: "'a::zero \<Rightarrow> 'a poly \<Rightarrow> 'a poly"
where
  [code del]: "pCons a p = Abs_poly (nat_case a (coeff p))"

lemma Poly_nat_case: "f \<in> Poly \<Longrightarrow> nat_case a f \<in> Poly"
  unfolding Poly_def by (auto split: nat.split)

lemma coeff_pCons:
  "coeff (pCons a p) = nat_case a (coeff p)"
  unfolding pCons_def
  by (simp add: Abs_poly_inverse Poly_nat_case coeff)

lemma coeff_pCons_0 [simp]: "coeff (pCons a p) 0 = a"
  by (simp add: coeff_pCons)

lemma coeff_pCons_Suc [simp]: "coeff (pCons a p) (Suc n) = coeff p n"
  by (simp add: coeff_pCons)

lemma degree_pCons_le: "degree (pCons a p) \<le> Suc (degree p)"
by (rule degree_le, simp add: coeff_eq_0 coeff_pCons split: nat.split)

lemma degree_pCons_eq:
  "p \<noteq> 0 \<Longrightarrow> degree (pCons a p) = Suc (degree p)"
apply (rule order_antisym [OF degree_pCons_le])
apply (rule le_degree, simp)
done

lemma degree_pCons_0: "degree (pCons a 0) = 0"
apply (rule order_antisym [OF _ le0])
apply (rule degree_le, simp add: coeff_pCons split: nat.split)
done

lemma degree_pCons_eq_if:
  "degree (pCons a p) = (if p = 0 then 0 else Suc (degree p))"
apply (cases "p = 0", simp_all)
apply (rule order_antisym [OF _ le0])
apply (rule degree_le, simp add: coeff_pCons split: nat.split)
apply (rule order_antisym [OF degree_pCons_le])
apply (rule le_degree, simp)
done

lemma pCons_0_0 [simp]: "pCons 0 0 = 0"
by (rule poly_ext, simp add: coeff_pCons split: nat.split)

lemma pCons_eq_iff [simp]:
  "pCons a p = pCons b q \<longleftrightarrow> a = b \<and> p = q"
proof (safe)
  assume "pCons a p = pCons b q"
  then have "coeff (pCons a p) 0 = coeff (pCons b q) 0" by simp
  then show "a = b" by simp
next
  assume "pCons a p = pCons b q"
  then have "\<forall>n. coeff (pCons a p) (Suc n) =
                 coeff (pCons b q) (Suc n)" by simp
  then show "p = q" by (simp add: expand_poly_eq)
qed

lemma pCons_eq_0_iff [simp]: "pCons a p = 0 \<longleftrightarrow> a = 0 \<and> p = 0"
  using pCons_eq_iff [of a p 0 0] by simp

lemma Poly_Suc: "f \<in> Poly \<Longrightarrow> (\<lambda>n. f (Suc n)) \<in> Poly"
  unfolding Poly_def
  by (clarify, rule_tac x=n in exI, simp)

lemma pCons_cases [cases type: poly]:
  obtains (pCons) a q where "p = pCons a q"
proof
  show "p = pCons (coeff p 0) (Abs_poly (\<lambda>n. coeff p (Suc n)))"
    by (rule poly_ext)
       (simp add: Abs_poly_inverse Poly_Suc coeff coeff_pCons
             split: nat.split)
qed

lemma pCons_induct [case_names 0 pCons, induct type: poly]:
  assumes zero: "P 0"
  assumes pCons: "\<And>a p. P p \<Longrightarrow> P (pCons a p)"
  shows "P p"
proof (induct p rule: measure_induct_rule [where f=degree])
  case (less p)
  obtain a q where "p = pCons a q" by (rule pCons_cases)
  have "P q"
  proof (cases "q = 0")
    case True
    then show "P q" by (simp add: zero)
  next
    case False
    then have "degree (pCons a q) = Suc (degree q)"
      by (rule degree_pCons_eq)
    then have "degree q < degree p"
      using `p = pCons a q` by simp
    then show "P q"
      by (rule less.hyps)
  qed
  then have "P (pCons a q)"
    by (rule pCons)
  then show ?case
    using `p = pCons a q` by simp
qed


subsection {* Monomials *}

definition
  monom :: "'a \<Rightarrow> nat \<Rightarrow> 'a::zero poly" where
  "monom a m = Abs_poly (\<lambda>n. if m = n then a else 0)"

lemma coeff_monom [simp]: "coeff (monom a m) n = (if m=n then a else 0)"
  unfolding monom_def
  by (subst Abs_poly_inverse, auto simp add: Poly_def)

lemma monom_0: "monom a 0 = pCons a 0"
  by (rule poly_ext, simp add: coeff_pCons split: nat.split)

lemma monom_Suc: "monom a (Suc n) = pCons 0 (monom a n)"
  by (rule poly_ext, simp add: coeff_pCons split: nat.split)

lemma monom_eq_0 [simp]: "monom 0 n = 0"
  by (rule poly_ext) simp

lemma monom_eq_0_iff [simp]: "monom a n = 0 \<longleftrightarrow> a = 0"
  by (simp add: expand_poly_eq)

lemma monom_eq_iff [simp]: "monom a n = monom b n \<longleftrightarrow> a = b"
  by (simp add: expand_poly_eq)

lemma degree_monom_le: "degree (monom a n) \<le> n"
  by (rule degree_le, simp)

lemma degree_monom_eq: "a \<noteq> 0 \<Longrightarrow> degree (monom a n) = n"
  apply (rule order_antisym [OF degree_monom_le])
  apply (rule le_degree, simp)
  done


subsection {* Addition and subtraction *}

instantiation poly :: (comm_monoid_add) comm_monoid_add
begin

definition
  plus_poly_def [code del]:
    "p + q = Abs_poly (\<lambda>n. coeff p n + coeff q n)"

lemma Poly_add:
  fixes f g :: "nat \<Rightarrow> 'a"
  shows "\<lbrakk>f \<in> Poly; g \<in> Poly\<rbrakk> \<Longrightarrow> (\<lambda>n. f n + g n) \<in> Poly"
  unfolding Poly_def
  apply (clarify, rename_tac m n)
  apply (rule_tac x="max m n" in exI, simp)
  done

lemma coeff_add [simp]:
  "coeff (p + q) n = coeff p n + coeff q n"
  unfolding plus_poly_def
  by (simp add: Abs_poly_inverse coeff Poly_add)

instance proof
  fix p q r :: "'a poly"
  show "(p + q) + r = p + (q + r)"
    by (simp add: expand_poly_eq add_assoc)
  show "p + q = q + p"
    by (simp add: expand_poly_eq add_commute)
  show "0 + p = p"
    by (simp add: expand_poly_eq)
qed

end

instantiation poly :: (ab_group_add) ab_group_add
begin

definition
  uminus_poly_def [code del]:
    "- p = Abs_poly (\<lambda>n. - coeff p n)"

definition
  minus_poly_def [code del]:
    "p - q = Abs_poly (\<lambda>n. coeff p n - coeff q n)"

lemma Poly_minus:
  fixes f :: "nat \<Rightarrow> 'a"
  shows "f \<in> Poly \<Longrightarrow> (\<lambda>n. - f n) \<in> Poly"
  unfolding Poly_def by simp

lemma Poly_diff:
  fixes f g :: "nat \<Rightarrow> 'a"
  shows "\<lbrakk>f \<in> Poly; g \<in> Poly\<rbrakk> \<Longrightarrow> (\<lambda>n. f n - g n) \<in> Poly"
  unfolding diff_minus by (simp add: Poly_add Poly_minus)

lemma coeff_minus [simp]: "coeff (- p) n = - coeff p n"
  unfolding uminus_poly_def
  by (simp add: Abs_poly_inverse coeff Poly_minus)

lemma coeff_diff [simp]:
  "coeff (p - q) n = coeff p n - coeff q n"
  unfolding minus_poly_def
  by (simp add: Abs_poly_inverse coeff Poly_diff)

instance proof
  fix p q :: "'a poly"
  show "- p + p = 0"
    by (simp add: expand_poly_eq)
  show "p - q = p + - q"
    by (simp add: expand_poly_eq diff_minus)
qed

end

lemma add_pCons [simp]:
  "pCons a p + pCons b q = pCons (a + b) (p + q)"
  by (rule poly_ext, simp add: coeff_pCons split: nat.split)

lemma minus_pCons [simp]:
  "- pCons a p = pCons (- a) (- p)"
  by (rule poly_ext, simp add: coeff_pCons split: nat.split)

lemma diff_pCons [simp]:
  "pCons a p - pCons b q = pCons (a - b) (p - q)"
  by (rule poly_ext, simp add: coeff_pCons split: nat.split)

lemma degree_add_le: "degree (p + q) \<le> max (degree p) (degree q)"
  by (rule degree_le, auto simp add: coeff_eq_0)

lemma degree_add_eq_right:
  "degree p < degree q \<Longrightarrow> degree (p + q) = degree q"
  apply (cases "q = 0", simp)
  apply (rule order_antisym)
  apply (rule ord_le_eq_trans [OF degree_add_le])
  apply simp
  apply (rule le_degree)
  apply (simp add: coeff_eq_0)
  done

lemma degree_add_eq_left:
  "degree q < degree p \<Longrightarrow> degree (p + q) = degree p"
  using degree_add_eq_right [of q p]
  by (simp add: add_commute)

lemma degree_minus [simp]: "degree (- p) = degree p"
  unfolding degree_def by simp

lemma degree_diff_le: "degree (p - q) \<le> max (degree p) (degree q)"
  using degree_add_le [where p=p and q="-q"]
  by (simp add: diff_minus)

lemma add_monom: "monom a n + monom b n = monom (a + b) n"
  by (rule poly_ext) simp

lemma diff_monom: "monom a n - monom b n = monom (a - b) n"
  by (rule poly_ext) simp

lemma minus_monom: "- monom a n = monom (-a) n"
  by (rule poly_ext) simp

lemma coeff_setsum: "coeff (\<Sum>x\<in>A. p x) i = (\<Sum>x\<in>A. coeff (p x) i)"
  by (cases "finite A", induct set: finite, simp_all)

lemma monom_setsum: "monom (\<Sum>x\<in>A. a x) n = (\<Sum>x\<in>A. monom (a x) n)"
  by (rule poly_ext) (simp add: coeff_setsum)


subsection {* Multiplication by a constant *}

definition
  smult :: "'a::comm_semiring_0 \<Rightarrow> 'a poly \<Rightarrow> 'a poly" where
  "smult a p = Abs_poly (\<lambda>n. a * coeff p n)"

lemma Poly_smult:
  fixes f :: "nat \<Rightarrow> 'a::comm_semiring_0"
  shows "f \<in> Poly \<Longrightarrow> (\<lambda>n. a * f n) \<in> Poly"
  unfolding Poly_def
  by (clarify, rule_tac x=n in exI, simp)

lemma coeff_smult [simp]: "coeff (smult a p) n = a * coeff p n"
  unfolding smult_def
  by (simp add: Abs_poly_inverse Poly_smult coeff)

lemma degree_smult_le: "degree (smult a p) \<le> degree p"
  by (rule degree_le, simp add: coeff_eq_0)

lemma smult_smult: "smult a (smult b p) = smult (a * b) p"
  by (rule poly_ext, simp add: mult_assoc)

lemma smult_0_right [simp]: "smult a 0 = 0"
  by (rule poly_ext, simp)

lemma smult_0_left [simp]: "smult 0 p = 0"
  by (rule poly_ext, simp)

lemma smult_1_left [simp]: "smult (1::'a::comm_semiring_1) p = p"
  by (rule poly_ext, simp)

lemma smult_add_right:
  "smult a (p + q) = smult a p + smult a q"
  by (rule poly_ext, simp add: ring_simps)

lemma smult_add_left:
  "smult (a + b) p = smult a p + smult b p"
  by (rule poly_ext, simp add: ring_simps)

lemma smult_minus_right:
  "smult (a::'a::comm_ring) (- p) = - smult a p"
  by (rule poly_ext, simp)

lemma smult_minus_left:
  "smult (- a::'a::comm_ring) p = - smult a p"
  by (rule poly_ext, simp)

lemma smult_diff_right:
  "smult (a::'a::comm_ring) (p - q) = smult a p - smult a q"
  by (rule poly_ext, simp add: ring_simps)

lemma smult_diff_left:
  "smult (a - b::'a::comm_ring) p = smult a p - smult b p"
  by (rule poly_ext, simp add: ring_simps)

lemma smult_pCons [simp]:
  "smult a (pCons b p) = pCons (a * b) (smult a p)"
  by (rule poly_ext, simp add: coeff_pCons split: nat.split)

lemma smult_monom: "smult a (monom b n) = monom (a * b) n"
  by (induct n, simp add: monom_0, simp add: monom_Suc)


subsection {* Multiplication of polynomials *}

lemma Poly_mult_lemma:
  fixes f g :: "nat \<Rightarrow> 'a::comm_semiring_0" and m n :: nat
  assumes "\<forall>i>m. f i = 0"
  assumes "\<forall>j>n. g j = 0"
  shows "\<forall>k>m+n. (\<Sum>i\<le>k. f i * g (k-i)) = 0"
proof (clarify)
  fix k :: nat
  assume "m + n < k"
  show "(\<Sum>i\<le>k. f i * g (k - i)) = 0"
  proof (rule setsum_0' [rule_format])
    fix i :: nat
    assume "i \<in> {..k}" hence "i \<le> k" by simp
    with `m + n < k` have "m < i \<or> n < k - i" by arith
    thus "f i * g (k - i) = 0"
      using prems by auto
  qed
qed

lemma Poly_mult:
  fixes f g :: "nat \<Rightarrow> 'a::comm_semiring_0"
  shows "\<lbrakk>f \<in> Poly; g \<in> Poly\<rbrakk> \<Longrightarrow> (\<lambda>n. \<Sum>i\<le>n. f i * g (n-i)) \<in> Poly"
  unfolding Poly_def
  by (safe, rule exI, rule Poly_mult_lemma)

lemma poly_mult_assoc_lemma:
  fixes k :: nat and f :: "nat \<Rightarrow> nat \<Rightarrow> nat \<Rightarrow> 'a::comm_monoid_add"
  shows "(\<Sum>j\<le>k. \<Sum>i\<le>j. f i (j - i) (n - j)) =
         (\<Sum>j\<le>k. \<Sum>i\<le>k - j. f j i (n - j - i))"
proof (induct k)
  case 0 show ?case by simp
next
  case (Suc k) thus ?case
    by (simp add: Suc_diff_le setsum_addf add_assoc
             cong: strong_setsum_cong)
qed

lemma poly_mult_commute_lemma:
  fixes n :: nat and f :: "nat \<Rightarrow> nat \<Rightarrow> 'a::comm_monoid_add"
  shows "(\<Sum>i\<le>n. f i (n - i)) = (\<Sum>i\<le>n. f (n - i) i)"
proof (rule setsum_reindex_cong)
  show "inj_on (\<lambda>i. n - i) {..n}"
    by (rule inj_onI) simp
  show "{..n} = (\<lambda>i. n - i) ` {..n}"
    by (auto, rule_tac x="n - x" in image_eqI, simp_all)
next
  fix i assume "i \<in> {..n}"
  hence "n - (n - i) = i" by simp
  thus "f (n - i) i = f (n - i) (n - (n - i))" by simp
qed

text {* TODO: move to appropriate theory *}
lemma setsum_atMost_Suc_shift:
  fixes f :: "nat \<Rightarrow> 'a::comm_monoid_add"
  shows "(\<Sum>i\<le>Suc n. f i) = f 0 + (\<Sum>i\<le>n. f (Suc i))"
proof (induct n)
  case 0 show ?case by simp
next
  case (Suc n) note IH = this
  have "(\<Sum>i\<le>Suc (Suc n). f i) = (\<Sum>i\<le>Suc n. f i) + f (Suc (Suc n))"
    by (rule setsum_atMost_Suc)
  also have "(\<Sum>i\<le>Suc n. f i) = f 0 + (\<Sum>i\<le>n. f (Suc i))"
    by (rule IH)
  also have "f 0 + (\<Sum>i\<le>n. f (Suc i)) + f (Suc (Suc n)) =
             f 0 + ((\<Sum>i\<le>n. f (Suc i)) + f (Suc (Suc n)))"
    by (rule add_assoc)
  also have "(\<Sum>i\<le>n. f (Suc i)) + f (Suc (Suc n)) = (\<Sum>i\<le>Suc n. f (Suc i))"
    by (rule setsum_atMost_Suc [symmetric])
  finally show ?case .
qed

instantiation poly :: (comm_semiring_0) comm_semiring_0
begin

definition
  times_poly_def:
    "p * q = Abs_poly (\<lambda>n. \<Sum>i\<le>n. coeff p i * coeff q (n-i))"

lemma coeff_mult:
  "coeff (p * q) n = (\<Sum>i\<le>n. coeff p i * coeff q (n-i))"
  unfolding times_poly_def
  by (simp add: Abs_poly_inverse coeff Poly_mult)

instance proof
  fix p q r :: "'a poly"
  show 0: "0 * p = 0"
    by (simp add: expand_poly_eq coeff_mult)
  show "p * 0 = 0"
    by (simp add: expand_poly_eq coeff_mult)
  show "(p + q) * r = p * r + q * r"
    by (simp add: expand_poly_eq coeff_mult left_distrib setsum_addf)
  show "(p * q) * r = p * (q * r)"
  proof (rule poly_ext)
    fix n :: nat
    have "(\<Sum>j\<le>n. \<Sum>i\<le>j. coeff p i * coeff q (j - i) * coeff r (n - j)) =
          (\<Sum>j\<le>n. \<Sum>i\<le>n - j. coeff p j * coeff q i * coeff r (n - j - i))"
      by (rule poly_mult_assoc_lemma)
    thus "coeff ((p * q) * r) n = coeff (p * (q * r)) n"
      by (simp add: coeff_mult setsum_right_distrib
                    setsum_left_distrib mult_assoc)
  qed
  show "p * q = q * p"
  proof (rule poly_ext)
    fix n :: nat
    have "(\<Sum>i\<le>n. coeff p i * coeff q (n - i)) =
          (\<Sum>i\<le>n. coeff p (n - i) * coeff q i)"
      by (rule poly_mult_commute_lemma)
    thus "coeff (p * q) n = coeff (q * p) n"
      by (simp add: coeff_mult mult_commute)
  qed
qed

end

lemma degree_mult_le: "degree (p * q) \<le> degree p + degree q"
apply (rule degree_le, simp add: coeff_mult)
apply (rule Poly_mult_lemma)
apply (simp_all add: coeff_eq_0)
done

lemma mult_pCons_left [simp]:
  "pCons a p * q = smult a q + pCons 0 (p * q)"
apply (rule poly_ext)
apply (case_tac n)
apply (simp add: coeff_mult)
apply (simp add: coeff_mult setsum_atMost_Suc_shift
            del: setsum_atMost_Suc)
done

lemma mult_pCons_right [simp]:
  "p * pCons a q = smult a p + pCons 0 (p * q)"
  using mult_pCons_left [of a q p] by (simp add: mult_commute)

lemma mult_smult_left: "smult a p * q = smult a (p * q)"
  by (induct p, simp, simp add: smult_add_right smult_smult)

lemma mult_smult_right: "p * smult a q = smult a (p * q)"
  using mult_smult_left [of a q p] by (simp add: mult_commute)

lemma mult_monom: "monom a m * monom b n = monom (a * b) (m + n)"
  by (induct m, simp add: monom_0 smult_monom, simp add: monom_Suc)


subsection {* The unit polynomial and exponentiation *}

instantiation poly :: (comm_semiring_1) comm_semiring_1
begin

definition
  one_poly_def:
    "1 = pCons 1 0"

instance proof
  fix p :: "'a poly" show "1 * p = p"
    unfolding one_poly_def
    by simp
next
  show "0 \<noteq> (1::'a poly)"
    unfolding one_poly_def by simp
qed

end

lemma coeff_1 [simp]: "coeff 1 n = (if n = 0 then 1 else 0)"
  unfolding one_poly_def
  by (simp add: coeff_pCons split: nat.split)

lemma degree_1 [simp]: "degree 1 = 0"
  unfolding one_poly_def
  by (rule degree_pCons_0)

instantiation poly :: (comm_semiring_1) recpower
begin

primrec power_poly where
  power_poly_0: "(p::'a poly) ^ 0 = 1"
| power_poly_Suc: "(p::'a poly) ^ (Suc n) = p * p ^ n"

instance
  by default simp_all

end

instance poly :: (comm_ring) comm_ring ..

instance poly :: (comm_ring_1) comm_ring_1 ..

instantiation poly :: (comm_ring_1) number_ring
begin

definition
  "number_of k = (of_int k :: 'a poly)"

instance
  by default (rule number_of_poly_def)

end


subsection {* Polynomials form an integral domain *}

lemma coeff_mult_degree_sum:
  "coeff (p * q) (degree p + degree q) =
   coeff p (degree p) * coeff q (degree q)"
 apply (simp add: coeff_mult)
 apply (subst setsum_diff1' [where a="degree p"])
   apply simp
  apply simp
 apply (subst setsum_0' [rule_format])
  apply clarsimp
  apply (subgoal_tac "degree p < a \<or> degree q < degree p + degree q - a")
   apply (force simp add: coeff_eq_0)
  apply arith
 apply simp
done

instance poly :: (idom) idom
proof
  fix p q :: "'a poly"
  assume "p \<noteq> 0" and "q \<noteq> 0"
  have "coeff (p * q) (degree p + degree q) =
        coeff p (degree p) * coeff q (degree q)"
    by (rule coeff_mult_degree_sum)
  also have "coeff p (degree p) * coeff q (degree q) \<noteq> 0"
    using `p \<noteq> 0` and `q \<noteq> 0` by simp
  finally have "\<exists>n. coeff (p * q) n \<noteq> 0" ..
  thus "p * q \<noteq> 0" by (simp add: expand_poly_eq)
qed

lemma degree_mult_eq:
  fixes p q :: "'a::idom poly"
  shows "\<lbrakk>p \<noteq> 0; q \<noteq> 0\<rbrakk> \<Longrightarrow> degree (p * q) = degree p + degree q"
apply (rule order_antisym [OF degree_mult_le le_degree])
apply (simp add: coeff_mult_degree_sum)
done

lemma dvd_imp_degree_le:
  fixes p q :: "'a::idom poly"
  shows "\<lbrakk>p dvd q; q \<noteq> 0\<rbrakk> \<Longrightarrow> degree p \<le> degree q"
  by (erule dvdE, simp add: degree_mult_eq)


subsection {* Polynomials form a Euclidean domain *}

definition
  divmod_poly_rel :: "'a::field poly \<Rightarrow> 'a poly \<Rightarrow> 'a poly \<Rightarrow> 'a poly \<Rightarrow> bool"
where
  "divmod_poly_rel x y q r \<longleftrightarrow>
    x = q * y + r \<and> (if y = 0 then q = 0 else r = 0 \<or> degree r < degree y)"

lemma divmod_poly_rel_0:
  "divmod_poly_rel 0 y 0 0"
  unfolding divmod_poly_rel_def by simp

lemma divmod_poly_rel_by_0:
  "divmod_poly_rel x 0 0 x"
  unfolding divmod_poly_rel_def by simp

lemma eq_zero_or_degree_less:
  assumes "degree p \<le> n" and "coeff p n = 0"
  shows "p = 0 \<or> degree p < n"
proof (cases n)
  case 0
  with `degree p \<le> n` and `coeff p n = 0`
  have "coeff p (degree p) = 0" by simp
  then have "p = 0" by simp
  then show ?thesis ..
next
  case (Suc m)
  have "\<forall>i>n. coeff p i = 0"
    using `degree p \<le> n` by (simp add: coeff_eq_0)
  then have "\<forall>i\<ge>n. coeff p i = 0"
    using `coeff p n = 0` by (simp add: le_less)
  then have "\<forall>i>m. coeff p i = 0"
    using `n = Suc m` by (simp add: less_eq_Suc_le)
  then have "degree p \<le> m"
    by (rule degree_le)
  then have "degree p < n"
    using `n = Suc m` by (simp add: less_Suc_eq_le)
  then show ?thesis ..
qed

lemma divmod_poly_rel_pCons:
  assumes rel: "divmod_poly_rel x y q r"
  assumes y: "y \<noteq> 0"
  assumes b: "b = coeff (pCons a r) (degree y) / coeff y (degree y)"
  shows "divmod_poly_rel (pCons a x) y (pCons b q) (pCons a r - smult b y)"
    (is "divmod_poly_rel ?x y ?q ?r")
proof -
  have x: "x = q * y + r" and r: "r = 0 \<or> degree r < degree y"
    using assms unfolding divmod_poly_rel_def by simp_all

  have 1: "?x = ?q * y + ?r"
    using b x by simp

  have 2: "?r = 0 \<or> degree ?r < degree y"
  proof (rule eq_zero_or_degree_less)
    have "degree ?r \<le> max (degree (pCons a r)) (degree (smult b y))"
      by (rule degree_diff_le)
    also have "\<dots> \<le> degree y"
    proof (rule min_max.le_supI)
      show "degree (pCons a r) \<le> degree y"
        using r by (auto simp add: degree_pCons_eq_if)
      show "degree (smult b y) \<le> degree y"
        by (rule degree_smult_le)
    qed
    finally show "degree ?r \<le> degree y" .
  next
    show "coeff ?r (degree y) = 0"
      using `y \<noteq> 0` unfolding b by simp
  qed

  from 1 2 show ?thesis
    unfolding divmod_poly_rel_def
    using `y \<noteq> 0` by simp
qed

lemma divmod_poly_rel_exists: "\<exists>q r. divmod_poly_rel x y q r"
apply (cases "y = 0")
apply (fast intro!: divmod_poly_rel_by_0)
apply (induct x)
apply (fast intro!: divmod_poly_rel_0)
apply (fast intro!: divmod_poly_rel_pCons)
done

lemma divmod_poly_rel_unique:
  assumes 1: "divmod_poly_rel x y q1 r1"
  assumes 2: "divmod_poly_rel x y q2 r2"
  shows "q1 = q2 \<and> r1 = r2"
proof (cases "y = 0")
  assume "y = 0" with assms show ?thesis
    by (simp add: divmod_poly_rel_def)
next
  assume [simp]: "y \<noteq> 0"
  from 1 have q1: "x = q1 * y + r1" and r1: "r1 = 0 \<or> degree r1 < degree y"
    unfolding divmod_poly_rel_def by simp_all
  from 2 have q2: "x = q2 * y + r2" and r2: "r2 = 0 \<or> degree r2 < degree y"
    unfolding divmod_poly_rel_def by simp_all
  from q1 q2 have q3: "(q1 - q2) * y = r2 - r1"
    by (simp add: ring_simps)
  from r1 r2 have r3: "(r2 - r1) = 0 \<or> degree (r2 - r1) < degree y"
    by (auto intro: le_less_trans [OF degree_diff_le])

  show "q1 = q2 \<and> r1 = r2"
  proof (rule ccontr)
    assume "\<not> (q1 = q2 \<and> r1 = r2)"
    with q3 have "q1 \<noteq> q2" and "r1 \<noteq> r2" by auto
    with r3 have "degree (r2 - r1) < degree y" by simp
    also have "degree y \<le> degree (q1 - q2) + degree y" by simp
    also have "\<dots> = degree ((q1 - q2) * y)"
      using `q1 \<noteq> q2` by (simp add: degree_mult_eq)
    also have "\<dots> = degree (r2 - r1)"
      using q3 by simp
    finally have "degree (r2 - r1) < degree (r2 - r1)" .
    then show "False" by simp
  qed
qed

lemmas divmod_poly_rel_unique_div =
  divmod_poly_rel_unique [THEN conjunct1, standard]

lemmas divmod_poly_rel_unique_mod =
  divmod_poly_rel_unique [THEN conjunct2, standard]

instantiation poly :: (field) ring_div
begin

definition div_poly where
  [code del]: "x div y = (THE q. \<exists>r. divmod_poly_rel x y q r)"

definition mod_poly where
  [code del]: "x mod y = (THE r. \<exists>q. divmod_poly_rel x y q r)"

lemma div_poly_eq:
  "divmod_poly_rel x y q r \<Longrightarrow> x div y = q"
unfolding div_poly_def
by (fast elim: divmod_poly_rel_unique_div)

lemma mod_poly_eq:
  "divmod_poly_rel x y q r \<Longrightarrow> x mod y = r"
unfolding mod_poly_def
by (fast elim: divmod_poly_rel_unique_mod)

lemma divmod_poly_rel:
  "divmod_poly_rel x y (x div y) (x mod y)"
proof -
  from divmod_poly_rel_exists
    obtain q r where "divmod_poly_rel x y q r" by fast
  thus ?thesis
    by (simp add: div_poly_eq mod_poly_eq)
qed

instance proof
  fix x y :: "'a poly"
  show "x div y * y + x mod y = x"
    using divmod_poly_rel [of x y]
    by (simp add: divmod_poly_rel_def)
next
  fix x :: "'a poly"
  have "divmod_poly_rel x 0 0 x"
    by (rule divmod_poly_rel_by_0)
  thus "x div 0 = 0"
    by (rule div_poly_eq)
next
  fix y :: "'a poly"
  have "divmod_poly_rel 0 y 0 0"
    by (rule divmod_poly_rel_0)
  thus "0 div y = 0"
    by (rule div_poly_eq)
next
  fix x y z :: "'a poly"
  assume "y \<noteq> 0"
  hence "divmod_poly_rel (x + z * y) y (z + x div y) (x mod y)"
    using divmod_poly_rel [of x y]
    by (simp add: divmod_poly_rel_def left_distrib)
  thus "(x + z * y) div y = z + x div y"
    by (rule div_poly_eq)
qed

end

lemma degree_mod_less:
  "y \<noteq> 0 \<Longrightarrow> x mod y = 0 \<or> degree (x mod y) < degree y"
  using divmod_poly_rel [of x y]
  unfolding divmod_poly_rel_def by simp

lemma div_poly_less: "degree x < degree y \<Longrightarrow> x div y = 0"
proof -
  assume "degree x < degree y"
  hence "divmod_poly_rel x y 0 x"
    by (simp add: divmod_poly_rel_def)
  thus "x div y = 0" by (rule div_poly_eq)
qed

lemma mod_poly_less: "degree x < degree y \<Longrightarrow> x mod y = x"
proof -
  assume "degree x < degree y"
  hence "divmod_poly_rel x y 0 x"
    by (simp add: divmod_poly_rel_def)
  thus "x mod y = x" by (rule mod_poly_eq)
qed

lemma mod_pCons:
  fixes a and x
  assumes y: "y \<noteq> 0"
  defines b: "b \<equiv> coeff (pCons a (x mod y)) (degree y) / coeff y (degree y)"
  shows "(pCons a x) mod y = (pCons a (x mod y) - smult b y)"
unfolding b
apply (rule mod_poly_eq)
apply (rule divmod_poly_rel_pCons [OF divmod_poly_rel y refl])
done


subsection {* Evaluation of polynomials *}

definition
  poly :: "'a::{comm_semiring_1,recpower} poly \<Rightarrow> 'a \<Rightarrow> 'a" where
  "poly p = (\<lambda>x. \<Sum>n\<le>degree p. coeff p n * x ^ n)"

lemma poly_0 [simp]: "poly 0 x = 0"
  unfolding poly_def by simp

lemma poly_pCons [simp]: "poly (pCons a p) x = a + x * poly p x"
  unfolding poly_def
  by (simp add: degree_pCons_eq_if setsum_atMost_Suc_shift power_Suc
                setsum_left_distrib setsum_right_distrib mult_ac
           del: setsum_atMost_Suc)

lemma poly_1 [simp]: "poly 1 x = 1"
  unfolding one_poly_def by simp

lemma poly_monom: "poly (monom a n) x = a * x ^ n"
  by (induct n, simp add: monom_0, simp add: monom_Suc power_Suc mult_ac)

lemma poly_add [simp]: "poly (p + q) x = poly p x + poly q x"
  apply (induct p arbitrary: q, simp)
  apply (case_tac q, simp, simp add: ring_simps)
  done

lemma poly_minus [simp]:
  fixes x :: "'a::{comm_ring_1,recpower}"
  shows "poly (- p) x = - poly p x"
  by (induct p, simp_all)

lemma poly_diff [simp]:
  fixes x :: "'a::{comm_ring_1,recpower}"
  shows "poly (p - q) x = poly p x - poly q x"
  by (simp add: diff_minus)

lemma poly_setsum: "poly (\<Sum>k\<in>A. p k) x = (\<Sum>k\<in>A. poly (p k) x)"
  by (cases "finite A", induct set: finite, simp_all)

lemma poly_smult [simp]: "poly (smult a p) x = a * poly p x"
  by (induct p, simp, simp add: ring_simps)

lemma poly_mult [simp]: "poly (p * q) x = poly p x * poly q x"
  by (induct p, simp_all, simp add: ring_simps)

end


More information about the isabelle-dev mailing list