/-
Copyright (c) 2018 Chris Hughes. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Chris Hughes, Abhimanyu Pallavi Sudhir, Jean Lo, Calle Sönne
-/
import analysis.special_functions.exp
import data.nat.factorization.basic

/-!
# Real logarithm

In this file we define `real.log` to be the logarithm of a real number. As usual, we extend it from
its domain `(0, +∞)` to a globally defined function. We choose to do it so that `log 0 = 0` and
`log (-x) = log x`.

We prove some basic properties of this function and show that it is continuous.

## Tags

logarithm, continuity
-/

open set filter function
open_locale topology
noncomputable theory

namespace real

variables {x y : ℝ}

/-- The real logarithm function, equal to the inverse of the exponential for `x > 0`,
to `log |x|` for `x < 0`, and to `0` for `0`. We use this unconventional extension to
`(-∞, 0]` as it gives the formula `log (x * y) = log x + log y` for all nonzero `x` and `y`, and
the derivative of `log` is `1/x` away from `0`. -/
@[pp_nodot] noncomputable def log (x : ℝ) : ℝ :=
if hx : x = 0 then 0 else exp_order_iso.symm ⟨|x|, abs_pos.2 hx⟩

lemma log_of_ne_zero (hx : x ≠ 0) : log x = exp_order_iso.symm ⟨|x|, abs_pos.2 hx⟩ := dif_neg hx

