Theory Argument_Principle_Sparse

(*<*)
section ‹A more convenient Argument Principle›
theory Argument_Principle_Sparse
imports "HOL-Complex_Analysis.Complex_Analysis" "Detour_Calculus.Detour_Calculus"
begin

(*TODO: most of the lemmas here should move to HOL-Complex_Analysis*)

text ‹TODO: replace the @{thm isolated_zeros} in the library; 
    optimise the proof of @{thm non_zero_neighbour_alt}
proposition isolated_zeros:
  assumes holf: "f holomorphic_on S"
      and "open S" "connected S" "ξ  S" "β  S" "f β  0"
    obtains r where "0 < r" and "ball ξ r  S" and
        "z. z  ball ξ r - {ξ}  f z  0"
proof (cases "f ξ = 0")
  case True
  obtain r where "0 < r" and r: "ball ξ r  S"
    using open S ξ  S open_contains_ball_eq by blast
  have powf: "((λn. (deriv ^^ n) f ξ / (fact n) * (z - ξ)^n) sums f z)" if "z  ball ξ r" for z
    by (intro holomorphic_power_series [OF _ that] holomorphic_on_subset [OF holf r])
  obtain m where m: "(deriv ^^ m) f ξ / (fact m)  0"
    using holomorphic_fun_eq_0_on_connected [OF holf open S connected S _ ξ  S β  S] f β  0
    by auto
  then have "m  0" using True funpow_0 by fastforce
  obtain s where "0 < s" and s: "z. z  cball ξ s - {ξ}  f z  0"
    using powser_0_nonzero [OF 0 < r powf f ξ = 0 m]
    by (metis m  0 dist_norm mem_ball norm_minus_commute not_gr_zero)
  have "0 < min r s"  by (simp add: 0 < r 0 < s)
  then show thesis
    apply (rule that)
    using r s by auto
next
  case False
  moreover have "isCont f ξ"
    using assms(2) assms(4) field_differentiable_imp_continuous_at holf 
      holomorphic_on_imp_differentiable_at 
    by blast
  ultimately obtain e1 where "e1>0" "y. dist ξ y < e1  f y  0"
    using continuous_at_avoid by blast
  obtain e2 where "e2>0" "ball ξ e2  S"
    using assms(2) assms(4) openE by auto
  define e where "e = min e1 e2"
  have "e>0" by (simp add: 0 < e1 0 < e2 e_def)
  moreover have "y. dist ξ y < e  f y  0" 
    by (simp add: y. dist ξ y < e1  f y  0 e_def)
  moreover have "ball ξ e  S" 
    using ball ξ e2  S e_def by fastforce
  ultimately show thesis
    using that by auto
qed

(* TODO: refactor using sparseness? *)
lemma holomorphic_compact_finite_zeros':
  assumes S: "f holomorphic_on S" "open S" "connected S"
      and "compact K" "K  S"
      and "β  S" "f β 0"
    shows "finite {zK. f z = 0}"
proof (rule ccontr)
  assume "infinite {zK. f z = 0}"
  then obtain z where "z  K" and z: "z islimpt {zK. f z = 0}"
    using compact K by (auto simp: compact_eq_Bolzano_Weierstrass)
  moreover have "{zK. f z = 0}  S"
    using K  S by blast
  ultimately show False
    using assms analytic_continuation [OF S] by blast
qed

lemma holomorphic_imp_sparse_zeros:
  assumes f_holo: "f holomorphic_on D" and "open D" and "connected D"
    and f_nconst: "β  D" "f β  0"
  shows "{x. f x = 0} sparse_in D" 
unfolding sparse_in_open[OF open D]
proof 
  fix y assume "y  D"
  from isolated_zeros[OF f_holo open D connected D y  D β  D f β0]
  obtain r where "0 < r" "ball y r  D" 
      "(z. z  ball y r - {y}  f z  0)"
    by auto
  then show "¬ y islimpt {x. f x = 0}"
    by (smt (verit) Elementary_Metric_Spaces.open_ball centre_in_ball 
        insert_Diff insert_iff islimpt_def mem_Collect_eq)
qed

lemma sparse_in_imp_isolated_singularity:
  assumes "pts sparse_in D" and f_ana: "f analytic_on D - pts" 
    and "z  D" and "open D" 
  shows "isolated_singularity_at f z"
proof -
  have "open (D - (pts-{z}))"
    by (meson Diff_subset assms(1) assms(4) 
        open_diff_sparse_pts sparse_in_subset2)
  then obtain e1 where "e1>0" "ball z e1  D - (pts-{z})"
    by (metis Diff_iff assms(3) insertCI openE)
  then have "f analytic_on ball z e1 - {z}"
    using f analytic_on D - pts 
    by (smt (verit, ccfv_threshold) Diff_eq_empty_iff Diff_iff 
        analytic_on_analytic_at insert_absorb insert_not_empty)
  with e1>0 
  show ?thesis unfolding isolated_singularity_at_def by auto
