/-
Copyright (c) 2020 Kevin Lacker. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kevin Lacker, Heather Macbeth
-/

import analysis.special_functions.trigonometric.complex

/-!
# IMO 1962 Q4

Solve the equation `cos x ^ 2 + cos (2 * x) ^ 2 + cos (3 * x) ^ 2 = 1`.

Since Lean does not have a concept of "simplest form", we just express what is
in fact the simplest form of the set of solutions, and then prove it equals the set of solutions.
-/

open real
open_locale real
noncomputable theory

def problem_equation (x : ℝ) : Prop := cos x ^ 2 + cos (2 * x) ^ 2 + cos (3 * x) ^ 2 = 1

def solution_set : set ℝ :=
{ x : ℝ | ∃ k : ℤ, x = (2 * ↑k + 1) * π / 4 ∨ x = (2 * ↑k + 1) * π / 6 }

/-
The key to solving this problem simply is that we can rewrite the equation as
a product of terms, shown in `alt_formula`, being equal to zero.
-/

def alt_formula (x : ℝ) : ℝ := cos x * (cos x ^ 2 - 1/2) * cos (3 * x)

lemma cos_sum_equiv {x : ℝ} :
(cos x ^ 2 + cos (2 * x) ^ 2 + cos (3 * x) ^ 2 - 1) / 4 = alt_formula x :=
begin
  simp only [real.cos_two_mul, cos_three_mul, alt_formula],
  ring
end

lemma alt_equiv {x : ℝ} : problem_equation x ↔ alt_formula x = 0 :=
begin
  rw [ problem_equation, ← cos_sum_equiv, div_eq_zero_iff, sub_eq_zero],
  norm_num,
end

lemma finding_zeros {x : ℝ} :
alt_formula x = 0 ↔ cos x ^ 2 = 1/2 ∨ cos (3 * x) = 0 :=
begin
  simp only [alt_formula, mul_assoc, mul_eq_zero, sub_eq_zero],
  split,
  { rintro (h1|h2),
    { right,
      rw [cos_three_mul, h1],
      ring },
    { exact h2 } },
  { exact or.inr }
end

/-
Now we can solve for `x` using basic-ish trigonometry.
-/

lemma solve_cos2_half {x : ℝ} : cos x ^ 2 = 1/2 ↔ ∃ k : ℤ, x = (2 * ↑k + 1) * π / 4 :=
begin
  rw cos_sq,
  simp only [add_right_eq_self, div_eq_zero_iff],
  norm_num,
  rw cos_eq_zero_iff,
  split;
  { rintro ⟨k, h⟩,
    use k,
    linarith },
end

lemma solve_cos3x_0 {x : ℝ} : cos (3 * x) = 0 ↔ ∃ k : ℤ, x = (2 * ↑k + 1) * π / 6 :=
begin
  rw cos_eq_zero_iff,
  refine exists_congr (λ k, _),
  split; intro; linarith
end

/-
The final theorem is now just gluing together our lemmas.
-/

theorem imo1962_q4 {x : ℝ} : problem_equation x ↔ x ∈ solution_set :=
begin
  rw [alt_equiv, finding_zeros, solve_cos3x_0, solve_cos2_half],
  exact exists_or_distrib.symm
end

/-
We now present a second solution.  The key to this solution is that, when the identity is
converted to an identity which is polynomial in `a` := `cos x`, it can be rewritten as a product of
terms, `a ^ 2 * (2 * a ^ 2 - 1) * (4 * a ^ 2 - 3)`, being equal to zero.
-/

/-- Someday, when there is a Grobner basis tactic, try to automate this proof. (A little tricky --
the ideals are not the same but their Jacobson radicals are.) -/
lemma formula {R : Type*} [comm_ring R] [is_domain R] [char_zero R] (a : R) :
  a ^ 2 + (2 * a ^ 2 - 1) ^ 2 + (4 * a ^ 3 - 3 * a) ^ 2 = 1
  ↔ (2 * a ^ 2 - 1) * (4 * a ^ 3 - 3 * a) = 0 :=
