import topology.instances.real

open set filter
open_locale topological_space filter

/- TEXT:
.. index:: filter

.. _filters:

Filters
-------

A *filter* on a type ``X`` is a collection of sets of ``X`` that satisfies three
conditions that we will spell out below. The notion
supports two related ideas:

* *limits*, including all the kinds of limits discussed above: finite and infinite limits of sequences, finite and infinite limits of functions at a point or at infinity, and so on.

* *things happening eventually*, including things happening for large enough ``n : ℕ``, or sufficiently near a point ``x``, or for sufficiently close pairs of points, or almost everywhere in the sense of measure theory. Dually, filters can also express the idea of *things happening often*: for arbitrarily large ``n``, at a point in any neighborhood of given a point, etc.

The filters that correspond to these descriptions will be defined later in this section, but we can already name them:

* ``(at_top : filter ℕ)``, made of sets of ``ℕ`` containing ``{n | n ≥ N}`` for some ``N``
* ``𝓝 x``, made of neighborhoods of ``x`` in a topological space
* ``𝓤 X``, made of entourages of a uniform space (uniform spaces generalize metric spaces and topological groups)
* ``μ.a_e`` , made of sets whose complement has zero measure with respect to a measure ``μ``.

The general definition is as follows: a filter ``F : filter X`` is a
collection of sets ``F.sets : set (set X)`` satisfying the following:

* ``F.univ_sets : univ ∈ F.sets``
* ``F.sets_of_superset : ∀ {U V}, U ∈ F.sets → U ⊆ V → V ∈ F.sets``
* ``F.inter_sets : ∀ {U V}, U ∈ F.sets → V ∈ F.sets → U ∩ V ∈ F.sets``.

The first condition says that the set of all elements of ``X`` belongs to ``F.sets``.
The second condition says that if ``U`` belongs to ``F.sets`` then anything
containing ``U`` also belongs to ``F.sets``.
The third condition says that ``F.sets`` is closed under finite intersections.
In mathlib, a filter ``F`` is defined to be a structure bundling ``F.sets`` and its
three properties, but the properties carry no additional data,
and it is convenient to blur the distinction between ``F`` and ``F.sets``. We
therefore define ``U ∈ F`` to mean ``U ∈ F.sets``.
This explains why the word ``sets`` appears in the names of some lemmas that
that mention ``U ∈ F``.

It may help to think of a filter as defining a notion of a "sufficiently large" set. The first
condition then says that ``univ`` is sufficiently large, the second one says that a set containing a sufficiently
large set is sufficiently large and the third one says that the intersection of two sufficiently large sets
is sufficiently large.

It may be even  more useful to think of a filter on a type ``X``
as a generalized element of ``set X``. For instance, ``at_top`` is the
"set of very large numbers" and ``𝓝 x₀`` is the "set of points very close to ``x₀``."
One manifestation of this view is that we can associate to any ``s : set X`` the so-called *principal filter*
consisting of all sets that contain ``s``.
This definition is already in mathlib and has a notation ``𝓟`` (localized in the ``filter`` namespace).
For the purpose of demonstration, we ask you to take this opportunity to work out the definition here.
EXAMPLES: -/
-- QUOTE:
def principal {α : Type*} (s : set α) : filter α :=
{ sets := {t | s ⊆ t},
  univ_sets := sorry,
  sets_of_superset := sorry,
  inter_sets := sorry}
-- QUOTE.

-- SOLUTIONS:
-- In the next example we could use `tauto` in each proof instead of knowing the lemmas
example {α : Type*} (s : set α) : filter α :=
{ sets := {t | s ⊆ t},
  univ_sets := subset_univ s,
  sets_of_superset := λ U V hU hUV, subset.trans hU hUV,
  inter_sets := λ U V hU hV, subset_inter hU hV }

/- TEXT:
For our second example, we ask you to define the filter ``at_top : filter ℕ``.
(We could use any type with a preorder instead of ``ℕ``.)
EXAMPLES: -/
-- QUOTE:
example : filter ℕ :=
{ sets := {s | ∃ a, ∀ b, a ≤ b → b ∈ s},
  univ_sets := sorry,
  sets_of_superset := sorry,
  inter_sets := sorry }
-- QUOTE.

-- SOLUTIONS:
example : filter ℕ :=
{ sets := {s | ∃ a, ∀ b, a ≤ b → b ∈ s},
  univ_sets := begin
    use 42,
    finish,
  end,
  sets_of_superset := begin
    rintros U V ⟨N, hN⟩ hUV,
    use N,
    tauto,
  end,
  inter_sets := begin
    rintros U V ⟨N, hN⟩ ⟨N', hN'⟩,
    use max N N',
    intros b hb,
    rw max_le_iff at hb,
    split ; tauto,
  end }

/- TEXT:
We can also directly define the filter ``𝓝 x`` of neighborhoods of any ``x : ℝ``.
In the real numbers, a neighborhood of ``x`` is a set containing an open interval
:math:`(x_0 - \varepsilon, x_0 + \varepsilon)`,
defined in mathlib as ``Ioo (x₀ - ε) (x₀ + ε)``.
(This is notion of a neighborhood is only a special case of a more general construction in mathlib.)

With these examples, we can already define what is means for a function ``f : X → Y``
to converge to some ``G : filter Y`` along some ``F : filter X``,
as follows:
BOTH: -/
-- QUOTE:
def tendsto₁ {X Y : Type*} (f : X → Y) (F : filter X) (G : filter Y) :=
∀ V ∈ G, f ⁻¹' V ∈ F
-- QUOTE.

/- TEXT:
When ``X`` is ``ℕ`` and ``Y`` is ``ℝ``, ``tendsto₁ u at_top (𝓝 x)`` is equivalent to saying that the sequence ``u : ℕ → ℝ``
converges to the real number ``x``. When both ``X`` and ``Y`` are ``ℝ``, ``tendsto f (𝓝 x₀) (𝓝 y₀)``
is equivalent to the familiar notion :math:`\lim_{x \to x₀} f(x) = y₀`.
All of the other kinds of limits mentioned in the introduction are
also equivalent to instances of ``tendsto₁`` for suitable choices of filters on the source and target.

The notion ``tendsto₁`` above is definitionally equivalent to the notion ``tendsto`` that is defined in mathlib,
but the latter is defined more abstractly.
The problem with the definition of ``tendsto₁`` is that it exposes a quantifier and elements of ``G``,
and it hides the intuition that we get by viewing filters as generalized sets. We can
hide the quantifier ``∀ V`` and make the intuition more salient by using more algebraic and set-theoretic machinery.
The first ingredient is the *pushforward* operation :math:`f_*` associated to any map ``f : X → Y``,
denoted ``filter.map f`` in mathlib. Given a filter ``F`` on ``X``, ``filter.map f F : filter Y`` is defined so that
``V ∈ filter.map f F ↔ f ⁻¹' V ∈ F`` holds definitionally.
In this examples file we've opened the ``filter`` namespace so that
``filter.map`` can be written as ``map``. This means that we can rewrite the definition of ``tendsto`` using
the order relation on ``filter Y``, which is reversed inclusion of the set of members.
In other words, given ``G H : filter Y``, we have ``G ≤ H ↔ ∀ V : set Y, V ∈ H → V ∈ G``.
EXAMPLES: -/
-- QUOTE:
def tendsto₂ {X Y : Type*} (f : X → Y) (F : filter X) (G : filter Y) :=
map f F ≤ G

example {X Y : Type*} (f : X → Y) (F : filter X) (G : filter Y) :
  tendsto₂ f F G ↔ tendsto₁ f F G := iff.rfl
-- QUOTE.

/- TEXT:
It may seem that the order relation on filters is backward. But recall that we can view filters on ``X`` as
generalized elements of ``set X``, via the inclusion of ``𝓟 : set X → filter X`` which maps any set ``s`` to the corresponding principal filter.
This inclusion is order preserving, so the order relation on ``filter`` can indeed be seen as the natural inclusion relation
between generalized sets. In this analogy, pushforward is analogous to the direct image.
And, indeed, ``map f (𝓟 s) = 𝓟 (f '' s)``.

We can now understand intuitively why a sequence ``u : ℕ → ℝ`` converges to
a point ``x₀`` if and only if we have ``map u at_top ≤ 𝓝 x₀``.
The inequality means the "direct image under ``u``" of
"the set of very big natural numbers" is "included" in "the set of points very close to ``x₀``."

As promised, the definition of ``tendsto₂`` does not exhibit any quantifiers or sets.
It also leverages the algebraic properties of the pushforward operation.
First, each ``filter.map f`` is monotone. And, second, ``filter.map`` is compatible with
composition.
EXAMPLES: -/
-- QUOTE:
#check (@filter.map_mono : ∀ {α β} {m : α → β}, monotone (map m))
#check (@filter.map_map : ∀ {α β γ} {f : filter α} {m : α → β} {m' : β → γ},
                            map m' (map m f) = map (m' ∘ m) f)
-- QUOTE.

/- TEXT:
Together these two properties allow us to prove that limits compose, yielding in one shot all 256 variants
of the composition lemma described in the introduction, and lots more.
You can practice proving the following statement using either the definition
of ``tendsto₁`` in terms of the
universal quantifier or the algebraic definition,
together with the two lemmas above.
EXAMPLES: -/
-- QUOTE:
example {X Y Z : Type*} {F : filter X} {G : filter Y} {H : filter Z} {f : X → Y} {g : Y → Z}
  (hf : tendsto₁ f F G) (hg : tendsto₁ g G H) : tendsto₁ (g ∘ f) F H :=
sorry
-- QUOTE.

-- SOLUTIONS:
example {X Y Z : Type*} {F : filter X} {G : filter Y} {H : filter Z} {f : X → Y} {g : Y → Z}
  (hf : tendsto₁ f F G) (hg : tendsto₁ g G H) : tendsto₁ (g ∘ f) F H :=
