/-
Copyright (c) 2021 Yaël Dillies, Bhavik Mehta. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yaël Dillies, Bhavik Mehta, Huỳnh Trần Khanh, Stuart Presnell
-/
import algebra.big_operators.basic
import data.finset.sym
import data.fintype.sum

/-!
# Stars and bars

In this file, we prove (in `sym.card_sym_eq_multichoose`) that the function `multichoose n k`
defined in `data/nat/choose/basic` counts the number of multisets of cardinality `k` over an
alphabet of cardinality `n`. In conjunction with `nat.multichoose_eq` proved in
`data/nat/choose/basic`, which shows that `multichoose n k = choose (n + k - 1) k`,
this is central to the "stars and bars" technique in combinatorics, where we switch between
counting multisets of size `k` over an alphabet of size `n` to counting strings of `k` elements
("stars") separated by `n-1` dividers ("bars").

## Informal statement

Many problems in mathematics are of the form of (or can be reduced to) putting `k` indistinguishable
objects into `n` distinguishable boxes; for example, the problem of finding natural numbers
`x1, ..., xn` whose sum is `k`. This is equivalent to forming a multiset of cardinality `k` from
an alphabet of cardinality `n` -- for each box `i ∈ [1, n]` the multiset contains as many copies
of `i` as there are items in the `i`th box.

The "stars and bars" technique arises from another way of presenting the same problem. Instead of
putting `k` items into `n` boxes, we take a row of `k` items (the "stars") and separate them by
inserting `n-1` dividers (the "bars").  For example, the pattern `*|||**|*|` exhibits 4 items
distributed into 6 boxes -- note that any box, including the first and last, may be empty.
Such arrangements of `k` stars and `n-1` bars are in 1-1 correspondence with multisets of size `k`
over an alphabet of size `n`, and are counted by `choose (n + k - 1) k`.

Note that this problem is one component of Gian-Carlo Rota's "Twelvefold Way"
https://en.wikipedia.org/wiki/Twelvefold_way

## Formal statement

Here we generalise the alphabet to an arbitrary fintype `α`, and we use `sym α k` as the type of
multisets of size `k` over `α`. Thus the statement that these are counted by `multichoose` is:
`sym.card_sym_eq_multichoose : card (sym α k) = multichoose (card α) k`
while the "stars and bars" technique gives
`sym.card_sym_eq_choose : card (sym α k) = choose (card α + k - 1) k`

## Tags

stars and bars, multichoose
-/

open finset fintype function sum nat

variables {α β : Type*}

namespace sym

section sym
variables (α) (n : ℕ)

