-- Prueba por inducción 4: ∀ m n : ℕ, m + n = n + m
-- ================================================

-- ----------------------------------------------------
-- Ej. 1. Sean m y n números naturales. Demostrar que
--    m + n = n + m
-- ----------------------------------------------------

import tactic

open nat

variables (m n : ℕ)

-- #check nat.add_zero
-- #check nat.add_succ
-- #check nat.zero_add
-- #check nat.succ_add

-- 1ª demostración
example : m + n = n + m :=
begin
  induction n with n HI,
  { rw nat.add_zero,
    rw nat.zero_add, },
  { rw add_succ,
    rw HI,
    rw succ_add, },
end

-- 2ª demostración
example : m + n = n + m :=
begin
  induction n with n HI,
  { simp only [nat.add_zero, nat.zero_add] },
  { simp only [add_succ, HI, succ_add] },
end

-- 3ª demostración
example : m + n = n + m :=
by induction n;
   simp only [*, nat.add_zero, add_succ, succ_add, nat.zero_add]

-- 4ª demostración
example : m + n = n + m :=
by induction n;
   simp [*, add_succ, succ_add]

-- 5ª demostración
example : m + n = n + m :=
nat.rec_on n
  (show m + 0 = 0 + m, from
    calc m + 0
             = m     : by rw nat.add_zero
         ... = 0 + m : by rw nat.zero_add )
  (assume n,
   assume HI : m + n = n + m,
   show m + n.succ = n.succ + m, from
     calc
       m + succ n
           = succ (m + n) : by rw add_succ
       ... = succ (n + m) : by rw HI
       ... = succ n + m   : by rw succ_add)

-- 6ª demostración
example : m + n = n + m :=
nat.rec_on n
  (show m + 0 = 0 + m, by rw [nat.zero_add, nat.add_zero])
  (assume n,
   assume HI : m + n = n + m,
   calc
     m + succ n = succ (m + n) : rfl
       ... = succ (n + m)      : by rw HI
       ... = succ n + m        : by rw succ_add)

-- 7ª demostración
example : m + n = n + m :=
nat.rec_on n
  (by simp only [nat.zero_add, nat.add_zero])
  (λ n HI, by simp only [add_succ, HI, succ_add])

-- 8ª demostración
example : m + n = n + m :=
nat.rec_on n
  (by simp)
  (λ n HI, by simp [add_succ, HI, succ_add])

-- 9ª demostración
example : m + n = n + m :=
-- by library_search
nat.add_comm m n

-- 10ª demostración
example : m + n = n + m :=
-- by hint
by finish

-- 11ª demostración
example : m + n = n + m :=
by linarith

-- 12ª demostración
example : m + n = n + m :=
by nlinarith

-- 13ª demostración
example : m + n = n + m :=
by ring

-- 14ª demostración
example : m + n = n + m :=
by omega

-- 15ª demostración
lemma conmutativa : ∀ m n : ℕ, m + n = n + m
| m 0     := by simp
| m (n+1) := by simp [add_succ, conmutativa m n, succ_add]

-- 16ª demostración
lemma conmutativa2 : ∀ m n : ℕ, m + n = n + m
| m 0     := by simp only [nat.add_zero, nat.zero_add]
| m (n+1) := by simp only [nat.add_zero, add_succ, conmutativa2 m n, succ_add]


-- ---------------------------------------------------------------------
-- Ejercicio 1. Importar la teoría de anillos.
-- ----------------------------------------------------------------------

import algebra.ring
import tactic

-- ---------------------------------------------------------------------
-- Ejercicio 2. Crear el espacio de nombre my_ring.
-- ----------------------------------------------------------------------

namespace my_ring

-- ---------------------------------------------------------------------
-- Ejercicio 3. Declara R una variable sobre anillos.
-- ----------------------------------------------------------------------

variables {R : Type*} [ring R]

-- ---------------------------------------------------------------------
-- Ejercicio 4. Declarar a, b y c como variables sobre R.
-- ----------------------------------------------------------------------

variables {a b c : R}

-- ---------------------------------------------------------------------
-- Ejercicio 5. Demostrar que si
--    a + b = a + c
-- entonces
--    b = c
-- ----------------------------------------------------------------------

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

theorem add_left_cancel
  (h : a + b = a + c)
  : b = c :=
calc b
     = 0 + b        : by rw zero_add
 ... = (-a + a) + b : by rw add_left_neg
 ... = -a + (a + b) : by rw add_assoc
 ... = -a + (a + c) : by rw h
 ... = (-a + a) + c : by rw ←add_assoc
 ... = 0 + c        : by rw add_left_neg
 ... = c            : by rw zero_add

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

example
  (h : a + b = a + c)
  : b = c :=
begin
  have h1 : -a + (a + b) = -a + (a + c),
    { by exact congr_arg (has_add.add (-a)) h, },
  clear h,
  rw ← add_assoc at h1,
  rw add_left_neg at h1,
  rw zero_add at h1,
  rw ← add_assoc at h1,
  rw add_left_neg at h1,
  rw zero_add at h1,
  exact h1,
end

-- El desarrollo de la prueba es
--
--    R : Type u_1,
--    _inst_1 : ring R,
--    a b c : R,
--    h : a + b = a + c
--    ⊢ b = c
-- have h1 : -a + (a + b) = -a + (a + c),
--    |    ⊢ -a + (a + b) = -a + (a + c)
--    | { by exact congr_arg (has_add.add (-a)) h },
--    h : a + b = a + c,
--    h1 : -a + (a + b) = -a + (a + c)
--    ⊢ b = c
-- clear h,
--    h1 : -a + (a + b) = -a + (a + c)
--    ⊢ b = c
-- rw ← add_assoc at h1,
--    h1 : -a + a + b = -a + (a + c)
--    ⊢ b = c
-- rw add_left_neg at h1,
--    h1 : 0 + b = -a + (a + c)
--    ⊢ b = c
-- rw zero_add at h1,
--    h1 : b = -a + (a + c)
--    ⊢ b = c
-- rw ← add_assoc at h1,
--    h1 : b = -a + a + c
--    ⊢ b = c
-- rw add_left_neg at h1,
--    h1 : b = 0 + c
--    ⊢ b = c
-- rw zero_add at h1,
--    h1 : b = c
--    ⊢ b = c
-- exact h1,
--    no goals

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

example
  (h : a + b = a + c)
  : b = c :=
by finish

-- ---------------------------------------------------------------------
-- Ejercicio 6. Demostrar que si
--    a + b = c + b
-- entonces
--    a = c
-- ----------------------------------------------------------------------

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

theorem add_right_cancel
  (h : a + b = c + b)
  : a = c :=
calc
  a
      = a + 0        : by rw add_zero
  ... = a + (b + -b) : by rw add_right_neg
  ... = (a + b) + -b : by rw add_assoc
  ... = (c + b) + -b : by rw h
  ... = c + (b + -b) : by rw ← add_assoc
  ... = c + 0        : by rw ← add_right_neg
  ... = c            : by rw add_zero

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

example
  (h : a + b = c + b)
  : a = c :=
by finish

-- ---------------------------------------------------------------------
-- Ejercicio 7. Cerrar el espacio de nombre my_ring.
-- ----------------------------------------------------------------------

end my_ring


--=============================--
------ LÓGICA PROPOSICIONAL------
--=============================--

-- En lógica clásica, la doble negación de p y p son equivalentes
theorem iff_not_not {p : Prop} : ¬¬p ↔ p :=
  Iff.intro
  Classical.byContradiction
  (fun hp : p => fun hnp : ¬p => (hnp hp))

theorem notfalse_iff_true : ¬False ↔ True :=
  ⟨fun _ => True.intro, fun _ hfalse => hfalse⟩

theorem nottrue_iff_false : ¬True ↔ False :=
  ⟨fun hnottrue => hnottrue True.intro, fun hfalse => hfalse.elim⟩

theorem forall_of_not_exists {p : α → Prop} : ¬ (∃ a, p a) → ∀ a, ¬ p a :=
  fun h a hpa => h ⟨a,hpa⟩

-- El contrarrecíproco de una implicación
theorem contrapositive {p q : Prop} : (p → q) → ¬q → ¬p :=
  fun hpq hnq hp => hnq (hpq hp)

theorem implies_of_not_and {p q : Prop} : ¬ (p ∧ q) → (p → ¬ q) :=
  fun hnpq hp hq => hnpq ⟨hp,hq⟩

theorem or_not_iff_not_and {p q : Prop} : ¬ (p ∧ q) ↔ ¬p ∨ ¬q :=
  ⟨fun hnpq : ¬(p ∧ q) =>
    Or.elim (Classical.em p)
    (fun hp => Or.inr (fun hq => hnpq ⟨hp,hq⟩))
    (fun hnp => Or.inl hnp),
  fun hnpnq : ¬p ∨ ¬q =>
    fun ⟨hp,hq⟩ => hnpnq.elim (fun hnp => hnp hp) (fun hnq => hnq hq)⟩

-- Una de las leyes de De Morgan
theorem and_or_iff {p q1 q2: Prop} : p ∧ (q1 ∨ q2) ↔ (p ∧ q1) ∨ (p ∧ q2) :=
  ⟨fun h => h.right.elim (fun hq1 => Or.inl ⟨h.left,hq1⟩) (fun hq2 => Or.inr ⟨h.left,hq2⟩),
  fun h => h.elim (fun hpq1 => ⟨hpq1.left, Or.inl hpq1.right⟩) (fun hpq2 => ⟨hpq2.left, Or.inr hpq2.right⟩)⟩

-- El recíproco del principio de extensionalidad funcional
theorem funext_converse {f g : α → β} : f ≠ g → ∃ x : α, f x ≠ g x :=
  fun hneq =>
    Classical.byContradiction (fun hnotex =>
      have hforall1 : ∀ x, ¬ f x ≠ g x := forall_of_not_exists hnotex
      have hforall : ∀ x, f x = g x := fun x => propext (@iff_not_not (f x = g x)) ▸ (hforall1 x)
      hneq (funext hforall))


import Mathlib.Tactic.LeftRight
/-!
# Advanced proposition world.

## Level 6: Or, and the [`left` and `right` tactics](../Tactics/leftright.lean.md).

`P ∨ Q` means "`P` or `Q`". So to prove it, you need to choose one of `P` or `Q`, and prove that
one. If `⊢ P ∨ Q` is your goal, then `left` changes this goal to `⊢ P`, and `right` changes it to `⊢ Q`.
Note that you can take a wrong turn here. Let's start with trying to prove `Q ⟹ (P ∨ Q)`. After
the `intro`, one of `left` and `right` leads to an impossible goal, the other to an easy finish.

## Lemma
If `P` and `Q` are true/false statements, then `Q ⟹(P ∨ Q).`
-/
example (P Q : Prop) : Q → (P ∨ Q) := by
  intro q
  right
  assumption

/-!
## Details

The tactics `left` and `right` work on a goal which is a type with
two constructors, the classic example being `P ∨ Q`.
To prove `P ∨ Q` it suffices to either prove `P` or prove `Q`,
and once you know which one you are going for you can change
the goal with `left` or `right` to the appropriate choice.

## Pro Tip!

Did you spot the `import Mathlib.Tactic.LeftRight`? What do you think it does?

You can make Mathlib available to your Lean package by adding the following
to your `lakefile.lean`:
```lean
require mathlib from git
  "https://github.com/leanprover-community/mathlib4.git" @ "56b19bdec560037016e326795d0feaa23b402c20"
```
This specifies a precise version of mathlib4 by commit Id.

Next up [Level 7](./Level7.lean.md)
-/


import .love02_backward_proofs_demo

/- # LoVe Exercise 2: Backward Proofs -/

set_option pp.beta true
set_option pp.generalized_field_notation false

namespace LoVe

namespace backward_proofs

/- ## Question 1: Connectives and Quantifiers

1.1. Carry out the following proofs using basic tactics.

Hint: Some strategies for carrying out such proofs are described at the end of
Section 2.3 in the Hitchhiker's Guide. -/

lemma I (a : Prop) :
  a → a :=
begin
  intro ha,
  exact ha
end

lemma K (a b : Prop) :
  a → b → b :=
begin
  intros ha hb,
  exact hb
end

lemma C (a b c : Prop) :
  (a → b → c) → b → a → c :=
begin
  intros hg hb ha,
  apply hg,
  exact ha,
  exact hb
end

lemma proj_1st (a : Prop) :
  a → a → a :=
begin
  intros ha ha',
  exact ha
end

/- Please give a different answer than for `proj_1st`: -/

lemma proj_2nd (a : Prop) :
  a → a → a :=
begin
  intros ha ha',
  exact ha'
end

lemma some_nonsense (a b c : Prop) :
  (a → b → c) → a → (a → c) → b → c :=
begin
  intros hg ha hf hb,
  apply hg,
  exact ha,
  exact hb
end

/- 1.2. Prove the contraposition rule using basic tactics. -/

lemma contrapositive (a b : Prop) :
  (a → b) → ¬ b → ¬ a :=
begin
  intros hab hnb ha,
  apply hnb,
  apply hab,
  apply ha
end

/- 1.3. Prove the distributivity of `∀` over `∧` using basic tactics.

Hint: This exercise is tricky, especially the right-to-left direction. Some
forward reasoning, like in the proof of `and_swap₂` in the lecture, might be
necessary. -/

lemma forall_and {α : Type} (p q : α → Prop) :
  (∀x, p x ∧ q x) ↔ (∀x, p x) ∧ (∀x, q x) :=
