-- BOTH:
import data.real.basic

/- TEXT:
.. _implication_and_the_universal_quantifier:

Implication and the Universal Quantifier
----------------------------------------

Consider the statement after the ``#check``:
TEXT. -/
-- QUOTE:
#check ∀ x : ℝ, 0 ≤ x → abs x = x
-- QUOTE.

/- TEXT:
In words, we would say "for every real number ``x``, if ``0 ≤ x`` then
the absolute value of ``x`` equals ``x``".
We can also have more complicated statements like:
TEXT. -/
-- QUOTE:
#check ∀ x y ε : ℝ, 0 < ε → ε ≤ 1 → abs x < ε → abs y < ε → abs (x * y) < ε
-- QUOTE.

/- TEXT:
In words, we would say "for every ``x``, ``y``, and ``ε``,
if ``0 < ε ≤ 1``, the absolute value of ``x`` is less than ``ε``,
and the absolute value of ``y`` is less than ``ε``,
then the absolute value of ``x * y`` is less than ``ε``."
In Lean, in a sequence of implications there are
implicit parentheses grouped to the right.
So the expression above means
"if ``0 < ε`` then if ``ε ≤ 1`` then if ``abs x < ε`` ..."
As a result, the expression says that all the
assumptions together imply the conclusion.

You have already seen that even though the universal quantifier
in this statement
ranges over objects and the implication arrows introduce hypotheses,
Lean treats the two in very similar ways.
In particular, if you have proved a theorem of that form,
you can apply it to objects and hypotheses in the same way:
TEXT. -/
-- QUOTE:
lemma my_lemma : ∀ x y ε : ℝ,
  0 < ε → ε ≤ 1 → abs x < ε → abs y < ε → abs (x * y) < ε :=
sorry

section
  variables a b δ : ℝ
  variables (h₀ : 0 < δ) (h₁ : δ ≤ 1)
  variables (ha : abs a < δ) (hb : abs b < δ)

  #check my_lemma a b δ
  #check my_lemma a b δ h₀ h₁
  #check my_lemma a b δ h₀ h₁ ha hb
end
-- QUOTE.

/- TEXT:
You have also already seen that it is common in Lean
to use curly brackets to make quantified variables implicit
when they can be inferred from subsequent hypotheses.
When we do that, we can just apply a lemma to the hypotheses without
mentioning the objects.
TEXT. -/
-- QUOTE:
lemma my_lemma2 : ∀ {x y ε : ℝ},
  0 < ε → ε ≤ 1 → abs x < ε → abs y < ε → abs (x * y) < ε :=
sorry

section
  variables a b δ : ℝ
  variables (h₀ : 0 < δ) (h₁ : δ ≤ 1)
  variables (ha : abs a < δ) (hb : abs b < δ)

  #check my_lemma2 h₀ h₁ ha hb
end
-- QUOTE.

/- TEXT:
At this stage, you also know that if you use
the ``apply`` tactic to apply ``my_lemma``
to a goal of the form ``abs (a * b) < δ``,
you are left with new goals that require you to  prove
each of the hypotheses.

.. index:: intros, tactics ; intros

To prove a statement like this, use the ``intros`` tactic.
Take a look at what it does in this example:
TEXT. -/
-- QUOTE:
lemma my_lemma3 : ∀ {x y ε : ℝ},
  0 < ε → ε ≤ 1 → abs x < ε → abs y < ε → abs (x * y) < ε :=
begin
  intros x y ε epos ele1 xlt ylt,
  sorry
end
-- QUOTE.

/- TEXT:
We can use any names we want for the universally quantified variables;
they do not have to be ``x``, ``y``, and ``ε``.
Notice that we have to introduce the variables
even though they are marked implicit:
making them implicit means that we leave them out when
we write an expression *using* ``my_lemma``,
but they are still an essential part of the statement
that we are proving.
After the ``intros`` command,
the goal is what it would have been at the start if we
listed all the variables and hypotheses *before* the colon,
as we did in the last section.
In a moment, we will see why it is sometimes necessary to
introduce variables and hypotheses after the proof begins.

To help you prove the lemma, we will start you off:
TEXT. -/
-- QUOTE:
lemma my_lemma4 : ∀ {x y ε : ℝ},
  0 < ε → ε ≤ 1 → abs x < ε → abs y < ε → abs (x * y) < ε :=
begin
  intros x y ε epos ele1 xlt ylt,
  calc
    abs (x * y) = abs x * abs y : sorry
    ... ≤ abs x * ε             : sorry
    ... < 1 * ε                 : sorry
    ... = ε                     : sorry
end
-- QUOTE.