calc a ^ 2 + (2 * a ^ 2 - 1) ^ 2 + (4 * a ^ 3 - 3 * a) ^ 2 = 1
    ↔ a ^ 2 + (2 * a ^ 2 - 1) ^ 2 + (4 * a ^ 3 - 3 * a) ^ 2 - 1 = 0 : by rw ← sub_eq_zero
... ↔ 2 * a ^ 2 * (2 * a ^ 2 - 1) * (4 * a ^ 2 - 3) = 0 : by { split; intros h; convert h; ring }
... ↔ a * (2 * a ^ 2 - 1) * (4 * a ^ 2 - 3) = 0 : by simp [(by norm_num : (2:R) ≠ 0)]
... ↔ (2 * a ^ 2 - 1) * (4 * a ^ 3 - 3 * a) = 0 : by { split; intros h; convert h using 1; ring }

/-
Again, we now can solve for `x` using basic-ish trigonometry.
-/

lemma solve_cos2x_0 {x : ℝ} :
  cos (2 * x) = 0 ↔ ∃ k : ℤ, x = (2 * ↑k + 1) * π / 4 :=
begin
  rw cos_eq_zero_iff,
  refine exists_congr (λ k, _),
  split; intro; linarith
end

/-
Again, the final theorem is now just gluing together our lemmas.
-/

theorem imo1962_q4' {x : ℝ} : problem_equation x ↔ x ∈ solution_set :=
calc problem_equation x
    ↔ cos x ^ 2 + cos (2 * x) ^ 2 + cos (3 * x) ^ 2 = 1 : by refl
... ↔ cos (2 * x) = 0 ∨ cos (3 * x) = 0 : by simp [cos_two_mul, cos_three_mul, formula]
... ↔ x ∈ solution_set : by { rw [solve_cos2x_0, solve_cos3x_0, ← exists_or_distrib], refl }


/-
  These logic puzzles are mainly inspired by the ones in the book
  "To Mock a Mockingbird", written by Raymond Smullyan.
-/