begin
  apply iff.intro,
  { intro h,
    apply and.intro,
    { intro x,
      apply and.elim_left,
      apply h },
    { intro x,
      apply and.elim_right,
      apply h } },
  { intros h x,
    apply and.intro,
    { apply and.elim_left h },
    { apply and.elim_right h } }
end

/- ## Question 2: Natural Numbers

2.1. Prove the following recursive equations on the first argument of the
`mul` operator defined in lecture 1. -/

#check mul

lemma mul_zero (n : ℕ) :
  mul 0 n = 0 :=
begin
  induction' n,
  { refl },
  { simp [add, mul, ih] }
end

lemma mul_succ (m n : ℕ) :
  mul (nat.succ m) n = add (mul m n) n :=
begin
  induction' n,
  { refl },
  { simp [add, add_succ, add_assoc, mul, ih] }
end

/- 2.2. Prove commutativity and associativity of multiplication using the
`induction'` tactic. Choose the induction variable carefully. -/

lemma mul_comm (m n : ℕ) :
  mul m n = mul n m :=
begin
  induction' m,
  { simp [mul, mul_zero] },
  { simp [mul, mul_succ, ih],
    cc }
end

lemma mul_assoc (l m n : ℕ) :
  mul (mul l m) n = mul l (mul m n) :=
begin
  induction' n,
  { refl },
  { simp [mul, mul_add, ih] }
end

/- 2.3. Prove the symmetric variant of `mul_add` using `rw`. To apply
commutativity at a specific position, instantiate the rule by passing some
arguments (e.g., `mul_comm _ l`). -/

lemma add_mul (l m n : ℕ) :
  mul (add l m) n = add (mul n l) (mul n m) :=
begin
  rw mul_comm _ n,
  rw mul_add
end

/- ## Question 3 (**optional**): Intuitionistic Logic

Intuitionistic logic is extended to classical logic by assuming a classical
axiom. There are several possibilities for the choice of axiom. In this
question, we are concerned with the logical equivalence of three different
axioms: -/

def excluded_middle :=
∀a : Prop, a ∨ ¬ a

def peirce :=
∀a b : Prop, ((a → b) → a) → a

def double_negation :=
∀a : Prop, (¬¬ a) → a

/- For the proofs below, please avoid using lemmas from Lean's `classical`
namespace, because this would defeat the purpose of the exercise.

3.1 (**optional**). Prove the following implication using tactics.

Hint: You will need `or.elim` and `false.elim`. You can use
`rw excluded_middle` to unfold the definition of `excluded_middle`,
and similarly for `peirce`. -/

lemma peirce_of_em :
  excluded_middle → peirce :=
begin
  rw excluded_middle,
  rw peirce,
  intro hem,
  intros a b haba,
  apply or.elim (hem a),
  { intro,
    assumption },
  { intro hna,
    apply haba,
    intro ha,
    apply false.elim,
    apply hna,
    assumption }
end

/- 3.2 (**optional**). Prove the following implication using tactics. -/

lemma dn_of_peirce :
  peirce → double_negation :=
begin
  rw peirce,
  rw double_negation,
  intros hpeirce a hnna,
  apply hpeirce a false,
  intro hna,
  apply false.elim,
  apply hnna,
  exact hna
end

/- We leave the missing implication for the homework: -/

namespace sorry_lemmas

lemma em_of_dn :
  double_negation → excluded_middle :=
sorry

end sorry_lemmas

end backward_proofs

end LoVe


/-
Copyright (c) 2021 Manuel Candales. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Manuel Candales
-/
import data.real.basic

/-!
# IMO 2005 Q3
Let `x`, `y` and `z` be positive real numbers such that `xyz ≥ 1`. Prove that:
`(x^5 - x^2)/(x^5 + y^2 + z^2) + (y^5 - y^2)/(y^5 + z^2 + x^2) + (z^5 - z^2)/(z^5 + x^2 + y^2) ≥ 0`

# Solution
The solution by Iurie Boreico from Moldova is presented, which won a special prize during the exam.
The key insight is that `(x^5-x^2)/(x^5+y^2+z^2) ≥ (x^2-y*z)/(x^2+y^2+z^2)`, which is proven by
factoring `(x^5-x^2)/(x^5+y^2+z^2) - (x^5-x^2)/(x^3*(x^2+y^2+z^2))` into a non-negative expression
and then making use of `xyz ≥ 1` to show `(x^5-x^2)/(x^3*(x^2+y^2+z^2)) ≥ (x^2-y*z)/(x^2+y^2+z^2)`.
-/

lemma key_insight (x y z : ℝ) (hx : x > 0) (hy : y > 0) (hz : z > 0) (h : x*y*z ≥ 1) :
  (x^5-x^2)/(x^5+y^2+z^2) ≥ (x^2-y*z)/(x^2+y^2+z^2) :=
begin
  have h₁ : 0 < x^5+y^2+z^2, linarith [pow_pos hx 5, pow_pos hy 2, pow_pos hz 2],
  have h₂ : 0 < x^3, exact pow_pos hx 3,
  have h₃ : 0 < x^2+y^2+z^2, linarith [pow_pos hx 2, pow_pos hy 2, pow_pos hz 2],
  have h₄ : 0 < x^3*(x^2+y^2+z^2), exact mul_pos h₂ h₃,

  have key : (x^5-x^2)/(x^5+y^2+z^2) - (x^5-x^2)/(x^3*(x^2+y^2+z^2))
           = ((x^3 - 1)^2*x^2*(y^2 + z^2))/((x^5+y^2+z^2)*(x^3*(x^2+y^2+z^2))),
  { field_simp [h₁.ne', h₄.ne'],
    ring },

  have h₅ : ((x^3 - 1)^2*x^2*(y^2 + z^2))/((x^5+y^2+z^2)*(x^3*(x^2+y^2+z^2))) ≥ 0,
  { refine div_nonneg _ _,
    refine mul_nonneg (mul_nonneg (sq_nonneg _) (sq_nonneg _)) _,
    exact add_nonneg (sq_nonneg _) (sq_nonneg _),
    exact le_of_lt (mul_pos h₁ h₄) },

  calc  (x^5-x^2)/(x^5+y^2+z^2)
      ≥ (x^5-x^2)/(x^3*(x^2+y^2+z^2)) : by linarith [key, h₅]
  ... ≥ (x^5-x^2*(x*y*z))/(x^3*(x^2+y^2+z^2)) :
        by { refine (div_le_div_right h₄).mpr _, simp,
             exact (le_mul_iff_one_le_right (pow_pos hx 2)).mpr h }
  ... = (x^2-y*z)/(x^2+y^2+z^2) :
        by { field_simp [h₂.ne', h₃.ne'], ring },
end

theorem imo2005_q3 (x y z : ℝ) (hx : x > 0) (hy : y > 0) (hz : z > 0) (h : x*y*z ≥ 1) :
  (x^5-x^2)/(x^5+y^2+z^2) + (y^5-y^2)/(y^5+z^2+x^2) + (z^5-z^2)/(z^5+x^2+y^2) ≥ 0 :=
begin
  calc  (x^5-x^2)/(x^5+y^2+z^2) + (y^5-y^2)/(y^5+z^2+x^2) + (z^5-z^2)/(z^5+x^2+y^2)
      ≥ (x^2-y*z)/(x^2+y^2+z^2) + (y^2-z*x)/(y^2+z^2+x^2) + (z^2-x*y)/(z^2+x^2+y^2) :
        by { linarith [key_insight x y z hx hy hz h,
                       key_insight y z x hy hz hx (by linarith [h]),
                       key_insight z x y hz hx hy (by linarith [h])] }
  ... = 1/2*( (x-y)^2 + (y-z)^2 + (z-x)^2 ) / (x^2+y^2+z^2) :
        by { have h₁ : y^2+z^2+x^2 = x^2+y^2+z^2, ring,
             have h₂ : z^2+x^2+y^2 = x^2+y^2+z^2, ring,
             rw [h₁, h₂], ring }
  ... ≥ 0 :
        by { exact div_nonneg
                (by linarith [sq_nonneg (x-y), sq_nonneg (y-z), sq_nonneg (z-x)])
                (by linarith [sq_nonneg x, sq_nonneg y, sq_nonneg z]) },
end


section propositional

variables P Q R : Prop

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

theorem doubleneg_intro :
  P → ¬¬P  :=
begin
  intro p,
  intro nop,
  have boom: false := nop p,
  exact boom,
end

theorem doubleneg_elim :
  ¬¬P → P  :=
begin
  intro nnp,
  by_cases h : P, --LEM
  --P
  exact h,
  --¬P
  have boom: false := nnp h,
  contradiction,
end

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

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

theorem disj_comm :
  (P ∨ Q) → (Q ∨ P)  :=
begin
  intro pvq,
  cases pvq with p q,
  --Case P
  right,
  exact p,
  --Case Q
  left,
  exact q,
end

theorem conj_comm :
  (P ∧ Q) → (Q ∧ P)  :=
begin
  intro pandq,
  split,
  --Parte Q
  cases pandq with p q,
  exact q,
  --Parte P
  cases pandq with p q,
  exact p,
end

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

theorem impl_as_disj_converse :
  (¬P ∨ Q) → (P → Q)  :=
begin
  intro nopvq,
  intro p,
  cases nopvq with nop q,
  --case noP
  exfalso,
  have boom: false := nop p,
  contradiction,
  --Case Q
  exact q,
end

theorem disj_as_impl :
  (P ∨ Q) → (¬P → Q)  :=
begin
  intro pvq,
  intro nop,
  cases pvq with p q,
  --Case P
  exfalso,
  have boom: false := nop p,
  exact boom,
  --Case Q
  exact q,
end

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

theorem impl_as_contrapositive :
  (P → Q) → (¬Q → ¬P)  :=
begin
  intro hpq,
  intro noq,
  intro p,
  have q: Q := hpq p,
  have boom: false := noq q,
  exact boom,
end

theorem impl_as_contrapositive_converse :
  (¬Q → ¬P) → (P → Q)  :=
begin
  intro h,
  intro hp,
  by_contradiction hboom,
  have hpp: ¬P := h hboom,
  have hpboom: false := hpp hp,
  exact hpboom,
end

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

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

theorem lem_irrefutable :
  ¬¬(P∨¬P)  :=
begin
  intro h,
  have pvnp : P∨¬P,
  right,
  intro nop,
  have pvq : P∨¬P,
  left,
  exact nop,
  contradiction,
  contradiction,
end

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

theorem peirce_law_weak :
  ((P → Q) → P) → ¬¬P  :=
begin
  intro h,
  intro nop,
  have pq : P → Q,
  intro p,
  contradiction,
  have pp : P := h pq,
  contradiction,
end

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

theorem disj_as_negconj :
  P∨Q → ¬(¬P∧¬Q)  :=
begin
  intro h,
  intro hnpnq,
  cases hnpnq with hp hq,
  cases h with hp hq,
  contradiction,
  contradiction,
end

theorem conj_as_negdisj :
  P∧Q → ¬(¬P∨¬Q)  :=
begin
  intro h,
  intro hnpnq,
  cases h with hp hq,
  cases hnpnq with hp hq,
  contradiction,
  contradiction,
end

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

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

theorem demorgan_disj_converse :
  (¬P ∧ ¬Q) → ¬(P∨Q)  :=
begin
  intro h,
  intro pvq,
  cases pvq with p q,
  cases h with np nq,
  contradiction,
  cases h with np nq,
  contradiction,
end

theorem demorgan_conj :
  ¬(P∧Q) → (¬Q ∨ ¬P)  :=
begin
  intro h,
  by_cases hq: Q,
  right,
  intro hp,
  have pandq : (P∧Q),
  split,
  exact hp,
  exact hq,
  contradiction,
  left,
  exact hq,
end

theorem demorgan_conj_converse :
  (¬Q ∨ ¬P) → ¬(P∧Q)  :=
begin
  intro h,
  intro hpandq,
  cases hpandq with nq np,
  cases h with noq nop,
  contradiction,
  contradiction,
end

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

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

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

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

theorem distr_conj_disj_converse :
  (P∧Q)∨(P∧R) → P∧(Q∨R)  :=
begin
  intro h,
  cases h with hl hr,
  cases hl with p q,
  split,
  exact p,
  left,
  exact q,
  cases hr with pp r,
  split,
  exact pp,
  right,
  exact r,
end

theorem distr_disj_conj :
  P∨(Q∧R) → (P∨Q)∧(P∨R)  :=
begin
  intro h,
  cases h 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 h,
  cases h with hl hr,
  cases hl with p q,
  left,
  exact p,
  cases hr with p r,
  left,
  exact p,
  right,
  split,
  exact q,
  exact r,
end

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

theorem curry_prop :
  ((P∧Q)→R) → (P→(Q→R))  :=
begin
  intro h,
  intro hp,
  intro hq,
  have pandq : P∧Q,
  split,
  exact hp,
  exact hq,
  apply h pandq,
end

theorem uncurry_prop :
  (P→(Q→R)) → ((P∧Q)→R)  :=
begin
  intro hpqr,
  intro hpandq,
  cases hpandq with p q,
  apply hpqr p,
  exact q,
end

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

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

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

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

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

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

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

theorem conj_idempot :
  (P∧P) ↔ P :=
begin
  split,
  intro hp,
  cases hp with p pp,
  exact p,
  intro hpp,
  split,
  exact hpp,
  exact hpp,
