import MyNat.Definition
import MyNat.Addition
import AdvancedAdditionWorld.Level9 -- succ_ne_zero
namespace MyNat
open MyNat
/-!
# Advanced Addition World

## Level 10: `add_left_eq_zero`

## Important: the definition of `≠`

In Lean, `a ≠ b` is *defined to mean* `(a = b) → false`.
This means that if you see `a ≠ b` you can *literally treat
it as saying* `(a = b) → false`. Computer scientists would
say that these two terms are *definitionally equal*.

The following lemma, `a+b=0 ⟹ b=0`, will be useful in [Inequality World](../InequalityWorld.lean.md).
Let me go through the proof, because it introduces several new
concepts:

* `cases b`, where `b : MyNat`
* `exfalso`
* `apply succ_ne_zero`

We're going to prove `a+b=0 ⟹ b=0`. Here is the
strategy. Each natural number is either `0` or `succ d` for
some other natural number `d`. So we can start the proof
with

`cases b with d`

and then we have two goals, the case `b = 0` (which you can solve easily)
and the case `b = succ d`, which looks like this:

```
a d : MyNat,
h : a + succ d = 0
⊢ succ d = 0
```

Our goal is impossible to prove. However our hypothesis `h`
is also impossible, meaning that we still have a chance!
First let's see why `h` is impossible. We can

`rw [add_succ] at h`

to turn `h` into `h : succ (a + d) = 0`. Because
`succ_ne_zero (a + d)` is a proof that `succ (a + d) ≠ 0`,
it is also a proof of the implication `succ (a + d) = 0 → false`.
Hence `succ_ne_zero (a + d) h` is a proof of `false`!
Unfortunately our goal is not `false`, it's a generic
false statement.

Recall however that the `exfalso` command turns any goal into `false`
(it's logically OK because `false` implies every proposition, true or false).

## Lemma
If `a` and `b` are natural numbers such that `a + b = 0` then `b = 0`.
-/
lemma add_left_eq_zero {{a b : MyNat}} (h : a + b = 0) : b = 0 := by
  cases b with
  | zero =>
    rfl
  | succ d =>
    rw [add_succ] at h
    exfalso
    apply succ_ne_zero (a + d)
    exact h

/-!
Next up [Level 11](./Level11.lean.md)
-/


/-
UVa CS 2102 Discrete Math, Semester Exam 2.

This is an individual evaluation. You may not
communicate with anyone about it by any means
for any reason whether directly or indirectly,
while you, or anyone else in this class with
whom you might be communicating, directly or
indirectly, has not yet taken and completed
the submission of their exam. You may not get
information from anyone else that could help
you with this exam, nor may you allow any of
your information to be so obtained by others.
Violations of these rules will result in the
failure of the exam, of the course, and can
be referred to the Honor Committee. Please
signal that you understand by signing the
Honor Pledge electronically when you submit
your exam. Some students will be on travel
or otherwise unable to take the exam today.
Do not make answers or any other knowledge
of the exam available until your instructors
indicate that all exams have been submitted.
That will take up to one full week.

The exam has six questions for a total of
100 points with an extra credit question
worth two points. Several of the questions
have several parts. In these cases, the total
points for a question are evenly distributed
over the parts.

If you don't know how to solve a particular
problem, just move on the the next problem.
Come back to any problems you skipped when
you are finished with all the problems you
know how to solve without difficuty.

This is an open book, open your-own-notes
exam.

Turn off all communications software on all
of your electronic devices before starting
this test. Turn off your cell phone. Turn off
your smart watch. Exit from Skype, messenger,
messages, Facetime, Signal, WhatsApp, and
all other such services.
-/

/-
The first three questions of the exam
use definitions that we enclose within
a namespace. The end  of the namespace
is after question 3. You can basically
ignore this except for a brief note in
the instructions for question 3, below.
-/
namespace quantifiers

/-
The following sequence of three problems
uses two defintions that we now provide.
We assume there are people, and that one
person can like another. We formalize
these assumptions in the following axioms.
We assume that there are people, formally
represented as terms of a type, Person;
and we assume that and given person, p1,
might Like another person, p2, represented
as a predicate, Likes, taking two arguments
of type Person. We intepret "Likes p1 p2"
as asserting that the person, p1, likes the
person, p2.
-/

axiom Person : Type
axiom Likes : Person → Person → Prop

/-
1. [30 points in 5 parts with 6 points each]

For each part of this problem, write a formal
proposition in Lean that expresses the given
propositions given in English. Give you answers
by filling them in in place of the holes.
-/

/-
1a. Everyone likes someone.
-/

def everyoneLikesSomeone : Prop :=
  (∀ (P: Person), ∃ (S : Person), Likes P S)

/-
1b. There is someone nobody likes.
-/

def thereIsSomeoneNobodyLikes : Prop :=
  (∃ (S : Person), ∀ P : Person, ¬Likes P S)

/-
1c. Someone likes everyone.
-/
def someoneLikesEveryone : Prop :=
  (∃ (S : Person), ∀ P : Person, Likes S P)

/-
1d. There is someone who doesn't like anyone.
-/

def thereIsSomeoneWhoLikesNoOne : Prop :=
  (∃ ( S : Person), ∀ P : Person, ¬Likes S P)

/-
1e. A predicate can be understood as defining
a relation. The pairs in the relation defined
by the Likes predicate are all of those pairs
of persons, (p1, p2) such that "Likes p1 p2" is
true. What you are to express formally here is
the proposition that "Likes is symmetric."
-/
def likesIsSymmetric : Prop :=
  ∀ p1 p2 : Person, Likes p1 p2 ↔ Likes p2 p1

/-
2. [5 points] Define a new predicate, LikedBy,
true for Persons p1 and p2 if "Likes p1 p2".
Remember that we formalize a predicate as a
function. You should use the Likes predicate
in formulating your answer.
-/

def LikedBy (p2 p1 : Person) : Prop :=
  Likes p1 p2

/-

3. [10 points]
You are to prove that "if there is someone who
nobody likes, then if Likes is also a symmetric
relation, then there is someone who doesn't like
anybody." Give your formal proof in place of the
hole.

Note: When you introduce the premises, they
will appear in your context with the name of
the enclosing "quantifiers" namespace attached.
E.g., you'll see "quantifiers.likesIsSymmetric".
If/when you want to use such a value, leave off
the name of the namespace. For example, you
would simply refer to the preceding value as
likesIsSymmetric.
-/
open classical

example :
    thereIsSomeoneNobodyLikes →
    likesIsSymmetric →
    thereIsSomeoneWhoLikesNoOne
:=
begin
  assume SNL,
  assume LIS,
  apply exists.elim SNL,
  assume S,
  assume Pfunc,
  -- have Sfunc := Pfunc S,
  apply exists.intro,
    assume P,
    assume like,
    have likeo := LIS S P,
    have likesp := likeo.1,
    have likeps := likesp like,
    have pf := Pfunc P,
    exact pf likeps,
    -- cases em (Likes P S) with l nl,
    --   contradiction,
    --   assume l,
    --   have Ls := LIS S P,
    --   have PS := Ls l,
    --   contradiction,
end

-- end of quantifiers namespace
end quantifiers

/-
4. [10 points] Implication and bi-implication

Prove each of the following propositions. Hint: Don't get distracted
by seemingly confusing complexities. Just focus on the overall form
and content of the propositions to be proved.
-/

-- 4a. Note: the conjecture to be proved is an implication.
example :
(¬ ∃ (a b c n : ℕ), a > 0 ∧ b > 0 ∧ c > 0 ∧ n > 2 ∧ a^n + b^n = c^n) →
1 + 1 = 2 :=
begin
  assume func,
  exact rfl,
end

-- 4b.
example :
1 + 1 = 0 →
(¬ ∃ (a b c n : ℕ), a > 0 ∧ b > 0 ∧ c > 0 ∧ n > 2 ∧ a^n + b^n = c^n) :=
begin
  assume ooz,
  assume func,
  have contra : 1 + 1 ≠ 0 :=
    begin
      apply nat.no_confusion,
    end,
  contradiction,
end

/-
Extra Credit [2 points[]]. What is the common name for the conjecture,
¬ ∃ (a b c n : ℕ), a > 0 ∧ b > 0 ∧ c > 0 ∧ n > 2 → a^n + b^n = c^n.

Your Answer: Fermat's Last Theorem

-/

-- 4c. [15 points] Prove that conjunction is associative.

example : ∀ (P Q R : Prop), P ∧ Q ∧ R ↔ (P ∧ Q) ∧ R :=
begin
  intros P Q R,
  apply iff.intro,
    assume pqr,
      have p := pqr.1,
      have q := pqr.2.1,
      have r := pqr.2.2,
      exact and.intro (and.intro p q) r,
    assume paqr,
      have paq := paqr.1,
      have r := paqr.2,
      have p := paq.1,
      have q := paq.2,
      exact and.intro p (and.intro q r),
end

/-
5. [20 points] Boolean Satisfiability.
-/

-- 5a. Find a solution; the only witnesses you may use are true and false.

example : ∃ (P Q : Prop), ((P ∧ ¬ Q) ∨ (Q ∧ ¬ P)) ∧ ¬ Q :=
begin
  apply exists.intro true,
  apply exists.intro false,
  split,
    apply or.inl,
      split,
        exact true.intro,
        exact false.elim,
      exact false.elim,
end

-- 5b. Show there is no satisfying solution for the following formula.
open classical

example : ¬ ∃ (P Q : Prop), ((P ∧ ¬ Q) ∨ (Q ∧ ¬ P)) ∧ ¬ (Q ∨ P) :=
begin
  assume func,
  apply exists.elim func,
  assume P,
  assume Pf,
  apply exists.elim Pf,
  assume Q,
  assume Qf,
  cases Qf with q1 q2,
    cases q1 with qq q3,
      have p := qq.1,
      have f : false := q2 (or.inr p),
      exact f,
      have q := q3.1,
      have f : false := q2 (or.inl q),
      exact f,
  -- cases em P with p np,
  --   cases em Q with q nq,
  --     have nqop := Qf.2,
  --     have f : false := nqop (or.inl q),
  --     exact f,
  --     have nqop := Qf.2,
  --     have f : false := nqop (or.inr p),
  --     exact f,
  --     cases em Q with q nq,
  --     have nqop := Qf.2,
  --     have f : false := nqop(or.inl q),
  --     exact f,
  --     have pnqqnp := Qf.1,
  --     cases pnqqnp with pnq qnp,
  --     have p := pnq.1,
  --     exact np p,
  --     have q := qnp.1,
  --     exact nq q,
end

/-
6. [10 points] Classical reasoning. Prove the following conjecture.
You may use the axiom of the excluded middle.
-/

open classical

example: ∀(A B: Prop),
  ¬(A ∧ B) → (¬A ∨ ¬B) :=
begin
  intros A B,
  assume nab,
  cases em A with a na,
    cases em B with b nb,
      have f : false := nab (and.intro a b),
      exact false.elim f,
      exact or.inr nb,
      exact or.inl na,
end


-- La_suma_de_los_n_primeros_impares_es_n^2.lean
-- La suma de los n primeros impares es n^2.
-- José A. Alonso Jiménez
-- Sevilla, 3 de septiembre de 2021
-- ---------------------------------------------------------------------

-- ---------------------------------------------------------------------
-- En Lean, se puede definir el n-ésimo número primo por
--    def impar (n : ℕ) := 2 * n + 1
-- Además, en la librería finset están definidas las funciones
--    range :: ℕ → finset ℕ
--    sum :: finset α → (α → β) → β
-- tales que
-- + (range n) es el conjunto de los n primeros números naturales. Por
--   ejemplo, el valor de (range 3) es {0, 1, 2}.
-- + (sum A f) es la suma del conjunto obtenido aplicando la función f a
--   los elementos del conjunto finito A. Por ejemplo, el valor de
--   (sum (range 3) impar) es 9.
--
-- Demostrar que la suma de los n primeros números impares es n².
-- ---------------------------------------------------------------------

import data.finset
import tactic.ring
open nat

set_option pp.structure_projections false

variable (n : ℕ)

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

-- 1ª 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

-- 2ª 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_nf, },
end


import data.nat.basic
import tactic.interval_cases

open nat

variable {n : ℕ}

-- 1ª demostración
example :
  (n % 3 = 0) ∨ (n % 3 = 1) ∨ (n % 3 = 2) :=
begin
  induction n with n hn,
  { exact or.inl (zero_mod 3), },
  { rw [succ_eq_add_one, add_mod],
    cases hn with hn hn,
    { rw hn,
      exact or.inr (or.inl rfl) },
    { cases hn with hn hn,
      { rw hn,
        exact or.inr (or.inr rfl) },
      { rw hn,
        exact or.inl rfl }}},
end

-- 1ª demostración
example :
  (n % 3 = 0) ∨ (n % 3 = 1) ∨ (n % 3 = 2) :=
begin
  induction n with n hn,
  { exact or.inl (zero_mod 3), },
  { rw [succ_eq_add_one, add_mod],
    rcases hn with hn | hn | hn;
      { rw hn, tauto }},
end

-- 3ª solución
example :
  (n % 3 = 0) ∨ (n % 3 = 1) ∨ (n % 3 = 2) :=
begin
  have : n % 3 < 3 := mod_lt n zero_lt_three,
  interval_cases n % 3; tauto,
end

-- 4ª demostración
example :
  (n % 3 = 0) ∨ (n % 3 = 1) ∨ (n % 3 = 2) :=
match n % 3, @nat.mod_lt n 3 dec_trivial with
| 0,   _ := or.inl rfl
| 1,   _ := or.inr $ or.inl rfl
| 2,   _ := or.inr $ or.inr rfl
| k+3, h := absurd h dec_trivial
end


import game.order.level09
import data.real.basic -- imports the real numbers ℝ

namespace xena -- hide

-- World name : Sup and Inf

/-
# Chapter 3 : Sup and Inf

