variable (p q r : Prop)

-- commutativity of ∧ and ∨
example : p ∧ q ↔ q ∧ p :=
  Iff.intro
    (fun h : p ∧ q => And.intro (And.right h) (And.left h))
    (fun h : q ∧ p => And.intro (And.right h) (And.left h))

example : p ∨ q ↔ q ∨ p :=
  have hpq : p ∨ q → q ∨ p :=
    (fun h : p ∨ q =>
      Or.elim h
        (fun hp : p => Or.inr hp)
        (fun hq : q => Or.inl hq))
  have hqp : q ∨ p → p ∨ q :=
    (fun h : q ∨ p =>
      Or.elim h
        (fun hq : q => Or.inr hq)
        (fun hp : p => Or.inl hp))
  Iff.intro hpq hqp

-- associativity of ∧ and ∨
example : (p ∧ q) ∧ r ↔ p ∧ (q ∧ r) :=
  Iff.intro
    (fun h_lhs : (p ∧ q) ∧ r =>
      And.intro (And.left (And.left h_lhs)) (And.intro (And.right (And.left h_lhs)) (And.right h_lhs)))
    (fun h_rhs : p ∧ (q ∧ r) =>
      And.intro (And.intro (And.left h_rhs) (And.left (And.right h_rhs))) (And.right (And.right h_rhs)))

example : (p ∨ q) ∨ r ↔ p ∨ (q ∨ r) :=
  have h_lhs : (p ∨ q) ∨ r → p ∨ (q ∨ r) :=
    (fun h : (p ∨ q) ∨ r =>
      Or.elim h
        (fun hpq : (p ∨ q) =>
          Or.elim hpq
            (fun hp : p => Or.inl hp)
            (fun hq : q => Or.inr (Or.inl hq)))
        (fun hr : r =>
          Or.inr (Or.inr hr)))
  have h_rhs : p ∨ (q ∨ r) → (p ∨ q) ∨ r :=
    (fun h : p ∨ (q ∨ r) =>
      Or.elim h
        (fun hp : p =>
          Or.inl (Or.inl hp))
        (fun hqr : q ∨ r =>
          Or.elim hqr
            (fun hq : q =>
              Or.inl (Or.inr hq))
            (fun hr : r =>
              Or.inr hr)))
  Iff.intro h_lhs h_rhs

-- distributivity
example : p ∧ (q ∨ r) ↔ (p ∧ q) ∨ (p ∧ r) :=
  have h_lhs : p ∧ (q ∨ r) → (p ∧ q) ∨ (p ∧ r) :=
    (fun h_pqr : p ∧ (q ∨ r) =>
      have hp : p := And.left h_pqr
      Or.elim (And.right h_pqr)
        (fun hq : q => Or.inl (And.intro hp hq))
        (fun hr : r => Or.inr (And.intro hp hr)))
  have h_rhs : (p ∧ q) ∨ (p ∧ r) → p ∧ (q ∨ r) :=
    (fun h_pq_pr : (p ∧ q) ∨ (p ∧ r) =>
      Or.elim h_pq_pr
        (fun hpq : p ∧ q =>
          (And.intro (And.left hpq) (Or.inl (And.right hpq))))
        (fun hpr : p ∧ r =>
          (And.intro (And.left hpr) (Or.inr (And.right hpr)))))
  Iff.intro h_lhs h_rhs

example : p ∨ (q ∧ r) ↔ (p ∨ q) ∧ (p ∨ r) :=
  have h_p_qr : p ∨ (q ∧ r) → (p ∨ q) ∧ (p ∨ r) :=
    (fun hp_qr : p ∨ (q ∧ r) =>
      Or.elim hp_qr
        (fun hp : p => And.intro (Or.inl hp) (Or.inl hp))
        (fun hqr : q ∧ r => And.intro (Or.inr (And.left hqr)) (Or.inr (And.right hqr))))
  have h_pq_pr : (p ∨ q) ∧ (p ∨ r) → p ∨ (q ∧ r) :=
    (fun h_pq_pr : (p ∨ q) ∧ (p ∨ r) =>
      Or.elim (And.left h_pq_pr)
        (fun hp : p => Or.inl hp)
        (fun hq : q =>
          Or.elim (And.right h_pq_pr)
            (fun hp : p => Or.inl hp)
            (fun hr : r => Or.inr (And.intro hq hr))))
  Iff.intro h_p_qr h_pq_pr

-- other properties
example : (p → (q → r)) ↔ (p ∧ q → r) :=
  have h_lhs : (p → (q → r)) → (p ∧ q → r) :=
    (fun h_pqr : p → (q → r) =>
      (fun hpq : p ∧ q =>
        (h_pqr (And.left hpq) (And.right hpq))))
  have h_rhs : (p ∧ q → r) → (p → (q → r)) :=
    (fun h_pqr : (p ∧ q → r) =>
      (fun hp : p =>
        (fun hq : q =>
          (h_pqr (And.intro hp hq)))))
  Iff.intro h_lhs h_rhs

example : ((p ∨ q) → r) ↔ (p → r) ∧ (q → r) :=
  have h_lhs : ((p ∨ q) → r) → (p → r) ∧ (q → r) :=
    (fun h_pq_r : ((p ∨ q) → r) =>
      And.intro
        (fun hp : p =>
          h_pq_r (Or.inl hp))
        (fun hq : q =>
          h_pq_r (Or.inr hq)))
  have h_rhs : (p → r) ∧ (q → r) → ((p ∨ q) → r) :=
    (fun h_pr_qr : (p → r) ∧ (q → r) =>
      (fun hpq : p ∨ q =>
        Or.elim hpq
          (fun hp : p => (And.left h_pr_qr) hp)
          (fun hq : q => (And.right h_pr_qr) hq)))
  Iff.intro h_lhs h_rhs

example : ¬(p ∨ q) ↔ ¬p ∧ ¬q :=
  have h_lhs : ¬(p ∨ q) → ¬p ∧ ¬q :=
    (fun (npq : (p ∨ q) → False) =>
      (And.intro
        (fun (hp : p) => (npq (Or.inl hp)))
        (fun (hq : q) => (npq (Or.inr hq)))))
  have h_rhs : ¬p ∧ ¬q → ¬(p ∨ q) :=
    (fun (hnpnq : ¬p ∧ ¬q) =>
      (fun (hpq : p ∨ q) =>
        Or.elim hpq
          (fun (hp : p) =>
            (And.left hnpnq) hp)
          (fun (hq : q) =>
            (And.right hnpnq) hq)))
  Iff.intro h_lhs h_rhs

example : ¬p ∨ ¬q → ¬(p ∧ q) :=
  (fun (hnpnq : ¬p ∨ ¬q) (hpq: p ∧ q) =>
    Or.elim hnpnq
      (fun hnp : ¬p => False.elim (hnp (And.left hpq)))
      (fun hnq : ¬q => False.elim (hnq (And.right hpq))))

example : ¬(p ∧ ¬p) :=
  (fun hpnp : p ∧ ¬p =>
    (False.elim ((And.right hpnp) (And.left hpnp))))

example : p ∧ ¬q → ¬(p → q) :=
  (fun (hpnq : p ∧ ¬q) (hpq : p → q) =>
    (False.elim ((And.right hpnq) (hpq (And.left hpnq)))))

example : ¬p → (p → q) :=
  (fun (hnp : ¬p) (hp : p) =>
    False.elim (hnp hp))

example : (¬p ∨ q) → (p → q) :=
  (fun h_npq : (¬p ∨ q) =>
    (fun hp : p =>
      Or.elim h_npq
        (fun hnp : ¬p =>
          False.elim (hnp hp))
        (fun hq : q => hq)))

example : p ∨ False ↔ p :=
  have h_lhs : p ∨ False → p :=
    (fun h_pf : p ∨ False =>
      Or.elim h_pf
        (fun hp : p => hp)
        (fun hf : False => False.elim hf))
  have h_rhs : p → p ∨ False :=
    (fun hp : p => Or.inl hp)
  Iff.intro h_lhs h_rhs

example : p ∧ False ↔ False :=
  have h_lhs : p ∧ False → False :=
    (fun h_pf : p ∧ False =>
      And.right h_pf)
  have h_rhs : False → p ∧ False :=
    (fun h_f : False =>
      And.intro (False.elim h_f) h_f)
  Iff.intro h_lhs h_rhs

example : (p → q) → (¬q → ¬p) :=
  (fun h_pq : p → q =>
    (fun hnq : ¬q =>
      (fun hp : p =>
        False.elim (hnq (h_pq hp)))))

open Classical

variable (p q r s : Prop)

theorem dne {p : Prop} (h : ¬¬p) : p :=
  Or.elim (em p)
  (fun hp : p => hp)
  (fun nhp : ¬p => False.elim (h nhp))

example : (p → r ∨ s) → ((p → r) ∨ (p → s)) := sorry
example : ¬(p ∧ q) → ¬p ∨ ¬q := sorry
example : ¬(p → q) → p ∧ ¬q := sorry
example : (p → q) → (¬p ∨ q) := sorry
example : (¬q → ¬p) → (p → q) := sorry
example : p ∨ ¬p := sorry
example : (((p → q) → p) → p) := sorry


open classical

variables { p q r : Prop }

theorem not_not_iff : ¬¬p ↔ p :=
⟨by_contradiction ∘ flip absurd, not_not_intro⟩

theorem imp_classical : p → q ↔ ¬ p ∨ q :=
⟨λh, by_cases (or.inr ∘ h) or.inl, λh h1, h.elim (absurd h1) id⟩

theorem not_and_iff_neg_or : ¬ (p ∧ q) ↔ (¬ p ∨ ¬ q) :=
by { split; intro h,
    { apply @by_cases p; intro h1,
        { apply @by_cases q; intro h2,
            { apply false.elim, apply h,
                split, exact h1, exact h2 },
            { right, exact h2 } },
        { left, exact h1 } },
    { intro h1, cases h1 with h2 h3, cases h with h h; apply h,
        exact h2, exact h3 } }

theorem not_or_iff_neg_and : ¬ (p ∨ q) ↔ (¬ p ∧ ¬ q) :=
by { split; intro h,
    { split; intro h1; apply h,
        { left, exact h1 }, { right, exact h1 } },
    { cases h with h h1, intro h2, cases h2 with h2 h2,
        { apply h, exact h2 }, { apply h1, exact h2 } } }


import game.sets.sets_level10
import data.real.basic

namespace xena -- hide

/-
# Chapter 2 : Order

## Level 1

This level aims to familiarize you with the use of the trichotomy property in
Lean, as it will come in handy in later levels.
This property is stated in Lean's mathlib is:

`lt_trichotomy : ∀ (a b : ?M_1), a < b ∨ a = b ∨ b < a`

and you can just use it to finish the proof below.
-/

/- Lemma
For any two real numbers $a$ and $b$, we have that
$$ a < b \lor a = b \lor b < a$$.
-/
theorem trichotomy' (a b : ℝ) : a < b ∨ a = b ∨ b < a :=
begin
    exact lt_trichotomy a b, done
end

end xena --hide


import game.max.level10

open_locale classical

noncomputable theory

namespace test

variables {a b c : ℝ}

-- What ℝ has that a general total order hasn't got, is - .

example : -a ≤ -b ↔ b ≤ a := by split; intros; linarith

def abs (x : ℝ) := max x (-x)

-- useful for rewriting
lemma abs_def (x : ℝ) : abs x = max x (-x) := rfl

-- needs congr'
lemma abs_neg (x : ℝ) : abs (-x) = abs x :=
begin
  rw abs_def,
  rw abs_def,
  rw max_comm,
  congr',
  ring,
end

-- order level 3
-- Powerful. Teaches them the colon.
theorem abs_le : abs a ≤ b ↔ -b ≤ a ∧ a ≤ b :=
begin
  rw abs_def,
  rw max_le_iff,
  split;
  intro h;
  cases h;
  split;
  linarith,
end

theorem abs_of_nonneg (h : 0 ≤ a) : abs a = a :=
begin
  rw abs_def,
  apply max_eq_left,
  linarith
end

theorem abs_of_nonpos (h : a ≤ 0) : abs a = -a :=
begin
  rw abs_def,
  apply max_eq_right,
  linarith
end

variables (a b) -- want them explicit in the next few

theorem abs_add : abs (a + b) ≤ abs a + abs b :=
begin
  rw abs_le,
  cases le_total 0 a with h0a ha0,
  { -- 0 ≤ a
    rw abs_of_nonneg h0a,
    cases le_total 0 b with h0b hb0,
    { rw abs_of_nonneg h0b,
      split; linarith
    },
    { rw abs_of_nonpos hb0,
      split; linarith
    },
  },
  { -- a ≤ 0
    rw abs_of_nonpos ha0,
    cases le_total 0 b with h0b hb0,
    { rw abs_of_nonneg h0b,
      split; linarith
    },
    { rw abs_of_nonpos hb0,
      split; linarith
    },
  },
end

-- order level 4
-- convert makes this simple
theorem abs_sub_le_add_abs : abs (a - b) ≤ abs a + abs b :=
begin
  rw ←abs_neg b,
  convert abs_add a (-b),
end

-- order level 5
-- combination of ring and linarith; always try and deduce from triangle ineq
theorem abs_abs_sub_le_abs_sub : abs (abs a - abs b) ≤ abs (a - b) :=
begin
  rw abs_le,
  split,
  { have h := abs_sub_le_add_abs a (a - b),
    ring at h,
    linarith,
  },
  { have h := abs_sub_le_add_abs (a - b) (-b),
    rw abs_neg at h,
    ring at h,
    linarith
  }
end

-- order level 2
theorem abs_mul (a b : ℝ) : abs (a * b) = abs a * abs b :=
begin
  cases le_total 0 a with h0a ha0;
  cases le_total 0 b with h0b hb0,
  { -- both nonnegative
    rw abs_of_nonneg h0a,
    rw abs_of_nonneg h0b,
    rw abs_of_nonneg,
    nlinarith,
  },
  { -- b <= 0 <= a
    rw abs_of_nonneg h0a,
    rw abs_of_nonpos hb0,
    rw abs_of_nonpos,
    { ring},
    nlinarith,
  },
  { -- a ≤ 0 ≤ b
    rw abs_of_nonpos ha0,
    rw abs_of_nonneg h0b,
    rw abs_of_nonpos,
    { ring},
    nlinarith,
  },
  { -- both nonnegative
    rw abs_of_nonpos ha0,
    rw abs_of_nonpos hb0,
    rw abs_of_nonneg,
    { ring},
    nlinarith,
  },
end

-- order level 6 (unfinished)
lemma le_iff_square_le (ha : 0 ≤ a) (hb : 0 ≤ b): a ≤ b ↔ a^2 ≤ b^2 :=
begin
  rw (show a^2 ≤ b^2 ↔ 0 ≤ b^2 - a^2, by split; intros; linarith),
  rw (show b^2 - a^2 = (b + a) * (b - a), by ring),
  rw (show a ≤ b ↔ 0 ≤ b - a, by split; intros; linarith), -- should be a lemma
  have hab : 0 ≤ b + a, by linarith,
  split,
  { intros,
    nlinarith},
  { intros,
    by_cases h : b + a = 0,
    { linarith },
    have ha2 : 0 < b + a,
    { by_contradiction hab,
      push_neg at hab,
      apply h,
      linarith
    },
    sorry
  }
end

end test


variables P Q : Prop

example : (P → Q) → (¬Q → ¬P) :=
begin
    intro HPQ,
    intro HnQ,

end


/-
Copyright (c) 2022 Alexander Bentkamp. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Alexander Bentkamp
-/
import analysis.inner_product_space.gram_schmidt_ortho
import linear_algebra.matrix.pos_def

/-! # LDL decomposition

This file proves the LDL-decomposition of matricies: Any positive definite matrix `S` can be
decomposed as `S = LDLᴴ` where `L` is a lower-triangular matrix and `D` is a diagonal matrix.

## Main definitions

 * `LDL.lower` is the lower triangular matrix `L`.
 * `LDL.lower_inv` is the inverse of the lower triangular matrix `L`.
 * `LDL.diag` is the diagonal matrix `D`.

## Main result