qed

lemma nconst_sparse_imp_nzero_neighbour:
  assumes f_holo: "f holomorphic_on D - pts" 
    and "open D" and "connected D" and "pts sparse_in D"
    and f_nconst: "¬(wD-pts. f w=0)"
    and "zD"and "not_essential f z"
  shows "(F w in at z. f w  0  w  D - pts)"
proof -
  obtain β where β: "β  D - pts" "f β0"
    using f_nconst by auto

  have ?thesis if "zpts" 
  proof -
    have "F w in at z. f w  0  w  D - pts"
      apply (rule non_zero_neighbour_alt[of f "D-pts" z  β])
      subgoal by fact
      subgoal by (simp add: assms(2) assms(4) open_diff_sparse_pts)
      subgoal using sparse_imp_connected 
        by (metis DIM_complex assms(2) assms(3) assms(4) dual_order.refl)
      subgoal by (simp add: assms(6) that)
      using β by auto
    then show ?thesis by (auto elim:eventually_mono)
  qed
  moreover have ?thesis if "zpts" "¬ f z 0" 
  proof -
    have "F w in at z. w  D - pts"
      by (smt (verit, del_insts) Diff_iff assms(2) assms(4) assms(6) 
          at_within_open eventually_at_topological sparse_in_not_in)
    moreover have "F w in at z. f w  0" 
    proof (cases  "is_pole f z")
      case True
      then show ?thesis using non_zero_neighbour_pole by auto
    next
      case False
      moreover have "not_essential f z" by fact
      ultimately obtain c where "c0" "f z c"
        by (metis ¬ f z 0 not_essential_def)
      then show ?thesis 
        using tendsto_imp_eventually_ne by auto
    qed
    ultimately show ?thesis by eventually_elim auto
  qed
  moreover have ?thesis if "zpts" "f z 0" 
  proof -
    define ff where "ff=(λx. if x=z then 0 else f x)"
    define A where "A=D - (pts - {z})"

    have "f holomorphic_on A - {z}"
      using A_def f_holo by fastforce
    moreover have "open A"  
      by (smt (verit, del_insts) A_def DiffE assms(2) assms(4) 
          islimpt_def open_diff_sparse_pts sparse_in_def)
    ultimately have "ff holomorphic_on A" 
      using f z 0 unfolding ff_def
      by (rule removable_singularity)
    moreover have "connected A"
    proof -
      have "connected (D - pts)"
        by (simp add: assms(2) assms(3) assms(4) sparse_imp_connected)
      moreover have "D - pts  A"
        unfolding A_def by auto
      moreover have "A  closure (D - pts)" unfolding A_def
        by (metis A_def Diff_empty Diff_insert Diff_insert0 
            open A at_within_open closure_subset insert_Diff 
            insert_subset not_trivial_limit_within trivial_limit_at)
      ultimately show ?thesis using connected_intermediate_closure 
        by auto
    qed
    moreover have "z  A" using A_def assms(6) by blast
    moreover have "ff z = 0" unfolding ff_def by auto
    moreover have "β  A " using A_def β(1) by blast
    moreover have "ff β  0" using β(1) β(2) ff_def that(1) by auto
    ultimately obtain r where "0 < r" 
        "ball z r  A" "x. x  ball z r - {z}  ff x  0"
      using open A isolated_zeros[of ff A z β] by auto
    then show ?thesis unfolding eventually_at ff_def
      by (intro exI[of _ r]) (auto simp: A_def dist_commute ball_def)
  qed
  ultimately show ?thesis by auto
qed

subsection ‹Argument principle›

lemma get_analytic_cover_path:
  fixes f :: "complex  complex" and A pts :: "complex set"
  assumes f_analytic: "f analytic_on (path_image γ)" and
          γ: "path γ" (*"pathfinish γ = pathstart γ" *)
  obtains e where "e > 0" "f analytic_on (xpath_image γ. ball x e)"
proof -
  obtain get_e where get_e: "get_e x>0" "f holomorphic_on ball x (get_e x)"
    if "xpath_image γ" for x
    using f_analytic by (metis analytic_at_ball analytic_on_analytic_at)
  
  obtain e where "e>0"
      and "(xpath_image γ. ball x e) 
               (xpath_image γ. ball x (get_e x))"
  proof -
    have "compact (path_image γ)" 
      using path γ by blast
    moreover have "open (xpath_image γ. ball x (get_e x))" 
      by blast
    moreover have "path_image γ  (xpath_image γ. ball x (get_e x))" 
      using get_e(1) by auto
    ultimately show ?thesis
      using compact_subset_open_imp_ball_epsilon_subset that by blast
  qed
  then have "f analytic_on (xpath_image γ. ball x e)"
    using get_e(2) 
    by (smt (verit) open_ball analytic_on_UN analytic_on_open 
        analytic_on_subset)
  then show ?thesis using that e>0 by auto
