Theory Pi_Relations

theory Pi_Relations
  imports J3_Relations
begin

subsection โ€นThe $\Pi$ polynomialโ€บ

(* Auxiliary lemma *)
lemma finite_when: "finite {x. (f x when x = c) โ‰  0}"
  by auto

locale section5_variables
begin

definition ๐’ฎ :: "int mpoly" where "๐’ฎ โ‰ก Var 4"
definition ๐’ฏ :: "int mpoly" where "๐’ฏ โ‰ก Var 5"
definition โ„› :: "int mpoly" where "โ„› โ‰ก Var 6"
definition โ„ :: "int mpoly" where "โ„ โ‰ก Var 7"
definition ๐’ด :: "int mpoly" where "๐’ด โ‰ก Var 8"
definition โ„จ :: "int mpoly" where "โ„จ โ‰ก Var 9"
definition ๐’ฅ :: "int mpoly" where "๐’ฅ โ‰ก Var 10"
definition ๐—‡ :: "int mpoly" where "๐—‡ โ‰ก Var 11"

definition ๐’ฐ ::"int mpoly" where "๐’ฐ = ๐’ฎ^2 * (2*โ„›-1)"
definition ๐’ฑ ::"int mpoly" where "๐’ฑ = ๐’ฏ^2 + ๐’ฎ^2 * ๐—‡"

lemmas defs = ๐’ฎ_def ๐’ฏ_def โ„›_def โ„_def ๐’ด_def โ„จ_def ๐’ฅ_def ๐—‡_def ๐’ฐ_def ๐’ฑ_def

end 

locale pi_relations = section5_variables + 
  fixes โ„ฑ :: "int mpoly"
    and v :: nat
  assumes F_monom_over_v: "vars โ„ฑ โІ {v}"
      and F_one: "coeff โ„ฑ (Abs_poly_mapping (ฮปk. (degree โ„ฑ v when k = v))) = 1"
begin

definition coeff_F where 
  "coeff_F d = coeff โ„ฑ (Abs_poly_mapping (ฮปk. (d when k = v)))"
definition q :: nat where 
  "q = degree โ„ฑ v"
definition G :: "int mpoly" where 
  "G = (โˆ‘i=0..q. of_int (coeff_F i) * (๐’ด-โ„-๐’ฏ^2)^i)"
definition G_sub :: "natโ‡’int mpoly" where
  "G_sub j = (โˆ‘i=j..q. of_int (coeff_F i) * of_nat (i choose j) * (-โ„-๐’ฏ^2)^(i-j))"
definition H :: "int mpoly" where 
  "H = (โˆ‘j=0..q. G_sub j * โ„จ^j * ๐’ฅ^(q-j))"
definition ฮ  :: "int mpoly" where 
  "ฮ  = (โˆ‘j=0..q. G_sub j * (๐’ฎ^2*๐—‡ + ๐’ฏ^2)^j * (๐’ฎ^2*(2*โ„›-1))^(q-j))"

lemma eval_F:
  "insertion f โ„ฑ = (โˆ‘d = 0..q. coeff_F d * (f v ^ d))"
