Theory Detour_Prerequisites

section ‹Prerequisites for the Detour Calculus›
theory Detour_Prerequisites
  imports 
    "HOL-Complex_Analysis.Residue_Theorem" 
    "Winding_Number_Eval.Missing_Analysis"
    "Winding_Number_Eval.Missing_Transcendental"
    "Path_Automation.Path_Automation"
    Path_Nhds
    "HOL-Library.Real_Mod"
begin

subsection ‹Miscellaneous›

lemma inverse_conv_cnj: "norm z = 1  inverse z = cnj z"
  by (simp add: divide_conv_cnj inverse_eq_divide)

lemma dist_cnj: "dist (cnj x) (cnj y) = dist x y"
  by (simp add: dist_norm norm_complex_def power2_commute)

lemma closed_segment_cnj: "closed_segment (cnj w) (cnj z) = cnj ` closed_segment w z"
  using closed_segment_linear_image linear_cnj by blast

lemma closed_segment_mult:
  "(*) (c :: 'a :: real_algebra_1) ` closed_segment a b = closed_segment (c * a) (c * b)"
  by (rule closed_segment_linear_image [symmetric]) auto

lemma arcsin_pos: "x  {0<..1}  arcsin x > 0"
  by (metis arcsin_0 arcsin_less_arcsin greaterThanAtMost_iff neg_le_0_iff_le zero_less_one_class.zero_le_one)

lemma sin_lt_zero': "x  {-pi<..<0}  sin x < 0"
  by (metis greaterThanLessThan_iff minus_less_iff neg_0_less_iff_less sin_gt_zero sin_minus)

lemma tan_arcsin: "x  {-1..1}  tan (arcsin x) = x / sqrt (1 - x ^ 2)"
  by (simp add: tan_def cos_arcsin)

lemma Arg_neg_real [simp]: "Im x = 0  Re x < 0  Arg x = pi"
  using Arg_eq_pi by blast

lemma cis_eq_1_iff':
  assumes "¦x¦ < 2 * pi"
  shows   "cis x = 1  x = 0"
proof
  assume "cis x = 1"
  then obtain n where n: "x = of_int n * 2 * pi"
    by (auto simp: cis_eq_1_iff)
  with assms have "n = 0"
    by (auto simp: abs_mult simp flip: of_int_abs)
  with n show "x = 0"
    by simp
qed auto

lemma DeMoivre_int: "cis x powi n = cis (of_int n * x)"
proof (cases "n  0")
  case True
  hence "cis x powi n = cis x ^ nat n"
    by (simp add: power_int_def)
  also have " = cis (real (nat n) * x)"
    by (rule Complex.DeMoivre)
  finally show ?thesis
    using True by simp
next
  case False
  hence "cis x powi n = cis (-x) ^ nat (-n)"
    by (simp add: power_int_def)
  also have " = cis (-real (nat (-n)) * x)"
    by (subst Complex.DeMoivre) auto
  finally show ?thesis
    using False by simp
qed

lemma cis_of_int_times_pi_half: "cis (of_int n * pi / 2) = 𝗂 powi n"
 using DeMoivre_int[of "pi / 2" n] by simp

lemma cis_of_nat_times_pi_half: "cis (real n * pi / 2) = 𝗂 ^ n"
  using cis_of_int_times_pi_half[of "int n"] by simp

lemma cis_of_int_times_pi: "cis (of_int n * pi) = (-1) powi n"
 using DeMoivre_int[of pi n] by simp

lemma cis_of_nat_times_pi: "cis (real n * pi) = (-1) ^ n"
  using cis_of_int_times_pi[of "int n"] by simp

lemma power3_i [simp]: "𝗂 ^ 3 = -𝗂"
  and power4_i [simp]: "𝗂 ^ 4 = 1"
  by (simp_all add: eval_nat_numeral)

lemma i_power_mod4: "𝗂 ^ n = 𝗂 ^ (n mod 4)"
proof -
  have "𝗂 ^ n = 𝗂 ^ (4 * (n div 4) + n mod 4)"
    by simp
  also have " = 𝗂 ^ (n mod 4)"
    by (subst power_add) (simp add: power_mult)
  finally show ?thesis .
qed

lemma cis_numeral_times_pi_half [simp]:
  "cis (numeral num * pi / 2) = 𝗂 ^ (numeral num mod 4)"
proof -
  have "cis (numeral num * pi / 2) = 𝗂 ^ numeral num"
    using cis_of_nat_times_pi_half[of "numeral num"] by simp
  also have " = 𝗂 ^ (numeral num mod 4)"
    by (rule i_power_mod4)
  finally show ?thesis .
qed

lemma cis_numeral_pi_times_numeral_half [simp]:
  "cis (pi * numeral num / 2) = 𝗂 ^ (numeral num mod 4)"
  by (subst mult.commute) simp

lemma cis_numeral_times_pi [simp]:
  "cis (numeral num * pi) = (-1) ^ (numeral num mod 2)"
proof -
  have "cis (numeral num * pi) = (-1) ^ numeral num"
    using cis_of_nat_times_pi[of "numeral num"] by simp
  also have " = (-1) ^ (numeral num mod 2)"
    by (metis even_mod_2_iff neg_one_even_power neg_one_odd_power)
  finally show ?thesis .
qed

lemma cis_pi_times_numeral [simp]:
  "cis (pi * numeral num) = (-1) ^ (numeral num mod 2)"
  by (subst mult.commute) simp

lemma cis_minus_numeral_times_pi_half [simp]:
  "cis (-(numeral num * pi / 2)) = (-𝗂) ^ (numeral num mod 4)"
  by (subst cis_cnj [symmetric]) auto

lemma cis_minus_numeral_times_pi [simp]:
  "cis (-(numeral num * pi)) = (-1) ^ (numeral num mod 2)"
  by (subst cis_cnj [symmetric]) auto

lemma cis_minus_pi_times_numeral_half [simp]:
  "cis (-(pi * numeral num / 2)) = (-𝗂) ^ (numeral num mod 4)"
  by (subst cis_cnj [symmetric]) auto

lemma cis_minus_pi_times_numeral_pi [simp]:
  "cis (-(pi * numeral num)) = (-1) ^ (numeral num mod 2)"
  by (subst cis_cnj [symmetric]) auto

lemma cis_minus_pi [simp]: "cis (-pi) = -1"
  by (simp flip: cis_cnj)

lemma inner_mult_both_complex: "(z * x :: complex)  (z * y) = norm z ^ 2 * (x  y)"
  unfolding cmod_power2 by (simp add: inner_complex_def algebra_simps power2_eq_square)

lemma orthogonal_transformation_mult_complex [intro]:
  "norm z = 1  orthogonal_transformation ((*) (z :: complex))"
  unfolding orthogonal_transformation_def
  by (auto intro!: linear_times simp: inner_mult_both_complex)

lemma contour_integral_affine:
  assumes "valid_path γ" "c  0"
  shows "contour_integral ((λx. c * x + b)  γ) f = contour_integral γ (λw. c * f (c * w + b))"
proof -
  define ff where "ff=(λx. c*x+b)"
  have "contour_integral (ff  γ) f = contour_integral γ (λw. deriv ff w * f (ff w))"
  proof (rule contour_integral_comp_analyticW)
    show "ff analytic_on UNIV" "path_image γ  UNIV" "valid_path γ"
    unfolding ff_def using valid_path γ
    by (auto intro: analytic_intros)
  qed
  also have " = contour_integral γ (λw. c * f (c * w + b))"
  proof -
    have "deriv ff  x = c" "ff x = c*x+b" for x
      unfolding ff_def by auto
    then show ?thesis by auto
  qed
  finally show ?thesis unfolding ff_def .
