Theory Gamma_Asymptotics

(*
  File:    Gamma_Asymptotics.thy
  Author:  Manuel Eberl

  The complete asymptotics of the real and complex logarithmic Gamma functions.
  Also of the real Polygamma functions (could be extended to the complex ones fairly easily
  if needed).
*)
section ‹Complete asymptotics of the logarithmic Gamma function›
theory Gamma_Asymptotics
imports 
  "HOL-Complex_Analysis.Complex_Analysis"
  Bernoulli.Bernoulli_FPS 
  Bernoulli.Periodic_Bernpoly 
  Stirling_Formula
begin

subsection ‹Auxiliary Facts›

(* TODO: could be automated with Laurent series expansions in the future *)
lemma stirling_limit_aux1: 
  "((λy. Ln (1 + z * of_real y) / of_real y)  z) (at_right 0)" for z :: complex
proof (cases "z = 0")
  case True
  then show ?thesis by simp
next
  case False
  have "((λy. ln (1 + z * of_real y)) has_vector_derivative 1 * z) (at 0)"
    by (rule has_vector_derivative_real_field) (auto intro!: derivative_eq_intros)
  then have "(λy. (Ln (1 + z * of_real y) - of_real y * z) / of_real ¦y¦) 0 0"
    by (auto simp add: has_vector_derivative_def has_derivative_def netlimit_at 
          scaleR_conv_of_real field_simps)
  then have "((λy. (Ln (1 + z * of_real y) - of_real y * z) / of_real ¦y¦)  0) (at_right 0)"
    by (rule filterlim_mono[OF _ _ at_le]) simp_all
  also have "?this  ((λy. Ln (1 + z * of_real y) / (of_real y) - z)  0) (at_right 0)"
    using eventually_at_right_less[of "0::real"]
    by (intro filterlim_cong refl) (auto elim!: eventually_mono simp: field_simps)
  finally show ?thesis by (simp only: LIM_zero_iff)
qed
  
lemma stirling_limit_aux2: 
  "((λy. y * Ln (1 + z / of_real y))  z) at_top" for z :: complex
  using stirling_limit_aux1[of z] by (subst filterlim_at_top_to_right) (simp add: field_simps)

lemma Union_atLeastAtMost: 
  assumes "N > 0" 
  shows   "(n{0..<N}. {real n..real (n + 1)}) = {0..real N}"
proof (intro equalityI subsetI)
  fix x assume x: "x  {0..real N}"
  thus "x  (n{0..<N}. {real n..real (n + 1)})"
  proof (cases "x = real N")
    case True
    with assms show ?thesis by (auto intro!: bexI[of _ "N - 1"])
  next
    case False
    with x have x: "x  0" "x < real N" by simp_all
    hence "x  real (nat x)" "x  real (nat x + 1)" by linarith+
    moreover from x have "nat x < N" by linarith
    ultimately have "n{0..<N}. x  {real n..real (n + 1)}"
      by (intro bexI[of _ "nat x"]) simp_all
    thus ?thesis by blast
  qed
qed auto


subsection ‹Cones in the complex plane›

definition complex_cone :: "real  real  complex set" where
  "complex_cone a b = {z. y{a..b}. z = rcis (norm z) y}"

abbreviation complex_cone' :: "real  complex set" where
  "complex_cone' a  complex_cone (-a) a"

lemma zero_in_complex_cone [simp, intro]: "a  b  0  complex_cone a b"
  by (auto simp: complex_cone_def)

lemma complex_coneE:
  assumes "z  complex_cone a b"
  obtains r α where "r  0" "α  {a..b}" "z = rcis r α"
proof -
  from assms obtain y where "y  {a..b}" "z = rcis (norm z) y"
    unfolding complex_cone_def by auto
  thus ?thesis using that[of "norm z" y] by auto
qed

lemma arg_cis [simp]:
  assumes "x  {-pi<..pi}"
  shows   "Arg (cis x) = x"
  using assms by (intro cis_Arg_unique) auto

lemma arg_mult_of_real_left [simp]:
  assumes "r > 0"
  shows   "Arg (of_real r * z) = Arg z"
proof (cases "z = 0")
  case False
  thus ?thesis
    using Arg_bounded[of z] assms
    by (intro cis_Arg_unique) (auto simp: sgn_mult sgn_of_real cis_Arg)
qed auto

lemma arg_mult_of_real_right [simp]:
  assumes "r > 0"
  shows   "Arg (z * of_real r) = Arg z"
  by (subst mult.commute, subst arg_mult_of_real_left) (simp_all add: assms)

lemma arg_rcis [simp]:
  assumes "x  {-pi<..pi}" "r > 0"
  shows   "Arg (rcis r x) = x"
  using assms by (simp add: rcis_def)

lemma rcis_in_complex_cone [intro]: 
  assumes "α  {a..b}" "r  0"
  shows   "rcis r α  complex_cone a b"
  using assms by (auto simp: complex_cone_def)  

lemma arg_imp_in_complex_cone:
  assumes "Arg z  {a..b}"
  shows   "z  complex_cone a b"
proof -
  have "z = rcis (norm z) (Arg z)"
    by (simp add: rcis_cmod_Arg)
  also have "  complex_cone a b"
    using assms by auto
  finally show ?thesis .
qed

lemma complex_cone_altdef:
  assumes "-pi < a" "a  b" "b  pi"
  shows   "complex_cone a b = insert 0 {z. Arg z  {a..b}}"
proof (intro equalityI subsetI)
  fix z assume "z  complex_cone a b"
  then obtain r α where *: "r  0" "α  {a..b}" "z = rcis r α"
    by (auto elim: complex_coneE)
  have "Arg z  {a..b}" if [simp]: "z  0"
  proof -
    have "r > 0" using that * by (subst (asm) *) auto
    hence "α  {a..b}"
      using *(1,2) assms by (auto simp: *(1))
    moreover from assms *(2) have "α  {-pi<..pi}"
      by auto
    ultimately show ?thesis using *(3) r > 0
      by (subst *) auto
  qed
  thus "z  insert 0 {z. Arg z  {a..b}}"
    by auto
qed (use assms in auto intro: arg_imp_in_complex_cone)

lemma nonneg_of_real_in_complex_cone [simp, intro]:
  assumes "x  0" "a  0" "0  b"
  shows   "of_real x  complex_cone a b"
proof -
  from assms have "rcis x 0  complex_cone a b"
    by (intro rcis_in_complex_cone) auto
  thus ?thesis by simp
qed

lemma one_in_complex_cone [simp, intro]: "a  0  0  b  1  complex_cone a b"
  using nonneg_of_real_in_complex_cone[of 1] by (simp del: nonneg_of_real_in_complex_cone)

lemma of_nat_in_complex_cone [simp, intro]: "a  0  0  b  of_nat n  complex_cone a b"
  using nonneg_of_real_in_complex_cone[of "real n"] by (simp del: nonneg_of_real_in_complex_cone)


subsection ‹Another integral representation of the Beta function›

lemma complex_cone_inter_nonpos_Reals:
  assumes "-pi < a" "a  b" "b < pi"
  shows   "complex_cone a b  0 = {0}"
proof (safe elim!: nonpos_Reals_cases)
  fix x :: real
  assume "complex_of_real x  complex_cone a b" "x  0"
  hence "¬(x < 0)"
    using assms by (intro notI) (auto simp: complex_cone_altdef)
  with x  0 show "complex_of_real x = 0" by auto  
qed (use assms in auto)

theorem 
  assumes a: "a > 0" and b: "b > (0 :: real)"
  shows has_integral_Beta_real': 
          "((λu. u powr (b - 1) / (1 + u) powr (a + b)) has_integral Beta a b) {0<..}"
    and Beta_conv_nn_integral:
          "Beta a b = (+u. ennreal (indicator {0<..} u * u powr (b - 1) / (1 + u) powr (a + b)) lborel)"
proof -
  define I where 
    "I = (+u. ennreal (indicator {0<..} u * u powr (b - 1) / (1 + u) powr (a + b)) lborel)"
  have "Gamma (a + b) > 0" "Beta a b > 0"
    using assms by (simp_all add: add_pos_pos Beta_def)
  from a b have "ennreal (Gamma a * Gamma b) =
    (+ t. ennreal (indicator {0..} t * t powr (a - 1) / exp t) lborel) *
    (+ t. ennreal (indicator {0..} t * t powr (b - 1) / exp t) lborel)"
    by (subst ennreal_mult') (simp_all add: Gamma_conv_nn_integral_real)
  also have " = (+t. +u. ennreal (indicator {0..} t * t powr (a - 1) / exp t) *
                            ennreal (indicator {0..} u * u powr (b - 1) / exp u) lborel lborel)"
    by (simp add: nn_integral_cmult nn_integral_multc)
  also have " = (+t. indicator {0<..} t * (+u. indicator {0..} u * t powr (a - 1) * u powr (b - 1)
                            / exp (t + u) lborel) lborel)"
    by (intro nn_integral_cong_AE AE_I[of _ _ "{0}"])
       (auto simp: indicator_def divide_ennreal ennreal_mult' [symmetric] exp_add mult_ac)
  also have " = (+t. indicator {0<..} t * (+u. indicator {0..} u * t powr (a - 1) * u powr (b - 1)
                            / exp (t + u) 
                    (density (distr lborel borel ((*) t)) (λx. ennreal ¦t¦))) lborel)"
    by (intro nn_integral_cong mult_indicator_cong, subst lborel_distr_mult' [symmetric]) auto
  also have " = (+(t::real). indicator {0<..} t * (+u. 
                     indicator {0..} (u * t) * t powr a *
                     (u * t) powr (b - 1) / exp (t + t * u) lborel) lborel)"
    by (intro nn_integral_cong mult_indicator_cong)
       (auto simp: nn_integral_density nn_integral_distr algebra_simps powr_diff
             simp flip: ennreal_mult)
  also have " = (+(t::real). +u. indicator ({0<..}×{0..}) (t, u) *
                     t powr a * (u * t) powr (b - 1) / exp (t * (u + 1)) lborel lborel)"
    by (subst nn_integral_cmult [symmetric], simp, intro nn_integral_cong)
       (auto simp: indicator_def zero_le_mult_iff algebra_simps)
  also have " = (+(t::real). +u. indicator ({0<..}×{0..}) (t, u) *
                     t powr (a + b - 1) * u powr (b - 1) / exp (t * (u + 1)) lborel lborel)"
    by (intro nn_integral_cong) (auto simp: powr_add powr_diff indicator_def powr_mult field_simps)
  also have " = (+(u::real). +t. indicator ({0<..}×{0..}) (t, u) *
                     t powr (a + b - 1) * u powr (b - 1) / exp (t * (u + 1)) lborel lborel)"
    by (rule lborel_pair.Fubini') auto
  also have " = (+(u::real). indicator {0..} u * (+t. indicator {0<..} t *
                     t powr (a + b - 1) * u powr (b - 1) / exp (t * (u + 1)) lborel) lborel)"
    by (intro nn_integral_cong mult_indicator_cong) (auto simp: indicator_def)
  also have " = (+(u::real). indicator {0<..} u * (+t. indicator {0<..} t *
                     t powr (a + b - 1) * u powr (b - 1) / exp (t * (u + 1)) lborel) lborel)"
    by (intro nn_integral_cong_AE AE_I[of _ _ "{0}"]) (auto simp: indicator_def)
  also have " = (+(u::real). indicator {0<..} u * (+t. indicator {0<..} t *
                     t powr (a + b - 1) * u powr (b - 1) / exp (t * (u + 1)) 
                    (density (distr lborel borel ((*) (1/(1+u)))) (λx. ennreal ¦1/(1+u)¦))) lborel)"
    by (intro nn_integral_cong mult_indicator_cong, subst lborel_distr_mult' [symmetric]) auto
  also have " = (+(u::real). indicator {0<..} u * 
                    (+t. ennreal (1 / (u + 1)) * ennreal (indicator {0<..} (t / (u + 1)) *
                     (t / (1+u)) powr (a + b - 1) * u powr (b - 1) / exp t)
                    lborel) lborel)"
    by (intro nn_integral_cong mult_indicator_cong)
       (auto simp: nn_integral_distr nn_integral_density add_ac)
  also have " = (+u. +t. indicator ({0<..}×{0<..}) (u, t) * 
                    1/(u+1) * (t / (u+1)) powr (a + b - 1) * u powr (b - 1) / exp t
                    lborel lborel)"
    by (subst nn_integral_cmult [symmetric], simp, intro nn_integral_cong)
       (auto simp: indicator_def field_simps divide_ennreal simp flip: ennreal_mult ennreal_mult')
  also have " = (+u. +t. ennreal (indicator {0<..} u * u powr (b - 1) / (1 + u) powr (a + b)) *
                            ennreal (indicator {0<..} t * t powr (a + b - 1) / exp t)
                    lborel lborel)"
    by (intro nn_integral_cong)
       (auto simp: indicator_def powr_add powr_diff powr_divide powr_minus divide_simps add_ac
             simp flip: ennreal_mult)
  also have " = I * (+t. indicator {0<..} t * t powr (a + b - 1) / exp t lborel)"
    by (simp add: nn_integral_cmult nn_integral_multc I_def)
  also have "(+t. indicator {0<..} t * t powr (a + b - 1) / exp t lborel) =
               ennreal (Gamma (a + b))"
    using assms
    by (subst Gamma_conv_nn_integral_real)
       (auto intro!: nn_integral_cong_AE[OF AE_I[of _ _ "{0}"]] 
             simp: indicator_def split: if_splits split_of_bool_asm)
  finally have "ennreal (Gamma a * Gamma b) = I * ennreal (Gamma (a + b))" .
  hence "ennreal (Gamma a * Gamma b) / ennreal (Gamma (a + b)) =
           I * ennreal (Gamma (a + b)) / ennreal (Gamma (a + b))" by simp
  also have " = I"
    using Gamma (a + b) > 0 by (intro ennreal_mult_divide_eq) auto
  also have "ennreal (Gamma a * Gamma b) / ennreal (Gamma (a + b)) =
               ennreal (Gamma a * Gamma b / Gamma (a + b))"
    using assms by (intro divide_ennreal) auto
  also have " = ennreal (Beta a b)"
    by (simp add: Beta_def)
  finally show *: "ennreal (Beta a b) = I" .

  define f where "f = (λu. u powr (b - 1) / (1 + u) powr (a + b))"
  have "((λu. indicator {0<..} u * f u) has_integral Beta a b) UNIV"
    using * Beta a b > 0
    by (subst has_integral_iff_nn_integral_lebesgue)
       (auto simp: f_def measurable_completion nn_integral_completion I_def mult_ac)
  also have "(λu. indicator {0<..} u * f u) = (λu. if u  {0<..} then f u else 0)"
    by (auto simp: fun_eq_iff)
  also have "( has_integral Beta a b) UNIV  (f has_integral Beta a b) {0<..}"
    by (rule has_integral_restrict_UNIV)
  finally show  by (simp add: f_def)