namespace enchantedforest

  namespace introduction
    /-
    A certain enchanted forest in a mystical land far away
    is inhabited by talking birds.
    -/

    -- all birds in the enchanted forest belong to the type `Bird`
    -- a `Type` can be thought of as being similar to a `Set`
    constant Bird : Type

    /-
    Given any birds `A` and `B`, if the name of bird `B` is called out to `A`,
    then `A` responds with the name of another bird.
    -/

    -- `response A B` is the response of Bird `A` on hearing Bird `B`'s name
    constant response : Bird → Bird → Bird

    -- better notation for denoting response
    -- the operator is left-associative
    -- the ` ◁ ` symbol (typed as `\lhd`) resembles an ear/beak
    infix ` ◁ ` : 100 := response

  end introduction

  open introduction

  namespace identitybird
    /-
      If the name of a bird `x` is called out to the identity bird `I`,
      it responds with just `x`.

      This bird is sometimes called the "Ibis", or also (rather rudely) as the
      "idiot bird".

      https://en.wikipedia.org/wiki/Ibis
    -/

    -- the definition of the identity bird
    constant I : Bird
    constant I.call : ∀ x : Bird, (I ◁ x) = x

  end identitybird

  /-
  A bird `A` is said to be *fond* of another bird `B` if
  the bird `A` responds to the name `B` with the same name `B`.
  -/

  -- the definition of fondness
  notation [parsing_only] A ` is_fond_of ` B := A ◁ B = B

  /-
  A bird `E` is called *egocentric* if it is fond of itself.
  -/

  -- the definition of egocentricity
  notation [parsing_only] E ` is_egocentric` := E ◁ E = E

  section defending_the_identitybird
    open identitybird

    /-
      Students of Combinatornithology sometimes rudely referred to the
      identity bird as the "idiot bird", because of its apparent simplicity.

      The theorems in this section show why the identity bird is actually quite
      intelligent.
    -/

    -- The identity bird is fond of every bird.
    theorem identity_fond_of_all : ∀ x : Bird, I is_fond_of x :=
    begin
      sorry
    end

    -- The identity bird is egocentric.
    theorem identity_egocentric : I is_egocentric :=
    begin
      sorry
    end

    /-
      The identity bird has an unusually large heart! It is fond of every bird.

      It is also egocentric, but it is fond of itself no more than it is of any
      other bird.
    -/
  end defending_the_identitybird

  /-
  A bird `B` is called *hopelessly egocentric* if
  for every bird `x`, `B ◁ x = B`.
  -/

  -- the definition of hopeless egocentricity
  notation [parsing_only] B ` is_hopelessly_egocentric` := ∀ x, B ◁ x = B

  /-
  More generally, a bird `A` is *fixated* on a bird `B` if
  for every bird `x`, the response of `A` on hearing `x` is `B`.

  Thus a hopelessly egocentric bird is one that is fixated on itself.
  -/

  -- the definition of fixatedness
  notation [parsing_only] A ` is_fixated_on ` B := ∀ x, A ◁ x = B

  namespace kestrel
    /-
      A bird `K` is a *kestrel* if for any bird `x`,
      the bird `K ◁ x` is fixated on `x`.

      https://en.wikipedia.org/wiki/Kestrel
    -/

    -- the definition of a kestrel
    constant K : Bird
    constant K.call : ∀ (x y : Bird), (K ◁ x) ◁ y = x

  end kestrel

  section kestrel_theorems
    open kestrel

    -- For any bird `x`, the bird `K ◁ x` is fixated on `x`.
    theorem k_x_fixated_on_x : ∀ x, K ◁ x is_fixated_on x :=
    begin
      sorry
    end

    -- An egocentric kestrel must be hopelessly egocentric.
    theorem kestrel_egocentrism
      (kestrel_egocentric : K is_egocentric) :
      (K is_hopelessly_egocentric) :=
    begin
      sorry
    end

    -- The left cancellation law for kestrels.
    theorem kestrel_left_cancellation
      (x y : Bird)
      (kestrel_application : (K ◁ x) = (K ◁ y)) :
      (x = y) :=
    begin
      sorry
    end

    -- `*` For an arbitrary bird `x`, if `K` is fond of `K ◁ x`,
    -- then `K` is fond of `x`.
    theorem kestrel_fondness
      (x : Bird)
      (fond_Kx : K is_fond_of (K ◁ x)) :
      K is_fond_of x :=
    begin
      sorry,
    end
  end kestrel_theorems

  /-
  Two birds `A` and `B` form an *agreeable pair* if there is another bird `x`
  that they agree on, i.e., if their responses on hearing the name `x` are the same.
  (or in symbols,  (A ◁ x) = (B ◁ x))

  A bird `A` is *agreeable* if it forms an agreeable pair with every other bird `B`.
  -/

  -- the definition of agreeable birds

  notation [parsing_only] A ` is_agreeable_with ` B := (∃ x : Bird, A ◁ x = B ◁ x)
  notation [parsing_only] A ` is_agreeable` := ∀ β : Bird, A is_agreeable_with β

  section identitybird_theorems
    open identitybird

    -- If the forest contains an identity bird `I` that is agreeable,
    -- then every bird is fond of at least one bird.
    -- This does not rely on the composition axiom.
    theorem agreeable_identity_induces_fondness
      (I_agreeable : I is_agreeable) :
      ∀ B, ∃ x, B is_fond_of x :=
    begin
      sorry
    end

    -- `*` If every bird is fond of at least one bird, then
    -- the identity bird must be agreeable.
    theorem fondness_induces_agreeable_identity
      (all_birds_fond : ∀ B, ∃ x, B is_fond_of x) :
      I is_agreeable :=
    begin
      sorry
    end
  end identitybird_theorems

  namespace mockingbird
    /-
      A *mockingbird* is a kind of bird whose response to any bird `x`
      is exactly the response of `x` to itself.

      https://en.wikipedia.org/wiki/Mockingbird
    -/

    -- the definition of a mockingbird
    constant M : Bird
    constant M.call : ∀ x, M ◁ x = x ◁ x

  end mockingbird

  namespace forestcompositionlaw
    /-
    Given any birds `A`, `B`, `C`, the bird `C` is said to *compose*
    `A` with `B` if for every bird `x`, the following condition holds
                      (C ◁ x) = (A ◁ (B ◁ x))

    This part of the forest has the property that for any two birds
    `A` and `B`, there is a third bird `C` that composes `A` with `B`.
    -/

    -- composition of birds, implemented as a function similar to `response`
    constant compose : Bird → Bird → Bird
    -- notation for composition
    infixr ` ∘ ` := compose
    -- the definition of composition
    axiom composition (A B : Bird) : ∀ {x : Bird}, (A ∘ B) ◁ x = A ◁ (B ◁ x)

  end forestcompositionlaw

  section mockingbird_theorems
    open mockingbird

    -- The mockingbird is agreeable.
    theorem mockingbird_agreeable : M is_agreeable :=
    begin
      sorry
    end

    open forestcompositionlaw

    -- If a mockingbird is in the forest and the composition law holds,
    -- then every bird is fond of at least one bird.
    theorem mockingbird_induces_fondness : ∀ A, ∃ B, A is_fond_of B :=
    begin
      intro A,

      existsi ((A ∘ M) ◁ (A ∘ M)),

      conv
      begin
        to_rhs,
      end,
      sorry,
    end

    -- If the composition law holds and a mockingbird is in the forest,
    -- then there is a bird that is egocentric.
    theorem exists_egocentric : ∃ E, E is_egocentric :=
    begin
      sorry,
    end

    -- `*` If `A` is an agreeable bird and the composition law holds,
    -- every bird is fond of at least one bird.
    theorem agreeability_induces_fondness
      (α : Bird)
      (α_agreeable : α is_agreeable)
      : ∀ A, ∃ B, A is_fond_of B :=
    begin
      sorry
    end

    -- A proof of the earlier theorem as a corollary of the previous one.
    theorem mockingbird_agreeable_fondness : ∀ A, ∃ B, A is_fond_of B :=
    begin
      exact (agreeability_induces_fondness M mockingbird_agreeable)
    end

  end mockingbird_theorems

  namespace bluebird
    /-
      The *bluebird* `B` is a bird that can perform composition.

      For birds `x`, `y`, `z`, the following property holds:
            `(((B ◁ x) ◁ y) ◁ z) = x ◁ (y ◁ z)`

      https://en.wikipedia.org/wiki/Bluebird
    -/

    -- the definition of a bluebird
    constant B : Bird
    constant B.call : ∀ (x y z : Bird), (((B ◁ x) ◁ y) ◁ z) = x ◁ (y ◁ z)

  end bluebird

  section bluebird_theorems
    open bluebird
    open forestcompositionlaw

    -- The bluebird is capable of composing one bird with another.
    theorem bluebird_composition (x y : Bird) :
      ∀ z : Bird, ((B ◁ x) ◁ y) ◁ z = (x ∘ y) ◁ z :=
    begin
      sorry
    end

    open mockingbird

    -- If a mockingbird and a bluebird are in the forest,
    -- for every bird `A` in the forest, one can contruct a
    -- bird `β` using bird calls such that `A` is fond of `β`.
    theorem all_birds_fond : ∀ A, ∃ β, A is_fond_of β :=
    begin
      sorry
    end

  end bluebird_theorems

  namespace lark
    /-
      The *lark* `L` is a bird which, on hearing the name of an
      arbitrary bird `x`, calls out the name of the bird that
      composes `x` with the mockingbird `M`.

      https://en.wikipedia.org/wiki/Lark
    -/

    -- the definition of a lark
    constant L : Bird
    constant L.call : ∀ (x y : Bird), (L ◁ x) ◁ y = x ◁ (y ◁ y)

  end lark

  namespace lark_theorems
    open lark

    -- `*` Every bird is fond of a hopelessly egocentric lark.
    theorem egocentric_lark_popular
      (egocentric_lark : L is_egocentric) :
      ∀ β, β is_fond_of L :=
    begin
      sorry
    end

  end lark_theorems

  namespace starling
    /-
      A *starling* is a bird `S` that satisfies the following condition
          `(((S ◁ x) ◁ y) ◁ z) = (x ◁ z) ◁ (y ◁ z)`

      https://en.wikipedia.org/wiki/Starling
    -/

    -- definition of the starling
    constant S : Bird
    constant S.call : ∀ (x y z : Bird), (((S ◁ x) ◁ y) ◁ z) = (x ◁ z) ◁ (y ◁ z)
  end starling

  section starling_theorems
    open starling
    open kestrel

    /-
      The existence of a Starling and a Kestrel in the forest is
      sufficient to imply the existence of several other birds.
    -/

    -- Derive the identity bird.

    -- Derive the mockingbird.

    -- `*` Derive the bluebird.

  end starling_theorems

  section summoning_a_sagebird
    /-
      According to folklore, a *sagebird* or an *oracle bird* `Θ` is
      believed to have the property that if the name of any bird `x` is
      called out to `θ`, it responds with the name of a bird that `x` is fond of.

      Interestingly, the existence of a sagebird can be deduced from the birds
      encountered so far.
    -/

    -- adding all the known birds
    open identitybird
    open kestrel
    open mockingbird
    open bluebird
    open lark
    open starling

    -- a sage bird exists in the forest
    theorem sagebird_existence :
      ∃ θ, ∀ x, x ◁ (θ ◁ x) = θ ◁ x :=
    begin
      sorry,
    end
  end summoning_a_sagebird

