/-
Copyright (c) 2021 Lu-Ming Zhang. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lu-Ming Zhang.
-/
import data.matrix.notation
import symmetric_matrix

/-!
# Hadamard product and Kronecker product.

This file defines the Hadamard product `matrix.Hadamard` and the Kronecker product `matrix.Kronecker` and
contains basic properties about them.

## Main definitions

- `matrix.Hadamard`: defines the Hadamard product, which is the pointwise product of two matrices of the same size.
- `matrix.Kronecker`: defines the Kronecker product, denoted by `⊗`.
  For `A : matrix I J α` and `B : matrix K L α`, `A ⊗ B ⟨a, b⟩ ⟨c, d⟩` is defined to be ` (A a c) * (B b d)`.
- `matrix.fin_Kronecker`: the `fin` version of `matrix.Kronecker`, denoted by `⊗ₖ`.
  For `A : matrix (fin m) (fin n) α` and `B : matrix (fin p) (fin q) α`, `A ⊗ₖ B` is of type `matrix (fin (m * p)) (fin (n * q))`.
  The difference from `A ⊗ B` is that each of the index types `fin (m * p)` and `fin (n * q)` of the resulting matrix has a natural order.

## Notation

* `⊙`: the Hadamard product `matrix.Hadamard`;
* `⊗`: the Kronecker product `matrix.Kronecker`;
* `⊗ₖ`: the Kronecker product `fin_Kronecker` of matrices with index types `fin _`.

## References

*  <https://en.wikipedia.org/wiki/Hadamard_product_(matrices)>
*  <https://en.wikipedia.org/wiki/Kronecker_product>

## Tags

Hadamard product, Kronecker product, Hadamard, Kronecker
-/

variables {α β γ I J K L M N: Type*}
variables {R : Type*}
variables {m n p q r s t: ℕ}
variables [fintype I] [fintype J] [fintype K] [fintype L] [fintype M] [fintype N]

namespace matrix
open_locale matrix big_operators
open complex

/- ## Hadamard product  -/
section Hadamard_product

def Hadamard [has_mul α] (A : matrix I J α) (B : matrix I J α) :
matrix I J α :=
λ i j, (A i j) * (B i j)
localized "infix `⊙`:100 := matrix.Hadamard" in matrix -- declares the notation

section basic_properties
variables (A : matrix I J α) (B : matrix I J α) (C : matrix I J α)

/- commutativity -/
lemma Had_comm [comm_semigroup α] : A ⊙ B = B ⊙ A :=
by ext; simp [Hadamard, mul_comm]

/- associativity -/
lemma Had_assoc [semigroup α] : A ⊙ B ⊙ C = A ⊙ (B ⊙ C) :=
by ext; simp [Hadamard, mul_assoc]

/- distributivity -/
section distrib
variables [distrib α]
lemma Had_add : A ⊙ (B + C) = A ⊙ B + A ⊙ C :=
by ext; simp [Hadamard, left_distrib]
lemma add_Had : (B + C) ⊙ A = B ⊙ A + C ⊙ A :=
by ext; simp [Hadamard, right_distrib]
end distrib

/- scalar multiplication -/
section scalar
@[simp] lemma smul_Had
[has_mul α] [has_scalar R α] [is_scalar_tower R α α] (k : R) :
(k • A) ⊙ B = k • A ⊙ B :=
by {ext, simp [Hadamard], exact smul_assoc _ (A i j) _}
@[simp] lemma Had_smul
[has_mul α] [has_scalar R α] [smul_comm_class R α α] (k : R):
A ⊙ (k • B) = k • A ⊙ B :=
by {ext, simp [Hadamard], exact (smul_comm k (A i j) (B i j)).symm}
end scalar

section zero
variables [mul_zero_class α]
@[simp] lemma Had_zero : A ⊙ (0 : matrix I J α) = 0 :=
by ext; simp [Hadamard]
@[simp] lemma zero_Had : (0 : matrix I J α) ⊙ A = 0 :=
by ext; simp [Hadamard]
end zero

section trace
variables [comm_semiring α] [decidable_eq I] [decidable_eq J]

/--`vᵀ (M₁ ⊙ M₂) w = tr ((diagonal v)ᵀ ⬝ M₁ ⬝ (diagonal w) ⬝ M₂ᵀ)` -/
lemma tr_identity (v : I → α) (w : J → α) (M₁ : matrix I J α) (M₂ : matrix I J α):
dot_product (vec_mul  v  (M₁ ⊙ M₂)) w =
tr ((diagonal v)ᵀ ⬝ M₁ ⬝ (diagonal w) ⬝ M₂ᵀ) :=
begin
  simp [dot_product, vec_mul, Hadamard, finset.sum_mul],
  rw finset.sum_comm,
  apply finset.sum_congr, refl, intros i hi,
  simp [diagonal, transpose, matrix.mul, dot_product],
  apply finset.sum_congr, refl, intros j hj,
  ring,
end

/-- `trace` version of `tr_identity` -/
lemma trace_identity (v : I → α) (w : J → α) (M₁ : matrix I J α) (M₂ : matrix I J α):
dot_product (vec_mul  v  (M₁ ⊙ M₂)) w =
trace I α α ((diagonal v)ᵀ ⬝ M₁ ⬝ (diagonal w) ⬝ M₂ᵀ) :=
by rw [trace_eq_tr, tr_identity]

/-- `∑ (i : I) (j : J), (M₁ ⊙ M₂) i j = tr (M₁ ⬝ M₂ᵀ)` -/
lemma sum_Had_eq_tr_mul (M₁ : matrix I J α) (M₂ : matrix I J α) :
∑ (i : I) (j : J), (M₁ ⊙ M₂) i j = tr (M₁ ⬝ M₂ᵀ) :=
begin
  have h:= tr_identity (λ i, 1 : I → α) (λ i, 1 : J → α) M₁ M₂,
  simp at h,
  rw finset.sum_comm at h,
  assumption,
end

/-- `vᴴ (M₁ ⊙ M₂) w = tr ((diagonal v)ᴴ ⬝ M₁ ⬝ (diagonal w) ⬝ M₂ᵀ)` over `ℂ` -/
lemma tr_identity_over_ℂ
(v : I → ℂ) (w : J → ℂ) (M₁ : matrix I J ℂ) (M₂ : matrix I J ℂ):
dot_product (vec_mul (star v)  (M₁ ⊙ M₂)) w =
tr ((diagonal v)ᴴ ⬝ M₁ ⬝ (diagonal w) ⬝ M₂ᵀ) :=
begin
  simp [dot_product, vec_mul, Hadamard, finset.sum_mul],
  rw finset.sum_comm,
  apply finset.sum_congr, refl, intros i hi,
  simp [diagonal, transpose, conj_transpose, matrix.mul, dot_product, star, has_star.star],
  apply finset.sum_congr, refl, intros j hj,
  ring_nf,
end

/-- `trace` version of `tr_identity_over_ℂ` -/
lemma trace_identity_over_ℂ
(v : I → ℂ) (w : J → ℂ) (M₁ : matrix I J ℂ) (M₂ : matrix I J ℂ):
dot_product (vec_mul (star v)  (M₁ ⊙ M₂)) w =
trace I ℂ ℂ ((diagonal v)ᴴ ⬝ M₁ ⬝ (diagonal w) ⬝ M₂ᵀ) :=
by rw [trace_eq_tr, tr_identity_over_ℂ]

end trace

/-
section rank
variables [decidable_eq J] [field α]
theorem rank_Had_le_rank_mul :
matrix.rank (A ⊙ B) ≤ A.rank  * B.rank := sorry
end rank
-/

end basic_properties

/-
section psd
open_locale complex_order
variables {A B : matrix I I ℂ}
variables (ha : A.is_pos_semidef) (hb : B.is_pos_semidef)
--Schur_product_theorem
theorem Hadamard.is_pos_semidef_of_is_pos_semidef : (A ⊙ B).is_pos_semidef :=
sorry
--#check det
variable [decidable_eq I]
theorem det_Had_ge_det_mul_of_psd : ((A ⊙ B).det) ≥ (A.det) * (B.det) :=
sorry
end psd
-/

end Hadamard_product
/- ## end Hadamard product  -/
----------------------------------------------------------------------------------

/- ## Kronecker product  -/
section Kronecker_product
open_locale matrix

@[elab_as_eliminator]
def Kronecker [has_mul α] (A : matrix I J α) (B : matrix K L α) :
matrix (I × K) (J × L) α :=
λ ⟨i, k⟩ ⟨j, l⟩, (A i j) * (B k l)

/- an infix notation for the Kronecker product -/
localized "infix `⊗`:100 := matrix.Kronecker" in matrix

/- The advantage of the following def is that one can directly #eval the Kronecker product of specific matrices-/
/- ## fin_Kronecker_prodcut  -/

@[elab_as_eliminator]
def fin_Kronecker [has_mul α]
(A : matrix (fin m) (fin n) α) (B : matrix (fin p) (fin q) α)
: matrix (fin (m * p)) (fin (n * q)) α :=
λ i j,
A ⟨(i / p), by {have h:= i.2, simp [mul_comm m] at *, apply nat.div_lt_of_lt_mul h}⟩
  ⟨(j / q), by {have h:= j.2, simp [mul_comm n] at *, apply nat.div_lt_of_lt_mul h}⟩
*
B ⟨(i % p), by {cases p, linarith [i.2], apply nat.mod_lt _ (nat.succ_pos _)}⟩
  ⟨(j % q), by {cases q, linarith [j.2], apply nat.mod_lt _ (nat.succ_pos _)}⟩

localized "infix `⊗ₖ`:100 := matrix.fin_Kronecker" in matrix

section notations
def matrix_empty : matrix (fin 0) (fin 0) α := λ x, ![]
localized "notation `!![]` := matrix.matrix_empty" in matrix
example : (!![]: matrix (fin 0) (fin 0) α) = ![] := by {ext, have h:= x.2, simp* at *}
end notations

section examples
open_locale matrix

def ex1:= ![![1, 2], ![3, 4]]
def ex2:= ![![0, 5], ![6, 7]]
def ex3:= ![![(1:ℤ), -4, 7], ![-2, 3, 3]]
def ex4:= ![![(8:ℤ), -9, -6, 5], ![1, -3, -4, 7], ![2, 8, -8, -3], ![1, 2, -5, -1]]

#eval (!![]: matrix (fin 0) (fin 0) ℕ)
#eval ex3 ⊗ₖ ex4
#eval ex1 ⊗ₖ ex2
#eval 2 • (ex1 ⊗ₖ ex2)
#eval ex2 ⊗ₖ ![![]]
#eval ![![]] ⊗ₖ ex2
#eval ex2 ⊗ₖ !![]
#eval !![] ⊗ₖ ex2
#eval ![![]] ⊗ₖ (![![]] :matrix (fin 1) (fin 0) ℕ)

end examples
/- ## end fin_Kronecker_prodcut  -/

lemma Kronecker_apply [has_mul α]
(A : matrix I J α) (B : matrix K L α) (a : I × K) (b : J × L) :
(A ⊗ B) a b = (A a.1 b.1) * (B a.2 b.2) :=
begin
  have ha : a = ⟨a.1, a.2⟩ := by {ext; simp},
  have hb : b = ⟨b.1, b.2⟩ := by {ext; simp},
  rw [ha, hb], dsimp [Kronecker], refl
end

