/-
Copyright (c) 2021 Yury Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury Kudryashov
-/
import analysis.box_integral.partition.filter
import analysis.box_integral.partition.measure
import topology.uniform_space.compact

/-!
# Integrals of Riemann, Henstock-Kurzweil, and McShane

In this file we define the integral of a function over a box in `ℝⁿ. The same definition works for
Riemann, Henstock-Kurzweil, and McShane integrals.

As usual, we represent `ℝⁿ` as the type of functions `ι → ℝ` for some finite type `ι`. A rectangular
box `(l, u]` in `ℝⁿ` is defined to be the set `{x : ι → ℝ | ∀ i, l i < x i ∧ x i ≤ u i}`, see
`box_integral.box`.

Let `vol` be a box-additive function on boxes in `ℝⁿ` with codomain `E →L[ℝ] F`. Given a function
`f : ℝⁿ → E`, a box `I` and a tagged partition `π` of this box, the *integral sum* of `f` over `π`
with respect to the volume `vol` is the sum of `vol J (f (π.tag J))` over all boxes of `π`. Here
`π.tag J` is the point (tag) in `ℝⁿ` associated with the box `J`.

The integral is defined as the limit of integral sums along a filter. Different filters correspond
to different integration theories. In order to avoid code duplication, all our definitions and
theorems take an argument `l : box_integral.integration_params`. This is a type that holds three
boolean values, and encodes eight filters including those corresponding to Riemann,
Henstock-Kurzweil, and McShane integrals.

Following the design of infinite sums (see `has_sum` and `tsum`), we define a predicate
`box_integral.has_integral` and a function `box_integral.integral` that returns a vector satisfying
the predicate or zero if the function is not integrable.

Then we prove some basic properties of box integrals (linearity, a formula for the integral of a
constant). We also prove a version of the Henstock-Sacks inequality (see
`box_integral.integrable.dist_integral_sum_le_of_mem_base_set` and
`box_integral.integrable.dist_integral_sum_sum_integral_le_of_mem_base_set_of_Union_eq`), prove
integrability of continuous functions, and provide a criterion for integrability w.r.t. a
non-Riemann filter (e.g., Henstock-Kurzweil and McShane).

## Notation

- `ℝⁿ`: local notation for `ι → ℝ`

## Tags

integral
-/

open_locale big_operators classical topology nnreal filter uniformity box_integral
open set finset function filter metric box_integral.integration_params

noncomputable theory

namespace box_integral

universes u v w

variables {ι : Type u} {E : Type v} {F : Type w} [normed_add_comm_group E] [normed_space ℝ E]
  [normed_add_comm_group F] [normed_space ℝ F] {I J : box ι} {π : tagged_prepartition I}

open tagged_prepartition

local notation `ℝⁿ` := ι → ℝ

/-!
### Integral sum and its basic properties
-/

/-- The integral sum of `f : ℝⁿ → E` over a tagged prepartition `π` w.r.t. box-additive volume `vol`
with codomain `E →L[ℝ] F` is the sum of `vol J (f (π.tag J))` over all boxes of `π`. -/
def integral_sum (f : ℝⁿ → E) (vol : ι →ᵇᵃ (E →L[ℝ] F)) (π : tagged_prepartition I) : F :=
∑ J in π.boxes, vol J (f (π.tag J))

lemma integral_sum_bUnion_tagged (f : ℝⁿ → E) (vol : ι →ᵇᵃ (E →L[ℝ] F)) (π : prepartition I)
  (πi : Π J, tagged_prepartition J) :
  integral_sum f vol (π.bUnion_tagged πi) = ∑ J in π.boxes, integral_sum f vol (πi J) :=
begin
  refine (π.sum_bUnion_boxes _ _).trans (sum_congr rfl $ λ J hJ, sum_congr rfl $ λ J' hJ', _),
  rw π.tag_bUnion_tagged hJ hJ'
end

lemma integral_sum_bUnion_partition (f : ℝⁿ → E) (vol : ι →ᵇᵃ (E →L[ℝ] F))
  (π : tagged_prepartition I) (πi : Π J, prepartition J) (hπi : ∀ J ∈ π, (πi J).is_partition) :
  integral_sum f vol (π.bUnion_prepartition πi) = integral_sum f vol π :=
begin
  refine (π.to_prepartition.sum_bUnion_boxes _ _).trans (sum_congr rfl $ λ J hJ, _),
  calc ∑ J' in (πi J).boxes, vol J' (f (π.tag $ π.to_prepartition.bUnion_index πi J'))
      = ∑ J' in (πi J).boxes, vol J' (f (π.tag J)) :
    sum_congr rfl (λ J' hJ', by rw [prepartition.bUnion_index_of_mem _ hJ hJ'])
  ... = vol J (f (π.tag J)) :
    (vol.map ⟨λ g : E →L[ℝ] F, g (f (π.tag J)), rfl, λ _ _, rfl⟩).sum_partition_boxes
      le_top (hπi J hJ)
end

lemma integral_sum_inf_partition (f : ℝⁿ → E) (vol : ι →ᵇᵃ (E →L[ℝ] F))
  (π : tagged_prepartition I) {π' : prepartition I} (h : π'.is_partition) :
  integral_sum f vol (π.inf_prepartition π') = integral_sum f vol π :=
integral_sum_bUnion_partition f vol  π _ $ λ J hJ, h.restrict (prepartition.le_of_mem _ hJ)

lemma integral_sum_fiberwise {α} (g : box ι → α) (f : ℝⁿ → E)
  (vol : ι →ᵇᵃ (E →L[ℝ] F)) (π : tagged_prepartition I) :
  ∑ y in π.boxes.image g, integral_sum f vol (π.filter (λ x, g x = y)) = integral_sum f vol π :=
π.to_prepartition.sum_fiberwise g (λ J, vol J (f $ π.tag J))

lemma integral_sum_sub_partitions (f : ℝⁿ → E) (vol : ι →ᵇᵃ (E →L[ℝ] F))
  {π₁ π₂ : tagged_prepartition I} (h₁ : π₁.is_partition) (h₂ : π₂.is_partition) :
  integral_sum f vol π₁ - integral_sum f vol π₂ =
    ∑ J in (π₁.to_prepartition ⊓ π₂.to_prepartition).boxes,
      (vol J (f $ (π₁.inf_prepartition π₂.to_prepartition).tag J) -
        vol J (f $ (π₂.inf_prepartition π₁.to_prepartition).tag J)) :=
begin
  rw [← integral_sum_inf_partition f vol π₁ h₂,
    ← integral_sum_inf_partition f vol π₂ h₁, integral_sum, integral_sum,
    finset.sum_sub_distrib],
  simp only [inf_prepartition_to_prepartition, _root_.inf_comm]
end

@[simp] lemma integral_sum_disj_union (f : ℝⁿ → E) (vol : ι →ᵇᵃ (E →L[ℝ] F))
  {π₁ π₂ : tagged_prepartition I} (h : disjoint π₁.Union π₂.Union) :
  integral_sum f vol (π₁.disj_union π₂ h) = integral_sum f vol π₁ + integral_sum f vol π₂ :=
begin
  refine (prepartition.sum_disj_union_boxes h _).trans
    (congr_arg2 (+) (sum_congr rfl $ λ J hJ, _) (sum_congr rfl $ λ J hJ, _)),
  { rw disj_union_tag_of_mem_left _ hJ },
  { rw disj_union_tag_of_mem_right _ hJ }
end

@[simp] lemma integral_sum_add (f g : ℝⁿ → E) (vol : ι →ᵇᵃ (E →L[ℝ] F))
  (π : tagged_prepartition I) :
  integral_sum (f + g) vol π = integral_sum f vol π + integral_sum g vol π :=
by simp only [integral_sum, pi.add_apply, (vol _).map_add, finset.sum_add_distrib]

@[simp] lemma integral_sum_neg (f : ℝⁿ → E) (vol : ι →ᵇᵃ (E →L[ℝ] F))
  (π : tagged_prepartition I) :
  integral_sum (-f) vol π = -integral_sum f vol π :=
by simp only [integral_sum, pi.neg_apply, (vol _).map_neg, finset.sum_neg_distrib]

@[simp] lemma integral_sum_smul (c : ℝ) (f : ℝⁿ → E) (vol : ι →ᵇᵃ (E →L[ℝ] F))
  (π : tagged_prepartition I) :
  integral_sum (c • f) vol π = c • integral_sum f vol π :=
by simp only [integral_sum, finset.smul_sum, pi.smul_apply, continuous_linear_map.map_smul]

variables [fintype ι]

/-!
### Basic integrability theory
-/

/-- The predicate `has_integral I l f vol y` says that `y` is the integral of `f` over `I` along `l`
w.r.t. volume `vol`. This means that integral sums of `f` tend to `𝓝 y` along
`box_integral.integration_params.to_filter_Union I ⊤`. -/
def has_integral (I : box ι) (l : integration_params) (f : ℝⁿ → E) (vol : ι →ᵇᵃ (E →L[ℝ] F))
  (y : F) : Prop :=
tendsto (integral_sum f vol) (l.to_filter_Union I ⊤) (𝓝 y)

/-- A function is integrable if there exists a vector that satisfies the `has_integral`
predicate. -/
def integrable (I : box ι) (l : integration_params) (f : ℝⁿ → E) (vol : ι →ᵇᵃ (E →L[ℝ] F)) :=
∃ y, has_integral I l f vol y

/-- The integral of a function `f` over a box `I` along a filter `l` w.r.t. a volume `vol`.  Returns
zero on non-integrable functions. -/
def integral (I : box ι) (l : integration_params) (f : ℝⁿ → E) (vol : ι →ᵇᵃ (E →L[ℝ] F)) :=
if h : integrable I l f vol then h.some else 0

variables {l : integration_params} {f g : ℝⁿ → E} {vol : ι →ᵇᵃ (E →L[ℝ] F)} {y y' : F}

/-- Reinterpret `box_integral.has_integral` as `filter.tendsto`, e.g., dot-notation theorems
that are shadowed in the `box_integral.has_integral` namespace. -/
lemma has_integral.tendsto (h : has_integral I l f vol y) :
  tendsto (integral_sum f vol) (l.to_filter_Union I ⊤) (𝓝 y) := h

/-- The `ε`-`δ` definition of `box_integral.has_integral`. -/
lemma has_integral_iff : has_integral I l f vol y ↔
  ∀ ε > (0 : ℝ), ∃ r : ℝ≥0 → ℝⁿ → Ioi (0 : ℝ), (∀ c, l.r_cond (r c)) ∧
    ∀ c π, l.mem_base_set I c (r c) π → is_partition π → dist (integral_sum f vol π) y ≤ ε :=
((l.has_basis_to_filter_Union_top I).tendsto_iff nhds_basis_closed_ball).trans $
  by simp [@forall_swap ℝ≥0 (tagged_prepartition I)]

/-- Quite often it is more natural to prove an estimate of the form `a * ε`, not `ε` in the RHS of
`box_integral.has_integral_iff`, so we provide this auxiliary lemma.  -/
lemma has_integral_of_mul (a : ℝ) (h : ∀ ε : ℝ, 0 < ε →
  ∃ r: ℝ≥0 → ℝⁿ → Ioi (0 : ℝ), (∀ c, l.r_cond (r c)) ∧ ∀ c π, l.mem_base_set I c (r c) π →
    is_partition π → dist (integral_sum f vol π) y ≤ a * ε) :
  has_integral I l f vol y :=
begin
  refine has_integral_iff.2 (λ ε hε, _),
  rcases exists_pos_mul_lt hε a with ⟨ε', hε', ha⟩,
  rcases h ε' hε' with ⟨r, hr, H⟩,
  exact ⟨r, hr, λ c π hπ hπp, (H c π hπ hπp).trans ha.le⟩
end

lemma integrable_iff_cauchy [complete_space F] :
  integrable I l f vol ↔ cauchy ((l.to_filter_Union I ⊤).map (integral_sum f vol)) :=
cauchy_map_iff_exists_tendsto.symm

/-- In a complete space, a function is integrable if and only if its integral sums form a Cauchy
net. Here we restate this fact in terms of `∀ ε > 0, ∃ r, ...`. -/
lemma integrable_iff_cauchy_basis [complete_space F] :
  integrable I l f vol ↔ ∀ ε > (0 : ℝ), ∃ r : ℝ≥0 → ℝⁿ → Ioi (0 : ℝ), (∀ c, l.r_cond (r c)) ∧
    ∀ c₁ c₂ π₁ π₂, l.mem_base_set I c₁ (r c₁) π₁ → π₁.is_partition → l.mem_base_set I c₂ (r c₂) π₂ →
      π₂.is_partition → dist (integral_sum f vol π₁) (integral_sum f vol π₂) ≤ ε :=
begin
  rw [integrable_iff_cauchy, cauchy_map_iff',
    (l.has_basis_to_filter_Union_top _).prod_self.tendsto_iff uniformity_basis_dist_le],
  refine forall₂_congr (λ ε ε0, exists_congr $ λ r, _),
  simp only [exists_prop, prod.forall, set.mem_Union, exists_imp_distrib,
    prod_mk_mem_set_prod_eq, and_imp, mem_inter_iff, mem_set_of_eq],
  exact and_congr iff.rfl ⟨λ H c₁ c₂ π₁ π₂ h₁ hU₁ h₂ hU₂, H π₁ π₂ c₁ h₁ hU₁ c₂ h₂ hU₂,
    λ H π₁ π₂ c₁ h₁ hU₁ c₂ h₂ hU₂, H c₁ c₂ π₁ π₂ h₁ hU₁ h₂ hU₂⟩
end

lemma has_integral.mono {l₁ l₂ : integration_params} (h : has_integral I l₁ f vol y)
  (hl : l₂ ≤ l₁) : has_integral I l₂ f vol y :=
h.mono_left $ integration_params.to_filter_Union_mono _ hl _

protected lemma integrable.has_integral (h : integrable I l f vol) :
  has_integral I l f vol (integral I l f vol) :=
by { rw [integral, dif_pos h], exact classical.some_spec h }

lemma integrable.mono {l'} (h : integrable I l f vol) (hle : l' ≤ l) : integrable I l' f vol :=
⟨_, h.has_integral.mono hle⟩

lemma has_integral.unique (h : has_integral I l f vol y) (h' : has_integral I l f vol y') :
  y = y' :=
tendsto_nhds_unique h h'

lemma has_integral.integrable (h : has_integral I l f vol y) : integrable I l f vol := ⟨_, h⟩

lemma has_integral.integral_eq (h : has_integral I l f vol y) :
  integral I l f vol = y :=
h.integrable.has_integral.unique h

lemma has_integral.add (h : has_integral I l f vol y) (h' : has_integral I l g vol y') :
  has_integral I l (f + g) vol (y + y') :=
by simpa only [has_integral, ← integral_sum_add] using h.add h'

lemma integrable.add (hf : integrable I l f vol) (hg : integrable I l g vol) :
  integrable I l (f + g) vol :=
(hf.has_integral.add hg.has_integral).integrable

lemma integral_add (hf : integrable I l f vol) (hg : integrable I l g vol) :
  integral I l (f + g) vol = integral I l f vol + integral I l g vol :=
(hf.has_integral.add hg.has_integral).integral_eq

lemma has_integral.neg (hf : has_integral I l f vol y) : has_integral I l (-f) vol (-y) :=
by simpa only [has_integral, ← integral_sum_neg] using hf.neg

lemma integrable.neg (hf : integrable I l f vol) : integrable I l (-f) vol :=
hf.has_integral.neg.integrable

lemma integrable.of_neg (hf : integrable I l (-f) vol) : integrable I l f vol := neg_neg f ▸ hf.neg

@[simp] lemma integrable_neg : integrable I l (-f) vol ↔ integrable I l f vol :=
⟨λ h, h.of_neg, λ h, h.neg⟩

@[simp] lemma integral_neg : integral I l (-f) vol = -integral I l f vol :=
if h : integrable I l f vol then h.has_integral.neg.integral_eq
else by rw [integral, integral, dif_neg h, dif_neg (mt integrable.of_neg h), neg_zero]

lemma has_integral.sub (h : has_integral I l f vol y) (h' : has_integral I l g vol y') :
  has_integral I l (f - g) vol (y - y') :=
by simpa only [sub_eq_add_neg] using h.add h'.neg

lemma integrable.sub (hf : integrable I l f vol) (hg : integrable I l g vol) :
  integrable I l (f - g) vol :=
(hf.has_integral.sub hg.has_integral).integrable

lemma integral_sub (hf : integrable I l f vol) (hg : integrable I l g vol) :
  integral I l (f - g) vol = integral I l f vol - integral I l g vol :=
(hf.has_integral.sub hg.has_integral).integral_eq

lemma has_integral_const (c : E) : has_integral I l (λ _, c) vol (vol I c) :=
tendsto_const_nhds.congr' $ (l.eventually_is_partition I).mono $ λ π hπ,
  ((vol.map ⟨λ g : E →L[ℝ] F, g c, rfl, λ _ _, rfl⟩).sum_partition_boxes le_top hπ).symm

@[simp] lemma integral_const (c : E) : integral I l (λ _, c) vol = vol I c :=
(has_integral_const c).integral_eq

lemma integrable_const (c : E) : integrable I l (λ _, c) vol :=
⟨_, has_integral_const c⟩

lemma has_integral_zero : has_integral I l (λ _, (0:E)) vol 0 :=
by simpa only [← (vol I).map_zero] using has_integral_const (0 : E)

lemma integrable_zero : integrable I l (λ _, (0:E)) vol := ⟨0, has_integral_zero⟩

lemma integral_zero : integral I l (λ _, (0:E)) vol = 0 := has_integral_zero.integral_eq

lemma has_integral_sum {α : Type*} {s : finset α} {f : α → ℝⁿ → E} {g : α → F}
  (h : ∀ i ∈ s, has_integral I l (f i) vol (g i)) :
  has_integral I l (λ x, ∑ i in s, f i x) vol (∑ i in s, g i) :=
begin
  induction s using finset.induction_on with a s ha ihs, { simp [has_integral_zero] },
  simp only [finset.sum_insert ha], rw finset.forall_mem_insert at h,
  exact h.1.add (ihs h.2)
end

lemma has_integral.smul (hf : has_integral I l f vol y) (c : ℝ) :
  has_integral I l (c • f) vol (c • y) :=
by simpa only [has_integral, ← integral_sum_smul]
  using (tendsto_const_nhds : tendsto _ _ (𝓝 c)).smul hf

lemma integrable.smul (hf : integrable I l f vol) (c : ℝ) :
  integrable I l (c • f) vol :=
(hf.has_integral.smul c).integrable

lemma integrable.of_smul {c : ℝ} (hf : integrable I l (c • f) vol) (hc : c ≠ 0) :
  integrable I l f vol :=
by { convert hf.smul c⁻¹, ext x, simp only [pi.smul_apply, inv_smul_smul₀ hc] }

@[simp] lemma integral_smul (c : ℝ) : integral I l (λ x, c • f x) vol = c • integral I l f vol :=
begin
  rcases eq_or_ne c 0 with rfl | hc, { simp only [zero_smul, integral_zero] },
  by_cases hf : integrable I l f vol,
  { exact (hf.has_integral.smul c).integral_eq },
  { have : ¬integrable I l (λ x, c • f x) vol, from mt (λ h, h.of_smul hc) hf,
    rw [integral, integral, dif_neg hf, dif_neg this, smul_zero] }
end

open measure_theory

/-- The integral of a nonnegative function w.r.t. a volume generated by a locally-finite measure is
nonnegative. -/
lemma integral_nonneg {g : ℝⁿ → ℝ} (hg : ∀ x ∈ I.Icc, 0 ≤ g x)
  (μ : measure ℝⁿ) [is_locally_finite_measure μ] :
  0 ≤ integral I l g μ.to_box_additive.to_smul :=
begin
  by_cases hgi : integrable I l g μ.to_box_additive.to_smul,
  { refine ge_of_tendsto' hgi.has_integral (λ π, sum_nonneg $ λ J hJ, _),
    exact mul_nonneg ennreal.to_real_nonneg (hg _ $ π.tag_mem_Icc _) },
  { rw [integral, dif_neg hgi] }
end

/-- If `‖f x‖ ≤ g x` on `[l, u]` and `g` is integrable, then the norm of the integral of `f` is less
than or equal to the integral of `g`. -/
lemma norm_integral_le_of_norm_le {g : ℝⁿ → ℝ} (hle : ∀ x ∈ I.Icc, ‖f x‖ ≤ g x)
  (μ : measure ℝⁿ) [is_locally_finite_measure μ]
  (hg : integrable I l g μ.to_box_additive.to_smul) :
  ‖(integral I l f μ.to_box_additive.to_smul : E)‖ ≤
    integral I l g μ.to_box_additive.to_smul :=
begin
  by_cases hfi : integrable.{u v v} I l f μ.to_box_additive.to_smul,
  { refine le_of_tendsto_of_tendsto' hfi.has_integral.norm hg.has_integral (λ π, _),
    refine norm_sum_le_of_le _ (λ J hJ, _),
    simp only [box_additive_map.to_smul_apply, norm_smul, smul_eq_mul, real.norm_eq_abs,
      μ.to_box_additive_apply, abs_of_nonneg ennreal.to_real_nonneg],
    exact mul_le_mul_of_nonneg_left (hle _ $ π.tag_mem_Icc _) ennreal.to_real_nonneg },
  { rw [integral, dif_neg hfi, norm_zero],
    exact integral_nonneg (λ x hx, (norm_nonneg _).trans (hle x hx)) μ }
end

lemma norm_integral_le_of_le_const {c : ℝ} (hc : ∀ x ∈ I.Icc, ‖f x‖ ≤ c)
  (μ : measure ℝⁿ) [is_locally_finite_measure μ] :
  ‖(integral I l f μ.to_box_additive.to_smul : E)‖ ≤ (μ I).to_real * c :=
by simpa only [integral_const]
  using norm_integral_le_of_norm_le hc μ (integrable_const c)

/-!
# Henstock-Sacks inequality and integrability on subboxes

Henstock-Sacks inequality for Henstock-Kurzweil integral says the following. Let `f` be a function
integrable on a box `I`; let `r : ℝⁿ → (0, ∞)` be a function such that for any tagged partition of
`I` subordinate to `r`, the integral sum over this partition is `ε`-close to the integral. Then for
any tagged prepartition (i.e. a finite collections of pairwise disjoint subboxes of `I` with tagged
points) `π`, the integral sum over `π` differs from the integral of `f` over the part of `I` covered
by `π` by at most `ε`. The actual statement in the library is a bit more complicated to make it work
for any `box_integral.integration_params`. We formalize several versions of this inequality in
`box_integral.integrable.dist_integral_sum_le_of_mem_base_set`,
`box_integral.integrable.dist_integral_sum_sum_integral_le_of_mem_base_set_of_Union_eq`, and
`box_integral.integrable.dist_integral_sum_sum_integral_le_of_mem_base_set`.

Instead of using predicate assumptions on `r`, we define
`box_integral.integrable.convergence_r (h : integrable I l f vol) (ε : ℝ) (c : ℝ≥0) : ℝⁿ → (0, ∞)`
to be a function `r` such that

- if `l.bRiemann`, then `r` is a constant;
- if `ε > 0`, then for any tagged partition `π` of `I` subordinate to `r` (more precisely,
  satisfying the predicate `l.mem_base_set I c r`), the integral sum of `f` over `π` differs from
  the integral of `f` over `I` by at most `ε`.

The proof is mostly based on
[Russel A. Gordon, *The integrals of Lebesgue, Denjoy, Perron, and Henstock*][Gordon55].

-/

namespace integrable

/-- If `ε > 0`, then `box_integral.integrable.convergence_r` is a function `r : ℝ≥0 → ℝⁿ → (0, ∞)`
such that for every `c : ℝ≥0`, for every tagged partition `π` subordinate to `r` (and satisfying
additional distortion estimates if `box_integral.integration_params.bDistortion l = tt`), the
corresponding integral sum is `ε`-close to the integral.

If `box.integral.integration_params.bRiemann = tt`, then `r c x` does not depend on `x`. If `ε ≤ 0`,
then we use `r c x = 1`.  -/
def convergence_r (h : integrable I l f vol) (ε : ℝ) : ℝ≥0 → ℝⁿ → Ioi (0 : ℝ) :=
if hε : 0 < ε then (has_integral_iff.1 h.has_integral ε hε).some
else λ _ _, ⟨1, set.mem_Ioi.2 zero_lt_one⟩

variables {c c₁ c₂ : ℝ≥0} {ε ε₁ ε₂ : ℝ} {π₁ π₂ : tagged_prepartition I}

lemma convergence_r_cond (h : integrable I l f vol) (ε : ℝ) (c : ℝ≥0) :
  l.r_cond (h.convergence_r ε c) :=
begin
  rw convergence_r, split_ifs with h₀,
  exacts [(has_integral_iff.1 h.has_integral ε h₀).some_spec.1 _, λ _ x, rfl]
end

lemma dist_integral_sum_integral_le_of_mem_base_set (h : integrable I l f vol) (h₀ : 0 < ε)
  (hπ : l.mem_base_set I c (h.convergence_r ε c) π) (hπp : π.is_partition) :
  dist (integral_sum f vol π) (integral I l f vol) ≤ ε :=
begin
  rw [convergence_r, dif_pos h₀] at hπ,
  exact (has_integral_iff.1 h.has_integral ε h₀).some_spec.2 c _ hπ hπp
end

/-- **Henstock-Sacks inequality**. Let `r₁ r₂ : ℝⁿ → (0, ∞)` be function such that for any tagged
*partition* of `I` subordinate to `rₖ`, `k=1,2`, the integral sum of `f` over this partition differs
from the integral of `f` by at most `εₖ`. Then for any two tagged *prepartition* `π₁ π₂` subordinate
to `r₁` and `r₂` respectively and covering the same part of `I`, the integral sums of `f` over these
prepartitions differ from each other by at most `ε₁ + ε₂`.

The actual statement

- uses `box_integral.integrable.convergence_r` instead of a predicate assumption on `r`;
- uses `box_integral.integration_params.mem_base_set` instead of “subordinate to `r`” to
  account for additional requirements like being a Henstock partition or having a bounded
  distortion.

See also `box_integral.integrable.dist_integral_sum_sum_integral_le_of_mem_base_set_of_Union_eq` and
`box_integral.integrable.dist_integral_sum_sum_integral_le_of_mem_base_set`.
-/
lemma dist_integral_sum_le_of_mem_base_set (h : integrable I l f vol)
  (hpos₁ : 0 < ε₁) (hpos₂ : 0 < ε₂) (h₁ : l.mem_base_set I c₁ (h.convergence_r ε₁ c₁) π₁)
  (h₂ : l.mem_base_set I c₂ (h.convergence_r ε₂ c₂) π₂) (HU : π₁.Union = π₂.Union) :
  dist (integral_sum f vol π₁) (integral_sum f vol π₂) ≤ ε₁ + ε₂ :=
begin
  rcases h₁.exists_common_compl h₂ HU with ⟨π, hπU, hπc₁, hπc₂⟩,
  set r : ℝⁿ → Ioi (0 : ℝ) := λ x, min (h.convergence_r ε₁ c₁ x) (h.convergence_r ε₂ c₂ x),
  have hr : l.r_cond r := (h.convergence_r_cond _ c₁).min (h.convergence_r_cond _ c₂),
  set πr := π.to_subordinate r,
  have H₁ : dist (integral_sum f vol (π₁.union_compl_to_subordinate π hπU r))
    (integral I l f vol) ≤ ε₁,
  from h.dist_integral_sum_integral_le_of_mem_base_set hpos₁
    (h₁.union_compl_to_subordinate (λ _ _, min_le_left _ _) hπU hπc₁)
    (is_partition_union_compl_to_subordinate _ _ _ _),
  rw HU at hπU,
  have H₂ : dist (integral_sum f vol (π₂.union_compl_to_subordinate π hπU r))
    (integral I l f vol) ≤ ε₂,
  from h.dist_integral_sum_integral_le_of_mem_base_set hpos₂
    (h₂.union_compl_to_subordinate (λ _ _, min_le_right _ _) hπU hπc₂)
    (is_partition_union_compl_to_subordinate _ _ _ _),
  simpa [union_compl_to_subordinate] using (dist_triangle_right _ _ _).trans (add_le_add H₁ H₂)
end

/-- If `f` is integrable on `I` along `l`, then for two sufficiently fine tagged prepartitions
(in the sense of the filter `box_integral.integration_params.to_filter l I`) such that they cover
the same part of `I`, the integral sums of `f` over `π₁` and `π₂` are very close to each other.  -/
lemma tendsto_integral_sum_to_filter_prod_self_inf_Union_eq_uniformity (h : integrable I l f vol) :
  tendsto
    (λ π : tagged_prepartition I × tagged_prepartition I,
      (integral_sum f vol π.1, integral_sum f vol π.2))
    ((l.to_filter I ×ᶠ l.to_filter I) ⊓ 𝓟 {π | π.1.Union = π.2.Union}) (𝓤 F) :=
begin
  refine (((l.has_basis_to_filter I).prod_self.inf_principal _).tendsto_iff
    uniformity_basis_dist_le).2 (λ ε ε0, _),
  replace ε0 := half_pos ε0,
  use [h.convergence_r (ε / 2), h.convergence_r_cond (ε / 2)], rintro ⟨π₁, π₂⟩ ⟨⟨h₁, h₂⟩, hU⟩,
  rw ← add_halves ε,
  exact h.dist_integral_sum_le_of_mem_base_set ε0 ε0 h₁.some_spec h₂.some_spec hU
end

/-- If `f` is integrable on a box `I` along `l`, then for any fixed subset `s` of `I` that can be
represented as a finite union of boxes, the integral sums of `f` over tagged prepartitions that
cover exactly `s` form a Cauchy “sequence” along `l`. -/
lemma cauchy_map_integral_sum_to_filter_Union (h : integrable I l f vol) (π₀ : prepartition I) :
  cauchy ((l.to_filter_Union I π₀).map (integral_sum f vol)) :=
begin
  refine ⟨infer_instance, _⟩,
  rw [prod_map_map_eq, ← to_filter_inf_Union_eq, ← prod_inf_prod, prod_principal_principal],
  exact h.tendsto_integral_sum_to_filter_prod_self_inf_Union_eq_uniformity.mono_left
    (inf_le_inf_left _ $ principal_mono.2 $ λ π h, h.1.trans h.2.symm)
end

variable [complete_space F]

lemma to_subbox_aux (h : integrable I l f vol) (hJ : J ≤ I) :
  ∃ y : F, has_integral J l f vol y ∧
    tendsto (integral_sum f vol) (l.to_filter_Union I (prepartition.single I J hJ)) (𝓝 y) :=
begin
  refine (cauchy_map_iff_exists_tendsto.1
    (h.cauchy_map_integral_sum_to_filter_Union (prepartition.single I J hJ))).imp (λ y hy, ⟨_, hy⟩),
  convert hy.comp (l.tendsto_embed_box_to_filter_Union_top hJ) -- faster than `exact` here
end

/-- If `f` is integrable on a box `I`, then it is integrable on any subbox of `I`. -/
lemma to_subbox (h : integrable I l f vol) (hJ : J ≤ I) : integrable J l f vol :=
(h.to_subbox_aux hJ).imp $ λ y, and.left

/-- If `f` is integrable on a box `I`, then integral sums of `f` over tagged prepartitions
that cover exactly a subbox `J ≤ I` tend to the integral of `f` over `J` along `l`. -/
lemma tendsto_integral_sum_to_filter_Union_single (h : integrable I l f vol) (hJ : J ≤ I) :
  tendsto (integral_sum f vol) (l.to_filter_Union I (prepartition.single I J hJ))
    (𝓝 $ integral J l f vol) :=
let ⟨y, h₁, h₂⟩ := h.to_subbox_aux hJ in h₁.integral_eq.symm ▸ h₂

/-- **Henstock-Sacks inequality**. Let `r : ℝⁿ → (0, ∞)` be a function such that for any tagged
*partition* of `I` subordinate to `r`, the integral sum of `f` over this partition differs from the
integral of `f` by at most `ε`. Then for any tagged *prepartition* `π` subordinate to `r`, the
integral sum of `f` over this prepartition differs from the integral of `f` over the part of `I`
covered by `π` by at most `ε`.

The actual statement

- uses `box_integral.integrable.convergence_r` instead of a predicate assumption on `r`;
- uses `box_integral.integration_params.mem_base_set` instead of “subordinate to `r`” to
  account for additional requirements like being a Henstock partition or having a bounded
  distortion;
- takes an extra argument `π₀ : prepartition I` and an assumption `π.Union = π₀.Union` instead of
  using `π.to_prepartition`.
-/
lemma dist_integral_sum_sum_integral_le_of_mem_base_set_of_Union_eq (h : integrable I l f vol)
  (h0 : 0 < ε) (hπ : l.mem_base_set I c (h.convergence_r ε c) π) {π₀ : prepartition I}
  (hU : π.Union = π₀.Union) :
  dist (integral_sum f vol π) (∑ J in π₀.boxes, integral J l f vol) ≤ ε :=
begin
  /- Let us prove that the distance is less than or equal to `ε + δ` for all positive `δ`. -/
  refine le_of_forall_pos_le_add (λ δ δ0, _),
  /- First we choose some constants. -/
  set δ' : ℝ := δ / (π₀.boxes.card + 1),
  have H0 : 0 < (π₀.boxes.card + 1 : ℝ) := nat.cast_add_one_pos _,
  have δ'0 : 0 < δ' := div_pos δ0 H0,
  set C := max π₀.distortion π₀.compl.distortion,
  /- Next we choose a tagged partition of each `J ∈ π₀` such that the integral sum of `f` over this
  partition is `δ'`-close to the integral of `f` over `J`. -/
  have : ∀ J ∈ π₀, ∃ πi : tagged_prepartition J, πi.is_partition ∧
    dist (integral_sum f vol πi) (integral J l f vol) ≤ δ' ∧
    l.mem_base_set J C (h.convergence_r δ' C) πi,
  { intros J hJ,
    have Hle : J ≤ I := π₀.le_of_mem hJ,
    have HJi : integrable J l f vol := h.to_subbox Hle,
    set r := λ x, min (h.convergence_r δ' C x) (HJi.convergence_r δ' C x),
    have hr : l.r_cond r, from (h.convergence_r_cond _ C).min (HJi.convergence_r_cond _ C),
    have hJd : J.distortion ≤ C, from le_trans (finset.le_sup hJ) (le_max_left _ _),
    rcases l.exists_mem_base_set_is_partition J hJd r with ⟨πJ, hC, hp⟩,
    have hC₁ : l.mem_base_set J C (HJi.convergence_r δ' C) πJ,
    { refine hC.mono J le_rfl le_rfl (λ x hx, _), exact min_le_right _ _ },
    have hC₂ : l.mem_base_set J C (h.convergence_r δ' C) πJ,
    { refine hC.mono J le_rfl le_rfl (λ x hx, _), exact min_le_left _ _ },
    exact ⟨πJ, hp, HJi.dist_integral_sum_integral_le_of_mem_base_set δ'0 hC₁ hp, hC₂⟩ },
  /- Now we combine these tagged partitions into a tagged prepartition of `I` that covers the
  same part of `I` as `π₀` and apply `box_integral.dist_integral_sum_le_of_mem_base_set` to
  `π` and this prepartition. -/
  choose! πi hπip hπiδ' hπiC,
  have : l.mem_base_set I C (h.convergence_r δ' C) (π₀.bUnion_tagged πi),
    from bUnion_tagged_mem_base_set hπiC hπip (λ _, le_max_right _ _),
  have hU' : π.Union = (π₀.bUnion_tagged πi).Union,
    from hU.trans (prepartition.Union_bUnion_partition _ hπip).symm,
  have := h.dist_integral_sum_le_of_mem_base_set h0 δ'0 hπ this hU',
  rw integral_sum_bUnion_tagged at this,
  calc dist (integral_sum f vol π) (∑ J in π₀.boxes, integral J l f vol)
      ≤ dist (integral_sum f vol π) (∑ J in π₀.boxes, integral_sum f vol (πi J)) +
        dist (∑ J in π₀.boxes, integral_sum f vol (πi J)) (∑ J in π₀.boxes, integral J l f vol) :
    dist_triangle _ _ _
  ... ≤ (ε + δ') + ∑ J in π₀.boxes, δ' : add_le_add this (dist_sum_sum_le_of_le _ hπiδ')
  ... = ε + δ : by { field_simp [H0.ne'], ring }
end

/-- **Henstock-Sacks inequality**. Let `r : ℝⁿ → (0, ∞)` be a function such that for any tagged
*partition* of `I` subordinate to `r`, the integral sum of `f` over this partition differs from the
integral of `f` by at most `ε`. Then for any tagged *prepartition* `π` subordinate to `r`, the
integral sum of `f` over this prepartition differs from the integral of `f` over the part of `I`
covered by `π` by at most `ε`.

The actual statement

- uses `box_integral.integrable.convergence_r` instead of a predicate assumption on `r`;
- uses `box_integral.integration_params.mem_base_set` instead of “subordinate to `r`” to
  account for additional requirements like being a Henstock partition or having a bounded
  distortion;
-/
lemma dist_integral_sum_sum_integral_le_of_mem_base_set (h : integrable I l f vol)
  (h0 : 0 < ε) (hπ : l.mem_base_set I c (h.convergence_r ε c) π) :
  dist (integral_sum f vol π) (∑ J in π.boxes, integral J l f vol) ≤ ε :=
h.dist_integral_sum_sum_integral_le_of_mem_base_set_of_Union_eq h0 hπ rfl

/-- Integral sum of `f` over a tagged prepartition `π` such that `π.Union = π₀.Union` tends to the
sum of integrals of `f` over the boxes of `π₀`. -/
lemma tendsto_integral_sum_sum_integral (h : integrable I l f vol) (π₀ : prepartition I) :
  tendsto (integral_sum f vol) (l.to_filter_Union I π₀) (𝓝 $ ∑ J in π₀.boxes, integral J l f vol) :=
begin
  refine ((l.has_basis_to_filter_Union I π₀).tendsto_iff nhds_basis_closed_ball).2 (λ ε ε0, _),
  refine ⟨h.convergence_r ε, h.convergence_r_cond ε, _⟩,
  simp only [mem_inter_iff, set.mem_Union, mem_set_of_eq],
  rintro π ⟨c, hc, hU⟩,
  exact h.dist_integral_sum_sum_integral_le_of_mem_base_set_of_Union_eq ε0 hc hU
end

/-- If `f` is integrable on `I`, then `λ J, integral J l f vol` is box-additive on subboxes of `I`:
if `π₁`, `π₂` are two prepartitions of `I` covering the same part of `I`, then the sum of integrals
of `f` over the boxes of `π₁` is equal to the sum of integrals of `f` over the boxes of `π₂`.

See also `box_integral.integrable.to_box_additive` for a bundled version. -/
lemma sum_integral_congr (h : integrable I l f vol) {π₁ π₂ : prepartition I}
  (hU : π₁.Union = π₂.Union) :
  ∑ J in π₁.boxes, integral J l f vol = ∑ J in π₂.boxes, integral J l f vol :=
begin
  refine tendsto_nhds_unique (h.tendsto_integral_sum_sum_integral π₁) _,
  rw l.to_filter_Union_congr _ hU,
  exact h.tendsto_integral_sum_sum_integral π₂
end

/-- If `f` is integrable on `I`, then `λ J, integral J l f vol` is box-additive on subboxes of `I`:
if `π₁`, `π₂` are two prepartitions of `I` covering the same part of `I`, then the sum of integrals
of `f` over the boxes of `π₁` is equal to the sum of integrals of `f` over the boxes of `π₂`.

See also `box_integral.integrable.sum_integral_congr` for an unbundled version. -/
@[simps] def to_box_additive (h : integrable I l f vol) : ι →ᵇᵃ[I] F :=
{ to_fun := λ J, integral J l f vol,
  sum_partition_boxes' := λ J hJ π hπ,
    begin
      replace hπ := hπ.Union_eq, rw ← prepartition.Union_top at hπ,
      rw [(h.to_subbox (with_top.coe_le_coe.1 hJ)).sum_integral_congr hπ,
        prepartition.top_boxes, sum_singleton]
    end }

end integrable

open measure_theory

/-!
### Integrability conditions
-/

variable (l)

/-- A continuous function is box-integrable with respect to any locally finite measure.

This is true for any volume with bounded variation. -/
lemma integrable_of_continuous_on [complete_space E] {I : box ι} {f : ℝⁿ → E}
  (hc : continuous_on f I.Icc) (μ : measure ℝⁿ) [is_locally_finite_measure μ] :
  integrable.{u v v} I l f μ.to_box_additive.to_smul :=
begin
  have huc := I.is_compact_Icc.uniform_continuous_on_of_continuous hc,
  rw metric.uniform_continuous_on_iff_le at huc,
  refine integrable_iff_cauchy_basis.2 (λ ε ε0, _),
  rcases exists_pos_mul_lt ε0 (μ.to_box_additive I) with ⟨ε', ε0', hε⟩,
  rcases huc ε' ε0' with ⟨δ, δ0 : 0 < δ, Hδ⟩,
  refine ⟨λ _ _, ⟨δ / 2, half_pos δ0⟩, λ _ _ _, rfl, λ c₁ c₂ π₁ π₂ h₁ h₁p h₂ h₂p, _⟩,
  simp only [dist_eq_norm, integral_sum_sub_partitions _ _ h₁p h₂p,
    box_additive_map.to_smul_apply, ← smul_sub],
  have : ∀ J ∈ π₁.to_prepartition ⊓ π₂.to_prepartition,
    ‖μ.to_box_additive J • (f ((π₁.inf_prepartition π₂.to_prepartition).tag J) -
      f ((π₂.inf_prepartition π₁.to_prepartition).tag J))‖ ≤ μ.to_box_additive J * ε',
  { intros J hJ,
    have : 0 ≤ μ.to_box_additive J, from ennreal.to_real_nonneg,
    rw [norm_smul, real.norm_eq_abs, abs_of_nonneg this, ← dist_eq_norm],
    refine mul_le_mul_of_nonneg_left _ this,
    refine Hδ _ (tagged_prepartition.tag_mem_Icc _ _) _ (tagged_prepartition.tag_mem_Icc _ _) _,
    rw [← add_halves δ],
    refine (dist_triangle_left _ _ J.upper).trans (add_le_add (h₁.1 _ _ _) (h₂.1 _ _ _)),
    { exact prepartition.bUnion_index_mem _ hJ },
    { exact box.le_iff_Icc.1 (prepartition.le_bUnion_index _ hJ) J.upper_mem_Icc },
    { rw _root_.inf_comm at hJ,
      exact prepartition.bUnion_index_mem _ hJ },
    { rw _root_.inf_comm at hJ,
      exact box.le_iff_Icc.1 (prepartition.le_bUnion_index _ hJ) J.upper_mem_Icc } },
  refine (norm_sum_le_of_le _ this).trans _,
  rw [← finset.sum_mul, μ.to_box_additive.sum_partition_boxes le_top (h₁p.inf h₂p)],
  exact hε.le
end

variable {l}

/-- This is an auxiliary lemma used to prove two statements at once. Use one of the next two
lemmas instead. -/
lemma has_integral_of_bRiemann_eq_ff_of_forall_is_o (hl : l.bRiemann = ff)
  (B : ι →ᵇᵃ[I] ℝ) (hB0 : ∀ J, 0 ≤ B J) (g : ι →ᵇᵃ[I] F) (s : set ℝⁿ) (hs : s.countable)
  (hlH : s.nonempty → l.bHenstock = tt)
  (H₁ : ∀ (c : ℝ≥0) (x ∈ I.Icc ∩ s) (ε > (0 : ℝ)), ∃ δ > 0, ∀ J ≤ I,
    J.Icc ⊆ metric.closed_ball x δ → x ∈ J.Icc →
    (l.bDistortion → J.distortion ≤ c) → dist (vol J (f x)) (g J) ≤ ε)
  (H₂ : ∀ (c : ℝ≥0) (x ∈ I.Icc \ s) (ε > (0 : ℝ)), ∃ δ > 0, ∀ J ≤ I,
    J.Icc ⊆ metric.closed_ball x δ → (l.bHenstock → x ∈ J.Icc) →
    (l.bDistortion → J.distortion ≤ c) → dist (vol J (f x)) (g J) ≤ ε * B J) :
  has_integral I l f vol (g I) :=
begin
  /- We choose `r x` differently for `x ∈ s` and `x ∉ s`.

  For `x ∈ s`, we choose `εs` such that `∑' x : s, εs x < ε / 2 / 2 ^ #ι`, then choose `r x` so that
  `dist (vol J (f x)) (g J) ≤ εs x` for `J` in the `r x`-neighborhood of `x`. This guarantees that
  the sum of these distances over boxes `J` such that `π.tag J ∈ s` is less than `ε / 2`. We need an
  additional multiplier `2 ^ #ι` because different boxes can have the same tag.

  For `x ∉ s`, we choose `r x` so that `dist (vol (J (f x))) (g J) ≤ (ε / 2 / B I) * B J` for a box
  `J` in the `δ`-neighborhood of `x`. -/
  refine ((l.has_basis_to_filter_Union_top _).tendsto_iff metric.nhds_basis_closed_ball).2 _,
  intros ε ε0,
  simp only [subtype.exists'] at H₁ H₂,
  choose! δ₁ Hδ₁ using H₁,
  choose! δ₂ Hδ₂ using H₂,
  have ε0' := half_pos ε0, have H0 : 0 < (2 ^ fintype.card ι : ℝ), from pow_pos zero_lt_two _,
  rcases hs.exists_pos_forall_sum_le (div_pos ε0' H0) with ⟨εs, hεs0, hεs⟩,
  simp only [le_div_iff' H0, mul_sum] at hεs,
  rcases exists_pos_mul_lt ε0' (B I) with ⟨ε', ε'0, hεI⟩,
  set δ : ℝ≥0 → ℝⁿ → Ioi (0 : ℝ) := λ c x, if x ∈ s then δ₁ c x (εs x) else (δ₂ c) x ε',
  refine ⟨δ, λ c, l.r_cond_of_bRiemann_eq_ff hl, _⟩,
  simp only [set.mem_Union, mem_inter_iff, mem_set_of_eq],
  rintro π ⟨c, hπδ, hπp⟩,
  /- Now we split the sum into two parts based on whether `π.tag J` belongs to `s` or not. -/
  rw [← g.sum_partition_boxes le_rfl hπp, mem_closed_ball, integral_sum,
    ← sum_filter_add_sum_filter_not π.boxes (λ J, π.tag J ∈ s),
    ← sum_filter_add_sum_filter_not π.boxes (λ J, π.tag J ∈ s), ← add_halves ε],
  refine dist_add_add_le_of_le _ _,
  { unfreezingI { rcases s.eq_empty_or_nonempty with rfl|hsne }, { simp [ε0'.le] },
    /- For the boxes such that `π.tag J ∈ s`, we use the fact that at most `2 ^ #ι` boxes have the
    same tag. -/
    specialize hlH hsne,
    have : ∀ J ∈ π.boxes.filter (λ J, π.tag J ∈ s), dist (vol J (f $ π.tag J)) (g J) ≤ εs (π.tag J),
    { intros J hJ, rw finset.mem_filter at hJ, cases hJ with hJ hJs,
      refine Hδ₁ c _ ⟨π.tag_mem_Icc _, hJs⟩ _ (hεs0 _) _ (π.le_of_mem' _ hJ) _
        (hπδ.2 hlH J hJ) (λ hD, (finset.le_sup hJ).trans (hπδ.3 hD)),
      convert hπδ.1 J hJ, exact (dif_pos hJs).symm },
    refine (dist_sum_sum_le_of_le _ this).trans _,
    rw sum_comp,
    refine (sum_le_sum _).trans (hεs _ _),
    { rintro b -,
      rw [← nat.cast_two, ← nat.cast_pow, ← nsmul_eq_mul],
      refine nsmul_le_nsmul (hεs0 _).le _,
      refine (finset.card_le_of_subset _).trans ((hπδ.is_Henstock hlH).card_filter_tag_eq_le b),
      exact filter_subset_filter _ (filter_subset _ _) },
    { rw [finset.coe_image, set.image_subset_iff],
      exact λ J hJ, (finset.mem_filter.1 hJ).2 } },
  /- Now we deal with boxes such that `π.tag J ∉ s`.
  In this case the estimate is straightforward. -/
  have H₂ : ∀ J ∈ π.boxes.filter (λ J, π.tag J ∉ s), dist (vol J (f $ π.tag J)) (g J) ≤ ε' * B J,
  { intros J hJ, rw finset.mem_filter at hJ, cases hJ with hJ hJs,
    refine Hδ₂ c _ ⟨π.tag_mem_Icc _, hJs⟩ _ ε'0 _ (π.le_of_mem' _ hJ) _ (λ hH, hπδ.2 hH J hJ)
      (λ hD, (finset.le_sup hJ).trans (hπδ.3 hD)),
    convert hπδ.1 J hJ, exact (dif_neg hJs).symm },
  refine (dist_sum_sum_le_of_le _ H₂).trans
    ((sum_le_sum_of_subset_of_nonneg (filter_subset _ _) _).trans _),
  { exact λ _ _ _, mul_nonneg ε'0.le (hB0 _) },
  { rw [← mul_sum, B.sum_partition_boxes le_rfl hπp, mul_comm],
    exact hεI.le }
end

/-- A function `f` has Henstock (or `⊥`) integral over `I` is equal to the value of a box-additive
function `g` on `I` provided that `vol J (f x)` is sufficiently close to `g J` for sufficiently
small boxes `J ∋ x`. This lemma is useful to prove, e.g., to prove the Divergence theorem for
integral along `⊥`.

Let `l` be either `box_integral.integration_params.Henstock` or `⊥`. Let `g` a box-additive function
on subboxes of `I`. Suppose that there exists a nonnegative box-additive function `B` and a
countable set `s` with the following property.

For every `c : ℝ≥0`, a point `x ∈ I.Icc`, and a positive `ε` there exists `δ > 0` such that for any
box `J ≤ I` such that

- `x ∈ J.Icc ⊆ metric.closed_ball x δ`;
- if `l.bDistortion` (i.e., `l = ⊥`), then the distortion of `J` is less than or equal to `c`,

the distance between the term `vol J (f x)` of an integral sum corresponding to `J` and `g J` is
less than or equal to `ε` if `x ∈ s` and is less than or equal to `ε * B J` otherwise.

Then `f` is integrable on `I along `l` with integral `g I`. -/
lemma has_integral_of_le_Henstock_of_forall_is_o (hl : l ≤ Henstock) (B : ι →ᵇᵃ[I] ℝ)
  (hB0 : ∀ J, 0 ≤ B J) (g : ι →ᵇᵃ[I] F) (s : set ℝⁿ) (hs : s.countable)
  (H₁ : ∀ (c : ℝ≥0) (x ∈ I.Icc ∩ s) (ε > (0 : ℝ)), ∃ δ > 0, ∀ J ≤ I,
    J.Icc ⊆ metric.closed_ball x δ → x ∈ J.Icc → (l.bDistortion → J.distortion ≤ c) →
    dist (vol J (f x)) (g J) ≤ ε)
  (H₂ : ∀ (c : ℝ≥0) (x ∈ I.Icc \ s) (ε > (0 : ℝ)), ∃ δ > 0, ∀ J ≤ I,
    J.Icc ⊆ metric.closed_ball x δ → x ∈ J.Icc → (l.bDistortion → J.distortion ≤ c) →
    dist (vol J (f x)) (g J) ≤ ε * B J) :
  has_integral I l f vol (g I) :=
have A : l.bHenstock, from hl.2.1.resolve_left dec_trivial,
has_integral_of_bRiemann_eq_ff_of_forall_is_o (hl.1.resolve_right dec_trivial) B hB0 _ s hs (λ _, A)
  H₁ $ by simpa only [A, true_implies_iff] using H₂

/-- Suppose that there exists a nonnegative box-additive function `B` with the following property.

For every `c : ℝ≥0`, a point `x ∈ I.Icc`, and a positive `ε` there exists `δ > 0` such that for any
box `J ≤ I` such that

- `J.Icc ⊆ metric.closed_ball x δ`;
- if `l.bDistortion` (i.e., `l = ⊥`), then the distortion of `J` is less than or equal to `c`,

the distance between the term `vol J (f x)` of an integral sum corresponding to `J` and `g J` is
less than or equal to `ε * B J`.

Then `f` is McShane integrable on `I` with integral `g I`. -/
lemma has_integral_McShane_of_forall_is_o (B : ι →ᵇᵃ[I] ℝ) (hB0 : ∀ J, 0 ≤ B J)
  (g : ι →ᵇᵃ[I] F) (H : ∀ (c : ℝ≥0) (x ∈ I.Icc) (ε > (0 : ℝ)), ∃ δ > 0, ∀ J ≤ I,
    J.Icc ⊆ metric.closed_ball x δ → dist (vol J (f x)) (g J) ≤ ε * B J) :
  has_integral I McShane f vol (g I) :=
has_integral_of_bRiemann_eq_ff_of_forall_is_o rfl B hB0 g ∅ countable_empty (λ ⟨x, hx⟩, hx.elim)
  (λ c x hx, hx.2.elim) $
  by simpa only [McShane, coe_sort_ff, false_implies_iff, true_implies_iff, diff_empty] using H

end box_integral


-- import algebra.field
import data.rat data.nat.modeq order.lattice
import tactic.tidy
import data.padics.padic_norm
import data.padics.padic_integers
import data.nat.prime
import data.zmod.basic
import set_theory.cardinal
import tactic.find
open rat nat lattice list padic_val_rat multiplicity

section harmonic

variable [prime_two : nat.prime 2]
include prime_two

local infix ` /. `:70 := mk

variable x:ℚ

def harmonic_number : ℕ → ℚ
| 0 := 0
| 1 := 1
| (succ n) := (harmonic_number n) + 1 /. (n+1)

@[simp] lemma finite_two (q : ℕ) (hq : q ≠ 0) : finite 2 q :=
begin
  apply (@finite_nat_iff 2 q).2,
  split, exact (prime.ne_one prime_two),
  apply nat.pos_of_ne_zero, assumption
end

lemma two_val_neg_denom_even (x : ℚ) : (x ≠ 0) → (padic_val_rat 2 x < 0) → 2 ∣ x.denom :=
begin
  intros h₁ h₂,
  rw padic_val_rat_def _ h₁ at h₂, swap, exact prime_two,
  rw [sub_lt_zero] at h₂,
  have := lt_of_le_of_lt (int.coe_nat_nonneg _) h₂,
  have := int.coe_nat_pos.1 this,
  rw [←enat.coe_lt_coe,enat.coe_get] at this,
  replace := dvd_of_multiplicity_pos this,
  rw int.coe_nat_dvd at this, assumption,
end

lemma two_val_rec_pow_two (r : ℕ) : padic_val_rat 2 (1 /. (2^r)) = -r :=
begin
  rw [←inv_def,←coe_int_eq_mk], simp,
  rw padic_val_rat.inv,
  {
    rw padic_val_rat.pow,
    have h : padic_val_rat 2 2 = padic_val_rat 2 ↑2, by refl,
    rw h,
    rw padic_val_rat_self one_lt_two,
    simp, exact two_ne_zero,
  },
  apply pow_ne_zero, exact two_ne_zero,
end

def max_pow_2_below (n : ℕ) := nat.find_greatest (λ (m : ℕ), 2^m ≤ n) n

lemma max_pow_le (n : ℕ) :
(n ≥ 1) → 2^(max_pow_2_below n) ≤ n :=
begin
  intro h,
  apply @find_greatest_spec (λ (m : ℕ), 2^m ≤ n), simp,
  existsi 0, simpa,
end

lemma is_lt_max_pow_plus_one (n : ℕ) : (n ≥ 1) →  n < 2^((max_pow_2_below n) + 1) :=
begin
  intro h,
  by_contra,
  rw not_lt at a,
  have k : (max_pow_2_below n + 1) ≤ max_pow_2_below n, {
    apply le_find_greatest,
    swap, exact a,
    -- induction n with m n h, simp at *,
    sorry
  },
  have := nat.le_sub_left_of_add_le k, simp at this, assumption
end

lemma two_val_add_eq_min {q r : ℚ} (hne : padic_val_rat 2 q ≠ padic_val_rat 2 r) :
  padic_val_rat 2 (q + r) = min (padic_val_rat 2 q) (padic_val_rat 2 r) :=
sorry

lemma two_val_le_max_pow (x n : ℕ) (hx : x ≤ n)(hn : n ≥ 1) :(padic_val_rat 2 x) ≤ (max_pow_2_below n) :=
begin
  by_contra,
  rw not_le at a,
  sorry,
end

lemma two_val_sum_pow_two (n k : ℕ)(h₀ : k ≠ 0)(hn : n ≥ 1)(hk : k < n) : padic_val_rat 2 k ≠ (max_pow_2_below n) → padic_val_rat 2 (1 /. k + 1 /. 2^(max_pow_2_below n)) = -max_pow_2_below n  :=
begin
  intros,
  rw two_val_add_eq_min,
  all_goals {rw [two_val_rec_pow_two,←inv_def,←coe_int_eq_mk],simp,rw padic_val_rat.inv}, swap, simpa,
  rw min_neg_neg, simp,
  apply max_eq_right,
  apply two_val_le_max_pow, apply le_of_lt hk, tidy,
end

theorem valuation_harmonic_number (n : ℕ) (hn : n ≥ 1) : padic_val_rat 2 (harmonic_number n) + max_pow_2_below n = 0 :=
sorry

-- lemma odd_denoms (q₁ q₂ : ℚ) (h₁ : ↑(denom q₁) % 2 = 1) (h₂ : ↑(denom q₂) % 2 = 1) : ((denom (q₁ + q₂)) % 2 = 1) :=
-- begin
--   rw add_num_denom,
--   have h : ((q₁.num * ↑(q₂.denom) + ↑(q₁.denom) * q₂.num) /. (↑(q₁.denom) * ↑(q₂.denom))).denom = (↑(q₁.denom) * ↑(q₂.denom)), by sorry,
--   rw h,
--   apply odd_mul_odd h₁ h₂,
-- end

end harmonic


/-
Copyright (c) 2020 Floris van Doorn. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Floris van Doorn
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.algebra.big_operators.basic
import Mathlib.data.nat.enat
import Mathlib.PostPort

universes u_1

namespace Mathlib

/-!
# Big operators in `enat`

A simple lemma about sums in `enat`.
-/

namespace finset

theorem sum_nat_coe_enat {α : Type u_1} (s : finset α) (f : α → ℕ) :
    (finset.sum s fun (x : α) => ↑(f x)) = ↑(finset.sum s fun (x : α) => f x) :=
  Eq.symm (add_monoid_hom.map_sum enat.coe_hom (fun (x : α) => f x) s)

end Mathlib


lemma one_pow (m : mynat) : (1 : mynat) ^ m = 1 :=
begin
induction m with n hn,
rwa [pow_zero],
rw pow_succ,
rw hn,
simp,
end


/-
Copyright (c) 2017 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro, Floris van Doorn, Violeta Hernández Palacios

! This file was ported from Lean 3 source module set_theory.cardinal.cofinality
! leanprover-community/mathlib commit bb168510ef455e9280a152e7f31673cabd3d7496
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.SetTheory.Cardinal.Ordinal
import Mathlib.SetTheory.Ordinal.FixedPoint

/-!
# Cofinality

This file contains the definition of cofinality of an ordinal number and regular cardinals

## Main Definitions

* `Ordinal.cof o` is the cofinality of the ordinal `o`.
  If `o` is the order type of the relation `<` on `α`, then `o.cof` is the smallest cardinality of a
  subset `s` of α that is *cofinal* in `α`, i.e. `∀ x : α, ∃ y ∈ s, ¬ y < x`.
* `Cardinal.IsLimit c` means that `c` is a (weak) limit cardinal: `c ≠ 0 ∧ ∀ x < c, succ x < c`.
* `Cardinal.IsStrongLimit c` means that `c` is a strong limit cardinal:
  `c ≠ 0 ∧ ∀ x < c, 2 ^ x < c`.
* `Cardinal.IsRegular c` means that `c` is a regular cardinal: `ℵ₀ ≤ c ∧ c.ord.cof = c`.
* `Cardinal.IsInaccessible c` means that `c` is strongly inaccessible:
  `ℵ₀ < c ∧ IsRegular c ∧ IsStrongLimit c`.

## Main Statements

* `Ordinal.infinite_pigeonhole_card`: the infinite pigeonhole principle
* `Cardinal.lt_power_cof`: A consequence of König's theorem stating that `c < c ^ c.ord.cof` for
  `c ≥ ℵ₀`
* `Cardinal.univ_inaccessible`: The type of ordinals in `Type u` form an inaccessible cardinal
  (in `Type v` with `v > u`). This shows (externally) that in `Type u` there are at least `u`
  inaccessible cardinals.

## Implementation Notes

* The cofinality is defined for ordinals.
  If `c` is a cardinal number, its cofinality is `c.ord.cof`.

## Tags

cofinality, regular cardinals, limits cardinals, inaccessible cardinals,
infinite pigeonhole principle
-/

noncomputable section

open Function Cardinal Set Order

open Classical Cardinal Ordinal

universe u v w

variable {α : Type _} {r : α → α → Prop}

/-! ### Cofinality of orders -/

namespace Order

/-- Cofinality of a reflexive order `≼`. This is the smallest cardinality
  of a subset `S : Set α` such that `∀ a, ∃ b ∈ S, a ≼ b`. -/
def cof (r : α → α → Prop) : Cardinal :=
  infₛ { c | ∃ S : Set α, (∀ a, ∃ b ∈ S, r a b) ∧ (#S) = c }
#align order.cof Order.cof

/-- The set in the definition of `Order.cof` is nonempty. -/
theorem cof_nonempty (r : α → α → Prop) [IsRefl α r] :
    { c | ∃ S : Set α, (∀ a, ∃ b ∈ S, r a b) ∧ (#S) = c }.Nonempty :=
  ⟨_, Set.univ, fun a => ⟨a, ⟨⟩, refl _⟩, rfl⟩
#align order.cof_nonempty Order.cof_nonempty

theorem cof_le (r : α → α → Prop) {S : Set α} (h : ∀ a, ∃ b ∈ S, r a b) : cof r ≤ (#S) :=
  cinfₛ_le' ⟨S, h, rfl⟩
#align order.cof_le Order.cof_le

theorem le_cof {r : α → α → Prop} [IsRefl α r] (c : Cardinal) :
    c ≤ cof r ↔ ∀ {S : Set α}, (∀ a, ∃ b ∈ S, r a b) → c ≤ (#S) := by
  rw [cof, le_cinfₛ_iff'' (cof_nonempty r)]
  use fun H S h => H _ ⟨S, h, rfl⟩
  rintro H d ⟨S, h, rfl⟩
  exact H h
#align order.le_cof Order.le_cof

end Order

theorem RelIso.cof_le_lift {α : Type u} {β : Type v} {r : α → α → Prop} {s} [IsRefl β s]
    (f : r ≃r s) : Cardinal.lift.{max u v} (Order.cof r) ≤ Cardinal.lift.{max u v} (Order.cof s) :=
  by
  rw [Order.cof, Order.cof, lift_infₛ, lift_infₛ,
    le_cinfₛ_iff'' (nonempty_image_iff.2 (Order.cof_nonempty s))]
  rintro - ⟨-, ⟨u, H, rfl⟩, rfl⟩
  apply cinfₛ_le'
  refine'
    ⟨_, ⟨f.symm '' u, fun a => _, rfl⟩,
      lift_mk_eq.{u, v, max u v}.2 ⟨(f.symm.toEquiv.image u).symm⟩⟩
  rcases H (f a) with ⟨b, hb, hb'⟩
  refine' ⟨f.symm b, mem_image_of_mem _ hb, f.map_rel_iff.1 _⟩
  rwa [RelIso.apply_symm_apply]
#align rel_iso.cof_le_lift RelIso.cof_le_lift

theorem RelIso.cof_eq_lift {α : Type u} {β : Type v} {r s} [IsRefl α r] [IsRefl β s] (f : r ≃r s) :
    Cardinal.lift.{max u v} (Order.cof r) = Cardinal.lift.{max u v} (Order.cof s) :=
  (RelIso.cof_le_lift f).antisymm (RelIso.cof_le_lift f.symm)
#align rel_iso.cof_eq_lift RelIso.cof_eq_lift

theorem RelIso.cof_le {α β : Type u} {r : α → α → Prop} {s} [IsRefl β s] (f : r ≃r s) :
    Order.cof r ≤ Order.cof s :=
  lift_le.1 (RelIso.cof_le_lift f)
#align rel_iso.cof_le RelIso.cof_le

theorem RelIso.cof_eq {α β : Type u} {r s} [IsRefl α r] [IsRefl β s] (f : r ≃r s) :
    Order.cof r = Order.cof s :=
  lift_inj.1 (RelIso.cof_eq_lift f)
#align rel_iso.cof_eq RelIso.cof_eq

/-- Cofinality of a strict order `≺`. This is the smallest cardinality of a set `S : Set α` such
that `∀ a, ∃ b ∈ S, ¬ b ≺ a`. -/
def StrictOrder.cof (r : α → α → Prop) : Cardinal :=
  Order.cof (swap rᶜ)
#align strict_order.cof StrictOrder.cof

/-- The set in the definition of `Order.StrictOrder.cof` is nonempty. -/
theorem StrictOrder.cof_nonempty (r : α → α → Prop) [IsIrrefl α r] :
    { c | ∃ S : Set α, Unbounded r S ∧ (#S) = c }.Nonempty :=
  @Order.cof_nonempty α _ (IsRefl.swap (rᶜ))
#align strict_order.cof_nonempty StrictOrder.cof_nonempty

/-! ### Cofinality of ordinals -/

namespace Ordinal

/-- Cofinality of an ordinal. This is the smallest cardinal of a
  subset `S` of the ordinal which is unbounded, in the sense
  `∀ a, ∃ b ∈ S, a ≤ b`. It is defined for all ordinals, but
  `cof 0 = 0` and `cof (succ o) = 1`, so it is only really
  interesting on limit ordinals (when it is an infinite cardinal). -/
def cof (o : Ordinal.{u}) : Cardinal.{u} :=
  o.liftOn (fun a => StrictOrder.cof a.r)
    (by
      rintro ⟨α, r, wo₁⟩ ⟨β, s, wo₂⟩ ⟨⟨f, hf⟩⟩
      haveI := wo₁; haveI := wo₂
      dsimp only
      apply @RelIso.cof_eq _ _ _ _ ?_ ?_
      · constructor
        exact @fun a b => not_iff_not.2 hf
      · dsimp only [swap]
        exact ⟨fun _ => irrefl _⟩
      · dsimp only [swap]
        exact ⟨fun _ => irrefl _⟩)
#align ordinal.cof Ordinal.cof

theorem cof_type (r : α → α → Prop) [IsWellOrder α r] : (type r).cof = StrictOrder.cof r :=
  rfl
#align ordinal.cof_type Ordinal.cof_type

theorem le_cof_type [IsWellOrder α r] {c} : c ≤ cof (type r) ↔ ∀ S, Unbounded r S → c ≤ (#S) :=
  (le_cinfₛ_iff'' (StrictOrder.cof_nonempty r)).trans
    ⟨fun H S h => H _ ⟨S, h, rfl⟩, by
      rintro H d ⟨S, h, rfl⟩
      exact H _ h⟩
#align ordinal.le_cof_type Ordinal.le_cof_type

theorem cof_type_le [IsWellOrder α r] {S : Set α} (h : Unbounded r S) : cof (type r) ≤ (#S) :=
  le_cof_type.1 le_rfl S h
#align ordinal.cof_type_le Ordinal.cof_type_le

theorem lt_cof_type [IsWellOrder α r] {S : Set α} : (#S) < cof (type r) → Bounded r S := by
  simpa using not_imp_not.2 cof_type_le
#align ordinal.lt_cof_type Ordinal.lt_cof_type

theorem cof_eq (r : α → α → Prop) [IsWellOrder α r] : ∃ S, Unbounded r S ∧ (#S) = cof (type r) :=
  cinfₛ_mem (StrictOrder.cof_nonempty r)
#align ordinal.cof_eq Ordinal.cof_eq

theorem ord_cof_eq (r : α → α → Prop) [IsWellOrder α r] :
    ∃ S, Unbounded r S ∧ type (Subrel r S) = (cof (type r)).ord := by
  let ⟨S, hS, e⟩ := cof_eq r
  let ⟨s, _, e'⟩ := Cardinal.ord_eq S
  let T : Set α := { a | ∃ aS : a ∈ S, ∀ b : S, s b ⟨_, aS⟩ → r b a }
  suffices : Unbounded r T
  · refine' ⟨T, this, le_antisymm _ (Cardinal.ord_le.2 <| cof_type_le this)⟩
    rw [← e, e']
    refine'
      (RelEmbedding.ofMonotone
          (fun a : T =>
            (⟨a,
                let ⟨aS, _⟩ := a.2
                aS⟩ :
              S))
          fun a b h => _).ordinal_type_le
    rcases a with ⟨a, aS, ha⟩
    rcases b with ⟨b, bS, hb⟩
    change s ⟨a, _⟩ ⟨b, _⟩
    refine' ((trichotomous_of s _ _).resolve_left fun hn => _).resolve_left _
    · exact asymm h (ha _ hn)
    · intro e
      injection e with e
      subst b
      exact irrefl _ h
  · intro a
    have : { b : S | ¬r b a }.Nonempty :=
      let ⟨b, bS, ba⟩ := hS a
      ⟨⟨b, bS⟩, ba⟩
    let b := (IsWellFounded.wf : WellFounded s).min _ this
    have ba : ¬r b a := IsWellFounded.wf.min_mem _ this
    refine' ⟨b, ⟨b.2, fun c => not_imp_not.1 fun h => _⟩, ba⟩
    rw [show ∀ b : S, (⟨b, b.2⟩ : S) = b by intro b; cases b; rfl]
    exact IsWellFounded.wf.not_lt_min _ this (IsOrderConnected.neg_trans h ba)
#align ordinal.ord_cof_eq Ordinal.ord_cof_eq

/-! ### Cofinality of suprema and least strict upper bounds -/

private theorem card_mem_cof {o} : ∃ (ι : _)(f : ι → Ordinal), lsub.{u, u} f = o ∧ (#ι) = o.card :=
  ⟨_, _, lsub_typein o, mk_ordinal_out o⟩

/-- The set in the `lsub` characterization of `cof` is nonempty. -/
theorem cof_lsub_def_nonempty (o) :
    { a : Cardinal | ∃ (ι : _)(f : ι → Ordinal), lsub.{u, u} f = o ∧ (#ι) = a }.Nonempty :=
  ⟨_, card_mem_cof⟩
#align ordinal.cof_lsub_def_nonempty Ordinal.cof_lsub_def_nonempty

theorem cof_eq_infₛ_lsub (o : Ordinal.{u}) :
    cof o = infₛ { a : Cardinal | ∃ (ι : Type u)(f : ι → Ordinal), lsub.{u, u} f = o ∧ (#ι) = a } :=
  by
  refine' le_antisymm (le_cinfₛ (cof_lsub_def_nonempty o) _) (cinfₛ_le' _)
  · rintro a ⟨ι, f, hf, rfl⟩
    rw [← type_lt o]
    refine'
      (cof_type_le fun a => _).trans
        (@mk_le_of_injective _ _
          (fun s : typein ((· < ·) : o.out.α → o.out.α → Prop) ⁻¹' Set.range f =>
            Classical.choose s.prop)
          fun s t hst => by
          let H := congr_arg f hst
          rwa [Classical.choose_spec s.prop, Classical.choose_spec t.prop, typein_inj,
            Subtype.coe_inj] at H)
    have := typein_lt_self a
    simp_rw [← hf, lt_lsub_iff] at this
    cases' this with i hi
    refine' ⟨enum (· < ·) (f i) _, _, _⟩
    · rw [type_lt, ← hf]
      apply lt_lsub
    · rw [mem_preimage, typein_enum]
      exact mem_range_self i
    · rwa [← typein_le_typein, typein_enum]
  · rcases cof_eq (· < · : (Quotient.out o).α → (Quotient.out o).α → Prop) with ⟨S, hS, hS'⟩
    let f : S → Ordinal := fun s => typein LT.lt s.val
    refine'
      ⟨S, f, le_antisymm (lsub_le fun i => typein_lt_self i) (le_of_forall_lt fun a ha => _), by
        rwa [type_lt o] at hS'⟩
    rw [← type_lt o] at ha
    rcases hS (enum (· < ·) a ha) with ⟨b, hb, hb'⟩
    rw [← typein_le_typein, typein_enum] at hb'
    exact hb'.trans_lt (lt_lsub.{u, u} f ⟨b, hb⟩)
#align ordinal.cof_eq_Inf_lsub Ordinal.cof_eq_infₛ_lsub

@[simp]
theorem lift_cof (o) : Cardinal.lift.{u, v} (cof o) = cof (Ordinal.lift.{u, v} o) := by
  refine' inductionOn o _
  intro α r _
  apply le_antisymm
  · refine' le_cof_type.2 fun S H => _
    have : Cardinal.lift.{u, v} (#(ULift.up ⁻¹' S)) ≤ (#(S : Type (max u v))) := by
      rw [← Cardinal.lift_umax.{v, u}, ← Cardinal.lift_id'.{v, u} (#S)]
      refine mk_preimage_of_injective_lift.{v, max u v} ULift.up S (ULift.up_injective.{u, v})
    refine' (Cardinal.lift_le.2 <| cof_type_le _).trans this
    exact fun a =>
      let ⟨⟨b⟩, bs, br⟩ := H ⟨a⟩
      ⟨b, bs, br⟩
  · rcases cof_eq r with ⟨S, H, e'⟩
    have : (#ULift.down.{u, v} ⁻¹' S) ≤ Cardinal.lift.{u, v} (#S) :=
      ⟨⟨fun ⟨⟨x⟩, h⟩ => ⟨⟨x, h⟩⟩, fun ⟨⟨x⟩, h₁⟩ ⟨⟨y⟩, h₂⟩ e => by
          simp at e; congr⟩⟩
    rw [e'] at this
    refine' (cof_type_le _).trans this
    exact fun ⟨a⟩ =>
      let ⟨b, bs, br⟩ := H a
      ⟨⟨b⟩, bs, br⟩
#align ordinal.lift_cof Ordinal.lift_cof

theorem cof_le_card (o) : cof o ≤ card o := by
  rw [cof_eq_infₛ_lsub]
  exact cinfₛ_le' card_mem_cof
#align ordinal.cof_le_card Ordinal.cof_le_card

theorem cof_ord_le (c : Cardinal) : c.ord.cof ≤ c := by simpa using cof_le_card c.ord
#align ordinal.cof_ord_le Ordinal.cof_ord_le

theorem ord_cof_le (o : Ordinal.{u}) : o.cof.ord ≤ o :=
  (ord_le_ord.2 (cof_le_card o)).trans (ord_card_le o)
#align ordinal.ord_cof_le Ordinal.ord_cof_le

theorem exists_lsub_cof (o : Ordinal) :
    ∃ (ι : _)(f : ι → Ordinal), lsub.{u, u} f = o ∧ (#ι) = cof o := by
  rw [cof_eq_infₛ_lsub]
  exact cinfₛ_mem (cof_lsub_def_nonempty o)
#align ordinal.exists_lsub_cof Ordinal.exists_lsub_cof

theorem cof_lsub_le {ι} (f : ι → Ordinal) : cof (lsub.{u, u} f) ≤ (#ι) := by
  rw [cof_eq_infₛ_lsub]
  exact cinfₛ_le' ⟨ι, f, rfl, rfl⟩
#align ordinal.cof_lsub_le Ordinal.cof_lsub_le

theorem cof_lsub_le_lift {ι} (f : ι → Ordinal) :
    cof (lsub.{u, v} f) ≤ Cardinal.lift.{v, u} (#ι) := by
  rw [← mk_uLift.{u, v}]
  convert cof_lsub_le.{max u v} fun i : ULift.{v, u} ι => f i.down
  exact
    lsub_eq_of_range_eq.{u, max u v, max u v}
      (Set.ext fun x => ⟨fun ⟨i, hi⟩ => ⟨ULift.up.{v, u} i, hi⟩, fun ⟨i, hi⟩ => ⟨_, hi⟩⟩)
#align ordinal.cof_lsub_le_lift Ordinal.cof_lsub_le_lift

theorem le_cof_iff_lsub {o : Ordinal} {a : Cardinal} :
    a ≤ cof o ↔ ∀ {ι} (f : ι → Ordinal), lsub.{u, u} f = o → a ≤ (#ι) := by
  rw [cof_eq_infₛ_lsub]
  exact
    (le_cinfₛ_iff'' (cof_lsub_def_nonempty o)).trans
      ⟨fun H ι f hf => H _ ⟨ι, f, hf, rfl⟩, fun H b ⟨ι, f, hf, hb⟩ =>
        by
        rw [← hb]
        exact H _ hf⟩
#align ordinal.le_cof_iff_lsub Ordinal.le_cof_iff_lsub

theorem lsub_lt_ord_lift {ι} {f : ι → Ordinal} {c : Ordinal}
    (hι : Cardinal.lift.{v, u} (#ι) < c.cof)
    (hf : ∀ i, f i < c) : lsub.{u, v} f < c :=
  lt_of_le_of_ne (lsub_le.{v, u} hf) fun h => by
    subst h
    exact (cof_lsub_le_lift.{u, v} f).not_lt hι
#align ordinal.lsub_lt_ord_lift Ordinal.lsub_lt_ord_lift

theorem lsub_lt_ord {ι} {f : ι → Ordinal} {c : Ordinal} (hι : (#ι) < c.cof) :
    (∀ i, f i < c) → lsub.{u, u} f < c :=
  lsub_lt_ord_lift (by rwa [(#ι).lift_id])
#align ordinal.lsub_lt_ord Ordinal.lsub_lt_ord

theorem cof_sup_le_lift {ι} {f : ι → Ordinal} (H : ∀ i, f i < sup.{u, v} f) :
    cof (sup.{u, v} f) ≤ Cardinal.lift.{v, u} (#ι) := by
  rw [← sup_eq_lsub_iff_lt_sup.{u, v}] at H
  rw [H]
  exact cof_lsub_le_lift f
#align ordinal.cof_sup_le_lift Ordinal.cof_sup_le_lift

theorem cof_sup_le {ι} {f : ι → Ordinal} (H : ∀ i, f i < sup.{u, u} f) :
    cof (sup.{u, u} f) ≤ (#ι) := by
  rw [← (#ι).lift_id]
  exact cof_sup_le_lift H
#align ordinal.cof_sup_le Ordinal.cof_sup_le

theorem sup_lt_ord_lift {ι} {f : ι → Ordinal} {c : Ordinal} (hι : Cardinal.lift.{v, u} (#ι) < c.cof)
    (hf : ∀ i, f i < c) : sup.{u, v} f < c :=
  (sup_le_lsub.{u, v} f).trans_lt (lsub_lt_ord_lift hι hf)
#align ordinal.sup_lt_ord_lift Ordinal.sup_lt_ord_lift

theorem sup_lt_ord {ι} {f : ι → Ordinal} {c : Ordinal} (hι : (#ι) < c.cof) :
    (∀ i, f i < c) → sup.{u, u} f < c :=
  sup_lt_ord_lift (by rwa [(#ι).lift_id])
#align ordinal.sup_lt_ord Ordinal.sup_lt_ord

theorem supᵢ_lt_lift {ι} {f : ι → Cardinal} {c : Cardinal}
    (hι : Cardinal.lift.{v, u} (#ι) < c.ord.cof)
    (hf : ∀ i, f i < c) : supᵢ.{max u v + 1, u + 1} f < c := by
  rw [← ord_lt_ord, supᵢ_ord (Cardinal.bddAbove_range.{u, v} _)]
  refine' sup_lt_ord_lift hι fun i => _
  rw [ord_lt_ord]
  apply hf
#align ordinal.supr_lt_lift Ordinal.supᵢ_lt_lift

theorem supᵢ_lt {ι} {f : ι → Cardinal} {c : Cardinal} (hι : (#ι) < c.ord.cof) :
    (∀ i, f i < c) → supᵢ f < c :=
  supᵢ_lt_lift (by rwa [(#ι).lift_id])
#align ordinal.supr_lt Ordinal.supᵢ_lt

theorem nfpFamily_lt_ord_lift {ι} {f : ι → Ordinal → Ordinal} {c} (hc : ℵ₀ < cof c)
    (hc' : Cardinal.lift.{v, u} (#ι) < cof c) (hf : ∀ (i), ∀ b < c, f i b < c) {a} (ha : a < c) :
    nfpFamily.{u, v} f a < c := by
  refine' sup_lt_ord_lift ((Cardinal.lift_le.2 (mk_list_le_max ι)).trans_lt _) fun l => _
  · rw [lift_max]
    apply max_lt _ hc'
    rwa [Cardinal.lift_aleph0]
  · induction' l with i l H
    · exact ha
    · exact hf _ _ H
#align ordinal.nfp_family_lt_ord_lift Ordinal.nfpFamily_lt_ord_lift

theorem nfpFamily_lt_ord {ι} {f : ι → Ordinal → Ordinal} {c} (hc : ℵ₀ < cof c) (hc' : (#ι) < cof c)
    (hf : ∀ (i), ∀ b < c, f i b < c) {a} : a < c → nfpFamily.{u, u} f a < c :=
  nfpFamily_lt_ord_lift hc (by rwa [(#ι).lift_id]) hf
#align ordinal.nfp_family_lt_ord Ordinal.nfpFamily_lt_ord

theorem nfpBFamily_lt_ord_lift {o : Ordinal} {f : ∀ a < o, Ordinal → Ordinal} {c} (hc : ℵ₀ < cof c)
    (hc' : Cardinal.lift.{v, u} o.card < cof c) (hf : ∀ (i hi), ∀ b < c, f i hi b < c) {a} :
    a < c → nfpBFamily.{u, v} o f a < c :=
  nfpFamily_lt_ord_lift hc (by rwa [mk_ordinal_out]) fun i => hf _ _
#align ordinal.nfp_bfamily_lt_ord_lift Ordinal.nfpBFamily_lt_ord_lift

theorem nfpBFamily_lt_ord {o : Ordinal} {f : ∀ a < o, Ordinal → Ordinal} {c} (hc : ℵ₀ < cof c)
    (hc' : o.card < cof c) (hf : ∀ (i hi), ∀ b < c, f i hi b < c) {a} :
    a < c → nfpBFamily.{u, u} o f a < c :=
  nfpBFamily_lt_ord_lift hc (by rwa [o.card.lift_id]) hf
#align ordinal.nfp_bfamily_lt_ord Ordinal.nfpBFamily_lt_ord

theorem nfp_lt_ord {f : Ordinal → Ordinal} {c} (hc : ℵ₀ < cof c) (hf : ∀ i < c, f i < c) {a} :
    a < c → nfp f a < c :=
  nfpFamily_lt_ord_lift hc (by simpa using Cardinal.one_lt_aleph0.trans hc) fun _ => hf
#align ordinal.nfp_lt_ord Ordinal.nfp_lt_ord

theorem exists_blsub_cof (o : Ordinal) : ∃ f : ∀ a < (cof o).ord, Ordinal, blsub.{u, u} _ f = o :=
  by
  rcases exists_lsub_cof o with ⟨ι, f, hf, hι⟩
  rcases Cardinal.ord_eq ι with ⟨r, hr, hι'⟩
  rw [← @blsub_eq_lsub' ι r hr] at hf
  rw [← hι, hι']
  exact ⟨_, hf⟩
#align ordinal.exists_blsub_cof Ordinal.exists_blsub_cof

theorem le_cof_iff_blsub {b : Ordinal} {a : Cardinal} :
    a ≤ cof b ↔ ∀ {o} (f : ∀ a < o, Ordinal), blsub.{u, u} o f = b → a ≤ o.card :=
  le_cof_iff_lsub.trans
    ⟨fun H o f hf => by simpa using H _ hf, fun H ι f hf =>
      by
      rcases Cardinal.ord_eq ι with ⟨r, hr, hι'⟩
      rw [← @blsub_eq_lsub' ι r hr] at hf
      simpa using H _ hf⟩
#align ordinal.le_cof_iff_blsub Ordinal.le_cof_iff_blsub

theorem cof_blsub_le_lift {o} (f : ∀ a < o, Ordinal) :
    cof (blsub.{u, v} o f) ≤ Cardinal.lift.{v, u} o.card := by
  rw [← mk_ordinal_out o]
  exact cof_lsub_le_lift _
#align ordinal.cof_blsub_le_lift Ordinal.cof_blsub_le_lift

theorem cof_blsub_le {o} (f : ∀ a < o, Ordinal) : cof (blsub.{u, u} o f) ≤ o.card := by
  rw [← o.card.lift_id]
  exact cof_blsub_le_lift f
#align ordinal.cof_blsub_le Ordinal.cof_blsub_le

theorem blsub_lt_ord_lift {o : Ordinal.{u}} {f : ∀ a < o, Ordinal} {c : Ordinal}
    (ho : Cardinal.lift.{v, u} o.card < c.cof) (hf : ∀ i hi, f i hi < c) : blsub.{u, v} o f < c :=
  lt_of_le_of_ne (blsub_le hf) fun h =>
    ho.not_le (by simpa [← supᵢ_ord, hf, h] using cof_blsub_le_lift.{u, v} f)
#align ordinal.blsub_lt_ord_lift Ordinal.blsub_lt_ord_lift

theorem blsub_lt_ord {o : Ordinal} {f : ∀ a < o, Ordinal} {c : Ordinal} (ho : o.card < c.cof)
    (hf : ∀ i hi, f i hi < c) : blsub.{u, u} o f < c :=
  blsub_lt_ord_lift (by rwa [o.card.lift_id]) hf
#align ordinal.blsub_lt_ord Ordinal.blsub_lt_ord

theorem cof_bsup_le_lift {o : Ordinal} {f : ∀ a < o, Ordinal} (H : ∀ i h, f i h < bsup.{u, v} o f) :
    cof (bsup.{u, v} o f) ≤ Cardinal.lift.{v, u} o.card := by
  rw [← bsup_eq_blsub_iff_lt_bsup.{u, v}] at H
  rw [H]
  exact cof_blsub_le_lift.{u, v} f
#align ordinal.cof_bsup_le_lift Ordinal.cof_bsup_le_lift

theorem cof_bsup_le {o : Ordinal} {f : ∀ a < o, Ordinal} :
    (∀ i h, f i h < bsup.{u, u} o f) → cof (bsup.{u, u} o f) ≤ o.card := by
  rw [← o.card.lift_id]
  exact cof_bsup_le_lift
#align ordinal.cof_bsup_le Ordinal.cof_bsup_le

theorem bsup_lt_ord_lift {o : Ordinal} {f : ∀ a < o, Ordinal} {c : Ordinal}
    (ho : Cardinal.lift.{v, u} o.card < c.cof) (hf : ∀ i hi, f i hi < c) : bsup.{u, v} o f < c :=
  (bsup_le_blsub f).trans_lt (blsub_lt_ord_lift ho hf)
#align ordinal.bsup_lt_ord_lift Ordinal.bsup_lt_ord_lift

theorem bsup_lt_ord {o : Ordinal} {f : ∀ a < o, Ordinal} {c : Ordinal} (ho : o.card < c.cof) :
    (∀ i hi, f i hi < c) → bsup.{u, u} o f < c :=
  bsup_lt_ord_lift (by rwa [o.card.lift_id])
#align ordinal.bsup_lt_ord Ordinal.bsup_lt_ord

/-! ### Basic results -/

@[simp]
theorem cof_zero : cof 0 = 0 :=
  (cof_le_card 0).antisymm (Cardinal.zero_le _)
#align ordinal.cof_zero Ordinal.cof_zero

@[simp]
theorem cof_eq_zero {o} : cof o = 0 ↔ o = 0 :=
  ⟨inductionOn o fun α r _ z =>
      let ⟨S, hl, e⟩ := cof_eq r
      type_eq_zero_iff_isEmpty.2 <|
        ⟨fun a =>
          let ⟨b, h, _⟩ := hl a
          (mk_eq_zero_iff.1 (e.trans z)).elim' ⟨_, h⟩⟩,
    fun e => by simp [e]⟩
#align ordinal.cof_eq_zero Ordinal.cof_eq_zero

theorem cof_ne_zero {o} : cof o ≠ 0 ↔ o ≠ 0 :=
  cof_eq_zero.not
#align ordinal.cof_ne_zero Ordinal.cof_ne_zero

@[simp]
theorem cof_succ (o) : cof (succ o) = 1 := by
  apply le_antisymm
  · refine' inductionOn o fun α r _ => _
    change cof (type _) ≤ _
    rw [← (_ : (#_) = 1)]
    apply cof_type_le
    · refine' fun a => ⟨Sum.inr PUnit.unit, Set.mem_singleton _, _⟩
      rcases a with (a | ⟨⟨⟨⟩⟩⟩) <;> simp [EmptyRelation]
    · rw [Cardinal.mk_fintype, Set.card_singleton]
      simp
  · rw [← Cardinal.succ_zero, succ_le_iff]
    simpa [lt_iff_le_and_ne, Cardinal.zero_le] using fun h =>
      succ_ne_zero o (cof_eq_zero.1 (Eq.symm h))
#align ordinal.cof_succ Ordinal.cof_succ

@[simp]
theorem cof_eq_one_iff_is_succ {o} : cof.{u} o = 1 ↔ ∃ a, o = succ a :=
  ⟨inductionOn o fun α r _ z => by
      skip
      rcases cof_eq r with ⟨S, hl, e⟩; rw [z] at e
      cases' mk_ne_zero_iff.1 (by rw [e]; exact one_ne_zero) with a
      refine'
        ⟨typein r a,
          Eq.symm <|
            Quotient.sound
              ⟨RelIso.ofSurjective (RelEmbedding.ofMonotone _ fun x y => _) fun x => _⟩⟩
      · apply Sum.rec <;> [exact Subtype.val, exact fun _ => a]
      · rcases x with (x | ⟨⟨⟨⟩⟩⟩) <;> rcases y with (y | ⟨⟨⟨⟩⟩⟩) <;>
          simp [Subrel, Order.Preimage, EmptyRelation]
        exact x.2
      · suffices : r x a ∨ ∃ _ : PUnit.{u}, ↑a = x
        . convert this
          dsimp [RelEmbedding.ofMonotone]; simp
        rcases trichotomous_of r x a with (h | h | h)
        · exact Or.inl h
        · exact Or.inr ⟨PUnit.unit, h.symm⟩
        · rcases hl x with ⟨a', aS, hn⟩
          rw [(_ : ↑a = a')] at h
          · exact absurd h hn
          refine' congr_arg Subtype.val (_ : a = ⟨a', aS⟩)
          haveI := le_one_iff_subsingleton.1 (le_of_eq e)
          apply Subsingleton.elim,
    fun ⟨a, e⟩ => by simp [e]⟩
#align ordinal.cof_eq_one_iff_is_succ Ordinal.cof_eq_one_iff_is_succ

/-- A fundamental sequence for `a` is an increasing sequence of length `o = cof a` that converges at
    `a`. We provide `o` explicitly in order to avoid type rewrites. -/
def IsFundamentalSequence (a o : Ordinal.{u}) (f : ∀ b < o, Ordinal.{u}) : Prop :=
  o ≤ a.cof.ord ∧ (∀ {i j} (hi hj), i < j → f i hi < f j hj) ∧ blsub.{u, u} o f = a
#align ordinal.is_fundamental_sequence Ordinal.IsFundamentalSequence

namespace IsFundamentalSequence

variable {a o : Ordinal.{u}} {f : ∀ b < o, Ordinal.{u}}

protected theorem cof_eq (hf : IsFundamentalSequence a o f) : a.cof.ord = o :=
  hf.1.antisymm' <| by
    rw [← hf.2.2]
    exact (ord_le_ord.2 (cof_blsub_le f)).trans (ord_card_le o)
#align ordinal.is_fundamental_sequence.cof_eq Ordinal.IsFundamentalSequence.cof_eq

protected theorem strict_mono (hf : IsFundamentalSequence a o f) {i j} :
    ∀ hi hj, i < j → f i hi < f j hj :=
  hf.2.1
#align ordinal.is_fundamental_sequence.strict_mono Ordinal.IsFundamentalSequence.strict_mono

theorem blsub_eq (hf : IsFundamentalSequence a o f) : blsub.{u, u} o f = a :=
  hf.2.2
#align ordinal.is_fundamental_sequence.blsub_eq Ordinal.IsFundamentalSequence.blsub_eq

theorem ord_cof (hf : IsFundamentalSequence a o f) :
    IsFundamentalSequence a a.cof.ord fun i hi => f i (hi.trans_le (by rw [hf.cof_eq])) := by
  have H := hf.cof_eq
  subst H
  exact hf
#align ordinal.is_fundamental_sequence.ord_cof Ordinal.IsFundamentalSequence.ord_cof

theorem id_of_le_cof (h : o ≤ o.cof.ord) : IsFundamentalSequence o o fun a _ => a :=
  ⟨h, @fun _ _ _ _ => id, blsub_id o⟩
#align ordinal.is_fundamental_sequence.id_of_le_cof Ordinal.IsFundamentalSequence.id_of_le_cof

protected theorem zero {f : ∀ b < (0 : Ordinal), Ordinal} : IsFundamentalSequence 0 0 f :=
  ⟨by rw [cof_zero, ord_zero], @fun i j hi => (Ordinal.not_lt_zero i hi).elim, blsub_zero f⟩
#align ordinal.is_fundamental_sequence.zero Ordinal.IsFundamentalSequence.zero

protected theorem succ : IsFundamentalSequence (succ o) 1 fun _ _ => o := by
  refine' ⟨_, @fun i j hi hj h => _, blsub_const Ordinal.one_ne_zero o⟩
  · rw [cof_succ, ord_one]
  · rw [lt_one_iff_zero] at hi hj
    rw [hi, hj] at h
    exact h.false.elim
#align ordinal.is_fundamental_sequence.succ Ordinal.IsFundamentalSequence.succ

protected theorem monotone (hf : IsFundamentalSequence a o f) {i j : Ordinal} (hi : i < o)
    (hj : j < o) (hij : i ≤ j) : f i hi ≤ f j hj := by
  rcases lt_or_eq_of_le hij with (hij | rfl)
  · exact (hf.2.1 hi hj hij).le
  · rfl
#align ordinal.is_fundamental_sequence.monotone Ordinal.IsFundamentalSequence.monotone

theorem trans {a o o' : Ordinal.{u}} {f : ∀ b < o, Ordinal.{u}} (hf : IsFundamentalSequence a o f)
    {g : ∀ b < o', Ordinal.{u}} (hg : IsFundamentalSequence o o' g) :
    IsFundamentalSequence a o' fun i hi =>
      f (g i hi) (by rw [← hg.2.2]; apply lt_blsub) := by
  refine' ⟨_, @fun i j _ _ h => hf.2.1 _ _ (hg.2.1 _ _ h), _⟩
  · rw [hf.cof_eq]
    exact hg.1.trans (ord_cof_le o)
  · rw [@blsub_comp.{u, u, u} o _ f (@IsFundamentalSequence.monotone _ _ f hf)]
    exact hf.2.2
    exact hg.2.2
#align ordinal.is_fundamental_sequence.trans Ordinal.IsFundamentalSequence.trans

end IsFundamentalSequence

/-- Every ordinal has a fundamental sequence. -/
theorem exists_fundamental_sequence (a : Ordinal.{u}) : ∃ f, IsFundamentalSequence a a.cof.ord f :=
  by
  suffices h : ∃ o f, IsFundamentalSequence a o f
  · rcases h with ⟨o, f, hf⟩
    exact ⟨_, hf.ord_cof⟩
  rcases exists_lsub_cof a with ⟨ι, f, hf, hι⟩
  rcases ord_eq ι with ⟨r, wo, hr⟩
  haveI := wo
  let r' := Subrel r { i | ∀ j, r j i → f j < f i }
  let hrr' : r' ↪r r := Subrel.relEmbedding _ _
  haveI := hrr'.isWellOrder
  refine'
    ⟨_, _, hrr'.ordinal_type_le.trans _, @fun i j _ h _ => (enum r' j h).prop _ _,
      le_antisymm (blsub_le fun i hi => lsub_le_iff.1 hf.le _) _⟩
  · rw [← hι, hr]
  · change r (hrr'.1 _) (hrr'.1 _)
    rwa [hrr'.2, @enum_lt_enum _ r']
  · rw [← hf, lsub_le_iff]
    intro i
    suffices h : ∃ i' hi', f i ≤ bfamilyOfFamily' r' (fun i => f i) i' hi'
    · rcases h with ⟨i', hi', hfg⟩
      exact hfg.trans_lt (lt_blsub _ _ _)
    by_cases h : ∀ j, r j i → f j < f i
    · refine' ⟨typein r' ⟨i, h⟩, typein_lt_type _ _, _⟩
      rw [bfamilyOfFamily'_typein]
    · push_neg  at h
      cases' wo.wf.min_mem _ h with hji hij
      refine' ⟨typein r' ⟨_, fun k hkj => lt_of_lt_of_le _ hij⟩, typein_lt_type _ _, _⟩
      · by_contra' H
        exact (wo.wf.not_lt_min _ h ⟨IsTrans.trans _ _ _ hkj hji, H⟩) hkj
      · rwa [bfamilyOfFamily'_typein]
#align ordinal.exists_fundamental_sequence Ordinal.exists_fundamental_sequence

@[simp]
theorem cof_cof (a : Ordinal.{u}) : cof (cof a).ord = cof a := by
  cases' exists_fundamental_sequence a with f hf
  cases' exists_fundamental_sequence a.cof.ord with g hg
  exact ord_injective (hf.trans hg).cof_eq.symm
#align ordinal.cof_cof Ordinal.cof_cof

protected theorem IsNormal.isFundamentalSequence {f : Ordinal.{u} → Ordinal.{u}} (hf : IsNormal f)
    {a o} (ha : IsLimit a) {g} (hg : IsFundamentalSequence a o g) :
    IsFundamentalSequence (f a) o fun b hb => f (g b hb) := by
  refine' ⟨_, @fun i j _ _ h => hf.strictMono (hg.2.1 _ _ h), _⟩
  · rcases exists_lsub_cof (f a) with ⟨ι, f', hf', hι⟩
    rw [← hg.cof_eq, ord_le_ord, ← hι]
    suffices (lsub.{u, u} fun i => infₛ { b : Ordinal | f' i ≤ f b }) = a
      by
      rw [← this]
      apply cof_lsub_le
    have H : ∀ i, ∃ b < a, f' i ≤ f b := fun i => by
      have := lt_lsub.{u, u} f' i
      rw [hf', ← IsNormal.blsub_eq.{u, u} hf ha, lt_blsub_iff] at this
      simpa using this
    refine' (lsub_le fun i => _).antisymm (le_of_forall_lt fun b hb => _)
    · rcases H i with ⟨b, hb, hb'⟩
      exact lt_of_le_of_lt (cinfₛ_le' hb') hb
    · have := hf.strictMono hb
      rw [← hf', lt_lsub_iff] at this
      cases' this with i hi
      rcases H i with ⟨b, _, hb⟩
      exact
        ((le_cinfₛ_iff'' ⟨b, by exact hb⟩).2 fun c hc =>
          hf.strictMono.le_iff_le.1 (hi.trans hc)).trans_lt (lt_lsub _ i)
  · rw [@blsub_comp.{u, u, u} a _ (fun b _ => f b) (@fun i j _ _ h => hf.strictMono.monotone h) g
        hg.2.2]
    exact IsNormal.blsub_eq.{u, u} hf ha
#align ordinal.is_normal.is_fundamental_sequence Ordinal.IsNormal.isFundamentalSequence

theorem IsNormal.cof_eq {f} (hf : IsNormal f) {a} (ha : IsLimit a) : cof (f a) = cof a :=
  let ⟨_, hg⟩ := exists_fundamental_sequence a
  ord_injective (hf.isFundamentalSequence ha hg).cof_eq
#align ordinal.is_normal.cof_eq Ordinal.IsNormal.cof_eq

theorem IsNormal.cof_le {f} (hf : IsNormal f) (a) : cof a ≤ cof (f a) := by
  rcases zero_or_succ_or_limit a with (rfl | ⟨b, rfl⟩ | ha)
  · rw [cof_zero]
    exact zero_le _
  · rw [cof_succ, Cardinal.one_le_iff_ne_zero, cof_ne_zero, ← Ordinal.pos_iff_ne_zero]
    exact (Ordinal.zero_le (f b)).trans_lt (hf.1 b)
  · rw [hf.cof_eq ha]
#align ordinal.is_normal.cof_le Ordinal.IsNormal.cof_le

@[simp]
theorem cof_add (a b : Ordinal) : b ≠ 0 → cof (a + b) = cof b := fun h => by
  rcases zero_or_succ_or_limit b with (rfl | ⟨c, rfl⟩ | hb)
  · contradiction
  · rw [add_succ, cof_succ, cof_succ]
  · exact (add_isNormal a).cof_eq hb
#align ordinal.cof_add Ordinal.cof_add

theorem aleph0_le_cof {o} : ℵ₀ ≤ cof o ↔ IsLimit o := by
  rcases zero_or_succ_or_limit o with (rfl | ⟨o, rfl⟩ | l)
  · simp [not_zero_isLimit, Cardinal.aleph0_ne_zero]
  · simp [not_succ_isLimit, Cardinal.one_lt_aleph0]
  · simp [l]
    refine' le_of_not_lt fun h => _
    cases' Cardinal.lt_aleph0.1 h with n e
    have := cof_cof o
    rw [e, ord_nat] at this
    cases n
    · simp at e
      simp [e, not_zero_isLimit] at l
    · rw [nat_cast_succ, cof_succ] at this
      rw [← this, cof_eq_one_iff_is_succ] at e
      rcases e with ⟨a, rfl⟩
      exact not_succ_isLimit _ l
#align ordinal.aleph_0_le_cof Ordinal.aleph0_le_cof

@[simp]
theorem aleph'_cof {o : Ordinal} (ho : o.IsLimit) : (aleph' o).ord.cof = o.cof :=
  aleph'_isNormal.cof_eq ho
#align ordinal.aleph'_cof Ordinal.aleph'_cof

@[simp]
theorem aleph_cof {o : Ordinal} (ho : o.IsLimit) : (aleph o).ord.cof = o.cof :=
  aleph_isNormal.cof_eq ho
#align ordinal.aleph_cof Ordinal.aleph_cof

@[simp]
theorem cof_omega : cof ω = ℵ₀ :=
  (aleph0_le_cof.2 omega_isLimit).antisymm' <|
    by
    rw [← card_omega]
    apply cof_le_card
#align ordinal.cof_omega Ordinal.cof_omega

theorem cof_eq' (r : α → α → Prop) [IsWellOrder α r] (h : IsLimit (type r)) :
    ∃ S : Set α, (∀ a, ∃ b ∈ S, r a b) ∧ (#S) = cof (type r) :=
  let ⟨S, H, e⟩ := cof_eq r
  ⟨S, fun a =>
    let a' := enum r _ (h.2 _ (typein_lt_type r a))
    let ⟨b, h, ab⟩ := H a'
    ⟨b, h,
      (IsOrderConnected.conn a b a' <|
            (typein_lt_typein r).1
              (by
                rw [typein_enum]
                exact lt_succ (typein _ _))).resolve_right
        ab⟩,
    e⟩
#align ordinal.cof_eq' Ordinal.cof_eq'

@[simp]
theorem cof_univ : cof univ.{u, v} = Cardinal.univ.{u, v} :=
  le_antisymm (cof_le_card _)
    (by
      refine' le_of_forall_lt fun c h => _
      rcases lt_univ'.1 h with ⟨c, rfl⟩
      rcases @cof_eq Ordinal.{u} (· < ·) _ with ⟨S, H, Se⟩
      rw [univ, ← lift_cof, ← Cardinal.lift_lift.{u, u + 1, v}, Cardinal.lift_lt, ← Se]
      refine' lt_of_not_ge fun h => _
      cases' Cardinal.lift_down h with a e
      refine' Quotient.inductionOn a (fun α e => _) e
      cases' Quotient.exact e with f
      have f := Equiv.ulift.symm.trans f
      let g a := (f a).1
      let o := succ (sup.{u, u} g)
      rcases H o with ⟨b, h, l⟩
      refine' l (lt_succ_iff.2 _)
      rw [← show g (f.symm ⟨b, h⟩) = b by simp]
      apply le_sup)
#align ordinal.cof_univ Ordinal.cof_univ

/-! ### Infinite pigeonhole principle -/

/-- If the union of s is unbounded and s is smaller than the cofinality,
  then s has an unbounded member -/
theorem unbounded_of_unbounded_unionₛ (r : α → α → Prop) [wo : IsWellOrder α r] {s : Set (Set α)}
    (h₁ : Unbounded r <| ⋃₀ s) (h₂ : (#s) < StrictOrder.cof r) : ∃ x ∈ s, Unbounded r x := by
  by_contra' h
  simp_rw [not_unbounded_iff] at h
  let f : s → α := fun x : s => wo.wf.sup x (h x.1 x.2)
  refine' h₂.not_le (le_trans (cinfₛ_le' ⟨range f, fun x => _, rfl⟩) mk_range_le)
  rcases h₁ x with ⟨y, ⟨c, hc, hy⟩, hxy⟩
  exact ⟨f ⟨c, hc⟩, mem_range_self _, fun hxz => hxy (Trans.trans (wo.wf.lt_sup _ hy) hxz)⟩
#align ordinal.unbounded_of_unbounded_sUnion Ordinal.unbounded_of_unbounded_unionₛ

/-- If the union of s is unbounded and s is smaller than the cofinality,
  then s has an unbounded member -/
theorem unbounded_of_unbounded_unionᵢ {α β : Type u} (r : α → α → Prop) [wo : IsWellOrder α r]
    (s : β → Set α) (h₁ : Unbounded r <| ⋃ x, s x) (h₂ : (#β) < StrictOrder.cof r) :
    ∃ x : β, Unbounded r (s x) := by
  rw [← unionₛ_range] at h₁
  rcases unbounded_of_unbounded_unionₛ r h₁ (mk_range_le.trans_lt h₂) with ⟨_, ⟨x, rfl⟩, u⟩
  exact ⟨x, u⟩
#align ordinal.unbounded_of_unbounded_Union Ordinal.unbounded_of_unbounded_unionᵢ

/-- The infinite pigeonhole principle -/
theorem infinite_pigeonhole {β α : Type u} (f : β → α) (h₁ : ℵ₀ ≤ (#β)) (h₂ : (#α) < (#β).ord.cof) :
    ∃ a : α, (#f ⁻¹' {a}) = (#β) := by
  have : ∃ a, (#β) ≤ (#f ⁻¹' {a}) := by
    by_contra' h
    apply mk_univ.not_lt
    rw [← preimage_univ, ← unionᵢ_of_singleton, preimage_unionᵢ]
    exact
      mk_unionᵢ_le_sum_mk.trans_lt
        ((sum_le_supᵢ _).trans_lt <| mul_lt_of_lt h₁ (h₂.trans_le <| cof_ord_le _) (supᵢ_lt h₂ h))
  cases' this with x h
  refine' ⟨x, h.antisymm' _⟩
  rw [le_mk_iff_exists_set]
  exact ⟨_, rfl⟩
#align ordinal.infinite_pigeonhole Ordinal.infinite_pigeonhole

/-- Pigeonhole principle for a cardinality below the cardinality of the domain -/
theorem infinite_pigeonhole_card {β α : Type u} (f : β → α) (θ : Cardinal) (hθ : θ ≤ (#β))
    (h₁ : ℵ₀ ≤ θ) (h₂ : (#α) < θ.ord.cof) : ∃ a : α, θ ≤ (#f ⁻¹' {a}) := by
  rcases le_mk_iff_exists_set.1 hθ with ⟨s, rfl⟩
  cases' infinite_pigeonhole (f ∘ Subtype.val : s → α) h₁ h₂ with a ha
  use a; rw [← ha, @preimage_comp _ _ _ Subtype.val f]
  exact mk_preimage_of_injective _ _ Subtype.val_injective
#align ordinal.infinite_pigeonhole_card Ordinal.infinite_pigeonhole_card

theorem infinite_pigeonhole_set {β α : Type u} {s : Set β} (f : s → α) (θ : Cardinal)
    (hθ : θ ≤ (#s)) (h₁ : ℵ₀ ≤ θ) (h₂ : (#α) < θ.ord.cof) :
    ∃ (a : α)(t : Set β)(h : t ⊆ s), θ ≤ (#t) ∧ ∀ ⦃x⦄ (hx : x ∈ t), f ⟨x, h hx⟩ = a := by
  cases' infinite_pigeonhole_card f θ hθ h₁ h₂ with a ha
  refine' ⟨a, { x | ∃ h, f ⟨x, h⟩ = a }, _, _, _⟩
  · rintro x ⟨hx, _⟩
    exact hx
  · refine'
      ha.trans
        (ge_of_eq <|
          Quotient.sound ⟨Equiv.trans _ (Equiv.subtypeSubtypeEquivSubtypeExists _ _).symm⟩)
    simp only [coe_eq_subtype, mem_singleton_iff, mem_preimage, mem_setOf_eq]
    rfl
  rintro x ⟨_, hx'⟩; exact hx'
#align ordinal.infinite_pigeonhole_set Ordinal.infinite_pigeonhole_set

end Ordinal

/-! ### Regular and inaccessible cardinals -/

namespace Cardinal

open Ordinal

--Porting note: commented out, doesn't seem necessary
-- mathport name: cardinal.pow
--local infixr:0 "^" => @HPow.hPow Cardinal Cardinal Cardinal instHPow

/-- A cardinal is a limit if it is not zero or a successor
  cardinal. Note that `ℵ₀` is a limit cardinal by this definition. -/
def IsLimit (c : Cardinal) : Prop :=
  c ≠ 0 ∧ ∀ x < c, succ x < c
#align cardinal.is_limit Cardinal.IsLimit

theorem IsLimit.ne_zero {c} (h : IsLimit c) : c ≠ 0 :=
  h.1
#align cardinal.is_limit.ne_zero Cardinal.IsLimit.ne_zero

theorem IsLimit.succ_lt {x c} (h : IsLimit c) : x < c → succ x < c :=
  h.2 x
#align cardinal.is_limit.succ_lt Cardinal.IsLimit.succ_lt

theorem IsLimit.aleph0_le {c} (h : IsLimit c) : ℵ₀ ≤ c := by
  by_contra' h'
  rcases lt_aleph0.1 h' with ⟨_ | n, rfl⟩
  · exact h.1.irrefl
  · simpa using h.2 n
#align cardinal.is_limit.aleph_0_le Cardinal.IsLimit.aleph0_le

/-- A cardinal is a strong limit if it is not zero and it is
  closed under powersets. Note that `ℵ₀` is a strong limit by this definition. -/
def IsStrongLimit (c : Cardinal) : Prop :=
  c ≠ 0 ∧ ∀ x < c, (2^x) < c
#align cardinal.is_strong_limit Cardinal.IsStrongLimit

theorem IsStrongLimit.ne_zero {c} (h : IsStrongLimit c) : c ≠ 0 :=
  h.1
#align cardinal.is_strong_limit.ne_zero Cardinal.IsStrongLimit.ne_zero

theorem IsStrongLimit.two_power_lt {x c} (h : IsStrongLimit c) : x < c → (2^x) < c :=
  h.2 x
#align cardinal.is_strong_limit.two_power_lt Cardinal.IsStrongLimit.two_power_lt

theorem isStrongLimit_aleph0 : IsStrongLimit ℵ₀ :=
  ⟨aleph0_ne_zero, fun x hx => by
    rcases lt_aleph0.1 hx with ⟨n, rfl⟩
    exact_mod_cast nat_lt_aleph0 (2 ^ n)⟩
#align cardinal.is_strong_limit_aleph_0 Cardinal.isStrongLimit_aleph0

theorem IsStrongLimit.isLimit {c} (H : IsStrongLimit c) : IsLimit c :=
  ⟨H.1, fun x h => (succ_le_of_lt <| cantor x).trans_lt (H.2 _ h)⟩
#align cardinal.is_strong_limit.is_limit Cardinal.IsStrongLimit.isLimit

theorem isLimit_aleph0 : IsLimit ℵ₀ :=
  isStrongLimit_aleph0.isLimit
#align cardinal.is_limit_aleph_0 Cardinal.isLimit_aleph0

theorem isStrongLimit_beth {o : Ordinal} (H : ∀ a < o, succ a < o) : IsStrongLimit (beth o) := by
  rcases eq_or_ne o 0 with (rfl | h)
  · rw [beth_zero]
    exact isStrongLimit_aleph0
  · refine' ⟨beth_ne_zero o, fun a ha => _⟩
    rw [beth_limit ⟨h, H⟩] at ha
    rcases exists_lt_of_lt_csupᵢ' ha with ⟨⟨i, hi⟩, ha⟩
    have := power_le_power_left two_ne_zero ha.le
    rw [← beth_succ] at this
    exact this.trans_lt (beth_lt.2 (H i hi))
#align cardinal.is_strong_limit_beth Cardinal.isStrongLimit_beth

theorem mk_subset_mk_lt_cof {α : Type _} (h : ∀ x < #α, (2^x) < (#α)) :
    (#{ s : Set α // (#s) < cof (#α).ord }) = (#α) := by
  rcases eq_or_ne (#α) 0 with (ha | ha)
  · rw [ha]
    simp [fun s => (Cardinal.zero_le s).not_lt]
  have h' : IsStrongLimit (#α) := ⟨ha, h⟩
  rcases ord_eq α with ⟨r, wo, hr⟩
  haveI := wo
  apply le_antisymm
  · conv_rhs => rw [← mk_bounded_subset h hr]
    apply mk_le_mk_of_subset
    intro s hs
    rw [hr] at hs
    exact lt_cof_type hs
  · refine' @mk_le_of_injective α _ (fun x => Subtype.mk {x} _) _
    · rw [mk_singleton]
      exact one_lt_aleph0.trans_le (aleph0_le_cof.2 (ord_isLimit h'.isLimit.aleph0_le))
    · intro a b hab
      simpa [singleton_eq_singleton_iff] using hab
#align cardinal.mk_subset_mk_lt_cof Cardinal.mk_subset_mk_lt_cof

/-- A cardinal is regular if it is infinite and it equals its own cofinality. -/
def IsRegular (c : Cardinal) : Prop :=
  ℵ₀ ≤ c ∧ c ≤ c.ord.cof
#align cardinal.is_regular Cardinal.IsRegular

theorem IsRegular.aleph0_le {c : Cardinal} (H : c.IsRegular) : ℵ₀ ≤ c :=
  H.1
#align cardinal.is_regular.aleph_0_le Cardinal.IsRegular.aleph0_le

theorem IsRegular.cof_eq {c : Cardinal} (H : c.IsRegular) : c.ord.cof = c :=
  (cof_ord_le c).antisymm H.2
#align cardinal.is_regular.cof_eq Cardinal.IsRegular.cof_eq

theorem IsRegular.pos {c : Cardinal} (H : c.IsRegular) : 0 < c :=
  aleph0_pos.trans_le H.1
#align cardinal.is_regular.pos Cardinal.IsRegular.pos

theorem IsRegular.ord_pos {c : Cardinal} (H : c.IsRegular) : 0 < c.ord := by
  rw [Cardinal.lt_ord]
  exact H.pos
#align cardinal.is_regular.ord_pos Cardinal.IsRegular.ord_pos

theorem isRegular_cof {o : Ordinal} (h : o.IsLimit) : IsRegular o.cof :=
  ⟨aleph0_le_cof.2 h, (cof_cof o).ge⟩
#align cardinal.is_regular_cof Cardinal.isRegular_cof

theorem isRegular_aleph0 : IsRegular ℵ₀ :=
  ⟨le_rfl, by simp⟩
#align cardinal.is_regular_aleph_0 Cardinal.isRegular_aleph0

theorem isRegular_succ {c : Cardinal.{u}} (h : ℵ₀ ≤ c) : IsRegular (succ c) :=
  ⟨h.trans (le_succ c),
    succ_le_of_lt
      (by
        cases' Quotient.exists_rep (@succ Cardinal _ _ c) with α αe; simp at αe
        rcases ord_eq α with ⟨r, wo, re⟩; skip
        have := ord_isLimit (h.trans (le_succ _))
        rw [← αe, re] at this⊢
        rcases cof_eq' r this with ⟨S, H, Se⟩
        rw [← Se]
        apply lt_imp_lt_of_le_imp_le fun h => mul_le_mul_right' h c
        rw [mul_eq_self h, ← succ_le_iff, ← αe, ← sum_const']
        refine' le_trans _ (sum_le_sum (fun (x : S) => card (typein r (x : α))) _ fun i => _)
        · simp only [← card_typein, ← mk_sigma]
          exact
            ⟨Embedding.ofSurjective (fun x => x.2.1) fun a =>
                let ⟨b, h, ab⟩ := H a
                ⟨⟨⟨_, h⟩, _, ab⟩, rfl⟩⟩
        · rw [← lt_succ_iff, ← lt_ord, ← αe, re]
          apply typein_lt_type)⟩
#align cardinal.is_regular_succ Cardinal.isRegular_succ

theorem isRegular_aleph_one : IsRegular (aleph 1) := by
  rw [← succ_aleph0]
  exact isRegular_succ le_rfl
#align cardinal.is_regular_aleph_one Cardinal.isRegular_aleph_one

theorem isRegular_aleph'_succ {o : Ordinal} (h : ω ≤ o) : IsRegular (aleph' (succ o)) := by
  rw [aleph'_succ]
  exact isRegular_succ (aleph0_le_aleph'.2 h)
#align cardinal.is_regular_aleph'_succ Cardinal.isRegular_aleph'_succ

theorem isRegular_aleph_succ (o : Ordinal) : IsRegular (aleph (succ o)) := by
  rw [aleph_succ]
  exact isRegular_succ (aleph0_le_aleph o)
#align cardinal.is_regular_aleph_succ Cardinal.isRegular_aleph_succ

/-- A function whose codomain's cardinality is infinite but strictly smaller than its domain's
has a fiber with cardinality strictly great than the codomain.
-/
theorem infinite_pigeonhole_card_lt {β α : Type u} (f : β → α) (w : (#α) < (#β)) (w' : ℵ₀ ≤ (#α)) :
    ∃ a : α, (#α) < (#f ⁻¹' {a}) := by
  simp_rw [← succ_le_iff]
  exact
    Ordinal.infinite_pigeonhole_card f (succ (#α)) (succ_le_of_lt w) (w'.trans (lt_succ _).le)
      ((lt_succ _).trans_le (isRegular_succ w').2.ge)
#align cardinal.infinite_pigeonhole_card_lt Cardinal.infinite_pigeonhole_card_lt

/-- A function whose codomain's cardinality is infinite but strictly smaller than its domain's
has an infinite fiber.
-/
theorem exists_infinite_fiber {β α : Type _} (f : β → α) (w : (#α) < (#β)) (w' : Infinite α) :
    ∃ a : α, Infinite (f ⁻¹' {a}) := by
  simp_rw [Cardinal.infinite_iff] at w'⊢
  cases' infinite_pigeonhole_card_lt f w w' with a ha
  exact ⟨a, w'.trans ha.le⟩
#align cardinal.exists_infinite_fiber Cardinal.exists_infinite_fiber

/-- If an infinite type `β` can be expressed as a union of finite sets,
then the cardinality of the collection of those finite sets
must be at least the cardinality of `β`.
-/
theorem le_range_of_union_finset_eq_top {α β : Type _} [Infinite β] (f : α → Finset β)
    (w : (⋃ a, (f a : Set β)) = ⊤) : (#β) ≤ (#range f) := by
  have k : _root_.Infinite (range f) := by
    rw [infinite_coe_iff]
    apply mt (union_finset_finite_of_range_finite f)
    rw [w]
    exact infinite_univ
  by_contra h
  simp only [not_le] at h
  let u : ∀ b, ∃ a, b ∈ f a := fun b => by simpa using (w.ge : _) (Set.mem_univ b)
  let u' : β → range f := fun b => ⟨f (u b).choose, by simp⟩
  have v' : ∀ a, u' ⁻¹' {⟨f a, by simp⟩} ≤ f a :=
    by
    rintro a p m
    simp at m
    rw [← m]
    apply fun b => (u b).choose_spec
  obtain ⟨⟨-, ⟨a, rfl⟩⟩, p⟩ := exists_infinite_fiber u' h k
  exact (@Infinite.of_injective _ _ p (inclusion (v' a)) (inclusion_injective _)).false
#align cardinal.le_range_of_union_finset_eq_top Cardinal.le_range_of_union_finset_eq_top

theorem lsub_lt_ord_lift_of_isRegular {ι} {f : ι → Ordinal} {c} (hc : IsRegular c)
    (hι : Cardinal.lift.{v, u} (#ι) < c) : (∀ i, f i < c.ord) → Ordinal.lsub.{u, v} f < c.ord :=
  lsub_lt_ord_lift (by rwa [hc.cof_eq])
#align cardinal.lsub_lt_ord_lift_of_is_regular Cardinal.lsub_lt_ord_lift_of_isRegular

theorem lsub_lt_ord_of_isRegular {ι} {f : ι → Ordinal} {c} (hc : IsRegular c) (hι : (#ι) < c) :
    (∀ i, f i < c.ord) → Ordinal.lsub f < c.ord :=
  lsub_lt_ord (by rwa [hc.cof_eq])
#align cardinal.lsub_lt_ord_of_is_regular Cardinal.lsub_lt_ord_of_isRegular

theorem sup_lt_ord_lift_of_isRegular {ι} {f : ι → Ordinal} {c} (hc : IsRegular c)
    (hι : Cardinal.lift.{v, u} (#ι) < c) : (∀ i, f i < c.ord) → Ordinal.sup.{u, v} f < c.ord :=
  sup_lt_ord_lift (by rwa [hc.cof_eq])
#align cardinal.sup_lt_ord_lift_of_is_regular Cardinal.sup_lt_ord_lift_of_isRegular

theorem sup_lt_ord_of_isRegular {ι} {f : ι → Ordinal} {c} (hc : IsRegular c) (hι : (#ι) < c) :
    (∀ i, f i < c.ord) → Ordinal.sup f < c.ord :=
  sup_lt_ord (by rwa [hc.cof_eq])
#align cardinal.sup_lt_ord_of_is_regular Cardinal.sup_lt_ord_of_isRegular

theorem blsub_lt_ord_lift_of_isRegular {o : Ordinal} {f : ∀ a < o, Ordinal} {c} (hc : IsRegular c)
    (ho : Cardinal.lift.{v, u} o.card < c) :
    (∀ i hi, f i hi < c.ord) → Ordinal.blsub.{u, v} o f < c.ord :=
  blsub_lt_ord_lift (by rwa [hc.cof_eq])
#align cardinal.blsub_lt_ord_lift_of_is_regular Cardinal.blsub_lt_ord_lift_of_isRegular

theorem blsub_lt_ord_of_isRegular {o : Ordinal} {f : ∀ a < o, Ordinal} {c} (hc : IsRegular c)
    (ho : o.card < c) : (∀ i hi, f i hi < c.ord) → Ordinal.blsub o f < c.ord :=
  blsub_lt_ord (by rwa [hc.cof_eq])
#align cardinal.blsub_lt_ord_of_is_regular Cardinal.blsub_lt_ord_of_isRegular

theorem bsup_lt_ord_lift_of_isRegular {o : Ordinal} {f : ∀ a < o, Ordinal} {c} (hc : IsRegular c)
    (hι : Cardinal.lift.{v, u} o.card < c) :
    (∀ i hi, f i hi < c.ord) → Ordinal.bsup.{u, v} o f < c.ord :=
  bsup_lt_ord_lift (by rwa [hc.cof_eq])
#align cardinal.bsup_lt_ord_lift_of_is_regular Cardinal.bsup_lt_ord_lift_of_isRegular

theorem bsup_lt_ord_of_isRegular {o : Ordinal} {f : ∀ a < o, Ordinal} {c} (hc : IsRegular c)
    (hι : o.card < c) : (∀ i hi, f i hi < c.ord) → Ordinal.bsup o f < c.ord :=
  bsup_lt_ord (by rwa [hc.cof_eq])
#align cardinal.bsup_lt_ord_of_is_regular Cardinal.bsup_lt_ord_of_isRegular

theorem supᵢ_lt_lift_of_isRegular {ι} {f : ι → Cardinal} {c} (hc : IsRegular c)
    (hι : Cardinal.lift.{v, u} (#ι) < c) : (∀ i, f i < c) → supᵢ.{max u v + 1, u + 1} f < c :=
  supᵢ_lt_lift.{u, v} (by rwa [hc.cof_eq])
#align cardinal.supr_lt_lift_of_is_regular Cardinal.supᵢ_lt_lift_of_isRegular

theorem supᵢ_lt_of_isRegular {ι} {f : ι → Cardinal} {c} (hc : IsRegular c) (hι : (#ι) < c) :
    (∀ i, f i < c) → supᵢ f < c :=
  supᵢ_lt (by rwa [hc.cof_eq])
#align cardinal.supr_lt_of_is_regular Cardinal.supᵢ_lt_of_isRegular

theorem sum_lt_lift_of_isRegular {ι : Type u} {f : ι → Cardinal} {c : Cardinal} (hc : IsRegular c)
    (hι : Cardinal.lift.{v, u} (#ι) < c) (hf : ∀ i, f i < c) : sum f < c :=
  (sum_le_supᵢ_lift _).trans_lt <| mul_lt_of_lt hc.1 hι (supᵢ_lt_lift_of_isRegular hc hι hf)
#align cardinal.sum_lt_lift_of_is_regular Cardinal.sum_lt_lift_of_isRegular

theorem sum_lt_of_isRegular {ι : Type u} {f : ι → Cardinal} {c : Cardinal} (hc : IsRegular c)
    (hι : (#ι) < c) : (∀ i, f i < c) → sum f < c :=
  sum_lt_lift_of_isRegular.{u, u} hc (by rwa [lift_id])
#align cardinal.sum_lt_of_is_regular Cardinal.sum_lt_of_isRegular

theorem nfpFamily_lt_ord_lift_of_isRegular {ι} {f : ι → Ordinal → Ordinal} {c} (hc : IsRegular c)
    (hι : Cardinal.lift.{v, u} (#ι) < c) (hc' : c ≠ ℵ₀) (hf : ∀ (i), ∀ b < c.ord, f i b < c.ord) {a}
    (ha : a < c.ord) : nfpFamily.{u, v} f a < c.ord := by
  apply nfpFamily_lt_ord_lift.{u, v} _ _ hf ha <;> rw [hc.cof_eq]
  exact lt_of_le_of_ne hc.1 hc'.symm
  exact hι
#align cardinal.nfp_family_lt_ord_lift_of_is_regular Cardinal.nfpFamily_lt_ord_lift_of_isRegular

theorem nfpFamily_lt_ord_of_isRegular {ι} {f : ι → Ordinal → Ordinal} {c} (hc : IsRegular c)
    (hι : (#ι) < c) (hc' : c ≠ ℵ₀) {a} (hf : ∀ (i), ∀ b < c.ord, f i b < c.ord) :
    a < c.ord → nfpFamily.{u, u} f a < c.ord :=
  nfpFamily_lt_ord_lift_of_isRegular hc (by rwa [lift_id]) hc' hf
#align cardinal.nfp_family_lt_ord_of_is_regular Cardinal.nfpFamily_lt_ord_of_isRegular

theorem nfpBFamily_lt_ord_lift_of_isRegular {o : Ordinal} {f : ∀ a < o, Ordinal → Ordinal} {c}
    (hc : IsRegular c) (ho : Cardinal.lift.{v, u} o.card < c) (hc' : c ≠ ℵ₀)
    (hf : ∀ (i hi), ∀ b < c.ord, f i hi b < c.ord) {a} :
    a < c.ord → nfpBFamily.{u, v} o f a < c.ord :=
  nfpFamily_lt_ord_lift_of_isRegular hc (by rwa [mk_ordinal_out]) hc' fun i => hf _ _
#align cardinal.nfp_bfamily_lt_ord_lift_of_is_regular Cardinal.nfpBFamily_lt_ord_lift_of_isRegular

theorem nfpBFamily_lt_ord_of_isRegular {o : Ordinal} {f : ∀ a < o, Ordinal → Ordinal} {c}
    (hc : IsRegular c) (ho : o.card < c) (hc' : c ≠ ℵ₀)
    (hf : ∀ (i hi), ∀ b < c.ord, f i hi b < c.ord) {a} :
    a < c.ord → nfpBFamily.{u, u} o f a < c.ord :=
  nfpBFamily_lt_ord_lift_of_isRegular hc (by rwa [lift_id]) hc' hf
#align cardinal.nfp_bfamily_lt_ord_of_is_regular Cardinal.nfpBFamily_lt_ord_of_isRegular

theorem nfp_lt_ord_of_isRegular {f : Ordinal → Ordinal} {c} (hc : IsRegular c) (hc' : c ≠ ℵ₀)
    (hf : ∀ i < c.ord, f i < c.ord) {a} : a < c.ord → nfp f a < c.ord :=
  nfp_lt_ord
    (by
      rw [hc.cof_eq]
      exact lt_of_le_of_ne hc.1 hc'.symm)
    hf
#align cardinal.nfp_lt_ord_of_is_regular Cardinal.nfp_lt_ord_of_isRegular

theorem derivFamily_lt_ord_lift {ι} {f : ι → Ordinal → Ordinal} {c} (hc : IsRegular c)
    (hι : Cardinal.lift.{v, u} (#ι) < c) (hc' : c ≠ ℵ₀)
    (hf : ∀ (i), ∀ b < c.ord, f i b < c.ord) {a} :
    a < c.ord → derivFamily.{u, v} f a < c.ord := by
  have hω : ℵ₀ < c.ord.cof := by
    rw [hc.cof_eq]
    exact lt_of_le_of_ne hc.1 hc'.symm
  apply a.limitRecOn
  · rw [derivFamily_zero]
    exact nfpFamily_lt_ord_lift hω (by rwa [hc.cof_eq]) hf
  · intro b hb hb'
    rw [derivFamily_succ]
    exact
      nfpFamily_lt_ord_lift hω (by rwa [hc.cof_eq]) hf
        ((ord_isLimit hc.1).2 _ (hb ((lt_succ b).trans hb')))
  · intro b hb H hb'
    rw [derivFamily_limit f hb]
    exact
      bsup_lt_ord_of_isRegular.{u, v} hc (ord_lt_ord.1 ((ord_card_le b).trans_lt hb')) fun o' ho' =>
        H o' ho' (ho'.trans hb')
#align cardinal.deriv_family_lt_ord_lift Cardinal.derivFamily_lt_ord_lift

theorem derivFamily_lt_ord {ι} {f : ι → Ordinal → Ordinal} {c} (hc : IsRegular c) (hι : (#ι) < c)
    (hc' : c ≠ ℵ₀) (hf : ∀ (i), ∀ b < c.ord, f i b < c.ord) {a} :
    a < c.ord → derivFamily.{u, u} f a < c.ord :=
  derivFamily_lt_ord_lift hc (by rwa [lift_id]) hc' hf
#align cardinal.deriv_family_lt_ord Cardinal.derivFamily_lt_ord

theorem derivBFamily_lt_ord_lift {o : Ordinal} {f : ∀ a < o, Ordinal → Ordinal} {c}
    (hc : IsRegular c) (hι : Cardinal.lift.{v, u} o.card < c) (hc' : c ≠ ℵ₀)
    (hf : ∀ (i hi), ∀ b < c.ord, f i hi b < c.ord) {a} :
    a < c.ord → derivBFamily.{u, v} o f a < c.ord :=
  derivFamily_lt_ord_lift hc (by rwa [mk_ordinal_out]) hc' fun i => hf _ _
#align cardinal.deriv_bfamily_lt_ord_lift Cardinal.derivBFamily_lt_ord_lift

theorem derivBFamily_lt_ord {o : Ordinal} {f : ∀ a < o, Ordinal → Ordinal} {c} (hc : IsRegular c)
    (hι : o.card < c) (hc' : c ≠ ℵ₀) (hf : ∀ (i hi), ∀ b < c.ord, f i hi b < c.ord) {a} :
    a < c.ord → derivBFamily.{u, u} o f a < c.ord :=
  derivBFamily_lt_ord_lift hc (by rwa [lift_id]) hc' hf
#align cardinal.deriv_bfamily_lt_ord Cardinal.derivBFamily_lt_ord

theorem deriv_lt_ord {f : Ordinal.{u} → Ordinal} {c} (hc : IsRegular c) (hc' : c ≠ ℵ₀)
    (hf : ∀ i < c.ord, f i < c.ord) {a} : a < c.ord → deriv f a < c.ord :=
  derivFamily_lt_ord_lift hc
    (by simpa using Cardinal.one_lt_aleph0.trans (lt_of_le_of_ne hc.1 hc'.symm)) hc' fun _ => hf
#align cardinal.deriv_lt_ord Cardinal.deriv_lt_ord

/-- A cardinal is inaccessible if it is an uncountable regular strong limit cardinal. -/
def IsInaccessible (c : Cardinal) :=
  ℵ₀ < c ∧ IsRegular c ∧ IsStrongLimit c
#align cardinal.is_inaccessible Cardinal.IsInaccessible

theorem IsInaccessible.mk {c} (h₁ : ℵ₀ < c) (h₂ : c ≤ c.ord.cof) (h₃ : ∀ x < c, (2^x) < c) :
    IsInaccessible c :=
  ⟨h₁, ⟨h₁.le, h₂⟩, (aleph0_pos.trans h₁).ne', h₃⟩
#align cardinal.is_inaccessible.mk Cardinal.IsInaccessible.mk

-- Lean's foundations prove the existence of ℵ₀ many inaccessible cardinals
theorem univ_inaccessible : IsInaccessible univ.{u, v} :=
  IsInaccessible.mk (by simpa using lift_lt_univ' ℵ₀) (by simp) fun c h =>
    by
    rcases lt_univ'.1 h with ⟨c, rfl⟩
    rw [← lift_two_power.{u, max (u + 1) v}]
    apply lift_lt_univ'
#align cardinal.univ_inaccessible Cardinal.univ_inaccessible

theorem lt_power_cof {c : Cardinal.{u}} : ℵ₀ ≤ c → c < (c^cof c.ord) :=
  Quotient.inductionOn c fun α h => by
    rcases ord_eq α with ⟨r, wo, re⟩; skip
    have := ord_isLimit h
    rw [mk'_def, re] at this⊢
    rcases cof_eq' r this with ⟨S, H, Se⟩
    have := sum_lt_prod (fun a : S => #{ x // r x a }) (fun _ => #α) fun i => ?_
    · simp only [Cardinal.prod_const, Cardinal.lift_id, ← Se, ← mk_sigma, power_def] at this ⊢
      refine' lt_of_le_of_lt _ this
      refine' ⟨Embedding.ofSurjective _ _⟩
      · exact fun x => x.2.1
      · exact fun a =>
          let ⟨b, h, ab⟩ := H a
          ⟨⟨⟨_, h⟩, _, ab⟩, rfl⟩
    · have := typein_lt_type r i
      rwa [← re, lt_ord] at this
#align cardinal.lt_power_cof Cardinal.lt_power_cof

theorem lt_cof_power {a b : Cardinal} (ha : ℵ₀ ≤ a) (b1 : 1 < b) : a < cof (b^a).ord := by
  have b0 : b ≠ 0 := (zero_lt_one.trans b1).ne'
  apply lt_imp_lt_of_le_imp_le (power_le_power_left <| power_ne_zero a b0)
  rw [← power_mul, mul_eq_self ha]
  exact lt_power_cof (ha.trans <| (cantor' _ b1).le)
#align cardinal.lt_cof_power Cardinal.lt_cof_power

end Cardinal