qed

(* TODO: unused *)
lemma finite_imp_eventually_sparse_at_0:
  assumes "finite X"
  shows   "eventually (λε. sparse ε X) (at_right 0)"
proof (cases "X = {}  is_singleton X")
  case False
  hence ne: "Sigma X (λx. X - {x})  {}"
    unfolding is_singleton_def by auto
  define m where "m = Min ((λ(x,y). dist x y) ` (Sigma X (λx. X - {x})))"
  have "m > 0"
    unfolding m_def using ne by (subst Min_gr_iff) (use assms in auto)
  show ?thesis
    using eventually_at_right_real[OF m > 0]
  proof eventually_elim
    case (elim ε)
    show ?case
    proof (intro sparseI)
      fix x y assume xy: "x  X" "y  X" "x  y"
      have "ε < m"
        using elim by simp
      also have "m  dist x y"
        using xy unfolding m_def by (intro Min.coboundedI) (use assms in auto)
      finally show "dist x y > ε" .
    qed
  qed
qed (auto elim!: is_singletonE)


subsection ‹Lipschitz continuity›

lemma complex_derivative_on_convex_imp_lipschitz:
  fixes f' :: "complex  complex"
  assumes deriv: "z. z  A  (f has_field_derivative f' z) (at z within A)"
  assumes A: "convex A" and C: "x. x  A  norm (f' x)  C" "C  0"
  shows   "C-lipschitz_on A f"
proof (rule lipschitz_onI)
  fix x y assume xy: "x  A" "y  A"
  let ?l = "linepath y x"
  have "(f' has_contour_integral f (pathfinish ?l) - f (pathstart ?l)) ?l"
  proof (rule contour_integral_primitive)
    show "(f has_field_derivative f' z) (at z within A)" if "z  A" for z
      using deriv[OF that] .
    show "path_image (linepath y x)  A"
      unfolding path_image_linepath by (intro closed_segment_subset xy A)
  qed auto
  hence "norm (f (pathfinish ?l) - f (pathstart ?l))  C * norm (x - y)"
  proof (rule has_contour_integral_bound_linepath)
    fix z assume "z  closed_segment y x"
    also have "closed_segment y x  A"
      by (intro closed_segment_subset xy A)
    finally show "norm (f' z)  C"
      using C(1)[of z] by auto
  qed fact+
  thus "dist (f x) (f y)  C * dist x y"
    by (simp add: dist_norm norm_minus_commute)
qed fact+

(* TODO: convex is *probably* not needed here. Just decompose into a finite union of discs. *)
lemma analytic_on_compact_convex_imp_lipschitz:
  assumes "f analytic_on A" "convex A" "compact A"
  obtains C where "C-lipschitz_on A f"
proof -
  have "deriv f analytic_on A"
    by (intro analytic_intros assms)
  define C where "C = Sup (norm ` (insert 0 (deriv f ` A)))"

  have "compact (insert 0 (deriv f ` A))"
    by (intro compact_insert compact_continuous_image analytic_imp_holomorphic
              holomorphic_on_imp_continuous_on analytic_intros assms)
  hence "bounded (insert 0 (deriv f ` A))"
    by (rule compact_imp_bounded)
  hence bdd: "bdd_above (norm ` insert 0 (deriv f ` A))"
    unfolding bdd_above_norm .

  show ?thesis
  proof (rule that[of C], rule complex_derivative_on_convex_imp_lipschitz)
    show "C  0"
      unfolding C_def by (intro cSup_upper bdd) auto
  next
    show "(f has_field_derivative deriv f z) (at z within A)" if "z  A" for z
      using that assms(1) analytic_on_holomorphic holomorphic_derivI by blast
  next
    show "norm (deriv f z)  C" if "z  A" for z
    proof -
      have "norm (deriv f z)  norm ` insert 0 (deriv f ` A)"
        using that by auto
      moreover have "norm ` insert 0 (deriv f ` A)  {}"
        by blast
      ultimately show "norm (deriv f z)  C"
        unfolding C_def using bdd by (intro cSup_upper)
    qed
  qed fact+
qed

lemma lipschitz_on_complex_inverse:
  assumes "C > 0"
  shows   "(1/C^2)-lipschitz_on {z. Im z  C} (λz. inverse z :: complex)"
proof (rule complex_derivative_on_convex_imp_lipschitz)
  show "((λz. inverse z :: complex) has_field_derivative (-1 / z ^ 2)) (at z within {z. Im z  C})"
    if "z  {z. Im z  C}" for z
    using that assms by (auto intro!: derivative_eq_intros simp: power2_eq_square field_simps)
  show "convex {z. Im z  C}"
    by (simp add: convex_halfspace_Im_ge)
  show "cmod (- 1 / z2)  1 / C2" if "z  {z. Im z  C}" for z
  proof -
    from that and assms have [simp]: "z  0"
      by auto
    have "C ^ 2  Im z ^ 2"
      using assms that by (intro power_mono) auto
    also have "  norm z ^ 2"
      unfolding cmod_power2 by simp
    finally show ?thesis
      using assms by (simp add: field_simps norm_divide norm_power)
  qed
qed (use assms in auto)

lemma lipschitz_on_cnj [lipschitz_intros]:
  fixes f::"'a::metric_space  complex"
  assumes "C-lipschitz_on U f"
  shows "C-lipschitz_on U (λx. cnj (f x))"
proof (rule lipschitz_onI)
  fix x y assume xy: "x  U" "y  U"
  have "dist (cnj (f x)) (cnj (f y)) = norm (cnj (f x) - cnj (f y))"
    by (simp add: dist_norm)
  also have "cnj (f x) - cnj (f y) = cnj (f x - f y)"
    by simp
  also have "norm  = dist (f x) (f y)"
    by (subst complex_mod_cnj) (auto simp: dist_norm)
  also have "  C * dist x y"
    by (rule lipschitz_onD[OF assms]) fact+
  finally show "dist (cnj (f x)) (cnj (f y))  C * dist x y"
    by (simp add: mult_ac)
qed (use assms lipschitz_on_nonneg in blast)

lemma lipschitz_on_cmult_complex [lipschitz_intros]:
  fixes f::"'a::metric_space  complex"
  assumes "C-lipschitz_on U f"
  shows "(norm c * C)-lipschitz_on U (λx. c * f x)"
proof (rule lipschitz_onI)
  have "C  0"
    using assms lipschitz_on_nonneg by blast
  thus "norm c * C  0"
    by simp
next
  fix x y assume xy: "x  U" "y  U"
  have "dist (c * f x) (c * f y) = norm c * dist (f x) (f y)"
    by (metis dist_norm norm_mult vector_space_over_itself.scale_right_diff_distrib)
  also have "  norm c * (C * dist x y)"
    by (intro mult_left_mono lipschitz_onD[OF assms]) (use xy in auto)
  finally show "dist (c * f x) (c * f y)  cmod c * C * dist x y"
    by (simp add: mult_ac)
qed

