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

! This file was ported from Lean 3 source module analysis.normed_space.lp_space
! leanprover-community/mathlib commit 57ac39bd365c2f80589a700f9fbb664d3a1a30c2
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Analysis.MeanInequalities
import Mathbin.Analysis.MeanInequalitiesPow
import Mathbin.Topology.Algebra.Order.LiminfLimsup

/-!
# ℓ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_add_comm_group` structure.
  Under appropriate conditions, this is also equipped with the instances `lp.normed_space`,
  `lp.complete_space`. For `p=∞`, there is also `lp.infty_normed_ring`,
  `lp.infty_normed_algebra`, `lp.infty_star_ring` and `lp.infty_cstar_ring`.

## 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`)

-/

noncomputable section

open NNReal ENNReal BigOperators

variable {α : Type _} {E : α → Type _} {p q : ℝ≥0∞} [∀ i, NormedAddCommGroup (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 BddAbove (Set.range fun i => ‖f i‖) else Summable fun i => ‖f i‖ ^ p.toReal
#align mem_ℓp Memℓp

theorem 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]
#align mem_ℓp_zero_iff memℓp_zero_iff

theorem memℓp_zero {f : ∀ i, E i} (hf : Set.Finite { i | f i ≠ 0 }) : Memℓp f 0 :=
  memℓp_zero_iff.2 hf
#align mem_ℓp_zero memℓp_zero

theorem memℓp_infty_iff {f : ∀ i, E i} : Memℓp f ∞ ↔ BddAbove (Set.range fun i => ‖f i‖) := by
  dsimp [Memℓp] <;> rw [if_neg ENNReal.top_ne_zero, if_pos rfl]
#align mem_ℓp_infty_iff memℓp_infty_iff

theorem memℓp_infty {f : ∀ i, E i} (hf : BddAbove (Set.range fun i => ‖f i‖)) : Memℓp f ∞ :=
  memℓp_infty_iff.2 hf
#align mem_ℓp_infty memℓp_infty

theorem memℓp_gen_iff (hp : 0 < p.toReal) {f : ∀ i, E i} :
    Memℓp f p ↔ Summable fun i => ‖f i‖ ^ p.toReal :=
  by
  rw [ENNReal.toReal_pos_iff] at hp
  dsimp [Memℓp]
  rw [if_neg hp.1.ne', if_neg hp.2.Ne]
#align mem_ℓp_gen_iff memℓp_gen_iff

theorem memℓp_gen {f : ∀ i, E i} (hf : Summable fun i => ‖f i‖ ^ p.toReal) : Memℓp f p :=
  by
  rcases p.trichotomy with (rfl | rfl | hp)
  · apply memℓp_zero
    have H : Summable fun i : α => (1 : ℝ) := by simpa using hf
    exact (Set.Finite.of_summable_const (by norm_num) H).Subset (Set.subset_univ _)
  · apply memℓp_infty
    have H : Summable fun i : α => (1 : ℝ) := by simpa using hf
    simpa using ((Set.Finite.of_summable_const (by norm_num) H).image fun i => ‖f i‖).BddAbove
  exact (memℓp_gen_iff hp).2 hf
#align mem_ℓp_gen memℓp_gen

theorem memℓp_gen' {C : ℝ} {f : ∀ i, E i} (hf : ∀ s : Finset α, (∑ i in s, ‖f i‖ ^ p.toReal) ≤ C) :
    Memℓp f p := by
  apply memℓp_gen
  use ⨆ s : Finset α, ∑ i in s, ‖f i‖ ^ p.to_real
  apply hasSum_of_isLUB_of_nonneg
  · intro b
    exact Real.rpow_nonneg_of_nonneg (norm_nonneg _) _
  apply isLUB_csupᵢ
  use C
  rintro - ⟨s, rfl⟩
  exact hf s
#align mem_ℓp_gen' memℓp_gen'

theorem zero_memℓp : Memℓp (0 : ∀ i, E i) p :=
  by
  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]
#align zero_mem_ℓp zero_memℓp

theorem zero_mem_ℓp' : Memℓp (fun i : α => (0 : E i)) p :=
  zero_memℓp
#align zero_mem_ℓp' zero_mem_ℓp'

namespace Memℓp

theorem finite_dsupport {f : ∀ i, E i} (hf : Memℓp f 0) : Set.Finite { i | f i ≠ 0 } :=
  memℓp_zero_iff.1 hf
#align mem_ℓp.finite_dsupport Memℓp.finite_dsupport

theorem bddAbove {f : ∀ i, E i} (hf : Memℓp f ∞) : BddAbove (Set.range fun i => ‖f i‖) :=
  memℓp_infty_iff.1 hf
#align mem_ℓp.bdd_above Memℓp.bddAbove

theorem summable (hp : 0 < p.toReal) {f : ∀ i, E i} (hf : Memℓp f p) :
    Summable fun i => ‖f i‖ ^ p.toReal :=
  (memℓp_gen_iff hp).1 hf
#align mem_ℓp.summable Memℓp.summable

theorem neg {f : ∀ i, E i} (hf : Memℓp f p) : Memℓp (-f) p :=
  by
  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
#align mem_ℓp.neg Memℓp.neg

@[simp]
theorem neg_iff {f : ∀ i, E i} : Memℓp (-f) p ↔ Memℓp f p :=
  ⟨fun h => neg_neg f ▸ h.neg, Memℓp.neg⟩
#align mem_ℓp.neg_iff Memℓp.neg_iff

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (i «expr ∉ » hfq.finite_dsupport.to_finset) -/
theorem of_exponent_ge {p q : ℝ≥0∞} {f : ∀ i, E i} (hfq : Memℓp f q) (hpq : q ≤ p) : Memℓp f p :=
  by
  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 fun i => ‖f i‖).BddAbove
    use max 0 C
    rintro 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) (_ : i ∉ hfq.finite_dsupport.to_finset), ‖f i‖ ^ p.to_real = 0 :=
      by
      intro 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.bddAbove_range_of_cofinite
    use A ^ q.to_real⁻¹
    rintro 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 := by
        simpa using
          eventually_lt_of_tendsto_lt (by norm_num : (0 : ℝ) < 1) hf'.tendsto_cofinite_zero
      exact H.subset fun i hi => Real.one_le_rpow hi hq.le
    · show ∀ i, ¬|‖f i‖ ^ p.to_real| ≤ ‖f i‖ ^ q.to_real → 1 ≤ ‖f i‖
      intro 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'
#align mem_ℓp.of_exponent_ge Memℓp.of_exponent_ge

theorem add {f g : ∀ i, E i} (hf : Memℓp f p) (hg : Memℓp g p) : Memℓp (f + g) p :=
  by
  rcases p.trichotomy with (rfl | rfl | hp)
  · apply memℓp_zero
    refine' (hf.finite_dsupport.union hg.finite_dsupport).Subset fun i => _
    simp only [Pi.add_apply, Ne.def, Set.mem_union, Set.mem_setOf_eq]
    contrapose!
    rintro ⟨hf', hg'⟩
    simp [hf', hg']
  · apply memℓp_infty
    obtain ⟨A, hA⟩ := hf.bdd_above
    obtain ⟨B, hB⟩ := hg.bdd_above
    refine' ⟨A + B, _⟩
    rintro 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 _ (fun i => _) (((hf.summable hp).add (hg.summable hp)).mul_left C)
  · exact fun 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.le h.le)
    · let F : Fin 2 → ℝ≥0 := ![‖f i‖₊, ‖g i‖₊]
      have : ∀ i, (0 : ℝ) ≤ F i := fun 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 fun i _ =>
          (F i).coe_nonneg
#align mem_ℓp.add Memℓp.add

theorem 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
#align mem_ℓp.sub Memℓp.sub

theorem finset_sum {ι} (s : Finset ι) {f : ι → ∀ i, E i} (hf : ∀ i ∈ s, Memℓp (f i) p) :
    Memℓp (fun a => ∑ i in s, f i a) p :=
  by
  haveI : DecidableEq ι := Classical.decEq _
  revert hf
  refine' Finset.induction_on s _ _
  · simp only [zero_mem_ℓp', Finset.sum_empty, imp_true_iff]
  · intro i s his ih hf
    simp only [his, Finset.sum_insert, not_false_iff]
    exact (hf i (s.mem_insert_self i)).add (ih fun j hj => hf j (Finset.mem_insert_of_mem hj))
#align mem_ℓp.finset_sum Memℓp.finset_sum

section NormedSpace

variable {𝕜 : Type _} [NormedField 𝕜] [∀ i, NormedSpace 𝕜 (E i)]

theorem const_smul {f : ∀ i, E i} (hf : Memℓp f p) (c : 𝕜) : Memℓp (c • f) p :=
  by
  rcases p.trichotomy with (rfl | rfl | hp)
  · apply memℓp_zero
    refine' hf.finite_dsupport.subset fun i => (_ : ¬c • f i = 0 → ¬f i = 0)
    exact not_imp_not.mpr fun hf' => hf'.symm ▸ smul_zero c
  · obtain ⟨A, hA⟩ := hf.bdd_above
    refine' memℓp_infty ⟨‖c‖ * A, _⟩
    rintro 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))]
#align mem_ℓp.const_smul Memℓp.const_smul

theorem const_mul {f : α → 𝕜} (hf : Memℓp f p) (c : 𝕜) : Memℓp (fun x => c * f x) p :=
  @Memℓp.const_smul α (fun i => 𝕜) _ _ 𝕜 _ _ _ hf c
#align mem_ℓp.const_mul Memℓp.const_mul

end NormedSpace

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). -/
@[nolint unused_arguments]
def PreLp (E : α → Type _) [∀ i, NormedAddCommGroup (E i)] : Type _ :=
  ∀ i, E i deriving AddCommGroup
#align pre_lp PreLp

instance PreLp.unique [IsEmpty α] : Unique (PreLp E) :=
  Pi.uniqueOfIsEmpty E
#align pre_lp.unique PreLp.unique

/-- lp space -/
def lp (E : α → Type _) [∀ i, NormedAddCommGroup (E i)] (p : ℝ≥0∞) : AddSubgroup (PreLp E)
    where
  carrier := { f | Memℓp f p }
  zero_mem' := zero_memℓp
  add_mem' f g := Memℓp.add
  neg_mem' f := Memℓp.neg
#align lp lp

namespace lp

instance : Coe (lp E p) (∀ i, E i) :=
  coeSubtype

instance : CoeFun (lp E p) fun _ => ∀ i, E i :=
  ⟨fun f => ((f : ∀ i, E i) : ∀ i, E i)⟩

@[ext]
theorem ext {f g : lp E p} (h : (f : ∀ i, E i) = g) : f = g :=
  Subtype.ext h
#align lp.ext lp.ext

protected theorem ext_iff {f g : lp E p} : f = g ↔ (f : ∀ i, E i) = g :=
  Subtype.ext_iff
#align lp.ext_iff lp.ext_iff

theorem eq_zero' [IsEmpty α] (f : lp E p) : f = 0 :=
  Subsingleton.elim f 0
#align lp.eq_zero' lp.eq_zero'

protected theorem monotone {p q : ℝ≥0∞} (hpq : q ≤ p) : lp E q ≤ lp E p := fun f hf =>
  Memℓp.of_exponent_ge hf hpq
#align lp.monotone lp.monotone

protected theorem memℓp (f : lp E p) : Memℓp f p :=
  f.Prop
#align lp.mem_ℓp lp.memℓp

variable (E p)

@[simp]
theorem coeFn_zero : ⇑(0 : lp E p) = 0 :=
  rfl
#align lp.coe_fn_zero lp.coeFn_zero

variable {E p}

@[simp]
theorem coeFn_neg (f : lp E p) : ⇑(-f) = -f :=
  rfl
#align lp.coe_fn_neg lp.coeFn_neg

@[simp]
theorem coeFn_add (f g : lp E p) : ⇑(f + g) = f + g :=
  rfl
#align lp.coe_fn_add lp.coeFn_add

@[simp]
theorem coeFn_sum {ι : Type _} (f : ι → lp E p) (s : Finset ι) :
    ⇑(∑ i in s, f i) = ∑ i in s, ⇑(f i) := by
  classical
    refine' Finset.induction _ _ s
    · simp
    intro i s his
    simp [Finset.sum_insert his]
#align lp.coe_fn_sum lp.coeFn_sum

@[simp]
theorem coeFn_sub (f g : lp E p) : ⇑(f - g) = f - g :=
  rfl
#align lp.coe_fn_sub lp.coeFn_sub

