Theory Polynomials.Power_Products

(* Author: Fabian Immler, Alexander Maletzky *)

section ‹Abstract Power-Products›

theory Power_Products
  imports Complex_Main
  "HOL-Library.Function_Algebras"
  "HOL-Library.Countable"
  "More_MPoly_Type"
  "Utils"
  Well_Quasi_Orders.Well_Quasi_Orders
begin

text ‹This theory formalizes the concept of "power-products". A power-product can be thought of as
  the product of some indeterminates, such as $x$, $x^2\,y$, $x\,y^3\,z^7$, etc., without any
  scalar coefficient.

The approach in this theory is to capture the notion of "power-product" (also called "monomial") as
type class. A canonical instance for power-product is the type @{typ "'var 0 nat"}, which is
interpreted as mapping from variables in the power-product to exponents.

A slightly unintuitive (but fitting better with the standard type class instantiations of
@{typ "'a 0 'b"}) approach is to write addition to denote "multiplication" of power products.
For example, $x^2y$ would be represented as a function p = (X ↦ 2, Y ↦ 1)›, $xz$ as a
function q = (X ↦ 1, Z ↦ 1)›. With the (pointwise) instantiation of addition of @{typ "'a 0 'b"},
we will write p + q = (X ↦ 3, Y ↦ 1, Z ↦ 1)› for the product $x^2y \cdot xz = x^3yz$
›

subsection ‹Constant @{term Keys}

text ‹Legacy:›
lemmas keys_eq_empty_iff = keys_eq_empty

definition Keys :: "('a 0 'b::zero) set  'a set"
  where "Keys F = (keys ` F)"

lemma in_Keys: "s  Keys F  (fF. s  keys f)"
  unfolding Keys_def by simp

lemma in_KeysI:
  assumes "s  keys f" and "f  F"
  shows "s  Keys F"
  unfolding in_Keys using assms ..

lemma in_KeysE:
  assumes "s  Keys F"
  obtains f where "s  keys f" and "f  F"
  using assms unfolding in_Keys ..

lemma Keys_mono:
  assumes "A  B"
  shows "Keys A  Keys B"
  using assms by (auto simp add: Keys_def)

lemma Keys_insert: "Keys (insert a A) = keys a  Keys A"
  by (simp add: Keys_def)

lemma Keys_Un: "Keys (A  B) = Keys A  Keys B"
  by (simp add: Keys_def)

lemma finite_Keys:
  assumes "finite A"
  shows "finite (Keys A)"
  unfolding Keys_def by (rule, fact assms, rule finite_keys)

lemma Keys_not_empty:
  assumes "a  A" and "a  0"
  shows "Keys A  {}"
proof
  assume "Keys A = {}"
  from a  0 have "keys a  {}" using aux by fastforce
  then obtain s where "s  keys a" by blast
  from this assms(1) have "s  Keys A" by (rule in_KeysI)
  with Keys A = {} show False by simp
qed

lemma Keys_empty [simp]: "Keys {} = {}"
  by (simp add: Keys_def)

lemma Keys_zero [simp]: "Keys {0} = {}"
  by (simp add: Keys_def)

lemma keys_subset_Keys:
  assumes "f  F"
  shows "keys f  Keys F"
  using in_KeysI[OF _ assms] by auto

lemma Keys_minus: "Keys (A - B)  Keys A"
  by (auto simp add: Keys_def)

lemma Keys_minus_zero: "Keys (A - {0}) = Keys A"
proof (cases "0  A")
  case True
  hence "(A - {0})  {0} = A" by auto
  hence "Keys A = Keys ((A - {0})  {0})" by simp
  also have "... = Keys (A - {0})  Keys {0::('a 0 'b)}" by (fact Keys_Un)
  also have "... = Keys (A - {0})" by simp
  finally show ?thesis by simp
next
  case False
  hence "A - {0} = A" by simp
  thus ?thesis by simp
qed

subsection ‹Constant @{term except}

definition except_fun :: "('a  'b)  'a set  ('a  'b::zero)"
  where "except_fun f S = (λx. (f x when x  S))"

lift_definition except :: "('a 0 'b)  'a set  ('a 0 'b::zero)" is except_fun
proof -
  fix p::"'a  'b" and S::"'a set"
  assume "finite {t. p t  0}"
  show "finite {t. except_fun p S t  0}"
  proof (rule finite_subset[of _ "{t. p t  0}"], rule)
    fix u
    assume "u  {t. except_fun p S t  0}"
    hence "p u  0" by (simp add: except_fun_def)
    thus "u  {t. p t  0}" by simp
  qed fact
qed

lemma lookup_except_when: "lookup (except p S) = (λt. lookup p t when t  S)"
  by (auto simp: except.rep_eq except_fun_def)

lemma lookup_except: "lookup (except p S) = (λt. if t  S then 0 else lookup p t)"
  by (rule ext) (simp add: lookup_except_when)

lemma lookup_except_singleton: "lookup (except p {t}) t = 0"
  by (simp add: lookup_except)

lemma except_zero [simp]: "except 0 S = 0"
  by (rule poly_mapping_eqI) (simp add: lookup_except)

lemma lookup_except_eq_idI:
  assumes "t  S"
  shows "lookup (except p S) t = lookup p t"
  using assms by (simp add: lookup_except)

lemma lookup_except_eq_zeroI:
  assumes "t  S"
  shows "lookup (except p S) t = 0"
  using assms by (simp add: lookup_except)

lemma except_empty [simp]: "except p {} = p"
  by (rule poly_mapping_eqI) (simp add: lookup_except)

lemma except_eq_zeroI:
  assumes "keys p  S"
  shows "except p S = 0"
proof (rule poly_mapping_eqI, simp)
  fix t
  show "lookup (except p S) t = 0"
  proof (cases "t  S")
    case True
    thus ?thesis by (rule lookup_except_eq_zeroI)
  next
    case False then show ?thesis
      by (metis assms in_keys_iff lookup_except_eq_idI subset_eq) 
  qed
qed

lemma except_eq_zeroE:
  assumes "except p S = 0"
  shows "keys p  S"
  by (metis assms aux in_keys_iff lookup_except_eq_idI subset_iff)

lemma except_eq_zero_iff: "except p S = 0  keys p  S"
  by (rule, elim except_eq_zeroE, elim except_eq_zeroI)

lemma except_keys [simp]: "except p (keys p) = 0"
  by (rule except_eq_zeroI, rule subset_refl)

lemma plus_except: "p = Poly_Mapping.single t (lookup p t) + except p {t}"
  by (rule poly_mapping_eqI, simp add: lookup_add lookup_single lookup_except when_def split: if_split)

lemma keys_except: "keys (except p S) = keys p - S"
  by (transfer, auto simp: except_fun_def)

lemma except_single: "except (Poly_Mapping.single u c) S = (Poly_Mapping.single u c when u  S)"
  by (rule poly_mapping_eqI) (simp add: lookup_except lookup_single when_def)

lemma except_plus: "except (p + q) S = except p S + except q S"
  by (rule poly_mapping_eqI) (simp add: lookup_except lookup_add)

lemma except_minus: "except (p - q) S = except p S - except q S"
  by (rule poly_mapping_eqI) (simp add: lookup_except lookup_minus)

lemma except_uminus: "except (- p) S = - except p S"
  by (rule poly_mapping_eqI) (simp add: lookup_except)

lemma except_except: "except (except p S) T = except p (S  T)"
  by (rule poly_mapping_eqI) (simp add: lookup_except)

lemma poly_mapping_keys_eqI:
  assumes a1: "keys p = keys q" and a2: "t. t  keys p  lookup p t = lookup q t"
  shows "p = q"
