/-
Copyright (c) 2021 Oliver Nash. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Oliver Nash
-/
import algebra.lie.of_associative
import linear_algebra.matrix

/-!
# Lie algebras of matrices

An important class of Lie algebras are those arising from the associative algebra structure on
square matrices over a commutative ring. This file provides some very basic definitions whose
primary value stems from their utility when constructing the classical Lie algebras using matrices.

## Main definitions

  * `lie_equiv_matrix'`
  * `matrix.lie_conj`
  * `matrix.reindex_lie_equiv`

## Tags

lie algebra, matrix
-/

universes u v w w₁ w₂

section matrices
open_locale matrix

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

/-- The natural equivalence between linear endomorphisms of finite free modules and square matrices
is compatible with the Lie algebra structures. -/
def lie_equiv_matrix' : module.End R (n → R) ≃ₗ⁅R⁆ matrix n n R :=
{ map_lie' := λ T S,
  begin
    let f := @linear_map.to_matrix' R _ n n _ _ _,
    change f (T.comp S - S.comp T) = (f T) * (f S) - (f S) * (f T),
    have h : ∀ (T S : module.End R _), f (T.comp S) = (f T) ⬝ (f S) := linear_map.to_matrix'_comp,
    rw [linear_equiv.map_sub, h, h, matrix.mul_eq_mul, matrix.mul_eq_mul],
  end,
  ..linear_map.to_matrix' }

@[simp] lemma lie_equiv_matrix'_apply (f : module.End R (n → R)) :
  lie_equiv_matrix' f = f.to_matrix' := rfl

@[simp] lemma lie_equiv_matrix'_symm_apply (A : matrix n n R) :
  (@lie_equiv_matrix' R _ n _ _).symm A = A.to_lin' := rfl

/-- An invertible matrix induces a Lie algebra equivalence from the space of matrices to itself. -/
noncomputable def matrix.lie_conj (P : matrix n n R) (h : is_unit P) :
  matrix n n R ≃ₗ⁅R⁆ matrix n n R :=
((@lie_equiv_matrix' R _ n _ _).symm.trans (P.to_linear_equiv' h).lie_conj).trans lie_equiv_matrix'

@[simp] lemma matrix.lie_conj_apply (P A : matrix n n R) (h : is_unit P) :
  P.lie_conj h A = P ⬝ A ⬝ P⁻¹ :=
by simp [linear_equiv.conj_apply, matrix.lie_conj, linear_map.to_matrix'_comp,
         linear_map.to_matrix'_to_lin']

@[simp] lemma matrix.lie_conj_symm_apply (P A : matrix n n R) (h : is_unit P) :
  (P.lie_conj h).symm A = P⁻¹ ⬝ A ⬝ P :=
by simp [linear_equiv.symm_conj_apply, matrix.lie_conj, linear_map.to_matrix'_comp,
         linear_map.to_matrix'_to_lin']

/-- For square matrices, the natural map that reindexes a matrix's rows and columns with equivalent
types, `matrix.reindex`, is an equivalence of Lie algebras. -/
def matrix.reindex_lie_equiv {m : Type w₁} [decidable_eq m] [fintype m]
  (e : n ≃ m) : matrix n n R ≃ₗ⁅R⁆ matrix m m R :=
{ to_fun := matrix.reindex e e,
  map_lie' := λ M N, by simp only [lie_ring.of_associative_ring_bracket, matrix.reindex_apply,
    ←matrix.minor_mul_equiv _ _ _ _, matrix.mul_eq_mul, matrix.minor_sub, pi.sub_apply],
  ..(matrix.reindex_linear_equiv e e) }

@[simp] lemma matrix.reindex_lie_equiv_apply {m : Type w₁} [decidable_eq m] [fintype m]
  (e : n ≃ m) (M : matrix n n R) :
  matrix.reindex_lie_equiv e M = matrix.reindex e e M :=
rfl

@[simp] lemma matrix.reindex_lie_equiv_symm {m : Type w₁} [decidable_eq m] [fintype m]
  (e : n ≃ m) :
  (matrix.reindex_lie_equiv e : _ ≃ₗ⁅R⁆ _).symm = matrix.reindex_lie_equiv e.symm :=
rfl

end matrices


/-
Copyright (c) 2017 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl, Yaël Dillies
-/
import order.complete_lattice

/-!
# Frames, completely distributive lattices and Boolean algebras

In this file we define and provide API for frames, completely distributive lattices and completely
distributive Boolean algebras.

## Typeclasses

* `order.frame`: Frame: A complete lattice whose `⊓` distributes over `⨆`.
* `order.coframe`: Coframe: A complete lattice whose `⊔` distributes over `⨅`.
* `complete_distrib_lattice`: Completely distributive lattices: A complete lattice whose `⊓` and `⊔`
  distribute over `⨆` and `⨅` respectively.
* `complete_boolean_algebra`: Completely distributive Boolean algebra: A Boolean algebra whose `⊓`
  and `⊔` distribute over `⨆` and `⨅` respectively.

A set of opens gives rise to a topological space precisely if it forms a frame. Such a frame is also
completely distributive, but not all frames are. `filter` is a coframe but not a completely
distributive lattice.

## TODO

Add instances for `prod`

## References

* [Wikipedia, *Complete Heyting algebra*](https://en.wikipedia.org/wiki/Complete_Heyting_algebra)
* [Francis Borceux, *Handbook of Categorical Algebra III*][borceux-vol3]
-/

set_option old_structure_cmd true

open function set

universes u v w
variables {α : Type u} {β : Type v} {ι : Sort w} {κ : ι → Sort*}

/-- A frame, aka complete Heyting algebra, is a complete lattice whose `⊓` distributes over `⨆`. -/
class order.frame (α : Type*) extends complete_lattice α :=
(inf_Sup_le_supr_inf (a : α) (s : set α) : a ⊓ Sup s ≤ ⨆ b ∈ s, a ⊓ b)

/-- A coframe, aka complete Brouwer algebra or complete co-Heyting algebra, is a complete lattice
whose `⊔` distributes over `⨅`. -/
class order.coframe (α : Type*) extends complete_lattice α :=
(infi_sup_le_sup_Inf (a : α) (s : set α) : (⨅ b ∈ s, a ⊔ b) ≤ a ⊔ Inf s)

open order

/-- A completely distributive lattice is a complete lattice whose `⊔` and `⊓` respectively
distribute over `⨅` and `⨆`. -/
class complete_distrib_lattice (α : Type*) extends frame α :=
(infi_sup_le_sup_Inf : ∀ a s, (⨅ b ∈ s, a ⊔ b) ≤ a ⊔ Inf s)

@[priority 100] -- See note [lower instance priority]
instance complete_distrib_lattice.to_coframe [complete_distrib_lattice α] : coframe α :=
{ .. ‹complete_distrib_lattice α› }

section frame
variables [frame α] {s t : set α} {a b : α}

instance order_dual.coframe : coframe (order_dual α) :=
{ infi_sup_le_sup_Inf := frame.inf_Sup_le_supr_inf, ..order_dual.complete_lattice α }

lemma inf_Sup_eq : a ⊓ Sup s = ⨆ b ∈ s, a ⊓ b :=
(frame.inf_Sup_le_supr_inf _ _).antisymm supr_inf_le_inf_Sup

lemma Sup_inf_eq : Sup s ⊓ b = ⨆ a ∈ s, a ⊓ b :=
by simpa only [inf_comm] using @inf_Sup_eq α _ s b

lemma supr_inf_eq (f : ι → α) (a : α) : (⨆ i, f i) ⊓ a = ⨆ i, f i ⊓ a :=
by rw [supr, Sup_inf_eq, supr_range]

lemma inf_supr_eq (a : α) (f : ι → α) : a ⊓ (⨆ i, f i) = ⨆ i, a ⊓ f i :=
by simpa only [inf_comm] using supr_inf_eq f a

lemma bsupr_inf_eq {f : Π i, κ i → α} (a : α) : (⨆ i j, f i j) ⊓ a = ⨆ i j, f i j ⊓ a :=
by simp only [supr_inf_eq]

lemma inf_bsupr_eq {f : Π i, κ i → α} (a : α) : a ⊓ (⨆ i j, f i j) = ⨆ i j, a ⊓ f i j :=
by simp only [inf_supr_eq]

lemma supr_inf_supr {ι ι' : Type*} {f : ι → α} {g : ι' → α} :
  (⨆ i, f i) ⊓ (⨆ j, g j) = ⨆ i : ι × ι', f i.1 ⊓ g i.2 :=
by simp only [inf_supr_eq, supr_inf_eq, supr_prod]

lemma bsupr_inf_bsupr {ι ι' : Type*} {f : ι → α} {g : ι' → α} {s : set ι} {t : set ι'} :
  (⨆ i ∈ s, f i) ⊓ (⨆ j ∈ t, g j) = ⨆ p ∈ s ×ˢ t, f (p : ι × ι').1 ⊓ g p.2 :=
begin
  simp only [supr_subtype', supr_inf_supr],
  exact (equiv.surjective _).supr_congr (equiv.set.prod s t).symm (λ x, rfl)
end

lemma Sup_inf_Sup : Sup s ⊓ Sup t = ⨆ p ∈ s ×ˢ t, (p : α × α).1 ⊓ p.2 :=
by simp only [Sup_eq_supr, bsupr_inf_bsupr]

lemma supr_disjoint_iff {f : ι → α} : disjoint (⨆ i, f i) a ↔ ∀ i, disjoint (f i) a :=
by simp only [disjoint_iff, supr_inf_eq, supr_eq_bot]

lemma disjoint_supr_iff {f : ι → α} : disjoint a (⨆ i, f i) ↔ ∀ i, disjoint a (f i) :=
by simpa only [disjoint.comm] using supr_disjoint_iff

lemma Sup_disjoint_iff {s : set α} : disjoint (Sup s) a ↔ ∀ b ∈ s, disjoint b a :=
by simp only [disjoint_iff, Sup_inf_eq, supr_eq_bot]

lemma disjoint_Sup_iff {s : set α} : disjoint a (Sup s) ↔ ∀ b ∈ s, disjoint a b :=
by simpa only [disjoint.comm] using Sup_disjoint_iff

instance pi.frame {ι : Type*} {π : ι → Type*} [Π i, frame (π i)] : frame (Π i, π i) :=
{ inf_Sup_le_supr_inf := λ a s i,
    by simp only [complete_lattice.Sup, Sup_apply, supr_apply, pi.inf_apply, inf_supr_eq,
      ← supr_subtype''],
  ..pi.complete_lattice }

end frame

section coframe
variables [coframe α] {s t : set α} {a b : α}

instance order_dual.frame : frame (order_dual α) :=
{ inf_Sup_le_supr_inf := coframe.infi_sup_le_sup_Inf, ..order_dual.complete_lattice α }

theorem sup_Inf_eq : a ⊔ Inf s = (⨅ b ∈ s, a ⊔ b) :=
@inf_Sup_eq (order_dual α) _ _ _

theorem Inf_sup_eq : Inf s ⊔ b = (⨅ a ∈ s, a ⊔ b) :=
@Sup_inf_eq (order_dual α) _ _ _

theorem infi_sup_eq (f : ι → α) (a : α) : (⨅ i, f i) ⊔ a = ⨅ i, f i ⊔ a :=
@supr_inf_eq (order_dual α) _ _ _ _

theorem sup_infi_eq (a : α) (f : ι → α) : a ⊔ (⨅ i, f i) = ⨅ i, a ⊔ f i :=
@inf_supr_eq (order_dual α) _ _ _ _

lemma binfi_sup_eq {f : Π i, κ i → α} (a : α) : (⨅ i j, f i j) ⊔ a = ⨅ i j, f i j ⊔ a :=
@bsupr_inf_eq (order_dual α) _ _ _ _ _

lemma sup_binfi_eq {f : Π i, κ i → α} (a : α) : a ⊔ (⨅ i j, f i j) = ⨅ i j, a ⊔ f i j :=
@inf_bsupr_eq (order_dual α) _ _ _ _ _

lemma infi_sup_infi {ι ι' : Type*} {f : ι → α} {g : ι' → α} :
  (⨅ i, f i) ⊔ (⨅ i, g i) = ⨅ i : ι × ι', f i.1 ⊔ g i.2 :=
@supr_inf_supr (order_dual α) _ _ _ _ _

lemma binfi_sup_binfi {ι ι' : Type*} {f : ι → α} {g : ι' → α} {s : set ι} {t : set ι'} :
  (⨅ i ∈ s, f i) ⊔ (⨅ j ∈ t, g j) = ⨅ p ∈ s ×ˢ t, f (p : ι × ι').1 ⊔ g p.2 :=
@bsupr_inf_bsupr (order_dual α) _ _ _ _ _ _ _

theorem Inf_sup_Inf : Inf s ⊔ Inf t = (⨅ p ∈ s ×ˢ t, (p : α × α).1 ⊔ p.2) :=
@Sup_inf_Sup (order_dual α) _ _ _

instance pi.coframe {ι : Type*} {π : ι → Type*} [Π i, coframe (π i)] : coframe (Π i, π i) :=
{ Inf := Inf,
  infi_sup_le_sup_Inf := λ a s i,
    by simp only [←sup_infi_eq, Inf_apply, ←infi_subtype'', infi_apply, pi.sup_apply],
  ..pi.complete_lattice }

end coframe

section complete_distrib_lattice
variables [complete_distrib_lattice α] {a b : α} {s t : set α}

instance : complete_distrib_lattice (order_dual α) := { ..order_dual.frame, ..order_dual.coframe }

instance pi.complete_distrib_lattice {ι : Type*} {π : ι → Type*}
  [Π i, complete_distrib_lattice (π i)] : complete_distrib_lattice (Π i, π i) :=
{ ..pi.frame, ..pi.coframe }

end complete_distrib_lattice

@[priority 100] -- see Note [lower instance priority]
instance complete_distrib_lattice.to_distrib_lattice [d : complete_distrib_lattice α] :
  distrib_lattice α :=
{ le_sup_inf := λ x y z, by rw [← Inf_pair, ← Inf_pair, sup_Inf_eq, ← Inf_image, set.image_pair],
  ..d }

/-- A complete Boolean algebra is a completely distributive Boolean algebra. -/
class complete_boolean_algebra α extends boolean_algebra α, complete_distrib_lattice α

instance pi.complete_boolean_algebra {ι : Type*} {π : ι → Type*}
  [∀ i, complete_boolean_algebra (π i)] : complete_boolean_algebra (Π i, π i) :=
{ .. pi.boolean_algebra, .. pi.complete_distrib_lattice }

instance Prop.complete_boolean_algebra : complete_boolean_algebra Prop :=
{ infi_sup_le_sup_Inf := λ p s, iff.mp $
    by simp only [forall_or_distrib_left, complete_lattice.Inf, infi_Prop_eq, sup_Prop_eq],
  inf_Sup_le_supr_inf := λ p s, iff.mp $
    by simp only [complete_lattice.Sup, exists_and_distrib_left, inf_Prop_eq, supr_Prop_eq],
  .. Prop.boolean_algebra, .. Prop.complete_lattice }

section complete_boolean_algebra
variables [complete_boolean_algebra α] {a b : α} {s : set α} {f : ι → α}

theorem compl_infi : (infi f)ᶜ = (⨆ i, (f i)ᶜ) :=
le_antisymm
  (compl_le_of_compl_le $ le_infi $ λ i, compl_le_of_compl_le $ le_supr (compl ∘ f) i)
  (supr_le $ λ i, compl_le_compl $ infi_le _ _)

theorem compl_supr : (supr f)ᶜ = (⨅ i, (f i)ᶜ) :=
compl_injective (by simp [compl_infi])

lemma compl_Inf : (Inf s)ᶜ = (⨆ i ∈ s, iᶜ) := by simp only [Inf_eq_infi, compl_infi]
lemma compl_Sup : (Sup s)ᶜ = (⨅ i ∈ s, iᶜ) := by simp only [Sup_eq_supr, compl_supr]
lemma compl_Inf' : (Inf s)ᶜ = Sup (compl '' s) := compl_Inf.trans Sup_image.symm
lemma compl_Sup' : (Sup s)ᶜ = Inf (compl '' s) := compl_Sup.trans Inf_image.symm

end complete_boolean_algebra

section lift

/-- Pullback an `order.frame` along an injection. -/
@[reducible] -- See note [reducible non-instances]
protected def function.injective.frame [has_sup α] [has_inf α] [has_Sup α] [has_Inf α] [has_top α]
  [has_bot α] [frame β] (f : α → β) (hf : injective f) (map_sup : ∀ a b, f (a ⊔ b) = f a ⊔ f b)
  (map_inf : ∀ a b, f (a ⊓ b) = f a ⊓ f b) (map_Sup : ∀ s, f (Sup s) = ⨆ a ∈ s, f a)
  (map_Inf : ∀ s, f (Inf s) = ⨅ a ∈ s, f a) (map_top : f ⊤ = ⊤) (map_bot : f ⊥ = ⊥) :
  frame α :=
{ inf_Sup_le_supr_inf := λ a s, begin
    change f (a ⊓ Sup s) ≤ f _,
    rw [←Sup_image, map_inf, map_Sup s, inf_bsupr_eq],
    simp_rw ←map_inf,
    exact ((map_Sup _).trans supr_image).ge,
  end,
  ..hf.complete_lattice f map_sup map_inf map_Sup map_Inf map_top map_bot }

/-- Pullback an `order.coframe` along an injection. -/
@[reducible] -- See note [reducible non-instances]
protected def function.injective.coframe [has_sup α] [has_inf α] [has_Sup α] [has_Inf α] [has_top α]
  [has_bot α] [coframe β] (f : α → β) (hf : injective f) (map_sup : ∀ a b, f (a ⊔ b) = f a ⊔ f b)
  (map_inf : ∀ a b, f (a ⊓ b) = f a ⊓ f b) (map_Sup : ∀ s, f (Sup s) = ⨆ a ∈ s, f a)
  (map_Inf : ∀ s, f (Inf s) = ⨅ a ∈ s, f a) (map_top : f ⊤ = ⊤) (map_bot : f ⊥ = ⊥) :
  coframe α :=
{ infi_sup_le_sup_Inf := λ a s, begin
    change f _ ≤ f (a ⊔ Inf s),
    rw [←Inf_image, map_sup, map_Inf s, sup_binfi_eq],
    simp_rw ←map_sup,
    exact ((map_Inf _).trans infi_image).le,
  end,
  ..hf.complete_lattice f map_sup map_inf map_Sup map_Inf map_top map_bot }

/-- Pullback a `complete_distrib_lattice` along an injection. -/
@[reducible] -- See note [reducible non-instances]
protected def function.injective.complete_distrib_lattice [has_sup α] [has_inf α] [has_Sup α]
  [has_Inf α] [has_top α] [has_bot α] [complete_distrib_lattice β]
  (f : α → β) (hf : function.injective f) (map_sup : ∀ a b, f (a ⊔ b) = f a ⊔ f b)
  (map_inf : ∀ a b, f (a ⊓ b) = f a ⊓ f b) (map_Sup : ∀ s, f (Sup s) = ⨆ a ∈ s, f a)
  (map_Inf : ∀ s, f (Inf s) = ⨅ a ∈ s, f a) (map_top : f ⊤ = ⊤) (map_bot : f ⊥ = ⊥) :
  complete_distrib_lattice α :=
{ ..hf.frame f map_sup map_inf map_Sup map_Inf map_top map_bot,
  ..hf.coframe f map_sup map_inf map_Sup map_Inf map_top map_bot }

/-- Pullback a `complete_boolean_algebra` along an injection. -/
@[reducible] -- See note [reducible non-instances]
protected def function.injective.complete_boolean_algebra [has_sup α] [has_inf α] [has_Sup α]
  [has_Inf α] [has_top α] [has_bot α] [has_compl α] [has_sdiff α] [complete_boolean_algebra β]
  (f : α → β) (hf : function.injective f) (map_sup : ∀ a b, f (a ⊔ b) = f a ⊔ f b)
  (map_inf : ∀ a b, f (a ⊓ b) = f a ⊓ f b) (map_Sup : ∀ s, f (Sup s) = ⨆ a ∈ s, f a)
  (map_Inf : ∀ s, f (Inf s) = ⨅ a ∈ s, f a) (map_top : f ⊤ = ⊤) (map_bot : f ⊥ = ⊥)
  (map_compl : ∀ a, f aᶜ = (f a)ᶜ) (map_sdiff : ∀ a b, f (a \ b) = f a \ f b) :
  complete_boolean_algebra α :=
{ ..hf.complete_distrib_lattice f map_sup map_inf map_Sup map_Inf map_top map_bot,
  ..hf.boolean_algebra f map_sup map_inf map_top map_bot map_compl map_sdiff }

end lift


-- Copyright (c) Microsoft Corporation. All rights reserved.
-- Licensed under the MIT license.

def size := { n:nat // 0 < n }

namespace size

instance size_to_nat: has_coe size nat :=
⟨λ s, s.1⟩

instance : has_to_string size :=
⟨λ s, to_string s.1⟩

@[pattern, reducible] def one : size := ⟨1, dec_trivial⟩

@[reducible] def add (sz1:size) (sz2:size):size :=
⟨sz1.1 + sz2.1, nat.lt_trans sz1.2 -- 0 < sz -> sz < sz+sz2 -> 0 < sz+sz2
    (nat.lt_add_of_pos_right sz2.2)⟩
instance : has_add size := ⟨add⟩

@[reducible] def incr (sz:size):size := sz.add size.one

def incr_nat (sz:nat):size := ⟨nat.succ sz, dec_trivial⟩

@[reducible] def sub (sz1:size) (sz2:size) (prf:sz1.1 > sz2.1):size :=
⟨sz1.1 - sz2.1, nat.sub_pos_of_lt prf⟩

theorem add_sub_eq (sz1 sz2:size) (prf:sz1.1 > sz2.1) :
  (sz2.add (sz1.sub sz2 prf)) = sz1 :=
begin
  apply subtype.eq, simp,
  rw nat.add_sub_of_le (nat.le_of_succ_le prf)
end

theorem add_sub_eq2 (sz1 sz2:size) (prf:(sz1.add sz2).val > sz2.val) :
  ((sz1.add sz2).sub sz2 prf) = sz1 :=
begin
  apply subtype.eq, simp, apply nat.add_sub_cancel
end

theorem add_gt (sz m:size):
  (sz.add m).val > sz.val :=
begin
  cases sz,
  cases m,
  simp, apply lt_add_of_pos_right, assumption
end

theorem add_gt2 (sz m:size):
  (sz.add m).val > m.val :=
begin
  cases sz,
  cases m,
  simp, rw nat.add_comm, apply lt_add_of_pos_right, assumption
end

theorem add_one_incr_eq (sz1:size) :
  (sz1.incr) = (sz1.add size.one) :=
begin apply subtype.eq, simp end

end size

@[reducible] def within_signed_range (sz:size) (x:ℤ) : Prop :=
  (-int.of_nat (2 ^ (sz.val - 1)) ≤ x) ∧ (x < int.of_nat (2 ^ (sz.val - 1)))

def all_one_nat (sz:size) : ℕ := (2 ^ sz.val) - 1
def int_min_nat (sz:size) : ℕ := 2 ^ (sz.val - 1)


import data.set

variables X Y: Type

def surjective {X: Type} {Y: Type} (f : X → Y) : Prop := ∀ y, ∃ x, f x = y

theorem Cantor : ∀ (A: set X), ¬ ∃ (f: A → set A),  surjective f :=

    begin
        intro A,
        intro h,
        cases h with f h1,
        have h2: ∃ x, f x = {t : A | ¬ (t ∈ f t)}, from h1 {t : A | ¬ (t ∈ f t)},
        cases h2 with x h3,
        apply or.elim (classical.em (x ∈ {t : A | ¬ (t ∈ f t)})),
            intro h4,
                have h5: ¬ (x ∈ f x), from h4,
                rw (eq.symm h3) at h4,
                apply h5 h4,
            intro h4,
                rw (eq.symm h3) at h4,
                have h5: x ∈ {t : A | ¬ (t ∈ f t)}, from h4,
                rw (eq.symm h3) at h5,
                apply h4 h5,
    end


import data.set data.int.basic data.set
open function int set

section
  def g (x : ℤ) : ℤ := -x
  def h (x : ℤ) : ℤ := 2 * x + 3

  example : injective h :=
    assume x₁ x₂,
    assume :2 * x₁ + 3 = 2 * x₂ + 3,
    have 2 * x₁ = 2 * x₂, from add_right_cancel this,
    show x₁ = x₂, from mul_left_cancel' (dec_trivial) this

  example : surjective g :=
    assume y,
    have g (-y) = y, from calc
      g (-y) = -(-y): rfl
      ... = y: neg_neg y,
    exists.intro (-y) this

  example (A B : Type) (u : A → B) (v1 : B → A) (v2 : B → A)
    (h1 : left_inverse v1 u) (h2 : right_inverse v2 u) : v1 = v2 :=
  funext
    (assume x,
      calc
        v1 x = v1 (u (v2 x)) : by rw h2
        ... = v2 x          : by rw h1)
end

section
  variables {X Y : Type}
  variable  f : X → Y
  variables A B : set X

  example : f '' (A ∩ B) ⊆ f '' A ∩ f '' B :=
  assume y,
  assume h1 : y ∈ f '' (A ∩ B),
  show y ∈ f '' A ∩ f '' B, from
    exists.elim h1 $
      assume x h,
      have feq: f x = y, from and.right h,
      have x ∈ A ∩ B, from and.left h,
      have x ∈ A, from and.left ‹x ∈ A ∩ B›,
      have y ∈ f '' A, from exists.intro x $ ⟨this, feq⟩,
      have x ∈ B, from and.right ‹x ∈ A ∩ B›,
      have y ∈ f '' B, from exists.intro x $ ⟨this, feq⟩,
      ⟨‹y ∈ f '' A›, ‹y ∈ f '' B›⟩
end


/-
Copyright (c) 2021 Rémy Degenne. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Rémy Degenne, Kexing Ying
-/
import probability.notation
import probability.process.stopping

/-!
# Martingales

A family of functions `f : ι → Ω → E` is a martingale with respect to a filtration `ℱ` if every
`f i` is integrable, `f` is adapted with respect to `ℱ` and for all `i ≤ j`,
`μ[f j | ℱ i] =ᵐ[μ] f i`. On the other hand, `f : ι → Ω → E` is said to be a supermartingale
with respect to the filtration `ℱ` if `f i` is integrable, `f` is adapted with resepct to `ℱ`
and for all `i ≤ j`, `μ[f j | ℱ i] ≤ᵐ[μ] f i`. Finally, `f : ι → Ω → E` is said to be a
submartingale with respect to the filtration `ℱ` if `f i` is integrable, `f` is adapted with
resepct to `ℱ` and for all `i ≤ j`, `f i ≤ᵐ[μ] μ[f j | ℱ i]`.

The definitions of filtration and adapted can be found in `probability.stopping`.

### Definitions

* `measure_theory.martingale f ℱ μ`: `f` is a martingale with respect to filtration `ℱ` and
  measure `μ`.
* `measure_theory.supermartingale f ℱ μ`: `f` is a supermartingale with respect to
  filtration `ℱ` and measure `μ`.
* `measure_theory.submartingale f ℱ μ`: `f` is a submartingale with respect to filtration `ℱ` and
  measure `μ`.

### Results

* `measure_theory.martingale_condexp f ℱ μ`: the sequence `λ i, μ[f | ℱ i, ℱ.le i])` is a
  martingale with respect to `ℱ` and `μ`.

-/

open topological_space filter
open_locale nnreal ennreal measure_theory probability_theory big_operators

namespace measure_theory

variables {Ω E ι : Type*} [preorder ι]
  {m0 : measurable_space Ω} {μ : measure Ω}
  [normed_add_comm_group E] [normed_space ℝ E] [complete_space E]
  {f g : ι → Ω → E} {ℱ : filtration ι m0}

/-- A family of functions `f : ι → Ω → E` is a martingale with respect to a filtration `ℱ` if `f`
is adapted with respect to `ℱ` and for all `i ≤ j`, `μ[f j | ℱ i] =ᵐ[μ] f i`. -/
def martingale (f : ι → Ω → E) (ℱ : filtration ι m0) (μ : measure Ω . volume_tac) : Prop :=
adapted ℱ f ∧ ∀ i j, i ≤ j → μ[f j | ℱ i] =ᵐ[μ] f i

/-- A family of integrable functions `f : ι → Ω → E` is a supermartingale with respect to a
filtration `ℱ` if `f` is adapted with respect to `ℱ` and for all `i ≤ j`,
`μ[f j | ℱ.le i] ≤ᵐ[μ] f i`. -/
def supermartingale [has_le E] (f : ι → Ω → E) (ℱ : filtration ι m0) (μ : measure Ω . volume_tac) :
  Prop :=
adapted ℱ f ∧ (∀ i j, i ≤ j → μ[f j | ℱ i] ≤ᵐ[μ] f i) ∧ ∀ i, integrable (f i) μ

/-- A family of integrable functions `f : ι → Ω → E` is a submartingale with respect to a
filtration `ℱ` if `f` is adapted with respect to `ℱ` and for all `i ≤ j`,
`f i ≤ᵐ[μ] μ[f j | ℱ.le i]`. -/
def submartingale [has_le E] (f : ι → Ω → E) (ℱ : filtration ι m0) (μ : measure Ω . volume_tac) :
  Prop :=
adapted ℱ f ∧ (∀ i j, i ≤ j → f i ≤ᵐ[μ] μ[f j | ℱ i]) ∧ ∀ i, integrable (f i) μ

lemma martingale_const (ℱ : filtration ι m0) (μ : measure Ω) [is_finite_measure μ] (x : E) :
  martingale (λ _ _, x) ℱ μ :=
⟨adapted_const ℱ _, λ i j hij, by rw condexp_const (ℱ.le _)⟩

lemma martingale_const_fun [order_bot ι]
  (ℱ : filtration ι m0) (μ : measure Ω) [is_finite_measure μ]
  {f : Ω → E} (hf : strongly_measurable[ℱ ⊥] f) (hfint : integrable f μ) :
  martingale (λ _, f) ℱ μ :=
begin
  refine ⟨λ i, hf.mono $ ℱ.mono bot_le, λ i j hij, _⟩,
  rw condexp_of_strongly_measurable (ℱ.le _) (hf.mono $ ℱ.mono bot_le) hfint,
  apply_instance,
end

variables (E)

namespace martingale

@[protected]
lemma adapted (hf : martingale f ℱ μ) : adapted ℱ f := hf.1

@[protected]
lemma strongly_measurable (hf : martingale f ℱ μ) (i : ι) : strongly_measurable[ℱ i] (f i) :=
hf.adapted i

lemma condexp_ae_eq (hf : martingale f ℱ μ) {i j : ι} (hij : i ≤ j) :
  μ[f j | ℱ i] =ᵐ[μ] f i :=
hf.2 i j hij

@[protected]
lemma integrable (hf : martingale f ℱ μ) (i : ι) : integrable (f i) μ :=
integrable_condexp.congr (hf.condexp_ae_eq (le_refl i))

lemma set_integral_eq [sigma_finite_filtration μ ℱ] (hf : martingale f ℱ μ) {i j : ι} (hij : i ≤ j)
  {s : set Ω} (hs : measurable_set[ℱ i] s) :
  ∫ ω in s, f i ω ∂μ = ∫ ω in s, f j ω ∂μ :=
begin
  rw ← @set_integral_condexp _ _ _ _ _ (ℱ i) m0 _ _ _ (ℱ.le i) _ (hf.integrable j) hs,
  refine set_integral_congr_ae (ℱ.le i s hs) _,
  filter_upwards [hf.2 i j hij] with _ heq _ using heq.symm,
end

lemma add (hf : martingale f ℱ μ) (hg : martingale g ℱ μ) : martingale (f + g) ℱ μ :=
begin
  refine ⟨hf.adapted.add hg.adapted, λ i j hij, _⟩,
  exact (condexp_add (hf.integrable j) (hg.integrable j)).trans
    ((hf.2 i j hij).add (hg.2 i j hij)),
end

lemma neg (hf : martingale f ℱ μ) : martingale (-f) ℱ μ :=
⟨hf.adapted.neg, λ i j hij, (condexp_neg (f j)).trans ((hf.2 i j hij).neg)⟩

lemma sub (hf : martingale f ℱ μ) (hg : martingale g ℱ μ) : martingale (f - g) ℱ μ :=
by { rw sub_eq_add_neg, exact hf.add hg.neg, }

lemma smul (c : ℝ) (hf : martingale f ℱ μ) : martingale (c • f) ℱ μ :=
begin
  refine ⟨hf.adapted.smul c, λ i j hij, _⟩,
  refine (condexp_smul c (f j)).trans ((hf.2 i j hij).mono (λ x hx, _)),
  rw [pi.smul_apply, hx, pi.smul_apply, pi.smul_apply],
end

lemma supermartingale [preorder E] (hf : martingale f ℱ μ) : supermartingale f ℱ μ :=
⟨hf.1, λ i j hij, (hf.2 i j hij).le, λ i, hf.integrable i⟩

lemma submartingale [preorder E] (hf : martingale f ℱ μ) : submartingale f ℱ μ :=
⟨hf.1, λ i j hij, (hf.2 i j hij).symm.le, λ i, hf.integrable i⟩

end martingale

lemma martingale_iff [partial_order E] : martingale f ℱ μ ↔
  supermartingale f ℱ μ ∧ submartingale f ℱ μ :=
⟨λ hf, ⟨hf.supermartingale, hf.submartingale⟩,
 λ ⟨hf₁, hf₂⟩, ⟨hf₁.1, λ i j hij, (hf₁.2.1 i j hij).antisymm (hf₂.2.1 i j hij)⟩⟩

lemma martingale_condexp (f : Ω → E) (ℱ : filtration ι m0) (μ : measure Ω)
  [sigma_finite_filtration μ ℱ] :
  martingale (λ i, μ[f | ℱ i]) ℱ μ :=
⟨λ i, strongly_measurable_condexp, λ i j hij, condexp_condexp_of_le (ℱ.mono hij) (ℱ.le j)⟩

namespace supermartingale

@[protected]
lemma adapted [has_le E] (hf : supermartingale f ℱ μ) : adapted ℱ f := hf.1

@[protected]
lemma strongly_measurable [has_le E] (hf : supermartingale f ℱ μ) (i : ι) :
  strongly_measurable[ℱ i] (f i) :=
hf.adapted i

@[protected]
lemma integrable [has_le E] (hf : supermartingale f ℱ μ) (i : ι) : integrable (f i) μ := hf.2.2 i

lemma condexp_ae_le [has_le E] (hf : supermartingale f ℱ μ) {i j : ι} (hij : i ≤ j) :
  μ[f j | ℱ i] ≤ᵐ[μ] f i :=
hf.2.1 i j hij

lemma set_integral_le [sigma_finite_filtration μ ℱ] {f : ι → Ω → ℝ} (hf : supermartingale f ℱ μ)
  {i j : ι} (hij : i ≤ j) {s : set Ω} (hs : measurable_set[ℱ i] s) :
  ∫ ω in s, f j ω ∂μ ≤ ∫ ω in s, f i ω ∂μ :=
begin
  rw ← set_integral_condexp (ℱ.le i) (hf.integrable j) hs,
  refine set_integral_mono_ae integrable_condexp.integrable_on (hf.integrable i).integrable_on _,
  filter_upwards [hf.2.1 i j hij] with _ heq using heq,
end

lemma add [preorder E] [covariant_class E E (+) (≤)]
  (hf : supermartingale f ℱ μ) (hg : supermartingale g ℱ μ) : supermartingale (f + g) ℱ μ :=
begin
  refine ⟨hf.1.add hg.1, λ i j hij, _, λ i, (hf.2.2 i).add (hg.2.2 i)⟩,
  refine (condexp_add (hf.integrable j) (hg.integrable j)).le.trans _,
  filter_upwards [hf.2.1 i j hij, hg.2.1 i j hij],
  intros,
  refine add_le_add _ _; assumption,
end

lemma add_martingale [preorder E] [covariant_class E E (+) (≤)]
  (hf : supermartingale f ℱ μ) (hg : martingale g ℱ μ) : supermartingale (f + g) ℱ μ :=
hf.add hg.supermartingale

lemma neg [preorder E] [covariant_class E E (+) (≤)]
  (hf : supermartingale f ℱ μ) : submartingale (-f) ℱ μ :=
begin
  refine ⟨hf.1.neg, λ i j hij, _, λ i, (hf.2.2 i).neg⟩,
  refine eventually_le.trans _ (condexp_neg (f j)).symm.le,
  filter_upwards [hf.2.1 i j hij] with _ _,
  simpa,
end

end supermartingale

namespace submartingale

@[protected]
lemma adapted [has_le E] (hf : submartingale f ℱ μ) : adapted ℱ f := hf.1

@[protected]
lemma strongly_measurable [has_le E] (hf : submartingale f ℱ μ) (i : ι) :
  strongly_measurable[ℱ i] (f i) :=
hf.adapted i

@[protected]
lemma integrable [has_le E] (hf : submartingale f ℱ μ) (i : ι) : integrable (f i) μ := hf.2.2 i

lemma ae_le_condexp [has_le E] (hf : submartingale f ℱ μ) {i j : ι} (hij : i ≤ j) :
  f i ≤ᵐ[μ] μ[f j | ℱ i] :=
hf.2.1 i j hij

lemma add [preorder E] [covariant_class E E (+) (≤)]
  (hf : submartingale f ℱ μ) (hg : submartingale g ℱ μ) : submartingale (f + g) ℱ μ :=
begin
  refine ⟨hf.1.add hg.1, λ i j hij, _, λ i, (hf.2.2 i).add (hg.2.2 i)⟩,
  refine eventually_le.trans _ (condexp_add (hf.integrable j) (hg.integrable j)).symm.le,
  filter_upwards [hf.2.1 i j hij, hg.2.1 i j hij],
  intros,
  refine add_le_add _ _; assumption,
end

lemma add_martingale [preorder E] [covariant_class E E (+) (≤)]
  (hf : submartingale f ℱ μ) (hg : martingale g ℱ μ) : submartingale (f + g) ℱ μ :=
hf.add hg.submartingale

lemma neg [preorder E] [covariant_class E E (+) (≤)]
  (hf : submartingale f ℱ μ) : supermartingale (-f) ℱ μ :=
begin
  refine ⟨hf.1.neg, λ i j hij, (condexp_neg (f j)).le.trans _, λ i, (hf.2.2 i).neg⟩,
  filter_upwards [hf.2.1 i j hij] with _ _,
  simpa,
end

/-- The converse of this lemma is `measure_theory.submartingale_of_set_integral_le`. -/
lemma set_integral_le [sigma_finite_filtration μ ℱ] {f : ι → Ω → ℝ} (hf : submartingale f ℱ μ)
  {i j : ι} (hij : i ≤ j) {s : set Ω} (hs : measurable_set[ℱ i] s) :
  ∫ ω in s, f i ω ∂μ ≤ ∫ ω in s, f j ω ∂μ :=
begin
  rw [← neg_le_neg_iff, ← integral_neg, ← integral_neg],
  exact supermartingale.set_integral_le hf.neg hij hs,
end

lemma sub_supermartingale [preorder E] [covariant_class E E (+) (≤)]
  (hf : submartingale f ℱ μ) (hg : supermartingale g ℱ μ) : submartingale (f - g) ℱ μ :=
by { rw sub_eq_add_neg, exact hf.add hg.neg }

lemma sub_martingale [preorder E] [covariant_class E E (+) (≤)]
  (hf : submartingale f ℱ μ) (hg : martingale g ℱ μ) : submartingale (f - g) ℱ μ :=
hf.sub_supermartingale hg.supermartingale

protected lemma sup {f g : ι → Ω → ℝ} (hf : submartingale f ℱ μ) (hg : submartingale g ℱ μ) :
  submartingale (f ⊔ g) ℱ μ :=
begin
  refine ⟨λ i, @strongly_measurable.sup _ _ _ _ (ℱ i) _ _ _ (hf.adapted i) (hg.adapted i),
    λ i j hij, _, λ i, integrable.sup (hf.integrable _) (hg.integrable _)⟩,
  refine eventually_le.sup_le _ _,
  { exact eventually_le.trans (hf.2.1 i j hij)
      (condexp_mono (hf.integrable _) (integrable.sup (hf.integrable j) (hg.integrable j))
      (eventually_of_forall (λ x, le_max_left _ _))) },
  { exact eventually_le.trans (hg.2.1 i j hij)
      (condexp_mono (hg.integrable _) (integrable.sup (hf.integrable j) (hg.integrable j))
      (eventually_of_forall (λ x, le_max_right _ _))) }
end

protected lemma pos {f : ι → Ω → ℝ} (hf : submartingale f ℱ μ) :
  submartingale (f⁺) ℱ μ :=
hf.sup (martingale_zero _ _ _).submartingale

end submartingale

section submartingale

lemma submartingale_of_set_integral_le [is_finite_measure μ]
  {f : ι → Ω → ℝ} (hadp : adapted ℱ f) (hint : ∀ i, integrable (f i) μ)
  (hf : ∀ i j : ι, i ≤ j → ∀ s : set Ω, measurable_set[ℱ i] s →
    ∫ ω in s, f i ω ∂μ ≤ ∫ ω in s, f j ω ∂μ) :
  submartingale f ℱ μ :=
begin
  refine ⟨hadp, λ i j hij, _, hint⟩,
  suffices : f i ≤ᵐ[μ.trim (ℱ.le i)] μ[f j| ℱ i],
  { exact ae_le_of_ae_le_trim this },
  suffices : 0 ≤ᵐ[μ.trim (ℱ.le i)] μ[f j| ℱ i] - f i,
  { filter_upwards [this] with x hx,
    rwa ← sub_nonneg },
  refine ae_nonneg_of_forall_set_integral_nonneg
    ((integrable_condexp.sub (hint i)).trim _ (strongly_measurable_condexp.sub $ hadp i))
    (λ s hs h's, _),
  specialize hf i j hij s hs,
  rwa [← set_integral_trim _ (strongly_measurable_condexp.sub $ hadp i) hs,
    integral_sub' integrable_condexp.integrable_on (hint i).integrable_on, sub_nonneg,
    set_integral_condexp (ℱ.le i) (hint j) hs],
end

lemma submartingale_of_condexp_sub_nonneg [is_finite_measure μ]
  {f : ι → Ω → ℝ} (hadp : adapted ℱ f) (hint : ∀ i, integrable (f i) μ)
  (hf : ∀ i j, i ≤ j → 0 ≤ᵐ[μ] μ[f j - f i | ℱ i]) :
  submartingale f ℱ μ :=
begin
  refine ⟨hadp, λ i j hij, _, hint⟩,
  rw [← condexp_of_strongly_measurable (ℱ.le _) (hadp _) (hint _), ← eventually_sub_nonneg],
  exact eventually_le.trans (hf i j hij) (condexp_sub (hint _) (hint _)).le,
  apply_instance
end

lemma submartingale.condexp_sub_nonneg
  {f : ι → Ω → ℝ} (hf : submartingale f ℱ μ) {i j : ι} (hij : i ≤ j) :
  0 ≤ᵐ[μ] μ[f j - f i | ℱ i] :=
begin
  by_cases h : sigma_finite (μ.trim (ℱ.le i)),
  swap, { rw condexp_of_not_sigma_finite (ℱ.le i) h },
  refine eventually_le.trans _ (condexp_sub (hf.integrable _) (hf.integrable _)).symm.le,
  rw [eventually_sub_nonneg,
    condexp_of_strongly_measurable (ℱ.le _) (hf.adapted _) (hf.integrable _)],
  { exact hf.2.1 i j hij },
  { exact h }
end

lemma submartingale_iff_condexp_sub_nonneg [is_finite_measure μ] {f : ι → Ω → ℝ} :
  submartingale f ℱ μ ↔ adapted ℱ f ∧ (∀ i, integrable (f i) μ) ∧ ∀ i j, i ≤ j →
  0 ≤ᵐ[μ] μ[f j - f i | ℱ i] :=
⟨λ h, ⟨h.adapted, h.integrable, λ i j, h.condexp_sub_nonneg⟩,
 λ ⟨hadp, hint, h⟩, submartingale_of_condexp_sub_nonneg hadp hint h⟩

end submartingale

namespace supermartingale

lemma sub_submartingale [preorder E] [covariant_class E E (+) (≤)]
  (hf : supermartingale f ℱ μ) (hg : submartingale g ℱ μ) : supermartingale (f - g) ℱ μ :=
by { rw sub_eq_add_neg, exact hf.add hg.neg }

lemma sub_martingale [preorder E] [covariant_class E E (+) (≤)]
  (hf : supermartingale f ℱ μ) (hg : martingale g ℱ μ) : supermartingale (f - g) ℱ μ :=
hf.sub_submartingale hg.submartingale

section

variables {F : Type*} [normed_lattice_add_comm_group F]
  [normed_space ℝ F] [complete_space F] [ordered_smul ℝ F]

lemma smul_nonneg {f : ι → Ω → F}
  {c : ℝ} (hc : 0 ≤ c) (hf : supermartingale f ℱ μ) :
  supermartingale (c • f) ℱ μ :=
begin
  refine ⟨hf.1.smul c, λ i j hij, _, λ i, (hf.2.2 i).smul c⟩,
  refine (condexp_smul c (f j)).le.trans _,
  filter_upwards [hf.2.1 i j hij] with _ hle,
  simp,
  exact smul_le_smul_of_nonneg hle hc,
end

lemma smul_nonpos {f : ι → Ω → F}
  {c : ℝ} (hc : c ≤ 0) (hf : supermartingale f ℱ μ) :
  submartingale (c • f) ℱ μ :=
begin
  rw [← neg_neg c, (by { ext i x, simp } : - -c • f = -(-c • f))],
  exact (hf.smul_nonneg $ neg_nonneg.2 hc).neg,
end

end

end supermartingale

namespace submartingale

section

variables {F : Type*} [normed_lattice_add_comm_group F]
  [normed_space ℝ F] [complete_space F] [ordered_smul ℝ F]

lemma smul_nonneg {f : ι → Ω → F}
  {c : ℝ} (hc : 0 ≤ c) (hf : submartingale f ℱ μ) :
  submartingale (c • f) ℱ μ :=
begin
  rw [← neg_neg c, (by { ext i x, simp } : - -c • f = -(c • -f))],
  exact supermartingale.neg (hf.neg.smul_nonneg hc),
end

lemma smul_nonpos {f : ι → Ω → F}
  {c : ℝ} (hc : c ≤ 0) (hf : submartingale f ℱ μ) :
  supermartingale (c • f) ℱ μ :=
begin
  rw [← neg_neg c, (by { ext i x, simp } : - -c • f = -(-c • f))],
  exact (hf.smul_nonneg $ neg_nonneg.2 hc).neg,
end

end

end submartingale

section nat

variables {𝒢 : filtration ℕ m0}

lemma submartingale_of_set_integral_le_succ [is_finite_measure μ]
  {f : ℕ → Ω → ℝ} (hadp : adapted 𝒢 f) (hint : ∀ i, integrable (f i) μ)
  (hf : ∀ i, ∀ s : set Ω, measurable_set[𝒢 i] s → ∫ ω in s, f i ω ∂μ ≤ ∫ ω in s, f (i + 1) ω ∂μ) :
  submartingale f 𝒢 μ :=
begin
  refine submartingale_of_set_integral_le hadp hint (λ i j hij s hs, _),
  induction hij with k hk₁ hk₂,
  { exact le_rfl },
  { exact le_trans hk₂ (hf k s (𝒢.mono hk₁ _ hs)) }
end

lemma supermartingale_of_set_integral_succ_le [is_finite_measure μ]
  {f : ℕ → Ω → ℝ} (hadp : adapted 𝒢 f) (hint : ∀ i, integrable (f i) μ)
  (hf : ∀ i, ∀ s : set Ω, measurable_set[𝒢 i] s → ∫ ω in s, f (i + 1) ω ∂μ ≤ ∫ ω in s, f i ω ∂μ) :
  supermartingale f 𝒢 μ :=
begin
  rw ← neg_neg f,
  refine (submartingale_of_set_integral_le_succ hadp.neg (λ i, (hint i).neg) _).neg,
  simpa only [integral_neg, pi.neg_apply, neg_le_neg_iff],
end

lemma martingale_of_set_integral_eq_succ [is_finite_measure μ]
  {f : ℕ → Ω → ℝ} (hadp : adapted 𝒢 f) (hint : ∀ i, integrable (f i) μ)
  (hf : ∀ i, ∀ s : set Ω, measurable_set[𝒢 i] s → ∫ ω in s, f i ω ∂μ = ∫ ω in s, f (i + 1) ω ∂μ) :
  martingale f 𝒢 μ :=
martingale_iff.2
  ⟨supermartingale_of_set_integral_succ_le hadp hint $ λ i s hs, (hf i s hs).ge,
   submartingale_of_set_integral_le_succ hadp hint $ λ i s hs, (hf i s hs).le⟩

lemma submartingale_nat [is_finite_measure μ]
  {f : ℕ → Ω → ℝ} (hadp : adapted 𝒢 f) (hint : ∀ i, integrable (f i) μ)
  (hf : ∀ i, f i ≤ᵐ[μ] μ[f (i + 1) | 𝒢 i]) :
  submartingale f 𝒢 μ :=
begin
  refine submartingale_of_set_integral_le_succ hadp hint (λ i s hs, _),
  have : ∫ ω in s, f (i + 1) ω ∂μ = ∫ ω in s, μ[f (i + 1)|𝒢 i] ω ∂μ :=
    (set_integral_condexp (𝒢.le i) (hint _) hs).symm,
  rw this,
  exact set_integral_mono_ae (hint i).integrable_on integrable_condexp.integrable_on (hf i),
end

lemma supermartingale_nat [is_finite_measure μ]
  {f : ℕ → Ω → ℝ} (hadp : adapted 𝒢 f) (hint : ∀ i, integrable (f i) μ)
  (hf : ∀ i, μ[f (i + 1) | 𝒢 i] ≤ᵐ[μ] f i) :
  supermartingale f 𝒢 μ :=
begin
  rw ← neg_neg f,
  refine (submartingale_nat hadp.neg (λ i, (hint i).neg) $ λ i,
    eventually_le.trans _ (condexp_neg _).symm.le).neg,
  filter_upwards [hf i] with x hx using neg_le_neg hx,
end

lemma martingale_nat [is_finite_measure μ]
  {f : ℕ → Ω → ℝ} (hadp : adapted 𝒢 f) (hint : ∀ i, integrable (f i) μ)
  (hf : ∀ i, f i =ᵐ[μ] μ[f (i + 1) | 𝒢 i]) :
  martingale f 𝒢 μ :=
martingale_iff.2 ⟨supermartingale_nat hadp hint $ λ i, (hf i).symm.le,
  submartingale_nat hadp hint $ λ i, (hf i).le⟩

lemma submartingale_of_condexp_sub_nonneg_nat [is_finite_measure μ]
  {f : ℕ → Ω → ℝ} (hadp : adapted 𝒢 f) (hint : ∀ i, integrable (f i) μ)
  (hf : ∀ i, 0 ≤ᵐ[μ] μ[f (i + 1) - f i | 𝒢 i]) :
  submartingale f 𝒢 μ :=
begin
  refine submartingale_nat hadp hint (λ i, _),
  rw [← condexp_of_strongly_measurable (𝒢.le _) (hadp _) (hint _), ← eventually_sub_nonneg],
  exact eventually_le.trans (hf i) (condexp_sub (hint _) (hint _)).le,
  apply_instance
end

lemma supermartingale_of_condexp_sub_nonneg_nat [is_finite_measure μ]
  {f : ℕ → Ω → ℝ} (hadp : adapted 𝒢 f) (hint : ∀ i, integrable (f i) μ)
  (hf : ∀ i, 0 ≤ᵐ[μ] μ[f i - f (i + 1) | 𝒢 i]) :
  supermartingale f 𝒢 μ :=
begin
  rw ← neg_neg f,
  refine (submartingale_of_condexp_sub_nonneg_nat hadp.neg (λ i, (hint i).neg) _).neg,
  simpa only [pi.zero_apply, pi.neg_apply, neg_sub_neg]
end

lemma martingale_of_condexp_sub_eq_zero_nat [is_finite_measure μ]
  {f : ℕ → Ω → ℝ} (hadp : adapted 𝒢 f) (hint : ∀ i, integrable (f i) μ)
  (hf : ∀ i, μ[f (i + 1) - f i | 𝒢 i] =ᵐ[μ] 0) :
  martingale f 𝒢 μ :=
begin
  refine martingale_iff.2 ⟨supermartingale_of_condexp_sub_nonneg_nat hadp hint $ λ i, _,
    submartingale_of_condexp_sub_nonneg_nat hadp hint $ λ i, (hf i).symm.le⟩,
  rw ← neg_sub,
  refine (eventually_eq.trans _ (condexp_neg _).symm).le,
  filter_upwards [hf i] with x hx,
  simpa only [pi.zero_apply, pi.neg_apply, zero_eq_neg],
end

-- Note that one cannot use `submartingale.zero_le_of_predictable` to prove the other two
-- corresponding lemmas without imposing more restrictions to the ordering of `E`
/-- A predictable submartingale is a.e. greater equal than its initial state. -/
lemma submartingale.zero_le_of_predictable [preorder E] [sigma_finite_filtration μ 𝒢]
  {f : ℕ → Ω → E} (hfmgle : submartingale f 𝒢 μ) (hfadp : adapted 𝒢 (λ n, f (n + 1))) (n : ℕ) :
  f 0 ≤ᵐ[μ] f n :=
begin
  induction n with k ih,
  { refl },
  { exact ih.trans ((hfmgle.2.1 k (k + 1) k.le_succ).trans_eq $ germ.coe_eq.mp $ congr_arg coe $
      condexp_of_strongly_measurable (𝒢.le _) (hfadp _) $ hfmgle.integrable _) }
end

/-- A predictable supermartingale is a.e. less equal than its initial state. -/
lemma supermartingale.le_zero_of_predictable [preorder E] [sigma_finite_filtration μ 𝒢]
  {f : ℕ → Ω → E} (hfmgle : supermartingale f 𝒢 μ) (hfadp : adapted 𝒢 (λ n, f (n + 1))) (n : ℕ) :
  f n ≤ᵐ[μ] f 0 :=
begin
  induction n with k ih,
  { refl },
  { exact ((germ.coe_eq.mp $ congr_arg coe $ condexp_of_strongly_measurable (𝒢.le _) (hfadp _) $
      hfmgle.integrable _).symm.trans_le (hfmgle.2.1 k (k + 1) k.le_succ)).trans ih }
end

/-- A predictable martingale is a.e. equal to its initial state. -/
lemma martingale.eq_zero_of_predictable [sigma_finite_filtration μ 𝒢]
  {f : ℕ → Ω → E} (hfmgle : martingale f 𝒢 μ) (hfadp : adapted 𝒢 (λ n, f (n + 1))) (n : ℕ) :
  f n =ᵐ[μ] f 0 :=
begin
  induction n with k ih,
  { refl },
  { exact ((germ.coe_eq.mp (congr_arg coe $ condexp_of_strongly_measurable (𝒢.le _) (hfadp _)
      (hfmgle.integrable _))).symm.trans (hfmgle.2 k (k + 1) k.le_succ)).trans ih }
end

namespace submartingale

@[protected]
lemma integrable_stopped_value [has_le E] {f : ℕ → Ω → E} (hf : submartingale f 𝒢 μ) {τ : Ω → ℕ}
  (hτ : is_stopping_time 𝒢 τ) {N : ℕ} (hbdd : ∀ ω, τ ω ≤ N) :
  integrable (stopped_value f τ) μ :=
integrable_stopped_value ℕ hτ hf.integrable hbdd

end submartingale

lemma submartingale.sum_mul_sub [is_finite_measure μ] {R : ℝ} {ξ f : ℕ → Ω → ℝ}
  (hf : submartingale f 𝒢 μ) (hξ : adapted 𝒢 ξ)
  (hbdd : ∀ n ω, ξ n ω ≤ R) (hnonneg : ∀ n ω, 0 ≤ ξ n ω) :
  submartingale (λ n, ∑ k in finset.range n, ξ k * (f (k + 1) - f k)) 𝒢 μ :=
begin
  have hξbdd : ∀ i, ∃ C, ∀ ω, |ξ i ω| ≤ C :=
    λ i, ⟨R, λ ω, (abs_of_nonneg (hnonneg i ω)).trans_le (hbdd i ω)⟩,
  have hint : ∀ m, integrable (∑ k in finset.range m, ξ k * (f (k + 1) - f k)) μ :=
    λ m, integrable_finset_sum' _
      (λ i hi, integrable.bdd_mul ((hf.integrable _).sub (hf.integrable _))
      hξ.strongly_measurable.ae_strongly_measurable (hξbdd _)),
  have hadp : adapted 𝒢 (λ n, ∑ k in finset.range n, ξ k * (f (k + 1) - f k)),
  { intro m,
    refine finset.strongly_measurable_sum' _ (λ i hi, _),
    rw finset.mem_range at hi,
    exact (hξ.strongly_measurable_le hi.le).mul
      ((hf.adapted.strongly_measurable_le (nat.succ_le_of_lt hi)).sub
      (hf.adapted.strongly_measurable_le hi.le)) },
  refine submartingale_of_condexp_sub_nonneg_nat hadp hint (λ i, _),
  simp only [← finset.sum_Ico_eq_sub _ (nat.le_succ _), finset.sum_apply, pi.mul_apply,
    pi.sub_apply, nat.Ico_succ_singleton, finset.sum_singleton],
  exact eventually_le.trans (eventually_le.mul_nonneg (eventually_of_forall (hnonneg _))
    (hf.condexp_sub_nonneg (nat.le_succ _))) (condexp_strongly_measurable_mul (hξ _)
    (((hf.integrable _).sub (hf.integrable _)).bdd_mul
      hξ.strongly_measurable.ae_strongly_measurable (hξbdd _))
    ((hf.integrable _).sub (hf.integrable _))).symm.le,
end

/-- Given a discrete submartingale `f` and a predictable process `ξ` (i.e. `ξ (n + 1)` is adapted)
the process defined by `λ n, ∑ k in finset.range n, ξ (k + 1) * (f (k + 1) - f k)` is also a
submartingale. -/
lemma submartingale.sum_mul_sub' [is_finite_measure μ] {R : ℝ} {ξ f : ℕ → Ω → ℝ}
  (hf : submartingale f 𝒢 μ) (hξ : adapted 𝒢 (λ n, ξ (n + 1)))
  (hbdd : ∀ n ω, ξ n ω ≤ R) (hnonneg : ∀ n ω, 0 ≤ ξ n ω) :
  submartingale (λ n, ∑ k in finset.range n, ξ (k + 1) * (f (k + 1) - f k)) 𝒢 μ :=
hf.sum_mul_sub hξ (λ n, hbdd _) (λ n, hnonneg _)

end nat

end measure_theory


import topology.basic
open topological_space
open set filter classical
variables {X : Type} [topological_space X] {A E F U V : set X}

-- Challenge Accepted

-- defining some terms to avoid mess (not that this file is not mess but it helps)
def lecture_closure (A : set X) := A ∪ {x : X | ∀(N ∈ nhds x) (H: is_open N), A ∩ N ≠ ∅}
def lecture_interior (A : set X) := {x : X | ∃ (N ∈ nhds x) (H: is_open N), N ⊆ A}

-- Good old antisymm method for showing equivalence (i.e. a ⊆ b ∧ b ⊆ a → a = b)

lemma lecture_interior_to_lean_interior : lecture_interior A ⊆ interior A :=
begin
  -- ∀ x in lecture_interior, contained in open neighborhood N in A, N is subset of maximal open set in A
  intros x xinlec,
  rcases xinlec with ⟨ N, Nnhd, Nopen, nhdinA ⟩,
  apply set.mem_sUnion_of_mem (mem_of_mem_nhds Nnhd) (and.intro Nopen nhdinA),
end

lemma lean_interior_to_lecture_interior : interior A ⊆ lecture_interior A :=
begin
  -- x in lean_interior means there exist an open set in A containing x. Call that set N. Done.
  unfold interior,
  intros x lean_interior,
  rw set.mem_sUnion at lean_interior,
  rcases lean_interior with ⟨ t, ht, xint ⟩,
  use t,
  split,
  {
    rw mem_nhds_iff,
    use t,
    split,
    refl,
    exact and.intro ht.left xint,
  },
  {
    split,
    exact ht.left,
    exact ht.right,
  },
end

-- antisymm to combine the above result
lemma interior_equivalence : interior A = lecture_interior A :=
begin
  apply set.subset.antisymm,
  exact lean_interior_to_lecture_interior,
  exact lecture_interior_to_lean_interior,
end

-- Now the turn for equivalence of closure
lemma lecture_closure_to_lean_closure (x : X): x ∈ lecture_closure A → x ∈ closure A :=
begin
  -- if x in A, easy since all closed set including A includes x,
  -- if x is limit point of A, if x is not in lean_closure and hence there is a closed set t not containing x,
  -- tᶜ is an open set containing x so (since x is limit point of A), tᶜ has element of A while A ⊆ t.
  intro lec_clo,
  cases lec_clo;
  unfold closure;
  rw set.mem_sInter;
  intros t ht,
  {exact set.mem_of_mem_of_subset lec_clo ht.right,},
  {
    by_contradiction,
    have tclosed := ht.left,
    rw ← is_open_compl_iff at tclosed,
    have := lec_clo tᶜ ,
    apply lec_clo tᶜ (is_open.mem_nhds tclosed (set.mem_compl h)) tclosed,
    rw [← set.subset_empty_iff, ← set.inter_compl_self t],
    exact set.inter_subset_inter_left tᶜ ht.right,
  },
end

-- I don't seem to be able to prove lean_closure_to_lecture_closure directly so
lemma useful : (lecture_interior Aᶜ)ᶜ ⊆ lecture_closure A :=
begin
  unfold lecture_interior,
  rw set.compl_set_of,
  push_neg,
  intros x b,
  cases classical.em (x ∈ A),
  {left,
    exact h},
  {
    right,
    intros N Nnhd Nopen,
    rw [set.inter_comm, set.ne_empty_iff_nonempty, ← compl_compl A, set.inter_compl_nonempty_iff],
    exact b N Nnhd Nopen,
  },
end

-- Now that I have the above (definitionaly) useful lemma I can prove this very easily
lemma lean_closure_to_lecture_closure (x : X): x ∈ closure A → x ∈ lecture_closure A :=
begin
  rw closure_eq_compl_interior_compl,
  rw interior_equivalence,
  intro a,
  exact useful a,
end

-- Antisymm for the last time and DONE!!!!!!!!!!!!!!!!!!!!!!!!!!
theorem closure_equivalence : closure A = lecture_closure A :=
begin
  apply set.subset.antisymm,
  exact lean_closure_to_lecture_closure,
  exact lecture_closure_to_lean_closure,
end

------------------------------------------------------------------------------------

-- Example 2.1.1.
example : is_closed F → A ⊆ F → closure A ⊆ F :=
begin
  intros Fclosed AinF,
  unfold closure,
  /-
  this is not how closure is defined in the module.
  smallest closed set containing A? I guess it kinda makes sense.
  -/
  let T := {t : set X | is_closed t ∧ A ⊆ t},
  have : F ∈ T,
  apply and.intro,
  exact Fclosed,
  exact AinF,
  /-
  Since F is one of the closed set containing A,
  intersections of sets in T (including F) is obviously a subset of F.
  -/
  apply sInter_subset_of_mem,
  exact this,
end

example : is_open U → U ⊆ A → U ⊆ interior A :=
begin
  intros Uopen UinA,
  unfold interior,
  -- A° = largest open set within A?
  let T := {t : set X | is_open t ∧ t ⊆ A},
  have h1 : U ∈ T,
    exact and.intro Uopen UinA,
  -- I would assume there is a equivalent thing but I don't seem to be able to find it so,
  apply set.subset_sUnion_of_subset T U,
  {by refl},
  {exact h1},
end

github-repos/lean/Jun2M-Lean_Topology/Lean_Topology-74fa55946995ea139133ae5d5e67236570ba7e0d/src/Week 7 top ver 2.lean


/-
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, Benjamin Davidson
-/
import algebra.order.to_interval_mod
import analysis.special_functions.trigonometric.angle
import analysis.special_functions.trigonometric.inverse

/-!
# The argument of a complex number.

We define `arg : ℂ → ℝ`, returing a real number in the range (-π, π],
such that for `x ≠ 0`, `sin (arg x) = x.im / x.abs` and `cos (arg x) = x.re / x.abs`,
while `arg 0` defaults to `0`
-/

noncomputable theory

namespace complex

open_locale complex_conjugate real topology
open filter set

/-- `arg` returns values in the range (-π, π], such that for `x ≠ 0`,
  `sin (arg x) = x.im / x.abs` and `cos (arg x) = x.re / x.abs`,
  `arg 0` defaults to `0` -/
noncomputable def arg (x : ℂ) : ℝ :=
if 0 ≤ x.re
then real.arcsin (x.im / x.abs)
else if 0 ≤ x.im
then real.arcsin ((-x).im / x.abs) + π
else real.arcsin ((-x).im / x.abs) - π

lemma sin_arg (x : ℂ) : real.sin (arg x) = x.im / x.abs :=
by unfold arg; split_ifs;
  simp [sub_eq_add_neg, arg, real.sin_arcsin (abs_le.1 (abs_im_div_abs_le_one x)).1
    (abs_le.1 (abs_im_div_abs_le_one x)).2, real.sin_add, neg_div, real.arcsin_neg,
    real.sin_neg]

lemma cos_arg {x : ℂ} (hx : x ≠ 0) : real.cos (arg x) = x.re / x.abs :=
begin
  have habs : 0 < abs x := abs.pos hx,
  have him : |im x / abs x| ≤ 1,
  { rw [_root_.abs_div, abs_abs],
    exact div_le_one_of_le x.abs_im_le_abs (abs.nonneg x) },
  rw abs_le at him,
  rw arg, split_ifs with h₁ h₂ h₂,
  { rw [real.cos_arcsin], field_simp [real.sqrt_sq, habs.le, *] },
  { rw [real.cos_add_pi, real.cos_arcsin],
    field_simp [real.sqrt_div (sq_nonneg _), real.sqrt_sq_eq_abs,
      _root_.abs_of_neg (not_le.1 h₁), *] },
  { rw [real.cos_sub_pi, real.cos_arcsin],
    field_simp [real.sqrt_div (sq_nonneg _), real.sqrt_sq_eq_abs,
      _root_.abs_of_neg (not_le.1 h₁), *] }
end

@[simp] lemma abs_mul_exp_arg_mul_I (x : ℂ) : ↑(abs x) * exp (arg x * I) = x :=
begin
  rcases eq_or_ne x 0 with (rfl|hx),
  { simp },
  { have : abs x ≠ 0 := abs.ne_zero hx,
    ext; field_simp [sin_arg, cos_arg hx, this, mul_comm (abs x)] }
end

@[simp] lemma abs_mul_cos_add_sin_mul_I (x : ℂ) :
  (abs x * (cos (arg x) + sin (arg x) * I) : ℂ) = x :=
by rw [← exp_mul_I, abs_mul_exp_arg_mul_I]

lemma abs_eq_one_iff (z : ℂ) : abs z = 1 ↔ ∃ θ : ℝ, exp (θ * I) = z :=
begin
  refine ⟨λ hz, ⟨arg z, _⟩, _⟩,
  { calc exp (arg z * I) = abs z * exp (arg z * I) : by rw [hz, of_real_one, one_mul]
    ... = z : abs_mul_exp_arg_mul_I z },
  { rintro ⟨θ, rfl⟩,
    exact complex.abs_exp_of_real_mul_I θ },
end

@[simp] lemma range_exp_mul_I : range (λ x : ℝ, exp (x * I)) = metric.sphere 0 1 :=
by { ext x, simp only [mem_sphere_zero_iff_norm, norm_eq_abs, abs_eq_one_iff, mem_range] }

lemma arg_mul_cos_add_sin_mul_I {r : ℝ} (hr : 0 < r) {θ : ℝ} (hθ : θ ∈ Ioc (-π) π) :
  arg (r * (cos θ + sin θ * I)) = θ :=
begin
  simp only [arg, map_mul, abs_cos_add_sin_mul_I, abs_of_nonneg hr.le, mul_one],
  simp only [of_real_mul_re, of_real_mul_im, neg_im, ← of_real_cos, ← of_real_sin,
    ← mk_eq_add_mul_I, neg_div, mul_div_cancel_left _ hr.ne',
    mul_nonneg_iff_right_nonneg_of_pos hr],
  by_cases h₁ : θ ∈ Icc (-(π / 2)) (π / 2),
  { rw if_pos, exacts [real.arcsin_sin' h₁, real.cos_nonneg_of_mem_Icc h₁] },
  { rw [mem_Icc, not_and_distrib, not_le, not_le] at h₁, cases h₁,
    { replace hθ := hθ.1,
      have hcos : real.cos θ < 0,
      { rw [← neg_pos, ← real.cos_add_pi], refine real.cos_pos_of_mem_Ioo ⟨_, _⟩; linarith },
      have hsin : real.sin θ < 0 := real.sin_neg_of_neg_of_neg_pi_lt (by linarith) hθ,
      rw [if_neg, if_neg, ← real.sin_add_pi, real.arcsin_sin, add_sub_cancel];
        [linarith, linarith, exact hsin.not_le, exact hcos.not_le] },
    { replace hθ := hθ.2,
      have hcos : real.cos θ < 0 := real.cos_neg_of_pi_div_two_lt_of_lt h₁ (by linarith),
      have hsin : 0 ≤ real.sin θ := real.sin_nonneg_of_mem_Icc ⟨by linarith, hθ⟩,
      rw [if_neg, if_pos, ← real.sin_sub_pi, real.arcsin_sin, sub_add_cancel];
        [linarith, linarith, exact hsin, exact hcos.not_le] } }
end

lemma arg_cos_add_sin_mul_I {θ : ℝ} (hθ : θ ∈ Ioc (-π) π) :
  arg (cos θ + sin θ * I) = θ :=
by rw [← one_mul (_ + _), ← of_real_one, arg_mul_cos_add_sin_mul_I zero_lt_one hθ]

@[simp] lemma arg_zero : arg 0 = 0 := by simp [arg, le_refl]

lemma ext_abs_arg {x y : ℂ} (h₁ : x.abs = y.abs) (h₂ : x.arg = y.arg) : x = y :=
by rw [← abs_mul_exp_arg_mul_I x, ← abs_mul_exp_arg_mul_I y, h₁, h₂]

lemma ext_abs_arg_iff {x y : ℂ} : x = y ↔ abs x = abs y ∧ arg x = arg y :=
⟨λ h, h ▸ ⟨rfl, rfl⟩, and_imp.2 ext_abs_arg⟩

lemma arg_mem_Ioc (z : ℂ) : arg z ∈ Ioc (-π) π :=
begin
  have hπ : 0 < π := real.pi_pos,
  rcases eq_or_ne z 0 with (rfl|hz), simp [hπ, hπ.le],
  rcases exists_unique_add_zsmul_mem_Ioc real.two_pi_pos (arg z) (-π) with ⟨N, hN, -⟩,
  rw [two_mul, neg_add_cancel_left, ← two_mul, zsmul_eq_mul] at hN,
  rw [← abs_mul_cos_add_sin_mul_I z, ← cos_add_int_mul_two_pi _ N,
    ← sin_add_int_mul_two_pi _ N],
  simp only [← of_real_one, ← of_real_bit0, ← of_real_mul, ← of_real_add, ← of_real_int_cast],
  rwa [arg_mul_cos_add_sin_mul_I (abs.pos hz) hN]
end

@[simp] lemma range_arg : range arg = Ioc (-π) π :=
(range_subset_iff.2 arg_mem_Ioc).antisymm (λ x hx, ⟨_, arg_cos_add_sin_mul_I hx⟩)

lemma arg_le_pi (x : ℂ) : arg x ≤ π :=
(arg_mem_Ioc x).2

lemma neg_pi_lt_arg (x : ℂ) : -π < arg x :=
(arg_mem_Ioc x).1

lemma abs_arg_le_pi (z : ℂ) : |arg z| ≤ π := abs_le.2 ⟨(neg_pi_lt_arg z).le, arg_le_pi z⟩

@[simp] lemma arg_nonneg_iff {z : ℂ} : 0 ≤ arg z ↔ 0 ≤ z.im :=
begin
  rcases eq_or_ne z 0 with (rfl|h₀), { simp },
  calc 0 ≤ arg z ↔ 0 ≤ real.sin (arg z) :
    ⟨λ h, real.sin_nonneg_of_mem_Icc ⟨h, arg_le_pi z⟩,
      by { contrapose!, intro h, exact real.sin_neg_of_neg_of_neg_pi_lt h (neg_pi_lt_arg _) }⟩
  ... ↔ _ : by rw [sin_arg, le_div_iff (abs.pos h₀), zero_mul]
end

@[simp] lemma arg_neg_iff {z : ℂ} : arg z < 0 ↔ z.im < 0 :=
lt_iff_lt_of_le_iff_le arg_nonneg_iff

lemma arg_real_mul (x : ℂ) {r : ℝ} (hr : 0 < r) : arg (r * x) = arg x :=
begin
  rcases eq_or_ne x 0 with (rfl|hx), { rw mul_zero },
  conv_lhs { rw [← abs_mul_cos_add_sin_mul_I x, ← mul_assoc, ← of_real_mul,
    arg_mul_cos_add_sin_mul_I (mul_pos hr (abs.pos hx)) x.arg_mem_Ioc] }
end

lemma arg_eq_arg_iff {x y : ℂ} (hx : x ≠ 0) (hy : y ≠ 0) :
  arg x = arg y ↔ (abs y / abs x : ℂ) * x = y :=
begin
  simp only [ext_abs_arg_iff, map_mul, map_div₀, abs_of_real, abs_abs,
    div_mul_cancel _ (abs.ne_zero hx), eq_self_iff_true, true_and],
  rw [← of_real_div, arg_real_mul],
  exact div_pos (abs.pos hy) (abs.pos hx)
end

@[simp] lemma arg_one : arg 1 = 0 :=
by simp [arg, zero_le_one]

@[simp] lemma arg_neg_one : arg (-1) = π :=
by simp [arg, le_refl, not_le.2 (zero_lt_one' ℝ)]

@[simp] lemma arg_I : arg I = π / 2 :=
by simp [arg, le_refl]

@[simp] lemma arg_neg_I : arg (-I) = -(π / 2) :=
by simp [arg, le_refl]

@[simp] lemma tan_arg (x : ℂ) : real.tan (arg x) = x.im / x.re :=
begin
  by_cases h : x = 0,
  { simp only [h, zero_div, complex.zero_im, complex.arg_zero, real.tan_zero, complex.zero_re] },
  rw [real.tan_eq_sin_div_cos, sin_arg, cos_arg h,
      div_div_div_cancel_right _ (abs.ne_zero h)]
end

lemma arg_of_real_of_nonneg {x : ℝ} (hx : 0 ≤ x) : arg x = 0 :=
by simp [arg, hx]

lemma arg_eq_zero_iff {z : ℂ} : arg z = 0 ↔ 0 ≤ z.re ∧ z.im = 0 :=
begin
  refine ⟨λ h, _, _⟩,
  { rw [←abs_mul_cos_add_sin_mul_I z, h],
    simp [abs.nonneg] },
  { cases z with x y,
    rintro ⟨h, rfl : y = 0⟩,
    exact arg_of_real_of_nonneg h }
end

lemma arg_eq_pi_iff {z : ℂ} : arg z = π ↔ z.re < 0 ∧ z.im = 0 :=
begin
  by_cases h₀ : z = 0, { simp [h₀, lt_irrefl, real.pi_ne_zero.symm] },
  split,
  { intro h, rw [← abs_mul_cos_add_sin_mul_I z, h], simp [h₀] },
  { cases z with x y, rintro ⟨h : x < 0, rfl : y = 0⟩,
    rw [← arg_neg_one, ← arg_real_mul (-1) (neg_pos.2 h)], simp [← of_real_def] }
end

lemma arg_lt_pi_iff {z : ℂ} : arg z < π ↔ 0 ≤ z.re ∨ z.im ≠ 0 :=
by rw [(arg_le_pi z).lt_iff_ne, not_iff_comm, not_or_distrib, not_le, not_not, arg_eq_pi_iff]

lemma arg_of_real_of_neg {x : ℝ} (hx : x < 0) : arg x = π :=
arg_eq_pi_iff.2 ⟨hx, rfl⟩

lemma arg_eq_pi_div_two_iff {z : ℂ} : arg z = π / 2 ↔ z.re = 0 ∧ 0 < z.im :=
begin
  by_cases h₀ : z = 0, { simp [h₀, lt_irrefl, real.pi_div_two_pos.ne] },
  split,
  { intro h, rw [← abs_mul_cos_add_sin_mul_I z, h], simp [h₀] },
  { cases z with x y, rintro ⟨rfl : x = 0, hy : 0 < y⟩,
    rw [← arg_I, ← arg_real_mul I hy, of_real_mul', I_re, I_im, mul_zero, mul_one] }
end

lemma arg_eq_neg_pi_div_two_iff {z : ℂ} : arg z = - (π / 2) ↔ z.re = 0 ∧ z.im < 0 :=
begin
  by_cases h₀ : z = 0, { simp [h₀, lt_irrefl, real.pi_ne_zero] },
  split,
  { intro h, rw [← abs_mul_cos_add_sin_mul_I z, h], simp [h₀] },
  { cases z with x y, rintro ⟨rfl : x = 0, hy : y < 0⟩,
    rw [← arg_neg_I, ← arg_real_mul (-I) (neg_pos.2 hy), mk_eq_add_mul_I],
    simp }
end

lemma arg_of_re_nonneg {x : ℂ} (hx : 0 ≤ x.re) : arg x = real.arcsin (x.im / x.abs) :=
if_pos hx

lemma arg_of_re_neg_of_im_nonneg {x : ℂ} (hx_re : x.re < 0) (hx_im : 0 ≤ x.im) :
  arg x = real.arcsin ((-x).im / x.abs) + π :=
by simp only [arg, hx_re.not_le, hx_im, if_true, if_false]

lemma arg_of_re_neg_of_im_neg {x : ℂ} (hx_re : x.re < 0) (hx_im : x.im < 0) :
  arg x = real.arcsin ((-x).im / x.abs) - π :=
by simp only [arg, hx_re.not_le, hx_im.not_le, if_false]

lemma arg_of_im_nonneg_of_ne_zero {z : ℂ} (h₁ : 0 ≤ z.im) (h₂ : z ≠ 0) :
  arg z = real.arccos (z.re / abs z) :=
by rw [← cos_arg h₂, real.arccos_cos (arg_nonneg_iff.2 h₁) (arg_le_pi _)]

lemma arg_of_im_pos {z : ℂ} (hz : 0 < z.im) : arg z = real.arccos (z.re / abs z) :=
arg_of_im_nonneg_of_ne_zero hz.le (λ h, hz.ne' $ h.symm ▸ rfl)

lemma arg_of_im_neg {z : ℂ} (hz : z.im < 0) : arg z = -real.arccos (z.re / abs z) :=
begin
  have h₀ : z ≠ 0, from mt (congr_arg im) hz.ne,
  rw [← cos_arg h₀, ← real.cos_neg, real.arccos_cos, neg_neg],
  exacts [neg_nonneg.2 (arg_neg_iff.2 hz).le, neg_le.2 (neg_pi_lt_arg z).le]
end

lemma arg_conj (x : ℂ) : arg (conj x) = if arg x = π then π else -arg x :=
begin
  simp_rw [arg_eq_pi_iff, arg, neg_im, conj_im, conj_re, abs_conj, neg_div, neg_neg,
           real.arcsin_neg, apply_ite has_neg.neg, neg_add, neg_sub, neg_neg, ←sub_eq_add_neg,
           sub_neg_eq_add, add_comm π],
  rcases lt_trichotomy x.re 0 with (hr|hr|hr); rcases lt_trichotomy x.im 0 with (hi|hi|hi),
  { simp [hr, hr.not_le, hi.le, hi.ne, not_le.2 hi] },
  { simp [hr, hr.not_le, hi] },
  { simp [hr, hr.not_le, hi.ne.symm, hi.le, not_le.2 hi] },
  { simp [hr] },
  { simp [hr] },
  { simp [hr] },
  { simp [hr, hr.le, hi.ne] },
  { simp [hr, hr.le, hr.le.not_lt] },
  { simp [hr, hr.le, hr.le.not_lt] },
end

lemma arg_inv (x : ℂ) : arg x⁻¹ = if arg x = π then π else -arg x :=
begin
  rw [←arg_conj, inv_def, mul_comm],
  by_cases hx : x = 0,
  { simp [hx] },
  { exact arg_real_mul (conj x) (by simp [hx]) }
end

lemma arg_le_pi_div_two_iff {z : ℂ} : arg z ≤ π / 2 ↔ 0 ≤ re z ∨ im z < 0 :=
begin
  cases le_or_lt 0 (re z) with hre hre,
  { simp only [hre, arg_of_re_nonneg hre, real.arcsin_le_pi_div_two, true_or] },
  simp only [hre.not_le, false_or],
  cases le_or_lt 0 (im z) with him him,
  { simp only [him.not_lt],
    rw [iff_false, not_le, arg_of_re_neg_of_im_nonneg hre him, ← sub_lt_iff_lt_add, half_sub,
      real.neg_pi_div_two_lt_arcsin, neg_im, neg_div, neg_lt_neg_iff, div_lt_one, ←
      _root_.abs_of_nonneg him, abs_im_lt_abs],
    exacts [hre.ne, abs.pos $ ne_of_apply_ne re hre.ne] },
  { simp only [him],
    rw [iff_true, arg_of_re_neg_of_im_neg hre him],
    exact (sub_le_self _ real.pi_pos.le).trans (real.arcsin_le_pi_div_two _) }
end

lemma neg_pi_div_two_le_arg_iff {z : ℂ} : -(π / 2) ≤ arg z ↔ 0 ≤ re z ∨ 0 ≤ im z :=
begin
  cases le_or_lt 0 (re z) with hre hre,
  { simp only [hre, arg_of_re_nonneg hre, real.neg_pi_div_two_le_arcsin, true_or] },
  simp only [hre.not_le, false_or],
  cases le_or_lt 0 (im z) with him him,
  { simp only [him],
    rw [iff_true, arg_of_re_neg_of_im_nonneg hre him],
    exact (real.neg_pi_div_two_le_arcsin _).trans (le_add_of_nonneg_right real.pi_pos.le) },
  { simp only [him.not_le],
    rw [iff_false, not_le, arg_of_re_neg_of_im_neg hre him, sub_lt_iff_lt_add', ← sub_eq_add_neg,
      sub_half, real.arcsin_lt_pi_div_two, div_lt_one, neg_im, ← abs_of_neg him, abs_im_lt_abs],
    exacts [hre.ne, abs.pos $ ne_of_apply_ne re hre.ne] }
end

@[simp] lemma abs_arg_le_pi_div_two_iff {z : ℂ} : |arg z| ≤ π / 2 ↔ 0 ≤ re z :=
by rw [abs_le, arg_le_pi_div_two_iff, neg_pi_div_two_le_arg_iff, ← or_and_distrib_left, ← not_le,
  and_not_self, or_false]

@[simp] lemma arg_conj_coe_angle (x : ℂ) : (arg (conj x) : real.angle) = -arg x :=
begin
  by_cases h : arg x = π;
    simp [arg_conj, h]
end

@[simp] lemma arg_inv_coe_angle (x : ℂ) : (arg x⁻¹ : real.angle) = -arg x :=
begin
  by_cases h : arg x = π;
    simp [arg_inv, h]
end

lemma arg_neg_eq_arg_sub_pi_of_im_pos {x : ℂ} (hi : 0 < x.im) : arg (-x) = arg x - π :=
begin
  rw [arg_of_im_pos hi, arg_of_im_neg (show (-x).im < 0, from left.neg_neg_iff.2 hi)],
  simp [neg_div, real.arccos_neg]
end

lemma arg_neg_eq_arg_add_pi_of_im_neg {x : ℂ} (hi : x.im < 0) : arg (-x) = arg x + π :=
begin
  rw [arg_of_im_neg hi, arg_of_im_pos (show 0 < (-x).im, from left.neg_pos_iff.2 hi)],
  simp [neg_div, real.arccos_neg, add_comm, ←sub_eq_add_neg]
end

lemma arg_neg_eq_arg_sub_pi_iff {x : ℂ} :
  arg (-x) = arg x - π ↔ (0 < x.im ∨ x.im = 0 ∧ x.re < 0) :=
begin
  rcases lt_trichotomy x.im 0 with (hi|hi|hi),
  { simp [hi, hi.ne, hi.not_lt, arg_neg_eq_arg_add_pi_of_im_neg, sub_eq_add_neg,
          ←add_eq_zero_iff_eq_neg, real.pi_ne_zero] },
  { rw (ext rfl hi : x = x.re),
    rcases lt_trichotomy x.re 0 with (hr|hr|hr),
    { rw [arg_of_real_of_neg hr, ←of_real_neg, arg_of_real_of_nonneg (left.neg_pos_iff.2 hr).le],
      simp [hr] },
    { simp [hr, hi, real.pi_ne_zero] },
    { rw [arg_of_real_of_nonneg hr.le, ←of_real_neg, arg_of_real_of_neg (left.neg_neg_iff.2 hr)],
      simp [hr.not_lt, ←add_eq_zero_iff_eq_neg, real.pi_ne_zero] } },
  { simp [hi, arg_neg_eq_arg_sub_pi_of_im_pos] }
end

lemma arg_neg_eq_arg_add_pi_iff {x : ℂ} :
  arg (-x) = arg x + π ↔ (x.im < 0 ∨ x.im = 0 ∧ 0 < x.re) :=
begin
  rcases lt_trichotomy x.im 0 with (hi|hi|hi),
  { simp [hi, arg_neg_eq_arg_add_pi_of_im_neg] },
  { rw (ext rfl hi : x = x.re),
    rcases lt_trichotomy x.re 0 with (hr|hr|hr),
    { rw [arg_of_real_of_neg hr, ←of_real_neg, arg_of_real_of_nonneg (left.neg_pos_iff.2 hr).le],
      simp [hr.not_lt, ←two_mul, real.pi_ne_zero] },
    { simp [hr, hi, real.pi_ne_zero.symm] },
    { rw [arg_of_real_of_nonneg hr.le, ←of_real_neg, arg_of_real_of_neg (left.neg_neg_iff.2 hr)],
      simp [hr] } },
  { simp [hi, hi.ne.symm, hi.not_lt, arg_neg_eq_arg_sub_pi_of_im_pos, sub_eq_add_neg,
          ←add_eq_zero_iff_neg_eq, real.pi_ne_zero] }
end

lemma arg_neg_coe_angle {x : ℂ} (hx : x ≠ 0) : (arg (-x) : real.angle) = arg x + π :=
begin
  rcases lt_trichotomy x.im 0 with (hi|hi|hi),
  { rw [arg_neg_eq_arg_add_pi_of_im_neg hi, real.angle.coe_add] },
  { rw (ext rfl hi : x = x.re),
    rcases lt_trichotomy x.re 0 with (hr|hr|hr),
    { rw [arg_of_real_of_neg hr, ←of_real_neg, arg_of_real_of_nonneg (left.neg_pos_iff.2 hr).le,
          ←real.angle.coe_add, ←two_mul, real.angle.coe_two_pi, real.angle.coe_zero] },
    { exact false.elim (hx (ext hr hi)) },
    { rw [arg_of_real_of_nonneg hr.le, ←of_real_neg, arg_of_real_of_neg (left.neg_neg_iff.2 hr),
          real.angle.coe_zero, zero_add] } },
  { rw [arg_neg_eq_arg_sub_pi_of_im_pos hi, real.angle.coe_sub,
        real.angle.sub_coe_pi_eq_add_coe_pi] }
end

lemma arg_mul_cos_add_sin_mul_I_eq_to_Ioc_mod {r : ℝ} (hr : 0 < r) (θ : ℝ) :
  arg (r * (cos θ + sin θ * I)) = to_Ioc_mod (-π) real.two_pi_pos θ :=
begin
  have hi : to_Ioc_mod (-π) real.two_pi_pos θ ∈ Ioc (-π) π,
  { convert to_Ioc_mod_mem_Ioc _ real.two_pi_pos _,
    ring },
  convert arg_mul_cos_add_sin_mul_I hr hi using 3,
  simp [to_Ioc_mod, cos_sub_int_mul_two_pi, sin_sub_int_mul_two_pi]
end

lemma arg_cos_add_sin_mul_I_eq_to_Ioc_mod (θ : ℝ) :
  arg (cos θ + sin θ * I) = to_Ioc_mod (-π) real.two_pi_pos θ :=
by rw [←one_mul (_ + _), ←of_real_one, arg_mul_cos_add_sin_mul_I_eq_to_Ioc_mod zero_lt_one]

lemma arg_mul_cos_add_sin_mul_I_sub {r : ℝ} (hr : 0 < r) (θ : ℝ) :
  arg (r * (cos θ + sin θ * I)) - θ = 2 * π * ⌊(π - θ) / (2 * π)⌋ :=
begin
  rw [arg_mul_cos_add_sin_mul_I_eq_to_Ioc_mod hr, to_Ioc_mod_sub_self, to_Ioc_div_eq_neg_floor,
      zsmul_eq_mul],
  ring_nf
end

lemma arg_cos_add_sin_mul_I_sub (θ : ℝ) :
  arg (cos θ + sin θ * I) - θ = 2 * π * ⌊(π - θ) / (2 * π)⌋ :=
by rw [←one_mul (_ + _), ←of_real_one, arg_mul_cos_add_sin_mul_I_sub zero_lt_one]

lemma arg_mul_cos_add_sin_mul_I_coe_angle {r : ℝ} (hr : 0 < r) (θ : real.angle) :
  (arg (r * (real.angle.cos θ + real.angle.sin θ * I)) : real.angle) = θ :=
begin
  induction θ using real.angle.induction_on,
  rw [real.angle.cos_coe, real.angle.sin_coe, real.angle.angle_eq_iff_two_pi_dvd_sub],
  use ⌊(π - θ) / (2 * π)⌋,
  exact_mod_cast arg_mul_cos_add_sin_mul_I_sub hr θ
end

lemma arg_cos_add_sin_mul_I_coe_angle (θ : real.angle) :
  (arg (real.angle.cos θ + real.angle.sin θ * I) : real.angle) = θ :=
by rw [←one_mul (_ + _), ←of_real_one, arg_mul_cos_add_sin_mul_I_coe_angle zero_lt_one]

lemma arg_mul_coe_angle {x y : ℂ} (hx : x ≠ 0) (hy : y ≠ 0) :
  (arg (x * y) : real.angle) = arg x + arg y :=
begin
  convert arg_mul_cos_add_sin_mul_I_coe_angle (mul_pos (abs.pos hx) (abs.pos hy))
                                              (arg x + arg y : real.angle) using 3,
  simp_rw [←real.angle.coe_add, real.angle.sin_coe, real.angle.cos_coe, of_real_cos,
           of_real_sin, cos_add_sin_I, of_real_add, add_mul, exp_add, of_real_mul],
  rw [mul_assoc, mul_comm (exp _), ←mul_assoc (abs y : ℂ), abs_mul_exp_arg_mul_I, mul_comm y,
      ←mul_assoc, abs_mul_exp_arg_mul_I]
end

lemma arg_div_coe_angle {x y : ℂ} (hx : x ≠ 0) (hy : y ≠ 0) :
  (arg (x / y) : real.angle) = arg x - arg y :=
by rw [div_eq_mul_inv, arg_mul_coe_angle hx (inv_ne_zero hy), arg_inv_coe_angle, sub_eq_add_neg]

@[simp] lemma arg_coe_angle_to_real_eq_arg (z : ℂ) : (arg z : real.angle).to_real = arg z :=
begin
  rw real.angle.to_real_coe_eq_self_iff_mem_Ioc,
  exact arg_mem_Ioc _
end

lemma arg_coe_angle_eq_iff_eq_to_real {z : ℂ} {θ : real.angle} :
  (arg z : real.angle) = θ ↔ arg z = θ.to_real :=
by rw [←real.angle.to_real_inj, arg_coe_angle_to_real_eq_arg]

@[simp] lemma arg_coe_angle_eq_iff {x y : ℂ} : (arg x : real.angle) = arg y ↔ arg x = arg y :=
by simp_rw [←real.angle.to_real_inj, arg_coe_angle_to_real_eq_arg]

section continuity

variables {x z : ℂ}

lemma arg_eq_nhds_of_re_pos (hx : 0 < x.re) : arg =ᶠ[𝓝 x] λ x, real.arcsin (x.im / x.abs) :=
((continuous_re.tendsto _).eventually (lt_mem_nhds hx)).mono $ λ y hy, arg_of_re_nonneg hy.le

lemma arg_eq_nhds_of_re_neg_of_im_pos (hx_re : x.re < 0) (hx_im : 0 < x.im) :
  arg =ᶠ[𝓝 x] λ x, real.arcsin ((-x).im / x.abs) + π :=
begin
  suffices h_forall_nhds : ∀ᶠ (y : ℂ) in (𝓝 x), y.re < 0 ∧ 0 < y.im,
    from h_forall_nhds.mono (λ y hy, arg_of_re_neg_of_im_nonneg hy.1 hy.2.le),
  refine is_open.eventually_mem _ (⟨hx_re, hx_im⟩ : x.re < 0 ∧ 0 < x.im),
  exact is_open.and (is_open_lt continuous_re continuous_zero)
    (is_open_lt continuous_zero continuous_im),
end

lemma arg_eq_nhds_of_re_neg_of_im_neg (hx_re : x.re < 0) (hx_im : x.im < 0) :
  arg =ᶠ[𝓝 x] λ x, real.arcsin ((-x).im / x.abs) - π :=
begin
  suffices h_forall_nhds : ∀ᶠ (y : ℂ) in (𝓝 x), y.re < 0 ∧ y.im < 0,
    from h_forall_nhds.mono (λ y hy, arg_of_re_neg_of_im_neg hy.1 hy.2),
  refine is_open.eventually_mem _ (⟨hx_re, hx_im⟩ : x.re < 0 ∧ x.im < 0),
  exact is_open.and (is_open_lt continuous_re continuous_zero)
    (is_open_lt continuous_im continuous_zero),
end

lemma arg_eq_nhds_of_im_neg (hz : im z < 0) :
  arg =ᶠ[𝓝 z] λ x, -real.arccos (x.re / abs x) :=
((continuous_im.tendsto _).eventually (gt_mem_nhds hz)).mono $ λ x, arg_of_im_neg

lemma continuous_at_arg (h : 0 < x.re ∨ x.im ≠ 0) : continuous_at arg x :=
begin
  have h₀ : abs x ≠ 0, { rw abs.ne_zero_iff, rintro rfl, simpa using h },
  rw [← lt_or_lt_iff_ne] at h,
  rcases h with (hx_re|hx_im|hx_im),
  exacts [(real.continuous_at_arcsin.comp (continuous_im.continuous_at.div
    continuous_abs.continuous_at h₀)).congr (arg_eq_nhds_of_re_pos hx_re).symm,
    (real.continuous_arccos.continuous_at.comp (continuous_re.continuous_at.div
      continuous_abs.continuous_at h₀)).neg.congr (arg_eq_nhds_of_im_neg hx_im).symm,
    (real.continuous_arccos.continuous_at.comp (continuous_re.continuous_at.div
      continuous_abs.continuous_at h₀)).congr (arg_eq_nhds_of_im_pos hx_im).symm]
end

lemma tendsto_arg_nhds_within_im_neg_of_re_neg_of_im_zero
  {z : ℂ} (hre : z.re < 0) (him : z.im = 0) :
  tendsto arg (𝓝[{z : ℂ | z.im < 0}] z) (𝓝 (-π)) :=
begin
  suffices H :
    tendsto (λ x : ℂ, real.arcsin ((-x).im / x.abs) - π) (𝓝[{z : ℂ | z.im < 0}] z) (𝓝 (-π)),
  { refine H.congr' _,
    have : ∀ᶠ x : ℂ in 𝓝 z, x.re < 0, from continuous_re.tendsto z (gt_mem_nhds hre),
    filter_upwards [self_mem_nhds_within, mem_nhds_within_of_mem_nhds this] with _ him hre,
    rw [arg, if_neg hre.not_le, if_neg him.not_le], },
  convert (real.continuous_at_arcsin.comp_continuous_within_at
    ((continuous_im.continuous_at.comp_continuous_within_at continuous_within_at_neg).div
      continuous_abs.continuous_within_at _)).sub tendsto_const_nhds,
  { simp [him] },
  { lift z to ℝ using him, simpa using hre.ne }
end

lemma continuous_within_at_arg_of_re_neg_of_im_zero
  {z : ℂ} (hre : z.re < 0) (him : z.im = 0) :
  continuous_within_at arg {z : ℂ | 0 ≤ z.im} z :=
begin
  have : arg =ᶠ[𝓝[{z : ℂ | 0 ≤ z.im}] z] λ x, real.arcsin ((-x).im / x.abs) + π,
  { have : ∀ᶠ x : ℂ in 𝓝 z, x.re < 0, from continuous_re.tendsto z (gt_mem_nhds hre),
    filter_upwards [self_mem_nhds_within, mem_nhds_within_of_mem_nhds this] with _ him hre,
    rw [arg, if_neg hre.not_le, if_pos him] },
  refine continuous_within_at.congr_of_eventually_eq _ this _,
  { refine (real.continuous_at_arcsin.comp_continuous_within_at
      ((continuous_im.continuous_at.comp_continuous_within_at continuous_within_at_neg).div
        continuous_abs.continuous_within_at _)).add tendsto_const_nhds,
    lift z to ℝ using him, simpa using hre.ne },
  { rw [arg, if_neg hre.not_le, if_pos him.ge] }
end

lemma tendsto_arg_nhds_within_im_nonneg_of_re_neg_of_im_zero
  {z : ℂ} (hre : z.re < 0) (him : z.im = 0) :
  tendsto arg (𝓝[{z : ℂ | 0 ≤ z.im}] z) (𝓝 π) :=
by simpa only [arg_eq_pi_iff.2 ⟨hre, him⟩]
  using (continuous_within_at_arg_of_re_neg_of_im_zero hre him).tendsto

lemma continuous_at_arg_coe_angle (h : x ≠ 0) : continuous_at (coe ∘ arg : ℂ → real.angle) x :=
begin
  by_cases hs : 0 < x.re ∨ x.im ≠ 0,
  { exact real.angle.continuous_coe.continuous_at.comp (continuous_at_arg hs) },
  { rw [←function.comp.right_id (coe ∘ arg),
        (function.funext_iff.2 (λ _, (neg_neg _).symm) :
          (id : ℂ → ℂ) = has_neg.neg ∘ has_neg.neg), ←function.comp.assoc],
    refine continuous_at.comp _ continuous_neg.continuous_at,
    suffices : continuous_at (function.update ((coe ∘ arg) ∘ has_neg.neg : ℂ → real.angle) 0 π)
      (-x), by rwa continuous_at_update_of_ne (neg_ne_zero.2 h) at this,
    have ha : function.update ((coe ∘ arg) ∘ has_neg.neg : ℂ → real.angle) 0 π =
      λ z, (arg z : real.angle) + π,
    { rw function.update_eq_iff,
      exact ⟨by simp, λ z hz, arg_neg_coe_angle hz⟩ },
    rw ha,
    push_neg at hs,
    refine (real.angle.continuous_coe.continuous_at.comp (continuous_at_arg (or.inl _))).add
      continuous_at_const,
    rw [neg_re, neg_pos],
    exact hs.1.lt_of_ne (λ h0, h (ext_iff.2 ⟨h0, hs.2⟩)) }
end

end continuity

end complex


import Mathlib.Data.Nat.GCD.Basic
import Mathlib.Tactic.Ring
import Std
import Demo.Util

def fib : Nat → Nat
  | 0 => 0
  | 1 => 1
  | n + 2 => fib (n + 1) + fib n

#eval fib 5
#eval List.range 20 |>.map fib

-- set_option trace.Meta.Tactic.simp.rewrite true in
theorem fib_add_two {n : ℕ} : fib (n + 2) = fib n + fib (n + 1) := by
  simp [fib, add_comm]

lemma fib_add (m n : ℕ) :
    fib (m + n + 1) = fib m * fib n + fib (m + 1) * fib (n + 1) := by
  induction n generalizing m
  · case zero =>
    simp [fib]
  · case succ n ih =>
    intros
    specialize ih (m + 1)
    rw [add_assoc m 1 n, add_comm 1 n] at ih
    simp only [fib_add_two, ih]
    ring

def fib₀ : Nat → Nat
  | 0 => 0
  | n + 1 => (aux n).2
where
  aux : Nat → Nat × Nat
    | 0 => (0, 1)
    | n + 1 =>
      let (a, b) := aux n
      (b, a + b)

#eval List.range 20 |>.map fib₀

lemma fib₀_aux_eq (n : Nat) : fib₀.aux n = (fib n, fib (n + 1)) := by
  induction n
  . simp [fib]
  . next _ ih =>
    simp [fib₀.aux, fib, ih, add_comm]

lemma fib₀_aux_eq_alt (n : Nat) : fib₀.aux n = (fib n, fib (n + 1)) := by
  induction n <;> simp [*, fib, fib₀.aux, add_comm]

theorem fib₀_eq (n : Nat) : fib n = fib₀ n := by
  cases n <;> simp [fib₀, fib₀_aux_eq]

/-
@[csimp] theorem fib₀_eq : fib = fib₀ := by
  ext n
  cases n <;> simp [fib₀, fib₀_aux_eq]
-/

-- #eval fib₀ 1000
-- #eval fib 1000

def fib₁ (n : Nat) :=
  aux n 0 1
where aux : Nat → Nat → Nat → Nat
  | 0,   a, _ => a
  | n+1, a, b => aux n b (a + b)

-- #eval fib₁ 400000

def fib₂ (n : Nat) : Nat := Id.run do
  let mut p := (0, 1)
  for _ in [:n] do
    let (a, b) := p
    p := (b, a + b)
  return p.1

def fib₃ (n : Nat) : Nat := Id.run do
  let mut a := 0
  let mut b := 1
  for _ in [:n] do
    let temp := a
    a := b
    b := temp + a
  return a

#eval fib₃ 100000

def fib₄ (n : Nat) : { m // m = fib n } :=
  let ⟨pair, property⟩ :=
  loop_with_invariant
    (invariant := fun i p => p = (fib i, fib (i + 1)))
    (start_state := (0, 1))
    (init := by simp)
    (next_state := fun i p =>
      let (a, b) := p
      (b, a + b))
    (step := by intro i; simp [fib, add_comm])
    n
  ⟨pair.1, by simp [property]⟩

#eval fib₃ 100
#eval fib₄ 100

def fib₅ (n : Nat) : { m // m = fib n } :=
  for i upto n
    state: p := (0, 1)
    invariant: inv := p = (fib i, fib (i + 1))
    init:= by simp
    next:
      let (a, b) := p
      (b, a + b)
    step:= by intro i; simp [fib, add_comm]
  ⟨p.1, by simp [inv]⟩

-- #eval fib₅ 400000

/-
Notes.

Conventional wisdom:
- Functional programming is easier to reason about.
- Functional proramming is better for concurrency.
- Imperative programming is more efficient.

Lean somewhat simulates an imperative style.
It uses dynamic arrays with destructive updates when they are not shared.
The standard library has arrays and hashmaps.

Two questions:
- Are the imperative features good enough for real applications? Which ones?
- Can we reason about them?

Two big Lean programming projects:
- https://github.com/leanprover/lean4
- https://github.com/leanprover-community/duper

Thesis: we should be able to simulate the functionality of e.g. Dafny.
- SMT proofs can be trusted, reconstructed, or checked.
- We can use varying degrees of interaction.
- Lean can call external functions and link them to reference implementations

-/

-- set_option trace.Meta.Tactic.simp true in
-- set_option trace.Meta.synthInstance true in
example : (x + 2) + 2 = x + 4 := by simp [add_assoc]

#check add_assoc


/-
Copyright (c) 2022 Kyle Miller. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kyle Miller
-/
import set_theory.cardinal.finite

/-!

# Cardinality of finite types

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

The cardinality of a finite type `α` is given by `nat.card α`. This function has
the "junk value" of `0` for infinite types, but to ensure the function has valid
output, one just needs to know that it's possible to produce a `finite` instance
for the type. (Note: we could have defined a `finite.card` that required you to
supply a `finite` instance, but (a) the function would be `noncomputable` anyway
so there is no need to supply the instance and (b) the function would have a more
complicated dependent type that easily leads to "motive not type correct" errors.)

## Implementation notes

Theorems about `nat.card` are sometimes incidentally true for both finite and infinite
types. If removing a finiteness constraint results in no loss in legibility, we remove
it. We generally put such theorems into the `set_theory.cardinal.finite` module.

-/

noncomputable theory
open_locale classical

variables {α β γ : Type*}

/-- There is (noncomputably) an equivalence between a finite type `α` and `fin (nat.card α)`. -/
def finite.equiv_fin (α : Type*) [finite α] : α ≃ fin (nat.card α) :=
begin
  have := (finite.exists_equiv_fin α).some_spec.some,
  rwa nat.card_eq_of_equiv_fin this,
end

/-- Similar to `finite.equiv_fin` but with control over the term used for the cardinality. -/
def finite.equiv_fin_of_card_eq [finite α] {n : ℕ} (h : nat.card α = n) : α ≃ fin n :=
by { subst h, apply finite.equiv_fin }

lemma nat.card_eq (α : Type*) :
  nat.card α = if h : finite α then by exactI @fintype.card α (fintype.of_finite α) else 0 :=
begin
  casesI finite_or_infinite α,
  { letI := fintype.of_finite α,
    simp only [*, nat.card_eq_fintype_card, dif_pos] },
  { simp [*, not_finite_iff_infinite.mpr h] },
end

lemma finite.card_pos_iff [finite α] : 0 < nat.card α ↔ nonempty α :=
begin
  haveI := fintype.of_finite α,
  rw [nat.card_eq_fintype_card, fintype.card_pos_iff],
end

lemma finite.card_pos [finite α] [h : nonempty α] : 0 < nat.card α :=
finite.card_pos_iff.mpr h

namespace finite

lemma cast_card_eq_mk {α : Type*} [finite α] : ↑(nat.card α) = cardinal.mk α :=
cardinal.cast_to_nat_of_lt_aleph_0 (cardinal.lt_aleph_0_of_finite α)

lemma card_eq [finite α] [finite β] : nat.card α = nat.card β ↔ nonempty (α ≃ β) :=
by { haveI := fintype.of_finite α, haveI := fintype.of_finite β, simp [fintype.card_eq] }

lemma card_le_one_iff_subsingleton [finite α] : nat.card α ≤ 1 ↔ subsingleton α :=
by { haveI := fintype.of_finite α, simp [fintype.card_le_one_iff_subsingleton] }

lemma one_lt_card_iff_nontrivial [finite α] : 1 < nat.card α ↔ nontrivial α :=
by { haveI := fintype.of_finite α, simp [fintype.one_lt_card_iff_nontrivial] }

lemma one_lt_card [finite α] [h : nontrivial α] : 1 < nat.card α :=
one_lt_card_iff_nontrivial.mpr h

@[simp] lemma card_option [finite α] : nat.card (option α) = nat.card α + 1 :=
by { haveI := fintype.of_finite α, simp }

lemma card_le_of_injective [finite β] (f : α → β) (hf : function.injective f) :
  nat.card α ≤ nat.card β :=
by { haveI := fintype.of_finite β, haveI := fintype.of_injective f hf,
     simpa using fintype.card_le_of_injective f hf }

lemma card_le_of_embedding [finite β] (f : α ↪ β) : nat.card α ≤ nat.card β :=
card_le_of_injective _ f.injective

lemma card_le_of_surjective [finite α] (f : α → β) (hf : function.surjective f) :
  nat.card β ≤ nat.card α :=
by { haveI := fintype.of_finite α, haveI := fintype.of_surjective f hf,
     simpa using fintype.card_le_of_surjective f hf }

lemma card_eq_zero_iff [finite α] : nat.card α = 0 ↔ is_empty α :=
by { haveI := fintype.of_finite α, simp [fintype.card_eq_zero_iff] }

/-- If `f` is injective, then `nat.card α ≤ nat.card β`. We must also assume
  `nat.card β = 0 → nat.card α = 0` since `nat.card` is defined to be `0` for infinite types. -/
lemma card_le_of_injective' {f : α → β} (hf : function.injective f)
  (h : nat.card β = 0 → nat.card α = 0) : nat.card α ≤ nat.card β :=
(or_not_of_imp h).cases_on (λ h, le_of_eq_of_le h zero_le')
  (λ h, @card_le_of_injective α β (nat.finite_of_card_ne_zero h) f hf)

/-- If `f` is an embedding, then `nat.card α ≤ nat.card β`. We must also assume
  `nat.card β = 0 → nat.card α = 0` since `nat.card` is defined to be `0` for infinite types. -/
lemma card_le_of_embedding' (f : α ↪ β) (h : nat.card β = 0 → nat.card α = 0) :
  nat.card α ≤ nat.card β :=
card_le_of_injective' f.2 h

/-- If `f` is surjective, then `nat.card β ≤ nat.card α`. We must also assume
  `nat.card α = 0 → nat.card β = 0` since `nat.card` is defined to be `0` for infinite types. -/
lemma card_le_of_surjective' {f : α → β} (hf : function.surjective f)
  (h : nat.card α = 0 → nat.card β = 0) : nat.card β ≤ nat.card α :=
(or_not_of_imp h).cases_on (λ h, le_of_eq_of_le h zero_le')
  (λ h, @card_le_of_surjective α β (nat.finite_of_card_ne_zero h) f hf)

/-- NB: `nat.card` is defined to be `0` for infinite types. -/
lemma card_eq_zero_of_surjective {f : α → β} (hf : function.surjective f) (h : nat.card β = 0) :
  nat.card α = 0 :=
begin
  casesI finite_or_infinite β,
  { haveI := card_eq_zero_iff.mp h,
    haveI := function.is_empty f,
    exact nat.card_of_is_empty },
  { haveI := infinite.of_surjective f hf,
    exact nat.card_eq_zero_of_infinite },
end

/-- NB: `nat.card` is defined to be `0` for infinite types. -/
lemma card_eq_zero_of_injective [nonempty α] {f : α → β} (hf : function.injective f)
  (h : nat.card α = 0) : nat.card β = 0 :=
card_eq_zero_of_surjective (function.inv_fun_surjective hf) h

/-- NB: `nat.card` is defined to be `0` for infinite types. -/
lemma card_eq_zero_of_embedding [nonempty α] (f : α ↪ β) (h : nat.card α = 0) : nat.card β = 0 :=
card_eq_zero_of_injective f.2 h

lemma card_sum [finite α] [finite β] : nat.card (α ⊕ β) = nat.card α + nat.card β :=
by { haveI := fintype.of_finite α, haveI := fintype.of_finite β, simp }

lemma card_image_le {s : set α} [finite s] (f : α → β) : nat.card (f '' s) ≤ nat.card s :=
card_le_of_surjective _ set.surjective_onto_image

lemma card_range_le [finite α] (f : α → β) : nat.card (set.range f) ≤ nat.card α :=
card_le_of_surjective _ set.surjective_onto_range

theorem card_subtype_le [finite α] (p : α → Prop) :
  nat.card {x // p x} ≤ nat.card α :=
by { haveI := fintype.of_finite α, simpa using fintype.card_subtype_le p }

theorem card_subtype_lt [finite α] {p : α → Prop} {x : α} (hx : ¬ p x) :
  nat.card {x // p x} < nat.card α :=
by { haveI := fintype.of_finite α, simpa using fintype.card_subtype_lt hx }

end finite

namespace set

lemma card_union_le (s t : set α) : nat.card ↥(s ∪ t) ≤ nat.card s + nat.card t :=
begin
  casesI _root_.finite_or_infinite ↥(s ∪ t) with h h,
  { rw [finite_coe_iff, finite_union, ←finite_coe_iff, ←finite_coe_iff] at h,
    casesI h,
    rw [←cardinal.nat_cast_le, nat.cast_add,
        finite.cast_card_eq_mk, finite.cast_card_eq_mk, finite.cast_card_eq_mk],
    exact cardinal.mk_union_le s t },
  { exact nat.card_eq_zero_of_infinite.trans_le (zero_le _) },
end

end set


/-
Copyright (c) 2022 Yakov Pechersky. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yakov Pechersky, Floris van Doorn

! This file was ported from Lean 3 source module data.pnat.find
! leanprover-community/mathlib commit 207cfac9fcd06138865b5d04f7091e46d9320432
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Data.PNat.Basic

/-!
# Explicit least witnesses to existentials on positive natural numbers

Implemented via calling out to `Nat.find`.

-/

namespace PNat

variable {p q : ℕ+ → Prop} [DecidablePred p] [DecidablePred q] (h : ∃ n, p n)

instance decidablePredExistsNat : DecidablePred fun n' : ℕ => ∃ (n : ℕ+)(_ : n' = n), p n :=
  fun n' =>
  decidable_of_iff' (∃ h : 0 < n', p ⟨n', h⟩) <|
    Subtype.exists.trans <| by
      simp_rw [mk_coe, @exists_comm (_ < _) (_ = _), exists_prop, exists_eq_left']
#align pnat.decidable_pred_exists_nat PNat.decidablePredExistsNat

--include h

/-- The `PNat` version of `Nat.findX` -/
protected def findX : { n // p n ∧ ∀ m : ℕ+, m < n → ¬p m } := by
  have : ∃ (n' : ℕ)(n : ℕ+)(_ : n' = n), p n := Exists.elim h fun n hn => ⟨n, n, rfl, hn⟩
  have n := Nat.findX this
  refine' ⟨⟨n, _⟩, _, fun m hm pm => _⟩
  · obtain ⟨n', hn', -⟩ := n.prop.1
    rw [hn']
    exact n'.prop
  · obtain ⟨n', hn', pn'⟩ := n.prop.1
    simpa [hn', Subtype.coe_eta] using pn'
  · exact n.prop.2 m hm ⟨m, rfl, pm⟩
#align pnat.find_x PNat.findX

/-- If `p` is a (decidable) predicate on `ℕ+` and `hp : ∃ (n : ℕ+), p n` is a proof that
there exists some positive natural number satisfying `p`, then `PNat.find hp` is the
smallest positive natural number satisfying `p`. Note that `PNat.find` is protected,
meaning that you can't just write `find`, even if the `PNat` namespace is open.

The API for `PNat.find` is:

* `PNat.find_spec` is the proof that `PNat.find hp` satisfies `p`.
* `PNat.find_min` is the proof that if `m < PNat.find hp` then `m` does not satisfy `p`.
* `PNat.find_min'` is the proof that if `m` does satisfy `p` then `PNat.find hp ≤ m`.
-/
protected def find : ℕ+ :=
  PNat.findX h
#align pnat.find PNat.find

protected theorem find_spec : p (PNat.find h) :=
  (PNat.findX h).prop.left
#align pnat.find_spec PNat.find_spec

protected theorem find_min : ∀ {m : ℕ+}, m < PNat.find h → ¬p m :=
  @(PNat.findX h).prop.right
#align pnat.find_min PNat.find_min

protected theorem find_min' {m : ℕ+} (hm : p m) : PNat.find h ≤ m :=
  le_of_not_lt fun l => PNat.find_min h l hm
#align pnat.find_min' PNat.find_min'

variable {n m : ℕ+}

theorem find_eq_iff : PNat.find h = m ↔ p m ∧ ∀ n < m, ¬p n := by
  constructor
  · rintro rfl
    exact ⟨PNat.find_spec h, fun _ => PNat.find_min h⟩
  · rintro ⟨hm, hlt⟩
    exact le_antisymm (PNat.find_min' h hm) (not_lt.1 <| imp_not_comm.1 (hlt _) <| PNat.find_spec h)
#align pnat.find_eq_iff PNat.find_eq_iff

@[simp]
theorem find_lt_iff (n : ℕ+) : PNat.find h < n ↔ ∃ m < n, p m :=
  ⟨fun h2 => ⟨PNat.find h, h2, PNat.find_spec h⟩, fun ⟨_, hmn, hm⟩ =>
    (PNat.find_min' h hm).trans_lt hmn⟩
#align pnat.find_lt_iff PNat.find_lt_iff

@[simp]
theorem find_le_iff (n : ℕ+) : PNat.find h ≤ n ↔ ∃ m ≤ n, p m := by
  simp only [exists_prop, ← lt_add_one_iff, find_lt_iff]
#align pnat.find_le_iff PNat.find_le_iff

@[simp]
theorem le_find_iff (n : ℕ+) : n ≤ PNat.find h ↔ ∀ m < n, ¬p m := by
  simp only [← not_lt, find_lt_iff, not_exists, not_and]
#align pnat.le_find_iff PNat.le_find_iff

@[simp]
theorem lt_find_iff (n : ℕ+) : n < PNat.find h ↔ ∀ m ≤ n, ¬p m := by
  simp only [← add_one_le_iff, le_find_iff, add_le_add_iff_right]
#align pnat.lt_find_iff PNat.lt_find_iff

@[simp]
theorem find_eq_one : PNat.find h = 1 ↔ p 1 := by simp [find_eq_iff]
#align pnat.find_eq_one PNat.find_eq_one

-- porting notes: deleted `@[simp]` to satisfy the linter becuase `le_find_iff` is more general
theorem one_le_find : 1 < PNat.find h ↔ ¬p 1 :=
  not_iff_not.mp <| by simp
#align pnat.one_le_find PNat.one_le_find

theorem find_mono (h : ∀ n, q n → p n) {hp : ∃ n, p n} {hq : ∃ n, q n} :
    PNat.find hp ≤ PNat.find hq :=
  PNat.find_min' _ (h _ (PNat.find_spec hq))
#align pnat.find_mono PNat.find_mono

theorem find_le {h : ∃ n, p n} (hn : p n) : PNat.find h ≤ n :=
  (PNat.find_le_iff _ _).2 ⟨n, le_rfl, hn⟩
#align pnat.find_le PNat.find_le

theorem find_comp_succ (h : ∃ n, p n) (h₂ : ∃ n, p (n + 1)) (h1 : ¬p 1) :
    PNat.find h = PNat.find h₂ + 1 := by
  refine' (find_eq_iff _).2 ⟨PNat.find_spec h₂, fun n => PNat.recOn n _ _⟩
  · simp [h1]
  intro m _ hm
  simp only [add_lt_add_iff_right, lt_find_iff] at hm
  exact hm _ le_rfl
#align pnat.find_comp_succ PNat.find_comp_succ

end PNat


/-
Copyright (c) 2021 Yaël Dillies. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yaël Dillies
-/
import data.fin.basic
import order.succ_pred.basic

/-!
# Successors and predecessors of naturals

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

In this file, we show that `ℕ` is both an archimedean `succ_order` and an archimedean `pred_order`.
-/

open function order

namespace nat

@[reducible] -- so that Lean reads `nat.succ` through `succ_order.succ`
instance : succ_order ℕ :=
{ succ := succ,
  ..succ_order.of_succ_le_iff succ (λ a b, iff.rfl) }

@[reducible] -- so that Lean reads `nat.pred` through `pred_order.pred`
instance : pred_order ℕ :=
{ pred := pred,
  pred_le := pred_le,
  min_of_le_pred := λ a ha, begin
    cases a,
    { exact is_min_bot },
    { exact (not_succ_le_self _ ha).elim }
  end,
  le_pred_of_lt := λ a b h, begin
    cases b,
    { exact (a.not_lt_zero h).elim },
    { exact le_of_succ_le_succ h }
  end,
  le_of_pred_lt := λ a b h, begin
    cases a,
    { exact b.zero_le },
    { exact h }
  end }

@[simp] lemma succ_eq_succ : order.succ = succ := rfl
@[simp] lemma pred_eq_pred : order.pred = pred := rfl

lemma succ_iterate (a : ℕ) : ∀ n, succ^[n] a = a + n
| 0       := rfl
| (n + 1) := by { rw [function.iterate_succ', add_succ], exact congr_arg _ n.succ_iterate }

lemma pred_iterate (a : ℕ) : ∀ n, pred^[n] a = a - n
| 0       := rfl
| (n + 1) := by { rw [function.iterate_succ', sub_succ], exact congr_arg _ n.pred_iterate }

instance : is_succ_archimedean ℕ :=
⟨λ a b h, ⟨b - a, by rw [succ_eq_succ, succ_iterate, add_tsub_cancel_of_le h]⟩⟩

instance : is_pred_archimedean ℕ :=
⟨λ a b h, ⟨b - a, by rw [pred_eq_pred, pred_iterate, tsub_tsub_cancel_of_le h]⟩⟩

/-! ### Covering relation -/

protected lemma covby_iff_succ_eq {m n : ℕ} : m ⋖ n ↔ m + 1 = n := succ_eq_iff_covby.symm

end nat

@[simp, norm_cast] lemma fin.coe_covby_iff {n : ℕ} {a b : fin n} : (a : ℕ) ⋖ b ↔ a ⋖ b :=
and_congr_right' ⟨λ h c hc, h hc, λ h c ha hb, @h ⟨c, hb.trans b.prop⟩ ha hb⟩

alias fin.coe_covby_iff ↔ _ covby.coe_fin


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

import order.bounded_lattice
import data.set.intervals.basic

/-!
# Intervals in Lattices

In this file, we provide instances of lattice structures on intervals within lattices.
Some of them depend on the order of the endpoints of the interval, and thus are not made
global instances. These are probably not all of the lattice instances that could be placed on these
intervals, but more can be added easily along the same lines when needed.

## Main definitions
In the following, `*` can represent either `c`, `o`, or `i`.
  * `set.Ic*.semilattice_inf_bot`
  * `set.Ii*.semillatice_inf`
  * `set.I*c.semilattice_sup_top`
  * `set.I*c.semillatice_inf`
  * `set.Iic.bounded_lattice`, within a `bounded_lattice`
  * `set.Ici.bounded_lattice`, within a `bounded_lattice`

-/

variable {α : Type*}

namespace set

namespace Ico

variables {a b : α}

instance [semilattice_inf α] : semilattice_inf (Ico a b) :=
subtype.semilattice_inf (λ x y hx hy, ⟨le_inf hx.1 hy.1, lt_of_le_of_lt inf_le_left hx.2⟩)

/-- `Ico a b` has a bottom element whenever `a < b`. -/
def order_bot [partial_order α] (h : a < b) : order_bot (Ico a b) :=
{ bot := ⟨a, ⟨le_refl a, h⟩⟩,
  bot_le := λ x, x.prop.1,
  .. (subtype.partial_order _) }

/-- `Ico a b` is a `semilattice_inf_bot` whenever `a < b`. -/
def semilattice_inf_bot [semilattice_inf α] (h : a < b) : semilattice_inf_bot (Ico a b) :=
{ .. (Ico.semilattice_inf), .. (Ico.order_bot h) }

end Ico

namespace Iio

instance [semilattice_inf α] {a : α} : semilattice_inf (Iio a) :=
subtype.semilattice_inf (λ x y hx hy, lt_of_le_of_lt inf_le_left hx)

end Iio

namespace Ioc

variables {a b : α}

instance [semilattice_sup α] : semilattice_sup (Ioc a b) :=
subtype.semilattice_sup (λ x y hx hy, ⟨lt_of_lt_of_le hx.1 le_sup_left, sup_le hx.2 hy.2⟩)

/-- `Ioc a b` has a top element whenever `a < b`. -/
def order_top [partial_order α] (h : a < b) : order_top (Ioc a b) :=
{ top := ⟨b, ⟨h, le_refl b⟩⟩,
  le_top := λ x, x.prop.2,
  .. (subtype.partial_order _) }

/-- `Ioc a b` is a `semilattice_sup_top` whenever `a < b`. -/
def semilattice_sup_top [semilattice_sup α] (h : a < b) : semilattice_sup_top (Ioc a b) :=
{ .. (Ioc.semilattice_sup), .. (Ioc.order_top h) }

end Ioc

namespace Iio

instance [semilattice_sup α] {a : α} : semilattice_sup (Ioi a) :=
subtype.semilattice_sup (λ x y hx hy, lt_of_lt_of_le hx le_sup_left)

end Iio

namespace Iic

variables {a : α}

instance [semilattice_inf α] : semilattice_inf (Iic a) :=
subtype.semilattice_inf (λ x y hx hy, le_trans inf_le_left hx)

instance [semilattice_sup α] : semilattice_sup (Iic a) :=
subtype.semilattice_sup (λ x y hx hy, sup_le hx hy)

instance [lattice α] : lattice (Iic a) :=
{ .. (Iic.semilattice_inf),
  .. (Iic.semilattice_sup) }

instance [partial_order α] : order_top (Iic a) :=
{ top := ⟨a, le_refl a⟩,
  le_top := λ x, x.prop,
  .. (subtype.partial_order _) }

@[simp] lemma coe_top [partial_order α] {a : α} : ↑(⊤ : Iic a) = a := rfl

instance [semilattice_inf α] : semilattice_inf_top (Iic a) :=
{ .. (Iic.semilattice_inf),
  .. (Iic.order_top) }

instance [semilattice_sup α] : semilattice_sup_top (Iic a) :=
{ .. (Iic.semilattice_sup),
  .. (Iic.order_top) }

instance [order_bot α] : order_bot (Iic a) :=
{ bot := ⟨⊥, bot_le⟩,
  bot_le := λ ⟨_,_⟩, subtype.mk_le_mk.2 bot_le,
  .. (infer_instance : partial_order (Iic a)) }

@[simp] lemma coe_bot [order_bot α] {a : α} : ↑(⊥ : Iic a) = (⊥ : α) := rfl

instance [semilattice_inf_bot α] : semilattice_inf_bot (Iic a) :=
{ .. (Iic.semilattice_inf),
  .. (Iic.order_bot) }

instance [semilattice_sup_bot α] : semilattice_sup_bot (Iic a) :=
{ .. (Iic.semilattice_sup),
  .. (Iic.order_bot) }

instance [bounded_lattice α] : bounded_lattice (Iic a) :=
{ .. (Iic.order_top),
  .. (Iic.order_bot),
  .. (Iic.lattice) }
end Iic

namespace Ici

variables {a : α}

instance [semilattice_inf α] : semilattice_inf (Ici a) :=
subtype.semilattice_inf (λ x y hx hy, le_inf hx hy)

instance [semilattice_sup α] : semilattice_sup (Ici a) :=
subtype.semilattice_sup (λ x y hx hy, le_trans hx le_sup_left)

instance [lattice α] : lattice (Ici a) :=
{ .. (Ici.semilattice_inf),
  .. (Ici.semilattice_sup) }

instance [partial_order α] : order_bot (Ici a) :=
{ bot := ⟨a, le_refl a⟩,
  bot_le := λ x, x.prop,
  .. (subtype.partial_order _) }

@[simp] lemma coe_bot [partial_order α] {a : α} : ↑(⊥ : Ici a) = a := rfl

instance [semilattice_inf α] : semilattice_inf_bot (Ici a) :=
{ .. (Ici.semilattice_inf),
  .. (Ici.order_bot) }

instance [semilattice_sup α] : semilattice_sup_bot (Ici a) :=
{ .. (Ici.semilattice_sup),
  .. (Ici.order_bot) }

instance [order_top α] : order_top (Ici a) :=
{ top := ⟨⊤, le_top⟩,
  le_top := λ ⟨_,_⟩, subtype.mk_le_mk.2 le_top,
  .. (infer_instance : partial_order (Ici a)) }

@[simp] lemma coe_top [order_top α] {a : α} : ↑(⊤ : Ici a) = (⊤ : α) := rfl

instance [semilattice_sup_top α] : semilattice_sup_top (Ici a) :=
{ .. (Ici.semilattice_sup),
  .. (Ici.order_top) }

instance [semilattice_inf_top α] : semilattice_inf_top (Ici a) :=
{ .. (Ici.semilattice_inf),
  .. (Ici.order_top) }

instance [bounded_lattice α] : bounded_lattice (Ici a) :=
{ .. (Ici.order_top),
  .. (Ici.order_bot),
  .. (Ici.lattice) }

end Ici

namespace Icc

instance [semilattice_inf α] {a b : α} : semilattice_inf (Icc a b) :=
subtype.semilattice_inf (λ x y hx hy, ⟨le_inf hx.1 hy.1, le_trans inf_le_left hx.2⟩)

instance [semilattice_sup α] {a b : α} : semilattice_sup (Icc a b) :=
subtype.semilattice_sup (λ x y hx hy, ⟨le_trans hx.1 le_sup_left, sup_le hx.2 hy.2⟩)

instance [lattice α] {a b : α} : lattice (Icc a b) :=
{ .. (Icc.semilattice_inf),
  .. (Icc.semilattice_sup) }

/-- `Icc a b` has a bottom element whenever `a ≤ b`. -/
def order_bot [partial_order α] {a b : α} (h : a ≤ b) : order_bot (Icc a b) :=
{ bot := ⟨a, ⟨le_refl a, h⟩⟩,
  bot_le := λ x, x.prop.1,
  .. (subtype.partial_order _) }

/-- `Icc a b` has a top element whenever `a ≤ b`. -/
def order_top [partial_order α] {a b : α} (h : a ≤ b) : order_top (Icc a b) :=
{ top := ⟨b, ⟨h, le_refl b⟩⟩,
  le_top := λ x, x.prop.2,
  .. (subtype.partial_order _) }

/-- `Icc a b` is a `semilattice_inf_bot` whenever `a ≤ b`. -/
def semilattice_inf_bot [semilattice_inf α] {a b : α} (h : a ≤ b) :
  semilattice_inf_bot (Icc a b) :=
{ .. (Icc.order_bot h),
  .. (Icc.semilattice_inf) }

/-- `Icc a b` is a `semilattice_inf_top` whenever `a ≤ b`. -/
def semilattice_inf_top [semilattice_inf α] {a b : α} (h : a ≤ b) :
  semilattice_inf_top (Icc a b) :=
{ .. (Icc.order_top h),
  .. (Icc.semilattice_inf) }

/-- `Icc a b` is a `semilattice_sup_bot` whenever `a ≤ b`. -/
def semilattice_sup_bot [semilattice_sup α] {a b : α} (h : a ≤ b) :
  semilattice_sup_bot (Icc a b) :=
{ .. (Icc.order_bot h),
  .. (Icc.semilattice_sup) }

/-- `Icc a b` is a `semilattice_sup_top` whenever `a ≤ b`. -/
def semilattice_sup_top [semilattice_sup α] {a b : α} (h : a ≤ b) :
  semilattice_sup_top (Icc a b) :=
{ .. (Icc.order_top h),
  .. (Icc.semilattice_sup) }

/-- `Icc a b` is a `bounded_lattice` whenever `a ≤ b`. -/
def bounded_lattice [lattice α] {a b : α} (h : a ≤ b) :
  bounded_lattice (Icc a b) :=
{ .. (Icc.semilattice_inf_bot h),
  .. (Icc.semilattice_sup_top h) }

end Icc

end set


/-
Copyright (c) 2021 Floris van Doorn. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Floris van Doorn, Yury Kudryashov
-/
import order.symm_diff
import tactic.monotonicity.basic

/-!
# Implication and equivalence as operations on a boolean algebra

In this file we define `lattice.imp` (notation: `a ⇒ₒ b`) and `lattice.biimp` (notation: `a ⇔ₒ b`)
to be the implication and equivalence as operations on a boolean algebra. More precisely, we put
`a ⇒ₒ b = aᶜ ⊔ b` and `a ⇔ₒ b = (a ⇒ₒ b) ⊓ (b ⇒ₒ a)`. Equivalently, `a ⇒ₒ b = (a \ b)ᶜ` and
`a ⇔ₒ b = (a Δ b)ᶜ`. For propositions these operations are equal to the usual implication and `iff`.
-/

variables {α β : Type*}

namespace lattice

/-- Implication as a binary operation on a boolean algebra. -/
def imp [has_compl α] [has_sup α] (a b : α) : α := aᶜ ⊔ b

infix ` ⇒ₒ `:65 := lattice.imp

/-- Equivalence as a binary operation on a boolean algebra. -/
def biimp [has_compl α] [has_sup α] [has_inf α] (a b : α) : α := (a ⇒ₒ b) ⊓ (b ⇒ₒ a)

infix ` ⇔ₒ `:60 := lattice.biimp

@[simp] lemma imp_eq_arrow (p q : Prop) : p ⇒ₒ q = (p → q) := propext imp_iff_not_or.symm

@[simp] lemma biimp_eq_iff (p q : Prop) : p ⇔ₒ q = (p ↔ q) := by simp [biimp, ← iff_def]

variables [boolean_algebra α] {a b c d : α}

@[simp] lemma compl_imp (a b : α) : (a ⇒ₒ b)ᶜ = a \ b := by simp [imp, sdiff_eq]

lemma compl_sdiff (a b : α) : (a \ b)ᶜ = a ⇒ₒ b := by rw [← compl_imp, compl_compl]

@[mono] lemma imp_mono (h₁ : a ≤ b) (h₂ : c ≤ d) : b ⇒ₒ c ≤ a ⇒ₒ d :=
sup_le_sup (compl_le_compl h₁) h₂

lemma inf_imp_eq (a b c : α) : a ⊓ (b ⇒ₒ c) = (a ⇒ₒ b) ⇒ₒ (a ⊓ c) :=
by unfold imp; simp [inf_sup_left]

@[simp] lemma imp_eq_top_iff : (a ⇒ₒ b = ⊤) ↔ a ≤ b :=
by rw [← compl_sdiff, compl_eq_top, sdiff_eq_bot_iff]

@[simp] lemma imp_eq_bot_iff : (a ⇒ₒ b = ⊥) ↔ (a = ⊤ ∧ b = ⊥) := by simp [imp]

@[simp] lemma imp_bot (a : α) : a ⇒ₒ ⊥ = aᶜ := sup_bot_eq

@[simp] lemma top_imp (a : α) : ⊤ ⇒ₒ a = a := by simp [imp]

@[simp] lemma bot_imp (a : α) : ⊥ ⇒ₒ a = ⊤ := imp_eq_top_iff.2 bot_le

@[simp] lemma imp_top (a : α) : a ⇒ₒ ⊤ = ⊤ := imp_eq_top_iff.2 le_top

@[simp] lemma imp_self (a : α) : a ⇒ₒ a = ⊤ := compl_sup_eq_top

@[simp] lemma compl_imp_compl (a b : α) : aᶜ ⇒ₒ bᶜ = b ⇒ₒ a := by simp [imp, sup_comm]

lemma imp_inf_le {α : Type*} [boolean_algebra α] (a b : α) : (a ⇒ₒ b) ⊓ a ≤ b :=
by { unfold imp, rw [inf_sup_right], simp }

lemma inf_imp_eq_imp_imp (a b c : α) : ((a ⊓ b) ⇒ₒ c) = (a ⇒ₒ (b ⇒ₒ c)) := by simp [imp, sup_assoc]

lemma le_imp_iff : a ≤ (b ⇒ₒ c) ↔ a ⊓ b ≤ c :=
by rw [imp, sup_comm, is_compl_compl.le_sup_right_iff_inf_left_le]

lemma biimp_mp (a b : α) : (a ⇔ₒ b) ≤ (a ⇒ₒ b) := inf_le_left

lemma biimp_mpr (a b : α) : (a ⇔ₒ b) ≤ (b ⇒ₒ a) := inf_le_right

lemma biimp_comm (a b : α) : (a ⇔ₒ b) = (b ⇔ₒ a) :=
by {unfold lattice.biimp, rw inf_comm}

@[simp] lemma biimp_eq_top_iff : a ⇔ₒ b = ⊤ ↔ a = b :=
by simp [biimp, ← le_antisymm_iff]

@[simp] lemma biimp_self (a : α) : a ⇔ₒ a = ⊤ := biimp_eq_top_iff.2 rfl

lemma biimp_symm : a ≤ (b ⇔ₒ c) ↔ a ≤ (c ⇔ₒ b) := by rw biimp_comm

lemma compl_symm_diff (a b : α) : (a Δ b)ᶜ = a ⇔ₒ b :=
by simp only [biimp, imp, symm_diff, sdiff_eq, compl_sup, compl_inf, compl_compl]

lemma compl_biimp (a b : α) : (a ⇔ₒ b)ᶜ = a Δ b := by rw [← compl_symm_diff, compl_compl]

@[simp] lemma compl_biimp_compl : aᶜ ⇔ₒ bᶜ = a ⇔ₒ b := by simp [biimp, inf_comm]

end lattice