* `ldl_decomposition` states that any positive definite matrix can be decomposed as `LDLᴴ`.

## TODO

* Prove that `LDL.lower` is lower triangular from `LDL.lower_inv_triangular`.

-/

variables {𝕜 : Type*} [is_R_or_C 𝕜]
variables {n : Type*} [linear_order n] [is_well_order n (<)] [locally_finite_order_bot n]

local notation `⟪`x`, `y`⟫ₑ` := @inner 𝕜 _ _ ((pi_Lp.equiv 2 _).symm x) ((pi_Lp.equiv _ _).symm y)

open matrix
open_locale matrix
variables {S : matrix n n 𝕜} [fintype n] (hS : S.pos_def)

/-- The inverse of the lower triangular matrix `L` of the LDL-decomposition. It is obtained by
applying Gram-Schmidt-Orthogonalization w.r.t. the inner product induced by `Sᵀ` on the standard
basis vectors `pi.basis_fun`. -/
noncomputable def LDL.lower_inv : matrix n n 𝕜 :=
@gram_schmidt
  𝕜 (n → 𝕜) _
  (_ : _)
  (inner_product_space.of_matrix hS.transpose) n _ _ _ (pi.basis_fun 𝕜 n)

lemma LDL.lower_inv_eq_gram_schmidt_basis :
  LDL.lower_inv hS = ((pi.basis_fun 𝕜 n).to_matrix
    (@gram_schmidt_basis 𝕜 (n → 𝕜) _ (_ : _)
    (inner_product_space.of_matrix hS.transpose) n _ _ _ (pi.basis_fun 𝕜 n)))ᵀ :=
begin
  ext i j,
  rw [LDL.lower_inv, basis.coe_pi_basis_fun.to_matrix_eq_transpose, coe_gram_schmidt_basis],
  refl
end

noncomputable instance LDL.invertible_lower_inv : invertible (LDL.lower_inv hS) :=
begin
  rw [LDL.lower_inv_eq_gram_schmidt_basis],
  haveI := basis.invertible_to_matrix (pi.basis_fun 𝕜 n)
    (@gram_schmidt_basis 𝕜 (n → 𝕜) _ (_ : _) (inner_product_space.of_matrix hS.transpose)
      n _ _ _ (pi.basis_fun 𝕜 n)),
  apply_instance
end

lemma LDL.lower_inv_orthogonal {i j : n} (h₀ : i ≠ j) :
  ⟪(LDL.lower_inv hS i), Sᵀ.mul_vec (LDL.lower_inv hS j)⟫ₑ = 0 :=
@gram_schmidt_orthogonal 𝕜 _ _ (_ : _) (inner_product_space.of_matrix hS.transpose) _ _ _ _ _ _ _ h₀

/-- The entries of the diagonal matrix `D` of the LDL decomposition. -/
noncomputable def LDL.diag_entries : n → 𝕜 :=
λ i, ⟪star (LDL.lower_inv hS i), S.mul_vec (star (LDL.lower_inv hS i))⟫ₑ

/-- The diagonal matrix `D` of the LDL decomposition. -/
noncomputable def LDL.diag : matrix n n 𝕜 := matrix.diagonal (LDL.diag_entries hS)

lemma LDL.lower_inv_triangular {i j : n} (hij : i < j) :
  LDL.lower_inv hS i j = 0 :=
by rw [← @gram_schmidt_triangular
    𝕜 (n → 𝕜) _ (_ : _) (inner_product_space.of_matrix hS.transpose) n _ _ _
    i j hij (pi.basis_fun 𝕜 n), pi.basis_fun_repr, LDL.lower_inv]

/-- Inverse statement of **LDL decomposition**: we can conjugate a positive definite matrix
by some lower triangular matrix and get a diagonal matrix. -/
lemma LDL.diag_eq_lower_inv_conj : LDL.diag hS = LDL.lower_inv hS ⬝ S ⬝ (LDL.lower_inv hS)ᴴ :=
begin
  ext i j,
  by_cases hij : i = j,
  { simpa only [hij, LDL.diag, diagonal_apply_eq, LDL.diag_entries, matrix.mul_assoc,
      euclidean_space.inner_pi_Lp_equiv_symm, star_star] },
  { simp only [LDL.diag, hij, diagonal_apply_ne, ne.def, not_false_iff, mul_mul_apply],
    rw [conj_transpose, transpose_map, transpose_transpose, dot_product_mul_vec,
      (LDL.lower_inv_orthogonal hS (λ h : j = i, hij h.symm)).symm,
      ← inner_conj_symm, mul_vec_transpose, euclidean_space.inner_pi_Lp_equiv_symm,
      ← is_R_or_C.star_def, ← star_dot_product_star, dot_product_comm, star_star],
    refl }
end

/-- The lower triangular matrix `L` of the LDL decomposition. -/
noncomputable def LDL.lower := (LDL.lower_inv hS)⁻¹

/-- **LDL decomposition**: any positive definite matrix `S` can be
decomposed as `S = LDLᴴ` where `L` is a lower-triangular matrix and `D` is a diagonal matrix.  -/
theorem LDL.lower_conj_diag :
  LDL.lower hS ⬝ LDL.diag hS ⬝ (LDL.lower hS)ᴴ = S :=
begin
  rw [LDL.lower, conj_transpose_nonsing_inv, matrix.mul_assoc,
    matrix.inv_mul_eq_iff_eq_mul_of_invertible (LDL.lower_inv hS),
    matrix.mul_inv_eq_iff_eq_mul_of_invertible],
  exact LDL.diag_eq_lower_inv_conj hS,
end


import tactic

inductive pre_term
| var : ℕ → pre_term
| app : pre_term → pre_term → pre_term
| abs : ℕ → pre_term → pre_term

def free_variables : pre_term → set ℕ
| (pre_term.var n) := {n}
|	(pre_term.app M N) := free_variables M ∪ free_variables N
| (pre_term.abs n M) := free_variables M \ {n}

lemma free_variables_finite : ∀ M, (free_variables M).finite :=
λ M, pre_term.rec (λ n, set.finite_singleton n)
  (λ M N ihM ihN, ihM.union ihN)
  (λ n M ihn, ihn.diff {n}) M

lemma exists_new_free_var (M N : pre_term) : ∃n, n ∉ free_variables M ∧ n ∉ free_variables N :=
begin
	 suffices : ∃n, n ∉ (free_variables M ∪ free_variables N),
	 {
		 cases this with n hn,
		 use n,
		 exact not_or_distrib.mp hn,
	 },

	 have : free_variables M ∪ free_variables N ≠ set.univ,
	 {
		intro h,
	 	have h_fin := (free_variables_finite M).union(free_variables_finite N),
		rw h at h_fin,
		exact @not_fintype ℕ nat.infinite (set.fintype_of_finite_univ h_fin),
	 },

	by_contra,
	push_neg at h, apply this,
	ext, split,
	{ exact λ hx, set.mem_univ x },
	{exact λ gbg, h x},
end

open classical
local attribute [instance] prop_decidable

def closed : pre_term → Prop := λM, free_variables M = ∅

-- noncomputable def subst : pre_term → ℕ → pre_term → pre_term
-- | (pre_term.var n) x N := if x = n then N else pre_term.var n
-- | (pre_term.app P Q) x N := pre_term.app (subst P x N) (subst Q x N)
-- | (pre_term.abs y P) x N := if x = y then (pre_term.abs y P) else
-- (
-- 	if x ∈ free_variables P ∧ x ∈ free_variables N then
-- 	let z := classical.some (exists_new_free_var P N) in let temp := (subst P y (pre_term.var z)) in
-- 	pre_term.abs z (subst temp x N)
-- )

constant subst : pre_term → ℕ → pre_term → pre_term

inductive α_equiv : pre_term → pre_term → Prop
| refl (M) : α_equiv M M
| symm (M N) : α_equiv M N → α_equiv N M
| trans (M N O) : α_equiv M N → α_equiv N O → α_equiv M O
| app (M M' N N') : α_equiv M M' → α_equiv N N' → α_equiv (pre_term.app M N) (pre_term.app M' N')
| abs (P P' x) : α_equiv P P' → α_equiv (pre_term.abs x P) (pre_term.abs x P')
| rename (P) (x y) :
 y ∉ free_variables P → α_equiv (pre_term.abs x P) (pre_term.abs y (subst P x (pre_term.var y)))

instance lambda_term.setoid : setoid pre_term :=
setoid.mk α_equiv ⟨α_equiv.refl, α_equiv.symm, α_equiv.trans⟩

def lamba_term := quotient lambda_term.setoid


/-
Copyright (c) 2021 Damiano Testa. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Damiano Testa

! This file was ported from Lean 3 source module algebra.regular.smul
! leanprover-community/mathlib commit c3291da49cfa65f0d43b094750541c0731edc932
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.SmulWithZero
import Mathbin.Algebra.Regular.Basic

/-!
# Action of regular elements on a module

> THIS FILE IS SYNCHRONIZED WITH MATHLIB4.
> Any changes to this file require a corresponding PR to mathlib4.

We introduce `M`-regular elements, in the context of an `R`-module `M`.  The corresponding
predicate is called `is_smul_regular`.

There are very limited typeclass assumptions on `R` and `M`, but the "mathematical" case of interest
is a commutative ring `R` acting an a module `M`. Since the properties are "multiplicative", there
is no actual requirement of having an addition, but there is a zero in both `R` and `M`.
Smultiplications involving `0` are, of course, all trivial.

The defining property is that an element `a ∈ R` is `M`-regular if the smultiplication map
`M → M`, defined by `m ↦ a • m`, is injective.

This property is the direct generalization to modules of the property `is_left_regular` defined in
`algebra/regular`.  Lemma `is_smul_regular.is_left_regular_iff` shows that indeed the two notions
coincide.
-/

variable {R S : Type _} (M : Type _) {a b : R} {s : S}

#print IsSMulRegular /-
/-- An `M`-regular element is an element `c` such that multiplication on the left by `c` is an
injective map `M → M`. -/
def IsSMulRegular [SMul R M] (c : R) :=
  Function.Injective ((· • ·) c : M → M)
#align is_smul_regular IsSMulRegular
-/

#print IsLeftRegular.isSMulRegular /-
theorem IsLeftRegular.isSMulRegular [Mul R] {c : R} (h : IsLeftRegular c) : IsSMulRegular R c :=
  h
#align is_left_regular.is_smul_regular IsLeftRegular.isSMulRegular
-/

#print isLeftRegular_iff /-
/-- Left-regular multiplication on `R` is equivalent to `R`-regularity of `R` itself. -/
theorem isLeftRegular_iff [Mul R] {a : R} : IsLeftRegular a ↔ IsSMulRegular R a :=
  Iff.rfl
#align is_left_regular_iff isLeftRegular_iff
-/

#print IsRightRegular.isSMulRegular /-
theorem IsRightRegular.isSMulRegular [Mul R] {c : R} (h : IsRightRegular c) :
    IsSMulRegular R (MulOpposite.op c) :=
  h
#align is_right_regular.is_smul_regular IsRightRegular.isSMulRegular
-/

#print isRightRegular_iff /-
/-- Right-regular multiplication on `R` is equivalent to `Rᵐᵒᵖ`-regularity of `R` itself. -/
theorem isRightRegular_iff [Mul R] {a : R} :
    IsRightRegular a ↔ IsSMulRegular R (MulOpposite.op a) :=
  Iff.rfl
#align is_right_regular_iff isRightRegular_iff
-/

namespace IsSMulRegular

variable {M}

section SMul

variable [SMul R M] [SMul R S] [SMul S M] [IsScalarTower R S M]

/- warning: is_smul_regular.smul -> IsSMulRegular.smul is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {S : Type.{u2}} {M : Type.{u3}} {a : R} {s : S} [_inst_1 : SMul.{u1, u3} R M] [_inst_2 : SMul.{u1, u2} R S] [_inst_3 : SMul.{u2, u3} S M] [_inst_4 : IsScalarTower.{u1, u2, u3} R S M _inst_2 _inst_3 _inst_1], (IsSMulRegular.{u1, u3} R M _inst_1 a) -> (IsSMulRegular.{u2, u3} S M _inst_3 s) -> (IsSMulRegular.{u2, u3} S M _inst_3 (SMul.smul.{u1, u2} R S _inst_2 a s))
but is expected to have type
  forall {R : Type.{u3}} {S : Type.{u1}} {M : Type.{u2}} {a : R} {s : S} [_inst_1 : SMul.{u3, u2} R M] [_inst_2 : SMul.{u3, u1} R S] [_inst_3 : SMul.{u1, u2} S M] [_inst_4 : IsScalarTower.{u3, u1, u2} R S M _inst_2 _inst_3 _inst_1], (IsSMulRegular.{u3, u2} R M _inst_1 a) -> (IsSMulRegular.{u1, u2} S M _inst_3 s) -> (IsSMulRegular.{u1, u2} S M _inst_3 (HSMul.hSMul.{u3, u1, u1} R S S (instHSMul.{u3, u1} R S _inst_2) a s))
Case conversion may be inaccurate. Consider using '#align is_smul_regular.smul IsSMulRegular.smulₓ'. -/
/-- The product of `M`-regular elements is `M`-regular. -/
theorem smul (ra : IsSMulRegular M a) (rs : IsSMulRegular M s) : IsSMulRegular M (a • s) :=
  fun a b ab => rs (ra ((smul_assoc _ _ _).symm.trans (ab.trans (smul_assoc _ _ _))))
#align is_smul_regular.smul IsSMulRegular.smul

/- warning: is_smul_regular.of_smul -> IsSMulRegular.of_smul is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {S : Type.{u2}} {M : Type.{u3}} {s : S} [_inst_1 : SMul.{u1, u3} R M] [_inst_2 : SMul.{u1, u2} R S] [_inst_3 : SMul.{u2, u3} S M] [_inst_4 : IsScalarTower.{u1, u2, u3} R S M _inst_2 _inst_3 _inst_1] (a : R), (IsSMulRegular.{u2, u3} S M _inst_3 (SMul.smul.{u1, u2} R S _inst_2 a s)) -> (IsSMulRegular.{u2, u3} S M _inst_3 s)
but is expected to have type
  forall {R : Type.{u1}} {S : Type.{u3}} {M : Type.{u2}} {s : S} [_inst_1 : SMul.{u1, u2} R M] [_inst_2 : SMul.{u1, u3} R S] [_inst_3 : SMul.{u3, u2} S M] [_inst_4 : IsScalarTower.{u1, u3, u2} R S M _inst_2 _inst_3 _inst_1] (a : R), (IsSMulRegular.{u3, u2} S M _inst_3 (HSMul.hSMul.{u1, u3, u3} R S S (instHSMul.{u1, u3} R S _inst_2) a s)) -> (IsSMulRegular.{u3, u2} S M _inst_3 s)
Case conversion may be inaccurate. Consider using '#align is_smul_regular.of_smul IsSMulRegular.of_smulₓ'. -/
/-- If an element `b` becomes `M`-regular after multiplying it on the left by an `M`-regular
element, then `b` is `M`-regular. -/
theorem of_smul (a : R) (ab : IsSMulRegular M (a • s)) : IsSMulRegular M s :=
  @Function.Injective.of_comp _ _ _ (fun m : M => a • m) _ fun c d cd =>
    ab (by rwa [smul_assoc, smul_assoc])
#align is_smul_regular.of_smul IsSMulRegular.of_smul

/- warning: is_smul_regular.smul_iff -> IsSMulRegular.smul_iff is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {S : Type.{u2}} {M : Type.{u3}} {a : R} [_inst_1 : SMul.{u1, u3} R M] [_inst_2 : SMul.{u1, u2} R S] [_inst_3 : SMul.{u2, u3} S M] [_inst_4 : IsScalarTower.{u1, u2, u3} R S M _inst_2 _inst_3 _inst_1] (b : S), (IsSMulRegular.{u1, u3} R M _inst_1 a) -> (Iff (IsSMulRegular.{u2, u3} S M _inst_3 (SMul.smul.{u1, u2} R S _inst_2 a b)) (IsSMulRegular.{u2, u3} S M _inst_3 b))
but is expected to have type
  forall {R : Type.{u3}} {S : Type.{u1}} {M : Type.{u2}} {a : R} [_inst_1 : SMul.{u3, u2} R M] [_inst_2 : SMul.{u3, u1} R S] [_inst_3 : SMul.{u1, u2} S M] [_inst_4 : IsScalarTower.{u3, u1, u2} R S M _inst_2 _inst_3 _inst_1] (b : S), (IsSMulRegular.{u3, u2} R M _inst_1 a) -> (Iff (IsSMulRegular.{u1, u2} S M _inst_3 (HSMul.hSMul.{u3, u1, u1} R S S (instHSMul.{u3, u1} R S _inst_2) a b)) (IsSMulRegular.{u1, u2} S M _inst_3 b))
Case conversion may be inaccurate. Consider using '#align is_smul_regular.smul_iff IsSMulRegular.smul_iffₓ'. -/
/-- An element is `M`-regular if and only if multiplying it on the left by an `M`-regular element
is `M`-regular. -/
@[simp]
theorem smul_iff (b : S) (ha : IsSMulRegular M a) : IsSMulRegular M (a • b) ↔ IsSMulRegular M b :=
  ⟨of_smul _, ha.smul⟩
#align is_smul_regular.smul_iff IsSMulRegular.smul_iff

#print IsSMulRegular.isLeftRegular /-
theorem isLeftRegular [Mul R] {a : R} (h : IsSMulRegular R a) : IsLeftRegular a :=
  h
#align is_smul_regular.is_left_regular IsSMulRegular.isLeftRegular
-/

#print IsSMulRegular.isRightRegular /-
theorem isRightRegular [Mul R] {a : R} (h : IsSMulRegular R (MulOpposite.op a)) :
    IsRightRegular a :=
  h
#align is_smul_regular.is_right_regular IsSMulRegular.isRightRegular
-/

/- warning: is_smul_regular.mul -> IsSMulRegular.mul is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {M : Type.{u2}} {a : R} {b : R} [_inst_1 : SMul.{u1, u2} R M] [_inst_5 : Mul.{u1} R] [_inst_6 : IsScalarTower.{u1, u1, u2} R R M (Mul.toSMul.{u1} R _inst_5) _inst_1 _inst_1], (IsSMulRegular.{u1, u2} R M _inst_1 a) -> (IsSMulRegular.{u1, u2} R M _inst_1 b) -> (IsSMulRegular.{u1, u2} R M _inst_1 (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R _inst_5) a b))
but is expected to have type
  forall {R : Type.{u2}} {M : Type.{u1}} {a : R} {b : R} [_inst_1 : SMul.{u2, u1} R M] [_inst_5 : Mul.{u2} R] [_inst_6 : IsScalarTower.{u2, u2, u1} R R M (Mul.toSMul.{u2} R _inst_5) _inst_1 _inst_1], (IsSMulRegular.{u2, u1} R M _inst_1 a) -> (IsSMulRegular.{u2, u1} R M _inst_1 b) -> (IsSMulRegular.{u2, u1} R M _inst_1 (HMul.hMul.{u2, u2, u2} R R R (instHMul.{u2} R _inst_5) a b))
Case conversion may be inaccurate. Consider using '#align is_smul_regular.mul IsSMulRegular.mulₓ'. -/
theorem mul [Mul R] [IsScalarTower R R M] (ra : IsSMulRegular M a) (rb : IsSMulRegular M b) :
    IsSMulRegular M (a * b) :=
  ra.smul rb
#align is_smul_regular.mul IsSMulRegular.mul

/- warning: is_smul_regular.of_mul -> IsSMulRegular.of_mul is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {M : Type.{u2}} {a : R} {b : R} [_inst_1 : SMul.{u1, u2} R M] [_inst_5 : Mul.{u1} R] [_inst_6 : IsScalarTower.{u1, u1, u2} R R M (Mul.toSMul.{u1} R _inst_5) _inst_1 _inst_1], (IsSMulRegular.{u1, u2} R M _inst_1 (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R _inst_5) a b)) -> (IsSMulRegular.{u1, u2} R M _inst_1 b)
but is expected to have type
  forall {R : Type.{u2}} {M : Type.{u1}} {a : R} {b : R} [_inst_1 : SMul.{u2, u1} R M] [_inst_5 : Mul.{u2} R] [_inst_6 : IsScalarTower.{u2, u2, u1} R R M (Mul.toSMul.{u2} R _inst_5) _inst_1 _inst_1], (IsSMulRegular.{u2, u1} R M _inst_1 (HMul.hMul.{u2, u2, u2} R R R (instHMul.{u2} R _inst_5) a b)) -> (IsSMulRegular.{u2, u1} R M _inst_1 b)