/--
Over `fin n+1`, the multisets of size `k+1` containing `0` are equivalent to those of size `k`,
as demonstrated by respectively erasing or appending `0`.
-/
protected def E1 {n k : ℕ} :
  {s : sym (fin n.succ) k.succ // ↑0 ∈ s} ≃ sym (fin n.succ) k :=
{ to_fun    := λ s, s.1.erase 0 s.2,
  inv_fun   := λ s, ⟨cons 0 s, mem_cons_self 0 s⟩,
  left_inv  := λ s, by simp,
  right_inv := λ s, by simp }

/--
The multisets of size `k` over `fin n+2` not containing `0`
are equivalent to those of size `k` over `fin n+1`,
as demonstrated by respectively decrementing or incrementing every element of the multiset.
-/
protected def E2 {n k : ℕ} :
  {s : sym (fin n.succ.succ) k // ↑0 ∉ s} ≃ sym (fin n.succ) k :=
{ to_fun    := λ s, map (fin.pred_above 0) s.1,
  inv_fun   := λ s, ⟨map (fin.succ_above 0) s,
    (mt mem_map.1) (not_exists.2 (λ t, (not_and.2 (λ _, (fin.succ_above_ne _ t)))))⟩,
  left_inv  := λ s, by
  { obtain ⟨s, hs⟩ := s,
    simp only [map_map, comp_app],
    nth_rewrite_rhs 0 ←(map_id' s),
    refine sym.map_congr (λ v hv,  _),
    simp [fin.pred_above_zero (ne_of_mem_of_not_mem hv hs)] },
  right_inv := λ s, by
  { simp only [fin.zero_succ_above, map_map, comp_app],
    nth_rewrite_rhs 0 ←(map_id' s),
    refine sym.map_congr (λ v hv,  _),
    rw [←fin.zero_succ_above v, ←@fin.cast_succ_zero n.succ, fin.pred_above_succ_above 0 v] } }

lemma card_sym_fin_eq_multichoose (n k : ℕ) : card (sym (fin n) k) = multichoose n k :=
begin
  apply @pincer_recursion (λ n k, card (sym (fin n) k) = multichoose n k),
  { simp },
  { intros b,
    induction b with b IHb, { simp },
    rw [multichoose_zero_succ, card_eq_zero_iff],
    apply_instance },
  { intros x y h1 h2,
    rw [multichoose_succ_succ, ←h1, ←h2, add_comm],
    cases x,
    { simp only [card_eq_zero_iff, card_unique, self_eq_add_right],
      apply_instance },
    rw ←card_sum,
    refine fintype.card_congr (equiv.symm _),
    apply (equiv.sum_congr sym.E1.symm sym.E2.symm).trans,
    apply equiv.sum_compl },
end

/-- For any fintype `α` of cardinality `n`, `card (sym α k) = multichoose (card α) k` -/
lemma card_sym_eq_multichoose (α : Type*) (k : ℕ) [fintype α] [fintype (sym α k)] :
  card (sym α k) = multichoose (card α) k :=
by { rw ←card_sym_fin_eq_multichoose, exact card_congr (equiv_congr (equiv_fin α)) }

/-- The *stars and bars* lemma: the cardinality of `sym α k` is equal to
`nat.choose (card α + k - 1) k`. -/
lemma card_sym_eq_choose {α : Type*} [fintype α] (k : ℕ) [fintype (sym α k)] :
  card (sym α k) = (card α + k - 1).choose k :=
by rw [card_sym_eq_multichoose, nat.multichoose_eq]

end sym
end sym

namespace sym2
variables [decidable_eq α]

/-- The `diag` of `s : finset α` is sent on a finset of `sym2 α` of card `s.card`. -/
lemma card_image_diag (s : finset α) : (s.diag.image quotient.mk).card = s.card :=
begin
  rw [card_image_of_inj_on, diag_card],
  rintro ⟨x₀, x₁⟩ hx _ _ h,
  cases quotient.eq.1 h,
  { refl },
  { simp only [mem_coe, mem_diag] at hx,
    rw hx.2 }
end

lemma two_mul_card_image_off_diag (s : finset α) :
  2 * (s.off_diag.image quotient.mk).card = s.off_diag.card :=
begin
  rw [card_eq_sum_card_fiberwise
    (λ x, mem_image_of_mem _ : ∀ x ∈ s.off_diag, quotient.mk x ∈ s.off_diag.image quotient.mk),
    sum_const_nat (quotient.ind _), mul_comm],
  rintro ⟨x, y⟩ hxy,
  simp_rw [mem_image, exists_prop, mem_off_diag, quotient.eq] at hxy,
  obtain ⟨a, ⟨ha₁, ha₂, ha⟩, h⟩ := hxy,
  obtain ⟨hx, hy, hxy⟩ : x ∈ s ∧ y ∈ s ∧ x ≠ y,
  { cases h; have := ha.symm; exact ⟨‹_›, ‹_›, ‹_›⟩ },
  have hxy' : y ≠ x := hxy.symm,
  have : s.off_diag.filter (λ z, ⟦z⟧ = ⟦(x, y)⟧) = ({(x, y), (y, x)} : finset _),
  { ext ⟨x₁, y₁⟩,
    rw [mem_filter, mem_insert, mem_singleton, sym2.eq_iff, prod.mk.inj_iff, prod.mk.inj_iff,
      and_iff_right_iff_imp],
    rintro (⟨rfl, rfl⟩ | ⟨rfl, rfl⟩); rw mem_off_diag; exact ⟨‹_›, ‹_›, ‹_›⟩ }, -- hxy' is used here
  rw [this, card_insert_of_not_mem, card_singleton],
  simp only [not_and, prod.mk.inj_iff, mem_singleton],
  exact λ _, hxy',
end

/-- The `off_diag` of `s : finset α` is sent on a finset of `sym2 α` of card `s.off_diag.card / 2`.
This is because every element `⟦(x, y)⟧` of `sym2 α` not on the diagonal comes from exactly two
pairs: `(x, y)` and `(y, x)`. -/
lemma card_image_off_diag (s : finset α) :
  (s.off_diag.image quotient.mk).card = s.card.choose 2 :=
by rw [nat.choose_two_right, mul_tsub, mul_one, ←off_diag_card,
  nat.div_eq_of_eq_mul_right zero_lt_two (two_mul_card_image_off_diag s).symm]

lemma card_subtype_diag [fintype α] :
  card {a : sym2 α // a.is_diag} = card α :=
begin
  convert card_image_diag (univ : finset α),
  rw [fintype.card_of_subtype, ←filter_image_quotient_mk_is_diag],
  rintro x,
  rw [mem_filter, univ_product_univ, mem_image],
  obtain ⟨a, ha⟩ := quotient.exists_rep x,
  exact and_iff_right ⟨a, mem_univ _, ha⟩,
end

lemma card_subtype_not_diag [fintype α] :
  card {a : sym2 α // ¬a.is_diag} = (card α).choose 2 :=
begin
  convert card_image_off_diag (univ : finset α),
  rw [fintype.card_of_subtype, ←filter_image_quotient_mk_not_is_diag],
  rintro x,
  rw [mem_filter, univ_product_univ, mem_image],
  obtain ⟨a, ha⟩ := quotient.exists_rep x,
  exact and_iff_right ⟨a, mem_univ _, ha⟩,
end

/-- Finset **stars and bars** for the case `n = 2`. -/
lemma _root_.finset.card_sym2 (s : finset α) : s.sym2.card = s.card * (s.card + 1) / 2 :=
begin
  rw [←image_diag_union_image_off_diag, card_union_eq, sym2.card_image_diag,
    sym2.card_image_off_diag, nat.choose_two_right, add_comm, ←nat.triangle_succ, nat.succ_sub_one,
    mul_comm],
  rw disjoint_left,
  rintro m ha hb,
  rw [mem_image] at ha hb,
  obtain ⟨⟨a, ha, rfl⟩, ⟨b, hb, hab⟩⟩ := ⟨ha, hb⟩,
  refine not_is_diag_mk_of_mem_off_diag hb _,
  rw hab,
  exact is_diag_mk_of_mem_diag ha,
end

/-- Type **stars and bars** for the case `n = 2`. -/
protected lemma card [fintype α] : card (sym2 α) = card α * (card α + 1) / 2 := finset.card_sym2 _

end sym2


import pseudo_normed_group.basic
import analysis.normed_space.basic
/-!

# A seminormed group is pseudo-normed

This file contains the construction of a pseudo-normed group from a seminormed group.

-/
open_locale nnreal

namespace seminormed_add_comm_group

instance (V : Type*) [seminormed_add_comm_group V] : pseudo_normed_group V :=
{ filtration := λ c, {v | ∥v∥₊ ≤ c},
  filtration_mono := λ c₁ c₂ h v (hv : ∥v∥ ≤ c₁), le_trans hv h,
  zero_mem_filtration := λ c, by simp only [set.mem_set_of_eq, nnnorm_zero, zero_le],
  neg_mem_filtration := λ c v hv, by simpa only [set.mem_set_of_eq, nnnorm_neg] using hv,
  add_mem_filtration := λ c₁ c₂ v₁ v₂ hv₁ hv₂,
    calc ∥v₁ + v₂∥
        ≤ ∥v₁∥ + ∥v₂∥ : norm_add_le _ _
    ... ≤ c₁ + c₂ : add_le_add hv₁ hv₂ }

variables {V : Type*} [seminormed_add_comm_group V]

open pseudo_normed_group

lemma mem_filtration_nnnorm (v : V) : v ∈ filtration V (∥v∥₊) :=
show ∥v∥₊ ≤ ∥v∥₊, from le_rfl

@[simp] lemma mem_filtration_iff (v : V) (c : ℝ≥0) :
  v ∈ filtration V c ↔ ∥v∥₊ ≤ c := iff.rfl

end seminormed_add_comm_group


import tactic.ring
-- Lemma 1.52, 2022-10-02 version, "Automatic complexity: a measure of irregularity"
lemma lemma_1_52 (r x y:ℤ)(hr:0<r)(hy:0 ≤ y)(hx:0 ≤ x) :
  2 * r^2 = (x+y)*r+(y+1) ↔ (x,y) = (r,r-1) :=
  -- General facts:
  have hyy: 0 < y+1, from calc
            0 ≤ y: hy
          ... < y+1: lt_add_one y,
  have hle: 0 ≤ y+1, from le_of_lt hyy,

  have hrn: r ≠ 0, from norm_num.ne_zero_of_pos r hr,
  have hlt1: 0 < r+1, from calc
        0 < r: hr
      ... < r+1: lt_add_one r,
  have hltr: 0 ≤ r, from le_of_lt hr,
  have hr1: 0 ≤ r+1, from le_of_lt hlt1,

  have h5:y ≤ x + y, from calc
          y = 0+y: by ring
        ... ≤ x+y: add_le_add hx (le_refl y),
  have hzxy: 0 ≤ x+y, from calc
             0 ≤ y: hy
            ...≤ x+y:h5,
  have h6:y*r ≤ (x+y)*r, from mul_le_mul h5 (le_refl r) hltr hzxy,
  -- Easy direction, just plugging in:
  have h2: (x,y) = (r,r-1) → 2* r^2 = (x+y)*r+(y+1) , from λ h,
    have hx: x=r,   from congr_arg (λ p:ℤ×ℤ, p.1) h,
    have hy: y=r-1, from congr_arg (λ p:ℤ×ℤ, p.2) h,
    calc 2*r^2 = (r+(r-1)) * r + ((r-1)+1): by ring
           ... = (x+y)     * r + (y+1): by {rw hx,rw hy},
  -- Hard direction:
  have h1: 2 * r^2 = (x+y)*r+(y+1) → (x,y) = (r,r-1), from
    λ h:2 * r^2 = (x+y)*r+(y+1),
    have hz3: 0  = y+1 - r * ((y+1)/r), from calc
              0 = ((2*r)*r) % r:(int.mul_mod_left (2*r) r).symm
            ... = (2 * r^2) % r:by ring_nf
            ... = ((x+y)*r+(y+1)) % r: by rw h
            ... = ((y+1)+(x+y)*r) % r: by ring_nf
            ... = (y+1)%r: int.add_mul_mod_self
            ... = y+1 - r * ((y+1)/r): int.mod_def (y+1) r,
    have hy1: y+1 = r * ((y+1)/r), from calc
              y+1 = (y+1-r*((y+1)/r)) + r*((y+1)/r): by ring
              ... = 0 + r*((y+1)/r): by rw hz3
              ... = r*((y+1)/r): by ring,
    have r ∣ (y+1), from exists.intro ((y+1)/r) hy1, -- note it's \ mid not just |
    have hQ: 0 < (y+1)/r, from int.div_pos_of_pos_of_dvd hyy hltr this,
    have h_Q:0 ≤ (y+1)/r, from le_of_lt hQ,
    have hone: 1 ≤ (y+1)/r, from int.add_one_le_of_lt hQ,
    have 1 = (y+1)/r ∨ 2 ≤ (y+1)/r, from eq_or_lt_of_le hone,
    -- First OR statement
    have r =  y+1    ∨ r*2 ≤ y+1, from or.elim this (
      λ h11: 1 = (y+1)/r,
      or.inl (calc r = r *     1    : by ring
                 ... = r * ((y+1)/r): congr_arg _ h11
                 ... = y+1: hy1.symm)) (
      λ h2: 2 ≤ (y+1)/r,
      or.inr (calc r*2 = 2*r: by ring
                  ...  ≤ ((y+1)/r) * r: mul_le_mul h2 (le_refl r) hltr h_Q
                  ...  = r * ((y+1)/r): by ring
                  ...  = y+1: hy1.symm)
    ),
    -- Second OR statement
    have y = r-1 ∨ 2*r-1 ≤ y, from or.elim this (
      λ H: r = y+1,   or.inl (calc y = (y+1)-1: by ring
                              ...    = r-1: by rw H)) (
      λ H: r*2 ≤ y+1, or.inr (calc
        2*r-1 = (r*2) + (-1): by ring
        ...   ≤ (y+1) + (-1): int.add_le_add_right H (-1)
        ...   = y: by ring)
    ), -- Now finish the proof
    or.elim this (-- Easy case
      λ hyr : y = r - 1,
      have hurr:  r*r = x*r, from calc
                  r*r = (2*r^2) - r*r : by ring
                  ... = x*r : by {rw h,rw hyr,ring,},
      have hrx: r = x, from (mul_left_inj' hrn).mp hurr,
      congr_arg2 (λ (x y : ℤ), (x, y)) hrx.symm hyr
    )
    (λ hry:2*r-1≤ y,-- Contradiction case
      have hok:(2*r-1)* (r+1) ≤ y*(r+1), from mul_le_mul hry (le_refl (r+1)) hr1 hy,
      have  2 * r^2 < 2 * r^2, from calc
            2 * r^2 < 2 * r^2 + r: lt_add_of_pos_right (2 * r^2) hr
                ... = (2*r-1)* (r+1)+1: by ring
                ... ≤ y      * (r+1)+1:  add_le_add hok (le_refl 1)
                ... = y  * r + (y + 1): by ring
                ... ≤ (x+y)*r+(y+1): add_le_add h6 (le_refl (y+1))
                ... = 2 * r^2: h.symm,
    false.elim ((lt_self_iff_false (2*r^2)).mp this)),
iff.intro h1 h2


import .formula

def qfree : formula → Prop
| formula.true := true
| formula.false := true
| (formula.atom _) := true
| (formula.not p) := qfree p
| (formula.or  p q) := qfree p /\ qfree q
| (formula.and p q) := qfree p /\ qfree q
| (formula.ex p) := false

def nqfree : formula → Prop
| ⊤' := true
| ⊥' := true
| A' a := true
| (¬' p) := false
| (p ∨' q) := nqfree p ∧ nqfree q
| (p ∧' q) := nqfree p ∧ nqfree q
| (∃' p) := false

lemma qfree_not_o {p : formula} : qfree p → qfree (not_o p) :=
begin intro h, cases p; try {trivial <|> assumption} end

lemma qfree_and_o {p q : formula} : qfree p → qfree q → qfree (and_o p q) :=
begin
  intros hp hq, apply cases_and_o;
  try { trivial <|> assumption }, apply and.intro hp hq
end

lemma qfree_or_o {p q : formula} : qfree p → qfree q → qfree (or_o p q) :=
begin
  intros hp hq, apply cases_or_o;
  try { trivial <|> assumption }, apply and.intro hp hq
end

lemma qfree_disj :
  ∀ {ps : list (formula)}, (∀ p ∈ ps, qfree p) → qfree (disj ps)
| [] _ := trivial
| (p::ps) h :=
  have hp : qfree p := h _ (or.inl rfl),
  have hps : qfree (disj ps) :=
    qfree_disj (list.forall_mem_of_forall_mem_cons h),
  begin
    simp [disj], apply cases_or_o;
    try {simp [qfree], constructor};
    {trivial <|> assumption}
  end

lemma qfree_conj :
∀ {ps : list (formula)}, (∀ p ∈ ps, qfree p) → qfree (conj ps)
| [] _ := trivial
| (p::ps) h :=
  have hp : qfree p := h _ (or.inl rfl),
  have hps : qfree (conj ps) :=
    qfree_conj (list.forall_mem_of_forall_mem_cons h),
  begin
    simp [conj], apply cases_and_o;
    try {simp [qfree], constructor};
    try {trivial <|> assumption},
  end

lemma qfree_conj_atom :
∀ {as : list (atom)}, qfree (conj_atom as)
| [] := trivial
| (a::as) := qfree_and_o trivial qfree_conj_atom

lemma qfree_of_nqfree : ∀ {p : formula}, nqfree p → qfree p
| ⊤' h := trivial
| ⊥' h := trivial
| (A' a) h := trivial
| (¬' p) h := by cases h
| (p ∨' q) h :=
  begin
    cases h with hp hq, apply and.intro;
    apply qfree_of_nqfree; assumption
  end
| (p ∧' q) h :=
  begin
    cases h with hp hq, apply and.intro;
    apply qfree_of_nqfree; assumption
  end
| (∃' p) h := by cases h


/-
Copyright (c) 2022 Chris Birkbeck. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Chris Birkbeck
-/
import analysis.complex.cauchy_integral
import analysis.analytic.basic
import analysis.calculus.parametric_interval_integral
import data.complex.basic
import measure_theory.integral.circle_integral
/-!
# Circle integral transform
In this file we define the circle integral transform of a function `f` with complex domain. This is
defined as $(2πi)^{-1}\frac{f(x)}{x-w}$ where `x` moves along a circle. We then prove some basic
facts about these functions.
These results are useful for proving that the uniform limit of a sequence of holomorphic functions
is holomorphic.
-/

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

noncomputable theory

variables {E : Type} [normed_add_comm_group E] [normed_space ℂ E] (R : ℝ) (z w : ℂ)

namespace complex

/-- Given a function `f : ℂ → E`, `circle_transform R z w f` is the functions mapping `θ` to
`(2 * ↑π * I)⁻¹ • deriv (circle_map z R) θ • ((circle_map z R θ) - w)⁻¹ • f (circle_map z R θ)`.
If `f` is differentiable and `w` is in the interior of the ball, then the integral from `0` to
`2 * π` of this gives the value `f(w)`. -/
def circle_transform (f : ℂ → E) (θ : ℝ) : E :=
(2 * ↑π * I)⁻¹ • deriv (circle_map z R) θ • ((circle_map z R θ) - w)⁻¹ • f (circle_map z R θ)

/-- The derivative of `circle_transform` w.r.t `w`.-/
def circle_transform_deriv (f : ℂ → E) (θ : ℝ) : E :=
(2 * ↑π * I)⁻¹ • deriv (circle_map z R) θ • ((circle_map z R θ - w) ^ 2)⁻¹ • f (circle_map z R θ)

lemma circle_transform_deriv_periodic (f : ℂ → E) :
  periodic (circle_transform_deriv R z w f) (2 * π) :=
begin
  have := periodic_circle_map,
  simp_rw periodic at *,
  intro x,
  simp_rw [circle_transform_deriv, this],
  congr' 2,
  simp [this],
end

lemma circle_transform_deriv_eq (f : ℂ → E) :
  circle_transform_deriv R z w f =
  (λ θ, (circle_map z R θ - w)⁻¹ • (circle_transform R z w f θ)) :=
begin
  ext,
  simp_rw [circle_transform_deriv, circle_transform, ←mul_smul, ←mul_assoc],
  ring_nf,
  rw inv_pow,
  congr,
  ring,
end

lemma integral_circle_transform [complete_space E] (f : ℂ → E) :
  ∫ (θ : ℝ) in 0..2 * π, circle_transform R z w f θ =
  (2 * ↑π * I)⁻¹ • ∮ z in C(z, R), (z - w)⁻¹ • f z :=
begin
  simp_rw [circle_transform, circle_integral, deriv_circle_map, circle_map],
  simp,
end

lemma continuous_circle_transform {R : ℝ} (hR : 0 < R) {f : ℂ → E} {z w : ℂ}
  (hf : continuous_on f $ sphere z R) (hw : w ∈ ball z R) :
  continuous (circle_transform R z w f) :=
begin
  apply_rules [continuous.smul, continuous_const],
  simp_rw deriv_circle_map,
  apply_rules [continuous.mul, (continuous_circle_map 0 R), continuous_const],
  { apply continuous_circle_map_inv hw },
  { apply continuous_on.comp_continuous hf (continuous_circle_map z R),
    exact (λ _, (circle_map_mem_sphere _ hR.le) _) },
end

lemma continuous_circle_transform_deriv {R : ℝ} (hR : 0 < R) {f : ℂ → E} {z w : ℂ}
  (hf : continuous_on f (sphere z R)) (hw : w ∈ ball z R) :
  continuous (circle_transform_deriv R z w f) :=
begin
  rw circle_transform_deriv_eq,
  exact (continuous_circle_map_inv hw).smul (continuous_circle_transform hR hf hw),
end

/--A useful bound for circle integrals (with complex codomain)-/
def circle_transform_bounding_function (R : ℝ) (z : ℂ) (w : ℂ × ℝ) : ℂ :=
circle_transform_deriv R z w.1 (λ x, 1) w.2

lemma continuous_on_prod_circle_transform_function {R r : ℝ} (hr : r < R) {z : ℂ} :
 continuous_on (λ (w : ℂ × ℝ), ((circle_map z R w.snd - w.fst)⁻¹) ^ 2)
  ((closed_ball z r) ×ˢ (⊤ : set ℝ)) :=
begin
  simp_rw ←one_div,
  apply_rules [continuous_on.pow, continuous_on.div, continuous_on_const],
  refine ((continuous_circle_map z R).continuous_on.comp continuous_on_snd (λ _, and.right)).sub
    (continuous_on_id.comp continuous_on_fst (λ _, and.left)),
  simp only [mem_prod, ne.def, and_imp, prod.forall],
  intros a b ha hb,
  have ha2 : a ∈ ball z R, by {simp at *, linarith,},
  exact (sub_ne_zero.2 (circle_map_ne_mem_ball ha2 b)),
end

lemma continuous_on_abs_circle_transform_bounding_function {R r : ℝ} (hr : r < R) (z : ℂ) :
  continuous_on (abs ∘ (λ t, circle_transform_bounding_function R z t))
  ((closed_ball z r) ×ˢ (⊤ : set ℝ) : set $ ℂ × ℝ) :=
begin
  have : continuous_on (circle_transform_bounding_function R z) (closed_ball z r ×ˢ (⊤ : set ℝ)),
  { apply_rules [continuous_on.smul, continuous_on_const],
    simp only [deriv_circle_map],
    have c := (continuous_circle_map 0 R).continuous_on,
    apply_rules [continuous_on.mul, c.comp continuous_on_snd (λ _, and.right), continuous_on_const],
    simp_rw ←inv_pow,
    apply continuous_on_prod_circle_transform_function hr, },
  refine continuous_abs.continuous_on.comp this _,
  show maps_to _ _ (⊤ : set ℂ),
  simp [maps_to],
end

lemma abs_circle_transform_bounding_function_le {R r : ℝ} (hr : r < R) (hr' : 0 ≤ r) (z : ℂ) :
  ∃ (x : ((closed_ball z r) ×ˢ [0, 2 * π] : set $ ℂ × ℝ)),
  ∀ (y : ((closed_ball z r) ×ˢ [0, 2 * π] : set $ ℂ × ℝ)),
  abs (circle_transform_bounding_function R z y) ≤ abs (circle_transform_bounding_function R z x) :=
begin
  have cts := continuous_on_abs_circle_transform_bounding_function hr z,
  have comp : is_compact (((closed_ball z r) ×ˢ [0, 2 * π]) : set (ℂ × ℝ)),
  { apply_rules [is_compact.prod, proper_space.is_compact_closed_ball z r, is_compact_uIcc], },
  have none := (nonempty_closed_ball.2 hr').prod (nonempty_uIcc),
  simpa using is_compact.exists_forall_ge comp none (cts.mono (by { intro z, simp, tauto })),
end

/-- The derivative of a `circle_transform` is locally bounded. -/
lemma circle_transform_deriv_bound {R : ℝ} (hR : 0 < R) {z x : ℂ} {f : ℂ → ℂ}
  (hx : x ∈ ball z R) (hf : continuous_on f (sphere z R)) :
  ∃ (B ε : ℝ), 0 < ε ∧ ball x ε ⊆ ball z R ∧
  (∀ (t : ℝ) (y ∈ ball x ε), complex.abs (circle_transform_deriv R z y f t) ≤ B) :=
begin
  obtain ⟨r, hr, hrx⟩ := exists_lt_mem_ball_of_mem_ball hx,
  obtain ⟨ε', hε', H⟩ := exists_ball_subset_ball hrx,
  obtain ⟨⟨⟨a, b⟩, ⟨ha, hb⟩⟩, hab⟩ := abs_circle_transform_bounding_function_le hr
    (pos_of_mem_ball hrx).le z,
  let V : ℝ → (ℂ → ℂ) := λ θ w, circle_transform_deriv R z w (λ x, 1) θ,
  have funccomp : continuous_on (λ r , abs (f r)) (sphere z R),
  by { have cabs : continuous_on abs ⊤ := by apply continuous_abs.continuous_on,
    apply cabs.comp (hf), rw maps_to, tauto,},
  have sbou := is_compact.exists_forall_ge (is_compact_sphere z R)
    (normed_space.sphere_nonempty.2 hR.le) funccomp,
  obtain ⟨X, HX, HX2⟩ := sbou,
  refine ⟨abs (V b a) * abs (f X), ε' , hε', subset.trans H (ball_subset_ball hr.le), _ ⟩,
  intros y v hv,
  obtain ⟨y1, hy1, hfun⟩ := periodic.exists_mem_Ico₀
    (circle_transform_deriv_periodic R z v f) real.two_pi_pos y,
  have hy2: y1 ∈ [0, 2*π], by {convert (Ico_subset_Icc_self hy1),
    simp [real.two_pi_pos.le]},
  have := mul_le_mul (hab ⟨⟨v, y1⟩, ⟨ball_subset_closed_ball (H hv), hy2⟩⟩)
   (HX2 (circle_map z R y1) (circle_map_mem_sphere z hR.le y1))
   (complex.abs.nonneg _) (complex.abs.nonneg _),
  simp_rw hfun,
  simp only [circle_transform_bounding_function, circle_transform_deriv, V, norm_eq_abs,
    algebra.id.smul_eq_mul, deriv_circle_map, map_mul, abs_circle_map_zero, abs_I, mul_one,
    ←mul_assoc, mul_inv_rev, inv_I, abs_neg, abs_inv, abs_of_real, one_mul, abs_two, abs_pow,
    mem_ball, gt_iff_lt, subtype.coe_mk, set_coe.forall, mem_prod, mem_closed_ball, and_imp,
    prod.forall, normed_space.sphere_nonempty, mem_sphere_iff_norm] at *,
  exact this,
end

/--Cauchy integral form of a function at `z` in a disk of radius `R`-/
def circle_integral_form [complete_space E] (R : ℝ) (z : ℂ) (f : ℂ → E): (ℂ → E) :=
λ w, (2 * π * I : ℂ)⁻¹ • (∮ z in C(z, R), (z - w)⁻¹ • f z)

lemma circle_intgral_form_eq_int [complete_space E] (R : ℝ) (z : ℂ) (f : ℂ → E) :
  circle_integral_form R z f = λ w, ∫ (θ : ℝ) in 0..2 * π, (circle_transform R z w f) θ :=
by simp_rw [circle_transform, circle_integral_form, circle_integral,
  interval_integral.integral_smul]

lemma circle_transform_circle_int [complete_space E] (R : ℝ) (z w : ℂ) (f : ℂ → E) :
  ∫ (θ : ℝ) in 0..2 * π, circle_transform R z w f θ =
  (2 * π * I : ℂ)⁻¹ • ∮ z in C(z, R), (z - w)⁻¹ • f z :=
begin
  simp_rw [circle_transform, circle_integral,deriv_circle_map, circle_map],
  simp only [real_smul, nsmul_eq_mul, nat.cast_bit0, nat.cast_one, one_div,
    interval_integral.integral_smul, zero_add],
end

lemma circle_transform_has_deriv_at (R : ℝ) (z : ℂ) (f : ℂ → ℂ) :
  ∀ (t : ℝ) y ∈ ball z R, has_deriv_at (λ y, (circle_transform R z y f) t)
  ((circle_transform_deriv R z y f) t) y :=
begin
  intros y x hx,
  simp only [circle_transform, circle_transform_deriv, algebra.id.smul_eq_mul,
   ←mul_assoc, deriv_circle_map],
  apply_rules [has_deriv_at.mul_const, has_deriv_at.const_mul],
  have H : has_deriv_at (λ (y_1 : ℂ), (circle_map z R y - y_1)) (-1 ) x,
   by {apply has_deriv_at.const_sub, apply has_deriv_at_id,},
  have hfin := has_deriv_at.inv H (sub_ne_zero.2 (circle_map_ne_mem_ball hx y)),
  simp only [one_div, neg_neg] at hfin,
  exact hfin,
end

lemma circle_transform_ae_measurable {R : ℝ} {f : ℂ → ℂ} (hR : 0 < R)
  (z x : ℂ) (hx : x ∈ ball z R) (hf : continuous_on f (sphere z R)) :
  ∀ᶠ y in 𝓝 x, ae_measurable (( λ w, (λ θ, (circle_transform R z w f θ))) y)
  (volume.restrict (Ι 0 (2 * π))):=
begin
  rw filter.eventually_iff_exists_mem,
  obtain ⟨ε', He, HB⟩ := (exists_ball_subset_ball hx),
  refine  ⟨(ball x ε'), _⟩,
  simp only [metric.ball_mem_nhds x He, exists_true_left],
  intros y hy,
  exact continuous_on.ae_measurable ((continuous_circle_transform hR hf (HB hy))).continuous_on
    (measurable_set_Ioc),
end

lemma circle_interval_integrable {R : ℝ} {f : ℂ → ℂ} (hR : 0 < R)
  (z x : ℂ) (hx : x ∈ ball z R) (hf : continuous_on f (sphere z R)) :
  interval_integrable ((λ w, (λ θ, (circle_transform R z w f θ))) x) volume 0 (2 * π) :=
(continuous_on.interval_integrable) (continuous_circle_transform hR hf hx).continuous_on

lemma circle_transform_deriv_ae_measurable {R : ℝ} (hR : 0 < R)
  (z x : ℂ) (hx : x ∈ ball z R) (f : ℂ → ℂ) (hf : continuous_on f (sphere z R)) :
   ae_measurable (( λ w θ, (circle_transform_deriv R z w f θ)) x)
  (volume.restrict (Ι 0 (2 * π))) :=
continuous_on.ae_measurable ((continuous_circle_transform_deriv hR hf (hx))).continuous_on
     (measurable_set_Ioc)

/--The `circle_integral_form` of a function, which is continuous on `sphere z R` is differentiable
on `ball z R`. -/
lemma circle_integral_form_differentiable_on {R : ℝ} {f : ℂ → ℂ} (hR : 0 < R) (z : ℂ)
  (hf : continuous_on f (sphere z R)) :
  differentiable_on ℂ (circle_integral_form R z f) (ball z R) :=
begin
  simp_rw [circle_integral_form, ←circle_transform_circle_int R z _ f,
    differentiable_on, differentiable_within_at],
  intros x hx,
  have h4R : 0 < (4⁻¹*R), by {apply left.mul_pos, rw inv_pos, linarith, apply hR,},
  set F : ℂ → ℝ → ℂ := λ w, (λ θ, (circle_transform R z w f θ)),
  set F' : ℂ → ℝ → ℂ := λ w, circle_transform_deriv R z w f,
  have hF_meas : ∀ᶠ y in 𝓝 x, ae_strongly_measurable (F y) (volume.restrict (Ι 0 (2 * π))),
  by {simp_rw [F, _root_.ae_strongly_measurable_iff_ae_measurable],
    exact circle_transform_ae_measurable hR z x hx hf},
  have hF_int : interval_integrable (F x) volume 0 (2 * π),
  by {simp_rw F,
    apply circle_interval_integrable hR z x hx hf},
  have hF'_meas : ae_strongly_measurable (F' x) (volume.restrict (Ι 0 (2 * π))),
  by {simp_rw [F', _root_.ae_strongly_measurable_iff_ae_measurable],
    exact circle_transform_deriv_ae_measurable hR z x hx f hf},
  have BOU := circle_transform_deriv_bound hR hx hf,
  obtain ⟨bound, ε, hε ,h_ball, h_boun⟩ := BOU,
  have h_bound : ∀ᵐ t ∂volume, t ∈ Ι 0 (2 * π) → ∀ y ∈ ball x ε , complex.abs(F' y t) ≤ bound,
  by {apply eventually_of_forall,
    exact (λ _,(λ _, by {apply h_boun})) },
  have bound_integrable : interval_integrable (λ _, bound) volume 0 (2 * π),
  by {exact _root_.interval_integrable_const, },
  have h_diff : ∀ᵐ t ∂volume, t ∈ Ι 0 (2 * π) → ∀ y ∈ ball x ε,
  has_deriv_at (λ y, F y t) (F' y t) y,
  by {simp_rw [F, F', circle_transform, circle_transform_deriv],
    have := circle_transform_has_deriv_at R z f,
    apply eventually_of_forall,
    simp_rw [circle_transform, circle_transform_deriv] at this,
    intros y hy x hx,
    simp  [real.two_pi_pos.le] at hy,
    have hy2 : y ∈ [0, 2*π], by {convert (Ioc_subset_Icc_self hy),
      simp [ real.two_pi_pos.le]},
    exact this y x (h_ball hx)},
  have := interval_integral.has_deriv_at_integral_of_dominated_loc_of_deriv_le hε hF_meas hF_int
    hF'_meas h_bound bound_integrable h_diff,
  simp only [F, has_deriv_at, has_deriv_at_filter, has_fderiv_within_at, mem_ball, zero_lt_mul_left,
    inv_pos, zero_lt_bit0, zero_lt_one, norm_eq_abs,
    interval_integral.interval_integrable_const] at *,
  exact ⟨continuous_linear_map.smul_right (1 : ℂ →L[ℂ] ℂ)
    (interval_integral (F' x) 0 (2 * π) volume), (has_fderiv_at_filter.mono this.2 inf_le_left)⟩,
end

/--The differece of the `circle_transform` of two functions `f,g` is the `circle_transform` of the
difference `f - g`. -/
lemma circle_transform_sub (R : ℝ) (f g : ℂ → ℂ) (z w : ℂ) (θ : ℝ) :
  ((circle_transform R z w f ) θ) - ((circle_transform R z w g) θ) =
  (circle_transform R z w (f - g) θ) :=
begin
  simp only [circle_transform, mul_inv_rev, inv_I, neg_mul, deriv_circle_map,
    algebra.id.smul_eq_mul, neg_sub_neg, pi.sub_apply],
  ring,
end

lemma circle_transform_of_bound_is_bound {R : ℝ} (hR : 0 < R) (f : ℂ → ℂ) (z w : ℂ) (r : ℝ)
  (h : ∀ (x : sphere z R), (complex.abs (f x) ≤ abs r)) (θ : ℝ) :
  complex.abs (circle_transform R z w f θ) ≤ complex.abs (circle_transform R z w (λ x, r) θ) :=
begin
  simp only [circle_transform, abs_of_real, mul_one, algebra.id.smul_eq_mul, abs_I, abs_two,
    ←mul_assoc, deriv_circle_map, abs_circle_map_zero, mul_inv_rev, inv_I, absolute_value.map_neg,
    absolute_value.map_mul, map_inv₀, one_mul],
  apply_rules [monotone_mul_left_of_nonneg, mul_nonneg, mul_nonneg],
  repeat {simp_rw inv_nonneg},
  swap,
  nlinarith,
  repeat {apply _root_.abs_nonneg},
  { simp only [map_nonneg] },
  { simp only [abs_of_real, set_coe.forall, subtype.coe_mk] at h,
    exact h _ (circle_map_mem_sphere z hR.le θ)},
end

/--The `circle_transform` of a function is integrable. -/
lemma circle_transform_integrable {R : ℝ} {F : ℂ → ℂ} (hR : 0 < R) (z : ℂ)
  (F_cts : continuous_on F (sphere z R)) (w : ball z R) :
  integrable (circle_transform R z w F) (volume.restrict (Ioc 0 (2*π))) :=
begin
  apply integrable_on.integrable,
  rw ←(interval_integrable_iff_integrable_Ioc_of_le real.two_pi_pos.le),
  apply continuous_on.interval_integrable ((continuous_circle_transform hR F_cts
    w.property).continuous_on),
  exact real.locally_finite_volume,
end

/--The (complex) absolute value of the `circle_transform` of a function is integrable. -/
lemma circle_transform_integrable_abs {R : ℝ} {F : ℂ → ℂ} (hR : 0 < R) (z : ℂ)
  (F_cts : continuous_on F (sphere z R)) (w : ball z R) :
  integrable (complex.abs ∘ (circle_transform R z w F)) (volume.restrict (Ioc 0 (2*π))) :=
⟨(circle_transform_integrable hR z F_cts w).ae_strongly_measurable.norm,
    (circle_transform_integrable hR z F_cts w).has_finite_integral.norm⟩

lemma abs_sub_add_cancel_bound (x : ℂ) (r : ℝ)
  (h : ∃ (b : ℂ), complex.abs (x - b) + complex.abs(b) ≤ r) : complex.abs(x) ≤ r :=
begin
  obtain ⟨b, hb⟩ := h,
  rw ←sub_add_cancel x b,
  exact le_trans (abs.add_le (x - b) b) hb,
end

/--The `circle_transform` of a unifom limit of functions `F n` tends to the `circle_transform` of
  the limit function `f`. -/
lemma circle_transform_of_unifom_limit {R : ℝ} {F : ℕ → ℂ → ℂ} (hR : 0 < R) (f : ℂ → ℂ) (z : ℂ)
  (hlim : tendsto_uniformly_on F f filter.at_top (sphere z R)) (w : ball z R) (y : ℝ) :
  tendsto (λ n, ((circle_transform R z w (F n))) y)
  at_top (𝓝 (((circle_transform R z w f )) y)) :=
begin
  rw metric.tendsto_uniformly_on_iff at hlim,
  simp only [metric.tendsto_nhds, dist_comm, circle_transform, one_div,
    algebra.id.smul_eq_mul, gt_iff_lt, mem_closed_ball, nat.cast_bit0, real_smul, ge_iff_le,
    nsmul_eq_mul, nat.cast_one, eventually_at_top] at *,
  intros ε hε,
  set r : ℂ := (2 * π * I : ℂ)⁻¹ * circle_map 0 R y * I * ((circle_map z R y - ↑w)⁻¹),
  have hr : 0 < complex.abs ( r ),
  by {simp only [r, norm_eq_abs, abs_mul, abs_inv, abs_two, abs_of_real, abs_I, mul_one,
    abs_circle_map_zero],
  simp only [absolute_value.map_neg, absolute_value.map_mul, abs_I, map_inv₀, abs_of_real, abs_two,
    one_mul, abs_circle_map_zero, mul_one],
  apply left.mul_pos (left.mul_pos (inv_pos.2 (left.mul_pos (@two_pos ℝ _ _ _ _ _)
    (_root_.abs_pos.2 real.pi_ne_zero)))  (_root_.abs_pos_of_pos hR)) _,
  simp only [inv_pos, absolute_value.pos_iff],
  exact sub_ne_zero.2 (circle_map_ne_mem_ball w.2 y), },
  let e := ( complex.abs(r))⁻¹ * (ε/2),
  have he : 0 < e, by {simp_rw e, apply mul_pos (inv_pos.2 hr) (div_pos hε two_pos) },
  obtain ⟨a, ha⟩ := (hlim e he),
  refine ⟨ a, (λ b hb, _)⟩,
  simp_rw [deriv_circle_map, dist_eq_norm, ← mul_sub] at *,
  have hg : complex.abs((2 * π * I : ℂ)⁻¹ * (circle_map 0 R y * I *
    ((circle_map z R y - ↑w)⁻¹ * (f (circle_map z R y) - F b (circle_map z R y))))) =
    complex.abs( (2 * π * I : ℂ)⁻¹ * circle_map 0 R y * I * ((circle_map z R y - ↑w)⁻¹) ) *
    complex.abs( (f (z + ↑R * exp (↑y * I)) - F b (z + ↑R * exp (↑y * I)))),
  by {simp only [circle_map, abs_of_real, abs_exp_of_real_mul_I, mul_one, abs_I, abs_two,
        norm_eq_abs, mul_inv_rev, inv_I, zero_add, one_mul, absolute_value.map_neg,
        absolute_value.map_mul, map_inv₀],
      ring,},
  simp at *,
  simp_rw [hg],
  have hab := (ha b hb) (z + ↑R * exp (↑y * I)) (circle_map_mem_sphere z hR.le y),
  apply lt_trans (mul_lt_mul_of_pos_left hab hr),
  simp_rw [e,r],
  simp only [mul_inv_rev, absolute_value.map_mul, abs_I, map_inv₀, abs_of_real, abs_two,
  abs_circle_map_zero, mul_one, inv_inv],
  simp_rw [div_eq_inv_mul, ← mul_assoc],
  have := mul_inv_cancel (ne_of_gt hr),
  have hfin : |π|⁻¹ * 2⁻¹ * |R| * (complex.abs (circle_map z R y - ↑w))⁻¹ *
    complex.abs (circle_map z R y - ↑w) *  |R|⁻¹ * 2 * |π| * 2⁻¹ =
    2⁻¹ *  (|π|⁻¹ * 2⁻¹ * |R| * (complex.abs (circle_map z R y - ↑w))⁻¹ *
    (|π|⁻¹ * 2⁻¹ * |R| * (complex.abs (circle_map z R y - ↑w))⁻¹)⁻¹), by {simp only [mul_inv_rev, inv_inv],ring_exp},
  rw [hfin, this],
  simp only [inv_eq_one_div],
  nlinarith,
end

/--A uniform limit of functions on a `sphere` can be eventually bounded by an integrable
function.  -/
lemma circle_transform_of_uniform_exists_bounding_function {R : ℝ} {F : ℕ → ℂ → ℂ} (hR : 0 < R)
  (f : ℂ → ℂ) (z : ℂ) (w : ball z R) (F_cts : ∀ n, continuous_on (F n) (sphere z R))
  (hlim : tendsto_uniformly_on F f filter.at_top (sphere z R) ):
  ∃ (bound : ℝ → ℝ), ((∀ n, ∀ᵐ r ∂(volume.restrict (Ioc 0 (2*π))),
  complex.abs((circle_transform R z w (F n)) r) ≤ bound r)
  ∧ integrable bound (volume.restrict (Ioc 0 (2*π)))) :=
begin
  have f_cont : continuous_on f (sphere z R) ,
  by {apply tendsto_uniformly_on.continuous_on hlim,
      simp only [F_cts, eventually_at_top, implies_true_iff, exists_const],},
  simp only [metric.tendsto_uniformly_on_iff, gt_iff_lt, ge_iff_le, eventually_at_top] at hlim,
  obtain ⟨a, ha⟩ := (hlim 1 (zero_lt_one)),
  set bound : ℝ → ℝ := λ θ, (∑ i in finset.range (a+1),
    complex.abs ((circle_transform R z w (F i)) θ))
    + complex.abs ((circle_transform R z w (λ x, 1)) θ) +
    complex.abs ((circle_transform R z w f) θ),
  refine ⟨bound, (λ n, _), _⟩,
  rw [ae_restrict_iff'],
  apply eventually_of_forall,
  intros y hyl,
  by_cases (n ≤ a),
  simp_rw bound,
  have hnn : n ∈ finset.range(a + 1), by {simp only [finset.mem_range], linarith},
  have := finset.add_sum_erase (finset.range (a + 1))
  (λ i , complex.abs ((circle_transform R z w (F i)) y)) hnn,
  simp only [and_imp, mem_Ioc, finset.mem_range, mem_sphere_iff_norm, norm_eq_abs] at *,
  simp_rw [←this, add_assoc, le_add_iff_nonneg_right],
  apply add_nonneg,
  { apply finset.sum_nonneg,
    intros a b,
    apply absolute_value.nonneg},
  { apply add_nonneg,
    apply absolute_value.nonneg,
    apply absolute_value.nonneg},
  { apply abs_sub_add_cancel_bound ((circle_transform R z w (F n)) y) (bound y),
    refine ⟨circle_transform R z ↑w f y,_⟩,
    simp_rw [circle_transform_sub, bound],
    simp only [add_le_add_iff_right, finset.univ_eq_attach],
    have := circle_transform_of_bound_is_bound hR ((F n) - f) z w 1,
    have haan := ha n (not_le.1 h).le,
    simp only [of_real_one, abs_one, pi.sub_apply] at this,
    simp_rw dist_eq_norm at *,
    simp only [norm_eq_abs] at haan,
    have haf : ∀ (x : sphere z R), abs (F n x - f x) ≤ 1,
    by {intro x, rw absolute_value.map_sub, apply (haan x.1 x.property).le,},
    apply le_add_of_nonneg_of_le,
    apply finset.sum_nonneg,
    intros d dd,
    apply absolute_value.nonneg,
    simp only [absolute_value.map_one] at this,
    apply ((this haf) y)},
  { simp only [measurable_set_Ioc]},
  { simp_rw bound,
   apply_rules [integrable.add, integrable.add, integrable_finset_sum],
   refine (λ _ _, circle_transform_integrable_abs hR z (F_cts _) w),
   apply circle_transform_integrable_abs hR z continuous_const.continuous_on,
   apply circle_transform_integrable_abs hR z f_cont},
end

/--The integral of a uniform limit of functions `F n` tends to the integral of the limit function
`f`. -/
lemma circle_int_uniform_lim_eq_lim_of_int {R : ℝ} {F : ℕ → ℂ → ℂ} (hR : 0 < R) (f : ℂ → ℂ)
  (z : ℂ) (w : ball z R) (F_cts : ∀ n, continuous_on (F n) (sphere z R))
  (hlim : tendsto_uniformly_on F f filter.at_top (sphere z R)) :
  tendsto (λn, ∫ (θ : ℝ) in 0..2 * π, (circle_transform R z w (F n)) θ)
  at_top (𝓝 $ ∫ (θ : ℝ) in 0..2 * π, (circle_transform R z w f ) θ) :=
begin
  have F_measurable : ∀ n,
  ae_strongly_measurable (circle_transform R z w (F n)) (volume.restrict (Ioc 0 (2*π))),
  by {intro n, simp_rw _root_.ae_strongly_measurable_iff_ae_measurable,
    apply (circle_transform_integrable hR z (F_cts n) w).ae_measurable},
  have h_lim'' : ∀ (a : ℝ), tendsto (λ n, ((circle_transform R z w (F n))) a)
  at_top (𝓝 (((circle_transform R z w f)) a)),
  by {apply circle_transform_of_unifom_limit hR f z hlim},
  have h_lim' : ∀ᵐ a ∂(volume.restrict (Ioc 0 (2*π))),
  tendsto (λ n, ((circle_transform R z w (F n))) a)
  at_top (𝓝 (((circle_transform R z w f )) a)),
  by {simp only [h_lim'', eventually_true],},
  have hboundlem := circle_transform_of_uniform_exists_bounding_function hR f z w F_cts hlim,
  obtain ⟨bound, h_bound, bound_integrable⟩ := hboundlem,
  simp_rw integral_of_le (real.two_pi_pos.le),
  exact tendsto_integral_of_dominated_convergence bound F_measurable bound_integrable h_bound
    h_lim',
end

lemma complex_abs_sub_le (a b : ℂ) : complex.abs(- b) =complex.abs(b ) :=
begin
  exact abs.map_neg b,
end

lemma Ineq1 (a b c d e f r : ℂ) (ε : ℝ) (hε : 0 < ε) (h1 : abs (a - b) < 8⁻¹ * abs(r ) * ε)
(h2 : abs (c - d) < 8⁻¹ * abs(r) * ε ) (h3 : (abs r)⁻¹ * abs ((b - d) - (e - f)) < (2/3) * ε) :
(abs r)⁻¹ * abs ((a - b) - (c - d) + (b - d) - (e - f) ) < ε :=
begin
  have h4 : abs (((a - b) - (c - d)) + (b - d) - (e - f) ) ≤ abs ((a - b) - (c - d))
  + abs ((b - d) - (e - f)),
  by {convert ( complex.abs.add_le' ((a - b) - (c - d)) ((b - d) - (e - f))), ring_nf,},
  have h5 : abs (a - b - (c - d)) ≤ abs (a - b) + abs (c - d),
  by {  have := abs.sub_le (a - b) 0 (c - d),
  simp at this,
  rw abs.map_sub c d,
  exact this,},
  have h6 : (abs r)⁻¹ * abs (((a - b) - (c - d)) + (b - d) - (e - f)) ≤
  (abs r)⁻¹ * abs (a - b) + (abs r)⁻¹ * abs (c - d) + (abs r)⁻¹ * abs ((b - d) - (e - f)),
  by {simp_rw ←mul_add, nth_rewrite_lhs 0 mul_comm, nth_rewrite_rhs 0 mul_comm,
  apply mul_le_mul_of_nonneg_right, swap, rw inv_nonneg, simp, simp_rw ←add_assoc,
  apply le_trans h4, simp_rw ← add_assoc, simp only [h5, add_le_add_iff_right],},
  have hr : 0 < abs r,
  by {by_contradiction h,
  simp  at h,
  rw h at h1,
  simp [zero_mul, abs_zero, mul_zero] at h1,
  linarith [ (abs.nonneg (a - b)), h1],},
  have e1 : 8⁻¹ * (abs r) * ε = 8⁻¹ * ε * (abs r), by {ring_nf,},
  rw e1 at *,
  apply lt_trans (lt_of_le_of_lt h6 (add_lt_add (add_lt_add ((inv_mul_lt_iff' hr).mpr h1)
    ((inv_mul_lt_iff' hr).mpr h2)) h3)),
  ring_exp,
  linarith,
end

lemma Ineq2 (a b c d r : ℂ) (ε : ℝ) (hε : 0 < ε )
 (h : ∃ (x y : ℂ), abs (a - y) < 8⁻¹ * abs(r) * ε ∧ abs (b - x) < 8⁻¹ * abs(r) * ε ∧
 (abs r)⁻¹ * abs ((y - x) - (c - d)) < 8⁻¹ * ε) :
 (abs r)⁻¹ * abs ((a - b)- (c - d)) < (2/3) * ε :=
begin
  obtain ⟨x, y, h1, h2, h3⟩ := h,
  have hr : 0 < abs r,
  by {by_contradiction h,
  simp  at h,
  simp [h, zero_mul, abs_zero, mul_zero] at h1,
  linarith [abs.nonneg (a - y), h1] },
  have h33 : (abs r)⁻¹ * abs ((c - d) - (y - x)) < 8⁻¹ * ε,
  by {rw abs.map_sub, apply h3,},
  have h5 : abs ((a - b) - (c - d)) = abs (((a - y) - (b - x))- ((c - d) - (y - x))), by {ring_nf,},
  rw h5,
  have h6 : (abs r)⁻¹ * abs (((a - y) - (b - x))- ((c - d) - (y - x))) ≤ (abs r)⁻¹ * abs (a - y) +
  (abs r)⁻¹ * abs(b - x) + (abs r)⁻¹ * abs ((c - d) - (y - x)),
  by {simp_rw ←mul_add, nth_rewrite_lhs 0 mul_comm, nth_rewrite_rhs 0 mul_comm,
  apply mul_le_mul_of_nonneg_right, swap, simp,
  have h4 : abs (((a - y) - (b - x)) + -((c - d) - (y - x)) ) ≤ abs ((a - y) - (b - x)) +
  abs ((c - d) - (y - x)),
  by {have := complex.abs.add_le ((a - y) - (b - x)) (-((c - d) - (y - x))),
  have ho : abs (c - d - (y - x)) = abs (-((c - d) - (y - x))), by {rw abs.map_neg},
  rw ho,
  convert this,},
  have h44 : abs ((a - y) - (b - x)) ≤ abs (a - y) + abs (b - x),
  by {have := abs.sub_le (a - y) 0 (b - x),
  simp only [zero_sub, sub_zero, neg_sub] at this,
  have hcd : abs (b - x) = abs (x - b), by {apply abs.map_sub,},
  convert this,},
  apply le_trans h4,
  simp only [← add_assoc, h44, add_le_add_iff_right],},
  have e1 : 8⁻¹ * (abs r) * ε = 8⁻¹ * ε * (abs r), by {ring_nf,},
  rw e1 at *,
  apply lt_trans (lt_of_le_of_lt h6 (add_lt_add (add_lt_add ((inv_mul_lt_iff' hr).mpr h1)
    ((inv_mul_lt_iff' hr).mpr h2)) h33)),
  field_simp,
  linarith,
end

lemma Ineq3 (a b c d e f r : ℂ) (ε : ℝ) (hε : 0 < ε) (h1 : abs (a- b) < 8⁻¹ * abs(r) * ε)
  (h2 : abs (c - d) < 8⁻¹ * abs(r) * ε )
  (h : ∃ (x y : ℂ), abs (b - y) < 8⁻¹ * abs(r) * ε ∧ abs (d - x) < 8⁻¹ * abs(r) * ε ∧
  (abs r)⁻¹ * abs ((y - x) - (e - f)) < 8⁻¹ * ε) :
  (abs r)⁻¹ * abs ((a - b) - (c - d) + (b - d) - (e - f)) < ε :=
begin
  apply (Ineq1 _ _ _ _ _ _ _ _ hε h1 h2),
  apply Ineq2 _ _ _ _ _ _ hε h,
end

lemma circle_integral_unif_of_diff_has_fderiv {F : ℕ → ℂ → ℂ} {f : ℂ → ℂ} (z : ℂ) (R : ℝ)
  (hlim : tendsto_uniformly_on F f filter.at_top (closed_ball z R))
  (F_alt : ∀ (n : ℕ) (c : ball z R ), F n c = (circle_integral_form R z (F n)) c) (x : ℂ)
  (hx : x ∈ ball z R)
  (keyb : ∀ (w : ↥(ball z R)),
  tendsto (λ (n : ℕ), ∫ (θ : ℝ) in 0..2 * π, circle_transform R z ↑w (F n) θ) at_top
  (𝓝 (∫ (θ : ℝ) in 0..2 * π, circle_transform R z ↑w f θ)))
  (D : ℂ →L[ℂ] ℂ )
  (hD : has_fderiv_within_at (circle_integral_form R z f) D (ball z R) x ) :
  ∃ (f' : ℂ →L[ℂ] ℂ), has_fderiv_within_at f f' (ball z R) x :=
begin
  refine ⟨D, _⟩,
  simp_rw [has_fderiv_within_at_iff_tendsto, metric.tendsto_nhds, tendsto_uniformly_on_iff,
  dist_eq_norm] at *,
  intros ε hε,
  have h8 : 0 < 8⁻¹ * ε, by {rw inv_eq_one_div, linarith,},
  have hDε := hD (8⁻¹ * ε) h8,
  clear hD,
  simp only [mem_ball, gt_iff_lt, mem_closed_ball, norm_mul, ge_iff_le,
  nonempty_of_inhabited, sub_zero, zero_lt_bit0, zero_lt_mul_left, continuous_linear_map.map_sub,
  set_coe.forall, subtype.coe_mk, eventually_at_top, zero_lt_one, inv_pos, norm_eq_abs,
  norm_inv] at *,
  rw filter.eventually_iff_exists_mem at *,
  obtain ⟨S1, hS1, HS1⟩ := hDε,
  let U := S1 ⊓ ball z R,
  refine ⟨U, _⟩,
  have hU : U ∈ 𝓝[ball z R] x ,
  by {simp only [U, metric.mem_nhds_within_iff, exists_prop, mem_ball, and_true, gt_iff_lt,
  inf_eq_inter, inter_subset_right, subset_inter_iff] at *, exact hS1,},
  simp only [hU, exists_true_left],
  clear hU hS1,
  intros y hy,
  simp_rw U at hy,
  by_cases ht : abs (y - x) ≠ 0,
  simp [mem_ball, inf_eq_inter] at hy,
  simp_rw [real.norm_eq_abs, abs_abs],
  have h8' : 0 < 8⁻¹ * abs (y - x) * ε, by {have : 0 < (8 : ℝ), by {linarith},
  apply mul_pos (mul_pos (inv_pos.2 this) _) hε, apply abs.pos, simp at ht, exact sub_ne_zero.mpr ht},
  obtain ⟨a'', ha''⟩ := (keyb y ((mem_ball.2 hy.2)))
    (8⁻¹ * abs (y - x) * ε) h8',
  obtain ⟨a, ha⟩ := (hlim (8⁻¹ * abs (y - x) * ε) h8'),
  obtain ⟨a', ha'⟩ := ((keyb x (hx)) (8⁻¹ * abs (y - x) * ε) h8'),
  set A' : ℕ := max a a',
  have test := mem_ball.1 ((mem_ball.2 hy.2)),
  simp only [mem_ball, abs_eq_zero, ne.def, subtype.coe_mk] at *,
  set A : ℕ := max A' a'',
  have haA : a ≤ A, by {simp only [le_refl, true_or, le_max_iff],},
  have ha'A : a' ≤ A, by {simp only [le_refl, true_or, or_true, le_max_iff],},
  have ha''A : a'' ≤ A, by {simp only [le_refl, or_true, le_max_iff],},
  have HH : ∀ (y : ℂ), f y - f x - (D y - D x) =
    (f y - F A y) - ((f x) - (F A x)) + ((F A y) - (F A x)) - (D y - D x),
  by {intro y, simp only [sub_left_inj], ring_nf,},
  simp_rw HH,
  apply Ineq3 _ _ _ _ _ _ _ _ hε
    (ha A haA y (mem_ball.1 ((mem_ball.2 hy.2))).le)
    (ha A haA x (mem_ball.1 (hx)).le),
  clear keyb HH hε h8 h8',
  refine ⟨(circle_integral_form R z f x), (circle_integral_form R z f y), _⟩,
  simp_rw circle_intgral_form_eq_int,
  refine ⟨by {have := F_alt A ⟨y,(mem_ball.2 hy.2)⟩,
  simp only [subtype.coe_mk] at this,
  rw [this, circle_intgral_form_eq_int],
  apply ha'' A ha''A},
  by {have := F_alt A ⟨x,(mem_ball.2 hx)⟩,
  simp only [subtype.coe_mk] at this,
  rw [this, circle_intgral_form_eq_int],
  apply ha' A ha'A},
  by {simp_rw [real.norm_eq_abs, abs_abs, circle_intgral_form_eq_int ] at HS1,
  apply HS1 _ hy.1}⟩,
  simp only [abs_eq_zero, not_not] at ht,
  simp only [ht, norm_zero, zero_mul, abs_zero, inv_zero, hε],
end

lemma uniform_of_diff_circle_int_is_diff {R : ℝ} (F : ℕ → ℂ → ℂ) (f : ℂ → ℂ) (z : ℂ)
  (hR : 0 < R) (hdiff : ∀ (n : ℕ), differentiable_on ℂ (F n) (closed_ball z R))
  (hlim : tendsto_uniformly_on F f filter.at_top (closed_ball z R)) :
  differentiable_on ℂ f (ball z R) :=
begin
  have F_alt : ∀ (n : ℕ) (c : ball z R), F n c = (circle_integral_form R z (F n)) c,
  by {intros n c,
  have hc : c.1 ∈ ball z R , by { apply c.property,},
  have hcc : ∀ (x : ℂ), x ∈ ball z R \ ∅ → differentiable_at ℂ (F n) x,
  by { simp only [diff_empty], intros x hx,
    apply_rules [(hdiff n).differentiable_at, closed_ball_mem_nhds_of_mem, hx]},
  have ttt := (two_pi_I_inv_smul_circle_integral_sub_inv_smul_of_differentiable_on_off_countable
    countable_empty hc (hdiff n).continuous_on hcc),
  simp only [mem_ball, algebra.id.smul_eq_mul, subtype.val_eq_coe, diff_empty] at *,
  rw ←ttt,
  simp only [circle_integral_form, circle_transform, one_div, algebra.id.smul_eq_mul,
  nat.cast_bit0, real_smul,integral_const_mul, nsmul_eq_mul, nat.cast_one],},
  have F_cts_ball : ∀ n, continuous_on (F n) (closed_ball z R),
  by {intro n, apply (hdiff n).continuous_on,},
  have F_cts_sphere :∀ n, continuous_on (F n) (sphere z R),
  by {intro n, apply (F_cts_ball n).mono sphere_subset_closed_ball},
  rw differentiable_on,
  intros x hx,
  have keyb := λ ww, circle_int_uniform_lim_eq_lim_of_int hR f z ww F_cts_sphere
  (hlim.mono sphere_subset_closed_ball),
  rw differentiable_within_at,
  have hf : continuous_on f (closed_ball z R), by {apply tendsto_uniformly_on.continuous_on hlim,
  simp only [eventually_at_top, implies_true_iff, exists_const, F_cts_ball],},
  have HF := circle_integral_form_differentiable_on hR z (hf.mono sphere_subset_closed_ball),
  clear hf F_cts_ball F_cts_sphere hdiff,
  rw differentiable_on at HF,
  have HF2 := HF x hx,
  clear HF,
  simp only [hx, forall_true_left, differentiable_within_at] at HF2,
  obtain ⟨D, hD⟩ := HF2,
  apply circle_integral_unif_of_diff_has_fderiv z R hlim F_alt x hx keyb D hD,
end

end complex


example (p q r : Prop) : p ∧ (q ∨ r) → (p ∧ q) ∨ (p ∧ r) :=
begin
  intro h,
  cases h with hp hqr,
  show (p ∧ q) ∨ (p ∧ r),
  cases hqr with hq hr,
    have hpq : p ∧ q,
      split; assumption,
    left, exact hpq,
    have hpr : p ∧ r,
      split; assumption,
    right, exact hpr
end


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

open_locale nat rat real big_operators topological_space

theorem algebra_sqineq_at2malt1
  (a : ℝ) :
  a * (2 - a) ≤ 1 :=
begin
  suffices: 0 ≤ a^2 - 2*a + 1, nlinarith,
  suffices: 0 ≤ (a - 1)^2, nlinarith,
  nlinarith,
end


/-
Copyright (c) 2022 Matthias Uschold. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Matthias Uschold.
-/
import data.set        -- basics on sets
import data.set.finite -- basics on finite sets
import data.finset     -- type-level finite sets
import data.real.basic

import topology.category.Top

import algebra.big_operators.basic
import algebra.big_operators.order

/-!

# Auxiliary Lemmas on Sums

This file collects some auxiliary lemmas on sums over finsets.

-/

open_locale big_operators -- to enable ∑ notation

open classical

lemma sum_scalar {α : Type*} {S:finset α}
  {f: α → ℝ}
  (r:ℝ)
  : ∑ x in S, (r *  (f x)) = r *  (∑ x in S, f x )
:= begin
  classical,
  apply finset.induction_on S,
    --case emptyset
    simp,
  assume a s a_nin_s,
  assume ind_hyp,
  calc  ∑ (x : α) in insert a s, r * f x
      = (∑ (x : α) in s, r * f x) + (r * f a)
        : by finish
  ... =  r* (∑ (x : α) in s, f x) + r * f a
        : by simp [ind_hyp]
  ... = r * ((∑ (x : α) in s, f x) + f a )
        : by ring
  ... = r * ∑ (x : α) in insert a s,  f x
        : by finish,
end

/--For the difference between summing over S and T, we can
take sums over the relative complements-/
lemma symm_diff_of_diff_sum
  {α : Type*} [decidable_eq α]
  {S T:finset α}
  {f: α → ℝ}
  : ∑ x in S, f x - ∑ x in T, f x
  = ∑ x in S \ T, f x - ∑ x in T \ S, f x
:= begin
  apply finset.induction_on S,
    by simp,
  assume a s a_nin_s,
  assume ind_hyp,
  rw finset.sum_insert a_nin_s,
  by_cases a_in_T : a ∈ T,
  {
    have h₁ : insert a s \ T = s \ T,
    {
      ext x,
      split,
      {
        assume xinL,
        simp at xinL,
        simp,
        finish,
      },
      assume xinR,
      simp at xinR,
      simp,
      split,
      {
        right,
        exact xinR.left,
      },
      exact xinR.right,
    },
    have h₂ : T \ s = insert a (T \ (insert a s)),
    {
      ext x,
      simp,
      split,
      {
        intro xin,
        by_cases xa: x=a,
          left,
          exact xa,
        right,
        apply and.intro xin.left,
        push_neg,
        apply and.intro xa,
        exact xin.right,
      },
      intro xin,
      cases xin,
      {
        rw xin,
        exact ⟨a_in_T, a_nin_s⟩,
      },
      apply and.intro xin.left,
      push_neg at xin,
      exact xin.right.right,
    },
    have h₃ : a ∉ T \ insert a s :=  by simp,
    calc  f a + ∑ (x : α) in s, f x - ∑ (x : α) in T, f x
        = f a + (∑ (x : α) in s, f x - ∑ (x : α) in T, f x)
          : by ring
    ... = f a + (∑ (x : α) in s \ T, f x - ∑ (x : α) in T \ s, f x)
          : by rw ind_hyp
    ... = f a + (∑ (x : α) in (insert a s) \ T, f x - ∑ (x : α) in T \ s, f x)
          : by rw h₁
    ... = f a + (∑ (x : α) in (insert a s) \ T, f x - ∑ (x : α) in insert a (T \ (insert a s)), f x)
          : by rw h₂
    ... = f a + (∑ (x : α) in (insert a s) \ T, f x - (f a + ∑ (x : α) in (T \ (insert a s)), f x))
          : by rw finset.sum_insert h₃
    ... = ∑ (x : α) in insert a s \ T, f x - ∑ (x : α) in T \ insert a s, f x
          : by ring,
  },
  {
    have h₁ : insert a s \ T = insert a (s \ T),
    {
      ext x,
      simp,
      split,
      {
        intro xin,
        cases xin.left with xa xins,
          left,
          exact xa,
        right,
        exact ⟨xins, xin.right⟩,
      },
      intro xin,
      split,
      {
        cases xin with xa xins,
            left,
            exact xa,
        right,
        exact xins.left
      },
      cases xin with xa xalt,
      {
        rw xa,
        exact a_in_T,
      },
      exact xalt.right,
    },
    have h₂ : T \ s = T \ (insert a s),
    {
      ext x,
      split,
      {
        intro xin,
        simp at xin,
        simp,
        apply and.intro xin.left,
        push_neg,
        split,
          intro h,
          rw ← h at a_in_T,
          exact a_in_T xin.left,
        exact xin.right,
      },
      intro xin,
      simp at xin,
      simp,
      tauto,
    },
    have h₃ : a ∉ s \ T,
    {
      simp,
      intro h,
      exfalso,
      exact a_nin_s h,
    },
    calc  f a + ∑ (x : α) in s, f x - ∑ (x : α) in T, f x
        = f a + (∑ (x : α) in s, f x - ∑ (x : α) in T, f x)
          : by ring
    ... = f a + (∑ (x : α) in s \ T, f x - ∑ (x : α) in T \ s, f x)
          : by rw ind_hyp
    ... = (f a + ∑ (x : α) in s \ T, f x) - ∑ (x : α) in T \ s, f x
          : by ring
    ... = ∑ (x : α) in insert a (s \ T), f x - ∑ (x : α) in T \ s, f x
          : by rw finset.sum_insert h₃
    ... = ∑ (x : α) in insert a s \ T, f x - ∑ (x : α) in T \ s, f x
          : by rw h₁
    ... = ∑ (x : α) in insert a s \ T, f x - ∑ (x : α) in T \ insert a s, f x
          : by rw h₂,
  },
end

lemma abs_le_symm_diff_of_diff_sum
  {α : Type*} [decidable_eq α]
  {S T:finset α}
  {f: α → ℝ}
  : |∑ x in S, f x - ∑ x in T, f x|
  ≤ ∑ x in symm_diff S T, |f x|
:= begin
  calc  |∑ x in S, f x - ∑ x in T, f x|
      = |∑ x in S \ T, f x - ∑ x in T \ S, f x|
        : by rw symm_diff_of_diff_sum
  ... ≤ |∑ x in S \ T, f x| + |∑ x in T \ S, f x|
        : abs_sub _ _
  ... ≤ ∑ x in S \ T, |f x| + ∑ x in T \ S, |f x|
        : by {
          apply add_le_add; exact finset.abs_sum_le_sum_abs _ _,
        }
  ... = ∑ x in symm_diff S T, |f x|
        : by {
          dsimp[symm_diff],
          exact eq.symm (finset.sum_union (begin
            unfold disjoint,
            assume x xininters,
            finish,
          end)),
        },
end


import data.zmod.quadratic_reciprocity

-- Wilson’s Theorem

open nat

theorem t051 {p : ℕ} : Π (hp : nat.prime p), (fact (p - 1) : zmodp p hp) = -1
:= zmodp.wilsons_lemma


/-
Copyright (c) 2017 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro
-/
import algebra.ordered_group order.lattice

open lattice

universes u v
variables {α : Type u} {β : Type v}

attribute [simp] max_eq_left max_eq_right min_eq_left min_eq_right

section
variables [decidable_linear_order α] [decidable_linear_order β] {f : α → β} {a b c d : α}

-- translate from lattices to linear orders (sup → max, inf → min)
@[simp] lemma le_min_iff : c ≤ min a b ↔ c ≤ a ∧ c ≤ b := le_inf_iff
@[simp] lemma max_le_iff : max a b ≤ c ↔ a ≤ c ∧ b ≤ c := sup_le_iff
lemma max_le_max : a ≤ c → b ≤ d → max a b ≤ max c d := sup_le_sup
lemma min_le_min : a ≤ c → b ≤ d → min a b ≤ min c d := inf_le_inf
lemma le_max_left_of_le : a ≤ b → a ≤ max b c := le_sup_left_of_le
lemma le_max_right_of_le : a ≤ c → a ≤ max b c := le_sup_right_of_le
lemma min_le_left_of_le : a ≤ c → min a b ≤ c := inf_le_left_of_le
lemma min_le_right_of_le : b ≤ c → min a b ≤ c := inf_le_right_of_le
lemma max_min_distrib_left : max a (min b c) = min (max a b) (max a c) := sup_inf_left
lemma max_min_distrib_right : max (min a b) c = min (max a c) (max b c) := sup_inf_right
lemma min_max_distrib_left : min a (max b c) = max (min a b) (min a c) := inf_sup_left
lemma min_max_distrib_right : min (max a b) c = max (min a c) (min b c) := inf_sup_right

instance max_idem : is_idempotent α max := by apply_instance
instance min_idem : is_idempotent α min := by apply_instance

@[simp] lemma min_le_iff : min a b ≤ c ↔ a ≤ c ∨ b ≤ c :=
have a ≤ b → (a ≤ c ∨ b ≤ c ↔ a ≤ c),
  from assume h, or_iff_left_of_imp $ le_trans h,
have b ≤ a → (a ≤ c ∨ b ≤ c ↔ b ≤ c),
  from assume h, or_iff_right_of_imp $ le_trans h,
by cases le_total a b; simp *

@[simp] lemma le_max_iff : a ≤ max b c ↔ a ≤ b ∨ a ≤ c :=
have b ≤ c → (a ≤ b ∨ a ≤ c ↔ a ≤ c),
  from assume h, or_iff_right_of_imp $ assume h', le_trans h' h,
have c ≤ b → (a ≤ b ∨ a ≤ c ↔ a ≤ b),
  from assume h, or_iff_left_of_imp $ assume h', le_trans h' h,
by cases le_total b c; simp *

@[simp] lemma max_lt_iff : max a b < c ↔ (a < c ∧ b < c) :=
by rw [lt_iff_not_ge]; simp [(≥), le_max_iff, not_or_distrib]

@[simp] lemma lt_min_iff : a < min b c ↔ (a < b ∧ a < c) :=
by rw [lt_iff_not_ge]; simp [(≥), min_le_iff, not_or_distrib]

@[simp] lemma lt_max_iff : a < max b c ↔ a < b ∨ a < c :=
by rw [lt_iff_not_ge]; simp [(≥), max_le_iff, not_and_distrib]

@[simp] lemma min_lt_iff : min a b < c ↔ a < c ∨ b < c :=
by rw [lt_iff_not_ge]; simp [(≥), le_min_iff, not_and_distrib]

lemma max_lt_max (h₁ : a < c) (h₂ : b < d) : max a b < max c d :=
by apply max_lt; simp [lt_max_iff, h₁, h₂]

lemma min_lt_min (h₁ : a < c) (h₂ : b < d) : min a b < min c d :=
by apply lt_min; simp [min_lt_iff, h₁, h₂]

theorem min_right_comm (a b c : α) : min (min a b) c = min (min a c) b :=
right_comm min min_comm min_assoc a b c

theorem max.left_comm (a b c : α) : max a (max b c) = max b (max a c) :=
left_comm max max_comm max_assoc a b c

theorem max.right_comm (a b c : α) : max (max a b) c = max (max a c) b :=
right_comm max max_comm max_assoc a b c

lemma max_distrib_of_monotone (hf : monotone f) : f (max a b) = max (f a) (f b) :=
by cases le_total a b; simp [h, hf h]

lemma min_distrib_of_monotone (hf : monotone f) : f (min a b) = min (f a) (f b) :=
by cases le_total a b; simp [h, hf h]

theorem min_choice (a b : α) : min a b = a ∨ min a b = b :=
by by_cases h : a ≤ b; simp [min, h]

theorem max_choice (a b : α) : max a b = a ∨ max a b = b :=
by by_cases h : a ≤ b; simp [max, h]

lemma le_of_max_le_left {a b c : α} (h : max a b ≤ c) : a ≤ c :=
le_trans (le_max_left _ _) h

lemma le_of_max_le_right {a b c : α} (h : max a b ≤ c) : b ≤ c :=
le_trans (le_max_right _ _) h

end

lemma min_add {α : Type u} [decidable_linear_ordered_comm_group α] (a b c : α) :
      min a b + c = min (a + c) (b + c) :=
if hle : a ≤ b then
  have a - c ≤ b - c, from sub_le_sub hle (le_refl _),
  by simp * at *
else
  have b - c ≤ a - c, from sub_le_sub (le_of_lt (lt_of_not_ge hle)) (le_refl _),
  by simp * at *

lemma min_sub {α : Type u} [decidable_linear_ordered_comm_group α] (a b c : α) :
      min a b - c = min (a - c) (b - c) :=
by simp [min_add, sub_eq_add_neg]

section decidable_linear_ordered_comm_group
variables [decidable_linear_ordered_comm_group α] {a b c : α}

attribute [simp] abs_zero abs_neg

def abs_add := @abs_add_le_abs_add_abs

theorem abs_le : abs a ≤ b ↔ - b ≤ a ∧ a ≤ b :=
⟨assume h, ⟨neg_le_of_neg_le $ le_trans (neg_le_abs_self _) h, le_trans (le_abs_self _) h⟩,
  assume ⟨h₁, h₂⟩, abs_le_of_le_of_neg_le h₂ $ neg_le_of_neg_le h₁⟩

lemma abs_lt : abs a < b ↔ - b < a ∧ a < b :=
⟨assume h, ⟨neg_lt_of_neg_lt $ lt_of_le_of_lt (neg_le_abs_self _) h, lt_of_le_of_lt (le_abs_self _) h⟩,
  assume ⟨h₁, h₂⟩, abs_lt_of_lt_of_neg_lt h₂ $ neg_lt_of_neg_lt h₁⟩

lemma abs_sub_le_iff : abs (a - b) ≤ c ↔ a - b ≤ c ∧ b - a ≤ c :=
by rw [abs_le, neg_le_sub_iff_le_add, @sub_le_iff_le_add' _ _ b, and_comm]

lemma abs_sub_lt_iff : abs (a - b) < c ↔ a - b < c ∧ b - a < c :=
by rw [abs_lt, neg_lt_sub_iff_lt_add, @sub_lt_iff_lt_add' _ _ b, and_comm]

def sub_abs_le_abs_sub := @abs_sub_abs_le_abs_sub

lemma abs_abs_sub_le_abs_sub (a b : α) : abs (abs a - abs b) ≤ abs (a - b) :=
abs_sub_le_iff.2 ⟨sub_abs_le_abs_sub _ _, by rw abs_sub; apply sub_abs_le_abs_sub⟩

lemma abs_eq (hb : b ≥ 0) : abs a = b ↔ a = b ∨ a = -b :=
iff.intro
  begin
    cases le_total a 0 with a_nonpos a_nonneg,
    { rw [abs_of_nonpos a_nonpos, neg_eq_iff_neg_eq, eq_comm], exact or.inr },
    { rw [abs_of_nonneg a_nonneg, eq_comm], exact or.inl }
  end
  (by intro h; cases h; subst h; try { rw abs_neg }; exact abs_of_nonneg hb)

@[simp] lemma abs_eq_zero : abs a = 0 ↔ a = 0 :=
⟨eq_zero_of_abs_eq_zero, λ e, e.symm ▸ abs_zero⟩

lemma abs_pos_iff {a : α} : 0 < abs a ↔ a ≠ 0 :=
⟨λ h, mt abs_eq_zero.2 (ne_of_gt h), abs_pos_of_ne_zero⟩

lemma abs_le_max_abs_abs (hab : a ≤ b)  (hbc : b ≤ c) : abs b ≤ max (abs a) (abs c) :=
abs_le_of_le_of_neg_le
  (by simp [le_max_iff, le_trans hbc (le_abs_self c)])
  (by simp [le_max_iff, le_trans (neg_le_neg hab) (neg_le_abs_self a)])

theorem abs_le_abs {α : Type*} [decidable_linear_ordered_comm_group α] {a b : α}
  (h₀ : a ≤ b) (h₁ : -a ≤ b) :
  abs a ≤ abs b :=
calc  abs a
    ≤ b : by { apply abs_le_of_le_of_neg_le; assumption }
... ≤ abs b : le_abs_self _

lemma min_le_add_of_nonneg_right {a b : α} (hb : b ≥ 0) : min a b ≤ a + b :=
calc
  min a b ≤ a     : by apply min_le_left
      ... ≤ a + b : le_add_of_nonneg_right hb

lemma min_le_add_of_nonneg_left {a b : α} (ha : a ≥ 0) : min a b ≤ a + b :=
calc
  min a b ≤ b     : by apply min_le_right
      ... ≤ a + b : le_add_of_nonneg_left ha

lemma max_le_add_of_nonneg {a b : α} (ha : a ≥ 0) (hb : b ≥ 0) : max a b ≤ a + b :=
max_le_iff.2 (by split; simpa)

end decidable_linear_ordered_comm_group

section decidable_linear_ordered_comm_ring
variables [decidable_linear_ordered_comm_ring α] {a b c d : α}

@[simp] lemma abs_one : abs (1 : α) = 1 := abs_of_pos zero_lt_one

lemma monotone_mul_of_nonneg (ha : 0 ≤ a) : monotone (λ x, a*x) :=
assume b c b_le_c, mul_le_mul_of_nonneg_left b_le_c ha

lemma mul_max_of_nonneg (b c : α) (ha : 0 ≤ a) : a * max b c = max (a * b) (a * c) :=
max_distrib_of_monotone (monotone_mul_of_nonneg ha)

lemma mul_min_of_nonneg (b c : α) (ha : 0 ≤ a) : a * min b c = min (a * b) (a * c) :=
min_distrib_of_monotone (monotone_mul_of_nonneg ha)

lemma max_mul_mul_le_max_mul_max (b c : α) (ha : 0 ≤ a) (hd: 0 ≤ d) :
  max (a * b) (d * c) ≤ max a c * max d b :=
have ba : b * a ≤ max d b * max c a,
  from mul_le_mul (le_max_right d b) (le_max_right c a) ha (le_trans hd (le_max_left d b)),
have cd : c * d ≤ max a c * max b d,
  from mul_le_mul (le_max_right a c) (le_max_right b d) hd (le_trans ha (le_max_left a c)),
max_le
  (by simpa [mul_comm, max_comm] using ba)
  (by simpa [mul_comm, max_comm] using cd)

end decidable_linear_ordered_comm_ring


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

Miscellaneous function constructions and lemmas.
-/
import logic.basic data.option

universes u v w

namespace function

section
variables {α : Sort u} {β : Sort v} {f : α → β}

lemma hfunext {α α': Sort u} {β : α → Sort v} {β' : α' → Sort v} {f : Πa, β a} {f' : Πa, β' a}
  (hα : α = α') (h : ∀a a', a == a' → f a == f' a') : f == f' :=
begin
  subst hα,
  have : ∀a, f a == f' a,
  { intro a, exact h a a (heq.refl a) },
  have : β = β',
  { funext a, exact type_eq_of_heq (this a) },
  subst this,
  apply heq_of_eq,
  funext a,
  exact eq_of_heq (this a)
end

lemma funext_iff {β : α → Sort*} {f₁ f₂ : Π (x : α), β x} : f₁ = f₂ ↔ (∀a, f₁ a = f₂ a) :=
iff.intro (assume h a, h ▸ rfl) funext

lemma comp_apply {α : Sort u} {β : Sort v} {φ : Sort w} (f : β → φ) (g : α → β) (a : α) :
  (f ∘ g) a = f (g a) := rfl

@[simp] theorem injective.eq_iff (I : injective f) {a b : α} :
  f a = f b ↔ a = b :=
⟨@I _ _, congr_arg f⟩

def injective.decidable_eq [decidable_eq β] (I : injective f) : decidable_eq α
| a b := decidable_of_iff _ I.eq_iff

instance decidable_eq_pfun (p : Prop) [decidable p] (α : p → Type*)
  [Π hp, decidable_eq (α hp)] : decidable_eq (Π hp, α hp)
| f g := decidable_of_iff (∀ hp, f hp = g hp) funext_iff.symm

theorem cantor_surjective {α} (f : α → α → Prop) : ¬ function.surjective f | h :=
let ⟨D, e⟩ := h (λ a, ¬ f a a) in
(iff_not_self (f D D)).1 $ iff_of_eq (congr_fun e D)

theorem cantor_injective {α : Type*} (f : (α → Prop) → α) :
  ¬ function.injective f | i :=
cantor_surjective (λ a b, ∀ U, a = f U → U b) $
surjective_of_has_right_inverse ⟨f, λ U, funext $
  λ a, propext ⟨λ h, h U rfl, λ h' U' e, i e ▸ h'⟩⟩

/-- `g` is a partial inverse to `f` (an injective but not necessarily
  surjective function) if `g y = some x` implies `f x = y`, and `g y = none`
  implies that `y` is not in the range of `f`. -/
def is_partial_inv {α β} (f : α → β) (g : β → option α) : Prop :=
∀ x y, g y = some x ↔ f x = y

theorem is_partial_inv_left {α β} {f : α → β} {g} (H : is_partial_inv f g) (x) : g (f x) = some x :=
(H _ _).2 rfl

theorem injective_of_partial_inv {α β} {f : α → β} {g} (H : is_partial_inv f g) : injective f :=
λ a b h, option.some.inj $ ((H _ _).2 h).symm.trans ((H _ _).2 rfl)

theorem injective_of_partial_inv_right {α β} {f : α → β} {g} (H : is_partial_inv f g)
 (x y b) (h₁ : b ∈ g x) (h₂ : b ∈ g y) : x = y :=
((H _ _).1 h₁).symm.trans ((H _ _).1 h₂)

theorem left_inverse.comp_eq_id {f : α → β} {g : β → α} (h : left_inverse f g) : f ∘ g = id :=
funext h

theorem right_inverse.comp_eq_id {f : α → β} {g : β → α} (h : right_inverse f g) : g ∘ f = id :=
funext h

theorem left_inverse.comp {γ} {f : α → β} {g : β → α} {h : β → γ} {i : γ → β}
  (hf : left_inverse f g) (hh : left_inverse h i) : left_inverse (h ∘ f) (g ∘ i) :=
assume a, show h (f (g (i a))) = a, by rw [hf (i a), hh a]

theorem right_inverse.comp {γ} {f : α → β} {g : β → α} {h : β → γ} {i : γ → β}
  (hf : right_inverse f g) (hh : right_inverse h i) : right_inverse (h ∘ f) (g ∘ i) :=
left_inverse.comp hh hf

local attribute [instance] classical.prop_decidable

/-- We can use choice to construct explicitly a partial inverse for
  a given injective function `f`. -/
noncomputable def partial_inv {α β} (f : α → β) (b : β) : option α :=
if h : ∃ a, f a = b then some (classical.some h) else none

theorem partial_inv_of_injective {α β} {f : α → β} (I : injective f) :
  is_partial_inv f (partial_inv f) | a b :=
⟨λ h, if h' : ∃ a, f a = b then begin
    rw [partial_inv, dif_pos h'] at h,
    injection h with h, subst h,
    apply classical.some_spec h'
  end else by rw [partial_inv, dif_neg h'] at h; contradiction,
 λ e, e ▸ have h : ∃ a', f a' = f a, from ⟨_, rfl⟩,
   (dif_pos h).trans (congr_arg _ (I $ classical.some_spec h))⟩

theorem partial_inv_left {α β} {f : α → β} (I : injective f) : ∀ x, partial_inv f (f x) = some x :=
is_partial_inv_left (partial_inv_of_injective I)

end

section inv_fun
variables {α : Type u} [inhabited α] {β : Sort v} {f : α → β} {s : set α} {a : α} {b : β}

local attribute [instance] classical.prop_decidable

/-- Construct the inverse for a function `f` on domain `s`. -/
noncomputable def inv_fun_on (f : α → β) (s : set α) (b : β) : α :=
if h : ∃a, a ∈ s ∧ f a = b then classical.some h else default α

theorem inv_fun_on_pos (h : ∃a∈s, f a = b) : inv_fun_on f s b ∈ s ∧ f (inv_fun_on f s b) = b :=
by rw [bex_def] at h; rw [inv_fun_on, dif_pos h]; exact classical.some_spec h

theorem inv_fun_on_mem (h : ∃a∈s, f a = b) : inv_fun_on f s b ∈ s := (inv_fun_on_pos h).left

theorem inv_fun_on_eq (h : ∃a∈s, f a = b) : f (inv_fun_on f s b) = b := (inv_fun_on_pos h).right

theorem inv_fun_on_eq' (h : ∀ x y ∈ s, f x = f y → x = y) (ha : a ∈ s) :
  inv_fun_on f s (f a) = a :=
have ∃a'∈s, f a' = f a, from ⟨a, ha, rfl⟩,
h _ _ (inv_fun_on_mem this) ha (inv_fun_on_eq this)

theorem inv_fun_on_neg (h : ¬ ∃a∈s, f a = b) : inv_fun_on f s b = default α :=
by rw [bex_def] at h; rw [inv_fun_on, dif_neg h]

/-- The inverse of a function (which is a left inverse if `f` is injective
  and a right inverse if `f` is surjective). -/
noncomputable def inv_fun (f : α → β) : β → α := inv_fun_on f set.univ

theorem inv_fun_eq (h : ∃a, f a = b) : f (inv_fun f b) = b :=
inv_fun_on_eq $ let ⟨a, ha⟩ := h in ⟨a, trivial, ha⟩

theorem inv_fun_eq_of_injective_of_right_inverse {g : β → α}
  (hf : injective f) (hg : right_inverse g f) : inv_fun f = g :=
funext $ assume b,
hf begin rw [hg b], exact inv_fun_eq ⟨g b, hg b⟩ end

lemma right_inverse_inv_fun (hf : surjective f) : right_inverse (inv_fun f) f :=
assume b, inv_fun_eq $ hf b

lemma left_inverse_inv_fun (hf : injective f) : left_inverse (inv_fun f) f :=
assume b,
have f (inv_fun f (f b)) = f b,
  from inv_fun_eq ⟨b, rfl⟩,
hf this

lemma inv_fun_surjective (hf : injective f) : surjective (inv_fun f) :=
surjective_of_has_right_inverse ⟨_, left_inverse_inv_fun hf⟩

lemma inv_fun_comp (hf : injective f) : inv_fun f ∘ f = id := funext $ left_inverse_inv_fun hf

lemma injective.has_left_inverse (hf : injective f) : has_left_inverse f :=
⟨inv_fun f, left_inverse_inv_fun hf⟩

lemma injective_iff_has_left_inverse : injective f ↔ has_left_inverse f :=
⟨injective.has_left_inverse, injective_of_has_left_inverse⟩

end inv_fun

section surj_inv
variables {α : Sort u} {β : Sort v} {f : α → β}

/-- The inverse of a surjective function. (Unlike `inv_fun`, this does not require
  `α` to be inhabited.) -/
noncomputable def surj_inv {f : α → β} (h : surjective f) (b : β) : α := classical.some (h b)

lemma surj_inv_eq (h : surjective f) (b) : f (surj_inv h b) = b := classical.some_spec (h b)

lemma right_inverse_surj_inv (hf : surjective f) : right_inverse (surj_inv hf) f :=
surj_inv_eq hf

lemma left_inverse_surj_inv (hf : bijective f) : left_inverse (surj_inv hf.2) f :=
right_inverse_of_injective_of_left_inverse hf.1 (right_inverse_surj_inv hf.2)

lemma surjective.has_right_inverse (hf : surjective f) : has_right_inverse f :=
⟨_, right_inverse_surj_inv hf⟩

lemma surjective_iff_has_right_inverse : surjective f ↔ has_right_inverse f :=
⟨surjective.has_right_inverse, surjective_of_has_right_inverse⟩

lemma bijective_iff_has_inverse : bijective f ↔ ∃ g, left_inverse g f ∧ right_inverse g f :=
⟨λ hf, ⟨_, left_inverse_surj_inv hf, right_inverse_surj_inv hf.2⟩,
 λ ⟨g, gl, gr⟩, ⟨injective_of_left_inverse gl, surjective_of_has_right_inverse ⟨_, gr⟩⟩⟩

lemma injective_surj_inv (h : surjective f) : injective (surj_inv h) :=
injective_of_has_left_inverse ⟨f, right_inverse_surj_inv h⟩

end surj_inv

section update
variables {α : Sort u} {β : α → Sort v} [decidable_eq α]

def update (f : Πa, β a) (a' : α) (v : β a') (a : α) : β a :=
if h : a = a' then eq.rec v h.symm else f a

@[simp] lemma update_same {a : α} {v : β a} {f : Πa, β a} : update f a v a = v :=
dif_pos rfl

@[simp] lemma update_noteq {a a' : α} {v : β a'} {f : Πa, β a} (h : a ≠ a') : update f a' v a = f a :=
dif_neg h

end update

lemma uncurry_def {α β γ} (f : α → β → γ) : uncurry f = (λp, f p.1 p.2) :=
funext $ assume ⟨a, b⟩, rfl
end function


/-
Copyright (c) 2022 Markus Himmel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
import data.opposite
import data.set.basic

/-!
# The opposite of a set

The opposite of a set `s` is simply the set obtained by taking the opposite of each member of `s`.
-/

variables {α : Type*}

open opposite

namespace set

/-- The opposite of a set `s` is the set obtained by taking the opposite of each member of `s`. -/
protected def op (s : set α) : set αᵒᵖ :=
unop ⁻¹' s

/-- The unop of a set `s` is the set obtained by taking the unop of each member of `s`. -/
protected def unop (s : set αᵒᵖ) : set α :=
op ⁻¹' s

@[simp] lemma mem_op {s : set α} {a : αᵒᵖ} : a ∈ s.op ↔ unop a ∈ s :=
iff.rfl

@[simp] lemma op_mem_op {s : set α} {a : α} : op a ∈ s.op ↔ a ∈ s :=
by rw [mem_op, unop_op]

@[simp] lemma mem_unop {s : set αᵒᵖ} {a : α} : a ∈ s.unop ↔ op a ∈ s :=
iff.rfl

@[simp] lemma unop_mem_unop {s : set αᵒᵖ} {a : αᵒᵖ} : unop a ∈ s.unop ↔ a ∈ s :=
by rw [mem_unop, op_unop]

@[simp] lemma op_unop (s : set α) : s.op.unop = s :=
ext (by simp only [mem_unop, op_mem_op, iff_self, implies_true_iff])

@[simp] lemma unop_op (s : set αᵒᵖ) : s.unop.op = s :=
ext (by simp only [mem_op, unop_mem_unop, iff_self, implies_true_iff])

/-- Taking opposites as an equivalence of powersets. -/
@[simps] def op_equiv : set α ≃ set αᵒᵖ :=
⟨set.op, set.unop, op_unop, unop_op⟩

@[simp] lemma singleton_op (x : α) : ({x} : set α).op = {op x} :=
ext $ λ y, by simpa only [mem_op, mem_singleton_iff] using unop_eq_iff_eq_op

@[simp] lemma singleton_unop (x : αᵒᵖ) : ({x} : set αᵒᵖ).unop = {unop x} :=
ext $ λ y, by simpa only [mem_unop, mem_singleton_iff] using op_eq_iff_eq_unop

@[simp] lemma singleton_op_unop (x : α) : ({op x} : set αᵒᵖ).unop = {x} :=
by simp only [singleton_unop, opposite.unop_op]

@[simp] lemma singleton_unop_op (x : αᵒᵖ) : ({unop x} : set α).op = {x} :=
by simp only [singleton_op, opposite.op_unop]

end set


/-
Copyright (c) 2017 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl, Kenny Lau
-/
import order.complete_lattice
import dynamics.fixed_points.basic

/-!
# Fixed point construction on complete lattices
-/

universes u v w
variables {α : Type u} {β : Type v} {γ : Type w}

open function (fixed_points)

section fixedpoint
variables [complete_lattice α] {f : α → α}

/-- Least fixed point of a monotone function -/
def lfp (f : α → α) : α := Inf {a | f a ≤ a}
/-- Greatest fixed point of a monotone function -/
def gfp (f : α → α) : α := Sup {a | a ≤ f a}

theorem lfp_le {a : α} (h : f a ≤ a) : lfp f ≤ a :=
Inf_le h

theorem le_lfp {a : α} (h : ∀b, f b ≤ b → a ≤ b) : a ≤ lfp f :=
le_Inf h

theorem lfp_eq (m : monotone f) : lfp f = f (lfp f) :=
have f (lfp f) ≤ lfp f,
  from le_lfp $ assume b, assume h : f b ≤ b, le_trans (m (lfp_le h)) h,
le_antisymm (lfp_le (m this)) this

theorem lfp_induct {p : α → Prop} (m : monotone f)
  (step : ∀a, p a → a ≤ lfp f → p (f a)) (sup : ∀s, (∀a∈s, p a) → p (Sup s)) :
p (lfp f) :=
let s := {a | a ≤ lfp f ∧ p a} in
have p_s : p (Sup s),
  from sup s (assume a ⟨_, h⟩, h),
have Sup s ≤ lfp f,
  from le_lfp $ assume a, assume h : f a ≤ a, Sup_le $ assume b ⟨b_le, _⟩, le_trans b_le (lfp_le h),
have Sup s = lfp f,
  from le_antisymm this $ lfp_le $ le_Sup
    ⟨le_trans (m this) $ ge_of_eq $ lfp_eq m, step _ p_s this⟩,
this ▸ p_s

theorem monotone_lfp : monotone (@lfp α _) :=
assume f g, assume : f ≤ g, le_lfp $ assume a, assume : g a ≤ a, lfp_le $ le_trans (‹f ≤ g› a) this

theorem le_gfp {a : α} (h : a ≤ f a) : a ≤ gfp f :=
le_Sup h

theorem gfp_le {a : α} (h : ∀b, b ≤ f b → b ≤ a) : gfp f ≤ a :=
Sup_le h

theorem gfp_eq (m : monotone f) : gfp f = f (gfp f) :=
have gfp f ≤ f (gfp f),
  from gfp_le $ assume b, assume h : b ≤ f b, le_trans h (m (le_gfp h)),
le_antisymm this (le_gfp (m this))

theorem gfp_induct {p : α → Prop} (m : monotone f)
  (step : ∀a, p a → gfp f ≤ a → p (f a)) (inf : ∀s, (∀a∈s, p a) → p (Inf s)) :
p (gfp f) :=
let s := {a | gfp f ≤ a ∧ p a} in
have p_s : p (Inf s),
  from inf s (assume a ⟨_, h⟩, h),
have gfp f ≤ Inf s,
  from gfp_le $ assume a, assume h : a ≤ f a, le_Inf $ assume b ⟨le_b, _⟩, le_trans (le_gfp h) le_b,
have Inf s = gfp f,
  from le_antisymm (le_gfp $ Inf_le
    ⟨le_trans (le_of_eq $ gfp_eq m) (m this), step _ p_s this⟩) this,
this ▸ p_s

theorem monotone_gfp : monotone (@gfp α _) :=
assume f g, assume : f ≤ g, gfp_le $ assume a, assume : a ≤ f a, le_gfp $ le_trans this (‹f ≤ g› a)

end fixedpoint

section fixedpoint_eqn
variables [complete_lattice α] [complete_lattice β] {f : β → α} {g : α → β}

-- Rolling rule
theorem lfp_comp (m_f : monotone f) (m_g : monotone g) : lfp (f ∘ g) = f (lfp (g ∘ f)) :=
le_antisymm
  (lfp_le $ m_f $ ge_of_eq $ lfp_eq $ m_g.comp m_f)
  (le_lfp $ assume a fg_le,
    le_trans (m_f $ lfp_le $ show (g ∘ f) (g a) ≤ g a, from m_g fg_le) fg_le)

theorem gfp_comp (m_f : monotone f) (m_g : monotone g) : gfp (f ∘ g) = f (gfp (g ∘ f)) :=
le_antisymm
  (gfp_le $ assume a fg_le,
    le_trans fg_le $ m_f $ le_gfp $ show g a ≤ (g ∘ f) (g a), from m_g fg_le)
  (le_gfp $ m_f $ le_of_eq $ gfp_eq $ m_g.comp m_f)

-- Diagonal rule
theorem lfp_lfp {h : α → α → α} (m : ∀⦃a b c d⦄, a ≤ b → c ≤ d → h a c ≤ h b d) :
  lfp (lfp ∘ h) = lfp (λx, h x x) :=
let f := lfp (lfp ∘ h) in
have f_eq : f = lfp (h f),
  from lfp_eq $ monotone.comp monotone_lfp (assume a b h x, m h (le_refl _)) ,
le_antisymm
  (lfp_le $ lfp_le $ ge_of_eq $ lfp_eq $ assume a b h, m h h)
  (lfp_le $ ge_of_eq $
    calc f = lfp (h f)       : f_eq
       ... = h f (lfp (h f)) : lfp_eq $ assume a b h, m (le_refl _) h
       ... = h f f           : congr_arg (h f) f_eq.symm)

theorem gfp_gfp {h : α → α → α} (m : ∀⦃a b c d⦄, a ≤ b → c ≤ d → h a c ≤ h b d) :
  gfp (gfp ∘ h) = gfp (λx, h x x) :=
let f := gfp (gfp ∘ h) in
have f_eq : f = gfp (h f),
  from gfp_eq $ monotone.comp monotone_gfp (assume a b h x, m h (le_refl _)),
le_antisymm
  (le_gfp $ le_of_eq $
    calc f = gfp (h f)       : f_eq
       ... = h f (gfp (h f)) : gfp_eq $ assume a b h, m (le_refl _) h
       ... = h f f           : congr_arg (h f) f_eq.symm)
  (le_gfp $ le_gfp $ le_of_eq $ gfp_eq $ assume a b h, m h h)

end fixedpoint_eqn

/- The complete lattice of fixed points of a function f -/
namespace fixed_points
variables [complete_lattice α] (f : α → α) (hf : monotone f)

def prev (x : α) : α := gfp (λ z, x ⊓ f z)
def next (x : α) : α := lfp (λ z, x ⊔ f z)

variable {f}

theorem prev_le {x : α} : prev f x ≤ x := gfp_le $ λ z hz, le_trans hz inf_le_left

lemma prev_eq (hf : monotone f) {a : α} (h : f a ≤ a) : prev f a = f (prev f a) :=
calc prev f a = a ⊓ f (prev f a) :
    gfp_eq $ show monotone (λz, a ⊓ f z), from assume x y h, inf_le_inf_left _ (hf h)
  ... = f (prev f a) :
    inf_of_le_right $ le_trans (hf prev_le) h

def prev_fixed (hf : monotone f) (a : α) (h : f a ≤ a) : fixed_points f :=
⟨prev f a, (prev_eq hf h).symm⟩

theorem next_le {x : α} : x ≤ next f x := le_lfp $ λ z hz, le_trans le_sup_left hz

lemma next_eq (hf : monotone f) {a : α} (h : a ≤ f a) : next f a = f (next f a) :=
calc next f a = a ⊔ f (next f a) :
    lfp_eq $ show monotone (λz, a ⊔ f z), from assume x y h, sup_le_sup_left (hf h) _
 ... = f (next f a) :
    sup_of_le_right $ le_trans h (hf next_le)

def next_fixed (hf : monotone f) (a : α) (h : a ≤ f a) : fixed_points f :=
⟨next f a, (next_eq hf h).symm⟩

variable f

theorem sup_le_f_of_fixed_points (x y : fixed_points f) : x.1 ⊔ y.1 ≤ f (x.1 ⊔ y.1) :=
sup_le
  (x.2 ▸ (hf $ show x.1 ≤ f x.1 ⊔ y.1, from x.2.symm ▸ le_sup_left))
  (y.2 ▸ (hf $ show y.1 ≤ x.1 ⊔ f y.1, from y.2.symm ▸ le_sup_right))

theorem f_le_inf_of_fixed_points (x y : fixed_points f) : f (x.1 ⊓ y.1) ≤ x.1 ⊓ y.1 :=
le_inf
  (x.2 ▸ (hf $ show f (x.1) ⊓ y.1 ≤ x.1, from x.2.symm ▸ inf_le_left))
  (y.2 ▸ (hf $ show x.1 ⊓ f (y.1) ≤ y.1, from y.2.symm ▸ inf_le_right))

theorem Sup_le_f_of_fixed_points (A : set α) (HA : A ⊆ fixed_points f) : Sup A ≤ f (Sup A) :=
Sup_le $ λ x hxA, (HA hxA) ▸ (hf $ le_Sup hxA)

theorem f_le_Inf_of_fixed_points (A : set α) (HA : A ⊆ fixed_points f) : f (Inf A) ≤ Inf A :=
le_Inf $ λ x hxA, (HA hxA) ▸ (hf $ Inf_le hxA)

/-- The fixed points of `f` form a complete lattice.
This cannot be an instance, since it depends on the monotonicity of `f`. -/
protected def complete_lattice : complete_lattice (fixed_points f) :=
{ le           := λx y, x.1 ≤ y.1,
  le_refl      := λ x, le_refl x,
  le_trans     := λ x y z, le_trans,
  le_antisymm  := λ x y hx hy, subtype.eq $ le_antisymm hx hy,

  sup          := λ x y, next_fixed hf (x.1 ⊔ y.1) (sup_le_f_of_fixed_points f hf x y),
  le_sup_left  := λ x y, show x.1 ≤ _, from le_trans le_sup_left next_le,
  le_sup_right := λ x y, show y.1 ≤ _, from le_trans le_sup_right next_le,
  sup_le       := λ x y z hxz hyz, lfp_le $ sup_le (sup_le hxz hyz) (z.2.symm ▸ le_refl z.1),

  inf          := λ x y, prev_fixed hf (x.1 ⊓ y.1) (f_le_inf_of_fixed_points f hf x y),
  inf_le_left  := λ x y, show _ ≤ x.1, from le_trans prev_le inf_le_left,
  inf_le_right := λ x y, show _ ≤ y.1, from le_trans prev_le inf_le_right,
  le_inf       := λ x y z hxy hxz, le_gfp $ le_inf (le_inf hxy hxz) (x.2.symm ▸ le_refl x),

  top          := prev_fixed hf ⊤ le_top,
  le_top       := λ ⟨x, H⟩, le_gfp $ le_inf le_top (H.symm ▸ le_refl x),

  bot          := next_fixed hf ⊥ bot_le,
  bot_le       := λ ⟨x, H⟩, lfp_le $ sup_le bot_le (H.symm ▸ le_refl x),

  Sup          := λ A, next_fixed hf (Sup $ subtype.val '' A)
    (Sup_le_f_of_fixed_points f hf (subtype.val '' A) (λ z ⟨x, hx⟩, hx.2 ▸ x.2)),
  le_Sup       := λ A x hxA, show x.1 ≤ _, from le_trans
    (le_Sup $ show x.1 ∈ subtype.val '' A, from ⟨x, hxA, rfl⟩)
    next_le,
  Sup_le       := λ A x Hx, lfp_le $ sup_le
    (Sup_le $ λ z ⟨y, hyA, hyz⟩, hyz ▸ Hx y hyA) (x.2.symm ▸ le_refl x),

  Inf          := λ A, prev_fixed hf (Inf $ subtype.val '' A)
    (f_le_Inf_of_fixed_points f hf (subtype.val '' A) (λ z ⟨x, hx⟩, hx.2 ▸ x.2)),
  le_Inf       := λ A x Hx, le_gfp $ le_inf
    (le_Inf $ λ z ⟨y, hyA, hyz⟩, hyz ▸ Hx y hyA) (x.2.symm ▸ le_refl x.1),
  Inf_le       := λ A x hxA, show _ ≤ x.1, from le_trans
    prev_le
    (Inf_le $ show x.1 ∈ subtype.val '' A, from ⟨x, hxA, rfl⟩) }

end fixed_points


import order.filter
import for_mathlib.boolean_subalgebra

/-
We say a filter F *decides* a set S if either S or Sᶜ belongs to F.
The sets decided by a filter form a boolean subalgebra.
For a fixed boolean subalgebra A, an *ultrafilter on A*
is a filter which decides every member of A.
To check that a filter F is an ultrafilter on A,
it then suffices to check that it decides the members
of a family of elements of A which generate it as a boolean subalgebra.

An ultrafilter in the mathlib sense is the same as
an ultrafilter on the boolean algebra of all sets.
-/

variables {α : Type*}

namespace filter

def decides (f : filter α) (s : set α) : Prop :=
s ∈ f ∨ sᶜ ∈ f

variables {f : filter α} {s t : set α}

lemma decides_empty : f.decides ∅ :=
or.inr (by { convert f.univ_sets, simp })

lemma decides.union (hs : f.decides s) (ht : f.decides t) :
  f.decides (s ∪ t) :=
begin
  -- If either s or t belongs to f, then so does s ∪ t
  cases hs,
  { left, apply f.sets_of_superset hs (set.subset_union_left _ _) },
  cases ht,
  { left, apply f.sets_of_superset ht (set.subset_union_right _ _) },
  -- Otherwise, both sᶜ and tᶜ belong to f
  right,
  rw set.compl_union,
  apply f.inter_sets hs ht
end

lemma decides.compl (hs : f.decides s) : f.decides (sᶜ) :=
begin
  unfold decides,
  rw [or_comm, set.compl_compl],
  exact hs
end

lemma is_boolean_subalgebra_decides : is_boolean_subalgebra f.decides :=
⟨decides_empty, λ _ _, decides.union, λ _, decides.compl⟩

/-- If a filter decides every member of S, then it also decides
every member of the boolean subalgebra generated by S. -/
lemma decides_gen {S : set (set α)} (h : ∀ s ∈ S, f.decides s) :
  ∀ {t : set α}, t ∈ boolean_subalgebra.gen S → f.decides t :=
(boolean_subalgebra.gen_subset_iff_subset is_boolean_subalgebra_decides).mpr h

end filter


def Nat.isZero (x : Nat) : Bool :=
  match x with
  | 0 => true
  | x+1 => false

example (x : Nat) : (1 + id x.succ.pred).isZero = false := by
  dsimp
  trace_state
  simp [Nat.succ_add]
  dsimp [Nat.isZero]

#check Nat.pred_succ

example (x : Nat) : (id x.succ.succ).isZero = false := by
  dsimp [Nat.isZero]

example (x : Nat) : (id x.succ.succ).isZero = false := by
  dsimp!

@[simp] theorem isZero_succ (x : Nat) : (x + 1).isZero = false :=
  rfl

theorem ex1 (x : Nat) : (id x.succ.succ.pred).isZero = false := by
  dsimp


/-
Copyright (c) 2021 Rémy Degenne. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Rémy Degenne, Kexing Ying
-/
import probability.notation
import probability.stopping

/-!
# Martingales

A family of functions `f : ι → α → E` is a martingale with respect to a filtration `ℱ` if every
`f i` is integrable, `f` is adapted with respect to `ℱ` and for all `i ≤ j`,
`μ[f j | ℱ.le i] =ᵐ[μ] f i`. On the other hand, `f : ι → α → E` is said to be a supermartingale
with respect to the filtration `ℱ` if `f i` is integrable, `f` is adapted with resepct to `ℱ`
and for all `i ≤ j`, `μ[f j | ℱ.le i] ≤ᵐ[μ] f i`. Finally, `f : ι → α → E` is said to be a
submartingale with respect to the filtration `ℱ` if `f i` is integrable, `f` is adapted with
resepct to `ℱ` and for all `i ≤ j`, `f i ≤ᵐ[μ] μ[f j | ℱ.le i]`.

The definitions of filtration and adapted can be found in `probability.stopping`.

### Definitions

* `measure_theory.martingale f ℱ μ`: `f` is a martingale with respect to filtration `ℱ` and
  measure `μ`.
* `measure_theory.supermartingale f ℱ μ`: `f` is a supermartingale with respect to
  filtration `ℱ` and measure `μ`.
* `measure_theory.submartingale f ℱ μ`: `f` is a submartingale with respect to filtration `ℱ` and
  measure `μ`.

### Results

* `measure_theory.martingale_condexp f ℱ μ`: the sequence `λ i, μ[f | ℱ i, ℱ.le i])` is a
  martingale with respect to `ℱ` and `μ`.

-/

open topological_space filter
open_locale nnreal ennreal measure_theory probability_theory big_operators

namespace measure_theory

variables {α E ι : Type*} [preorder ι]
  {m0 : measurable_space α} {μ : measure α}
  [normed_group E] [normed_space ℝ E] [complete_space E]
  {f g : ι → α → E} {ℱ : filtration ι m0} [sigma_finite_filtration μ ℱ]

/-- A family of functions `f : ι → α → E` is a martingale with respect to a filtration `ℱ` if `f`
is adapted with respect to `ℱ` and for all `i ≤ j`, `μ[f j | ℱ.le i] =ᵐ[μ] f i`. -/
def martingale (f : ι → α → E) (ℱ : filtration ι m0) (μ : measure α)
  [sigma_finite_filtration μ ℱ] : Prop :=
adapted ℱ f ∧ ∀ i j, i ≤ j → μ[f j | ℱ i, ℱ.le i] =ᵐ[μ] f i

/-- A family of integrable functions `f : ι → α → E` is a supermartingale with respect to a
filtration `ℱ` if `f` is adapted with respect to `ℱ` and for all `i ≤ j`,
`μ[f j | ℱ.le i] ≤ᵐ[μ] f i`. -/
def supermartingale [has_le E] (f : ι → α → E) (ℱ : filtration ι m0) (μ : measure α)
  [sigma_finite_filtration μ ℱ] : Prop :=
adapted ℱ f ∧ (∀ i j, i ≤ j → μ[f j | ℱ i, ℱ.le i] ≤ᵐ[μ] f i) ∧ ∀ i, integrable (f i) μ

/-- A family of integrable functions `f : ι → α → E` is a submartingale with respect to a
filtration `ℱ` if `f` is adapted with respect to `ℱ` and for all `i ≤ j`,
`f i ≤ᵐ[μ] μ[f j | ℱ.le i]`. -/
def submartingale [has_le E] (f : ι → α → E) (ℱ : filtration ι m0) (μ : measure α)
  [sigma_finite_filtration μ ℱ] : Prop :=
adapted ℱ f ∧ (∀ i j, i ≤ j → f i ≤ᵐ[μ] μ[f j | ℱ i, ℱ.le i]) ∧ ∀ i, integrable (f i) μ

variables (E)
lemma martingale_zero (ℱ : filtration ι m0) (μ : measure α) [sigma_finite_filtration μ ℱ] :
  martingale (0 : ι → α → E) ℱ μ :=
⟨adapted_zero E ℱ, λ i j hij, by { rw [pi.zero_apply, condexp_zero], simp, }⟩
variables {E}

namespace martingale

@[protected]
lemma adapted (hf : martingale f ℱ μ) : adapted ℱ f := hf.1

@[protected]
lemma strongly_measurable (hf : martingale f ℱ μ) (i : ι) : strongly_measurable[ℱ i] (f i) :=
hf.adapted i

lemma condexp_ae_eq (hf : martingale f ℱ μ) {i j : ι} (hij : i ≤ j) :
  μ[f j | ℱ i, ℱ.le i] =ᵐ[μ] f i :=
hf.2 i j hij

@[protected]
lemma integrable (hf : martingale f ℱ μ) (i : ι) : integrable (f i) μ :=
integrable_condexp.congr (hf.condexp_ae_eq (le_refl i))

lemma set_integral_eq (hf : martingale f ℱ μ) {i j : ι} (hij : i ≤ j) {s : set α}
  (hs : measurable_set[ℱ i] s) :
  ∫ x in s, f i x ∂μ = ∫ x in s, f j x ∂μ :=
begin
  rw ← @set_integral_condexp _ _ _ _ _ (ℱ i) m0 _ (ℱ.le i) _ _ _ (hf.integrable j) hs,
  refine set_integral_congr_ae (ℱ.le i s hs) _,
  filter_upwards [hf.2 i j hij] with _ heq _ using heq.symm,
end

lemma add (hf : martingale f ℱ μ) (hg : martingale g ℱ μ) : martingale (f + g) ℱ μ :=
begin
  refine ⟨hf.adapted.add hg.adapted, λ i j hij, _⟩,
  exact (condexp_add (hf.integrable j) (hg.integrable j)).trans
    ((hf.2 i j hij).add (hg.2 i j hij)),
end

lemma neg (hf : martingale f ℱ μ) : martingale (-f) ℱ μ :=
⟨hf.adapted.neg, λ i j hij, (condexp_neg (f j)).trans ((hf.2 i j hij).neg)⟩

lemma sub (hf : martingale f ℱ μ) (hg : martingale g ℱ μ) : martingale (f - g) ℱ μ :=
by { rw sub_eq_add_neg, exact hf.add hg.neg, }

lemma smul (c : ℝ) (hf : martingale f ℱ μ) : martingale (c • f) ℱ μ :=
begin
  refine ⟨hf.adapted.smul c, λ i j hij, _⟩,
  refine (condexp_smul c (f j)).trans ((hf.2 i j hij).mono (λ x hx, _)),
  rw [pi.smul_apply, hx, pi.smul_apply, pi.smul_apply],
end

lemma supermartingale [preorder E] (hf : martingale f ℱ μ) : supermartingale f ℱ μ :=
⟨hf.1, λ i j hij, (hf.2 i j hij).le, λ i, hf.integrable i⟩

lemma submartingale [preorder E] (hf : martingale f ℱ μ) : submartingale f ℱ μ :=
⟨hf.1, λ i j hij, (hf.2 i j hij).symm.le, λ i, hf.integrable i⟩

end martingale

lemma martingale_iff [partial_order E] : martingale f ℱ μ ↔
  supermartingale f ℱ μ ∧ submartingale f ℱ μ :=
⟨λ hf, ⟨hf.supermartingale, hf.submartingale⟩,
 λ ⟨hf₁, hf₂⟩, ⟨hf₁.1, λ i j hij, (hf₁.2.1 i j hij).antisymm (hf₂.2.1 i j hij)⟩⟩

lemma martingale_condexp (f : α → E) (ℱ : filtration ι m0) (μ : measure α)
  [sigma_finite_filtration μ ℱ] :
  martingale (λ i, μ[f | ℱ i, ℱ.le i]) ℱ μ :=
⟨λ i, strongly_measurable_condexp, λ i j hij, condexp_condexp_of_le (ℱ.mono hij) _⟩

namespace supermartingale

@[protected]
lemma adapted [has_le E] (hf : supermartingale f ℱ μ) : adapted ℱ f := hf.1

@[protected]
lemma strongly_measurable [has_le E] (hf : supermartingale f ℱ μ) (i : ι) :
  strongly_measurable[ℱ i] (f i) :=
hf.adapted i

@[protected]
lemma integrable [has_le E] (hf : supermartingale f ℱ μ) (i : ι) : integrable (f i) μ := hf.2.2 i

lemma condexp_ae_le [has_le E] (hf : supermartingale f ℱ μ) {i j : ι} (hij : i ≤ j) :
  μ[f j | ℱ i, ℱ.le i] ≤ᵐ[μ] f i :=
hf.2.1 i j hij

lemma set_integral_le {f : ι → α → ℝ} (hf : supermartingale f ℱ μ)
  {i j : ι} (hij : i ≤ j) {s : set α} (hs : measurable_set[ℱ i] s) :
  ∫ x in s, f j x ∂μ ≤ ∫ x in s, f i x ∂μ :=
begin
  rw ← set_integral_condexp (ℱ.le i) (hf.integrable j) hs,
  refine set_integral_mono_ae integrable_condexp.integrable_on (hf.integrable i).integrable_on _,
  filter_upwards [hf.2.1 i j hij] with _ heq using heq,
end

lemma add [preorder E] [covariant_class E E (+) (≤)]
  (hf : supermartingale f ℱ μ) (hg : supermartingale g ℱ μ) : supermartingale (f + g) ℱ μ :=
begin
  refine ⟨hf.1.add hg.1, λ i j hij, _, λ i, (hf.2.2 i).add (hg.2.2 i)⟩,
  refine (condexp_add (hf.integrable j) (hg.integrable j)).le.trans _,
  filter_upwards [hf.2.1 i j hij, hg.2.1 i j hij],
  intros,
  refine add_le_add _ _; assumption,
end

lemma add_martingale [preorder E] [covariant_class E E (+) (≤)]
  (hf : supermartingale f ℱ μ) (hg : martingale g ℱ μ) : supermartingale (f + g) ℱ μ :=
hf.add hg.supermartingale

lemma neg [preorder E] [covariant_class E E (+) (≤)]
  (hf : supermartingale f ℱ μ) : submartingale (-f) ℱ μ :=
begin
  refine ⟨hf.1.neg, λ i j hij, _, λ i, (hf.2.2 i).neg⟩,
  refine eventually_le.trans _ (condexp_neg (f j)).symm.le,
  filter_upwards [hf.2.1 i j hij] with _ _,
  simpa,
end

end supermartingale

namespace submartingale

@[protected]
lemma adapted [has_le E] (hf : submartingale f ℱ μ) : adapted ℱ f := hf.1

@[protected]
lemma strongly_measurable [has_le E] (hf : submartingale f ℱ μ) (i : ι) :
  strongly_measurable[ℱ i] (f i) :=
hf.adapted i

@[protected]
lemma integrable [has_le E] (hf : submartingale f ℱ μ) (i : ι) : integrable (f i) μ := hf.2.2 i

lemma ae_le_condexp [has_le E] (hf : submartingale f ℱ μ) {i j : ι} (hij : i ≤ j) :
  f i ≤ᵐ[μ] μ[f j | ℱ i, ℱ.le i] :=
hf.2.1 i j hij

lemma add [preorder E] [covariant_class E E (+) (≤)]
  (hf : submartingale f ℱ μ) (hg : submartingale g ℱ μ) : submartingale (f + g) ℱ μ :=
begin
  refine ⟨hf.1.add hg.1, λ i j hij, _, λ i, (hf.2.2 i).add (hg.2.2 i)⟩,
  refine eventually_le.trans _ (condexp_add (hf.integrable j) (hg.integrable j)).symm.le,
  filter_upwards [hf.2.1 i j hij, hg.2.1 i j hij],
  intros,
  refine add_le_add _ _; assumption,
end

lemma add_martingale [preorder E] [covariant_class E E (+) (≤)]
  (hf : submartingale f ℱ μ) (hg : martingale g ℱ μ) : submartingale (f + g) ℱ μ :=
hf.add hg.submartingale

lemma neg [preorder E] [covariant_class E E (+) (≤)]
  (hf : submartingale f ℱ μ) : supermartingale (-f) ℱ μ :=
begin
  refine ⟨hf.1.neg, λ i j hij, (condexp_neg (f j)).le.trans _, λ i, (hf.2.2 i).neg⟩,
  filter_upwards [hf.2.1 i j hij] with _ _,
  simpa,
end

lemma set_integral_le {f : ι → α → ℝ} (hf : submartingale f ℱ μ)
  {i j : ι} (hij : i ≤ j) {s : set α} (hs : measurable_set[ℱ i] s) :
  ∫ x in s, f i x ∂μ ≤ ∫ x in s, f j x ∂μ :=
begin
  rw [← neg_le_neg_iff, ← integral_neg, ← integral_neg],
  exact supermartingale.set_integral_le hf.neg hij hs,
end

lemma sub_supermartingale [preorder E] [covariant_class E E (+) (≤)]
  (hf : submartingale f ℱ μ) (hg : supermartingale g ℱ μ) : submartingale (f - g) ℱ μ :=
by { rw sub_eq_add_neg, exact hf.add hg.neg }

lemma sub_martingale [preorder E] [covariant_class E E (+) (≤)]
  (hf : submartingale f ℱ μ) (hg : martingale g ℱ μ) : submartingale (f - g) ℱ μ :=
hf.sub_supermartingale hg.supermartingale

end submartingale

namespace supermartingale

lemma sub_submartingale [preorder E] [covariant_class E E (+) (≤)]
  (hf : supermartingale f ℱ μ) (hg : submartingale g ℱ μ) : supermartingale (f - g) ℱ μ :=
by { rw sub_eq_add_neg, exact hf.add hg.neg }

lemma sub_martingale [preorder E] [covariant_class E E (+) (≤)]
  (hf : supermartingale f ℱ μ) (hg : martingale g ℱ μ) : supermartingale (f - g) ℱ μ :=
hf.sub_submartingale hg.submartingale

section

variables {F : Type*} [normed_lattice_add_comm_group F]
  [normed_space ℝ F] [complete_space F] [ordered_smul ℝ F]

lemma smul_nonneg {f : ι → α → F}
  {c : ℝ} (hc : 0 ≤ c) (hf : supermartingale f ℱ μ) :
  supermartingale (c • f) ℱ μ :=
begin
  refine ⟨hf.1.smul c, λ i j hij, _, λ i, (hf.2.2 i).smul c⟩,
  refine (condexp_smul c (f j)).le.trans _,
  filter_upwards [hf.2.1 i j hij] with _ hle,
  simp,
  exact smul_le_smul_of_nonneg hle hc,
end

lemma smul_nonpos {f : ι → α → F}
  {c : ℝ} (hc : c ≤ 0) (hf : supermartingale f ℱ μ) :
  submartingale (c • f) ℱ μ :=
begin
  rw [← neg_neg c, (by { ext i x, simp } : - -c • f = -(-c • f))],
  exact (hf.smul_nonneg $ neg_nonneg.2 hc).neg,
end

end

end supermartingale

namespace submartingale

section

variables {F : Type*} [normed_lattice_add_comm_group F]
  [normed_space ℝ F] [complete_space F] [ordered_smul ℝ F]

lemma smul_nonneg {f : ι → α → F}
  {c : ℝ} (hc : 0 ≤ c) (hf : submartingale f ℱ μ) :
  submartingale (c • f) ℱ μ :=
begin
  rw [← neg_neg c, (by { ext i x, simp } : - -c • f = -(c • -f))],
  exact supermartingale.neg (hf.neg.smul_nonneg hc),
end

lemma smul_nonpos {f : ι → α → F}
  {c : ℝ} (hc : c ≤ 0) (hf : submartingale f ℱ μ) :
  supermartingale (c • f) ℱ μ :=
begin
  rw [← neg_neg c, (by { ext i x, simp } : - -c • f = -(-c • f))],
  exact (hf.smul_nonneg $ neg_nonneg.2 hc).neg,
end

end

end submartingale

section nat

variables {𝒢 : filtration ℕ m0} [sigma_finite_filtration μ 𝒢]

namespace submartingale

lemma integrable_stopped_value [has_le E] {f : ℕ → α → E} (hf : submartingale f 𝒢 μ) {τ : α → ℕ}
  (hτ : is_stopping_time 𝒢 τ) {N : ℕ} (hbdd : ∀ x, τ x ≤ N) :
  integrable (stopped_value f τ) μ :=
integrable_stopped_value hτ hf.integrable hbdd

-- We may generalize the below lemma to functions taking value in a `normed_lattice_add_comm_group`.
-- Similarly, generalize `(super/)submartingale.set_integral_le`.

/-- Given a submartingale `f` and bounded stopping times `τ` and `π` such that `τ ≤ π`, the
expectation of `stopped_value f τ` is less than or equal to the expectation of `stopped_value f π`.
This is the forward direction of the optional stopping theorem. -/
lemma expected_stopped_value_mono {f : ℕ → α → ℝ} (hf : submartingale f 𝒢 μ) {τ π : α → ℕ}
  (hτ : is_stopping_time 𝒢 τ) (hπ : is_stopping_time 𝒢 π) (hle : τ ≤ π)
  {N : ℕ} (hbdd : ∀ x, π x ≤ N) :
  μ[stopped_value f τ] ≤ μ[stopped_value f π] :=
begin
  rw [← sub_nonneg, ← integral_sub', stopped_value_sub_eq_sum' hle hbdd],
  { simp only [finset.sum_apply],
    have : ∀ i, measurable_set[𝒢 i] {x : α | τ x ≤ i ∧ i < π x},
    { intro i,
      refine (hτ i).inter _,
      convert (hπ i).compl,
      ext x,
      simpa },
    rw integral_finset_sum,
    { refine finset.sum_nonneg (λ i hi, _),
      rw [integral_indicator (𝒢.le _ _ (this _)), integral_sub', sub_nonneg],
      { exact hf.set_integral_le (nat.le_succ i) (this _) },
      { exact (hf.integrable _).integrable_on },
      { exact (hf.integrable _).integrable_on } },
    intros i hi,
    exact integrable.indicator (integrable.sub (hf.integrable _) (hf.integrable _))
      (𝒢.le _ _ (this _)) },
  { exact hf.integrable_stopped_value hπ hbdd },
  { exact hf.integrable_stopped_value hτ (λ x, le_trans (hle x) (hbdd x)) }
end

end submartingale

end nat

end measure_theory