/-
Copyright (c) 2019 Tim Baanen. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Tim Baanen, Lu-Ming Zhang

! This file was ported from Lean 3 source module linear_algebra.matrix.nonsingular_inverse
! leanprover-community/mathlib commit da420a8c6dd5bdfb85c4ced85c34388f633bc6ff
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.LinearAlgebra.Matrix.Adjugate

/-!
# Nonsingular inverses

In this file, we define an inverse for square matrices of invertible determinant.

For matrices that are not square or not of full rank, there is a more general notion of
pseudoinverses which we do not consider here.

The definition of inverse used in this file is the adjugate divided by the determinant.
We show that dividing the adjugate by `det A` (if possible), giving a matrix `A⁻¹` (`nonsing_inv`),
will result in a multiplicative inverse to `A`.

Note that there are at least three different inverses in mathlib:

* `A⁻¹` (`has_inv.inv`): alone, this satisfies no properties, although it is usually used in
  conjunction with `group` or `group_with_zero`. On matrices, this is defined to be zero when no
  inverse exists.
* `⅟A` (`inv_of`): this is only available in the presence of `[invertible A]`, which guarantees an
  inverse exists.
* `ring.inverse A`: this is defined on any `monoid_with_zero`, and just like `⁻¹` on matrices, is
  defined to be zero when no inverse exists.

We start by working with `invertible`, and show the main results:

* `matrix.invertible_of_det_invertible`
* `matrix.det_invertible_of_invertible`
* `matrix.is_unit_iff_is_unit_det`
* `matrix.mul_eq_one_comm`

After this we define `matrix.has_inv` and show it matches `⅟A` and `ring.inverse A`.
The rest of the results in the file are then about `A⁻¹`

## References

  * https://en.wikipedia.org/wiki/Cramer's_rule#Finding_inverse_matrix

## Tags

matrix inverse, cramer, cramer's rule, adjugate
-/

namespace Matrix

universe u u' v