qed

lemma get_sparse_cover_path:
  assumes sparse: "pts sparse_in (path_image γ)" and "path γ" 
  obtains e where "e > 0" "z (xpath_image γ. ball x e). ¬z islimpt pts"
proof -
  obtain get_e where get_e: "get_e x>0"
      "(y(ball x (get_e x)). ¬ y islimpt pts)" 
    if "xpath_image γ" for x
    using sparse unfolding sparse_in_ball_def by metis

  obtain e where "e>0"
      and "(xpath_image γ. ball x e) 
               (xpath_image γ. ball x (get_e x))"
  proof -
    have "compact (path_image γ)" 
      using path γ by blast
    moreover have "open (xpath_image γ. ball x (get_e x))" 
      by blast
    moreover have "path_image γ  (xpath_image γ. ball x (get_e x))" 
      using get_e(1) by auto
    ultimately show ?thesis
      using compact_subset_open_imp_ball_epsilon_subset that by blast
  qed
  then have "z (xpath_image γ. ball x e). ¬ z islimpt pts"
    using get_e(2) by blast
  then show ?thesis using that e>0 by auto
qed

theorem argument_principle_sparse:
  fixes f:: "complex  complex"
  assumes f_analytic: "f analytic_on A - pts" and
          γ: "valid_path γ" "pathfinish γ = pathstart γ" "path_image γ  A - pts" and
          inside_subset: "inside (path_image γ)  A" and
          not_essential: "ppts. not_essential f p" and 
          sparse: "pts sparse_in A" and
          f_nz: "z. z  A - pts  f z  0" 
  shows "contour_integral γ (λx. deriv f x / f x) = 2 * pi * 𝗂 *
          ( zpts. winding_number γ z * zorder f z)"