end enchantedforest

  /-
    A star~t~ling fact:

    All birds can be derived from just the
    kestrel (`K`) and the
    starling (`S`)!

    # The algorithm

    Define the `α-eliminate` of an expression `E` to be
    an expression `F` such that `F α = E`.
    1. The α-eliminate of `α` is `I`.
    2. If `α` does not occur in `E`, then `K E` is the
        α-eliminate.
    3. If `E` is of the form `F α`, then `F` is the
        α-eliminate of `E`.
    4. If `E = F G`, and `F'` and `G'` are the corresponding
      α-eliminates of the expressions, then
          `S (F') (G')` is the corresponding α-eliminate.

    Repeated α-elimination of all the variables involved gives the
    expression for the bird in terms of `S` and `K`.

    For a recursive bird `U` (i.e., a bird whose call depends on itself),
    replace every occurrence of the letter `U` in the call with an unused
    variable name and solve as above.

    Call this modified bird `V`. It satisfies the property `V ◁ U = U`.

    Since the existence of the mockingbird and the bluebird is sufficient to
    guarantee that every bird is fond of some bird, one can find a bird `F` such that
    `V` is fond of `F`, that is, `V ◁ F = F`. But this is the property that `U` is required
    to satisfy. Thus `F`, which can be derived from `S` and `K` using the sage bird,
    is the required bird.
  -/