qed

lemma has_integral_Beta2:
  fixes a :: real
  assumes "a < -1/2"
  shows   "((λx. (1 + x ^ 2) powr a) has_integral Beta (- a - 1 / 2) (1 / 2) / 2) {0<..}"
proof -
  define f where "f = (λu. u powr (-1/2) / (1 + u) powr (-a))"
  define C where "C = Beta (-a-1/2) (1/2)"
  have I: "(f has_integral C) {0<..}"
    using has_integral_Beta_real'[of "-a-1/2" "1/2"] assms
    by (simp_all add: diff_divide_distrib f_def C_def)

  define g where "g = (λx. x ^ 2 :: real)"
  have bij: "bij_betw g {0<..} {0<..}"
    by (intro bij_betwI[of _ _ _ sqrt]) (auto simp: g_def)

  have "(f absolutely_integrable_on g ` {0<..}  integral (g ` {0<..}) f = C)"
    using I bij by (simp add: bij_betw_def has_integral_iff absolutely_integrable_on_def f_def)
  also have "?this  ((λx. ¦2 * x¦ *R f (g x)) absolutely_integrable_on {0<..} 
                         integral {0<..} (λx. ¦2 * x¦ *R f (g x)) = C)"
    using bij by (intro has_absolute_integral_change_of_variables_1' [symmetric])
                 (auto intro!: derivative_eq_intros simp: g_def bij_betw_def)
  finally have "((λx. ¦2 * x¦ * f (g x)) has_integral C) {0<..}"
    by (simp add: absolutely_integrable_on_def f_def has_integral_iff)
  also have "?this  ((λx::real. 2 * (1 + x2) powr a) has_integral C) {0<..}"
    by (intro has_integral_cong) (auto simp: f_def g_def powr_def exp_minus ln_realpow field_simps)
  finally have "((λx::real. 1/2 * (2 * (1 + x2) powr a)) has_integral 1/2 * C) {0<..}"
    by (intro has_integral_mult_right)
  thus ?thesis by (simp add: C_def)
qed

lemma has_integral_Beta3:
  fixes a b :: real
  assumes "a < -1/2" and "b > 0"
  shows   "((λx. (b + x ^ 2) powr a) has_integral
             Beta (-a - 1/2) (1/2) / 2 * b powr (a + 1/2)) {0<..}"
proof -
  define C where "C = Beta (- a - 1 / 2) (1 / 2) / 2"
  have int: "nn_integral lborel (λx. indicator {0<..} x * (1 + x ^ 2) powr a) = C"
    using nn_integral_has_integral_lebesgue[OF _ has_integral_Beta2[OF assms(1)]]
    by (auto simp: C_def)
  have "nn_integral lborel (λx. indicator {0<..} x * (b + x ^ 2) powr a) =
        (+x. ennreal (indicat_real {0<..} (x * sqrt b) * (b + (x * sqrt b)2) powr a * sqrt b) lborel)"
    using assms
    by (subst lborel_distr_mult'[of "sqrt b"])
       (auto simp: nn_integral_density nn_integral_distr mult_ac simp flip: ennreal_mult)
  also have " = (+x. ennreal (indicat_real {0<..} x * (b * (1 + x ^ 2)) powr a * sqrt b) lborel)"
    using assms
    by (intro nn_integral_cong) (auto simp: indicator_def field_simps zero_less_mult_iff)
  also have " = (+x. ennreal (indicat_real {0<..} x * b powr (a + 1/2) * (1 + x ^ 2) powr a) lborel)"
    using assms
    by (intro nn_integral_cong) (auto simp: indicator_def powr_add powr_half_sqrt powr_mult)    
  also have " = b powr (a + 1/2) * (+x. ennreal (indicat_real {0<..} x * (1 + x ^ 2) powr a) lborel)"
    using assms by (subst nn_integral_cmult [symmetric]) (simp_all add: mult_ac flip: ennreal_mult)
  also have "(+x. ennreal (indicat_real {0<..} x * (1 + x ^ 2) powr a) lborel) = C"
    using int by simp
  also have "ennreal (b powr (a + 1/2)) * ennreal C = ennreal (C * b powr (a + 1/2))"
    using assms by (subst ennreal_mult) (auto simp: C_def mult_ac Beta_def)
  finally have *: "(+ x. ennreal (indicat_real {0<..} x * (b + x2) powr a) lborel) = " .
  hence "((λx. indicator {0<..} x * (b + x^2) powr a) has_integral C * b powr (a + 1/2)) UNIV"
    using assms
    by (subst has_integral_iff_nn_integral_lebesgue)
       (auto simp: C_def measurable_completion nn_integral_completion Beta_def)
  also have "(λx. indicator {0<..} x * (b + x^2) powr a) =
             (λx. if x  {0<..} then (b + x^2) powr a else 0)"
    by (auto simp: fun_eq_iff)
  finally show ?thesis
    by (subst (asm) has_integral_restrict_UNIV) (auto simp: C_def)
qed

  
subsection ‹Asymptotics of the real $\log\Gamma$ function and its derivatives›

text ‹
  This is the error term that occurs in the expansion of @{term ln_Gamma}. It can be shown to 
  be of order $O(s^{-n})$.
›
definition stirling_integral :: "nat  'a :: {real_normed_div_algebra, banach}  'a" where
  "stirling_integral n s = 
     lim (λN. integral {0..N} (λx. of_real (pbernpoly n x) / (of_real x + s) ^ n))"

context
  fixes s :: complex assumes s: "s  0"
  fixes approx :: "nat  complex"
  defines "approx  (λN. 
    (n = 1..<N. s / of_nat n - ln (1 + s / of_nat n)) - (euler_mascheroni * s + ln s) - ― ‹⟶ ln_Gamma s›
    (ln_Gamma (of_nat N) - ln (2 * pi / of_nat N) / 2 - of_nat N * ln (of_nat N) + of_nat N) - ― ‹⟶ 0›
    s * (harm (N - 1) - ln (of_nat (N - 1)) - euler_mascheroni) + ― ‹⟶ 0›
    s * (ln (of_nat N + s) - ln (of_nat (N - 1))) - ― ‹⟶ 0›
    (1/2) * (ln (of_nat N + s) - ln (of_nat N)) +       ― ‹⟶ 0›
    of_nat N * (ln (of_nat N + s) - ln (of_nat N)) -  ― ‹⟶ s›
    (s - 1/2) * ln s - ln (2 * pi) / 2)"
begin       
  
qualified lemma
  assumes N: "N > 0"
  shows   integrable_pbernpoly_1:
            "(λx. of_real (-pbernpoly 1 x) / (of_real x + s)) integrable_on {0..real N}"
  and     integral_pbernpoly_1_aux:
            "integral {0..real N} (λx. -of_real (pbernpoly 1 x) / (of_real x + s)) = approx N"
  and     has_integral_pbernpoly_1:
            "((λx. pbernpoly 1 x /(x + s)) has_integral 
              (m<N. (of_nat m + 1 / 2 + s) * (ln (of_nat m + s) - 
                        ln (of_nat m + 1 + s)) + 1)) {0..real N}"
proof -
  let ?A = "(λn. {of_nat n..of_nat (n+1)}) ` {0..<N}"
  have has_integral: 
    "((λx. -pbernpoly 1 x / (x + s)) has_integral 
             (of_nat n + 1/2 + s) * (ln (of_nat (n + 1) + s) - ln (of_nat n + s)) - 1) 
           {of_nat n..of_nat (n + 1)}" for n
  proof (rule has_integral_spike)      
    have "((λx. (of_nat n + 1/2 + s) * (1 / (of_real x + s)) - 1) has_integral 
              (of_nat n + 1/2 + s) * (ln (of_real (real (n + 1)) + s) - ln (of_real (real n) + s)) - 1) 
            {of_nat n..of_nat (n + 1)}" 
      using s has_integral_const_real[of 1 "of_nat n" "of_nat (n + 1)"]
      by (intro has_integral_diff has_integral_mult_right fundamental_theorem_of_calculus)
         (auto intro!: derivative_eq_intros has_vector_derivative_real_field
               simp: has_real_derivative_iff_has_vector_derivative [symmetric] field_simps
                     complex_nonpos_Reals_iff)
    thus "((λx. (of_nat n + 1/2 + s) * (1 / (of_real x + s)) - 1) has_integral 
              (of_nat n + 1/2 + s) * (ln (of_nat (n + 1) + s) - ln (of_nat n + s)) - 1) 
            {of_nat n..of_nat (n + 1)}" by simp
             
    show "-pbernpoly 1 x / (x + s) = (of_nat n + 1/2 + s) * (1 / (x + s)) - 1"
         if "x  {of_nat n..of_nat (n + 1)} - {of_nat (n + 1)}" for x
    proof -
      have x: "x  real n" "x < real (n + 1)" using that by simp_all
      hence "floor x = int n" by linarith
      moreover from s x have "complex_of_real x  -s" 
        by (auto simp add: complex_eq_iff complex_nonpos_Reals_iff simp del: of_nat_Suc)
      ultimately show "-pbernpoly 1 x / (x + s) = (of_nat n + 1/2 + s) * (1 / (x + s)) - 1"
        by (auto simp: pbernpoly_def bernpoly_def frac_def divide_simps add_eq_0_iff2)
    qed
  qed simp_all
  hence *: "I. I?A  ((λx. -pbernpoly 1 x / (x + s)) has_integral 
              (Inf I + 1/2 + s) * (ln (Inf I + 1 + s) - ln (Inf I + s)) - 1) I"
    by (auto simp: add_ac)
  have "((λx. - pbernpoly 1 x / (x + s)) has_integral
          (I?A. (Inf I + 1 / 2 + s) * (ln (Inf I + 1 + s) - ln (Inf I + s)) - 1))
          (n{0..<N}. {real n..real (n + 1)})" (is "(_ has_integral ?i) _")
    apply (intro has_integral_Union * finite_imageI)
      apply (force intro!: negligible_atLeastAtMostI pairwiseI)+
    done
  hence has_integral: "((λx. - pbernpoly 1 x / (x + s)) has_integral ?i) {0..real N}"
    by (subst has_integral_spike_set_eq)
       (use Union_atLeastAtMost assms in auto simp: intro!: empty_imp_negligible)
  hence "(λx. - pbernpoly 1 x / (x + s)) integrable_on {0..real N}"
    and integral:   "integral {0..real N} (λx. - pbernpoly 1 x / (x + s)) = ?i"
    by (simp_all add: has_integral_iff)
  show "(λx. - pbernpoly 1 x / (x + s)) integrable_on {0..real N}" by fact

  note has_integral_neg[OF has_integral]
  also have "-?i = (x<N. (of_nat x + 1 / 2 + s) * (ln (of_nat x + s) - ln (of_nat x + 1 + s)) + 1)" 
    by (subst sum.reindex) 
       (simp_all add: inj_on_def atLeast0LessThan algebra_simps sum_negf [symmetric])
  finally show has_integral: 
    "((λx. of_real (pbernpoly 1 x) / (of_real x + s)) has_integral ) {0..real N}" by simp
      
  note integral
  also have "?i = (n<N. (of_nat n + 1 / 2 + s) * 
                    (ln (of_nat n + 1 + s) - ln (of_nat n + s))) - N" (is "_ = ?S - _")
    by (subst sum.reindex) (simp_all add: inj_on_def sum_subtractf atLeast0LessThan)
  also have "?S = (n<N. of_nat n * (ln (of_nat n + 1 + s) - ln (of_nat n + s))) +
                    (s + 1 / 2) * (n<N. ln (of_nat (Suc n) + s) - ln (of_nat n + s))" 
    (is "_ = ?S1 + _ * ?S2") by (simp add: algebra_simps sum.distrib sum_subtractf sum_distrib_left)
  also have "?S2 = ln (of_nat N + s) - ln s" by (subst sum_lessThan_telescope) simp
  also have "?S1 = (n=1..<N. of_nat n * (ln (of_nat n + 1 + s) - ln (of_nat n + s)))"
    by (intro sum.mono_neutral_right) auto
  also have " = (n=1..<N. of_nat n * ln (of_nat n + 1 + s)) - (n=1..<N. of_nat n * ln (of_nat n + s))"
    by (simp add: algebra_simps sum_subtractf)
  also have "(n=1..<N. of_nat n * ln (of_nat n + 1 + s)) = 
               (n=1..<N. (of_nat n - 1) * ln (of_nat n + s)) + (N - 1) * ln (of_nat N + s)"
    by (induction N) (simp_all add: add_ac of_nat_diff)
  also have " - (n = 1..<N. of_nat n * ln (of_nat n + s)) =
               -(n=1..<N. ln (of_nat n + s)) + (N - 1) * ln (of_nat N + s)"
    by (induction N) (simp_all add: algebra_simps)
  also from s have neq: "s + of_nat x  0" for x
    by (auto simp:  complex_nonpos_Reals_iff complex_eq_iff)
  hence "(n=1..<N. ln (of_nat n + s)) = (n=1..<N. ln (of_nat n) + ln (1 + s/n))"
    by (intro sum.cong refl, subst Ln_times_of_nat [symmetric]) (auto simp: divide_simps add_ac)
  also have " = ln (fact (N - 1)) + (n=1..<N. ln (1 + s/n))"
    by (induction N) (simp_all add: Ln_times_of_nat fact_reduce add_ac)
  also have "(n=1..<N. ln (1 + s/n)) = -(n=1..<N. s / n - ln (1 + s/n)) + s * (n=1..<N. 1 / of_nat n)"
    by (simp add: sum_distrib_left sum_subtractf) 
  also from N have "ln (fact (N - 1)) = ln_Gamma (of_nat N :: complex)" 
    by (simp add: ln_Gamma_complex_conv_fact)
  also have "{1..<N} = {1..N - 1}" by auto
  hence "(n = 1..<N. 1 / of_nat n) = (harm (N - 1) :: complex)" 
    by (simp add: harm_def divide_simps)
  also have "- (ln_Gamma (of_nat N) + (- (n = 1..<N. s / of_nat n - ln (1 + s / of_nat n)) +
                 s * harm (N - 1))) + of_nat (N - 1) * ln (of_nat N + s) +
                (s + 1 / 2) * (ln (of_nat N + s) - ln s) - of_nat N = approx N"
    using N by (simp add: field_simps of_nat_diff ln_div approx_def Ln_of_nat 
                          ln_Gamma_complex_of_real [symmetric])
  finally show "integral {0..of_nat N} (λx. -of_real (pbernpoly 1 x) / (of_real x + s)) = " 
    by simp
qed
  
lemma integrable_ln_Gamma_aux:
  shows   "(λx. of_real (pbernpoly n x) / (of_real x + s) ^ n) integrable_on {0..real N}"