lemma lipschitz_on_cmult_complex' [lipschitz_intros]:
  fixes f::"'a::metric_space  complex"
  assumes "C-lipschitz_on U f" "C'  norm c * C"
  shows "C'-lipschitz_on U (λx. c * f x)"
  using lipschitz_on_cmult_complex[OF assms(1), of c] assms(2) lipschitz_on_le by blast

lemma lipschitz_on_cadd_left [lipschitz_intros]:
  fixes f :: "_  'b :: real_normed_vector"
  assumes "C-lipschitz_on A f"
  shows   "C-lipschitz_on A (λx. c + f x)"
proof (rule lipschitz_onI)
  fix x y assume "x  A" "y  A" 
  thus "dist (c + f x) (c + f y)  C * dist x y"
    using lipschitz_onD[OF assms, of x y] by (simp add: dist_norm)
qed (use assms lipschitz_on_nonneg in blast)

lemma lipschitz_on_cadd_right [lipschitz_intros]:
  fixes f :: "_  'b :: real_normed_vector"
  assumes "C-lipschitz_on A f"
  shows   "C-lipschitz_on A (λx. f x + c)"
  by (subst add.commute, rule lipschitz_on_cadd_left [OF assms])


subsection ‹Homotopy›

lemma simply_connected_imp_homotopic_paths:
  fixes S :: "'a :: real_normed_vector set"
  assumes "simply_connected S" "path p" "path_image p  S" "path q" "path_image q  S"
  assumes "pathstart q = pathstart p  pathfinish q = pathfinish p"
  shows   "homotopic_paths S p q"
  using assms unfolding simply_connected_eq_homotopic_paths by blast

lemma homotopic_loops_part_circlepath_circlepath:
  assumes "b = a + 2 * pi" "sphere x r  A" "r  0"
  shows   "homotopic_loops A (part_circlepath x r a b) (circlepath x r)"
proof -
  have "homotopic_loops A (shiftpath' (a / (2 * pi)) (circlepath x r)) (circlepath x r)"
    using assms by (intro homotopic_loops_shiftpath'_left) auto
  also have "shiftpath' (a / (2 * pi)) (circlepath x r) = part_circlepath x r a ((a / pi + 2) * pi)"
    by (simp add: shiftpath'_circlepath)
  also have "(a / pi + 2) * pi = b"
    using assms by (simp add: divide_simps del: div_mult_self3 div_mult_self4 div_mult_self2 div_mult_self1)
  finally show ?thesis .
qed

lemma homotopic_loops_reversepath_D:
  "homotopic_loops A p q  homotopic_loops A (reversepath p) (reversepath q)"
  apply (simp add: homotopic_loops_def homotopic_with_def, clarify)
  apply (rule_tac x="h  (λx. (fst x, 1 - snd x))" in exI)
  apply (rule conjI continuous_intros)+
  apply (auto simp: reversepath_def pathstart_def pathfinish_def elim!: continuous_on_subset)
  done

lemma homotopic_loops_reversepath:
  "homotopic_loops A (reversepath p) (reversepath q)  homotopic_loops A p q"
  using homotopic_loops_reversepath_D reversepath_reversepath by metis

lemmas [trans] = homotopic_loops_trans

(* TODO: could probably be generalised a lot. But do we still need it? *)
(* TODO unused *)
lemma homotopic_paths_split:
  assumes p: "path p" and A: "path_image p  A"
  assumes a: "a  {0..1}"
  assumes eq1: "x. x  {0..1}  p1 x = subpath 0 a p x"
  assumes eq2: "x. x  {0..1}  p2 x = subpath a 1 p x"
  shows   "homotopic_paths A p (p1 +++ p2)"
proof -
  have "homotopic_paths (path_image p) (subpath 0 a p +++ subpath a 1 p) (subpath 0 1 p)"
    by (rule homotopic_join_subpaths) (use a p in auto)
  hence "homotopic_paths (path_image p) (subpath 0 1 p) (subpath 0 a p +++ subpath a 1 p)"
    by (simp add: homotopic_paths_sym_eq)
  also have "homotopic_paths (path_image p) (subpath 0 a p +++ subpath a 1 p) (p1 +++ p2)"
    using assms (* TODO cleanup *)
    apply (intro homotopic_paths_eq)
      apply auto
     apply (subst (asm) path_image_join)
    apply auto
      apply (auto simp: joinpaths_def path_image_def split: if_splits)
      apply (metis (no_types, opaque_lifting) atLeastAtMost_iff image_subset_iff le_numeral_extra(3) path_image_def path_image_subpath_subset zero_less_one_class.zero_le_one)
    apply (metis (full_types) atLeastAtMost_iff image_subset_iff le_numeral_extra(4) path_image_def path_image_subpath_subset zero_less_one_class.zero_le_one)
    done
  also have "subpath 0 1 p = p"
    by simp
  finally show ?thesis
    by (rule homotopic_paths_subset) fact
qed


subsection ‹Winding numbers›

(*
TODO: update the definition of "winding_number_prop" by incorporating 
      the condition of "path γ" "z ∉ path_image γ" so that the following
      lemma stands unconditionally.
*)
lemma winding_number_comp_plus:
  assumes "path γ" "z  path_image γ"
  shows "winding_number ((+) c  γ) (z + c) = winding_number γ z"
proof (rule winding_number_unique)
  define f where "f = (λx. c+x)"
  have f_alt:"f = (λx. x+c)" unfolding f_def by auto

  show "p. winding_number_prop (f  γ)
                (z + c) e p (winding_number γ z)"
    if "e>0" for e
  proof -
    obtain p where "winding_number_prop γ z e p (winding_number γ z)"
      using 0 < e assms winding_number by blast
    then have p:"valid_path p" "z  path_image p"
        "pathstart p = pathstart γ"
        "pathfinish p = pathfinish γ"
        "(t{0..1}. cmod (γ t - p t) < e)" and
        p_cont:"contour_integral p (λw. 1 / (w - z)) =
          complex_of_real (2 * pi) * 𝗂 * winding_number γ z" 
      unfolding winding_number_prop_def by auto
    
    have "valid_path (f  p)"
      using p(1) unfolding f_def valid_path_translation_eq by simp
    moreover have "z + c  path_image (f  p)"
      using p(2) unfolding f_def by (auto simp add:path_image_def)
    moreover have "pathstart (f  p) = pathstart (f  γ)" 
      using p(3) by (simp add:pathstart_compose)
    moreover have "pathfinish (f  p) = pathfinish (f  γ)" 
      using p(4) by (simp add:pathfinish_compose)
    moreover have "(t{0..1}. cmod ((f  γ) t 
            - (f  p) t) < e)" 
      using p(5) unfolding f_def by simp
    moreover have "contour_integral (f  p) 
              (λw. 1 / (w - (z + c))) =
                  complex_of_real (2 * pi) * 𝗂 * winding_number γ z"
      unfolding f_alt
      apply (subst contour_integral_affine[where c=1,simplified])
      using p_cont p(1,2) by auto
    ultimately show ?thesis
      apply (rule_tac x="f  p" in exI)
      unfolding winding_number_prop_def by auto
  qed
  show "path (f  γ)"
    using path γ unfolding f_def by (simp add:path_translation_eq)
  show "z + c  path_image (f  γ)"
    using z  path_image γ unfolding path_image_def f_def by auto
qed

lemma winding_number_comp_times:
  assumes "path γ"
        and "z  path_image γ" 
        and "c  0"
      shows "winding_number ((*) c  γ) (z * c) = winding_number γ z"
