/-
Copyright (c) 2017 Kevin Buzzard. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kevin Buzzard, Mario Carneiro

! This file was ported from Lean 3 source module data.complex.basic
! leanprover-community/mathlib commit 92ca63f0fb391a9ca5f22d2409a6080e786d99f7
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Data.Real.Sqrt

/-!
# The complex numbers

The complex numbers are modelled as ℝ^2 in the obvious way and it is shown that they form a field
of characteristic zero. The result that the complex numbers are algebraically closed, see
`FieldTheory.AlgebraicClosure`.
-/

open BigOperators

open Set Function

/-! ### Definition and basic arithmmetic -/

/-- Complex numbers consist of two `Real`s: a real part `re` and an imaginary part `im`. -/
structure Complex : Type where
  re : ℝ
  im : ℝ
#align complex Complex

notation "ℂ" => Complex

namespace Complex

open ComplexConjugate

noncomputable instance : DecidableEq ℂ :=
  Classical.decEq _

/-- The equivalence between the complex numbers and `ℝ × ℝ`. -/
@[simps apply]
def equivRealProd : ℂ ≃ ℝ × ℝ where
  toFun z := ⟨z.re, z.im⟩
  invFun p := ⟨p.1, p.2⟩
  left_inv := fun ⟨_, _⟩ => rfl
  right_inv := fun ⟨_, _⟩ => rfl
#align complex.equiv_real_prod Complex.equivRealProd

@[simp]
theorem eta : ∀ z : ℂ, Complex.mk z.re z.im = z
  | ⟨_, _⟩ => rfl
#align complex.eta Complex.eta

@[ext]
theorem ext : ∀ {z w : ℂ}, z.re = w.re → z.im = w.im → z = w
  | ⟨_, _⟩, ⟨_, _⟩, rfl, rfl => rfl
#align complex.ext Complex.ext

theorem ext_iff {z w : ℂ} : z = w ↔ z.re = w.re ∧ z.im = w.im :=
  ⟨fun H => by simp [H], fun h => ext h.1 h.2⟩
#align complex.ext_iff Complex.ext_iff

theorem re_surjective : Surjective re := fun x => ⟨⟨x, 0⟩, rfl⟩
#align complex.re_surjective Complex.re_surjective

theorem im_surjective : Surjective im := fun y => ⟨⟨0, y⟩, rfl⟩
#align complex.im_surjective Complex.im_surjective

@[simp]
theorem range_re : range re = univ :=
  re_surjective.range_eq
#align complex.range_re Complex.range_re

@[simp]
theorem range_im : range im = univ :=
  im_surjective.range_eq
#align complex.range_im Complex.range_im

-- Porting note: refactored instance to allow `norm_cast` to work
/-- The natural inclusion of the real numbers into the complex numbers.
The name `Complex.ofReal` is reserved for the bundled homomorphism. -/
@[coe]
def ofReal' (r : ℝ) : ℂ :=
  ⟨r, 0⟩
instance : Coe ℝ ℂ :=
  ⟨ofReal'⟩

/- Porting note: `simp` attribute removed as this has a variable as head symbol of
the left-hand side (after whnfR)-/
@[norm_cast]
theorem ofReal_re (r : ℝ) : Complex.re (r : ℂ) = r :=
  rfl
#align complex.of_real_re Complex.ofReal_re

@[simp, norm_cast]
theorem ofReal_im (r : ℝ) : (r : ℂ).im = 0 :=
  rfl
#align complex.of_real_im Complex.ofReal_im

theorem ofReal_def (r : ℝ) : (r : ℂ) = ⟨r, 0⟩ :=
  rfl
#align complex.of_real_def Complex.ofReal_def

@[simp, norm_cast]
theorem ofReal_inj {z w : ℝ} : (z : ℂ) = w ↔ z = w :=
  ⟨congrArg re, by apply congrArg⟩
#align complex.of_real_inj Complex.ofReal_inj

-- Porting note: made coercion explicit
theorem ofReal_injective : Function.Injective ((↑) : ℝ → ℂ) := fun _ _ => congrArg re
#align complex.of_real_injective Complex.ofReal_injective

-- Porting note: made coercion explicit
instance canLift : CanLift ℂ ℝ (↑) fun z => z.im = 0 where
  prf z hz := ⟨z.re, ext rfl hz.symm⟩
#align complex.can_lift Complex.canLift

/-- The product of a set on the real axis and a set on the imaginary axis of the complex plane,
denoted by `s ×ℂ t`. -/
def Set.reProdIm (s t : Set ℝ) : Set ℂ :=
  re ⁻¹' s ∩ im ⁻¹' t
#align set.re_prod_im Complex.Set.reProdIm

infixl:72 " ×ℂ " => Set.reProdIm

theorem mem_reProdIm {z : ℂ} {s t : Set ℝ} : z ∈ s ×ℂ t ↔ z.re ∈ s ∧ z.im ∈ t :=
  Iff.rfl
#align complex.mem_re_prod_im Complex.mem_reProdIm

instance : Zero ℂ :=
  ⟨(0 : ℝ)⟩

instance : Inhabited ℂ :=
  ⟨0⟩

@[simp]
theorem zero_re : (0 : ℂ).re = 0 :=
  rfl
#align complex.zero_re Complex.zero_re

@[simp]
theorem zero_im : (0 : ℂ).im = 0 :=
  rfl
#align complex.zero_im Complex.zero_im

@[simp, norm_cast]
theorem ofReal_zero : ((0 : ℝ) : ℂ) = 0 :=
  rfl
#align complex.of_real_zero Complex.ofReal_zero

@[simp]
theorem ofReal_eq_zero {z : ℝ} : (z : ℂ) = 0 ↔ z = 0 :=
  ofReal_inj
#align complex.of_real_eq_zero Complex.ofReal_eq_zero

theorem ofReal_ne_zero {z : ℝ} : (z : ℂ) ≠ 0 ↔ z ≠ 0 :=
  not_congr ofReal_eq_zero
#align complex.of_real_ne_zero Complex.ofReal_ne_zero

instance : One ℂ :=
  ⟨(1 : ℝ)⟩

@[simp]
theorem one_re : (1 : ℂ).re = 1 :=
  rfl
#align complex.one_re Complex.one_re

@[simp]
theorem one_im : (1 : ℂ).im = 0 :=
  rfl
#align complex.one_im Complex.one_im

@[simp, norm_cast]
theorem ofReal_one : ((1 : ℝ) : ℂ) = 1 :=
  rfl
#align complex.of_real_one Complex.ofReal_one

@[simp]
theorem ofReal_eq_one {z : ℝ} : (z : ℂ) = 1 ↔ z = 1 :=
  ofReal_inj
#align complex.of_real_eq_one Complex.ofReal_eq_one

theorem ofReal_ne_one {z : ℝ} : (z : ℂ) ≠ 1 ↔ z ≠ 1 :=
  not_congr ofReal_eq_one
#align complex.of_real_ne_one Complex.ofReal_ne_one

instance : Add ℂ :=
  ⟨fun z w => ⟨z.re + w.re, z.im + w.im⟩⟩

@[simp]
theorem add_re (z w : ℂ) : (z + w).re = z.re + w.re :=
  rfl
#align complex.add_re Complex.add_re

@[simp]
theorem add_im (z w : ℂ) : (z + w).im = z.im + w.im :=
  rfl
#align complex.add_im Complex.add_im

section
set_option linter.deprecated false
@[simp]
theorem bit0_re (z : ℂ) : (bit0 z).re = bit0 z.re :=
  rfl
#align complex.bit0_re Complex.bit0_re

@[simp]
theorem bit1_re (z : ℂ) : (bit1 z).re = bit1 z.re :=
  rfl
#align complex.bit1_re Complex.bit1_re

@[simp]
theorem bit0_im (z : ℂ) : (bit0 z).im = bit0 z.im :=
  Eq.refl _
#align complex.bit0_im Complex.bit0_im

@[simp]
theorem bit1_im (z : ℂ) : (bit1 z).im = bit0 z.im :=
  add_zero _
#align complex.bit1_im Complex.bit1_im