calc map (g ∘ f) F = map g (map f F) : by rw map_map
... ≤ map g G : map_mono hf
... ≤ H : hg

example {X Y Z : Type*} {F : filter X} {G : filter Y} {H : filter Z} {f : X → Y} {g : Y → Z}
  (hf : tendsto₁ f F G) (hg : tendsto₁ g G H) : tendsto₁ (g ∘ f) F H :=
begin
  intros V hV,
  rw preimage_comp,
  apply hf,
  apply hg,
  exact hV
end

/- TEXT:
The pushforward construction uses a map to push filters from the map source to the map target.
There also a *pullback* operation, ``filter.comap``, going in the other direction.
This generalizes the
preimage operation on sets. For any map ``f``,
``filter.map f`` and ``filter.comap f`` form what is known as a *Galois connection*,
which is to say, they satisfy

  ``filter.map_le_iff_le_comap : filter.map f F ≤ G ↔ F ≤ filter.comap f G``

for every ``F`` and ``G``.
This operation could be used to provided another formulation of ``tendsto`` that would be provably
(but not definitionaly) equivalent to the one in mathlib.

The ``comap`` operation can be used to restrict filters to a subtype. For instance, suppose we have ``f : ℝ → ℝ``,
``x₀ : ℝ`` and ``y₀ : ℝ``, and suppose we want to state that ``f x`` approaches ``y₀`` when ``x`` approaches ``x₀`` within the rational numbers.
We can pull the filter ``𝓝 x₀`` back to ``ℚ`` using the coercion map
``coe : ℚ → ℝ`` and state ``tendsto (f ∘ coe : ℚ → ℝ) (comap coe (𝓝 x₀)) (𝓝 y₀)``.
EXAMPLES: -/
-- QUOTE:
variables (f : ℝ → ℝ) (x₀ y₀ : ℝ)

#check comap (coe : ℚ → ℝ) (𝓝 x₀)
#check tendsto (f ∘ coe) (comap (coe : ℚ → ℝ) (𝓝 x₀)) (𝓝 y₀)
-- QUOTE.

/- TEXT:
The pullback operation is also compatible with composition, but it *contravariant*,
which is to say, it reverses the order of the arguments.
EXAMPLES: -/
-- QUOTE:
section
variables {α β γ : Type*} (F : filter α) {m : γ → β} {n : β → α}

#check (comap_comap : comap m (comap n F) = comap (n ∘ m) F)
end
-- QUOTE.

/- TEXT:
Let's now shift attention to the plane ``ℝ × ℝ`` and try to understand how the neighborhoods of a point
``(x₀, y₀)`` are related to ``𝓝 x₀`` and ``𝓝 y₀``. There is a product operation
``filter.prod : filter X → filter Y → filter (X × Y)``, denoted by ``×ᶠ``, which answers this question:
EXAMPLES: -/
-- QUOTE:
example : 𝓝 (x₀, y₀) = 𝓝 x₀ ×ᶠ 𝓝 y₀ := nhds_prod_eq
-- QUOTE.

/- TEXT:
The product operation is defined in terms of the pullback operation and the ``inf`` operation:

  ``F ×ᶠ G = (comap prod.fst F) ⊓ (comap prod.snd G)``.

Here the ``inf`` operation refers to the lattice structure on ``filter X`` for any type ``X``, whereby
``F ⊓ G`` is the greatest filter that is smaller than both ``F`` and ``G``.
Thus the ``inf`` operation generalizes the notion of the intersection of sets.

A lot of proofs in mathlib use all of the aforementioned structure (``map``, ``comap``, ``inf``, ``sup``, and ``prod``)
to give algebraic proofs about convergence without ever referring to members of filters.
You can practice doing this in a proof of the following lemma, unfolding the definition of ``tendsto``
and ``filter.prod`` if needed.
EXAMPLES: -/
-- QUOTE:
#check le_inf_iff

example (f : ℕ → ℝ × ℝ) (x₀ y₀ : ℝ) :
  tendsto f at_top (𝓝 (x₀, y₀)) ↔
    tendsto (prod.fst ∘ f) at_top (𝓝 x₀) ∧ tendsto (prod.snd ∘ f) at_top (𝓝 y₀) :=
sorry
-- QUOTE.

-- SOLUTIONS:
example (f : ℕ → ℝ × ℝ) (x₀ y₀ : ℝ) :
  tendsto f at_top (𝓝 (x₀, y₀)) ↔
  tendsto (prod.fst ∘ f) at_top (𝓝 x₀) ∧ tendsto (prod.snd ∘ f) at_top (𝓝 y₀) :=
calc
tendsto f at_top (𝓝 (x₀, y₀)) ↔ map f at_top ≤ 𝓝 (x₀, y₀) : iff.rfl
... ↔ map f at_top ≤ 𝓝 x₀ ×ᶠ 𝓝 y₀ : by rw nhds_prod_eq
... ↔ map f at_top ≤ (comap prod.fst (𝓝 x₀) ⊓ comap prod.snd (𝓝 y₀)) : iff.rfl
... ↔ map f at_top ≤ comap prod.fst (𝓝 x₀) ∧ map f at_top ≤ (comap prod.snd (𝓝 y₀)) : le_inf_iff
... ↔ map prod.fst (map f at_top) ≤ 𝓝 x₀ ∧ map prod.snd (map f at_top) ≤ 𝓝 y₀ :
        by rw [← map_le_iff_le_comap, ← map_le_iff_le_comap]
... ↔ map (prod.fst ∘ f) at_top ≤ 𝓝 x₀ ∧ map (prod.snd ∘ f) at_top ≤ 𝓝 y₀ : by rw [map_map, map_map]

-- an alternative solution
example (f : ℕ → ℝ × ℝ) (x₀ y₀ : ℝ) :
  tendsto f at_top (𝓝 (x₀, y₀)) ↔
  tendsto (prod.fst ∘ f) at_top (𝓝 x₀) ∧ tendsto (prod.snd ∘ f) at_top (𝓝 y₀) :=
begin
  rw nhds_prod_eq,
  unfold tendsto filter.prod,
  rw [le_inf_iff, ← map_le_iff_le_comap, map_map, ← map_le_iff_le_comap, map_map]
end

/- TEXT:
The ordered type ``filter X`` is actually a *complete* lattice,
which is to say, there is a bottom element, there is a top element, and
every set of filters on ``X`` has an ``Inf`` and a ``Sup``.

Note that given the second property in the definition of a filter
(if ``U`` belongs to ``F`` then anything larger than ``U`` also belongs to ``F``),
the first property
(the set of all inhabitants of ``X`` belongs to ``F``) is
equivalent to the property that ``F`` is not the empty collection of sets.
This shouldn't be confused with the more subtle question as to whether
the empty set is an *element* of ``F``. The
definition of a filter does not prohibit ``∅ ∈ F``,
but if the empty set is in ``F`` then
every set is in ``F``, which is to say, ``∀ U : set X, U ∈ F``.
In this case, ``F`` is a rather trivial filter, which is precisely the
bottom element of the complete lattice ``filter X``.
This contrasts with the definition of filters in
Bourbaki, which doesn't allow filters containing the empty set.

Because we include the trivial filter in our definition, we sometimes need to explicitly assume
nontriviality in some lemmas.
In return, however, the theory has nicer global properties.
We have already seen that including the trivial filter gives us a
bottom element. It also allows us to define ``principal : set X → filter X``,
which maps  ``∅`` to ``⊥``, without additing a precondition to rule out the empty set.
And it allows us to define the pullback operation without a precondition as well.
Indeed, it can happen that ``comap f F = ⊥`` although ``F ≠ ⊥``. For instance,
given ``x₀ : ℝ`` and ``s : set ℝ``, the pullback of ``𝓝 x₀`` under the coercion
from the subtype corresponding to ``s`` is nontrivial if and only if ``x₀`` belongs to the
closure of ``s``.

In order to manage lemmas that do need to assume some filter is nontrivial, mathlib has
a type class ``filter.ne_bot``, and the library has lemmas that assume
``(F : filter X) [F.ne_bot]``. The instance database knows, for example, that ``(at_top : filter ℕ).ne_bot``,
and it knows that pushing forward a nontrivial filter gives a nontrivial filter.
As a result, a lemma assuming ``[F.ne_bot]`` will automatically apply to ``map u at_top`` for any sequence ``u``.

Our tour of the algebraic properties of filters and their relation to limits is essentially done,
but we have not yet justified our claim to have recaptured the usual limit notions.
Superficially, it may seem that ``tendsto u at_top (𝓝 x₀)``
is stronger than the notion of convergence defined in :numref:`sequences_and_convergence` because we ask that *every* neighborhood of ``x₀``
has a preimage belonging to ``at_top``, whereas the usual definition only requires
this for the standard neighborhoods ``Ioo (x₀ - ε) (x₀ + ε)``.
The key is that, by definition, every neighborhood contains such a standard one.
This observation leads to the notion of a *filter basis*.

Given ``F : filter X``,
a family of sets `s : ι → set X` is a basis for ``F`` if for every set ``U``,
we have ``U ∈ F`` if and only if it contains some ``s i``. In other words, formally speaking,
``s`` is a basis if it satisfies
``∀ U : set X, U ∈ F ↔ ∃ i, s i ⊆ U``. It is even more flexible to consider
a predicate on ``ι`` that selects only some of the values ``i`` in the indexing type.
In the case of ``𝓝 x₀``, we want ``ι`` to be ``ℝ``, we write ``ε`` for ``i``, and the predicate should select the positive values of ``ε``.
So the fact that the sets ``Ioo  (x₀ - ε) (x₀ + ε)`` form a basis for the
neighborhood topology on ``ℝ`` is stated as follows:
EXAMPLES: -/
-- QUOTE:
example (x₀ : ℝ) : has_basis (𝓝 x₀) (λ ε : ℝ, 0 < ε) (λ ε, Ioo (x₀ - ε) (x₀ + ε)) :=
nhds_basis_Ioo_pos x₀
-- QUOTE.