proof (rule winding_number_unique)
  define f where "f = (λx. c*x)"
  have f_alt:"f = (λx. x*c)" unfolding f_def by auto

  show "p. winding_number_prop (f  γ)
                (z * c) e p (winding_number γ z)"
    if "e>0" for e
  proof -
    have "cmod c>0" using c0 by simp
    then have "(1/cmod c) * e>0"
      using that by auto
    from winding_number[OF assms(1,2) this]
    obtain p where "winding_number_prop γ z ((1/cmod c) * e) 
        p (winding_number γ z)"
      by blast
    then have p:"valid_path p" "z  path_image p"
        "pathstart p = pathstart γ"
        "pathfinish p = pathfinish γ"
        "(t{0..1}. cmod (γ t - p t) < ((1/cmod c) * e))" and
        p_cont:"contour_integral p (λw. 1 / (w - z)) =
          complex_of_real (2 * pi) * 𝗂 * winding_number γ z" 
      unfolding winding_number_prop_def by auto
    
    have "valid_path (f  p)"
      using p(1) c0 valid_path_times 
      unfolding f_def by auto
    moreover have "z * c  path_image (f  p)"
      using p(2) c0 unfolding f_def 
      by (auto simp add:path_image_def)
    moreover have "pathstart (f  p) = pathstart (f  γ)" 
      using p(3) by (simp add:pathstart_compose)
    moreover have "pathfinish (f  p) = pathfinish (f  γ)" 
      using p(4) by (simp add:pathfinish_compose)
    moreover have "cmod ((f  γ) t - (f  p) t) < e"
      if "t{0..1}" for t
    proof -
      have "cmod ((f  γ) t - (f  p) t) = cmod (c*(γ t - p t))"
        unfolding f_def by (auto simp:algebra_simps)
      also have " = cmod c * cmod (γ t - p t)"
        by (auto simp:norm_mult)
      also have " < cmod c * (1 / cmod c * e)"
        using p(5)[rule_format,OF that] cmod c>0 
        using mult_less_cancel_left_pos by blast
      also have " = e"
        using cmod c>0 by auto
      finally show ?thesis .
    qed
    moreover have "contour_integral (f  p)
              (λw. 1 / (w - (z * c))) =
          complex_of_real (2 * pi) * 𝗂 * winding_number γ z" (is "?L=?R")
    proof -
      have "?L = contour_integral p (λw. c / (c * (w - z)))"
        unfolding f_def
        apply (subst contour_integral_affine[where b=0,simplified])
        using p(1,2) c0 by (auto simp:algebra_simps)
      also have " = contour_integral p (λw. 1 / ( w - z))"
        using c0 by simp
      also have " = ?R" using p_cont .
      finally show ?thesis .
    qed
    ultimately show ?thesis
      apply (rule_tac x="f  p" in exI)
      unfolding winding_number_prop_def by auto
  qed
  show "path (f  γ)"
    using path_mult[OF path_const path γ] unfolding f_def comp_def
    by simp
  show "z * c  path_image (f  γ)"
    using z  path_image γ c0 
    unfolding path_image_def f_def by auto
qed

lemma winding_number_part_circlepath_full:
  assumes "y  ball x r" "α + 2 * pi = β"
  shows   "winding_number (part_circlepath x r α β) y = 1"
proof -
  have "0  dist x y"
    by auto
  also have " < r"
    using assms by auto
  finally have "r > 0" .
  have "homotopic_loops (-{y}) (part_circlepath x r α (α + 2 * pi)) (circlepath x r)"
    by (rule homotopic_loops_part_circlepath_circlepath) (use assms r > 0 in auto)
  hence "winding_number (part_circlepath x r α (α + 2 * pi)) y = winding_number (circlepath x r) y"
    by (rule winding_number_homotopic_loops)
  also have " = 1"
    by (intro winding_number_circlepath) (use assms in auto simp: dist_norm norm_minus_commute)
  also have "α + 2 * pi = β"
    using assms(2) by (simp add: algebra_simps)
  finally show ?thesis .
qed

lemma winding_number_part_circlepath_full':
  assumes "y  ball x r" "α - 2 * pi = β"
  shows   "winding_number (part_circlepath x r α β) y = -1"
proof -
  have "0  dist x y"
    by simp
  also from assms have "dist x y < r"
    by auto
  finally have r: "r > 0"
    by simp
  have "winding_number (part_circlepath x r (α - 2 * pi) α) y = 1"
    by (rule winding_number_part_circlepath_full) (use assms in auto)
  also have "part_circlepath x r (α - 2 * pi) α = reversepath (part_circlepath x r α (α - 2 * pi))"
    by simp
  also have "winding_number  y = -winding_number (part_circlepath x r α (α - 2 * pi)) y"
    using path_image_part_circlepath_subset'[of r x α "α - 2 * pi"] r assms
    by (intro winding_number_reversepath) auto
  also have "α - 2 * pi = β"
    using assms(2) by (simp add: algebra_simps)
  finally show ?thesis
    by Groebner_Basis.algebra
qed

lemma winding_number_inverse_valid_path:
  assumes "valid_path γ" "0  path_image γ" "z  path_image γ" "z  0"
  shows "winding_number (inverse  γ) (inverse z) = winding_number γ z - winding_number γ 0"
proof -
  define C where "C = 1 / (complex_of_real (2 * pi) * 𝗂)"
  have "winding_number (inverse  γ) (inverse z)
        = C * contour_integral γ (λw. deriv inverse w / (inverse w - inverse z))"
    unfolding C_def
  proof (rule winding_number_comp[of "UNIV - {0,z}"])
    show "open (UNIV - {0, z})"
      by (metis Diff_insert open_UNIV open_delete)
    show "inverse holomorphic_on UNIV - {0, z}"
      by (auto intro:holomorphic_intros)
    show "path_image γ  UNIV - {0, z}" "inverse z  path_image (inverse  γ)"
      using 0  path_image γ z  path_image γ unfolding path_image_def
      by auto
  qed fact
  also have " = C * (contour_integral γ (λw. 1 / (w - z) - 1 / w))"
  proof -
    have "contour_integral γ (λw. deriv inverse w / (inverse w - inverse z)) =
          contour_integral γ (λw. 1 / (w - z) - 1 / w)"
    proof (rule contour_integral_cong)
      fix x assume "x  path_image γ"
      then have "x0" "xz"
        using 0  path_image γ z  path_image γ unfolding path_image_def
        by auto
      then show "deriv inverse x / (inverse x - inverse z) = 1 / (x - z) - 1 / x"
        using z0
        by (auto simp:divide_simps power2_eq_square algebra_simps)
    qed simp
    then show ?thesis by simp
  qed
  also have " = C * (contour_integral γ (λw. 1 / (w - z)) - contour_integral γ (λw. 1 / w))"
  proof (subst contour_integral_diff)
    show "(λw. 1 / (w - z)) contour_integrable_on γ"
      by (rule contour_integrable_inversediff) fact+
    show "(/) 1 contour_integrable_on γ"
      using contour_integrable_inversediff[OF valid_path γ 0  path_image γ]
      by simp
  qed simp
  also have " =  winding_number γ z - winding_number γ 0"
    unfolding C_def by (subst (1 2) winding_number_valid_path) (use assms in auto simp: algebra_simps)
  finally show ?thesis .
qed

