Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
leanprover-community
GitHub Repository: leanprover-community/tutorials
Path: blob/master/src/solutions/03_forall_or.lean
212 views
import data.real.basic
import algebra.group.pi

set_option pp.beta true

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Hence we can compress the above proof to:
-/

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

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

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

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

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

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

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

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

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

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

Even better we could have written the definition
of `non_decreasing` as: ∀ {x₁ x₂}, x₁ ≤ x₂ → f x₁ ≤ f x₂, with curly braces to denote
implicit arguments.
 
But let's leave that aside for now. One possible variation on the above proof is to
use the `specialize` tactic to replace hf by its specialization to the relevant value.
 -/

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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