section \<open>Misc results about polynomials\<close>

theory RRI_Misc imports
  "HOL-Computational_Algebra.Computational_Algebra"
  "Budan_Fourier.BF_Misc"
  "Polynomial_Interpolation.Ring_Hom_Poly"
begin

subsection \<open>Misc\<close>

declare pcompose_pCons[simp del]

lemma Setcompr_subset: "\<And>f P S. {f x | x. P x} \<subseteq> S = (\<forall> x. P x \<longrightarrow> f x \<in> S)"
  by blast

lemma map_cong':
  assumes "xs = map h ys" and "\<And>y. y \<in> set ys \<Longrightarrow> f (h y) = g y"
  shows "map f xs = map g ys"
  using assms map_replicate_trivial by simp

lemma nth_default_replicate_eq:
    "nth_default dflt (replicate n x) i = (if i < n then x else dflt)"
  by (auto simp: nth_default_def)

lemma square_bounded_less:
  fixes a b::"'a :: linordered_ring_strict"
  shows "-a < b \<and> b < a \<Longrightarrow> b*b < a*a"
  by (metis (no_types, lifting) leD leI minus_less_iff minus_mult_minus mult_strict_mono'
      neg_less_eq_nonneg neg_less_pos verit_minus_simplify(4) zero_le_mult_iff zero_le_square)

lemma square_bounded_le:
  fixes a b::"'a :: linordered_ring_strict"
  shows "-a \<le> b \<and> b \<le> a \<Longrightarrow> b*b \<le> a*a"
  by (metis le_less minus_mult_minus square_bounded_less)

context vector_space
begin

lemma card_le_dim_spanning:
  assumes BV: "B \<subseteq> V"
    and VB: "V \<subseteq> span B"
    and fB: "finite B"
    and dVB: "dim V \<ge> card B"
  shows "independent B"
