/-
Copyright (c) 2018 Robert Y. Lewis. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Robert Y. Lewis

! This file was ported from Lean 3 source module number_theory.padics.padic_numbers
! leanprover-community/mathlib commit b9b2114f7711fec1c1e055d507f082f8ceb2c3b7
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.NumberTheory.Padics.PadicNorm
import Mathbin.Analysis.Normed.Field.Basic

/-!
# p-adic numbers

This file defines the `p`-adic numbers (rationals) `ℚ_[p]` as
the completion of `ℚ` with respect to the `p`-adic norm.
We show that the `p`-adic norm on `ℚ` extends to `ℚ_[p]`, that `ℚ` is embedded in `ℚ_[p]`,
and that `ℚ_[p]` is Cauchy complete.

## Important definitions

* `padic` : the type of `p`-adic numbers
* `padic_norm_e` : the rational valued `p`-adic norm on `ℚ_[p]`
* `padic.add_valuation` : the additive `p`-adic valuation on `ℚ_[p]`, with values in `with_top ℤ`

## Notation

We introduce the notation `ℚ_[p]` for the `p`-adic numbers.

## Implementation notes

Much, but not all, of this file assumes that `p` is prime. This assumption is inferred automatically
by taking `[fact p.prime]` as a type class argument.

We use the same concrete Cauchy sequence construction that is used to construct `ℝ`.
`ℚ_[p]` inherits a field structure from this construction.
The extension of the norm on `ℚ` to `ℚ_[p]` is *not* analogous to extending the absolute value to
`ℝ` and hence the proof that `ℚ_[p]` is complete is different from the proof that ℝ is complete.

A small special-purpose simplification tactic, `padic_index_simp`, is used to manipulate sequence
indices in the proof that the norm extends.

`padic_norm_e` is the rational-valued `p`-adic norm on `ℚ_[p]`.
To instantiate `ℚ_[p]` as a normed field, we must cast this into a `ℝ`-valued norm.
The `ℝ`-valued norm, using notation `‖ ‖` from normed spaces,
is the canonical representation of this norm.

`simp` prefers `padic_norm` to `padic_norm_e` when possible.
Since `padic_norm_e` and `‖ ‖` have different types, `simp` does not rewrite one to the other.

Coercions from `ℚ` to `ℚ_[p]` are set up to work with the `norm_cast` tactic.

## References

