/-
Copyright (c) 2021 Yury Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury Kudryashov
-/
import measure_theory.measure.complex_lebesgue
import measure_theory.integral.divergence_theorem
import measure_theory.integral.circle_integral
import analysis.calculus.dslope
import analysis.analytic.basic
import analysis.complex.re_im_topology
import analysis.calculus.diff_on_int_cont
import data.real.cardinality

/-!
# Cauchy integral formula

In this file we prove the Cauchy-Goursat theorem and the Cauchy integral formula for integrals over
circles. Most results are formulated for a function `f : ℂ → E` that takes values in a complex
Banach space with second countable topology.

## Main statements

In the following theorems, if the name ends with `off_countable`, then the actual theorem assumes
differentiability at all but countably many points of the set mentioned below.

* `complex.integral_boundary_rect_of_has_fderiv_within_at_real_off_countable`: If a function
  `f : ℂ → E` is continuous on a closed rectangle and *real* differentiable on its interior, then
  its integral over the boundary of this rectangle is equal to the integral of
  `I • f' (x + y * I) 1 - f' (x + y * I) I` over the rectangle, where `f' z w : E` is the derivative
  of `f` at `z` in the direction `w` and `I = complex.I` is the imaginary unit.

* `complex.integral_boundary_rect_eq_zero_of_differentiable_on_off_countable`: If a function
  `f : ℂ → E` is continuous on a closed rectangle and is *complex* differentiable on its interior,
  then its integral over the boundary of this rectangle is equal to zero.

* `complex.circle_integral_sub_center_inv_smul_eq_of_differentiable_on_annulus_off_countable`: If a
  function `f : ℂ → E` is continuous on a closed annulus `{z | r ≤ |z - c| ≤ R}` and is complex
  differentiable on its interior `{z | r < |z - c| < R}`, then the integrals of `(z - c)⁻¹ • f z`
  over the outer boundary and over the inner boundary are equal.

* `complex.circle_integral_sub_center_inv_smul_of_differentiable_on_off_countable_of_tendsto`,
  `complex.circle_integral_sub_center_inv_smul_of_differentiable_on_off_countable`:
  If a function `f : ℂ → E` is continuous on a punctured closed disc `{z | |z - c| ≤ R ∧ z ≠ c}`, is
  complex differentiable on the corresponding punctured open disc, and tends to `y` as `z → c`,
  `z ≠ c`, then the integral of `(z - c)⁻¹ • f z` over the circle `|z - c| = R` is equal to
  `2πiy`. In particular, if `f` is continuous on the whole closed disc and is complex differentiable
  on the corresponding open disc, then this integral is equal to `2πif(c)`.

* `complex.circle_integral_sub_inv_smul_of_differentiable_on_off_countable`,
  `complex.two_pi_I_inv_smul_circle_integral_sub_inv_smul_of_differentiable_on_off_countable`
  **Cauchy integral formula**: if `f : ℂ → E` is continuous on a closed disc of radius `R` and is
  complex differentiable on the corresponding open disc, then for any `w` in the corresponding open
  disc the integral of `(z - w)⁻¹ • f z` over the boundary of the disc is equal to `2πif(w)`.
  Two versions of the lemma put the multiplier `2πi` at the different sides of the equality.

* `complex.has_fpower_series_on_ball_of_differentiable_off_countable`: If `f : ℂ → E` is continuous
  on a closed disc of positive radius and is complex differentiable on the corresponding open disc,
  then it is analytic on the corresponding open disc, and the coefficients of the power series are
  given by Cauchy integral formulas.

* `differentiable_on.has_fpower_series_on_ball`: If `f : ℂ → E` is complex differentiable on a
  closed disc of positive radius, then it is analytic on the corresponding open disc, and the
  coefficients of the power series are given by Cauchy integral formulas.

* `differentiable_on.analytic_at`, `differentiable.analytic_at`: If `f : ℂ → E` is differentiable
  on a neighborhood of a point, then it is analytic at this point. In particular, if `f : ℂ → E`
  is differentiable on the whole `ℂ`, then it is analytic at every point `z : ℂ`.

* `differentiable.has_power_series_on_ball`: If `f : ℂ → E` is differentiable everywhere then the
  `cauchy_power_series f z R` is a formal power series representing `f` at `z` with infinite
  radius of convergence (this holds for any choice of `0 < R`).

## Implementation details

The proof of the Cauchy integral formula in this file is based on a very general version of the
divergence theorem, see `measure_theory.integral_divergence_of_has_fderiv_within_at_off_countable`
(a version for functions defined on `fin (n + 1) → ℝ`),
`measure_theory.integral_divergence_prod_Icc_of_has_fderiv_within_at_off_countable_of_le`, and
`measure_theory.integral2_divergence_prod_of_has_fderiv_within_at_off_countable` (versions for
functions defined on `ℝ × ℝ`).

Usually, the divergence theorem is formulated for a $C^1$ smooth function. The theorems formulated
above deal with a function that is

* continuous on a closed box/rectangle;
* differentiable at all but countably many points of its interior;
* have divergence integrable over the closed box/rectangle.

First, we reformulate the theorem for a *real*-differentiable map `ℂ → E`, and relate the integral
of `f` over the boundary of a rectangle in `ℂ` to the integral of the derivative
$\frac{\partial f}{\partial \bar z}$ over the interior of this box. In particular, for a *complex*
differentiable function, the latter derivative is zero, hence the integral over the boundary of a
rectangle is zero. Thus we get the Cauchy-Goursat theorem for a rectangle in `ℂ`.

Next, we apply the this theorem to the function $F(z)=f(c+e^{z})$ on the rectangle
$[\ln r, \ln R]\times [0, 2\pi]$ to prove that
$$
  \oint_{|z-c|=r}\frac{f(z)\,dz}{z-c}=\oint_{|z-c|=R}\frac{f(z)\,dz}{z-c}
$$
provided that `f` is continuous on the closed annulus `r ≤ |z - c| ≤ R` and is complex
differentiable on its interior `r < |z - c| < R` (possibly, at all but countably many points).

Here and below, we write $\frac{f(z)}{z-c}$ in the documentation while the actual lemmas use
`(z - c)⁻¹ • f z` because `f z` belongs to some Banach space over `ℂ` and `f z / (z - c)` is
undefined.

Taking the limit of this equality as `r` tends to `𝓝[>] 0`, we prove
$$
  \oint_{|z-c|=R}\frac{f(z)\,dz}{z-c}=2\pi if(c)
$$
provided that `f` is continuous on the closed disc `|z - c| ≤ R` and is differentiable at all but
countably many points of its interior. This is the Cauchy integral formula for the center of a
circle. In particular, if we apply this function to `F z = (z - c) • f z`, then we get
$$
  \oint_{|z-c|=R} f(z)\,dz=0.
$$

In order to deduce the Cauchy integral formula for any point `w`, `|w - c| < R`, we consider the
slope function `g : ℂ → E` given by `g z = (z - w)⁻¹ • (f z - f w)` if `z ≠ w` and `g w = f' w`.
This function satisfies assumptions of the previous theorem, so we have
$$
  \oint_{|z-c|=R} \frac{f(z)\,dz}{z-w}=\oint_{|z-c|=R} \frac{f(w)\,dz}{z-w}=
  \left(\oint_{|z-c|=R} \frac{dz}{z-w}\right)f(w).
$$
The latter integral was computed in `circle_integral.integral_sub_inv_of_mem_ball` and is equal to
`2 * π * complex.I`.

There is one more step in the actual proof. Since we allow `f` to be non-differentiable on a
countable set `s`, we cannot immediately claim that `g` is continuous at `w` if `w ∈ s`. So, we use
the proof outlined in the previous paragraph for `w ∉ s` (see
`complex.circle_integral_sub_inv_smul_of_differentiable_on_off_countable_aux`), then use continuity
of both sides of the formula and density of `sᶜ` to prove the formula for all points of the open
ball, see `complex.circle_integral_sub_inv_smul_of_differentiable_on_off_countable`.

Finally, we use the properties of the Cauchy integrals established elsewhere (see
`has_fpower_series_on_cauchy_integral`) and Cauchy integral formula to prove that the original
function is analytic on the open ball.

## Tags

Cauchy-Goursat theorem, Cauchy integral formula
-/

open topological_space set measure_theory interval_integral metric filter function
open_locale interval real nnreal ennreal topological_space big_operators

noncomputable theory

universes u

variables {E : Type u} [normed_group E] [normed_space ℂ E] [complete_space E]

namespace complex