/- TEXT:
There is also a nice basis for the filter ``at_top``. The lemma
``filter.has_basis.tendsto_iff`` allows
us to reformulate a statement of the form ``tendsto f F G``
given bases for ``F`` and ``G``.
Putting these pieces together gives us essentially the notion of convergence
that we used in :numref:`sequences_and_convergence`.
EXAMPLES: -/
-- QUOTE:
example (u : ℕ → ℝ) (x₀ : ℝ) :
  tendsto u at_top (𝓝 x₀) ↔ ∀ ε > 0, ∃ N, ∀ n ≥ N, u n ∈ Ioo (x₀ - ε) (x₀ + ε) :=
begin
  have : at_top.has_basis (λ n : ℕ, true) Ici := at_top_basis,
  rw this.tendsto_iff (nhds_basis_Ioo_pos x₀),
  simp
end
-- QUOTE.

/- TEXT:
We now show how filters facilitate working with properties that hold for sufficiently large numbers
or for points that are sufficiently close to a given point. In :numref:`sequences_and_convergence`, we were often faced with the situation where
we knew that some property ``P n`` holds for sufficiently large ``n`` and that some
other property ``Q n`` holds for sufficiently large ``n``.
Using ``cases`` twice gave us ``N_P`` and ``N_Q`` satisfying
``∀ n ≥ N_P, P n`` and ``∀ n ≥ N_Q, Q n``. Using ``set N := max N_P N_Q``, we could
eventually prove ``∀ n ≥ N, P n ∧ Q n``.
Doing this repeatedly becomes tiresome.

We can do better by noting that the statement "``P n`` and ``Q n`` hold for large enough ``n``" means
that we have ``{n | P n} ∈ at_top`` and ``{n | Q n} ∈ at_top``.
The fact that ``at_top`` is a filter implies that the intersection of two elements of ``at_top``
is again in ``at_top``, so we have ``{n | P n ∧ Q n} ∈ at_top``.
Writing ``{n | P n} ∈ at_top`` is unpleasant,
but we can use the more suggestive notation ``∀ᶠ n in at_top, P n``.
Here the superscripted ``f`` stands for "filter."
You can think of the notation as saying that for all ``n`` in the "set of very large numbers," ``P n`` holds. The ``∀ᶠ``
notation stands for ``filter.eventually``, and the lemma ``filter.eventually.and`` uses the intersection property of filters to do what we just described:
EXAMPLES: -/
-- QUOTE:
example (P Q : ℕ → Prop) (hP : ∀ᶠ n in at_top, P n) (hQ : ∀ᶠ n in at_top, Q n) :
  ∀ᶠ n in at_top, P n ∧ Q n := hP.and hQ
-- QUOTE.

/- TEXT:
This notation is so convenient and intuitive that we also have specializations
when ``P`` is an equality or inequality statement. For example, let ``u`` and ``v`` be
two sequences of real numbers, and let us show that if
``u n`` and ``v n`` coincide for sufficiently large ``n`` then
``u`` tends to ``x₀`` if and only if ``v`` tends to ``x₀``.
First we'll use the generic ``eventually`` and then the one
specialized for the equality predicate, ``eventually_eq``. The two statements are
definitionaly equivalent so the same proof work in both cases.
EXAMPLES: -/
-- QUOTE:
example (u v : ℕ → ℝ) (h : ∀ᶠ n in at_top, u n = v n) (x₀ : ℝ) :
  tendsto u at_top (𝓝 x₀) ↔ tendsto v at_top (𝓝 x₀) :=
tendsto_congr' h

example (u v : ℕ → ℝ) (h : u =ᶠ[at_top] v) (x₀ : ℝ) :
  tendsto u at_top (𝓝 x₀) ↔ tendsto v at_top (𝓝 x₀) :=
tendsto_congr' h
-- QUOTE.

/- TEXT:
It is instructive to review the definition of filters in terms of ``eventually``.
Given ``F : filter X``, for any predicates ``P`` and ``Q`` on ``X``,

* the condition ``univ ∈ F`` ensures ``(∀ x, P x) → ∀ᶠ x in F, P x``,
* the condition ``U ∈ F → U ⊆ V → V ∈ F`` ensures ``(∀ᶠ x in F, P x) → (∀ x, P x → Q x) → ∀ᶠ x in F, Q x``, and
* the condition ``U ∈ F → V ∈ F → U ∩ V ∈ F`` ensures ``(∀ᶠ x in F, P x) → (∀ᶠ x in F, Q x) → ∀ᶠ x in F, P x ∧ Q x``.
EXAMPLES: -/
-- QUOTE:
#check @eventually_of_forall
#check @eventually.mono
#check @eventually.and
-- QUOTE.

/- TEXT:
The second item, corresponding to ``eventually.mono``, supports nice ways
of using filters, especially when combined
with ``eventually.and``. The ``filter_upwards`` tactic allows us to combine them.
Compare:
EXAMPLES: -/
-- QUOTE:
example (P Q R : ℕ → Prop) (hP : ∀ᶠ n in at_top, P n) (hQ : ∀ᶠ n in at_top, Q n)
  (hR : ∀ᶠ n in at_top, P n ∧ Q n → R n) :
  ∀ᶠ n in at_top, R n :=
begin
  apply (hP.and (hQ.and hR)).mono,
  rintros n ⟨h, h', h''⟩,
  exact h'' ⟨h, h'⟩
end

example (P Q R : ℕ → Prop) (hP : ∀ᶠ n in at_top, P n) (hQ : ∀ᶠ n in at_top, Q n)
  (hR : ∀ᶠ n in at_top, P n ∧ Q n → R n) :
  ∀ᶠ n in at_top, R n :=
begin
  filter_upwards [hP, hQ, hR],
  intros n h h' h'',
  exact h'' ⟨h, h'⟩
end
-- QUOTE.

/- TEXT:
Readers who know about measure theory will note that the filter ``μ.ae`` of sets whose complement has measure zero
(aka "the set consisting of almost every point") is not very useful as the source or target of ``tendsto``, but it can be conveniently
used with ``eventually`` to say that a property holds for almost every point.

There is a dual version of ``∀ᶠ x in F, P x``, which is occasionally useful:
``∃ᶠ x in F, P x`` means
``{x | ¬P x} ∉ F``. For example, ``∃ᶠ n in at_top, P n`` means there are arbitrarily large ``n`` such that ``P n`` holds.
The ``∃ᶠ`` notation stands for ``filter.frequently``.

For a more sophisticated example, consider the following statement about a sequence
``u``, a set ``M``, and a value ``x``:

  If ``u`` converges to ``x`` and ``u n`` belongs to ``M`` for
  sufficiently large ``n`` then ``x`` is in the closure of ``M``.

This can be formalized as follows:

  ``tendsto u at_top (𝓝 x) → (∀ᶠ n in at_top, u n ∈ M) → x ∈ closure M``.

This is a special case of the theorem ``mem_closure_of_tendsto`` from the
topology library.
See if you can prove it using the quoted lemmas,
using the fact that ``cluster_pt x F`` means ``(𝓝 x ⊓ F).ne_bot``.

EXAMPLES: -/
-- QUOTE:
#check mem_closure_iff_cluster_pt
#check le_principal_iff
#check ne_bot_of_le

example (u : ℕ → ℝ) (M : set ℝ) (x : ℝ)
  (hux : tendsto u at_top (𝓝 x)) (huM : ∀ᶠ n in at_top, u n ∈ M) : x ∈ closure M :=
sorry
-- QUOTE.

-- SOLUTIONS:
example (u : ℕ → ℝ) (M : set ℝ) (x : ℝ)
  (hux : tendsto u at_top (𝓝 x)) (huM : ∀ᶠ n in at_top, u n ∈ M) : x ∈ closure M :=
mem_closure_iff_cluster_pt.mpr (ne_bot_of_le $ le_inf hux $ le_principal_iff.mpr huM)


import tactic
import data.real.irrational

/-- If n is any positive integer, then √n + √2 is irrational. -/
lemma exercise (n : ℕ) (n_pos : n > 0) : irrational (real.sqrt n + real.sqrt 2) :=
begin
  sorry
end


/-
Copyright (c) 2021 OpenAI. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kunhao Zheng, Stanislas Polu, David Renshaw, OpenAI GPT-f
-/
import mathzoo.imports.miniF2F

open_locale nat rat real big_operators topological_space

theorem mathd_algebra_388
  (x y z : ℝ)
  (h₀ : 3 * x + 4 * y - 12 * z = 10)
  (h₁ : -2 * x - 3 * y + 9 * z = -4) :
  x = 14 :=
begin
  linarith,
end


import .constructions .projection

namespace ftype

variables {U : ftype}

def is_common_ind (M₁ M₂ : rankfun U) (X : U) :=
  is_indep M₁ X ∧ is_indep M₂ X

lemma bot_is_common_ind (M₁ M₂ : rankfun U) :
  is_common_ind M₁ M₂ ⊥ :=
  ⟨empty_indep M₁, empty_indep M₂⟩

lemma exists_common_ind {M₁ M₂ : rankfun U} :
  ∃ X, is_common_ind M₁ M₂ X :=
  ⟨⊥, bot_is_common_ind M₁ M₂⟩

-- size of largest common independent set
def ν (M₁ M₂ : rankfun U) : ℤ :=
  max_val_over size (is_common_ind M₁ M₂) exists_common_ind

def largest_common_ind (M₁ M₂ : rankfun U) : U :=
  arg_max_over size (is_common_ind M₁ M₂) exists_common_ind