Case conversion may be inaccurate. Consider using '#align is_smul_regular.of_mul IsSMulRegular.of_mulₓ'. -/
theorem of_mul [Mul R] [IsScalarTower R R M] (ab : IsSMulRegular M (a * b)) : IsSMulRegular M b :=
  by
  rw [← smul_eq_mul] at ab
  exact ab.of_smul _
#align is_smul_regular.of_mul IsSMulRegular.of_mul

/- warning: is_smul_regular.mul_iff_right -> IsSMulRegular.mul_iff_right is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {M : Type.{u2}} {a : R} {b : R} [_inst_1 : SMul.{u1, u2} R M] [_inst_5 : Mul.{u1} R] [_inst_6 : IsScalarTower.{u1, u1, u2} R R M (Mul.toSMul.{u1} R _inst_5) _inst_1 _inst_1], (IsSMulRegular.{u1, u2} R M _inst_1 a) -> (Iff (IsSMulRegular.{u1, u2} R M _inst_1 (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R _inst_5) a b)) (IsSMulRegular.{u1, u2} R M _inst_1 b))
but is expected to have type
  forall {R : Type.{u2}} {M : Type.{u1}} {a : R} {b : R} [_inst_1 : SMul.{u2, u1} R M] [_inst_5 : Mul.{u2} R] [_inst_6 : IsScalarTower.{u2, u2, u1} R R M (Mul.toSMul.{u2} R _inst_5) _inst_1 _inst_1], (IsSMulRegular.{u2, u1} R M _inst_1 a) -> (Iff (IsSMulRegular.{u2, u1} R M _inst_1 (HMul.hMul.{u2, u2, u2} R R R (instHMul.{u2} R _inst_5) a b)) (IsSMulRegular.{u2, u1} R M _inst_1 b))
Case conversion may be inaccurate. Consider using '#align is_smul_regular.mul_iff_right IsSMulRegular.mul_iff_rightₓ'. -/
@[simp]
theorem mul_iff_right [Mul R] [IsScalarTower R R M] (ha : IsSMulRegular M a) :
    IsSMulRegular M (a * b) ↔ IsSMulRegular M b :=
  ⟨of_mul, ha.mul⟩
#align is_smul_regular.mul_iff_right IsSMulRegular.mul_iff_right

/- warning: is_smul_regular.mul_and_mul_iff -> IsSMulRegular.mul_and_mul_iff is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {M : Type.{u2}} {a : R} {b : R} [_inst_1 : SMul.{u1, u2} R M] [_inst_5 : Mul.{u1} R] [_inst_6 : IsScalarTower.{u1, u1, u2} R R M (Mul.toSMul.{u1} R _inst_5) _inst_1 _inst_1], Iff (And (IsSMulRegular.{u1, u2} R M _inst_1 (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R _inst_5) a b)) (IsSMulRegular.{u1, u2} R M _inst_1 (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R _inst_5) b a))) (And (IsSMulRegular.{u1, u2} R M _inst_1 a) (IsSMulRegular.{u1, u2} R M _inst_1 b))
but is expected to have type
  forall {R : Type.{u2}} {M : Type.{u1}} {a : R} {b : R} [_inst_1 : SMul.{u2, u1} R M] [_inst_5 : Mul.{u2} R] [_inst_6 : IsScalarTower.{u2, u2, u1} R R M (Mul.toSMul.{u2} R _inst_5) _inst_1 _inst_1], Iff (And (IsSMulRegular.{u2, u1} R M _inst_1 (HMul.hMul.{u2, u2, u2} R R R (instHMul.{u2} R _inst_5) a b)) (IsSMulRegular.{u2, u1} R M _inst_1 (HMul.hMul.{u2, u2, u2} R R R (instHMul.{u2} R _inst_5) b a))) (And (IsSMulRegular.{u2, u1} R M _inst_1 a) (IsSMulRegular.{u2, u1} R M _inst_1 b))
Case conversion may be inaccurate. Consider using '#align is_smul_regular.mul_and_mul_iff IsSMulRegular.mul_and_mul_iffₓ'. -/
/-- Two elements `a` and `b` are `M`-regular if and only if both products `a * b` and `b * a`
are `M`-regular. -/
theorem mul_and_mul_iff [Mul R] [IsScalarTower R R M] :
    IsSMulRegular M (a * b) ∧ IsSMulRegular M (b * a) ↔ IsSMulRegular M a ∧ IsSMulRegular M b :=
  by
  refine' ⟨_, _⟩
  · rintro ⟨ab, ba⟩
    refine' ⟨ba.of_mul, ab.of_mul⟩
  · rintro ⟨ha, hb⟩
    exact ⟨ha.mul hb, hb.mul ha⟩
#align is_smul_regular.mul_and_mul_iff IsSMulRegular.mul_and_mul_iff

end SMul

section Monoid

variable [Monoid R] [MulAction R M]

variable (M)

/- warning: is_smul_regular.one -> IsSMulRegular.one is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} (M : Type.{u2}) [_inst_1 : Monoid.{u1} R] [_inst_2 : MulAction.{u1, u2} R M _inst_1], IsSMulRegular.{u1, u2} R M (MulAction.toHasSmul.{u1, u2} R M _inst_1 _inst_2) (OfNat.ofNat.{u1} R 1 (OfNat.mk.{u1} R 1 (One.one.{u1} R (MulOneClass.toHasOne.{u1} R (Monoid.toMulOneClass.{u1} R _inst_1)))))
but is expected to have type
  forall {R : Type.{u2}} (M : Type.{u1}) [_inst_1 : Monoid.{u2} R] [_inst_2 : MulAction.{u2, u1} R M _inst_1], IsSMulRegular.{u2, u1} R M (MulAction.toSMul.{u2, u1} R M _inst_1 _inst_2) (OfNat.ofNat.{u2} R 1 (One.toOfNat1.{u2} R (Monoid.toOne.{u2} R _inst_1)))
Case conversion may be inaccurate. Consider using '#align is_smul_regular.one IsSMulRegular.oneₓ'. -/
/-- One is `M`-regular always. -/
@[simp]
theorem one : IsSMulRegular M (1 : R) := fun a b ab => by rwa [one_smul, one_smul] at ab
#align is_smul_regular.one IsSMulRegular.one

variable {M}

/- warning: is_smul_regular.of_mul_eq_one -> IsSMulRegular.of_mul_eq_one is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {M : Type.{u2}} {a : R} {b : R} [_inst_1 : Monoid.{u1} R] [_inst_2 : MulAction.{u1, u2} R M _inst_1], (Eq.{succ u1} R (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (MulOneClass.toHasMul.{u1} R (Monoid.toMulOneClass.{u1} R _inst_1))) a b) (OfNat.ofNat.{u1} R 1 (OfNat.mk.{u1} R 1 (One.one.{u1} R (MulOneClass.toHasOne.{u1} R (Monoid.toMulOneClass.{u1} R _inst_1)))))) -> (IsSMulRegular.{u1, u2} R M (MulAction.toHasSmul.{u1, u2} R M _inst_1 _inst_2) b)
but is expected to have type
  forall {R : Type.{u2}} {M : Type.{u1}} {a : R} {b : R} [_inst_1 : Monoid.{u2} R] [_inst_2 : MulAction.{u2, u1} R M _inst_1], (Eq.{succ u2} R (HMul.hMul.{u2, u2, u2} R R R (instHMul.{u2} R (MulOneClass.toMul.{u2} R (Monoid.toMulOneClass.{u2} R _inst_1))) a b) (OfNat.ofNat.{u2} R 1 (One.toOfNat1.{u2} R (Monoid.toOne.{u2} R _inst_1)))) -> (IsSMulRegular.{u2, u1} R M (MulAction.toSMul.{u2, u1} R M _inst_1 _inst_2) b)
Case conversion may be inaccurate. Consider using '#align is_smul_regular.of_mul_eq_one IsSMulRegular.of_mul_eq_oneₓ'. -/
/-- An element of `R` admitting a left inverse is `M`-regular. -/
theorem of_mul_eq_one (h : a * b = 1) : IsSMulRegular M b :=
  of_mul
    (by
      rw [h]
      exact one M)
#align is_smul_regular.of_mul_eq_one IsSMulRegular.of_mul_eq_one

/- warning: is_smul_regular.pow -> IsSMulRegular.pow is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {M : Type.{u2}} {a : R} [_inst_1 : Monoid.{u1} R] [_inst_2 : MulAction.{u1, u2} R M _inst_1] (n : Nat), (IsSMulRegular.{u1, u2} R M (MulAction.toHasSmul.{u1, u2} R M _inst_1 _inst_2) a) -> (IsSMulRegular.{u1, u2} R M (MulAction.toHasSmul.{u1, u2} R M _inst_1 _inst_2) (HPow.hPow.{u1, 0, u1} R Nat R (instHPow.{u1, 0} R Nat (Monoid.Pow.{u1} R _inst_1)) a n))
but is expected to have type
  forall {R : Type.{u2}} {M : Type.{u1}} {a : R} [_inst_1 : Monoid.{u2} R] [_inst_2 : MulAction.{u2, u1} R M _inst_1] (n : Nat), (IsSMulRegular.{u2, u1} R M (MulAction.toSMul.{u2, u1} R M _inst_1 _inst_2) a) -> (IsSMulRegular.{u2, u1} R M (MulAction.toSMul.{u2, u1} R M _inst_1 _inst_2) (HPow.hPow.{u2, 0, u2} R Nat R (instHPow.{u2, 0} R Nat (Monoid.Pow.{u2} R _inst_1)) a n))
Case conversion may be inaccurate. Consider using '#align is_smul_regular.pow IsSMulRegular.powₓ'. -/
/-- Any power of an `M`-regular element is `M`-regular. -/
theorem pow (n : ℕ) (ra : IsSMulRegular M a) : IsSMulRegular M (a ^ n) :=
  by
  induction' n with n hn
  · simp only [one, pow_zero]
  · rw [pow_succ]
    exact (ra.smul_iff (a ^ n)).mpr hn
#align is_smul_regular.pow IsSMulRegular.pow

/- warning: is_smul_regular.pow_iff -> IsSMulRegular.pow_iff is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {M : Type.{u2}} {a : R} [_inst_1 : Monoid.{u1} R] [_inst_2 : MulAction.{u1, u2} R M _inst_1] {n : Nat}, (LT.lt.{0} Nat Nat.hasLt (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero))) n) -> (Iff (IsSMulRegular.{u1, u2} R M (MulAction.toHasSmul.{u1, u2} R M _inst_1 _inst_2) (HPow.hPow.{u1, 0, u1} R Nat R (instHPow.{u1, 0} R Nat (Monoid.Pow.{u1} R _inst_1)) a n)) (IsSMulRegular.{u1, u2} R M (MulAction.toHasSmul.{u1, u2} R M _inst_1 _inst_2) a))
but is expected to have type
  forall {R : Type.{u2}} {M : Type.{u1}} {a : R} [_inst_1 : Monoid.{u2} R] [_inst_2 : MulAction.{u2, u1} R M _inst_1] {n : Nat}, (LT.lt.{0} Nat instLTNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)) n) -> (Iff (IsSMulRegular.{u2, u1} R M (MulAction.toSMul.{u2, u1} R M _inst_1 _inst_2) (HPow.hPow.{u2, 0, u2} R Nat R (instHPow.{u2, 0} R Nat (Monoid.Pow.{u2} R _inst_1)) a n)) (IsSMulRegular.{u2, u1} R M (MulAction.toSMul.{u2, u1} R M _inst_1 _inst_2) a))