variable {m : Type u} {n : Type u'} {α : Type v}

open Matrix BigOperators

open Equiv Equiv.Perm Finset

/-! ### Matrices are `invertible` iff their determinants are -/

section Invertible

variable [Fintype n] [DecidableEq n] [CommRing α]

/-- A copy of `inv_of_mul_self` using `⬝` not `*`. -/
protected theorem invOf_mul_self (A : Matrix n n α) [Invertible A] : ⅟ A ⬝ A = 1 :=
  invOf_mul_self A
#align matrix.inv_of_mul_self Matrix.invOf_mul_self

/-- A copy of `mul_inv_of_self` using `⬝` not `*`. -/
protected theorem mul_invOf_self (A : Matrix n n α) [Invertible A] : A ⬝ ⅟ A = 1 :=
  mul_invOf_self A
#align matrix.mul_inv_of_self Matrix.mul_invOf_self

/-- A copy of `inv_of_mul_self_assoc` using `⬝` not `*`. -/
protected theorem invOf_mul_self_assoc (A : Matrix n n α) (B : Matrix n m α) [Invertible A] :
    ⅟ A ⬝ (A ⬝ B) = B := by rw [← Matrix.mul_assoc, Matrix.invOf_mul_self, Matrix.one_mul]
#align matrix.inv_of_mul_self_assoc Matrix.invOf_mul_self_assoc

/-- A copy of `mul_inv_of_self_assoc` using `⬝` not `*`. -/
protected theorem mul_invOf_self_assoc (A : Matrix n n α) (B : Matrix n m α) [Invertible A] :
    A ⬝ (⅟ A ⬝ B) = B := by rw [← Matrix.mul_assoc, Matrix.mul_invOf_self, Matrix.one_mul]
#align matrix.mul_inv_of_self_assoc Matrix.mul_invOf_self_assoc

/-- A copy of `mul_inv_of_mul_self_cancel` using `⬝` not `*`. -/
protected theorem mul_invOf_mul_self_cancel (A : Matrix m n α) (B : Matrix n n α) [Invertible B] :
    A ⬝ ⅟ B ⬝ B = A := by rw [Matrix.mul_assoc, Matrix.invOf_mul_self, Matrix.mul_one]
#align matrix.mul_inv_of_mul_self_cancel Matrix.mul_invOf_mul_self_cancel

/-- A copy of `mul_mul_inv_of_self_cancel` using `⬝` not `*`. -/
protected theorem mul_mul_invOf_self_cancel (A : Matrix m n α) (B : Matrix n n α) [Invertible B] :
    A ⬝ B ⬝ ⅟ B = A := by rw [Matrix.mul_assoc, Matrix.mul_invOf_self, Matrix.mul_one]
#align matrix.mul_mul_inv_of_self_cancel Matrix.mul_mul_invOf_self_cancel

variable (A : Matrix n n α) (B : Matrix n n α)

/-- If `A.det` has a constructive inverse, produce one for `A`. -/
def invertibleOfDetInvertible [Invertible A.det] : Invertible A
    where
  invOf := ⅟ A.det • A.adjugate
  mul_invOf_self := by
    rw [mul_smul_comm, Matrix.mul_eq_mul, mul_adjugate, smul_smul, invOf_mul_self, one_smul]
  invOf_mul_self := by
    rw [smul_mul_assoc, Matrix.mul_eq_mul, adjugate_mul, smul_smul, invOf_mul_self, one_smul]
#align matrix.invertible_of_det_invertible Matrix.invertibleOfDetInvertible

theorem invOf_eq [Invertible A.det] [Invertible A] : ⅟ A = ⅟ A.det • A.adjugate :=
  by
  letI := invertible_of_det_invertible A
  convert(rfl : ⅟ A = _)
#align matrix.inv_of_eq Matrix.invOf_eq

/-- `A.det` is invertible if `A` has a left inverse. -/
def detInvertibleOfLeftInverse (h : B ⬝ A = 1) : Invertible A.det
    where
  invOf := B.det
  mul_invOf_self := by rw [mul_comm, ← det_mul, h, det_one]
  invOf_mul_self := by rw [← det_mul, h, det_one]
#align matrix.det_invertible_of_left_inverse Matrix.detInvertibleOfLeftInverse

/-- `A.det` is invertible if `A` has a right inverse. -/
def detInvertibleOfRightInverse (h : A ⬝ B = 1) : Invertible A.det
    where
  invOf := B.det
  mul_invOf_self := by rw [← det_mul, h, det_one]
  invOf_mul_self := by rw [mul_comm, ← det_mul, h, det_one]
#align matrix.det_invertible_of_right_inverse Matrix.detInvertibleOfRightInverse

/-- If `A` has a constructive inverse, produce one for `A.det`. -/
def detInvertibleOfInvertible [Invertible A] : Invertible A.det :=
  detInvertibleOfLeftInverse A (⅟ A) (invOf_mul_self _)
#align matrix.det_invertible_of_invertible Matrix.detInvertibleOfInvertible

theorem det_invOf [Invertible A] [Invertible A.det] : (⅟ A).det = ⅟ A.det :=
  by
  letI := det_invertible_of_invertible A
  convert(rfl : _ = ⅟ A.det)
#align matrix.det_inv_of Matrix.det_invOf

/-- Together `matrix.det_invertible_of_invertible` and `matrix.invertible_of_det_invertible` form an
equivalence, although both sides of the equiv are subsingleton anyway. -/
@[simps]
def invertibleEquivDetInvertible : Invertible A ≃ Invertible A.det
    where
  toFun := @detInvertibleOfInvertible _ _ _ _ _ A
  invFun := @invertibleOfDetInvertible _ _ _ _ _ A
  left_inv _ := Subsingleton.elim _ _
  right_inv _ := Subsingleton.elim _ _
#align matrix.invertible_equiv_det_invertible Matrix.invertibleEquivDetInvertible

variable {A B}

theorem mul_eq_one_comm : A ⬝ B = 1 ↔ B ⬝ A = 1 :=
  suffices ∀ A B, A ⬝ B = 1 → B ⬝ A = 1 from ⟨this A B, this B A⟩
  fun A B h => by
  letI : Invertible B.det := det_invertible_of_left_inverse _ _ h
  letI : Invertible B := invertible_of_det_invertible B
  calc
    B ⬝ A = B ⬝ A ⬝ (B ⬝ ⅟ B) := by rw [Matrix.mul_invOf_self, Matrix.mul_one]
    _ = B ⬝ (A ⬝ B ⬝ ⅟ B) := by simp only [Matrix.mul_assoc]
    _ = B ⬝ ⅟ B := by rw [h, Matrix.one_mul]
    _ = 1 := Matrix.mul_invOf_self B

#align matrix.mul_eq_one_comm Matrix.mul_eq_one_comm

variable (A B)

/-- We can construct an instance of invertible A if A has a left inverse. -/
def invertibleOfLeftInverse (h : B ⬝ A = 1) : Invertible A :=
  ⟨B, h, mul_eq_one_comm.mp h⟩
#align matrix.invertible_of_left_inverse Matrix.invertibleOfLeftInverse

/-- We can construct an instance of invertible A if A has a right inverse. -/
def invertibleOfRightInverse (h : A ⬝ B = 1) : Invertible A :=
  ⟨B, mul_eq_one_comm.mp h, h⟩
#align matrix.invertible_of_right_inverse Matrix.invertibleOfRightInverse

/-- The transpose of an invertible matrix is invertible. -/
instance invertibleTranspose [Invertible A] : Invertible Aᵀ :=
  haveI : Invertible Aᵀ.det := by simpa using det_invertible_of_invertible A
  invertible_of_det_invertible Aᵀ
#align matrix.invertible_transpose Matrix.invertibleTranspose

/-- A matrix is invertible if the transpose is invertible. -/
def invertibleOfInvertibleTranspose [Invertible Aᵀ] : Invertible A :=
  by
  rw [← transpose_transpose A]
  infer_instance
#align matrix.invertible__of_invertible_transpose Matrix.invertibleOfInvertibleTranspose

/-- A matrix is invertible if the conjugate transpose is invertible. -/
def invertibleOfInvertibleConjTranspose [StarRing α] [Invertible Aᴴ] : Invertible A :=
  by
  rw [← conj_transpose_conj_transpose A]
  infer_instance
#align matrix.invertible_of_invertible_conj_transpose Matrix.invertibleOfInvertibleConjTranspose

/-- Given a proof that `A.det` has a constructive inverse, lift `A` to `(matrix n n α)ˣ`-/
def unitOfDetInvertible [Invertible A.det] : (Matrix n n α)ˣ :=
  @unitOfInvertible _ _ A (invertibleOfDetInvertible A)
#align matrix.unit_of_det_invertible Matrix.unitOfDetInvertible

/-- When lowered to a prop, `matrix.invertible_equiv_det_invertible` forms an `iff`. -/
theorem isUnit_iff_isUnit_det : IsUnit A ↔ IsUnit A.det := by
  simp only [← nonempty_invertible_iff_isUnit, (invertible_equiv_det_invertible A).nonempty_congr]
#align matrix.is_unit_iff_is_unit_det Matrix.isUnit_iff_isUnit_det

/-! #### Variants of the statements above with `is_unit`-/

theorem isUnit_det_of_invertible [Invertible A] : IsUnit A.det :=
  @isUnit_of_invertible _ _ _ (detInvertibleOfInvertible A)
#align matrix.is_unit_det_of_invertible Matrix.isUnit_det_of_invertible

variable {A B}

theorem isUnit_of_left_inverse (h : B ⬝ A = 1) : IsUnit A :=
  ⟨⟨A, B, mul_eq_one_comm.mp h, h⟩, rfl⟩
#align matrix.is_unit_of_left_inverse Matrix.isUnit_of_left_inverse

theorem isUnit_of_right_inverse (h : A ⬝ B = 1) : IsUnit A :=
  ⟨⟨A, B, h, mul_eq_one_comm.mp h⟩, rfl⟩
#align matrix.is_unit_of_right_inverse Matrix.isUnit_of_right_inverse

theorem isUnit_det_of_left_inverse (h : B ⬝ A = 1) : IsUnit A.det :=
  @isUnit_of_invertible _ _ _ (detInvertibleOfLeftInverse _ _ h)
#align matrix.is_unit_det_of_left_inverse Matrix.isUnit_det_of_left_inverse

theorem isUnit_det_of_right_inverse (h : A ⬝ B = 1) : IsUnit A.det :=
  @isUnit_of_invertible _ _ _ (detInvertibleOfRightInverse _ _ h)
#align matrix.is_unit_det_of_right_inverse Matrix.isUnit_det_of_right_inverse

theorem det_ne_zero_of_left_inverse [Nontrivial α] (h : B ⬝ A = 1) : A.det ≠ 0 :=
  (isUnit_det_of_left_inverse h).NeZero
#align matrix.det_ne_zero_of_left_inverse Matrix.det_ne_zero_of_left_inverse

theorem det_ne_zero_of_right_inverse [Nontrivial α] (h : A ⬝ B = 1) : A.det ≠ 0 :=
  (isUnit_det_of_right_inverse h).NeZero
#align matrix.det_ne_zero_of_right_inverse Matrix.det_ne_zero_of_right_inverse

end Invertible

variable [Fintype n] [DecidableEq n] [CommRing α]

variable (A : Matrix n n α) (B : Matrix n n α)

theorem isUnit_det_transpose (h : IsUnit A.det) : IsUnit Aᵀ.det :=
  by
  rw [det_transpose]
  exact h
#align matrix.is_unit_det_transpose Matrix.isUnit_det_transpose

/-! ### A noncomputable `has_inv` instance  -/

/-- The inverse of a square matrix, when it is invertible (and zero otherwise).-/
noncomputable instance : Inv (Matrix n n α) :=
  ⟨fun A => Ring.inverse A.det • A.adjugate⟩

theorem inv_def (A : Matrix n n α) : A⁻¹ = Ring.inverse A.det • A.adjugate :=
  rfl
#align matrix.inv_def Matrix.inv_def

theorem nonsing_inv_apply_not_isUnit (h : ¬IsUnit A.det) : A⁻¹ = 0 := by
  rw [inv_def, Ring.inverse_non_unit _ h, zero_smul]
#align matrix.nonsing_inv_apply_not_is_unit Matrix.nonsing_inv_apply_not_isUnit

theorem nonsing_inv_apply (h : IsUnit A.det) : A⁻¹ = (↑h.Unit⁻¹ : α) • A.adjugate := by
  rw [inv_def, ← Ring.inverse_unit h.unit, IsUnit.unit_spec]
#align matrix.nonsing_inv_apply Matrix.nonsing_inv_apply

/-- The nonsingular inverse is the same as `inv_of` when `A` is invertible. -/
@[simp]
theorem invOf_eq_nonsing_inv [Invertible A] : ⅟ A = A⁻¹ :=
  by
  letI := det_invertible_of_invertible A
  rw [inv_def, Ring.inverse_invertible, inv_of_eq]
#align matrix.inv_of_eq_nonsing_inv Matrix.invOf_eq_nonsing_inv

/-- Coercing the result of `units.has_inv` is the same as coercing first and applying the
nonsingular inverse. -/
@[simp, norm_cast]
theorem coe_units_inv (A : (Matrix n n α)ˣ) : ↑A⁻¹ = (A⁻¹ : Matrix n n α) :=
  by
  letI := A.invertible
  rw [← inv_of_eq_nonsing_inv, invOf_units]
#align matrix.coe_units_inv Matrix.coe_units_inv

/-- The nonsingular inverse is the same as the general `ring.inverse`. -/
theorem nonsing_inv_eq_ring_inverse : A⁻¹ = Ring.inverse A :=
  by
  by_cases h_det : IsUnit A.det
  · cases (A.is_unit_iff_is_unit_det.mpr h_det).nonempty_invertible
    rw [← inv_of_eq_nonsing_inv, Ring.inverse_invertible]
  · have h := mt A.is_unit_iff_is_unit_det.mp h_det
    rw [Ring.inverse_non_unit _ h, nonsing_inv_apply_not_is_unit A h_det]
#align matrix.nonsing_inv_eq_ring_inverse Matrix.nonsing_inv_eq_ring_inverse

theorem transpose_nonsing_inv : A⁻¹ᵀ = Aᵀ⁻¹ := by
  rw [inv_def, inv_def, transpose_smul, det_transpose, adjugate_transpose]
#align matrix.transpose_nonsing_inv Matrix.transpose_nonsing_inv

theorem conjTranspose_nonsing_inv [StarRing α] : A⁻¹ᴴ = Aᴴ⁻¹ := by
  rw [inv_def, inv_def, conj_transpose_smul, det_conj_transpose, adjugate_conj_transpose,
    Ring.inverse_star]
#align matrix.conj_transpose_nonsing_inv Matrix.conjTranspose_nonsing_inv

/-- The `nonsing_inv` of `A` is a right inverse. -/
@[simp]
theorem mul_nonsing_inv (h : IsUnit A.det) : A ⬝ A⁻¹ = 1 :=
  by
  cases (A.is_unit_iff_is_unit_det.mpr h).nonempty_invertible
  rw [← inv_of_eq_nonsing_inv, Matrix.mul_invOf_self]
#align matrix.mul_nonsing_inv Matrix.mul_nonsing_inv

/-- The `nonsing_inv` of `A` is a left inverse. -/
@[simp]
theorem nonsing_inv_mul (h : IsUnit A.det) : A⁻¹ ⬝ A = 1 :=
  by
  cases (A.is_unit_iff_is_unit_det.mpr h).nonempty_invertible
  rw [← inv_of_eq_nonsing_inv, Matrix.invOf_mul_self]
#align matrix.nonsing_inv_mul Matrix.nonsing_inv_mul

instance [Invertible A] : Invertible A⁻¹ :=
  by
  rw [← inv_of_eq_nonsing_inv]
  infer_instance

@[simp]
theorem inv_inv_of_invertible [Invertible A] : A⁻¹⁻¹ = A := by
  simp only [← inv_of_eq_nonsing_inv, invOf_invOf]
#align matrix.inv_inv_of_invertible Matrix.inv_inv_of_invertible

@[simp]
theorem mul_nonsing_inv_cancel_right (B : Matrix m n α) (h : IsUnit A.det) : B ⬝ A ⬝ A⁻¹ = B := by
  simp [Matrix.mul_assoc, mul_nonsing_inv A h]
#align matrix.mul_nonsing_inv_cancel_right Matrix.mul_nonsing_inv_cancel_right

@[simp]
theorem mul_nonsing_inv_cancel_left (B : Matrix n m α) (h : IsUnit A.det) : A ⬝ (A⁻¹ ⬝ B) = B := by
  simp [← Matrix.mul_assoc, mul_nonsing_inv A h]
#align matrix.mul_nonsing_inv_cancel_left Matrix.mul_nonsing_inv_cancel_left

@[simp]
theorem nonsing_inv_mul_cancel_right (B : Matrix m n α) (h : IsUnit A.det) : B ⬝ A⁻¹ ⬝ A = B := by
  simp [Matrix.mul_assoc, nonsing_inv_mul A h]
#align matrix.nonsing_inv_mul_cancel_right Matrix.nonsing_inv_mul_cancel_right

@[simp]
theorem nonsing_inv_mul_cancel_left (B : Matrix n m α) (h : IsUnit A.det) : A⁻¹ ⬝ (A ⬝ B) = B := by
  simp [← Matrix.mul_assoc, nonsing_inv_mul A h]
#align matrix.nonsing_inv_mul_cancel_left Matrix.nonsing_inv_mul_cancel_left

@[simp]
theorem mul_inv_of_invertible [Invertible A] : A ⬝ A⁻¹ = 1 :=
  mul_nonsing_inv A (isUnit_det_of_invertible A)
#align matrix.mul_inv_of_invertible Matrix.mul_inv_of_invertible

@[simp]
theorem inv_mul_of_invertible [Invertible A] : A⁻¹ ⬝ A = 1 :=
  nonsing_inv_mul A (isUnit_det_of_invertible A)
#align matrix.inv_mul_of_invertible Matrix.inv_mul_of_invertible

@[simp]
theorem mul_inv_cancel_right_of_invertible (B : Matrix m n α) [Invertible A] : B ⬝ A ⬝ A⁻¹ = B :=
  mul_nonsing_inv_cancel_right A B (isUnit_det_of_invertible A)
#align matrix.mul_inv_cancel_right_of_invertible Matrix.mul_inv_cancel_right_of_invertible

@[simp]
theorem mul_inv_cancel_left_of_invertible (B : Matrix n m α) [Invertible A] : A ⬝ (A⁻¹ ⬝ B) = B :=
  mul_nonsing_inv_cancel_left A B (isUnit_det_of_invertible A)
#align matrix.mul_inv_cancel_left_of_invertible Matrix.mul_inv_cancel_left_of_invertible

@[simp]
theorem inv_mul_cancel_right_of_invertible (B : Matrix m n α) [Invertible A] : B ⬝ A⁻¹ ⬝ A = B :=
  nonsing_inv_mul_cancel_right A B (isUnit_det_of_invertible A)
#align matrix.inv_mul_cancel_right_of_invertible Matrix.inv_mul_cancel_right_of_invertible

@[simp]
theorem inv_mul_cancel_left_of_invertible (B : Matrix n m α) [Invertible A] : A⁻¹ ⬝ (A ⬝ B) = B :=
  nonsing_inv_mul_cancel_left A B (isUnit_det_of_invertible A)
#align matrix.inv_mul_cancel_left_of_invertible Matrix.inv_mul_cancel_left_of_invertible

theorem inv_mul_eq_iff_eq_mul_of_invertible (A B C : Matrix n n α) [Invertible A] :
    A⁻¹ ⬝ B = C ↔ B = A ⬝ C :=
  ⟨fun h => by rw [← h, mul_inv_cancel_left_of_invertible], fun h => by
    rw [h, inv_mul_cancel_left_of_invertible]⟩
#align matrix.inv_mul_eq_iff_eq_mul_of_invertible Matrix.inv_mul_eq_iff_eq_mul_of_invertible

theorem mul_inv_eq_iff_eq_mul_of_invertible (A B C : Matrix n n α) [Invertible A] :
    B ⬝ A⁻¹ = C ↔ B = C ⬝ A :=
  ⟨fun h => by rw [← h, inv_mul_cancel_right_of_invertible], fun h => by
    rw [h, mul_inv_cancel_right_of_invertible]⟩
#align matrix.mul_inv_eq_iff_eq_mul_of_invertible Matrix.mul_inv_eq_iff_eq_mul_of_invertible

theorem nonsing_inv_cancel_or_zero : A⁻¹ ⬝ A = 1 ∧ A ⬝ A⁻¹ = 1 ∨ A⁻¹ = 0 :=
  by
  by_cases h : IsUnit A.det
  · exact Or.inl ⟨nonsing_inv_mul _ h, mul_nonsing_inv _ h⟩
  · exact Or.inr (nonsing_inv_apply_not_is_unit _ h)
#align matrix.nonsing_inv_cancel_or_zero Matrix.nonsing_inv_cancel_or_zero

theorem det_nonsing_inv_mul_det (h : IsUnit A.det) : A⁻¹.det * A.det = 1 := by
  rw [← det_mul, A.nonsing_inv_mul h, det_one]
#align matrix.det_nonsing_inv_mul_det Matrix.det_nonsing_inv_mul_det

@[simp]
theorem det_nonsing_inv : A⁻¹.det = Ring.inverse A.det :=
  by
  by_cases h : IsUnit A.det
  · cases h.nonempty_invertible
    letI := invertible_of_det_invertible A
    rw [Ring.inverse_invertible, ← inv_of_eq_nonsing_inv, det_inv_of]
  cases isEmpty_or_nonempty n
  · rw [det_is_empty, det_is_empty, Ring.inverse_one]
  · rw [Ring.inverse_non_unit _ h, nonsing_inv_apply_not_is_unit _ h, det_zero ‹_›]
#align matrix.det_nonsing_inv Matrix.det_nonsing_inv

theorem isUnit_nonsing_inv_det (h : IsUnit A.det) : IsUnit A⁻¹.det :=
  isUnit_of_mul_eq_one _ _ (A.det_nonsing_inv_mul_det h)
#align matrix.is_unit_nonsing_inv_det Matrix.isUnit_nonsing_inv_det

@[simp]
theorem nonsing_inv_nonsing_inv (h : IsUnit A.det) : A⁻¹⁻¹ = A :=
  calc
    A⁻¹⁻¹ = 1 ⬝ A⁻¹⁻¹ := by rw [Matrix.one_mul]
    _ = A ⬝ A⁻¹ ⬝ A⁻¹⁻¹ := by rw [A.mul_nonsing_inv h]
    _ = A := by
      rw [Matrix.mul_assoc, A⁻¹.mul_nonsing_inv (A.is_unit_nonsing_inv_det h), Matrix.mul_one]

#align matrix.nonsing_inv_nonsing_inv Matrix.nonsing_inv_nonsing_inv

theorem isUnit_nonsing_inv_det_iff {A : Matrix n n α} : IsUnit A⁻¹.det ↔ IsUnit A.det := by
  rw [Matrix.det_nonsing_inv, isUnit_ring_inverse]
#align matrix.is_unit_nonsing_inv_det_iff Matrix.isUnit_nonsing_inv_det_iff

-- `is_unit.invertible` lifts the proposition `is_unit A` to a constructive inverse of `A`.
/-- A version of `matrix.invertible_of_det_invertible` with the inverse defeq to `A⁻¹` that is
therefore noncomputable. -/
noncomputable def invertibleOfIsUnitDet (h : IsUnit A.det) : Invertible A :=
  ⟨A⁻¹, nonsing_inv_mul A h, mul_nonsing_inv A h⟩
#align matrix.invertible_of_is_unit_det Matrix.invertibleOfIsUnitDet

/-- A version of `matrix.units_of_det_invertible` with the inverse defeq to `A⁻¹` that is therefore
noncomputable. -/
noncomputable def nonsingInvUnit (h : IsUnit A.det) : (Matrix n n α)ˣ :=
  @unitOfInvertible _ _ _ (invertibleOfIsUnitDet A h)
#align matrix.nonsing_inv_unit Matrix.nonsingInvUnit

theorem unitOfDetInvertible_eq_nonsingInvUnit [Invertible A.det] :
    unitOfDetInvertible A = nonsingInvUnit A (isUnit_of_invertible _) :=
  by
  ext
  rfl
#align matrix.unit_of_det_invertible_eq_nonsing_inv_unit Matrix.unitOfDetInvertible_eq_nonsingInvUnit

variable {A} {B}

/-- If matrix A is left invertible, then its inverse equals its left inverse. -/
theorem inv_eq_left_inv (h : B ⬝ A = 1) : A⁻¹ = B :=
  letI := invertible_of_left_inverse _ _ h
  inv_of_eq_nonsing_inv A ▸ invOf_eq_left_inv h
#align matrix.inv_eq_left_inv Matrix.inv_eq_left_inv

/-- If matrix A is right invertible, then its inverse equals its right inverse. -/
theorem inv_eq_right_inv (h : A ⬝ B = 1) : A⁻¹ = B :=
  inv_eq_left_inv (mul_eq_one_comm.2 h)
#align matrix.inv_eq_right_inv Matrix.inv_eq_right_inv

section InvEqInv

variable {C : Matrix n n α}

/-- The left inverse of matrix A is unique when existing. -/
theorem left_inv_eq_left_inv (h : B ⬝ A = 1) (g : C ⬝ A = 1) : B = C := by
  rw [← inv_eq_left_inv h, ← inv_eq_left_inv g]
#align matrix.left_inv_eq_left_inv Matrix.left_inv_eq_left_inv

/-- The right inverse of matrix A is unique when existing. -/
theorem right_inv_eq_right_inv (h : A ⬝ B = 1) (g : A ⬝ C = 1) : B = C := by
  rw [← inv_eq_right_inv h, ← inv_eq_right_inv g]
#align matrix.right_inv_eq_right_inv Matrix.right_inv_eq_right_inv

/-- The right inverse of matrix A equals the left inverse of A when they exist. -/
theorem right_inv_eq_left_inv (h : A ⬝ B = 1) (g : C ⬝ A = 1) : B = C := by
  rw [← inv_eq_right_inv h, ← inv_eq_left_inv g]
#align matrix.right_inv_eq_left_inv Matrix.right_inv_eq_left_inv

theorem inv_inj (h : A⁻¹ = B⁻¹) (h' : IsUnit A.det) : A = B :=
  by
  refine' left_inv_eq_left_inv (mul_nonsing_inv _ h') _
  rw [h]
  refine' mul_nonsing_inv _ _
  rwa [← is_unit_nonsing_inv_det_iff, ← h, is_unit_nonsing_inv_det_iff]
#align matrix.inv_inj Matrix.inv_inj

end InvEqInv

variable (A)

@[simp]
theorem inv_zero : (0 : Matrix n n α)⁻¹ = 0 :=
  by
  cases' subsingleton_or_nontrivial α with ht ht
  · simp
  cases' (Fintype.card n).zero_le.eq_or_lt with hc hc
  · rw [eq_comm, Fintype.card_eq_zero_iff] at hc
    haveI := hc
    ext i
    exact (IsEmpty.false i).elim
  · have hn : Nonempty n := fintype.card_pos_iff.mp hc
    refine' nonsing_inv_apply_not_is_unit _ _
    simp [hn]
#align matrix.inv_zero Matrix.inv_zero

noncomputable instance : InvOneClass (Matrix n n α) :=
  { Matrix.hasOne, Matrix.hasInv with inv_one := inv_eq_left_inv (by simp) }

theorem inv_smul (k : α) [Invertible k] (h : IsUnit A.det) : (k • A)⁻¹ = ⅟ k • A⁻¹ :=
  inv_eq_left_inv (by simp [h, smul_smul])
#align matrix.inv_smul Matrix.inv_smul

theorem inv_smul' (k : αˣ) (h : IsUnit A.det) : (k • A)⁻¹ = k⁻¹ • A⁻¹ :=
  inv_eq_left_inv (by simp [h, smul_smul])
#align matrix.inv_smul' Matrix.inv_smul'

theorem inv_adjugate (A : Matrix n n α) (h : IsUnit A.det) : (adjugate A)⁻¹ = h.Unit⁻¹ • A :=
  by
  refine' inv_eq_left_inv _
  rw [smul_mul, mul_adjugate, Units.smul_def, smul_smul, h.coe_inv_mul, one_smul]
#align matrix.inv_adjugate Matrix.inv_adjugate

/-- `diagonal v` is invertible if `v` is -/
def diagonalInvertible {α} [NonAssocSemiring α] (v : n → α) [Invertible v] :
    Invertible (diagonal v) :=
  Invertible.map (diagonalRingHom n α) v
#align matrix.diagonal_invertible Matrix.diagonalInvertible

theorem invOf_diagonal_eq {α} [Semiring α] (v : n → α) [Invertible v] [Invertible (diagonal v)] :
    ⅟ (diagonal v) = diagonal (⅟ v) :=
  by
  letI := diagonal_invertible v
  haveI := Invertible.subsingleton (diagonal v)
  convert(rfl : ⅟ (diagonal v) = _)
#align matrix.inv_of_diagonal_eq Matrix.invOf_diagonal_eq

/-- `v` is invertible if `diagonal v` is -/
def invertibleOfDiagonalInvertible (v : n → α) [Invertible (diagonal v)] : Invertible v
    where
  invOf := diag (⅟ (diagonal v))
  invOf_mul_self :=
    funext fun i =>
      by
      letI : Invertible (diagonal v).det := det_invertible_of_invertible _
      rw [inv_of_eq, diag_smul, adjugate_diagonal, diag_diagonal]
      dsimp
      rw [mul_assoc, prod_erase_mul _ _ (Finset.mem_univ _), ← det_diagonal]
      exact mul_invOf_self _
  mul_invOf_self :=
    funext fun i =>
      by
      letI : Invertible (diagonal v).det := det_invertible_of_invertible _
      rw [inv_of_eq, diag_smul, adjugate_diagonal, diag_diagonal]
      dsimp
      rw [mul_left_comm, mul_prod_erase _ _ (Finset.mem_univ _), ← det_diagonal]
      exact mul_invOf_self _
#align matrix.invertible_of_diagonal_invertible Matrix.invertibleOfDiagonalInvertible

/-- Together `matrix.diagonal_invertible` and `matrix.invertible_of_diagonal_invertible` form an
equivalence, although both sides of the equiv are subsingleton anyway. -/
@[simps]
def diagonalInvertibleEquivInvertible (v : n → α) : Invertible (diagonal v) ≃ Invertible v
    where
  toFun := @invertibleOfDiagonalInvertible _ _ _ _ _ _
  invFun := @diagonalInvertible _ _ _ _ _ _
  left_inv _ := Subsingleton.elim _ _
  right_inv _ := Subsingleton.elim _ _
#align matrix.diagonal_invertible_equiv_invertible Matrix.diagonalInvertibleEquivInvertible

/-- When lowered to a prop, `matrix.diagonal_invertible_equiv_invertible` forms an `iff`. -/
@[simp]
theorem isUnit_diagonal {v : n → α} : IsUnit (diagonal v) ↔ IsUnit v := by
  simp only [← nonempty_invertible_iff_isUnit,
    (diagonal_invertible_equiv_invertible v).nonempty_congr]
#align matrix.is_unit_diagonal Matrix.isUnit_diagonal

theorem inv_diagonal (v : n → α) : (diagonal v)⁻¹ = diagonal (Ring.inverse v) :=
  by
  rw [nonsing_inv_eq_ring_inverse]
  by_cases h : IsUnit v
  · have := is_unit_diagonal.mpr h
    cases this.nonempty_invertible
    cases h.nonempty_invertible
    rw [Ring.inverse_invertible, Ring.inverse_invertible, inv_of_diagonal_eq]
  · have := is_unit_diagonal.not.mpr h
    rw [Ring.inverse_non_unit _ h, Pi.zero_def, diagonal_zero, Ring.inverse_non_unit _ this]
#align matrix.inv_diagonal Matrix.inv_diagonal

@[simp]
theorem inv_inv_inv (A : Matrix n n α) : A⁻¹⁻¹⁻¹ = A⁻¹ :=
  by
  by_cases h : IsUnit A.det
  · rw [nonsing_inv_nonsing_inv _ h]
  · simp [nonsing_inv_apply_not_is_unit _ h]
#align matrix.inv_inv_inv Matrix.inv_inv_inv

theorem mul_inv_rev (A B : Matrix n n α) : (A ⬝ B)⁻¹ = B⁻¹ ⬝ A⁻¹ :=
  by
  simp only [inv_def]
  rw [Matrix.smul_mul, Matrix.mul_smul, smul_smul, det_mul, adjugate_mul_distrib,
    Ring.mul_inverse_rev]
#align matrix.mul_inv_rev Matrix.mul_inv_rev

/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/-- A version of `list.prod_inv_reverse` for `matrix.has_inv`. -/
theorem list_prod_inv_reverse : ∀ l : List (Matrix n n α), l.Prod⁻¹ = (l.reverse.map Inv.inv).Prod
  | [] => by rw [List.reverse_nil, List.map_nil, List.prod_nil, inv_one]
  | A::Xs => by
    rw [List.reverse_cons', List.map_concat, List.prod_concat, List.prod_cons, Matrix.mul_eq_mul,
      Matrix.mul_eq_mul, mul_inv_rev, list_prod_inv_reverse]
#align matrix.list_prod_inv_reverse Matrix.list_prod_inv_reverse

/-- One form of **Cramer's rule**. See `matrix.mul_vec_cramer` for a stronger form. -/
@[simp]
theorem det_smul_inv_mulVec_eq_cramer (A : Matrix n n α) (b : n → α) (h : IsUnit A.det) :
    A.det • A⁻¹.mulVec b = cramer A b := by
  rw [cramer_eq_adjugate_mul_vec, A.nonsing_inv_apply h, ← smul_mul_vec_assoc, smul_smul,
    h.mul_coe_inv, one_smul]
#align matrix.det_smul_inv_mul_vec_eq_cramer Matrix.det_smul_inv_mulVec_eq_cramer

/-- One form of **Cramer's rule**. See `matrix.mul_vec_cramer` for a stronger form. -/
@[simp]
theorem det_smul_inv_vecMul_eq_cramer_transpose (A : Matrix n n α) (b : n → α) (h : IsUnit A.det) :
    A.det • A⁻¹.vecMul b = cramer Aᵀ b := by
  rw [← A⁻¹.transpose_transpose, vec_mul_transpose, transpose_nonsing_inv, ← det_transpose,
    Aᵀ.det_smul_inv_mulVec_eq_cramer _ (is_unit_det_transpose A h)]
#align matrix.det_smul_inv_vec_mul_eq_cramer_transpose Matrix.det_smul_inv_vecMul_eq_cramer_transpose

/-! ### More results about determinants -/

section Det

variable [Fintype m] [DecidableEq m]

/-- A variant of `matrix.det_units_conj`. -/
theorem det_conj {M : Matrix m m α} (h : IsUnit M) (N : Matrix m m α) : det (M ⬝ N ⬝ M⁻¹) = det N :=
  by rw [← h.unit_spec, ← coe_units_inv, det_units_conj]
#align matrix.det_conj Matrix.det_conj

/-- A variant of `matrix.det_units_conj'`. -/
theorem det_conj' {M : Matrix m m α} (h : IsUnit M) (N : Matrix m m α) :
    det (M⁻¹ ⬝ N ⬝ M) = det N := by rw [← h.unit_spec, ← coe_units_inv, det_units_conj']
#align matrix.det_conj' Matrix.det_conj'

/-- Determinant of a 2×2 block matrix, expanded around an invertible top left element in terms of
the Schur complement. -/
theorem det_from_blocks₁₁ (A : Matrix m m α) (B : Matrix m n α) (C : Matrix n m α)
    (D : Matrix n n α) [Invertible A] :
    (Matrix.fromBlocks A B C D).det = det A * det (D - C ⬝ ⅟ A ⬝ B) :=
  by
  have :
    from_blocks A B C D =
      from_blocks 1 0 (C ⬝ ⅟ A) 1 ⬝ from_blocks A 0 0 (D - C ⬝ ⅟ A ⬝ B) ⬝
        from_blocks 1 (⅟ A ⬝ B) 0 1 :=
    by
    simp only [from_blocks_multiply, Matrix.mul_zero, Matrix.zero_mul, add_zero, zero_add,
      Matrix.one_mul, Matrix.mul_one, Matrix.invOf_mul_self, Matrix.mul_invOf_self_assoc,
      Matrix.mul_invOf_mul_self_cancel, Matrix.mul_assoc, add_sub_cancel'_right]
  rw [this, det_mul, det_mul, det_from_blocks_zero₂₁, det_from_blocks_zero₂₁,
    det_from_blocks_zero₁₂, det_one, det_one, one_mul, one_mul, mul_one]
#align matrix.det_from_blocks₁₁ Matrix.det_from_blocks₁₁

@[simp]
theorem det_fromBlocks_one₁₁ (B : Matrix m n α) (C : Matrix n m α) (D : Matrix n n α) :
    (Matrix.fromBlocks 1 B C D).det = det (D - C ⬝ B) :=
  by
  haveI : Invertible (1 : Matrix m m α) := invertibleOne
  rw [det_from_blocks₁₁, invOf_one, Matrix.mul_one, det_one, one_mul]
#align matrix.det_from_blocks_one₁₁ Matrix.det_fromBlocks_one₁₁

/-- Determinant of a 2×2 block matrix, expanded around an invertible bottom right element in terms
of the Schur complement. -/
theorem det_from_blocks₂₂ (A : Matrix m m α) (B : Matrix m n α) (C : Matrix n m α)
    (D : Matrix n n α) [Invertible D] :
    (Matrix.fromBlocks A B C D).det = det D * det (A - B ⬝ ⅟ D ⬝ C) :=
  by
  have : from_blocks A B C D = (from_blocks D C B A).submatrix (sum_comm _ _) (sum_comm _ _) :=
    by
    ext (i j)
    cases i <;> cases j <;> rfl
  rw [this, det_submatrix_equiv_self, det_from_blocks₁₁]
#align matrix.det_from_blocks₂₂ Matrix.det_from_blocks₂₂

@[simp]
theorem det_fromBlocks_one₂₂ (A : Matrix m m α) (B : Matrix m n α) (C : Matrix n m α) :
    (Matrix.fromBlocks A B C 1).det = det (A - B ⬝ C) :=
  by
  haveI : Invertible (1 : Matrix n n α) := invertibleOne
  rw [det_from_blocks₂₂, invOf_one, Matrix.mul_one, det_one, one_mul]
#align matrix.det_from_blocks_one₂₂ Matrix.det_fromBlocks_one₂₂

/-- The **Weinstein–Aronszajn identity**. Note the `1` on the LHS is of shape m×m, while the `1` on
the RHS is of shape n×n. -/
theorem det_one_add_mul_comm (A : Matrix m n α) (B : Matrix n m α) :
    det (1 + A ⬝ B) = det (1 + B ⬝ A) :=
  calc
    det (1 + A ⬝ B) = det (fromBlocks 1 (-A) B 1) := by
      rw [det_from_blocks_one₂₂, Matrix.neg_mul, sub_neg_eq_add]
    _ = det (1 + B ⬝ A) := by rw [det_from_blocks_one₁₁, Matrix.mul_neg, sub_neg_eq_add]

#align matrix.det_one_add_mul_comm Matrix.det_one_add_mul_comm

/-- Alternate statement of the **Weinstein–Aronszajn identity** -/
theorem det_mul_add_one_comm (A : Matrix m n α) (B : Matrix n m α) :
    det (A ⬝ B + 1) = det (B ⬝ A + 1) := by rw [add_comm, det_one_add_mul_comm, add_comm]
#align matrix.det_mul_add_one_comm Matrix.det_mul_add_one_comm

theorem det_one_sub_mul_comm (A : Matrix m n α) (B : Matrix n m α) :
    det (1 - A ⬝ B) = det (1 - B ⬝ A) := by
  rw [sub_eq_add_neg, ← Matrix.neg_mul, det_one_add_mul_comm, Matrix.mul_neg, ← sub_eq_add_neg]
#align matrix.det_one_sub_mul_comm Matrix.det_one_sub_mul_comm

/-- A special case of the **Matrix determinant lemma** for when `A = I`.

TODO: show this more generally. -/
theorem det_one_add_col_mul_row (u v : m → α) : det (1 + col u ⬝ row v) = 1 + v ⬝ᵥ u := by
  rw [det_one_add_mul_comm, det_unique, Pi.add_apply, Pi.add_apply, Matrix.one_apply_eq,
    Matrix.row_mul_col_apply]
#align matrix.det_one_add_col_mul_row Matrix.det_one_add_col_mul_row

end Det

end Matrix


/-
Copyright (c) 2021 Sébastien Gouëzel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sébastien Gouëzel
-/
import analysis.normed.group.pointwise
import analysis.normed_space.basic
import topology.metric_space.hausdorff_distance

/-!
# Properties of pointwise scalar multiplication of sets in normed spaces.

We explore the relationships between scalar multiplication of sets in vector spaces, and the norm.
Notably, we express arbitrary balls as rescaling of other balls, and we show that the
multiplication of bounded sets remain bounded.
-/

open metric set
open_locale pointwise topological_space

section normed_space

variables {𝕜 : Type*} [normed_field 𝕜] {E : Type*} [semi_normed_group E] [normed_space 𝕜 E]

theorem smul_ball {c : 𝕜} (hc : c ≠ 0) (x : E) (r : ℝ) :
  c • ball x r = ball (c • x) (∥c∥ * r) :=
begin
  ext y,
  rw mem_smul_set_iff_inv_smul_mem₀ hc,
  conv_lhs { rw ←inv_smul_smul₀ hc x },
  simp [← div_eq_inv_mul, div_lt_iff (norm_pos_iff.2 hc), mul_comm _ r, dist_smul],
end

theorem smul_sphere' {c : 𝕜} (hc : c ≠ 0) (x : E) (r : ℝ) :
  c • sphere x r = sphere (c • x) (∥c∥ * r) :=
begin
  ext y,
  rw mem_smul_set_iff_inv_smul_mem₀ hc,
  conv_lhs { rw ←inv_smul_smul₀ hc x },
  simp only [mem_sphere, dist_smul, normed_field.norm_inv, ← div_eq_inv_mul,
    div_eq_iff (norm_pos_iff.2 hc).ne', mul_comm r],
end

/-- In a nontrivial real normed space, a sphere is nonempty if and only if its radius is
nonnegative. -/
@[simp] theorem normed_space.sphere_nonempty {E : Type*} [normed_group E]
  [normed_space ℝ E] [nontrivial E] {x : E} {r : ℝ} :
  (sphere x r).nonempty ↔ 0 ≤ r :=
begin
  refine ⟨λ h, nonempty_closed_ball.1 (h.mono sphere_subset_closed_ball), λ hr, _⟩,
  rcases exists_ne x with ⟨y, hy⟩,
  have : ∥y - x∥ ≠ 0, by simpa [sub_eq_zero],
  use r • ∥y - x∥⁻¹ • (y - x) + x,
  simp [norm_smul, this, real.norm_of_nonneg hr]
end

theorem smul_sphere {E : Type*} [normed_group E] [normed_space 𝕜 E] [normed_space ℝ E]
  [nontrivial E] (c : 𝕜) (x : E) {r : ℝ} (hr : 0 ≤ r) :
  c • sphere x r = sphere (c • x) (∥c∥ * r) :=
begin
  rcases eq_or_ne c 0 with rfl|hc,
  { simp [zero_smul_set, set.singleton_zero, hr] },
  { exact smul_sphere' hc x r }
end

theorem smul_closed_ball' {c : 𝕜} (hc : c ≠ 0) (x : E) (r : ℝ) :
  c • closed_ball x r = closed_ball (c • x) (∥c∥ * r) :=
by simp only [← ball_union_sphere, set.smul_set_union, smul_ball hc, smul_sphere' hc]

lemma metric.bounded.smul {s : set E} (hs : bounded s) (c : 𝕜) :
  bounded (c • s) :=
begin
  obtain ⟨R, hR⟩ : ∃ (R : ℝ), ∀ x ∈ s, ∥x∥ ≤ R := hs.exists_norm_le,
  refine (bounded_iff_exists_norm_le).2 ⟨∥c∥ * R, _⟩,
  assume z hz,
  obtain ⟨y, ys, rfl⟩ : ∃ (y : E), y ∈ s ∧ c • y = z := mem_smul_set.1 hz,
  calc ∥c • y∥ = ∥c∥ * ∥y∥ : norm_smul _ _
  ... ≤ ∥c∥ * R : mul_le_mul_of_nonneg_left (hR y ys) (norm_nonneg _)
end

/-- If `s` is a bounded set, then for small enough `r`, the set `{x} + r • s` is contained in any
fixed neighborhood of `x`. -/
lemma eventually_singleton_add_smul_subset
  {x : E} {s : set E} (hs : bounded s) {u : set E} (hu : u ∈ 𝓝 x) :
  ∀ᶠ r in 𝓝 (0 : 𝕜), {x} + r • s ⊆ u :=
begin
  obtain ⟨ε, εpos, hε⟩ : ∃ ε (hε : 0 < ε), closed_ball x ε ⊆ u :=
    nhds_basis_closed_ball.mem_iff.1 hu,
  obtain ⟨R, Rpos, hR⟩ : ∃ (R : ℝ), 0 < R ∧ s ⊆ closed_ball 0 R := hs.subset_ball_lt 0 0,
  have : metric.closed_ball (0 : 𝕜) (ε / R) ∈ 𝓝 (0 : 𝕜) :=
    closed_ball_mem_nhds _ (div_pos εpos Rpos),
  filter_upwards [this] with r hr,
  simp only [image_add_left, singleton_add],
  assume y hy,
  obtain ⟨z, zs, hz⟩ : ∃ (z : E), z ∈ s ∧ r • z = -x + y, by simpa [mem_smul_set] using hy,
  have I : ∥r • z∥ ≤ ε := calc
    ∥r • z∥ = ∥r∥ * ∥z∥ : norm_smul _ _
    ... ≤ (ε / R) * R :
      mul_le_mul (mem_closed_ball_zero_iff.1 hr)
        (mem_closed_ball_zero_iff.1 (hR zs)) (norm_nonneg _) (div_pos εpos Rpos).le
    ... = ε : by field_simp [Rpos.ne'],
  have : y = x + r • z, by simp only [hz, add_neg_cancel_left],
  apply hε,
  simpa only [this, dist_eq_norm, add_sub_cancel', mem_closed_ball] using I,
end

lemma set_smul_mem_nhds_zero {s : set E} (hs : s ∈ 𝓝 (0 : E)) {c : 𝕜} (hc : c ≠ 0) :
  c • s ∈ 𝓝 (0 : E) :=
begin
  obtain ⟨ε, εpos, hε⟩ : ∃ (ε : ℝ) (H : 0 < ε), ball 0 ε ⊆ s := metric.mem_nhds_iff.1 hs,
  have : c • ball (0 : E) ε ∈ 𝓝 (0 : E),
  { rw [smul_ball hc, smul_zero],
    exact ball_mem_nhds _ (mul_pos (by simpa using hc) εpos) },
  exact filter.mem_of_superset this ((set_smul_subset_set_smul_iff₀ hc).2 hε)
end

lemma set_smul_mem_nhds_zero_iff (s : set E) {c : 𝕜} (hc : c ≠ 0) :
  c • s ∈ 𝓝 (0 : E) ↔ s ∈ 𝓝(0 : E) :=
begin
  refine ⟨λ h, _, λ h, set_smul_mem_nhds_zero h hc⟩,
  convert set_smul_mem_nhds_zero h (inv_ne_zero hc),
  rw [smul_smul, inv_mul_cancel hc, one_smul],
end

end normed_space

section normed_space

variables {𝕜 : Type*} [normed_field 𝕜] {E : Type*} [normed_group E] [normed_space 𝕜 E]

theorem smul_closed_ball (c : 𝕜) (x : E) {r : ℝ} (hr : 0 ≤ r) :
  c • closed_ball x r = closed_ball (c • x) (∥c∥ * r) :=
begin
  rcases eq_or_ne c 0 with rfl|hc,
  { simp [hr, zero_smul_set, set.singleton_zero, ← nonempty_closed_ball] },
  { exact smul_closed_ball' hc x r }
end

end normed_space


/-
Copyright (c) 2020 Patrick Stevens. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Patrick Stevens
-/
import tactic.ring_exp
import data.nat.parity
import data.nat.choose.sum

/-!
# Primorial

This file defines the primorial function (the product of primes less than or equal to some bound),
and proves that `primorial n ≤ 4 ^ n`.

## Notations

We use the local notation `n#` for the primorial of `n`: that is, the product of the primes less
than or equal to `n`.
-/

open finset
open nat
open_locale big_operators nat

/-- The primorial `n#` of `n` is the product of the primes less than or equal to `n`.
-/
def primorial (n : ℕ) : ℕ := ∏ p in (filter nat.prime (range (n + 1))), p
local notation x`#` := primorial x

lemma primorial_succ {n : ℕ} (n_big : 1 < n) (r : n % 2 = 1) : (n + 1)# = n# :=
begin
  have not_prime : ¬nat.prime (n + 1),
  { intros is_prime,
    cases (prime.eq_two_or_odd is_prime) with _ n_even,
    { linarith, },
    { apply nat.zero_ne_one,
      rwa [add_mod, r, nat.one_mod, ←two_mul, mul_one, nat.mod_self] at n_even, }, },
  apply finset.prod_congr,
  { rw [@range_succ (n + 1), filter_insert, if_neg not_prime], },
  { exact λ _ _, rfl, },
end

lemma dvd_choose_of_middling_prime (p : ℕ) (is_prime : nat.prime p) (m : ℕ)
  (p_big : m + 1 < p) (p_small : p ≤ 2 * m + 1) : p ∣ choose (2 * m + 1) (m + 1) :=
begin
  have m_size : m + 1 ≤ 2 * m + 1 := le_of_lt (lt_of_lt_of_le p_big p_small),
  have s : ¬(p ∣ (m + 1)!),
  { intros p_div_fact,
    have p_le_succ_m : p ≤ m + 1 := (prime.dvd_factorial is_prime).mp p_div_fact,
    linarith, },
  have t : ¬(p ∣ (2 * m + 1 - (m + 1))!),
  { intros p_div_fact,
    have p_small : p ≤ 2 * m + 1 - (m + 1) := (prime.dvd_factorial is_prime).mp p_div_fact,
    linarith, },
  have expanded :
    choose (2 * m + 1) (m + 1) * (m + 1)! * (2 * m + 1 - (m + 1))! = (2 * m + 1)! :=
    @choose_mul_factorial_mul_factorial (2 * m + 1) (m + 1) m_size,
  have p_div_big_fact : p ∣ (2 * m + 1)! := (prime.dvd_factorial is_prime).mpr p_small,
  rw [←expanded, mul_assoc] at p_div_big_fact,
  obtain p_div_choose | p_div_facts : p ∣ choose (2 * m + 1) (m + 1) ∨ p ∣ _! * _! :=
    (prime.dvd_mul is_prime).1 p_div_big_fact,
  { exact p_div_choose, },
  cases (prime.dvd_mul is_prime).1 p_div_facts,
  cc, cc,
end

lemma prod_primes_dvd {s : finset ℕ} : ∀ (n : ℕ) (h : ∀ a ∈ s, nat.prime a) (div : ∀ a ∈ s, a ∣ n),
  (∏ p in s, p) ∣ n :=
begin
  apply finset.induction_on s,
  { simp, },
  { intros a s a_not_in_s induct n primes divs,
    rw finset.prod_insert a_not_in_s,
    obtain ⟨k, rfl⟩ : a ∣ n := divs a (finset.mem_insert_self a s),
    apply mul_dvd_mul_left a,
    apply induct k,
    { intros b b_in_s,
      exact primes b (finset.mem_insert_of_mem b_in_s), },
    { intros b b_in_s,
      have b_div_n := divs b (finset.mem_insert_of_mem b_in_s),
      have a_prime := primes a (finset.mem_insert_self a s),
      have b_prime := primes b (finset.mem_insert_of_mem b_in_s),
      refine ((prime.dvd_mul b_prime).mp b_div_n).resolve_left (λ b_div_a, _),
      obtain rfl : b = a := ((nat.dvd_prime a_prime).1 b_div_a).resolve_left b_prime.ne_one,
      exact a_not_in_s b_in_s, } },
end

lemma primorial_le_4_pow : ∀ (n : ℕ), n# ≤ 4 ^ n
| 0 := le_rfl
| 1 := le_of_inf_eq rfl
| (n + 2) :=
  match nat.mod_two_eq_zero_or_one (n + 1) with
  | or.inl n_odd :=
    match nat.even_iff.2 n_odd with
    | ⟨m, twice_m⟩ :=
      let recurse : m + 1 < n + 2 := by linarith in
      begin
        calc (n + 2)#
            = ∏ i in filter nat.prime (range (2 * m + 2)), i : by simpa [←twice_m]
        ... = ∏ i in filter nat.prime (finset.Ico (m + 2) (2 * m + 2) ∪ range (m + 2)), i :
              begin
                rw [range_eq_Ico, finset.union_comm, finset.Ico_union_Ico_eq_Ico],
                exact bot_le,
                simp only [add_le_add_iff_right],
                linarith,
              end
        ... = ∏ i in (filter nat.prime (finset.Ico (m + 2) (2 * m + 2))
              ∪ (filter nat.prime (range (m + 2)))), i :
              by rw filter_union
        ... = (∏ i in filter nat.prime (finset.Ico (m + 2) (2 * m + 2)), i)
              * (∏ i in filter nat.prime (range (m + 2)), i) :
              begin
                apply finset.prod_union,
                have disj : disjoint (finset.Ico (m + 2) (2 * m + 2)) (range (m + 2)),
                { simp only [finset.disjoint_left, and_imp, finset.mem_Ico, not_lt,
                    finset.mem_range],
                  intros _ pr _, exact pr, },
                exact finset.disjoint_filter_filter disj,
              end
        ... ≤ (∏ i in filter nat.prime (finset.Ico (m + 2) (2 * m + 2)), i) * 4 ^ (m + 1) :
              nat.mul_le_mul_left _ (primorial_le_4_pow (m + 1))
        ... ≤ (choose (2 * m + 1) (m + 1)) * 4 ^ (m + 1) :
              begin
                have s : ∏ i in filter nat.prime (finset.Ico (m + 2) (2 * m + 2)),
                  i ∣ choose (2 * m + 1) (m + 1),
                { refine prod_primes_dvd  (choose (2 * m + 1) (m + 1)) _ _,
                  { intros a, rw finset.mem_filter, cc, },
                  { intros a, rw finset.mem_filter,
                    intros pr,
                    rcases pr with ⟨ size, is_prime ⟩,
                    simp only [finset.mem_Ico] at size,
                    rcases size with ⟨ a_big , a_small ⟩,
                    exact dvd_choose_of_middling_prime a is_prime m a_big
                      (nat.lt_succ_iff.mp a_small), }, },
                have r : ∏ i in filter nat.prime (finset.Ico (m + 2) (2 * m + 2)),
                  i ≤ choose (2 * m + 1) (m + 1),
                { refine @nat.le_of_dvd _ _ _ s,
                  exact @choose_pos (2 * m + 1) (m + 1) (by linarith), },
                exact nat.mul_le_mul_right _ r,
              end
        ... = (choose (2 * m + 1) m) * 4 ^ (m + 1) : by rw choose_symm_half m
        ... ≤ 4 ^ m * 4 ^ (m + 1) : nat.mul_le_mul_right _ (choose_middle_le_pow m)
        ... = 4 ^ (2 * m + 1) : by ring_exp
        ... = 4 ^ (n + 2) : by rw ←twice_m,
      end
    end
  | or.inr n_even :=
    begin
      obtain one_lt_n | n_le_one : 1 < n + 1 ∨ n + 1 ≤ 1 := lt_or_le 1 (n + 1),
      { rw primorial_succ (by linarith) n_even,
        calc (n + 1)#
              ≤ 4 ^ n.succ : primorial_le_4_pow (n + 1)
          ... ≤ 4 ^ (n + 2) : pow_le_pow (by norm_num) (nat.le_succ _), },
      { have n_zero : n = 0 := eq_bot_iff.2 (succ_le_succ_iff.1 n_le_one),
        norm_num [n_zero, primorial, range_succ, prod_filter, nat.not_prime_zero, nat.prime_two] },
    end

end


variables p q r : Prop

-- commutativity of ∧ and ∨
example : p ∧ q ↔ q ∧ p :=
  ⟨λ h, ⟨h.right, h.left⟩, λ h, ⟨h.right, h.left⟩⟩
example : p ∨ q ↔ q ∨ p :=
  ⟨λ h, h.elim (λ hp, or.inr hp) (λ hq, or.inl hq), λ h, h.elim (λ hp, or.inr hp) (λ hq, or.inl hq)⟩

-- associativity of ∧ and ∨
example : (p ∧ q) ∧ r ↔ p ∧ (q ∧ r) :=
  ⟨λ h, ⟨h.left.left, h.left.right, h.right⟩, λ h, ⟨⟨h.left, h.right.left⟩, h.right.right⟩⟩
example : (p ∨ q) ∨ r ↔ p ∨ (q ∨ r) :=
  ⟨λ h,
    h.elim
      (assume hpq,
        hpq.elim
          (assume hp, or.inl hp)
          (assume hq, or.inr (or.inl hq)))
      (assume hr,
        or.inr (or.inr hr)),
   λ h,
     h.elim
       (assume hp, or.inl (or.inl hp))
       (assume hqr,
         hqr.elim
           (assume hq, or.inl (or.inr hq))
           (assume hr, or.inr hr))⟩

-- distributivity
example : p ∧ (q ∨ r) ↔ (p ∧ q) ∨ (p ∧ r) :=
  ⟨λ h,
    h.right.elim
      (assume hq, or.inl ⟨h.left, hq⟩)
      (assume hr, or.inr ⟨h.left, hr⟩),
   λ h,
    h.elim
      (assume hpq, ⟨hpq.left, or.inl hpq.right⟩)
      (assume hpr, ⟨hpr.left, or.inr hpr.right⟩)⟩
example : p ∨ (q ∧ r) ↔ (p ∨ q) ∧ (p ∨ r) :=
  ⟨λ h,
    h.elim
      (assume hp, ⟨or.inl hp, or.inl hp⟩)
      (assume hqr, ⟨or.inr hqr.left, or.inr hqr.right⟩),
   λ h,
    h.left.elim
      (assume hp, or.inl hp)
      (assume hq,
        h.right.elim
          (assume hp, or.inl hp)
          (assume hr, or.inr ⟨hq, hr⟩))⟩

-- other properties
example : (p → (q → r)) ↔ (p ∧ q → r) :=
  ⟨λ h hpq, h hpq.left hpq.right, λ h hp hq, h ⟨hp, hq⟩⟩
example : ((p ∨ q) → r) ↔ (p → r) ∧ (q → r) :=
  ⟨λ h, ⟨λ hp, h (or.inl hp), λ hq, h (or.inr hq)⟩,
   λ h hpq,
     hpq.elim
       (assume hp, h.left hp)
       (assume hq, h.right hq)⟩
example : ¬(p ∨ q) ↔ ¬p ∧ ¬q :=
  ⟨λ h, ⟨λ hp, h (or.inl hp), λ hq, h (or.inr hq)⟩, λ h hpq, hpq.elim h.left h.right⟩
example : ¬p ∨ ¬q → ¬(p ∧ q) :=
  assume h hpq,
  h.elim
    (assume hnp, hnp hpq.left)
    (assume hnq, hnq hpq.right)
example : ¬(p ∧ ¬p) := λ h, h.right h.left
example : p ∧ ¬q → ¬(p → q) :=
  assume hpnq hp2q,
  suffices hq : q, from hpnq.right hq,
  show q, from hp2q hpnq.left
example : ¬p → (p → q) := λ hnp hp, absurd hp hnp
example : (¬p ∨ q) → (p → q) :=
  assume hnpq hp,
  hnpq.elim
    (assume hnp, absurd hp hnp)
    (assume hq, hq)
example : p ∨ false ↔ p :=
  ⟨λ h, h.elim id false.elim, λ h, or.inl h⟩
example : p ∧ false ↔ false :=
  ⟨λ h, h.right, λ h, false.elim h⟩
example : ¬(p ↔ ¬p) :=
  assume h,
  suffices hnp : ¬p, from hnp (h.mpr hnp),
  assume hp,
  h.mp hp hp
example : (p → q) → (¬q → ¬p) :=
  assume hp2q hnq hp,
  hnq (hp2q hp)


open classical

--one way DeMorgan's Law
lemma DeMorganOne : ∀ P Q : Prop, ¬ (P ∧ Q) → ¬ P ∨ ¬ Q :=
begin
assume P Q : Prop,
assume npq : ¬ (P ∧ Q),
change (P ∧ Q) → false at npq,
have pnp : P ∨ ¬ P := em P,
have qnq : Q ∨ ¬ Q := em Q,
cases pnp with p np,
cases qnq with q nq,
have pq : P ∧ Q := and.intro p q,
have f : false := npq pq,
apply false.elim f,
right,
assumption,
left,
assumption,
end

--check to make sure it works
variables (A B : Prop)
#check DeMorganOne ¬ A ¬ B

--defining ∨ when you only have ∧
example : ∀ P Q : Prop, P ∨ Q ↔ ¬(¬P ∧ ¬Q) :=
begin
assume P Q : Prop,
split,
assume pq : P ∨ Q,
assume npq : ¬ P ∧ ¬ Q,
show false,
from
  begin
  cases pq with p q,
  have np : ¬ P := and.elim_left npq,
  contradiction,
  have nq : ¬ Q := and.elim_right npq,
  contradiction,
  end,
assume nnpnq,
have DMO := DeMorganOne (¬ P) (¬ Q),
have nnpnnq : ¬ ¬ P ∨ ¬ ¬ Q := DMO nnpnq,
have pnp : P ∨ ¬ P := em P,
have qnq : Q ∨ ¬ Q := em Q,
cases nnpnnq with nnp nnq,
change (P → false) → false at nnp,
cases pnp with p np,
left,
assumption,
change (P → false) at np,
have f : false := nnp np,
trivial,
change (Q → false) → false at nnq,
cases qnq with q nq,
right,
assumption,
change (Q → false) at nq,
have f : false := nnq nq,
trivial,
end

--going the other way is easy (I will use more shortcuts)
lemma DeMorganTwo : ∀ P Q : Prop, ¬ (P ∨ Q) → ¬ P ∧ ¬ Q :=
begin
intros,
change (P ∨ Q) → false at a,
have pnp := em P,
have qnq := em Q,
cases pnp with p np,
have pq := or.inl p,
have f := a pq,
trivial,
cases qnq with q nq,
have pq := or.inr q,
have f := a pq,
trivial,
apply and.intro np nq,
end

-- defining ∧ from ∨
example : ∀ P Q : Prop, P ∧ Q ↔ ¬ (¬ P ∨ ¬ Q) :=
begin
intros,
split,
intros,
assume b,
show false,
from
  begin
  have p := and.elim_left a,
  have q := and.elim_right a,
  cases b with np nq,
  trivial,
  trivial,
  end,
intros,
have DMT := DeMorganTwo (¬ P) (¬ Q),
have nnpnnq := DMT a,
have pnp := em P,
have qnq := em Q,
cases nnpnnq with nnp nnq,
change (¬ P) → false at nnp,
change (¬ Q) → false at nnq,
cases pnp with p np,
cases qnq with q nq,
apply and.intro p q,
have f := nnq nq,
trivial,
have f := nnp np,
trivial
end

--proving them again without relying on a proof of DML
example : ∀ P Q : Prop, P ∨ Q ↔ ¬(¬P ∧ ¬Q) :=
begin
intros,
split,
intros,
assume npq,
show false,
from
  begin
  cases a,
  have np := and.elim_left npq,
  trivial,
  have nq := and.elim_right npq,
  trivial,
  end,
assume nnpnq,
have pnp : P ∨ ¬ P := em P,
have qnq : Q ∨ ¬ Q := em Q,
change (¬ P ∧ ¬ Q) → false at nnpnq,
cases pnp,
left,
assumption,
cases qnq,
right,
assumption,
have f := nnpnq (and.intro pnp qnq),
trivial,
end

example : ∀ P Q : Prop, P ∧ Q ↔ ¬ (¬ P ∨ ¬ Q) :=
begin
intros,
split,
intros,
assume b,
show false,
from
  begin
  have p := and.elim_left a,
  have q := and.elim_right a,
  cases b with np nq,
  trivial,
  trivial,
  end,
intros,
have pnp := em P,
have qnq := em Q,
change (¬ P ∨ ¬ Q) → false at a,
cases pnp with p np,
cases qnq with q nq,
apply and.intro p q,
have f := a (or.inr nq),
trivial,
have f := a (or.inl np),
trivial,
end


-- Kuratowski-finite powerset
import .preds
       galois.tactic

universes u

section
parameter {A : Type u}
def incl_elements (xs ys : list A) : Prop :=
  ∀ x : A, x ∈ xs → x ∈ ys

def same_elements (xs ys : list A) : Prop :=
  ∀ x : A, x ∈ xs ↔ x ∈ ys

def incl_same {xs ys : list A}
  (H : incl_elements xs ys) (H' : incl_elements ys xs)
  : same_elements xs ys
:= begin
intros z, split, apply H, apply H'
end

lemma incl_elements_refl (xs : list A) : incl_elements xs xs
:= begin
intros x H, apply H
end

lemma incl_elements_trans {xs ys zs : list A}
  (H : incl_elements xs ys) (H' : incl_elements ys zs)
  : incl_elements xs zs
:= begin
intros x H'', apply H', apply H, assumption,
end

lemma incl_elements_app {xs ys xs' ys' : list A}
  (H : incl_elements xs xs') (H' : incl_elements ys ys')
  : incl_elements (xs ++ ys) (xs' ++ ys')
:= begin
intros z X, rw list.mem_append, rw list.mem_append at X,
induction X with X X,
left, apply H, assumption,
right, apply H', assumption
end

lemma same_elements_app_comm {xs ys : list A}
  : same_elements (xs ++ ys) (ys ++ xs)
:= begin
intros z, repeat {rw list.mem_append},
rw or_comm,
end

lemma same_elements_refl (xs : list A) : same_elements xs xs
:= begin
dsimp [same_elements], intros, trivial,
end

lemma same_elements_trans {xs ys zs : list A}
  (H : same_elements xs ys) (H' : same_elements ys zs)
  : same_elements xs zs
:= begin
dsimp [same_elements],
intros, rw H, rw H',
end

lemma same_elements_symm {xs ys : list A}
  (H : same_elements xs ys)
  : same_elements ys xs
:= begin
dsimp [same_elements] at *,
intros, rw H,
end

def same_incl1 {xs ys : list A}
  (H : same_elements xs ys)
  : incl_elements xs ys
:= begin
intros z,
destruct (H z), intros, apply mp, assumption
end

def same_incl2 {xs ys : list A}
  (H : same_elements xs ys)
  : incl_elements ys xs
:= begin
intros z,
destruct (H z), intros, apply mpr, assumption
end

def same_incl  {xs ys : list A}
  (H : same_elements xs ys)
  : incl_elements xs ys ∧ incl_elements ys xs
:= begin
split, apply (same_incl1 H), apply (same_incl2 H)
end

lemma same_elements_app {xs ys xs' ys' : list A}
  (H : same_elements xs xs') (H' : same_elements ys ys')
  : same_elements (xs ++ ys) (xs' ++ ys')
:= begin
apply incl_same,
apply incl_elements_app; apply same_incl1; assumption,
apply incl_elements_app; apply same_incl2; assumption,
end

def cons_mono {x y : A} {xs ys : list A}
  (Hhd : x = y)
  (Htl : incl_elements xs ys)
  : incl_elements (x :: xs) (y :: ys)
:= begin
induction Hhd,
intros z Hz, simp [has_mem.mem, list.mem] at Hz,
induction Hz, induction a, simp [has_mem.mem, list.mem],
simp [has_mem.mem, list.mem],
right, apply Htl, assumption
end

def cons_same {x y : A} {xs ys : list A}
  (Hhd : x = y)
  (Htl : same_elements xs ys)
  : same_elements (x :: xs) (y :: ys)
:= begin
have Htl' := same_incl Htl, clear Htl,
induction Htl' with Htl1 Htl2,
apply incl_same; apply cons_mono, assumption,
apply Htl1, symmetry, assumption, apply Htl2,
end

end

def fpow (A : Type u) := quot (@same_elements A)

namespace fpow
section
parameter {A : Type u}

def from_list : list A → fpow A := quot.mk _

def nil : fpow A := quot.mk _ []

def cons (x : A) (xs : fpow A) : fpow A :=
begin
fapply (quot.lift_on xs); clear xs,
exact (λ xs, quot.mk _ (x :: xs)),
intros xs ys Heq, apply quot.sound,
unfold same_elements,
intros z, apply cons_same, reflexivity, assumption
end

instance mem  : has_mem A (fpow A) :=
begin
constructor, intros x xs,
fapply (quot.lift_on xs); clear xs,
exact (λ xs, x ∈ xs),
intros xs ys H, apply propext, apply H
end

end

end fpow

lemma same_elements_fpow {A} {xs ys : list A}
  : same_elements xs ys ↔ fpow.from_list xs = fpow.from_list ys
:= begin
split; intros H,
apply quot.sound, assumption,
have H' := quot.exact _ H,
clear H,
induction H', assumption,
apply same_elements_refl,
apply same_elements_symm; assumption,
apply same_elements_trans; assumption,
end


import tactic
import data.set.basic

open set

/-- a cover of X is a set of subsets of X whose union is X -/
structure cover (X : Type) :=
(C : set (set X))
(cov : ∀ x : X, ∃ U ∈ C, x ∈ U)

-- note that the subset X of X isn't called X! X is a type. The subset X of X
-- is called `univ`

/-- The cover {X} of X -/
def univ_cover (X : Type) : cover X :=
{ C := {univ},
  cov := by simp} -- proof it's a cover is obvious and `simp` finds it

variable {X : Type}

-- definition of star refinement
def star_ref (P : cover X) (Q : cover X) :=
∀ A ∈ P.C, ∃ U ∈ Q.C, ∀ B ∈ P.C, A ∩ B ≠ ∅ → B ⊆ U

-- this may or may not work, Lean might get confused because `<` means something else
notation P ` <* ` Q := star_ref P Q

theorem star_ref_iff (P : cover X) (Q : cover X) :
P <* Q ↔ ∀ A ∈ P.C, ∃ U ∈ Q.C, ∀ B ∈ P.C, A ∩ B ≠ ∅ → B ⊆ U := iff.rfl

/-
    {X} is a uniform cover (i.e. {X} ∈ Θ).
    If P <* Q and P is a uniform cover, then Q is also a uniform cover.
    If P and Q are uniform covers, then there is a uniform cover R that
    star-refines both P and Q.

Given a point x and a uniform cover P, one can consider the union of the members of P that contain x as a typical neighbourhood of x of "size" P, and this intuitive measure applies uniformly over the space.

Given a uniform space in the entourage sense, define a cover P to be uniform if there is some entourage U such that for each x ∈ X, there is an A ∈ P such that U[x] ⊆ A. These uniform covers form a uniform space as in the second definition. Conversely, given a uniform space in the uniform cover sense, the supersets of ⋃{A × A : A ∈ P}, as P ranges over the uniform covers, are the entourages for a uniform space as in the first definition. Moreover, these two transformations are inverses of each other.
-/

/-- A distinguished family of covers for a set X is a filter for <*  -/
structure dist_covers (X : Type) :=
-- collection of covers
(Θ : set (cover X) )
-- {X} is in Θ
(univ_mem : univ_cover X ∈ Θ)
-- if P is in Θ and P <* Q then Q is in Θ
(star_mem (P Q : cover X) (hP : P ∈ Θ) (hPQ : P <* Q) : Q ∈ Θ)
-- if P, Q ∈ Θ then there exists R ∈ Θ with P <* R and Q <* R
(ub_mem (P Q : cover X) (hP : P ∈ Θ) (hQ : Q ∈ Θ) : ∃ R : cover X, R ∈ Θ ∧ R <* P ∧ R <* Q)


/-
Copyright (c) 2022 Violeta Hernández Palacios. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Violeta Hernández Palacios

! This file was ported from Lean 3 source module set_theory.game.birthday
! leanprover-community/mathlib commit a347076985674932c0e91da09b9961ed0a79508c
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.SetTheory.Game.Ordinal
import Mathbin.SetTheory.Ordinal.NaturalOps

/-!
# Birthdays of games

The birthday of a game is an ordinal that represents at which "step" the game was constructed. We
define it recursively as the least ordinal larger than the birthdays of its left and right games. We
prove the basic properties about these.

# Main declarations

- `pgame.birthday`: The birthday of a pre-game.

# Todo

- Define the birthdays of `game`s and `surreal`s.
- Characterize the birthdays of basic arithmetical operations.
-/

universe u

open Ordinal

open NaturalOps Pgame

namespace Pgame

/-- The birthday of a pre-game is inductively defined as the least strict upper bound of the
birthdays of its left and right games. It may be thought as the "step" in which a certain game is
constructed. -/
noncomputable def birthday : Pgame.{u} → Ordinal.{u}
  | ⟨xl, xr, xL, xR⟩ =>
    max (lsub.{u, u} fun i => birthday (xL i)) (lsub.{u, u} fun i => birthday (xR i))
#align pgame.birthday Pgame.birthday

theorem birthday_def (x : Pgame) :
    birthday x =
      max (lsub.{u, u} fun i => birthday (x.moveLeft i))
        (lsub.{u, u} fun i => birthday (x.moveRight i)) :=
  by
  cases x
  rw [birthday]
  rfl
#align pgame.birthday_def Pgame.birthday_def

theorem birthday_moveLeft_lt {x : Pgame} (i : x.LeftMoves) : (x.moveLeft i).birthday < x.birthday :=
  by
  cases x
  rw [birthday]
  exact lt_max_of_lt_left (lt_lsub _ i)
#align pgame.birthday_move_left_lt Pgame.birthday_moveLeft_lt

theorem birthday_moveRight_lt {x : Pgame} (i : x.RightMoves) :
    (x.moveRight i).birthday < x.birthday := by
  cases x
  rw [birthday]
  exact lt_max_of_lt_right (lt_lsub _ i)
#align pgame.birthday_move_right_lt Pgame.birthday_moveRight_lt

theorem lt_birthday_iff {x : Pgame} {o : Ordinal} :
    o < x.birthday ↔
      (∃ i : x.LeftMoves, o ≤ (x.moveLeft i).birthday) ∨
        ∃ i : x.RightMoves, o ≤ (x.moveRight i).birthday :=
  by
  constructor
  · rw [birthday_def]
    intro h
    cases' lt_max_iff.1 h with h' h'
    · left
      rwa [lt_lsub_iff] at h'
    · right
      rwa [lt_lsub_iff] at h'
  · rintro (⟨i, hi⟩ | ⟨i, hi⟩)
    · exact hi.trans_lt (birthday_move_left_lt i)
    · exact hi.trans_lt (birthday_move_right_lt i)
#align pgame.lt_birthday_iff Pgame.lt_birthday_iff

theorem Relabelling.birthday_congr : ∀ {x y : Pgame.{u}}, x ≡r y → birthday x = birthday y
  | ⟨xl, xr, xL, xR⟩, ⟨yl, yr, yL, yR⟩, r =>
    by
    unfold birthday
    congr 1
    all_goals
      apply lsub_eq_of_range_eq.{u, u, u}
      ext i; constructor
    all_goals rintro ⟨j, rfl⟩
    · exact ⟨_, (r.move_left j).birthday_congr.symm⟩
    · exact ⟨_, (r.move_left_symm j).birthday_congr⟩
    · exact ⟨_, (r.move_right j).birthday_congr.symm⟩
    · exact ⟨_, (r.move_right_symm j).birthday_congr⟩decreasing_by pgame_wf_tac
#align pgame.relabelling.birthday_congr Pgame.Relabelling.birthday_congr

@[simp]
theorem birthday_eq_zero {x : Pgame} :
    birthday x = 0 ↔ IsEmpty x.LeftMoves ∧ IsEmpty x.RightMoves := by
  rw [birthday_def, max_eq_zero, lsub_eq_zero_iff, lsub_eq_zero_iff]
#align pgame.birthday_eq_zero Pgame.birthday_eq_zero

@[simp]
theorem birthday_zero : birthday 0 = 0 := by simp [PEmpty.isEmpty]
#align pgame.birthday_zero Pgame.birthday_zero

@[simp]
theorem birthday_one : birthday 1 = 1 := by
  rw [birthday_def]
  simp
#align pgame.birthday_one Pgame.birthday_one

@[simp]
theorem birthday_star : birthday star = 1 :=
  by
  rw [birthday_def]
  simp
#align pgame.birthday_star Pgame.birthday_star

@[simp]
theorem neg_birthday : ∀ x : Pgame, (-x).birthday = x.birthday
  | ⟨xl, xr, xL, xR⟩ => by
    rw [birthday_def, birthday_def, max_comm]
    congr <;> funext <;> apply neg_birthday
#align pgame.neg_birthday Pgame.neg_birthday

@[simp]
theorem toPgame_birthday (o : Ordinal) : o.toPgame.birthday = o :=
  by
  induction' o using Ordinal.induction with o IH
  rw [to_pgame_def, Pgame.birthday]
  simp only [lsub_empty, max_zero_right]
  nth_rw 1 [← lsub_typein o]
  congr with x
  exact IH _ (typein_lt_self x)
#align pgame.to_pgame_birthday Pgame.toPgame_birthday

theorem le_birthday : ∀ x : Pgame, x ≤ x.birthday.toPgame
  | ⟨xl, _, xL, _⟩ =>
    le_def.2
      ⟨fun i =>
        Or.inl ⟨toLeftMovesToPgame ⟨_, birthday_moveLeft_lt i⟩, by simp [le_birthday (xL i)]⟩,
        isEmptyElim⟩
#align pgame.le_birthday Pgame.le_birthday

variable (a b x : Pgame.{u})

theorem neg_birthday_le : -x.birthday.toPgame ≤ x := by
  simpa only [neg_birthday, ← neg_le_iff] using le_birthday (-x)
#align pgame.neg_birthday_le Pgame.neg_birthday_le

@[simp]
theorem birthday_add : ∀ x y : Pgame.{u}, (x + y).birthday = x.birthday ♯ y.birthday
  | ⟨xl, xr, xL, xR⟩, ⟨yl, yr, yL, yR⟩ =>
    by
    rw [birthday_def, nadd_def]
    simp only [birthday_add, lsub_sum, mk_add_move_left_inl, move_left_mk, mk_add_move_left_inr,
      mk_add_move_right_inl, move_right_mk, mk_add_move_right_inr]
    rw [max_max_max_comm]
    congr <;> apply le_antisymm
    any_goals
      exact
        max_le_iff.2
          ⟨lsub_le_iff.2 fun i => lt_blsub _ _ (birthday_move_left_lt i),
            lsub_le_iff.2 fun i => lt_blsub _ _ (birthday_move_right_lt i)⟩
    all_goals
      apply blsub_le_iff.2 fun i hi => _
      rcases lt_birthday_iff.1 hi with (⟨j, hj⟩ | ⟨j, hj⟩)
    · exact lt_max_of_lt_left ((nadd_le_nadd_right hj _).trans_lt (lt_lsub _ _))
    · exact lt_max_of_lt_right ((nadd_le_nadd_right hj _).trans_lt (lt_lsub _ _))
    · exact lt_max_of_lt_left ((nadd_le_nadd_left hj _).trans_lt (lt_lsub _ _))
    · exact lt_max_of_lt_right ((nadd_le_nadd_left hj _).trans_lt (lt_lsub _ _))decreasing_by
  pgame_wf_tac
#align pgame.birthday_add Pgame.birthday_add

theorem birthday_add_zero : (a + 0).birthday = a.birthday := by simp
#align pgame.birthday_add_zero Pgame.birthday_add_zero

theorem birthday_zero_add : (0 + a).birthday = a.birthday := by simp
#align pgame.birthday_zero_add Pgame.birthday_zero_add

theorem birthday_add_one : (a + 1).birthday = Order.succ a.birthday := by simp
#align pgame.birthday_add_one Pgame.birthday_add_one

theorem birthday_one_add : (1 + a).birthday = Order.succ a.birthday := by simp
#align pgame.birthday_one_add Pgame.birthday_one_add

@[simp]
theorem birthday_nat_cast : ∀ n : ℕ, birthday n = n
  | 0 => birthday_zero
  | n + 1 => by simp [birthday_nat_cast]
#align pgame.birthday_nat_cast Pgame.birthday_nat_cast

theorem birthday_add_nat (n : ℕ) : (a + n).birthday = a.birthday + n := by simp
#align pgame.birthday_add_nat Pgame.birthday_add_nat

theorem birthday_nat_add (n : ℕ) : (↑n + a).birthday = a.birthday + n := by simp
#align pgame.birthday_nat_add Pgame.birthday_nat_add

end Pgame


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

! This file was ported from Lean 3 source module data.complex.module
! leanprover-community/mathlib commit c310cfdc40da4d99a10a58c33a95360ef9e6e0bf
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.LinearAlgebra.Orientation
import Mathbin.Algebra.Order.Smul
import Mathbin.Data.Complex.Basic
import Mathbin.Data.Fin.VecNotation
import Mathbin.FieldTheory.Tower
import Mathbin.Algebra.CharP.Invertible

/-!
# Complex number as a vector space over `ℝ`

This file contains the following instances:
* Any `•`-structure (`has_smul`, `mul_action`, `distrib_mul_action`, `module`, `algebra`) on
  `ℝ` imbues a corresponding structure on `ℂ`. This includes the statement that `ℂ` is an `ℝ`
  algebra.
* any complex vector space is a real vector space;
* any finite dimensional complex vector space is a finite dimensional real vector space;
* the space of `ℝ`-linear maps from a real vector space to a complex vector space is a complex
  vector space.

It also defines bundled versions of four standard maps (respectively, the real part, the imaginary
part, the embedding of `ℝ` in `ℂ`, and the complex conjugate):

* `complex.re_lm` (`ℝ`-linear map);
* `complex.im_lm` (`ℝ`-linear map);
* `complex.of_real_am` (`ℝ`-algebra (homo)morphism);
* `complex.conj_ae` (`ℝ`-algebra equivalence).

It also provides a universal property of the complex numbers `complex.lift`, which constructs a
`ℂ →ₐ[ℝ] A` into any `ℝ`-algebra `A` given a square root of `-1`.

In addition, this file provides a decomposition into `real_part` and `imaginary_part` for any
element of a `star_module` over `ℂ`.

## Notation

* `ℜ` and `ℑ` for the `real_part` and `imaginary_part`, respectively, in the locale
  `complex_star_module`.
-/

namespace Complex

open ComplexConjugate

variable {R : Type _} {S : Type _}

section

variable [SMul R ℝ]

/- The useless `0` multiplication in `smul` is to make sure that
`restrict_scalars.module ℝ ℂ ℂ = complex.module` definitionally. -/
instance : SMul R ℂ where smul r x := ⟨r • x.re - 0 * x.im, r • x.im + 0 * x.re⟩

theorem smul_re (r : R) (z : ℂ) : (r • z).re = r • z.re := by simp [(· • ·)]
#align complex.smul_re Complex.smul_re

theorem smul_im (r : R) (z : ℂ) : (r • z).im = r • z.im := by simp [(· • ·)]
#align complex.smul_im Complex.smul_im

@[simp]
theorem real_smul {x : ℝ} {z : ℂ} : x • z = x * z :=
  rfl
#align complex.real_smul Complex.real_smul

end

instance [SMul R ℝ] [SMul S ℝ] [SMulCommClass R S ℝ] : SMulCommClass R S ℂ
    where smul_comm r s x := by ext <;> simp [smul_re, smul_im, smul_comm]

instance [SMul R S] [SMul R ℝ] [SMul S ℝ] [IsScalarTower R S ℝ] : IsScalarTower R S ℂ
    where smul_assoc r s x := by ext <;> simp [smul_re, smul_im, smul_assoc]

instance [SMul R ℝ] [SMul Rᵐᵒᵖ ℝ] [IsCentralScalar R ℝ] : IsCentralScalar R ℂ
    where op_smul_eq_smul r x := by ext <;> simp [smul_re, smul_im, op_smul_eq_smul]

instance [Monoid R] [MulAction R ℝ] : MulAction R ℂ
    where
  one_smul x := by ext <;> simp [smul_re, smul_im, one_smul]
  mul_smul r s x := by ext <;> simp [smul_re, smul_im, mul_smul]

instance [DistribSMul R ℝ] : DistribSMul R ℂ
    where
  smul_add r x y := by ext <;> simp [smul_re, smul_im, smul_add]
  smul_zero r := by ext <;> simp [smul_re, smul_im, smul_zero]

instance [Semiring R] [DistribMulAction R ℝ] : DistribMulAction R ℂ :=
  { Complex.distribSmul with }

instance [Semiring R] [Module R ℝ] : Module R ℂ
    where
  add_smul r s x := by ext <;> simp [smul_re, smul_im, add_smul]
  zero_smul r := by ext <;> simp [smul_re, smul_im, zero_smul]

instance [CommSemiring R] [Algebra R ℝ] : Algebra R ℂ :=
  { Complex.ofReal.comp (algebraMap R ℝ) with
    smul := (· • ·)
    smul_def' := fun r x => by ext <;> simp [smul_re, smul_im, Algebra.smul_def]
    commutes' := fun r ⟨xr, xi⟩ => by ext <;> simp [smul_re, smul_im, Algebra.commutes] }

instance : StarModule ℝ ℂ :=
  ⟨fun r x => by simp only [star_def, star_trivial, real_smul, map_mul, conj_of_real]⟩

@[simp]
theorem coe_algebraMap : (algebraMap ℝ ℂ : ℝ → ℂ) = coe :=
  rfl
#align complex.coe_algebra_map Complex.coe_algebraMap

section

variable {A : Type _} [Semiring A] [Algebra ℝ A]

/-- We need this lemma since `complex.coe_algebra_map` diverts the simp-normal form away from
`alg_hom.commutes`. -/
@[simp]
theorem AlgHom.map_coe_real_complex (f : ℂ →ₐ[ℝ] A) (x : ℝ) : f x = algebraMap ℝ A x :=
  f.commutes x
#align alg_hom.map_coe_real_complex AlgHom.map_coe_real_complex

/-- Two `ℝ`-algebra homomorphisms from ℂ are equal if they agree on `complex.I`. -/
@[ext]
theorem algHom_ext ⦃f g : ℂ →ₐ[ℝ] A⦄ (h : f I = g I) : f = g :=
  by
  ext ⟨x, y⟩
  simp only [mk_eq_add_mul_I, AlgHom.map_add, AlgHom.map_coe_real_complex, AlgHom.map_mul, h]
#align complex.alg_hom_ext Complex.algHom_ext

end

section

open ComplexOrder

protected theorem orderedSMul : OrderedSMul ℝ ℂ :=
  OrderedSMul.mk' fun a b r hab hr => ⟨by simp [hr, hab.1.le], by simp [hab.2]⟩
#align complex.ordered_smul Complex.orderedSMul

scoped[ComplexOrder] attribute [instance] Complex.orderedSMul

end

open Submodule FiniteDimensional

/-- `ℂ` has a basis over `ℝ` given by `1` and `I`. -/
noncomputable def basisOneI : Basis (Fin 2) ℝ ℂ :=
  Basis.ofEquivFun
    { toFun := fun z => ![z.re, z.im]
      invFun := fun c => c 0 + c 1 • I
      left_inv := fun z => by simp
      right_inv := fun c => by
        ext i
        fin_cases i <;> simp
      map_add' := fun z z' => by simp
      -- why does `simp` not know how to apply `smul_cons`, which is a `@[simp]` lemma, here?
      map_smul' := fun c z => by simp [Matrix.smul_cons c z.re, Matrix.smul_cons c z.im] }
#align complex.basis_one_I Complex.basisOneI

@[simp]
theorem coe_basisOneI_repr (z : ℂ) : ⇑(basisOneI.repr z) = ![z.re, z.im] :=
  rfl
#align complex.coe_basis_one_I_repr Complex.coe_basisOneI_repr

@[simp]
theorem coe_basisOneI : ⇑basisOneI = ![1, I] :=
  funext fun i =>
    Basis.apply_eq_iff.mpr <|
      Finsupp.ext fun j => by
        fin_cases i <;> fin_cases j <;>
          simp only [coe_basis_one_I_repr, Finsupp.single_eq_of_ne, Matrix.cons_val_zero,
            Matrix.cons_val_one, Matrix.head_cons, Fin.one_eq_zero_iff, Ne.def, not_false_iff, I_re,
            Nat.succ_succ_ne_one, one_im, I_im, one_re, Finsupp.single_eq_same, Fin.zero_eq_one_iff]
#align complex.coe_basis_one_I Complex.coe_basisOneI

instance : FiniteDimensional ℝ ℂ :=
  of_fintype_basis basisOneI

@[simp]
theorem finrank_real_complex : FiniteDimensional.finrank ℝ ℂ = 2 := by
  rw [finrank_eq_card_basis basis_one_I, Fintype.card_fin]
#align complex.finrank_real_complex Complex.finrank_real_complex

@[simp]
theorem dim_real_complex : Module.rank ℝ ℂ = 2 := by simp [← finrank_eq_dim, finrank_real_complex]
#align complex.dim_real_complex Complex.dim_real_complex

theorem dim_real_complex'.{u} : Cardinal.lift.{u} (Module.rank ℝ ℂ) = 2 := by
  simp [← finrank_eq_dim, finrank_real_complex, bit0]
#align complex.dim_real_complex' Complex.dim_real_complex'

/-- `fact` version of the dimension of `ℂ` over `ℝ`, locally useful in the definition of the
circle. -/
theorem finrank_real_complex_fact : Fact (finrank ℝ ℂ = 2) :=
  ⟨finrank_real_complex⟩
#align complex.finrank_real_complex_fact Complex.finrank_real_complex_fact

/-- The standard orientation on `ℂ`. -/
protected noncomputable def orientation : Orientation ℝ ℂ (Fin 2) :=
  Complex.basisOneI.Orientation
#align complex.orientation Complex.orientation

end Complex

/- Register as an instance (with low priority) the fact that a complex vector space is also a real
vector space. -/
instance (priority := 900) Module.complexToReal (E : Type _) [AddCommGroup E] [Module ℂ E] :
    Module ℝ E :=
  RestrictScalars.module ℝ ℂ E
#align module.complex_to_real Module.complexToReal

instance Module.real_complex_tower (E : Type _) [AddCommGroup E] [Module ℂ E] :
    IsScalarTower ℝ ℂ E :=
  RestrictScalars.isScalarTower ℝ ℂ E
#align module.real_complex_tower Module.real_complex_tower

@[simp, norm_cast]
theorem Complex.coe_smul {E : Type _} [AddCommGroup E] [Module ℂ E] (x : ℝ) (y : E) :
    (x : ℂ) • y = x • y :=
  rfl
#align complex.coe_smul Complex.coe_smul

/-- The scalar action of `ℝ` on a `ℂ`-module `E` induced by `module.complex_to_real` commutes with
another scalar action of `M` on `E` whenever the action of `ℂ` commutes with the action of `M`. -/
instance (priority := 900) SMulCommClass.complexToReal {M E : Type _} [AddCommGroup E] [Module ℂ E]
    [SMul M E] [SMulCommClass ℂ M E] : SMulCommClass ℝ M E
    where smul_comm r _ _ := (smul_comm (r : ℂ) _ _ : _)
#align smul_comm_class.complex_to_real SMulCommClass.complexToReal

instance (priority := 100) FiniteDimensional.complexToReal (E : Type _) [AddCommGroup E]
    [Module ℂ E] [FiniteDimensional ℂ E] : FiniteDimensional ℝ E :=
  FiniteDimensional.trans ℝ ℂ E
#align finite_dimensional.complex_to_real FiniteDimensional.complexToReal

theorem dim_real_of_complex (E : Type _) [AddCommGroup E] [Module ℂ E] :
    Module.rank ℝ E = 2 * Module.rank ℂ E :=
  Cardinal.lift_inj.1 <| by
    rw [← dim_mul_dim' ℝ ℂ E, Complex.dim_real_complex]
    simp [bit0]
#align dim_real_of_complex dim_real_of_complex

theorem finrank_real_of_complex (E : Type _) [AddCommGroup E] [Module ℂ E] :
    FiniteDimensional.finrank ℝ E = 2 * FiniteDimensional.finrank ℂ E := by
  rw [← FiniteDimensional.finrank_mul_finrank ℝ ℂ E, Complex.finrank_real_complex]
#align finrank_real_of_complex finrank_real_of_complex

instance (priority := 900) StarModule.complexToReal {E : Type _} [AddCommGroup E] [Star E]
    [Module ℂ E] [StarModule ℂ E] : StarModule ℝ E :=
  ⟨fun r a => by rw [← smul_one_smul ℂ r a, star_smul, star_smul, star_one, smul_one_smul]⟩
#align star_module.complex_to_real StarModule.complexToReal

namespace Complex

open ComplexConjugate

/-- Linear map version of the real part function, from `ℂ` to `ℝ`. -/
def reLm : ℂ →ₗ[ℝ] ℝ where
  toFun x := x.re
  map_add' := add_re
  map_smul' := by simp
#align complex.re_lm Complex.reLm

@[simp]
theorem reLm_coe : ⇑reLm = re :=
  rfl
#align complex.re_lm_coe Complex.reLm_coe

/-- Linear map version of the imaginary part function, from `ℂ` to `ℝ`. -/
def imLm : ℂ →ₗ[ℝ] ℝ where
  toFun x := x.im
  map_add' := add_im
  map_smul' := by simp
#align complex.im_lm Complex.imLm

@[simp]
theorem imLm_coe : ⇑imLm = im :=
  rfl
#align complex.im_lm_coe Complex.imLm_coe

/-- `ℝ`-algebra morphism version of the canonical embedding of `ℝ` in `ℂ`. -/
def ofRealAm : ℝ →ₐ[ℝ] ℂ :=
  Algebra.ofId ℝ ℂ
#align complex.of_real_am Complex.ofRealAm

@[simp]
theorem ofRealAm_coe : ⇑ofRealAm = coe :=
  rfl
#align complex.of_real_am_coe Complex.ofRealAm_coe

/-- `ℝ`-algebra isomorphism version of the complex conjugation function from `ℂ` to `ℂ` -/
def conjAe : ℂ ≃ₐ[ℝ] ℂ :=
  { conj with
    invFun := conj
    left_inv := star_star
    right_inv := star_star
    commutes' := conj_ofReal }
#align complex.conj_ae Complex.conjAe

@[simp]
theorem conjAe_coe : ⇑conjAe = conj :=
  rfl
#align complex.conj_ae_coe Complex.conjAe_coe

/- ./././Mathport/Syntax/Translate/Expr.lean:207:4: warning: unsupported notation `«expr!![ » -/
/- ./././Mathport/Syntax/Translate/Expr.lean:387:14: unsupported user notation matrix.notation -/
/-- The matrix representation of `conj_ae`. -/
@[simp]
theorem toMatrix_conjAe :
    LinearMap.toMatrix basisOneI basisOneI conjAe.toLinearMap =
      «expr!![ »
        "./././Mathport/Syntax/Translate/Expr.lean:387:14: unsupported user notation matrix.notation" :=
  by
  ext (i j)
  simp [LinearMap.toMatrix_apply]
  fin_cases i <;> fin_cases j <;> simp
#align complex.to_matrix_conj_ae Complex.toMatrix_conjAe

/-- The identity and the complex conjugation are the only two `ℝ`-algebra homomorphisms of `ℂ`. -/
theorem real_algHom_eq_id_or_conj (f : ℂ →ₐ[ℝ] ℂ) : f = AlgHom.id ℝ ℂ ∨ f = conjAe :=
  by
  refine'
      (eq_or_eq_neg_of_sq_eq_sq (f I) I <| by rw [← map_pow, I_sq, map_neg, map_one]).imp _ _ <;>
    refine' fun h => alg_hom_ext _
  exacts[h, conj_I.symm ▸ h]
#align complex.real_alg_hom_eq_id_or_conj Complex.real_algHom_eq_id_or_conj

/-- The natural `add_equiv` from `ℂ` to `ℝ × ℝ`. -/
@[simps (config := { simpRhs := true }) apply symm_apply_re symm_apply_im]
def equivRealProdAddHom : ℂ ≃+ ℝ × ℝ :=
  { equivRealProd with map_add' := by simp }
#align complex.equiv_real_prod_add_hom Complex.equivRealProdAddHom

/-- The natural `linear_equiv` from `ℂ` to `ℝ × ℝ`. -/
@[simps (config := { simpRhs := true }) apply symm_apply_re symm_apply_im]
def equivRealProdLm : ℂ ≃ₗ[ℝ] ℝ × ℝ :=
  { equivRealProdAddHom with map_smul' := by simp [equiv_real_prod_add_hom] }
#align complex.equiv_real_prod_lm Complex.equivRealProdLm

section lift

variable {A : Type _} [Ring A] [Algebra ℝ A]

/-- There is an alg_hom from `ℂ` to any `ℝ`-algebra with an element that squares to `-1`.

See `complex.lift` for this as an equiv. -/
def liftAux (I' : A) (hf : I' * I' = -1) : ℂ →ₐ[ℝ] A :=
  AlgHom.ofLinearMap
    ((Algebra.ofId ℝ A).toLinearMap.comp reLm + (LinearMap.toSpanSingleton _ _ I').comp imLm)
    (show algebraMap ℝ A 1 + (0 : ℝ) • I' = 1 by rw [RingHom.map_one, zero_smul, add_zero])
    fun ⟨x₁, y₁⟩ ⟨x₂, y₂⟩ =>
    show
      algebraMap ℝ A (x₁ * x₂ - y₁ * y₂) + (x₁ * y₂ + y₁ * x₂) • I' =
        (algebraMap ℝ A x₁ + y₁ • I') * (algebraMap ℝ A x₂ + y₂ • I')
      by
      rw [add_mul, mul_add, mul_add, add_comm _ (y₁ • I' * y₂ • I'), add_add_add_comm]
      congr 1
      -- equate "real" and "imaginary" parts
      ·
        rw [smul_mul_smul, hf, smul_neg, ← Algebra.algebraMap_eq_smul_one, ← sub_eq_add_neg, ←
          RingHom.map_mul, ← RingHom.map_sub]
      ·
        rw [Algebra.smul_def, Algebra.smul_def, Algebra.smul_def, ← Algebra.right_comm _ x₂, ←
          mul_assoc, ← add_mul, ← RingHom.map_mul, ← RingHom.map_mul, ← RingHom.map_add]
#align complex.lift_aux Complex.liftAux

@[simp]
theorem liftAux_apply (I' : A) (hI') (z : ℂ) : liftAux I' hI' z = algebraMap ℝ A z.re + z.im • I' :=
  rfl
#align complex.lift_aux_apply Complex.liftAux_apply

theorem liftAux_apply_i (I' : A) (hI') : liftAux I' hI' I = I' := by simp
#align complex.lift_aux_apply_I Complex.liftAux_apply_i

/-- A universal property of the complex numbers, providing a unique `ℂ →ₐ[ℝ] A` for every element
of `A` which squares to `-1`.

This can be used to embed the complex numbers in the `quaternion`s.

This isomorphism is named to match the very similar `zsqrtd.lift`. -/
@[simps (config := { simpRhs := true })]
def lift : { I' : A // I' * I' = -1 } ≃ (ℂ →ₐ[ℝ] A)
    where
  toFun I' := liftAux I' I'.Prop
  invFun F := ⟨F I, by rw [← F.map_mul, I_mul_I, AlgHom.map_neg, AlgHom.map_one]⟩
  left_inv I' := Subtype.ext <| liftAux_apply_i I' I'.Prop
  right_inv F := algHom_ext <| liftAux_apply_i _ _
#align complex.lift Complex.lift

-- When applied to `complex.I` itself, `lift` is the identity.
@[simp]
theorem liftAux_i : liftAux I I_mul_I = AlgHom.id ℝ ℂ :=
  algHom_ext <| liftAux_apply_i _ _
#align complex.lift_aux_I Complex.liftAux_i

-- When applied to `-complex.I`, `lift` is conjugation, `conj`.
@[simp]
theorem liftAux_neg_i : liftAux (-I) ((neg_mul_neg _ _).trans I_mul_I) = conjAe :=
  algHom_ext <| (liftAux_apply_i _ _).trans conj_I.symm
#align complex.lift_aux_neg_I Complex.liftAux_neg_i

end lift

end Complex

section RealImaginaryPart

open Complex

variable {A : Type _} [AddCommGroup A] [Module ℂ A] [StarAddMonoid A] [StarModule ℂ A]

/-- Create a `self_adjoint` element from a `skew_adjoint` element by multiplying by the scalar
`-complex.I`. -/
@[simps]
def skewAdjoint.negISmul : skewAdjoint A →ₗ[ℝ] selfAdjoint A
    where
  toFun a :=
    ⟨-I • a, by
      simp only [selfAdjoint.mem_iff, neg_smul, star_neg, star_smul, star_def, conj_I,
        skewAdjoint.star_val_eq, neg_smul_neg]⟩
  map_add' a b := by
    ext
    simp only [AddSubgroup.coe_add, smul_add, AddMemClass.mk_add_mk]
  map_smul' a b := by
    ext
    simp only [neg_smul, skewAdjoint.val_smul, AddSubgroup.coe_mk, RingHom.id_apply,
      selfAdjoint.val_smul, smul_neg, neg_inj]
    rw [smul_comm]
#align skew_adjoint.neg_I_smul skewAdjoint.negISmul

theorem skewAdjoint.i_smul_neg_i (a : skewAdjoint A) : I • (skewAdjoint.negISmul a : A) = a := by
  simp only [smul_smul, skewAdjoint.negISmul_apply_coe, neg_smul, smul_neg, I_mul_I, one_smul,
    neg_neg]
#align skew_adjoint.I_smul_neg_I skewAdjoint.i_smul_neg_i

/-- The real part `ℜ a` of an element `a` of a star module over `ℂ`, as a linear map. This is just
`self_adjoint_part ℝ`, but we provide it as a separate definition in order to link it with lemmas
concerning the `imaginary_part`, which doesn't exist in star modules over other rings. -/
noncomputable def realPart : A →ₗ[ℝ] selfAdjoint A :=
  selfAdjointPart ℝ
#align real_part realPart

/-- The imaginary part `ℑ a` of an element `a` of a star module over `ℂ`, as a linear map into the
self adjoint elements. In a general star module, we have a decomposition into the `self_adjoint`
and `skew_adjoint` parts, but in a star module over `ℂ` we have
`real_part_add_I_smul_imaginary_part`, which allows us to decompose into a linear combination of
`self_adjoint`s. -/
noncomputable def imaginaryPart : A →ₗ[ℝ] selfAdjoint A :=
  skewAdjoint.negISmul.comp (skewAdjointPart ℝ)
#align imaginary_part imaginaryPart

-- mathport name: exprℜ
scoped[ComplexStarModule] notation "ℜ" => realPart

-- mathport name: exprℑ
scoped[ComplexStarModule] notation "ℑ" => imaginaryPart

@[simp]
theorem realPart_apply_coe (a : A) : (ℜ a : A) = (2 : ℝ)⁻¹ • (a + star a) :=
  by
  unfold realPart
  simp only [selfAdjointPart_apply_coe, invOf_eq_inv]
#align real_part_apply_coe realPart_apply_coe

@[simp]
theorem imaginaryPart_apply_coe (a : A) : (ℑ a : A) = -I • (2 : ℝ)⁻¹ • (a - star a) :=
  by
  unfold imaginaryPart
  simp only [LinearMap.coe_comp, skewAdjoint.negISmul_apply_coe, skewAdjointPart_apply_coe,
    invOf_eq_inv]
#align imaginary_part_apply_coe imaginaryPart_apply_coe

/-- The standard decomposition of `ℜ a + complex.I • ℑ a = a` of an element of a star module over
`ℂ` into a linear combination of self adjoint elements. -/
theorem realPart_add_i_smul_imaginaryPart (a : A) : (ℜ a + I • ℑ a : A) = a := by
  simpa only [smul_smul, realPart_apply_coe, imaginaryPart_apply_coe, neg_smul, I_mul_I, one_smul,
    neg_sub, add_add_sub_cancel, smul_sub, smul_add, neg_sub_neg, invOf_eq_inv] using
    inv_of_two_smul_add_inv_of_two_smul ℝ a
#align real_part_add_I_smul_imaginary_part realPart_add_i_smul_imaginaryPart

@[simp]
theorem realPart_i_smul (a : A) : ℜ (I • a) = -ℑ a :=
  by
  ext
  simp [smul_comm I, smul_sub, sub_eq_add_neg, add_comm]
#align real_part_I_smul realPart_i_smul

@[simp]
theorem imaginaryPart_i_smul (a : A) : ℑ (I • a) = ℜ a :=
  by
  ext
  simp [smul_comm I, smul_smul I]
#align imaginary_part_I_smul imaginaryPart_i_smul

theorem realPart_smul (z : ℂ) (a : A) : ℜ (z • a) = z.re • ℜ a - z.im • ℑ a :=
  by
  nth_rw 1 [← re_add_im z]
  simp [-re_add_im, add_smul, ← smul_smul, sub_eq_add_neg]
#align real_part_smul realPart_smul

theorem imaginaryPart_smul (z : ℂ) (a : A) : ℑ (z • a) = z.re • ℑ a + z.im • ℜ a :=
  by
  nth_rw 1 [← re_add_im z]
  simp [-re_add_im, add_smul, ← smul_smul]
#align imaginary_part_smul imaginaryPart_smul

end RealImaginaryPart


/-
Copyright (c) 2020 Aaron Anderson. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Aaron Anderson
-/
import algebra.big_operators.order
import data.nat.interval
import data.nat.factors

/-!
# Divisor finsets

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

This file defines sets of divisors of a natural number. This is particularly useful as background
for defining Dirichlet convolution.

## Main Definitions
Let `n : ℕ`. All of the following definitions are in the `nat` namespace:
 * `divisors n` is the `finset` of natural numbers that divide `n`.
 * `proper_divisors n` is the `finset` of natural numbers that divide `n`, other than `n`.
 * `divisors_antidiagonal n` is the `finset` of pairs `(x,y)` such that `x * y = n`.
 * `perfect n` is true when `n` is positive and the sum of `proper_divisors n` is `n`.

## Implementation details
 * `divisors 0`, `proper_divisors 0`, and `divisors_antidiagonal 0` are defined to be `∅`.

## Tags
divisors, perfect numbers

-/

open_locale classical
open_locale big_operators
open finset

namespace nat
variable (n : ℕ)

/-- `divisors n` is the `finset` of divisors of `n`. As a special case, `divisors 0 = ∅`. -/
def divisors : finset ℕ := finset.filter (λ x : ℕ, x ∣ n) (finset.Ico 1 (n + 1))

/-- `proper_divisors n` is the `finset` of divisors of `n`, other than `n`.
  As a special case, `proper_divisors 0 = ∅`. -/
def proper_divisors : finset ℕ := finset.filter (λ x : ℕ, x ∣ n) (finset.Ico 1 n)

/-- `divisors_antidiagonal n` is the `finset` of pairs `(x,y)` such that `x * y = n`.
  As a special case, `divisors_antidiagonal 0 = ∅`. -/
def divisors_antidiagonal : finset (ℕ × ℕ) :=
(Ico 1 (n + 1) ×ˢ Ico 1 (n + 1)).filter (λ x, x.fst * x.snd = n)

variable {n}

@[simp]
lemma filter_dvd_eq_divisors (h : n ≠ 0) :
  (finset.range n.succ).filter (∣ n) = n.divisors :=
begin
  ext,
  simp only [divisors, mem_filter, mem_range, mem_Ico, and.congr_left_iff, iff_and_self],
  exact λ ha _, succ_le_iff.mpr (pos_of_dvd_of_pos ha h.bot_lt),
end

@[simp]
lemma filter_dvd_eq_proper_divisors (h : n ≠ 0) :
  (finset.range n).filter (∣ n) = n.proper_divisors :=
begin
  ext,
  simp only [proper_divisors, mem_filter, mem_range, mem_Ico, and.congr_left_iff, iff_and_self],
  exact λ ha _, succ_le_iff.mpr (pos_of_dvd_of_pos ha h.bot_lt),
end

lemma proper_divisors.not_self_mem : ¬ n ∈ proper_divisors n :=
by simp [proper_divisors]

@[simp]
lemma mem_proper_divisors {m : ℕ} : n ∈ proper_divisors m ↔ n ∣ m ∧ n < m :=
begin
  rcases eq_or_ne m 0 with rfl | hm, { simp [proper_divisors] },
  simp only [and_comm, ←filter_dvd_eq_proper_divisors hm, mem_filter, mem_range],
end

lemma insert_self_proper_divisors (h : n ≠ 0): insert n (proper_divisors n) = divisors n :=
by rw [divisors, proper_divisors, Ico_succ_right_eq_insert_Ico (one_le_iff_ne_zero.2 h),
  finset.filter_insert, if_pos (dvd_refl n)]

lemma cons_self_proper_divisors (h : n ≠ 0) :
  cons n (proper_divisors n) proper_divisors.not_self_mem = divisors n :=
by rw [cons_eq_insert, insert_self_proper_divisors h]

@[simp]
lemma mem_divisors {m : ℕ} : n ∈ divisors m ↔ (n ∣ m ∧ m ≠ 0) :=
begin
  rcases eq_or_ne m 0 with rfl | hm, { simp [divisors] },
  simp only [hm, ne.def, not_false_iff, and_true, ←filter_dvd_eq_divisors hm, mem_filter,
    mem_range, and_iff_right_iff_imp, lt_succ_iff],
  exact le_of_dvd hm.bot_lt,
end

lemma one_mem_divisors : 1 ∈ divisors n ↔ n ≠ 0 := by simp

lemma mem_divisors_self (n : ℕ) (h : n ≠ 0) : n ∈ n.divisors := mem_divisors.2 ⟨dvd_rfl, h⟩

lemma dvd_of_mem_divisors {m : ℕ} (h : n ∈ divisors m) : n ∣ m :=
begin
  cases m,
  { apply dvd_zero },
  { simp [mem_divisors.1 h], }
end

@[simp]
lemma mem_divisors_antidiagonal {x : ℕ × ℕ} :
  x ∈ divisors_antidiagonal n ↔ x.fst * x.snd = n ∧ n ≠ 0 :=
begin
  simp only [divisors_antidiagonal, finset.mem_Ico, ne.def, finset.mem_filter, finset.mem_product],
  rw and_comm,
  apply and_congr_right,
  rintro rfl,
  split; intro h,
  { contrapose! h, simp [h], },
  { rw [nat.lt_add_one_iff, nat.lt_add_one_iff],
    rw [mul_eq_zero, decidable.not_or_iff_and_not] at h,
    simp only [succ_le_of_lt (nat.pos_of_ne_zero h.1), succ_le_of_lt (nat.pos_of_ne_zero h.2),
               true_and],
    exact ⟨le_mul_of_pos_right (nat.pos_of_ne_zero h.2),
      le_mul_of_pos_left (nat.pos_of_ne_zero h.1)⟩ }
end

variable {n}

lemma divisor_le {m : ℕ}:
n ∈ divisors m → n ≤ m :=
begin
  cases m,
  { simp },
  simp only [mem_divisors, m.succ_ne_zero, and_true, ne.def, not_false_iff],
  exact nat.le_of_dvd (nat.succ_pos m),
end

lemma divisors_subset_of_dvd {m : ℕ} (hzero : n ≠ 0) (h : m ∣ n) : divisors m ⊆ divisors n :=
finset.subset_iff.2 $ λ x hx, nat.mem_divisors.mpr (⟨(nat.mem_divisors.mp hx).1.trans h, hzero⟩)

lemma divisors_subset_proper_divisors {m : ℕ} (hzero : n ≠ 0) (h : m ∣ n) (hdiff : m ≠ n) :
  divisors m ⊆ proper_divisors n :=
begin
  apply finset.subset_iff.2,
  intros x hx,
  exact nat.mem_proper_divisors.2 (⟨(nat.mem_divisors.1 hx).1.trans h,
    lt_of_le_of_lt (divisor_le hx) (lt_of_le_of_ne (divisor_le (nat.mem_divisors.2
    ⟨h, hzero⟩)) hdiff)⟩)
end

@[simp]
lemma divisors_zero : divisors 0 = ∅ := by { ext, simp }

@[simp]
lemma proper_divisors_zero : proper_divisors 0 = ∅ := by { ext, simp }

lemma proper_divisors_subset_divisors : proper_divisors n ⊆ divisors n :=
filter_subset_filter _ $ Ico_subset_Ico_right n.le_succ

@[simp]
lemma divisors_one : divisors 1 = {1} := by { ext, simp }

@[simp]
lemma proper_divisors_one : proper_divisors 1 = ∅ :=
by rw [proper_divisors, Ico_self, filter_empty]

lemma pos_of_mem_divisors {m : ℕ} (h : m ∈ n.divisors) : 0 < m :=
begin
  cases m,
  { rw [mem_divisors, zero_dvd_iff] at h, cases h.2 h.1 },
  apply nat.succ_pos,
end

lemma pos_of_mem_proper_divisors {m : ℕ} (h : m ∈ n.proper_divisors) : 0 < m :=
pos_of_mem_divisors (proper_divisors_subset_divisors h)

lemma one_mem_proper_divisors_iff_one_lt :
  1 ∈ n.proper_divisors ↔ 1 < n :=
by rw [mem_proper_divisors, and_iff_right (one_dvd _)]

@[simp]
lemma divisors_antidiagonal_zero : divisors_antidiagonal 0 = ∅ := by { ext, simp }

@[simp]
lemma divisors_antidiagonal_one : divisors_antidiagonal 1 = {(1,1)} :=
by { ext, simp [nat.mul_eq_one_iff, prod.ext_iff], }

@[simp] lemma swap_mem_divisors_antidiagonal {x : ℕ × ℕ} :
  x.swap ∈ divisors_antidiagonal n ↔ x ∈ divisors_antidiagonal n :=
by rw [mem_divisors_antidiagonal, mem_divisors_antidiagonal, mul_comm, prod.swap]

lemma snd_mem_divisors_of_mem_antidiagonal {x : ℕ × ℕ} (h : x ∈ divisors_antidiagonal n) :
  x.snd ∈ divisors n :=
begin
  rw mem_divisors_antidiagonal at h,
  simp [dvd.intro_left _ h.1, h.2],
end

@[simp]
lemma map_swap_divisors_antidiagonal :
  (divisors_antidiagonal n).map (equiv.prod_comm _ _).to_embedding = divisors_antidiagonal n :=
begin
  rw [← coe_inj, coe_map, equiv.coe_to_embedding, equiv.coe_prod_comm,
    set.image_swap_eq_preimage_swap],
  ext,
  exact swap_mem_divisors_antidiagonal,
end

@[simp] lemma image_fst_divisors_antidiagonal :
  (divisors_antidiagonal n).image prod.fst = divisors n :=
by { ext, simp [has_dvd.dvd, @eq_comm _ n (_ * _)] }

@[simp] lemma image_snd_divisors_antidiagonal :
  (divisors_antidiagonal n).image prod.snd = divisors n :=
begin
  rw [←map_swap_divisors_antidiagonal, map_eq_image, image_image],
  exact image_fst_divisors_antidiagonal
end

lemma map_div_right_divisors :
  n.divisors.map ⟨λ d, (d, n/d), λ p₁ p₂, congr_arg prod.fst⟩ = n.divisors_antidiagonal :=
begin
  ext ⟨d, nd⟩,
  simp only [mem_map, mem_divisors_antidiagonal, function.embedding.coe_fn_mk, mem_divisors,
    prod.ext_iff, exists_prop, and.left_comm, exists_eq_left],
  split,
  { rintro ⟨⟨⟨k, rfl⟩, hn⟩, rfl⟩,
    rw [nat.mul_div_cancel_left _ (left_ne_zero_of_mul hn).bot_lt],
    exact ⟨rfl, hn⟩ },
  { rintro ⟨rfl, hn⟩,
    exact ⟨⟨dvd_mul_right _ _, hn⟩, nat.mul_div_cancel_left _ (left_ne_zero_of_mul hn).bot_lt⟩ }
end

lemma map_div_left_divisors :
  n.divisors.map ⟨λ d, (n/d, d), λ p₁ p₂, congr_arg prod.snd⟩ = n.divisors_antidiagonal :=
begin
  apply finset.map_injective (equiv.prod_comm _ _).to_embedding,
  rw [map_swap_divisors_antidiagonal, ←map_div_right_divisors, finset.map_map],
  refl,
end

lemma sum_divisors_eq_sum_proper_divisors_add_self :
  ∑ i in divisors n, i = ∑ i in proper_divisors n, i + n :=
begin
  rcases decidable.eq_or_ne n 0 with rfl|hn,
  { simp },
  { rw [← cons_self_proper_divisors hn, finset.sum_cons, add_comm] }
end

/-- `n : ℕ` is perfect if and only the sum of the proper divisors of `n` is `n` and `n`
  is positive. -/
def perfect (n : ℕ) : Prop := (∑ i in proper_divisors n, i = n) ∧ 0 < n

theorem perfect_iff_sum_proper_divisors (h : 0 < n) :
  perfect n ↔ ∑ i in proper_divisors n, i = n := and_iff_left h

theorem perfect_iff_sum_divisors_eq_two_mul (h : 0 < n) :
  perfect n ↔ ∑ i in divisors n, i = 2 * n :=
begin
  rw [perfect_iff_sum_proper_divisors h, sum_divisors_eq_sum_proper_divisors_add_self, two_mul],
  split; intro h,
  { rw h },
  { apply add_right_cancel h }
end

lemma mem_divisors_prime_pow {p : ℕ} (pp : p.prime) (k : ℕ) {x : ℕ} :
  x ∈ divisors (p ^ k) ↔ ∃ (j : ℕ) (H : j ≤ k), x = p ^ j :=
by rw [mem_divisors, nat.dvd_prime_pow pp, and_iff_left (ne_of_gt (pow_pos pp.pos k))]

lemma prime.divisors {p : ℕ} (pp : p.prime) :
  divisors p = {1, p} :=
begin
  ext,
  rw [mem_divisors, dvd_prime pp, and_iff_left pp.ne_zero, finset.mem_insert, finset.mem_singleton]
end

lemma prime.proper_divisors {p : ℕ} (pp : p.prime) :
  proper_divisors p = {1} :=
by rw [← erase_insert proper_divisors.not_self_mem, insert_self_proper_divisors pp.ne_zero,
    pp.divisors, pair_comm, erase_insert (λ con, pp.ne_one (mem_singleton.1 con))]

lemma divisors_prime_pow {p : ℕ} (pp : p.prime) (k : ℕ) :
  divisors (p ^ k) = (finset.range (k + 1)).map ⟨pow p, pow_right_injective pp.two_le⟩ :=
by { ext, simp [mem_divisors_prime_pow, pp, nat.lt_succ_iff, @eq_comm _ a] }

lemma eq_proper_divisors_of_subset_of_sum_eq_sum {s : finset ℕ} (hsub : s ⊆ n.proper_divisors) :
  ∑ x in s, x = ∑ x in n.proper_divisors, x → s = n.proper_divisors :=
begin
  cases n,
  { rw [proper_divisors_zero, subset_empty] at hsub,
    simp [hsub] },
  classical,
  rw [← sum_sdiff hsub],
  intros h,
  apply subset.antisymm hsub,
  rw [← sdiff_eq_empty_iff_subset],
  contrapose h,
  rw [← ne.def, ← nonempty_iff_ne_empty] at h,
  apply ne_of_lt,
  rw [← zero_add (∑ x in s, x), ← add_assoc, add_zero],
  apply add_lt_add_right,
  have hlt := sum_lt_sum_of_nonempty h (λ x hx, pos_of_mem_proper_divisors (sdiff_subset _ _ hx)),
  simp only [sum_const_zero] at hlt,
  apply hlt
end

lemma sum_proper_divisors_dvd (h : ∑ x in n.proper_divisors, x ∣ n) :
  (∑ x in n.proper_divisors, x = 1) ∨ (∑ x in n.proper_divisors, x = n) :=
begin
  cases n,
  { simp },
  cases n,
  { contrapose! h,
    simp, },
  rw or_iff_not_imp_right,
  intro ne_n,
  have hlt : ∑ x in n.succ.succ.proper_divisors, x < n.succ.succ :=
    lt_of_le_of_ne (nat.le_of_dvd (nat.succ_pos _) h) ne_n,
  symmetry,
  rw [← mem_singleton, eq_proper_divisors_of_subset_of_sum_eq_sum (singleton_subset_iff.2
        (mem_proper_divisors.2 ⟨h, hlt⟩)) sum_singleton, mem_proper_divisors],
  refine ⟨one_dvd _, nat.succ_lt_succ (nat.succ_pos _)⟩,
end

@[simp, to_additive]
lemma prime.prod_proper_divisors {α : Type*} [comm_monoid α] {p : ℕ} {f : ℕ → α} (h : p.prime) :
  ∏ x in p.proper_divisors, f x = f 1 :=
by simp [h.proper_divisors]

@[simp, to_additive]
lemma prime.prod_divisors {α : Type*} [comm_monoid α] {p : ℕ} {f : ℕ → α} (h : p.prime) :
  ∏ x in p.divisors, f x = f p * f 1 :=
by rw [← cons_self_proper_divisors h.ne_zero, prod_cons, h.prod_proper_divisors]

lemma proper_divisors_eq_singleton_one_iff_prime :
  n.proper_divisors = {1} ↔ n.prime :=
⟨λ h, begin
  have h1 := mem_singleton.2 rfl,
  rw [← h, mem_proper_divisors] at h1,
  refine nat.prime_def_lt''.mpr ⟨h1.2, λ m hdvd, _⟩,
  rw [← mem_singleton, ← h, mem_proper_divisors],
  have hle := nat.le_of_dvd (lt_trans (nat.succ_pos _) h1.2) hdvd,
  exact or.imp_left (λ hlt, ⟨hdvd, hlt⟩) hle.lt_or_eq
end, prime.proper_divisors⟩

lemma sum_proper_divisors_eq_one_iff_prime :
  ∑ x in n.proper_divisors, x = 1 ↔ n.prime :=
begin
  cases n,
  { simp [nat.not_prime_zero] },
  cases n,
  { simp [nat.not_prime_one] },
  rw [← proper_divisors_eq_singleton_one_iff_prime],
  refine ⟨λ h, _, λ h, h.symm ▸ sum_singleton⟩,
  rw [@eq_comm (finset ℕ) _ _],
  apply eq_proper_divisors_of_subset_of_sum_eq_sum
    (singleton_subset_iff.2 (one_mem_proper_divisors_iff_one_lt.2 (succ_lt_succ (nat.succ_pos _))))
    (eq.trans sum_singleton h.symm)
end

lemma mem_proper_divisors_prime_pow {p : ℕ} (pp : p.prime) (k : ℕ) {x : ℕ} :
  x ∈ proper_divisors (p ^ k) ↔ ∃ (j : ℕ) (H : j < k), x = p ^ j :=
begin
  rw [mem_proper_divisors, nat.dvd_prime_pow pp, ← exists_and_distrib_right],
  simp only [exists_prop, and_assoc],
  apply exists_congr,
  intro a,
  split; intro h,
  { rcases h with ⟨h_left, rfl, h_right⟩,
    rwa pow_lt_pow_iff pp.one_lt at h_right,
    simpa, },
  { rcases h with ⟨h_left, rfl⟩,
    rwa pow_lt_pow_iff pp.one_lt,
    simp [h_left, le_of_lt], },
end

lemma proper_divisors_prime_pow {p : ℕ} (pp : p.prime) (k : ℕ) :
  proper_divisors (p ^ k) = (finset.range k).map ⟨pow p, pow_right_injective pp.two_le⟩ :=
by { ext, simp [mem_proper_divisors_prime_pow, pp, nat.lt_succ_iff, @eq_comm _ a], }

@[simp, to_additive]
lemma prod_proper_divisors_prime_pow {α : Type*} [comm_monoid α] {k p : ℕ} {f : ℕ → α}
  (h : p.prime) : ∏ x in (p ^ k).proper_divisors, f x = ∏ x in range k, f (p ^ x) :=
by simp [h, proper_divisors_prime_pow]

@[simp, to_additive sum_divisors_prime_pow]
lemma prod_divisors_prime_pow {α : Type*} [comm_monoid α] {k p : ℕ} {f : ℕ → α} (h : p.prime) :
  ∏ x in (p ^ k).divisors, f x = ∏ x in range (k + 1), f (p ^ x) :=
by simp [h, divisors_prime_pow]

@[to_additive]
lemma prod_divisors_antidiagonal {M : Type*} [comm_monoid M] (f : ℕ → ℕ → M) {n : ℕ} :
  ∏ i in n.divisors_antidiagonal, f i.1 i.2 = ∏ i in n.divisors, f i (n / i) :=
begin
  rw [←map_div_right_divisors, finset.prod_map],
  refl,
end

@[to_additive]
lemma prod_divisors_antidiagonal' {M : Type*} [comm_monoid M] (f : ℕ → ℕ → M) {n : ℕ} :
  ∏ i in n.divisors_antidiagonal, f i.1 i.2 = ∏ i in n.divisors, f (n / i) i :=
begin
  rw [←map_swap_divisors_antidiagonal, finset.prod_map],
  exact prod_divisors_antidiagonal (λ i j, f j i),
end

/-- The factors of `n` are the prime divisors -/
lemma prime_divisors_eq_to_filter_divisors_prime (n : ℕ) :
  n.factors.to_finset = (divisors n).filter prime :=
begin
  rcases n.eq_zero_or_pos with rfl | hn,
  { simp },
  { ext q,
    simpa [hn, hn.ne', mem_factors] using and_comm (prime q) (q ∣ n) }
end

@[simp]
lemma image_div_divisors_eq_divisors (n : ℕ) : image (λ (x : ℕ), n / x) n.divisors = n.divisors :=
begin
  by_cases hn : n = 0, { simp [hn] },
  ext,
  split,
  { rw mem_image,
    rintros ⟨x, hx1, hx2⟩,
    rw mem_divisors at *,
    refine ⟨_,hn⟩,
    rw ←hx2,
    exact div_dvd_of_dvd hx1.1 },
  { rw [mem_divisors, mem_image],
    rintros ⟨h1, -⟩,
    exact ⟨n/a, mem_divisors.mpr ⟨div_dvd_of_dvd h1, hn⟩, nat.div_div_self h1 hn⟩ },
end

@[simp, to_additive sum_div_divisors]
lemma prod_div_divisors {α : Type*} [comm_monoid α] (n : ℕ) (f : ℕ → α) :
  ∏ d in n.divisors, f (n/d) = n.divisors.prod f :=
begin
  by_cases hn : n = 0, { simp [hn] },
  rw ←prod_image,
  { exact prod_congr (image_div_divisors_eq_divisors n) (by simp) },
  { intros x hx y hy h,
    rw mem_divisors at hx hy,
    exact (div_eq_iff_eq_of_dvd_dvd hn hx.1 hy.1).mp h }
end

end nat


/-
Copyright (c) 2021 Anne Baanen. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Anne Baanen
-/
import number_theory.class_number.admissible_card_pow_degree
import number_theory.class_number.finite
import number_theory.function_field

/-!
# Class numbers of function fields

This file defines the class number of a function field as the (finite) cardinality of
the class group of its ring of integers. It also proves some elementary results
on the class number.

## Main definitions
- `function_field.class_number`: the class number of a function field is the (finite)
cardinality of the class group of its ring of integers
-/

namespace function_field

variables (Fq F : Type) [field Fq] [fintype Fq] [field F]
variables [algebra (polynomial Fq) F] [algebra (ratfunc Fq) F]
variables [is_scalar_tower (polynomial Fq) (ratfunc Fq) F]
variables [function_field Fq F] [is_separable (ratfunc Fq) F]

open_locale classical

namespace ring_of_integers

open function_field

noncomputable instance  : fintype (class_group (ring_of_integers Fq F) F) :=
class_group.fintype_of_admissible_of_finite (ratfunc Fq) F
  (polynomial.card_pow_degree_is_admissible : absolute_value.is_admissible
    (polynomial.card_pow_degree : absolute_value (polynomial Fq) ℤ))

end ring_of_integers

/-- The class number in a function field is the (finite) cardinality of the class group. -/
noncomputable def class_number : ℕ := fintype.card (class_group (ring_of_integers Fq F) F)

/-- The class number of a function field is `1` iff the ring of integers is a PID. -/
theorem class_number_eq_one_iff :
  class_number Fq F = 1 ↔ is_principal_ideal_ring (ring_of_integers Fq F) :=
card_class_group_eq_one_iff

end function_field