lemma size_largest_common_ind_eq_nu (M₁ M₂ : rankfun U) :
  size (largest_common_ind M₁ M₂) = ν M₁ M₂ :=
  (arg_max_over_attains _ _ _).2

lemma largest_common_ind_is_common_ind (M₁ M₂ : rankfun U) :
  is_common_ind M₁ M₂ (largest_common_ind M₁ M₂) :=
  (arg_max_over_attains _ _ _).1

lemma size_common_ind_le_nu {M₁ M₂ : rankfun U} {X : U} :
  is_common_ind M₁ M₂ X → size X ≤ ν M₁ M₂ :=
  λ h, (max_over_is_ub _ _ _ X h)

lemma nu_nonneg (M₁ M₂ : rankfun U) :
  0 ≤ ν M₁ M₂ :=
  by {rw ←(size_bot U), from size_common_ind_le_nu (bot_is_common_ind _ _)}

def matroid_intersection_ub_fn (M₁ M₂ : rankfun U) : U → ℤ :=
  (λ X, M₁.r X + M₂.r Xᶜ)

theorem matroid_intersection_pair_le {M₁ M₂ : rankfun U} {I : U} (A : U) :
  is_common_ind M₁ M₂ I → size I ≤ M₁.r A + M₂.r Aᶜ :=
begin
  rintros ⟨h₁,h₂⟩,
  rw ←(compl_inter_size A I),
  have h₁i := indep_of_subset_indep (inter_subset_right A I) h₁,
  have h₂i := indep_of_subset_indep (inter_subset_right Aᶜ I) h₂,
  rw [←indep_iff_r.mp h₁i, ←indep_iff_r.mp h₂i],
  linarith [R2 M₁ (inter_subset_left A I), R2 M₂ (inter_subset_left Aᶜ I)],
end

lemma matroid_intersection_ub (M₁ M₂ : rankfun U) :
  ν M₁ M₂ ≤ min_val (λ X, M₁.r X + M₂.r Xᶜ) :=
begin
  set ub_fn := λ X, M₁.r X + M₂.r Xᶜ with h_ub_fn,
  set A := arg_min ub_fn with hA ,
  set I := largest_common_ind M₁ M₂ with hI,
  rw [←size_largest_common_ind_eq_nu, ←arg_min_attains ub_fn, ←hA],
  from matroid_intersection_pair_le A (largest_common_ind_is_common_ind M₁ M₂)
end

theorem matroid_intersection (M₁ M₂ : rankfun U) :
  ν M₁ M₂ = min_val (λ X, M₁.r X + M₂.r Xᶜ) :=
