/-
Copyright (c) 2020 Sébastien Gouëzel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sébastien Gouëzel, Yury Kudryashov
-/
import analysis.calculus.formal_multilinear_series
import analysis.specific_limits.normed
import logic.equiv.fin

/-!
# Analytic functions

A function is analytic in one dimension around `0` if it can be written as a converging power series
`Σ pₙ zⁿ`. This definition can be extended to any dimension (even in infinite dimension) by
requiring that `pₙ` is a continuous `n`-multilinear map. In general, `pₙ` is not unique (in two
dimensions, taking `p₂ (x, y) (x', y') = x y'` or `y x'` gives the same map when applied to a
vector `(x, y) (x, y)`). A way to guarantee uniqueness is to take a symmetric `pₙ`, but this is not
always possible in nonzero characteristic (in characteristic 2, the previous example has no
symmetric representative). Therefore, we do not insist on symmetry or uniqueness in the definition,
and we only require the existence of a converging series.

The general framework is important to say that the exponential map on bounded operators on a Banach
space is analytic, as well as the inverse on invertible operators.

## Main definitions

Let `p` be a formal multilinear series from `E` to `F`, i.e., `p n` is a multilinear map on `E^n`
for `n : ℕ`.

* `p.radius`: the largest `r : ℝ≥0∞` such that `∥p n∥ * r^n` grows subexponentially.
* `p.le_radius_of_bound`, `p.le_radius_of_bound_nnreal`, `p.le_radius_of_is_O`: if `∥p n∥ * r ^ n`
  is bounded above, then `r ≤ p.radius`;
* `p.is_o_of_lt_radius`, `p.norm_mul_pow_le_mul_pow_of_lt_radius`, `p.is_o_one_of_lt_radius`,
  `p.norm_mul_pow_le_of_lt_radius`, `p.nnnorm_mul_pow_le_of_lt_radius`: if `r < p.radius`, then
  `∥p n∥ * r ^ n` tends to zero exponentially;
* `p.lt_radius_of_is_O`: if `r ≠ 0` and `∥p n∥ * r ^ n = O(a ^ n)` for some `-1 < a < 1`, then
  `r < p.radius`;
* `p.partial_sum n x`: the sum `∑_{i = 0}^{n-1} pᵢ xⁱ`.
* `p.sum x`: the sum `∑'_{i = 0}^{∞} pᵢ xⁱ`.

Additionally, let `f` be a function from `E` to `F`.

* `has_fpower_series_on_ball f p x r`: on the ball of center `x` with radius `r`,
  `f (x + y) = ∑'_n pₙ yⁿ`.
* `has_fpower_series_at f p x`: on some ball of center `x` with positive radius, holds
  `has_fpower_series_on_ball f p x r`.
* `analytic_at 𝕜 f x`: there exists a power series `p` such that holds
  `has_fpower_series_at f p x`.
* `analytic_on 𝕜 f s`: the function `f` is analytic at every point of `s`.

We develop the basic properties of these notions, notably:
* If a function admits a power series, it is continuous (see
  `has_fpower_series_on_ball.continuous_on` and `has_fpower_series_at.continuous_at` and
  `analytic_at.continuous_at`).
* In a complete space, the sum of a formal power series with positive radius is well defined on the
  disk of convergence, see `formal_multilinear_series.has_fpower_series_on_ball`.
* If a function admits a power series in a ball, then it is analytic at any point `y` of this ball,
  and the power series there can be expressed in terms of the initial power series `p` as
  `p.change_origin y`. See `has_fpower_series_on_ball.change_origin`. It follows in particular that
  the set of points at which a given function is analytic is open, see `is_open_analytic_at`.

## Implementation details

We only introduce the radius of convergence of a power series, as `p.radius`.
For a power series in finitely many dimensions, there is a finer (directional, coordinate-dependent)
notion, describing the polydisk of convergence. This notion is more specific, and not necessary to
build the general theory. We do not define it here.
-/

noncomputable theory

variables {𝕜 : Type*} [nondiscrete_normed_field 𝕜]
{E : Type*} [normed_group E] [normed_space 𝕜 E]
{F : Type*} [normed_group F] [normed_space 𝕜 F]
{G : Type*} [normed_group G] [normed_space 𝕜 G]

open_locale topological_space classical big_operators nnreal filter ennreal
open set filter asymptotics

/-! ### The radius of a formal multilinear series -/

namespace formal_multilinear_series

variables (p : formal_multilinear_series 𝕜 E F) {r : ℝ≥0}

/-- The radius of a formal multilinear series is the largest `r` such that the sum `Σ ∥pₙ∥ ∥y∥ⁿ`
converges for all `∥y∥ < r`. This implies that `Σ pₙ yⁿ` converges for all `∥y∥ < r`, but these
definitions are *not* equivalent in general. -/
def radius (p : formal_multilinear_series 𝕜 E F) : ℝ≥0∞ :=
⨆ (r : ℝ≥0) (C : ℝ) (hr : ∀ n, ∥p n∥ * r ^ n ≤ C), (r : ℝ≥0∞)

/-- If `∥pₙ∥ rⁿ` is bounded in `n`, then the radius of `p` is at least `r`. -/
lemma le_radius_of_bound (C : ℝ) {r : ℝ≥0} (h : ∀ (n : ℕ), ∥p n∥ * r^n ≤ C) :
  (r : ℝ≥0∞) ≤ p.radius :=
le_supr_of_le r $ le_supr_of_le C $ (le_supr (λ _, (r : ℝ≥0∞)) h)

/-- If `∥pₙ∥ rⁿ` is bounded in `n`, then the radius of `p` is at least `r`. -/
lemma le_radius_of_bound_nnreal (C : ℝ≥0) {r : ℝ≥0} (h : ∀ (n : ℕ), ∥p n∥₊ * r^n ≤ C) :
  (r : ℝ≥0∞) ≤ p.radius :=
p.le_radius_of_bound C $ λ n, by exact_mod_cast (h n)

/-- If `∥pₙ∥ rⁿ = O(1)`, as `n → ∞`, then the radius of `p` is at least `r`. -/
lemma le_radius_of_is_O (h : is_O (λ n, ∥p n∥ * r^n) (λ n, (1 : ℝ)) at_top) : ↑r ≤ p.radius :=
exists.elim (is_O_one_nat_at_top_iff.1 h) $ λ C hC, p.le_radius_of_bound C $
  λ n, (le_abs_self _).trans (hC n)

lemma le_radius_of_eventually_le (C) (h : ∀ᶠ n in at_top, ∥p n∥ * r ^ n ≤ C) : ↑r ≤ p.radius :=
p.le_radius_of_is_O $ is_O.of_bound C $ h.mono $ λ n hn, by simpa