end

theorem disj_idempot :
  (P∨P) ↔ P  :=
begin
  split,
  intro h,
  cases h with p pp,
  exact p,
  exact pp,
  intro hp,
  left,
  exact hp,
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
  intro he,
  intro x,
  intro p,
  apply he,
  existsi x,
  exact p,
end

theorem demorgan_exists_converse :
  (∀x, ¬P x) → ¬(∃x, P x)  :=
begin
  intro ha,
  intro he,
  cases he with t ht,
  exact (ha t) ht,
end

theorem demorgan_forall :
  ¬(∀x, P x) → (∃x, ¬P x)  :=
begin
  rw contrapositive_law,
  rw doubleneg_law,
  intro he,
  intro a,
  by_contradiction hboom,
  apply he,
  existsi a,
  exact hboom,
end

theorem demorgan_forall_converse :
  (∃x, ¬P x) → ¬(∀x, P x)  :=
begin
  intro he,
  intro ha,
  cases he with t ht,
  have pt : P t := ha t,
  contradiction,
end

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

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

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

theorem exists_as_neg_forall :
  (∃x, P x) → ¬(∀x, ¬P x)  :=
begin
  intro he,
  intro ha,
  cases he with x he,
  have nhp : ¬P x := ha x,
  contradiction,
end

theorem forall_as_neg_exists :
  (∀x, P x) → ¬(∃x, ¬P x)  :=
begin
  intro ha,
  intro he,
  cases he with x he,
  have hp : P x := ha x,
  contradiction,
end

theorem forall_as_neg_exists_converse :
  ¬(∃x, ¬P x) → (∀x, P x)  :=
begin
  intro he,
  intro x,
  by_contradiction px,
  apply he,
  existsi x,
  exact px,
end

theorem exists_as_neg_forall_converse :
  ¬(∀x, ¬P x) → (∃x, P x)  :=
begin
  rw contrapositive_law,
  intro he,
  intro ha,
  apply ha,
  intro t,
  intro pt,
  apply he,
  existsi t,
  exact pt,
end

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

theorem exists_as_neg_forall_law :
  (∃x, P x) ↔ ¬(∀x, ¬P x)  :=
begin
  split,
  exact exists_as_neg_forall U P,
  exact exists_as_neg_forall_converse U P,
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 h,
  cases h with a ha,
  cases ha with hp hq,
  split,
  --Parte P(x)
  existsi a,
  exact hp,
  --Parte Q(x)
  existsi a,
  exact hq,
end

theorem exists_disj_as_disj_exists :
  (∃x, P x ∨ Q x) → (∃x, P x) ∨ (∃x, Q x)  :=
begin
  intro h,
  cases h with a ha,
  cases ha with Pa Qa,
  left,
  existsi a,
  exact Pa,
  right,
  existsi a,
  exact Qa,
end

theorem exists_disj_as_disj_exists_converse :
  (∃x, P x) ∨ (∃x, Q x) → (∃x, P x ∨ Q x)  :=
begin
  intro h,
  cases h with Pa Qa,
  cases Pa with a ha,
  existsi a,
  left,
  exact ha,
  cases Qa with a ha,
  existsi a,
  right,
  exact ha,
end

theorem forall_conj_as_conj_forall :
  (∀x, P x ∧ Q x) → (∀x, P x) ∧ (∀x, Q x)  :=
begin
  intro h,
  split,
  intro a,
  have ha : P a ∧ Q a := h a,
  cases ha with hl hr,
  exact hl,
  intro a,
  have ha : P a ∧ Q a := h a,
  cases ha with hl hr,
  exact hr,
end

theorem forall_conj_as_conj_forall_converse :
  (∀x, P x) ∧ (∀x, Q x) → (∀x, P x ∧ Q x)  :=
begin
  intro h,
  intro a,
  cases h with hl hr,
  split,
  have hp : P a := hl a,
  exact hp,
  have hq : Q a := hr a,
  exact hq,
end

theorem forall_disj_as_disj_forall_converse :
  (∀x, P x) ∨ (∀x, Q x) → (∀x, P x ∨ Q x)  :=
begin
  intro h,
  intro a,
  cases h with hl hr,
  have hp : P a := hl a,
  left,
  exact hp,
  have hq : Q a := hr a,
  right,
  exact hq,
end

/- NOT THEOREMS --------------------------------

theorem forall_disj_as_disj_forall :
  (∀x, P x ∨ Q x) → (∀x, P x) ∨ (∀x, Q x)  :=
begin
end

theorem exists_conj_as_conj_exists_converse :
  (∃x, P x) ∧ (∃x, Q x) → (∃x, P x ∧ Q x)  :=
begin
end

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

end predicate


section propositional

variables P Q R : Prop

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

theorem doubleneg_intro :
  P → ¬¬P  :=
begin
  intros p not_p,
  exact not_p(p),
end

theorem doubleneg_elim :
  ¬¬P → P  :=
begin
  intro not_not_p,
  by_contra not_p,
  exact not_not_p(not_p),
end

theorem doubleneg_law :
  ¬¬P ↔ P  :=
begin
  split,
  exact doubleneg_elim P,

  exact doubleneg_intro P,
end

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

theorem disj_comm :
  (P ∨ Q) → (Q ∨ P)  :=
begin
  intro p_or_q,
  cases p_or_q with p q,
  right,
  exact p,
  left,
  exact q,
end

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

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

theorem impl_as_disj_converse :
  (¬P ∨ Q) → (P → Q)  :=
begin
  intros neg_p_or_q p,
  cases neg_p_or_q with neg_p q,
  exfalso,
  exact neg_p(p),

  exact q,
end

theorem disj_as_impl :
  (P ∨ Q) → (¬P → Q)  :=
begin
  intros p_or_q not_p,
  cases p_or_q with p q,
  exfalso,
  exact not_p(p),

  exact q,
end

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

theorem impl_as_contrapositive :
  (P → Q) → (¬Q → ¬P)  :=
begin
  intros p_imp_q not_q,
  intro p,
  have q := p_imp_q(p),
  exact not_q(q),
end

theorem impl_as_contrapositive_converse :
  (¬Q → ¬P) → (P → Q)  :=
begin
  intros not_q_imp_not_p p,
  by_contra not_q,
  have not_p := not_q_imp_not_p(not_q),
  exact not_p(p),
end

theorem contrapositive_law :
  (P → Q) ↔ (¬Q → ¬P)  :=
begin
  split,
  exact impl_as_contrapositive P Q,

  exact impl_as_contrapositive_converse P Q,
end

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

theorem lem_irrefutable :
  ¬¬(P∨¬P)  :=
begin
  intro h1,
  apply h1,
  right,
  intro h2,
  have p_or_not_p : (P ∨ ¬P),
  left,
  exact h2,

  exact h1 p_or_not_p,
end

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

theorem peirce_law_weak :
  ((P → Q) → P) → ¬¬P  :=
begin
  intro h1,
  intro not_p,
  apply not_p,
  apply h1,
  intro p,
  exfalso,
  exact not_p p,
end

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

theorem disj_as_negconj :
  P∨Q → ¬(¬P∧¬Q)  :=
begin
  intro p_or_q,
  intro h1,
  cases h1 with not_p not_q,
  cases p_or_q with p q,
  exact not_p p,

  exact not_q q,
end

theorem conj_as_negdisj :
  P∧Q → ¬(¬P∨¬Q)  :=
begin
  intro p_and_q,
  intro h1,
  cases p_and_q with p q,
  cases h1 with not_p not_q,
  exact not_p p,

  exact not_q q,
end

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

theorem demorgan_disj :
  ¬(P∨Q) → (¬P ∧ ¬Q)  :=
begin
  intro h,
  split,
  intro p,
  apply h,
  left,
  exact p,

  intro q,
  apply h,
  right,
  exact q,
end

theorem demorgan_disj_converse :
  (¬P ∧ ¬Q) → ¬(P∨Q)  :=
begin
  intro not_p_and_not_q,
  intro p_or_q,
  cases not_p_and_not_q with not_p not_q,
  cases p_or_q with p q,
  exact not_p p,
  exact not_q q,
end

theorem demorgan_conj :
  ¬(P∧Q) → (¬Q ∨ ¬P)  :=
begin
  intro h,
  by_contradiction hboom,
  apply h,
  split,
  by_contradiction not_p,
  apply hboom,
  right,
  exact not_p,
  by_contradiction not_q,
  apply hboom,
  left,
  exact not_q,
end

theorem demorgan_conj_converse :
  (¬Q ∨ ¬P) → ¬(P∧Q)  :=
begin
  intro not_q_or_not_p,
  intro p_or_q,
  cases not_q_or_not_p with not_q not_p,
  cases p_or_q with p q,
  exact not_q q,

  cases p_or_q with p q,
  exact not_p p,
end

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

  exact demorgan_conj_converse P Q,
end

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

  exact demorgan_disj_converse P Q,
end

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

theorem distr_conj_disj :
  P∧(Q∨R) → (P∧Q)∨(P∧R)  :=
begin
  intro h,
  cases h with p q_or_r,
  cases q_or_r with q r,
  left,
  split,
  exact p,
  exact q,

  right,
  split,
  exact p,
  exact r,
end

theorem distr_conj_disj_converse :
  (P∧Q)∨(P∧R) → P∧(Q∨R)  :=
begin
  intro h,
  cases h with p_and_q p_and_r,
  cases p_and_q with p q,
  split,
  exact p,

  left,
  exact q,

  cases p_and_r with p r,
  split,
  exact p,

  right,
  exact r,
end

theorem distr_disj_conj :
  P∨(Q∧R) → (P∨Q)∧(P∨R)  :=
begin
  intro h,
  split,
  cases h with p q_and_r,
  left,
  exact p,

  cases q_and_r with q r,
  right,
  exact q,

  cases h with p q_and_r,
  left,
  exact p,

  cases q_and_r with q r,
  right,
  exact r,
end

theorem distr_disj_conj_converse :
  (P∨Q)∧(P∨R) → P∨(Q∧R)  :=
begin
  intro h,
  cases h 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 h p q,
  apply h,
  split,
  exact p,
  exact q,
end

theorem uncurry_prop :
  (P→(Q→R)) → ((P∧Q)→R)  :=
begin
  intros h p_and_q,
  apply h,
  exact p_and_q.left,

  exact p_and_q.right,
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,
  exact p_and_q.left,
end

theorem weaken_conj_left :
  (P∧Q) → Q  :=
begin
  intro p_and_q,
  exact p_and_q.right,
end

theorem conj_idempot :
  (P∧P) ↔ P :=
begin
  split,
  intro p_and_p,
  exact p_and_p.left,

  intro p,
  split,
  exact p,
  exact p,
end

theorem disj_idempot :
  (P∨P) ↔ P  :=
begin
  split,
  intro p_or_p,
  cases p_or_p with p p,
  exact p,

  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
  intro h,
  intro u,
  intro p_u,
  apply h,
  existsi u,
  exact p_u,
end

theorem demorgan_exists_converse :
  (∀x, ¬P x) → ¬(∃x, P x)  :=
begin
  intro h1,
  intro h2,
  cases h2 with u hu,
  exact h1 u hu,
end

theorem demorgan_forall :
  ¬(∀x, P x) → (∃x, ¬P x)  :=
begin
  intro h,
  by_contradiction hboom,
  apply h,
  intro u,
  by_contradiction hboom2,
  apply hboom,
  existsi u,
  exact hboom2,
end

theorem demorgan_forall_converse :
  (∃x, ¬P x) → ¬(∀x, P x)  :=
begin
  intro h,
  intro f,
  cases h with u hu,
  have p_u := f u,
  exact hu p_u,
end

theorem demorgan_forall_law :
  ¬(∀x, P x) ↔ (∃x, ¬P x)  :=
begin
  split,
  exact demorgan_forall U P,

  exact demorgan_forall_converse U P,
end

theorem demorgan_exists_law :
  ¬(∃x, P x) ↔ (∀x, ¬P x)  :=
begin
  split,
  exact demorgan_exists U P,

  exact demorgan_exists_converse U P,
end

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

theorem exists_as_neg_forall :
  (∃x, P x) → ¬(∀x, ¬P x)  :=
begin
  intro h1,
  intro h2,
  cases h1 with u p_u,
  have neg_p_u := h2 u,
  exact neg_p_u p_u,
end

theorem forall_as_neg_exists :
  (∀x, P x) → ¬(∃x, ¬P x)  :=
begin
  intro h1,
  intro h2,
  cases h2 with u neg_p_u,
  have p_u := h1 u,
  exact neg_p_u p_u,
end

theorem forall_as_neg_exists_converse :
  ¬(∃x, ¬P x) → (∀x, P x)  :=
begin
  intro h1,
  intro u,
  by_contradiction hboom,
  apply h1,
  existsi u,
  exact hboom,
end

theorem exists_as_neg_forall_converse :
  ¬(∀x, ¬P x) → (∃x, P x)  :=
begin
  intro h1,
  by_contradiction h2,
  apply h1,
  intro u,
  intro p_u,
  apply h2,
  existsi u,
  exact p_u,
end

theorem forall_as_neg_exists_law :
  (∀x, P x) ↔ ¬(∃x, ¬P x)  :=
begin
  split,
  exact forall_as_neg_exists U P,

  exact forall_as_neg_exists_converse U P,
end

theorem exists_as_neg_forall_law :
  (∃x, P x) ↔ ¬(∀x, ¬P x)  :=