Case conversion may be inaccurate. Consider using '#align is_smul_regular.pow_iff IsSMulRegular.pow_iffₓ'. -/
/-- An element `a` is `M`-regular if and only if a positive power of `a` is `M`-regular. -/
theorem pow_iff {n : ℕ} (n0 : 0 < n) : IsSMulRegular M (a ^ n) ↔ IsSMulRegular M a :=
  by
  refine' ⟨_, pow n⟩
  rw [← Nat.succ_pred_eq_of_pos n0, pow_succ', ← smul_eq_mul]
  exact of_smul _
#align is_smul_regular.pow_iff IsSMulRegular.pow_iff

end Monoid

section MonoidSmul

variable [Monoid S] [SMul R M] [SMul R S] [MulAction S M] [IsScalarTower R S M]

/- warning: is_smul_regular.of_smul_eq_one -> IsSMulRegular.of_smul_eq_one is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {S : Type.{u2}} {M : Type.{u3}} {a : R} {s : S} [_inst_1 : Monoid.{u2} S] [_inst_2 : SMul.{u1, u3} R M] [_inst_3 : SMul.{u1, u2} R S] [_inst_4 : MulAction.{u2, u3} S M _inst_1] [_inst_5 : IsScalarTower.{u1, u2, u3} R S M _inst_3 (MulAction.toHasSmul.{u2, u3} S M _inst_1 _inst_4) _inst_2], (Eq.{succ u2} S (SMul.smul.{u1, u2} R S _inst_3 a s) (OfNat.ofNat.{u2} S 1 (OfNat.mk.{u2} S 1 (One.one.{u2} S (MulOneClass.toHasOne.{u2} S (Monoid.toMulOneClass.{u2} S _inst_1)))))) -> (IsSMulRegular.{u2, u3} S M (MulAction.toHasSmul.{u2, u3} S M _inst_1 _inst_4) s)
but is expected to have type
  forall {R : Type.{u2}} {S : Type.{u3}} {M : Type.{u1}} {a : R} {s : S} [_inst_1 : Monoid.{u3} S] [_inst_2 : SMul.{u2, u1} R M] [_inst_3 : SMul.{u2, u3} R S] [_inst_4 : MulAction.{u3, u1} S M _inst_1] [_inst_5 : IsScalarTower.{u2, u3, u1} R S M _inst_3 (MulAction.toSMul.{u3, u1} S M _inst_1 _inst_4) _inst_2], (Eq.{succ u3} S (HSMul.hSMul.{u2, u3, u3} R S S (instHSMul.{u2, u3} R S _inst_3) a s) (OfNat.ofNat.{u3} S 1 (One.toOfNat1.{u3} S (Monoid.toOne.{u3} S _inst_1)))) -> (IsSMulRegular.{u3, u1} S M (MulAction.toSMul.{u3, u1} S M _inst_1 _inst_4) s)
Case conversion may be inaccurate. Consider using '#align is_smul_regular.of_smul_eq_one IsSMulRegular.of_smul_eq_oneₓ'. -/
/-- An element of `S` admitting a left inverse in `R` is `M`-regular. -/
theorem of_smul_eq_one (h : a • s = 1) : IsSMulRegular M s :=
  of_smul a
    (by
      rw [h]
      exact one M)
#align is_smul_regular.of_smul_eq_one IsSMulRegular.of_smul_eq_one

end MonoidSmul

section MonoidWithZero

variable [MonoidWithZero R] [MonoidWithZero S] [Zero M] [MulActionWithZero R M]
  [MulActionWithZero R S] [MulActionWithZero S M] [IsScalarTower R S M]

/- warning: is_smul_regular.subsingleton -> IsSMulRegular.subsingleton is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {M : Type.{u2}} [_inst_1 : MonoidWithZero.{u1} R] [_inst_3 : Zero.{u2} M] [_inst_4 : MulActionWithZero.{u1, u2} R M _inst_1 _inst_3], (IsSMulRegular.{u1, u2} R M (SMulZeroClass.toHasSmul.{u1, u2} R M _inst_3 (SMulWithZero.toSmulZeroClass.{u1, u2} R M (MulZeroClass.toHasZero.{u1} R (MulZeroOneClass.toMulZeroClass.{u1} R (MonoidWithZero.toMulZeroOneClass.{u1} R _inst_1))) _inst_3 (MulActionWithZero.toSMulWithZero.{u1, u2} R M _inst_1 _inst_3 _inst_4))) (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R (MulZeroOneClass.toMulZeroClass.{u1} R (MonoidWithZero.toMulZeroOneClass.{u1} R _inst_1))))))) -> (Subsingleton.{succ u2} M)
but is expected to have type
  forall {R : Type.{u2}} {M : Type.{u1}} [_inst_1 : MonoidWithZero.{u2} R] [_inst_3 : Zero.{u1} M] [_inst_4 : MulActionWithZero.{u2, u1} R M _inst_1 _inst_3], (IsSMulRegular.{u2, u1} R M (SMulZeroClass.toSMul.{u2, u1} R M _inst_3 (SMulWithZero.toSMulZeroClass.{u2, u1} R M (MonoidWithZero.toZero.{u2} R _inst_1) _inst_3 (MulActionWithZero.toSMulWithZero.{u2, u1} R M _inst_1 _inst_3 _inst_4))) (OfNat.ofNat.{u2} R 0 (Zero.toOfNat0.{u2} R (MonoidWithZero.toZero.{u2} R _inst_1)))) -> (Subsingleton.{succ u1} M)
Case conversion may be inaccurate. Consider using '#align is_smul_regular.subsingleton IsSMulRegular.subsingletonₓ'. -/
/-- The element `0` is `M`-regular if and only if `M` is trivial. -/
protected theorem subsingleton (h : IsSMulRegular M (0 : R)) : Subsingleton M :=
  ⟨fun a b => h (by repeat' rw [MulActionWithZero.zero_smul])⟩
#align is_smul_regular.subsingleton IsSMulRegular.subsingleton

/- warning: is_smul_regular.zero_iff_subsingleton -> IsSMulRegular.zero_iff_subsingleton is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {M : Type.{u2}} [_inst_1 : MonoidWithZero.{u1} R] [_inst_3 : Zero.{u2} M] [_inst_4 : MulActionWithZero.{u1, u2} R M _inst_1 _inst_3], Iff (IsSMulRegular.{u1, u2} R M (SMulZeroClass.toHasSmul.{u1, u2} R M _inst_3 (SMulWithZero.toSmulZeroClass.{u1, u2} R M (MulZeroClass.toHasZero.{u1} R (MulZeroOneClass.toMulZeroClass.{u1} R (MonoidWithZero.toMulZeroOneClass.{u1} R _inst_1))) _inst_3 (MulActionWithZero.toSMulWithZero.{u1, u2} R M _inst_1 _inst_3 _inst_4))) (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R (MulZeroOneClass.toMulZeroClass.{u1} R (MonoidWithZero.toMulZeroOneClass.{u1} R _inst_1))))))) (Subsingleton.{succ u2} M)
but is expected to have type
  forall {R : Type.{u2}} {M : Type.{u1}} [_inst_1 : MonoidWithZero.{u2} R] [_inst_3 : Zero.{u1} M] [_inst_4 : MulActionWithZero.{u2, u1} R M _inst_1 _inst_3], Iff (IsSMulRegular.{u2, u1} R M (SMulZeroClass.toSMul.{u2, u1} R M _inst_3 (SMulWithZero.toSMulZeroClass.{u2, u1} R M (MonoidWithZero.toZero.{u2} R _inst_1) _inst_3 (MulActionWithZero.toSMulWithZero.{u2, u1} R M _inst_1 _inst_3 _inst_4))) (OfNat.ofNat.{u2} R 0 (Zero.toOfNat0.{u2} R (MonoidWithZero.toZero.{u2} R _inst_1)))) (Subsingleton.{succ u1} M)
Case conversion may be inaccurate. Consider using '#align is_smul_regular.zero_iff_subsingleton IsSMulRegular.zero_iff_subsingletonₓ'. -/
/-- The element `0` is `M`-regular if and only if `M` is trivial. -/
theorem zero_iff_subsingleton : IsSMulRegular M (0 : R) ↔ Subsingleton M :=
  ⟨fun h => h.Subsingleton, fun H a b h => @Subsingleton.elim _ H a b⟩
#align is_smul_regular.zero_iff_subsingleton IsSMulRegular.zero_iff_subsingleton

/- warning: is_smul_regular.not_zero_iff -> IsSMulRegular.not_zero_iff is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {M : Type.{u2}} [_inst_1 : MonoidWithZero.{u1} R] [_inst_3 : Zero.{u2} M] [_inst_4 : MulActionWithZero.{u1, u2} R M _inst_1 _inst_3], Iff (Not (IsSMulRegular.{u1, u2} R M (SMulZeroClass.toHasSmul.{u1, u2} R M _inst_3 (SMulWithZero.toSmulZeroClass.{u1, u2} R M (MulZeroClass.toHasZero.{u1} R (MulZeroOneClass.toMulZeroClass.{u1} R (MonoidWithZero.toMulZeroOneClass.{u1} R _inst_1))) _inst_3 (MulActionWithZero.toSMulWithZero.{u1, u2} R M _inst_1 _inst_3 _inst_4))) (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R (MulZeroOneClass.toMulZeroClass.{u1} R (MonoidWithZero.toMulZeroOneClass.{u1} R _inst_1)))))))) (Nontrivial.{u2} M)
but is expected to have type
  forall {R : Type.{u2}} {M : Type.{u1}} [_inst_1 : MonoidWithZero.{u2} R] [_inst_3 : Zero.{u1} M] [_inst_4 : MulActionWithZero.{u2, u1} R M _inst_1 _inst_3], Iff (Not (IsSMulRegular.{u2, u1} R M (SMulZeroClass.toSMul.{u2, u1} R M _inst_3 (SMulWithZero.toSMulZeroClass.{u2, u1} R M (MonoidWithZero.toZero.{u2} R _inst_1) _inst_3 (MulActionWithZero.toSMulWithZero.{u2, u1} R M _inst_1 _inst_3 _inst_4))) (OfNat.ofNat.{u2} R 0 (Zero.toOfNat0.{u2} R (MonoidWithZero.toZero.{u2} R _inst_1))))) (Nontrivial.{u1} M)
Case conversion may be inaccurate. Consider using '#align is_smul_regular.not_zero_iff IsSMulRegular.not_zero_iffₓ'. -/
/-- The `0` element is not `M`-regular, on a non-trivial module. -/
theorem not_zero_iff : ¬IsSMulRegular M (0 : R) ↔ Nontrivial M :=
  by
  rw [nontrivial_iff, not_iff_comm, zero_iff_subsingleton, subsingleton_iff]
  push_neg
  exact Iff.rfl
#align is_smul_regular.not_zero_iff IsSMulRegular.not_zero_iff

/- warning: is_smul_regular.zero -> IsSMulRegular.zero is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {M : Type.{u2}} [_inst_1 : MonoidWithZero.{u1} R] [_inst_3 : Zero.{u2} M] [_inst_4 : MulActionWithZero.{u1, u2} R M _inst_1 _inst_3] [sM : Subsingleton.{succ u2} M], IsSMulRegular.{u1, u2} R M (SMulZeroClass.toHasSmul.{u1, u2} R M _inst_3 (SMulWithZero.toSmulZeroClass.{u1, u2} R M (MulZeroClass.toHasZero.{u1} R (MulZeroOneClass.toMulZeroClass.{u1} R (MonoidWithZero.toMulZeroOneClass.{u1} R _inst_1))) _inst_3 (MulActionWithZero.toSMulWithZero.{u1, u2} R M _inst_1 _inst_3 _inst_4))) (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R (MulZeroOneClass.toMulZeroClass.{u1} R (MonoidWithZero.toMulZeroOneClass.{u1} R _inst_1))))))
but is expected to have type
  forall {R : Type.{u1}} {M : Type.{u2}} [_inst_1 : MonoidWithZero.{u1} R] [_inst_3 : Zero.{u2} M] [_inst_4 : MulActionWithZero.{u1, u2} R M _inst_1 _inst_3] [sM : Subsingleton.{succ u2} M], IsSMulRegular.{u1, u2} R M (SMulZeroClass.toSMul.{u1, u2} R M _inst_3 (SMulWithZero.toSMulZeroClass.{u1, u2} R M (MonoidWithZero.toZero.{u1} R _inst_1) _inst_3 (MulActionWithZero.toSMulWithZero.{u1, u2} R M _inst_1 _inst_3 _inst_4))) (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (MonoidWithZero.toZero.{u1} R _inst_1)))
Case conversion may be inaccurate. Consider using '#align is_smul_regular.zero IsSMulRegular.zeroₓ'. -/
/-- The element `0` is `M`-regular when `M` is trivial. -/
theorem zero [sM : Subsingleton M] : IsSMulRegular M (0 : R) :=
  zero_iff_subsingleton.mpr sM
#align is_smul_regular.zero IsSMulRegular.zero

/- warning: is_smul_regular.not_zero -> IsSMulRegular.not_zero is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {M : Type.{u2}} [_inst_1 : MonoidWithZero.{u1} R] [_inst_3 : Zero.{u2} M] [_inst_4 : MulActionWithZero.{u1, u2} R M _inst_1 _inst_3] [nM : Nontrivial.{u2} M], Not (IsSMulRegular.{u1, u2} R M (SMulZeroClass.toHasSmul.{u1, u2} R M _inst_3 (SMulWithZero.toSmulZeroClass.{u1, u2} R M (MulZeroClass.toHasZero.{u1} R (MulZeroOneClass.toMulZeroClass.{u1} R (MonoidWithZero.toMulZeroOneClass.{u1} R _inst_1))) _inst_3 (MulActionWithZero.toSMulWithZero.{u1, u2} R M _inst_1 _inst_3 _inst_4))) (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R (MulZeroOneClass.toMulZeroClass.{u1} R (MonoidWithZero.toMulZeroOneClass.{u1} R _inst_1)))))))
but is expected to have type
  forall {R : Type.{u1}} {M : Type.{u2}} [_inst_1 : MonoidWithZero.{u1} R] [_inst_3 : Zero.{u2} M] [_inst_4 : MulActionWithZero.{u1, u2} R M _inst_1 _inst_3] [nM : Nontrivial.{u2} M], Not (IsSMulRegular.{u1, u2} R M (SMulZeroClass.toSMul.{u1, u2} R M _inst_3 (SMulWithZero.toSMulZeroClass.{u1, u2} R M (MonoidWithZero.toZero.{u1} R _inst_1) _inst_3 (MulActionWithZero.toSMulWithZero.{u1, u2} R M _inst_1 _inst_3 _inst_4))) (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (MonoidWithZero.toZero.{u1} R _inst_1))))
Case conversion may be inaccurate. Consider using '#align is_smul_regular.not_zero IsSMulRegular.not_zeroₓ'. -/
/-- The `0` element is not `M`-regular, on a non-trivial module. -/
theorem not_zero [nM : Nontrivial M] : ¬IsSMulRegular M (0 : R) :=
  not_zero_iff.mpr nM
#align is_smul_regular.not_zero IsSMulRegular.not_zero

end MonoidWithZero

section CommSemigroup

variable [CommSemigroup R] [SMul R M] [IsScalarTower R R M]

/- warning: is_smul_regular.mul_iff -> IsSMulRegular.mul_iff is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {M : Type.{u2}} {a : R} {b : R} [_inst_1 : CommSemigroup.{u1} R] [_inst_2 : SMul.{u1, u2} R M] [_inst_3 : IsScalarTower.{u1, u1, u2} R R M (Mul.toSMul.{u1} R (Semigroup.toHasMul.{u1} R (CommSemigroup.toSemigroup.{u1} R _inst_1))) _inst_2 _inst_2], Iff (IsSMulRegular.{u1, u2} R M _inst_2 (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Semigroup.toHasMul.{u1} R (CommSemigroup.toSemigroup.{u1} R _inst_1))) a b)) (And (IsSMulRegular.{u1, u2} R M _inst_2 a) (IsSMulRegular.{u1, u2} R M _inst_2 b))
but is expected to have type
  forall {R : Type.{u2}} {M : Type.{u1}} {a : R} {b : R} [_inst_1 : CommSemigroup.{u2} R] [_inst_2 : SMul.{u2, u1} R M] [_inst_3 : IsScalarTower.{u2, u2, u1} R R M (Mul.toSMul.{u2} R (Semigroup.toMul.{u2} R (CommSemigroup.toSemigroup.{u2} R _inst_1))) _inst_2 _inst_2], Iff (IsSMulRegular.{u2, u1} R M _inst_2 (HMul.hMul.{u2, u2, u2} R R R (instHMul.{u2} R (Semigroup.toMul.{u2} R (CommSemigroup.toSemigroup.{u2} R _inst_1))) a b)) (And (IsSMulRegular.{u2, u1} R M _inst_2 a) (IsSMulRegular.{u2, u1} R M _inst_2 b))