lemma le_radius_of_summable_nnnorm (h : summable (λ n, ∥p n∥₊ * r ^ n)) : ↑r ≤ p.radius :=
p.le_radius_of_bound_nnreal (∑' n, ∥p n∥₊ * r ^ n) $ λ n, le_tsum' h _

lemma le_radius_of_summable (h : summable (λ n, ∥p n∥ * r ^ n)) : ↑r ≤ p.radius :=
p.le_radius_of_summable_nnnorm $ by { simp only [← coe_nnnorm] at h, exact_mod_cast h }

lemma radius_eq_top_of_forall_nnreal_is_O
  (h : ∀ r : ℝ≥0, is_O (λ n, ∥p n∥ * r^n) (λ n, (1 : ℝ)) at_top) : p.radius = ∞ :=
ennreal.eq_top_of_forall_nnreal_le $ λ r, p.le_radius_of_is_O (h r)

lemma radius_eq_top_of_eventually_eq_zero (h : ∀ᶠ n in at_top, p n = 0) : p.radius = ∞ :=
p.radius_eq_top_of_forall_nnreal_is_O $
  λ r, (is_O_zero _ _).congr' (h.mono $ λ n hn, by simp [hn]) eventually_eq.rfl

lemma radius_eq_top_of_forall_image_add_eq_zero (n : ℕ) (hn : ∀ m, p (m + n) = 0) : p.radius = ∞ :=
p.radius_eq_top_of_eventually_eq_zero $ mem_at_top_sets.2
  ⟨n, λ k hk, tsub_add_cancel_of_le hk ▸ hn _⟩

/-- For `r` strictly smaller than the radius of `p`, then `∥pₙ∥ rⁿ` tends to zero exponentially:
for some `0 < a < 1`, `∥p n∥ rⁿ = o(aⁿ)`. -/
lemma is_o_of_lt_radius (h : ↑r < p.radius) :
  ∃ a ∈ Ioo (0 : ℝ) 1, is_o (λ n, ∥p n∥ * r ^ n) (pow a) at_top :=
begin
  rw (tfae_exists_lt_is_o_pow (λ n, ∥p n∥ * r ^ n) 1).out 1 4,
  simp only [radius, lt_supr_iff] at h,
  rcases h with ⟨t, C, hC, rt⟩,
  rw [ennreal.coe_lt_coe, ← nnreal.coe_lt_coe] at rt,
  have : 0 < (t : ℝ), from r.coe_nonneg.trans_lt rt,
  rw [← div_lt_one this] at rt,
  refine ⟨_, rt, C, or.inr zero_lt_one, λ n, _⟩,
  calc |∥p n∥ * r ^ n| = (∥p n∥ * t ^ n) * (r / t) ^ n :
    by field_simp [mul_right_comm, abs_mul, this.ne']
  ... ≤ C * (r / t) ^ n : mul_le_mul_of_nonneg_right (hC n) (pow_nonneg (div_nonneg r.2 t.2) _)
end

/-- For `r` strictly smaller than the radius of `p`, then `∥pₙ∥ rⁿ = o(1)`. -/
lemma is_o_one_of_lt_radius (h : ↑r < p.radius) :
  is_o (λ n, ∥p n∥ * r ^ n) (λ _, 1 : ℕ → ℝ) at_top :=
let ⟨a, ha, hp⟩ := p.is_o_of_lt_radius h in
hp.trans $ (is_o_pow_pow_of_lt_left ha.1.le ha.2).congr (λ n, rfl) one_pow

/-- For `r` strictly smaller than the radius of `p`, then `∥pₙ∥ rⁿ` tends to zero exponentially:
for some `0 < a < 1` and `C > 0`,  `∥p n∥ * r ^ n ≤ C * a ^ n`. -/
lemma norm_mul_pow_le_mul_pow_of_lt_radius (h : ↑r < p.radius) :
  ∃ (a ∈ Ioo (0 : ℝ) 1) (C > 0), ∀ n, ∥p n∥ * r^n ≤ C * a^n :=
begin
  rcases ((tfae_exists_lt_is_o_pow (λ n, ∥p n∥ * r ^ n) 1).out 1 5).mp (p.is_o_of_lt_radius h)
    with ⟨a, ha, C, hC, H⟩,
  exact ⟨a, ha, C, hC, λ n, (le_abs_self _).trans (H n)⟩
end

/-- If `r ≠ 0` and `∥pₙ∥ rⁿ = O(aⁿ)` for some `-1 < a < 1`, then `r < p.radius`. -/
lemma lt_radius_of_is_O (h₀ : r ≠ 0) {a : ℝ} (ha : a ∈ Ioo (-1 : ℝ) 1)
  (hp : is_O (λ n, ∥p n∥ * r ^ n) (pow a) at_top) :
  ↑r < p.radius :=
begin
  rcases ((tfae_exists_lt_is_o_pow (λ n, ∥p n∥ * r ^ n) 1).out 2 5).mp ⟨a, ha, hp⟩
    with ⟨a, ha, C, hC, hp⟩,
  rw [← pos_iff_ne_zero, ← nnreal.coe_pos] at h₀,
  lift a to ℝ≥0 using ha.1.le,
  have : (r : ℝ) < r / a :=
    by simpa only [div_one] using (div_lt_div_left h₀ zero_lt_one ha.1).2 ha.2,
  norm_cast at this,
  rw [← ennreal.coe_lt_coe] at this,
  refine this.trans_le (p.le_radius_of_bound C $ λ n, _),
  rw [nnreal.coe_div, div_pow, ← mul_div_assoc, div_le_iff (pow_pos ha.1 n)],
  exact (le_abs_self _).trans (hp n)
end

/-- For `r` strictly smaller than the radius of `p`, then `∥pₙ∥ rⁿ` is bounded. -/
lemma norm_mul_pow_le_of_lt_radius (p : formal_multilinear_series 𝕜 E F) {r : ℝ≥0}
  (h : (r : ℝ≥0∞) < p.radius) : ∃ C > 0, ∀ n, ∥p n∥ * r^n ≤ C :=
let ⟨a, ha, C, hC, h⟩ := p.norm_mul_pow_le_mul_pow_of_lt_radius h
in ⟨C, hC, λ n, (h n).trans $ mul_le_of_le_one_right hC.lt.le (pow_le_one _ ha.1.le ha.2.le)⟩

/-- For `r` strictly smaller than the radius of `p`, then `∥pₙ∥ rⁿ` is bounded. -/
lemma norm_le_div_pow_of_pos_of_lt_radius (p : formal_multilinear_series 𝕜 E F) {r : ℝ≥0}
  (h0 : 0 < r) (h : (r : ℝ≥0∞) < p.radius) : ∃ C > 0, ∀ n, ∥p n∥ ≤ C / r ^ n :=
let ⟨C, hC, hp⟩ := p.norm_mul_pow_le_of_lt_radius h in
⟨C, hC, λ n, iff.mpr (le_div_iff (pow_pos h0 _)) (hp n)⟩

/-- For `r` strictly smaller than the radius of `p`, then `∥pₙ∥ rⁿ` is bounded. -/
lemma nnnorm_mul_pow_le_of_lt_radius (p : formal_multilinear_series 𝕜 E F) {r : ℝ≥0}
  (h : (r : ℝ≥0∞) < p.radius) : ∃ C > 0, ∀ n, ∥p n∥₊ * r^n ≤ C :=
let ⟨C, hC, hp⟩ := p.norm_mul_pow_le_of_lt_radius h
in ⟨⟨C, hC.lt.le⟩, hC, by exact_mod_cast hp⟩

lemma le_radius_of_tendsto (p : formal_multilinear_series 𝕜 E F) {l : ℝ}
  (h : tendsto (λ n, ∥p n∥ * r^n) at_top (𝓝 l)) : ↑r ≤ p.radius :=
p.le_radius_of_is_O (is_O_one_of_tendsto _ h)

lemma le_radius_of_summable_norm (p : formal_multilinear_series 𝕜 E F)
  (hs : summable (λ n, ∥p n∥ * r^n)) : ↑r ≤ p.radius :=
p.le_radius_of_tendsto hs.tendsto_at_top_zero

lemma not_summable_norm_of_radius_lt_nnnorm (p : formal_multilinear_series 𝕜 E F) {x : E}
  (h : p.radius < ∥x∥₊) : ¬ summable (λ n, ∥p n∥ * ∥x∥^n) :=
λ hs, not_le_of_lt h (p.le_radius_of_summable_norm hs)

lemma summable_norm_mul_pow (p : formal_multilinear_series 𝕜 E F)
  {r : ℝ≥0} (h : ↑r < p.radius) :
  summable (λ n : ℕ, ∥p n∥ * r ^ n) :=
begin
  obtain ⟨a, ha : a ∈ Ioo (0 : ℝ) 1, C, hC : 0 < C, hp⟩ := p.norm_mul_pow_le_mul_pow_of_lt_radius h,
  exact summable_of_nonneg_of_le (λ n, mul_nonneg (norm_nonneg _) (pow_nonneg r.coe_nonneg _)) hp
    ((summable_geometric_of_lt_1 ha.1.le ha.2).mul_left _),
end

lemma summable_norm_apply (p : formal_multilinear_series 𝕜 E F)
  {x : E} (hx : x ∈ emetric.ball (0 : E) p.radius) :
  summable (λ n : ℕ, ∥p n (λ _, x)∥) :=
begin
  rw mem_emetric_ball_zero_iff at hx,
  refine summable_of_nonneg_of_le (λ _, norm_nonneg _) (λ n, ((p n).le_op_norm _).trans_eq _)
    (p.summable_norm_mul_pow hx),
  simp
end

lemma summable_nnnorm_mul_pow (p : formal_multilinear_series 𝕜 E F)
  {r : ℝ≥0} (h : ↑r < p.radius) :
  summable (λ n : ℕ, ∥p n∥₊ * r ^ n) :=
by { rw ← nnreal.summable_coe, push_cast, exact p.summable_norm_mul_pow h }

protected lemma summable [complete_space F]
  (p : formal_multilinear_series 𝕜 E F) {x : E} (hx : x ∈ emetric.ball (0 : E) p.radius) :
  summable (λ n : ℕ, p n (λ _, x)) :=
summable_of_summable_norm (p.summable_norm_apply hx)

lemma radius_eq_top_of_summable_norm (p : formal_multilinear_series 𝕜 E F)
  (hs : ∀ r : ℝ≥0, summable (λ n, ∥p n∥ * r^n)) : p.radius = ∞ :=
ennreal.eq_top_of_forall_nnreal_le (λ r, p.le_radius_of_summable_norm (hs r))

lemma radius_eq_top_iff_summable_norm (p : formal_multilinear_series 𝕜 E F) :
  p.radius = ∞ ↔ ∀ r : ℝ≥0, summable (λ n, ∥p n∥ * r^n) :=
begin
  split,
  { intros h r,
    obtain ⟨a, ha : a ∈ Ioo (0 : ℝ) 1, C, hC : 0 < C, hp⟩ :=
      p.norm_mul_pow_le_mul_pow_of_lt_radius
      (show (r:ℝ≥0∞) < p.radius, from h.symm ▸ ennreal.coe_lt_top),
    refine (summable_of_norm_bounded (λ n, (C : ℝ) * a ^ n)
      ((summable_geometric_of_lt_1 ha.1.le ha.2).mul_left _) (λ n, _)),
    specialize hp n,
    rwa real.norm_of_nonneg (mul_nonneg (norm_nonneg _) (pow_nonneg r.coe_nonneg n)) },
  { exact p.radius_eq_top_of_summable_norm }
end

/-- If the radius of `p` is positive, then `∥pₙ∥` grows at most geometrically. -/
lemma le_mul_pow_of_radius_pos (p : formal_multilinear_series 𝕜 E F) (h : 0 < p.radius) :
  ∃ C r (hC : 0 < C) (hr : 0 < r), ∀ n, ∥p n∥ ≤ C * r ^ n :=
begin
  rcases ennreal.lt_iff_exists_nnreal_btwn.1 h with ⟨r, r0, rlt⟩,
  have rpos : 0 < (r : ℝ), by simp [ennreal.coe_pos.1 r0],
  rcases norm_le_div_pow_of_pos_of_lt_radius p rpos rlt with ⟨C, Cpos, hCp⟩,
  refine ⟨C, r ⁻¹, Cpos, by simp [rpos], λ n, _⟩,
  convert hCp n,
  exact inv_pow₀ _ _,
end

/-- The radius of the sum of two formal series is at least the minimum of their two radii. -/
lemma min_radius_le_radius_add (p q : formal_multilinear_series 𝕜 E F) :
  min p.radius q.radius ≤ (p + q).radius :=
begin
  refine ennreal.le_of_forall_nnreal_lt (λ r hr, _),
  rw lt_min_iff at hr,
  have := ((p.is_o_one_of_lt_radius hr.1).add (q.is_o_one_of_lt_radius hr.2)).is_O,
  refine (p + q).le_radius_of_is_O ((is_O_of_le _ $ λ n, _).trans this),
  rw [← add_mul, norm_mul, norm_mul, norm_norm],
  exact mul_le_mul_of_nonneg_right ((norm_add_le _ _).trans (le_abs_self _)) (norm_nonneg _)
end

@[simp] lemma radius_neg (p : formal_multilinear_series 𝕜 E F) : (-p).radius = p.radius :=
by simp [radius]

/-- Given a formal multilinear series `p` and a vector `x`, then `p.sum x` is the sum `Σ pₙ xⁿ`. A
priori, it only behaves well when `∥x∥ < p.radius`. -/
protected def sum (p : formal_multilinear_series 𝕜 E F) (x : E) : F := ∑' n : ℕ , p n (λ i, x)

protected lemma has_sum [complete_space F]
  (p : formal_multilinear_series 𝕜 E F) {x : E} (hx : x ∈ emetric.ball (0 : E) p.radius) :
  has_sum (λ n : ℕ, p n (λ _, x)) (p.sum x) :=
(p.summable hx).has_sum

/-- Given a formal multilinear series `p` and a vector `x`, then `p.partial_sum n x` is the sum
`Σ pₖ xᵏ` for `k ∈ {0,..., n-1}`. -/
def partial_sum (p : formal_multilinear_series 𝕜 E F) (n : ℕ) (x : E) : F :=
∑ k in finset.range n, p k (λ(i : fin k), x)

/-- The partial sums of a formal multilinear series are continuous. -/
lemma partial_sum_continuous (p : formal_multilinear_series 𝕜 E F) (n : ℕ) :
  continuous (p.partial_sum n) :=
by continuity

lemma radius_le_radius_continuous_linear_map_comp
  (p : formal_multilinear_series 𝕜 E F) (f : F →L[𝕜] G) :
  p.radius ≤ (f.comp_formal_multilinear_series p).radius :=
begin
  refine ennreal.le_of_forall_nnreal_lt (λ r hr, _),
  apply le_radius_of_is_O,
  apply (is_O.trans_is_o _ (p.is_o_one_of_lt_radius hr)).is_O,
  refine is_O.mul (@is_O_with.is_O _ _ _ _ _ (∥f∥) _ _ _ _) (is_O_refl _ _),
  apply is_O_with.of_bound (eventually_of_forall (λ n, _)),
  simpa only [norm_norm] using f.norm_comp_continuous_multilinear_map_le (p n)
end

end formal_multilinear_series

/-! ### Expanding a function as a power series -/
section

variables {f g : E → F} {p pf pg : formal_multilinear_series 𝕜 E F} {x : E} {r r' : ℝ≥0∞}

/-- Given a function `f : E → F` and a formal multilinear series `p`, we say that `f` has `p` as
a power series on the ball of radius `r > 0` around `x` if `f (x + y) = ∑' pₙ yⁿ` for all `∥y∥ < r`.
-/
structure has_fpower_series_on_ball
  (f : E → F) (p : formal_multilinear_series 𝕜 E F) (x : E) (r : ℝ≥0∞) : Prop :=
(r_le    : r ≤ p.radius)
(r_pos   : 0 < r)
(has_sum : ∀ {y}, y ∈ emetric.ball (0 : E) r → has_sum (λn:ℕ, p n (λ(i : fin n), y)) (f (x + y)))

/-- Given a function `f : E → F` and a formal multilinear series `p`, we say that `f` has `p` as
a power series around `x` if `f (x + y) = ∑' pₙ yⁿ` for all `y` in a neighborhood of `0`. -/
def has_fpower_series_at (f : E → F) (p : formal_multilinear_series 𝕜 E F) (x : E) :=
∃ r, has_fpower_series_on_ball f p x r

variable (𝕜)
/-- Given a function `f : E → F`, we say that `f` is analytic at `x` if it admits a convergent power
series expansion around `x`. -/
def analytic_at (f : E → F) (x : E) :=
∃ (p : formal_multilinear_series 𝕜 E F), has_fpower_series_at f p x

/-- Given a function `f : E → F`, we say that `f` is analytic on a set `s` if it is analytic around
every point of `s`. -/
def analytic_on (f : E → F) (s : set E) :=
∀ x, x ∈ s → analytic_at 𝕜 f x

variable {𝕜}

lemma has_fpower_series_on_ball.has_fpower_series_at (hf : has_fpower_series_on_ball f p x r) :
  has_fpower_series_at f p x := ⟨r, hf⟩

lemma has_fpower_series_at.analytic_at (hf : has_fpower_series_at f p x) : analytic_at 𝕜 f x :=
⟨p, hf⟩

lemma has_fpower_series_on_ball.analytic_at (hf : has_fpower_series_on_ball f p x r) :
  analytic_at 𝕜 f x :=
hf.has_fpower_series_at.analytic_at

lemma has_fpower_series_on_ball.congr (hf : has_fpower_series_on_ball f p x r)
  (hg : eq_on f g (emetric.ball x r)) :
  has_fpower_series_on_ball g p x r :=
{ r_le := hf.r_le,
  r_pos := hf.r_pos,
  has_sum := λ y hy,
  begin
    convert hf.has_sum hy,
    apply hg.symm,
    simpa [edist_eq_coe_nnnorm_sub] using hy,
  end }

/-- If a function `f` has a power series `p` around `x`, then the function `z ↦ f (z - y)` has the
same power series around `x + y`. -/
lemma has_fpower_series_on_ball.comp_sub (hf : has_fpower_series_on_ball f p x r) (y : E) :
  has_fpower_series_on_ball (λ z, f (z - y)) p (x + y) r :=
{ r_le := hf.r_le,
  r_pos := hf.r_pos,
  has_sum := λ z hz, by { convert hf.has_sum hz, abel } }

lemma has_fpower_series_on_ball.has_sum_sub (hf : has_fpower_series_on_ball f p x r) {y : E}
  (hy : y ∈ emetric.ball x r) :
  has_sum (λ n : ℕ, p n (λ i, y - x)) (f y) :=
have y - x ∈ emetric.ball (0 : E) r, by simpa [edist_eq_coe_nnnorm_sub] using hy,
by simpa only [add_sub_cancel'_right] using hf.has_sum this

lemma has_fpower_series_on_ball.radius_pos (hf : has_fpower_series_on_ball f p x r) :
  0 < p.radius :=
lt_of_lt_of_le hf.r_pos hf.r_le

lemma has_fpower_series_at.radius_pos (hf : has_fpower_series_at f p x) :
  0 < p.radius :=
let ⟨r, hr⟩ := hf in hr.radius_pos

lemma has_fpower_series_on_ball.mono
  (hf : has_fpower_series_on_ball f p x r) (r'_pos : 0 < r') (hr : r' ≤ r) :
  has_fpower_series_on_ball f p x r' :=
⟨le_trans hr hf.1, r'_pos, λ y hy, hf.has_sum (emetric.ball_subset_ball hr hy)⟩

protected lemma has_fpower_series_at.eventually (hf : has_fpower_series_at f p x) :
  ∀ᶠ r : ℝ≥0∞ in 𝓝[>] 0, has_fpower_series_on_ball f p x r :=
let ⟨r, hr⟩ := hf in
mem_of_superset (Ioo_mem_nhds_within_Ioi (left_mem_Ico.2 hr.r_pos)) $
  λ r' hr', hr.mono hr'.1 hr'.2.le

lemma has_fpower_series_on_ball.add
  (hf : has_fpower_series_on_ball f pf x r) (hg : has_fpower_series_on_ball g pg x r) :
  has_fpower_series_on_ball (f + g) (pf + pg) x r :=
{ r_le := le_trans (le_min_iff.2 ⟨hf.r_le, hg.r_le⟩) (pf.min_radius_le_radius_add pg),
  r_pos := hf.r_pos,
  has_sum := λ y hy, (hf.has_sum hy).add (hg.has_sum hy) }

lemma has_fpower_series_at.add
  (hf : has_fpower_series_at f pf x) (hg : has_fpower_series_at g pg x) :
  has_fpower_series_at (f + g) (pf + pg) x :=
begin
  rcases (hf.eventually.and hg.eventually).exists with ⟨r, hr⟩,
  exact ⟨r, hr.1.add hr.2⟩
end

lemma analytic_at.add (hf : analytic_at 𝕜 f x) (hg : analytic_at 𝕜 g x) :
  analytic_at 𝕜 (f + g) x :=
let ⟨pf, hpf⟩ := hf, ⟨qf, hqf⟩ := hg in (hpf.add hqf).analytic_at

lemma has_fpower_series_on_ball.neg (hf : has_fpower_series_on_ball f pf x r) :
  has_fpower_series_on_ball (-f) (-pf) x r :=
{ r_le    := by { rw pf.radius_neg, exact hf.r_le },
  r_pos   := hf.r_pos,
  has_sum := λ y hy, (hf.has_sum hy).neg }

lemma has_fpower_series_at.neg
  (hf : has_fpower_series_at f pf x) : has_fpower_series_at (-f) (-pf) x :=
let ⟨rf, hrf⟩ := hf in hrf.neg.has_fpower_series_at

lemma analytic_at.neg (hf : analytic_at 𝕜 f x) : analytic_at 𝕜 (-f) x :=
let ⟨pf, hpf⟩ := hf in hpf.neg.analytic_at

lemma has_fpower_series_on_ball.sub
  (hf : has_fpower_series_on_ball f pf x r) (hg : has_fpower_series_on_ball g pg x r) :
  has_fpower_series_on_ball (f - g) (pf - pg) x r :=
by simpa only [sub_eq_add_neg] using hf.add hg.neg

lemma has_fpower_series_at.sub
  (hf : has_fpower_series_at f pf x) (hg : has_fpower_series_at g pg x) :
  has_fpower_series_at (f - g) (pf - pg) x :=
by simpa only [sub_eq_add_neg] using hf.add hg.neg

lemma analytic_at.sub (hf : analytic_at 𝕜 f x) (hg : analytic_at 𝕜 g x) :
  analytic_at 𝕜 (f - g) x :=
by simpa only [sub_eq_add_neg] using hf.add hg.neg

lemma has_fpower_series_on_ball.coeff_zero (hf : has_fpower_series_on_ball f pf x r)
  (v : fin 0 → E) : pf 0 v = f x :=
begin
  have v_eq : v = (λ i, 0) := subsingleton.elim _ _,
  have zero_mem : (0 : E) ∈ emetric.ball (0 : E) r, by simp [hf.r_pos],
  have : ∀ i ≠ 0, pf i (λ j, 0) = 0,
  { assume i hi,
    have : 0 < i := pos_iff_ne_zero.2 hi,
    exact continuous_multilinear_map.map_coord_zero _ (⟨0, this⟩ : fin i) rfl },
  have A := (hf.has_sum zero_mem).unique (has_sum_single _ this),
  simpa [v_eq] using A.symm,
end

lemma has_fpower_series_at.coeff_zero (hf : has_fpower_series_at f pf x) (v : fin 0 → E) :
  pf 0 v = f x :=
let ⟨rf, hrf⟩ := hf in hrf.coeff_zero v

/-- If a function `f` has a power series `p` on a ball and `g` is linear, then `g ∘ f` has the
power series `g ∘ p` on the same ball. -/
lemma _root_.continuous_linear_map.comp_has_fpower_series_on_ball
  (g : F →L[𝕜] G) (h : has_fpower_series_on_ball f p x r) :
  has_fpower_series_on_ball (g ∘ f) (g.comp_formal_multilinear_series p) x r :=
{ r_le := h.r_le.trans (p.radius_le_radius_continuous_linear_map_comp _),
  r_pos := h.r_pos,
  has_sum := λ y hy, by simpa only [continuous_linear_map.comp_formal_multilinear_series_apply,
      continuous_linear_map.comp_continuous_multilinear_map_coe, function.comp_app]
    using g.has_sum (h.has_sum hy) }

/-- If a function `f` is analytic on a set `s` and `g` is linear, then `g ∘ f` is analytic
on `s`. -/
lemma _root_.continuous_linear_map.comp_analytic_on {s : set E}
  (g : F →L[𝕜] G) (h : analytic_on 𝕜 f s) :
  analytic_on 𝕜 (g ∘ f) s :=
begin
  rintros x hx,
  rcases h x hx with ⟨p, r, hp⟩,
  exact ⟨g.comp_formal_multilinear_series p, r, g.comp_has_fpower_series_on_ball hp⟩,
end

/-- If a function admits a power series expansion, then it is exponentially close to the partial
sums of this power series on strict subdisks of the disk of convergence.

This version provides an upper estimate that decreases both in `∥y∥` and `n`. See also
`has_fpower_series_on_ball.uniform_geometric_approx` for a weaker version. -/
lemma has_fpower_series_on_ball.uniform_geometric_approx' {r' : ℝ≥0}
  (hf : has_fpower_series_on_ball f p x r) (h : (r' : ℝ≥0∞) < r) :
  ∃ (a ∈ Ioo (0 : ℝ) 1) (C > 0), (∀ y ∈ metric.ball (0 : E) r', ∀ n,
    ∥f (x + y) - p.partial_sum n y∥ ≤ C * (a * (∥y∥ / r')) ^ n) :=
begin
  obtain ⟨a, ha, C, hC, hp⟩ : ∃ (a ∈ Ioo (0 : ℝ) 1) (C > 0), ∀ n, ∥p n∥ * r' ^n ≤ C * a^n :=
    p.norm_mul_pow_le_mul_pow_of_lt_radius (h.trans_le hf.r_le),
  refine ⟨a, ha, C / (1 - a), div_pos hC (sub_pos.2 ha.2), λ y hy n, _⟩,
  have yr' : ∥y∥ < r', by { rw ball_zero_eq at hy, exact hy },
  have hr'0 : 0 < (r' : ℝ), from (norm_nonneg _).trans_lt yr',
  have : y ∈ emetric.ball (0 : E) r,
  { refine mem_emetric_ball_zero_iff.2 (lt_trans _ h),
    exact_mod_cast yr' },
  rw [norm_sub_rev, ← mul_div_right_comm],
  have ya : a * (∥y∥ / ↑r') ≤ a,
    from mul_le_of_le_one_right ha.1.le (div_le_one_of_le yr'.le r'.coe_nonneg),
  suffices : ∥p.partial_sum n y - f (x + y)∥ ≤ C * (a * (∥y∥ / r')) ^ n / (1 - a * (∥y∥ / r')),
  { refine this.trans _,
    apply_rules [div_le_div_of_le_left, sub_pos.2, div_nonneg, mul_nonneg, pow_nonneg, hC.lt.le,
      ha.1.le, norm_nonneg, nnreal.coe_nonneg, ha.2, (sub_le_sub_iff_left _).2]; apply_instance },
  apply norm_sub_le_of_geometric_bound_of_has_sum (ya.trans_lt ha.2) _ (hf.has_sum this),
  assume n,
  calc ∥(p n) (λ (i : fin n), y)∥ ≤ ∥p n∥ * (∏ i : fin n, ∥y∥) :
      continuous_multilinear_map.le_op_norm _ _
    ... = (∥p n∥ * r' ^ n) * (∥y∥ / r') ^ n : by field_simp [hr'0.ne', mul_right_comm]
    ... ≤ (C * a ^ n) * (∥y∥ / r') ^ n :
      mul_le_mul_of_nonneg_right (hp n) (pow_nonneg (div_nonneg (norm_nonneg _) r'.coe_nonneg) _)
    ... ≤ C * (a * (∥y∥ / r')) ^ n : by rw [mul_pow, mul_assoc]
end

/-- If a function admits a power series expansion, then it is exponentially close to the partial
sums of this power series on strict subdisks of the disk of convergence. -/
lemma has_fpower_series_on_ball.uniform_geometric_approx {r' : ℝ≥0}
  (hf : has_fpower_series_on_ball f p x r) (h : (r' : ℝ≥0∞) < r) :
  ∃ (a ∈ Ioo (0 : ℝ) 1) (C > 0), (∀ y ∈ metric.ball (0 : E) r', ∀ n,
    ∥f (x + y) - p.partial_sum n y∥ ≤ C * a ^ n) :=
begin
  obtain ⟨a, ha, C, hC, hp⟩ : ∃ (a ∈ Ioo (0 : ℝ) 1) (C > 0),
    (∀ y ∈ metric.ball (0 : E) r', ∀ n, ∥f (x + y) - p.partial_sum n y∥ ≤ C * (a * (∥y∥ / r')) ^ n),
    from hf.uniform_geometric_approx' h,
  refine ⟨a, ha, C, hC, λ y hy n, (hp y hy n).trans _⟩,
  have yr' : ∥y∥ < r', by rwa ball_zero_eq at hy,
  refine mul_le_mul_of_nonneg_left (pow_le_pow_of_le_left _ _ _) hC.lt.le,
  exacts [mul_nonneg ha.1.le (div_nonneg (norm_nonneg y) r'.coe_nonneg),
    mul_le_of_le_one_right ha.1.le (div_le_one_of_le yr'.le r'.coe_nonneg)]
end

/-- Taylor formula for an analytic function, `is_O` version. -/
lemma has_fpower_series_at.is_O_sub_partial_sum_pow (hf : has_fpower_series_at f p x) (n : ℕ) :
  is_O (λ y : E, f (x + y) - p.partial_sum n y) (λ y, ∥y∥ ^ n) (𝓝 0) :=
begin
  rcases hf with ⟨r, hf⟩,
  rcases ennreal.lt_iff_exists_nnreal_btwn.1 hf.r_pos with ⟨r', r'0, h⟩,
  obtain ⟨a, ha, C, hC, hp⟩ : ∃ (a ∈ Ioo (0 : ℝ) 1) (C > 0),
    (∀ y ∈ metric.ball (0 : E) r', ∀ n, ∥f (x + y) - p.partial_sum n y∥ ≤ C * (a * (∥y∥ / r')) ^ n),
    from hf.uniform_geometric_approx' h,
  refine is_O_iff.2 ⟨C * (a / r') ^ n, _⟩,
  replace r'0 : 0 < (r' : ℝ), by exact_mod_cast r'0,
  filter_upwards [metric.ball_mem_nhds (0 : E) r'0] with y hy,
  simpa [mul_pow, mul_div_assoc, mul_assoc, div_mul_eq_mul_div] using hp y hy n,
end

/-- If `f` has formal power series `∑ n, pₙ` on a ball of radius `r`, then for `y, z` in any smaller
ball, the norm of the difference `f y - f z - p 1 (λ _, y - z)` is bounded above by
`C * (max ∥y - x∥ ∥z - x∥) * ∥y - z∥`. This lemma formulates this property using `is_O` and
`filter.principal` on `E × E`. -/
lemma has_fpower_series_on_ball.is_O_image_sub_image_sub_deriv_principal
  (hf : has_fpower_series_on_ball f p x r) (hr : r' < r) :
  is_O (λ y : E × E, f y.1 - f y.2 - (p 1 (λ _, y.1 - y.2)))
    (λ y, ∥y - (x, x)∥ * ∥y.1 - y.2∥) (𝓟 $ emetric.ball (x, x) r') :=
begin
  lift r' to ℝ≥0 using ne_top_of_lt hr,
  rcases (zero_le r').eq_or_lt with rfl|hr'0,
  { simp only [is_O_bot, emetric.ball_zero, principal_empty, ennreal.coe_zero] },
  obtain ⟨a, ha, C, hC : 0 < C, hp⟩ :
    ∃ (a ∈ Ioo (0 : ℝ) 1) (C > 0), ∀ (n : ℕ), ∥p n∥ * ↑r' ^ n ≤ C * a ^ n,
    from p.norm_mul_pow_le_mul_pow_of_lt_radius (hr.trans_le hf.r_le),
  simp only [← le_div_iff (pow_pos (nnreal.coe_pos.2 hr'0) _)] at hp,
  set L : E × E → ℝ := λ y,
    (C * (a / r') ^ 2) * (∥y - (x, x)∥ * ∥y.1 - y.2∥) * (a / (1 - a) ^ 2 + 2 / (1 - a)),
  have hL : ∀ y ∈ emetric.ball (x, x) r',
    ∥f y.1 - f y.2 - (p 1 (λ _, y.1 - y.2))∥ ≤ L y,
  { intros y hy',
    have hy : y ∈ emetric.ball x r ×ˢ emetric.ball x r,
    { rw [emetric.ball_prod_same], exact emetric.ball_subset_ball hr.le hy' },
    set A : ℕ → F := λ n, p n (λ _, y.1 - x) - p n (λ _, y.2 - x),
    have hA : has_sum (λ n, A (n + 2)) (f y.1 - f y.2 - (p 1 (λ _, y.1 - y.2))),
    { convert (has_sum_nat_add_iff' 2).2 ((hf.has_sum_sub hy.1).sub (hf.has_sum_sub hy.2)) using 1,
      rw [finset.sum_range_succ, finset.sum_range_one, hf.coeff_zero, hf.coeff_zero, sub_self,
        zero_add, ← subsingleton.pi_single_eq (0 : fin 1) (y.1 - x), pi.single,
        ← subsingleton.pi_single_eq (0 : fin 1) (y.2 - x), pi.single, ← (p 1).map_sub, ← pi.single,
        subsingleton.pi_single_eq, sub_sub_sub_cancel_right] },
    rw [emetric.mem_ball, edist_eq_coe_nnnorm_sub, ennreal.coe_lt_coe] at hy',
    set B : ℕ → ℝ := λ n,
      (C * (a / r') ^ 2) * (∥y - (x, x)∥ * ∥y.1 - y.2∥) * ((n + 2) * a ^ n),
    have hAB : ∀ n, ∥A (n + 2)∥ ≤ B n := λ n,
    calc ∥A (n + 2)∥ ≤ ∥p (n + 2)∥ * ↑(n + 2) * ∥y - (x, x)∥ ^ (n + 1) * ∥y.1 - y.2∥ :
      by simpa only [fintype.card_fin, pi_norm_const (_ : E), prod.norm_def, pi.sub_def,
        prod.fst_sub, prod.snd_sub, sub_sub_sub_cancel_right]
        using (p $ n + 2).norm_image_sub_le (λ _, y.1 - x) (λ _, y.2 - x)
    ... = ∥p (n + 2)∥ * ∥y - (x, x)∥ ^ n * (↑(n + 2) * ∥y - (x, x)∥ * ∥y.1 - y.2∥) :
      by { rw [pow_succ ∥y - (x, x)∥], ring }
    ... ≤ (C * a ^ (n + 2) / r' ^ (n + 2)) * r' ^ n * (↑(n + 2) * ∥y - (x, x)∥ * ∥y.1 - y.2∥) :
      by apply_rules [mul_le_mul_of_nonneg_right, mul_le_mul, hp, pow_le_pow_of_le_left,
        hy'.le, norm_nonneg, pow_nonneg, div_nonneg, mul_nonneg, nat.cast_nonneg,
        hC.le, r'.coe_nonneg, ha.1.le]
    ... = B n :
      by { field_simp [B, pow_succ, hr'0.ne'], simp only [mul_assoc, mul_comm, mul_left_comm] },
    have hBL : has_sum B (L y),
    { apply has_sum.mul_left,
      simp only [add_mul],
      have : ∥a∥ < 1, by simp only [real.norm_eq_abs, abs_of_pos ha.1, ha.2],
      convert (has_sum_coe_mul_geometric_of_norm_lt_1 this).add
        ((has_sum_geometric_of_norm_lt_1 this).mul_left 2) },
    exact hA.norm_le_of_bounded hBL hAB },
  suffices : is_O L (λ y, ∥y - (x, x)∥ * ∥y.1 - y.2∥) (𝓟 (emetric.ball (x, x) r')),
  { refine (is_O.of_bound 1 (eventually_principal.2 $ λ y hy, _)).trans this,
    rw one_mul,
    exact (hL y hy).trans (le_abs_self _) },
  simp_rw [L, mul_right_comm _ (_ * _)],
  exact (is_O_refl _ _).const_mul_left _,
end

/-- If `f` has formal power series `∑ n, pₙ` on a ball of radius `r`, then for `y, z` in any smaller
ball, the norm of the difference `f y - f z - p 1 (λ _, y - z)` is bounded above by
`C * (max ∥y - x∥ ∥z - x∥) * ∥y - z∥`. -/
lemma has_fpower_series_on_ball.image_sub_sub_deriv_le
  (hf : has_fpower_series_on_ball f p x r) (hr : r' < r) :
  ∃ C, ∀ (y z ∈ emetric.ball x r'),
    ∥f y - f z - (p 1 (λ _, y - z))∥ ≤ C * (max ∥y - x∥ ∥z - x∥) * ∥y - z∥ :=
by simpa only [is_O_principal, mul_assoc, norm_mul, norm_norm, prod.forall,
  emetric.mem_ball, prod.edist_eq, max_lt_iff, and_imp, @forall_swap (_ < _) E]
  using hf.is_O_image_sub_image_sub_deriv_principal hr

/-- If `f` has formal power series `∑ n, pₙ` at `x`, then
`f y - f z - p 1 (λ _, y - z) = O(∥(y, z) - (x, x)∥ * ∥y - z∥)` as `(y, z) → (x, x)`.
In particular, `f` is strictly differentiable at `x`. -/
lemma has_fpower_series_at.is_O_image_sub_norm_mul_norm_sub (hf : has_fpower_series_at f p x) :
  is_O (λ y : E × E, f y.1 - f y.2 - (p 1 (λ _, y.1 - y.2)))
    (λ y, ∥y - (x, x)∥ * ∥y.1 - y.2∥) (𝓝 (x, x)) :=
begin
  rcases hf with ⟨r, hf⟩,
  rcases ennreal.lt_iff_exists_nnreal_btwn.1 hf.r_pos with ⟨r', r'0, h⟩,
  refine (hf.is_O_image_sub_image_sub_deriv_principal h).mono _,
  exact le_principal_iff.2 (emetric.ball_mem_nhds _ r'0)
end

/-- If a function admits a power series expansion at `x`, then it is the uniform limit of the
partial sums of this power series on strict subdisks of the disk of convergence, i.e., `f (x + y)`
is the uniform limit of `p.partial_sum n y` there. -/
lemma has_fpower_series_on_ball.tendsto_uniformly_on {r' : ℝ≥0}
  (hf : has_fpower_series_on_ball f p x r) (h : (r' : ℝ≥0∞) < r) :
  tendsto_uniformly_on (λ n y, p.partial_sum n y)
    (λ y, f (x + y)) at_top (metric.ball (0 : E) r') :=
begin
  obtain ⟨a, ha, C, hC, hp⟩ : ∃ (a ∈ Ioo (0 : ℝ) 1) (C > 0),
    (∀ y ∈ metric.ball (0 : E) r', ∀ n, ∥f (x + y) - p.partial_sum n y∥ ≤ C * a ^ n),
    from hf.uniform_geometric_approx h,
  refine metric.tendsto_uniformly_on_iff.2 (λ ε εpos, _),
  have L : tendsto (λ n, (C : ℝ) * a^n) at_top (𝓝 ((C : ℝ) * 0)) :=
    tendsto_const_nhds.mul (tendsto_pow_at_top_nhds_0_of_lt_1 ha.1.le ha.2),
  rw mul_zero at L,
  refine (L.eventually (gt_mem_nhds εpos)).mono (λ n hn y hy, _),
  rw dist_eq_norm,
  exact (hp y hy n).trans_lt hn
end

/-- If a function admits a power series expansion at `x`, then it is the locally uniform limit of
the partial sums of this power series on the disk of convergence, i.e., `f (x + y)`
is the locally uniform limit of `p.partial_sum n y` there. -/
lemma has_fpower_series_on_ball.tendsto_locally_uniformly_on
  (hf : has_fpower_series_on_ball f p x r) :
  tendsto_locally_uniformly_on (λ n y, p.partial_sum n y) (λ y, f (x + y))
    at_top (emetric.ball (0 : E) r) :=
begin
  assume u hu x hx,
  rcases ennreal.lt_iff_exists_nnreal_btwn.1 hx with ⟨r', xr', hr'⟩,
  have : emetric.ball (0 : E) r' ∈ 𝓝 x :=
    is_open.mem_nhds emetric.is_open_ball xr',
  refine ⟨emetric.ball (0 : E) r', mem_nhds_within_of_mem_nhds this, _⟩,
  simpa [metric.emetric_ball_nnreal] using hf.tendsto_uniformly_on hr' u hu
end

/-- If a function admits a power series expansion at `x`, then it is the uniform limit of the
partial sums of this power series on strict subdisks of the disk of convergence, i.e., `f y`
is the uniform limit of `p.partial_sum n (y - x)` there. -/
lemma has_fpower_series_on_ball.tendsto_uniformly_on' {r' : ℝ≥0}
  (hf : has_fpower_series_on_ball f p x r) (h : (r' : ℝ≥0∞) < r) :
  tendsto_uniformly_on (λ n y, p.partial_sum n (y - x)) f at_top (metric.ball (x : E) r') :=
begin
  convert (hf.tendsto_uniformly_on h).comp (λ y, y - x),
  { simp [(∘)] },
  { ext z, simp [dist_eq_norm] }
end

/-- If a function admits a power series expansion at `x`, then it is the locally uniform limit of
the  partial sums of this power series on the disk of convergence, i.e., `f y`
is the locally uniform limit of `p.partial_sum n (y - x)` there. -/
lemma has_fpower_series_on_ball.tendsto_locally_uniformly_on'
  (hf : has_fpower_series_on_ball f p x r) :
  tendsto_locally_uniformly_on (λ n y, p.partial_sum n (y - x)) f at_top (emetric.ball (x : E) r) :=
begin
  have A : continuous_on (λ (y : E), y - x) (emetric.ball (x : E) r) :=
    (continuous_id.sub continuous_const).continuous_on,
  convert (hf.tendsto_locally_uniformly_on).comp (λ (y : E), y - x) _ A,
  { ext z, simp },
  { assume z, simp [edist_eq_coe_nnnorm, edist_eq_coe_nnnorm_sub] }
end

/-- If a function admits a power series expansion on a disk, then it is continuous there. -/
protected lemma has_fpower_series_on_ball.continuous_on
  (hf : has_fpower_series_on_ball f p x r) : continuous_on f (emetric.ball x r) :=
hf.tendsto_locally_uniformly_on'.continuous_on $ eventually_of_forall $ λ n,
  ((p.partial_sum_continuous n).comp (continuous_id.sub continuous_const)).continuous_on

protected lemma has_fpower_series_at.continuous_at (hf : has_fpower_series_at f p x) :
  continuous_at f x :=
let ⟨r, hr⟩ := hf in hr.continuous_on.continuous_at (emetric.ball_mem_nhds x (hr.r_pos))

protected lemma analytic_at.continuous_at (hf : analytic_at 𝕜 f x) : continuous_at f x :=
let ⟨p, hp⟩ := hf in hp.continuous_at

protected lemma analytic_on.continuous_on {s : set E} (hf : analytic_on 𝕜 f s) :
  continuous_on f s :=
λ x hx, (hf x hx).continuous_at.continuous_within_at

/-- In a complete space, the sum of a converging power series `p` admits `p` as a power series.
This is not totally obvious as we need to check the convergence of the series. -/
protected lemma formal_multilinear_series.has_fpower_series_on_ball [complete_space F]
  (p : formal_multilinear_series 𝕜 E F) (h : 0 < p.radius) :
  has_fpower_series_on_ball p.sum p 0 p.radius :=
{ r_le    := le_rfl,
  r_pos   := h,
  has_sum := λ y hy, by { rw zero_add, exact p.has_sum hy } }

lemma has_fpower_series_on_ball.sum (h : has_fpower_series_on_ball f p x r)
  {y : E} (hy : y ∈ emetric.ball (0 : E) r) : f (x + y) = p.sum y :=
(h.has_sum hy).tsum_eq.symm

/-- The sum of a converging power series is continuous in its disk of convergence. -/
protected lemma formal_multilinear_series.continuous_on [complete_space F] :
  continuous_on p.sum (emetric.ball 0 p.radius) :=
begin
  cases (zero_le p.radius).eq_or_lt with h h,
  { simp [← h, continuous_on_empty] },
  { exact (p.has_fpower_series_on_ball h).continuous_on }
end

end

/-!
### Uniqueness of power series
If a function `f : E → F` has two representations as power series at a point `x : E`, corresponding
to formal multilinear series `p₁` and `p₂`, then these representations agree term-by-term. That is,
for any `n : ℕ` and `y : E`,  `p₁ n (λ i, y) = p₂ n (λ i, y)`. In the one-dimensional case, when
`f : 𝕜 → E`, the continuous multilinear maps `p₁ n` and `p₂ n` are given by
`formal_multilinear_series.mk_pi_field`, and hence are determined completely by the value of
`p₁ n (λ i, 1)`, so `p₁ = p₂`. Consequently, the radius of convergence for one series can be
transferred to the other.
-/

section uniqueness

open continuous_multilinear_map

lemma asymptotics.is_O.continuous_multilinear_map_apply_eq_zero {n : ℕ} {p : E [×n]→L[𝕜] F}
  (h : is_O (λ y, p (λ i, y)) (λ y, ∥y∥ ^ (n + 1)) (𝓝 0)) (y : E) :
  p (λ i, y) = 0 :=
begin
  obtain ⟨c, c_pos, hc⟩ := h.exists_pos,
  obtain ⟨t, ht, t_open, z_mem⟩ := eventually_nhds_iff.mp (is_O_with_iff.mp hc),
  obtain ⟨δ, δ_pos, δε⟩ := (metric.is_open_iff.mp t_open) 0 z_mem,
  clear h hc z_mem,
  cases n,
  { exact norm_eq_zero.mp (by simpa only [fin0_apply_norm, norm_eq_zero, norm_zero, zero_pow',
      ne.def, nat.one_ne_zero, not_false_iff, mul_zero, norm_le_zero_iff]
      using ht 0 (δε (metric.mem_ball_self δ_pos))), },
  { refine or.elim (em (y = 0)) (λ hy, by simpa only [hy] using p.map_zero) (λ hy, _),
    replace hy := norm_pos_iff.mpr hy,
    refine norm_eq_zero.mp (le_antisymm (le_of_forall_pos_le_add (λ ε ε_pos, _)) (norm_nonneg _)),
    have h₀ := mul_pos c_pos (pow_pos hy (n.succ + 1)),
    obtain ⟨k, k_pos, k_norm⟩ := normed_field.exists_norm_lt 𝕜
      (lt_min (mul_pos δ_pos (inv_pos.mpr hy)) (mul_pos ε_pos (inv_pos.mpr h₀))),
    have h₁ : ∥k • y∥ < δ,
    { rw norm_smul,
      exact inv_mul_cancel_right₀ hy.ne.symm δ ▸ mul_lt_mul_of_pos_right
        (lt_of_lt_of_le k_norm (min_le_left _ _)) hy },
    have h₂ := calc
      ∥p (λ i, k • y)∥ ≤ c * ∥k • y∥ ^ (n.succ + 1)
                       : by simpa only [norm_pow, norm_norm]
                           using ht (k • y) (δε (mem_ball_zero_iff.mpr h₁))
      ...              = ∥k∥ ^ n.succ * (∥k∥ * (c * ∥y∥ ^ (n.succ + 1)))
                       : by { simp only [norm_smul, mul_pow], rw pow_succ, ring },
    have h₃ : ∥k∥ * (c * ∥y∥ ^ (n.succ + 1)) < ε, from inv_mul_cancel_right₀ h₀.ne.symm ε ▸
      mul_lt_mul_of_pos_right (lt_of_lt_of_le k_norm (min_le_right _ _)) h₀,
    calc ∥p (λ i, y)∥ = ∥(k⁻¹) ^ n.succ∥ * ∥p (λ i, k • y)∥
        : by simpa only [inv_smul_smul₀ (norm_pos_iff.mp k_pos),
            norm_smul, finset.prod_const, finset.card_fin] using
            congr_arg norm (p.map_smul_univ (λ (i : fin n.succ), k⁻¹) (λ (i : fin n.succ), k • y))
    ...              ≤ ∥(k⁻¹) ^ n.succ∥ * (∥k∥ ^ n.succ * (∥k∥ * (c * ∥y∥ ^ (n.succ + 1))))
        : mul_le_mul_of_nonneg_left h₂ (norm_nonneg _)
    ...              = ∥(k⁻¹ * k) ^ n.succ∥ * (∥k∥ * (c * ∥y∥ ^ (n.succ + 1)))
        : by { rw ←mul_assoc, simp [norm_mul, mul_pow] }
    ...              ≤ 0 + ε
        : by { rw inv_mul_cancel (norm_pos_iff.mp k_pos), simpa using h₃.le }, },
end

/-- If a formal multilinear series `p` represents the zero function at `x : E`, then the
terms `p n (λ i, y)` appearing the in sum are zero for any `n : ℕ`, `y : E`. -/
lemma has_fpower_series_at.apply_eq_zero {p : formal_multilinear_series 𝕜 E F} {x : E}
  (h : has_fpower_series_at 0 p x) (n : ℕ) :
  ∀ y : E, p n (λ i, y) = 0 :=
begin
  refine nat.strong_rec_on n (λ k hk, _),
  have psum_eq : p.partial_sum (k + 1) = (λ y, p k (λ i, y)),
  { funext z,
    refine finset.sum_eq_single _ (λ b hb hnb, _) (λ hn, _),
    { have := finset.mem_range_succ_iff.mp hb,
      simp only [hk b (this.lt_of_ne hnb), pi.zero_apply, zero_apply] },
    { exact false.elim (hn (finset.mem_range.mpr (lt_add_one k))) } },
  replace h := h.is_O_sub_partial_sum_pow k.succ,
  simp only [psum_eq, zero_sub, pi.zero_apply, asymptotics.is_O_neg_left] at h,
  exact h.continuous_multilinear_map_apply_eq_zero,
end

/-- A one-dimensional formal multilinear series representing the zero function is zero. -/
lemma has_fpower_series_at.eq_zero {p : formal_multilinear_series 𝕜 𝕜 E} {x : 𝕜}
  (h : has_fpower_series_at 0 p x) : p = 0 :=
by { ext n x, rw ←mk_pi_field_apply_one_eq_self (p n), simp [h.apply_eq_zero n 1] }

/-- One-dimensional formal multilinear series representing the same function are equal. -/
theorem has_fpower_series_at.eq_formal_multilinear_series
  {p₁ p₂ : formal_multilinear_series 𝕜 𝕜 E} {f : 𝕜 → E} {x : 𝕜}
  (h₁ : has_fpower_series_at f p₁ x) (h₂ : has_fpower_series_at f p₂ x) :
  p₁ = p₂ :=
sub_eq_zero.mp (has_fpower_series_at.eq_zero (by simpa only [sub_self] using h₁.sub h₂))

/-- If a function `f : 𝕜 → E` has two power series representations at `x`, then the given radii in
which convergence is guaranteed may be interchanged. This can be useful when the formal multilinear
series in one representation has a particularly nice form, but the other has a larger radius. -/
theorem has_fpower_series_on_ball.exchange_radius
  {p₁ p₂ : formal_multilinear_series 𝕜 𝕜 E} {f : 𝕜 → E} {r₁ r₂ : ℝ≥0∞} {x : 𝕜}
  (h₁ : has_fpower_series_on_ball f p₁ x r₁) (h₂ : has_fpower_series_on_ball f p₂ x r₂) :
  has_fpower_series_on_ball f p₁ x r₂ :=
h₂.has_fpower_series_at.eq_formal_multilinear_series h₁.has_fpower_series_at ▸ h₂

/-- If a function `f : 𝕜 → E` has power series representation `p` on a ball of some radius and for
each positive radius it has some power series representation, then `p` converges to `f` on the whole
`𝕜`. -/
theorem has_fpower_series_on_ball.r_eq_top_of_exists {f : 𝕜 → E} {r : ℝ≥0∞} {x : 𝕜}
  {p : formal_multilinear_series 𝕜 𝕜 E} (h : has_fpower_series_on_ball f p x r)
  (h' : ∀ (r' : ℝ≥0) (hr : 0 < r'),
    ∃ p' : formal_multilinear_series 𝕜 𝕜 E, has_fpower_series_on_ball f p' x r') :
  has_fpower_series_on_ball f p x ∞ :=
{ r_le := ennreal.le_of_forall_pos_nnreal_lt $ λ r hr hr',
    let ⟨p', hp'⟩ := h' r hr in (h.exchange_radius hp').r_le,
  r_pos := ennreal.coe_lt_top,
  has_sum := λ y hy, let ⟨r', hr'⟩ := exists_gt ∥y∥₊, ⟨p', hp'⟩ := h' r' hr'.ne_bot.bot_lt
    in (h.exchange_radius hp').has_sum $ mem_emetric_ball_zero_iff.mpr (ennreal.coe_lt_coe.2 hr') }

end uniqueness

/-!
### Changing origin in a power series

If a function is analytic in a disk `D(x, R)`, then it is analytic in any disk contained in that
one. Indeed, one can write
$$
f (x + y + z) = \sum_{n} p_n (y + z)^n = \sum_{n, k} \binom{n}{k} p_n y^{n-k} z^k
= \sum_{k} \Bigl(\sum_{n} \binom{n}{k} p_n y^{n-k}\Bigr) z^k.
$$
The corresponding power series has thus a `k`-th coefficient equal to
$\sum_{n} \binom{n}{k} p_n y^{n-k}$. In the general case where `pₙ` is a multilinear map, this has
to be interpreted suitably: instead of having a binomial coefficient, one should sum over all
possible subsets `s` of `fin n` of cardinal `k`, and attribute `z` to the indices in `s` and
`y` to the indices outside of `s`.

In this paragraph, we implement this. The new power series is called `p.change_origin y`. Then, we
check its convergence and the fact that its sum coincides with the original sum. The outcome of this
discussion is that the set of points where a function is analytic is open.
-/

namespace formal_multilinear_series

section

variables (p : formal_multilinear_series 𝕜 E F) {x y : E} {r R : ℝ≥0}

/-- A term of `formal_multilinear_series.change_origin_series`.

Given a formal multilinear series `p` and a point `x` in its ball of convergence,
`p.change_origin x` is a formal multilinear series such that
`p.sum (x+y) = (p.change_origin x).sum y` when this makes sense. Each term of `p.change_origin x`
is itself an analytic function of `x` given by the series `p.change_origin_series`. Each term in
`change_origin_series` is the sum of `change_origin_series_term`'s over all `s` of cardinality `l`.
The definition is such that
`p.change_origin_series_term k l s hs (λ _, x) (λ _, y) = p (k + l) (s.piecewise (λ _, x) (λ _, y))`
-/
def change_origin_series_term (k l : ℕ) (s : finset (fin (k + l))) (hs : s.card = l) :
  E [×l]→L[𝕜] E [×k]→L[𝕜] F :=
continuous_multilinear_map.curry_fin_finset 𝕜 E F hs
    (by erw [finset.card_compl, fintype.card_fin, hs, add_tsub_cancel_right]) (p $ k + l)

lemma change_origin_series_term_apply (k l : ℕ) (s : finset (fin (k + l))) (hs : s.card = l)
  (x y : E) :
  p.change_origin_series_term k l s hs (λ _, x) (λ _, y) =
    p (k + l) (s.piecewise (λ _, x) (λ _, y)) :=
continuous_multilinear_map.curry_fin_finset_apply_const _ _ _ _ _

@[simp] lemma norm_change_origin_series_term (k l : ℕ) (s : finset (fin (k + l)))
  (hs : s.card = l) :
  ∥p.change_origin_series_term k l s hs∥ = ∥p (k + l)∥ :=
by simp only [change_origin_series_term, linear_isometry_equiv.norm_map]

@[simp] lemma nnnorm_change_origin_series_term (k l : ℕ) (s : finset (fin (k + l)))
  (hs : s.card = l) :
  ∥p.change_origin_series_term k l s hs∥₊ = ∥p (k + l)∥₊ :=
by simp only [change_origin_series_term, linear_isometry_equiv.nnnorm_map]

lemma nnnorm_change_origin_series_term_apply_le (k l : ℕ) (s : finset (fin (k + l)))
  (hs : s.card = l) (x y : E) :
  ∥p.change_origin_series_term k l s hs (λ _, x) (λ _, y)∥₊ ≤ ∥p (k + l)∥₊ * ∥x∥₊ ^ l * ∥y∥₊ ^ k :=
begin
  rw [← p.nnnorm_change_origin_series_term k l s hs, ← fin.prod_const, ← fin.prod_const],
  apply continuous_multilinear_map.le_of_op_nnnorm_le,
  apply continuous_multilinear_map.le_op_nnnorm
end

/-- The power series for `f.change_origin k`.

Given a formal multilinear series `p` and a point `x` in its ball of convergence,
`p.change_origin x` is a formal multilinear series such that
`p.sum (x+y) = (p.change_origin x).sum y` when this makes sense. Its `k`-th term is the sum of
the series `p.change_origin_series k`. -/
def change_origin_series (k : ℕ) : formal_multilinear_series 𝕜 E (E [×k]→L[𝕜] F) :=
λ l, ∑ s : {s : finset (fin (k + l)) // finset.card s = l}, p.change_origin_series_term k l s s.2

lemma nnnorm_change_origin_series_le_tsum (k l : ℕ) :
  ∥p.change_origin_series k l∥₊ ≤
    ∑' (x : {s : finset (fin (k + l)) // s.card = l}), ∥p (k + l)∥₊ :=
(nnnorm_sum_le _ _).trans_eq $ by simp only [tsum_fintype, nnnorm_change_origin_series_term]

lemma nnnorm_change_origin_series_apply_le_tsum (k l : ℕ) (x : E) :
  ∥p.change_origin_series k l (λ _, x)∥₊ ≤
    ∑' s : {s : finset (fin (k + l)) // s.card = l}, ∥p (k + l)∥₊ * ∥x∥₊ ^ l :=
begin
  rw [nnreal.tsum_mul_right, ← fin.prod_const],
  exact (p.change_origin_series k l).le_of_op_nnnorm_le _
    (p.nnnorm_change_origin_series_le_tsum _ _)
end

/--
Changing the origin of a formal multilinear series `p`, so that
`p.sum (x+y) = (p.change_origin x).sum y` when this makes sense.
-/
def change_origin (x : E) : formal_multilinear_series 𝕜 E F :=
λ k, (p.change_origin_series k).sum x

/-- An auxiliary equivalence useful in the proofs about
`formal_multilinear_series.change_origin_series`: the set of triples `(k, l, s)`, where `s` is a
`finset (fin (k + l))` of cardinality `l` is equivalent to the set of pairs `(n, s)`, where `s` is a
`finset (fin n)`.

The forward map sends `(k, l, s)` to `(k + l, s)` and the inverse map sends `(n, s)` to
`(n - finset.card s, finset.card s, s)`. The actual definition is less readable because of problems
with non-definitional equalities. -/
@[simps] def change_origin_index_equiv :
  (Σ k l : ℕ, {s : finset (fin (k + l)) // s.card = l}) ≃ Σ n : ℕ, finset (fin n) :=
{ to_fun := λ s, ⟨s.1 + s.2.1, s.2.2⟩,
  inv_fun := λ s, ⟨s.1 - s.2.card, s.2.card, ⟨s.2.map
    (fin.cast $ (tsub_add_cancel_of_le $ card_finset_fin_le s.2).symm).to_equiv.to_embedding,
    finset.card_map _⟩⟩,
  left_inv :=
    begin
      rintro ⟨k, l, ⟨s : finset (fin $ k + l), hs : s.card = l⟩⟩,
      dsimp only [subtype.coe_mk],
      -- Lean can't automatically generalize `k' = k + l - s.card`, `l' = s.card`, so we explicitly
      -- formulate the generalized goal
      suffices : ∀ k' l', k' = k → l' = l → ∀ (hkl : k + l = k' + l') hs',
        (⟨k', l', ⟨finset.map (fin.cast hkl).to_equiv.to_embedding s, hs'⟩⟩ :
          (Σ k l : ℕ, {s : finset (fin (k + l)) // s.card = l})) = ⟨k, l, ⟨s, hs⟩⟩,
      { apply this; simp only [hs, add_tsub_cancel_right] },
      rintro _ _ rfl rfl hkl hs',
      simp only [equiv.refl_to_embedding, fin.cast_refl, finset.map_refl, eq_self_iff_true,
        order_iso.refl_to_equiv, and_self, heq_iff_eq]
    end,
  right_inv :=
    begin
      rintro ⟨n, s⟩,
      simp [tsub_add_cancel_of_le (card_finset_fin_le s), fin.cast_to_equiv]
    end }

lemma change_origin_series_summable_aux₁ {r r' : ℝ≥0} (hr : (r + r' : ℝ≥0∞) < p.radius) :
  summable (λ s : Σ k l : ℕ, {s : finset (fin (k + l)) // s.card = l},
    ∥p (s.1 + s.2.1)∥₊ * r ^ s.2.1 * r' ^ s.1) :=
begin
  rw ← change_origin_index_equiv.symm.summable_iff,
  dsimp only [(∘), change_origin_index_equiv_symm_apply_fst,
    change_origin_index_equiv_symm_apply_snd_fst],
  have : ∀ n : ℕ, has_sum
    (λ s : finset (fin n), ∥p (n - s.card + s.card)∥₊ * r ^ s.card * r' ^ (n - s.card))
    (∥p n∥₊ * (r + r') ^ n),
  { intro n,
    -- TODO: why `simp only [tsub_add_cancel_of_le (card_finset_fin_le _)]` fails?
    convert_to has_sum (λ s : finset (fin n), ∥p n∥₊ * (r ^ s.card * r' ^ (n - s.card))) _,
    { ext1 s, rw [tsub_add_cancel_of_le (card_finset_fin_le _), mul_assoc] },
    rw ← fin.sum_pow_mul_eq_add_pow,
    exact (has_sum_fintype _).mul_left _ },
  refine nnreal.summable_sigma.2 ⟨λ n, (this n).summable, _⟩,
  simp only [(this _).tsum_eq],
  exact p.summable_nnnorm_mul_pow hr
end

lemma change_origin_series_summable_aux₂ (hr : (r : ℝ≥0∞) < p.radius) (k : ℕ) :
  summable (λ s : Σ l : ℕ, {s : finset (fin (k + l)) // s.card = l}, ∥p (k + s.1)∥₊ * r ^ s.1) :=
begin
  rcases ennreal.lt_iff_exists_add_pos_lt.1 hr with ⟨r', h0, hr'⟩,
  simpa only [mul_inv_cancel_right₀ (pow_pos h0 _).ne']
    using ((nnreal.summable_sigma.1
      (p.change_origin_series_summable_aux₁ hr')).1 k).mul_right (r' ^ k)⁻¹
end

lemma change_origin_series_summable_aux₃ {r : ℝ≥0} (hr : ↑r < p.radius) (k : ℕ) :
  summable (λ l : ℕ, ∥p.change_origin_series k l∥₊ * r ^ l) :=
begin
  refine nnreal.summable_of_le (λ n, _)
    (nnreal.summable_sigma.1 $ p.change_origin_series_summable_aux₂ hr k).2,
  simp only [nnreal.tsum_mul_right],
  exact mul_le_mul' (p.nnnorm_change_origin_series_le_tsum _ _) le_rfl
end

lemma le_change_origin_series_radius (k : ℕ) :
  p.radius ≤ (p.change_origin_series k).radius :=
ennreal.le_of_forall_nnreal_lt $ λ r hr,
  le_radius_of_summable_nnnorm _ (p.change_origin_series_summable_aux₃ hr k)

lemma nnnorm_change_origin_le (k : ℕ) (h : (∥x∥₊ : ℝ≥0∞) < p.radius) :
  ∥p.change_origin x k∥₊ ≤
    ∑' s : Σ l : ℕ, {s : finset (fin (k + l)) // s.card = l}, ∥p (k + s.1)∥₊ * ∥x∥₊ ^ s.1 :=
begin
  refine tsum_of_nnnorm_bounded _ (λ l, p.nnnorm_change_origin_series_apply_le_tsum k l x),
  have := p.change_origin_series_summable_aux₂ h k,
  refine has_sum.sigma this.has_sum (λ l, _),
  exact ((nnreal.summable_sigma.1 this).1 l).has_sum
end

/-- The radius of convergence of `p.change_origin x` is at least `p.radius - ∥x∥`. In other words,
`p.change_origin x` is well defined on the largest ball contained in the original ball of
convergence.-/
lemma change_origin_radius : p.radius - ∥x∥₊ ≤ (p.change_origin x).radius :=
begin
  refine ennreal.le_of_forall_pos_nnreal_lt (λ r h0 hr, _),
  rw [lt_tsub_iff_right, add_comm] at hr,
  have hr' : (∥x∥₊ : ℝ≥0∞) < p.radius, from (le_add_right le_rfl).trans_lt hr,
  apply le_radius_of_summable_nnnorm,
  have : ∀ k : ℕ, ∥p.change_origin x k∥₊ * r ^ k ≤
    (∑' s : Σ l : ℕ, {s : finset (fin (k + l)) // s.card = l}, ∥p (k + s.1)∥₊ * ∥x∥₊ ^ s.1) * r ^ k,
    from λ k, mul_le_mul_right' (p.nnnorm_change_origin_le k hr') (r ^ k),
  refine nnreal.summable_of_le this _,
  simpa only [← nnreal.tsum_mul_right]
    using (nnreal.summable_sigma.1 (p.change_origin_series_summable_aux₁ hr)).2
end

end

-- From this point on, assume that the space is complete, to make sure that series that converge
-- in norm also converge in `F`.
variables [complete_space F] (p : formal_multilinear_series 𝕜 E F) {x y : E} {r R : ℝ≥0}

lemma has_fpower_series_on_ball_change_origin (k : ℕ) (hr : 0 < p.radius) :
  has_fpower_series_on_ball (λ x, p.change_origin x k) (p.change_origin_series k) 0 p.radius :=
have _ := p.le_change_origin_series_radius k,
((p.change_origin_series k).has_fpower_series_on_ball (hr.trans_le this)).mono hr this

/-- Summing the series `p.change_origin x` at a point `y` gives back `p (x + y)`-/
theorem change_origin_eval (h : (∥x∥₊ + ∥y∥₊ : ℝ≥0∞) < p.radius) :
  (p.change_origin x).sum y = (p.sum (x + y)) :=
begin
  have radius_pos : 0 < p.radius := lt_of_le_of_lt (zero_le _) h,
  have x_mem_ball : x ∈ emetric.ball (0 : E) p.radius,
    from mem_emetric_ball_zero_iff.2 ((le_add_right le_rfl).trans_lt h),
  have y_mem_ball : y ∈ emetric.ball (0 : E) (p.change_origin x).radius,
  { refine mem_emetric_ball_zero_iff.2 (lt_of_lt_of_le _ p.change_origin_radius),
    rwa [lt_tsub_iff_right, add_comm] },
  have x_add_y_mem_ball : x + y ∈ emetric.ball (0 : E) p.radius,
  { refine mem_emetric_ball_zero_iff.2 (lt_of_le_of_lt _ h),
    exact_mod_cast nnnorm_add_le x y },
  set f : (Σ (k l : ℕ), {s : finset (fin (k + l)) // s.card = l}) → F :=
    λ s, p.change_origin_series_term s.1 s.2.1 s.2.2 s.2.2.2 (λ _, x) (λ _, y),
  have hsf : summable f,
  { refine summable_of_nnnorm_bounded _ (p.change_origin_series_summable_aux₁ h) _,
    rintro ⟨k, l, s, hs⟩, dsimp only [subtype.coe_mk],
    exact p.nnnorm_change_origin_series_term_apply_le _ _ _ _ _ _ },
  have hf : has_sum f ((p.change_origin x).sum y),
  { refine has_sum.sigma_of_has_sum ((p.change_origin x).summable y_mem_ball).has_sum (λ k, _) hsf,
    { dsimp only [f],
      refine continuous_multilinear_map.has_sum_eval _ _,
      have := (p.has_fpower_series_on_ball_change_origin k radius_pos).has_sum x_mem_ball,
      rw zero_add at this,
      refine has_sum.sigma_of_has_sum this (λ l, _) _,
      { simp only [change_origin_series, continuous_multilinear_map.sum_apply],
        apply has_sum_fintype },
      { refine summable_of_nnnorm_bounded _ (p.change_origin_series_summable_aux₂
          (mem_emetric_ball_zero_iff.1 x_mem_ball) k) (λ s, _),
        refine (continuous_multilinear_map.le_op_nnnorm _ _).trans_eq _,
        simp } } },
  refine hf.unique (change_origin_index_equiv.symm.has_sum_iff.1 _),
  refine has_sum.sigma_of_has_sum (p.has_sum x_add_y_mem_ball) (λ n, _)
    (change_origin_index_equiv.symm.summable_iff.2 hsf),
  erw [(p n).map_add_univ (λ _, x) (λ _, y)],
  convert has_sum_fintype _,
  ext1 s,
  dsimp only [f, change_origin_series_term, (∘), change_origin_index_equiv_symm_apply_fst,
    change_origin_index_equiv_symm_apply_snd_fst, change_origin_index_equiv_symm_apply_snd_snd_coe],
  rw continuous_multilinear_map.curry_fin_finset_apply_const,
  have : ∀ m (hm : n = m), p n (s.piecewise (λ _, x) (λ _, y)) =
    p m ((s.map (fin.cast hm).to_equiv.to_embedding).piecewise (λ _, x) (λ _, y)),
  { rintro m rfl, simp },
  apply this
end

end formal_multilinear_series

section

variables [complete_space F] {f : E → F} {p : formal_multilinear_series 𝕜 E F} {x y : E}
{r : ℝ≥0∞}

/-- If a function admits a power series expansion `p` on a ball `B (x, r)`, then it also admits a
power series on any subball of this ball (even with a different center), given by `p.change_origin`.
-/
theorem has_fpower_series_on_ball.change_origin
  (hf : has_fpower_series_on_ball f p x r) (h : (∥y∥₊ : ℝ≥0∞) < r) :
  has_fpower_series_on_ball f (p.change_origin y) (x + y) (r - ∥y∥₊) :=
{ r_le := begin
    apply le_trans _ p.change_origin_radius,
    exact tsub_le_tsub hf.r_le le_rfl
  end,
  r_pos := by simp [h],
  has_sum := λ z hz, begin
    convert (p.change_origin y).has_sum _,
    { rw [mem_emetric_ball_zero_iff, lt_tsub_iff_right, add_comm] at hz,
      rw [p.change_origin_eval (hz.trans_le hf.r_le), add_assoc, hf.sum],
      refine mem_emetric_ball_zero_iff.2 (lt_of_le_of_lt _ hz),
      exact_mod_cast nnnorm_add_le y z },
    { refine emetric.ball_subset_ball (le_trans _ p.change_origin_radius) hz,
      exact tsub_le_tsub hf.r_le le_rfl }
  end }

/-- If a function admits a power series expansion `p` on an open ball `B (x, r)`, then
it is analytic at every point of this ball. -/
lemma has_fpower_series_on_ball.analytic_at_of_mem
  (hf : has_fpower_series_on_ball f p x r) (h : y ∈ emetric.ball x r) :
  analytic_at 𝕜 f y :=
begin
  have : (∥y - x∥₊ : ℝ≥0∞) < r, by simpa [edist_eq_coe_nnnorm_sub] using h,
  have := hf.change_origin this,
  rw [add_sub_cancel'_right] at this,
  exact this.analytic_at
end

lemma has_fpower_series_on_ball.analytic_on (hf : has_fpower_series_on_ball f p x r) :
  analytic_on 𝕜 f (emetric.ball x r) :=
λ y hy, hf.analytic_at_of_mem hy

variables (𝕜 f)

/-- For any function `f` from a normed vector space to a Banach space, the set of points `x` such
that `f` is analytic at `x` is open. -/
lemma is_open_analytic_at : is_open {x | analytic_at 𝕜 f x} :=
begin
  rw is_open_iff_mem_nhds,
  rintro x ⟨p, r, hr⟩,
  exact mem_of_superset (emetric.ball_mem_nhds _ hr.r_pos) (λ y hy, hr.analytic_at_of_mem hy)
end

end


/-
Copyright (c) 2020 Heather Macbeth, Patrick Massot. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Heather Macbeth, Patrick Massot

! This file was ported from Lean 3 source module group_theory.archimedean
! leanprover-community/mathlib commit a11f9106a169dd302a285019e5165f8ab32ff433
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.Order.Archimedean
import Mathbin.GroupTheory.Subgroup.Basic

/-!
# Archimedean groups

> THIS FILE IS SYNCHRONIZED WITH MATHLIB4.
> Any changes to this file require a corresponding PR to mathlib4.

This file proves a few facts about ordered groups which satisfy the `archimedean` property, that is:
`class archimedean (α) [ordered_add_comm_monoid α] : Prop :=`
`(arch : ∀ (x : α) {y}, 0 < y → ∃ n : ℕ, x ≤ n • y)`

They are placed here in a separate file (rather than incorporated as a continuation of
`algebra.order.archimedean`) because they rely on some imports from `group_theory` -- bundled
subgroups in particular.

The main result is `add_subgroup.cyclic_of_min`:  a subgroup of a decidable archimedean abelian
group is cyclic, if its set of positive elements has a minimal element.

This result is used in this file to deduce `int.subgroup_cyclic`, proving that every subgroup of `ℤ`
is cyclic.  (There are several other methods one could use to prove this fact, including more purely
algebraic methods, but none seem to exist in mathlib as of writing.  The closest is
`subgroup.is_cyclic`, but that has not been transferred to `add_subgroup`.)

The result is also used in `topology.instances.real` as an ingredient in the classification of
subgroups of `ℝ`.
-/

variable {G : Type _} [LinearOrderedAddCommGroup G] [Archimedean G]

open LinearOrderedAddCommGroup

/- warning: add_subgroup.cyclic_of_min -> AddSubgroup.cyclic_of_min is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_1 : LinearOrderedAddCommGroup.{u1} G] [_inst_2 : Archimedean.{u1} G (OrderedCancelAddCommMonoid.toOrderedAddCommMonoid.{u1} G (OrderedAddCommGroup.toOrderedCancelAddCommMonoid.{u1} G (LinearOrderedAddCommGroup.toOrderedAddCommGroup.{u1} G _inst_1)))] {H : AddSubgroup.{u1} G (AddCommGroup.toAddGroup.{u1} G (OrderedAddCommGroup.toAddCommGroup.{u1} G (LinearOrderedAddCommGroup.toOrderedAddCommGroup.{u1} G _inst_1)))} {a : G}, (IsLeast.{u1} G (PartialOrder.toPreorder.{u1} G (OrderedAddCommGroup.toPartialOrder.{u1} G (LinearOrderedAddCommGroup.toOrderedAddCommGroup.{u1} G _inst_1))) (setOf.{u1} G (fun (g : G) => And (Membership.Mem.{u1, u1} G (AddSubgroup.{u1} G (AddCommGroup.toAddGroup.{u1} G (OrderedAddCommGroup.toAddCommGroup.{u1} G (LinearOrderedAddCommGroup.toOrderedAddCommGroup.{u1} G _inst_1)))) (SetLike.hasMem.{u1, u1} (AddSubgroup.{u1} G (AddCommGroup.toAddGroup.{u1} G (OrderedAddCommGroup.toAddCommGroup.{u1} G (LinearOrderedAddCommGroup.toOrderedAddCommGroup.{u1} G _inst_1)))) G (AddSubgroup.setLike.{u1} G (AddCommGroup.toAddGroup.{u1} G (OrderedAddCommGroup.toAddCommGroup.{u1} G (LinearOrderedAddCommGroup.toOrderedAddCommGroup.{u1} G _inst_1))))) g H) (LT.lt.{u1} G (Preorder.toLT.{u1} G (PartialOrder.toPreorder.{u1} G (OrderedAddCommGroup.toPartialOrder.{u1} G (LinearOrderedAddCommGroup.toOrderedAddCommGroup.{u1} G _inst_1)))) (OfNat.ofNat.{u1} G 0 (OfNat.mk.{u1} G 0 (Zero.zero.{u1} G (AddZeroClass.toHasZero.{u1} G (AddMonoid.toAddZeroClass.{u1} G (SubNegMonoid.toAddMonoid.{u1} G (AddGroup.toSubNegMonoid.{u1} G (AddCommGroup.toAddGroup.{u1} G (OrderedAddCommGroup.toAddCommGroup.{u1} G (LinearOrderedAddCommGroup.toOrderedAddCommGroup.{u1} G _inst_1)))))))))) g))) a) -> (Eq.{succ u1} (AddSubgroup.{u1} G (AddCommGroup.toAddGroup.{u1} G (OrderedAddCommGroup.toAddCommGroup.{u1} G (LinearOrderedAddCommGroup.toOrderedAddCommGroup.{u1} G _inst_1)))) H (AddSubgroup.closure.{u1} G (AddCommGroup.toAddGroup.{u1} G (OrderedAddCommGroup.toAddCommGroup.{u1} G (LinearOrderedAddCommGroup.toOrderedAddCommGroup.{u1} G _inst_1))) (Singleton.singleton.{u1, u1} G (Set.{u1} G) (Set.hasSingleton.{u1} G) a)))
but is expected to have type
  forall {G : Type.{u1}} [_inst_1 : LinearOrderedAddCommGroup.{u1} G] [_inst_2 : Archimedean.{u1} G (LinearOrderedAddCommMonoid.toOrderedAddCommMonoid.{u1} G (LinearOrderedCancelAddCommMonoid.toLinearOrderedAddCommMonoid.{u1} G (LinearOrderedAddCommGroup.toLinearOrderedAddCancelCommMonoid.{u1} G _inst_1)))] {H : AddSubgroup.{u1} G (AddCommGroup.toAddGroup.{u1} G (OrderedAddCommGroup.toAddCommGroup.{u1} G (LinearOrderedAddCommGroup.toOrderedAddCommGroup.{u1} G _inst_1)))} {a : G}, (IsLeast.{u1} G (PartialOrder.toPreorder.{u1} G (OrderedAddCommGroup.toPartialOrder.{u1} G (LinearOrderedAddCommGroup.toOrderedAddCommGroup.{u1} G _inst_1))) (setOf.{u1} G (fun (g : G) => And (Membership.mem.{u1, u1} G (AddSubgroup.{u1} G (AddCommGroup.toAddGroup.{u1} G (OrderedAddCommGroup.toAddCommGroup.{u1} G (LinearOrderedAddCommGroup.toOrderedAddCommGroup.{u1} G _inst_1)))) (SetLike.instMembership.{u1, u1} (AddSubgroup.{u1} G (AddCommGroup.toAddGroup.{u1} G (OrderedAddCommGroup.toAddCommGroup.{u1} G (LinearOrderedAddCommGroup.toOrderedAddCommGroup.{u1} G _inst_1)))) G (AddSubgroup.instSetLikeAddSubgroup.{u1} G (AddCommGroup.toAddGroup.{u1} G (OrderedAddCommGroup.toAddCommGroup.{u1} G (LinearOrderedAddCommGroup.toOrderedAddCommGroup.{u1} G _inst_1))))) g H) (LT.lt.{u1} G (Preorder.toLT.{u1} G (PartialOrder.toPreorder.{u1} G (OrderedAddCommGroup.toPartialOrder.{u1} G (LinearOrderedAddCommGroup.toOrderedAddCommGroup.{u1} G _inst_1)))) (OfNat.ofNat.{u1} G 0 (Zero.toOfNat0.{u1} G (NegZeroClass.toZero.{u1} G (SubNegZeroMonoid.toNegZeroClass.{u1} G (SubtractionMonoid.toSubNegZeroMonoid.{u1} G (SubtractionCommMonoid.toSubtractionMonoid.{u1} G (AddCommGroup.toDivisionAddCommMonoid.{u1} G (OrderedAddCommGroup.toAddCommGroup.{u1} G (LinearOrderedAddCommGroup.toOrderedAddCommGroup.{u1} G _inst_1))))))))) g))) a) -> (Eq.{succ u1} (AddSubgroup.{u1} G (AddCommGroup.toAddGroup.{u1} G (OrderedAddCommGroup.toAddCommGroup.{u1} G (LinearOrderedAddCommGroup.toOrderedAddCommGroup.{u1} G _inst_1)))) H (AddSubgroup.closure.{u1} G (AddCommGroup.toAddGroup.{u1} G (OrderedAddCommGroup.toAddCommGroup.{u1} G (LinearOrderedAddCommGroup.toOrderedAddCommGroup.{u1} G _inst_1))) (Singleton.singleton.{u1, u1} G (Set.{u1} G) (Set.instSingletonSet.{u1} G) a)))
Case conversion may be inaccurate. Consider using '#align add_subgroup.cyclic_of_min AddSubgroup.cyclic_of_minₓ'. -/
/-- Given a subgroup `H` of a decidable linearly ordered archimedean abelian group `G`, if there
exists a minimal element `a` of `H ∩ G_{>0}` then `H` is generated by `a`. -/
theorem AddSubgroup.cyclic_of_min {H : AddSubgroup G} {a : G}
    (ha : IsLeast { g : G | g ∈ H ∧ 0 < g } a) : H = AddSubgroup.closure {a} :=
  by
  obtain ⟨⟨a_in, a_pos⟩, a_min⟩ := ha
  refine' le_antisymm _ (H.closure_le.mpr <| by simp [a_in])
  intro g g_in
  obtain ⟨k, ⟨nonneg, lt⟩, _⟩ : ∃! k, 0 ≤ g - k • a ∧ g - k • a < a :=
    existsUnique_zsmul_near_of_pos' a_pos g
  have h_zero : g - k • a = 0 := by
    by_contra h
    have h : a ≤ g - k • a := by
      refine' a_min ⟨_, _⟩
      · exact AddSubgroup.sub_mem H g_in (AddSubgroup.zsmul_mem H a_in k)
      · exact lt_of_le_of_ne nonneg (Ne.symm h)
    have h' : ¬a ≤ g - k • a := not_le.mpr lt
    contradiction
  simp [sub_eq_zero.mp h_zero, AddSubgroup.mem_closure_singleton]
#align add_subgroup.cyclic_of_min AddSubgroup.cyclic_of_min

/- warning: int.subgroup_cyclic -> Int.subgroup_cyclic is a dubious translation:
lean 3 declaration is
  forall (H : AddSubgroup.{0} Int Int.addGroup), Exists.{1} Int (fun (a : Int) => Eq.{1} (AddSubgroup.{0} Int Int.addGroup) H (AddSubgroup.closure.{0} Int Int.addGroup (Singleton.singleton.{0, 0} Int (Set.{0} Int) (Set.hasSingleton.{0} Int) a)))
but is expected to have type
  forall (H : AddSubgroup.{0} Int Int.instAddGroupInt), Exists.{1} Int (fun (a : Int) => Eq.{1} (AddSubgroup.{0} Int Int.instAddGroupInt) H (AddSubgroup.closure.{0} Int Int.instAddGroupInt (Singleton.singleton.{0, 0} Int (Set.{0} Int) (Set.instSingletonSet.{0} Int) a)))
Case conversion may be inaccurate. Consider using '#align int.subgroup_cyclic Int.subgroup_cyclicₓ'. -/
/-- Every subgroup of `ℤ` is cyclic. -/
theorem Int.subgroup_cyclic (H : AddSubgroup ℤ) : ∃ a, H = AddSubgroup.closure {a} :=
  by
  cases' AddSubgroup.bot_or_exists_ne_zero H with h h
  · use 0
    rw [h]
    exact add_subgroup.closure_singleton_zero.symm
  let s := { g : ℤ | g ∈ H ∧ 0 < g }
  have h_bdd : ∀ g ∈ s, (0 : ℤ) ≤ g := fun _ h => le_of_lt h.2
  obtain ⟨g₀, g₀_in, g₀_ne⟩ := h
  obtain ⟨g₁, g₁_in, g₁_pos⟩ : ∃ g₁ : ℤ, g₁ ∈ H ∧ 0 < g₁ :=
    by
    cases' lt_or_gt_of_ne g₀_ne with Hg₀ Hg₀
    · exact ⟨-g₀, H.neg_mem g₀_in, neg_pos.mpr Hg₀⟩
    · exact ⟨g₀, g₀_in, Hg₀⟩
  obtain ⟨a, ha, ha'⟩ := Int.exists_least_of_bdd ⟨(0 : ℤ), h_bdd⟩ ⟨g₁, g₁_in, g₁_pos⟩
  exact ⟨a, AddSubgroup.cyclic_of_min ⟨ha, ha'⟩⟩
#align int.subgroup_cyclic Int.subgroup_cyclic


/-
Copyright (c) 2021 Johan Commelin. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johan Commelin

! This file was ported from Lean 3 source module category_theory.preadditive.functor_category
! leanprover-community/mathlib commit 829895f162a1f29d0133f4b3538f4cd1fb5bffd3
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.CategoryTheory.Preadditive.Basic

/-!
# Preadditive structure on functor categories

If `C` and `D` are categories and `D` is preadditive,
then `C ⥤ D` is also preadditive.

-/

open BigOperators

namespace CategoryTheory

open CategoryTheory.Limits Preadditive

variable {C D : Type _} [Category C] [Category D] [Preadditive D]

instance functorCategoryPreadditive : Preadditive (C ⥤ D)
    where
  homGroup F G :=
    { add := fun α β => { app := fun X => α.app X + β.app X }
      zero := { app := fun X => 0 }
      neg := fun α => { app := fun X => -α.app X }
      sub := fun α β => { app := fun X => α.app X - β.app X }
      add_assoc := by
        intros
        ext
        apply add_assoc
      zero_add := by
        intros
        ext
        apply zero_add
      add_zero := by
        intros
        ext
        apply add_zero
      add_comm := by
        intros
        ext
        apply add_comm
      sub_eq_add_neg := by
        intros
        ext
        apply sub_eq_add_neg
      add_left_neg := by
        intros
        ext
        apply add_left_neg }
  add_comp := by
    intros
    ext
    apply add_comp
  comp_add := by
    intros
    ext
    apply comp_add
#align category_theory.functor_category_preadditive CategoryTheory.functorCategoryPreadditive

namespace NatTrans

variable {F G : C ⥤ D}

/-- Application of a natural transformation at a fixed object,
as group homomorphism -/
@[simps]
def appHom (X : C) : (F ⟶ G) →+ (F.obj X ⟶ G.obj X)
    where
  toFun α := α.app X
  map_zero' := rfl
  map_add' _ _ := rfl
#align category_theory.nat_trans.app_hom CategoryTheory.NatTrans.appHom

@[simp]
theorem app_zero (X : C) : (0 : F ⟶ G).app X = 0 :=
  rfl
#align category_theory.nat_trans.app_zero CategoryTheory.NatTrans.app_zero

@[simp]
theorem app_add (X : C) (α β : F ⟶ G) : (α + β).app X = α.app X + β.app X :=
  rfl
#align category_theory.nat_trans.app_add CategoryTheory.NatTrans.app_add

@[simp]
theorem app_sub (X : C) (α β : F ⟶ G) : (α - β).app X = α.app X - β.app X :=
  rfl
#align category_theory.nat_trans.app_sub CategoryTheory.NatTrans.app_sub

@[simp]
theorem app_neg (X : C) (α : F ⟶ G) : (-α).app X = -α.app X :=
  rfl
#align category_theory.nat_trans.app_neg CategoryTheory.NatTrans.app_neg

@[simp]
theorem app_nsmul (X : C) (α : F ⟶ G) (n : ℕ) : (n • α).app X = n • α.app X :=
  (appHom X).map_nsmul α n
#align category_theory.nat_trans.app_nsmul CategoryTheory.NatTrans.app_nsmul

@[simp]
theorem app_zsmul (X : C) (α : F ⟶ G) (n : ℤ) : (n • α).app X = n • α.app X :=
  (appHom X : (F ⟶ G) →+ (F.obj X ⟶ G.obj X)).map_zsmul α n
#align category_theory.nat_trans.app_zsmul CategoryTheory.NatTrans.app_zsmul

@[simp]
theorem app_sum {ι : Type _} (s : Finset ι) (X : C) (α : ι → (F ⟶ G)) :
    (∑ i in s, α i).app X = ∑ i in s, (α i).app X := by
  simp only [← appHom_apply, map_sum]
#align category_theory.nat_trans.app_sum CategoryTheory.NatTrans.app_sum

end NatTrans

end CategoryTheory


import linear_algebra.matrix.block linear_algebra.matrix.nonsingular_inverse
  missing.linear_algebra.matrix.nonsingular_inverse
  missing.data.matrix.block
  missing.data.fintype.basic

namespace matrix
open_locale matrix
open finset
universes v
variables {α β m n o : Type*} {m' n' : α → Type*}
variables {R : Type v} [comm_ring R] {M : matrix m m R} {b : m → α}

section has_lt
variable [has_lt α]

lemma block_triangular_zero : block_triangular (0 : matrix m m R) b := λ i j h, rfl

protected lemma block_triangular.neg (hM : block_triangular M b) :
  block_triangular (- M) b :=
λ i j h, show - M i j = 0, by rw [hM h, neg_zero]

lemma block_triangular.add {M N : matrix m m R}
    (hM : block_triangular M b) (hN : block_triangular N b) :
  block_triangular (M + N) b :=
λ i j h, show M i j + N i j = 0, by rw [hM h, hN h, zero_add]

lemma block_triangular.sub {M N : matrix m m R}
    (hM : block_triangular M b) (hN : block_triangular N b) :
  block_triangular (M - N) b :=
λ i j h, show M i j - N i j = 0, by rw [hM h, hN h, zero_sub_zero]

end has_lt

section preorder
variables [preorder α]

lemma block_triangular_diagonal [decidable_eq m] (d : m → R) :
  block_triangular (diagonal d) b :=
λ i j h, diagonal_apply_ne' d (λ h', ne_of_lt h (congr_arg _ h'))

lemma block_triangular_block_diagonal' [decidable_eq α] (d : Π (i : α), matrix (m' i) (m' i) R) :
  block_triangular (block_diagonal' d) sigma.fst :=
begin
  rintros ⟨i, i'⟩ ⟨j, j'⟩ h,
  apply block_diagonal'_apply_ne d i' j' (λ h', ne_of_lt h h'.symm),
end

lemma block_triangular_block_diagonal [decidable_eq α] (d : α → matrix m m R) :
  block_triangular (block_diagonal d) prod.snd :=
begin
  rintros ⟨i, i'⟩ ⟨j, j'⟩ h,
  rw [block_diagonal'_eq_block_diagonal, block_triangular_block_diagonal'],
  exact h
end

end preorder

section linear_order
variables [linear_order α]

lemma block_triangular.mul [fintype m]
  {M N : matrix m m R} (hM : block_triangular M b) (hN : block_triangular N b):
  block_triangular (M * N) b :=
begin
  intros i j hij,
  apply finset.sum_eq_zero,
  intros k hk,
  by_cases hki : b k < b i,
  { simp_rw [hM hki, zero_mul], },
  { simp_rw [hN (lt_of_lt_of_le hij (le_of_not_lt hki)), mul_zero] },
end

end linear_order

/-! ### Invertible -/
variables [decidable_eq m] [fintype m] [decidable_eq n] [fintype n]

lemma to_block_inverse_mul_to_block_eq_one_of_block_triangular [linear_order α]
  [invertible M] (hM : block_triangular M b) (k : α) :
  M⁻¹.to_block (λ i, b i < k) (λ i, b i < k) ⬝ M.to_block (λ i, b i < k) (λ i, b i < k) = 1 :=
begin
  let p := (λ i, b i < k),
  have h_sum : M⁻¹.to_block p p ⬝ M.to_block p p +
      M⁻¹.to_block p (λ i, ¬ p i) ⬝ M.to_block (λ i, ¬ p i) p = 1,
    by rw [←to_block_mul_eq_add, inv_mul_of_invertible M, to_block_one_self],
  have h_zero : M.to_block (λ i, ¬ p i) p = 0,
  { ext i j,
    simpa using hM (lt_of_lt_of_le j.2 (le_of_not_lt i.2)) },
  simpa [h_zero] using h_sum
end

/-- The inverse of an upper-left subblock of a block-triangular matrix `M` is the upper-left
subblock of `M⁻¹`. -/
lemma inv_to_block_of_block_triangular [linear_order α]
  [invertible M] (hM : block_triangular M b) (k : α) :
  (M.to_block (λ i, b i < k) (λ i, b i < k))⁻¹ = M⁻¹.to_block (λ i, b i < k) (λ i, b i < k) :=
inv_eq_left_inv (to_block_inverse_mul_to_block_eq_one_of_block_triangular hM k)

/-- An upper-left subblock of an invertible block-triangular matrix is invertible. -/
def invertible_to_block_of_block_triangular
  [linear_order α] [invertible M] (hM : block_triangular M b) (k : α) :
  invertible (M.to_block (λ i, b i < k) (λ i, b i < k)) :=
invertible_of_left_inverse _ ((⅟M).to_block (λ i, b i < k) (λ i, b i < k))
  (by simpa only [inv_of_eq_nonsing_inv]
    using to_block_inverse_mul_to_block_eq_one_of_block_triangular hM k)

/-- A lower-left subblock of the inverse of a block-triangular matrix is zero. This is a first step
towards `block_triangular_inv_of_block_triangular` below. -/
lemma to_block_inverse_eq_zero [linear_order α]
  [invertible M] (hM : block_triangular M b) (k : α) :
  M⁻¹.to_block (λ i, k ≤ b i) (λ i, b i < k) = 0 :=
begin
  have := mul_inv_of_invertible M,
  have h_iff : (λ i, k ≤ b i) = (λ i, ¬ b i < k),
  { ext i, simp },
  let p := (λ i, b i < k),
  let q := (λ i, ¬ b i < k),
  have h_sum : M⁻¹.to_block q p ⬝ M.to_block p p +
      M⁻¹.to_block q q ⬝ M.to_block q p = 0,
  { rw [←to_block_mul_eq_add, inv_mul_of_invertible M, to_block_one_disjoint],
    rintros i ⟨hq, hp⟩, exact hq hp },
  have h_zero : M.to_block q p = 0,
  { ext i j,
    simpa using hM (lt_of_lt_of_le j.2 (le_of_not_lt i.2)) },
  have h_mul_eq_zero : M⁻¹.to_block q p ⬝ M.to_block p p = 0,
    by simpa [h_zero] using h_sum,
  haveI : invertible (M.to_block p p) := invertible_to_block_of_block_triangular hM k,
  rw [h_iff, ← matrix.zero_mul (M.to_block p p)⁻¹, ← h_mul_eq_zero,
    mul_inv_cancel_right_of_invertible],
end

/-- The inverse of a block-triangular matrix is block-triangular. -/
lemma block_triangular_inv_of_block_triangular
    [invertible M] [linear_order α] (hM : block_triangular M b) :
  block_triangular M⁻¹ b :=
begin
  unfreezingI { induction hs : univ.image b using finset.strong_induction
    with s ih generalizing m },
  subst hs,
  by_cases h : univ.image b = ∅,
  { intros i j,
    rw [image_eq_empty, univ_eq_empty_iff] at h,
    exact false.elim (@is_empty.false _ h i) },
  { let k := (univ.image b).max' (nonempty_of_ne_empty h),
    let b' := λ i : {a // b a < k}, b ↑i,
    let A := M.to_block (λ i, b i < k) (λ j, b j < k),
    let B := M.to_block (λ i, b i < k) (λ j, b j ≤ k),
    let C := M.to_block (λ i, b i ≤ k) (λ j, b j < k),
    let D := M.to_block (λ i, b i ≤ k) (λ j, b j ≤ k),
    show M⁻¹.block_triangular b,
    { intros i j hij,
      by_cases hbi : b i = k,
      { have hi : k ≤ b i := le_of_eq hbi.symm,
        have hj : b j < k := hbi ▸ hij,
        have : M⁻¹.to_block (λ (i : m), k ≤ b i) (λ (i : m), b i < k) ⟨i, hi⟩ ⟨j, hj⟩ = 0 :=
          by simp only [to_block_inverse_eq_zero hM k, pi.zero_apply],
        simp [this.symm] },
      { haveI : invertible A,
        { apply invertible_to_block_of_block_triangular hM },
        have hA : A.block_triangular b',
        { intros i j, apply hM },
        have hb' : image b' univ ⊂ (image b univ),
        { convert image_subtype_univ_ssubset_image_univ k b _ (λ a, a < k) (lt_irrefl _),
          convert max'_mem _ _, },
        have hA : A⁻¹.block_triangular b',
          from ih (image b' univ) hb' hA rfl,
        have hi : b i < k,
          from lt_of_le_of_ne (le_max' (univ.image b) (b i) (mem_image_of_mem _ (mem_univ _))) hbi,
        have hj : b j < k, from lt_trans hij hi,
        have hij' : b' ⟨j, hj⟩ < b' ⟨i, hi⟩, by simp_rw [b', subtype.coe_mk, hij],
        have hA := hA hij',
        have h_A_inv: A⁻¹ = M⁻¹.to_block (λ (i : m), b i < k) (λ (i : m), b i < k),
        { simp_rw [A],
          exact inv_to_block_of_block_triangular hM k },
        rw h_A_inv at hA,
        simp [hA.symm] } } }
end

lemma to_square_block_inv_mul_to_square_block_eq_one [linear_order α]
  [invertible M] (hM : block_triangular M b) (k : α) :
  M⁻¹.to_square_block b k ⬝ M.to_square_block b k = 1 :=
begin
  let p := (λ i, b i = k),
  have h_sum : M⁻¹.to_block p p ⬝ M.to_block p p +
      M⁻¹.to_block p (λ i, ¬ p i) ⬝ M.to_block (λ i, ¬ p i) p = 1,
    by rw [←to_block_mul_eq_add, inv_mul_of_invertible M, to_block_one_self],
  have h_zero :
    ∀ i j l, M⁻¹.to_block p (λ (i : m), ¬p i) i j * M.to_block (λ (i : m), ¬p i) p j l = 0,
  { intros i j l,
    by_cases hj : b j.1 ≤ k,
    { have hj := lt_of_le_of_ne hj j.2,
      have hM' := block_triangular_inv_of_block_triangular hM,
      apply mul_eq_zero_of_left,
      simpa using hM' (lt_of_lt_of_eq hj i.2.symm) },
    { have hj := lt_of_not_ge hj,
      apply mul_eq_zero_of_right,
      simpa using hM (lt_of_eq_of_lt l.2 hj) }},
  have h_zero' : M⁻¹.to_block p (λ (i : m), ¬p i) ⬝ M.to_block (λ (i : m), ¬p i) p = 0,
  { ext i l,
    apply sum_eq_zero (λ j hj, h_zero i j l) },
  simpa [h_zero'] using h_sum
end

end matrix


import .subgroup .normal_embedding .simple_group .trivial_class .quotient_group .fingroup

universe u

open category_theory quotient_group normal_embedding monoid_hom subgroup (hiding subtype)

inductive normal_series : Group.{u} → Type (u+1)
| trivial {G : Group} (hG : subsingleton G) : normal_series G
| cons (H G : Group)
  (f : normal_embedding H G) (s : normal_series H) : normal_series G

inductive add_normal_series : AddGroup.{u} → Type (u+1)
| trivial {G : AddGroup} (hG : subsingleton G) : add_normal_series G
| cons (H G : AddGroup)
  (f : add_normal_embedding H G) (s : add_normal_series H) : add_normal_series G

attribute [to_additive add_normal_series] normal_series

namespace normal_series

variables {G H K N : Group.{u}}

/-- If `G ≃* H` then we can build a normal series for `H` from a normal series for `G` by undoing
the last step and going into `H` instead. -/
def of_mul_equiv (e : G ≃* H) : normal_series G → normal_series H
| (trivial hG) := trivial $ @equiv.subsingleton.symm _ _ e.to_equiv hG
| (cons K G f s) := cons K H (normal_embedding.comp_mul_equiv f e) s

/-- If `G` is not trivial, it must have a nontrivial normal series. -/
@[to_additive]
lemma exists_cons_of_not_subsingleton (h : ¬ subsingleton G) :
  Π (σ : normal_series G), ∃ H f s, σ = cons H G f s
| (trivial hG) := absurd hG h
| (cons H G f s) := ⟨H, f, s, rfl⟩

/-- The factors of a normal series are the quotient groups of consecutive elements in the series. -/
def factors : Π {G : Group}, normal_series G → multiset (isomorphism_classes.obj $ Cat.of Group)
| _ (trivial _) := 0
| _ (cons H G f s) := quotient.mk' (Group.of $ quotient f.φ.range) ::ₘ factors s

end normal_series

namespace add_normal_series

variables {G H K N : AddGroup.{u}}

/-- If `G ≃+ H` then we can build a normal series for `H` from a normal series for `G` by undoing
the last step and going into `H` instead. -/
def of_add_equiv (e : G ≃+ H) : add_normal_series G → add_normal_series H
| (trivial hG) := trivial $ @equiv.subsingleton.symm _ _ e.to_equiv hG
| (cons K G f s) := cons K H (add_normal_embedding.comp_add_equiv f e) s

attribute [to_additive] normal_series.of_mul_equiv

open quotient_add_group

/-- The factors of a normal series are the quotient groups of consecutive elements in the series. -/
def factors : Π {G : AddGroup}, add_normal_series G → multiset (isomorphism_classes.obj $ Cat.of AddGroup)
| _ (trivial _) := 0
| _ (cons H G f s) := quotient.mk' (AddGroup.of $ quotient f.φ.range) ::ₘ factors s

attribute [to_additive] normal_series.factors

end add_normal_series

variables {G H K N : Group.{u}}

namespace normal_series

@[simp, to_additive]
lemma factors_trivial {hG : subsingleton G} : (trivial hG).factors = 0 := rfl

@[simp, to_additive]
lemma factors_cons {f : normal_embedding H G} {s : normal_series H} :
  (cons H G f s).factors = quotient.mk' (Group.of $ quotient f.φ.range) ::ₘ factors s :=
rfl

@[simp, to_additive]
lemma factors_of_mul_equiv (e : G ≃* H) :
  Π (σ : normal_series G), (of_mul_equiv e σ).factors = σ.factors
| (trivial hG) := by simp [of_mul_equiv]
| (cons K G f s) := begin
  simp only [of_mul_equiv, multiset.cons_inj_left, factors_cons],
  exact class_eq (equiv_quotient_comp_mul_equiv _ _),
end

end normal_series

/-- A composition series is a normal series with simple and nontrivial factors. -/
@[to_additive add_composition_series]
def composition_series (G : Group.{u}) : Type (u+1) :=
{ σ : normal_series G // ∀ G' ∈ σ.factors, is_simple_class G' ∧ ¬ is_trivial_class G' }

namespace add_composition_series
-- The namespace needs to exists so that to_additive translations can populate it.
end add_composition_series

namespace composition_series

open normal_series multiset fingroup

/-- Construct a composition series for `G` from a composition series for `H`, a normal embedding
of `H` into `G` and a proof that the new factor introduced is simple and nontrivial. -/
@[to_additive]
def cons' (σ : composition_series H) (f : normal_embedding H G)
  (h : is_simple (quotient f.φ.range) ∧ ¬ subsingleton (quotient f.φ.range)) :
  composition_series G :=
⟨cons _ _ f σ.val, λ G' hG',
  by { simp at hG', cases hG', { simpa [hG'] }, exact σ.prop _ hG' }⟩

@[simp, to_additive]
def factors_of_cons' {f : normal_embedding H G} {σ : composition_series H}
  {h : is_simple (quotient f.φ.range) ∧ ¬ subsingleton (quotient f.φ.range)} :
  (cons' σ f h).val.factors = quotient.mk' (Group.of $ quotient f.φ.range) ::ₘ σ.val.factors :=
by simp [cons']

/-- A trivial normal series is always a composition series. -/
@[to_additive]
def of_subsingleton (h : subsingleton G) : composition_series G :=
⟨trivial h, λ G' hG', absurd hG' $ multiset.not_mem_zero _⟩

/-- Given a composition series whose underlying normal series is constructed with `cons`, we can
produce a composition series from the normal series used in `cons`. -/
@[to_additive]
def of_cons {σ : composition_series G} {f : normal_embedding H G} {s : normal_series H} :
  σ.val = cons H G f s → composition_series H :=
λ h, ⟨s, λ G' hG', σ.prop G' (show G' ∈ σ.val.factors, by { simp [h, hG'] })⟩

@[simp, to_additive]
lemma factors_of_cons {σ : composition_series G} {f : normal_embedding H G} {s : normal_series H}
  (h : σ.val = cons H G f s) : (of_cons h).val.factors = s.factors := rfl

@[to_additive]
def of_mul_equiv (e : G ≃* H) : composition_series G → composition_series H :=
λ σ, ⟨normal_series.of_mul_equiv e σ.val, λ G' hG', σ.prop G' (by simpa using hG')⟩

@[simp, to_additive]
lemma factors_of_mul_equiv (e : G ≃* H) (σ : composition_series G) :
  (of_mul_equiv e σ).val.factors = σ.val.factors :=
normal_series.factors_of_mul_equiv e σ.val

/-- Any composition series for the trivial group has no factors, i.e., it is a trivial series. -/
@[to_additive]
lemma factors_of_subsingleton (hG : subsingleton G) :
  Π (s : composition_series G), s.val.factors = 0
| ⟨trivial _, _⟩ := rfl
| ⟨cons H G f s, h⟩ := by refine absurd hG (h (quotient.mk' G) _).2;
{ rw [factors_cons, mem_cons], left,
  refine class_eq' (mul_equiv.of_subsingleton hG $ @quotient.subsingleton _ hG _) }

/-- The unique composition series for a simple group. -/
@[to_additive]
def of_simple (h1 : is_simple G) (h2 : ¬ subsingleton G) :
  composition_series G :=
⟨cons (Group.of punit) G (from_subsingleton punit.subsingleton G) (trivial punit.subsingleton),
λ H hH, begin
  simp only [factors, multiset.mem_singleton] at hH,
  have : quotient (from_subsingleton punit.subsingleton ↥G).φ.range ≃* G :=
    mul_equiv.trans (equiv_quotient_of_eq from_subsingleton_range) quotient_bot,
  rw class_eq this at hH,
  simp only [hH, quotient.lift_on'_mk', is_simple_class, is_simple_coe_Group,
    is_trivial_class_mk', Group.coe_of], use [h1, h2],
end⟩

/-- Any composition series of a simple group `G` has `⟦G⟧` as its only composition factor. -/
@[to_additive]
lemma factors_of_simple (h1 : is_simple G) (h2 : ¬ subsingleton G) :
  Π (s : composition_series G), s.val.factors = quotient.mk' G ::ₘ 0
| ⟨trivial hG, h⟩ := absurd hG h2
| ⟨cons H G f s, h⟩ := begin
  simp, congr' 1,
  { apply class_eq',
    suffices : ∃ (h : quotient f.φ.range ≃* G), true, -- trick so we can use cases
      from classical.some this,
    cases h1 f.φ.range f.norm with h1 h1,
    { use [(equiv_quotient_of_eq h1).trans quotient_bot, trivial] },
    exfalso, apply (h (quotient.mk' $ Group.of (quotient f.φ.range)) (by simp)).2,
    simp, exact subsingleton_quotient_iff.mpr h1 },
  apply factors_of_subsingleton _ ⟨s, λ G' hG', h G' _⟩, swap,
  { rw [factors_cons, mem_cons], right, assumption },
  have h' := h (quotient.mk' $ Group.of (quotient_group.quotient f.φ.range)) _, swap,
  { rw [factors_cons, mem_cons], left, refl },
  cases h1 f.φ.range f.norm with h1 h1,
  { haveI := subsingleton_subgroup_iff.mpr h1, exact f.equiv_range.to_equiv.subsingleton },
  exfalso, apply h'.2, simp [h1, subsingleton_quotient_iff],
end

@[to_additive]
def cons_of_maximal_normal_subgroup {N : subgroup G} (h : N.maximal_normal_subgroup)
  (σ : composition_series (Group.of N)) : composition_series G :=
begin
  haveI := h.1, apply cons' σ (of_normal_subgroup N),
  have h' := (maximal_normal_subgroup_iff N).mp h, split,
  { rw is_simple_quotient_eq (range_of_normal_subgroup N), exact h'.1 },
  { rw range_of_normal_subgroup, exact h'.2 }
end

/-- The range of a normal embedding used in a composition series is a maximal normal subgroup. -/
@[to_additive]
lemma maximal_normal_subgroup_of_cons {σ : composition_series G}
  {f : normal_embedding H G} {s : normal_series H} :
  σ.val = cons H G f s → maximal_normal_subgroup f.φ.range :=
λ h, (maximal_normal_subgroup_iff _).mpr
  (by simpa using
    σ.prop (quotient.mk' $ Group.of (quotient f.φ.range)) (by simp [←subtype.val_eq_coe, h]))

local attribute [instance] classical.prop_decidable
variables [hG : fintype G]
include hG

/-- The cardinality of succesive terms in the composition series is strictly descreasing. -/
@[to_additive]
lemma card_lt_of_cons {σ : composition_series G} {f : normal_embedding H G} {s : normal_series H} :
  σ.val = cons H G f s → @fintype.card H f.fintype < fintype.card G :=
λ h, @fintype.of_equiv_card _ _ f.fintype f.equiv_range.to_equiv ▸
  by convert card_lt (maximal_normal_subgroup_of_cons h).2.1

open fintype

variable (G)
/-- Every finite group has a composition series. -/
@[to_additive]
theorem exists_composition_series_of_finite :
  nonempty (composition_series G) :=
strong_rec_on_card' G begin
  clear hG G, intro G, introI, intro ih,
  by_cases h : subsingleton G,
  { use of_subsingleton h },
  rcases exists_maximal_normal_subgroup h with ⟨N, hN⟩, haveI := hN.1,
  apply (ih (Group.of N) (card_lt hN.2.1)).elim,
  intro σ, use cons_of_maximal_normal_subgroup hN σ,
end

variable {G}
/-- Jordan-Hölder: Any two composition series for `G` have the same factors. -/
@[to_additive]
theorem eq_factors : Π (σ τ : composition_series G),
  σ.val.factors = τ.val.factors :=
strong_rec_on_card' G begin
  clear hG G, intros G, introI, intro ih, intros σ τ,
  by_cases hG' : subsingleton G, { simp only [factors_of_subsingleton hG'] },
  rcases exists_cons_of_not_subsingleton hG' σ.val with ⟨H, f, s, hs⟩,
  rcases exists_cons_of_not_subsingleton hG' τ.val with ⟨K, g, t, ht⟩,
  simp [hs, ht],
  by_cases f.φ.range = g.φ.range,
  { congr' 1, { exact class_eq (equiv_quotient_of_eq h) },
    have : H ≃* K := f.equiv_range.trans ((mul_equiv.subgroup_congr h).trans g.equiv_range.symm),
    rw [←factors_of_cons hs, ←factors_of_cons ht, ←factors_of_mul_equiv this.symm],
    apply @ih H f.fintype (card_lt_of_cons hs) },
  have htop := sup_maximal_normal_subgroup (maximal_normal_subgroup_of_cons hs)
    (maximal_normal_subgroup_of_cons ht) h,
  apply (exists_composition_series_of_finite (Group.of ↥(f.φ.range ⊓ g.φ.range))).elim, intro ρ,
  have := (maximal_normal_subgroup_iff _).mp (maximal_normal_subgroup_of_cons ht),
  let s' : composition_series H := cons' ρ (from_inf_range_left f g)
    ⟨(mul_equiv_is_simple_iff $ quotient_from_inf_range_left f g htop).mpr this.1,
    λ h, this.2 $ (quotient_from_inf_range_left f g htop).to_equiv.subsingleton_iff.mp h⟩,
  have := (maximal_normal_subgroup_iff _).mp (maximal_normal_subgroup_of_cons hs),
  let t' : composition_series K := cons' ρ (from_inf_range_right f g)
    ⟨(mul_equiv_is_simple_iff $ quotient_from_inf_range_right f g htop).mpr this.1,
    λ h, this.2 $ (quotient_from_inf_range_right f g htop).to_equiv.subsingleton_iff.mp h⟩,
  have hs' := @ih H f.fintype (card_lt_of_cons hs) s' (of_cons hs),
  have ht' := @ih K g.fintype (card_lt_of_cons ht) t' (of_cons ht),
  rw [factors_of_cons hs, factors_of_cons'] at hs', rw ←hs',
  rw [factors_of_cons ht, factors_of_cons'] at ht', rw ←ht',
  conv_rhs { rw multiset.cons_swap }, congr' 1,
  { exact class_eq (quotient_from_inf_range_right f g htop).symm },
  congr' 1, exact class_eq (quotient_from_inf_range_left f g htop),
end

end composition_series