-- OMIT:
/- TODO : remember to introduce ``suffices`` eventually

   We have introduced another new tactic here:
   ``suffices`` works like ``have`` in reverse,
   asking you to prove the goal using the
   stated fact,
   and then leaving you the new goal of proving that fact. -/

/- TEXT:
Finish the proof using the theorems
``abs_mul``, ``mul_le_mul``, ``abs_nonneg``,
``mul_lt_mul_right``, and ``one_mul``.
Remember that you can find theorems like these using
tab completion.
Remember also that you can use ``.mp`` and ``.mpr``
or ``.1`` and ``.2`` to extract the two directions
of an if-and-only-if statement.

Universal quantifiers are often hidden in definitions,
and Lean will unfold definitions to expose them when necessary.
For example, let's define two predicates,
``fn_ub f a`` and ``fn_lb f a``,
where ``f`` is a function from the real numbers to the real
numbers and ``a`` is a real number.
The first says that ``a`` is an upper bound on the
values of ``f``,
and the second says that ``a`` is a lower bound
on the values of ``f``.
BOTH: -/
-- QUOTE:
def fn_ub (f : ℝ → ℝ) (a : ℝ) : Prop := ∀ x, f x ≤ a
def fn_lb (f : ℝ → ℝ) (a : ℝ) : Prop := ∀ x, a ≤ f x
-- QUOTE.

/- TEXT:
.. index:: lambda abstraction

In the next example, ``λ x, f x + g x`` is a name for the
function that maps ``x`` to ``f x + g x``.
Computer scientists refer to this as "lambda abstraction,"
whereas a mathematician might describe it as the function
:math:`x \mapsto f(x) + g(x)`.
BOTH: -/
section
variables (f g : ℝ → ℝ) (a b : ℝ)

-- EXAMPLES:
-- QUOTE:
example (hfa : fn_ub f a) (hgb : fn_ub g b) :
  fn_ub (λ x, f x + g x) (a + b) :=
begin
  intro x,
  dsimp,
  apply add_le_add,
  apply hfa,
  apply hgb
end
-- QUOTE.

/- TEXT:
.. index:: dsimp, tactics ; dsimp, change, tactics ; change

Applying ``intro`` to the goal ``fn_ub (λ x, f x + g x) (a + b)``
forces Lean to unfold the definition of ``fn_ub``
and introduce ``x`` for the universal quantifier.
The goal is then ``(λ (x : ℝ), f x + g x) x ≤ a + b``.
But applying ``(λ x, f x + g x)`` to ``x`` should result in ``f x + g x``,
and the ``dsimp`` command performs that simplification.
(The "d" stands for "definitional.")
You can delete that command and the proof still works;
Lean would have to perform that contraction anyhow to make
sense of the next ``apply``.
The ``dsimp`` command simply makes the goal more readable
and helps us figure out what to do next.
Another option is to use the ``change`` tactic
by writing ``change f x + g x ≤ a + b``.
This helps make the proof more readable,
and gives you more control over how the goal is transformed.

The rest of the proof is routine.
The last two ``apply`` commands force Lean to unfold the definitions
of ``fn_ub`` in the hypotheses.
Try carrying out similar proofs of these:
TEXT. -/
-- QUOTE:
example (hfa : fn_lb f a) (hgb : fn_lb g b) :
  fn_lb (λ x, f x + g x) (a + b) :=
sorry

example (nnf : fn_lb f 0) (nng : fn_lb g 0) :
  fn_lb (λ x, f x * g x) 0 :=
sorry

example (hfa : fn_ub f a) (hfb : fn_ub g b)
    (nng : fn_lb g 0) (nna : 0 ≤ a) :
  fn_ub (λ x, f x * g x) (a * b) :=
sorry
-- QUOTE.

-- SOLUTIONS:
example (hfa : fn_lb f a) (hgb : fn_lb g b) :
  fn_lb (λ x, f x + g x) (a + b) :=
begin
  intro x,
  apply add_le_add,
  apply hfa,
  apply hgb
end

example (nnf : fn_lb f 0) (nng : fn_lb g 0) :
  fn_lb (λ x, f x * g x) 0 :=
begin
  intro x,
  apply mul_nonneg,
  apply nnf,
  apply nng
end

example (hfa : fn_ub f a) (hfb : fn_ub g b)
    (nng : fn_lb g 0) (nna : 0 ≤ a) :
  fn_ub (λ x, f x * g x) (a * b) :=
begin
  intro x,
  apply mul_le_mul,
  apply hfa,
  apply hfb,
  apply nng,
  apply nna
end

-- BOTH:
end

