Theory Incomplete_Gamma_Lemma_Bucket

(*
   File:    Incomplete_Gamma_Lemma_Bucket.thy
   Author:  Manuel Eberl, University of Innsbruck
*)
section ‹Auxiliary material›
theory Incomplete_Gamma_Lemma_Bucket
  imports
  "HOL-Complex_Analysis.Complex_Analysis"
  Safe_Power
  More_Dominated_Convergence
  Derivative_Method
begin

lemma sgn_sqrt [simp]: "sgn (sqrt x) = sgn x"
  by (auto simp: sgn_if)

(* TODO Move *)
lemma countably_generated_imp_convergent_sequence:
  assumes "countably_generated_filter F" "F  bot"
  obtains X where "filterlim X F sequentially"
proof -
  obtain B where B: "antimono_on UNIV B" "P. eventually P F = (i::nat. xB i. P x)"
    using countably_generated_filter_iff_decseq[of F] assms by blast
  have "B i  {}" for i
    using B(2)[of "λ_. False"] assms by (auto simp: trivial_limit_def)
  hence "i. x. x  B i"
    by blast
  then obtain X where X: "X i  B i" for i
    by metis
  have "filterlim X F sequentially"
    unfolding filterlim_def le_filter_def eventually_filtermap
  proof safe
    fix P assume "eventually P F"
    with B obtain i where i: "xB i. P x"
      by blast
    hence "P (X j)" if j: "j  i" for j
      using monotone_onD[OF B(1) _ _ j] X[of j] by auto
    thus "eventually (λj. P (X j)) sequentially"
      by (auto simp: eventually_at_top_linorder)
  qed
  thus ?thesis
    by (rule that)
qed

(* TODO Move *)
lemma (in sequential_filter) dominated_convergence':
  fixes f :: "'a  'n::euclidean_space  'm::euclidean_space"
  assumes f: "eventually (λk. (f k) integrable_on S) F" and h: "h integrable_on S"
    and le: "eventually (λk. xS. norm (f k x)  h x) F"
    and conv: "x. x  S  ((λk. f k x)  g x) F"
  shows "((λk. integral S (f k))  integral S g) F"