## Level 1 : Upper bounds
-/

/-
Let $X$ be a set of real numbers.

We say a real number $b$ is an *upper bound* for $X$ if every $x \in X$ is at most $b$.
-/

definition is_upper_bound (S : set ℝ) (x : ℝ) := ∀ s ∈ S, s ≤ x

/-
Here is an easy fact about upper bounds, which we shall prove below:
If $X \subseteq Y$ are two sets of reals, and $b$ is an upper bound for $Y$,
then it's also an upper bound for $X$.

You can prove this easily in Lean using the `change` tactic.
-/

/- Lemma
If $X \subseteq Y$ are two sets of reals, and $b$ is an upper bound for $Y$,
then it's also an upper bound for $X$.
-/
lemma upper_bounds_mono (X Y : set ℝ) (h1 : X ⊆ Y) (b : ℝ) : is_upper_bound Y b → is_upper_bound X b :=
begin
  intro h,
  intro j,
  intro k,
  apply h,
  change ∀j, j ∈ X → j ∈ Y at h1,
  apply h1,
  exact k,
end

end xena -- hide


import data.real.basic

-- now we have the field of real numbers

-- So now we can build the complex numbers as an ordered pair of reals.
structure complex : Type :=
(re' : ℝ) (im' : ℝ)

notation `ℂ` := complex

namespace complex

-- now some computer science boilerplate.

-- First define the two projections from the complexes back to the reals.
-- These are great examples of "eliminators" -- functions on the complex numbers.
definition re : ℂ → ℝ
| ⟨x, y⟩ := x

definition im : ℂ → ℝ
| ⟨x, y⟩ := y

-- You can also use this ⟨x, y⟩ notation to make the constructor.
-- Here the 0's are (0 : ℝ)
definition zero : ℂ := ⟨0, 0⟩

-- zero notation
instance : has_zero ℂ := ⟨complex.zero⟩

#check (0 : ℂ) -- now works

-- how to make 3 + 4i
example : ℂ := ⟨3, 4⟩

-- Now we should prove that the constructor applied to the eliminators
-- gets us back to where we started.
theorem eta (z : ℂ) : (⟨re z, im z⟩ : ℂ) = z := by cases z with x y; refl

-- Now we should prove the extensionality lemma for complex numbers;
-- two complex numbers are equal if and only if their real and imaginary
-- parts are equal. One way is trivial; here's the other way.
theorem ext (z w : ℂ) (Hre : re z = re w) (Him : im z = im w) :
z = w :=
begin
  cases w with x y,
  rw ←eta z,
  -- this is the goal now:
  show (⟨re z, im z⟩ : ℂ) = ⟨x, y⟩,
  rw Hre,
  rw Him,
  -- the goal is now true by definition
  show (⟨x, y⟩ : ℂ) = ⟨x, y⟩,
  refl
end

-- complex conjugation is a great example of seeing the constructor
-- and eliminator both in action.
definition conj : ℂ → ℂ
| ⟨x, y⟩ := ⟨x, -y⟩

definition add : ℂ → ℂ → ℂ := sorry -- try filling this in

-- add the notation
instance : has_add ℂ := ⟨complex.add⟩

definition mul : ℂ → ℂ → ℂ := sorry -- try filling this in

-- add the notation
instance : has_mul ℂ := ⟨complex.mul⟩

-- Can you prove this?
theorem add_mul (a b c : ℂ) :
(a + b) * c = a * c + b * c := sorry

-- Can you construct terms of these types?

instance : add_comm_group ℂ := sorry

instance : comm_ring ℂ := sorry

end complex


/-
Copyright (c) 2020 Yury Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury Kudryashov
-/
import algebra.quaternion
import analysis.inner_product_space.basic

/-!
# Quaternions as a normed algebra

In this file we define the following structures on the space `ℍ := ℍ[ℝ]` of quaternions:

* inner product space;
* normed ring;
* normed space over `ℝ`.

## Notation

The following notation is available with `open_locale quaternion`:

* `ℍ` : quaternions

## Tags

quaternion, normed ring, normed space, normed algebra
-/

localized "notation `ℍ` := quaternion ℝ" in quaternion
open_locale real_inner_product_space

noncomputable theory

namespace quaternion

instance : has_inner ℝ ℍ := ⟨λ a b, (a * b.conj).re⟩

lemma inner_self (a : ℍ) : ⟪a, a⟫ = norm_sq a := rfl

lemma inner_def (a b : ℍ) : ⟪a, b⟫ = (a * b.conj).re := rfl

instance : inner_product_space ℝ ℍ :=
inner_product_space.of_core
{ inner := has_inner.inner,
  conj_sym := λ x y, by simp [inner_def, mul_comm],
  nonneg_re := λ x, norm_sq_nonneg,
  definite := λ x, norm_sq_eq_zero.1,
  add_left := λ x y z, by simp only [inner_def, add_mul, add_re],
  smul_left := λ x y r, by simp [inner_def] }

lemma norm_sq_eq_norm_sq (a : ℍ) : norm_sq a = ∥a∥ * ∥a∥ :=
by rw [← inner_self, real_inner_self_eq_norm_mul_norm]

instance : norm_one_class ℍ :=
⟨by rw [norm_eq_sqrt_real_inner, inner_self, norm_sq.map_one, real.sqrt_one]⟩

@[simp, norm_cast] lemma norm_coe (a : ℝ) : ∥(a : ℍ)∥ = ∥a∥ :=
by rw [norm_eq_sqrt_real_inner, inner_self, norm_sq_coe, real.sqrt_sq_eq_abs, real.norm_eq_abs]

noncomputable instance : normed_division_ring ℍ :=
{ dist_eq := λ _ _, rfl,
  norm_mul' := λ a b, by { simp only [norm_eq_sqrt_real_inner, inner_self, norm_sq.map_mul],
                           exact real.sqrt_mul norm_sq_nonneg _ } }

noncomputable instance : normed_algebra ℝ ℍ :=
{ norm_algebra_map_eq := norm_coe,
  to_algebra := quaternion.algebra }

instance : has_coe ℂ ℍ := ⟨λ z, ⟨z.re, z.im, 0, 0⟩⟩

@[simp, norm_cast]

@[simp, norm_cast] lemma coe_complex_add (z w : ℂ) : ↑(z + w) = (z + w : ℍ) := by ext; simp
@[simp, norm_cast] lemma coe_complex_mul (z w : ℂ) : ↑(z * w) = (z * w : ℍ) := by ext; simp
@[simp, norm_cast] lemma coe_complex_zero : ((0 : ℂ) : ℍ) = 0 := rfl
@[simp, norm_cast] lemma coe_complex_one : ((1 : ℂ) : ℍ) = 1 := rfl
@[simp, norm_cast] lemma coe_real_complex_mul (r : ℝ) (z : ℂ) : (r • z : ℍ) = ↑r * ↑z :=
by ext; simp
@[simp, norm_cast] lemma coe_complex_coe (r : ℝ) : ((r : ℂ) : ℍ) = r := rfl