Case conversion may be inaccurate. Consider using '#align is_smul_regular.mul_iff IsSMulRegular.mul_iffₓ'. -/
/-- A product is `M`-regular if and only if the factors are. -/
theorem mul_iff : IsSMulRegular M (a * b) ↔ IsSMulRegular M a ∧ IsSMulRegular M b :=
  by
  rw [← mul_and_mul_iff]
  exact ⟨fun ab => ⟨ab, by rwa [mul_comm]⟩, fun rab => rab.1⟩
#align is_smul_regular.mul_iff IsSMulRegular.mul_iff

end CommSemigroup

end IsSMulRegular

section Group

variable {G : Type _} [Group G]

#print isSMulRegular_of_group /-
/-- An element of a group acting on a Type is regular. This relies on the availability
of the inverse given by groups, since there is no `left_cancel_smul` typeclass. -/
theorem isSMulRegular_of_group [MulAction G R] (g : G) : IsSMulRegular R g :=
  by
  intro x y h
  convert congr_arg ((· • ·) g⁻¹) h using 1 <;> simp [← smul_assoc]
#align is_smul_regular_of_group isSMulRegular_of_group
-/

end Group

section Units

variable [Monoid R] [MulAction R M]

/- warning: units.is_smul_regular -> Units.isSMulRegular is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} (M : Type.{u2}) [_inst_1 : Monoid.{u1} R] [_inst_2 : MulAction.{u1, u2} R M _inst_1] (a : Units.{u1} R _inst_1), IsSMulRegular.{u1, u2} R M (MulAction.toHasSmul.{u1, u2} R M _inst_1 _inst_2) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (Units.{u1} R _inst_1) R (HasLiftT.mk.{succ u1, succ u1} (Units.{u1} R _inst_1) R (CoeTCₓ.coe.{succ u1, succ u1} (Units.{u1} R _inst_1) R (coeBase.{succ u1, succ u1} (Units.{u1} R _inst_1) R (Units.hasCoe.{u1} R _inst_1)))) a)
but is expected to have type
  forall {R : Type.{u2}} (M : Type.{u1}) [_inst_1 : Monoid.{u2} R] [_inst_2 : MulAction.{u2, u1} R M _inst_1] (a : Units.{u2} R _inst_1), IsSMulRegular.{u2, u1} R M (MulAction.toSMul.{u2, u1} R M _inst_1 _inst_2) (Units.val.{u2} R _inst_1 a)
Case conversion may be inaccurate. Consider using '#align units.is_smul_regular Units.isSMulRegularₓ'. -/
/-- Any element in `Rˣ` is `M`-regular. -/
theorem Units.isSMulRegular (a : Rˣ) : IsSMulRegular M (a : R) :=
  IsSMulRegular.of_mul_eq_one a.inv_val
#align units.is_smul_regular Units.isSMulRegular

/- warning: is_unit.is_smul_regular -> IsUnit.isSMulRegular is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} (M : Type.{u2}) {a : R} [_inst_1 : Monoid.{u1} R] [_inst_2 : MulAction.{u1, u2} R M _inst_1], (IsUnit.{u1} R _inst_1 a) -> (IsSMulRegular.{u1, u2} R M (MulAction.toHasSmul.{u1, u2} R M _inst_1 _inst_2) a)
but is expected to have type
  forall {R : Type.{u2}} (M : Type.{u1}) {a : R} [_inst_1 : Monoid.{u2} R] [_inst_2 : MulAction.{u2, u1} R M _inst_1], (IsUnit.{u2} R _inst_1 a) -> (IsSMulRegular.{u2, u1} R M (MulAction.toSMul.{u2, u1} R M _inst_1 _inst_2) a)
Case conversion may be inaccurate. Consider using '#align is_unit.is_smul_regular IsUnit.isSMulRegularₓ'. -/
/-- A unit is `M`-regular. -/
theorem IsUnit.isSMulRegular (ua : IsUnit a) : IsSMulRegular M a :=
  by
  rcases ua with ⟨a, rfl⟩
  exact a.is_smul_regular M
#align is_unit.is_smul_regular IsUnit.isSMulRegular

end Units


/-
  Induced map from Spec(B) to Spec(A).

  https://stacks.math.columbia.edu/tag/00E2
-/

import topology.basic
import ring_theory.ideal_operations
import spectrum_of_a_ring.zariski_topology

open lattice

universes u v

variables {α : Type u} {β : Type v} [comm_ring α] [comm_ring β]
variables (f : α → β) [is_ring_hom f]

-- Given φ : A → B, we have Spec(φ) : Spec(B) → Spec(A), 𝔭′⟼φ⁻¹(𝔭′).

def Zariski.induced : Spec β → Spec α :=
λ ⟨P, HP⟩, ⟨ideal.comap f P, @ideal.is_prime.comap _ _ _ _ f _ P HP⟩

-- This induced map is continuous.

lemma Zariski.induced.continuous : continuous (Zariski.induced f) :=
begin
  rintros U ⟨E, HE⟩,
  use [f '' E],
  apply set.ext,
  rintros ⟨I, PI⟩,
  split,
  { intros HI HC,
    suffices HfI : Zariski.induced f ⟨I, PI⟩ ∈ Spec.V E,
      rw HE at HfI,
      apply HfI,
      exact HC,
    intros x Hx,
    simp [Zariski.induced] at *,
    have HfE : f '' E ⊆ I := HI,
    have Hfx : f x ∈ f '' E := set.mem_image_of_mem f Hx,
    exact (HfE Hfx), },
  { rintros HI x ⟨y, ⟨Hy, Hfy⟩⟩,
    suffices HfI : Zariski.induced f ⟨I, PI⟩ ∈ Spec.V E,
      rw ←Hfy,
      exact (HfI Hy),
    intros z Hz,
    simp [Zariski.induced] at *,
    replace HI : _ ∈ -U := HI,
    rw ←HE at HI,
    exact (HI Hz), }
end

theorem Zariski.induced.preimage_D (x : α)
: Zariski.induced f ⁻¹' (Spec.D' x) = Spec.D' (f x) :=
set.ext $ λ ⟨P, HP⟩,
by simp [Spec.D', Spec.V', Zariski.induced]


-- Asociatividad_del_supremo.lean
-- Si R es un retículo y x, y, z ∈ R, entonces (x ⊔ y) ⊔ z = x ⊔ (y ⊔ z)
-- José A. Alonso Jiménez <https://jaalonso.github.io>
-- Sevilla, 19-octubre-2022
-- ---------------------------------------------------------------------

import order.lattice

variables {R : Type*} [lattice R]
variables x y z : R

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

example : (x ⊔ y) ⊔ z = x ⊔ (y ⊔ z) :=
begin
  have h1 : (x ⊔ y) ⊔ z ≤ x ⊔ (y ⊔ z),
    { have h1a : x ⊔ y ≤ x ⊔ (y ⊔ z), by finish,
      have h1b : z ≤ x ⊔ (y ⊔ z), by finish,
      show (x ⊔ y) ⊔ z ≤ x ⊔ (y ⊔ z),
        by exact sup_le h1a h1b, },
  have h2 : x ⊔ (y ⊔ z) ≤ (x ⊔ y) ⊔ z,
    { have h2a : x ≤ (x ⊔ y) ⊔ z, by finish,
      have h2b : y ⊔ z ≤ (x ⊔ y) ⊔ z, by finish,
      show x ⊔ (y ⊔ z) ≤ (x ⊔ y) ⊔ z,
        by exact sup_le h2a h2b, },
  show (x ⊔ y) ⊔ z = x ⊔ (y ⊔ z),
    by exact le_antisymm h1 h2,
end

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

example : (x ⊔ y) ⊔ z = x ⊔ (y ⊔ z) :=
begin
  have h1 : (x ⊔ y) ⊔ z ≤ x ⊔ (y ⊔ z),
    { have h1a : x ⊔ y ≤ x ⊔ (y ⊔ z),
        { have h1a1 : x ≤ x ⊔ (y ⊔ z) :=
            le_sup_left,
          have h1a2 : y ≤ x ⊔ (y ⊔ z), calc
            y ≤ y ⊔ z         : le_sup_left
            ... ≤ x ⊔ (y ⊔ z) : le_sup_right,
          show x ⊔ y ≤ x ⊔ (y ⊔ z),
            by exact sup_le h1a1 h1a2, },
      have h1b : z ≤ x ⊔ (y ⊔ z), calc
        z   ≤ y ⊔ z       : le_sup_right
        ... ≤ x ⊔ (y ⊔ z) : le_sup_right,
      show (x ⊔ y) ⊔ z ≤ x ⊔ (y ⊔ z),
        by exact sup_le h1a h1b, },
  have h2 : x ⊔ (y ⊔ z) ≤ (x ⊔ y) ⊔ z,
    { have h2a : x ≤ (x ⊔ y) ⊔ z, calc
        x   ≤ x ⊔ y       : le_sup_left
        ... ≤ (x ⊔ y) ⊔ z : le_sup_left,
      have h2b : y ⊔ z ≤ (x ⊔ y) ⊔ z,
        { have h2b1 : y ≤ (x ⊔ y) ⊔ z, calc
            y   ≤ x ⊔ y       : le_sup_right
            ... ≤ (x ⊔ y) ⊔ z : le_sup_left,
          have h2b2 : z ≤ (x ⊔ y) ⊔ z :=
            le_sup_right,
          show y ⊔ z ≤ (x ⊔ y) ⊔ z,
            by exact sup_le h2b1 h2b2, },
      show x ⊔ (y ⊔ z) ≤ (x ⊔ y) ⊔ z,
        by exact sup_le h2a h2b, },
  show (x ⊔ y) ⊔ z = x ⊔ (y ⊔ z),
    by exact le_antisymm h1 h2,
end

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

example : (x ⊔ y) ⊔ z = x ⊔ (y ⊔ z) :=
begin
  apply le_antisymm,
  { apply sup_le,
    { apply sup_le le_sup_left (le_sup_of_le_right le_sup_left)},
    { apply le_sup_of_le_right le_sup_right}},
  { apply sup_le,
    { apply le_sup_of_le_left le_sup_left},
    { apply sup_le (le_sup_of_le_left le_sup_right) le_sup_right}},
end

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

example : (x ⊔ y) ⊔ z = x ⊔ (y ⊔ z) :=
le_antisymm
  (sup_le
    (sup_le le_sup_left (le_sup_of_le_right le_sup_left))
    (le_sup_of_le_right le_sup_right))
  (sup_le
    (le_sup_of_le_left le_sup_left)
    (sup_le (le_sup_of_le_left le_sup_right) le_sup_right))

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

example : x ⊔ y ⊔ z = x ⊔ (y ⊔ z) :=
-- by library_search
sup_assoc

-- 6ª demostración
-- ===============

example : x ⊔ y ⊔ z = x ⊔ (y ⊔ z) :=
-- by hint
by finish


import super
open tactic

def prime (n : ℕ) := ∀ d, d ∣ n → d = 1 ∨ d = n

lemma nat_mul_cancel_one {m n : ℕ} : m ≠ 0 → m * n = m → n = 1 :=
by cases m; super nat.zero_lt_succ nat.eq_of_mul_eq_mul_left nat.mul_one

lemma not_prime_zero : ¬ prime 0 :=
by intro h; cases h 2 ⟨0, rfl⟩; cases h_1

@[simp] lemma nat.dvd_refl (m : ℕ) : m ∣ m := ⟨1, by simp [nat.mul_one]⟩
@[simp] theorem nat.dvd_mul_left (a b : ℕ) : a ∣ b * a := ⟨b, nat.mul_comm _ _⟩

example {m n : ℕ} : prime (m * n) → m = 1 ∨ n = 1 :=
by super with prime nat.dvd_refl nat.dvd_mul_right nat.dvd_mul_left
nat_mul_cancel_one not_prime_zero nat.mul_zero nat.zero_mul

example : nat.zero ≠ nat.succ nat.zero := by super
example (x y : ℕ) : nat.succ x = nat.succ y → x = y := by super
example (i) (a b c : i) : [a,b,c] = [b,c,a] -> a = b ∧ b = c := by super

definition is_positive (n : ℕ) := n > 0
example (n : ℕ) : n > 0 ↔ is_positive n := by super with is_positive

example (m n : ℕ) : 0 + m = 0 + n → m = n :=
by super with nat.zero_add

example : ∀x y : ℕ, x + y = y + x :=
begin intros, have h : nat.zero = 0 := rfl, induction x,
      super with nat.add_zero nat.zero_add,
      super with nat.add_succ nat.succ_add end

example (i) [inhabited i] : nonempty i := by super
example (i) [nonempty i] : ¬(inhabited i → false) := by super

example : nonempty ℕ := by super
example : ¬(inhabited ℕ → false) := by super

example {a b} : ¬(b ∨ ¬a) ∨ (a → b) := by super
example {a} : a ∨ ¬a := by super
example {a} : (a ∧ a) ∨ (¬a ∧ ¬a) := by super
example (i) (c : i) (p : i → Prop) (f : i → i) :
  p c → (∀x, p x → p (f x)) → p (f (f (f c))) := by super

example (i) (p : i → Prop) : ∀x, p x → ∃x, p x := by super

example (i) [nonempty i] (p : i → i → Prop) : (∀x y, p x y) → ∃x, ∀z, p x z := by super

example (i) [nonempty i] (p : i → Prop) : (∀x, p x) → ¬¬∀x, p x := by super

-- Requires non-empty domain.
example {i} [nonempty i] (p : i → Prop) :
  (∀x y, p x ∨ p y) → ∃x y, p x ∧ p y := by super

example (i) (a b : i) (p : i → Prop) (H : a = b) : p b → p a :=
by super

example (i) (a b : i) (p : i → Prop) (H : a = b) : p a → p b :=
by super

example (i) (a b : i) (p : i → Prop) (H : a = b) : p b = p a :=
by super

example (i) (c : i) (p : i → Prop) (f g : i → i) :
p c → (∀x, p x → p (f x)) → (∀x, p x → f x = g x) → f (f c) = g (g c) :=
by super

example (i) (p q : i → i → Prop) (a b c d : i) :
  (∀x y z, p x y ∧ p y z → p x z) →
  (∀x y z, q x y ∧ q y z → q x z) →
  (∀x y, q x y → q y x) →
  (∀x y, p x y ∨ q x y) →
  p a b ∨ q c d :=
by super

-- This example from Davis-Putnam actually requires a non-empty domain

example (i) [nonempty i] (f g : i → i → Prop) :
  ∃x y, ∀z, (f x y → f y z ∧ f z z) ∧ (f x y ∧ g x y → g x z ∧ g z z) :=
by super

example (person) [nonempty person] (drinks : person → Prop) :
  ∃canary, drinks canary → ∀other, drinks other := by super

example {p q : ℕ → Prop} {r} : (∀x y, p x ∧ q y ∧ r) -> ∀x, (p x ∧ r ∧ q x) := by super


/-
Copyright (c) 2020 Zhouhang Zhou. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Zhouhang Zhou
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.order.bounds
import Mathlib.data.set.intervals.image_preimage
import Mathlib.PostPort

universes u

namespace Mathlib

/-!
# Intervals without endpoints ordering

In any decidable linear order `α`, we define the set of elements lying between two elements `a` and
`b` as `Icc (min a b) (max a b)`.

