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
  rw ← sub_nonneg,
  have key : (b+c)-(a+c) = b-a,
  ring,
  rw key,
  rw sub_nonneg,
  exact hab,
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
  calc a = a + 0 : by ring
  ... ≤ a + b : add_le_add_left hb a,
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
  calc 0 ≤ a : ha
  ... ≤ a + b : le_add_of_nonneg_right hb,
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
 calc a+c ≤ b+c : add_le_add_right hab c
 ... ≤ b+d : add_le_add_left hcd b,
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
  rw ← sub_nonneg,
  have key : a*c-b*c=(a-b)*c,
  {ring,},
  rw key,
  apply mul_nonneg_of_nonpos_of_nonpos,
  { rwa sub_nonpos },
  exact hc,
end

/- Using forward reasoning -/
-- 0014
example (a b c : ℝ) (hc : c ≤ 0) (hab :  a ≤ b) : b*c ≤ a*c :=
begin
  have hab' : a - b ≤ 0,
  -- { rw ← sub_nonpos at hab, exact hab, },
  { rwa ← sub_nonpos at hab, },
  have h₁ : 0 ≤ (a-b) * c,
  { exact mul_nonneg_of_nonpos_of_nonpos hab' hc, },
  have h₂ : (a-b)*c = a*c - b*c,
  { ring, },
  rw h₂ at h₁,
  rw sub_nonneg at h₁,
  exact h₁,
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
  rw ← sub_nonneg,
  calc 0 ≤ (a-b)*c : by exact mul_nonneg_of_nonpos_of_nonpos (by rwa sub_nonpos) hc
  ... = a*c - b*c : by ring,
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
  intros ha,
  exact le_add_of_nonneg_right ha,
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
  intro ha,
  cases ha with hb hc,
  split,
  exact hc,
  exact hb,
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
  rintros ⟨h₁, h₂⟩,
  exact ⟨h₂, h₁⟩,
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
  split,
  {
    intros ha p q,
    exact ha ⟨p, q⟩,
  },
  {
    rintros ha ⟨hbl, hbr⟩,
    exact ha hbl hbr,
  },
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
  linarith,
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
  linarith,
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
  -- split,
  -- have fact : gcd a b ∣ a ∧ gcd a b ∣ b,
  -- {
  --   have h : gcd a b ∣ gcd a b,
  --   { exact dvd_refl (gcd a b) },
  --   rwa dvd_gcd_iff at h,
  -- },
  -- cases fact,
  -- {
  --   intro hab,
  --   apply dvd_antisymm,
  --   { exact fact_left },
  --   {
  --     apply dvd_gcd_iff.mpr,
  --     exact ⟨(by exact dvd_refl a), hab⟩,
  --   },
  -- },
  -- {
  --   intro ha,
  --   have hc: a ∣ gcd a b,
  --   { rw ha },
  --   rw dvd_gcd_iff at hc,
  --   exact hc.right,
  -- },

  have fact : gcd a b ∣ a ∧ gcd a b ∣ b,
  { rw ← dvd_gcd_iff },
  split,
  {
    intro hab,
    apply dvd_antisymm fact.left,
    rw dvd_gcd_iff,
    exact ⟨dvd_refl a, hab⟩,
  },
  {
    intro ha,
    rw ← ha,
    exact fact.right,
  },
end


import complex.kb_solutions.conj -- solutions to exercise 2
/-!

# Exercise 3: Norms

Define `norm_sq : ℂ → ℝ` by defining `norm_sq(z)` to be `re(z)*re(z)+im(z)*im(z)` and see
what you can prove about it.

-/

namespace complex

/-- The real number which is the squared norm of z -/
def norm_sq (z : ℂ) : ℝ := re(z)*re(z)+im(z)*im(z)

/-! ## Behaviour with respect to 0, 1 and I -/

@[simp] lemma norm_sq_zero : norm_sq 0 = 0 :=
by simp [norm_sq]

@[simp] lemma norm_sq_one : norm_sq 1 = 1 :=
by simp [norm_sq]

@[simp] lemma norm_sq_I : norm_sq I = 1 :=
by simp [norm_sq]

/-! ## Behaviour with respect to *, + and - -/

@[simp] lemma norm_sq_mul (z w : ℂ) : norm_sq (z * w) = norm_sq z * norm_sq w :=
begin
  simp [norm_sq],
  ring,
end

lemma norm_sq_add (z w : ℂ) : norm_sq (z + w) =
  norm_sq z + norm_sq w + 2 * (z * conj w).re :=
begin
  simp [norm_sq],
  ring,
end

@[simp] lemma norm_sq_neg (z : ℂ) : norm_sq (-z) = norm_sq z :=
begin
  simp [norm_sq],
end

/-! ## Behaviour with respect to `conj` -/

@[simp] lemma norm_sq_conj (z : ℂ) : norm_sq (conj z) = norm_sq z :=
begin
  simp [norm_sq],
end

/-! ## Behaviour with respect to real 0, ≤, < and so on -/

end complex

-- Computer scientists tell me some theory of the reals is complete
-- So why so I have to prove these by hand?

lemma real_tac1 (x y : ℝ) : 0 ≤ x * x + y * y :=
begin
  apply add_nonneg;
  apply mul_self_nonneg,
end

lemma real_tac2 (x y : ℝ) : x * x + y * y = 0 ↔ x = 0 ∧ y = 0 :=
begin
  split,
  { intro h,
    rw add_eq_zero_iff' at h,
    { simp * at *},
    { apply mul_self_nonneg},
    { apply mul_self_nonneg}},
  { rintros ⟨rfl, rfl⟩,
    simp,
  }
end

namespace complex
-- Introducing
lemma norm_sq_nonneg (z : ℂ) : 0 ≤ norm_sq z :=
begin
  simp [norm_sq],
  -- (x y : ℝ) ⊢ 0 ≤ x * x + y * y
  apply real_tac1,
end

@[simp] lemma norm_sq_eq_zero {z : ℂ} : norm_sq z = 0 ↔ z = 0 :=
begin
  cases z with x y,
  simp [norm_sq],
  apply real_tac2,
end

@[simp] lemma norm_sq_pos {z : ℂ} : 0 < norm_sq z ↔ z ≠ 0 :=
begin
  rw lt_iff_le_and_ne,
  rw ne,
  rw eq_comm,
  simp [norm_sq_nonneg],
end

lemma re_sq_le_norm_sq (z : ℂ) : z.re * z.re ≤ norm_sq z :=
begin
  cases z with x y,
  simp [norm_sq],
  apply mul_self_nonneg,
end

lemma im_sq_le_norm_sq (z : ℂ) : z.im * z.im ≤ norm_sq z :=
begin
  cases z with x y,
  simp [norm_sq],
  apply mul_self_nonneg,
end

end complex


import tuto_lib
import data.int.parity
/-
Negations, proof by contradiction and contraposition.

This file introduces the logical rules and tactics related to negation:
exfalso, by_contradiction, contrapose, by_cases and push_neg.

There is a special statement denoted by `false` which, by definition,
has no proof.

So `false` implies everything. Indeed `false → P` means any proof of
`false` could be turned into a proof of P.
This fact is known by its latin name
"ex falso quod libet" (from false follows whatever you want).
Hence Lean's tactic to invoke this is called `exfalso`.
-/

example : false → 0 = 1 :=
begin
  intro h,
  exfalso,
  exact h,
end

/-
The preceding example suggests that this definition of `false` isn't very useful.
But actually it allows us to define the negation of a statement P as
"P implies false" that we can read as "if P were true, we would get
a contradiction". Lean denotes this by `¬ P`.

One can prove that (¬ P) ↔ (P ↔ false). But in practice we directly
use the definition of `¬ P`.
-/

example {x : ℝ} : ¬ x < x :=
begin
  intro hyp,
  rw lt_iff_le_and_ne at hyp,
  cases hyp with hyp_inf hyp_non,
  clear hyp_inf, -- we won't use that one, so let's discard it
  change x = x → false at hyp_non, -- Lean doesn't need this psychological line
  apply hyp_non,
  refl,
end

open int

-- 0045
example (n : ℤ) (h_even : even n) (h_not_even : ¬ even n) : 0 = 1 :=
begin
  exfalso,
  exact h_not_even h_even,
end

-- 0046
example (P Q : Prop) (h₁ : P ∨ Q) (h₂ : ¬ (P ∧ Q)) : ¬ P ↔ Q :=
begin
  split,
  {
    intro Pnot,
    cases h₁ with P Q,
    { exfalso,
      exact Pnot P },
    { exact Q },
  },
  {
    intros Q P,
    exact h₂ (and.intro P Q),
  }
end

/-
The definition of negation easily implies that, for every statement P,
P → ¬ ¬ P

The excluded middle axiom, which asserts P ∨ ¬ P allows us to
prove the converse implication.

Together those two implications form the principle of double negation elimination.
  not_not {P : Prop} : (¬ ¬ P) ↔ P

The implication `¬ ¬ P → P` is the basis for proofs by contradiction:
in order to prove P, it suffices to prove ¬¬ P, ie `¬ P → false`.

Of course there is no need to keep explaining all this. The tactic
`by_contradiction Hyp` will transform any goal P into `false` and
add Hyp : ¬ P to the local context.

Let's return to a proof from the 5th file: uniqueness of limits for a sequence.
This cannot be proved without using some version of the excluded middle
axiom. We used it secretely in

eq_of_abs_sub_le_all (x y : ℝ) : (∀ ε > 0, |x - y| ≤ ε) → x = y

(we'll prove a variation on this lemma below).

In the proof below, we also take the opportunity to introduce the `let` tactic
which creates a local definition. If needed, it can be unfolded using `dsimp` which
takes a list of definitions to unfold. For instance after using `let N₀ := max N N'`,
you could write `dsimp [N₀] at h` to replace `N₀` by its definition in some
local assumption `h`.
-/
example (u : ℕ → ℝ) (l l' : ℝ) : seq_limit u l → seq_limit u l' → l = l' :=
begin
  intros hl hl',
  by_contradiction H,
  change l ≠ l' at H, -- Lean does not need this line
  have ineg : |l-l'| > 0,
    exact abs_pos.mpr (sub_ne_zero_of_ne H), -- details about that line are not important
  cases hl ( |l-l'|/4 ) (by linarith) with N hN,
  cases hl' ( |l-l'|/4 ) (by linarith) with N' hN',
  let N₀ := max N N',
  specialize hN N₀ (le_max_left _ _),
  specialize hN' N₀ (le_max_right _ _),
  have clef : |l-l'| < |l-l'|,
    calc
    |l - l'| = |(l-u N₀) + (u N₀ -l')|   : by ring_nf
         ... ≤ |l - u N₀| + |u N₀ - l'|  : by apply abs_add
         ... = |u N₀ - l| + |u N₀ - l'|  : by rw abs_sub_comm
         ... < |l-l'|                    : by linarith,
  linarith, -- linarith can also find simple numerical contradictions
end

/-
Another incarnation of the excluded middle axiom is the principle of
contraposition: in order to prove P ⇒ Q, it suffices to prove
non Q ⇒ non P.
-/

-- Using a proof by contradiction, let's prove the contraposition principle
-- 0047
example (P Q : Prop) (h : ¬ Q → ¬ P) : P → Q :=
begin
  intro hP,
  by_contradiction hnQ,
  exact h hnQ hP,
end

/-
Again Lean doesn't need this principle explained to it. We can use the
`contrapose` tactic.
-/

example (P Q : Prop) (h : ¬ Q → ¬ P) : P → Q :=
begin
  contrapose,
  exact h,
end

/-
In the next exercise, we'll use
 odd n : ∃ k, n = 2*k + 1
 int.odd_iff_not_even {n : ℤ} : odd n ↔ ¬ even n
-/
-- 0048
example (n : ℤ) : even (n^2) ↔ even n :=
begin
  split,
  { contrapose,
    rw ← int.odd_iff_not_even,
    rw ← int.odd_iff_not_even,
    rintro ⟨m,rfl⟩,
    use (2*m^2 + 2*m),
    ring,
  },
  { rintro ⟨m,rfl⟩,
    use 2 * m^2,
    ring },
end
/-
As a last step on our law of the excluded middle tour, let's notice that, especially
in pure logic exercises, it can sometimes be useful to use the
excluded middle axiom in its original form:
  classical.em : ∀ P, P ∨ ¬ P

Instead of applying this lemma and then using the `cases` tactic, we
have the shortcut
 by_cases h : P,

combining both steps to create two proof branches: one assuming
h : P, and the other assuming h : ¬ P

For instance, let's prove a reformulation of this implication relation,
which is sometimes used as a definition in other logical foundations,
especially those based on truth tables (hence very strongly using
excluded middle from the very beginning).
-/

variables (P Q : Prop)

example : (P → Q) ↔ (¬ P ∨ Q) :=
begin
  split,
  { intro h,
    by_cases hP : P,
    { right,
      exact h hP },
    { left,
      exact hP } },
  { intros h hP,
    cases h with hnP hQ,
    { exfalso,
      exact hnP hP },
    { exact hQ } },
end

-- 0049
example : ¬ (P ∧ Q) ↔ ¬ P ∨ ¬ Q :=
begin
  split,
  {
    intros h,
    by_cases hP : P,
    {
      right,
      intro hQ,
      exact h ⟨hP,hQ⟩,
    },
    {
      left,
      exact hP,
    }
  },
  {
    rintros h1 ⟨hP,hQ⟩,
    cases h1 with hnP hnQ,
    { exact hnP hP },
    { exact hnQ hQ },
  }
end

/-
It is crucial to understand negation of quantifiers.
Let's do it by hand for a little while.
In the first exercise, only the definition of negation is needed.
-/

-- 0050
example (n : ℤ) : ¬ (∃ k, n = 2*k) ↔ ∀ k, n ≠ 2*k :=
begin
  split,
  {
    intros h1 k h2,
    exact h1 ⟨k, h2⟩,
  },
  {
    rintros h1 ⟨k,h2⟩,
    exact h1 k h2,
  },
end

/-
Contrary to negation of the existential quantifier, negation of the
universal quantifier requires excluded middle for the first implication.
In order to prove this, we can use either
* a double proof by contradiction
* a contraposition, not_not : (¬ ¬ P) ↔ P) and a proof by contradiction.
-/

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

-- 0051
example (f : ℝ → ℝ) : ¬ even_fun f ↔ ∃ x, f (-x) ≠ f x :=
begin
  unfold even_fun,
  -- push_neg, -- then done
  split,
  {
    contrapose,
    intros h,
    rw not_not,
    intros x,
    by_contradiction h2,
    -- exact h ⟨x,h2⟩,
    apply h,
    use x,
  },
  {
    rintros ⟨x, hx⟩ h2,
    -- by_contradiction h2, -- can use rintros
    exact hx (h2 x),
  },
end

/-
Of course we can't keep repeating the above proofs, especially the second one.
So we use the `push_neg` tactic.
-/

example : ¬ even_fun (λ x, 2*x) :=
begin
  unfold even_fun, -- Here unfolding is important because push_neg won't do it.
  push_neg,
  use 42,
  linarith,
end

-- 0052
example (f : ℝ → ℝ) : ¬ even_fun f ↔ ∃ x, f (-x) ≠ f x :=
begin
  unfold even_fun,
  push_neg,
  -- split,
  -- {
  --   intro h,
  --   push_neg at h,
  --   exact h,
  -- },
  -- {
  --   intro h,
  --   push_neg,
  --   exact h,
  -- },
end

def bounded_above (f : ℝ → ℝ) := ∃ M, ∀ x, f x ≤ M

example : ¬ bounded_above (λ x, x) :=
begin
  unfold bounded_above,
  push_neg,
  intro M,
  use M + 1,
  linarith,
end

-- Let's contrapose
-- 0053
example (x : ℝ) : (∀ ε > 0, x ≤ ε) → x ≤ 0 :=
begin
  contrapose,
  push_neg,
  intro h,
  use (x/2),
  -- ring, -- cannot
  split; linarith,
end

/-
The "contrapose, push_neg" combo is so common that we can abreviate it to
`contrapose!`

Let's use this trick, together with:
  eq_or_lt_of_le : a ≤ b → a = b ∨ a < b
-/

-- 0054
example (f : ℝ → ℝ) : (∀ x y, x < y → f x < f y) ↔ (∀ x y, (x ≤ y ↔ f x ≤ f y)) :=
begin
  split,
  {
    intros h1 x y,
    split,
    {
      intros hxy,
      rcases eq_or_lt_of_le hxy with ⟨heq,hle⟩,
      { rw h },
      { linarith [h1 x y h] },
    },
    {
      contrapose!,
      apply h1,
      -- intros hf,
      -- by_contradiction h2,
      -- push_neg at h2,
      -- linarith [h1 y x h2],
    },
  },
  {
    intros h1 x y,
    contrapose!,
    intro h2,
    rwa h1,
    -- by_contradiction h2,
    -- push_neg at h2,
    -- rcases h2 with ⟨x,y,hxy,hf⟩,
    -- have fact:y≤x,
    -- { exact (h1 y x).mpr hf },
    -- linarith,
  },
end


-- BOTH:
import data.real.basic

/- TEXT:
.. _more_on_order_and_divisibility:

More on Order and Divisibility
------------------------------

.. index:: min, max

The ``min`` function on the real numbers is uniquely characterized
by the following three facts:
TEXT. -/
-- BOTH:
section
variables a b c d : ℝ

-- QUOTE:
#check (min_le_left a b : min a b ≤ a)
#check (min_le_right a b : min a b ≤ b)
#check (le_min : c ≤ a → c ≤ b → c ≤ min a b)
-- QUOTE.

/- TEXT:
Can you guess the names of the theorems that characterize
``max`` in a similar way?

Notice that we have to apply ``min`` to a pair of arguments ``a`` and ``b``
by writing ``min a b`` rather than ``min (a, b)``.
Formally, ``min`` is a function of type ``ℝ → ℝ → ℝ``.
When we write a type like this with multiple arrows,
the convention is that the implicit parentheses associate
to the right, so the type is interpreted as ``ℝ → (ℝ → ℝ)``.
The net effect is that if ``a`` and ``b`` have type ``ℝ``
then ``min a`` has type ``ℝ → ℝ`` and
``min a b`` has type ``ℝ``, so ``min`` acts like a function
of two arguments, as we expect. Handling multiple
arguments in this way is known as *currying*,
after the logician Haskell Curry.

The order of operations in Lean can also take some getting used to.
Function application binds tighter than infix operations, so the
expression ``min a b + c`` is interpreted as ``(min a b) + c``.
With time, these conventions will become second nature.

Using the theorem ``le_antisymm``, we can show that two
real numbers are equal if each is less than or equal to the other.
Using this and the facts above,
we can show that ``min`` is commutative:
TEXT. -/
-- QUOTE:
example : min a b = min b a :=
begin
  apply le_antisymm,
  { show min a b ≤ min b a,
    apply le_min,
    { apply min_le_right },
    apply min_le_left },
  { show min b a ≤ min a b,
    apply le_min,
    { apply min_le_right },
    apply min_le_left }
end
-- QUOTE.

/- TEXT:
.. index:: show, tactics ; show

Here we have used curly brackets to separate proofs of
different goals.
Our usage is inconsistent:
at the outer level,
we use curly brackets and indentation for both goals,
whereas for the nested proofs,
we use curly brackets only until a single goal remains.
Both conventions are reasonable and useful.
We also use the ``show`` tactic to structure
the proof
and indicate what is being proved in each block.
The proof still works without the ``show`` commands,
but using them makes the proof easier to read and maintain.

It may bother you that the the proof is repetitive.
To foreshadow skills you will learn later on,
we note that one way to avoid the repetition
is to state a local lemma and then use it:
TEXT. -/
-- QUOTE:
example : min a b = min b a :=
begin
  have h : ∀ x y, min x y ≤ min y x,
  { intros x y,
    apply le_min,
    apply min_le_right,
    apply min_le_left },
  apply le_antisymm, apply h, apply h
end
-- QUOTE.

/- TEXT:
We will say more about the universal quantifier in
:numref:`implication_and_the_universal_quantifier`,
but suffice it to say here that the hypothesis
``h`` says that the desired inequality holds for
any ``x`` and ``y``,
and the ``intros`` tactic introduces an arbitrary
``x`` and ``y`` to establish the conclusion.
The first ``apply`` after ``le_antisymm`` implicitly
uses ``h a b``, whereas the second one uses ``h b a``.

.. index:: repeat, tactics ; repeat

Another solution is to use the ``repeat`` tactic,
which applies a tactic (or a block) as many times
as it can.
TEXT. -/
-- QUOTE:
example : min a b = min b a :=
begin
  apply le_antisymm,
  repeat {
    apply le_min,
    apply min_le_right,
    apply min_le_left }
end
-- QUOTE.

/- TEXT:
In any case,
whether or not you use these tricks,
we encourage you to prove the following:
TEXT. -/
-- QUOTE:
example : max a b = max b a :=
sorry

example : min (min a b) c = min a (min b c) :=
sorry
-- QUOTE.

-- SOLUTIONS:
example : max a b = max b a :=
begin
  apply le_antisymm,
  repeat {
    apply max_le,
    apply le_max_right,
    apply le_max_left }
end

example : min (min a b) c = min a (min b c) :=
begin
  apply le_antisymm,
  { apply le_min,
    { apply le_trans,
      apply min_le_left,
      apply min_le_left },
    apply le_min,
    { apply le_trans,
      apply min_le_left,
      apply min_le_right },
    apply min_le_right  },
  apply le_min,
  { apply le_min,
    { apply min_le_left },
    apply le_trans,
    apply min_le_right,
    apply min_le_left },
  apply le_trans,
  apply min_le_right,
  apply min_le_right
end

/- TEXT:
Of course, you are welcome to prove the associativity of ``max`` as well.

It is an interesting fact that ``min`` distributes over ``max``
the way that multiplication distributes over addition,
and vice-versa.
In other words, on the real numbers, we have the identity
``min a (max b c) ≤ max (min a b) (min a c)``
as well as the corresponding version with ``max`` and ``min``
switched.
But in the next section we will see that this does *not* follow
from the transitivity and reflexivity of ``≤`` and
the characterizing properties of ``min`` and ``max`` enumerated above.
We need to use the fact that ``≤`` on the real numbers is a *total order*,
which is to say,
it satisfies ``∀ x y, x ≤ y ∨ y ≤ x``.
Here the disjunction symbol, ``∨``, represents "or".
In the first case, we have ``min x y = x``,
and in the second case, we have ``min x y = y``.
We will learn how to reason by cases in :numref:`disjunction`,
but for now we will stick to examples that don't require the case split.

Here is one such example:
TEXT. -/
-- QUOTE:
lemma aux : min a b + c ≤ min (a + c) (b + c) :=
sorry

example : min a b + c = min (a + c) (b + c) :=
sorry
-- QUOTE.

-- SOLUTIONS:
lemma auxαα : min a b + c ≤ min (a + c) (b + c) :=
begin
  apply le_min,
  { apply add_le_add_right,
    apply min_le_left },
  apply add_le_add_right,
  apply min_le_right
end

example : min a b + c = min (a + c) (b + c) :=
begin
  apply le_antisymm,
  { apply aux },
  have h : min (a + c) (b + c) = min (a + c) (b + c) - c + c,
  { rw sub_add_cancel },
  rw h,
  apply add_le_add_right,
  rw sub_eq_add_neg,
  apply le_trans,
  apply aux,
  rw [add_neg_cancel_right, add_neg_cancel_right]
end

/- TEXT:
It is clear that ``aux`` provides one of the two inequalities
needed to prove the equality,
but applying it to suitable values yields the other direction
as well.
As a hint, you can use the theorem ``add_neg_cancel_right``
and the ``linarith`` tactic.

.. index:: absolute value

Lean's naming convention is made manifest
in the library's name for the triangle inequality:
TEXT. -/
-- QUOTE:
#check (abs_add : ∀ a b : ℝ, abs (a + b) ≤ abs a + abs b)
-- QUOTE.

/- TEXT:
Use it to prove the following variant:
TEXT. -/
-- QUOTE:
example : abs a - abs b ≤ abs (a - b) :=
sorry
-- QUOTE.

-- SOLUTIONS:
example : abs a - abs b ≤ abs (a - b) :=
calc
  abs a - abs b = abs (a - b + b) - abs b :
    by rw sub_add_cancel
  ... ≤ abs (a - b) + abs b - abs b :
    begin
      apply sub_le_sub_right,
      apply abs_add
    end
  ... ≤ abs (a - b) :
    by rw add_sub_cancel

-- alternatively
example : abs a - abs b ≤ abs (a - b) :=
begin
  have h := abs_add (a - b) b,
  rw sub_add_cancel at h,
  linarith
end

-- BOTH:
end

/- TEXT:
See if you can do this in three lines or less.
You can use the theorem ``sub_add_cancel``.

.. index:: divisibility

Another important relation that we will make use of
in the sections to come is the divisibility relation
on the natural numbers, ``x ∣ y``.
Be careful: the divisibility symbol is *not* the
ordinary bar on your keyboard.
Rather, it is a unicode character obtained by
typing ``\|`` in VS Code.
By convention, mathlib uses ``dvd``
to refer to it in theorem names.
TEXT. -/
-- BOTH:
section
variables w x y z : ℕ

-- QUOTE:
example (h₀ : x ∣ y) (h₁ : y ∣ z) : x ∣ z :=
dvd_trans h₀ h₁

example : x ∣ y * x * z :=
begin
  apply dvd_mul_of_dvd_left,
  apply dvd_mul_left
end

example : x ∣ x^2 :=
by apply dvd_mul_right
-- QUOTE.

/- TEXT:
In the last example, the exponent is a natural
number, and applying ``dvd_mul_right``
forces Lean to expand the definition of ``x^2`` to
``x^1 * x``.
See if you can guess the names of the theorems
you need to prove the following:
TEXT. -/
-- QUOTE:
example (h : x ∣ w) : x ∣ y * (x * z) + x^2 + w^2 :=
sorry
-- QUOTE.

-- SOLUTIONS:
example (h : x ∣ w) : x ∣ y * (x * z) + x^2 + w^2 :=
begin
  apply dvd_add,
  { apply dvd_add,
    { apply dvd_mul_of_dvd_right,
      apply dvd_mul_right },
    apply dvd_mul_right },
  rw pow_two,
  apply dvd_mul_of_dvd_right,
  exact h
end

-- BOTH:
end

/- TEXT:
.. index:: gcd, lcm

With respect to divisibility, the *greatest common divisor*,
``gcd``, and least common multiple, ``lcm``,
are analogous to ``min`` and ``max``.
Since every number divides ``0``,
``0`` is really the greatest element with respect to divisibility:
TEXT. -/
-- BOTH:
section
-- QUOTE:
variables m n : ℕ
open nat

#check (gcd_zero_right n : gcd n 0 = n)
#check (gcd_zero_left n  : gcd 0 n = n)
#check (lcm_zero_right n : lcm n 0 = 0)
#check (lcm_zero_left n  : lcm 0 n = 0)
-- QUOTE.

/- TEXT:
The functions ``gcd`` and ``lcm`` for natural numbers are in the
``nat`` namespace,
which means that the full identifiers are ``nat.gcd`` and ``nat.lcm``.
Similarly, the names of the theorems listed are prefixed by ``nat``.
The command ``open nat`` opens the namespace,
allowing us to use the shorter names.

See if you can guess the names of the theorems you will need to
prove the following:
TEXT. -/
-- QUOTE:
example : gcd m n = gcd n m :=
sorry
-- QUOTE.

-- SOLUTIONS:
example : gcd m n = gcd n m :=
begin
  apply dvd_antisymm,
  repeat {
    apply dvd_gcd,
    apply gcd_dvd_right,
    apply gcd_dvd_left }
end

-- BOTH:
end
/- TEXT:
Hint: you can use ``dvd_antisymm``.
TEXT. -/


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

import tactic -- imports all the Lean tactics

/-!

# Group theory in Lean

Lean has groups. But let's make our own anyway. In this sheet we will
learn about the `class` command, which is one of the ways to make
a theory of groups in Lean (and indeed the way it's done in `mathlib`)

We will also learn about `simp`, a tactic which does long series of
rewrites automatically. We'll even learn how to train it to solve a
certain kind of problem.

## Definition of a group

`group G` is already defined in `mathlib`, so let's define a new
type `mygroup G`. Here `G : Type` s a type, and `mygroup G` is the type of
group structures on `G`.

-/

/-- `mygroup G` is the type of group structures on the type `G`. -/
class mygroup (G : Type)
  extends has_one G, has_mul G, has_inv G : Type :=
(mul_assoc : ∀ a b c : G, (a * b) * c = a * (b * c))
(one_mul : ∀ a : G, 1 * a = a)
(mul_one : ∀ a : G, a * 1 = a)
(inv_mul_self : ∀ a : G, a⁻¹ * a = 1)
(mul_inv_self : ∀ a : G, a * a⁻¹ = 1)

/-

Right now the axioms are called `mygroup.mul_assoc`, `mygroup.one_mul` etc.
Furthermore we want to build some lemmas called things like `mygroup.one_inv`
and stuff. The fix? Let's move into the `mygroup` namespace, where they're
just caled `mul_assoc`, `one_inv` etc.

-/

namespace mygroup

-- tag all the axioms for groups with the `@[simp]` attribute.
-- Note that we can drop the `mygroup` prefix now as we're in the namespace.

attribute [simp] mul_assoc one_mul mul_one inv_mul_self mul_inv_self

-- Throughout our work in this namespace, let `G` be a group and let
-- `a`, `b` and `c` be elements of `G`.
variables {G : Type} [mygroup G] (a b c : G)

/-

Five of the next seven lemmas are tagged with `@[simp]` as well.
See if you can prove all seven using (for the most part) the `rw` tactic.

-/

@[simp] lemma inv_mul_cancel_left : a⁻¹ * (a * b) = b :=
begin
  rw <-mul_assoc,rw inv_mul_self,rw one_mul,
end

@[simp] lemma mul_inv_cancel_left : a * (a⁻¹*b) = b :=
begin
  rw <-mul_assoc,rw mul_inv_self,rw one_mul,
end

lemma left_inv_eq_right_inv {a b c : G} (h1 : b * a = 1) (h2 : a * c = 1) :
  b = c :=
begin
  -- hint for this one : establish the auxiliary fact
  -- that `b * (a * c) = (b * a) * c` with the `have` tactic.
  have q: b*(a*c)=(b*a)*c,{rw mul_assoc,},
  simpa [h1, h2] using q,
end

lemma mul_eq_one_iff_eq_inv : a * b = 1 ↔ a⁻¹ = b :=
begin
  split;
  intro q,
  {
    have w: a*a⁻¹=a*b,
    calc
      a*a⁻¹=1:by rw mul_inv_self
...   =a*b:by rw q,
calc
  a⁻¹=1*a⁻¹: by rw one_mul
... =a⁻¹*a*a⁻¹:by rw <- inv_mul_self
... =a⁻¹*(a*a⁻¹): by rw <- mul_assoc
... =a⁻¹*(a*b):by rw w
... =a⁻¹*a*b: by rw <- mul_assoc
... =1*b:by rw inv_mul_self
...=b:by rw one_mul,
  },
  {cases q, rw <- mul_inv_self,}
end

@[simp] lemma one_inv : (1 : G)⁻¹ = 1 :=
begin
    have q: 1 * (1:G)⁻¹ = 1, rw mul_eq_one_iff_eq_inv, simp [<- mul_eq_one_iff_eq_inv],
end

@[simp] lemma inv_inv : (a⁻¹)⁻¹ = a :=
begin
  have w: a⁻¹ * (a⁻¹)⁻¹ =1, rw mul_inv_self,simp [ <- mul_eq_one_iff_eq_inv],
end

@[simp] lemma mul_inv_rev : (a * b)⁻¹ = b⁻¹ * a⁻¹ :=
begin
  simp [ <- mul_eq_one_iff_eq_inv],
end

/-

Remember the `ring` tactic which didn't look at hypotheses but which could
prove hypothesis-free identities in commutative rings? A theorem of Knuth
and Bendix says that we have just trained Lean's simplifier to prove
arbitrary true hypothesis-free identities in groups; those ten lemmas
we tagged with `@[simp]` are all you need. We've made a Noetherian
confluent rewrite system for group theory!

-/
-- example of Knuth-Bendix theorem
example (G : Type) [mygroup G] (a b : G) :
  (b⁻¹ * a⁻¹)⁻¹ * 1⁻¹⁻¹ * b⁻¹ * (a⁻¹ * a⁻¹⁻¹⁻¹) * a = 1 := by simp

-- bonus puzzle : if g^2=1 for all g in G, then G is abelian
example (G : Type) [mygroup G] (h : ∀ g : G, g * g = 1) :
  ∀ g h : G, g * h = h * g :=
begin
  simp [mul_eq_one_iff_eq_inv] at h,
  intros a b,
  rw [<- h (a*b)],
  rw mul_inv_rev,
  simp [h],
end

end mygroup


namespace relation_2102_ns
section relation_2102_sec

/-
Let β be an arbitrary type, and r be
a binary relation on elements of type,
β.
-/
variable {β : Type}
variable (r : β → β → Prop)

/-
Applying r to two values, x and y, of
type, β, yields a proposition. You can
thus think of r as a property of pairs
of values. If the proposition is true,
then the ordered pair, (x, y), is in
the relation, otherwise it's not.
-/
variables x y : β
#check r x y

/-
In the preceding example, we wrote
the relation name before the arguments.
It's often more natural to write in in
between the arguments. We call such a
notation an infix notation. Lean gives
us a way to extend the syntax of Lean
by adding infix operators to reduce to
known prefix operators. Here we define
two infix versions of r, one called R,
and one called ≺. The number, 50, in the
following lines defines the precedence
of this operator, i.e., how tightly it
binds when appearing in an expression
with other operators.
-/

local infix `R`:50 := r
local infix `≺`:50 := r

/-
Now we can use either x R y or x ≺ y
to mean the same thing as r x y. We'll
use the x R y style in the file. Lean's
libraries happen to use x ≺ y, but they
mean the same thing, as we just said.
-/
#check x R y
#check x ≺ y

-- EXAMPLE: THE EQUALITY RELATION

/-
Let's look at equality as a binary
relation. The first thing to know is
that an expression, such as 1 = 1, is
a convenient way of writing "eq 1 1".
The = notation is defined as an infix
version of eq in just the same way
that we defined R and ≺ to be infix
versions of r. Check to see that the
following expressions mean the same
thing.
-/
#reduce 1 = 1
#reduce eq 1 1

/-
Let's look at the actual definition
of eq in Lean's libraries.
-/
#print eq
/-
Just look at the first line, skipping
the constructor for now. What is says
is that for any Type, α, eq is a binary
relation on values of that type. The "Pi"
symbol, Π,  is like ∀. ∀ is used when
working with propositions, but equality
is defined for any type, so we use Π
instead. You should read the definition as
saying that if you give eq any type, you
get back a binary relation on values of
that type: namely one for which the only
proofs that can be constructed are given
by eq.refl, which takes just one argument,
α, and returns a proof of α = α.

We thus expect the type of "eq ℕ" to be
a binary relation on ℕ values. We preface eq in the following examples with @, which
tells Lean not to use implicit typing for the type argument, α, to eq. We really do have to give a type argument explicitly.
-/

#check @eq nat
#check @eq β

/-
We now see that for any type, α, such as
ℕ, or, in this file, β, eq α is a binary relation on the set of values of type, α. It is thus of type, α → α → Prop. That is
the signal that you're looking at a binary
relation in Lean.
-/

-- REFLEXIVE

/-
Given that we now see that eq β is a binary relation, let's assert and prove
that it is a reflexive relation.

EXERCISE: Think about and discuss
what it means for equality to be a
reflexive relation. Do you believe
it is reflexive?
-/

lemma eq_refl : reflexive (@eq β) :=
begin
unfold reflexive,
intro, apply rfl,
end

-- SYMMETRIC

/-
A relation is symmetric if, whenever
two values, and and y, are related,
y and x are related, in the opposite
order.
-/
def symmetric := ∀ ⦃x y⦄, x R y → y R x

/-
Let's prove that equality is symmetric.
Of course we're cheating a little here
by using a rule already in the libraries
that eq is symmetric: the rule, eq.symm.
-/
lemma eq_symm : symmetric (@eq β) :=
begin
unfold symmetric,
intros,
apply eq.symm,
assumption,
end

/-
EXERCISE: Is the real-world "likes"
relation, as in "has-a-crush-on", a  symmetric relation? How about Facebook's
"likes" relation?
-/

-- TRANSITIVE

/-
A binary relation on values of some
type, β, is said to be transitive if
whenever x and y are related, and y
and z are related, then x and z are
related.
-/
def transitive :=
    ∀ ⦃x y z⦄, x R y → y R z → x R z

/-
EXERCISE: Is equality transitive?
-/

lemma eq_trans : transitive (@eq β) :=
begin
unfold transitive,
intros x y z xy yz,
apply eq.trans xy yz,
end

/-
An equivalence relation is a binary
relation that is reflexive, symmetric,
and transitive.
-/
def equivalence := reflexive r ∧ symmetric r ∧ transitive r

/-
Is eq an equivalence relation? Sure.
-/

theorem eq_equiv : equivalence (@eq β) :=
begin
unfold equivalence,
split, exact eq_refl,
split, exact eq_symm, exact eq_trans
end

/-
Let's discuss another equivalence
relation. Consider the set ℕ / 12,
the natural numbers modulo 12.

What is 2 mod 12? What is 14 mod 12?
What is 26 mod 12. All these numbers,
and indeed any natural number of the
form, 2 + k * 12 are congruent to 2
mod 12.
-/

#reduce 2 % 12
#reduce 14 % 12
#reduce 26 % 12

/-
This entire set of numbers mod 12
forms an equivalence class. Let's
formalize and prove this proposition.
-/

/-
First, the proposition itself.
-/
def mod_12_equiv : ℕ → ℕ → Prop :=
    λ x y, x % 12 = y % 12

/-
Now a simple test case as a sanity
check.
-/
example : mod_12_equiv 2 14 :=
begin
unfold mod_12_equiv, apply rfl,
end

/-
Let's prove that the whole infinite
set of values of the form n + k * 12
is congruent.
-/
example :
    ∀ k : ℕ, ∀ n : ℕ,
    mod_12_equiv n (n + k * 12) :=
begin
intros,
unfold mod_12_equiv,
simp,
end

def mod_12_equiv'': set (ℕ × ℕ) :=
  { p | p.1 % 12 = p.2 % 12 }

/-
The two ways of defining a relation
appear to be different in form (and
in type structure, in particular) but
conceptually equivalent. Are they
equivalent? Let's assert that they
are and see if we can prove it.
-/
example : ∀ n m, mod_12_equiv n m ↔ (n,m) ∈ mod_12_equiv'' := _

/-
EXERCISE: Do it!
-/

/-
Let's define what it means in general
for two numbers to be congruent mod 12.
We now see that congruence mod 12 is a
binary relation.
-/

def cong_12: ℕ → ℕ → Prop :=
    λ n m : ℕ, n % 12 = m % 12

example : reflexive cong_12 :=
begin
unfold cong_12,
unfold reflexive, -- EX: why just x here?
intro, apply rfl,
end

example : symmetric cong_12 :=
begin
unfold symmetric,
intros x y,
unfold cong_12,
intro h,
-- New tactic: rewrite using an equality
rw h,
end

example : transitive cong_12 :=
begin
unfold transitive,
intros x y z xy yz,
unfold cong_12 at xy,
unfold cong_12 at yz,
unfold cong_12,
rw xy, assumption,
end

/-
In the rest of this file, we transition
from x R y notation to x ≺ y. You will
find both used in mathematical writing.
Be careful not to read x ≺ y as saying
that x is less than y. The ≺ symbol in
this context can refer to any relation
whatsoever.
-/

/-
We now define additional properties of
binary relations on a set. For each of
them, come up with one or more familiar
relations having these, and not having
these, properties.
-/

def total := ∀ x y, x ≺ y ∨ y ≺ x

def irreflexive := ∀ x, ¬ x ≺ x

def anti_symmetric := ∀ ⦃x y⦄, x ≺ y → y ≺ x → x = y

variable {α : Type}

def empty_relation := λ a₁ a₂ : α, false

def subrelation (q r : β → β → Prop) := ∀ ⦃x y⦄, q x y → r x y

/-
Transitive closure. We're not ready for the
following formal definition of the transitive
closure of a relation, as we haven't covered
inductive definitions, but we can introduce
the idea informally now.
-/
inductive tc {α : Type} (r : α → α → Prop) : α → α → Prop
| base  : ∀ a b, r a b → tc a b
| trans : ∀ a b c, tc a b → tc b c → tc a c

/-
The following material is optional and will not be
tested.
-/

def inv_image (f : α → β) : α → α → Prop :=
λ a₁ a₂, f a₁ ≺ f a₂

lemma inv_image.trans (f : α → β) (h : transitive r) : transitive (inv_image r f) :=
λ (a₁ a₂ a₃ : α) (h₁ : inv_image r f a₁ a₂) (h₂ : inv_image r f a₂ a₃), h h₁ h₂

lemma inv_image.irreflexive (f : α → β) (h : irreflexive r) : irreflexive (inv_image r f) :=
λ (a : α) (h₁ : inv_image r f a a), h (f a) h₁

end relation_2102_sec

end relation_2102_ns


namespace mynat

/-
Our own implementation of nat
-/

inductive mynat : Type
| zero : mynat
| succ : mynat → mynat

/-
Convenient names for first few terms -/

def zero := mynat.zero
def one := mynat.succ zero
def two := mynat.succ one
def three :=
    mynat.succ
        (mynat.succ
            (mynat.succ
                zero))

#reduce one
#reduce two
#reduce three

/-
Unary functions
-/

-- identity function on mynat
def id_mynat (n: mynat) := n

-- constant zero function
def zero_mynat (n: mynat) := zero

-- predecessor function
def pred (n : mynat) :=
    match n with
    | mynat.zero := zero
    | mynat.succ n' := n'
    end

#reduce pred three
#reduce pred zero

def pred' (n: mynat) :=
  match n with
  | mynat.succ n' := n'
  | _ := zero
  end

example: pred = pred' := rfl

/-
There are two new and important
concepts here. The first is a new
kind of matching. The second is
that we define the predecessor
of zero to be zero. That's a bit
odd.

Look at the matching. The first
pattern is familar. The second,
though, it interesting. If we get
here, we know n isn't zero, so it
must by the successor of the next
smaller mynat. Here we give that
next smaller value the name n'.
And of course that's the number
we want to return as precessor!
-/

/-
Now let's see binary operations
and recursive functions. To
define a recursive function we
have a new syntax/
-/

def add_mynat: mynat → mynat → mynat
| mynat.zero m := m
| (mynat.succ n') m :=
    mynat.succ (add_mynat n' m)

#reduce add_mynat three two

/-
Syntax notes: use explicit function
type syntax. Omit any :=. Define how
the function works by cases. Each case
defines what the function does for the
specific kinds of arguments used in the
case. Omit commas between arguments
All cases may be covered
-/

-- It works!
#reduce add_mynat one two
#reduce add_mynat three three

/-
EXERCISES:

(1) We just implemented addition as
the recursive (iterated) application
of the successor function. Now you are
to implement multiplication as iterated
addition.

(2) Implement exponentiation as iterated
multiplication.

(3) Take this pattern one step further.
What function did you implement? How
would you write it in regular math
notation?
-/

def mul_mynat: mynat → mynat → mynat
| mynat.zero m := zero
| (mynat.succ n') m := add_mynat m (mul_mynat n' m)

#reduce mul_mynat three three

def exp_mynat : mynat → mynat → mynat
| n mynat.zero := one
| n (mynat.succ m') := mul_mynat n (exp_mynat n m')

#reduce exp_mynat three three

#reduce exp_mynat two three

--#reduce mul_mynat three two
--#reduce mul_mynat three three

/-
We can easily prove that for all m : ℕ,
add_mynat 0 m = m, because the definition
of add_mynat has a matching pattern (the
first one), which explains exactly how to
reduce a term with first argument zero.
-/

theorem zero_left_id:
    ∀ m : mynat,
        add_mynat mynat.zero m = m
:=
begin
intro m,
--apply rfl,
simp [add_mynat],
end

/-
We just asked Lean to simplify the
goal using the "simplication rules"
specified by the two cases in the
definition of add_mynat. The result
is m = m, which Lean takes care of
with an automated application of rfl.
-/

/-
Unfortunately, we don't have such
a simplification rule when the zero
is on the right! For that we need a
whole new proof strategy: proof by
induction.
-/

theorem zero_right_id :
    ∀ m : mynat,
        add_mynat m mynat.zero = m
:=
begin
    intro m,

    /-
    cases m,
    apply rfl,
    -/

    induction m with m' h,

      -- base case
      apply rfl,
      --simp [add_mynat],

      -- inductive case
      simp [add_mynat],
      assumption,
end

lemma add_n_succ_m :
∀ n m : mynat,
    add_mynat n (mynat.succ m) =
    mynat.succ (add_mynat n m) :=
begin
intros n m,
induction n with n' h,

apply rfl,

simp [add_mynat],
assumption,
end

/-
Property verification: our addition
operation is commutative!
-/
example :
    ∀ m n : mynat,
    add_mynat m n = add_mynat n m :=
begin
    intros m n,

    -- by induction on m
    induction m with m' h,

    --base case: m = zero
    simp [add_mynat],
    rw zero_right_id,

    -- inductive case:
    -- if true for m then true for succ m
    simp [add_mynat],
    rw add_n_succ_m,

    -- rewrite using induction hypothesis!
    rw h,
end

/-
Proof by induction is proof by
case analysis on the *constructors*
for values of a given type, with the
huge added benefit of an induction
hypothesis.

So, as an example, if we show that
some predicate involving a natural
number, n, is true no matter what
*constructor* was used to "build"
n, then we've' shown the predicate to
be true for *all* (∀) values of n,
because there are no n other than
those that can be built by the given
constructors. Proving a proposition
is true for all constructors is thus
tantamount to showing that it is
true for all values, even if there
are infinitely many.

Let's think about the two
constructors for values of type
mynat. First, there is the zero
constructor.  That's the "base
case". Second, there's the succ
constructor. From a value, n,
it constructs a value, succ n.

Now if we prove the following two
cases, we're done:

(1) the predicate in question is
true when n is zero

(2) if the predicate is true for
any n, then it is true for succ n.

The reason we're done is that
there are no other possibilities
for n.

The set of values of an inductively
defined type is defined to be exactly
the set of values that can be built
by using the available constructors
any *finite* number of times, and
that there are no other values of
the given type.
-/

/-
EXERCISE: try this proof using
cases instead of induction. Cases
also does case analysis on the
constructors. Why does simple case
analysis fail?
-/

/-
EXERCISES: To Come Shortly
-/

end mynat


import libs.tuto_lib
import data.int.parity
/-
Negations, proof by contradiction and contraposition.

This file introduces the logical rules and tactics related to negation:
exfalso, by_contradiction, contrapose, by_cases and push_neg.

There is a special statement denoted by `false` which, by definition,
has no proof.

So `false` implies everything. Indeed `false → P` means any proof of
`false` could be turned into a proof of P.
This fact is known by its latin name
"ex falso quod libet" (from false follows whatever you want).
Hence Lean's tactic to invoke this is called `exfalso`.
-/

example : false → 0 = 1 :=
begin
  intro h,
  exfalso,
  exact h,
end

/-
The preceding example suggests that this definition of `false` isn't very useful.
But actually it allows us to define the negation of a statement P as
"P implies false" that we can read as "if P were true, we would get
a contradiction". Lean denotes this by `¬ P`.

One can prove that (¬ P) ↔ (P ↔ false). But in practice we directly
use the definition of `¬ P`.
-/

example {x : ℝ} : ¬ x < x :=
begin
  intro hyp,
  rw lt_iff_le_and_ne at hyp,
  cases hyp with hyp_inf hyp_non,
  clear hyp_inf, -- we won't use that one, so let's discard it
  change x = x → false at hyp_non, -- Lean doesn't need this psychological line
  apply hyp_non,
  refl,
end

open int

-- 0045
example (n : ℤ) (h_even : even n) (h_not_even : ¬ even n) : 0 = 1 :=
begin
  exfalso,
  exact (h_not_even h_even), -- parens not needed!
end

-- 0046
example (P Q : Prop) (h₁ : P ∨ Q) (h₂ : ¬ (P ∧ Q)) : ¬ P ↔ Q :=
begin
  split,
  { intro nP,
    cases h₁,
    exfalso,
    exact nP h₁,
    assumption },
  intro q,
  intro p,
  exact h₂ ⟨p,q⟩, -- constructs the conjunction
end

/-
The definition of negation easily implies that, for every statement P,
P → ¬ ¬ P

The excluded middle axiom, which asserts P ∨ ¬ P allows us to
prove the converse implication.

Together those two implications form the principle of double negation elimination.
  not_not {P : Prop} : (¬ ¬ P) ↔ P

The implication `¬ ¬ P → P` is the basis for proofs by contradiction:
in order to prove P, it suffices to prove ¬¬ P, ie `¬ P → false`.

Of course there is no need to keep explaining all this. The tactic
`by_contradiction Hyp` will transform any goal P into `false` and
add Hyp : ¬ P to the local context.

Let's return to a proof from the 5th file: uniqueness of limits for a sequence.
This cannot be proved without using some version of the excluded middle
axiom. We used it secretely in

eq_of_abs_sub_le_all (x y : ℝ) : (∀ ε > 0, |x - y| ≤ ε) → x = y

(we'll prove a variation on this lemma below).

In the proof below, we also take the opportunity to introduce the `let` tactic
which creates a local definition. If needed, it can be unfolded using `dsimp` which
takes a list of definitions to unfold. For instance after using `let N₀ := max N N'`,
you could write `dsimp [N₀] at h` to replace `N₀` by its definition in some
local assumption `h`.
-/
example (u : ℕ → ℝ) (l l' : ℝ) : seq_limit u l → seq_limit u l' → l = l' :=
begin
  intros hl hl',
  by_contradiction H,
  change l ≠ l' at H, -- Lean does not need this line
  have ineg : |l-l'| > 0,
    exact abs_pos.mpr (sub_ne_zero_of_ne H), -- details about that line are not important
  cases hl ( |l-l'|/4 ) (by linarith) with N hN,
  cases hl' ( |l-l'|/4 ) (by linarith) with N' hN',
  let N₀ := max N N',
  specialize hN N₀ (le_max_left _ _),
  specialize hN' N₀ (le_max_right _ _),
  have clef : |l-l'| < |l-l'|,
    calc
    |l - l'| = |(l-u N₀) + (u N₀ -l')|   : by ring_nf
         ... ≤ |l - u N₀| + |u N₀ - l'|  : by apply abs_add
         ... = |u N₀ - l| + |u N₀ - l'|  : by rw abs_sub_comm
         ... < |l-l'|                    : by linarith,
  linarith, -- linarith can also find simple numerical contradictions
end

/-
Another incarnation of the excluded middle axiom is the principle of
contraposition: in order to prove P ⇒ Q, it suffices to prove
non Q ⇒ non P.
-/

-- Using a proof by contradiction, let's prove the contraposition principle
-- 0047
example (P Q : Prop) (h : ¬ Q → ¬ P) : P → Q :=
begin
  intro p,
  by_contradiction nq,
  exact h nq p,
end

/-
Again Lean doesn't need this principle explained to it. We can use the
`contrapose` tactic.
-/

example (P Q : Prop) (h : ¬ Q → ¬ P) : P → Q :=
begin
  contrapose,
  exact h,
end

/-
In the next exercise, we'll use
 odd n : ∃ k, n = 2*k + 1
 int.odd_iff_not_even {n : ℤ} : odd n ↔ ¬ even n
-/
-- 0048
example (n : ℤ) : even (n^2) ↔ even n :=
begin
  split,
  { contrapose,
    intro odd_n,
    rewrite ← odd_iff_not_even at *,
    rcases odd_n with ⟨k,rfl⟩,
    use 2*k^2 + 2*k,
    ring, },
  -- contrapose,
  -- intro odd_n2,
  -- rewrite ← odd_iff_not_even at *,
  -- cases odd_n2 with k hk,
  intro even_n,
  by_contradiction odd_n2,
  rewrite ← odd_iff_not_even at odd_n2,
  rcases even_n with ⟨k,rfl⟩,
  cases odd_n2 with m hm,
  have : 2 * (2* k^2) = 2 * m + 1 := by linarith, -- `ring` won't solve this
  have : (0 : ℤ) = 1,
  calc
    0   = 2 * (2 * k^2) % 2 : by { rw mul_mod_right, } -- `norm_num` works here
    ... = (2 * m +1) % 2 : by congr'
    ... = 2 * m % 2 + 1 : by { rw ← mod_add_mod, norm_num} -- distributing `%`
    ... = 1 : by { rw mul_mod_right, ring}, -- `norm_num` works here
  by linarith,
end

/-
As a last step on our law of the excluded middle tour, let's notice that, especially
in pure logic exercises, it can sometimes be useful to use the
excluded middle axiom in its original form:
  classical.em : ∀ P, P ∨ ¬ P

Instead of applying this lemma and then using the `cases` tactic, we
have the shortcut
 by_cases h : P,

combining both steps to create two proof branches: one assuming
h : P, and the other assuming h : ¬ P

For instance, let's prove a reformulation of this implication relation,
which is sometimes used as a definition in other logical foundations,
especially those based on truth tables (hence very strongly using
excluded middle from the very beginning).
-/

variables (P Q : Prop)

example : (P → Q) ↔ (¬ P ∨ Q) :=
begin
  split,
  { intro h,
    by_cases hP : P,
    { right,
      exact h hP },
    { left,
      exact hP } },
  { intros h hP,
    cases h with hnP hQ,
    { exfalso,
      exact hnP hP },
    { exact hQ } },
end

-- 0049
example : ¬ (P ∧ Q) ↔ ¬ P ∨ ¬ Q :=
begin
  sorry
end

/-
It is crucial to understand negation of quantifiers.
Let's do it by hand for a little while.
In the first exercise, only the definition of negation is needed.
-/

-- 0050
example (n : ℤ) : ¬ (∃ k, n = 2*k) ↔ ∀ k, n ≠ 2*k :=
begin
  sorry
end

/-
Contrary to negation of the existential quantifier, negation of the
universal quantifier requires excluded middle for the first implication.
In order to prove this, we can use either
* a double proof by contradiction
* a contraposition, not_not : (¬ ¬ P) ↔ P) and a proof by contradiction.
-/

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

-- 0051
example (f : ℝ → ℝ) : ¬ even_fun f ↔ ∃ x, f (-x) ≠ f x :=
begin
  sorry
end

/-
Of course we can't keep repeating the above proofs, especially the second one.
So we use the `push_neg` tactic.
-/

example : ¬ even_fun (λ x, 2*x) :=
begin
  unfold even_fun, -- Here unfolding is important because push_neg won't do it.
  push_neg,
  use 42,
  linarith,
end

-- 0052
example (f : ℝ → ℝ) : ¬ even_fun f ↔ ∃ x, f (-x) ≠ f x :=
begin
  sorry
end

def bounded_above (f : ℝ → ℝ) := ∃ M, ∀ x, f x ≤ M

example : ¬ bounded_above (λ x, x) :=
begin
  unfold bounded_above,
  push_neg,
  intro M,
  use M + 1,
  linarith,
end

-- Let's contrapose
-- 0053
example (x : ℝ) : (∀ ε > 0, x ≤ ε) → x ≤ 0 :=
begin
  sorry
end

/-
The "contrapose, push_neg" combo is so common that we can abreviate it to
`contrapose!`

Let's use this trick, together with:
  eq_or_lt_of_le : a ≤ b → a = b ∨ a < b
-/

-- 0054
example (f : ℝ → ℝ) : (∀ x y, x < y → f x < f y) ↔ (∀ x y, (x ≤ y ↔ f x ≤ f y)) :=
begin
  sorry
end


/-
Copyright (c) 2021 Alain Verberkmoes. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Alain Verberkmoes
-/
import data.int.dvd.basic

/-!
# IMO 2011 Q5

Let `f` be a function from the set of integers to the set
of positive integers.  Suppose that, for any two integers
`m` and `n`, the difference `f(m) - f(n)` is divisible by
`f(m - n)`.  Prove that, for all integers `m` and `n` with
`f(m) ≤ f(n)`, the number `f(n)` is divisible by `f(m)`.
-/

open int

theorem imo2011_q5 (f : ℤ → ℤ) (hpos : ∀ n : ℤ, 0 < f n)
  (hdvd : ∀ m n : ℤ, f (m - n) ∣ f m - f n) :
∀ m n : ℤ, f m ≤ f n → f m ∣ f n :=
begin
  intros m n h_fm_le_fn,
  cases lt_or_eq_of_le h_fm_le_fn with h_fm_lt_fn h_fm_eq_fn,
  { -- m < n
    let d := f m - f (m - n),
    have h_fn_dvd_d : f n ∣ d,
    { rw ←sub_sub_self m n,
      exact hdvd m (m - n) },
    have h_d_lt_fn : d < f n,
    { calc d < f m : sub_lt_self _ (hpos (m - n))
        ... < f n : h_fm_lt_fn },
    have h_neg_d_lt_fn : -d < f n,
    { calc -d = f (m - n) - f m : neg_sub _ _
        ... < f (m - n) : sub_lt_self _ (hpos m)
        ... ≤ f n - f m : le_of_dvd (sub_pos.mpr h_fm_lt_fn) _
        ... < f n : sub_lt_self _ (hpos m),
      -- ⊢ f (m - n) ∣ f n - f m
      rw [←dvd_neg, neg_sub], exact hdvd m n },
    have h_d_eq_zero : d = 0,
    { obtain (hd | hd | hd) : d > 0 ∨ d = 0 ∨ d < 0 := trichotomous d 0,
      { -- d > 0
        have h₁ : f n ≤ d, from le_of_dvd hd h_fn_dvd_d,
        have h₂ : ¬ f n ≤ d, from not_le.mpr h_d_lt_fn,
        contradiction },
      { -- d = 0
        exact hd },
      { -- d < 0
        have h₁ : f n ≤ -d, from le_of_dvd (neg_pos.mpr hd) ((dvd_neg _ _).mpr h_fn_dvd_d),
        have h₂ : ¬ f n ≤ -d, from not_le.mpr h_neg_d_lt_fn,
        contradiction } },
    have h₁ : f m = f (m - n), from sub_eq_zero.mp h_d_eq_zero,
    have h₂ : f (m - n) ∣ f m - f n, from hdvd m n,
    rw ←h₁ at h₂,
    exact (dvd_iff_dvd_of_dvd_sub h₂).mp dvd_rfl },
  { -- m = n
    rw h_fm_eq_fn }
end


/-
Copyright (c) 2021 Alain Verberkmoes. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Alain Verberkmoes
-/

import data.int.basic

/-!
# IMO 2011 Q5

Let `f` be a function from the set of integers to the set
of positive integers.  Suppose that, for any two integers
`m` and `n`, the difference `f(m) − f(n)` is divisible by
`f(m − n)`.  Prove that, for all integers `m` and `n` with
`f(m) ≤ f(n)`, the number `f(n)` is divisible by `f(m)`.
-/

open int

theorem imo2011_q5 (f : ℤ → ℤ) (hpos : ∀ n : ℤ, 0 < f n)
  (hdvd : ∀ m n : ℤ, f (m - n) ∣ f m - f n) :
∀ m n : ℤ, f m ≤ f n → f m ∣ f n :=
begin
  intros m n h_fm_le_fn,
  cases lt_or_eq_of_le h_fm_le_fn with h_fm_lt_fn h_fm_eq_fn,
  { -- m < n
    let d := f m - f (m - n),
    have h_fn_dvd_d : f n ∣ d,
    { rw ←sub_sub_self m n,
      exact hdvd m (m - n) },
    have h_d_lt_fn : d < f n,
    { calc d < f m : sub_lt_self _ (hpos (m - n))
        ... < f n : h_fm_lt_fn },
    have h_neg_d_lt_fn : -d < f n,
    { calc -d = f (m - n) - f m : neg_sub _ _
        ... < f (m - n) : sub_lt_self _ (hpos m)
        ... ≤ f n - f m : le_of_dvd (sub_pos.mpr h_fm_lt_fn) _
        ... < f n : sub_lt_self _ (hpos m),
      -- ⊢ f (m - n) ∣ f n - f m
      rw [←dvd_neg, neg_sub], exact hdvd m n },
    have h_d_eq_zero : d = 0,
    { obtain (hd | hd | hd) : d > 0 ∨ d = 0 ∨ d < 0 := trichotomous d 0,
      { -- d > 0
        have h₁ : f n ≤ d, from le_of_dvd hd h_fn_dvd_d,
        have h₂ : ¬ f n ≤ d, from not_le.mpr h_d_lt_fn,
        contradiction },
      { -- d = 0
        exact hd },
      { -- d < 0
        have h₁ : f n ≤ -d, from le_of_dvd (neg_pos.mpr hd) ((dvd_neg _ _).mpr h_fn_dvd_d),
        have h₂ : ¬ f n ≤ -d, from not_le.mpr h_neg_d_lt_fn,
        contradiction } },
    have h₁ : f m = f (m - n), from sub_eq_zero.mp h_d_eq_zero,
    have h₂ : f (m - n) ∣ f m - f n, from hdvd m n,
    rw ←h₁ at h₂,
    exact (dvd_iff_dvd_of_dvd_sub h₂).mp (dvd_refl _) },
  { -- m = n
    rw h_fm_eq_fn }
end


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

/-!

# Challenge sheet

This is a harder group theory question.

It turns out that two of the axioms in our definition of a group
are not needed; they can be deduced from the others. Let's define
a "weak group" class, where we only have three of the group axioms.
The question is: can you prove that a weak group is a group, by
proving the other axioms?

-/

-- removing `mul_one` and `mul_inv_self` from the five standard axioms
-- for a group.
class weak_group (G : Type) extends has_one G, has_mul G, has_inv G : Type :=
(mul_assoc : ∀ a b c : G, (a * b) * c = a * (b * c))
(one_mul : ∀ a : G, 1 * a = a)
(inv_mul_self : ∀ a : G, a⁻¹ * a = 1)

namespace weak_group

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

/-

The challenge is to prove that G is a group, which we can interpret as
proving the missing axioms `mul_one` and `mul_inv_self`. Note that you
can't use the `group` tactic any more because `G` isn't a group yet:
this is what you're trying to prove!

One way of doing it: try proving

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

and then

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

first.

-/

lemma mul_left_cancel (h : a * b = a * c) : b = c :=
begin
  sorry
end

lemma mul_one (a : G) : a * 1 = a :=
begin
  sorry,
end

lemma mul_inv_self (a : G) : a * a⁻¹ = 1 :=
begin
  sorry,
end

end weak_group

/-

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

-/

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

-- `bool` is a type with two terms, `bool.tt` and `bool.ff`. See if you can make it into
-- a bad group which isn't a group!
instance : has_one bool := ⟨sorry⟩
instance : has_mul bool := ⟨sorry⟩
instance : has_inv bool := ⟨sorry⟩

instance : bad_group bool :=
{ mul_assoc := sorry, -- `dec_trivial`, might be able to do this
  mul_one := sorry, -- dec_trivial,
  inv_mul_self := sorry, --dec_trivial,
}

example : ¬ (∀ a : bool, 1 * a = a) :=
begin
  sorry, --dec_trivial,
end


import .src_05_if_and_only_if
import .src_07_existential_quantification

namespace mth1001

section universal_quantification_introduction

/-
The symbol `∀`, written `\all`, is called the universal quantifier.
It corresponds roughly to the English phrases 'for all' or 'for every'.#check
-/

/-
Most of our results above implicitly involve the universal quantifier. The command below shows that
`and_iff_and` asserts:
  `∀ (p q : Prop), p ∧ q ↔ q ∧ p`.
This is an abbreviation of the longer
  `∀ (p : Prop), ∀ (q : Prop), p ∧ q ↔ q ∧ p`

In English, this says, 'for every proposition `p`, for every proposition `q`, `p` and `q` if and
only if `q` and `p`.`
-/
#check and_iff_and

/-
We can *explicitly* prove universally quantified statements (i.e. introduce the universal
quantifier) by using `intro`, much as we do in implication introduction.
-/
theorem even_square_of_even : ∀ m : ℤ, even m → even (m*m) :=
begin
  intro m, -- In English, we'd write, 'Let `m` be an integer`.
  rintro ⟨k, hk⟩,
  use (2*k*k),
  rw hk,
  ring,
end

-- Exercise 078:
example : ∀ m : ℤ, odd m → odd (m*m) :=
begin
  sorry
end

-- Exercise 079:
/-
Statements involving multiple universal quantifiers can be proved using multiple applications of
`intro`, or by using `intros`.
-/
example : ∀ (a b : Prop), a ∧ b ↔ b ∧ a :=
begin
  intros a b,
  split,
  { sorry,} ,
  { sorry,} ,
end

end universal_quantification_introduction

section universal_quantification_elimination
/-
Elimination of the universal quantifier has similar syntax to implication elimination.
-/
example (h : even 10) : even (10*10) :=
(even_square_of_even 10) h

/-
In the above example, `even_square_of_even 10` is the stament that results from eliminating the
universal quantifier to give `even 10 → even (10*10)`. We now use implication elimination,
applying this result to `h : even 10`, to give `even (10*10)`.

When applying statements or functions, Lean treats `f x y` to be the same as `(f x) y`, so the
above proof can be written more simply as folllows.
-/
example (h : even 10) : even (10*10) :=
even_square_of_even 10 h

/-
The tactic approach offers two other ways to eliminate the universal quantifier. We can use `apply`.
-/
example (h : even 10) : even (10*10) :=
begin
  apply even_square_of_even, -- This does both `∀`-elimination and `→`-elimination.
  exact h,
end

/-
If the universallly quantified statement is in the context, we can use `specialize`.
-/
example (h₁ : ∀ m, even m → even (m*m)) (h₂ : even 10): even (10*10) :=
begin
  specialize h₁ 10,
  exact h₁ h₂,
end

-- Exercise 080:
-- Solve the following using `specialize`, `have`, `linarith`, and `exact`.
example (y z : ℤ) (h₁ : ∀ x : ℤ, x ≥ 0 → x + y ≥ y) (h₂ : z ≥ -2) : (z + 2) + y ≥ y :=
begin
  sorry
end

end universal_quantification_elimination

end mth1001


import algebra.group_power tactic.norm_num algebra.big_operators

-- sheet 5 solns

def Fib : ℕ → ℕ
| 0 := 0
| 1 := 1
| (n+2) := Fib n + Fib (n+1)

--#eval Fib 10
--#reduce Fib 10

def is_even (n : ℕ) : Prop := ∃ k, n=2*k
def is_odd (n : ℕ) : Prop := ∃ k, n=2*k+1

lemma even_of_even_add_even (a b : ℕ) : is_even a → is_even b → is_even (a+b) :=
begin
intros Ha Hb,
cases Ha with k Hk,
cases Hb with l Hl,
existsi k+l,
simp [Hk,Hl,add_mul],
end

lemma odd_of_odd_add_even {a b : ℕ} : is_odd a → is_even b → is_odd (a+b) :=
begin
intros Ha Hb,
cases Ha with k Hk,
cases Hb with l Hl,
existsi k+l,
simp [Hk,Hl,add_mul],
end

lemma odd_of_even_add_odd {a b : ℕ} : is_even a → is_odd b → is_odd (a+b) :=
λ h1 h2, (add_comm b a) ▸ (odd_of_odd_add_even h2 h1)

lemma even_of_odd_add_odd {a b : ℕ} : is_odd a → is_odd b → is_even (a+b) :=
begin
intros Ha Hb,
cases Ha with k Hk,
cases Hb with l Hl,
existsi k+l+1,
-- simp [mul_add,Hk,Hl,one_add_one_eq_two] -- fails!
rw [Hk,Hl,mul_add,mul_add],
change 2 with 1+1,
simp [mul_add,add_mul],
end

theorem Q1a : ∀ n : ℕ, n ≥ 1 →
  is_odd (Fib (3*n-2)) ∧ is_odd (Fib (3*n-1)) ∧ is_even (Fib (3*n)) :=
begin
intros n Hn,
cases n with m,
have : ¬ (0 ≥ 1) := dec_trivial,
exfalso,
exact (this Hn),
induction m with d Hd,
  exact ⟨⟨0,rfl⟩,⟨0,rfl⟩,⟨1,rfl⟩⟩,
change 3*nat.succ d-2 with 3*d+1 at Hd,
change 3*nat.succ d -1 with 3*d+2 at Hd,
change 3*nat.succ d with 3*d+3 at Hd,
change 3*nat.succ (nat.succ d)-2 with 3*d+4,
change 3*nat.succ (nat.succ d)-1 with 3*d+5,
change 3*nat.succ (nat.succ d) with 3*d+6,

let Hyp := Hd (begin apply nat.succ_le_succ,exact nat.zero_le d,end),
let H1 := Hyp.left,
let H2 := Hyp.right.left,
let H3 := Hyp.right.right,
have H4 : is_odd (Fib (3*d+4)),
  change Fib (3*d+4) with Fib (3*d+2)+Fib(3*d+3),
  exact odd_of_odd_add_even H2 H3,
have H5 : is_odd (Fib (3*d+5)),
  change Fib (3*d+5) with Fib (3*d+3)+Fib(3*d+4),
  exact odd_of_even_add_odd H3 H4,
have H6 : is_even (Fib (3*d+6)),
  change Fib (3*d+6) with Fib (3*d+4)+Fib(3*d+5),
  exact even_of_odd_add_odd H4 H5,
exact ⟨H4,H5,H6⟩,
end

theorem Q1b : is_odd (Fib (2017)) :=
begin
have H : 2017 = 3*673-2 := dec_trivial,
rw [H],
exact (Q1a 673 (dec_trivial)).left,
end


/-
# Example: Knapsack

This is a simple example of the specification of an optimisation problem: the
[knapsack problem](https://en.wikipedia.org/wiki/Knapsack_problem).
-/
import LeanSpec.lib.Util

/-
Given is a set of items each with a weight (cost) and a value. The aim is to choose
the optimal set of items that maximises the value but whose weight does not exceed the
capacity of the knapsack.

## Item

First, define a structure that represents an item in the knapsack.
-/
structure Item where
  value : Nat
  cost  : Nat
deriving BEq

/-
Each item simply has a value and a cost. Note there may be multiple items in the
knapsack with the same value and cost.

A `structure` is Lean's equivalent of a `record` or a `struct` in other languages; it
packages separate elements into a single unit. However, as we shall see later, Lean's
structures are rather more powerful.

## cost

The cost of an item list is the sum of the costs of the individual items.
-/
def cost (items : List Item) : Nat :=
  (items.map Item.cost).add 0

/-
Map the function that extracts the cost of an item over the list, and sum the
resulting costs.

## value

The value of an item list is the sum of the values of the individual items.
-/
def value (items : List Item) : Nat :=
  (items.map Item.value).add 0

/-
## SubList

A list `as` is a sublist of `bs` if every element in `as` is also an element of `bs`,
taking account of duplicates. For example, if `as` has two copies of an item, `bs` must
contain at least two copies of the same item.
-/
def SubList [BEq α] (as bs : List α) :=
  ∀ a : α, as.numOccurs a ≤ bs.numOccurs a

/-
## Candidate

A list of items is a candidate solution to the knapsack problem if its elements are
all drawn from the source items, and its cost does not exceed the capacity.
-/
def Candidate₁ (capacity : Nat) (source : List Item) (candidate : List Item) :=
  SubList candidate source ∧ cost candidate ≤ capacity

/-
## Knapsack

With these definitions, we can specify the knapsack problem.

The optimal solution is a (not necessarily unique) candidate solution such that
no better candidate exists.
-/
def Knapsack₁ (capacity : Nat) (source : List Item) :=
  { opt : List Item
    // Candidate₁ capacity source opt ∧
       ∀ is : List Item, Candidate₁ capacity source is → value is ≤ value opt }

/-
This is a nice example of where formal specification works very well.
We capture a problem by characterising the properties it must exhibit with no
thought for how how it might be implemented. In this case we define the nature
of a potential solution, then specify the result as the (not necessarily unique)
solution that is at least as good as any other solution. This is a fairly typical
pattern for specifying optimisation problems.

There are always multiple ways to specify the same problem. Below,
rather than a predicate that determines if a list of items is a potential
solution, we define the type of potential solutions.
-/

def Candidate₂ (capacity : Nat) (source : List Item) :=
  { cand : List Item // SubList cand source ∧ cost cand ≤ capacity }

/-
A element of type `Candidate₂ capacity source` is any list whose elements are drawn from
`source` and whose cost does not exceed `capacity`.

With this definition of candidate, the knapsack problem can be specified as:
-/
def Knapsack₂ (capacity : Nat) (source : List Item) :=
  { opt : Candidate₂ capacity source
    // ∀ cand : Candidate₂ capacity source, value cand.val ≤ value opt.val }

/-
In this approach the subtype property can assume `opt` is a candidate solution.
In the first definition the subtype property could only assume `opt` is an
arbitrary list of items.
-/


/-
Copyright (c) 2019 Johan Commelin. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johan Commelin
-/
import data.multiset.nodup
import data.list.nat_antidiagonal

/-!
# Antidiagonals in ℕ × ℕ as multisets

This file defines the antidiagonals of ℕ × ℕ as multisets: the `n`-th antidiagonal is the multiset
of pairs `(i, j)` such that `i + j = n`. This is useful for polynomial multiplication and more
generally for sums going from `0` to `n`.

## Notes

This refines file `data.list.nat_antidiagonal` and is further refined by file
`data.finset.nat_antidiagonal`.
-/

namespace multiset
namespace nat

/-- The antidiagonal of a natural number `n` is
    the multiset of pairs `(i, j)` such that `i + j = n`. -/
def antidiagonal (n : ℕ) : multiset (ℕ × ℕ) :=
list.nat.antidiagonal n

/-- A pair (i, j) is contained in the antidiagonal of `n` if and only if `i + j = n`. -/
@[simp] lemma mem_antidiagonal {n : ℕ} {x : ℕ × ℕ} :
  x ∈ antidiagonal n ↔ x.1 + x.2 = n :=
by rw [antidiagonal, mem_coe, list.nat.mem_antidiagonal]

/-- The cardinality of the antidiagonal of `n` is `n+1`. -/
@[simp] lemma card_antidiagonal (n : ℕ) : (antidiagonal n).card = n+1 :=
by rw [antidiagonal, coe_card, list.nat.length_antidiagonal]

/-- The antidiagonal of `0` is the list `[(0, 0)]` -/
@[simp] lemma antidiagonal_zero : antidiagonal 0 = {(0, 0)} :=
rfl

/-- The antidiagonal of `n` does not contain duplicate entries. -/
@[simp] lemma nodup_antidiagonal (n : ℕ) : nodup (antidiagonal n) :=
coe_nodup.2 $ list.nat.nodup_antidiagonal n

@[simp] lemma antidiagonal_succ {n : ℕ} :
  antidiagonal (n + 1) = (0, n + 1) ::ₘ ((antidiagonal n).map (prod.map nat.succ id)) :=
by simp only [antidiagonal, list.nat.antidiagonal_succ, coe_map, cons_coe]

lemma antidiagonal_succ' {n : ℕ} :
  antidiagonal (n + 1) = (n + 1, 0) ::ₘ ((antidiagonal n).map (prod.map id nat.succ)) :=
by rw [antidiagonal, list.nat.antidiagonal_succ', ← coe_add, add_comm, antidiagonal, coe_map,
  coe_add, list.singleton_append, cons_coe]

lemma antidiagonal_succ_succ' {n : ℕ} :
  antidiagonal (n + 2) =
  (0, n + 2) ::ₘ (n + 2, 0) ::ₘ ((antidiagonal n).map (prod.map nat.succ nat.succ)) :=
by { rw [antidiagonal_succ, antidiagonal_succ', map_cons, map_map, prod_map], refl }

lemma map_swap_antidiagonal {n : ℕ} :
  (antidiagonal n).map prod.swap = antidiagonal n :=
by rw [antidiagonal, coe_map, list.nat.map_swap_antidiagonal, coe_reverse]

end nat
end multiset


import tactic.linarith

namespace mth1001

section existential_quantification

/-
An integer `n` is `even` if there is some integer `m` such that `n = 2*m`. This condition is
expressed as
  `∃ m, n = 2 * m`,
where the symbol `∃` is called 'the universal quantifier', read as 'there exists' or 'there is',
and typed as `\ex`.
-/

/-
The following is a Lean representation of the above definition.
-/
def even (n : ℤ) := ∃ m, n = 2*m

/-
To *prove* a particular number `n` is even, it suffices to provide an intger `m` and then
show that `n = 2 *m ` holds.
-/

/-
Below, the goal is initially to show `∃ m, 10 = 2 *m`. The tactic `use 5` removes the
universal quantifier and replaces `m` with `5`, leaving the goal `10 = 2 * 5`.

The tactic `norm_num` closes goals that involve simple arithmetic on numerical expressions.
-/
example : even 10 :=
begin
  use 5,
  norm_num,
end

-- Exercise 039:
example : even 18 :=
begin
  sorry
end

/-
We aren't restricted to using numerical expressions. As seen in a previous section, the
`linarith` tactic solves linear equations and inequalities.
-/
variables a b c d : ℤ

example (h : b = 6 * a) : even b :=
begin
  use (3*a), -- After this tactic, the goal is to prove `b = 2 * (3 * a)`.
  linarith, -- This tactic closes the goal.
end

-- Exercise 040:
example (h₁ : c = 2 * a + 1) (h₂ : d = 2 * b + 1) : even (c + d) :=
begin
  sorry
end

/-
An integer `n` is `odd` if `∃ m, n = 2 * m + 1`.
-/

def odd (n : ℤ) := ∃ m, n = 2 * m + 1

-- Exercise 041:
example : odd 7 :=
begin
  sorry
end

-- Exercise 042:
example (h₁ : c = 2 * a) (h₂ : d = 2 * b + 1) : odd (c + d) :=
begin
  sorry
end

/-
We've seen how to *prove* a number is even or even. Now we'll see how to *apply* the fact that
a number is even or even.

Given a premise `h : even a`, the tactic `cases h with k hk` removes `h` and introduces into the
context a new variable `k` and the premise `hk : a = 2 * k`. As usual, there is nothing special
about the names `k` or `hk`.
-/

example (h : even a) : even (5*a) :=
begin
  cases h with k hk,
  use (5*k),
  linarith,
end

-- Exercise 043:
-- Prove that the square of an even number is even.
example (h : even a) : even (a*a) :=
begin
  sorry
end

-- Exercise 044:
-- Prove that the sum of two odd numbers is even.
example (h₁ : odd a) (h₂ : odd b) : even (a + b) :=
begin
  sorry
end

-- Exercise 045:
/-
The results of this section can be rewritten to remove any premises, producing statements
that involve implication.

For example, you should only need to add one line to the beginning of a previous proof to
conclude the following.
-/
example : even a → even (a*a) :=
begin
  sorry
end

end existential_quantification

/-
SUMMARY:

* Definitions in Lean.
* Recap of the `norm_num` and `linarith` tactics.

* Introduction of `∃` via `use`.
* Elimination of `∃` via `cases`.
-/

end mth1001


-- ---------------------------------------------------------------------
-- Ejercicio 1. Realizar las siguientes acciones:
-- 1. Importar la teoría de los números reales.
-- 2. Declarar x e y como variables sobre los reales.
-- ----------------------------------------------------------------------

import data.real.basic   -- 1
variables {x y : ℝ}      -- 2

-- ---------------------------------------------------------------------
-- Ejercicio 2. Demostrar que si
--    x^2 + y^2 = 0)
-- entonces
--   x = 0
-- ----------------------------------------------------------------------

lemma aux
  (h : x^2 + y^2 = 0)
  : x = 0 :=
begin
  have h' : x^2 = 0,
    { apply le_antisymm,
      show x ^ 2 ≤ 0,
        calc
          x ^ 2 ≤ x^2 + y^2 : by simp [le_add_of_nonneg_right,
                                       pow_two_nonneg]
            ... = 0         : by exact h,
      apply pow_two_nonneg },
  exact pow_eq_zero h',
end

-- Prueba
-- ======

/-
x y : ℝ,
h : x ^ 2 + y ^ 2 = 0
⊢ x = 0
  >> have h' : x^2 = 0,
| ⊢ x ^ 2 = 0
|   >>   { apply le_antisymm,
| | ⊢ x ^ 2 ≤ 0
|   >>     show x ^ 2 ≤ 0,
|   >>       calc
|   >>         x ^ 2 ≤ x^2 + y^2 : by simp [le_add_of_nonneg_right,
|   >>                                      pow_two_nonneg]
|   >>           ... = 0         : by exact h,
| ⊢ 0 ≤ x ^ 2
  >>     apply pow_two_nonneg },
h' : x ^ 2 = 0
⊢ x = 0
  >> exact pow_eq_zero h',
no goals
-/

-- Comentario: Los lemas usados son:
-- + le_add_of_nonneg_right : 0 ≤ y → x ≤ x + y
-- + le_antisymm : x ≤ y → y ≤ x → x = y
-- + pow_eq_zero : ∀ {n : ℕ}, x ^ n = 0 → x = 0
-- + pow_two_nonneg x : 0 ≤ x ^ 2

-- ---------------------------------------------------------------------
-- Ejercicio 3. Demostrar que
--    x^2 + y^2 = 0 ↔ x = 0 ∧ y = 0
-- ----------------------------------------------------------------------

example : x^2 + y^2 = 0 ↔ x = 0 ∧ y = 0 :=
begin
  split,
    intro h,
    split,
     apply (aux h),
    rw add_comm at h,
    apply (aux h),
  intro h1,
  cases h1 with h2 h3,
  rw [h2, h3],
  ring,
end

-- Prueba
-- ======

/-
x y : ℝ
⊢ x ^ 2 + y ^ 2 = 0 ↔ x = 0 ∧ y = 0
  >> split,
| ⊢ x ^ 2 + y ^ 2 = 0 → x = 0 ∧ y = 0
|   >>   intro h,
| h : x ^ 2 + y ^ 2 = 0
| ⊢ x = 0 ∧ y = 0
|   >>   split,
| | ⊢ x = 0
|   >>    apply (aux h),
| h : y ^ 2 + x ^ 2 = 0
| ⊢ y = 0
|   >>   rw add_comm at h,
|   >>   apply (aux h),
⊢ x = 0 ∧ y = 0 → x ^ 2 + y ^ 2 = 0
  >> intro h1,
h1 : x = 0 ∧ y = 0
⊢ x ^ 2 + y ^ 2 = 0
  >> cases h1 with h2 h3,
h2 : x = 0,
h3 : y = 0
⊢ x ^ 2 + y ^ 2 = 0
  >> rw [h2, h3],
⊢ 0 ^ 2 + 0 ^ 2 = 0
  >> ring,
no goals
-/

-- Comentario: La táctica split, si el objetivo es un bicondicional
-- (P ↔ Q), aplica la introducción del bicondicional; es decir, lo
-- sustituye por dos nuevos objetivos: P → Q y Q → P.


/-
Copyright (c) 2021 Benjamin Davidson. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Benjamin Davidson
-/

import analysis.special_functions.integrals
open interval_integral real
open_locale real

/-! ### Simple functions -/

/- constants -/
example : ∫ x : ℝ in 8..11, (1 : ℝ) = 3 := by norm_num
example : ∫ x : ℝ in 5..19, (12 : ℝ) = 168 := by norm_num

/- the identity function -/
example : ∫ x : ℝ in (-1)..4, x = 15 / 2 := by norm_num
example : ∫ x : ℝ in 4..5, x * 2 = 9 := by norm_num

/- inverse -/
example : ∫ x : ℝ in 2..3, x⁻¹ = log (3 / 2) := by norm_num

/- natural powers -/
example : ∫ x : ℝ in 2..4, x ^ (3 : ℕ) = 60 := by norm_num

/- trigonometric functions -/
example : ∫ x in 0..π, sin x = 2 := by norm_num
example : ∫ x in 0..π/4, cos x = sqrt 2 / 2 := by simp
example : ∫ x in 0..π, 2 * sin x = 4 := by norm_num
example : ∫ x in 0..π/2, cos x / 2 = 1 / 2 := by simp
example : ∫ x : ℝ in 0..1, 1 / (1 + x ^ 2) = π/4 := by simp
example : ∫ x in 0..2*π, sin x ^ 2 = π := by simp [mul_div_cancel_left]
example : ∫ x in 0..π/2, cos x ^ 2 / 2 = π/8 := by norm_num [div_div_eq_div_mul]
example : ∫ x in 0..π, cos x ^ 2 - sin x ^ 2 = 0 := by simp [integral_cos_sq_sub_sin_sq]

/- the exponential function -/
example : ∫ x in 0..2, -exp x = 1 - exp 2 := by simp

/- linear combinations (e.g. polynomials) -/
example : ∫ x : ℝ in 0..2, 6*x^5 + 3*x^4 + x^3 - 2*x^2 + x - 7 = 1048 / 15 := by norm_num
example : ∫ x : ℝ in 0..1, exp x + 9 * x^8 + x^3 - x/2 + (1 + x^2)⁻¹ = exp 1 + π / 4 := by norm_num

/-! ### Functions composed with multiplication by and/or addition of a constant -/

/- many examples are computable by `norm_num` -/
example : ∫ x in 0..2, -exp (-x) = exp (-2) - 1 := by norm_num
example : ∫ x in 1..2, exp (5*x - 5) = 1/5 * (exp 5 - 1) := by norm_num
example : ∫ x in 0..π, cos (x/2) = 2 := by norm_num
example : ∫ x in 0..π/4, sin (2*x) = 1/2 := by norm_num [mul_div_comm, mul_one_div]
example (ω φ : ℝ) : ω * ∫ θ in 0..π, sin (ω*θ + φ) = cos φ - cos (ω*π + φ) := by simp

/- some examples may require a bit of algebraic massaging -/
example {L : ℝ} (h : L ≠ 0) : ∫ x in 0..2/L*π, sin (L/2 * x) = 4 / L :=
begin
  norm_num [div_ne_zero h, ← mul_assoc],
  field_simp [h, mul_div_cancel],
  norm_num,
end

/- you may need to provide `norm_num` with the composition lemma you are invoking if it has a
  difficult time recognizing the function you are trying to integrate -/
example : ∫ x : ℝ in 0..2, 3 * (x + 1) ^ 2 = 26 :=
  by norm_num [integral_comp_add_right (λ x, x ^ 2)]
example : ∫ x : ℝ in -1..0, (1 + (x + 1) ^ 2)⁻¹ = π/4 :=
  by simp [integral_comp_add_right (λ x, (1 + x ^ 2)⁻¹)]


import data.real.basic
import data.int.parity

/-
In this file, we learn how to handle the ∃ quantifier.

In order to prove `∃ x, P x`, we give some x₀ using tactic `use x₀` and
then prove `P x₀`. This x₀ can be an object from the local context
or a more complicated expression.
-/
example : ∃ n : ℕ, 8 = 2*n :=
begin
  use 4,
  refl,  -- this is the tactic analogue of the rfl proof term
end

/-
In order to use `h : ∃ x, P x`, we use the `cases` tactic to fix
one x₀ that works.

Again h can come straight from the local context or can be a more
complicated expression.
-/
example (n : ℕ) (h : ∃ k : ℕ, n = k + 1) : n > 0 :=
begin
  -- Let's fix k₀ such that n = k₀ + 1.
  cases h with k₀ hk₀,
  -- It now suffices to prove k₀ + 1 > 0.
  rw hk₀,
  -- and we have a lemma about this
  exact nat.succ_pos k₀,
end

/-
The next exercises use divisibility in ℤ (beware the ∣ symbol which is
not ASCII).

By definition, a ∣ b ↔ ∃ k, b = a*k, so you can prove a ∣ b using the
`use` tactic.
-/

-- Until the end of this file, a, b and c will denote integers, unless
-- explicitly stated otherwise
variables a b c : ℤ

-- 0029
example (h₁ : a ∣ b) (h₂ : b ∣ c) : a ∣ c :=
begin
  cases h₁ with x₁ hx₁,
  cases h₂ with x₂ hx₂,
  use x₁ * x₂,
  rw [hx₂, hx₁, mul_assoc],
end

/-
A very common pattern is to have an assumption or lemma asserting
  h : ∃ x, y = ...
and this is used through the combo:
  cases h with x hx,
  rw hx at ...
The tactic `rcases` allows us to do recursive `cases`, as indicated by its name,
and also simplifies the above combo when the name hx is replaced by the special
name `rfl`, as in the following example.
It uses the anonymous constructor angle brackets syntax.
-/

example (h1 : a ∣ b) (h2 : a ∣ c) : a ∣ b+c :=
begin
  rcases h1 with ⟨k, rfl⟩,
  rcases h2 with ⟨l, rfl⟩,
  use k+l,
  ring,
end

/-
You can use the same `rfl` trick with the `rintros` tactic.
-/

example : a ∣ b → a ∣ c → a ∣ b+c :=
begin
  rintros ⟨k, rfl⟩ ⟨l, rfl⟩,
  use k+l,
  ring,
end

-- 0030
example : 0 ∣ a ↔ a = 0 :=
begin
  split; intro h,
  show a = 0, by { rcases h with ⟨n, rfl⟩, ring },
  show 0 ∣ a, by { use 0, rw h, refl }
end

/-
We can now start combining quantifiers, using the definition

  surjective (f : X → Y) := ∀ y, ∃ x, f x = y

-/
open function

-- In the remaining of this file, f and g will denote functions from
-- ℝ to ℝ.
variables (f g : ℝ → ℝ)

-- 0031
example (h : surjective (g ∘ f)) : surjective g :=
begin
  intro y,
  rcases h y with ⟨x, rfl⟩,
  use f x,
end

/-
The above exercise can be done in three lines. Try to do the
next exercise in four lines.
-/

-- 0032
example (hf : surjective f) (hg : surjective g) : surjective (g ∘ f) :=
begin
  intro z,
  rcases hg z with ⟨y, rfl⟩,
  rcases hf y with ⟨z, rfl⟩,
  use z,
end


import tuto_lib
import data.int.parity
/-
Negations, proof by contradiction and contraposition.

This file introduces the logical rules and tactics related to negation:
exfalso, by_contradiction, contrapose, by_cases and push_neg.

There is a special statement denoted by `false` which, by definition,
has no proof.

So `false` implies everything. Indeed `false → P` means any proof of
`false` could be turned into a proof of P.
This fact is known by its latin name
"ex falso quod libet" (from false follows whatever you want).
Hence Lean's tactic to invoke this is called `exfalso`.
-/

example : false → 0 = 1 :=
begin
  intro h,
  exfalso,
  exact h,
end

/-
The preceding example suggests that this definition of `false` isn't very useful.
But actually it allows us to define the negation of a statement P as
"P implies false" that we can read as "if P were true, we would get
a contradiction". Lean denotes this by `¬ P`.

One can prove that (¬ P) ↔ (P ↔ false). But in practice we directly
use the definition of `¬ P`.
-/

example {x : ℝ} : ¬ x < x :=
begin
  intro hyp,
  rw lt_iff_le_and_ne at hyp,
  cases hyp with hyp_inf hyp_non,
  clear hyp_inf, -- we won't use that one, so let's discard it
  change x = x → false at hyp_non, -- Lean doesn't need this psychological line
  apply hyp_non,
  refl,
end

open int

-- 0045
example (n : ℤ) (h_pair : even n) (h_non_pair : ¬ even n) : 0 = 1 :=
begin
  sorry
end

-- 0046
example (P Q : Prop) (h₁ : P ∨ Q) (h₂ : ¬ (P ∧ Q)) : ¬ P ↔ Q :=
begin
  sorry
end

/-
The definition of negation easily implies that, for every statement P,
P → ¬ ¬ P

The excluded middle axiom, which asserts P ∨ ¬ P allows us to
prove the converse implication.

Together those two implications form the principle of double negation elimination.
  not_not {P : Prop} : (¬ ¬ P) ↔ P

The implication `¬ ¬ P → P` is the basis for proofs by contradiction:
in order to prove P, it suffices to prove ¬¬ P, ie `¬ P → false`.

Of course there is no need to keep explaining all this. The tactic
`by_contradiction Hyp` will transform any goal P into `false` and
add Hyp : ¬ P to the local context.

Let's return to a proof from the 5th file: uniqueness of limits for a sequence.
This cannot be proved without using some version of the excluded middle
axiom. We used it secretely in

eq_of_abs_sub_le_all (x y : ℝ) : (∀ ε > 0, |x - y| ≤ ε) → x = y

(we'll prove a variation on this lemma below).
-/
example (u : ℕ → ℝ) (l l' : ℝ) : seq_limit u l → seq_limit u l' → l = l' :=
begin
  intros hl hl',
  by_contradiction H,
  change l ≠ l' at H, -- Lean does not need this line
  have ineg : |l-l'| > 0,
    exact abs_pos_of_ne_zero (sub_ne_zero_of_ne H),
  cases hl ( |l-l'|/4 ) (by linarith) with N hN,
  cases hl' ( |l-l'|/4 ) (by linarith) with N' hN',
  let N₀ := max N N', -- this is a new tactic, whose effect should be clear
  specialize hN N₀ (le_max_left _ _),
  specialize hN' N₀ (le_max_right _ _),
  have clef : |l-l'| < |l-l'|,
    calc
    |l - l'| = |(l-u N₀) + (u N₀ -l')|   : by ring
         ... ≤ |l - u N₀| + |u N₀ - l'|  : by apply abs_add
         ... = |u N₀ - l| + |u N₀ - l'|  : by rw abs_sub
         ... < |l-l'|                    : by linarith,
  linarith, -- linarith can also find simple numerical contradictions
end

/-
Another incarnation of the excluded middle axiom is the principle of
contraposition: in order to prove P ⇒ Q, it suffices to prove
non Q ⇒ non P.
-/

-- Using a proof by contradiction, let's prove the contraposition principle
-- 0047
example (P Q : Prop) (h : ¬ Q → ¬ P) : P → Q :=
begin
  sorry
end

/-
Again Lean doesn't need to be explain this principle. We can use the
`contrapose` tactic.
-/

example (P Q : Prop) (h : ¬ Q → ¬ P) : P → Q :=
begin
  contrapose,
  exact h,
end

/-
In the next exercise, we'll use
 odd n : ∃ k, n = 2*k + 1
 not_even_iff_odd : ¬ even n ↔ odd n,
-/
-- 0048
example (n : ℤ) : even (n^2) ↔ even n :=
begin
  sorry
end
/-
As a last step on our law of the excluded middle tour, let's notice that, especially
in pure logic exercises, it can sometimes be useful to use the
excluded middle axiom in its original form:
  classical.em : ∀ P, P ∨ ¬ P

Instead of applying this lemma and then using the `cases` tactic, we
have the shortcut
 by_cases h : P,

combining both steps to create two proof branches: one assuming
h : P, and the other assuming h : ¬ P

For instance, let's prove a reformulation of this implication relation,
which is sometimes used as a definition in other logical foundations,
especially those based on truth tables (hence very strongly using
excluded middle from the very beginning).
-/

variables (P Q : Prop)

example : (P → Q) ↔ (¬ P ∨ Q) :=
begin
  split,
  { intro h,
    by_cases hP : P,
    { right,
      exact h hP },
    { left,
      exact hP } },
  { intros h hP,
    cases h with hnP hQ,
    { exfalso,
      exact hnP hP },
    { exact hQ } },
end

-- 0049
example : ¬ (P ∧ Q) ↔ ¬ P ∨ ¬ Q :=
begin
  sorry
end

/-
It is crucial to understand negation of quantifiers.
Let's do it by hand for a little while.
In the first exercise, only the definition of negation is needed.
-/

-- 0050
example (n : ℤ) : ¬ (∃ k, n = 2*k) ↔ ∀ k, n ≠ 2*k :=
begin
  sorry
end

/-
Contrary to negation of the existential quantifier, negation of the
universal quantifier requires excluded middle for the first implication.
In order to prove this, we can use either
* a double proof by contradiction
* a contraposition, not_not : (¬ ¬ P) ↔ P) and a proof by contradiction.
-/

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

-- 0051
example (f : ℝ → ℝ) : ¬ even_fun f ↔ ∃ x, f (-x) ≠ f x :=
begin
  sorry
end

/-
Of course we can't keep repeating the above proofs, especially the second one.
So we use the `push_neg` tactic.
-/

example : ¬ even_fun (λ x, 2*x) :=
begin
  unfold even_fun, -- Here unfolding is important because push_neg won't do it.
  push_neg,
  use 42,
  linarith,
end

-- 0052
example (f : ℝ → ℝ) : ¬ even_fun f ↔ ∃ x, f (-x) ≠ f x :=
begin
  sorry
end

def bounded_above (f : ℝ → ℝ) := ∃ M, ∀ x, f x ≤ M

example : ¬ bounded_above (λ x, x) :=
begin
  unfold bounded_above,
  push_neg,
  intro M,
  use M + 1,
  linarith,
end

-- Let's contrapose
-- 0053
example (x : ℝ) : (∀ ε > 0, x ≤ ε) → x ≤ 0 :=
begin
  sorry
end

/-
The "contrapose, push_neg" combo is so common that we can abreviate it to
`contrapose!`

Let's use this trick, together with:
  eq_or_lt_of_le : a ≤ b → a = b ∨ a < b
-/

-- 0054
example (f : ℝ → ℝ) : (∀ x y, x < y → f x < f y) ↔ (∀ x y, (x ≤ y ↔ f x ≤ f y)) :=
begin
  sorry
end