import data.int.basic
import ring_theory.principal_ideal_domain
import tactic

/- TEXT:
.. _section_building_the_gaussian_integers:

Building the Gaussian Integers
------------------------------

We will now illustrate the use of the algebraic hierarchy in Lean by
building an important mathematical object, the *Gaussian integers*,
and showing that it is a Euclidean domain. In other words, according to
the terminology we have been using, we will define the Gaussian integers
and show that they are an instance of the Euclidean domain structure.

In ordinary mathematical terms, the set of Gaussian integers :math:`\Bbb{Z}[i]`
is the set of complex numbers :math:`\{ a + b i \mid a, b \in \Bbb{Z}\}`.
But rather than define them as a subset of the complex numbers, our goal
here is to define them as a data type in their own right. We do this by
representing a Gaussian integer as a pair of integers, which we think of as the
*real* and *imaginary* parts.
BOTH: -/
-- QUOTE:
@[ext] structure gaussint := (re : ℤ) (im : ℤ)
-- QUOTE.

/- TEXT:
We first show that the Gaussian integers have the structure of a ring,
with ``0`` defined to be ``⟨0, 0⟩``, ``1`` defined to be ``⟨1, 0⟩``, and
addition defined pointwise. To work out the definition of multiplication,
remember that we want the element :math:`i`, represented by ``⟨0, 1⟩``, to
be a square root of :math:`-1`. Thus we want

.. math::

   (a + bi) (c + di) & = ac + bci + adi + bd i^2 \\
     & = (ac - bd) + (bc + ad)i.

This explains the definition of ``has_mul`` below.
BOTH: -/
namespace gaussint

-- QUOTE:
instance : has_zero gaussint := ⟨⟨0, 0⟩⟩
instance : has_one gaussint  := ⟨⟨1, 0⟩⟩
instance : has_add gaussint  := ⟨λ x y, ⟨x.re + y.re, x.im + y.im⟩⟩
instance : has_neg gaussint  := ⟨λ x, ⟨-x.re, -x.im⟩⟩
instance : has_mul gaussint  :=
  ⟨λ x y, ⟨x.re * y.re - x.im * y.im, x.re * y.im + x.im * y.re⟩⟩
-- QUOTE.

/- TEXT:
As noted in :numref:`section_structures`, it is a good idea to put all the definitions
related to a data type in a namespace with the same name. Thus in the Lean
files associated with this chapter, these definitions are made in the
``gaussint`` namespace.

Notice that here we are defining the interpretations of the notation ``0``,
``1``, ``+``, ``-``, and ``*`` directly, rather than naming them
``gaussint.zero`` and the like and assigning the notation to those.
It is often useful to have an explicit name for the definitions, for example,
to use with ``simp`` and ``rewrite``.
BOTH: -/
-- QUOTE:
theorem zero_def : (0 : gaussint) = ⟨0, 0⟩ := rfl
theorem one_def : (1 : gaussint) = ⟨1, 0⟩ := rfl
theorem add_def (x y : gaussint) : x + y = ⟨x.re + y.re, x.im + y.im⟩ := rfl
theorem neg_def (x : gaussint) : -x = ⟨-x.re, -x.im⟩ := rfl
theorem mul_def (x y : gaussint) :
  x * y = ⟨x.re * y.re - x.im * y.im, x.re * y.im + x.im * y.re⟩ := rfl
-- QUOTE.

/- TEXT:
It is also useful to name the rules that compute the real and imaginary
parts, and to declare them to the simplifier.
BOTH: -/
-- QUOTE:
@[simp] theorem zero_re : (0 : gaussint).re = 0 := rfl
@[simp] theorem zero_im : (0 : gaussint).im = 0 := rfl
@[simp] theorem one_re : (1 : gaussint).re = 1 := rfl
@[simp] theorem one_im : (1 : gaussint).im = 0 := rfl
@[simp] theorem add_re (x y : gaussint) : (x + y).re = x.re + y.re := rfl
@[simp] theorem add_im (x y : gaussint) : (x + y).im = x.im + y.im := rfl
@[simp] theorem neg_re (x : gaussint) : (-x).re = - x.re := rfl
@[simp] theorem neg_im (x : gaussint) : (-x).im = - x.im := rfl
@[simp] theorem mul_re (x y : gaussint) : (x * y).re = x.re * y.re - x.im * y.im := rfl
@[simp] theorem mul_im (x y : gaussint) : (x * y).im = x.re * y.im + x.im * y.re := rfl
-- QUOTE.

/- TEXT:
It is now surprisingly easy to show that the Gaussian integers are an instance
of a commutative ring. We are putting the structure concept to good use.
Each particular Gaussian integer is an instance of the ``gaussint`` structure,
whereas the type ``gaussint`` itself, together with the relevant operations, is an
instance of the ``comm_ring`` structure. The ``comm_ring`` structure, in turn,
extends the notational structures ``has_zero``, ``has_one``, ``has_add``,
``has_neg``, and ``has_mul``.

If you type ``instance : comm_ring gaussint := _``, click on the light bulb
that appears in VS Code, and then ask Lean to fill in a skeleton for the
structure definition, you will see a scary number of entries.
Jumping to the definition of the structure, however, shows that many of the
fields have default definitions that Lean will fill in for you automatically.
The essential ones appear in the definition below. In each case, the relevant
identity is proved by unfolding definitions, using the ``ext`` tactic
to reduce the identities to their real and imaginary components,
simplifying, and, if necessary, carrying out the relevant ring calculation in
the integers.
BOTH: -/
-- QUOTE:
instance : comm_ring gaussint :=
{ zero := 0,
  one := 1,
  add := (+),
  neg := λ x, -x,
  mul := (*),

  add_assoc := by { intros, ext; simp; ring },
  zero_add := by { intros, ext; simp },
  add_zero := by { intros, ext; simp },
  add_left_neg := by { intros, ext; simp },
  add_comm := by { intros, ext; simp; ring },
  mul_assoc := by { intros, ext; simp; ring },
  one_mul := by { intros, ext; simp },
  mul_one := by { intros, ext; simp },
  left_distrib := by { intros, ext; simp; ring },
  right_distrib := by { intros, ext; simp; ring },
  mul_comm := by { intros, ext; simp; ring } }
-- QUOTE.

/- TEXT:
Lean's library defines the class of *nontrivial* types to be types with at
least two distinct elements. In the context of a ring, this is equivalent
to saying the the zero is not equal to the one. Since some common theorems
depend on that fact, we may as well establish it now.
BOTH: -/
-- QUOTE:
instance : nontrivial gaussint :=
by { use [0, 1], rw [ne, gaussint.ext_iff], simp }
-- QUOTE.

end gaussint

/- TEXT:
We will now show that the Gaussian integers have an important additional
property. A *Euclidean domain* is a ring :math:`R` equipped with a *norm*
function :math:`N : R \to \mathbb{N}` with the following two properties:

- For every :math:`a` and :math:`b \ne 0` in :math:`R`, there are
  :math:`q` and :math:`r` in :math:`R` such that :math:`a = bq + r` and
  either :math:`r = 0` or `N(a) < N(b)`.
- For every :math:`a` and :math:`b \ne 0`, :math:`N(a) \le N(ab)`.

The ring of integers :math:`\Bbb{Z}` with :math:`N(a) = |a|` is an
archetypal example of a Euclidean domain.
In that case, we can take :math:`q` to be the
result of integer division of :math:`a` by :math:`b` and :math:`r`
to be the remainder. These functions are defined in Lean so that the
satisfy the following:
EXAMPLES: -/
-- QUOTE:
example (a b : ℤ) : a = b * (a / b) + a % b := eq.symm $ int.div_add_mod a b

example (a b : ℤ) : b ≠ 0 → 0 ≤ a % b := int.mod_nonneg a

example (a b : ℤ) : b ≠ 0 → a % b < abs b := int.mod_lt a
-- QUOTE.

/- TEXT:
In an arbitrary ring, an element :math:`a` is said to be a *unit* if it divides
:math:`1`. A nonzero element :math:`a` is said to be *irreducible* if it cannot
be written in the form :math:`a = bc`
where neither :math:`b` nor :math:`c` is a unit. In the integers, every
irreducible element :math:`a` is *prime*, which is to say, whenever :math:`a`
divides a product :math:`bc`, it divides either :math:`b` or :math:`c`. But
in other rings this property can fail. In the ring
:math:`\Bbb{Z}[\sqrt{-5}]`, we have

.. math::

  6 = 2 \cdot 3 = (1 + \sqrt{-5})(1 - \sqrt{-5}),

and the elements :math:`2`, :math:`3`, :math:`1 + \sqrt{-5}`, and
:math:`1 - \sqrt{-5}` are all irreducible, but they are not prime. For example,
:math:`2` divides the product :math:`(1 + \sqrt{-5})(1 - \sqrt{-5})`,
but it does not divide either factor. In particular, we no longer have
unique factorization: the number :math:`6` can be factored into irreducible
elements in more than one way.

In contrast, every Euclidean domain is a unique factorization domain, which
implies that every irreducible element is prime.
The axioms for a Euclidean domain imply that one can write any nonzero element
as a finite product of irreducible elements. They also imply that one can use
the Euclidean algorithm to find a greatest common divisor of any two
nonzero elements ``a`` and ``b``, i.e.~an element that is divisible by any
other common divisor. This, in turn, implies that factorization
into irreducible elements is unique up to multiplication by units.

We now show that the Gaussian integers are a Euclidean domain with
the norm defined by :math:`N(a + bi) = (a + bi)(a - bi) = a^2 + b^2`.
The Gaussian integer :math:`a - bi` is called the *conjugate* of :math:`a + bi`.
It is not hard to check that for any complex numbers :math:`x` and :math:`y`,
we have :math:`N(xy) = N(x)N(y)`.

To see that this definition of the norm makes the complex numbers a Euclidean
domain, only the first property is challenging. Suppose
we want to write :math:`a + bi = (c + di) q + r` for suitable :math:`q`
and :math:`r`. Treating :math:`a + bi` and :math:`c + di` are complex
numbers, carry out the division

.. math::

  \frac{a + bi}{c + di} = \frac{(a + bi)(c - di)}{(c + di)(c-di)} =
    \frac{ac + bd}{c^2 + d^2} + \frac{bc -ad}{c^2+d^2} i.

The real and imaginary parts might not be integers, but we can round
them to the nearest integers :math:`u` and :math:`v`. We can then express the
right-hand size as :math:`(u + vi) + (u' + v'i)`, where
:math:`u' + v'i` is the part left over. Note that we have
:math:`|u'| \le 1/2` and :math:`|v'| \le 1/2`, and hence

.. math::

  N(u' + v' i) = (u')^2 + (v')^2 \le 1/4 + 1/4 \le 1/2.

Multiplying through by :math:`c + di`, we have

.. math::

  a + bi = (c + di) (u + vi) + (c + di) (u' + v'i).

Setting :math:`q = u + vi` and :math:`r = (c + di) (u' + v'i)`, we have
:math:`a + bi = (c + di) q + r`, and we only need to
bound :math:`N(r)`:

.. math::

  N(r) = N(c + di)N(u' + v'i) \le N(c + di) \cdot 1/2 < N(c + di).

The argument we just carried out requires viewing the Gaussian integers
as a subset of the complex numbers. One option for formalizing it in Lean
is therefore to embed the Gaussian integers in the complex numbers, embed
the integers in the Gaussian integers, define the rounding function from the
real numbers to the integers, and take great care to pass back and forth
between these number systems appropriately.
In fact, this is exactly the approach that is followed in mathlib,
where the Gaussian integers themselves are constructed as a special case
of a ring of *quadratic integers*.
See the file `gaussian_int.lean <https://github.com/leanprover-community/mathlib/blob/master/src/number_theory/zsqrtd/gaussian_int.lean>`_.

Here we will instead carry out an argument that stays in the integers.
This illustrates an choice one commonly faces when formalizing mathematics.
Given an argument that requires concepts or machinery that is not already
in the library, one has two choices: either formalizes the concepts or machinery
needed, or adapt the argument to make use of concepts and machinery you
already have.
The first choice is generally a good investment of time when the results
can be used in other contexts.
Pragmatically speaking, however, sometimes seeking a more elementary proof
is more efficient.

