Theory Real_Analytic

section ‹Real-Analytic ($C^\omega$) Functions in Several Variables›

text ‹
  Real-analytic functions on Euclidean spaces, via convergent power series over the
  multi-indices ‹ra_idx›; in one dimension this agrees with convergence of the Taylor
  series.  Real-analytic functions are ‹C∞› and closed under the algebraic operations,
  composition and linear maps, and a real-analytic function that is not identically zero on
  a connected open set has a nowhere dense zero set.
›

theory Real_Analytic
  imports Higher_Differentiability_Multi Infinite_Sum
begin

(*Possibly to be moved up*)
lemma poly_geom_summable:
  fixes t :: real and p :: nat
  assumes t: "0 ≤ t" "t < 1"
  shows "summable (λn. real ((n+1)^p) * t ^ n)"
proof (cases "t = 0")
  case True
  then show ?thesis by (simp add: summable_comparison_test)
next
  case False
  with t have tpos: "0 < t" by simp
  define c where "c = (1 + t) / 2"
  have c1: "c < 1" using t by (simp add: c_def)
  have ct: "t < c" using False t by (simp add: c_def)
  have cpos: "0 < c" using tpos ct by simp
  have base_lim: "(λn. real (n+2) / real (n+1)) ⇢ 1"
  proof -
    have lim1: "(λn. 1 + inverse (real (Suc n))) ⇢ 1"
      by (rule LIMSEQ_inverse_real_of_nat_add)
    have "(λn. real (n+2)/real(n+1)) = (λn. 1 + inverse (real (Suc n)))"
      by (simp add: field_simps)
    with lim1 show ?thesis by simp
  qed
  have ratio_lim: "(λn. real ((n+2)^p) / real ((n+1)^p)) ⇢ 1"
  proof -
    have "(λn. (real (n+2) / real (n+1)) ^ p) ⇢ 1 ^ p"
      using base_lim by (rule tendsto_power)
    moreover have "⋀n. (real (n+2)/real(n+1))^p = real((n+2)^p)/real((n+1)^p)"
      by (simp add: power_divide)
    ultimately show ?thesis by simp
  qed
  have rt: "(λn. real ((n+2)^p) / real ((n+1)^p) * t) ⇢ t"
  proof -
    have "(λn. real ((n+2)^p) / real ((n+1)^p) * t) ⇢ 1 * t"
      using ratio_lim tendsto_const by (rule tendsto_mult)
    thus ?thesis by simp
  qed
  have "eventually (λn. real ((n+2)^p) / real ((n+1)^p) * t < c) sequentially"
    using rt ct by (intro order_tendstoD(2)) simp_all
  then obtain N where N: "⋀n. n ≥ N ⟹ real ((n+2)^p) / real ((n+1)^p) * t < c"
    by (auto simp: eventually_sequentially)
  show ?thesis
  proof (rule summable_ratio_test[where c = c and N = N])
    show "c < 1" by (rule c1)
    fix n assume "n ≥ N"
    have ineq: "real ((n+2)^p) / real ((n+1)^p) * t ≤ c" using N[OF ‹n≥N›] by simp
    have pos1: "(0::real) < real ((n+1)^p)" by simp
    have key: "real ((n+2)^p) * t^(n+1) ≤ c * (real ((n+1)^p) * t^n)"
    proof -
      have "real ((n+2)^p) * t^(n+1)
              = (real ((n+2)^p) / real ((n+1)^p) * t) * (real ((n+1)^p) * t^n)"
        using pos1 tpos by (simp add: field_simps)
      also have "… ≤ c * (real ((n+1)^p) * t^n)"
        using ineq pos1 tpos by (intro mult_right_mono) (auto simp: zero_le_mult_iff)
      finally show ?thesis .
    qed
    have tnn: "0 ≤ t ^ n" "0 ≤ t ^ (n+1)" using t by (auto intro: zero_le_power)
    have nn1: "0 ≤ real ((n+1)^p) * t^n" using tnn by simp
    have nn2: "0 ≤ real ((n+2)^p) * t^(n+1)" using tnn by simp
    have e1: "real ((Suc n + 1) ^ p) * t ^ Suc n = real ((n+2)^p) * t^(n+1)" by simp
    have "norm (real ((Suc n + 1) ^ p) * t ^ Suc n) = real ((n+2)^p) * t^(n+1)"
      using nn2 by (simp only: e1 real_norm_def abs_of_nonneg)
    also have "… ≤ c * (real ((n+1)^p) * t^n)" by (rule key)
    also have "… = c * norm (real ((n + 1) ^ p) * t ^ n)"
      using nn1 by (simp only: real_norm_def abs_of_nonneg)
    finally show "norm (real ((Suc n + 1) ^ p) * t ^ Suc n) ≤ c * norm (real ((n + 1) ^ p) * t ^ n)" .
  qed
qed

subsection ‹$C^\infty$ smoothness (infinitely many continuous derivatives)›

definition Cinfinity_at ::
  "('a::real_normed_vector ⇒ 'b::real_normed_vector) ⇒ 'a ⇒ bool" where
  "Cinfinity_at f x ⟷ (∀k. Ck_at k f x)"

definition Cinfinity_on ::
  "('a::real_normed_vector ⇒ 'b::real_normed_vector) ⇒ 'a set ⇒ bool" where
  "Cinfinity_on f U ⟷ open U ∧ (∀x∈U. Cinfinity_at f x)"

lemma Cinfinity_imp_Ck: "Cinfinity_at f x ⟹ Ck_at k f x"
  unfolding Cinfinity_at_def by blast

lemma Cinfinity_on_imp_Ck_on: "Cinfinity_on f U ⟹ Ck_on k f U"
  unfolding Cinfinity_on_def Ck_on_def Cinfinity_at_def by blast


subsection ‹One-dimensional real analyticity via the Peano/Taylor expansion›

text ‹‹f› is real-analytic at ‹c› if, near ‹c›, it is smooth and its Taylor series converges
  to it.›

definition real_analytic_at_1d :: "(real ⇒ real) ⇒ real ⇒ bool" where
  "real_analytic_at_1d f c ⟷
     (∃r>0. (∀x. ¦x - c¦ < r ⟶ (∀n. f n-times_differentiable_at x))
          ∧ (∀x. ¦x - c¦ < r ⟶
                 (λn. (deriv ^^ n) f c / fact n * (x - c) ^ n) sums f x))"


subsection ‹Multivariate real analyticity (local convergent power series)›

text ‹The basis monomial (a product over the Euclidean basis) and the finitely-supported multi-indices.›

definition ra_monomial :: "'a::euclidean_space ⇒ ('a ⇒ nat) ⇒ real" where
  "ra_monomial h α = (∏b∈Basis. (h ∙ b) ^ (α b))"

definition ra_idx :: "('a::euclidean_space ⇒ nat) set" where
  "ra_idx = {α. {b. α b ≠ 0} ⊆ Basis}"

definition real_analytic_on ::
  "('a::euclidean_space ⇒ 'b::real_normed_vector) ⇒ 'a set ⇒ bool" where
  "real_analytic_on f U ⟷ open U ∧
     (∀x0∈U. ∃r>0. ∃c. ∀x. dist x x0 < r ⟶
        ((λα. ra_monomial (x - x0) α *R c α) has_sum f x) ra_idx)"

text ‹On the line, the multivariate notion coincides with the Peano/Taylor one.›

lemma real_analytic_on_1d_iff:
  fixes f :: "real ⇒ real"
  shows "real_analytic_on f U ⟷ open U ∧ (∀c∈U. real_analytic_at_1d f c)"
proof -
  define i :: "nat ⇒ (real ⇒ nat)" where "i = (λn b. if b = 1 then n else 0)"
  define j :: "(real ⇒ nat) ⇒ nat" where "j = (λα. α 1)"
  ― ‹basic facts about the bijection between @{term ra_idx} and @{term "UNIV::nat set"}›
  have ij: "i (j α) = α" if "α ∈ ra_idx" for α
  proof (rule ext)
    fix b :: real
    show "i (j α) b = α b"
    proof (cases "b = 1")
      case True thus ?thesis by (simp add: i_def j_def)
    next
      case False
      with that have "α b = 0" by (auto simp: ra_idx_def)
      with False show ?thesis by (simp add: i_def)
    qed
  qed
  have ji: "j (i n) = n" for n by (simp add: i_def j_def)
  have i_idx: "i n ∈ ra_idx" for n by (auto simp: i_def ra_idx_def)
  have iev: "i n 1 = n" for n by (simp add: i_def)
  ― ‹the basis monomial on the line is just a power›
  have mono: "ra_monomial h α = h ^ (α 1)" for h :: real and α
    by (simp add: ra_monomial_def)
      ― ‹@{term "Basis::real set"} is @{term "{1}"} and @{term "inner h 1 = h"}, both [simp]›
  ― ‹the central reindexing equivalence, for a fixed expansion point and argument›
  have reindex:
    "((λn. (deriv ^^ n) f c0 / fact n * (x - c0) ^ n) has_sum s) (UNIV :: nat set)
       = ((λα. ra_monomial (x - c0) α *R (λβ. (deriv ^^ (β 1)) f c0 / fact (β 1)) α)
            has_sum s) ra_idx"
    for c0 x s
    by (rule has_sum_reindex_bij_witness[where i = j and j = i])
       (auto simp: ji i_idx ij mono iev mult.commute)

  show ?thesis
  proof
    ― ‹‹(⇒)›: a local power series gives smoothness and convergence of the Taylor series›
    assume A: "real_analytic_on f U"
    then have oU: "open U" by (simp only: real_analytic_on_def)
    have "real_analytic_at_1d f c" if cU: "c ∈ U" for c
    proof -
      from A cU obtain r cc where r: "0 < r"
        and HS: "⋀x. dist x c < r ⟹
                  ((λα. ra_monomial (x - c) α *R cc α) has_sum f x) ra_idx"
        unfolding real_analytic_on_def by blast
      ― ‹Reindex each unordered sum to an ordinary power series in @{term "x - c"}.›
      have sums_xc: "(λn. cc (i n) * (x - c) ^ n) sums f x" if "dist x c < r" for x
      proof -
        have "((λn. cc (i n) * (x - c) ^ n) has_sum f x) (UNIV :: nat set)
                = ((λα. ra_monomial (x - c) α *R cc α) has_sum f x) ra_idx"
          by (rule has_sum_reindex_bij_witness[where i = j and j = i])
             (auto simp: ji i_idx ij mono iev mult.commute)
        with HS[OF that] have
          "((λn. cc (i n) * (x - c) ^ n) has_sum f x) (UNIV :: nat set)" by simp
        thus ?thesis by (rule has_sum_imp_sums)
      qed
      ― ‹On the ball, ‹f› equals its power series; ‹termdiffs› makes it smooth and
         identifies the coefficients with the Taylor coefficients.›
      define a where "a = (λn. cc (i n))"
      have sums_a: "(λn. a n * z ^ n) sums f (c + z)" if "¦z¦ < r" for z
      proof -
        have "dist (c + z) c < r" using that by (simp only: dist_real_def)
        from sums_xc[OF this] show ?thesis by (simp add: a_def)
      qed
      have summ_a: "summable (λn. a n * z ^ n)" if "¦z¦ < r" for z
        using sums_a[OF that] by (rule sums_summable)
      ― ‹summability of every ‹diffs›-iterate strictly inside the radius›
      have summ_diffs: "summable (λm. (diffs ^^ n) a m * z ^ m)" if "¦z¦ < r" for n z
        using that
      proof (induction n arbitrary: z)
        case 0
        thus ?case using summ_a by simp
      next
        case (Suc n)
        have "summable (λm. diffs ((diffs ^^ n) a) m * z ^ m)"
        proof (rule termdiff_converges[where K = r])
          show "norm z < r" using Suc.prems by simp
          fix w :: real assume "norm w < r"
          hence "¦w¦ < r" by simp
          thus "summable (λm. (diffs ^^ n) a m * w ^ m)" by (rule Suc.IH)
        qed
        thus ?case by simp
      qed
      ― ‹the centered power series and its term-by-term derivative›
      define S where "S = (λn y. ∑m. (diffs ^^ n) a m * (y - c) ^ m)"
      have S0_eq_f: "S 0 x = f x" if "¦x - c¦ < r" for x
      proof -
        from sums_a[of "x - c"] that have "(λn. a n * (x - c) ^ n) sums f x" by simp
        thus ?thesis by (simp add: S_def sums_iff)
      qed
      have S_deriv: "(S n has_field_derivative S (Suc n) x) (at x)"
        if "¦x - c¦ < r" for n x
      proof -
        have H: "((λw. ∑m. (diffs ^^ n) a m * w ^ m)
                   has_field_derivative (∑m. diffs ((diffs ^^ n) a) m * (x - c) ^ m))
                  (at (x - c))"
        proof (rule termdiffs_strong'[where K = r])
          fix w :: real assume "norm w < r"
          thus "summable (λm. (diffs ^^ n) a m * w ^ m)" using summ_diffs by simp
        next
          show "norm (x - c) < r" using that by simp
        qed
        have shift: "((λy. y - c) has_field_derivative 1) (at x)"
          by (auto intro!: derivative_eq_intros)
        have "((λy. (λw. ∑m. (diffs ^^ n) a m * w ^ m) (y - c))
                 has_field_derivative
                 (∑m. diffs ((diffs ^^ n) a) m * (x - c) ^ m) * 1) (at x)"
          by (rule DERIV_chain'[OF shift]) (use H in simp)
        thus ?thesis by (simp add: S_def)
      qed
      ― ‹induction: ‹f› is ‹n›-times differentiable on the ball and ‹(deriv^^n) f = S n››
      have main: "∀x. ¦x - c¦ < r ⟶
                    f n-times_differentiable_at x ∧ (deriv ^^ n) f x = S n x" for n
      proof (induction n)
        case 0
        show ?case by (auto simp: S0_eq_f)
      next
        case (Suc n)
        show ?case
        proof (intro allI impI conjI)
          fix x assume xc: "¦x - c¦ < r"
          have ballopen: "{y. ¦y - c¦ < r} = ball c r"
            by (auto simp: dist_real_def abs_minus_commute)
          have eqA: "(deriv ^^ n) f y = S n y" if "¦y - c¦ < r" for y
            using Suc.IH that by blast
          have dn_deriv: "((deriv ^^ n) f has_field_derivative S (Suc n) x) (at x)"
          proof (rule has_field_derivative_transform_within_open
                       [where f = "S n" and S = "{y. ¦y - c¦ < r}"])
            show "(S n has_field_derivative S (Suc n) x) (at x)" by (rule S_deriv[OF xc])
            show "open {y. ¦y - c¦ < r}" by (simp add: ballopen)
            show "x ∈ {y. ¦y - c¦ < r}" using xc by simp
            show "⋀y. y ∈ {y. ¦y - c¦ < r} ⟹ S n y = (deriv ^^ n) f y"
              using eqA by auto
          qed
          show "(deriv ^^ Suc n) f x = S (Suc n) x"
            using dn_deriv by (simp only: kth_deriv_simps(2) DERIV_imp_deriv)
          show "f (Suc n)-times_differentiable_at x"
            unfolding k_times_differentiable_at.simps(2)
          proof
            show "∃ε>0. ∀y. ¦y - x¦ < ε ⟶ f n-times_differentiable_at y"
            proof (intro exI[where x = "r - ¦x - c¦"] conjI allI impI)
              show "0 < r - ¦x - c¦" using xc by simp
              fix y assume "¦y - x¦ < r - ¦x - c¦"
              hence "¦y - c¦ < r" by linarith
              thus "f n-times_differentiable_at y" using Suc.IH by blast
            qed
          next
            have "(deriv ^^ Suc n) f x = S (Suc n) x"
              using dn_deriv by (simp only: kth_deriv_simps(2) DERIV_imp_deriv)
            with dn_deriv
            show "((deriv ^^ n) f has_derivative (λh. (deriv ^^ Suc n) f x * h)) (at x)"
              by (simp only: has_field_derivative_def)
          qed
        qed
      qed
      ― ‹the ‹diffs›-iterate evaluated at ‹0› yields ‹fact n * a n››
      have diffs_fact: "(diffs ^^ n) g 0 = fact n * g n" for n and g :: "nat ⇒ real"
      proof -
        have gen: "fact m * (diffs ^^ n) g m = fact (m + n) * g (m + n)" for m
        proof (induction n arbitrary: g m)
          case 0 show ?case by simp
        next
          case (Suc n)
          have "fact m * (diffs ^^ Suc n) g m = fact m * (diffs ^^ n) (diffs g) m"
            by (simp only: funpow_Suc_right o_apply)
          also have "… = fact (m + n) * (diffs g) (m + n)"
            using Suc.IH[of m "diffs g"] by simp
          also have "… = fact (m + n) * (of_nat (Suc (m + n)) * g (Suc (m + n)))"
            by (simp only: diffs_def)
          also have "… = fact (Suc (m + n)) * g (Suc (m + n))"
            by (simp add: algebra_simps)
          finally show ?case by (simp add: add.commute)
        qed
        from gen[of 0] show ?thesis by simp
      qed
      ― ‹the two key facts›
      have coeff: "cc (i n) = (deriv ^^ n) f c / fact n" for n
      proof -
        have "¦c - c¦ < r" using r by simp
        with main[of n] have "(deriv ^^ n) f c = S n c" by blast
        also have "S n c = (diffs ^^ n) a 0" by (simp add: S_def)
        also have "… = fact n * a n" by (rule diffs_fact)
        finally have "(deriv ^^ n) f c = fact n * a n" .
        thus ?thesis by (simp add: a_def)
      qed
      have smooth: "f n-times_differentiable_at x" if "¦x - c¦ < r" for x n
        using main[of n] that by blast
      show ?thesis
        unfolding real_analytic_at_1d_def
      proof (intro exI[where x = r] conjI allI impI)
        show "0 < r" by (rule r)
      next
        fix x n assume "¦x - c¦ < r" thus "f n-times_differentiable_at x"
          by (rule smooth)
      next
        fix x assume "¦x - c¦ < r"
        then have "dist x c < r" by (simp only: dist_real_def)
        from sums_xc[OF this] show
          "(λn. (deriv ^^ n) f c / fact n * (x - c) ^ n) sums f x"
          by (simp only: coeff mult.commute)
      qed
    qed
    with oU show "open U ∧ (∀c∈U. real_analytic_at_1d f c)" by blast
  next
    ― ‹‹(⇐)›: inside its radius the Taylor series converges absolutely, giving a
       ‹has_sum› expansion.›
    assume B: "open U ∧ (∀c∈U. real_analytic_at_1d f c)"
    then have oU: "open U" and AT: "⋀c. c ∈ U ⟹ real_analytic_at_1d f c" by auto
    show "real_analytic_on f U"
      unfolding real_analytic_on_def
    proof (intro conjI ballI)
      show "open U" by (rule oU)
    next
      fix c assume cU: "c ∈ U"
      from AT[OF cU] obtain r where r: "0 < r"
        and TS: "⋀x. ¦x - c¦ < r ⟹
                  (λn. (deriv ^^ n) f c / fact n * (x - c) ^ n) sums f x"
        unfolding real_analytic_at_1d_def by blast
      show "∃r>0. ∃cc. ∀x. dist x c < r ⟶
              ((λα. ra_monomial (x - c) α *R cc α) has_sum f x) ra_idx"
      proof (intro exI[where x = r] exI[where x = "λβ. (deriv ^^ (β 1)) f c / fact (β 1)"]
                   conjI allI impI)
        show "0 < r" by (rule r)
      next
        fix x assume dx: "dist x c < r"
        then have axc: "¦x - c¦ < r" by (simp only: dist_real_def)
        ― ‹Choose an intermediate radius strictly between @{term "¦x - c¦"} and @{term r}.›
        define d where "d = (¦x - c¦ + r) / 2"
        define x1 where "x1 = c + d"
        have d_pos: "0 < d"
        proof -
          have "0 < ¦x - c¦ + r" using r by linarith
          thus ?thesis unfolding d_def by simp
        qed
        have x1c: "¦x1 - c¦ = d" using d_pos by (simp only: x1_def)
        have x1_in: "¦x1 - c¦ < r"
        proof -
          have "¦x - c¦ + r < 2 * r" using axc by linarith
          thus ?thesis unfolding x1c d_def by simp
        qed
        have lt: "¦x - c¦ < ¦x1 - c¦"
        proof -
          define a where "a = ¦x - c¦"
          have ar: "a < r" using axc by (simp only: a_def)
          have "a * 2 < a + r" using ar by linarith
          hence "a < (a + r) / 2" by (simp only: field_simps)
          thus ?thesis by (simp only: x1c d_def a_def)
        qed
        ― ‹Convergence at @{term x1} gives summability there; @{thm [source] powser_insidea}
           upgrades it to absolute summability at @{term x}.›
        have sm1: "summable (λn. (deriv ^^ n) f c / fact n * (x1 - c) ^ n)"
          using TS[OF x1_in] by (rule sums_summable)
        have absum: "summable (λn. norm ((deriv ^^ n) f c / fact n * (x - c) ^ n))"
          by (rule powser_insidea[OF sm1]) (use lt in simp)
        ― ‹Absolute + ordinary convergence ‹⇒› unordered convergence on ‹UNIV :: nat set›.›
        have HSnat: "((λn. (deriv ^^ n) f c / fact n * (x - c) ^ n) has_sum f x) (UNIV :: nat set)"
          by (rule norm_summable_imp_has_sum[OF absum TS[OF axc]])
        show "((λα. ra_monomial (x - c) α *R (λβ. (deriv ^^ (β 1)) f c / fact (β 1)) α)
                 has_sum f x) ra_idx"
          using HSnat by (simp only: reindex[of c x "f x"])
      qed
    qed
  qed
qed

subsection ‹Multivariate power-series differentiation›

definition ra_inc ::
  "('a ⇒ nat) ⇒ 'a ⇒ ('a ⇒ nat)" where
  "ra_inc α b = α(b := Suc (α b))"

definition ra_Dmonomial ::
  "'a::euclidean_space ⇒ ('a ⇒ nat) ⇒ 'a ⇒ real" where
  "ra_Dmonomial x α v =
     (∑b∈Basis.
        real (α b) * (v ∙ b) *
        (x ∙ b) ^ (α b - 1) *
        (∏d∈Basis - {b}. (x ∙ d) ^ (α d)))"

definition ra_dcoeff ::
  "(('a::euclidean_space ⇒ nat) ⇒ 'b::real_normed_vector) ⇒
    'a ⇒ ('a ⇒ nat) ⇒ 'b" where
  "ra_dcoeff c v α =
     (∑b∈Basis.
        (real (Suc (α b)) * (v ∙ b)) *R
          c (ra_inc α b))"



(* Differentiation of multivariate power series *)

definition ra_deg :: "('a::euclidean_space ⇒ nat) ⇒ nat" where
  "ra_deg α = (∑b∈Basis. α b)"


subsection ‹Per-term Fréchet derivative›

lemma ra_monomial_has_derivative:
  fixes x :: "'a::euclidean_space"
  shows
    "((λy. ra_monomial y α) has_derivative
        (ra_Dmonomial x α))
      (at x)"
proof -
  have factor_deriv:
    "((λy. (y ∙ b) ^ (α b)) has_derivative
        (λv. real (α b) * (v ∙ b) * (x ∙ b) ^ (α b - 1))) (at x)"
    if "b ∈ Basis" for b
  proof -
    have inner_d: "((λy. y ∙ b) has_derivative (λv. v ∙ b)) (at x)"
      using has_derivative_inner_left[OF has_derivative_id] by simp
    show "((λy. (y ∙ b) ^ (α b)) has_derivative
            (λv. real (α b) * (v ∙ b) * (x ∙ b) ^ (α b - 1))) (at x)"
      using has_derivative_power[OF inner_d, of "α b"] by simp
  qed
  have prod_d:
    "((λy. ∏b∈Basis. (y ∙ b) ^ (α b)) has_derivative
       (λv. ∑b∈Basis.
              (real (α b) * (v ∙ b) * (x ∙ b) ^ (α b - 1)) *
              (∏c∈Basis - {b}. (x ∙ c) ^ (α c)))) (at x)"
    by (rule has_derivative_prod[OF factor_deriv])
  have eq1: "(λy. ra_monomial y α) = (λy. ∏b∈Basis. (y ∙ b) ^ (α b))"
    by (simp only: ra_monomial_def)
  have eq2: "ra_Dmonomial x α =
      (λv. ∑b∈Basis.
              (real (α b) * (v ∙ b) * (x ∙ b) ^ (α b - 1)) *
              (∏c∈Basis - {b}. (x ∙ c) ^ (α c)))"
    by (rule ext)
       (simp only: ra_Dmonomial_def mult.commute mult.left_commute mult.assoc)
  show ?thesis
    using prod_d unfolding eq1 eq2 .
qed

lemma ra_term_has_derivative:
  fixes x :: "'a::euclidean_space"
    and c :: "('a ⇒ nat) ⇒ 'b::real_normed_vector"
  shows
    "((λy. ra_monomial y α *R c α) has_derivative
        (λv. ra_Dmonomial x α v *R c α))
      (at x)"
  by (rule has_derivative_scaleR_left[OF ra_monomial_has_derivative])

lemma ra_shifted_term_has_derivative:
  fixes x x0 :: "'a::euclidean_space"
    and c :: "('a ⇒ nat) ⇒ 'b::real_normed_vector"
  shows
    "((λy. ra_monomial (y - x0) α *R c α) has_derivative
        (λv. ra_Dmonomial (x - x0) α v *R c α))
      (at x)"
proof -
  have shift: "((λy. y - x0) has_derivative (λv. v)) (at x)"
    using has_derivative_diff[OF has_derivative_id has_derivative_const, of x0 "at x"]
    by simp
  have term_at: "((λz. ra_monomial z α *R c α) has_derivative
                   (λv. ra_Dmonomial (x - x0) α v *R c α)) (at (x - x0))"
    by (rule ra_term_has_derivative)
  show ?thesis
    using has_derivative_compose[OF shift term_at] by simp
qed


subsection ‹One-dimensional power-series infrastructure›

lemma ra_lev_finite:
  fixes n :: nat
  shows "finite {α::'a::euclidean_space ⇒ nat. α ∈ ra_idx ∧ ra_deg α = n}"
proof -
  have "{α::'a ⇒ nat. α ∈ ra_idx ∧ ra_deg α = n}
          ⊆ {hh. ∀x::'a. (x ∈ Basis ⟶ hh x ∈ {0..n}) ∧ (x ∉ Basis ⟶ hh x = 0)}"
  proof (rule subsetI)
    fix α :: "'a ⇒ nat" assume "α ∈ {α. α ∈ ra_idx ∧ ra_deg α = n}"
    then have a1: "α ∈ ra_idx" and a2: "ra_deg α = n" by auto
    have "∀x::'a. (x ∈ Basis ⟶ α x ∈ {0..n}) ∧ (x ∉ Basis ⟶ α x = 0)"
    proof (intro allI conjI impI)
      fix x :: 'a assume xB: "x ∈ Basis"
      have "α x ≤ (∑b∈Basis. α b)"
        by (rule member_le_sum[OF xB]) auto
      also have "… = n" using a2 by (simp only: ra_deg_def)
      finally show "α x ∈ {0..n}" by simp
    next
      fix x :: 'a assume "x ∉ Basis"
      with a1 show "α x = 0" by (auto simp: ra_idx_def)
    qed
    thus "α ∈ {hh. ∀x::'a. (x ∈ Basis ⟶ hh x ∈ {0..n}) ∧ (x ∉ Basis ⟶ hh x = 0)}"
      by simp
  qed
  moreover have "finite {hh::'a⇒nat. ∀x. (x ∈ Basis ⟶ hh x ∈ {0..n}) ∧ (x ∉ Basis ⟶ hh x = (0::nat))}"
    by (rule finite_set_of_finite_funs) auto
  ultimately show ?thesis by (rule finite_subset)
qed

lemma ra_lev_card_le:
  fixes n :: nat
  shows "card {α::'a::euclidean_space ⇒ nat. α ∈ ra_idx ∧ ra_deg α = n}
           ≤ (n+1) ^ card (Basis :: 'a set)"
proof -
  define G where "G = {hh::'a⇒nat. ∀x. (x ∈ Basis ⟶ hh x ∈ {0..n}) ∧ (x ∉ Basis ⟶ hh x = 0)}"
  have sub: "{α::'a ⇒ nat. α ∈ ra_idx ∧ ra_deg α = n} ⊆ G"
  proof (rule subsetI)
    fix α :: "'a ⇒ nat" assume "α ∈ {α. α ∈ ra_idx ∧ ra_deg α = n}"
    then have a1: "α ∈ ra_idx" and a2: "ra_deg α = n" by auto
    have "∀x::'a. (x ∈ Basis ⟶ α x ∈ {0..n}) ∧ (x ∉ Basis ⟶ α x = 0)"
    proof (intro allI conjI impI)
      fix x :: 'a assume xB: "x ∈ Basis"
      have "α x ≤ (∑b∈Basis. α b)" by (rule member_le_sum[OF xB]) auto
      also have "… = n" using a2 by (simp add: ra_deg_def)
      finally show "α x ∈ {0..n}" by simp
    next
      fix x :: 'a assume "x ∉ Basis"
      with a1 show "α x = 0" by (auto simp: ra_idx_def)
    qed
    thus "α ∈ G" by (simp add: G_def)
  qed
  have finG: "finite G" unfolding G_def
    by (rule finite_set_of_finite_funs) auto
  define R where "R = (λhh::'a⇒nat. restrict hh (Basis :: 'a set))"
  have Rinj: "inj_on R G"
  proof (rule inj_onI)
    fix x y assume xG: "x ∈ G" and yG: "y ∈ G" and Rxy: "R x = R y"
    show "x = y"
    proof (rule ext)
      fix b :: 'a
      show "x b = y b"
      proof (cases "b ∈ Basis")
        case True
        have "restrict x Basis b = restrict y Basis b" using Rxy by (simp add: R_def)
        thus ?thesis using True by (simp add: restrict_def)
      next
        case False
        with xG yG show ?thesis by (simp add: G_def)
      qed
    qed
  qed
  have Rimg: "R ` G ⊆ (Basis :: 'a set) →E {0..n}"
  proof
    fix g assume "g ∈ R ` G"
    then obtain hh where hh: "hh ∈ G" and g: "g = R hh" by auto
    have "g ∈ extensional Basis" using g by (simp add: R_def)
    moreover have "g ∈ Basis → {0..n}"
      using hh g by (auto simp: G_def R_def restrict_def)
    ultimately show "g ∈ (Basis :: 'a set) →E {0..n}"
      by (auto simp: PiE_def)
  qed
  have cardG: "card G ≤ (n+1) ^ card (Basis :: 'a set)"
  proof -
    have finPiE: "finite ((Basis :: 'a set) →E {0..n})"
      by (rule finite_PiE) auto
    have "card G = card (R ` G)" by (rule card_image[symmetric, OF Rinj])
    also have "… ≤ card ((Basis :: 'a set) →E {0..n})"
      by (rule card_mono[OF finPiE Rimg])
    also have "… = card {0..n} ^ card (Basis :: 'a set)"
      by (rule card_funcsetE) simp
    also have "… = (n+1) ^ card (Basis :: 'a set)" by simp
    finally show ?thesis .
  qed
  have "card {α::'a ⇒ nat. α ∈ ra_idx ∧ ra_deg α = n} ≤ card G"
    by (rule card_mono[OF finG sub])
  with cardG show ?thesis by linarith
qed


subsection ‹Coefficient bound›

lemma ra_coeff_bound:
  fixes c :: "('a::euclidean_space ⇒ nat) ⇒ 'b::real_normed_vector"
  assumes r: "0 < r"
    and HS: "⋀z. dist z x0 < r ⟹
              ((λα. ra_monomial (z - x0) α *R c α) has_sum F z) ra_idx"
    and ρpos: "0 < ρ"
    and corner: "ρ * norm (∑b∈(Basis::'a set). b) < r"
  obtains M where "M ≥ 0"
    and "⋀α. α ∈ ra_idx ⟹ norm (c α) ≤ M / ρ ^ (ra_deg α)"
proof -
  define e :: 'a where "e = (∑b∈(Basis::'a set). b)"
  define zc where "zc = x0 + ρ *R e"
  have dist_zc: "dist zc x0 < r"
  proof -
    have "dist zc x0 = norm (ρ *R e)" by (simp add: zc_def dist_norm)
    also have "… = ρ * norm e" using ρpos by simp
    also have "… < r" using corner by (simp add: e_def)
    finally show ?thesis .
  qed
  have mono_zc: "ra_monomial (zc - x0) α = ρ ^ (ra_deg α)" if "α ∈ ra_idx" for α
  proof -
    have inner_e: "e ∙ b = 1" if "b ∈ (Basis::'a set)" for b
      using that by (simp add: e_def inner_sum_left inner_Basis)
    have "ra_monomial (zc - x0) α = (∏b∈Basis. ((ρ *R e) ∙ b) ^ (α b))"
      by (simp add: ra_monomial_def zc_def)
    also have "… = (∏b∈(Basis::'a set). ρ ^ (α b))"
      by (rule prod.cong, auto simp: inner_e)
    also have "… = ρ ^ (∑b∈(Basis::'a set). α b)"
      by (simp add: power_sum)
    finally show ?thesis by (simp add: ra_deg_def)
  qed
  obtain M0 where Mnn: "M0 ≥ 0"
    and bound: "⋀α. α ∈ ra_idx ⟹ norm (ra_monomial (zc - x0) α *R c α) ≤ M0"
    using has_sum_imp_bounded_terms[OF HS[OF dist_zc]] by blast
  have final: "norm (c α) ≤ M0 / ρ ^ (ra_deg α)" if a: "α ∈ ra_idx" for α
  proof -
    have pos: "0 < ρ ^ (ra_deg α)" using ρpos by simp
    have "ρ ^ (ra_deg α) * norm (c α)
            = norm (ra_monomial (zc - x0) α *R c α)"
      using mono_zc[OF a] ρpos by simp
    also have "… ≤ M0" by (rule bound[OF a])
    finally have "ρ ^ (ra_deg α) * norm (c α) ≤ M0" .
    thus ?thesis using pos by (simp add: mult.commute pos_le_divide_eq)
  qed
  show ?thesis by (rule that[OF Mnn final])
qed

lemma ra_monomial_norm_le:
  fixes h :: "'a::euclidean_space"
  assumes "α ∈ ra_idx"
  shows "¦ra_monomial h α¦ ≤ norm h ^ (ra_deg α)"
proof -
  have factor: "¦(h ∙ b) ^ (α b)¦ ≤ norm h ^ (α b)" if "b ∈ (Basis::'a set)" for b
  proof -
    have "¦(h ∙ b) ^ (α b)¦ = ¦h ∙ b¦ ^ (α b)" by (simp add: power_abs)
    also have "… ≤ norm h ^ (α b)"
    proof (rule power_mono)
      have "¦h ∙ b¦ ≤ norm h * norm b" by (rule Cauchy_Schwarz_ineq2)
      thus "¦h ∙ b¦ ≤ norm h" using that by simp
      show "0 ≤ ¦h ∙ b¦" by simp
    qed
    finally show ?thesis .
  qed
  have "¦ra_monomial h α¦ = ¦∏b∈Basis. (h ∙ b) ^ (α b)¦"
    by (simp add: ra_monomial_def)
  also have "… = (∏b∈Basis. ¦(h ∙ b) ^ (α b)¦)"
    by (simp add: abs_prod)
  also have "… ≤ (∏b∈(Basis::'a set). norm h ^ (α b))"
    by (rule prod_mono) (auto simp: factor)
  also have "… = norm h ^ (∑b∈(Basis::'a set). α b)"
    by (simp add: power_sum)
  finally show ?thesis by (simp add: ra_deg_def)
qed


subsection ‹Directional-derivative monomial norm bound›

text ‹A uniform bound on @{const ra_Dmonomial}.›

lemma ra_Dmonomial_norm_le:
  fixes h v :: "'a::euclidean_space"
  assumes a: "α ∈ ra_idx" and s1: "1 ≤ s" and hs: "norm h ≤ s"
  shows "¦ra_Dmonomial h α v¦ ≤ norm v * real (ra_deg α) * s ^ (ra_deg α)"
proof -
  have spos: "0 ≤ s" using s1 by simp
  have hb: "¦h ∙ b¦ ≤ s" if "b ∈ (Basis::'a set)" for b
  proof -
    have "¦h ∙ b¦ ≤ norm h * norm b" by (rule Cauchy_Schwarz_ineq2)
    also have "… = norm h" using that by simp
    also have "… ≤ s" by (rule hs)
    finally show ?thesis .
  qed
  have vb: "¦v ∙ b¦ ≤ norm v" if "b ∈ (Basis::'a set)" for b
  proof -
    have "¦v ∙ b¦ ≤ norm v * norm b" by (rule Cauchy_Schwarz_ineq2)
    thus ?thesis using that by simp
  qed
  ― ‹each summand of @{const ra_Dmonomial} is bounded›
  have term_le:
    "¦real (α b) * (v ∙ b) * (h ∙ b) ^ (α b - 1) *
        (∏d∈Basis - {b}. (h ∙ d) ^ (α d))¦
       ≤ norm v * real (α b) * s ^ (ra_deg α)"
    if bB: "b ∈ (Basis::'a set)" for b
  proof -
    have e1: "¦(h ∙ b) ^ (α b - 1)¦ ≤ s ^ (α b - 1)"
    proof -
      have "¦(h ∙ b) ^ (α b - 1)¦ = ¦h ∙ b¦ ^ (α b - 1)" by (simp add: power_abs)
      also have "… ≤ s ^ (α b - 1)" using hb[OF bB] by (intro power_mono) auto
      finally show ?thesis .
    qed
    have e2: "¦∏d∈Basis - {b}. (h ∙ d) ^ (α d)¦ ≤ (∏d∈Basis - {b}. s ^ (α d))"
    proof -
      have "¦∏d∈Basis - {b}. (h ∙ d) ^ (α d)¦
              = (∏d∈Basis - {b}. ¦(h ∙ d) ^ (α d)¦)" by (simp add: abs_prod)
      also have "… ≤ (∏d∈Basis - {b}. s ^ (α d))"
      proof (rule prod_mono, intro conjI)
        fix d assume dB: "d ∈ Basis - {b}"
        show "0 ≤ ¦(h ∙ d) ^ (α d)¦" by simp
        have "¦(h ∙ d) ^ (α d)¦ = ¦h ∙ d¦ ^ (α d)" by (simp add: power_abs)
        also have "… ≤ s ^ (α d)" using hb dB by (intro power_mono) auto
        finally show "¦(h ∙ d) ^ (α d)¦ ≤ s ^ (α d)" .
      qed
      finally show ?thesis .
    qed
    have prod_s: "s ^ (α b - 1) * (∏d∈Basis - {b}. s ^ (α d)) ≤ s ^ (ra_deg α)"
    proof -
      have deg_split: "ra_deg α = α b + (∑d∈Basis - {b}. α d)"
        using bB by (simp add: ra_deg_def sum.remove[where x = b])
      have powsum: "(∏d∈Basis - {b}. s ^ (α d)) = s ^ (∑d∈Basis - {b}. α d)"
        by (simp add: power_sum)
      have "s ^ (α b - 1) * (∏d∈Basis - {b}. s ^ (α d))
              = s ^ ((α b - 1) + (∑d∈Basis - {b}. α d))"
        by (simp add: powsum power_add)
      also have "… ≤ s ^ (ra_deg α)"
      proof (rule power_increasing)
        show "(α b - 1) + (∑d∈Basis - {b}. α d) ≤ ra_deg α"
          using deg_split by simp
        show "1 ≤ s" by (rule s1)
      qed
      finally show ?thesis .
    qed
    have vbb: "¦v ∙ b¦ ≤ norm v" by (rule vb[OF bB])
    have nv0: "0 ≤ norm v" by simp
    have absprod_nn: "0 ≤ ¦∏d∈Basis - {b}. (h ∙ d) ^ (α d)¦" by simp
    have abspow_nn: "0 ≤ ¦(h ∙ b) ^ (α b - 1)¦" by simp
    have sprod_nn: "0 ≤ (∏d∈Basis - {b}. s ^ (α d))" using spos by (simp add: prod_nonneg)
    have spow_nn: "0 ≤ s ^ (α b - 1)" using spos by simp
    have "¦real (α b) * (v ∙ b) * (h ∙ b) ^ (α b - 1) *
            (∏d∈Basis - {b}. (h ∙ d) ^ (α d))¦
            = real (α b) * (¦v ∙ b¦ * (¦(h ∙ b) ^ (α b - 1)¦ *
                ¦∏d∈Basis - {b}. (h ∙ d) ^ (α d)¦))"
      by (simp add: abs_mult mult.assoc)
    also have "… ≤ real (α b) * (norm v * (s ^ (α b - 1) * (∏d∈Basis - {b}. s ^ (α d))))"
    proof (rule mult_left_mono)
      have "¦v ∙ b¦ * (¦(h ∙ b) ^ (α b - 1)¦ * ¦∏d∈Basis - {b}. (h ∙ d) ^ (α d)¦)
              ≤ norm v * (s ^ (α b - 1) * (∏d∈Basis - {b}. s ^ (α d)))"
      proof (rule mult_mono)
        show "¦v ∙ b¦ ≤ norm v" by (rule vbb)
        show "¦(h ∙ b) ^ (α b - 1)¦ * ¦∏d∈Basis - {b}. (h ∙ d) ^ (α d)¦
                ≤ s ^ (α b - 1) * (∏d∈Basis - {b}. s ^ (α d))"
          by (rule mult_mono[OF e1 e2 spow_nn absprod_nn])
        show "0 ≤ norm v" by simp
        show "0 ≤ ¦(h ∙ b) ^ (α b - 1)¦ * ¦∏d∈Basis - {b}. (h ∙ d) ^ (α d)¦"
          by simp
      qed
      thus "¦v ∙ b¦ * (¦(h ∙ b) ^ (α b - 1)¦ * ¦∏d∈Basis - {b}. (h ∙ d) ^ (α d)¦)
              ≤ norm v * (s ^ (α b - 1) * (∏d∈Basis - {b}. s ^ (α d)))" .
      show "0 ≤ real (α b)" by simp
    qed
    also have "… = norm v * real (α b) * (s ^ (α b - 1) * (∏d∈Basis - {b}. s ^ (α d)))"
      by (simp add: mult.commute mult.left_commute mult.assoc)
    also have "… ≤ norm v * real (α b) * s ^ (ra_deg α)"
    proof (rule mult_left_mono[OF prod_s])
      show "0 ≤ norm v * real (α b)" by simp
    qed
    finally show ?thesis .
  qed
  have "¦ra_Dmonomial h α v¦
          ≤ (∑b∈Basis. ¦real (α b) * (v ∙ b) * (h ∙ b) ^ (α b - 1) *
                (∏d∈Basis - {b}. (h ∙ d) ^ (α d))¦)"
    unfolding ra_Dmonomial_def by (rule sum_abs)
  also have "… ≤ (∑b∈Basis. norm v * real (α b) * s ^ (ra_deg α))"
    by (rule sum_mono) (use term_le in simp)
  also have "… = norm v * (∑b∈Basis. real (α b)) * s ^ (ra_deg α)"
    by (simp add: sum_distrib_left sum_distrib_right mult.assoc)
  also have "(∑b∈Basis. real (α b)) = real (ra_deg α)"
    by (simp add: ra_deg_def)
  finally show ?thesis .
qed


subsection ‹A degree-weighted geometric majorant is summable on @{const ra_idx}›

text ‹For ‹0 ≤ q < 1›, the family ‹(ra_deg α + 1) * q ^ ra_deg α› is summable on
  @{const ra_idx}.›

lemma deg_pow_summable:
  fixes q :: real
  assumes q0: "0 ≤ q" and q1: "q < 1"
  shows "(λα. real (ra_deg α + 1) * q ^ ra_deg α)
           summable_on (ra_idx :: ('a::euclidean_space ⇒ nat) set)"
proof (rule nonneg_bdd_above_summable_on)
  fix α :: "'a ⇒ nat" assume "α ∈ ra_idx"
  show "0 ≤ real (ra_deg α + 1) * q ^ ra_deg α" using q0 by simp
next
  define P where "P = card (Basis :: 'a set) + 1"
  define C where "C = (∑n. real ((n+1)^P) * q ^ n)"
  have pg_sum: "summable (λn. real ((n+1)^P) * q ^ n)"
    by (rule poly_geom_summable[OF q0 q1])
  show "bdd_above (sum (λα. real (ra_deg α + 1) * q ^ ra_deg α)
            ` {F. F ⊆ (ra_idx :: ('a ⇒ nat) set) ∧ finite F})"
  proof (rule bdd_aboveI2)
    fix F :: "('a ⇒ nat) set" assume "F ∈ {F. F ⊆ ra_idx ∧ finite F}"
    then have Fsub: "F ⊆ ra_idx" and Ffin: "finite F" by auto
    define D where "D = (if F = {} then 0 else Max (ra_deg ` F))"
    define lev where "lev = (λn. {α::'a⇒nat. α ∈ ra_idx ∧ ra_deg α = n})"
    define gg where "gg = (λα::'a⇒nat. real (ra_deg α + 1) * q ^ ra_deg α)"
    have ggnn: "0 ≤ gg β" if "β ∈ ra_idx" for β
      using q0 by (simp add: gg_def)
    have Fincl: "F ⊆ (⋃n∈{..D}. lev n)"
    proof
      fix β assume bF: "β ∈ F"
      have "ra_deg β ≤ D"
      proof (cases "F = {}")
        case True thus ?thesis using bF by simp
      next
        case False
        have "ra_deg β ≤ Max (ra_deg ` F)" using bF Ffin by (intro Max_ge) auto
        thus ?thesis using False by (simp add: D_def)
      qed
      moreover have "β ∈ ra_idx" using bF Fsub by auto
      ultimately show "β ∈ (⋃n∈{..D}. lev n)" by (auto simp: lev_def)
    qed
    have levfin: "finite (lev n)" for n by (simp add: lev_def ra_lev_finite)
    have Ufin: "finite (⋃n∈{..D}. lev n)" by (auto intro: levfin)
    have "sum gg F ≤ sum gg (⋃n∈{..D}. lev n)"
      by (rule sum_mono2[OF Ufin Fincl]) (use ggnn in ‹auto simp: lev_def›)
    also have "sum gg (⋃n∈{..D}. lev n) = (∑n≤D. sum gg (lev n))"
    proof (rule sum.UNION_disjoint)
      show "finite {..D}" by simp
      show "∀n∈{..D}. finite (lev n)" using levfin by simp
      show "∀m∈{..D}. ∀n∈{..D}. m ≠ n ⟶ lev m ∩ lev n = {}"
        by (auto simp: lev_def)
    qed
    also have "(∑n≤D. sum gg (lev n)) ≤ (∑n≤D. real ((n+1)^P) * q ^ n)"
    proof (rule sum_mono)
      fix n assume "n ∈ {..D}"
      have levn_deg: "ra_deg β = n" if "β ∈ lev n" for β
        using that by (simp add: lev_def)
      have "sum gg (lev n) = (∑β∈lev n. real (n + 1) * q ^ n)"
        by (rule sum.cong) (auto simp: gg_def levn_deg)
      also have "… = real (card (lev n)) * (real (n + 1) * q ^ n)" by simp
      also have "… ≤ real ((n+1) ^ card (Basis::'a set)) * (real (n + 1) * q ^ n)"
      proof (rule mult_right_mono)
        have "card (lev n) ≤ (n+1) ^ card (Basis::'a set)"
          using ra_lev_card_le[of n] by (simp add: lev_def)
        thus "real (card (lev n)) ≤ real ((n+1) ^ card (Basis::'a set))"
          by (simp only: of_nat_le_iff)
        show "0 ≤ real (n + 1) * q ^ n" using q0 by simp
      qed
      also have "… = real ((n+1)^P) * q ^ n"
      proof -
        have eqp: "(n+1) ^ P = (n+1) ^ card (Basis::'a set) * (n + 1)"
          by (simp add: P_def)
        have req: "real ((n+1) ^ card (Basis::'a set)) * real (n + 1) = real ((n+1) ^ P)"
        proof -
          have "real ((n+1) ^ card (Basis::'a set)) * real (n + 1)
                  = real ((n+1) ^ card (Basis::'a set) * (n + 1))"
            by (simp only: of_nat_mult)
          also have "… = real ((n+1) ^ P)" using eqp by simp
          finally show ?thesis .
        qed
        have "real ((n+1) ^ card (Basis::'a set)) * (real (n + 1) * q ^ n)
                = (real ((n+1) ^ card (Basis::'a set)) * real (n + 1)) * q ^ n"
          by (simp add: mult.assoc)
        also have "… = real ((n+1)^P) * q ^ n" by (simp only: req)
        finally show ?thesis .
      qed
      finally show "sum gg (lev n) ≤ real ((n+1)^P) * q ^ n" .
    qed
    also have "(∑n≤D. real ((n+1)^P) * q ^ n) ≤ (∑n. real ((n+1)^P) * q ^ n)"
      by (rule sum_le_suminf[OF pg_sum]) (auto simp: q0)
    also have "… = C" by (simp add: C_def)
    finally show "sum (λα. real (ra_deg α + 1) * q ^ ra_deg α) F ≤ C"
      by (simp add: gg_def)
  qed
qed


subsection ‹@{const ra_idx} is countably infinite›

lemma countable_ra_idx: "countable (ra_idx :: ('a::euclidean_space ⇒ nat) set)"
proof -
  have inj: "inj_on (λα. restrict α (Basis :: 'a set)) ra_idx"
  proof (rule inj_onI)
    fix x y assume xy: "x ∈ ra_idx" "y ∈ ra_idx"
      and eq: "restrict x (Basis::'a set) = restrict y (Basis::'a set)"
    show "x = y"
    proof (rule ext)
      fix b show "x b = y b"
      proof (cases "b ∈ (Basis::'a set)")
        case True
        have "restrict x Basis b = restrict y Basis b" using eq by simp
        thus ?thesis using True by simp
      next
        case False
        have "x b = 0" using xy(1) False by (force simp: ra_idx_def)
        moreover have "y b = 0" using xy(2) False by (force simp: ra_idx_def)
        ultimately show ?thesis by simp
      qed
    qed
  qed
  have img: "(λα. restrict α (Basis :: 'a set)) ` ra_idx ⊆ ((Basis::'a set) →E (UNIV :: nat set))"
    by (auto simp: PiE_def extensional_def restrict_def)
  have cnt: "countable ((Basis::'a set) →E (UNIV :: nat set))"
    by (rule countable_PiE) auto
  have "countable ((λα. restrict α (Basis :: 'a set)) ` ra_idx)"
    using img cnt by (rule countable_subset)
  thus ?thesis using inj by (rule countable_image_inj_on)
qed

lemma infinite_ra_idx: "infinite (ra_idx :: ('a::euclidean_space ⇒ nat) set)"
proof -
  obtain j where jB: "j ∈ (Basis :: 'a set)"
    using nonempty_Basis by blast
  define F where "F = (λn::nat. (λb::'a. if b = j then n else 0))"
  have injF: "inj F"
  proof (rule injI)
    fix m n assume "F m = F n"
    then have "F m j = F n j" by simp
    thus "m = n" by (simp add: F_def)
  qed
  have rng: "range F ⊆ ra_idx"
  proof (rule subsetI)
    fix z assume "z ∈ range F"
    then obtain n where z: "z = F n" by auto
    have "{b. z b ≠ 0} ⊆ {j}" by (auto simp: z F_def split: if_split_asm)
    also have "… ⊆ Basis" using jB by simp
    finally show "z ∈ ra_idx" by (simp add: ra_idx_def)
  qed
  have "infinite (range F)" using injF by (rule range_inj_infinite)
  with rng show ?thesis using finite_subset by blast
qed

definition ra_enum :: "nat ⇒ ('a::euclidean_space ⇒ nat)" where
  "ra_enum = from_nat_into (ra_idx :: ('a ⇒ nat) set)"

lemma bij_ra_enum: "bij_betw (ra_enum :: nat ⇒ ('a::euclidean_space ⇒ nat)) UNIV ra_idx"
  unfolding ra_enum_def
  by (rule bij_betw_from_nat_into[OF countable_ra_idx infinite_ra_idx])

lemma ra_enum_in: "ra_enum n ∈ (ra_idx :: ('a::euclidean_space ⇒ nat) set)"
  using bij_ra_enum by (auto simp: bij_betw_def)

lemma inj_ra_enum: "inj (ra_enum :: nat ⇒ ('a::euclidean_space ⇒ nat))"
  using bij_ra_enum by (auto simp: bij_betw_def)

lemma range_ra_enum: "range (ra_enum :: nat ⇒ ('a::euclidean_space ⇒ nat)) = ra_idx"
  using bij_betw_imp_surj_on[OF bij_ra_enum] by simp


subsection ‹Per-coordinate monomial / derivative bounds›

text ‹Coordinatewise bounds on ‹h› bound the basis monomial.›

lemma ra_monomial_norm_le_coord:
  fixes h :: "'a::euclidean_space"
  assumes hb: "⋀b. b ∈ Basis ⟹ ¦h ∙ b¦ ≤ s b"
  shows "¦ra_monomial h α¦ ≤ (∏b∈Basis. s b ^ (α b))"
proof -
  have "¦ra_monomial h α¦ = (∏b∈Basis. ¦(h ∙ b) ^ (α b)¦)"
    by (simp add: ra_monomial_def abs_prod)
  also have "… ≤ (∏b∈Basis. s b ^ (α b))"
  proof (rule prod_mono, intro conjI)
    fix b assume bB: "b ∈ (Basis::'a set)"
    show "0 ≤ ¦(h ∙ b) ^ (α b)¦" by simp
    have "¦(h ∙ b) ^ (α b)¦ = ¦h ∙ b¦ ^ (α b)" by (simp add: power_abs)
    also have "… ≤ s b ^ (α b)" using hb[OF bB] by (intro power_mono) auto
    finally show "¦(h ∙ b) ^ (α b)¦ ≤ s b ^ (α b)" .
  qed
  finally show ?thesis .
qed

text ‹Per-coordinate bound on the directional derivative monomial.›

lemma ra_Dmonomial_norm_le_coord:
  fixes h v :: "'a::euclidean_space"
  assumes hb: "⋀b. b ∈ Basis ⟹ ¦h ∙ b¦ ≤ s b"
    and spos: "⋀b. b ∈ Basis ⟹ 0 < s b"
  shows "¦ra_Dmonomial h α v¦
           ≤ norm v * (∑b∈Basis. real (α b) / s b) * (∏b∈Basis. s b ^ (α b))"
proof -
  have vb: "¦v ∙ b¦ ≤ norm v" if "b ∈ (Basis::'a set)" for b
  proof -
    have "¦v ∙ b¦ ≤ norm v * norm b" by (rule Cauchy_Schwarz_ineq2)
    thus ?thesis using that by simp
  qed
  have prodnn: "0 ≤ (∏d∈Basis. s d ^ (α d))"
    using spos by (intro prod_nonneg) (auto intro: less_imp_le)
  have term_le:
    "¦real (α b) * (v ∙ b) * (h ∙ b) ^ (α b - 1) *
        (∏d∈Basis - {b}. (h ∙ d) ^ (α d))¦
       ≤ norm v * (real (α b) / s b) * (∏d∈Basis. s d ^ (α d))"
    if bB: "b ∈ (Basis::'a set)" for b
  proof (cases "α b = 0")
    case True
    have rhs_nn: "0 ≤ norm v * (real (α b) / s b) * (∏d∈Basis. s d ^ (α d))"
      using prodnn spos[OF bB] by (simp add: zero_le_mult_iff)
    show ?thesis using True rhs_nn by simp
  next
    case False
    then have a1: "1 ≤ α b" by simp
    have sb_pos: "0 < s b" by (rule spos[OF bB])
    ― ‹absolute value of the b-summand›
    have e1: "¦(h ∙ b) ^ (α b - 1)¦ ≤ s b ^ (α b - 1)"
    proof -
      have "¦(h ∙ b) ^ (α b - 1)¦ = ¦h ∙ b¦ ^ (α b - 1)" by (simp add: power_abs)
      also have "… ≤ s b ^ (α b - 1)" using hb[OF bB] by (intro power_mono) auto
      finally show ?thesis .
    qed
    have e2: "¦∏d∈Basis - {b}. (h ∙ d) ^ (α d)¦ ≤ (∏d∈Basis - {b}. s d ^ (α d))"
    proof -
      have "¦∏d∈Basis - {b}. (h ∙ d) ^ (α d)¦
              = (∏d∈Basis - {b}. ¦(h ∙ d) ^ (α d)¦)" by (simp add: abs_prod)
      also have "… ≤ (∏d∈Basis - {b}. s d ^ (α d))"
      proof (rule prod_mono, intro conjI)
        fix d assume dB: "d ∈ Basis - {b}"
        show "0 ≤ ¦(h ∙ d) ^ (α d)¦" by simp
        have "¦(h ∙ d) ^ (α d)¦ = ¦h ∙ d¦ ^ (α d)" by (simp add: power_abs)
        also have "… ≤ s d ^ (α d)" using hb dB by (intro power_mono) auto
        finally show "¦(h ∙ d) ^ (α d)¦ ≤ s d ^ (α d)" .
      qed
      finally show ?thesis .
    qed
    ― ‹recombine the @{term "s b ^ (α b - 1)"} factor into the full product over the @{term ‹1/s b›}›
    have prod_id: "s b ^ (α b - 1) * (∏d∈Basis - {b}. s d ^ (α d))
                    = (1 / s b) * (∏d∈Basis. s d ^ (α d))"
    proof -
      have split: "(∏d∈Basis. s d ^ (α d))
                     = s b ^ (α b) * (∏d∈Basis - {b}. s d ^ (α d))"
        using bB by (simp add: prod.remove[where x = b])
      have pw: "s b ^ (α b) = s b * s b ^ (α b - 1)"
        using a1 by (simp add: power_eq_if)
      have "(1 / s b) * (∏d∈Basis. s d ^ (α d))
              = (1 / s b) * (s b * s b ^ (α b - 1)) * (∏d∈Basis - {b}. s d ^ (α d))"
        by (simp add: split pw mult.assoc)
      also have "… = s b ^ (α b - 1) * (∏d∈Basis - {b}. s d ^ (α d))"
        using sb_pos by simp
      finally show ?thesis by (rule sym)
    qed
    have abs_eq:
      "¦real (α b) * (v ∙ b) * (h ∙ b) ^ (α b - 1) *
          (∏d∈Basis - {b}. (h ∙ d) ^ (α d))¦
        = real (α b) * (¦v ∙ b¦ *
            (¦(h ∙ b) ^ (α b - 1)¦ * ¦∏d∈Basis - {b}. (h ∙ d) ^ (α d)¦))"
      by (simp add: abs_mult mult.assoc)
    have spow_nn: "0 ≤ s b ^ (α b - 1)" using sb_pos by simp
    have sprod_nn: "0 ≤ (∏d∈Basis - {b}. s d ^ (α d))" using spos by (intro prod_nonneg) (auto intro: less_imp_le)
    have absprod_nn: "0 ≤ ¦∏d∈Basis - {b}. (h ∙ d) ^ (α d)¦" by simp
    have "real (α b) * (¦v ∙ b¦ *
            (¦(h ∙ b) ^ (α b - 1)¦ * ¦∏d∈Basis - {b}. (h ∙ d) ^ (α d)¦))
          ≤ real (α b) * (norm v *
            (s b ^ (α b - 1) * (∏d∈Basis - {b}. s d ^ (α d))))"
    proof (rule mult_left_mono)
      show "¦v ∙ b¦ * (¦(h ∙ b) ^ (α b - 1)¦ * ¦∏d∈Basis - {b}. (h ∙ d) ^ (α d)¦)
              ≤ norm v * (s b ^ (α b - 1) * (∏d∈Basis - {b}. s d ^ (α d)))"
      proof (rule mult_mono)
        show "¦v ∙ b¦ ≤ norm v" by (rule vb[OF bB])
        show "¦(h ∙ b) ^ (α b - 1)¦ * ¦∏d∈Basis - {b}. (h ∙ d) ^ (α d)¦
                ≤ s b ^ (α b - 1) * (∏d∈Basis - {b}. s d ^ (α d))"
          by (rule mult_mono[OF e1 e2 spow_nn absprod_nn])
        show "0 ≤ norm v" by simp
        show "0 ≤ ¦(h ∙ b) ^ (α b - 1)¦ * ¦∏d∈Basis - {b}. (h ∙ d) ^ (α d)¦" by simp
      qed
      show "0 ≤ real (α b)" by simp
    qed
    also have "real (α b) * (norm v *
            (s b ^ (α b - 1) * (∏d∈Basis - {b}. s d ^ (α d))))
          = real (α b) * (norm v * ((1 / s b) * (∏d∈Basis. s d ^ (α d))))"
      by (simp only: prod_id)
    also have "… = norm v * (real (α b) / s b) * (∏d∈Basis. s d ^ (α d))"
      by (simp add: field_simps)
    finally show ?thesis using abs_eq by simp
  qed
  have "¦ra_Dmonomial h α v¦
          ≤ (∑b∈Basis. ¦real (α b) * (v ∙ b) * (h ∙ b) ^ (α b - 1) *
                (∏d∈Basis - {b}. (h ∙ d) ^ (α d))¦)"
    unfolding ra_Dmonomial_def by (rule sum_abs)
  also have "… ≤ (∑b∈Basis. norm v * (real (α b) / s b) * (∏d∈Basis. s d ^ (α d)))"
    by (rule sum_mono) (use term_le in simp)
  also have "… = norm v * (∑b∈Basis. real (α b) / s b) * (∏b∈Basis. s b ^ (α b))"
    by (simp add: sum_distrib_left sum_distrib_right mult.assoc)
  finally show ?thesis .
qed


subsection ‹Multivariate geometric majorant (single ratio)›

lemma geom_idx_summable:
  fixes q :: real
  assumes q0: "0 ≤ q" and q1: "q < 1"
  shows "(λα. q ^ ra_deg α) summable_on (ra_idx :: ('a::euclidean_space ⇒ nat) set)"
proof (rule nonneg_bdd_above_summable_on)
  fix α :: "'a ⇒ nat" assume "α ∈ ra_idx"
  show "0 ≤ q ^ ra_deg α" using q0 by simp
next
  define Bnd where "Bnd = (1 / (1 - q)) ^ card (Basis :: 'a set)"
  have geom_sum_le: "(∑k≤N. q ^ k) ≤ 1 / (1 - q)" for N
  proof -
    have nq: "norm q < 1" using q0 q1 by simp
    have "(∑k≤N. q ^ k) ≤ (∑k. q ^ k)"
      by (rule sum_le_suminf) (use q0 nq summable_geometric[of q] in auto)
    also have "… = 1 / (1 - q)" using nq by (simp add: suminf_geometric)
    finally show ?thesis .
  qed
  have part_bound: "(∑α∈F. q ^ ra_deg α) ≤ Bnd"
    if F: "F ⊆ ra_idx" "finite F" for F :: "('a ⇒ nat) set"
  proof -
    define N where "N = Max (insert 0 (⋃α∈F. α ` (Basis :: 'a set)))"
    have finUN: "finite (insert 0 (⋃α∈F. α ` (Basis :: 'a set)))"
      using F(2) by (simp add: finite_UN_I)
    have Nbound: "α b ≤ N" if "α ∈ F" "b ∈ Basis" for α b
    proof -
      have "α b ∈ insert 0 (⋃α∈F. α ` (Basis :: 'a set))" using that by auto
      thus ?thesis unfolding N_def using finUN by (intro Max_ge)
    qed
    define R where "R = (λα::'a⇒nat. restrict α (Basis :: 'a set))"
    have R_in: "R α ∈ PiE (Basis :: 'a set) (λ_. {0..N})" if "α ∈ F" for α
      using that Nbound by (auto simp: R_def PiE_def Pi_def extensional_def)
    have R_deg: "(∑b∈Basis. R α b) = ra_deg α" for α
      by (simp add: R_def ra_deg_def)
    have R_inj: "inj_on R F"
    proof (rule inj_onI)
      fix a b assume ab: "a ∈ F" "b ∈ F" and Req: "R a = R b"
      show "a = b"
      proof (rule ext)
        fix x show "a x = b x"
        proof (cases "x ∈ Basis")
          case True
          have "restrict a Basis x = restrict b Basis x" using Req by (simp add: R_def)
          thus ?thesis using True by simp
        next
          case False
          have "a ∈ ra_idx" "b ∈ ra_idx" using ab F(1) by auto
          then have "a x = 0" "b x = 0" using False by (auto simp: ra_idx_def)
          thus ?thesis by simp
        qed
      qed
    qed
    have "(∑α∈F. q ^ ra_deg α) = (∑g∈R ` F. q ^ (∑b∈Basis. g b))"
    proof -
      have "(∑α∈F. q ^ ra_deg α) = (∑α∈F. q ^ (∑b∈Basis. R α b))"
        by (simp add: R_deg)
      also have "… = (∑g∈R ` F. q ^ (∑b∈Basis. g b))"
        by (rule sum.reindex_cong[OF R_inj refl, symmetric]) simp
      finally show ?thesis .
    qed
    also have "… ≤ (∑g∈PiE (Basis :: 'a set) (λ_. {0..N}). q ^ (∑b∈Basis. g b))"
    proof (rule sum_mono2)
      show "finite (PiE (Basis :: 'a set) (λ_. {0..N}))" by (intro finite_PiE) auto
      show "R ` F ⊆ PiE (Basis :: 'a set) (λ_. {0..N})" using R_in by auto
      fix g assume "g ∈ PiE (Basis :: 'a set) (λ_. {0..N}) - R ` F"
      show "0 ≤ q ^ (∑b∈Basis. g b)" using q0 by simp
    qed
    also have "… = (∑g∈PiE (Basis :: 'a set) (λ_. {0..N}). (∏b∈Basis. q ^ g b))"
      by (intro sum.cong refl) (simp add: power_sum)
    also have "… = (∏b∈(Basis :: 'a set). ∑k∈{0..N}. q ^ k)"
    proof -
      have finPi: "finite (PiE (Basis :: 'a set) (λ_. {0..N}))"
        by (intro finite_PiE) auto
      have summable:
        "(λg. ∏b∈(Basis :: 'a set). q ^ g b)
          summable_on PiE (Basis :: 'a set) (λ_. {0..N})"
        by (rule summable_on_finite[OF finPi])
      have "(∑g∈PiE (Basis :: 'a set) (λ_. {0..N}).
              ∏b∈Basis. q ^ g b) =
            infsum (λg. ∏b∈(Basis :: 'a set). q ^ g b)
              (PiE (Basis :: 'a set) (λ_. {0..N}))"
        by (simp add: finPi)
      also have "… = (∏b∈(Basis :: 'a set). infsum (λk. q ^ k) {0..N})"
        using local.summable by (subst infsum_prod_PiE, simp_all)
      also have "… = (∏b∈(Basis :: 'a set). ∑k∈{0..N}. q ^ k)"
        by simp
      finally show ?thesis .
    qed
    also have "… ≤ (∏b∈(Basis :: 'a set). 1 / (1 - q))"
    proof (rule prod_mono)
      fix b :: 'a assume "b ∈ Basis"
      have "0 ≤ (∑k∈{0..N}. q ^ k)" using q0 by (intro sum_nonneg) simp
      moreover have "(∑k∈{0..N}. q ^ k) ≤ 1 / (1 - q)"
        using geom_sum_le[of N] by (simp add: atMost_atLeast0)
      ultimately show "0 ≤ (∑k∈{0..N}. q ^ k) ∧ (∑k∈{0..N}. q ^ k) ≤ 1 / (1 - q)" by blast
    qed
    also have "… = Bnd" by (simp add: Bnd_def)
    finally show ?thesis .
  qed
  show "bdd_above (sum (λα. q ^ ra_deg α) ` {F. F ⊆ (ra_idx :: ('a ⇒ nat) set) ∧ finite F})"
    by (rule bdd_aboveI2[where M = Bnd]) (use part_bound in auto)
qed


subsection ‹Per-coordinate coefficient bound›

text ‹The Cauchy estimate for the coefficients, with one radius ‹ρ b› per coordinate.›

lemma ra_coeff_bound_coord:
  fixes c :: "('a::euclidean_space ⇒ nat) ⇒ 'b::real_normed_vector"
  assumes r: "0 < r"
    and HS: "⋀z. dist z x0 < r ⟹
              ((λα. ra_monomial (z - x0) α *R c α) has_sum F z) ra_idx"
    and ρpos: "⋀b. b ∈ Basis ⟹ 0 < ρ b"
    and corner: "dist (x0 + (∑b∈(Basis::'a set). ρ b *R b)) x0 < r"
  obtains M where "M ≥ 0"
    and "⋀α. α ∈ ra_idx ⟹ norm (c α) ≤ M / (∏b∈Basis. ρ b ^ (α b))"
proof -
  define zc where "zc = x0 + (∑b∈(Basis::'a set). ρ b *R b)"
  have dist_zc: "dist zc x0 < r" using corner by (simp add: zc_def)
  have inner_zc: "(zc - x0) ∙ b = ρ b" if "b ∈ (Basis::'a set)" for b
  proof -
    have "(zc - x0) ∙ b = (∑d∈(Basis::'a set). ρ d *R d) ∙ b"
      by (simp add: zc_def)
    also have "… = (∑d∈(Basis::'a set). ρ d * (d ∙ b))"
      by (simp only: inner_sum_left inner_scaleR_left)
    also have "… = ρ b"
      using that by (simp add: inner_Basis sum.remove[where x = b] cong: if_cong)
    finally show ?thesis .
  qed
  have mono_zc: "ra_monomial (zc - x0) α = (∏b∈Basis. ρ b ^ (α b))" for α
    by (simp add: ra_monomial_def) (rule prod.cong[OF refl], simp add: inner_zc)
  have prodpos: "0 < (∏b∈Basis. ρ b ^ (α b))" for α
    using ρpos by (intro prod_pos) auto
  obtain M0 where Mnn: "M0 ≥ 0"
    and bound: "⋀α. α ∈ ra_idx ⟹ norm (ra_monomial (zc - x0) α *R c α) ≤ M0"
    using has_sum_imp_bounded_terms[OF HS[OF dist_zc]] by blast
  have final: "norm (c α) ≤ M0 / (∏b∈Basis. ρ b ^ (α b))" if a: "α ∈ ra_idx" for α
  proof -
    have pos: "0 < (∏b∈Basis. ρ b ^ (α b))" by (rule prodpos)
    have "(∏b∈Basis. ρ b ^ (α b)) * norm (c α)
            = norm (ra_monomial (zc - x0) α *R c α)"
      using mono_zc[of α] pos by simp
    also have "… ≤ M0" by (rule bound[OF a])
    finally have "(∏b∈Basis. ρ b ^ (α b)) * norm (c α) ≤ M0" .
    thus ?thesis using pos by (simp add: mult.commute pos_le_divide_eq)
  qed
  show ?thesis by (rule that[OF Mnn final])
qed

subsection ‹Uniform majorant for the differentiated series near an interior point›

text ‹Near an interior point of the ball, the differentiated series has a uniform summable
  majorant.›

lemma diff_majorant_interior:
  fixes c :: "('a::euclidean_space ⇒ nat) ⇒ 'b::real_normed_vector"
  assumes r: "0 < r"
    and HS: "⋀z. dist z x0 < r ⟹
              ((λα. ra_monomial (z - x0) α *R c α) has_sum F z) ra_idx"
    and x: "dist x x0 < r"
  obtains δ0 q K where
    "0 < δ0" "0 ≤ q" "q < 1" "0 ≤ K"
    and "(λα. K * real (ra_deg α + 1) * q ^ ra_deg α) summable_on ra_idx"
    and "⋀z. dist z x < δ0 ⟹ dist z x0 < r"
    and "⋀z α v. dist z x < δ0 ⟹ α ∈ ra_idx ⟹
          norm (ra_Dmonomial (z - x0) α v *R c α)
            ≤ norm v * (K * real (ra_deg α + 1) * q ^ ra_deg α)"
proof -
  define h0 where "h0 = x - x0"
  have nh0: "norm h0 < r" using x by (simp add: h0_def dist_norm)
  ― ‹choose a margin @{term δ} so the per-coordinate corner stays inside the ball›
  ― ‹the corner-squared distance as a function of margin›
  define csq where "csq = (λm::real. (∑b∈(Basis::'a set). (¦h0 ∙ b¦ + 2 * m)2))"
  have csq0: "csq 0 = (norm h0)2"
  proof -
    have "csq 0 = (∑b∈Basis. (h0 ∙ b)2)" by (simp add: csq_def)
    also have "… = (norm h0)2" by (rule norm_sq_eq_sum_coord[symmetric])
    finally show ?thesis .
  qed
  have csq_cont: "csq ─0→ csq 0"
    unfolding csq_def by (intro tendsto_intros)
  have "(norm h0)2 < r2" using nh0 by (simp add: power_strict_mono)
  then have csq0lt: "csq 0 < r2" using csq0 by simp
  have ev_at: "∀F m in at 0. csq m < r2"
    by (rule order_tendstoD(2)[OF csq_cont csq0lt])
  have "at_right (0::real) ≤ at 0" by (simp add: at_le)
  then have "∀F m in at_right (0::real). csq m < r2"
    using ev_at by (rule filter_leD)
  then obtain δ1 where δ1pos: "0 < δ1" and csqlt: "⋀m. 0 < m ⟹ m < δ1 ⟹ csq m < r2"
    by (auto simp: eventually_at_right_field)
  define δ where "δ = δ1 / 2"
  have δpos: "0 < δ" using δ1pos by (simp add: δ_def)
  have δlt: "δ < δ1" using δ1pos by (simp add: δ_def)
  have csqδ: "csq δ < r2" using csqlt[OF δpos δlt] .
  ― ‹the per-coordinate radii›
  define ρ where "ρ = (λb. ¦h0 ∙ b¦ + 2 * δ)"
  define ss where "ss = (λb. ¦h0 ∙ b¦ + δ)"
  have ρpos: "0 < ρ b" for b using δpos by (simp add: ρ_def)
  have sspos: "0 < ss b" for b using δpos by (simp add: ss_def)
  have ss_lt_ρ: "ss b < ρ b" for b using δpos by (simp add: ss_def ρ_def)
  ― ‹the single geometric ratio: largest of the per-coordinate ratios›
  define q where "q = Max ((λb. ss b / ρ b) ` (Basis :: 'a set))"
  have finB: "finite ((λb. ss b / ρ b) ` (Basis :: 'a set))" by simp
  have neB: "((λb. ss b / ρ b) ` (Basis :: 'a set)) ≠ {}"
    using nonempty_Basis by simp
  have q_ge: "ss b / ρ b ≤ q" if "b ∈ (Basis::'a set)" for b
    unfolding q_def using that by (intro Max_ge) auto
  have ratio_lt1: "ss b / ρ b < 1" for b
    using ss_lt_ρ[of b] ρpos[of b] by (simp add: divide_less_eq)
  have ratio_nn: "0 ≤ ss b / ρ b" for b
    using sspos[of b] ρpos[of b] by simp
  have q1: "q < 1" unfolding q_def
    using finB neB ratio_lt1 by (subst Max_less_iff) auto
  have q0: "0 ≤ q"
  proof -
    obtain b where "b ∈ (Basis::'a set)" using nonempty_Basis by blast
    thus ?thesis using q_ge[of b] ratio_nn[of b] by linarith
  qed
  ― ‹corner condition for the coefficient bound›
  have corner: "dist (x0 + (∑b∈(Basis::'a set). ρ b *R b)) x0 < r"
  proof -
    have "dist (x0 + (∑b∈(Basis::'a set). ρ b *R b)) x0
            = norm (∑b∈(Basis::'a set). ρ b *R b)"
      by (simp add: dist_norm)
    also have "… = sqrt (∑b∈(Basis::'a set). (ρ b)2)"
    proof -
      have "(norm (∑b∈(Basis::'a set). ρ b *R b))2 = (∑b∈(Basis::'a set). (ρ b)2)"
        by (rule norm_basis_combo_sq)
      moreover have "0 ≤ norm (∑b∈(Basis::'a set). ρ b *R b)" by simp
      ultimately show ?thesis by (metis real_sqrt_unique)
    qed
    also have "(∑b∈(Basis::'a set). (ρ b)2) = csq δ"
      by (simp add: csq_def ρ_def)
    also have "sqrt (csq δ) < sqrt (r2)"
      using csqδ by (intro real_sqrt_less_mono)
    also have "sqrt (r2) = r" using r by simp
    finally show ?thesis .
  qed
  ― ‹the per-coordinate coefficient bound›
  obtain M where Mnn: "M ≥ 0"
    and cbound: "⋀α. α ∈ ra_idx ⟹ norm (c α) ≤ M / (∏b∈Basis. ρ b ^ (α b))"
    using ra_coeff_bound_coord[OF r HS ρpos corner] by blast
  define K where "K = M / δ"
  have Knn: "0 ≤ K" using Mnn δpos by (simp add: K_def)
  ― ‹summability of the majorant›
  have maj_summ: "(λα. K * real (ra_deg α + 1) * q ^ ra_deg α) summable_on ra_idx"
  proof -
    have "(λα. real (ra_deg α + 1) * q ^ ra_deg α) summable_on ra_idx"
      by (rule deg_pow_summable[OF q0 q1])
    then have "(λα. K * (real (ra_deg α + 1) * q ^ ra_deg α)) summable_on ra_idx"
      by (rule summable_on_cmult_right)
    thus ?thesis by (simp add: mult.assoc)
  qed
  ― ‹the in-ball implication and the uniform term bound›
  have inball: "dist z x0 < r" if dz: "dist z x < δ" for z
  proof -
    ― ‹Bound coordinatewise, then sum-of-squares.›
    have coordb: "¦(z - x0) ∙ b¦ ≤ ss b" if "b ∈ (Basis::'a set)" for b
    proof -
      have "¦(z - x0) ∙ b¦ = ¦(z - x) ∙ b + h0 ∙ b¦"
        by (simp add: h0_def inner_diff_left)
      also have "… ≤ ¦(z - x) ∙ b¦ + ¦h0 ∙ b¦" by (rule abs_triangle_ineq)
      also have "¦(z - x) ∙ b¦ ≤ norm (z - x)"
      proof -
        have "¦(z - x) ∙ b¦ ≤ norm (z - x) * norm b" by (rule Cauchy_Schwarz_ineq2)
        thus ?thesis using that by simp
      qed
      also have "norm (z - x) < δ" using dz by (simp add: dist_norm)
      finally show ?thesis by (simp add: ss_def)
    qed
    have "(norm (z - x0))2 = (∑b∈(Basis::'a set). ((z - x0) ∙ b)2)"
      by (rule norm_sq_eq_sum_coord)
    also have "… ≤ (∑b∈(Basis::'a set). (ss b)2)"
    proof (rule sum_mono)
      fix b assume bB: "b ∈ (Basis::'a set)"
      have "((z - x0) ∙ b)2 = ¦(z - x0) ∙ b¦2" by simp
      also have "… ≤ (ss b)2"
        using coordb[OF bB] sspos[of b] by (intro power_mono) auto
      finally show "((z - x0) ∙ b)2 ≤ (ss b)2" .
    qed
    also have "(∑b∈(Basis::'a set). (ss b)2) < (∑b∈(Basis::'a set). (ρ b)2)"
    proof (rule sum_strict_mono)
      show "finite (Basis :: 'a set)" by simp
      show "(Basis :: 'a set) ≠ {}" using nonempty_Basis by simp
      fix b assume "b ∈ (Basis::'a set)"
      show "(ss b)2 < (ρ b)2"
        using ss_lt_ρ[of b] sspos[of b] ρpos[of b] by (intro power_strict_mono) auto
    qed
    also have "(∑b∈(Basis::'a set). (ρ b)2) = csq δ" by (simp add: csq_def ρ_def)
    also have "csq δ < r2" by (rule csqδ)
    finally have nlt: "(norm (z - x0))2 < r2" .
    have "norm (z - x0) < r"
      by (rule power2_less_imp_less[OF nlt]) (simp add: r less_imp_le)
    thus ?thesis by (simp add: dist_norm)
  qed
  have termbound:
    "norm (ra_Dmonomial (z - x0) α v *R c α)
       ≤ norm v * (K * real (ra_deg α + 1) * q ^ ra_deg α)"
    if dz: "dist z x < δ" and a: "α ∈ ra_idx" for z α v
  proof -
    have coordb: "¦(z - x0) ∙ b¦ ≤ ss b" if "b ∈ (Basis::'a set)" for b
    proof -
      have "¦(z - x0) ∙ b¦ = ¦(z - x) ∙ b + h0 ∙ b¦"
        by (simp add: h0_def inner_diff_left)
      also have "… ≤ ¦(z - x) ∙ b¦ + ¦h0 ∙ b¦" by (rule abs_triangle_ineq)
      also have "¦(z - x) ∙ b¦ ≤ norm (z - x)"
      proof -
        have "¦(z - x) ∙ b¦ ≤ norm (z - x) * norm b" by (rule Cauchy_Schwarz_ineq2)
        thus ?thesis using that by simp
      qed
      also have "norm (z - x) < δ" using dz by (simp add: dist_norm)
      finally show ?thesis by (simp add: ss_def)
    qed
    ― ‹the directional-derivative magnitude›
    have Dbound: "¦ra_Dmonomial (z - x0) α v¦
                    ≤ norm v * (∑b∈Basis. real (α b) / ss b) * (∏b∈Basis. ss b ^ (α b))"
      by (rule ra_Dmonomial_norm_le_coord[OF coordb sspos])
    ― ‹bound the coordinate-weighted sum factor›
    have sumfac: "(∑b∈Basis. real (α b) / ss b) ≤ real (ra_deg α) / δ"
    proof -
      have "(∑b∈(Basis::'a set). real (α b) / ss b) ≤ (∑b∈(Basis::'a set). real (α b) / δ)"
      proof (rule sum_mono)
        fix b assume bB: "b ∈ (Basis::'a set)"
        have "δ ≤ ss b" by (simp add: ss_def)
        thus "real (α b) / ss b ≤ real (α b) / δ"
          using δpos sspos[of b] by (intro divide_left_mono) (auto simp: zero_le_mult_iff)
      qed
      also have "… = (∑b∈(Basis::'a set). real (α b)) / δ"
        by (simp add: sum_divide_distrib)
      also have "(∑b∈(Basis::'a set). real (α b)) = real (ra_deg α)"
        by (simp add: ra_deg_def)
      finally show ?thesis .
    qed
    ― ‹bound the product factor times the coefficient norm›
    have prodq: "(∏b∈Basis. ss b ^ (α b)) / (∏b∈Basis. ρ b ^ (α b)) ≤ q ^ ra_deg α"
    proof -
      have prodpos: "0 < (∏b∈(Basis::'a set). ρ b ^ (α b))"
        using ρpos by (intro prod_pos) auto
      have "(∏b∈Basis. ss b ^ (α b)) / (∏b∈Basis. ρ b ^ (α b))
              = (∏b∈(Basis::'a set). (ss b / ρ b) ^ (α b))"
        by (simp add: prod_dividef power_divide)
      also have "… ≤ (∏b∈(Basis::'a set). q ^ (α b))"
      proof (rule prod_mono, intro conjI)
        fix b assume bB: "b ∈ (Basis::'a set)"
        show "0 ≤ (ss b / ρ b) ^ (α b)" using ratio_nn[of b] by simp
        show "(ss b / ρ b) ^ (α b) ≤ q ^ (α b)"
          using q_ge[OF bB] ratio_nn[of b] by (intro power_mono) auto
      qed
      also have "… = q ^ (∑b∈(Basis::'a set). α b)" by (simp add: power_sum)
      finally show ?thesis by (simp add: ra_deg_def)
    qed
    ― ‹assemble›
    have prodpos: "0 < (∏b∈(Basis::'a set). ρ b ^ (α b))"
      using ρpos by (intro prod_pos) auto
    have ssprod_nn: "0 ≤ (∏b∈(Basis::'a set). ss b ^ (α b))"
      using sspos by (intro prod_nonneg) (auto intro: less_imp_le)
    have deg_nn: "0 ≤ (∑b∈(Basis::'a set). real (α b) / ss b)"
      by (intro sum_nonneg) (simp add: sspos less_imp_le)
    have "norm (ra_Dmonomial (z - x0) α v *R c α)
            = ¦ra_Dmonomial (z - x0) α v¦ * norm (c α)"
      by (simp only: norm_scaleR)
    also have "… ≤ (norm v * (∑b∈Basis. real (α b) / ss b) * (∏b∈Basis. ss b ^ (α b)))
                      * (M / (∏b∈Basis. ρ b ^ (α b)))"
    proof (rule mult_mono[OF Dbound cbound[OF a]])
      show "0 ≤ norm v * (∑b∈Basis. real (α b) / ss b) * (∏b∈Basis. ss b ^ (α b))"
        using deg_nn ssprod_nn by (simp add: zero_le_mult_iff)
      show "0 ≤ norm (c α)" by simp
    qed
    also have "… = norm v * (∑b∈Basis. real (α b) / ss b) * M
                      * ((∏b∈Basis. ss b ^ (α b)) / (∏b∈Basis. ρ b ^ (α b)))"
      by (simp add: field_simps)
    also have "… ≤ norm v * (real (ra_deg α) / δ) * M * (q ^ ra_deg α)"
    proof -
      have le1: "norm v * (∑b∈Basis. real (α b) / ss b) * M ≤ norm v * (real (ra_deg α) / δ) * M"
      proof -
        have "norm v * (∑b∈Basis. real (α b) / ss b) ≤ norm v * (real (ra_deg α) / δ)"
          by (rule mult_left_mono[OF sumfac]) simp
        thus ?thesis by (rule mult_right_mono[OF _ Mnn])
      qed
      have nn_b: "0 ≤ norm v * (real (ra_deg α) / δ) * M"
        using δpos Mnn by (simp add: zero_le_mult_iff)
      have nn_c: "0 ≤ (∏b∈Basis. ss b ^ (α b)) / (∏b∈Basis. ρ b ^ (α b))"
        using ssprod_nn prodpos by (simp add: zero_le_divide_iff)
      show ?thesis by (rule mult_mono[OF le1 prodq nn_b nn_c])
    qed
    also have "… = norm v * (K * real (ra_deg α) * q ^ ra_deg α)"
      by (simp add: K_def field_simps)
    also have "… ≤ norm v * (K * real (ra_deg α + 1) * q ^ ra_deg α)"
    proof (rule mult_left_mono)
      have "K * real (ra_deg α) * q ^ ra_deg α ≤ K * real (ra_deg α + 1) * q ^ ra_deg α"
        using Knn q0 by (intro mult_right_mono mult_left_mono) auto
      thus "K * real (ra_deg α) * q ^ ra_deg α ≤ K * real (ra_deg α + 1) * q ^ ra_deg α" .
      show "0 ≤ norm v" by simp
    qed
    finally show ?thesis .
  qed
  show ?thesis
    by (rule that[OF δpos q0 q1 Knn maj_summ inball termbound])
qed


subsection ‹Term-by-term differentiation›

theorem ra_power_series_has_derivative:
  fixes f :: "'a::euclidean_space ⇒ 'b::banach"
  assumes r: "0 < r"
    and series: "⋀y. dist y x0 < r ⟹
        ((λα. ra_monomial (y - x0) α *R c α) has_sum f y) ra_idx"
    and x: "dist x x0 < r"
  shows
    "(f has_derivative
       (λv. infsum (λα. ra_Dmonomial (x - x0) α v *R c α) ra_idx))
      (at x)"
proof -
  ― ‹uniform majorant near @{term x}›
  obtain δ0 q K where δ0: "0 < δ0" and q0: "0 ≤ q" and q1: "q < 1" and Knn: "0 ≤ K"
    and majsumm: "(λα. K * real (ra_deg α + 1) * q ^ ra_deg α) summable_on ra_idx"
    and inball: "⋀z. dist z x < δ0 ⟹ dist z x0 < r"
    and tbound: "⋀z α v. dist z x < δ0 ⟹ α ∈ ra_idx ⟹
          norm (ra_Dmonomial (z - x0) α v *R c α)
            ≤ norm v * (K * real (ra_deg α + 1) * q ^ ra_deg α)"
    using diff_majorant_interior[OF r series x] by blast
  define D where "D = (λα::'a⇒nat. K * real (ra_deg α + 1) * q ^ ra_deg α)"
  have Dnn: "0 ≤ D α" for α using Knn q0 by (simp add: D_def)
  have D_summ: "D summable_on ra_idx" unfolding D_def using majsumm
    by (metis (lifting) ext deg_pow_summable q0 q1 summable_on_cmult_right
        vector_space_over_itself.scale_scale) 
  define S where "S = ball x δ0"
  have convS: "convex S" by (simp add: S_def)
  have xS: "x ∈ S" using δ0 by (simp add: S_def)
  have openS: "open S" by (simp add: S_def)
  have S_ball: "⋀z. z ∈ S ⟹ dist z x < δ0" by (simp add: S_def dist_commute)
  have S_in_r: "⋀z. z ∈ S ⟹ dist z x0 < r" using inball S_ball by blast
  ― ‹the enumeration of @{term ra_idx}›
  define en where "en = (ra_enum :: nat ⇒ ('a ⇒ nat))"
  have en_in: "en k ∈ ra_idx" for k by (simp add: en_def ra_enum_in)
  have en_inj: "inj en" by (simp add: en_def inj_ra_enum)
  have en_rng: "range en = ra_idx" by (simp add: en_def range_ra_enum)
  ― ‹partial sums and their derivatives›
  define fn where "fn = (λn z. ∑k<n. ra_monomial (z - x0) (en k) *R c (en k))"
  define f'n where
    "f'n = (λn z w. ∑k<n. ra_Dmonomial (z - x0) (en k) w *R c (en k))"
  define g' where "g' = (λz w. infsum (λα. ra_Dmonomial (z - x0) α w *R c α) ra_idx)"
  ― ‹each @{term ‹fn n›} has derivative @{term ‹f'n n z›} at @{term z}›
  have derfn: "((fn n) has_derivative (f'n n z)) (at z within S)" for n z
  proof -
    have "((λz. ∑k<n. ra_monomial (z - x0) (en k) *R c (en k))
            has_derivative (λw. ∑k<n. ra_Dmonomial (z - x0) (en k) w *R c (en k))) (at z)"
      by (rule has_derivative_sum)
         (rule ra_shifted_term_has_derivative)
    then have "((fn n) has_derivative (f'n n z)) (at z)"
      by (simp add: fn_def f'n_def)
    thus ?thesis by (rule has_derivative_at_withinI)
  qed
  ― ‹for ‹z ∈ S›, the partial sums ‹fn n z› converge to ‹f z››
  have termsum_z: "((λα. ra_monomial (z - x0) α *R c α) has_sum f z) ra_idx"
    if "z ∈ S" for z using series[OF S_in_r[OF that]] .
  have fn_lim: "(λn. fn n z) ⇢ f z" if zS: "z ∈ S" for z
  proof -
    have "((λα. ra_monomial (z - x0) α *R c α) has_sum f z) (range en)"
      using termsum_z[OF zS] by (simp add: en_rng)
    then have "((λk. ra_monomial (z - x0) (en k) *R c (en k)) has_sum f z) UNIV"
      using en_inj by (subst (asm) has_sum_reindex) (auto simp: o_def)
    then have "(λk. ra_monomial (z - x0) (en k) *R c (en k)) sums f z"
      by (rule has_sum_imp_sums)
    thus ?thesis by (simp add: fn_def sums_def)
  qed
  ― ‹for ‹z ∈ S› and each direction ‹w›, the derivative family is norm-summable with sum
     ‹g' z w››
  have Dsummf: "(λα. ra_Dmonomial (z - x0) α w *R c α) summable_on ra_idx"
    if zS: "z ∈ S" for z w
  proof (rule abs_summable_summable, rule summable_on_comparison_test
            [where f = "λα. norm w * D α"])
    show "(λα. norm w * D α) summable_on ra_idx"
      by (rule summable_on_cmult_right[OF D_summ])
  next
    fix α :: "'a ⇒ nat" assume a: "α ∈ ra_idx"
    show "norm (ra_Dmonomial (z - x0) α w *R c α) ≤ norm w * D α"
      using tbound[OF S_ball[OF zS] a] by (simp add: D_def)
  next
    fix α :: "'a ⇒ nat" assume "α ∈ ra_idx"
    show "0 ≤ norm (ra_Dmonomial (z - x0) α w *R c α)" by simp
  qed
  have g'sums: "(λk. ra_Dmonomial (z - x0) (en k) w *R c (en k)) sums g' z w"
    if zS: "z ∈ S" for z w
  proof -
    have "((λα. ra_Dmonomial (z - x0) α w *R c α) has_sum g' z w) ra_idx"
      unfolding g'_def using Dsummf[OF zS] by (rule has_sum_infsum)
    then have "((λα. ra_Dmonomial (z - x0) α w *R c α) has_sum g' z w) (range en)"
      by (simp add: en_rng)
    then have "((λk. ra_Dmonomial (z - x0) (en k) w *R c (en k)) has_sum g' z w) UNIV"
      using en_inj by (subst (asm) has_sum_reindex) (auto simp: o_def)
    thus ?thesis by (rule has_sum_imp_sums)
  qed
  ― ‹the majorant series, reindexed along @{term en}, is summable on @{term UNIV}›
  have Den_summ: "summable (λk. D (en k))"
  proof -
    have "(D has_sum (infsum D ra_idx)) ra_idx" using D_summ by (rule has_sum_infsum)
    then have "(D has_sum (infsum D ra_idx)) (range en)" by (simp add: en_rng)
    then have "((D ∘ en) has_sum (infsum D ra_idx)) UNIV"
      using en_inj by (subst (asm) has_sum_reindex)
    then have "(D ∘ en) sums (infsum D ra_idx)" by (rule has_sum_imp_sums)
    thus ?thesis by (auto simp: o_def summable_def)
  qed
  ― ‹the uniform-derivative condition for @{thm [source] has_derivative_sequence}›
  have unif: "∀F n in sequentially. ∀z∈S. ∀w. norm (f'n n z w - g' z w) ≤ e * norm w"
    if epos: "0 < e" for e
  proof -
    obtain N where N: "⋀n. n ≥ N ⟹ norm (∑i. D (en (i + n))) < e"
      using suminf_exist_split[OF epos Den_summ] by blast
    have key: "norm (f'n n z w - g' z w) ≤ e * norm w"
      if nN: "n ≥ N" and zS: "z ∈ S" for n z w
    proof -
      define gk where "gk = (λk. ra_Dmonomial (z - x0) (en k) w *R c (en k))"
      have gk_sums: "gk sums g' z w" using g'sums[OF zS] by (simp add: gk_def)
      have gk_summ: "summable gk" using gk_sums by (simp add: sums_summable)
      have gknorm_le: "norm (gk k) ≤ norm w * D (en k)" for k
        using tbound[OF S_ball[OF zS] en_in[of k]] by (simp add: gk_def D_def)
      ― ‹norm-summability of @{term gk} via comparison›
      have gknorm_summ: "summable (λk. norm (gk k))"
      proof (rule summable_comparison_test')
        show "summable (λk. norm w * D (en k))"
          by (rule summable_mult[OF Den_summ])
        fix k show "norm (norm (gk k)) ≤ norm w * D (en k)"
          using gknorm_le[of k] by simp
      qed
      ― ‹the tail estimate›
      have "norm (f'n n z w - g' z w) = norm ((∑k<n. gk k) - g' z w)"
        by (simp add: f'n_def gk_def)
      also have "… = norm (∑i. gk (i + n))"
      proof -
        have "(∑i. gk (i + n)) = g' z w - (∑k<n. gk k)"
          using sums_split_initial_segment[OF gk_sums, of n] by (simp add: sums_iff)
        thus ?thesis by (simp add: norm_minus_commute)
      qed
      also have "… ≤ (∑i. norm (gk (i + n)))"
      proof (rule summable_norm)
        show "summable (λi. norm (gk (i + n)))"
          using gknorm_summ by (rule summable_ignore_initial_segment[where k = n])
      qed
      also have "… ≤ (∑i. norm w * D (en (i + n)))"
      proof (rule suminf_le)
        show "⋀i. norm (gk (i + n)) ≤ norm w * D (en (i + n))" using gknorm_le by simp
        show "summable (λi. norm (gk (i + n)))"
          using gknorm_summ by (rule summable_ignore_initial_segment[where k = n])
        show "summable (λi. norm w * D (en (i + n)))"
        proof -
          have "summable (λk. norm w * D (en k))" by (rule summable_mult[OF Den_summ])
          thus ?thesis by (rule summable_ignore_initial_segment[where k = n])
        qed
      qed
      also have "… = norm w * (∑i. D (en (i + n)))"
        by (rule suminf_mult)
           (rule summable_ignore_initial_segment[OF Den_summ, where k = n, simplified])
      also have "… ≤ norm w * e"
      proof (rule mult_left_mono)
        have "(∑i. D (en (i + n))) ≤ norm (∑i. D (en (i + n)))" by simp
        also have "… < e" by (rule N[OF nN])
        finally show "(∑i. D (en (i + n))) ≤ e" by simp
        show "0 ≤ norm w" by simp
      qed
      finally show ?thesis by (simp only: mult.commute)
    qed
    show ?thesis
      unfolding eventually_sequentially using key by blast
  qed
  ― ‹apply @{thm [source] has_derivative_sequence}›
  have basept: "(λn. fn n x) ⇢ f x" by (rule fn_lim[OF xS])
  obtain g where g: "⋀z. z ∈ S ⟹ (λn. fn n z) ⇢ g z ∧ (g has_derivative g' z) (at z within S)"
    using has_derivative_sequence[OF convS derfn unif xS basept] by metis
  ― ‹@{term g} agrees with @{term f} on @{term S}›
  have g_eq_f: "g z = f z" if zS: "z ∈ S" for z
    using g[OF zS] fn_lim[OF zS] LIMSEQ_unique by blast
  have hd_g_within: "(g has_derivative g' x) (at x within S)" using g[OF xS] by blast
  have atSx: "at x within S = at x" using openS xS by (subst at_within_open, simp_all)
  have hd_g_at: "(g has_derivative g' x) (at x)" using hd_g_within by (simp only: atSx)
  ― ‹transfer to @{term f}›
  have "(f has_derivative g' x) (at x)"
    by (rule has_derivative_transform_within_open[OF hd_g_at openS xS])
       (simp add: g_eq_f)
  thus ?thesis by (simp only: g'_def)
qed


subsection ‹Incrementing a multi-index stays in the index set›

lemma ra_inc_in_idx:
  fixes α :: "'a::euclidean_space ⇒ nat"
  assumes "α ∈ ra_idx" and "b ∈ Basis"
  shows "ra_inc α b ∈ ra_idx"
proof -
  have "{c. ra_inc α b c ≠ 0} ⊆ {c. α c ≠ 0} ∪ {b}"
    by (auto simp: ra_inc_def)
  also have "… ⊆ Basis"
    using assms by (auto simp: ra_idx_def)
  finally show ?thesis by (simp add: ra_idx_def)
qed


subsection ‹Decrement is the inverse of @{const ra_inc} on the support slice›

definition ra_dec ::
  "('a ⇒ nat) ⇒ 'a ⇒ ('a ⇒ nat)" where
  "ra_dec α b = α(b := α b - 1)"

lemma ra_dec_in_idx:
  fixes α :: "'a::euclidean_space ⇒ nat"
  assumes "α ∈ ra_idx"
  shows "ra_dec α b ∈ ra_idx"
proof -
  have "{c. ra_dec α b c ≠ 0} ⊆ {c. α c ≠ 0}"
    by (auto simp: ra_dec_def)
  also have "… ⊆ Basis" using assms by (auto simp: ra_idx_def)
  finally show ?thesis by (simp add: ra_idx_def)
qed

lemma ra_inc_dec:
  assumes "1 ≤ α b"
  shows "ra_inc (ra_dec α b) b = α"
  using assms by (auto simp: ra_inc_def ra_dec_def fun_eq_iff)

lemma ra_dec_inc:
  "ra_dec (ra_inc α b) b = α"
  by (auto simp: ra_inc_def ra_dec_def fun_eq_iff)


subsection ‹Splitting a monomial off one basis direction›

lemma ra_monomial_split:
  fixes x :: "'a::euclidean_space"
  assumes "b ∈ Basis"
  shows "ra_monomial x β
          = (x ∙ b) ^ (β b) * (∏d∈Basis - {b}. (x ∙ d) ^ (β d))"
  unfolding ra_monomial_def
  by (subst prod.remove[OF finite_Basis assms]) simp


subsection ‹The per-basis LHS and RHS pieces›

definition ra_dterm ::
  "'a::euclidean_space ⇒ 'a ⇒ (('a ⇒ nat) ⇒ 'b::real_normed_vector) ⇒ 'a ⇒ ('a ⇒ nat) ⇒ 'b"
  where
  "ra_dterm x v c b α =
     (real (α b) * (v ∙ b) * (x ∙ b) ^ (α b - 1) *
        (∏d∈Basis - {b}. (x ∙ d) ^ (α d))) *R c α"

definition ra_dterm_shift ::
  "'a::euclidean_space ⇒ 'a ⇒ (('a ⇒ nat) ⇒ 'b::real_normed_vector) ⇒ 'a ⇒ ('a ⇒ nat) ⇒ 'b"
  where
  "ra_dterm_shift x v c b β =
     (real (Suc (β b)) * (v ∙ b)) *R (ra_monomial x β *R c (ra_inc β b))"

lemma Dmonomial_eq_sum_dterm:
  fixes x v :: "'a::euclidean_space"
  shows "ra_Dmonomial x α v *R c α = (∑b∈Basis. ra_dterm x v c b α)"
  unfolding ra_Dmonomial_def ra_dterm_def
  by (simp add: scaleR_sum_left)

lemma dcoeff_eq_sum_dterm_shift:
  fixes x v :: "'a::euclidean_space"
  shows "ra_monomial x α *R ra_dcoeff c v α = (∑b∈Basis. ra_dterm_shift x v c b α)"
  unfolding ra_dcoeff_def ra_dterm_shift_def
  by (simp add: scaleR_right.sum) (simp add: algebra_simps)

text ‹Key pointwise identity: the LHS piece at @{term "ra_inc β b"} equals the RHS piece at
  @{term β}.›

lemma ra_dterm_inc_eq_dterm_shift:
  fixes x v :: "'a::euclidean_space"
  assumes "b ∈ Basis"
  shows "ra_dterm x v c b (ra_inc β b) = ra_dterm_shift x v c b β"
proof -
  have inc_b: "(ra_inc β b) b = Suc (β b)" by (simp add: ra_inc_def)
  have inc_d: "⋀d. d ≠ b ⟹ (ra_inc β b) d = β d" by (simp add: ra_inc_def)
  have prod_eq: "(∏d∈Basis - {b}. (x ∙ d) ^ ((ra_inc β b) d))
                  = (∏d∈Basis - {b}. (x ∙ d) ^ (β d))"
    by (rule prod.cong) (auto simp: inc_d)
  have mono: "(x ∙ b) ^ (β b) * (∏d∈Basis - {b}. (x ∙ d) ^ (β d)) = ra_monomial x β"
    using ra_monomial_split[OF assms] by simp
  have "ra_dterm x v c b (ra_inc β b)
        = (real (Suc (β b)) * (v ∙ b) * (x ∙ b) ^ (β b) *
             (∏d∈Basis - {b}. (x ∙ d) ^ (β d))) *R c (ra_inc β b)"
    unfolding ra_dterm_def by (simp add: inc_b prod_eq)
  also have "… = (real (Suc (β b)) * (v ∙ b)) *R
                    (((x ∙ b) ^ (β b) * (∏d∈Basis - {b}. (x ∙ d) ^ (β d))) *R c (ra_inc β b))"
    by (simp add: mult.assoc)
  also have "… = (real (Suc (β b)) * (v ∙ b)) *R (ra_monomial x β *R c (ra_inc β b))"
    by (simp add: mono)
  finally show ?thesis by (simp add: ra_dterm_shift_def)
qed

text ‹Off the support (@{term "α b = 0"}) the LHS piece vanishes.›

lemma ra_dterm_zero_off_support:
  assumes "α b = 0"
  shows "ra_dterm x v c b α = 0"
  by (simp add: ra_dterm_def assms)


subsection ‹Per-basis reindexing of has-sum›

lemma has_sum_dterm_iff_dterm_shift:
  fixes x v :: "'a::euclidean_space"
    and c :: "('a ⇒ nat) ⇒ 'b::banach"
  assumes b: "b ∈ Basis"
  shows "(ra_dterm x v c b has_sum s) ra_idx ⟷ (ra_dterm_shift x v c b has_sum s) ra_idx"
proof -
  define A where "A = {α ∈ ra_idx. 1 ≤ α b}"
  have restrict: "(ra_dterm x v c b has_sum s) ra_idx ⟷ (ra_dterm x v c b has_sum s) A"
  proof (rule has_sum_cong_neutral)
    show "⋀α. α ∈ ra_idx - A ⟹ ra_dterm x v c b α = 0"
      by (auto simp: A_def intro!: ra_dterm_zero_off_support)
  next
    show "⋀α. α ∈ A - ra_idx ⟹ ra_dterm x v c b α = 0" by (auto simp: A_def)
  next
    show "⋀α. α ∈ ra_idx ∩ A ⟹ ra_dterm x v c b α = ra_dterm x v c b α" by simp
  qed
  have reidx: "(ra_dterm x v c b has_sum s) A ⟷ (ra_dterm_shift x v c b has_sum s) ra_idx"
  proof (rule has_sum_reindex_bij_witness[where i = "λβ. ra_inc β b" and j = "λα. ra_dec α b"])
    fix α assume "α ∈ A"
    then have "1 ≤ α b" by (simp add: A_def)
    thus "ra_inc (ra_dec α b) b = α" by (rule ra_inc_dec)
  next
    fix α assume "α ∈ A"
    then have "α ∈ ra_idx" by (simp add: A_def)
    thus "ra_dec α b ∈ ra_idx" by (rule ra_dec_in_idx)
  next
    fix β :: "'a ⇒ nat" assume "β ∈ ra_idx"
    show "ra_dec (ra_inc β b) b = β" by (rule ra_dec_inc)
  next
    fix β :: "'a ⇒ nat" assume "β ∈ ra_idx"
    then have "ra_inc β b ∈ ra_idx" using b by (rule ra_inc_in_idx)
    moreover have "1 ≤ (ra_inc β b) b" by (simp add: ra_inc_def)
    ultimately show "ra_inc β b ∈ A" by (simp add: A_def)
  next
    fix α assume "α ∈ A"
    then have a1: "1 ≤ α b" by (simp add: A_def)
    have "ra_dterm_shift x v c b (ra_dec α b) = ra_dterm x v c b (ra_inc (ra_dec α b) b)"
      by (rule ra_dterm_inc_eq_dterm_shift[OF b, symmetric])
    also have "… = ra_dterm x v c b α"
      using ra_inc_dec[where α = α and b = b, OF a1] by simp
    finally show "ra_dterm_shift x v c b (ra_dec α b) = ra_dterm x v c b α" .
  next
    show "s = s" by simp
  qed
  show ?thesis using restrict reidx by blast
qed

lemma summable_dterm_iff_dterm_shift:
  fixes x v :: "'a::euclidean_space"
    and c :: "('a ⇒ nat) ⇒ 'b::banach"
  assumes b: "b ∈ Basis"
  shows "ra_dterm x v c b summable_on ra_idx ⟷ ra_dterm_shift x v c b summable_on ra_idx"
  unfolding summable_on_def using has_sum_dterm_iff_dterm_shift[OF b] by blast

lemma infsum_dterm_eq_dterm_shift:
  fixes x v :: "'a::euclidean_space"
    and c :: "('a ⇒ nat) ⇒ 'b::banach"
  assumes b: "b ∈ Basis"
  shows "infsum (ra_dterm x v c b) ra_idx = infsum (ra_dterm_shift x v c b) ra_idx"
proof (cases "ra_dterm x v c b summable_on ra_idx")
  case True
  then obtain s where s_Def: "(ra_dterm x v c b has_sum s) ra_idx"
    by (auto simp: summable_on_def)
  then have "(ra_dterm_shift x v c b has_sum s) ra_idx"
    using has_sum_dterm_iff_dterm_shift[OF b] by blast
  then show ?thesis using infsumI
    using s_Def by blast 
next
  case False
  then have "¬ ra_dterm_shift x v c b summable_on ra_idx"
    using summable_dterm_iff_dterm_shift[OF b] by blast
  with False show ?thesis by (simp add: infsum_not_exists)
qed

subsection ‹The reindexing identity›

text ‹Summability in each basis direction is assumed: plain summability of the differentiated
  family would not suffice in an infinite-dimensional Banach space (Dvoretzky--Rogers).›

lemma ra_derivative_reindex:
  fixes x v :: "'a::euclidean_space"
    and c :: "('a ⇒ nat) ⇒ 'b::banach"
  assumes S: "⋀b. b ∈ Basis ⟹
                (λα. ra_monomial x α *R (real (Suc (α b)) *R c (ra_inc α b)))
                  summable_on ra_idx"
  shows
    "infsum (λα. ra_Dmonomial x α v *R c α) ra_idx
       = infsum (λα. ra_monomial x α *R ra_dcoeff c v α) ra_idx"
proof -
  have Qsum: "ra_dterm_shift x v c b summable_on ra_idx" if b: "b ∈ Basis" for b
  proof -
    have eq: "ra_dterm_shift x v c b
              = (λα. (v ∙ b) *R
                       (ra_monomial x α *R (real (Suc (α b)) *R c (ra_inc α b))))"
      by (rule ext, simp add: ra_dterm_shift_def  ac_simps)
    have "(λα. (v ∙ b) *R
              (ra_monomial x α *R (real (Suc (α b)) *R c (ra_inc α b))))
            summable_on ra_idx"
      using summable_on_bounded_linear[OF bounded_linear_scaleR_right[of "v ∙ b"], OF S[OF b]]
      by simp
    thus ?thesis by (simp only: eq)
  qed
  have Psum: "ra_dterm x v c b summable_on ra_idx" if b: "b ∈ Basis" for b
    using Qsum[OF b] summable_dterm_iff_dterm_shift[OF b] by blast
  have "infsum (λα. ra_Dmonomial x α v *R c α) ra_idx
        = infsum (λα. ∑b∈Basis. ra_dterm x v c b α) ra_idx"
    by (simp add: Dmonomial_eq_sum_dterm)
  also have "… = (∑b∈Basis. infsum (ra_dterm x v c b) ra_idx)"
    by (rule infsum_finite_sum[OF finite_Basis]) (rule Psum)
  also have "… = (∑b∈Basis. infsum (ra_dterm_shift x v c b) ra_idx)"
    by (rule sum.cong[OF refl]) (rule infsum_dterm_eq_dterm_shift)
  also have "… = infsum (λα. ∑b∈Basis. ra_dterm_shift x v c b α) ra_idx"
    by (rule infsum_finite_sum[OF finite_Basis, symmetric]) (rule Qsum)
  also have "… = infsum (λα. ra_monomial x α *R ra_dcoeff c v α) ra_idx"
    by (simp add: dcoeff_eq_sum_dterm_shift)
  finally show ?thesis .
qed


(* Term-by-term differentiation and the derivative power series *)


lemma ra_power_series_differentiable:
  fixes f :: "'a::euclidean_space ⇒ 'b::banach"
  assumes r: "0 < r"
    and series:
      "⋀y. dist y x0 < r ⟹
        ((λα. ra_monomial (y - x0) α *R c α)
          has_sum f y) ra_idx"
    and x: "dist x x0 < r"
  shows "f differentiable (at x)"
proof -
  have "(f has_derivative
       (λv. infsum (λα. ra_Dmonomial (x - x0) α v *R c α) ra_idx))
      (at x)"
    by (rule ra_power_series_has_derivative[OF r series x])
  thus ?thesis
    unfolding differentiable_def by blast
qed


lemma ra_power_series_frechet_derivative:
  fixes f :: "'a::euclidean_space ⇒ 'b::banach"
  assumes r: "0 < r"
    and series:
      "⋀y. dist y x0 < r ⟹
        ((λα. ra_monomial (y - x0) α *R c α)
          has_sum f y) ra_idx"
    and x: "dist x x0 < r"
  shows
    "frechet_derivative f (at x) v =
       infsum
         (λα. ra_Dmonomial (x - x0) α v *R c α)
         ra_idx"
proof -
  have "(λv. infsum (λα. ra_Dmonomial (x - x0) α v *R c α) ra_idx)
        = frechet_derivative f (at x)"
    by (rule frechet_derivative_at[OF ra_power_series_has_derivative[OF r series x]])
  thus ?thesis by (rule fun_cong[symmetric])
qed


lemma ra_power_series_continuous_on:
  fixes f :: "'a::euclidean_space ⇒ 'b::banach"
  assumes r: "0 < r"
    and series:
      "⋀y. dist y x0 < r ⟹
        ((λα. ra_monomial (y - x0) α *R c α)
          has_sum f y) ra_idx"
  shows "continuous_on (ball x0 r) f"
proof (rule continuous_at_imp_continuous_on, clarify)
  fix x assume "x ∈ ball x0 r"
  then have x: "dist x x0 < r" by (simp add: dist_commute)
  have "f differentiable (at x)"
    by (rule ra_power_series_differentiable[OF r series x])
  then have "continuous (at x within UNIV) f"
    by (rule differentiable_imp_continuous_within)
  then show "continuous (at x) f" by simp
qed

text ‹Summability of the differentiated coefficient family near an interior point, in any
  direction.›

lemma ra_Dmono_summable_interior:
  fixes c :: "('a::euclidean_space ⇒ nat) ⇒ 'b::banach"
  assumes r: "0 < r"
    and series: "⋀z. dist z x0 < r ⟹
              ((λα. ra_monomial (z - x0) α *R c α) has_sum F z) ra_idx"
    and y: "dist y x0 < r"
  shows "(λα. ra_Dmonomial (y - x0) α w *R c α) summable_on ra_idx"
proof -
  obtain δ0 q K where δ0: "0 < δ0" and q0: "0 ≤ q" and q1: "q < 1" and Knn: "0 ≤ K"
    and majsumm: "(λα. K * real (ra_deg α + 1) * q ^ ra_deg α) summable_on ra_idx"
    and inball: "⋀z. dist z y < δ0 ⟹ dist z x0 < r"
    and tbound: "⋀z α v. dist z y < δ0 ⟹ α ∈ ra_idx ⟹
          norm (ra_Dmonomial (z - x0) α v *R c α)
            ≤ norm v * (K * real (ra_deg α + 1) * q ^ ra_deg α)"
    using diff_majorant_interior[OF r series y] by blast
  have majw0: "(λα::'a⇒nat. (norm w * K) * (real (ra_deg α + 1) * q ^ ra_deg α))
                 summable_on ra_idx"
    by (rule summable_on_cmult_right[OF deg_pow_summable[OF q0 q1]])
  have majw: "(λα::'a⇒nat. norm w * (K * real (ra_deg α + 1) * q ^ ra_deg α))
                summable_on ra_idx"
    using majw0 by (simp add: mult.assoc)
  show ?thesis
  proof (rule abs_summable_summable,
         rule summable_on_comparison_test[OF majw])
    fix α :: "'a ⇒ nat" assume a: "α ∈ ra_idx"
    have dyy: "dist y y < δ0" using δ0 by simp
    show "norm (ra_Dmonomial (y - x0) α w *R c α)
            ≤ norm w * (K * real (ra_deg α + 1) * q ^ ra_deg α)"
      using tbound[OF dyy a] by simp
  next
    fix α :: "'a ⇒ nat" assume a: "α ∈ ra_idx"
    show "0 ≤ norm (ra_Dmonomial (y - x0) α w *R c α)" by simp
  qed
qed

text ‹The @{const ra_dterm} in the direction ‹b ∈ Basis› is a directional derivative term, hence
  summable.›

lemma ra_dterm_eq_Dmonomial_basis:
  fixes x :: "'a::euclidean_space"
    and c :: "('a ⇒ nat) ⇒ 'b::real_normed_vector"
  assumes b: "b ∈ Basis"
  shows "ra_dterm x b c b α = ra_Dmonomial x α b *R c α"
proof -
  have "ra_Dmonomial x α b
        = (∑b'∈Basis. real (α b') * (b ∙ b') *
              (x ∙ b') ^ (α b' - 1) * (∏d∈Basis - {b'}. (x ∙ d) ^ (α d)))"
    by (simp add: ra_Dmonomial_def)
  also have "… = real (α b) * (b ∙ b) *
              (x ∙ b) ^ (α b - 1) * (∏d∈Basis - {b}. (x ∙ d) ^ (α d))"
  proof (rule sum.remove[OF finite_Basis b, THEN trans])
    have "(∑b'∈Basis - {b}. real (α b') * (b ∙ b') *
              (x ∙ b') ^ (α b' - 1) * (∏d∈Basis - {b'}. (x ∙ d) ^ (α d))) = 0"
      by (rule sum.neutral) (auto simp: inner_Basis b)
    thus "real (α b) * (b ∙ b) * (x ∙ b) ^ (α b - 1) *
            (∏d∈Basis - {b}. (x ∙ d) ^ (α d)) +
          (∑b'∈Basis - {b}. real (α b') * (b ∙ b') *
              (x ∙ b') ^ (α b' - 1) * (∏d∈Basis - {b'}. (x ∙ d) ^ (α d)))
          = real (α b) * (b ∙ b) *
              (x ∙ b) ^ (α b - 1) * (∏d∈Basis - {b}. (x ∙ d) ^ (α d))"
      by simp
  qed
  finally have eqD: "ra_Dmonomial x α b
        = real (α b) * (b ∙ b) * (x ∙ b) ^ (α b - 1) *
              (∏d∈Basis - {b}. (x ∙ d) ^ (α d))" .
  have "ra_dterm x b c b α
        = (real (α b) * (b ∙ b) * (x ∙ b) ^ (α b - 1) *
              (∏d∈Basis - {b}. (x ∙ d) ^ (α d))) *R c α"
    by (simp add: ra_dterm_def)
  also have "… = ra_Dmonomial x α b *R c α" by (simp only: eqD)
  finally show ?thesis .
qed


lemma ra_directional_derivative_series:
  fixes f :: "'a::euclidean_space ⇒ 'b::banach"
  assumes r: "0 < r"
    and series:
      "⋀y. dist y x0 < r ⟹
        ((λα. ra_monomial (y - x0) α *R c α)
          has_sum f y) ra_idx"
    and y: "dist y x0 < r"
  shows
    "((λα. ra_monomial (y - x0) α *R
          ra_dcoeff c v α)
       has_sum frechet_derivative f (at y) v)
      ra_idx"
proof -
  ― ‹(i) the Fréchet derivative is the differentiated infsum, at @{term y}›
  have fd: "frechet_derivative f (at y) v
            = infsum (λα. ra_Dmonomial (y - x0) α v *R c α) ra_idx"
    by (rule ra_power_series_frechet_derivative[OF r series y])
  ― ‹(iii) the per-basis-direction summability hypothesis for the reindexing identity›
  have S6: "(λα. ra_monomial (y - x0) α *R
               (real (Suc (α b)) *R c (ra_inc α b))) summable_on ra_idx"
    if b: "b ∈ Basis" for b
  proof -
    ― ‹the differentiated family in direction @{term b} is summable›
    have Dsum: "(λα. ra_Dmonomial (y - x0) α b *R c α) summable_on ra_idx"
      by (rule ra_Dmono_summable_interior[OF r series y])
    ― ‹that family equals @{term ‹ra_dterm (y - x0) b c b›}›
    have Pid: "ra_dterm (y - x0) b c b = (λα. ra_Dmonomial (y - x0) α b *R c α)"
      by (rule ext) (rule ra_dterm_eq_Dmonomial_basis[OF b])
    have Psum: "ra_dterm (y - x0) b c b summable_on ra_idx"
      by (simp only: Pid Dsum)
    ― ‹hence @{term ‹ra_dterm_shift (y - x0) b c b›} is summable›
    have Qsum: "ra_dterm_shift (y - x0) b c b summable_on ra_idx"
      using Psum summable_dterm_iff_dterm_shift[OF b] by blast
    ― ‹and @{term ‹ra_dterm_shift (y - x0) b c b›} is the desired family (since @{term ‹b ∙ b = 1›})›
    have Qeq: "ra_dterm_shift (y - x0) b c b
               = (λα. ra_monomial (y - x0) α *R
                       (real (Suc (α b)) *R c (ra_inc α b)))"
      by (rule ext) (simp add: ra_dterm_shift_def inner_Basis b)
    show ?thesis using Qsum by (simp only: Qeq)
  qed
  ― ‹(iv) rewrite the infsum via the reindexing identity›
  have reindex: "infsum (λα. ra_Dmonomial (y - x0) α v *R c α) ra_idx
                 = infsum (λα. ra_monomial (y - x0) α *R ra_dcoeff c v α) ra_idx"
    by (rule ra_derivative_reindex[OF S6])
  ― ‹(v) the regrouped (@{const ra_dterm_shift}) family is summable›
  have Qfsum: "ra_dterm_shift (y - x0) v c b summable_on ra_idx" if b: "b ∈ Basis" for b
  proof -
    have Pid: "ra_dterm (y - x0) v c b summable_on ra_idx ⟹ ?thesis"
      using summable_dterm_iff_dterm_shift[OF b] by blast
    ― ‹@{term ‹ra_dterm (y - x0) v c b›} is summable, as a multiple of the family in direction
       @{term b}›
    have Dsum: "(λα. ra_Dmonomial (y - x0) α b *R c α) summable_on ra_idx"
      by (rule ra_Dmono_summable_interior[OF r series y])
    have Pbid: "ra_dterm (y - x0) b c b = (λα. ra_Dmonomial (y - x0) α b *R c α)"
      by (rule ext) (rule ra_dterm_eq_Dmonomial_basis[OF b])
    have Pbsum: "ra_dterm (y - x0) b c b summable_on ra_idx"
      by (simp only: Pbid Dsum)
    ― ‹relate it to @{term ‹ra_dterm (y - x0) b c b›} via the scalar @{term ‹v ∙ b›}›
    have scal: "ra_dterm (y - x0) v c b = (λα. (v ∙ b) *R ra_dterm (y - x0) b c b α)"
      by (rule ext) (simp add: ra_dterm_def inner_Basis b algebra_simps)
    have "(λα. (v ∙ b) *R ra_dterm (y - x0) b c b α) summable_on ra_idx"
      by (rule summable_on_bounded_linear[OF bounded_linear_scaleR_right Pbsum])
    then have "ra_dterm (y - x0) v c b summable_on ra_idx" by (simp only: scal)
    thus ?thesis using Pid by blast
  qed
  have Qreg: "(λα. ra_monomial (y - x0) α *R ra_dcoeff c v α)
                = (λα. ∑b∈Basis. ra_dterm_shift (y - x0) v c b α)"
    by (rule ext) (rule dcoeff_eq_sum_dterm_shift)
  have Qsumm: "(λα. ra_monomial (y - x0) α *R ra_dcoeff c v α) summable_on ra_idx"
    by (simp only: Qreg) (rule summable_on_finite_sum[OF finite_Basis Qfsum])
  ― ‹assemble: the family ‹has_sum› its own infsum, which equals the Fréchet derivative›
  have "((λα. ra_monomial (y - x0) α *R ra_dcoeff c v α)
          has_sum infsum (λα. ra_monomial (y - x0) α *R ra_dcoeff c v α) ra_idx) ra_idx"
    by (rule has_sum_infsum[OF Qsumm])
  also have "infsum (λα. ra_monomial (y - x0) α *R ra_dcoeff c v α) ra_idx
             = infsum (λα. ra_Dmonomial (y - x0) α v *R c α) ra_idx"
    by (rule reindex[symmetric])
  also have "… = frechet_derivative f (at y) v" by (rule fd[symmetric])
  finally show ?thesis .
qed


text ‹The pointwise @{const Ck_at} property, by induction on @{term k} for all power series at
  once (a directional derivative has coefficients @{term ‹ra_dcoeff cc v›}).›

lemma ra_power_series_Ck_at_aux:
  fixes x0 :: "'a::euclidean_space"
  assumes r: "0 < r"
  shows "⋀(g::'a ⇒ 'b::banach) cc x.
           (⋀z. dist z x0 < r ⟹
              ((λα. ra_monomial (z - x0) α *R cc α) has_sum g z) ra_idx)
           ⟹ x ∈ ball x0 r ⟹ Ck_at k g x"
proof (induct k)
  case (0 g cc x)
  then have x: "dist x x0 < r" by (simp add: dist_commute)
  have "g differentiable (at x)"
    by (rule ra_power_series_differentiable[OF r 0(1) x])
  then have "continuous (at x within UNIV) g"
    by (rule differentiable_imp_continuous_within)
  then have "continuous (at x) g" by simp
  thus ?case by simp
next
  case (Suc k g cc x)
  have series: "⋀z. dist z x0 < r ⟹
                  ((λα. ra_monomial (z - x0) α *R cc α) has_sum g z) ra_idx"
    by (rule Suc.prems(1))
  have xball: "x ∈ ball x0 r" by (rule Suc.prems(2))
  then have x: "dist x x0 < r" by (simp add: dist_commute)
  ― ‹(i) a neighbourhood (the ball) on which @{term ‹Ck_at k g›} holds›
  have nbhd: "open (ball x0 r) ∧ x ∈ ball x0 r ∧ (∀y∈ball x0 r. Ck_at k g y)"
  proof (intro conjI ballI)
    show "open (ball x0 r)" by simp
    show "x ∈ ball x0 r" by (rule xball)
    fix y assume yb: "y ∈ ball x0 r"
    show "Ck_at k g y" by (rule Suc.hyps[OF series yb])
  qed
  ― ‹(ii) @{term g} is differentiable at @{term x}›
  have diff: "g differentiable (at x)"
    by (rule ra_power_series_differentiable[OF r series x])
  ― ‹(iii) each directional-derivative field is again a @{term ‹Ck_at k›} power series›
  have dirCk: "Ck_at k (λy. frechet_derivative g (at y) v) x" for v
  proof -
    have dseries: "⋀z. dist z x0 < r ⟹
              ((λα. ra_monomial (z - x0) α *R ra_dcoeff cc v α)
                 has_sum frechet_derivative g (at z) v) ra_idx"
      by (rule ra_directional_derivative_series[OF r series])
    show "Ck_at k (λy. frechet_derivative g (at y) v) x"
      by (rule Suc.hyps[OF dseries xball])
  qed
  show ?case
    by (simp only: Ck_at.simps(2)) (intro conjI exI[where x = "ball x0 r"] nbhd diff allI dirCk)
qed


lemma ra_power_series_Ck_on:
  fixes f :: "'a::euclidean_space ⇒ 'b::banach"
  assumes r: "0 < r"
    and series:
      "⋀y. dist y x0 < r ⟹
        ((λα. ra_monomial (y - x0) α *R c α)
          has_sum f y) ra_idx"
  shows "Ck_on k f (ball x0 r)"
  unfolding Ck_on_def
proof (intro conjI ballI)
  show "open (ball x0 r)" by simp
  fix x assume xb: "x ∈ ball x0 r"
  show "Ck_at k f x"
    by (rule ra_power_series_Ck_at_aux[OF r series xb])
qed


lemma ra_power_series_Ck_at:
  fixes f :: "'a::euclidean_space ⇒ 'b::banach"
  assumes r: "0 < r"
    and series:
      "⋀y. dist y x0 < r ⟹
        ((λα. ra_monomial (y - x0) α *R c α)
          has_sum f y) ra_idx"
  shows "Ck_at k f x0"
proof -
  have C: "Ck_on k f (ball x0 r)"
    by (rule ra_power_series_Ck_on[OF r series])
  show ?thesis
    using C r
    by (simp add: Ck_on_def)
qed

subsection ‹Analytic implies infinitely differentiable›

theorem real_analytic_imp_Cinfinity:
  fixes f :: "'a::euclidean_space ⇒ 'b::banach"
  assumes A: "real_analytic_on f U"
  shows "Cinfinity_on f U"
proof -
  have openU: "open U"
    using A unfolding real_analytic_on_def by blast
  show ?thesis
    unfolding Cinfinity_on_def Cinfinity_at_def
  proof (intro conjI ballI allI)
    show "open U" by (rule openU)
  next
    fix x k
    assume xU: "x ∈ U"
    from A xU obtain r c where
      r: "0 < r"
      and series:
        "⋀y. dist y x < r ⟹
          ((λα. ra_monomial (y - x) α *R c α)
            has_sum f y) ra_idx"
      unfolding real_analytic_on_def by blast
    show "Ck_at k f x"
      by (rule ra_power_series_Ck_at[OF r series])
  qed
qed

text ‹The converse fails: the flat function ‹exp_bump› is a counterexample.›


lemma real_analytic_on_open_subset:
  assumes F: "real_analytic_on f U"
    and V: "open V"
    and sub: "V ⊆ U"
  shows "real_analytic_on f V"
  unfolding real_analytic_on_def
proof (intro conjI ballI)
  show "open V" by (rule V)
next
  fix x assume xV: "x ∈ V"
  hence xU: "x ∈ U"
    using sub by blast
  from F xU show "∃r>0. ∃c. ∀y. dist y x < r ⟶
      ((λα. ra_monomial (y - x) α *R c α) has_sum f y) ra_idx"
    unfolding real_analytic_on_def by blast
qed


subsection ‹Closure properties›

lemma real_analytic_on_const:
  fixes k :: "'b::real_normed_vector"
  shows "open U ⟹ real_analytic_on ((λ_. k) :: 'a::euclidean_space ⇒ 'b) U"
proof -
  assume U: "open U"
  define a0 :: "'a ⇒ nat" where "a0 = (λ_. 0)"
  have a0_idx: "a0 ∈ ra_idx" by (simp add: ra_idx_def a0_def)
  define c :: "('a ⇒ nat) ⇒ 'b" where "c = (λα. if α = a0 then k else 0)"
  have hs: "((λα. ra_monomial h α *R c α) has_sum k) ra_idx" for h :: 'a
  proof -
    have sing: "((λα. ra_monomial h α *R c α) has_sum
                   (∑α∈{a0}. ra_monomial h α *R c α)) {a0}"
      by (rule has_sum_finite) auto
    have val: "(∑α∈{a0}. ra_monomial h α *R c α) = k"
      by (simp add: c_def ra_monomial_def a0_def)
    have "((λα. ra_monomial h α *R c α) has_sum k) ra_idx
            = ((λα. ra_monomial h α *R c α) has_sum k) {a0}"
      by (rule has_sum_cong_neutral) (auto simp: c_def a0_idx)
    thus ?thesis using sing val by simp
  qed
  show ?thesis
    unfolding real_analytic_on_def
  proof (intro conjI ballI)
    show "open U" by (rule U)
  next
    fix x0 :: 'a assume "x0 ∈ U"
    show "∃r>0. ∃c. ∀x. dist x x0 < r ⟶
            ((λα. ra_monomial (x - x0) α *R c α) has_sum (λ_. k) x) ra_idx"
      by (intro exI[where x=1] conjI exI[where x=c] allI impI; simp only: hs)
  qed
qed

lemma real_analytic_on_add:
  assumes F: "real_analytic_on f U" and G: "real_analytic_on g U"
  shows "real_analytic_on (λx. f x + g x) U"
proof -
  from F have U: "open U" by (simp only: real_analytic_on_def)
  show ?thesis
    unfolding real_analytic_on_def
  proof (intro conjI ballI)
    show "open U" by (rule U)
  next
    fix x0 assume x0: "x0 ∈ U"
    from F x0 have "∃r>0. ∃c. ∀x. dist x x0 < r ⟶
        ((λα. ra_monomial (x - x0) α *R c α) has_sum f x) ra_idx"
      by (simp only: real_analytic_on_def)
    then obtain r1 c1 where r1: "0 < r1"
      and F1: "⋀x. dist x x0 < r1 ⟹
                ((λα. ra_monomial (x - x0) α *R c1 α) has_sum f x) ra_idx"
      by blast
    from G x0 have "∃r>0. ∃c. ∀x. dist x x0 < r ⟶
        ((λα. ra_monomial (x - x0) α *R c α) has_sum g x) ra_idx"
      by (simp only: real_analytic_on_def)
    then obtain r2 c2 where r2: "0 < r2"
      and G1: "⋀x. dist x x0 < r2 ⟹
                ((λα. ra_monomial (x - x0) α *R c2 α) has_sum g x) ra_idx"
      by blast
    show "∃r>0. ∃c. ∀x. dist x x0 < r ⟶
            ((λα. ra_monomial (x - x0) α *R c α) has_sum (f x + g x)) ra_idx"
    proof (intro exI[where x="min r1 r2"] conjI exI[where x="λα. c1 α + c2 α"] allI impI)
      show "0 < min r1 r2" using r1 r2 by simp
    next
      fix x assume d: "dist x x0 < min r1 r2"
      have "((λα. ra_monomial (x - x0) α *R c1 α
                  + ra_monomial (x - x0) α *R c2 α) has_sum (f x + g x)) ra_idx"
        by (rule has_sum_add) (use d F1 G1 in auto)
      thus "((λα. ra_monomial (x - x0) α *R (c1 α + c2 α)) has_sum (f x + g x)) ra_idx"
        by (simp only: scaleR_add_right)
    qed
  qed
qed

lemma real_analytic_on_scaleR:
  assumes F: "real_analytic_on f U"
  shows "real_analytic_on (λx. a *R f x) U"
proof -
  from F have U: "open U" by (simp only: real_analytic_on_def)
  show ?thesis
    unfolding real_analytic_on_def
  proof (intro conjI ballI)
    show "open U" by (rule U)
  next
    fix x0 assume x0: "x0 ∈ U"
    from F x0 have "∃r>0. ∃c. ∀x. dist x x0 < r ⟶
        ((λα. ra_monomial (x - x0) α *R c α) has_sum f x) ra_idx"
      by (simp only: real_analytic_on_def)
    then obtain r c where r: "0 < r"
      and F1: "⋀x. dist x x0 < r ⟹
                ((λα. ra_monomial (x - x0) α *R c α) has_sum f x) ra_idx"
      by blast
    show "∃r>0. ∃c. ∀x. dist x x0 < r ⟶
            ((λα. ra_monomial (x - x0) α *R c α) has_sum (a *R f x)) ra_idx"
    proof (intro exI[where x=r] conjI exI[where x="λα. a *R c α"] allI impI)
      show "0 < r" by (rule r)
    next
      fix x assume d: "dist x x0 < r"
      have "((λα. a *R (ra_monomial (x - x0) α *R c α)) has_sum (a *R f x)) ra_idx"
        using F1 bounded_linear_scaleR_right d has_sum_bounded_linear by blast
      thus "((λα. ra_monomial (x - x0) α *R (a *R c α)) has_sum (a *R f x)) ra_idx"
        by (simp only: scaleR_left_commute mult.commute)
    qed
  qed
qed

lemma real_analytic_on_mult:
  fixes f g :: "'a::euclidean_space ⇒ real"
  shows "real_analytic_on f U ⟹ real_analytic_on g U ⟹ real_analytic_on (λx. f x * g x) U"
proof -
  assume F: "real_analytic_on f U" and G: "real_analytic_on g U"
  from F have U: "open U" by (simp only: real_analytic_on_def)

  ― ‹pointwise sum and difference of multi-indices, and their order›
  define ra_idx_add :: "('a⇒nat) ⇒ ('a⇒nat) ⇒ ('a⇒nat)" where
    "ra_idx_add = (λα β b. α b + β b)"
  define ra_idx_diff :: "('a⇒nat) ⇒ ('a⇒nat) ⇒ ('a⇒nat)" where
    "ra_idx_diff = (λγ α b. γ b - α b)"
  define ra_idx_le :: "('a⇒nat) ⇒ ('a⇒nat) ⇒ bool" where
    "ra_idx_le = (λα γ. ∀b. α b ≤ γ b)"

  ― ‹(A) ‹ra_idx› is closed under ‹ra_idx_add› and ‹ra_idx_diff›.›
  have idx_add: "ra_idx_add α β ∈ ra_idx" if "α ∈ ra_idx" "β ∈ ra_idx" for α β
  proof -
    have "{b. ra_idx_add α β b ≠ 0} ⊆ {b. α b ≠ 0} ∪ {b. β b ≠ 0}"
      by (auto simp: ra_idx_add_def)
    also have "… ⊆ Basis" using that by (auto simp: ra_idx_def)
    finally show ?thesis by (simp add: ra_idx_def)
  qed
  have idx_sub: "ra_idx_diff γ α ∈ ra_idx" if "γ ∈ ra_idx" for γ α
  proof -
    have "{b. ra_idx_diff γ α b ≠ 0} ⊆ {b. γ b ≠ 0}" by (auto simp: ra_idx_diff_def)
    also have "… ⊆ Basis" using that by (auto simp: ra_idx_def)
    finally show ?thesis by (simp add: ra_idx_def)
  qed

  ― ‹(B) the basis monomial turns ‹ra_idx_add› into multiplication.›
  have mono_add: "ra_monomial h (ra_idx_add α β) = ra_monomial h α * ra_monomial h β"
    for h :: 'a and α β
    by (simp only: ra_monomial_def ra_idx_add_def power_add prod.distrib)

  ― ‹(C) finiteness of the lower set inside ‹ra_idx›.›
  have idx_lower_fin: "finite {α. α ∈ ra_idx ∧ ra_idx_le α γ}" for γ :: "'a ⇒ nat"
  proof -
    define N where "N = Max (insert 0 (γ ` (Basis :: 'a set)))"
    have "{α. α ∈ ra_idx ∧ ra_idx_le α γ}
            ⊆ {hh. ∀x::'a. (x ∈ Basis ⟶ hh x ∈ {0..N}) ∧ (x ∉ Basis ⟶ hh x = 0)}"
    proof (rule subsetI)
      fix α :: "'a ⇒ nat" assume "α ∈ {α. α ∈ ra_idx ∧ ra_idx_le α γ}"
      then have a1: "α ∈ ra_idx" and a2: "ra_idx_le α γ" by auto
      have "∀x::'a. (x ∈ Basis ⟶ α x ∈ {0..N}) ∧ (x ∉ Basis ⟶ α x = 0)"
      proof (intro allI conjI impI)
        fix x :: 'a assume "x ∈ Basis"
        have "α x ≤ γ x" using a2 by (simp only: ra_idx_le_def)
        also have "γ x ≤ N" unfolding N_def using ‹x ∈ Basis› by (intro Max_ge) auto
        finally show "α x ∈ {0..N}" by simp
      next
        fix x :: 'a assume "x ∉ Basis"
        with a1 show "α x = 0" by (auto simp: ra_idx_def)
      qed
      thus "α ∈ {hh. ∀x::'a. (x ∈ Basis ⟶ hh x ∈ {0..N}) ∧ (x ∉ Basis ⟶ hh x = 0)}"
        by simp
    qed
    moreover have "finite {hh::'a⇒nat. ∀x. (x ∈ Basis ⟶ hh x ∈ {0..N}) ∧ (x ∉ Basis ⟶ hh x = (0::nat))}"
      by (rule finite_set_of_finite_funs) auto
    ultimately show ?thesis by (rule finite_subset)
  qed

  ― ‹(D) the product of two real unordered sums over a common index set›
  have prod_has_sum:
    "((λ(α,β). u α * v β) has_sum (Uv * Vv)) (I × I)"
    if uHS: "(u has_sum Uv) I" and vHS: "(v has_sum Vv) I"
    for u v :: "('a ⇒ nat) ⇒ real" and Uv Vv I
  proof -
    have u_abs: "(λz. norm (u z)) summable_on I"
      using uHS has_sum_imp_summable summable_on_iff_abs_summable_on_real by blast
    have v_abs: "(λz. norm (v z)) summable_on I"
      using vHS has_sum_imp_summable summable_on_iff_abs_summable_on_real by blast
    have inner: "((λβ. u α * v β) has_sum (u α * Vv)) I" for α
      by (rule has_sum_cmult_right[OF vHS])
    have outer: "((λα. u α * Vv) has_sum (Uv * Vv)) I"
      by (rule has_sum_cmult_left[OF uHS])
    have inner_abs: "(λβ. norm (u α * v β)) summable_on I" for α
    proof -
      have "(λβ. norm (u α) * norm (v β)) summable_on I"
        using v_abs by (rule summable_on_cmult_right)
      thus ?thesis by (simp only: norm_mult flip: abs_mult)
    qed
    have tail_abs: "(λα. norm (∑∞β∈I. norm (u α * v β))) summable_on I"
    proof -
      have eq: "norm (∑∞β∈I. norm (u α * v β)) = norm (u α) * (∑∞β∈I. norm (v β))" for α
      proof -
        have nn: "(∑∞β∈I. norm (u α * v β)) = norm (u α) * (∑∞β∈I. norm (v β))"
        proof -
          have "(∑∞β∈I. norm (u α * v β)) = (∑∞β∈I. norm (u α) * norm (v β))"
            by (simp add: abs_mult)
          also have "… = norm (u α) * (∑∞β∈I. norm (v β))"
            by (rule infsum_cmult_right) (rule v_abs)
          finally show ?thesis .
        qed
        have ge: "(0::real) ≤ norm (u α) * (∑∞β∈I. norm (v β))"
          by (intro mult_nonneg_nonneg) (auto intro: infsum_nonneg)
        from nn ge show ?thesis by simp
      qed
      have "(λα. norm (u α) * (∑∞β∈I. norm (v β))) summable_on I"
        using u_abs by (rule summable_on_cmult_left)
      thus ?thesis unfolding eq .
    qed
    ― ‹the two conjuncts of @{thm [source] abs_summable_on_Sigma_iff}›
    have conj1: "∀α∈I. (λβ. norm ((λ(α,β). u α * v β) (α, β))) summable_on I"
    proof
      fix α assume "α ∈ I"
      have "(λβ. norm (u α * v β)) summable_on I" by (rule inner_abs)
      thus "(λβ. norm ((λ(α,β). u α * v β) (α, β))) summable_on I" by simp
    qed
    have conj2: "(λα. norm (∑∞β∈I. norm ((λ(α,β). u α * v β) (α, β)))) summable_on I"
    proof -
      have "(λα. norm (∑∞β∈I. norm (u α * v β))) summable_on I" by (rule tail_abs)
      thus ?thesis by simp
    qed
    have absS: "(λz. norm ((λ(α,β). u α * v β) z)) summable_on (Sigma I (λ_. I))"
      by (rule Infinite_Sum.abs_summable_on_Sigma_iff
            [where f = "λ(α,β). u α * v β" and A = I and B = "λ_. I", THEN iffD2,
             OF conjI[OF conj1 conj2]])
    have summ: "(λ(α,β). u α * v β) summable_on Sigma I (λ_. I)"
      by (rule abs_summable_summable[OF absS])
    have "((λ(α,β). u α * v β) has_sum (Uv * Vv)) (Sigma I (λ_. I))"
    proof (rule has_sum_SigmaI[where g = "λα. u α * Vv"])
      fix α assume "α ∈ I"
      have "((λβ. u α * v β) has_sum (u α * Vv)) I" by (rule inner)
      thus "((λβ. (λ(α,β). u α * v β) (α, β)) has_sum (u α * Vv)) I" by simp
    next
      show "((λα. u α * Vv) has_sum (Uv * Vv)) I" by (rule outer)
    next
      show "(λ(α,β). u α * v β) summable_on Sigma I (λ_. I)" by (rule summ)
    qed
    thus ?thesis by (simp only: Sigma_def)
  qed

  show "real_analytic_on (λx. f x * g x) U"
    unfolding real_analytic_on_def
  proof (intro conjI ballI)
    show "open U" by (rule U)
  next
    fix x0 assume x0: "x0 ∈ U"
    from F x0 obtain r1 c1 where r1: "0 < r1"
      and F1: "⋀x. dist x x0 < r1 ⟹
                ((λα. ra_monomial (x - x0) α *R c1 α) has_sum f x) ra_idx"
      unfolding real_analytic_on_def by blast
    from G x0 obtain r2 c2 where r2: "0 < r2"
      and G1: "⋀x. dist x x0 < r2 ⟹
                ((λα. ra_monomial (x - x0) α *R c2 α) has_sum g x) ra_idx"
      unfolding real_analytic_on_def by blast
    define low where "low = (λγ::'a⇒nat. {α. α ∈ ra_idx ∧ ra_idx_le α γ})"
    define cprod :: "('a ⇒ nat) ⇒ real" where
      "cprod = (λγ. ∑α∈low γ. c1 α * c2 (ra_idx_diff γ α))"
    show "∃r>0. ∃c. ∀x. dist x x0 < r ⟶
            ((λα. ra_monomial (x - x0) α *R c α) has_sum (f x * g x)) ra_idx"
    proof (intro exI[where x="min r1 r2"] conjI exI[where x=cprod] allI impI)
      show "0 < min r1 r2" using r1 r2 by simp
    next
      fix x assume dx: "dist x x0 < min r1 r2"
      have dx1: "dist x x0 < r1" using dx min.cobounded1 by (rule order_less_le_trans)
      have dx2: "dist x x0 < r2" using dx min.cobounded2 by (rule order_less_le_trans)
      define h where "h = x - x0"
      have F1': "((λα. ra_monomial h α * c1 α) has_sum f x) ra_idx"
        using F1[OF dx1] by (simp add: h_def)
      have G1': "((λβ. ra_monomial h β * c2 β) has_sum g x) ra_idx"
        using G1[OF dx2] by (simp add: h_def)
      ― ‹(1) product over ‹ra_idx × ra_idx›.›
      have step1:
        "((λ(α,β). (ra_monomial h α * c1 α) * (ra_monomial h β * c2 β))
             has_sum (f x * g x)) (ra_idx × ra_idx)"
        using prod_has_sum[OF F1' G1'] .
      ― ‹(2) reindex to the Sigma-convolution via ‹(α,β) ↦ (ra_idx_add α β, α)›.›
      have step2:
        "((λ(γ,α). ra_monomial h γ * (c1 α * c2 (ra_idx_diff γ α))) has_sum (f x * g x))
            (Sigma ra_idx low)"
      proof -
        have "((λ(α,β). (ra_monomial h α * c1 α) * (ra_monomial h β * c2 β))
                 has_sum (f x * g x)) (ra_idx × ra_idx)
              = ((λ(γ,α). ra_monomial h γ * (c1 α * c2 (ra_idx_diff γ α))) has_sum (f x * g x))
                 (Sigma ra_idx low)"
        proof (rule has_sum_reindex_bij_witness
                 [where j = "λ(α,β). (ra_idx_add α β, α)" and i = "λ(γ,α). (α, ra_idx_diff γ α)"])
          fix p :: "('a⇒nat) × ('a⇒nat)"
          assume "p ∈ ra_idx × ra_idx"
          obtain α β where p: "p = (α,β)" by (cases p)
          show "(case (case p of (α,β) ⇒ (ra_idx_add α β, α)) of (γ,α) ⇒ (α, ra_idx_diff γ α)) = p"
            by (simp add: p ra_idx_diff_def ra_idx_add_def)
        next
          fix p :: "('a⇒nat) × ('a⇒nat)"
          assume P: "p ∈ ra_idx × ra_idx"
          obtain α β where p: "p = (α,β)" by (cases p)
          have aα: "α ∈ ra_idx" and aβ: "β ∈ ra_idx" using P p by auto
          have m1: "ra_idx_add α β ∈ ra_idx" by (rule idx_add[OF aα aβ])
          have m2: "α ∈ low (ra_idx_add α β)"
            using aα by (simp add: low_def ra_idx_le_def ra_idx_add_def)
          show "(case p of (α,β) ⇒ (ra_idx_add α β, α)) ∈ Sigma ra_idx low"
            using m1 m2 by (simp add: p)
        next
          fix q :: "('a⇒nat) × ('a⇒nat)"
          assume Q: "q ∈ Sigma ra_idx low"
          obtain γ α where q: "q = (γ,α)" by (cases q)
          have gγ: "γ ∈ ra_idx" and l: "ra_idx_le α γ" using Q q by (auto simp: low_def)
          have "ra_idx_add α (ra_idx_diff γ α) = γ"
          proof (rule ext)
            fix b have "α b ≤ γ b" using l by (simp only: ra_idx_le_def)
            thus "ra_idx_add α (ra_idx_diff γ α) b = γ b" by (simp only: ra_idx_add_def ra_idx_diff_def)
          qed
          then show "(case (case q of (γ,α) ⇒ (α, ra_idx_diff γ α)) of (α,β) ⇒ (ra_idx_add α β, α)) = q"
            by (simp add: q)
        next
          fix q :: "('a⇒nat) × ('a⇒nat)"
          assume Q: "q ∈ Sigma ra_idx low"
          obtain γ α where q: "q = (γ,α)" by (cases q)
          have gγ: "γ ∈ ra_idx" and aα: "α ∈ ra_idx" using Q q by (auto simp: low_def)
          show "(case q of (γ,α) ⇒ (α, ra_idx_diff γ α)) ∈ ra_idx × ra_idx"
            by (simp add: q aα idx_sub[OF gγ])
        next
          fix p :: "('a⇒nat) × ('a⇒nat)"
          assume P: "p ∈ ra_idx × ra_idx"
          obtain α β where p: "p = (α,β)" by (cases p)
          have sub_eq: "ra_idx_diff (ra_idx_add α β) α = β" by (simp add: ra_idx_add_def ra_idx_diff_def)
          have mm: "ra_monomial h α * ra_monomial h β = ra_monomial h (ra_idx_add α β)"
            by (simp only: mono_add)
          have "ra_monomial h (ra_idx_add α β) * (c1 α * c2 (ra_idx_diff (ra_idx_add α β) α))
                  = ra_monomial h (ra_idx_add α β) * (c1 α * c2 β)" by (simp only: sub_eq)
          also have "… = (ra_monomial h α * ra_monomial h β) * (c1 α * c2 β)"
            by (simp only: mm)
          also have "… = (ra_monomial h α * c1 α) * (ra_monomial h β * c2 β)"
            by (simp only: mult.assoc mult.left_commute)
          finally show "(case (case p of (α,β) ⇒ (ra_idx_add α β, α)) of (γ,α) ⇒
                            ra_monomial h γ * (c1 α * c2 (ra_idx_diff γ α)))
                       = (case p of (α,β) ⇒ (ra_monomial h α * c1 α) * (ra_monomial h β * c2 β))"
            by (simp add: p)
        qed simp
        with step1 show ?thesis by simp
      qed
      ― ‹(3) collapse the finite inner sum.›
      have inner_fin:
        "((λα. ra_monomial h γ * (c1 α * c2 (ra_idx_diff γ α))) has_sum
            (ra_monomial h γ *R cprod γ)) (low γ)" if "γ ∈ ra_idx" for γ
      proof -
        have fin: "finite (low γ)" using idx_lower_fin[of γ] by (simp only: low_def)
        have "((λα. ra_monomial h γ * (c1 α * c2 (ra_idx_diff γ α))) has_sum
                 (∑α∈low γ. ra_monomial h γ * (c1 α * c2 (ra_idx_diff γ α)))) (low γ)"
          by (rule has_sum_finite[OF fin])
        also have "(∑α∈low γ. ra_monomial h γ * (c1 α * c2 (ra_idx_diff γ α)))
                     = ra_monomial h γ * (∑α∈low γ. c1 α * c2 (ra_idx_diff γ α))"
          by (simp only: sum_distrib_left)
        also have "(∑α∈low γ. c1 α * c2 (ra_idx_diff γ α)) = cprod γ"
          by (simp only: cprod_def)
        finally show ?thesis by simp
      qed
      ― ‹(4) Sigma to base via ‹has_sum_Sigma'›.›
      have "((λγ. ra_monomial h γ *R cprod γ) has_sum (f x * g x)) ra_idx"
      proof (rule has_sum_Sigma'
               [where f = "λ(γ,α). ra_monomial h γ * (c1 α * c2 (ra_idx_diff γ α))"
                  and A = ra_idx and B = low and a = "f x * g x"
                  and b = "λγ. ra_monomial h γ *R cprod γ"])
        show "((λ(γ,α). ra_monomial h γ * (c1 α * c2 (ra_idx_diff γ α))) has_sum (f x * g x))
                (Sigma ra_idx low)" by (rule step2)
      next
        fix γ :: "'a ⇒ nat" assume "γ ∈ ra_idx"
        then have "((λα. ra_monomial h γ * (c1 α * c2 (ra_idx_diff γ α))) has_sum
                      (ra_monomial h γ *R cprod γ)) (low γ)" by (rule inner_fin)
        thus "((λα. (λ(γ,α). ra_monomial h γ * (c1 α * c2 (ra_idx_diff γ α))) (γ, α)) has_sum
                  (ra_monomial h γ *R cprod γ)) (low γ)" by simp
      qed
      thus "((λα. ra_monomial (x - x0) α *R cprod α) has_sum (f x * g x)) ra_idx"
        by (simp only: h_def)
    qed
  qed
qed




subsection ‹Nowhere-dense zeros in one dimension›

text ‹On a connected open set, the zero set of a real-analytic function that is not identically
  zero is nowhere dense.  In one dimension, the set where all derivatives vanish is clopen;
  the multivariate case reduces to this via one-dimensional slices.›

text ‹If all derivatives vanish at a point, the function vanishes on its Taylor ball there.›

lemma analytic_all_derivs_zero_imp_zero_on_ball:
  fixes f :: "real ⇒ real"
  assumes ana: "real_analytic_at_1d f c"
    and z: "∀n. (deriv ^^ n) f c = 0"
  obtains r where "r > 0" and "⋀y. ¦y - c¦ < r ⟹ f y = 0"
proof -
  from ana obtain r where r: "r > 0"
    and sums: "⋀x. ¦x - c¦ < r ⟹
                 (λn. (deriv ^^ n) f c / fact n * (x - c) ^ n) sums f x"
    unfolding real_analytic_at_1d_def by blast
  have "f y = 0" if "¦y - c¦ < r" for y
  proof -
    have "(λn. (deriv ^^ n) f c / fact n * (y - c) ^ n) sums f y"
      by (rule sums[OF that])
    moreover have "(λn. (deriv ^^ n) f c / fact n * (y - c) ^ n) = (λn. 0)"
      using z by simp
    ultimately have "(λn. (0::real)) sums f y" by simp
    moreover have "(λn. (0::real)) sums 0" by (rule sums_zero)
    ultimately show "f y = 0" by (rule sums_unique2)
  qed
  with r show ?thesis using that by blast
qed

theorem real_analytic_1d_nowhere_dense_zeros:
  fixes f :: "real ⇒ real"
  assumes ana: "real_analytic_on f U" and conn: "connected U"
    and ex: "∃x∈U. f x ≠ 0"
  shows "interior (closure {x ∈ U. f x = 0}) = {}"
proof -
  from ana have oU: "open U"
    and at: "⋀c. c ∈ U ⟹ real_analytic_at_1d f c"
    by (auto simp: real_analytic_on_1d_iff)

  text ‹Smoothness on @{term U}: every derivative exists everywhere on @{term U}.›
  have smooth: "f n-times_differentiable_at x" if "x ∈ U" for n x
  proof -
    from at[OF that] obtain r where r: "r > 0"
      and diff: "⋀y. ¦y - x¦ < r ⟹ (∀m. f m-times_differentiable_at y)"
      unfolding real_analytic_at_1d_def by blast
    show ?thesis using diff[of x] r by simp
  qed

  define Z where "Z = {x ∈ U. ∀n. (deriv ^^ n) f x = 0}"

  text ‹$Z$ is open in $\mathbb{R}$ and contained in $U$, hence relatively open in $U$.›
  have Zsub: "Z ⊆ U" by (auto simp: Z_def)
  have Zopen: "open Z"
  proof (rule openI)
    fix x assume "x ∈ Z"
    then have xU: "x ∈ U" and xz: "∀n. (deriv ^^ n) f x = 0" by (auto simp: Z_def)
    ― ‹analytic ball where $f \equiv 0$›
    obtain r1 where r1: "r1 > 0"
      and fz: "⋀y. ¦y - x¦ < r1 ⟹ f y = 0"
      using analytic_all_derivs_zero_imp_zero_on_ball[OF at[OF xU] xz] by blast
    ― ‹ball inside $U$›
    obtain r2 where r2: "r2 > 0" and ballU: "ball x r2 ⊆ U"
      using oU xU open_contains_ball by blast
    define r where "r = min r1 r2"
    have rpos: "r > 0" using r1 r2 by (simp only: r_def)
    have ballsub: "ball x r ⊆ Z"
    proof
      fix z assume zb: "z ∈ ball x r"
      then have zd: "dist z x < r" by (simp add: dist_commute)
      have zU: "z ∈ U" using zb ballU by (auto simp: r_def dist_commute)
      ― ‹$f$ vanishes on an open neighbourhood of $z$, so all derivatives at $z$ vanish.›
      have feq: "eventually (λw. f w = (λ_. 0) w) (nhds z)"
      proof (rule eventually_nhds_in_open[THEN eventually_mono, of "ball x r" z])
        show "open (ball x r)" by simp
        show "z ∈ ball x r" using zb by simp
      next
        fix w assume "w ∈ ball x r"
        then have "¦w - x¦ < r" by (simp add: dist_real_def dist_commute)
        then have "¦w - x¦ < r1" by (simp only: r_def)
        thus "f w = (λ_. 0) w" by (simp only: fz)
      qed
      have "(deriv ^^ n) f z = 0" for n
      proof -
        have "(deriv ^^ n) f z = (deriv ^^ n) (λ_. 0) z"
          by (rule higher_deriv_cong_ev[OF feq refl])
        also have "… = 0" by (simp add: kth_deriv_const_cases)
        finally show ?thesis .
      qed
      thus "z ∈ Z" using zU by (simp add: Z_def)
    qed
    show "∃e>0. ball x e ⊆ Z" using rpos ballsub by blast
  qed
  have ZopenIn: "openin (top_of_set U) Z"
    using Zopen Zsub by (metis Int_absorb1 openin_open_Int)

  text ‹$Z$ is closed in $U$: each derivative is continuous on $U$.›
  have cont: "continuous_on U ((deriv ^^ n) f)" for n
  proof (rule continuous_at_imp_continuous_on, clarify)
    fix x assume xU: "x ∈ U"
    have "f (Suc n)-times_differentiable_at x" by (rule smooth[OF xU])
    thus "continuous (at x) ((deriv ^^ n) f)"
      by (rule k_times_differentiable_at_imp_isCont_kth_deriv[where j = n and k = n]) simp
  qed
  have Zinter: "Z = (⋂n. {x ∈ U. (deriv ^^ n) f x = 0})"
    by (auto simp: Z_def)
  have ZclosedIn: "closedin (top_of_set U) Z"
    unfolding Zinter
  proof (rule closedin_INT)
    show "(UNIV :: nat set) ≠ {}" by simp
    fix n :: nat assume "n ∈ (UNIV :: nat set)"
    show "closedin (top_of_set U) {x ∈ U. (deriv ^^ n) f x = 0}"
      by (rule continuous_closedin_preimage_constant[OF cont])
  qed

  text ‹If the interior of the closure were nonempty, $Z$ would be nonempty.›
  define W where "W = interior (closure {x ∈ U. f x = 0})"
  have Wopen: "open W" by (simp add: W_def)
  have Zne: "Z ≠ {}" if WNE: "W ≠ {}"
  proof -
    from WNE obtain w where wW: "w ∈ W" by blast
    ― ‹$W \cap U$ is open and nonempty›
    have Wsub: "W ⊆ closure {x ∈ U. f x = 0}" by (simp only: W_def interior_subset)
    ― ‹$w$ has a ball inside $W$, which meets $\{x\in U.\ f\,x=0\}\subseteq U$, so $W\cap U\neq\emptyset$›
    obtain e where epos: "e > 0" and eball: "ball w e ⊆ W"
      using wW Wopen open_contains_ball by blast
    have wcl: "w ∈ closure {x ∈ U. f x = 0}" using wW Wsub by blast
    have "ball w e ∩ {x ∈ U. f x = 0} ≠ {}"
    proof -
      have "w ∈ ball w e ∩ closure {x ∈ U. f x = 0}" using wcl epos by simp
      hence "ball w e ∩ closure {x ∈ U. f x = 0} ≠ {}" by blast
      thus "ball w e ∩ {x ∈ U. f x = 0} ≠ {}"
        using open_Int_closure_eq_empty[OF open_ball, of w e "{x ∈ U. f x = 0}"] by blast
    qed
    then obtain u where uU: "u ∈ ball w e" "u ∈ U" "f u = 0" by blast
    have uW: "u ∈ W" using uU(1) eball by blast
    ― ‹so $V = W \cap U$ is open and nonempty; on it $f \equiv 0$ by continuity›
    define V where "V = W ∩ U"
    have Vopen: "open V" unfolding V_def by (intro open_Int Wopen oU)
    have uV: "u ∈ V" using uW uU(2) by (simp add: V_def)
    have fzeroV: "f v = 0" if "v ∈ V" for v
    proof -
      have vW: "v ∈ W" and vU: "v ∈ U" using that by (auto simp: V_def)
      have vcl: "v ∈ closure {x ∈ U. f x = 0}" using vW Wsub by blast
      ― ‹$f$ is continuous at $v$ (a point of the open set $U$)›
      have contv: "continuous (at v) f"
      proof -
        have "f (Suc 0)-times_differentiable_at v" by (rule smooth[OF vU])
        thus ?thesis by (rule k_times_differentiable_at_imp_isCont)
      qed
      ― ‹limit of zeros through the closure›
      have "isCont f v" using contv by simp
      from vcl obtain s where s: "⋀k. s k ∈ {x ∈ U. f x = 0}" "s ⇢ v"
        using closure_sequential by blast
      have "(λk. f (s k)) ⇢ f v"
        using ‹isCont f v› s(2) by (simp only: continuous_within isCont_tendsto_compose)
      moreover have "(λk. f (s k)) = (λk. 0)" using s(1) by auto
      ultimately have "(λk. (0::real)) ⇢ f v" by simp
      thus "f v = 0" by (simp only: LIMSEQ_const_iff)
    qed
    ― ‹$f \equiv 0$ on the open ball around $u$ inside $V$; all derivatives at $u$ vanish›
    obtain ρ where ρ: "ρ > 0" and rball: "ball u ρ ⊆ V"
      using uV Vopen open_contains_ball by blast
    have feq: "eventually (λw. f w = (λ_. 0) w) (nhds u)"
    proof (rule eventually_nhds_in_open[THEN eventually_mono, of "ball u ρ" u])
      show "open (ball u ρ)" by simp
      show "u ∈ ball u ρ" using ρ by simp
    next
      fix w assume "w ∈ ball u ρ"
      then have "w ∈ V" using rball by blast
      thus "f w = (λ_. 0) w" by (simp only: fzeroV)
    qed
    have "(deriv ^^ n) f u = 0" for n
    proof -
      have "(deriv ^^ n) f u = (deriv ^^ n) (λ_. 0) u"
        by (rule higher_deriv_cong_ev[OF feq refl])
      also have "… = 0" by (simp add: kth_deriv_const_cases)
      finally show ?thesis .
    qed
    then have "u ∈ Z" using uU(2) by (simp add: Z_def)
    thus "Z ≠ {}" by blast
  qed

  text ‹Connectedness: $Z$ is clopen in $U$, so $Z = \emptyset$ or $Z = U$.›
  have clopen: "Z = {} ∨ Z = U"
    using conn ZopenIn ZclosedIn unfolding connected_clopen by blast

  text ‹If $W \neq \emptyset$ then $Z \neq \emptyset$, hence $Z = U$, forcing $f \equiv 0$ on $U$,
    contradicting the assumption.›
  show ?thesis
  proof (rule ccontr)
    assume "interior (closure {x ∈ U. f x = 0}) ≠ {}"
    then have WNE: "W ≠ {}" by (simp add: W_def)
    then have "Z ≠ {}" by (rule Zne)
    with clopen have "Z = U" by blast
    have "f x = 0" if "x ∈ U" for x
    proof -
      have "x ∈ Z" using that ‹Z = U› by simp
      then have "∀n. (deriv ^^ n) f x = 0" by (simp add: Z_def)
      then have "(deriv ^^ 0) f x = 0" by blast
      thus "f x = 0" by simp
    qed
    with ex show False by blast
  qed
qed


subsection ‹Convergent power series in one variable›

text ‹A convergent real power series about ‹c› makes ‹f› real-analytic at ‹c›.›

lemma real_powser_imp_real_analytic_at_1d:
  fixes f :: "real ⇒ real"
  assumes r: "0 < r"
    and PS: "⋀x. ¦x - c¦ < r ⟹ (λn. a n * (x - c) ^ n) sums f x"
  shows "real_analytic_at_1d f c"
proof -
  have sums_a: "(λn. a n * z ^ n) sums f (c + z)" if "¦z¦ < r" for z
  proof -
    have "¦(c + z) - c¦ < r" using that by simp
    from PS[OF this] show ?thesis by simp
  qed
  have summ_a: "summable (λn. a n * z ^ n)" if "¦z¦ < r" for z
    using sums_a[OF that] by (rule sums_summable)
  have summ_diffs: "summable (λm. (diffs ^^ n) a m * z ^ m)" if "¦z¦ < r" for n z
    using that
  proof (induction n arbitrary: z)
    case 0
    thus ?case using summ_a by simp
  next
    case (Suc n)
    have "summable (λm. diffs ((diffs ^^ n) a) m * z ^ m)"
    proof (rule termdiff_converges[where K = r])
      show "norm z < r" using Suc.prems by simp
      fix w :: real assume "norm w < r"
      hence "¦w¦ < r" by simp
      thus "summable (λm. (diffs ^^ n) a m * w ^ m)" by (rule Suc.IH)
    qed
    thus ?case by simp
  qed
  define S where "S = (λn y. ∑m. (diffs ^^ n) a m * (y - c) ^ m)"
  have S0_eq_f: "S 0 x = f x" if "¦x - c¦ < r" for x
  proof -
    from sums_a[of "x - c"] that have "(λn. a n * (x - c) ^ n) sums f x" by simp
    thus ?thesis by (simp add: S_def sums_iff)
  qed
  have S_deriv: "(S n has_field_derivative S (Suc n) x) (at x)"
    if "¦x - c¦ < r" for n x
  proof -
    have H: "((λw. ∑m. (diffs ^^ n) a m * w ^ m)
               has_field_derivative (∑m. diffs ((diffs ^^ n) a) m * (x - c) ^ m))
              (at (x - c))"
    proof (rule termdiffs_strong'[where K = r])
      fix w :: real assume "norm w < r"
      thus "summable (λm. (diffs ^^ n) a m * w ^ m)" using summ_diffs by simp
    next
      show "norm (x - c) < r" using that by simp
    qed
    have shift: "((λy. y - c) has_field_derivative 1) (at x)"
      by (auto intro!: derivative_eq_intros)
    have "((λy. (λw. ∑m. (diffs ^^ n) a m * w ^ m) (y - c))
             has_field_derivative
             (∑m. diffs ((diffs ^^ n) a) m * (x - c) ^ m) * 1) (at x)"
      by (rule DERIV_chain'[OF shift]) (use H in simp)
    thus ?thesis by (simp add: S_def)
  qed
  have main: "∀x. ¦x - c¦ < r ⟶
                f n-times_differentiable_at x ∧ (deriv ^^ n) f x = S n x" for n
  proof (induction n)
    case 0
    show ?case by (auto simp: S0_eq_f)
  next
    case (Suc n)
    show ?case
    proof (intro allI impI conjI)
      fix x assume xc: "¦x - c¦ < r"
      have ballopen: "{y. ¦y - c¦ < r} = ball c r"
        by (auto simp: dist_real_def abs_minus_commute)
      have eqA: "(deriv ^^ n) f y = S n y" if "¦y - c¦ < r" for y
        using Suc.IH that by blast
      have dn_deriv: "((deriv ^^ n) f has_field_derivative S (Suc n) x) (at x)"
      proof (rule has_field_derivative_transform_within_open
                   [where f = "S n" and S = "{y. ¦y - c¦ < r}"])
        show "(S n has_field_derivative S (Suc n) x) (at x)" by (rule S_deriv[OF xc])
        show "open {y. ¦y - c¦ < r}" by (simp add: ballopen)
        show "x ∈ {y. ¦y - c¦ < r}" using xc by simp
        show "⋀y. y ∈ {y. ¦y - c¦ < r} ⟹ S n y = (deriv ^^ n) f y"
          using eqA by auto
      qed
      show "(deriv ^^ Suc n) f x = S (Suc n) x"
        using dn_deriv by (simp only: kth_deriv_simps(2) DERIV_imp_deriv)
      show "f (Suc n)-times_differentiable_at x"
        unfolding k_times_differentiable_at.simps(2)
      proof
        show "∃ε>0. ∀y. ¦y - x¦ < ε ⟶ f n-times_differentiable_at y"
        proof (intro exI[where x = "r - ¦x - c¦"] conjI allI impI)
          show "0 < r - ¦x - c¦" using xc by simp
          fix y assume "¦y - x¦ < r - ¦x - c¦"
          hence "¦y - c¦ < r" by linarith
          thus "f n-times_differentiable_at y" using Suc.IH by blast
        qed
      next
        have "(deriv ^^ Suc n) f x = S (Suc n) x"
          using dn_deriv by (simp only: kth_deriv_simps(2) DERIV_imp_deriv)
        with dn_deriv
        show "((deriv ^^ n) f has_derivative (λh. (deriv ^^ Suc n) f x * h)) (at x)"
          by (simp only: has_field_derivative_def)
      qed
    qed
  qed
  have diffs_fact: "(diffs ^^ n) g 0 = fact n * g n" for n and g :: "nat ⇒ real"
  proof -
    have gen: "fact m * (diffs ^^ n) g m = fact (m + n) * g (m + n)" for m
    proof (induction n arbitrary: g m)
      case 0 show ?case by simp
    next
      case (Suc n)
      have "fact m * (diffs ^^ Suc n) g m = fact m * (diffs ^^ n) (diffs g) m"
        by (simp only: funpow_Suc_right o_apply)
      also have "… = fact (m + n) * (diffs g) (m + n)"
        using Suc.IH[of m "diffs g"] by simp
      also have "… = fact (m + n) * (of_nat (Suc (m + n)) * g (Suc (m + n)))"
        by (simp only: diffs_def)
      also have "… = fact (Suc (m + n)) * g (Suc (m + n))"
        by (simp add: algebra_simps)
      finally show ?case by (simp add: add.commute)
    qed
    from gen[of 0] show ?thesis by simp
  qed
  have coeff: "a n = (deriv ^^ n) f c / fact n" for n
  proof -
    have "¦c - c¦ < r" using r by simp
    with main[of n] have "(deriv ^^ n) f c = S n c" by blast
    also have "S n c = (diffs ^^ n) a 0" by (simp add: S_def)
    also have "… = fact n * a n" by (rule diffs_fact)
    finally have "(deriv ^^ n) f c = fact n * a n" .
    thus ?thesis by simp
  qed
  have smooth: "f n-times_differentiable_at x" if "¦x - c¦ < r" for x n
    using main[of n] that by blast
  show ?thesis
    unfolding real_analytic_at_1d_def
  proof (intro exI[where x = r] conjI allI impI)
    show "0 < r" by (rule r)
  next
    fix x n assume "¦x - c¦ < r" thus "f n-times_differentiable_at x"
      by (rule smooth)
  next
    fix x assume xc: "¦x - c¦ < r"
    from PS[OF xc] show
      "(λn. (deriv ^^ n) f c / fact n * (x - c) ^ n) sums f x"
      by (simp only: coeff)
  qed
qed


subsection ‹Degree of a multi-index and the monomial scaling identity›

text ‹Scaling a vector by a real ‹t› scales the basis monomial by ‹t^(deg α)›.›

lemma ra_monomial_scaleR:
  fixes d :: "'a::euclidean_space"
  shows "ra_monomial (t *R d) α = t ^ (ra_deg α) * ra_monomial d α"
proof -
  have "ra_monomial (t *R d) α = (∏b∈Basis. ((t *R d) ∙ b) ^ (α b))"
    by (simp only: ra_monomial_def)
  also have "… = (∏b∈Basis. (t * (d ∙ b)) ^ (α b))"
    by (simp only: inner_scaleR_left)
  also have "… = (∏b∈Basis. t ^ (α b) * (d ∙ b) ^ (α b))"
    by (simp only: power_mult_distrib)
  also have "… = (∏b∈Basis. t ^ (α b)) * (∏b∈Basis. (d ∙ b) ^ (α b))"
    by (simp only: prod.distrib)
  also have "(∏b∈Basis. t ^ (α b)) = t ^ (∑b∈Basis. α b)"
    by (simp only: power_sum)
  finally show ?thesis by (simp only: ra_deg_def ra_monomial_def)
qed

text ‹Each degree block of ‹ra_idx› is finite.›

lemma ra_deg_block_finite:
  fixes n :: nat
  shows "finite {α::'a::euclidean_space ⇒ nat. α ∈ ra_idx ∧ ra_deg α = n}"
proof -
  have "{α::'a ⇒ nat. α ∈ ra_idx ∧ ra_deg α = n}
          ⊆ {hh. ∀x::'a. (x ∈ Basis ⟶ hh x ∈ {0..n}) ∧ (x ∉ Basis ⟶ hh x = 0)}"
  proof (rule subsetI)
    fix α :: "'a ⇒ nat" assume "α ∈ {α. α ∈ ra_idx ∧ ra_deg α = n}"
    then have a1: "α ∈ ra_idx" and a2: "ra_deg α = n" by auto
    have "∀x::'a. (x ∈ Basis ⟶ α x ∈ {0..n}) ∧ (x ∉ Basis ⟶ α x = 0)"
    proof (intro allI conjI impI)
      fix x :: 'a assume xB: "x ∈ Basis"
      have "α x ≤ (∑b∈Basis. α b)"
        using xB by (intro member_le_sum) auto
      also have "… = n" using a2 by (simp only: ra_deg_def)
      finally show "α x ∈ {0..n}" by simp
    next
      fix x :: 'a assume "x ∉ Basis"
      with a1 show "α x = 0" by (auto simp: ra_idx_def)
    qed
    thus "α ∈ {hh. ∀x::'a. (x ∈ Basis ⟶ hh x ∈ {0..n}) ∧ (x ∉ Basis ⟶ hh x = 0)}"
      by simp
  qed
  moreover have "finite {hh::'a⇒nat. ∀x. (x ∈ Basis ⟶ hh x ∈ {0..n}) ∧ (x ∉ Basis ⟶ hh x = (0::nat))}"
    by (rule finite_set_of_finite_funs) auto
  ultimately show ?thesis by (rule finite_subset)
qed

text ‹The basis monomial is bounded in absolute value by ‹(norm h) ^ (deg α)›.›

lemma abs_ra_monomial_le:
  fixes h :: "'a::euclidean_space"
  shows "¦ra_monomial h α¦ ≤ (norm h) ^ (ra_deg α)"
proof -
  have "¦ra_monomial h α¦ = (∏b∈Basis. ¦h ∙ b¦ ^ (α b))"
    by (simp only: ra_monomial_def abs_prod power_abs)
  also have "… ≤ (∏b∈Basis. (norm h) ^ (α b))"
  proof (rule prod_mono)
    fix b :: 'a assume "b ∈ Basis"
    have "¦h ∙ b¦ ≤ norm h" using ‹b ∈ Basis› by (rule Basis_le_norm)
    thus "0 ≤ ¦h ∙ b¦ ^ (α b) ∧ ¦h ∙ b¦ ^ (α b) ≤ (norm h) ^ (α b)"
      by (auto intro: power_mono)
  qed
  also have "… = (norm h) ^ (∑b∈Basis. α b)"
    by (simp only: power_sum)
  finally show ?thesis by (simp only: ra_deg_def)
qed

text ‹The basis monomial at ‹0› is the indicator of the zero multi-index.›

lemma ra_monomial_zero:
  fixes α :: "'a::euclidean_space ⇒ nat"
  shows "ra_monomial (0::'a) α = (if ra_deg α = 0 then 1 else 0)"
proof (cases "ra_deg α = 0")
  case True
  then have "⋀b. b ∈ Basis ⟹ α b = 0"
    using finite_Basis by (simp add: ra_deg_def)
  thus ?thesis using True by (simp add: ra_monomial_def)
next
  case False
  then obtain b where bB: "b ∈ Basis" and apos: "α b ≠ 0"
    using finite_Basis by (auto simp: ra_deg_def)
  have "ra_monomial (0::'a) α = (∏b∈Basis. (0 ∙ b) ^ (α b))"
    by (simp only: ra_monomial_def)
  also have "… = 0"
    using bB apos by (intro prod_zero[OF finite_Basis]) auto
  finally show ?thesis using False by simp
qed

subsection ‹Real-analytic functions are continuous›

text ‹Continuity at ‹x0› from the local power series, via
  ‹¦f x - f x0¦ ≤ (dist x x0 / t) ⋅ S› near ‹x0›.›

lemma real_analytic_on_imp_continuous:
  fixes f :: "'a::euclidean_space ⇒ real"
  assumes ana: "real_analytic_on f U" and xU: "x0 ∈ U"
  shows "continuous (at x0) f"
proof -
  from ana xU obtain r c where r: "0 < r"
    and HS: "⋀x. dist x x0 < r ⟹
              ((λα. ra_monomial (x - x0) α *R c α) has_sum f x) ra_idx"
    unfolding real_analytic_on_def by blast
  define e1 where "e1 = (∑b∈(Basis::'a set). b)"
  define t where "t = r / (2 * (norm e1 + 1))"
  have ne1: "norm e1 + 1 > 0" by (simp add: add_nonneg_pos)
  have t_pos: "0 < t" using r ne1 by (simp add: t_def)
  define x2 where "x2 = x0 + t *R e1"
  have dist_x2: "dist x2 x0 < r"
  proof -
    have "dist x2 x0 = norm (t *R e1)" by (simp add: x2_def dist_norm)
    also have "… = t * norm e1" using t_pos by simp
    also have "… ≤ t * (norm e1 + 1)" using t_pos by simp
    also have "t * (norm e1 + 1) = r / 2"
      using ne1 by (simp add: t_def field_simps)
    also have "r / 2 < r" using r by simp
    finally show ?thesis .
  qed
  ― ‹at ‹x2› the monomial is exactly ‹t^(deg α)››
  have mono_x2: "ra_monomial (x2 - x0) α = t ^ (ra_deg α)" for α
  proof -
    have "x2 - x0 = t *R e1" by (simp add: x2_def)
    have inb: "(x2 - x0) ∙ b = t" if "b ∈ Basis" for b
    proof -
      have "(x2 - x0) ∙ b = (t *R e1) ∙ b" by (simp add: x2_def)
      also have "… = t * (e1 ∙ b)" by (simp only: inner_scaleR_left)
      also have "e1 ∙ b = 1" using that by (simp add: e1_def inner_sum_left inner_Basis)
      finally show ?thesis by simp
    qed
    have "ra_monomial (x2 - x0) α = (∏b∈Basis. ((x2 - x0) ∙ b) ^ (α b))"
      by (simp only: ra_monomial_def)
    also have "… = (∏b∈Basis. t ^ (α b))"
      by (intro prod.cong refl) (simp only: inb)
    also have "… = t ^ (∑b∈Basis. α b)" by (simp only: power_sum)
    finally show ?thesis by (simp only: ra_deg_def)
  qed
  ― ‹hence ‹t^(deg α) ⋅ ¦c ᦛ is summable›
  have HSx2: "((λα. t ^ (ra_deg α) * c α) has_sum f x2) ra_idx"
  proof -
    have "((λα. ra_monomial (x2 - x0) α *R c α) has_sum f x2) ra_idx" by (rule HS[OF dist_x2])
    thus ?thesis by (simp add: mono_x2)
  qed
  have abs_summ: "(λα. t ^ (ra_deg α) * ¦c α¦) summable_on ra_idx"
  proof -
    have sm: "(λα. t ^ (ra_deg α) * c α) summable_on ra_idx"
      by (rule has_sum_imp_summable[OF HSx2])
    have "(λα. norm (t ^ (ra_deg α) * c α)) summable_on ra_idx"
      using sm[THEN iffD1[OF summable_on_iff_abs_summable_on_real]] .
    moreover have "⋀α. norm (t ^ (ra_deg α) * c α) = t ^ (ra_deg α) * ¦c α¦"
      using t_pos by (simp add: abs_mult)
    ultimately show ?thesis by simp
  qed
  define S where "S = (∑∞α∈ra_idx. t ^ (ra_deg α) * ¦c α¦)"
  have HSS: "((λα. t ^ (ra_deg α) * ¦c α¦) has_sum S) ra_idx"
    unfolding S_def using abs_summ by (rule has_sum_infsum)
  ― ‹the key domination estimate around ‹x0››
  have estimate: "¦f x - f x0¦ ≤ (dist x x0 / t) * S" if dx: "dist x x0 ≤ t" for x
  proof -
    have dxr: "dist x x0 < r"
    proof -
      have "t ≤ t * (norm e1 + 1)" using t_pos by simp
      also have "t * (norm e1 + 1) = r / 2" using ne1 by (simp add: t_def field_simps)
      also have "r / 2 < r" using r by simp
      finally have "t < r" .
      thus ?thesis using dx by linarith
    qed
    have HSx: "((λα. ra_monomial (x - x0) α * c α) has_sum f x) ra_idx"
      using HS[OF dxr] by simp
    have HSx0: "((λα. ra_monomial ((0::'a)) α * c α) has_sum f x0) ra_idx"
    proof -
      have "dist x0 x0 < r" using r by simp
      from HS[OF this] show ?thesis by simp
    qed
    ― ‹the difference is a single ‹has_sum››
    have HSdiff: "((λα. (ra_monomial (x - x0) α - ra_monomial (0::'a) α) * c α)
                     has_sum (f x - f x0)) ra_idx"
    proof -
      have HSm0: "((λα. - (ra_monomial (0::'a) α * c α)) has_sum (- f x0)) ra_idx"
        by (subst has_sum_uminus, simp add: HSx0)
      have "((λα. ra_monomial (x - x0) α * c α + (- (ra_monomial (0::'a) α * c α)))
               has_sum (f x + (- f x0))) ra_idx"
        by (rule has_sum_add[OF HSx HSm0])
      thus ?thesis by (simp add: left_diff_distrib)
    qed
    ― ‹the dominating series ‹(dist x x0 / t) ⋅ t^(deg α) ⋅ ¦c ᦛ›
    have HSdom: "((λα. (dist x x0 / t) * (t ^ (ra_deg α) * ¦c α¦)) has_sum ((dist x x0 / t) * S)) ra_idx"
      by (rule has_sum_cmult_right[OF HSS])
    ― ‹term-by-term domination›
    have termbound: "¦(ra_monomial (x - x0) α - ra_monomial (0::'a) α) * c α¦
                       ≤ (dist x x0 / t) * (t ^ (ra_deg α) * ¦c α¦)" for α
    proof (cases "ra_deg α = 0")
      case True
      then have allz: "⋀b. b ∈ Basis ⟹ α b = 0"
        using finite_Basis by (simp add: ra_deg_def)
      have e1: "ra_monomial (x - x0) α = 1"
        by (simp add: ra_monomial_def allz)
      have e2: "ra_monomial (0::'a) α = 1"
        by (simp add: ra_monomial_def allz)
      have "¦(ra_monomial (x - x0) α - ra_monomial (0::'a) α) * c α¦ = 0"
        by (simp add: e1 e2)
      moreover have "0 ≤ (dist x x0 / t) * (t ^ (ra_deg α) * ¦c α¦)"
        using t_pos by (intro mult_nonneg_nonneg) auto
      ultimately show ?thesis by linarith
    next
      case False
      then have dpos: "ra_deg α ≥ 1" by simp
      have m0: "ra_monomial (0::'a) α = 0" by (simp add: ra_monomial_zero False)
      have nh: "norm (x - x0) = dist x x0" by (simp only: dist_norm)
      have "¦(ra_monomial (x - x0) α - ra_monomial (0::'a) α) * c α¦
              = ¦ra_monomial (x - x0) α¦ * ¦c α¦" by (simp add: m0 abs_mult)
      also have "¦ra_monomial (x - x0) α¦ ≤ (dist x x0) ^ (ra_deg α)"
        using abs_ra_monomial_le[of "x - x0" α] by (simp only: nh)
      also have "(dist x x0) ^ (ra_deg α) ≤ (dist x x0 / t) * t ^ (ra_deg α)"
      proof -
        obtain k where k: "ra_deg α = Suc k" using dpos by (cases "ra_deg α") auto
        have dnn: "0 ≤ dist x x0" by simp
        have "(dist x x0) ^ (Suc k) = dist x x0 * (dist x x0) ^ k" by simp
        also have "… ≤ dist x x0 * t ^ k"
          using dnn dx by (intro mult_left_mono power_mono) auto
        also have "dist x x0 * t ^ k = (dist x x0 / t) * t ^ (Suc k)"
          using t_pos by (simp add: field_simps)
        finally show ?thesis by (simp only: k)
      qed
      finally have "¦(ra_monomial (x - x0) α - ra_monomial (0::'a) α) * c α¦
                      ≤ ((dist x x0 / t) * t ^ (ra_deg α)) * ¦c α¦"
        by (simp only: mult_right_mono)
      thus ?thesis by (simp only: mult.assoc)
    qed
    have "norm (f x - f x0) ≤ (dist x x0 / t) * S"
      by (rule norm_infsum_le[OF HSdiff HSdom]) (use termbound in simp)
    thus ?thesis by simp
  qed
  ― ‹continuity by the squeeze ‹¦f x - f x0¦ ≤ (dist x x0 / t) ⋅ S → 0››
  have Snn: "0 ≤ S" unfolding S_def
  proof (rule infsum_nonneg)
    fix α :: "'a ⇒ nat" assume "α ∈ ra_idx"
    have "0 ≤ t ^ (ra_deg α)" using t_pos by simp
    thus "0 ≤ t ^ (ra_deg α) * ¦c α¦" by (simp only: mult_nonneg_nonneg)
  qed
  ― ‹the dominating function ‹x ↦ (dist x x0 / t) ⋅ S› tends to ‹0› at ‹x0››
  have domlim: "((λx. (dist x x0 / t) * S) ⤏ 0) (at x0)"
  proof -
    have idlim: "((λx::'a. x) ⤏ x0) (at x0)" by (simp only: tendsto_ident_at)
    have dl: "((λx. dist x x0) ⤏ dist x0 x0) (at x0)"
      by (intro tendsto_dist idlim tendsto_const)
    have "((λx. dist x x0) ⤏ 0) (at x0)" using dl by simp
    hence "((λx. dist x x0 * (S / t)) ⤏ 0 * (S / t)) (at x0)"
      by (rule tendsto_mult_right)
    moreover have "(λx. dist x x0 * (S / t)) = (λx. (dist x x0 / t) * S)"
      by (rule ext) simp
    ultimately show ?thesis by simp
  qed
  ― ‹the difference is eventually dominated›
  have evb: "∀F x in at x0. norm (f x - f x0) ≤ norm ((dist x x0 / t) * S)"
  proof -
    have "∀F x in at x0. x ∈ ball x0 t"
      by (rule eventually_at_in_open'[OF open_ball]) (simp add: t_pos)
    then have "∀F x in at x0. dist x x0 < t"
      by (rule eventually_mono) (simp add: dist_commute)
    thus ?thesis
    proof (rule eventually_mono)
      fix x assume "dist x x0 < t"
      hence dxt: "dist x x0 ≤ t" by simp
      have "norm (f x - f x0) = ¦f x - f x0¦" by simp
      also have "… ≤ (dist x x0 / t) * S" by (rule estimate[OF dxt])
      also have "… = norm ((dist x x0 / t) * S)"
        using t_pos Snn by simp
      finally show "norm (f x - f x0) ≤ norm ((dist x x0 / t) * S)" .
    qed
  qed
  have "((λx. f x - f x0) ⤏ 0) (at x0)"
    by (rule Lim_transform_bound[OF evb domlim])
  hence "(f ⤏ f x0) (at x0)" by (simp only: LIM_zero_iff)
  thus ?thesis by (simp only: continuous_at)
qed

subsection ‹The affine slice of a real-analytic function is real-analytic (at a point)›

text ‹The slice ‹s ↦ f (a + s *R d)› in a nonzero direction ‹d› is real-analytic at ‹t0›.›

lemma real_analytic_slice_at_point:
  fixes f :: "'a::euclidean_space ⇒ real"
  assumes ana: "real_analytic_on f U"
    and inU: "a + t0 *R d ∈ U"
    and dnz: "d ≠ 0"
  shows "real_analytic_at_1d (λs. f (a + s *R d)) t0"
proof -
  define x0 where "x0 = a + t0 *R d"
  have oU: "open U" using ana by (simp only: real_analytic_on_def)
  from ana inU obtain r c where r: "0 < r"
    and HS: "⋀x. dist x x0 < r ⟹
              ((λα. ra_monomial (x - x0) α *R c α) has_sum f x) ra_idx"
    unfolding real_analytic_on_def x0_def by blast
  define nd where "nd = norm d"
  have nd_pos: "0 < nd" using dnz by (simp add: nd_def)
  define r' where "r' = r / nd"
  have r'_pos: "0 < r'" using r nd_pos by (simp add: r'_def)
  ― ‹the slice coefficients: sum the basis-monomial coefficients over each degree block›
  define blk where "blk = (λn. {α::'a⇒nat. α ∈ ra_idx ∧ ra_deg α = n})"
  define acoef where "acoef = (λn. ∑α∈blk n. ra_monomial d α * c α)"
  have blk_fin: "finite (blk n)" for n
    unfolding blk_def by (rule ra_deg_block_finite)
  ― ‹the central power-series identity along the slice›
  have PS: "(λn. acoef n * (s - t0) ^ n) sums f (a + s *R d)" if slt: "¦s - t0¦ < r'" for s
  proof -
    define u where "u = s - t0"
    have ult: "¦u¦ < r'" using slt by (simp only: u_def)
    have pt_eq: "a + s *R d = x0 + u *R d"
      by (simp only: x0_def u_def algebra_simps)
    have dist_lt: "dist (x0 + u *R d) x0 < r"
    proof -
      have "dist (x0 + u *R d) x0 = norm (u *R d)" by (simp add: dist_norm)
      also have "… = ¦u¦ * nd" by (simp add: nd_def)
      also have "… < r' * nd" using ult nd_pos by (simp only: mult_strict_right_mono)
      also have "… = r" using nd_pos by (simp add: r'_def)
      finally show ?thesis .
    qed
    ― ‹multivariate ‹has_sum› at this point, with monomial scaled by ‹u››
    have HSu: "((λα. (u ^ (ra_deg α) * ra_monomial d α) * c α) has_sum f (x0 + u *R d)) ra_idx"
    proof -
      have "((λα. ra_monomial ((x0 + u *R d) - x0) α *R c α) has_sum f (x0 + u *R d)) ra_idx"
        by (rule HS[OF dist_lt])
      moreover have "(x0 + u *R d) - x0 = u *R d" by simp
      ultimately have "((λα. ra_monomial (u *R d) α *R c α) has_sum f (x0 + u *R d)) ra_idx"
        by simp
      thus ?thesis by (simp add: ra_monomial_scaleR)
    qed
    ― ‹reindex ‹ra_idx› as the disjoint union of degree blocks›
    have reidx: "((λq. u ^ (fst q) * (ra_monomial d (snd q) * c (snd q))) has_sum f (x0 + u *R d))
                   (Sigma (UNIV::nat set) blk)"
    proof -
      have "((λα. (u ^ (ra_deg α) * ra_monomial d α) * c α) has_sum f (x0 + u *R d)) ra_idx
              = ((λq. u ^ (fst q) * (ra_monomial d (snd q) * c (snd q))) has_sum f (x0 + u *R d))
                   (Sigma (UNIV::nat set) blk)"
      proof (rule has_sum_reindex_bij_witness
               [where j = "λα. (ra_deg α, α)" and i = snd])
        fix α :: "'a ⇒ nat" assume "α ∈ ra_idx"
        show "snd (ra_deg α, α) = α" by simp
      next
        fix α :: "'a ⇒ nat" assume "α ∈ ra_idx"
        thus "(ra_deg α, α) ∈ Sigma (UNIV::nat set) blk" by (simp add: blk_def)
      next
        fix q :: "nat × ('a ⇒ nat)" assume "q ∈ Sigma (UNIV::nat set) blk"
        then obtain m β where q: "q = (m, β)" and "β ∈ blk m" by (cases q) auto
        then have dn: "ra_deg β = m" by (simp add: blk_def)
        show "(ra_deg (snd q), snd q) = q" by (simp add: q dn)
      next
        fix q :: "nat × ('a ⇒ nat)" assume "q ∈ Sigma (UNIV::nat set) blk"
        then obtain m β where q: "q = (m, β)" and "β ∈ blk m" by (cases q, simp)
        thus "snd q ∈ ra_idx" by (simp add: blk_def)
      next
        fix α :: "'a ⇒ nat" assume "α ∈ ra_idx"
        show "u ^ (fst (ra_deg α, α)) * (ra_monomial d (snd (ra_deg α, α)) * c (snd (ra_deg α, α)))
                 = (u ^ (ra_deg α) * ra_monomial d α) * c α"
          by (simp add: mult.assoc)
      qed simp
      with HSu show ?thesis by blast
    qed
    ― ‹collapse each finite degree block›
    have inner: "((λα. u ^ n * (ra_monomial d α * c α)) has_sum (acoef n * u ^ n)) (blk n)" for n
    proof -
      have "((λα. u ^ n * (ra_monomial d α * c α)) has_sum
               (∑α∈blk n. u ^ n * (ra_monomial d α * c α))) (blk n)"
        by (rule has_sum_finite[OF blk_fin])
      moreover have "(∑α∈blk n. u ^ n * (ra_monomial d α * c α)) = acoef n * u ^ n"
        by (simp only: acoef_def sum_distrib_left mult.commute)
      ultimately show ?thesis by simp
    qed
    ― ‹partition sum: from Sigma to the base nat-indexed series›
    have basesum: "((λn. acoef n * u ^ n) has_sum f (x0 + u *R d)) (UNIV::nat set)"
    proof (rule has_sum_Sigma'[where f = "λq. u ^ (fst q) * (ra_monomial d (snd q) * c (snd q))"
                                 and B = blk])
      show "((λq. u ^ (fst q) * (ra_monomial d (snd q) * c (snd q))) has_sum f (x0 + u *R d))
              (Sigma (UNIV::nat set) blk)" by (rule reidx)
    next
      fix n :: nat assume "n ∈ (UNIV::nat set)"
      show "((λα. u ^ (fst (n, α)) * (ra_monomial d (snd (n, α)) * c (snd (n, α)))) has_sum (acoef n * u ^ n)) (blk n)"
        using inner[of n] by simp
    qed
    have "(λn. acoef n * u ^ n) sums f (x0 + u *R d)"
      by (rule has_sum_imp_sums[OF basesum])
    thus ?thesis by (simp only: pt_eq u_def)
  qed
  show ?thesis
    by (rule real_powser_imp_real_analytic_at_1d[OF r'_pos PS])
qed

subsection ‹Propagating a zero of an analytic function along a segment›

text ‹Identity theorem along a segment: if ‹f› is real-analytic on ‹U›, the segment from ‹z›
  to ‹y› lies in ‹U› and ‹f› vanishes near ‹z›, then ‹f y = 0›.›

lemma slice_zero_propagate:
  fixes f :: "'a::euclidean_space ⇒ real"
  assumes ana: "real_analytic_on f U"
    and seg: "closed_segment z y ⊆ U"
    and zero_near: "∃δ0>0. ∀w. dist w z < δ0 ⟶ f w = 0"
  shows "f y = 0"
proof (cases "y = z")
  case True
  from zero_near obtain δ0 where d0: "δ0 > 0" and fz: "⋀w. dist w z < δ0 ⟹ f w = 0"
    by blast
  show ?thesis using True fz[of z] d0 by simp
next
  case False
  define d where "d = y - z"
  have dnz: "d ≠ 0" using False by (simp add: d_def)
  have oU: "open U" using ana by (simp only: real_analytic_on_def)
  define g where "g = (λs::real. f (z + s *R d))"
  ― ‹parameter set where the line stays in ‹U››
  define I where "I = {s::real. z + s *R d ∈ U}"
  have segI: "{0..1} ⊆ I"
  proof
    fix s :: real assume s: "s ∈ {0..1}"
    have "z + s *R d = (1 - s) *R z + s *R y"
      by (simp add: d_def algebra_simps)
    moreover have "(1 - s) *R z + s *R y ∈ closed_segment z y"
      using s by (auto simp: in_segment)
    ultimately have "z + s *R d ∈ U" using seg by auto
    thus "s ∈ I" by (simp add: I_def)
  qed
  have Iopen: "open I"
  proof -
    have cont: "continuous (at s) (λs::real. z + s *R d)" for s
      by (intro continuous_intros)
    have "open ((λs::real. z + s *R d) -` U)"
      by (rule continuous_open_vimage[OF oU cont])
    moreover have "I = (λs::real. z + s *R d) -` U"
      by (auto simp: I_def)
    ultimately show ?thesis by simp
  qed
  ― ‹fatten the compact segment ‹[0,1]› inside the open set ‹I››
  obtain δ where dpos: "δ > 0" and fat: "(⋃x∈{0..1::real}. ball x δ) ⊆ I"
    using compact_subset_open_imp_ball_epsilon_subset[OF compact_Icc Iopen segI]
    by blast
  define J where "J = {s::real. -δ < s ∧ s < 1 + δ}"
  have Jopen: "open J"
    unfolding J_def by (simp add: open_Collect_conj open_Collect_less)
  have Jconn: "connected J"
  proof -
    have "J = {-δ<..<1+δ}" by (auto simp: J_def)
    thus ?thesis by (simp only: connected_Ioo)
  qed
  have JsubI: "J ⊆ I"
  proof
    fix s :: real assume "s ∈ J"
    then have sb: "-δ < s" "s < 1 + δ" by (auto simp: J_def)
    define x where "x = max 0 (min 1 s)"
    have xseg: "x ∈ {0..1}" by (simp add: x_def)
    have "dist s x < δ"
    proof (cases "s < 0")
      case True thus ?thesis using sb x_def by (simp only: dist_real_def)
    next
      case False
      show ?thesis
      proof (cases "s > 1")
        case True thus ?thesis using sb x_def by (simp only: dist_real_def)
      next
        case False
        with ‹¬ s < 0› have "x = s" by (simp only: x_def)
        thus ?thesis using dpos by (simp only: dist_real_def)
      qed
    qed
    then have "s ∈ ball x δ" by (simp add: dist_commute)
    then have "s ∈ (⋃x∈{0..1::real}. ball x δ)" using xseg by blast
    with fat show "s ∈ I" by blast
  qed
  have onein: "(1::real) ∈ J" using dpos by (simp add: J_def)
  ― ‹the slice ‹g› is real-analytic on ‹J››
  have gana: "real_analytic_on g J"
  proof -
    have "real_analytic_at_1d g c" if "c ∈ J" for c
    proof -
      have "z + c *R d ∈ U" using that JsubI by (auto simp: I_def)
      thus ?thesis
        unfolding g_def
        by (rule real_analytic_slice_at_point[OF ana _ dnz])
    qed
    thus ?thesis using Jopen by (simp add: real_analytic_on_1d_iff)
  qed
  ― ‹‹g› vanishes on an open subinterval of ‹J› around ‹0››
  from zero_near obtain δ0 where d0: "δ0 > 0" and fz: "⋀w. dist w z < δ0 ⟹ f w = 0"
    by blast
  define η where "η = min δ (δ0 / norm d)"
  have eta_pos: "η > 0" using dpos d0 dnz by (simp add: η_def)
  have gzero: "g s = 0" if "¦s¦ < η" for s
  proof -
    have ndpos: "norm d > 0" using dnz by simp
    have le1: "η * norm d ≤ (δ0 / norm d) * norm d"
      using ndpos by (intro mult_right_mono) (auto simp: η_def)
    have "dist (z + s *R d) z = ¦s¦ * norm d" by (simp add: dist_norm)
    also have "… < η * norm d" using that ndpos by (simp only: mult_strict_right_mono)
    also have "… ≤ (δ0 / norm d) * norm d" by (rule le1)
    also have "… = δ0" using ndpos by simp
    finally show ?thesis using fz[of "z + s *R d"] by (simp only: g_def)
  qed
  ― ‹the zero set of ‹g› on ‹J› is not nowhere dense›
  have zeroset_int: "interior (closure {s ∈ J. g s = 0}) ≠ {}"
  proof -
    have sub: "{s::real. -η < s ∧ s < η} ⊆ {s ∈ J. g s = 0}"
    proof
      fix s :: real assume "s ∈ {s. -η < s ∧ s < η}"
      then have sb: "-η < s" "s < η" by auto
      have "¦s¦ < η" using sb by simp
      have eta_le: "η ≤ δ" by (simp only: η_def)
      have "s ∈ J" unfolding J_def using sb eta_le dpos by simp
      then show "s ∈ {s ∈ J. g s = 0}" using gzero ‹¦s¦ < η› by auto
    qed
    have "open {s::real. -η < s ∧ s < η}"
      by (simp add: open_Collect_conj open_Collect_less)
    moreover have "(0::real) ∈ {s::real. -η < s ∧ s < η}" using eta_pos by simp
    ultimately have "(0::real) ∈ interior {s ∈ J. g s = 0}"
      using sub interior_maximal by (simp only: interiorI) 
    moreover have "interior {s ∈ J. g s = 0} ⊆ interior (closure {s ∈ J. g s = 0})"
      by (intro interior_mono closure_subset)
    ultimately show ?thesis by blast
  qed
  ― ‹hence by the one-dimensional nowhere-dense-zeros theorem ‹g› is identically zero on ‹J››
  have "¬ (∃x∈J. g x ≠ 0)"
  proof
    assume "∃x∈J. g x ≠ 0"
    from real_analytic_1d_nowhere_dense_zeros[OF gana Jconn this]
    have "interior (closure {s ∈ J. g s = 0}) = {}" .
    with zeroset_int show False by simp
  qed
  then have "g 1 = 0" using onein by blast
  thus ?thesis by (simp add: g_def d_def)
qed

subsection ‹Nowhere-dense zeros in several variables›

theorem real_analytic_nowhere_dense_zeros:
  fixes f :: "'a::euclidean_space ⇒ real"
  assumes ana: "real_analytic_on f U" and conn: "connected U"
    and ex: "∃x∈U. f x ≠ 0"
  shows "interior (closure {x ∈ U. f x = 0}) = {}"
proof -
  have oU: "open U" using ana by (simp only: real_analytic_on_def)

  text ‹Continuity of ‹f› on ‹U› (proved directly from the local power series).›
  have contf: "continuous (at x) f" if "x ∈ U" for x
    by (rule real_analytic_on_imp_continuous[OF ana that])

  text ‹The set of points around which ‹f› vanishes on a ball inside ‹U›.›
  define Z where "Z = {x ∈ U. ∃ε>0. ball x ε ⊆ U ∧ (∀y∈ball x ε. f y = 0)}"
  have Zsub: "Z ⊆ U" by (auto simp: Z_def)

  text ‹‹Z› is open.›
  have Zopen: "open Z"
  proof (rule openI)
    fix x assume "x ∈ Z"
    then obtain ε where xU: "x ∈ U" and epos: "ε > 0"
      and ballU: "ball x ε ⊆ U" and fz: "∀y∈ball x ε. f y = 0"
      by (auto simp: Z_def)
    have "ball x ε ⊆ Z"
    proof
      fix w assume w: "w ∈ ball x ε"
      then obtain ρ where rpos: "ρ > 0" and rsub: "ball w ρ ⊆ ball x ε"
        using open_contains_ball by (metis open_ball)
      have "w ∈ U" using w ballU by blast
      moreover have "ball w ρ ⊆ U" using rsub ballU by blast
      moreover have "∀y∈ball w ρ. f y = 0" using rsub fz by blast
      ultimately show "w ∈ Z" using rpos by (auto simp: Z_def)
    qed
    thus "∃e>0. ball x e ⊆ Z" using epos by blast
  qed
  have ZopenIn: "openin (top_of_set U) Z"
    using Zopen Zsub by (metis Int_absorb1 openin_open_Int)

  text ‹‹Z› is closed in ‹U›: this is the multivariate identity theorem via analytic slices.›
  have Zlimit: "x ∈ Z" if xU: "x ∈ U" and xcl: "x ∈ closure Z" for x
  proof -
    obtain ρ where rpos: "ρ > 0" and ballU: "ball x ρ ⊆ U"
      using oU xU open_contains_ball by blast
    ― ‹a point of ‹Z› within ‹ρ/2› of ‹x››
    have "ρ/2 > 0" using rpos by simp
    with xcl have "∃z∈Z. dist z x < ρ/2"
      using closure_approachable[of x Z] by blast
    then obtain z where zZ: "z ∈ Z" and zx: "dist z x < ρ/2" by blast
    from zZ obtain δ0 where d0: "δ0 > 0" and fz0: "∀y∈ball z δ0. f y = 0"
      by (auto simp: Z_def)
    have zb: "z ∈ ball x (ρ/2)" using zx by (simp add: dist_commute)
    ― ‹‹f› vanishes throughout ‹ball x (ρ/2)››
    have fvan: "f y = 0" if yb: "y ∈ ball x (ρ/2)" for y
    proof -
      have convB: "convex (ball x (ρ/2))" by (rule convex_ball)
      have "closed_segment z y ⊆ ball x (ρ/2)"
        by (rule closed_segment_subset[OF zb yb convB])
      also have "ball x (ρ/2) ⊆ ball x ρ" using rpos by (intro subset_ball) simp
      finally have segU: "closed_segment z y ⊆ U" using ballU by blast
      have znear: "∃δ0>0. ∀w. dist w z < δ0 ⟶ f w = 0"
        using d0 fz0 by (auto simp: dist_commute)
      show ?thesis by (rule slice_zero_propagate[OF ana segU znear])
    qed
    have "ball x (ρ/2) ⊆ ball x ρ" using rpos by (intro subset_ball) simp
    then have ballhalf: "ball x (ρ/2) ⊆ U" using ballU by blast
    have rh: "ρ/2 > 0" using rpos by simp
    have "∃ε>0. ball x ε ⊆ U ∧ (∀y∈ball x ε. f y = 0)"
      using rh ballhalf fvan by blast
    thus "x ∈ Z" using xU by (simp add: Z_def)
  qed
  have Zeq: "Z = U ∩ closure Z" using Zsub Zlimit closure_subset by blast
  have ZclosedIn: "closedin (top_of_set U) Z"
    by (subst Zeq) (simp add: closedin_closed_Int)

  text ‹If ‹W = interior (closure ...)› is nonempty then ‹Z› is nonempty.›
  define W where "W = interior (closure {x ∈ U. f x = 0})"
  have Wopen: "open W" by (simp add: W_def)
  have Zne: "Z ≠ {}" if WNE: "W ≠ {}"
  proof -
    from WNE obtain w where wW: "w ∈ W" by blast
    have Wsub: "W ⊆ closure {x ∈ U. f x = 0}" by (simp only: W_def interior_subset)
    obtain e where epos: "e > 0" and eball: "ball w e ⊆ W"
      using wW Wopen open_contains_ball by blast
    have wcl: "w ∈ closure {x ∈ U. f x = 0}" using wW Wsub by blast
    ― ‹the open ball ‹ball w e› meets the zero set, since it meets its closure›
    have "ball w e ∩ {x ∈ U. f x = 0} ≠ {}"
    proof -
      have "w ∈ ball w e ∩ closure {x ∈ U. f x = 0}" using wcl epos by simp
      hence "ball w e ∩ closure {x ∈ U. f x = 0} ≠ {}" by blast
      thus "ball w e ∩ {x ∈ U. f x = 0} ≠ {}"
        using open_Int_closure_eq_empty[OF open_ball, of w e "{x ∈ U. f x = 0}"] by blast
    qed
    then obtain u where uU: "u ∈ ball w e" "u ∈ U" "f u = 0" by blast
    have uW: "u ∈ W" using uU(1) eball by blast
    define V where "V = W ∩ U"
    have Vopen: "open V" unfolding V_def by (intro open_Int Wopen oU)
    have uV: "u ∈ V" using uW uU(2) by (simp add: V_def)
    ― ‹on the open set ‹V›, ‹f ≡ 0› by continuity through the closure›
    have fzeroV: "f v = 0" if "v ∈ V" for v
    proof -
      have vW: "v ∈ W" and vU: "v ∈ U" using that by (auto simp: V_def)
      have vcl: "v ∈ closure {x ∈ U. f x = 0}" using vW Wsub by blast
      have "isCont f v" using contf[OF vU] by simp
      from vcl obtain s where s: "⋀k. s k ∈ {x ∈ U. f x = 0}" "s ⇢ v"
        using closure_sequential by blast
      have "(λk. f (s k)) ⇢ f v"
        using ‹isCont f v› s(2) by (simp only: continuous_within isCont_tendsto_compose)
      moreover have "(λk. f (s k)) = (λk. 0)" using s(1) by auto
      ultimately have "(λk. (0::real)) ⇢ f v" by simp
      thus "f v = 0" by (simp only: LIMSEQ_const_iff)
    qed
    ― ‹a ball around ‹u› inside ‹V ⊆ U› on which ‹f ≡ 0›: so ‹u ∈ Z››
    obtain ρ where ρ: "ρ > 0" and rball: "ball u ρ ⊆ V"
      using uV Vopen open_contains_ball by blast
    have rballU: "ball u ρ ⊆ U" using rball by (auto simp: V_def)
    have "∀y∈ball u ρ. f y = 0" using rball fzeroV by blast
    then have "u ∈ Z" using ρ rballU uU(2) by (auto simp: Z_def)
    thus "Z ≠ {}" by blast
  qed

  text ‹Connectedness: ‹Z› is clopen in ‹U›, so ‹Z = ∅› or ‹Z = U›.›
  have clopen: "Z = {} ∨ Z = U"
    using conn ZopenIn ZclosedIn unfolding connected_clopen by blast

  show ?thesis
  proof (rule ccontr)
    assume "interior (closure {x ∈ U. f x = 0}) ≠ {}"
    then have WNE: "W ≠ {}" by (simp add: W_def)
    then have "Z ≠ {}" by (rule Zne)
    with clopen have "Z = U" by blast
    have "f x = 0" if "x ∈ U" for x
    proof -
      have "x ∈ Z" using that ‹Z = U› by simp
      then obtain ε where epos: "ε > 0" and fz: "∀y∈ball x ε. f y = 0"
        by (auto simp: Z_def)
      have "x ∈ ball x ε" using epos by simp
      thus "f x = 0" using fz by blast
    qed
    with ex show False by blast
  qed
qed



text ‹Scalar component (inner product with a fixed vector) of an analytic vector
  function is analytic.›

lemma real_analytic_on_inner_component:
  fixes f :: "'a::euclidean_space ⇒ 'b::euclidean_space"
  assumes F: "real_analytic_on f U"
  shows "real_analytic_on (λx. f x ∙ b) U"
proof -
  from F have U: "open U" by (simp only: real_analytic_on_def)
  show ?thesis
    unfolding real_analytic_on_def
  proof (intro conjI ballI)
    show "open U" by (rule U)
  next
    fix x0 assume x0: "x0 ∈ U"
    from F x0 obtain r c where r: "0 < r"
      and F1: "⋀x. dist x x0 < r ⟹
                ((λα. ra_monomial (x - x0) α *R c α) has_sum f x) ra_idx"
      unfolding real_analytic_on_def by blast
    show "∃r>0. ∃cc. ∀x. dist x x0 < r ⟶
            ((λα. ra_monomial (x - x0) α *R cc α) has_sum (f x ∙ b)) ra_idx"
    proof (intro exI[where x=r] conjI exI[where x="λα. c α ∙ b"] allI impI)
      show "0 < r" by (rule r)
    next
      fix x assume d: "dist x x0 < r"
      have bl: "bounded_linear (λy::'b. y ∙ b)"
        by (rule bounded_linear_inner_left)
      have "((λα. (ra_monomial (x - x0) α *R c α) ∙ b) has_sum (f x ∙ b)) ra_idx"
        by (rule has_sum_bounded_linear[OF bl F1[OF d]])
      thus "((λα. ra_monomial (x - x0) α *R (c α ∙ b)) has_sum (f x ∙ b)) ra_idx"
        by (simp only: inner_scaleR_left scaleR_conv_of_real) simp
    qed
  qed
qed

subsection ‹Explicit multi-index operations›

definition ra_idx_add :: "('a⇒nat) ⇒ ('a⇒nat) ⇒ ('a⇒nat)" where
  "ra_idx_add = (λα β b. α b + β b)"
definition ra_idx_diff :: "('a⇒nat) ⇒ ('a⇒nat) ⇒ ('a⇒nat)" where
  "ra_idx_diff = (λγ α b. γ b - α b)"
definition ra_idx_le :: "('a⇒nat) ⇒ ('a⇒nat) ⇒ bool" where
  "ra_idx_le = (λα γ. ∀b. α b ≤ γ b)"
definition ra_idx_below :: "('a⇒nat) ⇒ ('a::euclidean_space⇒nat) set" where
  "ra_idx_below γ = {α. α ∈ ra_idx ∧ ra_idx_le α γ}"

lemma idx_add: "ra_idx_add α β ∈ ra_idx" if "α ∈ ra_idx" "β ∈ ra_idx"
  for α β :: "'a::euclidean_space⇒nat"
proof -
  have "{b. ra_idx_add α β b ≠ 0} ⊆ {b. α b ≠ 0} ∪ {b. β b ≠ 0}"
    by (auto simp: ra_idx_add_def)
  also have "… ⊆ Basis" using that by (auto simp: ra_idx_def)
  finally show ?thesis by (simp add: ra_idx_def)
qed

lemma idx_sub: "ra_idx_diff γ α ∈ ra_idx" if "γ ∈ ra_idx"
  for γ α :: "'a::euclidean_space⇒nat"
proof -
  have "{b. ra_idx_diff γ α b ≠ 0} ⊆ {b. γ b ≠ 0}" by (auto simp: ra_idx_diff_def)
  also have "… ⊆ Basis" using that by (auto simp: ra_idx_def)
  finally show ?thesis by (simp add: ra_idx_def)
qed

lemma ra_monomial_idx_add: "ra_monomial h (ra_idx_add α β) = ra_monomial h α * ra_monomial h β"
  for h :: "'a::euclidean_space" and α β
  by (simp only: ra_monomial_def ra_idx_add_def power_add prod.distrib)

lemma idx_lower_fin: "finite (ra_idx_below γ)" for γ :: "'a::euclidean_space ⇒ nat"
proof -
  define N where "N = Max (insert 0 (γ ` (Basis :: 'a set)))"
  have "ra_idx_below γ
          ⊆ {hh. ∀x::'a. (x ∈ Basis ⟶ hh x ∈ {0..N}) ∧ (x ∉ Basis ⟶ hh x = 0)}"
  proof (rule subsetI)
    fix α :: "'a ⇒ nat" assume "α ∈ ra_idx_below γ"
    then have a1: "α ∈ ra_idx" and a2: "ra_idx_le α γ" by (auto simp: ra_idx_below_def)
    have "∀x::'a. (x ∈ Basis ⟶ α x ∈ {0..N}) ∧ (x ∉ Basis ⟶ α x = 0)"
    proof (intro allI conjI impI)
      fix x :: 'a assume "x ∈ Basis"
      have "α x ≤ γ x" using a2 by (simp only: ra_idx_le_def)
      also have "γ x ≤ N" unfolding N_def using ‹x ∈ Basis› by (intro Max_ge) auto
      finally show "α x ∈ {0..N}" by simp
    next
      fix x :: 'a assume "x ∉ Basis"
      with a1 show "α x = 0" by (auto simp: ra_idx_def)
    qed
    thus "α ∈ {hh. ∀x::'a. (x ∈ Basis ⟶ hh x ∈ {0..N}) ∧ (x ∉ Basis ⟶ hh x = 0)}"
      by simp
  qed
  moreover have "finite {hh::'a⇒nat. ∀x. (x ∈ Basis ⟶ hh x ∈ {0..N}) ∧ (x ∉ Basis ⟶ hh x = (0::nat))}"
    by (rule finite_set_of_finite_funs) auto
  ultimately show ?thesis by (rule finite_subset)
qed

text ‹The Fubini/Cauchy product of two real unordered sums over a common index set.›

lemma prod_has_sum:
  "((λ(α,β). u α * v β) has_sum (Uv * Vv)) (I × I)"
  if uHS: "(u has_sum Uv) I" and vHS: "(v has_sum Vv) I"
  for u v :: "'i ⇒ real" and Uv Vv I
proof -
  have u_abs: "(λz. norm (u z)) summable_on I"
    using uHS has_sum_imp_summable summable_on_iff_abs_summable_on_real by blast
  have v_abs: "(λz. norm (v z)) summable_on I"
    using vHS has_sum_imp_summable summable_on_iff_abs_summable_on_real by blast
  have inner: "((λβ. u α * v β) has_sum (u α * Vv)) I" for α
    by (rule has_sum_cmult_right[OF vHS])
  have outer: "((λα. u α * Vv) has_sum (Uv * Vv)) I"
    by (rule has_sum_cmult_left[OF uHS])
  have inner_abs: "(λβ. norm (u α * v β)) summable_on I" for α
  proof -
    have "(λβ. norm (u α) * norm (v β)) summable_on I"
      using v_abs by (rule summable_on_cmult_right)
    thus ?thesis by (simp only: norm_mult flip: abs_mult)
  qed
  have tail_abs: "(λα. norm (∑∞β∈I. norm (u α * v β))) summable_on I"
  proof -
    have eq: "norm (∑∞β∈I. norm (u α * v β)) = norm (u α) * (∑∞β∈I. norm (v β))" for α
    proof -
      have nn: "(∑∞β∈I. norm (u α * v β)) = norm (u α) * (∑∞β∈I. norm (v β))"
      proof -
        have "(∑∞β∈I. norm (u α * v β)) = (∑∞β∈I. norm (u α) * norm (v β))"
          by (simp add: abs_mult)
        also have "… = norm (u α) * (∑∞β∈I. norm (v β))"
          by (rule infsum_cmult_right) (rule v_abs)
        finally show ?thesis .
      qed
      have ge: "(0::real) ≤ norm (u α) * (∑∞β∈I. norm (v β))"
        by (intro mult_nonneg_nonneg) (auto intro: infsum_nonneg)
      from nn ge show ?thesis by simp
    qed
    have "(λα. norm (u α) * (∑∞β∈I. norm (v β))) summable_on I"
      using u_abs by (rule summable_on_cmult_left)
    thus ?thesis unfolding eq .
  qed
  have conj1: "∀α∈I. (λβ. norm ((λ(α,β). u α * v β) (α, β))) summable_on I"
  proof
    fix α assume "α ∈ I"
    have "(λβ. norm (u α * v β)) summable_on I" by (rule inner_abs)
    thus "(λβ. norm ((λ(α,β). u α * v β) (α, β))) summable_on I" by simp
  qed
  have conj2: "(λα. norm (∑∞β∈I. norm ((λ(α,β). u α * v β) (α, β)))) summable_on I"
  proof -
    have "(λα. norm (∑∞β∈I. norm (u α * v β))) summable_on I" by (rule tail_abs)
    thus ?thesis by simp
  qed
  have absS: "(λz. norm ((λ(α,β). u α * v β) z)) summable_on (Sigma I (λ_. I))"
    by (rule Infinite_Sum.abs_summable_on_Sigma_iff
          [where f = "λ(α,β). u α * v β" and A = I and B = "λ_. I", THEN iffD2,
           OF conjI[OF conj1 conj2]])
  have summ: "(λ(α,β). u α * v β) summable_on Sigma I (λ_. I)"
    by (rule abs_summable_summable[OF absS])
  have "((λ(α,β). u α * v β) has_sum (Uv * Vv)) (Sigma I (λ_. I))"
  proof (rule has_sum_SigmaI[where g = "λα. u α * Vv"])
    fix α assume "α ∈ I"
    have "((λβ. u α * v β) has_sum (u α * Vv)) I" by (rule inner)
    thus "((λβ. (λ(α,β). u α * v β) (α, β)) has_sum (u α * Vv)) I" by simp
  next
    show "((λα. u α * Vv) has_sum (Uv * Vv)) I" by (rule outer)
  next
    show "(λ(α,β). u α * v β) summable_on Sigma I (λ_. I)" by (rule summ)
  qed
  thus ?thesis by (simp only: Sigma_def)
qed

text ‹The explicit Cauchy-product coefficient operator.›

definition ra_cauchy_prod :: "(('a::euclidean_space⇒nat) ⇒ real) ⇒ (('a⇒nat) ⇒ real) ⇒ (('a⇒nat) ⇒ real)" where
  "ra_cauchy_prod c1 c2 = (λγ. ∑α∈ra_idx_below γ. c1 α * c2 (ra_idx_diff γ α))"

text ‹Quantitative Cauchy product on a common ball, exposing the (x-independent)
  coefficient family.›

lemma quant_mult:
  fixes c1 c2 :: "('a::euclidean_space ⇒ nat) ⇒ real"
  assumes F1: "((λα. ra_monomial h α * c1 α) has_sum Fv) ra_idx"
    and G1: "((λβ. ra_monomial h β * c2 β) has_sum Gv) ra_idx"
  shows "((λγ. ra_monomial h γ *R ra_cauchy_prod c1 c2 γ) has_sum (Fv * Gv)) ra_idx"
proof -
  have step1:
    "((λ(α,β). (ra_monomial h α * c1 α) * (ra_monomial h β * c2 β))
         has_sum (Fv * Gv)) (ra_idx × ra_idx)"
    using prod_has_sum[OF F1 G1] .
  have step2:
    "((λ(γ,α). ra_monomial h γ * (c1 α * c2 (ra_idx_diff γ α))) has_sum (Fv * Gv))
        (Sigma ra_idx ra_idx_below)"
  proof -
    have "((λ(α,β). (ra_monomial h α * c1 α) * (ra_monomial h β * c2 β))
             has_sum (Fv * Gv)) (ra_idx × ra_idx)
          = ((λ(γ,α). ra_monomial h γ * (c1 α * c2 (ra_idx_diff γ α))) has_sum (Fv * Gv))
             (Sigma ra_idx ra_idx_below)"
    proof (rule has_sum_reindex_bij_witness
             [where j = "λ(α,β). (ra_idx_add α β, α)" and i = "λ(γ,α). (α, ra_idx_diff γ α)"])
      fix p :: "('a⇒nat) × ('a⇒nat)"
      assume "p ∈ ra_idx × ra_idx"
      obtain α β where p: "p = (α,β)" by (cases p)
      show "(case (case p of (α,β) ⇒ (ra_idx_add α β, α)) of (γ,α) ⇒ (α, ra_idx_diff γ α)) = p"
        by (simp add: p ra_idx_diff_def ra_idx_add_def)
    next
      fix p :: "('a⇒nat) × ('a⇒nat)"
      assume P: "p ∈ ra_idx × ra_idx"
      obtain α β where p: "p = (α,β)" by (cases p)
      have aα: "α ∈ ra_idx" and aβ: "β ∈ ra_idx" using P p by auto
      have m1: "ra_idx_add α β ∈ ra_idx" by (rule idx_add[OF aα aβ])
      have m2: "α ∈ ra_idx_below (ra_idx_add α β)"
        using aα by (simp add: ra_idx_below_def ra_idx_le_def ra_idx_add_def)
      show "(case p of (α,β) ⇒ (ra_idx_add α β, α)) ∈ Sigma ra_idx ra_idx_below"
        using m1 m2 by (simp add: p)
    next
      fix q :: "('a⇒nat) × ('a⇒nat)"
      assume Q: "q ∈ Sigma ra_idx ra_idx_below"
      obtain γ α where q: "q = (γ,α)" by (cases q)
      have gγ: "γ ∈ ra_idx" and l: "ra_idx_le α γ" using Q q by (auto simp: ra_idx_below_def)
      have "ra_idx_add α (ra_idx_diff γ α) = γ"
      proof (rule ext)
        fix b have "α b ≤ γ b" using l by (simp only: ra_idx_le_def)
        thus "ra_idx_add α (ra_idx_diff γ α) b = γ b" by (simp only: ra_idx_add_def ra_idx_diff_def)
      qed
      then show "(case (case q of (γ,α) ⇒ (α, ra_idx_diff γ α)) of (α,β) ⇒ (ra_idx_add α β, α)) = q"
        by (simp add: q)
    next
      fix q :: "('a⇒nat) × ('a⇒nat)"
      assume Q: "q ∈ Sigma ra_idx ra_idx_below"
      obtain γ α where q: "q = (γ,α)" by (cases q)
      have gγ: "γ ∈ ra_idx" and aα: "α ∈ ra_idx" using Q q by (auto simp: ra_idx_below_def)
      show "(case q of (γ,α) ⇒ (α, ra_idx_diff γ α)) ∈ ra_idx × ra_idx"
        by (simp add: q aα idx_sub[OF gγ])
    next
      fix p :: "('a⇒nat) × ('a⇒nat)"
      assume P: "p ∈ ra_idx × ra_idx"
      obtain α β where p: "p = (α,β)" by (cases p)
      have sub_eq: "ra_idx_diff (ra_idx_add α β) α = β" by (simp add: ra_idx_add_def ra_idx_diff_def)
      have mm: "ra_monomial h α * ra_monomial h β = ra_monomial h (ra_idx_add α β)"
        by (simp only: ra_monomial_idx_add)
      have "ra_monomial h (ra_idx_add α β) * (c1 α * c2 (ra_idx_diff (ra_idx_add α β) α))
              = ra_monomial h (ra_idx_add α β) * (c1 α * c2 β)" by (simp only: sub_eq)
      also have "… = (ra_monomial h α * ra_monomial h β) * (c1 α * c2 β)"
        by (simp only: mm)
      also have "… = (ra_monomial h α * c1 α) * (ra_monomial h β * c2 β)"
        by (simp only: mult.assoc mult.left_commute)
      finally show "(case (case p of (α,β) ⇒ (ra_idx_add α β, α)) of (γ,α) ⇒
                        ra_monomial h γ * (c1 α * c2 (ra_idx_diff γ α)))
                   = (case p of (α,β) ⇒ (ra_monomial h α * c1 α) * (ra_monomial h β * c2 β))"
        by (simp add: p)
    qed simp
    with step1 show ?thesis by simp
  qed
  have inner_fin:
    "((λα. ra_monomial h γ * (c1 α * c2 (ra_idx_diff γ α))) has_sum
        (ra_monomial h γ *R ra_cauchy_prod c1 c2 γ)) (ra_idx_below γ)" if "γ ∈ ra_idx" for γ
  proof -
    have fin: "finite (ra_idx_below γ)" by (rule idx_lower_fin)
    have "((λα. ra_monomial h γ * (c1 α * c2 (ra_idx_diff γ α))) has_sum
             (∑α∈ra_idx_below γ. ra_monomial h γ * (c1 α * c2 (ra_idx_diff γ α)))) (ra_idx_below γ)"
      by (rule has_sum_finite[OF fin])
    also have "(∑α∈ra_idx_below γ. ra_monomial h γ * (c1 α * c2 (ra_idx_diff γ α)))
                 = ra_monomial h γ * (∑α∈ra_idx_below γ. c1 α * c2 (ra_idx_diff γ α))"
      by (simp only: sum_distrib_left)
    also have "(∑α∈ra_idx_below γ. c1 α * c2 (ra_idx_diff γ α)) = ra_cauchy_prod c1 c2 γ"
      by (simp only: ra_cauchy_prod_def)
    finally show ?thesis by simp
  qed
  have "((λγ. ra_monomial h γ *R ra_cauchy_prod c1 c2 γ) has_sum (Fv * Gv)) ra_idx"
  proof (rule has_sum_Sigma'
           [where f = "λ(γ,α). ra_monomial h γ * (c1 α * c2 (ra_idx_diff γ α))"
              and A = ra_idx and B = ra_idx_below and a = "Fv * Gv"
              and b = "λγ. ra_monomial h γ *R ra_cauchy_prod c1 c2 γ"])
    show "((λ(γ,α). ra_monomial h γ * (c1 α * c2 (ra_idx_diff γ α))) has_sum (Fv * Gv))
            (Sigma ra_idx ra_idx_below)" by (rule step2)
  next
    fix γ :: "'a ⇒ nat" assume "γ ∈ ra_idx"
    then have "((λα. ra_monomial h γ * (c1 α * c2 (ra_idx_diff γ α))) has_sum
                  (ra_monomial h γ *R ra_cauchy_prod c1 c2 γ)) (ra_idx_below γ)" by (rule inner_fin)
    thus "((λα. (λ(γ,α). ra_monomial h γ * (c1 α * c2 (ra_idx_diff γ α))) (γ, α)) has_sum
              (ra_monomial h γ *R ra_cauchy_prod c1 c2 γ)) (ra_idx_below γ)" by simp
  qed
  thus ?thesis .
qed

subsection ‹Quantitative series-on-a-ball representation›

definition ra_series_on ::
  "'a::euclidean_space ⇒ real ⇒ (('a⇒nat)⇒real) ⇒ ('a⇒real) ⇒ bool" where
  "ra_series_on x0 r c F ⟷
     (∀x. dist x x0 < r ⟶ ((λα. ra_monomial (x - x0) α *R c α) has_sum F x) ra_idx)"

lemma ra_series_on_mono_radius:
  assumes "ra_series_on x0 r c F" and "r' ≤ r"
  shows "ra_series_on x0 r' c F"
  using assms unfolding ra_series_on_def by fastforce

definition ra_idx_zero :: "'a::euclidean_space ⇒ nat" where
  "ra_idx_zero = (λ_. 0)"

definition ra_coeff_one :: "('a::euclidean_space⇒nat) ⇒ real" where
  "ra_coeff_one = (λα. if α = ra_idx_zero then 1 else 0)"

lemma ra_idx_zero_in: "ra_idx_zero ∈ ra_idx"
  by (simp add: ra_idx_def ra_idx_zero_def)

lemma ra_series_on_one: "ra_series_on x0 r ra_coeff_one (λ_. 1)"
  unfolding ra_series_on_def
proof (intro allI impI)
  fix x :: 'a assume "dist x x0 < r"
  define h where "h = x - x0"
  have sing: "((λα. ra_monomial h α *R ra_coeff_one α) has_sum
                 (∑α∈{ra_idx_zero}. ra_monomial h α *R ra_coeff_one α)) {ra_idx_zero}"
    by (rule has_sum_finite) auto
  have val: "(∑α∈{ra_idx_zero}. ra_monomial h α *R ra_coeff_one α) = (1::real)"
    by (simp add: ra_coeff_one_def ra_monomial_def ra_idx_zero_def)
  have "((λα. ra_monomial h α *R ra_coeff_one α) has_sum (1::real)) ra_idx
          = ((λα. ra_monomial h α *R ra_coeff_one α) has_sum (1::real)) {ra_idx_zero}"
    by (rule has_sum_cong_neutral) (auto simp: ra_coeff_one_def ra_idx_zero_in)
  hence "((λα. ra_monomial h α *R ra_coeff_one α) has_sum (1::real)) ra_idx"
    using sing val by simp
  thus "((λα. ra_monomial (x - x0) α *R ra_coeff_one α) has_sum (1::real)) ra_idx"
    by (simp only: h_def)
qed

lemma ra_series_on_mult:
  assumes "ra_series_on x0 r c1 F" and "ra_series_on x0 r c2 G"
  shows "ra_series_on x0 r (ra_cauchy_prod c1 c2) (λx. F x * G x)"
  unfolding ra_series_on_def
proof (intro allI impI)
  fix x :: 'a assume d: "dist x x0 < r"
  have F1: "((λα. ra_monomial (x - x0) α * c1 α) has_sum F x) ra_idx"
    using assms(1) d unfolding ra_series_on_def by simp
  have G1: "((λβ. ra_monomial (x - x0) β * c2 β) has_sum G x) ra_idx"
    using assms(2) d unfolding ra_series_on_def by simp
  show "((λγ. ra_monomial (x - x0) γ *R ra_cauchy_prod c1 c2 γ) has_sum (F x * G x)) ra_idx"
    by (rule quant_mult[OF F1 G1])
qed

definition ra_cauchy_pow :: "(('a::euclidean_space⇒nat)⇒real) ⇒ nat ⇒ (('a⇒nat)⇒real)" where
  "ra_cauchy_pow c n = (ra_cauchy_prod c ^^ n) ra_coeff_one"

lemma ra_cauchy_pow_0: "ra_cauchy_pow c 0 = ra_coeff_one" by (simp add: ra_cauchy_pow_def)
lemma ra_cauchy_pow_Suc: "ra_cauchy_pow c (Suc n) = ra_cauchy_prod c (ra_cauchy_pow c n)" by (simp add: ra_cauchy_pow_def)

lemma ra_series_on_power:
  assumes "ra_series_on x0 r c F"
  shows "ra_series_on x0 r (ra_cauchy_pow c n) (λx. (F x) ^ n)"
proof (induction n)
  case 0
  show ?case using ra_series_on_one[of x0 r] by (simp add: ra_cauchy_pow_0)
next
  case (Suc n)
  have "ra_series_on x0 r (ra_cauchy_prod c (ra_cauchy_pow c n)) (λx. F x * (F x) ^ n)"
    by (rule ra_series_on_mult[OF assms Suc.IH])
  thus ?case by (simp add: ra_cauchy_pow_Suc)
qed

lemma ra_series_on_prod:
  fixes F :: "'i ⇒ 'a::euclidean_space ⇒ real"
  assumes "finite I"
    and "⋀i. i ∈ I ⟹ ∃c. ra_series_on x0 r c (F i)"
  shows "∃cc. ra_series_on x0 r cc (λx. ∏i∈I. F i x)"
  using assms
proof (induction I rule: finite_induct)
  case empty
  have "ra_series_on x0 r ra_coeff_one (λx. ∏i∈{}. F i x)" using ra_series_on_one[of x0 r] by simp
  thus ?case by blast
next
  case (insert j I)
  obtain cj where cj: "ra_series_on x0 r cj (F j)" using insert.prems[of j] by blast
  obtain crest where crest: "ra_series_on x0 r crest (λx. ∏i∈I. F i x)"
    using insert.prems insert.IH by blast
  have "ra_series_on x0 r (ra_cauchy_prod cj crest) (λx. F j x * (∏i∈I. F i x))"
    by (rule ra_series_on_mult[OF cj crest])
  hence "ra_series_on x0 r (ra_cauchy_prod cj crest) (λx. ∏i∈insert j I. F i x)"
    using insert.hyps by simp
  thus ?case by blast
qed

lemma ra_series_on_const:
  "ra_series_on x0 r (λα. k * ra_coeff_one α) (λ_. k)"
  unfolding ra_series_on_def
proof (intro allI impI)
  fix x :: 'a assume d: "dist x x0 < r"
  have "((λα. ra_monomial (x - x0) α *R ra_coeff_one α) has_sum (1::real)) ra_idx"
    using ra_series_on_one[of x0 r] d unfolding ra_series_on_def by simp
  hence "((λα. k * (ra_monomial (x - x0) α *R ra_coeff_one α)) has_sum (k * 1)) ra_idx"
    by (rule has_sum_cmult_right)
  thus "((λα. ra_monomial (x - x0) α *R (k * ra_coeff_one α)) has_sum k) ra_idx"
    by (simp add: mult.left_commute)
qed

lemma ra_series_on_add:
  assumes "ra_series_on x0 r c1 F" and "ra_series_on x0 r c2 G"
  shows "ra_series_on x0 r (λα. c1 α + c2 α) (λx. F x + G x)"
  unfolding ra_series_on_def
proof (intro allI impI)
  fix x :: 'a assume d: "dist x x0 < r"
  have F1: "((λα. ra_monomial (x - x0) α *R c1 α) has_sum F x) ra_idx"
    using assms(1) d unfolding ra_series_on_def by simp
  have G1: "((λα. ra_monomial (x - x0) α *R c2 α) has_sum G x) ra_idx"
    using assms(2) d unfolding ra_series_on_def by simp
  have "((λα. ra_monomial (x - x0) α *R c1 α + ra_monomial (x - x0) α *R c2 α)
            has_sum (F x + G x)) ra_idx"
    by (rule has_sum_add[OF F1 G1])
  thus "((λα. ra_monomial (x - x0) α *R (c1 α + c2 α)) has_sum (F x + G x)) ra_idx"
    by (simp only: scaleR_add_right)
qed

lemma ra_series_on_diff:
  assumes "ra_series_on x0 r c1 F" and "ra_series_on x0 r c2 G"
  shows "ra_series_on x0 r (λα. c1 α - c2 α) (λx. F x - G x)"
  unfolding ra_series_on_def
proof (intro allI impI)
  fix x :: 'a assume d: "dist x x0 < r"
  have F1: "((λα. ra_monomial (x - x0) α *R c1 α) has_sum F x) ra_idx"
    using assms(1) d unfolding ra_series_on_def by simp
  have G1: "((λα. ra_monomial (x - x0) α *R c2 α) has_sum G x) ra_idx"
    using assms(2) d unfolding ra_series_on_def by simp
  have G1': "((λα. - (ra_monomial (x - x0) α *R c2 α)) has_sum (- G x)) ra_idx"
    using G1 by (simp add: has_sum_uminus)
  have "((λα. ra_monomial (x - x0) α *R c1 α + (- (ra_monomial (x - x0) α *R c2 α)))
            has_sum (F x + (- G x))) ra_idx"
    by (rule has_sum_add[OF F1 G1'])
  thus "((λα. ra_monomial (x - x0) α *R (c1 α - c2 α)) has_sum (F x - G x)) ra_idx"
    by (simp only: scaleR_diff_right, simp)
qed

text ‹From a vector series on a ball, the scalar component has a series on the
  same ball.›

lemma ra_series_on_component:
  fixes cf :: "('a::euclidean_space ⇒ nat) ⇒ 'b::euclidean_space"
  assumes HS: "⋀x. dist x x0 < r ⟹
              ((λα. ra_monomial (x - x0) α *R cf α) has_sum f x) ra_idx"
  shows "ra_series_on x0 r (λα. cf α ∙ b) (λx. f x ∙ b)"
  unfolding ra_series_on_def
proof (intro allI impI)
  fix x :: 'a assume d: "dist x x0 < r"
  have bl: "bounded_linear (λy::'b. y ∙ b)" by (rule bounded_linear_inner_left)
  have "((λα. (ra_monomial (x - x0) α *R cf α) ∙ b) has_sum (f x ∙ b)) ra_idx"
    by (rule has_sum_bounded_linear[OF bl HS[OF d]])
  thus "((λα. ra_monomial (x - x0) α *R (cf α ∙ b)) has_sum (f x ∙ b)) ra_idx"
    by (simp only: inner_scaleR_left scaleR_conv_of_real) simp
qed

text ‹A series for ‹x ↦ ra_monomial (f x - z) β› on the same ball as the series of ‹f›.›

lemma ra_series_on_ra_monomial_compose:
  fixes cf :: "('a::euclidean_space ⇒ nat) ⇒ 'b::euclidean_space"
  assumes HS: "⋀x. dist x x0 < r ⟹
              ((λα. ra_monomial (x - x0) α *R cf α) has_sum f x) ra_idx"
  shows "∃cc. ra_series_on x0 r cc (λx. ra_monomial (f x - z) β)"
proof -
  have comp: "∃c. ra_series_on x0 r c (λx. ((f x - z) ∙ b) ^ (β b))" if "b ∈ Basis" for b
  proof -
    have s1: "ra_series_on x0 r (λα. cf α ∙ b) (λx. f x ∙ b)"
      by (rule ra_series_on_component[OF HS])
    have s2: "ra_series_on x0 r (λα. (z ∙ b) * ra_coeff_one α) (λ_. z ∙ b)"
      by (rule ra_series_on_const)
    have "ra_series_on x0 r (λα. (cf α ∙ b) - (z ∙ b) * ra_coeff_one α) (λx. (f x ∙ b) - (z ∙ b))"
      by (rule ra_series_on_diff[OF s1 s2])
    hence "ra_series_on x0 r (λα. (cf α ∙ b) - (z ∙ b) * ra_coeff_one α) (λx. (f x - z) ∙ b)"
      by (simp only: inner_diff_left)
    hence "ra_series_on x0 r (ra_cauchy_pow (λα. (cf α ∙ b) - (z ∙ b) * ra_coeff_one α) (β b))
              (λx. ((f x - z) ∙ b) ^ (β b))"
      by (rule ra_series_on_power)
    thus ?thesis by blast
  qed
  have "∃cc. ra_series_on x0 r cc (λx. ∏b∈Basis. ((f x - z) ∙ b) ^ (β b))"
    by (rule ra_series_on_prod[OF finite_Basis]) (use comp in simp)
  then obtain cc where "ra_series_on x0 r cc (λx. ∏b∈Basis. ((f x - z) ∙ b) ^ (β b))"
    by blast
  hence "ra_series_on x0 r cc (λx. ra_monomial (f x - z) β)"
    by (simp only: ra_monomial_def)
  thus ?thesis by blast
qed

subsection ‹Majorant norm: submultiplicative under the Cauchy product›

definition ra_weighted_abs :: "real ⇒ (('a::euclidean_space⇒nat)⇒real) ⇒ (('a⇒nat)⇒real)" where
  "ra_weighted_abs σ u = (λα. ¦u α¦ * σ ^ ra_deg α)"

definition ra_majorized :: "real ⇒ (('a::euclidean_space⇒nat)⇒real) ⇒ real ⇒ bool" where
  "ra_majorized σ u K ⟷ (ra_weighted_abs σ u summable_on ra_idx) ∧ (∑∞α∈ra_idx. ra_weighted_abs σ u α) ≤ K"

lemma ra_weighted_abs_nonneg: "0 ≤ σ ⟹ 0 ≤ ra_weighted_abs σ u α"
  by (simp add: ra_weighted_abs_def)

lemma ra_majorized_imp_nonneg_sum:
  assumes "0 ≤ σ" "ra_majorized σ u K"
  shows "0 ≤ (∑∞α∈ra_idx. ra_weighted_abs σ u α)"
  using assms by (auto intro!: infsum_nonneg simp: ra_weighted_abs_nonneg)

lemma ra_majorized_one:
  assumes "0 ≤ σ"
  shows "ra_majorized σ ra_coeff_one 1"
proof -
  have hs': "(ra_weighted_abs σ ra_coeff_one has_sum 1) ra_idx"
  proof (rule has_sum_finite_neutralI)
    show "finite {ra_idx_zero}"
      by simp
    show "{ra_idx_zero} ⊆ ra_idx"
      using ra_idx_zero_in by simp   
    show "(1::real) = (∑α∈{ra_idx_zero}. ra_weighted_abs σ ra_coeff_one α)"
      by (simp add: ra_weighted_abs_def ra_coeff_one_def ra_deg_def ra_idx_zero_def)
    show "⋀x. x ∈ ra_idx - {ra_idx_zero} ⟹ ra_weighted_abs σ Real_Analytic.ra_coeff_one x = 0"
      by (simp add: Real_Analytic.ra_coeff_one_def ra_weighted_abs_def)
  qed
  have s: "ra_weighted_abs σ ra_coeff_one summable_on ra_idx"
    using hs' has_sum_imp_summable by blast
  have i: "(∑∞α∈ra_idx. ra_weighted_abs σ ra_coeff_one α) = 1"
    by (rule infsumI[OF hs'])
  show ?thesis
    by (simp add: i ra_majorized_def s)
qed

text ‹Convolution of two nonnegative summable families: the inner-low finite sums
  sum (over ‹ra_idx›) to the product of the totals.›

lemma cauchy_abs_prod:
  fixes u' v' :: "('a::euclidean_space ⇒ nat) ⇒ real"
  assumes Uhs: "(u' has_sum Uv) ra_idx" and Vhs: "(v' has_sum Vv) ra_idx"
  shows "((λγ. ∑α∈ra_idx_below γ. u' α * v' (ra_idx_diff γ α)) has_sum (Uv * Vv)) ra_idx"
proof -
  have prodHS: "((λ(α,β). u' α * v' β) has_sum (Uv * Vv)) (ra_idx × ra_idx)"
    using prod_has_sum[OF Uhs Vhs] .
  have reix: "((λ(γ,α). u' α * v' (ra_idx_diff γ α)) has_sum (Uv * Vv)) (Sigma ra_idx ra_idx_below)"
  proof -
    have "((λ(α,β). u' α * v' β) has_sum (Uv * Vv)) (ra_idx × ra_idx)
          = ((λ(γ,α). u' α * v' (ra_idx_diff γ α)) has_sum (Uv * Vv)) (Sigma ra_idx ra_idx_below)"
    proof (rule has_sum_reindex_bij_witness
             [where j = "λ(α,β). (ra_idx_add α β, α)" and i = "λ(γ,α). (α, ra_idx_diff γ α)"])
      fix p :: "('a⇒nat) × ('a⇒nat)"
      assume "p ∈ ra_idx × ra_idx"
      obtain α β where p: "p = (α,β)" by (cases p)
      show "(case (case p of (α,β) ⇒ (ra_idx_add α β, α)) of (γ,α) ⇒ (α, ra_idx_diff γ α)) = p"
        by (simp add: p ra_idx_diff_def ra_idx_add_def)
    next
      fix p :: "('a⇒nat) × ('a⇒nat)"
      assume P: "p ∈ ra_idx × ra_idx"
      obtain α β where p: "p = (α,β)" by (cases p)
      have aα: "α ∈ ra_idx" and aβ: "β ∈ ra_idx" using P p by auto
      have m1: "ra_idx_add α β ∈ ra_idx" by (rule idx_add[OF aα aβ])
      have m2: "α ∈ ra_idx_below (ra_idx_add α β)" using aα by (simp add: ra_idx_below_def ra_idx_le_def ra_idx_add_def)
      show "(case p of (α,β) ⇒ (ra_idx_add α β, α)) ∈ Sigma ra_idx ra_idx_below"
        using m1 m2 by (simp add: p)
    next
      fix q :: "('a⇒nat) × ('a⇒nat)"
      assume Q: "q ∈ Sigma ra_idx ra_idx_below"
      obtain γ α where q: "q = (γ,α)" by (cases q)
      have gγ: "γ ∈ ra_idx" and l: "ra_idx_le α γ" using Q q by (auto simp: ra_idx_below_def)
      have "ra_idx_add α (ra_idx_diff γ α) = γ"
      proof (rule ext)
        fix b have "α b ≤ γ b" using l by (simp only: ra_idx_le_def)
        thus "ra_idx_add α (ra_idx_diff γ α) b = γ b" by (simp only: ra_idx_add_def ra_idx_diff_def)
      qed
      then show "(case (case q of (γ,α) ⇒ (α, ra_idx_diff γ α)) of (α,β) ⇒ (ra_idx_add α β, α)) = q"
        by (simp add: q)
    next
      fix q :: "('a⇒nat) × ('a⇒nat)"
      assume Q: "q ∈ Sigma ra_idx ra_idx_below"
      obtain γ α where q: "q = (γ,α)" by (cases q)
      have gγ: "γ ∈ ra_idx" and aα: "α ∈ ra_idx" using Q q by (auto simp: ra_idx_below_def)
      show "(case q of (γ,α) ⇒ (α, ra_idx_diff γ α)) ∈ ra_idx × ra_idx"
        by (simp add: q aα idx_sub[OF gγ])
    next
      fix p :: "('a⇒nat) × ('a⇒nat)"
      assume P: "p ∈ ra_idx × ra_idx"
      obtain α β where p: "p = (α,β)" by (cases p)
      have sub_eq: "ra_idx_diff (ra_idx_add α β) α = β" by (simp add: ra_idx_add_def ra_idx_diff_def)
      show "(case (case p of (α,β) ⇒ (ra_idx_add α β, α)) of (γ,α) ⇒ u' α * v' (ra_idx_diff γ α))
                 = (case p of (α,β) ⇒ u' α * v' β)"
        by (simp add: p sub_eq)
    qed simp
    with prodHS show ?thesis by simp
  qed
  show ?thesis
  proof (rule has_sum_Sigma'
           [where f = "λ(γ,α). u' α * v' (ra_idx_diff γ α)" and A = ra_idx and B = ra_idx_below
              and a = "Uv * Vv" and b = "λγ. ∑α∈ra_idx_below γ. u' α * v' (ra_idx_diff γ α)"])
    show "((λ(γ,α). u' α * v' (ra_idx_diff γ α)) has_sum (Uv * Vv)) (Sigma ra_idx ra_idx_below)"
      by (rule reix)
  next
    fix γ :: "'a ⇒ nat" assume g: "γ ∈ ra_idx"
    have fin: "finite (ra_idx_below γ)" by (rule idx_lower_fin)
    have "((λα. u' α * v' (ra_idx_diff γ α)) has_sum
             (∑α∈ra_idx_below γ. u' α * v' (ra_idx_diff γ α))) (ra_idx_below γ)"
      by (rule has_sum_finite[OF fin])
    thus "((λα. (λ(γ,α). u' α * v' (ra_idx_diff γ α)) (γ, α)) has_sum
              (∑α∈ra_idx_below γ. u' α * v' (ra_idx_diff γ α))) (ra_idx_below γ)" by simp
  qed
qed

text ‹Degree is additive across the Cauchy split.›

lemma ra_deg_split:
  fixes γ :: "'a::euclidean_space ⇒ nat"
  assumes "α ∈ ra_idx_below γ"
  shows "ra_deg γ = ra_deg α + ra_deg (ra_idx_diff γ α)"
proof -
  have l: "ra_idx_le α γ" using assms by (simp add: ra_idx_below_def)
  have "ra_idx_add α (ra_idx_diff γ α) = γ"
  proof (rule ext)
    fix b have "α b ≤ γ b" using l by (simp only: ra_idx_le_def)
    thus "ra_idx_add α (ra_idx_diff γ α) b = γ b" by (simp only: ra_idx_add_def ra_idx_diff_def)
  qed
  hence "ra_deg γ = ra_deg (ra_idx_add α (ra_idx_diff γ α))" by simp
  also have "… = ra_deg α + ra_deg (ra_idx_diff γ α)"
    by (simp only: ra_deg_def ra_idx_add_def sum.distrib)
  finally show ?thesis .
qed

text ‹The key submultiplicativity estimate.›

lemma ra_majorized_cauchy_prod:
  fixes u v :: "('a::euclidean_space ⇒ nat) ⇒ real"
  assumes s0: "0 ≤ σ"
    and U: "ra_majorized σ u KU" and V: "ra_majorized σ v KV"
    and KUnn: "0 ≤ KU" and KVnn: "0 ≤ KV"
  shows "ra_majorized σ (ra_cauchy_prod u v) (KU * KV)"
proof -
  define u' where "u' = ra_weighted_abs σ u"
  define v' where "v' = ra_weighted_abs σ v"
  have u'nn: "0 ≤ u' α" for α using s0 by (simp add: u'_def ra_weighted_abs_nonneg)
  have v'nn: "0 ≤ v' α" for α using s0 by (simp add: v'_def ra_weighted_abs_nonneg)
  have u'_sum: "u' summable_on ra_idx" using U by (simp add: ra_majorized_def u'_def)
  have v'_sum: "v' summable_on ra_idx" using V by (simp add: ra_majorized_def v'_def)
  define Uv where "Uv = (∑∞α∈ra_idx. u' α)"
  define Vv where "Vv = (∑∞β∈ra_idx. v' β)"
  have Uhs: "(u' has_sum Uv) ra_idx" using u'_sum by (simp add: Uv_def)
  have Vhs: "(v' has_sum Vv) ra_idx" using v'_sum by (simp add: Vv_def)
  have UvKU: "Uv ≤ KU" using U by (simp add: ra_majorized_def u'_def Uv_def)
  have VvKV: "Vv ≤ KV" using V by (simp add: ra_majorized_def v'_def Vv_def)
  have Uvnn: "0 ≤ Uv" unfolding Uv_def using u'nn u'_sum by (auto intro!: infsum_nonneg)
  have Vvnn: "0 ≤ Vv" unfolding Vv_def using v'nn v'_sum by (auto intro!: infsum_nonneg)
  define S where "S = (λγ. ∑α∈ra_idx_below γ. u' α * v' (ra_idx_diff γ α))"
  have collapse: "(S has_sum (Uv * Vv)) ra_idx"
    unfolding S_def by (rule cauchy_abs_prod[OF Uhs Vhs])
  have collapse_sum: "S summable_on ra_idx"
    using collapse has_sum_imp_summable by blast
  have Snn: "0 ≤ S γ" for γ
    unfolding S_def by (intro sum_nonneg mult_nonneg_nonneg u'nn v'nn)
  ― ‹each Cauchy coefficient majorant term is bounded by the inner-low sum›
  have termbound: "ra_weighted_abs σ (ra_cauchy_prod u v) γ ≤ S γ" if g: "γ ∈ ra_idx" for γ
  proof -
    have fin: "finite (ra_idx_below γ)" by (rule idx_lower_fin)
    have termeq: "¦u α * v (ra_idx_diff γ α)¦ * σ ^ ra_deg γ = u' α * v' (ra_idx_diff γ α)"
      if a: "α ∈ ra_idx_below γ" for α
    proof -
      have dd: "σ ^ ra_deg γ = σ ^ ra_deg α * σ ^ ra_deg (ra_idx_diff γ α)"
        by (simp only: ra_deg_split[OF a] power_add)
      have "¦u α * v (ra_idx_diff γ α)¦ * σ ^ ra_deg γ
              = (¦u α¦ * ¦v (ra_idx_diff γ α)¦) * (σ ^ ra_deg α * σ ^ ra_deg (ra_idx_diff γ α))"
        by (simp only: abs_mult dd)
      also have "… = (¦u α¦ * σ ^ ra_deg α) * (¦v (ra_idx_diff γ α)¦ * σ ^ ra_deg (ra_idx_diff γ α))"
        by (simp only: mult.assoc mult.left_commute)
      finally show ?thesis by (simp only: u'_def v'_def ra_weighted_abs_def)
    qed
    have "ra_weighted_abs σ (ra_cauchy_prod u v) γ = ¦∑α∈ra_idx_below γ. u α * v (ra_idx_diff γ α)¦ * σ ^ ra_deg γ"
      by (simp only: ra_weighted_abs_def ra_cauchy_prod_def)
    also have "… ≤ (∑α∈ra_idx_below γ. ¦u α * v (ra_idx_diff γ α)¦) * σ ^ ra_deg γ"
      by (rule mult_right_mono[OF sum_abs]) (use s0 in simp)
    also have "… = (∑α∈ra_idx_below γ. ¦u α * v (ra_idx_diff γ α)¦ * σ ^ ra_deg γ)"
      by (simp only: sum_distrib_right)
    also have "… = (∑α∈ra_idx_below γ. u' α * v' (ra_idx_diff γ α))"
      by (rule sum.cong[OF refl termeq])
    also have "… = S γ" by (simp only: S_def)
    finally show ?thesis .
  qed
  have cnn: "0 ≤ ra_weighted_abs σ (ra_cauchy_prod u v) γ" for γ using s0 by (simp add: ra_weighted_abs_nonneg)
  have cmaj_sum: "ra_weighted_abs σ (ra_cauchy_prod u v) summable_on ra_idx"
  proof (rule summable_on_comparison_test[where f = S and g = "ra_weighted_abs σ (ra_cauchy_prod u v)"])
    show "S summable_on ra_idx" by (rule collapse_sum)
  next
    fix γ :: "'a ⇒ nat" assume g: "γ ∈ ra_idx"
    show "ra_weighted_abs σ (ra_cauchy_prod u v) γ ≤ S γ" by (rule termbound[OF g])
  next
    fix γ :: "'a ⇒ nat" assume "γ ∈ ra_idx"
    show "0 ≤ ra_weighted_abs σ (ra_cauchy_prod u v) γ" by (rule cnn)
  qed
  have "(∑∞γ∈ra_idx. ra_weighted_abs σ (ra_cauchy_prod u v) γ) ≤ (∑∞γ∈ra_idx. S γ)"
    by (rule infsum_mono[OF cmaj_sum collapse_sum]) (rule termbound)
  also have "(∑∞γ∈ra_idx. S γ) = Uv * Vv" by (rule infsumI[OF collapse])
  also have "Uv * Vv ≤ KU * KV"
    using UvKU VvKV Uvnn Vvnn KVnn by (intro mult_mono) auto
  finally have "(∑∞γ∈ra_idx. ra_weighted_abs σ (ra_cauchy_prod u v) γ) ≤ KU * KV" .
  thus ?thesis using cmaj_sum by (simp add: ra_majorized_def)
qed


text ‹Difference of analytic functions is analytic.›

lemma real_analytic_on_diff:
  fixes f g :: "'a::euclidean_space ⇒ 'b::real_normed_vector"
  assumes F: "real_analytic_on f U" and G: "real_analytic_on g U"
  shows "real_analytic_on (λx. f x - g x) U"
proof -
  have "real_analytic_on (λx. (-1) *R g x) U"
    by (rule real_analytic_on_scaleR[OF G])
  hence "real_analytic_on (λx. f x + (-1) *R g x) U"
    by (rule real_analytic_on_add[OF F])
  thus ?thesis by simp
qed

text ‹A natural power of an analytic scalar function is analytic.›

lemma real_analytic_on_power:
  fixes f :: "'a::euclidean_space ⇒ real"
  assumes F: "real_analytic_on f U"
  shows "real_analytic_on (λx. (f x) ^ n) U"
proof (induction n)
  case 0
  from F have U: "open U" by (simp only: real_analytic_on_def)
  show ?case using real_analytic_on_const[OF U, of "1::real"] by simp
next
  case (Suc n)
  have "real_analytic_on (λx. f x * (f x) ^ n) U"
    by (rule real_analytic_on_mult[OF F Suc.IH])
  thus ?case by simp
qed

text ‹A finite product of analytic scalar functions is analytic.›

lemma real_analytic_on_prod:
  fixes f :: "'i ⇒ 'a::euclidean_space ⇒ real"
  assumes U: "open U"
    and F: "⋀i. i ∈ I ⟹ real_analytic_on (f i) U"
  shows "real_analytic_on (λx. ∏i∈I. f i x) U"
  using F
proof (induction I rule: infinite_finite_induct)
  case (infinite I)
  have "(λx. ∏i∈I. f i x) = (λx. 1)" using infinite.hyps by simp
  thus ?case using real_analytic_on_const[OF U, of "1::real"] by simp
next
  case empty
  show ?case using real_analytic_on_const[OF U, of "1::real"] by simp
next
  case (insert j I)
  have aj: "real_analytic_on (f j) U" using insert.prems by simp
  have arest: "real_analytic_on (λx. ∏i∈I. f i x) U"
    using insert.prems by (intro insert.IH) simp
  have "real_analytic_on (λx. f j x * (∏i∈I. f i x)) U"
    by (rule real_analytic_on_mult[OF aj arest])
  thus ?case using insert.hyps by simp
qed

text ‹The basis-monomial of an analytic vector function (in the shifted argument)
  is analytic: ‹x ↦ ra_monomial (f x - z) β›.›

lemma real_analytic_on_ra_monomial_compose:
  fixes f :: "'a::euclidean_space ⇒ 'b::euclidean_space"
  assumes F: "real_analytic_on f U"
  shows "real_analytic_on (λx. ra_monomial (f x - z) β) U"
proof -
  from F have U: "open U" by (simp only: real_analytic_on_def)
  have comp: "real_analytic_on (λx. ((f x - z) ∙ b) ^ (β b)) U" if "b ∈ Basis" for b
  proof -
    have "real_analytic_on (λx. f x ∙ b) U"
      by (rule real_analytic_on_inner_component[OF F])
    moreover have "real_analytic_on (λx. z ∙ b) U"
      by (rule real_analytic_on_const[OF U])
    ultimately have "real_analytic_on (λx. (f x ∙ b) - (z ∙ b)) U"
      by (rule real_analytic_on_diff)
    hence "real_analytic_on (λx. (f x - z) ∙ b) U"
      by (simp only: inner_diff_left)
    thus ?thesis by (rule real_analytic_on_power)
  qed
  have "real_analytic_on (λx. ∏b∈Basis. ((f x - z) ∙ b) ^ (β b)) U"
    by (rule real_analytic_on_prod[OF U]) (use comp in simp)
  thus ?thesis by (simp only: ra_monomial_def)
qed

subsection ‹Step (a): power lift of a majorant bound›

lemma ra_majorized_cauchy_pow:
  fixes c :: "('a::euclidean_space⇒nat)⇒real"
  assumes s0: "0 ≤ σ" and K: "ra_majorized σ c K" and Knn: "0 ≤ K"
  shows "ra_majorized σ (ra_cauchy_pow c n) (K^n)"
proof (induction n)
  case 0
  show ?case using ra_majorized_one[OF s0] by (simp add: ra_cauchy_pow_0)
next
  case (Suc n)
  have "ra_majorized σ (ra_cauchy_prod c (ra_cauchy_pow c n)) (K * K^n)"
    by (rule ra_majorized_cauchy_prod[OF s0 K Suc.IH Knn], simp only: Knn zero_le_power)
  thus ?case by (simp add: ra_cauchy_pow_Suc)
qed

subsection ‹Step (b): coefficient bound gives a majorant bound›

text ‹The basis product of a constant base equals the base raised to the degree.›

lemma prod_const_ra_deg:
  fixes t :: real
  shows "(∏b∈(Basis::'a::euclidean_space set). t ^ (α b)) = t ^ ra_deg α"
  by (simp add: ra_deg_def power_sum)

lemma coeff_majorized_of_bound:
  fixes c :: "('b::euclidean_space⇒nat)⇒'d::real_normed_vector"
  assumes Mnn: "0 ≤ M"
    and bound: "⋀α. α∈ra_idx ⟹ norm (c α) ≤ M / t ^ (ra_deg α)"
    and t: "0 < t" and s0: "0 ≤ σ" and st: "σ < t"
  shows "ra_majorized σ (λα. norm (c α)) (M * (∑∞α∈(ra_idx::('b⇒nat) set). (σ/t) ^ ra_deg α))"
proof -
  define q where "q = σ/t"
  have q0: "0 ≤ q" using s0 t by (simp add: q_def)
  have q1: "q < 1" using st t by (simp add: q_def)
  have geomS: "(λα::'b⇒nat. q ^ ra_deg α) summable_on ra_idx"
    by (rule geom_idx_summable[OF q0 q1])
  have geomMS: "(λα. M * q ^ ra_deg α) summable_on (ra_idx::('b⇒nat) set)"
    by (rule summable_on_cmult_right[OF geomS])
  ― ‹pointwise domination of the majorant family by the geometric majorant›
  have ptwise: "ra_weighted_abs σ (λα. norm (c α)) α ≤ M * q ^ ra_deg α" if a: "α∈ra_idx" for α
  proof -
    have tpow: "0 < t ^ ra_deg α" using t by simp
    have "ra_weighted_abs σ (λα. norm (c α)) α = norm (c α) * σ ^ ra_deg α"
      by (simp add: ra_weighted_abs_def)
    also have "… ≤ (M / t ^ ra_deg α) * σ ^ ra_deg α"
      by (rule mult_right_mono[OF bound[OF a]]) (use s0 in simp)
    also have "… = M * (σ ^ ra_deg α / t ^ ra_deg α)" by simp
    also have "… = M * q ^ ra_deg α"
      by (simp add: q_def power_divide)
    finally show ?thesis .
  qed
  have nn: "0 ≤ ra_weighted_abs σ (λα. norm (c α)) α" for α using s0 by (rule ra_weighted_abs_nonneg)
  have summ: "ra_weighted_abs σ (λα. norm (c α)) summable_on ra_idx"
    by (rule summable_on_comparison_test[OF geomMS]) (use ptwise nn in auto)
  have "(∑∞α∈ra_idx. ra_weighted_abs σ (λα. norm (c α)) α)
          ≤ (∑∞α∈(ra_idx::('b⇒nat) set). M * q ^ ra_deg α)"
    by (rule infsum_mono[OF summ geomMS]) (rule ptwise)
  also have "… = M * (∑∞α∈(ra_idx::('b⇒nat) set). q ^ ra_deg α)"
    by (rule infsum_cmult_right[OF geomS])
  finally show ?thesis using summ by (simp add: ra_majorized_def q_def)
qed

subsection ‹Majle helpers: domination, scaling, triangle›

text ‹If ‹|c1 α| ≤ |c2 α|› pointwise and ‹c2› is majorized, so is ‹c1›.›

lemma ra_majorized_dom:
  fixes c1 c2 :: "('a::euclidean_space⇒nat)⇒real"
  assumes s0: "0 ≤ σ"
    and dom: "⋀α. α∈ra_idx ⟹ ¦c1 α¦ ≤ ¦c2 α¦"
    and B: "ra_majorized σ c2 K"
  shows "ra_majorized σ c1 K"
proof -
  have c2sum: "ra_weighted_abs σ c2 summable_on ra_idx" using B by (simp add: ra_majorized_def)
  have ptw: "ra_weighted_abs σ c1 α ≤ ra_weighted_abs σ c2 α" if "α∈ra_idx" for α
    unfolding ra_weighted_abs_def using dom[OF that] s0 by (simp add: mult_right_mono)
  have nn: "0 ≤ ra_weighted_abs σ c1 α" for α using s0 by (rule ra_weighted_abs_nonneg)
  have c1sum: "ra_weighted_abs σ c1 summable_on ra_idx"
    by (rule summable_on_comparison_test[OF c2sum]) (use ptw nn in auto)
  have "(∑∞α∈ra_idx. ra_weighted_abs σ c1 α) ≤ (∑∞α∈ra_idx. ra_weighted_abs σ c2 α)"
    by (rule infsum_mono[OF c1sum c2sum]) (rule ptw)
  also have "… ≤ K" using B by (simp add: ra_majorized_def)
  finally show ?thesis using c1sum by (simp add: ra_majorized_def)
qed

lemma ra_majorized_zero:
  "ra_majorized σ ((λ_. 0) :: ('a::euclidean_space ⇒ nat) ⇒ real) 0"
  by (simp add: ra_majorized_def ra_weighted_abs_def)

lemma ra_majorized_cmul:
  fixes c :: "('a::euclidean_space⇒nat)⇒real"
  assumes s0: "0 ≤ σ" and B: "ra_majorized σ c K"
  shows "ra_majorized σ (λα. a * c α) (¦a¦ * K)"
proof -
  have csum: "ra_weighted_abs σ c summable_on ra_idx" using B by (simp add: ra_majorized_def)
  have eq: "ra_weighted_abs σ (λα. a * c α) = (λα. ¦a¦ * ra_weighted_abs σ c α)"
    by (rule ext) (simp add: ra_weighted_abs_def abs_mult)
  have asum: "ra_weighted_abs σ (λα. a * c α) summable_on ra_idx"
    using summable_on_cmult_right[OF csum, where c = "¦a¦"] by (simp add: eq)
  have "(∑∞α∈ra_idx. ra_weighted_abs σ (λα. a * c α) α)
          = (∑∞α∈ra_idx. ¦a¦ * ra_weighted_abs σ c α)" by (simp add: eq[THEN fun_cong])
  also have "… = ¦a¦ * (∑∞α∈ra_idx. ra_weighted_abs σ c α)"
    by (rule infsum_cmult_right[OF csum])
  also have "… ≤ ¦a¦ * K" using B by (simp add: ra_majorized_def mult_left_mono)
  finally show ?thesis using asum by (simp add: ra_majorized_def)
qed

lemma ra_majorized_add:
  fixes c1 c2 :: "('a::euclidean_space⇒nat)⇒real"
  assumes s0: "0 ≤ σ" and A: "ra_majorized σ c1 K1" and B: "ra_majorized σ c2 K2"
  shows "ra_majorized σ (λα. c1 α + c2 α) (K1 + K2)"
proof -
  have c1sum: "ra_weighted_abs σ c1 summable_on ra_idx" using A by (simp add: ra_majorized_def)
  have c2sum: "ra_weighted_abs σ c2 summable_on ra_idx" using B by (simp add: ra_majorized_def)
  have ptw: "ra_weighted_abs σ (λα. c1 α + c2 α) α ≤ ra_weighted_abs σ c1 α + ra_weighted_abs σ c2 α" for α
  proof -
    have "ra_weighted_abs σ (λα. c1 α + c2 α) α = ¦c1 α + c2 α¦ * σ ^ ra_deg α"
      by (simp add: ra_weighted_abs_def)
    also have "… ≤ (¦c1 α¦ + ¦c2 α¦) * σ ^ ra_deg α"
      by (rule mult_right_mono) (use s0 in ‹auto simp: abs_triangle_ineq›)
    also have "… = ra_weighted_abs σ c1 α + ra_weighted_abs σ c2 α"
      by (simp add: ra_weighted_abs_def distrib_right)
    finally show ?thesis .
  qed
  have nn: "0 ≤ ra_weighted_abs σ (λα. c1 α + c2 α) α" for α using s0 by (rule ra_weighted_abs_nonneg)
  have sumadd: "(λα. ra_weighted_abs σ c1 α + ra_weighted_abs σ c2 α) summable_on ra_idx"
    using c1sum c2sum by (rule summable_on_add)
  have addsum: "ra_weighted_abs σ (λα. c1 α + c2 α) summable_on ra_idx"
    by (rule summable_on_comparison_test[OF sumadd]) (use ptw nn in auto)
  have "(∑∞α∈ra_idx. ra_weighted_abs σ (λα. c1 α + c2 α) α)
          ≤ (∑∞α∈ra_idx. ra_weighted_abs σ c1 α + ra_weighted_abs σ c2 α)"
    by (rule infsum_mono[OF addsum sumadd]) (rule ptw)
  also have "… = (∑∞α∈ra_idx. ra_weighted_abs σ c1 α) + (∑∞α∈ra_idx. ra_weighted_abs σ c2 α)"
    by (rule infsum_add[OF c1sum c2sum])
  also have "… ≤ K1 + K2" using A B by (simp add: ra_majorized_def add_mono)
  finally show ?thesis using addsum by (simp add: ra_majorized_def)
qed

lemma ra_majorized_vec_sum:
  fixes c :: "'i ⇒ ('a::euclidean_space ⇒ nat) ⇒ 'b::real_normed_vector"
  assumes s0: "0 ≤ σ"
    and fin: "finite I"
    and maj: "⋀i. i ∈ I ⟹ ra_majorized σ (λα. norm (c i α)) (K i)"
  shows "ra_majorized σ (λα. norm (∑i∈I. c i α)) (∑i∈I. K i)"
  using fin maj
proof (induction I rule: finite_induct)
  case empty
  show ?case
    by (simp add: ra_majorized_zero)
next
  case (insert i I)
  have ci: "ra_majorized σ (λα. norm (c i α)) (K i)"
    using insert.prems by simp
  have cI: "ra_majorized σ (λα. norm (∑j∈I. c j α)) (∑j∈I. K j)"
    using insert.IH insert.prems by blast
  have addmaj: "ra_majorized σ (λα. norm (c i α) + norm (∑j∈I. c j α))
      (K i + (∑j∈I. K j))"
    by (rule ra_majorized_add[OF s0 ci cI])
  have dom: "¦norm (c i α + (∑j∈I. c j α))¦
      ≤ ¦norm (c i α) + norm (∑j∈I. c j α)¦"
    for α :: "'a ⇒ nat"
    by (simp add: norm_triangle_ineq)
  show ?case
    using insert.hyps
    by (simp add: sum.insert[OF insert.hyps])
       (rule ra_majorized_dom[OF s0 _ addmaj], rule dom)
qed

lemma identity_coeff_series_majorized:
  obtains cid :: "('a::euclidean_space ⇒ nat) ⇒ 'a" where
    "⋀y. ((λα. ra_monomial y α *R cid α) has_sum y) ra_idx"
    "⋀σ. 0 ≤ σ ⟹
      ra_majorized σ (λα. norm (cid α)) (real (card (Basis :: 'a set)) * σ)"
proof -
  define e :: "'a ⇒ 'a ⇒ nat" where
    "e = (λb x. if x = b then 1 else 0)"
  define cid :: "('a ⇒ nat) ⇒ 'a" where
    "cid = (λα. ∑b∈Basis. if α = e b then b else 0)"

  have e_idx: "e b ∈ ra_idx" if "b ∈ Basis" for b
    using that by (auto simp: e_def ra_idx_def)

  have e_inj: "inj_on e (Basis :: 'a set)"
  proof (rule inj_onI)
    fix b c :: 'a
    assume b: "b ∈ Basis" and c: "c ∈ Basis" and eq: "e b = e c"
    have h0: "e b b = e c b"
      using eq by simp
    have h: "(1::nat) = (if b = c then 1 else 0)"
      using h0 by (simp add: e_def)
    show "b = c"
    proof (rule ccontr)
      assume "b ≠ c"
      with h show False by simp
    qed
  qed

  have mono_e: "ra_monomial y (e b) = y ∙ b" if b: "b ∈ Basis" for y b
  proof -
    have "ra_monomial y (e b) =
        (y ∙ b) * (∏c∈Basis - {b}. (y ∙ c) ^ (e b c))"
      using b by (simp add: ra_monomial_def sum.remove e_def prod.remove)
    also have "… = y ∙ b"
      by (simp add: e_def)
    finally show ?thesis .
  qed

  have deg_e: "ra_deg (e b) = 1" if b: "b ∈ Basis" for b
    using b by (simp add: ra_deg_def e_def sum.remove[where x = b])

  have cid_e: "cid (e b) = b" if b: "b ∈ Basis" for b
  proof -
    have "cid (e b) = (∑c∈Basis. if c = b then c else 0)"
      unfolding cid_def
      by (rule sum.cong) (use b e_inj in ‹auto simp: inj_on_def›)
    also have "… = b"
      using b by simp
    finally show ?thesis .
  qed

  have cid_zero: "cid α = 0" if "α ∈ ra_idx - image e Basis" for α
    unfolding cid_def
    by (rule sum.neutral) (use that in auto)

  have series: "((λα. ra_monomial y α *R cid α) has_sum y) ra_idx" for y
  proof (rule has_sum_finite_neutralI)
    show "finite (image e (Basis :: 'a set))"
      by simp
    show "image e (Basis :: 'a set) ⊆ ra_idx"
      using e_idx by blast
    have "(∑α∈image e Basis. ra_monomial y α *R cid α) =
        (∑b∈Basis. ra_monomial y (e b) *R cid (e b))"
      by (rule sum.reindex_cong[where l = e, OF e_inj refl]) simp
    also have "… = (∑b∈Basis. (y ∙ b) *R b)"
      by (rule sum.cong) (use mono_e cid_e in auto)
    also have "… = y"
      by (simp add: euclidean_representation)
    finally show "y = (∑α∈image e Basis. ra_monomial y α *R cid α)"
      by simp
    show "⋀α. α ∈ ra_idx - image e Basis ⟹ ra_monomial y α *R cid α = 0"
      by (simp add: cid_zero)
  qed

  have maj: "ra_majorized σ (λα. norm (cid α)) (real (card (Basis :: 'a set)) * σ)"
    if s0: "0 ≤ σ" for σ
  proof -
    have hs: "(ra_weighted_abs σ (λα. norm (cid α)) has_sum
        (real (card (Basis :: 'a set)) * σ)) ra_idx"
    proof (rule has_sum_finite_neutralI)
      show "finite (image e (Basis :: 'a set))"
        by simp
      show "image e (Basis :: 'a set) ⊆ ra_idx"
        using e_idx by blast
      have "(∑α∈image e Basis. ra_weighted_abs σ (λα. norm (cid α)) α) =
          (∑b∈Basis. ra_weighted_abs σ (λα. norm (cid α)) (e b))"
        by (rule sum.reindex_cong[where l = e, OF e_inj refl]) simp
      also have "… = (∑b∈(Basis :: 'a set). σ)"
      proof -
        have term_eq: "ra_weighted_abs σ (λα. norm (cid α)) (e b) = σ"
          if b: "b ∈ (Basis :: 'a set)" for b
        proof -
        have "ra_weighted_abs σ (λα. norm (cid α)) (e b) =
            norm (cid (e b)) * σ ^ ra_deg (e b)"
          by (simp add: ra_weighted_abs_def)
        also have "… = σ"
          using b by (simp add: cid_e deg_e)
          finally show ?thesis .
        qed
        show ?thesis
          by (rule sum.cong[OF refl term_eq])
      qed
      also have "… = real (card (Basis :: 'a set)) * σ"
        by simp
      finally show "real (card (Basis :: 'a set)) * σ =
          (∑α∈image e Basis. ra_weighted_abs σ (λα. norm (cid α)) α)"
        by simp
      show "⋀α. α ∈ ra_idx - image e Basis ⟹
        ra_weighted_abs σ (λα. norm (cid α)) α = 0"
        by (simp add: cid_zero ra_weighted_abs_def)
    qed
    have summ: "ra_weighted_abs σ (λα. norm (cid α)) summable_on ra_idx"
      using hs has_sum_imp_summable by blast
    have inf: "(∑∞α∈ra_idx. ra_weighted_abs σ (λα. norm (cid α)) α) =
        real (card (Basis :: 'a set)) * σ"
      by (rule infsumI[OF hs])
    show ?thesis
      by (simp add: ra_majorized_def summ inf)
  qed

  show ?thesis
    by (rule that[OF series maj])
qed

subsection ‹Combined series and majorant bookkeeping›

text ‹‹c› is the coefficient family of ‹F› on the ball and is majorized by ‹K›.›

definition ra_series_majorized ::
  "'a::euclidean_space ⇒ real ⇒ real ⇒ (('a⇒nat)⇒real) ⇒ ('a⇒real) ⇒ real ⇒ bool" where
  "ra_series_majorized x0 r σ c F K ⟷ ra_series_on x0 r c F ∧ ra_majorized σ c K"

lemma ra_series_majorized_mono_radius:
  assumes "ra_series_majorized x0 r σ c F K" and "r' ≤ r"
  shows "ra_series_majorized x0 r' σ c F K"
  using assms ra_series_on_mono_radius unfolding ra_series_majorized_def by blast

lemma ra_series_majorized_one:
  assumes "0 ≤ σ"
  shows "ra_series_majorized x0 r σ ra_coeff_one (λ_. 1) 1"
  unfolding ra_series_majorized_def using ra_series_on_one ra_majorized_one[OF assms] by blast

lemma ra_series_majorized_mult:
  assumes s0: "0 ≤ σ"
    and A: "ra_series_majorized x0 r σ c1 F K1" and B: "ra_series_majorized x0 r σ c2 G K2"
    and K1nn: "0 ≤ K1" and K2nn: "0 ≤ K2"
  shows "ra_series_majorized x0 r σ (ra_cauchy_prod c1 c2) (λx. F x * G x) (K1 * K2)"
  unfolding ra_series_majorized_def
proof
  show "ra_series_on x0 r (ra_cauchy_prod c1 c2) (λx. F x * G x)"
    using A B by (auto simp: ra_series_majorized_def intro: ra_series_on_mult)
  show "ra_majorized σ (ra_cauchy_prod c1 c2) (K1 * K2)"
    using A B by (auto simp: ra_series_majorized_def intro: ra_majorized_cauchy_prod[OF s0 _ _ K1nn K2nn])
qed

lemma ra_series_majorized_pow:
  assumes s0: "0 ≤ σ"
    and A: "ra_series_majorized x0 r σ c F K" and Knn: "0 ≤ K"
  shows "ra_series_majorized x0 r σ (ra_cauchy_pow c n) (λx. (F x) ^ n) (K ^ n)"
  unfolding ra_series_majorized_def
proof
  show "ra_series_on x0 r (ra_cauchy_pow c n) (λx. (F x) ^ n)"
    using A by (auto simp: ra_series_majorized_def intro: ra_series_on_power)
  show "ra_majorized σ (ra_cauchy_pow c n) (K ^ n)"
    using A by (auto simp: ra_series_majorized_def intro: ra_majorized_cauchy_pow[OF s0 _ Knn])
qed

text ‹Finite products, with the product of the majorant bounds.›

lemma ra_series_majorized_prod:
  fixes F :: "'i ⇒ 'a::euclidean_space ⇒ real"
  assumes s0: "0 ≤ σ" and fin: "finite I"
    and per: "⋀i. i ∈ I ⟹ ∃c. ra_series_majorized x0 r σ c (F i) (K i)"
    and Knn: "⋀i. i ∈ I ⟹ 0 ≤ K i"
  shows "∃cc. ra_series_majorized x0 r σ cc (λx. ∏i∈I. F i x) (∏i∈I. K i)"
  using fin per Knn
proof (induction I rule: finite_induct)
  case empty
  have "ra_series_majorized x0 r σ ra_coeff_one (λx. ∏i∈{}. F i x) (∏i∈{}. K i)"
    using ra_series_majorized_one[OF s0] by simp
  thus ?case by blast
next
  case (insert j I)
  obtain cj where cj: "ra_series_majorized x0 r σ cj (F j) (K j)" using insert.prems(1)[of j] by blast
  obtain crest where crest: "ra_series_majorized x0 r σ crest (λx. ∏i∈I. F i x) (∏i∈I. K i)"
    using insert.prems insert.IH by blast
  have Kjnn: "0 ≤ K j" using insert.prems(2)[of j] by simp
  have Krestnn: "0 ≤ (∏i∈I. K i)" using insert.prems(2) by (intro prod_nonneg) auto
  have "ra_series_majorized x0 r σ (ra_cauchy_prod cj crest) (λx. F j x * (∏i∈I. F i x)) (K j * (∏i∈I. K i))"
    by (rule ra_series_majorized_mult[OF s0 cj crest Kjnn Krestnn])
  hence "ra_series_majorized x0 r σ (ra_cauchy_prod cj crest) (λx. ∏i∈insert j I. F i x) (∏i∈insert j I. K i)"
    using insert.hyps by simp
  thus ?case by blast
qed

lemma ra_majorized_mono:
  assumes "ra_majorized σ c K" and "K ≤ K'"
  shows "ra_majorized σ c K'"
  using assms by (simp add: ra_majorized_def)

lemma ra_series_majorized_relax:
  assumes "ra_series_majorized x0 r σ c F K" and "K ≤ K'"
  shows "ra_series_majorized x0 r σ c F K'"
  using assms ra_majorized_mono by (auto simp: ra_series_majorized_def)

text ‹Product of a base raised to multi-index entries, dominated by a common base
  raised to the total degree.›

lemma prod_pow_le_deg:
  fixes Kb :: "'a::euclidean_space ⇒ real"
  assumes nn: "⋀b. b ∈ Basis ⟹ 0 ≤ Kb b"
    and le: "⋀b. b ∈ Basis ⟹ Kb b ≤ Kf"
  shows "(∏b∈Basis. (Kb b) ^ (β b)) ≤ Kf ^ ra_deg β"
proof -
  have "(∏b∈(Basis::'a set). (Kb b) ^ (β b)) ≤ (∏b∈(Basis::'a set). Kf ^ (β b))"
  proof (rule prod_mono)
    fix b assume b: "b ∈ (Basis::'a set)"
    have "0 ≤ (Kb b) ^ (β b)" using nn[OF b] by simp
    moreover have "(Kb b) ^ (β b) ≤ Kf ^ (β b)"
      by (rule power_mono[OF le[OF b] nn[OF b]])
    ultimately show "0 ≤ (Kb b) ^ (β b) ∧ (Kb b) ^ (β b) ≤ Kf ^ (β b)" by simp
  qed
  also have "(∏b∈(Basis::'a set). Kf ^ (β b)) = Kf ^ ra_deg β"
    by (rule prod_const_ra_deg)
  finally show ?thesis .
qed

text ‹The coefficient family of ‹x ↦ ra_monomial (f x - z) β›, with the majorant bound
  ‹Kf ^ ra_deg β› uniformly in ‹β›.›

lemma ra_series_majorized_ra_monomial_compose:
  fixes cf :: "('a::euclidean_space ⇒ nat) ⇒ 'b::euclidean_space"
  assumes s0: "0 ≤ σ"
    and HS: "⋀x. dist x x0 < r ⟹
              ((λα. ra_monomial (x - x0) α *R cf α) has_sum f x) ra_idx"
    and cfmaj: "ra_majorized σ (λα. norm (cf α)) Mc"
    and Kf_ge: "⋀b. b ∈ Basis ⟹ Mc + ¦z ∙ b¦ ≤ Kf"
    and Kf_nn: "0 ≤ Kf"
  shows "∃cc. ra_series_majorized x0 r σ cc (λx. ra_monomial (f x - z) β) (Kf ^ ra_deg β)"
proof -
  define Kb where "Kb = (λb. Mc + ¦z ∙ b¦)"
  have Mc_nn: "0 ≤ Mc" using ra_majorized_imp_nonneg_sum[OF s0 cfmaj] cfmaj by (simp add: ra_majorized_def)
  have Kbnn: "0 ≤ Kb b" for b using Mc_nn by (simp add: Kb_def)
  have Kble: "Kb b ≤ Kf" if "b ∈ Basis" for b using Kf_ge[OF that] by (simp add: Kb_def)
  ― ‹per-component @{const ra_series_majorized} for ‹(f x - z) ∙ b››
  have comp: "ra_series_majorized x0 r σ (λα. (cf α ∙ b) - (z ∙ b) * ra_coeff_one α)
                 (λx. (f x - z) ∙ b) (Kb b)" if b: "b ∈ Basis" for b
    unfolding ra_series_majorized_def
  proof
    have s1: "ra_series_on x0 r (λα. cf α ∙ b) (λx. f x ∙ b)"
      by (rule ra_series_on_component[OF HS])
    have s2: "ra_series_on x0 r (λα. (z ∙ b) * ra_coeff_one α) (λ_. z ∙ b)"
      by (rule ra_series_on_const)
    have "ra_series_on x0 r (λα. (cf α ∙ b) - (z ∙ b) * ra_coeff_one α) (λx. (f x ∙ b) - (z ∙ b))"
      by (rule ra_series_on_diff[OF s1 s2])
    thus "ra_series_on x0 r (λα. (cf α ∙ b) - (z ∙ b) * ra_coeff_one α) (λx. (f x - z) ∙ b)"
      by (simp only: inner_diff_left)
  next
    have comp_dom: "¦cf α ∙ b¦ ≤ ¦norm (cf α)¦" if "α∈ra_idx" for α
    proof -
      have "¦cf α ∙ b¦ ≤ norm (cf α) * norm b"
        by (rule Cauchy_Schwarz_ineq2)
      also have "… = norm (cf α)" using b by simp
      finally show ?thesis by simp
    qed
    have m1: "ra_majorized σ (λα. cf α ∙ b) Mc"
      by (rule ra_majorized_dom[OF s0 comp_dom cfmaj])
    have m2: "ra_majorized σ (λα. (z ∙ b) * ra_coeff_one α) (¦z ∙ b¦ * 1)"
      by (rule ra_majorized_cmul[OF s0 ra_majorized_one[OF s0]])
    have m2': "ra_majorized σ (λα. (z ∙ b) * ra_coeff_one α) (¦z ∙ b¦)" using m2 by simp
    have m2neg: "ra_majorized σ (λα. - ((z ∙ b) * ra_coeff_one α)) (¦z ∙ b¦)"
    proof -
      have "ra_majorized σ (λα. (-1) * ((z ∙ b) * ra_coeff_one α)) (¦-1::real¦ * ¦z ∙ b¦)"
        by (rule ra_majorized_cmul[OF s0 m2'])
      thus ?thesis by simp
    qed
    have "ra_majorized σ (λα. (cf α ∙ b) + (- ((z ∙ b) * ra_coeff_one α))) (Mc + ¦z ∙ b¦)"
      by (rule ra_majorized_add[OF s0 m1 m2neg])
    thus "ra_majorized σ (λα. (cf α ∙ b) - (z ∙ b) * ra_coeff_one α) (Kb b)"
      by (simp add: Kb_def)
  qed
  have comppow: "∃c. ra_series_majorized x0 r σ c (λx. ((f x - z) ∙ b) ^ (β b)) (Kb b ^ (β b))"
    if b: "b ∈ Basis" for b
    using ra_series_majorized_pow[OF s0 comp[OF b] Kbnn] by blast
  have "∃cc. ra_series_majorized x0 r σ cc (λx. ∏b∈Basis. ((f x - z) ∙ b) ^ (β b))
              (∏b∈Basis. Kb b ^ (β b))"
    by (rule ra_series_majorized_prod[OF s0 finite_Basis])
       (use comppow Kbnn in ‹auto intro: zero_le_power›)
  then obtain cc where cc: "ra_series_majorized x0 r σ cc (λx. ∏b∈Basis. ((f x - z) ∙ b) ^ (β b))
              (∏b∈Basis. Kb b ^ (β b))" by blast
  have prodle: "(∏b∈Basis. Kb b ^ (β b)) ≤ Kf ^ ra_deg β"
    by (rule prod_pow_le_deg[OF Kbnn Kble])
  have "ra_series_majorized x0 r σ cc (λx. ∏b∈Basis. ((f x - z) ∙ b) ^ (β b)) (Kf ^ ra_deg β)"
    by (rule ra_series_majorized_relax[OF cc prodle])
  hence "ra_series_majorized x0 r σ cc (λx. ra_monomial (f x - z) β) (Kf ^ ra_deg β)"
    by (simp only: ra_monomial_def)
  thus ?thesis by blast
qed

subsection ‹Step (c): composition›

text ‹Vector-valued continuity of an analytic function, obtained componentwise from
  the scalar continuity lemma.›

lemma real_analytic_on_imp_continuous_vec:
  fixes f :: "'a::euclidean_space ⇒ 'b::euclidean_space"
  assumes ana: "real_analytic_on f U" and xU: "x0 ∈ U"
  shows "continuous (at x0) f"
proof (subst continuous_componentwise, intro ballI)
  fix b :: 'b assume b: "b ∈ Basis"
  have "real_analytic_on (λx. f x ∙ b) U"
    by (rule real_analytic_on_inner_component[OF ana])
  thus "continuous (at x0) (λx. f x ∙ b)"
    by (rule real_analytic_on_imp_continuous[OF _ xU])
qed

text ‹Only ‹ra_idx_zero› has degree zero among the multi-indices.›

lemma ra_deg_eq0_iff:
  fixes α :: "'a::euclidean_space ⇒ nat"
  assumes "α ∈ ra_idx"
  shows "ra_deg α = 0 ⟷ α = ra_idx_zero"
proof
  assume d0: "ra_deg α = 0"
  have z: "α b = 0" if "b ∈ Basis" for b
  proof -
    have "α b ≤ (∑c∈Basis. α c)" using that by (intro member_le_sum) auto
    also have "… = 0" using d0 by (simp add: ra_deg_def)
    finally show ?thesis by simp
  qed
  show "α = ra_idx_zero"
  proof (rule ext)
    fix b show "α b = ra_idx_zero b"
    proof (cases "b ∈ Basis")
      case True thus ?thesis using z by (simp add: ra_idx_zero_def)
    next
      case False thus ?thesis using assms by (auto simp: ra_idx_def ra_idx_zero_def)
    qed
  qed
next
  assume "α = ra_idx_zero"
  thus "ra_deg α = 0" by (simp add: ra_deg_def ra_idx_zero_def)
qed

lemma ra_series_at_zero_coeff:
  fixes c :: "('a::euclidean_space ⇒ nat) ⇒ 'b::real_normed_vector"
  assumes hs: "((λα. ra_monomial (0::'a) α *R c α) has_sum v) ra_idx"
  shows "c ra_idx_zero = v"
proof -
  have neutral: "ra_monomial (0::'a) α *R c α = 0"
    if "α ∈ ra_idx - {ra_idx_zero}" for α
  proof -
    have "α ≠ ra_idx_zero"
      using that by simp
    hence "ra_deg α ≠ 0"
      using ra_deg_eq0_iff that by auto
    thus ?thesis
      by (simp add: ra_monomial_zero)
  qed
  have "((λα. ra_monomial (0::'a) α *R c α) has_sum v) ra_idx
          = ((λα. ra_monomial (0::'a) α *R c α) has_sum v) {ra_idx_zero}"
    by (rule has_sum_cong_neutral) (use neutral ra_idx_zero_in in auto)
  with hs have hs1:
    "((λα. ra_monomial (0::'a) α *R c α) has_sum v) {ra_idx_zero}"
    by simp
  moreover have "((λα. ra_monomial (0::'a) α *R c α) has_sum c ra_idx_zero) {ra_idx_zero}"
    by (rule has_sum_finiteI) (auto simp: ra_monomial_zero ra_idx_zero_def ra_deg_def)
  ultimately show ?thesis
    by (metis has_sum_unique)
qed

lemma ra_dcoeff_idx_zero_basis:
  fixes c :: "('a::euclidean_space ⇒ nat) ⇒ 'b::real_normed_vector"
  assumes b: "b ∈ Basis"
  shows "ra_dcoeff c b ra_idx_zero = c (λx. if x = b then 1 else 0)"
proof -
  define e :: "'a ⇒ nat" where "e = (λx. if x = b then 1 else 0)"
  have inc_b: "ra_inc ra_idx_zero b = e"
    by (rule ext) (simp add: ra_inc_def ra_idx_zero_def e_def)
  have rest0:
    "(∑x∈Basis - {b}.
        (real (Suc (ra_idx_zero x)) * (b ∙ x)) *R c (ra_inc ra_idx_zero x)) = 0"
    by (rule sum.neutral) (use b in ‹auto simp: inner_Basis›)
  have "ra_dcoeff c b ra_idx_zero =
      (real (Suc (ra_idx_zero b)) * (b ∙ b)) *R c (ra_inc ra_idx_zero b)
        + (∑x∈Basis - {b}.
            (real (Suc (ra_idx_zero x)) * (b ∙ x)) *R c (ra_inc ra_idx_zero x))"
    using b
    by (simp add: ra_dcoeff_def sum.remove[OF finite_Basis b])
  also have "… = c (ra_inc ra_idx_zero b)"
    using b rest0 by (simp add: ra_idx_zero_def inner_Basis)
  also have "… = c e"
    by (simp add: inc_b)
  finally show ?thesis
    by (simp add: e_def)
qed

lemma ra_dcoeff_idx_zero_eq_frechet_derivative:
  fixes f :: "'a::euclidean_space ⇒ 'b::banach"
    and c :: "('a ⇒ nat) ⇒ 'b"
  assumes r: "0 < r"
    and series:
      "⋀y. dist y x0 < r ⟹
        ((λα. ra_monomial (y - x0) α *R c α) has_sum f y) ra_idx"
  shows "ra_dcoeff c v ra_idx_zero = frechet_derivative f (at x0) v"
proof -
  have hs: "((λα. ra_monomial (0::'a) α *R ra_dcoeff c v α)
              has_sum frechet_derivative f (at x0) v) ra_idx"
    using ra_directional_derivative_series[OF r series, of x0 v] r
    by simp
  show ?thesis
    by (rule ra_series_at_zero_coeff[OF hs])
qed

lemma ra_linear_coeff_eq_frechet_derivative_basis:
  fixes f :: "'a::euclidean_space ⇒ 'b::banach"
    and c :: "('a ⇒ nat) ⇒ 'b"
  assumes b: "b ∈ Basis"
    and r: "0 < r"
    and series:
      "⋀y. dist y x0 < r ⟹
        ((λα. ra_monomial (y - x0) α *R c α) has_sum f y) ra_idx"
  shows "c (λx. if x = b then 1 else 0) = frechet_derivative f (at x0) b"
  using ra_dcoeff_idx_zero_eq_frechet_derivative[OF r series, of b]
        ra_dcoeff_idx_zero_basis[OF b, of c]
  by simp

lemma infsum_split_off:
  fixes f :: "('a::euclidean_space ⇒ nat) ⇒ real"
  assumes sf: "f summable_on ra_idx" and aA: "a ∈ ra_idx"
  shows "(∑∞α∈ra_idx. f α) = f a + (∑∞α∈(ra_idx - {a}). f α)"
proof -
  have nin: "a ∉ ra_idx - {a}" by simp
  have sub: "f summable_on (ra_idx - {a})"
    by (rule summable_on_subset_banach[OF sf]) auto
  have ins: "insert a (ra_idx - {a}) = ra_idx" using aA by blast
  have "(∑∞α∈insert a (ra_idx - {a}). f α)
          = f a + (∑∞α∈(ra_idx - {a}). f α)"
    by (rule infsum_insert[OF sub nin])
  thus ?thesis by (simp only: ins)
qed

text ‹The geometric tail (all nonzero indices) is linearly small in the ratio.›

lemma geom_idx_tail_small:
  fixes q q0 :: real
  assumes q0: "0 ≤ q" and qq0: "q ≤ q0" and q0pos: "0 < q0" and q01: "q0 < 1"
  shows "(∑∞α∈(ra_idx::('a::euclidean_space⇒nat) set). q ^ ra_deg α) - 1
           ≤ (q / q0) * (∑∞α∈(ra_idx::('a⇒nat) set). q0 ^ ra_deg α)"
proof -
  have q0nn: "0 ≤ q0" using q0pos by simp
  have sq: "(λα::'a⇒nat. q ^ ra_deg α) summable_on ra_idx"
    by (rule geom_idx_summable[OF q0 _]) (use qq0 q01 in linarith)
  have sq0: "(λα::'a⇒nat. q0 ^ ra_deg α) summable_on ra_idx"
    by (rule geom_idx_summable[OF q0nn q01])

  ― ‹split off the single zero-degree term›
  have czin: "(ra_idx_zero::'a⇒nat) ∈ ra_idx"
    by (rule ra_idx_zero_in)

  have srest_q': "(λα::'a⇒nat. q ^ ra_deg α) summable_on (ra_idx - {ra_idx_zero})"
    by (rule summable_on_subset_banach[OF sq]) auto

  have rest_eq:
    "(∑∞α∈(ra_idx::('a⇒nat) set). q ^ ra_deg α)
      = q ^ ra_deg (ra_idx_zero::'a⇒nat)
        + (∑∞α∈((ra_idx::('a⇒nat) set) - {(ra_idx_zero::'a⇒nat)}). q ^ ra_deg α)"
    by (rule infsum_split_off[OF sq czin])

  have ztermq: "q ^ ra_deg (ra_idx_zero::'a⇒nat) = 1"
    by (simp add: ra_deg_def ra_idx_zero_def)

  have srest_q:
    "(λα::'a⇒nat. q ^ ra_deg α)
      summable_on ((ra_idx::('a⇒nat) set) - {(ra_idx_zero::'a⇒nat)})"
    by (rule summable_on_subset_banach[OF sq]) auto

  have srest_q0:
    "(λα::'a⇒nat. q0 ^ ra_deg α)
      summable_on ((ra_idx::('a⇒nat) set) - {(ra_idx_zero::'a⇒nat)})"
    by (rule summable_on_subset_banach[OF sq0]) auto

  have srest_q0scaled:
    "(λα::'a⇒nat. (q / q0) * q0 ^ ra_deg α)
      summable_on ((ra_idx::('a⇒nat) set) - {(ra_idx_zero::'a⇒nat)})"
    by (rule summable_on_cmult_right[OF srest_q0])

  have ptw:
    "q ^ ra_deg α ≤ (q / q0) * q0 ^ ra_deg α"
    if a: "α ∈ ((ra_idx::('a⇒nat) set) - {(ra_idx_zero::'a⇒nat)})"
    for α :: "'a⇒nat"
  proof -
    have ar: "α ∈ ra_idx" and anz: "α ≠ ra_idx_zero"
      using a by auto
    have deg1: "1 ≤ ra_deg α"
      using ra_deg_eq0_iff[OF ar] anz
      by (cases "ra_deg α") auto

    have "q ^ ra_deg α = (q / q0) ^ ra_deg α * q0 ^ ra_deg α"
      using q0pos by (simp add: power_divide)
    also have "… ≤ (q / q0) ^ 1 * q0 ^ ra_deg α"
    proof (rule mult_right_mono)
      have rn: "0 ≤ q / q0"
        using q0 q0pos by simp
      have r1: "q / q0 ≤ 1"
        using qq0 q0pos by (simp add: divide_le_eq)
      show "(q / q0) ^ ra_deg α ≤ (q / q0) ^ 1"
        by (rule power_decreasing[OF deg1 rn r1])
      show "0 ≤ q0 ^ ra_deg α"
        using q0nn by simp
    qed
    finally show ?thesis by simp
  qed

  have rest_q_le_scaled_rest_q0:
    "(∑∞α∈((ra_idx::('a⇒nat) set) - {(ra_idx_zero::'a⇒nat)}). q ^ ra_deg α)
      ≤ (q / q0) *
          (∑∞α∈((ra_idx::('a⇒nat) set) - {(ra_idx_zero::'a⇒nat)}). q0 ^ ra_deg α)"
  proof -
    have "(∑∞α∈((ra_idx::('a⇒nat) set) - {(ra_idx_zero::'a⇒nat)}). q ^ ra_deg α)
        ≤ (∑∞α∈((ra_idx::('a⇒nat) set) - {(ra_idx_zero::'a⇒nat)}).
              (q / q0) * q0 ^ ra_deg α)"
      by (rule infsum_mono[OF srest_q srest_q0scaled]) (use ptw in auto)
    also have "… =
        (q / q0) *
          (∑∞α∈((ra_idx::('a⇒nat) set) - {(ra_idx_zero::'a⇒nat)}). q0 ^ ra_deg α)"
      by (rule infsum_cmult_right[OF srest_q0])
    finally show ?thesis .
  qed

  have rest_q0_le_all:
    "(∑∞α∈((ra_idx::('a⇒nat) set) - {(ra_idx_zero::'a⇒nat)}). q0 ^ ra_deg α)
      ≤ (∑∞α∈(ra_idx::('a⇒nat) set). q0 ^ ra_deg α)"
  proof -
    have rest_eq0:
      "(∑∞α∈(ra_idx::('a⇒nat) set). q0 ^ ra_deg α)
        = q0 ^ ra_deg (ra_idx_zero::'a⇒nat)
          + (∑∞α∈((ra_idx::('a⇒nat) set) - {(ra_idx_zero::'a⇒nat)}). q0 ^ ra_deg α)"
      by (rule infsum_split_off[OF sq0 czin])
    have ztermq0: "q0 ^ ra_deg (ra_idx_zero::'a⇒nat) = 1"
      by (simp add: ra_deg_def ra_idx_zero_def)
    show ?thesis
      using rest_eq0 ztermq0 by linarith
  qed

  have key:
    "(∑∞α∈((ra_idx::('a⇒nat) set) - {(ra_idx_zero::'a⇒nat)}). q ^ ra_deg α)
      ≤ (q / q0) * (∑∞α∈(ra_idx::('a⇒nat) set). q0 ^ ra_deg α)"
  proof -
    have "(∑∞α∈((ra_idx::('a⇒nat) set) - {(ra_idx_zero::'a⇒nat)}). q ^ ra_deg α)
      ≤ (q / q0) *
          (∑∞α∈((ra_idx::('a⇒nat) set) - {(ra_idx_zero::'a⇒nat)}). q0 ^ ra_deg α)"
      by (rule rest_q_le_scaled_rest_q0)
    also have "… ≤ (q / q0) *
          (∑∞α∈(ra_idx::('a⇒nat) set). q0 ^ ra_deg α)"
      by (rule mult_left_mono[OF rest_q0_le_all]) (use q0 q0pos in simp)
    finally show ?thesis .
  qed

  have "(∑∞α∈(ra_idx::('a⇒nat) set). q ^ ra_deg α) - 1
        = (∑∞α∈((ra_idx::('a⇒nat) set) - {(ra_idx_zero::'a⇒nat)}). q ^ ra_deg α)"
    using rest_eq ztermq by linarith
  also note key
  finally show ?thesis .
qed

subsection ‹Step (c): dominated Fubini sum of series families›

text ‹Monomial absolute bound by the majorant parameter on the working ball.›

lemma ra_monomial_abs_le_pow:
  fixes h :: "'a::euclidean_space" and σ :: real
  assumes a: "γ ∈ ra_idx" and hle: "norm h ≤ σ"
  shows "¦ra_monomial h γ¦ ≤ σ ^ ra_deg γ"
proof -
  have "¦ra_monomial h γ¦ ≤ norm h ^ ra_deg γ"
    by (rule ra_monomial_norm_le[OF a])
  also have "… ≤ σ ^ ra_deg γ"
    by (rule power_mono[OF hle]) simp
  finally show ?thesis .
qed

text ‹A majorant-dominated infinite sum of @{const ra_series_on} families is again such a
  family, with coefficients ‹λγ. ∑β a β * CC β γ›; ‹G› is the value of the sum.›

lemma ra_series_on_majdom:
  fixes CC :: "('b::euclidean_space ⇒ nat) ⇒ ('a::euclidean_space ⇒ nat) ⇒ real"
    and Fn :: "('b ⇒ nat) ⇒ 'a ⇒ real"
    and a  :: "('b ⇒ nat) ⇒ real"
    and Kk :: "('b ⇒ nat) ⇒ real"
  assumes s0: "0 < σ" and rσ: "r ≤ σ"
    and ser: "⋀β. β ∈ ra_idx ⟹ ra_series_on x0 r (CC β) (Fn β)"
    and maj: "⋀β. β ∈ ra_idx ⟹ ra_majorized σ (CC β) (Kk β)"
    and gsum: "(λβ. ¦a β¦ * Kk β) summable_on (ra_idx::('b⇒nat) set)"
    and Gval: "⋀x. dist x x0 < r ⟹ ((λβ. a β *R Fn β x) has_sum G x) (ra_idx::('b⇒nat) set)"
  shows "ra_series_on x0 r (λγ. ∑∞β∈(ra_idx::('b⇒nat) set). a β * CC β γ) G"
  unfolding ra_series_on_def
proof (intro allI impI)
  fix x :: 'a assume d: "dist x x0 < r"
  define h where "h = x - x0"
  have hle: "norm h ≤ σ"
  proof -
    have "norm h < r" using d by (simp add: h_def dist_norm)
    thus ?thesis using rσ by simp
  qed
  define D where "D = (λ(β,γ). ra_monomial h γ * (a β * CC β γ))"
  ― ‹per-‹β› facts›
  have serβ: "((λγ. ra_monomial h γ * CC β γ) has_sum Fn β x) (ra_idx::('a⇒nat) set)"
    if b: "β ∈ ra_idx" for β
    using ser[OF b] d unfolding ra_series_on_def h_def by simp
  have majβsum: "ra_weighted_abs σ (CC β) summable_on (ra_idx::('a⇒nat) set)"
    if b: "β ∈ ra_idx" for β
    using maj[OF b] by (simp add: ra_majorized_def)
  have majβle: "(∑∞γ∈(ra_idx::('a⇒nat) set). ra_weighted_abs σ (CC β) γ) ≤ Kk β"
    if b: "β ∈ ra_idx" for β
    using maj[OF b] by (simp add: ra_majorized_def)
  ― ‹pointwise domination of the double family›
  have Dbound: "¦D (β,γ)¦ ≤ ¦a β¦ * ra_weighted_abs σ (CC β) γ"
    if b: "β ∈ ra_idx" and g: "γ ∈ ra_idx" for β γ
  proof -
    have "¦D (β,γ)¦ = ¦ra_monomial h γ¦ * (¦a β¦ * ¦CC β γ¦)"
      by (simp add: D_def abs_mult)
    also have "… ≤ (σ ^ ra_deg γ) * (¦a β¦ * ¦CC β γ¦)"
      by (rule mult_right_mono[OF ra_monomial_abs_le_pow[OF g hle]]) simp
    also have "… = ¦a β¦ * (¦CC β γ¦ * σ ^ ra_deg γ)"
      by (simp add: mult.assoc mult.left_commute)
    also have "… = ¦a β¦ * ra_weighted_abs σ (CC β) γ" by (simp add: ra_weighted_abs_def)
    finally show ?thesis .
  qed
  ― ‹inner abs-summability (over ‹γ›) for each ‹β››
  have inner_abs: "(λγ. norm (D (β,γ))) summable_on (ra_idx::('a⇒nat) set)"
    if b: "β ∈ ra_idx" for β
  proof (rule summable_on_comparison_test[where f = "λγ. ¦a β¦ * ra_weighted_abs σ (CC β) γ"])
    show "(λγ. ¦a β¦ * ra_weighted_abs σ (CC β) γ) summable_on (ra_idx::('a⇒nat) set)"
      by (rule summable_on_cmult_right[OF majβsum[OF b]])
  next
    fix γ :: "'a⇒nat" assume g: "γ ∈ ra_idx"
    have "norm (D (β,γ)) = ¦D (β,γ)¦" by simp
    also have "… ≤ ¦a β¦ * ra_weighted_abs σ (CC β) γ" by (rule Dbound[OF b g])
    finally show "norm (D (β,γ)) ≤ ¦a β¦ * ra_weighted_abs σ (CC β) γ" .
  next
    fix γ :: "'a⇒nat" assume "γ ∈ ra_idx"
    show "0 ≤ norm (D (β,γ))" by simp
  qed
  ― ‹inner total bounded by the g-majorant term›
  have inner_tot_le: "(∑∞γ∈(ra_idx::('a⇒nat) set). norm (D (β,γ))) ≤ ¦a β¦ * Kk β"
    if b: "β ∈ ra_idx" for β
  proof -
    have "(∑∞γ∈(ra_idx::('a⇒nat) set). norm (D (β,γ)))
            ≤ (∑∞γ∈(ra_idx::('a⇒nat) set). ¦a β¦ * ra_weighted_abs σ (CC β) γ)"
    proof (rule infsum_mono)
      show "(λγ. norm (D (β,γ))) summable_on (ra_idx::('a⇒nat) set)"
        by (rule inner_abs[OF b])
      show "(λγ. ¦a β¦ * ra_weighted_abs σ (CC β) γ) summable_on (ra_idx::('a⇒nat) set)"
        by (rule summable_on_cmult_right[OF majβsum[OF b]])
      fix γ :: "'a⇒nat" assume g: "γ ∈ ra_idx"
      have "norm (D (β,γ)) = ¦D (β,γ)¦" by simp
      thus "norm (D (β,γ)) ≤ ¦a β¦ * ra_weighted_abs σ (CC β) γ"
        using Dbound[OF b g] by simp
    qed
    also have "… = ¦a β¦ * (∑∞γ∈(ra_idx::('a⇒nat) set). ra_weighted_abs σ (CC β) γ)"
      by (rule infsum_cmult_right[OF majβsum[OF b]])
    also have "… ≤ ¦a β¦ * Kk β"
      by (rule mult_left_mono[OF majβle[OF b]]) simp
    finally show ?thesis .
  qed
  ― ‹outer abs-summability of the inner totals›
  have outer_abs: "(λβ. ∑∞γ∈(ra_idx::('a⇒nat) set). norm (D (β,γ)))
                     summable_on (ra_idx::('b⇒nat) set)"
  proof (rule summable_on_comparison_test[where f = "λβ. ¦a β¦ * Kk β"])
    show "(λβ. ¦a β¦ * Kk β) summable_on (ra_idx::('b⇒nat) set)" by (rule gsum)
  next
    fix β :: "'b⇒nat" assume b: "β ∈ ra_idx"
    show "(∑∞γ∈(ra_idx::('a⇒nat) set). norm (D (β,γ))) ≤ ¦a β¦ * Kk β"
      by (rule inner_tot_le[OF b])
  next
    fix β :: "'b⇒nat" assume "β ∈ ra_idx"
    show "0 ≤ (∑∞γ∈(ra_idx::('a⇒nat) set). norm (D (β,γ)))"
      by (rule infsum_nonneg) simp
  qed
  have outer_abs': "(λβ. norm (∑∞γ∈(ra_idx::('a⇒nat) set). norm (D (β,γ))))
                      summable_on (ra_idx::('b⇒nat) set)"
  proof -
    have eq: "(λβ. norm (∑∞γ∈(ra_idx::('a⇒nat) set). norm (D (β,γ))))
                = (λβ. ∑∞γ∈(ra_idx::('a⇒nat) set). norm (D (β,γ)))"
    proof (rule ext)
      fix β :: "'b⇒nat"
      have "0 ≤ (∑∞γ∈(ra_idx::('a⇒nat) set). norm (D (β,γ)))"
        by (rule infsum_nonneg) simp
      thus "norm (∑∞γ∈(ra_idx::('a⇒nat) set). norm (D (β,γ)))
              = (∑∞γ∈(ra_idx::('a⇒nat) set). norm (D (β,γ)))" by simp
    qed
    show ?thesis using outer_abs by (simp only: eq)
  qed
  have conj1: "∀β∈(ra_idx::('b⇒nat) set). (λγ. norm (D (β,γ))) summable_on (ra_idx::('a⇒nat) set)"
    using inner_abs by blast
  have conj2: "(λβ. norm (∑∞γ∈(ra_idx::('a⇒nat) set). norm (D (β,γ))))
                 summable_on (ra_idx::('b⇒nat) set)"
    by (rule outer_abs')
  ― ‹the double family is absolutely summable on the product›
  have Dabs: "(λz. norm (D z)) summable_on (Sigma (ra_idx::('b⇒nat) set) (λ_. ra_idx::('a⇒nat) set))"
    by (rule Infinite_Sum.abs_summable_on_Sigma_iff
          [where f = D and A = "ra_idx::('b⇒nat) set" and B = "λ_. ra_idx::('a⇒nat) set",
           THEN iffD2, OF conjI[OF conj1 conj2]])
  have Dsumm: "D summable_on (Sigma (ra_idx::('b⇒nat) set) (λ_. ra_idx::('a⇒nat) set))"
    by (rule abs_summable_summable[OF Dabs])
  ― ‹sum the double family ‹γ›-first: total ‹= G x››
  have GhsB: "((λβ. a β *R Fn β x) has_sum G x) (ra_idx::('b⇒nat) set)"
    by (rule Gval[OF d])
  have innerB: "((λγ. D (β,γ)) has_sum (a β *R Fn β x)) (ra_idx::('a⇒nat) set)"
    if b: "β ∈ ra_idx" for β
  proof -
    have "((λγ. a β * (ra_monomial h γ * CC β γ)) has_sum (a β * Fn β x))
            (ra_idx::('a⇒nat) set)"
      by (rule has_sum_cmult_right[OF serβ[OF b]])
    thus ?thesis
      by (simp add: D_def mult.assoc mult.left_commute)
  qed
  have DhsG: "(D has_sum G x) (Sigma (ra_idx::('b⇒nat) set) (λ_. ra_idx::('a⇒nat) set))"
  proof (rule has_sum_SigmaI[where g = "λβ. a β *R Fn β x"])
    fix β :: "'b⇒nat" assume b: "β ∈ ra_idx"
    show "((λγ. D (β,γ)) has_sum (a β *R Fn β x)) (ra_idx::('a⇒nat) set)"
      by (rule innerB[OF b])
  next
    show "((λβ. a β *R Fn β x) has_sum G x) (ra_idx::('b⇒nat) set)" by (rule GhsB)
  next
    show "D summable_on (Sigma (ra_idx::('b⇒nat) set) (λ_. ra_idx::('a⇒nat) set))"
      by (rule Dsumm)
  qed
  ― ‹swap to ‹γ›-outer›
  have Dswap: "((λ(γ,β). D (β,γ)) has_sum G x)
                 ((ra_idx::('a⇒nat) set) × (ra_idx::('b⇒nat) set))"
  proof -
    have e1: "Sigma (ra_idx::('b⇒nat) set) (λ_. ra_idx::('a⇒nat) set)
                = (ra_idx::('b⇒nat) set) × (ra_idx::('a⇒nat) set)"
      by simp
    have "(D has_sum G x) ((ra_idx::('b⇒nat) set) × (ra_idx::('a⇒nat) set))"
      using DhsG e1 by simp
    thus ?thesis by (subst has_sum_swap) simp
  qed
  ― ‹summability of each inner ‹β›-slice on the swapped Sigma›
  have Dswap_summ: "(λ(γ,β). D (β,γ)) summable_on
                      (Sigma (ra_idx::('a⇒nat) set) (λ_. ra_idx::('b⇒nat) set))"
    using Dswap has_sum_imp_summable by (simp add: Sigma_def)
  have slice_summ: "(λβ. D (β,γ)) summable_on (ra_idx::('b⇒nat) set)"
    if g: "γ ∈ ra_idx" for γ
  proof -
    have "(λβ. (λ(γ,β). D (β,γ)) (γ,β)) summable_on (ra_idx::('b⇒nat) set)"
      by (rule summable_on_SigmaD1[OF _ g]) (use Dswap_summ in ‹simp add: case_prod_unfold›)
    thus ?thesis by simp
  qed
  ― ‹identify the inner ‹β›-sum and conclude›
  define Cc where "Cc = (λγ. ∑∞β∈(ra_idx::('b⇒nat) set). a β * CC β γ)"
  have innerγ: "((λβ. D (β,γ)) has_sum (ra_monomial h γ *R Cc γ)) (ra_idx::('b⇒nat) set)"
    if g: "γ ∈ ra_idx" for γ
  proof -
    have summabs: "(λβ. norm (a β * CC β γ)) summable_on (ra_idx::('b⇒nat) set)"
    proof (rule summable_on_comparison_test
             [where f = "λβ. (1 / σ ^ ra_deg γ) * (¦a β¦ * Kk β)"])
      show "(λβ. (1 / σ ^ ra_deg γ) * (¦a β¦ * Kk β)) summable_on (ra_idx::('b⇒nat) set)"
        by (rule summable_on_cmult_right[OF gsum])
    next
      fix β :: "'b⇒nat" assume "β ∈ ra_idx"
      show "0 ≤ norm (a β * CC β γ)" by simp
    next
      fix β :: "'b⇒nat" assume b: "β ∈ ra_idx"
      have sp: "0 < σ ^ ra_deg γ" using s0 by simp
      have one_term: "¦CC β γ¦ * σ ^ ra_deg γ ≤ Kk β"
      proof -
        have "¦CC β γ¦ * σ ^ ra_deg γ = ra_weighted_abs σ (CC β) γ" by (simp add: ra_weighted_abs_def)
        also have "… = (∑γ'∈{γ}. ra_weighted_abs σ (CC β) γ')" by simp
        also have "… ≤ (∑∞γ'∈(ra_idx::('a⇒nat) set). ra_weighted_abs σ (CC β) γ')"
        proof (rule finite_sum_le_infsum)
          show "ra_weighted_abs σ (CC β) summable_on (ra_idx::('a⇒nat) set)" by (rule majβsum[OF b])
          show "finite {γ}" by simp
          show "{γ} ⊆ ra_idx" using g by simp
          fix γ' :: "'a⇒nat" assume "γ' ∈ ra_idx - {γ}"
          show "0 ≤ ra_weighted_abs σ (CC β) γ'" using s0 by (simp add: ra_weighted_abs_nonneg)
        qed
        also have "… ≤ Kk β" by (rule majβle[OF b])
        finally show ?thesis .
      qed
      have ccle: "¦CC β γ¦ ≤ Kk β / σ ^ ra_deg γ"
        using one_term sp by (simp add: mult.commute pos_le_divide_eq)
      have "norm (a β * CC β γ) = ¦a β¦ * ¦CC β γ¦" by (simp add: abs_mult)
      also have "… ≤ ¦a β¦ * (Kk β / σ ^ ra_deg γ)"
        by (rule mult_left_mono[OF ccle]) simp
      also have "… = (1 / σ ^ ra_deg γ) * (¦a β¦ * Kk β)" by simp
      finally show "norm (a β * CC β γ) ≤ (1 / σ ^ ra_deg γ) * (¦a β¦ * Kk β)" .
    qed
    have summ: "(λβ. a β * CC β γ) summable_on (ra_idx::('b⇒nat) set)"
      by (rule abs_summable_summable[OF summabs])
    have base: "((λβ. a β * CC β γ) has_sum Cc γ) (ra_idx::('b⇒nat) set)"
      using summ unfolding Cc_def by (rule has_sum_infsum)
    have "((λβ. ra_monomial h γ * (a β * CC β γ)) has_sum (ra_monomial h γ * Cc γ))
            (ra_idx::('b⇒nat) set)"
      by (rule has_sum_cmult_right[OF base])
    thus ?thesis by (simp add: D_def)
  qed
  have "((λγ. ra_monomial h γ *R Cc γ) has_sum G x) (ra_idx::('a⇒nat) set)"
  proof (rule has_sum_SigmaD[where f = "λ(γ,β). D (β,γ)"
            and B = "λ_. ra_idx::('b⇒nat) set"])
    show "((λ(γ,β). D (β,γ)) has_sum G x)
            (Sigma (ra_idx::('a⇒nat) set) (λ_. ra_idx::('b⇒nat) set))"
      using Dswap by (simp add: Sigma_def)
  next
    fix γ :: "'a⇒nat" assume g: "γ ∈ ra_idx"
    show "((λβ. (λ(γ,β). D (β,γ)) (γ,β)) has_sum (ra_monomial h γ *R Cc γ))
            (ra_idx::('b⇒nat) set)"
      using innerγ[OF g] by simp
  qed
  thus "((λγ. ra_monomial (x - x0) γ *R (∑∞β∈(ra_idx::('b⇒nat) set). a β * CC β γ))
            has_sum G x) (ra_idx::('a⇒nat) set)"
    by (simp add: h_def Cc_def)
qed

text ‹Vector outer-coefficient version of the dominated Fubini sum.›

lemma ra_series_on_majdom_vec:
  fixes CC :: "('b::euclidean_space ⇒ nat) ⇒ ('a::euclidean_space ⇒ nat) ⇒ real"
    and Fn :: "('b ⇒ nat) ⇒ 'a ⇒ real"
    and vg :: "('b ⇒ nat) ⇒ 'c::banach"
    and Kk :: "('b ⇒ nat) ⇒ real"
  assumes s0: "0 < σ" and rσ: "r ≤ σ"
    and ser: "⋀β. β ∈ ra_idx ⟹ ra_series_on x0 r (CC β) (Fn β)"
    and maj: "⋀β. β ∈ ra_idx ⟹ ra_majorized σ (CC β) (Kk β)"
    and gsum: "(λβ. norm (vg β) * Kk β) summable_on (ra_idx::('b⇒nat) set)"
    and Gval: "⋀x. dist x x0 < r ⟹
                  ((λβ. Fn β x *R vg β) has_sum G x) (ra_idx::('b⇒nat) set)"
  shows "∀x. dist x x0 < r ⟶
           ((λγ. ra_monomial (x - x0) γ *R (∑∞β∈(ra_idx::('b⇒nat) set). CC β γ *R vg β))
              has_sum G x) (ra_idx::('a⇒nat) set)"
proof (intro allI impI)
  fix x :: 'a assume d: "dist x x0 < r"
  define h where "h = x - x0"
  have hle: "norm h ≤ σ"
  proof -
    have "norm h < r" using d by (simp add: h_def dist_norm)
    thus ?thesis using rσ by simp
  qed
  define D where "D = (λ(β,γ). ra_monomial h γ *R (CC β γ *R vg β))"
  have serβ: "((λγ. ra_monomial h γ * CC β γ) has_sum Fn β x) (ra_idx::('a⇒nat) set)"
    if b: "β ∈ ra_idx" for β
    using ser[OF b] d unfolding ra_series_on_def h_def by simp
  have majβsum: "ra_weighted_abs σ (CC β) summable_on (ra_idx::('a⇒nat) set)"
    if b: "β ∈ ra_idx" for β
    using maj[OF b] by (simp add: ra_majorized_def)
  have majβle: "(∑∞γ∈(ra_idx::('a⇒nat) set). ra_weighted_abs σ (CC β) γ) ≤ Kk β"
    if b: "β ∈ ra_idx" for β
    using maj[OF b] by (simp add: ra_majorized_def)
  ― ‹pointwise norm-domination of the double family›
  have Dbound: "norm (D (β,γ)) ≤ norm (vg β) * ra_weighted_abs σ (CC β) γ"
    if b: "β ∈ ra_idx" and g: "γ ∈ ra_idx" for β γ
  proof -
    have "norm (D (β,γ)) = ¦ra_monomial h γ¦ * (¦CC β γ¦ * norm (vg β))"
      by (simp add: D_def abs_mult)
    also have "… ≤ (σ ^ ra_deg γ) * (¦CC β γ¦ * norm (vg β))"
      by (rule mult_right_mono[OF ra_monomial_abs_le_pow[OF g hle]]) simp
    also have "… = norm (vg β) * (¦CC β γ¦ * σ ^ ra_deg γ)"
      by (simp add: mult.assoc mult.left_commute)
    also have "… = norm (vg β) * ra_weighted_abs σ (CC β) γ" by (simp add: ra_weighted_abs_def)
    finally show ?thesis .
  qed
  have inner_abs: "(λγ. norm (D (β,γ))) summable_on (ra_idx::('a⇒nat) set)"
    if b: "β ∈ ra_idx" for β
  proof (rule summable_on_comparison_test[where f = "λγ. norm (vg β) * ra_weighted_abs σ (CC β) γ"])
    show "(λγ. norm (vg β) * ra_weighted_abs σ (CC β) γ) summable_on (ra_idx::('a⇒nat) set)"
      by (rule summable_on_cmult_right[OF majβsum[OF b]])
  next
    fix γ :: "'a⇒nat" assume g: "γ ∈ ra_idx"
    show "norm (D (β,γ)) ≤ norm (vg β) * ra_weighted_abs σ (CC β) γ" by (rule Dbound[OF b g])
  next
    fix γ :: "'a⇒nat" assume "γ ∈ ra_idx"
    show "0 ≤ norm (D (β,γ))" by simp
  qed
  have inner_tot_le: "(∑∞γ∈(ra_idx::('a⇒nat) set). norm (D (β,γ))) ≤ norm (vg β) * Kk β"
    if b: "β ∈ ra_idx" for β
  proof -
    have "(∑∞γ∈(ra_idx::('a⇒nat) set). norm (D (β,γ)))
            ≤ (∑∞γ∈(ra_idx::('a⇒nat) set). norm (vg β) * ra_weighted_abs σ (CC β) γ)"
    proof (rule infsum_mono)
      show "(λγ. norm (D (β,γ))) summable_on (ra_idx::('a⇒nat) set)"
        by (rule inner_abs[OF b])
      show "(λγ. norm (vg β) * ra_weighted_abs σ (CC β) γ) summable_on (ra_idx::('a⇒nat) set)"
        by (rule summable_on_cmult_right[OF majβsum[OF b]])
      fix γ :: "'a⇒nat" assume g: "γ ∈ ra_idx"
      show "norm (D (β,γ)) ≤ norm (vg β) * ra_weighted_abs σ (CC β) γ" by (rule Dbound[OF b g])
    qed
    also have "… = norm (vg β) * (∑∞γ∈(ra_idx::('a⇒nat) set). ra_weighted_abs σ (CC β) γ)"
      by (rule infsum_cmult_right[OF majβsum[OF b]])
    also have "… ≤ norm (vg β) * Kk β"
      by (rule mult_left_mono[OF majβle[OF b]]) simp
    finally show ?thesis .
  qed
  have outer_abs: "(λβ. ∑∞γ∈(ra_idx::('a⇒nat) set). norm (D (β,γ)))
                     summable_on (ra_idx::('b⇒nat) set)"
  proof (rule summable_on_comparison_test[where f = "λβ. norm (vg β) * Kk β"])
    show "(λβ. norm (vg β) * Kk β) summable_on (ra_idx::('b⇒nat) set)" by (rule gsum)
  next
    fix β :: "'b⇒nat" assume b: "β ∈ ra_idx"
    show "(∑∞γ∈(ra_idx::('a⇒nat) set). norm (D (β,γ))) ≤ norm (vg β) * Kk β"
      by (rule inner_tot_le[OF b])
  next
    fix β :: "'b⇒nat" assume "β ∈ ra_idx"
    show "0 ≤ (∑∞γ∈(ra_idx::('a⇒nat) set). norm (D (β,γ)))"
      by (rule infsum_nonneg) simp
  qed
  have outer_abs': "(λβ. norm (∑∞γ∈(ra_idx::('a⇒nat) set). norm (D (β,γ))))
                      summable_on (ra_idx::('b⇒nat) set)"
  proof -
    have eq: "(λβ. norm (∑∞γ∈(ra_idx::('a⇒nat) set). norm (D (β,γ))))
                = (λβ. ∑∞γ∈(ra_idx::('a⇒nat) set). norm (D (β,γ)))"
    proof (rule ext)
      fix β :: "'b⇒nat"
      have "0 ≤ (∑∞γ∈(ra_idx::('a⇒nat) set). norm (D (β,γ)))"
        by (rule infsum_nonneg) simp
      thus "norm (∑∞γ∈(ra_idx::('a⇒nat) set). norm (D (β,γ)))
              = (∑∞γ∈(ra_idx::('a⇒nat) set). norm (D (β,γ)))" by simp
    qed
    show ?thesis using outer_abs by (simp only: eq)
  qed
  have conj1: "∀β∈(ra_idx::('b⇒nat) set). (λγ. norm (D (β,γ))) summable_on (ra_idx::('a⇒nat) set)"
    using inner_abs by blast
  have conj2: "(λβ. norm (∑∞γ∈(ra_idx::('a⇒nat) set). norm (D (β,γ))))
                 summable_on (ra_idx::('b⇒nat) set)"
    by (rule outer_abs')
  have Dabs: "(λz. norm (D z)) summable_on (Sigma (ra_idx::('b⇒nat) set) (λ_. ra_idx::('a⇒nat) set))"
    by (rule Infinite_Sum.abs_summable_on_Sigma_iff
          [where f = D and A = "ra_idx::('b⇒nat) set" and B = "λ_. ra_idx::('a⇒nat) set",
           THEN iffD2, OF conjI[OF conj1 conj2]])
  have Dsumm: "D summable_on (Sigma (ra_idx::('b⇒nat) set) (λ_. ra_idx::('a⇒nat) set))"
    by (rule abs_summable_summable[OF Dabs])
  have GhsB: "((λβ. Fn β x *R vg β) has_sum G x) (ra_idx::('b⇒nat) set)"
    by (rule Gval[OF d])
  have innerB: "((λγ. D (β,γ)) has_sum (Fn β x *R vg β)) (ra_idx::('a⇒nat) set)"
    if b: "β ∈ ra_idx" for β
  proof -
    have "((λγ. (ra_monomial h γ * CC β γ) *R vg β) has_sum (Fn β x *R vg β))
            (ra_idx::('a⇒nat) set)"
      by (rule has_sum_bounded_linear[OF bounded_linear_scaleR_left serβ[OF b]])
    thus ?thesis by (simp add: D_def)
  qed
  have DhsG: "(D has_sum G x) (Sigma (ra_idx::('b⇒nat) set) (λ_. ra_idx::('a⇒nat) set))"
  proof (rule has_sum_SigmaI[where g = "λβ. Fn β x *R vg β"])
    fix β :: "'b⇒nat" assume b: "β ∈ ra_idx"
    show "((λγ. D (β,γ)) has_sum (Fn β x *R vg β)) (ra_idx::('a⇒nat) set)"
      by (rule innerB[OF b])
  next
    show "((λβ. Fn β x *R vg β) has_sum G x) (ra_idx::('b⇒nat) set)" by (rule GhsB)
  next
    show "D summable_on (Sigma (ra_idx::('b⇒nat) set) (λ_. ra_idx::('a⇒nat) set))"
      by (rule Dsumm)
  qed
  have Dswap: "((λ(γ,β). D (β,γ)) has_sum G x)
                 ((ra_idx::('a⇒nat) set) × (ra_idx::('b⇒nat) set))"
  proof -
    have e1: "Sigma (ra_idx::('b⇒nat) set) (λ_. ra_idx::('a⇒nat) set)
                = (ra_idx::('b⇒nat) set) × (ra_idx::('a⇒nat) set)" by simp
    have "(D has_sum G x) ((ra_idx::('b⇒nat) set) × (ra_idx::('a⇒nat) set))"
      using DhsG e1 by simp
    thus ?thesis by (subst has_sum_swap) simp
  qed
  have Dswap_summ: "(λ(γ,β). D (β,γ)) summable_on
                      (Sigma (ra_idx::('a⇒nat) set) (λ_. ra_idx::('b⇒nat) set))"
    using Dswap has_sum_imp_summable by (simp add: Sigma_def)
  define Cc where "Cc = (λγ. ∑∞β∈(ra_idx::('b⇒nat) set). CC β γ *R vg β)"
  have innerγ: "((λβ. D (β,γ)) has_sum (ra_monomial h γ *R Cc γ)) (ra_idx::('b⇒nat) set)"
    if g: "γ ∈ ra_idx" for γ
  proof -
    have summabs: "(λβ. norm (CC β γ *R vg β)) summable_on (ra_idx::('b⇒nat) set)"
    proof (rule summable_on_comparison_test
             [where f = "λβ. (1 / σ ^ ra_deg γ) * (norm (vg β) * Kk β)"])
      show "(λβ. (1 / σ ^ ra_deg γ) * (norm (vg β) * Kk β)) summable_on (ra_idx::('b⇒nat) set)"
        by (rule summable_on_cmult_right[OF gsum])
    next
      fix β :: "'b⇒nat" assume "β ∈ ra_idx"
      show "0 ≤ norm (CC β γ *R vg β)" by simp
    next
      fix β :: "'b⇒nat" assume b: "β ∈ ra_idx"
      have sp: "0 < σ ^ ra_deg γ" using s0 by simp
      have one_term: "¦CC β γ¦ * σ ^ ra_deg γ ≤ Kk β"
      proof -
        have "¦CC β γ¦ * σ ^ ra_deg γ = ra_weighted_abs σ (CC β) γ" by (simp add: ra_weighted_abs_def)
        also have "… = (∑γ'∈{γ}. ra_weighted_abs σ (CC β) γ')" by simp
        also have "… ≤ (∑∞γ'∈(ra_idx::('a⇒nat) set). ra_weighted_abs σ (CC β) γ')"
        proof (rule finite_sum_le_infsum)
          show "ra_weighted_abs σ (CC β) summable_on (ra_idx::('a⇒nat) set)" by (rule majβsum[OF b])
          show "finite {γ}" by simp
          show "{γ} ⊆ ra_idx" using g by simp
          fix γ' :: "'a⇒nat" assume "γ' ∈ ra_idx - {γ}"
          show "0 ≤ ra_weighted_abs σ (CC β) γ'" using s0 by (simp add: ra_weighted_abs_nonneg)
        qed
        also have "… ≤ Kk β" by (rule majβle[OF b])
        finally show ?thesis .
      qed
      have ccle: "¦CC β γ¦ ≤ Kk β / σ ^ ra_deg γ"
        using one_term sp by (simp add: mult.commute pos_le_divide_eq)
      have "norm (CC β γ *R vg β) = ¦CC β γ¦ * norm (vg β)" by simp
      also have "… ≤ (Kk β / σ ^ ra_deg γ) * norm (vg β)"
        by (rule mult_right_mono[OF ccle]) simp
      also have "… = (1 / σ ^ ra_deg γ) * (norm (vg β) * Kk β)" by simp
      finally show "norm (CC β γ *R vg β) ≤ (1 / σ ^ ra_deg γ) * (norm (vg β) * Kk β)" .
    qed
    have summ: "(λβ. CC β γ *R vg β) summable_on (ra_idx::('b⇒nat) set)"
      by (rule abs_summable_summable[OF summabs])
    have base: "((λβ. CC β γ *R vg β) has_sum Cc γ) (ra_idx::('b⇒nat) set)"
      using summ unfolding Cc_def by (rule has_sum_infsum)
    have "((λβ. ra_monomial h γ *R (CC β γ *R vg β)) has_sum (ra_monomial h γ *R Cc γ))
            (ra_idx::('b⇒nat) set)"
      by (rule has_sum_scaleR[OF base])
    thus ?thesis by (simp add: D_def)
  qed
  have "((λγ. ra_monomial h γ *R Cc γ) has_sum G x) (ra_idx::('a⇒nat) set)"
  proof (rule has_sum_SigmaD[where f = "λ(γ,β). D (β,γ)"
            and B = "λ_. ra_idx::('b⇒nat) set"])
    show "((λ(γ,β). D (β,γ)) has_sum G x)
            (Sigma (ra_idx::('a⇒nat) set) (λ_. ra_idx::('b⇒nat) set))"
      using Dswap by (simp add: Sigma_def)
  next
    fix γ :: "'a⇒nat" assume g: "γ ∈ ra_idx"
    show "((λβ. (λ(γ,β). D (β,γ)) (γ,β)) has_sum (ra_monomial h γ *R Cc γ))
            (ra_idx::('b⇒nat) set)"
      using innerγ[OF g] by simp
  qed
  thus "((λγ. ra_monomial (x - x0) γ *R (∑∞β∈(ra_idx::('b⇒nat) set). CC β γ *R vg β))
            has_sum G x) (ra_idx::('a⇒nat) set)"
    by (simp add: h_def Cc_def)
qed

subsection ‹Final composition theorem›

text ‹A coefficient bound with vanishing zero-degree term gives a majorant bound whose
  value is the geometric tail (which tends to 0 as ‹σ› shrinks).›

lemma ra_majorized_tail_bound:
  fixes c :: "('b::euclidean_space⇒nat)⇒'d::real_normed_vector"
  assumes Mnn: "0 ≤ M"
    and c0: "c ra_idx_zero = 0"
    and bound: "⋀α. α∈ra_idx ⟹ norm (c α) ≤ M / t ^ (ra_deg α)"
    and t: "0 < t" and s0: "0 ≤ σ" and st: "σ < t"
  shows "ra_majorized σ (λα. norm (c α))
           (M * ((∑∞α∈(ra_idx::('b⇒nat) set). (σ/t) ^ ra_deg α) - 1))"
proof -
  define q where "q = σ/t"
  have q0: "0 ≤ q" using s0 t by (simp add: q_def)
  have q1: "q < 1" using st t by (simp add: q_def)
  have geomS: "(λα::'b⇒nat. q ^ ra_deg α) summable_on ra_idx"
    by (rule geom_idx_summable[OF q0 q1])
  have geomMS: "(λα. M * q ^ ra_deg α) summable_on (ra_idx::('b⇒nat) set)"
    by (rule summable_on_cmult_right[OF geomS])
  have ptwise: "ra_weighted_abs σ (λα. norm (c α)) α ≤ M * q ^ ra_deg α" if a: "α∈ra_idx" for α
  proof -
    have "ra_weighted_abs σ (λα. norm (c α)) α = norm (c α) * σ ^ ra_deg α"
      by (simp add: ra_weighted_abs_def)
    also have "… ≤ (M / t ^ ra_deg α) * σ ^ ra_deg α"
      by (rule mult_right_mono[OF bound[OF a]]) (use s0 in simp)
    also have "… = M * q ^ ra_deg α" by (simp add: q_def power_divide)
    finally show ?thesis .
  qed
  have nn: "0 ≤ ra_weighted_abs σ (λα. norm (c α)) α" for α using s0 by (rule ra_weighted_abs_nonneg)
  have summ: "ra_weighted_abs σ (λα. norm (c α)) summable_on (ra_idx::('b⇒nat) set)"
    by (rule summable_on_comparison_test[OF geomMS]) (use ptwise nn in auto)
  ― ‹the zero-degree term of the majorant vanishes›
  have mfam0: "ra_weighted_abs σ (λα. norm (c α)) ra_idx_zero = 0"
    by (simp add: ra_weighted_abs_def c0)
  have czin: "(ra_idx_zero::'b⇒nat) ∈ ra_idx" by (rule ra_idx_zero_in)
  ― ‹split off the (zero) zero-degree term of the majorant sum›
  have splitM: "(∑∞α∈(ra_idx::('b⇒nat) set). ra_weighted_abs σ (λα. norm (c α)) α)
                  = (∑∞α∈((ra_idx::('b⇒nat) set) - {ra_idx_zero}).
                       ra_weighted_abs σ (λα. norm (c α)) α)"
    using infsum_split_off[OF summ czin] mfam0 by simp
  ― ‹and of the geometric sum›
  have splitG: "(∑∞α∈(ra_idx::('b⇒nat) set). M * q ^ ra_deg α)
                  = M * (q ^ ra_deg (ra_idx_zero::'b⇒nat))
                    + (∑∞α∈((ra_idx::('b⇒nat) set) - {ra_idx_zero}). M * q ^ ra_deg α)"
    using infsum_split_off[OF geomMS czin] by simp
  have geomMSrest: "(λα. M * q ^ ra_deg α) summable_on
                      ((ra_idx::('b⇒nat) set) - {ra_idx_zero})"
    by (rule summable_on_subset_banach[OF geomMS]) auto
  have summrest: "ra_weighted_abs σ (λα. norm (c α)) summable_on
                    ((ra_idx::('b⇒nat) set) - {ra_idx_zero})"
    by (rule summable_on_subset_banach[OF summ]) auto
  have tailbound:
    "(∑∞α∈((ra_idx::('b⇒nat) set) - {ra_idx_zero}). ra_weighted_abs σ (λα. norm (c α)) α)
      ≤ (∑∞α∈((ra_idx::('b⇒nat) set) - {ra_idx_zero}). M * q ^ ra_deg α)"
    by (rule infsum_mono[OF summrest geomMSrest]) (use ptwise in auto)
  have geom_eq: "(∑∞α∈(ra_idx::('b⇒nat) set). M * q ^ ra_deg α)
                   = M * (∑∞α∈(ra_idx::('b⇒nat) set). q ^ ra_deg α)"
    by (rule infsum_cmult_right[OF geomS])
  have ztermq: "q ^ ra_deg (ra_idx_zero::'b⇒nat) = 1"
    by (simp add: ra_deg_def ra_idx_zero_def)
  have "(∑∞α∈(ra_idx::('b⇒nat) set). ra_weighted_abs σ (λα. norm (c α)) α)
          = (∑∞α∈((ra_idx::('b⇒nat) set) - {ra_idx_zero}). ra_weighted_abs σ (λα. norm (c α)) α)"
    by (rule splitM)
  also have "… ≤ (∑∞α∈((ra_idx::('b⇒nat) set) - {ra_idx_zero}). M * q ^ ra_deg α)"
    by (rule tailbound)
  also have "… = (∑∞α∈(ra_idx::('b⇒nat) set). M * q ^ ra_deg α) - M * 1"
    using splitG ztermq by simp
  also have "… = M * ((∑∞α∈(ra_idx::('b⇒nat) set). q ^ ra_deg α) - 1)"
    using geom_eq by (simp add: algebra_simps)
  finally show ?thesis using summ by (simp add: ra_majorized_def q_def)
qed

text ‹Scaling a real-analytic scalar function by a fixed vector stays analytic.›

lemma real_analytic_on_scaleR_vec:
  fixes f :: "'a::euclidean_space ⇒ real" and v :: "'c::real_normed_vector"
  assumes F: "real_analytic_on f U"
  shows "real_analytic_on (λx. f x *R v) U"
proof -
  from F have U: "open U" by (simp only: real_analytic_on_def)
  show ?thesis
    unfolding real_analytic_on_def
  proof (intro conjI ballI)
    show "open U" by (rule U)
  next
    fix x0 assume x0: "x0 ∈ U"
    from F x0 obtain r c where r: "0 < r"
      and F1: "⋀x. dist x x0 < r ⟹
                ((λα. ra_monomial (x - x0) α *R c α) has_sum f x) ra_idx"
      unfolding real_analytic_on_def by blast
    show "∃r>0. ∃cc. ∀x. dist x x0 < r ⟶
            ((λα. ra_monomial (x - x0) α *R cc α) has_sum (f x *R v)) ra_idx"
    proof (intro exI[where x=r] conjI exI[where x="λα. c α *R v"] allI impI)
      show "0 < r" by (rule r)
    next
      fix x assume d: "dist x x0 < r"
      have bl: "bounded_linear (λt::real. t *R v)" by (rule bounded_linear_scaleR_left)
      have "((λα. (ra_monomial (x - x0) α *R c α) *R v) has_sum (f x *R v)) ra_idx"
        by (rule has_sum_bounded_linear[OF bl F1[OF d]])
      thus "((λα. ra_monomial (x - x0) α *R (c α *R v)) has_sum (f x *R v)) ra_idx"
        by simp
    qed
  qed
qed

text ‹Composition of real-analytic functions (target a Banach space).›

lemma real_analytic_on_compose:
  fixes f :: "'a::euclidean_space ⇒ 'b::euclidean_space"
    and g :: "'b ⇒ 'c::banach"
  assumes F: "real_analytic_on f U" and G: "real_analytic_on g V" and FV: "f ` U ⊆ V"
  shows "real_analytic_on (λx. g (f x)) U"
proof -
  from F have U: "open U" by (simp only: real_analytic_on_def)
  from G have Vopen: "open V" by (simp only: real_analytic_on_def)
  show ?thesis
    unfolding real_analytic_on_def
  proof (intro conjI ballI)
    show "open U" by (rule U)
  next
    fix x0 assume x0: "x0 ∈ U"
    define y0 where "y0 = f x0"
    have y0V: "y0 ∈ V" using FV x0 by (auto simp: y0_def)
    ― ‹g's local series around ‹y0››
    from G y0V obtain ρg cg where ρg: "0 < ρg"
      and Gser: "⋀y. dist y y0 < ρg ⟹
                  ((λβ. ra_monomial (y - y0) β *R cg β) has_sum g y) ra_idx"
      unfolding real_analytic_on_def by blast
    ― ‹Cauchy bound on g's coefficients at a corner inside the ‹ρg›-ball›
    define eB where "eB = (∑b∈(Basis::'b set). b)"
    have eBpos: "0 < norm eB"
    proof -
      have "eB ≠ 0"
      proof
        assume "eB = 0"
        then have "eB ∙ (SOME b. b ∈ (Basis::'b set)) = 0" by simp
        moreover obtain b0 :: 'b where b0: "b0 ∈ Basis" using nonempty_Basis by blast
        have "(SOME b. b ∈ (Basis::'b set)) ∈ Basis" using b0 by (rule someI)
        hence "eB ∙ (SOME b. b ∈ (Basis::'b set)) = 1"
          by (simp add: eB_def inner_sum_left inner_Basis)
        ultimately show False by simp
      qed
      thus ?thesis by simp
    qed
    define t where "t = ρg / (2 * norm eB)"
    have t0: "0 < t" using ρg eBpos by (simp add: t_def)
    have corner_g: "t * norm (∑b∈(Basis::'b set). b) < ρg"
    proof -
      have "t * norm eB = ρg / 2" using eBpos by (simp add: t_def)
      also have "… < ρg" using ρg by simp
      finally show ?thesis by (simp add: eB_def)
    qed
    obtain Mg where Mgnn: "Mg ≥ 0"
      and cgbound: "⋀β. β ∈ ra_idx ⟹ norm (cg β) ≤ Mg / t ^ (ra_deg β)"
      using ra_coeff_bound[OF ρg Gser t0 corner_g] by blast
    ― ‹shifted ‹f›: zero constant coefficient›
    have F': "real_analytic_on (λx. f x - y0) U"
      by (rule real_analytic_on_diff[OF F real_analytic_on_const[OF U]])
    from F' x0 obtain rf cf where rf: "0 < rf"
      and Fser: "⋀x. dist x x0 < rf ⟹
                  ((λα. ra_monomial (x - x0) α *R cf α) has_sum (f x - y0)) ra_idx"
      unfolding real_analytic_on_def by blast
    ― ‹Cauchy bound on the shifted ‹f›'s coefficients on a smaller ball›
    define eA where "eA = (∑b∈(Basis::'a set). b)"
    have eApos: "0 < norm eA"
    proof -
      have "eA ≠ 0"
      proof
        assume "eA = 0"
        moreover obtain a0 :: 'a where a0: "a0 ∈ Basis" using nonempty_Basis by blast
        have "(SOME b. b ∈ (Basis::'a set)) ∈ Basis" using a0 by (rule someI)
        hence "eA ∙ (SOME b. b ∈ (Basis::'a set)) = 1"
          by (simp add: eA_def inner_sum_left inner_Basis)
        ultimately show False by simp
      qed
      thus ?thesis by simp
    qed
    define sf where "sf = rf / (2 * norm eA)"
    have sf0: "0 < sf" using rf eApos by (simp add: sf_def)
    have corner_f: "sf * norm (∑b∈(Basis::'a set). b) < rf"
    proof -
      have "sf * norm eA = rf / 2" using eApos by (simp add: sf_def)
      also have "… < rf" using rf by simp
      finally show ?thesis by (simp add: eA_def)
    qed
    obtain Mf where Mfnn: "Mf ≥ 0"
      and cfbound: "⋀α. α ∈ ra_idx ⟹ norm (cf α) ≤ Mf / sf ^ (ra_deg α)"
      using ra_coeff_bound[OF rf Fser sf0 corner_f] by blast
    ― ‹choose ‹σ› small: drive the shifted-‹f› majorant below ‹t››
    define geo where "geo = (λq::real. ∑∞α∈(ra_idx::('a⇒nat) set). q ^ ra_deg α)"
    ― ‹the majorant constant of the shifted ‹f› as a function of ‹σ››
    have shifted_const0: "cf ra_idx_zero = 0"
    proof -
      have "((λα. ra_monomial (x0 - x0) α *R cf α) has_sum (f x0 - y0)) ra_idx"
        by (rule Fser) (simp add: rf)
      then have hs: "((λα. ra_monomial (0::'a) α *R cf α) has_sum (0::'b)) ra_idx"
        by (simp add: y0_def)
      have neutral: "ra_monomial (0::'a) α *R cf α = 0"
        if "α ∈ ra_idx - {ra_idx_zero}" for α
      proof -
        have "α ≠ ra_idx_zero" using that by simp
        hence "ra_deg α ≠ 0" using ra_deg_eq0_iff that by auto
        thus ?thesis by (simp add: ra_monomial_zero)
      qed
      have "((λα. ra_monomial (0::'a) α *R cf α) has_sum (0::'b)) ra_idx
              = ((λα. ra_monomial (0::'a) α *R cf α) has_sum (0::'b)) {ra_idx_zero}"
        by (rule has_sum_cong_neutral) (use neutral ra_idx_zero_in in auto)
      with hs have "((λα. ra_monomial (0::'a) α *R cf α) has_sum (0::'b)) {ra_idx_zero}" by simp
      moreover have "((λα. ra_monomial (0::'a) α *R cf α) has_sum (cf ra_idx_zero)) {ra_idx_zero}"
        by (rule has_sum_finiteI) (auto simp: ra_monomial_zero ra_idx_zero_def ra_deg_def)
      ultimately show ?thesis by (metis has_sum_unique)
    qed
    ― ‹continuity: ‹f x› stays in ‹g›'s ball near ‹x0››
    have contf: "continuous (at x0) f" by (rule real_analytic_on_imp_continuous_vec[OF F x0])
    have tend: "(f ⤏ f x0) (at x0)" using contf by (simp add: continuous_at)
    have evb: "∀F x in at x0. f x ∈ ball (f x0) ρg"
      by (rule topological_tendstoD[OF tend]) (use ρg in auto)
    have "∀F x in at x0. dist (f x) (f x0) < ρg"
      using evb by (simp add: dist_commute)
    then obtain δc where δc: "0 < δc"
      and contball: "⋀x. dist x x0 < δc ⟹ x ≠ x0 ⟹ dist (f x) y0 < ρg"
      using ρg by (auto simp: eventually_at y0_def dist_commute)
    have contball': "dist (f x) y0 < ρg" if "dist x x0 < δc" for x
    proof (cases "x = x0")
      case True thus ?thesis using ρg by (simp add: y0_def)
    next
      case False thus ?thesis using contball[OF that] by simp
    qed
    ― ‹geometric constant for the tail estimate (over ‹'a›-indices)›
    define gh where "gh = (∑∞α∈(ra_idx::('a⇒nat) set). (1/2::real) ^ ra_deg α)"
    have ghnn: "0 ≤ gh" unfolding gh_def by (rule infsum_nonneg) simp
    define C where "C = Mf * (2 / sf) * gh"
    have Cnn: "0 ≤ C" using Mfnn sf0 ghnn by (simp add: C_def)
    define th where "th = t / (C + 1)"
    have th0: "0 < th" using t0 Cnn by (simp add: th_def)
    ― ‹the working radius, also the majorant parameter›
    define σ where "σ = (min (sf/2) (min δc (min rf th))) / 2"
    have σ0: "0 < σ" using sf0 δc rf th0 by (simp add: σ_def)
    have σsf2: "σ ≤ sf/2" using δc rf th0 sf0 by (simp add: σ_def)
    have σsf: "σ < sf" using σsf2 sf0 by simp
    have σδc: "σ ≤ δc" using sf0 rf th0 δc by (simp add: σ_def)
    have σrf: "σ ≤ rf" using sf0 δc th0 rf by (simp add: σ_def)
    have σth: "σ ≤ th" using sf0 δc rf th0 by (simp add: σ_def)
    have qhalf: "σ/sf ≤ 1/2" using σsf2 sf0 by (simp add: divide_le_eq)
    ― ‹majorant bound for the coefficients of the shifted ‹f›, with no degree-zero term›
    define Mc where "Mc = Mf * ((∑∞α∈(ra_idx::('a⇒nat) set). (σ/sf) ^ ra_deg α) - 1)"
    have σnn: "0 ≤ σ" using σ0 by simp
    have cfmaj: "ra_majorized σ (λα. norm (cf α)) Mc"
      unfolding Mc_def
      by (rule ra_majorized_tail_bound[OF Mfnn shifted_const0 cfbound sf0 σnn σsf])
    have Mcnn: "0 ≤ Mc"
      using ra_majorized_imp_nonneg_sum[OF _ cfmaj] cfmaj σ0 by (simp add: ra_majorized_def)
    ― ‹the geometric tail is linearly small, hence ‹Mc < t››
    have tail_small: "(∑∞α∈(ra_idx::('a⇒nat) set). (σ/sf) ^ ra_deg α) - 1 ≤ (σ/sf)/(1/2) * gh"
      unfolding gh_def
      by (rule geom_idx_tail_small[OF _ qhalf]) (use σ0 sf0 in auto)
    have Mc_le: "Mc ≤ C * σ"
    proof -
      have "Mc ≤ Mf * ((σ/sf)/(1/2) * gh)"
        unfolding Mc_def by (rule mult_left_mono[OF tail_small Mfnn])
      also have "… = C * σ" using sf0 by (simp add: C_def field_simps)
      finally show ?thesis .
    qed
    have Mc_lt_t: "Mc < t"
    proof -
      have "C * σ ≤ C * th" using σth Cnn by (simp add: mult_left_mono)
      also have "… = C * t / (C + 1)" by (simp add: th_def)
      also have "… < t"
      proof -
        have "C * t / (C + 1) < t ⟷ C * t < t * (C + 1)"
          using Cnn by (simp add: pos_divide_less_eq)
        thus ?thesis using t0 Cnn by (simp add: field_simps)
      qed
      finally show ?thesis using Mc_le by linarith
    qed
    ― ‹the per-monomial majorant constant›
    define Km where "Km = (Mc + t)/2"
    have Km_ge: "Mc ≤ Km" using Mc_lt_t by (simp add: Km_def)
    have Km_lt: "Km < t" using Mc_lt_t by (simp add: Km_def)
    have Km_nn: "0 ≤ Km" using Mcnn Mc_lt_t by (simp add: Km_def)
    ― ‹shifted-f series available on radius ‹rf›, restrict to ‹σ››
    have Fser': "((λα. ra_monomial (x - x0) α *R cf α) has_sum (f x - y0)) ra_idx"
      if "dist x x0 < σ" for x
      using Fser[of x] that σrf by simp
    ― ‹per-‹β› @{const ra_series_majorized} for the composed monomials ‹x ↦ ra_monomial (f x - y0) β››
    have perbeta: "∃cc. ra_series_majorized x0 σ σ cc (λx. ra_monomial (f x - y0) β) (Km ^ ra_deg β)" for β
    proof -
      have "∃cc. ra_series_majorized x0 σ σ cc (λx. ra_monomial ((f x - y0) - 0) β) (Km ^ ra_deg β)"
      proof (rule ra_series_majorized_ra_monomial_compose[where cf = cf and Mc = Mc])
        show "0 ≤ σ" using σ0 by simp
        show "⋀x. dist x x0 < σ ⟹
                ((λα. ra_monomial (x - x0) α *R cf α) has_sum (f x - y0)) ra_idx"
          by (rule Fser')
        show "ra_majorized σ (λα. norm (cf α)) Mc" by (rule cfmaj)
        show "⋀b. b ∈ Basis ⟹ Mc + ¦(0::'b) ∙ b¦ ≤ Km" using Km_ge by simp
        show "0 ≤ Km" by (rule Km_nn)
      qed
      thus ?thesis by simp
    qed
    ― ‹choose the coefficient families›
    have "∃CC. ∀β. ra_series_majorized x0 σ σ (CC β) (λx. ra_monomial (f x - y0) β) (Km ^ ra_deg β)"
      by (subst choice_iff[symmetric]) (use perbeta in blast)
    then obtain CC where CCsmaj:
      "⋀β. ra_series_majorized x0 σ σ (CC β) (λx. ra_monomial (f x - y0) β) (Km ^ ra_deg β)" by blast
    have CCser: "⋀β. β ∈ ra_idx ⟹ ra_series_on x0 σ (CC β) (λx. ra_monomial (f x - y0) β)"
      using CCsmaj by (simp add: ra_series_majorized_def)
    have CCmaj: "⋀β. β ∈ ra_idx ⟹ ra_majorized σ (CC β) (Km ^ ra_deg β)"
      using CCsmaj by (simp add: ra_series_majorized_def)
    ― ‹g-coefficient summability against the per-monomial majorant›
    have gsum: "(λβ. norm (cg β) * Km ^ ra_deg β) summable_on (ra_idx::('b⇒nat) set)"
    proof (rule summable_on_comparison_test
             [where f = "λβ. Mg * (Km / t) ^ ra_deg β"])
      have qlt: "Km / t < 1" using Km_lt t0 by (simp add: divide_less_eq)
      have qnn: "0 ≤ Km / t" using Km_nn t0 by simp
      have "(λβ::'b⇒nat. (Km / t) ^ ra_deg β) summable_on ra_idx"
        by (rule geom_idx_summable[OF qnn qlt])
      thus "(λβ. Mg * (Km / t) ^ ra_deg β) summable_on (ra_idx::('b⇒nat) set)"
        by (rule summable_on_cmult_right)
    next
      fix β :: "'b⇒nat" assume b: "β ∈ ra_idx"
      have "norm (cg β) * Km ^ ra_deg β ≤ (Mg / t ^ ra_deg β) * Km ^ ra_deg β"
        by (rule mult_right_mono[OF cgbound[OF b]]) (use Km_nn in simp)
      also have "… = Mg * (Km ^ ra_deg β / t ^ ra_deg β)" by simp
      also have "… = Mg * (Km / t) ^ ra_deg β" by (simp add: power_divide)
      finally show "norm (cg β) * Km ^ ra_deg β ≤ Mg * (Km / t) ^ ra_deg β" .
    next
      fix β :: "'b⇒nat" assume "β ∈ ra_idx"
      show "0 ≤ norm (cg β) * Km ^ ra_deg β" using Km_nn by simp
    qed
    ― ‹the ‹β›-sum value: ‹g›'s series at ‹f x››
    have Gval: "((λβ. ra_monomial (f x - y0) β *R cg β) has_sum g (f x)) (ra_idx::('b⇒nat) set)"
      if "dist x x0 < σ" for x
    proof -
      have "dist (f x) y0 < ρg" using contball'[of x] that σδc by simp
      thus ?thesis by (rule Gser)
    qed
    ― ‹assemble via the vector dominated-Fubini helper›
    have main: "∀x. dist x x0 < σ ⟶
        ((λγ. ra_monomial (x - x0) γ *R
              (∑∞β∈(ra_idx::('b⇒nat) set). CC β γ *R cg β))
           has_sum g (f x)) (ra_idx::('a⇒nat) set)"
      by (rule ra_series_on_majdom_vec
            [where CC = CC and Fn = "λβ x. ra_monomial (f x - y0) β" and vg = cg
               and Kk = "λβ. Km ^ ra_deg β" and σ = σ and r = σ
               and G = "λx. g (f x)"])
         (use σ0 CCser CCmaj gsum Gval in auto)
    show "∃r>0. ∃cc. ∀x. dist x x0 < r ⟶
            ((λγ. ra_monomial (x - x0) γ *R cc γ) has_sum g (f x)) ra_idx"
      using σ0 main by (intro exI, auto)
  qed
qed

subsection ‹Closure under linear maps, pairing, and finite sums›

lemma real_analytic_on_bounded_linear:
  fixes L :: "'a::euclidean_space ⇒ 'b::real_normed_vector"
  assumes U: "open U" and L: "bounded_linear L"
  shows "real_analytic_on L U"
proof -
  interpret L: bounded_linear L by (rule L)
  define e :: "'a ⇒ 'a ⇒ nat" where "e = (λb c. if c = b then 1 else 0)"
  have e_idx: "e b ∈ ra_idx" if "b ∈ Basis" for b
    using that by (auto simp: e_def ra_idx_def)
  have mono_e: "ra_monomial h (e b) = h ∙ b" if "b ∈ Basis" for b h
    using that by (simp add: e_def ra_monomial_def prod.remove)
  have e_inj: "inj_on e Basis"
    by (auto simp: inj_on_def e_def fun_eq_iff)
  have zero_notin: "(ra_idx_zero::'a ⇒ nat) ∉ image e Basis"
  proof
    assume "(ra_idx_zero::'a ⇒ nat) ∈ image e Basis"
    then obtain b where b: "b ∈ Basis" "ra_idx_zero = e b" by blast
    have "(0::nat) = 1"
      using b by (metis ra_idx_zero_def e_def)
    thus False by simp
  qed
  show ?thesis
    unfolding real_analytic_on_def
  proof (intro conjI ballI)
    show "open U" by (rule U)
  next
    fix x0 assume "x0 ∈ U"
    define A where "A = insert (ra_idx_zero::'a ⇒ nat) (image e Basis)"
    define coeff where
      "coeff = (λα::'a ⇒ nat.
        if α = ra_idx_zero then L x0 else L (∑b∈Basis. if α = e b then b else 0))"
    have finA: "finite A" by (simp add: A_def)
    have Asub: "A ⊆ ra_idx"
      using e_idx by (auto simp: A_def ra_idx_zero_in)
    have neutral: "ra_monomial h α *R coeff α = 0" if a: "α ∈ ra_idx - A" for h α
    proof -
      have z: "(∑b∈Basis. if α = e b then b else 0) = 0"
        using a by (intro sum.neutral) (auto simp: A_def)
      show ?thesis using a z by (simp add: A_def coeff_def)
    qed
    have coeff_e: "coeff (e b) = L b" if b: "b ∈ Basis" for b
    proof -
      have eb_ne: "e b ≠ (ra_idx_zero::'a ⇒ nat)"
        using b zero_notin by (metis image_eqI)
      have "(∑c∈Basis. if e b = e c then c else 0) =
            (∑c∈Basis. if c = b then c else 0)"
        using b e_inj by (intro sum.cong) (auto simp: inj_on_def)
      also have "... = b"
        using b by simp
      finally show ?thesis using eb_ne by (simp add: coeff_def)
    qed
    have sum_img: "(∑α∈image e Basis. ra_monomial h α *R coeff α) = L h" for h
    proof -
      have "(∑α∈image e Basis. ra_monomial h α *R coeff α) =
            (∑b∈Basis. ra_monomial h (e b) *R coeff (e b))"
        by (rule sum.reindex_cong[OF e_inj]) auto
      also have "... = (∑b∈Basis. (h ∙ b) *R L b)"
        by (intro sum.cong refl) (simp add: mono_e coeff_e)
      also have "... = L (∑b∈Basis. (h ∙ b) *R b)"
        by (simp add: L.sum L.scaleR)
      also have "... = L h"
        by (simp add: euclidean_representation)
      finally show ?thesis .
    qed
    have zero_term: "ra_monomial h (ra_idx_zero::'a ⇒ nat) *R coeff ra_idx_zero = L x0" for h
      by (simp add: coeff_def ra_idx_zero_def ra_monomial_def)
    have sumA: "(∑α∈A. ra_monomial h α *R coeff α) = L (x0 + h)" for h
    proof -
      have "(∑α∈A. ra_monomial h α *R coeff α) =
            ra_monomial h ra_idx_zero *R coeff ra_idx_zero +
            (∑α∈image e Basis. ra_monomial h α *R coeff α)"
        using zero_notin by (simp add: A_def)
      also have "... = L x0 + L h"
        by (simp add: zero_term sum_img)
      also have "... = L (x0 + h)"
        by (simp add: L.add)
      finally show ?thesis .
    qed
    show "∃r>0. ∃c. ∀x. dist x x0 < r ⟶
        ((λα. ra_monomial (x - x0) α *R c α) has_sum L x) ra_idx"
    proof (intro exI[where x=1] conjI exI[where x=coeff] allI impI)
      show "0 < (1::real)" by simp
    next
      fix x assume "dist x x0 < (1::real)"
      have "((λα. ra_monomial (x - x0) α *R coeff α)
          has_sum (∑α∈A. ra_monomial (x - x0) α *R coeff α)) ra_idx"
      proof (rule has_sum_finite_neutralI)
        show "finite A" by (rule finA)
        show "A ⊆ ra_idx" by (rule Asub)
        fix α
        assume "α ∈ ra_idx - A"
        thus "ra_monomial (x - x0) α *R coeff α = 0"
          by (rule neutral)
      next
        show "(∑α∈A. ra_monomial (x - x0) α *R coeff α) =
              (∑α∈A. ra_monomial (x - x0) α *R coeff α)"
          by (rule refl)
      qed
      thus "((λα. ra_monomial (x - x0) α *R coeff α) has_sum L x) ra_idx"
        by (simp add: sumA)
    qed
  qed
qed

lemma real_analytic_on_Pair:
  assumes F: "real_analytic_on f U" and G: "real_analytic_on g U"
  shows "real_analytic_on (λx. (f x, g x)) U"
proof -
  from F have U: "open U" by (simp only: real_analytic_on_def)
  show ?thesis
    unfolding real_analytic_on_def
  proof (intro conjI ballI)
    show "open U" by (rule U)
  next
    fix x0 assume x0: "x0 ∈ U"
    from F x0 obtain r1 c1 where r1: "0 < r1"
      and F1: "⋀x. dist x x0 < r1 ⟹
        ((λα. ra_monomial (x - x0) α *R c1 α) has_sum f x) ra_idx"
      unfolding real_analytic_on_def by blast
    from G x0 obtain r2 c2 where r2: "0 < r2"
      and G1: "⋀x. dist x x0 < r2 ⟹
        ((λα. ra_monomial (x - x0) α *R c2 α) has_sum g x) ra_idx"
      unfolding real_analytic_on_def by blast
    show "∃r>0. ∃c. ∀x. dist x x0 < r ⟶
        ((λα. ra_monomial (x - x0) α *R c α) has_sum (f x, g x)) ra_idx"
    proof (intro exI[where x="min r1 r2"] conjI
        exI[where x="λα. (c1 α, c2 α)"] allI impI)
      show "0 < min r1 r2" using r1 r2 by simp
    next
      fix x assume d: "dist x x0 < min r1 r2"
      have t1: "((λA. ∑α∈A. ra_monomial (x - x0) α *R c1 α) ⤏ f x)
          (finite_subsets_at_top ra_idx)"
        using F1[of x] d unfolding has_sum_def by simp
      have t2: "((λA. ∑α∈A. ra_monomial (x - x0) α *R c2 α) ⤏ g x)
          (finite_subsets_at_top ra_idx)"
        using G1[of x] d unfolding has_sum_def by simp
      have "((λA. ((∑α∈A. ra_monomial (x - x0) α *R c1 α),
                     (∑α∈A. ra_monomial (x - x0) α *R c2 α)))
          ⤏ (f x, g x)) (finite_subsets_at_top ra_idx)"
        by (rule tendsto_Pair[OF t1 t2])
      hence pair_tendsto:
        "((λA. ∑α∈A. (ra_monomial (x - x0) α *R c1 α,
                         ra_monomial (x - x0) α *R c2 α))
          ⤏ (f x, g x)) (finite_subsets_at_top ra_idx)"
        by (simp add: sum_prod)
      have "((λα. (ra_monomial (x - x0) α *R c1 α,
                       ra_monomial (x - x0) α *R c2 α))
          has_sum (f x, g x)) ra_idx"
        unfolding has_sum_def by (rule pair_tendsto)
      thus "((λα. ra_monomial (x - x0) α *R (c1 α, c2 α))
          has_sum (f x, g x)) ra_idx"
        by simp
    qed
  qed
qed

lemma real_analytic_on_sum:
  fixes f :: "'i ⇒ 'a::euclidean_space ⇒ 'b::real_normed_vector"
  assumes U: "open U"
    and fin: "finite I"
    and ana: "⋀i. i ∈ I ⟹ real_analytic_on (f i) U"
  shows "real_analytic_on (λx. ∑i∈I. f i x) U"
  using fin ana
proof (induction I rule: finite_induct)
  case empty
  show ?case
    by (simp add: real_analytic_on_const[OF U])
next
  case (insert i I)
  have fi: "real_analytic_on (f i) U"
    using insert.prems by simp
  have fI: "real_analytic_on (λx. ∑j∈I. f j x) U"
    using insert.IH insert.prems by blast
  have "real_analytic_on (λx. f i x + (∑j∈I. f j x)) U"
    by (rule real_analytic_on_add[OF fi fI])
  thus ?case
    using insert.hyps by simp
qed

lemma real_analytic_on_componentwise:
  fixes f :: "'a::euclidean_space ⇒ 'b::euclidean_space"
  assumes U: "open U"
    and ana: "⋀b. b ∈ Basis ⟹ real_analytic_on (λx. f x ∙ b) U"
  shows "real_analytic_on f U"
proof -
  have term_ana: "⋀b. b ∈ Basis ⟹ real_analytic_on (λx. (f x ∙ b) *R b) U"
    by (rule real_analytic_on_scaleR_vec[OF ana])
  have "real_analytic_on (λx. ∑b∈Basis. (f x ∙ b) *R b) U"
    by (rule real_analytic_on_sum[OF U finite_Basis]) (use term_ana in blast)
  thus ?thesis
    by (simp add: euclidean_representation)
qed

lemma real_analytic_on_fst:
  assumes "open U"
  shows "real_analytic_on (fst :: ('a::euclidean_space × 'b::euclidean_space) ⇒ 'a) U"
  by (rule real_analytic_on_bounded_linear[OF assms bounded_linear_fst])

lemma real_analytic_on_snd:
  assumes "open U"
  shows "real_analytic_on (snd :: ('a::euclidean_space × 'b::euclidean_space) ⇒ 'b) U"
  by (rule real_analytic_on_bounded_linear[OF assms bounded_linear_snd])

lemma real_analytic_on_Pair_const:
  fixes c :: "'b::euclidean_space"
  assumes "open U"
  shows "real_analytic_on (λx::'a::euclidean_space. (x, c)) U"
  by (rule real_analytic_on_Pair)
     (rule real_analytic_on_bounded_linear[OF assms bounded_linear_ident],
      rule real_analytic_on_const[OF assms])



subsection ‹Locality of real-analyticity›

text ‹
  ‹real_analytic_on› is a local property: if every point of an open set ‹V› has a
  neighbourhood on which ‹g› is real-analytic, then ‹g› is real-analytic on all of ‹V›.
›

lemma real_analytic_on_locality:
  fixes g :: "'a::euclidean_space ⇒ 'b::real_normed_vector"
  assumes V: "open V"
    and loc: "⋀y. y ∈ V ⟹
      ∃W. open W ∧ y ∈ W ∧ W ⊆ V ∧ real_analytic_on g W"
  shows "real_analytic_on g V"
  unfolding real_analytic_on_def
proof (intro conjI ballI)
  show "open V" by (rule V)
next
  fix x assume xV: "x ∈ V"
  from loc[OF xV] obtain W where W: "open W" "x ∈ W" "W ⊆ V"
    and gW: "real_analytic_on g W" by blast
  from gW W(2) show "∃r>0. ∃c. ∀y. dist y x < r ⟶
      ((λα. ra_monomial (y - x) α *R c α) has_sum g y) ra_idx"
    unfolding real_analytic_on_def by blast
qed

text ‹
  If ‹G› is real-analytic on ‹V0› and ‹g = G› on an open subset ‹W›, then ‹g› is
  real-analytic on ‹W›.
›

lemma real_analytic_on_cong_nbhd:
  fixes g G :: "'a::euclidean_space ⇒ 'b::real_normed_vector"
  assumes G: "real_analytic_on G V0"
    and W: "open W" and sub: "W ⊆ V0"
    and eq: "⋀y. y ∈ W ⟹ g y = G y"
  shows "real_analytic_on g W"
  unfolding real_analytic_on_def
proof (intro conjI ballI)
  show "open W" by (rule W)
next
  fix x assume xW: "x ∈ W"
  hence xV0: "x ∈ V0" using sub by blast
  from G xV0 obtain ρ c where ρ: "0 < ρ"
    and ser: "⋀y. dist y x < ρ ⟹
      ((λα. ra_monomial (y - x) α *R c α) has_sum G y) ra_idx"
    unfolding real_analytic_on_def by blast
  from W xW obtain δ where δ: "0 < δ" and ballW: "ball x δ ⊆ W"
    using open_contains_ball by blast
  define r where "r = min ρ δ"
  have r0: "0 < r" using ρ δ by (simp add: r_def)
  have "∀y. dist y x < r ⟶
      ((λα. ra_monomial (y - x) α *R c α) has_sum g y) ra_idx"
  proof (intro allI impI)
    fix y assume d: "dist y x < r"
    have dr: "dist y x < ρ" using d by (simp add: r_def)
    have dd: "dist y x < δ" using d by (simp add: r_def)
    have "y ∈ ball x δ" using dd by (simp add: dist_commute)
    hence yW: "y ∈ W" using ballW by blast
    have "((λα. ra_monomial (y - x) α *R c α) has_sum G y) ra_idx"
      by (rule ser[OF dr])
    thus "((λα. ra_monomial (y - x) α *R c α) has_sum g y) ra_idx"
      by (simp add: eq[OF yW])
  qed
  with r0 show "∃r>0. ∃c. ∀y. dist y x < r ⟶
      ((λα. ra_monomial (y - x) α *R c α) has_sum g y) ra_idx"
    by blast
qed


text ‹
  The inverse and implicit function theorems for real-analytic maps
  (‹real_analytic_local_inverse›, ‹real_analytic_implicit_function›) build on this theory.
›


section ‹A $C^\infty$ Function That Is Not Analytic›

text ‹
  The flat function ‹x ↦ exp (-1/x)› for ‹x > 0›, ‹0› otherwise, is ‹C∞› on ‹ℝ›.
  All of its derivatives vanish at ‹0›, while the function is positive to the right of
  ‹0›; so it is not real-analytic at ‹0›.  This witnesses ‹Cω ≠ C∞›.
›

definition exp_bump :: "real ⇒ real" where
  "exp_bump x = (if 0 < x then exp (- (1 / x)) else 0)"


subsection ‹Derivatives of the flat function›

text ‹Every derivative of ‹exp_bump› has the form ‹x ↦ p (1/x) * exp (-1/x)› for
  ‹x > 0› and vanishes for ‹x ≤ 0›, where ‹p› is a real polynomial function.  The key
  estimate is that ‹p (1/x) * exp (-1/x) ⟶ 0› as ‹x ⟶ 0+›.›

lemma flat_poly_tendsto_zero:
  assumes "real_polynomial_function p"
  shows "((λx. p (inverse x) * exp (- inverse x)) ⤏ 0) (at_right 0)"
proof -
  obtain a n where p: "p = (λt. ∑i≤n. a i * t ^ i)"
    using assms real_polynomial_function_iff_sum by blast
  have "((λt. ∑i≤n. a i * (t ^ i / exp t)) ⤏ (∑i≤n. a i * 0)) at_top"
    by (intro tendsto_sum tendsto_mult tendsto_const tendsto_power_div_exp_0)
  moreover have "(∑i≤n. a i * (t ^ i / exp t)) = p t * exp (- t)" for t
    by (simp add: p sum_distrib_right exp_minus divide_inverse mult.assoc)
  ultimately have "((λt. p t * exp (- t)) ⤏ 0) at_top"
    by simp
  from filterlim_compose[OF this filterlim_inverse_at_top_right] show ?thesis .
qed

lemma flat_poly_has_derivative:
  assumes p: "real_polynomial_function p"
  shows "((λx. if 0 < x then p (inverse x) * exp (- inverse x) else 0) has_real_derivative
      (if 0 < x then (inverse x)2 * (p (inverse x) - deriv p (inverse x)) * exp (- inverse x)
       else 0)) (at x)"
    (is "(?G has_real_derivative _) _")
proof -
  have Dp: "(p has_real_derivative deriv p t) (at t)" for t
  proof -
    obtain p' where "∀t. (p has_real_derivative p' t) (at t)"
      using has_real_derivative_polynomial_function[OF p] by blast
    then show ?thesis
      using DERIV_imp_deriv by metis
  qed
  consider "x < 0" | "x = 0" | "0 < x"
    by linarith
  then show ?thesis
  proof cases
    case 1
    have "((λy. 0) has_real_derivative 0) (at x)"
      by simp
    then have "(?G has_real_derivative 0) (at x)"
      by (rule has_field_derivative_transform_within_open[where S = "{..<0}"]) (use 1 in auto)
    then show ?thesis
      using 1 by simp
  next
    case 3
    then have x0: "x ≠ 0" by simp
    have d1: "((λy. p (inverse y)) has_real_derivative
        deriv p (inverse x) * - (inverse x ^ Suc (Suc 0))) (at x)"
      by (rule DERIV_chain2[OF Dp DERIV_inverse[OF x0]])
    have d2: "((λy. exp (- inverse y)) has_real_derivative
        exp (- inverse x) * - (- (inverse x ^ Suc (Suc 0)))) (at x)"
      by (rule DERIV_chain2[OF DERIV_exp DERIV_minus[OF DERIV_inverse[OF x0]]])
    have "((λy. p (inverse y) * exp (- inverse y)) has_real_derivative
        (inverse x)2 * (p (inverse x) - deriv p (inverse x)) * exp (- inverse x)) (at x)"
      using DERIV_mult[OF d1 d2] by (simp add: power2_eq_square algebra_simps)
    then have "(?G has_real_derivative
        (inverse x)2 * (p (inverse x) - deriv p (inverse x)) * exp (- inverse x)) (at x)"
      by (rule has_field_derivative_transform_within_open[where S = "{0<..}"]) (use 3 in auto)
    then show ?thesis
      using 3 by simp
  next
    case 2
    have right: "((λy. (?G y - ?G 0) / (y - 0)) ⤏ 0) (at_right 0)"
    proof -
      have "real_polynomial_function (λt. t * p t)"
        by (rule real_polynomial_function.intros(4)
            [OF real_polynomial_function.intros(1)[OF bounded_linear_ident] p])
      then have "((λy. inverse y * p (inverse y) * exp (- inverse y)) ⤏ 0) (at_right 0)"
        by (rule flat_poly_tendsto_zero)
      moreover have "∀F y in at_right 0.
          inverse y * p (inverse y) * exp (- inverse y) = (?G y - ?G 0) / (y - 0)"
        using eventually_at_right_less[of "0::real"]
        by eventually_elim (simp add: divide_inverse)
      ultimately show ?thesis
        by (rule Lim_transform_eventually)
    qed
    have left: "((λy. (?G y - ?G 0) / (y - 0)) ⤏ 0) (at_left 0)"
    proof -
      have "∀F y in at_left (0::real). y ∈ {-1<..<0}"
        by (rule eventually_at_left_real) simp
      then have "∀F y in at_left (0::real). y < 0"
        by eventually_elim simp
      then have "∀F y in at_left 0. 0 = (?G y - ?G 0) / (y - 0)"
        by eventually_elim simp
      with tendsto_const show ?thesis
        by (rule Lim_transform_eventually)
    qed
    have "(?G has_real_derivative 0) (at 0)"
      unfolding has_field_derivative_iff by (rule filterlim_split_at[OF left right])
    then show ?thesis
      using 2 by simp
  qed
qed

lemma exp_bump_deriv_formula:
  "∃p. real_polynomial_function p ∧
     (deriv ^^ n) exp_bump = (λx. if 0 < x then p (inverse x) * exp (- inverse x) else 0)"
proof (induction n)
  case 0
  have "exp_bump = (λx. if 0 < x then 1 * exp (- inverse x) else 0)"
    by (auto simp: exp_bump_def inverse_eq_divide)
  then show ?case
    by (intro exI[of _ "λ_. 1"]) auto
next
  case (Suc n)
  then obtain p where p: "real_polynomial_function p"
    and eq: "(deriv ^^ n) exp_bump = (λx. if 0 < x then p (inverse x) * exp (- inverse x) else 0)"
    by blast
  define q where "q t = t2 * (p t - deriv p t)" for t
  have q: "real_polynomial_function q"
    unfolding q_def
    by (intro real_polynomial_function.intros(4) real_polynomial_function_power
        real_polynomial_function_diff p deriv_real_polynomial_function[OF p]
        real_polynomial_function.intros(1)[OF bounded_linear_ident])
  have "(deriv ^^ Suc n) exp_bump =
      (λx. if 0 < x then q (inverse x) * exp (- inverse x) else 0)"
  proof
    fix x
    have "deriv ((deriv ^^ n) exp_bump) x = (if 0 < x then q (inverse x) * exp (- inverse x) else 0)"
      unfolding eq q_def by (rule DERIV_imp_deriv[OF flat_poly_has_derivative[OF p]])
    then show "(deriv ^^ Suc n) exp_bump x =
        (if 0 < x then q (inverse x) * exp (- inverse x) else 0)"
      by simp
  qed
  with q show ?case
    by blast
qed

lemma exp_bump_flat_deriv: "(deriv ^^ n) exp_bump 0 = 0"
  using exp_bump_deriv_formula[of n] by auto

lemma exp_bump_kth_deriv_differentiable: "(deriv ^^ n) exp_bump differentiable (at x)"
proof -
  obtain p where p: "real_polynomial_function p"
    and eq: "(deriv ^^ n) exp_bump = (λx. if 0 < x then p (inverse x) * exp (- inverse x) else 0)"
    using exp_bump_deriv_formula by blast
  show ?thesis
    unfolding eq using flat_poly_has_derivative[OF p] real_differentiable_def by blast
qed

lemma exp_bump_C_k_on: "C_k_on k exp_bump UNIV"
proof -
  have diff: "(deriv ^^ n) exp_bump differentiable_on UNIV" for n
    by (rule differentiable_at_imp_differentiable_on) (rule exp_bump_kth_deriv_differentiable)
  have cont: "continuous_on UNIV ((deriv ^^ n) exp_bump)" for n
    using diff[of n] by (rule differentiable_imp_continuous_on)
  show ?thesis
  proof (cases "k = 0")
    case True
    then show ?thesis
      using cont[of 0] by (simp add: C_k_on_def)
  next
    case False
    then show ?thesis
      using diff cont by (simp add: C_k_on_def del: funpow.simps)
  qed
qed


subsection ‹Smooth but not analytic›

theorem exp_bump_Cinfinity: "Cinfinity_on exp_bump UNIV"
proof -
  have "Ck_on k exp_bump UNIV" for k
    using exp_bump_C_k_on by (simp add: Ck_on_real_iff)
  then show ?thesis
    unfolding Cinfinity_on_def Cinfinity_at_def by (simp add: Ck_on_def)
qed

theorem exp_bump_not_real_analytic: "¬ real_analytic_on exp_bump (ball 0 1)"
proof
  assume A: "real_analytic_on exp_bump (ball (0::real) 1)"
  have "0 ∈ ball (0::real) 1" by simp
  with A have "real_analytic_at_1d exp_bump 0"
    using real_analytic_on_1d_iff[of exp_bump "ball 0 1"] by blast
  then obtain r where r: "0 < r"
    and TS: "⋀x. ¦x - 0¦ < r ⟹
              (λn. (deriv ^^ n) exp_bump 0 / fact n * (x - 0) ^ n) sums exp_bump x"
    unfolding real_analytic_at_1d_def by blast
  ― ‹A strictly positive point inside the convergence radius.›
  define x0 where "x0 = r / 2"
  have x0_pos: "0 < x0" using r by (simp add: x0_def)
  have "¦x0 - 0¦ = x0" using x0_pos by simp
  also have "x0 < r" using r unfolding x0_def by linarith
  finally have x0_lt: "¦x0 - 0¦ < r" .
  ― ‹All Taylor coefficients vanish, so the series is identically zero and sums to 0.›
  have "(λn. (deriv ^^ n) exp_bump 0 / fact n * (x0 - 0) ^ n) = (λn. 0)"
    by (simp add: exp_bump_flat_deriv)
  with TS[OF x0_lt] have z0: "(λn. (0::real)) sums exp_bump x0" by simp
  have "exp_bump x0 = 0"
    using sums_unique2[OF z0 sums_zero] by simp
  ― ‹But the function is strictly positive there.›
  moreover have "exp_bump x0 = exp (- (1 / x0))"
    using x0_pos by (simp add: exp_bump_def)
  ultimately show False by simp
qed

end