/-
Copyright (c) 2018 Mario Carneiro, Kevin Buzzard. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro, Kevin Buzzard

! This file was ported from Lean 3 source module ring_theory.noetherian
! leanprover-community/mathlib commit da420a8c6dd5bdfb85c4ced85c34388f633bc6ff
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Algebra.Algebra.Subalgebra.Basic
import Mathlib.Algebra.Algebra.Tower
import Mathlib.Algebra.Ring.Idempotents
import Mathlib.GroupTheory.Finiteness
import Mathlib.LinearAlgebra.LinearIndependent
import Mathlib.Order.CompactlyGenerated
import Mathlib.Order.OrderIsoNat
import Mathlib.RingTheory.Finiteness
import Mathlib.RingTheory.Nilpotent

/-!
# Noetherian rings and modules

The following are equivalent for a module M over a ring R:
1. Every increasing chain of submodules M₁ ⊆ M₂ ⊆ M₃ ⊆ ⋯ eventually stabilises.
2. Every submodule is finitely generated.

A module satisfying these equivalent conditions is said to be a *Noetherian* R-module.
A ring is a *Noetherian ring* if it is Noetherian as a module over itself.

(Note that we do not assume yet that our rings are commutative,
so perhaps this should be called "left Noetherian".
To avoid cumbersome names once we specialize to the commutative case,
we don't make this explicit in the declaration names.)

## Main definitions

Let `R` be a ring and let `M` and `P` be `R`-modules. Let `N` be an `R`-submodule of `M`.

* `IsNoetherian R M` is the proposition that `M` is a Noetherian `R`-module. It is a class,
  implemented as the predicate that all `R`-submodules of `M` are finitely generated.

## Main statements

* `isNoetherian_iff_wellFounded` is the theorem that an R-module M is Noetherian iff
  `>` is well-founded on `Submodule R M`.

Note that the Hilbert basis theorem, that if a commutative ring R is Noetherian then so is R[X],
is proved in `RingTheory.Polynomial`.

## References

* [M. F. Atiyah and I. G. Macdonald, *Introduction to commutative algebra*][atiyah-macdonald]
* [samuel1967]

## Tags

Noetherian, noetherian, Noetherian ring, Noetherian module, noetherian ring, noetherian module

-/

open Set

open BigOperators Pointwise

/-- `IsNoetherian R M` is the proposition that `M` is a Noetherian `R`-module,
implemented as the predicate that all `R`-submodules of `M` are finitely generated.
-/
-- Porting note: should this be renamed to `Noetherian`?
class IsNoetherian (R M) [Semiring R] [AddCommMonoid M] [Module R M] : Prop where
  noetherian : ∀ s : Submodule R M, s.Fg
#align is_noetherian IsNoetherian

attribute [inherit_doc IsNoetherian] IsNoetherian.noetherian

section

variable {R : Type _} {M : Type _} {P : Type _}

variable [Semiring R] [AddCommMonoid M] [AddCommMonoid P]

variable [Module R M] [Module R P]

open IsNoetherian

/-- An R-module is Noetherian iff all its submodules are finitely-generated. -/
theorem isNoetherian_def : IsNoetherian R M ↔ ∀ s : Submodule R M, s.Fg :=
  ⟨fun h => h.noetherian, IsNoetherian.mk⟩
#align is_noetherian_def isNoetherian_def

theorem isNoetherian_submodule {N : Submodule R M} :
    IsNoetherian R N ↔ ∀ s : Submodule R M, s ≤ N → s.Fg := by
  refine ⟨fun ⟨hn⟩ => fun s hs =>
    have : s ≤ LinearMap.range N.subtype := N.range_subtype.symm ▸ hs
    Submodule.map_comap_eq_self this ▸ (hn _).map _,
    fun h => ⟨fun s => ?_⟩⟩
  have f := (Submodule.equivMapOfInjective N.subtype Subtype.val_injective s).symm
  have h₁ := h (s.map N.subtype) (Submodule.map_subtype_le N s)
  have h₂ : (⊤ : Submodule R (s.map N.subtype)).map f = ⊤ := by simp
  have h₃ := ((Submodule.fg_top _).2 h₁).map (↑f : _ →ₗ[R] s)
  exact (Submodule.fg_top _).1 (h₂ ▸ h₃)
#align is_noetherian_submodule isNoetherian_submodule

theorem isNoetherian_submodule_left {N : Submodule R M} :
    IsNoetherian R N ↔ ∀ s : Submodule R M, (N ⊓ s).Fg :=
  isNoetherian_submodule.trans ⟨fun H _ => H _ inf_le_left, fun H _ hs => inf_of_le_right hs ▸ H _⟩
#align is_noetherian_submodule_left isNoetherian_submodule_left

theorem isNoetherian_submodule_right {N : Submodule R M} :
    IsNoetherian R N ↔ ∀ s : Submodule R M, (s ⊓ N).Fg :=
  isNoetherian_submodule.trans ⟨fun H _ => H _ inf_le_right, fun H _ hs => inf_of_le_left hs ▸ H _⟩
#align is_noetherian_submodule_right isNoetherian_submodule_right

instance isNoetherian_submodule' [IsNoetherian R M] (N : Submodule R M) : IsNoetherian R N :=
  isNoetherian_submodule.2 fun _ _ => IsNoetherian.noetherian _
#align is_noetherian_submodule' isNoetherian_submodule'

theorem isNoetherian_of_le {s t : Submodule R M} [ht : IsNoetherian R t] (h : s ≤ t) :
    IsNoetherian R s :=
  isNoetherian_submodule.mpr fun _ hs' => isNoetherian_submodule.mp ht _ (le_trans hs' h)
#align is_noetherian_of_le isNoetherian_of_le

variable (M)

theorem isNoetherian_of_surjective (f : M →ₗ[R] P) (hf : LinearMap.range f = ⊤) [IsNoetherian R M] :
    IsNoetherian R P :=
  ⟨fun s =>
    have : (s.comap f).map f = s := Submodule.map_comap_eq_self <| hf.symm ▸ le_top
    this ▸ (noetherian _).map _⟩
#align is_noetherian_of_surjective isNoetherian_of_surjective

variable {M}

theorem isNoetherian_of_linearEquiv (f : M ≃ₗ[R] P) [IsNoetherian R M] : IsNoetherian R P :=
  isNoetherian_of_surjective _ f.toLinearMap f.range
#align is_noetherian_of_linear_equiv isNoetherian_of_linearEquiv

theorem isNoetherian_top_iff : IsNoetherian R (⊤ : Submodule R M) ↔ IsNoetherian R M := by
  constructor <;> intro h
  · exact isNoetherian_of_linearEquiv (LinearEquiv.ofTop (⊤ : Submodule R M) rfl)
  · exact isNoetherian_of_linearEquiv (LinearEquiv.ofTop (⊤ : Submodule R M) rfl).symm
#align is_noetherian_top_iff isNoetherian_top_iff

theorem isNoetherian_of_injective [IsNoetherian R P] (f : M →ₗ[R] P) (hf : Function.Injective f) :
    IsNoetherian R M :=
  isNoetherian_of_linearEquiv (LinearEquiv.ofInjective f hf).symm
#align is_noetherian_of_injective isNoetherian_of_injective

theorem fg_of_injective [IsNoetherian R P] {N : Submodule R M} (f : M →ₗ[R] P)
    (hf : Function.Injective f) : N.Fg :=
  haveI := isNoetherian_of_injective f hf
  IsNoetherian.noetherian N
#align fg_of_injective fg_of_injective

end

namespace Module

variable {R M N : Type _}

variable [Semiring R] [AddCommMonoid M] [AddCommMonoid N] [Module R M] [Module R N]

variable (R M)

-- see Note [lower instance priority]
instance (priority := 100) IsNoetherian.finite [IsNoetherian R M] : Finite R M :=
  ⟨IsNoetherian.noetherian ⊤⟩
#align module.is_noetherian.finite Module.IsNoetherian.finite

variable {R M}

theorem Finite.of_injective [IsNoetherian R N] (f : M →ₗ[R] N) (hf : Function.Injective f) :
    Finite R M :=
  ⟨fg_of_injective f hf⟩
#align module.finite.of_injective Module.Finite.of_injective

end Module

section

variable {R : Type _} {M : Type _} {P : Type _}

variable [Ring R] [AddCommGroup M] [AddCommGroup P]

variable [Module R M] [Module R P]

open IsNoetherian

set_option synthInstance.etaExperiment true in
theorem isNoetherian_of_ker_bot [IsNoetherian R P] (f : M →ₗ[R] P) (hf : LinearMap.ker f = ⊥) :
    IsNoetherian R M :=
  isNoetherian_of_linearEquiv (LinearEquiv.ofInjective f <| LinearMap.ker_eq_bot.mp hf).symm
#align is_noetherian_of_ker_bot isNoetherian_of_ker_bot

set_option synthInstance.etaExperiment true in
theorem fg_of_ker_bot [IsNoetherian R P] {N : Submodule R M} (f : M →ₗ[R] P)
    (hf : LinearMap.ker f = ⊥) : N.Fg :=
  haveI := isNoetherian_of_ker_bot f hf
  IsNoetherian.noetherian N
#align fg_of_ker_bot fg_of_ker_bot

instance isNoetherian_prod [IsNoetherian R M] [IsNoetherian R P] : IsNoetherian R (M × P) :=
  ⟨fun s =>
    Submodule.fg_of_fg_map_of_fg_inf_ker (LinearMap.snd R M P) (noetherian _) <|
      have : s ⊓ LinearMap.ker (LinearMap.snd R M P) ≤ LinearMap.range (LinearMap.inl R M P) :=
        fun x ⟨_, hx2⟩ => ⟨x.1, Prod.ext rfl <| Eq.symm <| LinearMap.mem_ker.1 hx2⟩
      Submodule.map_comap_eq_self this ▸ (noetherian _).map _⟩
#align is_noetherian_prod isNoetherian_prod

instance isNoetherian_pi {R ι : Type _} {M : ι → Type _}
    [Ring R] [∀ i, AddCommGroup (M i)] [∀ i, Module R (M i)] [Finite ι]
    [∀ i, IsNoetherian R (M i)] : IsNoetherian R (∀ i, M i) := by
  cases nonempty_fintype ι
  haveI := Classical.decEq ι
  suffices on_finset : ∀ s : Finset ι, IsNoetherian R (∀ i : s, M i)
  · let coe_e := Equiv.subtypeUnivEquiv <| @Finset.mem_univ ι _
    letI : IsNoetherian R (∀ i : Finset.univ, M (coe_e i)) := on_finset Finset.univ
    exact isNoetherian_of_linearEquiv (LinearEquiv.piCongrLeft R M coe_e)
  intro s
  induction' s using Finset.induction with a s has ih
  · exact ⟨fun s => by
      have : s = ⊥ := by simp only [eq_iff_true_of_subsingleton]
      rw [this]
      apply Submodule.fg_bot⟩
  refine
    @isNoetherian_of_linearEquiv R (M a × ((i : s) → M i)) _ _ _ _ _ _ ?_ <|
      @isNoetherian_prod R (M a) _ _ _ _ _ _ _ ih
  refine
  { toFun := fun f i =>
      (Finset.mem_insert.1 i.2).by_cases
        (fun h : i.1 = a => show M i.1 from Eq.recOn h.symm f.1)
        (fun h : i.1 ∈ s => show M i.1 from f.2 ⟨i.1, h⟩),
    invFun := fun f =>
      (f ⟨a, Finset.mem_insert_self _ _⟩, fun i => f ⟨i.1, Finset.mem_insert_of_mem i.2⟩),
    map_add' := ?_,
    map_smul' := ?_
    left_inv := ?_,
    right_inv := ?_ }
  · intro f g
    ext i
    unfold Or.by_cases
    cases' i with i hi
    rcases Finset.mem_insert.1 hi with (rfl | h)
    · change _ = _ + _
      simp only [dif_pos]
      rfl
    · change _ = _ + _
      have : ¬i = a := by
        rintro rfl
        exact has h
      simp only [dif_neg this, dif_pos h]
      rfl
  · intro c f
    ext i
    unfold Or.by_cases
    cases' i with i hi
    rcases Finset.mem_insert.1 hi with (rfl | h)
    · dsimp
      simp only [dif_pos]
    · dsimp
      have : ¬i = a := by
        rintro rfl
        exact has h
      simp only [dif_neg this, dif_pos h]
  · intro f
    apply Prod.ext
    · simp only [Or.by_cases, dif_pos]
    · ext ⟨i, his⟩
      have : ¬i = a := by
        rintro rfl
        exact has his
      simp only [Or.by_cases, this, not_false_iff, dif_neg]
  · intro f
    ext ⟨i, hi⟩
    rcases Finset.mem_insert.1 hi with (rfl | h)
    · simp only [Or.by_cases, dif_pos]
    · have : ¬i = a := by
        rintro rfl
        exact has h
      simp only [Or.by_cases, dif_neg this, dif_pos h]
#align is_noetherian_pi isNoetherian_pi

/-- A version of `isNoetherian_pi` for non-dependent functions. We need this instance because
sometimes Lean fails to apply the dependent version in non-dependent settings (e.g., it fails to
prove that `ι → ℝ` is finite dimensional over `ℝ`). -/
instance isNoetherian_pi' {R ι M : Type _} [Ring R] [AddCommGroup M] [Module R M] [Finite ι]
    [IsNoetherian R M] : IsNoetherian R (ι → M) :=
  isNoetherian_pi
#align is_noetherian_pi' isNoetherian_pi'

end

open IsNoetherian Submodule Function

section

universe w

variable {R M P : Type _} {N : Type w} [Semiring R] [AddCommMonoid M] [Module R M] [AddCommMonoid N]
  [Module R N] [AddCommMonoid P] [Module R P]

theorem isNoetherian_iff_wellFounded :
    IsNoetherian R M ↔ WellFounded ((· > ·) : Submodule R M → Submodule R M → Prop) := by
  have := (CompleteLattice.wellFounded_characterisations <| Submodule R M).out 0 3
  -- Porting note: inlining this makes rw complain about it being a metavariable
  rw [this]
  exact
    ⟨fun ⟨h⟩ => fun k => (fg_iff_compact k).mp (h k), fun h =>
      ⟨fun k => (fg_iff_compact k).mpr (h k)⟩⟩
#align is_noetherian_iff_well_founded isNoetherian_iff_wellFounded