begin
  split,
  exact exists_as_neg_forall U P,

  exact exists_as_neg_forall_converse U P,
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 h,
  cases h with  u hu,
  cases hu with p_u q_u,
  split,
  existsi u,
  exact p_u,

  existsi u,
  exact q_u,
end

theorem exists_disj_as_disj_exists :
  (∃x, P x ∨ Q x) → (∃x, P x) ∨ (∃x, Q x)  :=
begin
  intro h,
  cases h with u hu,
  cases hu with p_u q_u,
  left,
  existsi u,
  exact p_u,

  right,
  existsi u,
  exact q_u,
end

theorem exists_disj_as_disj_exists_converse :
  (∃x, P x) ∨ (∃x, Q x) → (∃x, P x ∨ Q x)  :=
begin
  intro h,
  cases h with ex_u_pu ex_u_qu,
  cases ex_u_pu with u pu,
  existsi u,
  left,
  exact pu,

  cases ex_u_qu with u qu,
  existsi u,
  right,
  exact qu,
end

theorem forall_conj_as_conj_forall :
  (∀x, P x ∧ Q x) → (∀x, P x) ∧ (∀x, Q x)  :=
begin
  intro h,
  split,
  intro u,
  have p_u_and_q_u := h u,
  cases p_u_and_q_u with p_u q_u,
  exact p_u,

  intro u,
  have p_u_and_q_u := h u,
  cases p_u_and_q_u with p_u q_u,
  exact q_u,
end

theorem forall_conj_as_conj_forall_converse :
  (∀x, P x) ∧ (∀x, Q x) → (∀x, P x ∧ Q x)  :=
begin
  intro h,
  intro u,
  cases h with p_x q_x,
  have p_u := p_x u,
  have q_u := q_x u,
  split,
  exact p_u,

  exact q_u,
end

theorem forall_disj_as_disj_forall_converse :
  (∀x, P x) ∨ (∀x, Q x) → (∀x, P x ∨ Q x)  :=
begin
  intro h,
  intro u,
  cases h with forall_p_x forall_q_x,
  have p_u := forall_p_x u,
  left,
  exact p_u,

  have q_u := forall_q_x u,
  right,
  exact q_u,
end

/- NOT THEOREMS --------------------------------

theorem forall_disj_as_disj_forall :
  (∀x, P x ∨ Q x) → (∀x, P x) ∨ (∀x, Q x)  :=
begin
end

theorem exists_conj_as_conj_exists_converse :
  (∃x, P x) ∧ (∃x, Q x) → (∃x, P x ∧ Q x)  :=
begin
end

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

end predicate


-- Tactics are commands or instructions that describe how to build a proof.
-- Tactics are easier and shorter to write

theorem test (p q : Prop) (hp : p) (hq : q) :
p ∧ q ∧ p :=
begin
 apply and.intro,
 apply hp,
 apply and.intro,
 apply hq,
 apply hp
end

#print test

example (p q r : Prop) :
p ∧ (q ∨ r) <-> (p ∧ q) ∨ (p ∧ r) :=
begin
 apply iff.intro,
 intro h,
 apply or.elim (and.right h),
 intro hq,
 apply or.inl,
 apply and.intro,
 exact and.left h,
 exact hq,
 intro h',
 apply or.inr,
 apply and.intro,
 apply and.left h,
 exact h',
 intro h'',
 apply or.elim h'',
 intro h2,
 apply and.intro,
 apply and.left h2,
 apply or.inl,
 apply and.right h2,
 intro h3,
 apply and.intro,
 apply and.left h3,
 apply or.inr,
 apply and.right h3,
end

example (α : Type) : α -> α :=
begin
 intro a,
 exact a
end

example (α : Type) : ∀ x : α, x = x :=
begin
 intro x,
 apply eq.refl x
end

example : ∃ a : ℕ, 5 = a :=
begin
 apply exists.intro,
 reflexivity
end

example : ∃ a : ℕ, a = a :=
begin
 fapply exists.intro,
 exact 1,
 reflexivity
end

example (x : ℕ) : x = x :=
begin
 revert x,
 intro y,
 reflexivity
end

example (x y : ℕ) (h : x = y) : y = x :=
begin
 symmetry,
 assumption
end

example : 3 = 3 :=
begin
reflexivity
end

example (p q : Prop) : p ∨ q -> q ∨ p :=
begin
 intro h,
 cases h with hp hq,
 right,
 exact hp,
 left,
 exact hq
end

example (p q : ℕ -> Prop) : (∃ x, p x) -> ∃ x, p x ∨ q x :=
begin
intro h,
cases h with x px,
existsi x,
left,
exact px
end

example (p q r : Prop) : p ∧ (q ∨ r) ↔ (p ∧ q) ∨ (p ∧ r) :=
begin
  apply iff.intro,
    intro h,
    apply or.elim (and.right h),
      intro hq,
      apply or.inl,
      apply and.intro,
        exact and.left h,
      exact hq,
    intro hr,
    apply or.inr,
    apply and.intro,
      exact and.left h,
    exact hr,
  intro h,
  apply or.elim h,
    intro hpq,
    apply and.intro,
      exact and.left hpq,
    apply or.inl,
    exact and.right hpq,
  intro hpr,
  apply and.intro,
    exact and.left hpr,
  apply or.inr,
  exact and.right hpr
end

example : ∀ a b c : ℕ, a = b → a = c → c = b :=
begin
  intros,
  apply eq.trans,
  apply eq.symm,
  assumption,
  assumption
end

example (y : ℕ) : (λ x : ℕ, 0) y = 0 :=
begin
  refl
end

example (x : ℕ) : x ≤ x :=
begin
  refl
end

example : ∃ a : ℕ, 5 = a :=
begin
  apply exists.intro,
  reflexivity
end

example : ∃ a : ℕ, a = a :=
begin
  fapply exists.intro,
  exact 0,
  reflexivity
end

example (x : ℕ) : x = x :=
begin
  revert x,
  -- goal is ⊢ ∀ (x : ℕ), x = x
  intro y,
  -- goal is y : ℕ ⊢ y = y
  reflexivity
end

example (p q r : Prop) : p ∧ (q ∨ r) ↔ (p ∧ q) ∨ (p ∧ r) :=
begin
  apply iff.intro,
  intro h,
   cases h with hp hqr,
   cases hqr with hq hr,
     left, constructor, repeat { assumption },
     right, constructor, repeat { assumption },
  intro h,
    cases h with hpq hpr,
      cases hpq with hp hq,
        constructor, exact hp, left, exact hq,
      cases hpr with hp hr,
        constructor, exact hp, right, exact hr
end

example (p q : ℕ → Prop) : (∃ x, p x) → ∃ x, p x ∨ q x :=
begin
 intro h,
 cases h with x px,
 constructor, left, exact px
end

example (p q : ℕ → Prop) : (∃ x, p x) → ∃ x, p x ∨ q x :=
begin
 intro h,
 cases h with x px,
 constructor, left, assumption
end

example (p q : ℕ → Prop) :
  (∃ x, p x ∧ q x) → ∃ x, q x ∧ p x :=
begin
  intro h,
  cases h with x hpq,
  cases hpq with hp hq,
  existsi x,
  split; assumption
end

universes u v
def swap_pair {α : Type u} {β : Type v} : α × β → β × α :=
begin
intro h,
cases h with ha hb,
constructor, assumption,
assumption
end

def swap_sum {α : Type u} {β : Type v} : α ⊕ β → β ⊕ α :=
begin
intro h,
cases h with ha hb,
right, assumption,
left, assumption
end

open nat
example (P : ℕ → Prop) (h₀ : P 0) (h₁ : ∀ n, P (succ n)) (m : ℕ) :
  P m :=
begin
  cases m with m', exact h₀, exact h₁ m'
end

example (p q : Prop) : p ∧ ¬ p → q :=
begin
  intro h, cases h, contradiction
end

example (p q r : Prop) : p ∧ (q ∨ r) → (p ∧ q) ∨ (p ∧ r) :=
begin
  intro h,
  exact
    have hp : p, from h.left,
    have hqr : q ∨ r, from h.right,
    show (p ∧ q) ∨ (p ∧ r),
    begin
      cases hqr with hq hr,
        exact or.inl ⟨hp, hq⟩,
      exact or.inr ⟨hp, hr⟩
    end
end

example (p q r : Prop) : p ∧ (q ∨ r) ↔ (p ∧ q) ∨ (p ∧ r) :=
begin
 apply iff.intro,
 intro h,
 cases h.right with hq hr,
 apply or.inl,
 apply and.intro,
 apply and.left h,
 assumption,
 exact or.inr ⟨h.left, hr⟩,
 intro h,
 cases h with hpq hpr,
 exact ⟨hpq.left, or.inl hpq.right⟩,
  exact ⟨hpr.left, or.inr hpr.right⟩
end

example (p q r : Prop) : p ∧ (q ∨ r) ↔ (p ∧ q) ∨ (p ∧ r) :=
begin
  apply iff.intro,
    intro h,
    cases h.right with hq hr,
      show (p ∧ q) ∨ (p ∧ r),
        { left, split, exact h.left, assumption },
      show (p ∧ q) ∨ (p ∧ r),
        { right, split, exact h.left, assumption },
  intro h,
  cases h with hpq hpr,
    show p ∧ (q ∨ r),
      { cases hpq, split, assumption, left, assumption },
    show p ∧ (q ∨ r),
      { cases hpr, split, assumption, right, assumption }
end

example (n : ℕ) : n + 1 = nat.succ n :=
begin
  show nat.succ n = nat.succ n,
  reflexivity
end

example (p q : Prop) : p ∧ q → q ∧ p :=
begin
  intro h,
  cases h with hp hq,
  split,
  assumption,
  assumption
end

example (p q r : Prop) : p ∧ (q ∨ r) ↔ (p ∧ q) ∨ (p ∧ r) :=
begin
  apply iff.intro,
  begin
    intro h,
    cases h.right with hq hr,
    begin
      show (p ∧ q) ∨ (p ∧ r),
        exact or.inl ⟨h.left, hq⟩
    end,
    show (p ∧ q) ∨ (p ∧ r),
      exact or.inr ⟨h.left, hr⟩
  end,
  intro h,
  cases h with hpq hpr,
  begin
    show p ∧ (q ∨ r),
      exact ⟨hpq.left, or.inl hpq.right⟩
  end,
  show p ∧ (q ∨ r),
    exact ⟨hpr.left, or.inr hpr.right⟩
end

example (p q r : Prop) : p ∧ (q ∨ r) ↔ (p ∧ q) ∨ (p ∧ r) :=
begin
  apply iff.intro,
  { intro h,
    cases h.right with hq hr,
    { show (p ∧ q) ∨ (p ∧ r),
        exact or.inl ⟨h.left, hq⟩ },
    show (p ∧ q) ∨ (p ∧ r),
      exact or.inr ⟨h.left, hr⟩ },
  intro h,
  cases h with hpq hpr,
  { show p ∧ (q ∨ r),
      exact ⟨hpq.left, or.inl hpq.right⟩ },
  show p ∧ (q ∨ r),
    exact ⟨hpr.left, or.inr hpr.right⟩
end

example (p q : Prop) (hp : p) : p ∨ q :=
begin left, assumption end

example (p q : Prop) (hp : p) : p ∨ q :=
by { left, assumption }

example (p q : Prop) (hp : p) (hq : q) : p ∧ q :=
by split; assumption

example (p q : Prop) (hp : p) : p ∨ q :=
by { left, assumption } <|> { right, assumption}

meta def my_tac : tactic unit :=
`[ repeat { {left, assumption} <|> right <|> assumption } ]

example (p q r : Prop) (hp : p) : p ∨ q ∨ r :=
by my_tac

example (p q r : Prop) (hq : q) : p ∨ q ∨ r :=
by my_tac

example (p q r : Prop) (hr : r) : p ∨ q ∨ r :=
by my_tac

example (p q r : Prop) : p ∧ (q ∨ r) → (p ∧ q) ∨ (p ∧ r) :=
begin
  intro h,
  cases h with hp hqr,
  show (p ∧ q) ∨ (p ∧ r),
  cases hqr with hq hr,
    have hpq : p ∧ q,
      from and.intro hp hq,
    left, exact hpq,
  have hpr : p ∧ r,
    from and.intro hp hr,
  right, exact hpr
end

example : ∃ x, x + 2 = 8 :=
begin
  let a : ℕ := 3 * 2,
  existsi a,
  reflexivity
end

example (p q r : Prop) : p ∧ (q ∨ r) → (p ∧ q) ∨ (p ∧ r) :=
begin
  intro h,
  have hp : p := h.left,
  have hqr : q ∨ r := h.right,
  show (p ∧ q) ∨ (p ∧ r),
  cases hqr with hq hr,
    exact or.inl ⟨hp, hq⟩,
  exact or.inr ⟨hp, hr⟩
end

example (p q r : Prop) : p ∧ (q ∨ r) ↔ (p ∧ q) ∨ (p ∧ r) :=
begin
  apply iff.intro,
    intro h,
    cases h.right with hq hr,
      show (p ∧ q) ∨ (p ∧ r),
        from or.inl ⟨h.left, hq⟩,
      show (p ∧ q) ∨ (p ∧ r),
        from or.inr ⟨h.left, hr⟩,
  intro h,
  cases h with hpq hpr,
    show p ∧ (q ∨ r),
      from ⟨hpq.left, or.inl hpq.right⟩,
    show p ∧ (q ∨ r),
      from ⟨hpr.left, or.inr hpr.right⟩
end


import Mathlib
/-!
# Examples of Proofs

We see our next proofs, most of which involve the `≤` relation on natural numbers.

We will see that the natural numbers are "defined" in terms of the `zero` and `succ` constructors.

* `Nat.zero : ℕ`
* `Nat.succ : ℕ → ℕ`

Analogous to this, (modulo renaming) the `≤` relation is defined in terms of the `le_refl` and `le_step` constructors.

* `Nat.le : ℕ → ℕ → Prop`
* `Nat.le_refl : ∀ n : ℕ, n ≤ n`
* `Nat.le_step : ∀ {n m : ℕ}, n ≤ m → n ≤ Nat.succ m`
-/

#check Nat.zero -- Nat.zero : ℕ
#check Nat.succ -- Nat.succ : ℕ → ℕ

#check Nat.le -- Nat.le : ℕ → ℕ → Prop
#check Nat.le_step -- Nat.le_step : ∀ {n m : ℕ}, n ≤ m → n ≤ Nat.succ m
#check Nat.le_refl -- Nat.le_refl : ∀ n : ℕ, n ≤ n

/-!
The first proof we see is of `3 ≤  3`. This is a direct application of `Nat.le_refl`. This is analogous to applying `Nat.le_refl` as a function to the argument `3`.

```lean
theorem three_le_three : 3 ≤ 3 :=
  Nat.le_refl 3