-- TO-DO
namespace ornithologic
end ornithologic

namespace avianarithmetic
end avianarithmetic

/-
  # References:

  1. "To Mock a Mockingbird", by Raymond Smullyan (https://en.wikipedia.org/wiki/To_Mock_a_Mockingbird)
  2. "To Dissect a Mockingbird", by David Keenan (https://dkeenan.com/Lambda/index.htm)
  3. SKI Combinator calculus (https://en.wikipedia.org/wiki/SKI_combinator_calculus)
  4. The Natural Number Game (https://www.ma.imperial.ac.uk/~buzzard/xena/natural_number_game/)
-/


/-
Copyright (c) 2022 Sébastien Gouëzel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sébastien Gouëzel, Violeta Hernández Palacios, Pedro Sánchez Terraf
-/
import borel_hierarchy

/-!
# Cardinal of sigma-algebras

If a sigma-algebra is generated by a set of sets `s`, then the cardinality of the sigma-algebra is
bounded by `(max (#s) 2) ^ ℵ₀`.
This is stated in `measurable_space.cardinal_generate_measurable_le`
and `measurable_space.cardinal_measurable_set_le`.

In particular, if `#s ≤ 𝔠`, then the generated sigma-algebra has cardinality at most `𝔠`, see
`measurable_space.cardinal_measurable_set_le_continuum`.

For the proof, we rely on the explicit inductive construction of the sigma-algebra generated by
`s` provided by `pointclass.gen_measurable`
(instead of the inductive predicate `generate_measurable`).
-/

universe u
variables {α : Type u}

namespace measurable_space

open_locale cardinal ordinal
open cardinal set pointclass

/-- At each step of the inductive construction, the cardinality bound `≤ (max (#s) 2) ^ ℵ₀` holds.

The result holds for arbitrary `i`, but it is easier to prove this way -/
lemma cardinal_sigma0_le (s : set (set α)) (i : ordinal.{u}) (hi : i ≤ ω₁) :
  #(sigma0 s i) ≤ (max (#s) 2) ^ aleph_0.{u} :=
begin
  induction i using ordinal.induction with i IH,
  have Upi0sub : (⋃ j < i, pi0 s j) ⊆ s ∪ {∅, univ} ∪ ⋃ j < i, compl '' sigma0 s j,
  { simp only [mem_singleton_iff, union_insert, union_singleton, mem_insert_iff, Union_subset_iff],
    intros j hj x hx,
    rcases classical.em (j=0) with rfl | hjnz,
    { simp only [mem_singleton_iff, union_insert, union_singleton, mem_insert_iff, pi0_zero] at hx,
      exact mem_union_left _ hx },
    { rw pi0_eq_compl_sigma0 s j hjnz at hx,
      exact mem_union_right _ (mem_Union.mpr ⟨j, mem_Union.mpr ⟨hj, hx⟩⟩) } },
  have cardcompl : ∀ j, #(sigma0 s j) = #(compl '' sigma0 s j) :=
    λ j, cardinal.eq.mpr (⟨equiv.set.image _ _  compl_injective⟩),
  have A := aleph_0_le_aleph 1,
  have B : aleph 1 ≤ (max (#s) 2) ^ aleph_0.{u} :=
    aleph_one_le_continuum.trans (power_le_power_right (le_max_right _ _)),
  have C : ℵ₀ ≤ (max (#s) 2) ^ aleph_0.{u} := A.trans B,
  have L : #(↥(s ∪ {∅, univ})) ≤ (max (#s) 2) ^ aleph_0.{u},
  { apply_rules [(mk_union_le _ _).trans, add_le_of_le C, mk_image_le.trans],
    { exact (le_max_left _ _).trans (self_le_power _ one_lt_aleph_0.le) },
    repeat { simp only [mk_fintype, fintype.card_unique, nat.cast_one, mk_singleton],
      exact one_lt_aleph_0.le.trans C } },
  have K : #(↥⋃ j < i, compl '' sigma0 s j) ≤ (max (#s) 2) ^ aleph_0.{u},
  { apply mk_Union_ordinal_le_of_le (hi.trans $ ord_le_ord.mpr B) C,
    intros j hj,
    rw ← cardcompl,
    exact IH j hj (le_of_lt $ lt_of_lt_of_le hj hi) },
  have J : #(↥(s ∪ {∅, univ} ∪ ⋃ j < i, compl '' sigma0 s j)) ≤ (max (#s) 2) ^ aleph_0.{u},
    { calc
      #(↥(s ∪ {∅, univ} ∪ ⋃ j < i, compl '' sigma0 s j)) ≤
        #(↥(s ∪ {∅, univ})) + #(↥⋃ j < i, compl '' sigma0 s j) : mk_union_le _ _
      ... ≤ (max (#s) 2) ^ aleph_0.{u} + (max (#s) 2) ^ aleph_0.{u} :
        (add_le_add (le_refl _) K).trans (add_le_add L (le_refl _))
      ... = (max (#s) 2) ^ aleph_0.{u} :
        (add_eq_max C).trans (max_eq_right (le_refl _)) },
  -- The main calculation:
  calc
  #↥(sigma0 s i) =
    #↥(range (λ (f : ℕ → (↥⋃ j < i, pi0 s j)), ⋃ n, ↑(f n))) :
    by { rw sigma0_eq_Union_pi0, simp }
  ... ≤ #(ℕ → (↥⋃ j < i, pi0 s j))                  : mk_range_le
  ... = prod (λ n : ℕ, #(↥⋃ j < i, pi0 s j))        : mk_pi _
  ... = #(↥⋃ j < i, pi0 s j) ^ aleph_0.{u}          : by { simp [prod_const] }
  ... ≤ #(↥(s ∪ {∅, univ} ∪ ⋃ j < i, compl '' sigma0 s j)) ^ aleph_0.{u} :
    power_le_power_right (mk_le_mk_of_subset Upi0sub)
  ... ≤ (max (# ↥s) 2 ^ aleph_0.{u}) ^ aleph_0.{u}  : power_le_power_right J
  ... ≤ (max (# ↥s) 2 ^ aleph_0.{u})                :
    by { rwa [← power_mul, aleph_0_mul_aleph_0] }
end

theorem cardinal_gen_measurable_le (s : set (set α)) :
  #(gen_measurable s) ≤ (max (#s) 2) ^ aleph_0.{u} := cardinal_sigma0_le _ _ (le_refl _)

/-- If a sigma-algebra is generated by a set of sets `s`, then the sigma-algebra has cardinality at
most `(max (#s) 2) ^ ℵ₀`. -/
theorem cardinal_generate_measurable_le (s : set (set α)) :
  #{t | generate_measurable s t} ≤ (max (#s) 2) ^ aleph_0.{u} :=
begin
  rw generate_measurable_eq_gen_measurable,
  exact cardinal_gen_measurable_le s,
end

/-- If a sigma-algebra is generated by a set of sets `s`, then the sigma
algebra has cardinality at most `(max (#s) 2) ^ ℵ₀`. -/
theorem cardinal_measurable_set_le' (s : set (set α)) :
  #{t | @measurable_set α (generate_from s) t} ≤ (max (#s) 2) ^ aleph_0.{u} :=
cardinal_generate_measurable_le s

/-- If a sigma-algebra is generated by a set of sets `s` with cardinality at most the continuum,
then the sigma algebra has the same cardinality bound. -/
theorem cardinal_generate_measurable_le_continuum {s : set (set α)} (hs : #s ≤ 𝔠) :
  #{t | generate_measurable s t} ≤ 𝔠 :=
(cardinal_generate_measurable_le s).trans begin
  rw ←continuum_power_aleph_0,
  exact_mod_cast power_le_power_right (max_le hs (nat_lt_continuum 2).le)
end

/-- If a sigma-algebra is generated by a set of sets `s` with cardinality at most the continuum,
then the sigma algebra has the same cardinality bound. -/
theorem cardinal_measurable_set_le_continuum {s : set (set α)} :
  #s ≤ 𝔠 → #{t | @measurable_set α (generate_from s) t} ≤ 𝔠 :=
cardinal_generate_measurable_le_continuum

end measurable_space


import tactic

import data.real.basic

example: ∀ x : ℝ, ∃ y : ℝ, x + y > 0 :=
begin
    intro x,
    use 64-x,
    simp,
    norm_num,
end

-- ∃ y : ℝ, ∀  x : ℝ, x + y > 0 :=
-- Is not true, as we can prove the opposite.

example: ¬ (∃ y : ℝ, ∀  x : ℝ, x + y > 0) :=
begin
    push_neg,
    intro y,
    use -73 - y,
    simp,
    norm_num,
end

variable (α : Type)
-- P is a predicate of α so it is a function that assigns a true-false for each x : alpha.
example : (α → Prop) ≃ set α :=
{ to_fun := λ P, {x : α | P x},
  inv_fun := λ X, λ a, a ∈ X,
  left_inv:= begin
      intro P,
      dsimp,
      refl,
  end
  ,
  right_inv  := begin
      intro P,
      dsimp,
      refl,
  end
}


/-
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 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