proof (cases "n = 1")
  case True
  with s show ?thesis using integrable_neg[OF integrable_pbernpoly_1[of N]] 
    by (cases "N = 0") (simp_all add: integrable_negligible)
next
  case False
  from s have "of_real x + s  0" if "x  0" for x using that 
    by (auto simp: complex_eq_iff add_eq_0_iff2 complex_nonpos_Reals_iff)
  with False s show ?thesis
    by (auto intro!: integrable_continuous_real continuous_intros)
qed
  
text ‹
  This following proof is based on ``Rudiments of the theory of the gamma function'' 
  by Bruce Berndt~cite"berndt".
›
lemma tendsto_of_real_0_I: 
  "(f  0) G  ((λx. (of_real (f x)))  (0 ::'a::real_normed_div_algebra)) G"
  using tendsto_of_real_iff by force

qualified lemma integral_pbernpoly_1:
  "(λN. integral {0..real N} (λx. pbernpoly 1 x / (x + s)))
      -ln_Gamma s - s + (s - 1 / 2) * ln s + ln (2 * pi) / 2"
proof -  
  have neq: "s + of_real x  0" if "x  0" for x :: real
    using that s by (auto simp: complex_eq_iff complex_nonpos_Reals_iff)
  have "(approx  ln_Gamma s - 0 - 0 + 0 - 0 + s - (s - 1/2) * ln s - ln (2 * pi) / 2) at_top"
    unfolding approx_def
  proof (intro tendsto_add tendsto_diff)
    from s have s': "s  0" by (auto simp: complex_nonpos_Reals_iff elim!: nonpos_Ints_cases)
    have "(λn. i=1..<n. s / of_nat i - ln (1 + s / of_nat i))  
             ln_Gamma s + euler_mascheroni * s + ln s" (is "?f  _")
      using ln_Gamma_series'_aux[OF s'] unfolding sums_def 
      by (subst filterlim_sequentially_Suc [symmetric], subst (asm) sum.atLeast1_atMost_eq [symmetric]) 
         (simp add: atLeastLessThanSuc_atLeastAtMost)
    thus "((λn. ?f n - (euler_mascheroni * s + ln s))  ln_Gamma s) at_top"
      by (auto intro: tendsto_eq_intros)
  next
    show "(λx. complex_of_real (ln_Gamma (real x) - ln (2 * pi / real x) / 2 - 
                 real x * ln (real x) + real x))  0"
    proof (intro tendsto_of_real_0_I 
             filterlim_compose[OF tendsto_sandwich filterlim_real_sequentially])
      show "eventually (λx::real. ln_Gamma x - ln (2 * pi / x) / 2 - x * ln x + x  0) at_top"
        using eventually_ge_at_top[of "1::real"] 
        by eventually_elim (insert ln_Gamma_bounds(1), simp add: algebra_simps)
      show "eventually (λx::real. ln_Gamma x - ln (2 * pi / x) / 2 - x * ln x + x  
              1 / 12 * inverse x) at_top"
        using eventually_ge_at_top[of "1::real"] 
        by eventually_elim (insert ln_Gamma_bounds(2), simp add: field_simps)
      show "((λx::real. 1 / 12 * inverse x)  0) at_top"
        by real_asymp
    qed simp_all
  next
    have "(λx. s * of_real (harm (x - 1) - ln (real (x - 1)) - euler_mascheroni))  
            s * of_real (euler_mascheroni - euler_mascheroni)"
      by (subst filterlim_sequentially_Suc [symmetric], intro tendsto_intros) 
         (insert euler_mascheroni_LIMSEQ, simp_all)
    also have "?this  (λx. s * (harm (x - 1) - ln (of_nat (x - 1)) - euler_mascheroni))  0"
      by (intro filterlim_cong refl eventually_mono[OF eventually_gt_at_top[of "1::nat"]]) 
         (auto simp: Ln_of_nat of_real_harm)
    finally show "(λx. s * (harm (x - 1) - ln (of_nat (x - 1)) - euler_mascheroni))  0"  .
  next
    have "((λx. ln (1 + (s + 1) / of_real x))  ln (1 + 0)) at_top" (is ?P)
      by (intro tendsto_intros tendsto_divide_0[OF tendsto_const]) 
         (simp_all add: filterlim_ident filterlim_at_infinity_conv_norm_at_top filterlim_abs_real)
    also have "ln (of_real (x + 1) + s) - ln (complex_of_real x) = ln (1 + (s + 1) / of_real x)" 
      if "x > 1" for x using that s
      using Ln_divide_of_real[of x "of_real (x + 1) + s", symmetric] neq[of "x+1"]
      by (simp add: field_simps Ln_of_real)
    hence "?P  ((λx. ln (of_real (x + 1) + s) - ln (of_real x))  0) at_top"
      by (intro filterlim_cong refl) 
         (auto intro: eventually_mono[OF eventually_gt_at_top[of "1::real"]])
    finally have "((λn. ln (of_real (real n + 1) + s) - ln (of_real (real n)))  0) at_top"
      by (rule filterlim_compose[OF _ filterlim_real_sequentially])
    hence "((λn. ln (of_nat n + s) - ln (of_nat (n - 1)))  0) at_top"
      by (subst filterlim_sequentially_Suc [symmetric]) (simp add: add_ac)
    thus "(λx. s * (ln (of_nat x + s) - ln (of_nat (x - 1))))  0"
      by (rule tendsto_mult_right_zero)
  next
    have "((λx. ln (1 + s / of_real x))  ln (1 + 0)) at_top" (is ?P)
      by (intro tendsto_intros tendsto_divide_0[OF tendsto_const]) 
         (simp_all add: filterlim_ident  filterlim_at_infinity_conv_norm_at_top filterlim_abs_real)
    also have "ln (of_real x + s) - ln (of_real x) = ln (1 + s / of_real x)" if "x > 0" for x
      using Ln_divide_of_real[of x "of_real x + s"] neq[of x] that
      by (auto simp: field_simps Ln_of_real)
    hence "?P  ((λx. ln (of_real x + s) - ln (of_real x))  0) at_top"
      using s by (intro filterlim_cong refl) 
                 (auto intro: eventually_mono [OF eventually_gt_at_top[of "1::real"]])
    finally have "(λx. (1/2) * (ln (of_real (real x) + s) - ln (of_real (real x))))  0"        
      by (rule tendsto_mult_right_zero[OF filterlim_compose[OF _ filterlim_real_sequentially]])
    thus "(λx. (1/2) * (ln (of_nat x + s) - ln (of_nat x)))  0" by simp
  next
    have "((λx. x * (ln (1 + s / of_real x)))  s) at_top" (is ?P) 
      by (rule stirling_limit_aux2)
    also have "ln (1 + s / of_real x) = ln (of_real x + s) - ln (of_real x)" if "x > 1" for x 
      using that s Ln_divide_of_real [of x "of_real x + s", symmetric] neq[of x]
      by (auto simp: Ln_of_real field_simps)
    hence "?P  ((λx. of_real x * (ln (of_real x + s) - ln (of_real x)))  s) at_top"
      by (intro filterlim_cong refl) 
         (auto intro: eventually_mono[OF eventually_gt_at_top[of "1::real"]])
    finally have "(λn. of_real (real n) * (ln (of_real (real n) + s) - ln (of_real (real n))))  s"
      by (rule filterlim_compose[OF _ filterlim_real_sequentially])
    thus "(λn. of_nat n * (ln (of_nat n + s) - ln (of_nat n)))  s" by simp
  qed simp_all
  also have "?this  ((λN. integral {0..real N} (λx. -pbernpoly 1 x / (x + s))) 
                         ln_Gamma s + s - (s - 1/2) * ln s - ln (2 * pi) / 2) at_top"
    using integral_pbernpoly_1_aux
    by (intro filterlim_cong refl) 
       (auto intro: eventually_mono[OF eventually_gt_at_top[of "0::nat"]])
  also have "(λN. integral {0..real N} (λx. -pbernpoly 1 x / (x + s))) =
               (λN. -integral {0..real N} (λx. pbernpoly 1 x / (x + s)))"
    by (simp add: fun_eq_iff)
  finally show ?thesis by (simp add: tendsto_minus_cancel_left [symmetric] algebra_simps)
qed


qualified lemma pbernpoly_integral_conv_pbernpoly_integral_Suc:
  assumes "n  1"
  shows   "integral {0..real N} (λx. pbernpoly n x / (x + s) ^ n) =
             of_real (pbernpoly (Suc n) (real N)) / (of_nat (Suc n) * (s + of_nat N) ^ n) -
             of_real (bernoulli (Suc n)) / (of_nat (Suc n) * s ^ n) + of_nat n / of_nat (Suc n) * 
               integral {0..real N} (λx. of_real (pbernpoly (Suc n) x) / (of_real x + s) ^ Suc n)"
proof - 
  note [derivative_intros] = has_field_derivative_pbernpoly_Suc'
  define I where "I = -of_real (pbernpoly (Suc n) (of_nat N)) / (of_nat (Suc n) * (of_nat N + s) ^ n) +
            of_real (bernoulli (Suc n) / real (Suc n)) / s ^ n +
            integral {0..real N} (λx. of_real (pbernpoly n x) / (of_real x + s) ^ n)"
  have "((λx. (-of_nat n * inverse (of_real x + s) ^ Suc n) * 
          (of_real (pbernpoly (Suc n) x) / (of_nat (Suc n))))
          has_integral -I) {0..real N}"
  proof (rule integration_by_parts_interior_strong[OF bounded_bilinear_mult])
    fix x :: real assume x: "x  {0<..<real N} - real ` {0..N}"
    have "x  "
    proof
      assume "x  "
      then obtain n where "x = of_int n" by (auto elim!: Ints_cases)
      with x have x': "x = of_nat (nat n)" by simp
      from x show False by (auto simp: x')
    qed
    hence "((λx. of_real (pbernpoly (Suc n) x / of_nat (Suc n))) has_vector_derivative
        complex_of_real (pbernpoly n x)) (at x)"
      by (intro has_vector_derivative_of_real) (auto intro!: derivative_eq_intros)
    thus "((λx. of_real (pbernpoly (Suc n) x) / of_nat (Suc n)) has_vector_derivative
            complex_of_real (pbernpoly n x)) (at x)" by simp
    from x s have "complex_of_real x + s  0"
      by (auto simp: complex_eq_iff complex_nonpos_Reals_iff)
    thus "((λx. inverse (of_real x + s) ^ n) has_vector_derivative 
             - of_nat n * inverse (of_real x + s) ^ Suc n) (at x)" using x s assms
      by (auto intro!: derivative_eq_intros has_vector_derivative_real_field simp: divide_simps power_add [symmetric]
               simp del: power_Suc)
  next
    have "complex_of_real x + s  0" if "x  0" for x 
      using that s by (auto simp: complex_eq_iff complex_nonpos_Reals_iff)
    thus "continuous_on {0..real N} (λx. inverse (of_real x + s) ^ n)" 
         "continuous_on {0..real N} (λx. complex_of_real (pbernpoly (Suc n) x) / of_nat (Suc n))"
      using assms s by (auto intro!: continuous_intros simp del: of_nat_Suc)
  next
    have "((λx. inverse (of_real x + s) ^ n * of_real (pbernpoly n x)) has_integral
            pbernpoly (Suc n) (of_nat N) / (of_nat (Suc n) * (of_nat N + s) ^ n) -
            of_real (bernoulli (Suc n) / real (Suc n)) / s ^ n - -I) {0..real N}" 
      using integrable_ln_Gamma_aux[of n N] assms
      by (auto simp: I_def has_integral_integral divide_simps)
    thus "((λx. inverse (of_real x + s) ^ n * of_real (pbernpoly n x)) has_integral
              inverse (of_real (real N) + s) ^ n * (of_real (pbernpoly (Suc n) (real N)) / 
                  of_nat (Suc n)) -
              inverse (of_real 0 + s) ^ n * (of_real (pbernpoly (Suc n) 0) / of_nat (Suc n)) - - I)
            {0..real N}" by (simp_all add: field_simps)
  qed simp_all
  also have "(λx. - of_nat n * inverse (of_real x + s) ^ Suc n * (of_real (pbernpoly (Suc n) x) /
                         of_nat (Suc n))) =
             (λx. - (of_nat n / of_nat (Suc n) * of_real (pbernpoly (Suc n) x) / 
                         (of_real x + s) ^ Suc n))"
    by (simp add: divide_simps fun_eq_iff)
  finally have "((λx. - (of_nat n / of_nat (Suc n) * of_real (pbernpoly (Suc n) x) /
                            (of_real x + s) ^ Suc n)) has_integral - I) {0..real N}" .
  from has_integral_neg[OF this] show ?thesis
    by (auto simp add: I_def has_integral_iff algebra_simps integral_mult_right [symmetric] 
             simp del: power_Suc of_nat_Suc )
qed

lemma pbernpoly_over_power_tendsto_0: 
  assumes "n > 0"
  shows   "(λx. of_real (pbernpoly (Suc n) (real x)) / (of_nat (Suc n) * (s + of_nat x) ^ n))  0"
proof -
  from s have neq: "s + of_nat n  0" for n
    by (auto simp: complex_eq_iff complex_nonpos_Reals_iff)
  obtain c where c: "x. norm (pbernpoly (Suc n) x)  c"
    using bounded_pbernpoly by auto
  have "eventually (λx. real x + Re s > 0) at_top"
    by real_asymp
  hence "eventually (λx. norm (of_real (pbernpoly (Suc n) (real x)) / 
                                    (of_nat (Suc n) * (s + of_nat x) ^ n)) 
                          (c / real (Suc n)) / (real x + Re s) ^ n) at_top"
    using eventually_gt_at_top[of "0::nat"]
  proof eventually_elim
    case (elim x)
    have "norm (of_real (pbernpoly (Suc n) (real x)) / 
                                    (of_nat (Suc n) * (s + of_nat x) ^ n)) 
            (c / real (Suc n)) / norm (s + of_nat x) ^ n" (is "_  ?rhs") using c[of x]
      by (auto simp: norm_divide norm_mult norm_power neq field_simps simp del: of_nat_Suc)
    also have "(real x + Re s)  cmod (s + of_nat x)"
      using complex_Re_le_cmod[of "s + of_nat x"] s by (auto simp add: complex_nonpos_Reals_iff)
    hence "?rhs  (c / real (Suc n)) / (real x + Re s) ^ n" using s elim c[of 0] neq[of x]
      by (intro divide_left_mono power_mono mult_pos_pos divide_nonneg_pos zero_less_power) auto
    finally show ?case .
  qed 
  moreover have "(λx. (c / real (Suc n)) / (real x + Re s) ^ n)  0"
    using n > 0 by real_asymp
  ultimately show ?thesis by (rule Lim_null_comparison)
qed

lemma convergent_stirling_integral:
  assumes "n > 0"
  shows   "convergent (λN. integral {0..real N} 
             (λx. of_real (pbernpoly n x) / (of_real x + s) ^ n))" (is "convergent (?f n)")