proof -
  note [simp del] = div_mult_self3 div_mult_self4 div_mult_self2 div_mult_self1
  define B1 where "B1 = path_image γ  inside (path_image γ)"
  have "compact B1"
  proof -
    have "closed B1" unfolding B1_def 
      apply (rule closed_path_image_Un_inside)
      by (simp add: γ(1) valid_path_imp_path)
    moreover have "bounded B1" unfolding B1_def 
      by (simp add: γ(1) bounded_inside bounded_path_image valid_path_imp_path)
    ultimately show ?thesis using compact_eq_bounded_closed by auto
  qed
  have "connected B1" 
  proof -
    have "closed (path_image γ)"
      by (simp add: assms(2) closed_valid_path_image)
    moreover have "connected (path_image γ)" 
      by (simp add: assms(2) connected_valid_path_image)
    ultimately show ?thesis
      unfolding B1_def using connected_with_inside by auto
  qed

  have "finite (B1  pts)"
  proof (rule sparse_in_compact_finite)
    have "B1  A" using B1_def assms(4) local.inside_subset by auto
    then show "pts sparse_in B1" 
      using sparse_in_subset sparse by auto
  qed fact
  
  obtain get_e where get_e: "get_e x>0" "f holomorphic_on ball x (get_e x)"
    if "xA - pts" for x
    using f_analytic by (metis analytic_at_ball analytic_on_analytic_at)
  obtain get_b where get_b: "get_b x>0"
      "(y(ball x (get_b x)). ¬ y islimpt pts)" if "xA" for x
    using sparse unfolding sparse_in_ball_def by metis


  define g_eb where "g_eb = (λx. ball x (min (get_e x) (get_b x)))"
  have open_g_eb: "open (xpath_image γ. g_eb x)" 
    unfolding g_eb_def using assms(4) get_b(2) by auto
  have g_eb_path_image: "path_image γ   (g_eb ` path_image γ)"
    unfolding g_eb_def 
    using assms(4) get_b(1) get_e(1) by force

  define B2 where "B2 = (xpath_image γ. g_eb x)  inside (path_image γ)"
  have not_islimpt_B2: "¬ z islimpt pts" if "zB2" for z
  proof -
    have ?thesis if "z(xpath_image γ. g_eb x)"
    proof -
      from that
      obtain x where "xpath_image γ" "zg_eb x" 
        using get_b(2) unfolding B2_def by auto
      from xpath_image γ have "xA" 
        using assms(4) by auto
      moreover have "zball x (get_b x)" 
        using zg_eb x unfolding g_eb_def by auto
      ultimately show ?thesis using get_b(2) by auto
    qed
    moreover have ?thesis if "zinside (path_image γ)"
      using get_b(1) get_b(2) local.inside_subset that by auto
    ultimately show ?thesis using that unfolding B2_def by auto
  qed  
  have f_B2_holo: "f holomorphic_on B2 - (B1 pts)"
  proof -
    have "f holomorphic_on (xpath_image γ. g_eb x) - B1  pts" 
    proof -
      have "(xpath_image γ. g_eb x) - B1  pts 
               (xpath_image γ. ball x (get_e x))"
        unfolding g_eb_def by auto
      moreover have "f holomorphic_on (xpath_image γ. ball x (get_e x))"
        using assms(4) get_e(2) 
        by (meson Elementary_Metric_Spaces.open_ball holomorphic_on_UN_open subsetD)
      ultimately show ?thesis by auto
    qed
    moreover have "f holomorphic_on (inside (path_image γ) - B1  pts)" 
      by (smt (verit, best) B1_def Diff_Int2 Diff_eq Un_Int_eq(4)
          analytic_imp_holomorphic f_analytic holomorphic_on_subset inf_commute 
          inf_sup_distrib2 local.inside_subset sup.absorb_iff2)
    moreover have "open ( (g_eb ` path_image γ) - B1  pts)"
      apply (rule open_Diff)
      using open_g_eb finite (B1  pts)[THEN finite_imp_closed] by auto
    moreover have "open (inside (path_image γ) - B1  pts)" 
      by (simp add: γ(1) finite (B1  pts) closed_valid_path_image 
          finite_imp_closed open_Diff open_inside)
    ultimately show ?thesis
      unfolding B2_def Un_Diff by (rule holomorphic_on_Un)
  qed
  have "open B2" 
  proof -
    have "open (inside (path_image γ))" 
      by (simp add: γ(1) closed_valid_path_image open_inside)
    with open_g_eb show ?thesis unfolding B2_def by auto
  qed
  moreover have "B1  B2" 
    using g_eb_path_image unfolding B1_def B2_def by auto
  ultimately obtain e0 where e0: "e0 > 0"  "(xB1. cball x e0)  B2" 
    using compact_subset_open_imp_cball_epsilon_subset[OF compact B1] that 
    by auto
  define e where "e = e0 / 2" 
  have "e>0" unfolding e_def using e0>0 by auto

  have B1_e_open: "open (xB1. ball x e)" by blast
  have connected_B1_ball: "connected (xB1. ball x e)" if "e>0" for e
  proof -
    have "connected (B1  (xB1. ball x e))"
      apply (rule connected_Un_UN)
      subgoal by fact
      subgoal by blast
      subgoal using 0 < e imageE by auto
      done
    moreover have "B1  (xB1. ball x e) = (xB1. ball x e)"
      using 0 < e by auto
    ultimately show "connected (xB1. ball x e)" by simp
  qed

  have "compact (xB1. cball x e)"
    apply (rule compact_minkowski_sum_cball)
    by fact

  ―‹To show f is non-vanishing›
  obtain β where "β  path_image γ" by auto
  then have "β  B1" "β  pts" "f β  0" 
    unfolding B1_def using assms(4) f_nz by auto

  define B3 where "B3 = (xB1. ball x e)"
  define pz where "pz = B3  (pts  {x. f x =0})"
  have "open B3" using B3_def by blast
  have "finite pz" 
  proof -
    have "pz = (B3  pts)  ((B3-B1)  -pts  {x. f x =0})
                             (B1  -pts  {x. f x =0})"
    proof -
      have "B1  B3" unfolding B1_def B3_def using e>0 by auto
      then show ?thesis unfolding pz_def by fast
    qed
    moreover have "(B1  -pts  {x. f x =0}) = {}"
    proof -
      have "B1  A" unfolding B1_def 
        using assms(4) local.inside_subset by auto
      with f_nz show ?thesis by blast
    qed
    moreover have "finite ((B3-B1)  -pts  {x. f x =0})" 
    proof -
      have "finite {z  (xB1. cball x e) - inside (path_image γ). f z = 0}"
      proof (rule holomorphic_compact_finite_zeros')
        from f_B2_holo
        show "f holomorphic_on (xB1. ball x e0) - B1  pts"
          apply (elim holomorphic_on_subset)
          using e0(2) by force
        show "open ((xB1. ball x e0) - B1  pts)"
          by (simp add: finite (B1  pts) 
              finite_imp_sparse open_UN open_diff_sparse_pts)
        show "connected ((xB1. ball x e0) - B1  pts)"
        proof (rule connected_open_delete_finite)
          show "connected (xB1. ball x e0)"
            using connected_B1_ball[OF e0>0] .
        qed (use finite (B1  pts) in auto)

        show "compact ((xB1. cball x e) - inside (path_image γ))"
          using compact (xB1. cball x e) assms(2) 
            closed_valid_path_image compact_diff open_inside 
          by blast
        
        have "(xB1. cball x e)  (xB1. ball x e0)" 
          unfolding e_def using e0>0 by force
        moreover have " B1  pts  inside (path_image γ)" 
          unfolding B1_def using DiffE UnE assms(4) by auto
        ultimately show "(xB1. cball x e) - inside (path_image γ)
             (xB1. ball x e0) - B1  pts"
          by auto
        show "β  (xB1. ball x e0) - B1  pts"
          by (metis Diff_iff IntD2 UN_iff β  B1 β  pts centre_in_ball e0(1))
        show "f β  0" by fact
      qed
      moreover have "((B3-B1)  -pts  {x. f x =0})
           {z  (xB1. cball x e) - inside (path_image γ). f z = 0}"
        unfolding B3_def B1_def using e>0 by force
      ultimately show ?thesis by (elim rev_finite_subset)
    qed
    moreover have "finite (B3  pts)" 
    proof -
      have "finite ((xB1. cball x e)  pts)"
      proof (rule finite_not_islimpt_in_compact)
        show "¬ z islimpt pts" if "z  (xB1. cball x e)" for z 
        proof -
          have "zB2" using e0(2) that e0>0 unfolding e_def by force
          then show ?thesis by (simp add: not_islimpt_B2)
        qed
      qed fact
      moreover have "(B3  pts)  ((xB1. cball x e)  pts)"
        unfolding pz_def B3_def by auto
      ultimately show ?thesis by (elim finite_subset)
    qed
    ultimately show ?thesis by auto
  qed
  have f_holo: "f holomorphic_on B3 - (B1  pts)" 
  proof -
    have "B3 - (B1  pts)  B2 - (B1 pts)"
      using e0 unfolding B3_def e_def by force
    then show ?thesis using f_B2_holo by fast
  qed
  have "connected B3" 
    by (simp add: B3_def 0 < e connected_B1_ball)

  define c where "c  2 * complex_of_real pi * 𝗂 "
  define ff where "ff  (λx. deriv f x / f x)"
  define cont where "cont  λff p e. (ff has_contour_integral c * zorder f p) (circlepath p e)"
  define avoid where "avoid  λp e. wcball p e. w  B3  (w  p  w  pz)"

  have "e>0. avoid p e  (ppz  cont ff p e)" when "pB3" for p
  proof -
    obtain e1 where "e1>0" and e1_avoid: "avoid p e1"
      using finite_cball_avoid[OF open B3 finite pz] pB3 unfolding avoid_def by auto
    have "e2>0. cball p e2  ball p e1  cont ff p e2" when "ppz"
    proof -
      define po where "po  zorder f p"
      define pp where "pp  zor_poly f p"
      define f' where "f'  λw. pp w * (w - p) powi po"
      define ff' where "ff'  (λx. deriv f' x / f' x)"
      obtain r where "pp p0" "r>0" and
          "r<e1" and
          pp_holo: "pp holomorphic_on cball p r" and
          pp_po: "(wcball p r-{p}. f w = pp w * (w - p) powi po  pp w  0)"
      proof -
        have "isolated_singularity_at f p"
        proof -
          have "f holomorphic_on ball p e1 - {p}"
            apply (intro holomorphic_on_subset[OF f_holo])
            using e1_avoid ppz unfolding avoid_def pz_def by force
          then show ?thesis unfolding isolated_singularity_at_def
            using e1>0 analytic_on_open open_delete by blast
        qed
        moreover have "not_essential f p" 
          using p  pz not_essential pz_def 
          by (meson DiffI IntD2 finite (B1  pts) open B3 p  B3 
              f_holo finite_imp_closed not_essential_holomorphic open_Diff)
        moreover have "F w in at p. f w  0"
        proof (rule ccontr)
          assume "¬ (F w in at p. f w  0)"
          then have "F w in at p. f w= 0" unfolding frequently_def by auto

          have "F w in at p. f w  0  w  B3 - B1  pts"
          proof (rule nconst_sparse_imp_nzero_neighbour)
            have "β  B3 - B1  pts" 
              by  (metis B3_def Diff_iff IntE SUP_upper 
                  0 < e β  B1 β  pts open B3 open_contains_ball_eq)
            then show " ¬ (wB3 - B1  pts. f w = 0)"
              using f β0 by blast
            show "B1  pts sparse_in B3"
              by (simp add: finite (B1  pts) finite_imp_sparse)
          qed fact+
          then have "F w in at p. False" using F w in at p. f w= 0
            by eventually_elim simp
          then show False by simp
        qed
        ultimately obtain r where "pp p  0" and r: "r>0" "pp holomorphic_on cball p r"
                  "(wcball p r - {p}. f w = pp w * (w - p) powi po  pp w  0)"
          using zorder_exist[of f p,folded po_def pp_def] by auto
        define r1 where "r1=min r e1 / 2"
        have "r1<e1" unfolding r1_def using e1>0 r>0 by auto
        moreover have "r1>0" "pp holomorphic_on cball p r1"
                  "(wcball p r1 - {p}. f w = pp w * (w - p) powi po  pp w  0)"
          unfolding r1_def using e1>0 r by auto
        ultimately show ?thesis using that pp p0 by auto
      qed

      define e2 where "e2  r/2"
      have "e2>0" using r>0 unfolding e2_def by auto
      define anal where "anal  λw. deriv pp w/ pp w"
      define prin where "prin  λw. po/ (w - p)"
      have "((λw.  prin w + anal w) has_contour_integral c * po) (circlepath p e2)"
      proof (rule has_contour_integral_add[of _ _ _ _ 0,simplified])
        have "ball p r  B3"
          using r<e1 avoid_def ball_subset_cball e1_avoid by (simp add: subset_eq)
        then have "cball p e2  B3"
          using r>0 unfolding e2_def by auto
        then show "(prin has_contour_integral c * po) (circlepath p e2)"
          unfolding prin_def
          apply (intro Cauchy_integral_circlepath_simple[folded c_def])
          using e2>0 by auto
        have "anal holomorphic_on ball p r" unfolding anal_def
          using pp_holo pp_po ball p r  B3 pp p0
          by (auto intro!: holomorphic_intros)
        then show "(anal has_contour_integral 0) (circlepath p e2)"
          using e2_def r>0
          by (auto elim!: Cauchy_theorem_disc_simple)
      qed
      then have "cont ff' p e2" unfolding cont_def po_def
      proof (elim has_contour_integral_eq)
        fix w assume "w  path_image (circlepath p e2)"
        then have "wball p r" and "wp" unfolding e2_def using r>0 by auto
        define wp where "wp  w-p"
        have "wp0" and "pp w 0"
          unfolding wp_def using wp wball p r pp_po by auto
        moreover have der_f': "deriv f' w = po * pp w * (w-p) powi (po - 1) + deriv pp w * (w-p) powi po"
        proof (rule DERIV_imp_deriv)
          have "(pp has_field_derivative (deriv pp w)) (at w)"
            using DERIV_deriv_iff_has_field_derivative pp_holo wp
            by (meson open_ball w  ball p r ball_subset_cball holomorphic_derivI holomorphic_on_subset)
          then show " (f' has_field_derivative of_int po * pp w * (w - p) powi (po - 1)
                  + deriv pp w * (w - p) powi po) (at w)"
            unfolding f'_def using wp
            by (auto intro!: derivative_eq_intros DERIV_cong[OF has_field_derivative_powr_of_int])
        qed
        ultimately show "prin w + anal w = ff' w"
          unfolding ff'_def prin_def anal_def
          apply simp
          apply (unfold f'_def)
          apply (fold wp_def)
          apply (auto simp add:field_simps)
          by (metis (no_types, lifting) mult.commute power_int_minus_mult)
      qed
      then have "cont ff p e2" unfolding cont_def
      proof (elim has_contour_integral_eq)
        fix w assume "w  path_image (circlepath p e2)"
        then have "wball p r" and "wp" unfolding e2_def using r>0 by auto
        have "deriv f' w =  deriv f w"
        proof (rule complex_derivative_transform_within_open[where s="ball p r - {p}"])
          show "f' holomorphic_on ball p r - {p}" unfolding f'_def using pp_holo
            by (auto intro!: holomorphic_intros)
        next
          have "ball p e1 - {p}  B3 - pts"
            using ball_subset_cball e1_avoid[unfolded avoid_def] unfolding pz_def
            by auto
          then have "ball p r - {p}  B3 - pts"
            apply (elim dual_order.trans)
            using r<e1 by auto
          then show "f holomorphic_on ball p r - {p}" 
            using f_holo by auto
        next
          show "open (ball p r - {p})" by auto
          show "w  ball p r - {p}" using wball p r wp by auto
        next
          fix x assume "x  ball p r - {p}"
          then show "f' x = f x"
            using pp_po unfolding f'_def by auto
        qed
        moreover have " f' w  =  f w "
          using w  ball p r ball_subset_cball subset_iff pp_po wp
          unfolding f'_def by auto
        ultimately show "ff' w = ff w"
          unfolding ff'_def ff_def by simp
      qed
      moreover have "cball p e2  ball p e1"
        using 0 < r r<e1 e2_def by auto
      ultimately show ?thesis using e2>0 by auto
    qed
    then obtain e2 where e2: "ppz  e2>0  cball p e2  ball p e1  cont ff p e2"
      by auto
    define e4 where "e4  if ppz then e2 else  e1"
    have "e4>0" using e2 e1>0 unfolding e4_def by auto
    moreover have "avoid p e4" using e2 e1>0 e1_avoid unfolding e4_def avoid_def by auto
    moreover have "ppz  cont ff p e4"
      by (auto simp add: e2 e4_def)
    ultimately show ?thesis by auto
  qed
  then obtain get_e where get_e: "pB3. get_e p>0  avoid p (get_e p)
       (ppz  cont ff p (get_e p))"
    by metis
  define ci where "ci  λp. contour_integral (circlepath p (get_e p)) ff"
  define w where "w  λp. winding_number γ p"
  have "contour_integral γ ff = (ppz. w p * ci p)" unfolding ci_def w_def
  proof (rule Cauchy_theorem_singularities)
    have "open (B3 - pz)" using open_Diff[OF _ finite_imp_closed[OF finite pz]] open B3 
      by auto
    then show "ff holomorphic_on B3 - pz" unfolding ff_def using f_holo 
      by (auto intro!: holomorphic_intros simp add:pz_def)
    show "pB3. 0 < get_e p  (wcball p (get_e p). w  B3  (w  p  w  pz))"
      using get_e using avoid_def by blast
    show "open B3" "connected B3" "finite pz" "valid_path γ" "pathfinish γ = pathstart γ"
      by fact+

    show "path_image γ  B3 - pz" 
    proof -
      have "path_image γ  B3" 
        unfolding B3_def B1_def  using e>0 by auto
      moreover have "path_image γ  - pts" 
        using ComplI assms(4) by auto
      moreover have "path_image γ  - {x. f x = 0}" 
        using assms(4) f_nz by auto
      ultimately show ?thesis unfolding pz_def by auto
    qed

    have "winding_number γ z = 0" if "z  B3" for z
    proof (rule winding_number_zero_in_outside)
      show "z  outside (path_image γ)"
        by (metis B1_def B3_def ComplI SUP_upper 0 < e open B3 
            open_contains_ball_eq that union_with_inside)
      show "path γ" by (simp add: assms(2) valid_path_imp_path)
    qed fact
    then show " z. z  B3  winding_number γ z = 0" by simp
  qed
  also have "... = (ppz. c * w p * zorder f p)"
  proof (rule sum.cong[of pz pz,simplified])
    fix p assume "p  pz"
    show "w p * ci p = c * w p * (zorder f p)"
    proof (cases "pB3")
      assume "p  B3"
      have "ci p = c * (zorder f p)" unfolding ci_def
        apply (rule contour_integral_unique)
        using get_e pB3 ppz unfolding cont_def by metis
      thus ?thesis by auto
    next (*TODO: simplify*)
      assume "pB3"
      then have "w p=0" unfolding w_def 
        using p  pz pz_def by auto
      then show ?thesis by auto
    qed
  qed
  also have "... = c*(ppz. w p * zorder f p)"
    unfolding sum_distrib_left by (simp add:algebra_simps)
  also have "... = c*(p(pzB1). w p * zorder f p)"
  proof -
    have "(ppz. w p * zorder f p) 
            = (p(pzB1). w p * zorder f p)"
    proof (rule sum.mono_neutral_right)
      show "xpz - pz  B1. w x * complex_of_int (zorder f x) = 0"
      proof 
        fix x assume "x  pz - pz  B1"
        then have "x  outside (path_image γ)" unfolding B1_def
          by (auto simp add:  union_with_inside)
        then have "w x = 0"
          unfolding w_def using winding_number_zero_in_outside
          by (simp add: assms(2) assms(3) valid_path_imp_path)
        then show "w x * complex_of_int (zorder f x) = 0" by simp
      qed
      show "finite pz" by fact
      show "pz  B1  pz" by simp
    qed
    then show ?thesis by simp
  qed
  also have "... = c*(p(B1  pts). w p * zorder f p)"
  proof -
    have "B1  B3" unfolding B3_def using e>0 by auto
    moreover have "{x. f x = 0}B1  pts  B1 " 
      using f_nz unfolding B1_def 
      by (smt (verit, del_insts) Diff_iff Int_iff UnE assms(4)
          local.inside_subset mem_Collect_eq subset_eq)
    ultimately have "pzB1 = (B1  pts)" unfolding pz_def by auto
    then show ?thesis by auto
  qed
  also have "... = c*(p(B1  pts). w p * zorder f p)"
    using finite (B1  pts) by auto
  also have "... = c*(ppts. w p * zorder f p)"
  proof -
    have "(p(B1  pts). w p * zorder f p) 
              = (ppts. w p * zorder f p)"
    proof (rule infsum_cong_neutral)
      fix x assume "x  pts - B1  pts" 
      then have "x  outside (path_image γ)" unfolding B1_def
        by (auto simp add:  union_with_inside)
      then have "w x = 0"
        unfolding w_def using winding_number_zero_in_outside
        by (simp add: assms(2) assms(3) valid_path_imp_path)
      then show "w x * complex_of_int (zorder f x) = 0" by simp
    qed auto
    then show ?thesis by simp
  qed
  finally show ?thesis unfolding ff_def c_def w_def by simp
qed

corollary argument_principle_finite:
  fixes f:: "complex  complex"
  assumes f_analytic: "f analytic_on A - pts" and
          γ: "valid_path γ" "pathfinish γ = pathstart γ" "path_image γ  A - pts" and
          inside_subset: "inside (path_image γ)  A" and
          not_essential: "ppts. not_essential f p" and 
          sparse: "finite pts" and
          f_nz: "z. z  A - pts  f z  0" 
  shows "contour_integral γ (λx. deriv f x / f x) = 2 * pi * 𝗂 *
          (zpts. winding_number γ z * zorder f z)"
proof -
  have "contour_integral γ (λx. deriv f x / f x) = 2 * pi * 𝗂 *
          ( zpts. winding_number γ z * zorder f z)"
  proof (rule argument_principle_sparse)
    show " pts sparse_in A" 
      by (simp add: finite_imp_sparse finite pts)
  qed fact+
  moreover have "( zpts. winding_number γ z * zorder f z)
    = (zpts. winding_number γ z * zorder f z)"
    by (rule infsum_finite) fact
  ultimately show ?thesis by simp
qed

corollary argument_principle_ccw_analytic:
  fixes f:: "complex  complex"
  assumes f_analytic: "f analytic_on A - pts" and
          γ: "valid_path γ" "simple_loop_ccw γ" "path_image γ  A - pts" and
          inside_subset: "inside (path_image γ)  A" and
          not_essential: "ppts. not_essential f p" and 
          sparse: "pts sparse_in A" and
          f_nz: "z. z  A - pts  f z  0" 
  shows "contour_integral γ (λx. deriv f x / f x) = 2 * pi * 𝗂 *
          (zptsinside (path_image γ). zorder f z)"
proof -
  have "contour_integral γ (λx. deriv f x / f x) =
          2 * pi * 𝗂 * (zpts. winding_number γ z * (zorder f z))"   
  proof (rule argument_principle_sparse)
    show "pathfinish γ = pathstart γ" 
      by (metis γ(2) simple_loop_ccw_def simple_loop_def)
  qed fact+

  moreover have "(zpts. winding_number γ z * (zorder f z))
    = (zptsinside (path_image γ). complex_of_int (zorder f z))" (is "?L=?R")
  proof - 
    have "?L = (zpts  inside (path_image γ). 
                winding_number γ z * (zorder f z))"
    proof (rule infsum_cong_neutral)
      fix x assume "x  pts - pts  inside (path_image γ)"
      then have "x  outside (path_image γ)"
        using γ(2) γ(3)
        by (metis DiffE Int_iff Set.basic_monos(7)
            inside_simple_loop_iff outside_simple_loop_iff simple_loop_ccw_def)
      then have "winding_number γ x =0" 
        using γ(2) outside_simple_loop_iff simple_loop_ccw_def by blast
      then show "winding_number γ x * complex_of_int (zorder f x) = 0" 
        by simp
    qed auto
    also have "... = (zpts  inside (path_image γ). 
                  winding_number γ z * (zorder f z))"
    proof (rule infsum_finite)
      define B1 where "B1 = path_image γ  inside (path_image γ)"
      have "compact B1"
      proof -
        have "closed B1" unfolding B1_def 
          apply (rule closed_path_image_Un_inside)
          by (simp add: γ(1) valid_path_imp_path)
        moreover have "bounded B1" unfolding B1_def 
          by (simp add: γ(1) bounded_inside bounded_path_image valid_path_imp_path)
        ultimately show ?thesis using compact_eq_bounded_closed by auto
      qed
      moreover have "pts sparse_in B1" 
        unfolding B1_def using assms(4) assms(5) assms(7) 
        by (meson Diff_subset dual_order.trans sparse_in_subset sup_least)
      ultimately have "finite (B1  pts)"
        using sparse_in_compact_finite by auto
      then show "finite (pts  inside (path_image γ))" 
        apply (elim rev_finite_subset)
        unfolding B1_def by auto
    qed
    also have "... = ?R" 
    proof (rule sum.cong)
      fix x assume "x  pts  inside (path_image γ)"
      then have "winding_number γ x = 1" 
        by (metis IntD2 γ(2) inside_simple_loop_iff 
            simple_closed_path_winding_number_inside 
            simple_loop_ccw_def simple_loop_cw_def simple_loop_def
            simple_path_not_cw_and_ccw)
      then show "winding_number γ x *  zorder f x = zorder f x" by simp
    qed simp
    finally show ?thesis .
  qed
  ultimately show ?thesis by simp
qed

end
(*>*)