begin
  set ub_fn := λ (N₁ N₂ : rankfun U) X, N₁.r X + N₂.r Xᶜ with h_ub_fn,
  --induction boilerplate (and ≥ suffices)
  set Q : ℤ → Prop :=
    λ s, ∀ (M₁ M₂ : rankfun U), size (loops M₁ ∪ loops M₂)ᶜ = s →
      min_val (ub_fn M₁ M₂) ≤ ν M₁ M₂,
  suffices : ∀ n₀, 0 ≤ n₀ → Q n₀,
    from antisymm (matroid_intersection_ub M₁ M₂) (this _ (size_nonneg _) M₁ M₂ rfl) ,
  refine nonneg_int_strong_induction _ (λ N₁ N₂ hloops, _) (λ n hn IH N₁ N₂ hsize, _),

  -- base case
  rw [size_zero_iff_bot, top_iff_compl_bot] at hloops,
  have h' : (ub_fn N₁ N₂) (loops N₁) = 0 :=  by
  {
      simp_rw h_ub_fn,
      linarith [R0 N₂ (loops N₁)ᶜ, rank_loops N₁, rank_loops N₂,
                                R2 N₂ (cover_compl_subset hloops)],
  },
  linarith [nu_nonneg N₁ N₂, min_is_lb (ub_fn N₁ N₂) (loops N₁)],

  --inductive step
  set k := ν N₁ N₂ with hk,
  rw ←hsize at hn,
  cases exists_mem_of_size_pos hn with e he,

  have h_e_nl : (is_nonloop N₁ e) ∧ (is_nonloop N₂ e) := by split;
  {
    rw [nonloop_iff_not_mem_loops, ←mem_compl_iff],
    refine subset.trans he _,
    simp only [compl_union, inter_subset_left, inter_subset_right],
  },

  set N₁d := loopify N₁ e with hN₁d,
  set N₂d := loopify N₂ e with hN₂d,
  set N₁c := project N₁ e with hN₁c,
  set N₂c := project N₂ e with hN₂c,

  set Id := largest_common_ind N₁d N₂d with hId,
  set Ic := largest_common_ind N₁c N₂c with hIc,
  let hId' : is_common_ind N₁d N₂d Id := by apply largest_common_ind_is_common_ind,
  let hIc' : is_common_ind N₁c N₂c Ic := by apply largest_common_ind_is_common_ind,

  have heIc : e ∉ Ic := λ heIc, by
  {
    have := projected_set_rank_zero N₁ e,
    rw [←hN₁c, mem_indep_r heIc hIc'.1] at this,
    from one_ne_zero this,
  },

  have h_nu_d : ν N₁d N₂d ≤ k := by
  {
    rw [←size_largest_common_ind_eq_nu, ←hId, hk],
    from size_common_ind_le_nu
          ⟨indep_of_loopify_indep hId'.1,
           indep_of_loopify_indep hId'.2⟩,
  },

  have h_nu_c : ν N₁c N₂c ≤ k-1 := by
  {
    rw [←size_largest_common_ind_eq_nu, ←hIc, hk],
    linarith
    [
      size_union_nonmem_singleton heIc,
      size_common_ind_le_nu
      ⟨
        indep_of_project_indep hIc'.1 (nonloop_iff_indep.mp h_e_nl.1),
        indep_of_project_indep hIc'.2 (nonloop_iff_indep.mp h_e_nl.2)
      ⟩
    ],
  },

  -- these next two claims let us apply IH to deletion/contraction
  have h_more_loops_d : size (loops N₁d ∪ loops N₂d)ᶜ < n := by
  {
    have h_add_e := union_subset_union
      (loopify_makes_loops N₁ e)
      (loopify_makes_loops N₂ e),
    rw ←union_distrib_union_left at h_add_e,
    have := size_monotone h_add_e,
    rw [size_union_singleton_compl he, ←hN₁d, ←hN₂d] at this,
    rw [compl_size] at ⊢ hsize, linarith,
  },

  have h_more_loops_c : size (loops N₁c ∪ loops N₂c)ᶜ < n := by
  {
    have h_add_e := union_subset_union
      (project_makes_loops N₁ e)
      (project_makes_loops N₂ e),
    rw ←union_distrib_union_left at h_add_e,
    have := size_monotone h_add_e,
    rw [size_union_singleton_compl he, ←hN₁c, ←hN₂c] at this,
    rw [compl_size] at ⊢ hsize, linarith,
  },

  -- apply IH to deletion, get minimizer Ad
  rcases IH _ h_more_loops_d N₁d N₂d rfl with hd,
  set Ad := arg_min (ub_fn N₁d N₂d) with hAd,
  rw [←arg_min_attains (ub_fn N₁d N₂d), ←hAd] at hd,
  have hAd_ub : N₁.r (Ad \ {e}) + N₂.r (Adᶜ \ {e}) ≤ k := le_trans hd h_nu_d,

  --apply IH to contraction, get minimizer Ac
  rcases IH _ h_more_loops_c N₁c N₂c rfl with hc,
  set Ac := arg_min (ub_fn N₁c N₂c) with hAc,
  rw [←arg_min_attains (ub_fn N₁c N₂c), ←hAc] at hc,
  have hAc_ub : N₁.r (Ac ∪ {e}) + N₂.r (Acᶜ ∪ {e}) ≤ k+1 := by
  {
    suffices : (N₁.r (Ac ∪ {e}) - N₁.r e) + (N₂.r (Acᶜ ∪ {e}) - N₂.r e) ≤ k-1,
      by linarith [rank_nonloop h_e_nl.1, rank_nonloop h_e_nl.2],
    from le_trans hc h_nu_c,
  },

  by_contra h_contr, push_neg at h_contr,
  replace h_contr : ∀ X, k + 1 ≤ ub_fn N₁ N₂ X :=
    λ X, by linarith [min_is_lb (ub_fn N₁ N₂) X],

  have hi := h_contr (Ac ∩ Ad \ {e}),
  have hu := h_contr (Ac ∪ Ad ∪ {e}),
  simp_rw h_ub_fn at hi hu,
  rw [compl_union, compl_union, ←diff_eq] at hu,
  rw [compl_diff, compl_inter] at hi,
  have sm1 := R3 N₁ (Ac ∪ {e}) (Ad \ {e}),
  have sm2 := R3 N₂ (Acᶜ ∪ {e}) (Adᶜ \ {e}),
  rw [union_union_diff, union_inter_diff] at sm1 sm2,
  linarith only [sm1, sm2, hi, hu, hAd_ub, hAc_ub],
end

theorem matroid_intersection_exists_pair_eq (M₁ M₂ : rankfun U) :
  ∃ I A, is_common_ind M₁ M₂ I ∧ size I =  M₁.r A + M₂.r Aᶜ  :=
begin
  set I := largest_common_ind M₁ M₂ with hI,
  let h_ind := largest_common_ind_is_common_ind M₁ M₂,
  rw ←hI at h_ind,
  set A := arg_min (λ X, M₁.r X + M₂.r Xᶜ) with hX,
  refine ⟨I, ⟨A, ⟨h_ind,_⟩⟩⟩,
  rw [hI, size_largest_common_ind_eq_nu, matroid_intersection M₁ M₂],
  from (arg_min_attains _).symm,
end

end ftype


/-
Copyright (c) 2020 Markus Himmel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel

! This file was ported from Lean 3 source module data.nat.bitwise
! leanprover-community/mathlib commit be24ec5de6701447e5df5ca75400ffee19d65659
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.List.Basic
import Mathbin.Data.Nat.Bits
import Mathbin.Tactic.Linarith.Default

/-!
# Bitwise operations on natural numbers

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

In the first half of this file, we provide theorems for reasoning about natural numbers from their
bitwise properties. In the second half of this file, we show properties of the bitwise operations
`lor`, `land` and `lxor`, which are defined in core.

## Main results
* `eq_of_test_bit_eq`: two natural numbers are equal if they have equal bits at every position.
* `exists_most_significant_bit`: if `n ≠ 0`, then there is some position `i` that contains the most
  significant `1`-bit of `n`.
* `lt_of_test_bit`: if `n` and `m` are numbers and `i` is a position such that the `i`-th bit of
  of `n` is zero, the `i`-th bit of `m` is one, and all more significant bits are equal, then
  `n < m`.

## Future work

There is another way to express bitwise properties of natural number: `digits 2`. The two ways
should be connected.

## Keywords

bitwise, and, or, xor
-/

open Function

namespace Nat

#print Nat.bit_false /-
@[simp]
theorem bit_false : bit false = bit0 :=
  rfl
#align nat.bit_ff Nat.bit_false
-/

#print Nat.bit_true /-
@[simp]
theorem bit_true : bit true = bit1 :=
  rfl
#align nat.bit_tt Nat.bit_true
-/

#print Nat.bit_eq_zero /-
@[simp]
theorem bit_eq_zero {n : ℕ} {b : Bool} : n.bit b = 0 ↔ n = 0 ∧ b = false := by
  cases b <;> simp [Nat.bit0_eq_zero, Nat.bit1_ne_zero]
#align nat.bit_eq_zero Nat.bit_eq_zero
-/

#print Nat.zero_of_testBit_eq_false /-
theorem zero_of_testBit_eq_false {n : ℕ} (h : ∀ i, testBit n i = false) : n = 0 :=
  by
  induction' n using Nat.binaryRec with b n hn
  · rfl
  · have : b = ff := by simpa using h 0
    rw [this, bit_ff, bit0_val, hn fun i => by rw [← h (i + 1), test_bit_succ],
      MulZeroClass.mul_zero]
#align nat.zero_of_test_bit_eq_ff Nat.zero_of_testBit_eq_false
-/

#print Nat.zero_testBit /-
@[simp]
theorem zero_testBit (i : ℕ) : testBit 0 i = false := by simp [test_bit]
#align nat.zero_test_bit Nat.zero_testBit
-/

#print Nat.testBit_eq_inth /-
/-- The ith bit is the ith element of `n.bits`. -/
theorem testBit_eq_inth (n i : ℕ) : n.testBit i = n.bits.getI i :=
  by
  induction' i with i ih generalizing n
  · simp [test_bit, shiftr, bodd_eq_bits_head, List.getI_zero_eq_headI]
  conv_lhs => rw [← bit_decomp n]
  rw [test_bit_succ, ih n.div2, div2_bits_eq_tail]
  cases n.bits <;> simp
#align nat.test_bit_eq_inth Nat.testBit_eq_inth
-/

#print Nat.eq_of_testBit_eq /-
/-- Bitwise extensionality: Two numbers agree if they agree at every bit position. -/
theorem eq_of_testBit_eq {n m : ℕ} (h : ∀ i, testBit n i = testBit m i) : n = m :=
  by
  induction' n using Nat.binaryRec with b n hn generalizing m
  · simp only [zero_test_bit] at h
    exact (zero_of_test_bit_eq_ff fun i => (h i).symm).symm
  induction' m using Nat.binaryRec with b' m hm
  · simp only [zero_test_bit] at h
    exact zero_of_test_bit_eq_ff h
  suffices h' : n = m
  · rw [h', show b = b' by simpa using h 0]
  exact hn fun i => by convert h (i + 1) using 1 <;> rw [test_bit_succ]
#align nat.eq_of_test_bit_eq Nat.eq_of_testBit_eq
-/

#print Nat.exists_most_significant_bit /-
theorem exists_most_significant_bit {n : ℕ} (h : n ≠ 0) :
    ∃ i, testBit n i = true ∧ ∀ j, i < j → testBit n j = false :=
  by
  induction' n using Nat.binaryRec with b n hn
  · exact False.elim (h rfl)
  by_cases h' : n = 0
  · subst h'
    rw [show b = tt by
        revert h
        cases b <;> simp]
    refine' ⟨0, ⟨by rw [test_bit_zero], fun j hj => _⟩⟩
    obtain ⟨j', rfl⟩ := exists_eq_succ_of_ne_zero (ne_of_gt hj)
    rw [test_bit_succ, zero_test_bit]
  · obtain ⟨k, ⟨hk, hk'⟩⟩ := hn h'
    refine' ⟨k + 1, ⟨by rw [test_bit_succ, hk], fun j hj => _⟩⟩
    obtain ⟨j', rfl⟩ := exists_eq_succ_of_ne_zero (show j ≠ 0 by linarith)
    exact (test_bit_succ _ _ _).trans (hk' _ (lt_of_succ_lt_succ hj))
#align nat.exists_most_significant_bit Nat.exists_most_significant_bit
-/

#print Nat.lt_of_testBit /-
theorem lt_of_testBit {n m : ℕ} (i : ℕ) (hn : testBit n i = false) (hm : testBit m i = true)
    (hnm : ∀ j, i < j → testBit n j = testBit m j) : n < m :=
  by
  induction' n using Nat.binaryRec with b n hn' generalizing i m
  · contrapose! hm
    rw [le_zero_iff] at hm
    simp [hm]
  induction' m using Nat.binaryRec with b' m hm' generalizing i
  · exact False.elim (Bool.false_ne_true ((zero_test_bit i).symm.trans hm))
  by_cases hi : i = 0
  · subst hi
    simp only [test_bit_zero] at hn hm
    have : n = m :=
      eq_of_test_bit_eq fun i => by convert hnm (i + 1) (by decide) using 1 <;> rw [test_bit_succ]
    rw [hn, hm, this, bit_ff, bit_tt, bit0_val, bit1_val]
    exact lt_add_one _
  · obtain ⟨i', rfl⟩ := exists_eq_succ_of_ne_zero hi
    simp only [test_bit_succ] at hn hm
    have :=
      hn' _ hn hm fun j hj => by convert hnm j.succ (succ_lt_succ hj) using 1 <;> rw [test_bit_succ]
    cases b <;> cases b' <;>
        simp only [bit_ff, bit_tt, bit0_val n, bit1_val n, bit0_val m, bit1_val m] <;>
      linarith
#align nat.lt_of_test_bit Nat.lt_of_testBit
-/

#print Nat.testBit_two_pow_self /-
@[simp]
theorem testBit_two_pow_self (n : ℕ) : testBit (2 ^ n) n = true := by
  rw [test_bit, shiftr_eq_div_pow, Nat.div_self (pow_pos zero_lt_two n), bodd_one]
#align nat.test_bit_two_pow_self Nat.testBit_two_pow_self
-/

#print Nat.testBit_two_pow_of_ne /-
theorem testBit_two_pow_of_ne {n m : ℕ} (hm : n ≠ m) : testBit (2 ^ n) m = false :=
  by
  rw [test_bit, shiftr_eq_div_pow]
  cases' hm.lt_or_lt with hm hm
  · rw [Nat.div_eq_zero, bodd_zero]
    exact Nat.pow_lt_pow_of_lt_right one_lt_two hm
  · rw [pow_div hm.le zero_lt_two, ← tsub_add_cancel_of_le (succ_le_of_lt <| tsub_pos_of_lt hm)]
    simp [pow_succ]
#align nat.test_bit_two_pow_of_ne Nat.testBit_two_pow_of_ne
-/

/- warning: nat.test_bit_two_pow -> Nat.testBit_two_pow is a dubious translation:
lean 3 declaration is
  forall (n : Nat) (m : Nat), Eq.{1} Bool (Nat.testBit (HPow.hPow.{0, 0, 0} Nat Nat Nat (instHPow.{0, 0} Nat Nat (Monoid.Pow.{0} Nat Nat.monoid)) (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne)))) n) m) (Decidable.decide (Eq.{1} Nat n m) (Nat.decidableEq n m))
but is expected to have type
  forall (n : Nat) (m : Nat), Eq.{1} Prop (Eq.{1} Bool (Nat.testBit (HPow.hPow.{0, 0, 0} Nat Nat Nat (instHPow.{0, 0} Nat Nat instPowNat) (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2)) n) m) Bool.true) (Eq.{1} Nat n m)
Case conversion may be inaccurate. Consider using '#align nat.test_bit_two_pow Nat.testBit_two_powₓ'. -/
theorem testBit_two_pow (n m : ℕ) : testBit (2 ^ n) m = (n = m) :=
  by
  by_cases n = m
  · cases h
    simp
  · rw [test_bit_two_pow_of_ne h]
    simp [h]
#align nat.test_bit_two_pow Nat.testBit_two_pow

#print Nat.bitwise'_comm /-
/-- If `f` is a commutative operation on bools such that `f ff ff = ff`, then `bitwise f` is also
    commutative. -/
