/-
Copyright (c) 2017 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl
-/
import topology.instances.nnreal
import order.liminf_limsup
import topology.metric_space.lipschitz
/-!
# Extended non-negative reals
-/

noncomputable theory

open classical set filter metric
open_locale classical topological_space ennreal nnreal big_operators filter

variables {α : Type*} {β : Type*} {γ : Type*}

namespace ennreal
variables {a b c d : ℝ≥0∞} {r p q : ℝ≥0}
variables {x y z : ℝ≥0∞} {ε ε₁ ε₂ : ℝ≥0∞} {s : set ℝ≥0∞}

section topological_space
open topological_space

/-- Topology on `ℝ≥0∞`.

Note: this is different from the `emetric_space` topology. The `emetric_space` topology has
`is_open {⊤}`, while this topology doesn't have singleton elements. -/
instance : topological_space ℝ≥0∞ := preorder.topology ℝ≥0∞

instance : order_topology ℝ≥0∞ := ⟨rfl⟩

instance : t2_space ℝ≥0∞ := by apply_instance -- short-circuit type class inference

instance : normal_space ℝ≥0∞ := normal_of_compact_t2

instance : second_countable_topology ℝ≥0∞ :=
⟨⟨⋃q ≥ (0:ℚ), {{a : ℝ≥0∞ | a < real.to_nnreal q}, {a : ℝ≥0∞ | ↑(real.to_nnreal q) < a}},
  (countable_encodable _).bUnion $ assume a ha, (countable_singleton _).insert _,
  le_antisymm
    (le_generate_from $ by simp [or_imp_distrib, is_open_lt', is_open_gt'] {contextual := tt})
    (le_generate_from $ λ s h, begin
      rcases h with ⟨a, hs | hs⟩;
      [ rw show s = ⋃q∈{q:ℚ | 0 ≤ q ∧ a < real.to_nnreal q}, {b | ↑(real.to_nnreal q) < b},
           from set.ext (assume b, by simp [hs, @ennreal.lt_iff_exists_rat_btwn a b, and_assoc]),
        rw show s = ⋃q∈{q:ℚ | 0 ≤ q ∧ ↑(real.to_nnreal q) < a}, {b | b < ↑(real.to_nnreal q)},
           from set.ext (assume b,
             by simp [hs, @ennreal.lt_iff_exists_rat_btwn b a, and_comm, and_assoc])];
      { apply is_open_Union, intro q,
        apply is_open_Union, intro hq,
        exact generate_open.basic _ (mem_bUnion hq.1 $ by simp) }
    end)⟩⟩