```

Our second result is similar and has a similar proof.
However in the proof we did not specify the argument `4`
and instead used the *placeholder* `_`. Lean deduced that
the unique way to get types correct is to fill in `4`.

```lean
/-- The result `4 ≤ 4`. -/
def four_le_four : 4 ≤ 4 :=
  Nat.le_refl _
```
-/

/-- The result `3 ≤ 3`. -/
theorem three_le_three : 3 ≤ 3 :=
  Nat.le_refl 3

/-- The result `4 ≤ 4`. -/
def four_le_four : 4 ≤ 4 :=
  Nat.le_refl _

#eval Nat.succ 0

-- #eval succ 0

open Nat in
#eval succ 0

#check le_refl

/-!
Some more complex proofs. In the first case the proof is given fully while in the second we allow a parameter to be inferred

```lean
theorem three_le_five : 3 ≤ 5 :=
  Nat.le_step (Nat.le_step (Nat.le_refl 3))

theorem three_le_six : 3 ≤ 6 :=
  Nat.le_step (
    Nat.le_step
     (Nat.le_step (Nat.le_refl _)))
```
-/

theorem three_le_five : 3 ≤ 5 :=
  Nat.le_step (Nat.le_step (Nat.le_refl 3))

theorem three_le_six : 3 ≤ 6 :=
  Nat.le_step (
    Nat.le_step
     (Nat.le_step (Nat.le_refl _)))

/-!
In the next proof we use tactics, specifically the `apply`
tactic. We also see a case where it fails.

```lean
theorem four_le_seven : 4 ≤ 7 := by
  apply Nat.le_step
  -- apply Nat.le_refl
  /-tactic 'apply' failed, failed to unify
  ?n ≤ ?n
    with
  4 ≤ 6-/
  apply Nat.le_step
  apply Nat.le_step
  apply Nat.le_refl
```

-/

theorem four_le_seven : 4 ≤ 7 := by
  apply Nat.le_step
  -- apply Nat.le_refl
  /-tactic 'apply' failed, failed to unify
  ?n ≤ ?n
    with
  4 ≤ 6-/
  apply Nat.le_step
  apply Nat.le_step
  apply Nat.le_refl

/-!
Note that the proofs produced by tactics are proofs in the usual sense.

```lean
#print four_le_seven /- theorem four_le_seven : 4 ≤ 7 :=
 Nat.le_step (Nat.le_step (Nat.le_step (Nat.le_refl 4))) -/
```
-/

#print four_le_seven -- theorem four_le_seven : 4 ≤ 7 :=
-- Nat.le_step (Nat.le_step (Nat.le_step (Nat.le_refl 4)))

/-!
Lean has many powerful tactics. The `decide` tactic can prove propositions that (are true and) can be decided by
an algorithm corresponding to the `Decidable` typeclass (which we see later).

```lean
theorem four_le_ten : 4 ≤ 10 :=
  by decide
```

The proof produced by the `decide` tacic is similar to the above proofs.
```lean
def four_le_ten' : 4 ≤ 10 :=
  by decide

#reduce four_le_ten' /- Nat.le.step (Nat.le.step (Nat.le.step (Nat.le.step (Nat.le.step (Nat.le.step Nat.le.refl)))))
-/
```
-/

theorem four_le_ten : 4 ≤ 10 :=
  by decide

def four_le_ten' : 4 ≤ 10 :=
  by decide

#reduce four_le_ten' /- Nat.le.step (Nat.le.step (Nat.le.step (Nat.le.step (Nat.le.step (Nat.le.step Nat.le.refl)))))
-/

/-!
We can combine tactics. `repeat` applies a tactic as long as it is valid; `first` applies the first applicable tactic.

```lean
example : 4 ≤ 10 :=
  by
    -- repeat (apply Nat.le_step) -- goal: 4 ≤ 0
    repeat (first |
          apply Nat.le_refl |
          apply Nat.le_step)
    done
```
-/
example : 4 ≤ 10 :=
  by
    -- repeat (apply Nat.le_step) -- goal: 4 ≤ 0
    repeat (first |
          apply Nat.le_refl |
          apply Nat.le_step)
    done

example : 3 ≤ 6 := by
  apply Nat.le_step
  apply three_le_five

/-! We will write some basic tactics
-/

/-- A tactic for proving `≤` for natural numbers -/
macro "nat_le" : tactic =>
  `(tactic| repeat (first |
          apply Nat.le_refl |
          apply Nat.le_step))

example : 3 ≤ 33 := by nat_le

/-- A tactic where we try repeatedly to finish with a theorem or take a step with another. -/
macro "finish_with" x:term
  "steps" y:term : tactic => do
  `(tactic| repeat (first |
          apply ($x:term) |
          apply $y))

/-!
We can use our more general tactic in different ways.

```lean
example : 4 ≤ 44 := by
  finish_with Nat.le_refl steps Nat.le_step

#check Nat.succ_le_succ -- ∀ {n m : ℕ}, n ≤ m → Nat.succ n ≤ Nat.succ m
#check Nat.zero_le -- ∀ (n : ℕ), 0 ≤ n

example : 4 ≤ 44 := by
  finish_with Nat.zero_le steps Nat.succ_le_succ
```
-/

example : 4 ≤ 44 := by
  finish_with Nat.le_refl steps Nat.le_step

#check Nat.succ_le_succ -- ∀ {n m : ℕ}, n ≤ m → Nat.succ n ≤ Nat.succ m
#check Nat.zero_le -- ∀ (n : ℕ), 0 ≤ n

example : 4 ≤ 44 := by
  finish_with Nat.zero_le steps Nat.succ_le_succ


/-
Copyright (c) 2023 María Inés de Frutos-Fernández. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author : María Inés de Frutos-Fernández
-/

import tactic
import group_theory.subgroup.basic -- importamos subgrupos de mathlib

/-!
# Subggrupos y homomorfismos

Para practicar el uso de lemas de la librería, crearemos nuestra propia definición de subgrupo,
y demostraremos lemas sobre ella utilizando la API de grupos de mathlib.

Después demostraremos lemas sobre homomorfismos de grupo (para ello, pasaremos a utilizar la
definición de subgrupo disponible en mathlib). -/

/-- `subgrupo G` es el tipo de subgrupos de un grupo `G`. -/
@[ext] structure subgrupo (G : Type) [group G] :=
(carrier : set G) -- `carrier` es un subconjunto de `G`
(one_mem' : (1 : G) ∈ carrier)
-- después añadiremos otra versión `one_mem` de este axioma sin nombrar el `carrier`
(mul_mem' {x y} : x ∈ carrier → y ∈ carrier → x * y ∈ carrier) -- también `mul_mem`
(inv_mem' {x} : x ∈ carrier → x⁻¹ ∈ carrier) -- también `inv_mem`

/- NOTA: no queremos que `subgrupo` sea una clase, ya que `G` puede tener múltiples subgrupos. -/

/- Gracias a la etiqueta `@[ext]`, la táctica `ext` funciona sobre subgrupos y nos permite
  reemplazar una igualdad de subgrupos `H₁ = H₂` por `∀ g, g ∈ H₁ ↔ g ∈ H₂`. -/

namespace subgrupo

-- Sea G un grupo y H un subgrupo de G
variables {G : Type} [group G] (H : subgrupo G)

/-- Esta instancia nos permite hablar de elementos de un subgrupo, y utilizar la notación `x ∈ H`
  para ello. -/
instance : has_mem G (subgrupo G) :=
{ mem := λ m H, m ∈ H.carrier }

/-- Esta instancia nos permite interpretar un `H : subgrupo G` como un conjunto `H : set G`. -/
instance : has_coe (subgrupo G) (set G) :=
{ coe := λ H, H.carrier }

/-- `g` es un elemento de `H` considerado como suconjunto de `G`, si y sólo si `g` es un
elemento del sugrupo `H` de `G`. -/
@[simp] lemma mem_coe {g : G} : g ∈ (H : set G) ↔ g ∈ H :=
begin
  -- These two concepts just mean the same thing
  refl
end

/-- Añadimos otro lemma de extensionalidad: dos subgrupos de un grupo son iguales si tienen
  los mismos elementos.-/
@[ext] def ext' (H K : subgrupo G) (h : ∀ g : G, g ∈ H ↔ g ∈ K) : H = K :=
begin
  ext x,
  exact h x,
end

theorem one_mem : (1 : G) ∈ H :=
begin
  apply H.one_mem',
end

theorem mul_mem {x y : G} : x ∈ H → y ∈ H → x * y ∈ H :=
begin
  apply H.mul_mem',
end

theorem inv_mem {x : G} : x ∈ H → x⁻¹ ∈ H :=
begin
  apply H.inv_mem',
end

/- ## Ejercicios sobre subgrupos
En esta sección hay una lista de teoremas sobre subgrupos. Para demostrarlos, tendréis que
usar los tres lemas anteriores, así como resultados sobre grupos disponibles en mathlib.

Si `H : subgrupo G`, y `x y : G`, los lemas anteriores dicen:
`H.one_mem : (1 : G) ∈ H`
`H.mul_mem : x ∈ H → y ∈ H → x * y ∈ H`
`H.inv_mem : x ∈ H → x⁻¹ ∈ H`
-/

@[simp] theorem inv_mem_iff {x : G} : x⁻¹ ∈ H ↔ x ∈ H :=
begin
  sorry
end

theorem mul_mem_cancel_left {x y : G} (hx : x ∈ H) :
  x * y ∈ H ↔ y ∈ H :=
begin
  sorry
end

theorem mul_mem_cancel_right {x y : G} (hx : x ∈ H) :
  y * x ∈ H ↔ y ∈ H :=
begin
  sorry
end

/-- `conjugate H g` es el subgrupo conjugado `gHg⁻¹` de `H`. -/
def conjugate (H : subgrupo G) (g : G) : subgrupo G :=
{ carrier := { a : G | ∃ h ∈ H, a = g * h * g⁻¹ },
  one_mem' := begin
    simp only [exists_prop, set.mem_set_of_eq],
    sorry
  end,
  inv_mem' := begin
    sorry
  end,
  mul_mem' := begin
    sorry
  end,
}

/-- Definición de abeliano. -/
def is_abelian (G : Type) [group G] : Prop :=
∀ a b : G, a * b = b * a

/-- Un subgrupo es normal si y sólo si es igual a todos sus conjugados. -/
def is_normal {G : Type} [group G] (H : subgrupo G) : Prop :=
∀ g : G, conjugate H g = H

/-- Si G es abeliano, todos sus subgrupos son normales. -/
example (h_ab : is_abelian G) (H : subgrupo G) : is_normal H :=
begin
  sorry
end

end subgrupo

/-
# Homomorfismos de grupos
Creamos el tipo `hom_grupos G H` de homomorfismos de grupos de `G` a `H`, y la notación `G →** H`,
es decir, `f : G →** H` indica que `f` es un homomorfismo de grupos de`G` a `H`".
-/

/-- `hom_grupos G H` es el tipo de homomorfismos de grupos de `G` a `H`. -/
@[ext] structure hom_grupos (G H : Type) [group G] [group H] :=
(to_fun : G → H)
(map_mul' (a b : G) : to_fun (a * b) = to_fun a * to_fun b)

namespace hom_grupos

-- `G` y `H` son grupos
variables {G H : Type} [group G] [group H]

-- Fijamos la notación.
notation G ` →** ` H := hom_grupos G H

/-- Esta instancia nos permite ver un homomorfismo de grupos como una función.  -/
instance : has_coe_to_fun (G →** H) (λ _, G → H) :=
{ coe := hom_grupos.to_fun }

/- la instancia anterior nos permite escribir `f (a)` -/
lemma map_mul (f : G →** H) (a b : G) :
  f (a * b) = f a * f b :=
begin
  -- `f.map_mul` y `f.map_mul'` son *definicionalmente iguales*, pero *sintácticamente distintos*.
  exact f.map_mul' a b
end

/- ## Ejercicios sobre homomorfismos de grupos -/

-- Sea `f` un homomorfismo de grupos
variable (f : G →** H)

