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

! This file was ported from Lean 3 source module analysis.box_integral.box.basic
! leanprover-community/mathlib commit f2ce6086713c78a7f880485f7917ea547a215982
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Set.Intervals.Monotone
import Mathbin.Topology.Algebra.Order.MonotoneConvergence
import Mathbin.Topology.MetricSpace.Basic

/-!
# Rectangular boxes in `ℝⁿ`

In this file we define rectangular boxes in `ℝⁿ`. As usual, we represent `ℝⁿ` as the type of
functions `ι → ℝ` (usually `ι = fin n` for some `n`). When we need to interpret a box `[l, u]` as a
set, we use the product `{x | ∀ i, l i < x i ∧ x i ≤ u i}` of half-open intervals `(l i, u i]`. We
exclude `l i` because this way boxes of a partition are disjoint as sets in `ℝⁿ`.

Currently, the only use cases for these constructions are the definitions of Riemann-style integrals
(Riemann, Henstock-Kurzweil, McShane).

## Main definitions

We use the same structure `box_integral.box` both for ambient boxes and for elements of a partition.
Each box is stored as two points `lower upper : ι → ℝ` and a proof of `∀ i, lower i < upper i`. We
define instances `has_mem (ι → ℝ) (box ι)` and `has_coe_t (box ι) (set $ ι → ℝ)` so that each box is
interpreted as the set `{x | ∀ i, x i ∈ set.Ioc (I.lower i) (I.upper i)}`. This way boxes of a
partition are pairwise disjoint and their union is exactly the original box.

We require boxes to be nonempty, because this way coercion to sets is injective. The empty box can
be represented as `⊥ : with_bot (box_integral.box ι)`.

We define the following operations on boxes:

* coercion to `set (ι → ℝ)` and `has_mem (ι → ℝ) (box_integral.box ι)` as described above;
* `partial_order` and `semilattice_sup` instances such that `I ≤ J` is equivalent to
  `(I : set (ι → ℝ)) ⊆ J`;
* `lattice` instances on `with_bot (box_integral.box ι)`;
* `box_integral.box.Icc`: the closed box `set.Icc I.lower I.upper`; defined as a bundled monotone
  map from `box ι` to `set (ι → ℝ)`;
* `box_integral.box.face I i : box (fin n)`: a hyperface of `I : box_integral.box (fin (n + 1))`;
* `box_integral.box.distortion`: the maximal ratio of two lengths of edges of a box; defined as the
  supremum of `nndist I.lower I.upper / nndist (I.lower i) (I.upper i)`.

We also provide a convenience constructor `box_integral.box.mk' (l u : ι → ℝ) : with_bot (box ι)`
that returns the box `⟨l, u, _⟩` if it is nonempty and `⊥` otherwise.

## Tags

rectangular box
-/

open Set Function Metric Filter

noncomputable section

open NNReal Classical Topology

namespace BoxIntegral

variable {ι : Type _}

/-!
### Rectangular box: definition and partial order
-/

/-- A nontrivial rectangular box in `ι → ℝ` with corners `lower` and `upper`. Repesents the product
of half-open intervals `(lower i, upper i]`. -/
structure Box (ι : Type _) where
  (lower upper : ι → ℝ)
  lower_lt_upper : ∀ i, lower i < upper i
#align box_integral.box BoxIntegral.Box

attribute [simp] box.lower_lt_upper

namespace Box

variable (I J : Box ι) {x y : ι → ℝ}

instance : Inhabited (Box ι) :=
  ⟨⟨0, 1, fun i => zero_lt_one⟩⟩

theorem lower_le_upper : I.lower ≤ I.upper := fun i => (I.lower_lt_upper i).le
#align box_integral.box.lower_le_upper BoxIntegral.Box.lower_le_upper

theorem lower_ne_upper (i) : I.lower i ≠ I.upper i :=
  (I.lower_lt_upper i).Ne
#align box_integral.box.lower_ne_upper BoxIntegral.Box.lower_ne_upper

instance : Membership (ι → ℝ) (Box ι) :=
  ⟨fun x I => ∀ i, x i ∈ Ioc (I.lower i) (I.upper i)⟩

instance : CoeTC (Box ι) (Set <| ι → ℝ) :=
  ⟨fun I => { x | x ∈ I }⟩

@[simp]
theorem mem_mk {l u x : ι → ℝ} {H} : x ∈ mk l u H ↔ ∀ i, x i ∈ Ioc (l i) (u i) :=
  Iff.rfl
#align box_integral.box.mem_mk BoxIntegral.Box.mem_mk

@[simp, norm_cast]
theorem mem_coe : x ∈ (I : Set (ι → ℝ)) ↔ x ∈ I :=
  Iff.rfl
#align box_integral.box.mem_coe BoxIntegral.Box.mem_coe

theorem mem_def : x ∈ I ↔ ∀ i, x i ∈ Ioc (I.lower i) (I.upper i) :=
  Iff.rfl
#align box_integral.box.mem_def BoxIntegral.Box.mem_def

theorem mem_univ_Ioc {I : Box ι} : (x ∈ pi univ fun i => Ioc (I.lower i) (I.upper i)) ↔ x ∈ I :=
  mem_univ_pi
#align box_integral.box.mem_univ_Ioc BoxIntegral.Box.mem_univ_Ioc

theorem coe_eq_pi : (I : Set (ι → ℝ)) = pi univ fun i => Ioc (I.lower i) (I.upper i) :=
  Set.ext fun x => mem_univ_Ioc.symm
#align box_integral.box.coe_eq_pi BoxIntegral.Box.coe_eq_pi

@[simp]
theorem upper_mem : I.upper ∈ I := fun i => right_mem_Ioc.2 <| I.lower_lt_upper i
#align box_integral.box.upper_mem BoxIntegral.Box.upper_mem

theorem exists_mem : ∃ x, x ∈ I :=
  ⟨_, I.upper_mem⟩
#align box_integral.box.exists_mem BoxIntegral.Box.exists_mem

theorem nonempty_coe : Set.Nonempty (I : Set (ι → ℝ)) :=
  I.exists_mem
#align box_integral.box.nonempty_coe BoxIntegral.Box.nonempty_coe

@[simp]
theorem coe_ne_empty : (I : Set (ι → ℝ)) ≠ ∅ :=
  I.nonempty_coe.ne_empty
#align box_integral.box.coe_ne_empty BoxIntegral.Box.coe_ne_empty

@[simp]
theorem empty_ne_coe : ∅ ≠ (I : Set (ι → ℝ)) :=
  I.coe_ne_empty.symm
#align box_integral.box.empty_ne_coe BoxIntegral.Box.empty_ne_coe

instance : LE (Box ι) :=
  ⟨fun I J => ∀ ⦃x⦄, x ∈ I → x ∈ J⟩

theorem le_def : I ≤ J ↔ ∀ x ∈ I, x ∈ J :=
  Iff.rfl
#align box_integral.box.le_def BoxIntegral.Box.le_def

theorem le_tFAE :
    TFAE
      [I ≤ J, (I : Set (ι → ℝ)) ⊆ J, Icc I.lower I.upper ⊆ Icc J.lower J.upper,
        J.lower ≤ I.lower ∧ I.upper ≤ J.upper] :=
  by
  tfae_have 1 ↔ 2; exact Iff.rfl
  tfae_have 2 → 3
  · intro h
    simpa [coe_eq_pi, closure_pi_set, lower_ne_upper] using closure_mono h
  tfae_have 3 ↔ 4; exact Icc_subset_Icc_iff I.lower_le_upper
  tfae_have 4 → 2; exact fun h x hx i => Ioc_subset_Ioc (h.1 i) (h.2 i) (hx i)
  tfae_finish
#align box_integral.box.le_tfae BoxIntegral.Box.le_tFAE

variable {I J}

@[simp, norm_cast]
theorem coe_subset_coe : (I : Set (ι → ℝ)) ⊆ J ↔ I ≤ J :=
  Iff.rfl
#align box_integral.box.coe_subset_coe BoxIntegral.Box.coe_subset_coe

theorem le_iff_bounds : I ≤ J ↔ J.lower ≤ I.lower ∧ I.upper ≤ J.upper :=
  (le_tFAE I J).out 0 3
#align box_integral.box.le_iff_bounds BoxIntegral.Box.le_iff_bounds

theorem injective_coe : Injective (coe : Box ι → Set (ι → ℝ)) :=
  by
  rintro ⟨l₁, u₁, h₁⟩ ⟨l₂, u₂, h₂⟩ h
  simp only [subset.antisymm_iff, coe_subset_coe, le_iff_bounds] at h
  congr
  exacts[le_antisymm h.2.1 h.1.1, le_antisymm h.1.2 h.2.2]
#align box_integral.box.injective_coe BoxIntegral.Box.injective_coe

@[simp, norm_cast]
theorem coe_inj : (I : Set (ι → ℝ)) = J ↔ I = J :=
  injective_coe.eq_iff