* [F. Q. Gouvêa, *p-adic numbers*][gouvea1997]
* [R. Y. Lewis, *A formal proof of Hensel's lemma over the p-adic integers*][lewis2019]
* <https://en.wikipedia.org/wiki/P-adic_number>

## Tags

p-adic, p adic, padic, norm, valuation, cauchy, completion, p-adic completion
-/

noncomputable section

open Classical

open Nat multiplicity padicNorm CauSeq CauSeq.Completion Metric

/-- The type of Cauchy sequences of rationals with respect to the `p`-adic norm. -/
@[reducible]
def PadicSeq (p : ℕ) :=
  CauSeq _ (padicNorm p)
#align padic_seq PadicSeq

namespace PadicSeq

section

variable {p : ℕ} [Fact p.Prime]

/-- The `p`-adic norm of the entries of a nonzero Cauchy sequence of rationals is eventually
constant. -/
theorem stationary {f : CauSeq ℚ (padicNorm p)} (hf : ¬f ≈ 0) :
    ∃ N, ∀ m n, N ≤ m → N ≤ n → padicNorm p (f n) = padicNorm p (f m) :=
  have : ∃ ε > 0, ∃ N1, ∀ j ≥ N1, ε ≤ padicNorm p (f j) :=
    CauSeq.abv_pos_of_not_limZero <| not_limZero_of_not_congr_zero hf
  let ⟨ε, hε, N1, hN1⟩ := this
  let ⟨N2, hN2⟩ := CauSeq.cauchy₂ f hε
  ⟨max N1 N2, fun n m hn hm =>
    by
    have : padicNorm p (f n - f m) < ε := hN2 _ (max_le_iff.1 hn).2 _ (max_le_iff.1 hm).2
    have : padicNorm p (f n - f m) < padicNorm p (f n) :=
      lt_of_lt_of_le this <| hN1 _ (max_le_iff.1 hn).1
    have : padicNorm p (f n - f m) < max (padicNorm p (f n)) (padicNorm p (f m)) :=
      lt_max_iff.2 (Or.inl this)
    by_contra hne
    rw [← padicNorm.neg (f m)] at hne
    have hnam := add_eq_max_of_ne hne
    rw [padicNorm.neg, max_comm] at hnam
    rw [← hnam, sub_eq_add_neg, add_comm] at this
    apply _root_.lt_irrefl _ this⟩
#align padic_seq.stationary PadicSeq.stationary

/-- For all `n ≥ stationary_point f hf`, the `p`-adic norm of `f n` is the same. -/
def stationaryPoint {f : PadicSeq p} (hf : ¬f ≈ 0) : ℕ :=
  Classical.choose <| stationary hf
#align padic_seq.stationary_point PadicSeq.stationaryPoint

theorem stationaryPoint_spec {f : PadicSeq p} (hf : ¬f ≈ 0) :
    ∀ {m n},
      stationaryPoint hf ≤ m → stationaryPoint hf ≤ n → padicNorm p (f n) = padicNorm p (f m) :=
  Classical.choose_spec <| stationary hf
#align padic_seq.stationary_point_spec PadicSeq.stationaryPoint_spec

/-- Since the norm of the entries of a Cauchy sequence is eventually stationary,
we can lift the norm to sequences. -/
def norm (f : PadicSeq p) : ℚ :=
  if hf : f ≈ 0 then 0 else padicNorm p (f (stationaryPoint hf))
#align padic_seq.norm PadicSeq.norm

theorem norm_zero_iff (f : PadicSeq p) : f.norm = 0 ↔ f ≈ 0 :=
  by
  constructor
  · intro h
    by_contra hf
    unfold norm at h
    split_ifs  at h
    apply hf
    intro ε hε
    exists stationary_point hf
    intro j hj
    have heq := stationary_point_spec hf le_rfl hj
    simpa [h, HEq]
  · intro h
    simp [norm, h]
#align padic_seq.norm_zero_iff PadicSeq.norm_zero_iff

end

section Embedding

open CauSeq

variable {p : ℕ} [Fact p.Prime]

theorem equiv_zero_of_val_eq_of_equiv_zero {f g : PadicSeq p}
    (h : ∀ k, padicNorm p (f k) = padicNorm p (g k)) (hf : f ≈ 0) : g ≈ 0 := fun ε hε =>
  let ⟨i, hi⟩ := hf _ hε
  ⟨i, fun j hj => by simpa [h] using hi _ hj⟩
#align padic_seq.equiv_zero_of_val_eq_of_equiv_zero PadicSeq.equiv_zero_of_val_eq_of_equiv_zero

theorem norm_nonzero_of_not_equiv_zero {f : PadicSeq p} (hf : ¬f ≈ 0) : f.norm ≠ 0 :=
  hf ∘ f.norm_zero_iff.1
#align padic_seq.norm_nonzero_of_not_equiv_zero PadicSeq.norm_nonzero_of_not_equiv_zero

theorem norm_eq_norm_app_of_nonzero {f : PadicSeq p} (hf : ¬f ≈ 0) :
    ∃ k, f.norm = padicNorm p k ∧ k ≠ 0 :=
  have heq : f.norm = padicNorm p (f <| stationaryPoint hf) := by simp [norm, hf]
  ⟨f <| stationaryPoint hf, HEq, fun h =>
    norm_nonzero_of_not_equiv_zero hf (by simpa [h] using HEq)⟩
#align padic_seq.norm_eq_norm_app_of_nonzero PadicSeq.norm_eq_norm_app_of_nonzero

theorem not_limZero_const_of_nonzero {q : ℚ} (hq : q ≠ 0) : ¬LimZero (const (padicNorm p) q) :=
  fun h' => hq <| const_limZero.1 h'
#align padic_seq.not_lim_zero_const_of_nonzero PadicSeq.not_limZero_const_of_nonzero

theorem not_equiv_zero_const_of_nonzero {q : ℚ} (hq : q ≠ 0) : ¬const (padicNorm p) q ≈ 0 :=
  fun h : LimZero (const (padicNorm p) q - 0) => not_limZero_const_of_nonzero hq <| by simpa using h
#align padic_seq.not_equiv_zero_const_of_nonzero PadicSeq.not_equiv_zero_const_of_nonzero

theorem norm_nonneg (f : PadicSeq p) : 0 ≤ f.norm :=
  if hf : f ≈ 0 then by simp [hf, norm] else by simp [norm, hf, padicNorm.nonneg]
#align padic_seq.norm_nonneg PadicSeq.norm_nonneg

/-- An auxiliary lemma for manipulating sequence indices. -/
theorem lift_index_left_left {f : PadicSeq p} (hf : ¬f ≈ 0) (v2 v3 : ℕ) :
    padicNorm p (f (stationaryPoint hf)) = padicNorm p (f (max (stationaryPoint hf) (max v2 v3))) :=
  by
  apply stationary_point_spec hf
  · apply le_max_left
  · exact le_rfl
#align padic_seq.lift_index_left_left PadicSeq.lift_index_left_left

/-- An auxiliary lemma for manipulating sequence indices. -/
theorem lift_index_left {f : PadicSeq p} (hf : ¬f ≈ 0) (v1 v3 : ℕ) :
    padicNorm p (f (stationaryPoint hf)) = padicNorm p (f (max v1 (max (stationaryPoint hf) v3))) :=
  by
  apply stationary_point_spec hf
  · apply le_trans
    · apply le_max_left _ v3
    · apply le_max_right
  · exact le_rfl
#align padic_seq.lift_index_left PadicSeq.lift_index_left

/-- An auxiliary lemma for manipulating sequence indices. -/
theorem lift_index_right {f : PadicSeq p} (hf : ¬f ≈ 0) (v1 v2 : ℕ) :
    padicNorm p (f (stationaryPoint hf)) = padicNorm p (f (max v1 (max v2 (stationaryPoint hf)))) :=
  by
  apply stationary_point_spec hf
  · apply le_trans
    · apply le_max_right v2
    · apply le_max_right
  · exact le_rfl
#align padic_seq.lift_index_right PadicSeq.lift_index_right

end Embedding

section Valuation

open CauSeq

variable {p : ℕ} [Fact p.Prime]

/-! ### Valuation on `padic_seq` -/

/-- The `p`-adic valuation on `ℚ` lifts to `padic_seq p`.
`valuation f` is defined to be the valuation of the (`ℚ`-valued) stationary point of `f`. -/
def valuation (f : PadicSeq p) : ℤ :=
  if hf : f ≈ 0 then 0 else padicValRat p (f (stationaryPoint hf))
#align padic_seq.valuation PadicSeq.valuation

theorem norm_eq_pow_val {f : PadicSeq p} (hf : ¬f ≈ 0) : f.norm = p ^ (-f.Valuation : ℤ) :=
  by
  rw [norm, Valuation, dif_neg hf, dif_neg hf, padicNorm, if_neg]
  intro H
  apply CauSeq.not_limZero_of_not_congr_zero hf
  intro ε hε
  use stationary_point hf
  intro n hn
  rw [stationary_point_spec hf le_rfl hn]
  simpa [H] using hε
#align padic_seq.norm_eq_pow_val PadicSeq.norm_eq_pow_val

theorem val_eq_iff_norm_eq {f g : PadicSeq p} (hf : ¬f ≈ 0) (hg : ¬g ≈ 0) :
    f.Valuation = g.Valuation ↔ f.norm = g.norm :=
  by
  rw [norm_eq_pow_val hf, norm_eq_pow_val hg, ← neg_inj, zpow_inj]
  · exact_mod_cast (Fact.out p.prime).Pos
  · exact_mod_cast (Fact.out p.prime).ne_one
#align padic_seq.val_eq_iff_norm_eq PadicSeq.val_eq_iff_norm_eq

end Valuation

end PadicSeq

section

open PadicSeq

private unsafe def index_simp_core (hh hf hg : expr)
    (at_ : Interactive.Loc := Interactive.Loc.ns [none]) : tactic Unit := do
  let [v1, v2, v3] ← [hh, hf, hg].mapM fun n => tactic.mk_app `` stationary_point [n] <|> return n
  let e1 ← tactic.mk_app `` lift_index_left_left [hh, v2, v3] <|> return q(True)
  let e2 ← tactic.mk_app `` lift_index_left [hf, v1, v3] <|> return q(True)
  let e3 ← tactic.mk_app `` lift_index_right [hg, v1, v2] <|> return q(True)
  let sl ← [e1, e2, e3].foldlM (fun s e => simp_lemmas.add s e) simp_lemmas.mk
  when at_ (tactic.simp_target sl >> tactic.skip)
  let hs ← at_.get_locals
  hs (tactic.simp_hyp sl [])
#align index_simp_core index_simp_core

/-- This is a special-purpose tactic that lifts `padic_norm (f (stationary_point f))` to
`padic_norm (f (max _ _ _))`. -/
unsafe def tactic.interactive.padic_index_simp (l : interactive.parse interactive.types.pexpr_list)
    (at_ : interactive.parse interactive.types.location) : tactic Unit := do
  let [h, f, g] ← l.mapM tactic.i_to_expr
  index_simp_core h f g at_
#align tactic.interactive.padic_index_simp tactic.interactive.padic_index_simp

end

namespace PadicSeq

section Embedding

open CauSeq

variable {p : ℕ} [hp : Fact p.Prime]

include hp

theorem norm_mul (f g : PadicSeq p) : (f * g).norm = f.norm * g.norm :=
  if hf : f ≈ 0 then by
    have hg : f * g ≈ 0 := mul_equiv_zero' _ hf
    simp only [hf, hg, norm, dif_pos, MulZeroClass.zero_mul]
  else
    if hg : g ≈ 0 then by
      have hf : f * g ≈ 0 := mul_equiv_zero _ hg
      simp only [hf, hg, norm, dif_pos, MulZeroClass.mul_zero]
    else by
      have hfg : ¬f * g ≈ 0 := by apply mul_not_equiv_zero <;> assumption
      unfold norm
      split_ifs
      padic_index_simp [hfg, hf, hg]
      apply padicNorm.mul
#align padic_seq.norm_mul PadicSeq.norm_mul

theorem eq_zero_iff_equiv_zero (f : PadicSeq p) : mk f = 0 ↔ f ≈ 0 :=
  mk_eq
#align padic_seq.eq_zero_iff_equiv_zero PadicSeq.eq_zero_iff_equiv_zero

theorem ne_zero_iff_nequiv_zero (f : PadicSeq p) : mk f ≠ 0 ↔ ¬f ≈ 0 :=
  not_iff_not.2 (eq_zero_iff_equiv_zero _)
#align padic_seq.ne_zero_iff_nequiv_zero PadicSeq.ne_zero_iff_nequiv_zero

theorem norm_const (q : ℚ) : norm (const (padicNorm p) q) = padicNorm p q :=
  if hq : q = 0 then
    by
    have : const (padicNorm p) q ≈ 0 := by simp [hq] <;> apply Setoid.refl (const (padicNorm p) 0)
    subst hq <;> simp [norm, this]
  else by
    have : ¬const (padicNorm p) q ≈ 0 := not_equiv_zero_const_of_nonzero hq
    simp [norm, this]
#align padic_seq.norm_const PadicSeq.norm_const

theorem norm_values_discrete (a : PadicSeq p) (ha : ¬a ≈ 0) : ∃ z : ℤ, a.norm = p ^ (-z) :=
  by
  let ⟨k, hk, hk'⟩ := norm_eq_norm_app_of_nonzero ha
  simpa [hk] using padicNorm.values_discrete hk'
#align padic_seq.norm_values_discrete PadicSeq.norm_values_discrete

theorem norm_one : norm (1 : PadicSeq p) = 1 :=
  by
  have h1 : ¬(1 : PadicSeq p) ≈ 0 := one_not_equiv_zero _
  simp [h1, norm, hp.1.one_lt]
#align padic_seq.norm_one PadicSeq.norm_one

private theorem norm_eq_of_equiv_aux {f g : PadicSeq p} (hf : ¬f ≈ 0) (hg : ¬g ≈ 0) (hfg : f ≈ g)
    (h : padicNorm p (f (stationaryPoint hf)) ≠ padicNorm p (g (stationaryPoint hg)))
    (hlt : padicNorm p (g (stationaryPoint hg)) < padicNorm p (f (stationaryPoint hf))) : False :=
  by
  have hpn : 0 < padicNorm p (f (stationary_point hf)) - padicNorm p (g (stationary_point hg)) :=
    sub_pos_of_lt hlt
  cases' hfg _ hpn with N hN
  let i := max N (max (stationary_point hf) (stationary_point hg))
  have hi : N ≤ i := le_max_left _ _
  have hN' := hN _ hi
  padic_index_simp [N, hf, hg]  at hN' h hlt
  have hpne : padicNorm p (f i) ≠ padicNorm p (-g i) := by rwa [← padicNorm.neg (g i)] at h
  let hpnem := add_eq_max_of_ne hpne
  have hpeq : padicNorm p ((f - g) i) = max (padicNorm p (f i)) (padicNorm p (g i)) := by
    rwa [padicNorm.neg] at hpnem
  rw [hpeq, max_eq_left_of_lt hlt] at hN'
  have : padicNorm p (f i) < padicNorm p (f i) :=
    by
    apply lt_of_lt_of_le hN'
    apply sub_le_self
    apply padicNorm.nonneg
  exact lt_irrefl _ this
#align padic_seq.norm_eq_of_equiv_aux padic_seq.norm_eq_of_equiv_aux

private theorem norm_eq_of_equiv {f g : PadicSeq p} (hf : ¬f ≈ 0) (hg : ¬g ≈ 0) (hfg : f ≈ g) :
    padicNorm p (f (stationaryPoint hf)) = padicNorm p (g (stationaryPoint hg)) :=
  by
  by_contra h
  cases'
    Decidable.em
      (padicNorm p (g (stationary_point hg)) < padicNorm p (f (stationary_point hf))) with
    hlt hnlt
  · exact norm_eq_of_equiv_aux hf hg hfg h hlt
  · apply norm_eq_of_equiv_aux hg hf (Setoid.symm hfg) (Ne.symm h)
    apply lt_of_le_of_ne
    apply le_of_not_gt hnlt
    apply h
#align padic_seq.norm_eq_of_equiv padic_seq.norm_eq_of_equiv

theorem norm_equiv {f g : PadicSeq p} (hfg : f ≈ g) : f.norm = g.norm :=
  if hf : f ≈ 0 then by
    have hg : g ≈ 0 := Setoid.trans (Setoid.symm hfg) hf
    simp [norm, hf, hg]
  else by
    have hg : ¬g ≈ 0 := hf ∘ Setoid.trans hfg
    unfold norm <;> split_ifs <;> exact norm_eq_of_equiv hf hg hfg
#align padic_seq.norm_equiv PadicSeq.norm_equiv

private theorem norm_nonarchimedean_aux {f g : PadicSeq p} (hfg : ¬f + g ≈ 0) (hf : ¬f ≈ 0)
    (hg : ¬g ≈ 0) : (f + g).norm ≤ max f.norm g.norm :=
  by
  unfold norm; split_ifs
  padic_index_simp [hfg, hf, hg]
  apply padicNorm.nonarchimedean
#align padic_seq.norm_nonarchimedean_aux padic_seq.norm_nonarchimedean_aux

theorem norm_nonarchimedean (f g : PadicSeq p) : (f + g).norm ≤ max f.norm g.norm :=
  if hfg : f + g ≈ 0 then
    by
    have : 0 ≤ max f.norm g.norm := le_max_of_le_left (norm_nonneg _)
    simpa only [hfg, norm, Ne.def, le_max_iff, CauSeq.add_apply, not_true, dif_pos]
  else
    if hf : f ≈ 0 then
      by
      have hfg' : f + g ≈ g := by
        change lim_zero (f - 0) at hf
        show lim_zero (f + g - g); · simpa only [sub_zero, add_sub_cancel] using hf
      have hcfg : (f + g).norm = g.norm := norm_equiv hfg'
      have hcl : f.norm = 0 := (norm_zero_iff f).2 hf
      have : max f.norm g.norm = g.norm := by rw [hcl] <;> exact max_eq_right (norm_nonneg _)
      rw [this, hcfg]
    else
      if hg : g ≈ 0 then
        by
        have hfg' : f + g ≈ f := by
          change lim_zero (g - 0) at hg
          show lim_zero (f + g - f); · simpa only [add_sub_cancel', sub_zero] using hg
        have hcfg : (f + g).norm = f.norm := norm_equiv hfg'
        have hcl : g.norm = 0 := (norm_zero_iff g).2 hg
        have : max f.norm g.norm = f.norm := by rw [hcl] <;> exact max_eq_left (norm_nonneg _)
        rw [this, hcfg]
      else norm_nonarchimedean_aux hfg hf hg
#align padic_seq.norm_nonarchimedean PadicSeq.norm_nonarchimedean

theorem norm_eq {f g : PadicSeq p} (h : ∀ k, padicNorm p (f k) = padicNorm p (g k)) :
    f.norm = g.norm :=
  if hf : f ≈ 0 then by
    have hg : g ≈ 0 := equiv_zero_of_val_eq_of_equiv_zero h hf
    simp only [hf, hg, norm, dif_pos]
  else
    by
    have hg : ¬g ≈ 0 := fun hg =>
      hf <| equiv_zero_of_val_eq_of_equiv_zero (by simp only [h, forall_const, eq_self_iff_true]) hg
    simp only [hg, hf, norm, dif_neg, not_false_iff]
    let i := max (stationary_point hf) (stationary_point hg)
    have hpf : padicNorm p (f (stationary_point hf)) = padicNorm p (f i) :=
      by
      apply stationary_point_spec
      apply le_max_left
      exact le_rfl
    have hpg : padicNorm p (g (stationary_point hg)) = padicNorm p (g i) :=
      by
      apply stationary_point_spec
      apply le_max_right
      exact le_rfl
    rw [hpf, hpg, h]
#align padic_seq.norm_eq PadicSeq.norm_eq

theorem norm_neg (a : PadicSeq p) : (-a).norm = a.norm :=
  norm_eq <| by simp
#align padic_seq.norm_neg PadicSeq.norm_neg

theorem norm_eq_of_add_equiv_zero {f g : PadicSeq p} (h : f + g ≈ 0) : f.norm = g.norm :=
  by
  have : LimZero (f + g - 0) := h
  have : f ≈ -g := show LimZero (f - -g) by simpa only [sub_zero, sub_neg_eq_add]
  have : f.norm = (-g).norm := norm_equiv this
  simpa only [norm_neg] using this
#align padic_seq.norm_eq_of_add_equiv_zero PadicSeq.norm_eq_of_add_equiv_zero

theorem add_eq_max_of_ne {f g : PadicSeq p} (hfgne : f.norm ≠ g.norm) :
    (f + g).norm = max f.norm g.norm :=
  have hfg : ¬f + g ≈ 0 := mt norm_eq_of_add_equiv_zero hfgne
  if hf : f ≈ 0 then by
    have : LimZero (f - 0) := hf
    have : f + g ≈ g := show LimZero (f + g - g) by simpa only [sub_zero, add_sub_cancel]
    have h1 : (f + g).norm = g.norm := norm_equiv this
    have h2 : f.norm = 0 := (norm_zero_iff _).2 hf
    rw [h1, h2] <;> rw [max_eq_right (norm_nonneg _)]
  else
    if hg : g ≈ 0 then by
      have : LimZero (g - 0) := hg
      have : f + g ≈ f := show LimZero (f + g - f) by rw [add_sub_cancel'] <;> simpa only [sub_zero]
      have h1 : (f + g).norm = f.norm := norm_equiv this
      have h2 : g.norm = 0 := (norm_zero_iff _).2 hg
      rw [h1, h2] <;> rw [max_eq_left (norm_nonneg _)]
    else by
      unfold norm at hfgne⊢; split_ifs  at hfgne⊢
      padic_index_simp [hfg, hf, hg]  at hfgne⊢
      exact padicNorm.add_eq_max_of_ne hfgne
#align padic_seq.add_eq_max_of_ne PadicSeq.add_eq_max_of_ne

end Embedding

end PadicSeq

/-- The `p`-adic numbers `ℚ_[p]` are the Cauchy completion of `ℚ` with respect to the `p`-adic norm.
-/
def Padic (p : ℕ) [Fact p.Prime] :=
  @CauSeq.Completion.Cauchy _ _ _ _ (padicNorm p) _
#align padic Padic

-- mathport name: «exprℚ_[ ]»
notation "ℚ_[" p "]" => Padic p

namespace Padic

section Completion

variable {p : ℕ} [Fact p.Prime]

instance : Field ℚ_[p] :=
  Cauchy.field

instance : Inhabited ℚ_[p] :=
  ⟨0⟩

-- short circuits
instance : CommRing ℚ_[p] :=
  Cauchy.commRing

instance : Ring ℚ_[p] :=
  Cauchy.ring

instance : Zero ℚ_[p] := by infer_instance

instance : One ℚ_[p] := by infer_instance

instance : Add ℚ_[p] := by infer_instance

instance : Mul ℚ_[p] := by infer_instance

instance : Sub ℚ_[p] := by infer_instance

instance : Neg ℚ_[p] := by infer_instance

instance : Div ℚ_[p] := by infer_instance

instance : AddCommGroup ℚ_[p] := by infer_instance

/-- Builds the equivalence class of a Cauchy sequence of rationals. -/
def mk : PadicSeq p → ℚ_[p] :=
  Quotient.mk'
#align padic.mk Padic.mk

variable (p)

theorem zero_def : (0 : ℚ_[p]) = ⟦0⟧ :=
  rfl
#align padic.zero_def Padic.zero_def

theorem mk_eq {f g : PadicSeq p} : mk f = mk g ↔ f ≈ g :=
  Quotient.eq'
#align padic.mk_eq Padic.mk_eq

theorem const_equiv {q r : ℚ} : const (padicNorm p) q ≈ const (padicNorm p) r ↔ q = r :=
  ⟨fun heq => eq_of_sub_eq_zero <| const_limZero.1 HEq, fun heq => by
    rw [HEq] <;> apply Setoid.refl _⟩
#align padic.const_equiv Padic.const_equiv

@[norm_cast]
theorem coe_inj {q r : ℚ} : (↑q : ℚ_[p]) = ↑r ↔ q = r :=
  ⟨(const_equiv p).1 ∘ Quotient.eq'.1, fun h => by rw [h]⟩
#align padic.coe_inj Padic.coe_inj

instance : CharZero ℚ_[p] :=
  ⟨fun m n => by
    rw [← Rat.cast_coe_nat]
    norm_cast
    exact id⟩

@[norm_cast]
theorem coe_add : ∀ {x y : ℚ}, (↑(x + y) : ℚ_[p]) = ↑x + ↑y :=
  Rat.cast_add
#align padic.coe_add Padic.coe_add

@[norm_cast]
theorem coe_neg : ∀ {x : ℚ}, (↑(-x) : ℚ_[p]) = -↑x :=
  Rat.cast_neg
#align padic.coe_neg Padic.coe_neg

@[norm_cast]
theorem coe_mul : ∀ {x y : ℚ}, (↑(x * y) : ℚ_[p]) = ↑x * ↑y :=
  Rat.cast_mul
#align padic.coe_mul Padic.coe_mul

@[norm_cast]
theorem coe_sub : ∀ {x y : ℚ}, (↑(x - y) : ℚ_[p]) = ↑x - ↑y :=
  Rat.cast_sub
#align padic.coe_sub Padic.coe_sub

@[norm_cast]
theorem coe_div : ∀ {x y : ℚ}, (↑(x / y) : ℚ_[p]) = ↑x / ↑y :=
  Rat.cast_div
#align padic.coe_div Padic.coe_div

@[norm_cast]
theorem coe_one : (↑1 : ℚ_[p]) = 1 :=
  rfl
#align padic.coe_one Padic.coe_one

@[norm_cast]
theorem coe_zero : (↑0 : ℚ_[p]) = 0 :=
  rfl
#align padic.coe_zero Padic.coe_zero

end Completion

end Padic

/-- The rational-valued `p`-adic norm on `ℚ_[p]` is lifted from the norm on Cauchy sequences. The
canonical form of this function is the normed space instance, with notation `‖ ‖`. -/
def padicNormE {p : ℕ} [hp : Fact p.Prime] : AbsoluteValue ℚ_[p] ℚ
    where
  toFun := Quotient.lift PadicSeq.norm <| @PadicSeq.norm_equiv _ _
  map_mul' q r := Quotient.induction_on₂ q r <| PadicSeq.norm_mul
  nonneg' q := Quotient.inductionOn q <| PadicSeq.norm_nonneg
  eq_zero' q :=
    Quotient.inductionOn q <| by
      simpa only [Padic.zero_def, Quotient.eq'] using PadicSeq.norm_zero_iff
  add_le' q r :=
    by
    trans
      max ((Quotient.lift PadicSeq.norm <| @PadicSeq.norm_equiv _ _) q)
        ((Quotient.lift PadicSeq.norm <| @PadicSeq.norm_equiv _ _) r)
    exact Quotient.induction_on₂ q r <| PadicSeq.norm_nonarchimedean
    refine' max_le_add_of_nonneg (Quotient.inductionOn q <| PadicSeq.norm_nonneg) _
    exact Quotient.inductionOn r <| PadicSeq.norm_nonneg
#align padic_norm_e padicNormE

namespace padicNormE

section Embedding

open PadicSeq

variable {p : ℕ} [Fact p.Prime]

theorem defn (f : PadicSeq p) {ε : ℚ} (hε : 0 < ε) : ∃ N, ∀ i ≥ N, padicNormE (⟦f⟧ - f i) < ε :=
  by
  dsimp [padicNormE]
  change ∃ N, ∀ i ≥ N, (f - const _ (f i)).norm < ε
  by_contra' h
  cases' cauchy₂ f hε with N hN
  rcases h N with ⟨i, hi, hge⟩
  have hne : ¬f - const (padicNorm p) (f i) ≈ 0 :=
    by
    intro h
    unfold PadicSeq.norm at hge <;> split_ifs  at hge
    exact not_lt_of_ge hge hε
  unfold PadicSeq.norm at hge <;> split_ifs  at hge
  apply not_le_of_gt _ hge
  cases' em (N ≤ stationary_point hne) with hgen hngen
  · apply hN _ hgen _ hi
  · have := stationary_point_spec hne le_rfl (le_of_not_le hngen)
    rw [← this]
    exact hN _ le_rfl _ hi
#align padic_norm_e.defn padicNormE.defn

/-- Theorems about `padic_norm_e` are named with a `'` so the names do not conflict with the
equivalent theorems about `norm` (`‖ ‖`). -/
theorem nonarchimedean' (q r : ℚ_[p]) : padicNormE (q + r) ≤ max (padicNormE q) (padicNormE r) :=
  Quotient.induction_on₂ q r <| norm_nonarchimedean
#align padic_norm_e.nonarchimedean' padicNormE.nonarchimedean'

/-- Theorems about `padic_norm_e` are named with a `'` so the names do not conflict with the
equivalent theorems about `norm` (`‖ ‖`). -/
theorem add_eq_max_of_ne' {q r : ℚ_[p]} :
    padicNormE q ≠ padicNormE r → padicNormE (q + r) = max (padicNormE q) (padicNormE r) :=
  Quotient.induction_on₂ q r fun _ _ => PadicSeq.add_eq_max_of_ne
#align padic_norm_e.add_eq_max_of_ne' padicNormE.add_eq_max_of_ne'

@[simp]
theorem eq_padic_norm' (q : ℚ) : padicNormE (q : ℚ_[p]) = padicNorm p q :=
  norm_const _
#align padic_norm_e.eq_padic_norm' padicNormE.eq_padic_norm'

protected theorem image' {q : ℚ_[p]} : q ≠ 0 → ∃ n : ℤ, padicNormE q = p ^ (-n) :=
  Quotient.inductionOn q fun f hf =>
    have : ¬f ≈ 0 := (ne_zero_iff_nequiv_zero f).1 hf
    norm_values_discrete f this
#align padic_norm_e.image' padicNormE.image'

end Embedding

end padicNormE

namespace Padic

section Complete

open PadicSeq Padic

variable {p : ℕ} [Fact p.Prime] (f : CauSeq _ (@padicNormE p _))

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (m n «expr ≥ » N) -/
theorem rat_dense' (q : ℚ_[p]) {ε : ℚ} (hε : 0 < ε) : ∃ r : ℚ, padicNormE (q - r) < ε :=
  Quotient.inductionOn q fun q' =>
    have : ∃ N, ∀ (m) (_ : m ≥ N) (n) (_ : n ≥ N), padicNorm p (q' m - q' n) < ε := cauchy₂ _ hε
    let ⟨N, hN⟩ := this
    ⟨q' N, by
      dsimp [padicNormE]
      change PadicSeq.norm (q' - const _ (q' N)) < ε
      cases' Decidable.em (q' - const (padicNorm p) (q' N) ≈ 0) with heq hne'
      · simpa only [HEq, PadicSeq.norm, dif_pos]
      · simp only [PadicSeq.norm, dif_neg hne']
        change padicNorm p (q' _ - q' _) < ε
        have := stationary_point_spec hne'
        cases' Decidable.em (stationary_point hne' ≤ N) with hle hle
        · have := Eq.symm (this le_rfl hle)
          simp only [const_apply, sub_apply, padicNorm.zero, sub_self] at this
          simpa only [this]
        · exact hN _ (lt_of_not_ge hle).le _ le_rfl⟩
#align padic.rat_dense' Padic.rat_dense'

open Classical

private theorem div_nat_pos (n : ℕ) : 0 < 1 / (n + 1 : ℚ) :=
  div_pos zero_lt_one (by exact_mod_cast succ_pos _)
#align padic.div_nat_pos padic.div_nat_pos

/-- `lim_seq f`, for `f` a Cauchy sequence of `p`-adic numbers, is a sequence of rationals with the
same limit point as `f`. -/
def limSeq : ℕ → ℚ := fun n => Classical.choose (rat_dense' (f n) (div_nat_pos n))
#align padic.lim_seq Padic.limSeq

theorem exi_rat_seq_conv {ε : ℚ} (hε : 0 < ε) :
    ∃ N, ∀ i ≥ N, padicNormE (f i - (limSeq f i : ℚ_[p])) < ε :=
  by
  refine' (exists_nat_gt (1 / ε)).imp fun N hN i hi => _
  have h := Classical.choose_spec (rat_dense' (f i) (div_nat_pos i))
  refine' lt_of_lt_of_le h ((div_le_iff' <| by exact_mod_cast succ_pos _).mpr _)
  rw [right_distrib]
  apply le_add_of_le_of_nonneg
  · exact (div_le_iff hε).mp (le_trans (le_of_lt hN) (by exact_mod_cast hi))
  · apply le_of_lt
    simpa
#align padic.exi_rat_seq_conv Padic.exi_rat_seq_conv

theorem exi_rat_seq_conv_cauchy : IsCauSeq (padicNorm p) (limSeq f) := fun ε hε =>
  by
  have hε3 : 0 < ε / 3 := div_pos hε (by norm_num)
  let ⟨N, hN⟩ := exi_rat_seq_conv f hε3
  let ⟨N2, hN2⟩ := f.cauchy₂ hε3
  exists max N N2
  intro j hj
  suffices padicNormE (lim_seq f j - f (max N N2) + (f (max N N2) - lim_seq f (max N N2))) < ε
    by
    ring_nf  at this⊢
    rw [← padicNormE.eq_padic_norm']
    exact_mod_cast this
  · apply lt_of_le_of_lt
    · apply padic_norm_e.add_le
    · have : (3 : ℚ) ≠ 0 := by norm_num
      have : ε = ε / 3 + ε / 3 + ε / 3 := by
        field_simp [this]
        simp only [bit0, bit1, mul_add, mul_one]
      rw [this]
      apply add_lt_add
      · suffices padicNormE (lim_seq f j - f j + (f j - f (max N N2))) < ε / 3 + ε / 3 by
          simpa only [sub_add_sub_cancel]
        apply lt_of_le_of_lt
        · apply padic_norm_e.add_le
        · apply add_lt_add
          · rw [padic_norm_e.map_sub]
            apply_mod_cast hN
            exact le_of_max_le_left hj
          · exact hN2 _ (le_of_max_le_right hj) _ (le_max_right _ _)
      · apply_mod_cast hN
        apply le_max_left
#align padic.exi_rat_seq_conv_cauchy Padic.exi_rat_seq_conv_cauchy

private def lim' : PadicSeq p :=
  ⟨_, exi_rat_seq_conv_cauchy f⟩
#align padic.lim' padic.lim'

private def lim : ℚ_[p] :=
  ⟦lim' f⟧
#align padic.lim padic.lim

theorem complete' : ∃ q : ℚ_[p], ∀ ε > 0, ∃ N, ∀ i ≥ N, padicNormE (q - f i) < ε :=
  ⟨limUnder f, fun ε hε =>
    by
    obtain ⟨N, hN⟩ := exi_rat_seq_conv f (half_pos hε)
    obtain ⟨N2, hN2⟩ := padicNormE.defn (lim' f) (half_pos hε)
    refine' ⟨max N N2, fun i hi => _⟩
    rw [← sub_add_sub_cancel _ (lim' f i : ℚ_[p]) _]
    refine' (padic_norm_e.add_le _ _).trans_lt _
    rw [← add_halves ε]
    apply add_lt_add
    · apply hN2 _ (le_of_max_le_right hi)
    · rw [padic_norm_e.map_sub]
      exact hN _ (le_of_max_le_left hi)⟩
#align padic.complete' Padic.complete'

end Complete

section NormedSpace

variable (p : ℕ) [Fact p.Prime]

instance : Dist ℚ_[p] :=
  ⟨fun x y => padicNormE (x - y)⟩

instance : MetricSpace ℚ_[p] where
  dist_self := by simp [dist]
  dist := dist
  dist_comm x y := by simp [dist, ← padic_norm_e.map_neg (x - y)]
  dist_triangle x y z := by
    unfold dist
    exact_mod_cast padic_norm_e.sub_le _ _ _
  eq_of_dist_eq_zero := by
    unfold dist; intro _ _ h
    apply eq_of_sub_eq_zero
    apply padic_norm_e.eq_zero.1
    exact_mod_cast h

instance : Norm ℚ_[p] :=
  ⟨fun x => padicNormE x⟩

instance : NormedField ℚ_[p] :=
  { Padic.field,
    Padic.metricSpace p with
    dist_eq := fun _ _ => rfl
    norm_mul' := by simp [Norm.norm, map_mul]
    norm := norm }

instance isAbsoluteValue : IsAbsoluteValue fun a : ℚ_[p] => ‖a‖
    where
  abv_nonneg := norm_nonneg
  abv_eq_zero _ := norm_eq_zero
  abv_add := norm_add_le
  abv_mul := by simp [Norm.norm, map_mul]
#align padic.is_absolute_value Padic.isAbsoluteValue

theorem rat_dense (q : ℚ_[p]) {ε : ℝ} (hε : 0 < ε) : ∃ r : ℚ, ‖q - r‖ < ε :=
  let ⟨ε', hε'l, hε'r⟩ := exists_rat_btwn hε
  let ⟨r, hr⟩ := rat_dense' q (by simpa using hε'l)
  ⟨r, lt_trans (by simpa [Norm.norm] using hr) hε'r⟩
#align padic.rat_dense Padic.rat_dense

end NormedSpace

end Padic

namespace padicNormE

section NormedSpace

variable {p : ℕ} [hp : Fact p.Prime]

include hp

@[simp]
protected theorem mul (q r : ℚ_[p]) : ‖q * r‖ = ‖q‖ * ‖r‖ := by simp [Norm.norm, map_mul]
#align padic_norm_e.mul padicNormE.mul

protected theorem is_norm (q : ℚ_[p]) : ↑(padicNormE q) = ‖q‖ :=
  rfl
#align padic_norm_e.is_norm padicNormE.is_norm

theorem nonarchimedean (q r : ℚ_[p]) : ‖q + r‖ ≤ max ‖q‖ ‖r‖ :=
  by
  unfold Norm.norm
  exact_mod_cast nonarchimedean' _ _
#align padic_norm_e.nonarchimedean padicNormE.nonarchimedean

theorem add_eq_max_of_ne {q r : ℚ_[p]} (h : ‖q‖ ≠ ‖r‖) : ‖q + r‖ = max ‖q‖ ‖r‖ :=
  by
  unfold Norm.norm
  apply_mod_cast add_eq_max_of_ne'
  intro h'
  apply h
  unfold Norm.norm
  exact_mod_cast h'
#align padic_norm_e.add_eq_max_of_ne padicNormE.add_eq_max_of_ne

@[simp]
theorem eq_padicNorm (q : ℚ) : ‖(q : ℚ_[p])‖ = padicNorm p q :=
  by
  unfold Norm.norm
  rw [← padicNormE.eq_padic_norm']
#align padic_norm_e.eq_padic_norm padicNormE.eq_padicNorm

@[simp]
theorem norm_p : ‖(p : ℚ_[p])‖ = p⁻¹ :=
  by
  have p₀ : p ≠ 0 := hp.1.NeZero
  have p₁ : p ≠ 1 := hp.1.ne_one
  rw [← @Rat.cast_coe_nat ℝ _ p]
  rw [← @Rat.cast_coe_nat ℚ_[p] _ p]
  simp [p₀, p₁, norm, padicNorm, padicValRat, padicValInt, zpow_neg, -Rat.cast_coe_nat]
#align padic_norm_e.norm_p padicNormE.norm_p

theorem norm_p_lt_one : ‖(p : ℚ_[p])‖ < 1 :=
  by
  rw [norm_p]
  apply inv_lt_one
  exact_mod_cast hp.1.one_lt
#align padic_norm_e.norm_p_lt_one padicNormE.norm_p_lt_one

@[simp]
theorem norm_p_zpow (n : ℤ) : ‖(p ^ n : ℚ_[p])‖ = p ^ (-n) := by
  rw [norm_zpow, norm_p, zpow_neg, inv_zpow]
#align padic_norm_e.norm_p_zpow padicNormE.norm_p_zpow

@[simp]
theorem norm_p_pow (n : ℕ) : ‖(p ^ n : ℚ_[p])‖ = p ^ (-n : ℤ) := by rw [← norm_p_zpow, zpow_ofNat]
#align padic_norm_e.norm_p_pow padicNormE.norm_p_pow

instance : NontriviallyNormedField ℚ_[p] :=
  { Padic.normedField p with
    non_trivial :=
      ⟨p⁻¹, by
        rw [norm_inv, norm_p, inv_inv]
        exact_mod_cast hp.1.one_lt⟩ }

protected theorem image {q : ℚ_[p]} : q ≠ 0 → ∃ n : ℤ, ‖q‖ = ↑((p : ℚ) ^ (-n)) :=
  Quotient.inductionOn q fun f hf =>
    have : ¬f ≈ 0 := (PadicSeq.ne_zero_iff_nequiv_zero f).1 hf
    let ⟨n, hn⟩ := PadicSeq.norm_values_discrete f this
    ⟨n, congr_arg coe hn⟩
#align padic_norm_e.image padicNormE.image

protected theorem is_rat (q : ℚ_[p]) : ∃ q' : ℚ, ‖q‖ = q' :=
  if h : q = 0 then ⟨0, by simp [h]⟩
  else
    let ⟨n, hn⟩ := padicNormE.image h
    ⟨_, hn⟩
#align padic_norm_e.is_rat padicNormE.is_rat

/-- `rat_norm q`, for a `p`-adic number `q` is the `p`-adic norm of `q`, as rational number.

The lemma `padic_norm_e.eq_rat_norm` asserts `‖q‖ = rat_norm q`. -/
def ratNorm (q : ℚ_[p]) : ℚ :=
  Classical.choose (padicNormE.is_rat q)
#align padic_norm_e.rat_norm padicNormE.ratNorm

theorem eq_ratNorm (q : ℚ_[p]) : ‖q‖ = ratNorm q :=
  Classical.choose_spec (padicNormE.is_rat q)
#align padic_norm_e.eq_rat_norm padicNormE.eq_ratNorm

theorem norm_rat_le_one : ∀ {q : ℚ} (hq : ¬p ∣ q.den), ‖(q : ℚ_[p])‖ ≤ 1
  | ⟨n, d, hn, hd⟩ => fun hq : ¬p ∣ d =>
    if hnz : n = 0 then
      by
      have : (⟨n, d, hn, hd⟩ : ℚ) = 0 := Rat.zero_iff_num_zero.mpr hnz
      norm_num [this]
    else
      by
      have hnz' :
        {   num := n
            den := d
            Pos := hn
            cop := hd } ≠ 0 := mt Rat.zero_iff_num_zero.1 hnz
      rw [padicNormE.eq_padicNorm]
      norm_cast
      rw [padicNorm.eq_zpow_of_nonzero hnz', padicValRat, neg_sub,
        padicValNat.eq_zero_of_not_dvd hq]
      norm_cast
      rw [zero_sub, zpow_neg, zpow_ofNat]
      apply inv_le_one
      · norm_cast
        apply one_le_pow
        exact hp.1.Pos
#align padic_norm_e.norm_rat_le_one padicNormE.norm_rat_le_one

theorem norm_int_le_one (z : ℤ) : ‖(z : ℚ_[p])‖ ≤ 1 :=
  suffices ‖((z : ℚ) : ℚ_[p])‖ ≤ 1 by simpa
  norm_rat_le_one <| by simp [hp.1.ne_one]
#align padic_norm_e.norm_int_le_one padicNormE.norm_int_le_one

theorem norm_int_lt_one_iff_dvd (k : ℤ) : ‖(k : ℚ_[p])‖ < 1 ↔ ↑p ∣ k :=
  by
  constructor
  · intro h
    contrapose! h
    apply le_of_eq
    rw [eq_comm]
    calc
      ‖(k : ℚ_[p])‖ = ‖((k : ℚ) : ℚ_[p])‖ := by norm_cast
      _ = padicNorm p k := (padicNormE.eq_padicNorm _)
      _ = 1 := _

    rw [padicNorm]
    split_ifs with H
    · exfalso
      apply h
      norm_cast  at H
      rw [H]
      apply dvd_zero
    · norm_cast  at H⊢
      convert zpow_zero _
      rw [neg_eq_zero, padicValRat.of_int]
      norm_cast
      apply padicValInt.eq_zero_of_not_dvd h
  · rintro ⟨x, rfl⟩
    push_cast
    rw [padicNormE.mul]
    calc
      _ ≤ ‖(p : ℚ_[p])‖ * 1 :=
        mul_le_mul le_rfl (by simpa using norm_int_le_one _) (norm_nonneg _) (norm_nonneg _)
      _ < 1 := _

    · rw [mul_one, padicNormE.norm_p]
      apply inv_lt_one
      exact_mod_cast hp.1.one_lt
#align padic_norm_e.norm_int_lt_one_iff_dvd padicNormE.norm_int_lt_one_iff_dvd

theorem norm_int_le_pow_iff_dvd (k : ℤ) (n : ℕ) : ‖(k : ℚ_[p])‖ ≤ ↑p ^ (-n : ℤ) ↔ ↑(p ^ n) ∣ k :=
  by
  have : (p : ℝ) ^ (-n : ℤ) = ↑(p ^ (-n : ℤ) : ℚ) := by simp
  rw [show (k : ℚ_[p]) = ((k : ℚ) : ℚ_[p]) by norm_cast, eq_padic_norm, this]
  norm_cast
  rw [← padicNorm.dvd_iff_norm_le]
#align padic_norm_e.norm_int_le_pow_iff_dvd padicNormE.norm_int_le_pow_iff_dvd

theorem eq_of_norm_add_lt_right {z1 z2 : ℚ_[p]} (h : ‖z1 + z2‖ < ‖z2‖) : ‖z1‖ = ‖z2‖ :=
  by_contradiction fun hne =>
    not_lt_of_ge (by rw [padicNormE.add_eq_max_of_ne hne] <;> apply le_max_right) h
#align padic_norm_e.eq_of_norm_add_lt_right padicNormE.eq_of_norm_add_lt_right

theorem eq_of_norm_add_lt_left {z1 z2 : ℚ_[p]} (h : ‖z1 + z2‖ < ‖z1‖) : ‖z1‖ = ‖z2‖ :=
  by_contradiction fun hne =>
    not_lt_of_ge (by rw [padicNormE.add_eq_max_of_ne hne] <;> apply le_max_left) h
#align padic_norm_e.eq_of_norm_add_lt_left padicNormE.eq_of_norm_add_lt_left

end NormedSpace

end padicNormE

namespace Padic

variable {p : ℕ} [hp : Fact p.Prime]

include hp

/- ./././Mathport/Syntax/Translate/Basic.lean:334:40: warning: unsupported option eqn_compiler.zeta -/
set_option eqn_compiler.zeta true

instance complete : CauSeq.IsComplete ℚ_[p] norm :=
  by
  constructor
  intro f
  have cau_seq_norm_e : IsCauSeq padicNormE f :=
    by
    intro ε hε
    let h := is_cau f ε (by exact_mod_cast hε)
    unfold norm at h
    apply_mod_cast h
  cases' Padic.complete' ⟨f, cau_seq_norm_e⟩ with q hq
  exists q
  intro ε hε
  cases' exists_rat_btwn hε with ε' hε'
  norm_cast  at hε'
  cases' hq ε' hε'.1 with N hN
  exists N
  intro i hi
  let h := hN i hi
  unfold norm
  rw_mod_cast [padic_norm_e.map_sub]
  refine' lt_trans _ hε'.2
  exact_mod_cast hN i hi
#align padic.complete Padic.complete

theorem padic_norm_e_lim_le {f : CauSeq ℚ_[p] norm} {a : ℝ} (ha : 0 < a) (hf : ∀ i, ‖f i‖ ≤ a) :
    ‖f.lim‖ ≤ a :=
  let ⟨N, hN⟩ := Setoid.symm (CauSeq.equiv_lim f) _ ha
  calc
    ‖f.lim‖ = ‖f.lim - f N + f N‖ := by simp
    _ ≤ max ‖f.lim - f N‖ ‖f N‖ := (padicNormE.nonarchimedean _ _)
    _ ≤ a := max_le (le_of_lt (hN _ le_rfl)) (hf _)

#align padic.padic_norm_e_lim_le Padic.padic_norm_e_lim_le

open Filter Set

instance : CompleteSpace ℚ_[p] :=
  by
  apply complete_of_cauchy_seq_tendsto
  intro u hu
  let c : CauSeq ℚ_[p] norm := ⟨u, metric.cauchy_seq_iff'.mp hu⟩
  refine' ⟨c.lim, fun s h => _⟩
  rcases Metric.mem_nhds_iff.1 h with ⟨ε, ε0, hε⟩
  have := c.equiv_lim ε ε0
  simp only [mem_map, mem_at_top_sets, mem_set_of_eq]
  exact this.imp fun N hN n hn => hε (hN n hn)

/-! ### Valuation on `ℚ_[p]` -/

/-- `padic.valuation` lifts the `p`-adic valuation on rationals to `ℚ_[p]`. -/
def valuation : ℚ_[p] → ℤ :=
  Quotient.lift (@PadicSeq.valuation p _) fun f g h =>
    by
    by_cases hf : f ≈ 0
    · have hg : g ≈ 0 := Setoid.trans (Setoid.symm h) hf
      simp [hf, hg, PadicSeq.valuation]
    · have hg : ¬g ≈ 0 := fun hg => hf (Setoid.trans h hg)
      rw [PadicSeq.val_eq_iff_norm_eq hf hg]
      exact PadicSeq.norm_equiv h
#align padic.valuation Padic.valuation

@[simp]
theorem valuation_zero : valuation (0 : ℚ_[p]) = 0 :=
  dif_pos ((const_equiv p).2 rfl)
#align padic.valuation_zero Padic.valuation_zero

@[simp]
theorem valuation_one : valuation (1 : ℚ_[p]) = 0 :=
  by
  change dite (CauSeq.const (padicNorm p) 1 ≈ _) _ _ = _
  have h : ¬CauSeq.const (padicNorm p) 1 ≈ 0 :=
    by
    intro H
    erw [const_equiv p] at H
    exact one_ne_zero H
  rw [dif_neg h]
  simp
#align padic.valuation_one Padic.valuation_one

theorem norm_eq_pow_val {x : ℚ_[p]} : x ≠ 0 → ‖x‖ = p ^ (-x.Valuation) :=
  by
  apply Quotient.inductionOn' x; clear x
  intro f hf
  change (PadicSeq.norm _ : ℝ) = (p : ℝ) ^ (-PadicSeq.valuation _)
  rw [PadicSeq.norm_eq_pow_val]
  change ↑((p : ℚ) ^ (-PadicSeq.valuation f)) = (p : ℝ) ^ (-PadicSeq.valuation f)
  · rw [Rat.cast_zpow, Rat.cast_coe_nat]
  · apply CauSeq.not_limZero_of_not_congr_zero
    contrapose! hf
    apply Quotient.sound
    simpa using hf
#align padic.norm_eq_pow_val Padic.norm_eq_pow_val

@[simp]
theorem valuation_p : valuation (p : ℚ_[p]) = 1 :=
  by
  have h : (1 : ℝ) < p := by exact_mod_cast (Fact.out p.prime).one_lt
  refine' neg_injective ((zpow_strictMono h).Injective <| (norm_eq_pow_val _).symm.trans _)
  · exact_mod_cast (Fact.out p.prime).NeZero
  · simp
#align padic.valuation_p Padic.valuation_p

theorem valuation_map_add {x y : ℚ_[p]} (hxy : x + y ≠ 0) :
    min (valuation x) (valuation y) ≤ valuation (x + y) :=
  by
  by_cases hx : x = 0
  · rw [hx, zero_add]
    exact min_le_right _ _
  · by_cases hy : y = 0
    · rw [hy, add_zero]
      exact min_le_left _ _
    · have h_norm : ‖x + y‖ ≤ max ‖x‖ ‖y‖ := padicNormE.nonarchimedean x y
      have hp_one : (1 : ℝ) < p := by
        rw [← Nat.cast_one, Nat.cast_lt]
        exact Nat.Prime.one_lt hp.elim
      rwa [norm_eq_pow_val hx, norm_eq_pow_val hy, norm_eq_pow_val hxy,
        zpow_le_max_iff_min_le hp_one] at h_norm
#align padic.valuation_map_add Padic.valuation_map_add

@[simp]
theorem valuation_map_mul {x y : ℚ_[p]} (hx : x ≠ 0) (hy : y ≠ 0) :
    valuation (x * y) = valuation x + valuation y :=
  by
  have h_norm : ‖x * y‖ = ‖x‖ * ‖y‖ := norm_mul x y
  have hp_ne_one : (p : ℝ) ≠ 1 :=
    by
    rw [← Nat.cast_one, Ne.def, Nat.cast_inj]
    exact Nat.Prime.ne_one hp.elim
  have hp_pos : (0 : ℝ) < p := by
    rw [← Nat.cast_zero, Nat.cast_lt]
    exact Nat.Prime.pos hp.elim
  rw [norm_eq_pow_val hx, norm_eq_pow_val hy, norm_eq_pow_val (mul_ne_zero hx hy), ←
    zpow_add₀ (ne_of_gt hp_pos), zpow_inj hp_pos hp_ne_one, ← neg_add, neg_inj] at h_norm
  exact h_norm
#align padic.valuation_map_mul Padic.valuation_map_mul

/-- The additive `p`-adic valuation on `ℚ_[p]`, with values in `with_top ℤ`. -/
def addValuationDef : ℚ_[p] → WithTop ℤ := fun x => if x = 0 then ⊤ else x.Valuation
#align padic.add_valuation_def Padic.addValuationDef

@[simp]
theorem AddValuation.map_zero : addValuationDef (0 : ℚ_[p]) = ⊤ := by
  simp only [add_valuation_def, if_pos (Eq.refl _)]
#align padic.add_valuation.map_zero Padic.AddValuation.map_zero

@[simp]
theorem AddValuation.map_one : addValuationDef (1 : ℚ_[p]) = 0 := by
  simp only [add_valuation_def, if_neg one_ne_zero, valuation_one, WithTop.coe_zero]
#align padic.add_valuation.map_one Padic.AddValuation.map_one

theorem AddValuation.map_mul (x y : ℚ_[p]) :
    addValuationDef (x * y) = addValuationDef x + addValuationDef y :=
  by
  simp only [add_valuation_def]
  by_cases hx : x = 0
  · rw [hx, if_pos (Eq.refl _), MulZeroClass.zero_mul, if_pos (Eq.refl _), WithTop.top_add]
  · by_cases hy : y = 0
    · rw [hy, if_pos (Eq.refl _), MulZeroClass.mul_zero, if_pos (Eq.refl _), WithTop.add_top]
    ·
      rw [if_neg hx, if_neg hy, if_neg (mul_ne_zero hx hy), ← WithTop.coe_add, WithTop.coe_eq_coe,
        valuation_map_mul hx hy]
#align padic.add_valuation.map_mul Padic.AddValuation.map_mul

theorem AddValuation.map_add (x y : ℚ_[p]) :
    min (addValuationDef x) (addValuationDef y) ≤ addValuationDef (x + y) :=
  by
  simp only [add_valuation_def]
  by_cases hxy : x + y = 0
  · rw [hxy, if_pos (Eq.refl _)]
    exact le_top
  · by_cases hx : x = 0
    · simp only [hx, if_pos (Eq.refl _), min_eq_right, le_top, zero_add, le_refl]
    · by_cases hy : y = 0
      · simp only [hy, if_pos (Eq.refl _), min_eq_left, le_top, add_zero, le_refl]
      · rw [if_neg hx, if_neg hy, if_neg hxy, ← WithTop.coe_min, WithTop.coe_le_coe]
        exact valuation_map_add hxy
#align padic.add_valuation.map_add Padic.AddValuation.map_add

/-- The additive `p`-adic valuation on `ℚ_[p]`, as an `add_valuation`. -/
def addValuation : AddValuation ℚ_[p] (WithTop ℤ) :=
  AddValuation.of addValuationDef AddValuation.map_zero AddValuation.map_one AddValuation.map_add
    AddValuation.map_mul
#align padic.add_valuation Padic.addValuation

@[simp]
theorem addValuation.apply {x : ℚ_[p]} (hx : x ≠ 0) : x.AddValuation = x.Valuation := by
  simp only [AddValuation, AddValuation.of_apply, add_valuation_def, if_neg hx]
#align padic.add_valuation.apply Padic.addValuation.apply

section NormLeIff

/-! ### Various characterizations of open unit balls -/

theorem norm_le_pow_iff_norm_lt_pow_add_one (x : ℚ_[p]) (n : ℤ) : ‖x‖ ≤ p ^ n ↔ ‖x‖ < p ^ (n + 1) :=
  by
  have aux : ∀ n : ℤ, 0 < (p ^ n : ℝ) :=
    by
    apply Nat.zpow_pos_of_pos
    exact hp.1.Pos
  by_cases hx0 : x = 0
  · simp [hx0, norm_zero, aux, le_of_lt (aux _)]
  rw [norm_eq_pow_val hx0]
  have h1p : 1 < (p : ℝ) := by exact_mod_cast hp.1.one_lt
  have H := zpow_strictMono h1p
  rw [H.le_iff_le, H.lt_iff_lt, Int.lt_add_one_iff]
#align padic.norm_le_pow_iff_norm_lt_pow_add_one Padic.norm_le_pow_iff_norm_lt_pow_add_one

theorem norm_lt_pow_iff_norm_le_pow_sub_one (x : ℚ_[p]) (n : ℤ) : ‖x‖ < p ^ n ↔ ‖x‖ ≤ p ^ (n - 1) :=
  by rw [norm_le_pow_iff_norm_lt_pow_add_one, sub_add_cancel]
#align padic.norm_lt_pow_iff_norm_le_pow_sub_one Padic.norm_lt_pow_iff_norm_le_pow_sub_one

theorem norm_le_one_iff_val_nonneg (x : ℚ_[p]) : ‖x‖ ≤ 1 ↔ 0 ≤ x.Valuation :=
  by
  by_cases hx : x = 0
  · simp only [hx, norm_zero, valuation_zero, zero_le_one, le_refl]
  · rw [norm_eq_pow_val hx, ← zpow_zero (p : ℝ), zpow_le_iff_le, Right.neg_nonpos_iff]
    exact Nat.one_lt_cast.2 (Nat.Prime.one_lt' p).1
#align padic.norm_le_one_iff_val_nonneg Padic.norm_le_one_iff_val_nonneg

end NormLeIff

end Padic


import data.set.basic -- hide
import data.set.finite -- hide
open set -- hide

/- Axiom : finite.induction_on

If P is a property of sets, to prove that P(s) is true for all *finite* sets s
it is enough to prove it for the empty set (h₀) and to prove
(h₁) that if s is a finite set and a ∉ s then P(s) →  P({a} ∪ s).

finite.induction_on {P : set α → Prop} {s : set α} (h : finite s)
  (h₀ : P ∅) (h₁ : ∀ {a s}, a ∉ s → finite s → P s → P (insert a s)) : P s
-/

/- Axiom : mem_insert_iff

We have x ∈ {a} ∪ s if and only if x = a or x ∈ s.

mem_insert_iff {x a : α} {s : set α} : x ∈ insert a s ↔ x = a ∨ x ∈ s
-/

/- Axiom : sInter_empty

The empty intersection is everything.

sInter_empty : ⋂₀ ∅ = univ
-/

/- Axiom : sInter_insert
If T is a set of sets, and s is a set, the set-wise intersection ⋂₀ (s ∪ T) equals s ∩ ⋂₀ T.

sInter_insert (s : set α) (T : set (set α)) : ⋂₀ (insert s T) = s ∩ ⋂₀ T

-/

/-
This is the final level, and be advised that it is *much* harder than the previous ones.

We will use the following lemma when we start proving facts about topological spaces.

It seems clear that we want to use induction, so try to apply the `finite.induction_on`
lemma.

For the base case you will need `sInter_empty`, while for inductive step the lemmas `sInter_insert`, `mem_insert_iff` may be useful. Check the sidebar for their meaning.
-/

variables {X Y : Type} -- hide

/- Lemma :
If P is a property of sets which is closed under pairwise intersection then it is also closed under
arbitrary finite intersections.
-/
lemma sInter_of_inter {S : set (set X)} {P : set X → Prop} (hfin : finite S) (huniv : P univ) (hinter : ∀ A B : set X, P A → P B → P (A ∩ B))
 : (∀ s ∈ S, P s) → P ( sInter S ) :=
begin
  apply finite.induction_on hfin,
  {
    intro h,
    rwa sInter_empty,
  },
  {
    intros U S hUS hSfin hind h,
    rw sInter_insert,
    apply hinter,
    {
      apply h,
      rw mem_insert_iff,
      left, refl,
    },
    {
      apply hind,
      intros s hs,
      apply h,
      rw mem_insert_iff,
      right,
      exact hs,
    }
  },

end


/-
Copyright (c) 2020 Aaron Anderson, Jalex Stark. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Aaron Anderson, Jalex Stark
-/

import linear_algebra.matrix.charpoly.coeff
import linear_algebra.matrix.to_lin
import ring_theory.power_basis

/-!
# The minimal polynomial divides the characteristic polynomial of a matrix.
-/

noncomputable theory

universes u v

open polynomial matrix

variables {R : Type u} [comm_ring R]
variables {n : Type v} [decidable_eq n] [fintype n]

open finset

variable {M : matrix n n R}

namespace matrix

theorem is_integral : is_integral R M := ⟨M.charpoly, ⟨charpoly_monic M, aeval_self_charpoly M⟩⟩

theorem minpoly_dvd_charpoly {K : Type*} [field K] (M : matrix n n K) :
  (minpoly K M) ∣ M.charpoly :=
minpoly.dvd _ _ (aeval_self_charpoly M)

end matrix

section power_basis

open algebra

/-- The characteristic polynomial of the map `λ x, a * x` is the minimal polynomial of `a`.

In combination with `det_eq_sign_charpoly_coeff` or `trace_eq_neg_charpoly_coeff`
and a bit of rewriting, this will allow us to conclude the
field norm resp. trace of `x` is the product resp. sum of `x`'s conjugates.
-/
lemma charpoly_left_mul_matrix {S : Type*} [ring S] [algebra R S] (h : power_basis R S) :
  (left_mul_matrix h.basis h.gen).charpoly = minpoly R h.gen :=
begin
  casesI subsingleton_or_nontrivial R, { apply subsingleton.elim },
  apply minpoly.unique' R h.gen (charpoly_monic _),
  { apply (injective_iff_map_eq_zero (left_mul_matrix _)).mp (left_mul_matrix_injective h.basis),
    rw [← polynomial.aeval_alg_hom_apply, aeval_self_charpoly] },
  refine λ q hq, or_iff_not_imp_left.2 (λ h0, _),
  rw [matrix.charpoly_degree_eq_dim, fintype.card_fin] at hq,
  contrapose! hq, exact h.dim_le_degree_of_root h0 hq,
end

end power_basis


/-
Copyright (c) 2020 Oliver Nash. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Oliver Nash
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.linear_algebra.dual
import Mathlib.PostPort

universes u v

namespace Mathlib

/-!
# Contractions

Given modules $M, N$ over a commutative ring $R$, this file defines the natural linear maps:
$M^* \otimes M \to R$, $M \otimes M^* \to R$, and $M^* \otimes N → Hom(M, N)$, as well as proving
some basic properties of these maps.

## Tags

contraction, dual module, tensor product
-/

/-- The natural left-handed pairing between a module and its dual. -/
def contract_left (R : Type u) (M : Type v) [comm_ring R] [add_comm_group M] [module R M] :
    linear_map R (tensor_product R (module.dual R M) M) R :=
  linear_map.to_fun (tensor_product.uncurry R (module.dual R M) M R) linear_map.id

/-- The natural right-handed pairing between a module and its dual. -/
def contract_right (R : Type u) (M : Type v) [comm_ring R] [add_comm_group M] [module R M] :
    linear_map R (tensor_product R M (module.dual R M)) R :=
  linear_map.to_fun (tensor_product.uncurry R M (module.dual R M) R) (linear_map.flip linear_map.id)

/-- The natural map associating a linear map to the tensor product of two modules. -/
def dual_tensor_hom (R : Type u) (M : Type v) (N : Type v) [comm_ring R] [add_comm_group M]
    [add_comm_group N] [module R M] [module R N] :
    linear_map R (tensor_product R (module.dual R M) N) (linear_map R M N) :=
  let M' : Type (max v u) := module.dual R M;
  coe_fn (tensor_product.uncurry R M' N (linear_map R M N)) linear_map.smul_rightₗ

@[simp] theorem contract_left_apply {R : Type u} {M : Type v} [comm_ring R] [add_comm_group M]
    [module R M] (f : module.dual R M) (m : M) :
    coe_fn (contract_left R M) (tensor_product.tmul R f m) = coe_fn f m :=
  tensor_product.uncurry_apply linear_map.id f m

@[simp] theorem contract_right_apply {R : Type u} {M : Type v} [comm_ring R] [add_comm_group M]
    [module R M] (f : module.dual R M) (m : M) :
    coe_fn (contract_right R M) (tensor_product.tmul R m f) = coe_fn f m :=
  tensor_product.uncurry_apply (linear_map.flip linear_map.id) m f

@[simp] theorem dual_tensor_hom_apply {R : Type u} {M : Type v} {N : Type v} [comm_ring R]
    [add_comm_group M] [add_comm_group N] [module R M] [module R N] (f : module.dual R M) (m : M)
    (n : N) :
    coe_fn (coe_fn (dual_tensor_hom R M N) (tensor_product.tmul R f n)) m = coe_fn f m • n :=
  sorry

end Mathlib


-- 5.7 Using the Simplifier

import data.nat.basic

variables (x y z : ℕ) (p : ℕ → Prop)
variable (h : p (x * y))

example : (x + 0) * (0 + y * 1 + z * 0) = x * y :=
by simp

include h
example : p ((x + 0) * (0 + y * 1 + z *0)) :=
by { simp, assumption}


/-
Copyright (c) 2023 Eric Wieser. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Eric Wieser

! This file was ported from Lean 3 source module analysis.special_functions.trigonometric.series
! leanprover-community/mathlib commit ccf84e0d918668460a34aa19d02fe2e0e2286da0
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Analysis.SpecialFunctions.Exponential

/-!
# Trigonometric functions as sums of infinite series

In this file we express trigonometric functions in terms of their series expansion.

## Main results

* `complex.has_sum_cos`, `complex.tsum_cos`: `complex.cos` as the sum of an infinite series.
* `real.has_sum_cos`, `real.tsum_cos`: `real.cos` as the sum of an infinite series.
* `complex.has_sum_sin`, `complex.tsum_sin`: `complex.sin` as the sum of an infinite series.
* `real.has_sum_sin`, `real.tsum_sin`: `real.sin` as the sum of an infinite series.
-/

open Nat

/-! ### `cos` and `sin` for `ℝ` and `ℂ` -/

section SinCos

theorem Complex.hasSum_cos' (z : ℂ) :
    HasSum (fun n : ℕ => (z * Complex.I) ^ (2 * n) / ↑(2 * n)!) (Complex.cos z) :=
  by
  rw [Complex.cos, Complex.exp_eq_exp_ℂ]
  have :=
    ((exp_series_div_hasSum_exp ℂ (z * Complex.I)).add
          (exp_series_div_hasSum_exp ℂ (-z * Complex.I))).div_const
      2
  replace := (Nat.divModEquiv 2).symm.hasSum_iff.mpr this
  dsimp [Function.comp] at this
  simp_rw [← mul_comm 2 _] at this
  refine' this.prod_fiberwise fun k => _
  dsimp only
  convert hasSum_fintype (_ : Fin 2 → ℂ) using 1
  rw [Fin.sum_univ_two]
  simp_rw [Fin.val_zero, Fin.val_one, add_zero, pow_succ', pow_mul, mul_pow, neg_sq, ← two_mul,
    neg_mul, mul_neg, neg_div, add_right_neg, zero_div, add_zero,
    mul_div_cancel_left _ (two_ne_zero : (2 : ℂ) ≠ 0)]
#align complex.has_sum_cos' Complex.hasSum_cos'

theorem Complex.hasSum_sin' (z : ℂ) :
    HasSum (fun n : ℕ => (z * Complex.I) ^ (2 * n + 1) / ↑(2 * n + 1)! / Complex.I)
      (Complex.sin z) :=
  by
  rw [Complex.sin, Complex.exp_eq_exp_ℂ]
  have :=
    (((exp_series_div_hasSum_exp ℂ (-z * Complex.I)).sub
              (exp_series_div_hasSum_exp ℂ (z * Complex.I))).mul_right
          Complex.I).div_const
      2
  replace := (Nat.divModEquiv 2).symm.hasSum_iff.mpr this
  dsimp [Function.comp] at this
  simp_rw [← mul_comm 2 _] at this
  refine' this.prod_fiberwise fun k => _
  dsimp only
  convert hasSum_fintype (_ : Fin 2 → ℂ) using 1
  rw [Fin.sum_univ_two]
  simp_rw [Fin.val_zero, Fin.val_one, add_zero, pow_succ', pow_mul, mul_pow, neg_sq, sub_self,
    MulZeroClass.zero_mul, zero_div, zero_add, neg_mul, mul_neg, neg_div, ← neg_add', ← two_mul,
    neg_mul, neg_div, mul_assoc, mul_div_cancel_left _ (two_ne_zero : (2 : ℂ) ≠ 0), Complex.div_I]
#align complex.has_sum_sin' Complex.hasSum_sin'

/-- The power series expansion of `complex.cos`. -/
theorem Complex.hasSum_cos (z : ℂ) :
    HasSum (fun n : ℕ => (-1) ^ n * z ^ (2 * n) / ↑(2 * n)!) (Complex.cos z) :=
  by
  convert Complex.hasSum_cos' z using 1
  simp_rw [mul_pow, pow_mul, Complex.I_sq, mul_comm]
#align complex.has_sum_cos Complex.hasSum_cos

/-- The power series expansion of `complex.sin`. -/
theorem Complex.hasSum_sin (z : ℂ) :
    HasSum (fun n : ℕ => (-1) ^ n * z ^ (2 * n + 1) / ↑(2 * n + 1)!) (Complex.sin z) :=
  by
  convert Complex.hasSum_sin' z using 1
  simp_rw [mul_pow, pow_succ', pow_mul, Complex.I_sq, ← mul_assoc, mul_div_assoc, div_right_comm,
    div_self Complex.I_ne_zero, mul_comm _ ((-1 : ℂ) ^ _), mul_one_div, mul_div_assoc, mul_assoc]
#align complex.has_sum_sin Complex.hasSum_sin

theorem Complex.cos_eq_tsum' (z : ℂ) :
    Complex.cos z = ∑' n : ℕ, (z * Complex.I) ^ (2 * n) / ↑(2 * n)! :=
  (Complex.hasSum_cos' z).tsum_eq.symm
#align complex.cos_eq_tsum' Complex.cos_eq_tsum'

theorem Complex.sin_eq_tsum' (z : ℂ) :
    Complex.sin z = ∑' n : ℕ, (z * Complex.I) ^ (2 * n + 1) / ↑(2 * n + 1)! / Complex.I :=
  (Complex.hasSum_sin' z).tsum_eq.symm
#align complex.sin_eq_tsum' Complex.sin_eq_tsum'

theorem Complex.cos_eq_tsum (z : ℂ) :
    Complex.cos z = ∑' n : ℕ, (-1) ^ n * z ^ (2 * n) / ↑(2 * n)! :=
  (Complex.hasSum_cos z).tsum_eq.symm
#align complex.cos_eq_tsum Complex.cos_eq_tsum

theorem Complex.sin_eq_tsum (z : ℂ) :
    Complex.sin z = ∑' n : ℕ, (-1) ^ n * z ^ (2 * n + 1) / ↑(2 * n + 1)! :=
  (Complex.hasSum_sin z).tsum_eq.symm
#align complex.sin_eq_tsum Complex.sin_eq_tsum

/-- The power series expansion of `real.cos`. -/
theorem Real.hasSum_cos (r : ℝ) :
    HasSum (fun n : ℕ => (-1) ^ n * r ^ (2 * n) / ↑(2 * n)!) (Real.cos r) := by
  exact_mod_cast Complex.hasSum_cos r
#align real.has_sum_cos Real.hasSum_cos

/-- The power series expansion of `real.sin`. -/
theorem Real.hasSum_sin (r : ℝ) :
    HasSum (fun n : ℕ => (-1) ^ n * r ^ (2 * n + 1) / ↑(2 * n + 1)!) (Real.sin r) := by
  exact_mod_cast Complex.hasSum_sin r
#align real.has_sum_sin Real.hasSum_sin

theorem Real.cos_eq_tsum (r : ℝ) : Real.cos r = ∑' n : ℕ, (-1) ^ n * r ^ (2 * n) / ↑(2 * n)! :=
  (Real.hasSum_cos r).tsum_eq.symm
#align real.cos_eq_tsum Real.cos_eq_tsum

theorem Real.sin_eq_tsum (r : ℝ) :
    Real.sin r = ∑' n : ℕ, (-1) ^ n * r ^ (2 * n + 1) / ↑(2 * n + 1)! :=
  (Real.hasSum_sin r).tsum_eq.symm
#align real.sin_eq_tsum Real.sin_eq_tsum

end SinCos


import data.int.modeq
import data.nat.modeq
import data.nat.prime
import data.nat.gcd
import algebra.group_power
import data.zmod.quadratic_reciprocity

-- Su, with a lot of help from Kenny, Chris
open nat

--#check @zmodp.fermat_little
-- zmodp.fermat_little : ∀ {p : ℕ} (hp : prime p) {a : zmodp p hp}, a ≠ 0 → a ^ (p - 1) = 1

theorem Ferlit (a p : ℕ) : nat.prime p → (gcd a ↑p) = 1 → a ^ (p - 1) ≡ 1 [ZMOD p] := sorry
-- Chris has done this already! It's in the Xena library, apparently.

theorem pow_mod (a b c : ℤ) (n : ℕ) : a ≡ b [ZMOD c] → (a ^ n) ≡ (b ^ n) [ZMOD c] :=
begin
induction n,
intro a1,
simp,
intro a2,
have q := n_ih a2,
exact int.modeq.modeq_mul a2 q,
end

theorem pow_neg_one (k : ℕ) (p : ℕ) : nat.prime p → p > 2 → ¬ (2 ∣ k) → (-1) ^ k ≡ - 1 [ZMOD p] :=
begin
intros a b,
apply nat.strong_induction_on k,
intros a1 a2 a3,
cases a1,
exact absurd a3 dec_trivial,
cases a1,
refl,
have a4 : ¬2 ∣ a1,
intro,
apply a3,
have a5 : succ (succ a1) = a1 + 2, by simp,
rw a5,
have a6 : 2 ∣ 2 := by simp,
exact dvd_add a_1 a6,
have a5 : succ (succ a1) = a1 + 2, by simp,
rw a5,
have : 0 < 2 , from dec_trivial,
have this1 := add_lt_add_left this a1,
have this0 : a1 + 0 = a1, by simp,
rw this0 at this1,
rw ←a5 at this1,
have this2 := a2 a1 this1 a4,
have this3 := _root_.pow_add (- 1 : ℤ) a1 2,
rw this3,
have End1 : (- 1 : ℤ) ^ 2 = 1 := dec_trivial,
rw End1,
simp,
exact this2,
end

theorem one_ne_neg_one_of_odd (p : ℕ) : nat.prime p → p > 2 → ¬ (1 ≡ -1 [ZMOD p]) := sorry
-- Can be done immediately with Chris' lemma!

theorem nat.pow_pow_mod (x m p : ℕ) : nat.prime p → (↑x ^ 2) ^ m ≡ ↑x ^ (2 * m) [ZMOD p] :=
begin
intro a,
have this := pow_mul (↑x : ℤ) 2 m,
rw this,
end

theorem wannaprove (p : ℕ) : nat.prime p →  p ≡ 3 [ZMOD 4] →  ¬ (∃ (x : ℕ), x ^ 2 + 1 ≡ 0 [ZMOD p]) :=
begin
intros a b c,
cases c with x hx,
have e : p - 1 ≡ p - 1 [ZMOD p] := rfl,
have d := int.modeq.modeq_add e hx,
simp at d,
have g : 1 + (p - 1 + x ^ 2) = p + x ^ 2 := by rw [← add_assoc, nat.add_sub_cancel' a.pos],
have hey1 : p + x ^ 2 = x ^ 2 + p := by simp,
have a1 : p + x ^ 2 - x ^ 2 = x ^ 2 + p - x ^ 2 := by simp,
suffices this3 : p ∣ ((p + x ^ 2) - x ^ 2),
{
begin
have this1 : ↑p ∣ ↑p := dvd_refl _,
have this2 := int.modeq.modeq_zero_iff.2 this1,
have this5 : x ^ 2 ≡ x ^ 2 [ZMOD p]:= by refl,
have h : p + x ^ 2 ≡ x ^ 2 [ZMOD p] :=
begin
have this11 := int.modeq.modeq_add this5 this2,
simp at this11,
exact this11,
end,
have f : gcd x ↑p = 1,
{
  have h1 : gcd x ↑p ∣ x, from gcd_dvd_left _ _,
  have h2 : gcd x ↑p ∣ ↑p, from gcd_dvd_right _ _,
  have h3 : gcd x ↑p ∣ x^2 + 1,
  {
    have hx1 := hx,
    generalize H9 : ↑x ^ 2 + 1 = k1,
    have an1 : ↑x ^ 2 + 1 ≡ k1 [ZMOD p],
    {
      rw ←H9,
      simp,
    },
    have an99 : x ^ 2 + 1 ≡ 0 [MOD ↑p],
    {
    have an9 := int.modeq.coe_nat_modeq_iff.1 hx1,
    simp at an9,
    have re1 := add_comm 1 (x ^ 2),
    have re2 := _root_.pow_two x,
    simp [hx],
    rw ←re2 at an9,
    simpa using an9,
    },
    from dvd_trans h2 (nat.modeq.modeq_zero_iff.1 an99),
  },
  have h4 : x ∣ x^2,
  { have i1 := _root_.pow_two x,
    have i2 : 1 ≤ 2 := dec_trivial,
    have i3 := nat.pow_dvd_pow x i2,
    simp at i3,
    exact i3,
  },
  have h5 : gcd x ↑p ∣ x^2, from dvd_trans h1 h4,
  have h6 : gcd x ↑p ∣ 1, from (nat.dvd_add_iff_right h5).2 h3,
  exact nat.dvd_one.1 h6,
},

have ii : p - 1 ≡ p - 1 [ZMOD p] := by refl,
have i : x ^ 2 ≡ p - 1 [ZMOD p] :=
begin
have a21 := int.modeq.modeq_add hx this2.symm,
 simp at a21,
 have a22 : ↑x ^ 2 + 1 ≡ 1 + ↑x ^ 2 [ZMOD p] := by simp,
have s1 : 1 ≡ 1 [ZMOD p] := by refl,
have s2 : (p - 1) + 1 ≡ p [ZMOD p] := by simp,
have a24 := a21.trans s2.symm,
have a24b := a22.trans a24,
have a23 := int.modeq.modeq_add_cancel_right s1 a24b,
exact a23,
end,
have Heyya := Ferlit x p a f,
have important : p > 2,
{
by_contradiction cont,
have pri := prime.two_le a,
have prii := le_of_not_gt cont,
have priii := le_antisymm pri prii,
have pre := int.modeq.modeq_iff_dvd.1 b,
rw ←priii at pre; exact absurd pre dec_trivial,
},
have i1 : ∃ m : ℕ, (¬ (2 ∣ m)) ∧ (p - 1 = 2 * m),
{
    have Heynow := nat.mod_add_div p 2,
    have H1 : ∀ n, n < 2 → n = 0 ∨ n = 1,
    from dec_trivial,
  have H2 : p % 2 = 0 ∨ p % 2 = 1,
    from H1 (p % 2) (nat.mod_lt p dec_trivial),
  cases H2 with H2 H2,
  { exfalso,
  have extra : (2 : ℤ) ∣ (4 : ℤ) := dec_trivial,
    have wrong := int.modeq.modeq_of_dvd_of_modeq extra b,
    unfold int.modeq at wrong,
    have right : (3 : ℤ) % (2 : ℤ) = 1 := dec_trivial,
    rw right at wrong,
    have isthis := int.coe_nat_mod p 2,
    rw H2 at isthis,
    change (p : ℤ) % ((2 : ℕ) : ℤ) = 1 at wrong,
    rw wrong at isthis,
    exact absurd isthis dec_trivial,
  },
existsi p/2,
split,
intro nine,
have bnew := int.modeq.coe_nat_modeq_iff.1 b,
change p ≡ 3 [MOD 4] at bnew,
change p % 4 = 3 at bnew,
have thing11 := mod_add_div p 4,
rw bnew at thing11,
{begin
rw ← thing11 at nine,
  change 2 ∣ (3 + (2 * 2) * (p / 4)) / 2 at nine,
  rw [mul_assoc, nat.add_mul_div_left, ← nat.dvd_add_iff_left] at nine,
  { exact absurd nine dec_trivial },
  { exact dvd_mul_right _ _ },
  { exact dec_trivial },
end},
rw H2 at Heynow,
generalize H9 : 2 * (p / 2) = k,
rw H9 at Heynow,
have F2 := nat.sub_eq_of_eq_add Heynow.symm,
exact F2,
},
cases i1 with m b2,
cases b2 with c1 c2,
have i2 := int.modeq.modeq_iff_dvd.1 b,
cases i2,
have j1 : x ^ (p - 1) ≡ - 1 [ZMOD p],
{rw c2,
have s1 : 1 ≡ 1 [ZMOD p] := by refl,
have a123 := int.modeq.modeq_sub s1 hx,
simp at a123,
have a321 := int.modeq.modeq_neg a123,
simp at a321,
have a222 : (↑x ^ 2) ^ m ≡ ↑x ^ (2 * m) [ZMOD p], from (@nat.pow_pow_mod x m p a),
have a111 := pow_mod (x ^ 2) (-1) p m a321,
have g1 := pow_neg_one m p a important c1,
have afinal := a111.trans g1,
have a2222 := a222.symm,
exact a2222.trans afinal},
have Heyya1 := Heyya.symm,
have a25 := Heyya1.trans j1,
have ans := one_ne_neg_one_of_odd p a important,
exact absurd a25 ans,
end},
{
rw a1,
have thes: p + x ^ 2 - x ^ 2 = p := @nat.add_sub_cancel p (x ^ 2),
have thees : x ^ 2 + p - x ^ 2 = p + x ^ 2 - x ^ 2 := by simp,
rw ←thees at thes,
rw thes,
},
end


/-
Copyright (c) 2021 Christopher Hoskin. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Christopher Hoskin
-/
import topology.order.basic
import topology.constructions

/-!
# Topological lattices

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

In this file we define mixin classes `has_continuous_inf` and `has_continuous_sup`. We define the
class `topological_lattice` as a topological space and lattice `L` extending `has_continuous_inf`
and `has_continuous_sup`.

## References

* [Gierz et al, A Compendium of Continuous Lattices][GierzEtAl1980]

## Tags

topological, lattice
-/

open filter
open_locale topology

/--
Let `L` be a topological space and let `L×L` be equipped with the product topology and let
`⊓:L×L → L` be an infimum. Then `L` is said to have *(jointly) continuous infimum* if the map
`⊓:L×L → L` is continuous.
-/
class has_continuous_inf (L : Type*) [topological_space L] [has_inf L] : Prop :=
(continuous_inf : continuous (λ p : L × L, p.1 ⊓ p.2))

/--
Let `L` be a topological space and let `L×L` be equipped with the product topology and let
`⊓:L×L → L` be a supremum. Then `L` is said to have *(jointly) continuous supremum* if the map
`⊓:L×L → L` is continuous.
-/
class has_continuous_sup (L : Type*) [topological_space L] [has_sup L] : Prop :=
(continuous_sup : continuous (λ p : L × L, p.1 ⊔ p.2))

@[priority 100] -- see Note [lower instance priority]
instance order_dual.has_continuous_sup
  (L : Type*) [topological_space L] [has_inf L] [has_continuous_inf L] : has_continuous_sup Lᵒᵈ :=
{ continuous_sup := @has_continuous_inf.continuous_inf L _ _ _ }

@[priority 100] -- see Note [lower instance priority]
instance order_dual.has_continuous_inf
  (L : Type*) [topological_space L] [has_sup L] [has_continuous_sup L] : has_continuous_inf Lᵒᵈ :=
{ continuous_inf := @has_continuous_sup.continuous_sup L _ _ _ }

/--
Let `L` be a lattice equipped with a topology such that `L` has continuous infimum and supremum.
Then `L` is said to be a *topological lattice*.
-/
class topological_lattice (L : Type*) [topological_space L] [lattice L]
  extends has_continuous_inf L, has_continuous_sup L

@[priority 100] -- see Note [lower instance priority]
instance order_dual.topological_lattice
  (L : Type*) [topological_space L] [lattice L] [topological_lattice L] :
  topological_lattice Lᵒᵈ := {}

@[priority 100] -- see Note [lower instance priority]
instance linear_order.topological_lattice {L : Type*} [topological_space L] [linear_order L]
  [order_closed_topology L] : topological_lattice L :=
{ continuous_inf := continuous_min, continuous_sup := continuous_max }

variables {L : Type*} [topological_space L]
variables {X : Type*} [topological_space X]

@[continuity] lemma continuous_inf [has_inf L] [has_continuous_inf L] :
  continuous (λp:L×L, p.1 ⊓ p.2) :=
has_continuous_inf.continuous_inf

@[continuity] lemma continuous.inf [has_inf L] [has_continuous_inf L]
  {f g : X → L} (hf : continuous f) (hg : continuous g) :
  continuous (λx, f x ⊓ g x) :=
continuous_inf.comp (hf.prod_mk hg : _)

@[continuity] lemma continuous_sup [has_sup L] [has_continuous_sup L] :
  continuous (λp:L×L, p.1 ⊔ p.2) :=
has_continuous_sup.continuous_sup

@[continuity] lemma continuous.sup [has_sup L] [has_continuous_sup L]
  {f g : X → L} (hf : continuous f) (hg : continuous g) :
  continuous (λx, f x ⊔ g x) :=
continuous_sup.comp (hf.prod_mk hg : _)

lemma filter.tendsto.sup_right_nhds' {ι β} [topological_space β] [has_sup β] [has_continuous_sup β]
  {l : filter ι} {f g : ι → β} {x y : β}
  (hf : tendsto f l (𝓝 x)) (hg : tendsto g l (𝓝 y)) :
  tendsto (f ⊔ g) l (𝓝 (x ⊔ y)) :=
(continuous_sup.tendsto _).comp (tendsto.prod_mk_nhds hf hg)

lemma filter.tendsto.sup_right_nhds {ι β} [topological_space β] [has_sup β] [has_continuous_sup β]
  {l : filter ι} {f g : ι → β} {x y : β}
  (hf : tendsto f l (𝓝 x)) (hg : tendsto g l (𝓝 y)) :
  tendsto (λ i, f i ⊔ g i) l (𝓝 (x ⊔ y)) :=
hf.sup_right_nhds' hg

lemma filter.tendsto.inf_right_nhds' {ι β} [topological_space β] [has_inf β] [has_continuous_inf β]
  {l : filter ι} {f g : ι → β} {x y : β}
  (hf : tendsto f l (𝓝 x)) (hg : tendsto g l (𝓝 y)) :
  tendsto (f ⊓ g) l (𝓝 (x ⊓ y)) :=
(continuous_inf.tendsto _).comp (tendsto.prod_mk_nhds hf hg)

lemma filter.tendsto.inf_right_nhds {ι β} [topological_space β] [has_inf β] [has_continuous_inf β]
  {l : filter ι} {f g : ι → β} {x y : β}
  (hf : tendsto f l (𝓝 x)) (hg : tendsto g l (𝓝 y)) :
  tendsto (λ i, f i ⊓ g i) l (𝓝 (x ⊓ y)) :=
hf.inf_right_nhds' hg


import data.set.basic -- hide
import tactic -- hide
open set -- hide

open set function -- hide

variables {X Y I: Type} -- hide

/- Axiom :
mem_preimage : (a ∈ f ⁻¹' s) ↔ (f a ∈ s)
-/

/- Axiom :
mem_Inter : x ∈ (⋂ i, s i) ↔ ∀ i, x ∈ s i
-/

/-

# Level 3: The preimage of an intersection

To complete this level you will need to use the `mem_preimage` and `mem_Inter`
theorems, which you can find in the side bar.

-/

/- Lemma : no-side-bar
If f is a function and A_i are sets, then f⁻¹(⋂ A_i)=⋂ f⁻¹(A_i)
-/
lemma preimage_intersection_is_intersection (f: X → Y) ( A: I → set Y) : f ⁻¹'(⋂ i, A i)=⋂ i, f ⁻¹'  A i :=
begin
ext y,
split,
{
  intro h,
  rw mem_preimage at h,
  rw mem_Inter at h ⊢,
  intro i,
  rw mem_preimage,
  exact h i,

},
{
  intro h,
  rw mem_Inter at h,
  rw mem_preimage,
  rw mem_Inter,
  intro i,
  rw ← mem_preimage,
  exact h i,

},

end


import GMLInit.Logic.Basic
import GMLInit.Logic.Relation
import GMLInit.Logic.Equivalence

namespace Relation
variable {α} (r : α → α → Prop)

abbrev coRel (x y) := ¬ r y x
abbrev reflRel (x y) := r x y ∨ x = y
abbrev asymRel (x y) := r x y ∧ coRel r x y
abbrev symmRel (x y) := r x y ∧ r y x

class Preorder extends Reflexive r, Transitive r : Prop

def Preorder.infer [Reflexive r] [Transitive r] : Preorder r where
  refl := Reflexive.refl
  trans := Transitive.trans

class PartialOrder extends Preorder r, Antisymmetric r : Prop

def PartialOrder.infer [Reflexive r] [Transitive r] [Antisymmetric r] : PartialOrder r where
  refl := Reflexive.refl
  trans := Transitive.trans
  antisymm := Antisymmetric.antisymm

class TotalPreorder extends Preorder r, Total r : Prop

def TotalPreorder.infer [Reflexive r] [Transitive r] [Total r] : TotalPreorder r where
  refl := Reflexive.refl
  trans := Transitive.trans
  total := Total.total

class TotalOrder extends PartialOrder r, TotalPreorder r : Prop

def TotalOrder.infer [Reflexive r] [Transitive r] [Antisymmetric r] [Total r] : TotalOrder r where
  refl := Reflexive.refl
  trans := Transitive.trans
  antisymm := Antisymmetric.antisymm
  total := Total.total

class Quasiorder extends Transitive r : Prop where
  protected irrefl (x) : ¬ r x x

def Quasiorder.infer [Transitive r] [Irreflexive r] : Quasiorder r where
  trans := Transitive.trans
  irrefl := Irreflexive.irrefl

namespace Quasiorder
variable {r} [self : Quasiorder r]

protected theorem asymm {x y} : r x y → ¬ r y x := by
  intro hxy hyx
  apply self.irrefl x
  exact self.trans hxy hyx

end Quasiorder

instance [self : Quasiorder r] : Irreflexive r := ⟨self.irrefl⟩
instance [self : Quasiorder r] : Asymmetric r := ⟨self.asymm⟩

class LinearQuasiorder extends Quasiorder r, Comparison r : Prop

def LinearQuasiorder.infer [Transitive r] [Irreflexive r] [Comparison r] : LinearQuasiorder r where
  trans := Transitive.trans
  irrefl := Irreflexive.irrefl
  compare := Comparison.compare

class LinearOrder extends LinearQuasiorder r, Connex r : Prop

def LinearOrder.infer [Transitive r] [Irreflexive r] [Comparison r] [Connex r] : LinearOrder r where
  trans := Transitive.trans
  irrefl := Irreflexive.irrefl
  compare := Comparison.compare
  connex := Connex.connex

namespace Preorder
variable {r} [self : Preorder r]

theorem co_irrefl (x) : ¬coRel r x x := by
  exact not_not_intro Reflexive.rfl

theorem co_compare [WeaklyComplementedRel r] {x y} : coRel r x y → ∀ z, coRel r x z ∨ coRel r z y := by
  intro cxy z
  rw [←And.deMorgan]
  intro ⟨hzx, hyz⟩
  absurd cxy
  exact Transitive.trans hyz hzx

theorem as_irrefl (x) : ¬asymRel r x x := by
  intro ⟨_, cxx⟩
  exact co_irrefl x cxx

theorem as_trans {x y z} : asymRel r x y → asymRel r y z → asymRel r x z := by
  intro ⟨hxy, cxy⟩ ⟨hyz, _⟩
  constr
  · exact Transitive.trans hxy hyz
  · intro hzx
    absurd cxy
    exact Transitive.trans hyz hzx

instance : Quasiorder (asymRel r) where
  irrefl := Preorder.as_irrefl
  trans := Preorder.as_trans

end Preorder

namespace PartialOrder
variable {r} [self : PartialOrder r]

theorem co_connex [WeaklyComplementedRel r] {x y} : x ≠ y → coRel r x y ∨ coRel r y x := by
  intro hne
  rw [←And.deMorgan]
  intro ⟨hyx, hxy⟩
  absurd hne
  exact Antisymmetric.antisymm hxy hyx

end PartialOrder

namespace TotalPreorder
variable {r} [self : TotalPreorder r]

theorem coRel_iff_asymRel (x y) : coRel r x y ↔ asymRel r x y := by
  constr
  · intro cxy
    constr
    · cases Total.total (r:=r) x y with
      | inl hxy => exact hxy
      | inr hyx => absurd cxy; exact hyx
    · exact cxy
  · exact And.right

theorem co_trans {x y z} : coRel r x y → coRel r y z → coRel r x z := by
  repeat rw [coRel_iff_asymRel]
  exact Preorder.as_trans

theorem as_compare [WeaklyComplementedRel r] {x y} : asymRel r x y → ∀ z, asymRel r x z ∨ asymRel r z y := by
  intro h z
  rw [←coRel_iff_asymRel] at h
  repeat rw [←coRel_iff_asymRel]
  exact Preorder.co_compare h z

instance : Quasiorder (coRel r) where
  irrefl := Preorder.co_irrefl
  trans := TotalPreorder.co_trans

instance [WeaklyComplementedRel r] : LinearQuasiorder (coRel r) where
  toQuasiorder := inferInstance
  compare := Preorder.co_compare

instance [WeaklyComplementedRel r] : LinearQuasiorder (asymRel r) where
  toQuasiorder := inferInstance
  compare := TotalPreorder.as_compare

end TotalPreorder

namespace TotalOrder
variable {r} [self : TotalOrder r]

theorem coRel_iff_rel_and_ne (x y) : coRel r x y ↔ r x y ∧ x ≠ y := by
  constr
  · intro cxy
    constr
    · cases Total.total (r:=r) x y with
      | inl hxy => exact hxy
      | inr hyx => absurd cxy; exact hyx
    · intro | rfl => absurd cxy; exact Reflexive.rfl
  · intro ⟨hxy, hne⟩ hyx
    absurd hne
    exact Antisymmetric.antisymm hxy hyx

instance [WeaklyComplementedRel r] : LinearOrder (coRel r) where
  toLinearQuasiorder := inferInstance
  connex := PartialOrder.co_connex

end TotalOrder

namespace Quasiorder
variable {r} [self : Quasiorder r]

theorem co_refl (x) : coRel r x x := Irreflexive.irrfl

theorem rc_refl (x) : reflRel r x x := by right; rfl

theorem rc_trans {x y z} : reflRel r x y → reflRel r y z → reflRel r x z := by
  intro hxy hyz
  match hxy, hyz with
  | Or.inr rfl, Or.inr rfl => right; rfl
  | Or.inr rfl, Or.inl hyz => left; exact hyz
  | Or.inl hxy, Or.inr rfl => left; exact hxy
  | Or.inl hxy, Or.inl hyz => left; exact Transitive.trans hxy hyz

theorem rc_antisymm {x y} : reflRel r x y → reflRel r y x → x = y := by
  intro hxy hyx
  match hxy, hyx with
  | Or.inr rfl, _ => rfl
  | _, Or.inr rfl => rfl
  | Or.inl hxy, Or.inl hyx =>
    absurd hxy
    exact Asymmetric.asymm hyx

instance : PartialOrder (reflRel r) where
  refl := Quasiorder.rc_refl
  trans := Quasiorder.rc_trans
  antisymm := Quasiorder.rc_antisymm

end Quasiorder

namespace LinearQuasiorder
variable {r} [self : LinearQuasiorder r]

theorem co_trans {x y z} : coRel r x y → coRel r y z → coRel r x z := by
  intro cxy cyz rzx
  cases Comparison.compare rzx y with
  | inl rzy => exact cyz rzy
  | inr ryx => exact cxy ryx

theorem co_total [ComplementedRel r] (x y) : coRel r x y ∨ coRel r y x := by
  match inferComplemented (r x y) with
  | .isTrue hxy => left; exact Asymmetric.asymm hxy
  | .isFalse nxy => right; exact nxy

instance : Preorder (coRel r) where
  refl := Quasiorder.co_refl
  trans := LinearQuasiorder.co_trans

instance [ComplementedRel r] : TotalPreorder (coRel r) where
  refl := Quasiorder.co_refl
  trans := LinearQuasiorder.co_trans
  total := LinearQuasiorder.co_total

end LinearQuasiorder

namespace LinearOrder
variable {r} [self : LinearOrder r]

theorem co_antisymm [StableEq α] {x y} : coRel r x y → coRel r y x → x = y := by
  intro cxy cyx
  by_contradiction
  | assuming hne =>
    cases Connex.connex (r:=r) hne with
    | inl hxy => absurd hxy; exact cyx
    | inr hyx => absurd hyx; exact cxy

theorem coRel_iff_reflRel [StableEq α] [ComplementedRel r] (x y) : coRel r x y ↔ reflRel r x y := by
  constr
  · intro cxy
    match inferComplemented (r x y) with
    | .isTrue hxy => left; exact hxy
    | .isFalse cyx =>
      right
      by_contradiction
      | assuming hne =>
        cases Connex.connex (r:=r) hne with
        | inl hxy => absurd cyx; exact hxy
        | inr hyx => absurd cxy; exact hyx
  · intro
    | .inr rfl => exact Quasiorder.co_refl x
    | .inl hxy => exact Asymmetric.asymm hxy

theorem rc_total [ComplementedEq α] (x y) : reflRel r x y ∨ reflRel r y x := by
  match inferComplemented (x = y) with
  | .isTrue rfl => left; right; rfl
  | .isFalse hne =>
    cases Connex.connex (r:=r) hne with
    | inl hxy => left; left; exact hxy
    | inr hyx => right; left; exact hyx

instance [StableEq α] : PartialOrder (coRel r) where
  toPreorder := inferInstance
  antisymm := co_antisymm

instance [ComplementedRel r] [StableEq α] : TotalOrder (coRel r) where
  toPartialOrder := inferInstance
  total := LinearQuasiorder.co_total

instance [ComplementedEq α] : TotalOrder (reflRel r) where
  toPartialOrder := inferInstance
  total := LinearOrder.rc_total

end LinearOrder

end Relation


/-
Copyright (c) 2022 Flemming Hermansen.
Released under Apache 2.0 license as described in
https://www.apache.org/licenses/LICENSE-2.0
Authors: Flemming Hermansen
-/

import tactic
import tactic.basic
import field_theory.separable

/-
This file defines the property 'is_rel_inv a b' for elements in a semigroup.
This property means that a is the inverse relatively to a unique idempotent element (a*b).
The definition does not mention a zero element, so it does not need a semiring structure.
Zero has zero as its relative inverse.
Non-zero nilpotent elements can not have a relative inverse.
Properties of the relative inverse if it exists:
The relation is commutative. A relative inverse element is unique if it exists.
Being relative inverses transfers along any possibly non-unital homomorphism.
All elements in a field have relativerse elements.
Products of semigroups preserve the property that all the elements have relative inverses.

Later it will be shown that
all algebraic separable elements in a commutative algebra over a field have
relative inverses.

Relative inverses can be used to prove that certain principal ideals are
isomorphic to fields.

Another application is to prove that certain ideals are maximal by producing a one-element
if the ideals are extended.
-/

section inv_a

open_locale classical
open_locale big_operators
noncomputable theory

def is_rel_inv {A : Type} [comm_semigroup A] (a b : A) :=
-- let e := (a * b) in
(a * b) * (a * b) = (a * b) ∧ -- (a * b) is idempotent and b = inv a is the relative inverse
(a * b) * a = a ∧ -- the idempotent acts as a one-element for a
(a * b) * b = b. -- the idempotent acts as a one-element for b
-- if a = 0 then b = inv a = 0 and (a * b) = 0, but 0 is not mentioned explicitly

def all_rel_inv (A : Type*) [comm_semigroup A] : Prop :=
∀ (a : A), ∃ (b : A), is_rel_inv a b

lemma rel_inv_comm {A : Type} [comm_semigroup A] {a b : A} : is_rel_inv a b → is_rel_inv b a :=
begin
  unfold is_rel_inv,
  intros h, cases h with he h, cases h with ha hb,
  simp only [mul_comm b _],
  split, assumption,  split, assumption, assumption,
end

lemma rel_inv_is_unique {A : Type} [comm_semigroup A] (a b b' : A)
: is_rel_inv a b ∧ is_rel_inv a b' → b = b' :=
begin
  intros h, cases h with hb hb',
  unfold is_rel_inv at hb, unfold is_rel_inv at hb',
  rcases hb with ⟨he, hea, heb⟩, rcases hb' with ⟨he', hea', heb'⟩,
  have e_eq : a * b = a * b',
  begin
    calc a * b = ((a * b') * a ) * b : by rw hea'
    ... = ((a * b) * a) * b' : by simp only [mul_comm, mul_assoc]
    ... = a * b' : by rw hea
  end,
  have b_eq_b' : b = b',
  begin
    calc b = (a * b) * b : by rw heb
    ... =(a * b') * b : by rw e_eq
    ... = (a * b) * b' : by simp only [mul_comm, mul_assoc]
    ... = (a * b') * b' : by rw e_eq
    ... = b' : by rw heb'
  end,
  assumption,
end

lemma inv_invariant_hom_a_b {A : Type} [comm_semigroup A] {A' : Type} [comm_semigroup A']
(h : A →ₙ* A') { a b : A} : is_rel_inv a b → is_rel_inv (h.to_fun a) (h.to_fun b) :=
begin
  -- →ₙ* means a non_unital_homomorphism
  intros hl1,
  rcases hl1 with ⟨he, ha, hb⟩,
  unfold is_rel_inv,
  have mul1 : ∀ (x y : A), h.to_fun (x * y) = h.to_fun x * h.to_fun y,
    by exact h.map_mul,
  -- h_map_mul': ∀ (x y : A), h_to_fun (x * y) = h_to_fun x * h_to_fun y
  repeat {rw ← (mul1 _ _)},
  rw he, rw ha, rw hb,
  split, refl, split, refl, refl,
end

lemma inv_invariant_a_b {A : Type} [comm_semigroup A] {A' : Type} [comm_semigroup A']
(h : A ≃* A') { b : A} {a' : A'}:
(h.inv_fun a' * b) * (h.inv_fun a' * b) = (h.inv_fun a' * b) ∧
(h.inv_fun a' * b) * h.inv_fun a' = h.inv_fun a' ∧
(h.inv_fun a' * b) * b = b →
(a' * h.to_fun b) * (a' * h.to_fun b) = (a' * h.to_fun b) ∧
(a' * h.to_fun b) * a' = a' ∧
(a' * h.to_fun b) * h.to_fun b = h.to_fun b :=
begin
  intros hl1,
  rcases hl1 with ⟨he, ha, hb⟩,
  have ha' : a' = h.to_fun (h.inv_fun a'),
  begin
    rw ← (h.right_inv a'), simp,
  end,
  rw ha',
  have mul1 : ∀ (x y : A), h.to_fun (x * y) = h.to_fun x * h.to_fun y,
    by exact h.map_mul,
  -- h_map_mul': ∀ (x y : A), h_to_fun (x * y) = h_to_fun x * h_to_fun y
  repeat {rw ← (mul1 _ _)},
  rw he, rw ha, rw hb,
  split, refl, split, refl, refl,
end

lemma rel_inv_invariant_isomorphism' {A : Type} [comm_semigroup A] {A' : Type} [comm_semigroup A']
(h : A ≃* A') : all_rel_inv A → all_rel_inv A' :=
begin
  unfold all_rel_inv, unfold is_rel_inv,
  intros hl, intros a',
  specialize hl (h.inv_fun a'), cases hl with b hl1,
  use  h.to_fun b,
  apply inv_invariant_a_b h, assumption,
end

lemma field_all_rel_inv {A : Type} [field A] : all_rel_inv A :=
begin
  unfold all_rel_inv, unfold is_rel_inv,
  intros a,
  by_cases ha0 : a=0,
  {rw ha0,simp}, -- a = 0
  -- now ha0 : a ≠ 0
  have ha_div_a : a * (1/a) = 1, by exact mul_div_cancel' 1 ha0,
  use 1/a, rw ha_div_a, ring_nf, simp,
end

-- Products preserve all_rel_inv
lemma product_all_rel_inv {A : Type*} [comm_semigroup A] {A' : Type*} [comm_semigroup A']
  : ( all_rel_inv A) → ( all_rel_inv A')  → all_rel_inv (A × A') :=
  begin
    unfold all_rel_inv is_rel_inv,
    intros hA hA' c,
    cases c with a a', specialize hA a, specialize hA' a',
    cases hA with b hA, cases hA with he hA, cases hA with ha hb,
    cases hA' with b' hA', cases hA' with he' hA', cases hA' with ha' hb',
    use ⟨ b, b' ⟩,
    split, simp, exact and.intro he he',
    split, simp, exact and.intro ha ha',
    simp, exact and.intro hb hb',
  end

  end inv_a


import computability.regular_expressions
import computability.language

open list

universes u
variables {α β γ : Type*}

namespace regular_expressions

def prod : list (regular_expression α) → regular_expression α
| [] := 1
| (x :: xs) := x * prod xs

@[simp]
lemma prod_nil : prod ([] : list (regular_expression α)) = 1 := by refl

@[simp]
lemma prod_cons {x : regular_expression α} {xs} : prod (x :: xs) = x * prod xs := by refl

lemma mathces_prod {x : list (regular_expression α)} :
(prod x).matches = list.prod (map (λ x : regular_expression α, x.matches) x) :=
begin
  induction x,
  simp,
  simp,
  rw x_ih,
end

@[simp]
lemma matches_prod_cons {x : regular_expression α} {xs} :
(prod (x :: xs)).matches = x.matches * (prod xs).matches := by simp

@[simp]
lemma matches_prod_append {x y :  list (regular_expression α)} :
(prod (x ++ y)).matches = (prod x).matches * (prod y).matches :=
begin
  induction x,
  simp,
  simp,
  rw [x_ih, ← mul_assoc],
end

@[simp]
lemma map_prod {f : α → β} {x : list (regular_expression α)} :
regular_expression.map f (prod x) = prod (map (λ x, regular_expression.map f x) x) :=
begin
  induction x,
  simp,
  simp,
  rw x_ih,
end

end regular_expressions


import ...mathlib.logic.basic

lemma or_iff_or (p p' q q' : Prop) :
  (p ↔ p') → (q ↔ q') → ((p ∨ q) ↔ (p' ∨ q')) :=
begin
  intros hp hq, rewrite hp, rewrite hq
end

lemma and_iff_and (p p' q q' : Prop) :
  (p ↔ p') → (q ↔ q') → ((p ∧ q) ↔ (p' ∧ q')) :=
begin
  intros hp hq, rewrite hp, rewrite hq
end

lemma forall_iff_not_exists_not {α : Type} {p : α → Prop} :
   (∀ x, p x) ↔ (¬ ∃ x, ¬ p x) :=
by rewrite (@not_exists_not _ p (λ x, classical.dec _))


import Aesop
import Mathlib.Data.Nat.Order.Basic

/-!

## An example of `Aesop` proof search

The lemma `Nat.max_eq_zero_iff` in `Mathlib` states that if the
maximum of two natural numbers is zero, then one of them must be zero.

This is a straightforward proof that crucially rests on the fact that
the natural numbers form a total order, i.e., that `∀ m n : ℕ, m ≤ n ∨ m ≥ n`.

This file demonstrates how the `Mathlib` proof, which is around `9` lines long,
can be automated by an `aesop` invocation with the appropriate configuration.

This is an example of how a verbose proof can be replaced by
a proof search with just the core details of the proof specified.
-/

-- As the lemma in `Mathlib` is tagged with `simp`,
-- `Aesop` can find the proof instantly if this attribute is not removed.
attribute [-simp] Nat.max_eq_zero_iff

theorem max_eq_zero_iff : max m n = 0 ↔ m = 0 ∧ n = 0 := by
  constructor
  · intro h
    cases' le_total m n with H H
    · simp only [H, max_eq_right] at h
      exact ⟨le_antisymm (H.trans h.le) (zero_le _), h⟩
    · simp only [H, max_eq_left] at h
      exact ⟨h, le_antisymm (H.trans h.le) (zero_le _)⟩
  · rintro ⟨rfl, rfl⟩
    simp

theorem max_eq_zero_iff' : max m n = 0 ↔ m = 0 ∧ n = 0 := by
  aesop (add unsafe forward [le_total])


/-
Copyright (c) 2019 Tim Baanen. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Tim Baanen
-/
import algebra.associated
import linear_algebra.determinant
import tactic.linarith
import tactic.ring_exp

/-!
# Nonsingular inverses

In this file, we define an inverse for square matrices of invertible
determinant. For matrices that are not square or not of full rank, there is a
more general notion of pseudoinverses which we do not consider here.

The definition of inverse used in this file is the adjugate divided by the determinant.
The adjugate is calculated with Cramer's rule, which we introduce first.
The vectors returned by Cramer's rule are given by the linear map `cramer`,
which sends a matrix `A` and vector `b` to the vector consisting of the
determinant of replacing the `i`th column of `A` with `b` at index `i`
(written as `(A.update_column i b).det`).
Using Cramer's rule, we can compute for each matrix `A` the matrix `adjugate A`.
The entries of the adjugate are the determinants of each minor of `A`.
Instead of defining a minor to be `A` with row `i` and column `j` deleted, we
replace the `i`th row of `A` with the `j`th basis vector; this has the same
determinant as the minor but more importantly equals Cramer's rule applied
to `A` and the `j`th basis vector, simplifying the subsequent proofs.
We prove the adjugate behaves like `det A • A⁻¹`. Finally, we show that dividing
the adjugate by `det A` (if possible), giving a matrix `nonsing_inv A`, will
result in a multiplicative inverse to `A`.

## References

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

## Tags

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

namespace matrix
universes u v
variables {n : Type u} [decidable_eq n] [fintype n] {α : Type v} [comm_ring α]
open_locale matrix big_operators
open equiv equiv.perm finset

section cramer
/-!
  ### `cramer` section

  Introduce the linear map `cramer` with values defined by `cramer_map`.
  After defining `cramer_map` and showing it is linear,
  we will restrict our proofs to using `cramer`.
-/
variables (A : matrix n n α) (b : n → α)

/--
  `cramer_map A b i` is the determinant of the matrix `A` with column `i` replaced with `b`,
  and thus `cramer_map A b` is the vector output by Cramer's rule on `A` and `b`.

  If `A ⬝ x = b` has a unique solution in `x`, `cramer_map A` sends the vector `b` to `A.det • x`.
  Otherwise, the outcome of `cramer_map` is well-defined but not necessarily useful.
-/
def cramer_map (i : n) : α := (A.update_column i b).det

lemma cramer_map_is_linear (i : n) : is_linear_map α (λ b, cramer_map A b i) :=
{ map_add := det_update_column_add _ _,
  map_smul := det_update_column_smul _ _ }

lemma cramer_is_linear : is_linear_map α (cramer_map A) :=
begin
  split; intros; ext i,
  { apply (cramer_map_is_linear A i).1 },
  { apply (cramer_map_is_linear A i).2 }
end

/--
  `cramer A b i` is the determinant of the matrix `A` with column `i` replaced with `b`,
  and thus `cramer A b` is the vector output by Cramer's rule on `A` and `b`.

  If `A ⬝ x = b` has a unique solution in `x`, `cramer A` sends the vector `b` to `A.det • x`.
  Otherwise, the outcome of `cramer` is well-defined but not necessarily useful.
 -/
def cramer (A : matrix n n α) : (n → α) →ₗ[α] (n → α) :=
is_linear_map.mk' (cramer_map A) (cramer_is_linear A)

lemma cramer_apply (i : n) : cramer A b i = (A.update_column i b).det := rfl

lemma cramer_transpose_row_self (i : n) :
  Aᵀ.cramer (A i) = λ j, ite (i = j) A.det 0 :=
begin
  ext j,
  rw cramer_apply,
  by_cases h : i = j,
  { -- i = j: this entry should be `A.det`
    rw [update_column_transpose, det_transpose], simp [update_row, h], },
  { -- i ≠ j: this entry should be 0
    rw [if_neg h, update_column_transpose, det_transpose],
    apply det_zero_of_row_eq h,
    rw [update_row_self, update_row_ne],
    apply h }
end

/-- Use linearity of `cramer` to take it out of a summation. -/
lemma sum_cramer {β} (s : finset β) (f : β → n → α) :
  ∑ x in s, cramer A (f x) = cramer A (∑ x in s, f x) :=
(linear_map.map_sum (cramer A)).symm

/-- Use linearity of `cramer` and vector evaluation to take `cramer A _ i` out of a summation. -/
lemma sum_cramer_apply {β} (s : finset β) (f : n → β → α) (i : n) :
∑ x in s, cramer A (λ j, f j x) i = cramer A (λ (j : n), ∑ x in s, f j x) i :=
calc ∑ x in s, cramer A (λ j, f j x) i
    = (∑ x in s, cramer A (λ j, f j x)) i : (finset.sum_apply i s _).symm
... = cramer A (λ (j : n), ∑ x in s, f j x) i :
  by { rw [sum_cramer, cramer_apply], congr' with j, apply finset.sum_apply }

end cramer

section adjugate
/-!
### `adjugate` section

Define the `adjugate` matrix and a few equations.
These will hold for any matrix over a commutative ring,
while the `inv` section is specifically for invertible matrices.
-/

/-- The adjugate matrix is the transpose of the cofactor matrix.

  Typically, the cofactor matrix is defined by taking the determinant of minors,
  i.e. the matrix with a row and column removed.
  However, the proof of `mul_adjugate` becomes a lot easier if we define the
  minor as replacing a column with a basis vector, since it allows us to use
  facts about the `cramer` map.
-/
def adjugate (A : matrix n n α) : matrix n n α := λ i, cramer Aᵀ (λ j, if i = j then 1 else 0)

lemma adjugate_def (A : matrix n n α) :
  adjugate A = λ i, cramer Aᵀ (λ j, if i = j then 1 else 0) := rfl

lemma adjugate_apply (A : matrix n n α) (i j : n) :
  adjugate A i j = (A.update_row j (λ j, if i = j then 1 else 0)).det :=
by { rw adjugate_def, simp only, rw [cramer_apply, update_column_transpose, det_transpose], }

lemma adjugate_transpose (A : matrix n n α) : (adjugate A)ᵀ = adjugate (Aᵀ) :=
begin
  ext i j,
  rw [transpose_apply, adjugate_apply, adjugate_apply, update_row_transpose, det_transpose],
  rw [det_apply', det_apply'],
  apply finset.sum_congr rfl,
  intros σ _,
  congr' 1,

  by_cases i = σ j,
  { -- Everything except `(i , j)` (= `(σ j , j)`) is given by A, and the rest is a single `1`.
    congr; ext j',
    have := (@equiv.injective _ _ σ j j' : σ j = σ j' → j = j'),
    rw [update_row_apply, update_column_apply],
    finish },
  { -- Otherwise, we need to show that there is a `0` somewhere in the product.
    have : (∏ j' : n, update_column A j (λ (i' : n), ite (i = i') 1 0) (σ j') j') = 0,
    { apply prod_eq_zero (mem_univ j),
      rw [update_column_self],
      exact if_neg h },
    rw this,
    apply prod_eq_zero (mem_univ (σ⁻¹ i)),
    erw [apply_symm_apply σ i, update_row_self],
    apply if_neg,
    intro h',
    exact h ((symm_apply_eq σ).mp h'.symm) }
end

/-- Since the map `b ↦ cramer A b` is linear in `b`, it must be multiplication by some matrix. This
matrix is `A.adjugate`. -/
lemma cramer_eq_adjugate_mul_vec (A : matrix n n α) (b : n → α) :
  cramer A b = A.adjugate.mul_vec b :=
begin
  nth_rewrite 1 ← A.transpose_transpose,
  rw [← adjugate_transpose, adjugate_def],
  have : b = ∑ i, (b i) • (λ j, if i = j then 1 else 0), { ext i, simp, },
  rw this, ext k,
  simp [mul_vec, dot_product, mul_comm],
end

lemma mul_adjugate_apply (A : matrix n n α) (i j k) :
  A i k * adjugate A k j = cramer Aᵀ (λ j, if k = j then A i k else 0) j :=
begin
  erw [←smul_eq_mul, ←pi.smul_apply, ←linear_map.map_smul],
  congr' with l,
  rw [pi.smul_apply, smul_eq_mul, mul_boole],
end

lemma mul_adjugate (A : matrix n n α) : A ⬝ adjugate A = A.det • 1 :=
begin
  ext i j,
  rw [mul_apply, smul_apply, one_apply, mul_boole],
  simp [mul_adjugate_apply, sum_cramer_apply, cramer_transpose_row_self],
end

lemma adjugate_mul (A : matrix n n α) : adjugate A ⬝ A = A.det • 1 :=
calc adjugate A ⬝ A = (Aᵀ ⬝ (adjugate Aᵀ))ᵀ :
  by rw [←adjugate_transpose, ←transpose_mul, transpose_transpose]
... = A.det • 1 : by rw [mul_adjugate (Aᵀ), det_transpose, transpose_smul, transpose_one]

/-- `det_adjugate_of_cancel` is an auxiliary lemma for computing `(adjugate A).det`,
  used in `det_adjugate_eq_one` and `det_adjugate_of_is_unit`.

  The formula for the determinant of the adjugate of an `n` by `n` matrix `A`
  is in general `(adjugate A).det = A.det ^ (n - 1)`, but the proof differs in several cases.
  This lemma `det_adjugate_of_cancel` covers the case that `det A` cancels
  on the left of the equation `A.det * b = A.det ^ n`.
-/
lemma det_adjugate_of_cancel {A : matrix n n α}
  (h : ∀ b, A.det * b = A.det ^ fintype.card n → b = A.det ^ (fintype.card n - 1)) :
  (adjugate A).det = A.det ^ (fintype.card n - 1) :=
h (adjugate A).det (calc A.det * (adjugate A).det = (A ⬝ adjugate A).det   : (det_mul _ _).symm
                                              ... = A.det ^ fintype.card n : by simp [mul_adjugate])

lemma adjugate_eq_one_of_card_eq_one {A : matrix n n α} (h : fintype.card n = 1) : adjugate A = 1 :=
begin
  haveI : subsingleton n := fintype.card_le_one_iff_subsingleton.mp h.le,
  ext i j,
  simp [subsingleton.elim i j, adjugate_apply, det_eq_elem_of_card_eq_one h j],
end

@[simp] lemma adjugate_zero (h : 1 < fintype.card n) : adjugate (0 : matrix n n α) = 0 :=
begin
  ext i j,
  obtain ⟨j', hj'⟩ : ∃ j', j' ≠ j := fintype.exists_ne_of_one_lt_card h j,
  apply det_eq_zero_of_column_eq_zero j',
  intro j'',
  simp [update_column_ne hj'],
end

lemma det_adjugate_eq_one {A : matrix n n α} (h : A.det = 1) : (adjugate A).det = 1 :=
calc (adjugate A).det
    = A.det ^ (fintype.card n - 1) : det_adjugate_of_cancel (λ b hb, by simpa [h] using hb)
... = 1                            : by rw [h, one_pow]

/-- `det_adjugate_of_is_unit` gives the formula for `(adjugate A).det` if `A.det` has an inverse.

  The formula for the determinant of the adjugate of an `n` by `n` matrix `A`
  is in general `(adjugate A).det = A.det ^ (n - 1)`, but the proof differs in several cases.
  This lemma `det_adjugate_of_is_unit` covers the case that `det A` has an inverse.
-/
lemma det_adjugate_of_is_unit {A : matrix n n α} (h : is_unit A.det) :
  (adjugate A).det = A.det ^ (fintype.card n - 1) :=
begin
  rcases is_unit_iff_exists_inv'.mp h with ⟨a, ha⟩,
  by_cases card_lt_zero : fintype.card n ≤ 0,
  { have h : fintype.card n = 0 := by linarith,
    simp [det_eq_one_of_card_eq_zero h] },
  have zero_lt_card : 0 < fintype.card n := by linarith,
  have n_nonempty : nonempty n := fintype.card_pos_iff.mp zero_lt_card,

  by_cases card_lt_one : fintype.card n ≤ 1,
  { have h : fintype.card n = 1 := by linarith,
    simp [h, adjugate_eq_one_of_card_eq_one h] },
  have one_lt_card : 1 < fintype.card n := by linarith,
  have zero_lt_card_sub_one : 0 < fintype.card n - 1 :=
    (nat.sub_lt_sub_right_iff (refl 1)).mpr one_lt_card,

  apply det_adjugate_of_cancel,
  intros b hb,
  calc b = a * (det A ^ (fintype.card n - 1 + 1)) :
       by rw [←one_mul b, ←ha, mul_assoc, hb, nat.sub_add_cancel zero_lt_card]
     ... = a * det A * det A ^ (fintype.card n - 1) : by ring_exp
     ... = det A ^ (fintype.card n - 1) : by rw [ha, one_mul]
end

end adjugate

section inv
/-!
### `inv` section

Defines the matrix `nonsing_inv A` and proves it is the inverse matrix
of a square matrix `A` as long as `det A` has a multiplicative inverse.
-/

variables (A : matrix n n α)

open_locale classical

lemma is_unit_det_transpose (h : is_unit A.det) : is_unit Aᵀ.det :=
by { rw det_transpose, exact h, }

/-- The inverse of a square matrix, when it is invertible (and zero otherwise).-/
noncomputable def nonsing_inv : matrix n n α :=
if h : is_unit A.det then (↑h.unit⁻¹ : α) • A.adjugate else 0

noncomputable instance : has_inv (matrix n n α) := ⟨matrix.nonsing_inv⟩

lemma nonsing_inv_apply (h : is_unit A.det) :
  A⁻¹ = (↑h.unit⁻¹ : α) • A.adjugate :=
by { change A.nonsing_inv = _, dunfold nonsing_inv, simp only [dif_pos, h], }

lemma transpose_nonsing_inv (h : is_unit A.det) :
  (A⁻¹)ᵀ = (Aᵀ)⁻¹ :=
begin
  have h' := A.is_unit_det_transpose h,
  have dets_eq : (↑h.unit : α) = ↑h'.unit := by rw [h.unit_spec, h'.unit_spec, det_transpose],
  rw [A.nonsing_inv_apply h, Aᵀ.nonsing_inv_apply h',
      units.inv_unique dets_eq, A.adjugate_transpose.symm],
  refl,
end

/-- The `nonsing_inv` of `A` is a right inverse. -/
@[simp] lemma mul_nonsing_inv (h : is_unit A.det) : A ⬝ A⁻¹ = 1 :=
by rw [A.nonsing_inv_apply h, mul_smul, mul_adjugate, smul_smul, units.inv_mul_of_eq h.unit_spec,
       one_smul]

/-- The `nonsing_inv` of `A` is a left inverse. -/
@[simp] lemma nonsing_inv_mul (h : is_unit A.det) : A⁻¹ ⬝ A = 1 :=
calc A⁻¹ ⬝ A = (Aᵀ ⬝ (Aᵀ)⁻¹)ᵀ : by { rw [transpose_mul,
                                    Aᵀ.transpose_nonsing_inv (A.is_unit_det_transpose h),
                                    transpose_transpose], }
         ... = 1ᵀ             : by { rw Aᵀ.mul_nonsing_inv, exact A.is_unit_det_transpose h, }
         ... = 1              : transpose_one

@[simp] lemma nonsing_inv_det (h : is_unit A.det) : A⁻¹.det * A.det = 1 :=
by rw [←det_mul, A.nonsing_inv_mul h, det_one]

lemma is_unit_nonsing_inv_det (h : is_unit A.det) : is_unit A⁻¹.det :=
is_unit_of_mul_eq_one _ _ (A.nonsing_inv_det h)

@[simp] lemma nonsing_inv_nonsing_inv (h : is_unit A.det) : (A⁻¹)⁻¹ = A :=
calc (A⁻¹)⁻¹ = 1 ⬝ (A⁻¹)⁻¹        : by rw matrix.one_mul
         ... = A ⬝ A⁻¹ ⬝ (A⁻¹)⁻¹  : by rw A.mul_nonsing_inv h
         ... = A                  : by { rw [matrix.mul_assoc,
                                         (A⁻¹).mul_nonsing_inv (A.is_unit_nonsing_inv_det h),
                                         matrix.mul_one], }

/-- A matrix whose determinant is a unit is itself a unit. -/
noncomputable def nonsing_inv_unit (h : is_unit A.det) : units (matrix n n α) :=
{ val     := A,
  inv     := A⁻¹,
  val_inv := by { rw matrix.mul_eq_mul, apply A.mul_nonsing_inv h, },
  inv_val := by { rw matrix.mul_eq_mul, apply A.nonsing_inv_mul h, } }

lemma is_unit_iff_is_unit_det : is_unit A ↔ is_unit A.det :=
begin
  split; intros h,
  { -- is_unit A → is_unit A.det
    suffices : ∃ (B : matrix n n α), A ⬝ B = 1,
    { rcases this with ⟨B, hB⟩, apply is_unit_of_mul_eq_one _ B.det, rw [←det_mul, hB, det_one], },
    refine ⟨↑h.unit⁻¹, _⟩, conv_lhs { congr, rw ←h.unit_spec, }, exact h.unit.mul_inv, },
  { -- is_unit A.det → is_unit A
    exact (A.nonsing_inv_unit h).is_unit, },
end

lemma is_unit_det_of_left_inverse (B : matrix n n α) (h : B ⬝ A = 1) : is_unit A.det :=
⟨{ val     := A.det,
    inv     := B.det,
    val_inv := by rw [mul_comm, ← det_mul, h, det_one],
    inv_val := by rw [← det_mul, h, det_one],
}, rfl⟩

lemma is_unit_det_of_right_inverse (B : matrix n n α) (h : A ⬝ B = 1) : is_unit A.det :=
⟨{ val     := A.det,
    inv     := B.det,
    val_inv := by rw [← det_mul, h, det_one],
    inv_val := by rw [mul_comm, ← det_mul, h, det_one],
}, rfl⟩

lemma nonsing_inv_left_right (B : matrix n n α) (h : A ⬝ B = 1) : B ⬝ A = 1 :=
begin
  have h' : is_unit B.det := B.is_unit_det_of_left_inverse A h,
  calc B ⬝ A = (B ⬝ A) ⬝ (B ⬝ B⁻¹) : by simp only [h', matrix.mul_one, mul_nonsing_inv]
        ... = B ⬝ ((A ⬝ B) ⬝ B⁻¹) : by simp only [matrix.mul_assoc]
        ... = B ⬝ B⁻¹ : by simp only [h, matrix.one_mul]
        ... = 1 : mul_nonsing_inv B h',
end

lemma nonsing_inv_right_left (B : matrix n n α) (h : B ⬝ A = 1) : A ⬝ B = 1 :=
B.nonsing_inv_left_right A h

end inv

/- One form of Cramer's rule. -/
@[simp] lemma det_smul_inv_mul_vec_eq_cramer (A : matrix n n α) (b : n → α) (h : is_unit A.det) :
  A.det • A⁻¹.mul_vec b = cramer A b :=
begin
  rw [cramer_eq_adjugate_mul_vec, A.nonsing_inv_apply h, ← smul_mul_vec_assoc],
  conv_lhs { congr, congr, rw ← h.unit_spec, },
  rw units.smul_inv_smul,
end

/- A stronger form of Cramer's rule that allows us to solve some instances of `A ⬝ x = b` even if
the determinant is not a unit. A sufficient (but still not necessary) condition is that `A.det`
divides `b`. -/
@[simp] lemma mul_vec_cramer (A : matrix n n α) (b : n → α) :
  A.mul_vec (cramer A b) = A.det • b :=
by rw [cramer_eq_adjugate_mul_vec, mul_vec_mul_vec, mul_adjugate, smul_mul_vec_assoc, mul_vec_one]

end matrix


theorem option_em {t: Type} (x: option t): x = none ∨ ∃ (v: t), x = some v :=
begin
  cases x,
  left,
  refl,
  right,
  existsi x,
  refl,
end

theorem option_not_both {t: Type} (x: option t): ¬ (x = none ∧ ∃ (v: t), x = some v) :=
begin
  intro h,
  cases h,
  cases h_right,
  rw h_left at h_right_h,
  contradiction,
end

theorem not_some_is_none {t: Type} {x: option t}: (∀ (v: t), x ≠ some v) -> x = none :=
begin
  intro h,
  cases option_em x,
  exact h_1,
  cases h_1,
  exfalso,
  exact h h_1_w h_1_h,
end