/- TEXT:
Even though we have defined ``fn_ub`` and ``fn_lb`` for functions
from the reals to the reals,
you should recognize that the definitions and proofs are much
more general.
The definitions make sense for functions between any two types
for which there is a notion of order on the codomain.
Checking the type of the theorem ``add_le_add`` shows that it holds
of any structure that is an "ordered additive commutative monoid";
the details of what that means don't matter now,
but it is worth knowing that the natural numbers, integers, rationals,
and real numbers are all instances.
So if we prove the theorem ``fn_ub_add`` at that level of generality,
it will apply in all these instances.
TEXT. -/
section
-- QUOTE:
variables {α : Type*} {R : Type*} [ordered_cancel_add_comm_monoid R]

#check @add_le_add

def fn_ub' (f : α → R) (a : R) : Prop := ∀ x, f x ≤ a

theorem fn_ub_add {f g : α → R} {a b : R}
    (hfa : fn_ub' f a) (hgb : fn_ub' g b) :
  fn_ub' (λ x, f x + g x) (a + b) :=
λ x, add_le_add (hfa x) (hgb x)
-- QUOTE.
end

/- TEXT:
You have already seen square brackets like these in
Section :numref:`proving_identities_in_algebraic_structures`,
though we still haven't explained what they mean.
For concreteness, we will stick to the real numbers
for most of our examples,
but it is worth knowing that mathlib contains definitions and theorems
that work at a high level of generality.

.. index:: monotone function

For another example of a hidden universal quantifier,
mathlib defines a predicate ``monotone``,
which says that a function is nondecreasing in its arguments:
TEXT. -/
-- QUOTE:
example (f : ℝ → ℝ) (h : monotone f) :
  ∀ {a b}, a ≤ b → f a ≤ f b := h
-- QUOTE.

/- TEXT:
Proving statements about monotonicity
involves using ``intros`` to introduce two variables,
say, ``a`` and ``b``, and the hypothesis ``a ≤ b``.
To *use* a monotonicity hypothesis,
you can apply it to suitable arguments and hypotheses,
and then apply the resulting expression to the goal.
Or you can apply it to the goal and let Lean help you
work backwards by displaying the remaining hypotheses
as new subgoals.
BOTH: -/
section
variables (f g : ℝ → ℝ)

-- EXAMPLES:
-- QUOTE:
example (mf : monotone f) (mg : monotone g) :
  monotone (λ x, f x + g x) :=
begin
  intros a b aleb,
  apply add_le_add,
  apply mf aleb,
  apply mg aleb
end
-- QUOTE.

/- TEXT:
When a proof is this short, it is often convenient
to give a proof term instead.
To describe a proof that temporarily introduces objects
``a`` and ``b`` and a hypothesis ``aleb``,
Lean uses the notation ``λ a b aleb, ...``.
This is analogous to the way that a lambda abstraction
like ``λ x, x^2`` describes a function
by temporarily naming an object, ``x``,
and then using it to describe a value.
So the ``intros`` command in the previous proof
corresponds to the lambda abstraction in the next proof term.
The ``apply`` commands then correspond to building
the application of the theorem to its arguments.
TEXT. -/
-- QUOTE:
example (mf : monotone f) (mg : monotone g) :
  monotone (λ x, f x + g x) :=
λ a b aleb, add_le_add (mf aleb) (mg aleb)
-- QUOTE.

/- TEXT:
Here is a useful trick: if you start writing
the proof term ``λ a b aleb, _`` using
an underscore where the rest of the
expression should go,
Lean will flag an error,
indicating that it can't guess the value of that expression.
If you check the Lean Goal window in VS Code or
hover over the squiggly error marker,
Lean will show you the goal that the remaining
expression has to solve.

Try proving these, with either tactics or proof terms:
TEXT. -/
-- QUOTE:
example {c : ℝ} (mf : monotone f) (nnc : 0 ≤ c) :
  monotone (λ x, c * f x) :=
sorry

example (mf : monotone f) (mg : monotone g) :
  monotone (λ x, f (g x)) :=
sorry
-- QUOTE.

-- SOLUTIONS:
example {c : ℝ} (mf : monotone f) (nnc : 0 ≤ c) :
  monotone (λ x, c * f x) :=
begin
  intros a b aleb,
  apply mul_le_mul_of_nonneg_left _ nnc,
  apply mf aleb
end

example {c : ℝ} (mf : monotone f) (nnc : 0 ≤ c) :
  monotone (λ x, c * f x) :=
λ a b aleb, mul_le_mul_of_nonneg_left (mf aleb) nnc

example (mf : monotone f) (mg : monotone g) :
  monotone (λ x, f (g x)) :=
begin
  intros a b aleb,
  apply mf,
  apply mg,
  apply aleb
end

example (mf : monotone f) (mg : monotone g) :
  monotone (λ x, f (g x)) :=
λ a b aleb, mf (mg aleb)

