/-
We've already seen that we can assert
that a proposition is true by defining
a variable to have that proposition as
its type, and we can prove the proposition
by assigning a proof term to the variable.
-/

lemma zeqz : 0 = 0 := eq.refl 0

/-
Sometimes it's harder to write an exact
proof term (here, eq.refl 0). In these
cases it can help to figure out a proof
term step by step. Lean supports step
by step development of proof terms with
what are called tactic-based proving.
Here's an equivalent tactic-based proof.
-/

lemma zeqz' : 0 = 0 :=
begin
  apply eq.refl 0,
end

/-
In this case, the proof is so simple
that writing a script is more work.
The key thing to see here, though, is
the "apply" tactic. It applies some
already known rule, here eq.refl, to
move from a state in which something
is to be proved to a state in which
something new has been proved.
-/

lemma zeqz'' : 2-1 = 1 :=
begin
  apply eq.refl
end

/-
Now open the Lean Messages panel by typing
control-shift-enter or command-shift-enter
(Windows/Mac). Now place your cursor first
at the start of the "apply". The message
window will display the "tactic state" at
this point in the script. The state say
that nothing is assumed and the remaining
goal to be proved is 0 = 0. Now move your
cursor to the next line, before the "end."
The tactic state is empty, nothing is left
to be proved, QED.
-/

/-
EXERCISE. Define zeqz'' as also
being of type 0 = 0, but after the :=,
just write begin on the next line and
then an end on the following line. You
need to type the begin and end lines
before continuing.
-/

/-
HOW TO SOLVE IT:

Initially there will be an error. Hover
over the red squiggle under the "end."
It tells you that you haven't yet proved
something that remains to be proved, and
it tells you what remains to be proved.