proof -
  have "convergent (?f (Suc n))" for n
  proof (induction n)
    case 0
    thus ?case using integral_pbernpoly_1 by (auto intro!: convergentI)
  next
    case (Suc n)
    have "convergent (λN. ?f (Suc n) N -
            of_real (pbernpoly (Suc (Suc n)) (real N)) / 
                (of_nat (Suc (Suc n)) * (s + of_nat N) ^ Suc n) +
            of_real (bernoulli (Suc (Suc n)) / (real (Suc (Suc n)))) / s ^ Suc n)" 
      (is "convergent ?g")
      by (intro convergent_add convergent_diff Suc 
            convergent_const convergentI[OF pbernpoly_over_power_tendsto_0]) simp_all
    also have "?g = (λN. of_nat (Suc n) / of_nat (Suc (Suc n)) * ?f (Suc (Suc n)) N)" using s
      by (subst pbernpoly_integral_conv_pbernpoly_integral_Suc) 
         (auto simp: fun_eq_iff field_simps simp del: of_nat_Suc power_Suc)
    also have "convergent   convergent (?f (Suc (Suc n)))"
      by (intro convergent_mult_const_iff) (simp_all del: of_nat_Suc)
    finally show ?case .
  qed
  from this[of "n - 1"] assms show ?thesis by simp
qed

lemma stirling_integral_conv_stirling_integral_Suc:
  assumes "n > 0"
  shows   "stirling_integral n s =
             of_nat n / of_nat (Suc n) * stirling_integral (Suc n) s -
             of_real (bernoulli (Suc n)) / (of_nat (Suc n) * s ^ n)"
proof -
  have "(λN. of_real (pbernpoly (Suc n) (real N)) / (of_nat (Suc n) * (s + of_nat N) ^ n) -
             of_real (bernoulli (Suc n)) / (real (Suc n) * s ^ n) +
             integral {0..real N} (λx. of_nat n / of_nat (Suc n) * 
                (of_real (pbernpoly (Suc n) x) / (of_real x + s) ^ Suc n)))
            0 - of_real (bernoulli (Suc n)) / (of_nat (Suc n) * s ^ n) +
                   of_nat n / of_nat (Suc n) * stirling_integral (Suc n) s" (is "?f  _")
    unfolding stirling_integral_def integral_mult_right
    using convergent_stirling_integral[of "Suc n"] assms s
    by (intro tendsto_intros pbernpoly_over_power_tendsto_0)
       (auto simp: convergent_LIMSEQ_iff simp del: of_nat_Suc)
  also have "?this  (λN. integral {0..real N} 
               (λx. of_real (pbernpoly n x) / (of_real x + s) ^ n)) 
               of_nat n / of_nat (Suc n) * stirling_integral (Suc n) s -
                 of_real (bernoulli (Suc n)) / (of_nat (Suc n) * s ^ n)" 
    using eventually_gt_at_top[of "0::nat"] pbernpoly_integral_conv_pbernpoly_integral_Suc[of n] 
          assms unfolding integral_mult_right
    by (intro filterlim_cong refl) (auto elim!: eventually_mono simp del: power_Suc)
  finally show ?thesis unfolding stirling_integral_def[of n] by (rule limI)
qed

lemma stirling_integral_1_unfold:
  assumes "m > 0"
  shows   "stirling_integral 1 s = stirling_integral m s / of_nat m - 
             (k=1..<m. of_real (bernoulli (Suc k)) / (of_nat k * of_nat (Suc k) * s ^ k))"
proof -
  have "stirling_integral 1 s = stirling_integral (Suc m) s / of_nat (Suc m) -
          (k=1..<Suc m. of_real (bernoulli (Suc k)) / (of_nat k * of_nat (Suc k) * s ^ k))" for m
  proof (induction m)
    case (Suc m)
    let ?C = "(k = 1..<Suc m. of_real (bernoulli (Suc k)) / (of_nat k * of_nat (Suc k) * s ^ k))"
    note Suc.IH
    also have "stirling_integral (Suc m) s / of_nat (Suc m) = 
                 stirling_integral (Suc (Suc m)) s / of_nat (Suc (Suc m)) -
                 of_real (bernoulli (Suc (Suc m))) / 
                   (of_nat (Suc m) * of_nat (Suc (Suc m)) * s ^ Suc m)"
      (is "_ = ?A - ?B") by (subst stirling_integral_conv_stirling_integral_Suc)
                            (simp_all del: of_nat_Suc power_Suc add: divide_simps)
    also have "?A - ?B - ?C = ?A - (?B + ?C)" by (rule diff_diff_eq)
    also have "?B + ?C = (k = 1..<Suc (Suc m). of_real (bernoulli (Suc k)) /
                             (of_nat k * of_nat (Suc k) * s ^ k))" 
      using s by (simp add: divide_simps)
    finally show ?case .
  qed simp_all
  note this[of "m - 1"]
  also from assms have "Suc (m - 1) = m" by simp
  finally show ?thesis .
qed
  
lemma ln_Gamma_stirling_complex:
  assumes "m > 0"
  shows   "ln_Gamma s = (s - 1 / 2) * ln s - s + ln (2 * pi) / 2 +
             (k=1..<m. of_real (bernoulli (Suc k)) / (of_nat k * of_nat (Suc k) * s ^ k)) - 
             stirling_integral m s / of_nat m"
proof -
  have "ln_Gamma s = (s - 1 / 2) * ln s - s + ln (2 * pi) / 2 - stirling_integral 1 s"
    using limI[OF integral_pbernpoly_1] by (simp add: stirling_integral_def algebra_simps)
  also have "stirling_integral 1 s = stirling_integral m s / of_nat m -
               (k = 1..<m. of_real (bernoulli (Suc k)) / (of_nat k * of_nat (Suc k) * s ^ k))"
    using assms by (rule stirling_integral_1_unfold)
  finally show ?thesis by simp
qed

lemma LIMSEQ_stirling_integral:
  "n > 0  (λx. integral {0..real x} (λx. of_real (pbernpoly n x) / (of_real x + s) ^ n))
      stirling_integral n s" unfolding stirling_integral_def 
  using convergent_stirling_integral[of n] by (simp only: convergent_LIMSEQ_iff)

end

lemmas has_integral_of_real = has_integral_linear[OF _ bounded_linear_of_real, unfolded o_def]
lemmas integral_of_real = integral_linear[OF _ bounded_linear_of_real, unfolded o_def]

lemma integrable_ln_Gamma_aux_real:
  assumes "0 < s"
  shows   "(λx. pbernpoly n x / (x + s) ^ n) integrable_on {0..real N}"
proof -
  have "(λx. complex_of_real (pbernpoly n x / (x + s) ^ n)) integrable_on {0..real N}"
    using integrable_ln_Gamma_aux[of "of_real s" n N] assms by simp
  from integrable_linear[OF this bounded_linear_Re] show ?thesis 
    by (simp only: o_def Re_complex_of_real) 
qed
  
lemma  
  assumes "x > 0" "n > 0"
  shows   stirling_integral_complex_of_real:
            "stirling_integral n (complex_of_real x) = of_real (stirling_integral n x)"
    and   LIMSEQ_stirling_integral_real:
            "(λN. integral {0..real N} (λt. pbernpoly n t / (t + x) ^ n))
             stirling_integral n x"
    and   stirling_integral_real_convergent:
            "convergent (λN. integral {0..real N} (λt. pbernpoly n t / (t + x) ^ n))"
proof -
  have "(λN. integral {0..real N} (λt. of_real (pbernpoly n t / (t + x) ^ n)))
            stirling_integral n (complex_of_real x)"
    using LIMSEQ_stirling_integral[of "complex_of_real x" n] assms by simp
  hence "(λN. of_real (integral {0..real N} (λt. pbernpoly n t / (t + x) ^ n)))
            stirling_integral n (complex_of_real x)"
    using integrable_ln_Gamma_aux_real[OF assms(1), of n] 
    by (subst (asm) integral_of_real) simp
  from tendsto_Re[OF this] 
    have "(λN. integral {0..real N} (λt. pbernpoly n t / (t + x) ^ n))
            Re (stirling_integral n (complex_of_real x))" by simp
  thus "convergent (λN. integral {0..real N} (λt. pbernpoly n t / (t + x) ^ n))"
    by (rule convergentI)
  thus "(λN. integral {0..real N} (λt. pbernpoly n t / (t + x) ^ n))
           stirling_integral n x" unfolding stirling_integral_def
    by (simp add: convergent_LIMSEQ_iff)
  from tendsto_of_real[OF this, where 'a = complex] 
       integrable_ln_Gamma_aux_real[OF assms(1), of n]
    have "(λxa. integral {0..real xa} 
                    (λxa. complex_of_real (pbernpoly n xa) / (complex_of_real xa + x) ^ n))
              complex_of_real (stirling_integral n x)"
    by (subst (asm) integral_of_real [symmetric]) simp_all
  from LIMSEQ_unique[OF this LIMSEQ_stirling_integral[of "complex_of_real x" n]] assms
    show "stirling_integral n (complex_of_real x) = of_real (stirling_integral n x)" by simp
qed

lemma ln_Gamma_stirling_real:
  assumes "x > (0 :: real)" "m > (0::nat)"
  shows   "ln_Gamma x = (x - 1 / 2) * ln x - x + ln (2 * pi) / 2 +
              (k = 1..<m. bernoulli (Suc k) / (of_nat k * of_nat (Suc k) * x ^ k)) -
              stirling_integral m x / of_nat m"
proof -
  from assms have "complex_of_real (ln_Gamma x) = ln_Gamma (complex_of_real x)"
    by (simp add: ln_Gamma_complex_of_real)
  also have "ln_Gamma (complex_of_real x) = complex_of_real (
                (x - 1 / 2) * ln x - x + ln (2 * pi) / 2 +
                (k = 1..<m. bernoulli (Suc k) / (of_nat k * of_nat (Suc k) * x ^ k)) -
                stirling_integral m x / of_nat m)" using assms
    by (subst ln_Gamma_stirling_complex[of _ m])
       (simp_all add: Ln_of_real stirling_integral_complex_of_real)
  finally show ?thesis by (subst (asm) of_real_eq_iff)
qed


lemma stirling_integral_bound_aux:
  assumes n: "n > (1::nat)"
  obtains c where "s. Re s > 0  norm (stirling_integral n s)   c / Re s ^ (n - 1)"
proof -
  obtain c where c: "norm (pbernpoly n x)  c" for x by (rule bounded_pbernpoly[of n]) blast
  have c': "pbernpoly n x  c" for x using c[of x] by (simp add: abs_real_def split: if_splits)
  from c[of 0] have c_nonneg: "c  0" by simp
  have "norm (stirling_integral n s)  c / (real n - 1) / Re s ^ (n - 1)" if s: "Re s > 0" for s
  proof (rule Lim_norm_ubound[OF _ LIMSEQ_stirling_integral])
    have pos: "x + norm s > 0" if "x  0" for x using s that by (intro add_nonneg_pos) auto
    have nz: "of_real x + s  0" if "x  0" for x using s that by (auto simp: complex_eq_iff)
    let ?bound = "λN. c / (Re s ^ (n - 1) * (real n - 1)) - 
                        c / ((real N + Re s) ^ (n - 1) * (real n - 1))"
    show "eventually (λN. norm (integral {0..real N} 
              (λx. of_real (pbernpoly n x) / (of_real x + s) ^ n))  
            c / (real n - 1) / Re s ^ (n - 1)) at_top"
      using eventually_gt_at_top[of "0::nat"]
    proof eventually_elim
      case (elim N)
      let ?F = "λx. -c / ((x + Re s) ^ (n - 1) * (real n - 1))"
      from n s have "((λx. c / (x + Re s) ^ n) has_integral (?F (real N) - ?F 0)) {0..real N}"
        by (intro fundamental_theorem_of_calculus)
           (auto intro!: derivative_eq_intros simp: divide_simps power_diff add_eq_0_iff2
                   has_real_derivative_iff_has_vector_derivative [symmetric])      
      also have "?F (real N) - ?F 0 = ?bound N" by simp
      finally have *: "((λx. c / (x + Re s) ^ n) has_integral ?bound N) {0..real N}" .
      have "norm (integral {0..real N} (λx. of_real (pbernpoly n x) / (of_real x + s) ^ n)) 
              integral {0..real N} (λx. c / (x + Re s) ^ n)"
      proof (intro integral_norm_bound_integral integrable_ln_Gamma_aux s ballI)
        fix x assume x: "x  {0..real N}"
        have "norm (of_real (pbernpoly n x) / (of_real x + s) ^ n)  c / norm (of_real x + s) ^ n"
          unfolding norm_divide norm_power using c by (intro divide_right_mono) simp_all
        also have "  c / (x + Re s) ^ n" 
          using x c c_nonneg s nz[of x] complex_Re_le_cmod[of "of_real x + s"]
          by (intro divide_left_mono power_mono mult_pos_pos zero_less_power add_nonneg_pos) auto
        finally show "norm (of_real (pbernpoly n x) / (of_real x + s) ^ n)  " .
      qed (insert n s * pos nz c, auto simp: complex_nonpos_Reals_iff)
      also have " = ?bound N" using * by (simp add: has_integral_iff)
      also have "  c / (Re s ^ (n - 1) * (real n - 1))" using c_nonneg elim s n by simp
      also have " = c / (real n - 1) / (Re s ^ (n - 1))" by simp
      finally show "norm (integral {0..real N} (λx. of_real (pbernpoly n x) /
                      (of_real x + s) ^ n))  c / (real n - 1) / Re s ^ (n - 1)" .
    qed
  qed (insert s n, simp_all add: complex_nonpos_Reals_iff)
  thus ?thesis by (rule that)
qed

lemma stirling_integral_bound_aux_integral1:
  fixes a b c :: real and n :: nat
  assumes "a  0" "b > 0" "c  0" "n > 1" "l < a - b" "r > a + b"
  shows "((λx. c / max b ¦x - a¦ ^ n) has_integral
           2*c*(n / (n - 1))/b^(n-1) - c/(n-1) * (1/(a-l)^(n-1) + 1/(r-a)^(n-1))) {l..r}"