proof (rule poly_mapping_eqI)
  fix t
  show "lookup p t = lookup q t"
  proof (cases "t  keys p")
    case True
    thus ?thesis by (rule a2)
  next
    case False
    moreover from this have "t  keys q" unfolding a1 .
    ultimately have "lookup p t = 0" and "lookup q t = 0" unfolding in_keys_iff by simp_all
    thus ?thesis by simp
  qed
qed

lemma except_id_iff: "except p S = p  keys p  S = {}"
  by (metis Diff_Diff_Int Diff_eq_empty_iff Diff_triv inf_le2 keys_except lookup_except_eq_idI
      lookup_except_eq_zeroI not_in_keys_iff_lookup_eq_zero poly_mapping_keys_eqI)

lemma keys_subset_wf:
  "wfP (λp q::('a, 'b::zero) poly_mapping. keys p  keys q)"
unfolding wfp_def
proof (intro wfI_min)
  fix x::"('a, 'b) poly_mapping" and Q
  assume x_in: "x  Q"
  let ?Q0 = "card ` keys ` Q"
  from x_in have "card (keys x)  ?Q0" by simp
  from wfE_min[OF wf this] obtain z0
    where z0_in: "z0  ?Q0" and z0_min: "y. (y, z0)  {(x, y). x < y}  y  ?Q0" by auto
  from z0_in obtain z where z0_def: "z0 = card (keys z)" and "z  Q" by auto
  show "zQ. y. (y, z)  {(p, q). keys p  keys q}  y  Q"
  proof (intro bexI[of _ z], rule, rule)
    fix y::"('a, 'b) poly_mapping"
    let ?y0 = "card (keys y)"
    assume "(y, z)  {(p, q). keys p  keys q}"
    hence "keys y  keys z" by simp
    hence "?y0 < z0" unfolding z0_def by (simp add: psubset_card_mono) 
    hence "(?y0, z0)  {(x, y). x < y}" by simp
    from z0_min[OF this] show "y  Q" by auto
  qed (fact)
qed

lemma poly_mapping_except_induct:
  assumes base: "P 0" and ind: "p t. p  0  t  keys p  P (except p {t})  P p"
  shows "P p"
proof (induct rule: wfp_induct[OF keys_subset_wf])
  fix p::"('a, 'b) poly_mapping"
  assume "q. keys q  keys p  P q"
  hence IH: "q. keys q  keys p  P q" by simp
  show "P p"
  proof (cases "p = 0")
    case True
    thus ?thesis using base by simp
  next
    case False
    hence "keys p  {}" by simp
    then obtain t where "t  keys p" by blast
    show ?thesis
    proof (rule ind, fact, fact, rule IH, simp only: keys_except, rule, rule Diff_subset, rule)
      assume "keys p - {t} = keys p"
      hence "t  keys p" by blast
      from this t  keys p show False ..
    qed
  qed
qed

lemma poly_mapping_except_induct':
  assumes "p. (t. t  keys p  P (except p {t}))  P p"
  shows "P p"
proof (induct "card (keys p)" arbitrary: p)
  case 0
  with finite_keys[of p] have "keys p = {}" by simp
  show ?case by (rule assms, simp add: keys p = {})
next
  case step: (Suc n)
  show ?case
  proof (rule assms)
    fix t
    assume "t  keys p"
    show "P (except p {t})"
    proof (rule step(1), simp add: keys_except)
      from step(2) t  keys p finite_keys[of p] show "n = card (keys p - {t})" by simp
    qed
  qed
qed

lemma poly_mapping_plus_induct:
  assumes "P 0" and "p c t. c  0  t  keys p  P p  P (Poly_Mapping.single t c + p)"
  shows "P p"
proof (induct "card (keys p)" arbitrary: p)
  case 0
  with finite_keys[of p] have "keys p = {}" by simp
  hence "p = 0" by simp
  with assms(1) show ?case by simp
next
  case step: (Suc n)
  from step(2) obtain t where t: "t  keys p" by (metis card_eq_SucD insert_iff)
  define c where "c = lookup p t"
  define q where "q = except p {t}"
  have *: "p = Poly_Mapping.single t c + q"
    by (rule poly_mapping_eqI, simp add: lookup_add lookup_single Poly_Mapping.when_def, intro conjI impI,
        simp add: q_def lookup_except c_def, simp add: q_def lookup_except_eq_idI)
  show ?case
  proof (simp only: *, rule assms(2))
    from t show "c  0"
      using c_def by auto
  next
    show "t  keys q" by (simp add: q_def keys_except)
  next
    show "P q"
    proof (rule step(1))
      from step(2) t  keys p show "n = card (keys q)" unfolding q_def keys_except
        by (metis Suc_inject card.remove finite_keys)
    qed
  qed
qed

lemma except_Diff_singleton: "except p (keys p - {t}) = Poly_Mapping.single t (lookup p t)"
  by (rule poly_mapping_eqI) (simp add: lookup_single in_keys_iff lookup_except when_def)

lemma except_Un_plus_Int: "except p (U  V) + except p (U  V) = except p U + except p V"
  by (rule poly_mapping_eqI) (simp add: lookup_except lookup_add)

corollary except_Int:
  assumes "keys p  U  V"
  shows "except p (U  V) = except p U + except p V"
proof -
  from assms have "except p (U  V) = 0" by (rule except_eq_zeroI)
  hence "except p (U  V) = except p (U  V) + except p (U  V)" by simp
  also have " = except p U + except p V" by (fact except_Un_plus_Int)
  finally show ?thesis .
qed

lemma except_keys_Int [simp]: "except p (keys p  U) = except p U"
  by (rule poly_mapping_eqI) (simp add: in_keys_iff lookup_except)

lemma except_Int_keys [simp]: "except p (U  keys p) = except p U"
  by (simp only: Int_commute[of U] except_keys_Int)

lemma except_keys_Diff: "except p (keys p - U) = except p (- U)"
proof -
  have "except p (keys p - U) = except p (keys p  (- U))" by (simp only: Diff_eq)
  also have " = except p (- U)" by simp
  finally show ?thesis .
qed

lemma except_decomp: "p = except p U + except p (- U)"
  by (rule poly_mapping_eqI) (simp add: lookup_except lookup_add)