theorem bitwise'_comm {f : Bool → Bool → Bool} (hf : ∀ b b', f b b' = f b' b)
    (hf' : f false false = false) (n m : ℕ) : bitwise' f n m = bitwise' f m n :=
  suffices bitwise' f = swap (bitwise' f) by conv_lhs => rw [this]
  calc
    bitwise' f = bitwise' (swap f) := congr_arg _ <| funext fun _ => funext <| hf _
    _ = swap (bitwise' f) := bitwise'_swap hf'

#align nat.bitwise_comm Nat.bitwise'_comm
-/

#print Nat.lor'_comm /-
theorem lor'_comm (n m : ℕ) : lor' n m = lor' m n :=
  bitwise'_comm Bool.or_comm rfl n m
#align nat.lor_comm Nat.lor'_comm
-/

#print Nat.land'_comm /-
theorem land'_comm (n m : ℕ) : land' n m = land' m n :=
  bitwise'_comm Bool.and_comm rfl n m
#align nat.land_comm Nat.land'_comm
-/

#print Nat.lxor'_comm /-
theorem lxor'_comm (n m : ℕ) : lxor' n m = lxor' m n :=
  bitwise'_comm Bool.xor_comm rfl n m
#align nat.lxor_comm Nat.lxor'_comm
-/

#print Nat.zero_lxor' /-
@[simp]
theorem zero_lxor' (n : ℕ) : lxor' 0 n = n := by simp [lxor]
#align nat.zero_lxor Nat.zero_lxor'
-/

#print Nat.lxor'_zero /-
@[simp]
theorem lxor'_zero (n : ℕ) : lxor' n 0 = n := by simp [lxor]
#align nat.lxor_zero Nat.lxor'_zero
-/

#print Nat.zero_land' /-
@[simp]
theorem zero_land' (n : ℕ) : land' 0 n = 0 := by simp [land]
#align nat.zero_land Nat.zero_land'
-/

#print Nat.land'_zero /-
@[simp]
theorem land'_zero (n : ℕ) : land' n 0 = 0 := by simp [land]
#align nat.land_zero Nat.land'_zero
-/

#print Nat.zero_lor' /-
@[simp]
theorem zero_lor' (n : ℕ) : lor' 0 n = n := by simp [lor]
#align nat.zero_lor Nat.zero_lor'
-/

#print Nat.lor'_zero /-
@[simp]
theorem lor'_zero (n : ℕ) : lor' n 0 = n := by simp [lor]
#align nat.lor_zero Nat.lor'_zero
-/

/- ./././Mathport/Syntax/Translate/Expr.lean:330:4: warning: unsupported (TODO): `[tacs] -/
/-- Proving associativity of bitwise operations in general essentially boils down to a huge case
    distinction, so it is shorter to use this tactic instead of proving it in the general case. -/
unsafe def bitwise_assoc_tac : tactic Unit :=
  sorry
#align nat.bitwise_assoc_tac nat.bitwise_assoc_tac

/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic nat.bitwise_assoc_tac -/
#print Nat.lxor'_assoc /-
theorem lxor'_assoc (n m k : ℕ) : lxor' (lxor' n m) k = lxor' n (lxor' m k) := by
  run_tac
    bitwise_assoc_tac
#align nat.lxor_assoc Nat.lxor'_assoc
-/

/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic nat.bitwise_assoc_tac -/
#print Nat.land'_assoc /-
theorem land'_assoc (n m k : ℕ) : land' (land' n m) k = land' n (land' m k) := by
  run_tac
    bitwise_assoc_tac
#align nat.land_assoc Nat.land'_assoc
-/

/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic nat.bitwise_assoc_tac -/
#print Nat.lor'_assoc /-
theorem lor'_assoc (n m k : ℕ) : lor' (lor' n m) k = lor' n (lor' m k) := by
  run_tac
    bitwise_assoc_tac
#align nat.lor_assoc Nat.lor'_assoc
-/

#print Nat.lxor'_self /-
@[simp]
theorem lxor'_self (n : ℕ) : lxor' n n = 0 :=
  zero_of_testBit_eq_false fun i => by simp
#align nat.lxor_self Nat.lxor'_self
-/

#print Nat.lxor_cancel_right /-
-- These lemmas match `mul_inv_cancel_right` and `mul_inv_cancel_left`.
theorem lxor_cancel_right (n m : ℕ) : lxor' (lxor' m n) n = m := by
  rw [lxor_assoc, lxor_self, lxor_zero]
#align nat.lxor_cancel_right Nat.lxor_cancel_right
-/

#print Nat.lxor'_cancel_left /-
theorem lxor'_cancel_left (n m : ℕ) : lxor' n (lxor' n m) = m := by
  rw [← lxor_assoc, lxor_self, zero_lxor]
#align nat.lxor_cancel_left Nat.lxor'_cancel_left
-/

#print Nat.lxor'_right_injective /-
theorem lxor'_right_injective {n : ℕ} : Function.Injective (lxor' n) := fun m m' h => by
  rw [← lxor_cancel_left n m, ← lxor_cancel_left n m', h]
#align nat.lxor_right_injective Nat.lxor'_right_injective
-/

#print Nat.lxor'_left_injective /-
theorem lxor'_left_injective {n : ℕ} : Function.Injective fun m => lxor' m n :=
  fun m m' (h : lxor' m n = lxor' m' n) => by
  rw [← lxor_cancel_right n m, ← lxor_cancel_right n m', h]
#align nat.lxor_left_injective Nat.lxor'_left_injective
-/

#print Nat.lxor'_right_inj /-
@[simp]
theorem lxor'_right_inj {n m m' : ℕ} : lxor' n m = lxor' n m' ↔ m = m' :=
  lxor'_right_injective.eq_iff
#align nat.lxor_right_inj Nat.lxor'_right_inj
-/

#print Nat.lxor'_left_inj /-
@[simp]
theorem lxor'_left_inj {n m m' : ℕ} : lxor' m n = lxor' m' n ↔ m = m' :=
  lxor'_left_injective.eq_iff
#align nat.lxor_left_inj Nat.lxor'_left_inj
-/

#print Nat.lxor'_eq_zero /-
@[simp]
theorem lxor'_eq_zero {n m : ℕ} : lxor' n m = 0 ↔ n = m := by
  rw [← lxor_self n, lxor_right_inj, eq_comm]
#align nat.lxor_eq_zero Nat.lxor'_eq_zero
-/

#print Nat.lxor'_ne_zero /-
theorem lxor'_ne_zero {n m : ℕ} : lxor' n m ≠ 0 ↔ n ≠ m :=
  lxor'_eq_zero.Not
#align nat.lxor_ne_zero Nat.lxor'_ne_zero
-/

#print Nat.lxor'_trichotomy /-
theorem lxor'_trichotomy {a b c : ℕ} (h : a ≠ lxor' b c) :
    lxor' b c < a ∨ lxor' a c < b ∨ lxor' a b < c :=
  by
  set v := lxor a (lxor b c) with hv
  -- The xor of any two of `a`, `b`, `c` is the xor of `v` and the third.
  have hab : lxor a b = lxor c v := by
    rw [hv]
    conv_rhs =>
      rw [lxor_comm]
      simp [lxor_assoc]
  have hac : lxor a c = lxor b v := by
    rw [hv]
    conv_rhs =>
      congr
      skip
      rw [lxor_comm]
    rw [← lxor_assoc, ← lxor_assoc, lxor_self, zero_lxor, lxor_comm]
  have hbc : lxor b c = lxor a v := by simp [hv, ← lxor_assoc]
  -- If `i` is the position of the most significant bit of `v`, then at least one of `a`, `b`, `c`
  -- has a one bit at position `i`.
  obtain ⟨i, ⟨hi, hi'⟩⟩ := exists_most_significant_bit (lxor_ne_zero.2 h)
  have : test_bit a i = tt ∨ test_bit b i = tt ∨ test_bit c i = tt :=
    by
    contrapose! hi
    simp only [Bool.eq_false_eq_not_eq_true, Ne, test_bit_lxor] at hi⊢
    rw [hi.1, hi.2.1, hi.2.2, Bool.xor_false, Bool.xor_false]
  -- If, say, `a` has a one bit at position `i`, then `a xor v` has a zero bit at position `i`, but
      -- the same bits as `a` in positions greater than `j`, so `a xor v < a`.
      rcases this with (h | h | h) <;>
      [·
        left
        rw [hbc],
      · right
        left
        rw [hac],
      · right
        right
        rw [hab]] <;>
    exact lt_of_test_bit i (by simp [h, hi]) h fun j hj => by simp [hi' _ hj]
#align nat.lxor_trichotomy Nat.lxor'_trichotomy
-/

#print Nat.lt_lxor'_cases /-
theorem lt_lxor'_cases {a b c : ℕ} (h : a < lxor' b c) : lxor' a c < b ∨ lxor' a b < c :=
  (or_iff_right fun h' => (h.asymm h').elim).1 <| lxor'_trichotomy h.Ne
#align nat.lt_lxor_cases Nat.lt_lxor'_cases
-/

end Nat


/-
Copyright (c) 2021 Yury Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury Kudryashov
-/
import topology.metric_space.basic
import topology.algebra.ordered.monotone_convergence
import data.set.intervals.monotone

/-!
# Rectangular boxes in `ℝⁿ`

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

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

## Main definitions

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

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

We define the following operations on boxes:

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

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

## Tags

rectangular box
-/

open set function metric filter

noncomputable theory
open_locale nnreal classical topological_space

namespace box_integral

variables {ι : Type*}

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

/-- A nontrivial rectangular box in `ι → ℝ` with corners `lower` and `upper`. Repesents the product
of half-open intervals `(lower i, upper i]`. -/
structure box (ι : Type*) :=
(lower upper : ι → ℝ)
(lower_lt_upper : ∀ i, lower i < upper i)

attribute [simp] box.lower_lt_upper

namespace box

variables (I J : box ι) {x y : ι → ℝ}

instance : inhabited (box ι) := ⟨⟨0, 1, λ i, zero_lt_one⟩⟩

lemma lower_le_upper : I.lower ≤ I.upper := λ i, (I.lower_lt_upper i).le
lemma lower_ne_upper (i) : I.lower i ≠ I.upper i := (I.lower_lt_upper i).ne

instance : has_mem (ι → ℝ) (box ι) := ⟨λ x I, ∀ i, x i ∈ Ioc (I.lower i) (I.upper i)⟩
instance : has_coe_t (box ι) (set $ ι → ℝ) := ⟨λ I, {x | x ∈ I}⟩

@[simp] lemma mem_mk {l u x : ι → ℝ} {H} : x ∈ mk l u H ↔ ∀ i, x i ∈ Ioc (l i) (u i) := iff.rfl
@[simp, norm_cast] lemma mem_coe : x ∈ (I : set (ι → ℝ)) ↔ x ∈ I := iff.rfl

lemma mem_def : x ∈ I ↔ ∀ i, x i ∈ Ioc (I.lower i) (I.upper i) := iff.rfl

lemma mem_univ_Ioc {I : box ι}  : x ∈ pi univ (λ i, Ioc (I.lower i) (I.upper i)) ↔ x ∈ I :=
mem_univ_pi

lemma coe_eq_pi : (I : set (ι → ℝ)) = pi univ (λ i, Ioc (I.lower i) (I.upper i)) :=
set.ext $ λ x, mem_univ_Ioc.symm

@[simp] lemma upper_mem : I.upper ∈ I := λ i, right_mem_Ioc.2 $ I.lower_lt_upper i

lemma exists_mem : ∃ x, x ∈ I := ⟨_, I.upper_mem⟩
lemma nonempty_coe : set.nonempty (I : set (ι → ℝ)) := I.exists_mem
@[simp] lemma coe_ne_empty : (I : set (ι → ℝ)) ≠ ∅ := I.nonempty_coe.ne_empty
@[simp] lemma empty_ne_coe : ∅ ≠ (I : set (ι → ℝ)) := I.coe_ne_empty.symm

instance : has_le (box ι) := ⟨λ I J, ∀ ⦃x⦄, x ∈ I → x ∈ J⟩

lemma le_def : I ≤ J ↔ ∀ x ∈ I, x ∈ J := iff.rfl

lemma le_tfae :
  tfae [I ≤ J,
    (I : set (ι → ℝ)) ⊆ J,
    Icc I.lower I.upper ⊆ Icc J.lower J.upper,
    J.lower ≤ I.lower ∧ I.upper ≤ J.upper] :=
begin
  tfae_have : 1 ↔ 2, from iff.rfl,
  tfae_have : 2 → 3,
  { intro h,
    simpa [coe_eq_pi, closure_pi_set, lower_ne_upper] using closure_mono h },
  tfae_have : 3 ↔ 4, from Icc_subset_Icc_iff I.lower_le_upper,
  tfae_have : 4 → 2, from λ h x hx i, Ioc_subset_Ioc (h.1 i) (h.2 i) (hx i),
  tfae_finish
end

variables {I J}

@[simp, norm_cast] lemma coe_subset_coe : (I : set (ι → ℝ)) ⊆ J ↔ I ≤ J := iff.rfl
lemma le_iff_bounds : I ≤ J ↔ J.lower ≤ I.lower ∧ I.upper ≤ J.upper := (le_tfae I J).out 0 3

lemma injective_coe : injective (coe : box ι → set (ι → ℝ)) :=
begin
  rintros ⟨l₁, u₁, h₁⟩ ⟨l₂, u₂, h₂⟩ h,
  simp only [subset.antisymm_iff, coe_subset_coe, le_iff_bounds] at h,
  congr,
  exacts [le_antisymm h.2.1 h.1.1, le_antisymm h.1.2 h.2.2]
end

@[simp, norm_cast] lemma coe_inj : (I : set (ι → ℝ)) = J ↔ I = J :=
injective_coe.eq_iff

@[ext] lemma ext (H : ∀ x, x ∈ I ↔ x ∈ J) : I = J :=
injective_coe $ set.ext H

lemma ne_of_disjoint_coe (h : disjoint (I : set (ι → ℝ)) J) : I ≠ J :=
mt coe_inj.2 $ h.ne I.coe_ne_empty

instance : partial_order (box ι) :=
{ le := (≤),
  .. partial_order.lift (coe : box ι → set (ι → ℝ)) injective_coe }

/-- Closed box corresponding to `I : box_integral.box ι`. -/
protected def Icc : box ι ↪o set (ι → ℝ) :=
order_embedding.of_map_le_iff (λ I : box ι, Icc I.lower I.upper) (λ I J, (le_tfae I J).out 2 0)

lemma Icc_def : I.Icc = Icc I.lower I.upper := rfl

@[simp] lemma upper_mem_Icc (I : box ι) : I.upper ∈ I.Icc := right_mem_Icc.2 I.lower_le_upper
@[simp] lemma lower_mem_Icc (I : box ι) : I.lower ∈ I.Icc := left_mem_Icc.2 I.lower_le_upper

protected lemma is_compact_Icc (I : box ι) : is_compact I.Icc := is_compact_Icc

lemma Icc_eq_pi : I.Icc = pi univ (λ i, Icc (I.lower i) (I.upper i)) := (pi_univ_Icc _ _).symm

lemma le_iff_Icc : I ≤ J ↔ I.Icc ⊆ J.Icc := (le_tfae I J).out 0 2

lemma antitone_lower : antitone (λ I : box ι, I.lower) :=
λ I J H, (le_iff_bounds.1 H).1

lemma monotone_upper : monotone (λ I : box ι, I.upper) :=
λ I J H, (le_iff_bounds.1 H).2

lemma coe_subset_Icc : ↑I ⊆ I.Icc := λ x hx, ⟨λ i, (hx i).1.le, λ i, (hx i).2⟩

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

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

instance : semilattice_sup (box ι) :=
{ le_sup_left := λ I J, le_iff_bounds.2 ⟨inf_le_left, le_sup_left⟩,
  le_sup_right := λ I J, le_iff_bounds.2 ⟨inf_le_right, le_sup_right⟩,
  sup_le := λ I₁ I₂ J h₁ h₂, le_iff_bounds.2 ⟨le_inf (antitone_lower h₁) (antitone_lower h₂),
    sup_le (monotone_upper h₁) (monotone_upper h₂)⟩,
  .. box.partial_order, .. box.has_sup }

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

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

instance with_bot_coe : has_coe_t (with_bot (box ι)) (set (ι → ℝ)) := ⟨λ o, o.elim ∅ coe⟩

@[simp, norm_cast] lemma coe_bot : ((⊥ : with_bot (box ι)) : set (ι → ℝ)) = ∅ := rfl

@[simp, norm_cast] lemma coe_coe : ((I : with_bot (box ι)) : set (ι → ℝ)) = I := rfl

lemma is_some_iff : ∀ {I : with_bot (box ι)}, I.is_some ↔ (I : set (ι → ℝ)).nonempty
| ⊥ := by { erw option.is_some, simp }
| (I : box ι) := by { erw option.is_some, simp [I.nonempty_coe] }

lemma bUnion_coe_eq_coe (I : with_bot (box ι)) :
  (⋃ (J : box ι) (hJ : ↑J = I), (J : set (ι → ℝ))) = I :=
by induction I using with_bot.rec_bot_coe; simp [with_bot.coe_eq_coe]

@[simp, norm_cast] lemma with_bot_coe_subset_iff {I J : with_bot (box ι)} :
  (I : set (ι → ℝ)) ⊆ J ↔ I ≤ J :=
begin
  induction I using with_bot.rec_bot_coe, { simp },
  induction J using with_bot.rec_bot_coe, { simp [subset_empty_iff] },
  simp
end

@[simp, norm_cast] lemma with_bot_coe_inj {I J : with_bot (box ι)} :
  (I : set (ι → ℝ)) = J ↔ I = J :=
by simp only [subset.antisymm_iff, ← le_antisymm_iff,  with_bot_coe_subset_iff]

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

@[simp] lemma mk'_eq_bot {l u : ι → ℝ} : mk' l u = ⊥ ↔ ∃ i, u i ≤ l i :=
by { rw mk', split_ifs; simpa using h }

@[simp] lemma mk'_eq_coe {l u : ι → ℝ} : mk' l u = I ↔ l = I.lower ∧ u = I.upper :=
begin
  cases I with lI uI hI, rw mk', split_ifs,
  { simp [with_bot.coe_eq_coe] },
  { suffices : l = lI → u ≠ uI, by simpa,
    rintro rfl rfl, exact h hI }
end

@[simp] lemma coe_mk' (l u : ι → ℝ) : (mk' l u : set (ι → ℝ)) = pi univ (λ i, Ioc (l i) (u i)) :=
begin
  rw mk', split_ifs,
  { exact coe_eq_pi _ },
  { rcases not_forall.mp h with ⟨i, hi⟩,
    rw [coe_bot, univ_pi_eq_empty], exact Ioc_eq_empty hi }
end

instance : has_inf (with_bot (box ι)) :=
⟨λ I, with_bot.rec_bot_coe (λ J, ⊥) (λ I J, with_bot.rec_bot_coe ⊥
  (λ J, mk' (I.lower ⊔ J.lower) (I.upper ⊓ J.upper)) J) I⟩

@[simp] lemma coe_inf (I J : with_bot (box ι)) : (↑(I ⊓ J) : set (ι → ℝ)) = I ∩ J :=
begin
  induction I using with_bot.rec_bot_coe, { change ∅ = _, simp },
  induction J using with_bot.rec_bot_coe, { change ∅ = _, simp },
  change ↑(mk' _ _) = _,
  simp only [coe_eq_pi, ← pi_inter_distrib, Ioc_inter_Ioc, pi.sup_apply, pi.inf_apply, coe_mk',
    coe_coe]
end

instance : lattice (with_bot (box ι)) :=
{ inf_le_left := λ I J,
    begin
      rw [← with_bot_coe_subset_iff, coe_inf],
      exact inter_subset_left _ _
    end,
  inf_le_right := λ I J,
    begin
      rw [← with_bot_coe_subset_iff, coe_inf],
      exact inter_subset_right _ _
    end,
  le_inf := λ I J₁ J₂ h₁ h₂,
    begin
      simp only [← with_bot_coe_subset_iff, coe_inf] at *,
      exact subset_inter h₁ h₂
    end,
  .. with_bot.semilattice_sup, .. box.with_bot.has_inf }

@[simp, norm_cast] lemma disjoint_with_bot_coe {I J : with_bot (box ι)} :
  disjoint (I : set (ι → ℝ)) J ↔ disjoint I J :=
by { simp only [disjoint, ← with_bot_coe_subset_iff, coe_inf], refl }

lemma not_disjoint_coe_iff_nonempty_inter :
  ¬disjoint (I : with_bot (box ι)) J ↔ (I ∩ J : set (ι → ℝ)).nonempty :=
by rw [disjoint_coe, set.not_disjoint_iff_nonempty_inter]

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

/-- Face of a box in `ℝⁿ⁺¹ = fin (n + 1) → ℝ`: the box in `ℝⁿ = fin n → ℝ` with corners at
`I.lower ∘ fin.succ_above i` and `I.upper ∘ fin.succ_above i`. -/
@[simps { simp_rhs := tt }] def face {n} (I : box (fin (n + 1))) (i : fin (n + 1)) : box (fin n) :=
⟨I.lower ∘ fin.succ_above i, I.upper ∘ fin.succ_above i, λ j, I.lower_lt_upper _⟩

@[simp] lemma face_mk {n} (l u : fin (n + 1) → ℝ) (h : ∀ i, l i < u i) (i : fin (n + 1)) :
  face ⟨l, u, h⟩ i = ⟨l ∘ fin.succ_above i, u ∘ fin.succ_above i, λ j, h _⟩ :=
rfl

@[mono] lemma face_mono {n} {I J : box (fin (n + 1))} (h : I ≤ J) (i : fin (n + 1)) :
  face I i ≤ face J i :=
λ x hx i, Ioc_subset_Ioc ((le_iff_bounds.1 h).1 _) ((le_iff_bounds.1 h).2 _) (hx _)

lemma monotone_face {n} (i : fin (n + 1)) : monotone (λ I, face I i) := λ I J h, face_mono h i

lemma maps_to_insert_nth_face_Icc {n} (I : box (fin (n + 1))) {i : fin (n + 1)} {x : ℝ}
  (hx : x ∈ Icc (I.lower i) (I.upper i)) :
  maps_to (i.insert_nth x) (I.face i).Icc I.Icc :=
λ y hy, fin.insert_nth_mem_Icc.2 ⟨hx, hy⟩

lemma maps_to_insert_nth_face {n} (I : box (fin (n + 1))) {i : fin (n + 1)} {x : ℝ}
  (hx : x ∈ Ioc (I.lower i) (I.upper i)) :
  maps_to (i.insert_nth x) (I.face i) I :=
λ y hy, by simpa only [mem_coe, mem_def, i.forall_iff_succ_above, hx, fin.insert_nth_apply_same,
  fin.insert_nth_apply_succ_above, true_and]

lemma continuous_on_face_Icc {X} [topological_space X] {n} {f : (fin (n + 1) → ℝ) → X}
  {I : box (fin (n + 1))} (h : continuous_on f I.Icc) {i : fin (n + 1)} {x : ℝ}
  (hx : x ∈ Icc (I.lower i) (I.upper i)) :
  continuous_on (f ∘ i.insert_nth x) (I.face i).Icc :=
h.comp (continuous_on_const.fin_insert_nth i continuous_on_id) (I.maps_to_insert_nth_face_Icc hx)

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

/-- The interior of a box. -/
protected def Ioo : box ι →o set (ι → ℝ) :=
{ to_fun := λ I, pi univ (λ i, Ioo (I.lower i) (I.upper i)),
  monotone' := λ I J h, pi_mono $ λ i hi, Ioo_subset_Ioo ((le_iff_bounds.1 h).1 i)
    ((le_iff_bounds.1 h).2 i) }

lemma Ioo_subset_coe (I : box ι) : I.Ioo ⊆ I := λ x hx i, Ioo_subset_Ioc_self (hx i trivial)

protected lemma Ioo_subset_Icc (I : box ι) : I.Ioo ⊆ I.Icc := I.Ioo_subset_coe.trans coe_subset_Icc

lemma Union_Ioo_of_tendsto [fintype ι] {I : box ι} {J : ℕ → box ι} (hJ : monotone J)
  (hl : tendsto (lower ∘ J) at_top (𝓝 I.lower)) (hu : tendsto (upper ∘ J) at_top (𝓝 I.upper)) :
  (⋃ n, (J n).Ioo) = I.Ioo :=
have hl' : ∀ i, antitone (λ n, (J n).lower i),
  from λ i, (monotone_eval i).comp_antitone (antitone_lower.comp_monotone hJ),
have hu' : ∀ i, monotone (λ n, (J n).upper i),
  from λ i, (monotone_eval i).comp (monotone_upper.comp hJ),
calc (⋃ n, (J n).Ioo) = pi univ (λ i, ⋃ n, Ioo ((J n).lower i) ((J n).upper i)) :
  Union_univ_pi_of_monotone (λ i, (hl' i).Ioo (hu' i))
... = I.Ioo :
  pi_congr rfl (λ i hi, Union_Ioo_of_mono_of_is_glb_of_is_lub (hl' i) (hu' i)
    (is_glb_of_tendsto_at_top (hl' i) (tendsto_pi_nhds.1 hl _))
    (is_lub_of_tendsto_at_top (hu' i) (tendsto_pi_nhds.1 hu _)))

lemma exists_seq_mono_tendsto (I : box ι) : ∃ J : ℕ →o box ι, (∀ n, (J n).Icc ⊆ I.Ioo) ∧
  tendsto (lower ∘ J) at_top (𝓝 I.lower) ∧ tendsto (upper ∘ J) at_top (𝓝 I.upper) :=
begin
  choose a b ha_anti hb_mono ha_mem hb_mem hab ha_tendsto hb_tendsto
    using λ i, exists_seq_strict_anti_strict_mono_tendsto (I.lower_lt_upper i),
  exact ⟨⟨λ k, ⟨flip a k, flip b k, λ i, hab _ _ _⟩,
    λ k l hkl, le_iff_bounds.2 ⟨λ i, (ha_anti i).antitone hkl, λ i, (hb_mono i).monotone hkl⟩⟩,
    λ n x hx i hi, ⟨(ha_mem _ _).1.trans_le (hx.1 _), (hx.2 _).trans_lt (hb_mem _ _).2⟩,
    tendsto_pi_nhds.2 ha_tendsto, tendsto_pi_nhds.2 hb_tendsto⟩
end

section distortion

variable [fintype ι]

/-- The distortion of a box `I` is the maximum of the ratios of the lengths of its edges.
It is defined as the maximum of the ratios
`nndist I.lower I.upper / nndist (I.lower i) (I.upper i)`. -/
def distortion (I : box ι) : ℝ≥0 :=
finset.univ.sup $ λ i : ι, nndist I.lower I.upper / nndist (I.lower i) (I.upper i)

lemma distortion_eq_of_sub_eq_div {I J : box ι} {r : ℝ}
  (h : ∀ i, I.upper i - I.lower i = (J.upper i - J.lower i) / r) :
  distortion I = distortion J :=
begin
  simp only [distortion, nndist_pi_def, real.nndist_eq', h, real.nnabs.map_div],
  congr' 1 with i,
  have : 0 < r,
  { by_contra hr,
    have := div_nonpos_of_nonneg_of_nonpos (sub_nonneg.2 $ J.lower_le_upper i) (not_lt.1 hr),
    rw ← h at this,
    exact this.not_lt (sub_pos.2 $ I.lower_lt_upper i) },
  simp only [nnreal.finset_sup_div, div_div_div_cancel_right _ (real.nnabs.map_ne_zero.2 this.ne')]
end

lemma nndist_le_distortion_mul (I : box ι) (i : ι) :
  nndist I.lower I.upper ≤ I.distortion * nndist (I.lower i) (I.upper i) :=
calc nndist I.lower I.upper =
  (nndist I.lower I.upper / nndist (I.lower i) (I.upper i)) *  nndist (I.lower i) (I.upper i) :
  (div_mul_cancel _ $ mt nndist_eq_zero.1 (I.lower_lt_upper i).ne).symm
... ≤ I.distortion * nndist (I.lower i) (I.upper i) :
  mul_le_mul_right' (finset.le_sup $ finset.mem_univ i) _

lemma dist_le_distortion_mul (I : box ι) (i : ι) :
  dist I.lower I.upper ≤ I.distortion * (I.upper i - I.lower i) :=
have A : I.lower i - I.upper i < 0, from sub_neg.2 (I.lower_lt_upper i),
by simpa only [← nnreal.coe_le_coe, ← dist_nndist, nnreal.coe_mul, real.dist_eq,
  abs_of_neg A, neg_sub] using I.nndist_le_distortion_mul i

lemma diam_Icc_le_of_distortion_le (I : box ι) (i : ι) {c : ℝ≥0} (h : I.distortion ≤ c) :
  diam I.Icc ≤ c * (I.upper i - I.lower i) :=
have (0 : ℝ) ≤ c * (I.upper i - I.lower i),
  from mul_nonneg c.coe_nonneg (sub_nonneg.2 $ I.lower_le_upper _),
diam_le_of_forall_dist_le this $ λ x hx y hy,
  calc dist x y ≤ dist I.lower I.upper : real.dist_le_of_mem_pi_Icc hx hy
  ... ≤ I.distortion * (I.upper i - I.lower i) : I.dist_le_distortion_mul i
  ... ≤ c * (I.upper i - I.lower i) :
    mul_le_mul_of_nonneg_right h (sub_nonneg.2 (I.lower_le_upper i))

end distortion

end box

end box_integral