`Icc a b` requires the assumption `a ≤ b` to be meaningful, which is sometimes inconvenient. The
interval as defined in this file is always the set of things lying between `a` and `b`, regardless
of the relative order of `a` and `b`.

For real numbers, `Icc (min a b) (max a b)` is the same as `segment a b`.

## Notation

We use the localized notation `[a, b]` for `interval a b`. One can open the locale `interval` to
make the notation available.

-/

namespace set

/-- `interval a b` is the set of elements lying between `a` and `b`, with `a` and `b` included. -/
def interval {α : Type u} [linear_order α] (a : α) (b : α) : set α :=
  Icc (min a b) (max a b)

@[simp] theorem interval_of_le {α : Type u} [linear_order α] {a : α} {b : α} (h : a ≤ b) : interval a b = Icc a b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (interval a b = Icc a b)) (interval.equations._eqn_1 a b)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (Icc (min a b) (max a b) = Icc a b)) (min_eq_left h)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (Icc a (max a b) = Icc a b)) (max_eq_right h))) (Eq.refl (Icc a b))))

@[simp] theorem interval_of_ge {α : Type u} [linear_order α] {a : α} {b : α} (h : b ≤ a) : interval a b = Icc b a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (interval a b = Icc b a)) (interval.equations._eqn_1 a b)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (Icc (min a b) (max a b) = Icc b a)) (min_eq_right h)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (Icc b (max a b) = Icc b a)) (max_eq_left h))) (Eq.refl (Icc b a))))

theorem interval_swap {α : Type u} [linear_order α] (a : α) (b : α) : interval a b = interval b a := sorry

theorem interval_of_lt {α : Type u} [linear_order α] {a : α} {b : α} (h : a < b) : interval a b = Icc a b :=
  interval_of_le (le_of_lt h)

theorem interval_of_gt {α : Type u} [linear_order α] {a : α} {b : α} (h : b < a) : interval a b = Icc b a :=
  interval_of_ge (le_of_lt h)

theorem interval_of_not_le {α : Type u} [linear_order α] {a : α} {b : α} (h : ¬a ≤ b) : interval a b = Icc b a :=
  interval_of_gt (lt_of_not_ge h)

theorem interval_of_not_ge {α : Type u} [linear_order α] {a : α} {b : α} (h : ¬b ≤ a) : interval a b = Icc a b :=
  interval_of_lt (lt_of_not_ge h)

@[simp] theorem interval_self {α : Type u} [linear_order α] {a : α} : interval a a = singleton a := sorry

@[simp] theorem nonempty_interval {α : Type u} [linear_order α] {a : α} {b : α} : set.nonempty (interval a b) := sorry

@[simp] theorem left_mem_interval {α : Type u} [linear_order α] {a : α} {b : α} : a ∈ interval a b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a ∈ interval a b)) (interval.equations._eqn_1 a b)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a ∈ Icc (min a b) (max a b))) (propext mem_Icc)))
      { left := min_le_left a b, right := le_max_left a b })

@[simp] theorem right_mem_interval {α : Type u} [linear_order α] {a : α} {b : α} : b ∈ interval a b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (b ∈ interval a b)) (interval_swap a b))) left_mem_interval

theorem Icc_subset_interval {α : Type u} [linear_order α] {a : α} {b : α} : Icc a b ⊆ interval a b :=
  id
    fun (x : α) (h : x ∈ Icc a b) =>
      eq.mpr (id (Eq._oldrec (Eq.refl (x ∈ interval a b)) (interval_of_le (le_trans (and.left h) (and.right h))))) h

theorem Icc_subset_interval' {α : Type u} [linear_order α] {a : α} {b : α} : Icc b a ⊆ interval a b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (Icc b a ⊆ interval a b)) (interval_swap a b))) Icc_subset_interval

theorem mem_interval_of_le {α : Type u} [linear_order α] {a : α} {b : α} {x : α} (ha : a ≤ x) (hb : x ≤ b) : x ∈ interval a b :=
  Icc_subset_interval { left := ha, right := hb }

theorem mem_interval_of_ge {α : Type u} [linear_order α] {a : α} {b : α} {x : α} (hb : b ≤ x) (ha : x ≤ a) : x ∈ interval a b :=
  Icc_subset_interval' { left := hb, right := ha }

theorem interval_subset_interval {α : Type u} [linear_order α] {a₁ : α} {a₂ : α} {b₁ : α} {b₂ : α} (h₁ : a₁ ∈ interval a₂ b₂) (h₂ : b₁ ∈ interval a₂ b₂) : interval a₁ b₁ ⊆ interval a₂ b₂ :=
  Icc_subset_Icc (le_min (and.left h₁) (and.left h₂)) (max_le (and.right h₁) (and.right h₂))

theorem interval_subset_interval_iff_mem {α : Type u} [linear_order α] {a₁ : α} {a₂ : α} {b₁ : α} {b₂ : α} : interval a₁ b₁ ⊆ interval a₂ b₂ ↔ a₁ ∈ interval a₂ b₂ ∧ b₁ ∈ interval a₂ b₂ :=
  { mp := fun (h : interval a₁ b₁ ⊆ interval a₂ b₂) => { left := h left_mem_interval, right := h right_mem_interval },
    mpr := fun (h : a₁ ∈ interval a₂ b₂ ∧ b₁ ∈ interval a₂ b₂) => interval_subset_interval (and.left h) (and.right h) }

theorem interval_subset_interval_iff_le {α : Type u} [linear_order α] {a₁ : α} {a₂ : α} {b₁ : α} {b₂ : α} : interval a₁ b₁ ⊆ interval a₂ b₂ ↔ min a₂ b₂ ≤ min a₁ b₁ ∧ max a₁ b₁ ≤ max a₂ b₂ := sorry

theorem interval_subset_interval_right {α : Type u} [linear_order α] {a : α} {b : α} {x : α} (h : x ∈ interval a b) : interval x b ⊆ interval a b :=
  interval_subset_interval h right_mem_interval

theorem interval_subset_interval_left {α : Type u} [linear_order α] {a : α} {b : α} {x : α} (h : x ∈ interval a b) : interval a x ⊆ interval a b :=
  interval_subset_interval left_mem_interval h

theorem bdd_below_bdd_above_iff_subset_interval {α : Type u} [linear_order α] (s : set α) : bdd_below s ∧ bdd_above s ↔ ∃ (a : α), ∃ (b : α), s ⊆ interval a b := sorry

@[simp] theorem preimage_const_add_interval {α : Type u} [linear_ordered_add_comm_group α] (a : α) (b : α) (c : α) : (fun (x : α) => a + x) ⁻¹' interval b c = interval (b - a) (c - a) := sorry

@[simp] theorem preimage_add_const_interval {α : Type u} [linear_ordered_add_comm_group α] (a : α) (b : α) (c : α) : (fun (x : α) => x + a) ⁻¹' interval b c = interval (b - a) (c - a) := sorry

@[simp] theorem preimage_neg_interval {α : Type u} [linear_ordered_add_comm_group α] (a : α) (b : α) : -interval a b = interval (-a) (-b) := sorry

@[simp] theorem preimage_sub_const_interval {α : Type u} [linear_ordered_add_comm_group α] (a : α) (b : α) (c : α) : (fun (x : α) => x - a) ⁻¹' interval b c = interval (b + a) (c + a) := sorry

@[simp] theorem preimage_const_sub_interval {α : Type u} [linear_ordered_add_comm_group α] (a : α) (b : α) (c : α) : (fun (x : α) => a - x) ⁻¹' interval b c = interval (a - b) (a - c) := sorry

@[simp] theorem image_const_add_interval {α : Type u} [linear_ordered_add_comm_group α] (a : α) (b : α) (c : α) : (fun (x : α) => a + x) '' interval b c = interval (a + b) (a + c) := sorry

@[simp] theorem image_add_const_interval {α : Type u} [linear_ordered_add_comm_group α] (a : α) (b : α) (c : α) : (fun (x : α) => x + a) '' interval b c = interval (b + a) (c + a) := sorry

@[simp] theorem image_const_sub_interval {α : Type u} [linear_ordered_add_comm_group α] (a : α) (b : α) (c : α) : (fun (x : α) => a - x) '' interval b c = interval (a - b) (a - c) := sorry

@[simp] theorem image_sub_const_interval {α : Type u} [linear_ordered_add_comm_group α] (a : α) (b : α) (c : α) : (fun (x : α) => x - a) '' interval b c = interval (b - a) (c - a) := sorry

theorem image_neg_interval {α : Type u} [linear_ordered_add_comm_group α] (a : α) (b : α) : Neg.neg '' interval a b = interval (-a) (-b) := sorry

/-- If `[x, y]` is a subinterval of `[a, b]`, then the distance between `x` and `y`
is less than or equal to that of `a` and `b` -/
theorem abs_sub_le_of_subinterval {α : Type u} [linear_ordered_add_comm_group α] {a : α} {b : α} {x : α} {y : α} (h : interval x y ⊆ interval a b) : abs (y - x) ≤ abs (b - a) := sorry

/-- If `x ∈ [a, b]`, then the distance between `a` and `x` is less than or equal to
that of `a` and `b`  -/
theorem abs_sub_left_of_mem_interval {α : Type u} [linear_ordered_add_comm_group α] {a : α} {b : α} {x : α} (h : x ∈ interval a b) : abs (x - a) ≤ abs (b - a) :=
  abs_sub_le_of_subinterval (interval_subset_interval_left h)

/-- If `x ∈ [a, b]`, then the distance between `x` and `b` is less than or equal to
that of `a` and `b`  -/
theorem abs_sub_right_of_mem_interval {α : Type u} [linear_ordered_add_comm_group α] {a : α} {b : α} {x : α} (h : x ∈ interval a b) : abs (b - x) ≤ abs (b - a) :=
  abs_sub_le_of_subinterval (interval_subset_interval_right h)

@[simp] theorem preimage_mul_const_interval {k : Type u} [linear_ordered_field k] {a : k} (ha : a ≠ 0) (b : k) (c : k) : (fun (x : k) => x * a) ⁻¹' interval b c = interval (b / a) (c / a) := sorry

@[simp] theorem preimage_const_mul_interval {k : Type u} [linear_ordered_field k] {a : k} (ha : a ≠ 0) (b : k) (c : k) : (fun (x : k) => a * x) ⁻¹' interval b c = interval (b / a) (c / a) := sorry

@[simp] theorem preimage_div_const_interval {k : Type u} [linear_ordered_field k] {a : k} (ha : a ≠ 0) (b : k) (c : k) : (fun (x : k) => x / a) ⁻¹' interval b c = interval (b * a) (c * a) := sorry

@[simp] theorem image_mul_const_interval {k : Type u} [linear_ordered_field k] (a : k) (b : k) (c : k) : (fun (x : k) => x * a) '' interval b c = interval (b * a) (c * a) := sorry

@[simp] theorem image_const_mul_interval {k : Type u} [linear_ordered_field k] (a : k) (b : k) (c : k) : (fun (x : k) => a * x) '' interval b c = interval (a * b) (a * c) := sorry

@[simp] theorem image_div_const_interval {k : Type u} [linear_ordered_field k] (a : k) (b : k) (c : k) : (fun (x : k) => x / a) '' interval b c = interval (b / a) (c / a) :=
  image_mul_const_interval (a⁻¹) b c


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

import tactic
import analysis.normed_space.lp_space -- theory of ℓᵖ spaces

/-

# ℓᵖ spaces

The set-up : `I` is an index type, `E` is a family of `normed_add_comm_group`s
(so if `i : I` then `E i` is a type and if `v : E i` then `‖v‖` makes sense and
is a real number).

Then given `p : ℝ≥0∞` (i.e. an element `p` of `[0,∞]`) there is a theory
of ℓᵖ spaces, which is the subspace of `∏ᵢ, E i` consisting of the
sections `vᵢ` such that `∑ᵢ ‖vᵢ‖ᵖ < ∞`. For `p=∞` this means "the ‖vᵢ‖ are
bounded".

-/

open_locale ennreal -- to get notation ℝ≥0∞

variables (I : Type) (E : I → Type) [∀ i, normed_add_comm_group (E i)] (p : ℝ≥0∞)

-- Here's how to say that an element of the product of the Eᵢ is in the ℓᵖ space

example (v : Π i, E i) : Prop := mem_ℓp v p

-- Technical note: 0^0=1 and x^0=0 for x>0, so ℓ⁰ is the functions with finite support.

variable (v : Π i, E i)

example : mem_ℓp v 0 ↔ set.finite {i | v i ≠ 0} :=
begin
  exact mem_ℓp_zero_iff,
end

example : mem_ℓp v ∞ ↔ bdd_above (set.range (λ i, ‖v i‖)) :=
begin
  exact mem_ℓp_infty_iff,
end

-- The function ennreal.to_real sends x<∞ to x and ∞ to 0.
-- So `0 < p.to_real` is a way of saying `0 < p < ∞`.

example (hp : 0 < p.to_real) :
  mem_ℓp v p ↔ summable (λ i, ‖v i‖ ^ p.to_real) :=
begin
  exact mem_ℓp_gen_iff hp
end

-- It's a theorem in the library that if p ≤ q then mem_ℓp v p → mem_ℓp v q

example (q : ℝ≥0∞) (hpq : p ≤ q) : mem_ℓp v p → mem_ℓp v q :=
begin
  intro h,
  exact h.of_exponent_ge hpq,
end

-- The space of all `v` satisfying `mem_ℓp v p` is
-- called lp E p

example : Type := lp E p

-- It has a norm:

noncomputable example (v : lp E p) : ℝ := ‖v‖

-- It's a `normed_add_comm_group` if `1 ≤ p`

noncomputable example [fact (1 ≤ p)] : normed_add_comm_group (lp E p) :=
begin
  apply_instance, -- Typeclass inference can't see 1 ≤ p unless we
                  -- use the `fact` typeclass (a way of putting arbitrary facts)
                  -- into the system
end

-- `real.is_conjugate_exponent p q` means that `p,q>1` are reals and `1/p+1/q=1`

example (p q : ℝ) (hp : 1 < p) (hq : 1 < q) (hpq : 1 / p + 1 / q = 1) :
  p.is_conjugate_exponent q :=
{ one_lt := hp,
  inv_add_inv_conj := hpq } -- note that `hq` not needed as it follows

-- We have a verison of Hoelder's inequality.

#check @lp.tsum_mul_le_mul_norm

example (q : ℝ≥0∞) (hpq : p.to_real.is_conjugate_exponent q.to_real) (f : lp E p)
  (g : lp E q) : ∑' (i : I), ‖f i‖ * ‖g i‖ ≤ ‖f‖ * ‖g‖ :=
begin
  have := lp.tsum_mul_le_mul_norm hpq f g,
  exact this.2,
end

-- This would be a useless theorem if `∑' (i : I), ‖f i‖ * ‖g i‖` diverged,
-- because in Lean if a sum diverges then by definition the `∑'` of it is 0.
-- So we also need this:

example (q : ℝ≥0∞) (hpq : p.to_real.is_conjugate_exponent q.to_real) (f : lp E p)
  (g : lp E q) : summable (λ i, ‖f i‖ * ‖g i‖) :=
begin
  have := lp.tsum_mul_le_mul_norm hpq f g,
  exact this.1,
end


import Chap5
namespace HTPI
set_option pp.funBinderTypes true

/- Section 5.1 -/
-- 1.
theorem func_from_graph_ltr {A B : Type} (F : Set (A × B)) :
    (∃ (f : A → B), graph f = F) → is_func_graph F := sorry

-- 2.
theorem Exercise_5_1_13a
    {A B C : Type} (R : Set (A × B)) (S : Set (B × C)) (f : A → C)
    (h1 : ∀ (b : B), b ∈ Ran R ∧ b ∈ Dom S) (h2 : graph f = comp S R) :
    is_func_graph S := sorry

-- 3.
theorem Exercise_5_1_14a
    {A B : Type} (f : A → B) (R : BinRel A) (S : BinRel B)
    (h : ∀ (x y : A), R x y ↔ S (f x) (f y)) :
    reflexive S → reflexive R := sorry

