/-
Copyright (c) 2021 Justus Springer. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Justus Springer
-/
import algebra.category.Group.basic
import algebra.category.Mon.filtered_colimits

/-!
# The forgetful functor from (commutative) (additive) groups preserves filtered colimits.

Forgetful functors from algebraic categories usually don't preserve colimits. However, they tend
to preserve _filtered_ colimits.

In this file, we start with a small filtered category `J` and a functor `F : J ⥤ Group`.
We show that the colimit of `F ⋙ forget₂ Group Mon` (in `Mon`) carries the structure of a group,
thereby showing that the forgetful functor `forget₂ Group Mon` preserves filtered colimits. In
particular, this implies that `forget Group` preserves filtered colimits. Similarly for `AddGroup`,
`CommGroup` and `AddCommGroup`.

-/

universe v

noncomputable theory
open_locale classical

open category_theory
open category_theory.limits
open category_theory.is_filtered (renaming max → max') -- avoid name collision with `_root_.max`.

namespace Group.filtered_colimits

section

open Mon.filtered_colimits (colimit_one_eq colimit_mul_mk_eq)

-- We use parameters here, mainly so we can have the abbreviations `G` and `G.mk` below, without
-- passing around `F` all the time.
parameters {J : Type v} [small_category J] [is_filtered J] (F : J ⥤ Group.{v})

/--
The colimit of `F ⋙ forget₂ Group Mon` in the category `Mon`.
In the following, we will show that this has the structure of a group.
-/
@[to_additive "The colimit of `F ⋙ forget₂ AddGroup AddMon` in the category `AddMon`.
In the following, we will show that this has the structure of an additive group."]
abbreviation G : Mon := Mon.filtered_colimits.colimit (F ⋙ forget₂ Group Mon)

/-- The canonical projection into the colimit, as a quotient type. -/
@[to_additive "The canonical projection into the colimit, as a quotient type."]
abbreviation G.mk : (Σ j, F.obj j) → G := quot.mk (types.quot.rel (F ⋙ forget Group))

@[to_additive]
lemma G.mk_eq (x y : Σ j, F.obj j)
  (h : ∃ (k : J) (f : x.1 ⟶ k) (g : y.1 ⟶ k), F.map f x.2 = F.map g y.2) :
  G.mk x = G.mk y :=
quot.eqv_gen_sound (types.filtered_colimit.eqv_gen_quot_rel_of_rel (F ⋙ forget Group) x y h)

/-- The "unlifted" version of taking inverses in the colimit. -/
@[to_additive "The \"unlifted\" version of negation in the colimit."]
def colimit_inv_aux (x : Σ j, F.obj j) : G :=
G.mk ⟨x.1, x.2 ⁻¹⟩

@[to_additive]
lemma colimit_inv_aux_eq_of_rel (x y : Σ j, F.obj j)
  (h : types.filtered_colimit.rel (F ⋙ forget Group) x y) :
  colimit_inv_aux x = colimit_inv_aux y :=
begin
  apply G.mk_eq,
  obtain ⟨k, f, g, hfg⟩ := h,
  use [k, f, g],
  rw [monoid_hom.map_inv, monoid_hom.map_inv, inv_inj],
  exact hfg,
end

/-- Taking inverses in the colimit. See also `colimit_inv_aux`. -/
@[to_additive "Negation in the colimit. See also `colimit_neg_aux`."]
instance colimit_has_inv : has_inv G :=
{ inv := λ x, begin
   refine quot.lift (colimit_inv_aux F) _ x,
  intros x y h,
  apply colimit_inv_aux_eq_of_rel,
  apply types.filtered_colimit.rel_of_quot_rel,
  exact h,
end }

@[simp, to_additive]
lemma colimit_inv_mk_eq (x : Σ j, F.obj j) : (G.mk x) ⁻¹ = G.mk ⟨x.1, x.2 ⁻¹⟩ := rfl

@[to_additive]
instance colimit_group : group G :=
{ mul_left_inv := λ x, begin
    apply quot.induction_on x, clear x, intro x,
    cases x with j x,
    erw [colimit_inv_mk_eq, colimit_mul_mk_eq (F ⋙ forget₂ Group Mon) ⟨j, _⟩ ⟨j, _⟩ j (𝟙 j) (𝟙 j),
      colimit_one_eq (F ⋙ forget₂ Group Mon) j],
    dsimp,
    simp only [category_theory.functor.map_id, id_apply, mul_left_inv],
  end,
  .. G.monoid,
  .. colimit_has_inv }

/-- The bundled group giving the filtered colimit of a diagram. -/
@[to_additive "The bundled additive group giving the filtered colimit of a diagram."]
def colimit : Group := Group.of G

/-- The cocone over the proposed colimit group. -/
@[to_additive "The cocone over the proposed colimit additive group."]
def colimit_cocone : cocone F :=
{ X := colimit,
  ι := { ..(Mon.filtered_colimits.colimit_cocone (F ⋙ forget₂ Group Mon)).ι } }

/-- The proposed colimit cocone is a colimit in `Group`. -/
@[to_additive "The proposed colimit cocone is a colimit in `AddGroup`."]
def colimit_cocone_is_colimit : is_colimit colimit_cocone :=
{ desc := λ t, Mon.filtered_colimits.colimit_desc (F ⋙ forget₂ Group Mon)
    ((forget₂ Group Mon).map_cocone t),
  fac' := λ t j, monoid_hom.coe_inj $
    (types.colimit_cocone_is_colimit (F ⋙ forget Group)).fac ((forget Group).map_cocone t) j,
  uniq' := λ t m h, monoid_hom.coe_inj $
    (types.colimit_cocone_is_colimit (F ⋙ forget Group)).uniq ((forget Group).map_cocone t) m
    ((λ j, funext $ λ x, monoid_hom.congr_fun (h j) x)) }

@[to_additive forget₂_AddMon_preserves_filtered_colimits]
instance forget₂_Mon_preserves_filtered_colimits :
  preserves_filtered_colimits (forget₂ Group Mon.{v}) :=
{ preserves_filtered_colimits := λ J _ _, by exactI
  { preserves_colimit := λ F, preserves_colimit_of_preserves_colimit_cocone
      (colimit_cocone_is_colimit F)
      (Mon.filtered_colimits.colimit_cocone_is_colimit (F ⋙ forget₂ Group Mon.{v})) } }

@[to_additive]
instance forget_preserves_filtered_colimits : preserves_filtered_colimits (forget Group) :=
limits.comp_preserves_filtered_colimits (forget₂ Group Mon) (forget Mon)

end

end Group.filtered_colimits

namespace CommGroup.filtered_colimits

section

-- We use parameters here, mainly so we can have the abbreviation `G` below, without
-- passing around `F` all the time.
parameters {J : Type v} [small_category J] [is_filtered J] (F : J ⥤ CommGroup.{v})

/--
The colimit of `F ⋙ forget₂ CommGroup Group` in the category `Group`.
In the following, we will show that this has the structure of a _commutative_ group.
-/
@[to_additive "The colimit of `F ⋙ forget₂ AddCommGroup AddGroup` in the category `AddGroup`.
In the following, we will show that this has the structure of a _commutative_ additive group."]
abbreviation G : Group := Group.filtered_colimits.colimit (F ⋙ forget₂ CommGroup Group.{v})

@[to_additive]
instance colimit_comm_group : comm_group G :=
{ ..G.group,
  ..CommMon.filtered_colimits.colimit_comm_monoid (F ⋙ forget₂ CommGroup CommMon.{v}) }

/-- The bundled commutative group giving the filtered colimit of a diagram. -/
@[to_additive "The bundled additive commutative group giving the filtered colimit of a diagram."]
def colimit : CommGroup := CommGroup.of G

/-- The cocone over the proposed colimit commutative group. -/
@[to_additive "The cocone over the proposed colimit additive commutative group."]
def colimit_cocone : cocone F :=
{ X := colimit,
  ι := { ..(Group.filtered_colimits.colimit_cocone (F ⋙ forget₂ CommGroup Group)).ι } }

/-- The proposed colimit cocone is a colimit in `CommGroup`. -/
@[to_additive "The proposed colimit cocone is a colimit in `AddCommGroup`."]
def colimit_cocone_is_colimit : is_colimit colimit_cocone :=
{ desc := λ t,
  (Group.filtered_colimits.colimit_cocone_is_colimit (F ⋙ forget₂ CommGroup Group.{v})).desc
    ((forget₂ CommGroup Group.{v}).map_cocone t),
  fac' := λ t j, monoid_hom.coe_inj $
    (types.colimit_cocone_is_colimit (F ⋙ forget CommGroup)).fac
    ((forget CommGroup).map_cocone t) j,
  uniq' := λ t m h, monoid_hom.coe_inj $
    (types.colimit_cocone_is_colimit (F ⋙ forget CommGroup)).uniq
    ((forget CommGroup).map_cocone t) m ((λ j, funext $ λ x, monoid_hom.congr_fun (h j) x)) }

@[to_additive forget₂_AddGroup_preserves_filtered_colimits]
instance forget₂_Group_preserves_filtered_colimits :
  preserves_filtered_colimits (forget₂ CommGroup Group.{v}) :=
{ preserves_filtered_colimits := λ J _ _, by exactI
  { preserves_colimit := λ F, preserves_colimit_of_preserves_colimit_cocone
      (colimit_cocone_is_colimit F)
      (Group.filtered_colimits.colimit_cocone_is_colimit (F ⋙ forget₂ CommGroup Group.{v})) } }

@[to_additive]
instance forget_preserves_filtered_colimits : preserves_filtered_colimits (forget CommGroup) :=
limits.comp_preserves_filtered_colimits (forget₂ CommGroup Group) (forget Group)

end

end CommGroup.filtered_colimits


import Lean4Axiomatic.Integer.Impl.Difference.Addition

namespace Lean4Axiomatic.Integer.Impl.Difference

/-! ## Negation of formal differences -/

variable {ℕ : Type} [Natural ℕ]

open Signed (Positive)

/--
Negation of differences.

**Definition intuition**: It's easiest to use the "directed gap" interpretation
of differences to see this. If `a——b` represents the process of traveling from
`a` to `b`, then its negation should represent the opposite process: traveling
from `b` to `a`.
-/
def neg : Difference ℕ → Difference ℕ
| a——b => b——a

instance negOp : Neg (Difference ℕ) := {
  neg := neg
}

/--
Negating two equivalent differences preserves their equivalence.

**Property intuition**: For negation to make sense as an operation (i.e., have
a consistent definition as a function) on integers, this property must be true.

**Proof intuition**: Nothing too insightful here, it's just expanding the
definitions of negation and equality and performing some algebra.
-/
theorem neg_subst {a₁ a₂ : Difference ℕ} : a₁ ≃ a₂ → -a₁ ≃ -a₂ := by
  revert a₁; intro (n——m); revert a₂; intro (k——j)
  intro (_ : n——m ≃ k——j)
  show -(n——m) ≃ -(k——j)
  have : n + j ≃ k + m := ‹n——m ≃ k——j›
  show m——n ≃ j——k
  show m + k ≃ j + n
  calc
    m + k ≃ _ := AA.comm
    k + m ≃ _ := Rel.symm ‹n + j ≃ k + m›
    n + j ≃ _ := AA.comm
    j + n ≃ _ := Rel.refl

def neg_substitutive
    : AA.Substitutive₁ (α := Difference ℕ) (-·) (· ≃ ·) (· ≃ ·)
    := {
  subst₁ := neg_subst
}

/--
The negation of a natural number difference is that difference's left additive
inverse.

**Property intuition**: This property is pretty much why the integers are a
useful concept in the first place.

**Proof intuition**: Negation swaps the elements of a difference, so adding a
difference to its negation will result in a difference with equal elements. All
differences with equal elements represent zero.
-/
theorem neg_invL {a : Difference ℕ} : (-a) + a ≃ 0 := by
  revert a; intro (n——m)
  show -(n——m) + n——m ≃ 0——0
  show m——n + n——m ≃ 0——0
  show (m + n)——(n + m) ≃ 0——0
  show (m + n) + 0 ≃ 0 + (n + m)
  apply Natural.add_swapped_zeros_eqv.mpr
  show m + n ≃ n + m
  exact AA.comm

def neg_inverseL : AA.InverseOn Hand.L (α := Difference ℕ) (-·) (· + ·) := {
  inverse := neg_invL
}

def neg_inverse : AA.Inverse (α := Difference ℕ) (-·) (· + ·) := {
  inverseL := neg_inverseL
  inverseR := AA.inverseR_from_inverseL neg_inverseL
}

instance negation : Negation (ℕ := ℕ) (Difference ℕ) := {
  negOp := negOp
  neg_substitutive := neg_substitutive
  neg_inverse := neg_inverse
}

end Lean4Axiomatic.Integer.Impl.Difference


/-
Copyright (c) 2022 Julian Kuelshammer. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author : Julian Kuelshammer
-/

import data.int.basic
import tactic.linarith
import tactic.linear_combination

/-
# Quotients in Lean

Upon request, let's try to see how to construct number systems like the integers or the
rational numbers in Lean. Note that this is again some mathematical way to do this, not the
actual way, e.g. integers are defined as the disjoint union of ℕ with itself, where the first
copy is interpreted as the usual natural numbers while the second copy is interpreted as the
numbers `1-n` where `n : ℕ`. Similarly, ℚ is contructed as pairs of coprime integers (p,q).
This makes them computationally a bit better behaved than our quotient way.

## Equivalence relations in Lean

Lean knows what an equivalence relation is. It is a reflexive, symmetric and transitive relation.
A relation on a set `X` is a function `X → X → Prop`, i.e. a function that takes two elements
of a set `X` and outputs a truth value depending whether they are related or not.

```
def reflexive := ∀ x, x ∼ x

def symmetric := ∀ ⦃x y⦄, x ∼ y → y ∼ x

def transitive := ∀ ⦃x y z⦄, x ∼ y → y ∼ m z → x ∼ z

def equivalence := reflexive r ∧ symmetric r ∧ transitive r
```

-/

def R (r s : ℕ × ℕ) : Prop :=
r.1+s.2=s.1+r.2

lemma R_def (r s : ℕ × ℕ) :
R r s ↔ r.1 + s.2 = s.1 + r.2 :=
begin
  sorry
end

lemma R_refl : reflexive R :=
begin
  sorry
end

lemma R_symm : symmetric R :=
begin
  sorry
end

lemma R_trans : transitive R :=
begin
  /- The lemma add_right_inj could be helpful at some point. -/
  sorry
end

lemma R_equiv : equivalence R :=
begin
  sorry
end

/- A setoid on a Type is a relation together with the fact that
  this relation is an equivalence relation. -/
instance s : setoid (ℕ × ℕ) :=
_

structure int_plane_non_zero :=
(fst : ℤ) (snd : ℤ) (non_zero : snd ≠ 0)

def S (r s : int_plane_non_zero) : Prop :=
r.1 * s.2 = s.1 * r.2

lemma S_def (r s : int_plane_non_zero) :
S r s ↔ r.1 * s.2 = s.1 * r.2 :=
begin
  sorry
end

lemma S_refl : reflexive S :=
begin
  sorry
end

lemma S_symm : symmetric S :=
begin
  sorry
end

lemma S_trans : transitive S :=
begin
  sorry
end

lemma S_equiv : equivalence S :=
begin
  sorry
end

instance t : setoid (int_plane_non_zero) :=
_


/-
Copyright (c) 2021 Yury Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury Kudryashov
-/
import measure_theory.group.fundamental_domain
import measure_theory.integral.interval_integral

/-!
# Integrals of periodic functions

In this file we prove that `∫ x in b..b + a, f x = ∫ x in c..c + a, f x` for any (not necessarily
measurable) function periodic function with period `a`.
-/

open set function measure_theory measure_theory.measure topological_space
open_locale measure_theory

lemma is_add_fundamental_domain_Ioc {a : ℝ} (ha : 0 < a) (b : ℝ) (μ : measure ℝ . volume_tac) :
  is_add_fundamental_domain (add_subgroup.zmultiples a) (Ioc b (b + a)) μ :=
begin
  refine is_add_fundamental_domain.mk' measurable_set_Ioc (λ x, _),
  have : bijective (cod_restrict (λ n : ℤ, n • a) (add_subgroup.zmultiples a) _),
    from (equiv.of_injective (λ n : ℤ, n • a) (zsmul_strict_mono_left ha).injective).bijective,
  refine this.exists_unique_iff.2 _,
  simpa only [add_comm x] using exists_unique_add_zsmul_mem_Ioc ha x b
end

variables {E : Type*} [normed_group E] [normed_space ℝ E] [measurable_space E] [borel_space E]
  [complete_space E] [second_countable_topology E]

namespace function

namespace periodic

/-- An auxiliary lemma for a more general `function.periodic.interval_integral_add_eq`. -/
lemma interval_integral_add_eq_of_pos {f : ℝ → E} {a : ℝ} (hf : periodic f a)
  (ha : 0 < a) (b c : ℝ) : ∫ x in b..b + a, f x = ∫ x in c..c + a, f x :=
begin
  haveI : encodable (add_subgroup.zmultiples a) := (countable_range _).to_encodable,
  simp only [interval_integral.integral_of_le, ha.le, le_add_iff_nonneg_right],
  haveI : vadd_invariant_measure (add_subgroup.zmultiples a) ℝ volume :=
    ⟨λ c s hs, real.volume_preimage_add_left _ _⟩,
  exact (is_add_fundamental_domain_Ioc ha b).set_integral_eq
    (is_add_fundamental_domain_Ioc ha c) hf.map_vadd_zmultiples
end

/-- If `f` is a periodic function with period `a`, then its integral over `[b, b + a]` does not
depend on `b`. -/
lemma interval_integral_add_eq {f : ℝ → E} {a : ℝ} (hf : periodic f a)
  (b c : ℝ) : ∫ x in b..b + a, f x = ∫ x in c..c + a, f x :=
begin
  rcases lt_trichotomy 0 a with (ha|rfl|ha),
  { exact hf.interval_integral_add_eq_of_pos ha b c },
  { simp },
  { rw [← neg_inj, ← interval_integral.integral_symm, ← interval_integral.integral_symm],
    simpa only [← sub_eq_add_neg, add_sub_cancel]
      using (hf.neg.interval_integral_add_eq_of_pos (neg_pos.2 ha) (b + a) (c + a)) }
end

end periodic

end function


/-
Copyright (c) 2022 Vincent Beffara. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Vincent Beffara
-/
import analysis.complex.removable_singularity
import analysis.calculus.series

/-!
# Locally uniform limits of holomorphic functions

This file gathers some results about locally uniform limits of holomorphic functions on an open
subset of the complex plane.

## Main results

* `tendsto_locally_uniformly_on.differentiable_on`: A locally uniform limit of holomorphic functions
  is holomorphic.
* `tendsto_locally_uniformly_on.deriv`: Locally uniform convergence implies locally uniform
  convergence of the derivatives to the derivative of the limit.
-/

open set metric measure_theory filter complex interval_integral
open_locale real topology

variables {E ι : Type*} [normed_add_comm_group E] [normed_space ℂ E] [complete_space E]
  {U K : set ℂ} {z : ℂ} {M r δ : ℝ} {φ : filter ι} {F : ι → ℂ → E} {f g : ℂ → E}

namespace complex

section cderiv

/-- A circle integral which coincides with `deriv f z` whenever one can apply the Cauchy formula for
the derivative. It is useful in the proof that locally uniform limits of holomorphic functions are
holomorphic, because it depends continuously on `f` for the uniform topology. -/
noncomputable def cderiv (r : ℝ) (f : ℂ → E) (z : ℂ) : E :=
(2 * π * I : ℂ)⁻¹ • ∮ w in C(z, r), ((w - z) ^ 2)⁻¹ • f w

lemma cderiv_eq_deriv (hU : is_open U) (hf : differentiable_on ℂ f U) (hr : 0 < r)
  (hzr : closed_ball z r ⊆ U) :
  cderiv r f z = deriv f z :=
two_pi_I_inv_smul_circle_integral_sub_sq_inv_smul_of_differentiable hU hzr hf (mem_ball_self hr)

lemma norm_cderiv_le (hr : 0 < r) (hf : ∀ w ∈ sphere z r, ‖f w‖ ≤ M) :
  ‖cderiv r f z‖ ≤ M / r :=
begin
  have hM : 0 ≤ M,
  { obtain ⟨w, hw⟩ : (sphere z r).nonempty := normed_space.sphere_nonempty.mpr hr.le,
    exact (norm_nonneg _).trans (hf w hw) },
  have h1 : ∀ w ∈ sphere z r, ‖((w - z) ^ 2)⁻¹ • f w‖ ≤ M / r ^ 2,
  { intros w hw,
    simp only [mem_sphere_iff_norm, norm_eq_abs] at hw,
    simp only [norm_smul, inv_mul_eq_div, hw, norm_eq_abs, map_inv₀, complex.abs_pow],
    exact div_le_div hM (hf w hw) (sq_pos_of_pos hr) le_rfl },
  have h2 := circle_integral.norm_integral_le_of_norm_le_const hr.le h1,
  simp only [cderiv, norm_smul],
  refine (mul_le_mul le_rfl h2 (norm_nonneg _) (norm_nonneg _)).trans (le_of_eq _),
  field_simp [_root_.abs_of_nonneg real.pi_pos.le, real.pi_pos.ne.symm, hr.ne.symm],
  ring
end

lemma cderiv_sub (hr : 0 < r) (hf : continuous_on f (sphere z r))
  (hg : continuous_on g (sphere z r)) :
  cderiv r (f - g) z = cderiv r f z - cderiv r g z :=
begin
  have h1 : continuous_on (λ (w : ℂ), ((w - z) ^ 2)⁻¹) (sphere z r),
  { refine ((continuous_id'.sub continuous_const).pow 2).continuous_on.inv₀ (λ w hw h, hr.ne _),
    rwa [mem_sphere_iff_norm, sq_eq_zero_iff.mp h, norm_zero] at hw },
  simp_rw [cderiv, ← smul_sub],
  congr' 1,
  simpa only [pi.sub_apply, smul_sub] using circle_integral.integral_sub
    ((h1.smul hf).circle_integrable hr.le) ((h1.smul hg).circle_integrable hr.le)
end

lemma norm_cderiv_lt (hr : 0 < r) (hfM : ∀ w ∈ sphere z r, ‖f w‖ < M)
  (hf : continuous_on f (sphere z r)) :
  ‖cderiv r f z‖ < M / r :=
begin
  obtain ⟨L, hL1, hL2⟩ : ∃ L < M, ∀ w ∈ sphere z r, ‖f w‖ ≤ L,
  { have e1 : (sphere z r).nonempty := normed_space.sphere_nonempty.mpr hr.le,
    have e2 : continuous_on (λ w, ‖f w‖) (sphere z r),
      from continuous_norm.comp_continuous_on hf,
    obtain ⟨x, hx, hx'⟩ := (is_compact_sphere z r).exists_forall_ge e1 e2,
    exact ⟨‖f x‖, hfM x hx, hx'⟩ },
  exact (norm_cderiv_le hr hL2).trans_lt ((div_lt_div_right hr).mpr hL1)
end

lemma norm_cderiv_sub_lt (hr : 0 < r) (hfg : ∀ w ∈ sphere z r, ‖f w - g w‖ < M)
  (hf : continuous_on f (sphere z r)) (hg : continuous_on g (sphere z r)) :
  ‖cderiv r f z - cderiv r g z‖ < M / r :=
cderiv_sub hr hf hg ▸ norm_cderiv_lt hr hfg (hf.sub hg)

lemma tendsto_uniformly_on.cderiv (hF : tendsto_uniformly_on F f φ (cthickening δ K)) (hδ : 0 < δ)
  (hFn : ∀ᶠ n in φ, continuous_on (F n) (cthickening δ K)) :
  tendsto_uniformly_on (cderiv δ ∘ F) (cderiv δ f) φ K :=
begin
  by_cases φ = ⊥,
  { simp only [h, tendsto_uniformly_on, eventually_bot, implies_true_iff]},
  haveI : φ.ne_bot := ne_bot_iff.2 h,
  have e1 : continuous_on f (cthickening δ K) := tendsto_uniformly_on.continuous_on hF hFn,
  rw [tendsto_uniformly_on_iff] at hF ⊢,
  rintro ε hε,
  filter_upwards [hF (ε * δ) (mul_pos hε hδ), hFn] with n h h' z hz,
  simp_rw [dist_eq_norm] at h ⊢,
  have e2 : ∀ w ∈ sphere z δ, ‖f w - F n w‖ < ε * δ,
    from λ w hw1, h w (closed_ball_subset_cthickening hz δ (sphere_subset_closed_ball hw1)),
  have e3 := sphere_subset_closed_ball.trans (closed_ball_subset_cthickening hz δ),
  have hf : continuous_on f (sphere z δ),
    from e1.mono (sphere_subset_closed_ball.trans (closed_ball_subset_cthickening hz δ)),
  simpa only [mul_div_cancel _ hδ.ne.symm] using norm_cderiv_sub_lt hδ e2 hf (h'.mono e3)
end

end cderiv

section weierstrass

lemma tendsto_uniformly_on_deriv_of_cthickening_subset (hf : tendsto_locally_uniformly_on F f φ U)
  (hF : ∀ᶠ n in φ, differentiable_on ℂ (F n) U) {δ : ℝ} (hδ: 0 < δ) (hK : is_compact K)
  (hU : is_open U) (hKU : cthickening δ K ⊆ U) :
  tendsto_uniformly_on (deriv ∘ F) (cderiv δ f) φ K :=
begin
  have h1 : ∀ᶠ n in φ, continuous_on (F n) (cthickening δ K),
    by filter_upwards [hF] with n h using h.continuous_on.mono hKU,
  have h2 : is_compact (cthickening δ K),
    from is_compact_of_is_closed_bounded is_closed_cthickening hK.bounded.cthickening,
  have h3 : tendsto_uniformly_on F f φ (cthickening δ K),
    from (tendsto_locally_uniformly_on_iff_forall_is_compact hU).mp hf (cthickening δ K) hKU h2,
  apply (h3.cderiv hδ h1).congr,
  filter_upwards [hF] with n h z hz,
  exact cderiv_eq_deriv hU h hδ ((closed_ball_subset_cthickening hz δ).trans hKU)
end

lemma exists_cthickening_tendsto_uniformly_on (hf : tendsto_locally_uniformly_on F f φ U)
  (hF : ∀ᶠ n in φ, differentiable_on ℂ (F n) U) (hK : is_compact K) (hU : is_open U) (hKU : K ⊆ U) :
  ∃ δ > 0, cthickening δ K ⊆ U ∧ tendsto_uniformly_on (deriv ∘ F) (cderiv δ f) φ K :=
begin
  obtain ⟨δ, hδ, hKδ⟩ := hK.exists_cthickening_subset_open hU hKU,
  exact ⟨δ, hδ, hKδ, tendsto_uniformly_on_deriv_of_cthickening_subset hf hF hδ hK hU hKδ⟩
end

/-- A locally uniform limit of holomorphic functions on an open domain of the complex plane is
holomorphic (the derivatives converge locally uniformly to that of the limit, which is proved
as `tendsto_locally_uniformly_on.deriv`). -/
theorem _root_.tendsto_locally_uniformly_on.differentiable_on [φ.ne_bot]
  (hf : tendsto_locally_uniformly_on F f φ U) (hF : ∀ᶠ n in φ, differentiable_on ℂ (F n) U)
  (hU : is_open U) :
  differentiable_on ℂ f U :=
begin
  rintro x hx,
  obtain ⟨K, ⟨hKx, hK⟩, hKU⟩ := (compact_basis_nhds x).mem_iff.mp (hU.mem_nhds hx),
  obtain ⟨δ, hδ, -, h1⟩ := exists_cthickening_tendsto_uniformly_on hf hF hK hU hKU,
  have h2 : interior K ⊆ U := interior_subset.trans hKU,
  have h3 : ∀ᶠ n in φ, differentiable_on ℂ (F n) (interior K),
    filter_upwards [hF] with n h using h.mono h2,
  have h4 : tendsto_locally_uniformly_on F f φ (interior K) := hf.mono h2,
  have h5 : tendsto_locally_uniformly_on (deriv ∘ F) (cderiv δ f) φ (interior K),
    from h1.tendsto_locally_uniformly_on.mono interior_subset,
  have h6 : ∀ x ∈ interior K, has_deriv_at f (cderiv δ f x) x,
    from λ x h, has_deriv_at_of_tendsto_locally_uniformly_on'
      is_open_interior h5 h3 (λ _, h4.tendsto_at) h,
  have h7 : differentiable_on ℂ f (interior K),
    from λ x hx, (h6 x hx).differentiable_at.differentiable_within_at,
  exact (h7.differentiable_at (interior_mem_nhds.mpr hKx)).differentiable_within_at
end

lemma _root_.tendsto_locally_uniformly_on.deriv (hf : tendsto_locally_uniformly_on F f φ U)
  (hF : ∀ᶠ n in φ, differentiable_on ℂ (F n) U) (hU : is_open U) :
  tendsto_locally_uniformly_on (deriv ∘ F) (deriv f) φ U :=
begin
  rw [tendsto_locally_uniformly_on_iff_forall_is_compact hU],
  by_cases φ = ⊥,
  { simp only [h, tendsto_uniformly_on, eventually_bot, implies_true_iff] },
  haveI : φ.ne_bot := ne_bot_iff.2 h,
  rintro K hKU hK,
  obtain ⟨δ, hδ, hK4, h⟩ := exists_cthickening_tendsto_uniformly_on hf hF hK hU hKU,
  refine h.congr_right (λ z hz, cderiv_eq_deriv hU (hf.differentiable_on hF hU) hδ _),
  exact (closed_ball_subset_cthickening hz δ).trans hK4,
end

end weierstrass

section tsums

/-- If the terms in the sum `∑' (i : ι), F i` are uniformly bounded on `U` by a
summable function, and each term in the sum is differentiable on `U`, then so is the sum. -/
lemma differentiable_on_tsum_of_summable_norm {u : ι → ℝ}
  (hu : summable u) (hf : ∀ (i : ι), differentiable_on ℂ (F i) U) (hU : is_open U)
  (hF_le : ∀ (i : ι) (w : ℂ), w ∈ U → ‖F i w‖ ≤ u i) :
  differentiable_on ℂ (λ w : ℂ, ∑' (i : ι), F i w) U :=
begin
  classical,
  have hc := (tendsto_uniformly_on_tsum hu hF_le).tendsto_locally_uniformly_on,
  refine hc.differentiable_on (eventually_of_forall $ λ s, _) hU,
  exact differentiable_on.sum (λ i hi, hf i),
end

/-- If the terms in the sum `∑' (i : ι), F i` are uniformly bounded on `U` by a
summable function, then the sum of `deriv F i` at a point in `U` is the derivative of the
sum. -/
lemma has_sum_deriv_of_summable_norm {u : ι → ℝ}
  (hu : summable u) (hf : ∀ (i : ι), differentiable_on ℂ (F i) U) (hU : is_open U)
  (hF_le : ∀ (i : ι) (w : ℂ), w ∈ U → ‖F i w‖ ≤ u i) (hz : z ∈ U) :
  has_sum (λ (i : ι), deriv (F i) z) (deriv (λ w : ℂ, ∑' (i : ι), F i w) z) :=
begin
  rw has_sum,
  have hc := (tendsto_uniformly_on_tsum hu hF_le).tendsto_locally_uniformly_on,
  convert (hc.deriv (eventually_of_forall $ λ s, differentiable_on.sum
    (λ i hi, hf i)) hU).tendsto_at hz using 1,
  ext1 s,
  exact (deriv_sum (λ i hi, (hf i).differentiable_at (hU.mem_nhds hz))).symm,
end

end tsums

end complex