corollary except_Compl: "except p (- U) = p - except p U"
  by (metis add_diff_cancel_left' except_decomp)

subsection ‹'Divisibility' on Additive Structures›

context plus begin

definition adds :: "'a  'a  bool" (infix "adds" 50)
  where "b adds a  (k. a = b + k)"

lemma addsI [intro?]: "a = b + k  b adds a"
  unfolding adds_def ..

lemma addsE [elim?]: "b adds a  (k. a = b + k  P)  P"
  unfolding adds_def by blast

end

context comm_monoid_add
begin

lemma adds_refl [simp]: "a adds a"
proof
  show "a = a + 0" by simp
qed

lemma adds_trans [trans]:
  assumes "a adds b" and "b adds c"
  shows "a adds c"
proof -
  from assms obtain v where "b = a + v"
    by (auto elim!: addsE)
  moreover from assms obtain w where "c = b + w"
    by (auto elim!: addsE)
  ultimately have "c = a + (v + w)"
    by (simp add: add.assoc)
  then show ?thesis ..
qed

lemma subset_divisors_adds: "{c. c adds a}  {c. c adds b}  a adds b"
  by (auto simp add: subset_iff intro: adds_trans)

lemma strict_subset_divisors_adds: "{c. c adds a}  {c. c adds b}  a adds b  ¬ b adds a"
  by (auto simp add: subset_iff intro: adds_trans)

lemma zero_adds [simp]: "0 adds a"
  by (auto intro!: addsI)

lemma adds_plus_right [simp]: "a adds c  a adds (b + c)"
  by (auto intro!: add.left_commute addsI elim!: addsE)

lemma adds_plus_left [simp]: "a adds b  a adds (b + c)"
  using adds_plus_right [of a b c] by (simp add: ac_simps)

lemma adds_triv_right [simp]: "a adds b + a"
  by (rule adds_plus_right) (rule adds_refl)

lemma adds_triv_left [simp]: "a adds a + b"
  by (rule adds_plus_left) (rule adds_refl)

lemma plus_adds_mono:
  assumes "a adds b"
    and "c adds d"
  shows "a + c adds b + d"
proof -
  from a adds b obtain b' where "b = a + b'" ..
  moreover from c adds d obtain d' where "d = c + d'" ..
  ultimately have "b + d = (a + c) + (b' + d')"
    by (simp add: ac_simps)
  then show ?thesis ..
qed

lemma plus_adds_left: "a + b adds c  a adds c"
  by (simp add: adds_def add.assoc) blast

lemma plus_adds_right: "a + b adds c  b adds c"
  using plus_adds_left [of b a c] by (simp add: ac_simps)

end

class ninv_comm_monoid_add = comm_monoid_add +
  assumes plus_eq_zero: "s + t = 0  s = 0"
begin

lemma plus_eq_zero_2: "t = 0" if "s + t = 0"
  using that
  by (simp only: add_commute[of s t] plus_eq_zero)

lemma adds_zero: "s adds 0  (s = 0)"
proof
  assume "s adds 0"
  from this obtain k where "0 = s + k" unfolding adds_def ..
  from this plus_eq_zero[of s k] show "s = 0"
    by blast
next
  assume "s = 0"
  thus "s adds 0" by simp
qed

end

context canonically_ordered_monoid_add
begin
subclass ninv_comm_monoid_add by (standard, simp)
end

class comm_powerprod = cancel_comm_monoid_add
begin

lemma adds_canc: "s + u adds t + u  s adds t" for s t u::'a
  unfolding adds_def
  apply auto
   apply (metis local.add.left_commute local.add_diff_cancel_left' local.add_diff_cancel_right')
  using add_assoc add_commute by auto

lemma adds_canc_2: "u + s adds u + t  s adds t"
  by (simp add: adds_canc ac_simps)

lemma add_minus_2: "(s + t) - s = t"
  by simp

lemma adds_minus:
  assumes "s adds t"
  shows "(t - s) + s = t"
proof -
  from assms adds_def[of s t] obtain u where u: "t = u + s" by (auto simp: ac_simps)
  then have "t - s = u"
    by simp
  thus ?thesis using u by simp
qed

lemma plus_adds_0:
  assumes "(s + t) adds u"
  shows "s adds (u - t)"
proof -
  from assms have "(s + t) adds ((u - t) + t)" using adds_minus local.plus_adds_right by presburger
  thus ?thesis using adds_canc[of s t "u - t"] by simp
qed

lemma plus_adds_2:
  assumes "t adds u" and "s adds (u - t)"
  shows "(s + t) adds u"
  by (metis adds_canc adds_minus assms)

lemma plus_adds:
  shows "(s + t) adds u  (t adds u  s adds (u - t))"
proof
  assume a1: "(s + t) adds u"
  show "t adds u  s adds (u - t)"
  proof
    from plus_adds_right[OF a1] show "t adds u" .
  next
    from plus_adds_0[OF a1] show "s adds (u - t)" .
  qed
next
  assume "t adds u  s adds (u - t)"
  hence "t adds u" and "s adds (u - t)" by auto
  from plus_adds_2[OF t adds u s adds (u - t)] show "(s + t) adds u" .
qed

lemma minus_plus:
  assumes "s adds t"
  shows "(t - s) + u = (t + u) - s"
proof -
  from assms obtain k where k: "t = s + k" unfolding adds_def ..
  hence "t - s = k" by simp
  also from k have "(t + u) - s = k + u"
    by (simp add: add_assoc)
  finally show ?thesis by simp
qed

lemma minus_plus_minus:
  assumes "s adds t" and "u adds v"
  shows "(t - s) + (v - u) = (t + v) - (s + u)"
  using add_commute assms(1) assms(2) diff_diff_add minus_plus by auto

lemma minus_plus_minus_cancel:
  assumes "u adds t" and "s adds u"
  shows "(t - u) + (u - s) = t - s"
  by (metis assms(1) assms(2) local.add_diff_cancel_left' local.add_diff_cancel_right local.addsE minus_plus)

end

text ‹Instances of class lcs_powerprod› are types of commutative power-products admitting
  (not necessarily unique) least common sums (inspired from least common multiplies).
  Note that if the components of indeterminates are arbitrary integers (as for instance in Laurent
  polynomials), then no unique lcss exist.›
class lcs_powerprod = comm_powerprod +
  fixes lcs::"'a  'a  'a"
  assumes adds_lcs: "s adds (lcs s t)"
  assumes lcs_adds: "s adds u  t adds u  (lcs s t) adds u"
  assumes lcs_comm: "lcs s t = lcs t s"
begin

lemma adds_lcs_2: "t adds (lcs s t)"
  by (simp only: lcs_comm[of s t], rule adds_lcs)

lemma lcs_adds_plus: "lcs s t adds s + t" by (simp add: lcs_adds)

text ‹"gcs" stands for "greatest common summand".›
definition gcs :: "'a  'a  'a" where "gcs s t = (s + t) - (lcs s t)"

lemma gcs_plus_lcs: "(gcs s t) + (lcs s t) = s + t"
  unfolding gcs_def by (rule adds_minus, fact lcs_adds_plus)

lemma gcs_adds: "(gcs s t) adds s"
proof -
  have "t adds (lcs s t)" (is "t adds ?l") unfolding lcs_comm[of s t] by (fact adds_lcs)
  then obtain u where eq1: "?l = t + u" unfolding adds_def ..
  from lcs_adds_plus[of s t] obtain v where eq2: "s + t = ?l + v" unfolding adds_def ..
  hence "t + s = t + (u + v)" unfolding eq1 by (simp add: ac_simps)
  hence s: "s = u + v" unfolding add_left_cancel .
  show ?thesis unfolding eq2 gcs_def unfolding s by simp
qed

lemma gcs_comm: "gcs s t = gcs t s" unfolding gcs_def by (simp add: lcs_comm ac_simps)

lemma gcs_adds_2: "(gcs s t) adds t"
  by (simp only: gcs_comm[of s t], rule gcs_adds)

end

class ulcs_powerprod = lcs_powerprod + ninv_comm_monoid_add
begin

lemma adds_antisym:
  assumes "s adds t" "t adds s"
  shows "s = t"
proof -
  from s adds t obtain u where u_def: "t = s + u" unfolding adds_def ..
  from t adds s obtain v where v_def: "s = t + v" unfolding adds_def ..
  from u_def v_def have "s = (s + u) + v" by (simp add: ac_simps)
  hence "s + 0 = s + (u + v)" by (simp add: ac_simps)
  hence "u + v = 0" by simp
  hence "u = 0" using plus_eq_zero[of u v] by simp
  thus ?thesis using u_def by simp
qed

lemma lcs_unique:
  assumes "s adds l" and "t adds l" and *: "u. s adds u  t adds u  l adds u"
  shows "l = lcs s t"
  by (rule adds_antisym, rule *, fact adds_lcs, fact adds_lcs_2, rule lcs_adds, fact+)

lemma lcs_zero: "lcs 0 t = t"
  by (rule lcs_unique[symmetric], fact zero_adds, fact adds_refl)

lemma lcs_plus_left: "lcs (u + s) (u + t) = u + lcs s t" 
proof (rule lcs_unique[symmetric], simp_all only: adds_canc_2, fact adds_lcs, fact adds_lcs_2,
    simp add: add.commute[of u] plus_adds)
  fix v
  assume "u adds v  s adds v - u"
  hence "s adds v - u" ..
  assume "t adds v - u"
  with s adds v - u show "lcs s t adds v - u" by (rule lcs_adds)
qed

lemma lcs_plus_right: "lcs (s + u) (t + u) = (lcs s t) + u"
  using lcs_plus_left[of u s t] by (simp add: ac_simps)

lemma adds_gcs:
  assumes "u adds s" and "u adds t"
  shows "u adds (gcs s t)"
proof -
  from assms have "s + u adds s + t" and "t + u adds t + s"
    by (simp_all add: plus_adds_mono)
  hence "lcs (s + u) (t + u) adds s + t"
    by (auto intro: lcs_adds simp add: ac_simps)
  hence "u + (lcs s t) adds s + t" unfolding lcs_plus_right by (simp add: ac_simps)
  hence "u adds (s + t) - (lcs s t)" unfolding plus_adds ..
  thus ?thesis unfolding gcs_def .
qed

lemma gcs_unique:
  assumes "g adds s" and "g adds t" and *: "u. u adds s  u adds t  u adds g"
  shows "g = gcs s t"
  by (rule adds_antisym, rule adds_gcs, fact, fact, rule *, fact gcs_adds, fact gcs_adds_2)

lemma gcs_plus_left: "gcs (u + s) (u + t) = u + gcs s t"
proof -
  have "u + s + (u + t) - (u + lcs s t) = u + s + (u + t) - u - lcs s t" by (simp only: diff_diff_add)
  also have "... = u + s + t + (u - u) - lcs s t" by (simp add: add.left_commute)
  also have "... = u + s + t - lcs s t" by simp
  also have "... =  u + (s + t - lcs s t)"
    using add_assoc add_commute local.lcs_adds_plus local.minus_plus by auto
  finally show ?thesis unfolding gcs_def lcs_plus_left .
qed

lemma gcs_plus_right: "gcs (s + u) (t + u) = (gcs s t) + u"
  using gcs_plus_left[of u s t] by (simp add: ac_simps)

lemma lcs_same [simp]: "lcs s s = s"
proof -
  have "lcs s s adds s" by (rule lcs_adds, simp_all)
  moreover have "s adds lcs s s" by (rule adds_lcs)
  ultimately show ?thesis by (rule adds_antisym)
qed

lemma gcs_same [simp]: "gcs s s = s"
proof -
  have "gcs s s adds s" by (rule gcs_adds)
  moreover have "s adds gcs s s" by (rule adds_gcs, simp_all)
  ultimately show ?thesis by (rule adds_antisym)
qed

end

subsection ‹Dickson Classes›

definition (in plus) dickson_grading :: "('a  nat)  bool"
  where "dickson_grading d 
          ((s t. d (s + t) = max (d s) (d t))  (n::nat. almost_full_on (adds) {x. d x  n}))"

definition dgrad_set :: "('a  nat)  nat  'a set"
  where "dgrad_set d m = {t. d t  m}"

definition dgrad_set_le :: "('a  nat)  ('a set)  ('a set)  bool"
  where "dgrad_set_le d S T  (sS. tT. d s  d t)"

lemma dickson_gradingI:
  assumes "s t. d (s + t) = max (d s) (d t)"
  assumes "n::nat. almost_full_on (adds) {x. d x  n}"
  shows "dickson_grading d"
  unfolding dickson_grading_def using assms by blast

lemma dickson_gradingD1: "dickson_grading d  d (s + t) = max (d s) (d t)"
  by (auto simp add: dickson_grading_def)

lemma dickson_gradingD2: "dickson_grading d  almost_full_on (adds) {x. d x  n}"
  by (auto simp add: dickson_grading_def)

lemma dickson_gradingD2':
  assumes "dickson_grading (d::'a::comm_monoid_add  nat)"
  shows "wqo_on (adds) {x. d x  n}"
proof (intro wqo_onI transp_onI)
  fix x y z :: 'a
  assume "x adds y" and "y adds z"
  thus "x adds z" by (rule adds_trans)
next
  from assms show "almost_full_on (adds) {x. d x  n}" by (rule dickson_gradingD2)
qed

lemma dickson_gradingE:
  assumes "dickson_grading d" and "i::nat. d ((seq::nat  'a::plus) i)  n"
  obtains i j where "i < j" and "seq i adds seq j"
proof -
  from assms(1) have "almost_full_on (adds) {x. d x  n}" by (rule dickson_gradingD2)
  moreover from assms(2) have "i. seq i  {x. d x  n}" by simp
  ultimately obtain i j where "i < j" and "seq i adds seq j" by (rule almost_full_onD)
  thus ?thesis ..
qed

lemma dickson_grading_adds_imp_le:
  assumes "dickson_grading d" and "s adds t"
  shows "d s  d t"
proof -
  from assms(2) obtain u where "t = s + u" ..
  hence "d t = max (d s) (d u)" by (simp only: dickson_gradingD1[OF assms(1)])
  thus ?thesis by simp
qed

lemma dickson_grading_minus:
  assumes "dickson_grading d" and "s adds (t::'a::cancel_ab_semigroup_add)"
  shows "d (t - s)  d t"
proof -
  from assms(2) obtain u where "t = s + u" ..
  hence "t - s = u" by simp
  from assms(1) have "d t = ord_class.max (d s) (d u)" unfolding t = s + u by (rule dickson_gradingD1)
  thus ?thesis by (simp add: t - s = u)
qed

lemma dickson_grading_lcs:
  assumes "dickson_grading d"
  shows "d (lcs s t)  max (d s) (d t)"
proof -
  from assms have "d (lcs s t)  d (s + t)" by (rule dickson_grading_adds_imp_le, intro lcs_adds_plus)
  thus ?thesis by (simp only: dickson_gradingD1[OF assms])
qed

lemma dickson_grading_lcs_minus:
  assumes "dickson_grading d"
  shows "d (lcs s t - s)  max (d s) (d t)"
proof -
  from assms have "d (lcs s t - s)  d (lcs s t)" by (rule dickson_grading_minus, intro adds_lcs)
  also from assms have "...  max (d s) (d t)" by (rule dickson_grading_lcs)
  finally show ?thesis .
qed

lemma dgrad_set_leI:
  assumes "s. s  S  tT. d s  d t"
  shows "dgrad_set_le d S T"
  using assms by (auto simp: dgrad_set_le_def)

lemma dgrad_set_leE:
  assumes "dgrad_set_le d S T" and "s  S"
  obtains t where "t  T" and "d s  d t"
  using assms by (auto simp: dgrad_set_le_def)

lemma dgrad_set_exhaust_expl:
  assumes "finite F"
  shows "F  dgrad_set d (Max (d ` F))"
proof
  fix f
  assume "f  F"
  hence "d f  d ` F" by simp
  with _ have "d f  Max (d ` F)"
  proof (rule Max_ge)
    from assms show "finite (d ` F)" by auto
  qed
  hence "dgrad_set d (d f)  dgrad_set d (Max (d ` F))" by (auto simp: dgrad_set_def)
  moreover have "f  dgrad_set d (d f)" by (simp add: dgrad_set_def)
  ultimately show "f  dgrad_set d (Max (d ` F))" ..
qed

lemma dgrad_set_exhaust:
  assumes "finite F"
  obtains m where "F  dgrad_set d m"
proof
  from assms show "F  dgrad_set d (Max (d ` F))" by (rule dgrad_set_exhaust_expl)
qed

lemma dgrad_set_le_trans [trans]:
  assumes "dgrad_set_le d S T" and "dgrad_set_le d T U"
  shows "dgrad_set_le d S U"
  unfolding dgrad_set_le_def
proof
  fix s
  assume "s  S"
  with assms(1) obtain t where "t  T" and 1: "d s  d t" by (auto simp add: dgrad_set_le_def)
  from assms(2) this(1) obtain u where "u  U" and 2: "d t  d u" by (auto simp add: dgrad_set_le_def)
  from this(1) show "uU. d s  d u"
  proof
    from 1 2 show "d s  d u" by (rule le_trans)
  qed
qed

lemma dgrad_set_le_Un: "dgrad_set_le d (S  T) U  (dgrad_set_le d S U  dgrad_set_le d T U)"
  by (auto simp add: dgrad_set_le_def)

lemma dgrad_set_le_subset:
  assumes "S  T"
  shows "dgrad_set_le d S T"
  unfolding dgrad_set_le_def using assms by blast

lemma dgrad_set_le_refl: "dgrad_set_le d S S"
  by (rule dgrad_set_le_subset, fact subset_refl)

lemma dgrad_set_le_dgrad_set:
  assumes "dgrad_set_le d F G" and "G  dgrad_set d m"
  shows "F  dgrad_set d m"
proof
  fix f
  assume "f  F"
  with assms(1) obtain g where "g  G" and *: "d f  d g" by (auto simp add: dgrad_set_le_def)
  from assms(2) this(1) have "g  dgrad_set d m" ..
  hence "d g  m" by (simp add: dgrad_set_def)
  with * have "d f  m" by (rule le_trans)
  thus "f  dgrad_set d m" by (simp add: dgrad_set_def)
qed

lemma dgrad_set_dgrad: "p  dgrad_set d (d p)"
  by (simp add: dgrad_set_def)

lemma dgrad_setI [intro]:
  assumes "d t  m"
  shows "t  dgrad_set d m"
  using assms by (auto simp: dgrad_set_def)

lemma dgrad_setD:
  assumes "t  dgrad_set d m"
  shows "d t  m"
  using assms by (simp add: dgrad_set_def)

lemma dgrad_set_zero [simp]: "dgrad_set (λ_. 0) m = UNIV"
  by auto

lemma subset_dgrad_set_zero: "F  dgrad_set (λ_. 0) m"
  by simp

lemma dgrad_set_subset:
  assumes "m  n"
  shows "dgrad_set d m  dgrad_set d n"
  using assms by (auto simp: dgrad_set_def)

lemma dgrad_set_closed_plus:
  assumes "dickson_grading d" and "s  dgrad_set d m" and "t  dgrad_set d m"
  shows "s + t  dgrad_set d m"
proof -
  from assms(1) have "d (s + t) = ord_class.max (d s) (d t)" by (rule dickson_gradingD1)
  also from assms(2, 3) have "...  m" by (simp add: dgrad_set_def)
  finally show ?thesis by (simp add: dgrad_set_def)
qed

lemma dgrad_set_closed_minus:
  assumes "dickson_grading d" and "s  dgrad_set d m" and "t adds (s::'a::cancel_ab_semigroup_add)"
  shows "s - t  dgrad_set d m"
proof -
  from assms(1, 3) have "d (s - t)  d s" by (rule dickson_grading_minus)
  also from assms(2) have "...  m" by (simp add: dgrad_set_def)
  finally show ?thesis by (simp add: dgrad_set_def)
qed

lemma dgrad_set_closed_lcs:
  assumes "dickson_grading d" and "s  dgrad_set d m" and "t  dgrad_set d m"
  shows "lcs s t  dgrad_set d m"
proof -
  from assms(1) have "d (lcs s t)  ord_class.max (d s) (d t)" by (rule dickson_grading_lcs)
  also from assms(2, 3) have "...  m" by (simp add: dgrad_set_def)
  finally show ?thesis by (simp add: dgrad_set_def)
qed

lemma dickson_gradingD_dgrad_set: "dickson_grading d  almost_full_on (adds) (dgrad_set d m)"
  by (auto dest: dickson_gradingD2 simp: dgrad_set_def)

lemma ex_finite_adds:
  assumes "dickson_grading d" and "S  dgrad_set d m"
  obtains T where "finite T" and "T  S" and "s. s  S  (tT. t adds (s::'a::cancel_comm_monoid_add))"
proof -
  have "reflp ((adds)::'a  _)" by (simp add: reflp_def)
  moreover from assms(2) have "almost_full_on (adds) S"
  proof (rule almost_full_on_subset)
    from assms(1) show "almost_full_on (adds) (dgrad_set d m)" by (rule dickson_gradingD_dgrad_set)
  qed
  ultimately obtain T where "finite T" and "T  S" and "s. s  S  (tT. t adds s)"
    by (rule almost_full_on_finite_subsetE, blast)
  thus ?thesis ..
qed

class graded_dickson_powerprod = ulcs_powerprod +
  assumes ex_dgrad: "d::'a  nat. dickson_grading d"
begin

definition dgrad_dummy where "dgrad_dummy = (SOME d. dickson_grading d)"

lemma dickson_grading_dgrad_dummy: "dickson_grading dgrad_dummy"
  unfolding dgrad_dummy_def using ex_dgrad by (rule someI_ex)

end (* graded_dickson_powerprod *)

class dickson_powerprod = ulcs_powerprod +
  assumes dickson: "almost_full_on (adds) UNIV"
begin

lemma dickson_grading_zero: "dickson_grading (λ_::'a. 0)"
  by (simp add: dickson_grading_def dickson)

subclass graded_dickson_powerprod by (standard, rule, fact dickson_grading_zero)

end (* dickson_powerprod *)

text ‹Class @{class graded_dickson_powerprod} is a slightly artificial construction. It is needed,
  because type @{typ "nat 0 nat"} does not satisfy the usual conditions of a "Dickson domain" (as
  formulated in class @{class dickson_powerprod}), but we still want to use that type as the type of
  power-products in the computation of Gr\"obner bases. So, we exploit the fact that in a finite
  set of polynomials (which is the input of Buchberger's algorithm) there is always some "highest"
  indeterminate that occurs with non-zero exponent, and no "higher" indeterminates are generated
  during the execution of the algorithm. This allows us to prove that the algorithm terminates, even
  though there are in principle infinitely many indeterminates.›

subsection ‹Additive Linear Orderings›
  
lemma group_eq_aux: "a + (b - a) = (b::'a::ab_group_add)"
proof -
  have "a + (b - a) = b - a + a" by simp
  also have "... = b" by simp
  finally show ?thesis .
qed

class semi_canonically_ordered_monoid_add = ordered_comm_monoid_add +
  assumes le_imp_add: "a  b  (c. b = a + c)"

context canonically_ordered_monoid_add
begin
subclass semi_canonically_ordered_monoid_add
  by (standard, simp only: le_iff_add)
end

class add_linorder_group = ordered_ab_semigroup_add_imp_le + ab_group_add + linorder

class add_linorder = ordered_ab_semigroup_add_imp_le + cancel_comm_monoid_add + semi_canonically_ordered_monoid_add + linorder
begin

subclass ordered_comm_monoid_add ..

subclass ordered_cancel_comm_monoid_add ..

lemma le_imp_inv:
  assumes "a  b"
  shows "b = a + (b - a)"
  using le_imp_add[OF assms] by auto

lemma max_eq_sum:
  obtains y where "max a b = a + y"
  unfolding max_def
proof (cases "a  b")
  case True
  hence "b = a + (b - a)" by (rule le_imp_inv)
  then obtain c where eq: "b = a + c" ..
  show ?thesis
  proof
    from True show "max a b = a + c" unfolding max_def eq by simp
  qed
next
  case False
  show ?thesis
  proof
    from False show "max a b = a + 0" unfolding max_def by simp
  qed
qed
  
lemma min_plus_max:
  shows "(min a b) + (max a b) = a + b"
proof (cases "a  b")
  case True
  thus ?thesis unfolding min_def max_def by simp
next
  case False
  thus ?thesis unfolding min_def max_def by (simp add: ac_simps)
qed

end (* add_linorder *)

class add_linorder_min = add_linorder +
  assumes zero_min: "0  x"
begin

subclass ninv_comm_monoid_add
proof
  fix x y
  assume *: "x + y = 0"
  show "x = 0"
  proof -
    from zero_min[of x] have "0 = x  x > 0" by auto
    thus ?thesis
    proof
      assume "x > 0"
      have "0  y" by (fact zero_min)
      also have "... = 0 + y" by simp
      also from x > 0 have "... < x + y" by (rule add_strict_right_mono)
      finally have "0 < x + y" .
      hence "x + y  0" by simp
      from this * show ?thesis ..
    qed simp
  qed
qed
  
lemma leq_add_right:
  shows "x  x + y"
  using add_left_mono[OF zero_min[of y], of x] by simp

lemma leq_add_left:
  shows "x  y + x"
  using add_right_mono[OF zero_min[of y], of x] by simp

subclass canonically_ordered_monoid_add
  by (standard, rule, elim le_imp_add, elim exE, simp add: leq_add_right)

end (* add_linorder_min *)
  
class add_wellorder = add_linorder_min + wellorder
  
instantiation nat :: add_linorder
begin

instance by (standard, simp)

end (* add_linorder *)
  
instantiation nat :: add_linorder_min
begin
instance by (standard, simp)
end
  
instantiation nat :: add_wellorder
begin
instance ..
end
  
context add_linorder_group
begin
  
subclass add_linorder
proof (standard, intro exI)
  fix a b
  show "b = a + (b - a)" using add_commute local.diff_add_cancel by auto
qed

end (* add_linorder_group *)
  
instantiation int :: add_linorder_group
begin
instance ..
end

instantiation rat :: add_linorder_group
begin
instance ..
end

instantiation real :: add_linorder_group
begin
instance ..
end

subsection ‹Ordered Power-Products›

locale ordered_powerprod =
  ordered_powerprod_lin: linorder ord ord_strict
  for ord::"'a  'a::comm_powerprod  bool" (infixl "" 50)
  and ord_strict::"'a  'a::comm_powerprod  bool" (infixl "" 50) +
  assumes zero_min: "0  t"
  assumes plus_monotone: "s  t  s + u  t + u"
begin

text ‹Conceal these relations defined in Equipollence›
no_notation lesspoll (infixl  50)
no_notation lepoll   (infixl "" 50)

abbreviation ord_conv (infixl "" 50) where "ord_conv  (≼)¯¯"
abbreviation ord_strict_conv (infixl "" 50) where "ord_strict_conv  (≺)¯¯"

lemma ord_canc:
  assumes "s + u  t + u"
  shows "s  t"
proof (rule ordered_powerprod_lin.le_cases[of s t], simp)
  assume "t  s"
  from assms plus_monotone[OF this, of u] have "t + u = s + u"
    using ordered_powerprod_lin.order.eq_iff by simp
  hence "t = s" by simp
  thus "s  t" by simp
qed

lemma ord_adds:
  assumes "s adds t"
  shows "s  t"
proof -
  from assms have "u. t = s + u" unfolding adds_def by simp
  then obtain k where "t = s + k" ..
  thus ?thesis using plus_monotone[OF zero_min[of k], of s] by (simp add: ac_simps)
qed

lemma ord_canc_left:
  assumes "u + s  u + t"
  shows "s  t"
  using assms unfolding add.commute[of u] by (rule ord_canc)

lemma ord_strict_canc:
  assumes "s + u  t + u"
  shows "s  t"
  using assms ord_canc[of s u t] add_right_cancel[of s u t]
    ordered_powerprod_lin.le_imp_less_or_eq ordered_powerprod_lin.order.strict_implies_order by blast

lemma ord_strict_canc_left:
  assumes "u + s  u + t"
  shows "s  t"
  using assms unfolding add.commute[of u] by (rule ord_strict_canc)

lemma plus_monotone_left:
  assumes "s  t"
  shows "u + s  u + t"
  using assms
  by (simp add: add.commute, rule plus_monotone)

lemma plus_monotone_strict:
  assumes "s  t"
  shows "s + u  t + u"
  using assms
  by (simp add: ordered_powerprod_lin.order.strict_iff_order plus_monotone)

lemma plus_monotone_strict_left:
  assumes "s  t"
  shows "u + s  u + t"
  using assms
  by (simp add: ordered_powerprod_lin.order.strict_iff_order plus_monotone_left)

end

locale gd_powerprod =
  ordered_powerprod ord ord_strict
  for ord::"'a  'a::graded_dickson_powerprod  bool" (infixl "" 50)
  and ord_strict (infixl "" 50)
begin

definition dickson_le :: "('a  nat)  nat  'a  'a  bool"
  where "dickson_le d m s t  (d s  m  d t  m  s  t)"

definition dickson_less :: "('a  nat)  nat  'a  'a  bool"
  where "dickson_less d m s t  (d s  m  d t  m  s  t)"

lemma dickson_leI:
  assumes "d s  m" and "d t  m" and "s  t"
  shows "dickson_le d m s t"
  using assms by (simp add: dickson_le_def)

lemma dickson_leD1:
  assumes "dickson_le d m s t"
  shows "d s  m"
  using assms by (simp add: dickson_le_def)

lemma dickson_leD2:
  assumes "dickson_le d m s t"
  shows "d t  m"
  using assms by (simp add: dickson_le_def)

lemma dickson_leD3:
  assumes "dickson_le d m s t"
  shows "s  t"
  using assms by (simp add: dickson_le_def)

lemma dickson_le_trans:
  assumes "dickson_le d m s t" and "dickson_le d m t u"
  shows "dickson_le d m s u"
  using assms by (auto simp add: dickson_le_def)

lemma dickson_lessI:
  assumes "d s  m" and "d t  m" and "s  t"
  shows "dickson_less d m s t"
  using assms by (simp add: dickson_less_def)

lemma dickson_lessD1:
  assumes "dickson_less d m s t"
  shows "d s  m"
  using assms by (simp add: dickson_less_def)

lemma dickson_lessD2:
  assumes "dickson_less d m s t"
  shows "d t  m"
  using assms by (simp add: dickson_less_def)

lemma dickson_lessD3:
  assumes "dickson_less d m s t"
  shows "s  t"
  using assms by (simp add: dickson_less_def)

lemma dickson_less_irrefl: "¬ dickson_less d m t t"
  by (simp add: dickson_less_def)

lemma dickson_less_trans:
  assumes "dickson_less d m s t" and "dickson_less d m t u"
  shows "dickson_less d m s u"
  using assms by (auto simp add: dickson_less_def)

lemma transp_dickson_less: "transp (dickson_less d m)"
  by (rule transpI, fact dickson_less_trans)

lemma wfp_on_ord_strict:
  assumes "dickson_grading d"
  shows "wfp_on (≺) {x. d x  n}"
proof -
  let ?A = "{x. d x  n}"
  have "strict (≼) = (≺)" by (intro ext, simp only: ordered_powerprod_lin.less_le_not_le)
  have "qo_on (adds) ?A" by (auto simp: qo_on_def reflp_on_def transp_on_def dest: adds_trans)
  moreover from assms have "wqo_on (adds) ?A" by (rule dickson_gradingD2')
  ultimately have "(Q. (x?A. y?A. x adds y  Q x y)  qo_on Q ?A  wfp_on (strict Q) ?A)"
    by (simp only: wqo_extensions_wf_conv)
  hence "(x?A. y?A. x adds y  x  y)  qo_on (≼) ?A  wfp_on (strict (≼)) ?A" ..
  thus ?thesis unfolding strict (≼) = (≺)
  proof
    show "(x?A. y?A. x adds y  x  y)  qo_on (≼) ?A"
    proof (intro conjI ballI impI ord_adds)
      show "qo_on (≼) ?A" by (auto simp: qo_on_def reflp_on_def transp_on_def)
    qed
  qed
qed

lemma wf_dickson_less:
  assumes "dickson_grading d"
  shows "wfP (dickson_less d m)"
proof (rule wfP_chain)
  show "¬ (seq. i. dickson_less d m (seq (Suc i)) (seq i))"
  proof
    assume "seq. i. dickson_less d m (seq (Suc i)) (seq i)"
    then obtain seq::"nat  'a" where "i. dickson_less d m (seq (Suc i)) (seq i)" ..
    hence *: "i. dickson_less d m (seq (Suc i)) (seq i)" ..
    with transp_dickson_less have seq_decr: "i j. i < j  dickson_less d m (seq j) (seq i)"
      by (rule transp_sequence)

    from assms obtain i j where "i < j" and i_adds_j: "seq i adds seq j"
    proof (rule dickson_gradingE)
      fix i
      from * show "d (seq i)  m" by (rule dickson_lessD2)
    qed
    from i < j have "dickson_less d m (seq j) (seq i)" by (rule seq_decr)
    hence "seq j  seq i" by (rule dickson_lessD3)
    moreover from i_adds_j have "seq i  seq j" by (rule ord_adds)
    ultimately show False by simp
  qed
qed

end

text gd_powerprod› stands for @{emph ‹graded ordered Dickson power-products›}.›

locale od_powerprod =
  ordered_powerprod ord ord_strict
  for ord::"'a  'a::dickson_powerprod  bool" (infixl "" 50)
  and ord_strict (infixl "" 50)
begin

sublocale gd_powerprod by standard

lemma wf_ord_strict: "wfP (≺)"
proof (rule wfP_chain)
  show "¬ (seq. i. seq (Suc i)  seq i)"
  proof
    assume "seq. i. seq (Suc i)  seq i"
    then obtain seq::"nat  'a" where "i. seq (Suc i)  seq i" ..
    hence "i. seq (Suc i)  seq i" ..
    with ordered_powerprod_lin.transp_on_less have seq_decr: "i j. i < j  (seq j)  (seq i)"
      by (rule transp_sequence)

    from dickson obtain i j::nat where "i < j" and i_adds_j: "seq i adds seq j"
      by (auto elim!: almost_full_onD)
    from seq_decr[OF i < j] have "seq j  seq i  seq j  seq i" by auto
    hence "seq j  seq i" and "seq j  seq i" by simp_all
    from seq j  seq i seq j  seq i ord_adds[OF i_adds_j]
         ordered_powerprod_lin.order.eq_iff[of "seq j" "seq i"]
      show False by simp
  qed
qed

end

text od_powerprod› stands for @{emph ‹ordered Dickson power-products›}.›

subsection ‹Functions as Power-Products›

lemma finite_neq_0:
  assumes fin_A: "finite {x. f x  0}" and fin_B: "finite {x. g x  0}" and "x. h x 0 0 = 0"
  shows "finite {x. h x (f x) (g x)  0}"
proof -
  from fin_A fin_B have  "finite ({x. f x  0}  {x. g x  0})" by (intro finite_UnI)
  hence finite_union: "finite {x. (f x  0)  (g x  0)}" by (simp only: Collect_disj_eq)
  have "{x. h x (f x) (g x)  0}  {x. (f x  0)  (g x  0)}"
  proof (intro Collect_mono, rule)
    fix x::'a
    assume h_not_zero: "h x (f x) (g x)  0"
    have "f x = 0  g x  0"
    proof
      assume "f x = 0" "g x = 0"
      thus False using h_not_zero h x 0 0 = 0  by simp
    qed
    thus "f x  0  g x  0" by auto
  qed
  from finite_subset[OF this] finite_union show "finite {x. h x (f x) (g x)  0}" .
qed

lemma finite_neq_0':
  assumes "finite {x. f x  0}" and "finite {x. g x  0}" and "h 0 0 = 0"
  shows "finite {x. h (f x) (g x)  0}"
  using assms by (rule finite_neq_0)

lemma finite_neq_0_inv:
  assumes fin_A: "finite {x. h x (f x) (g x)  0}" and fin_B: "finite {x. f x  0}" and "x y. h x 0 y = y"
  shows "finite {x. g x  0}"
proof -
  from fin_A and fin_B have "finite ({x. h x (f x) (g x)  0}  {x. f x  0})" by (intro finite_UnI)
  hence finite_union: "finite {x. (h x (f x) (g x)  0)  f x  0}" by (simp only: Collect_disj_eq)
  have "{x. g x  0}  {x. (h x (f x) (g x)  0)  f x  0}"
    by (intro Collect_mono, rule, rule disjCI, simp add: assms(3))
  from this finite_union show "finite {x. g x  0}" by (rule finite_subset)
qed

lemma finite_neq_0_inv':
  assumes inf_A: "finite {x. h (f x) (g x)  0}" and fin_B: "finite {x. f x  0}" and "x. h 0 x = x"
  shows "finite {x. g x  0}"
  using assms by (rule finite_neq_0_inv)

subsubsection @{typ "'a  'b"} belongs to class @{class comm_powerprod}

instance "fun" :: (type, cancel_comm_monoid_add) comm_powerprod
  by standard

subsubsection @{typ "'a  'b"} belongs to class @{class ninv_comm_monoid_add}

instance "fun" :: (type, ninv_comm_monoid_add) ninv_comm_monoid_add
  by (standard, simp only: plus_fun_def zero_fun_def fun_eq_iff, intro allI, rule plus_eq_zero, auto)

subsubsection @{typ "'a  'b"} belongs to class @{class lcs_powerprod}

instantiation "fun" :: (type, add_linorder) lcs_powerprod
begin

definition lcs_fun::"('a  'b)  ('a  'b)  ('a  'b)" where "lcs f g = (λx. max (f x) (g x))"

lemma adds_funI:
  assumes "s  t"
  shows "s adds (t::'a  'b)"
proof (rule addsI, rule)
  fix x
  from assms have "s x  t x" unfolding le_fun_def ..
  hence "t x = s x + (t x - s x)" by (rule le_imp_inv)
  thus "t x = (s + (t - s)) x" by simp
qed

lemma adds_fun_iff: "f adds (g::'a  'b)  (x. f x adds g x)"
  unfolding adds_def plus_fun_def by metis

lemma adds_fun_iff': "f adds (g::'a  'b)  (x. y. g x = f x + y)"
  unfolding adds_fun_iff unfolding adds_def plus_fun_def ..

lemma adds_lcs_fun:
  shows "s adds (lcs s (t::'a  'b))"
  by (rule adds_funI, simp only: le_fun_def lcs_fun_def, auto simp: max_def)

lemma lcs_comm_fun:  "lcs s t = lcs t (s::'a  'b)"
  unfolding lcs_fun_def
  by (auto simp: max_def intro!: ext)

lemma lcs_adds_fun:
  assumes "s adds u" and "t adds (u::'a  'b)"
  shows "(lcs s t) adds u"
  using assms unfolding lcs_fun_def adds_fun_iff'
proof -
  assume a1: "x. y. u x = s x + y" and a2: "x. y. u x = t x + y"
  show "x. y. u x = max (s x) (t x) + y"
  proof
    fix x
    from a1 have b1: "y. u x = s x + y" ..
    from a2 have b2: "y. u x = t x + y" ..
    show "y. u x = max (s x) (t x) + y" unfolding max_def
      by (split if_split, intro conjI impI, rule b2, rule b1)
  qed
qed

instance
  apply standard
  subgoal by (rule adds_lcs_fun)
  subgoal by (rule lcs_adds_fun)
  subgoal by (rule lcs_comm_fun)
  done

end

lemma leq_lcs_fun_1: "s  (lcs s (t::'a  'b::add_linorder))"
  by (simp add: lcs_fun_def le_fun_def)

lemma leq_lcs_fun_2: "t  (lcs s (t::'a  'b::add_linorder))"
  by (simp add: lcs_fun_def le_fun_def)

lemma lcs_leq_fun:
  assumes "s  u" and "t  (u::'a  'b::add_linorder)"
  shows "(lcs s t)  u"
  using assms by (simp add: lcs_fun_def le_fun_def)

lemma adds_fun: "s adds t  s  t"
  for s t::"'a  'b::add_linorder_min"
proof
  assume "s adds t"
  from this obtain k where "t = s + k" ..
  show "s  t" unfolding t = s + k le_fun_def plus_fun_def le_iff_add by (simp add: leq_add_right)
qed (rule adds_funI)

lemma gcs_fun: "gcs s (t::'a  ('b::add_linorder)) = (λx. min (s x) (t x))"
proof -
  show ?thesis unfolding gcs_def lcs_fun_def fun_diff_def
  proof (simp, rule)
    fix x
    have eq: "s x + t x = max (s x) (t x) + min (s x) (t x)" by (metis add.commute min_def max_def)
    thus "s x + t x - max (s x) (t x) = min (s x) (t x)" by simp
  qed
qed

lemma gcs_leq_fun_1: "(gcs s (t::'a  'b::add_linorder))  s"
  by (simp add: gcs_fun le_fun_def)

lemma gcs_leq_fun_2: "(gcs s (t::'a  'b::add_linorder))  t"
  by (simp add: gcs_fun le_fun_def)

lemma leq_gcs_fun:
  assumes "u  s" and "u  (t::'a  'b::add_linorder)"
  shows "u  (gcs s t)"
  using assms by (simp add: gcs_fun le_fun_def)

subsubsection @{typ "'a  'b"} belongs to class @{class ulcs_powerprod}

instance "fun" :: (type, add_linorder_min) ulcs_powerprod ..

subsubsection ‹Power-products in a given set of indeterminates›

definition supp_fun::"('a  'b::zero)  'a set" where "supp_fun f = {x. f x  0}"

text @{term supp_fun} for general functions is like @{term keys} for @{type poly_mapping},
  but does not need to be finite.›

lemma keys_eq_supp: "keys s = supp_fun (lookup s)"
  unfolding supp_fun_def by (transfer, rule)

lemma supp_fun_zero [simp]: "supp_fun 0 = {}"
  by (auto simp: supp_fun_def)

lemma supp_fun_eq_zero_iff: "supp_fun f = {}  f = 0"
  by (auto simp: supp_fun_def)

lemma sub_supp_empty: "supp_fun s  {}  (s = 0)"
  by (auto simp: supp_fun_def)

lemma except_fun_idI: "supp_fun f  V = {}  except_fun f V = f"
  by (auto simp: except_fun_def supp_fun_def when_def intro!: ext)

lemma supp_except_fun: "supp_fun (except_fun s V) = supp_fun s - V"
  by (auto simp: except_fun_def supp_fun_def)

lemma supp_fun_plus_subset: "supp_fun (s + t)  supp_fun s  supp_fun (t::'a  'b::monoid_add)"
  unfolding supp_fun_def by force

lemma fun_eq_zeroI:
  assumes "x. x  supp_fun f  f x = 0"
  shows "f = 0"
proof (rule, simp)
  fix x
  show "f x = 0"
  proof (cases "x  supp_fun f")
    case True
    then show ?thesis by (rule assms)
  next
    case False
    then show ?thesis by (simp add: supp_fun_def)
  qed
qed

lemma except_fun_cong1:
  "supp_fun s  ((V - U)  (U - V))  {}  except_fun s V = except_fun s U"
  by (auto simp: except_fun_def when_def supp_fun_def intro!: ext)

lemma adds_except_fun:
  "s adds t = (except_fun s V adds except_fun t V  except_fun s (- V) adds except_fun t (- V))"
  for s t :: "'a  'b::add_linorder"
  by (auto simp: supp_fun_def except_fun_def adds_fun_iff when_def)

lemma adds_except_fun_singleton: "s adds t = (except_fun s {v} adds except_fun t {v}  s v adds t v)"
  for s t :: "'a  'b::add_linorder"
  by (auto simp: supp_fun_def except_fun_def adds_fun_iff when_def)

subsubsection ‹Dickson's lemma for power-products in finitely many indeterminates›

lemma Dickson_fun:
  assumes "finite V"
  shows "almost_full_on (adds) {x::'a  'b::add_wellorder. supp_fun x  V}"
  using assms
proof (induct V)
  case empty
  have "finite {0}" by simp
  moreover have "reflp_on {0::'a  'b} (adds)" by (simp add: reflp_on_def)
  ultimately have "almost_full_on (adds) {0::'a  'b}" by (rule finite_almost_full_on)
  thus ?case by (simp add: supp_fun_eq_zero_iff)
next
  case (insert v V)
  show ?case
  proof (rule almost_full_onI)
    fix seq::"nat  'a  'b"
    assume "i. seq i  {x. supp_fun x  insert v V}"
    hence a: "supp_fun (seq i)  insert v V" for i by simp
    define seq' where "seq' = (λi. (except_fun (seq i) {v}, except_fun (seq i) V))"
    have "almost_full_on (adds) {x::'a  'b. supp_fun x  {v}}"
    proof (rule almost_full_onI)
      fix f::"nat  'a  'b"
      assume "i. f i  {x. supp_fun x  {v}}"
      hence b: "supp_fun (f i)  {v}" for i by simp
      let ?f = "λi. f i v"
      have "wfP ((<)::'b  _)" by (simp add: wf wfp_def)
      hence "f :: _  'b. i. f (Suc i) < f i"
        unfolding wf_iff_no_infinite_down_chain[to_pred] .
      hence "f::nat  'b. i. f i  f (Suc i)"
        by (simp add: not_less)
      hence "i. ?f i  ?f (Suc i)" ..
      then obtain i where "?f i  ?f (Suc i)" ..
      have "i < Suc i" by simp
      moreover have "f i adds f (Suc i)" unfolding adds_fun_iff
      proof
        fix x
        show "f i x adds f (Suc i) x"
        proof (cases "x = v")
          case True
          with ?f i  ?f (Suc i) show ?thesis by (simp add: adds_def le_iff_add)
        next
          case False
          with b have "x  supp_fun (f i)" and "x  supp_fun (f (Suc i))" by blast+
          thus ?thesis by (simp add: supp_fun_def)
        qed
      qed
      ultimately show "good (adds) f" by (meson goodI)
    qed
    with insert(3) have
      "almost_full_on (prod_le (adds) (adds)) ({x::'a  'b. supp_fun x  V} × {x::'a  'b. supp_fun x  {v}})"
      (is "almost_full_on ?P ?A") by (rule almost_full_on_Sigma)
    moreover from a have "seq' i  ?A" for i by (auto simp add: seq'_def supp_except_fun)
    ultimately obtain i j where "i < j" and "?P (seq' i) (seq' j)" by (rule