/-
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
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.real.sqrt
import Mathlib.PostPort

universes l u_1

namespace Mathlib

/-!
# The complex numbers

The complex numbers are modelled as ℝ^2 in the obvious way.
-/

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

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

notation:1024 "ℂ" => Mathlib.complex

namespace complex

protected instance decidable_eq : DecidableEq ℂ := classical.dec_eq ℂ

/-- The equivalence between the complex numbers and `ℝ × ℝ`. -/
def equiv_real_prod : ℂ ≃ ℝ × ℝ :=
  equiv.mk (fun (z : ℂ) => (re z, im z)) (fun (p : ℝ × ℝ) => mk (prod.fst p) (prod.snd p)) sorry
    sorry

@[simp] theorem equiv_real_prod_apply (z : ℂ) : coe_fn equiv_real_prod z = (re z, im z) := rfl

theorem equiv_real_prod_symm_re (x : ℝ) (y : ℝ) :
    re (coe_fn (equiv.symm equiv_real_prod) (x, y)) = x :=
  rfl

theorem equiv_real_prod_symm_im (x : ℝ) (y : ℝ) :
    im (coe_fn (equiv.symm equiv_real_prod) (x, y)) = y :=
  rfl

@[simp] theorem eta (z : ℂ) : mk (re z) (im z) = z :=
  cases_on z
    fun (z_re z_im : ℝ) =>
      idRhs
        (mk (re (mk z_re z_im)) (im (mk z_re z_im)) = mk (re (mk z_re z_im)) (im (mk z_re z_im)))
        rfl

theorem ext {z : ℂ} {w : ℂ} : re z = re w → im z = im w → z = w := sorry

theorem ext_iff {z : ℂ} {w : ℂ} : z = w ↔ re z = re w ∧ im z = im w := sorry

protected instance has_coe : has_coe ℝ ℂ := has_coe.mk fun (r : ℝ) => mk r 0

@[simp] theorem of_real_re (r : ℝ) : re ↑r = r := rfl

@[simp] theorem of_real_im (r : ℝ) : im ↑r = 0 := rfl

@[simp] theorem of_real_inj {z : ℝ} {w : ℝ} : ↑z = ↑w ↔ z = w :=
  { mp := congr_arg re, mpr := congr_arg fun {z : ℝ} => ↑z }

protected instance has_zero : HasZero ℂ := { zero := ↑0 }

protected instance inhabited : Inhabited ℂ := { default := 0 }

@[simp] theorem zero_re : re 0 = 0 := rfl

@[simp] theorem zero_im : im 0 = 0 := rfl

@[simp] theorem of_real_zero : ↑0 = 0 := rfl

@[simp] theorem of_real_eq_zero {z : ℝ} : ↑z = 0 ↔ z = 0 := of_real_inj

theorem of_real_ne_zero {z : ℝ} : ↑z ≠ 0 ↔ z ≠ 0 := not_congr of_real_eq_zero

protected instance has_one : HasOne ℂ := { one := ↑1 }

@[simp] theorem one_re : re 1 = 1 := rfl

@[simp] theorem one_im : im 1 = 0 := rfl

@[simp] theorem of_real_one : ↑1 = 1 := rfl

protected instance has_add : Add ℂ := { add := fun (z w : ℂ) => mk (re z + re w) (im z + im w) }

@[simp] theorem add_re (z : ℂ) (w : ℂ) : re (z + w) = re z + re w := rfl

@[simp] theorem add_im (z : ℂ) (w : ℂ) : im (z + w) = im z + im w := rfl

@[simp] theorem bit0_re (z : ℂ) : re (bit0 z) = bit0 (re z) := rfl

@[simp] theorem bit1_re (z : ℂ) : re (bit1 z) = bit1 (re z) := rfl

@[simp] theorem bit0_im (z : ℂ) : im (bit0 z) = bit0 (im z) := Eq.refl (im (bit0 z))

@[simp] theorem bit1_im (z : ℂ) : im (bit1 z) = bit0 (im z) := add_zero (im (bit0 z))

@[simp] theorem of_real_add (r : ℝ) (s : ℝ) : ↑(r + s) = ↑r + ↑s := sorry

@[simp] theorem of_real_bit0 (r : ℝ) : ↑(bit0 r) = bit0 ↑r := sorry

@[simp] theorem of_real_bit1 (r : ℝ) : ↑(bit1 r) = bit1 ↑r := sorry

protected instance has_neg : Neg ℂ := { neg := fun (z : ℂ) => mk (-re z) (-im z) }

@[simp] theorem neg_re (z : ℂ) : re (-z) = -re z := rfl

@[simp] theorem neg_im (z : ℂ) : im (-z) = -im z := rfl

@[simp] theorem of_real_neg (r : ℝ) : ↑(-r) = -↑r := sorry

protected instance has_sub : Sub ℂ := { sub := fun (z w : ℂ) => mk (re z - re w) (im z - im w) }

protected instance has_mul : Mul ℂ :=
  { mul := fun (z w : ℂ) => mk (re z * re w - im z * im w) (re z * im w + im z * re w) }

@[simp] theorem mul_re (z : ℂ) (w : ℂ) : re (z * w) = re z * re w - im z * im w := rfl

@[simp] theorem mul_im (z : ℂ) (w : ℂ) : im (z * w) = re z * im w + im z * re w := rfl

@[simp] theorem of_real_mul (r : ℝ) (s : ℝ) : ↑(r * s) = ↑r * ↑s := sorry

theorem smul_re (r : ℝ) (z : ℂ) : re (↑r * z) = r * re z := sorry

theorem smul_im (r : ℝ) (z : ℂ) : im (↑r * z) = r * im z := sorry

theorem of_real_smul (r : ℝ) (z : ℂ) : ↑r * z = mk (r * re z) (r * im z) :=
  ext (smul_re r z) (smul_im r z)

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

/-- The imaginary unit. -/
def I : ℂ := mk 0 1

@[simp] theorem I_re : re I = 0 := rfl

@[simp] theorem I_im : im I = 1 := rfl

@[simp] theorem I_mul_I : I * I = -1 := sorry

theorem I_mul (z : ℂ) : I * z = mk (-im z) (re z) := sorry

theorem I_ne_zero : I ≠ 0 := mt (congr_arg im) (ne.symm zero_ne_one)

theorem mk_eq_add_mul_I (a : ℝ) (b : ℝ) : mk a b = ↑a + ↑b * I := sorry

@[simp] theorem re_add_im (z : ℂ) : ↑(re z) + ↑(im z) * I = z := sorry

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

protected instance comm_ring : comm_ring ℂ :=
  comm_ring.mk Add.add sorry 0 sorry sorry Neg.neg Sub.sub sorry sorry Mul.mul sorry 1 sorry sorry
    sorry sorry sorry

protected instance re.is_add_group_hom : is_add_group_hom re := is_add_group_hom.mk

protected instance im.is_add_group_hom : is_add_group_hom im := is_add_group_hom.mk