-- El siguiente comentario nos indica un lema ↑til para probar `map_one`.
-- example (a b : G) : a * b = b ↔ a = 1 := by library_search
@[simp] lemma map_one : f 1 = 1 :=
begin
  sorry
end

lemma map_inv (a : G) : f a⁻¹ = (f a)⁻¹ :=
begin
  sorry
end

variable (G)

/-- `id G` es el homomorfismo identidad de `G` a `G`. -/
def id : G →** G :=
{ to_fun   := λ a, a,
  map_mul' := begin
    sorry
  end }

variables {K : Type} [group K] {G}

/-- `φ.comp ψ` es la composición de `φ` y `ψ`. -/
def comp (φ : G →** H) (ψ : H →** K) : G →** K :=
{ to_fun := λ g, ψ (φ g),
  map_mul' := λ a b, begin
    sorry,
  end
}
lemma id_comp : (id G).comp f = f :=
begin
  sorry,
end

lemma comp_id : f.comp (id H) = f :=
begin
  sorry,
end

lemma comp_assoc {L : Type} [group L] (φ : G →** H) (ψ : H →** K) (ρ : K →** L) :
  (φ.comp ψ).comp ρ = φ.comp (ψ.comp ρ) :=
begin
  sorry,
end

/-- El kernel de un homomorfismo de grupos, como subgrupo del dominio. -/
def ker (f : G →** H) : subgroup G :=
{ carrier := {g : G | f g = 1 },
  one_mem' := begin sorry, end,
  mul_mem' := begin
    sorry,
  end,
  inv_mem' := begin
    sorry,
  end,
}

/-- La imagen de un homomorfismo de grupos, como subgrupo del codominio. -/
def im (f : G →** H) : subgroup H :=
{ carrier := {h : H | ∃ g : G, f g = h },
  one_mem' := begin
    sorry,
  end,
  mul_mem' :=  begin
    sorry,
  end,
  inv_mem' := begin
    sorry,
  end,
}

/-- La imagen de un subgrupo bajo un homomorfismo de grupos, como subgrupo del codominio. -/
def map (f : G →** H) (K : subgroup G) : subgroup H :=
{ carrier := {h : H | ∃ g : G, g ∈ K ∧ f g = h },
  one_mem' := begin
    sorry,
  end,
  mul_mem' := begin
    sorry,
  end,
  inv_mem' := begin
    sorry,
  end,
}

/-- La preimagen de un subgrupo bajo un homomorfismo de grupos, como subgrupo del dominio. -/
def comap (f : G →** H) (K : subgroup H) : subgroup G :=
{ carrier := {g : G | f g ∈ K },
  one_mem' := begin
    sorry,
  end,
  mul_mem' := begin
    sorry,
  end,
  inv_mem' := begin
    sorry,
  end,
}

end hom_grupos


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

/-!

# Logic in Lean, example sheet 3 : "not" (`¬`)

We learn about how to manipulate `¬ P` in Lean.

# The definition of `¬ P`

In Lean, `¬ P` is *defined* to mean `P → false`. So `¬ P` and `P → false`
are *definitionally equal*. Check out the explanation of definitional
equality in the "equality" section of Part B of the course notes:
https://www.ma.imperial.ac.uk/~buzzard/xena/formalising-mathematics-2023/Part_B/equality.html

## Tactics

You'll need to know about the tactics from the previous sheets,
and the following tactics may also be useful:

* `change`
* `by_contra`
* `by_cases`

-/

-- Throughout this sheet, `P`, `Q` and `R` will denote propositions.

variables (P Q R : Prop)

example : ¬ true → false :=
begin
  intro hT,
  apply hT,
  triv,
end

-- using the proof of true
example : ¬ true → false :=
begin
  intro hT,
  exact hT true.intro,
end

example : false → ¬ true :=
begin
  intros hF _,
  exact hF,
end

example : ¬ false → true :=
begin
  intro _,
  triv,
end

example : true → ¬ false :=
begin
  intros _ hF,
  exact hF,
end

example : false → ¬ P :=
begin
  intros hF _,
  exact hF,
end

example : P → ¬ P → false :=
begin
  intros hP hNP,
  exact hNP hP,
end

example : P → ¬ (¬ P) :=
begin
  intros hP hPF,
  exact hPF hP,
end

example : (P → Q) → (¬ Q → ¬ P) :=
begin
  intros hPQ hQF hP,
  exact hQF (hPQ hP),
end

-- TODO: what the what kinda proof is this
example : ¬ ¬ false → false :=
begin
  intro hF,
  apply hF,
  intro hF1,
  exact hF1,
end

example : ¬ ¬ P → P :=
begin
  intro hPFF,
  by_contra,
  exact hPFF h,
end

-- using the proof above
example : ¬ ¬ false → false :=
begin
  intro hF,
  by_contra,
  exact hF h,
end

example : (¬ Q → ¬ P) → (P → Q) :=
begin
  intros hNQP hP,
  by_contra hQ,
  exact hNQP hQ hP,
end


import Mathlib.Tactic.Basic
import Mathlib.Tactic.Cases
import Basics.FunctionDefinitions
namespace hglv
/-!
## Proofs by Mathematical Induction

The `induction'` tactic performs structural induction on an inductive type. _Structural induction_
simply means that the induction follows the structure of the inductive type. For natural numbers
constructed from `Nat.zero` and `Nat.succ`, structural induction corresponds to standard mathematical
induction: To prove `p n`, it suffices to prove `p 0` and `∀k, p k → p (k + 1)`. Equipped with `induction'`,
we can reason about the addition and multiplication operations we defined by recursion in
[Function Definitions](../Basics/FunctionDefinitions.lean.md). Addition is defined by recursion on its
second argument. We will prove two lemmas, `add_zero` and `add_succ`, that give us alternative equations
that recurse on the first argument. We start with `add_zero`:
-/
lemma add_zero (n : ℕ) :
add 0 n = n := by
  induction' n with n ih
  { rfl }
  { simp [add, ih] }

/-!
The first `{ }` encloses the base case `⊢ add 0 0 = 0`. The second block corresponds
to the induction step
```lean
n : N, ih : add 0 n = n ` add 0 (Nat.succ n) = (Nat.succ n)
```
The local variable `n` in the induction step should not be confused with the `n` in
the lemma statement. Like mathematicians, `induction’` tries to reuse variable
names that are no longer needed. The name `ih` for the induction hypothesis, in
the induction step, is also generated by the `induction’` tactic.

We can keep on proving lemmas by structural induction:
-/

lemma add_succ (m n : ℕ) :
  add (Nat.succ m) n = Nat.succ (add m n) := by
  induction' n with n ih
  { rfl }
  { simp [add, ih] }

lemma add_comm (m n : ℕ) :
  add m n = add n m := by
  induction' n with n ih
  { simp [add, add_zero] }
  { simp [add, add_succ, ih] }

lemma add_assoc (l m n : ℕ) :
  add (add l m) n = add l (add m n) := by
  induction' n with n ih -- ih: add (add l m) n = add l (add m n)
  { rfl }
  { simp [add, ih] }

/-!
Once we have proved that a binary operator is commutative and associative, it
is a good idea to let Lean’s automation, notably `cc`, know about this
using type classes.  See [Type Classes](../Basics/TypeClasses.lean.md).

The following example uses the `cc` tactic to reason up to associativity and commutativity of `add`:
-/
set_option trace.Meta.Tactic.simp.rewrite true
lemma mul_add (l m n : ℕ) :
mul l (add m n) = add (mul l m) (mul l n) := by
  induction' n with n ih -- ih: mul l (add m n) = add (mul l m) (mul l n)
  { rfl }
  {
    simp [add_comm, add_assoc, add_succ]
  }
/-!
-- BUGBUG: 'cc' is still missing from mathlib... trying to write it without cc.

Here are a few hints on how to carry out proofs by induction:

- It is usually beneficial to perform induction following the structure of the definition of one of
  the functions appearing in the goal. In particular, if a function is defined by recursion on its
  _n_ th argument, it usually makes sense to perform the induction on that argument.

- If the base case of an induction is difficult, this is often a sign that the wrong variable was
  chosen or that some lemmas should be proved first.

-/


-- Igualdad a partir de acotaciones del valor absoluto de la diferencia
-- ====================================================================

import data.real.basic

variables (x y : ℝ)

-- ----------------------------------------------------
-- Ejercicio 1. Definir la notación |x| para el valor
-- absoluto de x.
-- ----------------------------------------------------

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

-- ----------------------------------------------------
-- Ejercicio 2. Demostrar que si |x - y| ≤ ε, para todo
-- ε > 0, entonces x = y.
-- ----------------------------------------------------

-- Se usará el lema demostrado anteriormente
lemma cero_de_abs_mne_todos
  (h : ∀ ε > 0, |x| ≤ ε)
  : x = 0 :=
abs_eq_zero.mp
  (eq_of_le_of_forall_le_of_dense (abs_nonneg x) h)

-- 1ª demostración
example
  (h : ∀ ε > 0, |x - y| ≤ ε)
  : x = y :=
begin
  rw ← sub_eq_zero,
  exact cero_de_abs_mne_todos (x - y) h,
end

-- 2ª demostración
lemma ig_de_abs_sub_mne_todos
  (h : ∀ ε > 0, |x - y| ≤ ε)
  : x = y :=
sub_eq_zero.mp (cero_de_abs_mne_todos (x - y) h)


import definitions tactic
universe u

namespace Algebra

namespace group

variables {G : Type u} [group G]

lemma group_nonempty : nonempty G := ⟨0⟩

def is_ident (e : G) : Prop := ∀ g : G, e + g = g ∧ g + e = g

lemma id_unique : ∀ e : G, is_ident e → e = 0 :=
begin
  intros e H,
  rw is_ident at H, cases H 0 with H1 H2,
  simp at H1,
  assumption
end

def is_inv (g g' : G) : Prop := g + g' = 0 ∧ g' + g = 0

lemma inv_unique : ∀ g g': G, is_inv g g' →  (-g) = g':=
begin
  intros a b H, cases H with H1 H2,
  have H3 : b + 0 = b, from group.ident_r,
  have H4 : b + (a + (-a)) = b, by {rwa ← group.inv_r at H3},
  rw ← group.associative at H4,
  rwa [H2, group.ident_l] at H4,
end

@[simp]
lemma op_inv : ∀ a b : G, -(a + b) = (-b) + (-a) :=
begin
  intros a b,
  have H1 : (a + b) + ((-b) + (-a)) = 0,
  by {rw ← group.associative, simp,},
  have H2 : (-b) + (-a) + (a + b) = 0,
  by { rw ← group.associative, rw  @group.associative G _inst_1 (-b) (-a) a,
       rw group.inv_l, simp,},
  have H3 : is_inv (a + b) (-b + -a) := ⟨H1,H2⟩,
  exact inv_unique (a + b) (-b + -a) H3,
end

def congr_l {b c : G} (a : G) (h : b = c) : a + b = a + c :=
congr (refl (λ g, a + g)) h

def congr_r {b c : G} (a : G) (h : b = c) :  b + a = c + a:=
@congr G G (λ g, g + a) (λ g, g + a) b c (by refl) h

lemma cancel_right : ∀ a b c : G, b + a = c + a →  b = c :=
begin
  intros a b c H,
  have : b + a + (-a) = c + a + (-a) := congr_r (-a) H,
  simp at this, exact this
end

lemma cancel_left : ∀ a b c : G, a + b = a + c → b = c :=
begin
  intros a b c H,
  have :  (-a) + (a + b) = (-a) + (a + c) := congr_l (-a) H,
  simp at this, exact this
end

lemma square_ident_imp_self_inv (G : Type u) [group G] : ∀ g : G, g ↟ 2 = 0 → (-g = g) :=
begin
  intro g, repeat {rw pow}, rw group.ident_r,
  intro H, apply inv_unique, exact ⟨H,H⟩
end

lemma square_ident_imp_abelian : (∀ g : G, g ↟ 2 = 0) → abelian G :=
begin
  intros H1 a b,
  obtain H2 := square_ident_imp_self_inv G a (H1 a),
  obtain H3 := square_ident_imp_self_inv G b (H1 b),
  obtain H4 := square_ident_imp_self_inv G (a + b) (H1 (a + b)),
  simp at H4,
  rw [H2,H3] at H4, symmetry, exact H4,
end

end group

end Algebra


-- ---------------------------------------------------------------------
-- Ejercicio 1. Realizar las siguientes acciones:
-- 1. Importar la librería de tácticas.
-- 2. Habilitar la lógica clásica.
-- 3. Declarar Q como una variable proposicional.
-- ----------------------------------------------------------------------

import tactic           -- 1
open_locale classical   -- 2
variable (Q : Prop)     -- 3

-- ---------------------------------------------------------------------
-- Ejercicio 2. Demostrar que si
--     ¬ ¬ Q
-- entonces
--    Q
-- ----------------------------------------------------------------------

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

example
  (h : ¬ ¬ Q)
  : Q :=
begin
  by_contradiction h1,
  exact (h h1),
end

-- Prueba
-- ======

/-
Q : Prop,
h : ¬¬Q
⊢ Q
  >> by_contradiction h1,
h1 : ¬Q
⊢ false
  >> exact (h h1),
no goals
-/

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

example
  (h : ¬ ¬ Q)
  : Q :=
not_not.mp h

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

example
  (h : ¬ ¬ Q)
  : Q :=
by tauto

-- Comentario: La táctica tauto demuestra las tautologís
-- proposionales.

-- ---------------------------------------------------------------------
-- Ejercicio 3. Demostrar que si
--    Q
-- entonces
--    ¬ ¬ Q
-- ----------------------------------------------------------------------

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

example
  (h : Q)
  : ¬ ¬ Q :=
begin
  intro h1,
  exact (h1 h),
end

-- Prueba
-- ======

/-
Q : Prop,
h : Q
⊢ ¬¬Q
  >> intro h1,
h1 : ¬Q
⊢ false
  >> exact (h1 h)
no goals
-/

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

example
  (h : Q)
  : ¬ ¬ Q :=
not_not.mpr h

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

example
  (h : Q)
  : ¬ ¬ Q :=
by tauto


import analysis.inner_product_space.pi_L2

/- Good work Priya!
90/90 Correctness
10/10 Style
-/

/-
This assignment is due midnight on Wednesday, February 1.

Copy this file to `src/assignment2/assignment2.lean` in your personal `har-ifvm-23-{username}`
directory, and fill in the proofs. When you are done, push it to Github:
```
  git add assignment2.lean
  git commit -m "my assignment2 solutions"
  git push