The usual quotient-remainder theorem for the integers says that for
every :math:`a` and nonzero :math:`b`, there are :math:`q` and :math:`r`
such that :math:`a = b q + r` and :math:`0 \le r < b`.
Here we will make use of the following variation, which says that there
are :math:`q'` and :math:`r'` such that
:math:`a = b q' + r'` and :math:`|r'| \le b/2`.
You can check that if the value of :math:`r` in the first statement
satisfies :math:`r \le b/2`, we can take :math:`q' = q` and :math:`r' = r`,
and otherwise we can take :math:`q' = q + 1` and :math:`r' = r - b`.
We are grateful to Heather Macbeth for suggesting the following more
elegant approach, which avoids definition by cases.
We simply add ``b / 2`` to ``a`` before dividing and then subtract it
from the remainder.
BOTH: -/
namespace int

-- QUOTE:
def div' (a b : ℤ) := (a + b / 2) / b

def mod' (a b : ℤ) := (a + b / 2) % b - b / 2

theorem div'_add_mod' (a b : ℤ) : b * div' a b + mod' a b = a :=
begin
  rw [div', mod'],
  linarith [int.div_add_mod (a + b / 2) b],
end

theorem abs_mod'_le (a b : ℤ) (h : 0 < b): abs (mod' a b) ≤ b / 2 :=
begin
  rw [mod', abs_le],
  split,
  { linarith [int.mod_nonneg (a + b / 2) h.ne'] },
  have := int.mod_lt_of_pos (a + b / 2) h,
  have := int.div_add_mod b 2,
  have := int.mod_lt_of_pos b zero_lt_two,
  linarith
end
-- QUOTE.

/- TEXT:
Note the use of our old friend, ``linarith``. We will also need to express
``mod'`` in terms of ``div'``.
BOTH: -/
-- QUOTE:
theorem mod'_eq (a b : ℤ) : mod' a b = a - b * div' a b :=
by linarith [div'_add_mod' a b]
-- QUOTE.

end int

/- TEXT:
We will use the fact that :math:`x^2 + y^2` is equal to zero if and only if
:math:`x` and :math:`y` are both zero. As an exercise, we ask you to prove
that this holds in any ordered ring.
SOLUTIONS: -/
private theorem aux {α : Type*} [linear_ordered_ring α] {x y : α} (h : x^2 + y^2 = 0) : x = 0 :=
begin
  have h' : x^2 = 0,
  { apply le_antisymm _ (sq_nonneg x),
    rw ←h,
    apply le_add_of_nonneg_right (sq_nonneg y) },
  exact pow_eq_zero h'
end

-- QUOTE:
-- BOTH:
theorem sq_add_sq_eq_zero {α : Type*} [linear_ordered_ring α] (x y : α) :
  x^2 + y^2 = 0 ↔ x = 0 ∧ y = 0 :=
/- EXAMPLES:
  sorry
SOLUTIONS: -/
begin
  split,
  { intro h,
    split,
    { exact aux h },
    rw add_comm at h,
    exact aux h },
  rintros ⟨rfl, rfl⟩,
  norm_num
end
-- QUOTE.
-- BOTH:

/- TEXT:
We will put all the remaining definitions and theorems in this section
in the ``gaussint`` namespace.
First, we define the ``norm`` function and ask you to establish
some of its properties.
The proofs are all short.
BOTH: -/
namespace gaussint

-- QUOTE:
def norm (x : gaussint) := x.re^2 + x.im^2

@[simp] theorem norm_nonneg (x : gaussint) : 0 ≤ norm x :=
/- EXAMPLES:
sorry
SOLUTIONS: -/
by { apply add_nonneg; apply sq_nonneg }
-- BOTH:

theorem norm_eq_zero (x : gaussint) : norm x = 0 ↔ x = 0 :=
/- EXAMPLES:
sorry
SOLUTIONS: -/
by { rw [norm, sq_add_sq_eq_zero, gaussint.ext_iff], refl }
-- BOTH:

theorem norm_pos (x : gaussint) : 0 < norm x ↔ x ≠ 0 :=
/- EXAMPLES:
sorry
SOLUTIONS: -/
by { rw [lt_iff_le_and_ne, ne_comm, ne, norm_eq_zero], simp [norm_nonneg] }
-- BOTH:

theorem norm_mul (x y : gaussint) : norm (x * y) = norm x * norm y :=
/- EXAMPLES:
sorry
SOLUTIONS: -/
by { simp [norm], ring }
-- BOTH:
-- QUOTE.

/- TEXT:
Next we define the conjugate function:
BOTH: -/
-- QUOTE:
def conj (x : gaussint) : gaussint := ⟨x.re, -x.im⟩

@[simp] theorem conj_re (x : gaussint) : (conj x).re = x.re := rfl
@[simp] theorem conj_im (x : gaussint) : (conj x).im = - x.im := rfl

theorem norm_conj (x : gaussint) : norm (conj x) = norm x :=
by { simp [norm] }
-- QUOTE.

/- TEXT:
Finally, we define division for the Gaussian integers
with the notation ``x / y``, that rounds the complex quotient to the nearest
Gaussian integer. We use our bespoke ``int.div'`` for that purpose.
As we calculated above, if ``x`` is :math:`a + bi` and ``y`` is :math:`c + di`,
then the real and imaginary parts of ``x / y`` are the nearest integers to

.. math::

  \frac{ac + bd}{c^2 + d^2} \quad \text{and} \quad \frac{bc -ad}{c^2+d^2},

respectively. Here the numerators are the real and imaginary parts of
:math:`(a + bi) (c - di)`, and the denominators are both equal to the norm
of :math:`c + di`.
BOTH: -/
-- QUOTE:
instance : has_div gaussint :=
⟨λ x y, ⟨int.div' (x * conj y).re (norm y), int.div' (x * conj y).im (norm y)⟩⟩
-- QUOTE.

/- TEXT:
Having defined ``x / y``, We define ``x % y`` to be the remainder,
``x - (x / y) * y``. As above, we record the definitions in the
theorems ``div_def`` and
``mod_def`` so that we can use them with ``simp`` and ``rewrite``.
BOTH: -/
-- QUOTE:
instance : has_mod gaussint := ⟨λ x y, x - y * (x / y)⟩

theorem div_def (x y : gaussint) :
  x / y = ⟨int.div' (x * conj y).re (norm y), int.div' (x * conj y).im (norm y)⟩ := rfl

theorem mod_def (x y : gaussint) : x % y = x - y * (x / y) := rfl
-- QUOTE.

/- TEXT:
These definitions immediately yield ``x = y * (x / y) + x % y`` for every
``x`` and ``y``, so all we need to do is show that the norm of ``x % y`` is
less than the norm of ``y`` when ``y`` is not zero.

We just defined the real and imaginary parts of ``x / y`` to be
``div' (x * conj y).re (norm y)`` and ``div' (x * conj y).im (norm y)``,
respectively.
Calculating, we have

  ``(x % y) * conj y = (x - x / y * y) * conj y = x * conj y - x * (y * conj j)``

The real and imaginary parts of the right-hand side are exactly ``mod' (x * conj y).re (norm y)`` and ``mod' (x * conj y).im (norm y)``.
By the properties of ``div'`` and ``mod'``,
these are guaranteed to be less than or equal to ``norm y / 2``.
So we have

  ``norm ((x % y) * conj y) ≤ (norm y / 2)^2 + (norm y / 2)^2 ≤ (norm y / 2) * norm y``.

On the other hand, we have

  ``norm ((x % y) * conj y) = norm (x % y) * norm (conj y) = norm (x % y) * norm y``.

Dividing through by ``norm y`` we have ``norm (x % y) ≤ (norm y) / 2 < norm y``,
as required.

This messy calculation is carried out in the next proof. We encourage you
to step through the details and see if you can find a nicer argument.
BOTH: -/
-- QUOTE:
lemma norm_mod_lt (x : gaussint) {y : gaussint} (hy : y ≠ 0) : (x % y).norm < y.norm :=
begin
  have norm_y_pos : 0 < norm y,
    by rwa [norm_pos],
  have : (x % y) * conj y =
    ⟨int.mod' (x * conj y).re (norm y), int.mod' (x * conj y).im (norm y)⟩,
  { rw [mod_def, sub_mul, int.mod'_eq, int.mod'_eq, sub_eq_add_neg, norm, div_def],
    ext; simp; ring },
  have : norm (x % y) * norm y ≤ (norm y / 2) * norm y,
  { conv { to_lhs, rw [←norm_conj y, ←norm_mul, this, norm] },
    simp,
    transitivity 2 * (y.norm / 2)^2,
    { rw [two_mul],
      apply add_le_add;
      { rw [sq_le_sq],
        apply le_trans (int.abs_mod'_le _ _ norm_y_pos),
        apply le_abs_self } },
      rw [pow_two, ←mul_assoc, mul_comm, mul_comm (2 : ℤ)],
      apply mul_le_mul_of_nonneg_left _ _,
      { apply int.div_mul_le, norm_num },
      apply int.div_nonneg (norm_nonneg y), norm_num },
  have : norm (x % y) ≤ norm y / 2 := le_of_mul_le_mul_right this norm_y_pos,
  apply lt_of_le_of_lt this,
  apply int.div_lt_of_lt_mul, { norm_num },
  linarith
end
-- QUOTE.

/- TEXT:
We are in the home stretch. Our ``norm`` function maps Gaussian integers to
nonnegative integers. We need a function that maps Gaussian integers to natural
numbers, and we obtain that by composing ``norm`` with the function
``int.nat_abs``, which maps integers to the natural numbers.
The first of the next two lemmas establishes that mapping the norm to the
natural numbers and back to the integers does not change the value.
The second one re-expresses the fact that the norm is decreasing.
BOTH: -/
-- QUOTE:
lemma coe_nat_abs_norm (x : gaussint) : (x.norm.nat_abs : ℤ) = x.norm :=
int.nat_abs_of_nonneg (norm_nonneg _)

lemma nat_abs_norm_mod_lt (x y : gaussint) (hy : y ≠ 0) :
  (x % y).norm.nat_abs < y.norm.nat_abs :=
begin
  apply int.coe_nat_lt.1, simp,
  exact int.nat_abs_lt_nat_abs_of_nonneg_of_lt (norm_nonneg _) (norm_mod_lt x hy)
end
-- QUOTE.

/- TEXT:
We also need to establish the second key property of the norm function
on a Euclidean domain.
BOTH: -/
-- QUOTE:
lemma not_norm_mul_left_lt_norm (x : gaussint) {y : gaussint} (hy : y ≠ 0) :
  ¬ (norm (x * y)).nat_abs < (norm x).nat_abs :=
begin
  apply not_lt_of_ge,
  rw [norm_mul, int.nat_abs_mul],
  apply le_mul_of_one_le_right (nat.zero_le _),
  apply int.coe_nat_le.1,
  rw [coe_nat_abs_norm],
  exact int.add_one_le_of_lt ((norm_pos _).mpr hy)
end
-- QUOTE.

/- TEXT:
We can now put it together to show that the Gaussian integers are an
instance of a Euclidean domain. We use the quotient and remainder function we
have defined.
The mathlib definition of a Euclidean domain is more general than the one
above in that it allows us to show that remainder decreases with respect
to any well-founded measure.
Comparing the values of a norm function that returns natural numbers is
just one instance of such a measure,
and in that case, the required properties are the theorems
``nat_abs_norm_mod_lt`` and ``not_norm_mul_left_lt_norm``.
BOTH: -/
-- QUOTE:
instance : euclidean_domain gaussint :=
{ quotient        := (/),
  remainder       := (%),
  quotient_mul_add_remainder_eq :=
                     λ x y, by {rw [mod_def, add_comm, sub_add_cancel] },
  quotient_zero   := λ x, by { simp [div_def, norm, int.div'], refl },
  r               := measure (int.nat_abs ∘ norm),
  r_well_founded  := measure_wf (int.nat_abs ∘ norm),
  remainder_lt    := nat_abs_norm_mod_lt,
  mul_left_not_lt := not_norm_mul_left_lt_norm,
  .. gaussint.comm_ring }
-- QUOTE.

/- TEXT:
An immediate payoff is that we now know that, in the Gaussian integers,
the notions of being prime and being irreducible coincide.
BOTH: -/
-- QUOTE:
example (x : gaussint) : irreducible x ↔ prime x :=
principal_ideal_ring.irreducible_iff_prime
-- QUOTE.

end gaussint


/-
Copyright (c) 2021 Alex J. Best. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Alex J. Best
-/
import number_theory.cyclotomic.basic
import ready_for_mathlib.homogenization

open_locale big_operators

open polynomial finset mv_polynomial

-- TODO might be nice to have an explicit two variable version of this factorization
-- in an mv_polynomial ring with two chosen variables

/-- If there is a primitive `n`th root of unity in `K`, then `X ^ n - Y ^ n = ∏ (X - μ Y)`,
where `μ` varies over the `n`-th roots of unity. -/
lemma pow_sub_pow_eq_prod_sub_zeta_runity_mul {K : Type*} [comm_ring K] [is_domain K] {ζ : K}
  {n : ℕ} (hpos : 0 < n) (h : is_primitive_root ζ n) (x y : K) :
  x ^ (n : ℕ) - y ^ (n : ℕ) = ∏ (ζ : K) in nth_roots_finset n K, (x - ζ * y) :=
begin
  -- suffices to show the identity in a multivariate polynomial ring with two generators over K
  suffices : (X 0 : mv_polynomial (fin 2) K) ^ (n : ℕ) - (X 1) ^ (n : ℕ) =
    ∏ ζ in nth_roots_finset n K, (X 0 - C ζ * X 1),
  { apply_fun (mv_polynomial.eval ((λ i : fin 2, if i = 0 then x else y))) at this,
    simpa [mv_polynomial.eval_prod] using this, },
  -- we know the univariate / one argument version of the identity
  have := X_pow_sub_one_eq_prod hpos h,
  -- transfer this to a polynomial ring with two variables
  have := congr_arg (aeval (X 0 : mv_polynomial (fin 2) K)) this,
  simp only [map_prod, aeval_X_pow, polynomial.aeval_X, aeval_one, polynomial.aeval_C,
    alg_hom.map_sub] at this,
  -- the homogenization of the identity is also true
  have := congr_arg (homogenization 1) this,
  -- simplify to get the result we want
  simpa [homogenization_prod, algebra_map_eq, hpos],
end

/-- If there is a primitive `n`th root of unity in `K` and `n` is odd, then
`X ^ n + Y ^ n = ∏ (X + μ Y)`, where `μ` varies over the `n`-th roots of unity. -/
lemma pow_add_pow_eq_prod_add_zeta_runity_mul {K : Type*} [comm_ring K] [is_domain K] {ζ : K}
  {n : ℕ} (hodd : n % 2 = 1) (h : is_primitive_root ζ n) (x y : K) :
  x ^ (n : ℕ) + y ^ (n : ℕ) = ∏ (ζ : K) in nth_roots_finset n K, (x + ζ * y) :=
begin
  have := pow_sub_pow_eq_prod_sub_zeta_runity_mul (nat.odd_iff.mpr hodd).pos h x (-y),
  simp only [mul_neg, sub_neg_eq_add] at this,
  rw [neg_pow, neg_one_pow_eq_pow_mod_two] at this,
  simpa [hodd] using this,
end


-- Derek Johnson

namespace hidden        -- you can ignore this

/-
1a. Define a polymorphic tree type. A tree of objects of type α
is either empty or it is a value of type α along with two
smaller trees of the same kind. You can call them left and right
respectively.
-/

inductive tree (α : Type) : Type
| empty : tree
| nonempty (a : α) (left : tree) (right : tree) : tree
/-
1b.
Define a polymorphic function, is_empty, that takes a value of
type, tree α, and that returns Boolean tt if it is empty and ff
otherwise.
-/
open tree
def is_empty {α : Type} : (tree α) → bool
| (tree.empty a) := tt
| _ := ff
def ee := tree.empty ℕ
#check is_empty ee
/-
1b. Define a polymorphic function, num_nodes, that takes a value
of type, tree α, and that returns the number of nodes in the
tree.
-/

def num_nodes {α : Type} : (tree α) → ℕ
| (tree.empty a) := 0
| (tree.nonempty a l r) := 1 + (num_nodes l) + (num_nodes r)

/-
2. A bit has one of two values. Imagine a type, let's call it
trit, with three values (e.g., true, false, and dont-know). How
many binary functions are there, taking two values of tihs type
and returning a value of the same type?
-/
 -- (3^1)^(3^2) = 19,683
/-
3. Define a function, sum_squares, that takes a natural number,
n, as an argument, and that returns the sum of the squaress of
all of the natural numbers from 0 to n, inclusive.
-/
open nat
def sum_squares : ℕ → ℕ
| 0 := 0
| (succ n) := ((n+1)^2) + sum_squares (n)

/-
4a. We represent a binary relation, R, on a set of  values of
some  type, α, as a predicate with two arguments. In Lean such a
predicate is of type α → α → Prop. A property of such a relation
is a predicate on a relation, and so is of type (α → α → Prop) →
Prop.

Here is the definition of a property of binary relations called
asymmetry. A relation is said to be asymmetric if whenever (x, y)
is in the relation, (y, x) is not. For example, the less-than
relation on natural numbers is asymmetric.
-/

def asymmetric {α : Type} (R : α → α → Prop) : Prop :=
    ∀ (x y : α), R x y → ¬ R y x

/-
Similarly, a relation is said to be irreflexive if for all x,
(x, x) is not in the relation. For example, an "is-unequal-to"
relation on the natural numbers would include (3, 4) because
3 is unequal to 4, but it would not include (3, 3) because
3 is not unqual to 3. Write a formal definition of irreflexive
in the style of the definition of asymmetric above. Start the
actual definition (after the :=) with ∀ (x : α).
-/

def irreflexive {α : Type} (R : α → α → Prop) : Prop :=
    ∀ (x : α), ¬ R x x

/-
4b. Prove that if a relation is asymmetric then it is
irreflexive by completing the following proof.
-/

theorem asy_imp_irr :
    ∀ (α : Type) (F : α → α → Prop),
        asymmetric F → irreflexive F :=
begin
assume α : Type,
assume F,
unfold asymmetric irreflexive,
assume asym,
assume x,
assume fxx,
have bbb := asym x x,
have ll := bbb fxx,
contradiction,
--exact ll fxx,
end

example :
    ∀ (α : Type) (F : α → α → Prop),
        asymmetric F → irreflexive F :=
begin
    assume α,
    assume F,
    unfold asymmetric irreflexive,
    assume asym,
    assume x,
    assume reflex,
    have contra := asym x x,
    have contra2 := contra reflex,
    apply contra2 reflex,
end

/-
5. Prove the following. You may use a tactic script,
as indicated here, or switch to another format for your
proof. Whatever you are most comfortable with is fine.
-/

example :
    ∀ (P Q R : Prop),
        (∀ (p : P), Q) → (∀ (q :Q), R) → (P ∨ Q) → R :=
begin
    intros,
    cases a_2,
    apply a_1 (a a_2),
    apply a_1 a_2,
end

/-
6. Prove that every natural number, n, has a successor.
Formalize this statement using universal and existential
quantifiers: for every natural number n there exists a
natural number m such that m is the successor of n. Then
complete the proof. Finish what we've started for you.
-/

example : ∀ (n : ℕ), ∃(m : ℕ), m = succ n  :=
    begin
        assume n,
        apply exists.intro (n+1),
        refl,
    end

/-
7. Consider a binary relation, squares, on the natural
numbers. A pair, (x, y), is in this relation if and only
if y=x^2. Formalize this relation in Lean as a predicate,
squares, with two natural number arguments and one proof
constructor called intro. Then state and prove the simple
proposition that the pair, (7, 49), is in this relation.
-/

-- Answer here
inductive squares : ℕ → ℕ → Prop
| intros : ∀(x y : ℕ), x^2=y → squares x y

open squares
theorem  squaresEx: squares 7 49 :=
    begin
        apply intros 7 49,
        apply eq.refl (7^2),
    end

/-
8. This question asks you to model a little world.
The world has people in it (a type) and there is a
binary relation, parent_of, on people. What is means
for a pair (x, y) to be in this relation is that x
is a parent of y.
-/

axiom Person : Type
axiom ParentOf : Person → Person → Prop

/-
Define a GrandParentOf relation, such that (x, y) is
in this relation if and only if x is a grand parent of
y. What this means, of course, is that there is some z
such that x is the parent of z and z is the parent of y.
Define the GrandParentOf relation using an inductive
type definition in the usual way. It needs only one
constructor, which must enforce the condition that
defines what it means "to be a grandparent of".
-/
inductive GrandParentOf : Person → Person → Prop
| const : ∀(x y  : Person), (∃(z : Person),(ParentOf x z) ∧ (ParentOf z y)) → GrandParentOf x y
/-
9. In simple English explain what it means for a binary
relation on a set to be

* reflexive: Every value equals itself. In variables, x = x
* transitive: Results from seperate operations can be combined to form
              extended results. In variables, if x = y and y = z, x = z
* symmetric: It does not matter what order two values are compared,
             they will return the same value. In variables, if x = y
             then y = x.

Give an example of an everyday mathematical relation
other than equality that is transitive.
 Greater-than
Is the greater-than relation reflexive? Explain.
    No, greater than implies that one value is larger than the other,
    something that can not happen when the two values are the same.
Is it symmetric? Explain.
    No, greater-than requires ordering and the symmetric property disregards
    ordering. For example 2 is greater than 1 but 1 is not greater than 2.
-/

/-
10. Give a natural language proof, in your own words,
showing that the square root of two is irrational. You
may find the details of this proof easily by searching
online.
-/
 /-
If 2^(1/2) were rational we would be able to express it in a fractional form
b/a in which a and b are relatively prime numbers. We can then extend the equation to
    2 = (b^2)/(a^2)
From here we can simplify again
    2(a^2) = b^2
We know now that since it is multiplied by 2, 2(a^2) will be even and therefore,
b^2 will be even as well. Therefore b can be written as 2c resulting in the equatino
    2(a^2) = 4c^2
    (a^2) = 2c^2
Since 2c^2 is even, a^2 is even, and since a^2 is even, so is a.
However, two even numbers cannot be relatively prime because 2 would be a common
multiple!
Because of this 2^(1/2) can not be expressed as a rational fraction
Hence, 2^(1/2) is irrational
 -/
/-
11. Prove formally that 0 ≠ 1, then translate your
formal proof, step by step, into an English language
proof, citing the reasoning principles that you use.
What fundamental proof strategy is centrally involved
in this proof?
-/
example : 0 ≠ 1 :=
    begin
        assume h : 0=1,
        cases h,
    end
/-
    In order to prove that 0 ≠ 1, we are really proving that the
    Proposition (0 ≠ 1) → false. To prove this, we first need a
    proof of 0 =1. We then can prove then derive a proof of false
    for every case. This is where the cases command is applied.
    In this instance, there are no cases of h because there is no
    way to prove equality with two distint values. With no cases left
    to prove, the overall proof is done.
-/
/-
12. Lean includes the law of the excluded middle in a
closed namespace called classical. The axiom goes
by the name, classical.em. You may apply it to any
proposition, P, to obtain a proof of P ∨ ¬ P. Use
the law of the excluded middle to show that proof
by contradiction is valid, as long as you accept
em as an axiom. Hint: We covered this in class.
-/
theorem by_contradiction: ∀(P : Prop), ¬ ¬ P → P :=
    begin
        assume P,
        cases (classical.em P),
            assume nnp,
            exact h,
            assume nnp,
            contradiction
    end
/-
13. Let's define a natural number to be cool if it's
either 2 or 5 or the sum or the product of two
cool numbers. Formalize this definition in Lean
and then state and prove the proposition that 35
is cool.
-/
inductive cool : ℕ → Prop
| cool2 : cool 2
| cool5 : cool 5
| coolSum : ∀(x y: ℕ), cool x → cool y → cool (x + y)
| coolMult : ∀(x y: ℕ), cool x → cool y → cool (x * y)
open cool
example : cool 35 :=
    begin
        apply coolMult 7 5,
        apply coolSum 5 2,
        exact cool5,
        exact cool2,
        exact cool5,
    end

end hidden


import BrownCs22.Library.Tactics
import BrownCs22.Library.Defs

/-

# First order logic in Lean

We were a little vague in class about what function symbols, predicate symbols,
etc. we were going to allow in our formulas.
In Lean, we need to be more precise.

Symbols in Lean must have *types*:
a predicate holds on `ℕ`, or on `ℤ`, or on some arbitrary type `T`,
but not on all at once.

We can introduce a new predicate symbol that takes one natural number argument
as follows:

-/

variable (P : ℕ → Prop)

#check P 3

/-

Notice that we don't need to write parentheses around the argument.
We can, though:

-/

#check P (3)

/-

If we want a predicate symbol that takes two arguments:

-/

variable (R : ℕ → ℕ → Prop)

#check R 3 4

-- this doesn't work!
-- #check R (3, 4)

/-

Finally, Lean has lots of predicates and function symbols built in.
Want to use `+`, `*`, `=`, all that familiar stuff? It's here already.

-/

/-

## Universal introduction

We said that to prove `∀ x : T, ...` goals, we got to introduce a new
`x : T`. The tactic for this proof rule is familiar. The pattern here
sounds a lot like implication: move something from the goal to the context.

We use the tactic `intro` again!

The `reflexivity` tactic will prove things equal to themselves.
This is a very basic property of `=`.

-/

example : ∀ x : ℤ, x = x := by
  intro x
  reflexivity

 -- Every even integer greater than two is the sum of two primes.
example : ∀ n : ℕ, Even n ∧ (n > 2) → ∃p q : ℕ, Prime p ∧ Prime q ∧ (n = p + q) := by
  intro n
  intro h_and
  sorry

/-

## Universal elimination

If we know `hall : ∀ x : T, H x`, we can conclude `H t` for any `t : T`.
We again do this just like implication elimination:
`have ht : H t := hall t`.

We can also use `apply`.

Note: I've started this example with the hypothesis `hxp` "pre-intro'ed."
Putting it left of the `:` means it is in my context at the beginning.
This saves us a line.

-/

example (hxp : ∀ x : ℕ, P x) : P 5 ∧ P 6 := by
  split_goal

  -- method 1: using `have`
  { have hP5 : P 5 := hxp 5
    assumption }

  -- method 2: using apply
  { apply hxp }

/-

## Existential introduction

To prove an `exists` goal, we provide a witness.
The tactic for giving a witness is `existsi` (for "exists introduction," creatively).

The tactic `numbers` proves silly obvious facts about explicit numbers.
(It doesn't know anything about variables, like `x`.)

-/

example : ∃ x : ℕ, x > 10 := by
  existsi 1000
  numbers

/-

## Existential elimination

This will be familiar again!
To use an `exists` hypothesis, we can use the good old `eliminate` tactic
to get a witness and a hypothesis about that witness.

-/

example (hex : ∃ x : ℕ, P x) : 0 = 0 := by
  eliminate hex with k hPk
  numbers

example (hex : ∃ x : ℕ, P x) (hall : ∀ x : ℕ, P x → P (x + 1)) :
    ∃ x : ℕ, P x ∧ P (x + 1) := by
  eliminate hex with k hPk
  have hk : P k → P (k + 1) := hall k
  have hPk1 : P (k + 1) := hk hPk
  existsi k
  split_goal
  { assumption }
  { assumption }


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,
  calc 0 ≤ (a - b)*c : mul_nonneg_of_nonpos_of_nonpos (by rwa sub_nonpos) hc
  ... = a*c - b*c : by ring,
end

/- Using forward reasoning -/
-- 0014
example (a b c : ℝ) (hc : c ≤ 0) (hab :  a ≤ b) : b*c ≤ a*c :=
begin
  have key₁ : a - b ≤ 0,
  { rwa sub_nonpos },
  have key₂ : 0 ≤ (a - b)*c,
  { exact mul_nonneg_of_nonpos_of_nonpos key₁ hc },
  have key₃ : (a - b)*c = a*c - b*c,
  { ring },
  rw key₃ at key₂,
  rwa ← sub_nonneg,
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
  -- whoops, I'll do the backward one here
  rw ← sub_nonneg,
  have key : 0 ≤(a - b)*c,
  apply mul_nonneg_of_nonpos_of_nonpos,
  { rwa sub_nonpos},
  exact hc,
  have key₂ : (a - b)* c = a*c - b*c,
  { ring },
  rwa ← key₂,
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
  intros ha,
  cases ha with hap haq,
  split,
  exact haq,
  exact hap,
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 a b c,
  exact a ⟨b, c⟩, },
  { rintros hyp ⟨a, b⟩,
  exact hyp a b, },
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
  have key : gcd a b ∣ a ∧ gcd a b ∣ b,
  {rw ← dvd_gcd_iff, },
  split,
  intro hyp,
  apply dvd_antisymm,
  exact key.left,
  rwa dvd_gcd_iff,
  exact ⟨dvd_refl a, hyp⟩,
  intro hyp,
  rw hyp at key,
  exact key.right,
end


/- Axiom : not_iff_imp_false (P : Prop) :
¬ P ↔ P → false
-/

/-

By definition `¬Q` is equivalent to `Q → false`. Lets call this result

`not_iff_imp_false (P : Prop) : ¬ P ↔ (P → false)`

-/
lemma not_iff_imp_false (P : Prop) : ¬ P ↔ P → false :=iff.rfl-- hide
/-

You can convince yourself this is true by writting out the truth table for `¬ P` and `P → false`.

In order to use it, lets look at a new tactic.

## The `rw` tactic

The <mark style ="background-color : #ebdef0 ">`rw`</mark> tactic (short for <mark style ="background-color : #ebdef0 ">`rewrite`</mark>)
is a tactic that allows you to do a substitution. For example, if we have the
assumption `h : P ↔ Q`, then `rw h` will replace the (first) `P` in the goal with `Q`. If instead you would like to
turn the first `Q` in your goal to `P` then use `rw ←h`. (To get the `←` arrow, type `\l`).

Combining `rw` and `not_iff_imp_false`, try to prove the following:

-/

/- Hint : Hint
 Start by using `rw not_iff_imp_false,` and see what changes.
-/

/- Lemma
If $P$ and $Q$ are propositions, and $P\implies Q$, then
$\lnot Q\implies \lnot P$.
-/
lemma contrapositive (P Q : Prop) : (P → Q) → (¬ Q → ¬ P) :=
begin
  rw not_iff_imp_false,
  rw not_iff_imp_false,
  intro f,
  intro h,
  intro p,
  apply h,
  apply f,
  exact p,

end

/-Hint : Tip
If you want to use the `rw` tactic on one of your assumptions then this can be done.

For example, if we have `h1 : P ↔ Q` and `h2` is the assumption we want to change, then
 `rw h1 at h2` will substitute the first `P` in `h2` to `Q`.

-/

/- Tactic : rw

The `rw` tactic is a tactic that allows you to substitute in things. For example, if we have the
assumption `h: P ↔ Q`, then `rw h` will replace the `P`'s in the goal with `Q`'s. If instead you would like to
turn all of the `Q`'s in your goal to `P`'s then using `rw ←h` will do this. (To get the `←` arrow, type `\l`).

Moreover, if we have `h1 : P ↔ Q` and `h2` is the assumption we want to change, then
 `rw h1 at h2` will substitute the first `P` in `h2` to `Q`.

-/


/-
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.list.range

/-!
# Antidiagonals in ℕ × ℕ as lists

This file defines the antidiagonals of ℕ × ℕ as lists: the `n`-th antidiagonal is the list 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

Files `data.multiset.nat_antidiagonal` and `data.finset.nat_antidiagonal` successively turn the
`list` definition we have here into `multiset` and `finset`.
-/

open list function nat

namespace list
namespace nat

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

/-- 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 :=
begin
  rw [antidiagonal, mem_map], split,
  { rintros ⟨i, hi, rfl⟩, rw [mem_range, lt_succ_iff] at hi, exact add_tsub_cancel_of_le hi },
  { rintro rfl, refine ⟨x.fst, _, _⟩,
    { rw [mem_range, add_assoc, lt_add_iff_pos_right], exact zero_lt_succ _ },
    { exact prod.ext rfl (add_tsub_cancel_left _ _) } }
end

/-- The length of the antidiagonal of `n` is `n + 1`. -/
@[simp] lemma length_antidiagonal (n : ℕ) : (antidiagonal n).length = n+1 :=
by rw [antidiagonal, length_map, length_range]

/-- 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. -/
lemma nodup_antidiagonal (n : ℕ) : nodup (antidiagonal n) :=
nodup_map (@left_inverse.injective ℕ (ℕ × ℕ) prod.fst (λ i, (i, n-i)) $ λ i, rfl) (nodup_range _)

@[simp] lemma antidiagonal_succ {n : ℕ} :
  antidiagonal (n + 1) = (0, n + 1) :: ((antidiagonal n).map (prod.map nat.succ id) ) :=
begin
  simp only [antidiagonal, range_succ_eq_map, map_cons, true_and, nat.add_succ_sub_one, add_zero,
    id.def, eq_self_iff_true, tsub_zero, map_map, prod.map_mk],
  apply congr (congr rfl _) rfl,
  ext; simp,
end

end nat
end list


/-
Copyright (c) 2022 Floris van Doorn. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Floris van Doorn, Patrick Massot
-/
import topology.basic
/-!
# Neighborhoods of a set

In this file we define the filter `𝓝ˢ s` or `nhds_set s` consisting of all neighborhoods of a set
`s`.

## Main Properties

There are a couple different notions equivalent to `s ∈ 𝓝ˢ t`:
* `s ⊆ interior t` using `subset_interior_iff_mem_nhds_set`
* `∀ (x : α), x ∈ t → s ∈ 𝓝 x` using `mem_nhds_set_iff_forall`
* `∃ U : set α, is_open U ∧ t ⊆ U ∧ U ⊆ s` using `mem_nhds_set_iff_exists`

Furthermore, we have the following results:
* `monotone_nhds_set`: `𝓝ˢ` is monotone
* In T₁-spaces, `𝓝ˢ`is strictly monotone and hence injective:
  `strict_mono_nhds_set`/`injective_nhds_set`. These results are in `topology.separation`.
-/

open set filter
open_locale topological_space

variables {α : Type*} [topological_space α] {s t s₁ s₂ t₁ t₂ : set α} {x : α}

/-- The filter of neighborhoods of a set in a topological space. -/
def nhds_set (s : set α) : filter α :=
Sup (nhds '' s)

localized "notation `𝓝ˢ` := nhds_set" in topological_space

lemma mem_nhds_set_iff_forall : s ∈ 𝓝ˢ t ↔ ∀ (x : α), x ∈ t → s ∈ 𝓝 x :=
by simp_rw [nhds_set, filter.mem_Sup, ball_image_iff]

lemma subset_interior_iff_mem_nhds_set : s ⊆ interior t ↔ t ∈ 𝓝ˢ s :=
by simp_rw [mem_nhds_set_iff_forall, subset_interior_iff_nhds]

lemma mem_nhds_set_iff_exists : s ∈ 𝓝ˢ t ↔ ∃ U : set α, is_open U ∧ t ⊆ U ∧ U ⊆ s :=
by { rw [← subset_interior_iff_mem_nhds_set, subset_interior_iff] }

lemma has_basis_nhds_set (s : set α) : (𝓝ˢ s).has_basis (λ U, is_open U ∧ s ⊆ U) (λ U, U) :=
⟨λ t, by simp [mem_nhds_set_iff_exists, and_assoc]⟩

lemma is_open.mem_nhds_set (hU : is_open s) : s ∈ 𝓝ˢ t ↔ t ⊆ s :=
by rw [← subset_interior_iff_mem_nhds_set, interior_eq_iff_open.mpr hU]

@[simp] lemma nhds_set_singleton : 𝓝ˢ {x} = 𝓝 x :=
by { ext,
     rw [← subset_interior_iff_mem_nhds_set, ← mem_interior_iff_mem_nhds, singleton_subset_iff] }

lemma mem_nhds_set_interior : s ∈ 𝓝ˢ (interior s) :=
subset_interior_iff_mem_nhds_set.mp subset.rfl

lemma mem_nhds_set_empty : s ∈ 𝓝ˢ (∅ : set α) :=
subset_interior_iff_mem_nhds_set.mp $ empty_subset _

@[simp] lemma nhds_set_empty : 𝓝ˢ (∅ : set α) = ⊥ :=
by { ext, simp [mem_nhds_set_empty] }

@[simp] lemma nhds_set_univ : 𝓝ˢ (univ : set α) = ⊤ :=
by { ext, rw [← subset_interior_iff_mem_nhds_set, univ_subset_iff, interior_eq_univ, mem_top] }

lemma monotone_nhds_set : monotone (𝓝ˢ : set α → filter α) :=
λ s t hst, Sup_le_Sup $ image_subset _ hst

@[simp] lemma nhds_set_union (s t : set α) : 𝓝ˢ (s ∪ t) = 𝓝ˢ s ⊔ 𝓝ˢ t :=
by simp only [nhds_set, image_union, Sup_union]

lemma union_mem_nhds_set (h₁ : s₁ ∈ 𝓝ˢ t₁) (h₂ : s₂ ∈ 𝓝ˢ t₂) : s₁ ∪ s₂ ∈ 𝓝ˢ (t₁ ∪ t₂) :=
by { rw nhds_set_union, exact union_mem_sup h₁ h₂ }


import Mathlib.Tactic.Contrapose

open Classical

-- Section 2.1 The Peano axioms

-- Axiom 2.1. 0 is a natural number.
-- Axiom 2.2. If n is a natural number, then n++ is also a natural number.
inductive nat : Type | zero : nat | succ : nat → nat

namespace nat

-- Definition 2.1.3.
def one : nat := succ nat.zero
def two : nat := succ one
def three : nat := succ two
def four : nat := succ three
def five : nat := succ four
def six : nat := succ five

-- Proposition 2.1.4. 3 is a natural number.
#check three

-- Axiom 2.3. 0 is not the successor of any natural number; i.e.,
-- we have n++ ̸= 0 for every natural number n.
axiom zero_not_succ : ∀ n : nat, zero ≠ succ n

-- Proposition 2.1.6. 4 is not equal to 0.
theorem four_not_zero : four ≠ zero :=
  have h1 : succ three = four := rfl
  have h2 : zero ≠ succ three := zero_not_succ three
  have h3 : zero ≠ four := h1 ▸ h2
  show four ≠ zero from Ne.symm h3

-- Axiom 2.4. Different natural numbers must have
-- different successors; i.e., if n, m are natural
-- numbers and n ≠ m, then n++ ≠ m++. Equivalently,
-- if n++ = m++, then we must have n = m.
axiom succ_inj : ∀ n : nat, ∀ m : nat, n ≠ m → succ n ≠ succ m

-- Proposition 2.1.8. 6 is not equal to 2.
theorem six_not_two : six ≠ two :=
    have h1 : four ≠ zero := four_not_zero
    have h2 : succ four ≠ succ zero := succ_inj four zero h1
    have h3 : succ (succ four) ≠ succ (succ zero) := succ_inj (succ four) (succ zero) h2
    show six ≠ two from h3

-- Axiom 2.5 (Principle of mathematical induction). Let
-- P(n) be any property pertaining to a natural number n.
-- Suppose that P(0) is true, and suppose that whenever
-- P(n) is true, P(n++) is also true. Then P(n) is true
-- for every natural number n.
axiom induction : ∀ P : nat → Prop, P zero → (∀ n : nat, P n → P (succ n)) → ∀ n : nat, P n

-- Remark: It appears Axiom 2.5 is unneded for Lean.

-- Section 2.2 Addition
def add : nat → nat → nat
| zero, m => m
| succ n, m => succ (add n m)

-- Get to use the plus operator
instance : Add nat where add := add

-- Lemma 2.2.2. For any natural number n, n + 0 = n.
theorem add_zero (n : nat) : add n zero = n := by
  induction n with
  | zero => rfl
  | succ n ih => rw [add, ih]

-- Lemma 2.2.3. For any natural numbers n and m,
-- n + (m++) = (n + m)++.
theorem add_succ (n m : nat) : n + (succ m) = succ (n + m) := by
  induction n with
  | zero => rfl
  | succ n ih =>
    have h1 : succ n + succ m = succ (n + succ m) := rfl
    repeat rw [h1, ih]
    rfl

-- Proposition 2.2.4 (Addition is commutative). For any natural
-- numbers n and m, n + m = m + n.
theorem add_comm (n m : nat) : n + m = m + n := by
  induction n with
  | zero =>
      have h1 : zero + m = m := by rfl
      have h2 : m + zero = m := by apply add_zero
      rw [h1, h2]
  | succ n ih =>
      have h1 : succ n + m = succ (n + m) := by rfl
      have h2 : m + succ n = succ (m + n) := by apply add_succ
      rw [h1, ih, h2]

-- Proposition 2.2.5 (Addition is associative). For any natural
-- numbers a,b,c, we have (a+b)+c=a+(b+c).
theorem add_assoc (a b c : nat) : (a + b) + c = a + (b + c) := by
  induction a with
  | zero =>
      have h1 : (zero + b) + c = b + c := by rfl
      have h2 : zero + (b + c) =  b + c := by rfl
      rw [h1, h2]
  | succ a ih =>
      have h1 : succ a + (b + c) = succ (a + (b + c)) := by rfl
      have h2 : succ a + b + c = succ ((a + b) + c) := by rfl
      rw [h2, ih, h1]

-- Proposition 2.2.6 (Cancellation law). Let a,b,c be natural
-- numbers such that a+b=a+c. Then we have b=c.
theorem cancellation_law (a b c : nat) : (a + b) = (a + c) → b = c := by
  intro h
  induction a with
  | zero =>
    have h1 : b = zero + b := by rfl
    have h2 : c = zero + c := by rfl
    rw [h1, h2]
    exact h
  | succ a ih =>
    have h1 : succ a + b = succ (a + b) := by rfl
    have h2 : succ a + c = succ (a + c) := by rfl
    rw [h1,h2] at h
    apply ih
    contrapose h
    apply succ_inj
    exact h

end nat


import data.real.basic

/-Lean is a programming language used for proof formalisation,
meaning the program checks that a given proof is actually correct.
Here, we will give a brief summary of how to use Lean. We will then
move onto proving some results requiring the uses of axioms for the real
numbers -/

/-We defined the axioms of addition for the real numbers as follows
(A.1) Commutativity : a + b = b + a, ∀ a,b ∈ ℝ
(A.2) Associativity : (a + b) + c = a + (b + c), ∀ a, b, c ∈ ℝ
(A.3) Existence of 0 : ∃ (0∈ℝ) such that 0 + a = a
(A.4) Existence of the negative : ∀ a∈ℝ, ∃x∈ℝ such that a + x = 0-/

/-Our axioms of multiplication for the real numbers are the following
(M.1) Commutativity : a*b = b*a for all a, b ∈ ℝ.
(M.2) Associativity : (a * b) * c = a * (b * c) for all a, b, c ∈ ℝ.
(M.3) Existence of 1 : There is a real number 1 ̸= 0 with 1 * a = a for all a ∈ ℝ.
(M.4) Existence of the inverse : ∀ a ∈ R \ {0} there is a unique x ∈ ℝ with a * x = 1.

-/

/-We also have distributivity
(D.1) Distributivity : a * (b + c) = (a * b) + (a * c) for all a, b, c ∈ ℝ.
-/

/-When using Lean, if we want to use a theorem involving an equals sign,
we use the rw tactic to tell Lean our intention. This will apply the said
theorem at all locations it can. If you only want to use it at a certain
location, you can use nth_rewrite x, where x is a non-negative integer
and specifies the location to apply the theorem
When we want to use a theorem with an implication, you should use the apply
tactic

We now provide a few examples on how to use Lean
-/

example (a : ℝ) : 0 + a + 0 = a:=
begin
rw add_zero,
rw zero_add
end

/-In thenext example we use a backward arrow ← (type \l )to use the
backward version of the equality
-/

example (a b c : ℝ): a+c+b = a + b + c:=
begin
rw add_assoc,
rw add_comm c b,
rw ← add_assoc,
end

/-When we specify where to change a statement with nth_rewrite, the first
position starts from 0-/

example (a b : ℝ):  a*1 + 1*a   =  a*1 + a*1 :=
begin
nth_rewrite 3 mul_comm,
end

example (a b : ℝ):  a*1 + 1*a   =  a*1 + a*1 :=
begin
nth_rewrite 1 ← mul_comm,
end

theorem neg_sub_pos_self1 : (-1 : ℝ) + 1 = 0 :=
begin
exact neg_add_self 1,
end

/-Now we've seen some examples, here are some theorems that can be
used to solve the next problem. Remove the 'sorry' to continue the
proof. Only rw with the following theorems is needed to close the goal.
-/

#check (left_distrib : ∀ (a b c : ℝ), a * (b + c) = (a * b) + (a * c))
#check (mul_comm : ∀ (a b : ℝ) , a * b = b * a)
#check (one_mul : ∀ a : ℝ , 1 * a = a)

example:  2 * 2 = 4 :=
begin
nth_rewrite 0 ← one_add_one_eq_two,
nth_rewrite 0 ← one_add_one_eq_two,
sorry
end

/-A list of additional theorems needed for the next question are listed below.
You may have to use ←left_distrib at some point-/

#check (neg_sub_pos_self1 : -1 + 1 = 0)
#check (mul_zero : ∀ a :ℝ, a * 0 = 0)

theorem neg_one_a_inv (a b:ℝ): -a = (-1)*a:=
begin
suffices: (-1)*a +a = 0,
{apply eq.symm at this,
exact eq_neg_iff_add_eq_zero.mpr this,},
nth_rewrite 1 ←one_mul a,
sorry
end

/-For the final problem, you will have an additional goal after the
first of ℝ. This can be shown with exact a. Furthermore, after we specify
a theorem, we can specify the specific place we what it to occur. An example
can be seen in line 137. Another example would be if our goal read as
⊢ a * 1 + 1 * a = a * 1 + a * 1
then we could type
rw mul_comm 1 a,
to specify we want to use commutativity at 1*a
 We now list some additional
theorems. -/

#check (mul_assoc : ∀ (a b c:ℝ), a * b * c = a * (b * c))
#check (pow_two : ∀ a :ℝ, a ^ 2 = a * a)
#check (neg_one_mul : ∀ a :ℝ, (-1) * a = -a)
#check (neg_neg : ∀ a : ℝ, - - a = a)

example (a:ℝ): (-a)^2 = a^2:=
begin
rw pow_two,
rw neg_one_a_inv,
rw mul_assoc,
rw ← mul_assoc a,
rw mul_comm a (-1),
sorry
end


import data.real.basic
import game.order.level01

namespace xena -- hide

/-
# Chapter 2 : Order

## Level 2

This level invites you to work out a property of the absolute value.
In Lean the absolute value of $x$ is denoted by `abs x`.
-/

/- Hint : The definition of the absolute value in mathlib:
definition abs {α : Type u} [decidable_linear_ordered_add_comm_group α] (a : α) : α := max a (-a)
-/

/-
For ease of use, a notation can be wrapped around that definition as below.
-/

/- Hint : Cases, Cases, Cases!
-/

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

/- Lemma
For any two real numbers $a$ and $b$, we have that
$$|ab| = |a||b|$$.
-/
theorem abs_prod (a b : ℝ) : |a * b| = |a| * |b| :=
begin
    rcases lt_trichotomy a 0 with han | haz | hap,
    swap,
    have h : a * b = 0, norm_num,
    left,
    exact haz,
    rw h,
    rw haz,
    norm_num,
    rcases lt_trichotomy b 0 with hbn | hbz | hbp,
    swap,
    have h : a * b = 0, norm_num,
    right,
    exact hbz,
    rw h,
    rw hbz,
    norm_num,
    have h1 : 0 < a * b, exact mul_pos_of_neg_of_neg han hbn,
    have h2 : | a * b | = a * b, exact abs_of_pos h1,
    have h3 : | a | = - a, exact abs_of_neg han,
    have h4 : | b | = - b, exact abs_of_neg hbn,
    rw h2, rw h3, rw h4,
    norm_num,
    have h1 : a * b < 0, exact mul_neg_of_neg_of_pos han hbp,
    have h2 : | a * b | = - (a * b), exact abs_of_neg h1,
    have h3 : | a | = - a, exact abs_of_neg han,
    have h4 : | b | = b, exact abs_of_pos hbp,
    rw h2, rw h3, rw h4,
    norm_num,
    rcases lt_trichotomy b 0 with hbn | hbz | hbp,
    swap,
    have h : a * b = 0, norm_num,
    right,
    exact hbz,
    rw h,
    rw hbz,
    norm_num,
    have h1 : a * b < 0, exact mul_neg_of_pos_of_neg hap hbn,
    have h2 : | a * b | = - (a * b), exact abs_of_neg h1,
    have h3 : | a | = a, exact abs_of_pos hap,
    have h4 : | b | = - b, exact abs_of_neg hbn,
    rw [h2,h3,h4],
    norm_num,
    have h1 : 0 < a * b, exact mul_pos hap hbp,
    have h2 : | a * b | = a * b, exact abs_of_pos h1,
    have h3 : | a | = a, exact abs_of_pos hap,
    have h4 : | b | = b, exact abs_of_pos hbp,
    rw [h2,h3,h4],
end

end xena --hide


import tactic
open classical

def is_injective_not_used (X : Type*) (Y : Type*) (f : X → Y ) :=
    ∀ x:X, ∀ x' : X, f x = f x' → x = x'

-- A simpler definition uses implicit parameters!!!
-- This means we don't need to supply those when using the lang.
-- They will be inferred. However, we can supply those with @is_injective

def is_injective {X Y : Type*} (f : X → Y ) :=
    ∀ x x' : X, f x = f x' → x = x'

def is_surjective {X Y : Type*} (f : X → Y) :=
∀ y : Y, ∃ x : X, f x = y

def is_bijective {X Y : Type*} (f : X → Y) :=
is_injective f ∧ is_surjective f

lemma bij_inj {X Y : Type*} (f : X → Y)
    (f_bij : is_bijective f) :
    is_injective f := and.elim_left f_bij

lemma bij_surj {X Y : Type*} (f : X → Y)
    (f_bij : is_bijective f) :
    is_surjective f := and.elim_right f_bij

lemma surj_inj_bij
    {X Y : Type*}
    (f : X → Y)
    (f_surjective: is_surjective f)
    (f_injective: is_injective f)
    : is_bijective f := and.intro f_injective f_surjective

lemma inj_comp_injfirst
{X Y Z : Type*}
(f : X → Y)
(g : Y → Z)
(gf_injective : is_injective (g ∘ f))
: is_injective f :=
begin
assume x : X,
assume x' : X,
intro ante,
have anteg_xx' : (g ∘ f) x =  (g ∘ f) x',  from
calc  (g ∘ f) x  = g (f x)  : by simp
      ... = g (f x') : by simp[ante]
      ... = (g ∘ f ) x' : by simp,

show x = x', by {apply gf_injective, apply anteg_xx'}

end

lemma surj_comp_surjsecond
{X Y Z : Type*}
(f : X → Y)
(g : Y → Z)
(gf_surjective : is_surjective (g ∘ f))
: is_surjective g
:=
begin
assume z : Z,
-- I have to introduce an x: X s.t. gfx = z,
-- this should follow from gf_surjective
-- Thus, I open the def of gf_surjective with rcases
-- While doing so I stablish that I'm using x:X and gfxz with it TODO: grokk
rcases gf_surjective z  with ⟨x:X, gfxz : (g ∘ f) x = z  ⟩ ,
let y:Y := f x, -- had never used let
use y, -- not sure what use is doing here !!!
show g y = z , from
calc g y = g ( f x) : by simp
     ... = (g ∘ f) x : by simp
     ... = z  : by simp[gfxz], -- or as she did, with by exact gfxz
end

-- Her explanation below !!
/- in order to prove the inheritance of surjectivity, we construct the
desired ∀-statement. Surjectivity of the composition gives us the existence
of a preimage for the composition. To extract such a preimage from the ∃-
statement, we can use rcases to eliminate the quantifier and extract a witness
(whose defining property is named gf_x_z). Through let, we introduce a
new name y for the term f x (which will serve as the desired preimage for x
under g). To build the claimed ∃-statement, it suffices to give one suitable
example; this is introduced via use. Finally, a small calculation finishes the
proof by showing that y has the correct properties. -/

lemma square_bij_bij
{X : Type*}
(f : X → X)
(ff_bijective : is_bijective (f ∘ f))
: is_bijective f
:=
begin
    have f_injective : is_injective f,  from
-- inj_comp_injfirst should be used here
        begin
            have ff_injective : is_injective (f ∘ f) :=
                 (bij_inj (f ∘ f) ff_bijective),
            exact inj_comp_injfirst f f ff_injective, -- here it is
        end,
    have f_surjective : is_surjective f,  from
-- surj_comp_surjsecond should be used here
        begin
            have ff_surjective : is_surjective (f ∘ f) :=
                (bij_surj (f ∘ f) ff_bijective),
            exact surj_comp_surjsecond f f ff_surjective, -- here it is
        end,
    show is_bijective f, by exact and.intro f_injective f_surjective,
end

-- TODO : differentiate the have  ..., from
-- From the have ..., by

/-- I've just copied the ones below  from https://gitlab.com/polywuisch/exploring-formalisation
# Examples
-/

/- The map {1,2,3} -> {1,2,3},
   1 -> 1, 2 -> 1, 3 -> 2
   is neither injective nor surjective -/
inductive A : Type
| A_1
| A_2
| A_3

def f
  : A → A
| A.A_1 := A.A_1
| A.A_2 := A.A_1
| A.A_3 := A.A_2

lemma not_inj_f
    : ¬ is_injective f
:=
begin
  -- idea: f A_1 = f A_2, even though A_1 ≠ A_2
  let x  : A := A.A_1,
  let x' : A := A.A_2,

  -- x and x' are witnesses for non-injectivity:
  have f_xx'_x_neq_x' : f x = f x' ∧ x ≠ x', from
  begin
    have f_xx' : f x  = f x',
         by simp[f],
    have x_neq_x' :  x ≠ x',
         by finish,

    show _, by exact and.intro f_xx' x_neq_x',
  end,

  -- we simplify (and thus move the negation into the expression),
  -- use x and x' as examples for the existential quantifier,
  -- and then conclude via f_xx'_x_neg_x'
  show ¬ is_injective f, from
  begin
    simp only [is_injective, not_forall],
    use x,
    use x',
    show _, by exact f_xx'_x_neq_x',
  end
end

lemma not_surj_f
    : ¬ is_surjective f
:=
begin
  -- we first move the negation through the all-quantifier
  simp only[is_surjective, not_forall],
  show ∃ y : A, ¬(∃ x : A, f x = y), by
  begin
    -- we show that A_3 does not lie in the image
    use A.A_3,
    have A3_not_in_im : ∀ x : A, ¬ f x = A.A_3, from
    begin
      assume x : A,
      -- we now just consider all three cases
      cases x,
        case A.A_1 : {simp[f]}, -- alternatively: {finish}
        case A.A_2 : {simp[f]},
        case A.A_3 : {simp[f]},
    end,
    show _,
         by {simp only[not_exists], exact A3_not_in_im}
  end
end

-- TODO: take a look at only and finish, didn't know about those
-- also, not_forall

inductive mnat : Type
| zero  : mnat
| succ : mnat → mnat

def geometric_sum : nat → nat
| 0 := 1
| (nat.succ n) := geometric_sum n + 2^(n+1)

lemma geommetric_sum_eval (n : nat) : geometric_sum n = 2^(n+1) - 1:=
 begin
     induction n with ivar ihyp,
        case nat.zero  : {simp[geometric_sum]},
        case nat.succ :
            begin
             calc geometric_sum (ivar + 1) = geometric_sum ivar + 2^(ivar+1) : by simp[geometric_sum]
                                       ... = 2^(ivar + 1) - 1 + 2^(ivar+1)  : by simp[ihyp]
                                       ... = 2^(ivar+1) + 2^(ivar+1) - 1 : by omega
                                       ... = 2^(ivar+2) -1 : by ring_nf,

            end
end
-- tthis omega tactic is fishy. She just put it in there without further justification.;
-- Pay attention to where the comma goes. Only at the end of calc.

open finset -- range operator
open_locale big_operators -- ∑ notation

-- Definining the sum of 1 over 0..(n-1) n ∈ ℕ

def one_sum : ℕ → ℕ := λ n :ℕ, ∑ i:ℕ in range n,1 -- Take a look at sum and range, this is a subtle def

lemma one_sum_eval (n:ℕ) : one_sum n = n :=
begin
induction n with ivar ihyp,
    case nat.zero : begin simp[one_sum] end,
    case nat.succ :
        begin calc one_sum (ivar + 1) = ∑ i:ℕ in range (ivar + 1),1 : by simp[one_sum]
                      ... =  ivar + 1 : by simp, end ,
end

/-

VERY useful tip: tactic library_search can be used to search
mathlib for statements that can resolve a goal

 -/


/-
There are two key things we do with proofs.

(1) We build them
(2) We use them

In logic, the basic rules for building,
or constructing, proofs go by the name
of introduction rules. The basic rules
for using proofs are called elimination
rules.

Each *form* of proposition has its own
introduction and elimination rules. For
example, to build a proof of P ∧ Q, one
must use the introduction rule for and,
while using a proof of P ∧ Q relies on
the two elimination rules, which serve
to extract from a proof of P ∧ Q a proof
of P or a proof of Q.

In this class, we assume (P Q : Prop),
(T : Type), (x y : T) then summarize,
review,  and practice using the rules
to build and use proofs of propositions
of these forms:

- ∀ (p : P), Q  [Q usually a predicate]
- x = y
- P → Q
- P ∧ Q
- ∃ (x : P), Q  [Q usually a predicate]

A key to understanding how to build
proofs is that it's a top-down and
recursive process. For example, to
build a proof of ∀ (p : P), Q, we
use the introduction rule for ∀. It
tells us to assume that p is some
arbitrary but specific value of type
P, and then to prove Q for that p.
To do this, we recursively apply
our approach to building proofs to
build a proof of Q, but now within
a context in which we can use the
assumption that p is some specific
value of type P. We now repeat the
same process: (1) ask what form is
Q; (2) choose an introduction rule
for that form of proposition. Often
we need smaller proofs to apply the
introduction rules, and for this, we
use proofs we have already built or
assumed, using elimination rules as
necessary.
-/

/-
FORALL INTRODUCTION

Assume that you have an arbitrary but
specific value of the quantified type,
(p : P), then prove Q.

Example: Prove "all balls are green".

Step 0: Formalize proposition (whether
you are foralizing the proof or not).

∀ (b : Ball), Green b

Step 1: We start by assuming that
b0 is an arbitrary but specific
ball, and now all that remains to
be proved is Green b (that this b
in particular) is Green.

Formally: Green b0. Remaining goal.

Example: prove ∀ (n : ℕ), n = n.

Proof: We begin by supposing that
n0 is an arbitrary but specific
natural number. What remains to
be proved in this context is that
n0 = n0. [∀ intro].

To prove that n0 = n0 is trivial
by the axiom that tells us that
the equality relation is reflexive.
Thus we have proved the proposition.
[By reflexive property of equality.]
-/

theorem all_n_eq_n : ∀ (n : ℕ), n = n :=
    λ (n0 : ℕ),
        eq.refl n0

theorem all_n_eq_n' : ∀ (n : ℕ), n = n :=
begin
    assume (n0 : ℕ),
    exact eq.refl n0,
end

#check all_n_eq_n
#check all_n_eq_n'

example : ℕ := 5

theorem f :
    ∀ (n : ℕ), nat.pred (nat.succ n) = n :=
        λ (n : ℕ),
            (eq.refl n)
/- ∀ Elimination -/

#reduce f 3

/-
Example:

Prove that if every ball is green, then
any specific ball, b0, is green.

(∀ (b : Ball), Green b) → (∀ (b0 : Ball), Green b0)

See below. The elimination rule for forall is based
on the idea that we can *apply* a proof of a forall
to a specific value (an argument) to build proof
for that specific value.
-/

axioms (Ball : Type) (Green : Ball → Prop)

example : (∀ (b : Ball), Green b) → (∀ (b0 : Ball), Green b0) :=
begin
assume f,
assume (b0 : Ball),
exact (f b0),
end

/-
IMPLICATIONS

INTRODUCTION FOR →

Assume that we have a proof of the premise.
Then in that context, construct a proof of
the rest, the conclusion.

Example: Prove
(∀ (b : Ball, Green b)) → ((b0: Ball) → (Green b0))

We begin by assuming that every ball is green,
then in this context, all that remains to be
proved is that if b0 is ball then b0 is Green.

To prove this, we assume that b0 is some specific
ball, and in this context all that remains to be
proved is: b0 is Green. This is proved by the use
of forall eliminatio n. We apply our *assumed*
proof of ∀ (b: Ball), Green b, TO b0, to obtain
a proof that b0 is green.

QED

-/

example : (∀ (b : Ball), Green b) → ∀ (b0: Ball), Green b0 :=
    λ (h: (∀ (b : Ball), Green b)),
        λ (b0 : Ball),
            (h b0)

/-
Assume that we have proof, h, that every ball is green.
In this context what remains to be proved is that any
particular ball is green.

So now we assume that b0 is some arbitrary but specific
ball, and in this extend context of assumptions, all
that remains to be proved is that b0 is green.

But we already have a proof that every ball is green
so all we need to is to apply it to b0, and that
gives us a proof that b0 is green.

QED.

-/

theorem and_commutes : ∀ (P Q : Prop), P ∧ Q → Q ∧ P :=
begin
    intros P Q,
    assume (h : P ∧ Q),
    apply and.intro _ _,
    exact (and.elim_right h),
    exact (and.elim_left h)
end

theorem and_commutes' : ∀ (P Q : Prop), P ∧ Q → Q ∧ P :=
λ P Q,
    λ h,
        and.intro
            (and.elim_right h)
            (and.elim_left h)

namespace hidden

inductive nat : Type
| zero
| succ : nat → nat

def n0 := nat.zero
def n1 := nat.succ nat.zero
def n2 := nat.succ (nat.succ nat.zero)
def n3 := nat.succ (nat.succ (nat.succ nat.zero))
def n4 := nat.succ (nat.succ (nat.succ (nat.succ nat.zero)))

def inc (n : nat) : nat :=
    nat.succ n

def dec : nat → nat
| nat.zero := nat.zero
| (nat.succ n') := n'

end hidden


import Mathlib
/-!
# Welcome to the course

We start with a quick tour, where we:

* Use Lean as a calculator
* Define some functions and call them.
* Look at some types.
* Look at some proofs.

We will then see
* A glimpse of AI.
* A detailed example with programs and proofs.
-/

/-!
## Lean as a calculator.

We begin by using Lean as a calculator. We can use `#eval` to evaluate expressions.

```lean
#eval 1 + 2 -- 3

#eval "Hello " ++ "world!" -- "Hello world!"
```
-/
#eval 1 + 2

#eval "Hello " ++ "world!"

/-- An arbitrary number. -/
def some_number := 42

/-!
We next evaluate an expression involving a definition.

```lean
#eval some_number + 23 -- 65
```
-/

#eval some_number + 23

/-!
## Defining functions

We next define some functions. These are defined in terms of previously defined functions.
-/
/-- Add `2` to a natural number -/
def add_two (n : ℕ) : ℕ := n + 2

/-- Cube a natural number -/
def cube (n : ℕ) : ℕ := n * n * n

/-!
```lean
#eval cube (add_two 3) -- 125
```
-/
#eval cube (add_two 3)

/-- Cube a natural number -/
def cube' := fun (n : ℕ) ↦ n * n * n

/-- Cube a natural number -/
def cube'' : ℕ → ℕ := fun n ↦ n * n * n

example := λ (n : ℕ) => n * n * n

/-!
## Types

Terms in Lean, including functions, have types, which can be seen using `#check`

```lean
#check 1 + 2 -- ℕ

#check "Hello " ++ "world!" -- String

#check add_two -- ℕ → ℕ
#check cube   -- ℕ → ℕ

#check ℕ -- Type
#check Type -- Type 1
#check ℕ → ℕ -- Type
```
-/

#check 1 + 2

#check "Hello " ++ "world!"

#check add_two
#check cube

#check ℕ
#check Type
#check ℕ → ℕ

/-!
We next define a function of two arguments, and look at its type. We see that this is defined as a function from `ℕ` to a function from `ℕ` to `ℕ`.

-/
/-- Sum of squares of natural numbers `x` and `y` -/
def sum_of_squares (x y : ℕ) : ℕ := x * x + y * y

/-!
```lean
#check sum_of_squares -- ℕ → ℕ → ℕ

#check sum_of_squares 3 -- ℕ → ℕ
```

We can also define this in a way that makes the type clearer.
-/

#check sum_of_squares -- ℕ → (ℕ → ℕ)

#check sum_of_squares 3 -- ℕ → ℕ

/-- Sum of squares of natural numbers `x` and `y` -/
def sum_of_squares' : ℕ → ℕ → ℕ :=
  fun x ↦ fun y ↦  x * x + y * y


/-
Copyright (c) 2014 Jeremy Avigad. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jeremy Avigad, Leonardo de Moura

! This file was ported from Lean 3 source module data.nat.gcd.basic
! leanprover-community/mathlib commit a47cda9662ff3925c6df271090b5808adbca5b46
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Algebra.GroupPower.Basic
import Mathlib.Algebra.GroupWithZero.Divisibility
import Mathlib.Data.Nat.Order.Lemmas

/-!
# Definitions and properties of `Nat.gcd`, `Nat.lcm`, and `Nat.coprime`

Generalizations of these are provided in a later file as `GCDMonoid.gcd` and
`GCDMonoid.lcm`.

Note that the global `IsCoprime` is not a straightforward generalization of `Nat.coprime`, see
`Nat.isCoprime_iff_coprime` for the connection between the two.

-/

namespace Nat

/-! ### `gcd` -/

theorem gcd_greatest {a b d : ℕ} (hda : d ∣ a) (hdb : d ∣ b) (hd : ∀ e : ℕ, e ∣ a → e ∣ b → e ∣ d) :
    d = a.gcd b :=
  (dvd_antisymm (hd _ (gcd_dvd_left a b) (gcd_dvd_right a b)) (dvd_gcd hda hdb)).symm
#align nat.gcd_greatest Nat.gcd_greatest

-- Lemmas where one argument consists of addition of a multiple of the other
@[simp]
theorem gcd_add_mul_right_right (m n k : ℕ) : gcd m (n + k * m) = gcd m n := by
  simp [gcd_rec m (n + k * m), gcd_rec m n]
#align nat.gcd_add_mul_right_right Nat.gcd_add_mul_right_right

@[simp]
theorem gcd_add_mul_left_right (m n k : ℕ) : gcd m (n + m * k) = gcd m n := by
  simp [gcd_rec m (n + m * k), gcd_rec m n]
#align nat.gcd_add_mul_left_right Nat.gcd_add_mul_left_right

@[simp]
theorem gcd_mul_right_add_right (m n k : ℕ) : gcd m (k * m + n) = gcd m n := by simp [add_comm _ n]
#align nat.gcd_mul_right_add_right Nat.gcd_mul_right_add_right

@[simp]
theorem gcd_mul_left_add_right (m n k : ℕ) : gcd m (m * k + n) = gcd m n := by simp [add_comm _ n]
#align nat.gcd_mul_left_add_right Nat.gcd_mul_left_add_right

@[simp]
theorem gcd_add_mul_right_left (m n k : ℕ) : gcd (m + k * n) n = gcd m n := by
  rw [gcd_comm, gcd_add_mul_right_right, gcd_comm]
#align nat.gcd_add_mul_right_left Nat.gcd_add_mul_right_left

@[simp]
theorem gcd_add_mul_left_left (m n k : ℕ) : gcd (m + n * k) n = gcd m n := by
  rw [gcd_comm, gcd_add_mul_left_right, gcd_comm]
#align nat.gcd_add_mul_left_left Nat.gcd_add_mul_left_left

@[simp]
theorem gcd_mul_right_add_left (m n k : ℕ) : gcd (k * n + m) n = gcd m n := by
  rw [gcd_comm, gcd_mul_right_add_right, gcd_comm]
#align nat.gcd_mul_right_add_left Nat.gcd_mul_right_add_left

@[simp]
theorem gcd_mul_left_add_left (m n k : ℕ) : gcd (n * k + m) n = gcd m n := by
  rw [gcd_comm, gcd_mul_left_add_right, gcd_comm]
#align nat.gcd_mul_left_add_left Nat.gcd_mul_left_add_left

-- Lemmas where one argument consists of an addition of the other
@[simp]
theorem gcd_add_self_right (m n : ℕ) : gcd m (n + m) = gcd m n :=
  Eq.trans (by rw [one_mul]) (gcd_add_mul_right_right m n 1)
#align nat.gcd_add_self_right Nat.gcd_add_self_right

@[simp]
theorem gcd_add_self_left (m n : ℕ) : gcd (m + n) n = gcd m n := by
  rw [gcd_comm, gcd_add_self_right, gcd_comm]
#align nat.gcd_add_self_left Nat.gcd_add_self_left

@[simp]
theorem gcd_self_add_left (m n : ℕ) : gcd (m + n) m = gcd n m := by rw [add_comm, gcd_add_self_left]
#align nat.gcd_self_add_left Nat.gcd_self_add_left

@[simp]
theorem gcd_self_add_right (m n : ℕ) : gcd m (m + n) = gcd m n := by
  rw [add_comm, gcd_add_self_right]
#align nat.gcd_self_add_right Nat.gcd_self_add_right

/-! ### `lcm` -/

theorem lcm_dvd_mul (m n : ℕ) : lcm m n ∣ m * n :=
  lcm_dvd (dvd_mul_right _ _) (dvd_mul_left _ _)
#align nat.lcm_dvd_mul Nat.lcm_dvd_mul

theorem lcm_dvd_iff {m n k : ℕ} : lcm m n ∣ k ↔ m ∣ k ∧ n ∣ k :=
  ⟨fun h => ⟨(dvd_lcm_left _ _).trans h, (dvd_lcm_right _ _).trans h⟩, and_imp.2 lcm_dvd⟩
#align nat.lcm_dvd_iff Nat.lcm_dvd_iff

/-!
### `coprime`

See also `Nat.coprime_of_dvd` and `Nat.coprime_of_dvd'` to prove `Nat.coprime m n`.
-/

instance (m n : ℕ) : Decidable (coprime m n) := inferInstanceAs (Decidable (gcd m n = 1))

theorem coprime.lcm_eq_mul {m n : ℕ} (h : coprime m n) : lcm m n = m * n := by
  rw [← one_mul (lcm m n), ← h.gcd_eq_one, gcd_mul_lcm]
#align nat.coprime.lcm_eq_mul Nat.coprime.lcm_eq_mul

theorem coprime.symmetric : Symmetric coprime := fun _ _ => coprime.symm
#align nat.coprime.symmetric Nat.coprime.symmetric

theorem coprime.dvd_mul_right {m n k : ℕ} (H : coprime k n) : k ∣ m * n ↔ k ∣ m :=
  ⟨H.dvd_of_dvd_mul_right, fun h => dvd_mul_of_dvd_left h n⟩
#align nat.coprime.dvd_mul_right Nat.coprime.dvd_mul_right

theorem coprime.dvd_mul_left {m n k : ℕ} (H : coprime k m) : k ∣ m * n ↔ k ∣ n :=
  ⟨H.dvd_of_dvd_mul_left, fun h => dvd_mul_of_dvd_right h m⟩
#align nat.coprime.dvd_mul_left Nat.coprime.dvd_mul_left

@[simp]
theorem coprime_add_self_right {m n : ℕ} : coprime m (n + m) ↔ coprime m n := by
  rw [coprime, coprime, gcd_add_self_right]
#align nat.coprime_add_self_right Nat.coprime_add_self_right

@[simp]
theorem coprime_self_add_right {m n : ℕ} : coprime m (m + n) ↔ coprime m n := by
  rw [add_comm, coprime_add_self_right]
#align nat.coprime_self_add_right Nat.coprime_self_add_right

@[simp]
theorem coprime_add_self_left {m n : ℕ} : coprime (m + n) n ↔ coprime m n := by
  rw [coprime, coprime, gcd_add_self_left]
#align nat.coprime_add_self_left Nat.coprime_add_self_left

@[simp]
theorem coprime_self_add_left {m n : ℕ} : coprime (m + n) m ↔ coprime n m := by
  rw [coprime, coprime, gcd_self_add_left]
#align nat.coprime_self_add_left Nat.coprime_self_add_left

@[simp]

@[simp]
theorem coprime_add_mul_left_right (m n k : ℕ) : coprime m (n + m * k) ↔ coprime m n := by
  rw [coprime, coprime, gcd_add_mul_left_right]
#align nat.coprime_add_mul_left_right Nat.coprime_add_mul_left_right

@[simp]
theorem coprime_mul_right_add_right (m n k : ℕ) : coprime m (k * m + n) ↔ coprime m n := by
  rw [coprime, coprime, gcd_mul_right_add_right]
#align nat.coprime_mul_right_add_right Nat.coprime_mul_right_add_right

@[simp]
theorem coprime_mul_left_add_right (m n k : ℕ) : coprime m (m * k + n) ↔ coprime m n := by
  rw [coprime, coprime, gcd_mul_left_add_right]
#align nat.coprime_mul_left_add_right Nat.coprime_mul_left_add_right

@[simp]
theorem coprime_add_mul_right_left (m n k : ℕ) : coprime (m + k * n) n ↔ coprime m n := by
  rw [coprime, coprime, gcd_add_mul_right_left]
#align nat.coprime_add_mul_right_left Nat.coprime_add_mul_right_left

@[simp]
theorem coprime_add_mul_left_left (m n k : ℕ) : coprime (m + n * k) n ↔ coprime m n := by
  rw [coprime, coprime, gcd_add_mul_left_left]
#align nat.coprime_add_mul_left_left Nat.coprime_add_mul_left_left

@[simp]
theorem coprime_mul_right_add_left (m n k : ℕ) : coprime (k * n + m) n ↔ coprime m n := by
  rw [coprime, coprime, gcd_mul_right_add_left]
#align nat.coprime_mul_right_add_left Nat.coprime_mul_right_add_left

@[simp]
theorem coprime_mul_left_add_left (m n k : ℕ) : coprime (n * k + m) n ↔ coprime m n := by
  rw [coprime, coprime, gcd_mul_left_add_left]
#align nat.coprime_mul_left_add_left Nat.coprime_mul_left_add_left

@[simp]
theorem coprime_pow_left_iff {n : ℕ} (hn : 0 < n) (a b : ℕ) :
    Nat.coprime (a ^ n) b ↔ Nat.coprime a b := by
  obtain ⟨n, rfl⟩ := exists_eq_succ_of_ne_zero hn.ne'
  rw [pow_succ, Nat.coprime_mul_iff_left]
  exact ⟨And.right, fun hab => ⟨hab.pow_left _, hab⟩⟩
#align nat.coprime_pow_left_iff Nat.coprime_pow_left_iff

@[simp]
theorem coprime_pow_right_iff {n : ℕ} (hn : 0 < n) (a b : ℕ) :
    Nat.coprime a (b ^ n) ↔ Nat.coprime a b := by
  rw [Nat.coprime_comm, coprime_pow_left_iff hn, Nat.coprime_comm]
#align nat.coprime_pow_right_iff Nat.coprime_pow_right_iff

theorem not_coprime_zero_zero : ¬coprime 0 0 := by simp
#align nat.not_coprime_zero_zero Nat.not_coprime_zero_zero

theorem coprime_one_left_iff (n : ℕ) : coprime 1 n ↔ True := by simp [coprime]
#align nat.coprime_one_left_iff Nat.coprime_one_left_iff

theorem coprime_one_right_iff (n : ℕ) : coprime n 1 ↔ True := by simp [coprime]
#align nat.coprime_one_right_iff Nat.coprime_one_right_iff

theorem gcd_mul_of_coprime_of_dvd {a b c : ℕ} (hac : coprime a c) (b_dvd_c : b ∣ c) :
    gcd (a * b) c = b := by
  rcases exists_eq_mul_left_of_dvd b_dvd_c with ⟨d, rfl⟩
  rw [gcd_mul_right]
  convert one_mul b
  exact coprime.coprime_mul_right_right hac
#align nat.gcd_mul_of_coprime_of_dvd Nat.gcd_mul_of_coprime_of_dvd

theorem coprime.eq_of_mul_eq_zero {m n : ℕ} (h : m.coprime n) (hmn : m * n = 0) :
    m = 0 ∧ n = 1 ∨ m = 1 ∧ n = 0 :=
  (Nat.eq_zero_of_mul_eq_zero hmn).imp (fun hm => ⟨hm, n.coprime_zero_left.mp <| hm ▸ h⟩) fun hn =>
    let eq := hn ▸ h.symm
    ⟨m.coprime_zero_left.mp <| eq, hn⟩
#align nat.coprime.eq_of_mul_eq_zero Nat.coprime.eq_of_mul_eq_zero

/-- Represent a divisor of `m * n` as a product of a divisor of `m` and a divisor of `n`.

See `exists_dvd_and_dvd_of_dvd_mul` for the more general but less constructive version for other
`GCDMonoid`s. -/
def prodDvdAndDvdOfDvdProd {m n k : ℕ} (H : k ∣ m * n) :
    { d : { m' // m' ∣ m } × { n' // n' ∣ n } // k = d.1 * d.2 } := by
  cases h0 : gcd k m
  case zero =>
    obtain rfl : k = 0 := eq_zero_of_gcd_eq_zero_left h0
    obtain rfl : m = 0 := eq_zero_of_gcd_eq_zero_right h0
    exact ⟨⟨⟨0, dvd_refl 0⟩, ⟨n, dvd_refl n⟩⟩, (zero_mul n).symm⟩
  case succ tmp =>
    have hpos : 0 < gcd k m := h0.symm ▸ Nat.zero_lt_succ _ ; clear h0 tmp
    have hd : gcd k m * (k / gcd k m) = k := Nat.mul_div_cancel' (gcd_dvd_left k m)
    refine' ⟨⟨⟨gcd k m, gcd_dvd_right k m⟩, ⟨k / gcd k m, _⟩⟩, hd.symm⟩
    apply Nat.dvd_of_mul_dvd_mul_left hpos
    rw [hd, ← gcd_mul_right]
    exact dvd_gcd (dvd_mul_right _ _) H
#align nat.prod_dvd_and_dvd_of_dvd_prod Nat.prodDvdAndDvdOfDvdProd

theorem dvd_mul {x m n : ℕ} : x ∣ m * n ↔ ∃ y z, y ∣ m ∧ z ∣ n ∧ y * z = x := by
  constructor
  · intro h
    obtain ⟨⟨⟨y, hy⟩, ⟨z, hz⟩⟩, rfl⟩ := prod_dvd_and_dvd_of_dvd_prod h
    exact ⟨y, z, hy, hz, rfl⟩
  · rintro ⟨y, z, hy, hz, rfl⟩
    exact mul_dvd_mul hy hz
#align nat.dvd_mul Nat.dvd_mul

theorem pow_dvd_pow_iff {a b n : ℕ} (n0 : 0 < n) : a ^ n ∣ b ^ n ↔ a ∣ b := by
  refine' ⟨fun h => _, fun h => pow_dvd_pow_of_dvd h _⟩
  cases' Nat.eq_zero_or_pos (gcd a b) with g0 g0
  · simp [eq_zero_of_gcd_eq_zero_right g0]
  rcases exists_coprime' g0 with ⟨g, a', b', g0', co, rfl, rfl⟩
  rw [mul_pow, mul_pow] at h
  replace h := Nat.dvd_of_mul_dvd_mul_right (pow_pos g0' _) h
  have := pow_dvd_pow a' n0
  rw [pow_one, (co.pow n n).eq_one_of_dvd h] at this
  simp [eq_one_of_dvd_one this]
#align nat.pow_dvd_pow_iff Nat.pow_dvd_pow_iff

/-- If `k:ℕ` divides coprime `a` and `b` then `k = 1` -/
theorem eq_one_of_dvd_coprimes {a b k : ℕ} (h_ab_coprime : coprime a b) (hka : k ∣ a)
    (hkb : k ∣ b) : k = 1 := by
  rw [coprime_iff_gcd_eq_one] at h_ab_coprime
  have h1 := dvd_gcd hka hkb
  rw [h_ab_coprime] at h1
  exact Nat.dvd_one.mp h1
#align nat.eq_one_of_dvd_coprimes Nat.eq_one_of_dvd_coprimes

theorem coprime.mul_add_mul_ne_mul {m n a b : ℕ} (cop : coprime m n) (ha : a ≠ 0) (hb : b ≠ 0) :
    a * m + b * n ≠ m * n := by
  intro h
  obtain ⟨x, rfl⟩ : n ∣ a :=
    cop.symm.dvd_of_dvd_mul_right
      ((Nat.dvd_add_iff_left (Nat.dvd_mul_left n b)).mpr
        ((congr_arg _ h).mpr (Nat.dvd_mul_left n m)))
  obtain ⟨y, rfl⟩ : m ∣ b :=
    cop.dvd_of_dvd_mul_right
      ((Nat.dvd_add_iff_right (Nat.dvd_mul_left m (n * x))).mpr
        ((congr_arg _ h).mpr (Nat.dvd_mul_right m n)))
  rw [mul_comm, mul_ne_zero_iff, ← one_le_iff_ne_zero] at ha hb
  refine' mul_ne_zero hb.2 ha.2 (eq_zero_of_mul_eq_self_left (ne_of_gt (add_le_add ha.1 hb.1)) _)
  rw [← mul_assoc, ← h, add_mul, add_mul, mul_comm _ n, ← mul_assoc, mul_comm y]
#align nat.coprime.mul_add_mul_ne_mul Nat.coprime.mul_add_mul_ne_mul

end Nat


/-
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_rfl },
  { -- m = n
    rw h_fm_eq_fn }
end


import data.finset
import algebra.big_operators
import tactic.ring
open nat

set_option pp.structure_projections false

variable (n : ℕ)

def impar (n : ℕ) := 2 * n + 1

-- (finset.range n) es el conjunto finito {0,1,2,...,n-1}. Por ejemplo,
--    #eval (finset.range 3)                  -- => {0, 1, 2}
--    #eval finset.sum (finset.range 3) impar -- => 9

-- 1ª demostración
example :
  finset.sum (finset.range n) impar = n ^ 2 :=
begin
  induction n with d hd,
    refl,
  rw finset.sum_range_succ,
  rw hd,
  change d ^ 2 + (2 * d + 1) = (d + 1) ^ 2,
  ring,
end

-- 2ª demostración
example :
  finset.sum (finset.range n) impar = n ^ 2 :=
begin
  induction n with m HI,
  { calc finset.sum (finset.range 0) impar
          = 0
            : by simp
     ...  = 0 ^ 2
            : rfl, },
  { calc finset.sum (finset.range (succ m)) impar
         = finset.sum (finset.range m) impar + impar m
           : finset.sum_range_succ impar m
     ... = m ^ 2 + impar m
           : congr_arg2 (+) HI rfl
     ... = m ^ 2 + 2 * m + 1
           : rfl
     ... = (m + 1) ^ 2
           : by ring_nf
     ... = succ m ^ 2
           : rfl },
end