lemma embedding_coe : embedding (coe : ℝ≥0 → ℝ≥0∞) :=
⟨⟨begin
  refine le_antisymm _ _,
  { rw [@order_topology.topology_eq_generate_intervals ℝ≥0∞ _,
      ← coinduced_le_iff_le_induced],
    refine le_generate_from (assume s ha, _),
    rcases ha with ⟨a, rfl | rfl⟩,
    show is_open {b : ℝ≥0 | a < ↑b},
    { cases a; simp [none_eq_top, some_eq_coe, is_open_lt'] },
    show is_open {b : ℝ≥0 | ↑b < a},
    { cases a; simp [none_eq_top, some_eq_coe, is_open_gt', is_open_const] } },
  { rw [@order_topology.topology_eq_generate_intervals ℝ≥0 _],
    refine le_generate_from (assume s ha, _),
    rcases ha with ⟨a, rfl | rfl⟩,
    exact ⟨Ioi a, is_open_Ioi, by simp [Ioi]⟩,
    exact ⟨Iio a, is_open_Iio, by simp [Iio]⟩ }
  end⟩,
  assume a b, coe_eq_coe.1⟩

lemma is_open_ne_top : is_open {a : ℝ≥0∞ | a ≠ ⊤} := is_open_ne

lemma is_open_Ico_zero : is_open (Ico 0 b) := by { rw ennreal.Ico_eq_Iio, exact is_open_Iio}

lemma open_embedding_coe : open_embedding (coe : ℝ≥0 → ℝ≥0∞) :=
⟨embedding_coe, by { convert is_open_ne_top, ext (x|_); simp [none_eq_top, some_eq_coe] }⟩

lemma coe_range_mem_nhds : range (coe : ℝ≥0 → ℝ≥0∞) ∈ 𝓝 (r : ℝ≥0∞) :=
is_open.mem_nhds open_embedding_coe.open_range $ mem_range_self _

@[norm_cast] lemma tendsto_coe {f : filter α} {m : α → ℝ≥0} {a : ℝ≥0} :
  tendsto (λa, (m a : ℝ≥0∞)) f (𝓝 ↑a) ↔ tendsto m f (𝓝 a) :=
embedding_coe.tendsto_nhds_iff.symm

lemma continuous_coe : continuous (coe : ℝ≥0 → ℝ≥0∞) :=
embedding_coe.continuous

lemma continuous_coe_iff  {α} [topological_space α] {f : α → ℝ≥0} :
  continuous (λa, (f a : ℝ≥0∞)) ↔ continuous f :=
embedding_coe.continuous_iff.symm

lemma nhds_coe {r : ℝ≥0} : 𝓝 (r : ℝ≥0∞) = (𝓝 r).map coe :=
(open_embedding_coe.map_nhds_eq r).symm

lemma tendsto_nhds_coe_iff {α : Type*} {l : filter α} {x : ℝ≥0} {f : ℝ≥0∞ → α} :
  tendsto f (𝓝 ↑x) l ↔ tendsto (f ∘ coe : ℝ≥0 → α) (𝓝 x) l :=
show _ ≤ _ ↔ _ ≤ _, by rw [nhds_coe, filter.map_map]

lemma continuous_at_coe_iff {α : Type*} [topological_space α] {x : ℝ≥0} {f : ℝ≥0∞ → α} :
  continuous_at f (↑x) ↔ continuous_at (f ∘ coe : ℝ≥0 → α) x :=
tendsto_nhds_coe_iff

lemma nhds_coe_coe {r p : ℝ≥0} :
  𝓝 ((r : ℝ≥0∞), (p : ℝ≥0∞)) = (𝓝 (r, p)).map (λp:ℝ≥0×ℝ≥0, (p.1, p.2)) :=
((open_embedding_coe.prod open_embedding_coe).map_nhds_eq (r, p)).symm

lemma continuous_of_real : continuous ennreal.of_real :=
(continuous_coe_iff.2 continuous_id).comp continuous_real_to_nnreal

lemma tendsto_of_real {f : filter α} {m : α → ℝ} {a : ℝ} (h : tendsto m f (𝓝 a)) :
  tendsto (λa, ennreal.of_real (m a)) f (𝓝 (ennreal.of_real a)) :=
tendsto.comp (continuous.tendsto continuous_of_real _) h

lemma tendsto_to_nnreal {a : ℝ≥0∞} (ha : a ≠ ⊤) :
  tendsto ennreal.to_nnreal (𝓝 a) (𝓝 a.to_nnreal) :=
begin
  lift a to ℝ≥0 using ha,
  rw [nhds_coe, tendsto_map'_iff],
  exact tendsto_id
end

lemma eventually_eq_of_to_real_eventually_eq {l : filter α} {f g : α → ℝ≥0∞}
  (hfi : ∀ᶠ x in l, f x ≠ ∞) (hgi : ∀ᶠ x in l, g x ≠ ∞)
  (hfg : (λ x, (f x).to_real) =ᶠ[l] (λ x, (g x).to_real)) :
  f =ᶠ[l] g :=
begin
  filter_upwards [hfi, hgi, hfg] with _ hfx hgx _,
  rwa ← ennreal.to_real_eq_to_real hfx hgx,
end

lemma continuous_on_to_nnreal : continuous_on ennreal.to_nnreal {a | a ≠ ∞}  :=
λ a ha, continuous_at.continuous_within_at (tendsto_to_nnreal ha)

lemma tendsto_to_real {a : ℝ≥0∞} (ha : a ≠ ⊤) : tendsto ennreal.to_real (𝓝 a) (𝓝 a.to_real) :=
nnreal.tendsto_coe.2 $ tendsto_to_nnreal ha

/-- The set of finite `ℝ≥0∞` numbers is homeomorphic to `ℝ≥0`. -/
def ne_top_homeomorph_nnreal : {a | a ≠ ∞} ≃ₜ ℝ≥0 :=
{ continuous_to_fun := continuous_on_iff_continuous_restrict.1 continuous_on_to_nnreal,
  continuous_inv_fun := continuous_subtype_mk _ continuous_coe,
  .. ne_top_equiv_nnreal }

/-- The set of finite `ℝ≥0∞` numbers is homeomorphic to `ℝ≥0`. -/
def lt_top_homeomorph_nnreal : {a | a < ∞} ≃ₜ ℝ≥0 :=
by refine (homeomorph.set_congr $ set.ext $ λ x, _).trans ne_top_homeomorph_nnreal;
  simp only [mem_set_of_eq, lt_top_iff_ne_top]

lemma nhds_top : 𝓝 ∞ = ⨅ a ≠ ∞, 𝓟 (Ioi a) :=
nhds_top_order.trans $ by simp [lt_top_iff_ne_top, Ioi]

lemma nhds_top' : 𝓝 ∞ = ⨅ r : ℝ≥0, 𝓟 (Ioi r) :=
nhds_top.trans $ infi_ne_top _

lemma nhds_top_basis : (𝓝 ∞).has_basis (λ a, a < ∞) (λ a, Ioi a) := nhds_top_basis

lemma tendsto_nhds_top_iff_nnreal {m : α → ℝ≥0∞} {f : filter α} :
  tendsto m f (𝓝 ⊤) ↔ ∀ x : ℝ≥0, ∀ᶠ a in f, ↑x < m a :=
by simp only [nhds_top', tendsto_infi, tendsto_principal, mem_Ioi]

lemma tendsto_nhds_top_iff_nat {m : α → ℝ≥0∞} {f : filter α} :
  tendsto m f (𝓝 ⊤) ↔ ∀ n : ℕ, ∀ᶠ a in f, ↑n < m a :=
tendsto_nhds_top_iff_nnreal.trans ⟨λ h n, by simpa only [ennreal.coe_nat] using h n,
  λ h x, let ⟨n, hn⟩ := exists_nat_gt x in
    (h n).mono (λ y, lt_trans $ by rwa [← ennreal.coe_nat, coe_lt_coe])⟩

lemma tendsto_nhds_top {m : α → ℝ≥0∞} {f : filter α}
  (h : ∀ n : ℕ, ∀ᶠ a in f, ↑n < m a) : tendsto m f (𝓝 ⊤) :=
tendsto_nhds_top_iff_nat.2 h

lemma tendsto_nat_nhds_top : tendsto (λ n : ℕ, ↑n) at_top (𝓝 ∞) :=
tendsto_nhds_top $ λ n, mem_at_top_sets.2
  ⟨n+1, λ m hm, ennreal.coe_nat_lt_coe_nat.2 $ nat.lt_of_succ_le hm⟩

@[simp, norm_cast] lemma tendsto_coe_nhds_top {f : α → ℝ≥0} {l : filter α} :
  tendsto (λ x, (f x : ℝ≥0∞)) l (𝓝 ∞) ↔ tendsto f l at_top :=
by rw [tendsto_nhds_top_iff_nnreal, at_top_basis_Ioi.tendsto_right_iff];
  [simp, apply_instance, apply_instance]

lemma nhds_zero : 𝓝 (0 : ℝ≥0∞) = ⨅a ≠ 0, 𝓟 (Iio a) :=
nhds_bot_order.trans $ by simp [bot_lt_iff_ne_bot, Iio]

lemma nhds_zero_basis : (𝓝 (0 : ℝ≥0∞)).has_basis (λ a : ℝ≥0∞, 0 < a) (λ a, Iio a) := nhds_bot_basis

lemma nhds_zero_basis_Iic : (𝓝 (0 : ℝ≥0∞)).has_basis (λ a : ℝ≥0∞, 0 < a) Iic := nhds_bot_basis_Iic

@[instance] lemma nhds_within_Ioi_coe_ne_bot {r : ℝ≥0} : (𝓝[>] (r : ℝ≥0∞)).ne_bot :=
nhds_within_Ioi_self_ne_bot' ⟨⊤, ennreal.coe_lt_top⟩

@[instance] lemma nhds_within_Ioi_zero_ne_bot : (𝓝[>] (0 : ℝ≥0∞)).ne_bot :=
nhds_within_Ioi_coe_ne_bot

-- using Icc because
-- • don't have 'Ioo (x - ε) (x + ε) ∈ 𝓝 x' unless x > 0
-- • (x - y ≤ ε ↔ x ≤ ε + y) is true, while (x - y < ε ↔ x < ε + y) is not
lemma Icc_mem_nhds (xt : x ≠ ⊤) (ε0 : ε ≠ 0) : Icc (x - ε) (x + ε) ∈ 𝓝 x :=
begin
  rw _root_.mem_nhds_iff,
  by_cases x0 : x = 0,
  { use Iio (x + ε),
    have : Iio (x + ε) ⊆ Icc (x - ε) (x + ε), assume a, rw x0, simpa using le_of_lt,
    use this, exact ⟨is_open_Iio, mem_Iio_self_add xt ε0⟩ },
  { use Ioo (x - ε) (x + ε), use Ioo_subset_Icc_self,
    exact ⟨is_open_Ioo, mem_Ioo_self_sub_add xt x0 ε0 ε0 ⟩ }
end

lemma nhds_of_ne_top (xt : x ≠ ⊤) : 𝓝 x = ⨅ ε > 0, 𝓟 (Icc (x - ε) (x + ε)) :=
begin
  refine le_antisymm _ _,
  -- first direction
  simp only [le_infi_iff, le_principal_iff], assume ε ε0, exact Icc_mem_nhds xt ε0.lt.ne',
  -- second direction
  rw nhds_generate_from, refine le_infi (assume s, le_infi $ assume hs, _),
  rcases hs with ⟨xs, ⟨a, (rfl : s = Ioi a)|(rfl : s = Iio a)⟩⟩,
  { rcases exists_between xs with ⟨b, ab, bx⟩,
    have xb_pos : 0 < x - b := tsub_pos_iff_lt.2 bx,
    have xxb : x - (x - b) = b := sub_sub_cancel xt bx.le,
    refine infi_le_of_le (x - b) (infi_le_of_le xb_pos _),
    simp only [mem_principal, le_principal_iff],
    assume y, rintros ⟨h₁, h₂⟩, rw xxb at h₁, calc a < b : ab ... ≤ y : h₁ },
  { rcases exists_between xs with ⟨b, xb, ba⟩,
    have bx_pos : 0 < b - x := tsub_pos_iff_lt.2 xb,
    have xbx : x + (b - x) = b := add_tsub_cancel_of_le xb.le,
    refine infi_le_of_le (b - x) (infi_le_of_le bx_pos _),
    simp only [mem_principal, le_principal_iff],
    assume y, rintros ⟨h₁, h₂⟩, rw xbx at h₂, calc y ≤ b : h₂ ... < a : ba },
end

/-- Characterization of neighborhoods for `ℝ≥0∞` numbers. See also `tendsto_order`
for a version with strict inequalities. -/
protected theorem tendsto_nhds {f : filter α} {u : α → ℝ≥0∞} {a : ℝ≥0∞} (ha : a ≠ ⊤) :
  tendsto u f (𝓝 a) ↔ ∀ ε > 0, ∀ᶠ x in f, (u x) ∈ Icc (a - ε) (a + ε) :=
by simp only [nhds_of_ne_top ha, tendsto_infi, tendsto_principal, mem_Icc]

protected lemma tendsto_nhds_zero {f : filter α} {u : α → ℝ≥0∞} :
  tendsto u f (𝓝 0) ↔ ∀ ε > 0, ∀ᶠ x in f, u x ≤ ε :=
begin
  rw ennreal.tendsto_nhds zero_ne_top,
  simp only [true_and, zero_tsub, zero_le, zero_add, set.mem_Icc],
end

protected lemma tendsto_at_top [nonempty β] [semilattice_sup β] {f : β → ℝ≥0∞} {a : ℝ≥0∞}
  (ha : a ≠ ⊤) : tendsto f at_top (𝓝 a) ↔ ∀ε>0, ∃N, ∀n≥N, (f n) ∈ Icc (a - ε) (a + ε) :=
by simp only [ennreal.tendsto_nhds ha, mem_at_top_sets, mem_set_of_eq, filter.eventually]

instance : has_continuous_add ℝ≥0∞ :=
begin
  refine ⟨continuous_iff_continuous_at.2 _⟩,
  rintro ⟨(_|a), b⟩,
  { exact tendsto_nhds_top_mono' continuous_at_fst (λ p, le_add_right le_rfl) },
  rcases b with (_|b),
  { exact tendsto_nhds_top_mono' continuous_at_snd (λ p, le_add_left le_rfl) },
  simp only [continuous_at, some_eq_coe, nhds_coe_coe, ← coe_add, tendsto_map'_iff, (∘),
    tendsto_coe, tendsto_add]
end

protected lemma tendsto_at_top_zero [hβ : nonempty β] [semilattice_sup β] {f : β → ℝ≥0∞} :
  filter.at_top.tendsto f (𝓝 0) ↔ ∀ ε > 0, ∃ N, ∀ n ≥ N, f n ≤ ε :=
begin
  rw ennreal.tendsto_at_top zero_ne_top,
  { simp_rw [set.mem_Icc, zero_add, zero_tsub, zero_le _, true_and], },
  { exact hβ, },
end

lemma tendsto_sub {a b : ℝ≥0∞} (h : a ≠ ∞ ∨ b ≠ ∞) :
  tendsto (λ p : ℝ≥0∞ × ℝ≥0∞, p.1 - p.2) (𝓝 (a, b)) (𝓝 (a - b)) :=
begin
  cases a; cases b,
  { simp only [eq_self_iff_true, not_true, ne.def, none_eq_top, or_self] at h, contradiction },
  { simp only [some_eq_coe, with_top.top_sub_coe, none_eq_top],
    apply tendsto_nhds_top_iff_nnreal.2 (λ n, _),
    rw [nhds_prod_eq, eventually_prod_iff],
    refine ⟨λ z, ((n + (b + 1)) : ℝ≥0∞) < z,
            Ioi_mem_nhds (by simp only [one_lt_top, add_lt_top, coe_lt_top, and_self]),
            λ z, z < b + 1, Iio_mem_nhds ((ennreal.lt_add_right coe_ne_top one_ne_zero)),
            λ x hx y hy, _⟩,
    dsimp,
    rw lt_tsub_iff_right,
    have : ((n : ℝ≥0∞) + y) + (b + 1) < x + (b + 1) := calc
      ((n : ℝ≥0∞) + y) + (b + 1) = ((n : ℝ≥0∞) + (b + 1)) + y : by abel
      ... < x + (b + 1) : ennreal.add_lt_add hx hy,
    exact lt_of_add_lt_add_right this },
  { simp only [some_eq_coe, with_top.sub_top, none_eq_top],
    suffices H : ∀ᶠ (p : ℝ≥0∞ × ℝ≥0∞) in 𝓝 (a, ∞), 0 = p.1 - p.2,
      from tendsto_const_nhds.congr' H,
    rw [nhds_prod_eq, eventually_prod_iff],
    refine ⟨λ z, z < a + 1, Iio_mem_nhds (ennreal.lt_add_right coe_ne_top one_ne_zero),
            λ z, (a : ℝ≥0∞) + 1 < z,
            Ioi_mem_nhds (by simp only [one_lt_top, add_lt_top, coe_lt_top, and_self]),
            λ x hx y hy, _⟩,
    rw eq_comm,
    simp only [tsub_eq_zero_iff_le, (has_lt.lt.trans hx hy).le], },
  { simp only [some_eq_coe, nhds_coe_coe, tendsto_map'_iff, function.comp, ← ennreal.coe_sub,
               tendsto_coe],
    exact continuous.tendsto (by continuity) _ }
end

protected lemma tendsto.sub {f : filter α} {ma : α → ℝ≥0∞} {mb : α → ℝ≥0∞} {a b : ℝ≥0∞}
  (hma : tendsto ma f (𝓝 a)) (hmb : tendsto mb f (𝓝 b)) (h : a ≠ ∞ ∨ b ≠ ∞) :
  tendsto (λ a, ma a - mb a) f (𝓝 (a - b)) :=
show tendsto ((λ p : ℝ≥0∞ × ℝ≥0∞, p.1 - p.2) ∘ (λa, (ma a, mb a))) f (𝓝 (a - b)), from
tendsto.comp (ennreal.tendsto_sub h) (hma.prod_mk_nhds hmb)

protected lemma tendsto_mul (ha : a ≠ 0 ∨ b ≠ ⊤) (hb : b ≠ 0 ∨ a ≠ ⊤) :
  tendsto (λp:ℝ≥0∞×ℝ≥0∞, p.1 * p.2) (𝓝 (a, b)) (𝓝 (a * b)) :=
have ht : ∀b:ℝ≥0∞, b ≠ 0 → tendsto (λp:ℝ≥0∞×ℝ≥0∞, p.1 * p.2) (𝓝 ((⊤:ℝ≥0∞), b)) (𝓝 ⊤),
begin
  refine assume b hb, tendsto_nhds_top_iff_nnreal.2 $ assume n, _,
  rcases lt_iff_exists_nnreal_btwn.1 (pos_iff_ne_zero.2 hb) with ⟨ε, hε, hεb⟩,
  replace hε : 0 < ε, from coe_pos.1 hε,
  filter_upwards [prod_is_open.mem_nhds (lt_mem_nhds $ @coe_lt_top (n / ε)) (lt_mem_nhds hεb)],
  rintros ⟨a₁, a₂⟩ ⟨h₁, h₂⟩,
  dsimp at h₁ h₂ ⊢,
  rw [← div_mul_cancel n hε.ne', coe_mul],
  exact mul_lt_mul h₁ h₂
end,
begin
  cases a, {simp [none_eq_top] at hb, simp [none_eq_top, ht b hb, top_mul, hb] },
  cases b,
  { simp [none_eq_top] at ha,
    simp [*, nhds_swap (a : ℝ≥0∞) ⊤, none_eq_top, some_eq_coe, top_mul, tendsto_map'_iff, (∘),
      mul_comm] },
  simp [some_eq_coe, nhds_coe_coe, tendsto_map'_iff, (∘)],
  simp only [coe_mul.symm, tendsto_coe, tendsto_mul]
end

protected lemma tendsto.mul {f : filter α} {ma : α → ℝ≥0∞} {mb : α → ℝ≥0∞} {a b : ℝ≥0∞}
  (hma : tendsto ma f (𝓝 a)) (ha : a ≠ 0 ∨ b ≠ ⊤) (hmb : tendsto mb f (𝓝 b)) (hb : b ≠ 0 ∨ a ≠ ⊤) :
  tendsto (λa, ma a * mb a) f (𝓝 (a * b)) :=
show tendsto ((λp:ℝ≥0∞×ℝ≥0∞, p.1 * p.2) ∘ (λa, (ma a, mb a))) f (𝓝 (a * b)), from
tendsto.comp (ennreal.tendsto_mul ha hb) (hma.prod_mk_nhds hmb)

protected lemma tendsto.const_mul {f : filter α} {m : α → ℝ≥0∞} {a b : ℝ≥0∞}
  (hm : tendsto m f (𝓝 b)) (hb : b ≠ 0 ∨ a ≠ ⊤) : tendsto (λb, a * m b) f (𝓝 (a * b)) :=
by_cases
  (assume : a = 0, by simp [this, tendsto_const_nhds])
  (assume ha : a ≠ 0, ennreal.tendsto.mul tendsto_const_nhds (or.inl ha) hm hb)

protected lemma tendsto.mul_const {f : filter α} {m : α → ℝ≥0∞} {a b : ℝ≥0∞}
  (hm : tendsto m f (𝓝 a)) (ha : a ≠ 0 ∨ b ≠ ⊤) : tendsto (λx, m x * b) f (𝓝 (a * b)) :=
by simpa only [mul_comm] using ennreal.tendsto.const_mul hm ha

lemma tendsto_finset_prod_of_ne_top {ι : Type*} {f : ι → α → ℝ≥0∞} {x : filter α} {a : ι → ℝ≥0∞}
  (s : finset ι) (h : ∀ i ∈ s, tendsto (f i) x (𝓝 (a i))) (h' : ∀ i ∈ s, a i ≠ ∞):
  tendsto (λ b, ∏ c in s, f c b) x (𝓝 (∏ c in s, a c)) :=
begin
  induction s using finset.induction with a s has IH, { simp [tendsto_const_nhds] },
  simp only [finset.prod_insert has],
  apply tendsto.mul (h _ (finset.mem_insert_self _ _)),
  { right,
    exact (prod_lt_top (λ i hi, h' _ (finset.mem_insert_of_mem hi))).ne },
  { exact IH (λ i hi, h _ (finset.mem_insert_of_mem hi))
      (λ i hi, h' _ (finset.mem_insert_of_mem hi)) },
  { exact or.inr (h' _ (finset.mem_insert_self _ _)) }
end

protected lemma continuous_at_const_mul {a b : ℝ≥0∞} (h : a ≠ ⊤ ∨ b ≠ 0) :
  continuous_at ((*) a) b :=
tendsto.const_mul tendsto_id h.symm

protected lemma continuous_at_mul_const {a b : ℝ≥0∞} (h : a ≠ ⊤ ∨ b ≠ 0) :
  continuous_at (λ x, x * a) b :=
tendsto.mul_const tendsto_id h.symm

protected lemma continuous_const_mul {a : ℝ≥0∞} (ha : a ≠ ⊤) : continuous ((*) a) :=
continuous_iff_continuous_at.2 $ λ x, ennreal.continuous_at_const_mul (or.inl ha)

protected lemma continuous_mul_const {a : ℝ≥0∞} (ha : a ≠ ⊤) : continuous (λ x, x * a) :=
continuous_iff_continuous_at.2 $ λ x, ennreal.continuous_at_mul_const (or.inl ha)

@[continuity]
lemma continuous_pow (n : ℕ) : continuous (λ a : ℝ≥0∞, a ^ n) :=
begin
  induction n with n IH,
  { simp [continuous_const] },
  simp_rw [nat.succ_eq_add_one, pow_add, pow_one, continuous_iff_continuous_at],
  assume x,
  refine ennreal.tendsto.mul (IH.tendsto _) _ tendsto_id _;
  by_cases H : x = 0,
  { simp only [H, zero_ne_top, ne.def, or_true, not_false_iff]},
  { exact or.inl (λ h, H (pow_eq_zero h)) },
  { simp only [H, pow_eq_top_iff, zero_ne_top, false_or, eq_self_iff_true,
               not_true, ne.def, not_false_iff, false_and], },
  { simp only [H, true_or, ne.def, not_false_iff] }
end

protected lemma tendsto.pow {f : filter α} {m : α → ℝ≥0∞} {a : ℝ≥0∞} {n : ℕ}
  (hm : tendsto m f (𝓝 a)) :
  tendsto (λ x, (m x) ^ n) f (𝓝 (a ^ n)) :=
((continuous_pow n).tendsto a).comp hm

lemma le_of_forall_lt_one_mul_le {x y : ℝ≥0∞} (h : ∀ a < 1, a * x ≤ y) : x ≤ y :=
begin
  have : tendsto (* x) (𝓝[<] 1) (𝓝 (1 * x)) :=
    (ennreal.continuous_at_mul_const (or.inr one_ne_zero)).mono_left inf_le_left,
  rw one_mul at this,
  haveI : (𝓝[<] (1 : ℝ≥0∞)).ne_bot := nhds_within_Iio_self_ne_bot' ⟨0, ennreal.zero_lt_one⟩,
  exact le_of_tendsto this (eventually_nhds_within_iff.2 $ eventually_of_forall h)
end

lemma infi_mul_left' {ι} {f : ι → ℝ≥0∞} {a : ℝ≥0∞}
  (h : a = ⊤ → (⨅ i, f i) = 0 → ∃ i, f i = 0) (h0 : a = 0 → nonempty ι) :
  (⨅ i, a * f i) = a * ⨅ i, f i :=
begin
  by_cases H : a = ⊤ ∧ (⨅ i, f i) = 0,
  { rcases h H.1 H.2 with ⟨i, hi⟩,
    rw [H.2, mul_zero, ← bot_eq_zero, infi_eq_bot],
    exact λ b hb, ⟨i, by rwa [hi, mul_zero, ← bot_eq_zero]⟩ },
  { rw not_and_distrib at H,
    casesI is_empty_or_nonempty ι,
    { rw [infi_of_empty, infi_of_empty, mul_top, if_neg],
      exact mt h0 (not_nonempty_iff.2 ‹_›) },
    { exact (map_infi_of_continuous_at_of_monotone' (ennreal.continuous_at_const_mul H)
        ennreal.mul_left_mono).symm } }
end

lemma infi_mul_left {ι} [nonempty ι] {f : ι → ℝ≥0∞} {a : ℝ≥0∞}
  (h : a = ⊤ → (⨅ i, f i) = 0 → ∃ i, f i = 0) :
  (⨅ i, a * f i) = a * ⨅ i, f i :=
infi_mul_left' h (λ _, ‹nonempty ι›)

lemma infi_mul_right' {ι} {f : ι → ℝ≥0∞} {a : ℝ≥0∞}
  (h : a = ⊤ → (⨅ i, f i) = 0 → ∃ i, f i = 0) (h0 : a = 0 → nonempty ι) :
  (⨅ i, f i * a) = (⨅ i, f i) * a :=
by simpa only [mul_comm a] using infi_mul_left' h h0

lemma infi_mul_right {ι} [nonempty ι] {f : ι → ℝ≥0∞} {a : ℝ≥0∞}
  (h : a = ⊤ → (⨅ i, f i) = 0 → ∃ i, f i = 0) :
  (⨅ i, f i * a) = (⨅ i, f i) * a :=
infi_mul_right' h (λ _, ‹nonempty ι›)

lemma inv_map_infi {ι : Sort*} {x : ι → ℝ≥0∞} :
  (infi x)⁻¹ = (⨆ i, (x i)⁻¹) :=
order_iso.inv_ennreal.map_infi x

lemma inv_map_supr {ι : Sort*} {x : ι → ℝ≥0∞} :
  (supr x)⁻¹ = (⨅ i, (x i)⁻¹) :=
order_iso.inv_ennreal.map_supr x

lemma inv_liminf {ι : Sort*} {x : ι → ℝ≥0∞} {l : filter ι} :
  (l.liminf x)⁻¹ = l.limsup (λ i, (x i)⁻¹) :=
by simp only [limsup_eq_infi_supr, inv_map_infi, inv_map_supr, liminf_eq_supr_infi]

instance : has_continuous_inv ℝ≥0∞ :=
{ continuous_inv :=
  continuous_iff_continuous_at.2 $ λ a, tendsto_order.2
  ⟨λ b hb, by simpa only [ennreal.lt_inv_iff_lt_inv]
     using gt_mem_nhds (ennreal.lt_inv_iff_lt_inv.1 hb),
   λ b hb, by simpa only [gt_iff_lt, ennreal.inv_lt_iff_inv_lt]
     using lt_mem_nhds (ennreal.inv_lt_iff_inv_lt.1 hb)⟩ }

@[simp] protected lemma tendsto_inv_iff {f : filter α} {m : α → ℝ≥0∞} {a : ℝ≥0∞} :
  tendsto (λ x, (m x)⁻¹) f (𝓝 a⁻¹) ↔ tendsto m f (𝓝 a) :=
⟨λ h, by simpa only [inv_inv] using tendsto.inv h,  tendsto.inv⟩

protected lemma tendsto.div {f : filter α} {ma : α → ℝ≥0∞} {mb : α → ℝ≥0∞} {a b : ℝ≥0∞}
  (hma : tendsto ma f (𝓝 a)) (ha : a ≠ 0 ∨ b ≠ 0) (hmb : tendsto mb f (𝓝 b)) (hb : b ≠ ⊤ ∨ a ≠ ⊤) :
  tendsto (λa, ma a / mb a) f (𝓝 (a / b)) :=
by { apply tendsto.mul hma _ (ennreal.tendsto_inv_iff.2 hmb) _; simp [ha, hb] }

protected lemma tendsto.const_div {f : filter α} {m : α → ℝ≥0∞} {a b : ℝ≥0∞}
  (hm : tendsto m f (𝓝 b)) (hb : b ≠ ⊤ ∨ a ≠ ⊤) : tendsto (λb, a / m b) f (𝓝 (a / b)) :=
by { apply tendsto.const_mul (ennreal.tendsto_inv_iff.2 hm), simp [hb] }

protected lemma tendsto.div_const {f : filter α} {m : α → ℝ≥0∞} {a b : ℝ≥0∞}
  (hm : tendsto m f (𝓝 a)) (ha : a ≠ 0 ∨ b ≠ 0) : tendsto (λx, m x / b) f (𝓝 (a / b)) :=
by { apply tendsto.mul_const hm, simp [ha] }

protected lemma tendsto_inv_nat_nhds_zero : tendsto (λ n : ℕ, (n : ℝ≥0∞)⁻¹) at_top (𝓝 0) :=
ennreal.inv_top ▸ ennreal.tendsto_inv_iff.2 tendsto_nat_nhds_top

lemma bsupr_add {ι} {s : set ι} (hs : s.nonempty) {f : ι → ℝ≥0∞} :
  (⨆ i ∈ s, f i) + a = ⨆ i ∈ s, f i + a :=
begin
  simp only [← Sup_image], symmetry,
  rw [image_comp (+ a)],
  refine is_lub.Sup_eq ((is_lub_Sup $ f '' s).is_lub_of_tendsto _ (hs.image _) _),
  exacts [λ x _ y _ hxy, add_le_add hxy le_rfl,
    tendsto.add (tendsto_id' inf_le_left) tendsto_const_nhds]
end

lemma Sup_add {s : set ℝ≥0∞} (hs : s.nonempty) : Sup s + a = ⨆b∈s, b + a :=
by rw [Sup_eq_supr, bsupr_add hs]

lemma supr_add {ι : Sort*} {s : ι → ℝ≥0∞} [h : nonempty ι] : supr s + a = ⨆b, s b + a :=
let ⟨x⟩ := h in
calc supr s + a = Sup (range s) + a : by rw Sup_range
  ... = (⨆b∈range s, b + a) : Sup_add ⟨s x, x, rfl⟩
  ... = _ : supr_range

lemma add_supr {ι : Sort*} {s : ι → ℝ≥0∞} [h : nonempty ι] : a + supr s = ⨆b, a + s b :=
by rw [add_comm, supr_add]; simp [add_comm]

lemma supr_add_supr {ι : Sort*} {f g : ι → ℝ≥0∞} (h : ∀i j, ∃k, f i + g j ≤ f k + g k) :
  supr f + supr g = (⨆ a, f a + g a) :=
begin
  by_cases hι : nonempty ι,
  { letI := hι,
    refine le_antisymm _ (supr_le $ λ a, add_le_add (le_supr _ _) (le_supr _ _)),
    simpa [add_supr, supr_add] using
      λ i j:ι, show f i + g j ≤ ⨆ a, f a + g a, from
      let ⟨k, hk⟩ := h i j in le_supr_of_le k hk },
  { have : ∀f:ι → ℝ≥0∞, (⨆i, f i) = 0 := λ f, supr_eq_zero.mpr (λ i, (hι ⟨i⟩).elim),
    rw [this, this, this, zero_add] }
end

lemma supr_add_supr_of_monotone {ι : Sort*} [semilattice_sup ι]
  {f g : ι → ℝ≥0∞} (hf : monotone f) (hg : monotone g) :
  supr f + supr g = (⨆ a, f a + g a) :=
supr_add_supr $ assume i j, ⟨i ⊔ j, add_le_add (hf $ le_sup_left) (hg $ le_sup_right)⟩

lemma finset_sum_supr_nat {α} {ι} [semilattice_sup ι] {s : finset α} {f : α → ι → ℝ≥0∞}
  (hf : ∀a, monotone (f a)) :
  ∑ a in s, supr (f a) = (⨆ n, ∑ a in s, f a n) :=
begin
  refine finset.induction_on s _ _,
  { simp, },
  { assume a s has ih,
    simp only [finset.sum_insert has],
    rw [ih, supr_add_supr_of_monotone (hf a)],
    assume i j h,
    exact (finset.sum_le_sum $ assume a ha, hf a h) }
end

lemma mul_Sup {s : set ℝ≥0∞} {a : ℝ≥0∞} : a * Sup s = ⨆i∈s, a * i :=
begin
  by_cases hs : ∀x∈s, x = (0:ℝ≥0∞),
  { have h₁ : Sup s = 0 := (bot_unique $ Sup_le $ assume a ha, (hs a ha).symm ▸ le_refl 0),
    have h₂ : (⨆i ∈ s, a * i) = 0 :=
      (bot_unique $ supr_le $ assume a, supr_le $ assume ha, by simp [hs a ha]),
    rw [h₁, h₂, mul_zero] },
  { simp only [not_forall] at hs,
    rcases hs with ⟨x, hx, hx0⟩,
    have s₁ : Sup s ≠ 0 :=
      pos_iff_ne_zero.1 (lt_of_lt_of_le (pos_iff_ne_zero.2 hx0) (le_Sup hx)),
    have : Sup ((λb, a * b) '' s) = a * Sup s :=
      is_lub.Sup_eq ((is_lub_Sup s).is_lub_of_tendsto
        (assume x _ y _ h, mul_le_mul_left' h _)
        ⟨x, hx⟩
        (ennreal.tendsto.const_mul (tendsto_id' inf_le_left) (or.inl s₁))),
    rw [this.symm, Sup_image] }
end

lemma mul_supr {ι : Sort*} {f : ι → ℝ≥0∞} {a : ℝ≥0∞} : a * supr f = ⨆i, a * f i :=
by rw [← Sup_range, mul_Sup, supr_range]

lemma supr_mul {ι : Sort*} {f : ι → ℝ≥0∞} {a : ℝ≥0∞} : supr f * a = ⨆i, f i * a :=
by rw [mul_comm, mul_supr]; congr; funext; rw [mul_comm]

lemma supr_div {ι : Sort*} {f : ι → ℝ≥0∞} {a : ℝ≥0∞} : supr f / a = ⨆i, f i / a :=
supr_mul

protected lemma tendsto_coe_sub : ∀{b:ℝ≥0∞}, tendsto (λb:ℝ≥0∞, ↑r - b) (𝓝 b) (𝓝 (↑r - b)) :=
begin
  refine forall_ennreal.2 ⟨λ a, _, _⟩,
  { simp [@nhds_coe a, tendsto_map'_iff, (∘), tendsto_coe, ← with_top.coe_sub],
    exact tendsto_const_nhds.sub tendsto_id },
  simp,
  exact (tendsto.congr' (mem_of_superset (lt_mem_nhds $ @coe_lt_top r) $
    by simp [le_of_lt] {contextual := tt})) tendsto_const_nhds
end

lemma sub_supr {ι : Sort*} [nonempty ι] {b : ι → ℝ≥0∞} (hr : a < ⊤) :
  a - (⨆i, b i) = (⨅i, a - b i) :=
let ⟨r, eq, _⟩ := lt_iff_exists_coe.mp hr in
have Inf ((λb, ↑r - b) '' range b) = ↑r - (⨆i, b i),
  from is_glb.Inf_eq $ is_lub_supr.is_glb_of_tendsto
    (assume x _ y _, tsub_le_tsub (le_refl (r : ℝ≥0∞)))
    (range_nonempty _)
    (ennreal.tendsto_coe_sub.comp (tendsto_id' inf_le_left)),
by rw [eq, ←this]; simp [Inf_image, infi_range, -mem_range]; exact le_rfl

lemma exists_countable_dense_no_zero_top :
  ∃ (s : set ℝ≥0∞), countable s ∧ dense s ∧ 0 ∉ s ∧ ∞ ∉ s :=
begin
  obtain ⟨s, s_count, s_dense, hs⟩ : ∃ s : set ℝ≥0∞, countable s ∧ dense s ∧
    (∀ x, is_bot x → x ∉ s) ∧ (∀ x, is_top x → x ∉ s) := exists_countable_dense_no_bot_top ℝ≥0∞,
  exact ⟨s, s_count, s_dense, λ h, hs.1 0 (by simp) h, λ h, hs.2 ∞ (by simp) h⟩,
end

end topological_space

section tsum

variables {f g : α → ℝ≥0∞}

@[norm_cast] protected lemma has_sum_coe {f : α → ℝ≥0} {r : ℝ≥0} :
  has_sum (λa, (f a : ℝ≥0∞)) ↑r ↔ has_sum f r :=
have (λs:finset α, ∑ a in s, ↑(f a)) = (coe : ℝ≥0 → ℝ≥0∞) ∘ (λs:finset α, ∑ a in s, f a),
  from funext $ assume s, ennreal.coe_finset_sum.symm,
by unfold has_sum; rw [this, tendsto_coe]

protected lemma tsum_coe_eq {f : α → ℝ≥0} (h : has_sum f r) : ∑'a, (f a : ℝ≥0∞) = r :=
(ennreal.has_sum_coe.2 h).tsum_eq

protected lemma coe_tsum {f : α → ℝ≥0} : summable f → ↑(tsum f) = ∑'a, (f a : ℝ≥0∞)
| ⟨r, hr⟩ := by rw [hr.tsum_eq, ennreal.tsum_coe_eq hr]

protected lemma has_sum : has_sum f (⨆s:finset α, ∑ a in s, f a) :=
tendsto_at_top_supr $ λ s t, finset.sum_le_sum_of_subset

@[simp] protected lemma summable : summable f := ⟨_, ennreal.has_sum⟩

lemma tsum_coe_ne_top_iff_summable {f : β → ℝ≥0} :
  ∑' b, (f b:ℝ≥0∞) ≠ ∞ ↔ summable f :=
begin
  refine ⟨λ h, _, λ h, ennreal.coe_tsum h ▸ ennreal.coe_ne_top⟩,
  lift (∑' b, (f b:ℝ≥0∞)) to ℝ≥0 using h with a ha,
  refine ⟨a, ennreal.has_sum_coe.1 _⟩,
  rw ha,
  exact ennreal.summable.has_sum
end

protected lemma tsum_eq_supr_sum : ∑'a, f a = (⨆s:finset α, ∑ a in s, f a) :=
ennreal.has_sum.tsum_eq

protected lemma tsum_eq_supr_sum' {ι : Type*} (s : ι → finset α) (hs : ∀ t, ∃ i, t ⊆ s i) :
  ∑' a, f a = ⨆ i, ∑ a in s i, f a :=
begin
  rw [ennreal.tsum_eq_supr_sum],
  symmetry,
  change (⨆i:ι, (λ t : finset α, ∑ a in t, f a) (s i)) = ⨆s:finset α, ∑ a in s, f a,
  exact (finset.sum_mono_set f).supr_comp_eq hs
end

protected lemma tsum_sigma {β : α → Type*} (f : Πa, β a → ℝ≥0∞) :
  ∑'p:Σa, β a, f p.1 p.2 = ∑'a b, f a b :=
tsum_sigma' (assume b, ennreal.summable) ennreal.summable

protected lemma tsum_sigma' {β : α → Type*} (f : (Σ a, β a) → ℝ≥0∞) :
  ∑'p:(Σa, β a), f p = ∑'a b, f ⟨a, b⟩ :=
tsum_sigma' (assume b, ennreal.summable) ennreal.summable

protected lemma tsum_prod {f : α → β → ℝ≥0∞} : ∑'p:α×β, f p.1 p.2 = ∑'a, ∑'b, f a b :=
tsum_prod' ennreal.summable $ λ _, ennreal.summable

protected lemma tsum_comm {f : α → β → ℝ≥0∞} : ∑'a, ∑'b, f a b = ∑'b, ∑'a, f a b :=
tsum_comm' ennreal.summable (λ _, ennreal.summable) (λ _, ennreal.summable)

protected lemma tsum_add : ∑'a, (f a + g a) = (∑'a, f a) + (∑'a, g a) :=
tsum_add ennreal.summable ennreal.summable

protected lemma tsum_le_tsum (h : ∀a, f a ≤ g a) : ∑'a, f a ≤ ∑'a, g a :=
tsum_le_tsum h ennreal.summable ennreal.summable

protected lemma sum_le_tsum {f : α → ℝ≥0∞} (s : finset α) : ∑ x in s, f x ≤ ∑' x, f x :=
sum_le_tsum s (λ x hx, zero_le _) ennreal.summable

protected lemma tsum_eq_supr_nat' {f : ℕ → ℝ≥0∞} {N : ℕ → ℕ} (hN : tendsto N at_top at_top) :
  ∑'i:ℕ, f i = (⨆i:ℕ, ∑ a in finset.range (N i), f a) :=
ennreal.tsum_eq_supr_sum' _ $ λ t,
  let ⟨n, hn⟩    := t.exists_nat_subset_range,
      ⟨k, _, hk⟩ := exists_le_of_tendsto_at_top hN 0 n in
  ⟨k, finset.subset.trans hn (finset.range_mono hk)⟩

protected lemma tsum_eq_supr_nat {f : ℕ → ℝ≥0∞} :
  ∑'i:ℕ, f i = (⨆i:ℕ, ∑ a in finset.range i, f a) :=
ennreal.tsum_eq_supr_sum' _ finset.exists_nat_subset_range

protected lemma tsum_eq_liminf_sum_nat {f : ℕ → ℝ≥0∞} :
  ∑' i, f i = filter.at_top.liminf (λ n, ∑ i in finset.range n, f i) :=
begin
  rw [ennreal.tsum_eq_supr_nat, filter.liminf_eq_supr_infi_of_nat],
  congr,
  refine funext (λ n, le_antisymm _ _),
  { refine le_infi₂ (λ i hi, finset.sum_le_sum_of_subset_of_nonneg _ (λ _ _ _, zero_le _)),
    simpa only [finset.range_subset, add_le_add_iff_right] using hi, },
  { refine le_trans (infi_le _ n) _,
    simp [le_refl n, le_refl ((finset.range n).sum f)], },
end

protected lemma le_tsum (a : α) : f a ≤ ∑'a, f a :=
le_tsum' ennreal.summable a

@[simp] protected lemma tsum_eq_zero : ∑' i, f i = 0 ↔ ∀ i, f i = 0 :=
⟨λ h i, nonpos_iff_eq_zero.1 $ h ▸ ennreal.le_tsum i, λ h, by simp [h]⟩

protected lemma tsum_eq_top_of_eq_top : (∃ a, f a = ∞) → ∑' a, f a = ∞
| ⟨a, ha⟩ := top_unique $ ha ▸ ennreal.le_tsum a

@[simp] protected lemma tsum_top [nonempty α] : ∑' a : α, ∞ = ∞ :=
let ⟨a⟩ := ‹nonempty α› in ennreal.tsum_eq_top_of_eq_top ⟨a, rfl⟩

lemma tsum_const_eq_top_of_ne_zero {α : Type*} [infinite α] {c : ℝ≥0∞} (hc : c ≠ 0) :
  (∑' (a : α), c) = ∞ :=
begin
  have A : tendsto (λ (n : ℕ), (n : ℝ≥0∞) * c) at_top (𝓝 (∞ * c)),
  { apply ennreal.tendsto.mul_const tendsto_nat_nhds_top,
    simp only [true_or, top_ne_zero, ne.def, not_false_iff] },
  have B : ∀ (n : ℕ), (n : ℝ≥0∞) * c ≤ (∑' (a : α), c),
  { assume n,
    rcases infinite.exists_subset_card_eq α n with ⟨s, hs⟩,
    simpa [hs] using @ennreal.sum_le_tsum α (λ i, c) s },
  simpa [hc] using le_of_tendsto' A B,
end

protected lemma ne_top_of_tsum_ne_top (h : ∑' a, f a ≠ ∞) (a : α) : f a ≠ ∞ :=
λ ha, h $ ennreal.tsum_eq_top_of_eq_top ⟨a, ha⟩

protected lemma tsum_mul_left : ∑'i, a * f i = a * ∑'i, f i :=
if h : ∀i, f i = 0 then by simp [h] else
let ⟨i, (hi : f i ≠ 0)⟩ := not_forall.mp h in
have sum_ne_0 : ∑'i, f i ≠ 0, from ne_of_gt $
  calc 0 < f i : lt_of_le_of_ne (zero_le _) hi.symm
    ... ≤ ∑'i, f i : ennreal.le_tsum _,
have tendsto (λs:finset α, ∑ j in s, a * f j) at_top (𝓝 (a * ∑'i, f i)),
  by rw [← show (*) a ∘ (λs:finset α, ∑ j in s, f j) = λs, ∑ j in s, a * f j,
         from funext $ λ s, finset.mul_sum];
  exact ennreal.tendsto.const_mul ennreal.summable.has_sum (or.inl sum_ne_0),
has_sum.tsum_eq this

protected lemma tsum_mul_right : (∑'i, f i * a) = (∑'i, f i) * a :=
by simp [mul_comm, ennreal.tsum_mul_left]

@[simp] lemma tsum_supr_eq {α : Type*} (a : α) {f : α → ℝ≥0∞} :
  ∑'b:α, (⨆ (h : a = b), f b) = f a :=
le_antisymm
  (by rw [ennreal.tsum_eq_supr_sum]; exact supr_le (assume s,
    calc (∑ b in s, ⨆ (h : a = b), f b) ≤ ∑ b in {a}, ⨆ (h : a = b), f b :
        finset.sum_le_sum_of_ne_zero $ assume b _ hb,
          suffices a = b, by simpa using this.symm,
          classical.by_contradiction $ assume h,
            by simpa [h] using hb
      ... = f a : by simp))
  (calc f a ≤ (⨆ (h : a = a), f a) : le_supr (λh:a=a, f a) rfl
    ... ≤ (∑'b:α, ⨆ (h : a = b), f b) : ennreal.le_tsum _)

lemma has_sum_iff_tendsto_nat {f : ℕ → ℝ≥0∞} (r : ℝ≥0∞) :
  has_sum f r ↔ tendsto (λn:ℕ, ∑ i in finset.range n, f i) at_top (𝓝 r) :=
begin
  refine ⟨has_sum.tendsto_sum_nat, assume h, _⟩,
  rw [← supr_eq_of_tendsto _ h, ← ennreal.tsum_eq_supr_nat],
  { exact ennreal.summable.has_sum },
  { exact assume s t hst, finset.sum_le_sum_of_subset (finset.range_subset.2 hst) }
end

lemma tendsto_nat_tsum (f : ℕ → ℝ≥0∞) :
  tendsto (λn:ℕ, ∑ i in finset.range n, f i) at_top (𝓝 (∑' n, f n)) :=
by { rw ← has_sum_iff_tendsto_nat, exact ennreal.summable.has_sum }

lemma to_nnreal_apply_of_tsum_ne_top {α : Type*} {f : α → ℝ≥0∞} (hf : ∑' i, f i ≠ ∞) (x : α) :
  (((ennreal.to_nnreal ∘ f) x : ℝ≥0) : ℝ≥0∞) = f x :=
coe_to_nnreal $ ennreal.ne_top_of_tsum_ne_top hf _

lemma summable_to_nnreal_of_tsum_ne_top {α : Type*} {f : α → ℝ≥0∞} (hf : ∑' i, f i ≠ ∞) :
  summable (ennreal.to_nnreal ∘ f) :=
by simpa only [←tsum_coe_ne_top_iff_summable, to_nnreal_apply_of_tsum_ne_top hf] using hf

lemma tendsto_cofinite_zero_of_tsum_ne_top {α} {f : α → ℝ≥0∞} (hf : ∑' x, f x ≠ ∞) :
  tendsto f cofinite (𝓝 0) :=
begin
  have f_ne_top : ∀ n, f n ≠ ∞, from ennreal.ne_top_of_tsum_ne_top hf,
  have h_f_coe : f = λ n, ((f n).to_nnreal : ennreal),
    from funext (λ n, (coe_to_nnreal (f_ne_top n)).symm),
  rw [h_f_coe, ←@coe_zero, tendsto_coe],
  exact nnreal.tendsto_cofinite_zero_of_summable (summable_to_nnreal_of_tsum_ne_top hf),
end

lemma tendsto_at_top_zero_of_tsum_ne_top {f : ℕ → ℝ≥0∞} (hf : ∑' x, f x ≠ ∞) :
  tendsto f at_top (𝓝 0) :=
by { rw ←nat.cofinite_eq_at_top, exact tendsto_cofinite_zero_of_tsum_ne_top hf }

/-- The sum over the complement of a finset tends to `0` when the finset grows to cover the whole
space. This does not need a summability assumption, as otherwise all sums are zero. -/
lemma tendsto_tsum_compl_at_top_zero {α : Type*} {f : α → ℝ≥0∞} (hf : ∑' x, f x ≠ ∞) :
  tendsto (λ (s : finset α), ∑' b : {x // x ∉ s}, f b) at_top (𝓝 0) :=
begin
  lift f to α → ℝ≥0 using ennreal.ne_top_of_tsum_ne_top hf,
  convert ennreal.tendsto_coe.2 (nnreal.tendsto_tsum_compl_at_top_zero f),
  ext1 s,
  rw ennreal.coe_tsum,
  exact nnreal.summable_comp_injective (tsum_coe_ne_top_iff_summable.1 hf) subtype.coe_injective
end

protected lemma tsum_apply {ι α : Type*} {f : ι → α → ℝ≥0∞} {x : α} :
  (∑' i, f i) x = ∑' i, f i x :=
tsum_apply $ pi.summable.mpr $ λ _, ennreal.summable

lemma tsum_sub {f : ℕ → ℝ≥0∞} {g : ℕ → ℝ≥0∞} (h₁ : ∑' i, g i ≠ ∞) (h₂ : g ≤ f) :
  ∑' i, (f i - g i) = (∑' i, f i) - (∑' i, g i) :=
begin
  have h₃: ∑' i, (f i - g i) = ∑' i, (f i - g i + g i) - ∑' i, g i,
  { rw [ennreal.tsum_add, add_sub_self h₁]},
  have h₄:(λ i, (f i - g i) + (g i)) = f,
  { ext n, rw tsub_add_cancel_of_le (h₂ n)},
  rw h₄ at h₃, apply h₃,
end

lemma tsum_mono_subtype (f : α → ℝ≥0∞) {s t : set α} (h : s ⊆ t) :
  ∑' (x : s), f x ≤ ∑' (x : t), f x :=
begin
  simp only [tsum_subtype],
  apply ennreal.tsum_le_tsum,
  assume x,
  split_ifs,
  { exact le_rfl },
  { exact (h_2 (h h_1)).elim },
  { exact zero_le _ },
  { exact le_rfl }
end

lemma tsum_union_le (f : α → ℝ≥0∞) (s t : set α) :
  ∑' (x : s ∪ t), f x ≤ ∑' (x : s), f x + ∑' (x : t), f x :=
calc ∑' (x : s ∪ t), f x = ∑' (x : s ∪ (t \ s)), f x :
  by { apply tsum_congr_subtype, rw union_diff_self }
... = ∑' (x : s), f x + ∑' (x : t \ s), f x :
  tsum_union_disjoint disjoint_diff ennreal.summable ennreal.summable
... ≤ ∑' (x : s), f x + ∑' (x : t), f x :
  add_le_add le_rfl (tsum_mono_subtype _ (diff_subset _ _))

lemma tsum_bUnion_le {ι : Type*} (f : α → ℝ≥0∞) (s : finset ι) (t : ι → set α) :
  ∑' (x : ⋃ (i ∈ s), t i), f x ≤ ∑ i in s, ∑' (x : t i), f x :=
begin
  classical,
  induction s using finset.induction_on with i s hi ihs h, { simp },
  have : (⋃ (j ∈ insert i s), t j) = t i ∪ (⋃ (j ∈ s), t j), by simp,
  rw tsum_congr_subtype f this,
  calc ∑' (x : (t i ∪ (⋃ (j ∈ s), t j))), f x ≤
  ∑' (x : t i), f x + ∑' (x : ⋃ (j ∈ s), t j), f x : tsum_union_le _ _ _
  ... ≤ ∑' (x : t i), f x + ∑ i in s, ∑' (x : t i), f x : add_le_add le_rfl ihs
  ... = ∑ j in insert i s, ∑' (x : t j), f x : (finset.sum_insert hi).symm
end

lemma tsum_Union_le {ι : Type*} [fintype ι] (f : α → ℝ≥0∞) (t : ι → set α) :
  ∑' (x : ⋃ i, t i), f x ≤ ∑ i, ∑' (x : t i), f x :=
begin
  classical,
  have : (⋃ i, t i) = (⋃ (i ∈ (finset.univ : finset ι)), t i), by simp,
  rw tsum_congr_subtype f this,
  exact tsum_bUnion_le _ _ _
end

end tsum

lemma tendsto_to_real_iff {ι} {fi : filter ι} {f : ι → ℝ≥0∞} (hf : ∀ i, f i ≠ ∞) {x : ℝ≥0∞}
  (hx : x ≠ ∞) :
  fi.tendsto (λ n, (f n).to_real) (𝓝 x.to_real) ↔ fi.tendsto f (𝓝 x) :=
begin
  refine ⟨λ h, _, λ h, tendsto.comp (ennreal.tendsto_to_real hx) h⟩,
  have h_eq : f = (λ n, ennreal.of_real (f n).to_real),
    by { ext1 n, rw ennreal.of_real_to_real (hf n), },
  rw [h_eq, ← ennreal.of_real_to_real hx],
  exact ennreal.tendsto_of_real h,
end

lemma tsum_coe_ne_top_iff_summable_coe {f : α → ℝ≥0} :
  ∑' a, (f a : ℝ≥0∞) ≠ ∞ ↔ summable (λ a, (f a : ℝ)) :=
begin
  rw nnreal.summable_coe,
  exact tsum_coe_ne_top_iff_summable,
end

lemma tsum_coe_eq_top_iff_not_summable_coe {f : α → ℝ≥0} :
  ∑' a, (f a : ℝ≥0∞) = ∞ ↔ ¬ summable (λ a, (f a : ℝ)) :=
begin
  rw [← @not_not (∑' a, ↑(f a) = ⊤)],
  exact not_congr tsum_coe_ne_top_iff_summable_coe
end

lemma has_sum_to_real {f : α → ℝ≥0∞} (hsum : ∑' x, f x ≠ ∞) :
  has_sum (λ x, (f x).to_real) (∑' x, (f x).to_real) :=
begin
  lift f to α → ℝ≥0 using ennreal.ne_top_of_tsum_ne_top hsum,
  simp only [coe_to_real, ← nnreal.coe_tsum, nnreal.has_sum_coe],
  exact (tsum_coe_ne_top_iff_summable.1 hsum).has_sum
end

lemma summable_to_real {f : α → ℝ≥0∞} (hsum : ∑' x, f x ≠ ∞) :
  summable (λ x, (f x).to_real) :=
(has_sum_to_real hsum).summable

end ennreal

namespace nnreal

open_locale nnreal

lemma tsum_eq_to_nnreal_tsum {f : β → ℝ≥0} :
  (∑' b, f b) = (∑' b, (f b : ℝ≥0∞)).to_nnreal :=
begin
  by_cases h : summable f,
  { rw [← ennreal.coe_tsum h, ennreal.to_nnreal_coe] },
  { have A := tsum_eq_zero_of_not_summable h,
    simp only [← ennreal.tsum_coe_ne_top_iff_summable, not_not] at h,
    simp only [h, ennreal.top_to_nnreal, A] }
end

/-- Comparison test of convergence of `ℝ≥0`-valued series. -/
lemma exists_le_has_sum_of_le {f g : β → ℝ≥0} {r : ℝ≥0}
  (hgf : ∀b, g b ≤ f b) (hfr : has_sum f r) : ∃p≤r, has_sum g p :=
have ∑'b, (g b : ℝ≥0∞) ≤ r,
begin
  refine has_sum_le (assume b, _) ennreal.summable.has_sum (ennreal.has_sum_coe.2 hfr),
  exact ennreal.coe_le_coe.2 (hgf _)
end,
let ⟨p, eq, hpr⟩ := ennreal.le_coe_iff.1 this in
⟨p, hpr, ennreal.has_sum_coe.1 $ eq ▸ ennreal.summable.has_sum⟩

/-- Comparison test of convergence of `ℝ≥0`-valued series. -/
lemma summable_of_le {f g : β → ℝ≥0} (hgf : ∀b, g b ≤ f b) : summable f → summable g
| ⟨r, hfr⟩ := let ⟨p, _, hp⟩ := exists_le_has_sum_of_le hgf hfr in hp.summable

/-- A series of non-negative real numbers converges to `r` in the sense of `has_sum` if and only if
the sequence of partial sum converges to `r`. -/
lemma has_sum_iff_tendsto_nat {f : ℕ → ℝ≥0} {r : ℝ≥0} :
  has_sum f r ↔ tendsto (λn:ℕ, ∑ i in finset.range n, f i) at_top (𝓝 r) :=
begin
  rw [← ennreal.has_sum_coe, ennreal.has_sum_iff_tendsto_nat],
  simp only [ennreal.coe_finset_sum.symm],
  exact ennreal.tendsto_coe
end

lemma not_summable_iff_tendsto_nat_at_top {f : ℕ → ℝ≥0} :
  ¬ summable f ↔ tendsto (λ n : ℕ, ∑ i in finset.range n, f i) at_top at_top :=
begin
  split,
  { intros h,
    refine ((tendsto_of_monotone _).resolve_right h).comp _,
    exacts [finset.sum_mono_set _, tendsto_finset_range] },
  { rintros hnat ⟨r, hr⟩,
    exact not_tendsto_nhds_of_tendsto_at_top hnat _ (has_sum_iff_tendsto_nat.1 hr) }
end

lemma summable_iff_not_tendsto_nat_at_top {f : ℕ → ℝ≥0} :
  summable f ↔ ¬ tendsto (λ n : ℕ, ∑ i in finset.range n, f i) at_top at_top :=
by rw [← not_iff_not, not_not, not_summable_iff_tendsto_nat_at_top]

lemma summable_of_sum_range_le {f : ℕ → ℝ≥0} {c : ℝ≥0}
  (h : ∀ n, ∑ i in finset.range n, f i ≤ c) : summable f :=
begin
  apply summable_iff_not_tendsto_nat_at_top.2 (λ H, _),
  rcases exists_lt_of_tendsto_at_top H 0 c with ⟨n, -, hn⟩,
  exact lt_irrefl _ (hn.trans_le (h n)),
end

lemma tsum_le_of_sum_range_le {f : ℕ → ℝ≥0} {c : ℝ≥0}
  (h : ∀ n, ∑ i in finset.range n, f i ≤ c) : ∑' n, f n ≤ c :=
le_of_tendsto' (has_sum_iff_tendsto_nat.1 (summable_of_sum_range_le h).has_sum) h

lemma tsum_comp_le_tsum_of_inj {β : Type*} {f : α → ℝ≥0} (hf : summable f)
  {i : β → α} (hi : function.injective i) : ∑' x, f (i x) ≤ ∑' x, f x :=
tsum_le_tsum_of_inj i hi (λ c hc, zero_le _) (λ b, le_rfl) (summable_comp_injective hf hi) hf

lemma summable_sigma {β : Π x : α, Type*} {f : (Σ x, β x) → ℝ≥0} :
  summable f ↔ (∀ x, summable (λ y, f ⟨x, y⟩)) ∧ summable (λ x, ∑' y, f ⟨x, y⟩) :=
begin
  split,
  { simp only [← nnreal.summable_coe, nnreal.coe_tsum],
    exact λ h, ⟨h.sigma_factor, h.sigma⟩ },
  { rintro ⟨h₁, h₂⟩,
    simpa only [← ennreal.tsum_coe_ne_top_iff_summable, ennreal.tsum_sigma', ennreal.coe_tsum, h₁]
      using h₂ }
end

lemma indicator_summable {f : α → ℝ≥0} (hf : summable f) (s : set α) :
  summable (s.indicator f) :=
begin
  refine nnreal.summable_of_le (λ a, le_trans (le_of_eq (s.indicator_apply f a)) _) hf,
  split_ifs,
  exact le_refl (f a),
  exact zero_le_coe,
end

lemma tsum_indicator_ne_zero {f : α → ℝ≥0} (hf : summable f) {s : set α} (h : ∃ a ∈ s, f a ≠ 0) :
  ∑' x, (s.indicator f) x ≠ 0 :=
λ h', let ⟨a, ha, hap⟩ := h in
  hap (trans (set.indicator_apply_eq_self.mpr (absurd ha)).symm
    (((tsum_eq_zero_iff (indicator_summable hf s)).1 h') a))

open finset

/-- For `f : ℕ → ℝ≥0`, then `∑' k, f (k + i)` tends to zero. This does not require a summability
assumption on `f`, as otherwise all sums are zero. -/
lemma tendsto_sum_nat_add (f : ℕ → ℝ≥0) : tendsto (λ i, ∑' k, f (k + i)) at_top (𝓝 0) :=
begin
  rw ← tendsto_coe,
  convert tendsto_sum_nat_add (λ i, (f i : ℝ)),
  norm_cast,
end

lemma has_sum_lt {f g : α → ℝ≥0} {sf sg : ℝ≥0} {i : α} (h : ∀ (a : α), f a ≤ g a) (hi : f i < g i)
  (hf : has_sum f sf) (hg : has_sum g sg) : sf < sg :=
begin
  have A : ∀ (a : α), (f a : ℝ) ≤ g a := λ a, nnreal.coe_le_coe.2 (h a),
  have : (sf : ℝ) < sg :=
    has_sum_lt A (nnreal.coe_lt_coe.2 hi) (has_sum_coe.2 hf) (has_sum_coe.2 hg),
  exact nnreal.coe_lt_coe.1 this
end

@[mono] lemma has_sum_strict_mono
  {f g : α → ℝ≥0} {sf sg : ℝ≥0} (hf : has_sum f sf) (hg : has_sum g sg) (h : f < g) : sf < sg :=
let ⟨hle, i, hi⟩ := pi.lt_def.mp h in has_sum_lt hle hi hf hg

lemma tsum_lt_tsum {f g : α → ℝ≥0} {i : α} (h : ∀ (a : α), f a ≤ g a) (hi : f i < g i)
  (hg : summable g) : ∑' n, f n < ∑' n, g n :=
has_sum_lt h hi (summable_of_le h hg).has_sum hg.has_sum

@[mono] lemma tsum_strict_mono {f g : α → ℝ≥0} (hg : summable g) (h : f < g) :
  ∑' n, f n < ∑' n, g n :=
let ⟨hle, i, hi⟩ := pi.lt_def.mp h in tsum_lt_tsum hle hi hg

lemma tsum_pos {g : α → ℝ≥0} (hg : summable g) (i : α) (hi : 0 < g i) :
  0 < ∑' b, g b :=
by { rw ← tsum_zero, exact tsum_lt_tsum (λ a, zero_le _) hi hg }

end nnreal

namespace ennreal

lemma tsum_to_real_eq
  {f : α → ℝ≥0∞} (hf : ∀ a, f a ≠ ∞) :
  (∑' a, f a).to_real = ∑' a, (f a).to_real :=
begin
  lift f to α → ℝ≥0 using hf,
  have : (∑' (a : α), (f a : ℝ≥0∞)).to_real =
    ((∑' (a : α), (f a : ℝ≥0∞)).to_nnreal : ℝ≥0∞).to_real,
  { rw [ennreal.coe_to_real], refl },
  rw [this, ← nnreal.tsum_eq_to_nnreal_tsum, ennreal.coe_to_real],
  exact nnreal.coe_tsum
end

lemma tendsto_sum_nat_add (f : ℕ → ℝ≥0∞) (hf : ∑' i, f i ≠ ∞) :
  tendsto (λ i, ∑' k, f (k + i)) at_top (𝓝 0) :=
begin
  lift f to ℕ → ℝ≥0 using ennreal.ne_top_of_tsum_ne_top hf,
  replace hf : summable f := tsum_coe_ne_top_iff_summable.1 hf,
  simp only [← ennreal.coe_tsum, nnreal.summable_nat_add _ hf, ← ennreal.coe_zero],
  exact_mod_cast nnreal.tendsto_sum_nat_add f
end

end ennreal

lemma tsum_comp_le_tsum_of_inj {β : Type*} {f : α → ℝ} (hf : summable f) (hn : ∀ a, 0 ≤ f a)
  {i : β → α} (hi : function.injective i) : tsum (f ∘ i) ≤ tsum f :=
begin
  lift f to α → ℝ≥0 using hn,
  rw nnreal.summable_coe at hf,
  simpa only [(∘), ← nnreal.coe_tsum] using nnreal.tsum_comp_le_tsum_of_inj hf hi
end

/-- Comparison test of convergence of series of non-negative real numbers. -/
lemma summable_of_nonneg_of_le {f g : β → ℝ}
  (hg : ∀b, 0 ≤ g b) (hgf : ∀b, g b ≤ f b) (hf : summable f) : summable g :=
begin
  lift f to β → ℝ≥0 using λ b, (hg b).trans (hgf b),
  lift g to β → ℝ≥0 using hg,
  rw nnreal.summable_coe at hf ⊢,
  exact nnreal.summable_of_le (λ b, nnreal.coe_le_coe.1 (hgf b)) hf
end

/-- A series of non-negative real numbers converges to `r` in the sense of `has_sum` if and only if
the sequence of partial sum converges to `r`. -/
lemma has_sum_iff_tendsto_nat_of_nonneg {f : ℕ → ℝ} (hf : ∀i, 0 ≤ f i) (r : ℝ) :
  has_sum f r ↔ tendsto (λ n : ℕ, ∑ i in finset.range n, f i) at_top (𝓝 r) :=
begin
  lift f to ℕ → ℝ≥0 using hf,
  simp only [has_sum, ← nnreal.coe_sum, nnreal.tendsto_coe'],
  exact exists_congr (λ hr, nnreal.has_sum_iff_tendsto_nat)
end

lemma ennreal.of_real_tsum_of_nonneg {f : α → ℝ} (hf_nonneg : ∀ n, 0 ≤ f n) (hf : summable f) :
  ennreal.of_real (∑' n, f n) = ∑' n, ennreal.of_real (f n) :=
by simp_rw [ennreal.of_real, ennreal.tsum_coe_eq
  (nnreal.has_sum_real_to_nnreal_of_nonneg hf_nonneg hf)]

lemma not_summable_iff_tendsto_nat_at_top_of_nonneg {f : ℕ → ℝ} (hf : ∀ n, 0 ≤ f n) :
  ¬ summable f ↔ tendsto (λ n : ℕ, ∑ i in finset.range n, f i) at_top at_top :=
begin
  lift f to ℕ → ℝ≥0 using hf,
  exact_mod_cast nnreal.not_summable_iff_tendsto_nat_at_top
end

lemma summable_iff_not_tendsto_nat_at_top_of_nonneg {f : ℕ → ℝ} (hf : ∀ n, 0 ≤ f n) :
  summable f ↔ ¬ tendsto (λ n : ℕ, ∑ i in finset.range n, f i) at_top at_top :=
by rw [← not_iff_not, not_not, not_summable_iff_tendsto_nat_at_top_of_nonneg hf]

lemma summable_sigma_of_nonneg {β : Π x : α, Type*} {f : (Σ x, β x) → ℝ} (hf : ∀ x, 0 ≤ f x) :
  summable f ↔ (∀ x, summable (λ y, f ⟨x, y⟩)) ∧ summable (λ x, ∑' y, f ⟨x, y⟩) :=
by { lift f to (Σ x, β x) → ℝ≥0 using hf, exact_mod_cast nnreal.summable_sigma }

lemma summable_of_sum_le {ι : Type*} {f : ι → ℝ} {c : ℝ} (hf : 0 ≤ f)
  (h : ∀ u : finset ι, ∑ x in u, f x ≤ c) :
  summable f :=
⟨ ⨆ u : finset ι, ∑ x in u, f x,
  tendsto_at_top_csupr (finset.sum_mono_set_of_nonneg hf) ⟨c, λ y ⟨u, hu⟩, hu ▸ h u⟩ ⟩

lemma summable_of_sum_range_le {f : ℕ → ℝ} {c : ℝ} (hf : ∀ n, 0 ≤ f n)
  (h : ∀ n, ∑ i in finset.range n, f i ≤ c) : summable f :=
begin
  apply (summable_iff_not_tendsto_nat_at_top_of_nonneg hf).2 (λ H, _),
  rcases exists_lt_of_tendsto_at_top H 0 c with ⟨n, -, hn⟩,
  exact lt_irrefl _ (hn.trans_le (h n)),
end

lemma tsum_le_of_sum_range_le {f : ℕ → ℝ} {c : ℝ} (hf : ∀ n, 0 ≤ f n)
  (h : ∀ n, ∑ i in finset.range n, f i ≤ c) : ∑' n, f n ≤ c :=
le_of_tendsto' ((has_sum_iff_tendsto_nat_of_nonneg hf _).1
  (summable_of_sum_range_le hf h).has_sum) h

/-- If a sequence `f` with non-negative terms is dominated by a sequence `g` with summable
series and at least one term of `f` is strictly smaller than the corresponding term in `g`,
then the series of `f` is strictly smaller than the series of `g`. -/
lemma tsum_lt_tsum_of_nonneg {i : ℕ} {f g : ℕ → ℝ}
  (h0 : ∀ (b : ℕ), 0 ≤ f b) (h : ∀ (b : ℕ), f b ≤ g b) (hi : f i < g i) (hg : summable g) :
  ∑' n, f n < ∑' n, g n :=
tsum_lt_tsum h hi (summable_of_nonneg_of_le h0 h hg) hg

section
variables [emetric_space β]
open ennreal filter emetric

/-- In an emetric ball, the distance between points is everywhere finite -/
lemma edist_ne_top_of_mem_ball {a : β} {r : ℝ≥0∞} (x y : ball a r) : edist x.1 y.1 ≠ ⊤ :=
lt_top_iff_ne_top.1 $
calc edist x y ≤ edist a x + edist a y : edist_triangle_left x.1 y.1 a
  ... < r + r : by rw [edist_comm a x, edist_comm a y]; exact add_lt_add x.2 y.2
  ... ≤ ⊤ : le_top

/-- Each ball in an extended metric space gives us a metric space, as the edist
is everywhere finite. -/
def metric_space_emetric_ball (a : β) (r : ℝ≥0∞) : metric_space (ball a r) :=
emetric_space.to_metric_space edist_ne_top_of_mem_ball

local attribute [instance] metric_space_emetric_ball

lemma nhds_eq_nhds_emetric_ball (a x : β) (r : ℝ≥0∞) (h : x ∈ ball a r) :
  𝓝 x = map (coe : ball a r → β) (𝓝 ⟨x, h⟩) :=
(map_nhds_subtype_coe_eq _ $ is_open.mem_nhds emetric.is_open_ball h).symm
end

section
variable [pseudo_emetric_space α]
open emetric

lemma tendsto_iff_edist_tendsto_0 {l : filter β} {f : β → α} {y : α} :
  tendsto f l (𝓝 y) ↔ tendsto (λ x, edist (f x) y) l (𝓝 0) :=
by simp only [emetric.nhds_basis_eball.tendsto_right_iff, emetric.mem_ball,
  @tendsto_order ℝ≥0∞ β _ _, forall_prop_of_false ennreal.not_lt_zero, forall_const, true_and]

/-- Yet another metric characterization of Cauchy sequences on integers. This one is often the
most efficient. -/
lemma emetric.cauchy_seq_iff_le_tendsto_0 [nonempty β] [semilattice_sup β] {s : β → α} :
  cauchy_seq s ↔ (∃ (b: β → ℝ≥0∞), (∀ n m N : β, N ≤ n → N ≤ m → edist (s n) (s m) ≤ b N)
                    ∧ (tendsto b at_top (𝓝 0))) :=
⟨begin
  assume hs,
  rw emetric.cauchy_seq_iff at hs,
  /- `s` is Cauchy sequence. The sequence `b` will be constructed by taking
  the supremum of the distances between `s n` and `s m` for `n m ≥ N`-/
  let b := λN, Sup ((λ(p : β × β), edist (s p.1) (s p.2))''{p | p.1 ≥ N ∧ p.2 ≥ N}),
  --Prove that it bounds the distances of points in the Cauchy sequence
  have C : ∀ n m N, N ≤ n → N ≤ m → edist (s n) (s m) ≤ b N,
  { refine λm n N hm hn, le_Sup _,
    use (prod.mk m n),
    simp only [and_true, eq_self_iff_true, set.mem_set_of_eq],
    exact ⟨hm, hn⟩ },
  --Prove that it tends to `0`, by using the Cauchy property of `s`
  have D : tendsto b at_top (𝓝 0),
  { refine tendsto_order.2 ⟨λa ha, absurd ha (ennreal.not_lt_zero), λε εpos, _⟩,
    rcases exists_between εpos with ⟨δ, δpos, δlt⟩,
    rcases hs δ δpos with ⟨N, hN⟩,
    refine filter.mem_at_top_sets.2 ⟨N, λn hn, _⟩,
    have : b n ≤ δ := Sup_le begin
      simp only [and_imp, set.mem_image, set.mem_set_of_eq, exists_imp_distrib, prod.exists],
      intros d p q hp hq hd,
      rw ← hd,
      exact le_of_lt (hN p (le_trans hn hp) q (le_trans hn hq))
    end,
    simpa using lt_of_le_of_lt this δlt },
  -- Conclude
  exact ⟨b, ⟨C, D⟩⟩
end,
begin
  rintros ⟨b, ⟨b_bound, b_lim⟩⟩,
  /-b : ℕ → ℝ, b_bound : ∀ (n m N : ℕ), N ≤ n → N ≤ m → edist (s n) (s m) ≤ b N,
    b_lim : tendsto b at_top (𝓝 0)-/
  refine emetric.cauchy_seq_iff.2 (λε εpos, _),
  have : ∀ᶠ n in at_top, b n < ε := (tendsto_order.1 b_lim ).2 _ εpos,
  rcases filter.mem_at_top_sets.1 this with ⟨N, hN⟩,
  exact ⟨N, λ m hm n hn, calc
    edist (s m) (s n) ≤ b N : b_bound m n N hm hn
    ... < ε : (hN _ (le_refl N)) ⟩
end⟩

lemma continuous_of_le_add_edist {f : α → ℝ≥0∞} (C : ℝ≥0∞)
  (hC : C ≠ ⊤) (h : ∀x y, f x ≤ f y + C * edist x y) : continuous f :=
begin
  rcases eq_or_ne C 0 with (rfl|C0),
  { simp only [zero_mul, add_zero] at h,
    exact continuous_of_const (λ x y, le_antisymm (h _ _) (h _ _)) },
  { refine continuous_iff_continuous_at.2 (λ x, _),
    by_cases hx : f x = ∞,
    { have : f =ᶠ[𝓝 x] (λ _, ∞),
      { filter_upwards [emetric.ball_mem_nhds x ennreal.coe_lt_top],
        refine λ y (hy : edist y x < ⊤), _, rw edist_comm at hy,
        simpa [hx, hC, hy.ne] using h x y },
      exact this.continuous_at },
    { refine (ennreal.tendsto_nhds hx).2 (λ ε (ε0 : 0 < ε), _),
      filter_upwards [emetric.closed_ball_mem_nhds x (ennreal.div_pos_iff.2 ⟨ε0.ne', hC⟩)],
      have hεC : C * (ε / C) = ε := ennreal.mul_div_cancel' C0 hC,
      refine λ y (hy : edist y x ≤ ε / C), ⟨tsub_le_iff_right.2 _, _⟩,
      { rw edist_comm at hy,
        calc f x ≤ f y + C * edist x y : h x y
        ... ≤ f y + C * (ε / C) : add_le_add_left (mul_le_mul_left' hy C) (f y)
        ... = f y + ε : by rw hεC },
      { calc f y ≤ f x + C * edist y x : h y x
        ... ≤ f x + C * (ε / C) : add_le_add_left (mul_le_mul_left' hy C) (f x)
        ... = f x + ε : by rw hεC } } }
end

theorem continuous_edist : continuous (λp:α×α, edist p.1 p.2) :=
begin
  apply continuous_of_le_add_edist 2 (by norm_num),
  rintros ⟨x, y⟩ ⟨x', y'⟩,
  calc edist x y ≤ edist x x' + edist x' y' + edist y' y : edist_triangle4 _ _ _ _
    ... = edist x' y' + (edist x x' + edist y y') : by simp [edist_comm]; cc
    ... ≤ edist x' y' + (edist (x, y) (x', y') + edist (x, y) (x', y')) :
      add_le_add_left (add_le_add (le_max_left _ _) (le_max_right _ _)) _
    ... = edist x' y' + 2 * edist (x, y) (x', y') : by rw [← mul_two, mul_comm]
end

@[continuity] theorem continuous.edist [topological_space β] {f g : β → α}
  (hf : continuous f) (hg : continuous g) : continuous (λb, edist (f b) (g b)) :=
continuous_edist.comp (hf.prod_mk hg : _)

theorem filter.tendsto.edist {f g : β → α} {x : filter β} {a b : α}
  (hf : tendsto f x (𝓝 a)) (hg : tendsto g x (𝓝 b)) :
  tendsto (λx, edist (f x) (g x)) x (𝓝 (edist a b)) :=
(continuous_edist.tendsto (a, b)).comp (hf.prod_mk_nhds hg)

lemma cauchy_seq_of_edist_le_of_tsum_ne_top {f : ℕ → α} (d : ℕ → ℝ≥0∞)
  (hf : ∀ n, edist (f n) (f n.succ) ≤ d n) (hd : tsum d ≠ ∞) :
  cauchy_seq f :=
begin
  lift d to (ℕ → nnreal) using (λ i, ennreal.ne_top_of_tsum_ne_top hd i),
  rw ennreal.tsum_coe_ne_top_iff_summable at hd,
  exact cauchy_seq_of_edist_le_of_summable d hf hd
end

lemma emetric.is_closed_ball {a : α} {r : ℝ≥0∞} : is_closed (closed_ball a r) :=
is_closed_le (continuous_id.edist continuous_const) continuous_const

@[simp] lemma emetric.diam_closure (s : set α) : diam (closure s) = diam s :=
begin
  refine le_antisymm (diam_le $ λ x hx y hy, _) (diam_mono subset_closure),
  have : edist x y ∈ closure (Iic (diam s)),
    from  map_mem_closure2 (@continuous_edist α _) hx hy (λ _ _, edist_le_diam_of_mem),
  rwa closure_Iic at this
end

@[simp] lemma metric.diam_closure {α : Type*} [pseudo_metric_space α] (s : set α) :
  metric.diam (closure s) = diam s :=
by simp only [metric.diam, emetric.diam_closure]

lemma is_closed_set_of_lipschitz_on_with {α β} [pseudo_emetric_space α] [pseudo_emetric_space β]
  (K : ℝ≥0) (s : set α) :
  is_closed {f : α → β | lipschitz_on_with K f s} :=
begin
  simp only [lipschitz_on_with, set_of_forall],
  refine is_closed_bInter (λ x hx, is_closed_bInter $ λ y hy, is_closed_le _ _),
  exacts [continuous.edist (continuous_apply x) (continuous_apply y), continuous_const]
end

lemma is_closed_set_of_lipschitz_with {α β} [pseudo_emetric_space α] [pseudo_emetric_space β]
  (K : ℝ≥0) :
  is_closed {f : α → β | lipschitz_with K f} :=
by simp only [← lipschitz_on_univ, is_closed_set_of_lipschitz_on_with]

namespace real

/-- For a bounded set `s : set ℝ`, its `emetric.diam` is equal to `Sup s - Inf s` reinterpreted as
`ℝ≥0∞`. -/
lemma ediam_eq {s : set ℝ} (h : bounded s) :
  emetric.diam s = ennreal.of_real (Sup s - Inf s) :=
begin
  rcases eq_empty_or_nonempty s with rfl|hne, { simp },
  refine le_antisymm (metric.ediam_le_of_forall_dist_le $ λ x hx y hy, _) _,
  { have := real.subset_Icc_Inf_Sup_of_bounded h,
    exact real.dist_le_of_mem_Icc (this hx) (this hy) },
  { apply ennreal.of_real_le_of_le_to_real,
    rw [← metric.diam, ← metric.diam_closure],
    have h' := real.bounded_iff_bdd_below_bdd_above.1 h,
    calc Sup s - Inf s ≤ dist (Sup s) (Inf s) : le_abs_self _
                   ... ≤ diam (closure s)     :
      dist_le_diam_of_mem h.closure (cSup_mem_closure hne h'.2) (cInf_mem_closure hne h'.1) }
end

/-- For a bounded set `s : set ℝ`, its `metric.diam` is equal to `Sup s - Inf s`. -/
lemma diam_eq {s : set ℝ} (h : bounded s) : metric.diam s = Sup s - Inf s :=
begin
  rw [metric.diam, real.ediam_eq h, ennreal.to_real_of_real],
  rw real.bounded_iff_bdd_below_bdd_above at h,
  exact sub_nonneg.2 (real.Inf_le_Sup s h.1 h.2)
end

@[simp] lemma ediam_Ioo (a b : ℝ) :
  emetric.diam (Ioo a b) = ennreal.of_real (b - a) :=
begin
  rcases le_or_lt b a with h|h,
  { simp [h] },
  { rw [real.ediam_eq (bounded_Ioo _ _), cSup_Ioo h, cInf_Ioo h] },
end

@[simp] lemma ediam_Icc (a b : ℝ) :
  emetric.diam (Icc a b) = ennreal.of_real (b - a) :=
begin
  rcases le_or_lt a b with h|h,
  { rw [real.ediam_eq (bounded_Icc _ _), cSup_Icc h, cInf_Icc h] },
  { simp [h, h.le] }
end

@[simp] lemma ediam_Ico (a b : ℝ) :
  emetric.diam (Ico a b) = ennreal.of_real (b - a) :=
le_antisymm (ediam_Icc a b ▸ diam_mono Ico_subset_Icc_self)
  (ediam_Ioo a b ▸ diam_mono Ioo_subset_Ico_self)

@[simp] lemma ediam_Ioc (a b : ℝ) :
  emetric.diam (Ioc a b) = ennreal.of_real (b - a) :=
le_antisymm (ediam_Icc a b ▸ diam_mono Ioc_subset_Icc_self)
  (ediam_Ioo a b ▸ diam_mono Ioo_subset_Ioc_self)

end real

/-- If `edist (f n) (f (n+1))` is bounded above by a function `d : ℕ → ℝ≥0∞`,
then the distance from `f n` to the limit is bounded by `∑'_{k=n}^∞ d k`. -/
lemma edist_le_tsum_of_edist_le_of_tendsto {f : ℕ → α} (d : ℕ → ℝ≥0∞)
  (hf : ∀ n, edist (f n) (f n.succ) ≤ d n)
  {a : α} (ha : tendsto f at_top (𝓝 a)) (n : ℕ) :
  edist (f n) a ≤ ∑' m, d (n + m) :=
begin
  refine le_of_tendsto (tendsto_const_nhds.edist ha)
    (mem_at_top_sets.2 ⟨n, λ m hnm, _⟩),
  refine le_trans (edist_le_Ico_sum_of_edist_le hnm (λ k _ _, hf k)) _,
  rw [finset.sum_Ico_eq_sum_range],
  exact sum_le_tsum _ (λ _ _, zero_le _) ennreal.summable
end

/-- If `edist (f n) (f (n+1))` is bounded above by a function `d : ℕ → ℝ≥0∞`,
then the distance from `f 0` to the limit is bounded by `∑'_{k=0}^∞ d k`. -/
lemma edist_le_tsum_of_edist_le_of_tendsto₀ {f : ℕ → α} (d : ℕ → ℝ≥0∞)
  (hf : ∀ n, edist (f n) (f n.succ) ≤ d n)
  {a : α} (ha : tendsto f at_top (𝓝 a)) :
  edist (f 0) a ≤ ∑' m, d m :=
by simpa using edist_le_tsum_of_edist_le_of_tendsto d hf ha 0

end --section


/-
Copyright (c) 2019 The Flypitch Project. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author(s): Jesse Michael Han, Floris van Doorn

Parsing formulas from Lean expressions.
-/

import .abel .fol tactic data.list

universe u

/- toy example by Mario -/
section fmla
@[derive has_reflect]
inductive fmla
| var : nat → fmla
| pi : fmla → fmla

meta def to_fmla : pexpr → option fmla
| (expr.var n) := some (fmla.var n)
| (expr.pi _ _ _ e) := fmla.pi <$> to_fmla e
| _ := none

end fmla

namespace tactic
namespace interactive

section parse_fmla
open interactive interactive.types expr

meta def parse_fmla (e : parse texpr) : tactic unit :=
do f <- to_fmla e, tactic.exact `(f)

end parse_fmla
end interactive
end tactic

def foo : fmla := by parse_fmla (∀ x : ℕ, x)
-- #print foo -- fmla.pi (fmla.var 0)
/- end toy example -/

open fol
meta instance preterm.reflect {L : Language.{0}} [reflected L] [∀ n, has_reflect (L.functions n)] :
  ∀{n}, has_reflect (preterm L n)
| _ &k          := `(&k)
| _ (func f)    := `(func f)
| _ (app t₁ t₂) := (`(λ x y, app x y).subst (preterm.reflect t₁)).subst (preterm.reflect t₂)

meta instance preformula.reflect {L : Language.{0}} [reflected L] [∀ n, has_reflect (L.functions n)]
  [∀ n, has_reflect (L.relations n)] : ∀{n}, has_reflect (preformula L n)
| _ falsum           := `(falsum)
| _ (equal t₁ t₂)    := (`(λ x₁ x₂, equal x₁ x₂).subst (preterm.reflect t₁)).subst (preterm.reflect t₂)
| _ (rel R)          := `(λ x, preformula.rel x).subst `(R)
| _ (apprel f t)     := (`(λ x₁ x₂, apprel x₁ x₂).subst (preformula.reflect f)).subst (preterm.reflect t)
| _ (imp f₁ f₂)      := (`(λ x₁ x₂, imp x₁ x₂).subst (preformula.reflect f₁)).subst (preformula.reflect f₂)
| _ (all f)          := `(λ x, all x).subst (preformula.reflect f)

meta instance L_empty.reflect : reflected L_empty := by apply_instance
meta instance L_empty_functions_reflect : ∀ n, has_reflect (L_empty.functions n) :=
λ _ _, empty.elim ‹_›

meta instance L_empty_relations_reflect : ∀ n, has_reflect (L_empty.relations n) :=
λ _ _, empty.elim ‹_›

section L_empty_parse

meta def to_term_empty : Π {n : ℕ}, expr → option (preterm L_empty n)
| 0     (expr.var k)    := some (var k)
| _     _               := none

-- TODO(jesse) insert a case analysis on whether e' : Prop to determine whether or not to use imp
meta def to_formula_empty : Π {n : ℕ}, expr → option (preformula L_empty n)
| 0 (expr.pi _ _ e' e)     := ((to_formula_empty e) >>= (λ f, return (all f)))
| 0 `(%%a = %%b)          := do e₁ <- to_term_empty a, e₂ <- to_term_empty b,
                               return $ equal e₁ e₂
-- | 0 `(%%a → %%b)          := do e₁ <- to_formula_empty a, e₂ <- to_formula_empty b,
--                                return $ imp e₁ e₂ -- equation compiler thinks this case
                                   -- is redundant since a → b compiles to Π _ : a, b
| 0 `(false)              := return (falsum)
| 0  _                    := none
| (n+1) _                 := none

end L_empty_parse
namespace tactic
namespace interactive

open interactive interactive.types expr
meta def parse_formula (t : parse texpr) : tactic unit :=
  do f <- ((@to_formula_empty 0) <$> (to_expr t)),
     match f with
     | (some ϕ) := tactic.exact `(ϕ)
     | none     := tactic.fail "to_formula_empty failed to parse"
     end

end interactive
end tactic

-- meta def test : expr := `(true → true)

-- meta def test' : expr := `(∀ x : ℕ, x = 0)

-- #eval (@expr.to_raw_fmt tt test).to_string

-- #eval (@expr.to_raw_fmt tt test').to_string

constant α : Type

def my_little_formula : preformula L_empty 0 :=
by parse_formula (∀ x : α, x = x)

def my_larger_formula : preformula L_empty 0 :=
by parse_formula (∀ x y : α, (x = y))

-- #reduce my_little_formula -- it works!
-- ∀'(&0 ≃ &0)

-- #print my_larger_formula -- it still works!
-- ∀'∀'(&1 ≃ &0)


/-
Copyright (c) 2019 Sébastien Gouëzel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sébastien Gouëzel
-/
import analysis.asymptotics.asymptotic_equivalent
import analysis.normed_space.add_torsor
import analysis.normed_space.affine_isometry
import analysis.normed_space.operator_norm
import analysis.normed_space.riesz_lemma
import linear_algebra.matrix.to_lin
import topology.algebra.module.finite_dimension
import topology.algebra.infinite_sum.module
import topology.instances.matrix

/-!
# Finite dimensional normed spaces over complete fields

Over a complete nontrivially normed field, in finite dimension, all norms are equivalent and all
linear maps are continuous. Moreover, a finite-dimensional subspace is always complete and closed.

## Main results:

* `finite_dimensional.complete` : a finite-dimensional space over a complete field is complete. This
  is not registered as an instance, as the field would be an unknown metavariable in typeclass
  resolution.
* `submodule.closed_of_finite_dimensional` : a finite-dimensional subspace over a complete field is
  closed
* `finite_dimensional.proper` : a finite-dimensional space over a proper field is proper. This
  is not registered as an instance, as the field would be an unknown metavariable in typeclass
  resolution. It is however registered as an instance for `𝕜 = ℝ` and `𝕜 = ℂ`. As properness
  implies completeness, there is no need to also register `finite_dimensional.complete` on `ℝ` or
  `ℂ`.
* `finite_dimensional_of_is_compact_closed_ball`: Riesz' theorem: if the closed unit ball is
  compact, then the space is finite-dimensional.

## Implementation notes

The fact that all norms are equivalent is not written explicitly, as it would mean having two norms
on a single space, which is not the way type classes work. However, if one has a
finite-dimensional vector space `E` with a norm, and a copy `E'` of this type with another norm,
then the identities from `E` to `E'` and from `E'`to `E` are continuous thanks to
`linear_map.continuous_of_finite_dimensional`. This gives the desired norm equivalence.
-/

universes u v w x

noncomputable theory

open set finite_dimensional topological_space filter asymptotics
open_locale classical big_operators filter topology asymptotics nnreal

namespace linear_isometry

open linear_map

variables {R : Type*} [semiring R]

variables {F E₁ : Type*} [seminormed_add_comm_group F]
  [normed_add_comm_group E₁] [module R E₁]

variables {R₁ : Type*} [field R₁] [module R₁ E₁] [module R₁ F]
  [finite_dimensional R₁ E₁] [finite_dimensional R₁ F]

/-- A linear isometry between finite dimensional spaces of equal dimension can be upgraded
    to a linear isometry equivalence. -/
def to_linear_isometry_equiv
  (li : E₁ →ₗᵢ[R₁] F) (h : finrank R₁ E₁ = finrank R₁ F) : E₁ ≃ₗᵢ[R₁] F :=
{ to_linear_equiv :=
    li.to_linear_map.linear_equiv_of_injective li.injective h,
  norm_map' := li.norm_map' }

@[simp] lemma coe_to_linear_isometry_equiv
  (li : E₁ →ₗᵢ[R₁] F) (h : finrank R₁ E₁ = finrank R₁ F) :
  (li.to_linear_isometry_equiv h : E₁ → F) = li := rfl

@[simp] lemma to_linear_isometry_equiv_apply
  (li : E₁ →ₗᵢ[R₁] F) (h : finrank R₁ E₁ = finrank R₁ F) (x : E₁) :
  (li.to_linear_isometry_equiv h) x = li x := rfl

end linear_isometry

namespace affine_isometry

open affine_map

variables {𝕜 : Type*} {V₁ V₂  : Type*} {P₁ P₂ : Type*}
  [normed_field 𝕜]
  [normed_add_comm_group V₁] [seminormed_add_comm_group V₂]
  [normed_space 𝕜 V₁] [normed_space 𝕜 V₂]
  [metric_space P₁] [pseudo_metric_space P₂]
  [normed_add_torsor V₁ P₁] [normed_add_torsor V₂ P₂]

variables [finite_dimensional 𝕜 V₁] [finite_dimensional 𝕜 V₂]

/-- An affine isometry between finite dimensional spaces of equal dimension can be upgraded
    to an affine isometry equivalence. -/
def to_affine_isometry_equiv [inhabited P₁]
  (li : P₁ →ᵃⁱ[𝕜] P₂) (h : finrank 𝕜 V₁ = finrank 𝕜 V₂) : P₁ ≃ᵃⁱ[𝕜] P₂ :=
affine_isometry_equiv.mk' li (li.linear_isometry.to_linear_isometry_equiv h) (arbitrary P₁)
  (λ p, by simp)

@[simp] lemma coe_to_affine_isometry_equiv [inhabited P₁]
  (li : P₁ →ᵃⁱ[𝕜] P₂) (h : finrank 𝕜 V₁ = finrank 𝕜 V₂) :
  (li.to_affine_isometry_equiv h : P₁ → P₂) = li := rfl

@[simp] lemma to_affine_isometry_equiv_apply [inhabited P₁]
  (li : P₁ →ᵃⁱ[𝕜] P₂) (h : finrank 𝕜 V₁ = finrank 𝕜 V₂) (x : P₁) :
  (li.to_affine_isometry_equiv h) x = li x := rfl

end affine_isometry

section complete_field

variables {𝕜 : Type u} [nontrivially_normed_field 𝕜]
{E : Type v} [normed_add_comm_group E] [normed_space 𝕜 E]
{F : Type w} [normed_add_comm_group F] [normed_space 𝕜 F]
{F' : Type x} [add_comm_group F'] [module 𝕜 F'] [topological_space F']
[topological_add_group F'] [has_continuous_smul 𝕜 F']
[complete_space 𝕜]

section affine

variables  {PE PF : Type*} [metric_space PE] [normed_add_torsor E PE] [metric_space PF]
  [normed_add_torsor F PF] [finite_dimensional 𝕜 E]

include E F

theorem affine_map.continuous_of_finite_dimensional (f : PE →ᵃ[𝕜] PF) : continuous f :=
affine_map.continuous_linear_iff.1 f.linear.continuous_of_finite_dimensional

theorem affine_equiv.continuous_of_finite_dimensional (f : PE ≃ᵃ[𝕜] PF) : continuous f :=
f.to_affine_map.continuous_of_finite_dimensional

/-- Reinterpret an affine equivalence as a homeomorphism. -/
def affine_equiv.to_homeomorph_of_finite_dimensional (f : PE ≃ᵃ[𝕜] PF) : PE ≃ₜ PF :=
{ to_equiv := f.to_equiv,
  continuous_to_fun := f.continuous_of_finite_dimensional,
  continuous_inv_fun :=
    begin
      haveI : finite_dimensional 𝕜 F, from f.linear.finite_dimensional,
      exact f.symm.continuous_of_finite_dimensional
    end }

@[simp] lemma affine_equiv.coe_to_homeomorph_of_finite_dimensional (f : PE ≃ᵃ[𝕜] PF) :
  ⇑f.to_homeomorph_of_finite_dimensional = f := rfl

@[simp] lemma affine_equiv.coe_to_homeomorph_of_finite_dimensional_symm (f : PE ≃ᵃ[𝕜] PF) :
  ⇑f.to_homeomorph_of_finite_dimensional.symm = f.symm := rfl

end affine

lemma continuous_linear_map.continuous_det :
  continuous (λ (f : E →L[𝕜] E), f.det) :=
begin
  change continuous (λ (f : E →L[𝕜] E), (f : E →ₗ[𝕜] E).det),
  by_cases h : ∃ (s : finset E), nonempty (basis ↥s 𝕜 E),
  { rcases h with ⟨s, ⟨b⟩⟩,
    haveI : finite_dimensional 𝕜 E := finite_dimensional.of_fintype_basis b,
    simp_rw linear_map.det_eq_det_to_matrix_of_finset b,
    refine continuous.matrix_det _,
    exact ((linear_map.to_matrix b b).to_linear_map.comp
        (continuous_linear_map.coe_lm 𝕜)).continuous_of_finite_dimensional },
  { unfold linear_map.det,
    simpa only [h, monoid_hom.one_apply, dif_neg, not_false_iff] using continuous_const }
end

/-- Any `K`-Lipschitz map from a subset `s` of a metric space `α` to a finite-dimensional real
vector space `E'` can be extended to a Lipschitz map on the whole space `α`, with a slightly worse
constant `C * K` where `C` only depends on `E'`. We record a working value for this constant `C`
as `lipschitz_extension_constant E'`. -/
@[irreducible] def lipschitz_extension_constant
  (E' : Type*) [normed_add_comm_group E'] [normed_space ℝ E'] [finite_dimensional ℝ E'] : ℝ≥0 :=
let A := (basis.of_vector_space ℝ E').equiv_fun.to_continuous_linear_equiv in
  max (‖A.symm.to_continuous_linear_map‖₊ * ‖A.to_continuous_linear_map‖₊) 1

lemma lipschitz_extension_constant_pos
  (E' : Type*) [normed_add_comm_group E'] [normed_space ℝ E'] [finite_dimensional ℝ E'] :
  0 < lipschitz_extension_constant E' :=
by { rw lipschitz_extension_constant, exact zero_lt_one.trans_le (le_max_right _ _) }

/-- Any `K`-Lipschitz map from a subset `s` of a metric space `α` to a finite-dimensional real
vector space `E'` can be extended to a Lipschitz map on the whole space `α`, with a slightly worse
constant `lipschitz_extension_constant E' * K`. -/
theorem lipschitz_on_with.extend_finite_dimension
  {α : Type*} [pseudo_metric_space α]
  {E' : Type*} [normed_add_comm_group E'] [normed_space ℝ E'] [finite_dimensional ℝ E']
  {s : set α} {f : α → E'} {K : ℝ≥0} (hf : lipschitz_on_with K f s) :
  ∃ (g : α → E'), lipschitz_with (lipschitz_extension_constant E' * K) g ∧ eq_on f g s :=
begin
  /- This result is already known for spaces `ι → ℝ`. We use a continuous linear equiv between
  `E'` and such a space to transfer the result to `E'`. -/
  let ι : Type* := basis.of_vector_space_index ℝ E',
  let A := (basis.of_vector_space ℝ E').equiv_fun.to_continuous_linear_equiv,
  have LA : lipschitz_with (‖A.to_continuous_linear_map‖₊) A, by apply A.lipschitz,
  have L : lipschitz_on_with (‖A.to_continuous_linear_map‖₊ * K) (A ∘ f) s :=
    LA.comp_lipschitz_on_with hf,
  obtain ⟨g, hg, gs⟩ : ∃ g : α → (ι → ℝ), lipschitz_with (‖A.to_continuous_linear_map‖₊ * K) g ∧
    eq_on (A ∘ f) g s := L.extend_pi,
  refine ⟨A.symm ∘ g, _, _⟩,
  { have LAsymm : lipschitz_with (‖A.symm.to_continuous_linear_map‖₊) A.symm,
      by apply A.symm.lipschitz,
    apply (LAsymm.comp hg).weaken,
    rw [lipschitz_extension_constant, ← mul_assoc],
    refine mul_le_mul' (le_max_left _ _) le_rfl },
  { assume x hx,
    have : A (f x) = g x := gs hx,
    simp only [(∘), ← this, A.symm_apply_apply] }
end

lemma linear_map.exists_antilipschitz_with [finite_dimensional 𝕜 E] (f : E →ₗ[𝕜] F)
  (hf : f.ker = ⊥) : ∃ K > 0, antilipschitz_with K f :=
begin
  casesI subsingleton_or_nontrivial E,
  { exact ⟨1, zero_lt_one, antilipschitz_with.of_subsingleton⟩ },
  { rw linear_map.ker_eq_bot at hf,
    let e : E ≃L[𝕜] f.range := (linear_equiv.of_injective f hf).to_continuous_linear_equiv,
    exact ⟨_, e.nnnorm_symm_pos, e.antilipschitz⟩ }
end

protected lemma linear_independent.eventually {ι} [finite ι] {f : ι → E}
  (hf : linear_independent 𝕜 f) : ∀ᶠ g in 𝓝 f, linear_independent 𝕜 g :=
begin
  casesI nonempty_fintype ι,
  simp only [fintype.linear_independent_iff'] at hf ⊢,
  rcases linear_map.exists_antilipschitz_with _ hf with ⟨K, K0, hK⟩,
  have : tendsto (λ g : ι → E, ∑ i, ‖g i - f i‖) (𝓝 f) (𝓝 $ ∑ i, ‖f i - f i‖),
    from tendsto_finset_sum _ (λ i hi, tendsto.norm $
      ((continuous_apply i).tendsto _).sub tendsto_const_nhds),
  simp only [sub_self, norm_zero, finset.sum_const_zero] at this,
  refine (this.eventually (gt_mem_nhds $ inv_pos.2 K0)).mono (λ g hg, _),
  replace hg : ∑ i, ‖g i - f i‖₊ < K⁻¹, by { rw ← nnreal.coe_lt_coe, push_cast, exact hg },
  rw linear_map.ker_eq_bot,
  refine (hK.add_sub_lipschitz_with (lipschitz_with.of_dist_le_mul $ λ v u, _) hg).injective,
  simp only [dist_eq_norm, linear_map.lsum_apply, pi.sub_apply, linear_map.sum_apply,
    linear_map.comp_apply, linear_map.proj_apply, linear_map.smul_right_apply, linear_map.id_apply,
    ← finset.sum_sub_distrib, ← smul_sub, ← sub_smul, nnreal.coe_sum, coe_nnnorm, finset.sum_mul],
  refine norm_sum_le_of_le _ (λ i _, _),
  rw [norm_smul, mul_comm],
  exact mul_le_mul_of_nonneg_left (norm_le_pi_norm (v - u) i) (norm_nonneg _)
end

lemma is_open_set_of_linear_independent {ι : Type*} [finite ι] :
  is_open {f : ι → E | linear_independent 𝕜 f} :=
is_open_iff_mem_nhds.2 $ λ f, linear_independent.eventually

lemma is_open_set_of_nat_le_rank (n : ℕ) : is_open {f : E →L[𝕜] F | ↑n ≤ rank (f : E →ₗ[𝕜] F)} :=
begin
  simp only [le_rank_iff_exists_linear_independent_finset, set_of_exists, ← exists_prop],
  refine is_open_bUnion (λ t ht, _),
  have : continuous (λ f : E →L[𝕜] F, (λ x : (t : set E), f x)),
    from continuous_pi (λ x, (continuous_linear_map.apply 𝕜 F (x : E)).continuous),
  exact is_open_set_of_linear_independent.preimage this
end

/-- Two finite-dimensional normed spaces are continuously linearly equivalent if they have the same
(finite) dimension. -/
theorem finite_dimensional.nonempty_continuous_linear_equiv_of_finrank_eq
  [finite_dimensional 𝕜 E] [finite_dimensional 𝕜 F] (cond : finrank 𝕜 E = finrank 𝕜 F) :
  nonempty (E ≃L[𝕜] F) :=
(nonempty_linear_equiv_of_finrank_eq cond).map linear_equiv.to_continuous_linear_equiv

/-- Two finite-dimensional normed spaces are continuously linearly equivalent if and only if they
have the same (finite) dimension. -/
theorem finite_dimensional.nonempty_continuous_linear_equiv_iff_finrank_eq
  [finite_dimensional 𝕜 E] [finite_dimensional 𝕜 F] :
   nonempty (E ≃L[𝕜] F) ↔ finrank 𝕜 E = finrank 𝕜 F :=
⟨ λ ⟨h⟩, h.to_linear_equiv.finrank_eq,
  λ h, finite_dimensional.nonempty_continuous_linear_equiv_of_finrank_eq h ⟩

/-- A continuous linear equivalence between two finite-dimensional normed spaces of the same
(finite) dimension. -/
def continuous_linear_equiv.of_finrank_eq [finite_dimensional 𝕜 E] [finite_dimensional 𝕜 F]
  (cond : finrank 𝕜 E = finrank 𝕜 F) :
  E ≃L[𝕜] F :=
(linear_equiv.of_finrank_eq E F cond).to_continuous_linear_equiv

variables {ι : Type*} [fintype ι]

/-- Construct a continuous linear map given the value at a finite basis. -/
def basis.constrL (v : basis ι 𝕜 E) (f : ι → F) :
  E →L[𝕜] F :=
by haveI : finite_dimensional 𝕜 E := finite_dimensional.of_fintype_basis v;
  exact (v.constr 𝕜 f).to_continuous_linear_map

@[simp, norm_cast] lemma basis.coe_constrL (v : basis ι 𝕜 E) (f : ι → F) :
  (v.constrL f : E →ₗ[𝕜] F) = v.constr 𝕜 f := rfl

/-- The continuous linear equivalence between a vector space over `𝕜` with a finite basis and
functions from its basis indexing type to `𝕜`. -/
def basis.equiv_funL (v : basis ι 𝕜 E) : E ≃L[𝕜] (ι → 𝕜) :=
{ continuous_to_fun := begin
    haveI : finite_dimensional 𝕜 E := finite_dimensional.of_fintype_basis v,
    exact v.equiv_fun.to_linear_map.continuous_of_finite_dimensional,
  end,
  continuous_inv_fun := begin
    change continuous v.equiv_fun.symm.to_fun,
    exact v.equiv_fun.symm.to_linear_map.continuous_of_finite_dimensional,
  end,
  ..v.equiv_fun }

@[simp] lemma basis.constrL_apply (v : basis ι 𝕜 E) (f : ι → F) (e : E) :
  (v.constrL f) e = ∑ i, (v.equiv_fun e i) • f i :=
v.constr_apply_fintype 𝕜 _ _

@[simp] lemma basis.constrL_basis (v : basis ι 𝕜 E) (f : ι → F) (i : ι) :
  (v.constrL f) (v i) = f i :=
v.constr_basis 𝕜 _ _

lemma basis.op_nnnorm_le {ι : Type*} [fintype ι] (v : basis ι 𝕜 E) {u : E →L[𝕜] F} (M : ℝ≥0)
  (hu : ∀ i, ‖u (v i)‖₊ ≤ M) :
  ‖u‖₊ ≤ fintype.card ι • ‖v.equiv_funL.to_continuous_linear_map‖₊ * M :=
u.op_nnnorm_le_bound _ $ λ e, begin
  set φ := v.equiv_funL.to_continuous_linear_map,
  calc
  ‖u e‖₊ = ‖u (∑ i, v.equiv_fun e i • v i)‖₊ :   by rw [v.sum_equiv_fun]
    ... = ‖∑ i, (v.equiv_fun e i) • (u $ v i)‖₊ : by simp [u.map_sum, linear_map.map_smul]
    ... ≤ ∑ i, ‖(v.equiv_fun e i) • (u $ v i)‖₊ : nnnorm_sum_le _ _
    ... = ∑ i, ‖v.equiv_fun e i‖₊ * ‖u (v i)‖₊ :   by simp only [nnnorm_smul]
    ... ≤ ∑ i, ‖v.equiv_fun e i‖₊ * M : finset.sum_le_sum (λ i hi,
                                                    mul_le_mul_of_nonneg_left (hu i) (zero_le _))
    ... = (∑ i, ‖v.equiv_fun e i‖₊) * M : finset.sum_mul.symm
    ... ≤ fintype.card ι • (‖φ‖₊ * ‖e‖₊) * M :
          (suffices _, from mul_le_mul_of_nonneg_right this (zero_le M),
          calc  ∑ i, ‖v.equiv_fun e i‖₊
              ≤ fintype.card ι • ‖φ e‖₊ : pi.sum_nnnorm_apply_le_nnnorm _
          ... ≤ fintype.card ι • (‖φ‖₊ * ‖e‖₊) : nsmul_le_nsmul_of_le_right (φ.le_op_nnnorm e) _)
    ... = fintype.card ι • ‖φ‖₊ * M * ‖e‖₊ : by simp only [smul_mul_assoc, mul_right_comm],
end

lemma basis.op_norm_le {ι : Type*} [fintype ι] (v : basis ι 𝕜 E) {u : E →L[𝕜] F} {M : ℝ}
  (hM : 0 ≤ M) (hu : ∀ i, ‖u (v i)‖ ≤ M) :
  ‖u‖ ≤ fintype.card ι • ‖v.equiv_funL.to_continuous_linear_map‖ * M :=
by simpa using nnreal.coe_le_coe.mpr (v.op_nnnorm_le ⟨M, hM⟩ hu)

/-- A weaker version of `basis.op_nnnorm_le` that abstracts away the value of `C`. -/
lemma basis.exists_op_nnnorm_le {ι : Type*} [finite ι] (v : basis ι 𝕜 E) :
  ∃ C > (0 : ℝ≥0), ∀ {u : E →L[𝕜] F} (M : ℝ≥0), (∀ i, ‖u (v i)‖₊ ≤ M) → ‖u‖₊ ≤ C*M :=
by casesI nonempty_fintype ι; exact
  ⟨max (fintype.card ι • ‖v.equiv_funL.to_continuous_linear_map‖₊) 1,
  zero_lt_one.trans_le (le_max_right _ _),
  λ u M hu, (v.op_nnnorm_le M hu).trans $ mul_le_mul_of_nonneg_right (le_max_left _ _) (zero_le M)⟩

/-- A weaker version of `basis.op_norm_le` that abstracts away the value of `C`. -/
lemma basis.exists_op_norm_le {ι : Type*} [finite ι] (v : basis ι 𝕜 E) :
  ∃ C > (0 : ℝ), ∀ {u : E →L[𝕜] F} {M : ℝ}, 0 ≤ M → (∀ i, ‖u (v i)‖ ≤ M) → ‖u‖ ≤ C*M :=
let ⟨C, hC, h⟩ := v.exists_op_nnnorm_le in ⟨C, hC, λ u, subtype.forall'.mpr h⟩

instance [finite_dimensional 𝕜 E] [second_countable_topology F] :
  second_countable_topology (E →L[𝕜] F) :=
begin
  set d := finite_dimensional.finrank 𝕜 E,
  suffices :
    ∀ ε > (0 : ℝ), ∃ n : (E →L[𝕜] F) → fin d → ℕ, ∀ (f g : E →L[𝕜] F), n f = n g → dist f g ≤ ε,
  from metric.second_countable_of_countable_discretization
    (λ ε ε_pos, ⟨fin d → ℕ, by apply_instance, this ε ε_pos⟩),
  intros ε ε_pos,
  obtain ⟨u : ℕ → F, hu : dense_range u⟩ := exists_dense_seq F,
  let v := finite_dimensional.fin_basis 𝕜 E,
  obtain ⟨C : ℝ, C_pos : 0 < C,
          hC : ∀ {φ : E →L[𝕜] F} {M : ℝ}, 0 ≤ M → (∀ i, ‖φ (v i)‖ ≤ M) → ‖φ‖ ≤ C * M⟩ :=
    v.exists_op_norm_le,
  have h_2C : 0 < 2*C := mul_pos zero_lt_two C_pos,
  have hε2C : 0 < ε/(2*C) := div_pos ε_pos h_2C,
  have : ∀ φ : E →L[𝕜] F, ∃ n : fin d → ℕ, ‖φ - (v.constrL $ u ∘ n)‖ ≤ ε/2,
  { intros φ,
    have : ∀ i, ∃ n, ‖φ (v i) - u n‖ ≤ ε/(2*C),
    { simp only [norm_sub_rev],
      intro i,
      have : φ (v i) ∈ closure (range u) := hu _,
      obtain ⟨n, hn⟩ : ∃ n, ‖u n - φ (v i)‖ < ε / (2 * C),
      { rw mem_closure_iff_nhds_basis metric.nhds_basis_ball at this,
        specialize this (ε/(2*C)) hε2C,
        simpa [dist_eq_norm] },
      exact ⟨n, le_of_lt hn⟩ },
    choose n hn using this,
    use n,
    replace hn : ∀ i : fin d, ‖(φ - (v.constrL $ u ∘ n)) (v i)‖ ≤ ε / (2 * C), by simp [hn],
    have : C * (ε / (2 * C)) = ε/2,
    { rw [eq_div_iff (two_ne_zero : (2 : ℝ) ≠ 0), mul_comm, ← mul_assoc,
          mul_div_cancel' _ (ne_of_gt h_2C)] },
    specialize hC (le_of_lt hε2C) hn,
    rwa this at hC },
  choose n hn using this,
  set Φ := λ φ : E →L[𝕜] F, (v.constrL $ u ∘ (n φ)),
  change ∀ z, dist z (Φ z) ≤ ε/2 at hn,
  use n,
  intros x y hxy,
  calc dist x y ≤ dist x (Φ x) + dist (Φ x) y : dist_triangle _ _ _
  ... = dist x (Φ x) + dist y (Φ y) : by simp [Φ, hxy, dist_comm]
  ... ≤ ε : by linarith [hn x, hn y]
end

/-- Any finite-dimensional vector space over a complete field is complete.
We do not register this as an instance to avoid an instance loop when trying to prove the
completeness of `𝕜`, and the search for `𝕜` as an unknown metavariable. Declare the instance
explicitly when needed. -/
variables (𝕜 E)
lemma finite_dimensional.complete [finite_dimensional 𝕜 E] : complete_space E :=
begin
  set e := continuous_linear_equiv.of_finrank_eq (@finrank_fin_fun 𝕜 _ (finrank 𝕜 E)).symm,
  have : uniform_embedding e.to_linear_equiv.to_equiv.symm := e.symm.uniform_embedding,
  exact (complete_space_congr this).1 (by apply_instance)
end

variables {𝕜 E}
/-- A finite-dimensional subspace is complete. -/
lemma submodule.complete_of_finite_dimensional (s : submodule 𝕜 E) [finite_dimensional 𝕜 s] :
  is_complete (s : set E) :=
complete_space_coe_iff_is_complete.1 (finite_dimensional.complete 𝕜 s)

/-- A finite-dimensional subspace is closed. -/
lemma submodule.closed_of_finite_dimensional (s : submodule 𝕜 E) [finite_dimensional 𝕜 s] :
  is_closed (s : set E) :=
s.complete_of_finite_dimensional.is_closed

lemma affine_subspace.closed_of_finite_dimensional {P : Type*} [metric_space P]
  [normed_add_torsor E P] (s : affine_subspace 𝕜 P) [finite_dimensional 𝕜 s.direction] :
  is_closed (s : set P) :=
s.is_closed_direction_iff.mp s.direction.closed_of_finite_dimensional

section riesz

/-- In an infinite dimensional space, given a finite number of points, one may find a point
with norm at most `R` which is at distance at least `1` of all these points. -/
theorem exists_norm_le_le_norm_sub_of_finset {c : 𝕜} (hc : 1 < ‖c‖) {R : ℝ} (hR : ‖c‖ < R)
  (h : ¬ (finite_dimensional 𝕜 E)) (s : finset E) :
  ∃ (x : E), ‖x‖ ≤ R ∧ ∀ y ∈ s, 1 ≤ ‖y - x‖ :=
begin
  let F := submodule.span 𝕜 (s : set E),
  haveI : finite_dimensional 𝕜 F := module.finite_def.2
    ((submodule.fg_top _).2 (submodule.fg_def.2 ⟨s, finset.finite_to_set _, rfl⟩)),
  have Fclosed : is_closed (F : set E) := submodule.closed_of_finite_dimensional _,
  have : ∃ x, x ∉ F,
  { contrapose! h,
    have : (⊤ : submodule 𝕜 E) = F, by { ext x, simp [h] },
    have : finite_dimensional 𝕜 (⊤ : submodule 𝕜 E), by rwa this,
    refine module.finite_def.2 ((submodule.fg_top _).1 (module.finite_def.1 this)) },
  obtain ⟨x, xR, hx⟩ : ∃ (x : E), ‖x‖ ≤ R ∧ ∀ (y : E), y ∈ F → 1 ≤ ‖x - y‖ :=
    riesz_lemma_of_norm_lt hc hR Fclosed this,
  have hx' : ∀ (y : E), y ∈ F → 1 ≤ ‖y - x‖,
  { assume y hy, rw ← norm_neg, simpa using hx y hy },
  exact ⟨x, xR, λ y hy, hx' _ (submodule.subset_span hy)⟩,
end

/-- In an infinite-dimensional normed space, there exists a sequence of points which are all
bounded by `R` and at distance at least `1`. For a version not assuming `c` and `R`, see
`exists_seq_norm_le_one_le_norm_sub`. -/
theorem exists_seq_norm_le_one_le_norm_sub' {c : 𝕜} (hc : 1 < ‖c‖) {R : ℝ} (hR : ‖c‖ < R)
  (h : ¬ (finite_dimensional 𝕜 E)) :
  ∃ f : ℕ → E, (∀ n, ‖f n‖ ≤ R) ∧ (∀ m n, m ≠ n → 1 ≤ ‖f m - f n‖) :=
begin
  haveI : is_symm E (λ (x y : E), 1 ≤ ‖x - y‖),
  { constructor,
    assume x y hxy,
    rw ← norm_neg,
    simpa },
  apply exists_seq_of_forall_finset_exists' (λ (x : E), ‖x‖ ≤ R) (λ (x : E) (y : E), 1 ≤ ‖x - y‖),
  assume s hs,
  exact exists_norm_le_le_norm_sub_of_finset hc hR h s,
end

theorem exists_seq_norm_le_one_le_norm_sub (h : ¬ (finite_dimensional 𝕜 E)) :
  ∃ (R : ℝ) (f : ℕ → E), (1 < R) ∧ (∀ n, ‖f n‖ ≤ R) ∧ (∀ m n, m ≠ n → 1 ≤ ‖f m - f n‖) :=
begin
  obtain ⟨c, hc⟩ : ∃ (c : 𝕜), 1 < ‖c‖ := normed_field.exists_one_lt_norm 𝕜,
  have A : ‖c‖ < ‖c‖ + 1, by linarith,
  rcases exists_seq_norm_le_one_le_norm_sub' hc A h with ⟨f, hf⟩,
  exact ⟨‖c‖ + 1, f, hc.trans A, hf.1, hf.2⟩
end

variable (𝕜)

/-- **Riesz's theorem**: if a closed ball with center zero of positive radius is compact in a vector
space, then the space is finite-dimensional. -/
theorem finite_dimensional_of_is_compact_closed_ball₀ {r : ℝ} (rpos : 0 < r)
  (h : is_compact (metric.closed_ball (0 : E) r)) : finite_dimensional 𝕜 E :=
begin
  by_contra hfin,
  obtain ⟨R, f, Rgt, fle, lef⟩ :
    ∃ (R : ℝ) (f : ℕ → E), (1 < R) ∧ (∀ n, ‖f n‖ ≤ R) ∧ (∀ m n, m ≠ n → 1 ≤ ‖f m - f n‖) :=
      exists_seq_norm_le_one_le_norm_sub hfin,
  have rRpos : 0 < r / R := div_pos rpos (zero_lt_one.trans Rgt),
  obtain ⟨c, hc⟩ : ∃ (c : 𝕜), 0 < ‖c‖ ∧ ‖c‖ < (r / R) := normed_field.exists_norm_lt _ rRpos,
  let g := λ (n : ℕ), c • f n,
  have A : ∀ n, g n ∈ metric.closed_ball (0 : E) r,
  { assume n,
    simp only [norm_smul, dist_zero_right, metric.mem_closed_ball],
    calc ‖c‖ * ‖f n‖ ≤ (r / R) * R : mul_le_mul hc.2.le (fle n) (norm_nonneg _) rRpos.le
    ... = r : by field_simp [(zero_lt_one.trans Rgt).ne'] },
  obtain ⟨x, hx, φ, φmono, φlim⟩ : ∃ (x : E) (H : x ∈ metric.closed_ball (0 : E) r) (φ : ℕ → ℕ),
    strict_mono φ ∧ tendsto (g ∘ φ) at_top (𝓝 x) := h.tendsto_subseq A,
  have B : cauchy_seq (g ∘ φ) := φlim.cauchy_seq,
  obtain ⟨N, hN⟩ : ∃ (N : ℕ), ∀ (n : ℕ), N ≤ n → dist ((g ∘ φ) n) ((g ∘ φ) N) < ‖c‖ :=
    metric.cauchy_seq_iff'.1 B (‖c‖) hc.1,
  apply lt_irrefl (‖c‖),
  calc ‖c‖ ≤ dist (g (φ (N+1))) (g (φ N)) : begin
    conv_lhs { rw [← mul_one (‖c‖)] },
    simp only [g, dist_eq_norm, ←smul_sub, norm_smul, -mul_one],
    apply mul_le_mul_of_nonneg_left (lef _ _ (ne_of_gt _)) (norm_nonneg _),
    exact φmono (nat.lt_succ_self N)
  end
  ... < ‖c‖ : hN (N+1) (nat.le_succ N)
end

/-- **Riesz's theorem**: if a closed ball of positive radius is compact in a vector space, then the
space is finite-dimensional. -/
theorem finite_dimensional_of_is_compact_closed_ball {r : ℝ} (rpos : 0 < r) {c : E}
  (h : is_compact (metric.closed_ball c r)) : finite_dimensional 𝕜 E :=
begin
  apply finite_dimensional_of_is_compact_closed_ball₀ 𝕜 rpos,
  have : continuous (λ x, -c + x), from continuous_const.add continuous_id,
  simpa using h.image this,
end

/-- If a function has compact multiplicative support, then either the function is trivial or the
space if finite-dimensional. -/
@[to_additive "If a function has compact support, then either the function is trivial or the
space if finite-dimensional."]
lemma has_compact_mul_support.eq_one_or_finite_dimensional {X : Type*}
  [topological_space X] [has_one X] [t2_space X]
  {f : E → X} (hf : has_compact_mul_support f) (h'f : continuous f) :
  f = 1 ∨ finite_dimensional 𝕜 E :=
begin
  by_cases h : ∀ x, f x = 1, { apply or.inl, ext x, exact h x },
  apply or.inr,
  push_neg at h,
  obtain ⟨x, hx⟩ : ∃ x, f x ≠ 1, from h,
  have : function.mul_support f ∈ 𝓝 x, from h'f.is_open_mul_support.mem_nhds hx,
  obtain ⟨r, rpos, hr⟩ : ∃ (r : ℝ) (hi : 0 < r), metric.closed_ball x r ⊆ function.mul_support f,
    from metric.nhds_basis_closed_ball.mem_iff.1 this,
  have : is_compact (metric.closed_ball x r),
    from is_compact_of_is_closed_subset hf metric.is_closed_ball (hr.trans (subset_mul_tsupport _)),
  exact finite_dimensional_of_is_compact_closed_ball 𝕜 rpos this,
end

end riesz

/-- An injective linear map with finite-dimensional domain is a closed embedding. -/
lemma linear_equiv.closed_embedding_of_injective {f : E →ₗ[𝕜] F} (hf : f.ker = ⊥)
  [finite_dimensional 𝕜 E] :
  closed_embedding ⇑f :=
let g := linear_equiv.of_injective f (linear_map.ker_eq_bot.mp hf) in
{ closed_range := begin
    haveI := f.finite_dimensional_range,
    simpa [f.range_coe] using f.range.closed_of_finite_dimensional
  end,
  .. embedding_subtype_coe.comp g.to_continuous_linear_equiv.to_homeomorph.embedding }

lemma continuous_linear_map.exists_right_inverse_of_surjective [finite_dimensional 𝕜 F]
  (f : E →L[𝕜] F) (hf : linear_map.range f = ⊤) :
  ∃ g : F →L[𝕜] E, f.comp g = continuous_linear_map.id 𝕜 F :=
let ⟨g, hg⟩ := (f : E →ₗ[𝕜] F).exists_right_inverse_of_surjective hf in
⟨g.to_continuous_linear_map, continuous_linear_map.ext $ linear_map.ext_iff.1 hg⟩

lemma closed_embedding_smul_left {c : E} (hc : c ≠ 0) : closed_embedding (λ x : 𝕜, x • c) :=
linear_equiv.closed_embedding_of_injective (linear_map.ker_to_span_singleton 𝕜 E hc)

/- `smul` is a closed map in the first argument. -/
lemma is_closed_map_smul_left (c : E) : is_closed_map (λ x : 𝕜, x • c) :=
begin
  by_cases hc : c = 0,
  { simp_rw [hc, smul_zero], exact is_closed_map_const },
  { exact (closed_embedding_smul_left hc).is_closed_map }
end

open continuous_linear_map

/-- Continuous linear equivalence between continuous linear functions `𝕜ⁿ → E` and `Eⁿ`.
The spaces `𝕜ⁿ` and `Eⁿ` are represented as `ι → 𝕜` and `ι → E`, respectively,
where `ι` is a finite type. -/
def continuous_linear_equiv.pi_ring (ι : Type*) [fintype ι] [decidable_eq ι] :
  ((ι → 𝕜) →L[𝕜] E) ≃L[𝕜] (ι → E) :=
{ continuous_to_fun :=
  begin
    refine continuous_pi (λ i, _),
    exact (continuous_linear_map.apply 𝕜 E (pi.single i 1)).continuous,
  end,
  continuous_inv_fun :=
  begin
    simp_rw [linear_equiv.inv_fun_eq_symm, linear_equiv.trans_symm, linear_equiv.symm_symm],
    change continuous (linear_map.to_continuous_linear_map.to_linear_map.comp
                      (linear_equiv.pi_ring 𝕜 E ι 𝕜).symm.to_linear_map),
    apply add_monoid_hom_class.continuous_of_bound _ (fintype.card ι : ℝ) (λ g, _),
    rw ← nsmul_eq_mul,
    apply op_norm_le_bound _ (nsmul_nonneg (norm_nonneg g) (fintype.card ι)) (λ t, _),
    simp_rw [linear_map.coe_comp, linear_equiv.coe_to_linear_map, function.comp_app,
      linear_map.coe_to_continuous_linear_map', linear_equiv.pi_ring_symm_apply],
    apply le_trans (norm_sum_le _ _),
    rw smul_mul_assoc,
    refine finset.sum_le_card_nsmul _ _ _ (λ i hi, _),
    rw [norm_smul, mul_comm],
    exact mul_le_mul (norm_le_pi_norm g i) (norm_le_pi_norm t i) (norm_nonneg _) (norm_nonneg g),
  end,
  .. linear_map.to_continuous_linear_map.symm.trans (linear_equiv.pi_ring 𝕜 E ι 𝕜) }

/-- A family of continuous linear maps is continuous on `s` if all its applications are. -/
lemma continuous_on_clm_apply {X : Type*} [topological_space X]
  [finite_dimensional 𝕜 E] {f : X → E →L[𝕜] F} {s : set X} :
  continuous_on f s ↔ ∀ y, continuous_on (λ x, f x y) s :=
begin
  refine ⟨λ h y, (continuous_linear_map.apply 𝕜 F y).continuous.comp_continuous_on h, λ h, _⟩,
  let d := finrank 𝕜 E,
  have hd : d = finrank 𝕜 (fin d → 𝕜) := (finrank_fin_fun 𝕜).symm,
  let e₁ : E ≃L[𝕜] fin d → 𝕜 := continuous_linear_equiv.of_finrank_eq hd,
  let e₂ : (E →L[𝕜] F) ≃L[𝕜] fin d → F :=
    (e₁.arrow_congr (1 : F ≃L[𝕜] F)).trans (continuous_linear_equiv.pi_ring (fin d)),
  rw [← function.comp.left_id f, ← e₂.symm_comp_self],
  exact e₂.symm.continuous.comp_continuous_on (continuous_on_pi.mpr (λ i, h _))
end

lemma continuous_clm_apply {X : Type*} [topological_space X] [finite_dimensional 𝕜 E]
  {f : X → E →L[𝕜] F} :
  continuous f ↔ ∀ y, continuous (λ x, f x y) :=
by simp_rw [continuous_iff_continuous_on_univ, continuous_on_clm_apply]

end complete_field

section proper_field
variables (𝕜 : Type u) [nontrivially_normed_field 𝕜]
(E : Type v) [normed_add_comm_group E] [normed_space 𝕜 E] [proper_space 𝕜]

/-- Any finite-dimensional vector space over a proper field is proper.
We do not register this as an instance to avoid an instance loop when trying to prove the
properness of `𝕜`, and the search for `𝕜` as an unknown metavariable. Declare the instance
explicitly when needed. -/
lemma finite_dimensional.proper [finite_dimensional 𝕜 E] : proper_space E :=
begin
  set e := continuous_linear_equiv.of_finrank_eq (@finrank_fin_fun 𝕜 _ (finrank 𝕜 E)).symm,
  exact e.symm.antilipschitz.proper_space e.symm.continuous e.symm.surjective
end

end proper_field

/- Over the real numbers, we can register the previous statement as an instance as it will not
cause problems in instance resolution since the properness of `ℝ` is already known. -/
@[priority 900]
instance finite_dimensional.proper_real (E : Type u) [normed_add_comm_group E] [normed_space ℝ E]
  [finite_dimensional ℝ E] : proper_space E :=
finite_dimensional.proper ℝ E

/-- If `E` is a finite dimensional normed real vector space, `x : E`, and `s` is a neighborhood of
`x` that is not equal to the whole space, then there exists a point `y ∈ frontier s` at distance
`metric.inf_dist x sᶜ` from `x`. See also
`is_compact.exists_mem_frontier_inf_dist_compl_eq_dist`. -/
lemma exists_mem_frontier_inf_dist_compl_eq_dist {E : Type*} [normed_add_comm_group E]
  [normed_space ℝ E] [finite_dimensional ℝ E] {x : E} {s : set E} (hx : x ∈ s) (hs : s ≠ univ) :
  ∃ y ∈ frontier s, metric.inf_dist x sᶜ = dist x y :=
begin
  rcases metric.exists_mem_closure_inf_dist_eq_dist (nonempty_compl.2 hs) x with ⟨y, hys, hyd⟩,
  rw closure_compl at hys,
  refine ⟨y, ⟨metric.closed_ball_inf_dist_compl_subset_closure hx $
    metric.mem_closed_ball.2 $ ge_of_eq _, hys⟩, hyd⟩,
  rwa dist_comm
end

/-- If `K` is a compact set in a nontrivial real normed space and `x ∈ K`, then there exists a point
`y` of the boundary of `K` at distance `metric.inf_dist x Kᶜ` from `x`. See also
`exists_mem_frontier_inf_dist_compl_eq_dist`. -/
lemma is_compact.exists_mem_frontier_inf_dist_compl_eq_dist {E : Type*} [normed_add_comm_group E]
  [normed_space ℝ E] [nontrivial E] {x : E} {K : set E} (hK : is_compact K) (hx : x ∈ K) :
  ∃ y ∈ frontier K, metric.inf_dist x Kᶜ = dist x y :=
begin
  obtain (hx'|hx') : x ∈ interior K ∪ frontier K,
  { rw ← closure_eq_interior_union_frontier, exact subset_closure hx },
  { rw [mem_interior_iff_mem_nhds, metric.nhds_basis_closed_ball.mem_iff] at hx',
    rcases hx' with ⟨r, hr₀, hrK⟩,
    haveI : finite_dimensional ℝ E,
      from finite_dimensional_of_is_compact_closed_ball ℝ hr₀
        (is_compact_of_is_closed_subset hK metric.is_closed_ball hrK),
    exact exists_mem_frontier_inf_dist_compl_eq_dist hx hK.ne_univ },
  { refine ⟨x, hx', _⟩,
    rw frontier_eq_closure_inter_closure at hx',
    rw [metric.inf_dist_zero_of_mem_closure hx'.2, dist_self] },
end

/-- In a finite dimensional vector space over `ℝ`, the series `∑ x, ‖f x‖` is unconditionally
summable if and only if the series `∑ x, f x` is unconditionally summable. One implication holds in
any complete normed space, while the other holds only in finite dimensional spaces. -/
lemma summable_norm_iff {α E : Type*} [normed_add_comm_group E] [normed_space ℝ E]
  [finite_dimensional ℝ E] {f : α → E} : summable (λ x, ‖f x‖) ↔ summable f :=
begin
  refine ⟨summable_of_summable_norm, λ hf, _⟩,
  -- First we use a finite basis to reduce the problem to the case `E = fin N → ℝ`
  suffices : ∀ {N : ℕ} {g : α → fin N → ℝ}, summable g → summable (λ x, ‖g x‖),
  { obtain v := fin_basis ℝ E,
    set e := v.equiv_funL,
    have : summable (λ x, ‖e (f x)‖) := this (e.summable.2 hf),
    refine summable_of_norm_bounded _ (this.mul_left
      ↑(‖(e.symm : (fin (finrank ℝ E) → ℝ) →L[ℝ] E)‖₊)) (λ i, _),
    simpa using (e.symm : (fin (finrank ℝ E) → ℝ) →L[ℝ] E).le_op_norm (e $ f i) },
  unfreezingI { clear_dependent E },
  -- Now we deal with `g : α → fin N → ℝ`
  intros N g hg,
  have : ∀ i, summable (λ x, ‖g x i‖) := λ i, (pi.summable.1 hg i).abs,
  refine summable_of_norm_bounded _ (summable_sum (λ i (hi : i ∈ finset.univ), this i)) (λ x, _),
  rw [norm_norm, pi_norm_le_iff_of_nonneg],
  { refine λ i, finset.single_le_sum (λ i hi, _) (finset.mem_univ i),
    exact norm_nonneg (g x i) },
  { exact finset.sum_nonneg (λ _ _, norm_nonneg _) }
end

lemma summable_of_is_O' {ι E F : Type*} [normed_add_comm_group E] [complete_space E]
  [normed_add_comm_group F] [normed_space ℝ F] [finite_dimensional ℝ F] {f : ι → E} {g : ι → F}
  (hg : summable g) (h : f =O[cofinite] g) : summable f :=
summable_of_is_O (summable_norm_iff.mpr hg) h.norm_right

lemma summable_of_is_O_nat' {E F : Type*} [normed_add_comm_group E] [complete_space E]
  [normed_add_comm_group F] [normed_space ℝ F] [finite_dimensional ℝ F] {f : ℕ → E} {g : ℕ → F}
  (hg : summable g) (h : f =O[at_top] g) : summable f :=
summable_of_is_O_nat (summable_norm_iff.mpr hg) h.norm_right

lemma summable_of_is_equivalent {ι E : Type*} [normed_add_comm_group E] [normed_space ℝ E]
  [finite_dimensional ℝ E] {f : ι → E} {g : ι → E}
  (hg : summable g) (h : f ~[cofinite] g) : summable f :=
hg.trans_sub (summable_of_is_O' hg h.is_o.is_O)

lemma summable_of_is_equivalent_nat {E : Type*} [normed_add_comm_group E] [normed_space ℝ E]
  [finite_dimensional ℝ E] {f : ℕ → E} {g : ℕ → E}
  (hg : summable g) (h : f ~[at_top] g) : summable f :=
hg.trans_sub (summable_of_is_O_nat' hg h.is_o.is_O)

lemma is_equivalent.summable_iff {ι E : Type*} [normed_add_comm_group E] [normed_space ℝ E]
  [finite_dimensional ℝ E] {f : ι → E} {g : ι → E}
  (h : f ~[cofinite] g) : summable f ↔ summable g :=
⟨λ hf, summable_of_is_equivalent hf h.symm, λ hg, summable_of_is_equivalent hg h⟩

lemma is_equivalent.summable_iff_nat {E : Type*} [normed_add_comm_group E] [normed_space ℝ E]
  [finite_dimensional ℝ E] {f : ℕ → E} {g : ℕ → E}
  (h : f ~[at_top] g) : summable f ↔ summable g :=
⟨λ hf, summable_of_is_equivalent_nat hf h.symm, λ hg, summable_of_is_equivalent_nat hg h⟩