/-
Copyright (c) 2019 Floris van Doorn. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Floris van Doorn
-/
import set_theory.continuum
import analysis.specific_limits
import data.rat.denumerable
import data.set.intervals.image_preimage

/-!
# The cardinality of the reals

This file shows that the real numbers have cardinality continuum, i.e. `#ℝ = 𝔠`.

We show that `#ℝ ≤ 𝔠` by noting that every real number is determined by a Cauchy-sequence of the
form `ℕ → ℚ`, which has cardinality `𝔠`. To show that `#ℝ ≥ 𝔠` we define an injection from
`{0, 1} ^ ℕ` to `ℝ` with `f ↦ Σ n, f n * (1 / 3) ^ n`.

We conclude that all intervals with distinct endpoints have cardinality continuum.

## Main definitions

* `cardinal.cantor_function` is the function that sends `f` in `{0, 1} ^ ℕ` to `ℝ` by
  `f ↦ Σ' n, f n * (1 / 3) ^ n`

## Main statements

* `cardinal.mk_real : #ℝ = 𝔠`: the reals have cardinality continuum.
* `cardinal.not_countable_real`: the universal set of real numbers is not countable.
  We can use this same proof to show that all the other sets in this file are not countable.
* 8 lemmas of the form `mk_Ixy_real` for `x,y ∈ {i,o,c}` state that intervals on the reals
  have cardinality continuum.

## Notation

* `𝔠` : notation for `cardinal.continuum` in locale `cardinal`, defined in `set_theory.continuum`.

## Tags
continuum, cardinality, reals, cardinality of the reals
-/

open nat set
open_locale cardinal
noncomputable theory

namespace cardinal

variables {c : ℝ} {f g : ℕ → bool} {n : ℕ}

/-- The body of the sum in `cantor_function`.
`cantor_function_aux c f n = c ^ n` if `f n = tt`;
`cantor_function_aux c f n = 0` if `f n = ff`. -/
def cantor_function_aux (c : ℝ) (f : ℕ → bool) (n : ℕ) : ℝ := cond (f n) (c ^ n) 0

@[simp] lemma cantor_function_aux_tt (h : f n = tt) : cantor_function_aux c f n = c ^ n :=
by simp [cantor_function_aux, h]

@[simp] lemma cantor_function_aux_ff (h : f n = ff) : cantor_function_aux c f n = 0 :=
by simp [cantor_function_aux, h]

lemma cantor_function_aux_nonneg (h : 0 ≤ c) : 0 ≤ cantor_function_aux c f n :=
by { cases h' : f n; simp [h'], apply pow_nonneg h }

lemma cantor_function_aux_eq (h : f n = g n) :
  cantor_function_aux c f n = cantor_function_aux c g n :=
by simp [cantor_function_aux, h]

lemma cantor_function_aux_succ (f : ℕ → bool) :
  (λ n, cantor_function_aux c f (n + 1)) = λ n, c * cantor_function_aux c (λ n, f (n + 1)) n :=
by { ext n, cases h : f (n + 1); simp [h, pow_succ] }

lemma summable_cantor_function (f : ℕ → bool) (h1 : 0 ≤ c) (h2 : c < 1) :
  summable (cantor_function_aux c f) :=
begin
  apply (summable_geometric_of_lt_1 h1 h2).summable_of_eq_zero_or_self,
  intro n, cases h : f n; simp [h]
end

/-- `cantor_function c (f : ℕ → bool)` is `Σ n, f n * c ^ n`, where `tt` is interpreted as `1` and
`ff` is interpreted as `0`. It is implemented using `cantor_function_aux`. -/
def cantor_function (c : ℝ) (f : ℕ → bool) : ℝ := ∑' n, cantor_function_aux c f n

lemma cantor_function_le (h1 : 0 ≤ c) (h2 : c < 1) (h3 : ∀ n, f n → g n) :
  cantor_function c f ≤ cantor_function c g :=
begin
  apply tsum_le_tsum _ (summable_cantor_function f h1 h2) (summable_cantor_function g h1 h2),
  intro n, cases h : f n, simp [h, cantor_function_aux_nonneg h1],
  replace h3 : g n = tt := h3 n h, simp [h, h3]
end

lemma cantor_function_succ (f : ℕ → bool) (h1 : 0 ≤ c) (h2 : c < 1) :
  cantor_function c f = cond (f 0) 1 0 + c * cantor_function c (λ n, f (n+1)) :=
begin
  rw [cantor_function, tsum_eq_zero_add (summable_cantor_function f h1 h2)],
  rw [cantor_function_aux_succ, tsum_mul_left, cantor_function_aux, pow_zero],
  refl
end

/-- `cantor_function c` is strictly increasing with if `0 < c < 1/2`, if we endow `ℕ → bool` with a
lexicographic order. The lexicographic order doesn't exist for these infinitary products, so we
explicitly write out what it means. -/
lemma increasing_cantor_function (h1 : 0 < c) (h2 : c < 1 / 2) {n : ℕ} {f g : ℕ → bool}
  (hn : ∀(k < n), f k = g k) (fn : f n = ff) (gn : g n = tt) :
  cantor_function c f < cantor_function c g :=
begin
  have h3 : c < 1, { apply h2.trans, norm_num },
  induction n with n ih generalizing f g,
  { let f_max : ℕ → bool := λ n, nat.rec ff (λ _ _, tt) n,
    have hf_max : ∀n, f n → f_max n,
    { intros n hn, cases n, rw [fn] at hn, contradiction, apply rfl },
    let g_min : ℕ → bool := λ n, nat.rec tt (λ _ _, ff) n,
    have hg_min : ∀n, g_min n → g n,
    { intros n hn, cases n, rw [gn], apply rfl, contradiction },
    apply (cantor_function_le (le_of_lt h1) h3 hf_max).trans_lt,
    refine lt_of_lt_of_le _ (cantor_function_le (le_of_lt h1) h3 hg_min),
    have : c / (1 - c) < 1,
    { rw [div_lt_one, lt_sub_iff_add_lt],
      { convert add_lt_add h2 h2, norm_num },
      rwa sub_pos },
    convert this,
    { rw [cantor_function_succ _ (le_of_lt h1) h3, div_eq_mul_inv,
          ←tsum_geometric_of_lt_1 (le_of_lt h1) h3],
      apply zero_add },
    { convert tsum_eq_single 0 _,
      { apply_instance },
      { intros n hn, cases n, contradiction, refl } } },
  rw [cantor_function_succ f (le_of_lt h1) h3, cantor_function_succ g (le_of_lt h1) h3],
  rw [hn 0 $ zero_lt_succ n],
  apply add_lt_add_left, rw mul_lt_mul_left h1, exact ih (λ k hk, hn _ $ succ_lt_succ hk) fn gn
end

/-- `cantor_function c` is injective if `0 < c < 1/2`. -/
lemma cantor_function_injective (h1 : 0 < c) (h2 : c < 1 / 2) :
  function.injective (cantor_function c) :=
begin
  intros f g hfg, classical, by_contra h, revert hfg,
  have : ∃n, f n ≠ g n,
  { rw [←not_forall], intro h', apply h, ext, apply h' },
  let n := nat.find this,
  have hn : ∀ (k : ℕ), k < n → f k = g k,
  { intros k hk, apply of_not_not, exact nat.find_min this hk },
  cases fn : f n,
  { apply ne_of_lt, refine increasing_cantor_function h1 h2 hn fn _,
    apply eq_tt_of_not_eq_ff, rw [←fn], apply ne.symm, exact nat.find_spec this },
  { apply ne_of_gt, refine increasing_cantor_function h1 h2 (λ k hk, (hn k hk).symm) _ fn,
    apply eq_ff_of_not_eq_tt, rw [←fn], apply ne.symm, exact nat.find_spec this }
end

/-- The cardinality of the reals, as a type. -/
lemma mk_real : #ℝ = 𝔠 :=
begin
  apply le_antisymm,
  { rw real.equiv_Cauchy.cardinal_eq,
    apply mk_quotient_le.trans, apply (mk_subtype_le _).trans_eq,
    rw [← power_def, mk_nat, mk_rat, omega_power_omega] },
  { convert mk_le_of_injective (cantor_function_injective _ _),
    rw [←power_def, mk_bool, mk_nat, two_power_omega], exact 1 / 3, norm_num, norm_num }
end

/-- The cardinality of the reals, as a set. -/
lemma mk_univ_real : #(set.univ : set ℝ) = 𝔠 :=
by rw [mk_univ, mk_real]

/-- **Non-Denumerability of the Continuum**: The reals are not countable. -/
lemma not_countable_real : ¬ countable (set.univ : set ℝ) :=
by { rw [← mk_set_le_omega, not_le, mk_univ_real], apply cantor }

/-- The cardinality of the interval (a, ∞). -/
lemma mk_Ioi_real (a : ℝ) : #(Ioi a) = 𝔠 :=
begin
  refine le_antisymm (mk_real ▸ mk_set_le _) _,
  rw [← not_lt], intro h,
  refine ne_of_lt _ mk_univ_real,
  have hu : Iio a ∪ {a} ∪ Ioi a = set.univ,
  { convert Iic_union_Ioi, exact Iio_union_right },
  rw ← hu,
  refine lt_of_le_of_lt (mk_union_le _ _) _,
  refine lt_of_le_of_lt (add_le_add_right (mk_union_le _ _) _) _,
  have h2 : (λ x, a + a - x) '' Ioi a = Iio a,
  { convert image_const_sub_Ioi _ _, simp },
  rw ← h2,
  refine add_lt_of_lt (cantor _).le _ h,
  refine add_lt_of_lt (cantor _).le (mk_image_le.trans_lt h) _,
  rw mk_singleton,
  exact one_lt_omega.trans (cantor _)
end

/-- The cardinality of the interval [a, ∞). -/
lemma mk_Ici_real (a : ℝ) : #(Ici a) = 𝔠 :=
le_antisymm (mk_real ▸ mk_set_le _) (mk_Ioi_real a ▸ mk_le_mk_of_subset Ioi_subset_Ici_self)

/-- The cardinality of the interval (-∞, a). -/
lemma mk_Iio_real (a : ℝ) : #(Iio a) = 𝔠 :=
begin
  refine le_antisymm (mk_real ▸ mk_set_le _) _,
  have h2 : (λ x, a + a - x) '' Iio a = Ioi a,
  { convert image_const_sub_Iio _ _, simp },
  exact mk_Ioi_real a ▸ h2 ▸ mk_image_le
end

/-- The cardinality of the interval (-∞, a]. -/
lemma mk_Iic_real (a : ℝ) : #(Iic a) = 𝔠 :=
le_antisymm (mk_real ▸ mk_set_le _) (mk_Iio_real a ▸ mk_le_mk_of_subset Iio_subset_Iic_self)

/-- The cardinality of the interval (a, b). -/
lemma mk_Ioo_real {a b : ℝ} (h : a < b) : #(Ioo a b) = 𝔠 :=
begin
  refine le_antisymm (mk_real ▸ mk_set_le _) _,
  have h1 : #((λ x, x - a) '' Ioo a b) ≤ #(Ioo a b) := mk_image_le,
  refine le_trans _ h1,
  rw [image_sub_const_Ioo, sub_self],
  replace h := sub_pos_of_lt h,
  have h2 : #(has_inv.inv '' Ioo 0 (b - a)) ≤ #(Ioo 0 (b - a)) := mk_image_le,
  refine le_trans _ h2,
  rw [image_inv_Ioo_0_left h, mk_Ioi_real]