-- 4.
--You might not be able to complete this proof
theorem Exercise_5_1_15a
    {A B : Type} (f : A → B) (R : BinRel A) (S : BinRel B)
    (h : ∀ (x y : B), S x y ↔ ∃ (u v : A), f u = x ∧ f v = y ∧ R u v) :
    reflexive R → reflexive S := sorry

-- 5.
--You might not be able to complete this proof
theorem Exercise_5_1_15c
    {A B : Type} (f : A → B) (R : BinRel A) (S : BinRel B)
    (h : ∀ (x y : B), S x y ↔ ∃ (u v : A), f u = x ∧ f v = y ∧ R u v) :
    transitive R → transitive S := sorry

-- 6.
theorem Exercise_5_1_16b
    {A B : Type} (R : BinRel B) (S : BinRel (A → B))
    (h : ∀ (f g : A → B), S f g ↔ ∀ (x : A), R (f x) (g x)) :
    symmetric R → symmetric S := sorry

-- 7.
theorem Exercise_5_1_17a {A : Type} (f : A → A) (a : A)
    (h : ∀ (x : A), f x = a) : ∀ (g : A → A), f ∘ g = f := sorry

-- 8.
theorem Exercise_5_1_17b {A : Type} (f : A → A) (a : A)
    (h : ∀ (g : A → A), f ∘ g = f) :
    ∃ (y : A), ∀ (x : A), f x = y := sorry

/- Section 5.2 -/
-- 1.
theorem Exercise_5_2_10a {A B C : Type} (f: A → B) (g : B → C) :
    onto (g ∘ f) → onto g := sorry

-- 2.
theorem Exercise_5_2_10b {A B C : Type} (f: A → B) (g : B → C) :
    one_to_one (g ∘ f) → one_to_one f := sorry

-- 3.
theorem Exercise_5_2_11a {A B C : Type} (f: A → B) (g : B → C) :
    onto f → ¬(one_to_one g) → ¬(one_to_one (g ∘ f)) := sorry

-- 4.
theorem Exercise_5_2_11b {A B C : Type} (f: A → B) (g : B → C) :
    ¬(onto f) → one_to_one g → ¬(onto (g ∘ f)) := sorry

-- 5.
theorem Exercise_5_2_12 {A B : Type} (f : A → B) (g : B → Set A)
    (h : ∀ (b : B), g b = { a : A | f a = b }) :
    onto f → one_to_one g := sorry

-- 6.
theorem Exercise_5_2_16 {A B C : Type}
    (R : Set (A × B)) (S : Set (B × C)) (f : A → C) (g : B → C)
    (h1 : graph f = comp S R) (h2 : graph g = S) (h3 : one_to_one g) :
    is_func_graph R := sorry

-- 7.
theorem Exercise_5_2_17a
    {A B : Type} (f : A → B) (R : BinRel A) (S : BinRel B)
    (h1 : ∀ (x y : B), S x y ↔ ∃ (u v : A), f u = x ∧ f v = y ∧ R u v)
    (h2 : onto f) : reflexive R → reflexive S := sorry

-- 8.
theorem Exercise_5_2_17b
    {A B : Type} (f : A → B) (R : BinRel A) (S : BinRel B)
    (h1 : ∀ (x y : B), S x y ↔ ∃ (u v : A), f u = x ∧ f v = y ∧ R u v)
    (h2 : one_to_one f) : transitive R → transitive S := sorry

-- 9.
theorem Exercise_5_2_21a {A B C : Type} (f : B → C) (g h : A → B)
    (h1 : one_to_one f) (h2 : f ∘ g = f ∘ h) : g = h := sorry

-- 10.
theorem Exercise_5_2_21b {A B C : Type} (f : B → C) (a : A)
    (h1 : ∀ (g h : A → B), f ∘ g = f ∘ h → g = h) :
    one_to_one f := sorry

/- Section 5.3 -/
-- 1.
theorem Theorem_5_3_2_2_ex {A B : Type} (f : A → B) (g : B → A)
    (h1 : graph g = inv (graph f)) : f ∘ g = id := sorry

-- 2.
theorem Theorem_5_3_3_2_ex {A B : Type} (f : A → B) :
    (∃ (g : B → A), f ∘ g = id) → onto f := sorry

-- 3.
theorem Exercise_5_3_11a {A B : Type} (f : A → B) (g : B → A) :
    one_to_one f → f ∘ g = id → graph g = inv (graph f) := sorry

-- 4.
theorem Exercise_5_3_11b {A B : Type} (f : A → B) (g : B → A) :
    onto f → g ∘ f = id → graph g = inv (graph f) := sorry

-- 5.
theorem Exercise_5_3_14a {A B : Type} (f : A → B) (g : B → A)
    (h : f ∘ g = id) : ∀ x ∈ Ran (graph g), g (f x) = x := sorry

-- 6.
theorem Exercise_5_3_18 {A B C : Type} (f : A → C) (g : B → C)
    (h1 : one_to_one g) (h2 : onto g) :
    ∃ (h : A → B), g ∘ h = f := sorry

-- Definition for next two exercises:
def conj (A : Type) (f1 f2 : A → A) : Prop :=
    ∃ (g g' : A → A), (f1 = g' ∘ f2 ∘ g) ∧ (g ∘ g' = id) ∧ (g' ∘ g = id)

-- 7.
theorem Exercise_5_3_17a {A : Type} : symmetric (conj A) := sorry

-- 8.
theorem Exercise_5_3_17b {A : Type} (f1 f2 : A → A)
    (h1 : conj A f1 f2) (h2 : ∃ (a : A), f1 a = a) :
    ∃ (a : A), f2 a = a := sorry

/- Section 5.4 -/
-- 1.
example {A : Type} (F : Set (Set A)) (B : Set A) :
    smallestElt (sub A) B F → B = ⋂₀ F := sorry

-- 2.
def complement {A : Type} (B : Set A) : Set A := { a : A | a ∉ B }

theorem Exercise_5_4_7 {A : Type} (f g : A → A) (C : Set A)
    (h1 : f ∘ g = id) (h2 : closed f C) : closed g (complement C) := sorry

-- 3.
theorem Exercise_5_4_9a {A : Type} (f : A → A) (C1 C2 : Set A)
    (h1 : closed f C1) (h2 : closed f C2) : closed f (C1 ∪ C2) := sorry

-- 4.
theorem Exercise_5_4_10a {A : Type} (f : A → A) (B1 B2 C1 C2 : Set A)
    (h1 : closure f B1 C1) (h2 : closure f B2 C2) :
    B1 ⊆ B2 → C1 ⊆ C2 := sorry

-- 5.
theorem Exercise_5_4_10b {A : Type} (f : A → A) (B1 B2 C1 C2 : Set A)
    (h1 : closure f B1 C1) (h2 : closure f B2 C2) :
    closure f (B1 ∪ B2) (C1 ∪ C2) := sorry

-- 6.
theorem Theorem_5_4_9_ex {A : Type} (f : A → A → A) (B : Set A) :
    ∃ (C : Set A), closure2 f B C := sorry

-- 7.
theorem Exercise_5_4_13a {A : Type} (F : Set (A → A)) (B : Set A) :
    ∃ (C : Set A), closure_family F B C := sorry

/- Section 5.5 -/

--Warning!  Not all of these examples are correct!
example {A B : Type} (f : A → B) (W X : Set A) :
    image f (W ∪ X) = image f W ∪ image f X := sorry

example {A B : Type} (f : A → B) (W X : Set A) :
    image f (W \ X) = image f W \ image f X := sorry

example {A B : Type} (f : A → B) (W X : Set A) :
    W ⊆ X ↔ image f W ⊆ image f X := sorry

example {A B : Type} (f : A → B) (Y Z : Set B) :
    inverse_image f  (Y ∩ Z) =
        inverse_image f Y ∩ inverse_image f Z := sorry

example {A B : Type} (f : A → B) (Y Z : Set B) :
    inverse_image f  (Y ∪ Z) =
        inverse_image f Y ∪ inverse_image f Z := sorry

example {A B : Type} (f : A → B) (Y Z : Set B) :
    inverse_image f  (Y \ Z) =
        inverse_image f Y \ inverse_image f Z := sorry

example {A B : Type} (f : A → B) (Y Z : Set B) :
    Y ⊆ Z ↔ inverse_image f Y ⊆ inverse_image f Z := sorry

example {A B : Type} (f : A → B) (X : Set A) :
    inverse_image f (image f X) = X := sorry

example {A B : Type} (f : A → B) (Y : Set B) :
    image f (inverse_image f Y) = Y := sorry

example {A : Type} (f : A → A) (C : Set A) :
    closed f C → image f C ⊆ C := sorry

example {A : Type} (f : A → A) (C : Set A) :
    image f C ⊆ C → C ⊆ inverse_image f C := sorry

example {A : Type} (f : A → A) (C : Set A) :
    C ⊆ inverse_image f C → closed f C := sorry

example {A B : Type} (f : A → B) (g : B → A) (Y : Set B)
    (h1 : f ∘ g = id) (h2 : g ∘ f = id) :
    inverse_image f Y = image g Y := sorry


/-
1. Prove these equivalences:
-/

variables (α : Type) (p q : α → Prop)

example : (∀ x, p x ∧ q x) ↔ (∀ x, p x) ∧ (∀ x, q x) :=
    iff.intro
    (
        assume h : ∀ x, p x ∧ q x,
        show (∀ x, p x) ∧ (∀ x, q x), from
            and.intro
            (
                show ∀ x, p x, from
                    assume x,
                    (h x).left
            )
            (
                show ∀ x, q x, from
                    assume x,
                    (h x).right
            )
    )
    (
        assume h : (∀ x, p x) ∧ (∀ x, q x),
        show ∀ x, p x ∧ q x, from
            assume x,
            and.intro (h.left x) (h.right x)
    )
-- short version
example : (∀ x, p x ∧ q x) ↔ (∀ x, p x) ∧ (∀ x, q x) :=
    iff.intro
    (λ h, and.intro (λ x, (h x).left) (λ x, (h x).right))
    (λ h, λ x, and.intro (h.left x) (h.right x))

example : (∀ x, p x → q x) → (∀ x, p x) → (∀ x, q x) :=
    assume h : ∀ x, p x → q x,
    assume g : ∀ x, p x,
    show ∀ x, q x, from
        assume x,
        show q x, from (h x) (g x)
-- short version
example : (∀ x, p x → q x) → (∀ x, p x) → (∀ x, q x) :=
    λ h, λ g, λ x, (h x) (g x)

example : (∀ x, p x) ∨ (∀ x, q x) → ∀ x, p x ∨ q x :=
    assume h : (∀ x, p x) ∨ (∀ x, q x),
    show ∀ x, p x ∨ q x, from
        assume x,
        h.elim
        (
            assume h1 : ∀ x, p x,
            show p x ∨ q x, from
                or.inl (h1 x)
        )
        (
            assume h2 : ∀ x, q x,
            show p x ∨ q x, from
                or.inr (h2 x)
        )
-- short version
example : (∀ x, p x) ∨ (∀ x, q x) → ∀ x, p x ∨ q x :=
    λ h, λ x, h.elim (λ h1, or.inl (h1 x)) (λ h2, or.inr (h2 x))


import algebra.big_operators algebra.group_power chris_hughes_various.zmod data.fintype data.nat.gcd M3P14.order_zmodn_kmb M3P14.Arithmetic_functions.mobius

open nat
open fintype

--TODO: Add explicit formula for τ n
-- make the non-computable definition of mobius function work
-- add the mobius inversion formula

-- arithmetic functions and their properties

def is_mult (f : ℕ → ℕ) (m n : ℕ) (hp: gcd m n = 1) := f (m * n) = (f m) * (f n)
def is_strong_mult (f : ℕ → ℕ) (m n : ℕ) := f (m * n) = (f m) * (f n)
def is_add (f : ℕ → ℤ) (m n : ℕ) (hp: gcd m n = 1) := f (m + n) = (f m) + (f n)
def is_strong_add (f : ℕ → ℤ) (m n : ℕ) := f (m + n) = (f m) + (f n)

-- minor arithmetic functions that nobody cares about probably

--liouville function

def liouville_function (n : ℕ) : int := (-1)^(primes_div_dup n)
local notation `δ` := liouville_function
-- lambda was already taken up by lambda functions
theorem lio_strong_mul (n m : ℕ) : δ (m * n) = (δ m) * (δ n) := sorry

--number of divisors

def number_of_divisors_function (n : ℕ) := n.factors.erase_dup.length
local notation `τ` := number_of_divisors_function

theorem tau_is_mul (n m : ℕ) (hp: gcd n m = 1) : τ (n*m) = (τ n) * (τ m) := sorry

--theorem tau_formula (n α : ℕ)
    -- ((range n.succ).filter (∣ n))


/-
Copyright (c) 2021 Chris Hughes. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Chris Hughes
-/
import data.mv_polynomial.cardinal
import data.mv_polynomial.equiv
/-!
# Cardinality of Polynomial Ring

The reuslt in this file is that the cardinality of `polynomial R` is at most the maximum
of `#R` and `ω`.
-/
universe u

open_locale cardinal polynomial
open cardinal

namespace polynomial

lemma cardinal_mk_le_max {R : Type u} [comm_semiring R] : #R[X] ≤ max (#R) ω :=
calc #R[X] = #(mv_polynomial punit.{u + 1} R) :
  cardinal.eq.2 ⟨(mv_polynomial.punit_alg_equiv.{u u} R).to_equiv.symm⟩
... ≤ _ : mv_polynomial.cardinal_mk_le_max
... ≤ _ : begin
  have : #(punit.{u + 1}) ≤ ω, from le_of_lt (lt_omega_iff_fintype.2 ⟨infer_instance⟩),
  rw [max_assoc, max_eq_right this]
end

end polynomial


/-
6. Give a calculational proof of the theorem log_mul below.
-/

import algebra.ordered_ring

variables (real : Type) [ordered_ring real]
variables (log exp : real → real)
variable  log_exp_eq : ∀ x, log (exp x) = x
variable  exp_log_eq : ∀ {x}, x > 0 → exp (log x) = x
variable  exp_pos    : ∀ x, exp x > 0
variable  exp_add    : ∀ x y, exp (x + y) = exp x * exp y

-- this ensures the assumptions are available in tactic proofs
include log_exp_eq exp_log_eq exp_pos exp_add

example (x y z : real) :
  exp (x + y + z) = exp x * exp y * exp z :=
by rw [exp_add, exp_add]

example (y : real) (h : y > 0)  : exp (log y) = y :=
exp_log_eq h

theorem log_mul {x y : real} (hx : x > 0) (hy : y > 0) :
  log (x * y) = log x + log y :=
  calc
    log (x * y) = log (exp (log x) * y) : by rw exp_log_eq hx
            ... = log (exp (log x) * exp (log y)) : by rw exp_log_eq hy
            ... = log (exp ((log x) + (log y))) : by rw exp_add
            ... = log x + log y : by rw log_exp_eq


-- 1
example : 0 ≠ 1 :=
begin
  -- ¬ (0 = 1)
  -- (0 = 1) → false
  assume h,
  cases h,
end

-- 2
example : 0 ≠ 0 → 2 = 3 :=
begin
  assume h,
  have f : false := h (eq.refl 0),
  exact false.elim (f),
end

-- 3
example : ∀ (P : Prop), P → ¬¬P :=
begin
  assume P,
  assume (p : P),
  -- ¬¬P
  -- ¬P → false
  -- (P → false) → false
  assume h,
  have f := h p,
  exact f,
end

-- We might need classical (vs constructive) reasoning
#check classical.em
open classical
#check em

/-
axiom em : ∀ (p : Prop), p ∨ ¬p

This is the famous and historically controversial
"law" (now axiom) of the excluded middle. It's is
a key to proving many intuitive theorems in logic
and mathematics. But it also leads to giving up on
having evidence *why* something is either true or
not true, in that you no longer need a proof of
either P or of ¬P to have a proof of P ∨ ¬P.
-/

-- 4
theorem neg_elim : ∀ (P : Prop), ¬¬P → P :=
begin
  assume P,
  assume h,
  have pornp := classical.em P,
  cases pornp with p pn,
  assumption,
  contradiction,