@[simp] theorem I_pow_bit0 (n : ℕ) : I ^ bit0 n = (-1) ^ n :=
  eq.mpr (id (Eq._oldrec (Eq.refl (I ^ bit0 n = (-1) ^ n)) (pow_bit0' I n)))
    (eq.mpr (id (Eq._oldrec (Eq.refl ((I * I) ^ n = (-1) ^ n)) I_mul_I)) (Eq.refl ((-1) ^ n)))

@[simp] theorem I_pow_bit1 (n : ℕ) : I ^ bit1 n = (-1) ^ n * I :=
  eq.mpr (id (Eq._oldrec (Eq.refl (I ^ bit1 n = (-1) ^ n * I)) (pow_bit1' I n)))
    (eq.mpr (id (Eq._oldrec (Eq.refl ((I * I) ^ n * I = (-1) ^ n * I)) I_mul_I))
      (Eq.refl ((-1) ^ n * I)))

/-! ### Complex conjugation -/

/-- The complex conjugate. -/
def conj : ℂ →+* ℂ := ring_hom.mk (fun (z : ℂ) => mk (re z) (-im z)) sorry sorry sorry sorry

@[simp] theorem conj_re (z : ℂ) : re (coe_fn conj z) = re z := rfl

@[simp] theorem conj_im (z : ℂ) : im (coe_fn conj z) = -im z := rfl

@[simp] theorem conj_of_real (r : ℝ) : coe_fn conj ↑r = ↑r := sorry

@[simp] theorem conj_I : coe_fn conj I = -I := sorry

@[simp] theorem conj_bit0 (z : ℂ) : coe_fn conj (bit0 z) = bit0 (coe_fn conj z) := sorry

@[simp] theorem conj_bit1 (z : ℂ) : coe_fn conj (bit1 z) = bit1 (coe_fn conj z) := sorry

@[simp] theorem conj_neg_I : coe_fn conj (-I) = I := sorry

@[simp] theorem conj_conj (z : ℂ) : coe_fn conj (coe_fn conj z) = z := sorry

theorem conj_involutive : function.involutive ⇑conj := conj_conj

theorem conj_bijective : function.bijective ⇑conj := function.involutive.bijective conj_involutive

theorem conj_inj {z : ℂ} {w : ℂ} : coe_fn conj z = coe_fn conj w ↔ z = w :=
  function.injective.eq_iff (and.left conj_bijective)

@[simp] theorem conj_eq_zero {z : ℂ} : coe_fn conj z = 0 ↔ z = 0 := sorry

theorem eq_conj_iff_real {z : ℂ} : coe_fn conj z = z ↔ ∃ (r : ℝ), z = ↑r := sorry

theorem eq_conj_iff_re {z : ℂ} : coe_fn conj z = z ↔ ↑(re z) = z := sorry

protected instance star_ring : star_ring ℂ := star_ring.mk sorry

/-! ### Norm squared -/

/-- The norm squared function. -/
def norm_sq : monoid_with_zero_hom ℂ ℝ :=
  monoid_with_zero_hom.mk (fun (z : ℂ) => re z * re z + im z * im z) sorry sorry sorry

theorem norm_sq_apply (z : ℂ) : coe_fn norm_sq z = re z * re z + im z * im z := rfl

@[simp] theorem norm_sq_of_real (r : ℝ) : coe_fn norm_sq ↑r = r * r := sorry

theorem norm_sq_zero : coe_fn norm_sq 0 = 0 := monoid_with_zero_hom.map_zero norm_sq

theorem norm_sq_one : coe_fn norm_sq 1 = 1 := monoid_with_zero_hom.map_one norm_sq

@[simp] theorem norm_sq_I : coe_fn norm_sq I = 1 := sorry

theorem norm_sq_nonneg (z : ℂ) : 0 ≤ coe_fn norm_sq z :=
  add_nonneg (mul_self_nonneg (re z)) (mul_self_nonneg (im z))

theorem norm_sq_eq_zero {z : ℂ} : coe_fn norm_sq z = 0 ↔ z = 0 := sorry

@[simp] theorem norm_sq_pos {z : ℂ} : 0 < coe_fn norm_sq z ↔ z ≠ 0 :=
  iff.trans (has_le.le.lt_iff_ne (norm_sq_nonneg z)) (not_congr (iff.trans eq_comm norm_sq_eq_zero))

@[simp] theorem norm_sq_neg (z : ℂ) : coe_fn norm_sq (-z) = coe_fn norm_sq z := sorry

@[simp] theorem norm_sq_conj (z : ℂ) : coe_fn norm_sq (coe_fn conj z) = coe_fn norm_sq z := sorry

theorem norm_sq_mul (z : ℂ) (w : ℂ) :
    coe_fn norm_sq (z * w) = coe_fn norm_sq z * coe_fn norm_sq w :=
  monoid_with_zero_hom.map_mul norm_sq z w

theorem norm_sq_add (z : ℂ) (w : ℂ) :
    coe_fn norm_sq (z + w) =
        coe_fn norm_sq z + coe_fn norm_sq w + bit0 1 * re (z * coe_fn conj w) :=
  sorry

theorem re_sq_le_norm_sq (z : ℂ) : re z * re z ≤ coe_fn norm_sq z :=
  le_add_of_nonneg_right (mul_self_nonneg (im z))

theorem im_sq_le_norm_sq (z : ℂ) : im z * im z ≤ coe_fn norm_sq z :=
  le_add_of_nonneg_left (mul_self_nonneg (re z))

theorem mul_conj (z : ℂ) : z * coe_fn conj z = ↑(coe_fn norm_sq z) := sorry

theorem add_conj (z : ℂ) : z + coe_fn conj z = ↑(bit0 1 * re z) := sorry

/-- The coercion `ℝ → ℂ` as a `ring_hom`. -/
def of_real : ℝ →+* ℂ := ring_hom.mk coe of_real_one of_real_mul of_real_zero of_real_add

@[simp] theorem of_real_eq_coe (r : ℝ) : coe_fn of_real r = ↑r := rfl

@[simp] theorem I_sq : I ^ bit0 1 = -1 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (I ^ bit0 1 = -1)) (pow_two I)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (I * I = -1)) I_mul_I)) (Eq.refl (-1)))

@[simp] theorem sub_re (z : ℂ) (w : ℂ) : re (z - w) = re z - re w := rfl

@[simp] theorem sub_im (z : ℂ) (w : ℂ) : im (z - w) = im z - im w := rfl

@[simp] theorem of_real_sub (r : ℝ) (s : ℝ) : ↑(r - s) = ↑r - ↑s := sorry

@[simp] theorem of_real_pow (r : ℝ) (n : ℕ) : ↑(r ^ n) = ↑r ^ n := sorry

theorem sub_conj (z : ℂ) : z - coe_fn conj z = ↑(bit0 1 * im z) * I := sorry

theorem norm_sq_sub (z : ℂ) (w : ℂ) :
    coe_fn norm_sq (z - w) =
        coe_fn norm_sq z + coe_fn norm_sq w - bit0 1 * re (z * coe_fn conj w) :=
  sorry

/-! ### Inversion -/

protected instance has_inv : has_inv ℂ :=
  has_inv.mk fun (z : ℂ) => coe_fn conj z * ↑(coe_fn norm_sq z⁻¹)

theorem inv_def (z : ℂ) : z⁻¹ = coe_fn conj z * ↑(coe_fn norm_sq z⁻¹) := rfl

@[simp] theorem inv_re (z : ℂ) : re (z⁻¹) = re z / coe_fn norm_sq z := sorry

@[simp] theorem inv_im (z : ℂ) : im (z⁻¹) = -im z / coe_fn norm_sq z := sorry

@[simp] theorem of_real_inv (r : ℝ) : ↑(r⁻¹) = (↑r⁻¹) := sorry

protected theorem inv_zero : 0⁻¹ = 0 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (0⁻¹ = 0)) (Eq.symm of_real_zero)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (↑0⁻¹ = ↑0)) (Eq.symm (of_real_inv 0))))
      (eq.mpr (id (Eq._oldrec (Eq.refl (↑(0⁻¹) = ↑0)) inv_zero)) (Eq.refl ↑0)))

protected theorem mul_inv_cancel {z : ℂ} (h : z ≠ 0) : z * (z⁻¹) = 1 := sorry

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

protected instance field : field ℂ :=
  field.mk comm_ring.add comm_ring.add_assoc comm_ring.zero comm_ring.zero_add comm_ring.add_zero
    comm_ring.neg comm_ring.sub comm_ring.add_left_neg comm_ring.add_comm comm_ring.mul
    comm_ring.mul_assoc comm_ring.one comm_ring.one_mul comm_ring.mul_one comm_ring.left_distrib
    comm_ring.right_distrib comm_ring.mul_comm has_inv.inv sorry complex.mul_inv_cancel
    complex.inv_zero