/- TEXT:
Here are some more examples.
A function :math:`f` from :math:`\Bbb R` to
:math:`\Bbb R` is said to be *even* if
:math:`f(-x) = f(x)` for every :math:`x`,
and *odd* if :math:`f(-x) = -f(x)` for every :math:`x`.
The following example defines these two notions formally
and establishes one fact about them.
You can complete the proofs of the others.
TEXT. -/
-- QUOTE:
-- BOTH:
def fn_even (f : ℝ → ℝ) : Prop := ∀ x, f x = f (-x)
def fn_odd (f : ℝ → ℝ) : Prop := ∀ x, f x = - f (-x)

-- EXAMPLES:
example (ef : fn_even f) (eg : fn_even g) : fn_even (λ x, f x + g x) :=
begin
  intro x,
  calc
    (λ x, f x + g x) x = f x + g x       : rfl
                    ... = f (-x) + g (-x) : by rw [ef, eg]
end

example (of : fn_odd f) (og : fn_odd g) : fn_even (λ x, f x * g x) :=
sorry

example (ef : fn_even f) (og : fn_odd g) : fn_odd (λ x, f x * g x) :=
sorry

example (ef : fn_even f) (og : fn_odd g) : fn_even (λ x, f (g x)) :=
sorry
-- QUOTE.

-- SOLUTIONS:
example (of : fn_odd f) (og : fn_odd g) : fn_even (λ x, f x * g x) :=
begin
  intro x,
  calc
    (λ x, f x * g x) x = f x * g x          : rfl
                    ... = f (- x) * g (- x) : by rw [of, og, neg_mul_neg]
end

example (ef : fn_even f) (og : fn_odd g) : fn_odd (λ x, f x * g x) :=
begin
  intro x,
  dsimp,
  rw [ef, og, neg_mul_eq_mul_neg]
end

example (ef : fn_even f) (og : fn_odd g) : fn_even (λ x, f (g x)) :=
begin
  intro x,
  dsimp,
  rw [og, ←ef]
end

-- BOTH:
end

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

The first proof can be shortened using ``dsimp`` or ``change``
to get rid of the lambda.
But you can check that the subsequent ``rw`` won't work
unless we get rid of the lambda explicitly,
because otherwise it cannot find the patterns ``f x`` and ``g x``
in the expression.
Contrary to some other tactics, ``rw`` operates on the syntactic level,
it won't unfold definitions or apply reductions for you
(it has a variant called ``erw`` that tries a little harder in this
direction, but not much harder).

You can find implicit universal quantifiers all over the place,
once you know how to spot them.
Mathlib includes a good library for rudimentary set theory.
Lean's logical foundation imposes the restriction that when
we talk about sets, we are always talking about sets of
elements of some type. If ``x`` has type ``α`` and ``s`` has
type ``set α``, then ``x ∈ s`` is a proposition that
asserts that ``x`` is an element of ``s``.
If ``s`` and ``t`` are of type ``set α``,
then the subset relation ``s ⊆ t`` is defined to mean
``∀ {x : α}, x ∈ s → x ∈ t``.
The variable in the quantifier is marked implicit so that
given ``h : s ⊆ t`` and ``h' : x ∈ s``,
we can write ``h h'`` as justification for ``x ∈ t``.
The following example provides a tactic proof and a proof term
justifying the reflexivity of the subset relation,
and asks you to do the same for transitivity.
TEXT. -/
-- BOTH:
section
-- QUOTE:
variables {α : Type*} (r s t : set α)

-- EXAMPLES:
example : s ⊆ s :=
by { intros x xs, exact xs }

theorem subset.refl : s ⊆ s := λ x xs, xs

theorem subset.trans : r ⊆ s → s ⊆ t → r ⊆ t :=
sorry
-- QUOTE.

-- SOLUTIONS:
example : r ⊆ s → s ⊆ t → r ⊆ t :=
begin
  intros rsubs ssubt x xr,
  apply ssubt,
  apply rsubs,
  apply xr
end

theorem subset.transαα : r ⊆ s → s ⊆ t → r ⊆ t :=
λ rsubs ssubt x xr, ssubt (rsubs xr)

-- BOTH:
end

/- TEXT:
Just as we defined ``fn_ub`` for functions,
we can define ``set_ub s a`` to mean that ``a``
is an upper bound on the set ``s``,
assuming ``s`` is a set of elements of some type that
has an order associated with it.
In the next example, we ask you to prove that
if ``a`` is a bound on ``s`` and ``a ≤ b``,
then ``b`` is a bound on ``s`` as well.
TEXT. -/
-- BOTH:
section

-- QUOTE:
variables {α : Type*} [partial_order α]
variables (s : set α) (a b : α)