/- distributivity -/
section distrib
variables [distrib α] -- variables are restricted to this section
variables (A : matrix I J α) (B : matrix K L α) (B' : matrix K L α)
lemma K_add :A ⊗ (B + B') = A ⊗ B + A ⊗ B' :=
by {ext ⟨a,b⟩ ⟨c,d⟩, simp [Kronecker, left_distrib]}
lemma add_K :(B + B') ⊗ A = B ⊗ A + B' ⊗ A :=
by {ext ⟨a,b⟩ ⟨c,d⟩, simp [Kronecker, right_distrib]}
end distrib

/- distributivity over substraction -/
section distrib_sub
variables [ring α]
variables (A : matrix I J α) (B : matrix K L α) (B' : matrix K L α)
lemma K_sub :A ⊗ (B - B') = A ⊗ B - A ⊗ B' :=
by {ext ⟨a,b⟩ ⟨c,d⟩, simp [Kronecker, mul_sub]}
lemma sub_K :(B - B') ⊗ A = B ⊗ A - B' ⊗ A :=
by {ext ⟨a,b⟩ ⟨c,d⟩, simp [Kronecker, sub_mul]}
end distrib_sub

/-
section non_comm
variables [decidable_eq I] [decidable_eq K] [decidable_eq J] [decidable_eq L] [mul_one_class α] [add_comm_monoid α]
variables (A : matrix I J α) (B : matrix K L α)
lemma non_comm : ∃ P Q,  B ⊗ A = reindex_prod_comm (P ⬝ (A ⊗ B) ⬝ Q) ∧ P.is_perfect_shuffle ∧ Q.is_perfect_shuffle :=
sorry
end non_comm
-/

/-- associativity -/
lemma K_assoc
[semigroup α] (A : matrix I J α) (B : matrix K L α) (C : matrix M N α) :
A ⊗ B ⊗ C = A ⊗ (B ⊗ C) :=
by {ext ⟨⟨a1, b1⟩, c1⟩ ⟨⟨a2, b2⟩, c2⟩, simp[Kronecker, mul_assoc], refl}

section zero
variables [mul_zero_class α] (A : matrix I J α)
@[simp] lemma K_zero : A ⊗ (0 : matrix K L α) = 0 :=
by {ext ⟨a,b⟩ ⟨c,d⟩, simp [Kronecker]}
@[simp] lemma K_zero' : A ⊗ ((λ _ _, 0):matrix K L α) = 0 :=
K_zero A
@[simp] lemma zero_K : (0 : matrix K L α) ⊗ A = 0 :=
by {ext ⟨a,b⟩ ⟨c,d⟩, simp [Kronecker]}
@[simp] lemma zero_K' : ((λ _ _, 0):matrix K L α) ⊗ A = 0 :=
zero_K A
end zero

/-- `1 ⊗ 1 = 1`.
    The Kronecker product of two identity matrices is an identity matrix. -/
@[simp] lemma one_K_one
[mul_zero_one_class α] [decidable_eq I] [decidable_eq J] :
(1 :matrix I I α) ⊗ (1 :matrix J J α) = 1 :=
begin
  ext ⟨a,b⟩ ⟨c,d⟩,
  by_cases h: a = c,
  any_goals {by_cases g: b = d},
  any_goals {simp[*, Kronecker] at *},
end

section neg
variables [ring α]
variables (A : matrix I J α) (B : matrix K L α)
@[simp] lemma neg_K: (-A) ⊗ B = - A ⊗ B := by {ext ⟨a,b⟩ ⟨c,d⟩, simp [Kronecker]}
@[simp] lemma K_neg: A ⊗ (-B) = - A ⊗ B := by {ext ⟨a,b⟩ ⟨c,d⟩, simp [Kronecker]}
end neg

/- scalar multiplication -/
section scalar
@[simp] lemma smul_K
[has_mul α] [has_scalar R α] [is_scalar_tower R α α]
(k : R) (A : matrix I J α) (B : matrix K L α) :
(k • A) ⊗ B = k • A ⊗ B :=
by {ext ⟨a,b⟩ ⟨c,d⟩, simp [Kronecker], exact smul_assoc _ (A a c) _}
@[simp] lemma K_smul
[has_mul α] [has_scalar R α] [smul_comm_class R α α]
(k : R) (A : matrix I J α) (B : matrix K L α) :
A ⊗ (k • B) = k • A ⊗ B :=
by {ext ⟨a,b⟩ ⟨c,d⟩, simp [Kronecker], exact (smul_comm k (A a c) _).symm}
end scalar

/- Kronecker product mixes matrix multiplication -/
section Kronecker_mul
variables [comm_ring α]
variables
(A : matrix I J α) (C : matrix J K α)
(B : matrix L M α) (D : matrix M N α)
lemma K_mul: (A ⊗ B) ⬝ (C ⊗ D) = (A ⬝ C) ⊗ (B ⬝ D) :=
begin
  ext ⟨a,b⟩ ⟨c,d⟩,
  simp [matrix.mul,dot_product,Kronecker,finset.sum_mul,finset.mul_sum],
  rw [←finset.univ_product_univ,finset.sum_product],
  simp [Kronecker._match_1,Kronecker._match_2],
  rw finset.sum_comm,
  repeat {congr, ext},
  ring,
end
variables [decidable_eq I] [decidable_eq M] [decidable_eq L] [decidable_eq J]
@[simp] lemma one_K_mul: (1 ⊗ B) ⬝ (A ⊗ 1) = A ⊗ B := by simp [K_mul]
@[simp] lemma K_one_mul: (A ⊗ 1) ⬝ (1 ⊗ B) = A ⊗ B := by simp [K_mul]
end Kronecker_mul

/- Kronecker product mixes Hadamard product -/
section Kronecker_Hadamard
variables [comm_semigroup α]
(A : matrix I J α) (C : matrix I J α)
(B : matrix K L α) (D : matrix K L α)
lemma Kronecker_Hadamard : (A ⊗ B) ⊙ (C ⊗ D) = (A ⊙ C) ⊗ (B ⊙ D) :=
begin
  ext ⟨a, b⟩ ⟨c, d⟩,
  simp [Hadamard, Kronecker],
  rw ← mul_assoc,
  rw mul_assoc _ (B b d),
  rw mul_comm (B b d),
  simp [mul_assoc]
end
end Kronecker_Hadamard

lemma transpose_K
[has_mul α] (A : matrix I J α) (B : matrix K L α):
(A ⊗ B)ᵀ = Aᵀ ⊗ Bᵀ :=
by ext ⟨a,b⟩ ⟨c,d⟩; simp [transpose, Kronecker]

lemma conj_transpose_K
[comm_monoid α] [star_monoid α] (M₁ : matrix I J α) (M₂ : matrix K L α) :
(M₁ ⊗ M₂)ᴴ = M₁ᴴ ⊗ M₂ᴴ:=
by ext ⟨a,b⟩ ⟨c,d⟩; simp [conj_transpose,Kronecker, mul_comm]

section trace
variables [semiring β] [non_unital_non_assoc_semiring α] [module β α]
variables (A : matrix I I α) (B : matrix J J α)
lemma trace_K: trace (I × J) β α (A ⊗ B) = (trace I β α A) * (trace J β α B) :=
begin
  simp[Kronecker, trace, ←finset.univ_product_univ, finset.sum_product,
       Kronecker._match_2,finset.sum_mul,finset.mul_sum],
  rw finset.sum_comm,
end
end trace

section inverse

variables [decidable_eq I] [decidable_eq J] [comm_ring α]
variables (A : matrix I I α) (B : matrix J J α) (C : matrix I I α)

lemma K_inverse [invertible A] [invertible B] :(A ⊗ B)⁻¹ = A⁻¹ ⊗ B⁻¹ :=
begin
  suffices : (A⁻¹ ⊗ B⁻¹) ⬝ (A ⊗ B) = 1,
  apply inv_eq_left_inv this,
  simp [K_mul],
end

@[simp] noncomputable
def Kronecker.invertible_of_invertible [invertible A] [invertible B] : invertible (A ⊗ B) :=
⟨A⁻¹ ⊗ B⁻¹, by simp [K_mul], by simp [K_mul]⟩

@[simp] lemma Kronecker.unit_of_unit (ha : is_unit A) (hb : is_unit B) : is_unit (A ⊗ B) :=
@is_unit_of_invertible _ _ (A ⊗ B) (@Kronecker.invertible_of_invertible _ _ _ _ _ _ _ _ A B (is_unit.invertible ha) (is_unit.invertible hb))

end inverse

section symmetric
variables [has_mul α]
@[simp] lemma Kronecker.is_sym_of_is_sym {A : matrix I I α} {B : matrix J J α} (ha: A.is_sym) (hb: B.is_sym) :
(A ⊗ B).is_sym := by simp [matrix.is_sym, transpose_K, *] at *
@[simp] lemma Kronecker.is_Hermitian_of_is_Hermitian {A : matrix I I ℂ} {B : matrix J J ℂ} (ha: A.is_Hermitian) (hb: B.is_Hermitian) :
(A ⊗ B).is_Hermitian := by simp [matrix.is_Hermitian, conj_transpose_K, *] at *
end symmetric

/-
section pos_def
@[simp]
lemma Kronecker.is_pos_def_of_is_pos_def {A : matrix I I ℂ} {B : matrix J J ℂ} (ha : A.is_pos_def) (hb : B.is_pos_def) :
(A ⊗ B).is_pos_def :=
begin
  /-
  simp [matrix.is_pos_def, *] at *,
  simp [dot_product, mul_vec] at *,
  intros v hv,
  simp [←finset.univ_product_univ, finset.sum_product],
  simp [Kronecker,finset.mul_sum] at *,
  have h1 := ha.2,
  have h2 := hb.2,
  -/
  sorry -- I suspect there are more missing lemmas to get this
end
end pos_def
-/

section ortho
variables  [decidable_eq I] [decidable_eq J]
@[simp] lemma Kronecker.is_ortho_of_is_ortho {A : matrix I I ℝ} {B : matrix J J ℝ} (ha : A.is_ortho) (hb : B.is_ortho) :
(A ⊗ B).is_ortho := by simp [matrix.is_ortho,  transpose_K, K_mul, ha, hb, *] at *
end ortho

section perm
open equiv

variables [decidable_eq I] [decidable_eq J] [mul_zero_one_class α]
variables {A : matrix I I α} {B : matrix J J α}
@[simp] lemma Kronecker.is_perm_of_is_perm (ha : A.is_perm) (hb : B.is_perm) :
(A ⊗ B).is_perm :=
begin
  rcases ha with ⟨σ₁, rfl⟩,
  rcases hb with ⟨σ₂, rfl⟩,
  use prod_congr σ₁ σ₂,
  ext ⟨a,b⟩ ⟨c,d⟩,
  by_cases h1: σ₁ a = c,
  all_goals {simp [*, perm.to_matrix, Kronecker]},
end
end perm

/-
section det
variables [comm_ring α] [decidable_eq I] [decidable_eq J]
variables
#check det
lemma K_det (A : matrix I I α) (B : matrix J J α) :
(A ⊗ B).det = (A.det)^(fintype.card J) * (B.det)^(fintype.card I) :=
sorry
lemma K_det' (A : matrix (fin n) (fin n) α) (B : matrix (fin m) (fin m) α) :
(A ⊗ B).det = (A.det)^m * (B.det)^n := by simp [K_det, fintype.card_fin]
end det
-/

end Kronecker_product

open_locale matrix

section dot_product

lemma dot_product_Kronecker_row [has_mul α] [add_comm_monoid α]
(A : matrix I K α) (B : matrix J L α) (a b : I × J):
dot_product ((A ⊗ B) a) ((A ⊗ B) b) =
∑ (k : K) (l : L), (A a.1 k * B a.2 l) * (A b.1 k * B b.2 l) :=
by simp [dot_product, ←finset.univ_product_univ, finset.sum_product, Kronecker_apply]

lemma dot_product_Kronecker_row' [comm_semiring α]
(A : matrix I K α) (B : matrix J L α) (a b : I × J):
dot_product ((A ⊗ B) a) ((A ⊗ B) b) =
(∑ (k : K), (A a.1 k * A b.1 k)) * ∑ (l : L), (B a.2 l * B b.2 l) :=
begin
simp [dot_product_Kronecker_row, finset.mul_sum, finset.sum_mul],
repeat {apply finset.sum_congr rfl, intros _ _},
ring
end

lemma dot_product_Kronecker_row_split [comm_semiring α]
(A : matrix I K α) (B : matrix J L α) (a b : I × J):
dot_product ((A ⊗ B) a) ((A ⊗ B) b) =
(dot_product (A a.1)  (A b.1)) * (dot_product (B a.2) (B b.2)) :=
by rw [dot_product_Kronecker_row', dot_product, dot_product]

end dot_product

section sym

/-- `A ⊗ B` is symmetric if `A` and `B` are symmetric. -/
lemma is_sym_K_of [has_mul α] {A : matrix I I α} {B : matrix J J α}
(ha : A.is_sym) (hb : B.is_sym) : (A ⊗ B).is_sym :=
begin
  ext ⟨a, b⟩ ⟨c, d⟩,
  simp [transpose_K, Kronecker, ha.apply', hb.apply'],
end

end sym
/- ## end Kronecker product  -/

end matrix
----------------------------------------------- end of file


import util.data.fin
import util.data.minimum.nat
import util.data.set
import util.logic

open nat

namespace fin

section

parameter (n : ℕ)
@[reducible]
def t := fin n.succ
parameter {n}
variables (s : set t)
variables [decidable_pred (λ y : ℕ, y ∈ val <$> s)]
variables [decidable (val <$> s = ∅)]

lemma lt_of_mem_fin_set {k : ℕ}
  (h : k ∈ val <$> s)
: k < n.succ :=
begin
  unfold functor.map set.image at h,
  rw [mem_set_of] at h,
  cases h with i h, cases h with h₀ h₁,
  rw ← h₁, apply i.is_lt
end

lemma nat_minimum_bounded
: nat.minimum (val <$> s) < n.succ :=
begin
  cases decidable.em (val <$> s = ∅) with h h,
  { unfold nat.minimum,
    rw dif_pos h, apply zero_lt_succ },
  { have h' := nat.minimum_mem (val <$> s) h,
    apply lt_of_mem_fin_set _ h', },
end

protected def minimum : t :=
let r := nat.minimum (fin.val <$> s) in
have hr : r < nat.succ n, from nat_minimum_bounded _,
⟨r,hr⟩

lemma minimum_mem
  (h : s ≠ ∅)
: fin.minimum s ∈ s :=
begin
  apply set.mem_of_mem_fmap fin.val_injective,
  apply nat.minimum_mem,
  simp [@set.fmap_eq_empty_iff_eq_empty _ _ s val,h],
end

protected lemma minimum_le {x : t}
  (h : x ∈ s)
: fin.minimum s ≤ x :=
begin
  simp [le_def],
  apply nat.minimum_le,
  apply set.mem_fmap_of_mem h,
end

protected lemma le_minimum_of_forall_le (x : t)
  (h₀ : s ≠ ∅)
  (h₁ : ∀ y, y ∈ s → x ≤ y)
: x ≤ fin.minimum s :=
begin
  simp [le_def],
  apply nat.le_minimum_of_forall_le,
  { simp [set.fmap_eq_empty_iff_eq_empty,h₀], },
  { intros y h,
    have hy := lt_of_mem_fin_set _ h,
    change x.val ≤ fin.val ⟨y,hy⟩,
    rw ← le_def,
    apply h₁,
    apply set.mem_of_mem_fmap val_injective, apply h, },
end

end

end fin

local attribute [instance] classical.prop_decidable

noncomputable instance {n} : has_minimum (fin $ succ n) :=
{ (_ : partial_order (fin $ succ n)) with
  minimum := λ s, fin.minimum s
, minimum_le := λ s x h, fin.minimum_le s h
, le_minimum_of_forall_le := λ s, fin.le_minimum_of_forall_le s
, minimum_mem := λ s, fin.minimum_mem _ }


/-
Copyright (c) 2022 Moritz Doll. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Moritz Doll

! This file was ported from Lean 3 source module analysis.locally_convex.balanced_core_hull
! leanprover-community/mathlib commit f2ce6086713c78a7f880485f7917ea547a215982
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Analysis.LocallyConvex.Basic

/-!
# Balanced Core and Balanced Hull

## Main definitions

* `balanced_core`: The largest balanced subset of a set `s`.
* `balanced_hull`: The smallest balanced superset of a set `s`.

## Main statements

* `balanced_core_eq_Inter`: Characterization of the balanced core as an intersection over subsets.
* `nhds_basis_closed_balanced`: The closed balanced sets form a basis of the neighborhood filter.

## Implementation details

The balanced core and hull are implemented differently: for the core we take the obvious definition
of the union over all balanced sets that are contained in `s`, whereas for the hull, we take the
union over `r • s`, for `r` the scalars with `‖r‖ ≤ 1`. We show that `balanced_hull` has the
defining properties of a hull in `balanced.hull_minimal` and `subset_balanced_hull`.
For the core we need slightly stronger assumptions to obtain a characterization as an intersection,
this is `balanced_core_eq_Inter`.

## References

* [Bourbaki, *Topological Vector Spaces*][bourbaki1987]

## Tags

balanced
-/

open Set

open Pointwise Topology Filter

variable {𝕜 E ι : Type _}

section balancedHull

section SeminormedRing

variable [SeminormedRing 𝕜]

section SMul

variable (𝕜) [SMul 𝕜 E] {s t : Set E} {x : E}

/-- The largest balanced subset of `s`.-/
def balancedCore (s : Set E) :=
  ⋃₀ { t : Set E | Balanced 𝕜 t ∧ t ⊆ s }
#align balanced_core balancedCore

/-- Helper definition to prove `balanced_core_eq_Inter`-/
def balancedCoreAux (s : Set E) :=
  ⋂ (r : 𝕜) (hr : 1 ≤ ‖r‖), r • s
#align balanced_core_aux balancedCoreAux

/-- The smallest balanced superset of `s`.-/
def balancedHull (s : Set E) :=
  ⋃ (r : 𝕜) (hr : ‖r‖ ≤ 1), r • s
#align balanced_hull balancedHull

variable {𝕜}

theorem balancedCore_subset (s : Set E) : balancedCore 𝕜 s ⊆ s :=
  unionₛ_subset fun t ht => ht.2
#align balanced_core_subset balancedCore_subset

theorem balancedCore_empty : balancedCore 𝕜 (∅ : Set E) = ∅ :=
  eq_empty_of_subset_empty (balancedCore_subset _)
#align balanced_core_empty balancedCore_empty

theorem mem_balancedCore_iff : x ∈ balancedCore 𝕜 s ↔ ∃ t, Balanced 𝕜 t ∧ t ⊆ s ∧ x ∈ t := by
  simp_rw [balancedCore, mem_sUnion, mem_set_of_eq, exists_prop, and_assoc']
#align mem_balanced_core_iff mem_balancedCore_iff

theorem smul_balancedCore_subset (s : Set E) {a : 𝕜} (ha : ‖a‖ ≤ 1) :
    a • balancedCore 𝕜 s ⊆ balancedCore 𝕜 s :=
  by
  rintro x ⟨y, hy, rfl⟩
  rw [mem_balancedCore_iff] at hy
  rcases hy with ⟨t, ht1, ht2, hy⟩
  exact ⟨t, ⟨ht1, ht2⟩, ht1 a ha (smul_mem_smul_set hy)⟩
#align smul_balanced_core_subset smul_balancedCore_subset

theorem balancedCore_balanced (s : Set E) : Balanced 𝕜 (balancedCore 𝕜 s) := fun _ =>
  smul_balancedCore_subset s
#align balanced_core_balanced balancedCore_balanced

/-- The balanced core of `t` is maximal in the sense that it contains any balanced subset
`s` of `t`.-/
theorem Balanced.subset_core_of_subset (hs : Balanced 𝕜 s) (h : s ⊆ t) : s ⊆ balancedCore 𝕜 t :=
  subset_unionₛ_of_mem ⟨hs, h⟩
#align balanced.subset_core_of_subset Balanced.subset_core_of_subset

theorem mem_balancedCoreAux_iff : x ∈ balancedCoreAux 𝕜 s ↔ ∀ r : 𝕜, 1 ≤ ‖r‖ → x ∈ r • s :=
  mem_interᵢ₂
#align mem_balanced_core_aux_iff mem_balancedCoreAux_iff

theorem mem_balancedHull_iff : x ∈ balancedHull 𝕜 s ↔ ∃ (r : 𝕜)(hr : ‖r‖ ≤ 1), x ∈ r • s :=
  mem_unionᵢ₂
#align mem_balanced_hull_iff mem_balancedHull_iff

/-- The balanced hull of `s` is minimal in the sense that it is contained in any balanced superset
`t` of `s`. -/
theorem Balanced.hull_subset_of_subset (ht : Balanced 𝕜 t) (h : s ⊆ t) : balancedHull 𝕜 s ⊆ t :=
  fun x hx => by
  obtain ⟨r, hr, y, hy, rfl⟩ := mem_balancedHull_iff.1 hx
  exact ht.smul_mem hr (h hy)
#align balanced.hull_subset_of_subset Balanced.hull_subset_of_subset

end SMul

section Module

variable [AddCommGroup E] [Module 𝕜 E] {s : Set E}

theorem balancedCore_zero_mem (hs : (0 : E) ∈ s) : (0 : E) ∈ balancedCore 𝕜 s :=
  mem_balancedCore_iff.2 ⟨0, balanced_zero, zero_subset.2 hs, zero_mem_zero⟩
#align balanced_core_zero_mem balancedCore_zero_mem

theorem balancedCore_nonempty_iff : (balancedCore 𝕜 s).Nonempty ↔ (0 : E) ∈ s :=
  ⟨fun h =>
    zero_subset.1 <|
      (zero_smul_set h).Superset.trans <|
        (balancedCore_balanced s (0 : 𝕜) <| norm_zero.trans_le zero_le_one).trans <|
          balancedCore_subset _,
    fun h => ⟨0, balancedCore_zero_mem h⟩⟩
#align balanced_core_nonempty_iff balancedCore_nonempty_iff

variable (𝕜)

theorem subset_balancedHull [NormOneClass 𝕜] {s : Set E} : s ⊆ balancedHull 𝕜 s := fun _ hx =>
  mem_balancedHull_iff.2 ⟨1, norm_one.le, _, hx, one_smul _ _⟩
#align subset_balanced_hull subset_balancedHull

variable {𝕜}

theorem balancedHull.balanced (s : Set E) : Balanced 𝕜 (balancedHull 𝕜 s) :=
  by
  intro a ha
  simp_rw [balancedHull, smul_set_Union₂, subset_def, mem_Union₂]
  rintro x ⟨r, hr, hx⟩
  rw [← smul_assoc] at hx
  exact ⟨a • r, (SeminormedRing.norm_mul _ _).trans (mul_le_one ha (norm_nonneg r) hr), hx⟩
#align balanced_hull.balanced balancedHull.balanced

end Module

end SeminormedRing

section NormedField

variable [NormedField 𝕜] [AddCommGroup E] [Module 𝕜 E] {s t : Set E}

@[simp]
theorem balancedCoreAux_empty : balancedCoreAux 𝕜 (∅ : Set E) = ∅ :=
  by
  simp_rw [balancedCoreAux, Inter₂_eq_empty_iff, smul_set_empty]
  exact fun _ => ⟨1, norm_one.ge, not_mem_empty _⟩
#align balanced_core_aux_empty balancedCoreAux_empty

theorem balancedCoreAux_subset (s : Set E) : balancedCoreAux 𝕜 s ⊆ s := fun x hx => by
  simpa only [one_smul] using mem_balancedCoreAux_iff.1 hx 1 norm_one.ge
#align balanced_core_aux_subset balancedCoreAux_subset

theorem balancedCoreAux_balanced (h0 : (0 : E) ∈ balancedCoreAux 𝕜 s) :
    Balanced 𝕜 (balancedCoreAux 𝕜 s) :=
  by
  rintro a ha x ⟨y, hy, rfl⟩
  obtain rfl | h := eq_or_ne a 0
  · rwa [zero_smul]
  rw [mem_balancedCoreAux_iff] at hy⊢
  intro r hr
  have h'' : 1 ≤ ‖a⁻¹ • r‖ := by
    rw [norm_smul, norm_inv]
    exact one_le_mul_of_one_le_of_one_le (one_le_inv (norm_pos_iff.mpr h) ha) hr
  have h' := hy (a⁻¹ • r) h''
  rwa [smul_assoc, mem_inv_smul_set_iff₀ h] at h'
#align balanced_core_aux_balanced balancedCoreAux_balanced

theorem balancedCoreAux_maximal (h : t ⊆ s) (ht : Balanced 𝕜 t) : t ⊆ balancedCoreAux 𝕜 s :=
  by
  refine' fun x hx => mem_balancedCoreAux_iff.2 fun r hr => _
  rw [mem_smul_set_iff_inv_smul_mem₀ (norm_pos_iff.mp <| zero_lt_one.trans_le hr)]
  refine' h (ht.smul_mem _ hx)
  rw [norm_inv]
  exact inv_le_one hr
#align balanced_core_aux_maximal balancedCoreAux_maximal

theorem balancedCore_subset_balancedCoreAux : balancedCore 𝕜 s ⊆ balancedCoreAux 𝕜 s :=
  balancedCoreAux_maximal (balancedCore_subset s) (balancedCore_balanced s)
#align balanced_core_subset_balanced_core_aux balancedCore_subset_balancedCoreAux

theorem balancedCore_eq_interᵢ (hs : (0 : E) ∈ s) :
    balancedCore 𝕜 s = ⋂ (r : 𝕜) (hr : 1 ≤ ‖r‖), r • s :=
  by
  refine' balanced_core_subset_balanced_core_aux.antisymm _
  refine' (balancedCoreAux_balanced _).subset_core_of_subset (balancedCoreAux_subset s)
  exact balancedCore_subset_balancedCoreAux (balancedCore_zero_mem hs)
#align balanced_core_eq_Inter balancedCore_eq_interᵢ

theorem subset_balancedCore (ht : (0 : E) ∈ t) (hst : ∀ (a : 𝕜) (ha : ‖a‖ ≤ 1), a • s ⊆ t) :
    s ⊆ balancedCore 𝕜 t := by
  rw [balancedCore_eq_interᵢ ht]
  refine' subset_Inter₂ fun a ha => _
  rw [← smul_inv_smul₀ (norm_pos_iff.mp <| zero_lt_one.trans_le ha) s]
  refine' smul_set_mono (hst _ _)
  rw [norm_inv]
  exact inv_le_one ha
#align subset_balanced_core subset_balancedCore

end NormedField

end balancedHull

/-! ### Topological properties -/

section Topology

variable [NontriviallyNormedField 𝕜] [AddCommGroup E] [Module 𝕜 E] [TopologicalSpace E]
  [ContinuousSMul 𝕜 E] {U : Set E}

protected theorem IsClosed.balancedCore (hU : IsClosed U) : IsClosed (balancedCore 𝕜 U) :=
  by
  by_cases h : (0 : E) ∈ U
  · rw [balancedCore_eq_interᵢ h]
    refine' isClosed_interᵢ fun a => _
    refine' isClosed_interᵢ fun ha => _
    have ha' := lt_of_lt_of_le zero_lt_one ha
    rw [norm_pos_iff] at ha'
    refine' isClosedMap_smul_of_ne_zero ha' U hU
  convert isClosed_empty
  contrapose! h
  exact balanced_core_nonempty_iff.mp (Set.nonempty_iff_ne_empty.2 h)
#align is_closed.balanced_core IsClosed.balancedCore

theorem balancedCore_mem_nhds_zero (hU : U ∈ 𝓝 (0 : E)) : balancedCore 𝕜 U ∈ 𝓝 (0 : E) :=
  by
  -- Getting neighborhoods of the origin for `0 : 𝕜` and `0 : E`
  obtain ⟨r, V, hr, hV, hrVU⟩ :
    ∃ (r : ℝ)(V : Set E), 0 < r ∧ V ∈ 𝓝 (0 : E) ∧ ∀ (c : 𝕜) (y : E), ‖c‖ < r → y ∈ V → c • y ∈ U :=
    by
    have h : Filter.Tendsto (fun x : 𝕜 × E => x.fst • x.snd) (𝓝 (0, 0)) (𝓝 0) :=
      continuous_smul.tendsto' (0, 0) _ (smul_zero _)
    simpa only [← Prod.exists', ← Prod.forall', ← and_imp, ← and_assoc, exists_prop] using
      h.basis_left (normed_add_comm_group.nhds_zero_basis_norm_lt.prod_nhds (𝓝 _).basis_sets) U hU
  rcases NormedField.exists_norm_lt 𝕜 hr with ⟨y, hy₀, hyr⟩
  rw [norm_pos_iff] at hy₀
  have : y • V ∈ 𝓝 (0 : E) := (set_smul_mem_nhds_zero_iff hy₀).mpr hV
  -- It remains to show that `y • V ⊆ balanced_core 𝕜 U`
  refine' Filter.mem_of_superset this (subset_balancedCore (mem_of_mem_nhds hU) fun a ha => _)
  rw [smul_smul]
  rintro _ ⟨z, hz, rfl⟩
  refine' hrVU _ _ _ hz
  rw [norm_mul, ← one_mul r]
  exact mul_lt_mul' ha hyr (norm_nonneg y) one_pos
#align balanced_core_mem_nhds_zero balancedCore_mem_nhds_zero

variable (𝕜 E)

theorem nhds_basis_balanced :
    (𝓝 (0 : E)).HasBasis (fun s : Set E => s ∈ 𝓝 (0 : E) ∧ Balanced 𝕜 s) id :=
  Filter.hasBasis_self.mpr fun s hs =>
    ⟨balancedCore 𝕜 s, balancedCore_mem_nhds_zero hs, balancedCore_balanced s,
      balancedCore_subset s⟩
#align nhds_basis_balanced nhds_basis_balanced

theorem nhds_basis_closed_balanced [RegularSpace E] :
    (𝓝 (0 : E)).HasBasis (fun s : Set E => s ∈ 𝓝 (0 : E) ∧ IsClosed s ∧ Balanced 𝕜 s) id :=
  by
  refine'
    (closed_nhds_basis 0).to_hasBasis (fun s hs => _) fun s hs => ⟨s, ⟨hs.1, hs.2.1⟩, rfl.subset⟩
  refine' ⟨balancedCore 𝕜 s, ⟨balancedCore_mem_nhds_zero hs.1, _⟩, balancedCore_subset s⟩
  exact ⟨hs.2.balancedCore, balancedCore_balanced s⟩
#align nhds_basis_closed_balanced nhds_basis_closed_balanced

end Topology


/-
Copyright (c) 2018 Chris Hughes. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Chris Hughes, Johannes Hölzl, Scott Morrison, Jens Wagemaker
-/
import tactic.ring_exp
import tactic.chain
import algebra.monoid_algebra
import data.finset.sort

/-!
# Theory of univariate polynomials

Polynomials are represented as `add_monoid_algebra R ℕ`, where `R` is a commutative semiring.
In this file, we define `polynomial`, provide basic instances, and prove an `ext` lemma.
-/

noncomputable theory

/-- `polynomial R` is the type of univariate polynomials over `R`.

Polynomials should be seen as (semi-)rings with the additional constructor `X`.
The embedding from `R` is called `C`. -/
def polynomial (R : Type*) [semiring R] := add_monoid_algebra R ℕ

open finsupp add_monoid_algebra
open_locale big_operators

namespace polynomial
universes u
variables {R : Type u} {a b : R} {m n : ℕ}

section semiring
variables [semiring R] {p q : polynomial R}

instance : inhabited (polynomial R) := add_monoid_algebra.inhabited _ _
instance : semiring (polynomial R) := add_monoid_algebra.semiring
instance {S} [semiring S] [module S R] : module S (polynomial R) :=
add_monoid_algebra.module
instance {S₁ S₂} [semiring S₁] [semiring S₂] [module S₁ R] [module S₂ R]
  [smul_comm_class S₁ S₂ R] : smul_comm_class S₁ S₂ (polynomial R) :=
add_monoid_algebra.smul_comm_class
instance {S₁ S₂} [has_scalar S₁ S₂] [semiring S₁] [semiring S₂] [module S₁ R] [module S₂ R]
  [is_scalar_tower S₁ S₂ R] : is_scalar_tower S₁ S₂ (polynomial R) :=
add_monoid_algebra.is_scalar_tower

instance [subsingleton R] : unique (polynomial R) := add_monoid_algebra.unique

/--
The set of all `n` such that `X^n` has a non-zero coefficient.
-/
def support (p : polynomial R) : finset ℕ :=
p.support

@[simp] lemma support_zero : (0 : polynomial R).support = ∅ := rfl

@[simp] lemma support_eq_empty : p.support = ∅ ↔ p = 0 :=
by simp [support]

lemma card_support_eq_zero : p.support.card = 0 ↔ p = 0 :=
by simp

/-- `monomial s a` is the monomial `a * X^s` -/
def monomial (n : ℕ) : R →ₗ[R] polynomial R := finsupp.lsingle n

@[simp]
lemma monomial_zero_right (n : ℕ) :
  monomial n (0 : R) = 0 :=
finsupp.single_zero

-- This is not a `simp` lemma as `monomial_zero_left` is more general.
lemma monomial_zero_one : monomial 0 (1 : R) = 1 := rfl

lemma monomial_def (n : ℕ) (a : R) : monomial n a = finsupp.single n a := rfl

lemma monomial_add (n : ℕ) (r s : R) :
  monomial n (r + s) = monomial n r + monomial n s :=
finsupp.single_add

lemma monomial_mul_monomial (n m : ℕ) (r s : R) :
  monomial n r * monomial m s = monomial (n + m) (r * s) :=
add_monoid_algebra.single_mul_single

@[simp]
lemma monomial_pow (n : ℕ) (r : R) (k : ℕ) :
  (monomial n r)^k = monomial (n*k) (r^k) :=
begin
  rw mul_comm,
  exact add_monoid_algebra.single_pow k,
end

lemma smul_monomial {S} [semiring S] [module S R] (a : S) (n : ℕ) (b : R) :
  a • monomial n b = monomial n (a • b) :=
finsupp.smul_single _ _ _

lemma support_add : (p + q).support ⊆ p.support ∪ q.support := support_add

/--
`C a` is the constant polynomial `a`.
`C` is provided as a ring homomorphism.
-/
def C : R →+* polynomial R := add_monoid_algebra.single_zero_ring_hom

@[simp] lemma monomial_zero_left (a : R) : monomial 0 a = C a := rfl

lemma C_0 : C (0 : R) = 0 := single_zero

lemma C_1 : C (1 : R) = 1 := rfl

lemma C_mul : C (a * b) = C a * C b := C.map_mul a b

lemma C_add : C (a + b) = C a + C b := C.map_add a b

@[simp] lemma C_bit0 : C (bit0 a) = bit0 (C a) := C_add

@[simp] lemma C_bit1 : C (bit1 a) = bit1 (C a) := by simp [bit1, C_bit0]

lemma C_pow : C (a ^ n) = C a ^ n := C.map_pow a n

@[simp]
lemma C_eq_nat_cast (n : ℕ) : C (n : R) = (n : polynomial R) :=
C.map_nat_cast n

@[simp]
lemma sum_C_index {a} {β} [add_comm_monoid β] {f : ℕ → R → β} (h : f 0 0 = 0) :
  (C a).sum f = f 0 a :=
sum_single_index h

/-- `X` is the polynomial variable (aka indeterminant). -/
def X : polynomial R := monomial 1 1

lemma monomial_one_one_eq_X : monomial 1 (1 : R) = X := rfl
lemma monomial_one_right_eq_X_pow (n : ℕ) : monomial n 1 = X^n :=
begin
  induction n with n ih,
  { simp [monomial_zero_one], },
  { rw [pow_succ, ←ih, ←monomial_one_one_eq_X, monomial_mul_monomial, add_comm, one_mul], }
end

/-- `X` commutes with everything, even when the coefficients are noncommutative. -/
lemma X_mul : X * p = p * X :=
by { ext, simp [X, monomial, add_monoid_algebra.mul_apply, sum_single_index, add_comm] }

lemma X_pow_mul {n : ℕ} : X^n * p = p * X^n :=
begin
  induction n with n ih,
  { simp, },
  { conv_lhs { rw pow_succ', },
    rw [mul_assoc, X_mul, ←mul_assoc, ih, mul_assoc, ←pow_succ'], }
end

lemma X_pow_mul_assoc {n : ℕ} : (p * X^n) * q = (p * q) * X^n :=
by rw [mul_assoc, X_pow_mul, ←mul_assoc]

lemma commute_X (p : polynomial R) : commute X p := X_mul

@[simp]
lemma monomial_mul_X (n : ℕ) (r : R) : monomial n r * X = monomial (n+1) r :=
by erw [monomial_mul_monomial, mul_one]

@[simp]
lemma monomial_mul_X_pow (n : ℕ) (r : R) (k : ℕ) : monomial n r * X^k = monomial (n+k) r :=
begin
  induction k with k ih,
  { simp, },
  { simp [ih, pow_succ', ←mul_assoc, add_assoc], },
end

@[simp]
lemma X_mul_monomial (n : ℕ) (r : R) : X * monomial n r = monomial (n+1) r :=
by rw [X_mul, monomial_mul_X]

@[simp]
lemma X_pow_mul_monomial (k n : ℕ) (r : R) : X^k * monomial n r = monomial (n+k) r :=
by rw [X_pow_mul, monomial_mul_X_pow]

/-- coeff p n is the coefficient of X^n in p -/
def coeff (p : polynomial R) : ℕ → R := @coe_fn (ℕ →₀ R) _ p

@[simp] lemma coeff_mk (s) (f) (h) : coeff (finsupp.mk s f h : polynomial R) = f := rfl

lemma coeff_monomial : coeff (monomial n a) m = if n = m then a else 0 :=
finsupp.single_apply

@[simp] lemma coeff_zero (n : ℕ) : coeff (0 : polynomial R) n = 0 := rfl

@[simp] lemma coeff_one_zero : coeff (1 : polynomial R) 0 = 1 := coeff_monomial

@[simp] lemma coeff_X_one : coeff (X : polynomial R) 1 = 1 := coeff_monomial

@[simp] lemma coeff_X_zero : coeff (X : polynomial R) 0 = 0 := coeff_monomial

@[simp] lemma coeff_monomial_succ : coeff (monomial (n+1) a) 0 = 0 :=
by simp [coeff_monomial]

lemma coeff_X : coeff (X : polynomial R) n = if 1 = n then 1 else 0 := coeff_monomial

lemma coeff_X_of_ne_one {n : ℕ} (hn : n ≠ 1) : coeff (X : polynomial R) n = 0 :=
by rw [coeff_X, if_neg hn.symm]

lemma coeff_C : coeff (C a) n = ite (n = 0) a 0 :=
by { convert coeff_monomial using 2, simp [eq_comm], }

@[simp] lemma coeff_C_zero : coeff (C a) 0 = a := coeff_monomial

lemma coeff_C_ne_zero (h : n ≠ 0) : (C a).coeff n = 0 :=
by rw [coeff_C, if_neg h]

theorem nontrivial.of_polynomial_ne (h : p ≠ q) : nontrivial R :=
⟨⟨0, 1, λ h01 : 0 = 1, h $
    by rw [← mul_one p, ← mul_one q, ← C_1, ← h01, C_0, mul_zero, mul_zero] ⟩⟩

lemma single_eq_C_mul_X : ∀{n}, monomial n a = C a * X^n
| 0     := (mul_one _).symm
| (n+1) :=
  calc monomial (n + 1) a = monomial n a * X : by { rw [X, monomial_mul_monomial, mul_one], }
    ... = (C a * X^n) * X : by rw [single_eq_C_mul_X]
    ... = C a * X^(n+1) : by simp only [pow_add, mul_assoc, pow_one]

@[simp] lemma C_inj : C a = C b ↔ a = b :=
⟨λ h, coeff_C_zero.symm.trans (h.symm ▸ coeff_C_zero), congr_arg C⟩

@[simp] lemma C_eq_zero : C a = 0 ↔ a = 0 :=
calc C a = 0 ↔ C a = C 0 : by rw C_0
         ... ↔ a = 0 : C_inj

theorem ext_iff {p q : polynomial R} : p = q ↔ ∀ n, coeff p n = coeff q n :=
finsupp.ext_iff

@[ext] lemma ext {p q : polynomial R} : (∀ n, coeff p n = coeff q n) → p = q :=
finsupp.ext

@[ext] lemma add_hom_ext' {M : Type*} [add_monoid M] {f g : polynomial R →+ M}
  (h : ∀ n, f.comp (monomial n).to_add_monoid_hom = g.comp (monomial n).to_add_monoid_hom) :
  f = g :=
finsupp.add_hom_ext' h

lemma add_hom_ext {M : Type*} [add_monoid M] {f g : polynomial R →+ M}
  (h : ∀ n a, f (monomial n a) = g (monomial n a)) :
  f = g :=
finsupp.add_hom_ext h

@[ext] lemma lhom_ext' {M : Type*} [add_comm_monoid M] [module R M] {f g : polynomial R →ₗ[R] M}
  (h : ∀ n, f.comp (monomial n) = g.comp (monomial n)) :
  f = g :=
finsupp.lhom_ext' h

-- this has the same content as the subsingleton
lemma eq_zero_of_eq_zero (h : (0 : R) = (1 : R)) (p : polynomial R) : p = 0 :=
by rw [←one_smul R p, ←h, zero_smul]

lemma support_monomial (n) (a : R) (H : a ≠ 0) : (monomial n a).support = singleton n :=
finsupp.support_single_ne_zero H

lemma support_monomial' (n) (a : R) : (monomial n a).support ⊆ singleton n :=
finsupp.support_single_subset

lemma X_pow_eq_monomial (n) : X ^ n = monomial n (1:R) :=
begin
  induction n with n hn,
  { refl, },
  { rw [pow_succ', hn, X, monomial_mul_monomial, one_mul] },
end

lemma monomial_eq_smul_X {n} : monomial n (a : R) = a • X^n :=
calc monomial n a = monomial n (a * 1) : by simp
  ... = a • monomial n 1 : (smul_single' _ _ _).symm
  ... = a • X^n  : by rw X_pow_eq_monomial

lemma support_X_pow (H : ¬ (1:R) = 0) (n : ℕ) : (X^n : polynomial R).support = singleton n :=
begin
  convert support_monomial n 1 H,
  exact X_pow_eq_monomial n,
end

lemma support_X_empty (H : (1:R)=0) : (X : polynomial R).support = ∅ :=
begin
  rw [X, H, monomial_zero_right, support_zero],
end

lemma support_X (H : ¬ (1 : R) = 0) : (X : polynomial R).support = singleton 1 :=
begin
  rw [← pow_one X, support_X_pow H 1],
end

lemma monomial_left_inj {R : Type*} [semiring R] {a : R} (ha : a ≠ 0) {i j : ℕ} :
  (monomial i a) = (monomial j a) ↔ i = j :=
finsupp.single_left_inj ha

lemma nat_cast_mul {R : Type*} [semiring R] (n : ℕ) (p : polynomial R) :
  (n : polynomial R) * p = n • p :=
(nsmul_eq_mul _ _).symm

end semiring

section comm_semiring
variables [comm_semiring R]

instance : comm_semiring (polynomial R) := add_monoid_algebra.comm_semiring

end comm_semiring

section ring
variables [ring R]

instance : ring (polynomial R) := add_monoid_algebra.ring

@[simp] lemma coeff_neg (p : polynomial R) (n : ℕ) : coeff (-p) n = -coeff p n := rfl

@[simp]
lemma coeff_sub (p q : polynomial R) (n : ℕ) : coeff (p - q) n = coeff p n - coeff q n := rfl

@[simp] lemma monomial_neg (n : ℕ) (a : R) : monomial n (-a) = -(monomial n a) :=
by rw [eq_neg_iff_add_eq_zero, ←monomial_add, neg_add_self, monomial_zero_right]

@[simp] lemma support_neg {p : polynomial R} : (-p).support = p.support :=
by simp [support]

end ring

instance [comm_ring R] : comm_ring (polynomial R) := add_monoid_algebra.comm_ring

section nonzero_semiring

variables [semiring R] [nontrivial R]
instance : nontrivial (polynomial R) := add_monoid_algebra.nontrivial

lemma X_ne_zero : (X : polynomial R) ≠ 0 :=
mt (congr_arg (λ p, coeff p 1)) (by simp)

end nonzero_semiring

section repr
variables [semiring R]
local attribute [instance, priority 100] classical.prop_decidable

instance [has_repr R] : has_repr (polynomial R) :=
⟨λ p, if p = 0 then "0"
  else (p.support.sort (≤)).foldr
    (λ n a, a ++ (if a = "" then "" else " + ") ++
      if n = 0
        then "C (" ++ repr (coeff p n) ++ ")"
        else if n = 1
          then if (coeff p n) = 1 then "X" else "C (" ++ repr (coeff p n) ++ ") * X"
          else if (coeff p n) = 1 then "X ^ " ++ repr n
            else "C (" ++ repr (coeff p n) ++ ") * X ^ " ++ repr n) ""⟩

end repr

end polynomial


import data.sym2
open function

/-
We begin by defining a graph as a triple:
- vertices;
- edges;
- and an endpoint map taking each edge to the unordered pair of its endpoints.
-/

structure graph :=
  (vertices : Type)
  (edges : Type)
  (endpoints : edges → sym2 vertices)

/-
This theorem is a sanity check to see that sym2 works as we want.
-/

theorem endpoint_sym (G : graph) (a b : G.vertices) : ⟦(a,b)⟧ = ⟦(b,a)⟧ :=
begin
  exact sym2.eq_swap,
end

/-
Now we can define adjacency.
-/

def are_adjacent (G : graph) (a b : G.vertices) : Prop := ∃ e : G.edges, G.endpoints(e) = ⟦(a,b)⟧

/-
We now define a morphism of graphs to be a triple:
- a map on vertices;
- a map on edges;
- and a compatability condition between the two maps.
-/

structure morphism (G : graph) (H : graph) :=
  (vertex_map : G.vertices → H.vertices)
  (edge_map : G.edges → H.edges)
  (compatability : H.endpoints ∘ edge_map = sym2.map vertex_map ∘ G.endpoints)

/-
I want a convenient infix notation for a morphism.
I tried to overload the usual → arrow, but Lean got confused
(or probably I did it incorrectly).
-/

infix `↦` :50 := morphism

/-
We want to define composition of morphisms, but we need to prove that the
compatability condition needed holds when we do the `obvious' thing ...
-/

theorem compatability {G H K : graph} (α : G ↦ H) (β : H ↦ K) : K.endpoints ∘ β.edge_map ∘ α.edge_map = sym2.map (β.vertex_map ∘ α.vertex_map) ∘ G.endpoints :=
begin
  calc K.endpoints ∘ β.edge_map ∘ α.edge_map = (sym2.map β.vertex_map ∘ H.endpoints) ∘ α.edge_map : by rw ← β.compatability
                                         ... = sym2.map β.vertex_map ∘ (sym2.map α.vertex_map ∘ G.endpoints) : by rw ← α.compatability
                                         ... = (sym2.map β.vertex_map ∘ sym2.map α.vertex_map) ∘ G.endpoints : by refl
                                         ... = sym2.map (β.vertex_map ∘ α.vertex_map) ∘ G.endpoints : by rw ← sym2.map_comp,
end

/-
Now that we know the compatability condition holds, we can go ahead and
define composition of morphisms.
-/

def compose {G H K : graph} (β : H ↦ K) (α : G ↦ H) : G ↦ K :=
  morphism.mk (β.vertex_map ∘ α.vertex_map) (β.edge_map ∘ α.edge_map) (by exact compatability α β)

/-
We want a convenient infix operator for composition of morphisms.
After my experience of trying to overload → I decided not to even attempt
to overload ∘.
-/

infix `⊚`:80 := compose

/-
Proving associativity of composition is now trivial ...
-/

theorem mor_assoc {G H K L : graph} (α : G ↦ H) (β : H ↦ K) (γ : K ↦ L) : γ ⊚ (β ⊚ α) = (γ ⊚ β) ⊚ α :=
begin
  refl,
end

/-
We want to define the identity morphism, but that requires a compatability proof.
-/

theorem id_compatability (G : graph) : G.endpoints ∘ id = sym2.map id ∘ G.endpoints :=
begin
  rw sym2.map_id,
  refl,
end

/-
Now we can define the identity morphism.
-/

def 𝕀 (G : graph) : G ↦ G :=
  morphism.mk (id) (id) (by exact id_compatability G)

/-
Next we turn our attention to defining an isomorphism.
-/

def isomorphism {G H : graph} (α : G ↦ H) : Prop :=
  ∃ β : H ↦ G, α ⊚ β = 𝕀 H ∧ β ⊚ α = 𝕀 G

/-
This is a sanity check: proving that the identity is an isomorphism.
-/

theorem id_is_iso (G : graph) : isomorphism (𝕀 G) :=
begin
  use 𝕀 G,
  split,
  refl,
  refl,
end

/-
Proving equality on structures is a pain, so we should prove an extensionality
theorem.  Basically, it suffices for the respective vertex maps and edge maps to
be equal.
-/

theorem mor_ext {G H : graph} (α : G ↦ H) (β : G ↦ H) (hv : α.vertex_map = β.vertex_map) (he : α.edge_map = β.edge_map) : α = β :=
begin
  cases α,
  cases β,
  rw morphism.mk.inj_eq,
  exact ⟨hv, he⟩,
end

/-
The next two are helpful so that we don't have to keep repeating silly proofs.
Basically, we show that equal morphisms have equal vertex maps and edge maps.
-/

theorem eq_mor_eq_vmap {G H : graph} (α : G ↦ H) (β : G ↦ H) (hyp : α = β) : α.vertex_map = β.vertex_map :=
begin
  cases α,
  cases β,
  rw morphism.mk.inj_eq at hyp,
  exact hyp.1,
end

theorem eq_mor_eq_emap {G H : graph} (α : G ↦ H) (β : G ↦ H) (hyp : α = β) : α.edge_map = β.edge_map :=
begin
  cases α,
  cases β,
  rw morphism.mk.inj_eq at hyp,
  exact hyp.2,
end

/-
Our final two results are proving that a morphism is an isomorphism if and
only if the vertex map and edge map are both bijective.  Of course, we are
leaning very heavily on the corresponding results for functions.
-/

theorem iso.bij {G H : graph} (α : G ↦ H) (hyp : isomorphism α) : bijective α.vertex_map ∧ bijective α.edge_map :=
begin
  cases hyp with β hβ,
  cases hβ with h₁ h₂,
  split,
  split,
  have h₃ : left_inverse β.vertex_map α.vertex_map,
  {
    intro x,
    calc β.vertex_map (α.vertex_map x) = (β ⊚ α).vertex_map x : by refl
                                   ... = (𝕀 G).vertex_map x : by rw eq_mor_eq_vmap (β ⊚ α) (𝕀 G) (h₂)
                                   ... = x : by refl,
  },
  apply left_inverse.injective h₃,

  have h₃ : right_inverse β.vertex_map α.vertex_map,
  {
    intro x,
    calc α.vertex_map (β.vertex_map x) = (α ⊚ β).vertex_map x : by refl
                                   ... = (𝕀 H).vertex_map x : by rw eq_mor_eq_vmap (α ⊚ β) (𝕀 H) (h₁)
                                   ... = x : by refl,
  },
  apply right_inverse.surjective h₃,

  split,
  have h₃ : left_inverse β.edge_map α.edge_map,
  {
    intro x,
    calc β.edge_map (α.edge_map x) = (β ⊚ α).edge_map x : by refl
                                   ... = (𝕀 G).edge_map x : by rw eq_mor_eq_emap (β ⊚ α) (𝕀 G) (h₂)
                                   ... = x : by refl,
  },
  apply left_inverse.injective h₃,

  have h₃ : right_inverse β.edge_map α.edge_map,
  {
    intro x,
    calc α.edge_map (β.edge_map x) = (α ⊚ β).edge_map x : by refl
                                   ... = (𝕀 H).edge_map x : by rw eq_mor_eq_emap (α ⊚ β) (𝕀 H) (h₁)
                                   ... = x : by refl,
  },
  apply right_inverse.surjective h₃,
end

theorem bij.iso {G H : graph} (α : G ↦ H) (hv : bijective α.vertex_map) (he : bijective α.edge_map) : isomorphism α :=
begin
  let φ := α.vertex_map,
  let ψ := α.edge_map,

  rw bijective_iff_has_inverse at hv,
  cases hv with φ' hφ',
  rw bijective_iff_has_inverse at he,
  cases he with ψ' hψ',

  have comp' : G.endpoints ∘ ψ' = sym2.map φ' ∘ H.endpoints,
  {
    calc G.endpoints ∘ ψ' = id ∘ G.endpoints ∘ ψ' : by refl
                      ... = sym2.map id ∘ G.endpoints ∘ ψ' : by rw sym2.map_id
                      ... = sym2.map (φ' ∘ φ) ∘ G.endpoints ∘ ψ' : by rw left_inverse.id hφ'.1
                      ... = sym2.map φ' ∘ sym2.map φ ∘ G.endpoints ∘ ψ' : by rw sym2.map_comp
                      ... = sym2.map φ' ∘ (H.endpoints ∘ ψ) ∘ ψ' : by rw α.compatability
                      ... = sym2.map φ' ∘ H.endpoints ∘ (ψ ∘ ψ') : by refl
                      ... = sym2.map φ' ∘ H.endpoints ∘ id : by rw right_inverse.id hψ'.2
                      ... = sym2.map φ' ∘ H.endpoints : by refl,
  },

  let β := morphism.mk (φ') (ψ') (comp'),
  use β,
  split,

  {
    apply mor_ext,
    {
      calc (α ⊚ β).vertex_map = α.vertex_map ∘ β.vertex_map : by refl
                          ... = φ ∘ φ' : by refl
                          ... = id : by exact left_inverse.id hφ'.2
                          ... = (𝕀 H).vertex_map : by refl,
    },
    {
      calc (α ⊚ β).edge_map = α.edge_map ∘ β.edge_map : by refl
                          ... = ψ ∘ ψ' : by refl
                          ... = id : by exact left_inverse.id hψ'.2
                          ... = (𝕀 H).edge_map : by refl,
    },
  },

  {
    apply mor_ext,
    {
      calc (β ⊚ α).vertex_map = β.vertex_map ∘ α.vertex_map : by refl
                          ... = φ' ∘ φ : by refl
                          ... = id : by exact left_inverse.id hφ'.1
                          ... = (𝕀 G).vertex_map : by refl,
    },
    {
      calc (β ⊚ α).edge_map = β.edge_map ∘ α.edge_map : by refl
                          ... = ψ' ∘ ψ : by refl
                          ... = id : by exact left_inverse.id hψ'.1
                          ... = (𝕀 G).edge_map : by refl,
    },
  },
end


/-
Copyright (c) 2020 Johan Commelin. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johan Commelin
-/
import analysis.special_functions.complex.log
import ring_theory.roots_of_unity

/-!
# Complex roots of unity

In this file we show that the `n`-th complex roots of unity
are exactly the complex numbers `e ^ (2 * real.pi * complex.I * (i / n))` for `i ∈ finset.range n`.

## Main declarations

* `complex.mem_roots_of_unity`: the complex `n`-th roots of unity are exactly the
  complex numbers of the form `e ^ (2 * real.pi * complex.I * (i / n))` for some `i < n`.
* `complex.card_roots_of_unity`: the number of `n`-th roots of unity is exactly `n`.

-/

namespace complex

open polynomial real
open_locale nat real

lemma is_primitive_root_exp_of_coprime (i n : ℕ) (h0 : n ≠ 0) (hi : i.coprime n) :
  is_primitive_root (exp (2 * π * I * (i / n))) n :=
begin
  rw is_primitive_root.iff_def,
  simp only [← exp_nat_mul, exp_eq_one_iff],
  have hn0 : (n : ℂ) ≠ 0, by exact_mod_cast h0,
  split,
  { use i,
    field_simp [hn0, mul_comm (i : ℂ), mul_comm (n : ℂ)] },
  { simp only [hn0, mul_right_comm _ _ ↑n, mul_left_inj' two_pi_I_ne_zero, ne.def, not_false_iff,
      mul_comm _ (i : ℂ), ← mul_assoc _ (i : ℂ), exists_imp_distrib] with field_simps,
    norm_cast,
    rintro l k hk,
    have : n ∣ i * l,
    { rw [← int.coe_nat_dvd, hk], apply dvd_mul_left },
    exact hi.symm.dvd_of_dvd_mul_left this }
end

lemma is_primitive_root_exp (n : ℕ) (h0 : n ≠ 0) : is_primitive_root (exp (2 * π * I / n)) n :=
by simpa only [nat.cast_one, one_div]
  using is_primitive_root_exp_of_coprime 1 n h0 n.coprime_one_left

lemma is_primitive_root_iff (ζ : ℂ) (n : ℕ) (hn : n ≠ 0) :
  is_primitive_root ζ n ↔ (∃ (i < (n : ℕ)) (hi : i.coprime n), exp (2 * π * I * (i / n)) = ζ) :=
begin
  have hn0 : (n : ℂ) ≠ 0 := by exact_mod_cast hn,
  split, swap,
  { rintro ⟨i, -, hi, rfl⟩, exact is_primitive_root_exp_of_coprime i n hn hi },
  intro h,
  obtain ⟨i, hi, rfl⟩ :=
    (is_primitive_root_exp n hn).eq_pow_of_pow_eq_one h.pow_eq_one (nat.pos_of_ne_zero hn),
  refine ⟨i, hi, ((is_primitive_root_exp n hn).pow_iff_coprime (nat.pos_of_ne_zero hn) i).mp h, _⟩,
  rw [← exp_nat_mul],
  congr' 1,
  field_simp [hn0, mul_comm (i : ℂ)]
end

/-- The complex `n`-th roots of unity are exactly the
complex numbers of the form `e ^ (2 * real.pi * complex.I * (i / n))` for some `i < n`. -/
lemma mem_roots_of_unity (n : ℕ+) (x : units ℂ) :
  x ∈ roots_of_unity n ℂ ↔ (∃ i < (n : ℕ), exp (2 * π * I * (i / n)) = x) :=
begin
  rw [mem_roots_of_unity, units.ext_iff, units.coe_pow, units.coe_one],
  have hn0 : (n : ℂ) ≠ 0 := by exact_mod_cast (n.ne_zero),
  split,
  { intro h,
    obtain ⟨i, hi, H⟩ : ∃ i < (n : ℕ), exp (2 * π * I / n) ^ i = x,
    { simpa only using (is_primitive_root_exp n n.ne_zero).eq_pow_of_pow_eq_one h n.pos },
    refine ⟨i, hi, _⟩,
    rw [← H, ← exp_nat_mul],
    congr' 1,
    field_simp [hn0, mul_comm (i : ℂ)] },
  { rintro ⟨i, hi, H⟩,
    rw [← H, ← exp_nat_mul, exp_eq_one_iff],
    use i,
    field_simp [hn0, mul_comm ((n : ℕ) : ℂ), mul_comm (i : ℂ)] }
end

lemma card_roots_of_unity (n : ℕ+) : fintype.card (roots_of_unity n ℂ) = n :=
(is_primitive_root_exp n n.ne_zero).card_roots_of_unity

lemma card_primitive_roots (k : ℕ) : (primitive_roots k ℂ).card = φ k :=
begin
  by_cases h : k = 0,
  { simp [h] },
  exact (is_primitive_root_exp k h).card_primitive_roots,
end

end complex

lemma is_primitive_root.norm'_eq_one {ζ : ℂ} {n : ℕ} (h : is_primitive_root ζ n) (hn : n ≠ 0) :
  ‖ζ‖ = 1 := complex.norm_eq_one_of_pow_eq_one h.pow_eq_one hn

lemma is_primitive_root.nnnorm_eq_one {ζ : ℂ} {n : ℕ} (h : is_primitive_root ζ n) (hn : n ≠ 0) :
  ‖ζ‖₊ = 1 := subtype.ext $ h.norm'_eq_one hn

lemma is_primitive_root.arg_ext {n m : ℕ} {ζ μ : ℂ} (hζ : is_primitive_root ζ n)
  (hμ : is_primitive_root μ m) (hn : n ≠ 0) (hm : m ≠ 0) (h : ζ.arg = μ.arg) : ζ = μ :=
complex.ext_abs_arg ((hζ.norm'_eq_one hn).trans (hμ.norm'_eq_one hm).symm) h

lemma is_primitive_root.arg_eq_zero_iff {n : ℕ} {ζ : ℂ} (hζ : is_primitive_root ζ n)
  (hn : n ≠ 0) : ζ.arg = 0 ↔ ζ = 1 :=
⟨λ h, hζ.arg_ext is_primitive_root.one hn one_ne_zero (h.trans complex.arg_one.symm),
 λ h, h.symm ▸ complex.arg_one⟩

lemma is_primitive_root.arg_eq_pi_iff {n : ℕ} {ζ : ℂ} (hζ : is_primitive_root ζ n)
  (hn : n ≠ 0) : ζ.arg = real.pi ↔ ζ = -1 :=
⟨λ h, hζ.arg_ext (is_primitive_root.neg_one 0 two_ne_zero.symm) hn two_ne_zero
      (h.trans complex.arg_neg_one.symm), λ h, h.symm ▸ complex.arg_neg_one⟩

lemma is_primitive_root.arg {n : ℕ} {ζ : ℂ} (h : is_primitive_root ζ n) (hn : n ≠ 0) :
  ∃ i : ℤ, ζ.arg = i / n * (2 * real.pi) ∧ is_coprime i n ∧ i.nat_abs < n :=
begin
  rw complex.is_primitive_root_iff _ _ hn at h,
  obtain ⟨i, h, hin, rfl⟩ := h,
  rw [mul_comm, ←mul_assoc, complex.exp_mul_I],
  refine ⟨if i * 2 ≤ n then i else i - n, _, _, _⟩,
  work_on_goal 2
  { replace hin := nat.is_coprime_iff_coprime.mpr hin,
    split_ifs with _,
    { exact hin },
    { convert hin.add_mul_left_left (-1),
      rw [mul_neg_one, sub_eq_add_neg] } },
  work_on_goal 2
  { split_ifs with h₂,
    { exact_mod_cast h },
    suffices : (i - n : ℤ).nat_abs = n - i,
    { rw this,
      apply tsub_lt_self hn.bot_lt,
      contrapose! h₂,
      rw [nat.eq_zero_of_le_zero h₂, zero_mul],
      exact zero_le _ },
    rw [←int.nat_abs_neg, neg_sub, int.nat_abs_eq_iff],
    exact or.inl (int.coe_nat_sub h.le).symm },
  split_ifs with h₂,
  { convert complex.arg_cos_add_sin_mul_I _,
    { push_cast },
    { push_cast },
    field_simp [hn],
    refine ⟨(neg_lt_neg real.pi_pos).trans_le _, _⟩,
    { rw neg_zero,
      exact mul_nonneg (mul_nonneg i.cast_nonneg $ by simp [real.pi_pos.le]) (by simp) },
    rw [←mul_rotate', mul_div_assoc],
    rw ←mul_one n at h₂,
    exact mul_le_of_le_one_right real.pi_pos.le
      ((div_le_iff' $ by exact_mod_cast (pos_of_gt h)).mpr $ by exact_mod_cast h₂) },
  rw [←complex.cos_sub_two_pi, ←complex.sin_sub_two_pi],
  convert complex.arg_cos_add_sin_mul_I _,
  { push_cast,
    rw [←sub_one_mul, sub_div, div_self],
    exact_mod_cast hn },
  { push_cast,
    rw [←sub_one_mul, sub_div, div_self],
    exact_mod_cast hn },
  field_simp [hn],
  refine ⟨_, le_trans _ real.pi_pos.le⟩,
  work_on_goal 2
  { rw [mul_div_assoc],
    exact mul_nonpos_of_nonpos_of_nonneg (sub_nonpos.mpr $ by exact_mod_cast h.le)
      (div_nonneg (by simp [real.pi_pos.le]) $ by simp) },
  rw [←mul_rotate', mul_div_assoc, neg_lt, ←mul_neg, mul_lt_iff_lt_one_right real.pi_pos,
      ←neg_div, ←neg_mul, neg_sub, div_lt_iff, one_mul, sub_mul, sub_lt_comm, ←mul_sub_one],
  norm_num,
  exact_mod_cast not_le.mp h₂,
  { exact (nat.cast_pos.mpr hn.bot_lt) }
end


/-
Copyright (c) 2021 Oliver Nash. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Oliver Nash
-/
import algebra.lie.abelian
import algebra.lie.solvable
import linear_algebra.dual

/-!
# Characters of Lie algebras

A character of a Lie algebra `L` over a commutative ring `R` is a morphism of Lie algebras `L → R`,
where `R` is regarded as a Lie algebra over itself via the ring commutator. For an Abelian Lie
algebra (e.g., a Cartan subalgebra of a semisimple Lie algebra) a character is just a linear form.

## Main definitions

  * `lie_algebra.lie_character`
  * `lie_algebra.lie_character_equiv_linear_dual`

## Tags

lie algebra, lie character
-/

universes u v w w₁

namespace lie_algebra

variables (R : Type u) (L : Type v) [comm_ring R] [lie_ring L] [lie_algebra R L]

/-- A character of a Lie algebra is a morphism to the scalars. -/
abbreviation lie_character := L →ₗ⁅R⁆ R

variables {R L}

@[simp] lemma lie_character_apply_lie (χ : lie_character R L) (x y : L) : χ ⁅x, y⁆ = 0 :=
by rw [lie_hom.map_lie, lie_ring.of_associative_ring_bracket, mul_comm, sub_self]

lemma lie_character_apply_of_mem_derived
  (χ : lie_character R L) {x : L} (h : x ∈ derived_series R L 1) : χ x = 0 :=
begin
  rw [derived_series_def, derived_series_of_ideal_succ, derived_series_of_ideal_zero,
    ← lie_submodule.mem_coe_submodule, lie_submodule.lie_ideal_oper_eq_linear_span] at h,
  apply submodule.span_induction h,
  { rintros y ⟨⟨z, hz⟩, ⟨⟨w, hw⟩, rfl⟩⟩, apply lie_character_apply_lie, },
  { exact χ.map_zero, },
  { intros y z hy hz, rw [lie_hom.map_add, hy, hz, add_zero], },
  { intros t y hy, rw [lie_hom.map_smul, hy, smul_zero], },
end

/-- For an Abelian Lie algebra, characters are just linear forms. -/
@[simps] def lie_character_equiv_linear_dual [is_lie_abelian L] :
  lie_character R L ≃ module.dual R L :=
{ to_fun    := λ χ, (χ : L →ₗ[R] R),
  inv_fun   := λ ψ,
  { map_lie' := λ x y, by
    rw [lie_module.is_trivial.trivial, lie_ring.of_associative_ring_bracket, mul_comm, sub_self,
      linear_map.to_fun_eq_coe, linear_map.map_zero],
    .. ψ, },
  left_inv  := λ χ, by { ext, refl, },
  right_inv := λ ψ, by { ext, refl, }, }

end lie_algebra


/-
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 order.succ_pred.limit
! leanprover-community/mathlib commit baba818b9acea366489e8ba32d2cc0fcaf50a1f7
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Order.SuccPred.Basic

/-!
# Successor and predecessor limits

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

We define the predicate `order.is_succ_limit` for "successor limits", values that don't cover any
others. They are so named since they can't be the successors of anything smaller. We define
`order.is_pred_limit` analogously, and prove basic results.

## Todo

The plan is to eventually replace `ordinal.is_limit` and `cardinal.is_limit` with the common
predicate `order.is_succ_limit`.
-/

variable {α : Type _}

namespace Order

open Function Set OrderDual

/-! ### Successor limits -/

section LT

variable [LT α]

#print Order.IsSuccLimit /-
/-- A successor limit is a value that doesn't cover any other.

It's so named because in a successor order, a successor limit can't be the successor of anything
smaller. -/
def IsSuccLimit (a : α) : Prop :=
  ∀ b, ¬b ⋖ a
#align order.is_succ_limit Order.IsSuccLimit
-/

#print Order.not_isSuccLimit_iff_exists_covby /-
theorem not_isSuccLimit_iff_exists_covby (a : α) : ¬IsSuccLimit a ↔ ∃ b, b ⋖ a := by
  simp [is_succ_limit]
#align order.not_is_succ_limit_iff_exists_covby Order.not_isSuccLimit_iff_exists_covby
-/

#print Order.isSuccLimit_of_dense /-
@[simp]
theorem isSuccLimit_of_dense [DenselyOrdered α] (a : α) : IsSuccLimit a := fun b => not_covby
#align order.is_succ_limit_of_dense Order.isSuccLimit_of_dense
-/

end LT

section Preorder

variable [Preorder α] {a : α}

#print Order.IsMin.isSuccLimit /-
protected theorem Order.IsMin.isSuccLimit : IsMin a → IsSuccLimit a := fun h b hab =>
  not_isMin_of_lt hab.lt h
#align is_min.is_succ_limit Order.IsMin.isSuccLimit
-/

/- warning: order.is_succ_limit_bot -> Order.isSuccLimit_bot is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : Preorder.{u1} α] [_inst_2 : OrderBot.{u1} α (Preorder.toLE.{u1} α _inst_1)], Order.IsSuccLimit.{u1} α (Preorder.toLT.{u1} α _inst_1) (Bot.bot.{u1} α (OrderBot.toHasBot.{u1} α (Preorder.toLE.{u1} α _inst_1) _inst_2))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : Preorder.{u1} α] [_inst_2 : OrderBot.{u1} α (Preorder.toLE.{u1} α _inst_1)], Order.IsSuccLimit.{u1} α (Preorder.toLT.{u1} α _inst_1) (Bot.bot.{u1} α (OrderBot.toBot.{u1} α (Preorder.toLE.{u1} α _inst_1) _inst_2))
Case conversion may be inaccurate. Consider using '#align order.is_succ_limit_bot Order.isSuccLimit_botₓ'. -/
theorem isSuccLimit_bot [OrderBot α] : IsSuccLimit (⊥ : α) :=
  isMin_bot.IsSuccLimit
#align order.is_succ_limit_bot Order.isSuccLimit_bot

variable [SuccOrder α]

#print Order.IsSuccLimit.isMax /-
protected theorem IsSuccLimit.isMax (h : IsSuccLimit (succ a)) : IsMax a :=
  by
  by_contra H
  exact h a (covby_succ_of_not_is_max H)
#align order.is_succ_limit.is_max Order.IsSuccLimit.isMax
-/

#print Order.not_isSuccLimit_succ_of_not_isMax /-
theorem not_isSuccLimit_succ_of_not_isMax (ha : ¬IsMax a) : ¬IsSuccLimit (succ a) :=
  by
  contrapose! ha
  exact ha.is_max
#align order.not_is_succ_limit_succ_of_not_is_max Order.not_isSuccLimit_succ_of_not_isMax
-/

section NoMaxOrder

variable [NoMaxOrder α]

#print Order.IsSuccLimit.succ_ne /-
theorem IsSuccLimit.succ_ne (h : IsSuccLimit a) (b : α) : succ b ≠ a :=
  by
  rintro rfl
  exact not_isMax _ h.is_max
#align order.is_succ_limit.succ_ne Order.IsSuccLimit.succ_ne
-/

#print Order.not_isSuccLimit_succ /-
@[simp]
theorem not_isSuccLimit_succ (a : α) : ¬IsSuccLimit (succ a) := fun h => h.succ_ne _ rfl
#align order.not_is_succ_limit_succ Order.not_isSuccLimit_succ
-/

end NoMaxOrder

section IsSuccArchimedean

variable [IsSuccArchimedean α]

#print Order.IsSuccLimit.isMin_of_noMax /-
theorem IsSuccLimit.isMin_of_noMax [NoMaxOrder α] (h : IsSuccLimit a) : IsMin a := fun b hb =>
  by
  rcases hb.exists_succ_iterate with ⟨_ | n, rfl⟩
  · exact le_rfl
  · rw [iterate_succ_apply'] at h
    exact (not_is_succ_limit_succ _ h).elim
#align order.is_succ_limit.is_min_of_no_max Order.IsSuccLimit.isMin_of_noMax
-/

#print Order.isSuccLimit_iff_of_noMax /-
@[simp]
theorem isSuccLimit_iff_of_noMax [NoMaxOrder α] : IsSuccLimit a ↔ IsMin a :=
  ⟨IsSuccLimit.isMin_of_noMax, Order.IsMin.isSuccLimit⟩
#align order.is_succ_limit_iff_of_no_max Order.isSuccLimit_iff_of_noMax
-/

#print Order.not_isSuccLimit_of_noMax /-
theorem not_isSuccLimit_of_noMax [NoMinOrder α] [NoMaxOrder α] : ¬IsSuccLimit a := by simp
#align order.not_is_succ_limit_of_no_max Order.not_isSuccLimit_of_noMax
-/

end IsSuccArchimedean

end Preorder

section PartialOrder

variable [PartialOrder α] [SuccOrder α] {a b : α} {C : α → Sort _}

#print Order.isSuccLimit_of_succ_ne /-
theorem isSuccLimit_of_succ_ne (h : ∀ b, succ b ≠ a) : IsSuccLimit a := fun b hba => h b hba.succ_eq
#align order.is_succ_limit_of_succ_ne Order.isSuccLimit_of_succ_ne
-/

#print Order.not_isSuccLimit_iff /-
theorem not_isSuccLimit_iff : ¬IsSuccLimit a ↔ ∃ b, ¬IsMax b ∧ succ b = a :=
  by
  rw [not_is_succ_limit_iff_exists_covby]
  refine' exists_congr fun b => ⟨fun hba => ⟨hba.lt.not_is_max, hba.succ_eq⟩, _⟩
  rintro ⟨h, rfl⟩
  exact covby_succ_of_not_is_max h
#align order.not_is_succ_limit_iff Order.not_isSuccLimit_iff
-/

#print Order.mem_range_succ_of_not_isSuccLimit /-
/-- See `not_is_succ_limit_iff` for a version that states that `a` is a successor of a value other
than itself. -/
theorem mem_range_succ_of_not_isSuccLimit (h : ¬IsSuccLimit a) : a ∈ range (@succ α _ _) :=
  by
  cases' not_is_succ_limit_iff.1 h with b hb
  exact ⟨b, hb.2⟩
#align order.mem_range_succ_of_not_is_succ_limit Order.mem_range_succ_of_not_isSuccLimit
-/

#print Order.isSuccLimit_of_succ_lt /-
theorem isSuccLimit_of_succ_lt (H : ∀ a < b, succ a < b) : IsSuccLimit b := fun a hab =>
  (H a hab.lt).Ne hab.succ_eq
#align order.is_succ_limit_of_succ_lt Order.isSuccLimit_of_succ_lt
-/

#print Order.IsSuccLimit.succ_lt /-
theorem IsSuccLimit.succ_lt (hb : IsSuccLimit b) (ha : a < b) : succ a < b :=
  by
  by_cases h : IsMax a
  · rwa [h.succ_eq]
  · rw [lt_iff_le_and_ne, succ_le_iff_of_not_is_max h]
    refine' ⟨ha, fun hab => _⟩
    subst hab
    exact (h hb.is_max).elim
#align order.is_succ_limit.succ_lt Order.IsSuccLimit.succ_lt
-/

#print Order.IsSuccLimit.succ_lt_iff /-
theorem IsSuccLimit.succ_lt_iff (hb : IsSuccLimit b) : succ a < b ↔ a < b :=
  ⟨fun h => (le_succ a).trans_lt h, hb.succ_lt⟩
#align order.is_succ_limit.succ_lt_iff Order.IsSuccLimit.succ_lt_iff
-/

#print Order.isSuccLimit_iff_succ_lt /-
theorem isSuccLimit_iff_succ_lt : IsSuccLimit b ↔ ∀ a < b, succ a < b :=
  ⟨fun hb a => hb.succ_lt, isSuccLimit_of_succ_lt⟩
#align order.is_succ_limit_iff_succ_lt Order.isSuccLimit_iff_succ_lt
-/

#print Order.isSuccLimitRecOn /-
/-- A value can be built by building it on successors and successor limits. -/
@[elab_as_elim]
noncomputable def isSuccLimitRecOn (b : α) (hs : ∀ a, ¬IsMax a → C (succ a))
    (hl : ∀ a, IsSuccLimit a → C a) : C b :=
  by
  by_cases hb : is_succ_limit b
  · exact hl b hb
  · have H := Classical.choose_spec (not_is_succ_limit_iff.1 hb)
    rw [← H.2]
    exact hs _ H.1
#align order.is_succ_limit_rec_on Order.isSuccLimitRecOn
-/

/- warning: order.is_succ_limit_rec_on_limit -> Order.isSuccLimitRecOn_limit is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : PartialOrder.{u1} α] [_inst_2 : SuccOrder.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1)] {b : α} {C : α -> Sort.{u2}} (hs : forall (a : α), (Not (IsMax.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1)) a)) -> (C (Order.succ.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1) _inst_2 a))) (hl : forall (a : α), (Order.IsSuccLimit.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1)) a) -> (C a)) (hb : Order.IsSuccLimit.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1)) b), Eq.{u2} (C b) (Order.isSuccLimitRecOn.{u1, u2} α _inst_1 _inst_2 C b hs hl) (hl b hb)
but is expected to have type
  forall {α : Type.{u2}} [_inst_1 : PartialOrder.{u2} α] [_inst_2 : SuccOrder.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1)] {b : α} {C : α -> Sort.{u1}} (hs : forall (a : α), (Not (IsMax.{u2} α (Preorder.toLE.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1)) a)) -> (C (Order.succ.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1) _inst_2 a))) (hl : forall (a : α), (Order.IsSuccLimit.{u2} α (Preorder.toLT.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1)) a) -> (C a)) (hb : Order.IsSuccLimit.{u2} α (Preorder.toLT.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1)) b), Eq.{u1} (C b) (Order.isSuccLimitRecOn.{u2, u1} α _inst_1 _inst_2 C b hs hl) (hl b hb)
Case conversion may be inaccurate. Consider using '#align order.is_succ_limit_rec_on_limit Order.isSuccLimitRecOn_limitₓ'. -/
theorem isSuccLimitRecOn_limit (hs : ∀ a, ¬IsMax a → C (succ a)) (hl : ∀ a, IsSuccLimit a → C a)
    (hb : IsSuccLimit b) : @isSuccLimitRecOn α _ _ C b hs hl = hl b hb := by
  classical exact dif_pos hb
#align order.is_succ_limit_rec_on_limit Order.isSuccLimitRecOn_limit

/- warning: order.is_succ_limit_rec_on_succ' -> Order.isSuccLimitRecOn_succ' is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : PartialOrder.{u1} α] [_inst_2 : SuccOrder.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1)] {C : α -> Sort.{u2}} (hs : forall (a : α), (Not (IsMax.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1)) a)) -> (C (Order.succ.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1) _inst_2 a))) (hl : forall (a : α), (Order.IsSuccLimit.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1)) a) -> (C a)) {b : α} (hb : Not (IsMax.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1)) b)), Eq.{u2} (C (Order.succ.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1) _inst_2 b)) (Order.isSuccLimitRecOn.{u1, u2} α _inst_1 _inst_2 C (Order.succ.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1) _inst_2 b) hs hl) (hs b hb)
but is expected to have type
  forall {α : Type.{u2}} [_inst_1 : PartialOrder.{u2} α] [_inst_2 : SuccOrder.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1)] {C : α -> Sort.{u1}} (hs : forall (a : α), (Not (IsMax.{u2} α (Preorder.toLE.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1)) a)) -> (C (Order.succ.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1) _inst_2 a))) (hl : forall (a : α), (Order.IsSuccLimit.{u2} α (Preorder.toLT.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1)) a) -> (C a)) {b : α} (hb : Not (IsMax.{u2} α (Preorder.toLE.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1)) b)), Eq.{u1} (C (Order.succ.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1) _inst_2 b)) (Order.isSuccLimitRecOn.{u2, u1} α _inst_1 _inst_2 C (Order.succ.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1) _inst_2 b) hs hl) (hs b hb)
Case conversion may be inaccurate. Consider using '#align order.is_succ_limit_rec_on_succ' Order.isSuccLimitRecOn_succ'ₓ'. -/
theorem isSuccLimitRecOn_succ' (hs : ∀ a, ¬IsMax a → C (succ a)) (hl : ∀ a, IsSuccLimit a → C a)
    {b : α} (hb : ¬IsMax b) : @isSuccLimitRecOn α _ _ C (succ b) hs hl = hs b hb :=
  by
  have hb' := not_is_succ_limit_succ_of_not_is_max hb
  have H := Classical.choose_spec (not_is_succ_limit_iff.1 hb')
  rw [is_succ_limit_rec_on]
  simp only [cast_eq_iff_heq, hb', not_false_iff, eq_mpr_eq_cast, dif_neg]
  congr
  · exact (succ_eq_succ_iff_of_not_is_max H.1 hb).1 H.2
  · apply proof_irrel_heq
#align order.is_succ_limit_rec_on_succ' Order.isSuccLimitRecOn_succ'

section NoMaxOrder

variable [NoMaxOrder α]

/- warning: order.is_succ_limit_rec_on_succ -> Order.isSuccLimitRecOn_succ is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : PartialOrder.{u1} α] [_inst_2 : SuccOrder.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1)] {C : α -> Sort.{u2}} [_inst_3 : NoMaxOrder.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1))] (hs : forall (a : α), (Not (IsMax.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1)) a)) -> (C (Order.succ.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1) _inst_2 a))) (hl : forall (a : α), (Order.IsSuccLimit.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1)) a) -> (C a)) (b : α), Eq.{u2} (C (Order.succ.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1) _inst_2 b)) (Order.isSuccLimitRecOn.{u1, u2} α _inst_1 _inst_2 C (Order.succ.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1) _inst_2 b) hs hl) (hs b (not_isMax.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1) _inst_3 b))
but is expected to have type
  forall {α : Type.{u2}} [_inst_1 : PartialOrder.{u2} α] [_inst_2 : SuccOrder.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1)] {C : α -> Sort.{u1}} [_inst_3 : NoMaxOrder.{u2} α (Preorder.toLT.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1))] (hs : forall (a : α), (Not (IsMax.{u2} α (Preorder.toLE.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1)) a)) -> (C (Order.succ.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1) _inst_2 a))) (hl : forall (a : α), (Order.IsSuccLimit.{u2} α (Preorder.toLT.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1)) a) -> (C a)) (b : α), Eq.{u1} (C (Order.succ.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1) _inst_2 b)) (Order.isSuccLimitRecOn.{u2, u1} α _inst_1 _inst_2 C (Order.succ.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1) _inst_2 b) hs hl) (hs b (not_isMax.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1) _inst_3 b))
Case conversion may be inaccurate. Consider using '#align order.is_succ_limit_rec_on_succ Order.isSuccLimitRecOn_succₓ'. -/
@[simp]
theorem isSuccLimitRecOn_succ (hs : ∀ a, ¬IsMax a → C (succ a)) (hl : ∀ a, IsSuccLimit a → C a)
    (b : α) : @isSuccLimitRecOn α _ _ C (succ b) hs hl = hs b (not_isMax b) :=
  isSuccLimitRecOn_succ' _ _ _
#align order.is_succ_limit_rec_on_succ Order.isSuccLimitRecOn_succ

#print Order.isSuccLimit_iff_succ_ne /-
theorem isSuccLimit_iff_succ_ne : IsSuccLimit a ↔ ∀ b, succ b ≠ a :=
  ⟨IsSuccLimit.succ_ne, isSuccLimit_of_succ_ne⟩
#align order.is_succ_limit_iff_succ_ne Order.isSuccLimit_iff_succ_ne
-/

#print Order.not_isSuccLimit_iff' /-
theorem not_isSuccLimit_iff' : ¬IsSuccLimit a ↔ a ∈ range (@succ α _ _) :=
  by
  simp_rw [is_succ_limit_iff_succ_ne, not_forall, not_ne_iff]
  rfl
#align order.not_is_succ_limit_iff' Order.not_isSuccLimit_iff'
-/

end NoMaxOrder

section IsSuccArchimedean

variable [IsSuccArchimedean α]

#print Order.IsSuccLimit.isMin /-
protected theorem IsSuccLimit.isMin (h : IsSuccLimit a) : IsMin a := fun b hb =>
  by
  revert h
  refine' Succ.rec (fun _ => le_rfl) (fun c hbc H hc => _) hb
  have := hc.is_max.succ_eq
  rw [this] at hc⊢
  exact H hc
#align order.is_succ_limit.is_min Order.IsSuccLimit.isMin
-/

#print Order.isSuccLimit_iff /-
@[simp]
theorem isSuccLimit_iff : IsSuccLimit a ↔ IsMin a :=
  ⟨IsSuccLimit.isMin, Order.IsMin.isSuccLimit⟩
#align order.is_succ_limit_iff Order.isSuccLimit_iff
-/

#print Order.not_isSuccLimit /-
theorem not_isSuccLimit [NoMinOrder α] : ¬IsSuccLimit a := by simp
#align order.not_is_succ_limit Order.not_isSuccLimit
-/

end IsSuccArchimedean

end PartialOrder

/-! ### Predecessor limits -/

section LT

variable [LT α] {a : α}

#print Order.IsPredLimit /-
/-- A predecessor limit is a value that isn't covered by any other.

It's so named because in a predecessor order, a predecessor limit can't be the predecessor of
anything greater. -/
def IsPredLimit (a : α) : Prop :=
  ∀ b, ¬a ⋖ b
#align order.is_pred_limit Order.IsPredLimit
-/

#print Order.not_isPredLimit_iff_exists_covby /-
theorem not_isPredLimit_iff_exists_covby (a : α) : ¬IsPredLimit a ↔ ∃ b, a ⋖ b := by
  simp [is_pred_limit]
#align order.not_is_pred_limit_iff_exists_covby Order.not_isPredLimit_iff_exists_covby
-/

#print Order.isPredLimit_of_dense /-
theorem isPredLimit_of_dense [DenselyOrdered α] (a : α) : IsPredLimit a := fun b => not_covby
#align order.is_pred_limit_of_dense Order.isPredLimit_of_dense
-/

#print Order.isSuccLimit_toDual_iff /-
@[simp]
theorem isSuccLimit_toDual_iff : IsSuccLimit (toDual a) ↔ IsPredLimit a := by
  simp [is_succ_limit, is_pred_limit]
#align order.is_succ_limit_to_dual_iff Order.isSuccLimit_toDual_iff
-/

#print Order.isPredLimit_toDual_iff /-
@[simp]
theorem isPredLimit_toDual_iff : IsPredLimit (toDual a) ↔ IsSuccLimit a := by
  simp [is_succ_limit, is_pred_limit]
#align order.is_pred_limit_to_dual_iff Order.isPredLimit_toDual_iff
-/

alias is_succ_limit_to_dual_iff ↔ _ is_pred_limit.dual
#align order.is_pred_limit.dual Order.isPredLimit.dual

alias is_pred_limit_to_dual_iff ↔ _ is_succ_limit.dual
#align order.is_succ_limit.dual Order.isSuccLimit.dual

end LT

section Preorder

variable [Preorder α] {a : α}

#print Order.IsMax.isPredLimit /-
protected theorem Order.IsMax.isPredLimit : IsMax a → IsPredLimit a := fun h b hab =>
  not_isMax_of_lt hab.lt h
#align is_max.is_pred_limit Order.IsMax.isPredLimit
-/

/- warning: order.is_pred_limit_top -> Order.isPredLimit_top is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : Preorder.{u1} α] [_inst_2 : OrderTop.{u1} α (Preorder.toLE.{u1} α _inst_1)], Order.IsPredLimit.{u1} α (Preorder.toLT.{u1} α _inst_1) (Top.top.{u1} α (OrderTop.toHasTop.{u1} α (Preorder.toLE.{u1} α _inst_1) _inst_2))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : Preorder.{u1} α] [_inst_2 : OrderTop.{u1} α (Preorder.toLE.{u1} α _inst_1)], Order.IsPredLimit.{u1} α (Preorder.toLT.{u1} α _inst_1) (Top.top.{u1} α (OrderTop.toTop.{u1} α (Preorder.toLE.{u1} α _inst_1) _inst_2))
Case conversion may be inaccurate. Consider using '#align order.is_pred_limit_top Order.isPredLimit_topₓ'. -/
theorem isPredLimit_top [OrderTop α] : IsPredLimit (⊤ : α) :=
  isMax_top.IsPredLimit
#align order.is_pred_limit_top Order.isPredLimit_top

variable [PredOrder α]

#print Order.IsPredLimit.isMin /-
protected theorem IsPredLimit.isMin (h : IsPredLimit (pred a)) : IsMin a :=
  by
  by_contra H
  exact h a (pred_covby_of_not_is_min H)
#align order.is_pred_limit.is_min Order.IsPredLimit.isMin
-/

#print Order.not_isPredLimit_pred_of_not_isMin /-
theorem not_isPredLimit_pred_of_not_isMin (ha : ¬IsMin a) : ¬IsPredLimit (pred a) :=
  by
  contrapose! ha
  exact ha.is_min
#align order.not_is_pred_limit_pred_of_not_is_min Order.not_isPredLimit_pred_of_not_isMin
-/

section NoMinOrder

variable [NoMinOrder α]

#print Order.IsPredLimit.pred_ne /-
theorem IsPredLimit.pred_ne (h : IsPredLimit a) (b : α) : pred b ≠ a :=
  by
  rintro rfl
  exact not_isMin _ h.is_min
#align order.is_pred_limit.pred_ne Order.IsPredLimit.pred_ne
-/

#print Order.not_isPredLimit_pred /-
@[simp]
theorem not_isPredLimit_pred (a : α) : ¬IsPredLimit (pred a) := fun h => h.pred_ne _ rfl
#align order.not_is_pred_limit_pred Order.not_isPredLimit_pred
-/

end NoMinOrder

section IsPredArchimedean

variable [IsPredArchimedean α]

#print Order.IsPredLimit.isMax_of_noMin /-
protected theorem IsPredLimit.isMax_of_noMin [NoMinOrder α] (h : IsPredLimit a) : IsMax a :=
  h.dual.isMin_of_noMax
#align order.is_pred_limit.is_max_of_no_min Order.IsPredLimit.isMax_of_noMin
-/

#print Order.isPredLimit_iff_of_noMin /-
@[simp]
theorem isPredLimit_iff_of_noMin [NoMinOrder α] : IsPredLimit a ↔ IsMax a :=
  isSuccLimit_toDual_iff.symm.trans isSuccLimit_iff_of_noMax
#align order.is_pred_limit_iff_of_no_min Order.isPredLimit_iff_of_noMin
-/

#print Order.not_isPredLimit_of_noMin /-
theorem not_isPredLimit_of_noMin [NoMinOrder α] [NoMaxOrder α] : ¬IsPredLimit a := by simp
#align order.not_is_pred_limit_of_no_min Order.not_isPredLimit_of_noMin
-/

end IsPredArchimedean

end Preorder

section PartialOrder

variable [PartialOrder α] [PredOrder α] {a b : α} {C : α → Sort _}

#print Order.isPredLimit_of_pred_ne /-
theorem isPredLimit_of_pred_ne (h : ∀ b, pred b ≠ a) : IsPredLimit a := fun b hba => h b hba.pred_eq
#align order.is_pred_limit_of_pred_ne Order.isPredLimit_of_pred_ne
-/

#print Order.not_isPredLimit_iff /-
theorem not_isPredLimit_iff : ¬IsPredLimit a ↔ ∃ b, ¬IsMin b ∧ pred b = a :=
  by
  rw [← is_succ_limit_to_dual_iff]
  exact not_is_succ_limit_iff
#align order.not_is_pred_limit_iff Order.not_isPredLimit_iff
-/

#print Order.mem_range_pred_of_not_isPredLimit /-
/-- See `not_is_pred_limit_iff` for a version that states that `a` is a successor of a value other
than itself. -/
theorem mem_range_pred_of_not_isPredLimit (h : ¬IsPredLimit a) : a ∈ range (@pred α _ _) :=
  by
  cases' not_is_pred_limit_iff.1 h with b hb
  exact ⟨b, hb.2⟩
#align order.mem_range_pred_of_not_is_pred_limit Order.mem_range_pred_of_not_isPredLimit
-/

#print Order.isPredLimit_of_pred_lt /-
theorem isPredLimit_of_pred_lt (H : ∀ a > b, pred a < b) : IsPredLimit b := fun a hab =>
  (H a hab.lt).Ne hab.pred_eq
#align order.is_pred_limit_of_pred_lt Order.isPredLimit_of_pred_lt
-/

#print Order.IsPredLimit.lt_pred /-
theorem IsPredLimit.lt_pred (h : IsPredLimit a) : a < b → a < pred b :=
  h.dual.succ_lt
#align order.is_pred_limit.lt_pred Order.IsPredLimit.lt_pred
-/

#print Order.IsPredLimit.lt_pred_iff /-
theorem IsPredLimit.lt_pred_iff (h : IsPredLimit a) : a < pred b ↔ a < b :=
  h.dual.succ_lt_iff
#align order.is_pred_limit.lt_pred_iff Order.IsPredLimit.lt_pred_iff
-/

#print Order.isPredLimit_iff_lt_pred /-
theorem isPredLimit_iff_lt_pred : IsPredLimit a ↔ ∀ ⦃b⦄, a < b → a < pred b :=
  isSuccLimit_toDual_iff.symm.trans isSuccLimit_iff_succ_lt
#align order.is_pred_limit_iff_lt_pred Order.isPredLimit_iff_lt_pred
-/

#print Order.isPredLimitRecOn /-
/-- A value can be built by building it on predecessors and predecessor limits. -/
@[elab_as_elim]
noncomputable def isPredLimitRecOn (b : α) (hs : ∀ a, ¬IsMin a → C (pred a))
    (hl : ∀ a, IsPredLimit a → C a) : C b :=
  @isSuccLimitRecOn αᵒᵈ _ _ _ _ hs fun a ha => hl _ ha.dual
#align order.is_pred_limit_rec_on Order.isPredLimitRecOn
-/

/- warning: order.is_pred_limit_rec_on_limit -> Order.isPredLimitRecOn_limit is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : PartialOrder.{u1} α] [_inst_2 : PredOrder.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1)] {b : α} {C : α -> Sort.{u2}} (hs : forall (a : α), (Not (IsMin.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1)) a)) -> (C (Order.pred.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1) _inst_2 a))) (hl : forall (a : α), (Order.IsPredLimit.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1)) a) -> (C a)) (hb : Order.IsPredLimit.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1)) b), Eq.{u2} (C b) (Order.isPredLimitRecOn.{u1, u2} α _inst_1 _inst_2 C b hs hl) (hl b hb)
but is expected to have type
  forall {α : Type.{u2}} [_inst_1 : PartialOrder.{u2} α] [_inst_2 : PredOrder.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1)] {b : α} {C : α -> Sort.{u1}} (hs : forall (a : α), (Not (IsMin.{u2} α (Preorder.toLE.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1)) a)) -> (C (Order.pred.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1) _inst_2 a))) (hl : forall (a : α), (Order.IsPredLimit.{u2} α (Preorder.toLT.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1)) a) -> (C a)) (hb : Order.IsPredLimit.{u2} α (Preorder.toLT.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1)) b), Eq.{u1} (C b) (Order.isPredLimitRecOn.{u2, u1} α _inst_1 _inst_2 C b hs hl) (hl b hb)
Case conversion may be inaccurate. Consider using '#align order.is_pred_limit_rec_on_limit Order.isPredLimitRecOn_limitₓ'. -/
theorem isPredLimitRecOn_limit (hs : ∀ a, ¬IsMin a → C (pred a)) (hl : ∀ a, IsPredLimit a → C a)
    (hb : IsPredLimit b) : @isPredLimitRecOn α _ _ C b hs hl = hl b hb :=
  isSuccLimitRecOn_limit _ _ hb.dual
#align order.is_pred_limit_rec_on_limit Order.isPredLimitRecOn_limit

/- warning: order.is_pred_limit_rec_on_pred' -> Order.isPredLimitRecOn_pred' is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : PartialOrder.{u1} α] [_inst_2 : PredOrder.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1)] {C : α -> Sort.{u2}} (hs : forall (a : α), (Not (IsMin.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1)) a)) -> (C (Order.pred.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1) _inst_2 a))) (hl : forall (a : α), (Order.IsPredLimit.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1)) a) -> (C a)) {b : α} (hb : Not (IsMin.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1)) b)), Eq.{u2} (C (Order.pred.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1) _inst_2 b)) (Order.isPredLimitRecOn.{u1, u2} α _inst_1 _inst_2 C (Order.pred.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1) _inst_2 b) hs hl) (hs b hb)
but is expected to have type
  forall {α : Type.{u2}} [_inst_1 : PartialOrder.{u2} α] [_inst_2 : PredOrder.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1)] {C : α -> Sort.{u1}} (hs : forall (a : α), (Not (IsMin.{u2} α (Preorder.toLE.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1)) a)) -> (C (Order.pred.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1) _inst_2 a))) (hl : forall (a : α), (Order.IsPredLimit.{u2} α (Preorder.toLT.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1)) a) -> (C a)) {b : α} (hb : Not (IsMin.{u2} α (Preorder.toLE.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1)) b)), Eq.{u1} (C (Order.pred.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1) _inst_2 b)) (Order.isPredLimitRecOn.{u2, u1} α _inst_1 _inst_2 C (Order.pred.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1) _inst_2 b) hs hl) (hs b hb)
Case conversion may be inaccurate. Consider using '#align order.is_pred_limit_rec_on_pred' Order.isPredLimitRecOn_pred'ₓ'. -/
theorem isPredLimitRecOn_pred' (hs : ∀ a, ¬IsMin a → C (pred a)) (hl : ∀ a, IsPredLimit a → C a)
    {b : α} (hb : ¬IsMin b) : @isPredLimitRecOn α _ _ C (pred b) hs hl = hs b hb :=
  isSuccLimitRecOn_succ' _ _ _
#align order.is_pred_limit_rec_on_pred' Order.isPredLimitRecOn_pred'

section NoMinOrder

variable [NoMinOrder α]

/- warning: order.is_pred_limit_rec_on_pred -> Order.isPredLimitRecOn_pred is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : PartialOrder.{u1} α] [_inst_2 : PredOrder.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1)] {C : α -> Sort.{u2}} [_inst_3 : NoMinOrder.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1))] (hs : forall (a : α), (Not (IsMin.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1)) a)) -> (C (Order.pred.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1) _inst_2 a))) (hl : forall (a : α), (Order.IsPredLimit.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1)) a) -> (C a)) (b : α), Eq.{u2} (C (Order.pred.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1) _inst_2 b)) (Order.isPredLimitRecOn.{u1, u2} α _inst_1 _inst_2 C (Order.pred.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1) _inst_2 b) hs hl) (hs b (not_isMin.{u1} α (PartialOrder.toPreorder.{u1} α _inst_1) _inst_3 b))
but is expected to have type
  forall {α : Type.{u2}} [_inst_1 : PartialOrder.{u2} α] [_inst_2 : PredOrder.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1)] {C : α -> Sort.{u1}} [_inst_3 : NoMinOrder.{u2} α (Preorder.toLT.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1))] (hs : forall (a : α), (Not (IsMin.{u2} α (Preorder.toLE.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1)) a)) -> (C (Order.pred.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1) _inst_2 a))) (hl : forall (a : α), (Order.IsPredLimit.{u2} α (Preorder.toLT.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1)) a) -> (C a)) (b : α), Eq.{u1} (C (Order.pred.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1) _inst_2 b)) (Order.isPredLimitRecOn.{u2, u1} α _inst_1 _inst_2 C (Order.pred.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1) _inst_2 b) hs hl) (hs b (not_isMin.{u2} α (PartialOrder.toPreorder.{u2} α _inst_1) _inst_3 b))
Case conversion may be inaccurate. Consider using '#align order.is_pred_limit_rec_on_pred Order.isPredLimitRecOn_predₓ'. -/
@[simp]
theorem isPredLimitRecOn_pred (hs : ∀ a, ¬IsMin a → C (pred a)) (hl : ∀ a, IsPredLimit a → C a)
    (b : α) : @isPredLimitRecOn α _ _ C (pred b) hs hl = hs b (not_isMin b) :=
  isSuccLimitRecOn_succ _ _ _
#align order.is_pred_limit_rec_on_pred Order.isPredLimitRecOn_pred

end NoMinOrder

section IsPredArchimedean

variable [IsPredArchimedean α]

#print Order.IsPredLimit.isMax /-
protected theorem IsPredLimit.isMax (h : IsPredLimit a) : IsMax a :=
  h.dual.IsMin
#align order.is_pred_limit.is_max Order.IsPredLimit.isMax
-/

#print Order.isPredLimit_iff /-
@[simp]
theorem isPredLimit_iff : IsPredLimit a ↔ IsMax a :=
  isSuccLimit_toDual_iff.symm.trans isSuccLimit_iff
#align order.is_pred_limit_iff Order.isPredLimit_iff
-/

#print Order.not_isPredLimit /-
theorem not_isPredLimit [NoMaxOrder α] : ¬IsPredLimit a := by simp
#align order.not_is_pred_limit Order.not_isPredLimit
-/

end IsPredArchimedean

end PartialOrder

end Order