@[simp] theorem I_fpow_bit0 (n : ℤ) : I ^ bit0 n = (-1) ^ n :=
  eq.mpr (id (Eq._oldrec (Eq.refl (I ^ bit0 n = (-1) ^ n)) (fpow_bit0' I n)))
    (eq.mpr (id (Eq._oldrec (Eq.refl ((I * I) ^ n = (-1) ^ n)) I_mul_I)) (Eq.refl ((-1) ^ n)))

@[simp] theorem I_fpow_bit1 (n : ℤ) : I ^ bit1 n = (-1) ^ n * I :=
  eq.mpr (id (Eq._oldrec (Eq.refl (I ^ bit1 n = (-1) ^ n * I)) (fpow_bit1' I n)))
    (eq.mpr (id (Eq._oldrec (Eq.refl ((I * I) ^ n * I = (-1) ^ n * I)) I_mul_I))
      (Eq.refl ((-1) ^ n * I)))

theorem div_re (z : ℂ) (w : ℂ) :
    re (z / w) = re z * re w / coe_fn norm_sq w + im z * im w / coe_fn norm_sq w :=
  sorry

theorem div_im (z : ℂ) (w : ℂ) :
    im (z / w) = im z * re w / coe_fn norm_sq w - re z * im w / coe_fn norm_sq w :=
  sorry

@[simp] theorem of_real_div (r : ℝ) (s : ℝ) : ↑(r / s) = ↑r / ↑s := ring_hom.map_div of_real r s

@[simp] theorem of_real_fpow (r : ℝ) (n : ℤ) : ↑(r ^ n) = ↑r ^ n := ring_hom.map_fpow of_real r n

@[simp] theorem div_I (z : ℂ) : z / I = -(z * I) := sorry

@[simp] theorem inv_I : I⁻¹ = -I := sorry

@[simp] theorem norm_sq_inv (z : ℂ) : coe_fn norm_sq (z⁻¹) = (coe_fn norm_sq z⁻¹) :=
  monoid_with_zero_hom.map_inv' norm_sq z

@[simp] theorem norm_sq_div (z : ℂ) (w : ℂ) :
    coe_fn norm_sq (z / w) = coe_fn norm_sq z / coe_fn norm_sq w :=
  monoid_with_zero_hom.map_div norm_sq z w

/-! ### Cast lemmas -/

@[simp] theorem of_real_nat_cast (n : ℕ) : ↑↑n = ↑n := ring_hom.map_nat_cast of_real n

@[simp] theorem nat_cast_re (n : ℕ) : re ↑n = ↑n :=
  eq.mpr (id (Eq._oldrec (Eq.refl (re ↑n = ↑n)) (Eq.symm (of_real_nat_cast n))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (re ↑↑n = ↑n)) (of_real_re ↑n))) (Eq.refl ↑n))

@[simp] theorem nat_cast_im (n : ℕ) : im ↑n = 0 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (im ↑n = 0)) (Eq.symm (of_real_nat_cast n))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (im ↑↑n = 0)) (of_real_im ↑n))) (Eq.refl 0))

@[simp] theorem of_real_int_cast (n : ℤ) : ↑↑n = ↑n := ring_hom.map_int_cast of_real n

@[simp] theorem int_cast_re (n : ℤ) : re ↑n = ↑n :=
  eq.mpr (id (Eq._oldrec (Eq.refl (re ↑n = ↑n)) (Eq.symm (of_real_int_cast n))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (re ↑↑n = ↑n)) (of_real_re ↑n))) (Eq.refl ↑n))

@[simp] theorem int_cast_im (n : ℤ) : im ↑n = 0 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (im ↑n = 0)) (Eq.symm (of_real_int_cast n))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (im ↑↑n = 0)) (of_real_im ↑n))) (Eq.refl 0))

@[simp] theorem of_real_rat_cast (n : ℚ) : ↑↑n = ↑n := ring_hom.map_rat_cast of_real n

@[simp] theorem rat_cast_re (q : ℚ) : re ↑q = ↑q :=
  eq.mpr (id (Eq._oldrec (Eq.refl (re ↑q = ↑q)) (Eq.symm (of_real_rat_cast q))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (re ↑↑q = ↑q)) (of_real_re ↑q))) (Eq.refl ↑q))

@[simp] theorem rat_cast_im (q : ℚ) : im ↑q = 0 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (im ↑q = 0)) (Eq.symm (of_real_rat_cast q))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (im ↑↑q = 0)) (of_real_im ↑q))) (Eq.refl 0))

/-! ### Characteristic zero -/

protected instance char_zero_complex : char_zero ℂ :=
  char_zero_of_inj_zero
    fun (n : ℕ) (h : ↑n = 0) =>
      eq.mp (Eq._oldrec (Eq.refl (↑n = 0)) (propext nat.cast_eq_zero))
        (eq.mp (Eq._oldrec (Eq.refl (↑↑n = 0)) (propext of_real_eq_zero))
          (eq.mp (Eq._oldrec (Eq.refl (↑n = 0)) (Eq.symm (of_real_nat_cast n))) h))

/-- A complex number `z` plus its conjugate `conj z` is `2` times its real part. -/
theorem re_eq_add_conj (z : ℂ) : ↑(re z) = (z + coe_fn conj z) / bit0 1 := sorry

/-- A complex number `z` minus its conjugate `conj z` is `2i` times its imaginary part. -/
theorem im_eq_sub_conj (z : ℂ) : ↑(im z) = (z - coe_fn conj z) / (bit0 1 * I) := sorry

/-! ### Absolute value -/

/-- The complex absolute value function, defined as the square root of the norm squared. -/
def abs (z : ℂ) : ℝ := real.sqrt (coe_fn norm_sq z)

@[simp] theorem abs_of_real (r : ℝ) : abs ↑r = abs r := sorry

theorem abs_of_nonneg {r : ℝ} (h : 0 ≤ r) : abs ↑r = r := Eq.trans (abs_of_real r) (abs_of_nonneg h)

theorem abs_of_nat (n : ℕ) : abs ↑n = ↑n :=
  Eq.trans
    (eq.mpr (id (Eq._oldrec (Eq.refl (abs ↑n = abs ↑↑n)) (of_real_nat_cast n))) (Eq.refl (abs ↑n)))
    (abs_of_nonneg (nat.cast_nonneg n))

theorem mul_self_abs (z : ℂ) : abs z * abs z = coe_fn norm_sq z :=
  real.mul_self_sqrt (norm_sq_nonneg z)

@[simp] theorem abs_zero : abs 0 = 0 := sorry

@[simp] theorem abs_one : abs 1 = 1 := sorry

@[simp] theorem abs_I : abs I = 1 := sorry

@[simp] theorem abs_two : abs (bit0 1) = bit0 1 := sorry

theorem abs_nonneg (z : ℂ) : 0 ≤ abs z := real.sqrt_nonneg (coe_fn norm_sq z)

@[simp] theorem abs_eq_zero {z : ℂ} : abs z = 0 ↔ z = 0 :=
  iff.trans (real.sqrt_eq_zero (norm_sq_nonneg z)) norm_sq_eq_zero

theorem abs_ne_zero {z : ℂ} : abs z ≠ 0 ↔ z ≠ 0 := not_congr abs_eq_zero

@[simp] theorem abs_conj (z : ℂ) : abs (coe_fn conj z) = abs z := sorry

@[simp] theorem abs_mul (z : ℂ) (w : ℂ) : abs (z * w) = abs z * abs w := sorry

theorem abs_re_le_abs (z : ℂ) : abs (re z) ≤ abs z := sorry

theorem abs_im_le_abs (z : ℂ) : abs (im z) ≤ abs z := sorry

theorem re_le_abs (z : ℂ) : re z ≤ abs z := and.right (iff.mp abs_le (abs_re_le_abs z))

theorem im_le_abs (z : ℂ) : im z ≤ abs z := and.right (iff.mp abs_le (abs_im_le_abs z))

theorem abs_add (z : ℂ) (w : ℂ) : abs (z + w) ≤ abs z + abs w := sorry

protected instance abs.is_absolute_value : is_absolute_value abs :=
  is_absolute_value.mk abs_nonneg (fun (_x : ℂ) => abs_eq_zero) abs_add abs_mul

@[simp] theorem abs_abs (z : ℂ) : abs (abs z) = abs z := abs_of_nonneg (abs_nonneg z)

@[simp] theorem abs_pos {z : ℂ} : 0 < abs z ↔ z ≠ 0 := is_absolute_value.abv_pos abs

@[simp] theorem abs_neg (z : ℂ) : abs (-z) = abs z := is_absolute_value.abv_neg abs

theorem abs_sub (z : ℂ) (w : ℂ) : abs (z - w) = abs (w - z) := is_absolute_value.abv_sub abs

theorem abs_sub_le (a : ℂ) (b : ℂ) (c : ℂ) : abs (a - c) ≤ abs (a - b) + abs (b - c) :=
  is_absolute_value.abv_sub_le abs

@[simp] theorem abs_inv (z : ℂ) : abs (z⁻¹) = (abs z⁻¹) := is_absolute_value.abv_inv abs