instance : Norm (lp E p)
    where norm f :=
    if hp : p = 0 then by subst hp <;> exact (lp.memℓp f).finite_dsupport.toFinset.card
    else if p = ∞ then ⨆ i, ‖f i‖ else (∑' i, ‖f i‖ ^ p.toReal) ^ (1 / p.toReal)

theorem norm_eq_card_dsupport (f : lp E 0) : ‖f‖ = (lp.memℓp f).finite_dsupport.toFinset.card :=
  dif_pos rfl
#align lp.norm_eq_card_dsupport lp.norm_eq_card_dsupport

theorem norm_eq_csupr (f : lp E ∞) : ‖f‖ = ⨆ i, ‖f i‖ :=
  by
  dsimp [norm]
  rw [dif_neg ENNReal.top_ne_zero, if_pos rfl]
#align lp.norm_eq_csupr lp.norm_eq_csupr

theorem isLUB_norm [Nonempty α] (f : lp E ∞) : IsLUB (Set.range fun i => ‖f i‖) ‖f‖ :=
  by
  rw [lp.norm_eq_csupr]
  exact isLUB_csupᵢ (lp.memℓp f)
#align lp.is_lub_norm lp.isLUB_norm

theorem norm_eq_tsum_rpow (hp : 0 < p.toReal) (f : lp E p) :
    ‖f‖ = (∑' i, ‖f i‖ ^ p.toReal) ^ (1 / p.toReal) :=
  by
  dsimp [norm]
  rw [ENNReal.toReal_pos_iff] at hp
  rw [dif_neg hp.1.ne', if_neg hp.2.Ne]
#align lp.norm_eq_tsum_rpow lp.norm_eq_tsum_rpow

theorem norm_rpow_eq_tsum (hp : 0 < p.toReal) (f : lp E p) :
    ‖f‖ ^ p.toReal = ∑' i, ‖f i‖ ^ p.toReal :=
  by
  rw [norm_eq_tsum_rpow hp, ← Real.rpow_mul]
  · field_simp [hp.ne']
  apply tsum_nonneg
  intro 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

#align lp.norm_rpow_eq_tsum lp.norm_rpow_eq_tsum

theorem hasSum_norm (hp : 0 < p.toReal) (f : lp E p) :
    HasSum (fun i => ‖f i‖ ^ p.toReal) (‖f‖ ^ p.toReal) :=
  by
  rw [norm_rpow_eq_tsum hp]
  exact ((lp.memℓp f).Summable hp).HasSum
#align lp.has_sum_norm lp.hasSum_norm

theorem norm_nonneg' (f : lp E p) : 0 ≤ ‖f‖ :=
  by
  rcases p.trichotomy with (rfl | rfl | hp)
  · simp [lp.norm_eq_card_dsupport f]
  · cases' isEmpty_or_nonempty α with _i _i <;> skip
    · rw [lp.norm_eq_csupr]
      simp [Real.csupᵢ_empty]
    inhabit α
    exact (norm_nonneg (f default)).trans ((lp.isLUB_norm f).1 ⟨default, rfl⟩)
  · rw [lp.norm_eq_tsum_rpow hp f]
    refine' Real.rpow_nonneg_of_nonneg (tsum_nonneg _) _
    exact fun i => Real.rpow_nonneg_of_nonneg (norm_nonneg _) _
#align lp.norm_nonneg' lp.norm_nonneg'

@[simp]
theorem norm_zero : ‖(0 : lp E p)‖ = 0 :=
  by
  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'
#align lp.norm_zero lp.norm_zero

theorem norm_eq_zero_iff {f : lp E p} : ‖f‖ = 0 ↔ f = 0 := by
  classical
    refine'
      ⟨fun h => _, by
        rintro 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' isEmpty_or_nonempty α with _i _i <;> skip
      · simp
      have H : IsLUB (Set.range fun i => ‖f i‖) 0 := by simpa [h] using lp.isLUB_norm f
      ext i
      have : ‖f i‖ = 0 := le_antisymm (H.1 ⟨i, rfl⟩) (norm_nonneg _)
      simpa using this
    · have hf : HasSum (fun i : α => ‖f i‖ ^ p.to_real) 0 :=
        by
        have := lp.hasSum_norm hp f
        rwa [h, Real.zero_rpow hp.ne'] at this
      have : ∀ i, 0 ≤ ‖f i‖ ^ p.to_real := fun i => Real.rpow_nonneg_of_nonneg (norm_nonneg _) _
      rw [hasSum_zero_iff_of_nonneg this] at hf
      ext i
      have : f i = 0 ∧ p.to_real ≠ 0 := by
        simpa [Real.rpow_eq_zero_iff_of_nonneg (norm_nonneg (f i))] using congr_fun hf i
      exact this.1
#align lp.norm_eq_zero_iff lp.norm_eq_zero_iff

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

@[simp]
theorem norm_neg ⦃f : lp E p⦄ : ‖-f‖ = ‖f‖ :=
  by
  rcases p.trichotomy with (rfl | rfl | hp)
  · simp [lp.norm_eq_card_dsupport]
  · cases isEmpty_or_nonempty α <;> skip
    · simp [lp.eq_zero' f]
    apply (lp.isLUB_norm (-f)).unique
    simpa using lp.isLUB_norm f
  · suffices ‖-f‖ ^ p.to_real = ‖f‖ ^ p.to_real by
      exact Real.rpow_left_injOn hp.ne' (norm_nonneg' _) (norm_nonneg' _) this
    apply (lp.hasSum_norm hp (-f)).unique
    simpa using lp.hasSum_norm hp f
#align lp.norm_neg lp.norm_neg

instance [hp : Fact (1 ≤ p)] : NormedAddCommGroup (lp E p) :=
  AddGroupNorm.toNormedAddCommGroup
    { toFun := norm
      map_zero' := norm_zero
      neg' := norm_neg
      add_le' := fun f g => by
        rcases p.dichotomy with (rfl | hp')
        · cases isEmpty_or_nonempty α
          · simp [lp.eq_zero' f]
          refine' (lp.isLUB_norm (f + g)).2 _
          rintro x ⟨i, rfl⟩
          refine'
            le_trans _
              (add_mem_upperBounds_add (lp.isLUB_norm f).1 (lp.isLUB_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‖ := fun i => norm_nonneg _
          have hg₁ : ∀ i, 0 ≤ ‖g i‖ := fun i => norm_nonneg _
          have hf₂ := lp.hasSum_norm hp'' f
          have hg₂ := lp.hasSum_norm hp'' g
          -- apply Minkowski's inequality
          obtain ⟨C, hC₁, hC₂, hCfg⟩ :=
            Real.Lp_add_le_hasSum_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' hasSum_le _ (lp.hasSum_norm hp'' (f + g)) hCfg
          intro i
          exact Real.rpow_le_rpow (norm_nonneg _) (norm_add_le _ _) hp''.le
      eq_zero_of_map_eq_zero' := fun f => norm_eq_zero_iff.1 }

-- 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 theorem tsum_mul_le_mul_norm {p q : ℝ≥0∞} (hpq : p.toReal.IsConjugateExponent q.toReal)
    (f : lp E p) (g : lp E q) :
    (Summable fun i => ‖f i‖ * ‖g i‖) ∧ (∑' i, ‖f i‖ * ‖g i‖) ≤ ‖f‖ * ‖g‖ :=
  by
  have hf₁ : ∀ i, 0 ≤ ‖f i‖ := fun i => norm_nonneg _
  have hg₁ : ∀ i, 0 ≤ ‖g i‖ := fun i => norm_nonneg _
  have hf₂ := lp.hasSum_norm hpq.pos f
  have hg₂ := lp.hasSum_norm hpq.symm.pos g
  obtain ⟨C, -, hC', hC⟩ :=
    Real.inner_le_Lp_mul_Lq_hasSum_of_nonneg hpq (norm_nonneg' _) (norm_nonneg' _) hf₁ hg₁ hf₂ hg₂
  rw [← hC.tsum_eq] at hC'
  exact ⟨hC.summable, hC'⟩
#align lp.tsum_mul_le_mul_norm lp.tsum_mul_le_mul_norm

protected theorem summable_mul {p q : ℝ≥0∞} (hpq : p.toReal.IsConjugateExponent q.toReal)
    (f : lp E p) (g : lp E q) : Summable fun i => ‖f i‖ * ‖g i‖ :=
  (lp.tsum_mul_le_mul_norm hpq f g).1
#align lp.summable_mul lp.summable_mul

protected theorem tsum_mul_le_mul_norm' {p q : ℝ≥0∞} (hpq : p.toReal.IsConjugateExponent q.toReal)
    (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
#align lp.tsum_mul_le_mul_norm' lp.tsum_mul_le_mul_norm'

section ComparePointwise

theorem norm_apply_le_norm (hp : p ≠ 0) (f : lp E p) (i : α) : ‖f i‖ ≤ ‖f‖ :=
  by
  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.toReal_pos hp hp'
  have : ∀ i, 0 ≤ ‖f i‖ ^ p.to_real := fun i => Real.rpow_nonneg_of_nonneg (norm_nonneg _) _
  rw [← Real.rpow_le_rpow_iff (norm_nonneg _) (norm_nonneg' _) hp'']
  convert le_hasSum (has_sum_norm hp'' f) i fun i hi => this i
#align lp.norm_apply_le_norm lp.norm_apply_le_norm

theorem sum_rpow_le_norm_rpow (hp : 0 < p.toReal) (f : lp E p) (s : Finset α) :
    (∑ i in s, ‖f i‖ ^ p.toReal) ≤ ‖f‖ ^ p.toReal :=
  by
  rw [lp.norm_rpow_eq_tsum hp f]
  have : ∀ i, 0 ≤ ‖f i‖ ^ p.to_real := fun i => Real.rpow_nonneg_of_nonneg (norm_nonneg _) _
  refine' sum_le_tsum _ (fun i hi => this i) _
  exact (lp.memℓp f).Summable hp
#align lp.sum_rpow_le_norm_rpow lp.sum_rpow_le_norm_rpow

theorem norm_le_of_forall_le' [Nonempty α] {f : lp E ∞} (C : ℝ) (hCf : ∀ i, ‖f i‖ ≤ C) : ‖f‖ ≤ C :=
  by
  refine' (is_lub_norm f).2 _
  rintro - ⟨i, rfl⟩
  exact hCf i
#align lp.norm_le_of_forall_le' lp.norm_le_of_forall_le'

theorem norm_le_of_forall_le {f : lp E ∞} {C : ℝ} (hC : 0 ≤ C) (hCf : ∀ i, ‖f i‖ ≤ C) : ‖f‖ ≤ C :=
  by
  cases isEmpty_or_nonempty α
  · simpa [eq_zero' f] using hC
  · exact norm_le_of_forall_le' C hCf
#align lp.norm_le_of_forall_le lp.norm_le_of_forall_le

theorem norm_le_of_tsum_le (hp : 0 < p.toReal) {C : ℝ} (hC : 0 ≤ C) {f : lp E p}
    (hf : (∑' i, ‖f i‖ ^ p.toReal) ≤ C ^ p.toReal) : ‖f‖ ≤ C :=
  by
  rw [← Real.rpow_le_rpow_iff (norm_nonneg' _) hC hp, norm_rpow_eq_tsum hp]
  exact hf
#align lp.norm_le_of_tsum_le lp.norm_le_of_tsum_le

theorem norm_le_of_forall_sum_le (hp : 0 < p.toReal) {C : ℝ} (hC : 0 ≤ C) {f : lp E p}
    (hf : ∀ s : Finset α, (∑ i in s, ‖f i‖ ^ p.toReal) ≤ C ^ p.toReal) : ‖f‖ ≤ C :=
  norm_le_of_tsum_le hp hC (tsum_le_of_sum_le ((lp.memℓp f).Summable hp) hf)
#align lp.norm_le_of_forall_sum_le lp.norm_le_of_forall_sum_le

end ComparePointwise

section NormedSpace

variable {𝕜 : Type _} [NormedField 𝕜] [∀ i, NormedSpace 𝕜 (E i)]

instance : Module 𝕜 (PreLp E) :=
  Pi.module α E 𝕜

theorem mem_lp_const_smul (c : 𝕜) (f : lp E p) : c • (f : PreLp E) ∈ lp E p :=
  (lp.memℓp f).const_smul c
#align lp.mem_lp_const_smul lp.mem_lp_const_smul

variable (E p 𝕜)

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

variable {E p 𝕜}

theorem coe_lpSubmodule : (lpSubmodule E p 𝕜).toAddSubgroup = lp E p :=
  rfl
#align lp.coe_lp_submodule lp.coe_lpSubmodule

instance : Module 𝕜 (lp E p) :=
  { (lpSubmodule E p 𝕜).Module with }

@[simp]
theorem coeFn_smul (c : 𝕜) (f : lp E p) : ⇑(c • f) = c • f :=
  rfl
#align lp.coe_fn_smul lp.coeFn_smul

theorem norm_const_smul (hp : p ≠ 0) {c : 𝕜} (f : lp E p) : ‖c • f‖ = ‖c‖ * ‖f‖ :=
  by
  rcases p.trichotomy with (rfl | rfl | hp)
  · exact absurd rfl hp
  · cases isEmpty_or_nonempty α <;> skip
    · simp [lp.eq_zero' f]
    apply (lp.isLUB_norm (c • f)).unique
    convert(lp.isLUB_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
      by
      refine' Real.rpow_left_injOn hp.ne' _ _ this
      · exact norm_nonneg' _
      · exact mul_nonneg (norm_nonneg _) (norm_nonneg' _)
    apply (lp.hasSum_norm hp (c • f)).unique
    convert(lp.hasSum_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]
#align lp.norm_const_smul lp.norm_const_smul

instance [Fact (1 ≤ p)] : NormedSpace 𝕜 (lp E p)
    where norm_smul_le c f :=
    by
    have hp : 0 < p := zero_lt_one.trans_le (Fact.out _)
    simp [norm_const_smul hp.ne']

variable {𝕜' : Type _} [NormedField 𝕜']

instance [∀ i, NormedSpace 𝕜' (E i)] [SMul 𝕜' 𝕜] [∀ i, IsScalarTower 𝕜' 𝕜 (E i)] :
    IsScalarTower 𝕜' 𝕜 (lp E p) := by
  refine' ⟨fun r c f => _⟩
  ext1
  exact (lp.coeFn_smul _ _).trans (smul_assoc _ _ _)

end NormedSpace

section NormedStarGroup

variable [∀ i, StarAddMonoid (E i)] [∀ i, NormedStarGroup (E i)]

theorem Memℓp.star_mem {f : ∀ i, E i} (hf : Memℓp f p) : Memℓp (star f) p :=
  by
  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
#align mem_ℓp.star_mem Memℓp.star_mem

@[simp]
theorem Memℓp.star_iff {f : ∀ i, E i} : Memℓp (star f) p ↔ Memℓp f p :=
  ⟨fun h => star_star f ▸ Memℓp.star_mem h, Memℓp.star_mem⟩
#align mem_ℓp.star_iff Memℓp.star_iff

instance : Star (lp E p) where unit f := ⟨(star f : ∀ i, E i), f.property.star_mem⟩

@[simp]
theorem coeFn_star (f : lp E p) : ⇑(star f) = star f :=
  rfl
#align lp.coe_fn_star lp.coeFn_star

@[simp]
protected theorem star_apply (f : lp E p) (i : α) : star f i = star (f i) :=
  rfl
#align lp.star_apply lp.star_apply

instance : InvolutiveStar (lp E p)
    where star_involutive x := by
    ext
    simp

instance : StarAddMonoid (lp E p) where star_add f g := ext <| star_add _ _

instance [hp : Fact (1 ≤ p)] : NormedStarGroup (lp E p)
    where norm_star f := by
    rcases p.trichotomy with (rfl | rfl | h)
    · exfalso
      have := ENNReal.toReal_mono ENNReal.zero_ne_top hp.elim
      norm_num at this
    · simp only [lp.norm_eq_csupr, lp.star_apply, norm_star]
    · simp only [lp.norm_eq_tsum_rpow h, lp.star_apply, norm_star]

variable {𝕜 : Type _} [Star 𝕜] [NormedField 𝕜]

variable [∀ i, NormedSpace 𝕜 (E i)] [∀ i, StarModule 𝕜 (E i)]

instance : StarModule 𝕜 (lp E p) where star_smul r f := ext <| star_smul _ _

end NormedStarGroup

section NonUnitalNormedRing

variable {I : Type _} {B : I → Type _} [∀ i, NonUnitalNormedRing (B i)]

theorem Memℓp.infty_mul {f g : ∀ i, B i} (hf : Memℓp f ∞) (hg : Memℓp g ∞) : Memℓp (f * g) ∞ :=
  by
  rw [memℓp_infty_iff]
  obtain ⟨⟨Cf, hCf⟩, ⟨Cg, hCg⟩⟩ := hf.bdd_above, hg.bdd_above
  refine' ⟨Cf * Cg, _⟩
  rintro _ ⟨i, rfl⟩
  calc
    ‖(f * g) i‖ ≤ ‖f i‖ * ‖g i‖ := norm_mul_le (f i) (g i)
    _ ≤ Cf * Cg :=
      mul_le_mul (hCf ⟨i, rfl⟩) (hCg ⟨i, rfl⟩) (norm_nonneg _)
        ((norm_nonneg _).trans (hCf ⟨i, rfl⟩))

#align mem_ℓp.infty_mul Memℓp.infty_mul

instance : Mul (lp B ∞) where mul f g := ⟨(f * g : ∀ i, B i), f.property.infty_mul g.property⟩

@[simp]
theorem infty_coeFn_mul (f g : lp B ∞) : ⇑(f * g) = f * g :=
  rfl
#align lp.infty_coe_fn_mul lp.infty_coeFn_mul

instance : NonUnitalRing (lp B ∞) :=
  Function.Injective.nonUnitalRing lp.hasCoeToFun.coe Subtype.coe_injective (lp.coeFn_zero B ∞)
    lp.coeFn_add infty_coeFn_mul lp.coeFn_neg lp.coeFn_sub (fun _ _ => rfl) fun _ _ => rfl

instance : NonUnitalNormedRing (lp B ∞) :=
  { lp.normedAddCommGroup with
    norm_mul := fun f g =>
      lp.norm_le_of_forall_le (mul_nonneg (norm_nonneg f) (norm_nonneg g)) fun i =>
        calc
          ‖(f * g) i‖ ≤ ‖f i‖ * ‖g i‖ := norm_mul_le _ _
          _ ≤ ‖f‖ * ‖g‖ :=
            mul_le_mul (lp.norm_apply_le_norm ENNReal.top_ne_zero f i)
              (lp.norm_apply_le_norm ENNReal.top_ne_zero g i) (norm_nonneg _) (norm_nonneg _)
           }

-- we also want a `non_unital_normed_comm_ring` instance, but this has to wait for #13719
instance infty_isScalarTower {𝕜} [NormedField 𝕜] [∀ i, NormedSpace 𝕜 (B i)]
    [∀ i, IsScalarTower 𝕜 (B i) (B i)] : IsScalarTower 𝕜 (lp B ∞) (lp B ∞) :=
  ⟨fun r f g => lp.ext <| smul_assoc r (⇑f) ⇑g⟩
#align lp.infty_is_scalar_tower lp.infty_isScalarTower

instance infty_sMulCommClass {𝕜} [NormedField 𝕜] [∀ i, NormedSpace 𝕜 (B i)]
    [∀ i, SMulCommClass 𝕜 (B i) (B i)] : SMulCommClass 𝕜 (lp B ∞) (lp B ∞) :=
  ⟨fun r f g => lp.ext <| smul_comm r (⇑f) ⇑g⟩
#align lp.infty_smul_comm_class lp.infty_sMulCommClass

section StarRing

variable [∀ i, StarRing (B i)] [∀ i, NormedStarGroup (B i)]

instance inftyStarRing : StarRing (lp B ∞) :=
  {
    show StarAddMonoid (lp B ∞)
      by
      letI : ∀ i, StarAddMonoid (B i) := fun i => inferInstance
      infer_instance with
    star_mul := fun f g => ext <| star_mul (_ : ∀ i, B i) _ }
#align lp.infty_star_ring lp.inftyStarRing

instance infty_cstarRing [∀ i, CstarRing (B i)] : CstarRing (lp B ∞)
    where norm_star_mul_self f := by
    apply le_antisymm
    · rw [← sq]
      refine' lp.norm_le_of_forall_le (sq_nonneg ‖f‖) fun i => _
      simp only [lp.star_apply, CstarRing.norm_star_mul_self, ← sq, infty_coe_fn_mul, Pi.mul_apply]
      refine' sq_le_sq' _ (lp.norm_apply_le_norm ENNReal.top_ne_zero _ _)
      linarith [norm_nonneg (f i), norm_nonneg f]
    · rw [← sq, ← Real.le_sqrt (norm_nonneg _) (norm_nonneg _)]
      refine' lp.norm_le_of_forall_le ‖star f * f‖.sqrt_nonneg fun i => _
      rw [Real.le_sqrt (norm_nonneg _) (norm_nonneg _), sq, ← CstarRing.norm_star_mul_self]
      exact lp.norm_apply_le_norm ENNReal.top_ne_zero (star f * f) i
#align lp.infty_cstar_ring lp.infty_cstarRing

end StarRing

end NonUnitalNormedRing

section NormedRing

variable {I : Type _} {B : I → Type _} [∀ i, NormedRing (B i)]

instance PreLp.ring : Ring (PreLp B) :=
  Pi.ring
#align pre_lp.ring PreLp.ring

variable [∀ i, NormOneClass (B i)]

theorem one_memℓp_infty : Memℓp (1 : ∀ i, B i) ∞ :=
  ⟨1, by
    rintro i ⟨i, rfl⟩
    exact norm_one.le⟩
#align one_mem_ℓp_infty one_memℓp_infty

variable (B)

/-- The `𝕜`-subring of elements of `Π i : α, B i` whose `lp` norm is finite. This is `lp E ∞`,
with extra structure. -/
def lpInftySubring : Subring (PreLp B) :=
  { lp B ∞ with
    carrier := { f | Memℓp f ∞ }
    one_mem' := one_memℓp_infty
    mul_mem' := fun f g hf hg => hf.infty_mul hg }
#align lp_infty_subring lpInftySubring

variable {B}

instance inftyRing : Ring (lp B ∞) :=
  (lpInftySubring B).toRing
#align lp.infty_ring lp.inftyRing

theorem Memℓp.infty_pow {f : ∀ i, B i} (hf : Memℓp f ∞) (n : ℕ) : Memℓp (f ^ n) ∞ :=
  (lpInftySubring B).pow_mem hf n
#align mem_ℓp.infty_pow Memℓp.infty_pow

theorem nat_cast_memℓp_infty (n : ℕ) : Memℓp (n : ∀ i, B i) ∞ :=
  natCast_mem (lpInftySubring B) n
#align nat_cast_mem_ℓp_infty nat_cast_memℓp_infty

theorem int_cast_memℓp_infty (z : ℤ) : Memℓp (z : ∀ i, B i) ∞ :=
  coe_int_mem (lpInftySubring B) z
#align int_cast_mem_ℓp_infty int_cast_memℓp_infty

@[simp]
theorem infty_coeFn_one : ⇑(1 : lp B ∞) = 1 :=
  rfl
#align lp.infty_coe_fn_one lp.infty_coeFn_one

@[simp]
theorem infty_coeFn_pow (f : lp B ∞) (n : ℕ) : ⇑(f ^ n) = f ^ n :=
  rfl
#align lp.infty_coe_fn_pow lp.infty_coeFn_pow

@[simp]
theorem infty_coeFn_nat_cast (n : ℕ) : ⇑(n : lp B ∞) = n :=
  rfl
#align lp.infty_coe_fn_nat_cast lp.infty_coeFn_nat_cast

@[simp]
theorem infty_coeFn_int_cast (z : ℤ) : ⇑(z : lp B ∞) = z :=
  rfl
#align lp.infty_coe_fn_int_cast lp.infty_coeFn_int_cast

instance [Nonempty I] : NormOneClass (lp B ∞)
    where norm_one := by
    simp_rw [lp.norm_eq_csupr, infty_coe_fn_one, Pi.one_apply, norm_one, csupᵢ_const]

instance inftyNormedRing : NormedRing (lp B ∞) :=
  { lp.inftyRing, lp.nonUnitalNormedRing with }
#align lp.infty_normed_ring lp.inftyNormedRing

end NormedRing

section NormedCommRing

variable {I : Type _} {B : I → Type _} [∀ i, NormedCommRing (B i)] [∀ i, NormOneClass (B i)]

instance inftyCommRing : CommRing (lp B ∞) :=
  { lp.inftyRing with
    mul_comm := fun f g => by
      ext
      simp only [lp.infty_coeFn_mul, Pi.mul_apply, mul_comm] }
#align lp.infty_comm_ring lp.inftyCommRing

instance inftyNormedCommRing : NormedCommRing (lp B ∞) :=
  { lp.inftyCommRing, lp.inftyNormedRing with }
#align lp.infty_normed_comm_ring lp.inftyNormedCommRing

end NormedCommRing

section Algebra

variable {I : Type _} {𝕜 : Type _} {B : I → Type _}

variable [NormedField 𝕜] [∀ i, NormedRing (B i)] [∀ i, NormedAlgebra 𝕜 (B i)]

/-- A variant of `pi.algebra` that lean can't find otherwise. -/
instance Pi.algebraOfNormedAlgebra : Algebra 𝕜 (∀ i, B i) :=
  @Pi.algebra I 𝕜 B _ _ fun i => NormedAlgebra.toAlgebra
#align pi.algebra_of_normed_algebra Pi.algebraOfNormedAlgebra

instance PreLp.algebra : Algebra 𝕜 (PreLp B) :=
  Pi.algebraOfNormedAlgebra
#align pre_lp.algebra PreLp.algebra

variable [∀ i, NormOneClass (B i)]

theorem algebraMap_memℓp_infty (k : 𝕜) : Memℓp (algebraMap 𝕜 (∀ i, B i) k) ∞ :=
  by
  rw [Algebra.algebraMap_eq_smul_one]
  exact (one_mem_ℓp_infty.const_smul k : Memℓp (k • 1 : ∀ i, B i) ∞)
#align algebra_map_mem_ℓp_infty algebraMap_memℓp_infty

variable (𝕜 B)

/-- The `𝕜`-subalgebra of elements of `Π i : α, B i` whose `lp` norm is finite. This is `lp E ∞`,
with extra structure. -/
def lpInftySubalgebra : Subalgebra 𝕜 (PreLp B) :=
  { lpInftySubring B with
    carrier := { f | Memℓp f ∞ }
    algebraMap_mem' := algebraMap_memℓp_infty }
#align lp_infty_subalgebra lpInftySubalgebra

variable {𝕜 B}

instance inftyNormedAlgebra : NormedAlgebra 𝕜 (lp B ∞) :=
  { (lpInftySubalgebra 𝕜 B).Algebra, (lp.normedSpace : NormedSpace 𝕜 (lp B ∞)) with }
#align lp.infty_normed_algebra lp.inftyNormedAlgebra

end Algebra

section Single

variable {𝕜 : Type _} [NormedField 𝕜] [∀ i, NormedSpace 𝕜 (E i)]

variable [DecidableEq α]

/-- 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 :=
  ⟨fun j => if h : j = i then Eq.ndrec a h.symm else 0,
    by
    refine' (memℓp_zero _).of_exponent_ge (zero_le p)
    refine' (Set.finite_singleton i).Subset _
    intro j
    simp only [forall_exists_index, Set.mem_singleton_iff, Ne.def, dite_eq_right_iff,
      Set.mem_setOf_eq, not_forall]
    rintro rfl
    simp⟩
#align lp.single lp.single

protected theorem single_apply (p) (i : α) (a : E i) (j : α) :
    lp.single p i a j = if h : j = i then Eq.ndrec a h.symm else 0 :=
  rfl
#align lp.single_apply lp.single_apply

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

protected theorem 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]
#align lp.single_apply_ne lp.single_apply_ne

@[simp]
protected theorem single_neg (p) (i : α) (a : E i) : lp.single p i (-a) = -lp.single p i a :=
  by
  ext j
  by_cases hi : j = i
  · subst hi
    simp [lp.single_apply_self]
  · simp [lp.single_apply_ne p i _ hi]
#align lp.single_neg lp.single_neg

@[simp]
protected theorem single_smul (p) (i : α) (a : E i) (c : 𝕜) :
    lp.single p i (c • a) = c • lp.single p i a :=
  by
  ext j
  by_cases hi : j = i
  · subst hi
    simp [lp.single_apply_self]
  · simp [lp.single_apply_ne p i _ hi]
#align lp.single_smul lp.single_smul

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (i «expr ∉ » s) -/
protected theorem norm_sum_single (hp : 0 < p.toReal) (f : ∀ i, E i) (s : Finset α) :
    ‖∑ i in s, lp.single p i (f i)‖ ^ p.toReal = ∑ i in s, ‖f i‖ ^ p.toReal :=
  by
  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) (_ : i ∉ s), ‖ite (i ∈ s) (f i) 0‖ ^ p.to_real = 0 :=
    by
    intro 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 :=
    by
    intro i hi
    rw [if_pos hi]
  simpa [Finset.sum_congr rfl h'] using hasSum_sum_of_ne_finset_zero h
#align lp.norm_sum_single lp.norm_sum_single

protected theorem norm_single (hp : 0 < p.toReal) (f : ∀ i, E i) (i : α) :
    ‖lp.single p i (f i)‖ = ‖f i‖ :=
  by
  refine' Real.rpow_left_injOn hp.ne' (norm_nonneg' _) (norm_nonneg _) _
  simpa using lp.norm_sum_single hp f {i}
#align lp.norm_single lp.norm_single

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (i «expr ∉ » s) -/
protected theorem norm_sub_norm_compl_sub_single (hp : 0 < p.toReal) (f : lp E p) (s : Finset α) :
    ‖f‖ ^ p.toReal - ‖f - ∑ i in s, lp.single p i (f i)‖ ^ p.toReal = ∑ i in s, ‖f i‖ ^ p.toReal :=
  by
  refine' ((has_sum_norm hp f).sub (has_sum_norm hp (f - ∑ i in s, lp.single p i (f i)))).unique _
  let F : α → ℝ := fun i => ‖f i‖ ^ p.to_real - ‖(f - ∑ i in s, lp.single p i (f i)) i‖ ^ p.to_real
  have hF : ∀ (i) (_ : i ∉ s), F i = 0 := by
    intro i hi
    suffices ‖f i‖ ^ p.to_real - ‖f i - ite (i ∈ s) (f i) 0‖ ^ p.to_real = 0 by
      simpa only [F, coe_fn_sum, lp.single_apply, coe_fn_sub, Pi.sub_apply, Finset.sum_apply,
        Finset.sum_dite_eq] using this
    simp only [if_neg hi, sub_zero, sub_self]
  have hF' : ∀ i ∈ s, F i = ‖f i‖ ^ p.to_real :=
    by
    intro i hi
    simp only [F, coe_fn_sum, lp.single_apply, if_pos hi, sub_self, eq_self_iff_true, coe_fn_sub,
      Pi.sub_apply, Finset.sum_apply, Finset.sum_dite_eq, sub_eq_self]
    simp [Real.zero_rpow hp.ne']
  have : HasSum F (∑ i in s, F i) := hasSum_sum_of_ne_finset_zero hF
  rwa [Finset.sum_congr rfl hF'] at this
#align lp.norm_sub_norm_compl_sub_single lp.norm_sub_norm_compl_sub_single

protected theorem norm_compl_sum_single (hp : 0 < p.toReal) (f : lp E p) (s : Finset α) :
    ‖f - ∑ i in s, lp.single p i (f i)‖ ^ p.toReal = ‖f‖ ^ p.toReal - ∑ i in s, ‖f i‖ ^ p.toReal :=
  by linarith [lp.norm_sub_norm_compl_sub_single hp f s]
#align lp.norm_compl_sum_single lp.norm_compl_sum_single

/-- The canonical finitely-supported approximations to an element `f` of `lp` converge to it, in the
`lp` topology. -/
protected theorem hasSum_single [Fact (1 ≤ p)] (hp : p ≠ ⊤) (f : lp E p) :
    HasSum (fun i : α => lp.single p i (f i : E i)) f :=
  by
  have hp₀ : 0 < p := zero_lt_one.trans_le (Fact.out _)
  have hp' : 0 < p.to_real := ENNReal.toReal_pos hp₀.ne' hp
  have := lp.hasSum_norm hp' f
  rw [HasSum, Metric.tendsto_nhds] at this⊢
  intro ε hε
  refine' (this _ (Real.rpow_pos_of_pos hε p.to_real)).mono _
  intro 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 :=
    by
    simpa only [coe_fn_neg, Pi.neg_apply, lp.single_neg, Finset.sum_neg_distrib, neg_sub_neg,
      norm_neg, _root_.norm_neg] 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 :=
    by simp only [Real.abs_rpow_of_nonneg (norm_nonneg _), abs_norm_eq_norm]
  linarith
#align lp.has_sum_single lp.hasSum_single

end Single

section Topology

open Filter

open Topology uniformity

/-- The coercion from `lp E p` to `Π i, E i` is uniformly continuous. -/
theorem uniformContinuous_coe [_i : Fact (1 ≤ p)] : UniformContinuous (coe : lp E p → ∀ i, E i) :=
  by
  have hp : p ≠ 0 := (zero_lt_one.trans_le _i.elim).ne'
  rw [uniformContinuous_pi]
  intro i
  rw [normed_add_comm_group.uniformity_basis_dist.uniform_continuous_iff
      NormedAddCommGroup.uniformity_basis_dist]
  intro ε hε
  refine' ⟨ε, hε, _⟩
  rintro 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
#align lp.uniform_continuous_coe lp.uniformContinuous_coe

variable {ι : Type _} {l : Filter ι} [Filter.NeBot l]

theorem norm_apply_le_of_tendsto {C : ℝ} {F : ι → lp E ∞} (hCF : ∀ᶠ k in l, ‖F k‖ ≤ C)
    {f : ∀ a, E a} (hf : Tendsto (id fun i => F i : ι → ∀ a, E a) l (𝓝 f)) (a : α) : ‖f a‖ ≤ C :=
  by
  have : tendsto (fun k => ‖F k a‖) l (𝓝 ‖f a‖) :=
    (tendsto.comp (continuous_apply a).ContinuousAt hf).norm
  refine' le_of_tendsto this (hCF.mono _)
  intro k hCFk
  exact (norm_apply_le_norm ENNReal.top_ne_zero (F k) a).trans hCFk
#align lp.norm_apply_le_of_tendsto lp.norm_apply_le_of_tendsto

variable [_i : Fact (1 ≤ p)]

include _i

theorem 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 fun i => F i : ι → ∀ a, E a) l (𝓝 f)) (s : Finset α) :
    (∑ i : α in s, ‖f i‖ ^ p.toReal) ≤ C ^ p.toReal :=
  by
  have hp' : p ≠ 0 := (zero_lt_one.trans_le _i.elim).ne'
  have hp'' : 0 < p.to_real := ENNReal.toReal_pos hp' hp
  let G : (∀ a, E a) → ℝ := fun f => ∑ a in s, ‖f a‖ ^ p.to_real
  have hG : Continuous G := by
    refine' continuous_finset_sum s _
    intro a ha
    have : Continuous fun f : ∀ a, E a => f a := continuous_apply a
    exact this.norm.rpow_const fun _ => Or.inr hp''.le
  refine' le_of_tendsto (hG.continuous_at.tendsto.comp hf) _
  refine' hCF.mono _
  intro k hCFk
  refine' (lp.sum_rpow_le_norm_rpow hp'' (F k) s).trans _
  exact Real.rpow_le_rpow (norm_nonneg _) hCFk hp''.le
#align lp.sum_rpow_le_of_tendsto lp.sum_rpow_le_of_tendsto

/-- "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`. -/
theorem norm_le_of_tendsto {C : ℝ} {F : ι → lp E p} (hCF : ∀ᶠ k in l, ‖F k‖ ≤ C) {f : lp E p}
    (hf : Tendsto (id fun i => F i : ι → ∀ a, E a) l (𝓝 f)) : ‖f‖ ≤ C :=
  by
  obtain ⟨i, hi⟩ := hCF.exists
  have hC : 0 ≤ C := (norm_nonneg _).trans hi
  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 := zero_lt_one.trans_le _i.elim
    have hp' : 0 < p.to_real := ENNReal.toReal_pos this.ne' hp.ne
    apply norm_le_of_forall_sum_le hp' hC
    exact sum_rpow_le_of_tendsto hp.ne hCF hf
#align lp.norm_le_of_tendsto lp.norm_le_of_tendsto

/-- If `f` is the pointwise limit of a bounded sequence in `lp E p`, then `f` is in `lp E p`. -/
theorem memℓp_of_tendsto {F : ι → lp E p} (hF : Metric.Bounded (Set.range F)) {f : ∀ a, E a}
    (hf : Tendsto (id fun i => F i : ι → ∀ a, E a) l (𝓝 f)) : Memℓp f p :=
  by
  obtain ⟨C, hC, hCF'⟩ := hF.exists_pos_norm_le
  have hCF : ∀ k, ‖F k‖ ≤ C := fun k => hCF' _ ⟨k, rfl⟩
  rcases eq_top_or_lt_top p with (rfl | hp)
  · apply memℓp_infty
    use C
    rintro _ ⟨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
#align lp.mem_ℓp_of_tendsto lp.memℓp_of_tendsto

/-- 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. -/
theorem tendsto_lp_of_tendsto_pi {F : ℕ → lp E p} (hF : CauchySeq F) {f : lp E p}
    (hf : Tendsto (id fun i => F i : ℕ → ∀ a, E a) atTop (𝓝 f)) : Tendsto F atTop (𝓝 f) :=
  by
  rw [metric.nhds_basis_closed_ball.tendsto_right_iff]
  intro ε hε
  have hε' : { p : lp E p × lp E p | ‖p.1 - p.2‖ < ε } ∈ 𝓤 (lp E p) :=
    normed_add_comm_group.uniformity_basis_dist.mem_of_mem hε
  refine' (hF.eventually_eventually hε').mono _
  rintro n (hn : ∀ᶠ l in at_top, ‖(fun f => F n - f) (F l)‖ < ε)
  refine' norm_le_of_tendsto (hn.mono fun k hk => hk.le) _
  rw [tendsto_pi_nhds]
  intro a
  exact (hf.apply a).const_sub (F n a)
#align lp.tendsto_lp_of_tendsto_pi lp.tendsto_lp_of_tendsto_pi

variable [∀ a, CompleteSpace (E a)]

instance : CompleteSpace (lp E p) :=
  Metric.complete_of_cauchySeq_tendsto
    (by
      intro F hF
      -- A Cauchy sequence in `lp E p` is pointwise convergent; let `f` be the pointwise limit.
      obtain ⟨f, hf⟩ := cauchySeq_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 Topology

end lp


/-
Copyright (c) 2017 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Johannes Hölzl

Linear algebra -- classical

This file is inspired by Isabelle/HOL's linear algebra, and hence indirectly by HOL Light.

We define the following concepts:

* `lc α β`: linear combinations over `β` (`α` is the scalar ring)

* `span s`: the submodule generated by `s`

* `linear_independent s`: states that `s` are linear independent

* `linear_independent.repr s b`: choose the linear combination representing `b` on the linear
  independent vectors `s`. `b` should be in `span b` (uses classical choice)

* `is_basis s`: if `s` is a basis, i.e. linear independent and spans the entire space

* `is_basis.repr s b`: like `linear_independent.repr` but as a `linear_map`

* `is_basis.constr s g`: constructs a `linear_map` by extending `g` from the basis `s`

-/

import algebra algebra.big_operators order.zorn data.finset data.finsupp
noncomputable theory

open classical set function lattice
local attribute [instance] prop_decidable

reserve infix `≃ₗ` : 50

universes u v w x y
variables {α : Type u} {β : Type v} {γ : Type w} {δ : Type y} {ι : Type x}

namespace finset

lemma smul_sum [ring γ] [module γ β] {s : finset α} {a : γ} {f : α → β} :
  a • (s.sum f) = s.sum (λc, a • f c) :=
(finset.sum_hom ((•) a) (@smul_zero γ β _ _ a) (assume _ _, smul_add)).symm

end finset

namespace finsupp

lemma smul_sum [has_zero β] [ring γ] [module γ δ] {v : α →₀ β} {c : γ} {h : α → β → δ} :
  c • (v.sum h) = v.sum (λa b, c • h a b) :=
finset.smul_sum

end finsupp

/-- The type of linear coefficients, which are simply the finitely supported functions
from the module `β` to the scalar ring `α`. -/
@[reducible] def lc (α : Type u) (β : Type v) [ring α] [module α β] : Type (max u v) := β →₀ α

namespace lc
variables [ring α] [module α β]

instance : has_scalar α (lc α β) := finsupp.to_has_scalar

instance : module α (lc α β) := finsupp.to_module α

lemma is_linear_map_sum [module α γ] [module α δ] {f : β → α → γ} {g : δ → lc α β}
  (hf : ∀b, is_linear_map (f b)) (hg : is_linear_map g) : is_linear_map (λd, (g d).sum f) :=
⟨assume d₁ d₂, by simp [hg.add, finsupp.sum_add_index, (hf _).zero, (hf _).add],
  assume a d,
    by simp [hg.smul, finsupp.sum_smul_index, (hf _).zero, finsupp.smul_sum, ((hf _).smul _ _).symm]⟩

end lc

namespace is_linear_map

@[simp] lemma finsupp_sum [ring α] [module α β] [module α γ] [has_zero δ]
  {f : β → γ} {t : ι →₀ δ} {g : ι → δ → β} (hf : is_linear_map f) :
  f (t.sum g) = t.sum (λi d, f (g i d)) :=
hf.sum

end is_linear_map

structure linear_equiv {α : Type u} [ring α] (β : Type v) (γ : Type w) [module α β] [module α γ]
  extends equiv β γ :=
(linear_fun : is_linear_map to_fun)

infix ` ≃ₗ ` := linear_equiv

namespace linear_equiv
variables [ring α] [module α β] [module α γ] [module α δ]
include α

lemma linear_inv (e : β ≃ₗ γ) : is_linear_map e.inv_fun :=
e.linear_fun.inverse e.left_inv e.right_inv

section
variable (β)
def refl : β ≃ₗ β := { linear_fun := is_linear_map.id, .. equiv.refl β }
end

def symm (e : β ≃ₗ γ) : γ ≃ₗ β := { linear_fun := e.linear_inv, .. e.to_equiv.symm }

def trans (e₁ : β ≃ₗ γ) (e₂ : γ ≃ₗ δ) : β ≃ₗ δ :=
{ linear_fun := is_linear_map.comp e₂.linear_fun e₁.linear_fun,
  .. e₁.to_equiv.trans e₂.to_equiv }

end linear_equiv

section module
variables [ring α] [module α β] [module α γ] [module α δ]
variables {a a' : α} {s t : set β} {b b' b₁ b₂ : β}
include α

/-- Linear span of a set of vectors -/
def span (s : set β) : set β := { x | ∃(v : lc α β), (∀x∉s, v x = 0) ∧ x = v.sum (λb a, a • b) }

instance is_submodule_span : is_submodule (span s) :=
{ zero_ := ⟨0,
    by simp [finsupp.sum_zero_index]⟩,
  add_  := assume x y ⟨vx, hx, eqx⟩ ⟨vy, hy, eqy⟩, ⟨vx + vy,
    by simp [hx, hy, eqx, eqy, finsupp.sum_add_index, add_smul] {contextual := tt}⟩,
  smul  := assume a b ⟨v, hv, veq⟩, ⟨a • v,
    by simp [hv, veq, finsupp.sum_smul_index, finsupp.smul_sum, smul_smul] {contextual := tt}⟩ }

lemma subset_span : s ⊆ span s :=
assume b (hb : b ∈ s),
have ∀b'∉s, b ≠ b', by intros b' hb' ne; cc,
⟨finsupp.single b 1, by simp [finsupp.sum_single_index, this] {contextual := tt}⟩

lemma span_eq_of_is_submodule (hs : is_submodule s) : span s = s :=
have span s ⊆ s,
  from assume b ⟨v, hv, eq⟩,
  have ∀c, v c • c ∈ s, from assume c, is_submodule.smul_ne_0 $ not_imp_comm.mp $ hv c,
  eq.symm ▸ is_submodule.sum (by simp [this] {contextual := tt}),
subset.antisymm this subset_span

lemma span_mono (h : t ⊆ s) : span t ⊆ span s :=
assume b ⟨v, hv, eq⟩, ⟨v, assume b, hv b ∘ mt (@h b), eq⟩

lemma span_minimal (hs : is_submodule s) (h : t ⊆ s) : span t ⊆ s :=
calc span t ⊆ span s : span_mono h
  ... = s : span_eq_of_is_submodule hs

lemma span_eq (hs : is_submodule s) (hts : t ⊆ s) (hst : s ⊆ span t) : span t = s :=
subset.antisymm (span_minimal hs hts) hst

@[simp] lemma span_empty : span (∅ : set β) = {0} :=
span_eq is_submodule.single_zero (empty_subset _) (by simp [subset_def, is_submodule.zero])

lemma is_submodule_range_smul : is_submodule $ range (λa, a • b) :=
is_submodule.range $ is_linear_map.map_smul_left is_linear_map.id

lemma span_singleton : span {b} = range (λa, a • b) :=
span_eq is_submodule_range_smul
  (assume b' hb', ⟨1, by simp * at *⟩)
  (assume b' ⟨a, eq⟩, eq ▸ is_submodule.smul _ $ subset_span $ mem_singleton _)

lemma span_union : span (s ∪ t) = {z | ∃x∈span s, ∃y∈span t, z = x + y } :=
span_eq is_submodule.add_submodule
  (union_subset
    (assume x hx, ⟨x, subset_span hx, 0, is_submodule.zero, by simp⟩)
    (assume y hy, ⟨0, is_submodule.zero, y, subset_span hy, by simp⟩))
  (assume b ⟨x, hx, y, hy, eq⟩, eq.symm ▸ is_submodule.add
    (span_mono (subset_union_left _ _) hx)
    (span_mono (subset_union_right _ _) hy))

lemma span_insert_eq_span (h : b ∈ span s) : span (insert b s) = span s :=
span_eq is_submodule_span (set.insert_subset.mpr ⟨h, subset_span⟩) (span_mono $ subset_insert _ _)

lemma span_insert : span (insert b s) = {z | ∃a, ∃x∈span s, z = a • b + x } :=
set.ext $ assume b',
begin
  split; rw [insert_eq, span_union]; simp [span_singleton, set.ext_iff, range, -add_comm],
  exact (assume y a eq_y x hx eq, ⟨a, x, hx, by simp [eq_y, eq]⟩),
  exact (assume a b₂ hb₂ eq, ⟨a • b, ⟨a, rfl⟩, b₂, hb₂, eq⟩)
end

lemma mem_span_insert : b₁ ∈ span (insert b s) ↔ ∃a, b₁ + a • b ∈ span s :=
begin
  simp [span_insert],
  constructor,
  exact assume ⟨a, b, hb, eq⟩, ⟨-a, by simp [eq, hb]⟩,
  exact assume ⟨a, hb⟩, ⟨-a, _, hb, by simp⟩
end

@[simp] lemma span_span : span (span s) = span s :=
span_eq_of_is_submodule is_submodule_span

@[simp] lemma span_image_of_linear_map {f : β → γ} (hf : is_linear_map f) :
  span (f '' s) = f '' span s :=
subset.antisymm
  (span_minimal (is_submodule.image hf) (image_subset _ subset_span))
  (image_subset_iff.mpr $
    span_minimal (is_submodule.preimage hf) (image_subset_iff.mp subset_span))

lemma linear_eq_on {f g : β → γ} (hf : is_linear_map f) (hg : is_linear_map g)
  (h : ∀x∈s, f x = g x) : ∀{x}, x ∈ span s → f x = g x
| _ ⟨l, hl, rfl⟩ :=
  begin
    simp [hf.finsupp_sum, hg.finsupp_sum],
    apply finset.sum_congr rfl,
    assume b hb,
    have : b ∈ s, { by_contradiction, simp * at * },
    simp [this, h, hf.smul, hg.smul]
  end

/-- Linearly independent set of vectors -/
def linear_independent (s : set β) : Prop :=
∀l : lc α β, (∀x∉s, l x = 0) → l.sum (λv c, c • v) = 0 → l = 0

lemma linear_independent_empty : linear_independent (∅ : set β) :=
assume l hl eq, finsupp.ext $ by simp * at *

lemma linear_independent.mono (hs : linear_independent s) (h : t ⊆ s) : linear_independent t :=
assume l hl eq, hs l (assume b, hl b ∘ mt (@h b)) eq

lemma zero_not_mem_of_linear_independent (ne : 0 ≠ (1:α)) (hs : linear_independent s) : (0:β) ∉ s :=
assume (h : 0 ∈ s),
let l : lc α β := finsupp.single 0 1 in
have l = 0,
  from hs l
    (by intro x; by_cases 0 = x; simp [l, finsupp.single_apply, *] at *)
    (by simp [finsupp.sum_single_index]),
have l 0 = 1, from finsupp.single_eq_same,
by rw [‹l = 0›] at this; simp * at *

lemma linear_independent_union {s t : set β}
  (hs : linear_independent s) (ht : linear_independent t) (hst : span s ∩ span t = {0}) :
  linear_independent (s ∪ t) :=
(zero_ne_one_or_forall_eq_0 α).elim
  (assume ne l hl eq0,
    let ls := l.filter $ λb, b ∈ s, lt := l.filter $ λb, b ∈ t in
    have hls : ↑ls.support ⊆ s, by simp [ls, subset_def],
    have hlt : ↑lt.support ⊆ t, by simp [ls, subset_def],
    have lt.sum (λb a, a • b) ∈ span t,
      from is_submodule.sum $ assume b hb, is_submodule.smul _ $ subset_span $ hlt hb,
    have l = ls + lt,
      from
      have ∀b, b ∈ s → b ∉ t,
        from assume b hbs hbt,
        have b ∈ span s ∩ span t, from ⟨subset_span hbs, subset_span hbt⟩,
        have b = 0, by rw [hst] at this; simp * at *,
        zero_not_mem_of_linear_independent ne hs $ this ▸ hbs,
      have lt = l.filter (λb, b ∉ s),
        from finsupp.ext $ assume b, by by_cases b ∈ t; by_cases b ∈ s; simp * at *,
      by rw [this]; exact finsupp.filter_pos_add_filter_neg.symm,
    have ls.sum (λb a, a • b) + lt.sum (λb a, a • b) = l.sum (λb a, a • b),
      by rw [this, finsupp.sum_add_index]; simp [add_smul],
    have ls_eq_neg_lt : ls.sum (λb a, a • b) = - lt.sum (λb a, a • b),
      from eq_of_sub_eq_zero $ by simp [this, eq0],
    have ls_sum_eq : ls.sum (λb a, a • b) = 0,
      from
      have - lt.sum (λb a, a • b) ∈ span t,
        from is_submodule.neg $ is_submodule.sum $
          assume b hb, is_submodule.smul _ $ subset_span $ hlt hb,
      have ls.sum (λb a, a • b) ∈ span s ∩ span t,
        from ⟨is_submodule.sum $ assume b hb, is_submodule.smul _ $ subset_span $ hls hb,
          ls_eq_neg_lt.symm ▸ this⟩,
      by rw [hst] at this; simp * at *,
    have ls = 0, from hs _ (finsupp.support_subset_iff.mp hls) ls_sum_eq,
    have lt_sum_eq : lt.sum (λb a, a • b) = 0,
      from eq_of_neg_eq_neg $ by rw [←ls_eq_neg_lt, ls_sum_eq]; simp,
    have lt = 0, from ht _ (finsupp.support_subset_iff.mp hlt) lt_sum_eq,
    by simp [‹l = ls + lt›, ‹ls = 0›, ‹lt = 0›])
  (assume eq_0 l _ _, finsupp.ext $ assume b, eq_0 _)

lemma linear_independent_Union_of_directed {s : set (set β)} (hs : ∀a∈s, ∀b∈s, ∃c∈s, a ∪ b ⊆ c)
  (h : ∀a∈s, linear_independent a) : linear_independent (⋃₀s) :=
assume l hl eq,
have ∀f:finset β, {x | x ∈ f} ⊆ ⋃₀ s → f = ∅ ∨ (∃t∈s, {x | x ∈ f} ⊆ t),
  from assume f, finset.induction_on f (by simp) $
    assume a f haf ih haf_s,
    let ⟨t, ht, hat⟩ := haf_s $ finset.mem_insert_self _ _ in
    have f = ∅ ∨ ∃ (t : set β) (H : t ∈ s), {x : β | x ∈ f} ⊆ t,
      from ih $ assume x hx, haf_s $ finset.mem_insert_of_mem hx,
    or.inr $ this.elim
      (assume : f = ∅, ⟨t, ht, by simp [this, hat, subset_def]⟩)
      (assume ⟨t', ht', hft⟩,
        let ⟨t'', ht''s, ht''⟩ := hs t ht t' ht' in
        have a ∈ t'',
          from ht'' $ or.inl hat,
        have ∀x, x ∈ f → x ∈ t'',
          from subset.trans (subset.trans hft $ subset_union_right _ _) ht'',
        ⟨t'', ht''s, by simp [subset_def, or_imp_distrib, *] {contextual := tt}⟩),
have l.support = ∅ ∨ (∃t∈s, {x | x ∈ l.support} ⊆ t),
  from this _ $ by intros x hx; by_contradiction; simp * at *,
this.elim
  (assume : l.support = ∅, by simp [finset.ext] at this; exact finsupp.ext this)
  (assume ⟨t, ht, hts⟩,
    have ∀x, l x ≠ 0 → x ∈ t, by simpa using hts,
    h t ht l (assume x, not_imp_comm.mp $ this x) eq)

lemma linear_independent_bUnion_of_directed {ι : Type w} {i : set ι} {s : ι → set β}
  (hs : ∀a∈i, ∀b∈i, ∃c∈i, s a ∪ s b ⊆ s c)
  (h : ∀a∈i, linear_independent (s a)) : linear_independent (⋃a∈i, s a) :=
have linear_independent (⋃₀ (s '' i)),
  from linear_independent_Union_of_directed
    (assume a ⟨j, hj, a_eq⟩ b ⟨l, hl, b_eq⟩, let ⟨k, hk, h⟩ := hs j hj l hl in
      ⟨s k, mem_image_of_mem _ hk, a_eq ▸ b_eq ▸ h⟩)
    (assume a ⟨j, hj, a_eq⟩, a_eq ▸ h j hj),
by rwa [sUnion_image] at this

lemma linear_independent.unique (hs : linear_independent s) {l₁ l₂ : lc α β}
  (h₁ : ∀x∉s, l₁ x = 0) (h₂ : ∀x∉s, l₂ x = 0) (eq : l₁.sum (λv c, c • v) = l₂.sum (λv c, c • v)) :
  l₁ = l₂ :=
eq_of_sub_eq_zero $ show l₁ - l₂ = 0,
  from hs (l₁ - l₂)
    (by simp [h₁, h₂] {contextual:=tt})
    (by simp [finsupp.sum_sub_index, eq, sub_smul, -sub_eq_add_neg, sub_self])

section repr
variables (hs : linear_independent s)

def linear_independent.repr (hs : linear_independent s) (b : β) : lc α β :=
if h : b ∈ span s then classical.some h else 0

lemma repr_not_span (h : b ∉ span s) : hs.repr b = 0 := dif_neg h

lemma repr_spec (h : b ∈ span s) : (∀b'∉s, hs.repr b b' = 0) ∧ b = (hs.repr b).sum (λb a, a • b) :=
have hs.repr b = classical.some h, from dif_pos h,
by rw [this]; exact classical.some_spec h

lemma repr_eq_zero (hb' : b' ∉ s) : hs.repr b b' = 0 :=
by_cases
  (assume : b ∈ span s, (repr_spec hs this).left _ hb')
  (assume : b ∉ span s, by rw [repr_not_span hs this]; refl)

lemma repr_sum_eq (hb : b ∈ span s) : (hs.repr b).sum (λb a, a • b) = b :=
(repr_spec hs hb).right.symm

lemma repr_eq {l : lc α β} (hb : b ∈ span s) (h : ∀x∉s, l x = 0) (eq : l.sum (λv c, c • v) = b) :
  hs.repr b = l :=
hs.unique (assume b, repr_eq_zero hs) h (by rw [repr_sum_eq hs hb, eq])

lemma repr_eq_single (hb : b ∈ s) : hs.repr b = finsupp.single b 1 :=
repr_eq hs (subset_span hb)
  (assume b' hb', finsupp.single_eq_of_ne $ show b ≠ b', from assume eq, by simp * at *)
  (by simp [finsupp.sum_single_index, add_smul])

@[simp] lemma repr_zero : hs.repr 0 = 0 :=
repr_eq hs is_submodule.zero (by simp) (by simp [finsupp.sum_zero_index])

lemma repr_support : ↑(hs.repr b).support ⊆ s :=
assume x hx, classical.by_contradiction $ assume hxs,
  by simp at hx; exact hx (repr_eq_zero hs hxs)

@[simp] lemma repr_add (hb : b ∈ span s) (hb' : b' ∈ span s) :
  hs.repr (b + b') = hs.repr b + hs.repr b' :=
repr_eq hs (is_submodule.add hb hb')
  (by simp [repr_eq_zero] {contextual := tt})
  (by simp [finsupp.sum_add_index, add_smul, repr_sum_eq hs, hb, hb'])

@[simp] lemma repr_smul (hb : b ∈ span s) : hs.repr (a • b) = a • hs.repr b :=
repr_eq hs (is_submodule.smul _ hb)
  (by simp [repr_eq_zero] {contextual := tt})
  (calc (a • hs.repr b).sum (λb a, a • b) = (hs.repr b).sum (λb a', a • (a' • b)) :
      by simp [finsupp.sum_smul_index, add_smul, smul_smul]
    ... = a • (hs.repr b).sum (λb a', a' • b) : finsupp.smul_sum.symm
    ... = a • b : by rw [repr_sum_eq hs hb])

@[simp] lemma repr_neg : hs.repr (- b) = - hs.repr b :=
by_cases
  (assume hb : b ∈ span s,
    have hs.repr ((-1) • b) = (-1) • hs.repr b, from repr_smul hs hb,
    by simpa)
  (assume hb : b ∉ span s,
    have -b ∉ span s, from assume hb, have - - b ∈ span s, from is_submodule.neg hb, by simpa,
    by simp [repr_not_span, this, hb])

@[simp] lemma repr_sub (hb : b ∈ span s) (hb' : b' ∈ span s) :
  hs.repr (b - b') = hs.repr b - hs.repr b' :=
by simp [repr_add hs hb, repr_neg hs, is_submodule.neg hb']

@[simp] lemma repr_sum {ι : Type w} {f : finset ι} {b : ι → β} :
  (∀i∈f, b i ∈ span s) → hs.repr (f.sum b) = f.sum (λi, hs.repr (b i)) :=
by apply f.induction_on;
   simp [or_imp_distrib, forall_and_distrib, repr_add hs, is_submodule.sum] {contextual := tt}

@[simp] lemma repr_finsupp_sum {ι : Type w} {δ : Type x} [has_zero δ] {f : ι →₀ δ} {b : ι → δ → β} :
  (∀i∈f.support, b i (f i) ∈ span s) → hs.repr (f.sum b) = f.sum (λi d, hs.repr (b i d)) :=
repr_sum hs

lemma repr_eq_repr_of_subset {ht : linear_independent t} (h : t ⊆ s) (hb : b ∈ span t) :
  ht.repr b = hs.repr b :=
eq.symm $ repr_eq hs (span_mono h hb)
  (assume x hx, repr_eq_zero _ $ assume hxt, hx $ h hxt)
  (repr_sum_eq ht hb)

end repr

section
variables {f : β → γ} {l : lc α β}
  (hs : linear_independent (f '' s)) (hf : is_linear_map f)
  (hf_inj : ∀ a b ∈ s, f a = f b → a = b) (hl : ∀x∉s, l x = 0)
include hs hf hf_inj

private lemma l_eq_0 (h : f (l.sum (λb a, a • b)) = 0) : l = 0 :=
have l_imp_s : ∀{x}, l x ≠ 0 → x ∈ s,
  from assume x hx, classical.by_contradiction $ assume hnx, hx $ hl _ $ hnx,
have ∀c, c ∉ f '' s → c ∉ (l.map_domain f).support,
  from assume c, mt $ assume hb,
  have c ∈ l.support.image f, from finsupp.map_domain_support hb,
  have ∃b, l b ≠ 0 ∧ f b = c, by simpa,
  let ⟨b, hb, c_eq⟩ := this in
  ⟨b, l_imp_s hb, c_eq⟩,
have l.map_domain f = 0,
  from hs _ (by simpa) $
    calc (l.map_domain f).sum (λb a, a • b) = f (l.sum (λb a, a • b)):
        by simp [finsupp.sum_map_domain_index, add_smul, hf.finsupp_sum, hf.smul]
      ... = 0 : h,
calc l = l.map_domain id :
    by rw [finsupp.map_domain_id]
   ... = l.map_domain (@inv_fun_on _ ⟨0⟩ _ f s ∘ f) :
    finsupp.map_domain_congr $
      assume b hb, (@inv_fun_on_eq' _ ⟨0⟩ _ _ _ _ hf_inj $ l_imp_s $ by simpa using hb).symm
  ... = 0 :
    by rw [finsupp.map_domain_comp, this, finsupp.map_domain_zero]

lemma linear_independent.of_image : linear_independent s :=
assume l hl eq, l_eq_0 hs hf hf_inj hl $ by simp [eq, hf.zero]

lemma linear_independent.eq_0_of_span : ∀a∈span s, f a = 0 → a = 0
| _ ⟨l, hl, rfl⟩ eq_0 := by simp [l_eq_0 hs hf hf_inj hl eq_0, finsupp.sum_zero_index]

end

/-- A set of vectors is a basis if it is linearly independent and all vectors are in the span -/
def is_basis (s : set β) := linear_independent s ∧ (∀x, x ∈ span s)

section is_basis

lemma is_basis.map_repr (hs : is_basis s) : is_linear_map hs.1.repr :=
⟨assume b₁ b₂, repr_add hs.1 (hs.2 _) (hs.2 _), assume a b, repr_smul hs.1 (hs.2 _)⟩

/-- Construct a linear map given the value at the basis. -/
def is_basis.constr (hs : is_basis s) (f : β → γ) (b : β) : γ := (hs.1.repr b).sum (λb a, a • f b)

lemma is_basis.map_constr (hs : is_basis s) {f : β → γ} : is_linear_map (hs.constr f) :=
lc.is_linear_map_sum (assume b, is_linear_map.map_smul_left is_linear_map.id) hs.map_repr

lemma is_basis.eq_linear_map {f g : β → γ} (hf : is_linear_map f) (hg : is_linear_map g)
  (hs : is_basis s) (h : ∀b∈s, f b = g b) : f = g :=
funext $ assume b, linear_eq_on hf hg h (hs.2 b)

lemma constr_congr {f g : β → γ} {b : β} (hs : is_basis s) (h : ∀b∈s, f b = g b) :
  hs.constr f = hs.constr g :=
funext $ assume b', finset.sum_congr rfl $ assume b hb,
  have b ∈ s, from repr_support hs.1 hb,
  by simp [h b this]

lemma constr_basis {f : β → γ} {b : β} (hs : is_basis s) (hb : b ∈ s) :
  (hs.constr f : β → γ) b = f b :=
show (hs.1.repr b).sum (λb a, a • f b) = f b,
  by simp [hs.1, hs.2, hb, repr_eq_single, finsupp.sum_single_index]

lemma constr_eq {g : β → γ} {f : β → γ} (hs : is_basis s)
  (hf : is_linear_map f) (h : ∀x∈s, g x = f x) : hs.constr g = f :=
hs.eq_linear_map hs.map_constr hf $ assume b hb, h b hb ▸ constr_basis hs hb

lemma constr_zero (hs : is_basis s) : hs.constr (λb, (0 : γ)) = (λb, 0) :=
constr_eq hs is_linear_map.map_zero $ by simp

lemma constr_add {g f : β → γ} (hs : is_basis s) :
  hs.constr (λb, f b + g b) = (λb, hs.constr f b + hs.constr g b) :=
constr_eq hs (is_linear_map.map_add hs.map_constr hs.map_constr) $
  by simp [constr_basis hs] {contextual := tt}

lemma constr_sub {g f : β → γ} (hs : is_basis s) :
  hs.constr (λb, f b - g b) = (λb, hs.constr f b - hs.constr g b) :=
constr_eq hs (is_linear_map.map_sub hs.map_constr hs.map_constr) $
  by simp [constr_basis hs] {contextual := tt}

lemma constr_neg {f : β → γ} (hs : is_basis s) : hs.constr (λb, - f b) = (λb, - hs.constr f b) :=
constr_eq hs hs.map_constr.map_neg $ by simp [constr_basis hs] {contextual := tt}

-- this only works on functions if `α` is a commutative ring
lemma constr_smul {α : Type u} {β : Type v} {γ : Type w} [comm_ring α] [module α β] [module α γ]
  {f : β → γ} {a : α} {s : set β} (hs : is_basis s) {b : β} :
  hs.constr (λb, a • f b) = (λb, a • (hs.constr f) b) :=
constr_eq hs hs.map_constr.map_smul_right $ by simp [constr_basis hs] {contextual := tt}

lemma constr_mem_span (hs : is_basis s) {f : β → γ} : (hs.constr f : β → γ) b ∈ span (f '' s) :=
is_submodule.sum $ assume b' hb',
  have b' ∈ s, from repr_support hs.1 hb',
  is_submodule.smul _ $ subset_span $ mem_image_of_mem _ this

lemma constr_im_eq_span (hs : is_basis s) {f : β → γ} : range (hs.constr f) = span (f '' s) :=
eq.symm $ span_eq (is_submodule.range hs.map_constr)
  (assume b' ⟨b, hb, eq⟩, ⟨b, eq ▸ constr_basis hs hb⟩)
  (assume b' ⟨b, hb⟩, hb ▸ constr_mem_span hs)

def module_equiv_lc (hs : is_basis s) : β ≃ (s →₀ α) :=
{ to_fun    := assume b, (hs.1.repr b).subtype_domain _,
  inv_fun   := assume v, v.sum $ λb a, a • b.1,
  left_inv  := assume b,
    calc ((hs.1.repr b).subtype_domain s).sum (λb a, a • b.1) = (hs.1.repr b).sum (λb a, a • b) :
      @finsupp.sum_subtype_domain_index β _ _ _ _ (λx, x ∈ s) _ _ _ _ (λb a, a • b) (repr_support hs.1)
      ... = _ : repr_sum_eq _ $ hs.2 _,
  right_inv := assume v, finsupp.ext $ assume ⟨b, hb⟩,
    have v.sum (λb' a, hs.1.repr (a • b'.val) b) = v ⟨b, hb⟩,
      from calc v.sum (λb' a, hs.1.repr (a • b'.val) b) =
          v.sum (λb' a, a * (finsupp.single b'.val 1 : lc α β) b) :
            finset.sum_congr rfl $ assume ⟨b', hb'⟩ h',
              by dsimp; rw [repr_smul hs.1 (hs.2 _), repr_eq_single _ hb']; refl
        ... = ({⟨b, hb⟩} : finset s).sum (λb', v b' * (finsupp.single b'.val 1 : lc α β) b) :
          finset.sum_bij_ne_zero (λx hx x0, x)
            (assume ⟨x, hx⟩, by by_cases x = b; simp [*]) (by simp)
            (assume ⟨x, hx⟩, by simp; intro e; subst x;
               exact assume h, ⟨b, hb, assume h', by simp * at *, h, rfl⟩)
            (by simp)
        ... = v ⟨b, hb⟩ : by simp,
    begin
      dsimp,
      rw [repr_finsupp_sum, finsupp.sum_apply],
      { exact this },
      { simp [hs.2] }
    end }

def equiv_of_is_basis {s : set β} {t : set γ} {f : β → γ} {g : γ → β}
  (hs : is_basis s) (ht : is_basis t) (hf : ∀b∈s, f b ∈ t) (hg : ∀c∈t, g c ∈ s)
  (hgf : ∀b∈s, g (f b) = b) (hfg : ∀c∈t, f (g c) = c) :
  β ≃ₗ γ :=
{ to_fun := hs.constr f,
  inv_fun := ht.constr g,
  left_inv := assume b,
    congr_fun (hs.eq_linear_map (ht.map_constr.comp hs.map_constr) is_linear_map.id $
      by simp [constr_basis, hs, ht, hf, hgf, (∘)] {contextual := tt}) b,
  right_inv := assume c,
    congr_fun (ht.eq_linear_map (hs.map_constr.comp ht.map_constr) is_linear_map.id $
      by simp [constr_basis, hs, ht, hg, hfg, (∘)] {contextual := tt}) c,
  linear_fun := hs.map_constr }

end is_basis

lemma linear_independent.inj_span_iff_inj {s : set β} {f : β → γ}
  (hf : is_linear_map f) (hfs : linear_independent (f '' s)) :
  (∀a b∈span s, f a = f b → a = b) ↔ (∀a b∈s, f a = f b → a = b) :=
iff.intro
  (assume h a b ha hb eq, h a b (subset_span ha) (subset_span hb) eq)
  (assume h a b ha hb eq, eq_of_sub_eq_zero $ hfs.eq_0_of_span hf h _ (is_submodule.sub ha hb)
    (by simp [eq, hf.add, hf.neg]))

-- TODO: clean up proof / alternative proof
lemma linear_independent.image {s : set β} {f : β → γ}
  (hf : is_linear_map f) (hs : linear_independent s)
  (hf_inj : ∀ a b ∈ span s, f a = f b → a = b) :
  linear_independent (f '' s) :=
let g := @inv_fun_on _ ⟨0⟩ _ f (span s) in
have hg : ∀x∈span s, g (f x) = x,
  from assume x, @inv_fun_on_eq' _ ⟨0⟩ _ _ _ _ hf_inj,
assume l hl eq,
have l_g : ∀b∈(l.map_domain g).support, b ∈ s, from
  assume b hb,
  have b ∈ l.support.image g, from finsupp.map_domain_support hb,
  have ∃c, l c ≠ 0 ∧ g c = b, by simpa,
  let ⟨c, hc, b_eq⟩ := this in
  have c ∈ f '' s, by by_contradiction h; simp * at *,
  let ⟨b', hb', c_eq⟩ := this in
  have b' = b, from b_eq ▸ c_eq ▸ (hg _ $ subset_span hb').symm,
  this ▸ hb',
have l_f_g : l.map_domain (f ∘ g) = l.map_domain id, from
  finsupp.map_domain_congr $ assume c hc,
    have c ∈ f '' s, by by_contradiction h; simp * at *,
    let ⟨b, hb, c_eq⟩ := this in
    by simp [c_eq.symm, (∘), hg, subset_span hb],
have l.map_domain g = 0, from
  have l_g_s : (l.map_domain g).sum (λb a, a • b) ∈ span s,
    from is_submodule.sum $ assume b hb, is_submodule.smul _ $ subset_span $ l_g b hb,
  have f_sum : f ((l.map_domain g).sum (λb a, a • b)) = 0,
    from calc f ((l.map_domain g).sum (λb a, a • b)) =
          ((l.map_domain g).map_domain f).sum (λb a, a • b) :
        by simp [finsupp.sum_map_domain_index, add_smul, hf.finsupp_sum, hf.smul]
      ... = 0 :
        by rw [←finsupp.map_domain_comp, l_f_g, finsupp.map_domain_id, eq],
  have ∀b∉s, (l.map_domain g) b = 0,
    from assume b hb, classical.by_contradiction $ assume hnb, hb $ l_g b $ by simp *,
  hs _ this $ hf_inj _ _ l_g_s is_submodule.zero (by simpa [hf.zero] using f_sum),
calc l = (l.map_domain g).map_domain f :
    by rw [←finsupp.map_domain_comp, l_f_g, finsupp.map_domain_id]
  ... = 0 :
    by rw [this, finsupp.map_domain_zero]

lemma linear_map.linear_independent_image_iff {s : set β} {f : β → γ}
  (hf : is_linear_map f) (hf_inj : ∀ a b ∈ span s, f a = f b → a = b) :
  linear_independent (f '' s) ↔ linear_independent s :=
iff.intro
  (assume h, h.of_image hf $ assume x y hx hy, hf_inj x y (subset_span hx) (subset_span hy))
  (assume h, h.image hf hf_inj)

lemma is_basis.linear_equiv {s : set β} (hs : is_basis s)
  {f : β ≃ₗ γ} : is_basis ((f.to_equiv : β → γ) '' s) :=
⟨hs.1.image f.linear_fun $ λ b1 _ b2 _ H, f.to_equiv.bijective.1 H,
λ x, by rw span_image_of_linear_map (show is_linear_map f.to_equiv, from f.linear_fun);
  from ⟨f.to_equiv.symm x, hs.2 _, by rw equiv.apply_inverse_apply⟩⟩

end module

section vector_space
variables [field α] [vector_space α β] [vector_space α γ] {s t : set β} {b b₁ b₂ : β}
include α

local attribute [instance] is_submodule_span

/- TODO: some of the following proofs can generalized with a zero_ne_one predicate type class
   (instead of a data containing type classs) -/

lemma mem_span_insert_exchange : b₁ ∈ span (insert b₂ s) → b₁ ∉ span s → b₂ ∈ span (insert b₁ s) :=
begin
  simp [span_insert],
  exact assume a b₃ hb₃ b₁_eq hb₁,
    have a ≠ 0, from assume a0, by simp * at *,
    ⟨1/a, (- 1/a) • b₃, is_submodule.smul _ hb₃,
      by simp [b₁_eq, smul_add, smul_smul, mul_inv_cancel, this, neg_div]⟩
end

lemma linear_independent_iff_not_mem_span : linear_independent s ↔ (∀b∈s, b ∉ span (s \ {b})) :=
iff.intro
  (assume (hs : linear_independent s) b hb ⟨l, hl, b_eq⟩,
    let l' := l - finsupp.single b 1 in
    have ∀b', b' ∉ s → l' b' = 0,
      from assume b' hb',
      have ne: b ≠ b', from assume h, hb' $ h ▸ hb,
      have b' ∉ s \ {b}, from assume ⟨h₁, h₂⟩, hb' h₁,
      by simp [ne, hl b' this],
    have l' = 0, from hs l' this $
      by simp [l', finsupp.sum_add_index, finsupp.sum_neg_index, add_smul, b_eq.symm, finsupp.sum_single_index],
    have - l' b = 1, from have b ∉ s \ {b}, by simp, by simp [hl _ this],
    by rw [‹l' = 0›] at this; simp at this; assumption)
  (assume hs l hl eq, finsupp.ext $ assume b, classical.by_contradiction $ assume h : l b ≠ 0,
    let a := -1 / l b in
    hs b
      (show b ∈ s, from classical.by_contradiction $ assume hnb, h $ hl b hnb)
      ⟨a • l + finsupp.single b 1,
        assume b', by_cases
          (assume : b' = b, by simp [this, h, neg_div, a])
          (assume : b' ≠ b, by simp [finsupp.sub_apply, hl, this, this.symm] {contextual:=tt}),
        have l.sum (λb a', (a * a') • b) = a • l.sum (λb a, a • b),
          by simp [finsupp.smul_sum, smul_smul],
        by simp [-sub_eq_add_neg, add_smul, finsupp.sum_add_index, finsupp.sum_single_index,
                finsupp.sum_smul_index, this, eq]⟩)

lemma linear_independent_singleton {b : β} (hb : b ≠ 0) : linear_independent ({b} : set β) :=
linear_independent_iff_not_mem_span.mpr $ by simp [hb] {contextual := tt}

lemma linear_independent.insert (hs : linear_independent s) (hb : b ∉ span s) :
  linear_independent (insert b s) :=
assume l hl eq, by_cases
  (assume : l b = 0,
    hs l (assume x hx,
      by_cases (assume h : x = b, h.symm ▸ this) (assume h', hl _ $ by simp [not_or_distrib, hx, h']))
      eq)
  (assume lb_ne_zero : l b ≠ 0,
    have (1 / l b) • (- (- l b • b)) ∈ span s,
      from is_submodule.smul _ $ is_submodule.neg ⟨l - finsupp.single b (l b),
        assume x hx, by_cases
          (assume : b = x, by simp [this.symm])
          (assume ne : b ≠ x,
            have x ∉ insert b s, by simp [not_or_distrib, hx, ne.symm],
            by simp [hl x this, ne] {contextual := tt}),
        by simp [finsupp.sum_sub_index, finsupp.sum_single_index, -sub_eq_add_neg, sub_smul, eq]; simp⟩,
    have (1 / l b) • (- (- l b • b)) = b,
      by simp [smul_smul, mul_comm, mul_inv_cancel lb_ne_zero],
    by simp * at *)

lemma exists_linear_independent (hs : linear_independent s) (hst : s ⊆ t) :
  ∃b⊆t, s ⊆ b ∧ t ⊆ span b ∧ linear_independent b :=
let C := { b : set β // s ⊆ b ∧ b ⊆ t ∧ linear_independent b }, s' : C := ⟨s, le_refl s, hst, hs⟩ in
have ∀c, zorn.chain (λa b:C, a.val ⊆ b.val) c → c ≠ ∅ → ∃(m : C), ∀a:C, a ∈ c → a.val ⊆ m.val, from
  assume c hc ne,
  let ⟨a, ha⟩ := exists_mem_of_ne_empty ne in
  ⟨⟨(⋃a ∈ c, (a : C).val),
    subset.trans a.property.1 $ subset_bUnion_of_mem ha,
    bUnion_subset $ assume c hc, c.property.right.left,
    linear_independent_bUnion_of_directed
      (assume a ha b hb, by_cases
        (assume h : a = b, ⟨a, ha, h ▸ le_of_eq (@sup_idem (set _) _ a.val)⟩)
        (assume h : a ≠ b, (hc a ha b hb h).elim
          (assume h, ⟨b, hb, union_subset h (subset.refl _)⟩)
          (assume h, ⟨a, ha, union_subset (subset.refl _) h⟩)))
      (assume a ha, a.property.2.2)⟩,
    assume a ha, subset_bUnion_of_mem ha⟩,
have ∃m:C, ∀a:C, m.val ⊆ a.val → a.val ⊆ m.val, from
  zorn.zorn
    (assume c hc, by_cases (assume : c = ∅, ⟨s', assume a, this.symm ▸ false.elim⟩) (this c hc))
    (assume a b c, subset.trans),
let ⟨⟨m, hsm, hmt, hml⟩, hm⟩ := this in
have t ⊆ span m,
  from classical.by_contradiction $ assume : ¬ t ⊆ span m,
  let ⟨b, hb⟩ := classical.not_forall.mp this, ⟨hbt, hbm⟩ := not_imp.mp hb in
  have insert b m ⊆ m,
    from hm
      ⟨_, subset.trans hsm $ subset_insert _ _, by simp [set.insert_subset, hmt, hbt], hml.insert hbm⟩
      (subset_insert _ _),
  have b ∈ span m, from subset_span $ this $ mem_insert _ _,
  hbm this,
⟨m, hmt, hsm, this, hml⟩

lemma exists_subset_is_basis (hs : linear_independent s) : ∃b, s ⊆ b ∧ is_basis b :=
let ⟨b, hb₀, hb₁, hb₂, hb₃⟩ := exists_linear_independent hs (@subset_univ _ _) in
⟨b, hb₁, hb₃, assume x, hb₂ trivial⟩

variable (β)
lemma exists_is_basis : ∃b : set β, is_basis b :=
let ⟨b, _, hb⟩ := exists_subset_is_basis linear_independent_empty in ⟨b, hb⟩
variable {β}

lemma eq_of_linear_independent_of_span
  (hs : linear_independent s) (h : t ⊆ s) (hst : s ⊆ span t) : s = t :=
suffices s ⊆ t, from subset.antisymm this h,
assume b hb,
have (hs.mono h).repr b = finsupp.single b 1,
  from calc (hs.mono h).repr b = hs.repr b : repr_eq_repr_of_subset hs h $ hst hb
    ... = finsupp.single b 1 : repr_eq_single hs hb,
have b ∈ (↑((hs.mono h).repr b).support : set β), by simp [this],
repr_support _ this

lemma exists_of_linear_independent_of_finite_span {t : finset β}
  (hs : linear_independent s) (hst : s ⊆ span ↑t) :
  ∃t':finset β, ↑t' ⊆ s ∪ ↑t ∧ s ⊆ ↑t' ∧ t'.card = t.card :=
have ∀t, ∀(s' : finset β), ↑s' ⊆ s → s ∩ ↑t = ∅ → s ⊆ span ↑(s' ∪ t) →
  ∃t':finset β, ↑t' ⊆ s ∪ ↑t ∧ s ⊆ ↑t' ∧ t'.card = (s' ∪ t).card :=
assume t, finset.induction_on t
  (assume s' hs' _ hss',
    have s = ↑s', from eq_of_linear_independent_of_span hs hs' $ by simpa using hss',
    ⟨s', by simp [this]⟩)
  (assume b₁ t hb₁t ih s' hs' hst hss',
    have hb₁s : b₁ ∉ s,
      from assume h,
      have b₁ ∈ s ∩ ↑(insert b₁ t), from ⟨h, finset.mem_insert_self _ _⟩,
      by rwa [hst] at this,
    have hb₁s' : b₁ ∉ s', from assume h, hb₁s $ hs' h,
    have hst : s ∩ ↑t = ∅,
      from eq_empty_of_subset_empty $ subset.trans
        (by simp [inter_subset_inter, subset.refl]) (le_of_eq hst),
    by_cases
      (assume : s ⊆ span ↑(s' ∪ t),
        let ⟨u, hust, hsu, eq⟩ := ih _ hs' hst this in
        have hb₁u : b₁ ∉ u, from assume h, (hust h).elim hb₁s hb₁t,
        ⟨insert b₁ u, by simp [set.insert_subset_insert hust],
          subset.trans hsu (by simp), by simp [eq, hb₁t, hb₁s', hb₁u]⟩)
      (assume : ¬ s ⊆ span ↑(s' ∪ t),
        let ⟨b₂, hb₂s, hb₂t⟩ := set.not_subset.mp this in
        have hb₂t' : b₂ ∉ s' ∪ t, from assume h, hb₂t $ subset_span h,
        have s ⊆ span ↑(insert b₂ s' ∪ t), from
          assume b₃ hb₃,
          have ↑(s' ∪ insert b₁ t) ⊆ insert b₁ (insert b₂ ↑(s' ∪ t) : set β),
            by simp [insert_eq, -singleton_union, -union_singleton, union_subset_union, subset.refl, subset_union_right],
          have hb₃ : b₃ ∈ span (insert b₁ (insert b₂ ↑(s' ∪ t) : set β)),
            from span_mono this (hss' hb₃),
          have s ⊆ span (insert b₁ ↑(s' ∪ t)),
            by simpa [insert_eq, -singleton_union, -union_singleton] using hss',
          have hb₁ : b₁ ∈ span (insert b₂ ↑(s' ∪ t)),
            from mem_span_insert_exchange (this hb₂s) hb₂t,
          by rw [span_insert_eq_span hb₁] at hb₃; simpa using hb₃,
        let ⟨u, hust, hsu, eq⟩ := ih _ (by simp [set.insert_subset, hb₂s, hs']) hst this in
        ⟨u, subset.trans hust $ union_subset_union (subset.refl _) (by simp [subset_insert]),
          hsu, by rw [finset.union_comm] at hb₂t'; simp [eq, hb₂t', hb₁t, hb₁s']⟩)),
have eq : t.filter (λx, x ∈ s) ∪ t.filter (λx, x ∉ s) = t,
  from finset.ext.mpr $ assume x, by by_cases x ∈ s; simp *,
let ⟨u, h₁, h₂, h⟩ := this (t.filter (λx, x ∉ s)) (t.filter (λx, x ∈ s))
  (by simp [set.subset_def]) (by simp [set.ext_iff] {contextual := tt}) (by rwa [eq]) in
⟨u, subset.trans h₁ (by simp [subset_def, and_imp, or_imp_distrib] {contextual:=tt}),
  h₂, by rwa [eq] at h⟩

lemma exists_finite_card_le_of_finite_of_linear_independent_of_span
  (ht : finite t) (hs : linear_independent s) (hst : s ⊆ span t) :
  ∃h : finite s, h.to_finset.card ≤ ht.to_finset.card :=
have s ⊆ span ↑(ht.to_finset), by simp; assumption,
let ⟨u, hust, hsu, eq⟩ := exists_of_linear_independent_of_finite_span hs this in
have finite s, from finite_subset u.finite_to_set hsu,
⟨this, by rw [←eq]; exact (finset.card_le_of_subset $ finset.coe_subset.mp $ by simp [hsu])⟩

lemma exists_left_inverse_linear_map_of_injective {f : β → γ}
  (hf : is_linear_map f) (hf_inj : injective f) : ∃g:γ → β, is_linear_map g ∧ g ∘ f = id :=
let ⟨bβ, hbβ⟩ := exists_is_basis β in
have linear_independent (f '' bβ),
  from hbβ.1.image hf $ assume b₁ _ b₂ _ eq, hf_inj eq,
let ⟨bγ, hbγ₁, hbγ₂⟩ := exists_subset_is_basis this in
have ∀b∈bβ, (hbγ₂.constr (@inv_fun _ ⟨0⟩ _ f) : γ → β) (f b) = b,
  begin
    assume b hb,
    rw [constr_basis],
    { exact @inv_fun_on_eq' β ⟨0⟩ γ f univ b (assume b₁ _ b₂ _ eq, hf_inj eq) trivial },
    { exact hbγ₁ (mem_image_of_mem _ hb) }
  end,
⟨hbγ₂.constr $ @inv_fun _ ⟨0⟩ _ f, hbγ₂.map_constr,
  hbβ.eq_linear_map (hbγ₂.map_constr.comp hf) is_linear_map.id this⟩

lemma exists_right_inverse_linear_map_of_surjective {f : β → γ}
  (hf : is_linear_map f) (hf_surj : surjective f) :
  ∃g:γ → β, is_linear_map g ∧ f ∘ g = id :=
let g := @inv_fun _ ⟨0⟩ _ f in
have ri_gf : right_inverse g f, from @right_inverse_inv_fun _ ⟨0⟩ _ _ hf_surj,
have injective g, from injective_of_left_inverse ri_gf,
let ⟨bγ, hbγ⟩ := exists_is_basis γ in
have ∀c∈bγ, f ((hbγ.constr g : γ → β) c) = c,
  from assume c hc, by rw [constr_basis hbγ hc, ri_gf],
⟨hbγ.constr g, hbγ.map_constr, hbγ.eq_linear_map (hf.comp hbγ.map_constr) is_linear_map.id this⟩

end vector_space