proof (rule filterlim_sequentially_imp_filterlim)
  fix X assume X: "filterlim X F sequentially"
  have "eventually (λk. (xS. norm (f k x)  h x)  f k integrable_on S) F"
    using f le by eventually_elim auto
  hence "eventually (λk. (xS. norm (f (X k) x)  h x)  f (X k) integrable_on S) sequentially"
    by (rule eventually_compose_filterlim) fact
  then obtain N where N: "k. k  N  (xS. norm (f (X k) x)  h x)  f (X k) integrable_on S"
    by (auto simp: eventually_at_top_linorder)
  define X' where "X' = X  ((+) N)"
  have X': "xS. norm (f (X' k) x)  h x" "f (X' k) integrable_on S" for k
    using N[of "N + k"] by (simp_all add: X'_def)
  have lim_X': "filterlim X' F sequentially" unfolding X'_def o_def
    by (rule filterlim_compose[OF X]) 
       (use filterlim_add_const_nat_at_top[of N] in simp_all add: add_ac)

  have "(λn. integral S (f (X' n)))  integral S g"
  proof (rule dominated_convergence)
    show "h integrable_on S"
      by fact
    show "f (X' n) integrable_on S" for n
      by (rule X')
    show "norm (f (X' k) x)  h x" if "x  S" for x k
      using X'[of k] that by auto
    show "(λn. f (X' n) x)  g x" if "x  S" for x
      by (rule filterlim_compose[OF conv lim_X']) fact
  qed
  hence "(λn. integral S (f (X' (n - N))))  integral S g"
    by (rule filterlim_compose) (rule filterlim_minus_const_nat_at_top)
  also have "?this  (λn. integral S (f (X n)))  integral S g"
  proof (rule filterlim_cong)
    show "eventually (λn. integral S (f (X' (n - N))) = integral S (f (X n))) at_top"
      using eventually_ge_at_top[of N] by eventually_elim (auto simp: X'_def)
  qed auto
  finally show "(λn. integral S (f (X n)))  integral S g" .
qed

(* TODO Move *)
lemma dominated_convergence_countably_generated_filter:
  fixes f :: "'a  'n::euclidean_space  'm::euclidean_space"
  assumes cg: "countably_generated_filter F"
  assumes f: "eventually (λk. (f k) integrable_on S) F" and h: "h integrable_on S"
    and le: "eventually (λk. xS. norm (f k x)  h x) F"
    and conv: "x. x  S  ((λk. f k x)  g x) F"
  shows "F  bot  g integrable_on S" "((λk. integral S (f k))  integral S g) F"
proof -
  interpret sequential_filter F
    by (rule countably_generated_filter_imp_sequential_filter) fact
  show "((λk. integral S (f k))  integral S g) F"
    using f h le conv by (rule dominated_convergence')

  assume "F  bot"
  with cg obtain X where X: "filterlim X F sequentially"
    using countably_generated_imp_convergent_sequence by blast
  have "eventually (λk. (xS. norm (f k x)  h x)  f k integrable_on S) F"
    using f le by eventually_elim auto
  hence "eventually (λk. (xS. norm (f (X k) x)  h x)  f (X k) integrable_on S) sequentially"
    by (rule eventually_compose_filterlim) fact
  then obtain N where N: "k. k  N  (xS. norm (f (X k) x)  h x)  f (X k) integrable_on S"
    by (auto simp: eventually_at_top_linorder)
  define X' where "X' = X  ((+) N)"
  have X': "xS. norm (f (X' k) x)  h x" "f (X' k) integrable_on S" for k
    using N[of "N + k"] by (simp_all add: X'_def)
  have lim_X': "filterlim X' F sequentially" unfolding X'_def o_def
    by (rule filterlim_compose[OF X]) 
       (use filterlim_add_const_nat_at_top[of N] in simp_all add: add_ac)

  show "g integrable_on S"
  proof (rule dominated_convergence)
    show "h integrable_on S"
      by fact
    show "f (X' n) integrable_on S" for n
      by (rule X')
    show "norm (f (X' k) x)  h x" if "x  S" for x k
      using X'[of k] that by auto
    show "(λn. f (X' n) x)  g x" if "x  S" for x
      by (rule filterlim_compose[OF conv lim_X']) fact
  qed
qed



(* TODO Move *)
lemma nonpos_Reals_real_eq: "0 = {..(0::real)}"
  by (auto simp: nonpos_Reals_def)

(* TODO Move *)
lemma has_integral_complex_of_real_iff:
  "((λx. of_real (f x) :: complex) has_integral (of_real I)) A  (f has_integral I) A"
proof
  assume "((λx. of_real (f x) :: complex) has_integral (of_real I)) A"
  thus "(f has_integral I) A"
    by (subst (asm) has_integral_componentwise_iff) (auto simp: Basis_complex_def)
qed (auto intro: has_integral_of_real)

(* TODO Move *)
lemma integrable_on_complex_of_real_iff:
  "(λx. of_real (f x) :: complex) integrable_on A  f integrable_on A"
  using has_integral_complex_of_real_iff[of f _ A]
  by (metis (lifting) ext Re_complex_of_real complex_inner_1_right integrable_component
        integrable_on_def)

(* TODO Move *)
lemma integral_complex_of_real:
  "integral A (λx. complex_of_real (f x)) = of_real (integral A f)"
proof (cases "f integrable_on A")
  case True
  thus ?thesis
    using integral_linear[of f A complex_of_real]
    by (auto simp: bounded_linear_of_real o_def)
next
  case False
  hence "¬(λx. complex_of_real (f x)) integrable_on A"
    by (subst integrable_on_complex_of_real_iff)
  with False show ?thesis
    by (simp add: not_integrable_integral)
qed

(* TODO Move *)
lemma countable_nonpos_Ints [intro]: "countable 0"
  by (rule countable_subset[of _ ""]) (auto intro: countable_int)


(* TODO: Move *)
lemma
  fixes f :: "_  _  'a :: {banach, second_countable_topology}"
  assumes integrable: "i. set_integrable M A (f i)"
  and summable: "AE xA in M. summable (λi. norm (f i x))"
  and sums: "summable (λi. (xA. norm (f i x) M))"
  shows set_integrable_suminf: "set_integrable M A (λx. (i. f i x))"
    and sums_set_integral: "(λi. set_lebesgue_integral M A (f i)) sums (xA. (i. f i x) M)"
    and set_integral_suminf: "(xA. (i. f i x) M) = (i. set_lebesgue_integral M A (f i))"
    and summable_set_integral: "summable (λi. set_lebesgue_integral M A (f i))"
proof -
  have 1: "integrable M (λx. indicat_real A x *R f i x)" for i
    using integrable[of i] unfolding set_integrable_def .
  have 2: "AE x in M. summable (λi. norm (indicat_real A x *R f i x))"
    using summable by eventually_elim auto
  have 3: "summable (λi. lebesgue_integral M (λx. norm (indicat_real A x *R f i x)))"
    using sums unfolding set_lebesgue_integral_def by simp

  have "integrable M (λx. (i. indicator A x *R f i x))"
    using 1 2 3 by (rule integrable_suminf)
  also have "(λx. (i. indicator A x *R f i x)) = (λx. indicator A x *R (i. f i x))"
    by (auto simp: indicator_def)
  finally show "set_integrable M A (λx. (i. f i x))"
    unfolding set_integrable_def .

  have "(λi. set_lebesgue_integral M A (f i)) sums (x. (i. indicator A x *R f i x) M)"
    unfolding set_lebesgue_integral_def using 1 2 3 by (rule sums_integral)
  also have "(x. (i. indicator A x *R f i x) M) = (x. indicator A x *R (i. f i x) M)"
    by (rule Bochner_Integration.integral_cong) (simp_all add: indicator_def)
  finally show "(λi. set_lebesgue_integral M A (f i)) sums (xA. (i. f i x) M)"
    unfolding set_lebesgue_integral_def .

  have "(xA. i. f i xM) = (x. (i. indicator A x *R f i x) M)"
    unfolding set_lebesgue_integral_def 
    by (rule Bochner_Integration.integral_cong) (auto simp: indicator_def)
  also have " = (i. (xA. f i x M))"
    unfolding set_lebesgue_integral_def using 1 2 3 by (rule integral_suminf)
  finally show "(xA. (i. f i x) M) = (i. set_lebesgue_integral M A (f i))" .

  show "summable (λi. set_lebesgue_integral M A (f i))"
    unfolding set_lebesgue_integral_def using 1 2 3 by (rule summable_integral)
qed

(* TODO Move *)
lemma leibniz_rule_field_derivative_real:
  fixes f::"'a::{real_normed_field, banach}  real  'a"
  assumes fx: "x t. x  U  t  {a..b}  ((λx. f x t) has_field_derivative fx x t) (at x within U)"
  assumes integrable_f2: "x. x  U  (f x) integrable_on {a..b}"
  assumes cont_fx: "continuous_on (U × {a..b}) (λ(x, t). fx x t)"
  assumes U: "x0  U" "convex U"
  shows "((λx. integral {a..b} (f x)) has_field_derivative integral {a..b} (fx x0)) (at x0 within U)"    
  using leibniz_rule_field_derivative[of U a b f fx x0] assms by simp

lemma convex_minkowski_sum_ball:
  fixes A :: "'a :: euclidean_space set"
  assumes "convex A"
  shows   "convex (xA. ball x r)"
proof -
  have "convex (xA. yball 0 r. {x + y})"
    by (rule convex_sums) (use assms in auto)
  also have "(xA. yball 0 r. {x + y}) = (xA. ball x r)"
  proof (intro equalityI subsetI)
    fix z assume "z  (xA. yball 0 r. {x + y})"
    thus "z  (xA. ball x r)"
      by (force simp: dist_norm)
  next
    fix z assume "z  (xA. ball x r)"
    then obtain x where "x  A" "z  ball x r"
      by auto
    hence "z = x + (z - x)" "z - x  ball 0 r"
      by (auto simp: dist_norm)
    thus "z  (xA. yball 0 r. {x + y})"
      using x  A by blast
  qed
  finally show ?thesis .
qed

lemma convex_minkowski_sum_cball:
  fixes A :: "'a :: euclidean_space set"
  assumes "convex A"
  shows   "convex (xA. cball x r)"
proof -
  have "convex (xA. ycball 0 r. {x + y})"
    by (rule convex_sums) (use assms in auto)
  also have "(xA. ycball 0 r. {x + y}) = (xA. cball x r)"
  proof (intro equalityI subsetI)
    fix z assume "z  (xA. ycball 0 r. {x + y})"
    thus "z  (xA. cball x r)"
      by (force simp: dist_norm)
  next
    fix z assume "z  (xA. cball x r)"
    then obtain x where "x  A" "z  cball x r"
      by auto
    hence "z = x + (z - x)" "z - x  cball 0 r"
      by (auto simp: dist_norm)
    thus "z  (xA. ycball 0 r. {x + y})"
      using x  A by blast
  qed
  finally show ?thesis .
qed
    
lemma continuous_on_linepath [continuous_intros]:
  assumes "continuous_on A f" "continuous_on A g" "continuous_on A h"
  shows   "continuous_on A (λx. linepath (f x) (g x) (h x))"
  unfolding linepath_def by (intro continuous_intros assms)

lemma contour_integral_linepath_has_field_derivative:
  assumes A: "open A" "a  A" "z  A" "closed_segment a z  A"
  assumes holo: "f holomorphic_on A"
  shows   "((λz. contour_integral (linepath a z) f) has_field_derivative f z) (at z within B)"
proof -
  define e where "e = (if A = UNIV then 1 else setdist (closed_segment a z) (-A))"
  have "e > 0"
  proof (cases "A = UNIV")
    case False
    hence "setdist (closed_segment a z) (-A) > 0"
      using assms unfolding e_def
      by (subst setdist_gt_0_compact_closed compact_path_image) auto
    thus "e > 0"
      using False by (auto simp: e_def)
  qed (auto simp: e_def)

  have dist_ge_e: "dist x y  e" if "x  closed_segment a z" "y  -A" for x y
    using setdist_le_dist[OF that] that by (auto simp: e_def)

  define A' where "A' = (xclosed_segment a z. ball x e)"
  have "A'  A"
  proof
    fix x assume "x  A'"
    then obtain y where "y  closed_segment a z" "dist y x < e"
      by (auto simp: A'_def)
    thus "x  A"
      using dist_ge_e[of y x] by auto
  qed
  have "closed_segment a z  A'" "open A'"
    using e > 0 unfolding A'_def by fastforce+
  have "convex A'"
    unfolding A'_def by (intro convex_minkowski_sum_ball) auto
  have "a  A'" "z  A'"
    using closed_segment a z  A' by auto
  note A' = A'  A closed_segment a z  A' open A' convex A' a  A' z  A'
  note holo = holomorphic_on_subset[OF holo A'  A]

  have "(f has_field_derivative deriv f z) (at z)" if "z  A'" for z
    using that A' A by (auto intro!: holomorphic_derivI holo)
  note [derivative_intros] = DERIV_chain2[OF this]
  note [continuous_intros] = 
    continuous_on_compose2[OF holomorphic_on_imp_continuous_on [OF holo]]
    continuous_on_compose2[OF holomorphic_on_imp_continuous_on [OF holomorphic_deriv[OF holo]]]
  have [derivative_intros]:
      "((λx. linepath a x t) has_field_derivative of_real t) (at x within A')" for t x
    by (auto simp: linepath_def scaleR_conv_of_real intro!: derivative_eq_intros)
 
  have *: "linepath a b t  A'" if "a  A'" "b  A'" "t  {0..1}" for a b t
    using that linepath_in_convex_hull[of a A' b t] A' by (simp add: hull_same)
    
  have "((λz. integral {0..1} (λx. f (linepath a z x)) * (z - a)) has_field_derivative 
      integral {0..1} (λt. deriv f (linepath a z t) * of_real t) * (z - a) +
      integral {0..1} (λx. f (linepath a z x))) (at z within A')" 
      (is "(_ has_field_derivative ?I) _")
    by (rule derivative_eq_intros leibniz_rule_field_derivative_real * A')+
       (insert assms A' *,
        auto intro!: derivative_eq_intros leibniz_rule_field_derivative_real
                     integrable_continuous_real continuous_intros 
             simp:   split_beta scaleR_conv_of_real)
  also have "(λz. integral {0..1} (λx. f (linepath a z x)) * (z - a)) = 
               (λz. contour_integral (linepath a z) f)"
    by (simp add: contour_integral_integral)
  also have "?I = integral {0..1} (λx. deriv f (linepath a z x) * of_real x * (z - a) + 
                     f (linepath a z x))" (is "_ = integral _ ?g")
    by (subst integral_mult_left [symmetric], subst integral_add [symmetric])
       (insert assms A', auto intro!: integrable_continuous_real continuous_intros simp: *)
  also have "(?g has_integral of_real 1 * f (linepath a z 1) - of_real 0 * f (linepath a z 0)) {0..1}"
    using * A A'
    by (intro fundamental_theorem_of_calculus)
       (auto intro!: derivative_eq_intros has_vector_derivative_real_field 
             simp: linepath_def scaleR_conv_of_real)
  hence "integral {0..1} ?g = f (linepath a z 1)" by (simp add: has_integral_iff)
  also have "linepath a z 1 = z" by (simp add: linepath_def)
  also from z  A' and open A' have "at z within A' = at z" by (rule at_within_open)
  finally show ?thesis by (rule DERIV_subset) simp_all
qed



(* TODO: Move *)
lemma set_integrable_bigo:
  fixes f g :: "real  'a :: {banach, real_normed_field, second_countable_topology}"
  assumes "f  O(λx. g x)" and "set_integrable lborel {a..} g"
  assumes "b. b  a  set_integrable lborel {a..<b} f"
  assumes [measurable]: "set_borel_measurable borel {a..} f"
  shows   "set_integrable lborel {a..} f"
proof -
  from assms(1) obtain C x0 where C: "C > 0" "x. x  x0  norm (f x)  C * norm (g x)"
    by (fastforce elim!: landau_o.bigE simp: eventually_at_top_linorder)
  define x0' where "x0' = max a x0"

  have "set_integrable lborel {a..<x0'} f"
    by (intro assms) (auto simp: x0'_def)
  moreover have "set_integrable lborel {x0'..} f" unfolding set_integrable_def
  proof (rule Bochner_Integration.integrable_bound)
    from assms(2) have "set_integrable lborel {x0'..} g"
      by (rule set_integrable_subset) (auto simp: x0'_def)
    thus "integrable lborel (λx. C *R (indicator {x0'..} x *R g x))" unfolding set_integrable_def
      by (intro integrable_scaleR_right) (simp add: abs_mult norm_mult)
  next
    from assms(4) have "set_borel_measurable borel {x0'..} f"
      by (rule set_borel_measurable_subset) (auto simp: x0'_def)
    thus "(λx. indicator {x0'..} x *R f x)  borel_measurable lborel"
      by (simp add: set_borel_measurable_def)
  next
    show "AE x in lborel. norm (indicator {x0'..} x *R f x)
                             norm (C *R (indicator {x0'..} x *R g x))"
      using C by (intro AE_I2) (auto simp: abs_mult indicator_def x0'_def)
  qed
  ultimately have "set_integrable lborel ({a..<x0'}  {x0'..}) f"
    by (rule set_integrable_Un) auto
  also have "{a..<x0'}  {x0'..} = {a..}" by (auto simp: x0'_def)
  finally show ?thesis .
qed


subsection ‹Miscellaneous preliminary material›

(* TODO: Move to HOL-Complex_Analysis.Cauchy_Integral_Formula *)
lemma uniform_limit_analytic_at:
  assumes "uniform_limit A f g F"
  assumes "eventually (λx. f x holomorphic_on A) F"
  assumes "z  A" "open A" "F  bot"
  shows   "g analytic_on {z}"
proof -
  obtain r where r: "r > 0" "cball z r  A"
    using z  A open A by (meson open_contains_cball)
  show ?thesis
  proof (rule holomorphic_uniform_limit[of z r f F g])
    show "F x in F. continuous_on (cball z r) (f x)  f x holomorphic_on ball z r"
      using assms(2)
    proof eventually_elim
      case (elim x)
      have "ball z r  cball z r"
        by auto
      also have "  A"
        by fact
      finally have "f x holomorphic_on ball z r"
        using elim holomorphic_on_subset r by blast
      moreover have "continuous_on (cball z r) (f x)"
        using elim r by (auto intro: holomorphic_on_imp_continuous_on)
      ultimately show ?case by blast
    qed
  next
    assume "g holomorphic_on ball z r"
    thus "g analytic_on {z}"
      using z  A open A analytic_at_ball r(1) by blast
  next
    show "uniform_limit (cball z r) f g F"
      using assms(1) by (rule uniform_limit_on_subset) (use r in auto)
  qed (use assms in auto)
qed

lemma uniform_limit_holomorphic:
  assumes "uniform_limit A f g F"
  assumes "eventually (λx. f x holomorphic_on A) F"
  assumes "open A" "F  bot"
  shows   "g holomorphic_on A"
proof -
  have "g analytic_on A"
    using uniform_limit_analytic_at[OF assms(1,2) _ assms(3,4)] analytic_on_analytic_at
    by blast
  thus ?thesis
    using open A by (simp add: analytic_on_open)
qed

(* TODO: Move to HOL-Analysis.Euler_Mascheroni *)
lemma Re_euler_mascheroni [simp]: "Re euler_mascheroni = euler_mascheroni"
  and Im_euler_mascheroni [simp]: "Im euler_mascheroni = 0"
  by (simp_all add: euler_mascheroni_def)

(* TODO: Move to Deriv. Check if it breaks anything. *)
lemma has_real_derivative_imp_has_vector_derivative [derivative_intros]:
  assumes "(f has_real_derivative f') (at x)"
  shows   "(f has_vector_derivative f') (at x)"
  using assms by (simp add: has_real_derivative_iff_has_vector_derivative)

(* TODO: This version should probably replace the version in the library (without the ') *)
lemma DERIV_real_sqrt_generic':
  assumes "x  0"
  shows "(sqrt has_field_derivative (sgn x * inverse (sqrt x) / 2)) (at x within A)"
  unfolding sqrt_def
  by (rule has_field_derivative_at_within) (use assms in auto intro!: DERIV_real_root_generic)

(* TODO: This version should probably replace the version in the library (without the ') *)
lemma DERIV_real_root_generic':
  assumes "x  0"
  shows "(root n has_real_derivative (inverse (real n * (sgn x * root n x) ^ (n - Suc 0)))) (at x within A)"
proof (cases "n = 0")
  case False
  show ?thesis
    by (rule has_field_derivative_at_within)
       (use False assms in auto intro!: DERIV_real_root_generic simp: field_simps sgn_if)
next
  case True
  have "root 0 = (λx. 0)"
    by (auto simp: root_def fun_eq_iff)
  thus ?thesis
    using True by auto
qed

declare
  DERIV_real_sqrt_generic[THEN DERIV_chain2, derivative_intros del]
  DERIV_real_root_generic[THEN DERIV_chain2, derivative_intros del]
  DERIV_real_sqrt_generic'[THEN DERIV_chain2, derivative_intros] 
  DERIV_real_root_generic'[THEN DERIV_chain2, derivative_intros] 

lemmas [derivative_intros] = has_vector_derivative_real_field


(* TODO: Move to Complex *)
lemma DERIV_complex_norm [derivative_intros]:
  fixes f :: "real  complex"
  assumes "f x  0" and [derivative_intros]: "(f has_vector_derivative D') (at x)"
  shows "((λx. norm (f x)) has_field_derivative ((D'  f x) / norm (f x))) (at x)"
proof -
  have "norm (f x) > 0"
    using assms by simp
  hence *: "(Re (f x))2 + (Im (f x))2 > 0"
    unfolding cmod_def by simp
  hence **: "(Re (f x))2 + (Im (f x))2  0"
    by auto
  from assms have ***: "Re (f x)  0  Im (f x)  0"
    by (auto simp: complex_eq_iff)
  show ?thesis unfolding cmod_def
    using * by derivative (auto simp: divide_simps inner_complex_def)
qed


subsection ‹Preliminary facts about the Gamma and Digamma function›

(* TODO: much of this could be put in the library, to HOL_Analysis.Gamma_Function, or if
   necessary somewhere in HOL-Complex_Analysis (e.g. Complex_Transcendental).
   It is of general interest.
*)

lemma cnj_Digamma:
  assumes [simp]: "s  0"
  shows   "cnj (Digamma s) = Digamma (cnj s)"
proof (rule tendsto_unique)
  have "(λx. (complex_of_real (ln (real x)) - (n<x. inverse (cnj s + of_nat n))))  Digamma (cnj s)"
    (is "?f  _") by (rule tendsto_cnj Digamma_LIMSEQ)+ auto
  also have "?f = (λx. cnj (complex_of_real (ln (real x)) - (n<x. inverse (s + of_nat n))))"
    by simp
  finally show "  Digamma (cnj s)" .
  show "(λx. cnj (complex_of_real (ln (real x)) - (n<x. inverse (s + of_nat n))))  cnj (Digamma s)"
    by (rule tendsto_cnj Digamma_LIMSEQ)+ auto
qed auto

lemma cnj_Polygamma:
  assumes [simp]: "s  0"
  shows   "cnj (Polygamma n s) = Polygamma n (cnj s)"
proof (cases "n = 0")
  case True
  thus ?thesis using cnj_Digamma[of s] by simp
next
  case False
  have "(λk. inverse ((cnj s + of_nat k) ^ Suc n)) sums 
          ((-1) ^ Suc n * Polygamma n (cnj s) / fact n)"
    by (rule Polygamma_LIMSEQ) (use False in auto)
  also have "(λk. inverse ((cnj s + of_nat k) ^ Suc n)) = 
             (λk. cnj (inverse ((s + of_nat k) ^ Suc n)))"
    by simp
  finally have "(λk. cnj (inverse ((s + of_nat k) ^ Suc n))) sums 
                  ((-1) ^ Suc n * Polygamma n (cnj s) / fact n)" .
  moreover have "(λk. cnj (inverse ((s + of_nat k) ^ Suc n))) sums 
                   (cnj ((-1) ^ Suc n * Polygamma n s / fact n))"
    unfolding sums_cnj by (intro Polygamma_LIMSEQ) (use False in auto)
  ultimately have "(-1) ^ Suc n * Polygamma n (cnj s) / fact n = 
                   cnj ((-1) ^ Suc n * Polygamma n s / fact n)"
    by (rule sums_unique2)
  thus ?thesis by simp
qed

lemma norm_rGamma_Im_mono:
  assumes "Re z1 = Re z2" "¦Im z1¦  ¦Im z2¦"
  shows   "norm (rGamma z1)  norm (rGamma z2)"
proof -
  define a b1 b2 where "a = Re z1" and "b1 = ¦Im z1¦" and "b2 = ¦Im z2¦"
  from assms have b12: "0  b1" "b1  b2"
    by (simp_all add: b1_def b2_def)
  have *: "norm (rGamma_series_Weierstrass (Complex a b1) n)  norm (rGamma_series_Weierstrass (Complex a b2) n)" for n
    unfolding rGamma_series_Weierstrass_def norm_mult prod_norm [symmetric]
  proof (intro mult_mono mult_nonneg_nonneg norm_ge_zero exp_ge_zero prod_nonneg ballI prod_mono conjI)
    from b12 show "norm (Complex a b1)  norm (Complex a b2)"
      by (auto simp: cmod_def simp flip: abs_le_square_iff)
  next
    fix i assume i: "i  {1..n}"
    have *: "norm (1 + z / of_nat i) = norm (of_nat i + z) / real i" for z :: complex
    proof -
      have "norm (of_nat i + z) / real i = norm ((of_nat i + z) / of_nat i)"
        by (subst norm_divide) auto
      also have "(of_nat i + z) / of_nat i = 1 + z / of_nat i"
        using i by (simp add: field_simps)
      finally show ?thesis ..
    qed
    show "cmod (1 + Complex a b1 / of_nat i)  cmod (1 + Complex a b2 / of_nat i)"
      using i b12 unfolding *
      by (intro divide_right_mono) (auto simp: cmod_def simp flip: abs_le_square_iff)
  qed auto
  have "norm (rGamma (Complex a b1))  norm (rGamma (Complex a b2))"
    by (rule tendsto_le sequentially_bot tendsto_norm tendsto_norm rGamma_Weierstrass_complex)+
       (rule always_eventually allI *)+
  also have "Complex a b1  {z1, cnj z1}"
    by (auto simp: a_def b1_def complex_eq_iff)
  hence "norm (rGamma (Complex a b1)) = norm (rGamma z1)"
    by (auto simp flip: cnj_rGamma)
  also have "Complex a b2  {z2, cnj z2}"
    by (auto simp: assms a_def b2_def complex_eq_iff)
  hence "norm (rGamma (Complex a b2)) = norm (rGamma z2)"
    by (auto simp flip: cnj_rGamma)
  finally show ?thesis .
qed

lemma Re_Digamma_Im_mono:
  assumes "Re z1 = Re z2" "Re z1 > 0" "¦Im z1¦  ¦Im z2¦"
  shows "Re (Digamma z1)  Re (Digamma z2)"
proof -
  have [simp]: "z1  0" "z2  0"
    using assms by (auto simp: complex_eq_iff)
  define a b1 b2 where "a = Re z1" and "b1 = ¦Im z1¦" and "b2 = ¦Im z2¦"
  have "Re (Digamma (Complex a b1))  Re (Digamma (Complex a b2))"
  proof (rule tendsto_le[OF _ _ _ always_eventually[OF allI]])
    fix k :: nat
    have "(a + real n) / (b22 + (a + real n)2)  (a + real n) / (b12 + (a + real n)2)" for n
      using assms
      by (intro divide_left_mono mult_pos_pos add_nonneg_pos)
         (auto simp: a_def b1_def b2_def abs_le_square_iff)
    hence "(x<k. Re (1 / (of_nat x + Complex a b2)))  (x<k. Re (1 / (of_nat x + Complex a b1)))"
      using assms by (intro sum_mono) (auto simp: Re_divide field_simps)
    thus "Re (complex_of_real (ln (real k)) - (n<k. inverse (Complex a b1 + of_nat n))) 
          Re (complex_of_real (ln (real k)) - (n<k. inverse (Complex a b2 + of_nat n)))"
      by (simp add: field_simps)
  next
    show "sequentially  bot" by simp
  qed ((rule Digamma_LIMSEQ tendsto_Re)+; use assms in simp add: a_def complex_eq_iff)+
  also have "Complex a b1  {z1, cnj z1}"
    by (auto simp: complex_eq_iff a_def b1_def b2_def)
  hence "Re (Digamma (Complex a b1)) = Re (Digamma z1)"
    by (auto simp flip: cnj_Digamma)
  also have "Complex a b2  {z2, cnj z2}"
    by (auto simp: complex_eq_iff a_def b1_def b2_def assms)
  hence "Re (Digamma (Complex a b2)) = Re (Digamma z2)"
    by (auto simp flip: cnj_Digamma)
  finally show ?thesis .
qed
 
lemma Digamma_Re_le_Re_Digamma:
  assumes "Re z > 0"
  shows "Digamma (Re z)  Re (Digamma z)"
proof -
  have "Digamma (Re z) = Re (Digamma (of_real (Re z)))"
    using assms by (subst Polygamma_of_real) auto
  also have "  Re (Digamma z)"
    by (rule Re_Digamma_Im_mono) (use assms in auto)
  finally show ?thesis .
qed
    
lemma Re_Digamma_nonneg:
  assumes "Re z > 0" "Digamma (Re z)  0"
  shows "Re (Digamma z)  0"
proof -
  have "0  Digamma (Re z)"
    using assms by simp
  also have "  Re (Digamma z)"
    by (rule Digamma_Re_le_Re_Digamma) (use assms in auto)
  finally show ?thesis .
qed

lemma norm_Gamma_Re_mono:
  assumes "Im z1 = Im z2" "Re z1  Re z2" "Re z1 > 0" "Digamma (Re z1)  0"
  shows   "norm (Gamma z1)  norm (Gamma z2)"
proof -
  define a1 a2 b where "a1 = Re z1" and "a2 = Re z2" and "b = Im z1"

  have z1: "z1 = complex_of_real a1 + 𝗂 * complex_of_real b"
   and z2: "z2 = complex_of_real a2 + 𝗂 * complex_of_real b"
    by (simp_all add: a1_def a2_def b_def complex_eq_iff assms)

  show "norm (Gamma z1)  norm (Gamma z2)"
    unfolding z1 z2
  proof (rule deriv_nonneg_imp_mono[where g = "λa. norm (Gamma (complex_of_real a + 𝗂 * b))"])
    fix x :: real
    assume x: "x  {a1..a2}"
    with assms have "x > 0" by (auto simp: a1_def)
    define z where "z = complex_of_real x + 𝗂 * complex_of_real b"
    have *: "complex_of_real x + 𝗂 * complex_of_real b  0"
      using x > 0 by (auto elim!: nonpos_Ints_cases simp: complex_eq_iff)
    hence **: "Gamma (complex_of_real x + 𝗂 * complex_of_real b)  0"
      by (subst Gamma_eq_zero_iff) auto
    have "((λx. norm (Gamma (of_real x + 𝗂 * b))) has_field_derivative
            (Gamma z * Digamma z)  Gamma z / norm (Gamma z)) (at x)"
      using * ** by derivative (auto simp: z_def)
    also have "(Gamma z * Digamma z)  Gamma z = Re (Digamma z) * norm (Gamma z) ^ 2"
      by (simp add: inner_complex_def cmod_def power2_eq_square field_simps)
    also have " / norm (Gamma z) = Re (Digamma z) * norm (Gamma z)"
      by (simp add: power2_eq_square)
    finally show "((λx. norm (Gamma (of_real x + 𝗂 * b)))
                    has_field_derivative Re (Digamma z) * norm (Gamma z)) (at x)" .

    from x have "Re z > 0"
      using assms by (auto simp: z_def a1_def)
    moreover {
      have "0  Digamma a1" using assms by (simp add: a1_def)
      also have "  Digamma (Re x)"
        using assms x by (intro Digamma_real_mono) (auto simp: a1_def)
      finally have "Digamma (Re x)  0" .
    }
    ultimately have "Re (Digamma z)  0"
      by (intro Re_Digamma_nonneg) (auto simp: z_def)
    thus "Re (Digamma z) * norm (Gamma z)  0"
      using assms x by (intro mult_nonneg_nonneg) auto
  qed (use assms in auto simp: a1_def a2_def)
qed

lemma norm_Gamma_Im_mono:
  assumes "Re z1 = Re z2" "¦Im z1¦  ¦Im z2¦" "z1  0  z2  0"
  shows   "norm (Gamma z1)  norm (Gamma z2)"
proof (cases "z2  0")
  case True
  hence "Gamma z2 = 0" by (auto simp: Gamma_eq_zero_iff)
  thus ?thesis by simp
next
  case False
  with assms have "z1  0"
    by auto
  with assms False norm_rGamma_Im_mono[of z1 z2] show ?thesis
    by (auto simp: Gamma_def norm_divide field_simps rGamma_eq_zero_iff)
qed

lemma norm_Gamma_bound:
  assumes "Re z  0"
  shows   "norm (Gamma z)  ¦Gamma (Re z)¦"
proof -
  from assms have "z  0"
    by (auto simp: nonpos_Ints_def)
  with norm_Gamma_Im_mono[of "of_real (Re z)" z] assms show ?thesis
    by (auto simp: Gamma_complex_of_real of_real_in_nonpos_Ints_iff)
qed

lemma norm_Gamma_bound':
  assumes "Re z > 0"
  shows   "norm (Gamma z)  Gamma (Re z)"
proof -
  from assms have "Re z  0"
    by (auto elim: nonpos_Ints_cases)
  with assms show ?thesis
    using norm_Gamma_bound[of z] by simp
qed

end