@[simp, norm_cast]
theorem ofReal_add (r s : ℝ) : ((r + s : ℝ) : ℂ) = r + s :=
  ext_iff.2 <| by simp [ofReal']
#align complex.of_real_add Complex.ofReal_add

@[simp, norm_cast]
theorem ofReal_bit0 (r : ℝ) : ((bit0 r : ℝ) : ℂ) = bit0 (r : ℂ)  :=
  ext_iff.2 <| by simp [bit0]
#align complex.of_real_bit0 Complex.ofReal_bit0

@[simp,  norm_cast]
theorem ofReal_bit1 (r : ℝ) : ((bit1 r : ℝ) : ℂ) = bit1 (r : ℂ) :=
  ext_iff.2 <| by simp [bit1]
#align complex.of_real_bit1 Complex.ofReal_bit1

end

instance : Neg ℂ :=
  ⟨fun z => ⟨-z.re, -z.im⟩⟩

@[simp]
theorem neg_re (z : ℂ) : (-z).re = -z.re :=
  rfl
#align complex.neg_re Complex.neg_re

@[simp]
theorem neg_im (z : ℂ) : (-z).im = -z.im :=
  rfl
#align complex.neg_im Complex.neg_im

@[simp, norm_cast]
theorem ofReal_neg (r : ℝ) : ((-r : ℝ) : ℂ) = -r :=
  ext_iff.2 <| by simp [ofReal']
#align complex.of_real_neg Complex.ofReal_neg

instance : Sub ℂ :=
  ⟨fun z w => ⟨z.re - w.re, z.im - w.im⟩⟩

instance : Mul ℂ :=
  ⟨fun z w => ⟨z.re * w.re - z.im * w.im, z.re * w.im + z.im * w.re⟩⟩

@[simp]
theorem mul_re (z w : ℂ) : (z * w).re = z.re * w.re - z.im * w.im :=
  rfl
#align complex.mul_re Complex.mul_re

@[simp]
theorem mul_im (z w : ℂ) : (z * w).im = z.re * w.im + z.im * w.re :=
  rfl
#align complex.mul_im Complex.mul_im

@[simp, norm_cast]
theorem ofReal_mul (r s : ℝ) : ((r * s : ℝ) : ℂ) = r * s :=
  ext_iff.2 <| by simp [ofReal']
#align complex.of_real_mul Complex.ofReal_mul

theorem ofReal_mul_re (r : ℝ) (z : ℂ) : (↑r * z).re = r * z.re := by simp [ofReal']
#align complex.of_real_mul_re Complex.ofReal_mul_re

theorem ofReal_mul_im (r : ℝ) (z : ℂ) : (↑r * z).im = r * z.im := by simp [ofReal']
#align complex.of_real_mul_im Complex.ofReal_mul_im

theorem ofReal_mul' (r : ℝ) (z : ℂ) : ↑r * z = ⟨r * z.re, r * z.im⟩ :=
  ext (ofReal_mul_re _ _) (ofReal_mul_im _ _)
#align complex.of_real_mul' Complex.ofReal_mul'

/-! ### The imaginary unit, `I` -/

/-- The imaginary unit. -/
def I : ℂ :=
  ⟨0, 1⟩
set_option linter.uppercaseLean3 false in
#align complex.I Complex.I

@[simp]
theorem I_re : I.re = 0 :=
  rfl
set_option linter.uppercaseLean3 false in
#align complex.I_re Complex.I_re

@[simp]
theorem I_im : I.im = 1 :=
  rfl
set_option linter.uppercaseLean3 false in
#align complex.I_im Complex.I_im

@[simp]
theorem I_mul_I : I * I = -1 :=
  ext_iff.2 <| by simp
set_option linter.uppercaseLean3 false in
#align complex.I_mul_I Complex.I_mul_I

theorem I_mul (z : ℂ) : I * z = ⟨-z.im, z.re⟩ :=
  ext_iff.2 <| by simp
set_option linter.uppercaseLean3 false in
#align complex.I_mul Complex.I_mul

theorem I_ne_zero : (I : ℂ) ≠ 0 :=
  mt (congr_arg im) zero_ne_one.symm
set_option linter.uppercaseLean3 false in
#align complex.I_ne_zero Complex.I_ne_zero

theorem mk_eq_add_mul_I (a b : ℝ) : Complex.mk a b = a + b * I :=
  ext_iff.2 <| by simp [ofReal']
set_option linter.uppercaseLean3 false in
#align complex.mk_eq_add_mul_I Complex.mk_eq_add_mul_I

@[simp]
theorem re_add_im (z : ℂ) : (z.re : ℂ) + z.im * I = z :=
  ext_iff.2 <| by simp [ofReal']
#align complex.re_add_im Complex.re_add_im

theorem mul_I_re (z : ℂ) : (z * I).re = -z.im := by simp
set_option linter.uppercaseLean3 false in
#align complex.mul_I_re Complex.mul_I_re

theorem mul_I_im (z : ℂ) : (z * I).im = z.re := by simp
set_option linter.uppercaseLean3 false in
#align complex.mul_I_im Complex.mul_I_im

theorem I_mul_re (z : ℂ) : (I * z).re = -z.im := by simp
set_option linter.uppercaseLean3 false in
#align complex.I_mul_re Complex.I_mul_re

theorem I_mul_im (z : ℂ) : (I * z).im = z.re := by simp
set_option linter.uppercaseLean3 false in
#align complex.I_mul_im Complex.I_mul_im

@[simp]
theorem equivRealProd_symm_apply (p : ℝ × ℝ) : equivRealProd.symm p = p.1 + p.2 * I := by
  ext <;> simp [Complex.equivRealProd, ofReal']
#align complex.equiv_real_prod_symm_apply Complex.equivRealProd_symm_apply

/-! ### Commutative ring instance and lemmas -/

/- We use a nonstandard formula for the `ℕ` and `ℤ` actions to make sure there is no
diamond from the other actions they inherit through the `ℝ`-action on `ℂ` and action transitivity
defined in `Data.Complex.Module`. -/
instance : Nontrivial ℂ :=
  pullback_nonzero re rfl rfl

-- Porting note: proof needed modifications and rewritten fields
instance addCommGroup : AddCommGroup ℂ :=
{ zero := (0 : ℂ)
  add := (· + ·)
  neg := Neg.neg
  sub := Sub.sub
  nsmul := fun n z => ⟨n • z.re - 0 * z.im, n • z.im + 0 * z.re⟩
  zsmul := fun n z => ⟨n • z.re - 0 * z.im, n • z.im + 0 * z.re⟩
  zsmul_zero':= by intros; ext <;> simp
  nsmul_zero := by intros; ext <;> simp
  nsmul_succ := by
    intros; ext <;> simp [AddMonoid.nsmul_succ, add_mul, add_comm]
  zsmul_succ' := by
    intros; ext <;> simp [SubNegMonoid.zsmul_succ', add_mul, add_comm]
  zsmul_neg' := by
    intros; ext <;> simp [zsmul_neg', add_mul]
  add_assoc := by intros; ext <;> simp [add_assoc]
  zero_add := by intros; ext <;> simp
  add_zero := by intros; ext <;> simp
  add_comm := by intros; ext <;> simp [add_comm]
  add_left_neg := by intros; ext <;> simp }

instance Complex.addGroupWithOne : AddGroupWithOne ℂ :=
  { Complex.addCommGroup with
    natCast := fun n => ⟨n, 0⟩
    natCast_zero := by
      ext <;> simp [Nat.cast, AddMonoidWithOne.natCast_zero]
    natCast_succ := fun _ => by ext <;> simp [Nat.cast, AddMonoidWithOne.natCast_succ]
    intCast := fun n => ⟨n, 0⟩
    intCast_ofNat := fun _ => by ext <;> rfl
    intCast_negSucc := fun n => by
      ext
      · simp [AddGroupWithOne.intCast_negSucc]
        show -(1: ℝ) + (-n) = -(↑(n + 1))
        simp [Nat.cast_add, add_comm]
      · simp [AddGroupWithOne.intCast_negSucc]
        show im ⟨n, 0⟩ = 0
        rfl
    one := 1 }

-- Porting note: proof needed modifications and rewritten fields
instance commRing : CommRing ℂ :=
  { Complex.addGroupWithOne with
    zero := (0 : ℂ)
    add := (· + ·)
    one := 1
    mul := (· * ·)
    npow := @npowRec _ ⟨(1 : ℂ)⟩ ⟨(· * ·)⟩
    add_comm := by intros; ext <;> simp [add_comm]
    left_distrib := by
      intros; ext <;> simp [mul_re, mul_im] <;> ring
    right_distrib := by
      intros; ext <;> simp [mul_re, mul_im] <;> ring
    zero_mul := by intros; ext <;> simp [zero_mul]
    mul_zero := by intros; ext <;> simp [mul_zero]
    mul_assoc := by intros; ext <;> simp [mul_assoc] <;> ring
    one_mul := by intros; ext <;> simp [one_mul]
    mul_one := by intros; ext <;> simp [mul_one]
    mul_comm := by intros; ext <;> simp [mul_comm] ; ring }

/-- This shortcut instance ensures we do not find `Ring` via the noncomputable `Complex.field`
instance. -/
instance : Ring ℂ := by infer_instance

/-- This shortcut instance ensures we do not find `CommSemiring` via the noncomputable
`Complex.field` instance. -/
instance : CommSemiring ℂ :=
  inferInstance

/-- The "real part" map, considered as an additive group homomorphism. -/
def reAddGroupHom : ℂ →+ ℝ where
  toFun := re
  map_zero' := zero_re
  map_add' := add_re
#align complex.re_add_group_hom Complex.reAddGroupHom

@[simp]
theorem coe_reAddGroupHom : (reAddGroupHom : ℂ → ℝ) = re :=
  rfl
#align complex.coe_re_add_group_hom Complex.coe_reAddGroupHom

/-- The "imaginary part" map, considered as an additive group homomorphism. -/
def imAddGroupHom : ℂ →+ ℝ where
  toFun := im
  map_zero' := zero_im
  map_add' := add_im
#align complex.im_add_group_hom Complex.imAddGroupHom

@[simp]
theorem coe_imAddGroupHom : (imAddGroupHom : ℂ → ℝ) = im :=
  rfl
#align complex.coe_im_add_group_hom Complex.coe_imAddGroupHom

section
set_option linter.deprecated false
@[simp]
theorem I_pow_bit0 (n : ℕ) : I ^ bit0 n = (-1) ^ n := by rw [pow_bit0', Complex.I_mul_I]
set_option linter.uppercaseLean3 false in
#align complex.I_pow_bit0 Complex.I_pow_bit0

@[simp]
theorem I_pow_bit1 (n : ℕ) : I ^ bit1 n = (-1) ^ n * I := by rw [pow_bit1', Complex.I_mul_I]
set_option linter.uppercaseLean3 false in
#align complex.I_pow_bit1 Complex.I_pow_bit1

--Porting note: new theorem
@[simp, norm_cast]
theorem ofReal_ofNat (n : ℕ) [n.AtLeastTwo] : ((OfNat.ofNat n : ℝ) : ℂ) = OfNat.ofNat n :=
  rfl

@[simp]
theorem re_ofNat (n : ℕ) [n.AtLeastTwo] : (OfNat.ofNat n : ℂ).re = OfNat.ofNat n :=
  rfl

@[simp]
theorem im_ofNat (n : ℕ) [n.AtLeastTwo] : (OfNat.ofNat n : ℂ).im = 0 :=
  rfl

end
/-! ### Complex conjugation -/

/-- This defines the complex conjugate as the `star` operation of the `StarRing ℂ`. It
is recommended to use the ring endomorphism version `starRingEnd`, available under the
notation `conj` in the locale `ComplexConjugate`. -/
instance : StarRing ℂ where
  star z := ⟨z.re, -z.im⟩
  star_involutive x := by simp only [eta, neg_neg]
  star_mul a b := by ext <;> simp [add_comm] <;> ring
  star_add a b := by ext <;> simp [add_comm]

@[simp]
theorem conj_re (z : ℂ) : (conj z).re = z.re :=
  rfl
#align complex.conj_re Complex.conj_re

@[simp]
theorem conj_im (z : ℂ) : (conj z).im = -z.im :=
  rfl
#align complex.conj_im Complex.conj_im

theorem conj_ofReal (r : ℝ) : conj (r : ℂ) = r :=
  ext_iff.2 <| by simp [star]
#align complex.conj_of_real Complex.conj_ofReal

@[simp]
theorem conj_I : conj I = -I :=
  ext_iff.2 <| by simp
  set_option linter.uppercaseLean3 false in
#align complex.conj_I Complex.conj_I

section
set_option linter.deprecated false
theorem conj_bit0 (z : ℂ) : conj (bit0 z) = bit0 (conj z) :=
  ext_iff.2 <| by simp [bit0]
#align complex.conj_bit0 Complex.conj_bit0

theorem conj_bit1 (z : ℂ) : conj (bit1 z) = bit1 (conj z) :=
  ext_iff.2 <| by simp [bit0]
#align complex.conj_bit1 Complex.conj_bit1
end
-- @[simp]
/- Porting note: `simp` attribute removed as the result could be proved
by `simp only [@map_neg, Complex.conj_i, @neg_neg]`
-/
theorem conj_neg_I : conj (-I) = I :=
  ext_iff.2 <| by simp
set_option linter.uppercaseLean3 false in
#align complex.conj_neg_I Complex.conj_neg_I

theorem eq_conj_iff_real {z : ℂ} : conj z = z ↔ ∃ r : ℝ, z = r :=
  ⟨fun h => ⟨z.re, ext rfl <| eq_zero_of_neg_eq (congr_arg im h)⟩, fun ⟨h, e⟩ => by
    rw [e, conj_ofReal]⟩
#align complex.eq_conj_iff_real Complex.eq_conj_iff_real

theorem eq_conj_iff_re {z : ℂ} : conj z = z ↔ (z.re : ℂ) = z :=
  eq_conj_iff_real.trans ⟨by rintro ⟨r, rfl⟩ ; simp [ofReal'], fun h => ⟨_, h.symm⟩⟩
#align complex.eq_conj_iff_re Complex.eq_conj_iff_re

theorem eq_conj_iff_im {z : ℂ} : conj z = z ↔ z.im = 0 :=
  ⟨fun h => add_self_eq_zero.mp (neg_eq_iff_add_eq_zero.mp (congr_arg im h)), fun h =>
    ext rfl (neg_eq_iff_add_eq_zero.mpr (add_self_eq_zero.mpr h))⟩
#align complex.eq_conj_iff_im Complex.eq_conj_iff_im

-- `simpNF` complains about this being provable by `is_R_or_C.star_def` even
-- though it's not imported by this file.
-- Porting note: linter `simpNF` not found
@[simp]
theorem star_def : (Star.star : ℂ → ℂ) = conj :=
  rfl
#align complex.star_def Complex.star_def

/-! ### Norm squared -/

/-- The norm squared function. -/
-- Porting note: `@[pp_nodot]` not found
-- @[pp_nodot]
def normSq : ℂ →*₀ ℝ where
  toFun z := z.re * z.re + z.im * z.im
  map_zero' := by simp
  map_one' := by simp
  map_mul' z w := by
    dsimp
    ring
#align complex.norm_sq Complex.normSq

theorem normSq_apply (z : ℂ) : normSq z = z.re * z.re + z.im * z.im :=
  rfl
#align complex.norm_sq_apply Complex.normSq_apply

@[simp]
theorem normSq_ofReal (r : ℝ) : normSq r = r * r := by
  simp [normSq, ofReal']
#align complex.norm_sq_of_real Complex.normSq_ofReal

@[simp]
theorem normSq_mk (x y : ℝ) : normSq ⟨x, y⟩ = x * x + y * y :=
  rfl
#align complex.norm_sq_mk Complex.normSq_mk

theorem normSq_add_mul_I (x y : ℝ) : normSq (x + y * I) = x ^ 2 + y ^ 2 := by
  rw [← mk_eq_add_mul_I, normSq_mk, sq, sq]
set_option linter.uppercaseLean3 false in
#align complex.norm_sq_add_mul_I Complex.normSq_add_mul_I

theorem normSq_eq_conj_mul_self {z : ℂ} : (normSq z : ℂ) = conj z * z := by
  ext <;> simp [normSq, mul_comm, ofReal']
#align complex.norm_sq_eq_conj_mul_self Complex.normSq_eq_conj_mul_self

-- @[simp]
/- Porting note: `simp` attribute removed as linter reports this can be proved
by `simp only [@map_zero]` -/
theorem normSq_zero : normSq 0 = 0 :=
  normSq.map_zero
#align complex.norm_sq_zero Complex.normSq_zero

-- @[simp]
/- Porting note: `simp` attribute removed as linter reports this can be proved
by `simp only [@map_one]` -/
theorem normSq_one : normSq 1 = 1 :=
  normSq.map_one
#align complex.norm_sq_one Complex.normSq_one

@[simp]
theorem normSq_I : normSq I = 1 := by simp [normSq]
set_option linter.uppercaseLean3 false in
#align complex.norm_sq_I Complex.normSq_I

theorem normSq_nonneg (z : ℂ) : 0 ≤ normSq z :=
  add_nonneg (mul_self_nonneg _) (mul_self_nonneg _)
#align complex.norm_sq_nonneg Complex.normSq_nonneg

@[simp]
theorem range_normSq : range normSq = Ici 0 :=
  Subset.antisymm (range_subset_iff.2 normSq_nonneg) fun x hx =>
    ⟨Real.sqrt x, by rw [normSq_ofReal, Real.mul_self_sqrt hx]⟩
#align complex.range_norm_sq Complex.range_normSq

theorem normSq_eq_zero {z : ℂ} : normSq z = 0 ↔ z = 0 :=
  ⟨fun h =>
    ext (eq_zero_of_mul_self_add_mul_self_eq_zero h)
      (eq_zero_of_mul_self_add_mul_self_eq_zero <| (add_comm _ _).trans h),
    fun h => h.symm ▸ normSq_zero⟩
#align complex.norm_sq_eq_zero Complex.normSq_eq_zero

@[simp]
theorem normSq_pos {z : ℂ} : 0 < normSq z ↔ z ≠ 0 :=
  (normSq_nonneg z).lt_iff_ne.trans <| not_congr (eq_comm.trans normSq_eq_zero)
#align complex.norm_sq_pos Complex.normSq_pos

@[simp]
theorem normSq_neg (z : ℂ) : normSq (-z) = normSq z := by simp [normSq]
#align complex.norm_sq_neg Complex.normSq_neg

@[simp]
theorem normSq_conj (z : ℂ) : normSq (conj z) = normSq z := by simp [normSq]
#align complex.norm_sq_conj Complex.normSq_conj

theorem normSq_mul (z w : ℂ) : normSq (z * w) = normSq z * normSq w :=
  normSq.map_mul z w
#align complex.norm_sq_mul Complex.normSq_mul

theorem normSq_add (z w : ℂ) : normSq (z + w) = normSq z + normSq w + 2 * (z * conj w).re := by
  dsimp [normSq] ; ring
#align complex.norm_sq_add Complex.normSq_add

theorem re_sq_le_normSq (z : ℂ) : z.re * z.re ≤ normSq z :=
  le_add_of_nonneg_right (mul_self_nonneg _)
#align complex.re_sq_le_norm_sq Complex.re_sq_le_normSq

theorem im_sq_le_normSq (z : ℂ) : z.im * z.im ≤ normSq z :=
  le_add_of_nonneg_left (mul_self_nonneg _)
#align complex.im_sq_le_norm_sq Complex.im_sq_le_normSq

theorem mul_conj (z : ℂ) : z * conj z = normSq z :=
  ext_iff.2 <| by simp [normSq, mul_comm, sub_eq_neg_add, add_comm, ofReal']
#align complex.mul_conj Complex.mul_conj

theorem add_conj (z : ℂ) : z + conj z = (2 * z.re : ℝ) :=
  ext_iff.2 <| by simp [two_mul, ofReal']
#align complex.add_conj Complex.add_conj

/-- The coercion `ℝ → ℂ` as a `RingHom`. -/
def ofReal : ℝ →+* ℂ where
  toFun x := (x : ℂ)
  map_one' := ofReal_one
  map_zero' := ofReal_zero
  map_mul' := ofReal_mul
  map_add' := ofReal_add
#align complex.of_real Complex.ofReal

@[simp]
theorem ofReal_eq_coe (r : ℝ) : ofReal r = r :=
  rfl
#align complex.of_real_eq_coe Complex.ofReal_eq_coe

@[simp]
theorem I_sq : I ^ 2 = -1 := by rw [sq, I_mul_I]
set_option linter.uppercaseLean3 false in
#align complex.I_sq Complex.I_sq

@[simp]
theorem sub_re (z w : ℂ) : (z - w).re = z.re - w.re :=
  rfl
#align complex.sub_re Complex.sub_re

@[simp]

@[simp, norm_cast]
theorem ofReal_sub (r s : ℝ) : ((r - s : ℝ) : ℂ) = r - s :=
  ext_iff.2 <| by simp [ofReal']
#align complex.of_real_sub Complex.ofReal_sub

@[simp, norm_cast]
theorem ofReal_pow (r : ℝ) (n : ℕ) : ((r ^ n : ℝ) : ℂ) = (r : ℂ) ^ n := by
  induction n <;> simp [*, ofReal_mul, pow_succ]
#align complex.of_real_pow Complex.ofReal_pow

theorem sub_conj (z : ℂ) : z - conj z = (2 * z.im : ℝ) * I :=
  ext_iff.2 <| by simp [two_mul, sub_eq_add_neg, ofReal']
#align complex.sub_conj Complex.sub_conj

theorem normSq_sub (z w : ℂ) : normSq (z - w) = normSq z + normSq w - 2 * (z * conj w).re := by
  rw [sub_eq_add_neg, normSq_add]
  simp only [RingHom.map_neg, mul_neg, neg_re, normSq_neg]
  ring
#align complex.norm_sq_sub Complex.normSq_sub

/-! ### Inversion -/

noncomputable instance : Inv ℂ :=
  ⟨fun z => conj z * ((normSq z)⁻¹ : ℝ)⟩

theorem inv_def (z : ℂ) : z⁻¹ = conj z * ((normSq z)⁻¹ : ℝ) :=
  rfl
#align complex.inv_def Complex.inv_def

@[simp]
theorem inv_re (z : ℂ) : z⁻¹.re = z.re / normSq z := by simp [inv_def, division_def, ofReal']
#align complex.inv_re Complex.inv_re

@[simp]
theorem inv_im (z : ℂ) : z⁻¹.im = -z.im / normSq z := by simp [inv_def, division_def, ofReal']
#align complex.inv_im Complex.inv_im

@[simp, norm_cast]
theorem ofReal_inv (r : ℝ) : ((r⁻¹ : ℝ) : ℂ) = (r : ℂ)⁻¹ :=
  ext_iff.2 <| by simp [ofReal']
#align complex.of_real_inv Complex.ofReal_inv

protected theorem inv_zero : (0⁻¹ : ℂ) = 0 := by
  rw [← ofReal_zero, ← ofReal_inv, inv_zero]
#align complex.inv_zero Complex.inv_zero

protected theorem mul_inv_cancel {z : ℂ} (h : z ≠ 0) : z * z⁻¹ = 1 := by
  rw [inv_def, ← mul_assoc, mul_conj, ← ofReal_mul, mul_inv_cancel (mt normSq_eq_zero.1 h),
    ofReal_one]
#align complex.mul_inv_cancel Complex.mul_inv_cancel

/-! ### Field instance and lemmas -/

noncomputable instance : Field ℂ :=
{ inv := Inv.inv
  mul_inv_cancel := @Complex.mul_inv_cancel
  inv_zero := Complex.inv_zero }

section
set_option linter.deprecated false
@[simp]
theorem I_zpow_bit0 (n : ℤ) : I ^ bit0 n = (-1) ^ n := by rw [zpow_bit0', I_mul_I]
set_option linter.uppercaseLean3 false in
#align complex.I_zpow_bit0 Complex.I_zpow_bit0

@[simp]
theorem I_zpow_bit1 (n : ℤ) : I ^ bit1 n = (-1) ^ n * I := by rw [zpow_bit1', I_mul_I]
set_option linter.uppercaseLean3 false in
#align complex.I_zpow_bit1 Complex.I_zpow_bit1

end

theorem div_re (z w : ℂ) : (z / w).re = z.re * w.re / normSq w + z.im * w.im / normSq w := by
  simp [div_eq_mul_inv, mul_assoc, sub_eq_add_neg]
#align complex.div_re Complex.div_re

theorem div_im (z w : ℂ) : (z / w).im = z.im * w.re / normSq w - z.re * w.im / normSq w := by
  simp [div_eq_mul_inv, mul_assoc, sub_eq_add_neg, add_comm]
#align complex.div_im Complex.div_im

theorem conj_inv (x : ℂ) : conj x⁻¹ = (conj x)⁻¹ :=
  star_inv' _
#align complex.conj_inv Complex.conj_inv

@[simp, norm_cast]
theorem ofReal_div (r s : ℝ) : ((r / s : ℝ) : ℂ) = r / s :=
  map_div₀ ofReal r s
#align complex.of_real_div Complex.ofReal_div

@[simp, norm_cast]
theorem ofReal_zpow (r : ℝ) (n : ℤ) : ((r ^ n : ℝ) : ℂ) = (r : ℂ) ^ n :=
  map_zpow₀ ofReal r n
#align complex.of_real_zpow Complex.ofReal_zpow

@[simp]
theorem div_I (z : ℂ) : z / I = -(z * I) :=
  (div_eq_iff_mul_eq I_ne_zero).2 <| by simp [mul_assoc]
set_option linter.uppercaseLean3 false in
#align complex.div_I Complex.div_I

@[simp]
theorem inv_I : I⁻¹ = -I := by
  rw [inv_eq_one_div, div_I, one_mul]
set_option linter.uppercaseLean3 false in
#align complex.inv_I Complex.inv_I

-- @[simp]
/- Porting note: `simp` attribute removed as linter reports this can be proved
by `simp only [@map_inv₀]` -/
theorem normSq_inv (z : ℂ) : normSq z⁻¹ = (normSq z)⁻¹ :=
  map_inv₀ normSq z
#align complex.norm_sq_inv Complex.normSq_inv

-- @[simp]
/- Porting note: `simp` attribute removed as linter reports this can be proved
by `simp only [@map_div₀]` -/
theorem normSq_div (z w : ℂ) : normSq (z / w) = normSq z / normSq w :=
  map_div₀ normSq z w
#align complex.norm_sq_div Complex.normSq_div

/-! ### Cast lemmas -/

@[simp, norm_cast]
theorem ofReal_nat_cast (n : ℕ) : ((n : ℝ) : ℂ) = n :=
  map_natCast ofReal n
#align complex.of_real_nat_cast Complex.ofReal_nat_cast

@[simp, norm_cast]
theorem nat_cast_re (n : ℕ) : (n : ℂ).re = n := by rw [← ofReal_nat_cast, ofReal_re]
#align complex.nat_cast_re Complex.nat_cast_re

@[simp, norm_cast]
theorem nat_cast_im (n : ℕ) : (n : ℂ).im = 0 := by rw [← ofReal_nat_cast, ofReal_im]
#align complex.nat_cast_im Complex.nat_cast_im

@[simp, norm_cast]
theorem ofReal_int_cast (n : ℤ) : ((n : ℝ) : ℂ) = n :=
  map_intCast ofReal n
#align complex.of_real_int_cast Complex.ofReal_int_cast

@[simp, norm_cast]
theorem int_cast_re (n : ℤ) : (n : ℂ).re = n := by rw [← ofReal_int_cast, ofReal_re]
#align complex.int_cast_re Complex.int_cast_re

@[simp, norm_cast]
theorem int_cast_im (n : ℤ) : (n : ℂ).im = 0 := by rw [← ofReal_int_cast, ofReal_im]
#align complex.int_cast_im Complex.int_cast_im

@[simp, norm_cast]
theorem ofReal_rat_cast (n : ℚ) : ((n : ℝ) : ℂ) = (n : ℂ) :=
  map_ratCast ofReal n
#align complex.of_real_rat_cast Complex.ofReal_rat_cast

-- Porting note: removed `norm_cast` attribute because the RHS can't start with `↑`
@[simp]
theorem rat_cast_re (q : ℚ) : (q : ℂ).re = (q : ℂ) := by
 rw [← ofReal_rat_cast, ofReal_re]
#align complex.rat_cast_re Complex.rat_cast_re

-- Porting note: removed `norm_cast` attribute because the RHS can't start with `↑`
@[simp]
theorem rat_cast_im (q : ℚ) : (q : ℂ).im = 0 := by
 rw [← ofReal_rat_cast, ofReal_im]
#align complex.rat_cast_im Complex.rat_cast_im

/-! ### Characteristic zero -/

instance charZero : CharZero ℂ :=
  charZero_of_inj_zero fun n h => by
    rwa [← ofReal_nat_cast, ofReal_eq_zero, Nat.cast_eq_zero] at h
#align complex.char_zero_complex Complex.charZero

/-- A complex number `z` plus its conjugate `conj z` is `2` times its real part. -/
theorem re_eq_add_conj (z : ℂ) : (z.re : ℂ) = (z + conj z) / 2 := by
  have : (↑(↑2 : ℝ) : ℂ)  = (2 : ℂ) := by rfl
  simp only [add_conj, ofReal_mul, ofReal_one, ofReal_bit0, this,
    mul_div_cancel_left (z.re : ℂ) two_ne_zero]
#align complex.re_eq_add_conj Complex.re_eq_add_conj

/-- A complex number `z` minus its conjugate `conj z` is `2i` times its imaginary part. -/
theorem im_eq_sub_conj (z : ℂ) : (z.im : ℂ) = (z - conj z) / (2 * I) := by
  have : (↑2 : ℝ ) * I = 2 * I := by rfl
  simp only [sub_conj, ofReal_mul, ofReal_one, ofReal_bit0, mul_right_comm, this,
    mul_div_cancel_left _ (mul_ne_zero two_ne_zero I_ne_zero : 2 * I ≠ 0)]
#align complex.im_eq_sub_conj Complex.im_eq_sub_conj

/-! ### Absolute value -/

namespace AbsTheory

-- We develop enough theory to bundle `abs` into an `AbsoluteValue` before making things public;
-- this is so there's not two versions of it hanging around.
local notation "abs" z => Real.sqrt (normSq z)

private theorem mul_self_abs (z : ℂ) : ((abs z) * abs z) = normSq z :=
  Real.mul_self_sqrt (normSq_nonneg _)

private theorem abs_nonneg' (z : ℂ) : 0 ≤ abs z :=
  Real.sqrt_nonneg _

theorem abs_conj (z : ℂ) : (abs conj z) = abs z := by simp
#align complex.abs_theory.abs_conj Complex.AbsTheory.abs_conj

private theorem abs_re_le_abs (z : ℂ) : |z.re| ≤ abs z := by
  rw [mul_self_le_mul_self_iff (abs_nonneg z.re) (abs_nonneg' _), abs_mul_abs_self, mul_self_abs]
  apply re_sq_le_normSq

private theorem re_le_abs (z : ℂ) : z.re ≤ abs z :=
  (abs_le.1 (abs_re_le_abs _)).2

private theorem abs_mul (z w : ℂ) : (abs z * w) = (abs z) * abs w := by
  rw [normSq_mul, Real.sqrt_mul (normSq_nonneg _)]

private theorem abs_add (z w : ℂ) : (abs z + w) ≤ (abs z) + abs w :=
  (mul_self_le_mul_self_iff (abs_nonneg' (z + w)) (add_nonneg (abs_nonneg' z) (abs_nonneg' w))).2 <|
    by
    rw [mul_self_abs, add_mul_self_eq, mul_self_abs, mul_self_abs, add_right_comm, normSq_add,
      add_le_add_iff_left, mul_assoc, mul_le_mul_left (zero_lt_two' ℝ), ←
      Real.sqrt_mul <| normSq_nonneg z, ← normSq_conj w, ← map_mul]
    exact re_le_abs (z * conj w)

/-- The complex absolute value function, defined as the square root of the norm squared. -/
noncomputable def _root_.Complex.abs : AbsoluteValue ℂ ℝ where
  toFun x := abs x
  map_mul' := abs_mul
  nonneg' := abs_nonneg'
  eq_zero' _ := (Real.sqrt_eq_zero <| normSq_nonneg _).trans normSq_eq_zero
  add_le' := abs_add
#align complex.abs Complex.abs

end AbsTheory

theorem abs_def : (Complex.abs : ℂ → ℝ) = fun z => (normSq z).sqrt :=
  rfl
#align complex.abs_def Complex.abs_def

theorem abs_apply {z : ℂ} : Complex.abs z = (normSq z).sqrt :=
  rfl
#align complex.abs_apply Complex.abs_apply

@[simp, norm_cast]
theorem abs_ofReal (r : ℝ) : Complex.abs r = |r| := by
  simp [Complex.abs, normSq_ofReal, Real.sqrt_mul_self_eq_abs]
#align complex.abs_of_real Complex.abs_ofReal

nonrec theorem abs_of_nonneg {r : ℝ} (h : 0 ≤ r) : Complex.abs r = r :=
  (Complex.abs_ofReal _).trans (abs_of_nonneg h)
#align complex.abs_of_nonneg Complex.abs_of_nonneg

theorem abs_of_nat (n : ℕ) : Complex.abs n = n :=
  calc
    Complex.abs n = Complex.abs (n : ℝ) := by rw [ofReal_nat_cast]
    _ = _ := Complex.abs_of_nonneg (Nat.cast_nonneg n)

#align complex.abs_of_nat Complex.abs_of_nat

theorem mul_self_abs (z : ℂ) : Complex.abs z * Complex.abs z = normSq z :=
  Real.mul_self_sqrt (normSq_nonneg _)
#align complex.mul_self_abs Complex.mul_self_abs

theorem sq_abs (z : ℂ) : Complex.abs z ^ 2 = normSq z :=
  Real.sq_sqrt (normSq_nonneg _)
#align complex.sq_abs Complex.sq_abs

@[simp]
theorem sq_abs_sub_sq_re (z : ℂ) : Complex.abs z ^ 2 - z.re ^ 2 = z.im ^ 2 := by
  rw [sq_abs, normSq_apply, ← sq, ← sq, add_sub_cancel']
#align complex.sq_abs_sub_sq_re Complex.sq_abs_sub_sq_re

@[simp]
theorem sq_abs_sub_sq_im (z : ℂ) : Complex.abs z ^ 2 - z.im ^ 2 = z.re ^ 2 := by
  rw [← sq_abs_sub_sq_re, sub_sub_cancel]
#align complex.sq_abs_sub_sq_im Complex.sq_abs_sub_sq_im

@[simp]
theorem abs_I : Complex.abs I = 1 := by simp [Complex.abs]
set_option linter.uppercaseLean3 false in
#align complex.abs_I Complex.abs_I

@[simp]
theorem abs_two : Complex.abs 2 = 2 :=
  calc
    Complex.abs 2 = Complex.abs (2 : ℝ) := by rfl
    _ = (2 : ℝ) := Complex.abs_of_nonneg (by norm_num)
#align complex.abs_two Complex.abs_two

@[simp]
theorem range_abs : range Complex.abs = Ici 0 :=
  Subset.antisymm
    (by simp only [range_subset_iff, Ici, mem_setOf_eq, map_nonneg, forall_const])
    (fun x hx => ⟨x, Complex.abs_of_nonneg hx⟩)
#align complex.range_abs Complex.range_abs

@[simp]
theorem abs_conj (z : ℂ) : Complex.abs (conj z) = Complex.abs z :=
  AbsTheory.abs_conj z
#align complex.abs_conj Complex.abs_conj

@[simp]
theorem abs_prod {ι : Type _} (s : Finset ι) (f : ι → ℂ) :
    Complex.abs (s.prod f) = s.prod fun I => Complex.abs (f I) :=
  map_prod Complex.abs _ _
#align complex.abs_prod Complex.abs_prod

-- @[simp]
/- Porting note: `simp` attribute removed as linter reports this can be proved
by `simp only [@map_pow]` -/
theorem abs_pow (z : ℂ) (n : ℕ) : Complex.abs (z ^ n) = Complex.abs z ^ n :=
  map_pow Complex.abs z n
#align complex.abs_pow Complex.abs_pow

-- @[simp]
/- Porting note: `simp` attribute removed as linter reports this can be proved
by `simp only [@map_zpow₀]` -/
theorem abs_zpow (z : ℂ) (n : ℤ) : Complex.abs (z ^ n) = Complex.abs z ^ n :=
  map_zpow₀ Complex.abs z n
#align complex.abs_zpow Complex.abs_zpow

theorem abs_re_le_abs (z : ℂ) : |z.re| ≤ Complex.abs z :=
  Real.abs_le_sqrt <| by
    rw [normSq_apply, ← sq]
    exact le_add_of_nonneg_right (mul_self_nonneg _)
#align complex.abs_re_le_abs Complex.abs_re_le_abs

theorem abs_im_le_abs (z : ℂ) : |z.im| ≤ Complex.abs z :=
  Real.abs_le_sqrt <| by
    rw [normSq_apply, ← sq, ← sq]
    exact le_add_of_nonneg_left (sq_nonneg _)
#align complex.abs_im_le_abs Complex.abs_im_le_abs

theorem re_le_abs (z : ℂ) : z.re ≤ Complex.abs z :=
  (abs_le.1 (abs_re_le_abs _)).2
#align complex.re_le_abs Complex.re_le_abs

theorem im_le_abs (z : ℂ) : z.im ≤ Complex.abs z :=
  (abs_le.1 (abs_im_le_abs _)).2
#align complex.im_le_abs Complex.im_le_abs

@[simp]
theorem abs_re_lt_abs {z : ℂ} : |z.re| < Complex.abs z ↔ z.im ≠ 0 := by
  rw [Complex.abs, AbsoluteValue.coe_mk, MulHom.coe_mk, Real.lt_sqrt (abs_nonneg _), normSq_apply,
    _root_.sq_abs, ← sq, lt_add_iff_pos_right, mul_self_pos]
#align complex.abs_re_lt_abs Complex.abs_re_lt_abs

@[simp]
theorem abs_im_lt_abs {z : ℂ} : |z.im| < Complex.abs z ↔ z.re ≠ 0 := by
  simpa using @abs_re_lt_abs (z * I)
#align complex.abs_im_lt_abs Complex.abs_im_lt_abs

@[simp]
theorem abs_abs (z : ℂ) : |Complex.abs z| = Complex.abs z :=
  _root_.abs_of_nonneg (AbsoluteValue.nonneg _ z)
#align complex.abs_abs Complex.abs_abs

-- Porting note: probably should be golfed
theorem abs_le_abs_re_add_abs_im (z : ℂ) : Complex.abs z ≤ |z.re| + |z.im| := by
  simpa [re_add_im] using Complex.abs.add_le z.re (z.im * I)
#align complex.abs_le_abs_re_add_abs_im Complex.abs_le_abs_re_add_abs_im

-- Porting note: added so `two_pos` in the next proof works
-- TODO: move somewhere else
instance : NeZero (1 : ℝ) :=
 ⟨by apply one_ne_zero⟩

theorem abs_le_sqrt_two_mul_max (z : ℂ) : Complex.abs z ≤ Real.sqrt 2 * max (|z.re|) (|z.im|) := by
  cases' z with x y
  simp only [abs_apply, normSq_mk, ← sq]
  by_cases hle : |x| ≤ |y|
  · calc
      Real.sqrt (x ^ 2 + y ^ 2) ≤ Real.sqrt (y ^ 2 + y ^ 2) :=
        Real.sqrt_le_sqrt (add_le_add_right (sq_le_sq.2 hle) _)
      _ = Real.sqrt 2 * max (|x|) (|y|) := by
        rw [max_eq_right hle, ← two_mul, Real.sqrt_mul two_pos.le, Real.sqrt_sq_eq_abs]
  · have hle' := le_of_not_le hle
    rw [add_comm]
    calc
      Real.sqrt (y ^ 2 + x ^ 2) ≤ Real.sqrt (x ^ 2 + x ^ 2) :=
        Real.sqrt_le_sqrt (add_le_add_right (sq_le_sq.2 hle') _)
      _ = Real.sqrt 2 * max (|x|) (|y|) := by
        rw [max_eq_left hle', ← two_mul, Real.sqrt_mul two_pos.le, Real.sqrt_sq_eq_abs]
#align complex.abs_le_sqrt_two_mul_max Complex.abs_le_sqrt_two_mul_max

theorem abs_re_div_abs_le_one (z : ℂ) : |z.re / Complex.abs z| ≤ 1 :=
  if hz : z = 0 then by simp [hz, zero_le_one]
  else by simp_rw [_root_.abs_div, abs_abs,
    div_le_iff (AbsoluteValue.pos Complex.abs hz), one_mul, abs_re_le_abs]
#align complex.abs_re_div_abs_le_one Complex.abs_re_div_abs_le_one

theorem abs_im_div_abs_le_one (z : ℂ) : |z.im / Complex.abs z| ≤ 1 :=
  if hz : z = 0 then by simp [hz, zero_le_one]
  else by simp_rw [_root_.abs_div, abs_abs,
    div_le_iff (AbsoluteValue.pos Complex.abs hz), one_mul, abs_im_le_abs]
#align complex.abs_im_div_abs_le_one Complex.abs_im_div_abs_le_one

-- Porting note: removed `norm_cast` attribute because the RHS can't start with `↑`
@[simp]
theorem abs_cast_nat (n : ℕ) : Complex.abs (n : ℂ) = n := by
  rw [← ofReal_nat_cast, abs_of_nonneg (Nat.cast_nonneg n)]
#align complex.abs_cast_nat Complex.abs_cast_nat

@[simp, norm_cast]
theorem int_cast_abs (n : ℤ) : (|↑n|) = Complex.abs n := by
  rw [← ofReal_int_cast, abs_ofReal]
#align complex.int_cast_abs Complex.int_cast_abs

theorem normSq_eq_abs (x : ℂ) : normSq x = (Complex.abs x) ^ 2 := by
  simp [abs, sq, abs_def, Real.mul_self_sqrt (normSq_nonneg _)]
#align complex.norm_sq_eq_abs Complex.normSq_eq_abs

/-- We put a partial order on ℂ so that `z ≤ w` exactly if `w - z` is real and nonnegative.
Complex numbers with different imaginary parts are incomparable.
-/
protected def partialOrder : PartialOrder ℂ where
  le z w := z.re ≤ w.re ∧ z.im = w.im
  lt z w := z.re < w.re ∧ z.im = w.im
  lt_iff_le_not_le z w := by
    dsimp
    rw [lt_iff_le_not_le]
    tauto
  le_refl x := ⟨le_rfl, rfl⟩
  le_trans x y z h₁ h₂ := ⟨h₁.1.trans h₂.1, h₁.2.trans h₂.2⟩
  le_antisymm z w h₁ h₂ := ext (h₁.1.antisymm h₂.1) h₁.2
#align complex.partial_order Complex.partialOrder

namespace _root_.ComplexOrder

-- Porting note: made section into namespace to allow scoping
scoped[ComplexOrder] attribute [instance] Complex.partialOrder

end _root_.ComplexOrder

section ComplexOrder

open ComplexOrder

theorem le_def {z w : ℂ} : z ≤ w ↔ z.re ≤ w.re ∧ z.im = w.im :=
  Iff.rfl
#align complex.le_def Complex.le_def

theorem lt_def {z w : ℂ} : z < w ↔ z.re < w.re ∧ z.im = w.im :=
  Iff.rfl
#align complex.lt_def Complex.lt_def

@[simp, norm_cast]
theorem real_le_real {x y : ℝ} : (x : ℂ) ≤ (y : ℂ) ↔ x ≤ y := by simp [le_def, ofReal']
#align complex.real_le_real Complex.real_le_real

@[simp, norm_cast]
theorem real_lt_real {x y : ℝ} : (x : ℂ) < (y : ℂ) ↔ x < y := by simp [lt_def, ofReal']
#align complex.real_lt_real Complex.real_lt_real

@[simp, norm_cast]
theorem zero_le_real {x : ℝ} : (0 : ℂ) ≤ (x : ℂ) ↔ 0 ≤ x :=
  real_le_real
#align complex.zero_le_real Complex.zero_le_real

@[simp, norm_cast]
theorem zero_lt_real {x : ℝ} : (0 : ℂ) < (x : ℂ) ↔ 0 < x :=
  real_lt_real
#align complex.zero_lt_real Complex.zero_lt_real

theorem not_le_iff {z w : ℂ} : ¬z ≤ w ↔ w.re < z.re ∨ z.im ≠ w.im := by
  rw [le_def, not_and_or, not_le]
#align complex.not_le_iff Complex.not_le_iff

theorem not_lt_iff {z w : ℂ} : ¬z < w ↔ w.re ≤ z.re ∨ z.im ≠ w.im := by
  rw [lt_def, not_and_or, not_lt]
#align complex.not_lt_iff Complex.not_lt_iff

theorem not_le_zero_iff {z : ℂ} : ¬z ≤ 0 ↔ 0 < z.re ∨ z.im ≠ 0 :=
  not_le_iff
#align complex.not_le_zero_iff Complex.not_le_zero_iff

theorem not_lt_zero_iff {z : ℂ} : ¬z < 0 ↔ 0 ≤ z.re ∨ z.im ≠ 0 :=
  not_lt_iff
#align complex.not_lt_zero_iff Complex.not_lt_zero_iff

theorem eq_re_ofReal_le {r : ℝ} {z : ℂ} (hz : (r : ℂ) ≤ z) : z = z.re := by
  ext
  rfl
  simp only [← (Complex.le_def.1 hz).2, Complex.zero_im, Complex.ofReal_im]
#align complex.eq_re_of_real_le Complex.eq_re_ofReal_le

/-- With `z ≤ w` iff `w - z` is real and nonnegative, `ℂ` is a strictly ordered ring.
-/
protected def strictOrderedCommRing : StrictOrderedCommRing ℂ :=
{ zero_le_one := ⟨zero_le_one, rfl⟩
  add_le_add_left := fun w z h y => ⟨add_le_add_left h.1 _, congr_arg₂ (· + ·) rfl h.2⟩
  mul_pos := fun z w hz hw => by
    simp [lt_def, mul_re, mul_im, ← hz.2, ← hw.2, mul_pos hz.1 hw.1]
  mul_comm := by intros; ext <;> ring_nf }

#align complex.strict_ordered_comm_ring Complex.strictOrderedCommRing

scoped[ComplexOrder] attribute [instance] Complex.strictOrderedCommRing

/-- With `z ≤ w` iff `w - z` is real and nonnegative, `ℂ` is a star ordered ring.
(That is, a star ring in which the nonnegative elements are those of the form `star z * z`.)
-/
protected def starOrderedRing : StarOrderedRing ℂ :=
{ nonneg_iff := fun r => by
    refine' ⟨fun hr => ⟨Real.sqrt r.re, _⟩, fun h => _⟩
    · have h₁ : 0 ≤ r.re := by
        rw [le_def] at hr
        exact hr.1
      have h₂ : r.im = 0 := by
        rw [le_def] at hr
        exact hr.2.symm
      ext
      · simp only [ofReal_im, star_def, ofReal_re, sub_zero, conj_re, mul_re, mul_zero, ←
          Real.sqrt_mul h₁ r.re, Real.sqrt_mul_self h₁]
      · simp only [h₂, add_zero, ofReal_im, star_def, zero_mul, conj_im, mul_im, mul_zero,
          neg_zero]
    · obtain ⟨s, rfl⟩ := h
      simp only [← normSq_eq_conj_mul_self, normSq_nonneg, zero_le_real, star_def]
  add_le_add_left := by intros; simp [le_def] at *; assumption }
#align complex.star_ordered_ring Complex.starOrderedRing

scoped[ComplexOrder] attribute [instance] Complex.starOrderedRing

end ComplexOrder

/-! ### Cauchy sequences -/

local notation "abs'" => Abs.abs

theorem isCauSeq_re (f : CauSeq ℂ Complex.abs) : IsCauSeq abs' fun n => (f n).re := fun ε ε0 =>
  (f.cauchy ε0).imp fun i H j ij =>
    lt_of_le_of_lt (by simpa using abs_re_le_abs (f j - f i)) (H _ ij)
#align complex.is_cau_seq_re Complex.isCauSeq_re

theorem isCauSeq_im (f : CauSeq ℂ Complex.abs) : IsCauSeq abs' fun n => (f n).im := fun ε ε0 =>
  (f.cauchy ε0).imp fun i H j ij =>
    lt_of_le_of_lt (by simpa using abs_im_le_abs (f j - f i)) (H _ ij)
#align complex.is_cau_seq_im Complex.isCauSeq_im

/-- The real part of a complex Cauchy sequence, as a real Cauchy sequence. -/
noncomputable def cauSeqRe (f : CauSeq ℂ Complex.abs) : CauSeq ℝ abs' :=
  ⟨_, isCauSeq_re f⟩
#align complex.cau_seq_re Complex.cauSeqRe

/-- The imaginary part of a complex Cauchy sequence, as a real Cauchy sequence. -/
noncomputable def cauSeqIm (f : CauSeq ℂ Complex.abs) : CauSeq ℝ abs' :=
  ⟨_, isCauSeq_im f⟩
#align complex.cau_seq_im Complex.cauSeqIm

theorem isCauSeq_abs {f : ℕ → ℂ} (hf : IsCauSeq Complex.abs f) :
  IsCauSeq abs' (Complex.abs ∘ f) := fun ε ε0 =>
  let ⟨i, hi⟩ := hf ε ε0
  ⟨i, fun j hj => lt_of_le_of_lt
    (Complex.abs.abs_abv_sub_le_abv_sub _ _) (hi j hj)⟩
#align complex.is_cau_seq_abs Complex.isCauSeq_abs

/-- The limit of a Cauchy sequence of complex numbers. -/
noncomputable def limAux (f : CauSeq ℂ Complex.abs) : ℂ :=
  ⟨CauSeq.lim (cauSeqRe f), CauSeq.lim (cauSeqIm f)⟩
#align complex.lim_aux Complex.limAux

theorem equiv_limAux (f : CauSeq ℂ Complex.abs) :
  f ≈ CauSeq.const Complex.abs (limAux f) := fun ε ε0 =>
  (exists_forall_ge_and
  (CauSeq.equiv_lim ⟨_, isCauSeq_re f⟩ _ (half_pos ε0))
        (CauSeq.equiv_lim ⟨_, isCauSeq_im f⟩ _ (half_pos ε0))).imp
    fun i H j ij => by
    cases' H _ ij with H₁ H₂
    apply lt_of_le_of_lt (abs_le_abs_re_add_abs_im _)
    dsimp [limAux] at *
    have := add_lt_add H₁ H₂
    rwa [add_halves] at this
#align complex.equiv_lim_aux Complex.equiv_limAux

instance : CauSeq.IsComplete ℂ Complex.abs :=
  ⟨fun f => ⟨limAux f, equiv_limAux f⟩⟩

open CauSeq

theorem lim_eq_lim_im_add_lim_re (f : CauSeq ℂ Complex.abs) :
    lim f = ↑(lim (cauSeqRe f)) + ↑(lim (cauSeqIm f)) * I :=
  lim_eq_of_equiv_const <|
    calc
      f ≈ _ := equiv_limAux f
      _ = CauSeq.const Complex.abs (↑(lim (cauSeqRe f)) + ↑(lim (cauSeqIm f)) * I) :=
        CauSeq.ext fun _ =>
          Complex.ext (by simp [limAux, cauSeqRe, ofReal']) (by simp [limAux, cauSeqIm, ofReal'])

#align complex.lim_eq_lim_im_add_lim_re Complex.lim_eq_lim_im_add_lim_re

theorem lim_re (f : CauSeq ℂ Complex.abs) : lim (cauSeqRe f) = (lim f).re := by
  rw [lim_eq_lim_im_add_lim_re] ; simp [ofReal']
#align complex.lim_re Complex.lim_re

theorem lim_im (f : CauSeq ℂ Complex.abs) : lim (cauSeqIm f) = (lim f).im := by
  rw [lim_eq_lim_im_add_lim_re] ; simp [ofReal']
#align complex.lim_im Complex.lim_im

theorem isCauSeq_conj (f : CauSeq ℂ Complex.abs) :
  IsCauSeq Complex.abs fun n => conj (f n) := fun ε ε0 =>
  let ⟨i, hi⟩ := f.2 ε ε0
  ⟨i, fun j hj => by
    rw [← RingHom.map_sub, abs_conj] ; exact hi j hj⟩
#align complex.is_cau_seq_conj Complex.isCauSeq_conj

/-- The complex conjugate of a complex Cauchy sequence, as a complex Cauchy sequence. -/
noncomputable def cauSeqConj (f : CauSeq ℂ Complex.abs) : CauSeq ℂ Complex.abs :=
  ⟨_, isCauSeq_conj f⟩
#align complex.cau_seq_conj Complex.cauSeqConj

theorem lim_conj (f : CauSeq ℂ Complex.abs) : lim (cauSeqConj f) = conj (lim f) :=
  Complex.ext (by simp [cauSeqConj, (lim_re _).symm, cauSeqRe])
    (by simp [cauSeqConj, (lim_im _).symm, cauSeqIm, (lim_neg _).symm] ; rfl)
#align complex.lim_conj Complex.lim_conj

/-- The absolute value of a complex Cauchy sequence, as a real Cauchy sequence. -/
noncomputable def cauSeqAbs (f : CauSeq ℂ Complex.abs) : CauSeq ℝ abs' :=
  ⟨_, isCauSeq_abs f.2⟩
#align complex.cau_seq_abs Complex.cauSeqAbs

theorem lim_abs (f : CauSeq ℂ Complex.abs) : lim (cauSeqAbs f) = Complex.abs (lim f) :=
  lim_eq_of_equiv_const fun ε ε0 =>
    let ⟨i, hi⟩ := equiv_lim f ε ε0
    ⟨i, fun j hj => lt_of_le_of_lt (Complex.abs.abs_abv_sub_le_abv_sub _ _) (hi j hj)⟩
#align complex.lim_abs Complex.lim_abs

variable {α : Type _} (s : Finset α)

@[simp, norm_cast]
theorem ofReal_prod (f : α → ℝ) : ((∏ i in s, f i : ℝ) : ℂ) = ∏ i in s, (f i : ℂ) :=
  map_prod ofReal _ _
#align complex.of_real_prod Complex.ofReal_prod

@[simp, norm_cast]
theorem ofReal_sum (f : α → ℝ) : ((∑ i in s, f i : ℝ) : ℂ) = ∑ i in s, (f i : ℂ) :=
  map_sum ofReal _ _
#align complex.of_real_sum Complex.ofReal_sum

@[simp]
theorem re_sum (f : α → ℂ) : (∑ i in s, f i).re = ∑ i in s, (f i).re :=
  reAddGroupHom.map_sum f s
#align complex.re_sum Complex.re_sum

@[simp]
theorem im_sum (f : α → ℂ) : (∑ i in s, f i).im = ∑ i in s, (f i).im :=
  imAddGroupHom.map_sum f s
#align complex.im_sum Complex.im_sum

end Complex


import tactic
open_locale classical
-- tells Lean to allow the law of excluded middle

variables P Q R : Prop

/--------------------------------------------------------------------------
``push_neg``

  Simplifies negations in the target, if possible.
  If ``hp : P`` is a hypothesis, then
  ``push_neg at hp,`` simplyfies the negations at ``hp``, if possible.
--------------------------------------------------------------------------/

theorem ex1 : ¬ ¬ P → P :=
begin
  push_neg,
  intro hp,
  exact hp,
end

theorem ex2 : ¬ ¬ P → P :=
begin
  intro hp,
  push_neg at hp,
  exact hp,
end

/--------------------------------------------------------------------------
``by_contradiction``

  If the current target is ``P``,
  then ``by_contradiction hnp,`` changes the target to  ``false``
  and adds a hypothesis ``hnp : ¬P``.
--------------------------------------------------------------------------/

theorem ex3 : P ∨ ¬ P :=
begin
  by_contradiction h,
  push_neg at h,
  cases h,
  apply h_left,
  exact h_right,
end

/--------------------------------------------------------------------------
``by_cases``

  If ``P`` is a proposition,
  then ``by_cases P,`` creates two hypothesis ``h : P`` and ``h : ¬P``.

This is the law of excluded middle.
--------------------------------------------------------------------------/

theorem ex4 : P ∨ ¬ P :=
begin
  by_cases P,
  left,
  exact h,
  right,
  exact h,
end

/--------------------------------------------------------------------------
Delete the ``sorry,`` below and replace them with valid proofs.
--------------------------------------------------------------------------/

theorem de_morgan1 : ¬P ∧ ¬Q → ¬(P ∨ Q) :=
begin
  sorry,
end

theorem de_morgan1_converse : ¬(P ∨ Q) → ¬P ∧ ¬Q :=
begin
  sorry,
end

theorem de_morgan2 : ¬P ∨ ¬Q → ¬(P ∧ Q) :=
begin
  sorry,
end

theorem de_morgan2_converse : ¬(P ∧ Q) → (¬P ∨ ¬Q):=
begin
  sorry,
end

theorem contrapositive_converse : (¬Q → ¬P) → (P → Q) :=
begin
  sorry,
end


/-
Copyright (c) 2019 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl
-/

import algebra.char_p.basic
import data.polynomial.algebra_map
import data.mv_polynomial.variables
import linear_algebra.finsupp_vector_space

/-!
# Multivariate polynomials over commutative rings

This file contains basic facts about multivariate polynomials over commutative rings, for example
that the monomials form a basis.

## Main definitions

* `restrict_total_degree σ R m`: the subspace of multivariate polynomials indexed by `σ` over the
  commutative ring `R` of total degree at most `m`.
* `restrict_degree σ R m`: the subspace of multivariate polynomials indexed by `σ` over the
  commutative ring `R` such that the degree in each individual variable is at most `m`.

## Main statements

* The multivariate polynomial ring over a commutative ring of positive characteristic has positive
  characteristic.
* `basis_monomials`: shows that the monomials form a basis of the vector space of multivariate
  polynomials.

## TODO

Generalise to noncommutative (semi)rings
-/

noncomputable theory

open_locale classical

open set linear_map submodule
open_locale big_operators polynomial

universes u v
variables (σ : Type u) (R : Type v) [comm_ring R] (p m : ℕ)

namespace mv_polynomial

section char_p

instance [char_p R p] : char_p (mv_polynomial σ R) p :=
{ cast_eq_zero_iff := λ n, by rw [← C_eq_coe_nat, ← C_0, C_inj, char_p.cast_eq_zero_iff R p] }

end char_p

section homomorphism

lemma map_range_eq_map {R S : Type*} [comm_ring R] [comm_ring S] (p : mv_polynomial σ R)
  (f : R →+* S) :
  finsupp.map_range f f.map_zero p = map f p :=
begin
  -- `finsupp.map_range_finset_sum` expects `f : R →+ S`
  change finsupp.map_range (f : R →+ S) (f : R →+ S).map_zero p = map f p,
  rw [p.as_sum, finsupp.map_range_finset_sum, (map f).map_sum],
  refine finset.sum_congr rfl (assume n _, _),
  rw [map_monomial, ← single_eq_monomial, finsupp.map_range_single, single_eq_monomial,
    f.coe_add_monoid_hom],
end

end homomorphism

section degree

/-- The submodule of polynomials of total degree less than or equal to `m`.-/
def restrict_total_degree : submodule R (mv_polynomial σ R) :=
finsupp.supported _ _ {n | n.sum (λn e, e) ≤ m }

/-- The submodule of polynomials such that the degree with respect to each individual variable is
less than or equal to `m`.-/
def restrict_degree (m : ℕ) : submodule R (mv_polynomial σ R) :=
finsupp.supported _ _ {n | ∀i, n i ≤ m }

variable {R}

lemma mem_restrict_total_degree (p : mv_polynomial σ R) :
  p ∈ restrict_total_degree σ R m ↔ p.total_degree ≤ m :=
begin
  rw [total_degree, finset.sup_le_iff],
  refl
end

lemma mem_restrict_degree (p : mv_polynomial σ R) (n : ℕ) :
  p ∈ restrict_degree σ R n ↔ (∀s ∈ p.support, ∀i, (s : σ →₀ ℕ) i ≤ n) :=
begin
  rw [restrict_degree, finsupp.mem_supported],
  refl
end

lemma mem_restrict_degree_iff_sup (p : mv_polynomial σ R) (n : ℕ) :
  p ∈ restrict_degree σ R n ↔ ∀i, p.degrees.count i ≤ n :=
begin
  simp only [mem_restrict_degree, degrees, multiset.count_finset_sup, finsupp.count_to_multiset,
    finset.sup_le_iff],
  exact ⟨assume h n s hs, h s hs n, assume h s hs n, h n s hs⟩
end

variables (σ R)

/-- The monomials form a basis on `mv_polynomial σ R`. -/
def basis_monomials : basis (σ →₀ ℕ) R (mv_polynomial σ R) := finsupp.basis_single_one

@[simp] lemma coe_basis_monomials :
  (basis_monomials σ R : (σ →₀ ℕ) → mv_polynomial σ R) = λ s, monomial s 1 :=
rfl

lemma linear_independent_X : linear_independent R (X : σ → mv_polynomial σ R) :=
(basis_monomials σ R).linear_independent.comp
  (λ s : σ, finsupp.single s 1) (finsupp.single_left_injective one_ne_zero)

end degree

end mv_polynomial

/- this is here to avoid import cycle issues -/
namespace polynomial

/-- The monomials form a basis on `R[X]`. -/
noncomputable def basis_monomials : basis ℕ R R[X] :=
basis.of_repr (to_finsupp_iso_alg R).to_linear_equiv

@[simp] lemma coe_basis_monomials :
  (basis_monomials R : ℕ → R[X]) = λ s, monomial s 1 :=
_root_.funext $ λ n, of_finsupp_single _ _

end polynomial


-- IMO 1962 Q4
-- Resolver la ecuación cos x ^ 2 + cos (2 * x) ^ 2 + cos (3 * x) ^ 2 = 1`

import analysis.special_functions.trigonometric

open real
open_locale real
noncomputable theory

def problema (x : ℝ) : Prop :=
cos x ^ 2 + cos (2 * x) ^ 2 + cos (3 * x) ^ 2 = 1

def funAuxiliar (x : ℝ) : ℝ :=
cos x * (cos x ^ 2 - 1/2) * cos (3 * x)

lemma Igualdad {x : ℝ} :
  (cos x ^ 2 + cos (2 * x) ^ 2 + cos (3 * x) ^ 2 - 1) / 4 = funAuxiliar x :=
begin
  rw funAuxiliar,
  rw real.cos_two_mul,
  rw cos_three_mul,
  ring_nf,
end

lemma Equivalencia
  {x : ℝ}
  : problema x ↔ funAuxiliar x = 0 :=
begin
  split,
  { intro h1,
    rw problema at h1,
    rw ← Igualdad,
    rw div_eq_zero_iff,
    norm_num,
    rw sub_eq_zero,
    exact h1, },
  { intro h2,
    rw problema,
    rw ← Igualdad at h2,
    rw div_eq_zero_iff at h2,
    norm_num at h2,
    rw sub_eq_zero at h2,
    exact h2, },
end

lemma CasosSolucion
  {x : ℝ}
  : funAuxiliar x = 0 ↔ cos x ^ 2 = 1/2 ∨ cos (3 * x) = 0 :=
begin
  rw funAuxiliar,
  rw mul_assoc,
  rw mul_eq_zero,
  rw mul_eq_zero,
  rw sub_eq_zero,
  split,
  { intro h1,
    cases h1 with h11 h12,
    right,
    rw cos_three_mul,
    rw h11,
    ring,
    exact h12,},
  { intro h2,
    right,
    exact h2,},
end

lemma SolucionCosenoCuadrado
  {x : ℝ}
  : cos x ^ 2 = 1/2 ↔ ∃ k : ℤ, x = (2 * k + 1) * π / 4 :=
begin
  rw cos_sq,
  rw add_right_eq_self,
  rw div_eq_zero_iff,
  norm_num,
  split,
  { intro h1,
    rw cos_eq_zero_iff at h1,
    cases h1 with k1 hk1,
    use k1,
    linarith, },
  { intro h2,
    cases h2 with k2 hk2,
    rw cos_eq_zero_iff,
    use k2,
    linarith,},
end

lemma SolucionCosenoTriple
  {x : ℝ}
  : cos (3 * x) = 0 ↔ ∃ k : ℤ, x = (2 * k + 1) * π / 6 :=
begin
  rw cos_eq_zero_iff,
  split,
  { intro h1,
    cases h1 with k1 hk1,
    use k1,
    linarith,},
  { intro h2,
    cases h2 with k2 hk2,
    use k2,
    linarith,},
end

def Solucion : set ℝ :=
{x : ℝ | ∃ k : ℤ, x = (2 * k + 1) * π / 4 ∨ x = (2 * k + 1) * π / 6}

theorem imo1962_q4
  {x : ℝ}
  : problema x ↔ x ∈ Solucion :=
begin
  rw Equivalencia,
  rw CasosSolucion,
  rw SolucionCosenoTriple,
  rw SolucionCosenoCuadrado,
  rw Solucion,
  exact exists_or_distrib.symm,
end


-- Base-level definition of naturals
inductive mynat : Type
    | zero : mynat
    | succ : mynat -> mynat

open mynat

-------------------------------------------------------------------------------
--                                Addition                                   --
-------------------------------------------------------------------------------

-- Addition on naturals
def add : mynat -> mynat -> mynat
    | m zero     := m
    | m (succ n) := succ (add m n)

-------------------------
-- Prerequisite Proofs --

-- Proving that zero is the additive identity
lemma mynat_add_zero (n : mynat) : (add n zero) = n :=
    begin
        rw add,
    end

-- Proving that zero is the additive identity, even in reverse order.
lemma mynat_zero_add (n : mynat) :
    (add zero n) = n :=
    begin
        induction n with d hd,

        -- Base case
        rw mynat_add_zero,

        -- Inductive case
        rw add,
        rw hd,
    end

lemma mynat_add_succ (x y : mynat) :
    succ (add x y) = add (succ x) y :=
    begin
        induction y with d hd,

        -- Base case
        rw [mynat_add_zero, mynat_add_zero],

        -- Inductive case
        rw add,
        rw hd,
        rw add,
    end

-------------------------
-- Commutativity Proof --
lemma add_commutativity (x y : mynat) :
    add x y = add y x :=
    begin
        induction y with d hd,

        -- Base case
        rw mynat_add_zero,
        rw mynat_zero_add,

        -- Inductive case
        rw add,
        rw <- mynat_add_succ,
        rw hd,
    end

-------------------------
-- Associativity Proof --
lemma add_associativity (x y z : mynat) :
    add x (add y z) = add (add x y) z :=
    begin
        induction x with d hd,

        -- Base case
        rw [mynat_zero_add, mynat_zero_add],

        -- Inductive case
        rw <- mynat_add_succ,
        rw hd,
        rw <- mynat_add_succ,
        rw <- mynat_add_succ,
    end

-------------------------------------------------------------------------------
--                              Multiplication                               --
-------------------------------------------------------------------------------

-- Multiplication on naturals
def mul : mynat -> mynat -> mynat
    | m zero     := zero
    | m (succ n) := add m (mul m n)

-------------------------
-- Prerequisite Proofs --

lemma mynat_zero_mul (n : mynat) :
    mul zero n = zero :=
    begin
        induction n with d hd,

        -- Base case
        rw mul,

        -- Inductive case
        rw mul,
        rw hd,
        rw add,
    end

lemma mynat_mul_succ (x y : mynat) :
    mul (succ x) y = add y (mul x y) :=
    begin
        -- this is a lie
        sorry,
    end

-------------------------
-- Commutativity Proof --
lemma mul_commutativity (x y : mynat) :
    mul x y = mul y x :=
    begin
        induction y with d hd,

        -- Base case
        rw mul,
        rw mynat_zero_mul,

        -- Inductive case
        rw mul,
        rw mynat_mul_succ,
        rw hd,
    end

-------------------------
-- Associativity Proof --
lemma mul_associativity (x y z : mynat) :
    mul x (mul y z) = mul (mul x y) z :=
    begin
        induction z with d hd,

        -- Base Case
        sorry,

        -- Inductive Case
        sorry,
    end


import MyNat.Definition
import AdditionWorld.Level4 -- add_comm
import AdvancedAdditionWorld.Level5 -- add_right_cancel
namespace MyNat
open MyNat

/-!

# Advanced Addition World

## Level 7: `add_right_cancel_iff`

It's sometimes convenient to have the "if and only if" version
of theorems like `add_right_cancel`. Remember that you can use `constructor`
to split an `↔` goal into the `→` goal and the `←` goal.

## Pro tip:

Notice `exact add_right_cancel _ _ _` means "let Lean figure out the missing inputs"
so we don't have to spell it out like we did in Level 6.

## Theorem
For all naturals `a`, `b` and `t`, `a + t = b + t ↔ a = b.`
-/
theorem add_right_cancel_iff (t a b : MyNat) :  a + t = b + t ↔ a = b := by
  constructor
  exact add_right_cancel _ _ _
  intro h
  rw [h]

/-!
Next up [Level 8](./Level8.lean.md)
-/


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

import tactic

/-

# Prove that for every positive integer n the number 3(1^5 +2^5 +...+n^5)
# is divisible by 1^3+2^3+...+n^3

This is question 9 in Sierpinski's book

-/

open_locale big_operators

open finset

example (n : ℕ) : (∑ i in range n, i^3) ∣ (3 * ∑ i in range n, i^5) :=
begin
  sorry,
end


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

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

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

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

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


-- Distributiva_de_la_interseccion_respecto_de_la_union_general.lean
-- Distributiva de la intersección respecto de la unión general
-- José A. Alonso Jiménez <https://jaalonso.github.io>
-- Sevilla, 28-abril-2022
-- ---------------------------------------------------------------------

-- ---------------------------------------------------------------------
-- Demostrar que
--    s ∩ (⋃ i, A i) = ⋃ i, (A i ∩ s)
-- ----------------------------------------------------------------------

import data.set.basic
import data.set.lattice
import tactic

open set

variable {α : Type}
variable s : set α
variable A : ℕ → set α

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

example : s ∩ (⋃ i, A i) = ⋃ i, (A i ∩ s) :=
begin
  ext x,
  split,
  { intro h,
    rw mem_Union,
    cases h with xs xUAi,
    rw mem_Union at xUAi,
    cases xUAi with i xAi,
    use i,
    split,
    { exact xAi, },
    { exact xs, }},
  { intro h,
    rw mem_Union at h,
    cases h with i hi,
    cases hi with xAi xs,
    split,
    { exact xs, },
    { rw mem_Union,
      use i,
      exact xAi, }},
end

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

example : s ∩ (⋃ i, A i) = ⋃ i, (A i ∩ s) :=
begin
  ext x,
  simp,
  split,
  { rintros ⟨xs, ⟨i, xAi⟩⟩,
    exact ⟨⟨i, xAi⟩, xs⟩, },
  { rintros ⟨⟨i, xAi⟩, xs⟩,
    exact ⟨xs, ⟨i, xAi⟩⟩ },
end

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

example : s ∩ (⋃ i, A i) = ⋃ i, (A i ∩ s) :=
begin
  ext x,
  finish,
end

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

example : s ∩ (⋃ i, A i) = ⋃ i, (A i ∩ s) :=
by ext; finish

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

example : s ∩ (⋃ i, A i) = ⋃ i, (A i ∩ s) :=
by finish [ext_iff]

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

example : s ∩ (⋃ i, A i) = ⋃ i, (A i ∩ s) :=
by tidy


/-
Copyright (c) 2021 Heather Macbeth. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Heather Macbeth, Yury Kudryashov
-/
import topology.order.basic

/-!
# Bounded monotone sequences converge

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

In this file we prove a few theorems of the form “if the range of a monotone function `f : ι → α`
admits a least upper bound `a`, then `f x` tends to `a` as `x → ∞`”, as well as version of this
statement for (conditionally) complete lattices that use `⨆ x, f x` instead of `is_lub`.

These theorems work for linear orders with order topologies as well as their products (both in terms
of `prod` and in terms of function types). In order to reduce code duplication, we introduce two
typeclasses (one for the property formulated above and one for the dual property), prove theorems
assuming one of these typeclasses, and provide instances for linear orders and their products.

We also prove some "inverse" results: if `f n` is a monotone sequence and `a` is its limit,
then `f n ≤ a` for all `n`.

## Tags

monotone convergence
-/

open filter set function
open_locale filter topology classical

variables {α β : Type*}

/-- We say that `α` is a `Sup_convergence_class` if the following holds. Let `f : ι → α` be a
monotone function, let `a : α` be a least upper bound of `set.range f`. Then `f x` tends to `𝓝 a` as
`x → ∞` (formally, at the filter `filter.at_top`). We require this for `ι = (s : set α)`, `f = coe`
in the definition, then prove it for any `f` in `tendsto_at_top_is_lub`.

This property holds for linear orders with order topology as well as their products. -/
class Sup_convergence_class (α : Type*) [preorder α] [topological_space α] : Prop :=
(tendsto_coe_at_top_is_lub : ∀ (a : α) (s : set α), is_lub s a → tendsto (coe : s → α) at_top (𝓝 a))

/-- We say that `α` is an `Inf_convergence_class` if the following holds. Let `f : ι → α` be a
monotone function, let `a : α` be a greatest lower bound of `set.range f`. Then `f x` tends to `𝓝 a`
as `x → -∞` (formally, at the filter `filter.at_bot`). We require this for `ι = (s : set α)`,
`f = coe` in the definition, then prove it for any `f` in `tendsto_at_bot_is_glb`.

This property holds for linear orders with order topology as well as their products. -/
class Inf_convergence_class (α : Type*) [preorder α] [topological_space α] : Prop :=
(tendsto_coe_at_bot_is_glb : ∀ (a : α) (s : set α), is_glb s a → tendsto (coe : s → α) at_bot (𝓝 a))

instance order_dual.Sup_convergence_class [preorder α] [topological_space α]
  [Inf_convergence_class α] : Sup_convergence_class αᵒᵈ :=
⟨‹Inf_convergence_class α›.1⟩

instance order_dual.Inf_convergence_class [preorder α] [topological_space α]
  [Sup_convergence_class α] : Inf_convergence_class αᵒᵈ :=
⟨‹Sup_convergence_class α›.1⟩

@[priority 100] -- see Note [lower instance priority]
instance linear_order.Sup_convergence_class [topological_space α] [linear_order α]
  [order_topology α] : Sup_convergence_class α :=
begin
  refine ⟨λ a s ha, tendsto_order.2 ⟨λ b hb, _, λ b hb, _⟩⟩,
  { rcases ha.exists_between hb with ⟨c, hcs, bc, bca⟩,
    lift c to s using hcs,
    refine (eventually_ge_at_top c).mono (λ x hx, bc.trans_le hx) },
  { exact eventually_of_forall (λ x, (ha.1 x.2).trans_lt hb) }
end

@[priority 100] -- see Note [lower instance priority]
instance linear_order.Inf_convergence_class [topological_space α] [linear_order α]
  [order_topology α] : Inf_convergence_class α :=
show Inf_convergence_class αᵒᵈᵒᵈ, from order_dual.Inf_convergence_class

section

variables {ι : Type*} [preorder ι] [topological_space α]

section is_lub

variables [preorder α] [Sup_convergence_class α] {f : ι → α} {a : α}

lemma tendsto_at_top_is_lub (h_mono : monotone f) (ha : is_lub (set.range f) a) :
  tendsto f at_top (𝓝 a) :=
begin
  suffices : tendsto (range_factorization f) at_top at_top,
    from (Sup_convergence_class.tendsto_coe_at_top_is_lub _ _ ha).comp this,
  exact h_mono.range_factorization.tendsto_at_top_at_top (λ b, b.2.imp $ λ a ha, ha.ge)
end

lemma tendsto_at_bot_is_lub (h_anti : antitone f) (ha : is_lub (set.range f) a) :
  tendsto f at_bot (𝓝 a) :=
by convert tendsto_at_top_is_lub h_anti.dual_left ha

end is_lub

section is_glb

variables [preorder α] [Inf_convergence_class α] {f : ι → α} {a : α}

lemma tendsto_at_bot_is_glb (h_mono : monotone f) (ha : is_glb (set.range f) a) :
  tendsto f at_bot (𝓝 a) :=
by convert tendsto_at_top_is_lub h_mono.dual ha.dual

lemma tendsto_at_top_is_glb (h_anti : antitone f) (ha : is_glb (set.range f) a) :
  tendsto f at_top (𝓝 a) :=
by convert tendsto_at_bot_is_lub h_anti.dual ha.dual

end is_glb

section csupr

variables [conditionally_complete_lattice α] [Sup_convergence_class α] {f : ι → α} {a : α}

lemma tendsto_at_top_csupr (h_mono : monotone f) (hbdd : bdd_above $ range f) :
  tendsto f at_top (𝓝 (⨆i, f i)) :=
begin
  casesI is_empty_or_nonempty ι,
  exacts [tendsto_of_is_empty, tendsto_at_top_is_lub h_mono (is_lub_csupr hbdd)]
end

lemma tendsto_at_bot_csupr (h_anti : antitone f) (hbdd : bdd_above $ range f) :
  tendsto f at_bot (𝓝 (⨆ i, f i)) :=
by convert tendsto_at_top_csupr h_anti.dual hbdd.dual

end csupr

section cinfi

variables [conditionally_complete_lattice α] [Inf_convergence_class α] {f : ι → α} {a : α}

lemma tendsto_at_bot_cinfi (h_mono : monotone f) (hbdd : bdd_below $ range f) :
  tendsto f at_bot (𝓝 (⨅ i, f i)) :=
by convert tendsto_at_top_csupr h_mono.dual hbdd.dual

lemma tendsto_at_top_cinfi (h_anti : antitone f) (hbdd : bdd_below $ range f) :
  tendsto f at_top (𝓝 (⨅ i, f i)) :=
by convert tendsto_at_bot_csupr h_anti.dual hbdd.dual

end cinfi

section supr

variables [complete_lattice α] [Sup_convergence_class α] {f : ι → α} {a : α}

lemma tendsto_at_top_supr (h_mono : monotone f) : tendsto f at_top (𝓝 (⨆i, f i)) :=
tendsto_at_top_csupr h_mono (order_top.bdd_above _)

lemma tendsto_at_bot_supr (h_anti : antitone f) :
  tendsto f at_bot (𝓝 (⨆i, f i)) :=
tendsto_at_bot_csupr h_anti (order_top.bdd_above _)

end supr

section infi

variables [complete_lattice α] [Inf_convergence_class α] {f : ι → α} {a : α}

lemma tendsto_at_bot_infi (h_mono : monotone f) : tendsto f at_bot (𝓝 (⨅i, f i)) :=
tendsto_at_bot_cinfi h_mono (order_bot.bdd_below _)

lemma tendsto_at_top_infi (h_anti : antitone f) :
  tendsto f at_top (𝓝 (⨅i, f i)) :=
tendsto_at_top_cinfi h_anti (order_bot.bdd_below _)

end infi

end

instance [preorder α] [preorder β] [topological_space α] [topological_space β]
  [Sup_convergence_class α] [Sup_convergence_class β] : Sup_convergence_class (α × β) :=
begin
  constructor,
  rintro ⟨a, b⟩ s h,
  rw [is_lub_prod, ← range_restrict, ← range_restrict] at h,
  have A : tendsto (λ x : s, (x : α × β).1) at_top (𝓝 a),
    from tendsto_at_top_is_lub (monotone_fst.restrict s) h.1,
  have B : tendsto (λ x : s, (x : α × β).2) at_top (𝓝 b),
    from tendsto_at_top_is_lub (monotone_snd.restrict s) h.2,
  convert A.prod_mk_nhds B,
  ext1 ⟨⟨x, y⟩, h⟩, refl
end

instance [preorder α] [preorder β] [topological_space α] [topological_space β]
  [Inf_convergence_class α] [Inf_convergence_class β] : Inf_convergence_class (α × β) :=
show Inf_convergence_class (αᵒᵈ × βᵒᵈ)ᵒᵈ, from order_dual.Inf_convergence_class

instance {ι : Type*} {α : ι → Type*} [Π i, preorder (α i)] [Π i, topological_space (α i)]
  [Π i, Sup_convergence_class (α i)] : Sup_convergence_class (Π i, α i) :=
begin
  refine ⟨λ f s h, _⟩,
  simp only [is_lub_pi, ← range_restrict] at h,
  exact tendsto_pi_nhds.2 (λ i, tendsto_at_top_is_lub ((monotone_eval _).restrict _) (h i))
end

instance {ι : Type*} {α : ι → Type*} [Π i, preorder (α i)] [Π i, topological_space (α i)]
  [Π i, Inf_convergence_class (α i)] : Inf_convergence_class (Π i, α i) :=
show Inf_convergence_class (Π i, (α i)ᵒᵈ)ᵒᵈ, from order_dual.Inf_convergence_class

instance pi.Sup_convergence_class' {ι : Type*} [preorder α] [topological_space α]
  [Sup_convergence_class α] : Sup_convergence_class (ι → α) :=
pi.Sup_convergence_class

instance pi.Inf_convergence_class' {ι : Type*} [preorder α] [topological_space α]
  [Inf_convergence_class α] : Inf_convergence_class (ι → α) :=
pi.Inf_convergence_class

lemma tendsto_of_monotone {ι α : Type*} [preorder ι] [topological_space α]
  [conditionally_complete_linear_order α] [order_topology α] {f : ι → α} (h_mono : monotone f) :
  tendsto f at_top at_top ∨ (∃ l, tendsto f at_top (𝓝 l)) :=
if H : bdd_above (range f) then or.inr ⟨_, tendsto_at_top_csupr h_mono H⟩
else or.inl $ tendsto_at_top_at_top_of_monotone' h_mono H

lemma tendsto_iff_tendsto_subseq_of_monotone {ι₁ ι₂ α : Type*} [semilattice_sup ι₁] [preorder ι₂]
  [nonempty ι₁] [topological_space α] [conditionally_complete_linear_order α] [order_topology α]
  [no_max_order α] {f : ι₂ → α} {φ : ι₁ → ι₂} {l : α} (hf : monotone f)
  (hg : tendsto φ at_top at_top) :
  tendsto f at_top (𝓝 l) ↔ tendsto (f ∘ φ) at_top (𝓝 l) :=
begin
  split; intro h,
  { exact h.comp hg },
  { rcases tendsto_of_monotone hf with h' | ⟨l', hl'⟩,
    { exact (not_tendsto_at_top_of_tendsto_nhds h (h'.comp hg)).elim },
    { rwa tendsto_nhds_unique h (hl'.comp hg) } }
end

/-! The next family of results, such as `is_lub_of_tendsto_at_top` and `supr_eq_of_tendsto`, are
converses to the standard fact that bounded monotone functions converge. They state, that if a
monotone function `f` tends to `a` along `filter.at_top`, then that value `a` is a least upper bound
for the range of `f`.

Related theorems above (`is_lub.is_lub_of_tendsto`, `is_glb.is_glb_of_tendsto` etc) cover the case
when `f x` tends to `a` as `x` tends to some point `b` in the domain. -/

lemma monotone.ge_of_tendsto [topological_space α] [preorder α] [order_closed_topology α]
  [semilattice_sup β] {f : β → α} {a : α} (hf : monotone f)
  (ha : tendsto f at_top (𝓝 a)) (b : β) :
  f b ≤ a :=
begin
  haveI : nonempty β := nonempty.intro b,
  exact ge_of_tendsto ha ((eventually_ge_at_top b).mono (λ _ hxy, hf hxy))
end

lemma monotone.le_of_tendsto [topological_space α] [preorder α] [order_closed_topology α]
  [semilattice_inf β] {f : β → α} {a : α} (hf : monotone f)
  (ha : tendsto f at_bot (𝓝 a)) (b : β) :
  a ≤ f b :=
hf.dual.ge_of_tendsto ha b

lemma antitone.le_of_tendsto [topological_space α] [preorder α] [order_closed_topology α]
  [semilattice_sup β] {f : β → α} {a : α} (hf : antitone f)
  (ha : tendsto f at_top (𝓝 a)) (b : β) :
  a ≤ f b :=
hf.dual_right.ge_of_tendsto ha b

lemma antitone.ge_of_tendsto [topological_space α] [preorder α] [order_closed_topology α]
  [semilattice_inf β] {f : β → α} {a : α} (hf : antitone f)
  (ha : tendsto f at_bot (𝓝 a)) (b : β) :
  f b ≤ a :=
hf.dual_right.le_of_tendsto ha b

lemma is_lub_of_tendsto_at_top [topological_space α] [preorder α] [order_closed_topology α]
  [nonempty β] [semilattice_sup β] {f : β → α} {a : α} (hf : monotone f)
  (ha : tendsto f at_top (𝓝 a)) :
  is_lub (set.range f) a :=
begin
  split,
  { rintros _ ⟨b, rfl⟩,
    exact hf.ge_of_tendsto ha b },
  { exact λ _ hb, le_of_tendsto' ha (λ x, hb (set.mem_range_self x)) }
end

lemma is_glb_of_tendsto_at_bot [topological_space α] [preorder α] [order_closed_topology α]
  [nonempty β] [semilattice_inf β] {f : β → α} {a : α} (hf : monotone f)
  (ha : tendsto f at_bot (𝓝 a)) :
  is_glb (set.range f) a :=
@is_lub_of_tendsto_at_top αᵒᵈ βᵒᵈ _ _ _ _ _ _ _ hf.dual ha

lemma is_lub_of_tendsto_at_bot [topological_space α] [preorder α] [order_closed_topology α]
  [nonempty β] [semilattice_inf β] {f : β → α} {a : α} (hf : antitone f)
  (ha : tendsto f at_bot (𝓝 a)) :
  is_lub (set.range f) a :=
@is_lub_of_tendsto_at_top α βᵒᵈ  _ _ _ _ _ _ _ hf.dual_left ha

lemma is_glb_of_tendsto_at_top [topological_space α] [preorder α] [order_closed_topology α]
  [nonempty β] [semilattice_sup β] {f : β → α} {a : α} (hf : antitone f)
  (ha : tendsto f at_top (𝓝 a)) :
  is_glb (set.range f) a :=
@is_glb_of_tendsto_at_bot α βᵒᵈ  _ _ _ _ _ _ _ hf.dual_left ha

lemma supr_eq_of_tendsto {α β} [topological_space α] [complete_linear_order α] [order_topology α]
  [nonempty β] [semilattice_sup β] {f : β → α} {a : α} (hf : monotone f) :
  tendsto f at_top (𝓝 a) → supr f = a :=
tendsto_nhds_unique (tendsto_at_top_supr hf)

lemma infi_eq_of_tendsto {α} [topological_space α] [complete_linear_order α] [order_topology α]
  [nonempty β] [semilattice_sup β] {f : β → α} {a : α} (hf : antitone f) :
  tendsto f at_top (𝓝 a) → infi f = a :=
tendsto_nhds_unique (tendsto_at_top_infi hf)

lemma supr_eq_supr_subseq_of_monotone {ι₁ ι₂ α : Type*} [preorder ι₂] [complete_lattice α]
  {l : filter ι₁} [l.ne_bot] {f : ι₂ → α} {φ : ι₁ → ι₂} (hf : monotone f)
  (hφ : tendsto φ l at_top) :
  (⨆ i, f i) = (⨆ i, f (φ i)) :=
le_antisymm
  (supr_mono' $ λ i, exists_imp_exists (λ j (hj : i ≤ φ j), hf hj)
    (hφ.eventually $ eventually_ge_at_top i).exists)
  (supr_mono' $ λ i, ⟨φ i, le_rfl⟩)

lemma infi_eq_infi_subseq_of_monotone {ι₁ ι₂ α : Type*} [preorder ι₂] [complete_lattice α]
  {l : filter ι₁} [l.ne_bot] {f : ι₂ → α} {φ : ι₁ → ι₂} (hf : monotone f)
  (hφ : tendsto φ l at_bot) :
  (⨅ i, f i) = (⨅ i, f (φ i)) :=
supr_eq_supr_subseq_of_monotone hf.dual hφ