#align box_integral.box.coe_inj BoxIntegral.Box.coe_inj

@[ext]
theorem ext (H : ∀ x, x ∈ I ↔ x ∈ J) : I = J :=
  injective_coe <| Set.ext H
#align box_integral.box.ext BoxIntegral.Box.ext

theorem ne_of_disjoint_coe (h : Disjoint (I : Set (ι → ℝ)) J) : I ≠ J :=
  mt coe_inj.2 <| h.Ne I.coe_ne_empty
#align box_integral.box.ne_of_disjoint_coe BoxIntegral.Box.ne_of_disjoint_coe

instance : PartialOrder (Box ι) :=
  { PartialOrder.lift (coe : Box ι → Set (ι → ℝ)) injective_coe with le := (· ≤ ·) }

/-- Closed box corresponding to `I : box_integral.box ι`. -/
protected def icc : Box ι ↪o Set (ι → ℝ) :=
  OrderEmbedding.ofMapLEIff (fun I : Box ι => Icc I.lower I.upper) fun I J => (le_tFAE I J).out 2 0
#align box_integral.box.Icc BoxIntegral.Box.icc

theorem icc_def : I.Icc = Icc I.lower I.upper :=
  rfl
#align box_integral.box.Icc_def BoxIntegral.Box.icc_def

@[simp]
theorem upper_mem_icc (I : Box ι) : I.upper ∈ I.Icc :=
  right_mem_Icc.2 I.lower_le_upper
#align box_integral.box.upper_mem_Icc BoxIntegral.Box.upper_mem_icc

@[simp]
theorem lower_mem_icc (I : Box ι) : I.lower ∈ I.Icc :=
  left_mem_Icc.2 I.lower_le_upper
#align box_integral.box.lower_mem_Icc BoxIntegral.Box.lower_mem_icc

protected theorem isCompact_icc (I : Box ι) : IsCompact I.Icc :=
  isCompact_Icc
#align box_integral.box.is_compact_Icc BoxIntegral.Box.isCompact_icc

theorem icc_eq_pi : I.Icc = pi univ fun i => Icc (I.lower i) (I.upper i) :=
  (pi_univ_Icc _ _).symm
#align box_integral.box.Icc_eq_pi BoxIntegral.Box.icc_eq_pi

theorem le_iff_icc : I ≤ J ↔ I.Icc ⊆ J.Icc :=
  (le_tFAE I J).out 0 2
#align box_integral.box.le_iff_Icc BoxIntegral.Box.le_iff_icc

theorem antitone_lower : Antitone fun I : Box ι => I.lower := fun I J H => (le_iff_bounds.1 H).1
#align box_integral.box.antitone_lower BoxIntegral.Box.antitone_lower

theorem monotone_upper : Monotone fun I : Box ι => I.upper := fun I J H => (le_iff_bounds.1 H).2
#align box_integral.box.monotone_upper BoxIntegral.Box.monotone_upper

theorem coe_subset_icc : ↑I ⊆ I.Icc := fun x hx => ⟨fun i => (hx i).1.le, fun i => (hx i).2⟩
#align box_integral.box.coe_subset_Icc BoxIntegral.Box.coe_subset_icc

/-!
### Supremum of two boxes
-/

/-- `I ⊔ J` is the least box that includes both `I` and `J`. Since `↑I ∪ ↑J` is usually not a box,
`↑(I ⊔ J)` is larger than `↑I ∪ ↑J`. -/
instance : Sup (Box ι) :=
  ⟨fun I J =>
    ⟨I.lower ⊓ J.lower, I.upper ⊔ J.upper, fun i =>
      (min_le_left _ _).trans_lt <| (I.lower_lt_upper i).trans_le (le_max_left _ _)⟩⟩

instance : SemilatticeSup (Box ι) :=
  { Box.partialOrder,
    Box.hasSup with
    le_sup_left := fun I J => le_iff_bounds.2 ⟨inf_le_left, le_sup_left⟩
    le_sup_right := fun I J => le_iff_bounds.2 ⟨inf_le_right, le_sup_right⟩
    sup_le := fun I₁ I₂ J h₁ h₂ =>
      le_iff_bounds.2
        ⟨le_inf (antitone_lower h₁) (antitone_lower h₂),
          sup_le (monotone_upper h₁) (monotone_upper h₂)⟩ }

/-!
### `with_bot (box ι)`

In this section we define coercion from `with_bot (box ι)` to `set (ι → ℝ)` by sending `⊥` to `∅`.
-/

instance withBotCoe : CoeTC (WithBot (Box ι)) (Set (ι → ℝ)) :=
  ⟨fun o => o.elim ∅ coe⟩
#align box_integral.box.with_bot_coe BoxIntegral.Box.withBotCoe

@[simp, norm_cast]
theorem coe_bot : ((⊥ : WithBot (Box ι)) : Set (ι → ℝ)) = ∅ :=
  rfl
#align box_integral.box.coe_bot BoxIntegral.Box.coe_bot

@[simp, norm_cast]
theorem coe_coe : ((I : WithBot (Box ι)) : Set (ι → ℝ)) = I :=
  rfl
#align box_integral.box.coe_coe BoxIntegral.Box.coe_coe

theorem isSome_iff : ∀ {I : WithBot (Box ι)}, I.isSome ↔ (I : Set (ι → ℝ)).Nonempty
  | ⊥ => by
    erw [Option.isSome]
    simp
  | (I : box ι) => by
    erw [Option.isSome]
    simp [I.nonempty_coe]
#align box_integral.box.is_some_iff BoxIntegral.Box.isSome_iff

theorem bUnion_coe_eq_coe (I : WithBot (Box ι)) :
    (⋃ (J : Box ι) (hJ : ↑J = I), (J : Set (ι → ℝ))) = I := by
  induction I using WithBot.recBotCoe <;> simp [WithBot.coe_eq_coe]
#align box_integral.box.bUnion_coe_eq_coe BoxIntegral.Box.bUnion_coe_eq_coe

@[simp, norm_cast]
theorem withBotCoe_subset_iff {I J : WithBot (Box ι)} : (I : Set (ι → ℝ)) ⊆ J ↔ I ≤ J :=
  by
  induction I using WithBot.recBotCoe; · simp
  induction J using WithBot.recBotCoe; · simp [subset_empty_iff]
  simp
#align box_integral.box.with_bot_coe_subset_iff BoxIntegral.Box.withBotCoe_subset_iff

@[simp, norm_cast]
theorem withBotCoe_inj {I J : WithBot (Box ι)} : (I : Set (ι → ℝ)) = J ↔ I = J := by
  simp only [subset.antisymm_iff, ← le_antisymm_iff, with_bot_coe_subset_iff]
#align box_integral.box.with_bot_coe_inj BoxIntegral.Box.withBotCoe_inj

/-- Make a `with_bot (box ι)` from a pair of corners `l u : ι → ℝ`. If `l i < u i` for all `i`,
then the result is `⟨l, u, _⟩ : box ι`, otherwise it is `⊥`. In any case, the result interpreted
as a set in `ι → ℝ` is the set `{x : ι → ℝ | ∀ i, x i ∈ Ioc (l i) (u i)}`.  -/
def mk' (l u : ι → ℝ) : WithBot (Box ι) :=
  if h : ∀ i, l i < u i then ↑(⟨l, u, h⟩ : Box ι) else ⊥
#align box_integral.box.mk' BoxIntegral.Box.mk'

@[simp]
theorem mk'_eq_bot {l u : ι → ℝ} : mk' l u = ⊥ ↔ ∃ i, u i ≤ l i :=
  by
  rw [mk']
  split_ifs <;> simpa using h
#align box_integral.box.mk'_eq_bot BoxIntegral.Box.mk'_eq_bot

@[simp]
theorem mk'_eq_coe {l u : ι → ℝ} : mk' l u = I ↔ l = I.lower ∧ u = I.upper :=
  by
  cases' I with lI uI hI; rw [mk']; split_ifs
  · simp [WithBot.coe_eq_coe]
  · suffices l = lI → u ≠ uI by simpa
    rintro rfl rfl
    exact h hI
#align box_integral.box.mk'_eq_coe BoxIntegral.Box.mk'_eq_coe

