Theory Nullstellensatz

(* Author: Alexander Maletzky *)

section ‹Hilbert's Nullstellensatz›

theory Nullstellensatz
  imports Algebraically_Closed_Fields
          "HOL-Computational_Algebra.Fraction_Field"
          Lex_Order_PP
          Univariate_PM
          Groebner_Bases.Groebner_PM
begin

text ‹We prove the geometric version of Hilbert's Nullstellensatz, i.e. the precise correspondence
  between algebraic varieties and radical ideals.
  The field-theoretic version of the Nullstellensatz is proved in theory Nullstellensatz_Field›.›

subsection ‹Preliminaries›

lemma finite_linorder_induct [consumes 1, case_names empty insert]:
  assumes "finite (A::'a::linorder set)" and "P {}"
    and "a A. finite A  A  {..<a}  P A  P (insert a A)"
  shows "P A"
proof -
  define k where "k = card A"
  thus ?thesis using assms(1)
  proof (induct k arbitrary: A)
    case 0
    with assms(2) show ?case by simp
  next
    case (Suc k)
    define a where "a = Max A"
    from Suc.prems(1) have "A  {}" by auto
    with Suc.prems(2) have "a  A" unfolding a_def by (rule Max_in)
    with Suc.prems have "k = card (A - {a})" by simp
    moreover from Suc.prems(2) have "finite (A - {a})" by simp
    ultimately have "P (A - {a})" by (rule Suc.hyps)
    with finite (A - {a}) _ have "P (insert a (A - {a}))"
    proof (rule assms(3))
      show "A - {a}  {..<a}"
      proof
        fix b
        assume "b  A - {a}"
        hence "b  A" and "b  a" by simp_all
        moreover from Suc.prems(2) this(1) have "b  a" unfolding a_def by (rule Max_ge)
        ultimately show "b  {..<a}" by simp
      qed
    qed
    with a  A show ?case by (simp add: insert_absorb)
  qed
qed

lemma Fract_same: "Fract a a = (1 when a  0)"
  by (simp add: One_fract_def Zero_fract_def eq_fract when_def)

lemma Fract_eq_zero_iff: "Fract a b = 0  a = 0  b = 0"
  by (metis (no_types, lifting) Zero_fract_def eq_fract(1) eq_fract(2) mult_eq_0_iff one_neq_zero)

lemma poly_plus_rightE:
  obtains c where "poly p (x + y) = poly p x + c * y"
proof (induct p arbitrary: thesis)
  case 0
  have "poly 0 (x + y) = poly 0 x + 0 * y" by simp
  thus ?case by (rule 0)
next
  case (pCons a p)
  obtain c where "poly p (x + y) = poly p x + c * y" by (rule pCons.hyps)
  hence "poly (pCons a p) (x + y) = a + (x + y) * (poly p x + c * y)" by simp
  also have " = poly (pCons a p) x + (x * c + (poly p x + c * y)) * y" by (simp add: algebra_simps)
  finally show ?case by (rule pCons.prems)
qed