theorem isNoetherian_iff_fg_wellFounded :
    IsNoetherian R M ↔
      WellFounded
        ((· > ·) : { N : Submodule R M // N.Fg } → { N : Submodule R M // N.Fg } → Prop) := by
  let α := { N : Submodule R M // N.Fg }
  constructor
  · intro H
    let f : α ↪o Submodule R M := OrderEmbedding.subtype _
    exact OrderEmbedding.wellFounded f.dual (isNoetherian_iff_wellFounded.mp H)
  · intro H
    constructor
    intro N
    obtain ⟨⟨N₀, h₁⟩, e : N₀ ≤ N, h₂⟩ :=
      WellFounded.wellFounded_iff_has_max'.mp H { N' : α | N'.1 ≤ N }
        ⟨⟨⊥, Submodule.fg_bot⟩, @bot_le _ _ _ N⟩
    convert h₁
    refine' (e.antisymm _).symm
    by_contra h₃
    obtain ⟨x, hx₁ : x ∈ N, hx₂ : x ∉ N₀⟩ := Set.not_subset.mp h₃
    apply hx₂
    have := h₂ ⟨(R ∙ x) ⊔ N₀, ?_⟩ ?_ ?_
    · injection this with eq
      rw [← eq]
      exact (le_sup_left : (R ∙ x) ≤ (R ∙ x) ⊔ N₀) (Submodule.mem_span_singleton_self _)
    · exact Submodule.Fg.sup ⟨{x}, by rw [Finset.coe_singleton]⟩ h₁
    · exact sup_le ((Submodule.span_singleton_le_iff_mem _ _).mpr hx₁) e
    · show N₀ ≤ (R ∙ x) ⊔ N₀
      exact le_sup_right
#align is_noetherian_iff_fg_well_founded isNoetherian_iff_fg_wellFounded

variable (R M)

theorem wellFounded_submodule_gt (R M) [Semiring R] [AddCommMonoid M] [Module R M] :
    ∀ [IsNoetherian R M], WellFounded ((· > ·) : Submodule R M → Submodule R M → Prop) :=
  isNoetherian_iff_wellFounded.mp ‹_›
#align well_founded_submodule_gt wellFounded_submodule_gt

variable {R M}

/-- A module is Noetherian iff every nonempty set of submodules has a maximal submodule among them.
-/
theorem set_has_maximal_iff_noetherian :
    (∀ a : Set <| Submodule R M, a.Nonempty → ∃ M' ∈ a, ∀ I ∈ a, M' ≤ I → I = M') ↔
      IsNoetherian R M :=
  by rw [isNoetherian_iff_wellFounded, WellFounded.wellFounded_iff_has_max']
#align set_has_maximal_iff_noetherian set_has_maximal_iff_noetherian

/-- A module is Noetherian iff every increasing chain of submodules stabilizes. -/
theorem monotone_stabilizes_iff_noetherian :
    (∀ f : ℕ →o Submodule R M, ∃ n, ∀ m, n ≤ m → f n = f m) ↔ IsNoetherian R M := by
  rw [isNoetherian_iff_wellFounded, WellFounded.monotone_chain_condition]
#align monotone_stabilizes_iff_noetherian monotone_stabilizes_iff_noetherian

/-- If `∀ I > J, P I` implies `P J`, then `P` holds for all submodules. -/
theorem IsNoetherian.induction [IsNoetherian R M] {P : Submodule R M → Prop}
    (hgt : ∀ I, (∀ J > I, P J) → P I) (I : Submodule R M) : P I :=
  WellFounded.recursion (wellFounded_submodule_gt R M) I hgt
#align is_noetherian.induction IsNoetherian.induction

end

section

universe w

variable {R M P : Type _} {N : Type w} [Ring R] [AddCommGroup M] [Module R M] [AddCommGroup N]
  [Module R N] [AddCommGroup P] [Module R P]

theorem finite_of_linearIndependent [Nontrivial R] [IsNoetherian R M] {s : Set M}
    (hs : LinearIndependent R ((↑) : s → M)) : s.Finite := by
  refine'
    by_contradiction fun hf =>
      (RelEmbedding.wellFounded_iff_no_descending_seq.1 (wellFounded_submodule_gt R M)).elim' _
  have f : ℕ ↪ s := Set.Infinite.natEmbedding s hf
  have : ∀ n, (↑) ∘ f '' { m | m ≤ n } ⊆ s := by
    rintro n x ⟨y, _, rfl⟩
    exact (f y).2
  let coe' : s → M := (↑)
  have : ∀ a b : ℕ, a ≤ b ↔
    span R (coe' ∘ f '' { m | m ≤ a }) ≤ span R ((↑) ∘ f '' { m | m ≤ b }) := by
    intro a b
    rw [span_le_span_iff hs (this a) (this b),
      Set.image_subset_image_iff (Subtype.coe_injective.comp f.injective), Set.subset_def]
    exact ⟨fun hab x (hxa : x ≤ a) => le_trans hxa hab, fun hx => hx a (le_refl a)⟩
  exact
    ⟨⟨fun n => span R (coe' ∘ f '' { m | m ≤ n }), fun x y => by
        rw [le_antisymm_iff, (this x y).symm, (this y x).symm, ←le_antisymm_iff, imp_self]
        trivial⟩,
      by dsimp [GT.gt]; simp only [lt_iff_le_not_le, (this _ _).symm]; tauto⟩
#align finite_of_linear_independent finite_of_linearIndependent

set_option synthInstance.etaExperiment true in
/-- If the first and final modules in a short exact sequence are Noetherian,
  then the middle module is also Noetherian. -/
theorem isNoetherian_of_range_eq_ker [IsNoetherian R M] [IsNoetherian R P] (f : M →ₗ[R] N)
    (g : N →ₗ[R] P) (hf : Function.Injective f) (hg : Function.Surjective g)
    (h : LinearMap.range f = LinearMap.ker g) :
    IsNoetherian R N :=
  isNoetherian_iff_wellFounded.2 <|
    wellFounded_gt_exact_sequence (wellFounded_submodule_gt R M) (wellFounded_submodule_gt R P)
      (LinearMap.range f) (Submodule.map f) (Submodule.comap f) (Submodule.comap g)
      (Submodule.map g) (Submodule.gciMapComap hf) (Submodule.giMapComap hg)
      (by simp [Submodule.map_comap_eq, inf_comm]) (by simp [Submodule.comap_map_eq, h])
#align is_noetherian_of_range_eq_ker isNoetherian_of_range_eq_ker

/- Porting note (lean4#2074): this seems to cause a diamond with Ring.toSemiring when going to
NonAssocSemiring -/
attribute [-instance] Ring.toNonAssocRing

/-- For any endomorphism of a Noetherian module, there is some nontrivial iterate
with disjoint kernel and range.
-/
theorem IsNoetherian.exists_endomorphism_iterate_ker_inf_range_eq_bot [I : IsNoetherian R M]
    (f : M →ₗ[R] M) :
    ∃ n : ℕ, n ≠ 0 ∧ LinearMap.ker (f ^ n) ⊓ LinearMap.range (f ^ n) = ⊥ := by
  obtain ⟨n, w⟩ :=
    monotone_stabilizes_iff_noetherian.mpr I
      (f.iterateKer.comp ⟨fun n => n + 1, fun n m w => by linarith⟩)
  specialize w (2 * n + 1) (by linarith only)
  dsimp at w
  refine' ⟨n + 1, Nat.succ_ne_zero _, _⟩
  rw [eq_bot_iff]
  rintro - ⟨h, ⟨y, rfl⟩⟩
  rw [mem_bot, ← LinearMap.mem_ker, w]
  erw [LinearMap.mem_ker] at h⊢
  change (f ^ (n + 1) * f ^ (n + 1)) y = 0 at h
  rw [← pow_add] at h
  convert h using 3
  ring
#align is_noetherian.exists_endomorphism_iterate_ker_inf_range_eq_bot IsNoetherian.exists_endomorphism_iterate_ker_inf_range_eq_bot

/-- Any surjective endomorphism of a Noetherian module is injective. -/
theorem IsNoetherian.injective_of_surjective_endomorphism [IsNoetherian R M] (f : M →ₗ[R] M)
    (s : Surjective f) : Injective f := by
  obtain ⟨n, ne, w⟩ := IsNoetherian.exists_endomorphism_iterate_ker_inf_range_eq_bot f
  rw [LinearMap.range_eq_top.mpr (LinearMap.iterate_surjective s n), inf_top_eq,
    LinearMap.ker_eq_bot] at w
  exact LinearMap.injective_of_iterate_injective ne w
#align is_noetherian.injective_of_surjective_endomorphism IsNoetherian.injective_of_surjective_endomorphism

/-- Any surjective endomorphism of a Noetherian module is bijective. -/
theorem IsNoetherian.bijective_of_surjective_endomorphism [IsNoetherian R M] (f : M →ₗ[R] M)
    (s : Surjective f) : Bijective f :=
  ⟨IsNoetherian.injective_of_surjective_endomorphism f s, s⟩
#align is_noetherian.bijective_of_surjective_endomorphism IsNoetherian.bijective_of_surjective_endomorphism

/-- A sequence `f` of submodules of a noetherian module,
with `f (n+1)` disjoint from the supremum of `f 0`, ..., `f n`,
is eventually zero.
-/
theorem IsNoetherian.disjoint_partialSups_eventually_bot [I : IsNoetherian R M]
    (f : ℕ → Submodule R M) (h : ∀ n, Disjoint (partialSups f n) (f (n + 1))) :
    ∃ n : ℕ, ∀ m, n ≤ m → f m = ⊥ := by
  -- A little off-by-one cleanup first:
  suffices t : ∃ n : ℕ, ∀ m, n ≤ m → f (m + 1) = ⊥
  · obtain ⟨n, w⟩ := t
    use n + 1
    rintro (_ | m) p
    · cases p
    · apply w
      exact Nat.succ_le_succ_iff.mp p
  obtain ⟨n, w⟩ := monotone_stabilizes_iff_noetherian.mpr I (partialSups f)
  exact
    ⟨n, fun m p =>
      (h m).eq_bot_of_ge <| sup_eq_left.1 <| (w (m + 1) <| le_add_right p).symm.trans <| w m p⟩
#align is_noetherian.disjoint_partial_sups_eventually_bot IsNoetherian.disjoint_partialSups_eventually_bot

/-- If `M ⊕ N` embeds into `M`, for `M` noetherian over `R`, then `N` is trivial.
-/
noncomputable def IsNoetherian.equivPunitOfProdInjective [IsNoetherian R M] (f : M × N →ₗ[R] M)
    (i : Injective f) : N ≃ₗ[R] PUnit.{w + 1} := by
  apply Nonempty.some
  obtain ⟨n, w⟩ :=
    IsNoetherian.disjoint_partialSups_eventually_bot (f.tailing i) (f.tailings_disjoint_tailing i)
  specialize w n (le_refl n)
  apply Nonempty.intro
  -- Porting note: refine' makes this line time out at elaborator
  refine (LinearMap.tailingLinearEquiv f i n).symm ≪≫ₗ ?_
  rw [w]
  exact Submodule.botEquivPUnit
#align is_noetherian.equiv_punit_of_prod_injective IsNoetherian.equivPunitOfProdInjective

end

/-- A (semi)ring is Noetherian if it is Noetherian as a module over itself,
i.e. all its ideals are finitely generated.
-/
@[reducible]
def IsNoetherianRing (R) [Semiring R] :=
  IsNoetherian R R
#align is_noetherian_ring IsNoetherianRing

theorem isNoetherianRing_iff {R} [Semiring R] : IsNoetherianRing R ↔ IsNoetherian R R :=
  Iff.rfl
#align is_noetherian_ring_iff isNoetherianRing_iff

/-- A ring is Noetherian if and only if all its ideals are finitely-generated. -/
theorem isNoetherianRing_iff_ideal_fg (R : Type _) [Semiring R] :
    IsNoetherianRing R ↔ ∀ I : Ideal R, I.Fg :=
  isNoetherianRing_iff.trans isNoetherian_def
#align is_noetherian_ring_iff_ideal_fg isNoetherianRing_iff_ideal_fg

-- see Note [lower instance priority]
instance (priority := 80) isNoetherian_of_finite (R M) [Finite M] [Semiring R] [AddCommMonoid M]
    [Module R M] : IsNoetherian R M :=
  ⟨fun s => ⟨(s : Set M).toFinite.toFinset, by rw [Set.Finite.coe_toFinset, Submodule.span_eq]⟩⟩
#align is_noetherian_of_finite isNoetherian_of_finite

-- see Note [lower instance priority]
/-- Modules over the trivial ring are Noetherian. -/
instance (priority := 100) isNoetherian_of_subsingleton (R M) [Subsingleton R] [Semiring R]
    [AddCommMonoid M] [Module R M] : IsNoetherian R M :=
  haveI := Module.subsingleton R M
  isNoetherian_of_finite R M
#align is_noetherian_of_subsingleton isNoetherian_of_subsingleton

theorem isNoetherian_of_submodule_of_noetherian (R M) [Semiring R] [AddCommMonoid M] [Module R M]
    (N : Submodule R M) (h : IsNoetherian R M) : IsNoetherian R N := by
  rw [isNoetherian_iff_wellFounded] at h⊢
  exact OrderEmbedding.wellFounded (Submodule.MapSubtype.orderEmbedding N).dual h
#align is_noetherian_of_submodule_of_noetherian isNoetherian_of_submodule_of_noetherian

instance Submodule.Quotient.isNoetherian {R} [Ring R] {M} [AddCommGroup M] [Module R M]
    (N : Submodule R M) [h : IsNoetherian R M] : IsNoetherian R (M ⧸ N) := by
  rw [isNoetherian_iff_wellFounded] at h⊢
  exact OrderEmbedding.wellFounded (Submodule.comapMkQOrderEmbedding N).dual h
#align submodule.quotient.is_noetherian Submodule.Quotient.isNoetherian

/-- If `M / S / R` is a scalar tower, and `M / R` is Noetherian, then `M / S` is
also noetherian. -/
theorem isNoetherian_of_tower (R) {S M} [Semiring R] [Semiring S] [AddCommMonoid M] [SMul R S]
    [Module S M] [Module R M] [IsScalarTower R S M] (h : IsNoetherian R M) : IsNoetherian S M := by
  rw [isNoetherian_iff_wellFounded] at h⊢
  refine' (Submodule.restrictScalarsEmbedding R S M).dual.wellFounded h
#align is_noetherian_of_tower isNoetherian_of_tower

theorem isNoetherian_of_fg_of_noetherian {R M} [Ring R] [AddCommGroup M] [Module R M]
    (N : Submodule R M) [I : IsNoetherianRing R] (hN : N.Fg) : IsNoetherian R N := by
  let ⟨s, hs⟩ := hN
  haveI := Classical.decEq M
  haveI := Classical.decEq R
  -- Porting note: etaExperiment fixes inferInstance proof
  letI : IsNoetherian R R := I
  have : ∀ x ∈ s, x ∈ N := fun x hx => hs ▸ Submodule.subset_span hx
  refine
    @isNoetherian_of_surjective
      R ((↑s : Set M) → R) N _ _ _ (Pi.module _ _ _) _ ?_ ?_ isNoetherian_pi
  · fapply LinearMap.mk
    · fapply AddHom.mk
      · exact fun f => ⟨∑ i in s.attach, f i • i.1, N.sum_mem fun c _ => N.smul_mem _ <| this _ c.2⟩
      · intro f g
        apply Subtype.eq
        change (∑ i in s.attach, (f i + g i) • _) = _
        simp only [add_smul, Finset.sum_add_distrib]
        rfl
    · intro c f
      apply Subtype.eq
      change (∑ i in s.attach, (c • f i) • _) = _
      simp only [smul_eq_mul, mul_smul]
      exact Finset.smul_sum.symm
  · rw [LinearMap.range_eq_top]
    rintro ⟨n, hn⟩
    change n ∈ N at hn
    rw [← hs, ← Set.image_id (s : Set M), Finsupp.mem_span_image_iff_total] at hn
    rcases hn with ⟨l, hl1, hl2⟩
    refine' ⟨fun x => l x, Subtype.ext _⟩
    change (∑ i in s.attach, l i • (i : M)) = n
    rw [@Finset.sum_attach M M s _ fun i => l i • i, ← hl2,
      Finsupp.total_apply, Finsupp.sum, eq_comm]
    refine' Finset.sum_subset hl1 fun x _ hx => _
    rw [Finsupp.not_mem_support_iff.1 hx, zero_smul]
#align is_noetherian_of_fg_of_noetherian isNoetherian_of_fg_of_noetherian

theorem isNoetherian_of_fg_of_noetherian' {R M} [Ring R] [AddCommGroup M] [Module R M]
    [IsNoetherianRing R] (h : (⊤ : Submodule R M).Fg) : IsNoetherian R M :=
  have : IsNoetherian R (⊤ : Submodule R M) := isNoetherian_of_fg_of_noetherian _ h
  isNoetherian_of_linearEquiv (LinearEquiv.ofTop (⊤ : Submodule R M) rfl)
#align is_noetherian_of_fg_of_noetherian' isNoetherian_of_fg_of_noetherian'

/-- In a module over a Noetherian ring, the submodule generated by finitely many vectors is
Noetherian. -/
theorem isNoetherian_span_of_finite (R) {M} [Ring R] [AddCommGroup M] [Module R M]
    [IsNoetherianRing R] {A : Set M} (hA : A.Finite) : IsNoetherian R (Submodule.span R A) :=
  isNoetherian_of_fg_of_noetherian _ (Submodule.fg_def.mpr ⟨A, hA, rfl⟩)
#align is_noetherian_span_of_finite isNoetherian_span_of_finite

set_option synthInstance.etaExperiment true in
theorem isNoetherianRing_of_surjective (R) [Ring R] (S) [Ring S] (f : R →+* S)
    (hf : Function.Surjective f) [H : IsNoetherianRing R] : IsNoetherianRing S := by
  rw [isNoetherianRing_iff, isNoetherian_iff_wellFounded] at H⊢
  exact OrderEmbedding.wellFounded (Ideal.orderEmbeddingOfSurjective f hf).dual H
#align is_noetherian_ring_of_surjective isNoetherianRing_of_surjective

instance isNoetherianRing_range {R} [Ring R] {S} [Ring S] (f : R →+* S) [IsNoetherianRing R] :
    IsNoetherianRing f.range :=
  isNoetherianRing_of_surjective R f.range f.rangeRestrict f.rangeRestrict_surjective
#align is_noetherian_ring_range isNoetherianRing_range

theorem isNoetherianRing_of_ringEquiv (R) [Ring R] {S} [Ring S] (f : R ≃+* S) [IsNoetherianRing R] :
    IsNoetherianRing S :=
  isNoetherianRing_of_surjective R S f.toRingHom f.toEquiv.surjective
#align is_noetherian_ring_of_ring_equiv isNoetherianRing_of_ringEquiv

theorem IsNoetherianRing.isNilpotent_nilradical (R : Type _) [CommRing R] [IsNoetherianRing R] :
    IsNilpotent (nilradical R) := by
  obtain ⟨n, hn⟩ := Ideal.exists_radical_pow_le_of_fg (⊥ : Ideal R) (IsNoetherian.noetherian _)
  exact ⟨n, eq_bot_iff.mpr hn⟩
#align is_noetherian_ring.is_nilpotent_nilradical IsNoetherianRing.isNilpotent_nilradical


-- Ejercicios de lógica proposicional
-- ==================================

import tactic
variables (p q r s : Prop)

-- § Implicaciones
-- ================

-- ----------------------------------------------------
-- Ejercicio 1. Demostrar
--      p ⟶ q, p ⊢ q
-- ----------------------------------------------------

-- 1ª demostración
example
  (Hpq : p → q)
  (Hp  : p)
  : q :=
Hpq Hp

-- 2ª demostración
example
  (Hpq : p → q)
  (Hp  : p)
  : q :=
by tauto

-- ----------------------------------------------------
-- Ejercicio 2. Demostrar
--    p → q, q → r, p ⊢ r
-- ----------------------------------------------------

-- 1ª demostracióm
example
  (Hpq : p → q)
  (Hqr : q → r)
  (Hp : p)
  : r :=
begin
  apply Hqr,
  apply Hpq,
  exact Hp,
end

-- 2ª demostracióm
example
  (Hpq : p → q)
  (Hqr : q → r)
  (Hp : p)
  : r :=
begin
  apply Hqr,
  exact Hpq Hp,
end

-- 3ª demostracióm
example
  (Hpq : p → q)
  (Hqr : q → r)
  (Hp : p)
  : r :=
begin
  exact Hqr (Hpq Hp),
end

-- 3ª demostracióm
example
  (Hpq : p → q)
  (Hqr : q → r)
  (Hp : p)
  : r :=
Hqr (Hpq Hp)

-- 4ª demostracióm
example
  (Hpq : p → q)
  (Hqr : q → r)
  (Hp : p)
  : r :=
by tauto

-- 5ª demostracióm
example
  (Hpq : p → q)
  (Hqr : q → r)
  (Hp : p)
  : r :=
have Hq : q,
  from Hpq Hp,
show r,
  from Hqr Hq

-- ----------------------------------------------------
-- Ejercicio 3. Demostrar
--    p → (q → r), p → q, p ⊢ r
-- ----------------------------------------------------

-- 1ª demostración
example
  (Hpqr : p → (q → r))
  (Hpq  : p → q)
  (Hp   : p)
  : r :=
begin
  have Hqr : q → r, from Hpqr Hp,
  apply Hqr,
  apply Hpq,
  exact Hp,
end

-- 2ª demostración
example
  (Hpqr : p → (q → r))
  (Hpq  : p → q)
  (Hp   : p)
  : r :=
begin
  have Hqr : q → r, from Hpqr Hp,
  apply Hqr,
  exact Hpq Hp,
end

-- 3ª demostración
example
  (Hpqr : p → (q → r))
  (Hpq  : p → q)
  (Hp   : p)
  : r :=
begin
  have Hqr : q → r, from Hpqr Hp,
  exact Hqr (Hpq Hp),
end

-- 4ª demostración
example
  (Hpqr : p → (q → r))
  (Hpq  : p → q)
  (Hp   : p)
  : r :=
(Hpqr Hp) (Hpq Hp)

-- 5ª demostración
example
  (Hpqr : p → (q → r))
  (Hpq  : p → q)
  (Hp   : p)
  : r :=
-- by hint
by finish

-- ----------------------------------------------------
-- Ejercicio 4. Demostrar
--    p → q, q → r ⊢ p → r
-- ----------------------------------------------------

-- 1ª demostración
example
  (Hpq : p → q)
  (Hqr : q → r)
  : p → r :=
begin
  intro Hp,
  apply Hqr,
  apply Hpq,
  exact Hp,
end

-- 2ª demostración
example
  (Hpq : p → q)
  (Hqr : q → r)
  : p → r :=
begin
  intro Hp,
  apply Hqr,
  exact Hpq Hp,
end

-- 3ª demostración
example
  (Hpq : p → q)
  (Hqr : q → r)
  : p → r :=
begin
  intro Hp,
  exact Hqr (Hpq Hp),
end

-- 4ª demostración
example
  (Hpq : p → q)
  (Hqr : q → r)
  : p → r :=
λ Hp, Hqr (Hpq Hp)

-- 5ª demostración
example
  (Hpq : p → q)
  (Hqr : q → r)
  : p → r :=
assume Hp : p,
have Hq : q,
  from Hpq Hp,
show r,
  from Hqr Hq

-- 6ª demostración
example
  (Hpq : p → q)
  (Hqr : q → r)
  : p → r :=
assume Hp : p,
have Hq : q,
  from Hpq Hp,
Hqr Hq

-- 7ª demostración
example
  (Hpq : p → q)
  (Hqr : q → r)
  : p → r :=
assume Hp : p,
Hqr (Hpq Hp)

-- 8ª demostración
example
  (Hpq : p → q)
  (Hqr : q → r)
  : p → r :=
λ Hp, Hqr (Hpq Hp)

-- 9ª demostración
example
  (Hpq : p → q)
  (Hqr : q → r)
  : p → r :=
-- by hint
by tauto

-- 10ª demostración
example
  (Hpq : p → q)
  (Hqr : q → r)
  : p → r :=
by finish

-- ----------------------------------------------------
-- Ejercicio 5. Demostrar
--    p → (q → r) ⊢ q → (p → r)
-- ----------------------------------------------------

-- 1ª demostración
example
  (Hpqr : p → (q → r))
  : q → (p → r) :=
begin
  intro Hq,
  intro Hp,
  have Hqr : q → r,
    from Hpqr Hp,
  apply Hqr,
  exact Hq,
end

-- 2ª demostración
example
  (Hpqr : p → (q → r))
  : q → (p → r) :=
begin
  intro Hq,
  intro Hp,
  have Hqr : q → r,
    from Hpqr Hp,
  exact Hqr Hq,
end

-- 3ª demostración
example
  (Hpqr : p → (q → r))
  : q → (p → r) :=
begin
  intro Hq,
  intro Hp,
  exact (Hpqr Hp) Hq,
end

-- 4ª demostración
example
  (Hpqr : p → (q → r))
  : q → (p → r) :=
begin
  intros Hq Hp,
  exact (Hpqr Hp) Hq,
end

-- 5ª demostración
example
  (Hpqr : p → (q → r))
  : q → (p → r) :=
λ Hq Hp, (Hpqr Hp) Hq

-- 6ª demostración
example
  (Hpqr : p → (q → r))
  : q → (p → r) :=
begin
  intros Hq Hp,
  apply Hpqr,
  { exact Hp, },
  { exact Hq, },
end

-- 7ª demostración
example
  (Hpqr : p → (q → r))
  : q → (p → r) :=
assume Hq : q,
assume Hp : p,
have Hqr : q → r,
  from Hpqr Hp,
show r,
  from Hqr Hq

-- 8ª demostración
example
  (Hpqr : p → (q → r))
  : q → (p → r) :=
assume Hq : q,
assume Hp : p,
have Hqr : q → r,
  from Hpqr Hp,
Hqr Hq

-- 9ª demostración
example
  (Hpqr : p → (q → r))
  : q → (p → r) :=
assume Hq : q,
assume Hp : p,
(Hpqr Hp) Hq

-- 10ª demostración
example
  (Hpqr : p → (q → r))
  : q → (p → r) :=
λ Hq Hp, (Hpqr Hp) Hq

-- 11ª demostración
example
  (Hpqr : p → (q → r))
  : q → (p → r) :=
-- by hint
by tauto

-- 12ª demostración
example
  (Hpqr : p → (q → r))
  : q → (p → r) :=
by finish

-- ----------------------------------------------------
-- Ejercicio 6. Demostrar
--    p → (q → r) ⊢ (p → q) → (p → r)
-- ----------------------------------------------------

-- 1ª demostración
example
  (Hpqr : p → (q → r))
  : (p → q) → (p → r) :=
begin
  intros Hpq Hp,
  have Hqr : q → r,
    from Hpqr Hp,
  apply Hqr,
  apply Hpq,
  exact Hp,
end

-- 2ª demostración
example
  (Hpqr : p → (q → r))
  : (p → q) → (p → r) :=
begin
  intros Hpq Hp,
  have Hqr : q → r,
    from Hpqr Hp,
  apply Hqr,
  exact Hpq Hp,
end

-- 3ª demostración
example
  (Hpqr : p → (q → r))
  : (p → q) → (p → r) :=
begin
  intros Hpq Hp,
  have Hqr : q → r,
    from Hpqr Hp,
  exact Hqr (Hpq Hp),
end

-- 4ª demostración
example
  (Hpqr : p → (q → r))
  : (p → q) → (p → r) :=
begin
  intros Hpq Hp,
  exact (Hpqr Hp) (Hpq Hp),
end

-- 5ª demostración
example
  (Hpqr : p → (q → r))
  : (p → q) → (p → r) :=
λ Hpq Hp, (Hpqr Hp) (Hpq Hp)

-- 6ª demostración
example
  (Hpqr : p → (q → r))
  : (p → q) → (p → r) :=
assume Hpq : p → q,
assume Hp : p,
have Hqr : q → r,
  from Hpqr Hp,
have Hq : q,
  from Hpq Hp,
show r,
  from Hqr Hq

-- 7ª demostración
example
  (Hpqr : p → (q → r))
  : (p → q) → (p → r) :=
assume Hpq : p → q,
assume Hp : p,
have Hqr : q → r,
  from Hpqr Hp,
have Hq : q,
  from Hpq Hp,
Hqr Hq

-- 8ª demostración
example
  (Hpqr : p → (q → r))
  : (p → q) → (p → r) :=
assume Hpq : p → q,
assume Hp : p,
have Hqr : q → r,
  from Hpqr Hp,
Hqr (Hpq Hp)

-- 9ª demostración
example
  (Hpqr : p → (q → r))
  : (p → q) → (p → r) :=
assume Hpq : p → q,
assume Hp : p,
(Hpqr Hp) (Hpq Hp)

-- 10ª demostración
example
  (Hpqr : p → (q → r))
  : (p → q) → (p → r) :=
λ Hpq Hp, (Hpqr Hp) (Hpq Hp)

-- 11ª demostración
example
  (Hpqr : p → (q → r))
  : (p → q) → (p → r) :=
-- by hint
by finish

-- ----------------------------------------------------
-- Ejercicio 7. Demostrar
--    p ⊢ q → p
-- ----------------------------------------------------

-- 1ª demostración
example
  (Hp : p)
  : q → p :=
begin
  intro Hq,
  exact Hp,
end

-- 2ª demostración
example
  (H : p)
  : q → p :=
λ _, H

-- 3ª demostración
example
  (Hp : p)
  : q → p :=
assume Hq : q,
show p,
  from Hp

-- 4ª demostración
example
  (Hp : p)
  : q → p :=
-- by library_search
imp_intro Hp

-- 5ª demostración
example
  (Hp : p)
  : q → p :=
-- by hint
by tauto

-- 6ª demostración
example
  (Hp : p)
  : q → p :=
by finish

-- ----------------------------------------------------
-- Ejercicio 8. Demostrar
--    ⊢ p → (q → p)
-- ----------------------------------------------------

-- 1ª demostración
example :
  p → (q → p) :=
begin
  intros Hp Hq,
  exact Hp,
end

-- 2ª demostración
example :
  p → (q → p) :=
λ Hp _, Hp

-- 3ª demostración
example :
  p → (q → p) :=
assume Hp : p,
assume Hq : q,
show p,
  from Hp

-- 4ª demostración
example :
  p → (q → p) :=
-- by library_search
imp_intro

-- 5ª demostración
example :
  p → (q → p) :=
-- by hint
by tauto

-- 6ª demostración
example :
  p → (q → p) :=
by finish

-- ----------------------------------------------------
-- Ejercicio 9. Demostrar
--    p → q ⊢ (q → r) → (p → r)
-- ----------------------------------------------------

-- 1ª demostración
example
  (Hpq : p → q)
  : (q → r) → (p → r) :=
begin
  intros Hqr Hp,
  apply Hqr,
  apply Hpq,
  exact Hp,
end

-- 2ª demostración
example
  (Hpq : p → q)
  : (q → r) → (p → r) :=
begin
  intros Hqr Hp,
  apply Hqr,
  exact Hpq Hp,
end

-- 3ª demostración
example
  (Hpq : p → q)
  : (q → r) → (p → r) :=
begin
  intros Hqr Hp,
  exact Hqr (Hpq Hp),
end

-- 4ª demostración
example
  (Hpq : p → q)
  : (q → r) → (p → r) :=
λ Hqr Hp, Hqr (Hpq Hp)

-- 5ª demostración
example
  (Hpq : p → q)
  : (q → r) → (p → r) :=
assume Hqr : q → r,
assume Hp : p,
have Hq : q,
  from Hpq Hp,
show r,
  from Hqr Hq

-- 6ª demostración
example
  (Hpq : p → q)
  : (q → r) → (p → r) :=
assume Hqr : q → r,
assume Hp : p,
have Hq : q,
  from Hpq Hp,
Hqr Hq

-- 7ª demostración
example
  (Hpq : p → q)
  : (q → r) → (p → r) :=
λ Hqr Hp, Hqr (Hpq Hp)

-- 8ª demostración
example
  (Hpq : p → q)
  : (q → r) → (p → r) :=
-- by hint
by tauto

-- 9ª demostración
example
  (Hpq : p → q)
  : (q → r) → (p → r) :=
by finish

-- ----------------------------------------------------
-- Ejercicio 10. Demostrar
--    p → (q → (r → s)) ⊢ r → (q → (p → s))
-- ----------------------------------------------------

-- 1ª demostración
example
  (H : p → (q → (r → s)))
  : r → (q → (p → s)) :=
begin
  intros Hr Hq Hp,
  apply H,
  { exact Hp, },
  { exact Hq, },
  { exact Hr, },
end

-- 2ª demostración
example
  (H : p → (q → (r → s)))
  : r → (q → (p → s)) :=
begin
  intros Hr Hq Hp,
  exact H Hp Hq Hr,
end

-- 3ª demostración
example
  (H : p → (q → (r → s)))
  : r → (q → (p → s)) :=
λ Hr Hq Hp, H Hp Hq Hr

-- 4ª demostración
example
  (H : p → (q → (r → s)))
  : r → (q → (p → s)) :=
assume Hr : r,
assume Hq : q,
assume Hp : p,
have H1 : q → (r → s),
  from H Hp,
have H2 : r → s,
  from H1 Hq,
show s,
  from H2 Hr

-- 5ª demostración
example
  (H : p → (q → (r → s)))
  : r → (q → (p → s)) :=
assume Hr : r,
assume Hq : q,
assume Hp : p,
have H1 : q → (r → s),
  from H Hp,
have H2 : r → s,
  from H1 Hq,
H2 Hr

-- 6ª demostración
example
  (H : p → (q → (r → s)))
  : r → (q → (p → s)) :=
assume Hr : r,
assume Hq : q,
assume Hp : p,
have H1 : q → (r → s),
  from H Hp,
(H1 Hq) Hr

-- 7ª demostración
example
  (H : p → (q → (r → s)))
  : r → (q → (p → s)) :=
assume Hr : r,
assume Hq : q,
assume Hp : p,
((H Hp) Hq) Hr

-- 8ª demostración
example
  (H : p → (q → (r → s)))
  : r → (q → (p → s)) :=
λ Hr Hq Hp, ((H Hp) Hq) Hr

-- 9ª demostración
example
  (H : p → (q → (r → s)))
  : r → (q → (p → s)) :=
-- by hint
by finish

-- ----------------------------------------------------
-- Ejercicio 11. Demostrar
--    ⊢ (p → (q → r)) → ((p → q) → (p → r))
-- ----------------------------------------------------

-- 1ª demostración
example
  (Hpqr : p → (q → r))
  : (p → q) → (p → r) :=
begin
  intros Hpq Hp,
  apply Hpqr,
  { exact Hp, },
  { apply Hpq,
    exact Hp, },
end

-- 2ª demostración
example
  (Hpqr : p → (q → r))
  : (p → q) → (p → r) :=
begin
  intros Hpq Hp,
  apply Hpqr,
  { exact Hp, },
  { exact Hpq Hp, },
end

-- 3ª demostración
example
  (Hpqr : p → (q → r))
  : (p → q) → (p → r) :=
begin
  intros Hpq Hp,
  exact Hpqr Hp (Hpq Hp),
end

-- 4ª demostración
example
  (Hpqr : p → (q → r))
  : (p → q) → (p → r) :=
λ Hpq Hp, Hpqr Hp (Hpq Hp)

-- 5ª demostración
example
  (Hpqr : p → (q → r))
  : (p → q) → (p → r) :=
assume Hpq : p → q,
assume Hp : p,
have Hq : q,
  from Hpq Hp,
have Hqr : q → r,
  from Hpqr Hp,
show r,
  from Hqr Hq

-- 6ª demostración
example
  (Hpqr : p → (q → r))
  : (p → q) → (p → r) :=
assume Hpq : p → q,
assume Hp : p,
have Hq : q,
  from Hpq Hp,
have Hqr : q → r,
  from Hpqr Hp,
Hqr Hq

-- 7ª demostración
example
  (Hpqr : p → (q → r))
  : (p → q) → (p → r) :=
assume Hpq : p → q,
assume Hp : p,
have Hq : q,
  from Hpq Hp,
(Hpqr Hp) Hq

-- 8ª demostración
example
  (Hpqr : p → (q → r))
  : (p → q) → (p → r) :=
assume Hpq : p → q,
assume Hp : p,
(Hpqr Hp) (Hpq Hp)

-- 9ª demostración
example
  (Hpqr : p → (q → r))
  : (p → q) → (p → r) :=
λ Hpq Hp, (Hpqr Hp) (Hpq Hp)

-- 10ª demostración
example
  (Hpqr : p → (q → r))
  : (p → q) → (p → r) :=
-- by hint
by finish

-- ----------------------------------------------------
-- Ejercicio 12. Demostrar
--    (p → q) → r ⊢ p → (q → r)
-- ----------------------------------------------------

-- 1ª demostración
example
  (Hpqr : (p → q) → r)
  : p → (q → r) :=
begin
  intros Hp Hq,
  apply Hpqr,
  intro Hp,
  exact Hq,
end

-- 2ª demostración
example
  (Hpqr : (p → q) → r)
  : p → (q → r) :=
begin
  intros Hp Hq,
  apply Hpqr,
  exact (λ Hp, Hq),
end

-- 3ª demostración
example
  (Hpqr : (p → q) → r)
  : p → (q → r) :=
begin
  intros Hp Hq,
  exact Hpqr (λ Hp, Hq),
end

-- 4ª demostración
example
  (Hpqr : (p → q) → r)
  : p → (q → r) :=
λ Hp Hq, Hpqr (λ Hp, Hq)

-- 5ª demostración
example
  (Hpqr : (p → q) → r)
  : p → (q → r) :=
assume Hp : p,
assume Hq : q,
have Hpq : p → q,
  { assume p,
    show q,
      from Hq },
show r,
  from Hpqr Hpq

-- 6ª demostración
example
  (Hpqr : (p → q) → r)
  : p → (q → r) :=
assume Hp : p,
assume Hq : q,
have Hpq : p → q,
  { assume p,
    show q,
      from Hq },
Hpqr Hpq

-- 7ª demostración
example
  (Hpqr : (p → q) → r)
  : p → (q → r) :=
assume Hp : p,
assume Hq : q,
have Hpq : p → q,
  from (λ p, Hq),
Hpqr Hpq

-- 8ª demostración
example
  (Hpqr : (p → q) → r)
  : p → (q → r) :=
assume Hp : p,
assume Hq : q,
Hpqr (λ p, Hq)

-- 9ª demostración
example
  (Hpqr : (p → q) → r)
  : p → (q → r) :=
λ Hp Hq, Hpqr (λ p, Hq)

-- 10ª demostración
example
  (Hpqr : (p → q) → r)
  : p → (q → r) :=
-- by hint
by finish

-- § Conjunciones
-- ==============

-- ----------------------------------------------------
-- Ejercicio 13. Demostrar
--    p, q ⊢  p ∧ q
-- ----------------------------------------------------

-- 1ª demostración
example
  (Hp : p)
  (Hq : q)
  : p ∧ q :=
begin
  split,
  { exact Hp, },
  { exact Hq, },
end

-- 2ª demostración
example
  (Hp : p)
  (Hq : q)
  : p ∧ q :=
and.intro Hp Hq

-- 3ª demostración
example
  (Hp : p)
  (Hq : q)
  : p ∧ q :=
-- by library_search
⟨Hp, Hq⟩

-- 4ª demostración
example
  (Hp : p)
  (Hq : q)
  : p ∧ q :=
-- by hint
by tauto

-- 5ª demostración
example
  (Hp : p)
  (Hq : q)
  : p ∧ q :=
by finish

-- ----------------------------------------------------
-- Ejercicio 14. Demostrar
--    p ∧ q ⊢ p
-- ----------------------------------------------------

-- 1ª demostración
example
  (H : p ∧ q)
  : p :=
begin
  cases H with Hp Hq,
  exact Hp,
end

-- 2ª demostración
example
  (H : p ∧ q)
  : p :=
and.elim_left H

-- 3ª demostración
example
  (H : p ∧ q)
  : p :=
and.left H

-- 4ª demostración
example
  (H : p ∧ q)
  : p :=
H.left

-- 5ª demostración
example
  (H : p ∧ q)
  : p :=
H.1

-- 6ª demostración
example
  (H : p ∧ q)
  : p :=
-- by library_search
H.left

-- 7ª demostración
example
  (H : p ∧ q)
  : p :=
-- by hint
by tauto

-- 8ª demostración
example
  (H : p ∧ q)
  : p :=
by finish

-- ----------------------------------------------------
-- Ejercicio 15. Demostrar
--    p ∧ q ⊢ q
-- ----------------------------------------------------

-- 1ª demostración
example
  (H : p ∧ q)
  : q :=
begin
  cases H with Hp Hq,
  exact Hq,
end

-- 2ª demostración
example
  (H : p ∧ q)
  : q :=
and.elim_right H

-- 3ª demostración
example
  (H : p ∧ q)
  : q :=
and.right H

-- 4ª demostración
example
  (H : p ∧ q)
  : q :=
H.right

-- 5ª demostración
example
  (H : p ∧ q)
  : q :=
H.2

-- 6ª demostración
example
  (H : p ∧ q)
  : q :=
-- by library_search
H.right

-- 7ª demostración
example
  (H : p ∧ q)
  : q :=
-- by hint
by tauto

-- 8ª demostración
example
  (H : p ∧ q)
  : q :=
by finish

-- ----------------------------------------------------
-- Ejercicio 16. Demostrar
--    p ∧ (q ∧ r) ⊢ (p ∧ q) ∧ r
-- ----------------------------------------------------

-- 1ª demostración
example
  (Hpqr : p ∧ (q ∧ r))
  : (p ∧ q) ∧ r :=
begin
  cases Hpqr with Hp Hqr,
  cases Hqr with Hq Hr,
  split,
  { split,
    { exact Hp, },
    { exact Hq, }},
  { exact Hr, },
end

-- 2ª demostración
example
  (Hpqr : p ∧ (q ∧ r))
  : (p ∧ q) ∧ r :=
begin
  cases Hpqr with Hp Hqr,
  cases Hqr with Hq Hr,
  split,
  { exact ⟨Hp, Hq⟩, },
  { exact Hr, },
end

-- 3ª demostración
example
  (Hpqr : p ∧ (q ∧ r))
  : (p ∧ q) ∧ r :=
begin
  cases Hpqr with Hp Hqr,
  cases Hqr with Hq Hr,
  exact ⟨⟨Hp, Hq⟩, Hr⟩,
end

-- 4ª demostración
example
  (Hpqr : p ∧ (q ∧ r))
  : (p ∧ q) ∧ r :=
begin
  rcases Hpqr with ⟨Hp, ⟨Hq, Hr⟩⟩,
  exact ⟨⟨Hp, Hq⟩, Hr⟩,
end

-- 5ª demostración
example :
  p ∧ (q ∧ r) → (p ∧ q) ∧ r :=
begin
  rintros ⟨Hp, ⟨Hq, Hr⟩⟩,
  exact ⟨⟨Hp, Hq⟩, Hr⟩,
end

-- 6ª demostración
example :
  p ∧ (q ∧ r) → (p ∧ q) ∧ r :=
λ ⟨Hp, ⟨Hq, Hr⟩⟩, ⟨⟨Hp, Hq⟩, Hr⟩

-- 7ª demostración
example
  (Hpqr : p ∧ (q ∧ r))
  : (p ∧ q) ∧ r :=
have Hp : p,
  from and.left Hpqr,
have Hqr : q ∧ r,
  from and.right Hpqr,
have Hq : q,
  from and.left Hqr,
have Hr : r,
  from and.right Hqr,
have Hpq : p ∧ q,
  from and.intro Hp Hq,
show (p ∧ q) ∧ r,
  from and.intro Hpq Hr

-- 8ª demostración
example
  (Hpqr : p ∧ (q ∧ r))
  : (p ∧ q) ∧ r :=
-- by library_search
(and_assoc p q).mpr Hpqr

-- 9ª demostración
example
  (Hpqr : p ∧ (q ∧ r))
  : (p ∧ q) ∧ r :=
-- by hint
by tauto

-- 10ª demostración
example
  (Hpqr : p ∧ (q ∧ r))
  : (p ∧ q) ∧ r :=
by finish

-- ----------------------------------------------------
-- Ejercicio 17. Demostrar
--    (p ∧ q) ∧ r ⊢ p ∧ (q ∧ r)
-- ----------------------------------------------------

-- 1ª demostración
example
  (Hpqr : (p ∧ q) ∧ r)
  : p ∧ (q ∧ r) :=
begin
  rcases Hpqr with ⟨⟨Hp, Hq⟩, Hr⟩,
  exact ⟨Hp, ⟨Hq, Hr⟩⟩,
end

-- 2ª demostración
example
  : (p ∧ q) ∧ r → p ∧ (q ∧ r) :=
begin
  rintros ⟨⟨Hp, Hq⟩, Hr⟩,
  exact ⟨Hp, ⟨Hq, Hr⟩⟩,
end

-- 3ª demostración
example
  : (p ∧ q) ∧ r → p ∧ (q ∧ r) :=
λ ⟨⟨Hp, Hq⟩, Hr⟩, ⟨Hp, ⟨Hq, Hr⟩⟩

-- 4ª demostración
example
  (Hpqr : (p ∧ q) ∧ r)
  : p ∧ (q ∧ r) :=
have Hpq : p ∧ q,
  from and.left Hpqr,
have Hr : r,
  from and.right Hpqr,
have Hp : p,
  from and.left Hpq,
have Hq : q,
  from and.right Hpq,
have Hqr : q ∧ r,
  from and.intro Hq Hr,
show p ∧ (q ∧ r),
  from and.intro Hp Hqr

-- 5ª demostración
example
  (Hpqr : (p ∧ q) ∧ r)
  : p ∧ (q ∧ r) :=
-- by library_search
(and_assoc p q).mp Hpqr

-- 6ª demostración
example
  (Hpqr : (p ∧ q) ∧ r)
  : p ∧ (q ∧ r) :=
-- by hint
by tauto

-- 7ª demostración
example
  (Hpqr : (p ∧ q) ∧ r)
  : p ∧ (q ∧ r) :=
by finish

-- ----------------------------------------------------
-- Ejercicio 18. Demostrar
--    p ∧ q ⊢ p → q
-- ----------------------------------------------------

-- 1ª demostración
example
  (Hpq : p ∧ q)
  : p → q :=
begin
  intro p,
  exact Hpq.right,
end

-- 2ª demostración
example
  (Hpq : p ∧ q)
  : p → q :=
λ _, Hpq.2

-- 3ª demostración
example
  (Hpq : p ∧ q)
  : p → q :=
assume Hp : p,
show q,
  from and.right Hpq

-- 4ª demostración
example
  (Hpq : p ∧ q)
  : p → q :=
-- by hint
by tauto

-- 5ª demostración
example
  (Hpq : p ∧ q)
  : p → q :=
by finish

-- ----------------------------------------------------
-- Ejercicio 19. Demostrar
--    (p → q) ∧ (p → r) ⊢ p → q ∧ r
-- ----------------------------------------------------

-- 1ª demostración
example
  (H : (p → q) ∧ (p → r))
  : p → q ∧ r :=
begin
  cases H with Hpq Hpr,
  intro Hp,
  split,
  { apply Hpq,
    exact Hp, },
  { apply Hpr,
    exact Hp, },
end

-- 2ª demostración
example
  (H : (p → q) ∧ (p → r))
  : p → q ∧ r :=
begin
  cases H with Hpq Hpr,
  intro Hp,
  split,
  { exact Hpq Hp, },
  { exact Hpr Hp, },
end

-- 3ª demostración
example
  (H : (p → q) ∧ (p → r))
  : p → q ∧ r :=
begin
  cases H with Hpq Hpr,
  intro Hp,
  exact ⟨Hpq Hp, Hpr Hp⟩,
end

-- 4ª demostración
example
  : (p → q) ∧ (p → r) → (p → q ∧ r) :=
begin
  rintros ⟨Hpq, Hpr⟩ Hp,
  exact ⟨Hpq Hp, Hpr Hp⟩,
end

-- 5ª demostración
example
  : (p → q) ∧ (p → r) → (p → q ∧ r) :=
λ ⟨Hpq, Hpr⟩ Hp, ⟨Hpq Hp, Hpr Hp⟩

-- 6ª demostración
example
  (H : (p → q) ∧ (p → r))
  : p → q ∧ r :=
have Hpq : p → q,
  from and.left H,
have Hpr : p → r,
  from and.right H,
assume Hp : p,
have Hq : q,
  from Hpq Hp,
have Hr : r,
  from Hpr Hp,
show q ∧ r,
  from and.intro Hq Hr

-- 7ª demostración
example
  (H : (p → q) ∧ (p → r))
  : p → q ∧ r :=
-- by library_search
imp_and_distrib.mpr H

-- 8ª demostración
example
  (H : (p → q) ∧ (p → r))
  : p → q ∧ r :=
-- by hint
by tauto

-- 9ª demostración
example
  (H : (p → q) ∧ (p → r))
  : p → q ∧ r :=
by finish

-- ----------------------------------------------------
-- Ejercicio 20. Demostrar
--    p → q ∧ r ⊢ (p → q) ∧ (p → r)
-- ----------------------------------------------------

-- 1ª demostración
example
  (H : p → q ∧ r)
  : (p → q) ∧ (p → r) :=
begin
  split,
  { intro Hp,
    have Hqr : q ∧ r,
      from H Hp,
    exact Hqr.left, },
  { intro Hp,
    have Hqr : q ∧ r,
      from H Hp,
    exact Hqr.right, },
end

-- 2ª demostración
example
  (H : p → q ∧ r)
  : (p → q) ∧ (p → r) :=
begin
  split,
  { intro Hp,
    exact (H Hp).left, },
  { intro Hp,
    exact (H Hp).right, },
end

-- 3ª demostración
example
  (H : p → q ∧ r)
  : (p → q) ∧ (p → r) :=
⟨λ Hp, (H Hp).left,
 λ Hp, (H Hp).right⟩

-- 4ª demostración
example
  (H : p → q ∧ r)
  : (p → q) ∧ (p → r) :=
have Hpq : p → q, from
  assume Hp : p,
  have Hqr : q ∧ r,
    from H Hp,
  show q,
    from and.left Hqr,
have Hpr : p → r, from
  assume Hp : p,
  have Hqr : q ∧ r,
    from H Hp,
  show r,
    from and.right Hqr,
show (p → q) ∧ (p → r),
  from and.intro Hpq Hpr

-- 5ª demostración
example
  (H : p → q ∧ r)
  : (p → q) ∧ (p → r) :=
and.intro
  ( assume Hp : p,
    have Hqr : q ∧ r,
      from H Hp,
    show q,
      from and.left Hqr)
  ( assume Hp : p,
    have Hqr : q ∧ r,
      from H Hp,
    show r,
      from and.right Hqr)

-- 6ª demostración
example
  (H : p → q ∧ r)
  : (p → q) ∧ (p → r) :=
-- by library_search
imp_and_distrib.mp H

-- 7ª demostración
example
  (H : p → q ∧ r)
  : (p → q) ∧ (p → r) :=
-- by hint
by finish

-- ----------------------------------------------------
-- Ejercicio 21. Demostrar
--    p → (q → r) ⊢ p ∧ q → r
-- ----------------------------------------------------

-- 1ª demostración
example
  (H : p → (q → r))
  : p ∧ q → r :=
begin
  intro Hpq,
  apply H,
  { exact Hpq.left, },
  { exact Hpq.right, },
end

-- 2ª demostración
example
  (H : p → (q → r))
  : p ∧ q → r :=
begin
  intro Hpq,
  exact (H Hpq.left) Hpq.right,
end

-- 3ª demostración
example
  (H : p → (q → r))
  : p ∧ q → r :=
λ Hpq, (H Hpq.left) Hpq.right

-- 4ª demostración
example
  (H : p → (q → r))
  : p ∧ q → r :=
λ Hpq, H Hpq.1 Hpq.2

-- 5ª demostración
example
  (H : p → (q → r))
  : p ∧ q → r :=
assume Hpq : p ∧ q,
have Hp : p,
  from and.left Hpq,
have Hq : q,
  from and.right Hpq,
have Hqr : q → r,
  from H Hp,
show r,
  from Hqr Hq

-- 6ª demostración
example
  (H : p → (q → r))
  : p ∧ q → r :=
-- by library_search
and_imp.mpr H

-- 7ª demostración
example
  (H : p → (q → r))
  : p ∧ q → r :=
-- by hint
by tauto

-- 8ª demostración
example
  (H : p → (q → r))
  : p ∧ q → r :=
by finish

-- ----------------------------------------------------
-- Ejercicio 22. Demostrar
--    p ∧ q → r ⊢ p → (q → r)
-- ----------------------------------------------------

-- 1ª demostración
example
  (H : p ∧ q → r)
  : p → (q → r) :=
begin
  intros Hp Hq,
  apply H,
  split,
  { exact Hp, },
  { exact Hq, },
end

-- 2ª demostración
example
  (H : p ∧ q → r)
  : p → (q → r) :=
begin
  intros Hp Hq,
  apply H,
  exact ⟨Hp, Hq⟩,
end

-- 3ª demostración
example
  (H : p ∧ q → r)
  : p → (q → r) :=
begin
  intros Hp Hq,
  exact H ⟨Hp, Hq⟩,
end

-- 4ª demostración
example
  (H : p ∧ q → r)
  : p → (q → r) :=
λ Hp Hq, H ⟨Hp, Hq⟩

-- 5ª demostración
example
  (H : p ∧ q → r)
  : p → (q → r) :=
assume Hp : p,
show q → r, from
  assume Hq : q,
  have Hpq : p ∧ q,
    from and.intro Hp Hq,
  show r,
    from H Hpq

-- 6ª demostración
example
  (H : p ∧ q → r)
  : p → (q → r) :=
-- by library_search
and_imp.mp H

-- 7ª demostración
example
  (H : p ∧ q → r)
  : p → (q → r) :=
-- by hint
by finish

-- ----------------------------------------------------
-- Ejercicio 23. Demostrar
--    (p → q) → r ⊢ p ∧ q → r
-- ----------------------------------------------------

-- 1ª demostración
example
  (H : (p → q) → r)
  : p ∧ q → r :=
begin
  intro Hpq,
  apply H,
  intro Hp,
  exact Hpq.right,
end

-- 2ª demostración
example
  (H : (p → q) → r)
  : p ∧ q → r :=
begin
  intro Hpq,
  apply H,
  exact (λ Hp, Hpq.right),
end

-- 3ª demostración
example
  (H : (p → q) → r)
  : p ∧ q → r :=
begin
  intro Hpq,
  exact H (λ Hp, Hpq.right),
end

-- 4ª demostración
example
  (H : (p → q) → r)
  : p ∧ q → r :=
λ Hpq, H (λ _, Hpq.right)

-- 5ª demostración
example
  (H : (p → q) → r)
  : p ∧ q → r :=
assume Hpq : p ∧ q,
have H1 : p → q, from
  assume Hp : p,
  show q,
    from and.right Hpq,
show r,
  from H H1

-- 6ª demostración
example
  (H : (p → q) → r)
  : p ∧ q → r :=
-- by hint
by finish

-- ----------------------------------------------------
-- Ejercicio 24. Demostrar
--    p ∧ (q → r) ⊢ (p → q) → r
-- ----------------------------------------------------

-- 1ª demostración
example
  (H : p ∧ (q → r))
  : (p → q) → r :=
begin
  intro Hpq,
  cases H with Hp Hqr,
  apply Hqr,
  apply Hpq,
  exact Hp,
end

-- 2ª demostración
example
  (H : p ∧ (q → r))
  : (p → q) → r :=
begin
  intro Hpq,
  cases H with Hp Hqr,
  apply Hqr,
  exact Hpq Hp,
end

-- 3ª demostración
example
  (H : p ∧ (q → r))
  : (p → q) → r :=
begin
  intro Hpq,
  cases H with Hp Hqr,
  exact Hqr (Hpq Hp),
end

-- 4ª demostración
example
  (H : p ∧ (q → r))
  : (p → q) → r :=
begin
  intro Hpq,
  exact H.2 (Hpq H.1),
end

-- 5ª demostración
example
  (H : p ∧ (q → r))
  : (p → q) → r :=
λ Hpq, H.2 (Hpq H.1)

-- 6ª demostración
example
  (H : p ∧ (q → r))
  : (p → q) → r :=
assume Hpq : p → q,
have Hp : p,
  from and.left H,
have Hq : q,
  from Hpq Hp,
have Hqr : q → r,
  from H.right,
show r,
  from Hqr Hq

-- 7ª demostració
example
  (H : p ∧ (q → r))
  : (p → q) → r :=
-- by hint
by tauto

-- 8ª demostració
example
  (H : p ∧ (q → r))
  : (p → q) → r :=
-- by hint
by finish

-- § Disyunciones
-- ==============

-- ----------------------------------------------------
-- Ejercicio 25. Demostrar
--    p ⊢ p ∨ q
-- ----------------------------------------------------

-- 1ª demostración
example
  (H : p)
  : p ∨ q :=
begin
  left,
  exact H,
end

-- 2ª demostración
example
  (H : p)
  : p ∨ q :=
or.intro_left q H

-- 3ª demostración
example
  (H : p)
  : p ∨ q :=
-- by library_search
or.inl H

-- 4ª demostración
example
  (H : p)
  : p ∨ q :=
-- by hint
by tauto

-- 5ª demostración
example
  (H : p)
  : p ∨ q :=
by finish

-- ----------------------------------------------------
-- Ejercicio 26. Demostrar
--    q ⊢ p ∨ q
-- ----------------------------------------------------

-- 1ª demostración
example
  (H : q)
  : p ∨ q :=
begin
  right,
  exact H,
end

-- 2ª demostración
example
  (H : q)
  : p ∨ q :=
or.intro_right p H

-- 3ª demostración
example
  (H : q)
  : p ∨ q :=
-- by library_search
or.inr H

-- 4ª demostración
example
  (H : q)
  : p ∨ q :=
-- by hint
by tauto

-- 5ª demostración
example
  (H : q)
  : p ∨ q :=
by finish

-- ----------------------------------------------------
-- Ejercicio 27. Demostrar
--    p ∨ q ⊢ q ∨ p
-- ----------------------------------------------------

-- 1ª demostración
example
  (H : p ∨ q)
  : q ∨ p :=
begin
  cases H with Hp Hq,
  { right,
    exact Hp, },
  { left,
    exact Hq, },
end

-- 2ª demostración
example
  (H : p ∨ q)
  : q ∨ p :=
begin
  cases H with Hp Hq,
  { exact or.inr Hp, },
  { exact or.inl Hq, },
end

-- 3ª demostración
example
  (H : p ∨ q)
  : q ∨ p :=
or.elim H
  ( assume Hp : p,
    show q ∨ p,
      from or.inr Hp)
  ( assume Hq : q,
    show q ∨ p,
      from or.inl Hq)

-- 4ª demostración
example
  (H : p ∨ q)
  : q ∨ p :=
or.elim H
  ( assume Hp : p,
    or.inr Hp)
  ( assume Hq : q,
    or.inl Hq)

-- 5ª demostración
example
  (H : p ∨ q)
  : q ∨ p :=
or.elim H
  ( λ Hp, or.inr Hp)
  ( λ Hq, or.inl Hq)

-- 6ª demostración
example
  (H : p ∨ q)
  : q ∨ p :=
or.elim H or.inr or.inl

-- 7ª demostración
example
  (H : p ∨ q)
  : q ∨ p :=
-- by library_search
or.swap H

-- 8ª demostración
example
  (H : p ∨ q)
  : q ∨ p :=
-- by hint
by tauto

-- 9ª demostración
example
  (H : p ∨ q)
  : q ∨ p :=
by finish

-- ----------------------------------------------------
-- Ejercicio 28. Demostrar
--    q → r ⊢ p ∨ q → p ∨ r
-- ----------------------------------------------------

-- 1ª demostración
example
  (H : q → r)
  : p ∨ q → p ∨ r :=
begin
  intro H1,
  cases H1 with Hp Hq,
  { left,
    exact Hp, },
  { right,
    apply H,
    exact Hq, },
end

-- 2ª demostración
example
  (H : q → r)
  : p ∨ q → p ∨ r :=
begin
  rintro (Hp | Hq),
  { left,
    exact Hp, },
  { right,
    exact H Hq, },
end

-- 3ª demostración
example
  (H : q → r)
  : p ∨ q → p ∨ r :=
begin
  rintro (Hp | Hq),
  { exact or.inl Hp, },
  { exact or.inr (H Hq), },
end

-- 4ª demostración
example
  (H : q → r)
  : p ∨ q → p ∨ r :=
assume H1 : p ∨ q,
or.elim H1
  ( assume Hp : p,
    show p ∨ r,
      from or.inl Hp)
  ( assume Hq : q,
    have Hr : r,
      from H Hq,
    show p ∨ r,
      from or.inr Hr)

-- 5ª demostración
example
  (H : q → r)
  : p ∨ q → p ∨ r :=
assume H1 : p ∨ q,
or.elim H1
  ( assume Hp : p,
    or.inl Hp)
  ( assume Hq : q,
    have Hr : r,
      from H Hq,
    or.inr Hr)

-- 6ª demostración
example
  (H : q → r)
  : p ∨ q → p ∨ r :=
assume H1 : p ∨ q,
or.elim H1
  ( assume Hp : p,
    or.inl Hp)
  ( assume Hq : q,
    or.inr (H Hq))

-- 7ª demostración
example
  (H : q → r)
  : p ∨ q → p ∨ r :=
assume H1 : p ∨ q,
or.elim H1
  ( λ Hp, or.inl Hp)
  ( λ Hq, or.inr (H Hq))

-- 8ª demostración
example
  (H : q → r)
  : p ∨ q → p ∨ r :=
assume H1 : p ∨ q,
or.elim H1
  or.inl
  ( λ Hq, or.inr (H Hq))

-- 9ª demostración
example
  (H : q → r)
  : p ∨ q → p ∨ r :=
λ H1, or.elim H1 or.inl (λ Hq, or.inr (H Hq))

-- 10ª demostración
example
  (H : q → r)
  : p ∨ q → p ∨ r :=
-- by library_search
or.imp_right H

-- ----------------------------------------------------
-- Ejercicio 29. Demostrar
--    p ∨ p ⊢ p
-- ----------------------------------------------------

-- 1ª demostración
example
  (H : p ∨ p)
  : p :=
begin
  cases H with Hp Hp,
  { exact Hp, },
  { exact Hp, },
end

-- 2ª demostración
example
  (H : p ∨ p)
  : p :=
by cases H ; assumption

-- 3ª demostración
example
  (H : p ∨ p)
  : p :=
or.elim H
  ( assume Hp : p,
    show p,
      from Hp)
  ( assume Hp : p,
    show p,
      from Hp)

-- 4ª demostración
example
  (H : p ∨ p)
  : p :=
or.elim H
  ( assume Hp : p,
    Hp)
  ( assume Hp : p,
    Hp)

-- 5ª demostración
example
  (H : p ∨ p)
  : p :=
or.elim H
  ( λ Hp, Hp)
  ( λ Hp, Hp)

-- 6ª demostración
example
  (H : p ∨ p)
  : p :=
or.elim H id id

-- 7ª demostración
example
  (H : p ∨ p)
  : p :=
-- by library_search
(or_self p).mp H

-- 8ª demostración
example
  (H : p ∨ p)
  : p :=
-- by hint
by tauto

-- 9ª demostración
example
  (H : p ∨ p)
  : p :=
by finish

-- ----------------------------------------------------
-- Ejercicio 30. Demostrar
--    p ⊢ p ∨ p
-- ----------------------------------------------------

-- 1ª demostración
example
  (H : p)
  : p ∨ p :=
-- by library_search
or.inl H

-- 2ª demostración
example
  (H : p)
  : p ∨ p :=
-- by hint
by tauto

-- 3ª demostración
example
  (H : p)
  : p ∨ p :=
by finish

-- ----------------------------------------------------
-- Ejercicio 31. Demostrar
--    p ∨ (q ∨ r) ⊢ (p ∨ q) ∨ r
-- ----------------------------------------------------

-- 1ª demostración
example
  (H : p ∨ (q ∨ r))
  : (p ∨ q) ∨ r :=
begin
  cases H with Hp Hqr,
  { left,
    left,
    exact Hp, },
  { cases Hqr with Hq Hr,
    { left,
      right,
      exact Hq, },
    { right,
      exact Hr, }},
end

-- 2ª demostración
example
  (H : p ∨ (q ∨ r))
  : (p ∨ q) ∨ r :=
or.elim H
  ( assume Hp : p,
    have Hpq : p ∨ q,
      from or.inl Hp,
    show (p ∨ q) ∨ r,
      from or.inl Hpq)
  ( assume Hqr : q ∨ r,
    show (p ∨ q) ∨ r, from
      or.elim Hqr
        ( assume Hq : q,
          have Hpq : p ∨ q,
            from or.inr Hq,
          show (p ∨ q) ∨ r,
            from or.inl Hpq)
        ( assume Hr : r,
          show (p ∨ q) ∨ r,
            from or.inr Hr))

-- 3ª demostración
example
  (H : p ∨ (q ∨ r))
  : (p ∨ q) ∨ r :=
or.elim H
  ( assume Hp : p,
    have Hpq : p ∨ q,
      from or.inl Hp,
    show (p ∨ q) ∨ r,
      from or.inl Hpq)
  ( assume Hqr : q ∨ r,
    show (p ∨ q) ∨ r, from
      or.elim Hqr
        ( assume Hq : q,
          have Hpq : p ∨ q,
            from or.inr Hq,
          or.inl Hpq)
        ( assume Hr : r,
          or.inr Hr))

-- 4ª demostración
example
  (H : p ∨ (q ∨ r))
  : (p ∨ q) ∨ r :=
or.elim H
  ( assume Hp : p,
    have Hpq : p ∨ q,
      from or.inl Hp,
    show (p ∨ q) ∨ r,
      from or.inl Hpq)
  ( assume Hqr : q ∨ r,
    show (p ∨ q) ∨ r, from
      or.elim Hqr
        ( assume Hq : q,
          have Hpq : p ∨ q,
            from or.inr Hq,
          or.inl Hpq)
        or.inr)

-- 5ª demostración
example
  (H : p ∨ (q ∨ r))
  : (p ∨ q) ∨ r :=
or.elim H
  ( assume Hp : p,
    have Hpq : p ∨ q,
      from or.inl Hp,
    show (p ∨ q) ∨ r,
      from or.inl Hpq)
  ( assume Hqr : q ∨ r,
    show (p ∨ q) ∨ r, from
      or.elim Hqr
        ( λ Hq, or.inl (or.inr Hq))
        or.inr)

-- 6ª demostración
example
  (H : p ∨ (q ∨ r))
  : (p ∨ q) ∨ r :=
or.elim H
  ( assume Hp : p,
    have Hpq : p ∨ q,
      from or.inl Hp,
    show (p ∨ q) ∨ r,
      from or.inl Hpq)
  ( λ Hqr, or.elim Hqr ( λ Hq, or.inl (or.inr Hq)) or.inr)

-- 7ª demostración
example
  (H : p ∨ (q ∨ r))
  : (p ∨ q) ∨ r :=
or.elim H
  ( assume Hp : p,
    have Hpq : p ∨ q,
      from or.inl Hp,
    or.inl Hpq)
  (λ Hqr, or.elim Hqr ( λ Hq, or.inl (or.inr Hq)) or.inr)

-- 8ª demostración
example
  (H : p ∨ (q ∨ r))
  : (p ∨ q) ∨ r :=
or.elim H
  ( assume Hp : p,
    or.inl (or.inl Hp))
  (λ Hqr, or.elim Hqr ( λ Hq, or.inl (or.inr Hq)) or.inr)

-- 9ª demostración
example
  (H : p ∨ (q ∨ r))
  : (p ∨ q) ∨ r :=
or.elim H
  (λ Hp, or.inl (or.inl Hp))
  (λ Hqr, or.elim Hqr ( λ Hq, or.inl (or.inr Hq)) or.inr)

-- 10ª demostración
example
  (H : p ∨ (q ∨ r))
  : (p ∨ q) ∨ r :=
-- by library_search
or.assoc.mpr H

-- 11ª demostración
example
  (H : p ∨ (q ∨ r))
  : (p ∨ q) ∨ r :=
-- by hint
by tauto

-- 12ª demostración
example
  (H : p ∨ (q ∨ r))
  : (p ∨ q) ∨ r :=
by finish

-- ----------------------------------------------------
-- Ejercicio 32. Demostrar
--    (p ∨ q) ∨ r ⊢ p ∨ (q ∨ r)
-- ----------------------------------------------------

-- 1ª demostración
example
  (H : (p ∨ q) ∨ r)
  : p ∨ (q ∨ r) :=
begin
  rcases H with ((Hp | Hq) | Hr),
  { left,
    exact Hp, },
  { right,
    left,
    exact Hq, },
  { right,
    right,
    exact Hr, },
end

-- 2ª demostración
example
  (H : (p ∨ q) ∨ r)
  : p ∨ (q ∨ r) :=
or.elim H
  ( assume Hpq : p ∨ q,
    show p ∨ q ∨ r, from
      or.elim Hpq
        ( assume Hp : p,
          show p ∨ (q ∨ r),
            from or.inl Hp)
        ( assume Hq : q,
          have Hqr: q ∨ r,
            from or.inl Hq,
          show p ∨ (q ∨ r),
            from or.inr Hqr))
  ( assume Hr : r,
    have Hqr: q ∨ r,
      from or.inr Hr,
    show p ∨ (q ∨ r),
      from or.inr Hqr)

-- 3ª demostración
example
  (H : (p ∨ q) ∨ r)
  : p ∨ (q ∨ r) :=
or.elim H
  ( λ Hpq, or.elim Hpq or.inl (λ Hq, or.inr (or.inl Hq)))
  ( λ Hr, or.inr (or.inr Hr))

-- 4ª demostración
example
  (H : (p ∨ q) ∨ r)
  : p ∨ (q ∨ r) :=
-- by library_search
or.assoc.mp H

-- 5ª demostración
example
  (H : (p ∨ q) ∨ r)
  : p ∨ (q ∨ r) :=
-- by hint
by tauto

-- 6ª demostración
example
  (H : (p ∨ q) ∨ r)
  : p ∨ (q ∨ r) :=
by finish

-- ----------------------------------------------------
-- Ejercicio 33. Demostrar
--    p ∧ (q ∨ r) ⊢ (p ∧ q) ∨ (p ∧ r)
-- ----------------------------------------------------

-- 1ª demostración
example
  (H : p ∧ (q ∨ r))
  : (p ∧ q) ∨ (p ∧ r) :=
begin
  cases H with Hp Hqr,
  cases Hqr with Hq Hr,
  { left,
    split,
    { exact Hp, },
    { exact Hq, }},
  { right,
    split,
    { exact Hp, },
    { exact Hr, }},
end

-- 2ª demostración
example
  (H : p ∧ (q ∨ r))
  : (p ∧ q) ∨ (p ∧ r) :=
begin
  cases H with Hp Hqr,
  cases Hqr with Hq Hr,
  { left,
    exact ⟨Hp, Hq⟩, },
  { right,
    exact ⟨Hp, Hr⟩, },
end

-- 3ª demostración
example
  (H : p ∧ (q ∨ r))
  : (p ∧ q) ∨ (p ∧ r) :=
have Hp : p,
  from and.left H,
have Hqr : q ∨ r,
  from and.right H,
or.elim Hqr
  ( assume Hq : q,
    have Hpq : p ∧ q,
      from and.intro Hp Hq,
    show (p ∧ q) ∨ (p ∧ r),
      from or.inl Hpq)
  ( assume Hr : r,
    have Hpr : p ∧ r,
      from and.intro Hp Hr,
    show (p ∧ q) ∨ (p ∧ r),
      from or.inr Hpr)

-- 4ª demostración
example
  (H : p ∧ (q ∨ r))
  : (p ∧ q) ∨ (p ∧ r) :=
or.elim H.2
  (λ Hq, or.inl ⟨H.1, Hq⟩)
  (λ Hr, or.inr ⟨H.1, Hr⟩)

-- 5ª demostración
example
  (H : p ∧ (q ∨ r))
  : (p ∧ q) ∨ (p ∧ r) :=
-- by library_search
and_or_distrib_left.mp H

-- 6ª demostración
example
  (H : p ∧ (q ∨ r))
  : (p ∧ q) ∨ (p ∧ r) :=
-- by hint
by finish

-- ----------------------------------------------------
-- Ejercicio 34. Demostrar
--    (p ∧ q) ∨ (p ∧ r) ⊢ p ∧ (q ∨ r)
-- ----------------------------------------------------

-- 1ª demostración
example
  (H : (p ∧ q) ∨ (p ∧ r))
  : p ∧ (q ∨ r) :=
begin
  rcases H with (⟨Hp,Hq⟩ | ⟨Hp, Hr⟩),
  { exact ⟨Hp, or.inl Hq⟩, },
  { exact ⟨Hp, or.inr Hr⟩, },
end

-- 2ª demostración
example
  (H : (p ∧ q) ∨ (p ∧ r))
  : p ∧ (q ∨ r) :=
or.elim H
  ( assume Hpq : p ∧ q,
    have Hp : p,
      from and.left Hpq,
    have Hq : q,
      from and.right Hpq,
    have Hqr : q ∨ r,
      from or.inl Hq,
    show p ∧ (q ∨ r),
      from and.intro Hp Hqr)
  ( assume Hpr : p ∧ r,
    have Hp : p,
      from and.left Hpr,
    have Hr : r,
      from and.right Hpr,
    have Hqr : q ∨ r,
      from or.inr Hr,
    show p ∧ (q ∨ r),
      from and.intro Hp Hqr)

-- 3ª demostración
example
  (H : (p ∧ q) ∨ (p ∧ r))
  : p ∧ (q ∨ r) :=
or.elim H
  ( assume ⟨Hp, Hq⟩,
    have Hqr : q ∨ r,
      from or.inl Hq,
    show p ∧ (q ∨ r),
      from and.intro Hp Hqr)
  ( assume ⟨Hp, Hr⟩,
    have Hqr : q ∨ r,
      from or.inr Hr,
    show p ∧ (q ∨ r),
      from and.intro Hp Hqr)

-- 4ª demostración
example
  (H : (p ∧ q) ∨ (p ∧ r))
  : p ∧ (q ∨ r) :=
or.elim H
  (λ ⟨Hp, Hq⟩, ⟨Hp ,or.inl Hq⟩)
  (λ ⟨Hp, Hr⟩, ⟨Hp, or.inr Hr⟩)

-- 5ª demostración
example
  (H : (p ∧ q) ∨ (p ∧ r))
  : p ∧ (q ∨ r) :=
-- by library_search
and_or_distrib_left.mpr H

-- 6ª demostración
example
  (H : (p ∧ q) ∨ (p ∧ r))
  : p ∧ (q ∨ r) :=
-- by hint
by tauto

-- 7ª demostración
example
  (H : (p ∧ q) ∨ (p ∧ r))
  : p ∧ (q ∨ r) :=
by finish

-- ----------------------------------------------------
-- Ejercicio 35. Demostrar
--    p ∨ (q ∧ r) ⊢ (p ∨ q) ∧ (p ∨ r)
-- ----------------------------------------------------

-- 1ª demostración
example
  (H : p ∨ (q ∧ r))
  : (p ∨ q) ∧ (p ∨ r) :=
begin
  cases H with Hp Hqr,
  { split,
    { left,
      exact Hp, },
    { left,
      exact Hp, }},
  { split,
    { right,
      exact Hqr.left, },
    { right,
      exact Hqr.right, }},
end

-- 2ª demostración
example
  (H : p ∨ (q ∧ r))
  : (p ∨ q) ∧ (p ∨ r) :=
begin
  cases H with Hp Hqr,
  { split,
    { exact or.inl Hp, },
    { exact or.inl Hp, }},
  { split,
    { exact or.inr Hqr.left, },
    { exact or.inr Hqr.right, }},
end

-- 3ª demostración
example
  (H : p ∨ (q ∧ r))
  : (p ∨ q) ∧ (p ∨ r) :=
begin
  cases H with Hp Hqr,
  { exact ⟨or.inl Hp, or.inl Hp⟩, },
  { exact ⟨or.inr Hqr.left, or.inr Hqr.right⟩, },
end

-- 4ª demostración
example
  (H : p ∨ (q ∧ r))
  : (p ∨ q) ∧ (p ∨ r) :=
or.elim H
  (λ Hp, ⟨or.inl Hp, or.inl Hp⟩)
  (λ Hqr, ⟨or.inr Hqr.1, or.inr Hqr.2⟩)

-- 5ª demostración
example
  (H : p ∨ (q ∧ r))
  : (p ∨ q) ∧ (p ∨ r) :=
or.elim H
  (λ h, ⟨or.inl h,   or.inl h⟩)
  (λ h, ⟨or.inr h.1, or.inr h.2⟩)

-- 6ª demostración
example
  (H : p ∨ (q ∧ r))
  : (p ∨ q) ∧ (p ∨ r) :=
or.elim H
  ( assume Hp : p,
    have Hpq : p ∨ q,
      from or.inl Hp,
    have Hpr : p ∨ r,
      from or.inl Hp,
    show (p ∨ q) ∧ (p ∨ r),
      from and.intro Hpq Hpr)
  ( assume Hqr : q ∧ r,
    have Hq : q,
      from and.left Hqr,
    have Hr : r,
      from and.right Hqr,
    have Hpq : p ∨ q,
      from or.inr Hq,
    have Hpr : p ∨ r,
      from or.inr Hr,
    show (p ∨ q) ∧ (p ∨ r),
      from and.intro Hpq Hpr)

-- 7ª demostración
example
  (H : p ∨ (q ∧ r))
  : (p ∨ q) ∧ (p ∨ r) :=
or.elim H
  ( assume Hp : p,
    have Hpq : p ∨ q,
      from or.inl Hp,
    have Hpr : p ∨ r,
      from or.inl Hp,
    show (p ∨ q) ∧ (p ∨ r),
      from and.intro Hpq Hpr)
  ( assume Hqr : q ∧ r,
    have Hq : q,
      from and.left Hqr,
    have Hr : r,
      from and.right Hqr,
    have Hpq : p ∨ q,
      from or.inr Hq,
    have Hpr : p ∨ r,
      from or.inr Hr,
    and.intro Hpq Hpr)

-- 8ª demostración
example
  (H : p ∨ (q ∧ r))
  : (p ∨ q) ∧ (p ∨ r) :=
or.elim H
  ( assume Hp : p,
    have Hpq : p ∨ q,
      from or.inl Hp,
    have Hpr : p ∨ r,
      from or.inl Hp,
    show (p ∨ q) ∧ (p ∨ r),
      from and.intro Hpq Hpr)
  ( assume Hqr : q ∧ r,
    have Hq : q,
      from and.left Hqr,
    have Hr : r,
      from and.right Hqr,
    and.intro (or.inr Hq) (or.inr Hr))

-- 9ª demostración
example
  (H : p ∨ (q ∧ r))
  : (p ∨ q) ∧ (p ∨ r) :=
or.elim H
  ( assume Hp : p,
    have Hpq : p ∨ q,
      from or.inl Hp,
    have Hpr : p ∨ r,
      from or.inl Hp,
    show (p ∨ q) ∧ (p ∨ r),
      from and.intro Hpq Hpr)
  ( assume Hqr : q ∧ r,
    and.intro (or.inr (and.left Hqr)) (or.inr (and.right Hqr)))

-- 10ª demostración
example
  (H : p ∨ (q ∧ r))
  : (p ∨ q) ∧ (p ∨ r) :=
or.elim H
  ( assume Hp : p,
    have Hpq : p ∨ q,
      from or.inl Hp,
    have Hpr : p ∨ r,
      from or.inl Hp,
    show (p ∨ q) ∧ (p ∨ r),
      from and.intro Hpq Hpr)
  ( assume Hqr : q ∧ r,
    and.intro (or.inr Hqr.1) (or.inr Hqr.2))

-- 11ª demostración
example
  (H : p ∨ (q ∧ r))
  : (p ∨ q) ∧ (p ∨ r) :=
or.elim H
  ( assume Hp : p,
    have Hpq : p ∨ q,
      from or.inl Hp,
    have Hpr : p ∨ r,
      from or.inl Hp,
    show (p ∨ q) ∧ (p ∨ r),
      from and.intro Hpq Hpr)
  ( assume Hqr : q ∧ r,
    ⟨or.inr Hqr.1, or.inr Hqr.2⟩)

-- 12ª demostración
example
  (H : p ∨ (q ∧ r))
  : (p ∨ q) ∧ (p ∨ r) :=
or.elim H
  ( assume Hp : p,
    have Hpq : p ∨ q,
      from or.inl Hp,
    have Hpr : p ∨ r,
      from or.inl Hp,
    show (p ∨ q) ∧ (p ∨ r),
      from and.intro Hpq Hpr)
  ( λ Hqr, ⟨or.inr Hqr.1, or.inr Hqr.2⟩)

-- 13ª demostración
example
  (H : p ∨ (q ∧ r))
  : (p ∨ q) ∧ (p ∨ r) :=
or.elim H
  ( assume Hp : p,
    have Hpq : p ∨ q,
      from or.inl Hp,
    have Hpr : p ∨ r,
      from or.inl Hp,
    and.intro Hpq Hpr)
  ( λ Hqr, ⟨or.inr Hqr.1, or.inr Hqr.2⟩)

-- 14ª demostración
example
  (H : p ∨ (q ∧ r))
  : (p ∨ q) ∧ (p ∨ r) :=
or.elim H
  ( assume Hp : p,
    have Hpq : p ∨ q,
      from or.inl Hp,
    have Hpr : p ∨ r,
      from or.inl Hp,
    ⟨Hpq, Hpr⟩)
  ( λ Hqr, ⟨or.inr Hqr.1, or.inr Hqr.2⟩)

-- 15ª demostración
example
  (H : p ∨ (q ∧ r))
  : (p ∨ q) ∧ (p ∨ r) :=
or.elim H
  ( assume Hp : p,
    ⟨or.inl Hp, or.inl Hp⟩)
  ( λ Hqr, ⟨or.inr Hqr.1, or.inr Hqr.2⟩)

-- 16ª demostración
example
  (H : p ∨ (q ∧ r))
  : (p ∨ q) ∧ (p ∨ r) :=
or.elim H
  ( λ Hp, ⟨or.inl Hp, or.inl Hp⟩)
  ( λ Hqr, ⟨or.inr Hqr.1, or.inr Hqr.2⟩)

-- 17ª demostración
example
  (H : p ∨ (q ∧ r))
  : (p ∨ q) ∧ (p ∨ r) :=
-- by library_search
or_and_distrib_left.mp H

-- 18ª demostración
example
  (H : p ∨ (q ∧ r))
  : (p ∨ q) ∧ (p ∨ r) :=
-- by hint
by tauto

-- 19ª demostración
example
  (H : p ∨ (q ∧ r))
  : (p ∨ q) ∧ (p ∨ r) :=
by finish

-- ----------------------------------------------------
-- Ejercicio 36. Demostrar
--    (p ∨ q) ∧ (p ∨ r) ⊢ p ∨ (q ∧ r)
-- ----------------------------------------------------

-- 1ª demostración
example
  (H : (p ∨ q) ∧ (p ∨ r))
  : p ∨ (q ∧ r) :=
begin
  cases H with Hpq Hpr,
  cases Hpq with Hp Hq,
  { left,
    exact Hp, },
  { cases Hpr with Hp Hr,
    { left,
      exact Hp, },
    { right,
      split,
      { exact Hq, },
      { exact Hr, }}},
end

-- 2ª demostración
example
  (H : (p ∨ q) ∧ (p ∨ r))
  : p ∨ (q ∧ r) :=
begin
  cases H with Hpq Hpr,
  cases Hpq with Hp Hq,
  { left,
    exact Hp, },
  { cases Hpr with Hp Hr,
    { left,
      exact Hp, },
    { right,
      exact ⟨Hq, Hr⟩, }},
end

-- 3ª demostración
example
  (H : (p ∨ q) ∧ (p ∨ r))
  : p ∨ (q ∧ r) :=
begin
  cases H with Hpq Hpr,
  cases Hpq with Hp Hq,
  { left,
    exact Hp, },
  { cases Hpr with Hp Hr,
    { left,
      exact Hp, },
    { exact or.inr ⟨Hq, Hr⟩, }},
end

-- 4ª demostración
example
  (H : (p ∨ q) ∧ (p ∨ r))
  : p ∨ (q ∧ r) :=
begin
  cases H with Hpq Hpr,
  cases Hpq with Hp Hq,
  { left,
    exact Hp, },
  { cases Hpr with Hp Hr,
    { exact or.inl Hp, },
    { exact or.inr ⟨Hq, Hr⟩, }},
end

-- 5ª demostración
example
  (H : (p ∨ q) ∧ (p ∨ r))
  : p ∨ (q ∧ r) :=
begin
  cases H with Hpq Hpr,
  cases Hpq with Hp Hq,
  { exact or.inl Hp, },
  { cases Hpr with Hp Hr,
    { exact or.inl Hp, },
    { exact or.inr ⟨Hq, Hr⟩, }},
end

-- 6ª demostración
example
  (H : (p ∨ q) ∧ (p ∨ r))
  : p ∨ (q ∧ r) :=
begin
  rcases H with ⟨Hp | Hq, Hp | Hr⟩,
  { exact or.inl Hp, },
  { exact or.inl Hp, },
  { exact or.inl Hp, },
  { exact or.inr ⟨Hq, Hr⟩, },
end

-- 7ª demostración
example
  (H : (p ∨ q) ∧ (p ∨ r))
  : p ∨ (q ∧ r) :=
-- by library_search
or_and_distrib_left.mpr H

-- 8ª demostración
example
  (H : (p ∨ q) ∧ (p ∨ r))
  : p ∨ (q ∧ r) :=
have Hpq : p ∨ q,
  from and.left H,
or.elim Hpq
  ( assume Hp : p,
    show p ∨ (q ∧ r),
      from or.inl Hp )
  ( assume Hq : q,
    have Hpr : p ∨ r,
      from and.right H,
    or.elim Hpr
      ( assume Hp : p,
        show p ∨ (q ∧ r),
          from or.inl Hp )
      ( assume Hr : r,
        have Hqr : q ∧ r,
          from and.intro Hq Hr,
        show p ∨ (q ∧ r),
          from or.inr Hqr ))

-- 9ª demostración
example
  (H : (p ∨ q) ∧ (p ∨ r))
  : p ∨ (q ∧ r) :=
or.elim (and.left H)
  or.inl
  (λ Hq, or.elim (and.right H)
           or.inl
           (λ Hr, or.inr ⟨Hq, Hr⟩))

-- 10ª demostración
example
  (H : (p ∨ q) ∧ (p ∨ r))
  : p ∨ (q ∧ r) :=
-- by hint
by tauto

-- 11ª demostración
example
  (H : (p ∨ q) ∧ (p ∨ r))
  : p ∨ (q ∧ r) :=
by finish

-- ----------------------------------------------------
-- Ejercicio 37. Demostrar
--    (p → r) ∧ (q → r) ⊢ p ∨ q → r
-- ----------------------------------------------------

-- 1ª demostración
example
  (H : (p → r) ∧ (q → r))
  : p ∨ q → r :=
begin
  cases H with Hpr Hqr,
  intro Hpq,
  cases Hpq with Hp Hq,
  { apply Hpr,
    exact Hp, },
  { apply Hqr,
    exact Hq, },
end

-- 2ª demostración
example
  (H : (p → r) ∧ (q → r))
  : p ∨ q → r :=
begin
  cases H with Hpr Hqr,
  intro Hpq,
  cases Hpq with Hp Hq,
  { exact Hpr Hp, },
  { exact Hqr Hq, },
end

-- 3ª demostración
example
  (H : (p → r) ∧ (q → r))
  : p ∨ q → r :=
begin
  intro Hpq,
  cases Hpq with Hp Hq,
  { exact H.left  Hp, },
  { exact H.right Hq, },
end

-- 4ª demostración
example
  (H : (p → r) ∧ (q → r))
  : p ∨ q → r :=
-- by library_search
or_imp_distrib.mpr H

-- 5ª demostración
example
  (H : (p → r) ∧ (q → r))
  : p ∨ q → r :=
assume Hpq : p ∨ q,
or.elim Hpq
  ( assume Hp : p,
    have Hpr: p → r,
      from and.left H,
    show r,
      from Hpr Hp )
  ( assume Hq : q,
    have Hqr : q → r,
      from and.right H,
    show r,
      from Hqr Hq)

-- 6ª demostración
example
  (H : (p → r) ∧ (q → r))
  : p ∨ q → r :=
assume Hpq : p ∨ q,
or.elim Hpq
  ( assume Hp : p,
    have Hpr: p → r,
      from and.left H,
    Hpr Hp )
  ( assume Hq : q,
    have Hqr : q → r,
      from and.right H,
    Hqr Hq)

-- 7ª demostración
example
  (H : (p → r) ∧ (q → r))
  : p ∨ q → r :=
assume Hpq : p ∨ q,
or.elim Hpq
  ( assume Hp : p,
    H.1 Hp )
  ( assume Hq : q,
    H.2 Hq)

-- 8ª demostración
example
  (H : (p → r) ∧ (q → r))
  : p ∨ q → r :=
assume Hpq : p ∨ q,
or.elim Hpq
  (λ Hp, H.1 Hp)
  (λ Hq, H.2 Hq)

-- 9ª demostración
example
  (H : (p → r) ∧ (q → r))
  : p ∨ q → r :=
assume Hpq : p ∨ q,
or.elim Hpq H.1 H.2

-- 10ª demostración
example
  (H : (p → r) ∧ (q → r))
  : p ∨ q → r :=
λ Hpq, or.elim Hpq H.1 H.2

-- 11ª demostración
example
  (H : (p → r) ∧ (q → r))
  : p ∨ q → r :=
-- by hint
by tauto

-- 12ª demostración
example
  (H : (p → r) ∧ (q → r))
  : p ∨ q → r :=
by finish

-- ----------------------------------------------------
-- Ejercicio 38. Demostrar
--    p ∨ q → r ⊢ (p → r) ∧ (q → r)
-- ----------------------------------------------------

-- 1ª demostración
example
  (H : p ∨ q → r)
  : (p → r) ∧ (q → r) :=
begin
  split,
  { intro Hp,
    apply H,
    left,
    exact Hp, },
  { intro Hq,
    apply H,
    right,
    exact Hq, },
end

-- 2ª demostración
example
  (H : p ∨ q → r)
  : (p → r) ∧ (q → r) :=
begin
  split,
  { intro Hp,
    apply H,
    exact or.inl Hp, },
  { intro Hq,
    apply H,
    exact or.inr Hq, },
end

-- 3ª demostración
example
  (H : p ∨ q → r)
  : (p → r) ∧ (q → r) :=
begin
  split,
  { intro Hp,
    exact H (or.inl Hp), },
  { intro Hq,
    exact H (or.inr Hq), },
end

-- 4ª demostración
example
  (H : p ∨ q → r)
  : (p → r) ∧ (q → r) :=
⟨λ Hp, H (or.inl Hp),
 λ Hq, H (or.inr Hq)⟩

-- 5ª demostración
example
  (H : p ∨ q → r)
  : (p → r) ∧ (q → r) :=
-- by library_search
or_imp_distrib.mp H

-- 6ª demostración
example
  (H : p ∨ q → r)
  : (p → r) ∧ (q → r) :=
and.intro
  ( assume Hp : p,
    have Hpq : p ∨ q,
      from or.inl Hp,
    show r,
      from H Hpq)
  ( assume Hq : q,
    have Hpq : p ∨ q,
      from or.inr Hq,
    show r,
      from H Hpq)

-- 7ª demostración
example
  (H : p ∨ q → r)
  : (p → r) ∧ (q → r) :=
and.intro
  ( assume Hp : p,
    have Hpq : p ∨ q,
      from or.inl Hp,
    H Hpq)
  ( assume Hq : q,
    have Hpq : p ∨ q,
      from or.inr Hq,
    H Hpq)

-- 8ª demostración
example
  (H : p ∨ q → r)
  : (p → r) ∧ (q → r) :=
and.intro
  ( assume Hp : p,
    H (or.inl Hp))
  ( assume Hq : q,
    H (or.inr Hq))

-- 9ª demostración
example
  (H : p ∨ q → r)
  : (p → r) ∧ (q → r) :=
and.intro
  (λ Hp, H (or.inl Hp))
  (λ Hq, H (or.inr Hq))

-- 10ª demostración
example
  (H : p ∨ q → r)
  : (p → r) ∧ (q → r) :=
⟨λ Hp, H (or.inl Hp),
 λ Hq, H (or.inr Hq)⟩

-- § Negación
-- ==========

-- ----------------------------------------------------
-- Ejercicio 39. Demostrar
--    p ⊢ ¬¬p
-- ----------------------------------------------------

-- 1ª demostración
example
  (H : p)
  : ¬¬p :=
begin
  intro H1,
  apply H1 H,
end

-- 2ª demostración
example
  (H : p)
  : ¬¬p :=
λ H1, H1 H

-- 3ª demostración
example
  (H : p)
  : ¬¬p :=
-- by library_search
not_not.mpr H

-- 4ª demostración
example
  (H : p)
  : ¬¬p :=
assume H1 : ¬p,
show false,
  from H1 H

-- 5ª demostración
example
  (H : p)
  : ¬¬p :=
-- by hint
by tauto

-- 6ª demostración
example
  (H : p)
  : ¬¬p :=
by finish

-- ----------------------------------------------------
-- Ejercicio 40. Demostrar
--    ¬p ⊢ p → q
-- ----------------------------------------------------

-- 1ª demostración
example
  (H : ¬p)
  : p → q :=
begin
  intro Hp,
  exfalso,
  apply H,
  exact Hp,
end

-- 2ª demostración
example
  (H : ¬p)
  : p → q :=
begin
  intro Hp,
  exfalso,
  exact H Hp,
end

-- 3ª demostración
example
  (H : ¬p)
  : p → q :=
begin
  intro Hp,
  exact absurd Hp H,
end

-- 4ª demostración
example
  (H : ¬p)
  : p → q :=
λ Hp, absurd Hp H

-- 5ª demostración
example
  (H : ¬p)
  : p → q :=
-- by library_search
not.elim H

-- 6ª demostración
example
  (H : ¬p)
  : p → q :=
assume Hp : p,
show q,
  from absurd Hp H

-- ----------------------------------------------------
-- Ejercicio 41. Demostrar
--    p → q ⊢ ¬q → ¬p
-- ----------------------------------------------------

-- 1ª demostración
example
  (H : p → q)
  : ¬q → ¬p :=
begin
  intro Hnq,
  intro Hp,
  apply Hnq,
  exact H Hp,
end

-- 2ª demostración
example
  (H : p → q)
  : ¬q → ¬p :=
begin
  intro Hnq,
  intro Hp,
  exact Hnq (H Hp),
end

-- 3ª demostración
example
  (H : p → q)
  : ¬q → ¬p :=
begin
  intros Hnq Hp,
  exact Hnq (H Hp),
end

-- 4ª demostración
example
  (H : p → q)
  : ¬q → ¬p :=
λ Hnq Hp, Hnq (H Hp)

-- 5ª demostración
example
  (H : p → q)
  : ¬q → ¬p :=
-- by library_search
mt H

-- 6ª demostración
example
  (H : p → q)
  : ¬q → ¬p :=
assume Hnq : ¬q,
assume Hp : p,
have Hq : q,
  from H Hp,
show false,
  from Hnq Hq

-- 7ª demostración
example
  (H : p → q)
  : ¬q → ¬p :=
assume Hnq : ¬q,
assume Hp : p,
have Hq : q,
  from H Hp,
Hnq Hq

-- 8ª demostración
example
  (H : p → q)
  : ¬q → ¬p :=
assume Hnq : ¬q,
assume Hp : p,
Hnq (H Hp)

-- 9ª demostración
example
  (H : p → q)
  : ¬q → ¬p :=
assume Hnq : ¬q,
λ Hp, Hnq (H Hp)

-- 10ª demostración
example
  (H : p → q)
  : ¬q → ¬p :=
λ Hnq Hp, Hnq (H Hp)

-- 11ª demostración
example
  (H : p → q)
  : ¬q → ¬p :=
-- by hint
by tauto

-- 12ª demostración
example
  (H : p → q)
  : ¬q → ¬p :=
by finish

-- ----------------------------------------------------
-- Ejercicio 42. Demostrar
--    p ∨ q, ¬q ⊢ p
-- ----------------------------------------------------

-- 1ª demostración
example
  (Hpq : p ∨ q)
  (Hnq : ¬q)
  : p :=
begin
  cases Hpq with Hp Hq,
  { exact Hp, },
  { exact absurd Hq Hnq, },
end

-- 2ª demostración
example
  (Hpq : p ∨ q)
  (Hnq : ¬q)
  : p :=
-- by library_search
or.resolve_right Hpq Hnq

-- 3ª demostración
example
  (Hpq : p ∨ q)
  (Hnq : ¬q)
  : p :=
or.elim Hpq
  ( assume Hp : p,
    show p,
      from Hp)
  ( assume Hq : q,
    show p,
      from absurd Hq Hnq)

-- 4ª demostración
example
  (Hpq : p ∨ q)
  (Hnq : ¬q)
  : p :=
or.elim Hpq
  ( assume Hp : p,
    show p,
      from Hp)
  ( assume Hq : q,
    absurd Hq Hnq)

-- 5ª demostración
example
  (Hpq : p ∨ q)
  (Hnq : ¬q)
  : p :=
or.elim Hpq
  ( assume Hp : p,
    show p,
      from Hp)
  ( λ Hq, absurd Hq Hnq)

-- 6ª demostración
example
  (Hpq : p ∨ q)
  (Hnq : ¬q)
  : p :=
or.elim Hpq
  ( assume Hp : p,
    Hp)
  ( λ Hq, absurd Hq Hnq)

-- 7ª demostración
example
  (Hpq : p ∨ q)
  (Hnq : ¬q)
  : p :=
or.elim Hpq id (λ Hq, absurd Hq Hnq)

-- 8ª demostración
example
  (Hpq : p ∨ q)
  (Hnq : ¬q)
  : p :=
-- by hint
by tauto

-- 9ª demostración
example
  (Hpq : p ∨ q)
  (Hnq : ¬q)
  : p :=
by finish

-- ----------------------------------------------------
-- Ejercicio 43. Demostrar
--    p ∨ q, ¬p ⊢ q
-- ----------------------------------------------------

-- 1ª demostración
example
  (Hpq : p ∨ q)
  (Hnp: ¬p)
  : q :=
begin
  cases Hpq with Hp Hq,
  { exact absurd Hp Hnp, },
  { exact Hq, },
end

-- 2ª demostración
example
  (Hpq : p ∨ q)
  (Hnp: ¬p)
  : q :=
or.elim Hpq (λ Hp, absurd Hp Hnp) id

-- 3ª demostración
example
  (Hpq : p ∨ q)
  (Hnp: ¬p)
  : q :=
-- by library_search
or.resolve_left Hpq Hnp

-- 4ª demostración
example
  (Hpq : p ∨ q)
  (Hnp: ¬p)
  : q :=
or.elim Hpq
  ( assume Hp : p,
    show q,
      from absurd Hp Hnp)
  ( assume Hq : q,
    show q,
      from Hq)

-- 5ª demostración
example
  (Hpq : p ∨ q)
  (Hnp: ¬p)
  : q :=
-- by hint
by tauto

-- 6ª demostración
example
  (Hpq : p ∨ q)
  (Hnp: ¬p)
  : q :=
by finish

-- ----------------------------------------------------
-- Ejercicio 44. Demostrar
--    p ∨ q ⊢ ¬(¬p ∧ ¬q)
-- ----------------------------------------------------

-- 1ª demostración
example
  (H : p ∨ q)
  : ¬(¬p ∧ ¬q) :=
begin
  intro H1,
  cases H1 with H2 H3,
  cases H with H4 H5,
  { exact H2 H4, },
  { exact H3 H5, },
end

-- 2ª demostración
example
  (H : p ∨ q)
  : ¬(¬p ∧ ¬q) :=
begin
  rintro ⟨H2, H3⟩,
  cases H with H4 H5,
  { exact H2 H4, },
  { exact H3 H5, },
end

-- 3ª demostración
example
  (H : p ∨ q)
  : ¬(¬p ∧ ¬q) :=
λ ⟨H2, H3⟩, or.elim H (λ H4, H2 H4) (λ H5, H3 H5)

-- 4ª demostración
example
  (H : p ∨ q)
  : ¬(¬p ∧ ¬q) :=
-- by library_search
or_iff_not_and_not.mp H

-- 5ª demostración
example
  (H : p ∨ q)
  : ¬(¬p ∧ ¬q) :=
assume H3 : ¬p ∧ ¬q,
or.elim H
  ( assume Hp : p,
    show false,
      from absurd Hp (and.left H3))
  ( assume Hq : q,
    show false,
      from absurd Hq (and.right H3))

-- 6ª demostración
example
  (H : p ∨ q)
  : ¬(¬p ∧ ¬q) :=
-- by hint
by tauto

-- 7ª demostración
example
  (H : p ∨ q)
  : ¬(¬p ∧ ¬q) :=
by finish

-- ----------------------------------------------------
-- Ejercicio 45. Demostrar
--    p ∧ q ⊢ ¬(¬p ∨ ¬q)
-- ----------------------------------------------------

-- 1ª demostración
example
  (H : p ∧ q)
  : ¬(¬p ∨ ¬q) :=
begin
  intro H1,
  cases H1 with H2 H3,
  { apply H2,
    exact H.left, },
  { apply H3,
    exact H.right, },
end

-- 2ª demostración
example
  (H : p ∧ q)
  : ¬(¬p ∨ ¬q) :=
begin
  intro H1,
  cases H1 with H2 H3,
  { exact H2 H.left, },
  { exact H3 H.right, },
end

-- 3ª demostración
example
  (H : p ∧ q)
  : ¬(¬p ∨ ¬q) :=
λ H1, or.elim H1 (λ H2, H2 H.1) (λ H3, H3 H.2)

-- 4ª demostración
example
  (H : p ∧ q)
  : ¬(¬p ∨ ¬q) :=
begin
  rintro (H2 | H3),
  { exact H2 H.left, },
  { exact H3 H.right, },
end

-- 5ª demostración
example
  (H : p ∧ q)
  : ¬(¬p ∨ ¬q) :=
-- by library_search
and_iff_not_or_not.mp H

-- 6ª demostración
example
  (H : p ∧ q)
  : ¬(¬p ∨ ¬q) :=
-- by hint
by tauto

-- 7ª demostración
example
  (H : p ∧ q)
  : ¬(¬p ∨ ¬q) :=
by finish

-- ----------------------------------------------------
-- Ejercicio 46. Demostrar
--    ¬(p ∨ q) ⊢ ¬p ∧ ¬q
-- ----------------------------------------------------

-- 1ª demostración
example
  (H : ¬(p ∨ q))
  : ¬p ∧ ¬q :=
begin
  split,
  { intro Hp,
    apply H,
    exact or.inl Hp, },
  { intro Hq,
    apply H,
    exact or.inr Hq, },
end

-- 2ª demostración
example
  (H : ¬(p ∨ q))
  : ¬p ∧ ¬q :=
begin
  split,
  { intro Hp,
    exact H (or.inl Hp), },
  { intro Hq,
    exact H (or.inr Hq), },
end

-- 3ª demostración
example
  (H : ¬(p ∨ q))
  : ¬p ∧ ¬q :=
⟨ λ Hp, H (or.inl Hp),
  λ Hq, H (or.inr Hq)⟩

-- 4ª demostración
example
  (H : ¬(p ∨ q))
  : ¬p ∧ ¬q :=
-- by library_search
not_or_distrib.mp H

-- 5ª demostración
example
  (H : ¬(p ∨ q))
  : ¬p ∧ ¬q :=
have H1 : ¬p, from
  assume Hp : p,
  have H2: p ∨ q,
    from or.inl Hp,
  show false,
    from absurd H2 H,
have H3 : ¬q, from
  assume Hq : q,
  have H4: p ∨ q,
    from or.inr Hq,
  show false,
    from absurd H4 H,
show ¬p ∧ ¬q,
  from and.intro H1 H3

-- 6ª demostración
example
  (H : ¬(p ∨ q))
  : ¬p ∧ ¬q :=
-- by hint
by tauto

-- 7ª demostración
example
  (H : ¬(p ∨ q))
  : ¬p ∧ ¬q :=
by finish

-- ----------------------------------------------------
-- Ejercicio 47. Demostrar
--    ¬p ∧ ¬q ⊢ ¬(p ∨ q)
-- ----------------------------------------------------

-- 1ª demostración
example
  (H : ¬p ∧ ¬q)
  : ¬(p ∨ q) :=
begin
  intro H1,
  cases H1 with H2 H3,
  { exact absurd H2 H.1, },
  { exact absurd H3 H.2, },
end

-- 2ª demostración
example
  (H : ¬p ∧ ¬q)
  : ¬(p ∨ q) :=
λ H1, or.elim H1 (λ H2, absurd H2 H.1) (λ H3, absurd H3 H.2)

-- 3ª demostración
example
  (H : ¬p ∧ ¬q)
  : ¬(p ∨ q) :=
-- by library_search
not_or_distrib.mpr H

-- 4ª demostración
example
  (H : ¬p ∧ ¬q)
  : ¬(p ∨ q) :=
assume Hpq : p ∨ q,
or.elim Hpq
  ( assume Hp : p,
    show false,
      from absurd Hp H.left)
  ( assume Hq : q,
    show false,
      from absurd Hq H.right)

-- 5ª demostración
example
  (H : ¬p ∧ ¬q)
  : ¬(p ∨ q) :=
-- by hint
by tauto

-- 6ª demostración
example
  (H : ¬p ∧ ¬q)
  : ¬(p ∨ q) :=
by finish

-- ----------------------------------------------------
-- Ejercicio 48. Demostrar
--    ¬p ∨ ¬q ⊢ ¬(p ∧ q)
-- ----------------------------------------------------

-- 1ª demostración
example
  (H : ¬p ∨ ¬q)
  : ¬(p ∧ q) :=
begin
  intro Hpq,
  cases H with Hnp Hnq,
  { apply Hnp,
    exact Hpq.left, },
  { apply Hnq,
    exact Hpq.right, },
end

-- 2ª demostración
example
  (H : ¬p ∨ ¬q)
  : ¬(p ∧ q) :=
begin
  intro Hpq,
  cases H with Hnp Hnq,
  { exact Hnp Hpq.1, },
  { exact Hnq Hpq.2, },
end

-- 3ª demostración
example
  (H : ¬p ∨ ¬q)
  : ¬(p ∧ q) :=
begin
  intro Hpq,
  exact or.elim H (λ Hnp, Hnp Hpq.1) (λ Hnq, Hnq Hpq.2),
end

-- 4ª demostración
example
  (H : ¬p ∨ ¬q)
  : ¬(p ∧ q) :=
λ Hpq, or.elim H (λ Hnp, Hnp Hpq.1) (λ Hnq, Hnq Hpq.2)

-- 5ª demostración
example
  (H : ¬p ∨ ¬q)
  : ¬(p ∧ q) :=
-- by library_search
not_and_distrib.mpr H

-- 6ª demostración
example
  (H : ¬p ∨ ¬q)
  : ¬(p ∧ q) :=
assume Hpq : p ∧ q,
or.elim H
  ( assume Hnp : ¬p,
    show false,
      from Hnp (and.left Hpq))
  ( assume Hnq : ¬q,
    show false,
      from Hnq (and.right Hpq))

-- 7ª demostración
example
  (H : ¬p ∨ ¬q)
  : ¬(p ∧ q) :=
-- by hint
by tauto

-- 8ª demostración
example
  (H : ¬p ∨ ¬q)
  : ¬(p ∧ q) :=
by finish

-- ----------------------------------------------------
-- Ejercicio 49. Demostrar
--    ⊢ ¬(p ∧ ¬p)
-- ----------------------------------------------------

-- 1ª demostración
example :
  ¬(p ∧ ¬p) :=
begin
  intro H,
  apply H.right,
  exact H.left,
end

-- 2ª demostración
example :
  ¬(p ∧ ¬p) :=
begin
  intro H,
  exact H.right (H.left),
end

-- 3ª demostración
example :
  ¬(p ∧ ¬p) :=
λ H, H.right (H.left)

-- 4ª demostración
example :
  ¬(p ∧ ¬p) :=
begin
  rintro ⟨H1, H2⟩,
  exact H2 H1,
end

-- 5ª demostración
example :
  ¬(p ∧ ¬p) :=
λ ⟨H1, H2⟩, H2 H1

-- 6ª demostración
example :
  ¬(p ∧ ¬p) :=
-- by suggest
(and_not_self p).mp

-- 7ª demostración
example :
  ¬(p ∧ ¬p) :=
assume H : p ∧ ¬p,
have H1 : p,
  from and.left H,
have H2 : ¬p,
  from and.right H,
show false,
  from H2 H1

-- 8ª demostración
example :
  ¬(p ∧ ¬p) :=
-- by hint
by tauto

-- 9ª demostración
example :
  ¬(p ∧ ¬p) :=
by finish

-- 10ª demostración
example :
  ¬(p ∧ ¬p) :=
by simp

-- ----------------------------------------------------
-- Ejercicio 50. Demostrar
--    p ∧ ¬p ⊢ q
-- ----------------------------------------------------

-- 1ª demostración
example
  (H : p ∧ ¬p)
  : q :=
begin
  exfalso,
  apply H.2,
  exact H.1,
end

-- 2ª demostración
example
  (H : p ∧ ¬p)
  : q :=
begin
  exfalso,
  exact H.2 H.1,
end

-- 3ª demostración
example
  (H : p ∧ ¬p)
  : q :=
false.elim (H.2 H.1)

-- 4ª demostración
example
  (H : p ∧ ¬p)
  : q :=
have Hp : p,
  from and.left H,
have Hnp : ¬p,
  from and.right H,
have Hf : false,
  from Hnp Hp,
show q,
  from false.elim Hf

-- 5ª demostración
example
  (H : p ∧ ¬p)
  : q :=
-- by hint
by tauto

-- 6ª demostración
example
  (H : p ∧ ¬p)
  : q :=
by finish

-- ----------------------------------------------------
-- Ejercicio 51. Demostrar
--    ¬¬p ⊢ p
-- ----------------------------------------------------

open_locale classical

-- 1ª demostración
example
  (h1 : ¬¬p)
  : p :=
by_contra
  ( assume h2 : ¬p,
    show false,
      from h1 h2 )

-- 2ª demostración
example
  (h1 : ¬¬p)
  : p :=
by_contra
  ( assume h2 : ¬p,
    h1 h2 )

-- 3ª demostración
example
  (h1 : ¬¬p)
  : p :=
by_contra (λ h2, h1 h2)

-- 4ª demostración
example
  (h1 : ¬¬p)
  : p :=
-- by library_search
not_not.mp h1

-- 5ª demostración
example
  (h1 : ¬¬p)
  : p :=
begin
  by_contradiction h2,
  exact h1 h2,
end

-- 6ª demostración
example
  (h1 : ¬¬p)
  : p :=
-- by hint
by tauto

-- 7ª demostración
lemma aux
  (h1 : ¬¬p)
  : p :=
by finish

-- ----------------------------------------------------
-- Ejercicio 52. Demostrar
--    ⊢ p ∨ ¬p
-- ----------------------------------------------------

-- 1ª demostración
example : p ∨ ¬p :=
by_contradiction
  ( assume h1 : ¬(p ∨ ¬p),
    have h2 : ¬p, from
      assume h3 : p,
      have h4 : p ∨ ¬p, from or.inl h3,
      show false, from h1 h4,
    have h5 : p ∨ ¬p, from or.inr h2,
    show false, from h1 h5 )

-- 2ª demostración
example : p ∨ ¬p :=
by_contradiction
  ( assume h1 : ¬(p ∨ ¬p),
    have h2 : ¬p, from
      assume h3 : p,
      have h4 : p ∨ ¬p, from or.inl h3,
      show false, from h1 h4,
    have h5 : p ∨ ¬p, from or.inr h2,
    h1 h5 )

-- 3ª demostración
example : p ∨ ¬p :=
by_contradiction
  ( assume h1 : ¬(p ∨ ¬p),
    have h2 : ¬p, from
      assume h3 : p,
      have h4 : p ∨ ¬p, from or.inl h3,
      show false, from h1 h4,
    h1 (or.inr h2) )

-- 4ª demostración
example : p ∨ ¬p :=
by_contradiction
  ( assume h1 : ¬(p ∨ ¬p),
    have h2 : ¬p, from
      assume h3 : p,
      have h4 : p ∨ ¬p, from or.inl h3,
      h1 h4,
    h1 (or.inr h2) )

-- 5ª demostración
example : p ∨ ¬p :=
by_contradiction
  ( assume h1 : ¬(p ∨ ¬p),
    have h2 : ¬p, from
      assume h3 : p,
      h1 (or.inl h3),
    h1 (or.inr h2) )

-- 6ª demostración
example : p ∨ ¬p :=
by_contradiction
  ( assume h1 : ¬(p ∨ ¬p),
    have h2 : ¬p, from
      λ h3, h1 (or.inl h3),
    h1 (or.inr h2) )

-- 7ª demostración
example : p ∨ ¬p :=
by_contradiction
  ( assume h1 : ¬(p ∨ ¬p),
    h1 (or.inr (λ h3, h1 (or.inl h3))) )

-- 8ª demostración
example : p ∨ ¬p :=
by_contradiction
  ( λ h1, h1 (or.inr (λ h3, h1 (or.inl h3))) )

-- 9ª demostración
example : p ∨ ¬p :=
-- by library_search
em p

-- #print axioms em

-- 10ª demostración
example : p ∨ ¬p :=
begin
  by_contra h1,
  apply h1,
  apply or.inr,
  intro h2,
  apply h1,
  exact or.inl h2,
end

-- 11ª demostración
example : p ∨ ¬p :=
begin
  by_contra h1,
  apply h1,
  apply or.inr,
  intro h2,
  exact h1 (or.inl h2),
end

-- 12ª demostración
example : p ∨ ¬p :=
begin
  by_contra h1,
  apply h1,
  apply or.inr,
  exact λ h2, h1 (or.inl h2),
end

-- 13ª demostración
example : p ∨ ¬p :=
begin
  by_contra h1,
  apply h1,
  exact or.inr (λ h2, h1 (or.inl h2)),
end

-- 14ª demostración
example : p ∨ ¬p :=
begin
  by_contra h1,
  exact h1 (or.inr (λ h2, h1 (or.inl h2))),
end

-- 15ª demostración
example : p ∨ ¬p :=
by_contra (λ h1, h1 (or.inr (λh2, h1 (or.inl h2))))

-- 16ª demostración
example : p ∨ ¬p :=
begin
  by_contra h1,
  apply h1,
  right,
  intro h2,
  apply h1,
  left,
  exact h2,
end

-- 17ª demostración
example : p ∨ ¬p :=
-- by hint
by tauto

-- 18ª demostración
example : p ∨ ¬p :=
by finish

-- ----------------------------------------------------
-- Ejercicio 53. Demostrar
--    ⊢ ((p → q) → p) → p
-- ----------------------------------------------------

-- 1ª demostración
example :
  ((p → q) → p) → p :=
begin
  intro h1,
  by_cases h2 : p → q,
  { exact h1 h2, },
  { by_contra h3,
    apply h2,
    intro h4,
    exfalso,
    exact h3 h4, },
end

-- 2ª demostración
example :
  ((p → q) → p) → p :=
begin
  by_cases hp : p,
  { intro h1,
    exact hp, },
  { intro h2,
    exact h2 hp.elim, },
end

-- 3ª demostración
example :
  ((p → q) → p) → p :=
if hp : p then λ h, hp else λ h, h hp.elim

-- 4ª demostración
example :
  ((p → q) → p) → p :=
-- by library_search
peirce p q

-- 5ª demostración
example :
  ((p → q) → p) → p :=
assume h1 : (p → q) → p,
show p, from
  by_contradiction
    ( assume h2 : ¬p,
      have h3 : ¬(p → q),
        by exact mt h1 h2,
      have h4 : p → q, from
        assume h5 : p,
        show q,
          from not.elim h2 h5,
      show false,
        from h3 h4)

-- 6ª demostración
example :
  ((p → q) → p) → p :=
-- by hint
by tauto

-- 7ª demostración
example :
  ((p → q) → p) → p :=
by finish

-- ----------------------------------------------------
-- Ejercicio 54. Demostrar
--    ¬q → ¬p ⊢ p → q
-- ----------------------------------------------------

-- 1ª demostración
example
  (H : ¬q → ¬p)
  : p → q :=
begin
  intro Hp,
  by_contra Hnq,
  apply not.elim _ Hp,
  exact H Hnq,
end

-- 2ª demostración
example
  (H : ¬q → ¬p)
  : p → q :=
-- by library_search
not_imp_not.mp H

-- 3ª demostración
example
  (H : ¬q → ¬p)
  : p → q :=
assume Hp : p,
show q, from
  by_contradiction
    ( assume Hnq : ¬q,
      have Hnp : ¬p,
        from H Hnq,
      show false,
        from Hnp Hp )

-- 4ª demostración
example
  (H : ¬q → ¬p)
  : p → q :=
-- by hint
by tauto

-- 5ª demostración
example
  (H : ¬q → ¬p)
  : p → q :=
by finish

-- ----------------------------------------------------
-- Ejercicio 55. Demostrar
--    ¬(¬p ∧ ¬q) ⊢ p ∨ q
-- ----------------------------------------------------

-- 1ª demostración
example
  (H : ¬(¬p ∧ ¬q))
  : p ∨ q :=
begin
  by_cases Hp : p,
  { exact or.inl Hp, },
  { by_cases Hq : q,
    { exact or.inr Hq, },
    { exfalso,
      apply H,
      exact and.intro Hp Hq, }},
end

-- 2ª demostración
example
  (H : ¬(¬p ∧ ¬q))
  : p ∨ q :=
-- by library_search
or_iff_not_and_not.mpr H

-- 3ª demostración
example
  (H : ¬(¬p ∧ ¬q))
  : p ∨ q :=
or.elim (em p)
  ( assume Hp : p,
    show p ∨ q ,
      from or.inl Hp)
  ( assume Hnp : ¬p,
    show p ∨ q, from
      or.elim (em q)
        ( assume Hq : q,
          show p ∨ q,
            from or.inr Hq)
        ( assume Hnq : ¬q,
          have H' : ¬p ∧ ¬q,
            from and.intro Hnp Hnq,
          show p ∨ q,
            from not.elim H H'))

-- 4ª demostración
example
  (H : ¬(¬p ∧ ¬q))
  : p ∨ q :=
or.elim (em p)
  or.inl
  (λ Hnp, or.elim (em q)
            or.inr
            (λ Hnq, not.elim H (and.intro Hnp Hnq)))

-- 5ª demostración
example
  (H : ¬(¬p ∧ ¬q))
  : p ∨ q :=
-- by hint
by tauto

-- 6ª demostración
example
  (H : ¬(¬p ∧ ¬q))
  : p ∨ q :=
by finish

-- ----------------------------------------------------
-- Ejercicio 56. Demostrar
--    ¬(¬p ∨ ¬q) ⊢ p ∧ q
-- ----------------------------------------------------

-- 1ª demostración
example
  (H : ¬(¬p ∨ ¬q))
  : p ∧ q :=
begin
  split,
  { by_contra Hnp,
    apply H,
    exact or.inl Hnp, },
  { by_contra Hnq,
    apply H,
    exact or.inr Hnq, },
end

-- 2ª demostración
example
  (H : ¬(¬p ∨ ¬q))
  : p ∧ q :=
begin
  split,
  { by_contra Hnp,
    exact H (or.inl Hnp), },
  { by_contra Hnq,
    exact H (or.inr Hnq), },
end

-- 3ª demostración
example
  (H : ¬(¬p ∨ ¬q))
  : p ∧ q :=
begin
  split,
  { exact by_contra (λ Hnp, H (or.inl Hnp)), },
  { exact by_contra (λ Hnq, H (or.inr Hnq)), },
end

-- 4ª demostración
example
  (H : ¬(¬p ∨ ¬q))
  : p ∧ q :=
⟨by_contra (λ Hnp, H (or.inl Hnp)),
 by_contra (λ Hnq, H (or.inr Hnq))⟩

-- 5ª demostración
example
  (H : ¬(¬p ∨ ¬q))
  : p ∧ q :=
-- by library_search
and_iff_not_or_not.mpr H

-- 6ª demostración
example
  (H : ¬(¬p ∨ ¬q))
  : p ∧ q :=
and.intro
  ( show p, from by_contradiction
      ( assume Hnp : ¬p,
        have H' : ¬p ∨ ¬ q,
          from or.inl Hnp,
        show false,
          from H H'))
  ( show q, from by_contradiction
      ( assume Hnq : ¬q,
        have H' : ¬p ∨ ¬ q,
          from or.inr Hnq,
        show false,
          from H H'))

-- 7ª demostración
example
  (H : ¬(¬p ∨ ¬q))
  : p ∧ q :=
-- by hint
by tauto

-- 8ª demostración
example
  (H : ¬(¬p ∨ ¬q))
  : p ∧ q :=
by finish

-- ----------------------------------------------------
-- Ejercicio 57. Demostrar
--    ¬(p ∧ q) ⊢ ¬p ∨ ¬q
-- ----------------------------------------------------

-- 1ª demostración
example
  (H : ¬(p ∧ q))
  : ¬p ∨ ¬q :=
begin
  by_cases Hp : p,
  { by_cases Hq : q,
    { exfalso,
      apply H,
      exact ⟨Hp, Hq⟩, },
    { exact or.inr Hq, }},
  { exact or.inl Hp, },
end

-- 2ª demostración
example
  (H : ¬(p ∧ q))
  : ¬p ∨ ¬q :=
if Hp : p
then if Hq : q
     then not.elim H ⟨Hp, Hq⟩
     else or.inr Hq
else or.inl Hp

-- 3ª demostración
example
  (H : ¬(p ∧ q))
  : ¬p ∨ ¬q :=
-- by library_search
not_and_distrib.mp H

-- 4ª demostración
example
  (H : ¬(p ∧ q))
  : ¬p ∨ ¬q :=
or.elim (em p)
  ( assume Hp : p,
    or.elim (em q)
      ( assume Hq : q,
        show ¬p ∨ ¬q,
          from not.elim H ⟨Hp, Hq⟩)
      ( assume Hnq : ¬q,
        show ¬p ∨ ¬q,
          from or.inr Hnq))
  ( assume Hnp : ¬p,
    show ¬p ∨ ¬q,
      from or.inl Hnp)

-- 5ª demostración
example
  (H : ¬(p ∧ q))
  : ¬p ∨ ¬q :=
or.elim (em p)
  (λ Hp, or.elim (em q)
           (λ Hq, not.elim H ⟨Hp, Hq⟩)
           or.inr)
  or.inl

-- 6ª demostración
example
  (H : ¬(p ∧ q))
  : ¬p ∨ ¬q :=
-- by hint
by tauto

-- 7ª demostración
example
  (H : ¬(p ∧ q))
  : ¬p ∨ ¬q :=
by finish

-- ----------------------------------------------------
-- Ejercicio 58. Demostrar
--    ⊢ (p → q) ∨ (q → p)
-- ----------------------------------------------------

-- 1ª demostración
example :
  (p → q) ∨ (q → p) :=
begin
  by_cases H1 : p,
  { right,
    intro,
    exact H1, },
  { left,
    intro H2,
    exfalso,
    exact H1 H2, },
end

-- 2ª demostración
example :
  (p → q) ∨ (q → p) :=
begin
  cases (em p) with Hp Hnp,
  { exact or.inr (λ Hq, Hp), },
  { exact or.inl (λ Hp, not.elim Hnp Hp), },
end

-- 3ª demostración
example :
  (p → q) ∨ (q → p) :=
or.elim (em p)
  (λ Hp, or.inr (λ Hq, Hp))
  (λ Hnp, or.inl (λ Hp, not.elim Hnp Hp))

-- 4ª demostración
example :
  (p → q) ∨ (q → p) :=
if Hp : p
   then or.inr (λ _, Hp)
   else or.inl (λ H, not.elim Hp H)

-- 5ª demostración
example :
  (p → q) ∨ (q → p) :=
-- by hint
by tauto

-- 6ª demostración
example :
  (p → q) ∨ (q → p) :=
by finish