@[simp]
theorem coe_mk' (l u : ι → ℝ) : (mk' l u : Set (ι → ℝ)) = pi univ fun i => Ioc (l i) (u i) :=
  by
  rw [mk']; split_ifs
  · exact coe_eq_pi _
  · rcases not_forall.mp h with ⟨i, hi⟩
    rw [coe_bot, univ_pi_eq_empty]
    exact Ioc_eq_empty hi
#align box_integral.box.coe_mk' BoxIntegral.Box.coe_mk'

instance : Inf (WithBot (Box ι)) :=
  ⟨fun I =>
    WithBot.recBotCoe (fun J => ⊥)
      (fun I J => WithBot.recBotCoe ⊥ (fun J => mk' (I.lower ⊔ J.lower) (I.upper ⊓ J.upper)) J) I⟩

@[simp]
theorem coe_inf (I J : WithBot (Box ι)) : (↑(I ⊓ J) : Set (ι → ℝ)) = I ∩ J :=
  by
  induction I using WithBot.recBotCoe;
  · change ∅ = _
    simp
  induction J using WithBot.recBotCoe;
  · change ∅ = _
    simp
  change ↑(mk' _ _) = _
  simp only [coe_eq_pi, ← pi_inter_distrib, Ioc_inter_Ioc, Pi.sup_apply, Pi.inf_apply, coe_mk',
    coe_coe]
#align box_integral.box.coe_inf BoxIntegral.Box.coe_inf

instance : Lattice (WithBot (Box ι)) :=
  { WithBot.semilatticeSup,
    Box.WithBot.hasInf with
    inf_le_left := fun I J => by
      rw [← with_bot_coe_subset_iff, coe_inf]
      exact inter_subset_left _ _
    inf_le_right := fun I J => by
      rw [← with_bot_coe_subset_iff, coe_inf]
      exact inter_subset_right _ _
    le_inf := fun I J₁ J₂ h₁ h₂ =>
      by
      simp only [← with_bot_coe_subset_iff, coe_inf] at *
      exact subset_inter h₁ h₂ }

@[simp, norm_cast]
theorem disjoint_withBotCoe {I J : WithBot (Box ι)} : Disjoint (I : Set (ι → ℝ)) J ↔ Disjoint I J :=
  by
  simp only [disjoint_iff_inf_le, ← with_bot_coe_subset_iff, coe_inf]
  rfl
#align box_integral.box.disjoint_with_bot_coe BoxIntegral.Box.disjoint_withBotCoe

theorem disjoint_coe : Disjoint (I : WithBot (Box ι)) J ↔ Disjoint (I : Set (ι → ℝ)) J :=
  disjoint_withBotCoe.symm
#align box_integral.box.disjoint_coe BoxIntegral.Box.disjoint_coe

theorem not_disjoint_coe_iff_nonempty_inter :
    ¬Disjoint (I : WithBot (Box ι)) J ↔ (I ∩ J : Set (ι → ℝ)).Nonempty := by
  rw [disjoint_coe, Set.not_disjoint_iff_nonempty_inter]
#align box_integral.box.not_disjoint_coe_iff_nonempty_inter BoxIntegral.Box.not_disjoint_coe_iff_nonempty_inter

/-!
### Hyperface of a box in `ℝⁿ⁺¹ = fin (n + 1) → ℝ`
-/

/-- Face of a box in `ℝⁿ⁺¹ = fin (n + 1) → ℝ`: the box in `ℝⁿ = fin n → ℝ` with corners at
`I.lower ∘ fin.succ_above i` and `I.upper ∘ fin.succ_above i`. -/
@[simps (config := { simpRhs := true })]
def face {n} (I : Box (Fin (n + 1))) (i : Fin (n + 1)) : Box (Fin n) :=
  ⟨I.lower ∘ Fin.succAbove i, I.upper ∘ Fin.succAbove i, fun j => I.lower_lt_upper _⟩
#align box_integral.box.face BoxIntegral.Box.face

@[simp]
theorem face_mk {n} (l u : Fin (n + 1) → ℝ) (h : ∀ i, l i < u i) (i : Fin (n + 1)) :
    face ⟨l, u, h⟩ i = ⟨l ∘ Fin.succAbove i, u ∘ Fin.succAbove i, fun j => h _⟩ :=
  rfl
#align box_integral.box.face_mk BoxIntegral.Box.face_mk

@[mono]
theorem face_mono {n} {I J : Box (Fin (n + 1))} (h : I ≤ J) (i : Fin (n + 1)) :
    face I i ≤ face J i := fun x hx i =>
  Ioc_subset_Ioc ((le_iff_bounds.1 h).1 _) ((le_iff_bounds.1 h).2 _) (hx _)
#align box_integral.box.face_mono BoxIntegral.Box.face_mono

theorem monotone_face {n} (i : Fin (n + 1)) : Monotone fun I => face I i := fun I J h =>
  face_mono h i
#align box_integral.box.monotone_face BoxIntegral.Box.monotone_face

theorem mapsTo_insertNth_face_Icc {n} (I : Box (Fin (n + 1))) {i : Fin (n + 1)} {x : ℝ}
    (hx : x ∈ Icc (I.lower i) (I.upper i)) : MapsTo (i.insertNth x) (I.face i).Icc I.Icc :=
  fun y hy => Fin.insertNth_mem_Icc.2 ⟨hx, hy⟩
#align box_integral.box.maps_to_insert_nth_face_Icc BoxIntegral.Box.mapsTo_insertNth_face_Icc

theorem mapsTo_insertNth_face {n} (I : Box (Fin (n + 1))) {i : Fin (n + 1)} {x : ℝ}
    (hx : x ∈ Ioc (I.lower i) (I.upper i)) : MapsTo (i.insertNth x) (I.face i) I := fun y hy => by
  simpa only [mem_coe, mem_def, i.forall_iff_succ_above, hx, Fin.insertNth_apply_same,
    Fin.insertNth_apply_succAbove, true_and_iff]
#align box_integral.box.maps_to_insert_nth_face BoxIntegral.Box.mapsTo_insertNth_face

theorem continuousOn_face_icc {X} [TopologicalSpace X] {n} {f : (Fin (n + 1) → ℝ) → X}
    {I : Box (Fin (n + 1))} (h : ContinuousOn f I.Icc) {i : Fin (n + 1)} {x : ℝ}
    (hx : x ∈ Icc (I.lower i) (I.upper i)) : ContinuousOn (f ∘ i.insertNth x) (I.face i).Icc :=
  h.comp (continuousOn_const.fin_insertNth i continuousOn_id) (I.mapsTo_insertNth_face_Icc hx)
#align box_integral.box.continuous_on_face_Icc BoxIntegral.Box.continuousOn_face_icc

/-!
### Covering of the interior of a box by a monotone sequence of smaller boxes
-/

/-- The interior of a box. -/
protected def ioo : Box ι →o Set (ι → ℝ)
    where
  toFun I := pi univ fun i => Ioo (I.lower i) (I.upper i)
  monotone' I J h :=
    pi_mono fun i hi => Ioo_subset_Ioo ((le_iff_bounds.1 h).1 i) ((le_iff_bounds.1 h).2 i)
#align box_integral.box.Ioo BoxIntegral.Box.ioo

theorem ioo_subset_coe (I : Box ι) : I.Ioo ⊆ I := fun x hx i => Ioo_subset_Ioc_self (hx i trivial)
#align box_integral.box.Ioo_subset_coe BoxIntegral.Box.ioo_subset_coe

protected theorem ioo_subset_icc (I : Box ι) : I.Ioo ⊆ I.Icc :=
  I.ioo_subset_coe.trans coe_subset_icc
#align box_integral.box.Ioo_subset_Icc BoxIntegral.Box.ioo_subset_icc

theorem unionᵢ_ioo_of_tendsto [Finite ι] {I : Box ι} {J : ℕ → Box ι} (hJ : Monotone J)
    (hl : Tendsto (lower ∘ J) atTop (𝓝 I.lower)) (hu : Tendsto (upper ∘ J) atTop (𝓝 I.upper)) :
    (⋃ n, (J n).Ioo) = I.Ioo :=
  have hl' : ∀ i, Antitone fun n => (J n).lower i := fun i =>
    (monotone_eval i).comp_antitone (antitone_lower.comp_monotone hJ)
  have hu' : ∀ i, Monotone fun n => (J n).upper i := fun i =>
    (monotone_eval i).comp (monotone_upper.comp hJ)
  calc
    (⋃ n, (J n).Ioo) = pi univ fun i => ⋃ n, Ioo ((J n).lower i) ((J n).upper i) :=
      unionᵢ_univ_pi_of_monotone fun i => (hl' i).Ioo (hu' i)
    _ = I.Ioo :=
      pi_congr rfl fun i hi =>
        unionᵢ_Ioo_of_mono_of_isGLB_of_isLUB (hl' i) (hu' i)
          (isGLB_of_tendsto_atTop (hl' i) (tendsto_pi_nhds.1 hl _))
          (isLUB_of_tendsto_atTop (hu' i) (tendsto_pi_nhds.1 hu _))

#align box_integral.box.Union_Ioo_of_tendsto BoxIntegral.Box.unionᵢ_ioo_of_tendsto

theorem exists_seq_mono_tendsto (I : Box ι) :
    ∃ J : ℕ →o Box ι,
      (∀ n, (J n).Icc ⊆ I.Ioo) ∧
        Tendsto (lower ∘ J) atTop (𝓝 I.lower) ∧ Tendsto (upper ∘ J) atTop (𝓝 I.upper) :=
  by
  choose a b ha_anti hb_mono ha_mem hb_mem hab ha_tendsto hb_tendsto using fun i =>
    exists_seq_strictAnti_strictMono_tendsto (I.lower_lt_upper i)
  exact
    ⟨⟨fun k => ⟨flip a k, flip b k, fun i => hab _ _ _⟩, fun k l hkl =>
        le_iff_bounds.2 ⟨fun i => (ha_anti i).Antitone hkl, fun i => (hb_mono i).Monotone hkl⟩⟩,
      fun n x hx i hi => ⟨(ha_mem _ _).1.trans_le (hx.1 _), (hx.2 _).trans_lt (hb_mem _ _).2⟩,
      tendsto_pi_nhds.2 ha_tendsto, tendsto_pi_nhds.2 hb_tendsto⟩
#align box_integral.box.exists_seq_mono_tendsto BoxIntegral.Box.exists_seq_mono_tendsto

section Distortion

variable [Fintype ι]

/-- The distortion of a box `I` is the maximum of the ratios of the lengths of its edges.
It is defined as the maximum of the ratios
`nndist I.lower I.upper / nndist (I.lower i) (I.upper i)`. -/
def distortion (I : Box ι) : ℝ≥0 :=
  Finset.univ.sup fun i : ι => nndist I.lower I.upper / nndist (I.lower i) (I.upper i)
#align box_integral.box.distortion BoxIntegral.Box.distortion

theorem distortion_eq_of_sub_eq_div {I J : Box ι} {r : ℝ}
    (h : ∀ i, I.upper i - I.lower i = (J.upper i - J.lower i) / r) : distortion I = distortion J :=
  by
  simp only [distortion, nndist_pi_def, Real.nndist_eq', h, map_div₀]
  congr 1 with i
  have : 0 < r := by
    by_contra hr
    have := div_nonpos_of_nonneg_of_nonpos (sub_nonneg.2 <| J.lower_le_upper i) (not_lt.1 hr)
    rw [← h] at this
    exact this.not_lt (sub_pos.2 <| I.lower_lt_upper i)
  simp_rw [NNReal.finset_sup_div, div_div_div_cancel_right _ ((map_ne_zero Real.nnabs).2 this.ne')]
#align box_integral.box.distortion_eq_of_sub_eq_div BoxIntegral.Box.distortion_eq_of_sub_eq_div

theorem nndist_le_distortion_mul (I : Box ι) (i : ι) :
    nndist I.lower I.upper ≤ I.distortion * nndist (I.lower i) (I.upper i) :=
  calc
    nndist I.lower I.upper =
        nndist I.lower I.upper / nndist (I.lower i) (I.upper i) * nndist (I.lower i) (I.upper i) :=
      (div_mul_cancel _ <| mt nndist_eq_zero.1 (I.lower_lt_upper i).Ne).symm
    _ ≤ I.distortion * nndist (I.lower i) (I.upper i) :=
      mul_le_mul_right' (Finset.le_sup <| Finset.mem_univ i) _

#align box_integral.box.nndist_le_distortion_mul BoxIntegral.Box.nndist_le_distortion_mul

theorem dist_le_distortion_mul (I : Box ι) (i : ι) :
    dist I.lower I.upper ≤ I.distortion * (I.upper i - I.lower i) :=
  by
  have A : I.lower i - I.upper i < 0 := sub_neg.2 (I.lower_lt_upper i)
  simpa only [← NNReal.coe_le_coe, ← dist_nndist, NNReal.coe_mul, Real.dist_eq, abs_of_neg A,
    neg_sub] using I.nndist_le_distortion_mul i
#align box_integral.box.dist_le_distortion_mul BoxIntegral.Box.dist_le_distortion_mul

theorem diam_icc_le_of_distortion_le (I : Box ι) (i : ι) {c : ℝ≥0} (h : I.distortion ≤ c) :
    diam I.Icc ≤ c * (I.upper i - I.lower i) :=
  have : (0 : ℝ) ≤ c * (I.upper i - I.lower i) :=
    mul_nonneg c.coe_nonneg (sub_nonneg.2 <| I.lower_le_upper _)
  diam_le_of_forall_dist_le this fun x hx y hy =>
    calc
      dist x y ≤ dist I.lower I.upper := Real.dist_le_of_mem_pi_Icc hx hy
      _ ≤ I.distortion * (I.upper i - I.lower i) := (I.dist_le_distortion_mul i)
      _ ≤ c * (I.upper i - I.lower i) :=
        mul_le_mul_of_nonneg_right h (sub_nonneg.2 (I.lower_le_upper i))

#align box_integral.box.diam_Icc_le_of_distortion_le BoxIntegral.Box.diam_icc_le_of_distortion_le

end Distortion

end Box

end BoxIntegral


lemma zero_pow_succ (m : mynat) : (0 : mynat) ^ (succ m) = 0 :=
begin
rw pow_succ,
exact mul_zero (0 ^ m),
end


/-
Copyright (c) 2020 Kenny Lau. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kenny Lau
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.ring_theory.algebra_tower
import Mathlib.linear_algebra.matrix
import Mathlib.PostPort

universes u v w

namespace Mathlib

/-!
# Tower of field extensions

In this file we prove the tower law for arbitrary extensions and finite extensions.
Suppose `L` is a field extension of `K` and `K` is a field extension of `F`.
Then `[L:F] = [L:K] [K:F]` where `[E₁:E₂]` means the `E₂`-dimension of `E₁`.

In fact we generalize it to vector spaces, where `L` is not necessarily a field,
but just a vector space over `K`.

## Implementation notes

We prove two versions, since there are two notions of dimensions: `vector_space.dim` which gives
the dimension of an arbitrary vector space as a cardinal, and `finite_dimensional.findim` which
gives the dimension of a finitely-dimensional vector space as a natural number.

## Tags

tower law

-/

/-- Tower law: if `A` is a `K`-vector space and `K` is a field extension of `F` then
`dim_F(A) = dim_F(K) * dim_K(A)`. -/
theorem dim_mul_dim' (F : Type u) (K : Type v) (A : Type w) [field F] [field K] [add_comm_group A]
    [algebra F K] [vector_space K A] [vector_space F A] [is_scalar_tower F K A] :
    cardinal.lift (vector_space.dim F K) * cardinal.lift (vector_space.dim K A) =
        cardinal.lift (vector_space.dim F A) :=
  sorry

/-- Tower law: if `A` is a `K`-vector space and `K` is a field extension of `F` then
`dim_F(A) = dim_F(K) * dim_K(A)`. -/
theorem dim_mul_dim (F : Type u) (K : Type v) (A : Type v) [field F] [field K] [add_comm_group A]
    [algebra F K] [vector_space K A] [vector_space F A] [is_scalar_tower F K A] :
    vector_space.dim F K * vector_space.dim K A = vector_space.dim F A :=
  sorry

namespace finite_dimensional

theorem trans (F : Type u) (K : Type v) (A : Type w) [field F] [field K] [add_comm_group A]
    [algebra F K] [vector_space K A] [vector_space F A] [is_scalar_tower F K A]
    [finite_dimensional F K] [finite_dimensional K A] : finite_dimensional F A :=
  sorry

theorem right (F : Type u) (K : Type v) (A : Type w) [field F] [field K] [add_comm_group A]
    [algebra F K] [vector_space K A] [vector_space F A] [is_scalar_tower F K A]
    [hf : finite_dimensional F A] : finite_dimensional K A :=
  sorry

/-- Tower law: if `A` is a `K`-algebra and `K` is a field extension of `F` then
`dim_F(A) = dim_F(K) * dim_K(A)`. -/
theorem findim_mul_findim (F : Type u) (K : Type v) (A : Type w) [field F] [field K]
    [add_comm_group A] [algebra F K] [vector_space K A] [vector_space F A] [is_scalar_tower F K A]
    [finite_dimensional F K] : findim F K * findim K A = findim F A :=
  sorry

protected instance linear_map (F : Type u) (V : Type v) (W : Type w) [field F] [add_comm_group V]
    [vector_space F V] [add_comm_group W] [vector_space F W] [finite_dimensional F V]
    [finite_dimensional F W] : finite_dimensional F (linear_map F V W) :=
  sorry

theorem findim_linear_map (F : Type u) (V : Type v) (W : Type w) [field F] [add_comm_group V]
    [vector_space F V] [add_comm_group W] [vector_space F W] [finite_dimensional F V]
    [finite_dimensional F W] : findim F (linear_map F V W) = findim F V * findim F W :=
  sorry

-- TODO: generalize by removing [finite_dimensional F K]

-- V = ⊕F,

-- (V →ₗ[F] K) = ((⊕F) →ₗ[F] K) = (⊕ (F →ₗ[F] K)) = ⊕K

protected instance linear_map' (F : Type u) (K : Type v) (V : Type w) [field F] [field K]
    [algebra F K] [finite_dimensional F K] [add_comm_group V] [vector_space F V]
    [finite_dimensional F V] : finite_dimensional K (linear_map F V K) :=
  right F K (linear_map F V K)

theorem findim_linear_map' (F : Type u) (K : Type v) (V : Type w) [field F] [field K] [algebra F K]
    [finite_dimensional F K] [add_comm_group V] [vector_space F V] [finite_dimensional F V] :
    findim K (linear_map F V K) = findim F V :=
  iff.mp (nat.mul_right_inj ((fun (this : 0 < findim F K) => this) findim_pos))
    (Eq.trans (Eq.trans (findim_mul_findim F K (linear_map F V K)) (findim_linear_map F V K))
      (mul_comm (findim F V) (findim F K)))

end Mathlib


import set_theory.game

open pgame

namespace pgame

local infix ` ≈ ` := pgame.equiv

theorem equiv_of_mk_equiv {x y : pgame}
  (L : x.left_moves ≃ y.left_moves) (R : x.right_moves ≃ y.right_moves)
  (hl : ∀ (i : x.left_moves), x.move_left i ≈ y.move_left (L i))
  (hr : ∀ (j : y.right_moves), x.move_right (R.symm j) ≈ y.move_right j) : x ≈ y :=
sorry

theorem equiv_of_game_eq {x y : pgame}
  (L : x.left_moves ≃ y.left_moves) (R : x.right_moves ≃ y.right_moves)
  (hl : ∀ (i : x.left_moves), ⟦x.move_left i⟧ = ⟦y.move_left (L i)⟧)
  (hr : ∀ (j : y.right_moves), ⟦x.move_right (R.symm j)⟧ = ⟦y.move_right j⟧) :
  -- (hl : ∀ (i : x.left_moves), x.move_left i ≈ y.move_left (L i))
  -- (hr : ∀ (j : y.right_moves), x.move_right (R.symm j) ≈ y.move_right j) :
  x ≈ y := sorry
-- begin
--   fsplit; rw le_def,
--   { exact ⟨λ i, or.inl ⟨L i, (hl i).1⟩, λ j, or.inr ⟨R.symm j, (hr j).1⟩⟩ },
--   { fsplit; apply quotient.sound,
--     { intro i,
--       left,
--       specialize hl (L.symm i),
--       simp only [move_left_mk, equiv.apply_symm_apply] at hl,
--       use ⟨L.symm i, hl.2⟩ },
--     { intro j,
--       right,
--       specialize hr (R j),
--       simp only [move_right_mk, equiv.symm_apply_apply] at hr,
--       use ⟨R j, hr.2⟩ } }
-- end
#check add_congr

def mul (x y : pgame) : pgame :=
begin
  induction x with xl xr xL xR IHxl IHxr generalizing y,
  induction y with yl yr yL yR IHyl IHyr,
  have y := mk yl yr yL yR,
  refine ⟨xl × yl ⊕ xr × yr, xl × yr ⊕ xr × yl, _, _⟩; rintro (⟨i, j⟩ | ⟨i, j⟩),
  { exact IHxl i y + IHyl j - IHxl i (yL j) },
  { exact IHxr i y + IHyr j - IHxr i (yR j) },
  { exact IHxl i y + IHyr j - IHxl i (yR j) },
  { exact IHxr i y + IHyl j - IHxr i (yL j) }
end

instance : has_mul pgame := ⟨mul⟩

theorem mul_congr {w x y z : pgame} (h₁ : w ≈ x) (h₂ : y ≈ z) : w * y ≈ x * z :=
begin
  refine equiv_of_mk_equiv _ _ _ _,

end
end pgame


/-
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
import Lean.Parser.Term
import Std.Classes.SetNotation
import Mathlib.Mathport.Rename

/-!

# Sets

This file sets up the theory of sets whose elements have a given type.

## Main definitions

Given a type `X` and a predicate `p : X → Prop`:

* `Set X` : the type of sets whose elements have type `X`
* `{a : X | p a} : Set X` : the set of all elements of `X` satisfying `p`
* `{a | p a} : Set X` : a more concise notation for `{a : X | p a}`
* `{a ∈ S | p a} : Set X` : given `S : Set X`, the subset of `S` consisting of
   its elements satisfying `p`.

## Implementation issues

As in Lean 3, `Set X := X → Prop`

I didn't call this file Data.Set.Basic because it contains core Lean 3
stuff which happens before mathlib3's data.set.basic .
This file is a port of the core Lean 3 file `lib/lean/library/init/data/set.lean`.

-/

def Set (α : Type u) := α → Prop
#align set Set

def setOf {α : Type u} (p : α → Prop) : Set α :=
p
#align set_of setOf

namespace Set

/-- Membership in a set -/
protected def Mem (a : α) (s : Set α) : Prop :=
s a

instance : Membership α (Set α) :=
⟨Set.Mem⟩

theorem ext {a b : Set α} (h : ∀ (x : α), x ∈ a ↔ x ∈ b) : a = b :=
funext (fun x ↦ propext (h x))

protected def Subset (s₁ s₂ : Set α) :=
∀ ⦃a⦄, a ∈ s₁ → a ∈ s₂

/-- Porting note: we introduce `≤` before `⊆` to help the unifier when applying lattice theorems
to subset hypotheses. -/
instance : LE (Set α) :=
  ⟨Set.Subset⟩

instance : HasSubset (Set α) :=
  ⟨(· ≤ ·)⟩

instance : EmptyCollection (Set α) :=
⟨λ _ => False⟩

open Std.ExtendedBinder in
syntax "{ " extBinder " | " term " }" : term

macro_rules
  | `({ $x:ident | $p }) => `(setOf fun $x:ident ↦ $p)
  | `({ $x:ident : $t | $p }) => `(setOf fun $x:ident : $t ↦ $p)
  | `({ $x:ident $b:binderPred | $p }) =>
    `(setOf fun $x:ident ↦ satisfies_binder_pred% $x $b ∧ $p)

@[app_unexpander setOf]
def setOf.unexpander : Lean.PrettyPrinter.Unexpander
  | `($_ fun $x:ident ↦ $p) => `({ $x:ident | $p })
  | `($_ fun $x:ident : $ty:term ↦ $p) => `({ $x:ident : $ty:term | $p })
  | _ => throw ()

open Std.ExtendedBinder in
macro (priority := low) "{ " t:term " | " bs:extBinders " }" : term =>
  `({ x | ∃ᵉ $bs:extBinders, $t = x })

def univ : Set α := {_a | True}
#align set.univ Set.univ

protected def insert (a : α) (s : Set α) : Set α :=
{b | b = a ∨ b ∈ s}

instance : Insert α (Set α) := ⟨Set.insert⟩

protected def singleton (a : α) : Set α :=
{b | b = a}

instance : Singleton α (Set α) := ⟨Set.singleton⟩

protected def union (s₁ s₂ : Set α) : Set α :=
{a | a ∈ s₁ ∨ a ∈ s₂}

instance : Union (Set α) :=
⟨Set.union⟩

protected def inter (s₁ s₂ : Set α) : Set α :=
{a | a ∈ s₁ ∧ a ∈ s₂}

instance : Inter (Set α) :=
⟨Set.inter⟩

protected def compl (s : Set α) : Set α :=
{a | a ∉ s}

protected def diff (s t : Set α) : Set α :=
{a ∈ s | a ∉ t}

instance : SDiff (Set α) :=
⟨Set.diff⟩

def powerset (s : Set α) : Set (Set α) :=
{t | t ⊆ s}

prefix:100 "𝒫" => powerset

def image (f : α → β) (s : Set α) : Set β :=
  { f a | a ∈ s }

instance : Functor Set :=
{ map := @Set.image }

instance : LawfulFunctor Set where
  id_map _ := funext fun _ ↦ propext ⟨λ ⟨_, sb, rfl⟩ => sb, λ sb => ⟨_, sb, rfl⟩⟩
  comp_map g h _ := funext $ λ c => propext
    ⟨λ ⟨a, ⟨h₁, h₂⟩⟩ => ⟨g a, ⟨⟨a, ⟨h₁, rfl⟩⟩, h₂⟩⟩,
     λ ⟨_, ⟨⟨a, ⟨h₁, h₂⟩⟩, h₃⟩⟩ => ⟨a, ⟨h₁, show h (g a) = c from h₂ ▸ h₃⟩⟩⟩
  map_const := rfl

end Set


import data.real.basic
import data.int.parity
import algebra.geom_sum

open set
open finset

open_locale big_operators

noncomputable theory

-- exercici 9
example (θ χ : Prop) : θ ↔ (θ ∧ χ) ∨ (θ ∧ (¬ χ)) :=
begin
  -- sorry
  split,
  {
    intro t,
    by_cases c : χ,
    {
      left,
      split,
      {
        exact t,
      },
      {
        exact c,
      }
    },
    {
      right,
      split,
      {
        exact t,
      },
      {
        exact c,
      }
    }
  },
  {
    intro h,
    cases h with h1 h1;
    {
      exact h1.1,
    },
  }
  -- sorry
end

example (θ χ : Prop) : θ ↔ (θ ∧ χ) ∨ (θ ∧ (¬ χ)) :=
begin
  split;finish,
end

-- exercici 11
def R := λ (x y : ℕ), x < y
def S := λ (x y : ℕ), x > y

example : ∀ x, ∃ y, R x y :=
begin
  -- sorry
  intro x,
  unfold R,
  use x + 1,
  exact lt_add_one x,
  -- sorry
end

example : ¬ (∀ x, ∃ y, S x y) :=
begin
  -- sorry
  unfold S,
  push_neg,
  use 0,
  intro y,
  exact zero_le y,
  -- sorry
end

-- exercici 12
def S' := λ (a b c : ℕ), c = a + b

example : ∃ (x : ℕ), ∃ (z : ℕ), (¬ x = z) ∧
  (∃ y, S' y y x) ∧
  (∃ t, S' t t z) :=
begin
  -- sorry
  unfold S',
  use 0, use 2,
  split,
  {
    change 0 ≠ 2,
    exact two_ne_zero.symm,
  },
  split,
  {
    use 0,
  },
  {
    use 1,
  }
  -- sorry
end

example : ¬ (∃ x, (∀ y, ∃ z, S' y z x)) :=
begin
  -- sorry
  unfold S',
  push_neg,
  intro x,
  use x + 1,
  intro z,
  rw add_assoc,
  intro h,
  linarith,
  -- sorry
end

-- 14a
example (n : ℕ) : ∑ k in range (n + 1), (k : ℝ) = n * (n + 1) / 2 :=
begin
  -- sorry
  induction n with d hd,
  {
    simp,
  },
  {
    rw range_succ,
    rw sum_insert not_mem_range_self,
    rw hd,
    field_simp,
    ring,
  }
  -- sorry
end

-- 14b
example (n : ℕ) : ∑ k in range (n + 1), (k^2 : ℝ) = n*(n + 1)*(2*n + 1) / 6 :=
begin
  -- sorry
  induction n with d hd,
  {
    simp,
  },
  {
    rw range_succ,
    rw sum_insert not_mem_range_self,
    rw hd,
    field_simp,
    left,
    ring,
  }
  -- sorry
end

-- 14c
example (n : ℕ) : 6 ∣ (n * (n^2 + 5)) :=
begin
  -- sorry
  induction n with d hd,
  {
    use 0,
    ring_nf,
  },
  {
    obtain ⟨k, hk⟩ := hd,
    have he : even (d^2+d),
    {
      by_cases H : even d,
      {
        obtain ⟨e, he⟩ := H,
        use 2*e^2 + e,
        nlinarith,
      },
      {
        rw nat.even_iff_not_odd at H,
        push_neg at H,
        obtain ⟨o, ho⟩ := H,
        use 2*o^2 + 3*o + 1,
        rw ho,
        ring,
      }
    },
    obtain ⟨d2, hd2⟩ := he,
    use k + 1 + d2,
    rw add_pow_two _ 1,
    norm_num,
    rw add_mul _ 1,
    norm_num,
    rw mul_add 6 _,
    rw mul_add 6 _,
    rw ←hk,
    have six : 6 = 3 * 2, by norm_num,
    rw six,
    nth_rewrite_rhs 1 mul_assoc 3 2,
    rw ←hd2,
    nlinarith,
  }
  -- sorry
end

-- exercici 15
example (n : ℕ) : 3 ∣ (4:ℤ)^n - 1 :=
begin
  -- sorry
  rw ←mul_geom_sum,
  use geom_sum 4 n,
  refl,
  -- sorry
end

example : ∃ (n:ℕ), ¬ 3 ∣ (4:ℤ)^n + 1 :=
begin
  -- sorry
  use 1,
  simp,
  ring_nf,
  norm_num,
  -- sorry
end

-- exercici 17
example (a : ℕ → ℝ) (h1 : (∀ n, 0 < a n) ∨ (∀ n, a n < 0)) (h2 : ∀ n, a n > -1) (n : ℕ):
1 + ∑ i in range (n+2), a i < ∏ i in range (n+2), (1 + a i):=
begin
  -- sorry
  have explicit_range_2 : range 2 = ([0, 1]).to_finset := rfl,
  -- Fem inducció en n
  induction n with d hd,
  { -- Cas base: n = 0
    simp only [nat.nat_zero_eq_zero, zero_add],

    calc
    1 + ∑ (i : ℕ) in range 2, a i
        = 1 + ( a 0 + a 1) : by { simp [explicit_range_2]}
    ... < 1 + a 0 + a 1 + a 0 * a 1 : by {
      cases h1,
      {
       nlinarith [h1 0, h1 1],
      },
      {
        nlinarith [h1 0, h1 1],
      },
      --cases h1; nlinarith [h1 0, h1 1]}
    }
    ... = (1 + a 0) * (1 + a 1) : by ring
    ... = ∏ (x : ℕ) in range 2, (1 + a x) : by simp [explicit_range_2]
  },
  -- range k = [0, 1, ..., k-1]. Per tant, d+2 no pertany a range (d+2)
  have hd2 : d + 2 ∉ range (d+2) := not_mem_range_self,

  -- range (d+2) és no buit, té d+2 elements
  have nonempty_range : (range (d+2)).nonempty, by finish,

  -- aquesta és l'observació clau
  have hpos : 0 < a (d + 2) * ∑ (x : ℕ) in range (d + 2), a x,
  {
    cases h1,
    work_on_goal 0 {apply mul_pos (h1 (d+2))},
    work_on_goal 1 {apply mul_pos_of_neg_of_neg (h1 (d+2))},
    all_goals
    {
      rw ←sum_const_zero,
      apply sum_lt_sum_of_nonempty nonempty_range (λ i _, h1 i),
    }
  },
  rw [range_succ, sum_insert hd2, finset.prod_insert hd2],
  nlinarith [hpos, h2 (d+2)],
  -- sorry
end

-- exercici 17bis
example (a : ℕ → ℝ) (h1 : (∀ n, 0 < a n) ∨ (∀ n, a n < 0)) (h2 : ∀ n, a n > -1) :
∀ n : ℕ, 2 ≤ n → 1 + ∑ i in range n, a i < ∏ i in range n, (1 + a i):=
begin
  -- sorry
  -- Descripció explícita de range 2 = [0, 1]
  have H2 : range 2 = ([0, 1]).to_finset := rfl,

  -- Fem inducció en n
  apply nat.le_induction,
  {
    -- Cas base: n = 0
    simp only [nat.nat_zero_eq_zero, zero_add, H2],
    calc
    1 + ∑ (x : ℕ) in [0, 1].to_finset, a x
        = 1 + (a 0 + a 1) : by { simp }
    ... < 1 + a 0 + a 1 + a 0 * a 1 : by {cases h1; nlinarith [h1 0, h1 1]}
    ... = (1 + a 0) * (1 + a 1) : by ring
    ... = ∏ (x : ℕ) in [0, 1].to_finset, (1 + a x) : by simp
  },
  intros d htwo hd,
  -- range k = [0, 1, ..., k-1]. Per tant, d no pertany a range d
  have hd2 : d ∉ range d := not_mem_range_self,

  -- range d és no buit, té d elements i d ≥ 2
  have nonempty_range : (range d).nonempty, by finish,

  -- aquesta és l'observació clau
  have hpos : 0 < a d * ∑ (x : ℕ) in range d, a x,
  {
    cases h1,
    work_on_goal 0 {apply mul_pos (h1 d)},
    work_on_goal 1 {apply mul_pos_of_neg_of_neg (h1 d)},
    all_goals
    {
      rw ←sum_const_zero,
      apply sum_lt_sum_of_nonempty nonempty_range (λ i _, h1 i),
    }
  },
  rw [range_succ, sum_insert hd2, finset.prod_insert hd2],
  nlinarith [hpos, h2 d, htwo],
  -- sorry
end


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

! This file was ported from Lean 3 source module algebra.lie.of_associative
! leanprover-community/mathlib commit f0f3d964763ecd0090c9eb3ae0d15871d08781c4
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.Lie.Basic
import Mathbin.Algebra.Lie.Subalgebra
import Mathbin.Algebra.Lie.Submodule
import Mathbin.Algebra.Algebra.Subalgebra.Basic

/-!
# Lie algebras of associative algebras

This file defines the Lie algebra structure that arises on an associative algebra via the ring
commutator.

Since the linear endomorphisms of a Lie algebra form an associative algebra, one can define the
adjoint action as a morphism of Lie algebras from a Lie algebra to its linear endomorphisms. We
make such a definition in this file.

## Main definitions

 * `lie_algebra.of_associative_algebra`
 * `lie_algebra.of_associative_algebra_hom`
 * `lie_module.to_endomorphism`
 * `lie_algebra.ad`
 * `linear_equiv.lie_conj`
 * `alg_equiv.to_lie_equiv`

## Tags

lie algebra, ring commutator, adjoint action
-/

universe u v w w₁ w₂

section OfAssociative

variable {A : Type v} [Ring A]

namespace Ring

/-- The bracket operation for rings is the ring commutator, which captures the extent to which a
ring is commutative. It is identically zero exactly when the ring is commutative. -/
instance (priority := 100) : Bracket A A :=
  ⟨fun x y => x * y - y * x⟩

theorem lie_def (x y : A) : ⁅x, y⁆ = x * y - y * x :=
  rfl
#align ring.lie_def Ring.lie_def

end Ring

theorem commute_iff_lie_eq {x y : A} : Commute x y ↔ ⁅x, y⁆ = 0 :=
  sub_eq_zero.symm
#align commute_iff_lie_eq commute_iff_lie_eq

theorem Commute.lie_eq {x y : A} (h : Commute x y) : ⁅x, y⁆ = 0 :=
  sub_eq_zero_of_eq h
#align commute.lie_eq Commute.lie_eq

namespace LieRing

/-- An associative ring gives rise to a Lie ring by taking the bracket to be the ring commutator. -/
instance (priority := 100) ofAssociativeRing : LieRing A
    where
  add_lie := by
    simp only [Ring.lie_def, right_distrib, left_distrib, sub_eq_add_neg, add_comm, add_left_comm,
      forall_const, eq_self_iff_true, neg_add_rev]
  lie_add := by
    simp only [Ring.lie_def, right_distrib, left_distrib, sub_eq_add_neg, add_comm, add_left_comm,
      forall_const, eq_self_iff_true, neg_add_rev]
  lie_self := by simp only [Ring.lie_def, forall_const, sub_self]
  leibniz_lie x y z := by
    repeat' rw [Ring.lie_def]
    noncomm_ring
#align lie_ring.of_associative_ring LieRing.ofAssociativeRing

theorem of_associative_ring_bracket (x y : A) : ⁅x, y⁆ = x * y - y * x :=
  rfl
#align lie_ring.of_associative_ring_bracket LieRing.of_associative_ring_bracket

@[simp]
theorem lie_apply {α : Type _} (f g : α → A) (a : α) : ⁅f, g⁆ a = ⁅f a, g a⁆ :=
  rfl
#align lie_ring.lie_apply LieRing.lie_apply

end LieRing

section AssociativeModule

variable {M : Type w} [AddCommGroup M] [Module A M]

/-- We can regard a module over an associative ring `A` as a Lie ring module over `A` with Lie
bracket equal to its ring commutator.

Note that this cannot be a global instance because it would create a diamond when `M = A`,
specifically we can build two mathematically-different `has_bracket A A`s:
 1. `@ring.has_bracket A _` which says `⁅a, b⁆ = a * b - b * a`
 2. `(@lie_ring_module.of_associative_module A _ A _ _).to_has_bracket` which says `⁅a, b⁆ = a • b`
    (and thus `⁅a, b⁆ = a * b`)

See note [reducible non-instances] -/
@[reducible]
def LieRingModule.ofAssociativeModule : LieRingModule A M
    where
  bracket := (· • ·)
  add_lie := add_smul
  lie_add := smul_add
  leibniz_lie := by simp [LieRing.of_associative_ring_bracket, sub_smul, mul_smul, sub_add_cancel]
#align lie_ring_module.of_associative_module LieRingModule.ofAssociativeModule

attribute [local instance] LieRingModule.ofAssociativeModule

theorem lie_eq_smul (a : A) (m : M) : ⁅a, m⁆ = a • m :=
  rfl
#align lie_eq_smul lie_eq_smul

end AssociativeModule

section LieAlgebra

variable {R : Type u} [CommRing R] [Algebra R A]

/-- An associative algebra gives rise to a Lie algebra by taking the bracket to be the ring
commutator. -/
instance (priority := 100) LieAlgebra.ofAssociativeAlgebra : LieAlgebra R A
    where lie_smul t x y := by
    rw [LieRing.of_associative_ring_bracket, LieRing.of_associative_ring_bracket,
      Algebra.mul_smul_comm, Algebra.smul_mul_assoc, smul_sub]
#align lie_algebra.of_associative_algebra LieAlgebra.ofAssociativeAlgebra

attribute [local instance] LieRingModule.ofAssociativeModule

section AssociativeRepresentation

variable {M : Type w} [AddCommGroup M] [Module R M] [Module A M] [IsScalarTower R A M]

/-- A representation of an associative algebra `A` is also a representation of `A`, regarded as a
Lie algebra via the ring commutator.

See the comment at `lie_ring_module.of_associative_module` for why the possibility `M = A` means
this cannot be a global instance. -/
def LieModule.ofAssociativeModule : LieModule R A M
    where
  smul_lie := smul_assoc
  lie_smul := smul_algebra_smul_comm
#align lie_module.of_associative_module LieModule.ofAssociativeModule

instance Module.End.lieRingModule : LieRingModule (Module.End R M) M :=
  LieRingModule.ofAssociativeModule
#align module.End.lie_ring_module Module.End.lieRingModule

instance Module.End.lieModule : LieModule R (Module.End R M) M :=
  LieModule.ofAssociativeModule
#align module.End.lie_module Module.End.lieModule

end AssociativeRepresentation

namespace AlgHom

variable {B : Type w} {C : Type w₁} [Ring B] [Ring C] [Algebra R B] [Algebra R C]

variable (f : A →ₐ[R] B) (g : B →ₐ[R] C)

/-- The map `of_associative_algebra` associating a Lie algebra to an associative algebra is
functorial. -/
def toLieHom : A →ₗ⁅R⁆ B :=
  { f.toLinearMap with
    map_lie' := fun x y =>
      show f ⁅x, y⁆ = ⁅f x, f y⁆ by
        simp only [LieRing.of_associative_ring_bracket, AlgHom.map_sub, AlgHom.map_mul] }
#align alg_hom.to_lie_hom AlgHom.toLieHom

instance : Coe (A →ₐ[R] B) (A →ₗ⁅R⁆ B) :=
  ⟨toLieHom⟩

@[simp]
theorem toLieHom_coe : f.toLieHom = ↑f :=
  rfl
#align alg_hom.to_lie_hom_coe AlgHom.toLieHom_coe

@[simp]
theorem coe_to_lieHom : ((f : A →ₗ⁅R⁆ B) : A → B) = f :=
  rfl
#align alg_hom.coe_to_lie_hom AlgHom.coe_to_lieHom

theorem toLieHom_apply (x : A) : f.toLieHom x = f x :=
  rfl
#align alg_hom.to_lie_hom_apply AlgHom.toLieHom_apply

@[simp]
theorem to_lieHom_id : (AlgHom.id R A : A →ₗ⁅R⁆ A) = LieHom.id :=
  rfl
#align alg_hom.to_lie_hom_id AlgHom.to_lieHom_id

@[simp]
theorem to_lieHom_comp : (g.comp f : A →ₗ⁅R⁆ C) = (g : B →ₗ⁅R⁆ C).comp (f : A →ₗ⁅R⁆ B) :=
  rfl
#align alg_hom.to_lie_hom_comp AlgHom.to_lieHom_comp

theorem to_lieHom_injective {f g : A →ₐ[R] B} (h : (f : A →ₗ⁅R⁆ B) = (g : A →ₗ⁅R⁆ B)) : f = g :=
  by
  ext a
  exact LieHom.congr_fun h a
#align alg_hom.to_lie_hom_injective AlgHom.to_lieHom_injective

end AlgHom

end LieAlgebra

end OfAssociative

section AdjointAction

variable (R : Type u) (L : Type v) (M : Type w)

variable [CommRing R] [LieRing L] [LieAlgebra R L] [AddCommGroup M] [Module R M]

variable [LieRingModule L M] [LieModule R L M]

/-- A Lie module yields a Lie algebra morphism into the linear endomorphisms of the module.

See also `lie_module.to_module_hom`. -/
@[simps]
def LieModule.toEndomorphism : L →ₗ⁅R⁆ Module.End R M
    where
  toFun x :=
    { toFun := fun m => ⁅x, m⁆
      map_add' := lie_add x
      map_smul' := fun t => lie_smul t x }
  map_add' x y := by
    ext m
    apply add_lie
  map_smul' t x := by
    ext m
    apply smul_lie
  map_lie' x y := by
    ext m
    apply lie_lie
#align lie_module.to_endomorphism LieModule.toEndomorphism

/-- The adjoint action of a Lie algebra on itself. -/
def LieAlgebra.ad : L →ₗ⁅R⁆ Module.End R L :=
  LieModule.toEndomorphism R L L
#align lie_algebra.ad LieAlgebra.ad

@[simp]
theorem LieAlgebra.ad_apply (x y : L) : LieAlgebra.ad R L x y = ⁅x, y⁆ :=
  rfl
#align lie_algebra.ad_apply LieAlgebra.ad_apply

@[simp]
theorem LieModule.toEndomorphism_module_end :
    LieModule.toEndomorphism R (Module.End R M) M = LieHom.id :=
  by
  ext (g m)
  simp [lie_eq_smul]
#align lie_module.to_endomorphism_module_End LieModule.toEndomorphism_module_end

theorem LieSubalgebra.toEndomorphism_eq (K : LieSubalgebra R L) {x : K} :
    LieModule.toEndomorphism R K M x = LieModule.toEndomorphism R L M x :=
  rfl
#align lie_subalgebra.to_endomorphism_eq LieSubalgebra.toEndomorphism_eq

@[simp]
theorem LieSubalgebra.toEndomorphism_mk (K : LieSubalgebra R L) {x : L} (hx : x ∈ K) :
    LieModule.toEndomorphism R K M ⟨x, hx⟩ = LieModule.toEndomorphism R L M x :=
  rfl
#align lie_subalgebra.to_endomorphism_mk LieSubalgebra.toEndomorphism_mk

variable {R L M}

namespace LieSubmodule

open LieModule

variable {N : LieSubmodule R L M} {x : L}

theorem coe_map_toEndomorphism_le :
    (N : Submodule R M).map (LieModule.toEndomorphism R L M x) ≤ N :=
  by
  rintro n ⟨m, hm, rfl⟩
  exact N.lie_mem hm
#align lie_submodule.coe_map_to_endomorphism_le LieSubmodule.coe_map_toEndomorphism_le

variable (N x)

theorem toEndomorphism_comp_subtype_mem (m : M) (hm : m ∈ (N : Submodule R M)) :
    (toEndomorphism R L M x).comp (N : Submodule R M).Subtype ⟨m, hm⟩ ∈ (N : Submodule R M) := by
  simpa using N.lie_mem hm
#align lie_submodule.to_endomorphism_comp_subtype_mem LieSubmodule.toEndomorphism_comp_subtype_mem

@[simp]
theorem toEndomorphism_restrict_eq_toEndomorphism (h := N.toEndomorphism_comp_subtype_mem x) :
    (toEndomorphism R L M x).restrict h = toEndomorphism R L N x :=
  by
  ext
  simp [LinearMap.restrict_apply]
#align lie_submodule.to_endomorphism_restrict_eq_to_endomorphism LieSubmodule.toEndomorphism_restrict_eq_toEndomorphism

end LieSubmodule

open LieAlgebra

theorem LieAlgebra.ad_eq_lmul_left_sub_lmul_right (A : Type v) [Ring A] [Algebra R A] :
    (ad R A : A → Module.End R A) = LinearMap.mulLeft R - LinearMap.mulRight R :=
  by
  ext (a b)
  simp [LieRing.of_associative_ring_bracket]
#align lie_algebra.ad_eq_lmul_left_sub_lmul_right LieAlgebra.ad_eq_lmul_left_sub_lmul_right

theorem LieSubalgebra.ad_comp_incl_eq (K : LieSubalgebra R L) (x : K) :
    (ad R L ↑x).comp (K.incl : K →ₗ[R] L) = (K.incl : K →ₗ[R] L).comp (ad R K x) :=
  by
  ext y
  simp only [ad_apply, LieHom.coe_toLinearMap, LieSubalgebra.coe_incl, LinearMap.coe_comp,
    LieSubalgebra.coe_bracket, Function.comp_apply]
#align lie_subalgebra.ad_comp_incl_eq LieSubalgebra.ad_comp_incl_eq

end AdjointAction

/-- A subalgebra of an associative algebra is a Lie subalgebra of the associated Lie algebra. -/
def lieSubalgebraOfSubalgebra (R : Type u) [CommRing R] (A : Type v) [Ring A] [Algebra R A]
    (A' : Subalgebra R A) : LieSubalgebra R A :=
  { A'.toSubmodule with
    lie_mem' := fun x y hx hy => by
      change ⁅x, y⁆ ∈ A'
      change x ∈ A' at hx
      change y ∈ A' at hy
      rw [LieRing.of_associative_ring_bracket]
      have hxy := A'.mul_mem hx hy
      have hyx := A'.mul_mem hy hx
      exact Submodule.sub_mem A'.to_submodule hxy hyx }
#align lie_subalgebra_of_subalgebra lieSubalgebraOfSubalgebra

namespace LinearEquiv

variable {R : Type u} {M₁ : Type v} {M₂ : Type w}

variable [CommRing R] [AddCommGroup M₁] [Module R M₁] [AddCommGroup M₂] [Module R M₂]

variable (e : M₁ ≃ₗ[R] M₂)

/-- A linear equivalence of two modules induces a Lie algebra equivalence of their endomorphisms. -/
def lieConj : Module.End R M₁ ≃ₗ⁅R⁆ Module.End R M₂ :=
  { e.conj with
    map_lie' := fun f g =>
      show e.conj ⁅f, g⁆ = ⁅e.conj f, e.conj g⁆ by
        simp only [LieRing.of_associative_ring_bracket, LinearMap.mul_eq_comp, e.conj_comp,
          LinearEquiv.map_sub] }
#align linear_equiv.lie_conj LinearEquiv.lieConj

@[simp]
theorem lieConj_apply (f : Module.End R M₁) : e.lieConj f = e.conj f :=
  rfl
#align linear_equiv.lie_conj_apply LinearEquiv.lieConj_apply

@[simp]
theorem lieConj_symm : e.lieConj.symm = e.symm.lieConj :=
  rfl
#align linear_equiv.lie_conj_symm LinearEquiv.lieConj_symm

end LinearEquiv

namespace AlgEquiv

variable {R : Type u} {A₁ : Type v} {A₂ : Type w}

variable [CommRing R] [Ring A₁] [Ring A₂] [Algebra R A₁] [Algebra R A₂]

variable (e : A₁ ≃ₐ[R] A₂)

/-- An equivalence of associative algebras is an equivalence of associated Lie algebras. -/
def toLieEquiv : A₁ ≃ₗ⁅R⁆ A₂ :=
  { e.toLinearEquiv with
    toFun := e.toFun
    map_lie' := fun x y => by simp [LieRing.of_associative_ring_bracket] }
#align alg_equiv.to_lie_equiv AlgEquiv.toLieEquiv

@[simp]
theorem toLieEquiv_apply (x : A₁) : e.toLieEquiv x = e x :=
  rfl
#align alg_equiv.to_lie_equiv_apply AlgEquiv.toLieEquiv_apply

@[simp]
theorem toLieEquiv_symm_apply (x : A₂) : e.toLieEquiv.symm x = e.symm x :=
  rfl
#align alg_equiv.to_lie_equiv_symm_apply AlgEquiv.toLieEquiv_symm_apply

end AlgEquiv


/-
Copyright (c) 2021 Alex J. Best. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Alex J. Best
-/
import algebra.pointwise
import algebra.module.pi

/-!
# Pointwise actions on sets in Pi types

This file contains lemmas about pointwise actions on sets in Pi types.

## Tags

set multiplication, set addition, pointwise addition, pointwise multiplication, pi

-/

open_locale pointwise
open set

variables {K ι : Type*} {R : ι → Type*}

@[to_additive]
lemma smul_pi_subset [∀ i, has_scalar K (R i)] (r : K) (s : set ι) (t : Π i, set (R i)) :
  r • pi s t ⊆ pi s (r • t) :=
begin
  rintros x ⟨y, h, rfl⟩ i hi,
  exact smul_mem_smul_set (h i hi),
end

@[to_additive]
lemma smul_univ_pi [∀ i, has_scalar K (R i)] (r : K) (t : Π i, set (R i)) :
  r • pi (univ : set ι) t = pi (univ : set ι) (r • t) :=
subset.antisymm (smul_pi_subset _ _ _) $ λ x h, begin
  refine ⟨λ i, classical.some (h i $ set.mem_univ _), λ i hi, _, funext $ λ i, _⟩,
  { exact (classical.some_spec (h i _)).left, },
  { exact (classical.some_spec (h i _)).right, },
end

@[to_additive]
lemma smul_pi [group K] [∀ i, mul_action K (R i)] (r : K) (S : set ι) (t : Π i, set (R i)) :
  r • S.pi t = S.pi (r • t) :=
subset.antisymm (smul_pi_subset _ _ _) $ λ x h,
  ⟨r⁻¹ • x, λ i hiS, mem_smul_set_iff_inv_smul_mem.mp (h i hiS), smul_inv_smul _ _⟩

lemma smul_pi₀ [group_with_zero K] [∀ i, mul_action K (R i)] {r : K} (S : set ι)
  (t : Π i, set (R i)) (hr : r ≠ 0) : r • S.pi t = S.pi (r • t) :=
smul_pi (units.mk0 r hr) S t