Insert a blank line between the begin and
end. The tactic state tells you what is
known at a given point in a tactic script
(before the turnstile character, ⊢, and
what remains to be proved, after. Here,
the goal that remains is 0 = 0.

If you then click on the next line, end,
Lean tells you that the proof-generating
tactic script between the begin and end
lines failed because some goal remains to
be proved.

In general, a tactic will only partially
prove a goal, leaving some parts still to
be proved. In such cases, more tactics
are then used to finish the construction
of the required proof. Tactic commands
are separated by commas. We'll see more
later.

Go ahead and type the required tactic
between begin and end. Click on the
line with the tactic, then on the end. Watch how the tactic state changes as
you go from line to line.
-/

/-
You might have noticed that while "apply
eq.refl 0" finishes the proof, so does
just "apply eq.refl". In this case, Lean
infers both arguments to eq.refl from
context. That, in fact, is what rfl does.
It's not technically a tactic. It is just
using type inference to infer both of the
arguments needed for eq.refl!

Some people refer to such a script as a
proof. A better way to think about it is
as a step-by-step recipe for building a
proof. The actual proof at the end of the
day is the proof object that the script
constructs: eq.refl 0, in this case.
-/


/-
Copyright (c) 2022 Eric Rodriguez. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Eric Rodriguez
-/
import ring_theory.localization.basic
import set_theory.cardinal_ordinal
import ring_theory.integral_domain

/-!
# Cardinality of localizations

In this file, we establish the cardinality of localizations. In most cases, a localization has
cardinality equal to the base ring. If there are zero-divisors, however, this is no longer true -
for example, `zmod 6` localized at `{2, 4}` is equal to `zmod 3`, and if you have zero in your
submonoid, then your localization is trivial (see `is_localization.unique_of_zero_mem`).

## Main statements

* `is_localization.card_le`: A localization has cardinality no larger than the base ring.
* `is_localization.card`: If you don't localize at zero-divisors, the localization of a ring has
  cardinality equal to its base ring,

-/

open_locale cardinal non_zero_divisors

universes u v

namespace is_localization

variables {R : Type u} [comm_ring R] (S : submonoid R) {L : Type u} [comm_ring L]
          [algebra R L] [is_localization S L]
include S

/-- Localizing a finite ring can only reduce the amount of elements. -/
lemma algebra_map_surjective_of_fintype [fintype R] : function.surjective (algebra_map R L) :=
begin
  classical,
  haveI : fintype L := is_localization.fintype' S L,
  intro x,
  obtain ⟨⟨r, s⟩, h : x * (algebra_map R L) ↑s = (algebra_map R L) r⟩ := is_localization.surj S x,
  obtain ⟨n, hn, hp⟩ :=
    (is_of_fin_order_iff_pow_eq_one _).1 (exists_pow_eq_one (is_localization.map_units L s).unit),
  rw [units.ext_iff, units.coe_pow, is_unit.unit_spec, ←nat.succ_pred_eq_of_pos hn, pow_succ] at hp,
  exact ⟨r * s ^ (n - 1), by erw [map_mul, map_pow, ←h, mul_assoc, hp, mul_one]⟩
end

/-- A localization always has cardinality less than or equal to the base ring. -/
lemma card_le : #L ≤ #R :=
begin
  classical,
  casesI fintype_or_infinite R,
  { exact cardinal.mk_le_of_surjective (algebra_map_surjective_of_fintype S) },
  erw [←cardinal.mul_eq_self $ cardinal.omega_le_mk R],
  set f : R × R → L := λ aa, is_localization.mk' _ aa.1 (if h : aa.2 ∈ S then ⟨aa.2, h⟩ else 1),
  refine @cardinal.mk_le_of_surjective _ _ f (λ a, _),
  obtain ⟨x, y, h⟩ := is_localization.mk'_surjective S a,
  use (x, y),
  dsimp [f],
  rwa [dif_pos $ show ↑y ∈ S, from y.2, set_like.eta]
end

variables (L)

/-- If you do not localize at any zero-divisors, localization preserves cardinality. -/
lemma card (hS : S ≤ R⁰) : #R = #L :=
(cardinal.mk_le_of_injective (is_localization.injective L hS)).antisymm (card_le S)

end is_localization


/-
Copyright (c) 2018 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro, Chris Hughes, Floris van Doorn
-/

/- modified from https://github.com/leanprover-community/mathlib/blob/96a20388edb84fbf87c122ac622423c3dbec8525/src/data/nat/factorial.lean -/
import data.nat.basic
/-!
# The factorial function

-/

namespace nat
variables {m n : ℕ}

/-- `nat.factorial n` is the factorial of `n`. -/
@[simp] def factorial : nat → nat
| 0        := 1
| (succ n) := succ n * factorial n

localized "notation n `!`:10000 := nat.factorial n" in nat

@[simp] theorem factorial_zero : 0! = 1! := rfl

@[simp] theorem factorial_succ (n : ℕ) : n.succ! = succ n * n! := rfl

@[simp] theorem factorial_one : 1! = 1 := rfl

theorem mul_factorial_pred (hn : 0 < n) : n * (n - 1)! = n! :=
have n - 1 + 1 = n, from nat.sub_add_cancel (succ_le_of_lt hn),
calc n * (n - 1)! = (n - 1 + 1) * (n - 1)! : by rw this
... = (n - 1 + 1)! : rfl
... = n! : by rw this

theorem factorial_pos : ∀ n, 0 < n!
| 0        := zero_lt_one
| (succ n) := mul_pos (succ_pos _) (factorial_pos n)

theorem factorial_ne_zero (n : ℕ) : n! ≠ 0 := ne_of_gt (factorial_pos _)

theorem factorial_dvd_factorial {m n} (h : m ≤ n) : m! ∣ n! :=
begin
  induction n with n IH; simp,
  { have := eq_zero_of_le_zero h, subst m, simp },
  { cases eq_or_lt_of_le h with he hl,
    { subst m, simp },
    { apply dvd_mul_of_dvd_right (IH (le_of_lt_succ hl)) } }
end

theorem dvd_factorial : ∀ {m n}, 0 < m → m ≤ n → m ∣ n!
| (succ m) n _ h := dvd_of_mul_right_dvd (factorial_dvd_factorial h)

theorem factorial_le {m n} (h : m ≤ n) : m! ≤ n! :=
le_of_dvd (factorial_pos _) (factorial_dvd_factorial h)

lemma factorial_mul_pow_le_factorial : ∀ {m n : ℕ}, m! * m.succ ^ n ≤ (m + n)!
| m 0     := by simp
| m (n+1) :=
by  rw [← add_assoc, nat.factorial_succ, mul_comm (nat.succ _), pow_succ, ← mul_assoc];
  exact mul_le_mul factorial_mul_pow_le_factorial
    (nat.succ_le_succ (nat.le_add_right _ _)) (nat.zero_le _) (nat.zero_le _)

lemma monotone_factorial : monotone factorial := λ n m, factorial_le

lemma factorial_lt (h0 : 0 < n) : n! < m! ↔ n < m :=
begin
  split; intro h,
  { rw [← not_le], intro hmn, apply not_le_of_lt h (factorial_le hmn) },
  { have : ∀(n : ℕ), 0 < n → n! < n.succ!,
    { intros k hk, rw [factorial_succ, succ_mul, lt_add_iff_pos_left],
      apply mul_pos hk (factorial_pos k) },
    induction h with k hnk generalizing h0,
    { exact this _ h0, },
    { refine lt_trans (h_ih h0) (this _ _), exact lt_trans h0 (lt_of_succ_le hnk) }}
end

lemma one_lt_factorial : 1 < n! ↔ 1 < n :=
by { convert factorial_lt _, refl, exact one_pos }

lemma factorial_eq_one : n! = 1 ↔ n ≤ 1 :=
begin
  split; intro h,
  { rw [← not_lt, ← one_lt_factorial, h], apply lt_irrefl },
  { cases h with h h, refl, cases h, refl }
end

lemma factorial_inj (h0 : 1 < n!) : n! = m! ↔ n = m :=
begin
  split; intro h,
  { rcases lt_trichotomy n m with hnm|hnm|hnm,
    { exfalso, rw [← factorial_lt, h] at hnm, exact lt_irrefl _ hnm,
      rw [one_lt_factorial] at h0, exact lt_trans one_pos h0 },
    { exact hnm },
    { exfalso, rw [← factorial_lt, h] at hnm, exact lt_irrefl _ hnm,
      rw [h, one_lt_factorial] at h0, exact lt_trans one_pos h0 }},
  { rw h }
end

lemma le_mul_of_one_le_right {a b : ℕ} (h1: 0 < b) (h: 1 ≤ a) : b ≤ b * a :=
begin
    rw ← mul_one b,
    rw mul_assoc,
    rw one_mul a,
    apply mul_le_mul_of_nonneg_left,
    exact h,
    apply le_of_lt,
    exact h1,
end

lemma self_le_factorial : ∀ n : ℕ, n ≤ n!
| 0 := zero_le_one
| (k+1) := le_mul_of_one_le_right k.zero_lt_succ (nat.one_le_of_lt $ nat.factorial_pos _)

end nat


import tactic
import data.real.basic
import data.int.gcd
import number_theory.padics
import data.nat.prime_norm_num

/-!

## Exercises about numbers and casts

-/

/-!

## First exercises

These first examples are just to get you comfortable with
`norm_num`, `norm_cast`, and friends.

-/

example : 12345 < 67890 :=
begin
  sorry
end

example {α : Type} [linear_ordered_field α] : 123 + 45 < 67890/3 :=
begin
  sorry
end

example : nat.prime 17 :=
begin
  sorry
end

-- prove either this or its negation!
example : 7/3 > 2 :=
begin
  sorry
end

example (x : ℝ) (hx : x < 50*50) : x < 25*100 :=
begin
  sorry
end

example (x : ℤ) (hx : (x : ℝ) < 25*100) : x < 25*100 :=
begin
  sorry
end

example (x : ℤ) (hx : (x : ℝ) < 2500) : x < 25*100 :=
begin
  sorry
end

example (p q r : ℕ) (h : r < p - q) (hpq : q ≤ p) : (r : ℝ) < p - q :=
begin
  sorry
end

example (p q r : ℕ) (hr : r < p + 2 - p) : (r : ℤ) < 5 :=
begin
  sorry
end

/-!

## Exercise 2

This comes from the development of the p-adic numbers.
`norm_cast` is very useful here, since we need to talk about values in
ℕ, ℤ, ℚ, ℚ_p, and ℤ_p.

We've done some work to get you started. You might look for the lemmas:
-/

open padic_val_rat

#check zpow_le_of_le
#check zpow_nonneg
#check of_int_multiplicity

example {p n : ℕ} (hp : p.prime) {z : ℤ} (hd : ↑(p^n) ∣ z) : padic_norm p z ≤ ↑p ^ (-n : ℤ) :=
begin
  -- This lemma will be useful later in the proof.
  -- Ignore the "inst" argument; just use `apply aux_lemma` when you need it!
  -- Note that we haven't finished it. Fill in that final sorry.
  have aux_lemma : ∀ inst, (n : ℤ) ≤ (multiplicity ↑p z).get inst,
  { intro,
    norm_cast,
    rw [← part_enat.coe_le_coe, part_enat.coe_get],
    apply multiplicity.le_multiplicity_of_pow_dvd,
    sorry },

  unfold padic_norm, split_ifs with hz hz,
  { sorry },
  { sorry }
end

/-!

## Exercise 3

This seems like a very natural way to write the theorem
"If `a` and `b` are coprime, then there are coefficients `u` and `v` such that `u*a + v*b = 1`."

But I've made a mistake! What did I do wrong? Correct the statement of the theorem and prove it.

I've started you off with a lemma that will be useful.
You might find the `specialize` tactic to be handy as well:
if you have `h : ∀ (x y : T), R x y` and `a, b : T` in the context, then
`specialize h a b` will change the type of `h` to `R a b`.

-/

example (a b : ℕ) (h : nat.coprime a b) : ∃ u v, u*a+v*b = 1 :=
begin
  have := nat.gcd_eq_gcd_ab,
  sorry
end

/-!

## Exercise 4

We did an example together that was similar to this.

This one takes a bit more arithmetic work.
To save you some time, here are some lemmas that may be useful!
(You may not need all of them, depending on how you approach it.)

Remember you can also use `library_search` to try to find useful lemmas.

A hint: you might find it helpful to do this once you've introduced `n`.
```
have n_pos: 0 < n,
{ ... }
```

-/

#check sub_le_iff_le_add
#check add_le_add_iff_left
#check div_le_iff
#check mul_one_div_cancel
#check mul_le_mul_left

notation `|`x`|` := abs x

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

example : seq_limit (λ n : ℕ, (n+1)/n) 1 :=
begin
  sorry
end


/-
Copyright (c) 2022 Pierre-Alexandre Bazin. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Pierre-Alexandre Bazin
-/
import analysis.seminorm
/-!
# The lattice of seminorms is not distributive

We provide an example of three seminorms over the ℝ-vector space ℝ×ℝ which don't satisfy the lattice
distributivity property `(p ⊔ q1) ⊓ (p ⊔ q2) ≤ p ⊔ (q1 ⊓ q2)`.

This proves the lattice `seminorm ℝ (ℝ × ℝ)` is not distributive.

## References

* https://en.wikipedia.org/wiki/Seminorm#Examples
-/

open seminorm
open_locale nnreal

namespace seminorm_not_distrib

@[simps] noncomputable def p : seminorm ℝ (ℝ×ℝ) :=
(norm_seminorm ℝ ℝ).comp (linear_map.fst _ _ _) ⊔ (norm_seminorm ℝ ℝ).comp (linear_map.snd _ _ _)

@[simps] noncomputable def q1 : seminorm ℝ (ℝ×ℝ) :=
(4 : ℝ≥0) • (norm_seminorm ℝ ℝ).comp (linear_map.fst _ _ _)

@[simps] noncomputable def q2 : seminorm ℝ (ℝ×ℝ) :=
(4 : ℝ≥0) • (norm_seminorm ℝ ℝ).comp (linear_map.snd _ _ _)

lemma eq_one : (p ⊔ (q1 ⊓ q2)) (1, 1) = 1 :=
begin
  suffices : (⨅ x : ℝ × ℝ, q1 x + q2 (1 - x)) ≤ 1, by simpa,
  apply cinfi_le_of_le bdd_below_range_add ((0, 1) : ℝ×ℝ), dsimp [q1, q2],
  simp only [abs_zero, smul_zero, sub_self, add_zero, zero_le_one],
end

/-- This is a counterexample to the distributivity of the lattice `seminorm ℝ (ℝ × ℝ)`. -/
lemma not_distrib : ¬((p ⊔ q1) ⊓ (p ⊔ q2) ≤ p ⊔ (q1 ⊓ q2)) :=
begin
  intro le_sup_inf,
  have c : ¬(4/3 ≤ (1:ℝ)) := by norm_num,
  apply c, nth_rewrite 2 ← eq_one,
  apply le_trans _ (le_sup_inf _),
  apply le_cinfi, intro x,
  cases le_or_lt x.fst (1/3) with h1 h1,
  { cases le_or_lt x.snd (2/3) with h2 h2,
    { calc 4/3 = 4 * (1 - 2/3) : by norm_num
           ... ≤ 4 * (1 - x.snd) : (mul_le_mul_left zero_lt_four).mpr (sub_le_sub_left h2 _)
           ... ≤ 4 * |1 - x.snd| : (mul_le_mul_left zero_lt_four).mpr (le_abs_self _)
           ... = q2 ((1, 1) - x) : rfl
           ... ≤ (p ⊔ q2) ((1, 1) - x) : le_sup_right
           ... ≤ (p ⊔ q1) x + (p ⊔ q2) ((1, 1) - x) : le_add_of_nonneg_left (map_nonneg _ _) },
    { calc 4/3 = 2/3 + (1 - 1/3) : by norm_num
           ... ≤ x.snd + (1 - x.fst) : add_le_add (le_of_lt h2) (sub_le_sub_left h1 _)
           ... ≤ |x.snd| + |1 - x.fst| : add_le_add (le_abs_self _) (le_abs_self _)
           ... ≤ p x + p ((1, 1) - x) : add_le_add le_sup_right le_sup_left
           ... ≤ (p ⊔ q1) x + (p ⊔ q2) ((1, 1) - x) : add_le_add le_sup_left le_sup_left } },
  { calc 4/3 = 4 * (1/3) : by norm_num
         ... ≤ 4 * x.fst : (mul_le_mul_left zero_lt_four).mpr (le_of_lt h1)
         ... ≤ 4 * |x.fst| : (mul_le_mul_left zero_lt_four).mpr (le_abs_self _)
         ... = q1 x : rfl
         ... ≤ (p ⊔ q1) x : le_sup_right
         ... ≤ (p ⊔ q1) x + (p ⊔ q2) ((1, 1) - x) : le_add_of_nonneg_right (map_nonneg _ _) }
end

end seminorm_not_distrib


/-
Copyright (c) 2019 Chris Hughes. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Chris Hughes

! This file was ported from Lean 3 source module data.nat.multiplicity
! leanprover-community/mathlib commit ceb887ddf3344dab425292e497fa2af91498437c
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Algebra.BigOperators.Intervals
import Mathlib.Algebra.GeomSum
import Mathlib.Data.Nat.Bitwise
import Mathlib.Data.Nat.Log
import Mathlib.Data.Nat.Parity
import Mathlib.Data.Nat.Prime
import Mathlib.RingTheory.Multiplicity

/-!
# Natural number multiplicity

This file contains lemmas about the multiplicity function (the maximum prime power dividing a
number) when applied to naturals, in particular calculating it for factorials and binomial
coefficients.

## Multiplicity calculations

* `Nat.multiplicity_factorial`: Legendre's Theorem. The multiplicity of `p` in `n!` is
  `n/p + ... + n/p^b` for any `b` such that `n/p^(b + 1) = 0`.
* `Nat.multiplicity_factorial_mul`: The multiplicity of `p` in `(p * n)!` is `n` more than that of
  `n!`.
* `Nat.multiplicity_choose`: The multiplicity of `p` in `n.choose k` is the number of carries when
  `k` and`n - k` are added in base `p`.

## Other declarations

* `Nat.multiplicity_eq_card_pow_dvd`: The multiplicity of `m` in `n` is the number of positive
  natural numbers `i` such that `m ^ i` divides `n`.
* `Nat.multiplicity_two_factorial_lt`: The multiplicity of `2` in `n!` is strictly less than `n`.
* `Nat.prime.multiplicity_something`: Specialization of `multiplicity.something` to a prime in the
  naturals. Avoids having to provide `p ≠ 1` and other trivialities, along with translating between
  `prime` and `nat.prime`.

## Tags

Legendre, p-adic
-/

open Finset Nat multiplicity

open BigOperators Nat

namespace Nat

/-- The multiplicity of `m` in `n` is the number of positive natural numbers `i` such that `m ^ i`
divides `n`. This set is expressed by filtering `Ico 1 b` where `b` is any bound greater than
`log m n`. -/
theorem multiplicity_eq_card_pow_dvd {m n b : ℕ} (hm : m ≠ 1) (hn : 0 < n) (hb : log m n < b) :
    multiplicity m n = ↑((Finset.Ico 1 b).filter fun i => m ^ i ∣ n).card :=
  calc
    multiplicity m n = ↑(Ico 1 <| (multiplicity m n).get (finite_nat_iff.2 ⟨hm, hn⟩) + 1).card := by
      simp
    _ = ↑((Finset.Ico 1 b).filter fun i => m ^ i ∣ n).card :=
      congr_arg _ <|
        congr_arg card <|
          Finset.ext fun i => by
            rw [mem_filter, mem_Ico, mem_Ico, lt_succ_iff, ← @PartENat.coe_le_coe i,
              PartENat.natCast_get, ← pow_dvd_iff_le_multiplicity, and_right_comm]
            refine' (and_iff_left_of_imp fun h => lt_of_le_of_lt _ hb).symm
            cases' m with m
            · rw [zero_eq, zero_pow, zero_dvd_iff] at h
              exacts [(hn.ne' h.2).elim, h.1]
            exact le_log_of_pow_le (one_lt_iff_ne_zero_and_ne_one.2 ⟨m.succ_ne_zero, hm⟩)
                (le_of_dvd hn h.2)
#align nat.multiplicity_eq_card_pow_dvd Nat.multiplicity_eq_card_pow_dvd

namespace Prime

theorem multiplicity_one {p : ℕ} (hp : p.Prime) : multiplicity p 1 = 0 :=
  multiplicity.one_right hp.prime.not_unit
#align nat.prime.multiplicity_one Nat.Prime.multiplicity_one

theorem multiplicity_mul {p m n : ℕ} (hp : p.Prime) :
    multiplicity p (m * n) = multiplicity p m + multiplicity p n :=
  multiplicity.mul hp.prime
#align nat.prime.multiplicity_mul Nat.Prime.multiplicity_mul

theorem multiplicity_pow {p m n : ℕ} (hp : p.Prime) :
    multiplicity p (m ^ n) = n • multiplicity p m :=
  multiplicity.pow hp.prime
#align nat.prime.multiplicity_pow Nat.Prime.multiplicity_pow

theorem multiplicity_self {p : ℕ} (hp : p.Prime) : multiplicity p p = 1 :=
  multiplicity.multiplicity_self hp.prime.not_unit hp.ne_zero
#align nat.prime.multiplicity_self Nat.Prime.multiplicity_self

theorem multiplicity_pow_self {p n : ℕ} (hp : p.Prime) : multiplicity p (p ^ n) = n :=
  multiplicity.multiplicity_pow_self hp.ne_zero hp.prime.not_unit n
#align nat.prime.multiplicity_pow_self Nat.Prime.multiplicity_pow_self

/-- **Legendre's Theorem**

The multiplicity of a prime in `n!` is the sum of the quotients `n / p ^ i`. This sum is expressed
over the finset `Ico 1 b` where `b` is any bound greater than `log p n`. -/
theorem multiplicity_factorial {p : ℕ} (hp : p.Prime) :
    ∀ {n b : ℕ}, log p n < b → multiplicity p n ! = (∑ i in Ico 1 b, n / p ^ i : ℕ)
  | 0, b, _ => by simp [Ico, hp.multiplicity_one]
  | n + 1, b, hb =>
    calc
      multiplicity p (n + 1)! = multiplicity p n ! + multiplicity p (n + 1) := by
        rw [factorial_succ, hp.multiplicity_mul, add_comm]
      _ = (∑ i in Ico 1 b, n / p ^ i : ℕ) +
            ((Finset.Ico 1 b).filter fun i => p ^ i ∣ n + 1).card := by
        rw [multiplicity_factorial hp ((log_mono_right <| le_succ _).trans_lt hb), ←
          multiplicity_eq_card_pow_dvd hp.ne_one (succ_pos _) hb]
      _ = (∑ i in Ico 1 b, (n / p ^ i + if p ^ i ∣ n + 1 then 1 else 0) : ℕ) := by
        rw [sum_add_distrib, sum_boole]
        simp
      _ = (∑ i in Ico 1 b, (n + 1) / p ^ i : ℕ) :=
        congr_arg _ <| Finset.sum_congr rfl fun _ _ => (succ_div _ _).symm
#align nat.prime.multiplicity_factorial Nat.Prime.multiplicity_factorial

/-- The multiplicity of `p` in `(p * (n + 1))!` is one more than the sum
  of the multiplicities of `p` in `(p * n)!` and `n + 1`. -/
theorem multiplicity_factorial_mul_succ {n p : ℕ} (hp : p.Prime) :
    multiplicity p (p * (n + 1))! = multiplicity p (p * n)! + multiplicity p (n + 1) + 1 := by
  have hp' := hp.prime
  have h0 : 2 ≤ p := hp.two_le
  have h1 : 1 ≤ p * n + 1 := Nat.le_add_left _ _
  have h2 : p * n + 1 ≤ p * (n + 1)
  linarith
  have h3 : p * n + 1 ≤ p * (n + 1) + 1
  linarith
  have hm : multiplicity p (p * n)! ≠ ⊤ := by
    rw [Ne.def, eq_top_iff_not_finite, Classical.not_not, finite_nat_iff]
    exact ⟨hp.ne_one, factorial_pos _⟩
  revert hm
  have h4 : ∀ m ∈ Ico (p * n + 1) (p * (n + 1)), multiplicity p m = 0 := by
    intro m hm
    rw [multiplicity_eq_zero, ← not_dvd_iff_between_consec_multiples _ hp.pos]
    rw [mem_Ico] at hm
    exact ⟨n, lt_of_succ_le hm.1, hm.2⟩
  simp_rw [← prod_Ico_id_eq_factorial, multiplicity.Finset.prod hp', ← sum_Ico_consecutive _ h1 h3,
    add_assoc]
  intro h
  rw [PartENat.add_left_cancel_iff h, sum_Ico_succ_top h2, multiplicity.mul hp',
    hp.multiplicity_self, sum_congr rfl h4, sum_const_zero, zero_add, add_comm (1 : PartENat)]
#align nat.prime.multiplicity_factorial_mul_succ Nat.Prime.multiplicity_factorial_mul_succ

/-- The multiplicity of `p` in `(p * n)!` is `n` more than that of `n!`. -/
theorem multiplicity_factorial_mul {n p : ℕ} (hp : p.Prime) :
    multiplicity p (p * n)! = multiplicity p n ! + n := by
  induction' n with n ih
  · simp
  · simp only [succ_eq_add_one, multiplicity.mul, hp, hp.prime, ih, multiplicity_factorial_mul_succ,
      ← add_assoc, Nat.cast_one, Nat.cast_add, factorial_succ]
    congr 1
    rw [add_comm, add_assoc]
#align nat.prime.multiplicity_factorial_mul Nat.Prime.multiplicity_factorial_mul

/-- A prime power divides `n!` iff it is at most the sum of the quotients `n / p ^ i`.
  This sum is expressed over the set `Ico 1 b` where `b` is any bound greater than `log p n` -/
theorem pow_dvd_factorial_iff {p : ℕ} {n r b : ℕ} (hp : p.Prime) (hbn : log p n < b) :
    p ^ r ∣ n ! ↔ r ≤ ∑ i in Ico 1 b, n / p ^ i := by
  rw [← PartENat.coe_le_coe, ← hp.multiplicity_factorial hbn, ← pow_dvd_iff_le_multiplicity]
#align nat.prime.pow_dvd_factorial_iff Nat.Prime.pow_dvd_factorial_iff

theorem multiplicity_factorial_le_div_pred {p : ℕ} (hp : p.Prime) (n : ℕ) :
    multiplicity p n ! ≤ (n / (p - 1) : ℕ) := by
  rw [hp.multiplicity_factorial (lt_succ_self _), PartENat.coe_le_coe]
  exact Nat.geom_sum_Ico_le hp.two_le _ _
#align nat.prime.multiplicity_factorial_le_div_pred Nat.Prime.multiplicity_factorial_le_div_pred

theorem multiplicity_choose_aux {p n b k : ℕ} (hp : p.Prime) (hkn : k ≤ n) :
    (∑ i in Finset.Ico 1 b, n / p ^ i) =
      ((∑ i in Finset.Ico 1 b, k / p ^ i) + ∑ i in Finset.Ico 1 b, (n - k) / p ^ i) +
        ((Finset.Ico 1 b).filter fun i => p ^ i ≤ k % p ^ i + (n - k) % p ^ i).card :=
  calc
    (∑ i in Finset.Ico 1 b, n / p ^ i) = ∑ i in Finset.Ico 1 b, (k + (n - k)) / p ^ i := by
      simp only [add_tsub_cancel_of_le hkn]
    _ = ∑ i in Finset.Ico 1 b,
          (k / p ^ i + (n - k) / p ^ i + if p ^ i ≤ k % p ^ i + (n - k) % p ^ i then 1 else 0) :=
      by simp only [Nat.add_div (pow_pos hp.pos _)]
    _ = _ := by simp [sum_add_distrib, sum_boole]

#align nat.prime.multiplicity_choose_aux Nat.Prime.multiplicity_choose_aux

/-- The multiplicity of `p` in `choose n k` is the number of carries when `k` and `n - k`
  are added in base `p`. The set is expressed by filtering `Ico 1 b` where `b`
  is any bound greater than `log p n`. -/

/-- A lower bound on the multiplicity of `p` in `choose n k`. -/
theorem multiplicity_le_multiplicity_choose_add {p : ℕ} (hp : p.Prime) :
    ∀ n k : ℕ, multiplicity p n ≤ multiplicity p (choose n k) + multiplicity p k
  | _, 0 => by simp
  | 0, _ + 1 => by simp
  | n + 1, k + 1 => by
    rw [← hp.multiplicity_mul]
    refine' multiplicity_le_multiplicity_of_dvd_right _
    rw [← succ_mul_choose_eq]
    exact dvd_mul_right _ _
#align nat.prime.multiplicity_le_multiplicity_choose_add Nat.Prime.multiplicity_le_multiplicity_choose_add

variable {p n k : ℕ}

theorem multiplicity_choose_prime_pow_add_multiplicity (hp : p.Prime) (hkn : k ≤ p ^ n)
    (hk0 : k ≠ 0) : multiplicity p (choose (p ^ n) k) + multiplicity p k = n :=
  le_antisymm
    (by
      have hdisj :
        Disjoint ((Ico 1 n.succ).filter fun i => p ^ i ≤ k % p ^ i + (p ^ n - k) % p ^ i)
          ((Ico 1 n.succ).filter fun i => p ^ i ∣ k) := by
        simp (config := { contextual := true }) [disjoint_right, *, dvd_iff_mod_eq_zero,
          Nat.mod_lt _ (pow_pos hp.pos _)]
      rw [multiplicity_choose hp hkn (lt_succ_self _),
        multiplicity_eq_card_pow_dvd (ne_of_gt hp.one_lt) hk0.bot_lt
          (lt_succ_of_le (log_mono_right hkn)),
        ← Nat.cast_add, PartENat.coe_le_coe, log_pow hp.one_lt, ← card_disjoint_union hdisj,
        filter_union_right]
      have filter_le_Ico := (Ico 1 n.succ).card_filter_le
        fun x => p ^ x ≤ k % p ^ x + (p ^ n - k) % p ^ x ∨ p ^ x ∣ k
      rwa [card_Ico 1 n.succ] at filter_le_Ico)
    (by rw [← hp.multiplicity_pow_self]; exact multiplicity_le_multiplicity_choose_add hp _ _)
#align nat.prime.multiplicity_choose_prime_pow_add_multiplicity Nat.Prime.multiplicity_choose_prime_pow_add_multiplicity

theorem multiplicity_choose_prime_pow {p n k : ℕ} (hp : p.Prime) (hkn : k ≤ p ^ n) (hk0 : k ≠ 0) :
    multiplicity p (choose (p ^ n) k) =
      ↑(n - (multiplicity p k).get (finite_nat_iff.2 ⟨hp.ne_one, hk0.bot_lt⟩)) :=
  PartENat.eq_natCast_sub_of_add_eq_natCast <|
    multiplicity_choose_prime_pow_add_multiplicity hp hkn hk0
#align nat.prime.multiplicity_choose_prime_pow Nat.Prime.multiplicity_choose_prime_pow

theorem dvd_choose_pow (hp : Prime p) (hk : k ≠ 0) (hkp : k ≠ p ^ n) : p ∣ (p ^ n).choose k := by
  obtain hkp | hkp := hkp.symm.lt_or_lt
  · simp [choose_eq_zero_of_lt hkp]
  refine' multiplicity_ne_zero.1 fun h => hkp.not_le <| Nat.le_of_dvd hk.bot_lt _
  have H := hp.multiplicity_choose_prime_pow_add_multiplicity hkp.le hk
  rw [h, zero_add, eq_coe_iff] at H
  exact H.1
#align nat.prime.dvd_choose_pow Nat.Prime.dvd_choose_pow

theorem dvd_choose_pow_iff (hp : Prime p) : p ∣ (p ^ n).choose k ↔ k ≠ 0 ∧ k ≠ p ^ n := by
  refine' ⟨fun h => ⟨_, _⟩, fun h => dvd_choose_pow hp h.1 h.2⟩ <;> rintro rfl <;>
    simp [hp.ne_one] at h
#align nat.prime.dvd_choose_pow_iff Nat.Prime.dvd_choose_pow_iff

end Prime

theorem multiplicity_two_factorial_lt : ∀ {n : ℕ} (_ : n ≠ 0), multiplicity 2 n ! < n := by
  have h2 := prime_two.prime
  refine' binaryRec _ _
  · exact fun h => False.elim <| h rfl
  · intro b n ih h
    by_cases hn : n = 0
    · subst hn
      simp only [ne_eq, bit_eq_zero, true_and, Bool.not_eq_false] at h
      simp only [h, bit_true, bit1_zero, factorial, mul_one, Nat.isUnit_iff, cast_one]
      rw [Prime.multiplicity_one]
      simp only [zero_lt_one]
      decide
    have : multiplicity 2 (2 * n)! < (2 * n : ℕ) := by
      rw [prime_two.multiplicity_factorial_mul]
      refine' (PartENat.add_lt_add_right (ih hn) (PartENat.natCast_ne_top _)).trans_le _
      rw [two_mul]
      norm_cast
    cases b
    · simpa [bit0_eq_two_mul n]
    · suffices multiplicity 2 (2 * n + 1) + multiplicity 2 (2 * n)! < ↑(2 * n) + 1 by
        simpa [succ_eq_add_one, multiplicity.mul, h2, prime_two, Nat.bit1_eq_succ_bit0,
          bit0_eq_two_mul n]
      rw [multiplicity_eq_zero.2 (two_not_dvd_two_mul_add_one n), zero_add]
      refine' this.trans _
      exact_mod_cast lt_succ_self _
#align nat.multiplicity_two_factorial_lt Nat.multiplicity_two_factorial_lt

end Nat


import data.real.basic
import algebra.group.pi

set_option pp.beta true

/-
In this file, we'll learn about the ∀ quantifier, and the disjunction
operator ∨ (logical OR).

Let P be a predicate on a type X. This means for every mathematical
object x with type X, we get a mathematical statement P x.
In Lean, P x has type Prop.

Lean sees a proof h of `∀ x, P x` as a function sending any `x : X` to
a proof `h x` of `P x`.
This already explains the main way to use an assumption or lemma which
starts with a ∀.

In order to prove `∀ x, P x`, we use `intros x` to fix an arbitrary object
with type X, and call it x.

Note also we don't need to give the type of x in the expression `∀ x, P x`
as long as the type of P is clear to Lean, which can then infer the type of x.

Let's define two predicates to play with ∀.
-/

def even_fun (f : ℝ → ℝ) := ∀ x, f (-x) = f x

def odd_fun (f : ℝ → ℝ) := ∀ x, f (-x) = -f x

/-
In the next proof, we also take the opportunity to introduce the
`unfold` tactic, which simply unfolds definitions. Here this is purely
for didactic reason, Lean doesn't need those `unfold` invocations.
We will also use `rfl` which is a term proving equalities that are true
by definition (in a very strong sense to be discussed later).
-/

example (f g : ℝ → ℝ) : even_fun f → even_fun g →  even_fun (f + g) :=
begin
  -- Assume f is even
  intros hf,
  -- which means ∀ x, f (-x) = f x
  unfold even_fun at hf,
  -- and the same for g
  intros hg,
  unfold even_fun at hg,
  -- We need to prove ∀ x, (f+g)(-x) = (f+g)(x)
  unfold even_fun,
  -- Let x be any real number
  intros x,
  -- and let's compute
  calc (f + g) (-x) = f (-x) + g (-x) : rfl
  ... = f x + g (-x) : by rw hf x
  ... = f x + g x : by rw hg x
  ... = (f + g) x : rfl
end

/-
In the preceding proof, all `unfold` lines are purely for
psychological comfort.

Sometimes unfolding is necessary because we want to apply a tactic
that operates purely on the syntactical level.
The main such tactic is `rw`.

The same property of `rw` explain why the first computation line
is necessary, although its proof is simply `rfl`.
Before that line, `rw hf x` won't find anything like `f (-x)` hence
will give up.
The last line is not necessary however, since it only proves
something that is true by definition, and is not followed by
a `rw`.

Also, Lean doesn't need to be told that hf should be specialized to
x before rewriting, exactly as in the first file 01_equality_rewriting.
We can also gather several rewrites using a list of expressions.

One last trick is that `rw` can take a list of expressions to use for
rewriting. For instance `rw [h₁, h₂, h₃]` is equivalent to three
lines `rw h₁`, `rw h₂` and `rw h₃`. Note that you can inspect the tactic
state between those rewrites when reading a proof using this syntax. You
simply need to move the cursor inside the list.

Hence we can compress the above proof to:
-/

example (f g : ℝ → ℝ) : even_fun f → even_fun g →  even_fun (f + g) :=
begin
  intros hf hg x,
  calc (f + g) (-x) = f (-x) + g (-x) : rfl
  ... = f x + g x : by rw [hf, hg]
end

/-
Now let's practice. If you need to learn how to type a unicode symbol,
you can put your mouse cursor above the symbol and wait for one second.
-/

-- 0023
example (f g : ℝ → ℝ) : even_fun f → even_fun (g ∘ f) :=
begin
  -- sorry
  intros hf x,
  calc (g ∘ f) (-x) = g (f (-x)) : rfl
  ... = g (f x) : by rw hf
  -- sorry
end

-- 0024
example (f g : ℝ → ℝ) : odd_fun f → odd_fun g →  odd_fun (g ∘ f) :=
begin
  -- sorry
  intros hf hg x,
  calc (g ∘ f) (-x) = g (f (-x)) : rfl
  ... = - (g ∘ f) x : by rw [hf, hg],
  -- sorry
end

/-
Let's have more quantifiers, and play with forward and backward reasoning.

In the next definitions, note how `∀ x₁, ∀ x₂` is abreviated to `∀ x₁ x₂`.
-/

def non_decreasing (f : ℝ → ℝ) := ∀ x₁ x₂, x₁ ≤ x₂ → f x₁ ≤ f x₂

def non_increasing (f : ℝ → ℝ) := ∀ x₁ x₂, x₁ ≤ x₂ → f x₁ ≥ f x₂

/- Let's be very explicit and use forward reasoning first. -/
example (f g : ℝ → ℝ) (hf : non_decreasing f) (hg : non_decreasing g) : non_decreasing (g ∘ f) :=
begin
  -- Let x₁ and x₂ be real numbers such that x₁ ≤ x₂
  intros x₁ x₂ h,
  -- Since f is non-decreasing, f x₁ ≤ f x₂.
  have step₁ :  f x₁ ≤ f x₂,
    exact hf x₁ x₂ h,
  -- Since g is non-decreasing, we then get g (f x₁) ≤ g (f x₂).
  exact hg (f x₁) (f x₂) step₁,
end

/-
In the above proof, note how inconvenient it is to specify x₁ and x₂ in `hf x₁ x₂ h` since
they could be inferred from the type of h.
We could have written `hf _ _ h` and Lean would have filled the holes denoted by _.

Even better we could have written the definition
of `non_decreasing` as: ∀ {x₁ x₂}, x₁ ≤ x₂ → f x₁ ≤ f x₂, with curly braces to denote
implicit arguments.

But let's leave that aside for now. One possible variation on the above proof is to
use the `specialize` tactic to replace hf by its specialization to the relevant value.
 -/

example (f g : ℝ → ℝ) (hf : non_decreasing f) (hg : non_decreasing g) : non_decreasing (g ∘ f) :=
begin
  intros x₁ x₂ h,
  specialize hf x₁ x₂ h,
  exact hg (f x₁) (f x₂) hf,
end

/-
This `specialize` tactic is mostly useful for exploration, or in preparation for rewriting
in the assumption. One can very often replace its use by using more complicated expressions
directly involving the original assumption, as in the next variation:
-/
example (f g : ℝ → ℝ) (hf : non_decreasing f) (hg : non_decreasing g) : non_decreasing (g ∘ f) :=
begin
  intros x₁ x₂ h,
  exact hg (f x₁) (f x₂) (hf x₁ x₂ h),
end

/-
Since the above proof uses only `intros` and `exact`, we could very easily replace it by the
raw proof term:
-/
example (f g : ℝ → ℝ) (hf : non_decreasing f) (hg : non_decreasing g) : non_decreasing (g ∘ f) :=
λ x₁ x₂ h, hg (f x₁) (f x₂) (hf x₁ x₂ h)

/-
Of course the above proof is difficult to decipher. The principle in mathlib is to use
such a proof when the result is obvious and you don't want to read the proof anyway.

Instead of pursuing this style, let's see how backward reasoning would look like here.
As usual with this style, we use `apply` and enjoy Lean specializing assumptions for us
using unification.
-/

example (f g : ℝ → ℝ) (hf : non_decreasing f) (hg : non_decreasing g) : non_decreasing (g ∘ f) :=
begin
  -- Let x₁ and x₂ be real numbers such that x₁ ≤ x₂
  intros x₁ x₂ h,
  -- We need to prove (g ∘ f) x₁ ≤ (g ∘ f) x₂.
  -- Since g is non-decreasing, it suffices to prove f x₁ ≤ f x₂
  apply hg,
  -- which follows from our assumption on f
  apply hf,
  -- and on x₁ and x₂
  exact h
end

-- 0025
example (f g : ℝ → ℝ) (hf : non_decreasing f) (hg : non_increasing g) : non_increasing (g ∘ f) :=
begin
  -- sorry
  intros x₁ x₂ h,
  apply hg,
  exact hf x₁ x₂ h
  -- sorry
end

/-
Let's switch to disjunctions now. Lean denotes by ∨ the
logical OR operator.

In order to make use of an assumption
  hyp : P ∨ Q
we use the cases tactic:
  cases hyp with hP hQ
which creates two proof branches: one branch assuming hP : P,
and one branch assuming hQ : Q.

In order to directly prove a goal P ∨ Q,
we use either the `left` tactic and prove P or the `right`
tactic and prove Q.

In the next proof we use `ring` and `linarith` to get rid of
easy computations or inequalities, as well as one lemma:

  mul_eq_zero : a*b = 0 ↔ a = 0 ∨ b = 0
-/

example (a b : ℝ) : a = a*b → a = 0 ∨ b = 1 :=
begin
  intro hyp,
  have H : a*(1 - b) = 0,
  { calc a*(1 - b) = a - a*b : by ring
               ... = 0       : by linarith, },
  rw mul_eq_zero at H,
  cases H with Ha Hb,
  { left,
    exact Ha, },
  { right,
    linarith, },
end

-- 0026
example (x y : ℝ) : x^2 = y^2 → x = y ∨ x = -y :=
begin
  -- sorry
  intros hyp,
  have H : (x-y)*(x+y) = 0,
    calc (x-y)*(x+y) = x^2 - y^2 : by ring
    ... = y^2 - y^2 : by rw hyp
    ... = 0 : by ring,
  rw mul_eq_zero at H,
  cases H with h1 h2,
  { left,
    linarith, },
  { right,
    linarith, },
  -- sorry
end

/-
In the next exercise, we can use:
  eq_or_lt_of_le : x ≤ y → x = y ∨ x < y
-/

-- 0027
example (f : ℝ → ℝ) : non_decreasing f ↔ ∀ x y, x < y → f x ≤ f y :=
begin
  -- sorry
  split,
  { intros hf x y hxy,
    apply hf,
    linarith, },
  { intros hf x y hxy,
    have clef : x = y ∨ x < y,
    { exact eq_or_lt_of_le hxy },
    cases clef with hxy hxy,
    rw hxy,
    exact hf x y hxy, },
  -- sorry
end

/-
In the next exercise, we can use:
  le_total x y : x ≤ y ∨ y ≤ x
-/

-- 0028
example (f : ℝ → ℝ) (h : non_decreasing f) (h' : ∀ x, f (f x) = x) : ∀ x, f x = x :=
begin
  -- sorry
  intro x,
  have : f (f x) = x,
  { rw h' },
  have : (f x ≤ x) ∨ (x ≤ f x),
  { exact le_total (f x) x },
  cases this with hx hx,
  { have f1: f (f x) ≤ f x,
    { exact h (f x) x hx, },
    rw h' at f1,
    linarith, },
  { have f1: f x ≤ f (f x),
    { exact h x (f x) hx, },
    rw h' x at f1,
    linarith, },
  -- sorry
end


/-
Copyright (c) 2019 Jeremy Avigad. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jeremy Avigad, Benjamin Davidson
-/
import data.int.modeq
import data.nat.parity

/-!
# Parity of integers

This file contains theorems about the `even` and `odd` predicates on the integers.

## Tags

even, odd
-/

namespace int

variables {m n : ℤ}

@[simp] theorem mod_two_ne_one : ¬ n % 2 = 1 ↔ n % 2 = 0 :=
by cases mod_two_eq_zero_or_one n with h h; simp [h]

local attribute [simp] -- euclidean_domain.mod_eq_zero uses (2 ∣ n) as normal form
theorem mod_two_ne_zero : ¬ n % 2 = 0 ↔ n % 2 = 1 :=
by cases mod_two_eq_zero_or_one n with h h; simp [h]

@[simp] theorem even_coe_nat (n : ℕ) : even (n : ℤ) ↔ even n :=
have ∀ m, 2 * to_nat m = to_nat (2 * m),
 from λ m, by cases m; refl,
⟨λ ⟨m, hm⟩, ⟨to_nat m, by rw [this, ←to_nat_coe_nat n, hm]⟩,
 λ ⟨m, hm⟩, ⟨m, by simp [hm]⟩⟩

theorem even_iff : even n ↔ n % 2 = 0 :=
⟨λ ⟨m, hm⟩, by simp [hm], λ h, ⟨n / 2, (mod_add_div n 2).symm.trans (by simp [h])⟩⟩

theorem odd_iff : odd n ↔ n % 2 = 1 :=
⟨λ ⟨m, hm⟩, by { rw [hm, add_mod], norm_num },
 λ h, ⟨n / 2, (mod_add_div n 2).symm.trans (by { rw h, abel })⟩⟩

lemma not_even_iff : ¬ even n ↔ n % 2 = 1 :=
by rw [even_iff, mod_two_ne_zero]

lemma not_odd_iff : ¬ odd n ↔ n % 2 = 0 :=
by rw [odd_iff, mod_two_ne_one]

lemma even_iff_not_odd : even n ↔ ¬ odd n :=
by rw [not_odd_iff, even_iff]

@[simp] lemma odd_iff_not_even : odd n ↔ ¬ even n :=
by rw [not_even_iff, odd_iff]

lemma is_compl_even_odd : is_compl {n : ℕ | even n} {n | odd n} :=
by simp [← set.compl_set_of, is_compl_compl]

lemma even_or_odd (n : ℤ) : even n ∨ odd n :=
or.imp_right odd_iff_not_even.2 $ em $ even n

lemma even_or_odd' (n : ℤ) : ∃ k, n = 2 * k ∨ n = 2 * k + 1 :=
by simpa only [exists_or_distrib, ← odd, ← even] using even_or_odd n

lemma even_xor_odd (n : ℤ) : xor (even n) (odd n) :=
begin
  cases even_or_odd n with h,
  { exact or.inl ⟨h, even_iff_not_odd.mp h⟩ },
  { exact or.inr ⟨h, odd_iff_not_even.mp h⟩ },
end

lemma even_xor_odd' (n : ℤ) : ∃ k, xor (n = 2 * k) (n = 2 * k + 1) :=
begin
  rcases even_or_odd n with ⟨k, rfl⟩ | ⟨k, rfl⟩;
  use k,
  { simpa only [xor, true_and, eq_self_iff_true, not_true, or_false, and_false]
      using (succ_ne_self (2*k)).symm },
  { simp only [xor, add_right_eq_self, false_or, eq_self_iff_true, not_true, not_false_iff,
               one_ne_zero, and_self] },
end

@[simp] theorem two_dvd_ne_zero : ¬ 2 ∣ n ↔ n % 2 = 1 :=
not_even_iff

instance : decidable_pred (even : ℤ → Prop) :=
λ n, decidable_of_decidable_of_iff (by apply_instance) even_iff.symm

instance decidable_pred_odd : decidable_pred (odd : ℤ → Prop) :=
λ n, decidable_of_decidable_of_iff (by apply_instance) odd_iff_not_even.symm

@[simp] theorem even_zero : even (0 : ℤ) := ⟨0, dec_trivial⟩

@[simp] theorem not_even_one : ¬ even (1 : ℤ) :=
by rw even_iff; norm_num

@[simp] theorem even_bit0 (n : ℤ) : even (bit0 n) :=
⟨n, by rw [bit0, two_mul]⟩

@[parity_simps] theorem even_add : even (m + n) ↔ (even m ↔ even n) :=
by cases mod_two_eq_zero_or_one m with h₁ h₁;
   cases mod_two_eq_zero_or_one n with h₂ h₂;
   simp [even_iff, h₁, h₂, int.add_mod];
   norm_num

theorem even.add_even (hm : even m) (hn : even n) : even (m + n) :=
even_add.2 $ iff_of_true hm hn

theorem even_add' : even (m + n) ↔ (odd m ↔ odd n) :=
by rw [even_add, even_iff_not_odd, even_iff_not_odd, not_iff_not]

theorem odd.add_odd (hm : odd m) (hn : odd n) : even (m + n) :=
even_add'.2 $ iff_of_true hm hn

@[parity_simps] theorem even_neg : even (-n) ↔ even n :=
by simp [even_iff]

@[simp] theorem not_even_bit1 (n : ℤ) : ¬ even (bit1 n) :=
by simp [bit1] with parity_simps

lemma two_not_dvd_two_mul_add_one (n : ℤ) : ¬(2 ∣ 2 * n + 1) :=
by convert not_even_bit1 n; exact two_mul n

@[parity_simps] theorem even_sub : even (m - n) ↔ (even m ↔ even n) :=
by simp [sub_eq_add_neg] with parity_simps

theorem even.sub_even (hm : even m) (hn : even n) : even (m - n) :=
even_sub.2 $ iff_of_true hm hn

theorem even_sub' : even (m - n) ↔ (odd m ↔ odd n) :=
by rw [even_sub, even_iff_not_odd, even_iff_not_odd, not_iff_not]

theorem odd.sub_odd (hm : odd m) (hn : odd n) : even (m - n) :=
even_sub'.2 $ iff_of_true hm hn

@[parity_simps] theorem even_add_one : even (n + 1) ↔ ¬ even n :=
by simp [even_add]

@[parity_simps] theorem even_mul : even (m * n) ↔ even m ∨ even n :=
by cases mod_two_eq_zero_or_one m with h₁ h₁;
   cases mod_two_eq_zero_or_one n with h₂ h₂;
   simp [even_iff, h₁, h₂, int.mul_mod];
   norm_num

theorem odd_mul : odd (m * n) ↔ odd m ∧ odd n :=
by simp [not_or_distrib] with parity_simps

theorem even.mul_left (hm : even m) (n : ℤ) : even (m * n) :=
even_mul.mpr $ or.inl hm

theorem even.mul_right (m : ℤ) (hn : even n) : even (m * n) :=
even_mul.mpr $ or.inr hn

theorem odd.mul (hm : odd m) (hn : odd n) : odd (m * n) :=
odd_mul.mpr ⟨hm, hn⟩

theorem odd.of_mul_left (h : odd (m * n)) : odd m :=
(odd_mul.mp h).1

theorem odd.of_mul_right (h : odd (m * n)) : odd n :=
(odd_mul.mp h).2

@[parity_simps] theorem even_pow {n : ℕ} : even (m^n) ↔ even m ∧ n ≠ 0 :=
by { induction n with n ih; simp [*, even_mul, pow_succ], tauto }

@[parity_simps] theorem odd_add : odd (m + n) ↔ (odd m ↔ even n) :=
by rw [odd_iff_not_even, even_add, not_iff, odd_iff_not_even]

theorem odd.add_even (hm : odd m) (hn : even n) : odd (m + n) :=
odd_add.2 $ iff_of_true hm hn

theorem odd_add' : odd (m + n) ↔ (odd n ↔ even m) :=
by rw [add_comm, odd_add]

theorem even.add_odd (hm : even m) (hn : odd n) : odd (m + n) :=
odd_add'.2 $ iff_of_true hn hm

lemma ne_of_odd_add (h : odd (m + n)) : m ≠ n :=
λ hnot, by simpa [hnot] with parity_simps using h

@[parity_simps] theorem odd_sub : odd (m - n) ↔ (odd m ↔ even n) :=
by rw [odd_iff_not_even, even_sub, not_iff, odd_iff_not_even]

theorem odd.sub_even (hm : odd m) (hn : even n) : odd (m - n) :=
odd_sub.2 $ iff_of_true hm hn

theorem odd_sub' : odd (m - n) ↔ (odd n ↔ even m) :=
by rw [odd_iff_not_even, even_sub, not_iff, not_iff_comm, odd_iff_not_even]

theorem even.sub_odd (hm : even m) (hn : odd n) : odd (m - n) :=
odd_sub'.2 $ iff_of_true hn hm

lemma even_mul_succ_self (n : ℤ) : even (n * (n + 1)) :=
begin
  rw even_mul,
  convert n.even_or_odd,
  simp with parity_simps
end

-- Here are examples of how `parity_simps` can be used with `int`.

example (m n : ℤ) (h : even m) : ¬ even (n + 3) ↔ even (m^2 + m + n) :=
by simp [*, (dec_trivial : ¬ 2 = 0)] with parity_simps

example : ¬ even (25394535 : ℤ) :=
by simp

end int


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

import tactic -- imports all the Lean tactics
import measure_theory.measurable_space

/-

# Measure theory

## More on sigma algebras.

-/

-- Intersection of sigma algebras is a sigma algebra

-- Let 𝓐 be a family of sigma algebras on a type `X`
variables (X : Type) (I : Type) (𝓐 : I → measurable_space X)

-- Then their intersection is also a sigma algebra

open_locale measure_theory -- to get notation `measurable_set[S] U` for "U is in the sigma algebra S"
example : measurable_space X :=
{ measurable_set' := λ U, ∀ i : I, measurable_set[𝓐 i] U,
  measurable_set_empty := begin
    intro i,
    apply measurable_set.empty,
  end,
  measurable_set_compl := begin
    intros s hs i,
    apply measurable_set.compl,
    apply hs,
  end,
  measurable_set_Union := begin
    intros f h i,
    apply measurable_set.Union,
    intro j,
    apply h,
  end }

-- Lean knows that sigma algebras on X are a complete lattice
-- so you could also make it like this:
example : measurable_space X := ⨅ i, 𝓐 i

-- Sigma algebras are closed under countable intersection
-- Here, because there's only one sigma algebra involved,
-- I use the typeclass inference system to say "fix a canonical
-- sigma algebra on X" and just use that one throughout the question.
example (X : Type) [measurable_space X] (f : ℕ → set X)
  (hf : ∀ n, measurable_set (f n)) : measurable_set (⋂ n, f n) :=
begin
  rw ← measurable_set.compl_iff,
  rw set.compl_Inter,
  apply measurable_set.Union,
  intro b,
  apply measurable_set.compl,
  apply hf,
end


import data.real.basic

/-
In the previous file, we saw how to rewrite using equalities.
The analogue operation with mathematical statements is rewriting using
equivalences. This is also done using the `rw` tactic.
Lean uses ↔ to denote equivalence instead of ⇔.

In the following exercises we will use the lemma:

  sub_nonneg {x y : ℝ} : 0 ≤ y - x ↔ x ≤ y

The curly braces around x and y instead of parentheses mean Lean will always try to figure out what
x and y are from context, unless we really insist on telling it (we'll see how to insist much later).
Let's not worry about that for now.

In order to announce an intermediate statement we use:

  have my_name : my statement,

This triggers the apparition of a new goal: proving the statement. After this is done,
the statement becomes available under the name `my_name`.
We can focus on the current goal by typing tactics between curly braces.
-/

example {a b c : ℝ} (hab : a ≤ b) : c + a ≤ c + b :=
begin
  rw ← sub_nonneg,
  have key : (c + b) - (c + a) = b - a, -- Here we introduce an intermediate statement named key
  { ring, },  -- and prove it between curly braces
  rw key, -- we can now use the key statement
  rw sub_nonneg,
  exact hab,
end

/-
Of course the previous lemma is already in the core library, named `add_le_add_left`, so we can use it below.

Let's prove a variation (without invoking commutativity of addition since this would spoil our fun).
-/

-- 0009
example {a b : ℝ} (hab : a ≤ b) (c : ℝ) : a + c ≤ b + c :=
begin
  sorry
end

/-
Let's see how we could use this lemma. It is already in the core library, under the name `add_le_add_right`:

  add_le_add_right {a b : ℝ} (hab : a ≤ b) (c : ℝ) : a + c ≤ b + c

This can be read as: "add_le_add_right is a function that will take as input real numbers a and b, an
assumption `hab` claiming a ≤ b and a real number c, and will output a proof of a + c ≤ b + c".

In addition, recall that curly braces around a b mean Lean will figure out those arguments unless we
insist to help. This is because they can be deduced from the next argument `hab`.
So it will be sufficient to feed `hab` and c to this function.
-/

example {a b : ℝ}  (ha : 0 ≤ a) : b ≤ a + b :=
begin
  calc b = 0 + b : by ring
     ... ≤ a + b : by exact add_le_add_right ha b,
end

/-
In the second line of the above proof, we need to prove 0 + b ≤ a + b.
The proof after the colon says: this is exactly lemma `add_le_add_right` applied to ha and b.
Actually the `calc` block expects proof terms, and the `by` keyword is used to tell Lean we will use tactics
to build such a proof term. But since the only tactic used in this block is `exact`, we can skip
tactics entirely, and write:
-/

example (a b : ℝ)  (ha : 0 ≤ a) : b ≤ a + b :=
begin
  calc b = 0 + b : by ring
     ... ≤ a + b : add_le_add_right ha b,
end

/- Let's do a variant. -/

-- 0010
example (a b : ℝ) (hb : 0 ≤ b) : a ≤ a + b :=
begin
  sorry
end

/-
The two preceding examples are in the core library :

  le_add_of_nonneg_left  {a b : ℝ} (ha : 0 ≤ a) : b ≤ a + b
  le_add_of_nonneg_right {a b : ℝ} (hb : 0 ≤ b) : a ≤ a + b

Again, there won't be any need to memorize those names, we will
soon see how to get rid of such goals automatically.
But we can already try to understand how their names are built:
"le_add" describe the conclusion "less or equal than some addition"
It comes first because we are focussed on proving stuff, and
auto-completion works by looking at the beginning of words.
"of" introduces assumptions. "nonneg" is Lean's abbreviation for non-negative.
"left" or "right" disambiguates between the two variations.

Let's use those lemmas by hand for now.

Note that you can have several inequalities steps in a `calc` block,
transitivity of inequalities will be used automatically to assemble
the pieces.
-/

-- 0011
example (a b : ℝ) (ha : 0 ≤ a) (hb : 0 ≤ b) : 0 ≤ a + b :=
begin
  sorry
end

/- And let's combine with our earlier lemmas. -/

-- 0012
example (a b c d : ℝ) (hab : a ≤ b) (hcd : c ≤ d) : a + c ≤ b + d :=
begin
 sorry
end

/-
In the above examples, we prepared proofs of assumptions of our lemmas beforehand, so
that we could feed them to the lemmas. This is called forward reasoning.
The `calc` proofs also belong to this category.

We can also announce the use of a lemma, and provide proofs after the fact, using
the `apply` tactic. This is called backward reasoning because we get the conclusion
first, and provide proofs later. Using `rw` on the goal (rather than on an assumption
from the local context) is also backward reasoning.

Let's do that using the lemma

  mul_nonneg {x y : ℝ} (hx : 0 ≤ x) (hy : 0 ≤ y) : 0 ≤ x*y
-/

example (a b c  : ℝ) (hc : 0 ≤ c) (hab : a ≤ b) : a*c ≤ b*c :=
begin
  rw ← sub_nonneg,
  have key : b*c - a*c = (b - a)*c,
  { ring },
  rw key,
  apply mul_nonneg, -- Here we don't provide proofs for the lemma's assumptions
  -- Now we need to provide the proofs.
  { rw sub_nonneg,
    exact hab },
  { exact hc },
end

/-
Let's prove the same statement using only forward reasoning: announcing stuff,
proving it by working with known facts, moving forward.
-/

example (a b c  : ℝ) (hc : 0 ≤ c) (hab : a ≤ b) : a*c ≤ b*c :=
begin
  have hab' : 0 ≤ b - a,
  { rw ← sub_nonneg at hab,
    exact hab, },
  have h₁ : 0 ≤ (b - a)*c,
  { exact mul_nonneg hab' hc },
  have h₂ : (b - a)*c = b*c - a*c,
  { ring, },
  have h₃ : 0 ≤ b*c - a*c,
  { rw h₂ at h₁,
    exact h₁, },
  rw sub_nonneg at h₃,
  exact h₃,
end

/-
One reason why the backward reasoning proof is shorter is because Lean can
infer of lot of things by comparing the goal and the lemma statement. Indeed
in the `apply mul_nonneg` line, we didn't need to tell Lean that x = b - a
and y = c in the lemma. It was infered by "unification" between the lemma
statement and the goal.

To be fair to the forward reasoning version, we should introduce a convenient
variation on `rw`. The `rwa` tactic performs rewrite and then looks for an
assumption matching the goal. We can use it to rewrite our latest proof as:
-/

example (a b c  : ℝ) (hc : 0 ≤ c) (hab : a ≤ b) : a*c ≤ b*c :=
begin
  have hab' : 0 ≤ b - a,
  { rwa ← sub_nonneg at hab, },
  have h₁ : 0 ≤ (b - a)*c,
  { exact mul_nonneg hab' hc },
  have h₂ : (b - a)*c = b*c - a*c,
  { ring, },
  have h₃ : 0 ≤ b*c - a*c,
  { rwa h₂ at h₁, },
  rwa sub_nonneg at h₃,
end

/-
Let's now combine forward and backward reasoning, to get our most
efficient proof of this statement. Note in particular how unification is used
to know what to prove inside the parentheses in the `mul_nonneg` arguments.
-/
example (a b c : ℝ) (hc : 0 ≤ c) (hab : a ≤ b) : a*c ≤ b*c :=
begin
  rw ← sub_nonneg,
  calc 0 ≤ (b - a)*c  : mul_nonneg (by rwa sub_nonneg) hc
     ... =  b*c - a*c : by ring,
end

/-
Let's now practice all three styles using:

  mul_nonneg_of_nonpos_of_nonpos {a b : α} (ha : a ≤ 0) (hb : b ≤ 0) : 0 ≤ a * b

  sub_nonpos {a b : α} : a - b ≤ 0 ↔ a ≤ b
-/

/- First using mostly backward reasoning -/
-- 0013
example (a b c : ℝ) (hc : c ≤ 0) (hab :  a ≤ b) : b*c ≤ a*c :=
begin
  sorry
end

/- Using forward reasoning -/
-- 0014
example (a b c : ℝ) (hc : c ≤ 0) (hab :  a ≤ b) : b*c ≤ a*c :=
begin
  sorry
end

/-- Using a combination of both, with a `calc` block -/
-- 0015
example (a b c : ℝ) (hc : c ≤ 0) (hab :  a ≤ b) : b*c ≤ a*c :=
begin
  sorry
end

/-
Let's now move to proving implications. Lean denotes implications using
a simple arrow →, the same it uses for functions (say denoting the type of functions
from ℕ to ℕ by ℕ → ℕ). This is because it sees a proof of P ⇒ Q as a function turning
a proof of P into a proof Q.

Many of the examples that we already met are implications under the hood. For instance we proved

  le_add_of_nonneg_left (a b : ℝ) (ha : 0 ≤ a) : b ≤ a + b

But this can be rephrased as

  le_add_of_nonneg_left (a b : ℝ) : 0 ≤ a → b ≤ a + b

In order to prove P → Q, we use the tactic `intros`, followed by an assumption name.
This creates an assumption with that name asserting that P holds, and turns the goal into Q.

Let's check we can go from our old version of `le_add_of_nonneg_left` to the new one.

-/
example (a b : ℝ): 0 ≤ a → b ≤ a + b :=
begin
  intros ha,
  exact le_add_of_nonneg_left ha,
end

/-
Actually Lean doesn't make any difference between those two versions. It is also happy with
-/
example (a b : ℝ): 0 ≤ a → b ≤ a + b :=
le_add_of_nonneg_left

/- No tactic state is shown in the above line because we don't even need to enter
tactic mode using `begin` or `by`.

Let's practise using `intros`. -/

-- 0016
example (a b : ℝ): 0 ≤ b → a ≤ a + b :=
begin
  sorry
end

/-
What about lemmas having more than one assumption? For instance:

  add_nonneg {a b : ℝ} (ha : 0 ≤ a) (hb : 0 ≤ b) : 0 ≤ a + b

A natural idea is to use the conjunction operator (logical AND), which Lean denotes
by ∧. Assumptions built using this operator can be decomposed using the `cases` tactic,
which is a very general assumption-decomposing tactic.
-/
example {a b : ℝ} : (0 ≤ a ∧ 0 ≤ b) → 0 ≤ a + b :=
begin
  intros hyp,
  cases hyp with ha hb,
  exact add_nonneg ha hb,
end

/-
Needing that intermediate line invoking `cases` shows this formulation is not what is used by
Lean. It rather sees `add_nonneg` as two nested implications:
if a is non-negative then if b is non-negative then a+b is non-negative.
It reads funny, but it is much more convenient to use in practice.
-/
example {a b : ℝ} : 0 ≤ a → (0 ≤ b → 0 ≤ a + b) :=
add_nonneg

/-
The above pattern is so common that implications are defined as right-associative operators,
hence parentheses are not needed above.

Let's prove that the naive conjunction version implies the funny Lean version. For this we need
to know how to prove a conjunction. The `split` tactic creates two goals from a conjunction goal.
It can also be used to create two implication goals from an equivalence goal.
-/
example {a b : ℝ} (H : (0 ≤ a ∧ 0 ≤ b) → 0 ≤ a + b) : 0 ≤ a → (0 ≤ b → 0 ≤ a + b) :=
begin
  intros ha,
  intros hb,
  apply H,
  split,
  exact ha,
  exact hb,
end

/-
Let's practice `cases` and `split`. In the next exercise, P, Q and R denote
unspecified mathematical statements.
-/

-- 0017
example (P Q R : Prop) : P ∧ Q → Q ∧ P :=
begin
  sorry
end

/-
Of course using `split` only to be able to use `exact` twice in a row feels silly. One can
also use the anonymous constructor syntax: ⟨ ⟩
Beware those are not parentheses but angle brackets. This is a generic way of providing
compound objects to Lean when Lean already has a very clear idea of what it is waiting for.

So we could have replaced the last three lines by:
  exact ⟨hQ, hP⟩

We can also combine the `intros` steps. We can now compress our earlier proof to:
-/

example {a b : ℝ} (H : (0 ≤ a ∧ 0 ≤ b) → 0 ≤ a + b) : 0 ≤ a → (0 ≤ b → 0 ≤ a + b) :=
begin
  intros ha hb,
  exact H ⟨ha, hb⟩,
end

/-
The anonymous contructor trick actually also works in `intros` provided we use
its recursive version `rintros`. So we can replace
  intro h,
  cases h with h₁ h₂
by
  rintros ⟨h₁, h₂⟩,
Now redo the previous exercise using all those compressing techniques, in exactly two lines. -/

-- 0018
example (P Q R : Prop): P ∧ Q → Q ∧ P :=
begin
  sorry
end

/-
We are ready to come back to the equivalence between the different formulations of
lemmas having two assumptions. Remember the `split` tactic can be used to split
an equivalence into two implications.
-/

-- 0019
example (P Q R : Prop) : (P ∧ Q → R) ↔ (P → (Q → R)) :=
begin
  sorry
end

/-
If you used more than five lines in the above exercise then try to compress things
(without simply removing line ends).

One last compression technique: given a proof h of a conjunction P ∧ Q, one can get
a proof of P using h.left and a proof of Q using h.right, without using cases.
One can also use the more generic (but less legible) names h.1 and h.2.

Similarly, given a proof h of P ↔ Q, one can get a proof of P → Q using h.mp
and a proof of Q → P using h.mpr (or the generic h.1 and h.2 that are even less legible
in this case).

Before the final exercise in this file, let's make sure we'll be able to leave
without learning 10 lemma names. The `linarith` tactic will prove any equality or
inequality or contradiction that follows by linear combinations of assumptions from the
context (with constant coefficients).
-/

example (a b : ℝ) (hb : 0 ≤ b) : a ≤ a + b :=
begin
  linarith,
end

/-
Now let's enjoy this for a while.
-/

-- 0020
example (a b : ℝ) (ha : 0 ≤ a) (hb : 0 ≤ b) : 0 ≤ a + b :=
begin
  sorry
end

/- And let's combine with our earlier lemmas. -/

-- 0021
example (a b c d : ℝ) (hab : a ≤ b) (hcd : c ≤ d) : a + c ≤ b + d :=
begin
  sorry
end

/-
Final exercise

In the last exercise of this file, we will use the divisibility relation on ℕ,
denoted by ∣ (beware this is a unicode divisibility bar, not the ASCII pipe character),
and the gcd function.

The definitions are the usual ones, but our goal is to avoid using these definitions and
only use the following three lemmas:

  dvd_refl (a : ℕ) : a ∣ a

  dvd_antisymm {a b : ℕ} : a ∣ b → b ∣ a → a = b :=

  dvd_gcd_iff {a b c : ℕ} : c ∣ gcd a b ↔ c ∣ a ∧ c ∣ b
-/

-- All functions and lemmas below are about natural numbers.
open nat

-- 0022
example (a b : ℕ) : a ∣ b ↔ gcd a b = a :=
begin
  sorry
end


/-
Copyright (c) 2022 Eric Rodriguez. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Eric Rodriguez
-/
import algebra.field.ulift
import data.mv_polynomial.cardinal
import data.nat.factorization.prime_pow
import data.rat.denumerable
import field_theory.finite.galois_field
import logic.equiv.transfer_instance
import ring_theory.localization.cardinality
import set_theory.cardinal.divisibility

/-!
# Cardinality of Fields

In this file we show all the possible cardinalities of fields. All infinite cardinals can harbour
a field structure, and so can all types with prime power cardinalities, and this is sharp.

## Main statements

* `fintype.nonempty_field_iff`: A `fintype` can be given a field structure iff its cardinality is a
  prime power.
* `infinite.nonempty_field` : Any infinite type can be endowed a field structure.
* `field.nonempty_iff` : There is a field structure on type iff its cardinality is a prime power.

-/

local notation `‖` x `‖` := fintype.card x

open_locale cardinal non_zero_divisors

universe u

/-- A finite field has prime power cardinality. -/
lemma fintype.is_prime_pow_card_of_field {α} [fintype α] [field α] : is_prime_pow (‖α‖) :=
begin
  casesI char_p.exists α with p _,
  haveI hp := fact.mk (char_p.char_is_prime α p),
  let b := is_noetherian.finset_basis (zmod p) α,
  rw [module.card_fintype b, zmod.card, is_prime_pow_pow_iff],
  { exact hp.1.is_prime_pow },
  rw ←finite_dimensional.finrank_eq_card_basis b,
  exact finite_dimensional.finrank_pos.ne'
end

/-- A `fintype` can be given a field structure iff its cardinality is a prime power. -/
lemma fintype.nonempty_field_iff {α} [fintype α] : nonempty (field α) ↔ is_prime_pow (‖α‖) :=
begin
  refine ⟨λ ⟨h⟩, by exactI fintype.is_prime_pow_card_of_field, _⟩,
  rintros ⟨p, n, hp, hn, hα⟩,
  haveI := fact.mk hp.nat_prime,
  exact ⟨(fintype.equiv_of_card_eq ((galois_field.card p n hn.ne').trans hα)).symm.field⟩,
end

lemma fintype.not_is_field_of_card_not_prime_pow {α} [fintype α] [ring α] :
  ¬ is_prime_pow (‖α‖) → ¬ is_field α :=
mt $ λ h, fintype.nonempty_field_iff.mp ⟨h.to_field⟩

/-- Any infinite type can be endowed a field structure. -/
lemma infinite.nonempty_field {α : Type u} [infinite α] : nonempty (field α) :=
begin
  letI K := fraction_ring (mv_polynomial α $ ulift.{u} ℚ),
  suffices : #α = #K,
  { obtain ⟨e⟩ := cardinal.eq.1 this,
    exact ⟨e.field⟩ },
  rw ←is_localization.card (mv_polynomial α $ ulift.{u} ℚ)⁰ K le_rfl,
  apply le_antisymm,
  { refine ⟨⟨λ a, mv_polynomial.monomial (finsupp.single a 1) (1 : ulift.{u} ℚ), λ x y h, _⟩⟩,
    simpa [mv_polynomial.monomial_eq_monomial_iff, finsupp.single_eq_single_iff] using h },
  { simp }
end

/-- There is a field structure on type if and only if its cardinality is a prime power. -/
lemma field.nonempty_iff {α : Type u} : nonempty (field α) ↔ is_prime_pow (#α) :=
begin
  rw cardinal.is_prime_pow_iff,
  casesI fintype_or_infinite α with h h,
  { simpa only [cardinal.mk_fintype, nat.cast_inj, exists_eq_left',
        (cardinal.nat_lt_aleph_0 _).not_le, false_or]
      using fintype.nonempty_field_iff },
  { simpa only [← cardinal.infinite_iff, h, true_or, iff_true]
      using infinite.nonempty_field },
end


/-
The goal of this file is to prove that a linear operator
T : ℂ^n → ℂ^n is self-adjoint if and only if ⟪T v, v⟫_ℂ is
real for all v ∈ ℂ^n.

-- Checked over by:
-- Hans

-/

import .lemmas.ladr_7_lem

variable {n : ℕ}

localized "postfix `†`:1000 := linear_map.adjoint" in src

open_locale big_operators complex_conjugate matrix

variables (T : Lℂ^n) (u w : ℂ^n)

local notation `is_sa` := inner_product_space.is_self_adjoint

lemma is_self_adjoint_iff_eq_adjoint : is_sa T ↔ T = T† :=
  by rw [linear_map.eq_adjoint_iff, inner_product_space.is_self_adjoint]

/-
Linearity lemmas
-/
lemma inner_map_add_add : ⟪ T (u + w), u + w ⟫_ℂ = ⟪T u, u⟫_ℂ + ⟪T w, u⟫_ℂ + ⟪T u, w⟫_ℂ + ⟪T w, w⟫_ℂ :=
  by {rw [map_add, inner_add_left, inner_add_right, inner_add_right], ring}

lemma inner_map_sub_sub : ⟪ T (u - w), u - w ⟫_ℂ = ⟪T u, u⟫_ℂ - ⟪T w, u⟫_ℂ - ⟪T u, w⟫_ℂ + ⟪T w, w⟫_ℂ :=
  by {rw [map_sub,inner_sub_left, inner_sub_right, inner_sub_right], ring}

/-
Polarization identity
-/
lemma inner_map_polarization :
  4 * ⟪ T w, u ⟫_ℂ = ⟪T (u + w) , u + w⟫_ℂ - ⟪T (u - w) , u - w⟫_ℂ + I * ⟪T (u + I • w) , u + I • w⟫_ℂ - I * ⟪T (u - I • w), u - I • w ⟫_ℂ :=
begin
  symmetry,
  iterate {rw inner_map_add_add},
  iterate {rw inner_map_sub_sub},
  rw [linear_map.map_smul, inner_smul_left, inner_smul_right],
  ring_nf,
  rw complex.conj_I,
  ring_nf,
  rw complex.I_sq,
  ring_nf,
end

/-
If ⟪T u, u⟫_ℂ = 0 for all u ∈ ℂ^n, then ⟪T u, w⟫_ℂ = 0 for all u, w ∈ ℂ^n.
-/
lemma inner_map_eq_zero (h : ∀ (u : ℂ^n), ⟪T u, u⟫_ℂ = 0) :
  ∀ (u w : ℂ^n), ⟪T u, w ⟫_ℂ = 0 :=
begin
  have : ∀ u w : ℂ^n, 4 * ⟪T u, w ⟫_ℂ = 0 :=
  begin
    intros u w,
    rw inner_map_polarization,
    iterate {rw h},
    ring,
  end,
  intros u w,
  specialize this u w,
  simp only [false_or, bit0_eq_zero, one_ne_zero, mul_eq_zero] at this,
  exact this,
end

/-
If ⟪T u, u⟫_ℂ = 0 for all u ∈ ℂ^n, then ⟪T u, w⟫_ℂ = 0 for all u, w ∈ ℂ^n.
-/
lemma inner_map_self_eq_zero (h : ∀ u : ℂ^n, ⟪T u, u⟫_ℂ = 0) :
  T = 0 :=
begin
  have : ∀ u w : ℂ^n, ⟪T u, w ⟫_ℂ = 0 := inner_map_eq_zero T h,
  apply linear_map.ext,
  intro x,
  specialize this x (T x),
  rw inner_self_eq_zero at this,
  rw linear_map.zero_apply,
  exact this,
end

/-
If T is self-adjoint, then ⟪T v, v⟫_ℂ is real for all v ∈ ℂ
-/
lemma self_adjoint_real_inner (h : is_sa T) :
  ∀ v : ℂ^n, conj ⟪T v, v⟫_ℂ = ⟪T v, v⟫_ℂ :=
begin
  intro v,
  apply inner_product_space.is_self_adjoint.conj_inner_sym,
  exact h,
end

/-
If ⟪T v, v⟫_ℂ is real for all v ∈ ℂ^n, then T is self-adjoint.
-/
lemma real_inner_self_adjoint (h : ∀ v : ℂ^n, conj ⟪T v, v⟫_ℂ = ⟪T v, v⟫_ℂ) :
  is_sa T :=
begin
  rw is_self_adjoint_iff_eq_adjoint,
  rw ← sub_eq_zero,
  apply inner_map_self_eq_zero,
  intro v,
  specialize h v,
  rw linear_map.sub_apply,
  rw inner_sub_left,
  rw linear_map.adjoint_inner_left,
  rw ← h,
  rw inner_conj_sym,
  rw sub_self,
end

/-
T is self-adjoint if and only if ⟪T v, v⟫_ℂ
-/
lemma is_self_adjoint_iff_real_inner_map : (is_sa T) ↔ ∀ v : ℂ^n, conj ⟪T v, v⟫_ℂ = ⟪T v, v⟫_ℂ :=
begin
  split,
  intro h,
  apply self_adjoint_real_inner T h,
  intro h,
  apply real_inner_self_adjoint T h,
end


/-
Copyright (c) 2019 Jeremy Avigad. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jeremy Avigad, Benjamin Davidson
-/
import data.nat.parity

/-!
# Parity of integers

This file contains theorems about the `even` and `odd` predicates on the integers.

## Tags

even, odd
-/

namespace int

variables {m n : ℤ}

@[simp] theorem mod_two_ne_one : ¬ n % 2 = 1 ↔ n % 2 = 0 :=
by cases mod_two_eq_zero_or_one n with h h; simp [h]

local attribute [simp] -- euclidean_domain.mod_eq_zero uses (2 ∣ n) as normal form
theorem mod_two_ne_zero : ¬ n % 2 = 0 ↔ n % 2 = 1 :=
by cases mod_two_eq_zero_or_one n with h h; simp [h]

theorem even_iff : even n ↔ n % 2 = 0 :=
⟨λ ⟨m, hm⟩, by simp [hm], λ h, ⟨n / 2, (mod_add_div n 2).symm.trans (by simp [h])⟩⟩

theorem odd_iff : odd n ↔ n % 2 = 1 :=
⟨λ ⟨m, hm⟩, by { rw [hm, add_mod], norm_num },
 λ h, ⟨n / 2, (mod_add_div n 2).symm.trans (by { rw h, abel })⟩⟩

lemma not_even_iff : ¬ even n ↔ n % 2 = 1 :=
by rw [even_iff, mod_two_ne_zero]

lemma not_odd_iff : ¬ odd n ↔ n % 2 = 0 :=
by rw [odd_iff, mod_two_ne_one]

lemma even_iff_not_odd : even n ↔ ¬ odd n :=
by rw [not_odd_iff, even_iff]

@[simp] lemma odd_iff_not_even : odd n ↔ ¬ even n :=
by rw [not_even_iff, odd_iff]

lemma is_compl_even_odd : is_compl {n : ℤ | even n} {n | odd n} :=
by simp [← set.compl_set_of, is_compl_compl]

lemma even_or_odd (n : ℤ) : even n ∨ odd n :=
or.imp_right odd_iff_not_even.2 $ em $ even n

lemma even_or_odd' (n : ℤ) : ∃ k, n = 2 * k ∨ n = 2 * k + 1 :=
by simpa only [exists_or_distrib, ← odd, ← even] using even_or_odd n

lemma even_xor_odd (n : ℤ) : xor (even n) (odd n) :=
begin
  cases even_or_odd n with h,
  { exact or.inl ⟨h, even_iff_not_odd.mp h⟩ },
  { exact or.inr ⟨h, odd_iff_not_even.mp h⟩ },
end

lemma even_xor_odd' (n : ℤ) : ∃ k, xor (n = 2 * k) (n = 2 * k + 1) :=
begin
  rcases even_or_odd n with ⟨k, rfl⟩ | ⟨k, rfl⟩;
  use k,
  { simpa only [xor, true_and, eq_self_iff_true, not_true, or_false, and_false]
      using (succ_ne_self (2*k)).symm },
  { simp only [xor, add_right_eq_self, false_or, eq_self_iff_true, not_true, not_false_iff,
               one_ne_zero, and_self] },
end

@[simp] theorem two_dvd_ne_zero : ¬ 2 ∣ n ↔ n % 2 = 1 :=
not_even_iff

instance : decidable_pred (even : ℤ → Prop) :=
λ n, decidable_of_decidable_of_iff (by apply_instance) even_iff.symm

instance decidable_pred_odd : decidable_pred (odd : ℤ → Prop) :=
λ n, decidable_of_decidable_of_iff (by apply_instance) odd_iff_not_even.symm

@[simp] theorem even_zero : even (0 : ℤ) := ⟨0, dec_trivial⟩

@[simp] theorem not_even_one : ¬ even (1 : ℤ) :=
by rw even_iff; norm_num

@[simp] theorem even_bit0 (n : ℤ) : even (bit0 n) :=
⟨n, by rw [bit0, two_mul]⟩

@[parity_simps] theorem even_add : even (m + n) ↔ (even m ↔ even n) :=
by cases mod_two_eq_zero_or_one m with h₁ h₁;
   cases mod_two_eq_zero_or_one n with h₂ h₂;
   simp [even_iff, h₁, h₂, int.add_mod];
   norm_num

theorem even.add_even (hm : even m) (hn : even n) : even (m + n) :=
even_add.2 $ iff_of_true hm hn

theorem even_add' : even (m + n) ↔ (odd m ↔ odd n) :=
by rw [even_add, even_iff_not_odd, even_iff_not_odd, not_iff_not]

theorem odd.add_odd (hm : odd m) (hn : odd n) : even (m + n) :=
even_add'.2 $ iff_of_true hm hn

@[simp] theorem not_even_bit1 (n : ℤ) : ¬ even (bit1 n) :=
by simp [bit1] with parity_simps

lemma two_not_dvd_two_mul_add_one (n : ℤ) : ¬(2 ∣ 2 * n + 1) :=
by convert not_even_bit1 n; exact two_mul n

@[parity_simps] theorem even_sub : even (m - n) ↔ (even m ↔ even n) :=
by simp [sub_eq_add_neg] with parity_simps

theorem even.sub_even (hm : even m) (hn : even n) : even (m - n) :=
even_sub.2 $ iff_of_true hm hn

theorem even_sub' : even (m - n) ↔ (odd m ↔ odd n) :=
by rw [even_sub, even_iff_not_odd, even_iff_not_odd, not_iff_not]

theorem odd.sub_odd (hm : odd m) (hn : odd n) : even (m - n) :=
even_sub'.2 $ iff_of_true hm hn

@[parity_simps] theorem even_add_one : even (n + 1) ↔ ¬ even n :=
by simp [even_add]

@[parity_simps] theorem even_mul : even (m * n) ↔ even m ∨ even n :=
by cases mod_two_eq_zero_or_one m with h₁ h₁;
   cases mod_two_eq_zero_or_one n with h₂ h₂;
   simp [even_iff, h₁, h₂, int.mul_mod];
   norm_num

theorem odd_mul : odd (m * n) ↔ odd m ∧ odd n :=
by simp [not_or_distrib] with parity_simps

theorem even.mul_left (hm : even m) (n : ℤ) : even (m * n) :=
even_mul.mpr $ or.inl hm

theorem even.mul_right (m : ℤ) (hn : even n) : even (m * n) :=
even_mul.mpr $ or.inr hn

theorem odd.mul (hm : odd m) (hn : odd n) : odd (m * n) :=
odd_mul.mpr ⟨hm, hn⟩

theorem odd.of_mul_left (h : odd (m * n)) : odd m :=
(odd_mul.mp h).1

theorem odd.of_mul_right (h : odd (m * n)) : odd n :=
(odd_mul.mp h).2

@[parity_simps] theorem even_pow {n : ℕ} : even (m ^ n) ↔ even m ∧ n ≠ 0 :=
by { induction n with n ih; simp [*, even_mul, pow_succ], tauto }

theorem even_pow' {n : ℕ} (h : n ≠ 0) : even (m ^ n) ↔ even m :=
even_pow.trans $ and_iff_left h

@[parity_simps] theorem odd_add : odd (m + n) ↔ (odd m ↔ even n) :=
by rw [odd_iff_not_even, even_add, not_iff, odd_iff_not_even]

theorem odd.add_even (hm : odd m) (hn : even n) : odd (m + n) :=
odd_add.2 $ iff_of_true hm hn

theorem odd_add' : odd (m + n) ↔ (odd n ↔ even m) :=
by rw [add_comm, odd_add]

theorem even.add_odd (hm : even m) (hn : odd n) : odd (m + n) :=
odd_add'.2 $ iff_of_true hn hm

lemma ne_of_odd_add (h : odd (m + n)) : m ≠ n :=
λ hnot, by simpa [hnot] with parity_simps using h

@[parity_simps] theorem odd_sub : odd (m - n) ↔ (odd m ↔ even n) :=
by rw [odd_iff_not_even, even_sub, not_iff, odd_iff_not_even]

theorem odd.sub_even (hm : odd m) (hn : even n) : odd (m - n) :=
odd_sub.2 $ iff_of_true hm hn

theorem odd_sub' : odd (m - n) ↔ (odd n ↔ even m) :=
by rw [odd_iff_not_even, even_sub, not_iff, not_iff_comm, odd_iff_not_even]

theorem even.sub_odd (hm : even m) (hn : odd n) : odd (m - n) :=
odd_sub'.2 $ iff_of_true hn hm

lemma even_mul_succ_self (n : ℤ) : even (n * (n + 1)) :=
begin
  rw even_mul,
  convert n.even_or_odd,
  simp with parity_simps
end

@[simp, norm_cast] theorem even_coe_nat (n : ℕ) : even (n : ℤ) ↔ even n :=
by rw_mod_cast [even_iff, nat.even_iff]

@[simp, norm_cast] theorem odd_coe_nat (n : ℕ) : odd (n : ℤ) ↔ odd n :=
by rw [odd_iff_not_even, nat.odd_iff_not_even, even_coe_nat]

@[simp] theorem nat_abs_even : even n.nat_abs ↔ even n :=
coe_nat_dvd_left.symm

@[simp] theorem nat_abs_odd : odd n.nat_abs ↔ odd n :=
by rw [odd_iff_not_even, nat.odd_iff_not_even, nat_abs_even]

-- Here are examples of how `parity_simps` can be used with `int`.

example (m n : ℤ) (h : even m) : ¬ even (n + 3) ↔ even (m^2 + m + n) :=
by simp [*, (dec_trivial : ¬ 2 = 0)] with parity_simps

example : ¬ even (25394535 : ℤ) :=
by simp

end int


/- Here is a theorem and proof.  If there was anything wrong
   with it then Lean would complain.  As the proof is correct,
   Lean just accepts it silently.  To get more positive feedback,
   we use #check, which essentially restates the theorem.
-/

theorem one_plus_one : 1 + 1 ≤ 2 :=
/-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This line states a theorem, followed by " := ".
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-/
begin
 apply refl,
end
/-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This is the body of the proof.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-/

theorem one_plus_one_alt : 1 + 1 = 2 := rfl
/-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 The above proof was written in tactic mode, as indicated by
 the keywords `begin` and `end`.  This mode is most appropriate
 for long proofs with complex logical flow and many local
 definitions, which is of course not the case here.  It would
 be more efficient to use term mode, as shown here.
 Note that the previous proof uses the word `refl`, which is
 the name of a proof tactic.  The current proof uses the word
 `rfl`, which is the name of the fact that `x = x` for all `x`.
 The word `rfl` counts as a proof here, because we can just
 unwind the definitions of `1`, `2` and `+` and then the two
 sides of our equation become identical.  The proof tactic
 called `refl` just applies the fact called `rfl` together with
 some similar facts (such as the fact that `n ≤ n` for all
 natural numbers `n`).
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-/


import data.real.basic

/-
One of the earliest kind of proofs one encounters while learning mathematics is proving by
a calculation. It may not sound like a proof, but this is actually using lemmas expressing
properties of operations on numbers. It also uses the fundamental property of equality: if two
mathematical objects A and B are equal then, in any statement involving A, one can replace A
by B. This operation is called rewriting, and the Lean "tactic" for this is `rw`.

In the following exercises, we will use the following two lemmas:
  mul_assoc a b c : a * b * c = a * (b * c)
  mul_comm a b : a*b = b*a

Hence the command
  rw mul_assoc a b c,
will replace a*b*c by a*(b*c) in the current goal.

In order to replace backward, we use
  rw ← mul_assoc a b c,
replacing a*(b*c) by a*b*c in the current goal.

Of course we don't want to constantly invoke those lemmas, and we will eventually introduce
more powerful solutions.
-/

example (a b c : ℝ) : (a * b) * c = b * (a * c) :=
begin
  rw mul_comm a b,
  rw mul_assoc b a c,
end

-- 0001
example (a b c : ℝ) : (c * b) * a = b * (a * c) :=
begin
  rw mul_comm c b,
  rw mul_assoc,
  rw mul_comm c a,
end

-- 0002
example (a b c : ℝ) : a * (b * c) = b * (a * c) :=
begin
  rw ←mul_assoc,
  rw mul_comm a b,
  rw mul_assoc,
end

/-
Now let's return to the preceding example to experiment with what happens
if we don't give arguments to mul_assoc or mul_comm.
For instance, you can start the next proof with
  rw ← mul_assoc,
Try to figure out what happens.
-/

-- 0003
example (a b c : ℝ) : a * (b * c) = b * (a * c) :=
begin
  rw ←mul_assoc,
  rw mul_comm a b,
  rw mul_assoc,
end

/-
We can also perform rewriting in an assumption of the local context, using for instance
  rw mul_comm a b at hyp,
in order to replace a*b by b*a in assumption hyp.

The next example will use a third lemma:
  two_mul a : 2*a = a + a

Also we use the `exact` tactic, which allows to provide a direct proof term.
-/

example (a b c d : ℝ) (hyp : c = d*a + b) (hyp' : b = a*d) : c = 2*a*d :=
begin
  rw hyp' at hyp,
  rw mul_comm d a at hyp,
  rw ← two_mul (a*d) at hyp,
  rw ← mul_assoc 2 a d at hyp,
  exact hyp, -- Our assumption hyp is now exactly what we have to prove
end

/-
And the next one can use:
  sub_self x : x - x = 0
-/

-- 0004
example (a b c d : ℝ) (hyp : c = b*a - d) (hyp' : d = a*b) : c = 0 :=
begin
  rw mul_comm at hyp',
  rw hyp' at hyp,
  rwa sub_self at hyp,
end

/-
What is written in the two preceding example is very far away from what we would write on
paper. Let's now see how to get a more natural layout.
Inside each pair of curly braces below, the goal is to prove equality with the preceding line.
-/

example (a b c d : ℝ) (hyp : c = d*a + b) (hyp' : b = a*d) : c = 2*a*d :=
begin
  calc c = d*a + b : by { rw hyp }
  ... = d*a + a*d  : by { rw hyp' }
  ... = a*d + a*d  : by { rw mul_comm d a }
  ... = 2*(a*d)    : by { rw two_mul }
  ... = 2*a*d      : by { rw mul_assoc },
end

/-
Let's note there is no comma at the end of each line of calculation. `calc` is really one
command, and the comma comes only after it's fully done.

From a practical point of view, when writing such a proof, it is convenient to:
* pause the tactic state view update in VScode by clicking the Pause icon button
  in the top right corner of the Lean Goal buffer
* write the full calculation, ending each line with ": by {}"
* resume tactic state update by clicking the Play icon button and fill in proofs between
  curly braces.

Let's return to the other example using this method.
-/

-- 0005
example (a b c d : ℝ) (hyp : c = b*a - d) (hyp' : d = a*b) : c = 0 :=
begin
  calc c = b*a - d : by { rw hyp }
  ... = b*a - a*b  : by { rw hyp'}
  ... = a*b - a*b  : by { rw mul_comm }
  ... = 0          : by { rw sub_self }
end

/-
The preceding proofs have exhauted our supply of "mul_comm" patience. Now it's time
to get the computer to work harder. The `ring` tactic will prove any goal that follows by
applying only the axioms of commutative (semi-)rings, in particuler commutativity and
associativity of addition and multiplication, as well as distributivity.

We also note that curly braces are not necessary when we write a single tactic proof, so
let's get rid of them.
-/

example (a b c d : ℝ) (hyp : c = d*a + b) (hyp' : b = a*d) : c = 2*a*d :=
begin
  calc c = d*a + b   : by rw hyp
     ... = d*a + a*d : by rw hyp'
     ... = 2*a*d     : by ring,
end

/-
Of course we can use `ring` outside of `calc`. Let's do the next one in one line.
-/

-- 0006
example (a b c : ℝ) : a * (b * c) = b * (a * c) := by ring

/-
This is too much fun. Let's do it again.
-/

-- 0007
example (a b : ℝ) : (a + b) + a = 2*a + b := by ring

/-
Maybe this is cheating. Let's try to do the next computation without ring.
We could use:
pow_two x : x^2 = x*x
mul_sub a b c : a*(b-c) = a*b - a*c
add_mul a b c : (a+b)*c = a*c + b*c
add_sub a b c : a + (b - c) = (a + b) - c
sub_sub a b c : a - b - c = a - (b + c)
add_zero a : a + 0 = a
-/

-- 0008
example (a b : ℝ) : (a + b)*(a - b) = a^2 - b^2 := by ring

/- Let's stick to ring in the end. -/


/-
Justin Ngo
jmn4fms
4/28/20
Sullivan 2102-001
-/

/-
HOMEWORK #9

There is no need to import our previous definitions.
For this homework you will just use Lean's built-in
notations and definitions.
-/

/-
Prove the following. Note that you can read each of
the propositions to be proved as either a logical
statement or as simply a function definition. Use
what you already know about the latter to arrive
at a proof, and then understand the proof as one
that shows that the logical statement is true.
-/

theorem t1 {P Q : Prop} (p2q : P → Q) (p : P) : Q :=
/-If you have a proof of P → Q p2q, and a proof of P,
  you can get a proof of Q -/
  p2q p

theorem t2 {P Q R : Prop} (p2q : P → Q) (q2r : Q → R): P → R :=
  λ (p : P), -- Assume I have a proof of P, called p
    q2r (p2q p)

/-
Use "example" to state and prove the preceding two
theorems but using "cases" style notation rather
than C-style. Remember, "example" is a way to state
a proposition/type and give an example of a value.
Here's an example of the use of "example". Give
your answers following this example.
-/

-- example
example : ℕ := 5

-- Your two answers here
-- by cases
-- theorem t1 {P Q : Prop} (p2q : P → Q) (p : P) : Q :=
example: ∀ {P Q : Prop}, (P → Q) → P → Q :=
λ P Q p2q,
  λ p,
    p2q p

-- theorem t2 {P Q R : Prop} (p2q : P → Q) (q2r : Q → R): P → R :=
example: ∀ {P Q R : Prop}, (P → Q) → (Q → R) → P → R :=
λ P Q R p2q q2r,
  λ p,
    q2r (p2q p)

/-
Now give English-language versions of your two proofs.

1. If for and proposition P and Q, if P implies Q, then
    if we have a proof of P we can get a proof of Q.

2. If for any propositoins P, Q, and R, if P implies Q, and
    if Q implies R, then if we have a proof of P we can get
    a proof of R.
-/

/-
Prove the following using case analysis on one
of the arguments (i.e., use match...with...end
at a key point in your proof). Use "cases" style
notation.
-/

-- false elimination, false implies anything
theorem t3' : ∀ (P : Prop), false → P
/- For any proposition P, if false if true than p is true-/
| P f := false.elim f
-- false elim on f (false) to get p

theorem t3 : ∀ (P : Prop), false → P
| P f := match f with /-no cases-/ end

/-
Prove false → true by applying t3 to a proposition.
You have to figure out which one.
-/
theorem t4 : false → true := t3 true
-- apply t3 to true to get a proof of false → true

/-
Define t5 to be the same as t3 but with P taken as
an implicit argument.
-/
theorem t5 : ∀ {P : Prop}, false → P
| P f := false.elim f

/-
Define t6 to be a proof of false → true by
applying t5 to the right argument(s).
-/
theorem t6 : false → true := t5
-- implicit so we do not need to provide proposition

/-
That is almost magic. In English, t3 proves
that false implies *any* proposition, so just
*apply* t3 to *true* in particular, but use t5
instead of t3.

What you see here is really important: Once
we've proved a general theorem (a ∀ proposition)
we can *apply the proof* to any *particular* case
to yield a proof for that specific case. This is
the elimination rule for ∀. It is also known as
universal instantiation (UI).
-/

/-
Next we see the idea that test cases are really
just equality propositions to be proved. Here,
for example, is a definition of the factorial
function.
-/

def fac : ℕ → ℕ
| 0 := 1
| (n' + 1) := (n' + 1) * fac n'

/-
Use "example" to write test cases for the
first ten natural number arguments to this
function.
-/

example : fac 0 = 1 := eq.refl 1
example : fac 1 = 1 := eq.refl _  -- Inferred
example : fac 2 = 2 := rfl        -- Shorthand
#check @rfl           -- infers type and value

-- The rest of your answers here
example : fac 3 = 6 := eq.refl _
example : fac 4 = 24 := eq.refl _
example : fac 5 = 120 := eq.refl _

/-
Insight: A test case is an equality proposition.
It is proved by "running" the program under test
to reduce the application of the function to input
arguments to produce an output that is then asserted
to be equal to an expected output.

In many cases, all we have to do is to simplify
the expressions on each side of the eq to see if
they reduce to exactly the same value. If so, we
can *apply* eq.refl (a universal generalization!)
to that value. Using rfl we can avoid even having
to type that value in cases where Lean can infer
it.
-/

/-
The next problem requires thave you give a proof of
a bi-implication, a proposition whose connective is
↔. To prove a bi-implication requires that one prove
an implication in each direction.

Here you are asked to prove P ∧ Q ↔ Q ∧ P. What this
formula asserts is that ∧ is commutative. To construct
a proof of this proposition you will have to apply
iff.intro to two smaller proofs, one of P → Q and
and of Q → P.

Start by "assuming" that P and Q are arbitary but
specific propositions (∀ introduction), then apply
iff.intro to two "stubbed out" arguments (underscores).
We suggest that you put the underscores in parentheses
on different lines. Then recursively fill in each of
these stubs with the required types of proofs. Study
the context that Lean shows you in its Messages panel
to see what you have to work with at each point in
your proof constructions.
-/

theorem t7 : ∀ {P Q : Prop}, P ∧ Q ↔ Q ∧ P :=
-- need to use iff.intro (to prove if)
-- and and.intro to solve
λ P Q,
  iff.intro
    (λ pandq,
      and.intro pandq.right pandq.left)
    (λ qandp,
      and.intro qandp.right qandp.left)

-- P ∧ Q -- and.intro p q
-- P ↔ Q -- iff.intro p2q q2p
  -- Assume P then show Q (P → Q)
  -- Assume Q then show P (Q → P)

theorem and_intro {P Q : Prop} (pfPtoQ : P → Q) (pfQtoP : Q → P) : P ↔ Q :=
iff.intro pfPtoQ pfQtoP
/-
and.intro _ _ (Given pfP and pfQ, proves P ∧ Q)
-/

/-
In English, when asked to prove P ↔ Q, one says, "it
will suffice to show P → Q and then to show Q → P." One
then goes on to give a proof of each implication. It
then follows from iff.intro that a proof of P ↔ Q can
be constructed, proving the bi-implication.
-/

/-
The trick here is to do case analysis on porq
(use match ... with ... end) and to show that
a proof of R can be constructed *in either case*.
-/

/-
P ∨ Q can be constructed in 2 ways:
or.inl pfP (proves anything or P)
or.inr pfQ (proves anything or Q)
-/

theorem t8
          {P Q R : Prop}
          (p2r : P → R)
          (q2r : Q → R)
          (porq : P ∨ Q) :
          R :=
  match porq with
    | or.inl p := p2r p
    | or.inr q := q2r q
    end

theorem t8'
          {P Q R : Prop}
          (p2r : P → R)
          (q2r : Q → R)
          (porq : P ∨ Q) :
          R :=
  or.elim porq p2r q2r

/- Prove P ∨ Q → R,
  it will suffice to show P → R and then Q → R
  because if we do tha then we can use
  or.elim to get a proof of R.-/

/-
We suggest that you use  "let ... in" to give
names to intermediate results that you then combine
in a final expression to finish the proof.
-/
theorem t9 : ∀ (P Q: Prop), (P → Q) → ¬ (P ∧ ¬ Q) :=
λ P Q,
  λ (p2q : (P → Q)),
    λ (pandnq : (P ∧ ¬ Q)),
    -- ¬ (P ∧ ¬ Q) == (P ∧ ¬ Q) → false
      ( let p := pandnq.left in
        let nq := pandnq.right in
        let q := p2q p in
        nq q -- adding "¬" to (pandnq :(P ∧ ¬ Q))
      )

/-
To prove ¬ P, assume P and show that this leads to
a contradiction, then from that derive a proof of false,
and use false.elim to finish the proof.

* This is called "proof by negation"
-/

/-
Proof by contradiction.
To prove P, assume ¬ P, and show that leads to
a contradiction. This shows ¬ (¬ P). Now, use
the *classical* principle of negation elimination
to deduce P
-/

/-
theorem neg_elim' : ∀ (P : Prop), ¬ ¬ P → P :=
λ P,
λ nnp,
_           -- STUCK!!
-/

theorem neg_elim : ∀ (P : Prop), (P ∨ ¬ P) → (¬ ¬ P → P):=
λ P, -- Assume P is an arbitrary proposition
    λ excl_middle, -- Assume law of excluded middle/ "p or not p" (h)
        λ nnp,
            match excl_middle with -- case analysis on excl_middle (h)
            | or.inl p := p
            | or.inr np := false.elim (nnp np)  -- false elimination on a proof of false
            end

-- nnp : (¬ P) → false
-- np : ¬ P
-- nnp np = false!

-- Let's use excl_middle/ ("p or not p")/ (h) to mean
-- There is a sub-exponential time algorithm for Boolean sat.
-- ¬ excl_middle (h) means that there's not one.
-- (excl_middle ∨ ¬ excl_middle) H ∨ ¬H

-- make Lean into a classical logic
axiom em : ∀ (P : Prop), P ∨ ¬ P
#check classical.em
open classical

theorem t10 : ∀ (P : Prop), P ∨ ¬ P :=
  classical.em
  /-λ P,                      -- forall introduction
    λ (h : P ∨ ¬P),           -- proof by negation
      let p := (h.left) in    -- and.elim_left
      let np := (h.right) in  -- and.elim_right
      (np p) -/

#check @or.inl
#check @or.inr

theorem t11 : ∀ (P Q : Prop), ¬ (P ∨ Q) ↔ ¬ P ∧ ¬ Q :=
λ P Q,
    iff.intro
        -- first case
        (λ not_porq, -- Assume proof of ¬ (P ∨ Q)
            match (classical.em P) with -- Proof of p or not p
              | or.inl p := false.elim (not_porq (or.inl p))
              -- ¬ (P ∨ Q) == (P ∨ Q → false)
              -- use false.elim when a case is not possible
              | or.inr np :=
                  match (classical.em Q) with
                    | or.inl q := false.elim (not_porq (or.inr q))
                    | or.inr nq := and.intro np nq
                  end
            end
        )
        -- second case
        (λ np_and_nq,
          λ porq,
          -- need to do case analysis on the disjuntion
            match (porq) with
              | or.inl p := and.elim_left np_and_nq p
              | or.inr q := and.elim_right np_and_nq q
            end
        )

theorem t12 : ∀ (P Q : Prop), ¬ (P ∧ Q) ↔ ¬ P ∨ ¬ Q :=
λ P Q,
  iff.intro
    (λ (n_pandq),
      match (classical.em P) with
        | or.inl p :=
            match (classical.em Q) with
              | or.inl q := false.elim (n_pandq _)
              | or.inr nq := _
            end
        | or.inr np :=
          match (classical.em P) with
            | or.inl p := _
            | or.inr np := _
      end
    )
    _

/-
For the following exercises, we assume that there is
a type called Person and a binary relation, Likes, on
pairs of people.
-/
axiom Person : Type
axiom Likes : Person → Person → Prop

/-
Prove the following
-/
theorem t13 :
  (∃ (p : Person), ∀ (q : Person), Likes q p) →
  -- If there is someone that everyone likes
  (∀ (p : Person), ∃ (q : Person), Likes p q) :=
  -- Then everyone likes someone
λ h, -- Assume there is some person everyone likes
    match h with
    | exists.intro p pf :=
        λ q,
            (exists.intro p (pf q))
    end

/-
theorem t13' :
  (∃ (p : Person), ∀ (q : Person), Likes q p) →
  -- If there is someone that everyone likes
  (∀ (p : Person), ∃ (q : Person), Likes p q) :=
  -- Then everyone likes someone
λ h, -- Assume there is some person everyone likes
  λ p,
      (match h with
        | exists.intro loved pf := exists.intro loved _
      end
      )
-/

-- Notes
/-axiom Person : Type
axioms (Nice : Person → Prop)(Old : Person → Prop)

example : (∃ (p : Person), Nice p ∨ Old p) →
            (∃ (p : Person), Nice p) ∨
            (∃ (p : Person), Old p) :=
λ p_nice_or_old,
    match p_nice_or_old with
        | exists.intro fred pf_fred_either_nice_or_old :=
            -- need to break down proof of fred nice or old, elim
            match pf_fred_either_nice_or_old with
                | or.inl n := or.inl (exists.intro fred n)
                | or.inr o := or.inr (exists.intro fred o)
            end
        end
-/

/- Notes (not answer)
Answer looks like:
Proof by induction. Our aim is to prove that
for all n, the number of combinations of n bool variables
is 2^n. It will suffice to show that [two cases]
-/

-- exists.intro and exists.elim
-- intro rule builds a proof of an or (or.inl/ or.inr)
-- and elim rule uses a proof of an or

/-
Test cases for question 4
#reduce all_zero_interp (a_var.mk 5)

#reduce an_interp (a_var.mk 0) = 3
#reduce an_interp (a_var.mk 1) = 7
#reduce an_interp (a_var.mk 2) = 1
#reduce an_interp (a_var.mk 4) = 0
-/


/-
Copyright (c) 2022 Pierre-Alexandre Bazin. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Pierre-Alexandre Bazin
-/
import analysis.seminorm
/-!
# The lattice of seminorms is not distributive

We provide an example of three seminorms over the ℝ-vector space ℝ×ℝ which don't satisfy the lattice
distributivity property `(p ⊔ q1) ⊓ (p ⊔ q2) ≤ p ⊔ (q1 ⊓ q2)`.

This proves the lattice `seminorm ℝ (ℝ × ℝ)` is not distributive.

## References

* https://en.wikipedia.org/wiki/Seminorm#Examples
-/

namespace seminorm_not_distrib
open_locale nnreal

private lemma bdd_below_range_add {𝕜 E : Type*} [normed_field 𝕜] [add_comm_group E] [module 𝕜 E]
  (x : E) (p q : seminorm 𝕜 E) :
  bdd_below (set.range (λ (u : E), p u + q (x - u))) :=
by { use 0, rintro _ ⟨x, rfl⟩, exact add_nonneg (p.nonneg _) (q.nonneg _) }

@[simps] noncomputable def p : seminorm ℝ (ℝ×ℝ) :=
(norm_seminorm ℝ ℝ).comp (linear_map.fst _ _ _) ⊔ (norm_seminorm ℝ ℝ).comp (linear_map.snd _ _ _)

@[simps] noncomputable def q1 : seminorm ℝ (ℝ×ℝ) :=
(4 : ℝ≥0) • (norm_seminorm ℝ ℝ).comp (linear_map.fst _ _ _)

@[simps] noncomputable def q2 : seminorm ℝ (ℝ×ℝ) :=
(4 : ℝ≥0) • (norm_seminorm ℝ ℝ).comp (linear_map.snd _ _ _)

lemma eq_one : (p ⊔ (q1 ⊓ q2)) (1, 1) = 1 :=
begin
  dsimp [-seminorm.inf_apply],
  rw [sup_idem, norm_one, sup_eq_left],
  apply cinfi_le_of_le (bdd_below_range_add _ _ _) ((0, 1) : ℝ×ℝ), dsimp,
  simp only [norm_zero, smul_zero, sub_self, add_zero, zero_le_one]
end

/-- This is a counterexample to the distributivity of the lattice `seminorm ℝ (ℝ × ℝ)`. -/
lemma not_distrib : ¬((p ⊔ q1) ⊓ (p ⊔ q2) ≤ p ⊔ (q1 ⊓ q2)) :=
begin
  intro le_sup_inf,
  have c : ¬(4/3 ≤ (1:ℝ)) := by norm_num,
  apply c, nth_rewrite 2 ← eq_one,
  apply le_trans _ (le_sup_inf _),
  apply le_cinfi, intro x,
  cases le_or_lt x.fst (1/3) with h1 h1,
  { cases le_or_lt x.snd (2/3) with h2 h2,
    { calc 4/3 = 4 * (1 - 2/3) : by norm_num
           ... ≤ 4 * (1 - x.snd) : (mul_le_mul_left zero_lt_four).mpr (sub_le_sub_left h2 _)
           ... ≤ 4 * |1 - x.snd| : (mul_le_mul_left zero_lt_four).mpr (le_abs_self _)
           ... = q2 ((1, 1) - x) : rfl
           ... ≤ (p ⊔ q2) ((1, 1) - x) : le_sup_right
           ... ≤ (p ⊔ q1) x + (p ⊔ q2) ((1, 1) - x) : le_add_of_nonneg_left ((p ⊔ q1).nonneg _) },
    { calc 4/3 = 2/3 + (1 - 1/3) : by norm_num
           ... ≤ x.snd + (1 - x.fst) : add_le_add (le_of_lt h2) (sub_le_sub_left h1 _)
           ... ≤ |x.snd| + |1 - x.fst| : add_le_add (le_abs_self _) (le_abs_self _)
           ... ≤ p x + p ((1, 1) - x) : add_le_add le_sup_right le_sup_left
           ... ≤ (p ⊔ q1) x + (p ⊔ q2) ((1, 1) - x) : add_le_add le_sup_left le_sup_left } },
  { calc 4/3 = 4 * (1/3) : by norm_num
         ... ≤ 4 * x.fst : (mul_le_mul_left zero_lt_four).mpr (le_of_lt h1)
         ... ≤ 4 * |x.fst| : (mul_le_mul_left zero_lt_four).mpr (le_abs_self _)
         ... = q1 x : rfl
         ... ≤ (p ⊔ q1) x : le_sup_right
         ... ≤ (p ⊔ q1) x + (p ⊔ q2) ((1, 1) - x) : le_add_of_nonneg_right ((p ⊔ q2).nonneg _) }
end

end seminorm_not_distrib


-- ---------------------------------------------------------------------
-- Ejercicio. Demostrar que para todo par de números reales x e y, si
-- x < |y|, entonces x < y ó x < -y.
-- ----------------------------------------------------------------------

import data.real.basic

variables {x y : ℝ}

-- 1ª Demostración
-- ===============

example : x < abs y → x < y ∨ x < -y :=
begin
  cases le_or_gt 0 y with h1 h2,
  { rw abs_of_nonneg h1,
    intro h3,
    left,
    exact h3 },
  { rw abs_of_neg h2,
    intro h4,
    right,
    exact h4 },
end

-- Prueba
-- ======

/-
x y : ℝ
⊢ x < abs y → x < y ∨ x < -y
  >> cases le_or_gt 0 y with h1 h2,
| h1 : 0 ≤ y
| ⊢ x < abs y → x < y ∨ x < -y
|   >> { rw abs_of_nonneg h1,
| ⊢ x < y → x < y ∨ x < -y
|   >>   intro h3,
| h3 : x < y
| ⊢ x < y ∨ x < -y
|   >>   left,
| ⊢ x < y
|   >>   exact h3 },
h2 : 0 > y
⊢ x < abs y → x < y ∨ x < -y
  >> { rw abs_of_neg h2,
⊢ x < -y → x < y ∨ x < -y
  >>   intro h4,
h4 : x < -y
⊢ x < y ∨ x < -y
  >>   right,
⊢ x < -y
  >>   exact h4 },
no goals
-/

-- Comentarios:
-- + La táctica (cases h with h1 h2), cuando h es una diyunción, aplica
--   la regla de eliminación de la disyunción; es decir, si h es (P ∨ Q)
--   abre dos casos, en el primero añade la hipótesis (h1 : P) y en el
--   segundo (h2 : Q).
-- + Se han usado los siguientes lemas
--   + le_or_gt x y : x ≤ y ∨ x > y
--   + abs_of_nonneg : 0 ≤ x → abs x = x
--   + abs_of_neg : x < 0 → abs x = -x

-- Comprobación
-- #check (@le_or_gt ℝ _ x y)
-- #check (@abs_of_nonneg ℝ _ x)
-- #check (@abs_of_neg ℝ _ x)

-- 2ª Demostración
-- ===============

example : x < abs y → x < y ∨ x < -y :=
lt_abs.mp

-- Comentario: Se ha usado el lema
-- + lt_abs : x < abs y ↔ x < y ∨ x < -y

-- Comprobación
-- #check (@lt_abs ℝ _ x y)


-- https://mathcs.clarku.edu/~djoyce/java/elements/bookIX/propIX20.html

-- Definitions about natural numbers and primes
import data.nat.prime

-- Library on linear arithmatic
import tactic.linarith

-- Define namespace, which is natural numbers in this case
open nat

-- Define theorem or goal to prove
theorem infinitude_of_primes: ∀ N, ∃ p >= N, prime p :=
-- between begin-end block we write tactics
begin
  -- define N to be a natural number as a part of our local hypothesis
  intro N,

  -- Continue with proof as mentioned in link provided in header
  -- let M to be N! + 1
  let M := factorial N + 1,

  -- let p be smallest prime factor of M which is not 1
  let p := min_fac M,

  -- define supporting hypothesis pp, p is prime
  have pp : prime p :=
  -- begin proof for supporting p being prime
  begin
    -- minimum factor of a number is prime, but what about if M = 1
    refine min_fac_prime _,
    -- so here we prove M != 1 or M > 1
    have : factorial N > 0 := factorial_pos N,
    -- this just automatically takes care of linear arithmatic required for proof
    linarith,
  end,

  -- before this we had existenial statement but now we have condition in p
  use p,

  -- split our goal in  2 subgoals
  split,

  -- proof by contradiction so it should output False
  {by_contradiction,

   /- hypothesis h1, p divides N! + 1 proved by
   min_fac_dvd : ∀ (n : ℕ), n.min_fac ∣ n
   -/
   have h₁ : p ∣ factorial N + 1 := min_fac_dvd M,

   -- hypothesis h2, p divides N!
   have h₂ : p ∣  factorial N :=
   begin
     refine pp.dvd_factorial.mpr _,
     -- proved p <= N, using hypothsis h
     exact le_of_not_ge h,
   end,
   /-
   proved using dvd_add_right with support from local hypothesis h₂ and h₁
   -/
   have h : p ∣ 1 := (nat.dvd_add_right h₂).mp h₁,
   -- prime not dividing one using local hypothesis pp and h
   exact prime.not_dvd_one pp h, },
   -- second part of proof is just our hypothesis pp that we already proved
  {exact pp, },
end


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

import data.nat.prime
import tactic.ring

open nat

/-!
# IMO 1959 Q1
Prove that the fraction `(21n+4)/(14n+3)` is irreducible for every natural number `n`.
Since Lean doesn't have a concept of "irreducible fractions" per se, we just formalize this
as saying the numerator and denominator are relatively prime.
-/

lemma calculation (n k : ℕ) (h1 : k ∣ 21 * n + 4) (h2 : k ∣ 14 * n + 3) : k ∣ 1 :=
have h3 : k ∣ 2 * (21 * n + 4), from dvd_mul_of_dvd_right h1 2,
have h4 : k ∣ 3 * (14 * n + 3), from dvd_mul_of_dvd_right h2 3,
have h5 : 3 * (14 * n + 3) = 2 * (21 * n + 4) + 1, by ring,
(nat.dvd_add_right h3).mp (h5 ▸ h4)

theorem imo1959_q1 : ∀ n : ℕ, coprime (21 * n + 4) (14 * n + 3) :=
assume n, coprime_of_dvd' $ λ k hp h1 h2, calculation n k h1 h2