end

/-- The cardinality of the interval [a, b). -/

/-- The cardinality of the interval [a, b]. -/
lemma mk_Icc_real {a b : ℝ} (h : a < b) : #(Icc a b) = 𝔠 :=
le_antisymm (mk_real ▸ mk_set_le _) (mk_Ioo_real h ▸ mk_le_mk_of_subset Ioo_subset_Icc_self)

/-- The cardinality of the interval (a, b]. -/
lemma mk_Ioc_real {a b : ℝ} (h : a < b) : #(Ioc a b) = 𝔠 :=
le_antisymm (mk_real ▸ mk_set_le _) (mk_Ioo_real h ▸ mk_le_mk_of_subset Ioo_subset_Ioc_self)

end cardinal


/-
  Presheaf (of types).

  https://stacks.math.columbia.edu/tag/006D
-/

import topology.basic
import topology.opens

universes u v

-- Definition of a presheaf.

open topological_space lattice

structure presheaf (α : Type u) [topological_space α] :=
(F     : opens α → Type v)
(res   : ∀ (U V) (HVU : V ⊆ U), F U → F V)
(Hid   : ∀ (U), res U U (set.subset.refl U) = id)
(Hcomp : ∀ (U V W) (HWV : W ⊆ V) (HVU : V ⊆ U),
  res U W (set.subset.trans HWV HVU) = res V W HWV ∘ res U V HVU)

namespace presheaf

variables {α : Type u} [topological_space α]

instance : has_coe_to_fun (presheaf α) :=
{ F := λ _, opens α → Type v,
  coe := presheaf.F }

-- Simplification lemmas for Hid and Hcomp.

@[simp] lemma Hcomp' (F : presheaf α) :
∀ (U V W) (HWV : W ⊆ V) (HVU : V ⊆ U) (s : F U),
  (F.res U W (set.subset.trans HWV HVU)) s =
  (F.res V W HWV) ((F.res U V HVU) s) :=
λ U V W HWV HVU s, by rw F.Hcomp U V W HWV HVU

@[simp] lemma Hid' (F : presheaf α) :
∀ (U) (s : F U),
  (F.res U U (set.subset.refl U)) s = s :=
λ U s, by rw F.Hid U; simp

-- Morphism of presheaves.

structure morphism (F G : presheaf α) :=
(map      : ∀ (U), F U → G U)
(commutes : ∀ (U V) (HVU : V ⊆ U),
  (G.res U V HVU) ∘ (map U) = (map V) ∘ (F.res U V HVU))

infix `⟶`:80 := morphism

section morphism

def comp {F G H : presheaf α} (fg : F ⟶ G) (gh : G ⟶ H) : F ⟶ H :=
{ map := λ U, gh.map U ∘ fg.map U,
  commutes := λ U V HVU,
    begin
      rw [←function.comp.assoc, gh.commutes U V HVU], symmetry,
      rw [function.comp.assoc, ←fg.commutes U V HVU]
    end }

infix `⊚`:80 := comp

def id (F : presheaf α) : F ⟶ F :=
{ map := λ U, id,
  commutes := λ U V HVU, by simp, }

structure iso (F G : presheaf α) :=
(mor : F ⟶ G)
(inv : G ⟶ F)
(mor_inv_id : mor ⊚ inv = id F)
(inv_mor_id : inv ⊚ mor = id G)

infix `≅`:80 := λ A B, nonempty (iso A B)

end morphism

-- Equality lemma

lemma presheaf_eq_of_subset_eq (F : presheaf α) (U V : opens α)
: U = V → F U = F V :=
λ h, by rw h

end presheaf


/-
Copyright (c) 2022 Alex J. Best. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Alex J. Best

! This file was ported from Lean 3 source module data.finsupp.pwo
! leanprover-community/mathlib commit 59694bd07f0a39c5beccba34bd9f413a160782bf
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Data.Finsupp.Order
import Mathlib.Order.WellFoundedSet

/-!
# Partial well ordering on finsupps

This file contains the fact that finitely supported functions from a fintype are
partially well ordered when the codomain is a linear order that is well ordered.
It is in a separate file for now so as to not add imports to the file `Order.WellFoundedSet`.

## Main statements

* `Finsupp.isPwo` - finitely supported functions from a fintype are partially well ordered when
  the codomain is a linear order that is well ordered

## Tags

Dickson, order, partial well order
-/

/-- A version of **Dickson's lemma** any subset of functions `σ →₀ α` is partially well
ordered, when `σ` is `Finite` and `α` is a linear well order.
This version uses finsupps on a finite type as it is intended for use with `MVPowerSeries`.
-/
theorem Finsupp.isPwo {α σ : Type _} [Zero α] [LinearOrder α] [IsWellOrder α (· < ·)] [Finite σ]
    (S : Set (σ →₀ α)) : S.IsPwo :=
  Finsupp.equivFunOnFinite.symm_image_image S ▸
    Set.PartiallyWellOrderedOn.image_of_monotone_on (Pi.isPwo _) fun _a _b _ha _hb => id
#align finsupp.is_pwo Finsupp.isPwo


import data.real.basic
import data.nat.prime

open nat

-- BEGIN
example : ∃ x : ℝ, 2 < x ∧ x < 4 :=
begin
  use 5 / 2,
  split,
  norm_num,
  norm_num,
end

example : ∃ m n : ℕ,
  4 < m ∧ m < n ∧ n < 10 ∧ nat.prime m ∧ nat.prime n :=
begin
  use [5, 7],
  split; norm_num
end

example {x y : ℝ} : x ≤ y ∧ x ≠ y → x ≤ y ∧ ¬ y ≤ x :=
begin
  rintros ⟨h₀, h₁⟩,
  use [h₀, λ h', h₁ (le_antisymm h₀ h')]
end

/- Alternatively, the proof can be spelled out as done eariler -/
example {x y : ℝ} : x ≤ y ∧ x ≠ y → x ≤ y ∧ ¬ y ≤ x :=
begin
  intro h,
  cases h with h1 h2,
  split,
  exact h1,
  contrapose! h2,
  apply le_antisymm h1 h2,
end
-- END


/-

In lean we represent: (1) propositions as

types and (2) predicates as parameterized

propositions.

A parameterized type gives rise to a whole

family of proposition, once for each value

of each parameter.

We can think of one-parameter predicates

as specifying *properties* of objects. As

an example, as defined, is_even : ℕ → Prop

expresses the property of a natural number

(an argument value) "being even". We define

the constructors to ensure that there is a

proof of (is_even n) if and only if (n : ℕ)

is (a term that represents) an even number.

One-parameter predicates thus also specify

*sets* of objects: namely all and only those

values of a given argument type that have the

specified property as shown by the existence

of a proof that such a value "satisfies the

predicate" (makes the resulting proposition

true).

The is_even predicate thus specifies the

set of all even natural numbers, which we

can write as {0, 2, 4, 6, ...} or, better,

as { n : ℕ | is_even n }.

We can think of two-parameter predicates as

specifying binary relations, that is, sets of

*pairs* of argument values that make a given

proposition true.

The most commonplace example of a two-argument

predicate is equals. We can write (_ = _) to

make it clear that equals takes two values and

yields a proposition that the first equals the

second. For example, if a and b are of type ℕ,

then (a = b) is a proposition that has a proof

if and only if the two terms really are equal

(when reduced). So, for example, 4 = 2 + 2 is

true, there is a proof of it, because reducing

2 + 2 to 4 reduces the whole proposition to

4 = 4, and there is a proof of this proposition.

The proposition, a = b, in Lean is just a nice

shorthand notation for (eq a b). The two-place

predicate, eq, implements the equality relation.

It's polymorphic: it takes a *type parameter*, α,

implicitly. It then explicitly takes two values,

a and b, of type α, and yields the proposition,

a = b (which is of course itself a type).

The eq "type builder" (taking α and two values,

a and b of type α) and yielding the proposition

(a type), a = b, provides a single constructor

with which to build proofs. It's called refl,

and it establishes the "reflexive" property of

the eq relation, by taking *any* value, a : α,

for any type, α, and by returning a proof of

(eq a a), that is, of a = a. So, by applying

eq.refl to any value, a, of any type, we get

a proof of (a = a), and those are the *only*

proofs of equality that can be constructed.

There's thus no way to prove that 0 = 1, for

example, because those terms don't reduce to

the same values, and we can only use eq.refl

to construct proofs that two terms are equal

if they really do reduce to exactly the same

value.

Finally, we will represent some, but no all,

of the logical connetives, including "and",

"or", and "iff", as polymorphic propositions.

Whereas a predicate, such as eq, will take

values of arbitrary types as arguments (e.g.,

eq can take values of type ℕ), connectives

are like polymorphic types: they take types

as arguments, but in this case the types are

of type Prop.

Consider "and" for example. If P and Q are

any two pre-existing propositions (types of

type Prop), then we can form the proposition,

P ∧ Q, and it, too, will be a proposition (a

type of type, Prop). The type of "and" is thus

Prop → Prop → Prop: it takes two propositions

(types of type Prop) as arguments and yields

a new proposition (a new type of type Prop).

To enforce the logical meaning of "and", we

define the "and" type builder to have just

one constructor, which we call "intro", that

if applied to a proof, p, of P and to a proof,

q, of Q yields a term, (and.intro p q), that

is axiomatically accepted as being a "proof"

of (and P Q), which we write using an infix

notation as (P ∧ Q).

Finally, we note that if we're given a term,

(and.intro p q), of type (P ∧ Q), that is, if

we're given a proof of (P ∧ Q), we can get a

proof of P from it, by "destructuring" the

term using pattern matching. For example, we