lemma log_of_pos (hx : 0 < x) : log x = exp_order_iso.symm ⟨x, hx⟩ :=
by { rw [log_of_ne_zero hx.ne'], congr, exact abs_of_pos hx }

lemma exp_log_eq_abs (hx : x ≠ 0) : exp (log x) = |x| :=
by rw [log_of_ne_zero hx, ← coe_exp_order_iso_apply, order_iso.apply_symm_apply, subtype.coe_mk]

lemma exp_log (hx : 0 < x) : exp (log x) = x :=
by { rw exp_log_eq_abs hx.ne', exact abs_of_pos hx }

lemma exp_log_of_neg (hx : x < 0) : exp (log x) = -x :=
by { rw exp_log_eq_abs (ne_of_lt hx), exact abs_of_neg hx }

lemma le_exp_log (x : ℝ) : x ≤ exp (log x) :=
begin
  by_cases h_zero : x = 0,
  { rw [h_zero, log, dif_pos rfl, exp_zero], exact zero_le_one, },
  { rw exp_log_eq_abs h_zero, exact le_abs_self _, },
end
@[simp] lemma log_exp (x : ℝ) : log (exp x) = x :=
exp_injective $ exp_log (exp_pos x)

lemma surj_on_log : surj_on log (Ioi 0) univ :=
λ x _, ⟨exp x, exp_pos x, log_exp x⟩

lemma log_surjective : surjective log :=
λ x, ⟨exp x, log_exp x⟩

@[simp] lemma range_log : range log = univ :=
log_surjective.range_eq

@[simp] lemma log_zero : log 0 = 0 := dif_pos rfl

@[simp] lemma log_one : log 1 = 0 :=
exp_injective $ by rw [exp_log zero_lt_one, exp_zero]

@[simp] lemma log_abs (x : ℝ) : log (|x|) = log x :=
begin
  by_cases h : x = 0,
  { simp [h] },
  { rw [← exp_eq_exp, exp_log_eq_abs h, exp_log_eq_abs (abs_pos.2 h).ne', abs_abs] }
end

@[simp] lemma log_neg_eq_log (x : ℝ) : log (-x) = log x :=
by rw [← log_abs x, ← log_abs (-x), abs_neg]

lemma sinh_log {x : ℝ} (hx : 0 < x) : sinh (log x) = (x - x⁻¹) / 2 :=
by rw [sinh_eq, exp_neg, exp_log hx]

lemma cosh_log {x : ℝ} (hx : 0 < x) : cosh (log x) = (x + x⁻¹) / 2 :=
by rw [cosh_eq, exp_neg, exp_log hx]

lemma surj_on_log' : surj_on log (Iio 0) univ :=
λ x _, ⟨-exp x, neg_lt_zero.2 $ exp_pos x, by rw [log_neg_eq_log, log_exp]⟩

lemma log_mul (hx : x ≠ 0) (hy : y ≠ 0) : log (x * y) = log x + log y :=
exp_injective $
by rw [exp_log_eq_abs (mul_ne_zero hx hy), exp_add, exp_log_eq_abs hx, exp_log_eq_abs hy, abs_mul]

lemma log_div (hx : x ≠ 0) (hy : y ≠ 0) : log (x / y) = log x - log y :=
exp_injective $
by rw [exp_log_eq_abs (div_ne_zero hx hy), exp_sub, exp_log_eq_abs hx, exp_log_eq_abs hy, abs_div]

@[simp] lemma log_inv (x : ℝ) : log (x⁻¹) = -log x :=
begin
  by_cases hx : x = 0, { simp [hx] },
  rw [← exp_eq_exp, exp_log_eq_abs (inv_ne_zero hx), exp_neg, exp_log_eq_abs hx, abs_inv]
end

lemma log_le_log (h : 0 < x) (h₁ : 0 < y) : log x ≤ log y ↔ x ≤ y :=
by rw [← exp_le_exp, exp_log h, exp_log h₁]

lemma log_lt_log (hx : 0 < x) : x < y → log x < log y :=
by { intro h, rwa [← exp_lt_exp, exp_log hx, exp_log (lt_trans hx h)] }

lemma log_lt_log_iff (hx : 0 < x) (hy : 0 < y) : log x < log y ↔ x < y :=
by { rw [← exp_lt_exp, exp_log hx, exp_log hy] }

lemma log_le_iff_le_exp (hx : 0 < x) : log x ≤ y ↔ x ≤ exp y := by rw [←exp_le_exp, exp_log hx]

lemma log_lt_iff_lt_exp (hx : 0 < x) : log x < y ↔ x < exp y := by rw [←exp_lt_exp, exp_log hx]

lemma le_log_iff_exp_le (hy : 0 < y) : x ≤ log y ↔ exp x ≤ y := by rw [←exp_le_exp, exp_log hy]

lemma lt_log_iff_exp_lt (hy : 0 < y) : x < log y ↔ exp x < y := by rw [←exp_lt_exp, exp_log hy]

lemma log_pos_iff (hx : 0 < x) : 0 < log x ↔ 1 < x :=
by { rw ← log_one, exact log_lt_log_iff zero_lt_one hx }

lemma log_pos (hx : 1 < x) : 0 < log x :=
(log_pos_iff (lt_trans zero_lt_one hx)).2 hx

lemma log_neg_iff (h : 0 < x) : log x < 0 ↔ x < 1 :=
by { rw ← log_one, exact log_lt_log_iff h zero_lt_one }

lemma log_neg (h0 : 0 < x) (h1 : x < 1) : log x < 0 := (log_neg_iff h0).2 h1

lemma log_nonneg_iff (hx : 0 < x) : 0 ≤ log x ↔ 1 ≤ x :=
by rw [← not_lt, log_neg_iff hx, not_lt]

lemma log_nonneg (hx : 1 ≤ x) : 0 ≤ log x :=
(log_nonneg_iff (zero_lt_one.trans_le hx)).2 hx

lemma log_nonpos_iff (hx : 0 < x) : log x ≤ 0 ↔ x ≤ 1 :=
by rw [← not_lt, log_pos_iff hx, not_lt]

lemma log_nonpos_iff' (hx : 0 ≤ x) : log x ≤ 0 ↔ x ≤ 1 :=
begin
  rcases hx.eq_or_lt with (rfl|hx),
  { simp [le_refl, zero_le_one] },
  exact log_nonpos_iff hx
end

lemma log_nonpos (hx : 0 ≤ x) (h'x : x ≤ 1) : log x ≤ 0 :=
(log_nonpos_iff' hx).2 h'x

lemma strict_mono_on_log : strict_mono_on log (set.Ioi 0) :=
λ x hx y hy hxy, log_lt_log hx hxy

lemma log_inj_on_pos : set.inj_on log (set.Ioi 0) :=
strict_mono_on_log.inj_on

lemma eq_one_of_pos_of_log_eq_zero {x : ℝ} (h₁ : 0 < x) (h₂ : log x = 0) : x = 1 :=
log_inj_on_pos (set.mem_Ioi.2 h₁) (set.mem_Ioi.2 zero_lt_one) (h₂.trans real.log_one.symm)

lemma log_ne_zero_of_pos_of_ne_one {x : ℝ} (hx_pos : 0 < x) (hx : x ≠ 1) : log x ≠ 0 :=
mt (eq_one_of_pos_of_log_eq_zero hx_pos) hx

@[simp] lemma log_eq_zero {x : ℝ} : log x = 0 ↔ x = 0 ∨ x = 1 ∨ x = -1 :=
begin
  split,
  { intros h,
    rcases lt_trichotomy x 0 with x_lt_zero | rfl | x_gt_zero,
    { refine or.inr (or.inr (neg_eq_iff_eq_neg.mp _)),
      rw [←log_neg_eq_log x] at h,
      exact eq_one_of_pos_of_log_eq_zero (neg_pos.mpr x_lt_zero) h, },
    { exact or.inl rfl },
    { exact or.inr (or.inl (eq_one_of_pos_of_log_eq_zero x_gt_zero h)), }, },
  { rintro (rfl|rfl|rfl); simp only [log_one, log_zero, log_neg_eq_log], }
end

@[simp] lemma log_pow (x : ℝ) (n : ℕ) : log (x ^ n) = n * log x :=
begin
  induction n with n ih,
  { simp },
  rcases eq_or_ne x 0 with rfl | hx,
  { simp },
  rw [pow_succ', log_mul (pow_ne_zero _ hx) hx, ih, nat.cast_succ, add_mul, one_mul],
end

@[simp] lemma log_zpow (x : ℝ) (n : ℤ) : log (x ^ n) = n * log x :=
begin
  induction n,
  { rw [int.of_nat_eq_coe, zpow_coe_nat, log_pow, int.cast_coe_nat] },
  rw [zpow_neg_succ_of_nat, log_inv, log_pow, int.cast_neg_succ_of_nat, nat.cast_add_one,
    neg_mul_eq_neg_mul],
end

lemma log_sqrt {x : ℝ} (hx : 0 ≤ x) : log (sqrt x) = log x / 2 :=
by { rw [eq_div_iff, mul_comm, ← nat.cast_two, ← log_pow, sq_sqrt hx], exact two_ne_zero }

lemma log_le_sub_one_of_pos {x : ℝ} (hx : 0 < x) : log x ≤ x - 1 :=
begin
  rw le_sub_iff_add_le,
  convert add_one_le_exp (log x),
  rw exp_log hx,
end

/-- Bound for `|log x * x|` in the interval `(0, 1]`. -/
lemma abs_log_mul_self_lt (x: ℝ) (h1 : 0 < x) (h2 : x ≤ 1) : |log x * x| < 1 :=
begin
  have : 0 < 1/x := by simpa only [one_div, inv_pos] using h1,
  replace := log_le_sub_one_of_pos this,
  replace : log (1 / x) < 1/x := by linarith,
  rw [log_div one_ne_zero h1.ne', log_one, zero_sub, lt_div_iff h1] at this,
  have aux : 0 ≤ -log x * x,
  { refine mul_nonneg _ h1.le, rw ←log_inv, apply log_nonneg,
    rw [←(le_inv h1 zero_lt_one), inv_one], exact h2, },
  rw [←(abs_of_nonneg aux), neg_mul, abs_neg] at this, exact this,
end

/-- The real logarithm function tends to `+∞` at `+∞`. -/
lemma tendsto_log_at_top : tendsto log at_top at_top :=
tendsto_comp_exp_at_top.1 $ by simpa only [log_exp] using tendsto_id

lemma tendsto_log_nhds_within_zero : tendsto log (𝓝[≠] 0) at_bot :=
begin
  rw [← (show _ = log, from funext log_abs)],
  refine tendsto.comp _ tendsto_abs_nhds_within_zero,
  simpa [← tendsto_comp_exp_at_bot] using tendsto_id
end

lemma continuous_on_log : continuous_on log {0}ᶜ :=
begin
  rw [continuous_on_iff_continuous_restrict, restrict],
  conv in (log _) { rw [log_of_ne_zero (show (x : ℝ) ≠ 0, from x.2)] },
  exact exp_order_iso.symm.continuous.comp (continuous_subtype_coe.norm.subtype_mk _)
end

@[continuity] lemma continuous_log : continuous (λ x : {x : ℝ // x ≠ 0}, log x) :=
continuous_on_iff_continuous_restrict.1 $ continuous_on_log.mono $ λ x hx, hx

@[continuity] lemma continuous_log' : continuous (λ x : {x : ℝ // 0 < x}, log x) :=
continuous_on_iff_continuous_restrict.1 $ continuous_on_log.mono $ λ x hx, ne_of_gt hx

lemma continuous_at_log (hx : x ≠ 0) : continuous_at log x :=
(continuous_on_log x hx).continuous_at $ is_open.mem_nhds is_open_compl_singleton hx

@[simp] lemma continuous_at_log_iff : continuous_at log x ↔ x ≠ 0 :=
begin
  refine ⟨_, continuous_at_log⟩,
  rintros h rfl,
  exact not_tendsto_nhds_of_tendsto_at_bot tendsto_log_nhds_within_zero _
    (h.tendsto.mono_left inf_le_left)
end

open_locale big_operators

lemma log_prod {α : Type*} (s : finset α) (f : α → ℝ) (hf : ∀ x ∈ s, f x ≠ 0):
  log (∏ i in s, f i) = ∑ i in s, log (f i) :=
begin
  induction s using finset.cons_induction_on with a s ha ih,
  { simp },
  { rw [finset.forall_mem_cons] at hf,
    simp [ih hf.2, log_mul hf.1 (finset.prod_ne_zero_iff.2 hf.2)] }
end

lemma log_nat_eq_sum_factorization (n : ℕ) : log n = n.factorization.sum (λ p t, t * log p) :=
begin
  rcases eq_or_ne n 0 with rfl | hn,
  { simp },
  nth_rewrite 0 [←nat.factorization_prod_pow_eq_self hn],
  rw [finsupp.prod, nat.cast_prod, log_prod _ _ (λ p hp, _), finsupp.sum],
  { simp_rw [nat.cast_pow, log_pow] },
  { norm_cast,
    exact pow_ne_zero _ (nat.prime_of_mem_factorization hp).ne_zero },
end

lemma tendsto_pow_log_div_mul_add_at_top (a b : ℝ) (n : ℕ) (ha : a ≠ 0) :
  tendsto (λ x, log x ^ n / (a * x + b)) at_top (𝓝 0) :=
((tendsto_div_pow_mul_exp_add_at_top a b n ha.symm).comp tendsto_log_at_top).congr'
  (by filter_upwards [eventually_gt_at_top (0 : ℝ)] with x hx using by simp [exp_log hx])

lemma is_o_pow_log_id_at_top {n : ℕ} : (λ x, log x ^ n) =o[at_top] id :=
begin
  rw asymptotics.is_o_iff_tendsto',
  { simpa using tendsto_pow_log_div_mul_add_at_top 1 0 n one_ne_zero },
  filter_upwards [eventually_ne_at_top (0 : ℝ)] with x h₁ h₂ using (h₁ h₂).elim,
end

lemma is_o_log_id_at_top : log =o[at_top] id := is_o_pow_log_id_at_top.congr_left (λ x, pow_one _)

end real

section continuity

open real
variables {α : Type*}

lemma filter.tendsto.log {f : α → ℝ} {l : filter α} {x : ℝ} (h : tendsto f l (𝓝 x)) (hx : x ≠ 0) :
  tendsto (λ x, log (f x)) l (𝓝 (log x)) :=
(continuous_at_log hx).tendsto.comp h

variables [topological_space α] {f : α → ℝ} {s : set α} {a : α}

lemma continuous.log (hf : continuous f) (h₀ : ∀ x, f x ≠ 0) : continuous (λ x, log (f x)) :=
continuous_on_log.comp_continuous hf h₀

lemma continuous_at.log (hf : continuous_at f a) (h₀ : f a ≠ 0) :
  continuous_at (λ x, log (f x)) a :=
hf.log h₀

lemma continuous_within_at.log (hf : continuous_within_at f s a) (h₀ : f a ≠ 0) :
  continuous_within_at (λ x, log (f x)) s a :=
hf.log h₀

lemma continuous_on.log (hf : continuous_on f s) (h₀ : ∀ x ∈ s, f x ≠ 0) :
  continuous_on (λ x, log (f x)) s :=
λ x hx, (hf x hx).log (h₀ x hx)

end continuity

section tendsto_comp_add_sub

open filter
namespace real

lemma tendsto_log_comp_add_sub_log (y : ℝ) :
  tendsto (λ x:ℝ, log (x + y) - log x) at_top (𝓝 0) :=
begin
  refine tendsto.congr' (_ :  ∀ᶠ (x : ℝ) in at_top, log (1 + y / x) = _) _,
  { refine eventually.mp ((eventually_ne_at_top 0).and (eventually_gt_at_top (-y)))
    (eventually_of_forall (λ x hx, _)),
    rw ← log_div _ hx.1,
    { congr' 1,
      field_simp [hx.1] },
    { linarith [hx.2] } },
  { suffices : tendsto (λ (x : ℝ), log (1 + y / x)) at_top (𝓝 (log (1 + 0))), by simpa,
    refine tendsto.log _ (by simp),
    exact tendsto_const_nhds.add (tendsto_const_nhds.div_at_top tendsto_id) },
end

lemma tendsto_log_nat_add_one_sub_log : tendsto (λ (k : ℕ), log (k + 1) - log k) at_top (𝓝 0) :=
(tendsto_log_comp_add_sub_log 1).comp tendsto_coe_nat_at_top_at_top

end real
end tendsto_comp_add_sub


/-
Copyright (c) 2016 Jeremy Avigad. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jeremy Avigad, Leonardo de Moura, Mario Carneiro
-/
import algebra.order.ring.defs
import algebra.order.sub.canonical
import group_theory.group_action.defs

/-!
# Canoncially ordered rings and semirings.

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

* `canonically_ordered_comm_semiring`
  - `canonically_ordered_add_monoid` & multiplication & `*` respects `≤` & no zero divisors
  - `comm_semiring` & `a ≤ b ↔ ∃ c, b = a + c` & no zero divisors

## TODO

We're still missing some typeclasses, like
* `canonically_ordered_semiring`
They have yet to come up in practice.
-/

open function

set_option old_structure_cmd true

universe u
variables {α : Type u} {β : Type*}

/-- A canonically ordered commutative semiring is an ordered, commutative semiring in which `a ≤ b`
iff there exists `c` with `b = a + c`. This is satisfied by the natural numbers, for example, but
not the integers or other ordered groups. -/
@[protect_proj, ancestor canonically_ordered_add_monoid comm_semiring]
class canonically_ordered_comm_semiring (α : Type*) extends
  canonically_ordered_add_monoid α, comm_semiring α :=
(eq_zero_or_eq_zero_of_mul_eq_zero : ∀ {a b : α}, a * b = 0 → a = 0 ∨ b = 0)

section strict_ordered_semiring
variables [strict_ordered_semiring α] {a b c d : α}

section has_exists_add_of_le
variables [has_exists_add_of_le α]

/-- Binary **rearrangement inequality**. -/
lemma mul_add_mul_le_mul_add_mul (hab : a ≤ b) (hcd : c ≤ d) : a * d + b * c ≤ a * c + b * d :=
begin
  obtain ⟨b, rfl⟩ := exists_add_of_le hab,
  obtain ⟨d, rfl⟩ := exists_add_of_le hcd,
  rw [mul_add, add_right_comm, mul_add, ←add_assoc],
  exact add_le_add_left (mul_le_mul_of_nonneg_right hab $ (le_add_iff_nonneg_right _).1 hcd) _,
end

/-- Binary **rearrangement inequality**. -/
lemma mul_add_mul_le_mul_add_mul' (hba : b ≤ a) (hdc : d ≤ c) : a • d + b • c ≤ a • c + b • d :=
by { rw [add_comm (a • d), add_comm (a • c)], exact mul_add_mul_le_mul_add_mul hba hdc }

/-- Binary strict **rearrangement inequality**. -/
lemma mul_add_mul_lt_mul_add_mul (hab : a < b) (hcd : c < d) : a * d + b * c < a * c + b * d :=
begin
  obtain ⟨b, rfl⟩ := exists_add_of_le hab.le,
  obtain ⟨d, rfl⟩ := exists_add_of_le hcd.le,
  rw [mul_add, add_right_comm, mul_add, ←add_assoc],
  exact add_lt_add_left (mul_lt_mul_of_pos_right hab $ (lt_add_iff_pos_right _).1 hcd) _,
end

/-- Binary **rearrangement inequality**. -/
lemma mul_add_mul_lt_mul_add_mul' (hba : b < a) (hdc : d < c) : a • d + b • c < a • c + b • d :=
by { rw [add_comm (a • d), add_comm (a • c)], exact mul_add_mul_lt_mul_add_mul hba hdc }

end has_exists_add_of_le

end strict_ordered_semiring

namespace canonically_ordered_comm_semiring
variables [canonically_ordered_comm_semiring α] {a b : α}

@[priority 100] -- see Note [lower instance priority]
instance to_no_zero_divisors : no_zero_divisors α :=
⟨λ a b h, canonically_ordered_comm_semiring.eq_zero_or_eq_zero_of_mul_eq_zero h⟩

@[priority 100] -- see Note [lower instance priority]
instance to_covariant_mul_le : covariant_class α α (*) (≤) :=
begin
  refine ⟨λ a b c h, _⟩,
  rcases exists_add_of_le h with ⟨c, rfl⟩,
  rw mul_add,
  apply self_le_add_right
end

@[priority 100] -- see Note [lower instance priority]
instance to_ordered_comm_monoid : ordered_comm_monoid α :=
{ mul_le_mul_left := λ _ _, mul_le_mul_left',
  .. ‹canonically_ordered_comm_semiring α› }

@[priority 100] -- see Note [lower instance priority]
instance to_ordered_comm_semiring : ordered_comm_semiring α :=
{ zero_le_one := zero_le _,
  mul_le_mul_of_nonneg_left := λ a b c h _, mul_le_mul_left' h _,
  mul_le_mul_of_nonneg_right := λ a b c h _, mul_le_mul_right' h _,
  ..‹canonically_ordered_comm_semiring α› }

@[simp] lemma mul_pos : 0 < a * b ↔ (0 < a) ∧ (0 < b) :=
by simp only [pos_iff_ne_zero, ne.def, mul_eq_zero, not_or_distrib]

end canonically_ordered_comm_semiring

section sub

variables [canonically_ordered_comm_semiring α] {a b c : α}
variables [has_sub α] [has_ordered_sub α]

variables [is_total α (≤)]

namespace add_le_cancellable
protected lemma mul_tsub (h : add_le_cancellable (a * c)) :
  a * (b - c) = a * b - a * c :=
begin
  cases total_of (≤) b c with hbc hcb,
  { rw [tsub_eq_zero_iff_le.2 hbc, mul_zero, tsub_eq_zero_iff_le.2 (mul_le_mul_left' hbc a)] },
  { apply h.eq_tsub_of_add_eq, rw [← mul_add, tsub_add_cancel_of_le hcb] }
end

protected lemma tsub_mul (h : add_le_cancellable (b * c)) : (a - b) * c = a * c - b * c :=
by { simp only [mul_comm _ c] at *, exact h.mul_tsub }

end add_le_cancellable

variables [contravariant_class α α (+) (≤)]

lemma mul_tsub (a b c : α) : a * (b - c) = a * b - a * c :=
contravariant.add_le_cancellable.mul_tsub

lemma tsub_mul (a b c : α) : (a - b) * c = a * c - b * c :=
contravariant.add_le_cancellable.tsub_mul

end sub


/-
Copyright (c) 2018  Patrick Massot. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Patrick Massot, Chris Hughes, Michael Howes
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.algebra.group.hom
import Mathlib.data.equiv.mul_add_aut
import Mathlib.PostPort

universes u u_1 v

namespace Mathlib

/-!
# Conjugacy of group elements

See also `mul_aut.conj` and `quandle.conj`.
-/

/-- We say that `a` is conjugate to `b` if for some `c` we have `c * a * c⁻¹ = b`. -/
def is_conj {α : Type u} [group α] (a : α) (b : α) := ∃ (c : α), c * a * (c⁻¹) = b

theorem is_conj_refl {α : Type u} [group α] (a : α) : is_conj a a := sorry

theorem is_conj_symm {α : Type u} [group α] {a : α} {b : α} : is_conj a b → is_conj b a := sorry

theorem is_conj_trans {α : Type u} [group α] {a : α} {b : α} {c : α} :
    is_conj a b → is_conj b c → is_conj a c :=
  sorry

@[simp] theorem is_conj_one_right {α : Type u} [group α] {a : α} : is_conj 1 a ↔ a = 1 := sorry

@[simp] theorem is_conj_one_left {α : Type u} [group α] {a : α} : is_conj a 1 ↔ a = 1 :=
  iff.trans { mp := is_conj_symm, mpr := is_conj_symm } is_conj_one_right

@[simp] theorem conj_inv {α : Type u} [group α] {a : α} {b : α} :
    b * a * (b⁻¹)⁻¹ = b * (a⁻¹) * (b⁻¹) :=
  Eq.symm (mul_equiv.map_inv (coe_fn mul_aut.conj b) a)

@[simp] theorem conj_mul {α : Type u} [group α] {a : α} {b : α} {c : α} :
    b * a * (b⁻¹) * (b * c * (b⁻¹)) = b * (a * c) * (b⁻¹) :=
  Eq.symm (mul_equiv.map_mul (coe_fn mul_aut.conj b) a c)

theorem conj_injective {α : Type u} [group α] {x : α} :
    function.injective fun (g : α) => x * g * (x⁻¹) :=
  mul_equiv.injective (coe_fn mul_aut.conj x)

@[simp] theorem is_conj_iff_eq {α : Type u_1} [comm_group α] {a : α} {b : α} :
    is_conj a b ↔ a = b :=
  sorry

protected theorem monoid_hom.map_is_conj {α : Type u} {β : Type v} [group α] [group β] (f : α →* β)
    {a : α} {b : α} : is_conj a b → is_conj (coe_fn f a) (coe_fn f b) :=
  sorry

end Mathlib


/-
Copyright (c) 2014 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jeremy Avigad, Leonardo de Moura

Useful logical identities. Since we are not using propositional extensionality, some of the
calculations use the type class support provided by logic.instances.
-/
import logic.connectives logic.quantifiers logic.cast
open decidable

theorem or.right_comm (a b c : Prop) : (a ∨ b) ∨ c ↔ (a ∨ c) ∨ b :=
calc
  (a ∨ b) ∨ c ↔ a ∨ (b ∨ c) : or.assoc
    ... ↔ a ∨ (c ∨ b)       : {or.comm}
     ... ↔ (a ∨ c) ∨ b      : iff.symm or.assoc

theorem and.right_comm (a b c : Prop) : (a ∧ b) ∧ c ↔ (a ∧ c) ∧ b :=
calc
  (a ∧ b) ∧ c ↔ a ∧ (b ∧ c) : and.assoc
    ... ↔ a ∧ (c ∧ b)       : {and.comm}
     ... ↔ (a ∧ c) ∧ b      : iff.symm and.assoc

theorem or_not_self_iff (a : Prop) [D : decidable a] : a ∨ ¬ a ↔ true :=
iff.intro (assume H, trivial) (assume H, em a)

theorem not_or_self_iff (a : Prop) [D : decidable a] : ¬ a ∨ a ↔ true :=
iff.intro (λ H, trivial) (λ H, or.swap (em a))

theorem and_not_self_iff (a : Prop) : a ∧ ¬ a ↔ false :=
iff.intro (assume H, (and.right H) (and.left H)) (assume H, false.elim H)

theorem not_and_self_iff (a : Prop) : ¬ a ∧ a ↔ false :=
iff.intro (λ H, and.elim H (by contradiction)) (λ H, false.elim H)

theorem not_not_iff (a : Prop) [D : decidable a] : ¬¬a ↔ a :=
iff.intro by_contradiction not_not_intro

theorem not_not_elim {a : Prop} [D : decidable a] : ¬¬a → a :=
by_contradiction

theorem not_or_iff_not_and_not (a b : Prop) : ¬(a ∨ b) ↔ ¬a ∧ ¬b :=
or.imp_distrib

theorem not_or_not_of_not_and {a b : Prop} [Da : decidable a] (H : ¬ (a ∧ b)) : ¬ a ∨ ¬ b :=
by_cases (λHa, or.inr (not.mto (and.intro Ha) H)) or.inl

theorem not_or_not_of_not_and' {a b : Prop} [Db : decidable b] (H : ¬ (a ∧ b)) : ¬ a ∨ ¬ b :=
by_cases (λHb, or.inl (λHa, H (and.intro Ha Hb))) or.inr

theorem not_and_iff_not_or_not (a b : Prop) [Da : decidable a] :
  ¬(a ∧ b) ↔ ¬a ∨ ¬b :=
iff.intro
  not_or_not_of_not_and
  (or.rec (not.mto and.left) (not.mto and.right))

theorem or_iff_not_and_not (a b : Prop) [Da : decidable a] [Db : decidable b] :
  a ∨ b ↔ ¬ (¬a ∧ ¬b) :=
by rewrite [-not_or_iff_not_and_not, not_not_iff]

theorem and_iff_not_or_not (a b : Prop) [Da : decidable a] [Db : decidable b] :
  a ∧ b ↔ ¬ (¬ a ∨ ¬ b) :=
by rewrite [-not_and_iff_not_or_not, not_not_iff]

theorem imp_iff_not_or (a b : Prop) [Da : decidable a] : (a → b) ↔ ¬a ∨ b :=
iff.intro
  (by_cases (λHa H, or.inr (H Ha)) (λHa H, or.inl Ha))
  (or.rec not.elim imp.intro)

theorem not_implies_iff_and_not (a b : Prop) [Da : decidable a] :
  ¬(a → b) ↔ a ∧ ¬b :=
calc
  ¬(a → b) ↔ ¬(¬a ∨ b) : {imp_iff_not_or a b}
       ... ↔ ¬¬a ∧ ¬b  : not_or_iff_not_and_not
       ... ↔ a ∧ ¬b    : {not_not_iff a}

theorem and_not_of_not_implies {a b : Prop} [Da : decidable a] (H : ¬ (a → b)) : a ∧ ¬ b :=
iff.mp !not_implies_iff_and_not H

theorem not_implies_of_and_not {a b : Prop} [Da : decidable a] (H : a ∧ ¬ b) : ¬ (a → b) :=
iff.mpr !not_implies_iff_and_not H

theorem peirce (a b : Prop) [D : decidable a] : ((a → b) → a) → a :=
by_cases imp.intro (imp.syl imp.mp not.elim)

theorem forall_not_of_not_exists {A : Type} {p : A → Prop} [D : ∀x, decidable (p x)]
  (H : ¬∃x, p x) : ∀x, ¬p x :=
take x, by_cases
  (assume Hp : p x, absurd (exists.intro x Hp) H)
  imp.id

theorem forall_of_not_exists_not {A : Type} {p : A → Prop} [D : decidable_pred p] :
  ¬(∃ x, ¬p x) → ∀ x, p x :=
imp.syl (forall_imp_forall (λa, not_not_elim)) forall_not_of_not_exists

theorem exists_not_of_not_forall {A : Type} {p : A → Prop} [D : ∀x, decidable (p x)]
    [D' : decidable (∃x, ¬p x)] (H : ¬∀x, p x) :
  ∃x, ¬p x :=
by_contradiction (λH1, absurd (λx, not_not_elim (forall_not_of_not_exists H1 x)) H)

theorem exists_of_not_forall_not {A : Type} {p : A → Prop} [D : ∀x, decidable (p x)]
    [D' : decidable (∃x, p x)] (H : ¬∀x, ¬ p x) :
  ∃x, p x :=
by_contradiction (imp.syl H forall_not_of_not_exists)


/-
Copyright (c) 2021 Vladimir Goryachev. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yaël Dillies, Vladimir Goryachev, Kyle Miller, Scott Morrison, Eric Rodriguez

! This file was ported from Lean 3 source module data.nat.count
! leanprover-community/mathlib commit 1ead22342e1a078bd44744ace999f85756555d35
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.SetTheory.Cardinal.Basic
import Mathbin.Tactic.Ring

/-!
# Counting on ℕ

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

This file defines the `count` function, which gives, for any predicate on the natural numbers,
"how many numbers under `k` satisfy this predicate?".
We then prove several expected lemmas about `count`, relating it to the cardinality of other
objects, and helping to evaluate it for specific `k`.

-/

open Finset

namespace Nat

variable (p : ℕ → Prop)

section Count

variable [DecidablePred p]

#print Nat.count /-
/-- Count the number of naturals `k < n` satisfying `p k`. -/
def count (n : ℕ) : ℕ :=
  (List.range n).countp p
#align nat.count Nat.count
-/

#print Nat.count_zero /-
@[simp]
theorem count_zero : count p 0 = 0 := by rw [count, List.range_zero, List.countp]
#align nat.count_zero Nat.count_zero
-/

#print Nat.CountSet.fintype /-
/-- A fintype instance for the set relevant to `nat.count`. Locally an instance in locale `count` -/
def CountSet.fintype (n : ℕ) : Fintype { i // i < n ∧ p i } :=
  by
  apply Fintype.ofFinset ((Finset.range n).filterₓ p)
  intro x
  rw [mem_filter, mem_range]
  rfl
#align nat.count_set.fintype Nat.CountSet.fintype
-/

scoped[Count] attribute [instance] Nat.CountSet.fintype

#print Nat.count_eq_card_filter_range /-
theorem count_eq_card_filter_range (n : ℕ) : count p n = ((range n).filterₓ p).card :=
  by
  rw [count, List.countp_eq_length_filter]
  rfl
#align nat.count_eq_card_filter_range Nat.count_eq_card_filter_range
-/

#print Nat.count_eq_card_fintype /-
/-- `count p n` can be expressed as the cardinality of `{k // k < n ∧ p k}`. -/
theorem count_eq_card_fintype (n : ℕ) : count p n = Fintype.card { k : ℕ // k < n ∧ p k } :=
  by
  rw [count_eq_card_filter_range, ← Fintype.card_ofFinset, ← count_set.fintype]
  rfl
#align nat.count_eq_card_fintype Nat.count_eq_card_fintype
-/

#print Nat.count_succ /-
theorem count_succ (n : ℕ) : count p (n + 1) = count p n + if p n then 1 else 0 := by
  split_ifs <;> simp [count, List.range_succ, h]
#align nat.count_succ Nat.count_succ
-/

#print Nat.count_monotone /-
@[mono]
theorem count_monotone : Monotone (count p) :=
  monotone_nat_of_le_succ fun n => by by_cases h : p n <;> simp [count_succ, h]
#align nat.count_monotone Nat.count_monotone
-/

#print Nat.count_add /-
theorem count_add (a b : ℕ) : count p (a + b) = count p a + count (fun k => p (a + k)) b :=
  by
  have : Disjoint ((range a).filterₓ p) (((range b).map <| addLeftEmbedding a).filterₓ p) :=
    by
    apply disjoint_filter_filter
    rw [Finset.disjoint_left]
    simp_rw [mem_map, mem_range, addLeftEmbedding_apply]
    rintro x hx ⟨c, _, rfl⟩
    exact (self_le_add_right _ _).not_lt hx
  simp_rw [count_eq_card_filter_range, range_add, filter_union, card_disjoint_union this,
    filter_map, addLeftEmbedding, card_map]
  rfl
#align nat.count_add Nat.count_add
-/

#print Nat.count_add' /-
theorem count_add' (a b : ℕ) : count p (a + b) = count (fun k => p (k + b)) a + count p b :=
  by
  rw [add_comm, count_add, add_comm]
  simp_rw [add_comm b]
#align nat.count_add' Nat.count_add'
-/

#print Nat.count_one /-
theorem count_one : count p 1 = if p 0 then 1 else 0 := by simp [count_succ]
#align nat.count_one Nat.count_one
-/

#print Nat.count_succ' /-
theorem count_succ' (n : ℕ) :
    count p (n + 1) = count (fun k => p (k + 1)) n + if p 0 then 1 else 0 := by
  rw [count_add', count_one]
#align nat.count_succ' Nat.count_succ'
-/

variable {p}

#print Nat.count_lt_count_succ_iff /-
@[simp]
theorem count_lt_count_succ_iff {n : ℕ} : count p n < count p (n + 1) ↔ p n := by
  by_cases h : p n <;> simp [count_succ, h]
#align nat.count_lt_count_succ_iff Nat.count_lt_count_succ_iff
-/

#print Nat.count_succ_eq_succ_count_iff /-
theorem count_succ_eq_succ_count_iff {n : ℕ} : count p (n + 1) = count p n + 1 ↔ p n := by
  by_cases h : p n <;> simp [h, count_succ]
#align nat.count_succ_eq_succ_count_iff Nat.count_succ_eq_succ_count_iff
-/

#print Nat.count_succ_eq_count_iff /-
theorem count_succ_eq_count_iff {n : ℕ} : count p (n + 1) = count p n ↔ ¬p n := by
  by_cases h : p n <;> simp [h, count_succ]
#align nat.count_succ_eq_count_iff Nat.count_succ_eq_count_iff
-/

alias count_succ_eq_succ_count_iff ↔ _ count_succ_eq_succ_count
#align nat.count_succ_eq_succ_count Nat.count_succ_eq_succ_count

alias count_succ_eq_count_iff ↔ _ count_succ_eq_count
#align nat.count_succ_eq_count Nat.count_succ_eq_count

/- warning: nat.count_le_cardinal -> Nat.count_le_cardinal is a dubious translation:
lean 3 declaration is
  forall {p : Nat -> Prop} [_inst_1 : DecidablePred.{1} Nat p] (n : Nat), LE.le.{1} Cardinal.{0} Cardinal.hasLe.{0} ((fun (a : Type) (b : Type.{1}) [self : HasLiftT.{1, 2} a b] => self.0) Nat Cardinal.{0} (HasLiftT.mk.{1, 2} Nat Cardinal.{0} (CoeTCₓ.coe.{1, 2} Nat Cardinal.{0} (Nat.castCoe.{1} Cardinal.{0} Cardinal.hasNatCast.{0}))) (Nat.count p (fun (a : Nat) => _inst_1 a) n)) (Cardinal.mk.{0} (coeSort.{1, 2} (Set.{0} Nat) Type (Set.hasCoeToSort.{0} Nat) (setOf.{0} Nat (fun (k : Nat) => p k))))
but is expected to have type
  forall {p : Nat -> Prop} [_inst_1 : DecidablePred.{1} Nat p] (n : Nat), LE.le.{1} Cardinal.{0} Cardinal.instLECardinal.{0} (Nat.cast.{1} Cardinal.{0} Cardinal.instNatCastCardinal.{0} (Nat.count p (fun (a : Nat) => _inst_1 a) n)) (Cardinal.mk.{0} (Set.Elem.{0} Nat (setOf.{0} Nat (fun (k : Nat) => p k))))
Case conversion may be inaccurate. Consider using '#align nat.count_le_cardinal Nat.count_le_cardinalₓ'. -/
theorem count_le_cardinal (n : ℕ) : (count p n : Cardinal) ≤ Cardinal.mk { k | p k } :=
  by
  rw [count_eq_card_fintype, ← Cardinal.mk_fintype]
  exact Cardinal.mk_subtype_mono fun x hx => hx.2
#align nat.count_le_cardinal Nat.count_le_cardinal

#print Nat.lt_of_count_lt_count /-
theorem lt_of_count_lt_count {a b : ℕ} (h : count p a < count p b) : a < b :=
  (count_monotone p).reflect_lt h
#align nat.lt_of_count_lt_count Nat.lt_of_count_lt_count
-/

#print Nat.count_strict_mono /-
theorem count_strict_mono {m n : ℕ} (hm : p m) (hmn : m < n) : count p m < count p n :=
  (count_lt_count_succ_iff.2 hm).trans_le <| count_monotone _ (Nat.succ_le_iff.2 hmn)
#align nat.count_strict_mono Nat.count_strict_mono
-/

#print Nat.count_injective /-
theorem count_injective {m n : ℕ} (hm : p m) (hn : p n) (heq : count p m = count p n) : m = n :=
  by
  by_contra' h : m ≠ n
  wlog hmn : m < n
  · exact this hn hm HEq.symm h.symm (h.lt_or_lt.resolve_left hmn)
  · simpa [HEq] using count_strict_mono hm hmn
#align nat.count_injective Nat.count_injective
-/

#print Nat.count_le_card /-
theorem count_le_card (hp : (setOf p).Finite) (n : ℕ) : count p n ≤ hp.toFinset.card :=
  by
  rw [count_eq_card_filter_range]
  exact Finset.card_mono fun x hx => hp.mem_to_finset.2 (mem_filter.1 hx).2
#align nat.count_le_card Nat.count_le_card
-/

#print Nat.count_lt_card /-
theorem count_lt_card {n : ℕ} (hp : (setOf p).Finite) (hpn : p n) : count p n < hp.toFinset.card :=
  (count_lt_count_succ_iff.2 hpn).trans_le (count_le_card hp _)
#align nat.count_lt_card Nat.count_lt_card
-/

variable {q : ℕ → Prop}

variable [DecidablePred q]

#print Nat.count_mono_left /-
theorem count_mono_left {n : ℕ} (hpq : ∀ k, p k → q k) : count p n ≤ count q n :=
  by
  simp only [count_eq_card_filter_range]
  exact card_le_of_subset ((range n).monotone_filter_right hpq)
#align nat.count_mono_left Nat.count_mono_left
-/

end Count

end Nat


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

import tactic -- imports all the Lean tactics

/-!

# Sets in Lean, example sheet 2 : the empty set and the "universal set".

We know what the empty subset of `X` is, and the Lean notation for
it is `∅`, or, if you want to say which type we're the empty subset
of, it's `∅ : set X`.

At the other extreme, the subset of `X` containing all the terms of type `X`
is...well...mathematicians would just call it `X`, but `X` is a type, and
so if we want a set it's called `set.univ : set X`, or just `univ : set X` if
we have opened the `set` namespace. Let's do that now.

-/

open set

/-

## Important

`x ∈ ∅` is *by definition* equal to `false` and `x ∈ univ` is *by definition*
equal to `true`. You can use the `change` tactic to make these changes
if you like. But you don't have to.

## Tactics you will need

You've seen them already. `trivial` proves `⊢ true` and `exfalso`
changes `⊢ P` to `⊢ false`.

-/

-- set up variables
variables
  (X : Type) -- Everything will be a subset of `X`
  (A B C D E : set X) -- A,B,C,D,E are subsets of `X`
  (x y z : X) -- x,y,z are elements of `X` or, more precisely, terms of type `X`

/-

If `x : X` then `x ∈ ∅` is *by definition* `false`, and `x ∈ univ` is
*by definition* `true`. So you can use the `change` tactic to change
between these things, for example if your goal is

```
⊢ x ∈ univ
```

then `change true` will change the goal to

```
⊢ true
```

and you can now prove this goal with `trivial`. However you can prove
it with `trivial` even without `change`ing it.

-/

open set

example : x ∈ (univ : set X) :=
begin
  trivial,
end

example : x ∈ (∅ : set X) → false :=
begin
  intro h,
  exact h,
end

example : ∀ x : X, x ∈ A → x ∈ (univ : set X) :=
begin
  intros x h,
  trivial,
end

example : ∀ x : X, x ∈ (∅ : set X) → x ∈ A :=
begin
  intros x h,
  exfalso,
  exact h,
end


-- always import the tactics, we are mathematicians
import tactic
-- import the theory of G-module homomorphisms, for G a group
import algebra.group_action_hom

/-

# Introduction to G-modules in Lean

Let `G` be a group (with group law `*`) and let `M` be an abelian
group (with group law `+`). A `G`-action on `M` is just a group
homomorphism from `G` to the group automorphisms
of `M`, or in other words an action `•` of `G` on `M` (in the sense
of groups acting on sets/types) satisfying
the axiom `smul_add g m n : g • (m + n) = g • m + g • n`.

The goal of this workshop will be to set up a cohomology theory
for G-modules. We will just do H⁰ (G-invariant elements)
and H¹ (1-cocycles modulo coboundaries), but clearly one
could go on to 2-cocycles, n-cocycles etc.

### typeclass comments ("will it work for monoids/add_monoids?")

Note that the definition of G-module does not mention `g⁻¹` at all, so
we can even define it for monoids `G`, which we will. Loads
of the theory works for `G` a monoid in fact (certainly everything
we do in this workshop). But we use subtraction on `M` quite a
lot in practice when we get to `H¹` (e.g. the coboundary `g b - b`
needs subtraction) so I've assumed that `M` is an abelian group throughout for
pedagogical reasons (and because it solved some typeclass issue at some point).

The `G`-module structure on `M` is called `distrib_mul_action G M` in Lean.
-/

section distrib_mul_action_stuff

/-

## The interface for G-modules, i.e. the theory of `•`

In Lean we learn about the typeclass `[distrib_mul_action G M]`,
which gives us the notation `•` for an action of `G` on `M`,
and all the axioms.

Notation for this section:

Let `G` be a group. Let `M` be an abelian group and furthermore
assume `M` is a `G`-module. We use the usual notation `(g₁ * g₂) • (m₁ + m₂)`

-/

variables
  {G : Type} [monoid G] --`*`
  {M : Type} [add_comm_group M] --`+`
  [distrib_mul_action G M] --`•`

-- Let `g`'ish variables be elements of `G`, and let `m`ish variables be
-- elements of `M`.
variables (g g1 g2 g₁ g₂ : G) (m m1 m2 m₁ m₂ : M)

/-

### The interface for `•`

Below are the names of the theorem proofs which you will need
to know when manipulating an element of a fixed `G`-module,
for example the element `(g₁ * g₂) • (m₁ + m₂)`.

I have explained the names of the proofs in the form
of examples. The syntax for the examples is this:

`example : <Theorem statement> := <name of proof function> input1 input2 ...`

So these examples tell you the names of the proofs of the theorems.
The proofs are functions which need inputs, and the inputs are
the variables used in the theorem statement. I also mention
whether Lean's "rw-machine" (the `simp` tactic) knows about these theorems.

-/

example : g • (0 : M) = 0 := smul_zero g -- a simp lemma
example : g • (m₁ + m₂) = g • m₁ + g • m₂ := smul_add g m₁ m₂ -- a simp lemma
example : g • (-m) = -(g • m) := smul_neg g m -- a simp lemma
example : (1 : G) • m = m := one_smul G m -- a simp lemma
example : g • (m₁ - m₂) = g • m₁ - g • m₂ := smul_sub g m₁ m₂ -- at the time of writing not a simp lemma
example : (g₁ * g₂) • m = g₁ • g₂ • m := mul_smul g₁ g₂ m -- not a simp lemma

-- try some examples for yourself.
example : (g1 * g2) • (m1 + m2) = g1 • g2 • m1 + g1 • g2 • m2 :=
begin
  sorry
end

example : (1 * 1 * 1 : G) • m = m :=
begin
  sorry
end

end distrib_mul_action_stuff

/-

### Entirely optional digression on `simp`

Some of those lemmas above were "simp lemmas" (if you `#print one_smul`
you'll see it has a `@[simp]` tag). What makes a good simp lemma?

The most important rule is that, unless you really know what you're
doing, it should be of the form `A = B` or `A ↔ B`.

The second rule is that the right hand side should in some sense
be "simpler than" the left hand side.
so the lemma should say `A simplifies_to B`, indicating a flow towards
a solution.

For example `one_mul : 1 * a = a` is a `simp` lemma for groups (and
for monoids), because it is an equality, and the right hand side
is unarguably simpler than the left hand side.

Later on we'll be making some of our own structures, and
we will want to train Lean's simplifier to use those structures. The better
you understand how the simplifier works on your structures, the easier you
will find it to type "mathematics as the mathematician thinks about it"
into Lean.

-/

/-

### The `simp` tactic

A lemma is, by definition, a `simp` lemma, if its proof term is tagged
with the `@[simp]` attribute (you can check a term's attributes with `#print`)

`simp` is an algorithm which will "follow its nose", doing
stuff like expanding out brackets automatically and tidying up.
It would tidy up by simplifying `g • 0` to `0` for example,
and it would expand out by changing `g • (m₁ + m₂)` to `g • m₁ + g • m₂`.
In general `simp` tries to rewrite equivalences, e.g. things of the
form `A = B` or `A ↔ B`, with in each case `B` the "simplified" or
"expanded out" version of `A`. The equivalences it rewrites
with are the ones in its database of a few thousand (*shrug?)
so-called "`simp` lemmas" For example, if `⇑0 : G → M` is the
zero function then `zero_val g : ⇑0 g = 0` would be a good `simp`
lemma, which is why it is tagged with the `@[simp]` attribute,
making it part of the database.

Note that `simp` does not have "ideas". It will never apply
commutativity or associativity, for example, for fear that
it might be a waste of time which would have to be undone later.
`simp` will solve `x + 0 = x` but it will not solve `m + n = n + m`,
because it is not so clear that the right hand side is any simpler
than the left hand side. problems like that you need `abel`.

To learn more about `simp`, check out the simp docs on
the leanprover-community website.
-- TODO when online -- add link to simp docs in API at leanprover-community website

## A note on `abel`

`abel` should be able to solve all problems in abelian groups
of the form ∀ a b c, a + (c + -b) = (a - b) + c etc.
Note however that it *cannot use hypotheses*. It will only
prove identities which are true in all abelian groups.

-/
example (M : Type) [add_comm_group M] (a b c : M) :
  a + (c + -b) = (a - b) + c := by abel

/-
## The interface for morphisms of G-modules, i.e. the theory of `→+[_]`

Lean uses notation `M →+[G] N` and name `distrib_mul_action_hom`,
but you don't need to remember the name, it should work in the
background for you.

The type of G-module homs from `M` to `N`, i.e. the set
that a mathematician would call something like $$\Hom_G(M,N)$$,
is in Lean called `M →+[G] N`. The non-notation name for this function
type is `distrib_mul_action_hom G M N`, which is why you see this word in
namespaces or mentioned in sections.

Terms of this type are `G`-module morphisms from `M` to `N`.
So when we see `φ : M →+[G] N` it means that `φ` is a G-module hom
from `M` to `N`. We will often only be using `φ` only in terms of its
associated function `⇑φ : M → N`. `φ` itself is a package, consisting
of a function and a bunch of theorems about that function.

-/

namespace distrib_mul_action_hom

-- let's do the variables
variables
-- let `G` be a group (or a monoid)
{G : Type} [monoid G]

{M : Type}  [add_comm_group M] [distrib_mul_action G M] -- let `M` be a `G`-module
{N : Type}  [add_comm_group N] [distrib_mul_action G N] -- let `N` be a `G`-module
(φ : M →+[G] N) -- let φ be a morphism of G-modules
(g : G) (m m₁ m₂ m1 m2 : M) -- random useful variable names

/-

### API for `M →+[G] N`

Here are the names of the proofs of the basic axioms for G-module
morphisms. The proofs are in the `→+[_]` namespace, so you can
write things like `φ.map_smul` to access them easily.

-/
example : φ (g • m) = g • (φ m) := φ.map_smul g m -- a simp lemma
example (m₁ m₂ : M) : φ (m₁ + m₂) = φ m₁ + φ m₂ := φ.map_add m₁ m₂ -- a simp lemma

example : φ (g • (m₁ + m₂)) = g • φ m₁ + g • φ m₂ :=
begin
  -- what will you rewrite? Will you rewrite at all?
  sorry
end

/-

## A G-module morphism is a pair of things

A G-module morphism `φ : M →+[G] N` is two things.
1) a function `⇑φ : M → N`
2) the dot notation system for `φ`, a database where
all the axioms and theorems for G-module homs as applied to `φ` are stored.

For example, the type of φ.map_smul is *actually* a theorem about `⇑φ`.

`φ.map_smul g m : ⇑φ (g • m) = g • ⇑φ m`

-/

example (φ : M →+[G] N) : φ 0 = 0 :=
begin
  -- library_search will take some time (I don't know why)
  -- but will eventually find the answer to this one.
  -- But you can guess it quicker!
  -- what will you rewrite?
  sorry
end

-- Can you solve it in term mode like in those earlier examples?
example : φ 0 = 0 := sorry

-- Can you change `sorry` to the name of a tactic?
example : φ 0 = 0 := by sorry

/-

### Composition of G-module morphisms

You know how to compose functions, you just write `ψ (φ a)`
or whatever. Composition in the category of G-modules is
done with the `comp` method for `G`-module morphisms.

-/

-- let P be another G-module
variables {P : Type} [add_comm_monoid P] [distrib_mul_action G P]

-- Recall `φ : M →+[G] N` from earlier.
-- let ψ : N → P be another G-module morphism
variable (ψ : N →+[G] P) -- his is notation for `ψ : distrib_mul_action_hom G N P`

-- let's make function composition notation
infixr ` ∘ᵍ `:90 := distrib_mul_action_hom.comp

-- how to compose G-module maps
example : M →+[G] P := ψ ∘ᵍ φ

-- You should think of φ and ψ as morphisms in the category
-- of `G`-modules. They are functions, but they also have
-- some extra category-theoretic baggage (proofs that they
-- are G-linear maps) which needs to be moved around.

-- KB NOTE TO SELF do we ever actually compose morphisms?
-- My definition of short exact sequence of G-modules
-- is "image = kernel" , which is highly category-theoretic.
-- and functional evaluation often takes place after that.

-- The important fact is that `(ψ ∘ φ) m = ψ (φ m)`, as
-- terms of type `P`. Rather nicely, this theorem is called
-- `ψ.comp_apply` but it is also a `simp` lemma, and
-- furthermore true by definition
example (m : M) :
  (ψ ∘ᵍ φ) m = ψ (φ m) := ψ.comp_apply φ m -- and `rfl` works too and `by simp`

example (m : M) :
  -- normal function composition
  (ψ ∘ φ) m = ψ (φ m) := ψ.comp_apply φ m -- and `rfl` works too and `by simp`

-- By the way, `squeeze_simp` is a version of `simp` which tells you
-- which rewrites it did. Give it a try!
example :
  (ψ ∘ᵍ φ) (g • (m1 + m2)) = g • (ψ (φ m1) + ψ (φ m2)) :=
begin
  simp,
end

end distrib_mul_action_hom

section exactness_stuff

/-

## A half-developed API for exact sequences

I will do this one because right now we are missing some definitions.
SOME OF THE SORRYS IN THIS SECTION ARE CURRENTLY UNSOLVABLE -- JUST
LEAVE THEM ALL ALONE

-/

-- Let M, N and P be G-modules
variables {G M N P : Type}
  [monoid G] [add_comm_group M] [add_comm_group N] [add_comm_group P]
  [distrib_mul_action G M] [distrib_mul_action G N] [distrib_mul_action G P]

/-

### Stubbing out a theory

I need to talk about kernels and images as G-module maps.
Jobo is writing this. I just stub out the theory and
sorry the proofs I need, but sorrying definitions comes
with problems.

-/

-- THE WRONG DEFINITION
structure sub_distrib_mul_action
  (G : Type) [monoid G]
  (M : Type) [add_comm_group M] [distrib_mul_action G M] :=
(carrier : set M)

-- LEAVE THESE FOUR SORRYS ALONE
def distrib_mul_action_hom.ker (φ : M →+[G] N) :
  sub_distrib_mul_action G M :=
⟨{m | φ m = 0}⟩

def distrib_mul_action_hom.range (φ : M →+[G] N) :
  sub_distrib_mul_action G N :=
⟨set.range φ⟩

instance : has_coe (sub_distrib_mul_action G M) (set M) :=
⟨sub_distrib_mul_action.carrier⟩

theorem sub_distrib_mul_action.ext_iff {A B : sub_distrib_mul_action G M} :
  A = B ↔ ∀ m : M, m ∈ (A : set M) ↔ m ∈ (B : set M) := sorry

/-

That stuff above will all be deleted at some point

-/

-- It doesn't matter what the internal definition because the end user
-- will only be using `is_short_exact` for G-modules.
definition is_exact (φ : M →+[G] N) (ψ : N →+[G] P) : Prop :=
φ.range = ψ.ker

/-

### Mathematicians have more than one "definition" of `is_exact`

Of course exactness means the image is the kernel, of course it
means for every n, n is in the image iff n is in the kernel,
and of course it means `∀ n : N, (∃ m : M, φ m = n) ↔ ψ n` because
to a mathematician all of these ideas are *the same thing*.

In Lean some of these things are definitionally equal, some
equivalences involve coercions, some use set extensionality.
But we need to make sure that we offer the mathematician all
possible useful definitions of being "true by definition" .

-/

-- definition of exact in category theory world
@[simp] lemma is_exact.def_cat (φ : M →+[G] N) (ψ : N →+[G] P) :
  is_exact φ ψ ↔ φ.range = ψ.ker := iff.rfl

-- THIS SORRIED THEOREM CANNOT BE SOLVED UNTIL THE SORRIED DEFS ARE MADE
-- definition of exact in set theory world
@[simp] lemma is_exact.def_set (φ : M →+[G] N) (ψ : N →+[G] P) :
  is_exact φ ψ ↔ ∀ n : N, (∃ m : M, φ m = n) ↔ ψ n = 0 :=
begin
  rw is_exact.def_cat,
  rw sub_distrib_mul_action.ext_iff,
  sorry, -- will be refl once we have sub-G-modules.
end

/-

## Making an API for short exact sequences.

This is just basic logic and should be accessible to anyone who knows
the mathematics and has played the natural number game.

-/
open function

-- here is the docstring for short exact sequences

/--
Fundamental to cohomology theory is the concept of a short
exact sequence. If `φ : M →+[G] N` and
`ψ : N →+[G] P` are G-module morphisms, `is_short_exact φ ψ`
is the proposition stating that `0 → M -φ→ N -ψ→ P → 0` is short exact
in the usual sense, that is:

*) `φ` is injective,
*) the range of `φ` equals the kernel of `ψ`,
*) `ψ` is surjective.

If `h : is_short_exact φ ψ` then you can access various standard
facts about `φ` and `ψ` using dot notation with `h`. For example
`h.injective` is the proof that `φ` is injective, and
`h.exact_set` is the proof of some expanded-out version of the
statement that an element `n` is in the image
of `φ` if and only if it is in the kernel of `ψ`. A rather more
compact definition is `h.exact_cat`.
-/
-- This will do for an internal definition. The user should
-- never have to think about that though.
def is_short_exact (φ : M →+[G] N) (ψ : N →+[G] P) : Prop :=
  is_exact φ ψ ∧ injective φ ∧ surjective ψ

-- We need to make a nice API for this, it's easy and fun.

variables (φ : M →+[G] N) (ψ : N →+[G] P)

-- useful for rewrites when we're making the API, but the user
-- should never see this.
protected lemma is_short_exact_def :
  is_short_exact φ ψ ↔ is_exact φ ψ ∧ injective φ ∧ surjective ψ :=
-- true by definition
iff.rfl

-- I marked it protected because the end user should never have
-- to use this lemma in this repo, they should always use the `h.injective`
-- dot notation.

-- Now the proper API
namespace is_short_exact

-- We are making the API so we are allowed to unfold stuff

variables {φ} {ψ} (h : is_short_exact φ ψ)

include h
def injective : injective φ :=
begin
  /- put your infoview filter onto only props.
     You see

     h: is_short_exact φ ψ
     ⊢ injective ⇑φ

     That's the question. You can take `h` apart
     with `cases`, and even more effectively with
     `rcases`.
  -/
  sorry,
end

def surjective : surjective ψ :=
begin
  sorry,
end

-- again we don't really want the user messing
-- with this internal function, it should be thought
-- of as "an abbreviation for several things".
protected def exact : is_exact φ ψ := h.1

-- now `h.exact` is the proof that the short exact sequence
-- corresponding to `h` is exact.

/--
In a short exact sequence `short_exact_sequence φ ψ`,
an element of the middle module is in the
image of `φ` if and only if it is in the kernel of `ψ`.   -/
theorem exact_set : ∀ n : N, (∃ m : M, φ m = n) ↔ ψ n = 0 :=
begin
  sorry,
end

/--
In a short exact sequence `short_exact_sequence φ ψ`,
the image of `φ` equals kernel of `ψ`.   -/
def exact_cat : φ.range = ψ.ker :=
begin
  sorry,
end

/-

### Some more API for short exact sequences.

It's convenient to define a noncomputable function using axiom of choice,
which is a random splitting of the surjection ψ : P → N and hence a
one-sided inverse. We will need such a function when doing boundary maps.
We call this function `inverse_ψ` and prove `ψ (inverse_ψ p) = p` .
We also define `inverse_φ` -- given `n : N` and a proof that
it's in the image of `φ`, I pull back `n` to some explicit `inverse_φ n : M`
and prove `φ (inverse_φ n) = n`.

This use of the axiom of choice is a bit delicate so I'll do it.
-/

noncomputable def inverse_ψ : P → N := λ p, classical.some (h.surjective p)

@[simp] lemma inverse_ψ_spec (p : P) : ψ (h.inverse_ψ p) = p :=
classical.some_spec (h.surjective p)

-- now the same sort of thing for the injection φ : M → N; this is
-- the map from the image of φ back to M.
noncomputable def inverse_φ (h : is_short_exact φ ψ) (n : N)
  (hn : ∃ m : M, φ m = n) : M :=
classical.some hn

@[simp]
lemma inverse_φ_def (h : is_short_exact φ ψ) {n : N} (hn : ∃ m : M, φ m = n) :
  φ (h.inverse_φ n hn) = n :=
classical.some_spec hn

-- injectivity implies it's independent of choice, but we used choice anyway
@[simp] lemma inverse_φ_spec (h : is_short_exact φ ψ) {n : N} {m : M}
  (hm : φ m = n) : h.inverse_φ _ ⟨m, hm⟩ = m :=
begin
  apply h.injective,
  rw hm,
  exact classical.some_spec ⟨m, hm⟩,
end

end is_short_exact

end exactness_stuff


/-
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 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
`field_theory.algebraic_closure`.
-/

open_locale big_operators

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

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

notation `ℂ` := complex

namespace complex

open_locale complex_conjugate

noncomputable instance : decidable_eq ℂ := classical.dec_eq _

/-- The equivalence between the complex numbers and `ℝ × ℝ`. -/
@[simps] def equiv_real_prod : ℂ ≃ (ℝ × ℝ) :=
{ to_fun := λ z, ⟨z.re, z.im⟩,
  inv_fun := λ p, ⟨p.1, p.2⟩,
  left_inv := λ ⟨x, y⟩, rfl,
  right_inv := λ ⟨x, y⟩, rfl }

@[simp] theorem eta : ∀ z : ℂ, complex.mk z.re z.im = z
| ⟨a, b⟩ := rfl

@[ext]
theorem ext : ∀ {z w : ℂ}, z.re = w.re → z.im = w.im → z = w
| ⟨zr, zi⟩ ⟨_, _⟩ rfl rfl := rfl

theorem ext_iff {z w : ℂ} : z = w ↔ z.re = w.re ∧ z.im = w.im :=
⟨λ H, by simp [H], and.rec ext⟩

instance : has_coe ℝ ℂ := ⟨λ r, ⟨r, 0⟩⟩

@[simp, norm_cast] lemma of_real_re (r : ℝ) : (r : ℂ).re = r := rfl
@[simp, norm_cast] lemma of_real_im (r : ℝ) : (r : ℂ).im = 0 := rfl
lemma of_real_def (r : ℝ) : (r : ℂ) = ⟨r, 0⟩ := rfl

@[simp, norm_cast] theorem of_real_inj {z w : ℝ} : (z : ℂ) = w ↔ z = w :=
⟨congr_arg re, congr_arg _⟩

theorem of_real_injective : function.injective (coe : ℝ → ℂ) :=
λ z w, congr_arg re

instance : can_lift ℂ ℝ :=
{ cond := λ z, z.im = 0,
  coe := coe,
  prf := λ z hz, ⟨z.re, ext rfl hz.symm⟩ }

/-- 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 _root_.set.re_prod_im (s t : set ℝ) : set ℂ := re ⁻¹' s ∩ im ⁻¹' t

infix ` ×ℂ `:72 := set.re_prod_im

instance : has_zero ℂ := ⟨(0 : ℝ)⟩
instance : inhabited ℂ := ⟨0⟩

@[simp] lemma zero_re : (0 : ℂ).re = 0 := rfl
@[simp] lemma zero_im : (0 : ℂ).im = 0 := rfl
@[simp, norm_cast] lemma 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

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

@[simp] lemma one_re : (1 : ℂ).re = 1 := rfl
@[simp] lemma one_im : (1 : ℂ).im = 0 := rfl
@[simp, norm_cast] lemma of_real_one : ((1 : ℝ) : ℂ) = 1 := rfl

@[simp] theorem of_real_eq_one {z : ℝ} : (z : ℂ) = 1 ↔ z = 1 := of_real_inj
theorem of_real_ne_one {z : ℝ} : (z : ℂ) ≠ 1 ↔ z ≠ 1 := not_congr of_real_eq_one

instance : has_add ℂ := ⟨λ z w, ⟨z.re + w.re, z.im + w.im⟩⟩

@[simp] lemma add_re (z w : ℂ) : (z + w).re = z.re + w.re := rfl
@[simp] lemma add_im (z w : ℂ) : (z + w).im = z.im + w.im := rfl

@[simp] lemma bit0_re (z : ℂ) : (bit0 z).re = bit0 z.re := rfl
@[simp] lemma bit1_re (z : ℂ) : (bit1 z).re = bit1 z.re := rfl
@[simp] lemma bit0_im (z : ℂ) : (bit0 z).im = bit0 z.im := eq.refl _
@[simp] lemma bit1_im (z : ℂ) : (bit1 z).im = bit0 z.im := add_zero _

@[simp, norm_cast] lemma of_real_add (r s : ℝ) : ((r + s : ℝ) : ℂ) = r + s :=
ext_iff.2 $ by simp

@[simp, norm_cast] lemma of_real_bit0 (r : ℝ) : ((bit0 r : ℝ) : ℂ) = bit0 r :=
ext_iff.2 $ by simp [bit0]

@[simp, norm_cast] lemma of_real_bit1 (r : ℝ) : ((bit1 r : ℝ) : ℂ) = bit1 r :=
ext_iff.2 $ by simp [bit1]

instance : has_neg ℂ := ⟨λ z, ⟨-z.re, -z.im⟩⟩

@[simp] lemma neg_re (z : ℂ) : (-z).re = -z.re := rfl
@[simp] lemma neg_im (z : ℂ) : (-z).im = -z.im := rfl
@[simp, norm_cast] lemma of_real_neg (r : ℝ) : ((-r : ℝ) : ℂ) = -r := ext_iff.2 $ by simp

instance : has_sub ℂ := ⟨λ z w, ⟨z.re - w.re, z.im - w.im⟩⟩

instance : has_mul ℂ := ⟨λ z w, ⟨z.re * w.re - z.im * w.im, z.re * w.im + z.im * w.re⟩⟩

@[simp] lemma mul_re (z w : ℂ) : (z * w).re = z.re * w.re - z.im * w.im := rfl
@[simp] lemma mul_im (z w : ℂ) : (z * w).im = z.re * w.im + z.im * w.re := rfl
@[simp, norm_cast] lemma of_real_mul (r s : ℝ) : ((r * s : ℝ) : ℂ) = r * s := ext_iff.2 $ by simp

lemma of_real_mul_re (r : ℝ) (z : ℂ) : (↑r * z).re = r * z.re := by simp
lemma of_real_mul_im (r : ℝ) (z : ℂ) : (↑r * z).im = r * z.im := by simp
lemma of_real_mul' (r : ℝ) (z : ℂ) : (↑r * z) = ⟨r * z.re, r * z.im⟩ :=
ext (of_real_mul_re _ _) (of_real_mul_im _ _)

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

/-- The imaginary unit. -/
def I : ℂ := ⟨0, 1⟩

@[simp] lemma I_re : I.re = 0 := rfl
@[simp] lemma I_im : I.im = 1 := rfl

@[simp] lemma I_mul_I : I * I = -1 := ext_iff.2 $ by simp
lemma I_mul (z : ℂ) : I * z = ⟨-z.im, z.re⟩ :=
ext_iff.2 $ by simp

lemma I_ne_zero : (I : ℂ) ≠ 0 := mt (congr_arg im) zero_ne_one.symm

lemma mk_eq_add_mul_I (a b : ℝ) : complex.mk a b = a + b * I :=
ext_iff.2 $ by simp

@[simp] lemma re_add_im (z : ℂ) : (z.re : ℂ) + z.im * I = z :=
ext_iff.2 $ by simp

/-! ### 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.lean`. -/
instance : comm_ring ℂ :=
by refine_struct
  { zero := (0 : ℂ),
    add := (+),
    neg := has_neg.neg,
    sub := has_sub.sub,
    one := 1,
    mul := (*),
    zero_add := λ z, by { apply ext_iff.2, simp },
    add_zero := λ z, by { apply ext_iff.2, simp },
    nsmul := λ n z, ⟨n • z.re - 0 * z.im, n • z.im + 0 * z.re⟩,
    npow := @npow_rec _ ⟨(1 : ℂ)⟩ ⟨(*)⟩,
    zsmul := λ n z, ⟨n • z.re - 0 * z.im, n • z.im + 0 * z.re⟩ };
intros; try { refl }; apply ext_iff.2; split; simp; {ring1 <|> ring_nf}

/-- This shortcut instance ensures we do not find `ring` via the noncomputable `complex.field`
instance. -/
instance : ring ℂ := by apply_instance

/-- The "real part" map, considered as an additive group homomorphism. -/
def re_add_group_hom : ℂ →+ ℝ :=
{ to_fun := re,
  map_zero' := zero_re,
  map_add' := add_re }

@[simp] lemma coe_re_add_group_hom : (re_add_group_hom : ℂ → ℝ) = re := rfl

/-- The "imaginary part" map, considered as an additive group homomorphism. -/
def im_add_group_hom : ℂ →+ ℝ :=
{ to_fun := im,
  map_zero' := zero_im,
  map_add' := add_im }

@[simp] lemma coe_im_add_group_hom : (im_add_group_hom : ℂ → ℝ) = im := rfl

@[simp] lemma I_pow_bit0 (n : ℕ) : I ^ (bit0 n) = (-1) ^ n :=
by rw [pow_bit0', I_mul_I]

@[simp] lemma I_pow_bit1 (n : ℕ) : I ^ (bit1 n) = (-1) ^ n * I :=
by rw [pow_bit1', I_mul_I]

/-! ### Complex conjugation -/

/-- This defines the complex conjugate as the `star` operation of the `star_ring ℂ`. It
is recommended to use the ring endomorphism version `star_ring_end`, available under the
notation `conj` in the locale `complex_conjugate`. -/
instance : star_ring ℂ :=
{ 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] lemma conj_re (z : ℂ) : (conj z).re = z.re := rfl
@[simp] lemma conj_im (z : ℂ) : (conj z).im = -z.im := rfl

lemma conj_of_real (r : ℝ) : conj (r : ℂ) = r := ext_iff.2 $ by simp [conj]

@[simp] lemma conj_I : conj I = -I := ext_iff.2 $ by simp

lemma conj_bit0 (z : ℂ) : conj (bit0 z) = bit0 (conj z) := ext_iff.2 $ by simp [bit0]
lemma conj_bit1 (z : ℂ) : conj (bit1 z) = bit1 (conj z) := ext_iff.2 $ by simp [bit0]

@[simp] lemma conj_neg_I : conj (-I) = I := ext_iff.2 $ by simp

lemma eq_conj_iff_real {z : ℂ} : conj z = z ↔ ∃ r : ℝ, z = r :=
⟨λ h, ⟨z.re, ext rfl $ eq_zero_of_neg_eq (congr_arg im h)⟩,
 λ ⟨h, e⟩, by rw [e, conj_of_real]⟩

lemma eq_conj_iff_re {z : ℂ} : conj z = z ↔ (z.re : ℂ) = z :=
eq_conj_iff_real.trans ⟨by rintro ⟨r, rfl⟩; simp, λ h, ⟨_, h.symm⟩⟩

lemma eq_conj_iff_im {z : ℂ} : conj z = z ↔ z.im = 0 :=
⟨λ h, add_self_eq_zero.mp (neg_eq_iff_add_eq_zero.mp (congr_arg im h)),
  λ h, ext rfl (neg_eq_iff_add_eq_zero.mpr (add_self_eq_zero.mpr h))⟩

-- `simp_nf` complains about this being provable by `is_R_or_C.star_def` even
-- though it's not imported by this file.
@[simp, nolint simp_nf] lemma star_def : (has_star.star : ℂ → ℂ) = conj := rfl

/-! ### Norm squared -/

/-- The norm squared function. -/
@[pp_nodot] def norm_sq : ℂ →*₀ ℝ :=
{ to_fun := λ z, z.re * z.re + z.im * z.im,
  map_zero' := by simp,
  map_one' := by simp,
  map_mul' := λ z w, by { dsimp, ring } }

lemma norm_sq_apply (z : ℂ) : norm_sq z = z.re * z.re + z.im * z.im := rfl

@[simp] lemma norm_sq_of_real (r : ℝ) : norm_sq r = r * r :=
by simp [norm_sq]

@[simp] lemma norm_sq_mk (x y : ℝ) : norm_sq ⟨x, y⟩ = x * x + y * y := rfl

lemma norm_sq_add_mul_I (x y : ℝ) : norm_sq (x + y * I) = x ^ 2 + y ^ 2 :=
by rw [← mk_eq_add_mul_I, norm_sq_mk, sq, sq]

lemma norm_sq_eq_conj_mul_self {z : ℂ} : (norm_sq z : ℂ) = conj z * z :=
by { ext; simp [norm_sq, mul_comm], }

@[simp] lemma norm_sq_zero : norm_sq 0 = 0 := norm_sq.map_zero
@[simp] lemma norm_sq_one : norm_sq 1 = 1 := norm_sq.map_one
@[simp] lemma norm_sq_I : norm_sq I = 1 := by simp [norm_sq]

lemma norm_sq_nonneg (z : ℂ) : 0 ≤ norm_sq z :=
add_nonneg (mul_self_nonneg _) (mul_self_nonneg _)

lemma norm_sq_eq_zero {z : ℂ} : norm_sq z = 0 ↔ z = 0 :=
⟨λ 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),
 λ h, h.symm ▸ norm_sq_zero⟩

@[simp] lemma norm_sq_pos {z : ℂ} : 0 < norm_sq z ↔ z ≠ 0 :=
(norm_sq_nonneg z).lt_iff_ne.trans $ not_congr (eq_comm.trans norm_sq_eq_zero)

@[simp] lemma norm_sq_neg (z : ℂ) : norm_sq (-z) = norm_sq z :=
by simp [norm_sq]

@[simp] lemma norm_sq_conj (z : ℂ) : norm_sq (conj z) = norm_sq z :=
by simp [norm_sq]

lemma norm_sq_mul (z w : ℂ) : norm_sq (z * w) = norm_sq z * norm_sq w :=
norm_sq.map_mul z w

lemma norm_sq_add (z w : ℂ) : norm_sq (z + w) =
  norm_sq z + norm_sq w + 2 * (z * conj w).re :=
by dsimp [norm_sq]; ring

lemma re_sq_le_norm_sq (z : ℂ) : z.re * z.re ≤ norm_sq z :=
le_add_of_nonneg_right (mul_self_nonneg _)

lemma im_sq_le_norm_sq (z : ℂ) : z.im * z.im ≤ norm_sq z :=
le_add_of_nonneg_left (mul_self_nonneg _)

theorem mul_conj (z : ℂ) : z * conj z = norm_sq z :=
ext_iff.2 $ by simp [norm_sq, mul_comm, sub_eq_neg_add, add_comm]

theorem add_conj (z : ℂ) : z + conj z = (2 * z.re : ℝ) :=
ext_iff.2 $ by simp [two_mul]

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

@[simp] lemma of_real_eq_coe (r : ℝ) : of_real r = r := rfl

@[simp] lemma I_sq : I ^ 2 = -1 := by rw [sq, I_mul_I]

@[simp] lemma sub_re (z w : ℂ) : (z - w).re = z.re - w.re := rfl
@[simp] lemma sub_im (z w : ℂ) : (z - w).im = z.im - w.im := rfl
@[simp, norm_cast] lemma of_real_sub (r s : ℝ) : ((r - s : ℝ) : ℂ) = r - s := ext_iff.2 $ by simp
@[simp, norm_cast] lemma of_real_pow (r : ℝ) (n : ℕ) : ((r ^ n : ℝ) : ℂ) = r ^ n :=
by induction n; simp [*, of_real_mul, pow_succ]

theorem sub_conj (z : ℂ) : z - conj z = (2 * z.im : ℝ) * I :=
ext_iff.2 $ by simp [two_mul, sub_eq_add_neg]

lemma norm_sq_sub (z w : ℂ) : norm_sq (z - w) =
  norm_sq z + norm_sq w - 2 * (z * conj w).re :=
by { rw [sub_eq_add_neg, norm_sq_add],
     simp only [ring_hom.map_neg, mul_neg, neg_re,
                tactic.ring.add_neg_eq_sub, norm_sq_neg] }

/-! ### Inversion -/

noncomputable instance : has_inv ℂ := ⟨λ z, conj z * ((norm_sq z)⁻¹:ℝ)⟩

theorem inv_def (z : ℂ) : z⁻¹ = conj z * ((norm_sq z)⁻¹:ℝ) := rfl
@[simp] lemma inv_re (z : ℂ) : (z⁻¹).re = z.re / norm_sq z := by simp [inv_def, division_def]
@[simp] lemma inv_im (z : ℂ) : (z⁻¹).im = -z.im / norm_sq z := by simp [inv_def, division_def]

@[simp, norm_cast] lemma of_real_inv (r : ℝ) : ((r⁻¹ : ℝ) : ℂ) = r⁻¹ :=
ext_iff.2 $ by simp

protected

protected theorem mul_inv_cancel {z : ℂ} (h : z ≠ 0) : z * z⁻¹ = 1 :=
by rw [inv_def, ← mul_assoc, mul_conj, ← of_real_mul,
  mul_inv_cancel (mt norm_sq_eq_zero.1 h), of_real_one]

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

noncomputable instance : field ℂ :=
{ inv := has_inv.inv,
  exists_pair_ne := ⟨0, 1, mt (congr_arg re) zero_ne_one⟩,
  mul_inv_cancel := @complex.mul_inv_cancel,
  inv_zero := complex.inv_zero,
  ..complex.comm_ring }

@[simp] lemma I_zpow_bit0 (n : ℤ) : I ^ (bit0 n) = (-1) ^ n :=
by rw [zpow_bit0', I_mul_I]

@[simp] lemma I_zpow_bit1 (n : ℤ) : I ^ (bit1 n) = (-1) ^ n * I :=
by rw [zpow_bit1', I_mul_I]

lemma div_re (z w : ℂ) : (z / w).re = z.re * w.re / norm_sq w + z.im * w.im / norm_sq w :=
by simp [div_eq_mul_inv, mul_assoc, sub_eq_add_neg]
lemma div_im (z w : ℂ) : (z / w).im = z.im * w.re / norm_sq w - z.re * w.im / norm_sq w :=
by simp [div_eq_mul_inv, mul_assoc, sub_eq_add_neg, add_comm]

lemma conj_inv (x : ℂ) : conj (x⁻¹) = (conj x)⁻¹ := star_inv' _

@[simp, norm_cast] lemma of_real_div (r s : ℝ) : ((r / s : ℝ) : ℂ) = r / s :=
of_real.map_div r s

@[simp, norm_cast] lemma of_real_zpow (r : ℝ) (n : ℤ) : ((r ^ n : ℝ) : ℂ) = (r : ℂ) ^ n :=
of_real.map_zpow r n

@[simp] lemma div_I (z : ℂ) : z / I = -(z * I) :=
(div_eq_iff_mul_eq I_ne_zero).2 $ by simp [mul_assoc]

@[simp] lemma inv_I : I⁻¹ = -I :=
by simp [inv_eq_one_div]

@[simp] lemma norm_sq_inv (z : ℂ) : norm_sq z⁻¹ = (norm_sq z)⁻¹ :=
norm_sq.map_inv z

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

/-! ### Cast lemmas -/

@[simp, norm_cast] theorem of_real_nat_cast (n : ℕ) : ((n : ℝ) : ℂ) = n :=
map_nat_cast of_real n

@[simp, norm_cast] lemma nat_cast_re (n : ℕ) : (n : ℂ).re = n :=
by rw [← of_real_nat_cast, of_real_re]

@[simp, norm_cast] lemma nat_cast_im (n : ℕ) : (n : ℂ).im = 0 :=
by rw [← of_real_nat_cast, of_real_im]

@[simp, norm_cast] theorem of_real_int_cast (n : ℤ) : ((n : ℝ) : ℂ) = n :=
of_real.map_int_cast n

@[simp, norm_cast] lemma int_cast_re (n : ℤ) : (n : ℂ).re = n :=
by rw [← of_real_int_cast, of_real_re]

@[simp, norm_cast] lemma int_cast_im (n : ℤ) : (n : ℂ).im = 0 :=
by rw [← of_real_int_cast, of_real_im]

@[simp, norm_cast] theorem of_real_rat_cast (n : ℚ) : ((n : ℝ) : ℂ) = n :=
of_real.map_rat_cast n

@[simp, norm_cast] lemma rat_cast_re (q : ℚ) : (q : ℂ).re = q :=
by rw [← of_real_rat_cast, of_real_re]

@[simp, norm_cast] lemma rat_cast_im (q : ℚ) : (q : ℂ).im = 0 :=
by rw [← of_real_rat_cast, of_real_im]

/-! ### Characteristic zero -/

instance char_zero_complex : char_zero ℂ :=
char_zero_of_inj_zero $ λ n h,
by rwa [← of_real_nat_cast, of_real_eq_zero, nat.cast_eq_zero] at h

/-- 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 simp only [add_conj, of_real_mul, of_real_one, of_real_bit0,
     mul_div_cancel_left (z.re:ℂ) two_ne_zero']

/-- 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 simp only [sub_conj, of_real_mul, of_real_one, of_real_bit0, mul_right_comm,
     mul_div_cancel_left _ (mul_ne_zero two_ne_zero' I_ne_zero : 2 * I ≠ 0)]

/-! ### Absolute value -/

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

local notation `abs'` := has_abs.abs

@[simp, norm_cast] lemma abs_of_real (r : ℝ) : abs r = |r| :=
by simp [abs, norm_sq_of_real, real.sqrt_mul_self_eq_abs]

lemma abs_of_nonneg {r : ℝ} (h : 0 ≤ r) : abs r = r :=
(abs_of_real _).trans (abs_of_nonneg h)

lemma abs_of_nat (n : ℕ) : complex.abs n = n :=
calc complex.abs n = complex.abs (n:ℝ) : by rw [of_real_nat_cast]
  ... = _ : abs_of_nonneg (nat.cast_nonneg n)

lemma mul_self_abs (z : ℂ) : abs z * abs z = norm_sq z :=
real.mul_self_sqrt (norm_sq_nonneg _)

lemma sq_abs (z : ℂ) : abs z ^ 2 = norm_sq z :=
real.sq_sqrt (norm_sq_nonneg _)

@[simp] lemma sq_abs_sub_sq_re (z : ℂ) : abs z ^ 2 - z.re ^ 2 = z.im ^ 2 :=
by rw [sq_abs, norm_sq_apply, ← sq, ← sq, add_sub_cancel']

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

@[simp] lemma abs_zero : abs 0 = 0 := by simp [abs]
@[simp] lemma abs_one : abs 1 = 1 := by simp [abs]
@[simp] lemma abs_I : abs I = 1 := by simp [abs]

@[simp] lemma abs_two : abs 2 = 2 :=
calc abs 2 = abs (2 : ℝ) : by rw [of_real_bit0, of_real_one]
... = (2 : ℝ) : abs_of_nonneg (by norm_num)

lemma abs_nonneg (z : ℂ) : 0 ≤ abs z :=
real.sqrt_nonneg _

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

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

@[simp] lemma abs_conj (z : ℂ) : abs (conj z) = abs z :=
by simp [abs]

@[simp] lemma abs_mul (z w : ℂ) : abs (z * w) = abs z * abs w :=
by rw [abs, norm_sq_mul, real.sqrt_mul (norm_sq_nonneg _)]; refl

/-- `complex.abs` as a `monoid_with_zero_hom`. -/
@[simps] noncomputable def abs_hom : ℂ →*₀ ℝ :=
{ to_fun := abs,
  map_zero' := abs_zero,
  map_one' := abs_one,
  map_mul' := abs_mul }

@[simp] lemma abs_prod {ι : Type*} (s : finset ι) (f : ι → ℂ) :
  abs (s.prod f) = s.prod (λ i, abs (f i)) :=
map_prod abs_hom _ _

@[simp] lemma abs_pow (z : ℂ) (n : ℕ) : abs (z ^ n) = abs z ^ n :=
map_pow abs_hom z n

@[simp] lemma abs_zpow (z : ℂ) (n : ℤ) : abs (z ^ n) = abs z ^ n :=
abs_hom.map_zpow z n

lemma abs_re_le_abs (z : ℂ) : |z.re| ≤ abs z :=
by rw [mul_self_le_mul_self_iff (_root_.abs_nonneg z.re) (abs_nonneg _),
       abs_mul_abs_self, mul_self_abs];
   apply re_sq_le_norm_sq

lemma abs_im_le_abs (z : ℂ) : |z.im| ≤ abs z :=
by rw [mul_self_le_mul_self_iff (_root_.abs_nonneg z.im) (abs_nonneg _),
       abs_mul_abs_self, mul_self_abs];
   apply im_sq_le_norm_sq

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

lemma im_le_abs (z : ℂ) : z.im ≤ abs z :=
(abs_le.1 (abs_im_le_abs _)).2

/--
The **triangle inequality** for complex numbers.
-/
lemma abs_add (z w : ℂ) : abs (z + w) ≤ abs z + abs w :=
(mul_self_le_mul_self_iff (abs_nonneg _)
  (add_nonneg (abs_nonneg _) (abs_nonneg _))).2 $
begin
  rw [mul_self_abs, add_mul_self_eq, mul_self_abs, mul_self_abs,
      add_right_comm, norm_sq_add, add_le_add_iff_left,
      mul_assoc, mul_le_mul_left (@zero_lt_two ℝ _ _)],
  simpa [-mul_re] using re_le_abs (z * conj w)
end

instance : is_absolute_value abs :=
{ abv_nonneg  := abs_nonneg,
  abv_eq_zero := λ _, abs_eq_zero,
  abv_add     := abs_add,
  abv_mul     := abs_mul }
open is_absolute_value

@[simp] lemma abs_abs (z : ℂ) : |(abs z)| = abs z :=
_root_.abs_of_nonneg (abs_nonneg _)

@[simp] lemma abs_pos {z : ℂ} : 0 < abs z ↔ z ≠ 0 := abv_pos abs
@[simp] lemma abs_neg : ∀ z, abs (-z) = abs z := abv_neg abs
lemma abs_sub_comm : ∀ z w, abs (z - w) = abs (w - z) := abv_sub abs
lemma abs_sub_le : ∀ a b c, abs (a - c) ≤ abs (a - b) + abs (b - c) := abv_sub_le abs
@[simp] theorem abs_inv : ∀ z, abs z⁻¹ = (abs z)⁻¹ := abv_inv abs
@[simp] theorem abs_div : ∀ z w, abs (z / w) = abs z / abs w := abv_div abs

lemma abs_abs_sub_le_abs_sub : ∀ z w, |abs z - abs w| ≤ abs (z - w) :=
abs_abv_sub_le_abv_sub abs

lemma abs_le_abs_re_add_abs_im (z : ℂ) : abs z ≤ |z.re| + |z.im| :=
by simpa [re_add_im] using abs_add z.re (z.im * I)

lemma abs_re_div_abs_le_one (z : ℂ) : |z.re / z.abs| ≤ 1 :=
if hz : z = 0 then by simp [hz, zero_le_one]
else by { simp_rw [_root_.abs_div, abs_abs, div_le_iff (abs_pos.2 hz), one_mul, abs_re_le_abs] }

lemma abs_im_div_abs_le_one (z : ℂ) : |z.im / z.abs| ≤ 1 :=
if hz : z = 0 then by simp [hz, zero_le_one]
else by { simp_rw [_root_.abs_div, abs_abs, div_le_iff (abs_pos.2 hz), one_mul, abs_im_le_abs] }

@[simp, norm_cast] lemma abs_cast_nat (n : ℕ) : abs (n : ℂ) = n :=
by rw [← of_real_nat_cast, abs_of_nonneg (nat.cast_nonneg n)]

@[simp, norm_cast] lemma int_cast_abs (n : ℤ) : ↑|n| = abs n :=
by rw [← of_real_int_cast, abs_of_real, int.cast_abs]

lemma norm_sq_eq_abs (x : ℂ) : norm_sq x = abs x ^ 2 :=
by rw [abs, sq, real.mul_self_sqrt (norm_sq_nonneg _)]

/--
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 partial_order : partial_order ℂ :=
{ 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 }

section complex_order

localized "attribute [instance] complex.partial_order" in complex_order

lemma le_def {z w : ℂ} : z ≤ w ↔ z.re ≤ w.re ∧ z.im = w.im := iff.rfl
lemma lt_def {z w : ℂ} : z < w ↔ z.re < w.re ∧ z.im = w.im := iff.rfl

@[simp, norm_cast] lemma real_le_real {x y : ℝ} : (x : ℂ) ≤ (y : ℂ) ↔ x ≤ y := by simp [le_def]

@[simp, norm_cast] lemma real_lt_real {x y : ℝ} : (x : ℂ) < (y : ℂ) ↔ x < y := by simp [lt_def]

@[simp, norm_cast] lemma zero_le_real {x : ℝ} : (0 : ℂ) ≤ (x : ℂ) ↔ 0 ≤ x := real_le_real
@[simp, norm_cast] lemma zero_lt_real {x : ℝ} : (0 : ℂ) < (x : ℂ) ↔ 0 < x := real_lt_real

lemma not_le_iff {z w : ℂ} : ¬(z ≤ w) ↔ w.re < z.re ∨ z.im ≠ w.im :=
by rw [le_def, not_and_distrib, not_le]

lemma not_le_zero_iff {z : ℂ} : ¬z ≤ 0 ↔ 0 < z.re ∨ z.im ≠ 0 := not_le_iff

/--
With `z ≤ w` iff `w - z` is real and nonnegative, `ℂ` is an ordered ring.
-/
protected def ordered_comm_ring : ordered_comm_ring ℂ :=
{ zero_le_one := ⟨zero_le_one, rfl⟩,
  add_le_add_left := λ w z h y, ⟨add_le_add_left h.1 _, congr_arg2 (+) rfl h.2⟩,
  mul_pos := λ z w hz hw,
    by simp [lt_def, mul_re, mul_im, ← hz.2, ← hw.2, mul_pos hz.1 hw.1],
  .. complex.partial_order,
  .. complex.comm_ring }

localized "attribute [instance] complex.ordered_comm_ring" in complex_order

/--
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 star_ordered_ring : star_ordered_ring ℂ :=
{ nonneg_iff := λ r, by
  { refine ⟨λ hr, ⟨real.sqrt r.re, _⟩, λ 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 [of_real_im, star_def, of_real_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, of_real_im, star_def, zero_mul, conj_im,
                   mul_im, mul_zero, neg_zero] } },
    { obtain ⟨s, rfl⟩ := h,
      simp only [←norm_sq_eq_conj_mul_self, norm_sq_nonneg, zero_le_real, star_def] } },
  ..complex.ordered_comm_ring }

localized "attribute [instance] complex.star_ordered_ring" in complex_order

end complex_order

/-! ### Cauchy sequences -/

theorem is_cau_seq_re (f : cau_seq ℂ abs) : is_cau_seq abs' (λ n, (f n).re) :=
λ ε ε0, (f.cauchy ε0).imp $ λ i H j ij,
lt_of_le_of_lt (by simpa using abs_re_le_abs (f j - f i)) (H _ ij)

theorem is_cau_seq_im (f : cau_seq ℂ abs) : is_cau_seq abs' (λ n, (f n).im) :=
λ ε ε0, (f.cauchy ε0).imp $ λ i H j ij,
lt_of_le_of_lt (by simpa using abs_im_le_abs (f j - f i)) (H _ ij)

/-- The real part of a complex Cauchy sequence, as a real Cauchy sequence. -/
noncomputable def cau_seq_re (f : cau_seq ℂ abs) : cau_seq ℝ abs' :=
⟨_, is_cau_seq_re f⟩

/-- The imaginary part of a complex Cauchy sequence, as a real Cauchy sequence. -/
noncomputable def cau_seq_im (f : cau_seq ℂ abs) : cau_seq ℝ abs' :=
⟨_, is_cau_seq_im f⟩

lemma is_cau_seq_abs {f : ℕ → ℂ} (hf : is_cau_seq abs f) :
  is_cau_seq abs' (abs ∘ f) :=
λ ε ε0, let ⟨i, hi⟩ := hf ε ε0 in
⟨i, λ j hj, lt_of_le_of_lt (abs_abs_sub_le_abs_sub _ _) (hi j hj)⟩

/-- The limit of a Cauchy sequence of complex numbers. -/
noncomputable def lim_aux (f : cau_seq ℂ abs) : ℂ :=
⟨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) :=
λ ε ε0, (exists_forall_ge_and
  (cau_seq.equiv_lim ⟨_, is_cau_seq_re f⟩ _ (half_pos ε0))
  (cau_seq.equiv_lim ⟨_, is_cau_seq_im f⟩ _ (half_pos ε0))).imp $
λ i H j ij, begin
  cases H _ ij with H₁ H₂,
  apply lt_of_le_of_lt (abs_le_abs_re_add_abs_im _),
  dsimp [lim_aux] at *,
  have := add_lt_add H₁ H₂,
  rwa add_halves at this,
end

instance : cau_seq.is_complete ℂ abs :=
⟨λ f, ⟨lim_aux f, equiv_lim_aux f⟩⟩

open cau_seq

lemma lim_eq_lim_im_add_lim_re (f : cau_seq ℂ abs) : lim f =
  ↑(lim (cau_seq_re f)) + ↑(lim (cau_seq_im f)) * I :=
lim_eq_of_equiv_const $
calc f ≈ _ : equiv_lim_aux f
... = cau_seq.const abs (↑(lim (cau_seq_re f)) + ↑(lim (cau_seq_im f)) * I) :
  cau_seq.ext (λ _, complex.ext (by simp [lim_aux, cau_seq_re]) (by simp [lim_aux, cau_seq_im]))

lemma lim_re (f : cau_seq ℂ abs) : lim (cau_seq_re f) = (lim f).re :=
by rw [lim_eq_lim_im_add_lim_re]; simp

lemma lim_im (f : cau_seq ℂ abs) : lim (cau_seq_im f) = (lim f).im :=
by rw [lim_eq_lim_im_add_lim_re]; simp

lemma is_cau_seq_conj (f : cau_seq ℂ abs) : is_cau_seq abs (λ n, conj (f n)) :=
λ ε ε0, let ⟨i, hi⟩ := f.2 ε ε0 in
⟨i, λ j hj, by rw [← ring_hom.map_sub, abs_conj]; exact hi j hj⟩

/-- The complex conjugate of a complex Cauchy sequence, as a complex Cauchy sequence. -/
noncomputable def cau_seq_conj (f : cau_seq ℂ abs) : cau_seq ℂ abs :=
⟨_, is_cau_seq_conj f⟩

lemma lim_conj (f : cau_seq ℂ abs) : lim (cau_seq_conj f) = conj (lim f) :=
complex.ext (by simp [cau_seq_conj, (lim_re _).symm, cau_seq_re])
  (by simp [cau_seq_conj, (lim_im _).symm, cau_seq_im, (lim_neg _).symm]; refl)

/-- The absolute value of a complex Cauchy sequence, as a real Cauchy sequence. -/
noncomputable def cau_seq_abs (f : cau_seq ℂ abs) : cau_seq ℝ abs' :=
⟨_, is_cau_seq_abs f.2⟩

lemma lim_abs (f : cau_seq ℂ abs) : lim (cau_seq_abs f) = abs (lim f) :=
lim_eq_of_equiv_const (λ ε ε0,
let ⟨i, hi⟩ := equiv_lim f ε ε0 in
⟨i, λ j hj, lt_of_le_of_lt (abs_abs_sub_le_abs_sub _ _) (hi j hj)⟩)

@[simp, norm_cast] lemma of_real_prod {α : Type*} (s : finset α) (f : α → ℝ) :
  ((∏ i in s, f i : ℝ) : ℂ) = ∏ i in s, (f i : ℂ) :=
ring_hom.map_prod of_real _ _

@[simp, norm_cast] lemma of_real_sum {α : Type*} (s : finset α) (f : α → ℝ) :
  ((∑ i in s, f i : ℝ) : ℂ) = ∑ i in s, (f i : ℂ) :=
ring_hom.map_sum of_real _ _

end complex


import data.real.basic
import tactic

open function real

namespace vilnius

example (X Y Z : Type) (f : X → Y) (g : Y → Z)
  (hf : surjective f) (hg : surjective g) : surjective (g ∘ f) :=
begin
  sorry,
end

/- ### The λ notation:
In Lean (or in Type Theory, rather) the way to define a function is to use λ expressions: here, you
should think that `λ = ∀`: so, the function
`λ x, 3*x ^ 2 + 1`
is nothing else that the function
`f(x)=3*x ^ 2 + 1` or `f : x ↦ 3*x ^ 2 + 1`.

As for usual functions, the name of the variable does not matter, so
`λ x, 3*x ^ 2 + 1` is the same as `λ w, 3*w ^ 2 + 1`

The tactic to get rid of a `λ` term is
* `simp only` (possibly: at h)
because it "evaluates a λ-term", transforming, for instance
`(λ x, 2 * x + 1) 3` into `2 * 3 + 1`.
-/

definition A : ℕ → ℕ := λ n, n + 1

example : injective A :=
begin
  sorry,
end

example : ¬ surjective A :=
begin
  sorry,
end

-- Recall the-
definition is_linear (f : ℝ → ℝ) : Prop := ∀ c x y, f (c * x + y) = c * f (x) + f(y)

--as well as
theorem linear_at_0 (f : ℝ → ℝ) (H : is_linear f) : f 0 = 0 := sorry

-- And now a new

definition is_linear' (f : ℝ → ℝ) : Prop :=
(∀ x y, f ( x + y) = f (x) + f (y)) ∧ (∀ c x, f (c * x) = c * f (x))

example (f : ℝ → ℝ) : is_linear f ↔ is_linear' f :=
begin
  sorry,
end

--Recall the
definition is_affine (f : ℝ → ℝ) : Prop := ∃ a, ∀ x y, f (y) - f(x) = a * (y - x)

-- together with the
theorem linear_add_cnst_of_affine (f : ℝ → ℝ) : is_affine f → (∃ a : ℝ, ∃ g : ℝ → ℝ,
  (f = g + (λ x, a)) ∧ is_linear g) := sorry
-- as well as
theorem affine_of_linear_add_cnst (f : ℝ → ℝ) : (∃ b : ℝ, ∃ g : ℝ → ℝ,
  (f = g + (λ x, b)) ∧ is_linear g) → is_affine f := sorry

-- that we proved in the lesson.

example (f : ℝ → ℝ) : is_affine f ↔ ∃ a : ℝ, ∃ g : ℝ → ℝ, (f = g + (λ x, a)) ∧ is_linear g := -- iff.intro (linear_add_cnst_of_affine _) (affine_of_linear_add_cnst _)
begin
  sorry,
end

end vilnius


import Mathlib

/-!
# Jet Spaces

These consist of the value of a function at a point, and the value of its gradient at that point. Smooth functions are functions on jet spaces.
-/

noncomputable example : Field ℝ := inferInstance

namespace Jet

universe u

/-- Notation ℝ^n etc -/
instance : HPow (Type u) ℕ (Type u) := ⟨fun k n ↦ Vector k n⟩

structure Jet (n : ℕ) where
  value : ℝ
  gradient : ℝ ^ n

instance  {n : ℕ } : Add  (ℝ ^ n) := ⟨fun v₁ v₂ =>
  Vector.map₂ (· + ·) v₁ v₂⟩

#check Vector.map₂

instance addJets {n: ℕ} : Add (Jet n) :=
    ⟨fun j₁ j₂ => ⟨j₁.value + j₂.value, j₁.gradient + j₂.gradient⟩⟩

instance scMul {n : ℕ } : SMul ℝ  (ℝ ^ n) :=
  ⟨fun c v => v.map (c * ·)⟩

instance scMulJets {n : ℕ } : SMul ℝ (Jet n) :=
  ⟨fun c j => ⟨c * j.value, c • j.gradient⟩⟩

def Vector.dot {n: ℕ}(v₁ v₂ : ℝ ^ n) : ℝ :=
  (Vector.map₂ (· * ·) v₁ v₂).toList.sum

instance liebnitz {n: ℕ} : Mul (Jet n) :=
  ⟨fun j₁ j₂ => ⟨j₁.value * j₂.value, j₁.value • j₂.gradient + j₂.value • j₁.gradient⟩⟩

/-- Should be replaced by an actual definition eventually -/
opaque hasGradAt {n: ℕ} (f : ℝ ^ n → ℝ)(x : ℝ ^n) : Prop

/-- A function `ℝ^n → ℝ` with its gradient, the commented out condition should be added-/
structure SmoothFunction (n : ℕ)(m : ℕ) where
  asFunc : ℝ ^ n → ℝ ^ m
  grad : ℝ ^ n  → ℝ ^ n → ℝ ^ m
  --hasGradAt : ∀ x, hasGradAt jetMap x

instance : CoeFun (SmoothFunction n m) (fun _ => ℝ^n → ℝ^m) where
  coe := SmoothFunction.asFunc

/-- Should be proved as a theorem -/
axiom gradient_determined {n: ℕ} {m : ℕ} (f g : SmoothFunction n m) :
    f.asFunc = g.asFunc → f = g

def zeroVector {n : ℕ} : ℝ ^ n := match n with
  | 0 => Vector.nil
  | n + 1 => Vector.cons 0 (zeroVector : ℝ ^ n)

instance {n: ℕ} : Zero (ℝ ^ n) := ⟨zeroVector⟩

def consVector {n : ℕ} (c : ℝ) : ℝ ^ n := match n with
  | 0 => Vector.nil
  | n + 1 => Vector.cons c (zeroVector : ℝ ^ n)

def Jet.const (n : ℕ) (c : ℝ) : Jet n :=
  ⟨c, zeroVector⟩

def SmoothFunction.const (n : ℕ) (m : ℕ) (c : ℝ^m) : SmoothFunction n m :=
  ⟨fun _ => c, fun _ => 0⟩

def Vector.coord (i n : ℕ) : (i < n) →  ℝ ^ n :=
  fun h =>
  match i, n, h with
  | 0, k + 1, _ =>
    Vector.cons 1 (zeroVector : ℝ ^ k)
  | i + 1, k + 1, pf =>
     let tail : ℝ ^ k := Vector.coord i k (Nat.le_of_succ_le_succ pf)
     Vector.cons 0 tail

/-- The coordinate functions
-- Fix later

def SmoothFunction.coord (i n m : ℕ) (h : i < n) : SmoothFunction n m :=
  ⟨fun v : Vector (Vector ℝ m) n => v.get ⟨i, h⟩, fun _ => 0⟩
-/

-- instance : Coe  ℝ  (ℝ ^ 1) := ⟨fun c => Vector.cons c Vector.nil⟩
/- instance (l : List ℝ) (n : outParam ℕ)
         (h : outParam (l.length = n) := by rfl)
    : CoeDep (List ℝ) l (ℝ^n) where
  coe := ⟨l, h⟩ -/

instance : Coe  (ℝ ^ 1) ℝ  := ⟨fun v => v.get ⟨0, Nat.zero_lt_succ 0⟩⟩

/-- Composition with a smooth function `ℝ → ℝ` with chain rule for derivative-/

def SmoothFunction.comp {n: ℕ} {l : ℕ} {m : ℕ} (g : SmoothFunction m l) (f : SmoothFunction n m)  : SmoothFunction n l :=
  ⟨fun v => g.asFunc (f.asFunc v), fun v =>
    let g' : ℝ^m → ℝ^l := g.grad (f.asFunc v )
    let f' : ℝ^n → ℝ^m := f.grad v
    g' •  f'⟩

infix:65 " ∘ " => SmoothFunction.comp

def addVec {n : ℕ} (v1 : ℝ^n) : ℝ :=
  v1.toList.sum


import .love09_hoare_logic_demo

/-! # LoVe Exercise 9: Hoare Logic -/

set_option pp.beta true
set_option pp.generalized_field_notation false

namespace LoVe

/-! ## Question 1: Program Verification

The following WHILE program is intended to compute the Gaussian sum up to `n`,
leaving the result in `r`. -/

def GAUSS : stmt :=
stmt.assign "r" (λs, 0) ;;
stmt.while (λs, s "n" ≠ 0)
  (stmt.assign "r" (λs, s "r" + s "n") ;;
   stmt.assign "n" (λs, s "n" - 1))

/-! The summation function: -/

def sum_upto : ℕ → ℕ
| 0       := 0
| (n + 1) := n + 1 + sum_upto n

/-! 1.1. Prove the correctness of `GAUSS` using `vcg`. The main challenge is to
figure out which invariant to use for the while loop. The invariant should
capture both the work that has been done already (the intermediate result) and
the work that remains to be done. -/

lemma GAUSS_correct (n₀ : ℕ) :
  {* λs, s "n" = n₀ *} GAUSS {* λs, s "r" = sum_upto n₀ *} :=
sorry

/-! 1.2. The following WHILE program is intended to compute the product of `n`
and `m`, leaving the result in `r`. Prove its correctness using `vcg`.

Hint: If a variable `x` does not change in a program, it might be useful to
record this in the invariant, by adding a conjunct `s "x" = x₀`. -/

def MUL : stmt :=
stmt.assign "r" (λs, 0) ;;
stmt.while (λs, s "n" ≠ 0)
  (stmt.assign "r" (λs, s "r" + s "m") ;;
   stmt.assign "n" (λs, s "n" - 1))

lemma MUL_correct (n₀ m₀ : ℕ) :
  {* λs, s "n" = n₀ ∧ s "m" = m₀ *} MUL {* λs, s "r" = n₀ * m₀ *} :=
sorry

/-! ## Question 2: Hoare Triples for Total Correctness -/

def total_hoare (P : state → Prop) (S : stmt) (Q : state → Prop) : Prop :=
∀s, P s → ∃t, (S, s) ⟹ t ∧ Q t

notation `[* ` P : 1 ` *] ` S : 1 ` [* ` Q : 1 ` *]` :=
total_hoare P S Q

namespace total_hoare

/-! 2.1. Prove the consequence rule. -/

lemma consequence {P P' Q Q' : state → Prop} {S}
    (hS : [* P *] S [* Q *]) (hP : ∀s, P' s → P s) (hQ : ∀s, Q s → Q' s) :
  [* P' *] S [* Q' *] :=
sorry

/-! 2.2. Prove the rule for `skip`. -/

lemma skip_intro {P} :
  [* P *] stmt.skip [* P *] :=
sorry

/-! 2.3. Prove the rule for `assign`. -/

lemma assign_intro {P : state → Prop} {x} {a : state → ℕ} :
  [* λs, P (s{x ↦ a s}) *] stmt.assign x a [* P *] :=
sorry

/-! 2.4. Prove the rule for `seq`. -/

lemma seq_intro {P Q R S T} (hS : [* P *] S [* Q *]) (hT : [* Q *] T [* R *]) :
  [* P *] S ;; T [* R *] :=
sorry

/-! 2.5. Complete the proof of the rule for `ite`.

Hint: This requires a case distinction on the truth value of `b s`. -/

lemma ite_intro {b P Q : state → Prop} {S T}
    (hS : [* λs, P s ∧ b s *] S [* Q *])
    (hT : [* λs, P s ∧ ¬ b s *] T [* Q *]) :
  [* P *] stmt.ite b S T [* Q *] :=
sorry

/-! 2.6 (**optional**). Try to prove the rule for `while`.

The rule is parameterized by a loop invariant `I` and by a variant `V` that
decreases with each iteration of the loop body.

Before we prove the desired lemma, we introduce an auxiliary lemma. Its proof
requires well-founded induction. When using `while_var_intro_aux` as induction
hypothesis we recommend to do it directly after proving that the argument is
less than `v₀`:

    have ih : ∃u, (stmt.while b S, t) ⟹ u ∧ I u ∧ ¬ b u :=
      have V t < v₀ :=
        …,
      while_var_intro_aux (V t) …,

Similarly to `ite`, the proof requires a case distinction on `b s ∨ ¬ b s`. -/

lemma while_var_intro_aux {b : state → Prop} (I : state → Prop) (V : state → ℕ)
  {S} (h_inv : ∀v₀, [* λs, I s ∧ b s ∧ V s = v₀ *] S [* λs, I s ∧ V s < v₀ *]) :
  ∀v₀ s, V s = v₀ → I s → ∃t, (stmt.while b S, s) ⟹ t ∧ I t ∧ ¬ b t
| v₀ s V_eq hs :=
sorry

lemma while_var_intro {b : state → Prop} (I : state → Prop) (V : state → ℕ) {S}
  (hinv : ∀v₀, [* λs, I s ∧ b s ∧ V s = v₀ *] S [* λs, I s ∧ V s < v₀ *]) :
  [* I *] stmt.while b S [* λs, I s ∧ ¬ b s *] :=
sorry

end total_hoare

end LoVe


-- Las_relaciones_definidas_por_particiones_son_reflexivas.lean
-- Las relaciones definidas por particiones son reflexivas
-- José A. Alonso Jiménez
-- Sevilla, 9 de octubre de 2021
-- ---------------------------------------------------------------------

-- ---------------------------------------------------------------------
-- Para cada partición se define una relación de forma que un par de
-- elementos están relacionados si pertenecen al mismo bloque de la
-- partición. En Lena,
--    def relacion : (particion A) → (A → A → Prop) :=
--      λ P a b, ∀ X ∈ Bloques P, a ∈ X → b ∈ X
--
-- Demostrar que la relación definida por la partición P es reflexiva.
-- ---------------------------------------------------------------------

import tactic

@[ext] structure particion (A : Type) :=
(Bloques    : set (set A))
(Hno_vacios : ∀ X ∈ Bloques, (X : set A).nonempty)
(Hrecubren  : ∀ a, ∃ X ∈ Bloques, a ∈ X)
(Hdisjuntos : ∀ X Y ∈ Bloques, (X ∩ Y : set A).nonempty → X = Y)

namespace particion

variable  {A : Type}

def relacion : (particion A) → (A → A → Prop) :=
  λ P a b, ∀ X ∈ Bloques P, a ∈ X → b ∈ X

-- 1ª demostración
example
  (P : particion A)
  : reflexive (relacion P) :=
begin
  rw reflexive,
  intro a,
  unfold relacion,
  intros X hXC haX,
  exact haX,
end

-- 2ª demostración
example
  (P : particion A)
  : reflexive (relacion P) :=
begin
  intro a,
  intros X hXC haX,
  exact haX,
end

-- 3ª demostración
example
  (P : particion A)
  : reflexive (relacion P) :=
begin
  intros a X hXC haX,
  exact haX,
end

-- 4ª demostración
lemma reflexiva
  (P : particion A)
  : reflexive (relacion P) :=
λ a X hXC haX, haX

end particion