/-
Copyright (c) 2015 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura, Johannes Hölzl, Mario Carneiro
-/
import data.int.order.basic
import data.nat.size

/-!
# Square root of natural numbers

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

This file defines an efficient binary implementation of the square root function that returns the
unique `r` such that `r * r ≤ n < (r + 1) * (r + 1)`. It takes advantage of the binary
representation by replacing the multiplication by 2 appearing in
`(a + b)^2 = a^2 + 2 * a * b + b^2` by a bitmask manipulation.

## Reference

See [Wikipedia, *Methods of computing square roots*]
(https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Binary_numeral_system_(base_2)).
-/
namespace nat

theorem sqrt_aux_dec {b} (h : b ≠ 0) : shiftr b 2 < b :=
begin
  simp only [shiftr_eq_div_pow],
  apply (nat.div_lt_iff_lt_mul' (dec_trivial : 0 < 4)).2,
  have := nat.mul_lt_mul_of_pos_left
    (dec_trivial : 1 < 4) (nat.pos_of_ne_zero h),
  rwa mul_one at this
end

/-- Auxiliary function for `nat.sqrt`. See e.g.
<https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Binary_numeral_system_(base_2)> -/
def sqrt_aux : ℕ → ℕ → ℕ → ℕ
| b r n := if b0 : b = 0 then r else
  let b' := shiftr b 2 in
  have b' < b, from sqrt_aux_dec b0,
  match (n - (r + b : ℕ) : ℤ) with
  | (n' : ℕ) := sqrt_aux b' (div2 r + b) n'
  | _ := sqrt_aux b' (div2 r) n
  end

/-- `sqrt n` is the square root of a natural number `n`. If `n` is not a
  perfect square, it returns the largest `k:ℕ` such that `k*k ≤ n`. -/
@[pp_nodot] def sqrt (n : ℕ) : ℕ :=
match size n with
| 0      := 0
| succ s := sqrt_aux (shiftl 1 (bit0 (div2 s))) 0 n
end

theorem sqrt_aux_0 (r n) : sqrt_aux 0 r n = r :=
by rw sqrt_aux; simp
local attribute [simp] sqrt_aux_0

theorem sqrt_aux_1 {r n b} (h : b ≠ 0) {n'} (h₂ : r + b + n' = n) :
  sqrt_aux b r n = sqrt_aux (shiftr b 2) (div2 r + b) n' :=
by rw sqrt_aux; simp only [h, h₂.symm, int.coe_nat_add, if_false];
   rw [add_comm _ (n':ℤ), add_sub_cancel, sqrt_aux._match_1]

theorem sqrt_aux_2 {r n b} (h : b ≠ 0) (h₂ : n < r + b) :
  sqrt_aux b r n = sqrt_aux (shiftr b 2) (div2 r) n :=
begin
  rw sqrt_aux; simp only [h, h₂, if_false],
  cases int.eq_neg_succ_of_lt_zero
    (sub_lt_zero.2 (int.coe_nat_lt_coe_nat_of_lt h₂)) with k e,
  rw [e, sqrt_aux._match_1]
end

private def is_sqrt (n q : ℕ) : Prop := q*q ≤ n ∧ n < (q+1)*(q+1)

local attribute [-simp] mul_eq_mul_left_iff mul_eq_mul_right_iff

private lemma sqrt_aux_is_sqrt_lemma (m r n : ℕ)
  (h₁ : r*r ≤ n)
  (m') (hm : shiftr (2^m * 2^m) 2 = m')
  (H1 : n < (r + 2^m) * (r + 2^m) →
    is_sqrt n (sqrt_aux m' (r * 2^m) (n - r * r)))
  (H2 : (r + 2^m) * (r + 2^m) ≤ n →
    is_sqrt n (sqrt_aux m' ((r + 2^m) * 2^m) (n - (r + 2^m) * (r + 2^m)))) :
  is_sqrt n (sqrt_aux (2^m * 2^m) ((2*r)*2^m) (n - r*r)) :=
begin
  have b0 : 2 ^ m * 2 ^ m ≠ 0,
    from mul_self_ne_zero.2 (pow_ne_zero m two_ne_zero),
  have lb : n - r * r < 2 * r * 2^m + 2^m * 2^m ↔
            n < (r+2^m)*(r+2^m),
  { rw [tsub_lt_iff_right h₁],
    simp [left_distrib, right_distrib, two_mul, mul_comm, mul_assoc,
      add_comm, add_assoc, add_left_comm] },
  have re : div2 (2 * r * 2^m) = r * 2^m,
  { rw [div2_val, mul_assoc,
        nat.mul_div_cancel_left _ (dec_trivial:2>0)] },
  cases lt_or_ge n ((r+2^m)*(r+2^m)) with hl hl,
  { rw [sqrt_aux_2 b0 (lb.2 hl), hm, re], apply H1 hl },
  { cases le.dest hl with n' e,
    rw [@sqrt_aux_1 (2 * r * 2^m) (n-r*r) (2^m * 2^m) b0 (n - (r + 2^m) * (r + 2^m)),
      hm, re, ← right_distrib],
    { apply H2 hl },
    apply eq.symm, apply tsub_eq_of_eq_add_rev,
    rw [← add_assoc, (_ : r*r + _ = _)],
    exact (add_tsub_cancel_of_le hl).symm,
    simp [left_distrib, right_distrib, two_mul, mul_comm, mul_assoc, add_assoc] },
end

private lemma sqrt_aux_is_sqrt (n) : ∀ m r,
  r*r ≤ n → n < (r + 2^(m+1)) * (r + 2^(m+1)) →
  is_sqrt n (sqrt_aux (2^m * 2^m) (2*r*2^m) (n - r*r))
| 0 r h₁ h₂ := by apply sqrt_aux_is_sqrt_lemma 0 r n h₁ 0 rfl;
  intro h; simp; [exact ⟨h₁, h⟩, exact ⟨h, h₂⟩]
| (m+1) r h₁ h₂ := begin
    apply sqrt_aux_is_sqrt_lemma
      (m+1) r n h₁ (2^m * 2^m)
      (by simp [shiftr, pow_succ, div2_val, mul_comm, mul_left_comm];
          repeat {rw @nat.mul_div_cancel_left _ 2 dec_trivial});
      intro h,
    { have := sqrt_aux_is_sqrt m r h₁ h,
      simpa [pow_succ, mul_comm, mul_assoc] },
    { rw [pow_succ', mul_two, ← add_assoc] at h₂,
      have := sqrt_aux_is_sqrt m (r + 2^(m+1)) h h₂,
      rwa show (r + 2^(m + 1)) * 2^(m+1) = 2 * (r + 2^(m + 1)) * 2^m,
          by simp [pow_succ, mul_comm, mul_left_comm] }
  end

private lemma sqrt_is_sqrt (n : ℕ) : is_sqrt n (sqrt n) :=
begin
  generalize e : size n = s, cases s with s; simp [e, sqrt],
  { rw [size_eq_zero.1 e, is_sqrt], exact dec_trivial },
  { have := sqrt_aux_is_sqrt n (div2 s) 0 (zero_le _),
    simp [show 2^div2 s * 2^div2 s = shiftl 1 (bit0 (div2 s)), by
    { generalize: div2 s = x,
      change bit0 x with x+x,
      rw [one_shiftl, pow_add] }] at this,
    apply this,
    rw [← pow_add, ← mul_two], apply size_le.1,
    rw e, apply (@div_lt_iff_lt_mul _ _ 2 dec_trivial).1,
    rw [div2_val], apply lt_succ_self }
end

theorem sqrt_le (n : ℕ) : sqrt n * sqrt n ≤ n :=
(sqrt_is_sqrt n).left

theorem sqrt_le' (n : ℕ) : (sqrt n) ^ 2 ≤ n :=
eq.trans_le (sq (sqrt n)) (sqrt_le n)

theorem lt_succ_sqrt (n : ℕ) : n < succ (sqrt n) * succ (sqrt n) :=
(sqrt_is_sqrt n).right

theorem lt_succ_sqrt' (n : ℕ) : n < (succ (sqrt n)) ^ 2 :=
trans_rel_left (λ i j, i < j) (lt_succ_sqrt n) (sq (succ (sqrt n))).symm

theorem sqrt_le_add (n : ℕ) : n ≤ sqrt n * sqrt n + sqrt n + sqrt n :=
by rw ← succ_mul; exact le_of_lt_succ (lt_succ_sqrt n)

theorem le_sqrt {m n : ℕ} : m ≤ sqrt n ↔ m*m ≤ n :=
⟨λ h, le_trans (mul_self_le_mul_self h) (sqrt_le n),
 λ h, le_of_lt_succ $ mul_self_lt_mul_self_iff.2 $
   lt_of_le_of_lt h (lt_succ_sqrt n)⟩

theorem le_sqrt' {m n : ℕ} : m ≤ sqrt n ↔ m ^ 2 ≤ n :=
by simpa only [pow_two] using le_sqrt

theorem sqrt_lt {m n : ℕ} : sqrt m < n ↔ m < n*n :=
lt_iff_lt_of_le_iff_le le_sqrt

theorem sqrt_lt' {m n : ℕ} : sqrt m < n ↔ m < n ^ 2 :=
lt_iff_lt_of_le_iff_le le_sqrt'

theorem sqrt_le_self (n : ℕ) : sqrt n ≤ n :=
le_trans (le_mul_self _) (sqrt_le n)

theorem sqrt_le_sqrt {m n : ℕ} (h : m ≤ n) : sqrt m ≤ sqrt n :=
le_sqrt.2 (le_trans (sqrt_le _) h)

@[simp] lemma sqrt_zero : sqrt 0 = 0 :=
by rw [sqrt, size_zero, sqrt._match_1]

theorem sqrt_eq_zero {n : ℕ} : sqrt n = 0 ↔ n = 0 :=
⟨λ h, nat.eq_zero_of_le_zero $ le_of_lt_succ $ (@sqrt_lt n 1).1 $
  by rw [h]; exact dec_trivial,
 by { rintro rfl, simp }⟩

theorem eq_sqrt {n q} : q = sqrt n ↔ q*q ≤ n ∧ n < (q+1)*(q+1) :=
⟨λ e, e.symm ▸ sqrt_is_sqrt n,
 λ ⟨h₁, h₂⟩, le_antisymm (le_sqrt.2 h₁) (le_of_lt_succ $ sqrt_lt.2 h₂)⟩

theorem eq_sqrt' {n q} : q = sqrt n ↔ q ^ 2 ≤ n ∧ n < (q+1) ^ 2 :=
by simpa only [pow_two] using eq_sqrt

theorem le_three_of_sqrt_eq_one {n : ℕ} (h : sqrt n = 1) : n ≤ 3 :=
le_of_lt_succ $ (@sqrt_lt n 2).1 $
by rw [h]; exact dec_trivial

theorem sqrt_lt_self {n : ℕ} (h : 1 < n) : sqrt n < n :=
sqrt_lt.2 $ by
  have := nat.mul_lt_mul_of_pos_left h (lt_of_succ_lt h);
  rwa [mul_one] at this

theorem sqrt_pos {n : ℕ} : 0 < sqrt n ↔ 0 < n := le_sqrt

theorem sqrt_add_eq (n : ℕ) {a : ℕ} (h : a ≤ n + n) : sqrt (n*n + a) = n :=
le_antisymm
  (le_of_lt_succ $ sqrt_lt.2 $ by rw [succ_mul, mul_succ, add_succ, add_assoc];
    exact lt_succ_of_le (nat.add_le_add_left h _))
  (le_sqrt.2 $ nat.le_add_right _ _)

theorem sqrt_add_eq' (n : ℕ) {a : ℕ} (h : a ≤ n + n) : sqrt (n ^ 2 + a) = n :=
(congr_arg (λ i, sqrt (i + a)) (sq n)).trans (sqrt_add_eq n h)

theorem sqrt_eq (n : ℕ) : sqrt (n*n) = n :=
sqrt_add_eq n (zero_le _)

theorem sqrt_eq' (n : ℕ) : sqrt (n ^ 2) = n :=
sqrt_add_eq' n (zero_le _)

@[simp] lemma sqrt_one : sqrt 1 = 1 :=
sqrt_eq 1

theorem exists_mul_self (x : ℕ) :
  (∃ n, n * n = x) ↔ sqrt x * sqrt x = x :=
⟨λ ⟨n, hn⟩, by rw [← hn, sqrt_eq], λ h, ⟨sqrt x, h⟩⟩

theorem exists_mul_self' (x : ℕ) :
  (∃ n, n ^ 2 = x) ↔ (sqrt x) ^ 2 = x :=
by simpa only [pow_two] using exists_mul_self x

theorem sqrt_mul_sqrt_lt_succ (n : ℕ) : sqrt n * sqrt n < n + 1 :=
lt_succ_iff.mpr (sqrt_le _)

theorem sqrt_mul_sqrt_lt_succ' (n : ℕ) : (sqrt n) ^ 2 < n + 1 :=
lt_succ_iff.mpr (sqrt_le' _)

theorem succ_le_succ_sqrt (n : ℕ) : n + 1 ≤ (sqrt n + 1) * (sqrt n + 1) :=
le_of_pred_lt (lt_succ_sqrt _)

theorem succ_le_succ_sqrt' (n : ℕ) : n + 1 ≤ (sqrt n + 1) ^ 2 :=
le_of_pred_lt (lt_succ_sqrt' _)

/-- There are no perfect squares strictly between m² and (m+1)² -/
theorem not_exists_sq {n m : ℕ} (hl : m * m < n) (hr : n < (m + 1) * (m + 1)) :
  ¬ ∃ t, t * t = n :=
begin
  rintro ⟨t, rfl⟩,
  have h1 : m < t, from nat.mul_self_lt_mul_self_iff.mpr hl,
  have h2 : t < m + 1, from nat.mul_self_lt_mul_self_iff.mpr hr,
  exact (not_lt_of_ge $ le_of_lt_succ h2) h1
end

theorem not_exists_sq' {n m : ℕ} (hl : m ^ 2 < n) (hr : n < (m + 1) ^ 2) :
  ¬ ∃ t, t ^ 2 = n :=
  by simpa only [pow_two]
  using not_exists_sq (by simpa only [pow_two] using hl) (by simpa only [pow_two] using hr)

end nat


/- limits.lean

Limits of sequences.

Part of the M1P1 Lean project.

This file contains the basic definition of the limit of a sequence, and
proves basic properties about it.

It is full of comments in an attempt to make it more comprehensible to
mathematicians with little Lean experience, although by far the best
way to understand what is going on is to open the file within Lean 3.4.2
so you can check out the goal at each line -- this really helps understanding.
-/

-- The import below gives us a working copy of the real numbers ℝ,
-- and functions such as abs : ℝ → ℝ
import data.real.basic

-- This import has addition of functions, which we need for sums of limits.
import algebra.pi_instances

-- This next import gives us several tactics of use to mathematicians:
-- (1) norm_num [to prove basic facts about reals like 2+2 = 4]
-- (2) ring [to prove basic algebra identities like (a+b)^2 = a^2+2ab+b^2]
-- (3) linarith [to prove basic inequalities like x > 0 -> x/2 > 0]
import tactic.linarith

--import topology.basic
-- Ken Lee wanted to import this
--import analysis.exponential

-- These lines switch Lean into "maths proof mode" -- don't worry about them.
-- Basically they tell Lean to use the axiom of choice and the
-- law of the excluded middle, two standard maths facts which we
-- assume all the time in maths, usually without comment.

-- This imports Patrick's extension obv_ineq of linarith which solves
-- "obvious inequalities", and maybe other things too.
import xenalib.M1P1

noncomputable theory
local attribute [instance, priority 0] classical.prop_decidable

-- Let's also put things into an M1P1 namespace so we can define
-- stuff which is already defined in mathlib without breaking anything.
namespace M1P1

-- the maths starts here.

-- We introduce the usual mathematical notation for absolute value
notation `|` x `|` := abs x

-- We model a sequence a₀, a₁, a₂,... of real numbers as a function
-- from ℕ := {0,1,2,...} to ℝ, sending n to aₙ . So in the below
-- definition of the limit of a sequence, a : ℕ → ℝ is the sequence.

-- We first formalise the definition of "aₙ → l as n → ∞"
definition is_limit (a : ℕ → ℝ) (l : ℝ) : Prop :=
∀ ε > 0, ∃ N, ∀ n ≥ N, | a n - l | < ε

-- A sequence converges if and only if it has a limit. The difference
-- with this definition is that we don't specify the limit, we just
-- claim that it exists.
definition has_limit (a : ℕ → ℝ) : Prop := ∃ l : ℝ, is_limit a l

lemma tendsto_const (a : ℝ) : is_limit (λ n, a) a :=
begin
  intros ε εpos,
  use 0,
  intros n _,
  simpa using εpos
end

local attribute [-simp] sub_eq_add_neg
local attribute [simp] sub_zero

-- We will need an easy reformulation of the limit definition
lemma tendsto_iff_sub_tendsto_zero {a : ℕ → ℝ} {l : ℝ} :
  is_limit (λ n, a n - l) 0 ↔ is_limit a l :=
begin
  split ;
  { intros h ε εpos,
    rcases h ε εpos with ⟨N, H⟩,
    use N,
    intros n hn,
    simpa using H n hn }
end

-- In the definition of a limit, the final ε can be replaced
-- by a constant multiple of ε. We could assume this constant is positive
-- but we don't want to deal with this when applying the lemma.
lemma tendsto_of_mul_eps (a : ℕ → ℝ) (l : ℝ) (A : ℝ)
  (h : ∀ ε > 0, ∃ N, ∀ n ≥ N, | a n - l | < A*ε) : is_limit a l :=
begin
  -- Let ε be any positive number
  intros ε εpos,
  -- A is either non positive or positive
  cases le_or_gt A 0 with Anonpos Apos,
  { -- If A is non positive then our assumed bound quickly
    -- gives a contradiction.
    exfalso,
    -- Indeed we can apply our assumption to ε = 1 to get N such that
    -- ∀ (n : ℕ), n ≥ N → |a n - l| < A * 1
    rcases h 1 (by linarith) with ⟨N, H⟩,
    -- in particular this holds when n = N
    specialize H N (by linarith),
    -- but |a N - l| ≥ 0 so we get a contradiction
    have : |a N - l| ≥ 0, from abs_nonneg _,
    linarith },
  { -- Now assume A is positive. Our assumption h gives N such that
    -- ∀ n ≥ N, |a n - l| < A * (ε / A)
    rcases h (ε/A) (div_pos εpos Apos) with ⟨N, H⟩,
    -- we can simplify that A * (ε / A) and we are done.
    rw mul_div_cancel' _ (ne_of_gt Apos) at H,
    tauto }
end

-- We now start on the proof of the theorem that if a sequence has
-- two limits, they are equal.

-- Next lemma could be either hidden of given a user-friendly proof
lemma zero_of_abs_lt_all (x : ℝ) (h : ∀ ε > 0, |x| < ε) : x = 0 :=
eq_zero_of_abs_eq_zero $ eq_of_le_of_forall_le_of_dense (abs_nonneg x) $ λ ε ε_pos, le_of_lt (h ε ε_pos)

-- We're ready to prove the theorem.
theorem limits_are_unique (a : ℕ → ℝ) (l m : ℝ) (hl : is_limit a l)
(hm : is_limit a m) : l = m :=
begin
  -- Let prove |l - m| is smaller than any positive number, since that will easily imply l = m
  suffices : ∀ ε : ℝ, ε > 0 → |l - m| < ε,
    from eq_of_sub_eq_zero (zero_of_abs_lt_all _ this),
  -- Let ε be any positive number, and let's prove |l - m| < ε
  intros ε ε_pos,
  -- Because aₙ → l, there exists Nₗ such that n ≥ Nₗ → |aₙ - l| < ε/2
  cases hl (ε/2) (by obvious_ineq) with Nₗ Hₗ,
  -- Because aₙ → m, there exists Nₘ such that n ≥ Nₘ → |aₙ - m| < ε/2
  cases hm (ε/2) (by obvious_ineq) with Nₘ Hₘ,
  -- The trick is to let N be the max of Nₗ and Nₘ
  let N := max Nₗ Nₘ,
  -- Now clearly N ≥ Nₗ...
  have H₁ : Nₗ ≤ N := by obvious_ineq,
  -- ... so |a_N - l| < ε/2
  have H : | a N - l| < ε/2 := Hₗ N H₁,
  -- similarly N ≥ Nₘ...
  have H₂ : Nₘ ≤ N := by obvious_ineq,
  -- ... so |a_N - m| < ε/2 too
  have H' : | a N - m| < ε/2 := Hₘ N H₂,
  -- We now combine
  calc
    |l - m| = |(l - a N) + (a N - m)| : by ring
        ... ≤ |l - a N| + |a N - m|   : abs_add _ _
        ... = |a N - l | + |a N - m|  : by rw abs_sub
        ... < ε/2 + ε/2               : by obvious_ineq
        ... = ε                       : by ring,
end

-- We now prove that if aₙ → l and bₙ → m then aₙ + bₙ → l + m.
theorem tendsto_add (a : ℕ → ℝ) (b : ℕ → ℝ) (l m : ℝ)
  (h1 : is_limit a l) (h2 : is_limit b m) :
  is_limit (a + b) (l + m) :=
begin
  apply tendsto_of_mul_eps,
  -- let epsilon be a positive real
  intros ε Hε,
  -- Choose large M₁ such that n ≥ M₁ implies |a n - l| < ε
  cases (h1 ε Hε) with M₁ HM₁,
  -- similarly choose M₂ for the b sequence.
  cases (h2 ε Hε) with M₂ HM₂,
  -- let N be the max of M1 and M2
  let N := max M₁ M₂,
  -- and let's use that
  use N,
  -- Of course N ≥ M₁ and N ≥ M₂
  have H₁ : N ≥ M₁ := by obvious_ineq,
  have H₂ : N ≥ M₂ := by obvious_ineq,
  -- Now say n ≥ N.
  intros n Hn,
  -- Then obviously n ≥ M₁...
  have Hn₁ : n ≥ M₁ := by linarith,
  -- ...so |aₙ - l| < ε
  have H3 : |a n - l| < ε := HM₁ n Hn₁,
  -- and similarly n ≥ M₂, so |bₙ - l| < ε
  have H4 : |b n - m| < ε := HM₂ n (by linarith),
  -- And now we can estimate (|aₙ + bₙ - (l + m)| < 2ε)
                               -- First do some obvious algebra
  calc |(a + b) n - (l + m)| = |a n + b n - (l + m)| : rfl
  ...                        = |(a n - l) + (b n - m)| : by congr' 1; ring
                               -- now use the triangle inequality
  ...                        ≤ |(a n - l)| + |(b n - m)| : abs_add _ _
                               -- and our assumptions
  ...                        < ε + ε : by linarith
                               -- and a bit more algebra
  ...                        = 2*ε : by ring
end

-- A sequence (aₙ) is *bounded* if there exists some real number B such that |aₙ| ≤ B for all n≥0.

definition has_bound (a : ℕ → ℝ) (B : ℝ) := ∀ n, |a n| ≤ B
definition is_bounded (a : ℕ → ℝ) := ∃ B, has_bound a B

-- We really have a problem with that |.| notation
-- The following lemma is obvious
lemma has_bound_const (m : ℝ): has_bound (λ n, m) $ |m|  :=
assume n, by simp

-- A convergent sequence is bounded.
open finset
theorem bounded_of_convergent (a : ℕ → ℝ) (Ha : has_limit a) : is_bounded a :=
begin
  -- let l be the limit of the sequence.
  cases Ha with l Hl,
  -- By definition, there exist some N such that n ≥ N → |aₙ - l| < 1
  cases Hl 1 (zero_lt_one) with N HN,
  -- Let X be {|a₀|, |a₁|, ... , |a_N|}...
  let X := image (abs ∘ a) (range (N + 1)),
  -- ...let's remark that |a₀| ∈ X so X ≠ ∅ while we're here...
  have H2 : |a 0| ∈ X := mem_image_of_mem _ (mem_range.2 (nat.zero_lt_succ _)),
  have H3 : X ≠ ∅ := ne_empty_of_mem H2,
  -- ...and let B₀ be the max of X.
  let B₀ := max' X H3,
  -- If n ≤ N then |aₙ| ≤ B₀.
  have HB₀ : ∀ n ≤ N, |a n| ≤ B₀ := λ n Hn, le_max' X H3 _
    (mem_image_of_mem _ (mem_range.2 (nat.lt_succ_of_le Hn))),
  -- So now let B = max {B₀, |l| + 1}
  let B := max B₀ ( |l| + 1),
  -- and we claim this bound works, i.e. |aₙ| ≤ B for all n ∈ ℕ.
  use B,
  -- Because if n ∈ ℕ,
  intro n,
  -- then either n ≤ N or n > N.
  cases le_or_gt n N with Hle Hgt,
  { -- if n ≤ N, then |aₙ| ≤ B₀
    have h : |a n| ≤ B₀ := HB₀ n Hle,
    -- and B₀ ≤ B
    have h2 : B₀ ≤ B := le_max_left _ _,
    -- so we're done
    linarith },
  { -- and if n > N, then |aₙ - l| < 1...
    have h : |a n - l| < 1 := HN n (le_of_lt Hgt),
    -- ...so |aₙ| < 1 + |l|...
    have h2 : |a n| < |l| + 1,
      -- todo (kmb) -- remove linarith bug workaround
      revert h,unfold abs,unfold max,split_ifs;intros;linarith {restrict_type := ℝ},
    -- ...which is ≤ B
    have h3 : |l| + 1 ≤ B := le_max_right _ _,
    -- ...so we're done in this case too
    linarith
  }
end

-- more convenient theorem: a sequence with a limit
-- is bounded in absolute value by a positive real.
theorem bounded_pos_of_convergent (a : ℕ → ℝ) (Ha : has_limit a) :
∃ B : ℝ, B > 0 ∧ has_bound a B :=
begin
  -- We know the sequence is bounded. Say it's bounded by B₀.
  cases bounded_of_convergent a Ha with B₀ HB₀,
  -- let B = |B₀| + 1; this bound works.
  let B := |B₀| + 1,
  use B,
  -- B is obviously positive
  split,
  { -- (because 1 > 0...
    apply lt_of_lt_of_le zero_lt_one,
    show 1 ≤ |B₀| + 1,
    apply le_add_of_nonneg_left',
    -- ... and |B₀| ≥ 0)
    exact abs_nonneg _
  },
  -- so it suffices to prove B is a bound.
  -- If n is a natural
  intro n,
  -- then |aₙ| ≤ B₀
  apply le_trans (HB₀ n),
  -- so it suffices to show B₀ ≤ |B₀| + 1
  show B₀ ≤ |B₀| + 1,
  -- which is obvious.
  apply le_trans (le_abs_self B₀),
  simp [zero_le_one],
end

lemma tendsto_bounded_mul_zero {a : ℕ → ℝ} {b : ℕ → ℝ} {A : ℝ} (Apos : A > 0)
  (hA : has_bound a A) (hB : is_limit b 0)
  : is_limit (a*b) 0 :=
begin
  -- Let's apply our variant of the definition of limits where the final
  -- ε gets multiplied by some constant to be determined
  apply tendsto_of_mul_eps,
  -- Let ε be any positive number
  intros ε εpos,
  -- by assumption hB, we get some N such that
  -- ∀ (n : ℕ), n ≥ N → |b n| < ε
  cases hB ε εpos with N H,
  simp at H,
  -- Let's use that N
  use N,
  -- And compute for any n ≥ N
  intros n nN,
  calc
  |(a * b) n - 0| = |a n * b n|    : by simp
              ... = |a n| * |b n|  : abs_mul _ _
              ... ≤ A*|b n|        : mul_le_mul_of_nonneg_right (hA n) (abs_nonneg _)
              ... < A*ε            : mul_lt_mul_of_pos_left (H n nN) Apos
end

-- The limit of the product is the product of the limits.
-- If aₙ → l and bₙ → m then aₙ * bₙ → l * m.
theorem tendsto_mul (a : ℕ → ℝ) (b : ℕ → ℝ) (l m : ℝ)
  (h1 : is_limit a l) (h2 : is_limit b m) :
  is_limit (a * b) (l * m) :=
begin
  -- We apply the difference criterion so we need to prove a*b - l*m goes to zero
  rw ←tendsto_iff_sub_tendsto_zero,

  -- The key idea is to introduce (a_n - l) and (b_n - m) in this difference
  have key : (λ n, (a*b) n - l*m) = (λ n, (a n)*(b n - m) + m*(a n - l)),
    simp, ext, ring,
  rw key,

  -- By addition of limit, it then suffices to prove a_n * (b_n - m) and m*(a_n - l)
  -- both go to zero
  suffices : is_limit (λ n, a n * (b n - m)) 0 ∧ is_limit (λ n, m * (a n - l)) 0,
  { rw [show (0 : ℝ) = 0 + 0, by simp],
    exact tendsto_add _ _ _ _ this.left this.right},
  -- Let's tacke one after the other
  split,
  { -- Since a is convergent, it's bounded by some positive A
    rcases bounded_pos_of_convergent a ⟨l, h1⟩ with ⟨A, A_pos, hA⟩,
    -- We can reformulate the b convergence assumption as b_n - m goes to zero.
    have limb : is_limit (λ n, b n - m) 0,
     from tendsto_iff_sub_tendsto_zero.2 h2,
    -- So we can conclude using our lemma about product of a bounded sequence and a
    -- sequence converging to zero
    exact tendsto_bounded_mul_zero A_pos hA limb },
  { -- It remains to prove m * (a_n - l) goes to zero
    -- If m = 0 this is obvious.
    by_cases Hm : m = 0,
    { simp [Hm, tendsto_const] },
    -- Otherwise we follow the same strategy as above.
    { -- We reformulate our convergence assumption on a as a_n - l goes to zero
      have lima : is_limit (λ n, a n - l) 0,
        from tendsto_iff_sub_tendsto_zero.2 h1,
      -- and conclude using the same lemma
      exact tendsto_bounded_mul_zero (abs_pos_iff.2 Hm) (has_bound_const m) lima } }
end

-- If aₙ → l and bₙ → m, and aₙ ≤ bₙ for all n, then l ≤ m
theorem tendsto_le_of_le (a : ℕ → ℝ) (b : ℕ → ℝ)
  (l : ℝ) (m : ℝ) (hl : is_limit a l) (hm : is_limit b m)
  (hle : ∀ n, a n ≤ b n) : l ≤ m :=
begin
  -- Assume for a contradiction that m < l
  apply le_of_not_lt,
  intro hlt,
  -- Let ε be (l - m) / 2...
  let ε := (l - m) /2,
  -- ...and note that it's positive
  have Hε : ε > 0 := show (l - m) / 2 > 0 , by linarith,
  -- Choose Na s.t.  |aₙ - l| < ε for n ≥ Na
  cases hl ε Hε with Na HNa,
  have Hε : ε > 0 := show (l - m) / 2 > 0 , by linarith,
  -- Choose Nb s.t.  |bₙ - m| < ε for n ≥ Nb
  cases hm ε Hε with Nb HNb,
  -- let N be the max of Na and Nb...
  let N := max Na Nb,
  -- ...and note N ≥ Na and N ≥ Nb,
  have HNa' : Na ≤ N := le_max_left _ _,
  have HNb' : Nb ≤ N := le_max_right _ _,
  -- ... so |a_N - l| < ε and |b_N - m| < ε
  have Hl' : |a N - l| < ε := HNa N HNa',
  have Hm' : |b N - m| < ε := HNb N HNb',
  -- ... and also a_N ≤ b_N.
  have HN : a N ≤ b N := hle N,
  -- This is probably a contradiction.
  have Hε : ε = (l - m) / 2 := rfl,
  revert Hl' Hm',
  unfold abs,unfold max,split_ifs;intros;linarith
end

end M1P1


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

import data.finset.nat_antidiagonal
import algebra.big_operators.basic

/-!
# Big operators for `nat_antidiagonal`

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

This file contains theorems relevant to big operators over `finset.nat.antidiagonal`.
-/

open_locale big_operators

variables {M N : Type*} [comm_monoid M] [add_comm_monoid N]

namespace finset
namespace nat

lemma prod_antidiagonal_succ {n : ℕ} {f : ℕ × ℕ → M} :
  ∏ p in antidiagonal (n + 1), f p = f (0, n + 1) * ∏ p in antidiagonal n, f (p.1 + 1, p.2) :=
begin
  rw [antidiagonal_succ, prod_cons, prod_map], refl,
end

lemma sum_antidiagonal_succ {n : ℕ} {f : ℕ × ℕ → N} :
  ∑ p in antidiagonal (n + 1), f p = f (0, n + 1) + ∑ p in antidiagonal n, f (p.1 + 1, p.2) :=
@prod_antidiagonal_succ (multiplicative N) _ _ _

@[to_additive]
lemma prod_antidiagonal_swap {n : ℕ} {f : ℕ × ℕ → M} :
  ∏ p in antidiagonal n, f p.swap = ∏ p in antidiagonal n, f p :=
by { nth_rewrite 1 ← map_swap_antidiagonal, rw [prod_map], refl }

lemma prod_antidiagonal_succ' {n : ℕ} {f : ℕ × ℕ → M} :
  ∏ p in antidiagonal (n + 1), f p = f (n + 1, 0) * ∏ p in antidiagonal n, f (p.1, p.2 + 1) :=
begin
  rw [← prod_antidiagonal_swap, prod_antidiagonal_succ, ← prod_antidiagonal_swap],
  refl
end

lemma sum_antidiagonal_succ' {n : ℕ} {f : ℕ × ℕ → N} :
  ∑ p in antidiagonal (n + 1), f p = f (n + 1, 0) + ∑ p in antidiagonal n, f (p.1, p.2 + 1) :=
@prod_antidiagonal_succ' (multiplicative N) _ _ _

@[to_additive]
lemma prod_antidiagonal_subst {n : ℕ} {f : ℕ × ℕ → ℕ → M} :
  ∏ p in antidiagonal n, f p n = ∏ p in antidiagonal n, f p (p.1 + p.2) :=
prod_congr rfl $ λ p hp, by rw [nat.mem_antidiagonal.1 hp]

@[to_additive]
lemma prod_antidiagonal_eq_prod_range_succ_mk {M : Type*} [comm_monoid M] (f : ℕ × ℕ → M) (n : ℕ) :
  ∏ ij in finset.nat.antidiagonal n, f ij = ∏ k in range n.succ, f (k, n - k) :=
begin
  convert prod_map _ ⟨λ i, (i, n - i), λ x y h, (prod.mk.inj h).1⟩ _,
  refl,
end

/-- This lemma matches more generally than `finset.nat.prod_antidiagonal_eq_prod_range_succ_mk` when
using `rw ←`. -/
@[to_additive "This lemma matches more generally than
`finset.nat.sum_antidiagonal_eq_sum_range_succ_mk` when using `rw ←`."]
lemma prod_antidiagonal_eq_prod_range_succ {M : Type*} [comm_monoid M] (f : ℕ → ℕ → M) (n : ℕ) :
  ∏ ij in finset.nat.antidiagonal n, f ij.1 ij.2 = ∏ k in range n.succ, f k (n - k) :=
prod_antidiagonal_eq_prod_range_succ_mk _ _

end nat

end finset


/-
Copyright (c) 2020 Kexing Ying and Kevin Buzzard. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kexing Ying, Kevin Buzzard, Yury Kudryashov
-/
import algebra.big_operators.order
import algebra.indicator_function

/-!
# Finite products and sums over types and sets

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

We define products and sums over types and subsets of types, with no finiteness hypotheses.
All infinite products and sums are defined to be junk values (i.e. one or zero).
This approach is sometimes easier to use than `finset.sum`,
when issues arise with `finset` and `fintype` being data.

## Main definitions

We use the following variables:

* `α`, `β` - types with no structure;
* `s`, `t` - sets
* `M`, `N` - additive or multiplicative commutative monoids
* `f`, `g` - functions

Definitions in this file:

* `finsum f : M` : the sum of `f x` as `x` ranges over the support of `f`, if it's finite.
   Zero otherwise.

* `finprod f : M` : the product of `f x` as `x` ranges over the multiplicative support of `f`, if
   it's finite. One otherwise.

## Notation

* `∑ᶠ i, f i` and `∑ᶠ i : α, f i` for `finsum f`

* `∏ᶠ i, f i` and `∏ᶠ i : α, f i` for `finprod f`

This notation works for functions `f : p → M`, where `p : Prop`, so the following works:

* `∑ᶠ i ∈ s, f i`, where `f : α → M`, `s : set α` : sum over the set `s`;
* `∑ᶠ n < 5, f n`, where `f : ℕ → M` : same as `f 0 + f 1 + f 2 + f 3 + f 4`;
* `∏ᶠ (n >= -2) (hn : n < 3), f n`, where `f : ℤ → M` : same as `f (-2) * f (-1) * f 0 * f 1 * f 2`.

## Implementation notes

`finsum` and `finprod` is "yet another way of doing finite sums and products in Lean". However
experiments in the wild (e.g. with matroids) indicate that it is a helpful approach in settings
where the user is not interested in computability and wants to do reasoning without running into
typeclass diamonds caused by the constructive finiteness used in definitions such as `finset` and
`fintype`. By sticking solely to `set.finite` we avoid these problems. We are aware that there are
other solutions but for beginner mathematicians this approach is easier in practice.

Another application is the construction of a partition of unity from a collection of “bump”
function. In this case the finite set depends on the point and it's convenient to have a definition
that does not mention the set explicitly.

The first arguments in all definitions and lemmas is the codomain of the function of the big
operator. This is necessary for the heuristic in `@[to_additive]`.
See the documentation of `to_additive.attr` for more information.

We did not add `is_finite (X : Type) : Prop`, because it is simply `nonempty (fintype X)`.

## Tags

finsum, finprod, finite sum, finite product
-/

open function set

/-!
### Definition and relation to `finset.sum` and `finset.prod`
-/

section sort
variables {G M N : Type*} {α β ι : Sort*} [comm_monoid M] [comm_monoid N]

open_locale big_operators

section

/- Note: we use classical logic only for these definitions, to ensure that we do not write lemmas
with `classical.dec` in their statement. -/
open_locale classical

/-- Sum of `f x` as `x` ranges over the elements of the support of `f`, if it's finite. Zero
otherwise. -/
@[irreducible] noncomputable def finsum {M α} [add_comm_monoid M] (f : α → M) : M :=
if h : (support (f ∘ plift.down)).finite then ∑ i in h.to_finset, f i.down else 0

/-- Product of `f x` as `x` ranges over the elements of the multiplicative support of `f`, if it's
finite. One otherwise. -/
@[irreducible, to_additive]
noncomputable def finprod (f : α → M) : M :=
if h : (mul_support (f ∘ plift.down)).finite then ∏ i in h.to_finset, f i.down else 1

end

localized "notation (name := finsum)
  `∑ᶠ` binders `, ` r:(scoped:67 f, finsum f) := r" in big_operators

localized "notation (name := finprod)
  `∏ᶠ` binders `, ` r:(scoped:67 f, finprod f) := r" in big_operators

@[to_additive] lemma finprod_eq_prod_plift_of_mul_support_to_finset_subset
  {f : α → M} (hf : (mul_support (f ∘ plift.down)).finite) {s : finset (plift α)}
  (hs : hf.to_finset ⊆ s) :
  ∏ᶠ i, f i = ∏ i in s, f i.down :=
begin
  rw [finprod, dif_pos],
  refine finset.prod_subset hs (λ x hx hxf, _),
  rwa [hf.mem_to_finset, nmem_mul_support] at hxf
end

@[to_additive] lemma finprod_eq_prod_plift_of_mul_support_subset
  {f : α → M} {s : finset (plift α)} (hs : mul_support (f ∘ plift.down) ⊆ s) :
  ∏ᶠ i, f i = ∏ i in s, f i.down :=
finprod_eq_prod_plift_of_mul_support_to_finset_subset
  (s.finite_to_set.subset hs) $ λ x hx, by { rw finite.mem_to_finset at hx, exact hs hx }

@[simp, to_additive] lemma finprod_one : ∏ᶠ i : α, (1 : M) = 1 :=
begin
  have : mul_support (λ x : plift α, (λ _, 1 : α → M) x.down) ⊆ (∅ : finset (plift α)),
    from λ x h, h rfl,
  rw [finprod_eq_prod_plift_of_mul_support_subset this, finset.prod_empty]
end

@[to_additive] lemma finprod_of_is_empty [is_empty α] (f : α → M) : ∏ᶠ i, f i = 1 :=
by { rw ← finprod_one, congr }

@[simp, to_additive] lemma finprod_false (f : false → M) : ∏ᶠ i, f i = 1 :=
finprod_of_is_empty _

@[to_additive] lemma finprod_eq_single (f : α → M) (a : α) (ha : ∀ x ≠ a, f x = 1) :
  ∏ᶠ x, f x = f a :=
begin
  have : mul_support (f ∘ plift.down) ⊆ ({plift.up a} : finset (plift α)),
  { intro x, contrapose,
    simpa [plift.eq_up_iff_down_eq] using ha x.down },
  rw [finprod_eq_prod_plift_of_mul_support_subset this, finset.prod_singleton],
end

@[to_additive] lemma finprod_unique [unique α] (f : α → M) : ∏ᶠ i, f i = f default :=
finprod_eq_single f default $ λ x hx, (hx $ unique.eq_default _).elim

@[simp, to_additive] lemma finprod_true (f : true → M) : ∏ᶠ i, f i = f trivial :=
@finprod_unique M true _ ⟨⟨trivial⟩, λ _, rfl⟩ f

@[to_additive] lemma finprod_eq_dif {p : Prop} [decidable p] (f : p → M) :
  ∏ᶠ i, f i = if h : p then f h else 1 :=
begin
  split_ifs,
  { haveI : unique p := ⟨⟨h⟩, λ _, rfl⟩, exact finprod_unique f },
  { haveI : is_empty p := ⟨h⟩, exact finprod_of_is_empty f }
end

@[to_additive] lemma finprod_eq_if {p : Prop} [decidable p] {x : M} :
  ∏ᶠ i : p, x = if p then x else 1 :=
finprod_eq_dif (λ _, x)

@[to_additive] lemma finprod_congr {f g : α → M} (h : ∀ x, f x = g x) :
  finprod f = finprod g :=
congr_arg _ $ funext h

@[congr, to_additive] lemma finprod_congr_Prop {p q : Prop} {f : p → M} {g : q → M} (hpq : p = q)
  (hfg : ∀ h : q, f (hpq.mpr h) = g h) :
  finprod f = finprod g :=
by { subst q, exact finprod_congr hfg }

attribute [congr] finsum_congr_Prop

/-- To prove a property of a finite product, it suffices to prove that the property is
multiplicative and holds on the factors. -/
@[to_additive "To prove a property of a finite sum, it suffices to prove that the property is
additive and holds on the summands."]
lemma finprod_induction {f : α → M} (p : M → Prop) (hp₀ : p 1) (hp₁ : ∀ x y, p x → p y → p (x * y))
  (hp₂ : ∀ i, p (f i)) :
  p (∏ᶠ i, f i) :=
begin
  rw finprod,
  split_ifs,
  exacts [finset.prod_induction _ _ hp₁ hp₀ (λ i hi, hp₂ _), hp₀]
end

lemma finprod_nonneg {R : Type*} [ordered_comm_semiring R] {f : α → R} (hf : ∀ x, 0 ≤ f x) :
  0 ≤ ∏ᶠ x, f x :=
finprod_induction (λ x, 0 ≤ x) zero_le_one (λ x y, mul_nonneg) hf

@[to_additive finsum_nonneg]
lemma one_le_finprod' {M : Type*} [ordered_comm_monoid M] {f : α → M} (hf : ∀ i, 1 ≤ f i) :
  1 ≤ ∏ᶠ i, f i :=
finprod_induction _ le_rfl (λ _ _, one_le_mul) hf

@[to_additive] lemma monoid_hom.map_finprod_plift (f : M →* N) (g : α → M)
  (h : (mul_support $ g ∘ plift.down).finite) :
  f (∏ᶠ x, g x) = ∏ᶠ x, f (g x) :=
begin
  rw [finprod_eq_prod_plift_of_mul_support_subset h.coe_to_finset.ge,
    finprod_eq_prod_plift_of_mul_support_subset, f.map_prod],
  rw [h.coe_to_finset],
  exact mul_support_comp_subset f.map_one (g ∘ plift.down)
end

@[to_additive] lemma monoid_hom.map_finprod_Prop {p : Prop} (f : M →* N) (g : p → M) :
  f (∏ᶠ x, g x) = ∏ᶠ x, f (g x) :=
f.map_finprod_plift g (set.to_finite _)

@[to_additive] lemma monoid_hom.map_finprod_of_preimage_one (f : M →* N)
  (hf : ∀ x, f x = 1 → x = 1) (g : α → M) :
  f (∏ᶠ i, g i) = ∏ᶠ i, f (g i) :=
begin
  by_cases hg : (mul_support $ g ∘ plift.down).finite, { exact f.map_finprod_plift g hg },
  rw [finprod, dif_neg, f.map_one, finprod, dif_neg],
  exacts [infinite.mono (λ x hx, mt (hf (g x.down)) hx) hg, hg]
end

@[to_additive] lemma monoid_hom.map_finprod_of_injective (g : M →* N) (hg : injective g)
  (f : α → M) :
  g (∏ᶠ i, f i) = ∏ᶠ i, g (f i) :=
g.map_finprod_of_preimage_one (λ x, (hg.eq_iff' g.map_one).mp) f

@[to_additive] lemma mul_equiv.map_finprod (g : M ≃* N) (f : α → M) :
  g (∏ᶠ i, f i) = ∏ᶠ i, g (f i) :=
g.to_monoid_hom.map_finprod_of_injective g.injective f

lemma finsum_smul {R M : Type*} [ring R] [add_comm_group M] [module R M] [no_zero_smul_divisors R M]
  (f : ι → R) (x : M) :
  (∑ᶠ i, f i) • x = ∑ᶠ i, f i • x :=
begin
  rcases eq_or_ne x 0 with rfl|hx, { simp },
  exact ((smul_add_hom R M).flip x).map_finsum_of_injective (smul_left_injective R hx) _
end

lemma smul_finsum {R M : Type*} [ring R] [add_comm_group M] [module R M]
  [no_zero_smul_divisors R M] (c : R) (f : ι → M) :
  c • (∑ᶠ i, f i) = (∑ᶠ i, c • f i) :=
begin
  rcases eq_or_ne c 0 with rfl|hc, { simp },
  exact (smul_add_hom R M c).map_finsum_of_injective (smul_right_injective M hc) _
end

@[to_additive] lemma finprod_inv_distrib [division_comm_monoid G] (f : α → G) :
  ∏ᶠ x, (f x)⁻¹ = (∏ᶠ x, f x)⁻¹ :=
((mul_equiv.inv G).map_finprod f).symm

end sort

section type

variables {α β ι G M N : Type*} [comm_monoid M] [comm_monoid N]

open_locale big_operators

@[to_additive] lemma finprod_eq_mul_indicator_apply (s : set α)
  (f : α → M) (a : α) :
  ∏ᶠ (h : a ∈ s), f a = mul_indicator s f a :=
by convert finprod_eq_if

@[simp, to_additive] lemma finprod_mem_mul_support (f : α → M) (a : α) :
  ∏ᶠ (h : f a ≠ 1), f a = f a :=
by rw [← mem_mul_support, finprod_eq_mul_indicator_apply, mul_indicator_mul_support]

@[to_additive] lemma finprod_mem_def (s : set α) (f : α → M) :
  ∏ᶠ a ∈ s, f a = ∏ᶠ a, mul_indicator s f a :=
finprod_congr $ finprod_eq_mul_indicator_apply s f

@[to_additive] lemma finprod_eq_prod_of_mul_support_subset (f : α → M) {s : finset α}
  (h : mul_support f ⊆ s) :
  ∏ᶠ i, f i = ∏ i in s, f i :=
begin
  have A : mul_support (f ∘ plift.down) = equiv.plift.symm '' mul_support f,
  { rw mul_support_comp_eq_preimage,
    exact (equiv.plift.symm.image_eq_preimage _).symm },
  have : mul_support (f ∘ plift.down) ⊆ s.map equiv.plift.symm.to_embedding,
  { rw [A, finset.coe_map], exact image_subset _ h },
  rw [finprod_eq_prod_plift_of_mul_support_subset this],
  simp
end

@[to_additive] lemma finprod_eq_prod_of_mul_support_to_finset_subset (f : α → M)
  (hf : (mul_support f).finite) {s : finset α} (h : hf.to_finset ⊆ s) :
  ∏ᶠ i, f i = ∏ i in s, f i :=
finprod_eq_prod_of_mul_support_subset _ $ λ x hx, h $ hf.mem_to_finset.2 hx

@[to_additive] lemma finprod_eq_finset_prod_of_mul_support_subset
  (f : α → M) {s : finset α} (h : mul_support f ⊆ (s : set α)) :
  ∏ᶠ i, f i = ∏ i in s, f i :=
begin
  have h' : (s.finite_to_set.subset h).to_finset ⊆ s,
  { simpa [← finset.coe_subset, set.coe_to_finset], },
  exact finprod_eq_prod_of_mul_support_to_finset_subset _ _ h',
end

@[to_additive] lemma finprod_def (f : α → M) [decidable (mul_support f).finite] :
  ∏ᶠ i : α, f i = if h : (mul_support f).finite then ∏ i in h.to_finset, f i else 1 :=
begin
  split_ifs,
  { exact finprod_eq_prod_of_mul_support_to_finset_subset _ h (finset.subset.refl _) },
  { rw [finprod, dif_neg],
    rw [mul_support_comp_eq_preimage],
    exact mt (λ hf, hf.of_preimage equiv.plift.surjective) h}
end

@[to_additive] lemma finprod_of_infinite_mul_support {f : α → M} (hf : (mul_support f).infinite) :
  ∏ᶠ i, f i = 1 :=
by { classical, rw [finprod_def, dif_neg hf] }

@[to_additive] lemma finprod_eq_prod (f : α → M) (hf : (mul_support f).finite) :
  ∏ᶠ i : α, f i = ∏ i in hf.to_finset, f i :=
by { classical, rw [finprod_def, dif_pos hf] }

@[to_additive] lemma finprod_eq_prod_of_fintype [fintype α] (f : α → M) :
  ∏ᶠ i : α, f i = ∏ i, f i :=
finprod_eq_prod_of_mul_support_to_finset_subset _ (set.to_finite _) $ finset.subset_univ _

@[to_additive] lemma finprod_cond_eq_prod_of_cond_iff (f : α → M) {p : α → Prop} {t : finset α}
  (h : ∀ {x}, f x ≠ 1 → (p x ↔ x ∈ t)) :
  ∏ᶠ i (hi : p i), f i = ∏ i in t, f i :=
begin
  set s := {x | p x},
  have : mul_support (s.mul_indicator f) ⊆ t,
  { rw [set.mul_support_mul_indicator], intros x hx, exact (h hx.2).1 hx.1 },
  erw [finprod_mem_def, finprod_eq_prod_of_mul_support_subset _ this],
  refine finset.prod_congr rfl (λ x hx, mul_indicator_apply_eq_self.2 $ λ hxs, _),
  contrapose! hxs,
  exact (h hxs).2 hx
end

@[to_additive] lemma finprod_cond_ne (f : α → M) (a : α) [decidable_eq α]
  (hf : (mul_support f).finite) : (∏ᶠ i ≠ a, f i) = ∏ i in hf.to_finset.erase a, f i :=
begin
  apply finprod_cond_eq_prod_of_cond_iff,
  intros x hx,
  rw [finset.mem_erase, finite.mem_to_finset, mem_mul_support],
  exact ⟨λ h, and.intro h hx, λ h, h.1⟩
end

@[to_additive] lemma finprod_mem_eq_prod_of_inter_mul_support_eq (f : α → M) {s : set α}
  {t : finset α} (h : s ∩ mul_support f = t ∩ mul_support f) :
  ∏ᶠ i ∈ s, f i = ∏ i in t, f i :=
finprod_cond_eq_prod_of_cond_iff _ $ by simpa [set.ext_iff] using h

@[to_additive] lemma finprod_mem_eq_prod_of_subset (f : α → M) {s : set α} {t : finset α}
  (h₁ : s ∩ mul_support f ⊆ t) (h₂ : ↑t ⊆ s) :
  ∏ᶠ i ∈ s, f i = ∏ i in t, f i :=
finprod_cond_eq_prod_of_cond_iff _ $ λ x hx, ⟨λ h, h₁ ⟨h, hx⟩, λ h, h₂ h⟩

@[to_additive] lemma finprod_mem_eq_prod (f : α → M) {s : set α}
  (hf : (s ∩ mul_support f).finite) :
  ∏ᶠ i ∈ s, f i = ∏ i in hf.to_finset, f i :=
finprod_mem_eq_prod_of_inter_mul_support_eq _ $ by simp [inter_assoc]

@[to_additive] lemma finprod_mem_eq_prod_filter (f : α → M) (s : set α) [decidable_pred (∈ s)]
  (hf : (mul_support f).finite) :
  ∏ᶠ i ∈ s, f i = ∏ i in finset.filter (∈ s) hf.to_finset, f i :=
finprod_mem_eq_prod_of_inter_mul_support_eq _ $ by simp [inter_comm, inter_left_comm]

@[to_additive] lemma finprod_mem_eq_to_finset_prod (f : α → M) (s : set α) [fintype s] :
  ∏ᶠ i ∈ s, f i = ∏ i in s.to_finset, f i :=
finprod_mem_eq_prod_of_inter_mul_support_eq _ $ by rw [coe_to_finset]

@[to_additive] lemma finprod_mem_eq_finite_to_finset_prod (f : α → M) {s : set α} (hs : s.finite) :
  ∏ᶠ i ∈ s, f i = ∏ i in hs.to_finset, f i :=
finprod_mem_eq_prod_of_inter_mul_support_eq _ $ by rw [hs.coe_to_finset]

@[to_additive] lemma finprod_mem_finset_eq_prod (f : α → M) (s : finset α) :
  ∏ᶠ i ∈ s, f i = ∏ i in s, f i :=
finprod_mem_eq_prod_of_inter_mul_support_eq _ rfl

@[to_additive] lemma finprod_mem_coe_finset (f : α → M) (s : finset α) :
  ∏ᶠ i ∈ (s : set α), f i = ∏ i in s, f i :=
finprod_mem_eq_prod_of_inter_mul_support_eq _ rfl

@[to_additive] lemma finprod_mem_eq_one_of_infinite {f : α → M} {s : set α}
  (hs : (s ∩ mul_support f).infinite) : ∏ᶠ i ∈ s, f i = 1 :=
begin
  rw finprod_mem_def,
  apply finprod_of_infinite_mul_support,
  rwa [← mul_support_mul_indicator] at hs
end

@[to_additive]
lemma finprod_mem_eq_one_of_forall_eq_one {f : α → M} {s : set α} (h : ∀ x ∈ s, f x = 1) :
  ∏ᶠ i ∈ s, f i = 1 :=
by simp [h] {contextual := tt}

@[to_additive] lemma finprod_mem_inter_mul_support (f : α → M) (s : set α) :
  ∏ᶠ i ∈ (s ∩ mul_support f), f i = ∏ᶠ i ∈ s, f i :=
by rw [finprod_mem_def, finprod_mem_def, mul_indicator_inter_mul_support]

@[to_additive] lemma finprod_mem_inter_mul_support_eq (f : α → M) (s t : set α)
  (h : s ∩ mul_support f = t ∩ mul_support f) :
  ∏ᶠ i ∈ s, f i = ∏ᶠ i ∈ t, f i :=
by rw [← finprod_mem_inter_mul_support, h, finprod_mem_inter_mul_support]

@[to_additive] lemma finprod_mem_inter_mul_support_eq' (f : α → M) (s t : set α)
  (h : ∀ x ∈ mul_support f, x ∈ s ↔ x ∈ t) :
  ∏ᶠ i ∈ s, f i = ∏ᶠ i ∈ t, f i :=
begin
  apply finprod_mem_inter_mul_support_eq,
  ext x,
  exact and_congr_left (h x)
end

@[to_additive] lemma finprod_mem_univ (f : α → M) : ∏ᶠ i ∈ @set.univ α, f i = ∏ᶠ i : α, f i :=
finprod_congr $ λ i, finprod_true _

variables {f g : α → M} {a b : α} {s t : set α}

@[to_additive] lemma finprod_mem_congr (h₀ : s = t) (h₁ : ∀ x ∈ t, f x = g x) :
  ∏ᶠ i ∈ s, f i = ∏ᶠ i ∈ t, g i :=
h₀.symm ▸ (finprod_congr $ λ i, finprod_congr_Prop rfl (h₁ i))

@[to_additive]
lemma finprod_eq_one_of_forall_eq_one {f : α → M} (h : ∀ x, f x = 1) :
  ∏ᶠ i, f i = 1 :=
by simp [h] {contextual := tt}

/-!
### Distributivity w.r.t. addition, subtraction, and (scalar) multiplication
-/

/-- If the multiplicative supports of `f` and `g` are finite, then the product of `f i * g i` equals
the product of `f i` multiplied by the product of `g i`. -/
@[to_additive "If the additive supports of `f` and `g` are finite, then the sum of `f i + g i`
equals the sum of `f i` plus the sum of `g i`."]
lemma finprod_mul_distrib (hf : (mul_support f).finite) (hg : (mul_support g).finite) :
  ∏ᶠ i, f i * g i = (∏ᶠ i, f i) * ∏ᶠ i, g i :=
begin
  classical,
  rw [finprod_eq_prod_of_mul_support_to_finset_subset _ hf (finset.subset_union_left _ _),
    finprod_eq_prod_of_mul_support_to_finset_subset _ hg (finset.subset_union_right _ _),
    ← finset.prod_mul_distrib],
  refine finprod_eq_prod_of_mul_support_subset _ _,
  simp [mul_support_mul]
end

/-- If the multiplicative supports of `f` and `g` are finite, then the product of `f i / g i`
equals the product of `f i` divided by the product of `g i`. -/
@[to_additive "If the additive supports of `f` and `g` are finite, then the sum of `f i - g i`
equals the sum of `f i` minus the sum of `g i`."]
lemma finprod_div_distrib [division_comm_monoid G] {f g : α → G} (hf : (mul_support f).finite)
  (hg : (mul_support g).finite) :
  ∏ᶠ i, f i / g i = (∏ᶠ i, f i) / ∏ᶠ i, g i :=
by simp only [div_eq_mul_inv, finprod_mul_distrib hf ((mul_support_inv g).symm.rec hg),
              finprod_inv_distrib]

/-- A more general version of `finprod_mem_mul_distrib` that only requires `s ∩ mul_support f` and
`s ∩ mul_support g` rather than `s` to be finite. -/
@[to_additive "A more general version of `finsum_mem_add_distrib` that only requires `s ∩ support f`
and `s ∩ support g` rather than `s` to be finite."]
lemma finprod_mem_mul_distrib' (hf : (s ∩ mul_support f).finite) (hg : (s ∩ mul_support g).finite) :
  ∏ᶠ i ∈ s, f i * g i = (∏ᶠ i ∈ s, f i) * ∏ᶠ i ∈ s, g i :=
begin
  rw [← mul_support_mul_indicator] at hf hg,
  simp only [finprod_mem_def, mul_indicator_mul, finprod_mul_distrib hf hg]
end

/-- The product of the constant function `1` over any set equals `1`. -/
@[to_additive "The product of the constant function `0` over any set equals `0`."]
lemma finprod_mem_one (s : set α) : ∏ᶠ i ∈ s, (1 : M) = 1 := by simp

/-- If a function `f` equals `1` on a set `s`, then the product of `f i` over `i ∈ s` equals `1`. -/
@[to_additive "If a function `f` equals `0` on a set `s`, then the product of `f i` over `i ∈ s`
equals `0`."]
lemma finprod_mem_of_eq_on_one (hf : s.eq_on f 1) : ∏ᶠ i ∈ s, f i = 1 :=
by { rw ← finprod_mem_one s, exact finprod_mem_congr rfl hf }

/-- If the product of `f i` over `i ∈ s` is not equal to `1`, then there is some `x ∈ s` such that
`f x ≠ 1`. -/
@[to_additive "If the product of `f i` over `i ∈ s` is not equal to `0`, then there is some `x ∈ s`
such that `f x ≠ 0`."]
lemma exists_ne_one_of_finprod_mem_ne_one (h : ∏ᶠ i ∈ s, f i ≠ 1) : ∃ x ∈ s, f x ≠ 1 :=
begin
  by_contra' h',
  exact h (finprod_mem_of_eq_on_one h')
end

/-- Given a finite set `s`, the product of `f i * g i` over `i ∈ s` equals the product of `f i`
over `i ∈ s` times the product of `g i` over `i ∈ s`. -/
@[to_additive "Given a finite set `s`, the sum of `f i + g i` over `i ∈ s` equals the sum of `f i`
over `i ∈ s` plus the sum of `g i` over `i ∈ s`."]
lemma finprod_mem_mul_distrib (hs : s.finite) :
  ∏ᶠ i ∈ s, f i * g i = (∏ᶠ i ∈ s, f i) * ∏ᶠ i ∈ s, g i :=
finprod_mem_mul_distrib' (hs.inter_of_left _) (hs.inter_of_left _)

@[to_additive] lemma monoid_hom.map_finprod {f : α → M} (g : M →* N) (hf : (mul_support f).finite) :
  g (∏ᶠ i, f i) = ∏ᶠ i, g (f i) :=
g.map_finprod_plift f $ hf.preimage $ equiv.plift.injective.inj_on _

@[to_additive] lemma finprod_pow (hf : (mul_support f).finite) (n : ℕ) :
  (∏ᶠ i, f i) ^ n = ∏ᶠ i, f i ^ n :=
(pow_monoid_hom n).map_finprod hf

/-- A more general version of `monoid_hom.map_finprod_mem` that requires `s ∩ mul_support f` rather
than `s` to be finite. -/
@[to_additive "A more general version of `add_monoid_hom.map_finsum_mem` that requires
`s ∩ support f` rather than `s` to be finite."]
lemma monoid_hom.map_finprod_mem' {f : α → M} (g : M →* N) (h₀ : (s ∩ mul_support f).finite) :
  g (∏ᶠ j ∈ s, f j) = ∏ᶠ i ∈ s, g (f i) :=
begin
  rw [g.map_finprod],
  { simp only [g.map_finprod_Prop] },
  { simpa only [finprod_eq_mul_indicator_apply, mul_support_mul_indicator] }
end

/-- Given a monoid homomorphism `g : M →* N` and a function `f : α → M`, the value of `g` at the
product of `f i` over `i ∈ s` equals the product of `g (f i)` over `s`. -/
@[to_additive "Given an additive monoid homomorphism `g : M →* N` and a function `f : α → M`, the
value of `g` at the sum of `f i` over `i ∈ s` equals the sum of `g (f i)` over `s`."]
lemma monoid_hom.map_finprod_mem (f : α → M) (g : M →* N) (hs : s.finite) :
  g (∏ᶠ j ∈ s, f j) = ∏ᶠ i ∈ s, g (f i) :=
g.map_finprod_mem' (hs.inter_of_left _)

@[to_additive] lemma mul_equiv.map_finprod_mem (g : M ≃* N) (f : α → M) {s : set α}
  (hs : s.finite) : g (∏ᶠ i ∈ s, f i) = ∏ᶠ i ∈ s, g (f i) :=
g.to_monoid_hom.map_finprod_mem f hs

@[to_additive] lemma finprod_mem_inv_distrib [division_comm_monoid G] (f : α → G) (hs : s.finite) :
  ∏ᶠ x ∈ s, (f x)⁻¹ = (∏ᶠ x ∈ s, f x)⁻¹ :=
((mul_equiv.inv G).map_finprod_mem f hs).symm

/-- Given a finite set `s`, the product of `f i / g i` over `i ∈ s` equals the product of `f i`
over `i ∈ s` divided by the product of `g i` over `i ∈ s`. -/
@[to_additive "Given a finite set `s`, the sum of `f i / g i` over `i ∈ s` equals the sum of `f i`
over `i ∈ s` minus the sum of `g i` over `i ∈ s`."]
lemma finprod_mem_div_distrib [division_comm_monoid G] (f g : α → G) (hs : s.finite) :
  ∏ᶠ i ∈ s, f i / g i = (∏ᶠ i ∈ s, f i) / ∏ᶠ i ∈ s, g i :=
by simp only [div_eq_mul_inv, finprod_mem_mul_distrib hs, finprod_mem_inv_distrib g hs]

/-!
### `∏ᶠ x ∈ s, f x` and set operations
-/

/-- The product of any function over an empty set is `1`. -/
@[to_additive "The sum of any function over an empty set is `0`."]
lemma finprod_mem_empty : ∏ᶠ i ∈ (∅ : set α), f i = 1 := by simp

/-- A set `s` is nonempty if the product of some function over `s` is not equal to `1`. -/
@[to_additive "A set `s` is nonempty if the sum of some function over `s` is not equal to `0`."]
lemma nonempty_of_finprod_mem_ne_one (h : ∏ᶠ i ∈ s, f i ≠ 1) : s.nonempty :=
nonempty_iff_ne_empty.2 $ λ h', h $ h'.symm ▸ finprod_mem_empty

/-- Given finite sets `s` and `t`, the product of `f i` over `i ∈ s ∪ t` times the product of
`f i` over `i ∈ s ∩ t` equals the product of `f i` over `i ∈ s` times the product of `f i`
over `i ∈ t`. -/
@[to_additive "Given finite sets `s` and `t`, the sum of `f i` over `i ∈ s ∪ t` plus the sum of
`f i` over `i ∈ s ∩ t` equals the sum of `f i` over `i ∈ s` plus the sum of `f i` over `i ∈ t`."]
lemma finprod_mem_union_inter (hs : s.finite) (ht : t.finite) :
  (∏ᶠ i ∈ s ∪ t, f i) * ∏ᶠ i ∈ s ∩ t, f i = (∏ᶠ i ∈ s, f i) * ∏ᶠ i ∈ t, f i :=
begin
  lift s to finset α using hs, lift t to finset α using ht,
  classical,
  rw [← finset.coe_union, ← finset.coe_inter],
  simp only [finprod_mem_coe_finset, finset.prod_union_inter]
end

/-- A more general version of `finprod_mem_union_inter` that requires `s ∩ mul_support f` and
`t ∩ mul_support f` rather than `s` and `t` to be finite. -/
@[to_additive "A more general version of `finsum_mem_union_inter` that requires `s ∩ support f` and
`t ∩ support f` rather than `s` and `t` to be finite."] lemma finprod_mem_union_inter'
  (hs : (s ∩ mul_support f).finite) (ht : (t ∩ mul_support f).finite) :
  (∏ᶠ i ∈ s ∪ t, f i) * ∏ᶠ i ∈ s ∩ t, f i = (∏ᶠ i ∈ s, f i) * ∏ᶠ i ∈ t, f i :=
begin
  rw [← finprod_mem_inter_mul_support f s, ← finprod_mem_inter_mul_support f t,
    ← finprod_mem_union_inter hs ht, ← union_inter_distrib_right, finprod_mem_inter_mul_support,
    ← finprod_mem_inter_mul_support f (s ∩ t)],
  congr' 2,
  rw [inter_left_comm, inter_assoc, inter_assoc, inter_self, inter_left_comm]
end

/-- A more general version of `finprod_mem_union` that requires `s ∩ mul_support f` and
`t ∩ mul_support f` rather than `s` and `t` to be finite. -/
@[to_additive "A more general version of `finsum_mem_union` that requires `s ∩ support f` and
`t ∩ support f` rather than `s` and `t` to be finite."]
lemma finprod_mem_union' (hst : disjoint s t) (hs : (s ∩ mul_support f).finite)
  (ht : (t ∩ mul_support f).finite) :
  ∏ᶠ i ∈ s ∪ t, f i = (∏ᶠ i ∈ s, f i) * ∏ᶠ i ∈ t, f i :=
by rw [← finprod_mem_union_inter' hs ht, disjoint_iff_inter_eq_empty.1 hst, finprod_mem_empty,
  mul_one]

/-- Given two finite disjoint sets `s` and `t`, the product of `f i` over `i ∈ s ∪ t` equals the
product of `f i` over `i ∈ s` times the product of `f i` over `i ∈ t`. -/
@[to_additive "Given two finite disjoint sets `s` and `t`, the sum of `f i` over `i ∈ s ∪ t` equals
the sum of `f i` over `i ∈ s` plus the sum of `f i` over `i ∈ t`."]
lemma finprod_mem_union (hst : disjoint s t) (hs : s.finite) (ht : t.finite) :
  ∏ᶠ i ∈ s ∪ t, f i = (∏ᶠ i ∈ s, f i) * ∏ᶠ i ∈ t, f i :=
finprod_mem_union' hst (hs.inter_of_left _) (ht.inter_of_left _)

/-- A more general version of `finprod_mem_union'` that requires `s ∩ mul_support f` and
`t ∩ mul_support f` rather than `s` and `t` to be disjoint -/
@[to_additive "A more general version of `finsum_mem_union'` that requires `s ∩ support f` and
`t ∩ support f` rather than `s` and `t` to be disjoint"]
lemma finprod_mem_union'' (hst : disjoint (s ∩ mul_support f) (t ∩ mul_support f))
  (hs : (s ∩ mul_support f).finite) (ht : (t ∩ mul_support f).finite) :
  ∏ᶠ i ∈ s ∪ t, f i = (∏ᶠ i ∈ s, f i) * ∏ᶠ i ∈ t, f i :=
by rw [← finprod_mem_inter_mul_support f s, ← finprod_mem_inter_mul_support f t,
  ← finprod_mem_union hst hs ht, ← union_inter_distrib_right, finprod_mem_inter_mul_support]

/-- The product of `f i` over `i ∈ {a}` equals `f a`. -/
@[to_additive "The sum of `f i` over `i ∈ {a}` equals `f a`."]
lemma finprod_mem_singleton : ∏ᶠ i ∈ ({a} : set α), f i = f a :=
by rw [← finset.coe_singleton, finprod_mem_coe_finset, finset.prod_singleton]

@[simp, to_additive] lemma finprod_cond_eq_left : ∏ᶠ i = a, f i = f a :=
finprod_mem_singleton

@[simp, to_additive] lemma finprod_cond_eq_right : ∏ᶠ i (hi : a = i), f i = f a :=
by simp [@eq_comm _ a]

/-- A more general version of `finprod_mem_insert` that requires `s ∩ mul_support f` rather than `s`
to be finite. -/
@[to_additive "A more general version of `finsum_mem_insert` that requires `s ∩ support f` rather
than `s` to be finite."]
lemma finprod_mem_insert' (f : α → M) (h : a ∉ s) (hs : (s ∩ mul_support f).finite) :
  ∏ᶠ i ∈ insert a s, f i = f a * ∏ᶠ i ∈ s, f i :=
begin
  rw [insert_eq, finprod_mem_union' _ _ hs, finprod_mem_singleton],
  { rwa disjoint_singleton_left },
  { exact (finite_singleton a).inter_of_left _ }
end

/-- Given a finite set `s` and an element `a ∉ s`, the product of `f i` over `i ∈ insert a s` equals
`f a` times the product of `f i` over `i ∈ s`. -/
@[to_additive "Given a finite set `s` and an element `a ∉ s`, the sum of `f i` over `i ∈ insert a s`
equals `f a` plus the sum of `f i` over `i ∈ s`."]
lemma finprod_mem_insert (f : α → M) (h : a ∉ s) (hs : s.finite) :
  ∏ᶠ i ∈ insert a s, f i = f a * ∏ᶠ i ∈ s, f i :=
finprod_mem_insert' f h $ hs.inter_of_left _

/-- If `f a = 1` when `a ∉ s`, then the product of `f i` over `i ∈ insert a s` equals the product of
`f i` over `i ∈ s`. -/
@[to_additive "If `f a = 0` when `a ∉ s`, then the sum of `f i` over `i ∈ insert a s` equals the sum
of `f i` over `i ∈ s`."]
lemma finprod_mem_insert_of_eq_one_if_not_mem (h : a ∉ s → f a = 1) :
  ∏ᶠ i ∈ insert a s, f i = ∏ᶠ i ∈ s, f i :=
begin
  refine finprod_mem_inter_mul_support_eq' _ _ _ (λ x hx, ⟨_, or.inr⟩),
  rintro (rfl|hxs),
  exacts [not_imp_comm.1 h hx, hxs]
end

/-- If `f a = 1`, then the product of `f i` over `i ∈ insert a s` equals the product of `f i` over
`i ∈ s`. -/
@[to_additive "If `f a = 0`, then the sum of `f i` over `i ∈ insert a s` equals the sum of `f i`
over `i ∈ s`."]
lemma finprod_mem_insert_one (h : f a = 1) : ∏ᶠ i ∈ insert a s, f i = ∏ᶠ i ∈ s, f i :=
finprod_mem_insert_of_eq_one_if_not_mem (λ _, h)

/-- If the multiplicative support of `f` is finite, then for every `x` in the domain of `f`, `f x`
divides `finprod f`.  -/
lemma finprod_mem_dvd {f : α → N} (a : α) (hf : (mul_support f).finite) : f a ∣ finprod f :=
begin
  by_cases ha : a ∈ mul_support f,
  { rw finprod_eq_prod_of_mul_support_to_finset_subset f hf (set.subset.refl _),
    exact finset.dvd_prod_of_mem f ((finite.mem_to_finset hf).mpr ha) },
  { rw nmem_mul_support.mp ha,
    exact one_dvd (finprod f) }
end

/-- The product of `f i` over `i ∈ {a, b}`, `a ≠ b`, is equal to `f a * f b`. -/
@[to_additive "The sum of `f i` over `i ∈ {a, b}`, `a ≠ b`, is equal to `f a + f b`."]
lemma finprod_mem_pair (h : a ≠ b) : ∏ᶠ i ∈ ({a, b} : set α), f i = f a * f b :=
by { rw [finprod_mem_insert, finprod_mem_singleton], exacts [h, finite_singleton b] }

/-- The product of `f y` over `y ∈ g '' s` equals the product of `f (g i)` over `s`
provided that `g` is injective on `s ∩ mul_support (f ∘ g)`. -/
@[to_additive "The sum of `f y` over `y ∈ g '' s` equals the sum of `f (g i)` over `s` provided that
`g` is injective on `s ∩ support (f ∘ g)`."]
lemma finprod_mem_image' {s : set β} {g : β → α} (hg : (s ∩ mul_support (f ∘ g)).inj_on g) :
  ∏ᶠ i ∈ g '' s, f i = ∏ᶠ j ∈ s, f (g j) :=
begin
  classical,
  by_cases hs : (s ∩ mul_support (f ∘ g)).finite,
  { have hg : ∀ (x ∈ hs.to_finset) (y ∈ hs.to_finset), g x = g y → x = y,
      by simpa only [hs.mem_to_finset],
    rw [finprod_mem_eq_prod _ hs, ← finset.prod_image hg],
    refine finprod_mem_eq_prod_of_inter_mul_support_eq f _,
    rw [finset.coe_image, hs.coe_to_finset, ← image_inter_mul_support_eq, inter_assoc,
      inter_self] },
  { rw [finprod_mem_eq_one_of_infinite hs, finprod_mem_eq_one_of_infinite],
    rwa [image_inter_mul_support_eq, infinite_image_iff hg] }
end

/-- The product of `f y` over `y ∈ g '' s` equals the product of `f (g i)` over `s` provided that
`g` is injective on `s`. -/
@[to_additive "The sum of `f y` over `y ∈ g '' s` equals the sum of `f (g i)` over `s` provided that
`g` is injective on `s`."]
lemma finprod_mem_image {s : set β} {g : β → α} (hg : s.inj_on g) :
  ∏ᶠ i ∈ g '' s, f i = ∏ᶠ j ∈ s, f (g j) :=
finprod_mem_image' $ hg.mono $ inter_subset_left _ _

/-- The product of `f y` over `y ∈ set.range g` equals the product of `f (g i)` over all `i`
provided that `g` is injective on `mul_support (f ∘ g)`. -/
@[to_additive "The sum of `f y` over `y ∈ set.range g` equals the sum of `f (g i)` over all `i`
provided that `g` is injective on `support (f ∘ g)`."]
lemma finprod_mem_range' {g : β → α} (hg : (mul_support (f ∘ g)).inj_on g) :
  ∏ᶠ i ∈ range g, f i = ∏ᶠ j, f (g j) :=
begin
  rw [← image_univ, finprod_mem_image', finprod_mem_univ],
  rwa univ_inter
end

/-- The product of `f y` over `y ∈ set.range g` equals the product of `f (g i)` over all `i`
provided that `g` is injective. -/
@[to_additive "The sum of `f y` over `y ∈ set.range g` equals the sum of `f (g i)` over all `i`
provided that `g` is injective."]
lemma finprod_mem_range {g : β → α} (hg : injective g) : ∏ᶠ i ∈ range g, f i = ∏ᶠ j, f (g j) :=
finprod_mem_range' (hg.inj_on _)

/-- See also `finset.prod_bij`. -/
@[to_additive "See also `finset.sum_bij`."]
lemma finprod_mem_eq_of_bij_on {s : set α} {t : set β} {f : α → M} {g : β → M} (e : α → β)
  (he₀ : s.bij_on e t) (he₁ : ∀ x ∈ s, f x = g (e x)) :
  ∏ᶠ i ∈ s, f i = ∏ᶠ j ∈ t, g j :=
begin
  rw [← set.bij_on.image_eq he₀, finprod_mem_image he₀.2.1],
  exact finprod_mem_congr rfl he₁
end

/-- See `finprod_comp`, `fintype.prod_bijective` and `finset.prod_bij`. -/
@[to_additive "See `finsum_comp`, `fintype.sum_bijective` and `finset.sum_bij`."]
lemma finprod_eq_of_bijective {f : α → M} {g : β → M} (e : α → β) (he₀ : bijective e)
  (he₁ : ∀ x, f x = g (e x)) :
  ∏ᶠ i, f i = ∏ᶠ j, g j :=
begin
  rw [← finprod_mem_univ f, ← finprod_mem_univ g],
  exact finprod_mem_eq_of_bij_on _ (bijective_iff_bij_on_univ.mp he₀) (λ x _, he₁ x),
end

/-- See also `finprod_eq_of_bijective`, `fintype.prod_bijective` and `finset.prod_bij`. -/
@[to_additive "See also `finsum_eq_of_bijective`, `fintype.sum_bijective` and `finset.sum_bij`."]
lemma finprod_comp {g : β → M} (e : α → β) (he₀ : function.bijective e) :
  ∏ᶠ i, g (e i) = ∏ᶠ j, g j := finprod_eq_of_bijective e he₀ (λ x, rfl)

@[to_additive]
lemma finprod_comp_equiv (e : α ≃ β) {f : β → M} : ∏ᶠ i, f (e i) = ∏ᶠ i', f i' :=
finprod_comp e e.bijective

@[to_additive] lemma finprod_set_coe_eq_finprod_mem (s : set α) : ∏ᶠ j : s, f j = ∏ᶠ i ∈ s, f i :=
begin
  rw [← finprod_mem_range, subtype.range_coe],
  exact subtype.coe_injective
end

@[to_additive] lemma finprod_subtype_eq_finprod_cond (p : α → Prop) :
  ∏ᶠ j : subtype p, f j = ∏ᶠ i (hi : p i), f i :=
finprod_set_coe_eq_finprod_mem {i | p i}

@[to_additive] lemma finprod_mem_inter_mul_diff' (t : set α) (h : (s ∩ mul_support f).finite) :
  (∏ᶠ i ∈ s ∩ t, f i) * ∏ᶠ i ∈ s \ t, f i = ∏ᶠ i ∈ s, f i :=
begin
  rw [← finprod_mem_union', inter_union_diff],
  rw disjoint_iff_inf_le,
  exacts [λ x hx, hx.2.2 hx.1.2, h.subset (λ x hx, ⟨hx.1.1, hx.2⟩),
    h.subset (λ x hx, ⟨hx.1.1, hx.2⟩)],
end

@[to_additive] lemma finprod_mem_inter_mul_diff (t : set α) (h : s.finite) :
  (∏ᶠ i ∈ s ∩ t, f i) * ∏ᶠ i ∈ s \ t, f i = ∏ᶠ i ∈ s, f i :=
finprod_mem_inter_mul_diff' _ $ h.inter_of_left _

/-- A more general version of `finprod_mem_mul_diff` that requires `t ∩ mul_support f` rather than
`t` to be finite. -/
@[to_additive "A more general version of `finsum_mem_add_diff` that requires `t ∩ support f` rather
than `t` to be finite."]
lemma finprod_mem_mul_diff' (hst : s ⊆ t) (ht : (t ∩ mul_support f).finite) :
  (∏ᶠ i ∈ s, f i) * ∏ᶠ i ∈ t \ s, f i = ∏ᶠ i ∈ t, f i :=
by rw [← finprod_mem_inter_mul_diff' _ ht, inter_eq_self_of_subset_right hst]

/-- Given a finite set `t` and a subset `s` of `t`, the product of `f i` over `i ∈ s`
times the product of `f i` over `t \ s` equals the product of `f i` over `i ∈ t`. -/
@[to_additive "Given a finite set `t` and a subset `s` of `t`, the sum of `f i` over `i ∈ s` plus
the sum of `f i` over `t \\ s` equals the sum of `f i` over `i ∈ t`."]
lemma finprod_mem_mul_diff (hst : s ⊆ t) (ht : t.finite) :
  (∏ᶠ i ∈ s, f i) * ∏ᶠ i ∈ t \ s, f i = ∏ᶠ i ∈ t, f i :=
finprod_mem_mul_diff' hst (ht.inter_of_left _)

/-- Given a family of pairwise disjoint finite sets `t i` indexed by a finite type, the product of
`f a` over the union `⋃ i, t i` is equal to the product over all indexes `i` of the products of
`f a` over `a ∈ t i`. -/
@[to_additive "Given a family of pairwise disjoint finite sets `t i` indexed by a finite type, the
sum of `f a` over the union `⋃ i, t i` is equal to the sum over all indexes `i` of the sums of `f a`
over `a ∈ t i`."]
lemma finprod_mem_Union [finite ι] {t : ι → set α} (h : pairwise (disjoint on t))
  (ht : ∀ i, (t i).finite) :
  ∏ᶠ a ∈ (⋃ i : ι, t i), f a = ∏ᶠ i, ∏ᶠ a ∈ t i, f a :=
begin
  casesI nonempty_fintype ι,
  lift t to ι → finset α using ht,
  classical,
  rw [← bUnion_univ, ← finset.coe_univ, ← finset.coe_bUnion,
    finprod_mem_coe_finset, finset.prod_bUnion],
  { simp only [finprod_mem_coe_finset, finprod_eq_prod_of_fintype] },
  { exact λ x _ y _ hxy, finset.disjoint_coe.1 (h hxy) }
end

/-- Given a family of sets `t : ι → set α`, a finite set `I` in the index type such that all sets
`t i`, `i ∈ I`, are finite, if all `t i`, `i ∈ I`, are pairwise disjoint, then the product of `f a`
over `a ∈ ⋃ i ∈ I, t i` is equal to the product over `i ∈ I` of the products of `f a` over
`a ∈ t i`. -/
@[to_additive "Given a family of sets `t : ι → set α`, a finite set `I` in the index type such that
all sets `t i`, `i ∈ I`, are finite, if all `t i`, `i ∈ I`, are pairwise disjoint, then the sum of
`f a` over `a ∈ ⋃ i ∈ I, t i` is equal to the sum over `i ∈ I` of the sums of `f a` over
`a ∈ t i`."]
lemma finprod_mem_bUnion {I : set ι} {t : ι → set α} (h : I.pairwise_disjoint t) (hI : I.finite)
  (ht : ∀ i ∈ I, (t i).finite) :
  ∏ᶠ a ∈ ⋃ x ∈ I, t x, f a = ∏ᶠ i ∈ I, ∏ᶠ j ∈ t i, f j :=
begin
  haveI := hI.fintype,
  rw [bUnion_eq_Union, finprod_mem_Union, ← finprod_set_coe_eq_finprod_mem],
  exacts [λ x y hxy, h x.2 y.2 (subtype.coe_injective.ne hxy), λ b, ht b b.2]
end

/-- If `t` is a finite set of pairwise disjoint finite sets, then the product of `f a`
over `a ∈ ⋃₀ t` is the product over `s ∈ t` of the products of `f a` over `a ∈ s`. -/
@[to_additive "If `t` is a finite set of pairwise disjoint finite sets, then the sum of `f a` over
`a ∈ ⋃₀ t` is the sum over `s ∈ t` of the sums of `f a` over `a ∈ s`."]
lemma finprod_mem_sUnion {t : set (set α)} (h : t.pairwise_disjoint id) (ht₀ : t.finite)
  (ht₁ : ∀ x ∈ t, set.finite x) :
  ∏ᶠ a ∈ ⋃₀ t, f a = ∏ᶠ s ∈ t, ∏ᶠ a ∈ s, f a :=
by { rw set.sUnion_eq_bUnion, exact finprod_mem_bUnion h ht₀ ht₁ }

@[to_additive] lemma mul_finprod_cond_ne (a : α) (hf : (mul_support f).finite) :
  f a * (∏ᶠ i ≠ a, f i) = ∏ᶠ i, f i :=
begin
  classical,
  rw [finprod_eq_prod _ hf],
  have h : ∀ x : α, f x ≠ 1 → (x ≠ a ↔ x ∈ hf.to_finset \ {a}),
  { intros x hx,
    rw [finset.mem_sdiff, finset.mem_singleton, finite.mem_to_finset, mem_mul_support],
    exact ⟨λ h, and.intro hx h, λ h, h.2⟩,},
  rw [finprod_cond_eq_prod_of_cond_iff f h, finset.sdiff_singleton_eq_erase],
  by_cases ha : a ∈ mul_support f,
  { apply finset.mul_prod_erase _ _ ((finite.mem_to_finset _ ).mpr ha), },
  { rw [mem_mul_support, not_not] at ha,
    rw [ha, one_mul],
    apply finset.prod_erase _ ha, }
end

/-- If `s : set α` and `t : set β` are finite sets, then taking the product over `s` commutes with
taking the product over `t`. -/
@[to_additive "If `s : set α` and `t : set β` are finite sets, then summing over `s` commutes with
summing over `t`."]
lemma finprod_mem_comm {s : set α} {t : set β} (f : α → β → M) (hs : s.finite) (ht : t.finite) :
  ∏ᶠ i ∈ s, ∏ᶠ j ∈ t, f i j = ∏ᶠ j ∈ t, ∏ᶠ i ∈ s, f i j :=
begin
  lift s to finset α using hs, lift t to finset β using ht,
  simp only [finprod_mem_coe_finset],
  exact finset.prod_comm
end

/-- To prove a property of a finite product, it suffices to prove that the property is
multiplicative and holds on factors. -/
@[to_additive "To prove a property of a finite sum, it suffices to prove that the property is
additive and holds on summands."] lemma finprod_mem_induction (p : M → Prop) (hp₀ : p 1)
  (hp₁ : ∀ x y, p x → p y → p (x * y)) (hp₂ : ∀ x ∈ s, p $ f x) :
  p (∏ᶠ i ∈ s, f i) :=
finprod_induction _ hp₀ hp₁ $ λ x, finprod_induction _ hp₀ hp₁ $ hp₂ x

lemma finprod_cond_nonneg {R : Type*} [ordered_comm_semiring R] {p : α → Prop} {f : α → R}
  (hf : ∀ x, p x → 0 ≤ f x) :
  0 ≤ ∏ᶠ x (h : p x), f x :=
finprod_nonneg $ λ x, finprod_nonneg $ hf x

@[to_additive]
lemma single_le_finprod {M : Type*} [ordered_comm_monoid M] (i : α) {f : α → M}
  (hf : (mul_support f).finite) (h : ∀ j, 1 ≤ f j) :
  f i ≤ ∏ᶠ j, f j :=
by classical;
calc f i ≤ ∏ j in insert i hf.to_finset, f j :
  finset.single_le_prod' (λ j hj, h j) (finset.mem_insert_self _ _)
     ... = ∏ᶠ j, f j                :
  (finprod_eq_prod_of_mul_support_to_finset_subset _ hf (finset.subset_insert _ _)).symm

lemma finprod_eq_zero {M₀ : Type*} [comm_monoid_with_zero M₀] (f : α → M₀) (x : α)
  (hx : f x = 0) (hf : (mul_support f).finite) :
  ∏ᶠ x, f x = 0 :=
begin
  nontriviality,
  rw [finprod_eq_prod f hf],
  refine finset.prod_eq_zero (hf.mem_to_finset.2 _) hx,
  simp [hx]
end

@[to_additive] lemma finprod_prod_comm (s : finset β) (f : α → β → M)
  (h : ∀ b ∈ s, (mul_support (λ a, f a b)).finite) :
  ∏ᶠ a : α, ∏ b in s, f a b = ∏ b in s, ∏ᶠ a : α, f a b :=
begin
  have hU : mul_support (λ a, ∏ b in s, f a b) ⊆
    (s.finite_to_set.bUnion (λ b hb, h b (finset.mem_coe.1 hb))).to_finset,
  { rw finite.coe_to_finset,
    intros x hx,
    simp only [exists_prop, mem_Union, ne.def, mem_mul_support, finset.mem_coe],
    contrapose! hx,
    rw [mem_mul_support, not_not, finset.prod_congr rfl hx, finset.prod_const_one] },
  rw [finprod_eq_prod_of_mul_support_subset _ hU, finset.prod_comm],
  refine finset.prod_congr rfl (λ b hb, (finprod_eq_prod_of_mul_support_subset _ _).symm),
  intros a ha,
  simp only [finite.coe_to_finset, mem_Union],
  exact ⟨b, hb, ha⟩
end

@[to_additive] lemma prod_finprod_comm (s : finset α) (f : α → β → M)
  (h : ∀ a ∈ s, (mul_support (f a)).finite) :
  ∏ a in s, ∏ᶠ b : β, f a b = ∏ᶠ b : β, ∏ a in s, f a b :=
(finprod_prod_comm s (λ b a, f a b) h).symm

lemma mul_finsum {R : Type*} [semiring R] (f : α → R) (r : R) (h : (support f).finite) :
  r * ∑ᶠ a : α, f a = ∑ᶠ a : α, r * f a :=
(add_monoid_hom.mul_left r).map_finsum h

lemma finsum_mul {R : Type*} [semiring R] (f : α → R) (r : R) (h : (support f).finite) :
  (∑ᶠ a : α, f a) * r = ∑ᶠ a : α, f a * r :=
(add_monoid_hom.mul_right r).map_finsum h

@[to_additive] lemma finset.mul_support_of_fiberwise_prod_subset_image [decidable_eq β]
  (s : finset α) (f : α → M) (g : α → β) :
  mul_support (λ b, (s.filter (λ a, g a = b)).prod f) ⊆ s.image g :=
begin
  simp only [finset.coe_image, set.mem_image, finset.mem_coe, function.support_subset_iff],
  intros b h,
  suffices : (s.filter (λ (a : α), g a = b)).nonempty,
  { simpa only [s.fiber_nonempty_iff_mem_image g b, finset.mem_image, exists_prop], },
  exact finset.nonempty_of_prod_ne_one h,
end

/-- Note that `b ∈ (s.filter (λ ab, prod.fst ab = a)).image prod.snd` iff `(a, b) ∈ s` so we can
simplify the right hand side of this lemma. However the form stated here is more useful for
iterating this lemma, e.g., if we have `f : α × β × γ → M`. -/
@[to_additive "Note that `b ∈ (s.filter (λ ab, prod.fst ab = a)).image prod.snd` iff `(a, b) ∈ s` so
we can simplify the right hand side of this lemma. However the form stated here is more useful for
iterating this lemma, e.g., if we have `f : α × β × γ → M`."]
lemma finprod_mem_finset_product' [decidable_eq α] [decidable_eq β]
  (s : finset (α × β)) (f : α × β → M) :
  ∏ᶠ ab (h : ab ∈ s), f ab =
  ∏ᶠ a b (h : b ∈ (s.filter (λ ab, prod.fst ab = a)).image prod.snd), f (a, b) :=
begin
  have : ∀ a, ∏ (i : β) in (s.filter (λ ab, prod.fst ab = a)).image prod.snd, f (a, i) =
    (finset.filter (λ ab, prod.fst ab = a) s).prod f,
  { refine (λ a, finset.prod_bij (λ b _, (a, b)) _ _ _ _); -- `finish` closes these goals
    try { simp, done },
    suffices : ∀ a' b, (a', b) ∈ s → a' = a → (a, b) ∈ s ∧ a' = a, by simpa,
    rintros a' b hp rfl,
    exact ⟨hp, rfl⟩ },
  rw finprod_mem_finset_eq_prod,
  simp_rw [finprod_mem_finset_eq_prod, this],
  rw [finprod_eq_prod_of_mul_support_subset _
    (s.mul_support_of_fiberwise_prod_subset_image f prod.fst),
    ← finset.prod_fiberwise_of_maps_to _ f],  -- `finish` could close the goal here
  simp only [finset.mem_image, prod.mk.eta],
  exact λ x hx, ⟨x, hx, rfl⟩,
end

/-- See also `finprod_mem_finset_product'`. -/
@[to_additive "See also `finsum_mem_finset_product'`."]
lemma finprod_mem_finset_product (s : finset (α × β)) (f : α × β → M) :
  ∏ᶠ ab (h : ab ∈ s), f ab = ∏ᶠ a b (h : (a, b) ∈ s), f (a, b) :=
by { classical, rw finprod_mem_finset_product', simp, }

@[to_additive] lemma finprod_mem_finset_product₃ {γ : Type*}
  (s : finset (α × β × γ)) (f : α × β × γ → M) :
  ∏ᶠ abc (h : abc ∈ s), f abc = ∏ᶠ a b c (h : (a, b, c) ∈ s), f (a, b, c) :=
by { classical, rw finprod_mem_finset_product', simp_rw finprod_mem_finset_product', simp, }

@[to_additive] lemma finprod_curry (f : α × β → M) (hf : (mul_support f).finite) :
  ∏ᶠ ab, f ab = ∏ᶠ a b, f (a, b) :=
begin
  have h₁ : ∀ a, ∏ᶠ (h : a ∈ hf.to_finset), f a = f a, { simp, },
  have h₂ : ∏ᶠ a, f a = ∏ᶠ a (h : a ∈ hf.to_finset), f a, { simp, },
  simp_rw [h₂, finprod_mem_finset_product, h₁],
end

@[to_additive] lemma finprod_curry₃ {γ : Type*} (f : α × β × γ → M) (h : (mul_support f).finite) :
  ∏ᶠ abc, f abc = ∏ᶠ a b c, f (a, b, c) :=
by { rw finprod_curry f h, congr, ext a, rw finprod_curry, simp [h], }

@[to_additive]
lemma finprod_dmem {s : set α} [decidable_pred (∈ s)] (f : (Π (a : α), a ∈ s → M)) :
  ∏ᶠ (a : α) (h : a ∈ s), f a h = ∏ᶠ (a : α) (h : a ∈ s), if h' : a ∈ s then f a h' else 1 :=
finprod_congr (λ a, finprod_congr (λ ha, (dif_pos ha).symm))

@[to_additive]
lemma finprod_emb_domain' {f : α → β} (hf : injective f) [decidable_pred (∈ set.range f)]
  (g : α → M) :
  ∏ᶠ (b : β), (if h : b ∈ set.range f then g (classical.some h) else 1) = ∏ᶠ (a : α), g a :=
begin
  simp_rw [← finprod_eq_dif],
  rw [finprod_dmem, finprod_mem_range hf, finprod_congr (λ a, _)],
  rw [dif_pos (set.mem_range_self a), hf (classical.some_spec (set.mem_range_self a))]
end

@[to_additive]
lemma finprod_emb_domain (f : α ↪ β) [decidable_pred (∈ set.range f)] (g : α → M) :
  ∏ᶠ (b : β), (if h : b ∈ set.range f then g (classical.some h) else 1) = ∏ᶠ (a : α), g a :=
finprod_emb_domain' f.injective g

end type


/-
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 aime_1984_p15
  (x y z w : ℝ)
  (h₀ : (x^2 / (2^2 - 1)) + (y^2 / (2^2 - 3^2)) + (z^2 / (2^2 - 5^2)) + (w^2 / (2^2 - 7^2)) = 1)
  (h₁ : (x^2 / (4^2 - 1)) + (y^2 / (4^2 - 3^2)) + (z^2 / (4^2 - 5^2)) + (w^2 / (4^2 - 7^2)) = 1)
  (h₂ : (x^2 / (6^2 - 1)) + (y^2 / (6^2 - 3^2)) + (z^2 / (6^2 - 5^2)) + (w^2 / (6^2 - 7^2)) = 1)
  (h₃ : (x^2 / (8^2 - 1)) + (y^2 / (8^2 - 3^2)) + (z^2 / (8^2 - 5^2)) + (w^2 / (8^2 - 7^2)) = 1) :
  x^2 + y^2 + z^2 + w^2 = 36 :=
begin
  ring_exp at *,
  linarith,
end


import analysis.normed_space.banach
import analysis.mean_inequalities_pow

import normed_group.normed_with_aut

/-!
# p-Banach spaces

A `p`-Banach space is just like an ordinary Banach space,
except that the axiom `∥c • v∥ = ∥c∥ * ∥v∥` is replaced by `∥c • v∥ = ∥c∥^p * ∥v∥`.

In other words, a `p`-Banach space is a complete topological vector space
whose topology is induced by a `p`-norm.

In this file, we define `p`-normed spaces, called `normed_space'`,
and we prove that every `p`-normed space is also `p'`-normed, for `0 < p' ≤ p`.

-/

noncomputable theory

open_locale nnreal

section

structure has_p_norm (V : Type*) (p : ℝ)
  [add_comm_group V] [module ℝ V] [uniform_space V] extends has_norm V :=
(norm_smul : ∀ (α : ℝ) (v : V), ∥α • v∥ = |α|^p • ∥v∥)
(triangle : ∀ (v w : V), ∥v+w∥ ≤ ∥v∥ + ∥w∥)
(uniformity : uniformity V = ⨅ (ε : ℝ) (H : ε > 0),
  filter.principal {p : V × V | ∥p.fst - p.snd∥ < ε})

variables (V : Type*) (p : ℝ) [add_comm_group V] [module ℝ V] [uniform_space V]

def has_p_norm.semi_normed_group [fact (0 < p)] (h : has_p_norm V p) : semi_normed_group V :=
{ to_uniform_space := by apply_instance,
  uniformity_dist := h.uniformity,
  to_add_comm_group := by apply_instance,
  .. @semi_normed_group.of_core V _ h.to_has_norm $
    have hp0 : p ≠ 0 := (fact.out _ : 0 < p).ne',
    { norm_zero := by simpa only [zero_smul, abs_zero, real.zero_rpow hp0] using h.norm_smul 0 0,
      triangle := h.triangle,
      norm_neg := λ v, by simpa only [neg_smul, one_smul, abs_neg, abs_one, real.one_rpow]
                            using h.norm_smul (-1) v } }

structure p_banach : Prop :=
(exists_p_norm : nonempty (has_p_norm V p))
[topological_add_group : topological_add_group V]
[continuous_smul : has_continuous_smul ℝ V]
[complete : complete_space V]
[separated : separated_space V]

end

structure pBanach (p : ℝ) :=
(V : Type*)
[add_comm_group' : add_comm_group V]
[module' : module ℝ V]
[uniform_space' : uniform_space V]
(p_banach' : p_banach V p)

namespace pBanach

variables (p : ℝ) (V : pBanach p)

instance : has_coe_to_sort (pBanach p) (Type*) := ⟨λ X, X.V⟩

instance : _root_.add_comm_group V := V.add_comm_group'
instance : _root_.module ℝ V := V.module'
instance : _root_.uniform_space V := V.uniform_space'
instance : _root_.topological_add_group V := V.p_banach'.topological_add_group
instance : _root_.has_continuous_smul ℝ V := V.p_banach'.continuous_smul
instance : _root_.complete_space V := V.p_banach'.complete
instance : _root_.separated_space V := V.p_banach'.separated

variables {p}

/-- Highly non-canonical! -/
def choose_semi_normed_group [fact (0 < p)] : semi_normed_group V :=
(classical.choice V.p_banach'.exists_p_norm).semi_normed_group V p

@[simps] def smul_normed_hom [fact (0 < p)] (x : ℝ) :
  @normed_group_hom V V V.choose_semi_normed_group V.choose_semi_normed_group :=
{ to_fun := λ v, x • v,
  map_add' := λ v₁ v₂, smul_add _ _ _,
  bound' := ⟨|x|^p, λ v, by rw [has_p_norm.norm_smul, smul_eq_mul]⟩ }

/-- Highly non-canonical! -/
def choose_normed_with_aut [fact (0 < p)] (x : ℝ≥0) [fact (0 < x)] :
  normed_with_aut (x ^ p) ⟨V, choose_semi_normed_group V⟩ :=
{ T :=
  { hom := smul_normed_hom V x,
    inv := smul_normed_hom V (x⁻¹),
    hom_inv_id' := by { ext v, dsimp, rw [← mul_smul, inv_mul_cancel, one_smul],
      exact_mod_cast (fact.out _ : 0 < x).ne' },
    inv_hom_id' := by { ext v, dsimp, rw [← mul_smul, mul_inv_cancel, one_smul],
      exact_mod_cast (fact.out _ : 0 < x).ne' } } ,
  norm_T := λ v, by { dsimp, rw [has_p_norm.norm_smul, smul_eq_mul], congr' 2,
    rw abs_eq_self, exact x.coe_nonneg } }

@[simp]
lemma choose_normed_with_aut_T_hom [fact (0 < p)] (x : ℝ≥0) [fact (0 < x)] (v : V) :
  (@normed_with_aut.T (x ^ p) ⟨V, choose_semi_normed_group V⟩ (V.choose_normed_with_aut x)).hom v =
  x • v := rfl

@[simp]
lemma choose_normed_with_aut_T_inv [fact (0 < p)] (x : ℝ≥0) [fact (0 < x)] (v : V) :
  (@normed_with_aut.T (x ^ p) ⟨V, choose_semi_normed_group V⟩ (V.choose_normed_with_aut x)).inv v =
  x⁻¹ • v := rfl

end pBanach

-- noncomputable
-- def pBanach'_is_qBanach' (V: Type*) (p : ℝ) [fact (0 < p)] [fact (p ≤ 1)] (q : ℝ) [fact (0 < q)]
--   [fact (q ≤ 1)] [add_comm_group V] [module ℝ V] [uniform_space V] [has_continuous_smul ℝ V]
--   [topological_add_group V] [complete_space V] (hp : pBanach' V p) : pBanach' V q :=
-- begin
--   cases hp,
--   let Hp_norm := hp.some,
--   let ψ := Hp_norm.norm,
--   use λ v : V, (ψ v)^(q/p),--[FAE] Why λ v, ((h_p_norm.norm) v)^(q/p) does not work?
--   intros α v,
--   dsimp only [ψ],
--   admit,
--   admit,
  -- rw [Hp_norm.p_norm α v, smul_eq_mul, real.mul_rpow, ← real.rpow_mul, mul_div_cancel'],
  -- exacts [refl _, ne_of_gt (fact.out _), abs_nonneg α,
  --   (real.rpow_nonneg_of_nonneg (abs_nonneg α) p), hp_nonneg_norm v,
  --   (λ _, (real.rpow_nonneg_of_nonneg (hp_nonneg_norm _) _))],
-- end

section obsolete

-- move this
lemma real.add_rpow_le {x y r : ℝ}
  (hx : 0 ≤ x) (hy : 0 ≤ y) (h0r : 0 ≤ r) (hr1 : r ≤ 1) :
  (x + y)^r ≤ x^r + y^r :=
begin
  by_cases hr : 0 = r,
  { subst r, simp only [zero_le_one, real.rpow_zero, le_add_iff_nonneg_left], },
  let x' : ℝ≥0 := ⟨x, hx⟩,
  let y' : ℝ≥0 := ⟨y, hy⟩,
  exact_mod_cast ennreal.rpow_add_le_add_rpow x' y' (lt_of_le_of_ne h0r hr) hr1,
end

set_option extends_priority 920
-- Here, we set a rather high priority for the instance `[normed_space α β] : module α β`
-- to take precedence over `semiring.to_module` as this leads to instance paths with better
-- unification properties.
/-- A normed space over a normed field is a vector space endowed with a norm which satisfies the
equality `∥c • x∥ = ∥c∥ ∥x∥`. We require only `∥c • x∥ ≤ ∥c∥ ∥x∥` in the definition, then prove
`∥c • x∥ = ∥c∥ ∥x∥` in `norm_smul`. -/
class normed_space' (𝕜 : Type*) (p : out_param ℝ) (V : Type*)
  [normed_field 𝕜] [normed_group V] [module 𝕜 V] :=
(norm_smul : ∀ (c:𝕜) (v:V), ∥c • v∥ = ∥c∥^p * ∥v∥)

@[priority 100]
instance normed_space.normed_space'
  (𝕜 : Type*) (V : Type*) [normed_field 𝕜] [normed_group V] [normed_space 𝕜 V] :
  normed_space' 𝕜 1 V :=
{ norm_smul := λ c k, by simp only [real.rpow_one, norm_smul] }

/-- A type alias: `as_normed_space' p' V` is a `p'`-normed space over `𝕜`,
when `V` is a `p`-normed space over `𝕜` and `0 < p' ≤ p`. -/
@[nolint unused_arguments]
def as_normed_space' (p' : ℝ) (V : Type*) := V

namespace as_normed_space'

instance (p' : ℝ) (V : Type*) [i : inhabited V] : inhabited (as_normed_space' p' V) := i

/-- The identity map `V → as_normed_space' p' V`. -/
def up (p' : ℝ) {V : Type*} (v : V) : as_normed_space' p' V := v

/-- The identity map `as_normed_space' p' V → V`. -/
def down {p' : ℝ} {V : Type*} (v : as_normed_space' p' V) : V := v

instance (p' : ℝ) (V : Type*) [i : add_comm_group V] : add_comm_group (as_normed_space' p' V) := i

instance (p' : ℝ) (𝕜 V : Type*) [ring 𝕜] [add_comm_group V] [i : module 𝕜 V] :
  module 𝕜 (as_normed_space' p' V) := i

@[simp] lemma down_add {p' : ℝ} {V : Type*} [add_comm_group V] (v w : as_normed_space' p' V) :
  (v+w).down = v.down + w.down := rfl

@[simp] lemma down_neg {p' : ℝ} {V : Type*} [add_comm_group V] (v : as_normed_space' p' V) :
  (-v).down = - v.down := rfl

@[simp] lemma down_smul {p' : ℝ} {𝕜 V : Type*} [ring 𝕜] [add_comm_group V] [module 𝕜 V]
  (c : 𝕜) (v : as_normed_space' p' V) :
  (c • v).down = c • v.down := rfl

/-- The natural `p'`-norm on `as_normed_space' p' V` induced by a `p`-norm on `V`. -/
protected def has_norm (p' p : ℝ) (V : Type*) [has_norm V] :
  has_norm (as_normed_space' p' V) :=
⟨λ v, ∥v.down∥^(p'/p)⟩

lemma norm_def {V : Type*} [has_norm V] (p' p : ℝ) (v : as_normed_space' p' V) :
  @has_norm.norm _ (as_normed_space'.has_norm p' p V) v = ∥v.down∥^(p'/p) := rfl

/-- The natural `p'`-normed group structure on `as_normed_space' p' V`
induced by a `p`-normed group structure on `V` -/
protected def normed_group (V : Type*) [normed_group V] (p' p : ℝ) [fact (0 < p')] [fact (p' ≤ p)] :
  normed_group (as_normed_space' p' V) :=
@normed_group.of_core _ _ (as_normed_space'.has_norm p' p V) $
have hp' : 0 < p'   := fact.out _,
have hp  : 0 < p    := lt_of_lt_of_le hp' (fact.out _),
have H   : 0 < p'/p := div_pos hp' hp,
{ norm_eq_zero_iff := λ v, show ∥v.down∥^(p'/p) = 0 ↔ v = 0,
  by simpa only [real.rpow_eq_zero_iff_of_nonneg (norm_nonneg v.down), norm_eq_zero,
        H.ne', and_true, ne.def, not_false_iff],
  triangle := λ v w, show ∥(v+w).down∥^(p'/p) ≤ ∥v.down∥^(p'/p) + ∥w.down∥^(p'/p),
  begin
    rw [down_add],
    calc ∥v.down + w.down∥ ^ (p' / p)
        ≤ (∥v.down∥ + ∥w.down∥) ^ (p' / p) : real.rpow_le_rpow (norm_nonneg _) (norm_add_le _ _) H.le
    ... ≤ ∥v.down∥ ^ (p' / p) + ∥w.down∥ ^ (p' / p) :
      real.add_rpow_le (norm_nonneg _) (norm_nonneg _) H.le _,
    rw [div_le_iff hp, one_mul],
    exact fact.out _
  end,
  norm_neg := λ v, show ∥(-v).down∥^(p'/p) = ∥v.down∥^(p'/p), by rw [down_neg, norm_neg] }

local attribute [instance] as_normed_space'.normed_group

instance (𝕜 : Type*) (V : Type*) [normed_field 𝕜] [normed_group V] [module 𝕜 V]
  (p' p : ℝ) [fact (0 < p')] [fact (p' ≤ p)] [normed_space' 𝕜 p V] :
  normed_space' 𝕜 p' (as_normed_space' p' V) :=
{ norm_smul := λ c v,
  begin
    have hp' : 0 < p'   := fact.out _,
    have hp  : 0 < p    := lt_of_lt_of_le hp' (fact.out _),
    rw [norm_def, norm_def, down_smul, normed_space'.norm_smul, real.mul_rpow, ← real.rpow_mul,
      mul_div_cancel' _ hp.ne'],
    { exact norm_nonneg _ },
    { exact real.rpow_nonneg_of_nonneg (norm_nonneg _) _ },
    { exact norm_nonneg _ },
  end }

end as_normed_space'

end obsolete


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

! This file was ported from Lean 3 source module data.set.list
! leanprover-community/mathlib commit 2ec920d35348cb2d13ac0e1a2ad9df0fdf1a76b4
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Set.Image
import Mathbin.Data.List.Basic
import Mathbin.Data.Fin.Basic

/-!
# Lemmas about `list`s and `set.range`

In this file we prove lemmas about range of some operations on lists.
-/

open List

variable {α β : Type _} (l : List α)

namespace Set

#print Set.range_list_map /-
theorem range_list_map (f : α → β) : range (map f) = { l | ∀ x ∈ l, x ∈ range f } :=
  by
  refine'
    subset.antisymm (range_subset_iff.2 fun l => forall_mem_map_iff.2 fun y _ => mem_range_self _)
      fun l hl => _
  induction' l with a l ihl; · exact ⟨[], rfl⟩
  rcases ihl fun x hx => hl x <| subset_cons _ _ hx with ⟨l, rfl⟩
  rcases hl a (mem_cons_self _ _) with ⟨a, rfl⟩
  exact ⟨a :: l, map_cons _ _ _⟩
#align set.range_list_map Set.range_list_map
-/

#print Set.range_list_map_coe /-
theorem range_list_map_coe (s : Set α) : range (map (coe : s → α)) = { l | ∀ x ∈ l, x ∈ s } := by
  rw [range_list_map, Subtype.range_coe]
#align set.range_list_map_coe Set.range_list_map_coe
-/

#print Set.range_list_nthLe /-
@[simp]
theorem range_list_nthLe : (range fun k : Fin l.length => l.nthLe k k.2) = { x | x ∈ l } :=
  by
  ext x
  rw [mem_set_of_eq, mem_iff_nth_le]
  exact ⟨fun ⟨⟨n, h₁⟩, h₂⟩ => ⟨n, h₁, h₂⟩, fun ⟨n, h₁, h₂⟩ => ⟨⟨n, h₁⟩, h₂⟩⟩
#align set.range_list_nth_le Set.range_list_nthLe
-/

#print Set.range_list_get? /-
theorem range_list_get? : range l.get? = insert none (some '' { x | x ∈ l }) :=
  by
  rw [← range_list_nth_le, ← range_comp]
  refine' (range_subset_iff.2 fun n => _).antisymm (insert_subset.2 ⟨_, _⟩)
  exacts[(le_or_lt l.length n).imp nth_eq_none_iff.2 fun hlt => ⟨⟨_, _⟩, (nth_le_nth hlt).symm⟩,
    ⟨_, nth_eq_none_iff.2 le_rfl⟩, range_subset_iff.2 fun k => ⟨_, nth_le_nth _⟩]
#align set.range_list_nth Set.range_list_get?
-/

#print Set.range_list_getD /-
@[simp]
theorem range_list_getD (d : α) : (range fun n => l.getD n d) = insert d { x | x ∈ l } :=
  calc
    (range fun n => l.getD n d) = (fun o : Option α => o.getD d) '' range l.get? := by
      simp only [← range_comp, (· ∘ ·), nthd_eq_get_or_else_nth]
    _ = insert d { x | x ∈ l } := by
      simp only [range_list_nth, image_insert_eq, Option.getD, image_image, image_id']

#align set.range_list_nthd Set.range_list_getD
-/

#print Set.range_list_getI /-
@[simp]
theorem range_list_getI [Inhabited α] (l : List α) : range l.getI = insert default { x | x ∈ l } :=
  range_list_getD l default
#align set.range_list_inth Set.range_list_getI
-/

end Set

#print List.canLift /-
/-- If each element of a list can be lifted to some type, then the whole list can be lifted to this
type. -/
instance List.canLift (c) (p) [CanLift α β c p] :
    CanLift (List α) (List β) (List.map c) fun l => ∀ x ∈ l, p x
    where prf l H := by
    rw [← Set.mem_range, Set.range_list_map]
    exact fun a ha => CanLift.prf a (H a ha)
#align list.can_lift List.canLift
-/


import HTPIDefs
namespace HTPI
set_option pp.funBinderTypes true

theorem Example_3_2_4
    (P Q R : Prop) (h : P → (Q → R)) : ¬R → (P → ¬Q) := by
  assume h2 : ¬R
  assume h3 : P
  have h4 : Q → R := h h3
  contrapos at h4            --Now h4 : ¬R → ¬Q
  show ¬Q from h4 h2
  done

theorem extremely_easy (P : Prop) (h : P) : P := h

theorem very_easy
    (P Q : Prop) (h1 : P → Q) (h2 : P) : Q := h1 h2

theorem easy (P Q R : Prop) (h1 : P → Q)
    (h2 : Q → R) (h3 : P) : R := h2 (h1 h3)

theorem two_imp (P Q R : Prop)
    (h1 : P → Q) (h2 : Q → ¬R) : R → ¬P := by
  contrapos         --Goal is now P → ¬R
  assume h3 : P
  have h4 : Q := h1 h3
  show ¬R from h2 h4
  done

theorem Example_3_2_5_simple
    (B C : Set Nat) (a : Nat)
    (h1 : a ∈ B) (h2 : a ∉ B \ C) : a ∈ C := by
  define at h2       --Now h2 : ¬(a ∈ B ∧ ¬a ∈ C)
  demorgan at h2; conditional at h2
                     --Now h2 : a ∈ B → a ∈ C
  show a ∈ C from h2 h1
  done

theorem Example_3_2_5_simple_general
    (U : Type) (B C : Set U) (a : U)
    (h1 : a ∈ B) (h2 : a ∉ B \ C) : a ∈ C := by
  define at h2
  demorgan at h2; conditional at h2
  show a ∈ C from h2 h1
  done


import tactic
import data.real.basic

#check set ℕ

variable (S: set ℕ)

#check S

def X : set ℕ := {1,2,3}

def Y : set ℕ := {n : ℕ | ∃ t, n = 2 * t}

def Z := X ∩ Y

#check Z

-- #check (1 : Z) -- fails because set is not a type but somehoh a subset

-- set_option pp.notation false

example : 2 ∈ Z :=
begin
  -- unfold is very useful to figure out what you're working with
  --unfold Z, -- evil tactic as you're not using the API/interface for it
  --have h : 2 ∈ X ∧ 2 ∈ Y,
  -- Introduce a new goal (i.e. a sub lemma)
  -- suffices h : 2 ∈ X ∧ 2 ∈ Y,
  -- Same as have but the order is reversed
  --swap, -- Swap the order of goals
  split,
  {
    --unfold X,
    right, left, refl,
  },
  {
    --change ∃ t, 2 = 2 * t,
    --use 1,
    --norm_num,
    -- or using term mode
    exact ⟨1, rfl⟩,
  }
end


import lib.m154

open m154

section
/-
Avant d'aborder les exercices de cette section, il est important de bien
avoir en tête la liste des formules de négation :

* (¬ ¬ P) ⇔ P
* ¬(P ∨ Q) ⇔ ¬P ∧ ¬Q
* ¬(P ∧ Q) ⇔ ¬P ∨ ¬Q
* (P ⇒ Q) ⇔ (¬ P ∨ Q)
* (P ⇒ Q) ⇔ (¬ Q ⇒ ¬ P)
* ¬ (P ⇒ Q) ⇔ (P ∧ ¬ Q)
* ¬ (∀ x, P x) ⇔ ∃ x, ¬ P x
* ¬ (∃ x, P x) ⇔ ∀ x, ¬ P x
* ¬ (∀ ε > 0, P ε) ⇔ ∃ ε > 0, ¬ P ε
* ¬ (∃ ε > 0, P ε) ⇔ ∀ ε > 0, ¬ P ε

L'objectif de la première série de 4 exercices ci-desosus est de s'entraîner à
utiliser à la main la liste de formules de négation figurant dans cette liste.

Le cœur de chacun de ces 4 exercices est de compléter l'énoncé,
en écrivant que des quantificateurs sans négation et sans utiliser
la définition limite_suite. On peut ensuite vérifier son énoncé en remplaçant
le « sorry » de la démonstration par « verifie ». La commande verifie
est uniquement destinée à cette série d'exercices, elle se contente de déplier
des définitions, d'appeler « On pousse la négation » et de faire un peu de nettoyage.

On rappelle aussi la définition de « u tend vers l » :

def limite_suite (u : ℕ → ℝ) (l : ℝ) : Prop :=
∀ ε > 0, ∃ N, ∀ n ≥ N, |u n - l| ≤ ε
-/

-- Dans cette section, u désigne une suite de réels, f une fonction
-- de ℝ dans ℝ, et x₀ et l des réels.
variables (u : ℕ → ℝ) (f : ℝ → ℝ) (x₀ l : ℝ)

/- Négation de « u tend vers l » -/
example : ¬ (limite_suite u l) ↔
  sorry
:=
begin
  sorry
end

/- Négation de « f est continue en x₀ » -/
example : ¬ (∀ ε > 0, ∃ δ > 0, ∀ x, |x - x₀| ≤ δ →  |f x - f x₀| ≤ ε) ↔
  sorry
:=
begin
  sorry
end

/-
Dans l'exercice suivant, il est utile de se souvenir que « ∀ x x', ... » est
l'abréviation de « ∀ x, ∀ x', ... ». De même « ∃ x x', ... » est
l'abréviation de « ∃ x, ∃ x', ... ».
-/

/- Négation de « f est uniformément continue sur ℝ » -/
example : ¬ (∀ ε > 0, ∃ δ > 0, ∀ x x', |x' - x| ≤ δ →  |f x' - f x| ≤ ε) ↔
  sorry
:=
begin
  sorry
end

/- Négation de « f est séquentiellement continue en x₀ » -/
example : ¬ (∀ u : ℕ → ℝ, limite_suite u x₀ → limite_suite (f ∘ u) (f x₀))  ↔
  sorry
:=
begin
  sorry
end
end

/-
La suite de cette feuille explore des applications élémentaires de la négation
dans l'étude des convergences de suites de réels.

On rappelle que "On combine" peut débusquer des contradictions
faciles, comme dans l'exemple suivant.
-/

example (a b : ℝ) (h : a < b) (h' : b < a) : false :=
begin
  On combine [h, h'],
end

/-
On rappelle des lemmes potentiellement utiles :

`abs_inferieur_ssi (x y : ℝ) : |x| ≤ y ↔ -y ≤ x ∧ x ≤ y`

`superieur_max_ssi (p q r) : r ≥ max p q  ↔ r ≥ p ∧ r ≥ q`

`inferieur_max_gauche p q : p ≤ max p q`

`inferieur_max_droite p q : q ≤ max p q`
-/

/-- La suite `u` tend vers `+∞`. -/
def limite_infinie_suite (u : ℕ → ℝ) := ∀ A, ∃ N, ∀ n ≥ N, u n ≥ A

-- Si u tend vers +∞ alors u ne tend vers aucune limite finie
lemma limite_infinie_pas_finie {u : ℕ → ℝ} :
  limite_infinie_suite u → ∀ l, ¬ limite_suite u l :=
begin
  sorry
end

/-- La suite `u` est croissante. -/
def suite_croissante (u : ℕ → ℝ) := ∀ n m, n ≤ m → u n ≤ u m

-- Si u est croissante et tend vers l alors tous les u_n sont inférieurs à l.
example (u : ℕ → ℝ) (l : ℝ) (h : limite_suite u l) (h' : suite_croissante u) :
  ∀ n, u n ≤ l :=
begin
  sorry
end

/-
Dans les exercices suivants, « A : set ℝ » signifie que A est un ensemble de
nombres réels. On a la notation usuelle x ∈ A pour dire qu'un réel x est
dans l'ensemble de réels A.

La notation « ∀ x ∈ A, ...» est l'abréviation de « ∀ x, x ∈ A → ... »
(analogue à l'abréviation « ∀ ε > 0, ... »).
-/

/-- Le réel `x` est un majorant de l'ensemble de réels `A`. -/
def majorant (A : set ℝ) (x : ℝ) := ∀ a ∈ A, a ≤ x

/-- Le réel `x` est une  borne supérieure de l'ensemble de réels `A`. -/
def borne_sup (A : set ℝ) (x : ℝ) := majorant A x ∧ ∀ y, majorant A y → x ≤ y

/-
Remarque : on peut montrer facilement qu'un ensemble de réels admet au plus
une borne supérieure, mais ce ne sera pas utile ici.
-/

-- Si un réel x est borne supérieure d'un ensemble de réels A alors,
-- pour tout y, si y < x alors il existe a dans A strictement plus grand que y.
lemma lt_sup {A : set ℝ} {x : ℝ} (hx : borne_sup A x) :
∀ y, y < x → ∃ a ∈ A, y < a :=
begin
  sorry
end

/-
L'exercice suivant est une variante d'un exercice de la feuille 10.
Le but ici est de montrer son application à l'étude des limites de suites
dans le dernier exercice.
-/

-- Soit x et y deux réels. Si y ≤ x + ε pour tout ε > 0 alors y ≤ x.
lemma inferieur_si_inferieur_plus_eps {x y : ℝ} :
  (∀ ε > 0, y ≤ x + ε) →  y ≤ x :=
begin
  sorry
end

-- Si u tend vers x et u_n ≤ y pour tout n alors x ≤ y.
lemma lim_le {x y : ℝ} {u : ℕ → ℝ} (hu : limite_suite u x)
  (ineg : ∀ n, u n ≤ y) : x ≤ y :=
begin
  sorry
end