can obtain "p" (the proof of P "contained

inside" the proof of P ∧ Q) with the following

rule: | (and.intro p _) := p. This is how we

implement the two "elimination rules" for and.

-/

/-

Here's a statement of the proposition that

2 + 2 = 4 and a proof of it.

-/

lemma proof_of_two_plus_two_eq_four : 2 + 2 = 4 :=

    eq.refl 4

/-

1. State and prove the proposition that the

string,"Lean!", appended to the string, "Hello, "

is equal to the string, "Hello, Lean!". You may

and should use the Lean library-provided string

append operator, ++, as an infix notation for

append.

-/

-- string append example

#eval "Hi " ++ "There!"

-- fill in the blanks

lemma proof1 : _ = _

/-

2.  Sometimes we want to state and prove a

proposition without binding a name to the proof.

For that, instead of using def, theorem, or

lemma, we can use "example." Here's an example.

-/

example : 1 = 1 := _    -- Fill blank with proof.

/-

Note that we can use example for ordinary types

as well.

-/

example : ℕ := 5

/-

2. State and prove the proposition that 3 * 4 = 12,

using "example."

-/

-- Answer Here

/-

3. Use example to formally state and prove the

proposition that 1 = 1 ∧ 0 = 0.

-/

-- Here

/-

4. Complete the following program to show that

if P and Q are arbitrary propositions, and *if*

you're given a proof of P and Q, then you can

obtain and return a proof of P.

Hint: In Lean, and.intro is the one introduction

(proof creating) rule for and, and and.elim_left

and and.elim_right are the two elimination rules.

They are functions. Use one of them!

-/

theorem P_and_Q_imp_P {P Q : Prop} (pq : P ∧ Q) : P :=

    _


import Mathlib.Tactic.Basic
import Mathlib.Tactic.Cases
import Mathlib.Init.Data.Nat.Basic
/-!
## Structural Induction

_Structural induction_ is a generalization of mathematical induction to arbitrary inductive types.
To prove a goal `n : ℕ ⊢ P[n]` by structural induction on `n`, it suffces to show two subgoals,
traditionally called the base case and the induction step:
```lean
⊢ P[0]
k : ℕ,ih : P[k] ⊢ P[k + 1]
```
We can of course also write `Nat.zero` and `Nat.succ k`.

In general, the situation is more complex. The goal might contain some extra hypotheses (e.g., `Q`)
that do not depend on `n` and others (e.g., `R[n]`) that do. Assuming we have one hypothesis of each
kind, this gives the initial goal
```lean
hQ : Q, n : N, hR : R[n] ⊢ S[n]
```
Structural induction on `n` then produces the two subgoals
```lean
hQ : Q, hR : R[0] ⊢ S[0]
hQ : Q, k : N, ih : R[k] → S[k], hR : R[k + 1] ⊢ S[k + 1]
```
The hypothesis `Q` is simply carried over unchanged from the initial goal, whereas
`R[n] ⊢ S[n]` is treated almost the same as if the goal’s target had been
`R[n] → S[n]`. This is easy to check by taking `P[n] := R[n] → S[n]` in the first example
above. Since this general format is very verbose and hardly informative (now that
we understand how it works), from now on we will present goals in the simplest
form possible, without extra hypotheses.

For lists, given a goal `xs : List α ⊢ P[xs]`, structural induction on `xs` yields
```lean
⊢ P[[]]
y : α, ys : List α, ih : P[ys] ⊢ P[y :: ys]
```
We can of course also write `List.nil` and `List.cons y ys`. There is no induction hypothesis
associated with `y`, because `y` is not of List type.

For arithmetic expressions, the base cases are
```
i : ℤ ⊢ P[Aexp.num i]                    x : string ⊢ P[Aexp.var x]
```
and the induction steps for `add`, `sub`, `mul` and `div` are
```
e₁ e₂ : Aexp, ih₁ : P[e₁], ih₂ : P[e₂] ⊢ P[Aexp.add e₁ e₂]
e₁ e₂ : Aexp, ih₁ : P[e₁], ih₂ : P[e₂] ⊢ P[Aexp.sub e₁ e₂]
e₁ e₂ : Aexp, ih₁ : P[e₁], ih₂ : P[e₂] ⊢ P[Aexp.mul e₁ e₂]
e₁ e₂ : Aexp, ih₁ : P[e₁], ih₂ : P[e₂] ⊢ P[Aexp.div e₁ e₂]
```
Notice the two induction hypotheses, about e₁ and e₂.

In general, structural induction produces one subgoal per constructor. In each subgoal, induction
hypotheses are available for all constructor arguments of the type we are performing the induction
on.

Regardless of the inductive type `τ`, the procedure to compute the subgoals is
always the same:

1. Replace the hole in `P[ ]` with each possible constructor applied to fresh
variables (e.g., `y :: ys`), yielding as many subgoals as there are constructors.
2. Add these new variables (e.g., `y`, `ys`) to the local context.
3. Add induction hypotheses for all new variables of type `τ`.

As an example, we will prove that `Nat.succ n ≠ n` for all `n : ℕ`. We start with
an informal proof, because these require us to understand what we are doing:

The proof is by structural induction on `n`.

`Case 0`: We must show `Nat.succ 0 ≠ 0`. This follows from the “no confusion” property
of the constructors of inductive types.

`Case Nat.succ k`: The induction hypothesis is `Nat.succ k ≠ k`. We
must show `Nat.succ (Nat.succ k) ≠ Nat.succ k`. By the injectivity of
`Nat.succ`, we have that `Nat.succ (Nat.succ k) = Nat.succ k` is equivalent to
`Nat.succ k = k`. Thus, it suffces to prove `Nat.succ k ≠ k`,
which corresponds exactly to the induction hypothesis.

Notice the main features of this informal proof, which you should aim to reproduce in your own
informal arguments:

- The proof starts with an unambiguous announcement of the type of proof we are carrying out (e.g.,
  which kind of induction and on which variable).
- The cases are clearly identified, and for each case, both the goal’s target and the hypotheses are
  stated.
- The key lemmas on which the proof relies are explicitly invoked (e.g., injectivity of `Nat.succ`).

Now let us carry out the proof in Lean:
-/
lemma nat.succ_neq_self (n : ℕ) :
  Nat.succ n ≠ n := by
  induction' n with n ih  -- ih: Nat.succ n ≠ n
  { simp }           -- ⊢ Nat.succ Nat.zero ≠ Nat.zero
  { simp [ih] }      -- ⊢ Nat.succ (Nat.succ n) ≠ Nat.succ n
/-!
The routine reasoning about constructors is all carried out by `simp` automatically,
which is usually what we want.

We can supply our own names, and reorder the cases, by using the case tactic
in front of each case, together with the case’s name and the desired names for
the variables and hypotheses introduced by `induction’`. For example:
-/
lemma nat.succ_neq_self2 (n : ℕ) :
  Nat.succ n ≠ n := by
  induction' n with m IH
  case succ  => { simp [IH] }
  case zero => { simp }
/-!
Instead of `n` and `ih`, we chose the names `m` and `IH` and we moved the zero
case to the end.
-/


/-
Copyright (c) 2021 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.normed.group.pointwise
import analysis.normed.group.add_torsor
import analysis.normed_space.basic
import topology.metric_space.hausdorff_distance

/-!
# Properties of pointwise scalar multiplication of sets in normed spaces.

We explore the relationships between scalar multiplication of sets in vector spaces, and the norm.
Notably, we express arbitrary balls as rescaling of other balls, and we show that the
multiplication of bounded sets remain bounded.
-/

open metric set
open_locale pointwise topological_space

variables {𝕜 E : Type*} [normed_field 𝕜]

section semi_normed_group
variables [semi_normed_group E] [normed_space 𝕜 E]

theorem smul_ball {c : 𝕜} (hc : c ≠ 0) (x : E) (r : ℝ) :
  c • ball x r = ball (c • x) (∥c∥ * r) :=
begin
  ext y,
  rw mem_smul_set_iff_inv_smul_mem₀ hc,
  conv_lhs { rw ←inv_smul_smul₀ hc x },
  simp [← div_eq_inv_mul, div_lt_iff (norm_pos_iff.2 hc), mul_comm _ r, dist_smul],
end

lemma smul_unit_ball {c : 𝕜} (hc : c ≠ 0) : c • ball (0 : E) (1 : ℝ) = ball (0 : E) (∥c∥) :=
by rw [smul_ball hc, smul_zero, mul_one]

theorem smul_sphere' {c : 𝕜} (hc : c ≠ 0) (x : E) (r : ℝ) :
  c • sphere x r = sphere (c • x) (∥c∥ * r) :=
begin
  ext y,
  rw mem_smul_set_iff_inv_smul_mem₀ hc,
  conv_lhs { rw ←inv_smul_smul₀ hc x },
  simp only [mem_sphere, dist_smul, norm_inv, ← div_eq_inv_mul,
    div_eq_iff (norm_pos_iff.2 hc).ne', mul_comm r],
end

theorem smul_closed_ball' {c : 𝕜} (hc : c ≠ 0) (x : E) (r : ℝ) :
  c • closed_ball x r = closed_ball (c • x) (∥c∥ * r) :=
by simp only [← ball_union_sphere, set.smul_set_union, smul_ball hc, smul_sphere' hc]

lemma metric.bounded.smul {s : set E} (hs : bounded s) (c : 𝕜) :
  bounded (c • s) :=
begin
  obtain ⟨R, hR⟩ : ∃ (R : ℝ), ∀ x ∈ s, ∥x∥ ≤ R := hs.exists_norm_le,
  refine (bounded_iff_exists_norm_le).2 ⟨∥c∥ * R, _⟩,
  assume z hz,
  obtain ⟨y, ys, rfl⟩ : ∃ (y : E), y ∈ s ∧ c • y = z := mem_smul_set.1 hz,
  calc ∥c • y∥ = ∥c∥ * ∥y∥ : norm_smul _ _
  ... ≤ ∥c∥ * R : mul_le_mul_of_nonneg_left (hR y ys) (norm_nonneg _)
end

/-- If `s` is a bounded set, then for small enough `r`, the set `{x} + r • s` is contained in any
fixed neighborhood of `x`. -/
lemma eventually_singleton_add_smul_subset
  {x : E} {s : set E} (hs : bounded s) {u : set E} (hu : u ∈ 𝓝 x) :
  ∀ᶠ r in 𝓝 (0 : 𝕜), {x} + r • s ⊆ u :=
begin
  obtain ⟨ε, εpos, hε⟩ : ∃ ε (hε : 0 < ε), closed_ball x ε ⊆ u :=
    nhds_basis_closed_ball.mem_iff.1 hu,
  obtain ⟨R, Rpos, hR⟩ : ∃ (R : ℝ), 0 < R ∧ s ⊆ closed_ball 0 R := hs.subset_ball_lt 0 0,
  have : metric.closed_ball (0 : 𝕜) (ε / R) ∈ 𝓝 (0 : 𝕜) :=
    closed_ball_mem_nhds _ (div_pos εpos Rpos),
  filter_upwards [this] with r hr,
  simp only [image_add_left, singleton_add],
  assume y hy,
  obtain ⟨z, zs, hz⟩ : ∃ (z : E), z ∈ s ∧ r • z = -x + y, by simpa [mem_smul_set] using hy,
  have I : ∥r • z∥ ≤ ε := calc
    ∥r • z∥ = ∥r∥ * ∥z∥ : norm_smul _ _
    ... ≤ (ε / R) * R :
      mul_le_mul (mem_closed_ball_zero_iff.1 hr)
        (mem_closed_ball_zero_iff.1 (hR zs)) (norm_nonneg _) (div_pos εpos Rpos).le
    ... = ε : by field_simp [Rpos.ne'],
  have : y = x + r • z, by simp only [hz, add_neg_cancel_left],
  apply hε,
  simpa only [this, dist_eq_norm, add_sub_cancel', mem_closed_ball] using I,
end

/-- Any ball is the image of a ball centered at the origin under a shift. -/
lemma vadd_ball_zero (x : E) (r : ℝ) : x +ᵥ ball 0 r = ball x r :=
by rw [vadd_ball, vadd_eq_add, add_zero]

/-- Any closed ball is the image of a closed ball centered at the origin under a shift. -/
lemma vadd_closed_ball_zero (x : E) (r : ℝ) : x +ᵥ closed_ball 0 r = closed_ball x r :=
by rw [vadd_closed_ball, vadd_eq_add, add_zero]

variables [normed_space ℝ E]

/-- In a real normed space, the image of the unit ball under scalar multiplication by a positive
constant `r` is the ball of radius `r`. -/
lemma smul_unit_ball_of_pos {r : ℝ} (hr : 0 < r) : r • ball 0 1 = ball (0 : E) r :=
by rw [smul_unit_ball hr.ne', real.norm_of_nonneg hr.le]

end semi_normed_group

section normed_group
variables [normed_group E] [normed_space 𝕜 E]

theorem smul_closed_ball (c : 𝕜) (x : E) {r : ℝ} (hr : 0 ≤ r) :
  c • closed_ball x r = closed_ball (c • x) (∥c∥ * r) :=
begin
  rcases eq_or_ne c 0 with rfl|hc,
  { simp [hr, zero_smul_set, set.singleton_zero, ← nonempty_closed_ball] },
  { exact smul_closed_ball' hc x r }
end

lemma smul_closed_unit_ball (c : 𝕜) : c • closed_ball (0 : E) (1 : ℝ) = closed_ball (0 : E) (∥c∥) :=
by rw [smul_closed_ball _ _ zero_le_one, smul_zero, mul_one]

variables [normed_space ℝ E]

/-- In a real normed space, the image of the unit closed ball under multiplication by a nonnegative
number `r` is the closed ball of radius `r` with center at the origin. -/
lemma smul_closed_unit_ball_of_nonneg {r : ℝ} (hr : 0 ≤ r) :
  r • closed_ball 0 1 = closed_ball (0 : E) r :=
by rw [smul_closed_unit_ball, real.norm_of_nonneg hr]

/-- In a nontrivial real normed space, a sphere is nonempty if and only if its radius is
nonnegative. -/
@[simp] lemma normed_space.sphere_nonempty [nontrivial E] {x : E} {r : ℝ} :
  (sphere x r).nonempty ↔ 0 ≤ r :=
begin
  obtain ⟨y, hy⟩ := exists_ne x,
  refine ⟨λ h, nonempty_closed_ball.1 (h.mono sphere_subset_closed_ball), λ hr,
    ⟨r • ∥y - x∥⁻¹ • (y - x) + x, _⟩⟩,
  have : ∥y - x∥ ≠ 0, by simpa [sub_eq_zero],
  simp [norm_smul, this, real.norm_of_nonneg hr],
end

lemma smul_sphere [nontrivial E] (c : 𝕜) (x : E) {r : ℝ} (hr : 0 ≤ r) :
  c • sphere x r = sphere (c • x) (∥c∥ * r) :=
begin
  rcases eq_or_ne c 0 with rfl|hc,
  { simp [zero_smul_set, set.singleton_zero, hr] },
  { exact smul_sphere' hc x r }
end

/-- Any ball `metric.ball x r`, `0 < r` is the image of the unit ball under `λ y, x + r • y`. -/
lemma affinity_unit_ball {r : ℝ} (hr : 0 < r) (x : E) : x +ᵥ r • ball 0 1 = ball x r :=
by rw [smul_unit_ball_of_pos hr, vadd_ball_zero]

/-- Any closed ball `metric.closed_ball x r`, `0 ≤ r` is the image of the unit closed ball under
`λ y, x + r • y`. -/
lemma affinity_unit_closed_ball {r : ℝ} (hr : 0 ≤ r) (x : E) :
  x +ᵥ r • closed_ball 0 1 = closed_ball x r :=
by rw [smul_closed_unit_ball, real.norm_of_nonneg hr, vadd_closed_ball_zero]

end normed_group


import data.fintype.basic data.fintype.card
import algebra.big_operators.basic algebra.big_operators.order
import tactic.squeeze

namespace MAS114

universes u v
variables {α : Type u} {β : Type v} (p : α → β)
variables [fintype α] [fintype β] [decidable_eq α] [decidable_eq β]

def fiber (b : β) : Type* := { a : α // p a = b }

instance (b : β) : fintype (fiber p b) :=
 by { dsimp[fiber], apply_instance }

def fiber' (b : β) : finset α := finset.univ.filter (λ a, p a = b)

lemma mem_fiber' (b : β) (a : α) : a ∈ fiber' p b ↔ p a = b :=
 ⟨λ h,(finset.mem_filter.mp h).right,
  λ h,finset.mem_filter.mpr ⟨finset.mem_univ a,h⟩⟩

lemma card_fiber (b : β) : fintype.card (fiber p b) = (fiber' p b).card :=
 fintype.subtype_card (fiber' p b) (mem_fiber' p b)

def equiv_fibre_sigma : α ≃ Σ (b : β), (fiber p b) := {
  to_fun := λ a, ⟨p a,⟨a,rfl⟩⟩,
  inv_fun := λ x, x.2.val,
  left_inv := λ a, by { refl },
  right_inv := by { rintro ⟨b,⟨a,⟨e⟩⟩⟩, simp only[heq_iff_eq],split; refl }
}

lemma card_eq_fiber_sum :
 fintype.card α = finset.univ.sum (λ b, fintype.card (fiber p b)) :=
 (fintype.card_congr (equiv_fibre_sigma p)).trans (fintype.card_sigma (fiber p))

lemma card_eq_fiber_sum' :
 fintype.card α = finset.univ.sum (λ b, finset.card (fiber' p b)) :=
begin
 let e0 := card_eq_fiber_sum p,
 let e1 : ∀ b : β, b ∈ finset.univ → fintype.card (fiber p b) = finset.card (fiber' p b)
  := λ b _, card_fiber p b,
 let e2 := @finset.sum_congr ℕ β finset.univ _ _ _ _ rfl e1,
 exact e0.trans e2,
end

variable {p}

lemma fiber_nonempty_of_surjective
 (p_surj : function.surjective p) (b : β) : nonempty (fiber p b) :=
begin
 rcases p_surj b with ⟨a,e⟩,
 exact ⟨⟨a,e⟩⟩,
end

lemma card_le_of_surjective :
 function.surjective p → (fintype.card β) ≤ (fintype.card α) :=
begin
 intro p_surj,
 let c : β → ℕ := λ b, 1,
 have h0 : ∀ b, b ∈ finset.univ → (c b) ≤ fintype.card (fiber p b) :=
  λ b _, fintype.card_pos_iff.mpr (fiber_nonempty_of_surjective p_surj b),
 have h1 := @finset.sum_le_sum β ℕ _ c _ finset.univ h0,
 let h2 := calc
  finset.sum finset.univ (λ b : β, 1) =
   add_monoid.nsmul finset.univ.card 1 : finset.sum_const 1
  ... = ↑finset.univ.card : nsmul_one _
  ... = finset.univ.card : nat.cast_id _
  ... = fintype.card β : rfl,
 rw[h2,← card_eq_fiber_sum p] at h1,
 exact h1
end

end MAS114


/-
Copyright (c) 2016 Jeremy Avigad. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jeremy Avigad, Leonardo de Moura, Mario Carneiro, Johannes Hölzl
-/
import algebra.order.group.defs
import algebra.hom.equiv.units.basic

/-!
# Inverse and multiplication as order isomorphisms in ordered groups

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

-/

set_option old_structure_cmd true
open function

universe u
variable {α : Type u}

section group
variables [group α]

section typeclasses_left_right_le
variables [has_le α] [covariant_class α α (*) (≤)] [covariant_class α α (swap (*)) (≤)]
  {a b c d : α}

section

variable (α)

/-- `x ↦ x⁻¹` as an order-reversing equivalence. -/
@[to_additive "`x ↦ -x` as an order-reversing equivalence.", simps]
def order_iso.inv : α ≃o αᵒᵈ :=
{ to_equiv := (equiv.inv α).trans order_dual.to_dual,
  map_rel_iff' := λ a b, @inv_le_inv_iff α _ _ _ _ _ _ }

end

@[to_additive neg_le]
lemma inv_le' : a⁻¹ ≤ b ↔ b⁻¹ ≤ a :=
(order_iso.inv α).symm_apply_le

alias inv_le' ↔ inv_le_of_inv_le' _
attribute [to_additive neg_le_of_neg_le] inv_le_of_inv_le'

@[to_additive le_neg]
lemma le_inv' : a ≤ b⁻¹ ↔ b ≤ a⁻¹ :=
(order_iso.inv α).le_symm_apply

end typeclasses_left_right_le

end group

alias le_inv' ↔ le_inv_of_le_inv _
attribute [to_additive] le_inv_of_le_inv

section group
variables [group α] [has_le α]

section right
variables [covariant_class α α (swap (*)) (≤)] {a b c d : α}

/-- `equiv.mul_right` as an `order_iso`. See also `order_embedding.mul_right`. -/
@[to_additive "`equiv.add_right` as an `order_iso`. See also `order_embedding.add_right`.",
  simps to_equiv apply {simp_rhs := tt}]
def order_iso.mul_right (a : α) : α ≃o α :=
{ map_rel_iff' := λ _ _, mul_le_mul_iff_right a, to_equiv := equiv.mul_right a }

@[simp, to_additive] lemma order_iso.mul_right_symm (a : α) :
  (order_iso.mul_right a).symm = order_iso.mul_right a⁻¹ :=
by { ext x, refl }

end right

section left
variables [covariant_class α α (*) (≤)]

/-- `equiv.mul_left` as an `order_iso`. See also `order_embedding.mul_left`. -/
@[to_additive "`equiv.add_left` as an `order_iso`. See also `order_embedding.add_left`.",
  simps to_equiv apply  {simp_rhs := tt}]
def order_iso.mul_left (a : α) : α ≃o α :=
{ map_rel_iff' := λ _ _, mul_le_mul_iff_left a, to_equiv := equiv.mul_left a }

@[simp, to_additive] lemma order_iso.mul_left_symm (a : α) :
  (order_iso.mul_left a).symm = order_iso.mul_left a⁻¹ :=
by { ext x, refl }

end left

end group


/-
Copyright (c) 2021 Heather Macbeth. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Heather Macbeth
-/
import analysis.normed.group.pointwise
import analysis.mean_inequalities
import analysis.mean_inequalities_pow
import topology.algebra.ordered.liminf_limsup

/-!
# ℓp space

This file describes properties of elements `f` of a pi-type `Π i, E i` with finite "norm",
defined for `p:ℝ≥0∞` as the size of the support of `f` if `p=0`, `(∑' a, ∥f a∥^p) ^ (1/p)` for
`0 < p < ∞` and `⨆ a, ∥f a∥` for `p=∞`.

The Prop-valued `mem_ℓp f p` states that a function `f : Π i, E i` has finite norm according
to the above definition; that is, `f` has finite support if `p = 0`, `summable (λ a, ∥f a∥^p)` if
`0 < p < ∞`, and `bdd_above (norm '' (set.range f))` if `p = ∞`.

The space `lp E p` is the subtype of elements of `Π i : α, E i` which satisfy `mem_ℓp f p`. For
`1 ≤ p`, the "norm" is genuinely a norm and `lp` is a complete metric space.

## Main definitions

* `mem_ℓp f p` : property that the function `f` satisfies, as appropriate, `f` finitely supported
  if `p = 0`, `summable (λ a, ∥f a∥^p)` if `0 < p < ∞`, and `bdd_above (norm '' (set.range f))` if
  `p = ∞`
* `lp E p` : elements of `Π i : α, E i` such that `mem_ℓp f p`. Defined as an `add_subgroup` of
  a type synonym `pre_lp` for `Π i : α, E i`, and equipped with a `normed_group` structure; also
  equipped with `normed_space 𝕜` and `complete_space` instances under appropriate conditions

## Main results

* `mem_ℓp.of_exponent_ge`: For `q ≤ p`, a function which is `mem_ℓp` for `q` is also `mem_ℓp` for
  `p`
* `lp.mem_ℓp_of_tendsto`, `lp.norm_le_of_tendsto`: A pointwise limit of functions in `lp`, all with
  `lp` norm `≤ C`, is itself in `lp` and has `lp` norm `≤ C`.
* `lp.tsum_mul_le_mul_norm`: basic form of Hölder's inequality

## Implementation

Since `lp` is defined as an `add_subgroup`, dot notation does not work. Use `lp.norm_neg f` to
say that `∥-f∥ = ∥f∥`, instead of the non-working `f.norm_neg`.

## TODO

* More versions of Hölder's inequality (for example: the case `p = 1`, `q = ∞`; a version for normed
  rings which has `∥∑' i, f i * g i∥` rather than `∑' i, ∥f i∥ * g i∥` on the RHS; a version for
  three exponents satisfying `1 / r = 1 / p + 1 / q`)
* Equivalence with `pi_Lp`, for `α` finite
* Equivalence with `measure_theory.Lp`, for `f : α → E` (i.e., functions rather than pi-types) and
  the counting measure on `α`
* Equivalence with `bounded_continuous_function`, for `f : α → E` (i.e., functions rather than
  pi-types) and `p = ∞`, and the discrete topology on `α`

-/

noncomputable theory
open_locale nnreal ennreal big_operators

variables {α : Type*} {E : α → Type*} {p q : ℝ≥0∞} [Π i, normed_group (E i)]

/-!
### `mem_ℓp` predicate

-/

/-- The property that `f : Π i : α, E i`
* is finitely supported, if `p = 0`, or
* admits an upper bound for `set.range (λ i, ∥f i∥)`, if `p = ∞`, or
* has the series `∑' i, ∥f i∥ ^ p` be summable, if `0 < p < ∞`. -/
def mem_ℓp (f : Π i, E i) (p : ℝ≥0∞) : Prop :=
if p = 0 then (set.finite {i | f i ≠ 0}) else
  (if p = ∞ then bdd_above (set.range (λ i, ∥f i∥)) else summable (λ i, ∥f i∥ ^ p.to_real))

lemma mem_ℓp_zero_iff {f : Π i, E i} : mem_ℓp f 0 ↔ set.finite {i | f i ≠ 0} :=
by dsimp [mem_ℓp]; rw [if_pos rfl]

lemma mem_ℓp_zero {f : Π i, E i} (hf : set.finite {i | f i ≠ 0}) : mem_ℓp f 0 :=
mem_ℓp_zero_iff.2 hf

lemma mem_ℓp_infty_iff {f : Π i, E i} : mem_ℓp f ∞ ↔ bdd_above (set.range (λ i, ∥f i∥)) :=
by dsimp [mem_ℓp]; rw [if_neg ennreal.top_ne_zero, if_pos rfl]

lemma mem_ℓp_infty {f : Π i, E i} (hf : bdd_above (set.range (λ i, ∥f i∥))) : mem_ℓp f ∞ :=
mem_ℓp_infty_iff.2 hf

lemma mem_ℓp_gen_iff (hp : 0 < p.to_real) {f : Π i, E i} :
  mem_ℓp f p ↔ summable (λ i, ∥f i∥ ^ p.to_real) :=
begin
  rw ennreal.to_real_pos_iff at hp,
  dsimp [mem_ℓp],
  rw [if_neg hp.1.ne', if_neg hp.2.ne],
end

lemma mem_ℓp_gen {f : Π i, E i} (hf : summable (λ i, ∥f i∥ ^ p.to_real)) :
  mem_ℓp f p :=
begin
  rcases p.trichotomy with rfl | rfl | hp,
  { apply mem_ℓp_zero,
    have H : summable (λ i : α, (1:ℝ)) := by simpa using hf,
    exact (finite_of_summable_const (by norm_num) H).subset (set.subset_univ _) },
  { apply mem_ℓp_infty,
    have H : summable (λ i : α, (1:ℝ)) := by simpa using hf,
    simpa using ((finite_of_summable_const (by norm_num) H).image (λ i, ∥f i∥)).bdd_above },
  exact (mem_ℓp_gen_iff hp).2 hf
end

lemma mem_ℓp_gen' {C : ℝ} {f : Π i, E i} (hf : ∀ s : finset α, ∑ i in s, ∥f i∥ ^ p.to_real ≤ C) :
  mem_ℓp f p :=
begin
  apply mem_ℓp_gen,
  use ⨆ s : finset α, ∑ i in s, ∥f i∥ ^ p.to_real,
  apply has_sum_of_is_lub_of_nonneg,
  { intros b,
    exact real.rpow_nonneg_of_nonneg (norm_nonneg _) _ },
  apply is_lub_csupr,
  use C,
  rintros - ⟨s, rfl⟩,
  exact hf s
end

lemma zero_mem_ℓp : mem_ℓp (0 : Π i, E i) p :=
begin
  rcases p.trichotomy with rfl | rfl | hp,
  { apply mem_ℓp_zero,
    simp },
  { apply mem_ℓp_infty,
    simp only [norm_zero, pi.zero_apply],
    exact bdd_above_singleton.mono set.range_const_subset, },
  { apply mem_ℓp_gen,
    simp [real.zero_rpow hp.ne', summable_zero], }
end

lemma zero_mem_ℓp' : mem_ℓp (λ i : α, (0 : E i)) p := zero_mem_ℓp

namespace mem_ℓp

lemma finite_dsupport {f : Π i, E i} (hf : mem_ℓp f 0) : set.finite {i | f i ≠ 0} :=
mem_ℓp_zero_iff.1 hf

lemma bdd_above {f : Π i, E i} (hf : mem_ℓp f ∞) : bdd_above (set.range (λ i, ∥f i∥)) :=
mem_ℓp_infty_iff.1 hf

lemma summable (hp : 0 < p.to_real) {f : Π i, E i} (hf : mem_ℓp f p) :
  summable (λ i, ∥f i∥ ^ p.to_real) :=
(mem_ℓp_gen_iff hp).1 hf

lemma neg {f : Π i, E i} (hf : mem_ℓp f p) : mem_ℓp (-f) p :=
begin
  rcases p.trichotomy with rfl | rfl | hp,
  { apply mem_ℓp_zero,
    simp [hf.finite_dsupport] },
  { apply mem_ℓp_infty,
    simpa using hf.bdd_above },
  { apply mem_ℓp_gen,
    simpa using hf.summable hp },
end

@[simp] lemma neg_iff {f : Π i, E i} : mem_ℓp (-f) p ↔ mem_ℓp f p :=
⟨λ h, neg_neg f ▸ h.neg, mem_ℓp.neg⟩

lemma of_exponent_ge {p q : ℝ≥0∞} {f : Π i, E i}
  (hfq : mem_ℓp f q) (hpq : q ≤ p) :
  mem_ℓp f p :=
begin
  rcases ennreal.trichotomy₂ hpq with ⟨rfl, rfl⟩ | ⟨rfl, rfl⟩ | ⟨rfl, hp⟩ | ⟨rfl, rfl⟩ | ⟨hq, rfl⟩
    | ⟨hq, hp, hpq'⟩,
  { exact hfq },
  { apply mem_ℓp_infty,
    obtain ⟨C, hC⟩ := (hfq.finite_dsupport.image (λ i, ∥f i∥)).bdd_above,
    use max 0 C,
    rintros x ⟨i, rfl⟩,
    by_cases hi : f i = 0,
    { simp [hi] },
    { exact (hC ⟨i, hi, rfl⟩).trans (le_max_right _ _) } },
  { apply mem_ℓp_gen,
    have : ∀ i ∉ hfq.finite_dsupport.to_finset, ∥f i∥ ^ p.to_real = 0,
    { intros i hi,
      have : f i = 0 := by simpa using hi,
      simp [this, real.zero_rpow hp.ne'] },
    exact summable_of_ne_finset_zero this },
  { exact hfq },
  { apply mem_ℓp_infty,
    obtain ⟨A, hA⟩ := (hfq.summable hq).tendsto_cofinite_zero.bdd_above_range_of_cofinite,
    use A ^ (q.to_real⁻¹),
    rintros x ⟨i, rfl⟩,
    have : 0 ≤ ∥f i∥ ^ q.to_real := real.rpow_nonneg_of_nonneg (norm_nonneg _) _,
    simpa [← real.rpow_mul, mul_inv_cancel hq.ne'] using
      real.rpow_le_rpow this (hA ⟨i, rfl⟩) (inv_nonneg.mpr hq.le) },
  { apply mem_ℓp_gen,
    have hf' := hfq.summable hq,
    refine summable_of_norm_bounded_eventually _ hf' (@set.finite.subset _ {i | 1 ≤ ∥f i∥} _ _ _),
    { have H : {x : α | 1 ≤ ∥f x∥ ^ q.to_real}.finite,
      { simpa using eventually_lt_of_tendsto_lt (by norm_num : (0:ℝ) < 1)
          hf'.tendsto_cofinite_zero },
      exact H.subset (λ i hi, real.one_le_rpow hi hq.le) },
    { show ∀ i, ¬ (|∥f i∥ ^ p.to_real| ≤ ∥f i∥ ^ q.to_real) → 1 ≤ ∥f i∥,
      intros i hi,
      have : 0 ≤ ∥f i∥ ^ p.to_real := real.rpow_nonneg_of_nonneg (norm_nonneg _) p.to_real,
      simp only [abs_of_nonneg, this] at hi,
      contrapose! hi,
      exact real.rpow_le_rpow_of_exponent_ge' (norm_nonneg _) hi.le hq.le hpq' } }
end

lemma add {f g : Π i, E i} (hf : mem_ℓp f p) (hg : mem_ℓp g p) : mem_ℓp (f + g) p :=
begin
  rcases p.trichotomy with rfl | rfl | hp,
  { apply mem_ℓp_zero,
    refine (hf.finite_dsupport.union hg.finite_dsupport).subset (λ i, _),
    simp only [pi.add_apply, ne.def, set.mem_union_eq, set.mem_set_of_eq],
    contrapose!,
    rintros ⟨hf', hg'⟩,
    simp [hf', hg'] },
  { apply mem_ℓp_infty,
    obtain ⟨A, hA⟩ := hf.bdd_above,
    obtain ⟨B, hB⟩ := hg.bdd_above,
    refine ⟨A + B, _⟩,
    rintros a ⟨i, rfl⟩,
    exact le_trans (norm_add_le _ _) (add_le_add (hA ⟨i, rfl⟩) (hB ⟨i, rfl⟩)) },
  apply mem_ℓp_gen,
  let C : ℝ := if p.to_real < 1 then 1 else 2 ^ (p.to_real - 1),
  refine summable_of_nonneg_of_le _ (λ i, _) (((hf.summable hp).add (hg.summable hp)).mul_left C),
  { exact λ b, real.rpow_nonneg_of_nonneg (norm_nonneg (f b + g b)) p.to_real },
  { refine (real.rpow_le_rpow (norm_nonneg _) (norm_add_le _ _) hp.le).trans _,
    dsimp [C],
    split_ifs with h h,
    { simpa using nnreal.coe_le_coe.2 (nnreal.rpow_add_le_add_rpow (∥f i∥₊) (∥g i∥₊) hp h.le) },
    { let F : fin 2 → ℝ≥0 := ![∥f i∥₊, ∥g i∥₊],
      have : ∀ i, (0:ℝ) ≤ F i := λ i, (F i).coe_nonneg,
      simp only [not_lt] at h,
      simpa [F, fin.sum_univ_succ] using
        real.rpow_sum_le_const_mul_sum_rpow_of_nonneg (finset.univ : finset (fin 2)) h
        (λ i _, (F i).coe_nonneg) } }
end

lemma sub {f g : Π i, E i} (hf : mem_ℓp f p) (hg : mem_ℓp g p) : mem_ℓp (f - g) p :=
by { rw sub_eq_add_neg, exact hf.add hg.neg }

lemma finset_sum {ι} (s : finset ι) {f : ι → Π i, E i} (hf : ∀ i ∈ s, mem_ℓp (f i) p) :
  mem_ℓp (λ a, ∑ i in s, f i a) p :=
begin
  haveI : decidable_eq ι := classical.dec_eq _,
  revert hf,
  refine finset.induction_on s _ _,
  { simp only [zero_mem_ℓp', finset.sum_empty, implies_true_iff], },
  { intros i s his ih hf,
    simp only [his, finset.sum_insert, not_false_iff],
    exact (hf i (s.mem_insert_self i)).add (ih (λ j hj, hf j (finset.mem_insert_of_mem hj))), },
end

section normed_space

variables {𝕜 : Type*} [normed_field 𝕜] [Π i, normed_space 𝕜 (E i)]

lemma const_smul {f : Π i, E i} (hf : mem_ℓp f p) (c : 𝕜) : mem_ℓp (c • f) p :=
begin
  rcases p.trichotomy with rfl | rfl | hp,
  { apply mem_ℓp_zero,
    refine hf.finite_dsupport.subset (λ i, (_ : ¬c • f i = 0 → ¬f i = 0)),
    exact not_imp_not.mpr (λ hf', hf'.symm ▸ (smul_zero c)) },
  { obtain ⟨A, hA⟩ := hf.bdd_above,
    refine mem_ℓp_infty ⟨∥c∥ * A, _⟩,
    rintros a ⟨i, rfl⟩,
    simpa [norm_smul] using mul_le_mul_of_nonneg_left (hA ⟨i, rfl⟩) (norm_nonneg c) },
  { apply mem_ℓp_gen,
    convert (hf.summable hp).mul_left (∥c∥ ^ p.to_real),
    ext i,
    simp [norm_smul, real.mul_rpow (norm_nonneg c) (norm_nonneg (f i))] },
end

lemma const_mul {f : α → 𝕜} (hf : mem_ℓp f p) (c : 𝕜) : mem_ℓp (λ x, c * f x) p :=
@mem_ℓp.const_smul α (λ i, 𝕜) _ _ 𝕜 _ _ _ hf c

end normed_space

end mem_ℓp

/-!
### lp space

The space of elements of `Π i, E i` satisfying the predicate `mem_ℓp`.
-/

/-- We define `pre_lp E` to be a type synonym for `Π i, E i` which, importantly, does not inherit
the `pi` topology on `Π i, E i` (otherwise this topology would descend to `lp E p` and conflict
with the normed group topology we will later equip it with.)

We choose to deal with this issue by making a type synonym for `Π i, E i` rather than for the `lp`
subgroup itself, because this allows all the spaces `lp E p` (for varying `p`) to be subgroups of
the same ambient group, which permits lemma statements like `lp.monotone` (below). -/
@[derive add_comm_group, nolint unused_arguments]
def pre_lp (E : α → Type*) [Π i, normed_group (E i)] : Type* := Π i, E i

instance pre_lp.unique [is_empty α] : unique (pre_lp E) := pi.unique_of_is_empty E

/-- lp space -/
def lp (E : α → Type*) [Π i, normed_group (E i)]
  (p : ℝ≥0∞) : add_subgroup (pre_lp E) :=
{ carrier := {f | mem_ℓp f p},
  zero_mem' := zero_mem_ℓp,
  add_mem' := λ f g, mem_ℓp.add,
  neg_mem' := λ f, mem_ℓp.neg }

namespace lp

instance : has_coe (lp E p) (Π i, E i) := coe_subtype
instance : has_coe_to_fun (lp E p) (λ _, Π i, E i) := ⟨λ f, ((f : Π i, E i) : Π i, E i)⟩

@[ext] lemma ext {f g : lp E p} (h : (f : Π i, E i) = g) : f = g :=
subtype.ext h

protected lemma ext_iff {f g : lp E p} : f = g ↔ (f : Π i, E i) = g :=
subtype.ext_iff

lemma eq_zero' [is_empty α] (f : lp E p) : f = 0 := subsingleton.elim f 0

protected lemma monotone {p q : ℝ≥0∞} (hpq : q ≤ p) : lp E q ≤ lp E p :=
λ f hf, mem_ℓp.of_exponent_ge hf hpq

protected lemma mem_ℓp (f : lp E p) : mem_ℓp f p := f.prop

variables (E p)
@[simp] lemma coe_fn_zero : ⇑(0 : lp E p) = 0 := rfl
variables {E p}

@[simp] lemma coe_fn_neg (f : lp E p) : ⇑(-f) = -f := rfl

@[simp] lemma coe_fn_add (f g : lp E p) : ⇑(f + g) = f + g := rfl

@[simp] lemma coe_fn_sum {ι : Type*} (f : ι → lp E p) (s : finset ι) :
  ⇑(∑ i in s, f i) = ∑ i in s, ⇑(f i) :=
begin
  classical,
  refine finset.induction _ _ s,
  { simp },
  intros i s his,
  simp [finset.sum_insert his],
end

@[simp] lemma coe_fn_sub (f g : lp E p) : ⇑(f - g) = f - g := rfl

instance : has_norm (lp E p) :=
{ norm := λ f, if hp : p = 0 then by subst hp; exact (lp.mem_ℓp f).finite_dsupport.to_finset.card
   else (if p = ∞ then ⨆ i, ∥f i∥ else (∑' i, ∥f i∥ ^ p.to_real) ^ (1/p.to_real)) }

lemma norm_eq_card_dsupport (f : lp E 0) : ∥f∥ = (lp.mem_ℓp f).finite_dsupport.to_finset.card :=
dif_pos rfl

lemma norm_eq_csupr (f : lp E ∞) : ∥f∥ = ⨆ i, ∥f i∥ :=
begin
  dsimp [norm],
  rw [dif_neg ennreal.top_ne_zero, if_pos rfl]
end

lemma is_lub_norm [nonempty α] (f : lp E ∞) : is_lub (set.range (λ i, ∥f i∥)) ∥f∥ :=
begin
  rw lp.norm_eq_csupr,
  exact is_lub_csupr (lp.mem_ℓp f)
end

lemma norm_eq_tsum_rpow (hp : 0 < p.to_real) (f : lp E p) :
  ∥f∥ = (∑' i, ∥f i∥ ^ p.to_real) ^ (1/p.to_real) :=
begin
  dsimp [norm],
  rw ennreal.to_real_pos_iff at hp,
  rw [dif_neg hp.1.ne', if_neg hp.2.ne],
end

lemma norm_rpow_eq_tsum (hp : 0 < p.to_real) (f : lp E p) :
  ∥f∥ ^ p.to_real = ∑' i, ∥f i∥ ^ p.to_real :=
begin
  rw [norm_eq_tsum_rpow hp, ← real.rpow_mul],
  { field_simp [hp.ne'] },
  apply tsum_nonneg,
  intros i,
  calc (0:ℝ) = 0 ^ p.to_real : by rw real.zero_rpow hp.ne'
  ... ≤ _ : real.rpow_le_rpow rfl.le (norm_nonneg (f i)) hp.le
end

lemma has_sum_norm (hp : 0 < p.to_real) (f : lp E p) :
  has_sum (λ i, ∥f i∥ ^ p.to_real) (∥f∥ ^ p.to_real) :=
begin
  rw norm_rpow_eq_tsum hp,
  exact ((lp.mem_ℓp f).summable hp).has_sum
end

lemma norm_nonneg' (f : lp E p) : 0 ≤ ∥f∥ :=
begin
  rcases p.trichotomy with rfl | rfl | hp,
  { simp [lp.norm_eq_card_dsupport f] },
  { cases is_empty_or_nonempty α with _i _i; resetI,
    { rw lp.norm_eq_csupr,
      simp [real.csupr_empty] },
    inhabit α,
    exact (norm_nonneg (f default)).trans ((lp.is_lub_norm f).1 ⟨default, rfl⟩) },
  { rw lp.norm_eq_tsum_rpow hp f,
    refine real.rpow_nonneg_of_nonneg (tsum_nonneg _) _,
    exact λ i, real.rpow_nonneg_of_nonneg (norm_nonneg _) _ },
end

@[simp] lemma norm_zero : ∥(0 : lp E p)∥ = 0 :=
begin
  rcases p.trichotomy with rfl | rfl | hp,
  { simp [lp.norm_eq_card_dsupport] },
  { simp [lp.norm_eq_csupr] },
  { rw lp.norm_eq_tsum_rpow hp,
    have hp' : 1 / p.to_real ≠ 0 := one_div_ne_zero hp.ne',
    simpa [real.zero_rpow hp.ne'] using real.zero_rpow hp' }
end

lemma norm_eq_zero_iff ⦃f : lp E p⦄ : ∥f∥ = 0 ↔ f = 0 :=
begin
  classical,
  refine ⟨λ h, _, by { rintros rfl, exact norm_zero }⟩,
  rcases p.trichotomy with rfl | rfl | hp,
  { ext i,
    have : {i : α | ¬f i = 0} = ∅ := by simpa [lp.norm_eq_card_dsupport f] using h,
    have : (¬ (f i = 0)) = false := congr_fun this i,
    tauto },
  { cases is_empty_or_nonempty α with _i _i; resetI,
    { simp },
    have H : is_lub (set.range (λ i, ∥f i∥)) 0,
    { simpa [h] using lp.is_lub_norm f },
    ext i,
    have : ∥f i∥ = 0 := le_antisymm (H.1 ⟨i, rfl⟩) (norm_nonneg _),
    simpa using this },
  { have hf : has_sum (λ (i : α), ∥f i∥ ^ p.to_real) 0,
    { have := lp.has_sum_norm hp f,
      rwa [h, real.zero_rpow hp.ne'] at this },
    have : ∀ i, 0 ≤ ∥f i∥ ^ p.to_real := λ i, real.rpow_nonneg_of_nonneg (norm_nonneg _) _,
    rw has_sum_zero_iff_of_nonneg this at hf,
    ext i,
    have : f i = 0 ∧ p.to_real ≠ 0,
    { simpa [real.rpow_eq_zero_iff_of_nonneg (norm_nonneg (f i))] using congr_fun hf i },
    exact this.1 },
end

lemma eq_zero_iff_coe_fn_eq_zero {f : lp E p} : f = 0 ↔ ⇑f = 0 :=
by rw [lp.ext_iff, coe_fn_zero]

@[simp] lemma norm_neg ⦃f : lp E p⦄ : ∥-f∥ = ∥f∥ :=
begin
  rcases p.trichotomy with rfl | rfl | hp,
  { simp [lp.norm_eq_card_dsupport] },
  { cases is_empty_or_nonempty α; resetI,
    { simp [lp.eq_zero' f], },
    apply (lp.is_lub_norm (-f)).unique,
    simpa using lp.is_lub_norm f },
  { suffices : ∥-f∥ ^ p.to_real = ∥f∥ ^ p.to_real,
    { exact real.rpow_left_inj_on hp.ne' (norm_nonneg' _) (norm_nonneg' _) this },
    apply (lp.has_sum_norm hp (-f)).unique,
    simpa using lp.has_sum_norm hp f }
end

instance [hp : fact (1 ≤ p)] : normed_group (lp E p) :=
normed_group.of_core _
{ norm_eq_zero_iff := norm_eq_zero_iff,
  triangle := λ f g, begin
    unfreezingI { rcases p.dichotomy with rfl | hp' },
    { cases is_empty_or_nonempty α; resetI,
      { simp [lp.eq_zero' f] },
      refine (lp.is_lub_norm (f + g)).2 _,
      rintros x ⟨i, rfl⟩,
      refine le_trans _ (add_mem_upper_bounds_add (lp.is_lub_norm f).1 (lp.is_lub_norm g).1
        ⟨_, _, ⟨i, rfl⟩, ⟨i, rfl⟩, rfl⟩),
      exact norm_add_le (f i) (g i) },
    { have hp'' : 0 < p.to_real := zero_lt_one.trans_le hp',
      have hf₁ : ∀ i, 0 ≤ ∥f i∥ := λ i, norm_nonneg _,
      have hg₁ : ∀ i, 0 ≤ ∥g i∥ := λ i, norm_nonneg _,
      have hf₂ := lp.has_sum_norm hp'' f,
      have hg₂ := lp.has_sum_norm hp'' g,
      -- apply Minkowski's inequality
      obtain ⟨C, hC₁, hC₂, hCfg⟩ :=
        real.Lp_add_le_has_sum_of_nonneg hp' hf₁ hg₁ (norm_nonneg' _) (norm_nonneg' _) hf₂ hg₂,
      refine le_trans _ hC₂,
      rw ← real.rpow_le_rpow_iff (norm_nonneg' (f + g)) hC₁ hp'',
      refine has_sum_le _ (lp.has_sum_norm hp'' (f + g)) hCfg,
      intros i,
      exact real.rpow_le_rpow (norm_nonneg _) (norm_add_le _ _) hp''.le },
  end,
  norm_neg := norm_neg }

-- TODO: define an `ennreal` version of `is_conjugate_exponent`, and then express this inequality
-- in a better version which also covers the case `p = 1, q = ∞`.
/-- Hölder inequality -/
protected

protected lemma summable_mul {p q : ℝ≥0∞}
  (hpq : p.to_real.is_conjugate_exponent q.to_real) (f : lp E p) (g : lp E q) :
  summable (λ i, ∥f i∥ * ∥g i∥) :=
(lp.tsum_mul_le_mul_norm hpq f g).1

protected lemma tsum_mul_le_mul_norm' {p q : ℝ≥0∞}
  (hpq : p.to_real.is_conjugate_exponent q.to_real) (f : lp E p) (g : lp E q) :
  ∑' i, ∥f i∥ * ∥g i∥ ≤ ∥f∥ * ∥g∥ :=
(lp.tsum_mul_le_mul_norm hpq f g).2

section compare_pointwise

lemma norm_apply_le_norm (hp : p ≠ 0) (f : lp E p) (i : α) : ∥f i∥ ≤ ∥f∥ :=
begin
  rcases eq_or_ne p ∞ with rfl | hp',
  { haveI : nonempty α := ⟨i⟩,
    exact (is_lub_norm f).1 ⟨i, rfl⟩ },
  have hp'' : 0 < p.to_real := ennreal.to_real_pos hp hp',
  have : ∀ i, 0 ≤ ∥f i∥ ^ p.to_real,
  { exact λ i, real.rpow_nonneg_of_nonneg (norm_nonneg _) _ },
  rw ← real.rpow_le_rpow_iff (norm_nonneg _) (norm_nonneg' _) hp'',
  convert le_has_sum (has_sum_norm hp'' f) i (λ i hi, this i),
end

lemma sum_rpow_le_norm_rpow (hp : 0 < p.to_real) (f : lp E p) (s : finset α) :
  ∑ i in s, ∥f i∥ ^ p.to_real ≤ ∥f∥ ^ p.to_real :=
begin
  rw lp.norm_rpow_eq_tsum hp f,
  have : ∀ i, 0 ≤ ∥f i∥ ^ p.to_real,
  { exact λ i, real.rpow_nonneg_of_nonneg (norm_nonneg _) _ },
  refine sum_le_tsum _ (λ i hi, this i) _,
  exact (lp.mem_ℓp f).summable hp
end

lemma norm_le_of_forall_le' [nonempty α] {f : lp E ∞} (C : ℝ) (hCf : ∀ i, ∥f i∥ ≤ C) : ∥f∥ ≤ C :=
begin
  refine (is_lub_norm f).2 _,
  rintros - ⟨i, rfl⟩,
  exact hCf i,
end

lemma norm_le_of_forall_le {f : lp E ∞} {C : ℝ} (hC : 0 ≤ C) (hCf : ∀ i, ∥f i∥ ≤ C) : ∥f∥ ≤ C :=
begin
  casesI is_empty_or_nonempty α,
  { simpa [eq_zero' f] using hC, },
  { exact norm_le_of_forall_le' C hCf },
end

lemma norm_le_of_tsum_le (hp : 0 < p.to_real) {C : ℝ} (hC : 0 ≤ C) {f : lp E p}
  (hf : ∑' i, ∥f i∥ ^ p.to_real ≤ C ^ p.to_real) :
  ∥f∥ ≤ C :=
begin
  rw [← real.rpow_le_rpow_iff (norm_nonneg' _) hC hp, norm_rpow_eq_tsum hp],
  exact hf,
end

lemma norm_le_of_forall_sum_le (hp : 0 < p.to_real) {C : ℝ} (hC : 0 ≤ C) {f : lp E p}
  (hf : ∀ s : finset α, ∑ i in s, ∥f i∥ ^ p.to_real ≤ C ^ p.to_real) :
  ∥f∥ ≤ C :=
norm_le_of_tsum_le hp hC (tsum_le_of_sum_le ((lp.mem_ℓp f).summable hp) hf)

end compare_pointwise

section normed_space

variables {𝕜 : Type*} [normed_field 𝕜] [Π i, normed_space 𝕜 (E i)]

instance : module 𝕜 (pre_lp E) := pi.module α E 𝕜

lemma mem_lp_const_smul (c : 𝕜) (f : lp E p) : c • (f : pre_lp E) ∈ lp E p :=
(lp.mem_ℓp f).const_smul c

variables (E p 𝕜)

/-- The `𝕜`-submodule of elements of `Π i : α, E i` whose `lp` norm is finite.  This is `lp E p`,
with extra structure. -/
def lp_submodule : submodule 𝕜 (pre_lp E) :=
{ smul_mem' := λ c f hf, by simpa using mem_lp_const_smul c ⟨f, hf⟩,
  .. lp E p }

variables {E p 𝕜}

lemma coe_lp_submodule : (lp_submodule E p 𝕜).to_add_subgroup = lp E p := rfl

instance : module 𝕜 (lp E p) :=
{ .. (lp_submodule E p 𝕜).module }

@[simp] lemma coe_fn_smul (c : 𝕜) (f : lp E p) : ⇑(c • f) = c • f := rfl

lemma norm_const_smul (hp : p ≠ 0) {c : 𝕜} (f : lp E p) : ∥c • f∥ = ∥c∥ * ∥f∥ :=
begin
  rcases p.trichotomy with rfl | rfl | hp,
  { exact absurd rfl hp },
  { cases is_empty_or_nonempty α; resetI,
    { simp [lp.eq_zero' f], },
    apply (lp.is_lub_norm (c • f)).unique,
    convert (lp.is_lub_norm f).mul_left (norm_nonneg c),
    ext a,
    simp [coe_fn_smul, norm_smul] },
  { suffices : ∥c • f∥ ^ p.to_real = (∥c∥ * ∥f∥) ^ p.to_real,
    { refine real.rpow_left_inj_on hp.ne' _ _ this,
      { exact norm_nonneg' _ },
      { exact mul_nonneg (norm_nonneg _) (norm_nonneg' _) } },
    apply (lp.has_sum_norm hp (c • f)).unique,
    convert (lp.has_sum_norm hp f).mul_left (∥c∥ ^ p.to_real),
    { simp [coe_fn_smul, norm_smul, real.mul_rpow (norm_nonneg c) (norm_nonneg _)] },
    have hf : 0 ≤ ∥f∥ := lp.norm_nonneg' f,
    simp [coe_fn_smul, norm_smul, real.mul_rpow (norm_nonneg c) hf] }
end

instance [fact (1 ≤ p)] : normed_space 𝕜 (lp E p) :=
{ norm_smul_le := λ c f, begin
    have hp : 0 < p := ennreal.zero_lt_one.trans_le (fact.out _),
    simp [norm_const_smul hp.ne']
  end }

variables {𝕜' : Type*} [normed_field 𝕜']

instance [Π i, normed_space 𝕜' (E i)] [has_scalar 𝕜' 𝕜] [Π i, is_scalar_tower 𝕜' 𝕜 (E i)] :
  is_scalar_tower 𝕜' 𝕜 (lp E p) :=
begin
  refine ⟨λ r c f, _⟩,
  ext1,
  exact (lp.coe_fn_smul _ _).trans (smul_assoc _ _ _)
end

end normed_space

section single
variables {𝕜 : Type*} [normed_field 𝕜] [Π i, normed_space 𝕜 (E i)]
variables [decidable_eq α]

/-- The element of `lp E p` which is `a : E i` at the index `i`, and zero elsewhere. -/
protected def single (p) (i : α) (a : E i) : lp E p :=
⟨ λ j, if h : j = i then eq.rec a h.symm else 0,
  begin
    refine (mem_ℓp_zero _).of_exponent_ge (zero_le p),
    refine (set.finite_singleton i).subset _,
    intros j,
    simp only [forall_exists_index, set.mem_singleton_iff, ne.def, dite_eq_right_iff,
      set.mem_set_of_eq, not_forall],
    rintros rfl,
    simp,
  end ⟩

protected lemma single_apply (p) (i : α) (a : E i) (j : α) :
  lp.single p i a j = if h : j = i then eq.rec a h.symm else 0 :=
rfl

protected lemma single_apply_self (p) (i : α) (a : E i) :
  lp.single p i a i = a :=
by rw [lp.single_apply, dif_pos rfl]

protected lemma single_apply_ne (p) (i : α) (a : E i) {j : α} (hij : j ≠ i) :
  lp.single p i a j = 0 :=
by rw [lp.single_apply, dif_neg hij]

@[simp] protected lemma single_neg (p) (i : α) (a : E i) :
  lp.single p i (- a) = - lp.single p i a :=
begin
  ext j,
  by_cases hi : j = i,
  { subst hi,
    simp [lp.single_apply_self] },
  { simp [lp.single_apply_ne p i _ hi] }
end

@[simp] protected lemma single_smul (p) (i : α) (a : E i) (c : 𝕜) :
  lp.single p i (c • a) = c • lp.single p i a :=
begin
  ext j,
  by_cases hi : j = i,
  { subst hi,
    simp [lp.single_apply_self] },
  { simp [lp.single_apply_ne p i _ hi] }
end

protected lemma norm_sum_single (hp : 0 < p.to_real) (f : Π i, E i) (s : finset α) :
  ∥∑ i in s, lp.single p i (f i)∥ ^ p.to_real = ∑ i in s, ∥f i∥ ^ p.to_real :=
begin
  refine (has_sum_norm hp (∑ i in s, lp.single p i (f i))).unique _,
  simp only [lp.single_apply, coe_fn_sum, finset.sum_apply, finset.sum_dite_eq],
  have h : ∀ i ∉ s, ∥ite (i ∈ s) (f i) 0∥ ^ p.to_real = 0,
  { intros i hi,
    simp [if_neg hi, real.zero_rpow hp.ne'], },
  have h' : ∀ i ∈ s, ∥f i∥ ^ p.to_real = ∥ite (i ∈ s) (f i) 0∥ ^ p.to_real,
  { intros i hi,
    rw if_pos hi },
  simpa [finset.sum_congr rfl h'] using has_sum_sum_of_ne_finset_zero h,
end

protected lemma norm_single (hp : 0 < p.to_real) (f : Π i, E i) (i : α) :
  ∥lp.single p i (f i)∥ = ∥f i∥ :=
begin
  refine real.rpow_left_inj_on hp.ne' (norm_nonneg' _) (norm_nonneg _) _,
  simpa using lp.norm_sum_single hp f {i},
end

protected lemma norm_sub_norm_compl_sub_single (hp : 0 < p.to_real) (f : lp E p) (s : finset α) :
  ∥f∥ ^ p.to_real - ∥f - ∑ i in s, lp.single p i (f i)∥ ^ p.to_real = ∑ i in s, ∥f i∥ ^ p.to_real :=
begin
  refine ((has_sum_norm hp f).sub (has_sum_norm hp (f - ∑ i in s, lp.single p i (f i)))).unique _,
  let F : α → ℝ := λ i, ∥f i∥ ^ p.to_real - ∥(f - ∑ i in s, lp.single p i (f i)) i∥ ^ p.to_real,
  have hF : ∀ i ∉ s, F i = 0,
  { intros i hi,
    suffices : ∥f i∥ ^ p.to_real - ∥f i - ite (i ∈ s) (f i) 0∥ ^ p.to_real = 0,
    { simpa [F, coe_fn_sum, lp.single_apply] using this, },
    simp [if_neg hi] },
  have hF' : ∀ i ∈ s, F i = ∥f i∥ ^ p.to_real,
  { intros i hi,
    simp [F, coe_fn_sum, lp.single_apply, if_pos hi, real.zero_rpow hp.ne'] },
  have : has_sum F (∑ i in s, F i) := has_sum_sum_of_ne_finset_zero hF,
  rwa [finset.sum_congr rfl hF'] at this,
end

protected lemma norm_compl_sum_single (hp : 0 < p.to_real) (f : lp E p) (s : finset α) :
  ∥f - ∑ i in s, lp.single p i (f i)∥ ^ p.to_real = ∥f∥ ^ p.to_real - ∑ i in s, ∥f i∥ ^ p.to_real :=
by linarith [lp.norm_sub_norm_compl_sub_single hp f s]

/-- The canonical finitely-supported approximations to an element `f` of `lp` converge to it, in the
`lp` topology. -/
protected lemma has_sum_single [fact (1 ≤ p)] (hp : p ≠ ⊤) (f : lp E p) :
  has_sum (λ i : α, lp.single p i (f i : E i)) f :=
begin
  have hp₀ : 0 < p := ennreal.zero_lt_one.trans_le (fact.out _),
  have hp' : 0 < p.to_real := ennreal.to_real_pos hp₀.ne' hp,
  have := lp.has_sum_norm hp' f,
  dsimp [has_sum] at this ⊢,
  rw metric.tendsto_nhds at this ⊢,
  intros ε hε,
  refine (this _ (real.rpow_pos_of_pos hε p.to_real)).mono _,
  intros s hs,
  rw ← real.rpow_lt_rpow_iff dist_nonneg (le_of_lt hε) hp',
  rw dist_comm at hs,
  simp only [dist_eq_norm, real.norm_eq_abs] at hs ⊢,
  have H : ∥∑ i in s, lp.single p i (f i : E i) - f∥ ^ p.to_real
    = ∥f∥ ^ p.to_real - ∑ i in s, ∥f i∥ ^ p.to_real,
  { simpa using lp.norm_compl_sum_single hp' (-f) s },
  rw ← H at hs,
  have : |∥∑ i in s, lp.single p i (f i : E i) - f∥ ^ p.to_real|
    = ∥∑ i in s, lp.single p i (f i : E i) - f∥ ^ p.to_real,
  { simp [real.abs_rpow_of_nonneg (norm_nonneg _)] },
  linarith
end

end single

section topology

open filter
open_locale topological_space uniformity

/-- The coercion from `lp E p` to `Π i, E i` is uniformly continuous. -/
lemma uniform_continuous_coe [_i : fact (1 ≤ p)] : uniform_continuous (coe : lp E p → Π i, E i) :=
begin
  have hp : p ≠ 0 := (ennreal.zero_lt_one.trans_le _i.elim).ne',
  rw uniform_continuous_pi,
  intros i,
  rw normed_group.uniformity_basis_dist.uniform_continuous_iff normed_group.uniformity_basis_dist,
  intros ε hε,
  refine ⟨ε, hε, _⟩,
  rintros f g (hfg : ∥f - g∥ < ε),
  have : ∥f i - g i∥ ≤ ∥f - g∥ := norm_apply_le_norm hp (f - g) i,
  exact this.trans_lt hfg,
end

variables {ι : Type*} {l : filter ι} [filter.ne_bot l]

lemma norm_apply_le_of_tendsto {C : ℝ} {F : ι → lp E ∞} (hCF : ∀ᶠ k in l, ∥F k∥ ≤ C)
  {f : Π a, E a} (hf : tendsto (id (λ i, F i) : ι → Π a, E a) l (𝓝 f)) (a : α) :
  ∥f a∥ ≤ C :=
begin
  have : tendsto (λ k, ∥F k a∥) l (𝓝 ∥f a∥) :=
    (tendsto.comp (continuous_apply a).continuous_at hf).norm,
  refine le_of_tendsto this (hCF.mono _),
  intros k hCFk,
  exact (norm_apply_le_norm ennreal.top_ne_zero (F k) a).trans hCFk,
end

variables [_i : fact (1 ≤ p)]

include _i

lemma sum_rpow_le_of_tendsto (hp : p ≠ ∞) {C : ℝ} {F : ι → lp E p} (hCF : ∀ᶠ k in l, ∥F k∥ ≤ C)
  {f : Π a, E a} (hf : tendsto (id (λ i, F i) : ι → Π a, E a) l (𝓝 f)) (s : finset α) :
  ∑ (i : α) in s, ∥f i∥ ^ p.to_real ≤ C ^ p.to_real :=
begin
  have hp' : p ≠ 0 := (ennreal.zero_lt_one.trans_le _i.elim).ne',
  have hp'' : 0 < p.to_real := ennreal.to_real_pos hp' hp,
  let G : (Π a, E a) → ℝ := λ f, ∑ a in s, ∥f a∥ ^ p.to_real,
  have hG : continuous G,
  { refine continuous_finset_sum s _,
    intros a ha,
    have : continuous (λ f : Π a, E a, f a):= continuous_apply a,
    exact this.norm.rpow_const (λ _, or.inr hp''.le) },
  refine le_of_tendsto (hG.continuous_at.tendsto.comp hf) _,
  refine hCF.mono _,
  intros k hCFk,
  refine (lp.sum_rpow_le_norm_rpow hp'' (F k) s).trans _,
  exact real.rpow_le_rpow (norm_nonneg _) hCFk hp''.le,
end

/-- "Semicontinuity of the `lp` norm": If all sufficiently large elements of a sequence in `lp E p`
 have `lp` norm `≤ C`, then the pointwise limit, if it exists, also has `lp` norm `≤ C`. -/
lemma norm_le_of_tendsto {C : ℝ} {F : ι → lp E p} (hCF : ∀ᶠ k in l, ∥F k∥ ≤ C) {f : lp E p}
  (hf : tendsto (id (λ i, F i) : ι → Π a, E a) l (𝓝 f)) :
  ∥f∥ ≤ C :=
begin
  obtain ⟨i, hi⟩ := hCF.exists,
  have hC : 0 ≤ C := (norm_nonneg _).trans hi,
  unfreezingI { rcases eq_top_or_lt_top p with rfl | hp },
  { apply norm_le_of_forall_le hC,
    exact norm_apply_le_of_tendsto hCF hf, },
  { have : 0 < p := ennreal.zero_lt_one.trans_le _i.elim,
    have hp' : 0 < p.to_real := ennreal.to_real_pos this.ne' hp.ne,
    apply norm_le_of_forall_sum_le hp' hC,
    exact sum_rpow_le_of_tendsto hp.ne hCF hf, }
end

/-- If `f` is the pointwise limit of a bounded sequence in `lp E p`, then `f` is in `lp E p`. -/
lemma mem_ℓp_of_tendsto {F : ι → lp E p} (hF : metric.bounded (set.range F)) {f : Π a, E a}
  (hf : tendsto (id (λ i, F i) : ι → Π a, E a) l (𝓝 f)) :
  mem_ℓp f p :=
begin
  obtain ⟨C, hC, hCF'⟩ := hF.exists_pos_norm_le,
  have hCF : ∀ k, ∥F k∥ ≤ C := λ k, hCF' _ ⟨k, rfl⟩,
  unfreezingI { rcases eq_top_or_lt_top p with rfl | hp },
  { apply mem_ℓp_infty,
    use C,
    rintros _ ⟨a, rfl⟩,
    refine norm_apply_le_of_tendsto (eventually_of_forall hCF) hf a, },
  { apply mem_ℓp_gen',
    exact sum_rpow_le_of_tendsto hp.ne (eventually_of_forall hCF) hf },
end

/-- If a sequence is Cauchy in the `lp E p` topology and pointwise convergent to a element `f` of
`lp E p`, then it converges to `f` in the `lp E p` topology. -/
lemma tendsto_lp_of_tendsto_pi {F : ℕ → lp E p} (hF : cauchy_seq F) {f : lp E p}
  (hf : tendsto (id (λ i, F i) : ℕ → Π a, E a) at_top (𝓝 f)) :
  tendsto F at_top (𝓝 f) :=
begin
  rw metric.nhds_basis_closed_ball.tendsto_right_iff,
  intros ε hε,
  have hε' : {p : (lp E p) × (lp E p) | ∥p.1 - p.2∥ < ε} ∈ 𝓤 (lp E p),
  { exact normed_group.uniformity_basis_dist.mem_of_mem hε },
  refine (hF.eventually_eventually hε').mono _,
  rintros n (hn : ∀ᶠ l in at_top, ∥(λ f, F n - f) (F l)∥ < ε),
  refine norm_le_of_tendsto (hn.mono (λ k hk, hk.le)) _,
  rw tendsto_pi_nhds,
  intros a,
  exact (hf.apply a).const_sub (F n a),
end

variables [Π a, complete_space (E a)]

instance : complete_space (lp E p) :=
metric.complete_of_cauchy_seq_tendsto
begin
  intros F hF,
  -- A Cauchy sequence in `lp E p` is pointwise convergent; let `f` be the pointwise limit.
  obtain ⟨f, hf⟩ := cauchy_seq_tendsto_of_complete (uniform_continuous_coe.comp_cauchy_seq hF),
  -- Since the Cauchy sequence is bounded, its pointwise limit `f` is in `lp E p`.
  have hf' : mem_ℓp f p := mem_ℓp_of_tendsto hF.bounded_range hf,
  -- And therefore `f` is its limit in the `lp E p` topology as well as pointwise.
  exact ⟨⟨f, hf'⟩, tendsto_lp_of_tendsto_pi hF hf⟩
end

end topology

end lp