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

! This file was ported from Lean 3 source module number_theory.cyclotomic.discriminant
! leanprover-community/mathlib commit 8631e2d5ea77f6c13054d9151d82b83069680cb1
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.NumberTheory.Cyclotomic.PrimitiveRoots
import Mathbin.RingTheory.Discriminant

/-!
# Discriminant of cyclotomic fields
We compute the discriminant of a `p ^ n`-th cyclotomic extension.

## Main results
* `is_cyclotomic_extension.discr_odd_prime` : if `p` is an odd prime such that
  `is_cyclotomic_extension {p} K L` and `irreducible (cyclotomic p K)`, then
  `discr K (hζ.power_basis K).basis = (-1) ^ ((p - 1) / 2) * p ^ (p - 2)` for any
  `hζ : is_primitive_root ζ p`.

-/

universe u v

open Algebra Polynomial Nat IsPrimitiveRoot PowerBasis

open Polynomial Cyclotomic

namespace IsPrimitiveRoot

variable {n : ℕ+} {K : Type u} [Field K] [CharZero K] {ζ : K}

variable [IsCyclotomicExtension {n} ℚ K]

/-- The discriminant of the power basis given by a primitive root of unity `ζ` is the same as the
discriminant of the power basis given by `ζ - 1`. -/
theorem discr_zeta_eq_discr_zeta_sub_one (hζ : IsPrimitiveRoot ζ n) :
    discr ℚ (hζ.PowerBasis ℚ).basis = discr ℚ (hζ.subOnePowerBasis ℚ).basis :=
  by
  haveI : NumberField K := NumberField.mk
  have H₁ : (aeval (hζ.power_basis ℚ).gen) (X - 1 : ℤ[X]) = (hζ.sub_one_power_basis ℚ).gen := by
    simp
  have H₂ : (aeval (hζ.sub_one_power_basis ℚ).gen) (X + 1 : ℤ[X]) = (hζ.power_basis ℚ).gen := by
    simp
  refine'
    discr_eq_discr_of_to_matrix_coeff_is_integral _ (fun i j => to_matrix_is_integral H₁ _ _ _ _)
      fun i j => to_matrix_is_integral H₂ _ _ _ _
  · exact hζ.is_integral n.pos
  · refine' minpoly.isIntegrallyClosed_eq_field_fractions' _ (hζ.is_integral n.pos)
  · exact isIntegral_sub (hζ.is_integral n.pos) isIntegral_one
  · refine' minpoly.isIntegrallyClosed_eq_field_fractions' _ _
    exact isIntegral_sub (hζ.is_integral n.pos) isIntegral_one
#align is_primitive_root.discr_zeta_eq_discr_zeta_sub_one IsPrimitiveRoot.discr_zeta_eq_discr_zeta_sub_one

end IsPrimitiveRoot

namespace IsCyclotomicExtension

variable {p : ℕ+} {k : ℕ} {K : Type u} {L : Type v} {ζ : L} [Field K] [Field L]

variable [Algebra K L]