lemma poly_minus_rightE:
  obtains c where "poly p (x - y) = poly p x - c * (y::_::comm_ring)"
  by (metis add_diff_cancel_right' diff_add_cancel poly_plus_rightE)

lemma map_poly_plus:
  assumes "f 0 = 0" and "a b. f (a + b) = f a + f b"
  shows "map_poly f (p + q) = map_poly f p + map_poly f q"
  by (rule Polynomial.poly_eqI) (simp add: coeff_map_poly assms)

lemma map_poly_minus:
  assumes "f 0 = 0" and "a b. f (a - b) = f a - f b"
  shows "map_poly f (p - q) = map_poly f p - map_poly f q"
  by (rule Polynomial.poly_eqI) (simp add: coeff_map_poly assms)

lemma map_poly_sum:
  assumes "f 0 = 0" and "a b. f (a + b) = f a + f b"
  shows "map_poly f (sum g A) = (aA. map_poly f (g a))"
  by (induct A rule: infinite_finite_induct) (simp_all add: map_poly_plus assms)

lemma map_poly_times:
  assumes "f 0 = 0" and "a b. f (a + b) = f a + f b" and "a b. f (a * b) = f a * f b"
  shows "map_poly f (p * q) = map_poly f p * map_poly f q"
proof (induct p)
  case 0
  show ?case by simp
next
  case (pCons c p)
  show ?case by (simp add: assms map_poly_plus map_poly_smult map_poly_pCons pCons)
qed

lemma poly_Fract:
  assumes "set (Polynomial.coeffs p)  range (λx. Fract x 1)"
  obtains q m where "poly p (Fract a b) = Fract q (b ^ m)"
  using assms
proof (induct p arbitrary: thesis)
  case 0
  have "poly 0 (Fract a b) = Fract 0 (b ^ 1)" by (simp add: fract_collapse)
  thus ?case by (rule 0)
next
  case (pCons c p)
  from pCons.hyps(1) have "insert c (set (Polynomial.coeffs p)) = set (Polynomial.coeffs (pCons c p))"
    by auto
  with pCons.prems(2) have "c  range (λx. Fract x 1)" and "set (Polynomial.coeffs p)  range (λx. Fract x 1)"
    by blast+
  from this(2) obtain q0 m0 where poly_p: "poly p (Fract a b) = Fract q0 (b ^ m0)"
    using pCons.hyps(2) by blast
  from c  _ obtain c0 where c: "c = Fract c0 1" ..
  show ?case
  proof (cases "b = 0")
    case True
    hence "poly (pCons c p) (Fract a b) = Fract c0 (b ^ 0)" by (simp add: c fract_collapse)
    thus ?thesis by (rule pCons.prems)
  next
    case False
    hence "poly (pCons c p) (Fract a b) = Fract (c0 * b ^ Suc m0 + a * q0) (b ^ Suc m0)"
      by (simp add: poly_p c)
    thus ?thesis by (rule pCons.prems)
  qed
qed

lemma (in ordered_term) lt_sum_le_Max: "lt (sum f A) t ord_term_lin.Max {lt (f a) | a. a  A}"
proof (induct A rule: infinite_finite_induct)
  case (infinite A)
  thus ?case by (simp add: min_term_min)
next
  case empty
  thus ?case by (simp add: min_term_min)
next
  case (insert a A)
  show ?case
  proof (cases "A = {}")
    case True
    thus ?thesis by simp
  next
    case False
    from insert.hyps(1, 2) have "lt (sum f (insert a A)) = lt (f a + sum f A)" by simp
    also have " t ord_term_lin.max (lt (f a)) (lt (sum f A))" by (rule lt_plus_le_max)
    also have " t ord_term_lin.max (lt (f a)) (ord_term_lin.Max {lt (f a) |a. a  A})"
      using insert.hyps(3) ord_term_lin.max.mono by blast
    also from insert.hyps(1) False have " = ord_term_lin.Max (insert (lt (f a)) {lt (f x) |x. x  A})"
      by simp
    also have " = ord_term_lin.Max {lt (f x) |x. x  insert a A}"
      by (rule arg_cong[where f=ord_term_lin.Max]) blast
    finally show ?thesis .
  qed
qed

subsection ‹Ideals and Varieties›

definition variety_of :: "(('x 0 nat) 0 'a) set  ('x  'a::comm_semiring_1) set"
  where "variety_of F = {a. fF. poly_eval a f = 0}"

definition ideal_of :: "('x  'a::comm_semiring_1) set  (('x 0 nat) 0 'a) set"
  where "ideal_of A = {f. aA. poly_eval a f = 0}"

abbreviation "𝒱  variety_of"
abbreviation "  ideal_of"

lemma variety_ofI: "(f. f  F  poly_eval a f = 0)  a  𝒱 F"
  by (simp add: variety_of_def)

lemma variety_ofI_alt: "poly_eval a ` F  {0}  a  𝒱 F"
  by (auto intro: variety_ofI)

lemma variety_ofD: "a  𝒱 F  f  F  poly_eval a f = 0"
  by (simp add: variety_of_def)

lemma variety_of_empty [simp]: "𝒱 {} = UNIV"
  by (simp add: variety_of_def)

lemma variety_of_UNIV [simp]: "𝒱 UNIV = {}"
  by (metis (mono_tags, lifting) Collect_empty_eq UNIV_I one_neq_zero poly_eval_one variety_of_def)

lemma variety_of_antimono: "F  G  𝒱 G  𝒱 F"
  by (auto simp: variety_of_def)

lemma variety_of_ideal [simp]: "𝒱 (ideal F) = 𝒱 F"
proof
  show "𝒱 (ideal F)  𝒱 F" by (intro variety_of_antimono ideal.span_superset)
next
  show "𝒱 F  𝒱 (ideal F)"
  proof (intro subsetI variety_ofI)
    fix a f
    assume "a  𝒱 F" and "f  ideal F"
    from this(2) show "poly_eval a f = 0"
    proof (induct f rule: ideal.span_induct_alt)
      case base
      show ?case by simp
    next
      case (step c f g)
      with a  𝒱 F show ?case by (auto simp: poly_eval_plus poly_eval_times dest: variety_ofD)
    qed
  qed
qed

lemma ideal_ofI: "(a. a  A  poly_eval a f = 0)  f   A"
  by (simp add: ideal_of_def)

lemma ideal_ofD: "f   A  a  A  poly_eval a f = 0"
  by (simp add: ideal_of_def)

lemma ideal_of_empty [simp]: " {} = UNIV"
  by (simp add: ideal_of_def)

lemma ideal_of_antimono: "A  B   B   A"
  by (auto simp: ideal_of_def)

lemma ideal_ideal_of [simp]: "ideal ( A) =  A"
  unfolding ideal.span_eq_iff
proof (rule ideal.subspaceI)
  show "0   A" by (rule ideal_ofI) simp
next
  fix f g
  assume "f   A"
  hence f: "poly_eval a f = 0" if "a  A" for a using that by (rule ideal_ofD)
  assume "g   A"
  hence g: "poly_eval a g = 0" if "a  A" for a using that by (rule ideal_ofD)
  show "f + g   A" by (rule ideal_ofI) (simp add: poly_eval_plus f g)
next
  fix c f
  assume "f   A"
  hence f: "poly_eval a f = 0" if "a  A" for a using that by (rule ideal_ofD)
  show "c * f   A" by (rule ideal_ofI) (simp add: poly_eval_times f)
qed

lemma ideal_of_UN: " ( (A ` J)) = (jJ.  (A j))"
proof (intro set_eqI iffI ideal_ofI INT_I)
  fix p j a
  assume "p   ( (A ` J))"
  assume "j  J" and "a  A j"
  hence "a   (A ` J)" ..
  with p  _ show "poly_eval a p = 0" by (rule ideal_ofD)
next
  fix p a
  assume "a   (A ` J)"
  then obtain j where "j  J" and "a  A j" ..
  assume "p  (jJ.  (A j))"
  hence "p   (A j)" using j  J ..
  thus "poly_eval a p = 0" using a  A j by (rule ideal_ofD)
qed

corollary ideal_of_Un: " (A  B) =  A   B"
  using ideal_of_UN[of id "{A, B}"] by simp

lemma variety_of_ideal_of_variety [simp]: "𝒱 ( (𝒱 F)) = 𝒱 F" (is "_ = ?V")
proof
  have "F   (𝒱 F)" by (auto intro!: ideal_ofI dest: variety_ofD)
  thus "𝒱 ( ?V)  ?V" by (rule variety_of_antimono)
next
  show "?V  𝒱 ( ?V)" by (auto intro!: variety_ofI dest: ideal_ofD)
qed

lemma ideal_of_inj_on: "inj_on  (range (𝒱::(('x 0 nat) 0 'a::comm_semiring_1) set  _))"
proof (rule inj_onI)
  fix A B :: "('x  'a) set"
  assume "A  range 𝒱"
  then obtain F where A: "A = 𝒱 F" ..
  assume "B  range 𝒱"
  then obtain G where B: "B = 𝒱 G" ..
  assume " A =  B"
  hence "𝒱 ( A) = 𝒱 ( B)" by simp
  thus "A = B" by (simp add: A B)
qed

lemma ideal_of_variety_of_ideal [simp]: " (𝒱 ( A)) =  A" (is "_ = ?I")
proof
  have "A  𝒱 ( A)" by (auto intro!: variety_ofI dest: ideal_ofD)
  thus " (𝒱 ?I)  ?I" by (rule ideal_of_antimono)
next
  show "?I   (𝒱 ?I)" by (auto intro!: ideal_ofI dest: variety_ofD)
qed

lemma variety_of_inj_on: "inj_on 𝒱 (range (::('x  'a::comm_semiring_1) set  _))"
proof (rule inj_onI)
  fix F G :: "(('x 0 nat) 0 'a) set"
  assume "F  range "
  then obtain A where F: "F =  A" ..
  assume "G  range "
  then obtain B where G: "G =  B" ..
  assume "𝒱 F = 𝒱 G"
  hence " (𝒱 F) =  (𝒱 G)" by simp
  thus "F = G" by (simp add: F G)
qed

lemma image_map_indets_ideal_of:
  assumes "inj f"
  shows "map_indets f `  A =  ((λa. a  f) -` (A::('x  'a::comm_semiring_1) set))  P[range f]"
proof -
  {
    fix p and a::"'x  'a"
    assume "a(λa. a  f) -` A. poly_eval (a  f) p = 0"
    hence eq: "poly_eval (a  f) p = 0" if "a  f  A" for a using that by simp
    have "the_inv f  f = id" by (rule ext) (simp add: assms the_inv_f_f)
    hence a: "a = a  the_inv f  f" by (simp add: comp_assoc)
    moreover assume "a  A"
    ultimately have "(a  the_inv f)  f  A" by simp
    hence "poly_eval ((a  the_inv f)  f) p = 0" by (rule eq)
    hence "poly_eval a p = 0" by (simp flip: a)
  }
  thus ?thesis
    by (auto simp: ideal_of_def poly_eval_map_indets simp flip: range_map_indets intro!: imageI)
qed

lemma variety_of_map_indets: "𝒱 (map_indets f ` F) = (λa. a  f) -` 𝒱 F"
  by (auto simp: variety_of_def poly_eval_map_indets)

subsection ‹Radical Ideals›

definition radical :: "'a::monoid_mult set  'a set" ((_) [999] 999)
  where "radical F = {f. m. f ^ m  F}"

lemma radicalI: "f ^ m  F  f  F"
  by (auto simp: radical_def)

lemma radicalE:
  assumes "f  F"
  obtains m where "f ^ m  F"
  using assms by (auto simp: radical_def)

lemma radical_empty [simp]: "{} = {}"
  by (simp add: radical_def)

lemma radical_UNIV [simp]: "UNIV = UNIV"
  by (simp add: radical_def)

lemma radical_ideal_eq_UNIV_iff: "ideal F = UNIV  ideal F = UNIV"
proof
  assume "ideal F = UNIV"
  hence "1  ideal F" by simp
  then obtain m where "1 ^ m  ideal F" by (rule radicalE)
  thus "ideal F = UNIV" by (simp add: ideal_eq_UNIV_iff_contains_one)
qed simp

lemma zero_in_radical_ideal [simp]: "0  ideal F"
proof (rule radicalI)
  show "0 ^ 1  ideal F" by (simp add: ideal.span_zero)
qed

lemma radical_mono: "F  G  F  G"
  by (auto elim!: radicalE intro: radicalI)

lemma radical_superset: "F  F"
proof
  fix f
  assume "f  F"
  hence "f ^ 1  F" by simp
  thus "f  F" by (rule radicalI)
qed

lemma radical_idem [simp]: "F = F"
proof
  show "F  F" by (auto elim!: radicalE intro: radicalI simp flip: power_mult)
qed (fact radical_superset)

lemma radical_Int_subset: "(A  B)  A  B"
  by (auto intro: radicalI elim: radicalE)

lemma radical_ideal_Int: "(ideal F  ideal G) = ideal F  ideal G"
  using radical_Int_subset
proof (rule subset_antisym)
  show "ideal F  ideal G  (ideal F  ideal G)"
  proof
    fix p
    assume "p  ideal F  ideal G"
    hence "p  ideal F" and "p  ideal G" by simp_all
    from this(1) obtain m1 where p1: "p ^ m1  ideal F" by (rule radicalE)
    from p  ideal G obtain m2 where "p ^ m2  ideal G" by (rule radicalE)
    hence "p ^ m1 * p ^ m2  ideal G" by (rule ideal.span_scale)
    moreover from p1 have "p ^ m2 * p ^ m1  ideal F" by (rule ideal.span_scale)
    ultimately have "p ^ (m1 + m2)  ideal F  ideal G" by (simp add: power_add mult.commute)
    thus "p  (ideal F  ideal G)" by (rule radicalI)
  qed
qed

lemma ideal_radical_ideal [simp]: "ideal (ideal F) = ideal F" (is "_ = ?R")
  unfolding ideal.span_eq_iff
proof (rule ideal.subspaceI)
  have "0 ^ 1  ideal F" by (simp add: ideal.span_zero)
  thus "0  ?R" by (rule radicalI)
next
  fix a b
  assume "a  ?R"
  then obtain m where "a ^ m  ideal F" by (rule radicalE)
  have a: "a ^ k  ideal F" if "m  k" for k
  proof -
    from a ^ m  _ have "a ^ (k - m + m)  ideal F" by (simp only: power_add ideal.span_scale)
    with that show ?thesis by simp
  qed
  assume "b  ?R"
  then obtain n where "b ^ n  ideal F" by (rule radicalE)
  have b: "b ^ k  ideal F" if "n  k" for k
  proof -
    from b ^ n  _ have "b ^ (k - n + n)  ideal F" by (simp only: power_add ideal.span_scale)
    with that show ?thesis by simp
  qed
  have "(a + b) ^ (m + n)  ideal F" unfolding binomial_ring
  proof (rule ideal.span_sum)
    fix k
    show "of_nat (m + n choose k) * a ^ k * b ^ (m + n - k)  ideal F"
    proof (cases "k  m")
      case True
      hence "n  m + n - k" by simp
      hence "b ^ (m + n - k)  ideal F" by (rule b)
      thus ?thesis by (rule ideal.span_scale)
    next
      case False
      hence "m  k" by simp
      hence "a ^ k  ideal F" by (rule a)
      hence "of_nat (m + n choose k) * b ^ (m + n - k) * a ^ k  ideal F" by (rule ideal.span_scale)
      thus ?thesis by (simp only: ac_simps)
    qed
  qed
  thus "a + b  ?R" by (rule radicalI)
next
  fix c a
  assume "a  ?R"
  then obtain m where "a ^ m  ideal F" by (rule radicalE)
  hence "(c * a) ^ m  ideal F" by (simp only: power_mult_distrib ideal.span_scale)
  thus "c * a  ?R" by (rule radicalI)
qed

lemma radical_ideal_of [simp]: " A =  (A::(_  _::semiring_1_no_zero_divisors) set)"
proof
  show " A   A" by (auto elim!: radicalE dest!: ideal_ofD intro!: ideal_ofI simp: poly_eval_power)
qed (fact radical_superset)

lemma variety_of_radical_ideal [simp]: "𝒱 (ideal F) = 𝒱 (F::(_ 0 _::semiring_1_no_zero_divisors) set)"
proof
  have "F  ideal F" by (rule ideal.span_superset)
  also have "  ideal F" by (rule radical_superset)
  finally show "𝒱 (ideal F)  𝒱 F" by (rule variety_of_antimono)
next
  show "𝒱 F  𝒱 (ideal F)"
  proof (intro subsetI variety_ofI)
    fix a f
    assume "a  𝒱 F"
    hence "a  𝒱 (ideal F)" by simp
    assume "f  ideal F"
    then obtain m where "f ^ m  ideal F" by (rule radicalE)
    with a  𝒱 (ideal F) have "poly_eval a (f ^ m) = 0" by (rule variety_ofD)
    thus "poly_eval a f = 0" by (simp add: poly_eval_power)
  qed
qed

lemma image_map_indets_radical:
  assumes "inj f"
  shows "map_indets f ` F = (map_indets f ` (F::(_ 0 'a::comm_ring_1) set))  P[range f]"
proof
  show "map_indets f ` F  (map_indets f ` F)  P[range f]"
    by (auto simp: radical_def simp flip: map_indets_power range_map_indets intro!: imageI)
next
  show "(map_indets f ` F)  P[range f]  map_indets f ` F"
  proof
    fix p
    assume "p  (map_indets f ` F)  P[range f]"
    hence "p  (map_indets f ` F)" and "p  range (map_indets f)"
      by (simp_all add: range_map_indets)
    from this(1) obtain m where "p ^ m  map_indets f ` F" by (rule radicalE)
    then obtain q where "q  F" and p_m: "p ^ m = map_indets f q" ..
    from assms obtain g where "g  f = id" and "map_indets g  map_indets f = (id::_  _ 0 'a)"
      by (rule map_indets_inverseE)
    hence eq: "map_indets g (map_indets f p') = p'" for p'::"_ 0 'a"
      by (simp add: pointfree_idE)
    from p_m have "map_indets g (p ^ m) = map_indets g (map_indets f q)" by (rule arg_cong)
    hence "(map_indets g p) ^ m = q" by (simp add: eq)
    from p  range _ obtain p' where "p = map_indets f p'" ..
    hence "p = map_indets f (map_indets g p)" by (simp add: eq)
    moreover have "map_indets g p  F"
    proof (rule radicalI)
      from q  F show "map_indets g p ^ m  F" by (simp add: p_m eq flip: map_indets_power)
    qed
    ultimately show "p  map_indets f ` F" by (rule image_eqI)
  qed
qed

subsection ‹Geometric Version of the Nullstellensatz›

lemma weak_Nullstellensatz_aux_1:
  assumes "i. i  I  g i  ideal B"
  obtains c where "c  ideal B" and "(iI. (f i + g i) ^ m i) = (iI. f i ^ m i) + c"
  using assms
proof (induct I arbitrary: thesis rule: infinite_finite_induct)
  case (infinite I)
  from ideal.span_zero show ?case by (rule infinite) (simp add: infinite(1))
next
  case empty
  from ideal.span_zero show ?case by (rule empty) simp
next
  case (insert j I)
  have "g i  ideal B" if "i  I" for i by (rule insert.prems) (simp add: that)
  with insert.hyps(3) obtain c where c: "c  ideal B"
    and 1: "(iI. (f i + g i) ^ m i) = (iI. f i ^ m i) + c" by blast
  define k where "k = m j"
  obtain d where 2: "(f j + g j) ^ m j = f j ^ m j + d * g j" unfolding k_def[symmetric]
  proof (induct k arbitrary: thesis)
    case 0
    have "(f j + g j) ^ 0 = f j ^ 0 + 0 * g j" by simp
    thus ?case by (rule 0)
  next
    case (Suc k)
    obtain d where "(f j + g j) ^ k = f j ^ k + d * g j" by (rule Suc.hyps)
    hence "(f j + g j) ^ Suc k = (f j ^ k + d * g j) * (f j + g j)" by simp
    also have " = f j ^ Suc k + (f j ^ k + d * (f j + g j)) * g j" by (simp add: algebra_simps)
    finally show ?case by (rule Suc.prems)
  qed
  from c have *: "f j ^ m j * c + (((iI. f i ^ m i) + c) * d) * g j  ideal B" (is "?c  _")
    by (intro ideal.span_add ideal.span_scale insert.prems insertI1)
  from insert.hyps(1, 2) have "(iinsert j I. (f i + g i) ^ m i) =
                                (f j ^ m j + d * g j) * ((iI. f i ^ m i) + c)"
    by (simp add: 1 2)
  also from insert.hyps(1, 2) have " = (iinsert j I. f i ^ m i) + ?c" by (simp add: algebra_simps)
  finally have "(iinsert j I. (f i + g i) ^ m i) = (iinsert j I. f i ^ m i) + ?c" .
  with * show ?case by (rule insert.prems)
qed

lemma weak_Nullstellensatz_aux_2:
  assumes "finite X" and "F  P[insert x X]" and "X  {..<x::'x::{countable,linorder}}"
    and "1  ideal F" and "ideal F  P[{x}]  {0}"
  obtains a::"'a::alg_closed_field" where "1  ideal (poly_eval (λ_. monomial a 0) ` focus {x} ` F)"
proof -
  let ?x = "monomial 1 (Poly_Mapping.single x 1)"
  from assms(3) have "x  X" by blast
  hence eq1: "insert x X - {x} = X" and eq2: "insert x X - X = {x}" by blast+

  interpret i: pm_powerprod lex_pm "lex_pm_strict::('x 0 nat)  _"
    unfolding lex_pm_def lex_pm_strict_def
    by standard (simp_all add: lex_pm_zero_min lex_pm_plus_monotone flip: lex_pm_def)
  have lpp_focus: "i.lpp (focus X g) = except (i.lpp g) {x}" if "g  P[insert x X]" for g::"_ 0 'a"
  proof (cases "g = 0")
    case True
    thus ?thesis by simp
  next
    case False
    have keys_focus_g: "keys (focus X g) = (λt. except t {x}) ` keys g"
      unfolding keys_focus using refl
    proof (rule image_cong)
      fix t
      assume "t  keys g"
      also from that have "  .[insert x X]" by (rule PolysD)
      finally have "keys t  insert x X" by (rule PPsD)
      hence "except t (- X) = except t (insert x X  - X)"
        by (metis (no_types, lifting) Int_commute except_keys_Int inf.orderE inf_left_commute)
      also from x  X have "insert x X  - X = {x}" by simp
      finally show "except t (- X) = except t {x}" .
    qed
    show ?thesis
    proof (rule i.punit.lt_eqI_keys)
      from False have "i.lpp g  keys g" by (rule i.punit.lt_in_keys)
      thus "except (i.lpp g) {x}  keys (focus X g)" unfolding keys_focus_g by (rule imageI)

      fix t
      assume "t  keys (focus X g)"
      then obtain s where "s  keys g" and t: "t = except s {x}" unfolding keys_focus_g ..
      from this(1) have "lex_pm s (i.lpp g)" by (rule i.punit.lt_max_keys)
      moreover have "keys s  keys (i.lpp g)  {..x}"
      proof (rule Un_least)
        from g  P[_] have "keys g  .[insert x X]" by (rule PolysD)
        with s  keys g have "s  .[insert x X]" ..
        hence "keys s  insert x X" by (rule PPsD)
        thus "keys s  {..x}" using assms(3) by auto

        from i.lpp g  keys g keys g  _ have "i.lpp g  .[insert x X]" ..
        hence "keys (i.lpp g)  insert x X" by (rule PPsD)
        thus "keys (i.lpp g)  {..x}" using assms(3) by auto
      qed
      ultimately show "lex_pm t (except (i.lpp g) {x})" unfolding t by (rule lex_pm_except_max)
    qed
  qed

  define G where "G = i.punit.reduced_GB F"
  from assms(1) have "finite (insert x X)" by simp
  hence fin_G: "finite G" and G_sub: "G  P[insert x X]" and ideal_G: "ideal G = ideal F"
    and "0  G" and G_isGB: "i.punit.is_Groebner_basis G" unfolding G_def using assms(2)
    by (rule i.finite_reduced_GB_Polys, rule i.reduced_GB_Polys, rule i.reduced_GB_ideal_Polys,
        rule i.reduced_GB_nonzero_Polys, rule i.reduced_GB_is_GB_Polys)
  define G' where "G' = focus X ` G"
  from fin_G 0  G have fin_G': "finite G'" and "0  G'" by (auto simp: G'_def)
  have G'_sub: "G'  P[X]" by (auto simp: G'_def intro: focus_in_Polys)
  define G'' where "G'' = i.lcf ` G'"
  from 0  G' have "0  G''" by (auto simp: G''_def i.punit.lc_eq_zero_iff)
  have lookup_focus_in: "lookup (focus X g) t  P[{x}]" if "g  G" for g t
  proof -
    have "lookup (focus X g) t  range (lookup (focus X g))" by (rule rangeI)
    from that G_sub have "g  P[insert x X]" ..
    hence "range (lookup (focus X g))  P[insert x X - X]" by (rule focus_coeffs_subset_Polys')
    with _  range _ have "lookup (focus X g) t  P[insert x X - X]" ..
    also have "insert x X - X = {x}" by (simp only: eq2)
    finally show ?thesis .
  qed
  hence lcf_in: "i.lcf (focus X g)  P[{x}]" if "g  G" for g
    unfolding i.punit.lc_def using that by blast
  have G''_sub: "G''  P[{x}]"
  proof
    fix c
    assume "c  G''"
    then obtain g' where "g'  G'" and c: "c = i.lcf g'" unfolding G''_def ..
    from g'  G' obtain g where "g  G" and g': "g' = focus X g" unfolding G'_def ..
    from this(1) show "c  P[{x}]" unfolding c g' by (rule lcf_in)
  qed
  define P where "P = poly_of_pm x ` G''"
  from fin_G' have fin_P: "finite P" by (simp add: P_def G''_def)
  have "0  P"
  proof
    assume "0  P"
    then obtain g'' where "g''  G''" and "0 = poly_of_pm x g''" unfolding P_def ..
    from this(2) have *: "keys g''  .[{x}] = {}" by (simp add: poly_of_pm_eq_zero_iff)
    from g''  G'' G''_sub have "g''  P[{x}]" ..
    hence "keys g''  .[{x}]" by (rule PolysD)
    with * have "keys g'' = {}" by blast
    with g''  G'' 0  G'' show False by simp
  qed
  define Z where "Z = (pP. {z. poly p z = 0})"
  have "finite Z" unfolding Z_def using fin_P
  proof (rule finite_UN_I)
    fix p
    assume "p  P"
    with 0  P have "p  0" by blast
    thus "finite {z. poly p z = 0}" by (rule poly_roots_finite)
  qed
  with infinite_UNIV[where 'a='a] have "- Z  {}" using finite_compl by fastforce
  then obtain a where "a  Z" by blast

  have a_nz: "poly_eval (λ_. a) (i.lcf (focus X g))  0" if "g  G" for g
  proof -
    from that G_sub have "g  P[insert x X]" ..
    have "poly_eval (λ_. a) (i.lcf (focus X g)) = poly (poly_of_pm x (i.lcf (focus X g))) a"
      by (rule sym, intro poly_eq_poly_eval' lcf_in that)
    moreover have "poly_of_pm x (i.lcf (focus X g))  P"
      by (auto simp: P_def G''_def G'_def that intro!: imageI)
    ultimately show ?thesis using a  Z by (simp add: Z_def)
  qed

  let ?e = "poly_eval (λ_. monomial a 0)"
  have lookup_e_focus: "lookup (?e (focus {x} g)) t = poly_eval (λ_. a) (lookup (focus X g) t)"
    if "g  P[insert x X]" for g t
  proof -
    have "focus (- {x}) g = focus (- {x}  insert x X) g" by (rule sym) (rule focus_Int, fact)
    also have " = focus X g" by (simp add: Int_commute eq1 flip: Diff_eq)
    finally show ?thesis by (simp add: lookup_poly_eval_focus)
  qed
  have lpp_e_focus: "i.lpp (?e (focus {x} g)) = except (i.lpp g) {x}" if "g  G" for g
  proof (rule i.punit.lt_eqI_keys)
    from that G_sub have "g  P[insert x X]" ..
    hence "lookup (?e (focus {x} g)) (except (i.lpp g) {x}) = poly_eval (λ_. a) (i.lcf (focus X g))"
      by (simp only: lookup_e_focus lpp_focus i.punit.lc_def)
    also from that have "  0" by (rule a_nz)
    finally show "except (i.lpp g) {x}  keys (?e (focus {x} g))" by (simp add: in_keys_iff)

    fix t
    assume "t  keys (?e (focus {x} g))"
    hence "0  lookup (?e (focus {x} g)) t" by (simp add: in_keys_iff)
    also from g  P[_] have "lookup (?e (focus {x} g)) t = poly_eval (λ_. a) (lookup (focus X g) t)"
      by (rule lookup_e_focus)
    finally have "t  keys (focus X g)" by (auto simp flip: lookup_not_eq_zero_eq_in_keys)
    hence "lex_pm t (i.lpp (focus X g))" by (rule i.punit.lt_max_keys)
    with g  P[_] show "lex_pm t (except (i.lpp g) {x})" by (simp only: lpp_focus)
  qed

  show ?thesis
  proof
    define G3 where "G3 = ?e ` focus {x} ` G"
    have "G3  P[X]"
    proof
      fix h
      assume "h  G3"
      then obtain h0 where "h0  G" and h: "h = ?e (focus {x} h0)" by (auto simp: G3_def)
      from this(1) G_sub have "h0  P[insert x X]" ..
      hence "h  P[insert x X - {x}]" unfolding h by (rule poly_eval_focus_in_Polys)
      thus "h  P[X]" by (simp only: eq1)
    qed
    from fin_G have "finite G3" by (simp add: G3_def)
    
    have "ideal G3  P[- {x}] = ?e ` focus {x} ` ideal G"
      by (simp only: G3_def image_poly_eval_focus_ideal)
    also have " = ideal (?e ` focus {x} ` F)  P[- {x}]"
      by (simp only: ideal_G image_poly_eval_focus_ideal)
    finally have eq3: "ideal G3  P[- {x}] = ideal (?e ` focus {x} ` F)  P[- {x}]" .
    from assms(1) G3  P[X] finite G3 have G3_isGB: "i.punit.is_Groebner_basis G3"
    proof (rule i.punit.isGB_I_spoly_rep[simplified, OF dickson_grading_varnum,
                                          where m=0, simplified i.dgrad_p_set_varnum])
      fix g1 g2
      assume "g1  G3"
      then obtain g1' where "g1'  G" and g1: "g1 = ?e (focus {x} g1')"
        unfolding G3_def by blast
      from this(1) have lpp1: "i.lpp g1 = except (i.lpp g1') {x}" unfolding g1 by (rule lpp_e_focus)
      from g1'  G G_sub have "g1'  P[insert x X]" ..
      assume "g2  G3"
      then obtain g2' where "g2'  G" and g2: "g2 = ?e (focus {x} g2')"
        unfolding G3_def by blast
      from this(1) have lpp2: "i.lpp g2 = except (i.lpp g2') {x}" unfolding g2 by (rule lpp_e_focus)
      from g2'  G G_sub have "g2'  P[insert x X]" ..

      define l where "l = lcs (except (i.lpp g1') {x}) (except (i.lpp g2') {x})"
      define c1 where "c1 = i.lcf (focus X g1')"
      define c2 where "c2 = i.lcf (focus X g2')"
      define c where "c = poly_eval (λ_. a) c1 * poly_eval (λ_. a) c2"
      define s where "s = c2 * punit.monom_mult 1 (l - except (i.lpp g1') {x}) g1' -
                          c1 * punit.monom_mult 1 (l - except (i.lpp g2') {x}) g2'"
      have "c1  P[{x}]" unfolding c1_def using g1'  G by (rule lcf_in)
      hence eval_c1: "poly_eval (λ_. monomial a 0) (focus {x} c1) = monomial (poly_eval (λ_. a) c1) 0"
        by (simp add: focus_Polys poly_eval_sum poly_eval_monomial monomial_power_map_scale
                  times_monomial_monomial flip: punit.monomial_prod_sum monomial_sum)
           (simp add: poly_eval_alt)
      have "c2  P[{x}]" unfolding c2_def using g2'  G by (rule lcf_in)
      hence eval_c2: "poly_eval (λ_. monomial a 0) (focus {x} c2) = monomial (poly_eval (λ_. a) c2) 0"
        by (simp add: focus_Polys poly_eval_sum poly_eval_monomial monomial_power_map_scale
                  times_monomial_monomial flip: punit.monomial_prod_sum monomial_sum)
           (simp add: poly_eval_alt)

      assume spoly_nz: "i.punit.spoly g1 g2  0"
      assume "g1  0" and "g2  0"
      hence "g1'  0" and "g2'  0" by (auto simp: g1 g2)
      have c1_nz: "poly_eval (λ_. a) c1  0" unfolding c1_def using g1'  G by (rule a_nz)
      moreover have c2_nz: "poly_eval (λ_. a) c2  0" unfolding c2_def using g2'  G by (rule a_nz)
      ultimately have "c  0" by (simp add: c_def)
      hence "inverse c  0" by simp
      from g1'  P[_] have "except (i.lpp g1') {x}  .[insert x X - {x}]"
        by (intro PPs_closed_except' i.PPs_closed_lpp)
      moreover from g2'  P[_] have "except (i.lpp g2') {x}  .[insert x X - {x}]"
        by (intro PPs_closed_except' i.PPs_closed_lpp)
      ultimately have "l  .[insert x X - {x}]" unfolding l_def by (rule PPs_closed_lcs)
      hence "l  .[X]" by (simp only: eq1)
      hence "l  .[insert x X]" by rule (rule PPs_mono, blast)
      moreover from c1  P[{x}] have "c1  P[insert x X]" by rule (intro Polys_mono, simp)
      moreover from c2  P[{x}] have "c2  P[insert x X]" by rule (intro Polys_mono, simp)
      ultimately have "s  P[insert x X]" using g1'  P[_] g2'  P[_] unfolding s_def
        by (intro Polys_closed_minus Polys_closed_times Polys_closed_monom_mult PPs_closed_minus)
      have "s  ideal G" unfolding s_def times_monomial_left[symmetric]
        by (intro ideal.span_diff ideal.span_scale ideal.span_base g1'  G g2'  G)
      with G_isGB have "(i.punit.red G)** s 0" by (rule i.punit.GB_imp_zero_reducibility[simplified])
      with finite (insert x X) G_sub fin_G s  P[_]
      obtain q0 where 1: "s = 0 + (gG. q0 g * g)" and 2: "g. q0 g  P[insert x X]"
        and 3: "g. lex_pm (i.lpp (q0 g * g)) (i.lpp s)"
        by (rule i.punit.red_rtrancl_repE[simplified, OF dickson_grading_varnum, where m=0,
                                            simplified i.dgrad_p_set_varnum]) blast

      define q where "q = (λg. inverse c  (h{yG. ?e (focus {x} y) = g}. ?e (focus {x} (q0 h))))"

      have eq4: "?e (focus {x} (monomial 1 (l - t))) = monomial 1 (l - t)" for t
      proof -
        have "focus {x} (monomial (1::'a) (l - t)) = monomial (monomial 1 (l - t)) 0"
        proof (intro focus_Polys_Compl Polys_closed_monomial PPs_closed_minus)
          from x  X have "X  - {x}" by simp
          hence ".[X]  .[- {x}]" by (rule PPs_mono)
          with l  .[X] show "l  .[- {x}]" ..
        qed
        thus ?thesis by (simp add: poly_eval_monomial)
      qed
      from c2_nz have eq5: "inverse c * poly_eval (λ_. a) c2 = 1 / lookup g1 (i.lpp g1)"
        unfolding lpp1 using g1'  P[_]
        by (simp add: c_def mult.assoc divide_inverse_commute g1 lookup_e_focus
                flip: lpp_focus i.punit.lc_def c1_def)
      from c1_nz have eq6: "inverse c * poly_eval (λ_. a) c1 = 1 / lookup g2 (i.lpp g2)"
        unfolding lpp2 using g2'  P[_]
        by (simp add: c_def mult.assoc mult.left_commute[of "inverse (poly_eval (λ_. a) c1)"]
                    divide_inverse_commute g2 lookup_e_focus flip: lpp_focus i.punit.lc_def c2_def)
      have l_alt: "l = lcs (i.lpp g1) (i.lpp g2)" by (simp only: l_def lpp1 lpp2)
      have spoly_eq: "i.punit.spoly g1 g2 = (inverse c)  ?e (focus {x} s)"
        by (simp add: s_def focus_minus focus_times poly_eval_minus poly_eval_times eval_c1 eval_c2
                      eq4 eq5 eq6 map_scale_eq_times times_monomial_monomial right_diff_distrib
                      i.punit.spoly_def Let_def
                 flip: mult.assoc times_monomial_left g1 g2 lpp1 lpp2 l_alt)
      also have " = (gG. inverse c  (?e (focus {x} (q0 g)) * ?e (focus {x} g)))"
        by (simp add: 1 focus_sum poly_eval_sum focus_times poly_eval_times map_scale_sum_distrib_left)
      also have " = (gG3. h{yG. ?e (focus{x} y) = g}.
                                      inverse c  (?e (focus {x} (q0 h)) * ?e (focus {x} h)))"
        unfolding G3_def image_image using fin_G by (rule sum.image_gen)
      also have " = (gG3. inverse c  (h{yG. ?e (focus{x} y) = g}. ?e (focus {x} (q0 h))) * g)"
        by (intro sum.cong refl) (simp add: map_scale_eq_times sum_distrib_left sum_distrib_right mult.assoc)
      also from refl have " = (gG3. q g * g)" by (rule sum.cong) (simp add: q_def sum_distrib_right)
      finally have "i.punit.spoly g1 g2 = (gG3. q g * g)" .
      thus "i.punit.spoly_rep (varnum X) 0 G3 g1 g2"
      proof (rule i.punit.spoly_repI[simplified, where m=0 and d="varnum X",
                                        simplified i.dgrad_p_set_varnum])
        fix g
        show "q g  P[X]" unfolding q_def
        proof (intro Polys_closed_map_scale Polys_closed_sum)
          fix g0
          from q0 g0  P[insert x X] have "?e (focus {x} (q0 g0))  P[insert x X - {x}]"
            by (rule poly_eval_focus_in_Polys)
          thus "?e (focus {x} (q0 g0))  P[X]" by (simp only: eq1)
        qed

        assume "q g  0  g  0"
        hence "q g  0" ..
        have "i.lpp (q g * g) = i.lpp (h{yG. ?e (focus {x} y) = g}. inverse c  ?e (focus {x} (q0 h)) * g)"
          by (simp add: q_def map_scale_sum_distrib_left sum_distrib_right)
        also have "lex_pm  (i.ordered_powerprod_lin.Max
                {i.lpp (inverse c  ?e (focus {x} (q0 h)) * g) | h. h  {yG. ?e (focus {x} y) = g}})"
          (is "lex_pm _ (i.ordered_powerprod_lin.Max ?A)") by (fact i.punit.lt_sum_le_Max)
        also have "lex_pm  (i.lpp s)"
        proof (rule i.ordered_powerprod_lin.Max.boundedI)
          from fin_G show "finite ?A" by simp
        next
          show "?A  {}"
          proof
            assume "?A = {}"
            hence "{h  G. ?e (focus {x} h) = g} = {}" by simp
            hence "q g = 0" by (simp only: q_def sum.empty map_scale_zero_right)
            with q g  0 show False ..
          qed
        next
          fix t
          assume "t  ?A"
          then obtain h where "h  G" and g[symmetric]: "?e (focus {x} h) = g"
            and "t = i.lpp (inverse c  ?e (focus {x} (q0 h)) * g)" by blast
          note this(3)
          also have "i.lpp (inverse c  ?e (focus {x} (q0 h)) * g) =
                      i.lpp (inverse c  (?e (focus {x} (q0 h * h))))"
            by (simp only: map_scale_eq_times mult.assoc g poly_eval_times focus_times)
          also from inverse c  0 have " = i.lpp (?e (focus {x} (q0 h * h)))"
            by (rule i.punit.lt_map_scale)
          also have "lex_pm  (i.lpp (q0 h * h))"
          proof (rule i.punit.lt_le, rule ccontr)
            fix u
            assume "lookup (?e (focus {x} (q0 h * h))) u  0"
            hence "u  keys (?e (focus {x} (q0 h * h)))" by (simp add: in_keys_iff)
            with keys_poly_eval_focus_subset have "u  (λv. except v {x}) ` keys (q0 h * h)" ..
            then obtain v where "v  keys (q0 h * h)" and u: "u = except v {x}" ..
            have "lex_pm u (Poly_Mapping.single x (lookup v x) + u)"
              by (metis add.commute add.right_neutral i.plus_monotone_left lex_pm_zero_min)
            also have " = v" by (simp only: u flip: plus_except)
            also from v  _ have "lex_pm v (i.lpp (q0 h * h))" by (rule i.punit.lt_max_keys)
            finally have "lex_pm u (i.lpp (q0 h * h))" .
            moreover assume "lex_pm_strict (i.lpp (q0 h * h)) u"
            ultimately show False by simp
          qed
          also have "lex_pm  (i.lpp s)" by fact
          finally show "lex_pm t (i.lpp s)" .
        qed
        also have "lex_pm_strict  l"
        proof (rule i.punit.lt_less)
          from spoly_nz show "s  0" by (auto simp: spoly_eq)
        next
          fix t
          assume "lex_pm l t"

          have "g1' = flatten (focus X g1')" by simp
          also have " = flatten (monomial c1 (i.lpp (focus X g1')) + i.punit.tail (focus X g1'))"
            by (simp only: c1_def flip: i.punit.leading_monomial_tail)
          also from g1'  P[_] have " = punit.monom_mult 1 (except (i.lpp g1') {x}) c1 +
                                              flatten (i.punit.tail (focus X g1'))"
            by (simp only: flatten_plus flatten_monomial lpp_focus)
          finally have "punit.monom_mult 1 (except (i.lpp g1') {x}) c1 +
                              flatten (i.punit.tail (focus X g1')) = g1'" (is "?l = _") by (rule sym)
          moreover have "c2 * punit.monom_mult 1 (l - except (i.lpp g1') {x}) ?l =
                          punit.monom_mult 1 l (c1 * c2) +
                          c2 * punit.monom_mult 1 (l - i.lpp (focus X g1'))
                                                  (flatten (i.punit.tail (focus X g1')))"
            (is "_ = punit.monom_mult 1 l (c1 * c2) + ?a")
            by (simp add: punit.monom_mult_dist_right punit.monom_mult_assoc l_def minus_plus adds_lcs)
               (simp add: distrib_left lpp_focus g1'  P[_] flip: times_monomial_left)
          ultimately have a: "c2 * punit.monom_mult 1 (l - except (i.lpp g1') {x}) g1' =
                                punit.monom_mult 1 l (c1 * c2) + ?a" by simp

          have "g2' = flatten (focus X g2')" by simp
          also have " = flatten (monomial c2 (i.lpp (focus X g2')) + i.punit.tail (focus X g2'))"
            by (simp only: c2_def flip: i.punit.leading_monomial_tail)
          also from g2'  P[_] have " = punit.monom_mult 1 (except (i.lpp g2') {x}) c2 +
                                              flatten (i.punit.tail (focus X g2'))"
            by (simp only: flatten_plus flatten_monomial lpp_focus)
          finally have "punit.monom_mult 1 (except (i.lpp g2') {x}) c2 +
                              flatten (i.punit.tail (focus X g2')) = g2'" (is "?l = _") by (rule sym)
          moreover have "c1 * punit.monom_mult 1 (l - except (i.lpp g2') {x}) ?l =
                          punit.monom_mult 1 l (c1 * c2) +
                          c1 * punit.monom_mult 1 (l - i.lpp (focus X g2'))
                                                  (flatten (i.punit.tail (focus X g2')))"
            (is "_ = punit.monom_mult 1 l (c1 * c2) + ?b")
            by (simp add: punit.monom_mult_dist_right punit.monom_mult_assoc l_def minus_plus adds_lcs_2)
               (simp add: distrib_left lpp_focus g2'  P[_] flip: times_monomial_left)
          ultimately have b: "c1 * punit.monom_mult 1 (l - except (i.lpp g2') {x}) g2' =
                                punit.monom_mult 1 l (c1 * c2) + ?b" by simp

          have lex_pm_strict_t: "lex_pm_strict t (l - i.lpp (focus X h) + i.lpp (focus X h))"
            if "t  keys (d * punit.monom_mult 1 (l - i.lpp (focus X h))
                                            (flatten (i.punit.tail (focus X h))))"
            and "h  G" and "d  P[{x}]" for d h
          proof -
            have 0: "lex_pm_strict (u + v) w" if "lex_pm_strict v w" and "w  .[X]" and "u  .[{x}]"
              for u v w using that(1)
            proof (rule lex_pm_strict_plus_left)
              fix y z
              assume "y  keys w"
              also from that(2) have "  X" by (rule PPsD)
              also have "  {..<x}" by fact
              finally have "y < x" by simp
              assume "z  keys u"
              also from that(3) have "  {x}" by (rule PPsD)
              finally show "y < z" using y < x by simp
            qed
            let ?h = "focus X h"
            from that(2) have "?h  G'" by (simp add: G'_def)
            with G'  P[X] have "?h  P[X]" ..
            hence "i.lpp ?h  .[X]" by (rule i.PPs_closed_lpp)
            from that(1) obtain t1 t2 where "t1  keys d"
              and "t2  keys (punit.monom_mult 1 (l - i.lpp ?h) (flatten (i.punit.tail ?h)))"
              and t: "t = t1 + t2" by (rule in_keys_timesE)
            from this(2) obtain t3 where "t3  keys (flatten (i.punit.tail ?h))"
              and t2: "t2 = l - i.lpp ?h + t3" by (auto simp: punit.keys_monom_mult)
            from this(1) obtain t4 t5 where "t4  keys (i.punit.tail ?h)"
              and t5_in: "t5  keys (lookup (i.punit.tail ?h) t4)" and t3: "t3 = t4 + t5"
              using keys_flatten_subset by blast
            from this(1) have 1: "lex_pm_strict t4 (i.lpp ?h)" by (rule i.punit.keys_tail_less_lt)
            from that(2) have "lookup ?h t4  P[{x}]" by (rule lookup_focus_in)
            hence "keys (lookup ?h t4)  .[{x}]" by (rule PolysD)
            moreover from t5_in have t5_in: "t5  keys (lookup ?h t4)"
              by (simp add: i.punit.lookup_tail split: if_split_asm)
            ultimately have "t5  .[{x}]" ..
            with 1 i.lpp ?h  _ have "lex_pm_strict (t5 + t4) (i.lpp ?h)" by (rule 0)
            hence "lex_pm_strict t3 (i.lpp ?h)" by (simp only: t3 add.commute)
            hence "lex_pm_strict t2 (l - i.lpp ?h + i.lpp ?h)" unfolding t2
              by (rule i.plus_monotone_strict_left)
            moreover from l  .[X] i.lpp ?h  .[X] have "l - i.lpp ?h + i.lpp ?h  .[X]"
              by (intro PPs_closed_plus PPs_closed_minus)
            moreover from t1  keys d that(3) have "t1  .[{x}]" by (auto dest: PolysD)
            ultimately show ?thesis unfolding t by (rule 0)
          qed
          show "lookup s t = 0"
          proof (rule ccontr)
            assume "lookup s t  0"
            hence "t  keys s" by (simp add: in_keys_iff)
            also have " = keys (?a - ?b)" by (simp add: s_def a b)
            also have "  keys ?a  keys ?b" by (fact keys_minus)
            finally show False
            proof
              assume "t  keys ?a"
              hence "lex_pm_strict t (l - i.lpp (focus X g1') + i.lpp (focus X g1'))"
                using g1'  G c2  P[{x}] by (rule lex_pm_strict_t)
              with g1'  P[_] have "lex_pm_strict t l"
                by (simp add: lpp_focus l_def minus_plus adds_lcs)
              with lex_pm l t show ?thesis by simp
            next
              assume "t  keys ?b"
              hence "lex_pm_strict t (l - i.lpp (focus X g2') + i.lpp (focus X g2'))"
                using g2'  G c1  P[{x}] by (rule lex_pm_strict_t)
              with g2'  P[_] have "lex_pm_strict t l"
                by (simp add: lpp_focus l_def minus_plus adds_lcs_2)
              with lex_pm l t show ?thesis by simp
            qed
          qed
        qed
        also have " = lcs (i.lpp g1) (i.lpp g2)" by (simp only: l_def lpp1 lpp2)
        finally show "lex_pm_strict (i.lpp (q g * g)) (lcs (i.lpp g1) (i.lpp g2))" .
      qed
    qed
    have "1  ideal (?e ` focus {x} ` F)  1  ideal (?e ` focus {x} ` F)  P[- {x}]"
      by (simp add: one_in_Polys)
    also have "  1  ideal G3" by (simp add: one_in_Polys flip: eq3)
    also have "¬ "
    proof
      note G3_isGB
      moreover assume "1  ideal G3"
      moreover have "1  (0::_ 0 'a)" by simp
      ultimately obtain g where "g  G3" and "g  0" and "i.lpp g adds i.lpp (1::_ 0 'a)"
        by (rule i.punit.GB_adds_lt[simplified])
      from this(3) have "i.lpp g = 0" by (simp add: i.punit.lt_monomial adds_zero flip: single_one)
      hence "monomial (i.lcf g) 0 = g" by (rule i.punit.lt_eq_min_term_monomial[simplified])
      from g  G3 obtain g' where "g'  G" and g: "g = ?e (focus {x} g')" by (auto simp: G3_def)
      from this(1) have "i.lpp g = except (i.lpp g') {x}" unfolding g by (rule lpp_e_focus)
      hence "keys (i.lpp g')  {x}" by (simp add: i.lpp g = 0 except_eq_zero_iff)
      have "g'  P[{x}]"
      proof (intro PolysI subsetI PPsI)
        fix t y
        assume "t  keys g'"
        hence "lex_pm t (i.lpp g')" by (rule i.punit.lt_max_keys)
        moreover assume "y  keys t"
        ultimately obtain z where "z  keys (i.lpp g')" and "z  y" by (rule lex_pm_keys_leE)
        with keys (i.lpp g')  {x} have "x  y" by blast
        from g'  G G_sub have "g'  P[insert x X]" ..
        hence "indets g'  insert x X" by (rule PolysD)
        moreover from y  _ t  _ have "y  indets g'" by (rule in_indetsI)
        ultimately have "y  insert x X" ..
        thus "y  {x}"
        proof
          assume "y  X"
          with assms(3) have "y  {..<x}" ..
          with x  y show ?thesis by simp
        qed simp
      qed
      moreover from g'  G have "g'  ideal G" by (rule ideal.span_base)
      ultimately have "g'  ideal F  P[{x}]" by (simp add: ideal_G)
      with assms(5) have "g' = 0" by blast
      hence "g = 0" by (simp add: g)
      with g  0 show False ..
    qed
    finally show "1  ideal (?e ` focus {x} ` F)" .
  qed
qed

lemma weak_Nullstellensatz_aux_3:
  assumes "F  P[insert x X]" and "x  X" and "1  ideal F" and "¬ ideal F  P[{x}]  {0}"
  obtains a::"'a::alg_closed_field" where "1  ideal (poly_eval (λ_. monomial a 0) ` focus {x} ` F)"
proof -
  let ?x = "monomial 1 (Poly_Mapping.single x 1)"
  from assms(4) obtain f where "f  ideal F" and "f  P[{x}]" and "f  0" by blast
  define p where "p = poly_of_pm x f"
  from f  P[{x}] f  0 have "p  0"
    by (auto simp: p_def poly_of_pm_eq_zero_iff simp flip: keys_eq_empty dest!: PolysD(1))
  obtain c A m where A: "finite A" and p: "p = Polynomial.smult c (aA. [:- a, 1:] ^ m a)"
    and "x. m x = 0  x  A" and "c = 0  p = 0" and "z. poly p z = 0  (c = 0  z  A)"
    by (rule linear_factorsE) blast
  from this(4, 5) have "c  0" and "z. poly p z = 0  z  A" by (simp_all add: p  0)
  have "aA. 1  ideal (poly_eval (λ_. monomial a 0) ` focus {x} ` F)"
  proof (rule ccontr)
    assume asm: "¬ (aA. 1  ideal (poly_eval (λ_. monomial a 0) ` focus {x} ` F))"
    obtain g h where "g a  ideal F" and 1: "h a * (?x - monomial a 0) + g a = 1"
      if "a  A" for a
    proof -
      define P where "P = (λgh a. fst gh  ideal F  fst gh + snd gh * (?x - monomial a 0) = 1)"
      define gh where "gh = (λa. SOME gh. P gh a)"
      show ?thesis
      proof
        fix a
        assume "a  A"
        with asm have "1  ideal (poly_eval (λ_. monomial a 0) ` focus {x} ` F)" by blast
        hence "1  poly_eval (λ_. monomial a 0) ` focus {x} ` ideal F"
          by (simp add: image_poly_eval_focus_ideal one_in_Polys)
        then obtain g where "g  ideal F" and "1 = poly_eval (λ_. monomial a 0) (focus {x} g)"
          unfolding image_image ..
        note this(2)
        also have "poly_eval (λ_. monomial a 0) (focus {x} g) = poly (poly_of_focus x g) (monomial a 0)"
          by (simp only: poly_poly_of_focus)
        also have " = poly (poly_of_focus x g) (?x - (?x - monomial a 0))" by simp
        also obtain h where " = poly (poly_of_focus x g) ?x - h * (?x - monomial a 0)"
          by (rule poly_minus_rightE)
        also have " = g - h * (?x - monomial a 0)" by (simp only: poly_poly_of_focus_monomial)
        finally have "g - h * (?x - monomial a 0) = 1" by (rule sym)
        with g  ideal F have "P (g, - h) a" by (simp add: P_def)
        hence "P (gh a) a" unfolding gh_def by (rule someI)
        thus "fst (gh a)  ideal F" and "snd (gh a) * (?x - monomial a 0) + fst (gh a) = 1"
          by (simp_all only: P_def add.commute)
      qed
    qed
    from this(1) obtain g' where "g'  ideal F"
      and 2: "(aA. (h a * (?x - monomial a 0) + g a) ^ m a) =
                (aA. (h a * (?x - monomial a 0)) ^ m a) + g'"
      by (rule weak_Nullstellensatz_aux_1)
    have "1 = (aA. (h a * (?x - monomial a 0) + g a) ^ m a)"
      by (rule sym) (intro prod.neutral ballI, simp only: 1 power_one)
    also have " = (aA. h a ^ m a) * (aA. (?x - monomial a 0) ^ m a) + g'"
      by (simp only: 2 power_mult_distrib prod.distrib)
    also have "(aA. (?x - monomial a 0) ^ m a) = pm_of_poly x (aA. [:- a, 1:] ^ m a)"
      by (simp add: pm_of_poly_prod pm_of_poly_pCons single_uminus punit.monom_mult_monomial
              flip: single_one)
    also from c  0 have " = monomial (inverse c) 0 * pm_of_poly x p"
      by (simp add: p map_scale_assoc flip: map_scale_eq_times)
    also from f  P[{x}] have " = monomial (inverse c) 0 * f"
      by (simp only: p = poly_of_pm x f pm_of_poly_of_pm)
    finally have "1 = ((aA. h a ^ m a) * monomial (inverse c) 0) * f + g'"
      by (simp only: mult.assoc)
    also from f  ideal F g'  ideal F have "  ideal F" by (intro ideal.span_add ideal.span_scale)
    finally have "1  ideal F" .
    with assms(3) show False ..
  qed
  then obtain a where "1  ideal (poly_eval (λ_. monomial a 0) ` focus {x} ` F)" ..
  thus ?thesis ..
qed

theorem weak_Nullstellensatz:
  assumes "finite X" and "F  P[X]" and "𝒱 F = ({}::('x::{countable,linorder}  'a::alg_closed_field) set)"
  shows "ideal F = UNIV"
  unfolding ideal_eq_UNIV_iff_contains_one
proof (rule ccontr)
  assume "1  ideal F"
  with assms(1, 2) obtain a where "1  ideal (poly_eval a ` F)"
  proof (induct X arbitrary: F thesis rule: finite_linorder_induct)
    case empty
    have "F  {0}"
    proof
      fix f
      assume "f  F"
      with empty.prems(2) have "f  P[{}]" ..
      then obtain c where f: "f = monomial c 0" unfolding Polys_empty ..
      also have "c = 0"
      proof (rule ccontr)
        assume "c  0"
        from f  F have "f  ideal F" by (rule ideal.span_base)
        hence "monomial (inverse c) 0 * f  ideal F" by (rule ideal.span_scale)
        with c  0 have "1  ideal F" by (simp add: f times_monomial_monomial)
        with empty.prems(3) show False ..
      qed
      finally show "f  {0}" by simp
    qed
    hence "poly_eval 0 ` F  {0}" by auto
    hence "ideal (poly_eval 0 ` F) = {0}" by simp
    hence "1  ideal (poly_eval 0 ` F)" by (simp del: ideal_eq_zero_iff)
    thus ?case by (rule empty.prems)
  next
    case (insert x X)
    obtain a0 where "1  ideal (poly_eval (λ_. monomial a0 0) ` focus {x} ` F)" (is "_  ideal ?F")
    proof (cases "ideal F  P[{x}]  {0}")
      case True
      with insert.hyps(1) insert.prems(2) insert.hyps(2) insert.prems(3) obtain a0
        where "1  ideal (poly_eval (λ_. monomial a0 0) ` focus {x} ` F)"
        by (rule weak_Nullstellensatz_aux_2)
      thus ?thesis ..
    next
      case False
      from insert.hyps(2) have "x  X" by blast
      with insert.prems(2) obtain a0 where "1  ideal (poly_eval (λ_. monomial a0 0) ` focus {x} ` F)"
        using insert.prems(3) False by (rule weak_Nullstellensatz_aux_3)
      thus ?thesis ..
    qed
    moreover have "?F  P[X]"
    proof -
      {
        fix f
        assume "f  F"
        with insert.prems(2) have "f  P[insert x X]" ..
        hence "poly_eval (λ_. monomial a0 0) (focus {x} f)  P[insert x X - {x}]"
          by (rule poly_eval_focus_in_Polys)
        also have "  P[X]" by (rule Polys_mono) simp
        finally have "poly_eval (λ_. monomial a0 0) (focus {x} f)  P[X]" .
      }
      thus ?thesis by blast
    qed
    ultimately obtain a1 where "1  ideal (poly_eval a1 ` ?F)" using insert.hyps(3) by blast
    also have "poly_eval a1 ` ?F = poly_eval (a1(x := poly_eval a1 (monomial a0 0))) ` F"
      by (simp add: image_image poly_eval_poly_eval_focus fun_upd_def)
    finally show ?case by (rule insert.prems)
  qed
  hence "ideal (poly_eval a ` F)  UNIV" by (simp add: ideal_eq_UNIV_iff_contains_one)
  hence "ideal (poly_eval a ` F) = {0}" using ideal_field_disj[of "poly_eval a ` F"] by blast
  hence "poly_eval a ` F  {0}" by simp
  hence "a  𝒱 F" by (rule variety_ofI_alt)
  thus False by (simp add: assms(3))
qed

lemma radical_idealI:
  assumes "finite X" and "F  P[X]" and "f  P[X]" and "x  X"
    and "𝒱 (insert (1 - punit.monom_mult 1 (Poly_Mapping.single x 1) f) F) = {}"
  shows "(f::('x::{countable,linorder} 0 nat) 0 'a::alg_closed_field)  ideal F"
proof (cases "f = 0")
  case True
  thus ?thesis by simp
next
  case False
  from assms(4) have "P[X]  P[- {x}]" by (auto simp: Polys_alt)
  with assms(3) have "f  P[- {x}]" ..
  let ?x = "Poly_Mapping.single x 1"
  let ?f = "punit.monom_mult 1 ?x f"
  from assms(1) have "finite (insert x X)" by simp
  moreover have "insert (1 - ?f) F  P[insert x X]" unfolding insert_subset
  proof (intro conjI Polys_closed_minus one_in_Polys Polys_closed_monom_mult PPs_closed_single)
    have "P[X]  P[insert x X]" by (rule Polys_mono) blast
    with assms(2, 3) show "f  P[insert x X]" and "F  P[insert x X]" by blast+
  qed simp
  ultimately have "ideal (insert (1 - ?f) F) = UNIV"
    using assms(5) by (rule weak_Nullstellensatz)
  hence "1  ideal (insert (1 - ?f) F)" by simp
  then obtain F' q where fin': "finite F'" and F'_sub: "F'  insert (1 - ?f) F"
    and eq: "1 = (f'F'. q f' * f')" by (rule ideal.spanE)
  show "f  ideal F"
  proof (cases "1 - ?f  F'")
    case True
    define g where "g = (λx::('x 0 nat) 0 'a. Fract x 1)"
    define F'' where "F'' = F' - {1 - ?f}"
    define q0 where "q0 = q (1 - ?f)"
    have g_0: "g 0 = 0" by (simp add: g_def fract_collapse)
    have g_1: "g 1 = 1" by (simp add: g_def fract_collapse)
    have g_plus: "g (a + b) = g a + g b" for a b by (simp add: g_def)
    have g_minus: "g (a - b) = g a - g b" for a b by (simp add: g_def)
    have g_times: "g (a * b) = g a * g b" for a b by (simp add: g_def)
    from fin' have fin'': "finite F''" by (simp add: F''_def)
    from F'_sub have F''_sub: "F''  F" by (auto simp: F''_def)

    have "focus {x} ?f = monomial 1 ?x * focus {x} f"
      by (simp add: focus_times focus_monomial except_single flip: times_monomial_left)
    also from f  P[- {x}] have "focus {x} f = monomial f 0" by (rule focus_Polys_Compl)
    finally have "focus {x} ?f = monomial f ?x" by (simp add: times_monomial_monomial)
    hence eq1: "poly (map_poly g (poly_of_focus x (1 - ?f))) (Fract 1 f) = 0"
      by (simp add: poly_of_focus_def focus_minus poly_of_pm_minus poly_of_pm_monomial
                PPs_closed_single map_poly_minus g_0 g_1 g_minus map_poly_monom poly_monom)
         (simp add: g_def Fract_same f  0)
    have eq2: "poly (map_poly g (poly_of_focus x f')) (Fract 1 f) = Fract f' 1" if "f'  F''" for f'
    proof -
      from that F''_sub have "f'  F" ..
      with assms(2) have "f'  P[X]" ..
      with P[X]  _ have "f'  P[- {x}]" ..
      hence "focus {x} f' = monomial f' 0" by (rule focus_Polys_Compl)
      thus ?thesis
        by (simp add: poly_of_focus_def focus_minus poly_of_pm_minus poly_of_pm_monomial
                zero_in_PPs map_poly_minus g_0 g_1 g_minus map_poly_monom poly_monom)
           (simp only: g_def)
    qed

    define p0m0 where "p0m0 = (λf'. SOME z. poly (map_poly g (poly_of_focus x (q f'))) (Fract 1 f) =
                                              Fract (fst z) (f ^ snd z))"
    define p0 where "p0 = fst  p0m0"
    define m0 where "m0 = snd  p0m0"
    define m where "m = Max (m0 ` F'')"
    have eq3: "poly (map_poly g (poly_of_focus x (q f'))) (Fract 1 f) = Fract (p0 f') (f ^ m0 f')"
      for f'
    proof -
      have "g a = 0  a = 0" for a by (simp add: g_def Fract_eq_zero_iff)
      hence "set (Polynomial.coeffs (map_poly g (poly_of_focus x (q f'))))  range (λx. Fract x 1)"
        by (auto simp: set_coeffs_map_poly g_def)
      then obtain p m' where "poly (map_poly g (poly_of_focus x (q f'))) (Fract 1 f) = Fract p (f ^ m')"
        by (rule poly_Fract)
      hence "poly (map_poly g (poly_of_focus x (q f'))) (Fract 1 f) = Fract (fst (p, m')) (f ^ snd (p, m'))"
        by simp
      thus ?thesis unfolding p0_def m0_def p0m0_def o_def by (rule someI)
    qed
    
    note eq
    also from True fin' have "(f'F'. q f' * f') = q0 * (1 - ?f) + (f'F''. q f' * f')"
      by (simp add: q0_def F''_def sum.remove)
    finally have "poly_of_focus x 1 = poly_of_focus x (q0 * (1 - ?f) + (f'F''. q f' * f'))"
      by (rule arg_cong)
    hence "1 = poly (map_poly g (poly_of_focus x (q0 * (1 - ?f) + (f'F''. q f' * f')))) (Fract 1 f)"
      by (simp add: g_1)
    also have " = poly (map_poly g (poly_of_focus x (f'F''. q f' * f'))) (Fract 1 f)"
      by (simp only: poly_of_focus_plus map_poly_plus g_0 g_plus g_times poly_add
                poly_of_focus_times map_poly_times poly_mult eq1 mult_zero_right add_0_left)
    also have " = (f'F''. Fract (p0 f') (f ^ m0 f') * Fract f' 1)"
      by (simp only: poly_of_focus_sum poly_of_focus_times map_poly_sum map_poly_times
                g_0 g_plus g_times poly_sum poly_mult eq2 eq3 cong: sum.cong)
    finally have "Fract (f ^ m) 1 = Fract (f ^ m) 1 * (f'F''. Fract (p0 f' * f') (f ^ m0 f'))"
      by simp
    also have " = (f'F''. Fract (f ^ m * (p0 f' * f')) (f ^ m0 f'))"
      by (simp add: sum_distrib_left)
    also from refl have " = (f'F''. Fract ((f ^ (m - m0 f') * p0 f') * f') 1)"
    proof (rule sum.cong)
      fix f'
      assume "f'  F''"
      hence "m0 f'  m0 ` F''" by (rule imageI)
      with _ have "m0 f'  m" unfolding m_def by (rule Max_ge) (simp add: fin'')
      hence "f ^ m = f ^ (m0 f') * f ^ (m - m0 f')" by (simp flip: power_add)
      hence "Fract (f ^ m * (p0 f' * f')) (f ^ m0 f') = Fract (f ^ m0 f') (f ^ m0 f') *
                                                        Fract (f ^ (m - m0 f') * (p0 f' * f')) 1"
        by (simp add: ac_simps)
      also from f  0 have "Fract (f ^ m0 f') (f ^ m0 f') = 1" by (simp add: Fract_same)
      finally show "Fract (f ^ m * (p0 f' * f')) (f ^ m0 f') = Fract (f ^ (m - m0 f') * p0 f' * f') 1"
        by (simp add: ac_simps)
    qed
    also from fin'' have " = Fract (f'F''. (f ^ (m - m0 f') * p0 f') * f') 1"
      by (induct F'') (simp_all add: fract_collapse)
    finally have "f ^ m = (f'F''. (f ^ (m - m0 f') * p0 f') * f')"
      by (simp add: eq_fract)
    also have "  ideal F''" by (rule ideal.sum_in_spanI)
    also from F''  F have "  ideal F" by (rule ideal.span_mono)
    finally show "f  ideal F" by (rule radicalI)
  next
    case False
    with F'_sub have "F'  F" by blast
    have "1  ideal F'" unfolding eq by (rule ideal.sum_in_spanI)
    also from F'  F have "  ideal F" by (rule ideal.span_mono)
    finally have "ideal F = UNIV" by (simp only: ideal_eq_UNIV_iff_contains_one)
    thus ?thesis by simp
  qed
qed

corollary radical_idealI_extend_indets:
  assumes "finite X" and "F  P[X]"
    and "𝒱 (insert (1 - punit.monom_mult 1 (Poly_Mapping.single None 1) (extend_indets f))
                            (extend_indets ` F)) = {}"
  shows "(f::(_::{countable,linorder} 0 nat) 0 _::alg_closed_field)  ideal F"
proof -
  define Y where "Y = X  indets f"
  from assms(1) have fin_Y: "finite Y" by (simp add: Y_def finite_indets)
  have "P[X]  P[Y]" by (rule Polys_mono) (simp add: Y_def)
  with assms(2) have F_sub: "F  P[Y]" by (rule subset_trans)
  have f_in: "f  P[Y]" by (simp add: Y_def Polys_alt)

  let ?F = "extend_indets ` F"
  let ?f = "extend_indets f"
  let ?X = "Some ` Y"
  from fin_Y have "finite ?X" by (rule finite_imageI)
  moreover from F_sub have "?F  P[?X]"
    by (auto simp: indets_extend_indets intro!: PolysI_alt imageI dest!: PolysD(2) subsetD[of F])
  moreover from f_in have "?f  P[?X]"
    by (auto simp: indets_extend_indets intro!: PolysI_alt imageI dest!: PolysD(2))
  moreover have "None  ?X" by simp
  ultimately have "?f  ideal ?F" using assms(3) by (rule radical_idealI)
  also have "?f  ideal ?F  f  ideal F"
  proof
    assume "f  ideal F"
    then obtain m where "f ^ m  ideal F" by (rule radicalE)
    hence "extend_indets (f ^ m)  extend_indets ` ideal F" by (rule imageI)
    with extend_indets_ideal_subset have "?f ^ m  ideal ?F" unfolding extend_indets_power ..
    thus "?f  ideal ?F" by (rule radicalI)
  next
    assume "?f  ideal ?F"
    then obtain m where "?f ^ m  ideal ?F" by (rule radicalE)
    moreover have "?f ^ m  P[- {None}]"
      by (rule Polys_closed_power) (auto intro!: PolysI_alt simp: indets_extend_indets)
    ultimately have "extend_indets (f ^ m)  extend_indets ` ideal F"
      by (simp add: extend_indets_ideal extend_indets_power)
    hence "f ^ m  ideal F" by (simp only: inj_image_mem_iff[OF inj_extend_indets])
    thus "f  ideal F" by (rule radicalI)
  qed
  finally show ?thesis .
qed

theorem Nullstellensatz:
  assumes "finite X" and "F  P[X]"
    and "(f::(_::{countable,linorder} 0 nat) 0 _::alg_closed_field)   (𝒱 F)"
  shows "f  ideal F"
  using assms(1, 2)
proof (rule radical_idealI_extend_indets)
  let ?f = "punit.monom_mult 1 (monomial 1 None) (extend_indets f)"
  show "𝒱 (insert (1 - ?f) (extend_indets ` F)) = {}"
  proof (intro subset_antisym subsetI)
    fix a
    assume "a  𝒱 (insert (1 - ?f) (extend_indets ` F))"
    moreover have "1 - ?f  insert (1 - ?f) (extend_indets ` F)" by simp
    ultimately have "poly_eval a (1 - ?f) = 0" by (rule variety_ofD)
    hence "poly_eval a (extend_indets f)  0"
      by (auto simp: poly_eval_minus poly_eval_times simp flip: times_monomial_left)
    hence "poly_eval (a  Some) f  0" by (simp add: poly_eval_extend_indets)
    have "a  Some  𝒱 F"
    proof (rule variety_ofI)
      fix f'
      assume "f'  F"
      hence "extend_indets f'  insert (1 - ?f) (extend_indets ` F)" by simp
      with a  _ have "poly_eval a (extend_indets f') = 0" by (rule variety_ofD)
      thus "poly_eval (a  Some) f' = 0" by (simp only: poly_eval_extend_indets)
    qed
    with assms(3) have "poly_eval (a  Some) f = 0" by (rule ideal_ofD)
    with poly_eval (a  Some) f  0 show "a  {}" ..
  qed simp
qed

theorem strong_Nullstellensatz:
  assumes "finite X" and "F  P[X]"
  shows " (𝒱 F) = ideal (F::((_::{countable,linorder} 0 nat) 0 _::alg_closed_field) set)"
proof (intro subset_antisym subsetI)
  fix f
  assume "f   (𝒱 F)"
  with assms show "f  ideal F" by (rule Nullstellensatz)
qed (metis ideal_ofI variety_ofD variety_of_radical_ideal)

text ‹The following lemma can be used for actually ‹deciding› whether a polynomial is contained in
  the radical of an ideal or not.›

lemma radical_ideal_iff:
  assumes "finite X" and "F  P[X]" and "f  P[X]" and "x  X"
  shows "(f::(_::{countable,linorder} 0 nat) 0 _::alg_closed_field)  ideal F 
            1  ideal (insert (1 - punit.monom_mult 1 (Poly_Mapping.single x 1) f) F)"
proof -
  let ?f = "punit.monom_mult 1 (Poly_Mapping.single x 1) f"
  show ?thesis
  proof
    assume "f  ideal F"
    then obtain m where "f ^ m  ideal F" by (rule radicalE)
    from assms(1) have "finite (insert x X)" by simp
    moreover have "insert (1 - ?f) F  P[insert x X]" unfolding insert_subset
    proof (intro conjI Polys_closed_minus one_in_Polys Polys_closed_monom_mult PPs_closed_single)
      have "P[X]  P[insert x X]" by (rule Polys_mono) blast
      with assms(2, 3) show "f  P[insert x X]" and "F  P[insert x X]" by blast+
    qed simp
    moreover have "𝒱 (insert (1 - ?f) F) = {}"
    proof (intro subset_antisym subsetI)
      fix a
      assume "a  𝒱 (insert (1 - ?f) F)"
      moreover have "1 - ?f  insert (1 - ?f) F" by simp
      ultimately have "poly_eval a (1 - ?f) = 0" by (rule variety_ofD)
      hence "poly_eval a (f ^ m)  0"
        by (auto simp: poly_eval_minus poly_eval_times poly_eval_power simp flip: times_monomial_left)
      from a  _ have "a  𝒱 (ideal (insert (1 - ?f) F))" by (simp only: variety_of_ideal)
      moreover from f ^ m  ideal F ideal.span_mono have "f ^ m  ideal (insert (1 - ?f) F)"
        by (rule rev_subsetD) blast
      ultimately have "poly_eval a (f ^ m) = 0" by (rule variety_ofD)
      with poly_eval a (f ^ m)  0 show "a  {}" ..
    qed simp
    ultimately have "ideal (insert (1 - ?f) F) = UNIV" by (rule weak_Nullstellensatz)
    thus "1  ideal (insert (1 - ?f) F)" by simp
  next
    assume "1  ideal (insert (1 - ?f) F)"
    have "𝒱 (insert (1 - ?f) F) = {}"
    proof (intro subset_antisym subsetI)
      fix a
      assume "a  𝒱 (insert (1 - ?f) F)"
      hence "a  𝒱 (ideal (insert (1 - ?f) F))" by (simp only: variety_of_ideal)
      hence "poly_eval a 1 = 0" using 1  _ by (rule variety_ofD)
      thus "a  {}" by simp
    qed simp
    with assms show "f  ideal F" by (rule radical_idealI)
  qed
qed

end (* theory *)