@[simp] theorem abs_div (z : ℂ) (w : ℂ) : abs (z / w) = abs z / abs w :=
  is_absolute_value.abv_div abs

theorem abs_abs_sub_le_abs_sub (z : ℂ) (w : ℂ) : abs (abs z - abs w) ≤ abs (z - w) :=
  is_absolute_value.abs_abv_sub_le_abv_sub abs

theorem abs_le_abs_re_add_abs_im (z : ℂ) : abs z ≤ abs (re z) + abs (im z) := sorry

theorem abs_re_div_abs_le_one (z : ℂ) : abs (re z / abs z) ≤ 1 := sorry

theorem abs_im_div_abs_le_one (z : ℂ) : abs (im z / abs z) ≤ 1 := sorry

@[simp] theorem abs_cast_nat (n : ℕ) : abs ↑n = ↑n :=
  eq.mpr (id (Eq._oldrec (Eq.refl (abs ↑n = ↑n)) (Eq.symm (of_real_nat_cast n))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (abs ↑↑n = ↑n)) (abs_of_nonneg (nat.cast_nonneg n))))
      (Eq.refl ↑n))

@[simp] theorem int_cast_abs (n : ℤ) : ↑(abs n) = abs ↑n :=
  eq.mpr (id (Eq._oldrec (Eq.refl (↑(abs n) = abs ↑n)) (Eq.symm (of_real_int_cast n))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (↑(abs n) = abs ↑↑n)) (abs_of_real ↑n)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (↑(abs n) = abs ↑n)) int.cast_abs)) (Eq.refl (abs ↑n))))

theorem norm_sq_eq_abs (x : ℂ) : coe_fn norm_sq x = abs x ^ bit0 1 := sorry

/-! ### Cauchy sequences -/

theorem is_cau_seq_re (f : cau_seq ℂ abs) : is_cau_seq abs fun (n : ℕ) => re (coe_fn f n) := sorry

theorem is_cau_seq_im (f : cau_seq ℂ abs) : is_cau_seq abs fun (n : ℕ) => im (coe_fn f n) := sorry

/-- The real part of a complex Cauchy sequence, as a real Cauchy sequence. -/
def cau_seq_re (f : cau_seq ℂ abs) : cau_seq ℝ abs :=
  { val := fun (n : ℕ) => re (coe_fn f n), property := is_cau_seq_re f }

/-- The imaginary part of a complex Cauchy sequence, as a real Cauchy sequence. -/
def cau_seq_im (f : cau_seq ℂ abs) : cau_seq ℝ abs :=
  { val := fun (n : ℕ) => im (coe_fn f n), property := is_cau_seq_im f }

theorem is_cau_seq_abs {f : ℕ → ℂ} (hf : is_cau_seq abs f) : is_cau_seq abs (abs ∘ f) := sorry

/-- The limit of a Cauchy sequence of complex numbers. -/
def lim_aux (f : cau_seq ℂ abs) : ℂ := mk (cau_seq.lim (cau_seq_re f)) (cau_seq.lim (cau_seq_im f))

theorem equiv_lim_aux (f : cau_seq ℂ abs) : f ≈ cau_seq.const abs (lim_aux f) := sorry

protected instance abs.cau_seq.is_complete : cau_seq.is_complete ℂ abs :=
  cau_seq.is_complete.mk sorry

theorem lim_eq_lim_im_add_lim_re (f : cau_seq ℂ abs) :
    cau_seq.lim f = ↑(cau_seq.lim (cau_seq_re f)) + ↑(cau_seq.lim (cau_seq_im f)) * I :=
  sorry

theorem lim_re (f : cau_seq ℂ abs) : cau_seq.lim (cau_seq_re f) = re (cau_seq.lim f) := sorry

theorem lim_im (f : cau_seq ℂ abs) : cau_seq.lim (cau_seq_im f) = im (cau_seq.lim f) := sorry

theorem is_cau_seq_conj (f : cau_seq ℂ abs) :
    is_cau_seq abs fun (n : ℕ) => coe_fn conj (coe_fn f n) :=
  sorry

/-- The complex conjugate of a complex Cauchy sequence, as a complex Cauchy sequence. -/
def cau_seq_conj (f : cau_seq ℂ abs) : cau_seq ℂ abs :=
  { val := fun (n : ℕ) => coe_fn conj (coe_fn f n), property := is_cau_seq_conj f }

theorem lim_conj (f : cau_seq ℂ abs) : cau_seq.lim (cau_seq_conj f) = coe_fn conj (cau_seq.lim f) :=
  sorry

/-- The absolute value of a complex Cauchy sequence, as a real Cauchy sequence. -/
def cau_seq_abs (f : cau_seq ℂ abs) : cau_seq ℝ abs :=
  { val := abs ∘ subtype.val f, property := sorry }

theorem lim_abs (f : cau_seq ℂ abs) : cau_seq.lim (cau_seq_abs f) = abs (cau_seq.lim f) := sorry

@[simp] theorem of_real_prod {α : Type u_1} (s : finset α) (f : α → ℝ) :
    ↑(finset.prod s fun (i : α) => f i) = finset.prod s fun (i : α) => ↑(f i) :=
  ring_hom.map_prod of_real (fun (x : α) => f x) s

@[simp] theorem of_real_sum {α : Type u_1} (s : finset α) (f : α → ℝ) :
    ↑(finset.sum s fun (i : α) => f i) = finset.sum s fun (i : α) => ↑(f i) :=
  ring_hom.map_sum of_real (fun (x : α) => f x) s

end Mathlib


/-
Copyright (c) 2021 Kalle Kytölä. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kalle Kytölä
-/
import tactic
import weak_dual
import measure_theory.integral.bochner

noncomputable theory
open measure_theory
open set
open filter
open_locale topological_space bounded_continuous_function ennreal nnreal

section finite_measures
/-!
### Finite measures
In this section, we define the `Type` of finite measures on a given measurable space. This type
will be equipped with the topology of weak convergence of measures when the measurable space is
a topological space equipped with its Borel sigma-algebra.
-/

variables {α : Type} [measurable_space α]