def set_ub (s : set α) (a : α) := ∀ x, x ∈ s → x ≤ a

-- EXAMPLES:
example (h : set_ub s a) (h' : a ≤ b) : set_ub s b :=
sorry
-- QUOTE.

-- SOLUTIONS:
example (h : set_ub s a) (h' : a ≤ b) : set_ub s b :=
begin
  intros x xs,
  apply le_trans (h x xs) h'
end

example (h : set_ub s a) (h' : a ≤ b) : set_ub s b :=
λ x xs, le_trans (h x xs) h'

-- BOTH:
end

/- TEXT:
.. index:: injective function

We close this section with one last important example.
A function :math:`f` is said to be *injective* if for
every :math:`x_1` and :math:`x_2`,
if :math:`f(x_1) = f(x_2)` then :math:`x_1 = x_2`.
Mathlib defines ``function.injective f`` with
``x₁`` and ``x₂`` implicit.
The next example shows that, on the real numbers,
any function that adds a constant is injective.
We then ask you to show that multiplication by a nonzero
constant is also injective.
TEXT. -/
-- BOTH:
section
-- QUOTE:
open function

-- EXAMPLES:
example (c : ℝ) : injective (λ x, x + c) :=
begin
  intros x₁ x₂ h',
  exact (add_left_inj c).mp h',
end

example {c : ℝ} (h : c ≠ 0) : injective (λ x, c * x) :=
sorry
-- QUOTE.

-- SOLUTIONS:
example {c : ℝ} (h : c ≠ 0) : injective (λ x, c * x) :=
begin
  intros x₁ x₂ h',
  apply (mul_right_inj' h).mp h'
end

/- TEXT:
Finally, show that the composition of two injective functions is injective:
BOTH: -/
-- QUOTE:
variables {α : Type*} {β : Type*} {γ : Type*}
variables {g : β → γ} {f : α → β}

-- EXAMPLES:
example (injg : injective g) (injf : injective f) :
  injective (λ x, g (f x)) :=
sorry
-- QUOTE.

-- SOLUTIONS:
example (injg : injective g) (injf : injective f) :
  injective (λ x, g (f x)) :=
begin
  intros x₁ x₂ h,
  apply injf,
  apply injg,
  apply h
end

-- BOTH:
end


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

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

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

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

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

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

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

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

-- 0029
example (h₁ : a ∣ b) (h₂ : b ∣ c) : a ∣ c :=
begin
  cases h₁ with k1 hk1,
  cases h₂ with k2 hk2,
  have h: c = a * (k1 * k2), by
  {calc c = b * k2 : by rw hk2
      ... = (a * k1) * k2 : by rw hk1
      ... = a * (k1 * k2) : by rw mul_assoc},
  use k1*k2,
  exact h,
end

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

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

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

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

-- 0030
example : 0 ∣ a ↔ a = 0 :=
begin
  split,
    {rintros ⟨k, refl⟩,
    linarith
    },
    {intros h,
    use 0, -- 0 = 0 * 0, so 0 | 0
    linarith}
end

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

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

-/
open function

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

-- 0031
example (h : surjective (g ∘ f)) : surjective g :=
begin
  intros x,
  rcases h x with ⟨t,rfl⟩,
  exact ⟨f t, rfl⟩
end

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

-- 0032
example (hf : surjective f) (hg : surjective g) : surjective (g ∘ f) :=
begin
  intros x,
  rcases hg x with ⟨t, rfl⟩,
  rcases hf t with ⟨tt, rfl⟩,
  exact ⟨tt, rfl⟩
end


-- ---------------------------------------------------------------------
-- Ejercicio . Demostrar que la composición de funciones suprayectivas
-- es suprayectiva.
-- ----------------------------------------------------------------------

import tactic

open function

variables {α : Type*} {β : Type*} {γ : Type*}
variables {f : α → β} {g : β → γ}

example
  (surjg : surjective g)
  (surjf : surjective f)
  : surjective (λ x, g (f x)) :=
begin
  intro x,
  cases surjg x with y hy,
  cases surjf y with z hz,
  use z,
  change g (f z) = x,
  rw hz,
  exact hy,
end

-- La prueba es
--
-- α : Type u_1,
-- β : Type u_2,
-- γ : Type u_3,
-- f : α → β,
-- g : β → γ,
-- surjg : surjective g,
-- surjf : surjective f
-- ⊢ surjective (λ (x : α), g (f x))
--    >> intro x,
-- x : γ
-- ⊢ ∃ (a : α), (λ (x : α), g (f x)) a = x
--    >> cases surjg x with y hy,
-- y : β,
-- hy : g y = x
-- ⊢ ∃ (a : α), (λ (x : α), g (f x)) a = x
--    >> cases surjf y with z hz,
-- z : α,
-- hz : f z = y
-- ⊢ ∃ (a : α), (λ (x : α), g (f x)) a = x
--    >> use z,
-- ⊢ (λ (x : α), g (f x)) z = x
--    >> change g (f z) = x,
-- ⊢ g (f z) = x
--    >> rw hz,
-- ⊢ g y = x
--    >> exact hy
-- no goals


inductive Formula
| eqf  : Nat → Nat → Formula
| andf : Formula → Formula → Formula
| impf : Formula → Formula → Formula
| notf : Formula → Formula
| orf  : Formula → Formula → Formula
| allf : (Nat → Formula) → Formula

namespace Formula
def implies (a b : Prop) : Prop := a → b

def denote : Formula → Prop
| eqf n1 n2  => n1 = n2
| andf f1 f2 => denote f1 ∧ denote f2
| impf f1 f2 => implies (denote f1) (denote f2)
| orf f1 f2  => denote f1 ∨ denote f2
| notf f     => ¬ denote f
| allf f     => (n : Nat) → denote (f n)

theorem denote_eqf (n1 n2 : Nat) : denote (eqf n1 n2) = (n1 = n2) :=
rfl

theorem denote_andf (f1 f2 : Formula) : denote (andf f1 f2) = (denote f1 ∧ denote f2) :=
rfl

theorem denote_impf (f1 f2 : Formula) : denote (impf f1 f2) = (denote f1 → denote f2) :=
rfl

theorem denote_orf (f1 f2 : Formula) : denote (orf f1 f2) = (denote f1 ∨ denote f2) :=
rfl

theorem denote_notf (f : Formula) : denote (notf f) = ¬ denote f :=
rfl

theorem denote_allf (f : Nat → Formula) : denote (allf f) = (∀ n, denote (f n)) :=
rfl

theorem ex : denote (allf (fun n₁ => allf (fun n₂ => impf (eqf n₁ n₂) (eqf n₂ n₁)))) = (∀ (n₁ n₂ : Nat), n₁ = n₂ → n₂ = n₁) :=
rfl

end Formula


import ..lectures.love05_inductive_predicates_demo

/-! # LoVe Exercise 5: Inductive Predicates -/

set_option pp.beta true
set_option pp.generalized_field_notation false

namespace LoVe

/-! ## Question 1: Even and Odd

The `even` predicate is true for even numbers and false for odd numbers. -/

#check even

/-! We define `odd` as the negation of `even`: -/

def odd (n : ℕ) : Prop :=
  ¬ even n

/-! 1.1. Prove that 1 is odd and register this fact as a simp rule.

Hint: `cases'` is useful to reason about hypotheses of the form `even …`. -/

@[simp] lemma odd_1 :
  odd 1 :=
begin
  intro h,
  cases' h
end

/-! 1.2. Prove that 3 and 5 are odd. -/

lemma odd_3 :
  odd 3 :=
begin
  intro h,
  cases' h,
  cases' h
end

lemma odd_5 :
  odd 5 :=
begin
  intro h,
  cases' h,
  cases' h,
  cases' h
end

/-! 1.3. Complete the following proof by structural induction. -/

lemma even_two_times :
  ∀m : ℕ, even (2 * m)
| 0       := even.zero
| (m + 1) :=
  begin
    apply even.add_two,
    simp,
    apply even_two_times
  end

/-! 1.4. Complete the following proof by rule induction.

Hint: You can use the `cases'` tactic (or `match … with`) to destruct an
existential quantifier and extract the witness. -/

lemma even_imp_exists_two_times :
  ∀n : ℕ, even n → ∃m, n = 2 * m :=
begin
  intros n hen,
  induction' hen,
  case zero {
    apply exists.intro 0,
    refl },
  case add_two : k hek ih {
    cases' ih with w hk,
    apply exists.intro (w + 1),
    rw hk,
    linarith }
end

/-! 1.5. Using `even_two_times` and `even_imp_exists_two_times`, prove the
following equivalence. -/

lemma even_iff_exists_two_times (n : ℕ) :
  even n ↔ ∃m, n = 2 * m :=
begin
  apply iff.intro,
  { apply even_imp_exists_two_times },
  { intro h,
    cases' h,
    simp *,
    apply even_two_times }
end

/-! 1.6 (**optional**). Give a structurally recursive definition of `even` and
test it with `#eval`.

Hint: The negation operator on `bool` is called `not`. -/

def even_rec : nat → bool
| 0       := tt
| (n + 1) := not (even_rec n)

#eval even_rec 0
#eval even_rec 1
#eval even_rec 2
#eval even_rec 3
#eval even_rec 4

/-! ## Question 2: Tennis Games

Recall the inductive type of tennis scores from the demo: -/

#check score

/-! 2.1. Define an inductive predicate that returns true if the server is ahead
of the receiver and that returns false otherwise. -/

inductive srv_ahead : score → Prop
| vs {m n : ℕ} : m > n → srv_ahead (score.vs m n)
| adv_srv      : srv_ahead score.adv_srv
| game_srv     : srv_ahead score.game_srv

/-! 2.2. Validate your predicate definition by proving the following lemmas. -/

lemma srv_ahead_vs {m n : ℕ} (hgt : m > n) :
  srv_ahead (score.vs m n) :=
srv_ahead.vs hgt

lemma srv_ahead_adv_srv :
  srv_ahead score.adv_srv :=
srv_ahead.adv_srv

lemma not_srv_ahead_adv_rcv :
  ¬ srv_ahead score.adv_rcv :=
begin
  intro h,
  cases' h
end

lemma srv_ahead_game_srv :
  srv_ahead score.game_srv :=
srv_ahead.game_srv

lemma not_srv_ahead_game_rcv :
  ¬ srv_ahead score.game_rcv :=
begin
  intro h,
  cases' h
end

/-! 2.3. Compare the above lemma statements with your definition. What do you
observe? -/

/-! The positive lemmas correspond exactly to the introduction rules of the
definition. By contrast, the negative lemmas have no counterparts in the
definition. -/

/-! ## Question 3: Binary Trees

3.1. Prove the converse of `is_full_mirror`. You may exploit already proved
lemmas (e.g., `is_full_mirror`, `mirror_mirror`). -/

#check is_full_mirror
#check mirror_mirror

lemma mirror_is_full {α : Type} :
  ∀t : btree α, is_full (mirror t) → is_full t :=
begin
  intros t fmt,
  have fmmt : is_full (mirror (mirror t)) :=
    is_full_mirror _ fmt,
  rw mirror_mirror at fmmt,
  assumption
end

/-! 3.2. Define a `map` function on binary trees, similar to `list.map`. -/

def map_btree {α β : Type} (f : α → β) : btree α → btree β
| btree.empty        := btree.empty
| (btree.node a l r) := btree.node (f a) (map_btree l) (map_btree r)

/-! 3.3. Prove the following lemma by case distinction. -/

lemma map_btree_eq_empty_iff {α β : Type} (f : α → β) :
  ∀t : btree α, map_btree f t = btree.empty ↔ t = btree.empty
| btree.empty        := by simp [map_btree]
| (btree.node _ _ _) := by simp [map_btree]

/-! 3.4 (**optional**). Prove the following lemma by rule induction. -/

lemma map_btree_mirror {α β : Type} (f : α → β) :
  ∀t : btree α, is_full t → is_full (map_btree f t) :=
begin
  intros t hfull,
  induction' hfull,
  case empty {
    simp [map_btree],
    exact is_full.empty },
  case node {
    simp [map_btree],
    apply is_full.node,
    { exact ih_hfull f },
    { exact ih_hfull_1 f },
    { simp [map_btree_eq_empty_iff],
      assumption } }
end

end LoVe


/-
Copyright (c) 2021 Yourong Zang. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yourong Zang
-/
import analysis.calculus.conformal.normed_space
import analysis.inner_product_space.conformal_linear_map

/-!
# Conformal maps between inner product spaces

A function between inner product spaces is which has a derivative at `x`
is conformal at `x` iff the derivative preserves inner products up to a scalar multiple.
-/

noncomputable theory

variables {E F : Type*} [inner_product_space ℝ E] [inner_product_space ℝ F]

open_locale real_inner_product_space

/-- A real differentiable map `f` is conformal at point `x` if and only if its
    differential `fderiv ℝ f x` at that point scales every inner product by a positive scalar. -/
lemma conformal_at_iff' {f : E → F} {x : E} :
  conformal_at f x ↔
  ∃ (c : ℝ), 0 < c ∧ ∀ (u v : E), ⟪fderiv ℝ f x u, fderiv ℝ f x v⟫ = c * ⟪u, v⟫ :=
by rw [conformal_at_iff_is_conformal_map_fderiv, is_conformal_map_iff]

/-- A real differentiable map `f` is conformal at point `x` if and only if its
    differential `f'` at that point scales every inner product by a positive scalar. -/
lemma conformal_at_iff {f : E → F} {x : E} {f' : E →L[ℝ] F} (h : has_fderiv_at f f' x) :
  conformal_at f x ↔ ∃ (c : ℝ), 0 < c ∧ ∀ (u v : E), ⟪f' u, f' v⟫ = c * ⟪u, v⟫ :=
by simp only [conformal_at_iff', h.fderiv]

/-- The conformal factor of a conformal map at some point `x`. Some authors refer to this function
    as the characteristic function of the conformal map. -/
def conformal_factor_at {f : E → F} {x : E} (h : conformal_at f x) : ℝ :=
classical.some (conformal_at_iff'.mp h)

lemma conformal_factor_at_pos {f : E → F} {x : E} (h : conformal_at f x) :
  0 < conformal_factor_at h :=
(classical.some_spec $ conformal_at_iff'.mp h).1

lemma conformal_factor_at_inner_eq_mul_inner' {f : E → F} {x : E}
  (h : conformal_at f x) (u v : E) :
  ⟪(fderiv ℝ f x) u, (fderiv ℝ f x) v⟫ = (conformal_factor_at h : ℝ) * ⟪u, v⟫ :=
(classical.some_spec $ conformal_at_iff'.mp h).2 u v

lemma conformal_factor_at_inner_eq_mul_inner {f : E → F} {x : E} {f' : E →L[ℝ] F}
  (h : has_fderiv_at f f' x) (H : conformal_at f x) (u v : E) :
  ⟪f' u, f' v⟫ = (conformal_factor_at H : ℝ) * ⟪u, v⟫ :=
(H.differentiable_at.has_fderiv_at.unique h) ▸ conformal_factor_at_inner_eq_mul_inner' H u v


-- ---------------------------------------------------------------------
-- Ejercicio 1. Realizar las siguientes acciones:
-- 1. Importar la teoría Definicion_de_funciones_acotadas
-- 2. Declarar f como variable de funciones de ℝ en ℝ.
-- 3. Declarar a y c como variables sobre ℝ.
-- ----------------------------------------------------------------------

import .Definicion_de_funciones_acotadas -- 1

variables {f : ℝ → ℝ}                    -- 2
variables {a c : ℝ}                      -- 3

-- ---------------------------------------------------------------------
-- Ejercicio 2. Demostrar que si a es una cota superior de f y c no es
-- negativo, entonces c * a es una cota superior de c * f.
-- ----------------------------------------------------------------------

lemma fn_ub_mul
  (hfa : fn_ub f a)
  (h : c ≥ 0)
  : fn_ub (λ x, c * f x) (c * a) :=
λ x, mul_le_mul_of_nonneg_left (hfa x) h

-- ---------------------------------------------------------------------
-- Ejercicio 3. Demostrar que si c ≥ 0 y f está acotada superiormente,
-- entonces c * f también lo está.
-- ----------------------------------------------------------------------

-- 1ª demostración
-- ===============

example
  (ubf : fn_has_ub f)
  (h : c ≥ 0)
  : fn_has_ub (λ x, c * f x) :=
begin
  cases ubf with a ha,
  have h1 : fn_ub (λ x, c * f x) (c * a) := fn_ub_mul ha h,
  have h2 : ∃ z, ∀ x, (λ x, c * f x) x ≤ z,
    by exact Exists.intro (c * a) h1,
  show fn_has_ub (λ x, c * f x),
    by exact h2,
end

-- 2ª demostración
-- ===============

example
  (ubf : fn_has_ub f)
  (h : c ≥ 0)
  : fn_has_ub (λ x, c * f x) :=
begin
  cases ubf with a ha,
  use c * a,
  apply fn_ub_mul ha h,
end

-- Su desarrollo es
--
-- f : ℝ → ℝ,
-- c : ℝ,
-- ubf : fn_has_ub f,
-- h : c ≥ 0
-- ⊢ fn_has_ub (λ (x : ℝ), c * f x)
--    >> cases ubf with a ha,
-- a : ℝ,
-- ha : fn_ub f a
-- ⊢ fn_has_ub (λ (x : ℝ), c * f x)
--    >> use c * a,
-- ⊢ fn_ub (λ (x : ℝ), c * f x) (c * a)
--    >> apply fn_ub_mul ha h
-- no goals

-- 3ª demostración
-- ===============

example
  (ubf : fn_has_ub f)
  (h : c ≥ 0)
  : fn_has_ub (λ x, c * f x) :=
begin
  rcases ubf with ⟨a, ha⟩,
  exact ⟨c * a, fn_ub_mul ha h⟩,
end

-- 4ª demostración
-- ===============

example
  (h : c ≥ 0)
  : fn_has_ub f → fn_has_ub (λ x, c * f x) :=
begin
  rintro ⟨a, ha⟩,
  exact ⟨c * a, fn_ub_mul ha h⟩,
end

-- 5ª demostración
-- ===============

example
  (h : c ≥ 0)
  : fn_has_ub f → fn_has_ub (λ x, c * f x) :=
λ ⟨a, ha⟩, ⟨c * a, fn_ub_mul ha h⟩