proof -
  define x1 x2 where "x1 = a - b" and "x2 = a + b"
  define F1 where "F1 = (λx::real. c / (a - x) ^ (n - 1) / (n - 1))"
  define F3 where "F3 = (λx::real. -c / (x - a) ^ (n - 1) / (n - 1))"
  have deriv: "(F1 has_vector_derivative (c / (a - x) ^ n)) (at x within A)"
              "(F3 has_vector_derivative (c / (x - a) ^ n)) (at x within A)"
    if "x  a" for x :: real and A
    unfolding F1_def F3_def using assms that
    by (auto intro!: derivative_eq_intros simp: divide_simps power_diff add_eq_0_iff2
             simp flip: has_real_derivative_iff_has_vector_derivative)

  from assms have "((λx. c / (a - x) ^ n) has_integral (F1 x1 - F1 l)) {l..x1}"
    by (intro fundamental_theorem_of_calculus deriv) (auto simp: x1_def max_def split: if_splits)
  also have "?this  ((λx. c / max b ¦x - a¦ ^ n) has_integral (F1 x1 - F1 l)) {l..x1}"
    using assms
    by (intro has_integral_spike_finite_eq[of "{l}"]) (auto simp: x1_def max_def split: if_splits)
  finally have I1: "((λx. c / max b ¦x - a¦ ^ n) has_integral (F1 x1 - F1 l)) {l..x1}" .

  have "((λx. c / b ^ n) has_integral (x2 - x1) * c / b ^ n) {x1..x2}"
    using has_integral_const_real[of "c / b ^ n" x1 x2] assms by (simp add: x1_def x2_def)
  also have "?this  ((λx. c / max b ¦x - a¦ ^ n) has_integral ((x2 - x1) * c / b ^ n)) {x1..x2}"
    by (intro has_integral_spike_finite_eq[of "{x1, x2}"])
       (auto simp: x1_def x2_def split: if_splits)
  finally have I2: "((λx. c / max b ¦x - a¦ ^ n) has_integral ((x2 - x1) * c / b ^ n)) {x1..x2}" .

  from assms have I3: "((λx. c / (x - a) ^ n) has_integral (F3 r - F3 x2)) {x2..r}"
    by (intro fundamental_theorem_of_calculus deriv) (auto simp: x2_def min_def split: if_splits)
  also have "?this  ((λx. c / max b ¦x - a¦ ^ n) has_integral (F3 r - F3 x2)) {x2..r}"
    using assms
    by (intro has_integral_spike_finite_eq[of "{r}"]) (auto simp: x2_def min_def split: if_splits)
  finally have I3: "((λx. c / max b ¦x - a¦ ^ n) has_integral (F3 r - F3 x2)) {x2..r}" .

  have "((λx. c / max b ¦x - a¦ ^ n) has_integral (F1 x1 - F1 l) + ((x2 - x1) * c / b ^ n) + (F3 r - F3 x2)) {l..r}"
    using assms
    by (intro has_integral_combine[OF _ _ has_integral_combine[OF _ _ I1 I2] I3])
       (auto simp: x1_def x2_def)
  also have "(F1 x1 - F1 l) + ((x2 - x1) * c / b ^ n) + (F3 r - F3 x2) =
             F1 x1 - F1 l + F3 r - F3 x2 + (x2 - x1) * c / b ^ n"
    by (simp add: algebra_simps)
  also have "x2 - x1 = 2 * b"
    using assms by (simp add: x2_def x1_def min_def max_def)
  also have "2 * b * c / b ^ n = 2 * c / b ^ (n - 1)"
    using assms by (simp add: power_diff field_simps)
  also have "F1 x1 - F1 l + F3 r - F3 x2 =
               c/(n-1) * (2/b^(n-1) - 1/(a-l)^(n-1) - 1/(r-a)^(n-1))"
    using assms by (simp add: x1_def x2_def F1_def F3_def field_simps)
  also have " + 2 * c / b ^ (n - 1) =
             2*c*(1 + 1/(n-1))/b^(n-1) - c/(n-1) * (1/(a-l)^(n-1) + 1/(r-a)^(n-1))"
    using assms by (simp add: field_simps)
  also have "1 + 1 / (n - 1) = n / (n - 1)"
    using assms by (simp add: field_simps)
  finally show ?thesis .
qed

lemma stirling_integral_bound_aux_integral2:
  fixes a b c :: real and n :: nat
  assumes "a  0" "b > 0" "c  0" "n > 1"
  obtains I where "((λx. c / max b ¦x - a¦ ^ n) has_integral I) {l..r}"
                  "I  2 * c * (n / (n - 1)) / b ^ (n-1)"
proof -
  define l' where "l' = min l (a - b - 1)"
  define r' where "r' = max r (a + b + 1)"

  define A where "A = 2 * c * (n / (n - 1)) / b ^ (n - 1)"
  define B where "B = c / real (n - 1) * (1 / (a - l') ^ (n - 1) + 1 / (r' - a) ^ (n - 1))"

  have has_int: "((λx. c / max b ¦x - a¦ ^ n) has_integral (A - B)) {l'..r'}"
    using assms unfolding A_def B_def
    by (intro stirling_integral_bound_aux_integral1) (auto simp: l'_def r'_def)
  have "(λx. c / max b ¦x - a¦ ^ n) integrable_on {l..r}"
    by (rule integrable_on_subinterval[OF has_integral_integrable[OF has_int]])
       (auto simp: l'_def r'_def)
  then obtain I where has_int': "((λx. c / max b ¦x - a¦ ^ n) has_integral I) {l..r}"
    by (auto simp: integrable_on_def)

  from assms have "I  A - B"
    by (intro has_integral_subset_le[OF _ has_int' has_int]) (auto simp: l'_def r'_def)
  also have "  A"
    using assms by (simp add: B_def l'_def r'_def)
  finally show ?thesis using that[of I] has_int' unfolding A_def by blast
qed

lemma stirling_integral_bound_aux':
  assumes n: "n > (1::nat)" and α: "α  {0<..<pi}"
  obtains c where "s::complex. s  complex_cone' α - {0} 
                     norm (stirling_integral n s)  c / norm s ^ (n - 1)"
proof -
  obtain c where c: "norm (pbernpoly n x)  c" for x by (rule bounded_pbernpoly[of n]) blast
  have c': "pbernpoly n x  c" for x using c[of x] by (simp add: abs_real_def split: if_splits)
  from c[of 0] have c_nonneg: "c  0" by simp

  define D where "D = c * Beta (- (real_of_int (- int n) / 2) - 1 / 2) (1 / 2) / 2"
  define C where "C = max D (2*c*(n/(n-1))/sin α^(n-1))"

  have *: "norm (stirling_integral n s)  C / norm s ^ (n - 1)"
    if s: "s  complex_cone' α - {0}" for s :: complex
  proof (rule Lim_norm_ubound[OF _ LIMSEQ_stirling_integral])
    from s α have Arg: "¦Arg s¦  α" by (auto simp: complex_cone_altdef)
    have s': "s  0"
      using complex_cone_inter_nonpos_Reals[of "-α" α] α s  by auto
    from s have [simp]: "s  0" by auto

    show "eventually (λN. norm (integral {0..real N}
              (λx. of_real (pbernpoly n x) / (of_real x + s) ^ n))  
            C / norm s ^ (n - 1)) at_top"
      using eventually_gt_at_top[of "0::nat"]
    proof eventually_elim
      case (elim N)
      show ?case
      proof (cases "Re s > 0")
        case True
        have int: "((λx. c * (x^2 + norm s^2) powr (-n / 2)) has_integral
                  D * (norm s ^ 2) powr (-n / 2 + 1 / 2)) {0<..}"
          using has_integral_mult_left[OF has_integral_Beta3[of "-n/2" "norm s ^ 2"], of c] assms True
          unfolding D_def by (simp add: algebra_simps)
        hence int': "((λx. c * (x^2 + norm s^2) powr (-n / 2)) has_integral
                  D * (norm s ^ 2) powr (-n / 2 + 1 / 2)) {0..}"
          by (subst has_integral_interior [symmetric]) simp_all
        hence integrable: "(λx. c * (x^2 + norm s^2) powr (-n / 2)) integrable_on {0..}"
          by (simp add: has_integral_iff)

        have "norm (integral {0..real N} (λx. of_real (pbernpoly n x) / (of_real x + s) ^ n)) 
                integral {0..real N} (λx. c * (x^2 + norm s^2) powr (-n / 2))"
        proof (intro integral_norm_bound_integral s ballI integrable_ln_Gamma_aux)
          have [simp]: "{0<..} - {0::real..} = {}" "{0..} - {0<..} = {0::real}"
            by auto
          have "(λx. c * (x2 + (cmod s)2) powr (real_of_int (- int n) / 2)) integrable_on {0<..}"
            using int by (simp add: has_integral_iff)
          also have "?this  (λx. c * (x2 + (cmod s)2) powr (real_of_int (- int n) / 2)) integrable_on {0..}"
            by (intro integrable_spike_set_eq) auto
          finally show "(λx. c * (x2 + (cmod s)2) powr (real_of_int (- int n) / 2)) integrable_on
                   {0..real N}" by (rule integrable_on_subinterval) auto
        next
          fix x assume x: "x  {0..real N}"
          have nz: "complex_of_real x + s  0"
            using True x by (auto simp: complex_eq_iff)
          have "norm (of_real (pbernpoly n x) / (of_real x + s) ^ n)  c / norm (of_real x + s) ^ n"
            unfolding norm_divide norm_power using c by (intro divide_right_mono) simp_all
          also have "  c / sqrt (x ^ 2 + norm s ^ 2) ^ n"
          proof (intro divide_left_mono mult_pos_pos zero_less_power power_mono)
            show "sqrt (x2 + (cmod s)2)  cmod (complex_of_real x + s)"
              using x True by (simp add: cmod_def algebra_simps power2_eq_square)
          qed (use x True c_nonneg assms nz in auto simp: add_nonneg_pos)
          also have "sqrt (x ^ 2 + norm s ^ 2) ^ n = (x ^ 2 + norm s ^ 2) powr (1/2 * n)"
            by (subst powr_powr [symmetric], subst powr_realpow)
               (auto simp: powr_half_sqrt add_nonneg_pos)
          also have "c /  = c * (x^2 + norm s^2) powr (-n / 2)"
            by (simp add: powr_minus field_simps)
          finally show "norm (complex_of_real (pbernpoly n x) / (complex_of_real x + s) ^ n)  " .
        qed fact+
        also have "  integral {0..} (λx. c * (x^2 + norm s^2) powr (-n / 2))"
          using c_nonneg
          by (intro integral_subset_le integrable integrable_on_subinterval[OF integrable]) auto
        also have " = D * (norm s ^ 2) powr (-n / 2 + 1 / 2)"
          using int' by (simp add: has_integral_iff)
        also have "(norm s ^ 2) powr (-n / 2 + 1 / 2) = norm s powr (2 * (-n / 2 + 1 / 2))"
          by (subst powr_powr [symmetric]) auto
        also have " = norm s powr (-real (n - 1))"
          using assms by (simp add: of_nat_diff)
        also have "D *  = D / norm s ^ (n - 1)"
          by (auto simp: powr_minus powr_realpow field_simps)
        also have "  C / norm s ^ (n - 1)"
          by (intro divide_right_mono) (auto simp: C_def)
        finally show "norm (integral {0..real N} (λx. of_real (pbernpoly n x) / (of_real x + s) ^ n))  " .

      next

        case False
        have "cos ¦Arg s¦ = cos (Arg s)"
          by (simp add: abs_if)
        also have "cos (Arg s) = Re (rcis (norm s) (Arg s)) / norm s"
          by (subst Re_rcis) auto
        also have " = Re s / norm s"
          by (subst rcis_cmod_Arg) auto
        also have "  cos (pi / 2)"
          using False by (auto simp: field_simps)
        finally have "¦Arg s¦  pi / 2"
          using Arg α by (subst (asm) cos_mono_le_eq) auto

        have "sin α * norm s = sin (pi - α) * norm s"
          by simp
        also have "  sin (pi - ¦Arg s¦) * norm s"
          using α Arg ¦Arg s¦  pi / 2
          by (intro mult_right_mono sin_monotone_2pi_le) auto
        also have "sin ¦Arg s¦  0"
          using Arg_bounded[of s] by (intro sin_ge_zero) auto
        hence "sin (pi - ¦Arg s¦) = ¦sin ¦Arg s¦¦"
          by simp 
        also have " = ¦sin (Arg s)¦"
          by (simp add: abs_if)
        also have " * norm s = ¦Im (rcis (norm s) (Arg s))¦"
          by (simp add: abs_mult)
        also have " = ¦Im s¦"
          by (subst rcis_cmod_Arg) auto
        finally have abs_Im_ge: "¦Im s¦  sin α * norm s" .

        have [simp]: "Im s  0" "s  0"
          using s s  0 False
          by (auto simp: cmod_def zero_le_mult_iff complex_nonpos_Reals_iff)
        have "sin α > 0"
          using assms by (intro sin_gt_zero) auto
  
        obtain I where I: "((λx. c / max ¦Im s¦ ¦x + Re s¦ ^ n) has_integral I) {0..real N}"
                          "I  2*c*(n/(n-1)) / ¦Im s¦ ^ (n - 1)"
          using s c_nonneg assms False 
                stirling_integral_bound_aux_integral2[of "-Re s" "¦Im s¦" c n 0 "real N"] by auto
  
        have "norm (integral {0..real N} (λx. of_real (pbernpoly n x) / (of_real x + s) ^ n)) 
                integral {0..real N} (λx. c / max ¦Im s¦ ¦x + Re s¦ ^ n)"
        proof (intro integral_norm_bound_integral integrable_ln_Gamma_aux s ballI)
          show "(λx. c / max ¦Im s¦ ¦x + Re s¦ ^ n) integrable_on {0..real N}"
            using I(1) by (simp add: has_integral_iff)
        next
          fix x assume x: "x  {0..real N}"
          have nz: "complex_of_real x + s  0"
            by (auto simp: complex_eq_iff)
          have "norm (complex_of_real (pbernpoly n x) / (complex_of_real x + s) ^ n) 
                  c / norm (complex_of_real x + s) ^ n"
            unfolding norm_divide norm_power using c[of x] by (intro divide_right_mono) simp_all
          also have "  c / max ¦Im s¦ ¦x + Re s¦ ^ n"
            using c_nonneg nz abs_Re_le_cmod[of "of_real x + s"] abs_Im_le_cmod[of "of_real x + s"]
            by (intro divide_left_mono power_mono mult_pos_pos zero_less_power)
               (auto simp: less_max_iff_disj)
          finally show "norm (complex_of_real (pbernpoly n x) / (complex_of_real x + s) ^ n)  " .
        qed (auto simp: complex_nonpos_Reals_iff)
        also have "  2*c*(n/(n-1)) / ¦Im s¦ ^ (n - 1)"
          using I by (simp add: has_integral_iff)
        also have "  2*c*(n/(n-1)) / (sin α * norm s) ^ (n - 1)"
          using sin α > 0 s c_nonneg abs_Im_ge
          by (intro divide_left_mono mult_pos_pos zero_less_power power_mono mult_nonneg_nonneg) auto
        also have " = 2*c*(n/(n-1))/sin α^(n-1) / norm s ^ (n - 1)"
          by (simp add: field_simps)
        also have "  C / norm s ^ (n - 1)"
          by (intro divide_right_mono) (auto simp: C_def)
        finally show ?thesis .
      qed
    qed
  qed (use that assms complex_cone_inter_nonpos_Reals[of "-α" α] α in auto)
  thus ?thesis by (rule that)