/-- Finite measures are defined as the subtype of measures that have the property of being finite
measures (i.e., their total mass is finite). -/
def finite_measures (α : Type*) [measurable_space α] : Type :=
{ μ : measure α // finite_measure μ }

namespace finite_measures

instance has_zero {α : Type*} [measurable_space α] :
  has_zero (finite_measures α) :=
⟨{ val := 0,
   property := measure_theory.finite_measure_zero, }⟩

instance finite_measure_add {α : Type*} [measurable_space α]
  (μ ν : measure α) [finite_measure μ] [finite_measure ν] :
  finite_measure (μ + ν) :=
{ measure_univ_lt_top := begin
    simp only [measure.coe_add, pi.add_apply, ennreal.add_lt_top],
    exact ⟨measure_lt_top μ univ, measure_lt_top ν univ⟩,
  end, }

instance {α : Type*} [measurable_space α] : has_add (finite_measures α) :=
{ add := (λ (μ ν : finite_measures α),
  { val := μ.val + ν.val,
    property := @finite_measures.finite_measure_add _ _ _ _ μ.prop ν.prop, }), }

instance : inhabited (finite_measures α) := { default := 0 }

/-- A finite measure can be interpreted as a measure. -/
def to_measure (μ : finite_measures α) : (measure_theory.measure α) := subtype.val μ

instance to_measure.finite_measure (μ : finite_measures α) :
  finite_measure μ.to_measure := μ.prop

instance (α : Type*) [measurable_space α] :
  has_coe_to_fun (finite_measures α) := ⟨(λ _, set α → ℝ≥0),
    (λ μ, (λ s, (μ.val.measure_of s).to_nnreal))⟩

lemma to_fun_eq_to_measure_to_nnreal (ν : finite_measures α) :
  (ν : set α → ℝ≥0) = λ s, (ν.to_measure s).to_nnreal := rfl

lemma to_measure_eq_val (ν : finite_measures α) : ν.to_measure = ν.val := rfl

lemma to_measure_injective :
  function.injective (@finite_measures.to_measure α ‹measurable_space α›) :=
by { intros μ ν, exact subtype.eq, }

lemma to_measure_zero :
  (@finite_measures.to_measure α ‹measurable_space α›) 0 = 0 := rfl

lemma to_measure_add (μ ν : finite_measures α) :
  μ.to_measure + ν.to_measure = (μ + ν).to_measure := rfl

/-- The (total) mass of a finite measure `μ` is `μ univ`, i.e., the cast to `nnreal` of
`μ.to_measure univ`. -/
def mass {α : Type*} [measurable_space α] (μ : finite_measures α) : ℝ≥0 := μ univ

lemma mass_def {α : Type*} [measurable_space α] {μ : finite_measures α} :
  μ.mass = μ univ := rfl

@[simp] lemma mass_ennreal {α : Type*} [measurable_space α]
  {μ : finite_measures α} : (μ.mass : ℝ≥0∞) = μ.to_measure univ :=
begin
  apply ennreal.coe_to_nnreal,
  exact ennreal.lt_top_iff_ne_top.mp (μ.prop).measure_univ_lt_top,
end

instance {α : Type*} [measurable_space α] :
  add_comm_monoid (finite_measures α) :=
(@finite_measures.to_measure_injective α ‹measurable_space α›).add_comm_monoid
    (@finite_measures.to_measure α ‹measurable_space α›)
    finite_measures.to_measure_zero finite_measures.to_measure_add

--TODO: Make `finite_measures α` an `ℝ≥0`-module, so that it makes sense (later)
--      to define a continuous linear map `finite_measures α → weak_dual ℝ≥0 (α →ᵇ ℝ≥0)`.
--instance finite_measures.module : module ℝ≥0 (finite_measures α) := sorry

end finite_measures -- end namespace

end finite_measures -- end section

section probability_measures
/-!
### Probability measures
In this section, we define the `Type` of probability measures on a given measurable space. This
type will be equipped with the topology of weak convergence of measures when the measurable space
is a topological space equipped with its Borel sigma-algebra.

There is a coercion (embedding) to finite measures on the same space.
-/

variables {α : Type} [measurable_space α]

/-- Probability measures are defined as the subtype of measures that have the property of being
probability measures (i.e., their total mass is one). -/
def probability_measures (α : Type) [measurable_space α] : Type :=
{μ : measure α // probability_measure μ}

namespace probability_measures

instance [inhabited α] : inhabited (probability_measures α) :=
⟨{ val := measure_theory.measure.dirac (default α),
   property := measure_theory.measure.dirac.probability_measure, }⟩

/-- A probability measure can be interpreted as a measure. -/
def to_measure (μ : probability_measures α) : (measure_theory.measure α) := μ.val

instance to_measure.probability_measure (μ : probability_measures α) :
  probability_measure μ.to_measure := μ.prop

instance (α : Type*) [measurable_space α] :
  has_coe_to_fun (probability_measures α) := ⟨(λ _, set α → ℝ≥0),
    (λ μ, (λ s, (μ.val.measure_of s).to_nnreal))⟩

lemma to_fun_eq_to_measure_to_nnreal (ν : probability_measures α) :
  (ν : set α → ℝ≥0) = λ s, (ν.to_measure s).to_nnreal := rfl

@[simp]
lemma to_fun_univ (ν : probability_measures α) : ν univ = 1 :=
begin
  rw [to_fun_eq_to_measure_to_nnreal, ←ennreal.one_to_nnreal],
  exact congr_arg ennreal.to_nnreal ν.prop.measure_univ,
end

lemma to_measure_eq_val (ν : probability_measures α) : ν.to_measure = ν.val := rfl

lemma to_measure_injective :
  function.injective (@probability_measures.to_measure α ‹measurable_space α›) :=
by { intros μ ν, exact subtype.eq, }

instance has_coe_to_finite_measures (α : Type*) [measurable_space α] :
  has_coe (probability_measures α) (finite_measures α) :=
{ coe := λ μ , { val := μ.val,
                 property := begin
                   have key : (1 : ennreal) < ⊤ := ennreal.one_lt_top,
                   rw [←μ.prop.measure_univ] at key,
                   exact ⟨key⟩,
                 end, }}

/-- A probability measure can be interpreted as a finite measure. -/
def to_finite_measure (μ : probability_measures α) : (finite_measures α) := μ

@[simp]
lemma to_finite_measure_to_measure_eq_to_measure (ν : probability_measures α) :
  (ν.to_finite_measure).to_measure = ν.to_measure := rfl

lemma to_finite_measure_to_measure_eq_val (ν : probability_measures α) :
  (ν.to_finite_measure).to_measure = ν.val := rfl

@[simp]
lemma to_finite_measure_to_fun_eq_to_fun (ν : probability_measures α) :
  (ν.to_finite_measure : set α → ℝ≥0) = (ν : set α → ℝ≥0) := rfl

@[simp]
lemma to_finite_measure_mass (μ : probability_measures α) :
  μ.to_finite_measure.mass = 1 :=
begin
  unfold finite_measures.mass,
  rw [←μ.to_fun_univ, to_finite_measure_to_fun_eq_to_fun],
end

end probability_measures -- end namespace

end probability_measures -- end section

section various_lemmas

-- NOTE: Eh, what is the right way to do this `nnreal_mul_ennreal_to_nnreal`?
-- If this deserves to be added, then perhaps in `data.real.ennreal`?
-- It seems like a typical coercion issue to me, although it is only used once here.
lemma nnreal_mul_ennreal_to_nnreal (c : ℝ≥0) (z : ℝ≥0∞) :
  c * z.to_nnreal = ((c : ℝ≥0∞) * z).to_nnreal :=
begin
  by_cases z_infty : z = ⊤,
  { rw z_infty,
    simp only [ennreal.top_to_nnreal, ennreal.to_nnreal_mul_top, mul_zero], },
  { have z_lt_top : z < ⊤ := ennreal.lt_top_iff_ne_top.mpr z_infty,
    simp only [ennreal.to_nnreal_mul, ennreal.to_nnreal_coe], },
end

-- NOTE: I have been almost wondering why the following is not the definition of
-- boundedness. I have not found it in the library, but believe it should be there.
-- Is `topology.continuous_function.bounded` the appropriate place? The proof needs golf, though.
lemma bounded_continuous_function.radius_bounded {α β : Type*}
  [topological_space α] [metric_space β] (f : α →ᵇ β) (z : β):
  ∃ (c : ℝ), ∀ (a : α), dist z (f(a)) ≤ c :=
begin
  cases f.bounded with c hc ,
  by_cases is_empty α,
  { use 0, intros a, exfalso, exact is_empty_iff.mp h a, },
  cases not_is_empty_iff.mp h with a₀,
  use (dist z (f(a₀))) + c,
  intros a,
  have le : ∀ (y : ℝ), y + (dist (f(a₀)) (f(a))) ≤ y + c := λ y, add_le_add rfl.ge (hc a₀ a),
  exact (dist_triangle z (f(a₀)) (f(a))).trans (le _),
end

-- NOTE: The following is a "special case" of the above. I think it is also worthwhile, although
-- it is not needed in this file. If it should be added, what is the right place?
lemma bounded_continuous_function.norm_bounded {α β : Type*}
  [topological_space α] [normed_group β] (f : α →ᵇ β) :
  ∃ (c : ℝ), ∀ x, ∥ f x ∥ ≤ c :=
begin
  have norm_eq_dist : ∀ (z : β), ∥ z ∥ = dist 0 z := λ z, congr_fun (dist_zero_left).symm z,
  simp_rw [norm_eq_dist],
  exact bounded_continuous_function.radius_bounded f 0,
end

-- NOTE: The following is another "special case". While special, I think it may be worthwhile.
-- If so, what is the right place? How to golf?
lemma bounded_continuous_function.nnreal.upper_bound {α : Type*} [topological_space α]
  (f : α →ᵇ ℝ≥0) : ∃ (c : nnreal), (∀ x, f(x) ≤ c) :=
begin
  have val_eq_dist_nnreal : ∀ (z : ℝ≥0), (z : ℝ) = dist 0 z,
  { intros z,
    simp only [nnreal.dist_eq, nnreal.coe_zero, zero_sub, nnreal.abs_eq, abs_neg], },
  cases bounded_continuous_function.radius_bounded f 0 with c hc,
  simp_rw ←val_eq_dist_nnreal at hc,
  use (max c 0).to_nnreal,
  intros x,
  apply (@real.le_to_nnreal_iff_coe_le (f x) _ (le_max_right c 0)).mpr,
  apply (hc x).trans (le_max_left c 0),
end

-- NOTE: This one is not actually needed in the current version with nonnegative test functions.
-- It will be needed later. If this doesn't exist yet, then after golfing the proof, this might
-- be appropriate in `measure_theory.function.L1_space` (not in
-- `topology.continuous_function.bounded` because of imports?).
lemma bounded_continuous_function.integrable {α β: Type}
  [topological_space α] [measurable_space α] [opens_measurable_space α]
  [normed_group β] [measurable_space β] [borel_space β]
  (μ : measure α) [finite_measure μ] (f : α →ᵇ β) : integrable f μ :=
begin
  set f' := ennreal.of_real ∘ norm ∘ f with hf' ,
  suffices : lintegral μ f' < ⊤ ,
  { have ae_mble : @ae_measurable α β ‹measurable_space β› ‹measurable_space α› f μ
    := continuous.ae_measurable f.continuous μ,
    exact ⟨ ae_mble , (has_finite_integral_iff_norm f).mpr this ⟩, } ,
  have bdd : ∀ (x : α), f' x ≤ ennreal.of_real (∥ f ∥) ,
  { intros x,
    exact ennreal.of_real_le_of_real (bounded_continuous_function.norm_coe_le_norm f x), },
  have integr_bdd := @lintegral_mono α _ μ _ _ bdd,
  set c' := ennreal.of_real (∥ f ∥) with hc',
  have const_integr : lintegral μ (λ x , c') = c' * (μ(univ)) := by rw lintegral_const,
  have total : c' * (μ(univ)) < ⊤,
  { apply ennreal.mul_lt_top ennreal.of_real_lt_top
      (‹finite_measure μ›).measure_univ_lt_top, },
  rw ← const_integr at total,
  exact lt_of_le_of_lt integr_bdd total,
end

-- NOTE: I believe this is useful, but what is the right place?
-- `measure_theory.function.L1_space`? `topology.continuous_function.bounded`?
lemma bounded_continuous_function.coe_nnreal_comp_measurable {α : Type*} [topological_space α]
  [measurable_space α] [opens_measurable_space α] (f : α →ᵇ ℝ≥0) :
  measurable ((coe : ℝ≥0 → ℝ≥0∞) ∘ f) :=
measurable.comp measurable_coe_nnreal_ennreal (continuous.measurable f.continuous)

-- NOTE: The following is probably too special for anywhere else, but is a useful standalone
-- lemma here.
lemma lintegral_lt_top_of_bounded_continuous_to_nnreal {α : Type*} [topological_space α]
  [measurable_space α] (μ : measure α) [μ_fin : finite_measure μ] (f : α →ᵇ ℝ≥0) :
  lintegral μ ((coe : ℝ≥0 → ℝ≥0∞) ∘ f) < ⊤ :=
begin
  cases bounded_continuous_function.nnreal.upper_bound f with c hc,
  have le' : (coe : ℝ≥0 → ℝ≥0∞) ∘ f ≤ (λ (x : α), c),
  by { intros x, simp only [hc, ennreal.coe_le_coe], },
  apply lt_of_le_of_lt (@lintegral_mono _ _ μ _ _ le'),
  rw lintegral_const,
  exact ennreal.mul_lt_top ennreal.coe_lt_top μ_fin.measure_univ_lt_top,
end

-- TODO: What to do with this? Hmm... I've ended up using it quite regularly in the definitions
-- of certain topologies.
lemma tendsto_induced_iff {α β γ : Type*} {F : filter γ} [topological_space β]
  {f : α → β} {xs : γ → α} {x : α} :
  tendsto xs F (@nhds α (topological_space.induced f infer_instance) x) ↔
    tendsto (λ i, f(xs(i))) F (𝓝 (f(x))) :=
begin
  split,
  { intros conv_induced,
    have f_cont := @continuous_induced_dom α β f infer_instance,
    have key := @continuous.tendsto α β
      (topological_space.induced f infer_instance) infer_instance f f_cont,
    exact tendsto.comp (key x) conv_induced, },
  { intros conv_image,
    rw nhds_induced,
    exact map_le_iff_le_comap.mp conv_image, },
end

end various_lemmas

section weak_convergence_of_measures

/-!
### The topology of weak convergence of measures
In this section, we define the topology of weak convergence on the set of Borel probability
measures and on the set of finite Borel measures on a topological space.
-/

/- NOTE / TODO:
The pairing of measures with test functions are called `test_against_nn` since the test functions
are taken to be `ℝ≥0`-valued in the definition of the topology (this allows us to use the Lebesgue
integral `lintegral`, but requires some coercions). It seems natural to later add `test_against`
for pairings via Bochner integrals (`integral`) with (bounded continuous) functions with values
in an arbitrary Banach spaces, especially with values in `ℝ` and `ℂ`. -/

variables {α : Type} [measurable_space α] [topological_space α]

/-- The pairing of a (Borel) probability measure `μ` with a nonnegative bounded continuous
function is obtained by (Lebesgue) integrating the (test) function against the measure. This
is `probability_measures.test_against'`. -/
abbreviation probability_measures.test_against_nn
  (μ : probability_measures α) (f : α →ᵇ nnreal) : ℝ≥0 :=
(lintegral μ.to_measure ((coe : ℝ≥0 → ℝ≥0∞) ∘ f)).to_nnreal

lemma probability_measures.test_against_nn_def (μ : probability_measures α) {f : α →ᵇ nnreal} :
  μ.test_against_nn f = (lintegral μ.to_measure ((coe : ℝ≥0 → ℝ≥0∞) ∘ f)).to_nnreal := by refl

/-- The pairing of a finite (Borel) measure `μ` with a nonnegative bounded continuous
function is obtained by (Lebesgue) integrating the (test) function against the measure. This
is `finite_measures.test_against'`. -/
abbreviation finite_measures.test_against_nn
  (μ : finite_measures α) (f : α →ᵇ nnreal) : ℝ≥0 :=
(lintegral μ.to_measure ((coe : ℝ≥0 → ℝ≥0∞) ∘ f)).to_nnreal

lemma finite_measures.test_against_nn_def (μ : finite_measures α) {f : α →ᵇ nnreal} :
  μ.test_against_nn f = (lintegral μ.to_measure ((coe : ℝ≥0 → ℝ≥0∞) ∘ f)).to_nnreal := by refl

lemma finite_measures.test_against_nn_coe_eq {μ : finite_measures α} {f : α →ᵇ nnreal} :
  (μ.test_against_nn f : ℝ≥0∞) = lintegral μ.to_measure ((coe : ℝ≥0 → ℝ≥0∞) ∘ f) :=
begin
  have key_lt := lintegral_lt_top_of_bounded_continuous_to_nnreal μ.to_measure f,
  exact ennreal.coe_to_nnreal (ennreal.lt_top_iff_ne_top.mp key_lt),
end

lemma probability_measures.test_against_nn_coe_eq {μ : probability_measures α} {f : α →ᵇ nnreal} :
  (μ.test_against_nn f : ℝ≥0∞) = lintegral μ.to_measure ((coe : ℝ≥0 → ℝ≥0∞) ∘ f) :=
begin
  have key_lt := lintegral_lt_top_of_bounded_continuous_to_nnreal μ.to_measure f,
  exact ennreal.coe_to_nnreal (ennreal.lt_top_iff_ne_top.mp key_lt),
end

@[simp]
lemma probability_measures.to_finite_measure_test_against_nn_eq_test_against_nn (α : Type*)
  [measurable_space α] [topological_space α] {μ : probability_measures α} {f : α →ᵇ nnreal} :
  μ.to_finite_measure.test_against_nn f = μ.test_against_nn f := rfl

lemma finite_measures.test_against_nn_const (μ : finite_measures α) (c : ℝ≥0) :
  μ.test_against_nn (bounded_continuous_function.const α c) = c * μ.mass :=
begin
  rw finite_measures.test_against_nn_def,
  have eq : (coe : ℝ≥0 → ℝ≥0∞) ∘ (bounded_continuous_function.const α c) = (λ x, (c : ennreal)),
  by refl,
  rw [eq, lintegral_const, ennreal.to_nnreal_mul],
  simp only [mul_eq_mul_left_iff, ennreal.to_nnreal_coe, finite_measures.mass_def],
  left,
  refl,
end

lemma probability_measures.test_against_nn_const (μ : probability_measures α) (c : ℝ≥0) :
  μ.test_against_nn (bounded_continuous_function.const α c) = c :=
begin
  have key := finite_measures.test_against_nn_const μ.to_finite_measure c,
  simp only [mul_one, probability_measures.to_finite_measure_mass] at key,
  exact key,
end

lemma finite_measures.test_against_nn_mono (μ : finite_measures α)
  {f g : α →ᵇ ℝ≥0} (f_le_g : (f : α → ℝ≥0) ≤ g) :
  μ.test_against_nn f ≤ μ.test_against_nn g :=
begin
  repeat { rw finite_measures.test_against_nn_def, },
  apply ennreal.coe_le_coe.mp,
  repeat { rw ennreal.coe_to_nnreal, },
  { apply lintegral_mono,
    intros x,
    apply ennreal.coe_mono,
    exact f_le_g x, },
  repeat { apply ennreal.lt_top_iff_ne_top.mp,
           apply lintegral_lt_top_of_bounded_continuous_to_nnreal, },
end

lemma probability_measures.test_against_nn_mono (μ : probability_measures α)
  {f g : α →ᵇ ℝ≥0} (f_le_g : (f : α → ℝ≥0) ≤ g) :
  μ.test_against_nn f ≤ μ.test_against_nn g :=
begin
  have key := finite_measures.test_against_nn_mono μ.to_finite_measure f_le_g,
  simp only [probability_measures.to_finite_measure_test_against_nn_eq_test_against_nn] at key,
  exact key,
end

lemma continuous_bounded_nn_add_comp_coe {β : Type*} [topological_space β] {f₁ f₂ : β →ᵇ ℝ≥0} :
  (coe : ℝ≥0 → ℝ≥0∞) ∘ (f₁ + f₂) = ( ((coe : ℝ≥0 → ℝ≥0∞) ∘ f₁) + ((coe : ℝ≥0 → ℝ≥0∞) ∘ f₂)) :=
by { funext x, simp only [ennreal.coe_add, pi.add_apply, function.comp_app], }

lemma continuous_bounded_nn_smul_comp_coe {β : Type*} [topological_space β] {c : ℝ≥0}
  {f : β →ᵇ ℝ≥0} :
  (coe : ℝ≥0 → ℝ≥0∞) ∘ (c • f) = c • ( ((coe : ℝ≥0 → ℝ≥0∞) ∘ f)) :=
begin
  funext x,
  simpa only [algebra.id.smul_eq_mul, function.comp_app, pi.smul_apply, ennreal.coe_mul],
end

lemma continuous_bounded_nn_smul_eq {β : Type*} [topological_space β] {c : ℝ≥0} {f : β →ᵇ ℝ≥0} :
  (c • (f : β → ℝ≥0)) = ((c • f) : β →ᵇ ℝ≥0) := by refl

lemma continuous_bounded_nn_smul_comp_coe' {β : Type*} [topological_space β] {c : ℝ≥0} {f : β →ᵇ ℝ≥0} :
  (coe : ℝ≥0 → ℝ≥0∞) ∘ (c • f) = c • ( ((coe : ℝ≥0 → ℝ≥0∞) ∘ f)) :=
begin
  funext x,
  simpa only [algebra.id.smul_eq_mul, function.comp_app, pi.smul_apply, ennreal.coe_mul],
end

variables [opens_measurable_space α] -- [borel_space α]

lemma finite_measures.test_against_nn_add (μ : finite_measures α) (f₁ f₂ : α →ᵇ ℝ≥0) :
  μ.test_against_nn (f₁ + f₂) = μ.test_against_nn f₁ + μ.test_against_nn f₂ :=
begin
  rw ← ennreal.to_nnreal_add
    (lintegral_lt_top_of_bounded_continuous_to_nnreal μ.to_measure f₁)
    (lintegral_lt_top_of_bounded_continuous_to_nnreal μ.to_measure f₂),
  rw ← @lintegral_add _ _ μ.to_measure _ _
    f₁.coe_nnreal_comp_measurable f₂.coe_nnreal_comp_measurable,
  refl,
end

lemma finite_measures.test_against_nn_smul (μ : finite_measures α) (c : ℝ≥0) (f : α →ᵇ ℝ≥0) :
  μ.test_against_nn (c • f) = c * μ.test_against_nn f :=
begin
  have key_smul := @lintegral_mul_const _ _ μ.to_measure c _ f.coe_nnreal_comp_measurable,
  simp_rw mul_comm at key_smul,
  repeat { rw finite_measures.test_against_nn_def, },
  rw [nnreal_mul_ennreal_to_nnreal, ←key_smul],
  rw [←continuous_bounded_nn_smul_eq, @continuous_bounded_nn_smul_comp_coe _ _ c f],
  refl,
end

/-- Integration against a finite_measure defines a linear map from nonnegative bounded continuous
functions to nonnegative real numbers. -/
def finite_measures.test_against_nn_linear_map (μ : finite_measures α) :
  linear_map ℝ≥0 (α →ᵇ ℝ≥0) ℝ≥0 :=
{ to_fun := μ.test_against_nn,
  map_add' := finite_measures.test_against_nn_add μ,
  map_smul' := finite_measures.test_against_nn_smul μ, }

lemma nnreal.le_add_dist (a b : ℝ≥0) : a ≤ b + (dist a b).to_nnreal :=
begin
  suffices : (a : ℝ) ≤ (b : ℝ) + (dist a b),
  { have coe_eq : (coe : ℝ≥0 → ℝ) (b + (dist a b).to_nnreal) = (b : ℝ) + dist a b,
    { rw nnreal.coe_add,
      simp only [real.coe_to_nnreal', max_eq_left_iff, add_right_inj],
      exact dist_nonneg, },
    rw ←coe_eq at this,
    apply nnreal.coe_le_coe.mp this, },
  have key : abs (a-b : ℝ) ≤ (dist a b) := by refl,
  linarith [le_of_abs_le key],
end

lemma finite_measures.lipschitz_estimate (μ : finite_measures α) (f g : α →ᵇ ℝ≥0) :
  μ.test_against_nn f ≤ μ.test_against_nn g + (dist f g).to_nnreal * μ.mass :=
begin
  have coe_eq : ennreal.of_real (dist f g) = ((dist f g).to_nnreal : ℝ≥0∞) := by refl,
  rw ←finite_measures.test_against_nn_const μ (dist f g).to_nnreal,
  rw ←finite_measures.test_against_nn_add,
  repeat { rw finite_measures.test_against_nn_def, },
  apply ennreal.coe_le_coe.mp,
  repeat { rw ennreal.coe_to_nnreal, },
  { apply lintegral_mono,
    have le_dist : ∀ x, dist (f x) (g x) ≤ (dist f g)
    := bounded_continuous_function.dist_coe_le_dist,
    have le' : ∀ x, f(x) ≤ g(x) + (dist f g).to_nnreal,
    { intros x,
      apply (nnreal.le_add_dist (f x) (g x)).trans,
      rw add_le_add_iff_left,
      exact real.to_nnreal_mono (le_dist x), },
    have le : ∀ x, (f(x) : ℝ≥0∞) ≤ (g(x) : ℝ≥0∞) + ennreal.of_real (dist f g),
    { intros x,
      rw [coe_eq, ←ennreal.coe_add],
      exact ennreal.coe_mono (le' x), },
    exact le, },
  repeat { apply ennreal.lt_top_iff_ne_top.mp,
           apply lintegral_lt_top_of_bounded_continuous_to_nnreal, },
end

lemma finite_measures.test_against_nn_lipschitz (μ : finite_measures α) :
  lipschitz_with μ.mass μ.test_against_nn :=
begin
  rw lipschitz_with_iff_dist_le_mul,
  intros f₁ f₂,
  suffices : abs (μ.test_against_nn f₁ - μ.test_against_nn f₂ : ℝ) ≤ μ.mass * (dist f₁ f₂),
  { rwa nnreal.dist_eq, },
  apply (@abs_le ℝ _ _ _ _ _).mpr,
  split,
  { have key' := μ.lipschitz_estimate f₂ f₁,
    rw mul_comm at key',
    suffices : ↑(μ.test_against_nn f₂) ≤ ↑(μ.test_against_nn f₁) + ↑(μ.mass) * dist f₁ f₂,
    { linarith, },
    have key := nnreal.coe_mono key',
    rwa [nnreal.coe_add, nnreal.coe_mul, real.coe_to_nnreal, dist_comm] at key,
    exact dist_nonneg, },
  { have key' := μ.lipschitz_estimate f₁ f₂,
    rw mul_comm at key',
    suffices : ↑(μ.test_against_nn f₁) ≤ ↑(μ.test_against_nn f₂) + ↑(μ.mass) * dist f₁ f₂,
    { linarith, },
    have key := nnreal.coe_mono key',
    rwa [nnreal.coe_add, nnreal.coe_mul, real.coe_to_nnreal] at key,
    exact dist_nonneg, },
end

lemma probability_measures.test_against_nn_lipschitz (μ : probability_measures α) :
  lipschitz_with 1 μ.test_against_nn :=
begin
  have key := μ.to_finite_measure.test_against_nn_lipschitz,
  rwa μ.to_finite_measure_mass at key,
end

/-- A finite measure can be interpreted as an element of the (weak) dual of nonnegative
bounded continuous functions, the duality pairing being integration. -/
def finite_measures.to_weak_dual_of_bcnn (μ : finite_measures α) :
  weak_dual ℝ≥0 (α →ᵇ ℝ≥0) :=
{ to_fun := μ.test_against_nn,
  map_add' := finite_measures.test_against_nn_add μ,
  map_smul' := finite_measures.test_against_nn_smul μ,
  cont := μ.test_against_nn_lipschitz.continuous, }

/-
--TODO: Need `ℝ≥0`-module structure on `finite_measures α`.
--      Currently only `add_comm_monoid` is implemented.
def finite_measures.to_weak_dual_of_bounded_continuous_nn :
  finite_measures α →L[ℝ≥0] weak_dual ℝ≥0 (α →ᵇ ℝ≥0) := sorry
-/

/-- A probability measure can be interpreted as an element of the (weak) dual of nonnegative
bounded continuous functions (random variables), the duality pairing being integration (expected
value). -/
def probability_measures.to_weak_dual_of_bcnn (μ : probability_measures α) :
  weak_dual ℝ≥0 (α →ᵇ ℝ≥0) :=
{ to_fun := μ.test_against_nn,
  map_add' := μ.to_finite_measure.test_against_nn_add,
  map_smul' := μ.to_finite_measure.test_against_nn_smul,
  cont := μ.test_against_nn_lipschitz.continuous, }

lemma finite_measures.test_against_eq_to_weak_dual_test (μ : finite_measures α) :
  (μ.to_weak_dual_of_bcnn : (α →ᵇ ℝ≥0) → ℝ≥0) = μ.test_against_nn := by refl

lemma probability_measures.test_against_eq_to_weak_dual_test (μ : probability_measures α) :
  (μ.to_weak_dual_of_bcnn : (α →ᵇ ℝ≥0) → ℝ≥0) = μ.test_against_nn := by refl

/-- The topology of weak convergence on `finite_measures α` is inherited (induced) from the weak-*
topology on `weak_dual ℝ≥0 (α →ᵇ ℝ≥0)` via the function `finite_measures.to_weak_dual_of_bcnn`. -/
instance : topological_space (finite_measures α) :=
topological_space.induced
  (λ (μ : finite_measures α), μ.to_weak_dual_of_bcnn) infer_instance

/-- The topology of weak convergence on `probability_measures α`. This is inherited (induced) from
the weak-*  topology on `weak_dual ℝ≥0 (α →ᵇ ℝ≥0)` via the function
`probability_measures.to_weak_dual_of_bcnn`. -/
instance : topological_space (probability_measures α) :=
topological_space.induced
  (λ (μ : probability_measures α), μ.to_weak_dual_of_bcnn) infer_instance

/- Integration of (nonnegative bounded continuous) test functions against finite Borel measures
depends continuously on the measure. -/
lemma finite_measures.to_weak_dual_continuous :
  continuous (@finite_measures.to_weak_dual_of_bcnn α _ _ _) :=
continuous_induced_dom

/- Integration of (nonnegative bounded continuous) test functions against Borel probability
measures depends continuously on the measure. -/
lemma probability_measures.to_weak_dual_continuous :
  continuous (@probability_measures.to_weak_dual_of_bcnn α _ _ _) :=
continuous_induced_dom

/- The canonical mapping from probability measures to finite measures is an embedding. -/
lemma probability_measures.coe_embedding (α : Type*)
  [measurable_space α] [topological_space α] [opens_measurable_space α] :
  embedding (coe : probability_measures α → finite_measures α) :=
{ induced := begin
    have key := @induced_compose (probability_measures α) (finite_measures α) _ _ coe
      (@finite_measures.to_weak_dual_of_bcnn α _ _ _),
    exact key.symm,
  end,
  inj := begin
    intros μ ν h,
    apply subtype.eq,
    rw [←μ.to_finite_measure_to_measure_eq_val, ←ν.to_finite_measure_to_measure_eq_val],
    apply congr_arg _ h,
  end, }

lemma probability_measures.tendsto_nhds_iff_to_finite_measures_tendsto_nhds {δ : Type*}
  (F : filter δ) {μs : δ → probability_measures α} {μ₀ : probability_measures α} :
  tendsto μs F (𝓝 μ₀) ↔ tendsto (coe ∘ μs) F (𝓝 (μ₀.to_finite_measure)) :=
embedding.tendsto_nhds_iff (probability_measures.coe_embedding α)

lemma finite_measures.tendsto_iff_weak_star_tendsto {γ : Type*} {F : filter γ}
  {μs : γ → finite_measures α} {μ : finite_measures α} :
  tendsto μs F (𝓝 μ) ↔
    tendsto (λ i, (μs(i)).to_weak_dual_of_bcnn)
      F (𝓝 μ.to_weak_dual_of_bcnn) :=
by apply tendsto_induced_iff

theorem finite_measures.tendsto_iff_forall_test_against_nn_tendsto {γ : Type*} {F : filter γ}
  {μs : γ → finite_measures α} {μ : finite_measures α} :
  tendsto μs F (𝓝 μ) ↔
  ∀ (f : α →ᵇ ℝ≥0),
    tendsto (λ i, (μs(i)).to_weak_dual_of_bcnn f)
      F (𝓝 (μ.to_weak_dual_of_bcnn f)) :=
by rw [finite_measures.tendsto_iff_weak_star_tendsto,
       weak_dual.tendsto_iff_forall_test_tendsto]

theorem finite_measures.tendsto_iff_forall_lintegral_tendsto {γ : Type*} {F : filter γ}
  {μs : γ → finite_measures α} {μ : finite_measures α} :
  tendsto μs F (𝓝 μ) ↔
  ∀ (f : α →ᵇ ℝ≥0),
    tendsto (λ i, lintegral (μs(i)).to_measure (coe ∘ f)) F (𝓝 (lintegral μ.to_measure (coe ∘ f))) :=
begin
  rw finite_measures.tendsto_iff_forall_test_against_nn_tendsto,
  simp_rw finite_measures.test_against_eq_to_weak_dual_test,
  simp_rw ←finite_measures.test_against_nn_coe_eq,
  simp_rw ennreal.tendsto_coe,
end

/-- The usual definition of weak convergence of probability measures is given in terms of sequences
of probability measures: it is the requirement that the integrals of all continuous bounded
functions against members of the sequence converge. This version is a characterization using
nonnegative bounded continuous functions. -/
theorem probability_measures.tendsto_iff_forall_lintegral_tendsto {γ : Type*} {F : filter γ}
  {μs : γ → probability_measures α} {μ : probability_measures α} :
  tendsto μs F (𝓝 μ) ↔
  ∀ (f : α →ᵇ ℝ≥0),
    tendsto (λ i, lintegral (μs(i)).to_measure (coe ∘ f)) F (𝓝 (lintegral μ.to_measure (coe ∘ f))) :=
begin
  rw [probability_measures.tendsto_nhds_iff_to_finite_measures_tendsto_nhds,
      finite_measures.tendsto_iff_forall_lintegral_tendsto],
  refl,
end

end weak_convergence_of_measures