/-- Suppose that a function `f : ℂ → E` is continuous on a closed rectangle with opposite corners at
`z w : ℂ`, is *real* differentiable at all but countably many points of the corresponding open
rectangle, and $\frac{\partial f}{\partial \bar z}$ is integrable on this rectangle. Then the
integral of `f` over the boundary of the rectangle is equal to the integral of
$2i\frac{\partial f}{\partial \bar z}=i\frac{\partial f}{\partial x}-\frac{\partial f}{\partial y}$
over the rectangle. -/
lemma integral_boundary_rect_of_has_fderiv_at_real_off_countable (f : ℂ → E)
  (f' : ℂ → ℂ →L[ℝ] E) (z w : ℂ) (s : set ℂ) (hs : countable s)
  (Hc : continuous_on f ([z.re, w.re] ×ℂ [z.im, w.im]))
  (Hd : ∀ x ∈ (Ioo (min z.re w.re) (max z.re w.re) ×ℂ Ioo (min z.im w.im) (max z.im w.im)) \ s,
    has_fderiv_at f (f' x) x)
  (Hi : integrable_on (λ z, I • f' z 1 - f' z I) ([z.re, w.re] ×ℂ [z.im, w.im])) :
  (∫ x : ℝ in z.re..w.re, f (x + z.im * I)) - (∫ x : ℝ in z.re..w.re, f (x + w.im * I)) +
    (I • ∫ y : ℝ in z.im..w.im, f (re w + y * I)) - I • ∫ y : ℝ in z.im..w.im, f (re z + y * I) =
    ∫ x : ℝ in z.re..w.re, ∫ y : ℝ in z.im..w.im, I • f' (x + y * I) 1 - f' (x + y * I) I :=
begin
  set e : (ℝ × ℝ) ≃L[ℝ] ℂ := equiv_real_prodₗ.symm,
  have he : ∀ x y : ℝ, ↑x + ↑y * I = e (x, y), from λ x y, (mk_eq_add_mul_I x y).symm,
  have he₁ : e (1, 0) = 1 := rfl, have he₂ : e (0, 1) = I := rfl,
  simp only [he] at *,
  set F : (ℝ × ℝ) → E := f ∘ e,
  set F' : (ℝ × ℝ) → (ℝ × ℝ) →L[ℝ] E := λ p, (f' (e p)).comp (e : (ℝ × ℝ) →L[ℝ] ℂ),
  have hF' : ∀ p : ℝ × ℝ, (-(I • F' p)) (1, 0) + F' p (0, 1) = -(I • f' (e p) 1 - f' (e p) I),
  { rintro ⟨x, y⟩, simp [F', he₁, he₂, ← sub_eq_neg_add], },
  set R : set (ℝ × ℝ) := [z.re, w.re] ×ˢ [w.im, z.im],
  set t : set (ℝ × ℝ) := e ⁻¹' s,
  rw [interval_swap z.im] at Hc Hi, rw [min_comm z.im, max_comm z.im] at Hd,
  have hR : e ⁻¹' ([z.re, w.re] ×ℂ [w.im, z.im]) = R := rfl,
  have htc : continuous_on F R, from Hc.comp e.continuous_on hR.ge,
  have htd : ∀ p ∈ Ioo (min z.re w.re) (max z.re w.re) ×ˢ Ioo (min w.im z.im) (max w.im z.im) \ t,
    has_fderiv_at F (F' p) p := λ p hp, (Hd (e p) hp).comp p e.has_fderiv_at,
  simp_rw [← interval_integral.integral_smul, interval_integral.integral_symm w.im z.im,
    ← interval_integral.integral_neg, ← hF'],
  refine (integral2_divergence_prod_of_has_fderiv_within_at_off_countable
      (λ p, -(I • F p)) F (λ p, - (I • F' p)) F' z.re w.im w.re z.im t (hs.preimage e.injective)
      (htc.const_smul _).neg htc (λ p hp, ((htd p hp).const_smul I).neg) htd _).symm,
  rw [← volume_preserving_equiv_real_prod.symm.integrable_on_comp_preimage
    (measurable_equiv.measurable_embedding _)] at Hi,
  simpa only [hF'] using Hi.neg
end

/-- Suppose that a function `f : ℂ → E` is continuous on a closed rectangle with opposite corners at
`z w : ℂ`, is *real* differentiable on the corresponding open rectangle, and
$\frac{\partial f}{\partial \bar z}$ is integrable on this rectangle. Then the integral of `f` over
the boundary of the rectangle is equal to the integral of
$2i\frac{\partial f}{\partial \bar z}=i\frac{\partial f}{\partial x}-\frac{\partial f}{\partial y}$
over the rectangle. -/
lemma integral_boundary_rect_of_continuous_on_of_has_fderiv_at_real (f : ℂ → E)
  (f' : ℂ → ℂ →L[ℝ] E) (z w : ℂ)
  (Hc : continuous_on f ([z.re, w.re] ×ℂ [z.im, w.im]))
  (Hd : ∀ x ∈ (Ioo (min z.re w.re) (max z.re w.re) ×ℂ Ioo (min z.im w.im) (max z.im w.im)),
    has_fderiv_at f (f' x) x)
  (Hi : integrable_on (λ z, I • f' z 1 - f' z I) ([z.re, w.re] ×ℂ [z.im, w.im])) :
  (∫ x : ℝ in z.re..w.re, f (x + z.im * I)) - (∫ x : ℝ in z.re..w.re, f (x + w.im * I)) +
    (I • ∫ y : ℝ in z.im..w.im, f (re w + y * I)) - I • ∫ y : ℝ in z.im..w.im, f (re z + y * I) =
    ∫ x : ℝ in z.re..w.re, ∫ y : ℝ in z.im..w.im, I • f' (x + y * I) 1 - f' (x + y * I) I :=
integral_boundary_rect_of_has_fderiv_at_real_off_countable f f' z w ∅ countable_empty Hc
  (λ x hx, Hd x hx.1) Hi

/-- Suppose that a function `f : ℂ → E` is *real* differentiable on a closed rectangle with opposite
corners at `z w : ℂ` and $\frac{\partial f}{\partial \bar z}$ is integrable on this rectangle. Then
the integral of `f` over the boundary of the rectangle is equal to the integral of
$2i\frac{\partial f}{\partial \bar z}=i\frac{\partial f}{\partial x}-\frac{\partial f}{\partial y}$
over the rectangle. -/
lemma integral_boundary_rect_of_differentiable_on_real (f : ℂ → E) (z w : ℂ)
  (Hd : differentiable_on ℝ f ([z.re, w.re] ×ℂ [z.im, w.im]))
  (Hi : integrable_on (λ z, I • fderiv ℝ f z 1 - fderiv ℝ f z I) ([z.re, w.re] ×ℂ [z.im, w.im])) :
  (∫ x : ℝ in z.re..w.re, f (x + z.im * I)) - (∫ x : ℝ in z.re..w.re, f (x + w.im * I)) +
    (I • ∫ y : ℝ in z.im..w.im, f (re w + y * I)) - I • ∫ y : ℝ in z.im..w.im, f (re z + y * I) =
    ∫ x : ℝ in z.re..w.re, ∫ y : ℝ in z.im..w.im,
      I • fderiv ℝ f (x + y * I) 1 - fderiv ℝ f (x + y * I) I :=
integral_boundary_rect_of_has_fderiv_at_real_off_countable f (fderiv ℝ f) z w ∅ countable_empty
  Hd.continuous_on
  (λ x hx, Hd.has_fderiv_at $ by simpa only [← mem_interior_iff_mem_nhds,
    interior_re_prod_im, interval, interior_Icc] using hx.1) Hi

/-- **Cauchy-Goursat theorem** for a rectangle: the integral of a complex differentiable function
over the boundary of a rectangle equals zero. More precisely, if `f` is continuous on a closed
rectangle and is complex differentiable at all but countably many points of the corresponding open
rectangle, then its integral over the boundary of the rectangle equals zero. -/
lemma integral_boundary_rect_eq_zero_of_differentiable_on_off_countable (f : ℂ → E)
  (z w : ℂ) (s : set ℂ) (hs : countable s) (Hc : continuous_on f ([z.re, w.re] ×ℂ [z.im, w.im]))
  (Hd : ∀ x ∈ (Ioo (min z.re w.re) (max z.re w.re) ×ℂ Ioo (min z.im w.im) (max z.im w.im)) \ s,
    differentiable_at ℂ f x) :
  (∫ x : ℝ in z.re..w.re, f (x + z.im * I)) - (∫ x : ℝ in z.re..w.re, f (x + w.im * I)) +
    (I • ∫ y : ℝ in z.im..w.im, f (re w + y * I)) -
      I • ∫ y : ℝ in z.im..w.im, f (re z + y * I) = 0 :=
by refine (integral_boundary_rect_of_has_fderiv_at_real_off_countable f
  (λ z, (fderiv ℂ f z).restrict_scalars ℝ) z w s hs Hc
  (λ x hx, (Hd x hx).has_fderiv_at.restrict_scalars ℝ) _).trans _;
    simp [← continuous_linear_map.map_smul]

/-- **Cauchy-Goursat theorem for a rectangle**: the integral of a complex differentiable function
over the boundary of a rectangle equals zero. More precisely, if `f` is continuous on a closed
rectangle and is complex differentiable on the corresponding open rectangle, then its integral over
the boundary of the rectangle equals zero. -/
lemma integral_boundary_rect_eq_zero_of_continuous_on_of_differentiable_on (f : ℂ → E) (z w : ℂ)
  (Hc : continuous_on f ([z.re, w.re] ×ℂ [z.im, w.im]))
  (Hd : differentiable_on ℂ f
    (Ioo (min z.re w.re) (max z.re w.re) ×ℂ Ioo (min z.im w.im) (max z.im w.im))) :
  (∫ x : ℝ in z.re..w.re, f (x + z.im * I)) - (∫ x : ℝ in z.re..w.re, f (x + w.im * I)) +
    (I • ∫ y : ℝ in z.im..w.im, f (re w + y * I)) -
      I • ∫ y : ℝ in z.im..w.im, f (re z + y * I) = 0 :=
integral_boundary_rect_eq_zero_of_differentiable_on_off_countable f z w ∅ countable_empty
  Hc $ λ x hx, Hd.differentiable_at $ (is_open_Ioo.re_prod_im is_open_Ioo).mem_nhds hx.1

/-- **Cauchy-Goursat theorem** for a rectangle: the integral of a complex differentiable function
over the boundary of a rectangle equals zero. More precisely, if `f` is complex differentiable on a
closed rectangle, then its integral over the boundary of the rectangle equals zero. -/

/-- If `f : ℂ → E` is continuous the closed annulus `r ≤ ∥z - c∥ ≤ R`, `0 < r ≤ R`, and is complex
differentiable at all but countably many points of its interior, then the integrals of
`f z / (z - c)` (formally, `(z - c)⁻¹ • f z`) over the circles `∥z - c∥ = r` and `∥z - c∥ = R` are
equal to each other. -/
lemma circle_integral_sub_center_inv_smul_eq_of_differentiable_on_annulus_off_countable
  {c : ℂ} {r R : ℝ} (h0 : 0 < r) (hle : r ≤ R) {f : ℂ → E} {s : set ℂ} (hs : countable s)
  (hc : continuous_on f (closed_ball c R \ ball c r))
  (hd : ∀ z ∈ ball c R \ closed_ball c r \ s, differentiable_at ℂ f z) :
  ∮ z in C(c, R), (z - c)⁻¹ • f z = ∮ z in C(c, r), (z - c)⁻¹ • f z :=
begin
  /- We apply the previous lemma to `λ z, f (c + exp z)` on the rectangle
  `[log r, log R] × [0, 2 * π]`. -/
  set A := closed_ball c R \ ball c r,
  obtain ⟨a, rfl⟩ : ∃ a, real.exp a = r, from ⟨real.log r, real.exp_log h0⟩,
  obtain ⟨b, rfl⟩ : ∃ b, real.exp b = R, from ⟨real.log R, real.exp_log (h0.trans_le hle)⟩,
  rw [real.exp_le_exp] at hle,
  -- Unfold definition of `circle_integral` and cancel some terms.
  suffices : ∫ θ in 0..2 * π, I • f (circle_map c (real.exp b) θ) =
    ∫ θ in 0..2 * π, I • f (circle_map c (real.exp a) θ),
    by simpa only [circle_integral, add_sub_cancel', of_real_exp, ← exp_add, smul_smul,
      ← div_eq_mul_inv, mul_div_cancel_left _ (circle_map_ne_center (real.exp_pos _).ne'),
      circle_map_sub_center, deriv_circle_map],
  set R := [a, b] ×ℂ [0, 2 * π],
  set g : ℂ → ℂ := (+) c ∘ exp,
  have hdg : differentiable ℂ g := differentiable_exp.const_add _,
  replace hs : countable (g ⁻¹' s) := (hs.preimage (add_right_injective c)).preimage_cexp,
  have h_maps : maps_to g R A,
  { rintro z ⟨h, -⟩, simpa [dist_eq, g, abs_exp, hle] using h.symm },
  replace hc : continuous_on (f ∘ g) R, from hc.comp hdg.continuous.continuous_on h_maps,
  replace hd : ∀ z ∈ (Ioo (min a b) (max a b) ×ℂ Ioo (min 0 (2 * π)) (max 0 (2 * π))) \ g ⁻¹' s,
    differentiable_at ℂ (f ∘ g) z,
  { refine λ z hz, (hd (g z) ⟨_, hz.2⟩).comp z (hdg _),
    simpa [g, dist_eq, abs_exp, hle, and.comm] using hz.1.1 },
  simpa [g, circle_map, exp_periodic _, sub_eq_zero, ← exp_add]
    using integral_boundary_rect_eq_zero_of_differentiable_on_off_countable _ ⟨a, 0⟩ ⟨b, 2 * π⟩
      _ hs hc hd
end

/-- **Cauchy-Goursat theorem** for an annulus. If `f : ℂ → E` is continuous on the closed annulus
`r ≤ ∥z - c∥ ≤ R`, `0 < r ≤ R`, and is complex differentiable at all but countably many points of
its interior, then the integrals of `f` over the circles `∥z - c∥ = r` and `∥z - c∥ = R` are equal
to each other. -/
lemma circle_integral_eq_of_differentiable_on_annulus_off_countable
  {c : ℂ} {r R : ℝ} (h0 : 0 < r) (hle : r ≤ R) {f : ℂ → E} {s : set ℂ} (hs : countable s)
  (hc : continuous_on f (closed_ball c R \ ball c r))
  (hd : ∀ z ∈ ball c R \ closed_ball c r \ s, differentiable_at ℂ f z) :
  ∮ z in C(c, R), f z = ∮ z in C(c, r), f z :=
calc ∮ z in C(c, R), f z = ∮ z in C(c, R), (z - c)⁻¹ • (z - c) • f z :
  (circle_integral.integral_sub_inv_smul_sub_smul _ _ _ _).symm
... = ∮ z in C(c, r), (z - c)⁻¹ • (z - c) • f z :
  circle_integral_sub_center_inv_smul_eq_of_differentiable_on_annulus_off_countable h0 hle hs
    ((continuous_on_id.sub continuous_on_const).smul hc)
    (λ z hz, (differentiable_at_id.sub_const _).smul (hd z hz))
... = ∮ z in C(c, r), f z : circle_integral.integral_sub_inv_smul_sub_smul _ _ _ _

/-- **Cauchy integral formula** for the value at the center of a disc. If `f` is continuous on a
punctured closed disc of radius `R`, is differentiable at all but countably many points of the
interior of this disc, and has a limit `y` at the center of the disc, then the integral
$\oint_{∥z-c∥=R} \frac{f(z)}{z-c}\,dz$ is equal to $2πiy`. -/
lemma circle_integral_sub_center_inv_smul_of_differentiable_on_off_countable_of_tendsto
  {c : ℂ} {R : ℝ} (h0 : 0 < R) {f : ℂ → E} {y : E} {s : set ℂ} (hs : countable s)
  (hc : continuous_on f (closed_ball c R \ {c}))
  (hd : ∀ z ∈ ball c R \ {c} \ s, differentiable_at ℂ f z) (hy : tendsto f (𝓝[{c}ᶜ] c) (𝓝 y)) :
  ∮ z in C(c, R), (z - c)⁻¹ • f z = (2 * π * I : ℂ) • y :=
begin
  rw [← sub_eq_zero, ← norm_le_zero_iff],
  refine le_of_forall_le_of_dense (λ ε ε0, _),
  obtain ⟨δ, δ0, hδ⟩ :
    ∃ δ > (0 : ℝ), ∀ z ∈ closed_ball c δ \ {c}, dist (f z) y < ε / (2 * π),
    from ((nhds_within_has_basis nhds_basis_closed_ball _).tendsto_iff nhds_basis_ball).1 hy _
      (div_pos ε0 real.two_pi_pos),
  obtain ⟨r, hr0, hrδ, hrR⟩ : ∃ r, 0 < r ∧ r ≤ δ ∧ r ≤ R :=
    ⟨min δ R, lt_min δ0 h0, min_le_left _ _, min_le_right _ _⟩,
  have hsub : closed_ball c R \ ball c r ⊆ closed_ball c R \ {c},
    from diff_subset_diff_right (singleton_subset_iff.2 $ mem_ball_self hr0),
  have hsub' : ball c R \ closed_ball c r ⊆ ball c R \ {c},
    from diff_subset_diff_right (singleton_subset_iff.2 $ mem_closed_ball_self hr0.le),
  have hzne : ∀ z ∈ sphere c r, z ≠ c,
    from λ z hz, ne_of_mem_of_not_mem hz (λ h, hr0.ne' $ dist_self c ▸ eq.symm h),
  /- The integral `∮ z in C(c, r), f z / (z - c)` does not depend on `0 < r ≤ R` and tends to
  `2πIy` as `r → 0`. -/
  calc ∥(∮ z in C(c, R), (z - c)⁻¹ • f z) - (2 * ↑π * I) • y∥
      = ∥(∮ z in C(c, r), (z - c)⁻¹ • f z) - ∮ z in C(c, r), (z - c)⁻¹ • y∥ :
    begin
      congr' 2,
      { exact circle_integral_sub_center_inv_smul_eq_of_differentiable_on_annulus_off_countable
          hr0 hrR hs (hc.mono hsub) (λ z hz, hd z ⟨hsub' hz.1, hz.2⟩) },
      { simp [hr0.ne'] }
    end
  ... = ∥∮ z in C(c, r), (z - c)⁻¹ • (f z - y)∥ :
    begin
      simp only [smul_sub],
      have hc' : continuous_on (λ z, (z - c)⁻¹) (sphere c r),
        from (continuous_on_id.sub continuous_on_const).inv₀ (λ z hz, sub_ne_zero.2 $ hzne _ hz),
      rw circle_integral.integral_sub; refine (hc'.smul _).circle_integrable hr0.le,
      { exact hc.mono (subset_inter (sphere_subset_closed_ball.trans $
          closed_ball_subset_closed_ball hrR) hzne) },
      { exact continuous_on_const }
    end
  ... ≤ 2 * π * r * (r⁻¹ * (ε / (2 * π))) :
    begin
      refine circle_integral.norm_integral_le_of_norm_le_const hr0.le (λ z hz, _),
      specialize hzne z hz,
      rw [mem_sphere, dist_eq_norm] at hz,
      rw [norm_smul, norm_inv, hz, ← dist_eq_norm],
      refine mul_le_mul_of_nonneg_left (hδ _ ⟨_, hzne⟩).le (inv_nonneg.2 hr0.le),
      rwa [mem_closed_ball_iff_norm, hz]
    end
  ... = ε : by { field_simp [hr0.ne', real.two_pi_pos.ne'], ac_refl }
end

/-- **Cauchy integral formula** for the value at the center of a disc. If `f : ℂ → E` is continuous
on a closed disc of radius `R` and is complex differentiable at all but countably many points of its
interior, then the integral $\oint_{|z-c|=R} \frac{f(z)}{z-c}\,dz$ is equal to $2πiy`. -/
lemma circle_integral_sub_center_inv_smul_of_differentiable_on_off_countable {R : ℝ} (h0 : 0 < R)
  {f : ℂ → E} {c : ℂ} {s : set ℂ} (hs : countable s)
  (hc : continuous_on f (closed_ball c R)) (hd : ∀ z ∈ ball c R \ s, differentiable_at ℂ f z) :
  ∮ z in C(c, R), (z - c)⁻¹ • f z = (2 * π * I : ℂ) • f c :=
circle_integral_sub_center_inv_smul_of_differentiable_on_off_countable_of_tendsto h0 hs
  (hc.mono $ diff_subset _ _) (λ z hz, hd z ⟨hz.1.1, hz.2⟩)
  (hc.continuous_at $ closed_ball_mem_nhds _ h0).continuous_within_at

/-- **Cauchy-Goursat theorem** for a disk: if `f : ℂ → E` is continuous on a closed disk
`{z | ∥z - c∥ ≤ R}` and is complex differentiable at all but countably many points of its interior,
then the integral $\oint_{|z-c|=R}f(z)\,dz$ equals zero. -/
lemma circle_integral_eq_zero_of_differentiable_on_off_countable {R : ℝ} (h0 : 0 ≤ R) {f : ℂ → E}
  {c : ℂ} {s : set ℂ} (hs : countable s) (hc : continuous_on f (closed_ball c R))
  (hd : ∀ z ∈ ball c R \ s, differentiable_at ℂ f z) :
  ∮ z in C(c, R), f z = 0 :=
begin
  rcases h0.eq_or_lt with rfl|h0, { apply circle_integral.integral_radius_zero },
  calc ∮ z in C(c, R), f z = ∮ z in C(c, R), (z - c)⁻¹ • (z - c) • f z :
    (circle_integral.integral_sub_inv_smul_sub_smul _ _ _ _).symm
  ... = (2 * ↑π * I : ℂ) • (c - c) • f c :
    circle_integral_sub_center_inv_smul_of_differentiable_on_off_countable h0 hs
      ((continuous_on_id.sub continuous_on_const).smul hc)
      (λ z hz, (differentiable_at_id.sub_const _).smul (hd z hz))
  ... = 0 : by rw [sub_self, zero_smul, smul_zero]
end

/-- An auxiliary lemma for
`complex.circle_integral_sub_inv_smul_of_differentiable_on_off_countable`. This lemma assumes
`w ∉ s` while the main lemma drops this assumption. -/
lemma circle_integral_sub_inv_smul_of_differentiable_on_off_countable_aux {R : ℝ} {c w : ℂ}
  {f : ℂ → E} {s : set ℂ} (hs : countable s) (hw : w ∈ ball c R \ s)
  (hc : continuous_on f (closed_ball c R)) (hd : ∀ x ∈ ball c R \ s, differentiable_at ℂ f x) :
  ∮ z in C(c, R), (z - w)⁻¹ • f z = (2 * π * I : ℂ) • f w :=
begin
  have hR : 0 < R := dist_nonneg.trans_lt hw.1,
  set F : ℂ → E := dslope f w,
  have hws : countable (insert w s) := hs.insert _,
  have hnhds : closed_ball c R ∈ 𝓝 w, from closed_ball_mem_nhds_of_mem hw.1,
  have hcF : continuous_on F (closed_ball c R),
    from (continuous_on_dslope $ closed_ball_mem_nhds_of_mem hw.1).2 ⟨hc, hd _ hw⟩,
  have hdF : ∀ z ∈ ball (c : ℂ) R \ (insert w s), differentiable_at ℂ F z,
    from λ z hz, (differentiable_at_dslope_of_ne
      (ne_of_mem_of_not_mem (mem_insert _ _) hz.2).symm).2
      (hd _ (diff_subset_diff_right (subset_insert _ _) hz)),
  have HI := circle_integral_eq_zero_of_differentiable_on_off_countable hR.le hws hcF hdF,
  have hne : ∀ z ∈ sphere c R, z ≠ w, from λ z hz, ne_of_mem_of_not_mem hz (ne_of_lt hw.1),
  have hFeq : eq_on F (λ z, (z - w)⁻¹ • f z - (z - w)⁻¹ • f w) (sphere c R),
  { intros z hz,
    calc F z = (z - w)⁻¹ • (f z - f w) : update_noteq (hne z hz) _ _
    ... = (z - w)⁻¹ • f z - (z - w)⁻¹ • f w : smul_sub _ _ _ },
  have hc' : continuous_on (λ z, (z - w)⁻¹) (sphere c R),
    from (continuous_on_id.sub continuous_on_const).inv₀ (λ z hz, sub_ne_zero.2 $ hne z hz),
  rw [← circle_integral.integral_sub_inv_of_mem_ball hw.1, ← circle_integral.integral_smul_const,
    ← sub_eq_zero, ← circle_integral.integral_sub, ← circle_integral.integral_congr hR.le hFeq, HI],
  exacts [(hc'.smul (hc.mono sphere_subset_closed_ball)).circle_integrable hR.le,
    (hc'.smul continuous_on_const).circle_integrable hR.le]
end

/-- **Cauchy integral formula**: if `f : ℂ → E` is continuous on a closed disc of radius `R` and is
complex differentiable at all but countably many points of its interior, then for any `w` in this
interior we have $\frac{1}{2πi}\oint_{|z-c|=R}(z-w)^{-1}f(z)\,dz=f(w)$.
-/
lemma two_pi_I_inv_smul_circle_integral_sub_inv_smul_of_differentiable_on_off_countable
  {R : ℝ} {c w : ℂ} {f : ℂ → E} {s : set ℂ} (hs : countable s) (hw : w ∈ ball c R)
  (hc : continuous_on f (closed_ball c R)) (hd : ∀ x ∈ ball c R \ s, differentiable_at ℂ f x) :
  (2 * π * I : ℂ)⁻¹ • ∮ z in C(c, R), (z - w)⁻¹ • f z = f w :=
begin
  have hR : 0 < R := dist_nonneg.trans_lt hw,
  suffices : w ∈ closure (ball c R \ s),
  { lift R to ℝ≥0 using hR.le,
    have A : continuous_at (λ w, (2 * π * I : ℂ)⁻¹ • ∮ z in C(c, R), (z - w)⁻¹ • f z) w,
    { have := has_fpower_series_on_cauchy_integral
      ((hc.mono sphere_subset_closed_ball).circle_integrable R.coe_nonneg) hR,
      refine this.continuous_on.continuous_at (emetric.is_open_ball.mem_nhds _),
      rwa metric.emetric_ball_nnreal },
    have B : continuous_at f w, from hc.continuous_at (closed_ball_mem_nhds_of_mem hw),
    refine tendsto_nhds_unique_of_frequently_eq A B ((mem_closure_iff_frequently.1 this).mono _),
    intros z hz,
    rw [circle_integral_sub_inv_smul_of_differentiable_on_off_countable_aux hs hz hc hd,
      inv_smul_smul₀],
    simp [real.pi_ne_zero, I_ne_zero] },
  refine mem_closure_iff_nhds.2 (λ t ht, _),
  -- TODO: generalize to any vector space over `ℝ`
  set g : ℝ → ℂ := λ x, w + x,
  have : tendsto g (𝓝 0) (𝓝 w),
    from (continuous_const.add continuous_of_real).tendsto' 0 w (add_zero _),
  rcases mem_nhds_iff_exists_Ioo_subset.1 (this $ inter_mem ht $ is_open_ball.mem_nhds hw)
    with ⟨l, u, hlu₀, hlu_sub⟩,
  obtain ⟨x, hx⟩ : (Ioo l u \ g ⁻¹' s).nonempty,
  { refine nonempty_diff.2 (λ hsub, _),
    have : countable (Ioo l u),
      from (hs.preimage ((add_right_injective w).comp of_real_injective)).mono hsub,
    rw [← cardinal.mk_set_le_omega, cardinal.mk_Ioo_real (hlu₀.1.trans hlu₀.2)] at this,
    exact this.not_lt cardinal.omega_lt_continuum },
  exact ⟨g x, (hlu_sub hx.1).1, (hlu_sub hx.1).2, hx.2⟩
end

/-- **Cauchy integral formula**: if `f : ℂ → E` is continuous on a closed disc of radius `R` and is
complex differentiable at all but countably many points of its interior, then for any `w` in this
interior we have $\oint_{|z-c|=R}(z-w)^{-1}f(z)\,dz=2πif(w)$.
-/
lemma circle_integral_sub_inv_smul_of_differentiable_on_off_countable
  {R : ℝ} {c w : ℂ} {f : ℂ → E} {s : set ℂ} (hs : countable s) (hw : w ∈ ball c R)
  (hc : continuous_on f (closed_ball c R)) (hd : ∀ x ∈ ball c R \ s, differentiable_at ℂ f x) :
  ∮ z in C(c, R), (z - w)⁻¹ • f z = (2 * π * I : ℂ) • f w :=
by { rw [← two_pi_I_inv_smul_circle_integral_sub_inv_smul_of_differentiable_on_off_countable
  hs hw hc hd, smul_inv_smul₀], simp [real.pi_ne_zero, I_ne_zero] }

/-- **Cauchy integral formula**: if `f : ℂ → E` is continuous on a closed disc of radius `R` and is
complex differentiable on its interior, then for any `w` in this interior we have
$\oint_{|z-c|=R}(z-w)^{-1}f(z)\,dz=2πif(w)$.
-/
lemma _root_.diff_on_int_cont.circle_integral_sub_inv_smul {R : ℝ} {c w : ℂ} {f : ℂ → E}
  (h : diff_on_int_cont ℂ f (closed_ball c R)) (hw : w ∈ ball c R) :
  ∮ z in C(c, R), (z - w)⁻¹ • f z = (2 * π * I : ℂ) • f w :=
circle_integral_sub_inv_smul_of_differentiable_on_off_countable countable_empty hw
  h.continuous_on $ λ z hz, h.differentiable_at $ ball_subset_interior_closed_ball hz.1

/-- **Cauchy integral formula**: if `f : ℂ → E` is complex differentiable on a closed disc of radius
`R`, then for any `w` in its interior we have $\oint_{|z-c|=R}(z-w)^{-1}f(z)\,dz=2πif(w)$. -/
lemma _root_.differentiable_on.circle_integral_sub_inv_smul {R : ℝ} {c w : ℂ} {f : ℂ → E}
  (hd : differentiable_on ℂ f (closed_ball c R)) (hw : w ∈ ball c R)  :
  ∮ z in C(c, R), (z - w)⁻¹ • f z = (2 * π * I : ℂ) • f w :=
hd.diff_on_int_cont.circle_integral_sub_inv_smul hw

/-- **Cauchy integral formula**: if `f : ℂ → ℂ` is continuous on a closed disc of radius `R` and is
complex differentiable at all but countably many points of its interior, then for any `w` in this
interior we have $\oint_{|z-c|=R}\frac{f(z)}{z-w}dz=2\pi i\,f(w)$.
-/
lemma circle_integral_div_sub_of_differentiable_on_off_countable {R : ℝ} {c w : ℂ} {s : set ℂ}
  (hs : countable s) (hw : w ∈ ball c R) {f : ℂ → ℂ} (hc : continuous_on f (closed_ball c R))
  (hd : ∀ z ∈ ball c R \ s, differentiable_at ℂ f z) :
  ∮ z in C(c, R), f z / (z - w) = 2 * π * I * f w :=
by simpa only [smul_eq_mul, div_eq_inv_mul]
  using circle_integral_sub_inv_smul_of_differentiable_on_off_countable hs hw hc hd

/-- If `f : ℂ → E` is continuous on a closed ball of positive radius and is differentiable at all
but countably many points of the corresponding open ball, then it is analytic on the open ball with
coefficients of the power series given by Cauchy integral formulas. -/
lemma has_fpower_series_on_ball_of_differentiable_off_countable {R : ℝ≥0} {c : ℂ} {f : ℂ → E}
  {s : set ℂ} (hs : countable s) (hc : continuous_on f (closed_ball c R))
  (hd : ∀ z ∈ ball c R \ s, differentiable_at ℂ f z) (hR : 0 < R) :
  has_fpower_series_on_ball f (cauchy_power_series f c R) c R :=
{ r_le := le_radius_cauchy_power_series _ _ _,
  r_pos := ennreal.coe_pos.2 hR,
  has_sum := λ w hw,
    begin
      have hw' : c + w ∈ ball c R,
        by simpa only [add_mem_ball_iff_norm, ← coe_nnnorm, mem_emetric_ball_zero_iff,
          nnreal.coe_lt_coe, ennreal.coe_lt_coe] using hw,
      rw ← two_pi_I_inv_smul_circle_integral_sub_inv_smul_of_differentiable_on_off_countable hs
        hw' hc hd,
      exact (has_fpower_series_on_cauchy_integral
        ((hc.mono sphere_subset_closed_ball).circle_integrable R.2) hR).has_sum hw
    end }

/-- If `f : ℂ → E` is continuous on a closed ball of positive radius and is complex differentiable
on its interior, then it is analytic on the open ball with coefficients of the power series given by
Cauchy integral formulas. -/
lemma _root_.diff_on_int_cont.has_fpower_series_on_ball {R : ℝ≥0} {c : ℂ} {f : ℂ → E}
  (hf : diff_on_int_cont ℂ f (closed_ball c R)) (hR : 0 < R) :
  has_fpower_series_on_ball f (cauchy_power_series f c R) c R :=
has_fpower_series_on_ball_of_differentiable_off_countable countable_empty hf.continuous_on
  (λ z hz, hf.differentiable_at $ ball_subset_interior_closed_ball hz.1) hR

/-- If `f : ℂ → E` is complex differentiable on a closed disc of positive radius, then it is
analytic on the corresponding open disc, and the coefficients of the power series are given by
Cauchy integral formulas. See also
`complex.has_fpower_series_on_ball_of_differentiable_off_countable` for a version of this lemma with
weaker assumptions. -/
protected lemma _root_.differentiable_on.has_fpower_series_on_ball {R : ℝ≥0} {c : ℂ} {f : ℂ → E}
  (hd : differentiable_on ℂ f (closed_ball c R)) (hR : 0 < R) :
  has_fpower_series_on_ball f (cauchy_power_series f c R) c R :=
hd.diff_on_int_cont.has_fpower_series_on_ball hR

/-- If `f : ℂ → E` is complex differentiable on some set `s`, then it is analytic at any point `z`
such that `s ∈ 𝓝 z` (equivalently, `z ∈ interior s`). -/
protected lemma _root_.differentiable_on.analytic_at {s : set ℂ} {f : ℂ → E} {z : ℂ}
  (hd : differentiable_on ℂ f s) (hz : s ∈ 𝓝 z) : analytic_at ℂ f z :=
begin
  rcases nhds_basis_closed_ball.mem_iff.1 hz with ⟨R, hR0, hRs⟩,
  lift R to ℝ≥0 using hR0.le,
  exact ((hd.mono hRs).has_fpower_series_on_ball hR0).analytic_at
end

/-- A complex differentiable function `f : ℂ → E` is analytic at every point. -/
protected lemma _root_.differentiable.analytic_at {f : ℂ → E} (hf : differentiable ℂ f) (z : ℂ) :
  analytic_at ℂ f z :=
hf.differentiable_on.analytic_at univ_mem

/-- When `f : ℂ → E` is differentiable, the `cauchy_power_series f z R` represents `f` as a power
series centered at `z` in the entirety of `ℂ`, regardless of `R : ℝ≥0`, with  `0 < R`. -/
protected lemma _root_.differentiable.has_fpower_series_on_ball {f : ℂ → E}
  (h : differentiable ℂ f) (z : ℂ) {R : ℝ≥0} (hR : 0 < R) :
  has_fpower_series_on_ball f (cauchy_power_series f z R) z ∞ :=
(h.differentiable_on.has_fpower_series_on_ball hR).r_eq_top_of_exists $ λ r hr,
  ⟨_, h.differentiable_on.has_fpower_series_on_ball hr⟩

end complex


/-
Copyright (c) 2022 Kalle Kytölä. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kalle Kytölä
-/
import measure_theory.integral.interval_integral
import analysis.special_functions.integrals

/-!
# The layer cake formula / Cavalieri's principle / tail probability formula

In this file we prove the following layer cake formula.

Consider a non-negative measurable function `f` on a sigma-finite measure space. Apply pointwise
to it an increasing absolutely continuous function `G : ℝ≥0 → ℝ≥0` vanishing at the origin, with
derivative `G' = g` on the positive real line (in other words, `G` a primitive of a non-negative
locally integrable function `g` on the positive real line). Then the integral of the result,
`∫ G ∘ f`, can be written as the integral over the positive real line of the "tail measures" of `f`
(i.e., a function giving the measures of the sets on which `f` exceeds different positive real
values) weighted by `g`. In probability theory contexts, the "tail measures" could be referred to
as "tail probabilities" of the random variable `f`, or as values of the "complementary cumulative
distribution function" of the random variable `f`. The terminology "tail probability formula" is
therefore occasionally used for the layer cake formula (or a standard application of it).

The essence of the (mathematical) proof is Fubini's theorem.

We also give the two most common applications of the layer cake formula
 * a representation of the integral of a nonnegative function f:
   ∫ f(ω) ∂μ(ω) = ∫ μ {ω | f(ω) ≥ t} dt
 * a representation of the integral of the p:th power of a nonnegative function f:
   ∫ f(ω)^p ∂μ(ω) = p * ∫ t^(p-1) * μ {ω | f(ω) ≥ t} dt .

Variants of the formulas with measures of sets of the form {ω | f(ω) > t} instead of {ω | f(ω) ≥ t}
are also included.

## Main results

 * `lintegral_comp_eq_lintegral_meas_le_mul` and `lintegral_comp_eq_lintegral_meas_lt_mul`:
   The general layer cake formulas with Lebesgue integrals, written in terms of measures of
   sets of the forms {ω | t ≤ f(ω)} and {ω | t < f(ω)}, respectively.
 * `lintegral_eq_lintegral_meas_le` and `lintegral_eq_lintegral_meas_lt`:
   The most common special cases of the layer cake formulas, stating that for a nonnegative
   function f we have ∫ f(ω) ∂μ(ω) = ∫ μ {ω | f(ω) ≥ t} dt and
   ∫ f(ω) ∂μ(ω) = ∫ μ {ω | f(ω) > t} dt, respectively.
 * `lintegral_rpow_eq_lintegral_meas_le_mul` and `lintegral_rpow_eq_lintegral_meas_lt_mul`:
   Other common special cases of the layer cake formulas, stating that for a nonnegative function f
   and p > 0, we have ∫ f(ω)^p ∂μ(ω) = p * ∫ μ {ω | f(ω) ≥ t} * t^(p-1) dt and
   ∫ f(ω)^p ∂μ(ω) = p * ∫ μ {ω | f(ω) > t} * t^(p-1) dt, respectively.

## Tags

layer cake representation, Cavalieri's principle, tail probability formula
-/

noncomputable theory
open_locale ennreal measure_theory
open set measure_theory filter

/-! ### Layercake formula -/
section layercake

namespace measure_theory

variables {α : Type*} [measurable_space α] {f : α → ℝ} {g : ℝ → ℝ} {s : set α}

/-- An auxiliary version of the layer cake formula (Cavalieri's principle, tail probability
formula), with a measurability assumption that would also essentially follow from the
integrability assumptions.

See `measure_theory.lintegral_comp_eq_lintegral_meas_le_mul` and
`measure_theory.lintegral_comp_eq_lintegral_meas_lt_mul` for the main formulations of the layer
cake formula. -/
lemma lintegral_comp_eq_lintegral_meas_le_mul_of_measurable (μ : measure α) [sigma_finite μ]
  (f_nn : 0 ≤ f) (f_mble : measurable f)
  (g_intble : ∀ t > 0, interval_integrable g volume 0 t)
  (g_mble : measurable g) (g_nn : ∀ t > 0, 0 ≤ g t) :
  ∫⁻ ω, ennreal.of_real (∫ t in 0 .. (f ω), g t) ∂μ
    = ∫⁻ t in Ioi 0, (μ {a : α | t ≤ f a}) * ennreal.of_real (g t) :=
begin
  have g_intble' : ∀ (t : ℝ), 0 ≤ t → interval_integrable g volume 0 t,
  { intros t ht,
    cases eq_or_lt_of_le ht,
    { simp [← h], },
    { exact g_intble t h, }, },
  have integrand_eq : ∀ ω, ennreal.of_real (∫ t in 0 .. (f ω), g t)
                           = ∫⁻ t in Ioc 0 (f ω), ennreal.of_real (g t),
  { intro ω,
    have g_ae_nn : 0 ≤ᵐ[volume.restrict (Ioc 0 (f ω))] g,
    { filter_upwards [self_mem_ae_restrict (measurable_set_Ioc : measurable_set (Ioc 0 (f ω)))]
        with x hx using g_nn x hx.1, },
    rw ← of_real_integral_eq_lintegral_of_real (g_intble' (f ω) (f_nn ω)).1 g_ae_nn,
    congr,
    exact interval_integral.integral_of_le (f_nn ω), },
  simp_rw [integrand_eq, ← lintegral_indicator (λ t, ennreal.of_real (g t)) measurable_set_Ioc,
           ← lintegral_indicator _ measurable_set_Ioi],
  rw lintegral_lintegral_swap,
  { apply congr_arg,
    funext s,
    have aux₁ : (λ x, (Ioc 0 (f x)).indicator (λ (t : ℝ), ennreal.of_real (g t)) s)
                = (λ x, (ennreal.of_real (g s) * (Ioi (0 : ℝ)).indicator (λ _, 1) s)
                             * (Ici s).indicator (λ (t : ℝ), (1 : ℝ≥0∞)) (f x)),
    { funext a,
      by_cases s ∈ Ioc (0 : ℝ) (f a),
      { simp only [h, (show s ∈ Ioi (0 : ℝ), from h.1),
                   (show f a ∈ Ici s, from h.2), indicator_of_mem, mul_one], },
      { have h_copy := h,
        simp only [mem_Ioc, not_and, not_le] at h,
        by_cases h' : 0 < s,
        { simp only [h_copy, h h', indicator_of_not_mem, not_false_iff, mem_Ici, not_le,
                     mul_zero], },
        { have : s ∉ Ioi (0 : ℝ) := h',
          simp only [this, h', indicator_of_not_mem, not_false_iff, mul_zero, zero_mul, mem_Ioc,
                     false_and], }, }, },
    simp_rw aux₁,
    rw lintegral_const_mul',
    swap, { apply ennreal.mul_ne_top ennreal.of_real_ne_top,
            by_cases s ∈ Ioi (0 : ℝ); { simp [h], }, },
    simp_rw [(show (λ a, (Ici s).indicator (λ (t : ℝ), (1 : ℝ≥0∞)) (f a))
                   = (λ a, {a : α | s ≤ f a}.indicator (λ _, 1) a),
              by { funext a, by_cases s ≤ f a; simp [h], })],
    rw lintegral_indicator,
    swap, { exact f_mble measurable_set_Ici, },
    rw [lintegral_one, measure.restrict_apply measurable_set.univ, univ_inter, indicator_mul_left,
        mul_assoc,
        (show (Ioi 0).indicator (λ (_x : ℝ), (1 : ℝ≥0∞)) s * μ {a : α | s ≤ f a}
              = (Ioi 0).indicator (λ (_x : ℝ), 1 * μ {a : α | s ≤ f a}) s,
        by { by_cases 0 < s; simp [h], })],
    simp_rw [mul_comm _ (ennreal.of_real _), one_mul],
    refl, },
  have aux₂ : function.uncurry
              (λ (x : α) (y : ℝ), (Ioc 0 (f x)).indicator (λ (t : ℝ), ennreal.of_real (g t)) y)
              = {p : α × ℝ | p.2 ∈ Ioc 0 (f p.1)}.indicator (λ p, ennreal.of_real (g p.2)),
  { funext p,
    cases p,
    rw function.uncurry_apply_pair,
    by_cases p_snd ∈ Ioc 0 (f p_fst),
    { have h' : (p_fst, p_snd) ∈ {p : α × ℝ | p.snd ∈ Ioc 0 (f p.fst)} := h,
      rw [set.indicator_of_mem h', set.indicator_of_mem h], },
    { have h' : (p_fst, p_snd) ∉ {p : α × ℝ | p.snd ∈ Ioc 0 (f p.fst)} := h,
      rw [set.indicator_of_not_mem h', set.indicator_of_not_mem h], }, },
  rw aux₂,
  have mble := measurable_set_region_between_oc measurable_zero f_mble measurable_set.univ,
  simp_rw [mem_univ, pi.zero_apply, true_and] at mble,
  exact (ennreal.measurable_of_real.comp (g_mble.comp measurable_snd)).ae_measurable.indicator mble,
end

/-- The layer cake formula / Cavalieri's principle / tail probability formula:

Let `f` be a non-negative measurable function on a sigma-finite measure space. Let `G` be an
increasing absolutely continuous function on the positive real line, vanishing at the origin,
with derivative `G' = g`. Then the integral of the composition `G ∘ f` can be written as
the integral over the positive real line of the "tail measures" `μ {ω | f(ω) ≥ t}` of `f`
weighted by `g`.

Roughly speaking, the statement is: `∫⁻ (G ∘ f) ∂μ = ∫⁻ t in 0 .. ∞, g(t) * μ {ω | f(ω) ≥ t}`.

See `lintegral_comp_eq_lintegral_meas_lt_mul` for a version with sets of the form `{ω | f(ω) > t}`
instead. -/
theorem lintegral_comp_eq_lintegral_meas_le_mul (μ : measure α) [sigma_finite μ]
  (f_nn : 0 ≤ f) (f_mble : measurable f)
  (g_intble : ∀ t > 0, interval_integrable g volume 0 t)
  (g_nn : ∀ᵐ t ∂(volume.restrict (Ioi 0)), 0 ≤ g t) :
  ∫⁻ ω, ennreal.of_real (∫ t in 0 .. f ω, g t) ∂μ
    = ∫⁻ t in Ioi 0, μ {a : α | t ≤ f a} * ennreal.of_real (g t) :=
begin
  have ex_G : ∃ (G : ℝ → ℝ), measurable G ∧ 0 ≤ G ∧ g =ᵐ[volume.restrict (Ioi 0)] G,
  { refine ae_measurable.exists_measurable_nonneg _ g_nn,
    exact ae_measurable_Ioi_of_forall_Ioc (λ t ht, (g_intble t ht).1.1.ae_measurable), },
  rcases ex_G with ⟨G, G_mble, G_nn, g_eq_G⟩,
  have g_eq_G_on : ∀ t, g =ᵐ[volume.restrict (Ioc 0 t)] G,
    from λ t, ae_mono (measure.restrict_mono Ioc_subset_Ioi_self le_rfl) g_eq_G,
  have G_intble : ∀ t > 0, interval_integrable G volume 0 t,
  { refine λ t t_pos, ⟨integrable_on.congr_fun' (g_intble t t_pos).1 (g_eq_G_on t), _⟩,
    rw Ioc_eq_empty_of_le t_pos.lt.le,
    exact integrable_on_empty, },
  have eq₁ : ∫⁻ t in Ioi 0, μ {a : α | t ≤ f a} * ennreal.of_real (g t)
             = ∫⁻ t in Ioi 0, μ {a : α | t ≤ f a} * ennreal.of_real (G t),
  { apply lintegral_congr_ae,
    filter_upwards [g_eq_G] with a ha,
    rw [ha], },
  have eq₂ : ∀ ω, ∫ t in 0..f ω, g t = ∫ t in 0..f ω, G t,
  { refine λ ω, interval_integral.integral_congr_ae _,
    have fω_nn : 0 ≤ f ω := f_nn ω,
    rw [uIoc_of_le fω_nn,
        ← ae_restrict_iff' (measurable_set_Ioc : measurable_set (Ioc (0 : ℝ) (f ω)))],
    exact g_eq_G_on (f ω), },
  simp_rw [eq₁, eq₂],
  exact lintegral_comp_eq_lintegral_meas_le_mul_of_measurable μ f_nn f_mble
    G_intble G_mble (λ t t_pos, G_nn t),
end

/-- The standard case of the layer cake formula / Cavalieri's principle / tail probability formula:

For a nonnegative function `f` on a sigma-finite measure space, the Lebesgue integral of `f` can
be written (roughly speaking) as: `∫⁻ f ∂μ = ∫⁻ t in 0 .. ∞, μ {ω | f(ω) ≥ t}`.

See `lintegral_eq_lintegral_meas_lt` for a version with sets of the form `{ω | f(ω) > t}`
instead. -/
theorem lintegral_eq_lintegral_meas_le (μ : measure α) [sigma_finite μ]
  (f_nn : 0 ≤ f) (f_mble : measurable f) :
  ∫⁻ ω, ennreal.of_real (f ω) ∂μ = ∫⁻ t in Ioi 0, (μ {a : α | t ≤ f a}) :=
begin
  set cst := λ (t : ℝ), (1 : ℝ) with def_cst,
  have cst_intble : ∀ t > 0, interval_integrable cst volume 0 t,
    from λ _ _, interval_integrable_const,
  have key := lintegral_comp_eq_lintegral_meas_le_mul μ f_nn f_mble cst_intble
              (eventually_of_forall (λ t, zero_le_one)),
  simp_rw [def_cst, ennreal.of_real_one, mul_one] at key,
  rw ← key,
  congr' with ω,
  simp only [interval_integral.integral_const, sub_zero, algebra.id.smul_eq_mul, mul_one],
end

/-- An application of the layer cake formula / Cavalieri's principle / tail probability formula:

For a nonnegative function `f` on a sigma-finite measure space, the Lebesgue integral of `f` can
be written (roughly speaking) as: `∫⁻ f^p ∂μ = p * ∫⁻ t in 0 .. ∞, t^(p-1) * μ {ω | f(ω) ≥ t}`.

See `lintegral_rpow_eq_lintegral_meas_lt_mul` for a version with sets of the form `{ω | f(ω) > t}`
instead. -/
theorem lintegral_rpow_eq_lintegral_meas_le_mul (μ : measure α) [sigma_finite μ]
  (f_nn : 0 ≤ f) (f_mble : measurable f) {p : ℝ} (p_pos: 0 < p) :
  ∫⁻ ω, ennreal.of_real ((f ω)^p) ∂μ
    = (ennreal.of_real p) * ∫⁻ t in Ioi 0, (μ {a : α | t ≤ f a}) * ennreal.of_real (t^(p-1)) :=
begin
  have one_lt_p : -1 < p - 1 := by linarith,
  have obs : ∀ (x : ℝ), (∫ (t : ℝ) in 0..x, t^(p-1)) = x^p / p,
  { intros x,
    rw integral_rpow (or.inl one_lt_p),
    simp [real.zero_rpow p_pos.ne.symm], },
  set g := λ (t : ℝ), t^(p-1) with g_def,
  have g_nn : ∀ᵐ t ∂(volume.restrict (Ioi (0 : ℝ))), 0 ≤ g t,
  { filter_upwards [self_mem_ae_restrict (measurable_set_Ioi : measurable_set (Ioi (0 : ℝ)))],
    intros t t_pos,
    rw g_def,
    exact real.rpow_nonneg_of_nonneg (mem_Ioi.mp t_pos).le (p - 1), },
  have g_intble : ∀ t > 0, interval_integrable g volume 0 t,
    from λ _ _, interval_integral.interval_integrable_rpow' one_lt_p,
  have key := lintegral_comp_eq_lintegral_meas_le_mul μ f_nn f_mble g_intble g_nn,
  simp_rw [g_def] at key,
  rw [← key, ← lintegral_const_mul (ennreal.of_real p)]; simp_rw obs,
  { congr' with ω,
    rw [← ennreal.of_real_mul p_pos.le, mul_div_cancel' ((f ω)^p) p_pos.ne.symm], },
  { exact ((f_mble.pow measurable_const).div_const p).ennreal_of_real, },
end

end measure_theory

end layercake

section layercake_lt

open measure_theory

variables {α : Type*} [measurable_space α] (μ : measure α)
variables {β : Type*} [measurable_space β] [measurable_singleton_class β]

namespace measure

lemma meas_le_ne_meas_lt_subset_meas_pos {R : Type*} [linear_order R]
  [measurable_space R] [measurable_singleton_class R] {g : α → R} (g_mble : measurable g) {t : R}
  (ht : μ {a : α | t ≤ g a} ≠ μ {a : α | t < g a}) :
  0 < μ {a : α | g a = t} :=
begin
  have uni : {a : α | t ≤ g a } = {a : α | t < g a} ∪ {a : α | t = g a},
  { ext a,
    simp only [mem_set_of_eq, mem_union],
    apply le_iff_lt_or_eq, },
  rw (show {a : α | t = g a} = {a : α | g a = t}, by simp_rw [eq_comm]) at uni,
  have disj : {a : α | t < g a} ∩ {a : α | g a = t} = ∅,
  { ext a,
    simp only [mem_inter_iff, mem_set_of_eq, mem_empty_iff_false, iff_false, not_and],
    exact ne_of_gt, },
  have μ_add : μ {a : α | t ≤ g a} = μ {a : α | t < g a} + μ {a : α | g a = t},
    by rw [uni, measure_union (disjoint_iff_inter_eq_empty.mpr disj)
                              (g_mble (finite.measurable_set (finite_singleton t)))],
  by_contra con,
  rw [not_lt, nonpos_iff_eq_zero] at con,
  rw [con, add_zero] at μ_add,
  exact ht μ_add,
end

lemma countable_meas_le_ne_meas_lt [sigma_finite μ] {R : Type*} [linear_order R]
  [measurable_space R] [measurable_singleton_class R] {g : α → R} (g_mble : measurable g) :
  {t : R | μ {a : α | t ≤ g a } ≠ μ {a : α | t < g a}}.countable :=
countable.mono (show _, from λ t ht, meas_le_ne_meas_lt_subset_meas_pos μ g_mble ht)
               (measure.countable_meas_level_set_pos g_mble)

lemma meas_le_ae_eq_meas_lt [sigma_finite μ] {R : Type*} [linear_order R] [measurable_space R]
  [measurable_singleton_class R] (ν : measure R) [has_no_atoms ν]
  {g : α → R} (g_mble : measurable g) :
  (λ t, μ {a : α | t ≤ g a}) =ᵐ[ν] (λ t, μ {a : α | t < g a}) :=
set.countable.measure_zero (measure.countable_meas_le_ne_meas_lt μ g_mble) _

end measure

variables {f : α → ℝ} {g : ℝ → ℝ} {s : set α}

/-- The layer cake formula / Cavalieri's principle / tail probability formula:

Let `f` be a non-negative measurable function on a sigma-finite measure space. Let `G` be an
increasing absolutely continuous function on the positive real line, vanishing at the origin,
with derivative `G' = g`. Then the integral of the composition `G ∘ f` can be written as
the integral over the positive real line of the "tail measures" `μ {ω | f(ω) > t}` of `f`
weighted by `g`.

Roughly speaking, the statement is: `∫⁻ (G ∘ f) ∂μ = ∫⁻ t in 0 .. ∞, g(t) * μ {ω | f(ω) > t}`.

See `lintegral_comp_eq_lintegral_meas_le_mul` for a version with sets of the form `{ω | f(ω) ≥ t}`
instead. -/
theorem lintegral_comp_eq_lintegral_meas_lt_mul (μ : measure α) [sigma_finite μ]
  (f_nn : 0 ≤ f) (f_mble : measurable f)
  (g_intble : ∀ t > 0, interval_integrable g volume 0 t)
  (g_nn : ∀ᵐ t ∂(volume.restrict (Ioi 0)), 0 ≤ g t) :
  ∫⁻ ω, ennreal.of_real (∫ t in 0 .. f ω, g t) ∂μ
    = ∫⁻ t in Ioi 0, μ {a : α | t < f a} * ennreal.of_real (g t) :=
begin
  rw lintegral_comp_eq_lintegral_meas_le_mul μ f_nn f_mble g_intble g_nn,
  apply lintegral_congr_ae,
  filter_upwards [measure.meas_le_ae_eq_meas_lt μ (volume.restrict (Ioi 0)) f_mble] with t ht,
  rw ht,
end

/-- The standard case of the layer cake formula / Cavalieri's principle / tail probability formula:

For a nonnegative function `f` on a sigma-finite measure space, the Lebesgue integral of `f` can
be written (roughly speaking) as: `∫⁻ f ∂μ = ∫⁻ t in 0 .. ∞, μ {ω | f(ω) > t}`.

See `lintegral_eq_lintegral_meas_le` for a version with sets of the form `{ω | f(ω) ≥ t}`
instead. -/
theorem lintegral_eq_lintegral_meas_lt (μ : measure α) [sigma_finite μ]
  (f_nn : 0 ≤ f) (f_mble : measurable f) :
  ∫⁻ ω, ennreal.of_real (f ω) ∂μ = ∫⁻ t in Ioi 0, (μ {a : α | t < f a}) :=
begin
  rw lintegral_eq_lintegral_meas_le μ f_nn f_mble,
  apply lintegral_congr_ae,
  filter_upwards [measure.meas_le_ae_eq_meas_lt μ (volume.restrict (Ioi 0)) f_mble] with t ht,
  rw ht,
end

/-- An application of the layer cake formula / Cavalieri's principle / tail probability formula:

For a nonnegative function `f` on a sigma-finite measure space, the Lebesgue integral of `f` can
be written (roughly speaking) as: `∫⁻ f^p ∂μ = p * ∫⁻ t in 0 .. ∞, t^(p-1) * μ {ω | f(ω) > t}`.

See `lintegral_rpow_eq_lintegral_meas_le_mul` for a version with sets of the form `{ω | f(ω) ≥ t}`
instead. -/
theorem lintegral_rpow_eq_lintegral_meas_lt_mul (μ : measure α) [sigma_finite μ]
  (f_nn : 0 ≤ f) (f_mble : measurable f) {p : ℝ} (p_pos: 0 < p) :
  ∫⁻ ω, ennreal.of_real ((f ω)^p) ∂μ
    = (ennreal.of_real p) * ∫⁻ t in Ioi 0, (μ {a : α | t < f a}) * ennreal.of_real (t^(p-1)) :=
begin
  rw lintegral_rpow_eq_lintegral_meas_le_mul μ f_nn f_mble p_pos,
  apply congr_arg (λ z, (ennreal.of_real p * z)),
  apply lintegral_congr_ae,
  filter_upwards [measure.meas_le_ae_eq_meas_lt μ (volume.restrict (Ioi 0)) f_mble] with t ht,
  rw ht,
end

end layercake_lt


/-
Copyright (c) 2022 Kevin Buzzard. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author : Kevin Buzzard
-/

import tactic -- imports all the Lean tactics
import solutions.section03groups.sheet1 -- imports our definition of `mygroup`

/-!

# Challenge sheet

This is an optional "puzzle" sheet, which won't teach you any
new Lean concepts but will give you some practice in rewriteology,
and will teach you pretty much all there is to know about the
question "which axioms of a group can I safely drop?" If you're
not into puzzles like this, just move on to sheet 3.

It turns out that two of the axioms in our definition of a group
are not needed; they can be deduced from the others. Let's define
a "weak group" class, where we only have three of the group axioms.
The question is: can you prove that a weak group is a group?
The last def, `to_mygroup`, does this, but you need to fill in the
sorrys first. Note that the simplifier is less use to you now; we've
trained it to solve problems about `mygroup`s but it doesn't
know anything about `myweakgroup`.

-/

-- removing `mul_one` and `mul_inv_self`
class myweakgroup (G : Type)
  extends has_one G, has_mul G, has_inv G : Type :=
(mul_assoc : ∀ a b c : G, (a * b) * c = a * (b * c))
(one_mul : ∀ a : G, 1 * a = a)
(inv_mul_self : ∀ a : G, a⁻¹ * a = 1)

namespace myweakgroup

variables {G : Type} [myweakgroup G] (a b c : G)

/-

One way of doing it: try proving

`mul_left_cancel : a * b = a * c → b = c`

and then

`mul_eq_of_eq_inv_mul : b = a⁻¹ * c → a * b = c`

first.

-/

lemma mul_left_cancel (h : a * b = a * c) : b = c :=
begin
calc
     b = 1 * b         : by rw one_mul
...    = a⁻¹ * a * b   : by rw inv_mul_self
...    = a⁻¹ * (a * b) : by rw mul_assoc
...    = a⁻¹ * (a * c) : by rw h
...    = a⁻¹ * a * c   : by rw mul_assoc
...    = 1 * c         : by rw inv_mul_self
...    = c             : by rw one_mul
end

lemma mul_eq_of_eq_inv_mul (h : b = a⁻¹ * c) : a * b = c :=
begin
  apply mul_left_cancel a⁻¹,
  rw [← mul_assoc, inv_mul_self, one_mul, h],
end

lemma mul_one (a : G) : a * 1 = a :=
begin
  apply mul_eq_of_eq_inv_mul,
  rw inv_mul_self,
end

lemma mul_inv_self (a : G) : a * a⁻¹ = 1 :=
begin
  apply mul_eq_of_eq_inv_mul,
  rw mul_one,
end

def to_mygroup (G : Type) [myweakgroup G] : mygroup G :=
{ mul_assoc := mul_assoc,
  one_mul := one_mul,
  mul_one := mul_one,
  inv_mul_self := inv_mul_self,
  mul_inv_self := mul_inv_self }

end myweakgroup

/-
If you want to take this further: prove that if we make
a new class `my_even_weaker_group` by replacing
`one_mul` by `mul_one` in the definition of `myweakgroup`
then it is no longer true that you can prove `mul_inv_self`;
there are structures which satisfy `mul_assoc`, `mul_one`
and `inv_mul_self` but which really are not groups.
Can you find an example? Try it on paper first.
-/

-- claim: not a group in general
class my_even_weaker_group (G : Type)
  extends has_one G, has_mul G, has_inv G : Type :=
(mul_assoc : ∀ a b c : G, (a * b) * c = a * (b * c))
(mul_one : ∀ a : G, a * 1 = a)
(inv_mul_self : ∀ a : G, a⁻¹ * a = 1)

instance : my_even_weaker_group bool :=
{ one := tt,
  mul := λ x y, x, -- x * y = x for all x and y
  inv := λ x, tt, -- define x⁻¹ := 1 for all x
  mul_assoc := dec_trivial,
  mul_one := dec_trivial,
  inv_mul_self := dec_trivial }

example : ¬ (∀ g : bool, 1 * g = g) :=
begin
  intro h,
  specialize h ff,
  cases h,
end


/-
If a function, f, takes a type, T, as its first
argument, and a value, t, of that very type, T, as
its second argument, then when you fully apply the
function (to all its arguments), you will have to
give two arguments: a value for T, in other words
a type; and a value, t, of that type.

But why should you have to write out "nat" when Lean
knows from 4 that nat is all it can be? Good news: if
you specify the argument as implicit, then it will be
inferred, and if Lean can't infer it it will tell you.
-/

-- identity function on natural numbers
def id_nat : ℕ → ℕ
| n := n

example : id_nat 5 = 5 := rfl

def id_string : string → string
| s := s

def id_bool : bool → bool
| b := b

-- def id_T (T : Type) (a : T)
def id_T' (T : Type) : T → T
| t := t

def id_T'' : ∀ (T : Type), T → T
| T t := t

#eval id_T' nat 3
#eval id_T' bool tt
#eval id_T' string "I love logic"

#eval id_T'' nat 3
#eval id_T'' bool tt
#eval id_T'' string "I love logic"

def id_T {T : Type} : T → T
| t := t

#eval id_T 3
#eval id_T tt
#eval id_T "This is so cool"

#eval @id_T nat 3

#check id_T
#check @id_T

def identity1 : ∀ (T : Type) (t : T), T :=
begin
assume T t,
exact t,
end

#eval identity1 nat 1
#eval identity1 string "Hi!"

  /-
 This pair of examples is really very cool, as
 it illustrates what in computer science we call
 parametric polymorphism. That means that you
 specify a type as a parameter and then values
 of the given types as additional arguments,
and what this gives you is a whole family of
functions, here one for each type you might pass
as the actual value of the first parameter.
And, here's the real key idea: the "code" is the
same no matter the value of the type parameter.
Parametric polymorphism.

Try it out. Use #eval

as nat, bool, string, 0 = 0, etc) you might
pass as the first argument, the "type "
  -/

/-=
  arguments that are to
be inferred from context rather than specified explicitly. When
you #check a type, it prints implicit arguments as numbered
"meta-variables." To make Lean print the type making all of
the arguments explicit, you can use @.
-/

def example1A { T : Type } (t : T) := t
def example1B { T : Type } (t : T) : T := t

/-
When we specify arguments of a function or predicate, we can
declare them as named arguments (just in in Java or Python)m
before the colon. We can then continue the argument list after
the colon using arrown notation.
function
-/


-- ----------------------------------------------------
-- Ejercicio 30. Demostrar o refutar
--    (¬(∀ x, P x)) ↔ (∃x, ¬P x)
-- ----------------------------------------------------

import tactic

variable (U : Type)
variable (P : U -> Prop)

open_locale classical

-- 1ª demostración
example :
  (¬(∀ x, P x)) ↔ (∃x, ¬P x) :=
begin
  split,
  { intro h1,
    by_contradiction h2,
    apply h1,
    intro a,
    by_contradiction h3,
    apply h2,
    use a, },
  { rintro ⟨a, h4⟩ h5,
    apply h4,
    exact h5 a, },
end

-- 2ª demostración
example :
  (¬(∀ x, P x)) ↔ (∃x, ¬P x) :=
begin
  split,
  { intro h1,
    push_neg at h1,
    exact h1, },
  { intro h2,
    push_neg,
    exact h2, },
end

-- 3ª demostración
example :
  (¬(∀ x, P x)) ↔ (∃x, ¬P x) :=
begin
  push_neg,
  trivial,
end

-- 4ª demostración
example :
  (¬(∀ x, P x)) ↔ (∃x, ¬P x) :=
-- by library_search
not_forall

github-repos/lean/jaalonso-Logica_con_Lean/Logica_con_Lean-beb6765c6ff3c05590a03f45722eda0c815a25cd/src/2_LPO/Ejercicios/(¬(∀ x, P x)) ⟷ (∃x, ¬P x).lean


import data.set.basic
import data.list.basic
import tactic

open set list

-- Будем рассматривать языкы над алфавитом α
-- Язык это просто set (list α)
namespace languages

variables {α : Type} {L M : set (list α)}

-- Несколько удобных сокращений: ε = пустой список, 0 = ∅, 1 = {ε}, A + B = A ∪ B
def ε {α} : list α := []
instance : has_zero (set (list α)) := ⟨(∅ : set _)⟩
instance : has_one (set (list α)) := ⟨({[]} : set _)⟩
instance : has_add (set (list α)) := ⟨set.union⟩

-- Конкатенация языков состоит из таких слов `w`, что существуют слова `left ∈ L` и `right ∈ M`, что `w = left ++ right`)
def append (L M: set (list α)) :=
    { w : list α | ∃ (left ∈ L) (right ∈ M), w = left ++ right}

-- Будем обозначать умножение как `L * M`
instance : has_mul (set (list α)) := ⟨append⟩

-- И вспомогательные леммы
def zero_def : (0 : set (list α)) = ∅ := rfl
def one_def : (1 : set (list α)) = {[]} := rfl
@[simp] def mem_zero {w : list α} : w ∉ (0 : set (list α)) := by simp [zero_def]
@[simp] def mem_one {w : list α} : w ∈ (1 : set (list α)) ↔ w = [] := by refl
@[simp] def nil_mem_one : [] ∈ (1 : set (list α)) := by simp

-- Умелое использование `rcases` и `rintro` сильно вам поможет!
@[simp] lemma zero_mul (L : set (list α)) : 0 * L = 0 :=
begin
  ext w, split, {
    rintro ⟨left, hleft, right, hright, rfl⟩,
    simpa only using hleft,
  }, {
    intro h,
    exact absurd h mem_zero,
  }
end

@[simp] lemma mul_zero (L : set (list α)) : L * 0 = 0 :=
begin
  ext w, split, {
    rintro ⟨left, hleft, right, hright, rfl⟩,
    simpa only using hright,
  }, {
    intro h,
    exact absurd h mem_zero,
  }
end

@[simp] lemma append_one (A : set (list α)) : A * 1 = A :=
begin
  apply subset.antisymm, {
    rintro _ ⟨left, right, hleft, hright, rfl⟩,
    rw mem_one at hright,
    rwa [hright, list.append_nil],
  }, {
    rintro x xa,
    use [x, xa],
    use [[], nil_mem_one],
    exact (append_nil x).symm,
  },
end

@[simp] lemma one_append (A : set (list α)) : 1 * A = A :=
begin
  apply subset.antisymm, {
    rintro _ ⟨ left, hleft, right, hright, rfl ⟩,
    rw mem_one at hleft,
    rwa [hleft, list.nil_append],
  }, {
    rintro x xa,
    use [[], nil_mem_one, x, xa],
    exact (nil_append x).symm,
  },
end

lemma append_assoc (A B C : set (list α)):
    (A * B) * C = A * (B * C) :=
begin
  apply subset.antisymm, {
    rintro _ ⟨_, ⟨left, hleft, mid, hmid, rfl ⟩, right, hright, rfl ⟩,
    use [left, hleft],
    use [mid ++ right],
    use [mid, hmid, right, hright],
    exact append_assoc left mid right,
  }, {
    rintro _ ⟨left, hleft, _, ⟨mid, hmid, right, hright, rfl⟩, rfl ⟩,
    refine ⟨left ++ mid, ⟨left, hleft, mid, hmid, rfl⟩, right, hright, _⟩,
    exact (append_assoc left mid right).symm,
  },
end

@[simp] lemma left_distrib (A B C : set (list α)) : A * (B + C) = A * B + A * C :=
begin
  ext w, split, {
    rintro ⟨left, hleft, right, (hB | hC), rfl⟩,
    { left, exact ⟨left, hleft, right, hB, rfl⟩ },
    { right, exact ⟨left, hleft, right, hC, rfl⟩ },
  }, {
    rintro (⟨left, hleft, right, hB, rfl⟩ | ⟨left, hleft, right, hC, rfl⟩),
    { exact ⟨left, hleft, right, (or.inl hB), rfl⟩, },
    { exact ⟨left, hleft, right, (or.inr hC), rfl⟩, },
  }
end

@[simp] lemma right_distrib (A B C : set (list α)) : (A + B) * C = A * C + B * C :=
begin
    ext w, split, {
    rintro ⟨left, (hA | hB), right, hright, rfl⟩,
    { left, exact ⟨left, hA, right, hright, rfl⟩ },
    { right, exact ⟨left, hB, right, hright, rfl⟩ },
  }, {
    rintro (⟨left, hA, right, hright, rfl⟩ | ⟨left, hB, right, hright, rfl⟩),
    { exact ⟨left, (or.inl hA), right, hright, rfl⟩, },
    { exact ⟨left, (or.inr hB), right, hright, rfl⟩, },
  }
end

-- Докажем, что множество языков с операциями + = ∪ и * = append образуют полукольцо:
-- A + B - коммутативный моноид: 0, ассоциативность, коммутативность
-- A * B - полугруппа: 1, ассоциативность
-- Дистрибутивность и умножение на 0
-- Свойства (+) можно достать из стандартной библиотеки про `set.union`, а свойства (*) пришлось доказать
instance : semiring (set (list α)) := {
  add := (+),
  add_assoc := set.union_assoc,
  zero := 0,
  zero_add := set.empty_union,
  add_zero := set.union_empty,
  add_comm := set.union_comm,
  mul := (*),
  mul_assoc := append_assoc,
  zero_mul := zero_mul,
  mul_zero := mul_zero,
  one := 1,
  one_mul := one_append,
  mul_one := append_one,
  left_distrib := left_distrib,
  right_distrib := right_distrib,
}

-- Теперь можно использовать L^n = L * L * ... * L (n раз)
-- Полезные леммы: `pow_zero`, `pow_succ`, `pow_add`, ...

lemma append_subset_of_subset {A B C D : set (list α)} : A ⊆ C → B ⊆ D → A * B ⊆ C * D :=
begin
  rintro hAC hBD x ⟨left, hleft, right, hright, rfl⟩,
  use [left, hAC hleft, right, hBD hright],
end

lemma pow_subset_of_subset {A B : set (list α)} {n : ℕ} : A ⊆ B → A^n ⊆ B^n :=
begin
  intro hAB,
  induction n with n ih, {
      simp only [pow_zero],
  }, {
    rw [pow_succ, pow_succ],

    refine append_subset_of_subset hAB ih,
  },
end

lemma contain_eps_subset_power {A : set (list α)} {n : ℕ} (h : 1 ⊆ A) : A ⊆ A^(n.succ) :=
begin
  induction n with n ih, {
    rw pow_one,
  }, {
    rw pow_succ,
    nth_rewrite 0 ←one_append A,
    refine append_subset_of_subset h ih,
  }
end

-- Замыкание Клини языка L равно объединению L^n по всем натуральным n
def star (L : set (list α)) := {w : list α | ∃ (n : ℕ), w ∈ L^n}

@[simp] lemma mem_star {w : list α} : w ∈ star L ↔ ∃ (n : ℕ), w ∈ L^n := by refl

lemma star_eq_Union : star L = ⋃ (n : ℕ), L^n :=
begin
  ext w, simp only [mem_Union, mem_star],
end

@[simp] lemma nil_mem_star : [] ∈ star L :=
begin
  use 0,
  simp only [nil_mem_one, pow_zero],
end

@[simp] lemma one_subset_star : 1 ⊆ star L :=
begin
  simp [one_def],
end

@[simp] lemma pow_subset_star (n : ℕ) : L^n ⊆ star L :=
begin
  rw star_eq_Union,
  refine subset_Union _ _,
end

@[simp] lemma subset_star : L ⊆ star L :=
begin
  -- сделает `rw` только в левой части
  -- conv_lhs {rw ← pow_one L},
  nth_rewrite 0 [←pow_one L],
  exact pow_subset_star 1,
end

lemma star_subset_star : L ⊆ M → star L ⊆ star M :=
begin
  rintro hAB w ⟨n, ha⟩,
  use n,
  exact pow_subset_of_subset hAB ha,
end

lemma append_subset_star {A B L : set (list α)} :
    A ⊆ star L → B ⊆ star L → (A * B) ⊆ star L :=
begin
  rintro al bl _ ⟨left, hleft, right, hright, rfl⟩,
  rcases al hleft with ⟨an, ah⟩,
  rcases bl hright with ⟨bn, bh⟩,
  use an + bn,
  rw pow_add,
  use [left, ah, right, bh],
end

lemma star_append_star_eq_star : star L * star L = star L :=
begin
  apply subset.antisymm, {
    apply append_subset_star (set.subset.refl _) (set.subset.refl _),
  }, {
    conv_lhs {rw ← mul_one (star L)},
    apply append_subset_of_subset,
    { refl },
    { exact one_subset_star },
  }
end

lemma pow_star_eq_star (n : ℕ) : (star L)^n.succ = star L :=
begin
  induction n with n ih, {
    rw [pow_one],
  }, {
    apply subset.antisymm, {
      rw [pow_succ, ih, star_append_star_eq_star],
    }, {
      apply contain_eps_subset_power,
      exact one_subset_star,
    },
  },
end

-- Это было в ДЗ по дискретке!
theorem star_star_eq_star : star (star L) = star L :=
begin
  apply subset.antisymm, {
    rintro x ⟨n, hx⟩,
    cases n,
    { rw [pow_zero] at hx, apply one_subset_star hx },
    { rwa pow_star_eq_star at hx }
  }, {
    exact subset_star,
  },
end

-- L^n равен множеству слов, которые можно получить, как конкатенацию `n` слов из `L`.
-- Как записать, что `w` представляется как конкатенация `n` слов из `L`?
-- `∃ (l : list (list α)) (h : ∀ x ∈ l, x ∈ L), w = list.join l ∧ l.length = n`
-- Буквально: существует список слов `l`, что все слова в `l` принадлежат `L`, `w` равен `list.join l`, и длина `l` равна `n`
-- Начались сложные задания! Изучите подробнее API `list`, `list.has_mem`, `list.join` и прочие полезные вещи, или увереннее используйте `simp` :)
#check list.mem
#check mem_cons_self
#check list.join
#check join_append

lemma pow_eq_list_join {n : ℕ} :
    L^n = {w | ∃ (l : list (list α)) (h : ∀ x ∈ l, x ∈ L), w = list.join l ∧ l.length = n} :=
begin
  apply subset.antisymm, {
    induction n with n ih, {
      simp [one_def], use [[]], simp,
    }, {
      rw pow_succ,
      rintro _ ⟨left, hleft, right, hright, rfl⟩,
      obtain ⟨tail, hmem, rfl, rfl⟩ := ih hright,
      clear ih,
      simp,
      refine ⟨left :: tail, _, _, _⟩,
      -- Стало 3 цели: 1. все элементы списка лежат в L
      -- 2. join ведет себя по определению - `refl`
      -- 3. длина списка на 1 больше - `refl`
      { rintro x (rfl | xtail),
        { exact hleft, },
        { exact hmem _ xtail, },
      },
      { refl },
      { refl },
    }
  }, {
    induction n with n ih, {
      simp [one_def],
      rintro w l h rfl hlen,
      rw [length_eq_zero] at hlen,
      subst hlen,
      refl,
    }, {
      rintro w ⟨l, hmem, rfl, hlen⟩,
      rw pow_succ,
      -- Докажем, что l не пустой, потому что l.length = n.succ
      cases l with head tail,
      { exfalso, exact (ne_nil_of_length_eq_succ hlen) rfl, },
      rw [join],
      -- Осталось показать, что `head ∈ L` и `tail.join ∈ L^n`
      refine ⟨head, _, tail.join, _, rfl⟩, {
        apply hmem,
        apply mem_cons_self,
      }, {
        apply ih,
        simp,
        refine ⟨tail, _, _, _⟩,
        { rintro x xtail,
          apply hmem x,
          exact mem_cons_of_mem _ xtail, },
        { refl, },
        { simpa [nat.add_one] using hlen, },
      }
    }
  }
end

lemma mem_pow_iff_list_join {w : list α} {n : ℕ}
  : w ∈ L^n ↔ ∃ l (h : ∀ x ∈ l, x ∈ L), w = list.join l ∧ l.length = n :=
begin
  rw pow_eq_list_join,
  refl,
end

-- Замыкание Клини - все слова, которые можно представить как конкатенацию слов из L (без ограничения на количество)
lemma star_eq_list_join :
  star L = {w | ∃ l (h : ∀ x, x ∈ l → x ∈ L), w = list.join l} :=
begin
  ext w, split, {
    rintro ⟨n, hw⟩,
    rw pow_eq_list_join at hw,
    rcases hw with ⟨l, h, rfl, rfl⟩,
    use [l, h],
  }, {
    rintro ⟨l, h, rfl⟩,
    apply pow_subset_star l.length,
    rw [pow_eq_list_join],
    refine ⟨l, h, rfl, rfl⟩,
  }
end

lemma mem_star_iff_list_join {w : list α} :
  w ∈ star L ↔ ∃ l (h : ∀ x ∈ l, x ∈ L), w = list.join l :=
begin
  rw star_eq_list_join,
  refl,
end

-- Еще одно ДЗ
lemma union_star_eq_star_mul_star_star : star (L + M) = star (star L * star M) :=
begin
  apply subset.antisymm, {
    apply star_subset_star,
    apply union_subset, {
      conv_lhs {rw ←mul_one L},
      apply append_subset_of_subset (subset_star) (one_subset_star),
    }, {
      conv_lhs {rw ←one_mul M},
      apply append_subset_of_subset (one_subset_star) (subset_star),
    }
  }, {
    rw ← @star_star_eq_star _ (L + M),
    apply star_subset_star,
    conv_rhs { rw ← star_append_star_eq_star},
    apply append_subset_of_subset,
    { exact star_subset_star (subset_union_left _ _)},
    { exact star_subset_star (subset_union_right _ _)},
  }
end

lemma mul_star_subset_star : L * star L ⊆ star L :=
begin
  rintro _ ⟨left, hleft, right, ⟨n, hright⟩, rfl⟩,
  apply pow_subset_star (1 + n),
  rw [pow_add, pow_one],
  exact ⟨left, hleft, right, hright, rfl⟩,
end

lemma one_add_mul_star_eq_star : 1 + L * star L = star L :=
begin
  apply subset.antisymm, {
    exact union_subset one_subset_star mul_star_subset_star,
  }, {
    rintro w ⟨n, hw⟩,
    cases n, {
      left, exact hw,
    }, {
      right,
      rw [pow_succ] at hw,
      exact append_subset_of_subset (set.subset.refl _) (pow_subset_star n) hw,
    }
  }
end

lemma mul_star_mul_subset_star_mul : L * ((star L) * M) ⊆ (star L) * M :=
begin
  rw ← append_assoc,
  exact append_subset_of_subset (mul_star_subset_star) (set.subset.refl _),
end

-- Решаем уравнения в языках: если [] ∉ A, то L = A * L + B ↔ L = star A * b
lemma linear_eq_iff {A B : set (list α)} (hnil : [] ∉ A) : L = A * L + B ↔ L = star A * B :=
begin
  split, {
    intro h,
    ext w,
    suffices hn : ∀ (n : ℕ), ∀ (w : list α) (hn : n = w.length), (w ∈ L ↔ w ∈ star A * B), {
      refine hn w.length _ rfl,
    },
    clear w,
    intro n,
    induction n using nat.strong_induction_on with n ih,
    dsimp only at ih,
    rintro w rfl,
    split, {
      intro wL,
      rw h at wL,
      rcases wL with ⟨left, hleft, right, hright, rfl⟩ | wL, {
        have right_length : right.length < (left ++ right).length :=
        begin
          rw [length_append],
          have left_neq_nil : left ≠ [] := λ hln, by {subst hln, exact hnil hleft},
          have left_length_pos : left.length > 0 := length_pos_of_ne_nil left_neq_nil,
          simpa only [lt_add_iff_pos_left] using left_length_pos,
        end,
        specialize ih right.length right_length right rfl,
        rw ih at hright,
        apply mul_star_mul_subset_star_mul,
        exact ⟨left, hleft, right, hright, rfl⟩,
      }, {
        rw ←one_mul B at wL,
        apply append_subset_of_subset (one_subset_star) (set.subset.refl _) wL,
      },
    }, {
      rintro ⟨left, hleft, right, hright, rfl⟩,
      rw mem_star_iff_list_join at hleft,
      rcases hleft with ⟨l, hlist, rfl⟩,
      clear ih,
      induction l with head tail ih, {
        simp, rw h, exact or.inr hright,
      }, {
        simp, rw h, left,
        refine ⟨head, _, tail.join ++ right, _, rfl⟩,
        { apply hlist, exact mem_cons_self head tail},
        { apply ih, simp only [mem_cons_iff, forall_eq_or_imp] at hlist, exact hlist.2,},
      }
    }
  }, {
    rintro rfl,
    rw ←append_assoc,
    -- rw ←one_add_mul_star_eq_star,
    -- nth_rewrite 0 ←one_add_mul_star_eq_star,
    conv_lhs {rw [←one_add_mul_star_eq_star, right_distrib, one_append]},
    -- https://leanprover-community.github.io/mathlib_docs/tactics.html#abel
    abel,
    -- Также сработает ac_refl: https://leanprover-community.github.io/mathlib_docs/tactics.html#ac_refl
  }
end

end languages


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

! This file was ported from Lean 3 source module algebra.order.group.min_max
! leanprover-community/mathlib commit 10b4e499f43088dd3bb7b5796184ad5216648ab1
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Algebra.Order.Group.Abs
import Mathlib.Algebra.Order.Monoid.MinMax

/-!
# `min` and `max` in linearly ordered groups.
-/

section

variable {α : Type _} [Group α] [LinearOrder α] [CovariantClass α α (. * .) (. ≤ .)]

@[to_additive (attr := simp)]
theorem max_one_div_max_inv_one_eq_self (a : α) : max a 1 / max a⁻¹ 1 = a := by
  rcases le_total a 1 with (h | h) <;> simp [h]
#align max_one_div_max_inv_one_eq_self max_one_div_max_inv_one_eq_self
#align max_zero_sub_max_neg_zero_eq_self max_zero_sub_max_neg_zero_eq_self

alias max_zero_sub_max_neg_zero_eq_self ← max_zero_sub_eq_self
#align max_zero_sub_eq_self max_zero_sub_eq_self

end

section LinearOrderedCommGroup

variable {α : Type _} [LinearOrderedCommGroup α] {a b c : α}

@[to_additive min_neg_neg]
theorem min_inv_inv' (a b : α) : min a⁻¹ b⁻¹ = (max a b)⁻¹ :=
  Eq.symm <| (@Monotone.map_max α αᵒᵈ _ _ Inv.inv a b) fun _ _ =>
  -- Porting note: Explicit `α` necessary to infer `CovariantClass` instance
    (@inv_le_inv_iff α _ _ _).mpr
#align min_inv_inv' min_inv_inv'
#align min_neg_neg min_neg_neg

@[to_additive max_neg_neg]
theorem max_inv_inv' (a b : α) : max a⁻¹ b⁻¹ = (min a b)⁻¹ :=
  Eq.symm <| (@Monotone.map_min α αᵒᵈ _ _ Inv.inv a b) fun _ _ =>
  -- Porting note: Explicit `α` necessary to infer `CovariantClass` instance
    (@inv_le_inv_iff α _ _ _).mpr
#align max_inv_inv' max_inv_inv'
#align max_neg_neg max_neg_neg

@[to_additive min_sub_sub_right]
theorem min_div_div_right' (a b c : α) : min (a / c) (b / c) = min a b / c := by
  simpa only [div_eq_mul_inv] using min_mul_mul_right a b c⁻¹
#align min_div_div_right' min_div_div_right'
#align min_sub_sub_right min_sub_sub_right

@[to_additive max_sub_sub_right]
theorem max_div_div_right' (a b c : α) : max (a / c) (b / c) = max a b / c := by
  simpa only [div_eq_mul_inv] using max_mul_mul_right a b c⁻¹
#align max_div_div_right' max_div_div_right'
#align max_sub_sub_right max_sub_sub_right

@[to_additive min_sub_sub_left]
theorem min_div_div_left' (a b c : α) : min (a / b) (a / c) = a / max b c := by
  simp only [div_eq_mul_inv, min_mul_mul_left, min_inv_inv']
#align min_div_div_left' min_div_div_left'
#align min_sub_sub_left min_sub_sub_left

@[to_additive max_sub_sub_left]
theorem max_div_div_left' (a b c : α) : max (a / b) (a / c) = a / min b c := by
  simp only [div_eq_mul_inv, max_mul_mul_left, max_inv_inv']
#align max_div_div_left' max_div_div_left'
#align max_sub_sub_left max_sub_sub_left

end LinearOrderedCommGroup

section LinearOrderedAddCommGroup

variable {α : Type _} [LinearOrderedAddCommGroup α] {a b c : α}

theorem max_sub_max_le_max (a b c d : α) : max a b - max c d ≤ max (a - c) (b - d) := by
  simp only [sub_le_iff_le_add, max_le_iff]; constructor
  calc
    a = a - c + c := (sub_add_cancel a c).symm
    _ ≤ max (a - c) (b - d) + max c d := add_le_add (le_max_left _ _) (le_max_left _ _)

  calc
    b = b - d + d := (sub_add_cancel b d).symm
    _ ≤ max (a - c) (b - d) + max c d := add_le_add (le_max_right _ _) (le_max_right _ _)

#align max_sub_max_le_max max_sub_max_le_max

theorem abs_max_sub_max_le_max (a b c d : α) : |max a b - max c d| ≤ max (|a - c|) (|b - d|) := by
  refine' abs_sub_le_iff.2 ⟨_, _⟩
  · exact (max_sub_max_le_max _ _ _ _).trans (max_le_max (le_abs_self _) (le_abs_self _))
  · rw [abs_sub_comm a c, abs_sub_comm b d]
    exact (max_sub_max_le_max _ _ _ _).trans (max_le_max (le_abs_self _) (le_abs_self _))
#align abs_max_sub_max_le_max abs_max_sub_max_le_max

theorem abs_min_sub_min_le_max (a b c d : α) : |min a b - min c d| ≤ max (|a - c|) (|b - d|) := by
  simpa only [max_neg_neg, neg_sub_neg, abs_sub_comm] using
    abs_max_sub_max_le_max (-a) (-b) (-c) (-d)
#align abs_min_sub_min_le_max abs_min_sub_min_le_max

theorem abs_max_sub_max_le_abs (a b c : α) : |max a c - max b c| ≤ |a - b| := by
  simpa only [sub_self, abs_zero, max_eq_left (abs_nonneg (a - b))]
    using abs_max_sub_max_le_max a c b c
#align abs_max_sub_max_le_abs abs_max_sub_max_le_abs

end LinearOrderedAddCommGroup


import data.int.gcd
import algebra.big_operators.basic
import data.nat.interval
import tactic

open_locale big_operators -- enable notation
open finset

-- Sierpinski #8

lemma  div_eq_iff_mul_eq' (a b c : ℤ) (hb : b ≠ 0 ) :
  (a : ℚ) / (b : ℚ) = (c : ℚ) ↔ (a : ℚ) = (b : ℚ) * (c : ℚ) :=
begin
  split, {
    intro h,
    library_search,

  }, {

  }
end

lemma  sum_of_powers_of_a_minus_1_eq_power_of_a_minus_1 (m a : ℕ) (ha : a > 1) :
  (a^m - 1) / (a - 1) = ∑i  in finset.range (m-1), (a^(i+1) - 1) :=
begin
  induction m with k hk,
  simp,
  have ha2 : a - 1 > 0 := tsub_pos_of_lt ha,
  have ha3 : a - 1 ≠ 0 := ne_of_gt ha2,
  have hk2 : (a ^ k - 1)  = (a - 1) * (∑ (i : ℕ) in range (k - 1), (a ^ (i + 1) - 1)) := by library_search,

end

lemma  divides_a_minus_one_a_k_minus_one (a k : ℕ) (h : a > 1) :
  a - 1 ∣ a ^ k - 1 :=
begin
  sorry,
end

theorem  gcd_of_powers_minus_one_over_minus_one_eq_gcd_of_minus_one_and_m (m a : ℕ) (ha : 1 < a) :
  int.gcd((a^m - 1) / (a - 1)) (a - 1) = int.gcd (a - 1) m :=
begin
  have h := sum_of_powers_of_a_minus_1_eq_power_of_a_minus_1 m a ha,
  set d := int.gcd ((a^m - 1) / (a - 1)) (a - 1),
  have hd : d ∣ m := by sorry,
  set δ := int.gcd (↑a - 1) m,
  by_contra hc,
  change (d ≠ δ) at hc,
  have hc2 : d < δ ∨ d > δ := ne.lt_or_lt hc,
  cases hc2,
  {
    -- show d ∣ (a-1), d ∣ m --> d ≥ δ
    sorry,
  }, {

  }

end

-- Sierpinski #10

theorem  odd_gt_1_iff_dvd_sum_of_powers (n : ℕ) (hn : n > 1) :
  n ∣ ∑ i in finset.range n, i^n ↔ n % 2 = 1 :=
begin

end


/-
Copyright (c) 2020 Ruben Van de Velde, Stanislas Polu. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Ruben Van de Velde, Stanislas Polu
-/

import data.real.basic
import analysis.normed_space.basic

/-!
# IMO 1972 B2

Problem: `f` and `g` are real-valued functions defined on the real line. For all `x` and `y`,
`f(x + y) + f(x - y) = 2f(x)g(y)`. `f` is not identically zero and `|f(x)| ≤ 1` for all `x`.
Prove that `|g(x)| ≤ 1` for all `x`.
-/

/--
This proof begins by introducing the supremum of `f`, `k ≤ 1` as well as `k' = k / ∥g y∥`. We then
suppose that the conclusion does not hold (`hneg`) and show that `k ≤ k'` (by
`2 * (∥f x∥ * ∥g y∥) ≤ 2 * k` obtained from the main hypothesis `hf1`) and that `k' < k` (obtained
from `hneg` directly), finally raising a contradiction with `k' < k'`.

(Authored by Stanislas Polu inspired by Ruben Van de Velde).
-/
theorem imo1972_p5 (f g : ℝ → ℝ)
  (hf1 : ∀ x, ∀ y, (f(x+y) + f(x-y)) = 2 * f(x) * g(y))
  (hf2 : ∀ y, ∥f(y)∥ ≤ 1)
  (hf3 : ∃ x, f(x) ≠ 0)
  (y : ℝ) :
  ∥g(y)∥ ≤ 1 :=
begin
  classical,
  set S := set.range (λ x, ∥f x∥),
  -- Introduce `k`, the supremum of `f`.
  let k : ℝ := Sup (S),

  -- Show that `∥f x∥ ≤ k`.
  have hk₁ : ∀ x, ∥f x∥ ≤ k,
  { have h : bdd_above S, from ⟨1, set.forall_range_iff.mpr hf2⟩,
    intro x,
    exact le_cSup h (set.mem_range_self x), },
  -- Show that `2 * (∥f x∥ * ∥g y∥) ≤ 2 * k`.
  have hk₂ : ∀ x, 2 * (∥f x∥ * ∥g y∥) ≤ 2 * k,
  { intro x,
    calc 2 * (∥f x∥ * ∥g y∥)
        = ∥2 * f x * g y∥ : by simp [real.norm_eq_abs, abs_mul, mul_assoc]
    ... = ∥f (x + y) + f (x - y)∥ : by rw hf1
    ... ≤ ∥f (x + y)∥ + ∥f (x - y)∥ : norm_add_le _ _
    ... ≤ k + k : add_le_add (hk₁ _) (hk₁ _)
    ... = 2 * k : (two_mul _).symm, },

  -- Suppose the conclusion does not hold.
  by_contra' hneg,
  set k' := k / ∥g y∥,

  -- Demonstrate that `k' < k` using `hneg`.
  have H₁ : k' < k,
  { have h₁ : 0 < k,
    { obtain ⟨x, hx⟩ := hf3,
      calc 0
          < ∥f x∥ : norm_pos_iff.mpr hx
      ... ≤ k : hk₁ x },
    rw div_lt_iff,
    apply lt_mul_of_one_lt_right h₁ hneg,
    exact trans zero_lt_one hneg },

  -- Demonstrate that `k ≤ k'` using `hk₂`.
  have H₂ : k ≤ k',
  { have h₁ : ∃ x : ℝ, x ∈ S,
    { use ∥f 0∥, exact set.mem_range_self 0, },
    have h₂ : ∀ x, ∥f x∥ ≤ k',
    { intros x,
      rw le_div_iff,
      { apply (mul_le_mul_left zero_lt_two).mp (hk₂ x) },
      { exact trans zero_lt_one hneg } },
    apply cSup_le h₁,
    rintros y' ⟨yy, rfl⟩,
    exact h₂ yy },

  -- Conclude by obtaining a contradiction, `k' < k'`.
  apply lt_irrefl k',
  calc k'
      < k : H₁
  ... ≤ k' : H₂,
end


import game.world7.level8 -- hide
import game.world6.level8 -- hide
import tactic.tauto -- useful high-powered tactic
local attribute [instance, priority 10] classical.prop_decidable -- hide
/-
# Advanced proposition world.

You already know enough to embark on advanced addition world. But here are just a couple
more things.

## Level 9: `exfalso` and proof by contradiction.

It's certainly true that $P\land(\lnot P)\implies Q$ for any propositions $P$
and $Q$, because the left hand side of the implication is false. But how do
we prove that `false` implies any proposition $Q$? A cheap way of doing it in
Lean is using the `exfalso` tactic, which changes any goal at all to `false`.
You might think this is a step backwards, but if you have a hypothesis `h : ¬ P`
then after `rw not_iff_imp_false at h,` you can `apply h,` to make progress.
Try solving this level without using `cc` or `tauto`, but using `exfalso` instead.

-/

/- Lemma : no-side-bar
If $P$ and $Q$ are true/false statements, then
$$(P\land(\lnot P))\implies Q.$$
-/
lemma contra (P Q : Prop) : (P ∧ ¬ P) → Q :=
begin
  intro h,
  cases h with p np,
  rw not_iff_imp_false at np,
  exfalso,
  apply np,
  exact p,

end

/-
## Pro tip.

`¬ P` is actually `P → false` *by definition*. Try
commenting out `rw not_iff_imp_false at ...` by putting two minus signs `--`
before the `rw`. Does it still compile?
-/

/- Tactic : exfalso

## Summary

`exfalso` changes your goal to `false`.

## Details

We know that `false` implies `P` for any proposition `P`, and so if your goal is `P`
then you should be able to `apply` `false → P` and reduce your goal to `false`. This
is what the `exfalso` tactic does. The theorem that `false → P` is called `false.elim`
so one can achieve the same effect with `apply false.elim`.

This tactic can be used in a proof by contradiction, where the hypotheses are enough
to deduce a contradiction and the goal happens to be some random statement (possibly
a false one) which you just want to simplify to `false`.
-/


open nat

universe u

inductive pfin (n : nat) : Type u
| mk (val : nat) (is_lt : val < n) : pfin

namespace pfin

@[reducible]
def val {n} : pfin n → nat
| ⟨v, _⟩ := v

@[reducible]
def is_lt {n} : ∀ (a : pfin n), a.val < n
| ⟨_, h⟩ := h

protected def lt {n} (a b : pfin n) : Prop :=
a.val < b.val

protected def le {n} (a b : pfin n) : Prop :=
a.val ≤ b.val

instance {n} : has_lt (pfin n)  := ⟨pfin.lt⟩
instance {n} : has_le (pfin n)  := ⟨pfin.le⟩

instance decidable_lt {n} (a b : pfin n) :  decidable (a < b) :=
nat.decidable_lt _ _

instance decidable_le {n} (a b : pfin n) : decidable (a ≤ b) :=
nat.decidable_le _ _

def {w} elim0 {α : Sort w} : pfin 0 → α
| ⟨_, h⟩ := absurd h (nat.not_lt_zero _)

variable {n : nat}

lemma eq_of_veq : ∀ {i j : pfin n}, (val i) = (val j) → i = j
| ⟨iv, ilt₁⟩ ⟨.(iv), ilt₂⟩ rfl := rfl

lemma veq_of_eq : ∀ {i j : pfin n}, i = j → (val i) = (val j)
| ⟨iv, ilt⟩ .(_) rfl := rfl

lemma ne_of_vne {i j : pfin n} (h : val i ≠ val j) : i ≠ j :=
λ h', absurd (veq_of_eq h') h

lemma vne_of_ne {i j : pfin n} (h : i ≠ j) : val i ≠ val j :=
λ h', absurd (eq_of_veq h') h

end pfin

open pfin

instance (n : nat) : decidable_eq (pfin n) :=
λ i j, decidable_of_decidable_of_iff
  (nat.decidable_eq i.val j.val) ⟨eq_of_veq, veq_of_eq⟩


/-
Copyright (c) 2022 Aaron Anderson. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Aaron Anderson
-/
import model_theory.satisfiability
import combinatorics.simple_graph.basic

/-!
# First-Ordered Structures in Graph Theory
This file defines first-order languages, structures, and theories in graph theory.

## Main Definitions
* `first_order.language.graph` is the language consisting of a single relation representing
adjacency.
* `simple_graph.Structure` is the first-order structure corresponding to a given simple graph.
* `first_order.language.Theory.simple_graph` is the theory of simple graphs.
* `first_order.language.simple_graph_of_structure` gives the simple graph corresponding to a model
of the theory of simple graphs.

-/

universes u v w w'

namespace first_order
namespace language
open_locale first_order
open Structure

variables {L : language.{u v}} {α : Type w} {V : Type w'} {n : ℕ}

/-! ### Simple Graphs -/

/-- The language consisting of a single relation representing adjacency. -/
protected def graph : language :=
language.mk₂ empty empty empty empty unit

/-- The symbol representing the adjacency relation. -/
def adj : language.graph.relations 2 := unit.star

/-- Any simple graph can be thought of as a structure in the language of graphs. -/
def _root_.simple_graph.Structure (G : simple_graph V) :
  language.graph.Structure V :=
Structure.mk₂ empty.elim empty.elim empty.elim empty.elim (λ _, G.adj)

namespace graph

instance : is_relational (language.graph) := language.is_relational_mk₂

instance : subsingleton (language.graph.relations n) :=
language.subsingleton_mk₂_relations

end graph

/-- The theory of simple graphs. -/
protected def Theory.simple_graph : language.graph.Theory :=
{adj.irreflexive, adj.symmetric}

@[simp] lemma Theory.simple_graph_model_iff [language.graph.Structure V] :
  V ⊨ Theory.simple_graph ↔
    irreflexive (λ x y : V, rel_map adj ![x,y]) ∧ symmetric (λ x y : V, rel_map adj ![x,y]) :=
by simp [Theory.simple_graph]

instance simple_graph_model (G : simple_graph V) :
  @Theory.model _ V G.Structure Theory.simple_graph :=
begin
  simp only [Theory.simple_graph_model_iff, rel_map_apply₂],
  exact ⟨G.loopless, G.symm⟩,
end

variables (V)

/-- Any model of the theory of simple graphs represents a simple graph. -/
@[simps] def simple_graph_of_structure [language.graph.Structure V] [V ⊨ Theory.simple_graph] :
  simple_graph V :=
{ adj := λ x y, rel_map adj ![x,y],
  symm := relations.realize_symmetric.1 (Theory.realize_sentence_of_mem Theory.simple_graph
      (set.mem_insert_of_mem _ (set.mem_singleton _))),
  loopless := relations.realize_irreflexive.1 (Theory.realize_sentence_of_mem Theory.simple_graph
      (set.mem_insert _ _)) }

variables {V}

@[simp] lemma _root_.simple_graph.simple_graph_of_structure (G : simple_graph V) :
  @simple_graph_of_structure V G.Structure _ = G :=
by { ext, refl }

@[simp] lemma Structure_simple_graph_of_structure
  [S : language.graph.Structure V] [V ⊨ Theory.simple_graph] :
  (simple_graph_of_structure V).Structure = S :=
begin
  ext n f xs,
  { exact (is_relational.empty_functions n).elim f },
  { ext n r xs,
    rw iff_eq_eq,
    cases n,
    { exact r.elim },
    { cases n,
      { exact r.elim },
      { cases n,
        { cases r,
          change rel_map adj ![xs 0, xs 1] = _,
          refine congr rfl (funext _),
          simp [fin.forall_fin_two], },
        { exact r.elim } } } }
end

theorem Theory.simple_graph_is_satisfiable :
  Theory.is_satisfiable Theory.simple_graph :=
⟨@Theory.Model.of _ _ unit (simple_graph.Structure ⊥) _ _⟩

end language
end first_order


import data.nat.basic
import tactic.linarith
import tactic.ring

namespace nat
lemma power_geq_1 {k n : ℕ} : (succ k)^n ≥ 1 := begin
induction n,
{ rw [pow_zero], exact le_refl _ },
{ rw [pow_succ], exact _root_.le_add_left n_ih }
end

lemma mul_left_cancel {a b c : ℕ} : a > 0 → a * b = a * c → b = c := λ pos eq, calc
  b = b * a / a : symm (nat.mul_div_cancel _ pos)
  ... = a * b / a : by rw [mul_comm]
  ... = a * c / a : by rw [eq]
  ... = c * a / a : by rw [mul_comm]
  ... = c : nat.mul_div_cancel _ pos

lemma sub_add_from_add_sub : Π {a b c : ℕ}, a ≥ c -> b ≥ c -> a + (b - c) = (a - c) + b
| 0 b c a_ge_c b_ge_c := begin have : c = 0 := by linarith, simp [this] end
| (a+1) 0 c a_ge_c b_ge_c := begin have : c = 0 := by linarith, simp [this] end
| (a+1) (b+1) 0 a_ge_c b_ge_c := by simp
| (a+1) (b+1) (c+1) a_ge_c b_ge_c := begin
  have a_ge_c : a ≥ c := lt_succ_iff.mp a_ge_c,
  have b_ge_c : b ≥ c := lt_succ_iff.mp b_ge_c,
  repeat { rw [add_one] },
  repeat { rw [add_succ] <|> rw [succ_add] },
  repeat { rw [succ_sub_succ] },
  rw [sub_add_from_add_sub a_ge_c b_ge_c]
end
lemma pow_le_pow {a b k : ℕ} : a ≥ b -> a^k ≥ b^k := begin
  intro gt,
  induction k,
  { simp },
  rw [pow_succ, pow_succ],
  exact (mul_le_mul k_ih gt (by linarith) (by linarith))
end
lemma pow_ge_one {a k : ℕ} : a ≥ 1 -> a^k ≥ 1 := λ a_pos, calc
  a^k ≥ 1^k : pow_le_pow a_pos
  ... = 1 : by simp
lemma prime_positive {p : ℕ} : prime p -> p > 0 := λ pr, have h : p ≥ 2 := prime.two_le pr, by linarith
lemma prime_pow_ge_one {p k : ℕ} : prime p -> p^k ≥ 1 :=
  λ pr, pow_ge_one (prime_positive pr)

lemma pow_lt_pow_right {a k l : ℕ} : a > 1 -> k < l -> a^k < a^l := begin
  intros a_big lt,
  induction lt,
  { exact calc
      a^k = 1 * a^k : symm (one_mul _)
      ... < a * a^k : mul_lt_mul a_big (refl _) (pow_ge_one (by linarith)) (by linarith)
      ... = a^k * a : mul_comm _ _
      ... = a^(k + 1) : by simp [pow_succ]},
  exact calc
    a^k = 1 * a^k : symm (one_mul _)
    ... < a * a^lt_b : mul_lt_mul a_big (by linarith) (pow_ge_one (by linarith)) (by linarith)
    ... = a^lt_b * a : mul_comm _ _
    ... = a^(lt_b + 1) : by simp [pow_succ]
end

lemma prod_lt_gt {a a' b b' : ℕ} : b > 0 -> a * b = a' * b' -> a < a' -> b > b' := begin
  intros b_pos eq a_lt_a',
  have : a' * b > a' * b' := calc
  a' * b > a * b : mul_lt_mul_of_pos_right a_lt_a' b_pos
  ... = a' * b' : eq,
  apply lt_of_mul_lt_mul_left this (zero_le _)
end
lemma prod_gt_lt {a a' b b' : ℕ} : a > 0 -> a * b = a' * b' -> b < b' -> a > a' := begin
  rw [mul_comm a b, mul_comm a' b'],
  apply prod_lt_gt
end

lemma pos_of_mul_pos {a b : ℕ} : a * b > 0 -> a > 0 := begin
  cases a,
  { simp },
  intros _,
  exact succ_pos a
end
lemma pos_of_mul_pos_right {a b : ℕ} : a * b > 0 -> b > 0 := begin
  cases b,
  { simp },
  intros _,
  exact succ_pos b
end

lemma pos_of_dvd_pos {a b : ℕ} : b > 0 -> a ∣ b -> a > 0 := begin
  rintros gt ⟨k, prod⟩,
  rw [prod] at gt,
  apply pos_of_mul_pos gt,
end

lemma dvd_pow_succ {p k n : ℕ} : prime p -> n ∣ p^(k + 1) -> n = p^(k + 1) ∨ n ∣ p^k := begin
  rintros pr divides,
  obtain ⟨l , bound, pf⟩ := (dvd_prime_pow pr).1 divides,
  rcases lt_trichotomy l (k+1) with h | h | h,
  { have : l ≤ k := by linarith,
    apply or.inr,
    apply (dvd_prime_pow pr).2,
    finish },
  { apply or.inl,
    rw [pf, h] },
  have : p^(k + 1) > 0 := prime_pow_ge_one pr,
  have : n ≤ p^(k + 1) := le_of_dvd this divides,
  have : p^(k + 1) < p^(k + 1) := calc
  p^(k + 1) < p^l : pow_lt_pow_right (prime.two_le pr) h
  ... = n : symm pf
  ... ≤ p^(k + 1) : this,
  linarith
end

lemma coprime_gcd {a b c : ℕ} : coprime b c → coprime (gcd a b) (gcd a c) :=
  coprime.coprime_dvd_left (gcd_dvd_right a b) ∘
  coprime.coprime_dvd_right (gcd_dvd_right a c)

lemma prod_coprime_gcd {a b c : ℕ} : a > 0 -> coprime b c -> gcd a b * gcd a c = gcd a (b * c) := begin
  intros a_pos coprime,
  apply le_antisymm;
  apply le_of_dvd,
  { exact gcd_pos_of_pos_left _ a_pos },
  { exact nat.coprime.mul_dvd_of_dvd_of_dvd (coprime_gcd coprime) (gcd_dvd_gcd_mul_right_right a b c) (gcd_dvd_gcd_mul_left_right a c b) },
  { exact (mul_lt_mul_left (gcd_pos_of_pos_left _ a_pos)).2 (gcd_pos_of_pos_left _ a_pos) },
  { exact gcd_mul_dvd_mul_gcd _ _ _ }
end
lemma prod_coprime_gcd_left {a b c : ℕ} : c > 0 -> coprime a b -> gcd a c * gcd b c = gcd (a * b) c := begin
  rw [gcd_comm a c, gcd_comm b c, gcd_comm (a * b) c],
  exact prod_coprime_gcd
end

open nat (succ)

@[simp]
lemma gcd_add {a b : ℕ} : nat.gcd a (a + b) = nat.gcd b a := begin
  cases a,
  { simp },
  cases b,
  { simp },
  exact calc
    nat.gcd (succ a) (succ a + succ b)
        = nat.gcd (succ b % succ a) (succ a) : by rw [nat.gcd_succ, nat.add_mod_left]
    ... = nat.gcd (succ b) (succ a) : by rw [←nat.gcd_succ, nat.gcd_comm]
end

theorem coprime_sub_one {n : ℕ} : n > 0 -> coprime (n - 1) n := begin
  intro n_pos,
  cases n,
  { linarith },
  exact calc
    nat.gcd ((n + 1) - 1) (n + 1) = nat.gcd 1 n : by simp
    ... = 1 : nat.gcd_one_left _
end

end nat


import ..prooflab
import lectures.lec0_intro

/-! # Homework 0
Homework must be done individually.
Replace the placeholders `sorry` with your proofs.
refl, exact, rw
-/

set_option pp.beta true
set_option pp.generalized_field_notation false

namespace PROOFS

/-! ## Question 1  -/

example (x y : ℕ) :
  y + 0 = y :=
begin
  refl, -- 0 added to a number is that number
end

/-! ## Question 2 -/

example (m n : ℕ) (h₁ : n = 4) (h₂: m^2 = n) :
  n = m^2 :=
begin
  rw h₂, -- substitute m^2 with n, left with n=n, then applies refl
end

/-! ## Question 3 -/

example (x y : ℕ) (h₁ : y = x) (h₂ : y - 1 = 0) :
 5^(y - 1) = (2 + 3)^(x - 1) :=
begin
  rw h₁, -- replaces y with x in target, simplifies 2+3 to 5, then left with 5^(x-1) = 5^(x-1), then applies refl
end

/-! ## Question 4 -/

example (x y : ℕ) (h₁ : y = x) (h₂ : x - 1 = 0) :
 5^(y - 1) = 5^0 :=
begin
  rw h₁, -- replaces y with x in target, left with 5^(x-1) = 5^0
  rw h₂, -- replaces x-1 in target with 0, left with 5^0 = 5^0, applies refl
end

/-! ## Question 5 -/

example (a b c x y z : ℕ) (h₁ : 26 = x^2 + y^2 + z^2)
(h₂ : x^2 = 2 * a) (h₃ : y^2 = b) (h₄ : z^2 = 1) :
2 * a + b + 1 - z = 26 - z :=
begin
  rw h₂ at h₁, -- replaces x^2 with 2a in h₁
  rw h₃ at h₁, -- replaces y^2 with b in h₁
  rw h₄ at h₁, -- replaces z^2 with 1 in h₁
  rw h₁, -- replaces 2a + b + 1 in target with 26, left with 26-z = 26-z, applies refl
end

end PROOFS


theorem le_antisymm (a b : mynat) (hab : a ≤ b) (hba : b ≤ a) : a = b :=
begin
cases hab with c hc,
cases hba with d hd,
rw hc at hd,
rw add_assoc at hd,
symmetry at hd,
have h1 := eq_zero_of_add_right_eq_self hd,
have h2 := add_right_eq_zero h1,
rw hc,
rw h2,
rwa add_zero,
end


/-
Copyright (c) 2021 Anne Baanen. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Anne Baanen
-/
import algebra.algebra.basic
import number_theory.class_number.admissible_absolute_value

/-!
# Admissible absolute value on the integers
This file defines an admissible absolute value `absolute_value.abs_is_admissible`
which we use to show the class number of the ring of integers of a number field
is finite.

## Main results

 * `absolute_value.abs_is_admissible` shows the "standard" absolute value on `ℤ`,
   mapping negative `x` to `-x`, is admissible.
-/

namespace absolute_value

open int

/-- We can partition a finite family into `partition_card ε` sets, such that the remainders
in each set are close together. -/
lemma exists_partition_int (n : ℕ) {ε : ℝ} (hε : 0 < ε) {b : ℤ} (hb : b ≠ 0) (A : fin n → ℤ) :
  ∃ (t : fin n → fin ⌈1 / ε⌉₊),
  ∀ i₀ i₁, t i₀ = t i₁ → ↑(abs (A i₁ % b - A i₀ % b)) < abs b • ε :=
begin
  have hb' : (0 : ℝ) < ↑(abs b) := int.cast_pos.mpr (abs_pos.mpr hb),
  have hbε : 0 < abs b • ε,
  { rw algebra.smul_def,
    exact mul_pos hb' hε },
  have hfloor : ∀ i, 0 ≤ floor ((A i % b : ℤ) / (abs b • ε) : ℝ),
  { intro i,
    exact floor_nonneg.mpr (div_nonneg (cast_nonneg.mpr (mod_nonneg _ hb)) hbε.le) },
  refine ⟨λ i, ⟨nat_abs (floor ((A i % b : ℤ) / (abs b • ε) : ℝ)), _⟩, _⟩,
  { rw [← coe_nat_lt, nat_abs_of_nonneg (hfloor i), floor_lt],
    apply lt_of_lt_of_le _ (nat.le_ceil _),
    rw [algebra.smul_def, ring_hom.eq_int_cast, ← div_div, div_lt_div_right hε,
        div_lt_iff hb', one_mul, cast_lt],
    exact int.mod_lt _ hb },
  intros i₀ i₁ hi,
  have hi : (⌊↑(A i₀ % b) / abs b • ε⌋.nat_abs : ℤ) = ⌊↑(A i₁ % b) / abs b • ε⌋.nat_abs :=
    congr_arg (coe : ℕ → ℤ) (subtype.mk_eq_mk.mp hi),
  rw [nat_abs_of_nonneg (hfloor i₀), nat_abs_of_nonneg (hfloor i₁)] at hi,
  have hi := abs_sub_lt_one_of_floor_eq_floor hi,
  rw [abs_sub_comm, ← sub_div, abs_div, abs_of_nonneg hbε.le, div_lt_iff hbε, one_mul] at hi,
  rwa [int.cast_abs, int.cast_sub]
end

/-- `abs : ℤ → ℤ` is an admissible absolute value -/
noncomputable def abs_is_admissible : is_admissible absolute_value.abs :=
{ card := λ ε, ⌈1 / ε⌉₊,
  exists_partition' := λ n ε hε b hb, exists_partition_int n hε hb,
  .. absolute_value.abs_is_euclidean }

noncomputable instance : inhabited (is_admissible absolute_value.abs) :=
⟨abs_is_admissible⟩

end absolute_value