qed

lemma stirling_integral_bound:
  assumes "n > 0"
  obtains c where 
    "s. Re s > 0  norm (stirling_integral n s)  c / Re s ^ n"
proof -
  let ?f = "λs. of_nat n / of_nat (Suc n) * stirling_integral (Suc n) s -
                  of_real (bernoulli (Suc n)) / (of_nat (Suc n) * s ^ n)"
  from stirling_integral_bound_aux[of "Suc n"] assms obtain c where 
    c: "s. Re s > 0  norm (stirling_integral (Suc n) s)  c / Re s ^ n" by auto
  define c1 where "c1 = real n / real (Suc n) * c"
  define c2 where "c2 = ¦bernoulli (Suc n)¦ / real (Suc n)"
  have c2_nonneg: "c2  0" by (simp add: c2_def)
  show ?thesis
  proof (rule that)
    fix s :: complex assume s: "Re s > 0"
    hence s': "s  0" by (auto simp: complex_nonpos_Reals_iff)
    have "stirling_integral n s = ?f s" using s' assms 
      by (rule stirling_integral_conv_stirling_integral_Suc)
    also have "norm   norm (of_nat n / of_nat (Suc n) * stirling_integral (Suc n) s) +
                           norm (of_real (bernoulli (Suc n)) / (of_nat (Suc n) * s ^ n))"
      by (rule norm_triangle_ineq4)
    also have " = real n / real (Suc n) * norm (stirling_integral (Suc n) s) +
                      c2 / norm s ^ n" (is "_ = ?A + ?B")
      by (simp add: norm_divide norm_mult norm_power c2_def field_simps del: of_nat_Suc)
    also have "?A  real n / real (Suc n) * (c / Re s ^ n)"
      by (intro mult_left_mono c s) simp_all
    also have " = c1 / Re s ^ n" by (simp add: c1_def)
    also have "c2 / norm s ^ n  c2 / Re s ^ n" using s c2_nonneg
      by (intro divide_left_mono power_mono complex_Re_le_cmod mult_pos_pos zero_less_power) auto
    also have "c1 / Re s ^ n + c2 / Re s ^ n = (c1 + c2) / Re s ^ n" 
      using s by (simp add: field_simps)
    finally show "norm (stirling_integral n s)  (c1 + c2) / Re s ^ n" by - simp_all
  qed
qed

lemma stirling_integral_bound':
  assumes "n > 0" and "α  {0<..<pi}"
  obtains c where 
    "s::complex. s  complex_cone' α - {0}  norm (stirling_integral n s)  c / norm s ^ n"
proof -
  let ?f = "λs. of_nat n / of_nat (Suc n) * stirling_integral (Suc n) s -
                  of_real (bernoulli (Suc n)) / (of_nat (Suc n) * s ^ n)"
  from stirling_integral_bound_aux'[of "Suc n"] assms obtain c where 
    c: "s::complex. s  complex_cone' α - {0} 
            norm (stirling_integral (Suc n) s)  c / norm s ^ n" by auto
  define c1 where "c1 = real n / real (Suc n) * c"
  define c2 where "c2 = ¦bernoulli (Suc n)¦ / real (Suc n)"
  have c2_nonneg: "c2  0" by (simp add: c2_def)
  show ?thesis
  proof (rule that)
    fix s :: complex assume s: "s  complex_cone' α - {0}"
    have s': "s  0"
      using complex_cone_inter_nonpos_Reals[of "-α" α] assms s by auto
      
    have "stirling_integral n s = ?f s" using s' assms 
      by (intro stirling_integral_conv_stirling_integral_Suc) auto
    also have "norm   norm (of_nat n / of_nat (Suc n) * stirling_integral (Suc n) s) +
                           norm (of_real (bernoulli (Suc n)) / (of_nat (Suc n) * s ^ n))"
      by (rule norm_triangle_ineq4)
    also have " = real n / real (Suc n) * norm (stirling_integral (Suc n) s) +
                      c2 / norm s ^ n" (is "_ = ?A + ?B")
      by (simp add: norm_divide norm_mult norm_power c2_def field_simps del: of_nat_Suc)
    also have "?A  real n / real (Suc n) * (c / norm s ^ n)"
      by (intro mult_left_mono c s) simp_all
    also have " = c1 / norm s ^ n" by (simp add: c1_def)
    also have "c1 / norm s ^ n + c2 / norm s ^ n = (c1 + c2) / norm s ^ n" 
      using s by (simp add: divide_simps)
    finally show "norm (stirling_integral n s)  (c1 + c2) / norm s ^ n" by - simp_all
  qed
qed


lemma stirling_integral_holomorphic [holomorphic_intros]:
  assumes m: "m > 0" and "A  0 = {}"
  shows   "stirling_integral m holomorphic_on A"  
proof -
  from assms have [simp]: "z  0" if "z  A" for z
    using that by auto
  let ?f = "λs::complex. of_nat m * ((s - 1 / 2) * Ln s - s + of_real (ln (2 * pi) / 2) +
          (k=1..<m. of_real (bernoulli (Suc k)) / (of_nat k * of_nat (Suc k) * s ^ k)) - 
          ln_Gamma s)"
  have "?f holomorphic_on A" using assms
    by (auto intro!: holomorphic_intros simp del: of_nat_Suc elim!: nonpos_Reals_cases)
  also have "?this  stirling_integral m holomorphic_on A" 
    using assms by (intro holomorphic_cong refl) 
                   (simp_all add: field_simps ln_Gamma_stirling_complex)
  finally show "stirling_integral m holomorphic_on A" .
qed

lemma stirling_integral_continuous_on_complex [continuous_intros]:
  assumes m: "m > 0" and "A  0 = {}"
  shows   "continuous_on A (stirling_integral m :: _  complex)"
  by (intro holomorphic_on_imp_continuous_on stirling_integral_holomorphic assms)
    
lemma has_field_derivative_stirling_integral_complex:
  fixes x :: complex
  assumes "x  0" "n > 0"
  shows   "(stirling_integral n has_field_derivative deriv (stirling_integral n) x) (at x)"
  using assms
  by (intro holomorphic_derivI[OF stirling_integral_holomorphic, of n  "-0"]) auto


 
lemma
  assumes n: "n > 0" and "x > 0"
  shows   deriv_stirling_integral_complex_of_real:
            "(deriv ^^ j) (stirling_integral n) (complex_of_real x) =
               complex_of_real ((deriv ^^ j) (stirling_integral n) x)" (is "?lhs x = ?rhs x")
  and     differentiable_stirling_integral_real:
            "(deriv ^^ j) (stirling_integral n) field_differentiable at x" (is ?thesis2)
proof -
  let ?A = "{s. Re s > 0}"
  let ?f = "λj x. (deriv ^^ j) (stirling_integral n) (complex_of_real x)"
  let ?f' = "λj x. complex_of_real ((deriv ^^ j) (stirling_integral n) x)"
    
  have [simp]: "open ?A" by (simp add: open_halfspace_Re_gt)      

  have "?lhs x = ?rhs x  (deriv ^^ j) (stirling_integral n) field_differentiable at x" 
    if "x > 0" for x using that
  proof (induction j arbitrary: x)
    case 0
    have "((λx. Re (stirling_integral n (of_real x))) has_field_derivative 
                  Re (deriv (λx. stirling_integral n x) (of_real x))) (at x)" using 0 n
      by (auto intro!: derivative_intros has_vector_derivative_real_field
                 field_differentiable_derivI holomorphic_on_imp_differentiable_at[of _ ?A]
                 stirling_integral_holomorphic simp: complex_nonpos_Reals_iff)
    also have "?this  (stirling_integral n has_field_derivative 
             Re (deriv (λx. stirling_integral n x) (of_real x))) (at x)"
      using eventually_nhds_in_open[of "{0<..}" x] 0 n
      by (intro has_field_derivative_cong_ev refl) 
         (auto elim!: eventually_mono simp: stirling_integral_complex_of_real)
    finally have "stirling_integral n field_differentiable at x"
      by (auto simp: field_differentiable_def)
    with 0 n show ?case by (auto simp: stirling_integral_complex_of_real)
  next
    case (Suc j x)
    note IH = conjunct1[OF Suc.IH] conjunct2[OF Suc.IH]
    have *: "(deriv ^^ Suc j) (stirling_integral n) (complex_of_real x) =
                 of_real ((deriv ^^ Suc j) (stirling_integral n) x)" if x: "x > 0" for x
    proof -
      have "deriv ((deriv ^^ j) (stirling_integral n)) (complex_of_real x) =
              vector_derivative (λx. (deriv ^^ j) (stirling_integral n) (of_real x)) (at x)"
        using n x
        by (intro vector_derivative_of_real_right [symmetric] 
                   holomorphic_on_imp_differentiable_at[of _ ?A] holomorphic_higher_deriv
                   stirling_integral_holomorphic) (auto simp: complex_nonpos_Reals_iff)
      also have " = vector_derivative (λx. of_real ((deriv ^^ j) (stirling_integral n) x)) (at x)"
        using eventually_nhds_in_open[of "{0<..}" x] x
        by (intro vector_derivative_cong_eq) (auto elim!: eventually_mono simp: IH(1))
      also have " = of_real (deriv ((deriv ^^ j) (stirling_integral n)) x)"
        by (intro vector_derivative_of_real_left holomorphic_on_imp_differentiable_at[of _ ?A]
              field_differentiable_imp_differentiable IH(2) x)
      finally show ?thesis by simp
    qed
    have "((λx. Re ((deriv ^^ Suc j) (stirling_integral n) (of_real x))) has_field_derivative 
             Re (deriv ((deriv ^^ Suc j) (stirling_integral n)) (of_real x))) (at x)"
      using Suc.prems n
      by (intro derivative_intros has_vector_derivative_real_field field_differentiable_derivI
                holomorphic_on_imp_differentiable_at[of _ ?A] stirling_integral_holomorphic
                holomorphic_higher_deriv) (auto simp: complex_nonpos_Reals_iff)
    also have "?this  ((deriv ^^ Suc j) (stirling_integral n) has_field_derivative 
                  Re (deriv ((deriv ^^ Suc j) (stirling_integral n)) (of_real x))) (at x)"  
      using eventually_nhds_in_open[of "{0<..}" x] Suc.prems *
      by (intro has_field_derivative_cong_ev refl) (auto elim!: eventually_mono)
    finally have "(deriv ^^ Suc j) (stirling_integral n) field_differentiable at x"
      by (auto simp: field_differentiable_def)
    with *[OF Suc.prems] show ?case by blast
  qed
  from this[OF assms(2)] show "?lhs x = ?rhs x" ?thesis2 by blast+
qed

text ‹
  Unfortunately, asymptotic power series cannot, in general, be differentiated. However, since 
  @{term ln_Gamma} is holomorphic on the entire positive real half-space, we can differentiate 
  its asymptotic expansion after all.

  To do this, we use an ad-hoc version of the more general approach outlined in Erdelyi's
  ``Asymptotic Expansions'' for holomorphic functions: We bound the value of the $j$-th derivative 
  of the remainder term at some value $x$ by applying Cauchy's integral formula along a circle 
  centred at $x$ with radius $\frac{1}{2} x$.
›
lemma deriv_stirling_integral_real_bound:
  assumes m: "m > 0"
  shows   "(deriv ^^ j) (stirling_integral m)  O(λx::real. 1 / x ^ (m + j))"