```
Feel free to push any preliminary commits.
-/

/-
FIRST EXERCISE: the parallelogram law
-/

namespace parallelogram_exercise
open_locale real_inner_product_space

/-
In the following variable declaration, `euclidean_space ℝ (fin 2)` represents
the Euclidean plane, ℝ × ℝ, with the usual definition of inner product.
-/

variables x y z : euclidean_space ℝ (fin 2)

#check ⟪x, y⟫    -- the inner product
#check ‖x‖       -- the norm
#check x + y
#check 3 • x

/-
Hovering over the brackets in VS Code shows that the angle brackets for the inner product can be
written as `\<<` and `\>>`, and the bars for the norm can be written `\||`.

They satisfy the following identities:
-/

example : ⟪x, y + z⟫ = ⟪x, y⟫ + ⟪x, z⟫ := inner_add_right
example : ⟪x + y, z⟫ = ⟪x, z⟫ + ⟪y, z⟫ := inner_add_left
example : ⟪x, y - z⟫ = ⟪x, y⟫ - ⟪x, z⟫ := inner_sub_right
example : ⟪x - y, z⟫ = ⟪x, z⟫ - ⟪y, z⟫ := inner_sub_left

example :  ⟪x, x⟫ = ‖x‖^2 := real_inner_self_eq_norm_sq x

/-
The following identity is known as the *parallelogram law*. It says that the sum of the squares
of the lengths of the four sides of a parallegram is equal to the sum of the squares of the
lengths of the diagonals.

You can read a proof of it on Wikipedia: https://en.wikipedia.org/wiki/Parallelogram_law.

Formalize it using only the four identities above as well as the `ring` tactic.
-/

example :
  ‖x + y‖^2 + ‖x - y‖^2  = 2 * (‖x‖^2 + ‖y‖^2) :=
begin
  rw ← real_inner_self_eq_norm_sq,
  rw ← real_inner_self_eq_norm_sq,
  rw inner_add_right,
  rw inner_add_left,
  rw real_inner_self_eq_norm_sq x,
  rw inner_add_left,
  rw real_inner_self_eq_norm_sq y,
  ring,
  rw inner_sub_right,
  rw inner_sub_left,
  rw inner_sub_left,
  rw real_inner_self_eq_norm_sq,
  rw real_inner_self_eq_norm_sq,
  ring,
end

/-
In fact, the theorem holds for arbitrary inner product spaces, with exactly the same proof.
You can check this by replacing the variable declaration above by the following:

variables {E : Type*} [inner_product_space ℝ E]
variables x y z : E
-/

end parallelogram_exercise

/-
SECOND EXERCISE: Boolean rings
-/

namespace boolean_ring_exercise

/-
The notion of a ring is discussed in the textbook:
https://leanprover-community.github.io/mathematics_in_lean/02_Basics.html#proving-identities-in-algebraic-structures

A *Boolean* ring satisfies the additional property that for every `x`, `x^2 = x`.
You can read about Boolean rings here:
https://en.wikipedia.org/wiki/Boolean_ring
-/

variables {R : Type*} [ring R]

-- This is the assumption that makes `R` a Boolean ring:
variable (idem : ∀ x : R, x^2 = x)

-- This adds `idem` as a hypothesis to every theorem:
include idem

/-
This exercise asks you to prove that every Boolean ring is commutative, i.e.
satisfies `x * y = y * x` for every `x` and `y`. Unfortunately, we cannot use the
`ring` tactic along the way, since it only works once we know a ring is commutative.
So you will have to rely on theorems like `mul_add`, `add_mul`, etc. in the textbook.
-/

-- This is useful:
theorem mul_idem (x : R) : x * x = x :=
by rw [←pow_two, idem]

-- Unfortunately, you have to write `mul_idem idem` to use it.
example (x y : R) : (x + y) * (x + y) = x + y :=
by rw [mul_idem idem]

/-
Prove the following theorem, following the calculation in Wikipedia:
x + x = (x+x)^2 = x^2 + x^2 + x^2 + x^2 = (x + x) + (x + x).
-/

theorem add_self (x : R) : x + x = 0 :=
begin
  have h1 : x + x = (x + x) + (x + x),
  calc
    x + x = (x + x)^2 :
      begin
        rw idem,
      end
    ... = x + x + (x + x) :
      begin
        rw pow_two,
        rw mul_add,
        rw add_mul,
        rw ← add_assoc,
        rw mul_idem idem,
        rw add_assoc,
      end,
  have h2 : (x + x) + (x + x) - (x + x) = (x + x) - (x + x),
    by rw ←h1,
  rw [add_sub_cancel, sub_self] at h2,
  exact h2
end

-- Note: again, to use this theorem you have to mention `idem` explicitly
example (y : R) : y + y = 0 :=
add_self idem y

/-
Prove `neg_eq_self` using the calculation `-x = 0 - x = x + x - x = x`. You can use the theorems
`zero_sub` and `add_sub_cancel`, as well as `add_self idem`.
-/

theorem neg_eq_self (x : R) : -x = x :=
begin
  rw ← zero_sub,
  rw ← add_self idem x,
  rw add_sub_cancel,
end

/-
This is a corollary.
-/

theorem sub_eq_add (x y : R) : x - y = x + y :=
by rw [sub_eq_add_neg, neg_eq_self idem]

/-
Prove this, using the calculation `x = x + y - y = 0 - y = -y = y`.
-/
theorem eq_of_add_eq_zero {x y : R} (h : x + y = 0) :
  x = y :=
begin
  rw ← add_zero x,
  rw ← sub_self y,
  rw ← add_sub_assoc,
  rw h,
  rw zero_sub,
  rw neg_eq_self idem,
end

/- Finally, prove `mul_comm` using the following argument from Wikipedia:

`0 + x + y = x + y = (x + y)^2 = x^2 + xy + yx + y^2 = xy + yx + x + y`

You can use the `abel` tactic to rearrange sums.
-/

example (x y : R) : x + x * y + y * x + y = x * y + y * x + x + y :=
by abel

theorem mul_comm (x y : R) : x * y = y * x :=
begin
  have h1 : 0 + (x + y) = (x * y + y * x) + (x + y),
  calc
    0 + (x + y) = (x + y)^2 :
      begin
        rw zero_add,
        rw idem,
      end
    ... = x * y + y * x + (x + y) :
      begin
        rw pow_two,
        rw mul_add,
        rw add_mul,
        rw add_mul,
        rw ← add_assoc,
        rw mul_idem idem,
        rw mul_idem idem,
        abel,
      end,
  have h2 : 0 = x * y + y * x,
    exact add_right_cancel h1,
  show x * y = y * x,
    exact eq_of_add_eq_zero idem h2.symm
end

end boolean_ring_exercise

/-
THIRD EXERCISE: absolute values
-/

namespace absolute_value_exercise

variables x y z w : ℝ

/-
Bounding sums often boils down to using transitivity and inequalities. Step through the
next example and make sure you understand what is going on. `swap` switches the order of the goals,
and `norm_num` does a numeric calculation.

The `transitivity` tactic lets you state the intermediate expression. In contrast, applying
`le_trans` lets you make Lean figure it out from context. With the `transitivity` tactic,
we have to specify that the numerals are real numbers, because otherwise Lean assumes that they
are natural numbers.
-/

example
    (hx : abs x ≤ 10)
    (hy : abs y ≤ 5)
    (hz : abs z ≤ 4) :
  abs (x + y + z) ≤ 19 :=
begin
  transitivity ((10 : ℝ) + 5 + 4),
  swap, { by norm_num },
  apply le_trans,
  apply abs_add,
  apply add_le_add,
  { -- first goal
    apply le_trans,
    apply abs_add,
    exact add_le_add hx hy },
  -- second goal
  exact hz,
end

/-
We can finish the second goal earlier by giving `hz` right away.
-/

example
    (hx : abs x ≤ 10)
    (hy : abs y ≤ 5)
    (hz : abs z ≤ 4) :
  abs (x + y + z) ≤ 19 :=
begin
  transitivity ((10 : ℝ) + 5 + 4),
  swap, { by norm_num },
  apply le_trans,
  apply abs_add,
  -- the underscore means: figure it out or leave it as another goal
  apply add_le_add _ hz,
  apply le_trans,
  apply abs_add,
  exact add_le_add hx hy,
end

/-
Prove the following. You can also use the theorems `abs_sub`, `pow_two` to expand `w^2` to `w * w`,
`sq_abs`, and `mul_le_mul`. For the last theorem, you'll need to know that an absolute value is
nonnegative, which is the theorem `abs_nonneg`. You can also use `norm_num` to show that
`(9 : ℝ) = 3 * 3`.
-/

example
    (hx : abs x ≤ 10)
    (hy : abs y ≤ 5)
    (hz : abs z ≤ 4)
    (hw : abs w ≤ 3) :
  abs (x - y + z) + w^2 ≤ 28 :=
begin
  have h1 : w^2 ≤ 9,
  begin
    rw ← sq_abs,
    rw pow_two,
    transitivity ((3 : ℝ) * 3),
    swap, { norm_num },
    apply mul_le_mul,
    exact hw,
    exact hw,
    apply abs_nonneg,
    norm_num,
  end,
  transitivity ((10 : ℝ) + 5 + 4 + 9),
  apply add_le_add,
  swap, { exact h1 },
  swap, { norm_num },
  apply le_trans,
  apply abs_add,
  apply add_le_add,
  swap, { exact hz },
  apply le_trans,
  apply abs_sub,
  apply add_le_add,
  exact hx,
  exact hy,
end

end absolute_value_exercise


section propositional

variables P Q R : Prop

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

theorem doubleneg_intro :
  P → ¬¬P  :=
begin
  intros h h',
  contradiction,
end

theorem doubleneg_elim :
  ¬¬P → P  :=
begin
  intro h,
  by_contradiction h,
  contradiction,
end

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

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

theorem disj_comm :
  (P ∨ Q) → (Q ∨ P)  :=
begin
  intro h,
  cases h,
  right,
  apply h,
  left,
  apply h,
end

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

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

theorem impl_as_disj_converse :
  (¬P ∨ Q) → (P → Q)  :=
begin
  intros h h',
  cases h,
  contradiction,
  apply h,
end

theorem disj_as_impl :
  (P ∨ Q) → (¬P → Q)  :=
begin
  intros h h',
  cases h,
  contradiction,
  apply h,
end

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

theorem impl_as_contrapositive :
  (P → Q) → (¬Q → ¬P)  :=
begin
  intros h h' h'',
  have hq : Q := h h'',
  contradiction,
end

theorem impl_as_contrapositive_converse :
  (¬Q → ¬P) → (P → Q)  :=
begin
  intros h p,
  by_contradiction hboom,
  apply h,
  exact hboom,
  exact p,
end

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

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

theorem lem_irrefutable :
  ¬¬(P∨¬P)  :=
begin
  intro h,
  apply h,
  right,
  intro p,
  apply h,
  left,
  exact p,
end

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

theorem peirce_law_weak :
  ((P → Q) → P) → ¬¬P  :=
begin
  intros h np,
  have pq : P → Q,
  intro p,
  contradiction,
  have p : P := h pq,
  contradiction,
end

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

theorem disj_as_negconj :
  P∨Q → ¬(¬P∧¬Q)  :=
begin
  intros h h'',
  cases h'' with hnp hnq,
  cases h,
  contradiction,
  contradiction,
end

theorem conj_as_negdisj :
  P∧Q → ¬(¬P∨¬Q)  :=
begin
  intros h h'',
  cases h with hp hq,
  cases h'',
  contradiction,
  contradiction,
end

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

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

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

theorem demorgan_conj :
  ¬(P∧Q) → (¬Q ∨ ¬P)  :=
begin
  intro h,
  by_cases lem : Q,
  right,
  intro p,
  apply h,
  split,
  exact p,
  exact lem,
  left,
  exact lem,
end

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

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

theorem demorgan_disj_law :
  ¬(P∨Q) ↔ (¬P ∧ ¬Q)  :=
begin
  split,
  intro h,
  split,
  intro p,
  have poq : P∨Q,
  left,
  exact p,
  exact h poq,
  intro q,
  have poq : P∨Q,
  right,
  exact q,
  exact h poq,
  intros h h',
  cases h with np nq,
  cases h',
  contradiction,
  contradiction,
end

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

theorem distr_conj_disj :
  P∧(Q∨R) → (P∧Q)∨(P∧R)  :=
begin
  intro h,
  cases h with x y,
  cases y,
  left,
  split,
  exact x,
  exact y,
  right,
  split,
  exact x,
  exact y,
end

theorem distr_conj_disj_converse :
  (P∧Q)∨(P∧R) → P∧(Q∨R)  :=
begin
  intro h, -- Suponha -L.
  split,
  cases h,
  cases h with p q,
  exact p,
  cases h with p q,
  exact p,
  cases h,
  cases h with p q,
  left,
  exact q,
  cases h with p r,
  right,
  exact r,
end

theorem distr_disj_conj :
  P∨(Q∧R) → (P∨Q)∧(P∨R)  :=
begin
  intro h,
  cases h,
  split,
  left,
  exact h,
  left,
  exact h,
  cases h 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 h,
  cases h with poq por,
  cases poq,
  left,
  exact poq,
  cases por,
  left,
  exact por,
  right,
  split,
  exact poq,
  exact por,
end

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

theorem curry_prop :
  ((P∧Q)→R) → (P→(Q→R))  :=
begin
  intros h p q,
  have pq : P ∧ Q,
  split,
  exact p,
  exact q,
  exact h pq,
end

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

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

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

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

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

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

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

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

theorem conj_idempot :
  (P∧P) ↔ P :=
begin
  split,
  intro h,
  cases h with x y,
  exact x,
  intro h,
  split,
  exact h,
  exact h,
end

theorem disj_idempot :
  (P∨P) ↔ P  :=
begin
  split,
  intro h,
  cases h,
  exact h,
  exact h,
  intro h,
  left,
  exact h,
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 h x p,
  apply h,
  existsi x,
  exact p,
end

theorem demorgan_exists_converse :
  (∀x, ¬P x) → ¬(∃x, P x)  :=
begin
  intros h h',
  cases h' with a ha,
  have hna : ¬P a := h a,
  contradiction,
end

theorem demorgan_forall :
  ¬(∀x, P x) → (∃x, ¬P x)  :=
begin
  rw contrapositive_law,
  intros e p,
  apply p,
  intro x,
  by_cases lem : P x,
  exact lem,
  exfalso,
  apply e,
  existsi x,
  exact lem,
end

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

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

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

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

theorem exists_as_neg_forall :
  (∃x, P x) → ¬(∀x, ¬P x)  :=
begin
  intros h h',
  cases h with x px,
  have npx : ¬P x := h' x,
  contradiction,
end

theorem forall_as_neg_exists :
  (∀x, P x) → ¬(∃x, ¬P x)  :=
begin
  intros h h',
  cases h' with x npx,
  have px : P x := h x,
  contradiction,
end

theorem forall_as_neg_exists_converse :
  ¬(∃x, ¬P x) → (∀x, P x)  :=
begin
  intros h x,
  by_contradiction hboom,
  have e : ∃ (x : U), ¬P x,
  existsi x,
  exact hboom,
  exact h e,
end

theorem exists_as_neg_forall_converse :
  ¬(∀x, ¬P x) → (∃x, P x)  :=
begin
  rw contrapositive_law,
  intros e p,
  apply p,
  intros x px,
  apply e,
  existsi x,
  exact px,
end

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

theorem exists_as_neg_forall_law :
  (∃x, P x) ↔ ¬(∀x, ¬P x)  :=
begin
  split,
  exact exists_as_neg_forall U P,
  exact exists_as_neg_forall_converse U P,
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 h,
  cases h with x y,
  cases y with px qx,
  split,
  existsi x,
  exact px,
  existsi x,
  exact qx,
end

theorem exists_disj_as_disj_exists :
  (∃x, P x ∨ Q x) → (∃x, P x) ∨ (∃x, Q x)  :=
begin
  intro h,
  cases h with x y,
  cases y,
  left,
  existsi x,
  exact y,
  right,
  existsi x,
  exact y,
end

theorem exists_disj_as_disj_exists_converse :
  (∃x, P x) ∨ (∃x, Q x) → (∃x, P x ∨ Q x)  :=
begin
  intro h,
  cases h,
  cases h with x y,
  existsi x,
  left,
  exact y,
  cases h with x y,
  existsi x,
  right,
  exact y,
end

theorem forall_conj_as_conj_forall :
  (∀x, P x ∧ Q x) → (∀x, P x) ∧ (∀x, Q x)  :=
begin
  intro h,
  split,
  intro x,
  have pxqx : P x ∧ Q x := h x,
  cases pxqx with px qx,
  exact px,
  intro x,
  have pxqx : P x ∧ Q x := h x,
  cases pxqx with px qx,
  exact qx,
end

theorem forall_conj_as_conj_forall_converse :
  (∀x, P x) ∧ (∀x, Q x) → (∀x, P x ∧ Q x)  :=
begin
  intros h x,
  cases h,
  split,
  have px : P x := h_left x,
  exact px,
  have qx : Q x := h_right x,
  exact qx,
end

theorem forall_disj_as_disj_forall_converse :
  (∀x, P x) ∨ (∀x, Q x) → (∀x, P x ∨ Q x)  :=
begin
  intros h x,
  cases h,
  have px : P x := h x,
  left,
  exact px,
  have qx : Q x := h x,
  right,
  exact qx,
end

/- NOT THEOREMS --------------------------------

theorem forall_disj_as_disj_forall :
  (∀x, P x ∨ Q x) → (∀x, P x) ∨ (∀x, Q x)  :=
begin
end

theorem exists_conj_as_conj_exists_converse :
  (∃x, P x) ∧ (∃x, Q x) → (∃x, P x ∧ Q x)  :=
begin
end

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

end predicate


/-
3. Consider the “barber paradox,” that is, the claim that in a certain town there is a (male) barber that shaves all and only the men who do not shave themselves. Prove that this is a contradiction:
-/

open classical

variables (men : Type) (barber : men)
variable  (shaves : men → men → Prop)

example (h : ∀ x : men, shaves barber x ↔ ¬ shaves x x) : false :=
    or.elim (em (shaves barber barber))
    (
        assume h1 : shaves barber barber,
        have h2 : shaves barber barber ↔ ¬ shaves barber barber, from h barber,
        have h3 : shaves barber barber → ¬ shaves barber barber, from iff.mp h2,
        have h4 : ¬ shaves barber barber, from h3 h1,
        false.elim (h4 h1)
    )
    (
        assume h1 : ¬ shaves barber barber,
        have h2 : shaves barber barber ↔ ¬ shaves barber barber, from h barber,
        have h3 : ¬ shaves barber barber → shaves barber barber, from iff.mpr h2,
        have h4 : shaves barber barber, from h3 h1,
        false.elim (h1 h4)
    )
-- short version
example (h : ∀ x : men, shaves barber x ↔ ¬ shaves x x) : false :=
    (em (shaves barber barber)).elim
    (λ h1, absurd h1 ((iff.mp (h barber)) h1))
    (λ h1, absurd ((iff.mpr (h barber)) h1) h1)


/-
Copyright (c) 2023 María Inés de Frutos-Fernández. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author : María Inés de Frutos-Fernández
-/

import tactic
import data.nat.parity
import data.real.basic
import data.finset.basic
import data.real.basic

/-!
# Nuevas tácticas
* `specialize`
* `convert`
* `linarith`
-/

/- ## specialize
La táctica `specialize` se puede aplicar a hipótesis locales
de la forma `∀ x₁ ... xₙ , P x₁ ... xₙ` o `P → Q → R → ...`.

Al aplicar `specialize h a₁ ... aₙ`, las premisas de `h` se instancian con los
argumentos `a₁ ... aₙ`.

La táctica añade una nueva hipótesis `h := h a₁ ... aₙ` con el mismo nombre e intenta
eliminar la anterior

NOTA: no siempre es necesario utilizar esta táctica. Es especialmente útil cuando queremos
reescribir en la hipótesis resultante.
-/

example (h : ∀ n : ℕ, even (2*n)) : even 2 :=
begin
  specialize h 1,
  rw mul_one at h,
  exact h,
end

example {P Q : Prop} (hP : P) (h : P → Q) : Q :=
begin
  specialize h hP,
  exact h,
  -- Pero también podríamos haber hecho `exact h hP` directamente.
end

/- ## convert
`convert h` es similar a `refine h`, pero el tipo de `h` no tiene que ser exactamente
igual al de la meta, sino que se crearán nuevas metas para cada una de las diferencias.
-/

example (x y z ε : ℝ) (h : |x| < ε) : |x + z - z| < ε :=
begin
  convert h,
  ring,
end

/- A veces `convert` es demasiado agresivo. Podemos controlar su comportamiento utilizando
`convert h using n`, donde `n` es un número pequeño.  -/
example {x : ℝ} (hx : |x| = 3) : |(-x)| = 3 :=
begin
  --convert hx, -- Reduce la meta a -x = x
  convert hx using 1,
  exact abs_neg _,
end

/- ## linarith
La táctica `linarith` se puede utilizar para demostrar una desigualdad linear, o para derivar
una contradicción a partir de una familia de igualdades y desigualdades.
Utiliza las hipótesis locales.
-/

example {a b c d : ℝ} (h1 : a < b) (h2 : b ≤ c) (h3 : c = d) :
  a + a < d + b :=
begin
  linarith
end

example (x y z : ℚ) (h1 : 2*x  < 3*y) (h2 : -4*x + 2*z < 0) (h3 : 12*y - 4* z < 0)  : false :=
by linarith


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

/-!

# Logic in Lean, example sheet 3 : "not" (`¬`)

We learn about how to manipulate `¬ P` in Lean.

# Important : the definition of `¬ P`

In Lean, `¬ P` is *defined* to mean `P → false`. So `¬ P` and `P → false`
are *the same thing* and can be used interchangeably. You can change
from one to the other for free.

## Tactics

You'll need to know about the tactics from the previous sheets,
and also the following tactics:

* `change` (optional)
* `by_contra`
* `by_cases`

### The `change` tactic

The `change` tactic changes a goal to a goal which
is *equal to it by definition*. The example you need to know
is that `¬ P` and `P → false` are equal by definition.

If your goal is `⊢ ¬ P` then `change P → false,` will
change it to `P → false`. Similarly if you have a hypothesis
`h : ¬ P` then `change P → false at h,` will change it to `h : P → false`.

Note that this tactic is just for psychological purposes. If you finish
a proof which uses this tactic, try commenting out the `change` lines
and note that it doesn't break.

### The `by_contra` tactic

If your goal is `⊢ P` and you want to prove it by contradiction,
`by_contra h,` will change the goal to `false` and add a hypothesis
`h : ¬ P`.

### The `by_cases` tactic

If `P : Prop` is a true-false statement then `by_cases hP : P,`
turns your goal into two goals, one with hypothesis `hP : P`
and the other with hypothesis `hP : ¬ P`.

-/

-- Throughout this sheet, `P`, `Q` and `R` will denote propositions.

variables (P Q R : Prop)

example : ¬ P → (P → false) :=
begin
  sorry,
end

example : ¬ true → false :=
begin
  sorry
end

example : false → ¬ true :=
begin
  sorry
end

example : ¬ false → true :=
begin
  sorry
end

example : true → ¬ false :=
begin
  sorry
end

example : false → ¬ P :=
begin
  sorry
end

example : P → ¬ P → false :=
begin
  sorry
end

example : P → ¬ (¬ P) :=
begin
  sorry
end

example : (P → Q) → (¬ Q → ¬ P) :=
begin
  sorry
end

example : ¬ ¬ false → false :=
begin
  sorry
end

example : ¬ ¬ P → P :=
begin
  sorry
end

example : (¬ Q → ¬ P) → (P → Q) :=
begin
  sorry,
end


import data.real.basic

open_locale classical

/-
Theoretical negations.

This file is for people interested in logic who want to fully understand
negations.

Here we don't use `contrapose` or `push_neg`. The goal is to prove lemmas
that are used by those tactics. Of course we can use
`exfalso`, `by_contradiction` and `by_cases`.

If this doesn't sound like fun then skip ahead to the next file.
-/

section negation_prop

variables P Q : Prop

-- 0055
example : (P → Q) ↔ (¬ Q → ¬ P) :=
begin
  split,
  {
    intros h1 hnQ hP,
    exact hnQ (h1 hP),
  },
  {
    intros h1 hP,
    by_contradiction hnQ,
    exact h1 hnQ hP,
  }
end

-- 0056
lemma non_imp (P Q : Prop) : ¬ (P → Q) ↔ P ∧ ¬ Q :=
begin
  sorry
end

-- In the next one, let's use the axiom
-- propext {P Q : Prop} : (P ↔ Q) → P = Q

-- 0057
example (P : Prop) : ¬ P ↔ P = false :=
begin
  sorry
end

end negation_prop

section negation_quantifiers
variables (X : Type) (P : X → Prop)

-- 0058
example : ¬ (∀ x, P x) ↔ ∃ x, ¬ P x :=
begin
  sorry
end

-- 0059
example : ¬ (∃ x, P x) ↔ ∀ x, ¬ P x :=
begin
  sorry
end

-- 0060
example (P : ℝ → Prop) : ¬ (∃ ε > 0, P ε) ↔ ∀ ε > 0, ¬ P ε :=
begin
  sorry
end

-- 0061
example (P : ℝ → Prop) : ¬ (∀ x > 0, P x) ↔ ∃ x > 0, ¬ P x :=
begin
  sorry
end

end negation_quantifiers