end
#check not.intro
-- 5

theorem demorgan_1 : ∀ (P Q : Prop), ¬ (P ∧ Q) ↔ ¬ P ∨ ¬ Q :=
begin
  assume P Q,
  apply iff.intro _ _,
  --forward
    intro h,
    by_cases p : P,
    right,
    intro q,
    have pnq : P ∧ Q := and.intro p q,
    apply h pnq,
    left,
    exact p,

  --backward
  assume npornq,
  by_cases p : P,
  by_cases q : Q,
  apply not.intro,
  assume pnq,
  apply or.elim npornq,
    assume np,
    apply np p,
      assume nq,
      apply nq q,
        apply not.intro,
        assume pnq,
        have q1 : Q := and.elim_right pnq,
        apply q q1,
          apply not.intro,
          assume pnq,
          have p1 : P := and.elim_left pnq,
          apply p p1,
end

-- 6
theorem demorgan_2 : ∀ (P Q : Prop), ¬ (P ∨ Q) → ¬P ∧ ¬Q :=
begin
  assume P Q,
  intro h,
  have pornp := classical.em P,
  have qornq := classical.em Q,
  cases pornp with p np,
    -- p
    have falso := h (or.intro_left _ p),
    exact false.elim falso,
    -- np
    cases qornq with q nq,
      -- q
      have falso := h (or.intro_right _ q),
      exact false.elim falso,
      -- nq
      exact and.intro np nq,
end

-- 7
theorem disappearing_opposite :
  ∀ (P Q : Prop), P ∨ ¬P ∧ Q ↔ P ∨ Q :=
begin
  --forward
  assume P Q,
  apply iff.intro _ _,
  assume p_or_npandq,
  apply or.elim p_or_npandq,
  assume p,
  apply or.intro_left,
  exact p,
  assume npandq,
  have q := and.elim_right npandq,
  apply or.intro_right,
  exact q,
  --backward
  assume porq,
  apply or.elim porq,
  assume p,

  apply or.intro_left,
  exact p,
  /-assume q,
  apply or.intro_right,
  apply and.intro,-/
  assume q,
  have pornp := classical.em P,
  apply or.elim pornp,
  assume p,
  apply or.intro_left,
  exact p,
  assume np,
  apply or.intro_right,
  apply and.intro np q,

end

-- 8
theorem distrib_and_or :
  ∀ (P Q R: Prop), (P ∨ Q) ∧ (P ∨ R) ↔
                    P ∨ (Q ∧ R) :=
begin
  intros P Q R,
  apply iff.intro _ _,
  assume h,
  have porq := h.left,
  have porr := h.right,
  cases porq with p q,
    exact or.intro_left _ p,
      cases porr with p r,
        exact or.intro_left _ p,
          exact or.intro_right _ (and.intro q r),

  assume h,
  cases h with p qnr,
    -- p
    exact and.intro (or.intro_left _ p) (or.intro_left _ p),
    -- qnr
    have r := and.elim_right qnr,
    have q := and.elim_left qnr,
    exact and.intro (or.intro_right _ q) (or.intro_right _ r),
end

-- remember or is right associative
-- you need this to know what the lefts and rights are
-- 9
theorem distrib_and_or_foil :
  ∀ (P Q R S : Prop),
  (P ∨ Q) ∧ (R ∨ S) ↔
  (P ∧ R) ∨ (P ∧ S) ∨ (Q ∧ R) ∨ (Q ∧ S) :=
begin
  intros P Q R S,
  apply iff.intro _ _,
    --forward
    assume h,
    have rors := h.right,
    have porq := h.left,
    cases porq with p q,
      -- p
      cases rors with r s,
        -- r within p
        have j := and.intro p r,
        exact or.intro_left _ j,
        -- s within p
        have pands := and.intro p s,
        exact or.intro_right _ (or.intro_left _ pands),
      -- q
      cases rors with r s,
        -- r within q
        have qandr := and.intro q r,
        exact or.intro_right _ (or.intro_right _ (or.intro_left _ qandr)),
        -- s within q
        have qands := and.intro q s,
        have qandr_or_qands := or.intro_right _ qands,
        exact or.intro_right _ (or.intro_right _ qandr_or_qands),
    --backward
    assume h,
    cases h with i j,
      -- case i (P ∧ R)
      have p := and.elim_left i,
      have r := and.elim_right i,
      have porq := or.intro_left _ p,
      have rors := or.intro_left _ r,
      exact and.intro porq rors,
      -- case j ( P ∧ S ∨ Q ∧ R ∨ Q ∧ S)
      cases j with m n,
      -- case m (P ∧ S)
        have p:= and.elim_left m,
        have s := and.elim_right m,
        have rors := or.intro_right _ s,
        have porq := or.intro_left _ p,
        exact and.intro porq rors,
      -- case n (Q ∧ R ∨ Q ∧ S)
        cases n with a b,
        -- case a (Q ∧ R)
          have q := and.elim_left a,
          have r := and.elim_right a,
          have porq := or.intro_right _ q,
          have rors := or.intro_left _ r,
          exact and.intro porq rors,
        -- case b ( Q ∧ S)
          have q := and.elim_left b,
          have s := and.elim_right b,
          have porq := or.intro_right _ q,
          have rors := or.intro_right _ s,
          exact and.intro porq rors,

end

/- 10
Formally state and prove the proposition that
not every natural number is equal to zero.
-/
lemma not_all_nats_are_zero : ∀(n : ℕ), (n=0) ∨ (n≠0):=
begin
  assume n,
  apply classical.em,
end

-- 11. equivalence of P→Q and (¬P∨Q)
example : ∀ (P Q : Prop), (P → Q) ↔ (¬P ∨ Q) :=
begin
  intros P Q,
  apply iff.intro,
  --forward
    assume h,
    have pornp := classical.em P,
    cases pornp with p np,
    --case p
      have q := h p,
      exact or.intro_right _ q,
    -- case np
      exact or.intro_left _ np,
  --backward
    assume h,
    assume p,
    cases h with np q,
      -- case np
      exact false.elim(np p),
      --case q
      exact q,
end

-- 12
example : ∀ (P Q : Prop), (P → Q) → (¬ Q → ¬ P) :=
begin
  intros P Q,
  assume pimpq,
  assume nq,
  apply not.intro,
  assume p,
  have q := pimpq p,
  exact (nq q),
end

-- 13
example : ∀ (P Q : Prop), ( ¬P → ¬Q) → (Q → P) :=
begin
  intros P Q,
  assume npimpnq,
  assume q,
  have pornp := classical.em P,
  cases pornp with p np,
  -- p
    exact p,
  -- np
    have nq := npimpnq np,
    have falso := nq q,
    exact false.elim falso,
end


/-
Copyright (c) 2022 María Inés de Frutos-Fernández. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: María Inés de Frutos-Fernández
-/
import analysis.normed.ring.seminorm
import analysis.special_functions.pow

/-!
# Seminorm related definitions
## Tags
ring_norm, equivalent
-/

/-- A function `f : α → β` is nonarchimedean if it satisfies the inequality
  `f (a + b) ≤ max (f a) (f b)` for all `a, b ∈ α`. -/
def is_nonarchimedean {α : Type*} [has_add α] {β : Type*} [linear_order β] (f : α → β) : Prop :=
∀ r s, f (r + s) ≤ max (f r) (f s)

lemma is_nonarchimedean_def {α : Type*} [has_add α] {β : Type*} [linear_order β] (f : α → β) :
is_nonarchimedean f ↔ ∀ r s, f (r + s) ≤ max (f r) (f s) := iff.rfl

/-- A function `f : α → β` is `multiplicative` if it satisfies the equality
  `f (a * b) = (f a) * (f b)` for all `a, b ∈ α`. -/
def mul_eq {α : Type*} [has_mul α] {β : Type*} [has_mul β] [has_le β] (f : α → β) : Prop :=
∀ r s, f (r * s) = (f r) * (f s)

lemma mul_eq_def {α : Type*} [has_mul α] {β : Type*} [has_mul β] [has_le β] (f : α → β) :
mul_eq f ↔ ∀ r s, f (r * s) = (f r) * (f s) := iff.rfl

namespace mul_ring_norm

/-- Two multiplicative ring norms `f, g` on `R` are equivalent if there exists a positive constant
  `c` such that for all `x ∈ R`, `(f x)^c = g x`.
  This could be generalised to ring_norm, but mul_ring_norm does not extend this. -/
def equiv {R : Type*} [ring R] (f : mul_ring_norm R) (g : mul_ring_norm R) :=
  ∃ c : ℝ, 0 < c ∧ (λ x : R, (f x) ^ c) = g

lemma equiv_refl {R : Type*} [ring R] (f : mul_ring_norm R) :
  equiv f f := by refine ⟨1, by linarith, by simp only [real.rpow_one]⟩

lemma equiv_symm {R : Type*} [ring R] (f g : mul_ring_norm R) (hfg : equiv f g) :
  equiv g f :=
begin
  rcases hfg with ⟨c, hfg1, hfg2⟩,
  refine ⟨1 / c, by simp only [hfg1, one_div, inv_pos], _⟩,
  rw ← hfg2,
  ext,
  simp only [one_div],
  have h1 : c ≠ 0 := by linarith,
  rw ← real.rpow_mul (map_nonneg f x),
  simp only [h1, mul_inv_cancel, ne.def, not_false_iff, real.rpow_one],
end

lemma equiv_trans {R : Type*} [ring R] (f g k : mul_ring_norm R) (hfg : equiv f g) (hgk : equiv g k) :
  equiv f k :=
begin
  rcases hfg with ⟨c, hfg1, hfg2⟩,
  rcases hgk with ⟨d, hgk1, hgk2⟩,
  refine ⟨c * d, by simp only [hfg1, hgk1, zero_lt_mul_right], _⟩,
  rw ← hgk2,
  rw ← hfg2,
  ext,
  exact real.rpow_mul (map_nonneg f x) c d,
end

end mul_ring_norm


-- La_composicion_por_la_izquierda_con_una_inyectiva_es_inyectiva.lean
-- La composición por la izquierda con una inyectiva es una operación inyectiva
-- José A. Alonso Jiménez
-- Sevilla, 11 de agosto de 2021
-- ---------------------------------------------------------------------

-- ---------------------------------------------------------------------
-- Sean f₁ y f₂ funciones de X en Y y g una función de X en Y. Demostrar
-- que si g es inyectiva y g ∘ f₁ = g ∘ f₂, entonces f₁ = f₂.
-- ---------------------------------------------------------------------

import tactic

variables {X Y Z : Type*}
variables {f₁ f₂ : X → Y}
variable  {g : Y → Z}

example
  (hg : function.injective g)
  (hgf : g ∘ f₁ = g ∘ f₂)
  : f₁ = f₂ :=
begin
  funext,
  apply hg,
  calc g (f₁ x)
       = (g ∘ f₁) x : rfl
   ... = (g ∘ f₂) x : congr_fun hgf x
   ... = g (f₂ x)   : rfl,
end

example
  (hg : function.injective g)
  (hgf : g ∘ f₁ = g ∘ f₂)
  : f₁ = f₂ :=
begin
  funext,
  apply hg,
  exact congr_fun hgf x,
end

lemma function.injective.comp_left'
  (hg : function.injective g)
  (hgf : g ∘ f₁ = g ∘ f₂)
  : f₁ = f₂ :=
begin
  refine funext (λ i, hg _),
  exact congr_fun hgf i,
end

lemma function.injective.comp_left2
  (hg : function.injective g)
  : function.injective ((∘) g : (X → Y) → (X → Z)) :=
λ f₁ f₂ hgf, funext $ λ i, hg (congr_fun hgf i : _)


import MyNat.Definition
import MyNat.Inequality -- le_iff_exists_add
import Mathlib.Tactic.Use -- use tactic
import AdditionWorld.Level4 -- add_comm
namespace MyNat
open MyNat

/-!

# Inequality world.

## Level 1: the [`use` tactic](../Tactics/use.lean.md)

The goal below is to prove `x ≤ 1+x` for any natural number `x`.
First let's turn the goal explicitly into an existence problem with

`rw [le_iff_exists_add]`

and now the goal has become `∃ c : MyNat, 1 + x = x + c`. Clearly
this statement is true, and the proof is that `c=1` will work (we also
need the fact that addition is commutative, but we proved that a long
time ago). How do we make progress with this goal?

The `use` tactic can be used on goals of the form `∃ c, ...`. The idea
is that we choose which natural number we want to use, and then we use it.
So try

`use 1`

and now the goal becomes `⊢ 1 + x = x + 1`. You can solve this by
`exact add_comm 1 x`, or if you are lazy you can just use the `ring` tactic,
which is a powerful AI which will solve any equality in algebra which can
be proved using the standard rules of addition and multiplication. Now
look at your proof. We're going to remove a line.

## Important

An important time-saver here is to note that because `a ≤ b` is *defined*
as `∃ c : MyNat, b = a + c`, you *do not need to write* `rw [le_iff_exists_add]`.
The `use` tactic will work directly on a goal of the form `a ≤ b`. Just
use the difference `b - a` (note that we have not defined subtraction so
this does not formally make sense, but you can do the calculation in your head).
If you have written `rw [le_iff_exists_add]` below, then just put two minus signs `--`
before it and comment it out. See that the proof still compiles.

## Lemma : one_add_le_self
If `x` is a natural number, then `x ≤ 1+x`.
-/
lemma one_add_le_self (x : MyNat) : x ≤ 1 + x := by
  rw [le_iff_exists_add]
  use 1
  rw [add_comm]

/-!

Next up [Level 2](./Level2.lean.md)

-/


--------------------------------------------------------------------------------
-- File     : PUZ131_1 : TPTP v7.3.0. Released v5.0.0.
-- Domain   : Puzzles
-- Problem  : Victor teaches Michael
-- Version  : Especial.
-- English  : Every student is enrolled in at least one course. Every professor
--            teaches at least one course. Every course has at least one student
--            enrolled. Every course has at least one professor teaching. The
--            coordinator of a course teaches the course. If a student is
--            enroled in a course then the student is taught by every professor
--            who teaches the course. Michael is enrolled in CSC410. Victor is
--            the coordinator of CSC410. Therefore, Michael is taught by Victor.

-- Source   : [TPTP]
--------------------------------------------------------------------------------

variable student : Type
variable professor : Type
variable course : Type
variable michael : student
variable victor : professor
variable csc410 : course
variable enrolled : student × course → Prop
variable teaches : professor × course → Prop
variable taughtby : student × professor → Prop
variable coordinatorof : course → professor

-- axioms
variable student_enrolled_axiom :
  ∀ X : student,
  ∃ Y : course,
  enrolled (X, Y)

variable professor_teaches :
  ∀ X: professor,
  ∃ Y: course,
  teaches (X, Y)

variable course_enrolled :
  ∀ X: course,
  ∃ Y: student,
  enrolled(Y,X)

variable course_teaches :
  ∀ X : course,
  ∃ Y : professor,
  teaches(Y,X)

variable coordinator_teaches :
  ∀ X : course,
  teaches(coordinatorof(X),X)

variable student_enrolled_taught :
  ∀ (X: student)(Y: course),
  (enrolled(X,Y) →
  ∀ (Z: professor), (teaches(Z,Y) → taughtby(X,Z)))

variable michael_enrolled_csc410_axiom:
  enrolled(michael,csc410)

variable victor_coordinator_csc410_axiom:
  coordinatorof(csc410) = victor

-- conjecture : teaching_conjecture
include coordinatorof
include coordinator_teaches
include victor_coordinator_csc410_axiom
include student_enrolled_taught
include michael_enrolled_csc410_axiom

theorem teaching_conjecture : taughtby(michael,victor) :=
  have victor_teaches_csc410 : teaches(victor, csc410) :=
    begin
        have coordinator_teaches_csc410 : teaches(_,csc410),
        from coordinator_teaches csc410,
        rw victor_coordinator_csc410_axiom at coordinator_teaches_csc410,
        assumption
    end,
  begin
    have michael_taught,
    from student_enrolled_taught _ _ michael_enrolled_csc410_axiom,
    exact michael_taught _ victor_teaches_csc410
  end