proof -
  {
    fix a
    assume a: "a \<in> B" "a \<in> span (B - {a})"
    from a fB have c0: "card B \<noteq> 0"
      by auto
    from a fB have cb: "card (B - {a}) = card B - 1"
      by auto
    {
      fix x
      assume x: "x \<in> V"
      from a have eq: "insert a (B - {a}) = B"
        by blast
      from x VB have x': "x \<in> span B"
        by blast
      from span_trans[OF a(2), unfolded eq, OF x']
      have "x \<in> span (B - {a})" .
    }
    then have th1: "V \<subseteq> span (B - {a})"
      by blast
    have th2: "finite (B - {a})"
      using fB by auto
    from dim_le_card[OF th1 th2]
    have c: "dim V \<le> card (B - {a})" .
    from c c0 dVB cb have False by simp
  }
  then show ?thesis
    unfolding dependent_def by blast
qed

end

subsection \<open>Misc results about polynomials\<close>

lemma smult_power: "smult (x^n) (p^n) = smult x p ^ n"
  apply (induction n)
  subgoal by fastforce
  by (metis smult_power)

lemma reflect_poly_monom: "reflect_poly (monom n i) = monom n 0"
  apply (induction i)
  by (auto simp: coeffs_eq_iff coeffs_monom coeffs_reflect_poly)

lemma poly_eq_by_eval:
  fixes P Q :: "'a::{comm_ring_1,ring_no_zero_divisors,ring_char_0} poly"
  assumes h: "\<And>x. poly P x = poly Q x" shows "P = Q"
proof -
  have "poly P = poly Q" using h by fast
  thus ?thesis by (auto simp: poly_eq_poly_eq_iff)
qed

lemma poly_binomial:
  "[:(1::'a::comm_ring_1), 1:]^n = (\<Sum>k\<le>n. monom (of_nat (n choose k)) k)"
proof -
  have "[:(1::'a::comm_ring_1), 1:]^n = (monom 1 1 + 1)^n"
    by (metis (no_types, lifting) add.left_neutral add.right_neutral add_pCons
        monom_altdef pCons_one power_one_right smult_1_left)
  also have "... = (\<Sum>k\<le>n. of_nat (n choose k) * monom 1 1 ^ k)"
    apply (subst binomial_ring)
    by force
  also have "... = (\<Sum>k\<le>n. monom (of_nat (n choose k)) k)"
    by (auto simp: monom_altdef of_nat_poly)
  finally show ?thesis .
qed

lemma degree_0_iff: "degree P = 0 \<longleftrightarrow> (\<exists>a. P = [:a:])"
  by (meson degree_eq_zeroE degree_pCons_0)

interpretation poly_vs: vector_space smult
  by (simp add: vector_space_def smult_add_right smult_add_left)

lemma degree_subspace: "poly_vs.subspace {x. degree x \<le> n}"
  by (auto simp: poly_vs.subspace_def degree_add_le)

lemma monom_span:
  "poly_vs.span {monom 1 x | x. x \<le> p} = {(x::'a::field poly). degree x \<le> p}"
(is "?L = ?R")
proof
  show "?L \<subseteq> ?R"
  proof
    fix x assume "x \<in> ?L"
    moreover have hfin: "finite {P. \<exists>x \<in> {..p}. P = monom 1 x}"
      by auto
    ultimately have
      "x \<in> range (\<lambda>u. \<Sum>v\<in>{monom 1 x | x. x \<in> {..p}}. smult (u v) v)"
      by (simp add: poly_vs.span_finite)
    hence "\<exists> u. x = (\<Sum>v\<in>{monom 1 x | x. x \<in> {..p}}. smult (u v) v)"
      by (auto simp: image_iff)
    then obtain u
      where p': "x = (\<Sum>v\<in>{monom 1 x | x. x \<in> {..p}}. smult (u v) v)"
      by blast
    have "\<And>v. v \<in> {monom 1 x | x. x \<in> {..p}} \<Longrightarrow> degree (smult (u v) v) \<le> p"
      by (auto simp add: degree_monom_eq)
    hence "degree x \<le> p" using hfin
      apply (subst p')
      apply (rule degree_sum_le)
      by auto
    thus "x \<in> {x. degree x \<le> p}" by force
  qed
next
  show "?R \<subseteq> ?L"
  proof
    fix x assume "x \<in> ?R"
    hence "degree x \<le> p" by force
    hence "x = (\<Sum>i\<le>p. monom (coeff x i) i)"
      by (simp add: poly_as_sum_of_monoms')
    also have
      "... = (\<Sum>i\<le>p. smult (coeff x (degree (monom (1::'a) i))) (monom 1 i))"
      by (auto simp add: smult_monom degree_monom_eq)
    also have
      "... = (\<Sum>v\<in>{monom 1 x | x. x \<in> {..p}}. smult ((\<lambda>v. coeff x (degree v)) v) v)"
    proof (rule sum.reindex_cong)
      show "inj_on degree {monom (1::'a) x |x. x \<in> {..p}}"
      proof
        fix x
        assume "x \<in> {monom (1::'a) x |x. x \<in> {..p}}"
        hence "\<exists> a. x = monom 1 a" by blast
        then obtain a where hx: "x = monom 1 a" by blast
        fix y
        assume "y \<in> {monom (1::'a) x |x. x \<in> {..p}}"
        hence "\<exists> b. y = monom 1 b" by blast
        then obtain b where hy: "y = monom 1 b" by blast
        assume "degree x = degree y"
        thus "x = y" using hx hy by (simp add: degree_monom_eq)
      qed
      show "{..p} = degree ` {monom (1::'a) x |x. x \<in> {..p}}"
      proof
        show "{..p} \<subseteq> degree ` {monom (1::'a) x |x. x \<in> {..p}}"
        proof
          fix x assume "x \<in> {..p}"
          hence "monom (1::'a) x \<in> {monom 1 x |x. x \<in> {..p}}" by force
          moreover have "degree (monom (1::'a) x) = x"
            by (simp add: degree_monom_eq)
          ultimately show "x \<in> degree ` {monom (1::'a) x |x. x \<in> {..p}}" by auto
        qed
        show "degree ` {monom (1::'a) x |x. x \<in> {..p}} \<subseteq> {..p}"
          by (auto simp add: degree_monom_eq)
      qed
    next
      fix y assume "y \<in> {monom (1::'a) x |x. x \<in> {..p}}"
      hence "\<exists>z \<in> {..p}. y = monom (1::'a) z" by blast
      then obtain z where "y = monom (1::'a) z" by blast
      thus
        "smult (coeff x (degree (monom (1::'a) (degree y)))) (monom (1::'a) (degree y)) =
         smult (coeff x (degree y)) y"
        by (simp add: smult_monom degree_monom_eq)
    qed
    finally have "x = (\<Sum>v\<in>{monom 1 x | x. x \<in> {..p}}.
                      smult ((\<lambda>v. coeff x (degree v)) v) v)" .
    thus "x \<in> ?L" by (auto simp add: poly_vs.span_finite)
  qed
qed

lemma monom_independent:
  "poly_vs.independent {monom (1::'a::field) x | x. x \<le> p}"
proof (rule poly_vs.independent_if_scalars_zero)
  fix f::"'a poly \<Rightarrow> 'a"
  assume h: "(\<Sum>x\<in>{monom 1 x |x. x \<le> p}. smult (f x) x) = 0"
  have h': "(\<Sum>i\<le>p. monom (f (monom (1::'a) i)) i) =
            (\<Sum>x\<in>{monom (1::'a) x |x. x \<le> p}. smult (f x) x)"
  proof (rule sum.reindex_cong)
    show "inj_on degree {monom (1::'a) x |x. x \<le> p}"
      by (smt (verit) degree_monom_eq inj_on_def mem_Collect_eq zero_neq_one)
    show "{..p} = degree ` {monom (1::'a) x |x. x \<le> p}"
    proof
      show "{..p} \<subseteq> degree ` {monom (1::'a) x |x. x \<le> p}"
      proof
        fix x assume "x \<in> {..p}"
        then have "x = degree (monom (1::'a) x) \<and> x \<le> p"
          by (auto simp: degree_monom_eq)
        thus "x \<in> degree ` {monom (1::'a) x |x. x \<le> p}"
          by blast
      qed
      show "degree ` {monom (1::'a) x |x. x \<le> p} \<subseteq> {..p}"
        by (force simp: degree_monom_eq)
    qed
  qed (auto simp: degree_monom_eq smult_monom)

  fix x::"'a poly"
  assume "x \<in> {monom 1 x |x. x \<le> p}"
  then obtain y where "y \<le> p" and "x = monom 1 y" by blast
  hence "f x = coeff (\<Sum>x\<in>{monom 1 x |x. x \<le> p}. smult (f x) x) y"
    by (auto simp: coeff_sum h'[symmetric])
  thus "f x = 0"
    using h by auto
qed force

lemma dim_degree: "poly_vs.dim {x. degree x \<le> n} = n + 1"
  using poly_vs.dim_eq_card_independent[OF monom_independent]
  by (auto simp: monom_span[symmetric] card_image image_Collect[symmetric]
      inj_on_def monom_eq_iff')

lemma degree_div:
  fixes p q::"('a::idom_divide) poly"
  assumes "q dvd p"
  shows "degree (p div q) = degree p - degree q" using assms
  by (metis (no_types, lifting) add_diff_cancel_left' degree_0 degree_mult_eq
      diff_add_zero diff_zero div_by_0 dvd_div_eq_0_iff dvd_mult_div_cancel)

lemma lead_coeff_div:
  fixes p q::"('a::{idom_divide, inverse}) poly"
  assumes "q dvd p"
  shows "lead_coeff (p div q) = lead_coeff p / lead_coeff q" using assms
  by (smt (z3) div_by_0 dvd_div_mult_self lead_coeff_mult leading_coeff_0_iff
      nonzero_mult_div_cancel_right)

lemma complex_poly_eq:
  "r = map_poly of_real (map_poly Re r) + smult \<i> (map_poly of_real (map_poly Im r))"
  by (auto simp: poly_eq_iff coeff_map_poly complex_eq)

lemma complex_poly_cong:
  "(map_poly Re p = map_poly Re q \<and> map_poly Im p = map_poly Im q) = (p = q)"
  by (metis complex_poly_eq)

lemma map_poly_Im_of_real: "map_poly Im (map_poly of_real p) = 0"
  by (auto simp: poly_eq_iff coeff_map_poly)

lemma mult_map_poly_imp_map_poly:
  assumes "map_poly complex_of_real q = r * map_poly complex_of_real p"
          "p \<noteq> 0"
  shows "r = map_poly complex_of_real (map_poly Re r)"
proof -
  have h: "Im \<circ> (*) \<i> \<circ> complex_of_real = id" by fastforce
  have "map_poly complex_of_real q = r * map_poly complex_of_real p"
    using assms by blast
  also have "... = (map_poly of_real (map_poly Re r) +
                    smult \<i> (map_poly of_real (map_poly Im r))) *
                   map_poly complex_of_real p"
    using complex_poly_eq by fastforce
  also have "... = map_poly of_real (map_poly Re r * p) +
                   smult \<i> (map_poly of_real (map_poly Im r * p))"
    by (simp add: mult_poly_add_left)
  finally have "map_poly complex_of_real q =
                map_poly of_real (map_poly Re r * p) +
                smult \<i> (map_poly of_real (map_poly Im r * p))" .
  hence "0 = map_poly Im (map_poly of_real (map_poly Re r * p) +
             smult \<i> (map_poly of_real (map_poly Im r * p)))"
    by (auto simp: complex_poly_cong[symmetric] map_poly_Im_of_real)
  also have "... = map_poly of_real (map_poly Im r * p)"
    apply (rule poly_eqI)
    by (auto simp: coeff_map_poly coeff_mult)
  finally have "0 = map_poly complex_of_real (map_poly Im r) *
                    map_poly complex_of_real p"
    by auto
  hence "map_poly complex_of_real (map_poly Im r) = 0" using assms by fastforce
  thus ?thesis apply (subst complex_poly_eq) by auto
qed

lemma map_poly_dvd:
  fixes p q::"real poly"
  assumes hdvd: "map_poly complex_of_real p dvd
                    map_poly complex_of_real q" "q \<noteq> 0"
  shows "p dvd q"
proof -
  from hdvd obtain r
    where h:"map_poly complex_of_real q = r * map_poly complex_of_real p"
    by fastforce
  hence "r = map_poly complex_of_real (map_poly Re r)"
    using mult_map_poly_imp_map_poly assms by force
  hence "map_poly complex_of_real q = map_poly complex_of_real (p * map_poly Re r)"
    using h by auto
  hence "q = p * map_poly Re r" using of_real_poly_eq_iff by blast
  thus "p dvd q" by force
qed

lemma div_poly_eq_0:
  fixes p q::"('a::idom_divide) poly"
  assumes "q dvd p" "poly (p div q) x = 0" "q \<noteq> 0"
  shows "poly p x = 0"
  using assms by fastforce

lemma poly_map_poly_of_real_cnj:
    "poly (map_poly of_real p) (cnj z) = cnj (poly (map_poly of_real p) z)"
  apply (induction p)
  by auto

text \<open>
An induction rule on real polynomials, if $P \neq 0$ then either $(X-x)|P$ or
$(X-z)(X-cnj z)|P$, we induct by dividing by these polynomials.
\<close>
lemma real_poly_roots_induct:
  fixes P::"real poly \<Rightarrow> bool" and p::"real poly"
  assumes IH_real: "\<And>p x. P p \<Longrightarrow> P (p * [:-x, 1:])"
      and IH_complex: "\<And>p a b. b \<noteq> 0 \<Longrightarrow> P p
              \<Longrightarrow> P (p * [: a*a + b*b, -2*a, 1 :])"
      and H0: "\<And>a. P [:a:]"
  defines "d \<equiv> degree p"
  shows "P p"
  using d_def
proof (induction d arbitrary: p rule: less_induct)
  fix p::"real poly"
  assume IH: "(\<And>q. degree q < degree p \<Longrightarrow> P q)"
  show "P p"
  proof (cases "0 = degree p")
    fix p::"real poly" assume "0 = degree p"
    hence "\<exists> a. p = [:a:]"
      by (simp add: degree_0_iff)
    thus "P p" using H0 by blast
  next
    assume hdeg: "0 \<noteq> degree p"
    hence "\<not> constant (poly (map_poly of_real p))"
      by (metis (no_types, opaque_lifting) constant_def constant_degree of_real_eq_iff of_real_poly_map_poly)
    then obtain z::complex where h: "poly (map_poly of_real p) z = 0"
      using fundamental_theorem_of_algebra by blast
    show "P p"
    proof cases
      assume "Im z = 0"
      hence "z = Re z" by (simp add: complex_is_Real_iff)
      moreover have "[:-z, 1:] dvd map_poly of_real p"
        using h poly_eq_0_iff_dvd by blast
      ultimately have "[:-(Re z), 1:] dvd p"
        by (smt (z3) dvd_iff_poly_eq_0 h of_real_0 of_real_eq_iff of_real_poly_map_poly)
      hence 2:"P (p div [:-Re z, 1:])"
        apply (subst IH)
        using hdeg by (auto simp: degree_div)
      moreover have 1:"p = (p div [:- Re z, 1:]) * [:-Re z, 1:]"
        by (metis \<open>[:- Re z, 1:] dvd p\<close> dvd_div_mult_self)
      ultimately show "P p"
        apply (subst 1)
        by (rule IH_real[OF 2])
    next
      assume "Im z \<noteq> 0"
      hence hcnj: "cnj z \<noteq> z" by (metis cnj.simps(2) neg_equal_zero)
      have h2: "poly (map_poly of_real p) (cnj z) = 0"
        using h poly_map_poly_of_real_cnj by force
      have "[:-z, 1:] * [:-cnj z, 1:] dvd map_poly of_real p"
      proof (rule divides_mult)
        have "\<And>c. c dvd [:-z, 1:] \<Longrightarrow> c dvd [:- cnj z, 1:] \<Longrightarrow> is_unit c"
        proof -
          fix c
          assume h:"c dvd [:-z, 1:]" hence "degree c \<le> 1" using divides_degree by fastforce
          hence "degree c = 0 \<or> degree c = 1" by linarith
          thus "c dvd [:- cnj z, 1:] \<Longrightarrow> is_unit c"
          proof
            assume "degree c = 0"
            moreover have "c \<noteq> 0" using h by fastforce
            ultimately show "is_unit c" by (simp add: is_unit_iff_degree)
          next
            assume hdeg: "degree c = 1"
            then obtain x where 1:"[:-z, 1:] = x*c" using h by fastforce
            hence "degree [:-z, 1:] = degree x + degree c"
              by (metis add.inverse_neutral degree_mult_eq mult_cancel_right
                  mult_poly_0_left pCons_eq_0_iff zero_neq_neg_one)
            hence "degree x = 0" using hdeg by auto
            then obtain x' where 2: "x = [:x':]" using degree_0_iff by auto
            assume "c dvd [:-cnj z, 1:]"
            then obtain y where 3: "[:-cnj z, 1:] = y*c" by fastforce
            hence "degree [:-cnj z, 1:] = degree y + degree c"
              by (metis add.inverse_neutral degree_mult_eq mult_cancel_right
                  mult_poly_0_left pCons_eq_0_iff zero_neq_neg_one)
            hence "degree y = 0" using hdeg by auto
            then obtain y' where 4: "y = [:y':]" using degree_0_iff by auto
            moreover from hdeg obtain a b where 5:"c = [:a, b:]" and 6: "b \<noteq> 0"
              by (meson degree_eq_oneE)
            from 1 2 5 6 have "x' = inverse b" by (auto simp: field_simps)
            moreover from 3 4 5 6 have "y' = inverse b" by (auto simp: field_simps)
            ultimately have "x = y" using 2 4 by presburger
            then have "z = cnj z" using 1 3 by (metis neg_equal_iff_equal pCons_eq_iff)
            thus "is_unit c" using hcnj by argo
          qed
        qed
        thus "coprime [:- z, 1:] [:- cnj z, 1:]" by (meson not_coprimeE)
        show "[:- z, 1:] dvd map_poly complex_of_real p"
          using h poly_eq_0_iff_dvd by auto
        show "[:- cnj z, 1:] dvd map_poly complex_of_real p"
          using h2 poly_eq_0_iff_dvd by blast
      qed
      moreover have "[:- z, 1:] * [:- cnj z, 1:] =
                     map_poly of_real [:Re z*Re z + Im z*Im z, -2*Re z, 1:]"
        by (auto simp: complex_eqI)
      ultimately have hdvd:
        "map_poly complex_of_real [:Re z*Re z + Im z*Im z, -2*Re z, 1:] dvd
         map_poly complex_of_real p"
        by force
      hence "[:Re z*Re z + Im z*Im z, -2*Re z, 1:] dvd p" using map_poly_dvd
        by blast
      hence 2:"P (p div [:Re z*Re z + Im z*Im z, -2*Re z, 1:])"
        apply (subst IH)
        using hdeg by (auto simp: degree_div)
      moreover have 1:
        "p = (p div [:Re z*Re z + Im z*Im z, -2*Re z, 1:]) *
                    [:Re z*Re z + Im z*Im z, -2*Re z, 1:]"
        apply (subst dvd_div_mult_self)
        using \<open>[:Re z*Re z + Im z*Im z, -2*Re z, 1:] dvd p\<close> by auto
      ultimately show "P p"
        apply (subst 1)
        apply (rule IH_complex[of  "Im z" _ "Re z"])
        apply (meson \<open>Im z \<noteq> 0\<close>)
        by blast
    qed
  qed
qed

subsection \<open>The reciprocal polynomial\<close>

definition reciprocal_poly :: "nat \<Rightarrow> 'a::zero poly \<Rightarrow> 'a poly"
  where "reciprocal_poly p P =
          Poly (rev ((coeffs P) @ (replicate (p - degree P) 0)))"

lemma reciprocal_0: "reciprocal_poly p 0 = 0" by (simp add: reciprocal_poly_def)

lemma reciprocal_1: "reciprocal_poly p 1 = monom 1 p"
  by (simp add: reciprocal_poly_def monom_altdef Poly_append)

lemma coeff_reciprocal:
  assumes hi: "i \<le> p" and hP: "degree P \<le> p"
  shows "coeff (reciprocal_poly p P) i = coeff P (p - i)"
proof cases
  assume "i < p - degree P"
  hence "degree P < p - i" using hP by linarith
  thus "coeff (reciprocal_poly p P) i = coeff P (p - i)"
    by (auto simp: reciprocal_poly_def nth_default_append coeff_eq_0)
next
  assume h: "\<not>i < p - degree P"
  show "coeff (reciprocal_poly p P) i = coeff P (p - i)"
  proof cases
    assume "P = 0"
    thus "coeff (reciprocal_poly p P) i = coeff P (p - i)"
      by (simp add: reciprocal_0)
  next
    assume hP': "P \<noteq> 0"
    have "degree P \<ge> p - i" using h hP by linarith
    moreover hence "(i - (p - degree P)) < length (rev (coeffs P))"
      using hP' hP hi by (auto simp: length_coeffs)
    thus "coeff (reciprocal_poly p P) i = coeff P (p - i)"
      by (auto simp: reciprocal_poly_def nth_default_append coeff_eq_0 hP hP'
          nth_default_nth rev_nth calculation coeffs_nth length_coeffs_degree)
  qed
qed

lemma coeff_reciprocal_less:
  assumes hn: "p < i" and hP: "degree P \<le> p"
  shows "coeff (reciprocal_poly p P) i = 0"
proof cases
  assume "P = 0"
  thus ?thesis by (auto simp: reciprocal_0)
next
  assume "P \<noteq> 0"
  thus ?thesis
    using hn
    by (auto simp: reciprocal_poly_def nth_default_append
        nth_default_eq_dflt_iff hP length_coeffs)
qed

lemma reciprocal_monom:
  assumes "n \<le> p"
  shows "reciprocal_poly p (monom a n) = monom a (p-n)"
proof (cases "a=0")
  case True
  then show ?thesis by (simp add: reciprocal_0)
next
  case False
  with \<open>n\<le>p\<close> show ?thesis
    apply (rule_tac poly_eqI)
    by (metis coeff_monom coeff_reciprocal coeff_reciprocal_less
        diff_diff_cancel diff_le_self lead_coeff_monom not_le_imp_less)
qed

lemma reciprocal_degree: "reciprocal_poly (degree P) P = reflect_poly P"
  by (auto simp add: reciprocal_poly_def reflect_poly_def)

lemma degree_reciprocal:
  fixes P :: "('a::zero) poly"
  assumes hP: "degree P \<le> p"
  shows "degree (reciprocal_poly p P) \<le> p"
proof (auto simp add: reciprocal_poly_def)
  have "degree (reciprocal_poly p P) \<le>
        length (replicate (p - degree P) (0::'a) @ rev (coeffs P))"
    by (metis degree_Poly reciprocal_poly_def rev_append rev_replicate)
  thus "degree (Poly (replicate (p - degree P) 0 @ rev (coeffs P))) \<le> p"
    by (smt Suc_le_mono add_Suc_right coeffs_Poly degree_0 hP le_SucE le_SucI
        le_add_diff_inverse2 le_zero_eq length_append length_coeffs_degree
        length_replicate length_rev length_strip_while_le reciprocal_0
        reciprocal_poly_def rev_append rev_replicate)
qed

lemma reciprocal_0_iff:
  assumes hP: "degree P \<le> p"
  shows "(reciprocal_poly p P = 0) = (P = 0)"
proof
  assume h: "reciprocal_poly p P = 0"
  show "P = 0"
  proof (rule poly_eqI)
    fix n
    show "coeff P n = coeff 0 n"
    proof cases
      assume hn: "n \<le> p"
      hence "p - n \<le> p" by auto
      hence "coeff (reciprocal_poly p P) (p - n) = coeff P n"
        using hP hn by (auto simp: coeff_reciprocal)
      thus ?thesis using h by auto
    next
      assume hn: "\<not> n \<le> p"
      thus ?thesis using hP by (metis coeff_0 dual_order.trans le_degree)
    qed
  qed
next
  assume "P = 0"
  thus "reciprocal_poly p P = 0" using reciprocal_0 by fast
qed

lemma poly_reciprocal:
  fixes P::"'a::field poly"
  assumes hp: "degree P \<le> p" and hx: "x \<noteq> 0"
  shows "poly (reciprocal_poly p P) x = x^p * (poly P (inverse x))"
proof -
  have "poly (reciprocal_poly p P) x
      = poly ((Poly ((replicate (p - degree P) 0) @ rev (coeffs P)))) x"
    by (auto simp add: hx reflect_poly_def reciprocal_poly_def)
  also have "... = poly ((monom 1 (p - degree P)) * (reflect_poly P)) x"
    by (auto simp add: reflect_poly_def Poly_append)
  also have "... = x^(p - degree P) *  x ^ degree P * poly P (inverse x)"
    by (auto simp add: poly_reflect_poly_nz poly_monom hx)
  also have "... = x^p * poly P (inverse x)"
    by (auto simp add: hp power_add[symmetric])
  finally show ?thesis .
qed

lemma reciprocal_fcompose:
  fixes P::"('a::{ring_char_0,field}) poly"
  assumes hP: "degree P \<le> p"
  shows "reciprocal_poly p P = monom 1 (p - degree P) * fcompose P 1 [:0, 1:]"
proof (rule poly_eq_by_eval, cases)
  fix x::'a
  assume hx: "x = 0"
  hence "poly (reciprocal_poly p P) x = coeff P p"
    using hP by (auto simp: poly_0_coeff_0 coeff_reciprocal)
  moreover have "poly (monom 1 (p - degree P)
    * fcompose P 1 [:0, 1:]) x = coeff P p"
  proof cases
    assume "degree P = p"
    thus ?thesis
      apply (induction P arbitrary: p)
      using hx by (auto simp: poly_monom degree_0_iff fcompose_pCons)
  next
    assume "degree P \<noteq> p"
    hence "degree P < p" using hP by auto
    thus ?thesis
      using hx by (auto simp: poly_monom coeff_eq_0)
  qed
  ultimately show "poly (reciprocal_poly p P) x = poly (monom 1 (p - degree P) * fcompose P 1 [:0, 1:]) x"
    by presburger
next
  fix x::'a assume "x \<noteq> 0"
  thus "poly (reciprocal_poly p P) x =
        poly (monom 1 (p - degree P) * fcompose P 1 [:0, 1:]) x"
    using hP
    by (auto simp: poly_reciprocal poly_fcompose inverse_eq_divide
        poly_monom power_diff)
qed

lemma reciprocal_reciprocal:
  fixes P :: "'a::{field,ring_char_0} poly"
  assumes hP: "degree P \<le> p"
  shows "reciprocal_poly p (reciprocal_poly p P) = P"
proof (rule poly_eq_by_eval)
  fix x
  show "poly (reciprocal_poly p (reciprocal_poly p P)) x = poly P x"
  proof cases
    assume "x = 0"
    thus "poly (reciprocal_poly p (reciprocal_poly p P)) x = poly P x"
      using hP
      by (auto simp: poly_0_coeff_0 coeff_reciprocal degree_reciprocal)
  next
    assume hx: "x \<noteq> 0"
    hence "poly (reciprocal_poly p (reciprocal_poly p P)) x
        = x ^ p * (inverse x ^ p * poly P x)" using hP
      by (auto simp: poly_reciprocal degree_reciprocal)
    thus "poly (reciprocal_poly p (reciprocal_poly p P)) x = poly P x"
      using hP hx left_right_inverse_power right_inverse by auto
  qed
qed

lemma reciprocal_smult:
  fixes P :: "'a::idom poly"
  assumes h: "degree P \<le> p"
  shows "reciprocal_poly p (smult n P) = smult n (reciprocal_poly p P)"
proof cases
  assume "n = 0"
  thus ?thesis by (auto simp add: reciprocal_poly_def)
next
  assume "n \<noteq> 0"
  thus ?thesis
    by (auto simp add: reciprocal_poly_def smult_Poly coeffs_smult
        rev_map[symmetric])
qed

lemma reciprocal_add:
  fixes P Q :: "'a::comm_semiring_0 poly"
  assumes "degree P \<le> p" and "degree Q \<le> p"
  shows "reciprocal_poly p (P + Q) = reciprocal_poly p P + reciprocal_poly p Q"
(is "?L = ?R")
proof (rule poly_eqI, cases)
  fix n
  assume "n \<le> p"
  then show "coeff ?L n = coeff ?R n"
    using assms by (auto simp: degree_add_le coeff_reciprocal)
next
  fix n assume "\<not>n \<le> p"
  then show "coeff ?L n = coeff ?R n"
    using assms by (auto simp: degree_add_le coeff_reciprocal_less)
qed

lemma reciprocal_diff:
  fixes P Q :: "'a::comm_ring poly"
  assumes "degree P \<le> p" and "degree Q \<le> p"
  shows "reciprocal_poly p (P - Q) = reciprocal_poly p P - reciprocal_poly p Q"
  by (metis (no_types, lifting) ab_group_add_class.ab_diff_conv_add_uminus assms
      add_diff_cancel degree_add_le degree_minus diff_add_cancel reciprocal_add)

lemma reciprocal_sum:
  fixes P :: "'a \<Rightarrow> 'b::comm_semiring_0 poly"
  assumes hP: "\<And>k. degree (P k) \<le> p"
  shows "reciprocal_poly p (\<Sum>k\<in>A. P k) = (\<Sum>k\<in>A. reciprocal_poly p (P k))"
proof (induct A rule: infinite_finite_induct)
  case (infinite A)
  then show ?case by (simp add: reciprocal_0)
next
  case empty
  then show ?case by (simp add: reciprocal_0)
next
  case (insert x F)
  assume "x \<notin> F"
     and "reciprocal_poly p (sum P F) = (\<Sum>k\<in>F. reciprocal_poly p (P k))"
     and "finite F"
  moreover hence "reciprocal_poly p (sum P (insert x F))
      = reciprocal_poly p (P x) + reciprocal_poly p (sum P F)"
    by (auto simp add: reciprocal_add hP degree_sum_le)
  ultimately show "reciprocal_poly p (sum P (insert x F))
      = (\<Sum>k\<in>insert x F. reciprocal_poly p (P k))"
    by (auto simp: Groups_Big.comm_monoid_add_class.sum.insert_if)
qed

lemma reciprocal_mult:
  fixes P Q::"'a::{ring_char_0,field} poly"
  assumes "degree (P * Q) \<le> p"
    and "degree P \<le> p" and "degree Q \<le> p"
  shows "monom 1 p * reciprocal_poly p (P * Q) =
         reciprocal_poly p P * reciprocal_poly p Q"
proof (cases "P=0 \<or> Q=0")
  case True
  then show ?thesis using assms(1)
    by (auto simp: reciprocal_fcompose fcompose_mult)
next
  case False
  then show ?thesis
    using assms
    by (auto simp: degree_mult_eq mult_monom reciprocal_fcompose fcompose_mult)
qed

lemma reciprocal_reflect_poly:
  fixes P::"'a::{ring_char_0,field} poly"
  assumes hP: "degree P \<le> p"
  shows "reciprocal_poly p P = monom 1 (p - degree P) * reflect_poly P"
proof (rule poly_eqI)
  fix n
  show "coeff (reciprocal_poly p P) n =
    coeff (monom 1 (p - degree P) * reflect_poly P) n"
  proof cases
    assume "n \<le> p"
    thus ?thesis using hP
      by (auto simp: coeff_reciprocal coeff_monom_mult coeff_reflect_poly coeff_eq_0)
  next
    assume "\<not> n \<le> p"
    thus ?thesis using hP
      by (auto simp: coeff_reciprocal_less coeff_monom_mult coeff_reflect_poly)
  qed
qed

lemma map_poly_reciprocal:
  assumes "degree P \<le> p" and "f 0 = 0"
  shows "map_poly f (reciprocal_poly p P)  = reciprocal_poly p (map_poly f P)"
proof (rule poly_eqI)
  fix n
  show "coeff (map_poly f (reciprocal_poly p P)) n =
         coeff (reciprocal_poly p (map_poly f P)) n"
  proof (cases "n\<le>p")
    case True
    then show ?thesis
      apply (subst coeff_reciprocal[OF True])
      subgoal by (meson assms(1) assms(2) degree_map_poly_le le_trans)
      by (simp add: assms(1) assms(2) coeff_map_poly coeff_reciprocal)
  next
    case False
    then show ?thesis
      by (metis assms(1) assms(2) coeff_map_poly coeff_reciprocal_less
          degree_map_poly_le dual_order.trans leI)
  qed
qed

subsection \<open>More about @{term proots_count}\<close>

lemma proots_count_monom:
  assumes "0 \<notin> A"
  shows "proots_count (monom 1 d) A = 0"
  using assms by (auto simp: proots_count_def poly_monom)

lemma proots_count_reciprocal:
  fixes P::"'a::{ring_char_0,field} poly"
  assumes hP: "degree P \<le> p" and h0: "P \<noteq> 0" and h0': "0 \<notin> A"
  shows "proots_count (reciprocal_poly p P) A = proots_count P {x. inverse x \<in> A}"
proof -
  have "proots_count (reciprocal_poly p P) A =
        proots_count (fcompose P 1 [:0, 1:]) A"
    apply (subst reciprocal_fcompose[OF hP], subst proots_count_times)
    subgoal using h0 by (metis hP reciprocal_0_iff reciprocal_fcompose)
    subgoal using h0' by (auto simp: proots_count_monom)
    done

  also have "... = proots_count P {x. inverse x \<in> A}"
  proof (rule proots_fcompose_bij_eq[symmetric])
    show "bij_betw (\<lambda>x. poly 1 x / poly [:0, 1:] x) A {x. inverse x \<in> A}"
    proof (rule bij_betw_imageI)
      show "inj_on (\<lambda>x. poly 1 x / poly [:0, 1:] x) A"
        by (simp add: inj_on_def)

      show "(\<lambda>x. poly 1 x / poly [:0, 1:] x) ` A = {x. inverse x \<in> A}"
      proof
        show "(\<lambda>x. poly 1 x / poly [:0, 1:] x) ` A \<subseteq> {x. inverse x \<in> A}"
          by force
        show "{x. inverse x \<in> A} \<subseteq> (\<lambda>x. poly 1 x / poly [:0, 1:] x) ` A"
        proof
          fix x assume "x \<in> {x::'a. inverse x \<in> A}"
          hence "x = poly 1 (inverse x) / poly [:0, 1:] (inverse x) \<and> inverse x \<in> A"
            by (auto simp: inverse_eq_divide)
          thus "x \<in> (\<lambda>x. poly 1 x / poly [:0, 1:] x) ` A" by blast
        qed
      qed
    qed
  next
    show "\<forall>c. 1 \<noteq> smult c [:0, 1:]"
      by (metis coeff_pCons_0 degree_1 lead_coeff_1 pCons_0_0 pcompose_0'
          pcompose_smult smult_0_right zero_neq_one)
  qed (auto simp: assms infinite_UNIV_char_0)
  finally show ?thesis by linarith
qed

lemma proots_count_reciprocal':
  fixes P::"real poly"
  assumes hP: "degree P \<le> p" and h0: "P \<noteq> 0"
  shows "proots_count P {x. 0 < x \<and> x < 1} =
         proots_count (reciprocal_poly p P) {x. 1 < x}"
proof (subst proots_count_reciprocal)
  show "proots_count P {x. 0 < x \<and> x < 1} =
      proots_count P {x. inverse x \<in> Collect ((<) 1)}"
    apply (rule arg_cong[of _ _ "proots_count P"])
    using one_less_inverse_iff by fastforce
qed (use assms in auto)

lemma proots_count_pos:
  assumes "proots_count P S > 0"
  shows "\<exists>x \<in> S. poly P x = 0"
proof (rule ccontr)
  assume "\<not> (\<exists>x\<in>S. poly P x = 0)"
  hence "\<And>x. x \<in> S \<Longrightarrow> poly P x \<noteq> 0" by blast
  hence "\<And>x. x \<in> S \<Longrightarrow> order x P = 0" using order_0I by blast
  hence "proots_count P S = 0" by (force simp: proots_count_def)
  thus False using assms by presburger
qed

lemma proots_count_of_root_set:
  assumes "P \<noteq> 0" "R \<subseteq> S" and "\<And>x. x\<in>R \<Longrightarrow> poly P x = 0"
  shows "proots_count P S \<ge> card R"
proof -
  have "card R \<le> card (proots_within P S)"
    apply (rule card_mono)
    subgoal using assms by auto
    subgoal using assms(2) assms(3) by (auto simp: proots_within_def)
    done
  also have "... \<le> proots_count P S"
    by (rule card_proots_within_leq[OF assms(1)])
  finally show ?thesis .
qed

lemma proots_count_of_root: assumes "P \<noteq> 0" "x\<in>S" "poly P x = 0"
  shows "proots_count P S > 0"
  using proots_count_of_root_set[of P "{x}" S] assms by force

subsection \<open>More about @{term changes}\<close>

lemma changes_nonneg: "0 \<le> changes xs"
  apply (induction xs rule: changes.induct)
  by simp_all

lemma changes_replicate_0: shows "changes (replicate n 0) = 0"
  apply (induction n)
  by auto

lemma changes_append_replicate_0: "changes (xs @ replicate n 0) = changes xs"
proof (induction xs rule: changes.induct)
  case (2 uu)
  then show ?case
    apply (induction n)
    by auto
qed (auto simp: changes_replicate_0)

lemma changes_scale_Cons:
  fixes xs:: "real list" assumes hs: "s > 0"
  shows "changes (s * x # xs) = changes (x # xs)"
  apply (induction xs rule: changes.induct)
  using assms by (auto simp: mult_less_0_iff zero_less_mult_iff)

lemma changes_scale:
  fixes xs::"('a::linordered_idom) list"
  assumes hs: "\<And>i. i < n \<Longrightarrow> s i > 0" and hn: "length xs \<le> n"
  shows "changes [s i * (nth_default 0 xs i). i \<leftarrow> [0..<n]] = changes xs"
using assms
proof (induction xs arbitrary: s n rule: changes.induct)
  case 1
  show ?case by (auto simp: map_replicate_const changes_replicate_0)
next
  case (2 uu)
  show ?case
  proof (cases n)
    case 0
    then show ?thesis by force
  next
    case (Suc m)
    hence "map (\<lambda>i. s i * nth_default 0 [uu] i) [0..<n] = [s 0 * uu] @ replicate m 0"
    proof (induction m arbitrary: n)
      case (Suc m n)
      from Suc have "map (\<lambda>i. s i * nth_default 0 [uu] i) [0..<Suc m] =
                     [s 0 * uu] @ replicate m 0"
        by meson
      hence "map (\<lambda>i. s i * nth_default 0 [uu] i) [0..<n] =
             [s 0 * uu] @ replicate m 0 @ [0]"
        using Suc by auto
      also have "... = [s 0 * uu] @ replicate (Suc m) 0"
        by (simp add: replicate_append_same)
      finally show ?case .
    qed fastforce
    then show ?thesis
      by (metis changes.simps(2) changes_append_replicate_0)
  qed
next
  case (3 a b xs s n)
  obtain m where hn: "n = m + 2"
    using 3(5)
    by (metis add_2_eq_Suc' diff_diff_cancel diff_le_self length_Suc_conv
        nat_arith.suc1 ordered_cancel_comm_monoid_diff_class.add_diff_inverse)
  hence h:
    "map (\<lambda>i. s i * nth_default 0 (a # b # xs) i) [0..<n] =
      s 0 * a # s 1 * b # map (\<lambda>i.
      (\<lambda> i. s (i+2)) i * nth_default 0 (xs) i) [0..<m]"
    apply (induction m arbitrary: n)
    by auto
  consider (neg)"a*b<0" | (nil)"b=0" | (pos)"\<not>a*b<0 \<and> \<not>b=0" by linarith
  then show ?case
  proof (cases)
    case neg
    hence
      "changes (map (\<lambda>i. s i * nth_default 0 (a # b # xs) i) [0..<n]) =
        1 + changes (s 1 * b # map (\<lambda>i. (\<lambda> i. s (i+2)) i
                                        * nth_default 0 (xs) i) [0..<m])"
      apply (subst h)
      using 3(4)[of 0] 3(4)[of 1] hn
      by (metis (no_types, lifting) changes.simps(3) mult_less_0_iff pos2
          mult_pos_pos one_less_numeral_iff semiring_norm(76) trans_less_add2)
    also have
      "... = 1 + changes (map (\<lambda>i. s (Suc i) * nth_default 0 (b # xs) i) [0..<Suc m])"
      apply (rule arg_cong[of _ _ "\<lambda> x. 1 + changes x"])
      apply (induction m)
      by auto
    also have "... = changes (a # b # xs)"
      apply (subst 3(1)[OF neg])
      using 3 neg hn by auto
    finally show ?thesis .
  next
    case nil
    hence "changes (map (\<lambda>i. s i * nth_default 0 (a # b # xs) i) [0..<n]) =
           changes (s 0 * a # map (\<lambda>i. (\<lambda> i. s (i+2)) i * nth_default 0 (xs) i) [0..<m])"
      apply (subst h)
      using 3(4)[of 0] 3(4)[of 1] hn
      by auto
    also have
      "... = changes (map
               (\<lambda>i. s (if i = 0 then 0 else Suc i) * nth_default 0 (a # xs) i)
              [0..<Suc m])"
      apply (rule arg_cong[of _ _ "\<lambda> x. changes x"])
      apply (induction m)
      by auto
    also have "... = changes (a # b # xs)"
      apply (subst 3(2))
      using 3 nil hn by auto
    finally show ?thesis .
  next
    case pos
    hence "changes (map (\<lambda>i. s i * nth_default 0 (a # b # xs) i) [0..<n]) =
           changes (s 1 * b # map (\<lambda>i. (\<lambda> i. s (i+2)) i * nth_default 0 (xs) i) [0..<m])"
      apply (subst h)
      using 3(4)[of 0] 3(4)[of 1] hn
      by (metis (no_types, lifting) changes.simps(3) divisors_zero
          mult_less_0_iff nat_1_add_1 not_square_less_zero one_less_numeral_iff
          semiring_norm(76) trans_less_add2 zero_less_mult_pos zero_less_two)
    also have
      "... = changes (map (\<lambda>i. s (Suc i) * nth_default 0 (b # xs) i) [0..<Suc m])"
      apply (rule arg_cong[of _ _ "\<lambda> x. changes x"])
      apply (induction m)
      by auto
    also have "... = changes (a # b # xs)"
      apply (subst 3(3))
      using 3 pos hn by auto
    finally show ?thesis .
  qed
qed

lemma changes_scale_const: fixes xs::"'a::linordered_idom list"
  assumes hs: "s \<noteq> 0"
  shows "changes (map ((*) s) xs) = changes xs"
  apply (induction xs rule: changes.induct)
    apply (simp, force)
  using hs by (auto simp: mult_less_0_iff zero_less_mult_iff)

lemma changes_snoc: fixes xs::"'a::linordered_idom list"
  shows "changes (xs @ [b, a]) = (if a * b < 0 then 1 + changes (xs @ [b])
         else if b = 0 then changes (xs @ [a]) else changes (xs @ [b]))"
  apply (induction xs rule: changes.induct)
  subgoal by (force simp: mult_less_0_iff)
  subgoal by (force simp: mult_less_0_iff)
  subgoal by force
  done

lemma changes_rev: fixes xs:: "'a::linordered_idom list"
  shows "changes (rev xs) = changes xs"
  apply (induction xs rule: changes.induct)
  by (auto simp: changes_snoc)

lemma changes_rev_about: fixes xs:: "'a::linordered_idom list"
  shows "changes (replicate (p - length xs) 0 @ rev xs) = changes xs"
proof (induction p)
  case (Suc p)
  then show ?case
  proof cases
    assume "\<not>Suc p \<le> length xs"
    hence "Suc p - length xs = Suc (p - length xs)" by linarith
    thus ?case using Suc.IH changes_rev by auto
  qed (auto simp: changes_rev)
qed (auto simp: changes_rev)

lemma changes_add_between:
  assumes "a \<le> x" and "x \<le> b"
  shows "changes (as @ [a, b] @ bs) = changes (as @ [a, x, b] @ bs)"
proof (induction as rule: changes.induct)
  case 1
  then show ?case using assms
    apply (induction bs)
    by (auto simp: mult_less_0_iff)
next
  case (2 c)
  then show ?case
    apply (induction bs)
    using assms by (auto simp: mult_less_0_iff)
next
  case (3 y z as)
  then show ?case
    using assms by (auto simp: mult_less_0_iff)
qed

lemma changes_all_nonneg: assumes "\<And>i. nth_default 0 xs i \<ge> 0" shows "changes xs = 0"
  using assms
proof (induction xs rule: changes.induct)
  case (3 x1 x2 xs)
  moreover assume "(\<And>i. 0 \<le> nth_default 0 (x1 # x2 # xs) i)"
  moreover hence "(\<And>i. 0 \<le> nth_default 0 (x1 # xs) i)"
    and "(\<And>i. 0 \<le> nth_default 0 (x2 # xs) i)"
    and "x1 * x2 \<ge> 0"
  proof -
    fix i
    assume h:"(\<And>i. 0 \<le> nth_default 0 (x1 # x2 # xs) i)"
    show "0 \<le> nth_default 0 (x1 # xs) i"
    proof (cases i)
      case 0
      then show ?thesis using h[of 0] by force
    next
      case (Suc nat)
      then show ?thesis using h[of "Suc (Suc nat)"] by force
    qed
    show "0 \<le> nth_default 0 (x2 # xs) i" using h[of "Suc i"] by simp
    show "x1*x2 \<ge> 0" using h[of 0] h[of 1] by simp
  qed
  ultimately show ?case by auto
qed auto

lemma changes_pCons: "changes (coeffs (pCons 0 f)) = changes (coeffs f)"
  by (auto simp: cCons_def)

lemma changes_increasing:
  assumes "\<And>i. i < length xs - 1 \<Longrightarrow> xs ! (i + 1) \<ge> xs ! i"
    and "length xs > 1"
    and "hd xs < 0"
    and "last xs > 0"
  shows "changes xs = 1"
  using assms
proof (induction xs rule:changes.induct)
  case (3 x y xs)
  consider (neg)"x*y<0" | (nil)"y=0" | (pos)"\<not>x*y<0 \<and> \<not>y=0" by linarith
  then show ?case
  proof cases
    case neg
    have "changes (y # xs) = 0"
    proof (rule changes_all_nonneg)
      fix i
      show "0 \<le> nth_default 0 (y # xs) i"
      proof (cases "i < length (y # xs)")
        case True
        then show ?thesis using 3(4)[of i]
          apply (induction i)
          subgoal using 3(6) neg by (fastforce simp: mult_less_0_iff)
          subgoal using 3(4) by (auto simp: nth_default_def)
          done
      next
        case False
        then show ?thesis by (simp add: nth_default_def)
      qed
    qed
    thus "changes (x # y # xs) = 1"
      using neg by force
  next
    case nil
    hence "xs \<noteq> []" using 3(7) by force
    have h: "\<And>i. i < length (x # xs) - 1 \<Longrightarrow> (x # xs) ! i \<le> (x # xs) ! (i + 1)"
    proof -
      fix i assume "i < length (x # xs) - 1"
      thus "(x # xs) ! i \<le> (x # xs) ! (i + 1)"
        apply (cases "i = 0")
        subgoal using 3(4)[of 0] 3(4)[of 1] \<open>xs \<noteq> []\<close> by force
        using 3(4)[of "i+1"] by simp
    qed
    have "changes (x # xs) = 1"
      apply (rule 3(2))
      using nil h \<open>xs \<noteq> []\<close> 3(6) 3(7) by auto
    thus ?thesis
      using nil by force
  next
    case pos
    hence "xs \<noteq> []" using 3(6) 3(7) by (fastforce simp: mult_less_0_iff)
    have "changes (y # xs) = 1"
    proof (rule 3(3))
      show "\<not> x * y < 0" "y \<noteq> 0"
        using pos by auto
      show "\<And>i. i < length (y # xs) - 1
        \<Longrightarrow> (y # xs) ! i \<le> (y # xs) ! (i + 1)"
        using 3(4) by force
      show "1 < length (y # xs)"
        using \<open>xs \<noteq> []\<close> by force
      show "hd (y # xs) < 0"
        using 3(6) pos by (force simp: mult_less_0_iff)
      show "0 < last (y # xs)"
        using 3(7) by force
    qed
    thus ?thesis using pos by auto
  qed
qed auto

end


(*  Title:      HOL/Analysis/Uniform_Limit.thy
    Author:     Christoph Traut, TU München
    Author:     Fabian Immler, TU München
*)

section \<open>Uniform Limit and Uniform Convergence\<close>

theory Uniform_Limit
imports Connected Summation_Tests Infinite_Sum
begin

subsection \<open>Definition\<close>

definition\<^marker>\<open>tag important\<close> uniformly_on :: "'a set \<Rightarrow> ('a \<Rightarrow> 'b::metric_space) \<Rightarrow> ('a \<Rightarrow> 'b) filter"
  where "uniformly_on S l = (INF e\<in>{0 <..}. principal {f. \<forall>x\<in>S. dist (f x) (l x) < e})"

abbreviation\<^marker>\<open>tag important\<close>
  "uniform_limit S f l \<equiv> filterlim f (uniformly_on S l)"

definition uniformly_convergent_on where
  "uniformly_convergent_on X f \<longleftrightarrow> (\<exists>l. uniform_limit X f l sequentially)"

definition uniformly_Cauchy_on where
  "uniformly_Cauchy_on X f \<longleftrightarrow> (\<forall>e>0. \<exists>M. \<forall>x\<in>X. \<forall>(m::nat)\<ge>M. \<forall>n\<ge>M. dist (f m x) (f n x) < e)"

proposition uniform_limit_iff:
  "uniform_limit S f l F \<longleftrightarrow> (\<forall>e>0. \<forall>\<^sub>F n in F. \<forall>x\<in>S. dist (f n x) (l x) < e)"
  unfolding filterlim_iff uniformly_on_def
  by (subst eventually_INF_base)
    (fastforce
      simp: eventually_principal uniformly_on_def
      intro: bexI[where x="min a b" for a b]
      elim: eventually_mono)+

lemma uniform_limitD:
  "uniform_limit S f l F \<Longrightarrow> e > 0 \<Longrightarrow> \<forall>\<^sub>F n in F. \<forall>x\<in>S. dist (f n x) (l x) < e"
  by (simp add: uniform_limit_iff)

lemma uniform_limitI:
  "(\<And>e. e > 0 \<Longrightarrow> \<forall>\<^sub>F n in F. \<forall>x\<in>S. dist (f n x) (l x) < e) \<Longrightarrow> uniform_limit S f l F"
  by (simp add: uniform_limit_iff)

lemma uniform_limit_sequentially_iff:
  "uniform_limit S f l sequentially \<longleftrightarrow> (\<forall>e>0. \<exists>N. \<forall>n\<ge>N. \<forall>x \<in> S. dist (f n x) (l x) < e)"
  unfolding uniform_limit_iff eventually_sequentially ..

lemma uniform_limit_at_iff:
  "uniform_limit S f l (at x) \<longleftrightarrow>
    (\<forall>e>0. \<exists>d>0. \<forall>z. 0 < dist z x \<and> dist z x < d \<longrightarrow> (\<forall>x\<in>S. dist (f z x) (l x) < e))"
  unfolding uniform_limit_iff eventually_at by simp

lemma uniform_limit_at_le_iff:
  "uniform_limit S f l (at x) \<longleftrightarrow>
    (\<forall>e>0. \<exists>d>0. \<forall>z. 0 < dist z x \<and> dist z x < d \<longrightarrow> (\<forall>x\<in>S. dist (f z x) (l x) \<le> e))"
  unfolding uniform_limit_iff eventually_at
  by (fastforce dest: spec[where x = "e / 2" for e])

lemma metric_uniform_limit_imp_uniform_limit:
  assumes f: "uniform_limit S f a F"
  assumes le: "eventually (\<lambda>x. \<forall>y\<in>S. dist (g x y) (b y) \<le> dist (f x y) (a y)) F"
  shows "uniform_limit S g b F"
proof (rule uniform_limitI)
  fix e :: real assume "0 < e"
  from uniform_limitD[OF f this] le
  show "\<forall>\<^sub>F x in F. \<forall>y\<in>S. dist (g x y) (b y) < e"
    by eventually_elim force
qed

subsection \<open>Exchange limits\<close>

proposition swap_uniform_limit:
  assumes f: "\<forall>\<^sub>F n in F. (f n \<longlongrightarrow> g n) (at x within S)"
  assumes g: "(g \<longlongrightarrow> l) F"
  assumes uc: "uniform_limit S f h F"
  assumes "\<not>trivial_limit F"
  shows "(h \<longlongrightarrow> l) (at x within S)"
proof (rule tendstoI)
  fix e :: real
  define e' where "e' = e/3"
  assume "0 < e"
  then have "0 < e'" by (simp add: e'_def)
  from uniform_limitD[OF uc \<open>0 < e'\<close>]
  have "\<forall>\<^sub>F n in F. \<forall>x\<in>S. dist (h x) (f n x) < e'"
    by (simp add: dist_commute)
  moreover
  from f
  have "\<forall>\<^sub>F n in F. \<forall>\<^sub>F x in at x within S. dist (g n) (f n x) < e'"
    by eventually_elim (auto dest!: tendstoD[OF _ \<open>0 < e'\<close>] simp: dist_commute)
  moreover
  from tendstoD[OF g \<open>0 < e'\<close>] have "\<forall>\<^sub>F x in F. dist l (g x) < e'"
    by (simp add: dist_commute)
  ultimately
  have "\<forall>\<^sub>F _ in F. \<forall>\<^sub>F x in at x within S. dist (h x) l < e"
  proof eventually_elim
    case (elim n)
    note fh = elim(1)
    note gl = elim(3)
    have "\<forall>\<^sub>F x in at x within S. x \<in> S"
      by (auto simp: eventually_at_filter)
    with elim(2)
    show ?case
    proof eventually_elim
      case (elim x)
      from fh[rule_format, OF \<open>x \<in> S\<close>] elim(1)
      have "dist (h x) (g n) < e' + e'"
        by (rule dist_triangle_lt[OF add_strict_mono])
      from dist_triangle_lt[OF add_strict_mono, OF this gl]
      show ?case by (simp add: e'_def)
    qed
  qed
  thus "\<forall>\<^sub>F x in at x within S. dist (h x) l < e"
    using eventually_happens by (metis \<open>\<not>trivial_limit F\<close>)
qed

subsection \<open>Uniform limit theorem\<close>

lemma tendsto_uniform_limitI:
  assumes "uniform_limit S f l F"
  assumes "x \<in> S"
  shows "((\<lambda>y. f y x) \<longlongrightarrow> l x) F"
  using assms
  by (auto intro!: tendstoI simp: eventually_mono dest!: uniform_limitD)

theorem uniform_limit_theorem:
  assumes c: "\<forall>\<^sub>F n in F. continuous_on A (f n)"
  assumes ul: "uniform_limit A f l F"
  assumes "\<not> trivial_limit F"
  shows "continuous_on A l"
  unfolding continuous_on_def
proof safe
  fix x assume "x \<in> A"
  then have "\<forall>\<^sub>F n in F. (f n \<longlongrightarrow> f n x) (at x within A)" "((\<lambda>n. f n x) \<longlongrightarrow> l x) F"
    using c ul
    by (auto simp: continuous_on_def eventually_mono tendsto_uniform_limitI)
  then show "(l \<longlongrightarrow> l x) (at x within A)"
    by (rule swap_uniform_limit) fact+
qed

lemma uniformly_Cauchy_onI:
  assumes "\<And>e. e > 0 \<Longrightarrow> \<exists>M. \<forall>x\<in>X. \<forall>m\<ge>M. \<forall>n\<ge>M. dist (f m x) (f n x) < e"
  shows   "uniformly_Cauchy_on X f"
  using assms unfolding uniformly_Cauchy_on_def by blast

lemma uniformly_Cauchy_onI':
  assumes "\<And>e. e > 0 \<Longrightarrow> \<exists>M. \<forall>x\<in>X. \<forall>m\<ge>M. \<forall>n>m. dist (f m x) (f n x) < e"
  shows   "uniformly_Cauchy_on X f"
proof (rule uniformly_Cauchy_onI)
  fix e :: real assume e: "e > 0"
  from assms[OF this] obtain M
    where M: "\<And>x m n. x \<in> X \<Longrightarrow> m \<ge> M \<Longrightarrow> n > m \<Longrightarrow> dist (f m x) (f n x) < e" by fast
  {
    fix x m n assume x: "x \<in> X" and m: "m \<ge> M" and n: "n \<ge> M"
    with M[OF this(1,2), of n] M[OF this(1,3), of m] e have "dist (f m x) (f n x) < e"
      by (cases m n rule: linorder_cases) (simp_all add: dist_commute)
  }
  thus "\<exists>M. \<forall>x\<in>X. \<forall>m\<ge>M. \<forall>n\<ge>M. dist (f m x) (f n x) < e" by fast
qed

lemma uniformly_Cauchy_imp_Cauchy:
  "uniformly_Cauchy_on X f \<Longrightarrow> x \<in> X \<Longrightarrow> Cauchy (\<lambda>n. f n x)"
  unfolding Cauchy_def uniformly_Cauchy_on_def by fast

lemma uniform_limit_cong:
  fixes f g :: "'a \<Rightarrow> 'b \<Rightarrow> ('c :: metric_space)" and h i :: "'b \<Rightarrow> 'c"
  assumes "eventually (\<lambda>y. \<forall>x\<in>X. f y x = g y x) F"
  assumes "\<And>x. x \<in> X \<Longrightarrow> h x = i x"
  shows   "uniform_limit X f h F \<longleftrightarrow> uniform_limit X g i F"
proof -
  {
    fix f g :: "'a \<Rightarrow> 'b \<Rightarrow> 'c" and h i :: "'b \<Rightarrow> 'c"
    assume C: "uniform_limit X f h F" and A: "eventually (\<lambda>y. \<forall>x\<in>X. f y x = g y x) F"
       and B: "\<And>x. x \<in> X \<Longrightarrow> h x = i x"
    {
      fix e ::real assume "e > 0"
      with C have "eventually (\<lambda>y. \<forall>x\<in>X. dist (f y x) (h x) < e) F"
        unfolding uniform_limit_iff by blast
      with A have "eventually (\<lambda>y. \<forall>x\<in>X. dist (g y x) (i x) < e) F"
        by eventually_elim (insert B, simp_all)
    }
    hence "uniform_limit X g i F" unfolding uniform_limit_iff by blast
  } note A = this
  show ?thesis by (rule iffI) (erule A; insert assms; simp add: eq_commute)+
qed

lemma uniform_limit_cong':
  fixes f g :: "'a \<Rightarrow> 'b \<Rightarrow> ('c :: metric_space)" and h i :: "'b \<Rightarrow> 'c"
  assumes "\<And>y x. x \<in> X \<Longrightarrow> f y x = g y x"
  assumes "\<And>x. x \<in> X \<Longrightarrow> h x = i x"
  shows   "uniform_limit X f h F \<longleftrightarrow> uniform_limit X g i F"
  using assms by (intro uniform_limit_cong always_eventually) blast+

lemma uniformly_convergent_cong:
  assumes "eventually (\<lambda>x. \<forall>y\<in>A. f x y = g x y) sequentially" "A = B"
  shows "uniformly_convergent_on A f \<longleftrightarrow> uniformly_convergent_on B g"
  unfolding uniformly_convergent_on_def assms(2) [symmetric]
  by (intro iff_exI uniform_limit_cong eventually_mono [OF assms(1)]) auto

lemma uniformly_convergent_on_compose:
  assumes "uniformly_convergent_on A f"
  assumes "filterlim g sequentially sequentially"
  shows   "uniformly_convergent_on A (\<lambda>n. f (g n))"
proof -
  from assms(1) obtain f' where "uniform_limit A f f' sequentially"
    by (auto simp: uniformly_convergent_on_def)
  hence "uniform_limit A (\<lambda>n. f (g n)) f' sequentially"
    by (rule filterlim_compose) fact
  thus ?thesis
    by (auto simp: uniformly_convergent_on_def)
qed

lemma uniformly_convergent_uniform_limit_iff:
  "uniformly_convergent_on X f \<longleftrightarrow> uniform_limit X f (\<lambda>x. lim (\<lambda>n. f n x)) sequentially"
proof
  assume "uniformly_convergent_on X f"
  then obtain l where l: "uniform_limit X f l sequentially"
    unfolding uniformly_convergent_on_def by blast
  from l have "uniform_limit X f (\<lambda>x. lim (\<lambda>n. f n x)) sequentially \<longleftrightarrow>
                      uniform_limit X f l sequentially"
    by (intro uniform_limit_cong' limI tendsto_uniform_limitI[of f X l]) simp_all
  also note l
  finally show "uniform_limit X f (\<lambda>x. lim (\<lambda>n. f n x)) sequentially" .
qed (auto simp: uniformly_convergent_on_def)

lemma uniformly_convergentI: "uniform_limit X f l sequentially \<Longrightarrow> uniformly_convergent_on X f"
  unfolding uniformly_convergent_on_def by blast

lemma uniformly_convergent_on_empty [iff]: "uniformly_convergent_on {} f"
  by (simp add: uniformly_convergent_on_def uniform_limit_sequentially_iff)

lemma uniformly_convergent_on_const [simp,intro]:
  "uniformly_convergent_on A (\<lambda>_. c)"
  by (auto simp: uniformly_convergent_on_def uniform_limit_iff intro!: exI[of _ c])

text\<open>Cauchy-type criteria for uniform convergence.\<close>

lemma Cauchy_uniformly_convergent:
  fixes f :: "nat \<Rightarrow> 'a \<Rightarrow> 'b :: complete_space"
  assumes "uniformly_Cauchy_on X f"
  shows   "uniformly_convergent_on X f"
unfolding uniformly_convergent_uniform_limit_iff uniform_limit_iff
proof safe
  let ?f = "\<lambda>x. lim (\<lambda>n. f n x)"
  fix e :: real assume e: "e > 0"
  hence "e/2 > 0" by simp
  with assms obtain N where N: "\<And>x m n. x \<in> X \<Longrightarrow> m \<ge> N \<Longrightarrow> n \<ge> N \<Longrightarrow> dist (f m x) (f n x) < e/2"
    unfolding uniformly_Cauchy_on_def by fast
  show "eventually (\<lambda>n. \<forall>x\<in>X. dist (f n x) (?f x) < e) sequentially"
    using eventually_ge_at_top[of N]
  proof eventually_elim
    fix n assume n: "n \<ge> N"
    show "\<forall>x\<in>X. dist (f n x) (?f x) < e"
    proof
      fix x assume x: "x \<in> X"
      with assms have "(\<lambda>n. f n x) \<longlonglongrightarrow> ?f x"
        by (auto dest!: Cauchy_convergent uniformly_Cauchy_imp_Cauchy simp: convergent_LIMSEQ_iff)
      with \<open>e/2 > 0\<close> have "eventually (\<lambda>m. m \<ge> N \<and> dist (f m x) (?f x) < e/2) sequentially"
        by (intro tendstoD eventually_conj eventually_ge_at_top)
      then obtain m where m: "m \<ge> N" "dist (f m x) (?f x) < e/2"
        unfolding eventually_at_top_linorder by blast
      have "dist (f n x) (?f x) \<le> dist (f n x) (f m x) + dist (f m x) (?f x)"
          by (rule dist_triangle)
      also from x n have "... < e/2 + e/2" by (intro add_strict_mono N m)
      finally show "dist (f n x) (?f x) < e" by simp
    qed
  qed
qed

lemma uniformly_convergent_Cauchy:
  assumes "uniformly_convergent_on X f"
  shows "uniformly_Cauchy_on X f"
proof (rule uniformly_Cauchy_onI)
  fix e::real assume "e > 0"
  then have "0 < e / 2" by simp
  with assms[unfolded uniformly_convergent_on_def uniform_limit_sequentially_iff]
  obtain l N where l:"x \<in> X \<Longrightarrow> n \<ge> N \<Longrightarrow> dist (f n x) (l x) < e / 2" for n x
    by metis
  from l l have "x \<in> X \<Longrightarrow> n \<ge> N \<Longrightarrow> m \<ge> N \<Longrightarrow> dist (f n x) (f m x) < e" for n m x
    by (rule dist_triangle_half_l)
  then show "\<exists>M. \<forall>x\<in>X. \<forall>m\<ge>M. \<forall>n\<ge>M. dist (f m x) (f n x) < e" by blast
qed

lemma uniformly_convergent_eq_Cauchy:
  "uniformly_convergent_on X f = uniformly_Cauchy_on X f" for f::"nat \<Rightarrow> 'b \<Rightarrow> 'a::complete_space"
  using Cauchy_uniformly_convergent uniformly_convergent_Cauchy by blast

lemma uniformly_convergent_eq_cauchy:
  fixes s::"nat \<Rightarrow> 'b \<Rightarrow> 'a::complete_space"
  shows
    "(\<exists>l. \<forall>e>0. \<exists>N. \<forall>n x. N \<le> n \<and> P x \<longrightarrow> dist(s n x)(l x) < e) \<longleftrightarrow>
      (\<forall>e>0. \<exists>N. \<forall>m n x. N \<le> m \<and> N \<le> n \<and> P x  \<longrightarrow> dist (s m x) (s n x) < e)"
proof -
  have *: "(\<forall>n\<ge>N. \<forall>x. Q x \<longrightarrow> R n x) \<longleftrightarrow> (\<forall>n x. N \<le> n \<and> Q x \<longrightarrow> R n x)"
    "(\<forall>x. Q x \<longrightarrow> (\<forall>m\<ge>N. \<forall>n\<ge>N. S n m x)) \<longleftrightarrow> (\<forall>m n x. N \<le> m \<and> N \<le> n \<and> Q x \<longrightarrow> S n m x)"
    for N::nat and Q::"'b \<Rightarrow> bool" and R S
    by blast+
  show ?thesis
    using uniformly_convergent_eq_Cauchy[of "Collect P" s]
    unfolding uniformly_convergent_on_def uniformly_Cauchy_on_def uniform_limit_sequentially_iff
    by (simp add: *)
qed

lemma uniformly_cauchy_imp_uniformly_convergent:
  fixes s :: "nat \<Rightarrow> 'a \<Rightarrow> 'b::complete_space"
  assumes "\<forall>e>0.\<exists>N. \<forall>m (n::nat) x. N \<le> m \<and> N \<le> n \<and> P x --> dist(s m x)(s n x) < e"
    and "\<forall>x. P x --> (\<forall>e>0. \<exists>N. \<forall>n. N \<le> n \<longrightarrow> dist(s n x)(l x) < e)"
  shows "\<forall>e>0. \<exists>N. \<forall>n x. N \<le> n \<and> P x \<longrightarrow> dist(s n x)(l x) < e"
proof -
  obtain l' where l:"\<forall>e>0. \<exists>N. \<forall>n x. N \<le> n \<and> P x \<longrightarrow> dist (s n x) (l' x) < e"
    using assms(1) unfolding uniformly_convergent_eq_cauchy[symmetric] by auto
  moreover
  {
    fix x
    assume "P x"
    then have "l x = l' x"
      using tendsto_unique[OF trivial_limit_sequentially, of "\<lambda>n. s n x" "l x" "l' x"]
      using l and assms(2) unfolding lim_sequentially by blast
  }
  ultimately show ?thesis by auto
qed

lemma uniformly_convergent_on_sum_E:
  fixes \<epsilon>::real and f :: "nat \<Rightarrow> 'a \<Rightarrow> 'b::{complete_space,real_normed_vector}"
  assumes uconv: "uniformly_convergent_on K (\<lambda>n z. \<Sum>k<n. f k z)" and "\<epsilon>>0"
  obtains N where "\<And>m n z. \<lbrakk>N \<le> m; m \<le> n; z\<in>K\<rbrakk> \<Longrightarrow> norm(\<Sum>k=m..<n. f k z) < \<epsilon>"
proof -
  obtain N where N: "\<And>m n z. \<lbrakk>N \<le> m; N \<le> n; z\<in>K\<rbrakk> \<Longrightarrow> dist (\<Sum>k<m. f k z) (\<Sum>k<n. f k z) < \<epsilon>"
    using uconv \<open>\<epsilon>>0\<close> unfolding uniformly_Cauchy_on_def uniformly_convergent_eq_Cauchy by meson
  show thesis
  proof
    fix m n z
    assume "N \<le> m" "m \<le> n" "z \<in> K"
    moreover have "(\<Sum>k = m..<n. f k z) = (\<Sum>k<n. f k z) - (\<Sum>k<m. f k z)"
      by (metis atLeast0LessThan le0 sum_diff_nat_ivl \<open>m \<le> n\<close>)
    ultimately show "norm(\<Sum>k = m..<n. f k z) < \<epsilon>"
      using N  by (simp add: dist_norm)
  qed
qed

lemma uniformly_convergent_on_sum_iff:
  fixes f :: "nat \<Rightarrow> 'a \<Rightarrow> 'b::{complete_space,real_normed_vector}"
  shows "uniformly_convergent_on K (\<lambda>n z. \<Sum>k<n. f k z)
     \<longleftrightarrow> (\<forall>\<epsilon>>0. \<exists>N. \<forall>m n z. N\<le>m \<longrightarrow> m\<le>n \<longrightarrow> z\<in>K \<longrightarrow> norm (\<Sum>k=m..<n. f k z) < \<epsilon>)" (is "?lhs=?rhs")
proof
  assume R: ?rhs
  show ?lhs
    unfolding uniformly_Cauchy_on_def uniformly_convergent_eq_Cauchy
  proof (intro strip)
    fix \<epsilon>::real
    assume "\<epsilon>>0"
    then obtain N where "\<And>m n z. \<lbrakk>N \<le> m; m \<le> n; z\<in>K\<rbrakk> \<Longrightarrow> norm(\<Sum>k = m..<n. f k z) < \<epsilon>"
      using R by blast
    then have "\<forall>x\<in>K. \<forall>m\<ge>N. \<forall>n\<ge>m. norm ((\<Sum>k<m. f k x) - (\<Sum>k<n. f k x)) < \<epsilon>"
      by (metis atLeast0LessThan le0 sum_diff_nat_ivl norm_minus_commute)
    then have "\<forall>x\<in>K. \<forall>m\<ge>N. \<forall>n\<ge>N. norm ((\<Sum>k<m. f k x) - (\<Sum>k<n. f k x)) < \<epsilon>"
      by (metis linorder_le_cases norm_minus_commute)
    then show "\<exists>M. \<forall>x\<in>K. \<forall>m\<ge>M. \<forall>n\<ge>M. dist (\<Sum>k<m. f k x) (\<Sum>k<n. f k x) < \<epsilon>"
      by (metis dist_norm)
  qed
qed (metis uniformly_convergent_on_sum_E)

lemma uniform_limit_suminf:
  fixes f:: "nat \<Rightarrow> 'a::{metric_space, comm_monoid_add} \<Rightarrow> 'a"
  assumes "uniformly_convergent_on X (\<lambda>n x. \<Sum>k<n. f k x)"
  shows "uniform_limit X (\<lambda>n x. \<Sum>k<n. f k x) (\<lambda>x. \<Sum>k. f k x) sequentially"
proof -
  obtain S where S: "uniform_limit X (\<lambda>n x. \<Sum>k<n. f k x) S sequentially"
    using assms uniformly_convergent_on_def by blast
  then have "(\<Sum>k. f k x) = S x" if "x \<in> X" for x
    using that sums_iff sums_def by (blast intro: tendsto_uniform_limitI [OF S])
  with S show ?thesis
    by (simp cong: uniform_limit_cong')
qed

text \<open>TODO: remove explicit formulations
  @{thm uniformly_convergent_eq_cauchy uniformly_cauchy_imp_uniformly_convergent}?!\<close>

lemma uniformly_convergent_imp_convergent:
  "uniformly_convergent_on X f \<Longrightarrow> x \<in> X \<Longrightarrow> convergent (\<lambda>n. f n x)"
  unfolding uniformly_convergent_on_def convergent_def
  by (auto dest: tendsto_uniform_limitI)

subsection \<open>Comparison Test\<close>

lemma uniformly_summable_comparison_test:
  fixes f :: "nat \<Rightarrow> 'a \<Rightarrow> 'b :: banach"
  assumes "uniformly_convergent_on A (\<lambda>N x. \<Sum>n<N. g n x)"
  assumes "\<And>n x. x \<in> A \<Longrightarrow> norm (f n x) \<le> g n x"
  shows   "uniformly_convergent_on A (\<lambda>N x. \<Sum>n<N. f n x)"
proof -
  have "uniformly_Cauchy_on A (\<lambda>N x. \<Sum>n<N. f n x)"
  proof (rule uniformly_Cauchy_onI')
    fix e :: real assume e: "e > 0"
    obtain M where M: "\<And>x m n. x \<in> A \<Longrightarrow> m \<ge> M \<Longrightarrow> n \<ge> M \<Longrightarrow> dist (\<Sum>k<m. g k x) (\<Sum>k<n. g k x) < e"
      using assms(1) e unfolding uniformly_convergent_eq_Cauchy uniformly_Cauchy_on_def by metis
    show "\<exists>M. \<forall>x\<in>A. \<forall>m\<ge>M. \<forall>n>m. dist (\<Sum>k<m. f k x) (\<Sum>k<n. f k x) < e"
    proof (rule exI[of _ M], safe)
      fix x m n assume xmn: "x \<in> A" "m \<ge> M" "m < n"
      have nonneg: "g k x \<ge> 0" for k
        by (rule order.trans[OF _ assms(2)]) (use xmn in auto)
      have "dist (\<Sum>k<m. f k x) (\<Sum>k<n. f k x) = norm (\<Sum>k\<in>{..<n}-{..<m}. f k x)"
        using xmn by (subst sum_diff) (auto simp: dist_norm norm_minus_commute)
      also have "{..<n}-{..<m} = {m..<n}"
        by auto
      also have "norm (\<Sum>k\<in>{m..<n}. f k x) \<le> (\<Sum>k\<in>{m..<n}. norm (f k x))"
        using norm_sum by blast
      also have "\<dots> \<le> (\<Sum>k\<in>{m..<n}. g k x)"
        by (intro sum_mono assms xmn)
      also have "\<dots> = \<bar>\<Sum>k\<in>{m..<n}. g k x\<bar>"
        by (subst abs_of_nonneg) (auto simp: nonneg intro!: sum_nonneg)
      also have "{m..<n} = {..<n} - {..<m}"
        by auto
      also have "\<bar>\<Sum>k\<in>\<dots>. g k x\<bar> = dist (\<Sum>k<m. g k x) (\<Sum>k<n. g k x)"
        using xmn by (subst sum_diff) (auto simp: abs_minus_commute dist_norm)
      also have "\<dots> < e"
        by (rule M) (use xmn in auto)
      finally show "dist (\<Sum>k<m. f k x) (\<Sum>k<n. f k x) < e" .
    qed
  qed
  thus ?thesis
    unfolding uniformly_convergent_eq_Cauchy .
qed

lemma uniform_limit_compose_uniformly_continuous_on:
  fixes f :: "'a :: metric_space \<Rightarrow> 'b :: metric_space"
  assumes lim: "uniform_limit A g g' F"
  assumes cont: "uniformly_continuous_on B f"
  assumes ev: "eventually (\<lambda>x. \<forall>y\<in>A. g x y \<in> B) F" and "closed B"
  shows   "uniform_limit A (\<lambda>x y. f (g x y)) (\<lambda>y. f (g' y)) F"
proof (cases "F = bot")
  case [simp]: False
  show ?thesis
    unfolding uniform_limit_iff
  proof safe
    fix e :: real assume e: "e > 0"

    have g'_in_B: "g' y \<in> B" if "y \<in> A" for y
    proof (rule Lim_in_closed_set)
      show "eventually (\<lambda>x. g x y \<in> B) F"
        using ev by eventually_elim (use that in auto)
      show "((\<lambda>x. g x y) \<longlongrightarrow> g' y) F"
        using lim that by (metis tendsto_uniform_limitI)
    qed (use \<open>closed B\<close> in auto)

    obtain d where d: "d > 0" "\<And>x y. x \<in> B \<Longrightarrow> y \<in> B \<Longrightarrow> dist x y < d \<Longrightarrow> dist (f x) (f y) < e"
      using e cont unfolding uniformly_continuous_on_def by metis
    from lim have "eventually (\<lambda>x. \<forall>y\<in>A. dist (g x y) (g' y) < d) F"
      unfolding uniform_limit_iff using \<open>d > 0\<close>  by meson
    thus "eventually (\<lambda>x. \<forall>y\<in>A. dist (f (g x y)) (f (g' y)) < e) F"
      using assms(3)
    proof eventually_elim
      case (elim x)
      show "\<forall>y\<in>A. dist (f (g x y)) (f (g' y)) < e"
      proof safe
        fix y assume y: "y \<in> A"
        show "dist (f (g x y)) (f (g' y)) < e"
        proof (rule d)
          show "dist (g x y) (g' y) < d"
            using elim y by blast
        qed (use y elim g'_in_B in auto)
      qed
    qed
  qed
qed (auto simp: filterlim_def)

lemma uniformly_convergent_on_compose_uniformly_continuous_on:
  fixes f :: "'a :: metric_space \<Rightarrow> 'b :: metric_space"
  assumes lim: "uniformly_convergent_on A g"
  assumes cont: "uniformly_continuous_on B f"
  assumes ev: "eventually (\<lambda>x. \<forall>y\<in>A. g x y \<in> B) sequentially" and "closed B"
  shows   "uniformly_convergent_on A (\<lambda>x y. f (g x y))"
proof -
  from lim obtain g' where g': "uniform_limit A g g' sequentially"
    by (auto simp: uniformly_convergent_on_def)
  thus ?thesis
    using uniform_limit_compose_uniformly_continuous_on[OF g' cont ev \<open>closed B\<close>]
    by (auto simp: uniformly_convergent_on_def)
qed

subsection \<open>Weierstrass M-Test\<close>

proposition Weierstrass_m_test_ev:
fixes f :: "_ \<Rightarrow> _ \<Rightarrow> _ :: banach"
assumes "eventually (\<lambda>n. \<forall>x\<in>A. norm (f n x) \<le> M n) sequentially"
assumes "summable M"
shows "uniform_limit A (\<lambda>n x. \<Sum>i<n. f i x) (\<lambda>x. suminf (\<lambda>i. f i x)) sequentially"
proof (rule uniform_limitI)
  fix e :: real
  assume "0 < e"
  from suminf_exist_split[OF \<open>0 < e\<close> \<open>summable M\<close>]
  have "\<forall>\<^sub>F k in sequentially. norm (\<Sum>i. M (i + k)) < e"
    by (auto simp: eventually_sequentially)
  with eventually_all_ge_at_top[OF assms(1)]
    show "\<forall>\<^sub>F n in sequentially. \<forall>x\<in>A. dist (\<Sum>i<n. f i x) (\<Sum>i. f i x) < e"
  proof eventually_elim
    case (elim k)
    show ?case
    proof safe
      fix x assume "x \<in> A"
      have "\<exists>N. \<forall>n\<ge>N. norm (f n x) \<le> M n"
        using assms(1) \<open>x \<in> A\<close> by (force simp: eventually_at_top_linorder)
      hence summable_norm_f: "summable (\<lambda>n. norm (f n x))"
        by(rule summable_norm_comparison_test[OF _ \<open>summable M\<close>])
      have summable_f: "summable (\<lambda>n. f n x)"
        using summable_norm_cancel[OF summable_norm_f] .
      have summable_norm_f_plus_k: "summable (\<lambda>i. norm (f (i + k) x))"
        using summable_ignore_initial_segment[OF summable_norm_f]
        by auto
      have summable_M_plus_k: "summable (\<lambda>i. M (i + k))"
        using summable_ignore_initial_segment[OF \<open>summable M\<close>]
        by auto

      have "dist (\<Sum>i<k. f i x) (\<Sum>i. f i x) = norm ((\<Sum>i. f i x) - (\<Sum>i<k. f i x))"
        using dist_norm dist_commute by (subst dist_commute)
      also have "... = norm (\<Sum>i. f (i + k) x)"
        using suminf_minus_initial_segment[OF summable_f, where k=k] by simp
      also have "... \<le> (\<Sum>i. norm (f (i + k) x))"
        using summable_norm[OF summable_norm_f_plus_k] .
      also have "... \<le> (\<Sum>i. M (i + k))"
        by (rule suminf_le[OF _ summable_norm_f_plus_k summable_M_plus_k])
           (insert elim(1) \<open>x \<in> A\<close>, simp)
      finally show "dist (\<Sum>i<k. f i x) (\<Sum>i. f i x) < e"
        using elim by auto
    qed
  qed
qed

text\<open>Alternative version, formulated as in HOL Light\<close>
corollary\<^marker>\<open>tag unimportant\<close> series_comparison_uniform:
  fixes f :: "_ \<Rightarrow> nat \<Rightarrow> _ :: banach"
  assumes g: "summable g" and le: "\<And>n x. N \<le> n \<and> x \<in> A \<Longrightarrow> norm(f x n) \<le> g n"
    shows "\<exists>l. \<forall>e. 0 < e \<longrightarrow> (\<exists>N. \<forall>n x. N \<le> n \<and> x \<in> A \<longrightarrow> dist(sum (f x) {..<n}) (l x) < e)"
proof -
  have 1: "\<forall>\<^sub>F n in sequentially. \<forall>x\<in>A. norm (f x n) \<le> g n"
    using le eventually_sequentially by auto
  show ?thesis
    apply (rule_tac x="(\<lambda>x. \<Sum>i. f x i)" in exI)
    apply (metis (no_types, lifting) eventually_sequentially uniform_limitD [OF Weierstrass_m_test_ev [OF 1 g]])
    done
qed

corollary\<^marker>\<open>tag unimportant\<close> Weierstrass_m_test:
  fixes f :: "_ \<Rightarrow> _ \<Rightarrow> _ :: banach"
  assumes "\<And>n x. x \<in> A \<Longrightarrow> norm (f n x) \<le> M n"
  assumes "summable M"
  shows "uniform_limit A (\<lambda>n x. \<Sum>i<n. f i x) (\<lambda>x. suminf (\<lambda>i. f i x)) sequentially"
  using assms by (intro Weierstrass_m_test_ev always_eventually) auto

corollary\<^marker>\<open>tag unimportant\<close> Weierstrass_m_test'_ev:
  fixes f :: "_ \<Rightarrow> _ \<Rightarrow> _ :: banach"
  assumes "eventually (\<lambda>n. \<forall>x\<in>A. norm (f n x) \<le> M n) sequentially" "summable M"
  shows   "uniformly_convergent_on A (\<lambda>n x. \<Sum>i<n. f i x)"
  unfolding uniformly_convergent_on_def by (rule exI, rule Weierstrass_m_test_ev[OF assms])

corollary\<^marker>\<open>tag unimportant\<close> Weierstrass_m_test':
  fixes f :: "_ \<Rightarrow> _ \<Rightarrow> _ :: banach"
  assumes "\<And>n x. x \<in> A \<Longrightarrow> norm (f n x) \<le> M n" "summable M"
  shows   "uniformly_convergent_on A (\<lambda>n x. \<Sum>i<n. f i x)"
  unfolding uniformly_convergent_on_def by (rule exI, rule Weierstrass_m_test[OF assms])

lemma Weierstrass_m_test_general:
  fixes f :: "'a \<Rightarrow> 'b \<Rightarrow> 'c :: banach"
  fixes M :: "'a \<Rightarrow> real"
  assumes norm_le:  "\<And>x y. x \<in> X \<Longrightarrow> y \<in> Y \<Longrightarrow> norm (f x y) \<le> M x"
  assumes summable: "M summable_on X"
  shows "uniform_limit Y (\<lambda>X y. \<Sum>x\<in>X. f x y) (\<lambda>y. \<Sum>\<^sub>\<infinity>x\<in>X. f x y) (finite_subsets_at_top X)"
proof (rule uniform_limitI)
  fix \<epsilon> :: real
  assume "\<epsilon> > 0"
  define S where "S = (\<lambda>y. \<Sum>\<^sub>\<infinity>x\<in>X. f x y)"
  have S: "((\<lambda>x. f x y) has_sum S y) X" if y: "y \<in> Y" for y
    unfolding S_def
  proof (rule has_sum_infsum)
    have "(\<lambda>x. norm (f x y)) summable_on X"
      by (rule abs_summable_on_comparison_test'[OF summable norm_le]) (use y in auto)
    thus "(\<lambda>x. f x y) summable_on X"
      by (metis abs_summable_summable)
  qed

  define T where "T = (\<Sum>\<^sub>\<infinity>x\<in>X. M x)"
  have T: "(M has_sum T) X"
    unfolding T_def by (simp add: local.summable)
  have M_summable: "M summable_on X'" if "X' \<subseteq> X" for X'
    using local.summable summable_on_subset_banach that by blast

  have f_summable: "(\<lambda>x. f x y) summable_on X'" if "X' \<subseteq> X" "y \<in> Y" for X' y
    using S summable_on_def summable_on_subset_banach that by blast
  have "eventually (\<lambda>X'. dist (\<Sum>x\<in>X'. M x) T < \<epsilon>) (finite_subsets_at_top X)"
    using T \<open>\<epsilon> > 0\<close> unfolding T_def has_sum_def tendsto_iff by blast
  moreover have "eventually (\<lambda>X'. finite X' \<and> X' \<subseteq> X) (finite_subsets_at_top X)"
    by (simp add: eventually_finite_subsets_at_top_weakI)
  ultimately show "\<forall>\<^sub>F X' in finite_subsets_at_top X. \<forall>y\<in>Y. dist (\<Sum>x\<in>X'. f x y) (\<Sum>\<^sub>\<infinity>x\<in>X. f x y) < \<epsilon>"
  proof eventually_elim
    case X': (elim X')
    show "\<forall>y\<in>Y. dist (\<Sum>x\<in>X'. f x y) (\<Sum>\<^sub>\<infinity>x\<in>X. f x y) < \<epsilon>"
    proof
      fix y assume y: "y \<in> Y"
      have 1: "((\<lambda>x. f x y) has_sum (S y - (\<Sum>x\<in>X'. f x y))) (X - X')"
        using X' y by (intro has_sum_Diff S has_sum_finite[of X'] f_summable) auto
      have 2: "(M has_sum (T - (\<Sum>x\<in>X'. M x))) (X - X')"
        using X' y by (intro has_sum_Diff T has_sum_finite[of X'] M_summable) auto
      have "dist (\<Sum>x\<in>X'. f x y) (\<Sum>\<^sub>\<infinity>x\<in>X. f x y) = norm (S y - (\<Sum>x\<in>X'. f x y))"
        by (simp add: dist_norm norm_minus_commute S_def)
      also have "norm (S y - (\<Sum>x\<in>X'. f x y)) \<le> (\<Sum>\<^sub>\<infinity>x\<in>X-X'. M x)"
        using 2 y by (intro norm_infsum_le[OF 1 _ norm_le]) (auto simp: infsumI)
      also have "\<dots> = T - (\<Sum>x\<in>X'. M x)"
        using 2 by (auto simp: infsumI)
      also have "\<dots> < \<epsilon>"
        using X' by (simp add: dist_norm)
      finally show "dist (\<Sum>x\<in>X'. f x y) (\<Sum>\<^sub>\<infinity>x\<in>X. f x y) < \<epsilon>" .
    qed
  qed
qed

subsection\<^marker>\<open>tag unimportant\<close> \<open>Structural introduction rules\<close>

lemma uniform_limit_eq_rhs: "uniform_limit X f l F \<Longrightarrow> l = m \<Longrightarrow> uniform_limit X f m F"
  by simp

named_theorems uniform_limit_intros "introduction rules for uniform_limit"
setup \<open>
  Global_Theory.add_thms_dynamic (\<^binding>\<open>uniform_limit_eq_intros\<close>,
    fn context =>
      Named_Theorems.get (Context.proof_of context) \<^named_theorems>\<open>uniform_limit_intros\<close>
      |> map_filter (try (fn thm => @{thm uniform_limit_eq_rhs} OF [thm])))
\<close>

lemma (in bounded_linear) uniform_limit[uniform_limit_intros]:
  assumes "uniform_limit X g l F"
  shows "uniform_limit X (\<lambda>a b. f (g a b)) (\<lambda>a. f (l a)) F"
proof (rule uniform_limitI)
  fix e::real
  from pos_bounded obtain K
    where K: "\<And>x y. dist (f x) (f y) \<le> K * dist x y" "K > 0"
    by (auto simp: ac_simps dist_norm diff[symmetric])
  assume "0 < e" with \<open>K > 0\<close> have "e / K > 0" by simp
  from uniform_limitD[OF assms this]
  show "\<forall>\<^sub>F n in F. \<forall>x\<in>X. dist (f (g n x)) (f (l x)) < e"
    by eventually_elim (metis le_less_trans mult.commute pos_less_divide_eq K)
qed

lemma (in bounded_linear) uniformly_convergent_on:
  assumes "uniformly_convergent_on A g"
  shows   "uniformly_convergent_on A (\<lambda>x y. f (g x y))"
proof -
  from assms obtain l where "uniform_limit A g l sequentially"
    unfolding uniformly_convergent_on_def by blast
  hence "uniform_limit A (\<lambda>x y. f (g x y)) (\<lambda>x. f (l x)) sequentially"
    by (rule uniform_limit)
  thus ?thesis unfolding uniformly_convergent_on_def by blast
qed

lemmas bounded_linear_uniform_limit_intros[uniform_limit_intros] =
  bounded_linear.uniform_limit[OF bounded_linear_Im]
  bounded_linear.uniform_limit[OF bounded_linear_Re]
  bounded_linear.uniform_limit[OF bounded_linear_cnj]
  bounded_linear.uniform_limit[OF bounded_linear_fst]
  bounded_linear.uniform_limit[OF bounded_linear_snd]
  bounded_linear.uniform_limit[OF bounded_linear_zero]
  bounded_linear.uniform_limit[OF bounded_linear_of_real]
  bounded_linear.uniform_limit[OF bounded_linear_inner_left]
  bounded_linear.uniform_limit[OF bounded_linear_inner_right]
  bounded_linear.uniform_limit[OF bounded_linear_divide]
  bounded_linear.uniform_limit[OF bounded_linear_scaleR_right]
  bounded_linear.uniform_limit[OF bounded_linear_mult_left]
  bounded_linear.uniform_limit[OF bounded_linear_mult_right]
  bounded_linear.uniform_limit[OF bounded_linear_scaleR_left]

lemmas uniform_limit_uminus[uniform_limit_intros] =
  bounded_linear.uniform_limit[OF bounded_linear_minus[OF bounded_linear_ident]]

lemma uniform_limit_const[uniform_limit_intros]: "uniform_limit S (\<lambda>x. c) c f"
  by (auto intro!: uniform_limitI)

lemma uniform_limit_add[uniform_limit_intros]:
  fixes f g::"'a \<Rightarrow> 'b \<Rightarrow> 'c::real_normed_vector"
  assumes "uniform_limit X f l F"
  assumes "uniform_limit X g m F"
  shows "uniform_limit X (\<lambda>a b. f a b + g a b) (\<lambda>a. l a + m a) F"
proof (rule uniform_limitI)
  fix e::real
  assume "0 < e"
  hence "0 < e / 2" by simp
  from
    uniform_limitD[OF assms(1) this]
    uniform_limitD[OF assms(2) this]
  show "\<forall>\<^sub>F n in F. \<forall>x\<in>X. dist (f n x + g n x) (l x + m x) < e"
    by eventually_elim (simp add: dist_triangle_add_half)
qed

lemma uniform_limit_minus[uniform_limit_intros]:
  fixes f g::"'a \<Rightarrow> 'b \<Rightarrow> 'c::real_normed_vector"
  assumes "uniform_limit X f l F"
  assumes "uniform_limit X g m F"
  shows "uniform_limit X (\<lambda>a b. f a b - g a b) (\<lambda>a. l a - m a) F"
  unfolding diff_conv_add_uminus
  by (rule uniform_limit_intros assms)+

lemma uniform_limit_norm[uniform_limit_intros]:
  assumes "uniform_limit S g l f"
  shows "uniform_limit S (\<lambda>x y. norm (g x y)) (\<lambda>x. norm (l x)) f"
  using assms
  apply (rule metric_uniform_limit_imp_uniform_limit)
  apply (rule eventuallyI)
  by (metis dist_norm norm_triangle_ineq3 real_norm_def)

lemma (in bounded_bilinear) bounded_uniform_limit[uniform_limit_intros]:
  assumes "uniform_limit X f l F"
  assumes "uniform_limit X g m F"
  assumes "bounded (m ` X)"
  assumes "bounded (l ` X)"
  shows "uniform_limit X (\<lambda>a b. prod (f a b) (g a b)) (\<lambda>a. prod (l a) (m a)) F"
proof (rule uniform_limitI)
  fix e::real
  from pos_bounded obtain K where K:
    "0 < K" "\<And>a b. norm (prod a b) \<le> norm a * norm b * K"
    by auto
  hence "sqrt (K*4) > 0" by simp

  from assms obtain Km Kl
  where Km: "Km > 0" "\<And>x. x \<in> X \<Longrightarrow> norm (m x) \<le> Km"
    and Kl: "Kl > 0" "\<And>x. x \<in> X \<Longrightarrow> norm (l x) \<le> Kl"
    by (auto simp: bounded_pos)
  hence "K * Km * 4 > 0" "K * Kl * 4 > 0"
    using \<open>K > 0\<close>
    by simp_all
  assume "0 < e"

  hence "sqrt e > 0" by simp
  from uniform_limitD[OF assms(1) divide_pos_pos[OF this \<open>sqrt (K*4) > 0\<close>]]
    uniform_limitD[OF assms(2) divide_pos_pos[OF this \<open>sqrt (K*4) > 0\<close>]]
    uniform_limitD[OF assms(1) divide_pos_pos[OF \<open>e > 0\<close> \<open>K * Km * 4 > 0\<close>]]
    uniform_limitD[OF assms(2) divide_pos_pos[OF \<open>e > 0\<close> \<open>K * Kl * 4 > 0\<close>]]
  show "\<forall>\<^sub>F n in F. \<forall>x\<in>X. dist (prod (f n x) (g n x)) (prod (l x) (m x)) < e"
  proof eventually_elim
    case (elim n)
    show ?case
    proof safe
      fix x assume "x \<in> X"
      have "dist (prod (f n x) (g n x)) (prod (l x) (m x)) \<le>
        norm (prod (f n x - l x) (g n x - m x)) +
        norm (prod (f n x - l x) (m x)) +
        norm (prod (l x) (g n x - m x))"
        by (auto simp: dist_norm prod_diff_prod intro: order_trans norm_triangle_ineq add_mono)
      also note K(2)[of "f n x - l x" "g n x - m x"]
      also from elim(1)[THEN bspec, OF \<open>_ \<in> X\<close>, unfolded dist_norm]
      have "norm (f n x - l x) \<le> sqrt e / sqrt (K * 4)"
        by simp
      also from elim(2)[THEN bspec, OF \<open>_ \<in> X\<close>, unfolded dist_norm]
      have "norm (g n x - m x) \<le> sqrt e / sqrt (K * 4)"
        by simp
      also have "sqrt e / sqrt (K * 4) * (sqrt e / sqrt (K * 4)) * K = e / 4"
        using \<open>K > 0\<close> \<open>e > 0\<close> by auto
      also note K(2)[of "f n x - l x" "m x"]
      also note K(2)[of "l x" "g n x - m x"]
      also from elim(3)[THEN bspec, OF \<open>_ \<in> X\<close>, unfolded dist_norm]
      have "norm (f n x - l x) \<le> e / (K * Km * 4)"
        by simp
      also from elim(4)[THEN bspec, OF \<open>_ \<in> X\<close>, unfolded dist_norm]
      have "norm (g n x - m x) \<le> e / (K * Kl * 4)"
        by simp
      also note Kl(2)[OF \<open>_ \<in> X\<close>]
      also note Km(2)[OF \<open>_ \<in> X\<close>]
      also have "e / (K * Km * 4) * Km * K = e / 4"
        using \<open>K > 0\<close> \<open>Km > 0\<close> by simp
      also have " Kl * (e / (K * Kl * 4)) * K = e / 4"
        using \<open>K > 0\<close> \<open>Kl > 0\<close> by simp
      also have "e / 4 + e / 4 + e / 4 < e" using \<open>e > 0\<close> by simp
      finally show "dist (prod (f n x) (g n x)) (prod (l x) (m x)) < e"
        using \<open>K > 0\<close> \<open>Kl > 0\<close> \<open>Km > 0\<close> \<open>e > 0\<close>
        by (simp add: algebra_simps mult_right_mono divide_right_mono)
    qed
  qed
qed

lemmas bounded_bilinear_bounded_uniform_limit_intros[uniform_limit_intros] =
  bounded_bilinear.bounded_uniform_limit[OF Inner_Product.bounded_bilinear_inner]
  bounded_bilinear.bounded_uniform_limit[OF Real_Vector_Spaces.bounded_bilinear_mult]
  bounded_bilinear.bounded_uniform_limit[OF Real_Vector_Spaces.bounded_bilinear_scaleR]

lemma uniform_lim_mult:
  fixes f :: "'a \<Rightarrow> 'b \<Rightarrow> 'c::real_normed_algebra"
  assumes f: "uniform_limit S f l F"
      and g: "uniform_limit S g m F"
      and l: "bounded (l ` S)"
      and m: "bounded (m ` S)"
    shows "uniform_limit S (\<lambda>a b. f a b * g a b) (\<lambda>a. l a * m a) F"
  by (intro bounded_bilinear_bounded_uniform_limit_intros assms)

lemma uniform_lim_inverse:
  fixes f :: "'a \<Rightarrow> 'b \<Rightarrow> 'c::real_normed_field"
  assumes f: "uniform_limit S f l F"
      and b: "\<And>x. x \<in> S \<Longrightarrow> b \<le> norm(l x)"
      and "b > 0"
    shows "uniform_limit S (\<lambda>x y. inverse (f x y)) (inverse \<circ> l) F"
proof (rule uniform_limitI)
  fix e::real
  assume "e > 0"
  have lte: "dist (inverse (f x y)) ((inverse \<circ> l) y) < e"
           if "b/2 \<le> norm (f x y)" "norm (f x y - l y) < e * b\<^sup>2 / 2" "y \<in> S"
           for x y
  proof -
    have [simp]: "l y \<noteq> 0" "f x y \<noteq> 0"
      using \<open>b > 0\<close> that b [OF \<open>y \<in> S\<close>] by fastforce+
    have "norm (l y - f x y) <  e * b\<^sup>2 / 2"
      by (metis norm_minus_commute that(2))
    also have "... \<le> e * (norm (f x y) * norm (l y))"
      using \<open>e > 0\<close> that b [OF \<open>y \<in> S\<close>] apply (simp add: power2_eq_square)
      by (metis \<open>b > 0\<close> less_eq_real_def mult.left_commute mult_mono')
    finally show ?thesis
      by (auto simp: dist_norm field_split_simps norm_mult norm_divide)
  qed
  have "\<forall>\<^sub>F n in F. \<forall>x\<in>S. dist (f n x) (l x) < b/2"
    using uniform_limitD [OF f, of "b/2"] by (simp add: \<open>b > 0\<close>)
  then have "\<forall>\<^sub>F x in F. \<forall>y\<in>S. b/2 \<le> norm (f x y)"
    apply (rule eventually_mono)
    using b apply (simp only: dist_norm)
    by (metis (no_types, opaque_lifting) diff_zero dist_commute dist_norm norm_triangle_half_l not_less)
  then have "\<forall>\<^sub>F x in F. \<forall>y\<in>S. b/2 \<le> norm (f x y) \<and> norm (f x y - l y) < e * b\<^sup>2 / 2"
    apply (simp only: ball_conj_distrib dist_norm [symmetric])
    apply (rule eventually_conj, assumption)
      apply (rule uniform_limitD [OF f, of "e * b ^ 2 / 2"])
    using \<open>b > 0\<close> \<open>e > 0\<close> by auto
  then show "\<forall>\<^sub>F x in F. \<forall>y\<in>S. dist (inverse (f x y)) ((inverse \<circ> l) y) < e"
    using lte by (force intro: eventually_mono)
qed

lemma uniform_lim_divide:
  fixes f :: "'a \<Rightarrow> 'b \<Rightarrow> 'c::real_normed_field"
  assumes f: "uniform_limit S f l F"
      and g: "uniform_limit S g m F"
      and l: "bounded (l ` S)"
      and b: "\<And>x. x \<in> S \<Longrightarrow> b \<le> norm(m x)"
      and "b > 0"
    shows "uniform_limit S (\<lambda>a b. f a b / g a b) (\<lambda>a. l a / m a) F"
proof -
  have m: "bounded ((inverse \<circ> m) ` S)"
    using b \<open>b > 0\<close>
    apply (simp add: bounded_iff)
    by (metis le_imp_inverse_le norm_inverse)
  have "uniform_limit S (\<lambda>a b. f a b * inverse (g a b))
         (\<lambda>a. l a * (inverse \<circ> m) a) F"
    by (rule uniform_lim_mult [OF f uniform_lim_inverse [OF g b \<open>b > 0\<close>] l m])
  then show ?thesis
    by (simp add: field_class.field_divide_inverse)
qed

lemma uniform_limit_null_comparison:
  assumes "\<forall>\<^sub>F x in F. \<forall>a\<in>S. norm (f x a) \<le> g x a"
  assumes "uniform_limit S g (\<lambda>_. 0) F"
  shows "uniform_limit S f (\<lambda>_. 0) F"
  using assms(2)
proof (rule metric_uniform_limit_imp_uniform_limit)
  show "\<forall>\<^sub>F x in F. \<forall>y\<in>S. dist (f x y) 0 \<le> dist (g x y) 0"
    using assms(1) by (rule eventually_mono) (force simp add: dist_norm)
qed

lemma uniform_limit_on_Un:
  "uniform_limit I f g F \<Longrightarrow> uniform_limit J f g F \<Longrightarrow> uniform_limit (I \<union> J) f g F"
  by (auto intro!: uniform_limitI dest!: uniform_limitD elim: eventually_elim2)

lemma uniform_limit_on_empty [iff]:
  "uniform_limit {} f g F"
  by (auto intro!: uniform_limitI)

lemma uniform_limit_on_UNION:
  assumes "finite S"
  assumes "\<And>s. s \<in> S \<Longrightarrow> uniform_limit (h s) f g F"
  shows "uniform_limit (\<Union>(h ` S)) f g F"
  using assms
  by induct (auto intro: uniform_limit_on_empty uniform_limit_on_Un)

lemma uniform_limit_on_Union:
  assumes "finite I"
  assumes "\<And>J. J \<in> I \<Longrightarrow> uniform_limit J f g F"
  shows "uniform_limit (Union I) f g F"
  by (metis SUP_identity_eq assms uniform_limit_on_UNION)

lemma uniform_limit_on_subset:
  "uniform_limit J f g F \<Longrightarrow> I \<subseteq> J \<Longrightarrow> uniform_limit I f g F"
  by (auto intro!: uniform_limitI dest!: uniform_limitD intro: eventually_mono)

lemma uniform_limit_bounded:
  fixes f::"'i \<Rightarrow> 'a::topological_space \<Rightarrow> 'b::metric_space"
  assumes l: "uniform_limit S f l F"
  assumes bnd: "\<forall>\<^sub>F i in F. bounded (f i ` S)"
  assumes "F \<noteq> bot"
  shows "bounded (l ` S)"
proof -
  from l have "\<forall>\<^sub>F n in F. \<forall>x\<in>S. dist (l x) (f n x) < 1"
    by (auto simp: uniform_limit_iff dist_commute dest!: spec[where x=1])
  with bnd
  have "\<forall>\<^sub>F n in F. \<exists>M. \<forall>x\<in>S. dist undefined (l x) \<le> M + 1"
    by eventually_elim
      (auto intro!: order_trans[OF dist_triangle2 add_mono] intro: less_imp_le
        simp: bounded_any_center[where a=undefined])
  then show ?thesis using assms
    by (auto simp: bounded_any_center[where a=undefined] dest!: eventually_happens)
qed

lemma uniformly_convergent_add:
  "uniformly_convergent_on A f \<Longrightarrow> uniformly_convergent_on A g\<Longrightarrow>
      uniformly_convergent_on A (\<lambda>k x. f k x + g k x :: 'a :: {real_normed_algebra})"
  unfolding uniformly_convergent_on_def by (blast dest: uniform_limit_add)

lemma uniformly_convergent_minus:
  "uniformly_convergent_on A f \<Longrightarrow> uniformly_convergent_on A g\<Longrightarrow>
      uniformly_convergent_on A (\<lambda>k x. f k x - g k x :: 'a :: {real_normed_algebra})"
  unfolding uniformly_convergent_on_def by (blast dest: uniform_limit_minus)

lemma uniformly_convergent_mult:
  "uniformly_convergent_on A f \<Longrightarrow>
      uniformly_convergent_on A (\<lambda>k x. c * f k x :: 'a :: {real_normed_algebra})"
  unfolding uniformly_convergent_on_def
  by (blast dest: bounded_linear_uniform_limit_intros(13))

subsection\<open>Power series and uniform convergence\<close>

proposition powser_uniformly_convergent:
  fixes a :: "nat \<Rightarrow> 'a::{real_normed_div_algebra,banach}"
  assumes "r < conv_radius a"
  shows "uniformly_convergent_on (cball \<xi> r) (\<lambda>n x. \<Sum>i<n. a i * (x - \<xi>) ^ i)"
proof (cases "0 \<le> r")
  case True
  then have *: "summable (\<lambda>n. norm (a n) * r ^ n)"
    using abs_summable_in_conv_radius [of "of_real r" a] assms
    by (simp add: norm_mult norm_power)
  show ?thesis
    by (simp add: Weierstrass_m_test'_ev [OF _ *] norm_mult norm_power
              mult_left_mono power_mono dist_norm norm_minus_commute)
next
  case False then show ?thesis by (simp add: not_le)
qed

lemma powser_uniform_limit:
  fixes a :: "nat \<Rightarrow> 'a::{real_normed_div_algebra,banach}"
  assumes "r < conv_radius a"
  shows "uniform_limit (cball \<xi> r) (\<lambda>n x. \<Sum>i<n. a i * (x - \<xi>) ^ i) (\<lambda>x. suminf (\<lambda>i. a i * (x - \<xi>) ^ i)) sequentially"
using powser_uniformly_convergent [OF assms]
by (simp add: Uniform_Limit.uniformly_convergent_uniform_limit_iff Series.suminf_eq_lim)

lemma powser_continuous_suminf:
  fixes a :: "nat \<Rightarrow> 'a::{real_normed_div_algebra,banach}"
  assumes "r < conv_radius a"
  shows "continuous_on (cball \<xi> r) (\<lambda>x. suminf (\<lambda>i. a i * (x - \<xi>) ^ i))"
apply (rule uniform_limit_theorem [OF _ powser_uniform_limit])
apply (rule eventuallyI continuous_intros assms)+
apply (simp add:)
done

lemma powser_continuous_sums:
  fixes a :: "nat \<Rightarrow> 'a::{real_normed_div_algebra,banach}"
  assumes r: "r < conv_radius a"
      and sm: "\<And>x. x \<in> cball \<xi> r \<Longrightarrow> (\<lambda>n. a n * (x - \<xi>) ^ n) sums (f x)"
  shows "continuous_on (cball \<xi> r) f"
apply (rule continuous_on_cong [THEN iffD1, OF refl _ powser_continuous_suminf [OF r]])
using sm sums_unique by fastforce

lemmas uniform_limit_subset_union = uniform_limit_on_subset[OF uniform_limit_on_Union]

end