proof -
  obtain c where c: "s. 0 < Re s  cmod (stirling_integral m s)  c / Re s ^ m"
    using stirling_integral_bound[OF m] by auto
  have "0  cmod (stirling_integral m 1)" by simp
  also have "  c" using c[of 1] by simp
  finally have c_nonneg: "c  0" .
  define B where "B = c * 2 ^ (m + Suc j)"
  define B' where "B' = B * fact j / 2"

  have "eventually (λx::real. norm ((deriv ^^ j) (stirling_integral m) x)  
          B' * norm (1 / x ^ (m+ j))) at_top"
    using eventually_gt_at_top[of "0::real"]
  proof eventually_elim
    case (elim x)
    have "s  0" if "s  cball (of_real x) (x/2)" for s :: complex
    proof -
      have "x - Re s  norm (of_real x - s)" using complex_Re_le_cmod[of "of_real x - s"] by simp
      also from that have "  x/2" by (simp add: dist_complex_def)
      finally show ?thesis using elim by (auto simp: complex_nonpos_Reals_iff)
    qed
    hence "((λu. stirling_integral m u / (u - of_real x) ^ Suc j) has_contour_integral
            complex_of_real (2 * pi) * 𝗂 / fact j * 
              (deriv ^^ j) (stirling_integral m) (of_real x)) (circlepath (of_real x) (x/2))"
      using m elim
      by (intro Cauchy_has_contour_integral_higher_derivative_circlepath 
                stirling_integral_continuous_on_complex stirling_integral_holomorphic) auto
    hence "norm (of_real (2 * pi) * 𝗂 / fact j * (deriv ^^ j) (stirling_integral m) (of_real x)) 
            B / x ^ (m + Suc j) * (2 * pi * (x / 2))"
    proof (rule has_contour_integral_bound_circlepath)
      fix u :: complex assume dist: "norm (u - of_real x) = x / 2"
      have "Re (of_real x - u)  norm (of_real x - u)" by (rule complex_Re_le_cmod)
      also have " = x / 2" using dist by (simp add: norm_minus_commute)
      finally have Re_u: "Re u  x/2" using elim by simp
      have "norm (stirling_integral m u / (u - of_real x) ^ Suc j)  
              c / Re u ^ m / (x / 2) ^ Suc j" using Re_u elim
        unfolding norm_divide norm_power dist
        by (intro divide_right_mono zero_le_power c) simp_all
      also have "  c / (x/2) ^ m / (x / 2) ^ Suc j" using c_nonneg elim Re_u
        by (intro divide_right_mono divide_left_mono power_mono) simp_all
      also have " = B / x ^ (m + Suc j)" using elim by (simp add: B_def field_simps power_add)
      finally show "norm (stirling_integral m u / (u - of_real x) ^ Suc j)  B / x ^ (m + Suc j)" .
    qed (insert elim c_nonneg, auto simp: B_def simp del: power_Suc)
    hence "cmod ((deriv ^^ j) (stirling_integral m) (of_real x))  B' / x ^ (j + m)"
      using elim by (simp add: field_simps norm_divide norm_mult norm_power B'_def)
    with elim m show ?case by (simp_all add: add_ac deriv_stirling_integral_complex_of_real)
  qed
  thus ?thesis by (rule bigoI)
qed

definition stirling_sum where
  "stirling_sum j m x = 
     (-1) ^ j * (k = 1..<m. (of_real (bernoulli (Suc k)) * pochhammer (of_nat k) j / (of_nat k *
                                 of_nat (Suc k))) * inverse x ^ (k + j))"
  
definition stirling_sum' where
  "stirling_sum' j m x = 
     (-1) ^ (Suc j) * (km. (of_real (bernoulli' k) * 
       pochhammer (of_nat (Suc k)) (j - 1) * inverse x ^ (k + j)))"

lemma stirling_sum_complex_of_real:
  "stirling_sum j m (complex_of_real x) = complex_of_real (stirling_sum j m x)"
  by (simp add: stirling_sum_def pochhammer_of_real [symmetric] del: of_nat_Suc)

lemma stirling_sum'_complex_of_real:
  "stirling_sum' j m (complex_of_real x) = complex_of_real (stirling_sum' j m x)"
  by (simp add: stirling_sum'_def pochhammer_of_real [symmetric] del: of_nat_Suc)

lemma has_field_derivative_stirling_sum_complex [derivative_intros]:
  "Re x > 0  (stirling_sum j m has_field_derivative stirling_sum (Suc j) m x) (at x)"
    unfolding stirling_sum_def [abs_def] sum_distrib_left
    by (rule DERIV_sum) (auto intro!: derivative_eq_intros simp del: of_nat_Suc 
                              simp: pochhammer_Suc power_diff)

lemma has_field_derivative_stirling_sum_real [derivative_intros]:
  "x > (0::real)  (stirling_sum j m has_field_derivative stirling_sum (Suc j) m x) (at x)"
    unfolding stirling_sum_def [abs_def] sum_distrib_left
    by (rule DERIV_sum) (auto intro!: derivative_eq_intros simp del: of_nat_Suc 
                              simp: pochhammer_Suc power_diff)

lemma has_field_derivative_stirling_sum'_complex [derivative_intros]:
  assumes "j > 0" "Re x > 0"
  shows   "(stirling_sum' j m has_field_derivative stirling_sum' (Suc j) m x) (at x)"
proof (cases j)
  case (Suc j')
  from assms have [simp]: "x  0" by auto
  define c where "c = (λn. (-1) ^ Suc j * complex_of_real (bernoulli' n) * 
                          pochhammer (of_nat (Suc n)) j')"
  define T where "T = (λn x. c n * inverse x ^ (j + n))"
  define T' where "T' = (λn x. - (of_nat (j + n)) * c n * inverse x ^ (Suc (j + n)))"
  have "((λx. km. T k x) has_field_derivative (km. T' k x)) (at x)" using assms Suc
    by (intro DERIV_sum)
       (auto simp: T_def T'_def intro!: derivative_eq_intros 
             simp: field_simps power_add [symmetric]  simp del: of_nat_Suc power_Suc of_nat_add)
  also have "(λx. (km. T k x)) = stirling_sum' j m"
    by (simp add: Suc T_def c_def stirling_sum'_def fun_eq_iff add_ac mult.assoc sum_distrib_left)
  also have "(km. T' k x) = stirling_sum' (Suc j) m x"
    by (simp add: T'_def c_def Suc stirling_sum'_def sum_distrib_left 
          sum_distrib_right algebra_simps pochhammer_Suc)
  finally show ?thesis .
qed (insert assms, simp_all)
  
lemma has_field_derivative_stirling_sum'_real [derivative_intros]:
  assumes "j > 0" "x > (0::real)"
  shows   "(stirling_sum' j m has_field_derivative stirling_sum' (Suc j) m x) (at x)"
proof (cases j)
  case (Suc j')
  from assms have [simp]: "x  0" by auto
  define c where "c = (λn. (-1) ^ Suc j * (bernoulli' n) * pochhammer (of_nat (Suc n)) j')"
  define T where "T = (λn x. c n * inverse x ^ (j + n))"
  define T' where "T' = (λn x. - (of_nat (j + n)) * c n * inverse x ^ (Suc (j + n)))"
  have "((λx. km. T k x) has_field_derivative (km. T' k x)) (at x)" using assms Suc
    by (intro DERIV_sum)
       (auto simp: T_def T'_def intro!: derivative_eq_intros 
             simp: field_simps power_add [symmetric]  simp del: of_nat_Suc power_Suc of_nat_add)
  also have "(λx. (km. T k x)) = stirling_sum' j m"
    by (simp add: Suc T_def c_def stirling_sum'_def fun_eq_iff add_ac mult.assoc sum_distrib_left)
  also have "(km. T' k x) = stirling_sum' (Suc j) m x"
    by (simp add: T'_def c_def Suc stirling_sum'_def sum_distrib_left 
          sum_distrib_right algebra_simps pochhammer_Suc)
  finally show ?thesis .
qed (insert assms, simp_all)

lemma higher_deriv_stirling_sum_complex:
  "Re x > 0  (deriv ^^ i) (stirling_sum j m) x = stirling_sum (i + j) m x"
proof (induction i arbitrary: x)
  case (Suc i)
  have "deriv ((deriv ^^ i) (stirling_sum j m)) x = deriv (stirling_sum (i + j) m) x"
    using eventually_nhds_in_open[of "{x. Re x > 0}" x] Suc.prems
    by (intro deriv_cong_ev refl) (auto elim!: eventually_mono simp: open_halfspace_Re_gt Suc.IH)
  also from Suc.prems have " = stirling_sum (Suc (i + j)) m x"
    by (intro DERIV_imp_deriv has_field_derivative_stirling_sum_complex)
  finally show ?case by simp
qed simp_all
  

definition Polygamma_approx :: "nat  nat  'a  'a :: {real_normed_field, ln}" where
  "Polygamma_approx j m = 
     (deriv ^^ j) (λx. (x - 1 / 2) * ln x - x + of_real (ln (2 * pi)) / 2 + stirling_sum 0 m x)"
  
lemma Polygamma_approx_Suc: "Polygamma_approx (Suc j) m = deriv (Polygamma_approx j m)"
  by (simp add: Polygamma_approx_def)  

lemma Polygamma_approx_0: 
  "Polygamma_approx 0 m x = (x - 1/2) * ln x - x + of_real (ln (2*pi)) / 2 + stirling_sum 0 m x"
  by (simp add: Polygamma_approx_def)
    
lemma Polygamma_approx_1_complex: 
  "Re x > 0  
     Polygamma_approx (Suc 0) m x = ln x - 1 / (2*x) + stirling_sum (Suc 0) m x"
  unfolding Polygamma_approx_Suc Polygamma_approx_0
  by (intro DERIV_imp_deriv) 
     (auto intro!: derivative_eq_intros elim!: nonpos_Reals_cases simp: field_simps)
     
lemma Polygamma_approx_1_real: 
  "x > (0 :: real)  
     Polygamma_approx (Suc 0) m x = ln x - 1 / (2*x) + stirling_sum (Suc 0) m x"
  unfolding Polygamma_approx_Suc Polygamma_approx_0
  by (intro DERIV_imp_deriv) 
     (auto intro!: derivative_eq_intros elim!: nonpos_Reals_cases simp: field_simps)
 
lemma stirling_sum_2_conv_stirling_sum'_1:
  fixes x :: "'a :: {real_div_algebra, field_char_0}"
  assumes "m > 0" "x  0"
  shows   "stirling_sum' 1 m x = 1 / x + 1 / (2 * x^2) + stirling_sum 2 m x"
proof -
  have pochhammer_2: "pochhammer (of_nat k) 2 = of_nat k * of_nat (Suc k)" for k 
    by (simp add: pochhammer_Suc eval_nat_numeral add_ac)
  have "stirling_sum 2 m x = 
          (k = Suc 0..<m. of_real (bernoulli' (Suc k)) * inverse x ^ Suc (Suc k))"
    unfolding stirling_sum_def pochhammer_2 power2_minus power_one mult_1_left
    by (intro sum.cong refl)
       (simp_all add: stirling_sum_def pochhammer_2 power2_eq_square divide_simps bernoulli'_def
                 del: of_nat_Suc power_Suc)
  also have "1 / (2 * x^2) +  = 
               (k=0..<m. of_real (bernoulli' (Suc k)) * inverse x ^ Suc (Suc k))" using assms
    by (subst (2) sum.atLeast_Suc_lessThan) (simp_all add: power2_eq_square field_simps)
  also have "1 / x +  = (k=0..<Suc m. of_real (bernoulli' k) * inverse x ^ Suc k)"
    by (subst sum.atLeast0_lessThan_Suc_shift) (simp_all add: bernoulli'_def divide_simps)
  also have " = (km. of_real (bernoulli' k) * inverse x ^ Suc k)"
    by (intro sum.cong) auto
  also have " = stirling_sum' 1 m x" by (simp add: stirling_sum'_def)
  finally show ?thesis by (simp add: add_ac)
qed

lemma Polygamma_approx_2_real: 
  assumes "x > (0::real)" "m > 0"
  shows   "Polygamma_approx (Suc (Suc 0)) m x = stirling_sum' 1 m x"
proof -
  have "Polygamma_approx (Suc (Suc 0)) m x = deriv (Polygamma_approx (Suc 0) m) x" 
    by (simp add: Polygamma_approx_Suc)
  also have " = deriv (λx. ln x - 1 / (2*x) + stirling_sum (Suc 0) m x) x"
    using eventually_nhds_in_open[of "{0<..}" x] assms
    by (intro deriv_cong_ev) (auto elim!: eventually_mono simp: Polygamma_approx_1_real)
  also have " = 1 / x + 1 / (2*x^2) + stirling_sum (Suc (Suc 0)) m x" using assms
    by (intro DERIV_imp_deriv) (auto intro!: derivative_eq_intros 
           elim!: nonpos_Reals_cases simp: field_simps power2_eq_square)
  also have " = stirling_sum' 1 m x" using stirling_sum_2_conv_stirling_sum'_1[of m x] assms
    by (simp add: eval_nat_numeral)
  finally show ?thesis .
qed
  
lemma Polygamma_approx_2_complex: 
  assumes "Re x > 0" "m > 0"
  shows   "Polygamma_approx (Suc (Suc 0)) m x = stirling_sum' 1 m x"
proof -
  have "Polygamma_approx (Suc (Suc 0)) m x = deriv (Polygamma_approx (Suc 0) m) x" 
    by (simp add: Polygamma_approx_Suc)
  also have " = deriv (λx. ln x - 1 / (2*x) + stirling_sum (Suc 0) m x) x"
    using eventually_nhds_in_open[of "{s. Re s > 0}" x] assms
    by (intro deriv_cong_ev)
       (auto simp: open_halfspace_Re_gt elim!: eventually_mono simp: Polygamma_approx_1_complex)
  also have " = 1 / x + 1 / (2*x^2) + stirling_sum (Suc (Suc 0)) m x" using assms
    by (intro DERIV_imp_deriv) (auto intro!: derivative_eq_intros 
           elim!: nonpos_Reals_cases simp: field_simps power2_eq_square)
  also have " = stirling_sum' 1 m x" using stirling_sum_2_conv_stirling_sum'_1[of m x] assms
      by (subst stirling_sum_2_conv_stirling_sum'_1) (auto simp: eval_nat_numeral)
  finally show ?thesis .
qed
  
lemma Polygamma_approx_ge_2_real: 
  assumes "x > (0::real)" "m > 0"
  shows   "Polygamma_approx (Suc (Suc j)) m x = stirling_sum' (Suc j) m x"
using assms(1)
proof (induction j arbitrary: x)
  case (0 x)
  with assms show ?case by (simp add: Polygamma_approx_2_real)
next
  case (Suc j x)
  have "Polygamma_approx (Suc (Suc (Suc j))) m x = deriv (Polygamma_approx (Suc (Suc j)) m) x"
    by (simp add: Polygamma_approx_Suc)
  also have " = deriv (stirling_sum' (Suc j) m) x"
    using eventually_nhds_in_open[of "{0<..}" x] Suc.prems
    by (intro deriv_cong_ev refl) (auto elim!: eventually_mono simp: Suc.IH)
  also have " = stirling_sum' (Suc (Suc j)) m x" using Suc.prems
    by (intro DERIV_imp_deriv derivative_intros) simp_all
  finally show ?case .
qed
  
lemma Polygamma_approx_ge_2_complex:
  assumes "Re x > 0" "m > 0"
  shows   "Polygamma_approx (Suc (Suc j)) m x = stirling_sum' (Suc j) m x"
using assms(1)
proof (induction j arbitrary: x)
  case (0 x)
  with assms show ?case by (simp add: Polygamma_approx_2_complex)
next
  case (Suc j x)
  have "Polygamma_approx (Suc (Suc (Suc j))) m x = deriv (Polygamma_approx (Suc (Suc j)) m) x"
    by (simp add: Polygamma_approx_Suc)
  also have " = deriv (stirling_sum' (Suc j) m) x"
    using eventually_nhds_in_open[of "{x. Re x > 0}" x] Suc.prems
    by (intro deriv_cong_ev refl) (auto elim!: eventually_mono simp: Suc.IH open_halfspace_Re_gt)
  also have " = stirling_sum' (Suc (Suc j)) m x" using Suc.prems
    by (intro DERIV_imp_deriv derivative_intros) simp_all
  finally show ?case .
qed

lemma Polygamma_approx_complex_of_real:
  assumes "x > 0" "m > 0"
  shows   "Polygamma_approx j m (complex_of_real x) = of_real (Polygamma_approx j m x)"
proof (cases j)
  case 0
  with assms show ?thesis by (simp add: Polygamma_approx_0 Ln_of_real stirling_sum_complex_of_real)
next
  case [simp]: (Suc j')
  thus ?thesis
  proof (cases j')
    case 0
    with assms show ?thesis 
      by (simp add: Polygamma_approx_1_complex 
                    Polygamma_approx_1_real stirling_sum_complex_of_real Ln_of_real)
  next
    case (Suc j'')
    with assms show ?thesis
      by (simp add: Polygamma_approx_ge_2_complex Polygamma_approx_ge_2_real 
                    stirling_sum'_complex_of_real)
  qed
qed
  
lemma higher_deriv_Polygamma_approx [simp]: 
  "(deriv ^^ j) (Polygamma_approx i m) = Polygamma_approx (j + i) m"
  by (simp add: Polygamma_approx_def funpow_add)
  
lemma stirling_sum_holomorphic [holomorphic_intros]:
  "0  A  stirling_sum j m holomorphic_on A"
  unfolding stirling_sum_def by (intro holomorphic_intros) auto
  
lemma Polygamma_approx_holomorphic [holomorphic_intros]:
  "Polygamma_approx j m holomorphic_on {s. Re s > 0}"
  unfolding Polygamma_approx_def
  by (intro holomorphic_intros) (auto simp: open_halfspace_Re_gt elim!: nonpos_Reals_cases)
  
lemma higher_deriv_lnGamma_stirling:
  assumes m: "m > 0"
  shows   "(λx::real. (deriv ^^ j) ln_Gamma x - Polygamma_approx j m x)  O(λx. 1 / x ^ (m + j))"
proof -
  have "eventually (λx. ¦(deriv ^^ j) ln_Gamma x - Polygamma_approx j m x¦ =
                          inverse (real m) * ¦(deriv ^^ j) (stirling_integral m) x¦) at_top"
    using eventually_gt_at_top[of "0::real"]
  proof eventually_elim
    case (elim x)
    note x = this
    have "F y in nhds (complex_of_real x). y  - 0"
      using elim by (intro eventually_nhds_in_open) auto
    hence "(deriv ^^ j) (λx. ln_Gamma x - Polygamma_approx 0 m x) (complex_of_real x) =
            (deriv ^^ j) (λx. (-inverse (of_nat m)) * stirling_integral m x) (complex_of_real x)"
      using x m
      by (intro higher_deriv_cong_ev refl)
         (auto elim!: eventually_mono simp: ln_Gamma_stirling_complex Polygamma_approx_def 
            field_simps open_halfspace_Re_gt stirling_sum_def)
    also have " = - inverse (of_nat m) * (deriv ^^ j) (stirling_integral m) (of_real x)" using x m
      by (intro higher_deriv_cmult[of _ "-0"] stirling_integral_holomorphic)
         (auto simp: open_halfspace_Re_gt)
    also have "(deriv ^^ j) (λx. ln_Gamma x - Polygamma_approx 0 m x) (complex_of_real x) = 
                 (deriv ^^ j) ln_Gamma (of_real x) - (deriv ^^ j) (Polygamma_approx 0 m) (of_real x)"
      using x 
      by (intro higher_deriv_diff[of _ "{s. Re s > 0}"])
         (auto intro!: holomorphic_intros elim!: nonpos_Reals_cases simp: open_halfspace_Re_gt)
    also have "(deriv ^^ j) (Polygamma_approx 0 m) (complex_of_real x) =
                 of_real (Polygamma_approx j m x)" using x m
      by (simp add: Polygamma_approx_complex_of_real)
    also have "norm (- inverse (of_nat m) * (deriv ^^ j) (stirling_integral m) (complex_of_real x)) = 
                 inverse (real m) * ¦(deriv ^^ j) (stirling_integral m) x¦"
      using x m by (simp add: norm_mult norm_inverse deriv_stirling_integral_complex_of_real)
    also have "(deriv ^^ j) ln_Gamma (complex_of_real x) = of_real ((deriv ^^ j) ln_Gamma x)" using x
      by (simp add: higher_deriv_ln_Gamma_complex_of_real)
    also have "norm ( - of_real (Polygamma_approx j m x)) = 
                 ¦(deriv ^^ j) ln_Gamma x - Polygamma_approx j m x¦"
      by (simp only: of_real_diff [symmetric] norm_of_real)
    finally show ?case .
  qed
  from bigthetaI_cong[OF this] m
    have "(λx::real. (deriv ^^ j) ln_Gamma x - Polygamma_approx j m x)  
             Θ(λx. (deriv ^^ j) (stirling_integral m) x)" by simp
  also have "(λx::real. (deriv ^^ j) (stirling_integral m) x)  O(λx. 1 / x ^ (m + j))" using m
      by (rule deriv_stirling_integral_real_bound)
  finally show ?thesis .
qed

lemma Polygamma_approx_1_real':
  assumes x: "(x::real) > 0" and m: "m > 0"
  shows   "Polygamma_approx 1 m x = ln x - (k = Suc 0..m. bernoulli' k * inverse x ^ k / real k)"
proof -
  have "Polygamma_approx 1 m x = ln x - (1 / (2 * x) + 
          (k=Suc 0..<m. bernoulli (Suc k) * inverse x ^ Suc k / real (Suc k)))"
    (is "_ = _ - (_ + ?S)") using x by (simp add: Polygamma_approx_1_real stirling_sum_def)
  also have "?S = (k=Suc 0..<m. bernoulli' (Suc k) * inverse x ^ Suc k / real (Suc k))"
    by (intro sum.cong refl) (simp_all add: bernoulli'_def)
  also have "1 / (2 * x) +  = 
               (k=0..<m. bernoulli' (Suc k) * inverse x ^ Suc k / real (Suc k))" using m
    by (subst (2) sum.atLeast_Suc_lessThan) (simp_all add: field_simps)
  also have " = (k = Suc 0..m. bernoulli' k * inverse x ^ k / real k)" using assms
    by (subst sum.shift_bounds_Suc_ivl [symmetric]) (simp add: atLeastLessThanSuc_atLeastAtMost)
  finally show ?thesis .
qed

theorem
  assumes m: "m > 0"
  shows   ln_Gamma_real_asymptotics:
            "(λx. ln_Gamma x - ((x - 1 / 2) * ln x - x + ln (2 * pi) / 2 +
                     (k = 1..<m. bernoulli (Suc k) / (real k * real (Suc k)) / x^k)))
                 O(λx. 1 / x ^ m)" (is ?th1)
    and   Digamma_real_asymptotics:
            "(λx. Digamma x - (ln x - (k=1..m. bernoulli' k / real k / x ^ k)))
                 O(λx. 1 / (x ^ Suc m))" (is ?th2)
    and   Polygamma_real_asymptotics: "j > 0  
             (λx. Polygamma j x - (- 1) ^ Suc j * (km. bernoulli' k *
                     pochhammer (real (Suc k)) (j - 1) / x ^ (k + j))) 
                 O(λx. 1 / x ^ (m+j+1))" (is "_  ?th3")
proof -
  define G :: "nat  real  real" where 
    "G = (λm. if m = 0 then ln_Gamma else Polygamma (m - 1))"
  have *: "(λx. G j x - h x)  O(λx. 1 / x ^ (m + j))"
    if "x::real. x > 0  Polygamma_approx j m x = h x" for j h
  proof -
    have "(λx. G j x - h x)  
            Θ(λx. (deriv ^^ j) ln_Gamma x - Polygamma_approx j m x)" (is "_  Θ(?f)")
      using that
      by (intro bigthetaI_cong) (auto intro: eventually_mono[OF eventually_gt_at_top[of "0::real"]]
            simp del: funpow.simps simp: higher_deriv_ln_Gamma_real G_def)
    also have "?f  O(λx::real. 1 / x ^ (m + j))" using m
      by (rule higher_deriv_lnGamma_stirling)
    finally show ?thesis .
  qed  
    
  note [[simproc del: simplify_landau_sum]]
  from *[OF Polygamma_approx_0] assms show ?th1 
    by (simp add: G_def Polygamma_approx_0 stirling_sum_def field_simps)
  from *[OF Polygamma_approx_1_real'] assms show ?th2 by (simp add: G_def field_simps)
      
  assume j: "j > 0"
  from *[OF Polygamma_approx_ge_2_real, of "j - 1"] assms j show ?th3
    by (simp add: G_def stirling_sum'_def power_add power_diff field_simps)
qed




subsection ‹Asymptotics of the complex Gamma function›

text ‹
  The m›-th order remainder of Stirling's formula for $\log\Gamma$ is $O(s^{-m})$ uniformly over
  any complex cone $\text{Arg}(z) \leq \alpha$, $z\neq 0$ for any angle
  $\alpha\in(0, \pi)$. This means that there is bounded by $c z^{-m}$ for some constant $c$ for
  all $z$ in this cone.
›
context
  fixes F and α
  assumes α: "α  {0<..<pi}"
  defines "F  principal (complex_cone' α - {0})"
begin

lemma stirling_integral_bigo:
  fixes m :: nat
  assumes m: "m > 0"
  shows   "stirling_integral m  O[F](λs. 1 / s ^ m)"
proof -
  obtain c where c: "s. s  complex_cone' α - {0}  norm (stirling_integral m s)  c / norm s ^ m"
    using stirling_integral_bound'[OF m > 0 α] by blast
  have "0  norm (stirling_integral m 1 :: complex)"
    by simp
  also have "  c"
    using c[of 1] α by simp
  finally have "c  0" .

  have "eventually (λs. s  complex_cone' α - {0}) F"
    unfolding F_def by (auto simp: eventually_principal)
  hence "eventually (λs. norm (stirling_integral m s) 
                     c * norm (1 / s ^ m)) F"
    by eventually_elim (use c in simp add: norm_divide norm_power)
  thus "stirling_integral m  O[F](λs. 1 / s ^ m)"
    by (intro bigoI[of _ c]) auto
qed

end

text ‹
  The following is a more explicit statement of this:
›
theorem ln_Gamma_complex_asymptotics_explicit:
  fixes m :: nat and α :: real
  assumes "m > 0" and "α  {0<..<pi}"
  obtains C :: real and R :: "complex  complex"
  where "s::complex. s  0 
               ln_Gamma s = (s - 1/2) * ln s - s + ln (2 * pi) / 2 +
                            (k=1..<m. bernoulli (k+1) / (k * (k+1) * s ^ k)) - R s"
    and "s. s  0  ¦Arg s¦  α  norm (R s)  C / norm s ^ m"    
proof -
  obtain c where c: "s. s  complex_cone' α - {0}  norm (stirling_integral m s)  c / norm s ^ m"
    using stirling_integral_bound'[OF assms] by blast
  have "0  norm (stirling_integral m 1 :: complex)"
    by simp
  also have "  c"
    using c[of 1] assms by simp
  finally have "c  0" .
  define R where "R = (λs::complex. stirling_integral m s / of_nat m)"
  show ?thesis
  proof (rule that)
    from ln_Gamma_stirling_complex[of _ m] assms show
           "s::complex. s  0 
               ln_Gamma s = (s - 1 / 2) * ln s - s + ln (2 * pi) / 2 +
               (k=1..<m. bernoulli (k+1) / (k * (k+1) * s ^ k)) - R s"
      by (auto simp add: R_def algebra_simps)
    show "s. s  0  ¦Arg s¦  α  cmod (R s)  c / real m / cmod s ^ m"
    proof (safe, goal_cases)
      case (1 s)
      show ?case
        using 1 c[of s] assms
        by (auto simp: complex_cone_altdef abs_le_iff R_def norm_divide field_simps)
    qed
  qed
qed


text ‹
  Lastly, we can also derive the asymptotics of $\Gamma$ itself:
  \[\Gamma(z) \sim \sqrt{2\pi / z} \left(\frac{z}{e}\right)^z\]
  uniformly for $|z|\to\infty$ within the cone $\text{Arg}(z) \leq \alpha$ for $\alpha\in(0,\pi)$:
›

context
  fixes F and α
  assumes α: "α  {0<..<pi}"
  defines "F  inf at_infinity (principal (complex_cone' α))"
begin

lemma Gamma_complex_asymp_equiv:
  "Gamma ∼[F] (λs. sqrt (2 * pi) * (s / exp 1) powr s / s powr (1 / 2))"
proof -
  define I :: "complex  complex" where "I = stirling_integral 1"
  have "eventually (λs. s  complex_cone' α) F"
    by (auto simp: eventually_inf_principal F_def)
  moreover have "eventually (λs. s  0) F"
    unfolding F_def eventually_inf_principal
    using eventually_not_equal_at_infinity by eventually_elim auto
  ultimately have "eventually (λs. Gamma s =
                     sqrt (2 * pi) * (s / exp 1) powr s / s powr (1 / 2) / exp (I s)) F"
  proof eventually_elim
    case (elim s)
    from elim have s': "s  0"
      using complex_cone_inter_nonpos_Reals[of "-α" α] α by auto
    from elim have [simp]: "s  0" by auto      
    from s' have "Gamma s = exp (ln_Gamma s)"
      unfolding Gamma_complex_altdef using nonpos_Ints_subset_nonpos_Reals by auto
    also from s' have "ln_Gamma s = (s-1/2) * Ln s - s + complex_of_real (ln (2 * pi) / 2) - I s"
      by (subst ln_Gamma_stirling_complex[of _ 1]) (simp_all add: exp_add exp_diff I_def)
    also have "exp  = exp ((s - 1 / 2) * Ln s) / exp s *
                        exp (complex_of_real (ln (2 * pi) / 2)) / exp (I s)"
      unfolding exp_diff exp_add by (simp add: exp_diff exp_add)
    also have "exp ((s - 1 / 2) * Ln s) = s powr (s - 1 / 2)"
      by (simp add: powr_def)
    also have "exp (complex_of_real (ln (2 * pi) / 2)) = sqrt (2 * pi)"
      by (subst exp_of_real) (auto simp: powr_def simp flip: powr_half_sqrt)
    also have "exp s = exp 1 powr s"
      by (simp add: powr_def)
    also have "s powr (s - 1 / 2) / exp 1 powr s = (s powr s / exp 1 powr s) / s powr (1/2)"
      by (subst powr_diff) auto
    also have *: "Ln (s / exp 1) = Ln s - 1"
      using Ln_divide_of_real[of "exp 1" s] by (simp flip: exp_of_real)
    hence "s powr s / exp 1 powr s = (s / exp 1) powr s"
      unfolding powr_def by (subst *) (auto simp: exp_diff field_simps)
    finally show "Gamma s = sqrt (2 * pi) * (s / exp 1) powr s / s powr (1 / 2) / exp (I s)"
      by (simp add: algebra_simps)
  qed
  hence "Gamma ∼[F] (λs. sqrt (2 * pi) * (s / exp 1) powr s / s powr (1 / 2) / exp (I s))"
    by (rule asymp_equiv_refl_ev)
  also have " ∼[F] (λs. sqrt (2 * pi) * (s / exp 1) powr s / s powr (1 / 2) / 1)"
  proof (intro asymp_equiv_intros)
    have "F  principal (complex_cone' α - {0})"
      unfolding le_principal F_def eventually_inf_principal
      using eventually_not_equal_at_infinity by eventually_elim auto
    moreover have "I  O[principal (complex_cone' α - {0})](λs. 1 / s)"
      using stirling_integral_bigo[of α 1] α unfolding F_def by (simp add: I_def)
    ultimately have "I  O[F](λs. 1 / s)"
      by (rule landau_o.big.filter_mono)
    also have "(λs. 1 / s)  o[F](λs. 1)"
    proof (rule landau_o.smallI)
      fix c :: real
      assume c: "c > 0"
      hence "eventually (λz::complex. norm z  1 / c) at_infinity"
        by (auto simp: eventually_at_infinity)
      moreover have "eventually (λz::complex. z  0) at_infinity"
        by (rule eventually_not_equal_at_infinity)
      ultimately show "eventually (λz::complex. norm (1 / z)  c * norm (1 :: complex)) F"
        unfolding F_def eventually_inf_principal
        by eventually_elim (use c > 0 in auto simp: norm_divide field_simps)
    qed
    finally have "I  o[F](λs. 1)" .
    from smalloD_tendsto[OF this] have [tendsto_intros]: "(I  0) F"
      by simp
    show "(λx. exp (I x)) ∼[F] (λx. 1)"
      by (rule asymp_equivI' tendsto_eq_intros refl | simp)+
  qed
  finally show ?thesis by simp
qed

end

end