/-- If `p` is a prime and `is_cyclotomic_extension {p ^ (k + 1)} K L`, then the discriminant of
`hζ.power_basis K` is `(-1) ^ ((p ^ (k + 1).totient) / 2) * p ^ (p ^ k * ((p - 1) * (k + 1) - 1))`
if `irreducible (cyclotomic (p ^ (k + 1)) K))`, and `p ^ (k + 1) ≠ 2`. -/
theorem discr_prime_pow_ne_two [IsCyclotomicExtension {p ^ (k + 1)} K L] [hp : Fact (p : ℕ).Prime]
    (hζ : IsPrimitiveRoot ζ ↑(p ^ (k + 1))) (hirr : Irreducible (cyclotomic (↑(p ^ (k + 1)) : ℕ) K))
    (hk : p ^ (k + 1) ≠ 2) :
    discr K (hζ.PowerBasis K).basis =
      (-1) ^ ((p ^ (k + 1) : ℕ).totient / 2) * p ^ ((p : ℕ) ^ k * ((p - 1) * (k + 1) - 1)) :=
  by
  haveI hne := IsCyclotomicExtension.ne_zero' (p ^ (k + 1)) K L
  rw [discr_power_basis_eq_norm, finrank L hirr, hζ.power_basis_gen _, ←
    hζ.minpoly_eq_cyclotomic_of_irreducible hirr, PNat.pow_coe,
    totient_prime_pow hp.out (succ_pos k), succ_sub_one]
  have hp2 : p = 2 → k ≠ 0 := by
    rintro rfl rfl
    exact absurd rfl hk
  congr 1
  · rcases eq_or_ne p 2 with (rfl | hp2)
    · rcases Nat.exists_eq_succ_of_ne_zero (hp2 rfl) with ⟨k, rfl⟩
      rw [PNat.coe_bit0, PNat.one_coe, succ_sub_succ_eq_sub, tsub_zero, mul_one, pow_succ,
        mul_assoc, Nat.mul_div_cancel_left _ zero_lt_two, Nat.mul_div_cancel_left _ zero_lt_two]
      cases k
      · simp
      ·
        rw [pow_succ, (even_two.mul_right _).neg_one_pow,
          ((even_two.mul_right _).mul_right _).neg_one_pow]
    · replace hp2 : (p : ℕ) ≠ 2
      · rwa [Ne.def, ← PNat.one_coe, ← PNat.coe_bit0, PNat.coe_inj]
      have hpo : Odd (p : ℕ) := hp.out.odd_of_ne_two hp2
      obtain ⟨a, ha⟩ := (hp.out.even_sub_one hp2).two_dvd
      rw [ha, mul_left_comm, mul_assoc, Nat.mul_div_cancel_left _ two_pos,
        Nat.mul_div_cancel_left _ two_pos, mul_right_comm, pow_mul, (hpo.pow.mul _).neg_one_pow,
        pow_mul, hpo.pow.neg_one_pow]
      refine' Nat.Even.sub_odd _ (even_two_mul _) odd_one
      rw [mul_left_comm, ← ha]
      exact one_le_mul (one_le_pow _ _ hp.1.Pos) (succ_le_iff.2 <| tsub_pos_of_lt hp.1.one_lt)
  · have H := congr_arg derivative (cyclotomic_prime_pow_mul_X_pow_sub_one K p k)
    rw [derivative_mul, derivative_sub, derivative_one, sub_zero, derivative_X_pow, C_eq_nat_cast,
      derivative_sub, derivative_one, sub_zero, derivative_X_pow, C_eq_nat_cast, ← PNat.pow_coe,
      hζ.minpoly_eq_cyclotomic_of_irreducible hirr] at H
    replace H := congr_arg (fun P => aeval ζ P) H
    simp only [aeval_add, aeval_mul, minpoly.aeval, MulZeroClass.zero_mul, add_zero, aeval_nat_cast,
      _root_.map_sub, aeval_one, aeval_X_pow] at H
    replace H := congr_arg (Algebra.norm K) H
    have hnorm : (norm K) (ζ ^ (p : ℕ) ^ k - 1) = p ^ (p : ℕ) ^ k :=
      by
      by_cases hp : p = 2
      · exact hζ.pow_sub_one_norm_prime_pow_of_ne_zero hirr le_rfl (hp2 hp)
      · exact hζ.pow_sub_one_norm_prime_ne_two hirr le_rfl hp
    rw [MonoidHom.map_mul, hnorm, MonoidHom.map_mul, ← map_natCast (algebraMap K L),
      Algebra.norm_algebraMap, finrank L hirr, PNat.pow_coe, totient_prime_pow hp.out (succ_pos k),
      Nat.sub_one, Nat.pred_succ, ← hζ.minpoly_eq_cyclotomic_of_irreducible hirr, map_pow,
      hζ.norm_eq_one hk hirr, one_pow, mul_one, cast_pow, ← coe_coe, ← pow_mul, ← mul_assoc,
      mul_comm (k + 1), mul_assoc] at H
    · have := mul_pos (succ_pos k) (tsub_pos_of_lt hp.out.one_lt)
      rw [← succ_pred_eq_of_pos this, mul_succ, pow_add _ _ ((p : ℕ) ^ k)] at H
      replace H := (mul_left_inj' fun h => _).1 H
      · simpa only [← PNat.pow_coe, H, mul_comm _ (k + 1)]
      · replace h := pow_eq_zero h
        rw [coe_coe] at h
        simpa using hne.1
#align is_cyclotomic_extension.discr_prime_pow_ne_two IsCyclotomicExtension.discr_prime_pow_ne_two

/-- If `p` is a prime and `is_cyclotomic_extension {p ^ (k + 1)} K L`, then the discriminant of
`hζ.power_basis K` is `(-1) ^ (p ^ k * (p - 1) / 2) * p ^ (p ^ k * ((p - 1) * (k + 1) - 1))`
if `irreducible (cyclotomic (p ^ (k + 1)) K))`, and `p ^ (k + 1) ≠ 2`. -/
theorem discr_prime_pow_ne_two' [IsCyclotomicExtension {p ^ (k + 1)} K L] [hp : Fact (p : ℕ).Prime]
    (hζ : IsPrimitiveRoot ζ ↑(p ^ (k + 1))) (hirr : Irreducible (cyclotomic (↑(p ^ (k + 1)) : ℕ) K))
    (hk : p ^ (k + 1) ≠ 2) :
    discr K (hζ.PowerBasis K).basis =
      (-1) ^ ((p : ℕ) ^ k * (p - 1) / 2) * p ^ ((p : ℕ) ^ k * ((p - 1) * (k + 1) - 1)) :=
  by simpa [totient_prime_pow hp.out (succ_pos k)] using discr_prime_pow_ne_two hζ hirr hk
#align is_cyclotomic_extension.discr_prime_pow_ne_two' IsCyclotomicExtension.discr_prime_pow_ne_two'

/-- If `p` is a prime and `is_cyclotomic_extension {p ^ k} K L`, then the discriminant of
`hζ.power_basis K` is `(-1) ^ ((p ^ k).totient / 2) * p ^ (p ^ (k - 1) * ((p - 1) * k - 1))`
if `irreducible (cyclotomic (p ^ k) K))`. Beware that in the cases `p ^ k = 1` and `p ^ k = 2`
the formula uses `1 / 2 = 0` and `0 - 1 = 0`. It is useful only to have a uniform result.
See also `is_cyclotomic_extension.discr_prime_pow_eq_unit_mul_pow`. -/
theorem discr_prime_pow [hcycl : IsCyclotomicExtension {p ^ k} K L] [hp : Fact (p : ℕ).Prime]
    (hζ : IsPrimitiveRoot ζ ↑(p ^ k)) (hirr : Irreducible (cyclotomic (↑(p ^ k) : ℕ) K)) :
    discr K (hζ.PowerBasis K).basis =
      (-1) ^ ((p ^ k : ℕ).totient / 2) * p ^ ((p : ℕ) ^ (k - 1) * ((p - 1) * k - 1)) :=
  by
  cases k
  · simp only [coe_basis, pow_zero, power_basis_gen, totient_one, MulZeroClass.mul_zero, mul_one,
      show 1 / 2 = 0 by rfl, discr, trace_matrix]
    have hζone : ζ = 1 := by simpa using hζ
    rw [hζ.power_basis_dim _, hζone, ← (algebraMap K L).map_one,
      minpoly.eq_x_sub_c_of_algebraMap_inj _ (algebraMap K L).Injective, nat_degree_X_sub_C]
    simp only [trace_matrix, map_one, one_pow, Matrix.det_unique, trace_form_apply, mul_one]
    rw [← (algebraMap K L).map_one, trace_algebra_map, finrank _ hirr]
    · simp
    · infer_instance
    · exact hcycl
  · by_cases hk : p ^ (k + 1) = 2
    · have hp : p = 2 :=
        by
        rw [← PNat.coe_inj, PNat.coe_bit0, PNat.one_coe, PNat.pow_coe, ← pow_one 2] at hk
        replace hk :=
          eq_of_prime_pow_eq (prime_iff.1 hp.out) (prime_iff.1 Nat.prime_two) (succ_pos _) hk
        rwa [show 2 = ((2 : ℕ+) : ℕ) by simp, PNat.coe_inj] at hk
      rw [hp, ← PNat.coe_inj, PNat.pow_coe, PNat.coe_bit0, PNat.one_coe] at hk
      nth_rw 2 [← pow_one 2] at hk
      replace hk := Nat.pow_right_injective rfl.le hk
      rw [add_left_eq_self] at hk
      simp only [hp, hk, pow_one, PNat.coe_bit0, PNat.one_coe] at hζ
      simp only [hp, hk, show 1 / 2 = 0 by rfl, coe_basis, pow_one, power_basis_gen, PNat.coe_bit0,
        PNat.one_coe, totient_two, pow_zero, mul_one, MulZeroClass.mul_zero]
      rw [power_basis_dim, hζ.eq_neg_one_of_two_right, show (-1 : L) = algebraMap K L (-1) by simp,
        minpoly.eq_x_sub_c_of_algebraMap_inj _ (algebraMap K L).Injective, nat_degree_X_sub_C]
      simp only [discr, trace_matrix, Matrix.det_unique, Fin.default_eq_zero, Fin.val_zero,
        pow_zero, trace_form_apply, mul_one]
      rw [← (algebraMap K L).map_one, trace_algebra_map, finrank _ hirr, hp, hk]
      · simp
      · infer_instance
      · exact hcycl
    · exact discr_prime_pow_ne_two hζ hirr hk
#align is_cyclotomic_extension.discr_prime_pow IsCyclotomicExtension.discr_prime_pow

/-- If `p` is a prime and `is_cyclotomic_extension {p ^ k} K L`, then there are `u : ℤˣ` and
`n : ℕ` such that the discriminant of `hζ.power_basis K` is `u * p ^ n`. Often this is enough and
less cumbersome to use than `is_cyclotomic_extension.discr_prime_pow`. -/
theorem discr_prime_pow_eq_unit_mul_pow [IsCyclotomicExtension {p ^ k} K L]
    [hp : Fact (p : ℕ).Prime] (hζ : IsPrimitiveRoot ζ ↑(p ^ k))
    (hirr : Irreducible (cyclotomic (↑(p ^ k) : ℕ) K)) :
    ∃ (u : ℤˣ)(n : ℕ), discr K (hζ.PowerBasis K).basis = u * p ^ n :=
  by
  rw [discr_prime_pow hζ hirr]
  by_cases heven : Even ((p ^ k : ℕ).totient / 2)
  · refine' ⟨1, (p : ℕ) ^ (k - 1) * ((p - 1) * k - 1), by simp [heven.neg_one_pow]⟩
  ·
    exact
      ⟨-1, (p : ℕ) ^ (k - 1) * ((p - 1) * k - 1), by simp [(odd_iff_not_even.2 heven).neg_one_pow]⟩
#align is_cyclotomic_extension.discr_prime_pow_eq_unit_mul_pow IsCyclotomicExtension.discr_prime_pow_eq_unit_mul_pow

/-- If `p` is an odd prime and `is_cyclotomic_extension {p} K L`, then
`discr K (hζ.power_basis K).basis = (-1) ^ ((p - 1) / 2) * p ^ (p - 2)` if
`irreducible (cyclotomic p K)`. -/
theorem discr_odd_prime [IsCyclotomicExtension {p} K L] [hp : Fact (p : ℕ).Prime]
    (hζ : IsPrimitiveRoot ζ p) (hirr : Irreducible (cyclotomic p K)) (hodd : p ≠ 2) :
    discr K (hζ.PowerBasis K).basis = (-1) ^ (((p : ℕ) - 1) / 2) * p ^ ((p : ℕ) - 2) :=
  by
  have : IsCyclotomicExtension {p ^ (0 + 1)} K L :=
    by
    rw [zero_add, pow_one]
    infer_instance
  have hζ' : IsPrimitiveRoot ζ ↑(p ^ (0 + 1)) := by simpa using hζ
  convert discr_prime_pow_ne_two hζ' (by simpa [hirr] ) (by simp [hodd])
  · rw [zero_add, pow_one, totient_prime hp.out]
  · rw [pow_zero, one_mul, zero_add, mul_one, Nat.sub_sub]
#align is_cyclotomic_extension.discr_odd_prime IsCyclotomicExtension.discr_odd_prime

end IsCyclotomicExtension


import data.complex.basic
import linear_algebra.basic
import linear_algebra.determinant
import analysis.complex.polynomial
import data.polynomial
import tools.caracteristic.tools
open finset
universes u v w w'
variables
          {R : Type u}[nonzero_comm_ring R]
          {A : Type v} [fintype A][decidable_eq A]
          (M : matrix A A R)
          (N : matrix A A (polynomial R))
open polynomial open matrix
open with_bot tools
open_locale big_operators

/-
  unfold car_matrix, split_ifs, rw eval_add, rw eval_C, rw eval_X, rw h, rw add_val, rw smul_val, rw one_val, rw mul_ite,
     rw mul_one, rw mul_zero,simp, rw eval_C,rw add_val, rw smul_val, rw one_val, rw mul_ite,split_ifs, rw mul_zero,rw add_zero,
-/

lemma eval_ite (r : R) (P Q : polynomial R) (φ : Prop) [decidable φ]:
    eval r  (if  φ  then P  else Q)  = if φ then eval r P else eval r Q :=
    by split_ifs; exact rfl

#check if_congr
#check if_true

noncomputable def car_matrix :
      matrix A A (polynomial R) := λ x y, if x=y then  C (M x y) + X else C (M x y)

namespace car_matrix
lemma eval (r : R) (i j : A): eval r (car_matrix M i j) = (M + r • 1) i j :=
begin
    unfold car_matrix,  rw eval_ite, rw eval_add, rw eval_C, rw eval_X,
    rw add_val, rw smul_val, rw one_val, rw mul_ite, rw mul_one, rw mul_zero,
    rw add_ite, rw add_zero,
end

noncomputable def car_pol : polynomial R :=  det (car_matrix M)
/-!
    We admit a theorem
-/
-- monic_mul  monic_X_add_C

lemma degree_coeff (x  : A) : degree(car_matrix M x x) = 1  :=
begin
    unfold car_matrix, split_ifs,  let g := @degree_C_le R  (M x x) _,
    rw degree_add_eq_of_degree_lt,
    exact degree_X, rw degree_X,refine (lt_iff_not_ge (degree (C (M x x))) 1).mpr _, intro,
     let tr := le_trans a g,  let g := zero_le_one,
        have : 0 = 1,
            apply le_antisymm,
            exact g, exact (with_bot.coe_le rfl).mp tr, trivial, trivial,
end

lemma degree_coeff_ne {x y : A} (hyp : x ≠ y) : degree(car_matrix M x y) ≤ 0 := begin
    unfold car_matrix, split_ifs, trivial, exact degree_C_le,
end
lemma degree_coef_lt_one {x y : A} (hyp : x ≠ y) : degree(car_matrix M x y) < 1 :=
begin
    let g := degree_coeff_ne M hyp,
    refine lt_of_le_of_lt g _,apply  coe_lt_coe.mpr,
    exact zero_lt_one,
end
lemma  zero_le_one :  (0 : with_bot ℕ )  ≤ 1 :=
        begin     apply coe_le_coe.mpr,
            exact zero_le_one,
end
lemma degree_le_one (x y : A) : degree(car_matrix M x y) ≤ 1 :=
begin
    by_cases x = y,
        let g := degree_coeff M x, rw ← h, rw g, exact le_refl 1,
        let g := degree_coeff_ne M h,
        exact le_trans g (zero_le_one),
end

/--

-/
lemma leading_coef (x : A) :  leading_coeff (car_matrix M x x) = 1 :=
begin
        apply (monic.def).mp ,
        unfold car_matrix,
        split_ifs, rw add_comm, apply monic_X_add_C, trivial,
end
/--
      Technical lemma
-/

lemma car_monic (x : A) : monic (car_matrix M x x) := begin
    unfold monic, exact leading_coef M x,
end

end car_matrix

namespace car_pol
open car_matrix equiv.perm

/-!
    Now we reconstruct the coeffcient of det_car the
        ` Σ (σ ∈ perm A), sign σ  ×  ∏ (ℓ ∈ A) car_matrix M σ ℓ ℓ

-/

lemma eval_sum  (φ : A → polynomial R) (r : R)  : eval r (Σ   φ ) =  Σ (λ a : A,(eval r (φ a))) :=
begin
    rw finset.sum_hom finset.univ (polynomial.eval r),
end

lemma eval_prod (φ : A → polynomial R) (r : R) : eval r (finset.prod finset.univ φ) =
finset.prod finset.univ (λ a : A,(eval r (φ a))) :=
begin
    rw finset.prod_hom finset.univ (polynomial.eval r),
end

lemma   test (a : ℤ )(r : R) : eval r (a : polynomial R) = (a : R) :=
begin
    rw int_cast_eq_C, rw eval_C,
end

theorem eval_car_poly ( r : R) : eval r (car_pol M) = det (M+ r • 1) := begin
    unfold det, unfold car_pol, unfold det,
    erw eval_sum, congr, ext σ ,
    rw eval_mul,
    rw eval_prod,
    rw int_cast_eq_C, rw eval_C, congr, ext,
    rw car_matrix.eval,
end
lemma perm_mul (σ : equiv.perm A) (P : polynomial R) : degree ((sign σ : polynomial R) * P) = degree P := begin
    rcases int.units_eq_one_or (sign σ), congr,
     rw h, erw int.cast_one, rw one_mul,
     rw h,
     erw int.cast_neg, erw int.cast_one,
     norm_cast,simp,
end

lemma equiv_not_id (σ  : equiv.perm A) (hyp : σ ≠ 1) : ∃ x : A, σ x ≠ x := begin
    refine not_forall.mp _,
        intro, have : σ =1,
            ext, exact a x, trivial,
end

lemma exists_le_one (σ  : equiv.perm A) (hyp : σ ≠ 1) : ∃ ℓ0 : A,  degree( car_matrix M (σ ℓ0 ) ℓ0 ) < 1:= begin
    rcases (equiv_not_id σ hyp) with ⟨ℓ0,hyp_l ⟩,
    use ℓ0,
    let r := degree_coeff_ne M  hyp_l,
    refine lt_of_le_of_lt r _,
    apply  coe_lt_coe.mpr,
    exact zero_lt_one,
end

noncomputable def term_wihout  (σ : equiv.perm A) :=
     finset.prod univ (λ (x : A), car_matrix M (σ x) x)

noncomputable def term  (σ : equiv.perm A) :=
    ((equiv.perm.sign σ) : polynomial R ) * finset.prod univ (λ (x : A), car_matrix M (σ x) x)

lemma degree_term_eq_degree_term_without  (σ : equiv.perm A ) : degree (term M σ ) = degree (term_wihout M σ ) :=  perm_mul _ _

lemma degree_term_wihout (σ : equiv.perm A) : if σ = 1 then  degree (term_wihout M σ) = fintype.card A else  degree (term_wihout M σ) < fintype.card A
:= begin
    split_ifs, rw h, {
            unfold term_wihout,
            exact prod_monic_one (finset.univ) (λ ℓ, car_matrix M  ℓ ℓ )(degree_coeff M ) (car_monic M),
        },
        {
            unfold term_wihout,
            apply degree_prod_le_one_lt_card((λ ℓ, car_matrix M  (σ ℓ) ℓ )),
            intros ℓ, refine degree_le_one M _ _,
            rcases equiv_not_id (σ ) h with ⟨ℓ0 ,hyp_l0 ⟩ ,
            use ℓ0,
            exact degree_coef_lt_one M hyp_l0,
        },
end
lemma degree_term (σ : equiv.perm A) :
if σ = 1 then  degree (term M σ) = fintype.card A else  degree (term M σ) < fintype.card A :=
begin
    rw degree_term_eq_degree_term_without, exact degree_term_wihout M σ,
end
lemma degree_term_id :  degree (term M (1 : equiv.perm A)) = fintype.card A :=
begin
    let g := degree_term M (1 : equiv.perm A),
    split_ifs at g, exact g, trivial,

end
lemma degree_term_ne (σ : equiv.perm A) (hyp : 1 ≠ σ ) : degree (term M σ) < fintype.card A :=
begin
    let g :=  degree_term M σ ,
    split_ifs at g, rw h at hyp,trivial, exact g,
end
lemma degree_car : degree (car_pol M) = fintype.card A :=
begin
    unfold car_pol, unfold det,
    rw ← degree_term_id M,
    apply  proof_strategy.car_pol_degree(term M),
    rw degree_term_id M,
    exact degree_term_ne M,
    rw degree_term_id, exact bot_lt_some _,
end

theorem eigen_values_exist_mat (hyp : 0 < fintype.card A  ):  ∀ M : matrix A A ℂ ,  ∃ t : ℂ, det ( M + t •(1 : matrix A A ℂ )) = 0
:= begin
    intros M,
    let χ := car_pol M,
    let FTOA := @complex.exists_root χ,
    have : 0 < degree χ,
        erw  degree_car, apply coe_lt_coe.mpr,exact hyp,
    specialize FTOA this,
    rcases FTOA with ⟨ ζ,hyp⟩ ,
    use ζ,
    rw ←  eval_car_poly M ζ ,
    exact hyp,
end
end car_pol


import topology.basic
import data.real.nnreal
import data.real.basic

open set

theorem open_set_for_each
  (X : Type*)
  [topological_space X]
  (A : set X)
  (h₀ : ∀ x ∈ A, ∃ (U : set X), is_open U ∧ x ∈ U ∧ U ⊆ A) :
  is_open A :=
begin
  choose! f hf using h₀,
  have : A = ⋃ x ∈ A, f x,
  {
    ext p,
    split,
    {
      intro hp,
      have hfp := hf p hp,
      rw mem_Union₂,
      use p,
      exact ⟨ hp, hfp.2.1 ⟩,
    },
    {
      intro hp,
      rw mem_Union₂ at hp,
      cases hp with i hi,
      cases hi with hia hpfi,
      have x := hf i hia,
      exact x.2.2 hpfi,
    }
  },
  rw this,
  apply is_open_bUnion,
  intros i hi,
  exact (hf i hi).1,
end

def intersection_of_topologies {X : Type*} {ι : Sort*}
  (f : ι → topological_space X) : topological_space X :=
{ is_open := λ s, ∀ i, (f i).is_open s,
  is_open_univ :=
  begin
    sorry
  end,
  is_open_inter :=
  begin
    intros s t hos hot i,
    specialize hos i,
    specialize hot i,
    exact (f i).is_open_inter s t hos hot,
  end,
  is_open_sUnion := sorry }


variables p q : Prop

example (h : p ∨ q) : q ∨ p :=
  h.elim
    (assume hp : p, or.inr hp)
    (assume hq : q, or.inl hq)