lemma winding_number_inverse:
  assumes "path γ" "path_image γ  {z. Im z > 0}" "z  path_image γ" "Im z > 0"
  shows   "winding_number (inverse  γ) (inverse z) = winding_number γ z - winding_number γ 0"
proof -
  have compact: "compact (Im ` ({z}  (path_image γ)))"
    by (intro compact_continuous_image compact_Un compact_path_image assms continuous_intros) auto
  hence bdd: "bdd_below (Im ` ({z}  path_image γ))"
    by (meson bounded_imp_bdd_below compact_imp_bounded)
  define c' where "c' = Inf (Im ` ({z}  path_image γ))"
  have c'_le: "Im u  c'" if "u  {z}  path_image γ" for u
    using that bdd unfolding c'_def by (meson cINF_lower)
  have "c'  Im ` ({z}  path_image γ)"
    unfolding c'_def using compact by (intro closed_contains_Inf compact_imp_closed bdd) auto
  with assms have "c' > 0"
    by auto
  define c where "c = c' / 2"
  have "c > 0" "c < c'"
    using c' > 0 by (simp_all add: c_def)
  have c: "c > 0" "Im z > c" "u. u  path_image γ  Im u > c"
    using c > 0 c < c' c'_le by force+

  show ?thesis
  proof (rule winding_number_comp')
    show "inverse holomorphic_on {z. Im z > c}"
      using c by (intro holomorphic_intros) auto
    have "(1/c^2)-lipschitz_on {z. Im z > c} inverse"
      by (rule lipschitz_on_subset[OF lipschitz_on_complex_inverse[of c]]) (use c in auto)
    thus "uniformly_continuous_on {z. c < Im z} inverse"
      by (rule lipschitz_on_uniformly_continuous)
    show "inj_on inverse {z. Im z > c}"
      using assms by (auto simp: inj_on_def)
    show "open {z. Im z > c}"
      by (simp add: open_halfspace_Im_gt)
    show "path γ" "path_image γ  {z. Im z > c}" "z  {z. c < Im z}" "z  path_image γ"
      using c assms by auto
    have "F p in valid_path_nhds γ. valid_path p  path_image p  ({z. Im z  c}  {z}) = {}"
      by (intro eventually_conj eventually_valid_path_valid_path_nhds
                eventually_valid_path_nhds_avoid closed_Un closed_halfspace_Im_le)
         (use assms c in force)+
    moreover have "F p in path_nhds γ. winding_number p 0 = winding_number γ 0 
                               winding_number p z = winding_number γ z"
      by (intro eventually_conj eventually_winding_number_eq_path_nhds) (use assms in auto)
    hence "F p in valid_path_nhds γ. winding_number p 0 = winding_number γ 0 
                                      winding_number p z = winding_number γ z"
      by (meson filter_leD path_nhds_le_valid_path_nhds)
    ultimately have "F p in valid_path_nhds γ.
            contour_integral p (λw. deriv inverse w / (inverse w - inverse z)) =
            complex_of_real (2 * pi) * 𝗂 * (winding_number γ z - winding_number γ 0)"
    proof eventually_elim
      case (elim p)
      have "contour_integral p (λw. deriv inverse w / (inverse w - inverse z)) =
            contour_integral p (λw. 1 / (w - z) - 1 / w)"
      proof (rule contour_integral_cong)
        fix x assume "x  path_image p"
        then have "x  0" "x  z"
          using elim c by auto
        then show "deriv inverse x / (inverse x - inverse z) = 1 / (x - z) - 1 / x"
          using assms by (auto simp: divide_simps power2_eq_square)
      qed simp
      also have " = contour_integral p (λw. 1 / (w - z)) - contour_integral p ((/) 1)"
      proof (rule contour_integral_diff)
        show "(λw. 1 / (w - z)) contour_integrable_on p"
          by (rule contour_integrable_inversediff) (use elim in auto)
        have "(λw. 1 / (w - 0)) contour_integrable_on p"
          by (rule contour_integrable_inversediff) (use elim c in auto)
        thus "(λw. 1 / w) contour_integrable_on p"
          by simp
      qed
      also have " = (2 * pi * 𝗂) * (winding_number p z - winding_number p 0)"
        by (subst (1 2) winding_number_valid_path) (use elim c in auto simp: algebra_simps)
      finally show ?case
        using elim by (simp add: algebra_simps)
    qed
    thus "F p in valid_path_nhds γ.
            contour_integral p (λw. deriv inverse w / (inverse w - inverse z)) =
            complex_of_real (2 * pi) * 𝗂 * (winding_number γ z - winding_number γ 0)"
      by (rule eventually_frequently [rotated]) (use assms in auto)
  qed
qed

lemma winding_number_inverse_valid_path_0:
  assumes "valid_path γ" "pathstart γ = pathfinish γ" "0  path_image γ"
  shows   "winding_number (inverse  γ) 0 = -winding_number γ 0"
proof -
  have val: "valid_path (inverse  γ)" using assms
    by (intro valid_path_compose_holomorphic[of _ _ "-{0}"] assms)
       (auto intro!: holomorphic_intros)

  obtain B where B: "w. norm w  B  winding_number γ w = 0"
    using winding_number_zero_at_infinity[of γ] val assms
    by (auto simp: valid_path_imp_path)

  have "compact (path_image γ  path_image (inverse  γ))"
    using assms by (intro compact_Un compact_path_image valid_path_imp_path val) auto
  hence "open (-(path_image γ  path_image (inverse  γ)))"
    by (intro open_Compl compact_imp_closed)
  moreover have "0  -(path_image γ  path_image (inverse  γ))"
    using assms by (auto simp: path_image_compose)
  ultimately obtain ε where ε: "ε > 0" "cball 0 ε  -(path_image γ  path_image (inverse  γ))"
    unfolding open_contains_cball by blast
  define w where "w = complex_of_real (min ε (1 / max 1 B))"

  have pos: "min ε (1 / max 1 B) > 0"
    using ε by (auto simp: min_less_iff_disj)
  hence norm_w: "norm w = min ε (1 / max 1 B)"
    unfolding w_def norm_of_real by simp
  from pos have "norm w  0"
    unfolding norm_w by linarith
  hence "w  0"
    by auto
  have "w  cball 0 ε"
    by (auto simp: norm_w)

  have "B  max 1 B"
    by simp
  also have "max 1 B = 1 / (1 / max 1 B)"
    by (auto simp: field_simps)
  also have "  1 / min ε (1 / max 1 B)"
    using ε by (intro divide_left_mono) auto
  also have " = norm (inverse w)"
    by (simp add: norm w = _ norm_inverse field_simps norm_divide)
  finally have "B  norm (inverse w)" .

  have "w  inverse ` path_image γ"
    using w  cball 0 ε ε unfolding path_image_compose by blast
  hence "inverse w  path_image γ"
    using w  0 by (metis image_iff inverse_inverse_eq)

  have "winding_number (inverse  γ) 0 = winding_number (inverse  γ) w"
  proof (rule winding_number_eq)
    show "w  cball 0 ε" "0  cball 0 ε" "cball 0 ε  path_image (inverse  γ) = {}"
         "connected (cball 0 ε :: complex set)"
      using ε by (auto simp: w_def)
  qed (use assms val in auto intro: valid_path_imp_path simp: pathfinish_def pathstart_def)
  also have "winding_number (inverse  γ) w = winding_number (inverse  γ) (inverse (inverse w))"
    using w  0 by simp
  also have " = winding_number γ (inverse w) - winding_number γ 0"
    using assms w  cball 0 ε w  0 ε inverse w  path_image γ
    by (subst winding_number_inverse_valid_path) (auto simp: path_image_compose)
  also have "winding_number γ (inverse w) = 0"
    using ε B  norm (inverse w) by (intro B)
  finally show ?thesis by (simp add: o_def)
qed

text ‹
  The following allows us to compute the winding number of a non-closed circular arc with
  respect to its centre.
›
lemma winding_number_part_circlepath_centre:
  assumes "r > 0"
  shows   "winding_number (part_circlepath z r a b) z = (b - a) / (2 * pi)"
proof -
  have "winding_number (part_circlepath 0 r a b) 0 = (b - a) / (2 * pi)"
  proof (induction a b rule: linorder_wlog)
    case (le a b)
    show ?case
    proof (cases "a = b")
      case True
      thus ?thesis
        using assms by (simp add: part_circlepath_empty winding_number_zero_const)
    next
      case False
      let ?p = "part_circlepath 0 r a b"
      have "((λt. 𝗂) has_integral (b - a) *R 𝗂) {a..b}"
        using has_integral_const[of 𝗂 a b] assms False le by (simp add: scaleR_conv_of_real mult_ac)
      hence "((λz. 1 / z) has_contour_integral (b - a) *R 𝗂) ?p"
        by (subst has_contour_integral_part_circlepath_iff) (use assms False le in simp_all)
      moreover have "0  path_image ?p"
        using assms by (auto simp: path_image_part_circlepath')
      hence "((λz. 1 / z) has_contour_integral 2 * 𝗂 * pi * winding_number ?p 0) ?p"
        using has_contour_integral_winding_number[of ?p 0] assms by (auto simp add: mult_ac)
      ultimately have "(b - a) *R 𝗂 = 2 * 𝗂 * pi * winding_number ?p 0"
        using has_contour_integral_unique by blast
      thus ?thesis
        by (simp add: scaleR_conv_of_real)
    qed
  next
    case (sym a b)
    from assms have "0  path_image (part_circlepath 0 r a b)"
      by (auto simp: path_image_part_circlepath')
    thus ?case using assms sym
      by (simp add: reversepath_part_circlepath [symmetric, of 0 r b a]
                    winding_number_reversepath field_simps del: reversepath_part_circlepath)
  qed
  hence "winding_number ((+) z  part_circlepath 0 r a b) (0 + z) = (b - a) / (2 * pi)"
    by (subst winding_number_comp_plus) (use assms in auto simp: path_image_part_circlepath')
  thus ?thesis
    by (subst (asm) part_circlepath_translate) auto
qed


subsection ‹Continuous transformations that preserve the winding number in some way›

locale winding_preserving =
  fixes A :: "complex set" and f :: "complex  complex" and g :: "complex  complex"
  assumes inj: "inj_on f A"
  assumes cont: "continuous_on A f"
  assumes winding_number_eq:
    "p x. path p  path_image p  A  pathstart p = pathfinish p  x  A - path_image p 
        winding_number (f  p) (f x) = g (winding_number p x)"

lemma winding_preserving_comp:
  assumes "winding_preserving B f2 g2"
  assumes "winding_preserving A f1 g1"
  assumes subset: "f1 ` A  B"
  shows   "winding_preserving A (f2  f1) (g2  g1)"
proof
  interpret f1: winding_preserving A f1 g1
    by fact
  interpret f2: winding_preserving B f2 g2
    by fact
  show "inj_on (f2  f1) A"
    by (intro comp_inj_on f1.inj inj_on_subset[OF f2.inj] assms)
  show "continuous_on A (f2  f1)"
    by (intro continuous_on_compose f1.cont continuous_on_subset[OF f2.cont] subset)
  show "winding_number (f2  f1  p) ((f2  f1) x) = (g2  g1) (winding_number p x)"
    if p: "path p" "path_image p  A" "x  A - path_image p" "pathstart p = pathfinish p" for p x
  proof -
    have [simp]: "f1 x = f1 (p t)  x = p t" if "t  {0..1}" for t
      using f1.inj that p unfolding inj_on_def path_image_def by blast
    have "winding_number (f2  f1  p) ((f2  f1) x) =
          winding_number (f2  (f1  p)) (f2 (f1 x))"
      by (simp add: o_def)
    also have " = g2 (winding_number (f1  p) (f1 x))"
    proof (rule f2.winding_number_eq)
      show "path (f1  p)"
        using that by (intro path_continuous_image continuous_on_subset[OF f1.cont])
      show "path_image (f1  p)  B"
        using that subset by (auto simp: path_image_def)
      show "f1 x  B - path_image (f1  p)"
        using that subset f1.inj by (auto simp: path_image_def)
    qed (use p in auto simp: pathstart_compose pathfinish_compose)
    also have "winding_number (f1  p) (f1 x) = g1 (winding_number p x)"
      by (rule f1.winding_number_eq) (use p in auto)
    finally show ?thesis
      by (simp add: o_def)
  qed
qed

lemmas winding_preserving_comp' = winding_preserving_comp [unfolded o_def]

lemma winding_preserving_subset:
  assumes "winding_preserving A f g" "B  A"
  shows   "winding_preserving B f g"
proof -
  interpret winding_preserving A f g
    by fact
  show ?thesis
  proof
    show "inj_on f B"
      by (rule inj_on_subset[OF inj]) (use assms(2) in auto)
    show "continuous_on B f"
      by (rule continuous_on_subset[OF cont]) (use assms(2) in auto)
    show "winding_number (f  p) (f x) = g (winding_number p x)"
      if "path p" "path_image p  B" "x  B - path_image p" "pathstart p = pathfinish p" for p x
      using that winding_number_eq[of p x] assms(2) by auto
  qed
qed

lemma winding_preserving_translate: "winding_preserving A (λx. c + x) (λx. x)"
proof
  show "winding_number ((+) c  p) (c + x) = winding_number p x"
    if "path p" "path_image p  A" "x  A - path_image p" for p x
    using that winding_number_comp_plus[of p x c] by (auto simp: algebra_simps)
qed (auto intro!: inj_onI continuous_intros)

lemma winding_preserving_mult: "c  0  winding_preserving A (λx. c * x) (λx. x)"
proof
  assume "c  0"
  show "winding_number ((*) c  p) (c * x) = winding_number p x"
    if "path p" "path_image p  A" "x  A - path_image p" for p x
    using that winding_number_comp_times[of p x c] c  0 by (auto simp: algebra_simps)
qed (auto intro!: inj_onI continuous_intros)

lemma winding_preserving_cnj: "winding_preserving A cnj (λx. -cnj x)"
proof
  show "winding_number (cnj  p) (cnj x) = -cnj (winding_number p x)"
    if "path p" "path_image p  A" "x  A - path_image p" for p x
    using that winding_number_cnj[of p x] by (auto simp: algebra_simps)
qed (auto intro!: inj_onI continuous_intros)

lemma winding_preserving_uminus: "winding_preserving A (λx. -x) (λx. x)"
  using winding_preserving_mult[of "-1" A] by simp



subsection ‹Paths›

lemma simple_path_cnj [simp]: "simple_path (cnj  p)  simple_path p"
  by (rule simple_path_linear_image_eq) (auto intro!: linear_cnj simp: inj_def)

lemma part_circlepath_cnj': "cnj  part_circlepath c r a b = part_circlepath (cnj c) r (-a) (-b)"
  unfolding o_def by (intro ext part_circlepath_cnj)

lemma linepath_minus: "linepath (-a) (-b) x = -linepath a b x"
  by (simp add: linepath_def algebra_simps)

text ‹
  The following lemma is very difficult to bypass some nasty geometric reasoning:
  If a path only touches the frontier of a set at its beginning or end,
  then it is either fully inside the set or fully outside the set.
›
lemma path_fully_inside_or_fully_outside:
  fixes p :: "real  'a :: euclidean_space"
  assumes "path p" "x. x  {0<..<1}  p x  frontier A"
  shows   "path_image p  closure A  path_image p  interior A = {}"
proof (rule ccontr)
  assume *: "¬(path_image p  closure A  path_image p  interior A = {})"
  from * obtain x y where xy: "x  {0..1}" "y  {0..1}" "p x  closure A" "p y  interior A"
    unfolding path_image_def by blast
  define x' y' where "x' = min x y" and "y' = max x y"
  have xy': "x'  {0..1}" "y'  {0..1}" "x'  y'"
    using xy by (auto simp: x'_def y'_def)

  define q where "q = subpath x' y' p"
  have [simp]: "path q"
    using xy' by (auto simp: q_def assms)

  have "path_image q  frontier A  {}"
  proof (rule connected_Int_frontier)
    show "connected (path_image q)"
      by auto
  next
    have "q (if x  y then 0 else 1)  path_image q"
      by (auto simp: path_image_def)
    moreover have "q (if x  y then 0 else 1)  A"
      using xy closure_subset by (auto simp: q_def subpath_def x'_def y'_def)
    ultimately show "path_image q - A  {}"
      by blast
  next
    have "q (if x  y then 1 else 0)  path_image q"
      by (auto simp: path_image_def)
    moreover have "q (if x  y then 1 else 0)  A"
      using xy interior_subset by (auto simp: q_def subpath_def x'_def y'_def)
    ultimately show "path_image q  A  {}"
      by blast
  qed
  then obtain w where w: "w  {x'..y'}" "p w  frontier A"
    unfolding q_def path_image_subpath using xy' by (force split: if_splits)

  have "w  x"
    using xy w by (metis DiffE frontier_def)
  moreover have "w  y"
    using xy w unfolding frontier_def by auto
  ultimately have "w  {x'<..<y'}"
    using w(1) by (auto simp: x'_def y'_def)
  also have "  {0<..<1}"
    using xy' by auto
  finally have "w  {0<..<1}" .
  with assms(2)[of w] have "p w  frontier A"
    by blast
  with p w  frontier A show False
    by contradiction
qed

lemma simple_path_joinE':
  assumes "simple_path (g1 +++ g2)" and "pathfinish g1 = pathstart g2"
  shows   "path_image g1  path_image g2  
             (insert (pathstart g2) (if pathstart g1 = pathfinish g2 then {pathstart g1} else {}))"
  using assms
  by (smt (verit, del_insts) arc_join_eq insert_commute pathfinish_join pathstart_join simple_path_cases simple_path_joinE)

lemma simple_path_joinE'':
  assumes "simple_path (g1 +++ g2)" and "pathfinish g1 = pathstart g2"
          "x  path_image g1" "x  path_image g2"
  shows   "x = pathstart g2  x = pathfinish g2  pathstart g1 = pathfinish g2"
  using simple_path_joinE'[OF assms(1,2)] assms(3,4) by (auto split: if_splits)
  
lemma arc_continuous_image:
  assumes "arc p" "inj_on f (path_image p)" "continuous_on (path_image p) f"
  shows   "arc (f  p)"
  using assms by (auto simp: arc_def inj_on_def path_image_def intro!: path_continuous_image)

lemma eventually_path_image_cball_subset:
  fixes p :: "real  'a :: real_normed_vector"
  assumes "path p" "path_image p  interior A"
  shows "eventually (λε. (xpath_image p. cball x ε)  A) (at_right 0)"
proof -
  from assms have "compact ( path_image p)"
    by (intro compact_path_image) auto
  moreover have "path_image p  frontier A = {}"
    using assms by (simp add: disjoint_iff frontier_def subset_eq)
  ultimately have "eventually (λε. setdist_gt ε (path_image p) (frontier A)) (at_right 0)"
    by (intro compact_closed_imp_eventually_setdist_gt_at_right_0) auto
  moreover have "eventually (λε. ε > 0) (at_right (0 :: real))"
    by (simp add: eventually_at_right_less)
  ultimately have "eventually (λε. (xpath_image p. cball x ε)  A) (at_right 0)"
  proof eventually_elim
    case (elim ε)
    show "(xpath_image p. cball x ε)  A"
    proof (intro subsetI, elim UN_E)
      fix x y assume xy: "x  path_image p" "y  cball x ε"
      show "y  A"
      proof (rule ccontr)
        assume "y  A"
        have "cball x ε  frontier A  {}"
        proof (rule connected_Int_frontier)
          have "x  cball x ε"
            using ε > 0 by simp
          moreover have "x  A"
            using x  path_image p assms interior_subset by blast
          ultimately show "cball x ε  A  {}"
            by blast
        next
          from xy show "cball x ε - A  {}"
            using y  A by blast
        qed auto
        hence "¬setdist_gt ε {x} (frontier A)"
          by (force simp: setdist_gt_def)
        moreover have "setdist_gt ε {x} (frontier A)"
          by (rule setdist_gt_mono[OF elim(1)]) (use xy in auto)
        ultimately show False by contradiction
      qed
    qed
  qed
  thus ?thesis
    by eventually_elim (use assms interior_subset in auto)
qed

lemma eventually_path_image_cball_subset':
  fixes p :: "real  'a :: real_normed_vector"
  assumes "path p" "path_image p  interior A" "X  path_image p"
  shows "eventually (λε. path_image p  (xX. cball x ε)  A) (at_right 0)"
  using eventually_path_image_cball_subset[OF assms(1-2)]
        eventually_at_right_less[of 0]
proof eventually_elim
  case (elim ε)
  have "path_image p  (xX. cball x ε) = (xpath_image p. {x})  (xX. cball x ε)"
    by blast
  also have "  (xpath_image p. cball x ε)  (xX. cball x ε)"
    using elim(2) by (intro Un_mono UN_mono) auto
  also have " = (xpath_image p  X. cball x ε)"
    by blast
  also have "path_image p  X = path_image p"
    using assms by blast
  also have "(xpath_image p. cball x ε)  A"
    by fact
  finally show ?case .
qed

(* TODO: better name? *)
text ‹
  We say that a path does not cross a set A› if it enters A› at most at its beginning and
  end, and never inbetween.
›
definition does_not_cross :: "(real  'a :: real_vector)  'a set  bool" where
  "does_not_cross p A  (x{0<..<1}. p x  A)"

lemma does_not_cross_simple_path:
  assumes "simple_path p"
  shows   "does_not_cross p A  path_image p  A  {pathstart p, pathfinish p}"
proof
  assume "does_not_cross p A"
  thus "path_image p  A  {pathstart p, pathfinish p}" using assms
    by (force simp: does_not_cross_def simple_path_def path_image_def pathstart_def pathfinish_def)
next
  assume *: "path_image p  A  {pathstart p, pathfinish p}"
  show "does_not_cross p A"
    unfolding does_not_cross_def
  proof safe
    fix x :: real assume x: "x  {0<..<1}" "p x  A"
    hence "p x  {pathstart p, pathfinish p}"
      using assms by (force simp: simple_path_def loop_free_def pathstart_def pathfinish_def)+
    moreover from x have "p x  path_image p"
      by (auto simp: path_image_def)
    ultimately show False
      using * and x by blast
  qed
qed

lemma path_fully_inside_or_fully_outside':
  fixes p :: "real  'a :: euclidean_space"
  assumes "path p" "does_not_cross p (frontier A)"
  shows   "path_image p  closure A  path_image p  interior A = {}"
  using path_fully_inside_or_fully_outside[OF assms(1), of A] assms unfolding does_not_cross_def
  by auto

lemma in_path_image_part_circlepathI:
  assumes "y = x + rcis r u" "u  closed_segment a b"
  shows   "y  path_image (part_circlepath x r a b)"
proof -
  have "u  path_image (linepath a b)"
    using assms by simp
  then obtain v where v: "v  {0..1}" "u = linepath a b v"
    unfolding path_image_def by blast
  have "y = part_circlepath x r a b v"
    by (simp add: part_circlepath_altdef v assms)
  with v(1) show ?thesis
    unfolding path_image_def by blast
qed

lemma in_path_image_part_circlepathI':
  assumes "Arg (y - x)  closed_segment a b" "dist y x = r"
  shows   "y  path_image (part_circlepath x r a b)"
proof (rule in_path_image_part_circlepathI)
  show "y = x + rcis r (Arg (y - x))"
    using assms
    by (cases "x = y")
       (auto simp: rcis_def cis_Arg complex_sgn_def dist_commute scaleR_conv_of_real
                   field_simps dist_norm norm_minus_commute)
qed fact

lemma path_image_part_circlepath':
  "path_image (part_circlepath x r a b) = (λt. x + rcis r t) ` closed_segment a b"
proof -
  have "path_image (part_circlepath x r a b) = (λt. x + rcis r t) ` path_image (linepath a b)"
    by (simp add: path_image_def part_circlepath_altdef image_image)
  thus ?thesis
    by simp
qed

lemma path_image_part_circlepath:
  assumes "a  {-pi<..pi}" "b  {-pi<..pi}" "r > 0"
  shows   "path_image (part_circlepath x r a b) = {ysphere x r. Arg (y - x)  closed_segment a b}"
proof safe
  fix y assume y: "y  path_image (part_circlepath x r a b)"
  show "y  sphere x r"
    using y path_image_part_circlepath_subset'[of r x a b] assms by auto

  from y obtain u where u: "u  {0..1}" "y = part_circlepath x r a b u"
    by (auto simp: path_image_def)
  have "linepath a b u  closed_segment a b"
    using linepath_in_path u(1) by blast
  also have "  {-pi<..pi}"
    using assms by (intro closed_segment_subset) auto
  finally have *: "linepath a b u  {-pi<..pi}" .

  have "Arg (y - x) = Arg (rcis r (linepath a b u))"
    by (simp add: u part_circlepath_altdef)
  also have " = linepath a b u"
    using * assms by (subst Arg_rcis) auto
  also have "  closed_segment a b"
    by fact
  finally show "Arg (y - x)  closed_segment a b" .
next
  fix y assume y: "y  sphere x r" "Arg (y - x)  closed_segment a b"
  show "y  path_image (part_circlepath x r a b)"
    using y by (intro in_path_image_part_circlepathI') (auto simp: dist_commute)
qed

lemma path_image_part_circlepath_mono:
  assumes "min a' b'  min a b" "max a' b'  max a b"
  shows   "path_image (part_circlepath x r a b)  path_image (part_circlepath x r a' b')"
proof -
  have "path_image (part_circlepath x r a b) = (λt. x + rcis r t) ` path_image (linepath a b)"
    by (simp add: path_image_def part_circlepath_altdef image_image)
  also have "  (λt. x + rcis r t) ` path_image (linepath a' b')"
    unfolding path_image_linepath using assms
    by (intro image_mono) (auto simp: closed_segment_eq_real_ivl split: if_splits)
  also have " = path_image (part_circlepath x r a' b')"
    by (simp add: path_image_def part_circlepath_altdef image_image)
  finally show ?thesis .
qed


subsection ‹Topology›

lemma eventually_at_within_in_open:
  assumes "open X" "x  X"
  shows   "eventually (λz. z  X  A - {x}) (at x within A)"
  using eventually_nhds_in_open[OF assms] unfolding eventually_at_filter
  by eventually_elim auto

lemma filterlim_at_withinD:
  assumes "filterlim f (at L within A) F" "open X" "L  X"
  shows   "eventually (λx. f x  X  A - {L}) F"
proof -
  have "eventually (λz. z  X  A - {L}) (at L within A)"
    by (rule eventually_at_within_in_open) (use assms in auto)
  moreover from assms(1) have "filtermap f F  at L within A"
    unfolding filterlim_def .
  ultimately have "eventually (λz. z  X  A - {L}) (filtermap f F)"
    unfolding le_filter_def by blast
  thus ?thesis
    by (simp add: eventually_filtermap)
qed

lemma filterlim_at_withinD':
  assumes "filterlim f (at L within A) F" "open X" "L  X"
  shows   "eventually (λx. f x  X  A) F"
  using filterlim_at_withinD[OF assms] by eventually_elim auto

lemma filterlim_at_rightD:
  assumes "filterlim f (at_right L) F" "a > L"
  shows   "eventually (λx. f x  {L<..<a}) F"
  using filterlim_at_withinD'[OF assms(1), of "{..<a}"] assms(2)
  by (auto elim!: eventually_mono)

lemma filterlim_at_leftD:
  assumes "filterlim f (at_left L) F" "a < L"
  shows   "eventually (λx. f x  {a<..<L}) F"
  using filterlim_at_withinD'[OF assms(1), of "{a<..}"] assms(2)
  by (auto elim!: eventually_mono)

lemma eventually_Ball_at_right_0_real:
  assumes "eventually P (at_right (0 :: real))"
  shows   "eventually (λx. y{0<..x}. P y) (at_right 0)"
  using assms unfolding eventually_at_right_field by force   

lemma open_segment_same_Re:
  assumes "Re a = Re b"
  shows   "open_segment a b = {z. Re z = Re a  Im z  open_segment (Im a) (Im b)}"
  using assms by (auto simp: open_segment_def closed_segment_same_Re complex_eq_iff)

lemma open_segment_same_Im:
  assumes "Im a = Im b"
  shows   "open_segment a b = {z. Im z = Im a  Re z  open_segment (Re a) (Re b)}"
  using assms by (auto simp: open_segment_def closed_segment_same_Im complex_eq_iff)

lemma interior_halfspace_Re_ge [simp]: "interior {z. Re z  x} = {z. Re z > x}"
  and interior_halfspace_Re_le [simp]: "interior {z. Re z  x} = {z. Re z < x}"
  and interior_halfspace_Im_ge [simp]: "interior {z. Im z  x} = {z. Im z > x}"
  and interior_halfspace_Im_le [simp]: "interior {z. Im z  x} = {z. Im z < x}"
  using interior_halfspace_ge[of "1::complex" x] interior_halfspace_le[of "1::complex" x]
        interior_halfspace_ge[of 𝗂 x] interior_halfspace_le[of 𝗂 x]
  by (simp_all add: inner_complex_def)

thm arc_def
thm simple_path_def

end