proof -
  have aux1: " {a. lookup (mapping_of โ„ฑ) a * (โˆv. f v ^ lookup a v) โ‰  0}
    โІ {a โˆˆ range (ฮปd. Abs_poly_mapping (ฮปk. d when k = v)).
        lookup (mapping_of โ„ฑ) a * (โˆv. f v ^ lookup a v) โ‰  0}"
  proof -
    have "lookup (mapping_of โ„ฑ) a โ‰  0 โŸน
         (โˆv. f v ^ lookup a v) โ‰  0 โŸน
         a โˆˆ range (ฮปd. Abs_poly_mapping (ฮปk. d when k = v))" for a 
    proof -
      assume "lookup (mapping_of โ„ฑ) a โ‰  0"
      hence "a โˆˆ keys (mapping_of โ„ฑ)"
        unfolding keys.rep_eq
        by simp
      hence "โ‹€v'. v โ‰  v' โŸน lookup a v' = 0"
        subgoal for v'
        proof (cases "lookup a v' = 0")
          case True
          then show ?thesis by simp
        next
          case False
          assume "lookup a v' โ‰  0"
          hence "v' โˆˆ โ‹ƒ (keys ` keys (mapping_of โ„ฑ))"
            using `a โˆˆ keys (mapping_of โ„ฑ)`
            unfolding keys.rep_eq
            apply simp
            apply (rule exI[where ?x=a])
            by simp
          hence 0: "v' = v"
            using F_monom_over_v vars_def
            by auto
          assume 1: "v โ‰  v'"
          from 0 and 1 show ?thesis by simp
        qed
        done
      hence "a = Abs_poly_mapping (ฮปk. (lookup a v) when k = v)"
        apply (subst lookup_inverse[symmetric])
        apply (rule arg_cong[where ?f=Abs_poly_mapping])
        by (metis (mono_tags, opaque_lifting) "when"(1) "when"(2))
      thus "a โˆˆ range (ฮปd. Abs_poly_mapping (ฮปk. d when k = v))"
        by simp
    qed
    thus ?thesis by auto
  qed

  have aux2: "lookup (mapping_of โ„ฑ) (Abs_poly_mapping (ฮปk. d when k = v)) โ‰  0 โŸน
    (โˆva. f va ^ lookup (Abs_poly_mapping (ฮปk. d when k = v)) va) โ‰  0 โŸน d โˆˆ {0..q}" for d
  proof - 
    assume "lookup (mapping_of โ„ฑ) (Abs_poly_mapping (ฮปk. d when k = v)) โ‰  0"
    thus "d โˆˆ {0..q}"
      unfolding q_def degree.rep_eq keys.rep_eq
      apply (subst atLeastAtMost_iff)
      apply simp
      apply (rule Max_ge)
      subgoal by simp
      apply (rule insertI2)
      apply (rule image_eqI[where ?x="Abs_poly_mapping (ฮปk. d when k = v)"])
      subgoal
        apply (subst lookup_Abs_poly_mapping)
        subgoal using finite_when[where ?f="ฮป_. d"] by simp
        by auto
      by simp
  qed

  have aux3: "x โ‰ค q โŸน y โ‰ค q โŸน
    Abs_poly_mapping (ฮปk. x when k = v) = Abs_poly_mapping (ฮปk. y when k = v) โŸน x = y" for x y
    proof -
      assume "Abs_poly_mapping (ฮปk. x when k = v) = Abs_poly_mapping (ฮปk. y when k = v)"
      hence 0: "(ฮปk. x when k = v) = (ฮปk. y when k = v)"
        apply (subst Abs_poly_mapping_inject[symmetric])
        subgoal
          apply (rule CollectI)
          using finite_when[where ?f="ฮป_. x"] by simp
        subgoal
          apply (rule CollectI)
          using finite_when[where ?f="ฮป_. y"] by simp
        by simp
      have "(x when v = v) = (y when v = v)"
        by (rule fun_cong[OF 0, where ?x=v])
      thus ?thesis
        by simp
    qed

  have aux4: "lookup (mapping_of โ„ฑ) (Abs_poly_mapping (ฮปk. d when k = v)) โ‰  0 โŸน
    (โˆva. f va ^ lookup (Abs_poly_mapping (ฮปk. d when k = v)) va) โ‰  0 โŸน
    Abs_poly_mapping (ฮปk. d when k = v) โˆˆ (ฮปd. Abs_poly_mapping (ฮปk. d when k = v)) ` {0..q}" for d
    apply (rule image_eqI[where ?x=d, OF refl])
    using aux2 by simp

  have "insertion f โ„ฑ = (โˆ‘mโˆˆ{a. lookup (mapping_of โ„ฑ) a * (โˆv. f v ^ lookup a v) โ‰  0}.
       lookup (mapping_of โ„ฑ) m * (โˆv. f v ^ lookup m v))"
    unfolding insertion.rep_eq insertion_aux.rep_eq insertion_fun_def
    unfolding Sum_any.expand_set
    by simp
  also have "... =
    (โˆ‘mโˆˆ{a โˆˆ range (ฮปd. Abs_poly_mapping (ฮปk. d when k = v)).
          lookup (mapping_of โ„ฑ) a * (โˆv. f v ^ lookup a v) โ‰  0}.
      lookup (mapping_of โ„ฑ) m * (โˆv. f v ^ lookup m v))"
    apply (rule sum.mono_neutral_right[symmetric])
    subgoal by auto
    subgoal using aux1 by auto
    subgoal by auto
    done
  also have "... =
    (โˆ‘mโˆˆ{a โˆˆ (ฮปd. Abs_poly_mapping (ฮปk. d when k = v)) ` {0..q}.
          lookup (mapping_of โ„ฑ) a * (โˆv. f v ^ lookup a v) โ‰  0}.
      lookup (mapping_of โ„ฑ) m * (โˆv. f v ^ lookup m v))"
    apply (rule arg_cong[where ?f="sum _"])
    apply standard
    subgoal using aux4 by auto
    subgoal by auto
    done
  also have "... =
    (โˆ‘mโˆˆ(ฮปd. Abs_poly_mapping (ฮปk. d when k = v)) ` {0..q}.
      lookup (mapping_of โ„ฑ) m * (โˆv. f v ^ lookup m v))"
    apply (rule sum.mono_neutral_right[symmetric])
    by auto
  also have "... =
    sum
      ((ฮปm. lookup (mapping_of โ„ฑ) m *
       (โˆv. f v ^ lookup m v)) โˆ˜ (ฮปd. Abs_poly_mapping (ฮปk. d when k = v)))
      {0..q}"
    apply (rule sum.reindex) unfolding inj_on_def using aux3 by auto
  also have "... =
    (โˆ‘dโˆˆ{0..q}. lookup (mapping_of โ„ฑ) (Abs_poly_mapping (ฮปk. d when k = v)) *
      (โˆva. f va ^ lookup (Abs_poly_mapping (ฮปk. d when k = v)) va))"
    by simp
  finally show ?thesis
    unfolding coeff_F_def coeff_def
    apply simp
    apply (rule arg_cong[where ?f="ฮปf. (โˆ‘d = 0..q. _ d * f d)"])
    apply (rule ext)
    subgoal for d
      apply (subst lookup_Abs_poly_mapping)
      subgoal using finite_when[where ?f="ฮป_. d"] by simp
    proof (cases d)
      case 0
      then show "(โˆva. f va ^ (d when va = v)) = f v ^ d"
        apply (subst `d = 0`)
        by simp
    next
      case (Suc nat)
      then show "(โˆva. f va ^ (d when va = v)) = f v ^ d"
        apply (subst pow_when)
        by simp_all
    qed
    done
qed

(* Auxiliary lemma *)
lemma triangular_sum: "(โˆ‘k=0..(n::nat). (โˆ‘j=0..k. f j k)) = (โˆ‘j=0..(n::nat). (โˆ‘k=j..n. f j k))"
proof -
  have 0: "kโˆˆ{0..n} โŸน (โˆ‘j=0..k. f j k) = (โˆ‘j=0..n. (if jโ‰คk then f j k else 0))" for k
  proof -
    assume k_prop: "kโˆˆ{0..n}"
    hence "(โˆ‘j=0..k. f j k) = (โˆ‘j=0..k. f j k) + (โˆ‘j=(k+1)..n. 0)" by simp
    also have "... = (โˆ‘j=0..k. (if jโ‰คk then f j k else 0)) + (โˆ‘j=(k+1)..n. (if jโ‰คk then f j k else 0))"
      by simp
    also have "... = (โˆ‘j=0..n. (if jโ‰คk then f j k else 0))"
      using sum.union_disjoint[of "{0..k}" "{(k+1)..n}"]
      by (smt (verit) atLeastAtMost_iff k_prop le_iff_add sum.ub_add_nat zero_order(1))
    finally show ?thesis by blast
  qed
  have 1: "jโˆˆ{0..n} โŸน (โˆ‘k=j..n. f j k) = (โˆ‘k=0..n. (if jโ‰คk then f j k else 0))" for j
  proof -
    assume j_prop: "jโˆˆ{0..n}"
    hence "(โˆ‘k=j..n. f j k) = (โˆ‘k<j. 0) + (โˆ‘k=j..n. f j k)" by simp
    also have "... = (โˆ‘k<j. (if jโ‰คk then f j k else 0)) + (โˆ‘k=j..n. (if jโ‰คk then f j k else 0))"
      by simp
    also have "... = (โˆ‘k=0..n. (if jโ‰คk then f j k else 0))"
      using sum.union_disjoint[of "{0..<j}" "{j..n}"]
      by (smt (verit, best) atLeast0LessThan atLeastAtMost_iff finite_atLeastAtMost finite_atLeastLessThan ivl_disj_int_two(7) ivl_disj_un_two(7) j_prop)
    finally show ?thesis by blast
  qed
  hence "(โˆ‘k=0..(n::nat). (โˆ‘j=0..k. f j k)) = (โˆ‘k=0..n. (โˆ‘j=0..n. (if jโ‰คk then f j k else 0)))"
    using 0 by simp
  also have "... = (โˆ‘j=0..n. (โˆ‘k=0..n. (if jโ‰คk then f j k else 0)))" using sum.swap by fast
  finally show ?thesis using 1 by simp
qed


lemma G_expr:
  "G = (โˆ‘j=0..q. ๐’ด^j * (โˆ‘i=j..q. of_int (coeff_F i) * of_nat (i choose j) * (-โ„-๐’ฏ^2)^(i-j)))"
proof -
  have "๐’ด-โ„-๐’ฏ^2 = ๐’ด + (-โ„-๐’ฏ^2)" by simp
  hence "(๐’ด-โ„-๐’ฏ^2)^i = (โˆ‘jโ‰คi. of_nat (i choose j) * ๐’ด^j * (-โ„-๐’ฏ^2)^(i-j))" for i
    using binomial_ring[of ๐’ด "-โ„-๐’ฏ^2" i] by presburger
  hence "G = (โˆ‘i=0..q. of_int (coeff_F i) * (โˆ‘jโ‰คi. of_nat (i choose j) * ๐’ด^j * (-โ„-๐’ฏ^2)^(i-j)))"
    using G_def by presburger
  also have "... = (โˆ‘i=0..q. (โˆ‘j=0..i. of_int (coeff_F i) * of_nat (i choose j) * ๐’ด^j * (-โ„-๐’ฏ^2)^(i-j) ))"
    (is "(โˆ‘i=_.._. ?f i) = (โˆ‘i=_.._. ?g i)")
  proof (rule sum.cong[OF refl])
    fix i
    have "?f i = of_int (coeff_F i) * (โˆ‘j=0..i. of_nat (i choose j) * ๐’ด^j * (-โ„-๐’ฏ^2)^(i-j) )"
      using atMost_atLeast0 by simp
    thus "?f i = ?g i"
      unfolding mult.assoc
      by (simp add: sum_distrib_left)
  qed
  also have "... = (โˆ‘j=0..q. (โˆ‘i=j..q. of_int (coeff_F i) * of_nat (i choose j) * ๐’ด^j * (-โ„-๐’ฏ^2)^(i-j) ))"
    using triangular_sum[of "(ฮปj. (ฮปi. of_int (coeff_F i) * of_nat (i choose j) * ๐’ด^j * (-โ„-๐’ฏ^2)^(i-j)))" q]
    by blast
  also have "... = (โˆ‘j=0..q. (โˆ‘i=j..q. ๐’ด^j * of_int (coeff_F i) * of_nat (i choose j) * (-โ„-๐’ฏ^2)^(i-j) ))"
    apply (rule sum.cong[OF refl])
    apply (rule sum.cong[OF refl])
    by simp
  also have "... = (โˆ‘j=0..q. ๐’ด^j * (โˆ‘i=j..q. of_int (coeff_F i) * of_nat (i choose j) * (-โ„-๐’ฏ^2)^(i-j) ))"
    (is "(โˆ‘j=_.._. ?f j) = (โˆ‘j=_.._. ?g j)")
    apply (rule sum.cong[OF refl])
    unfolding mult.assoc
    by (simp add: sum_distrib_left)
  finally show ?thesis by simp
qed


lemma G_in_Y: "G = (โˆ‘j=0..q. ๐’ด^j * G_sub j)" 
  unfolding G_sub_def using G_expr by blast

lemma Gq_eq_1: "G_sub q = 1"
proof -
  have "coeff_F q = 1" using q_def F_one unfolding coeff_F_def by simp
  have "G_sub q = of_int (coeff_F q)" unfolding G_sub_def by simp
  also have "... = of_int 1" using q_def using โ€นcoeff_F q = 1โ€บ by presburger
  finally show ?thesis using Const0_one one_mpoly_def by simp
qed

lemma lemma_J_div_Z :
  "(โˆ‘j'=0..q. insertion f (G_sub j') * z^j' * j^(q-j')) = 0 โŸน j dvd z" for f z j
proof (cases "j=0 โˆง z=0")
  case True
  then show ?thesis by blast
next
  case False
  assume assm: "(โˆ‘j'=0..q. insertion f (G_sub j') * z^j' * j^(q-j')) = 0"
  define d where "d = gcd j z"
  have "d dvd j" using d_def by blast
  then obtain j0 where j0_def: "j = d * j0" by blast
  have "d dvd z" using d_def by blast
  then obtain z0 where z0_def: "z = d * z0" by blast
  have "gcd j z = d * gcd j0 z0" using d_def j0_def z0_def
    by (metis abs_gcd_int gcd_mult_distrib_int)
  hence coprime: "gcd j0 z0 = 1" using d_def using False by simp

  have dn0: "dโ‰ 0" using False d_def by simp

  have "j'โ‰คq โŸน j' + (q-j') =q" for j' by simp
  hence nat_pow: "j'โ‰คq โŸน d^j' * d^(q-j') = d^q" for j' using power_add by metis
  have "z^j' * j^(q-j') = d^j' * z0^j' * d^(q-j') * j0^(q-j')" for j'
    using j0_def z0_def by (simp add: power_mult_distrib)
  hence "j'โ‰คq โŸน z^j' * j^(q-j') =  d^q * z0^j' * j0^(q-j')" for j'
    using power_add nat_pow by simp
  hence "0 = (โˆ‘j'=0..q. insertion f (G_sub j') * d^q * z0^j' * j0^(q-j'))"
    using assm
    by (metis (no_types, lifting) ab_semigroup_mult_class.mult_ac(1) atLeastAtMost_iff sum.cong)
  also have "... = (โˆ‘j'=0..q. d^q * insertion f (G_sub j') * z0^j' * j0^(q-j'))"
    using mult.commute by (metis (no_types, opaque_lifting))
  also have "... = d^q * (โˆ‘j'=0..q. insertion f (G_sub j') * z0^j' * j0^(q-j'))"
    using sum_distrib_left[of "d^q" "(ฮปj'. insertion f (G_sub j') * z0^j' * j0^(q-j'))" "{0..q}"]
    by (metis (no_types, lifting) ab_semigroup_mult_class.mult_ac(1) sum.cong)
  finally have pol_red_0: "(โˆ‘j'=0..q. insertion f (G_sub j') * z0^j' * j0^(q-j')) = 0"
    using dn0 by simp

  have inser_Gq_1: "insertion f (G_sub q) = 1" using Gq_eq_1 by simp
  have j0_pow: "j'<q โŸน j0^(q-j') = j0^(q-j'-1) * j0" for j'
    by (metis power_minus_mult zero_less_diff)
  have "(โˆ‘j'=0..q. insertion f (G_sub j') * z0^j' * j0^(q-j'))
        = (โˆ‘j'=0..<q. insertion f (G_sub j') * z0^j' * j0^(q-j')) + insertion f (G_sub q) * z0^q * j0^(q-q)"
    by (simp add: sum.last_plus)
  also have "... = (โˆ‘j'=0..<q. insertion f (G_sub j') * z0^j' * j0^(q-j')) + z0^q"
    using inser_Gq_1 by simp
  also have "... = (โˆ‘j'=0..<q. insertion f (G_sub j') * z0^j' * j0^(q-j'-1)*j0) + z0^q"
    using j0_pow sum.cong by (simp add: ab_semigroup_mult_class.mult_ac(1))
  also have "... = (โˆ‘j'=0..<q. insertion f (G_sub j') * z0^j' * j0^(q-j'-1)) * j0 + z0^q"
    using sum_distrib_right[of _ _ "j0"] by metis
  finally have "z0^q = -(โˆ‘j'=0..<q. insertion f (G_sub j') * z0^j' * j0^(q-j'-1)) * j0"
    using pol_red_0 by simp
  hence "j0 dvd z0^q" by simp
  hence "j0 dvd z0" using coprime
    by (metis coprime_dvd_mult_left_iff coprime_dvd_mult_right_iff coprime_power_right_iff is_unit_gcd one_dvd)
  thus "j dvd z" using j0_def z0_def by simp
qed

lemma lemma_pos: 
  assumes "(โˆ‘j'=0..q. insertion f (G_sub j') * z^j' * j^(q-j')) = 0"
          "jโ‰ 0" "z = z' * j"
  shows "insertion (ฮป_. z' - f 7 - (f 5)^2) โ„ฑ = 0"
proof -
  have "z^j' = z'^j' * j^j'" for j' using power_mult_distrib[of "z'" "j" "j'"] assms by blast
  hence 0: "0 = (โˆ‘j'=0..q. insertion f (G_sub j') * z'^j' * j^j' * j^(q-j'))" using assms
    by (metis (no_types, lifting) ab_semigroup_mult_class.mult_ac(1) sum.cong)
  also have "... = (โˆ‘j'=0..q. insertion f (G_sub j') * z'^j' * j^(j'+(q-j')))"
    using power_add[of "j"] by (metis (no_types, lifting) ab_semigroup_mult_class.mult_ac(1))
  also have "... = (โˆ‘j'=0..q. insertion f (G_sub j') * z'^j' * j^q)" by simp
  also have "... = (โˆ‘j'=0..q. insertion f (G_sub j') * z'^j') * j^q"
    using sum_distrib_right[of _ _ "j^q"] by metis
  finally have G_is_0: "(โˆ‘j'=0..q. insertion f (G_sub j') * z'^j') = 0" using assms by simp

  define f' where "f' = (ฮปx. f x)(8:=z')" (* variable 8 is Y *)
  hence "insertion f' ๐’ด = z'"
    by (simp add: ๐’ด_def)
  hence ins_f'_Y: "insertion f' (๐’ด^j') = z'^j'" for j'::nat using insertion_pow by blast
  have ins_f'_Gsub: "insertion f' (G_sub j') = insertion f (G_sub j')" for j'
  (* Basically f and f' only differ on Y, and G_sub doesn't contain Y *)
  (* A better proof could be done with lemma insertion_irrelevant_vars *)
  proof (rule insertion_irrelevant_vars)
    fix v
    assume H: "v โˆˆ vars (G_sub j')"
    have "vars (G_sub j') โІ vars โ„ โˆช vars ๐’ฏ"
      unfolding G_sub_def
      apply (rule subset_trans[OF vars_setsum])
      subgoal by simp
      apply (rule union_subset)
      subgoal for x
        apply (rule subset_trans[OF vars_mult])
        apply (rule Un_least)
         apply (rule subset_trans[OF vars_mult])
         apply (subst of_int_Const)
         apply (subst of_nat_Const)
        using vars_Const
        subgoal by simp
        apply (rule subset_trans[OF vars_pow])
        apply (subst diff_conv_add_uminus)
        apply (rule subset_trans[OF vars_add])
        apply (rule Un_mono)
        using vars_neg
        subgoal by auto
        apply (subst vars_neg)
        apply (subst power2_eq_square)
        apply (rule subset_trans[OF vars_mult])
        by simp
      done
    also have "... โІ {5, 7}"
      unfolding defs
      unfolding MPoly_Type.Var.abs_eq[symmetric]
      by simp
    finally have "v โ‰  8"
      using H
      by auto
    thus "f' v = f v"
      unfolding f'_def
      by auto
  qed

  have "(โˆ‘j'=0..q. insertion f' (G_sub j') * insertion f' (๐’ด^j')) = 0"
    using ins_f'_Y ins_f'_Gsub G_is_0 by presburger
  hence "(โˆ‘j'=0..q. insertion f' (G_sub j' * ๐’ด^j')) = 0"
    using insertion_mult by (metis (no_types, lifting) sum.cong)
  hence "insertion f' (โˆ‘j'=0..q. G_sub j' * ๐’ด^j') = 0" using insertion_sum[of "{0..q}" "f'"] by simp
  hence "insertion f' G = 0" using G_in_Y mult.commute
    by (metis (no_types, lifting) sum.cong)

  hence "(โˆ‘i = 0..q. insertion f' (of_int (coeff_F i) * (๐’ด - โ„ - ๐’ฏ2) ^ i)) = 0"
    unfolding G_def using insertion_sum[of "{0..q}" "f'"] by simp 
  hence "(โˆ‘i = 0..q. coeff_F i * insertion f' ((๐’ด - โ„ - ๐’ฏ2) ^ i)) = 0"
    by simp
  hence 1: "(โˆ‘i = 0..q. coeff_F i * (insertion f' (๐’ด - โ„ - ๐’ฏ2)) ^ i) = 0"
    using insertion_pow[of "f'"] by simp
  
  have "insertion f' (๐’ด - โ„ - ๐’ฏ2) = z' - f 7 - (f 5)^2"
  proof -
    have "insertion f' (๐’ด - โ„ - ๐’ฏ2) = insertion f' ๐’ด + insertion f' (-โ„-๐’ฏ*๐’ฏ)"
      using insertion_add[of "f'" ๐’ด "-โ„-๐’ฏ*๐’ฏ"] power2_eq_square[of ๐’ฏ] add.assoc
      by (metis ab_group_add_class.ab_diff_conv_add_uminus)
    also have "... = insertion f' ๐’ด - insertion f' โ„ - insertion f' (๐’ฏ*๐’ฏ)"
      by simp
    also have "... = insertion f' ๐’ด - insertion f' โ„ - (insertion f' ๐’ฏ)^2"
      using insertion_mult[of "f'" ๐’ฏ ๐’ฏ] power2_eq_square by metis
    also have "... = f' 8 - f' 7 - (f' 5)^2"
      unfolding ๐’ด_def โ„_def ๐’ฏ_def by simp
    finally show ?thesis using f'_def by simp
  qed

  hence "(โˆ‘i = 0..q. coeff_F i * (z' - f 7 - (f 5)^2) ^ i) = 0" using 1 by simp
  thus "insertion (ฮป_. z' - f 7 - (f 5)^2) โ„ฑ = 0"
    using q_def eval_F
    by simp
qed

lemma ฮ _encodes_correctly:
  fixes S T R I :: int
  assumes "S โ‰  0" 
          "(โˆ€x. insertion (ฮป_. x) โ„ฑ = 0 โŸถ x > -I)"
      and "S dvd T"
          "R > 0"
          "insertion (ฮป_. y) โ„ฑ = 0"
  defines "ฯ† n โ‰ก (ฮป_. 0)(4:= S, 5:=T, 6:=R, 7:=I, 11:=int n)"
  shows "โˆƒn :: nat. insertion (ฯ† n) ฮ  = 0 โˆง n = (2*R-1)*(y + I + T^2) - (T div S) ^ 2"
proof -
  obtain TdS where TdS_def: "T = TdS * S" using โ€นS dvd Tโ€บ by force
  define ni where "ni = (2*R-1)*(y+I+T^2) - TdS^2"
  have nBe0: "niโ‰ฅ0"
  proof -
    have 0: "(2*R-1) โ‰ฅ 1" using assms by simp
    have "y > -I" using assms by blast
    hence 1: "y+I+T^2 > T^2" by linarith
    have "T^2 โ‰ฅ0" by simp
    hence "y+I+T^2 > 0" using 1 by linarith
    hence "(2*R-1)*(y+I+T^2) โ‰ฅ y+I+T^2" using 0 by simp
    hence 2: "(2*R-1)*(y+I+T^2) โ‰ฅ T^2" using 1 by simp

    have 3: "T^2 = TdS^2 * S^2" unfolding TdS_def using power_mult_distrib by blast
    have "S^2 > 0" using assms by simp
    hence 4: "S^2 โ‰ฅ 1" by linarith
    have "TdS^2 โ‰ฅ 0" by simp
    hence "TdS^2 * S^2 โ‰ฅ TdS^2" using 4 by (metis mult_cancel_left1 mult_left_mono)
    hence "T^2 โ‰ฅ TdS^2" using 3 by simp

    thus ?thesis unfolding ni_def using 2 by simp
  qed
  then obtain n where n_def: "int n = ni" using nonneg_int_cases by metis

  define f::"natโ‡’int" where "f = (ฮป_. 0) (4 := S, 5 := T, 6 := R, 7 := I, 11 := int n)"
  have 0: "insertion f ฮ 
  = (โˆ‘j = 0..q. insertion f (G_sub j * (๐’ฎ2 * ๐—‡ + ๐’ฏ2) ^ j * (๐’ฎ2 * (2 * โ„› - 1)) ^ (q - j)))"
    unfolding ฮ _def using insertion_sum by blast
  have 1: "insertion f (G_sub j * (๐’ฎ2 * ๐—‡ + ๐’ฏ2) ^ j * (๐’ฎ2 * (2 * โ„› - 1)) ^ (q - j))
       = insertion f (G_sub j) * (S^2 * n + T^2)^j * (S^2 * (2*R- 1)) ^ (q - j)" for j
  proof -
    have 0: "insertion f (G_sub j * (๐’ฎ2 * ๐—‡ + ๐’ฏ2) ^ j * (๐’ฎ2 * (2 * โ„› - 1)) ^ (q - j))
       = insertion f (G_sub j) * insertion f ((๐’ฎ2 * ๐—‡ + ๐’ฏ2) ^ j) * insertion f ((๐’ฎ2 * (2 * โ„› - 1)) ^ (q - j))"
      using insertion_mult[of f] by simp
    have 1: "insertion f ((๐’ฎ2 * ๐—‡ + ๐’ฏ2) ^ j) * insertion f ((๐’ฎ2 * (2 * โ„› - 1)) ^ (q - j))
        = (insertion f (๐’ฎ2 * ๐—‡ + ๐’ฏ2)) ^ j * (insertion f (๐’ฎ2 * (2 * โ„› - 1))) ^ (q - j)"
      using insertion_pow by metis
    have "insertion f (๐’ฎ2 * ๐—‡ + ๐’ฏ2) = insertion f (๐’ฎ*๐’ฎ) * insertion f ๐—‡ + insertion f (๐’ฏ*๐’ฏ)"
      using insertion_add[of f] insertion_mult[of f] power2_eq_square by metis
    also have "... = (insertion f ๐’ฎ)^2 * insertion f ๐—‡ + (insertion f ๐’ฏ)^2"
      using insertion_mult[of f] power2_eq_square by metis
    also have "... = S^2 * n + T^2"
      unfolding ๐’ฎ_def ๐—‡_def ๐’ฏ_def using f_def by simp
    finally have 2: "insertion f (๐’ฎ2 * ๐—‡ + ๐’ฏ2) = S^2 * n + T^2" by simp
    have "insertion f (๐’ฎ2 * (2 * โ„› - 1)) = (insertion f ๐’ฎ)^2 * insertion f (2 * โ„› - 1)"
      using insertion_mult[of f] power2_eq_square by metis
    also have "... = (insertion f ๐’ฎ)^2 * (insertion f (2*โ„›) - insertion f 1)"
      by simp
    also have "... = (insertion f ๐’ฎ)^2 * (2 * insertion f โ„› - 1)"
      by (simp add: insertion_mult[of f])
    also have "... = S^2 * (2*R-1)"
      unfolding ๐’ฎ_def โ„›_def using f_def by simp
    finally have 3: "insertion f (๐’ฎ2 * (2 * โ„› - 1)) = S^2 * (2*R-1)" by simp
    show ?thesis using 0 1 2 3 by simp
  qed
  have 2: "jโ‰คq โŸน (S^2 * n + T^2)^j * (S^2 * (2*R- 1)) ^ (q - j)
      = (y+I+T^2)^j * (S^2 * (2*R- 1)) ^ q" for j
  proof -
    assume jLeq: "jโ‰คq"
    have "S^2 * n + T^2 = S^2*(2*R-1)*(y+I+T^2) - S^2 * TdS^2 + T^2" unfolding n_def ni_def
      by (simp add: right_diff_distrib)
    also have "... = S^2*(2*R-1)*(y+I+T^2) - (S* TdS)^2 + T^2" using power_mult_distrib by metis
    also have "... = S^2*(2*R-1)*(y+I+T^2)" using TdS_def by (simp add: mult.commute)
    finally have "(S^2 * n + T^2)^j * (S^2 * (2*R- 1)) ^ (q - j)
      = (S^2*(2*R-1)*(y+I+T^2))^j * (S^2 * (2*R- 1)) ^ (q - j)" by simp
    also have "... = (y+I+T^2)^j * (S^2*(2*R-1))^j * (S^2 * (2*R- 1)) ^ (q - j)"
      using power_mult_distrib mult.commute by metis
    also have "... = (y+I+T^2)^j * (S^2 * (2*R- 1))^q" using power_add jLeq
      by (metis (no_types, lifting) add_diff_cancel_left' mult.assoc nat_le_iff_add)
    finally show ?thesis by simp
  qed           
  hence "insertion f ฮ 
       = (โˆ‘j = 0..q. insertion f (G_sub j) * (S^2 * n + T^2)^j * (S^2 * (2*R- 1)) ^ (q - j))"
    using 0 1 by presburger
  also have "... = (โˆ‘j = 0..q. insertion f (G_sub j) * (y+I+T^2)^j * (S^2 * (2*R- 1)) ^ q)"
    using 2 by (metis (no_types, lifting) ab_semigroup_mult_class.mult_ac(1) atLeastAtMost_iff sum.cong)
  finally have inser_expr_1: "insertion f ฮ  =
              (โˆ‘j = 0..q. insertion f (G_sub j) * (y+I+T^2)^j) * (S^2 * (2*R- 1)) ^ q"
    using sum_distrib_right[of _ _ "(S^2 * (2*R- 1)) ^ q"] by metis

  have inser_G_sub:  "insertion f (G_sub j) = (โˆ‘i=j..q. coeff_F i * of_nat (i choose j) * (-I-T^2)^(i-j))"
    for j
  proof -
    have "insertion f (G_sub j)
        = insertion f (โˆ‘i=j..q. of_int (coeff_F i) * of_nat (i choose j) * (-โ„-๐’ฏ^2)^(i-j))"
      unfolding G_sub_def by blast 
    also have "... = (โˆ‘i=j..q. insertion f (of_int (coeff_F i) * of_nat (i choose j) * (-โ„-๐’ฏ^2)^(i-j)))"
      using insertion_sum by blast
    also have "... = (โˆ‘i=j..q. (coeff_F i) * of_nat (i choose j) * insertion f ((-โ„-๐’ฏ^2)^(i-j)))"
    proof -
      have 1: "insertion f (of_int (coeff_F i) * of_nat (i choose j) * (-โ„-๐’ฏ^2)^(i-j))
         = (coeff_F i) * insertion f (of_nat (i choose j) * (-โ„-๐’ฏ^2)^(i-j))" for i
        using mult.assoc insertion_of_int_times by metis
      moreover have "(coeff_F i) * insertion f (of_nat (i choose j) * (-โ„-๐’ฏ^2)^(i-j))
         = (coeff_F i) * (i choose j) * insertion f ((-โ„-๐’ฏ^2)^(i-j))" for i
          using insertion_mult 
          by (smt (verit) ab_semigroup_mult_class.mult_ac(1) insertion_of_int_times mult.commute 
              of_int_of_nat_eq) 
      ultimately show ?thesis by presburger
    qed
    also have "... = (โˆ‘i=j..q. (coeff_F i) * of_nat (i choose j) * (insertion f (-โ„-๐’ฏ^2))^(i-j))"
    proof -
      have "insertion f ((-โ„-๐’ฏ^2)^(i-j)) = (insertion f (-โ„-๐’ฏ^2))^(i-j)" for i
        using insertion_pow by blast
      thus ?thesis by simp
    qed
    also have "... = (โˆ‘i=j..q. (coeff_F i) * of_nat (i choose j) * (-insertion f โ„ - (insertion f ๐’ฏ) ^2 )^(i-j))"
    proof -
      have "insertion f (-โ„-๐’ฏ^2) = -insertion f (โ„ + ๐’ฏ^2)" by simp
      also have "... = -insertion f โ„ - (insertion f (๐’ฏ^2))" by simp
      finally have "insertion f (-โ„-๐’ฏ^2) = -insertion f โ„ - (insertion f ๐’ฏ) ^2"
        using insertion_mult[of "f" ๐’ฏ ๐’ฏ] power2_eq_square by metis
      thus ?thesis by simp
    qed
    also have "... = (โˆ‘i=j..q. coeff_F i * of_nat (i choose j) * (-I-T^2)^(i-j))"
      unfolding defs using f_def by simp
    finally show ?thesis by blast
  qed

  hence "(โˆ‘j = 0..q. insertion f (G_sub j) * (y+I+T^2)^j)
      = (โˆ‘j = 0..q. (โˆ‘i=j..q. coeff_F i * of_nat (i choose j) * (-I-T^2)^(i-j)) * (y+I+T^2)^j)"
    using inser_G_sub by presburger
  also have "... = (โˆ‘j = 0..q. (โˆ‘i=j..q. coeff_F i * of_nat (i choose j) * (-I-T^2)^(i-j) * (y+I+T^2)^j))"
  proof -
    have "(โˆ‘i=j..q. coeff_F i * of_nat (i choose j) * (-I-T^2)^(i-j)) * (y+I+T^2)^j
         = (โˆ‘i=j..q. coeff_F i * of_nat (i choose j) * (-I-T^2)^(i-j) * (y+I+T^2)^j)" for j
      using sum_distrib_right by blast
    thus ?thesis by presburger
  qed
  also have "... = (โˆ‘i = 0..q. (โˆ‘j=0..i. coeff_F i * of_nat (i choose j) * (-I-T^2)^(i-j) * (y+I+T^2)^j))"
    using triangular_sum[of "(ฮปj. (ฮปi. coeff_F i * of_nat (i choose j) * (-I-T^2)^(i-j) * (y+I+T^2)^j))" q]
    by argo
  also have "... = (โˆ‘i = 0..q. coeff_F i * (โˆ‘j=0..i. of_nat (i choose j) * (-I-T^2)^(i-j) * (y+I+T^2)^j))"
  proof -
    have "(โˆ‘j=0..i. coeff_F i *  of_nat (i choose j) * (-I-T^2)^(i-j) * (y+I+T^2)^j)
         = coeff_F i * (โˆ‘j=0..i. of_nat (i choose j) * (-I-T^2)^(i-j) * (y+I+T^2)^j)" for i
      using sum_distrib_left[of "coeff_F i" "(ฮปj. of_nat (i choose j) * (-I-T^2)^(i-j) * (y+I+T^2)^j)" "{0..i}"]
      by (metis (no_types, lifting) ab_semigroup_mult_class.mult_ac(1) sum.cong)
    thus ?thesis by presburger
  qed
  also have "... = (โˆ‘i = 0..q. coeff_F i * y^i)"
  proof -
    have "(โˆ‘j=0..i. of_nat (i choose j) * (-I-T^2)^(i-j) * (y+I+T^2)^j) = y^i" for i
      using binomial_ring[of "y+I+T^2" "-I-T^2" i]
      by (smt (verit, best) ab_semigroup_mult_class.mult_ac(1) atLeast0AtMost mult.commute sum.cong)
    thus ?thesis by simp
  qed
  also have "... = insertion (ฮป_. y) โ„ฑ" using q_def eval_F by metis
  also have "... = 0" using assms by blast
  finally have "insertion f ฮ  = 0" using inser_expr_1 by presburger

  hence "insertion (ฯ† n) ฮ  = 0" 
    unfolding f_def ฯ†_def by auto

  moreover have "n = (2*R-1)*(y + I + T^2) - (T div S) ^ 2" 
    unfolding n_def ni_def TdS_def using โ€นS โ‰  0โ€บ by auto

  ultimately show "?thesis" by auto
qed



lemma ฮ _encodes_correctly_2:
  fixes S T R I :: int
  assumes "S โ‰  0" 
          "(โˆ€x. insertion (ฮป_. x) โ„ฑ = 0 โŸถ x > -I)"
  defines "ฯ† n โ‰ก (ฮป_. 0)(4:= S, 5:=T, 6:=R, 7:=I, 11:=int n)"
  assumes "โˆƒn :: nat. insertion (ฯ† n) ฮ  = 0"
  shows "S dvd T โˆง R > 0 โˆง (โˆƒx::int. (insertion (ฮป_. x) โ„ฑ) = 0)"
proof - 
  from assms obtain n where 
    prop_2_n: "insertion ((ฮป_. 0) (4 := S, 5:=T, 6:=R, 7:= I, 11:= int n)) ฮ  = 0"
    unfolding ฯ†_def by auto
  define subst::"natโ‡’int" where "subst = ((ฮป_. 0) (4 := S, 5 := T, 6 := R, 7 := I, 11 := n))"
  define J::int where "J = S^2 * (2*R-1)"
  have "2*R-1 โ‰  0" by presburger
  hence Jnot0: "Jโ‰ 0" unfolding J_def using assms by simp

  have "0 = insertion subst (โˆ‘j=0..q. G_sub j * (๐’ฎ^2 * ๐—‡ + ๐’ฏ^2)^j * (๐’ฎ^2*(2*โ„›-1))^(q-j))"
    using prop_2_n unfolding ฮ _def[symmetric] subst_def by argo
  also have "... = (โˆ‘j=0..q. insertion subst (G_sub j * (๐’ฎ^2 * ๐—‡ + ๐’ฏ^2)^j * (๐’ฎ^2*(2*โ„›-1))^(q-j)))"
    using insertion_sum by blast
  also have "... = (โˆ‘j=0..q. insertion subst (G_sub j) * insertion subst ((๐’ฎ^2 * ๐—‡ + ๐’ฏ^2)^j * (๐’ฎ^2*(2*โ„›-1))^(q-j)))"
  proof -
    have "insertion subst (G_sub j * (๐’ฎ^2 * ๐—‡ + ๐’ฏ^2)^j * (๐’ฎ^2*(2*โ„›-1))^(q-j))
          = insertion subst (G_sub j) * insertion subst ((๐’ฎ^2 * ๐—‡ + ๐’ฏ^2)^j * (๐’ฎ^2*(2*โ„›-1))^(q-j))" for j
      using insertion_mult[of subst] by simp
    thus ?thesis by presburger
  qed
  also have "... = (โˆ‘j=0..q. insertion subst (G_sub j) * (S^2 * n + T^2)^j * (S^2*(2*R-1))^(q-j))"
  proof -
    have "insertion subst ((๐’ฎ^2 * ๐—‡ + ๐’ฏ^2)^j * (๐’ฎ^2*(2*โ„›-1))^(q-j)) = (S^2 * n + T^2)^j * (S^2*(2*R-1))^(q-j)" for j
    proof -
    have 0: "insertion subst ((๐’ฎ2 * ๐—‡ + ๐’ฏ2) ^ j * (๐’ฎ2 * (2 * โ„› - 1)) ^ (q - j))
       = insertion subst ((๐’ฎ2 * ๐—‡ + ๐’ฏ2) ^ j) * insertion subst ((๐’ฎ2 * (2 * โ„› - 1)) ^ (q - j))"
      using insertion_mult[of subst] by simp
    have 1: "insertion subst ((๐’ฎ2 * ๐—‡ + ๐’ฏ2) ^ j) * insertion subst ((๐’ฎ2 * (2 * โ„› - 1)) ^ (q - j))
        = (insertion subst (๐’ฎ2 * ๐—‡ + ๐’ฏ2)) ^ j * (insertion subst (๐’ฎ2 * (2 * โ„› - 1))) ^ (q - j)"
      using insertion_pow by metis
    have "insertion subst (๐’ฎ2 * ๐—‡ + ๐’ฏ2) = insertion subst (๐’ฎ*๐’ฎ) * insertion subst ๐—‡ + insertion subst (๐’ฏ*๐’ฏ)"
      using insertion_add[of subst] insertion_mult[of subst] power2_eq_square by metis
    also have "... = (insertion subst ๐’ฎ)^2 * insertion subst ๐—‡ + (insertion subst ๐’ฏ)^2"
      using insertion_mult[of subst] power2_eq_square by metis
    also have "... = S^2 * n + T^2"
      unfolding ๐’ฎ_def ๐—‡_def ๐’ฏ_def using subst_def by simp
    finally have 2: "insertion subst (๐’ฎ2 * ๐—‡ + ๐’ฏ2) = S^2 * n + T^2" by simp
    have "insertion subst (๐’ฎ2 * (2 * โ„› - 1)) = (insertion subst ๐’ฎ)^2 * insertion subst (2 * โ„› - 1)"
      using insertion_mult[of subst] power2_eq_square by metis
    also have "... = (insertion subst ๐’ฎ)^2 * (insertion subst (2*โ„›) - insertion subst 1)"
      by simp
    also have "... = (insertion subst ๐’ฎ)^2 * (2 * insertion subst โ„› - 1)"
      using insertion_of_int_times[of subst 2] by simp
    also have "... = S^2 * (2*R-1)"
      unfolding ๐’ฎ_def โ„›_def using subst_def by simp
    finally have 3: "insertion subst (๐’ฎ2 * (2 * โ„› - 1)) = S^2 * (2*R-1)" by simp
      show ?thesis using 0 1 2 3 by argo
    qed
    thus ?thesis using sum.cong by (simp add: ab_semigroup_mult_class.mult_ac(1))
  qed
  finally have ฮ _eq_0: "(โˆ‘j=0..q. insertion subst (G_sub j) * (S^2 * n + T^2)^j * (S^2*(2*R-1))^(q-j)) = 0"
    by simp
  hence J_div_ST: "J dvd S^2 * n + T^2" using lemma_J_div_Z unfolding J_def by blast
  hence "S^2 dvd S^2 * n + T^2" unfolding J_def by fastforce
  hence "S^2 dvd (S^2*n + T^2) - S^2*n" by fastforce
  hence "S^2 dvd T^2" by simp
  hence S_dvd_T: "S dvd T" by simp

  obtain Z::int where Z_prop: "S^2 * n + T^2 = Z * J" using J_div_ST by fastforce
  hence "insertion (ฮป_. Z - subst 7 - (subst 5)^2) โ„ฑ = 0"
    using ฮ _eq_0 Jnot0 lemma_pos[of subst "S^2*n+T^2" "J" "Z"]
    unfolding J_def by blast
  hence F_cancels: "insertion (ฮป_. Z - I - T^2) โ„ฑ = 0"
    unfolding subst_def by simp
  hence "Z - I - T^2 > -I"
    using assms by blast           
  hence "Z > T^2" by simp
  hence "Z โ‰  0" by fastforce
  hence SnT_not_0: "S^2 * n + T^2 โ‰  0" using Z_prop Jnot0 by simp
  have Snt: "S^2 * n + T^2 โ‰ฅ 0" using prop_2_n by simp
  hence SnT_B_0: "S^2 * n + T^2 > 0" using SnT_not_0 by linarith
  have "T^2 โ‰ฅ0" by simp
  hence ZB0:"Z > 0" using โ€นZ > T^2โ€บ by linarith
  hence "Jโ‰ค0 โŸน False"
  proof -
    assume "Jโ‰ค0"
    hence "J<0" using Jnot0 by simp
    hence "S^2 * n + T^2 < 0" using Z_prop ZB0 
      by (metis SnT_B_0 one_dvd plus_int_code(2) zdvd_not_zless zero_less_mult_pos zless_add1_eq)
    thus "False" using Snt by simp
  qed
  hence JB0:"J>0" by fastforce
  hence "S^2 * (2 * R- 1) > 0" unfolding J_def by blast
  hence "2 * R- 1 > 0"
    by (metis mult_eq_0_iff zero_eq_power2 zero_less_mult_pos zero_less_power2)
  hence "2*R > 0" by simp
  hence RB0: "R > 0" by simp
  thus "?thesis" using S_dvd_T RB0 F_cancels by blast
qed

end

end