/-- Coercion `ℂ →ₐ[ℝ] ℍ` as an algebra homomorphism. -/
def of_complex : ℂ →ₐ[ℝ] ℍ :=
{ to_fun := coe,
  map_one' := rfl,
  map_zero' := rfl,
  map_add' := coe_complex_add,
  map_mul' := coe_complex_mul,
  commutes' := λ x, rfl }

@[simp] lemma coe_of_complex : ⇑of_complex = coe := rfl

end quaternion


/-
Proof strategies.

- direct proof: use established facts

- by negation: to prove ¬ P, assume P;
  show that this yields a contradiction,
  from which a proof of false can then
  be derived. This shows P → false, and
  that is the definition of ¬ P.

  - by contradiction : to prove P, assume
    ¬ P and show that this leads to a
    contradiction, from which a proof of
    false can be derived. This proves
    ¬ P → false, which is equivalent to
    ¬ ¬ P. The apply the *classical* rule
    of negation elimination to deduce P.with

- Classical rule of negation elimination:
  ∀ (P : Prop), ¬ ¬ P ¬ P

  Prove 0 = 0 by contradiction

  We want to prove P (0 = 0). Assume ¬ 0 = 0,
  and show that this leads to a contradiction. But by the
  reflexive property of equality, which says everything is equal to itself,
  we know timmedate that 0 = 0. That gives us a direct contradiction
  between ¬ (0 = 0) and (0 = 0). From such a contradiction we
  can derive a proof of false, showing that ¬ (0 = 0) → false.
  And this just means ¬ (0 = 0). Then by the classical negation
  elimination, this imlies 0 = 0.

    - Today: proof by induction.
-/

-- Answer to question about why contradictions imply false
axiom P : Prop
axiom p : P
axiom np : ¬ P -- P → false
#check np p

/-
Proof by negation english language example

Prove that the square root of two is NOT rational

Prove ¬ sqrt(2) is rational.#check

Proof:
Assume sqrt(2) is rational. In this case, we can write sqrt(2)
as some fraction a/b. We now want to show that this leads to
a contradiction...

Proof by negation ≠ proof by contradiction

-/

/-

Prove this: ∀ (m : ℕ), 0 + m = m

Proof: By the definition of addition,
and specifically by the first of the
two cases, which tells us that for any
m, 0 + m = m.

-/

def my_add : ℕ → ℕ → ℕ
| nat.zero m := m
| (nat.succ n') m := nat.succ (my_add n' m)

-- 0 + m = m
-- (1 + n') + m = 1 + (n' + m)

/-
Not needed for final

example : ∀ (m : ℕ), my_add 0 m = m
| _ := by simp [my_add]
-/

/-
Many proofs are accomplished by mere
simplification of both sides of some
equation *using function definitions*
that are already known.
-/

/-
If one is being precise, however, there
are some unexpected consequences. One is
that sometimes something that looks easy
turns out to be a bit more complicated.

For example, try to prove this (using)
only what we know so far.
-/

example : ∀ (n : ℕ), my_add n 0 = n
| _ := by simp [my_add]

/-
We have no rule (yet) for adding zero
on the right, so simplifying using the
definition of my_add doesn't work.with
Instead, we need to try a whole different
proof strategy.
-/

/-
Here's the idea:

- every inductively defined type has a
  corresponding induction rule

- It's a rule for showing that some
  proposition is true for *every* value
  of the given type

- The induction principle for ℕ is this:
  ∀ P : ℕ → Prop,
  P 0 →
  ((∀ n' : ℕ), P n' → P (nat.succ n')) →
  ∀ n, P n

In other words, for any predicate/property
P, to show ∀ (n : ℕ), P n, it *suffices*
to show the following:
    (1) P 0,
    (2) (∀ n' : ℕ), P n' → P (nat.succ n')
-/

/-
Example: We want to prove ∀ n, n + 0 = n.
(1) Case: n = 0. By first rule of my_add.
(2) Case: n = nat.succ n':
    Show P n' → P (nat.succ n').
    What does P n' say? my_add n' 0 = n'
    So *assume* P n', now show P (nat.succ n')
    That is, show (my_add (nat.succ n') 0 = (nat.succ n'))

    Using my_add
    Simp: nat.succ (my_add n' 0) = nat.succ n'
    Apply induction hypothesis: nat.succ n' = nat.succ n'
    Finish by reflexive property of equality
-/

/-
Sum numbers from 0 to n is n * (succ n) / 2.

Proof by induction. We will apply the principle of induction
for the natural numbers to two smaller proofs: one for n = 0,
and one that shows that if the formula is true for some n > 0,
then it must also be true succ n'.

Base case: prove (P 0): show sum from 0 to 0 = (0 * 1)/ 2 = 0
Inductive case: Show P n' → P (n' + 1)
Assume P n'. The sum from 0 to n' = n' * (n' + 1) / 2.
Show (P (n' + 1)):
The sum from 0 to n'++1 = (n' + 1)((n' +1)+1)/2.

1+2+3+4+5 if we assume this is 5*6/2
(1+2+3+4+5)+6 show this is 6*7/2
sum 0 to 5 + 6! = 5*6/2 +6

The sum from 0 to (n' + 1) = sum from 0 to n' + (n' + 1)
                           = (n'*(n+1)/2) + (n' + 1)
                           ...
                           = (n' + 1)((n'+1)+1)/2

-/


import tactic.linarith

namespace mth1001

section negating_quantifiers

/-
We *can* deal with negated quantified statements using `intro`, as we did before for other
negated statements.
-/

-- Exercise 081:
-- Finish the proof below using `specialize` and `linarith`.
-- *Note*, `linarith` can prove `false` if there is a contradiction amongst the premises.
example : ¬(∀ x : ℤ, 2 * x + 6 = 0) :=
begin
  intro h,
  sorry
end

/-
A more natural approach is to recognise that `¬(∀ x, P x)` can always be rewritten as
`∃ x, ¬(P x)`, where `P` is any function (more accurately a *predicate*) depending on `x`.
The Lean tactic `push_neg`, which we've seen before, accomplishes this transformation.
-/

-- Exercise 082:
-- Below, `push_neg` transforms the goal into `∃ x : ℤ, 2 * x + 6 ≠ 0`.
example : ¬(∀ x : ℤ, 2 * x + 6 = 0) :=
begin
  push_neg,
  sorry
end

-- Exercise 083:
-- Solve the following using `push_neg`
example : ¬(∀ x : ℤ, x > 5) :=
begin
  sorry
end

/-
Negating existential quantifiers follows a similar principle. We begin by using `intro`.
-/

-- Exercise 084:
-- Use `cases` and `linarith` to prove the following result.
example : ¬(∃ x : ℤ, (x + 2 ≥ 3) ∧ (2 * x + 10 ≤ 8)) :=
begin
  intro h,
  sorry
end

-- Exercise 085:
/-
An alternative approach is to recognise that `¬(∃ x, P x)` is equivalent to `∀ x, ¬(P x)`.

Lean offers this method via `push_neg`. Finish the proof below.
-/
example : ¬(∃ x : ℤ, (x + 2 ≥ 3) ∧ (2 * x + 10 ≤ 8)) :=
begin
  push_neg,
  intro x,
  by_cases h : x + 2 < 3,
  { sorry, },
  { sorry, },
end

end negating_quantifiers

end mth1001


import betweenness_world.level03 --hide
open IncidencePlane --hide

/-
# Betweenness World

## Level 4: another version of the previous level...

To solve this level, the mathematical proof in paper will be given to you **inside the Hint Box** (right below).
Remember that you can use theorem statements from previous worlds. Make an effort to write the proof by your own.
You may find the proof of the previous level very helpful for this one.

-/

/- Hint : Click here for a hint, in case you get stuck.
**Claim:** Given two different collinear points A and B, there is a third C that shares the same line with them and satisfies A * B * C.

**Proof:** Let `r` be the line that is incident with the points A and B.

**(i)** Let us assume that A ≠ B ∧ A ≠ C ∧ B ≠ C. By the first axiom of order `different_of_between`, since A * B * C, then we prove that
A ≠ B ∧ A ≠ C ∧ B ≠ C.

**(ii)** Let us assume that ∃ ℓ, such that A ∈ ℓ ∧ B ∈ ℓ ∧ C ∈ ℓ. By the first axiom of order `collinear_of_between`, since A * B * C, then
we prove that ∃ ℓ, such that A ∈ ℓ ∧ B ∈ ℓ ∧ C ∈ ℓ. Let this line be called `s`. Then, A ∈ s ∧ B ∈ s ∧ C ∈ s.

**(iii)** Let us assume that r = s. By the lemma `equal_lines_of_contain_two_points`, since A ≠ B, A ∈ r, A ∈ s, B ∈ r and B ∈ s, then we prove that
r = s. Because C ∈ s, which we proved in **(ii)**, and r = s, then we prove that C ∈ r.

Hence, we have shown that given two different collinear points A and B, there is a third C that shares the same line with them and satisfies A * B * C.

Still bewildered? Click on "View source" (located on the top right corner of the game screen) to see the solution.
-/

variables {Ω : Type} [IncidencePlane Ω] --hide
variables {A B C P Q R : Ω} --hide
variables {ℓ r s t : Line Ω} --hide

/- Lemma :
Given two different collinear points A and B, there is a third C that shares the same line with them and satisfies A * B * C.
-/
lemma between_points_share_line_v2 (hAr : A ∈ r) (hBr : B ∈ r) :
	(A * B * C) → C ∈ r :=
begin
    intro H,
    have h1 : A ≠ B ∧ A ≠ C ∧ B ≠ C,
    {
        exact different_of_between H,
    },
    have h2 : ∃ ℓ, A ∈ ℓ ∧ B ∈ ℓ ∧ C ∈ ℓ,
    {
        apply collinear_of_between,
        exact H,
    },
    cases h2 with s hs,
    have h3 : r = s,
    {
        exact equal_lines_of_contain_two_points h1.1 hAr hs.1 hBr hs.2.1,
    },
    rw h3,
    exact hs.2.2,

end


/- TACTICS -/

namespace TBA

-- definitions from last week
-- NOTE: We renamed it from `Nat'` for consistency. The new namespace makes sure we don't use the
-- standard library `Nat`.
inductive Nat : Type where
  | zero : Nat
  | succ (n : Nat) : Nat

open Nat

def add (m n : Nat) : Nat :=
  match n with
  | zero   => m
  | succ n => succ (add m n)

-- With this command we add a notation for `add`. From now on we will be able to write `m + n` for
-- `add m n`. The 65 denotes how strongly the operator should bind to what's adjacent to it.
-- The `priority` means that Lean will prefer it over the built-in `+`.
infix:65 (priority := high) " + " => add

def mul (m n : Nat) : Nat :=
  match n with
  | zero   => zero
  | succ n => (mul m n) + m

-- We also want a notation for `mul`, with a higher binding strength than addition so that
-- `a + b * c` means `a + (b * c`)`.
infix:70 (priority := high) " * " => mul

inductive LE : Nat → Nat → Prop where
  | refl (n : Nat) : LE n n
  | succ : LE m n → LE m (succ n)

-- lower binding strength than either addition or multiplication
infix:50 (priority := high) " ≤ " => LE

-- Let's start by reproving some theorems from last week, but this time with tactics!
-- useful tactics:
-- * `induction ... with ...`
-- * `rw [f]` to unfold applications of a function `f`
-- * `rw [h]` to rewrite every `a` to `b` if `h : a = b`
-- * `apply/exact`
-- * `simp/simp_all`... are powerful and basically always useful, though make sure that you could also
--   do the proof without them
theorem zero_add : zero + n = n := by
  induction n with
  | zero      =>
  | succ n ih =>

theorem le_add : m ≤ m + n := by

-- Alright, let's start automating more!
attribute [simp] add mul
-- These definitions will now automatically be unfolded when you use `simp/simp_all`

-- This one is a bit more tricky, you might need to prove a helper lemma!
theorem add_comm : n + m = m + n := by

-- Associativity can be proven in a similar way.
theorem add_assoc : (m + n) + k = m + (n + k) := by

def one := succ zero

theorem mul_one : m * one = m := by

-- To prove associativity of multiplication, you might have to come up with
-- some more lemmas about multiplication first. Some are similar to the above laws of
-- addition, some use both addition and multiplication ("distributivity" is the keyword).

theorem mul_assoc : (m * n) * k = m * (n * k) := by

-- Remember the structures for semigroups and monoids which we defined last week?
structure Semigroup (α : Type) where
  mul   : α → α → α
  assoc : mul (mul a b) c = mul a (mul b c)

structure Monoid (α : Type) extends Semigroup α where
  e     : α
  e_mul : mul e a = a
  mul_e : mul a e = a

-- You should now be able to instantiate two of them, including proofs!
def Nat_add_Monoid : Monoid Nat := _

def Nat_mul_Monoid : Monoid Nat := _

end TBA


-- Producto_por_funcion_acotada_superiormente.lean
-- Si c ≥ 0 y f está acotada superiormente, entonces c * f también lo está.
-- José A. Alonso Jiménez <https://jaalonso.github.io>
-- Sevilla, 30-noviembre-2022
-- ---------------------------------------------------------------------

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

import data.real.basic

variables {f : ℝ → ℝ}
variables {a c : ℝ}

-- (cota_superior f a) se verifica si a es una cota superior de f.
def cota_superior (f : ℝ → ℝ) (a : ℝ) : Prop := ∀ x, f x ≤ a

-- (acotada_sup f) se verifica si f tiene cota superior.
def acotada_sup (f : ℝ → ℝ) := ∃ a, cota_superior f a

-- Lema auxiliar
-- ============

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

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

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

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

example
  (hf : acotada_sup f)
  (h : c ≥ 0)
  : acotada_sup (λ x, c * f x) :=
begin
  cases hf with a ha,
  use c * a,
  apply cota_superior_mul ha h,
end

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

example
  (hf : acotada_sup f)
  (h : c ≥ 0)
  : acotada_sup (λ x, c * f x) :=
begin
  rcases hf with ⟨a, ha⟩,
  exact ⟨c * a, cota_superior_mul ha h⟩,
end

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

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

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

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


import tactic
import data.zmod.basic
import field_theory.finite.basic
/-

# Prove the theorem, due to Kraichik, asserting that 13|2^70+3^70

This is the sixth question in Sierpinski's book "250 elementary problems
in number theory".

-/

example : 13 ∣ 2^70 + 3^70 :=
begin
  use 192550423461109399456637645953021,
  norm_num,
end

/-

thoughts

2,4,8,3,6,12=-1,-2,-4,-8,-3,-6,1 2^12=1 mod 13

3,9,1
-/

example : 13 ∣ 2^70 + 3^70 :=
begin
  rw ← zmod.nat_coe_zmod_eq_zero_iff_dvd,
  push_cast,
  change (2 : zmod 13) ^ 70 + (3 : zmod 13) ^ 70 = 0,
  have h0 : nat.prime 13 := by norm_num,
  haveI : fact (nat.prime 13) := ⟨h0⟩,
  have h1 : (2 : zmod 13)^12 = 1,
  { apply zmod.pow_card_sub_one_eq_one,
    intro h2,
    have h3 : ((2 : ℕ) : zmod 13) = 0,
      assumption_mod_cast,
    rw zmod.nat_coe_zmod_eq_zero_iff_dvd at h3,
    revert h3,
    norm_num,
  },
  have h2 : (3 : zmod 13)^3 = 1,
    refl,
  conv_lhs begin
    congr,
    rw (show 70 = 12 * 5 + 10, by norm_num),
    skip,
    rw (show 70 = 3 * 23 + 1, by norm_num),
  end,
  rw [pow_add, pow_add, pow_mul, pow_mul, h1, h2],
  simp,
  refl,
end


-- We import all of Lean's standard tactics
import tactic

/-!
# Logic

We will develop the basic theory of following five basic logical symbols

* `→` ("implies" -- type with `\l`)
* `¬` ("not" -- type with `\not` or `\n`)
* `∧` ("and" -- type with `\and` or `\an`)
* `↔` ("iff" -- type with `\iff` or `\lr`)
* `∨` ("or" -- type with `\or` or `\v`

# Tactics you will need to know

* `intro`
* `exact`
* `apply`
* `rw`
* `cases`
* `split`
* `left`
* `right`

See `README.md` in `src/week_1` for an explanation of what these
tactics do.

Note that there are plenty of other tactics, and indeed once you've
"got the hang of it" you might want to try tactics such as `cc`,
`tauto` and its variant `tauto!`, `finish`, and `library_search`.

# What to do

The `example`s are to demonstrate things to you. They sometimes
use tactics you don't know. You can look at them but you don't
need to touch them.

The `theorem`s and `lemma`s are things which have no proof. You need to change
the `sorry`s into proofs which Lean will accept.

This paragraph is a comment, by the way. One-line comments
are preceded with `--`.
-/

-- We work in a "namespace". All this means is that whenever it
-- looks like we've defined a new theorem called `id`, its full
-- name is `xena.id`. Which is good because `id` is already
-- defined in Lean.
namespace xena

-- Throughout this namespace, P Q and R will be arbitrary (variable)
-- true-false statements.
variables (P Q R : Prop)

/-!
## implies (→)

To prove the theorems in this section, you will need to know about
the tactics `intro`, `apply` and `exact`. You might also like
the `assumption` tactic.
-/

/-- Every proposition implies itself. -/
theorem id : P → P :=
begin
  -- Prove this using `intro` and `exact`
  sorry
end

/-
Note that → isn't associative!
Try working out `false → (false → false) and (false → false) → false
-/

example : (false → (false → false)) ↔ true := by simp
example : ((false → false) → false) ↔ false := by simp

-- in Lean, `P → Q → R` is _defined_ to mean `P → (Q → R)`
-- Here's a proof of what I just said.
example : (P → Q → R) ↔ (P → (Q → R)) :=
begin
  -- look at the goal!
  refl -- true because ↔ is reflexive
end

theorem imp_intro : P → Q → P :=
begin
  -- remember that by definition the goal is P → (Q → P).
  -- Prove this proposition using `intro` and `exact`.
  -- Experiment. Can you prove it using `intros` and `assumption`?
  sorry
end

/-- If we know `P`, and we also know `P → Q`, we can deduce `Q`. -/
lemma modus_ponens : P → (P → Q) → Q :=
begin
  -- You might find the `apply` tactic useful here.
  sorry
end

/-- implication is transitive -/
lemma imp_trans : (P → Q) → (Q → R) → (P → R) :=
begin
  -- The tactics you know should be enough
  sorry
end

-- This one is a "relative modus ponens" -- in the
-- presence of P, if Q -> R and Q then R.
lemma forall_imp : (P → Q → R) → (P → Q) → (P → R) :=
begin
  -- `intros hPQR hPQ hP,` would be a fast way to start.
  -- Make sure you understand what is going on there, if you use it.
  sorry
end

/-

### not

`not P`, with notation `¬ P`, is *defined* to mean `P → false` in Lean,
i.e., the proposition that P implies false. You can easily check with
a truth table that P → false and ¬ P are equivalent.

We develop a basic interface for `¬`.
-/

-- I'll prove this one for you
theorem not_iff_imp_false : ¬ P ↔ (P → false) :=
begin
  -- true by definition
  refl
end

theorem not_not_intro : P → ¬ (¬ P) :=
begin
  intro hP,
  rw not_iff_imp_false,
  -- You can use `rw not_iff_imp_false` to change `¬ X` into `X → false`.
  -- But you don't actually have to, because they are the same *by definition*
  sorry,
end

-- Here is a funny alternative proof! Can you work out how it works?
example : P → ¬ (¬ P) :=
begin
  apply modus_ponens,
end

-- Here is a proof which does not use tactics at all, but uses lambda calculus.
-- It is called a "term mode" proof. We will not be discussing term mode
-- much in this course. It is a cool way to do basic logic proofs, but
-- it does not scale well in practice.
example : P → ¬ (¬ P) :=
λ hP hnP, hnP hP

-- This is "modus tollens". Some mathematicians think of it as
-- "proof by contradiction".
theorem modus_tollens : (P → Q) → (¬ Q → ¬ P) :=
begin
  sorry,
end

-- This one cannot be proved using constructive mathematics!
-- You _have_ to use a tactic like `by_contra` (or, if you're happy
-- to cheat, the full "truth table" tactic `tauto!`.
-- Try it without using these, and you'll get stuck!
theorem double_negation_elimination : ¬ (¬ P) → P :=
begin
  sorry,
end

/-!

### and

The hypothesis `hPaQ : P ∧ Q` in Lean, is equivalent to
hypotheses `hP : P` and `hQ : Q`.

If you have `hPaQ` as a hypothesis, and you want to get to
`hP` and `hQ`, you can use the `cases` tactic.

If you have `⊢ P ∧ Q` as a goal, and want to turn the goal
into two goals `⊢ P` and `⊢ Q`, then use the `split` tactic.

Note that after `split` it's good etiquette to use braces
e.g.

example (hP : P) (hQ : Q) : P ∧ Q :=
begin
  split,
  { exact hP },
  { exact hQ }
end

but for this sort of stuff I think principled indentation
is OK

```
example (hP : P) (hQ : Q) : P ∧ Q :=
begin
  split,
    exact hP,
  exact hQ
end
```

-/

theorem and.elim_left : P ∧ Q → P :=
begin
  -- I would recommend starting with
  -- `intro hPaQ,` and then `cases hPaQ with hP hQ`.
  sorry
end

theorem and.elim_right : P ∧ Q → Q :=
begin
  sorry
end

-- fancy term mode proof
example : P ∧ Q → Q := λ hPaQ, hPaQ.2

theorem and.intro : P → Q → P ∧ Q :=
begin
  -- remember the `split` tactic.
  sorry
end

/-- the eliminator for `∧` -/
theorem and.elim : P ∧ Q → (P → Q → R) → R :=
begin
  sorry,
end

/-- The recursor for `∧` -/
theorem and.rec : (P → Q → R) → P ∧ Q → R :=
begin
  sorry
end

/-- `∧` is symmetric -/
theorem and.symm : P ∧ Q → Q ∧ P :=
begin
  sorry
end

-- term mode proof
example : P ∧ Q → Q ∧ P :=
λ ⟨hP, hQ⟩, ⟨hQ, hP⟩

/-- `∧` is transitive -/
theorem and.trans : (P ∧ Q) → (Q ∧ R) → (P ∧ R) :=
begin
  -- The `rintro` tactic will do `intro` and `cases` all in one go.
  -- If you like, try starting this proof with `rintro ⟨hP, hQ⟩` if you want
  -- to experiment with it. Get the pointy brackets with `\<` and `\>`,
  -- or both at once with `\<>`.
  sorry,
end

/-
Recall that the convention for the implies sign →
is that it is _right associative_, by which
I mean that `P → Q → R` means `P → (Q → R)` by definition.
Now note that if `P` implies `Q → R`
then this means that `P` and `Q` together, imply `R`,
so `P → Q → R` is logically equivalent to `(P ∧ Q) → R`.

We proved that `P → Q → R` implied `(P ∧ Q) → R`; this was `and.rec`.
Let's go the other way.
-/

lemma imp_imp_of_and_imp : ((P ∧ Q) → R) → (P → Q → R) :=
begin
  sorry,
end

/-!

### iff

The basic theory of `iff`.

In Lean, to prove `P ∧ Q` you have to prove `P` and `Q`.
Similarly, to prove `P ↔ Q` in Lean, you have to prove `P → Q`
and `Q → P`. Just like `∧`, you can uses `cases h` if you have
a hypothesis `h : P ↔ Q`, and `split` if you have a goal `⊢ P ↔ Q`.
-/

/-- `P ↔ P` is true for all propositions `P`, i.e. `↔` is reflexive. -/
theorem iff.refl : P ↔ P :=
begin
  -- start with `split`
  sorry,
end

-- If you get stuck, there is always the "truth table" tactic `tauto!`
example : P ↔ P :=
begin
  tauto!, -- the "truth table" tactic.
end

-- refl tactic also works
example : P ↔ P :=
begin
  refl -- `refl` knows that `=` and `↔` are reflexive.
end

/-- `↔` is symmetric -/
theorem iff.symm : (P ↔ Q) → (Q ↔ P) :=
begin
  sorry
end

-- NB there is quite a devious proof of this using `rw`.

-- show-off term mode proof
example : (P ↔ Q) → (Q ↔ P) :=
λ ⟨hPQ, hQP⟩, ⟨hQP, hPQ⟩

/-- `↔` is commutative -/
theorem iff.comm : (P ↔ Q) ↔ (Q ↔ P) :=
begin
  sorry
end

-- without rw or cc this is painful!
/-- `↔` is transitive -/
theorem iff.trans :  (P ↔ Q) → (Q ↔ R) → (P ↔ R) :=
begin
  sorry,
end

-- This can be done constructively, but it's hard. You'll need to know
-- about the `have` tactic to do it. Alternatively the truth table
-- tactic `tauto!` will do it.
theorem iff.boss : ¬ (P ↔ ¬ P) :=
begin
  sorry
end

-- Now we have iff we can go back to and.

/-!
### ↔ and ∧
-/

/-- `∧` is commutative -/
theorem and.comm : P ∧ Q ↔ Q ∧ P :=
begin
  sorry,
end

-- fancy term-mode proof
example : P ∧ Q ↔ Q ∧ P :=
⟨and.symm _ _, and.symm _ _⟩

-- Note that ∧ is "right associative" in Lean, which means
-- that `P ∧ Q ∧ R` is _defined to mean_ `P ∧ (Q ∧ R)`.
-- Associativity can hence be written like this:
/-- `∧` is associative -/
theorem and_assoc : ((P ∧ Q) ∧ R) ↔ (P ∧ Q ∧ R) :=
begin
  sorry,
end

/-!

## Or

`P ∨ Q` is true when at least one of `P` and `Q` are true.
Here is how to work with `∨` in Lean.

If you have a hypothesis `hPoQ : P ∨ Q` then you
can break into the two cases `hP : P` and `hQ : Q` using
`cases hPoQ with hP hQ`

If you have a _goal_ of the form `⊢ P ∨ Q` then you
need to decide whether you're going to prove `P` or `Q`.
If you want to prove `P` then use the `left` tactic,
and if you want to prove `Q` then use the `right` tactic.

-/

-- recall that P, Q, R are Propositions. We'll need S for this one.
variable (S : Prop)

-- You will need to use the `left` tactic for this one.
theorem or.intro_left : P → P ∨ Q :=
begin
  sorry
end

theorem or.intro_right : Q → P ∨ Q :=
begin
  sorry,
end

/-- the eliminator for `∨`. -/
theorem or.elim : P ∨ Q → (P → R) → (Q → R) → R :=
begin
  sorry
end

/-- `∨` is symmetric -/
theorem or.symm : P ∨ Q → Q ∨ P :=
begin
  sorry
end

/-- `∨` is commutative -/
theorem or.comm : P ∨ Q ↔ Q ∨ P :=
begin
  sorry,
end

/-- `∨` is associative -/
theorem or.assoc : (P ∨ Q) ∨ R ↔ P ∨ Q ∨ R :=
begin
  sorry,
end

/-!
### More about → and ∨
-/

theorem or.imp : (P → R) → (Q → S) → P ∨ Q → R ∨ S :=
begin
  sorry,
end

theorem or.imp_left : (P → Q) → P ∨ R → Q ∨ R :=
begin
  sorry,
end

theorem or.imp_right : (P → Q) → R ∨ P → R ∨ Q :=
begin
  sorry,
end

theorem or.left_comm : P ∨ Q ∨ R ↔ Q ∨ P ∨ R :=
begin
  -- Try rewriting `or.comm` and `or.assoc` to do this one quickly.
  sorry,
end

/-- the recursor for `∨` -/
theorem or.rec : (P → R) → (Q → R) → P ∨ Q → R :=
begin
  sorry,
end

theorem or_congr : (P ↔ R) → (Q ↔ S) → (P ∨ Q ↔ R ∨ S) :=
begin
  sorry,
end

/-!

### true and false

`true` is a true-false statement, which can be proved with the `trivial` tactic.

`false` is a true-false statment which can only be proved if you manage
to find a contradiction within your assumptions.

If you manage to end up with a hypothesis `h : false` then there's quite
a funny way to proceed, which we now explain.

If you have `h : P ∧ Q` then you can uses `cases h with hP hQ` to split
into two cases.

If you have `h : false` then what do you think happens if we do `cases h`?
Hint: how many cases are there?
-/

/-- eliminator for `false` -/
theorem false.elim : false → P :=
begin
  sorry,
end

theorem and_true_iff : P ∧ true ↔ P :=
begin
  sorry,
end

theorem or_false_iff : P ∨ false ↔ P :=
begin
  sorry,
end

-- false.elim is handy for this one
theorem or.resolve_left : P ∨ Q → ¬P → Q :=
begin
  sorry,
end

-- this one you can't do constructively
theorem or_iff_not_imp_left : P ∨ Q ↔ ¬P → Q :=
begin
  sorry,
end

end xena


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

import tactic -- imports all the Lean tactics

/-!

# Sets in Lean, sheet 1 : ∪ ∩ ⊆ and all that

Lean doesn't have "abstract" sets, it only has *subsets* of a type. If `X : Type` is a type
then the type of subsets of `X` is called `set X`. A term `A : set X`
can be thought of in three ways:

1) A set of elements of `X` (i.e. a set of elements all of which have type `X`);
2) A subset of `X`;
3) An element of the power set of `X`;
4) A function from `X` to `Prop` (sending the elements of `A` to `true` and the other ones to `false`)

So `set X` could have been called `subset X` or `powerset X`; I guess they chose `set X`
because it was the shortest.

Note that `X` is a type, but `A` is a term; the type of `A` is `set X`. This means
that `a : A` doesn't make sense. What we say instead is `a : X` and `a ∈ A`.
Of course `a ∈ A` is a true-false statement, so `a ∈ A : Prop`.

All the sets `A`, `B`, `C` etc we consider will be subsets of `X`.
If `x : X` then `x` may or may not be an element of `A`, `B`, `C`,
but it will always be a term of type `X`.

-/

-- set up variables
variables
  (X : Type) -- Everything will be a subset of `X`
  (A B C D : set X) -- A,B,C are subsets of `X`
  (x y z : X) -- x,y,z are elements of `X` or, more precisely, terms of type `X`

/-

# subset (`⊆`), union (`∪`) and intersection (`∩`)

Here are some mathematical facts:

`A ⊆ B` is equivalent to `∀ x, x ∈ A → x ∈ B`;
`x ∈ A ∪ B` is equivalent to `x ∈ A ∨ x ∈ B`;
`x ∈ A ∩ B` is equivalent to `x ∈ A ∧ x ∈ B`.

All of these things are true *by definition* in Lean. Let's
check this.

-/

lemma subset_def : A ⊆ B ↔ ∀ x, x ∈ A → x ∈ B :=
begin
  -- ↔ is reflexive so `refl` works because LHS is defined to be equal to RHS
  refl
end

lemma mem_union_iff : x ∈ A ∪ B ↔ x ∈ A ∨ x ∈ B :=
begin
  refl
end

lemma mem_inter_iff : x ∈ A ∩ B ↔ x ∈ A ∧ x ∈ B :=
iff.rfl -- you don't even have to go into tactic mode to prove this

/-

So now to change one side of these `↔`s to the other, you can
`rw` the appropriate lemma, or you can just use `change`. Or
you can ask yourself whether you need to do this at all.

Let's prove some theorems.

-/

example : A ⊆ A :=
begin
  sorry,
end

example : A ⊆ B → B ⊆ C → A ⊆ C :=
begin
  sorry,
end

example : A ⊆ A ∪ B :=
begin
  sorry,
end

example : A ∩ B ⊆ A :=
begin
  sorry,
end

example : A ⊆ B → A ⊆ C → A ⊆ (B ∩ C) :=
begin
  sorry,
end

example : B ⊆ A → C ⊆ A → B ∪ C ⊆ A :=
begin
  sorry,
end

example : A ⊆ B → C ⊆ D → A ∪ C ⊆ B ∪ D :=
begin
  sorry,
end

example : A ⊆ B → C ⊆ D → A ∩ C ⊆ B ∩ D :=
begin
  sorry,
end


import Analysis.Set.Basic

namespace Set

def image (f : α → β) (s : Set α) : Set β := { y : β | ∃ x ∈ s, y = f x }

theorem memImage {s : Set α} {f : α → β} (y : β) :
  y ∈ s.image f ↔ ∃ x ∈ s, y = f x :=  Iff.rfl

theorem memImageOfMem {s : Set α} {f : α → β} {x : α} (hx : x ∈ s) :
  f x ∈ s.image f := Exists.intro x ⟨hx, rfl⟩

def preimage (f : α → β) (s : Set β) : Set α := { x : α | f x ∈ s }

theorem memPreimage {s : Set β} {f : α → β} {x : α} :
  x ∈ s.preimage f ↔ f x ∈ s := Iff.rfl

theorem preimageUniv (f : α → β) : univ.preimage f = univ := rfl

theorem preimageMono (f : α → β) {s t : Set β} (hst : s ⊆ t) :
  s.preimage f ⊆ t.preimage f :=
  λ x hx => hst _ hx

theorem preimageInter (f : α → β) (s t : Set β) :
  (s ∩ t).preimage f = s.preimage f ∩ t.preimage f := rfl

theorem preimageId (s : Set α) : preimage id s = s := rfl

end Set


section propositional

variables P Q R : Prop

------------------------------------------------
-- Proposições de dupla negaço:
------------------------------------------------

theorem doubleneg_intro :
  P → ¬¬P  :=
begin
  intros hp hpf, --"Suponha P, ¬P"
  have hf : false := hpf hp, --"Como P → ⊥ e P, logo ⊥"
  exact hf, --"Imediato (tenho meu alvo nos meus dados)"
end

theorem doubleneg_elim :
  ¬¬P → P  :=
begin
  intro hpf,
  by_cases p : P,
  exact p,
  exfalso,
  have hboom : false := hpf p,
  exact hboom,
end

theorem doubleneg_law :
  ¬¬P ↔ P  :=
begin
  split,
  apply doubleneg_elim,
  apply doubleneg_intro,
end

------------------------------------------------
-- Comutatividade dos ∨,∧:
------------------------------------------------

theorem disj_comm :
  (P ∨ Q) → (Q ∨ P)  :=
begin
  intro poq,
  cases poq with hp hq,
  right,
  exact hp,
  left,
  exact hq,
end

theorem conj_comm :
  (P ∧ Q) → (Q ∧ P)  :=
begin
  intro peq,
  cases peq with hp hq,
  split,
  exact hq,
  exact hp,
end

------------------------------------------------
-- Proposições de interdefinabilidade dos →,∨:
------------------------------------------------

theorem impl_as_disj_converse :
  (¬P ∨ Q) → (P → Q)  :=
begin
  intro h_fp_or_q,
  intro p,
  cases h_fp_or_q with hp hq,
  exfalso,
  contradiction,
  exact hq,
end

theorem disj_as_impl :
  (P ∨ Q) → (¬P → Q)  :=
begin
  intro h,
  intro np,
  cases h with hp hq,
  exfalso,
  contradiction,
  exact hq,
end

------------------------------------------------
-- Proposições de contraposição:
------------------------------------------------

theorem impl_as_contrapositive :
  (P → Q) → (¬Q → ¬P)  :=
begin
  intros hpq hnq hp,
  have hq := hpq hp,
  contradiction,
end

theorem impl_as_contrapositive_converse :
  (¬Q → ¬P) → (P → Q)  :=
begin
  intros hnq_np p,
  by_cases hq : Q,
  exact hq,
  exfalso,
  have hnp := hnq_np hq,
  have hboom := hnp p,
  contradiction,
end

theorem contrapositive_law :
  (P → Q) ↔ (¬Q → ¬P)  :=
begin
  split,
  apply impl_as_contrapositive,
  apply impl_as_contrapositive_converse,
end

------------------------------------------------
-- A irrefutabilidade do LEM:
------------------------------------------------

theorem lem_irrefutable :
  ¬¬(P∨¬P)  :=
begin
  intro n_p_np,
  apply n_p_np,
  right,
  intro p,
  have p_or_q : P ∨ ¬P,
  left,
  exact p,
  contradiction,
end

------------------------------------------------
-- A lei de Peirce
------------------------------------------------

theorem peirce_law_weak :
  ((P → Q) → P) → ¬¬P  :=
begin
  intros pq_p np,
  have p : P,
  apply pq_p,
  intro p,
  exfalso,
  contradiction,
  contradiction,
end

------------------------------------------------
-- Proposições de interdefinabilidade dos ∨,∧:
------------------------------------------------

theorem disj_as_negconj :
  P∨Q → ¬(¬P∧¬Q)  :=
begin
  intro poq,
  intro np_nq,
  cases np_nq with np nq,
  cases poq,
  contradiction,
  contradiction,
end

theorem conj_as_negdisj :
  P∧Q → ¬(¬P∨¬Q)  :=
begin
  intro p_and_q,
  intro np_or_nq,
  cases p_and_q with p q,
  cases np_or_nq,
  contradiction,
  contradiction,
end

------------------------------------------------
-- As leis de De Morgan para ∨,∧:
------------------------------------------------

theorem demorgan_disj :
  ¬(P∨Q) → (¬P ∧ ¬Q)  :=
begin
  intro n_p_or_q,
  split,
  intro p,
  have p_or_q : P ∨ Q,
  left,
  exact p,
  contradiction,
  intro q,
  have p_or_q : P ∨ Q,
  right,
  exact q,
  contradiction,
end

theorem demorgan_disj_converse :
  (¬P ∧ ¬Q) → ¬(P∨Q)  :=
begin
  intros np_and_nq p_or_q,
  cases np_and_nq with np nq,
  cases p_or_q,
  contradiction,
  contradiction,
end

theorem demorgan_conj :
  ¬(P∧Q) → (¬Q ∨ ¬P)  :=
begin
  intro n_p_and_q,
  by_contra n_nq_or_np,
  apply n_p_and_q,
  split,
  by_contra np,
  apply n_nq_or_np,
  right,
  assumption,
  by_contra nq,
  apply n_nq_or_np,
  left,
  assumption,
end

theorem demorgan_conj_converse :
  (¬Q ∨ ¬P) → ¬(P∧Q)  :=
begin
  intros nq_or_np p_and_q,
  cases p_and_q with p q,
  cases nq_or_np,
  contradiction,
  contradiction,
end

theorem demorgan_conj_law :
  ¬(P∧Q) ↔ (¬Q ∨ ¬P)  :=
begin
  split,
  apply demorgan_conj,
  apply demorgan_conj_converse,
end

theorem demorgan_disj_law :
  ¬(P∨Q) ↔ (¬P ∧ ¬Q)  :=
begin
  split,
  apply demorgan_disj,
  apply demorgan_disj_converse,
end

------------------------------------------------
-- Proposições de distributividade dos ∨,∧:
------------------------------------------------

theorem distr_conj_disj :
  P∧(Q∨R) → (P∧Q)∨(P∧R)  :=
begin
  intro p_and_pr,
  cases p_and_pr with p q_and_r,
  cases q_and_r with q r,
  left,
  split,
  repeat {assumption},
  right,
  split,
  repeat {assumption},
end

theorem distr_conj_disj_converse :
  (P∧Q)∨(P∧R) → P∧(Q∨R)  :=
begin
  intro pq_or_pr,
  cases pq_or_pr with pq pr,
  cases pq with p q,
  split,
  exact p,
  left,
  exact q,
  split,
  cases pr with p r,
  exact p,
  right,
  cases pr with p r,
  exact r,
end

theorem distr_disj_conj :
  P∨(Q∧R) → (P∨Q)∧(P∨R)  :=
begin
  intro p_or_qandr,
  cases p_or_qandr with p qandr,
  split,
  left,
  exact p,
  left,
  exact p,
  cases qandr with q r,
  split,
  right,
  exact q,
  right,
  exact r,
end

theorem distr_disj_conj_converse :
  (P∨Q)∧(P∨R) → P∨(Q∧R)  :=
begin
  intro porq_and_porr,
  cases porq_and_porr with p_or_q p_or_r,
  cases p_or_r with p r,
  left,
  exact p,
  cases p_or_q with p q,
  left,
  exact p,
  right,
  split,
  exact q,
  exact r,
end

------------------------------------------------
-- Currificação
------------------------------------------------

theorem curry_prop :
  ((P∧Q)→R) → (P→(Q→R))  :=
begin
  intros pandq_imp_r p q,
  apply pandq_imp_r,
  split,
  repeat {assumption},
end

theorem uncurry_prop :
  (P→(Q→R)) → ((P∧Q)→R)  :=
begin
  intros p_q_r p_and_q,
  apply p_q_r,
  cases p_and_q with p q,
  exact p,
  cases p_and_q with p q,
  exact q,
end

------------------------------------------------
-- Reflexividade da →:
------------------------------------------------

theorem impl_refl :
  P → P  :=
begin
  intro p,
  exact p,
end

------------------------------------------------
-- Weakening and contraction:
------------------------------------------------

theorem weaken_disj_right :
  P → (P∨Q)  :=
begin
  intro p,
  left,
  exact p,
end

theorem weaken_disj_left :
  Q → (P∨Q)  :=
begin
  intro q,
  right,
  exact q,
end

theorem weaken_conj_right :
  (P∧Q) → P  :=
begin
  intro p_and_q,
  cases p_and_q with p q,
  exact p,
end

theorem weaken_conj_left :
  (P∧Q) → Q  :=
begin
  intro p_and_q,
  cases p_and_q with p q,
  exact q,
end

theorem conj_idempot :
  (P∧P) ↔ P :=
begin
  split,
  intro p_and_p,
  cases p_and_p with p p,
  exact p,
  intro p,
  split,
  repeat {exact p},
end

theorem disj_idempot :
  (P∨P) ↔ P  :=
begin
  split,
  intro p_or_p,
  cases p_or_p with p p,
  repeat {exact p},
  intro p,
  left,
  exact p,
end

end propositional

----------------------------------------------------------------

section predicate

variable U : Type
variables P Q : U -> Prop

------------------------------------------------
-- As leis de De Morgan para ∃,∀:
------------------------------------------------

theorem demorgan_exists :
  ¬(∃x, P x) → (∀x, ¬P x)  :=
begin
  intros ex_px u pu,
  apply ex_px,
  existsi u,
  exact pu,
end

theorem demorgan_exists_converse :
  (∀x, ¬P x) → ¬(∃x, P x)  :=
begin
  intros all_npx ex_px,
  cases ex_px with u p_u,
  have np_u := all_npx u,
  contradiction,
end

theorem demorgan_forall :
  ¬(∀x, P x) → (∃x, ¬P x)  :=
begin
  intro n_all_p,
  by_contra nex_np,
  apply n_all_p,
  intro u,
  by_contra npu,
  apply nex_np,
  existsi u,
  intro pu,
  contradiction,
end

theorem demorgan_forall_converse :
  (∃x, ¬P x) → ¬(∀x, P x)  :=
begin
  intro h,
  cases h with x npx,
  intro all_p,
  have px := all_p x,
  contradiction,
end

theorem demorgan_forall_law :
  ¬(∀x, P x) ↔ (∃x, ¬P x)  :=
begin
  split,
  apply demorgan_forall,
  apply demorgan_forall_converse,
end

theorem demorgan_exists_law :
  ¬(∃x, P x) ↔ (∀x, ¬P x)  :=
begin
  split,
  apply demorgan_exists,
  apply demorgan_exists_converse,
end

------------------------------------------------
-- Proposições de interdefinabilidade dos ∃,∀:
------------------------------------------------

theorem exists_as_neg_forall :
  (∃x, P x) → ¬(∀x, ¬P x)  :=
begin
  intros ex_p all_np,
  cases ex_p with u pu,
  have npu := all_np u,
  contradiction,
end

theorem forall_as_neg_exists :
  (∀x, P x) → ¬(∃x, ¬P x)  :=
begin
  intros all_p ex_np,
  cases ex_np with u npu,
  have pu := all_p u,
  contradiction,
end

theorem forall_as_neg_exists_converse :
  ¬(∃x, ¬P x) → (∀x, P x)  :=
begin
  intro nex_np,
  by_contra nall_px,
  apply nall_px,
  intro u,
  by_contra npu,
  apply nex_np,
  existsi u,
  intro pu,
  contradiction,

end

theorem exists_as_neg_forall_converse :
  ¬(∀x, ¬P x) → (∃x, P x)  :=
begin
  intro n_all_np,
  by_contra nex_p,
  apply n_all_np,
  intro u,
  by_contra pu,
  apply nex_p,
  existsi u,
  exact pu,
end

theorem forall_as_neg_exists_law :
  (∀x, P x) ↔ ¬(∃x, ¬P x)  :=
begin
  split,
  apply forall_as_neg_exists,
  apply forall_as_neg_exists_converse,
end

theorem exists_as_neg_forall_law :
  (∃x, P x) ↔ ¬(∀x, ¬P x)  :=
begin
  split,
  apply exists_as_neg_forall,
  apply exists_as_neg_forall_converse,
end

------------------------------------------------
--  Proposições de distributividade de quantificadores:
------------------------------------------------

theorem exists_conj_as_conj_exists :
  (∃x, P x ∧ Q x) → (∃x, P x) ∧ (∃x, Q x)  :=
begin
  intro ex_p_and_q,
  cases ex_p_and_q with u pu_and_qu,
  cases pu_and_qu with pu qu,
  split,
  existsi u,
  assumption,
  existsi u,
  assumption,
end

theorem exists_disj_as_disj_exists :
  (∃x, P x ∨ Q x) → (∃x, P x) ∨ (∃x, Q x)  :=
begin
  intro ex_p_or_q,
  cases ex_p_or_q with u pu_or_qu,
  cases pu_or_qu with pu pq,
  left,
  existsi u,
  assumption,
  right,
  existsi u,
  assumption,
end

theorem exists_disj_as_disj_exists_converse :
  (∃x, P x) ∨ (∃x, Q x) → (∃x, P x ∨ Q x)  :=
begin
  intro exp_or_exq,
  cases exp_or_exq with exp exq,
  cases exp with u pu,
  existsi u,
  left,
  assumption,
  cases exq with u qu,
  existsi u,
  right,
  assumption,
end

theorem forall_conj_as_conj_forall :
  (∀x, P x ∧ Q x) → (∀x, P x) ∧ (∀x, Q x)  :=
begin
  intro all_p_and_q,
  split,
  intro u,
  have pu_and_qu := all_p_and_q u,
  cases pu_and_qu with pu qu,
  assumption,
  intro x,
  have px_and_qx := all_p_and_q x,
  cases px_and_qx with px qx,
  assumption,
end

theorem forall_conj_as_conj_forall_converse :
  (∀x, P x) ∧ (∀x, Q x) → (∀x, P x ∧ Q x)  :=
begin
  intro allp_and_allq,
  intro a,
  cases allp_and_allq with all_p all_q,
  split,
  have pa := all_p a,
  assumption,
  have qa := all_q a,
  assumption,
end

theorem forall_disj_as_disj_forall_converse :
  (∀x, P x) ∨ (∀x, Q x) → (∀x, P x ∨ Q x)  :=
begin
  intro allp_or_allq,
  intro u,
  cases allp_or_allq with allp allq,
  have pu := allp u,
  left,
  assumption,
  have qu := allq u,
  right,
  assumption,
end

/-SECTION "NOT THEOREMS"

theorem forall_disj_as_disj_forall :
  (∀x, P x ∨ Q x) → (∀x, P x) ∨ (∀x, Q x)  :=
begin
  intro all_p_or_q,
  left,
  intro u,
  have pu_or_qu := all_p_or_q u,
  cases pu_or_qu with pu qu,
  exact pu,
  --NÃO VALIDO
end

theorem exists_conj_as_conj_exists_converse :
  (∃x, P x) ∧ (∃x, Q x) → (∃x, P x ∧ Q x)  :=
begin
  intro exp_or_exq,
  cases exp_or_exq with exp exq,
  cases exp with u pu,
  existsi u,
  split,
  assumption,
  cases exq with u' qu,
  --NÃO VALIDO
end

-----------------------------------------/

end predicate


import .lecture_26
import data.set

namespace relations

section functions

variables {α β γ : Type} (r : α → β → Prop)
local infix `≺`:50 := r

/-
SINGLE-VALUED BINARY RELATION
-/

def single_valued :=
  ∀ {x : α} {y z : β}, r x y → r x z → y = z

#check @single_valued   -- property of a relation

/-
Exercises: Which of the following are single-valued?
- r = {(0,1), (0,2)}
- r = {(1,0), (2,0)}
- the unit circle on ℝ
- r = {(-1,0), (0,-1), (1,0), (0,1)}
- y = x^2
- x = y^2
- y = + / - square root x
- f(x) = 3x+1
- y = sin x
- x = sin y
-/

/- FUNCTION

A single-valued binary relation is also called a
function (sometimes a functional binary relation).
-/
def function := single_valued r

/-
One possible property of a relation, r, is the
property of being a function, i.e., of r being
single-valued. Single-valuedness is a predicate
on relations. (This idea is isn't definable in
first order predicate logic.)
-/
#check @function

/-
The same vocabulary applies to functions as to
relations, as functions are just special cases
(single-valued) of otherwise arbitrary binary
relations.

As with any relation, for example, a function
has a domain, α, a domain of definition, and a
co-domain, β. As with any relation, the set of
pairs of a function (that we're specifying) is
is some subset of α × β; or equivalently it is
in the powerset of α × β.

When you want to express the idea that you have
an arbitrary relation (possible a function) from
α to β, you may write either of the following:

  - let r ⊆ α × β be any relation from α → β
  - let r ∈ 𝒫 (α × β) be any relation, α → β
  - let r : α → β be any binary relation

Be sure you see that these are equivalent
statements! A key point is that in addition
to a set of pairs a function (or relation)
has a domain of definition and a co-domain.
Keeping track of exactly how a set of pairs
relates to its domain and codomain sets is
essential.
-/

/- FOR A RELATION TO BE "DEFINED" FOR A VALUE

Property: We say that a function is "defined" for some
value, (a : α), if there is some (b : β), such that the
pair, (a,b) is "in" r, i.e., (r a b) is true.
-/

def defined (a : α) := ∃ (b : β), r a b

/-
Examples: Which is partial, which is total?

- the positive square root function for x ∈ ℝ (dom def)
- the positive square root function for x ∈ ℝ, x ≥ 0
-/

/- THE TOTAL vs PARTIAL FUNCTION DICHOTOMY

Property: We say that a function is "total" if it is
defined for every value in its domain. Note that this
usage of the word "total" is completely distinct from
what we learned earlier for relations in general. It's
thus better to use "strongly connected" for relations
to mean every object is related to another in at least
one direction, and to use total to refer to a function
that is defined on every element of its domain.
-/

def total_function := function r ∧ ∀ (a : α), defined r a

/-
At this point we expect that for  a total function, r,
dom_of_def r = domain r. At one key juncture you use
the axiom of set extensionality to convert the goal
as an equality into that goal as a bi-implication.
After that it's basic predicate logical reasoning,
rather than more reasoning in set theory terms.
-/

example : total_function r → dom_of_def r = dom r :=
begin
  assume total_r,
  cases total_r with func_r defall,
  unfold dom_of_def,
  unfold dom,
  apply set.ext,
  assume x,
  split,

  -- forwards
  assume h,
  unfold defined at defall,
  unfold defined at defall,
  -- goal completed

  -- backwards
  assume h,
  exact defall x,

end

/-
With that proof down as an example, we return to complete
our list of properties of functions:
  - total
  - partial
  - strictly partial
Here are the definitions for the remaning two.
-/

def strictly_partial_fun := function r ∧ ¬total_function r
def partial_function := function r -- includes total funs

/-
Mathematicians generally consider the set of partial
functions to include the total functions. We will use
the term "strictly partial" function to mean a function,
f, that is not total, where dom_of_def f ⊊ dom f. (Be
sure you see that the subset symbol here means subset
but not equal. That's what the slash through the bottom
line in the symbol means: strict subset.)
-/

/- SURJECTIVE FUNCTIONS

A function that "covers" its codomain (where every value in
the codomain is an "output" for some value in its domain)
is said to map its domain *onto* its entire codomain.
Mathematicians will say that such a function is "onto,"
or "surjective."
-/

def surjective :=
  total_function r ∧
  ∀ (b : β), ∃ a : α, r a b

/-
Should this be true?
-/

example :
  surjective r →
  image_set r (dom r) = { b : β | true } :=
begin
-- homework (on your own ungraded but please do it!)
end

/-
Which of the following functions are surjective?

- y = log x, viewed as a function from ℝ → ℝ⁺

As written, the question is, um, tricky. Let's
analyze it. Then we'll give simpler questions.

First a little background on logarithmic and
exponential functions. Simply put, exponentiation
raises a base to an exponent to produce an output,
while the logarithm takes and converts it into
the exponent to which the base is raised to give
the input.

From basic algebra, the log (base 10) function,
y = log(x), is defined for any positive real, x,
and is equal to the exponent to which the base
(here 10) must be raised to produce x. Therefore
as usually defined its domain of definition is
the *positive reals* and its co-domain is (*all
of*) the reals.

Now consider the question again. The domain of
definition of log is the positive reals, so if
we expand the domain to all the reals, then the
resulting function becomes partial. On the other
side, if we restrict the range to the positive
reals, then we are excluding from the function
all those values in the interval (0,1) from the
input side in order to restrict the output to
values greater than 0.

Self homework: Graph this function.

- λ x, log x : ℝ+ → ℝ, bijective?
- y = x^2, viewed as a function from ℝ → ℝ
- y = x, viewed as a function from ℝ → ℝ
- y = sin x, viewed as a function from ℝ → ℝ
- y = sin x, as a function from ℝ to [-1,1] ∈ ℝ

-/

/- INJECTIVE FUNCTION

We have seen that for a relation to be a function, it
cannot be "one-to-many" (some x value is associated
with more than one y value). On the other hand, it is
possible for a function to associate many x values
with a single y value. There can be no fan-out from
x/domain values to y/codomain values, but there can
be fan-in from x to y values.

Which is the following functions exhibits "fan-in",
with different x values associated with the same y
values?

y = x
y = sin x
x = 1 (trick question)
y = 1
y = x^2 on ℝ
y = x^2 on ℝ⁺ (the positive reals)
-/

def injective :=
  total_function r ∧
  ∀ {x y : α} {z : β},
    r x z → r y z → x = y
/-
We will often want to know that a function does not
map multiple x values to the same y value. Example:
in a company, we will very like want a function that
maps each employee to an employee ID number *unique*
to that employee. Rather than being "many to one" we
call such a function "one-to-one." We also say that
such a function has the property of being *injective*.
-/

/-
We will often want to know that a function does not
map multiple x values to the same y value. Example:
in a company, we will very like want a function that
maps each employee to an employee ID number *unique*
to that employee. Rather than being "many to one" we
call such a function "one-to-one." We also say that
such a function has the property of being *injective*.
There is yet another way to understand the concept.
-/

/- BIJECTIVE FUNCTIONS
-/

/-
Finally, a function is called one-to-one and onto, or
*bijective* if it is both surjective and injective. In
this case, it has to map every element of its domain
-/

def bijective := surjective r ∧ injective r

/-
An essential property of any bijective relation is that
it puts the elements of the domain and codomain into a
one-to-one correspondence.

That we've assumed that a function is total is important
here. Here's a counterexample: consider the relation from
dom = {1,2,3} to codom = {A, B} with r = {(1,A), (2,B)}.
This function is injective and surjective but it clearly
does not establish a 1-1 correspondence.

We can define what it means for a strictly partial function
to be surjective or injective (we don't do it formally here).
We say that a partial function is surjective or injective if
its domain restriction to its domain of definition (making it
total) meets the definitions given above.

Note that our use of the term, one-to-one, here is
completely distinct from its use in the definition of
an injective function. An injective function is said
to be "one-to-one" in the sense that it's not many to
one: you can't have f(x) = k and f(y)=k where x ≠ y.
A one-to-one correspondence *between two sets*, on the
other hand, is a pairing of elements that associates
each element of one set with a unique single element
of the other set, and vice versa.
-/

/-
Question: Is the inverse of a function always a function.
Think about the function, y = x^2. What is its inverse?
Is it's inverse a function? There's your answer.

A critical property of a bijective function, on the other
hand, is that its inverse is also a bijective function. It
is easy to see: just reverse the "arrows" in a one-to-one
correspondence between two sets. A function who inverse
is a function is said to be invertible (as a function, as
every relation has and inverse that is again a relation).
-/

/-
EXERCISE #1: Prove that the inverse of a
bijective function is a function. Ok, yes,
we will work this one out for you! But you
should really read and understand it. Then
the rest shouldn't be too bad.
-/

example : bijective r → function (inverse r) :=
begin
  /-
    Assume hypothesis
  -/
  assume r_bij,

  /-
  Unfold definitions and, from definitions,
  deduce all the basic facts we'll have to
  work with.
  -/
  cases r_bij with r_sur r_inj,
  cases r_inj with r_tot r_one_to_one,
  cases r_sur with r_tot r_onto,
  unfold total_function at r_tot,
  cases r_tot with r_fun alldef,
  unfold function at r_fun,
  unfold single_valued at r_fun,
  unfold defined at alldef,

  /-
  What remains to be shown is that the
  inverse of r is function. Expanding
  the definition of function, that means
  r inverse is single-valued. Let's see.
  -/
  unfold function,
  unfold single_valued,
  /-
  To show that r inverse (mapping β values
  back to α values) r is single-valued,
  assume that b is some value of type β
  (in the codomain of r) and show that if
  r inverse maps b is mapped to both a1 and
  a2 then a1 = a2.
  -/
  assume b a1 a2 irba1 irba2,
  /-
  Key insight: (inverse r) b a means r a b.
  In other words, r b a is in r inverse (it
  contains the pair (b, a)) if and only if
  (a, b) is in r, i.e., r a b.
  -/
  unfold inverse at irba1 irba2,
  /-
  With those pairs now turned around, by the
  injectivity of r, we're there!
  -/
  apply r_one_to_one irba1 irba2,
end

/-
Just to set expectations: The reality is that
I explored numerous ways of writing this proof.
Often a first proof will be confusing, messy,
etc. Most proofs of theorems you see in most
mathematics books are gems, polished in their
presentations by generations of mathematicians.
It took me a little while to get to this proof
script and the sequence of reasoning steps and
intermediate proof states it traverses.
-/

/- INJECTIVE AND SURJECTIVE *PARTIAL* FUNCTIONS

Okay, we actually are now able to to define just
what is means for a *partial* function to be
injective, surjective, bijective, which is that
it is so when its domain is restricted to its
domain of definition, rendering it total (at
which point the preceding definition applies).
-/

def injectivep := function r ∧ injective (dom_res r (dom_of_def r))
def surjectivep := function r ∧ surjective (dom_res r (dom_of_def r))
def bijectivep := function r ∧ bijective (dom_res r (dom_of_def r))

-- EXERCISE #2: Prove that the inverse of a bijective function is bijective.
example : bijective r → bijective (inverse r) :=
begin
  assume bij_r,
  unfold bijective,
  unfold bijective at bij_r,
  cases bij_r with surj_r inj_r,
  unfold surjective at surj_r,
  unfold injective at inj_r,
  cases surj_r with r_tot r_onto,
  cases inj_r with r_tot r_one_to_one,
  cases r_tot with r_fun alldef,
  unfold function at r_fun,
  unfold single_valued at r_fun,

  apply and.intro _ _,

  unfold surjective,
  unfold total_function,
  split,
  split,
  unfold function,
  unfold single_valued,
  unfold inverse,
  assume b a x rab rxb,
  apply r_one_to_one rab,
  exact rxb,

  assume b,
  apply r_onto b,
  unfold inverse,
  unfold defined at alldef,
  exact alldef,

  unfold injective inverse,
  split,
  unfold total_function,
  split,
  unfold function,
  unfold single_valued,
  assume b a z rab rzb,
  apply r_one_to_one rab rzb,

  assume b,
  unfold defined at alldef,
  apply r_onto b,

  assume c b a rac rab,
  apply r_fun rac rab,
end

/-
EXERCISE #3: Prove that the inverse of the inverse of a bijective
function is that function.
-/
example : bijective r → (r = inverse (inverse r)) :=
begin
  assume bijr,
  unfold inverse,
end

/-
EXERCISE  #4: Formally state and prove that every injective function
has a *function* as an inverse.
-/
example : injective r → function (inverse r) :=
  begin
  assume injr,-- hint: remember recent work
  unfold function,
  unfold single_valued,
  cases injr with r_tot r_one_to_one,
  unfold total_function at r_tot,
  unfold function at r_tot,
  unfold single_valued at r_tot,
  unfold defined at r_tot,
  cases r_tot with r_fun alldef,
  assume x y z rxy rxz,
  unfold inverse at rxy,
  unfold inverse at rxz,
  apply r_one_to_one rxy rxz,
  end

/-
EXERCISE #5. Is bijectivity transitive? In other words, if the
relations, s and r, are both bijective, then is the
composition, s after r, also always bijective? Now
we'll see.
-/

open relations    -- for definition of composition

/-
Check the following proposition. True? prove it for all.
False? Present a counterexample.
-/
def bij_trans (s : β → γ → Prop)  (r : α → β → Prop) :
  bijective r → bijective s → bijective (composition s r) :=
  begin
    assume bijr bijs,
    unfold bijective,

    apply and.intro _ _,
    unfold bijective at bijr bijs,
    cases bijr with r_sur r_inj,
    cases bijs with s_sur s_inj,

    unfold surjective,
    unfold total_function,
    unfold function,
    unfold single_valued,

    unfold surjective at r_sur s_sur,
    unfold total_function at r_sur s_sur,
    cases r_sur with r_tot r_onto,
    cases s_sur with s_tot s_onto,
    unfold function at r_tot s_tot,
    unfold single_valued at r_tot s_tot,
    cases r_tot with r_fun r_alldef,
    cases s_tot with s_fun s_alldef,

    unfold injective at r_inj s_inj,
    cases r_inj with r_tot r_one_to_one,
    cases s_inj with s_tot s_one_to_one,

    unfold total_function at r_tot s_tot,
    unfold function at r_tot s_tot,
    unfold single_valued at r_tot s_tot,
    unfold defined at r_tot s_tot r_alldef s_alldef,

    cases r_tot with r_fun alldef_r,
    cases s_tot with s_fun alldef_s,

    split,
    split,

    unfold composition,
    assume x y z,
    assume h i,
    apply exists.elim h,
    apply exists.elim i,

    assume b sbz_rxb,
    assume t sty_rxt,

    have rxb := sbz_rxb.right,
    have rxt := sty_rxt.right,

    have bt := r_fun rxb rxt,

    have sbz := sbz_rxb.left,
    have sty := sty_rxt.left,
    have stz : s t z := eq.subst bt sbz,
    apply s_fun sty stz,

    assume i,
    unfold defined,
    have sr := composition s r,
    have idd := sr i,
    have idd2 := alldef_r i,

    unfold composition,
    cases idd2 with beta ribeta,
    have idd3 := alldef_s beta,
    cases idd3 with g sbetag,
    apply exists.intro,
    apply exists.intro,
    apply and.intro sbetag ribeta,

    assume g,
    unfold composition,
    have sr := composition s r,
    have idd := s_onto g,
    cases idd with b sbg,
    have idd2:= r_onto b,
    cases idd2 with a rab,
    apply exists.intro,
    apply exists.intro,
    apply and.intro sbg rab,

    unfold bijective at bijr bijs,
    cases bijr with r_sur r_inj,
    cases bijs with s_sur s_inj,

    unfold injective,
    unfold total_function,
    unfold function,
    unfold single_valued,

    unfold surjective at r_sur s_sur,
    unfold total_function at r_sur s_sur,
    cases r_sur with r_tot r_onto,
    cases s_sur with s_tot s_onto,
    unfold function at r_tot s_tot,
    unfold single_valued at r_tot s_tot,
    cases r_tot with r_fun r_alldef,
    cases s_tot with s_fun s_alldef,

    unfold injective at r_inj s_inj,
    cases r_inj with r_tot r_one_to_one,
    cases s_inj with s_tot s_one_to_one,

    unfold total_function at r_tot s_tot,
    unfold function at r_tot s_tot,
    unfold single_valued at r_tot s_tot,
    unfold defined at r_tot s_tot r_alldef s_alldef,

    cases r_tot with r_fun alldef_r,
    cases s_tot with s_fun alldef_s,

    split,
    split,

    unfold composition,
    assume x y z,
    assume h i,
    apply exists.elim h,
    apply exists.elim i,
    assume b sbz_rxb,
    assume t sty_rxt,

    have sbz := sbz_rxb.left,
    have sty := sty_rxt.left,

    have rxb := sbz_rxb.right,
    have rxt := sty_rxt.right,

    have bt := r_fun rxb rxt,

    have stz : s t z := eq.subst bt sbz,
    apply s_fun sty stz,

    assume i,
    unfold defined,
    have sr := composition s r,
    have idd := sr i,
    have idd2 := alldef_r i,

    unfold composition,
    cases idd2 with beta ribeta,
    have idd3 := alldef_s beta,
    cases idd3 with g sbetag,
    apply exists.intro,
    apply exists.intro,
    apply and.intro sbetag ribeta,

    assume i,
    unfold composition,
    have sr := composition s r,
    assume a g,
    assume e_sbg_rib e_sbg_rab,
    cases e_sbg_rib with b sbg_rib,
    cases e_sbg_rab with t stg_rat,

    have sbg := sbg_rib.left,
    have stg := stg_rat.left,

    have rib := sbg_rib.right,
    have rat := stg_rat.right,

    have bt := s_one_to_one sbg stg,

    have rit : r i t := eq.subst bt rib,
    apply r_one_to_one rit rat,
  end

/-
In general, an operation (such as inverse, here) that,
when applied twice, is the identity, is said to be an
involution. Relational inverse on bijective functions
is involutive in this sense.

A visualization: each green ball here goes to a red
ball there and the inverse takes each red ball right
back to the green ball from which it came, leaving
the original green ball as the end result, as well.
An identity function.
-/

end functions
end relations


-- Divisivilidad_productos.lean
-- Si x, y, z ∈ ℕ, entonces x ∣ y * x * z
-- José A. Alonso Jiménez <https://jaalonso.github.io>
-- Sevilla, 6-octubre-2022
-- ---------------------------------------------------------------------

-- ---------------------------------------------------------------------
-- Demostrar que si x, y, z ∈ N, entonces
--    x ∣ y * x * z
-- ----------------------------------------------------------------------

import data.nat.basic
variables x y z : ℕ

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

example : x ∣ y * x * z :=
begin
  have h1 : x ∣ y * x,
    { exact dvd_mul_left x y },
  have h2 : (y * x) ∣ (y * x * z),
    { exact dvd.intro z rfl},
  show x ∣ y * x * z,
    { exact dvd_trans h1 h2},
end

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

example : x ∣ y * x * z :=
dvd_trans (dvd_mul_left x y) (dvd.intro z rfl)

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

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


import tuto_lib

section
/-
The first part of this file makes sure you can negate quantified statements
in your head without the help of `push_neg`.

You need to complete the statement and then use the `check_me` tactic
to check your answer. This tactic exists only for those exercises,
it mostly calls `push_neg` and then cleans up a bit.

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

-- In this section, u denotes a sequence of real numbers
-- f is a function from ℝ to ℝ
-- x₀ and l are real numbers
variables (u : ℕ → ℝ) (f : ℝ → ℝ) (x₀ l : ℝ)

/- Negation of "u tends to l" -/
-- 0062
example : ¬ (∀ ε > 0, ∃ N, ∀ n ≥ N, |u n - l| ≤ ε) ↔
∃ ε > 0, ∀ N, ∃ n ≥ N, |u n - l| > ε
:=
begin
  check_me,
end

/- Negation of "f is continuous at x₀" -/
-- 0063
example : ¬ (∀ ε > 0, ∃ δ > 0, ∀ x, |x - x₀| ≤ δ →  |f x - f x₀| ≤ ε) ↔
∃ ε > 0, ∀ δ > 0, ∃ x, |x-x₀| ≤ δ ∧ |f x - f x₀| > ε
:=
begin
  check_me,
end

/-
In the next exercise, we need to keep in mind that
`∀ x x', ...` is the abbreviation of
`∀ x, ∀ x', ... `.

Also, `∃ x x', ...` is the abbreviation of `∃ x, ∃ x', ...`.
-/

/- Negation of "f is uniformly continuous on ℝ" -/
-- 0064
example : ¬ (∀ ε > 0, ∃ δ > 0, ∀ x x', |x' - x| ≤ δ →  |f x' - f x| ≤ ε) ↔
(∃ ε > 0, ∀ δ > 0, ∃ x x', |x'-x| ≤ δ ∧ |f x' - f x| > ε)
:=
begin
  check_me,
end

/- Negation of "f is sequentially continuous at x₀" -/
-- 0065
example : ¬ (∀ u : ℕ → ℝ, (∀ ε > 0, ∃ N, ∀ n ≥ N, |u n - x₀| ≤ ε) → (∀ ε > 0, ∃ N, ∀ n ≥ N, |(f ∘ u) n - f x₀| ≤ ε))  ↔
∃ u : ℕ → ℝ, (∀ ε > 0, ∃ N, ∀ n ≥ N, |u n - x₀| ≤ ε) ∧ (∃ ε > 0, ∀ N, ∃ n ≥ N, |(f ∘ u) n - f x₀| > ε)
:=
begin
  check_me,
end
end

/-
We now turn to elementary applications of negations to limits of sequences.
Remember that `linarith` can find easy numerical contradictions.

Also recall the following lemmas:

abs_le {x y : ℝ} : |x| ≤ y ↔ -y ≤ x ∧ x ≤ y

ge_max_iff (p q r) : r ≥ max p q  ↔ r ≥ p ∧ r ≥ q

le_max_left p q : p ≤ max p q

le_max_right p q : q ≤ max p q

/-- The sequence `u` tends to `+∞`. -/
def tendsto_infinity (u : ℕ → ℝ) := ∀ A, ∃ N, ∀ n ≥ N, u n ≥ A
-/

-- 0066
example {u : ℕ → ℝ} : tendsto_infinity u → ∀ l, ¬ seq_limit u l :=
begin
  intros h1 l h2,
  cases h1 (l+2) with M h3,
  cases h2 1 (by linarith) with N h4,
  let n := max N M,
  specialize h3 n (le_max_right _ _),
  specialize h4 n (le_max_left _ _),
  rw abs_le at h4,
  linarith,
end

def nondecreasing_seq (u : ℕ → ℝ) := ∀ n m, n ≤ m → u n ≤ u m

-- 0067
example (u : ℕ → ℝ) (l : ℝ) (h : seq_limit u l) (h' : nondecreasing_seq u) :
  ∀ n, u n ≤ l :=
begin
  intros n,
  by_contradiction h2,
  push_neg at h2,
  cases h ((u n - l)/2) (by linarith) with N h3,
  let m := max n N,
  specialize h3 m (le_max_right _ _),
  rw abs_le at h3,
  specialize h' n m (le_max_left _ _),
  linarith,
end

/-
In the following exercises, `A : set ℝ` means that A is a set of real numbers.
We can use the usual notation x ∈ A.

The notation `∀ x ∈ A, ...` is the abbreviation of `∀ x, x ∈ A → ... `

The notation `∃ x ∈ A, ...` is the abbreviation of `∃ x, x ∈ A ∧ ... `.
More precisely it is the abbreviation of `∃ x (H : x ∈ A), ...`
which is Lean's strange way of saying `∃ x, x ∈ A ∧ ... `.
You can convert between these forms using the lemma
  exists_prop {p q : Prop} : (∃ (h : p), q) ↔ p ∧ q

We'll work with upper bounds and supremums.
Again we'll introduce specialized definitions for the sake of exercises, but mathlib
has more general versions.

def upper_bound (A : set ℝ) (x : ℝ) := ∀ a ∈ A, a ≤ x

def is_sup (A : set ℝ) (x : ℝ) := upper_bound A x ∧ ∀ y, upper_bound A y → x ≤ y

Remark: one can easily show that a set of real numbers has at most one sup,
but we won't need this.
-/

-- 0068
example {A : set ℝ} {x : ℝ} (hx : is_sup A x) :
∀ y, y < x → ∃ a ∈ A, y < a :=
begin
  intros y,
  contrapose!,
  exact hx.right y,
  -- by_contradiction h2,
  -- push_neg at h2,
  -- linarith [hx.right y h2],
end

/-
Let's do a variation on an example from file 07 that will be useful in the last
exercise below.
-/

-- 0069
lemma le_of_le_add_all' {x y : ℝ} :
  (∀ ε > 0, y ≤ x + ε) →  y ≤ x :=
begin
  contrapose!,
  intros hxy,
  use (y-x)/2,
  split; linarith, -- NOTE split
end

-- 0070
example {x y : ℝ} {u : ℕ → ℝ} (hu : seq_limit u x)
  (ineg : ∀ n, u n ≤ y) : x ≤ y :=
begin
  by_contradiction hxy,
  push_neg at hxy,
  let ε := (x-y)/2,
  have fact : ε > 0,
  { dsimp [ε], linarith, },
  cases hu ε fact with N hN,
  specialize hN N (by linarith),
  rw abs_le at hN,
  cases hN,
  specialize ineg N,
  have h0 : y > y,
  calc y ≥ u N : ineg
  ... ≥ x - ε : by linarith
  ... > y : by { dsimp [ε], linarith, },
  linarith,
end


import .love12_basic_mathematical_structures_demo

/- # LoVe Exercise 12: Basic Mathematical Structures -/

set_option pp.beta true
set_option pp.generalized_field_notation false

namespace LoVe

/- ## Question 1: Type Classes

Recall the inductive type `btree` we introduced earlier: -/

#check btree

/- The following function takes two trees and attaches copies of the second
tree to each leaf of the first tree. -/

def btree.graft {α : Type} : btree α → btree α → btree α
| btree.empty        u := u
| (btree.node a l r) u := btree.node a (btree.graft l u) (btree.graft r u)

#reduce btree.graft (btree.node 1 btree.empty btree.empty)
  (btree.node 2 btree.empty btree.empty)

/- 1.1. Prove the following two lemmas by structural induction on `t`. -/

lemma btree.graft_assoc {α : Type} (t u v : btree α) :
  btree.graft (btree.graft t u) v = btree.graft t (btree.graft u v) :=
sorry

lemma btree.graft_empty {α : Type} (t : btree α) :
  btree.graft t btree.empty = t :=
sorry

/- 1.2. Declare btree an instance of `add_monoid` using `graft` as addition
operator. -/

#print add_monoid

@[instance] def btree.add_monid {α : Type} : add_monoid (btree α) :=
sorry

/- 1.3. Explain why `btree` with `graft` as addition cannot be declared an
instance of `add_group`. -/

#print add_group

/- 1.4 (**optional**). Prove the following lemma illustrating why `btree` with
`graft` as addition does not constitute an `add_group`. -/

lemma btree.add_left_neg_counterexample :
  ∃x : btree ℕ, ∀ y : btree ℕ, btree.graft y x ≠ btree.empty :=
sorry

/- ## Question 2: Multisets and Finsets

Recall the following definitions from the lecture: -/

#check multiset.elems
#check finset.elems
#check list.elems

/- 2.1. Prove that the multiset of nodes does not change when mirroring a tree.

Hints:

* Perform structural induction on `t`.

* The `cc` tactic also works with set operations. -/

lemma multiset.elems_mirror (t : btree ℕ) :
  multiset.elems (mirror t) = multiset.elems t :=
sorry

/- 2.2. Prove that the finite set of nodes does not change when mirroring a
tree. -/

lemma finset.elems_mirror (t : btree ℕ) :
  finset.elems (mirror t) = finset.elems t :=
sorry

/- 2.3. Show that this does not hold for the list of nodes by providing a
tree `t` for which `nodes_list t ≠ nodes_list (mirror t)`.

If you define a suitable counterexample, the proof below will succeed. -/

def rotten_tree : btree ℕ :=
sorry

#eval list.elems rotten_tree
#eval list.elems (mirror rotten_tree)

lemma list.elems_mirror_counterexample :
  ∃t : btree ℕ, list.elems t ≠ list.elems (mirror t) :=
begin
  apply exists.intro rotten_tree,
  exact dec_trivial
end

end LoVe


import ..fglib
import ..basic
import .data

namespace FG

/- ## Invertible 3x3 Matrix -/

/- `is_invertible` is a `Prop` of `mat3`. -/
namespace mat3

def is_invertible (A : mat3) : Prop :=
  ∃(B : mat3), A * B = 1

/- A `mat3` is unitary if it's the inverse of its transpose. -/
@[simp] def is_unitary (A : mat3) : Prop :=
  A * A.transpose = 1

lemma unitary_is_inverible (A : mat3) :
  is_unitary A → is_invertible A :=
begin
  intro h,
  use A.transpose,
  assumption
end

/- Get the inverse of a `mat3`, by calculating the adjugate matrix.
  It's noncomputable because of the dependency of `real.div`. -/
noncomputable def inverse (A : mat3): mat3 :=
begin
  let detA := A.det,
  let AT := A.transpose,
  let subdet := λ(a b c d : ℝ), a * d - b * c,
  let M : mat3 :=
  ⟨ ⟨ subdet AT.y.y AT.y.z AT.z.y AT.z.z, subdet AT.y.x AT.y.z AT.z.x AT.z.z, subdet AT.y.x AT.y.y AT.z.x AT.z.y ⟩,
    ⟨ subdet AT.x.y AT.x.z AT.z.y AT.z.z, subdet AT.x.x AT.x.z AT.z.x AT.z.z, subdet AT.x.x AT.x.y AT.z.x AT.z.y ⟩,
    ⟨ subdet AT.x.y AT.x.z AT.y.y AT.y.z, subdet AT.x.x AT.x.z AT.y.x AT.y.z, subdet AT.x.x AT.x.y AT.y.x AT.y.y ⟩ ⟩,
  use
  ⟨ ⟨ M.x.x / detA, -M.x.y / detA, M.x.z / detA ⟩,
    ⟨ -M.y.x / detA, M.y.y / detA, -M.y.z / detA ⟩,
    ⟨ M.z.x / detA, -M.z.y / detA, M.z.z / detA ⟩ ⟩
end

/- There is an inverse if the matrix has nonzero determinant.
  It is -/
@[simp] theorem mul_inv_eq_one (A : mat3) (h : A.det ≠ 0) :
  A * A.inverse = 1 :=
begin
  simp [inverse, *],
  repeat { apply and.intro },
  /- `field_simp` is used since divisions are involved. -/
  repeat { field_simp, ring }
end
@[simp] theorem inv_mul_eq_one (A : mat3) (h : A.det ≠ 0) :
  A.inverse * A = 1 :=
begin
  simp [inverse, *],
  repeat { apply and.intro },
  /- `field_simp` is used since divisions are involved. -/
  repeat { field_simp, ring }
end

/- `is_invertible` is equivalent to `det ≠ 0` -/
@[simp] lemma invertible_det_ne_zero (A : mat3) :
  is_invertible A → A.det ≠ 0 :=
begin
  intros ha,
  cases' ha with B hab,
  intro hn,
  have hadet0 : A.det = 0 := by simp [hn],
  have hdet0 : A.det * B.det = 0 := by rw hadet0; apply zero_mul B.det,
  have hdet := by calc A.det * B.det = (A * B).det
      : by rw mat3.det_mul_det
    ... = (1 : mat3).det
      : by rw hab
    ... = 1
      : by rw mat3.det_one,
  linarith
end

@[simp] lemma det_ne_zero_invertible (A : mat3) :
  A.det ≠ 0 → is_invertible A :=
begin
  intro h,
  use A.inverse,
  exact mul_inv_eq_one A h
end

theorem det_iff (A : mat3) :
  is_invertible A ↔ A.det ≠ 0 :=
begin
  split,
  { exact invertible_det_ne_zero A },
  { exact det_ne_zero_invertible A }
end

lemma inverse_invertible (A : mat3) (h : A.det ≠ 0) :
  is_invertible A.inverse :=
begin
  apply exists.intro A,
  exact inv_mul_eq_one A h
end

end mat3

/- `invertible_mat3` is a subtype of `mat3` that has the `is_invertible` property. -/

@[simp] def invertible_mat3 : Type := { A : mat3 // A.is_invertible }

namespace invertible_mat3

@[ext] theorem ext {A B : invertible_mat3} :
  A.val = B.val → A = B :=
subtype.eq

@[simps] def mul (A B : invertible_mat3) : invertible_mat3 :=
⟨ A.val * B.val,
  begin
    apply mat3.det_ne_zero_invertible,
    have ha := mat3.invertible_det_ne_zero A.val A.property,
    have hb := mat3.invertible_det_ne_zero B.val B.property,
    calc (A.val * B.val).det = A.val.det * B.val.det
        : by rw mat3.det_mul_det
      ... ≠ 0
        : by apply mul_ne_zero ha hb
  end ⟩

@[simps] def one : invertible_mat3 :=
⟨1, by simp⟩

@[simps] noncomputable def inv (A : invertible_mat3) :
  invertible_mat3 :=
⟨A.val.inverse, begin
  apply mat3.inverse_invertible,
  apply mat3.invertible_det_ne_zero,
  exact A.property
end⟩

@[simp] lemma one_mul (A : invertible_mat3) :
  mul one A = A :=
begin
  cases' A,
  apply ext,
  simp only [one, mul],
  cases' val,
  simp,
  repeat { apply and.intro },
  { cases' x, refl },
  { cases' y, refl },
  { cases' z, refl }
end

@[simp] lemma mul_one (A : invertible_mat3) :
  mul A one = A :=
begin
  cases' A,
  cases' val,
  ext,
  simp,
  repeat { apply and.intro },
  { cases' x, refl },
  { cases' y, refl },
  { cases' z, refl }
end

@[simp] lemma mul_assoc (A B C : invertible_mat3) :
  mul (mul A B) C = mul A (mul B C) :=
begin
  cases' A with A _,
  cases' B with B _,
  cases' C with C _,
  ext,
  simp,
  repeat { apply and.intro },
  repeat { ring }
end

@[simp] lemma mul_left_inv (A : invertible_mat3) :
  mul (inv A) A = one :=
begin
  cases' A,
  apply ext,
  simp only [one, inv, mul],
  apply mat3.inv_mul_eq_one,
  apply mat3.invertible_det_ne_zero,
  assumption
end

/- Then it's a `group`. Since any invertible matrix has an inverse. -/
@[simps] noncomputable instance group : group invertible_mat3 :=
{ mul := mul,
  one := one,
  inv := inv,
  one_mul := one_mul,
  mul_one := mul_one,
  mul_assoc := mul_assoc,
  mul_left_inv := mul_left_inv }

@[simps] def transpose (A : invertible_mat3) : invertible_mat3 :=
⟨A.val.transpose, begin
  apply mat3.det_ne_zero_invertible,
  rw mat3.transpose_det,
  apply mat3.invertible_det_ne_zero,
  exact A.property
end⟩

@[simp] def is_unitary (A : invertible_mat3) : Prop :=
  A.val.is_unitary

end invertible_mat3

end FG


/-
Copyright (c) Sébastien Gouëzel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sébastien Gouëzel

! This file was ported from Lean 3 source module analysis.complex.operator_norm
! leanprover-community/mathlib commit 468b141b14016d54b479eb7a0fff1e360b7e3cf6
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Analysis.Complex.Basic
import Mathbin.Analysis.NormedSpace.OperatorNorm
import Mathbin.Data.Complex.Determinant

/-! # The basic continuous linear maps associated to `ℂ`

The continuous linear maps `complex.re_clm` (real part), `complex.im_clm` (imaginary part),
`complex.conj_cle` (conjugation), and `complex.of_real_clm` (inclusion of `ℝ`) were introduced in
`analysis.complex.operator_norm`.  This file contains a few calculations requiring more imports:
the operator norm and (for `complex.conj_cle`) the determinant.
-/

open ContinuousLinearMap

namespace Complex

/-- The determinant of `conj_lie`, as a linear map. -/
@[simp]
theorem det_conjLie : (conjLie.toLinearEquiv : ℂ →ₗ[ℝ] ℂ).det = -1 :=
  det_conjAe
#align complex.det_conj_lie Complex.det_conjLie

/-- The determinant of `conj_lie`, as a linear equiv. -/
@[simp]
theorem linearEquiv_det_conjLie : conjLie.toLinearEquiv.det = -1 :=
  linearEquiv_det_conjAe
#align complex.linear_equiv_det_conj_lie Complex.linearEquiv_det_conjLie

@[simp]
theorem reClm_norm : ‖reClm‖ = 1 :=
  le_antisymm (LinearMap.mkContinuous_norm_le _ zero_le_one _) <|
    calc
      1 = ‖reClm 1‖ := by simp
      _ ≤ ‖reClm‖ := unit_le_op_norm _ _ (by simp)

#align complex.re_clm_norm Complex.reClm_norm

@[simp]
theorem reClm_nnnorm : ‖reClm‖₊ = 1 :=
  Subtype.ext reClm_norm
#align complex.re_clm_nnnorm Complex.reClm_nnnorm

@[simp]
theorem imClm_norm : ‖imClm‖ = 1 :=
  le_antisymm (LinearMap.mkContinuous_norm_le _ zero_le_one _) <|
    calc
      1 = ‖imClm I‖ := by simp
      _ ≤ ‖imClm‖ := unit_le_op_norm _ _ (by simp)

#align complex.im_clm_norm Complex.imClm_norm

@[simp]
theorem imClm_nnnorm : ‖imClm‖₊ = 1 :=
  Subtype.ext imClm_norm
#align complex.im_clm_nnnorm Complex.imClm_nnnorm

@[simp]
theorem conjCle_norm : ‖(conjCle : ℂ →L[ℝ] ℂ)‖ = 1 :=
  conjLie.toLinearIsometry.norm_toContinuousLinearMap
#align complex.conj_cle_norm Complex.conjCle_norm

@[simp]
theorem conjCle_nnorm : ‖(conjCle : ℂ →L[ℝ] ℂ)‖₊ = 1 :=
  Subtype.ext conjCle_norm
#align complex.conj_cle_nnorm Complex.conjCle_nnorm

@[simp]
theorem ofRealClm_norm : ‖ofRealClm‖ = 1 :=
  ofRealLi.norm_toContinuousLinearMap
#align complex.of_real_clm_norm Complex.ofRealClm_norm

@[simp]
theorem ofRealClm_nnnorm : ‖ofRealClm‖₊ = 1 :=
  Subtype.ext <| ofRealClm_norm
#align complex.of_real_clm_nnnorm Complex.ofRealClm_nnnorm

end Complex


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

import tactic -- imports all the Lean tactics
import linear_algebra.finite_dimensional -- finite-dimensional vector spaces

/-!

# Finite-dimensional vector spaces

Here's how you say "let `k` be a field and let `V` be a finite-dimensional `k`-vector space"

-/

-- let k be a field and let V be a k-vector space
variables (k : Type) [field k] (V : Type) [add_comm_group V] [module k V] [finite_dimensional k V]

/-

There are two concepts of "dimension" in Lean. There's a general `module.rank k V`, which
returns a `cardinal` (so in particular the answer could be one of many kinds of infinity)
and, in the finite-dimensional case, there is `finite_dimensional.finrank k V`, which returns
a natural number. Note that, as is idiomatic in Lean, the latter function will accept
an infinite-dimensional space as input (garbage in) and will return 0 for the answer
(garbage out). All our spaces will be finite-dimensional, so we can use
`finite_dimensional.finrank`. Note that if we `open finite_dimensional` then we can
just call it `finrank`.

# An example sheet question

A 2019 University of Edinburgh example sheet question: prove that if `V` is a 9-dimensional
vector space and `A, B` are two subspaces of dimension 5, then `A ∩ B` cannot be
the zero vector space. See below the question for the API you'll need.

-/

open finite_dimensional

example (A B : subspace k V) (hV : finrank k V = 9) (hA : finrank k A = 5) (hB : finrank k B = 5) :
  A ⊓ B ≠ ⊥ :=
begin
  intro h,
  have h1 := submodule.dim_sup_add_dim_inf_eq A B,
  rw [hA, hB, h, finrank_bot k V] at h1,
  norm_num at h1,
  have h2 := submodule.finrank_le (A ⊔ B),
  rw [h1, hV] at h2,
  linarith,
end

/-

Here's some API which you will need for this question. Noet that if `A : subspace k V` then
`A` is a term, not a type, and in particular it's not a vector space (it's a vector subspace).
However `↥A`, a "coercion to type", is a type, and hence has a dimension.

## Some API for finite-dimensional vector spaces

This should be all you need.

`submodule.dim_sup_add_dim_inf_eq A B : finrank k ↥(A ⊔ B) + finrank k ↥(A ⊓ B) = finrank k ↥A + finrank k ↥B`
`submodule.finrank_le A : finrank k ↥A ≤ finrank k V`
`finrank_bot k V : finrank K ↥⊥ = 0`

-/