import QL.FOL.coding QL.FOL.deduction

universes u v

namespace fol
open_locale logic_symbol aclogic

variables (L L₁ L₂ : language.{u}) (μ : Type v) (μ₁ : Type*) (μ₂ : Type*) (m m₁ m₂ n : ℕ)

namespace Tait

inductive subformula (μ : Type v) : ℕ → Type (max u v)
| verum        {n} : subformula n
| falsum       {n} : subformula n
| relation     {n} : ∀ {p}, L.pr p → (fin p → subterm L μ n) → subformula n
| neg_relation {n} : ∀ {p}, L.pr p → (fin p → subterm L μ n) → subformula n
| and          {n} : subformula n → subformula n → subformula n
| or           {n} : subformula n → subformula n → subformula n
| fal          {n} : subformula (n + 1) → subformula n
| ex           {n} : subformula (n + 1) → subformula n

@[reducible] def formula := subformula L μ 0

@[reducible] def bounded_subformula (m n : ℕ) := subformula L (fin m) n

@[reducible] def bounded_formula (m : ℕ) := bounded_subformula L m 0

@[reducible] def sentence := bounded_formula L 0

variables {L μ n}

namespace subformula

instance : has_univ_quantifier' (subformula L μ) := ⟨@fal L μ⟩

instance : has_exists_quantifier' (subformula L μ) := ⟨@ex L μ⟩

attribute [pattern]  has_sup.sup has_inf.inf has_univ_quantifier.univ has_exists_quantifier.ex

@[simp] def not : ∀ {n}, subformula L μ n → subformula L μ n
| n verum              := falsum
| n falsum             := verum
| n (relation r v)     := neg_relation r v
| n (neg_relation r v) := relation r v
| n (and p q)          := or (not p) (not q)
| n (or p q)           := and (not p) (not q)
| n (fal p)            := ex (not p)
| n (ex p)             := fal (not p)

def imply (p q : subformula L μ n) := or (not p) q

instance : has_logic_symbol (subformula L μ n) := Tait.logic_simbol_default (subformula L μ n) verum falsum not and or

lemma verum_eq : @verum L μ n = ⊤ := rfl
lemma falsum_eq : @falsum L μ n = ⊥ := rfl
lemma and_eq : @and L μ n = has_inf.inf := rfl
lemma or_eq : @or L μ n = has_sup.sup := rfl
lemma not_eq : @not L μ n = has_negation.neg := rfl
lemma imply_eq : @imply L μ n = has_arrow.arrow := rfl
lemma imply_def (p q : subformula L μ n) : (p ⟶ q) = (∼p) ⊔ q := by simp[←imply_eq, imply]; refl
lemma fal_eq : @fal L μ n = has_univ_quantifier'.univ := rfl
lemma ex_eq : @ex L μ n = has_exists_quantifier'.ex := rfl

@[simp] lemma and.inj' (p₁ q₁ p₂ q₂ : subformula L μ n) : p₁ ⊓ p₂ = q₁ ⊓ q₂ ↔ p₁ = q₁ ∧ p₂ = q₂ :=
by simp[has_inf.inf]

@[simp] lemma or.inj' (p₁ q₁ p₂ q₂ : subformula L μ n) : p₁ ⊔ p₂ = q₁ ⊔ q₂ ↔ p₁ = q₁ ∧ p₂ = q₂ :=
by simp[has_sup.sup]

@[simp] lemma fal.inj' (p q : subformula L μ (n + 1)) : ∀'p = ∀'q ↔ p = q := ⟨fal.inj, congr_arg _⟩

@[simp] lemma ex.inj' (p q : subformula L μ (n + 1)) : ∃'p = ∃'q ↔ p = q :=
by simp[has_exists_quantifier'.ex]

@[simp] lemma not_falsum : ∼(⊥ : subformula L μ n) = ⊤ := rfl

@[simp] lemma not_verum : ∼(⊤ : subformula L μ n) = ⊥ := rfl

@[simp] lemma not_relation {k} (r : L.pr k) (v : fin k → subterm L μ n) : ∼(relation r v) = neg_relation r v := rfl

@[simp] lemma not_neg_relation {k} (r : L.pr k) (v : fin k → subterm L μ n) : ∼(neg_relation r v) = relation r v := rfl

@[simp] lemma not_and (p q : subformula L μ n) : ∼(p ⊓ q) = ∼p ⊔ ∼q := rfl

@[simp] lemma not_or (p q : subformula L μ n) : ∼(p ⊔ q) = ∼p ⊓ ∼q := rfl

@[simp] lemma not_fal (p : subformula L μ (n + 1)) : ∼(∀'p) = ∃'∼p := rfl

@[simp] lemma not_ex (p : subformula L μ (n + 1)) : ∼(∃'p) = ∀'∼p := rfl

@[simp] def complexity : Π {n}, subformula L μ n → ℕ
| n verum              := 0
| n falsum             := 0
| n (relation r v)     := 0
| n (neg_relation r v) := 0
| n (and p q)          := max p.complexity q.complexity + 1
| n (or p q)           := max p.complexity q.complexity + 1
| n (fal p)            := p.complexity + 1
| n (ex p)             := p.complexity + 1

@[simp] lemma complexity_top : (⊤ : subformula L μ n).complexity = 0 := by refl

@[simp] lemma complexity_bot : (⊥ : subformula L μ n).complexity = 0 := by refl

@[simp] lemma complexity_and (p q : subformula L μ n) : (p ⊓ q).complexity = max p.complexity q.complexity + 1 := by refl

@[simp] lemma complexity_or (p q : subformula L μ n) : (p ⊔ q).complexity = max p.complexity q.complexity + 1 := by refl

@[simp] lemma complexity_fal (p : subformula L μ (n + 1)) : (∀'p).complexity = p.complexity + 1 := by refl

@[simp] lemma complexity_ex (p : subformula L μ (n + 1)) : (∃'p).complexity = p.complexity + 1 := by refl

def cases' {C : Π n, subformula L μ n → Sort*}
  (hverum : Π {n : ℕ}, C n ⊤)
  (hfalsum : Π {n : ℕ}, C n ⊥)
  (hrelation : Π {n l : ℕ} (r : L.pr l) (v : fin l → subterm L μ n), C n (relation r v))
  (hneg_relation : Π {n l : ℕ} (r : L.pr l) (v : fin l → subterm L μ n), C n (neg_relation r v))
  (hand : Π {n : ℕ} (p q : subformula L μ n), C n (p ⊓ q))
  (hor : Π {n : ℕ} (p q : subformula L μ n), C n (p ⊔ q))
  (hfal : Π {n : ℕ} (p : subformula L μ (n + 1)), C n (∀'p))
  (hex : Π {n : ℕ} (p : subformula L μ (n + 1)), C n (∃'p)) :
  Π {n : ℕ} (p : subformula L μ n), C n p
| n verum              := hverum
| n falsum             := hfalsum
| n (relation r v)     := hrelation r v
| n (neg_relation r v) := hneg_relation r v
| n (and p q)          := hand p q
| n (or p q)           := hor p q
| n (fal p)            := hfal p
| n (ex p)             := hex p

@[elab_as_eliminator]
protected def cases_on' {C : Π n, subformula L μ n → Sort*}
  {n : ℕ} (p : subformula L μ n)
  (verum : Π {n : ℕ}, C n ⊤)
  (falsum : Π {n : ℕ}, C n ⊥)
  (relation : Π {n l : ℕ} (r : L.pr l) (v : fin l → subterm L μ n), C n (relation r v))
  (neg_relation : Π {n l : ℕ} (r : L.pr l) (v : fin l → subterm L μ n), C n (neg_relation r v))
  (and : Π {n : ℕ} (p q : subformula L μ n), C n (p ⊓ q))
  (or : Π {n : ℕ} (p q : subformula L μ n), C n (p ⊔ q))
  (fal : Π {n : ℕ} (p : subformula L μ (n + 1)), C n (∀'p))
  (ex : Π {n : ℕ} (p : subformula L μ (n + 1)), C n (∃'p)) :
  C n p :=
cases' @verum @falsum @relation @neg_relation @and @or @fal @ex p

def ind {C : Π n, subformula L μ n → Sort*}
  (hverum : Π {n : ℕ}, C n ⊤)
  (hfalsum : Π {n : ℕ}, C n ⊥)
  (hrelation : Π {n l : ℕ} (r : L.pr l) (v : fin l → subterm L μ n), C n (relation r v))
  (hneg_relation : Π {n l : ℕ} (r : L.pr l) (v : fin l → subterm L μ n), C n (neg_relation r v))
  (hand : Π {n : ℕ} (p q : subformula L μ n), C n p → C n q → C n (p ⊓ q))
  (hor : Π {n : ℕ} (p q : subformula L μ n), C n p → C n q → C n (p ⊔ q))
  (hfal : Π {n : ℕ} (p : subformula L μ (n + 1)), C (n + 1) p → C n (∀'p))
  (hex : Π {n : ℕ} (p : subformula L μ (n + 1)), C (n + 1) p → C n (∃'p)) :
  Π {n : ℕ} (p : subformula L μ n), C n p
| n verum              := hverum
| n falsum             := hfalsum
| n (relation r v)     := hrelation r v
| n (neg_relation r v) := hneg_relation r v
| n (and p q)          := hand p q (ind p) (ind q)
| n (or p q)           := hor p q (ind p) (ind q)
| n (fal p)            := hfal p (ind p)
| n (ex p)             := hex p (ind p)

--普通に帰納法をすると論理記号で表示されないから
@[elab_as_eliminator]
protected def ind_on {C : Π n, subformula L μ n → Sort*}
  {n : ℕ} (p : subformula L μ n)
  (verum : Π {n : ℕ}, C n ⊤)
  (falsum : Π {n : ℕ}, C n ⊥)
  (relation : Π {n l : ℕ} (r : L.pr l) (v : fin l → subterm L μ n), C n (relation r v))
  (neg_relation : Π {n l : ℕ} (r : L.pr l) (v : fin l → subterm L μ n), C n (neg_relation r v))
  (and : Π {n : ℕ} (p q : subformula L μ n), C n p → C n q → C n (p ⊓ q))
  (or : Π {n : ℕ} (p q : subformula L μ n), C n p → C n q → C n (p ⊔ q))
  (fal : Π {n : ℕ} (p : subformula L μ (n + 1)), C (n + 1) p → C n (∀'p))
  (ex : Π {n : ℕ} (p : subformula L μ (n + 1)), C (n + 1) p → C n (∃'p)) :
  C n p :=
ind @verum @falsum @relation @neg_relation @and @or @fal @ex p

def ind_succ {C : Π n, subformula L μ (n + 1) → Sort*}
  (hverum : Π {n : ℕ}, C n ⊤)
  (hfalsum : Π {n : ℕ}, C n ⊥)
  (hrelation : Π {n l : ℕ} (r : L.pr l) (v : fin l → subterm L μ (n + 1)), C n (relation r v))
  (hneg_relation : Π {n l : ℕ} (r : L.pr l) (v : fin l → subterm L μ (n + 1)), C n (neg_relation r v))
  (hand : Π {n : ℕ} (p q : subformula L μ (n + 1)), C n p → C n q → C n (p ⊓ q))
  (hor : Π {n : ℕ} (p q : subformula L μ (n + 1)), C n p → C n q → C n (p ⊔ q))
  (hfal : Π {n : ℕ} (p : subformula L μ (n + 1 + 1)), C (n + 1) p → C n (∀'p))
  (hex : Π {n : ℕ} (p : subformula L μ (n + 1 + 1)), C (n + 1) p → C n (∃'p)) :
  Π {n : ℕ} (p : subformula L μ (n + 1)), C n p
| n verum              := hverum
| n falsum             := hfalsum
| n (relation r v)     := hrelation r v
| n (neg_relation r v) := hneg_relation r v
| n (and p q)          := hand p q (ind_succ p) (ind_succ q)
| n (or p q)           := hor p q (ind_succ p) (ind_succ q)
| n (fal p)            := hfal p (ind_succ p)
| n (ex p)             := hex p (ind_succ p)
using_well_founded {rel_tac := λ _ _, `[exact ⟨_, measure_wf (λ x, x.2.complexity)⟩]}

@[elab_as_eliminator]
def ind_succ_on {C : Π n, subformula L μ (n + 1) → Sort*}
  {n : ℕ} (p : subformula L μ (n + 1))
  (verum : Π {n : ℕ}, C n ⊤)
  (falsum : Π {n : ℕ}, C n ⊥)
  (relation : Π {n l : ℕ} (r : L.pr l) (v : fin l → subterm L μ (n + 1)), C n (relation r v))
  (neg_relation : Π {n l : ℕ} (r : L.pr l) (v : fin l → subterm L μ (n + 1)), C n (neg_relation r v))
  (and : Π {n : ℕ} (p q : subformula L μ (n + 1)), C n p → C n q → C n (p ⊓ q))
  (or : Π {n : ℕ} (p q : subformula L μ (n + 1)), C n p → C n q → C n (p ⊔ q))
  (fal : Π {n : ℕ} (p : subformula L μ (n + 1 + 1)), C (n + 1) p → C n (∀'p))
  (ex : Π {n : ℕ} (p : subformula L μ (n + 1 + 1)), C (n + 1) p → C n (∃'p)) :
  C n p :=
ind_succ @verum @falsum @relation @neg_relation @and @or @fal @ex p

section rew
variables {μ₁ μ₂} (s : μ₁ → subterm L μ₂ n) (f : μ₁ → μ₂)

@[simp] def rew' {μ₁ μ₂} : Π {n}, (μ₁ → subterm L μ₂ n) → subformula L μ₁ n → subformula L μ₂ n
| n s verum              := verum
| n s falsum             := falsum
| n s (relation p v)     := relation p (λ i, subterm.rew s (v i))
| n s (neg_relation p v) := neg_relation p (λ i, subterm.rew s (v i))
| n s (and p q)          := and (p.rew' s) (q.rew' s)
| n s (or p q)           := or (p.rew' s) (q.rew' s)
| n s (fal p)            := fal (p.rew' (subterm.lift ∘ s))
| n s (ex p)             := ex (p.rew' (subterm.lift ∘ s))

@[simp] lemma rew'_neg (p : subformula L μ₁ n) : (∼p).rew' s = ∼(p.rew' s) :=
by induction p; simp[←verum_eq, ←falsum_eq, ←and_eq, ←or_eq, ←not_eq, ←fal_eq, ←ex_eq, *] at*

def rew (s : μ₁ → subterm L μ₂ n) : subformula L μ₁ n →ₗ subformula L μ₂ n :=
{ to_fun := rew' s,
  map_neg' := λ p, by simp,
  map_imply' := λ p q, by simp[imply_def, ←or_eq],
  map_and' := λ p q, by refl,
  map_or' := λ p q, by refl,
  map_top' := by refl,
  map_bot' := by refl }

def map (f : μ₁ → μ₂) : subformula L μ₁ n →ₗ subformula L μ₂ n := rew (λ x, &(f x))

@[simp] lemma rew_relation {k} (r : L.pr k) (v : fin k → subterm L μ₁ n) :
  rew s (relation r v) = relation r (λ i, subterm.rew s (v i)) := rfl

@[simp] lemma rew_neg_relation {k} (r : L.pr k) (v : fin k → subterm L μ₁ n) :
  rew s (neg_relation r v) = neg_relation r (λ i, subterm.rew s (v i)) := rfl

@[simp] lemma rew_fal (p : subformula L μ₁ (n + 1)) :
  rew s (∀'p) = ∀'rew (λ x, subterm.lift (s x)) p := rfl

@[simp] lemma rew_ex (p : subformula L μ₁ (n + 1)) :
  rew s (∃'p) = ∃'rew (λ x, subterm.lift (s x)) p := rfl

@[simp] lemma map_relation {k} (r : L.pr k) (v : fin k → subterm L μ₁ n) :
  map f (relation r v) = relation r (λ x, subterm.map f (v x)) := rfl

@[simp] lemma map_neg_relation {k} (r : L.pr k) (v : fin k → subterm L μ₁ n) :
  map f (neg_relation r v) = neg_relation r (λ x, subterm.map f (v x)) := rfl

@[simp] lemma map_fal (p : subformula L μ₁ (n + 1)) :
  map f (∀'p) = ∀'map f p := rfl

@[simp] lemma map_ex (p : subformula L μ₁ (n + 1)) :
  map f (∃'p) = ∃'map f p := rfl

lemma eq_rew_of_eq {p : subformula L μ₁ n} {s₁ s₂ : μ₁ → subterm L μ₂ n} (h : s₁ = s₂) :
  rew s₁ p = rew s₂ p := by rw[h]

lemma eq_map_of_eq {p : subformula L μ₁ n} {f₁ f₂ : μ₁ → μ₂} (h : f₁ = f₂) :
  map f₁ p = map f₂ p := by rw[h]

@[simp] lemma rew_metavar (p : subformula L μ n) : rew subterm.metavar p = p :=
by induction p using fol.Tait.subformula.ind_on; simp[subterm.mlift_rew, *]

@[simp] lemma map_id (p : subformula L μ n) : map id p = p := by simp[map]

lemma rew_rew {μ₁ μ₂ μ₃} : ∀ {n} (p : subformula L μ₁ n) (s₀ : μ₁ → subterm L μ₂ n) (s₁ : μ₂ → subterm L μ₃ n),
  rew s₁ (rew s₀ p) = rew (λ x, subterm.rew s₁ (s₀ x)) p
| n verum              s₀ s₁ := by simp[verum_eq]
| n falsum             s₀ s₁ := by simp[falsum_eq]
| n (relation p v)     s₀ s₁ := by simp; funext; simp[subterm.rew_rew]
| n (neg_relation p v) s₀ s₁ := by simp; funext; simp[subterm.rew_rew]
| n (and p q)          s₀ s₁ := by simp[and_eq, rew_rew p, rew_rew q]
| n (or p q)           s₀ s₁ := by simp[or_eq, rew_rew p, rew_rew q]
| n (fal p)            s₀ s₁ := by simp[fal_eq, rew_rew p]; refine eq_rew_of_eq (by funext x; simp[subterm.lift_rew])
| n (ex p)             s₀ s₁ := by simp[ex_eq, rew_rew p]; refine eq_rew_of_eq (by funext x; simp[subterm.lift_rew])

@[simp] lemma map_map {μ₁ μ₂ μ₃} (f₁ : μ₁ → μ₂) (f₂ : μ₂ → μ₃) (p : subformula L μ₁ n) :
  map f₂ (map f₁ p) = map (f₂ ∘ f₁) p :=
by simp[map, rew_rew]

lemma map_inj_of_inj (f : μ₁ → μ₂) (I : function.injective f) :
  function.injective (map f : Tait.subformula L μ₁ n → Tait.subformula L μ₂ n) := λ p q,
begin
  induction p,
  case verum { cases q; simp[map, rew] },
  case falsum { cases q; simp[map, rew] },
  case relation : n k r v₁
  { cases q; simp[map, rew],
    case relation : _ _ r₂ v₂
    { rintros rfl rfl, simp, intros h, funext i, exact subterm.map_inj_of_inj f I (congr_fun h i) } },
  case neg_relation : n k r v₁
  { cases q; simp[map, rew],
    case neg_relation : _ _ r₂ v₂
    { rintros rfl rfl, simp, intros h, funext i, exact subterm.map_inj_of_inj f I (congr_fun h i) } },
  case and : n p₁ q₁ IH₁ IH₂ { cases q; simp[map, rew] at*, intros h₁ h₂, exact ⟨IH₁ _ h₁, IH₂ _ h₂⟩ },
  case or : n p₁ q₁ IH₁ IH₂ { cases q; simp[map, rew] at*, intros h₁ h₂, exact ⟨IH₁ _ h₁, IH₂ _ h₂⟩ },
  case fal : n p IH
  { cases q; simp[map, rew],
      case fal : _ p₂ { intros h, exact IH _ h } },
  case ex : n p IH
  { cases q; simp[map, rew],
      case ex : _ p₂ { intros h, exact IH _ h } }
end

instance : has_coe (bounded_subformula L 0 n) (subformula L μ n) := ⟨map fin.nil⟩

lemma sentence_coe_def (σ : bounded_subformula L 0 n) : (σ : subformula L μ n) = map fin.nil σ := rfl

@[simp] lemma sentence_coe_self (σ : bounded_subformula L 0 n) : (↑σ : bounded_subformula L 0 n) = σ :=
by simpa [sentence_coe_def] using @eq_map_of_eq _ _ _ _ σ fin.nil id (by ext x; exact fin_zero_elim x)

@[simp] lemma map_sentence_coe (f : fin 0 → μ₂) (σ : bounded_subformula L 0 n) :
  map f σ = σ :=
by simp[sentence_coe_def]; refine eq_map_of_eq (by ext x; exact fin_zero_elim x)

@[simp] lemma map_sentence_coe' (f : μ₁ → μ₂) (σ : bounded_subformula L 0 n) :
  map f (↑σ : subformula L μ₁ n) = σ :=
by simp only [sentence_coe_def, map_map]; refine eq_map_of_eq (by ext x; exact fin_zero_elim x)

@[simp] lemma complexity_rew (p : subformula L μ₁ n) : complexity (rew s p) = complexity p :=
by induction p using fol.Tait.subformula.ind_on; simp*

end rew

section subst

@[simp] def subst' : Π {n}, subterm L μ n → subformula L μ (n + 1) → subformula L μ n
| n u verum              := ⊤
| n u falsum             := ⊥
| n u (relation r v)     := relation r (λ i, subterm.subst u $ v i)
| n u (neg_relation r v) := neg_relation r (λ i, subterm.subst u $ v i)
| n u (and p q)          := subst' u p ⊓ subst' u q
| n u (or p q)           := subst' u p ⊔ subst' u q
| n u (fal p)            := ∀' (subst' u.lift p)
| n u (ex p)             := ∃' (subst' u.lift p)
using_well_founded {rel_tac := λ _ _, `[exact ⟨_, measure_wf (λ x, x.2.2.complexity)⟩]}

@[simp] lemma subst'_neg (p : subformula L μ₁ (n + 1)) : ∀ u, subst' u (∼p) = ∼(subst' u p) :=
by apply ind_succ_on p; intros; simp[←verum_eq, ←falsum_eq, ←and_eq, ←or_eq, ←not_eq, ←fal_eq, ←ex_eq, *] at*

def subst (u : subterm L μ n) : subformula L μ (n + 1) →ₗ subformula L μ n :=
{ to_fun := subst' u,
  map_neg' := λ p, by simp; refl,
  map_imply' := λ p q, by simp[imply_def, ←or_eq],
  map_and' := λ p q, by unfold has_inf.inf; simp; refl,
  map_or' := λ p q, by unfold has_sup.sup; simp; refl,
  map_top' := by unfold has_top.top; simp; refl,
  map_bot' := by unfold has_bot.bot; simp; refl }

variables (u : subterm L μ n)

@[simp] lemma subst_relation {k} (r : L.pr k) (v : fin k → subterm L μ (n + 1)) :
  subst u (relation r v) = relation r (λ i, u.subst (v i)) := by simp[subst]

@[simp] lemma subst_neg_relation {k} (r : L.pr k) (v : fin k → subterm L μ (n + 1)) :
  subst u (neg_relation r v) = neg_relation r (λ i, u.subst (v i)) := by simp[subst]

@[simp] lemma subst_fal (p : subformula L μ (n + 1 + 1)) :
  subst u (∀'p) = ∀'subst u.lift p := by simp[←fal_eq, subst]

@[simp] lemma subst_ex (p : subformula L μ (n + 1 + 1)) :
  subst u (∃'p) = ∃'subst u.lift p := by simp[←ex_eq, subst]

@[simp] lemma complexity_subst (p : subformula L μ (n + 1)) : ∀ u, complexity (subst u p) = complexity p :=
by apply ind_succ_on p; intros; simp*

variables {μ₁ μ₂} (f : μ₁ → μ₂)

lemma map_subst (p : subformula L μ₁ (n + 1)) : ∀ (u : subterm L μ₁ n),
  map f (subst u p) = subst (u.map f) (map f p) :=
by apply ind_succ_on p; intros; simp[*, subterm.map_subst, subterm.map_lift]

end subst

@[simp] def of_tait : Π {n}, subformula L μ n → fol.subformula L μ n
| n verum              := ⊤
| n falsum             := ⊥
| n (relation r v)     := fol.subformula.relation r v
| n (neg_relation r v) := ∼fol.subformula.relation r v
| n (and p q)          := p.of_tait ⊓ q.of_tait
| n (or p q)           := p.of_tait ⊔ q.of_tait
| n (fal p)            := ∀'p.of_tait
| n (ex p)             := ∃'p.of_tait

section of_tait

@[simp] lemma of_tait_verum : (of_tait (⊤ : Tait.subformula L μ n)) = ⊤ := rfl

@[simp] lemma of_tait_falsum : (of_tait (⊥ : Tait.subformula L μ n)) = ⊥ := rfl

@[simp] lemma of_tait_and (p q : subformula L μ n) : of_tait (p ⊓ q) = of_tait p ⊓ of_tait q := rfl

@[simp] lemma of_tait_or (p q : subformula L μ n) : of_tait (p ⊔ q) = of_tait p ⊔ of_tait q := rfl

@[simp] lemma of_tait_fal (p : subformula L μ (n + 1)) : of_tait (∀'p) = ∀'of_tait p := rfl

@[simp] lemma of_tait_ex (p : subformula L μ (n + 1)) : of_tait (∃'p) = ∃'of_tait p := rfl

lemma of_tait_subst (p : bounded_subformula L m (n + 1)) : ∀ t, of_tait (subst t p) = fol.subformula.subst t (of_tait p) :=
by apply ind_succ_on p; intros ; simp*

end of_tait

section bounded
variables {m}

section mlift

def mlift {m n} : bounded_subformula L m n →ₗ bounded_subformula L (m + 1) n := map fin.cast_succ

@[simp] lemma mlift_relation {k} (r : L.pr k) (v : fin k → bounded_subterm L m n) :
  mlift (relation r v) = relation r (λ i, (v i).mlift) := rfl

@[simp] lemma mlift_neg_relation {k} (r : L.pr k) (v : fin k → bounded_subterm L m n) :
  mlift (neg_relation r v) = neg_relation r (λ i, (v i).mlift) := rfl

@[simp] lemma mlift_fal (p : bounded_subformula L m (n + 1)) :
  mlift (∀'p) = ∀'mlift p := rfl

@[simp] lemma mslift_ex (p : bounded_subformula L m (n + 1)) :
  mlift (∃'p) = ∃'mlift p := rfl

@[simp] lemma mlift_coe (σ : bounded_subformula L 0 n) : mlift (σ : bounded_subformula L m n) = σ :=
by { simp only [sentence_coe_def, mlift, map_map], refine eq_map_of_eq (by ext x; exact fin_zero_elim x) }

lemma mlift_inj : function.injective (@mlift L m n) := map_inj_of_inj _ (rel_embedding.injective _)

@[simp] lemma mlift_inj' (p q : bounded_subformula L m n) :
  mlift p = mlift q ↔ p = q :=
⟨λ h, (map_inj_of_inj _ (rel_embedding.injective _)) h, λ h, by rw[h]⟩

end mlift

section push

@[simp] def push' {m} : Π {n}, bounded_subformula L m (n + 1) → bounded_subformula L (m + 1) n
| n verum              := ⊤
| n falsum             := ⊥
| n (relation p v)     := relation p (λ i, (v i).push)
| n (neg_relation p v) := neg_relation p (λ i, (v i).push)
| n (and p q)          := p.push' ⊓ q.push'
| n (or p q)           := p.push' ⊔ q.push'
| n (fal p)            := ∀'p.push'
| n (ex p)             := ∃'p.push'
using_well_founded {rel_tac := λ _ _, `[exact ⟨_, measure_wf (λ x, x.2.complexity)⟩]}

@[simp] lemma push'_neg : ∀ {n} (p : bounded_subformula L m (n + 1)), (∼p).push' = ∼p.push'
| n verum              := by simp[←falsum_eq, ←verum_eq, ←not_eq]
| n falsum             := by simp[←falsum_eq, ←verum_eq, ←not_eq]
| n (relation p v)     := by simp
| n (neg_relation p v) := by simp
| n (and p q)          := by simp[←not_eq, ←and_eq, ←or_eq]; exact ⟨push'_neg p, push'_neg q⟩
| n (or p q)           := by simp[←not_eq, ←and_eq, ←or_eq]; exact ⟨push'_neg p, push'_neg q⟩
| n (fal p)            := by simp[←not_eq, ←fal_eq, ←ex_eq]; rw[←fal_eq, ←ex_eq]; simp; exact push'_neg p
| n (ex p)             := by simp[←not_eq, ←fal_eq, ←ex_eq]; rw[←fal_eq, ←ex_eq]; simp; exact push'_neg p
using_well_founded {rel_tac := λ _ _, `[exact ⟨_, measure_wf (λ x, x.2.complexity)⟩]}

def push : bounded_subformula L m (n + 1) →ₗ bounded_subformula L (m + 1) n :=
{ to_fun := push',
  map_neg' := λ p, by simp,
  map_imply' := λ p q, by simp[imply_def, ←or_eq],
  map_and' := λ p q, by simp[←and_eq],
  map_or' := λ p q, by simp[←or_eq],
  map_top' := by simp[←verum_eq],
  map_bot' := by simp[←falsum_eq] }

@[simp] lemma push_relation {k} (r : L.pr k) (v : fin k → bounded_subterm L m (n + 1)) :
  push (relation r v) = relation r (λ i, (v i).push) := by simp[push]

@[simp] lemma push_neg_relation {k} (r : L.pr k) (v : fin k → bounded_subterm L m (n + 1)) :
  push (neg_relation r v) = neg_relation r (λ i, (v i).push) := by simp[push]

@[simp] lemma push_fal (p : bounded_subformula L m (n + 1 + 1)) :
  push (∀'p) = ∀'push p := by simp[push]; unfold has_univ_quantifier'.univ; simp; refl

@[simp] lemma push_ex (p : bounded_subformula L m (n + 1 + 1)) :
  push (∃'p) = ∃'push p := by simp[push]; unfold has_exists_quantifier'.ex; simp; refl

@[simp] lemma complexity_push {n} (p : bounded_subformula L m (n + 1)) : p.push.complexity = p.complexity :=
by apply ind_succ_on p; intros; simp*

end push

section pull

@[simp] def pull' {m} : Π {n}, bounded_subformula L (m + 1) n → bounded_subformula L m (n + 1)
| n verum              := ⊤
| n falsum             := ⊥
| n (relation p v)     := relation p (λ i, (v i).pull)
| n (neg_relation p v) := neg_relation p (λ i, (v i).pull)
| n (and p q)          := p.pull' ⊓ q.pull'
| n (or p q)           := p.pull' ⊔ q.pull'
| n (fal p)            := ∀'p.pull'
| n (ex p)             := ∃'p.pull'

@[simp] lemma pull_neg (p : bounded_subformula L (m + 1) n) : (∼p).pull' = ∼p.pull' :=
by induction p; simp[←verum_eq, ←falsum_eq, ←and_eq, ←or_eq, ←not_eq, ←fal_eq, ←ex_eq, *] at*; refl

def pull : bounded_subformula L (m + 1) n →ₗ bounded_subformula L m (n + 1) :=
{ to_fun := pull',
  map_neg' := λ p, by simp,
  map_imply' := λ p q, by simp[imply_def, ←or_eq],
  map_and' := λ p q, by refl,
  map_or' := λ p q, by refl,
  map_top' := by refl,
  map_bot' := by refl }

@[simp] lemma pull_relation {k} (r : L.pr k) (v : fin k → bounded_subterm L (m + 1) n) :
  pull (relation r v) = relation r (λ i, (v i).pull) := rfl

@[simp] lemma pull_neg_relation {k} (r : L.pr k) (v : fin k → bounded_subterm L (m + 1) n) :
  pull (neg_relation r v) = neg_relation r (λ i, (v i).pull) := rfl

@[simp] lemma pull_fal (p : bounded_subformula L (m + 1) (n + 1)) :
  pull (∀'p) = ∀'pull p := by simp[pull]; unfold has_univ_quantifier'.univ; simp; refl

@[simp] lemma pull_ex (p : bounded_subformula L (m + 1) (n + 1)) :
  pull (∃'p) = ∃'pull p := by simp[pull]; unfold has_exists_quantifier'.ex; simp; refl

@[simp] lemma pull_push (p : bounded_subformula L m (n + 1)) : p.push.pull = p :=
by apply ind_succ_on p; intros; simp[*, (∘)]

end pull

section cast_le
variables {m₁ m₂} (h : m₁ ≤ m₂)

def cast_le (h : m₁ ≤ m₂) : bounded_subformula L m₁ n →ₗ bounded_subformula L m₂ n :=
map (fin.cast_le h)

@[simp] lemma cast_le_inj (p q : bounded_subformula L m₁ n) : cast_le h p = cast_le h q ↔ p = q :=
⟨λ h, map_inj_of_inj _ (rel_embedding.injective _) h, λ h, by simp[h]⟩

@[simp] lemma cast_le_function {k} (r : L.pr k) (v : fin k → bounded_subterm L m₁ n) :
  cast_le h (relation r v) = relation r (λ i, subterm.cast_le h (v i)) :=
by simp[cast_le]; refl

@[simp] lemma cast_le_neg_function {k} (r : L.pr k) (v : fin k → bounded_subterm L m₁ n) :
  cast_le h (neg_relation r v) = neg_relation r (λ i, subterm.cast_le h (v i)) :=
by simp[cast_le]; refl

@[simp] lemma cast_le_fal (p : bounded_subformula L m₁ (n + 1)) :
  cast_le h (∀'p) = ∀'(cast_le h p) :=
by simp[cast_le]; refl

@[simp] lemma cast_le_ex (p : bounded_subformula L m₁ (n + 1)) :
  cast_le h (∃'p) = ∃'(cast_le h p) :=
by simp[cast_le]; refl

@[simp] lemma cast_le_cast_le {m₁ m₂ m₃ : ℕ} (h₁ : m₁ ≤ m₂) (h₂ : m₂ ≤ m₃) (p : bounded_subformula L m₁ n) :
  cast_le h₂ (cast_le h₁ p) = cast_le (le_trans h₁ h₂) p :=
by simp[cast_le]

@[simp] lemma uniform_subst (u : bounded_subterm L m₁ n) (p : bounded_subformula L m₁ (n + 1)) :
  cast_le h (subst u p) = subst (subterm.cast_le h u) (cast_le h p) :=
by simp[cast_le, subterm.cast_le, map_subst]

variables {m₁ m₂} (f : fin (m₁ + 1) → fin m₂)

@[simp] lemma cast_eq_id (h : m ≤ m) :
  (cast_le h : bounded_subformula L m n→ bounded_subformula L m n) = id :=
by { ext p, simp[cast_le, show ⇑(fin.cast_le h) = id, by funext x; simp; ext; simp],  }

lemma cast_le_eq_mlift (p : bounded_subformula L m n) (h : m ≤ m + 1) :
  cast_le h p = mlift p :=
by simp[cast_le, mlift]; refine eq_map_of_eq (by ext x; simp)

@[simp] lemma cast_le_mlift (p : bounded_subformula L m₁ n) (h : m₁ + 1 ≤ m₂) :
  cast_le h (mlift p) = cast_le (nat.le_of_succ_le h) p :=
by simp[cast_le, mlift]; congr

@[simp] lemma mlift_cast_le (p : bounded_subformula L m₁ n) (h : m₁ ≤ m₂) :
  mlift (cast_le h p) = cast_le (le_add_right h) p :=
by simp[cast_le, mlift]; congr

lemma push_eq_subst_mlift (p : bounded_subformula L m (n + 1)) :
  push p = subst &(fin.last m) p.mlift :=
by apply ind_succ_on p; intros; simp*

@[simp] lemma uniform_push (h : m₁ + 1 ≤ m₂) (p : bounded_subformula L m₁ (n + 1)) :
  cast_le h (push p) = subst &(fin.cast_le h $ fin.last m₁) (cast_le (nat.le_of_succ_le h) p) :=
by simp[push_eq_subst_mlift]

end cast_le

section of_tait

lemma of_tait_mlift (p : bounded_subformula L m n) : of_tait (mlift p) = fol.subformula.mlift (of_tait p) :=
by induction p using fol.Tait.subformula.ind_on; simp*

lemma of_tait_push (p : bounded_subformula L m (n + 1)) : of_tait (push p) = fol.subformula.push (of_tait p) :=
by apply ind_succ_on p; intros; simp*

lemma of_tait_pull (p : bounded_subformula L (m + 1) n) : of_tait (pull p) = fol.subformula.pull (of_tait p) :=
by induction p using fol.Tait.subformula.ind_on; simp*

end of_tait

end bounded

section nat

@[simp] def arity : Π {n}, subformula L ℕ n → ℕ
| n verum              := 0
| n falsum             := 0
| n (relation r v)     := ⨆ᶠ i, (v i).arity
| n (neg_relation r v) := ⨆ᶠ i, (v i).arity
| n (and p q)          := max p.arity q.arity
| n (or p q)           := max p.arity q.arity
| n (fal p)            := p.arity
| n (ex p)             := p.arity

@[simp] lemma arity_verum : (⊤ : subformula L ℕ n).arity = 0 := rfl
@[simp] lemma arity_falsum : (⊥ : subformula L ℕ n).arity = 0 := rfl
@[simp] lemma arity_and (p q : subformula L ℕ n) : (p ⊓ q).arity = max p.arity q.arity := rfl
@[simp] lemma arity_or (p q : subformula L ℕ n) : (p ⊔ q).arity = max p.arity q.arity := rfl
@[simp] lemma arity_fal (p : subformula L ℕ (n + 1)) : (∀'p).arity = p.arity := rfl
@[simp] lemma arity_ex (p : subformula L ℕ (n + 1)) : (∃'p).arity = p.arity := rfl

@[simp] lemma arity_not (p : subformula L ℕ n) : (∼p).arity = p.arity :=
by simp[←not_eq]; induction p; simp*

@[simp] def to_bform {m} : Π {n} (p : subformula L ℕ n), p.arity ≤ m → bounded_subformula L m n
| n verum              h := verum
| n falsum             h := falsum
| n (relation r v)     h := relation r (λ i, (v i).to_bterm (by simp at h; exact h i))
| n (neg_relation r v) h := neg_relation r (λ i, (v i).to_bterm (by simp at h; exact h i))
| n (and p q)          h := have p.arity ≤ m ∧ q.arity ≤ m, by simpa using h, and (p.to_bform this.1) (q.to_bform this.2)
| n (or p q)           h := have p.arity ≤ m ∧ q.arity ≤ m, by simpa using h, or (p.to_bform this.1) (q.to_bform this.2)
| n (fal p)            h := fal (p.to_bform (by simpa using h))
| n (ex p)             h := ex (p.to_bform (by simpa using h))

@[simp] lemma to_bform_verum (h) : ((⊤ : subformula L ℕ n).to_bform h : bounded_subformula L m n) = ⊤ := rfl
@[simp] lemma to_bform_falsum (h) : ((⊥ : subformula L ℕ n).to_bform h : bounded_subformula L m n) = ⊥ := rfl
@[simp] lemma to_bform_and (p q : subformula L ℕ n) (h) :
  ((p ⊓ q).to_bform h : bounded_subformula L m n) = p.to_bform (by simp at h; exact h.1) ⊓ q.to_bform (by simp at h; exact h.2) := rfl
@[simp] lemma to_bform_or (p q : subformula L ℕ n) (h) :
  ((p ⊔ q).to_bform h : bounded_subformula L m n) = p.to_bform (by simp at h; exact h.1) ⊔ q.to_bform (by simp at h; exact h.2) := rfl
@[simp] lemma to_bform_fal (p : subformula L ℕ (n + 1)) (h) :
  ((∀'p).to_bform h : bounded_subformula L m n) = ∀'p.to_bform (by simpa using h) := rfl
@[simp] lemma to_bform_ex (p : subformula L ℕ (n + 1)) (h) :
  ((∃'p).to_bform h : bounded_subformula L m n) = ∃'p.to_bform (by simpa using h) := rfl

@[simp] lemma to_bform_not (p : subformula L ℕ n) {h} : (to_bform (∼p) h : bounded_subformula L m n) = ∼(to_bform p (by simpa using h)) :=
by { simp[←not_eq], induction p; simp[*],
  case and : n p q IH₁ IH₂ { refine ⟨IH₁, IH₂⟩ },
  case or  : n p q IH₁ IH₂ { refine ⟨IH₁, IH₂⟩ },
  case fal : n p IH { exact IH },
  case ex : n p IH { exact IH } }

end nat

variables {T : bounded_preTheory L m} {m}

open axiomatic_classical_logic' axiomatic_classical_logic

lemma to_tait_not_equiv_aux : ∀ {m} (p : Tait.bounded_formula L m), ∅ ⊢ (∼p).of_tait ⟷ ∼p.of_tait
| m verum := by simp[verum_eq]
| m falsum := by apply equiv_symm; simp[falsum_eq]
| m (relation r v) := by simp
| m (neg_relation r v) := by simp
| m (and p q) := by { simp[and_eq],
    have : ∅ ⊢ (∼p).of_tait ⊔ (∼q).of_tait ⟷ ∼p.of_tait ⊔ ∼q.of_tait,
    from equiv_or_of_equiv (to_tait_not_equiv_aux p) (to_tait_not_equiv_aux q),
    refine equiv_trans this (equiv_symm (neg_and_equiv_or_neg _ _)) }
| m (or p q) := by { simp[or_eq],
    have : ∅ ⊢ (∼p).of_tait ⊓ (∼q).of_tait ⟷ ∼p.of_tait ⊓ ∼q.of_tait,
    from equiv_and_of_equiv (to_tait_not_equiv_aux p) (to_tait_not_equiv_aux q),
    refine equiv_trans this (equiv_symm (neg_or_equiv_and_neg _ _)) }
| m (fal p) :=
    begin
      simp[fal_eq],
      have : ∅ ⊢ ∃'(∼p).of_tait ⟷ ∃'∼p.of_tait,
      from provable.equiv_exists_of_equiv (by simpa[←of_tait_push] using to_tait_not_equiv_aux (push p)),
      refine equiv_trans this (equiv_symm $ provable.neg_forall_pnf p.of_tait)
    end
| m (ex p) :=
    begin
      simp[ex_eq],
      have : ∅ ⊢ ∀'(∼p).of_tait ⟷ ∀'∼p.of_tait,
      from provable.equiv_forall_of_equiv (by simpa[←of_tait_push] using to_tait_not_equiv_aux (push p)),
      refine equiv_trans this (equiv_symm $ provable.neg_exists_pnf p.of_tait)
    end
using_well_founded {rel_tac := λ _ _, `[exact ⟨_, measure_wf (λ x, x.2.complexity)⟩]}

lemma to_tait_not_equiv (p : Tait.bounded_formula L m) : T ⊢ (∼p).of_tait ⟷ ∼p.of_tait :=
weakening (by simp) (to_tait_not_equiv_aux p)

end subformula

end Tait

namespace subformula
variables {L μ m n}

@[simp] def to_tait : Π {n}, subformula L μ n → Tait.subformula L μ n
| n verum          := Tait.subformula.verum
| n (relation r v) := Tait.subformula.relation r v
| n (imply p q)    := p.to_tait.imply q.to_tait
| n (neg p)        := p.to_tait.not
| n (fal p)        := p.to_tait.fal

@[simp] lemma to_tait_verum : to_tait (⊤ : subformula L μ n) = ⊤ := rfl
@[simp] lemma to_tait_imply (p q : subformula L μ n) : to_tait (p ⟶ q) = to_tait p ⟶ to_tait q := rfl
@[simp] lemma to_tait_neg (p : subformula L μ n) : to_tait (∼p) = ∼to_tait p := rfl
@[simp] lemma to_tait_fal (p : subformula L μ (n + 1)) : to_tait (∀'p) = ∀'to_tait p := rfl

variables (T : bounded_preTheory L m)

lemma to_tait_push (p : bounded_subformula L m (n + 1)) : to_tait (push p) = Tait.subformula.push (to_tait p) :=
by apply ind_succ_on p; intros; simp*

open axiomatic_classical_logic' axiomatic_classical_logic

lemma to_tait_of_tait_aux : ∀ {m} (p : bounded_formula L m), ∅ ⊢ p.to_tait.of_tait ⟷ p
| m verum := by simp[top_eq]
| m (relation r v) := by simp
| m (imply p q) :=
    begin
      simp[imply_eq, Tait.subformula.imply_def],
      have : ∅ ⊢ ∼p ⊔ q ⟷ p ⟶ q, from equiv_symm impl_iff_or,
      refine equiv_of_equiv this _ (by simp),
      refine equiv_or_of_equiv
      (equiv_symm $ equiv_trans (Tait.subformula.to_tait_not_equiv_aux p.to_tait) (equiv_neg_of_equiv $ to_tait_of_tait_aux p))
      (equiv_symm $ to_tait_of_tait_aux q),
    end
| m (neg p) :=
    by simp[neg_eq]; refine equiv_trans (Tait.subformula.to_tait_not_equiv_aux p.to_tait) (equiv_neg_of_equiv $ to_tait_of_tait_aux p)
| m (fal p) := by { simp[fal_eq], refine provable.equiv_forall_of_equiv
    (by { simpa[←Tait.subformula.of_tait_push, to_tait_push] using to_tait_of_tait_aux (push p) }) }
using_well_founded {rel_tac := λ _ _, `[exact ⟨_, measure_wf (λ x, x.2.complexity)⟩]}

lemma to_tait_of_tait  {T : bounded_preTheory L m} (p : bounded_formula L m) : T ⊢ p.to_tait.of_tait ⟷ p :=
weakening (by simp) (to_tait_of_tait_aux p)

end subformula

end fol


/-
Copyright (c) 2022 Kexing Ying. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kexing Ying
-/
import probability.martingale.upcrossing
import measure_theory.function.uniform_integrable
import measure_theory.constructions.polish

/-!

# Martingale convergence theorems

The martingale convergence theorems are a collection of theorems characterizing the convergence
of a martingale provided it satisfies some boundedness conditions. This file contains the
almost everywhere martingale convergence theorem which provides an almost everywhere limit to
an L¹ bounded submartingale. It also contains the L¹ martingale convergence theorem which provides
an L¹ limit to a uniformly integrable submartingale. Finally, it also contains the Lévy upwards
theorems.

## Main results

* `measure_theory.submartingale.ae_tendsto_limit_process`: the almost everywhere martingale
  convergence theorem: an L¹-bounded submartingale adapted to the filtration `ℱ` converges almost
  everywhere to its limit process.
* `measure_theory.submartingale.mem_ℒp_limit_process`: the limit process of an Lᵖ-bounded
  submartingale is Lᵖ.
* `measure_theory.submartingale.tendsto_snorm_one_limit_process`: part a of the L¹ martingale
  convergence theorem: a uniformly integrable submartingale adapted to the filtration `ℱ` converges
  almost everywhere and in L¹ to an integrable function which is measurable with respect to
  the σ-algebra `⨆ n, ℱ n`.
* `measure_theory.martingale.ae_eq_condexp_limit_process`: part b the L¹ martingale convergence
  theorem: if `f` is a uniformly integrable martingale adapted to the filtration `ℱ`, then
  `f n` equals `𝔼[g | ℱ n]` almost everywhere where `g` is the limiting process of `f`.
* `measure_theory.integrable.tendsto_ae_condexp`: part c the L¹ martingale convergence theorem:
  given a `⨆ n, ℱ n`-measurable function `g` where `ℱ` is a filtration, `𝔼[g | ℱ n]` converges
  almost everywhere to `g`.
* `measure_theory.integrable.tendsto_snorm_condexp`: part c the L¹ martingale convergence theorem:
  given a `⨆ n, ℱ n`-measurable function `g` where `ℱ` is a filtration, `𝔼[g | ℱ n]` converges in
  L¹ to `g`.

-/

open topological_space filter measure_theory.filtration
open_locale nnreal ennreal measure_theory probability_theory big_operators topology

namespace measure_theory

variables {Ω ι : Type*} {m0 : measurable_space Ω} {μ : measure Ω} {ℱ : filtration ℕ m0}
variables {a b : ℝ} {f : ℕ → Ω → ℝ} {ω : Ω} {R : ℝ≥0}

section ae_convergence

/-!

### Almost everywhere martingale convergence theorem

We will now prove the almost everywhere martingale convergence theorem.

The a.e. martingale convergence theorem states: if `f` is an L¹-bounded `ℱ`-submartingale, then
it converges almost everywhere to an integrable function which is measurable with respect to
the σ-algebra `ℱ∞ := ⨆ n, ℱ n`.

Mathematically, we proceed by first noting that a real sequence $(x_n)$ converges if
(a) $\limsup_{n \to \infty} |x_n| < \infty$, (b) for all $a < b \in \mathbb{Q}$ we have the
number of upcrossings of $(x_n)$ from below $a$ to above $b$ is finite.
Thus, for all $\omega$ satisfying $\limsup_{n \to \infty} |f_n(\omega)| < \infty$ and the number of
upcrossings of $(f_n(\omega))$ from below $a$ to above $b$ is finite for all $a < b \in \mathbb{Q}$,
we have $(f_n(\omega))$ is convergent.

Hence, assuming $(f_n)$ is L¹-bounded, using Fatou's lemma, we have
$$
  \mathbb{E} \limsup_{n \to \infty} |f_n| \le \limsup_{n \to \infty} \mathbb{E}|f_n| < \infty
$$
implying $\limsup_{n \to \infty} |f_n| < \infty$ a.e. Furthermore, by the upcrossing estimate,
the number of upcrossings is finite almost everywhere implying $f$ converges pointwise almost
everywhere.

Thus, denoting $g$ the a.e. limit of $(f_n)$, $g$ is $\mathcal{F}_\infty$-measurable as for all
$n$, $f_n$ is $\mathcal{F}_n$-measurable and $\mathcal{F}_n \le \mathcal{F}_\infty$. Finally, $g$
is integrable as $|g| \le \liminf_{n \to \infty} |f_n|$ so
$$
  \mathbb{E}|g| \le \mathbb{E} \limsup_{n \to \infty} |f_n| \le
    \limsup_{n \to \infty} \mathbb{E}|f_n| < \infty
$$
as required.

Implementation wise, we have `tendsto_of_no_upcrossings` which showed that
a bounded sequence converges if it does not visit below $a$ and above $b$ infinitely often
for all $a, b ∈ s$ for some dense set $s$. So, we may skip the first step provided we can prove
that the realizations are bounded almost everywhere. Indeed, suppose $(|f_n(\omega)|)$ is not
bounded, then either $f_n(\omega) \to \pm \infty$ or one of $\limsup f_n(\omega)$ or
$\liminf f_n(\omega)$ equals $\pm \infty$ while the other is finite. But the first case
contradicts $\liminf |f_n(\omega)| < \infty$ while the second case contradicts finite upcrossings.

Furthermore, we introduced `filtration.limit_process` which chooses the limiting random variable
of a stochastic process if it exists, otherwise it returns 0. Hence, instead of showing an
existence statement, we phrased the a.e. martingale convergence theorem by showed that a
submartingale converges to its `limit_process` almost everywhere.

-/

/-- If a stochastic process has bounded upcrossing from below `a` to above `b`,
then it does not frequently visit both below `a` and above `b`. -/
lemma not_frequently_of_upcrossings_lt_top (hab : a < b) (hω : upcrossings a b f ω ≠ ∞) :
  ¬((∃ᶠ n in at_top, f n ω < a) ∧ (∃ᶠ n in at_top, b < f n ω)) :=
begin
  rw [← lt_top_iff_ne_top, upcrossings_lt_top_iff] at hω,
  replace hω : ∃ k, ∀ N, upcrossings_before a b f N ω < k,
  { obtain ⟨k, hk⟩ := hω,
    exact ⟨k + 1, λ N, lt_of_le_of_lt (hk N) k.lt_succ_self⟩ },
  rintro ⟨h₁, h₂⟩,
  rw frequently_at_top at h₁ h₂,
  refine not_not.2 hω _,
  push_neg,
  intro k,
  induction k with k ih,
  { simp only [zero_le', exists_const] },
  { obtain ⟨N, hN⟩ := ih,
    obtain ⟨N₁, hN₁, hN₁'⟩ := h₁ N,
    obtain ⟨N₂, hN₂, hN₂'⟩ := h₂ N₁,
    exact ⟨(N₂ + 1), nat.succ_le_of_lt $ lt_of_le_of_lt hN
      (upcrossings_before_lt_of_exists_upcrossing hab hN₁ hN₁' hN₂ hN₂')⟩ }
end

/-- A stochastic process that frequently visits below `a` and above `b` have infinite
upcrossings. -/
lemma upcrossings_eq_top_of_frequently_lt (hab : a < b)
  (h₁ : ∃ᶠ n in at_top, f n ω < a) (h₂ : ∃ᶠ n in at_top, b < f n ω) :
  upcrossings a b f ω = ∞ :=
classical.by_contradiction (λ h, not_frequently_of_upcrossings_lt_top hab h ⟨h₁, h₂⟩)

/-- A realization of a stochastic process with bounded upcrossings and bounded liminfs is
convergent.

We use the spelling `< ∞` instead of the standard `≠ ∞` in the assumptions since it is not as easy
to change `<` to `≠` under binders. -/
lemma tendsto_of_uncrossing_lt_top
  (hf₁ : liminf (λ n, (‖f n ω‖₊ : ℝ≥0∞)) at_top < ∞)
  (hf₂ : ∀ a b : ℚ, a < b → upcrossings a b f ω < ∞) :
  ∃ c, tendsto (λ n, f n ω) at_top (𝓝 c) :=
begin
  by_cases h : is_bounded_under (≤) at_top (λ n, |f n ω|),
  { rw is_bounded_under_le_abs at h,
    refine tendsto_of_no_upcrossings rat.dense_range_cast _ h.1 h.2,
    { intros a ha b hb hab,
      obtain ⟨⟨a, rfl⟩, ⟨b, rfl⟩⟩ := ⟨ha, hb⟩,
      exact not_frequently_of_upcrossings_lt_top hab (hf₂ a b (rat.cast_lt.1 hab)).ne } },
  { obtain ⟨a, b, hab, h₁, h₂⟩ := ennreal.exists_upcrossings_of_not_bounded_under hf₁.ne h,
    exact false.elim ((hf₂ a b hab).ne
      (upcrossings_eq_top_of_frequently_lt (rat.cast_lt.2 hab) h₁ h₂)) }
end

/-- An L¹-bounded submartingale has bounded upcrossings almost everywhere. -/
lemma submartingale.upcrossings_ae_lt_top' [is_finite_measure μ]
  (hf : submartingale f ℱ μ) (hbdd : ∀ n, snorm (f n) 1 μ ≤ R) (hab : a < b) :
  ∀ᵐ ω ∂μ, upcrossings a b f ω < ∞ :=
begin
  refine ae_lt_top (hf.adapted.measurable_upcrossings hab) _,
  have := hf.mul_lintegral_upcrossings_le_lintegral_pos_part a b,
  rw [mul_comm, ← ennreal.le_div_iff_mul_le] at this,
  { refine (lt_of_le_of_lt this (ennreal.div_lt_top _ _)).ne,
    { have hR' : ∀ n, ∫⁻ ω, ‖f n ω - a‖₊ ∂μ ≤ R + ‖a‖₊ * μ set.univ,
      { simp_rw snorm_one_eq_lintegral_nnnorm at hbdd,
        intro n,
        refine (lintegral_mono _ : ∫⁻ ω, ‖f n ω - a‖₊ ∂μ ≤ ∫⁻ ω, ‖f n ω‖₊ + ‖a‖₊ ∂μ).trans _,
        { intro ω,
          simp_rw [sub_eq_add_neg, ← nnnorm_neg a, ← ennreal.coe_add, ennreal.coe_le_coe],
          exact nnnorm_add_le _ _ },
        { simp_rw [ lintegral_add_right _ measurable_const, lintegral_const],
          exact add_le_add (hbdd _) le_rfl } },
      refine ne_of_lt (supr_lt_iff.2 ⟨R + ‖a‖₊ * μ set.univ, ennreal.add_lt_top.2
          ⟨ennreal.coe_lt_top, ennreal.mul_lt_top ennreal.coe_lt_top.ne (measure_ne_top _ _)⟩,
          λ n, le_trans _ (hR' n)⟩),
      refine lintegral_mono (λ ω, _),
      rw [ennreal.of_real_le_iff_le_to_real, ennreal.coe_to_real, coe_nnnorm],
      by_cases hnonneg : 0 ≤ f n ω - a,
      { rw [lattice_ordered_comm_group.pos_of_nonneg _ hnonneg,
          real.norm_eq_abs, abs_of_nonneg hnonneg] },
      { rw lattice_ordered_comm_group.pos_of_nonpos _ (not_le.1 hnonneg).le,
        exact norm_nonneg _ },
      { simp only [ne.def, ennreal.coe_ne_top, not_false_iff] } },
    { simp only [hab, ne.def, ennreal.of_real_eq_zero, sub_nonpos, not_le] } },
  { simp only [hab, ne.def, ennreal.of_real_eq_zero, sub_nonpos, not_le, true_or]},
  { simp only [ne.def, ennreal.of_real_ne_top, not_false_iff, true_or] }
end

lemma submartingale.upcrossings_ae_lt_top [is_finite_measure μ]
  (hf : submartingale f ℱ μ) (hbdd : ∀ n, snorm (f n) 1 μ ≤ R) :
  ∀ᵐ ω ∂μ, ∀ a b : ℚ, a < b → upcrossings a b f ω < ∞ :=
begin
  simp only [ae_all_iff, eventually_imp_distrib_left],
  rintro a b hab,
  exact hf.upcrossings_ae_lt_top' hbdd (rat.cast_lt.2 hab),
end

/-- An L¹-bounded submartingale converges almost everywhere. -/
lemma submartingale.exists_ae_tendsto_of_bdd [is_finite_measure μ]
  (hf : submartingale f ℱ μ) (hbdd : ∀ n, snorm (f n) 1 μ ≤ R) :
  ∀ᵐ ω ∂μ, ∃ c, tendsto (λ n, f n ω) at_top (𝓝 c) :=
begin
  filter_upwards [hf.upcrossings_ae_lt_top hbdd, ae_bdd_liminf_at_top_of_snorm_bdd one_ne_zero
    (λ n, (hf.strongly_measurable n).measurable.mono (ℱ.le n) le_rfl) hbdd] with ω h₁ h₂,
  exact tendsto_of_uncrossing_lt_top h₂ h₁,
end

lemma submartingale.exists_ae_trim_tendsto_of_bdd [is_finite_measure μ]
  (hf : submartingale f ℱ μ) (hbdd : ∀ n, snorm (f n) 1 μ ≤ R) :
  ∀ᵐ ω ∂(μ.trim (Sup_le (λ m ⟨n, hn⟩, hn ▸ ℱ.le _) : (⨆ n, ℱ n) ≤ m0)),
    ∃ c, tendsto (λ n, f n ω) at_top (𝓝 c) :=
begin
  rw [ae_iff, trim_measurable_set_eq],
  { exact hf.exists_ae_tendsto_of_bdd hbdd },
  { exact measurable_set.compl (@measurable_set_exists_tendsto _ _ _ _ _ _ (⨆ n, ℱ n) _ _ _ _ _
    (λ n, ((hf.strongly_measurable n).measurable.mono (le_Sup ⟨n, rfl⟩) le_rfl))) }
end

/-- **Almost everywhere martingale convergence theorem**: An L¹-bounded submartingale converges
almost everywhere to a `⨆ n, ℱ n`-measurable function. -/
lemma submartingale.ae_tendsto_limit_process [is_finite_measure μ]
  (hf : submartingale f ℱ μ) (hbdd : ∀ n, snorm (f n) 1 μ ≤ R) :
  ∀ᵐ ω ∂μ, tendsto (λ n, f n ω) at_top (𝓝 (ℱ.limit_process f μ ω)) :=
begin
  classical,
  suffices : ∃ g, strongly_measurable[⨆ n, ℱ n] g ∧ ∀ᵐ ω ∂μ, tendsto (λ n, f n ω) at_top (𝓝 (g ω)),
  { rw [limit_process, dif_pos this],
    exact (classical.some_spec this).2 },
  set g' : Ω → ℝ := λ ω, if h : ∃ c, tendsto (λ n, f n ω) at_top (𝓝 c) then h.some else 0,
  have hle : (⨆ n, ℱ n) ≤ m0 := Sup_le (λ m ⟨n, hn⟩, hn ▸ ℱ.le _),
  have hg' : ∀ᵐ ω ∂(μ.trim hle), tendsto (λ n, f n ω) at_top (𝓝 (g' ω)),
  { filter_upwards [hf.exists_ae_trim_tendsto_of_bdd hbdd] with ω hω,
    simp_rw [g', dif_pos hω],
    exact hω.some_spec },
  have hg'm : @ae_strongly_measurable _ _ _ (⨆ n, ℱ n) g' (μ.trim hle) :=
    (@ae_measurable_of_tendsto_metrizable_ae' _ _ (⨆ n, ℱ n) _ _ _ _ _ _ _
      (λ n, ((hf.strongly_measurable n).measurable.mono
      (le_Sup ⟨n, rfl⟩ : ℱ n ≤ ⨆ n, ℱ n) le_rfl).ae_measurable) hg').ae_strongly_measurable,
  obtain ⟨g, hgm, hae⟩ := hg'm,
  have hg : ∀ᵐ ω ∂μ.trim hle, tendsto (λ n, f n ω) at_top (𝓝 (g ω)),
  { filter_upwards [hae, hg'] with ω hω hg'ω,
    exact hω ▸ hg'ω },
  exact ⟨g, hgm, measure_eq_zero_of_trim_eq_zero hle hg⟩,
end

/-- The limiting process of an Lᵖ-bounded submartingale is Lᵖ. -/
lemma submartingale.mem_ℒp_limit_process {p : ℝ≥0∞}
  (hf : submartingale f ℱ μ) (hbdd : ∀ n, snorm (f n) p μ ≤ R) :
  mem_ℒp (ℱ.limit_process f μ) p μ :=
mem_ℒp_limit_process_of_snorm_bdd
  (λ n, ((hf.strongly_measurable n).mono (ℱ.le n)).ae_strongly_measurable) hbdd

end ae_convergence

section L1_convergence

variables [is_finite_measure μ] {g : Ω → ℝ}

/-!

### L¹ martingale convergence theorem

We will now prove the L¹ martingale convergence theorems.

The L¹ martingale convergence theorem states that:
(a) if `f` is a uniformly integrable (in the probability sense) submartingale adapted to the
  filtration `ℱ`, it converges in L¹ to an integrable function `g` which is measurable with
  respect to `ℱ∞ := ⨆ n, ℱ n` and
(b) if `f` is actually a martingale, `f n = 𝔼[g | ℱ n]` almost everywhere.
(c) Finally, if `h` is integrable and measurable with respect to `ℱ∞`, `(𝔼[h | ℱ n])ₙ` is a
  uniformly integrable martingale which converges to `h` almost everywhere and in L¹.

The proof is quite simple. (a) follows directly from the a.e. martingale convergence theorem
and the Vitali convergence theorem as our definition of uniform integrability (in the probability
sense) directly implies L¹-uniform boundedness. We note that our definition of uniform
integrability is slightly non-standard but is equivalent to the usual literary definition. This
equivalence is provided by `measure_theory.uniform_integrable_iff`.

(b) follows since given $n$, we have for all $m \ge n$,
$$
  \|f_n - \mathbb{E}[g \mid \mathcal{F}_n]\|_1 =
    \|\mathbb{E}[f_m - g \mid \mathcal{F}_n]\|_1 \le \|\|f_m - g\|_1.
$$
Thus, taking $m \to \infty$ provides the almost everywhere equality.

Finally, to prove (c), we define $f_n := \mathbb{E}[h \mid \mathcal{F}_n]$. It is clear that
$(f_n)_n$ is a martingale by the tower property for conditional expectations. Furthermore,
$(f_n)_n$ is uniformly integrable in the probability sense. Indeed, as a single function is
uniformly integrable in the measure theory sense, for all $\epsilon > 0$, there exists some
$\delta > 0$ such that for all measurable set $A$ with $\mu(A) < δ$, we have
$\mathbb{E}|h|\mathbf{1}_A < \epsilon$. So, since for sufficently large $\lambda$, by the Markov
inequality, we have for all $n$,
$$
  \mu(|f_n| \ge \lambda) \le \lambda^{-1}\mathbb{E}|f_n| \le \lambda^{-1}\mathbb|g| < \delta,
$$
we have for sufficently large $\lambda$, for all $n$,
$$
  \mathbb{E}|f_n|\mathbf{1}_{|f_n| \ge \lambda} \le
    \mathbb|g|\mathbf{1}_{|f_n| \ge \lambda} < \epsilon,
$$
implying $(f_n)_n$ is uniformly integrable. Now, to prove $f_n \to h$ almost everywhere and in
L¹, it suffices to show that $h = g$ almost everywhere where $g$ is the almost everywhere and L¹
limit of $(f_n)_n$ from part (b) of the theorem. By noting that, for all $s \in \mathcal{F}_n$,
we have
$$
  \mathbb{E}g\mathbf{1}_s = \mathbb{E}[\mathbb{E}[g \mid \mathcal{F}_n]\mathbf{1}_s] =
    \mathbb{E}[\mathbb{E}[h \mid \mathcal{F}_n]\mathbf{1}_s] = \mathbb{E}h\mathbf{1}_s
$$
where $\mathbb{E}[g \mid \mathcal{F}_n = \mathbb{E}[h \mid \mathcal{F}_n]$ almost everywhere
by part (b); the equality also holds for all $s \in \mathcal{F}_\infty$ by Dynkin's theorem.
Thus, as both $h$ and $g$ are $\mathcal{F}_\infty$-measurable, $h = g$ almost everywhere as
required.

Similar to the a.e. martingale convergence theorem, rather than showing the existence of the
limiting process, we phrased the L¹-martingale convergence theorem by proving that a submartingale
does converge in L¹ to its `limit_process`. However, in contrast to the a.e. martingale convergence
theorem, we do not need to introduce a L¹ version of `filtration.limit_process` as the L¹ limit
and the a.e. limit of a submartingale coincide.

-/

/-- Part a of the **L¹ martingale convergence theorem**: a uniformly integrable submartingale
adapted to the filtration `ℱ` converges a.e. and in L¹ to an integrable function which is
measurable with respect to the σ-algebra `⨆ n, ℱ n`. -/
lemma submartingale.tendsto_snorm_one_limit_process
  (hf : submartingale f ℱ μ) (hunif : uniform_integrable f 1 μ) :
  tendsto (λ n, snorm (f n - ℱ.limit_process f μ) 1 μ) at_top (𝓝 0) :=
begin
  obtain ⟨R, hR⟩ := hunif.2.2,
  have hmeas : ∀ n, ae_strongly_measurable (f n) μ :=
    λ n, ((hf.strongly_measurable n).mono (ℱ.le _)).ae_strongly_measurable,
  exact tendsto_Lp_of_tendsto_in_measure _ le_rfl ennreal.one_ne_top hmeas
    (mem_ℒp_limit_process_of_snorm_bdd hmeas hR) hunif.2.1
    (tendsto_in_measure_of_tendsto_ae hmeas $ hf.ae_tendsto_limit_process hR),
end

lemma submartingale.ae_tendsto_limit_process_of_uniform_integrable
  (hf : submartingale f ℱ μ) (hunif : uniform_integrable f 1 μ) :
  ∀ᵐ ω ∂μ, tendsto (λ n, f n ω) at_top (𝓝 (ℱ.limit_process f μ ω)) :=
let ⟨R, hR⟩ := hunif.2.2 in hf.ae_tendsto_limit_process hR

/-- If a martingale `f` adapted to `ℱ` converges in L¹ to `g`, then for all `n`, `f n` is almost
everywhere equal to `𝔼[g | ℱ n]`. -/
lemma martingale.eq_condexp_of_tendsto_snorm {μ : measure Ω}
  (hf : martingale f ℱ μ) (hg : integrable g μ)
  (hgtends : tendsto (λ n, snorm (f n - g) 1 μ) at_top (𝓝 0)) (n : ℕ) :
  f n =ᵐ[μ] μ[g | ℱ n] :=
begin
  rw [← sub_ae_eq_zero, ← snorm_eq_zero_iff ((((hf.strongly_measurable n).mono (ℱ.le _)).sub
    (strongly_measurable_condexp.mono (ℱ.le _))).ae_strongly_measurable) one_ne_zero],
  have ht : tendsto (λ m, snorm (μ[f m - g | ℱ n]) 1 μ) at_top (𝓝 0),
  { have hint : ∀ m, integrable (f m - g) μ := λ m, (hf.integrable m).sub hg,
    exact tendsto_of_tendsto_of_tendsto_of_le_of_le tendsto_const_nhds hgtends (λ m, zero_le _)
      (λ m, snorm_one_condexp_le_snorm _) },
  have hev : ∀ m ≥ n, snorm (μ[f m - g | ℱ n]) 1 μ = snorm (f n - μ[g | ℱ n]) 1 μ,
  { refine λ m hm, snorm_congr_ae
      ((condexp_sub (hf.integrable m) hg).trans _),
    filter_upwards [hf.2 n m hm] with x hx,
    simp only [hx, pi.sub_apply] },
  exact tendsto_nhds_unique (tendsto_at_top_of_eventually_const hev) ht,
end

/-- Part b of the **L¹ martingale convergence theorem**: if `f` is a uniformly integrable martingale
adapted to the filtration `ℱ`, then for all `n`, `f n` is almost everywhere equal to the conditional
expectation of its limiting process wrt. `ℱ n`. -/
lemma martingale.ae_eq_condexp_limit_process
  (hf : martingale f ℱ μ) (hbdd : uniform_integrable f 1 μ) (n : ℕ) :
  f n =ᵐ[μ] μ[ℱ.limit_process f μ | ℱ n] :=
let ⟨R, hR⟩ := hbdd.2.2 in hf.eq_condexp_of_tendsto_snorm
  ((mem_ℒp_limit_process_of_snorm_bdd hbdd.1 hR).integrable le_rfl)
  (hf.submartingale.tendsto_snorm_one_limit_process hbdd) n

/-- Part c of the **L¹ martingale convergnce theorem**: Given a integrable function `g` which
is measurable with respect to `⨆ n, ℱ n` where `ℱ` is a filtration, the martingale defined by
`𝔼[g | ℱ n]` converges almost everywhere to `g`.

This martingale also converges to `g` in L¹ and this result is provided by
`measure_theory.integrable.tendsto_snorm_condexp` -/
lemma integrable.tendsto_ae_condexp
  (hg : integrable g μ) (hgmeas : strongly_measurable[⨆ n, ℱ n] g) :
  ∀ᵐ x ∂μ, tendsto (λ n, μ[g | ℱ n] x) at_top (𝓝 (g x)) :=
begin
  have hle : (⨆ n, ℱ n) ≤ m0 := Sup_le (λ m ⟨n, hn⟩, hn ▸ ℱ.le _),
  have hunif : uniform_integrable (λ n, μ[g | ℱ n]) 1 μ := hg.uniform_integrable_condexp_filtration,
  obtain ⟨R, hR⟩ := hunif.2.2,
  have hlimint : integrable (ℱ.limit_process (λ n, μ[g | ℱ n]) μ) μ :=
    (mem_ℒp_limit_process_of_snorm_bdd hunif.1 hR).integrable le_rfl,
  suffices : g =ᵐ[μ] ℱ.limit_process (λ n x, μ[g | ℱ n] x) μ,
  { filter_upwards [this, (martingale_condexp g ℱ μ).submartingale.ae_tendsto_limit_process hR]
      with x heq ht,
    rwa heq },
  have : ∀ n s, measurable_set[ℱ n] s → ∫ x in s, g x ∂μ =
    ∫ x in s, ℱ.limit_process (λ n x, μ[g | ℱ n] x) μ x ∂μ,
  { intros n s hs,
    rw [← set_integral_condexp (ℱ.le n) hg hs, ← set_integral_condexp (ℱ.le n) hlimint hs],
    refine set_integral_congr_ae (ℱ.le _ _ hs) _,
    filter_upwards [(martingale_condexp g ℱ μ).ae_eq_condexp_limit_process hunif n] with x hx _,
    rwa hx },
  refine ae_eq_of_forall_set_integral_eq_of_sigma_finite' hle
    (λ s _ _, hg.integrable_on) (λ s _ _, hlimint.integrable_on) (λ s hs, _)
    hgmeas.ae_strongly_measurable' strongly_measurable_limit_process.ae_strongly_measurable',
  refine @measurable_space.induction_on_inter _ _ _ (⨆ n, ℱ n)
    (measurable_space.measurable_space_supr_eq ℱ) _ _ _ _ _ _ hs,
  { rintro s ⟨n, hs⟩ t ⟨m, ht⟩ -,
    by_cases hnm : n ≤ m,
    { exact ⟨m, (ℱ.mono hnm _ hs).inter ht⟩ },
    { exact ⟨n, hs.inter (ℱ.mono (not_le.1 hnm).le _ ht)⟩ } },
  { simp only [measure_empty, with_top.zero_lt_top, measure.restrict_empty,
      integral_zero_measure, forall_true_left] },
  { rintro t ⟨n, ht⟩ -,
    exact this n _ ht },
  { rintro t htmeas ht -,
    have hgeq := @integral_add_compl _ _ (⨆ n, ℱ n) _ _ _ _ _ _ htmeas (hg.trim hle hgmeas),
    have hheq := @integral_add_compl _ _ (⨆ n, ℱ n) _ _ _ _ _ _ htmeas
      (hlimint.trim hle strongly_measurable_limit_process),
    rw [add_comm, ← eq_sub_iff_add_eq] at hgeq hheq,
    rw [set_integral_trim hle hgmeas htmeas.compl,
      set_integral_trim hle strongly_measurable_limit_process htmeas.compl,
      hgeq, hheq, ← set_integral_trim hle hgmeas htmeas,
      ← set_integral_trim hle strongly_measurable_limit_process htmeas,
      ← integral_trim hle hgmeas, ← integral_trim hle strongly_measurable_limit_process,
      ← integral_univ, this 0 _ measurable_set.univ, integral_univ, ht (measure_lt_top _ _)] },
  { rintro f hf hfmeas heq -,
    rw [integral_Union (λ n, hle _ (hfmeas n)) hf hg.integrable_on,
      integral_Union (λ n, hle _ (hfmeas n)) hf hlimint.integrable_on],
    exact tsum_congr (λ n, heq _ (measure_lt_top _ _)) }
end

/-- Part c of the **L¹ martingale convergnce theorem**: Given a integrable function `g` which
is measurable with respect to `⨆ n, ℱ n` where `ℱ` is a filtration, the martingale defined by
`𝔼[g | ℱ n]` converges in L¹ to `g`.

This martingale also converges to `g` almost everywhere and this result is provided by
`measure_theory.integrable.tendsto_ae_condexp` -/
lemma integrable.tendsto_snorm_condexp
  (hg : integrable g μ) (hgmeas : strongly_measurable[⨆ n, ℱ n] g) :
  tendsto (λ n, snorm (μ[g | ℱ n] - g) 1 μ) at_top (𝓝 0) :=
tendsto_Lp_of_tendsto_in_measure _ le_rfl ennreal.one_ne_top
  (λ n, (strongly_measurable_condexp.mono (ℱ.le n)).ae_strongly_measurable)
  (mem_ℒp_one_iff_integrable.2 hg) (hg.uniform_integrable_condexp_filtration).2.1
    (tendsto_in_measure_of_tendsto_ae
    (λ n,(strongly_measurable_condexp.mono (ℱ.le n)).ae_strongly_measurable)
      (hg.tendsto_ae_condexp hgmeas))

/-- **Lévy's upward theorem**, almost everywhere version: given a function `g` and a filtration
`ℱ`, the sequence defined by `𝔼[g | ℱ n]` converges almost everywhere to `𝔼[g | ⨆ n, ℱ n]`. -/

/-- **Lévy's upward theorem**, L¹ version: given a function `g` and a filtration `ℱ`, the
sequence defined by `𝔼[g | ℱ n]` converges in L¹ to `𝔼[g | ⨆ n, ℱ n]`. -/
lemma tendsto_snorm_condexp (g : Ω → ℝ) :
  tendsto (λ n, snorm (μ[g | ℱ n] - μ[g | ⨆ n, ℱ n]) 1 μ) at_top (𝓝 0) :=
begin
  have ht : tendsto (λ n, snorm (μ[μ[g | ⨆ n, ℱ n] | ℱ n] - μ[g | ⨆ n, ℱ n]) 1 μ) at_top (𝓝 0) :=
    integrable_condexp.tendsto_snorm_condexp strongly_measurable_condexp,
  have heq : ∀ n, ∀ᵐ x ∂μ, μ[μ[g | ⨆ n, ℱ n] | ℱ n] x = μ[g | ℱ n] x :=
    λ n, condexp_condexp_of_le (le_supr _ n) (supr_le (λ n, ℱ.le n)),
  refine ht.congr (λ n, snorm_congr_ae _),
  filter_upwards [heq n] with x hxeq,
  simp only [hxeq, pi.sub_apply],
end

end L1_convergence

end measure_theory


/-
Copyright (c) 2021 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
import topology.algebra.polynomial
import topology.continuous_function.algebra
import topology.continuous_function.compact
import topology.unit_interval

/-!
# Constructions relating polynomial functions and continuous functions.

## Main definitions

* `polynomial.to_continuous_map_on p X`: for `X : set R`, interprets a polynomial `p`
  as a bundled continuous function in `C(X, R)`.
* `polynomial.to_continuous_map_on_alg_hom`: the same, as an `R`-algebra homomorphism.
* `polynomial_functions (X : set R) : subalgebra R C(X, R)`: polynomial functions as a subalgebra.
* `polynomial_functions_separates_points (X : set R) : (polynomial_functions X).separates_points`:
  the polynomial functions separate points.

-/

variables {R : Type*}

namespace polynomial

section
variables [semiring R] [topological_space R] [topological_ring R]

/--
Every polynomial with coefficients in a topological semiring gives a (bundled) continuous function.
-/
@[simps]
def to_continuous_map (p : polynomial R) : C(R, R) :=
⟨λ x : R, p.eval x, by continuity⟩

/--
A polynomial as a continuous function,
with domain restricted to some subset of the semiring of coefficients.

(This is particularly useful when restricting to compact sets, e.g. `[0,1]`.)
-/
@[simps]
def to_continuous_map_on (p : polynomial R) (X : set R) : C(X, R) :=
⟨λ x : X, p.to_continuous_map x, by continuity⟩

-- TODO some lemmas about when `to_continuous_map_on` is injective?

end

section
variables {α : Type*} [topological_space α]
  [comm_semiring R] [topological_space R] [topological_ring R]

@[simp] lemma aeval_continuous_map_apply (g : polynomial R) (f : C(α, R)) (x : α) :
  ((polynomial.aeval f) g) x = g.eval (f x) :=
begin
  apply polynomial.induction_on' g,
  { intros p q hp hq, simp [hp, hq], },
  { intros n a, simp [pi.pow_apply f x n], },
end

end

section

noncomputable theory

variables [comm_semiring R] [topological_space R] [topological_ring R]

/--
The algebra map from `polynomial R` to continuous functions `C(R, R)`.
-/
@[simps]
def to_continuous_map_alg_hom : polynomial R →ₐ[R] C(R, R) :=
{ to_fun := λ p, p.to_continuous_map,
  map_zero' := by { ext, simp, },
  map_add' := by { intros, ext, simp, },
  map_one' := by { ext, simp, },
  map_mul' := by { intros, ext, simp, },
  commutes' := by { intros, ext, simp [algebra.algebra_map_eq_smul_one], }, }

/--
The algebra map from `polynomial R` to continuous functions `C(X, R)`, for any subset `X` of `R`.
-/
@[simps]
def to_continuous_map_on_alg_hom (X : set R) : polynomial R →ₐ[R] C(X, R)  :=
{ to_fun := λ p, p.to_continuous_map_on X,
  map_zero' := by { ext, simp, },
  map_add' := by { intros, ext, simp, },
  map_one' := by { ext, simp, },
  map_mul' := by { intros, ext, simp, },
  commutes' := by { intros, ext, simp [algebra.algebra_map_eq_smul_one], }, }

end

end polynomial

section
variables [comm_semiring R] [topological_space R] [topological_ring R]

/--
The subalgebra of polynomial functions in `C(X, R)`, for `X` a subset of some topological ring `R`.
-/
def polynomial_functions (X : set R) : subalgebra R C(X, R) :=
(⊤ : subalgebra R (polynomial R)).map (polynomial.to_continuous_map_on_alg_hom X)

@[simp]
lemma polynomial_functions_coe (X : set R) :
  (polynomial_functions X : set C(X, R)) = set.range (polynomial.to_continuous_map_on_alg_hom X) :=
by { ext, simp [polynomial_functions], }

-- TODO:
-- if `f : R → R` is an affine equivalence, then pulling back along `f`
-- induces a normed algebra isomorphism between `polynomial_functions X` and
-- `polynomial_functions (f ⁻¹' X)`, intertwining the pullback along `f` of `C(R, R)` to itself.

lemma polynomial_functions_separates_points (X : set R) :
  (polynomial_functions X).separates_points :=
λ x y h,
begin
  -- We use `polynomial.X`, then clean up.
  refine ⟨_, ⟨⟨_, ⟨⟨polynomial.X, ⟨algebra.mem_top, rfl⟩⟩, rfl⟩⟩, _⟩⟩,
  dsimp, simp only [polynomial.eval_X],
  exact (λ h', h (subtype.ext h')),
end

open_locale unit_interval
open continuous_map

/-- The preimage of polynomials on `[0,1]` under the pullback map by `x ↦ (b-a) * x + a`
is the polynomials on `[a,b]`. -/
lemma polynomial_functions.comap'_comp_right_alg_hom_Icc_homeo_I (a b : ℝ) (h : a < b) :
  (polynomial_functions I).comap'
    (comp_right_alg_hom ℝ (Icc_homeo_I a b h).symm.to_continuous_map) =
    polynomial_functions (set.Icc a b) :=
begin
  ext f,
  fsplit,
  { rintro ⟨p, ⟨-,w⟩⟩,
    rw continuous_map.ext_iff at w,
    dsimp at w,
    let q := p.comp ((b - a)⁻¹ • polynomial.X + polynomial.C (-a * (b-a)⁻¹)),
    refine ⟨q, ⟨_, _⟩⟩,
    { simp, },
    { ext x,
      simp only [neg_mul_eq_neg_mul_symm,
        ring_hom.map_neg, ring_hom.map_mul, alg_hom.coe_to_ring_hom,
        polynomial.eval_X, polynomial.eval_neg, polynomial.eval_C, polynomial.eval_smul,
        polynomial.eval_mul, polynomial.eval_add, polynomial.coe_aeval_eq_eval,
        polynomial.eval_comp, polynomial.to_continuous_map_on_alg_hom_apply,
        polynomial.to_continuous_map_on_to_fun, polynomial.to_continuous_map_to_fun],
      convert w ⟨_, _⟩; clear w,
      { -- why does `comm_ring.add` appear here!?
        change x = (Icc_homeo_I a b h).symm ⟨_ + _, _⟩,
        ext,
        simp only [Icc_homeo_I_symm_apply_coe, subtype.coe_mk],
        replace h : b - a ≠ 0 := sub_ne_zero_of_ne h.ne.symm,
        simp only [mul_add],
        field_simp, ring, },
      { change _ + _ ∈ I,
        rw [mul_comm (b-a)⁻¹, ←neg_mul_eq_neg_mul_symm, ←add_mul, ←sub_eq_add_neg],
        have w₁ : 0 < (b-a)⁻¹ := inv_pos.mpr (sub_pos.mpr h),
        have w₂ : 0 ≤ (x : ℝ) - a := sub_nonneg.mpr x.2.1,
        have w₃ : (x : ℝ) - a ≤ b - a := sub_le_sub_right x.2.2 a,
        fsplit,
        { exact mul_nonneg w₂ (le_of_lt w₁), },
        { rw [←div_eq_mul_inv, div_le_one (sub_pos.mpr h)],
          exact w₃, }, }, }, },
  { rintro ⟨p, ⟨-,rfl⟩⟩,
    let q := p.comp ((b - a) • polynomial.X + polynomial.C a),
    refine ⟨q, ⟨_, _⟩⟩,
    { simp, },
    { ext x, simp [mul_comm], }, },
end

end


/-
Copyright (c) 2021 Frédéric Dupuis. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Frédéric Dupuis, Heather Macbeth
-/
import algebra.module.equiv
import data.real.basic

/-!
# Test file for conjugate linear maps

The implementation of conjugate-linear maps in mathlib is designed to have a few convenient
properties:

1. A conjugate-linear map with respect to the ring `ℝ` (or any other ring given the "trivial"
  `star_ring` instance) is *definitionally* a plain linear map; that is, the type is the same.

2. The composition of two conjugate-linear maps is *definitionally* a plain linear map.

3. The composition of a plain linear map and a conjugate-linear map is conjugate-linear.

4. The inverse of a conjugate-linear equivalence is *definitionally* a conjugate-linear
  equivalence.

This file contains tests to make sure that future refactors do not lose these definitional
properties.

## Tags

Conjugate linear maps, semilinear maps

-/

section real
variables {M : Type*} [add_comm_monoid M] [module ℝ M]
variables {M₂ : Type*} [add_comm_monoid M₂] [module ℝ M₂]

example (f : M →ₗ⋆[ℝ] M₂) : M →ₗ[ℝ] M₂ := f

example (f : M →ₗ[ℝ] M₂) : M →ₗ⋆[ℝ] M₂ := f

end real

section star_ring
variables {R : Type*} [comm_semiring R] [star_ring R]
variables {M : Type*} [add_comm_monoid M] [module R M]
variables {M₂ : Type*} [add_comm_monoid M₂] [module R M₂]
variables {M₃ : Type*} [add_comm_monoid M₃] [module R M₃]

example (f : M →ₗ⋆[R] M₂) (g : M₂ →ₗ⋆[R] M₃) : M →ₗ[R] M₃ := g.comp f

example (f : M →ₗ[R] M₂) (g : M₂ →ₗ⋆[R] M₃) : M →ₗ⋆[R] M₃ := g.comp f
example (f : M →ₗ⋆[R] M₂) (g : M₂ →ₗ[R] M₃) : M →ₗ⋆[R] M₃ := g.comp f

example (f : M ≃ₗ⋆[R] M₂) : M₂ ≃ₗ⋆[R] M := f.symm

end star_ring


import data.set.basic
import data.finset.basic
import automata.epsnfa
import languages.basic
import languages.star

open set epsnfa languages

namespace epsnfa.triv

variables {S : Type} [fintype S] [decidable_eq S]

def epsnfa_empty : epsNFA S bool := {
    start := ff,
    term := tt,
    next := λ q c, ∅,
    eps := λ q, ∅,
}

theorem epsnfa_empty_eq : lang_of_epsnfa (epsnfa_empty : epsNFA S _) = ∅ :=
begin
    rw eq_empty_iff_forall_not_mem,
    rintro x (_ | ⟨_, _, _, _, _, hnxt, _⟩ | ⟨_, _, _, _, hnxt, _⟩);
    simpa [epsnfa_empty] using hnxt,
end

theorem empty_is_epsnfa_lang : epsnfa_lang (∅ : set (list S)) := by exactI ⟨bool, _, _, epsnfa_empty, epsnfa_empty_eq⟩

def epsnfa_eps : epsNFA S bool := {
    start := ff,
    term := tt,
    next := λ q c, ∅,
    eps := bool.rec {tt} ∅
}

theorem epsnfa_eps_eq : lang_of_epsnfa (epsnfa_eps : epsNFA S _) = {[]} :=
begin
    ext w, split, {
        rintro hw,
        cases hw,
        case epsnfa.go.step : {
            exfalso; simpa [epsnfa_eps] using hw_h,
        },
        case epsnfa.go.eps : _ _ h_nxt h_go {
            simp [epsnfa_eps] at h_nxt,
            subst h_nxt,
            cases h_go,
            simp only [mem_singleton],
            simpa only [mem_singleton_iff] using h_go_h,
            simpa only [epsnfa_eps, mem_empty_eq] using h_go_h,
        }
    }, {
        rintro hw,
        rw [mem_singleton_iff] at hw,
        subst hw,
        refine go.eps _ (go.finish),
        simp only [epsnfa_eps, mem_singleton],
    }
end

theorem eps_is_epsnfa_lang : epsnfa_lang ({[]} : set (list S)) :=  by exactI ⟨bool, _, _, epsnfa_eps, epsnfa_eps_eq⟩

def epsnfa_one (char : S) : epsNFA S bool := {
    start := ff,
    term := tt,
    next := λ q c, bool.rec_on q (if (c = char) then {tt} else ∅) ∅,
    eps := λ q, ∅
}

theorem epsnfa_one_eq {char : S} : lang_of_epsnfa (epsnfa_one char) = {[char]} :=
begin
    ext w, split, {
        rintro hw,
        cases hw,
        case epsnfa.go.step : _ _ _ h_nxt h_go {
            simp only [epsnfa_one, dite_eq_ite] at h_nxt,
            split_ifs at h_nxt, {
                simp [epsnfa_one, h] at h_nxt,
                substs h_nxt h,
                cases h_go,
                simp only [mem_singleton],
                simpa only [mem_singleton_iff, and_false],
                simpa only [epsnfa_one, mem_empty_eq] using h_go_h,
            }, {
                simpa only [mem_empty_eq] using h_nxt,
            }
        }, {
            simpa [epsnfa_one] using hw_h,
        }
    }, {
        rintro hw,
        rw [mem_singleton_iff] at hw,
        subst hw,
        refine go.step _ go.finish,
        simp [epsnfa_one],
        simp only [if_true, eq_self_iff_true, mem_singleton],
    }
end

theorem one_is_epsnfa_lang {char : S} : epsnfa_lang ({[char]} : set (list S)) :=
    by exactI ⟨bool, _, _, epsnfa_one char, epsnfa_one_eq⟩

end epsnfa.triv


inductive fml
| atom (i : ℕ)
| imp (a b : fml)
| not (a : fml)

open fml

infixr ` →' `:50 := imp -- right associative

notation `¬' ` := fml.not

inductive prf : fml → Type
| axk {p q} : prf (p →' q →' p)
| axs {p q r} : prf $ (p →' q →' r) →' (p →' q) →' (p →' r)
| axX {p q} : prf $ ((¬' q) →' (¬' p)) →' p →' q
| mp {p q} : prf p → prf (p →' q) → prf q

open prf

lemma p_of_p_of_p_of_q (p q : fml) : prf $ (p →' q) →' (p →' p) :=
begin
  apply mp (p →' q →' p) ((p →' q) →' p →' p) (axk p q),
  exact axs p q p
end

lemma p_of_p_of_p_of_q' (p q : fml) : prf $ (p →' q) →' (p →' p) :=
mp (p →' q →' p) ((p →' q) →' p →' p) (axk p q) (axs p q p)

lemma p_of_p_of_p_of_q'' (p q : fml) : prf $ (p →' q) →' (p →' p) :=
mp axk axs
-- question 6a on Britnell sheet
theorem p_of_p (p : fml) : prf $ p →' p :=
begin
  exact mp (p →' p →' p) (p →' p) (axk p p) (p_of_p_of_p_of_q p (p →' p)),
end


/-
Copyright (c) 2020 Paul van Wamelen. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Paul van Wamelen
-/
import algebra.field.basic
import ring_theory.int.basic
import tactic.ring
import tactic.ring_exp
import tactic.field_simp
import data.int.nat_prime
import data.zmod.basic

import ntac
run_cmd ntac.copy_decls
/-!
# Pythagorean Triples

The main result is the classification of Pythagorean triples. The final result is for general
Pythagorean triples. It follows from the more interesting relatively prime case. We use the
"rational parametrization of the circle" method for the proof. The parametrization maps the point
`(x / z, y / z)` to the slope of the line through `(-1 , 0)` and `(x / z, y / z)`. This quickly
shows that `(x / z, y / z) = (2 * m * n / (m ^ 2 + n ^ 2), (m ^ 2 - n ^ 2) / (m ^ 2 + n ^ 2))` where
`m / n` is the slope. In order to identify numerators and denominators we now need results showing
that these are coprime. This is easy except for the prime 2. In order to deal with that we have to
analyze the parity of `x`, `y`, `m` and `n` and eliminate all the impossible cases. This takes up
the bulk of the proof below.
-/

lemma sq_ne_two_fin_zmod_four (z : zmod 4) : z * z ≠ 2 :=
begin
  change fin 4 at z,
  fin_cases z; norm_num [fin.ext_iff, fin.coe_bit0, fin.coe_bit1]
end

lemma int.sq_ne_two_mod_four (z : ℤ) : (z * z) % 4 ≠ 2 :=
suffices ¬ (z * z) % (4 : ℕ) = 2 % (4 : ℕ), by norm_num at this,
begin
  rw ← zmod.int_coe_eq_int_coe_iff',
  simpa using sq_ne_two_fin_zmod_four _
end

noncomputable theory
open_locale classical

/-- Three integers `x`, `y`, and `z` form a Pythagorean triple if `x * x + y * y = z * z`. -/
def pythagorean_triple (x y z : ℤ) : Prop := x * x + y * y = z * z

/-- Pythagorean triples are interchangable, i.e `x * x + y * y = y * y + x * x = z * z`.
This comes from additive commutativity. -/
lemma pythagorean_triple_comm {x y z : ℤ} :
 (pythagorean_triple x y z) ↔ (pythagorean_triple y x z) :=
by { delta pythagorean_triple, rw add_comm }

/-- The zeroth Pythagorean triple is all zeros. -/
lemma pythagorean_triple.zero : pythagorean_triple 0 0 0 :=
by simp only [pythagorean_triple, zero_mul, zero_add]

namespace pythagorean_triple

variables {x y z : ℤ} (h : pythagorean_triple x y z)

include h

lemma eq : x * x + y * y = z * z := h

@[symm]
lemma symm :
  pythagorean_triple y x z :=
by rwa [pythagorean_triple_comm]

/-- A triple is still a triple if you multiply `x`, `y` and `z`
by a constant `k`. -/
lemma mul (k : ℤ) : pythagorean_triple (k * x) (k * y) (k * z) :=
calc (k * x) * (k * x) + (k * y) * (k * y)
      = k ^ 2 * (x * x + y * y) : by ring
  ... = k ^ 2 * (z * z)         : by rw h.eq
  ... = (k * z) * (k * z)       : by ring

omit h

/-- `(k*x, k*y, k*z)` is a Pythagorean triple if and only if
`(x, y, z)` is also a triple. -/
lemma mul_iff (k : ℤ) (hk : k ≠ 0) :
  pythagorean_triple (k * x) (k * y) (k * z) ↔ pythagorean_triple x y z :=
begin
  refine ⟨_, λ h, h.mul k⟩,
  simp only [pythagorean_triple],
  intro h,
  rw ← mul_left_inj' (mul_ne_zero hk hk),
  convert h using 1; ring,
end

include h

/-- A Pythagorean triple `x, y, z` is “classified” if there exist integers `k, m, n` such that
either
 * `x = k * (m ^ 2 - n ^ 2)` and `y = k * (2 * m * n)`, or
 * `x = k * (2 * m * n)` and `y = k * (m ^ 2 - n ^ 2)`. -/
@[nolint unused_arguments] def is_classified := ∃ (k m n : ℤ),
  ((x = k * (m ^ 2 - n ^ 2) ∧ y = k * (2 * m * n))
    ∨ (x = k * (2 * m * n) ∧ y = k * (m ^ 2 - n ^ 2)))
  ∧ int.gcd m n = 1

/-- A primitive pythogorean triple `x, y, z` is a pythagorean triple with `x` and `y` coprime.
 Such a triple is “primitively classified” if there exist coprime integers `m, n` such that either
 * `x = m ^ 2 - n ^ 2` and `y = 2 * m * n`, or
 * `x = 2 * m * n` and `y = m ^ 2 - n ^ 2`.
-/
@[nolint unused_arguments] def is_primitive_classified := ∃ (m n : ℤ),
  ((x = m ^ 2 - n ^ 2 ∧ y = 2 * m * n)
    ∨ (x = 2 * m * n ∧ y = m ^ 2 - n ^ 2))
  ∧ int.gcd m n = 1
  ∧ ((m % 2 = 0 ∧ n % 2 = 1) ∨ (m % 2 = 1 ∧ n % 2 = 0))

lemma mul_is_classified (k : ℤ) (hc : h.is_classified) : (h.mul k).is_classified :=
begin
  obtain ⟨l, m, n, ⟨⟨rfl, rfl⟩ | ⟨rfl, rfl⟩, co⟩⟩ := hc,
  { use [k * l, m, n], apply and.intro _ co, left, split; ring },
  { use [k * l, m, n], apply and.intro _ co, right, split; ring },
end

lemma even_odd_of_coprime (hc : int.gcd x y = 1) :
  (x % 2 = 0 ∧ y % 2 = 1) ∨ (x % 2 = 1 ∧ y % 2 = 0) :=
begin
  cases int.mod_two_eq_zero_or_one x with hx hx;
    cases int.mod_two_eq_zero_or_one y with hy hy,
  { -- x even, y even
    exfalso,
    apply nat.not_coprime_of_dvd_of_dvd (dec_trivial : 1 < 2) _ _ hc,
    { apply int.dvd_nat_abs_of_of_nat_dvd, apply int.dvd_of_mod_eq_zero hx },
    { apply int.dvd_nat_abs_of_of_nat_dvd, apply int.dvd_of_mod_eq_zero hy } },
  { left, exact ⟨hx, hy⟩ },  -- x even, y odd
  { right, exact ⟨hx, hy⟩ }, -- x odd, y even
  { -- x odd, y odd
    exfalso,
    obtain ⟨x0, y0, rfl, rfl⟩ : ∃ x0 y0, x = x0* 2 + 1 ∧ y = y0 * 2 + 1,
    { cases exists_eq_mul_left_of_dvd (int.dvd_sub_of_mod_eq hx) with x0 hx2,
      cases exists_eq_mul_left_of_dvd (int.dvd_sub_of_mod_eq hy) with y0 hy2,
      rw sub_eq_iff_eq_add at hx2 hy2, exact ⟨x0, y0, hx2, hy2⟩ },
    apply int.sq_ne_two_mod_four z,
    rw show z * z = 4 * (x0 * x0 + x0 + y0 * y0 + y0) + 2, by { rw ← h.eq, ring },
    norm_num [int.add_mod] }
end

lemma gcd_dvd : (int.gcd x y : ℤ) ∣ z :=
begin
  by_cases h0 : int.gcd x y = 0,
  { have hx : x = 0, { apply int.nat_abs_eq_zero.mp, apply nat.eq_zero_of_gcd_eq_zero_left h0 },
    have hy : y = 0, { apply int.nat_abs_eq_zero.mp, apply nat.eq_zero_of_gcd_eq_zero_right h0 },
    have hz : z = 0,
    { simpa only [pythagorean_triple, hx, hy, add_zero, zero_eq_mul, mul_zero, or_self] using h },
    simp only [hz, dvd_zero], },
  obtain ⟨k, x0, y0, k0, h2, rfl, rfl⟩ :
    ∃ (k : ℕ) x0 y0, 0 < k ∧ int.gcd x0 y0 = 1 ∧ x = x0 * k ∧ y = y0 * k :=
    int.exists_gcd_one' (nat.pos_of_ne_zero h0),
  rw [int.gcd_mul_right, h2, int.nat_abs_of_nat, one_mul],
  rw [← int.pow_dvd_pow_iff zero_lt_two, sq z, ← h.eq],
  rw (by ring : x0 * k * (x0 * k) + y0 * k * (y0 * k) = k ^ 2 * (x0 * x0 + y0 * y0)),
  exact dvd_mul_right _ _
end

lemma normalize : pythagorean_triple (x / int.gcd x y) (y / int.gcd x y) (z / int.gcd x y) :=
begin
  by_cases h0 : int.gcd x y = 0,
  { have hx : x = 0, { apply int.nat_abs_eq_zero.mp, apply nat.eq_zero_of_gcd_eq_zero_left h0 },
    have hy : y = 0, { apply int.nat_abs_eq_zero.mp, apply nat.eq_zero_of_gcd_eq_zero_right h0 },
    have hz : z = 0,
    { simpa only [pythagorean_triple, hx, hy, add_zero, zero_eq_mul, mul_zero, or_self] using h },
    simp only [hx, hy, hz, int.zero_div], exact zero },
  rcases h.gcd_dvd with ⟨z0, rfl⟩,
  obtain ⟨k, x0, y0, k0, h2, rfl, rfl⟩ :
    ∃ (k : ℕ) x0 y0, 0 < k ∧ int.gcd x0 y0 = 1 ∧ x = x0 * k ∧ y = y0 * k :=
    int.exists_gcd_one' (nat.pos_of_ne_zero h0),
  have hk : (k : ℤ) ≠ 0, { norm_cast, rwa pos_iff_ne_zero at k0 },
  rw [int.gcd_mul_right, h2, int.nat_abs_of_nat, one_mul] at h ⊢,
  rw [mul_comm x0, mul_comm y0, mul_iff k hk] at h,
  rwa [int.mul_div_cancel _ hk, int.mul_div_cancel _ hk, int.mul_div_cancel_left _ hk],
end

lemma is_classified_of_is_primitive_classified (hp : h.is_primitive_classified) :
  h.is_classified :=
begin
  obtain ⟨m, n, H⟩ := hp,
  use [1, m, n],
  rcases H with ⟨t, co, pp⟩,
  rw [one_mul, one_mul],
  exact ⟨t, co⟩,
end

lemma is_classified_of_normalize_is_primitive_classified
  (hc : h.normalize.is_primitive_classified) : h.is_classified :=
begin
  convert h.normalize.mul_is_classified (int.gcd x y)
    (is_classified_of_is_primitive_classified h.normalize hc);
  rw int.mul_div_cancel',
  { exact int.gcd_dvd_left x y },
  { exact int.gcd_dvd_right x y },
  { exact h.gcd_dvd }
end

lemma ne_zero_of_coprime (hc : int.gcd x y = 1) : z ≠ 0 :=
begin
  suffices : 0 < z * z, { rintro rfl, norm_num at this },
  rw [← h.eq, ← sq, ← sq],
  have hc' : int.gcd x y ≠ 0, { rw hc, exact one_ne_zero },
  cases int.ne_zero_of_gcd hc' with hxz hyz,
  { apply lt_add_of_pos_of_le (sq_pos_of_ne_zero x hxz) (sq_nonneg y) },
  { apply lt_add_of_le_of_pos (sq_nonneg x) (sq_pos_of_ne_zero y hyz) }
end

lemma is_primitive_classified_of_coprime_of_zero_left (hc : int.gcd x y = 1) (hx : x = 0) :
  h.is_primitive_classified :=
begin
  subst x,
  change nat.gcd 0 (int.nat_abs y) = 1 at hc,
  rw [nat.gcd_zero_left (int.nat_abs y)] at hc,
  cases int.nat_abs_eq y with hy hy,
  { use [1, 0], rw [hy, hc, int.gcd_zero_right], norm_num },
  { use [0, 1], rw [hy, hc, int.gcd_zero_left], norm_num }
end

lemma coprime_of_coprime (hc : int.gcd x y = 1) : int.gcd y z = 1 :=
begin[ntac]
  by_contradiction H,
  obtain ⟨p, hp, hpy, hpz⟩ := nat.prime.not_coprime_iff_dvd.mp H,
  apply hp.not_dvd_one,
  rw [← hc],
  apply nat.dvd_gcd (int.prime.dvd_nat_abs_of_coe_dvd_sq hp _ _) hpy,
  rw [sq, eq_sub_of_add_eq h],
  rw [← int.coe_nat_dvd_left] at hpy hpz,
  exact dvd_sub ((hpz).mul_right _) ((hpy).mul_right _),
end

end pythagorean_triple

section circle_equiv_gen
/-!
### A parametrization of the unit circle

For the classification of pythogorean triples, we will use a parametrization of the unit circle.
-/

variables {K : Type*} [field K]

/--  A parameterization of the unit circle that is useful for classifying Pythagorean triples.
 (To be applied in the case where `K = ℚ`.) -/
def circle_equiv_gen (hk : ∀ x : K, 1 + x^2 ≠ 0) :
  K ≃ {p : K × K // p.1^2 + p.2^2 = 1 ∧ p.2 ≠ -1} :=
{ to_fun := λ x, ⟨⟨2 * x / (1 + x^2), (1 - x^2) / (1 + x^2)⟩,
    by { field_simp [hk x, div_pow], ring },
    begin
      simp only [ne.def, div_eq_iff (hk x), neg_mul, one_mul, neg_add,
        sub_eq_add_neg, add_left_inj],
      simpa only [eq_neg_iff_add_eq_zero, one_pow] using hk 1,
    end⟩,
  inv_fun := λ p, (p : K × K).1 / ((p : K × K).2 + 1),
  left_inv := λ x,
  begin
    have h2 : (1 + 1 : K) = 2 := rfl,
    have h3 : (2 : K) ≠ 0, { convert hk 1, rw [one_pow 2, h2] },
    field_simp [hk x, h2, add_assoc, add_comm, add_sub_cancel'_right, mul_comm],
  end,
  right_inv := λ ⟨⟨x, y⟩, hxy, hy⟩,
  begin
    change x ^ 2 + y ^ 2 = 1 at hxy,
    have h2 : y + 1 ≠ 0 := mt eq_neg_of_add_eq_zero_left hy,
    have h3 : (y + 1) ^ 2 + x ^ 2 = 2 * (y + 1),
    { rw [(add_neg_eq_iff_eq_add.mpr hxy.symm).symm], ring },
    have h4 : (2 : K) ≠ 0, { convert hk 1, rw one_pow 2, refl },
    simp only [prod.mk.inj_iff, subtype.mk_eq_mk],
    split,
    { field_simp [h3], ring },
    { field_simp [h3], rw [← add_neg_eq_iff_eq_add.mpr hxy.symm], ring }
  end }

@[simp] lemma circle_equiv_apply (hk : ∀ x : K, 1 + x^2 ≠ 0) (x : K) :
  (circle_equiv_gen hk x : K × K) = ⟨2 * x / (1 + x^2), (1 - x^2) / (1 + x^2)⟩ := rfl

@[simp] lemma circle_equiv_symm_apply (hk : ∀ x : K, 1 + x^2 ≠ 0)
  (v : {p : K × K // p.1^2 + p.2^2 = 1 ∧ p.2 ≠ -1}) :
  (circle_equiv_gen hk).symm v = (v : K × K).1 / ((v : K × K).2 + 1) := rfl

end circle_equiv_gen

private lemma coprime_sq_sub_sq_add_of_even_odd {m n : ℤ} (h : int.gcd m n = 1)
  (hm : m % 2 = 0) (hn : n % 2 = 1) :
  int.gcd (m ^ 2 - n ^ 2) (m ^ 2 + n ^ 2) = 1 :=
begin
  by_contradiction H,
  obtain ⟨p, hp, hp1, hp2⟩ := nat.prime.not_coprime_iff_dvd.mp H,
  rw ← int.coe_nat_dvd_left at hp1 hp2,
  have h2m : (p : ℤ) ∣ 2 * m ^ 2, { convert dvd_add hp2 hp1, ring },
  have h2n : (p : ℤ) ∣ 2 * n ^ 2, { convert dvd_sub hp2 hp1, ring },
  have hmc : p = 2 ∨ p ∣ int.nat_abs m :=
    prime_two_or_dvd_of_dvd_two_mul_pow_self_two hp h2m,
  have hnc : p = 2 ∨ p ∣ int.nat_abs n :=
    prime_two_or_dvd_of_dvd_two_mul_pow_self_two hp h2n,
  by_cases h2 : p = 2,
  { have h3 : (m ^ 2 + n ^ 2) % 2 = 1, { norm_num [sq, int.add_mod, int.mul_mod, hm, hn] },
    have h4 : (m ^ 2 + n ^ 2) % 2 = 0, { apply int.mod_eq_zero_of_dvd, rwa h2 at hp2 },
    rw h4 at h3, exact zero_ne_one h3 },
  { apply hp.not_dvd_one,
    rw ← h,
    exact nat.dvd_gcd (or.resolve_left hmc h2) (or.resolve_left hnc h2), }
end

private lemma coprime_sq_sub_sq_add_of_odd_even {m n : ℤ} (h : int.gcd m n = 1)
  (hm : m % 2 = 1) (hn : n % 2 = 0):
  int.gcd (m ^ 2 - n ^ 2) (m ^ 2 + n ^ 2) = 1 :=
begin
  rw [int.gcd, ← int.nat_abs_neg (m ^ 2 - n ^ 2)],
  rw [(by ring : -(m ^ 2 - n ^ 2) = n ^ 2 - m ^ 2), add_comm],
  apply coprime_sq_sub_sq_add_of_even_odd _ hn hm, rwa [int.gcd_comm],
end

private lemma coprime_sq_sub_mul_of_even_odd {m n : ℤ} (h : int.gcd m n = 1)
  (hm : m % 2 = 0) (hn : n % 2 = 1) :
  int.gcd (m ^ 2 - n ^ 2) (2 * m * n) = 1 :=
begin
  by_contradiction H,
  obtain ⟨p, hp, hp1, hp2⟩ := nat.prime.not_coprime_iff_dvd.mp H,
  rw ← int.coe_nat_dvd_left at hp1 hp2,
  have hnp : ¬ (p : ℤ) ∣ int.gcd m n,
  { rw h, norm_cast, exact mt nat.dvd_one.mp (nat.prime.ne_one hp) },
  cases int.prime.dvd_mul hp hp2 with hp2m hpn,
  { rw int.nat_abs_mul at hp2m,
    cases (nat.prime.dvd_mul hp).mp hp2m with hp2 hpm,
    { have hp2' : p = 2 := (nat.le_of_dvd zero_lt_two hp2).antisymm hp.two_le,
      revert hp1, rw hp2',
      apply mt int.mod_eq_zero_of_dvd,
      norm_num [sq, int.sub_mod, int.mul_mod, hm, hn] },
    apply mt (int.dvd_gcd (int.coe_nat_dvd_left.mpr hpm)) hnp,
    apply (or_self _).mp, apply int.prime.dvd_mul' hp,
    rw (by ring : n * n = - (m ^ 2 - n ^ 2) + m * m),
    apply dvd_add (dvd_neg_of_dvd hp1),
    exact dvd_mul_of_dvd_left (int.coe_nat_dvd_left.mpr hpm) m },
  rw int.gcd_comm at hnp,
  apply mt (int.dvd_gcd (int.coe_nat_dvd_left.mpr hpn)) hnp,
  apply (or_self _).mp, apply int.prime.dvd_mul' hp,
  rw (by ring : m * m = (m ^ 2 - n ^ 2) + n * n),
  apply dvd_add hp1,
  exact (int.coe_nat_dvd_left.mpr hpn).mul_right n
end

private lemma coprime_sq_sub_mul_of_odd_even {m n : ℤ} (h : int.gcd m n = 1)
  (hm : m % 2 = 1) (hn : n % 2 = 0) :
  int.gcd (m ^ 2 - n ^ 2) (2 * m * n) = 1 :=
begin
  rw [int.gcd, ← int.nat_abs_neg (m ^ 2 - n ^ 2)],
  rw [(by ring : 2 * m * n = 2 * n * m), (by ring : -(m ^ 2 - n ^ 2) = n ^ 2 - m ^ 2)],
  apply coprime_sq_sub_mul_of_even_odd _ hn hm, rwa [int.gcd_comm]
end

private lemma coprime_sq_sub_mul {m n : ℤ} (h : int.gcd m n = 1)
  (hmn : (m % 2 = 0 ∧ n % 2 = 1) ∨ (m % 2 = 1 ∧ n % 2 = 0)) :
  int.gcd (m ^ 2 - n ^ 2) (2 * m * n) = 1 :=
begin
  cases hmn with h1 h2,
  { exact coprime_sq_sub_mul_of_even_odd h h1.left h1.right },
  { exact coprime_sq_sub_mul_of_odd_even h h2.left h2.right }
end

private lemma coprime_sq_sub_sq_sum_of_odd_odd {m n : ℤ} (h : int.gcd m n = 1)
  (hm : m % 2 = 1) (hn : n % 2 = 1) :
  2 ∣ m ^ 2 + n ^ 2
  ∧ 2 ∣ m ^ 2 - n ^ 2
  ∧ ((m ^ 2 - n ^ 2) / 2) % 2 = 0
  ∧ int.gcd ((m ^ 2 - n ^ 2) / 2) ((m ^ 2 + n ^ 2) / 2) = 1 :=
begin
  cases exists_eq_mul_left_of_dvd (int.dvd_sub_of_mod_eq hm) with m0 hm2,
  cases exists_eq_mul_left_of_dvd (int.dvd_sub_of_mod_eq hn) with n0 hn2,
  rw sub_eq_iff_eq_add at hm2 hn2, subst m, subst n,
  have h1 : (m0 * 2 + 1) ^ 2 + (n0 * 2 + 1) ^ 2 = 2 * (2 * (m0 ^ 2 + n0 ^ 2 + m0 + n0) + 1),
  by ring_exp,
  have h2 : (m0 * 2 + 1) ^ 2 - (n0 * 2 + 1) ^ 2 = 2 * (2 * (m0 ^ 2 - n0 ^ 2 + m0 - n0)),
  by ring_exp,
  have h3 : ((m0 * 2 + 1) ^ 2 - (n0 * 2 + 1) ^ 2) / 2 % 2 = 0,
  { rw [h2, int.mul_div_cancel_left, int.mul_mod_right], exact dec_trivial },
  refine ⟨⟨_, h1⟩, ⟨_, h2⟩, h3, _⟩,
  have h20 : (2:ℤ) ≠ 0 := dec_trivial,
  rw [h1, h2, int.mul_div_cancel_left _ h20, int.mul_div_cancel_left _ h20],
  by_contra h4,
  obtain ⟨p, hp, hp1, hp2⟩ := nat.prime.not_coprime_iff_dvd.mp h4,
  apply hp.not_dvd_one,
  rw ← h,
  rw ← int.coe_nat_dvd_left at hp1 hp2,
  apply nat.dvd_gcd,
  { apply int.prime.dvd_nat_abs_of_coe_dvd_sq hp,
    convert dvd_add hp1 hp2, ring_exp },
  { apply int.prime.dvd_nat_abs_of_coe_dvd_sq hp,
    convert dvd_sub hp2 hp1, ring_exp },
end

namespace pythagorean_triple

variables {x y z : ℤ} (h : pythagorean_triple x y z)

include h

lemma is_primitive_classified_aux (hc : x.gcd y = 1) (hzpos : 0 < z)
  {m n : ℤ} (hm2n2 : 0 < m ^ 2 + n ^ 2)
  (hv2 : (x : ℚ) / z = 2 * m * n / (m ^ 2 + n ^ 2))
  (hw2 : (y : ℚ) / z = (m ^ 2 - n ^ 2) / (m ^ 2 + n ^ 2))
  (H : int.gcd (m ^ 2 - n ^ 2) (m ^ 2 + n ^ 2) = 1)
  (co : int.gcd m n = 1)
  (pp : (m % 2 = 0 ∧ n % 2 = 1) ∨ (m % 2 = 1 ∧ n % 2 = 0)):
  h.is_primitive_classified :=
begin
  have hz : z ≠ 0, apply ne_of_gt hzpos,
  have h2 : y = m ^ 2 - n ^ 2 ∧ z = m ^ 2 + n ^ 2,
  { apply rat.div_int_inj hzpos hm2n2 (h.coprime_of_coprime hc) H, rw [hw2], norm_cast },
  use [m, n], apply and.intro _ (and.intro co pp), right,
  refine ⟨_, h2.left⟩,
  rw [← rat.coe_int_inj _ _, ← div_left_inj' ((mt (rat.coe_int_inj z 0).mp) hz), hv2, h2.right],
  norm_cast
end

theorem is_primitive_classified_of_coprime_of_odd_of_pos
  (hc : int.gcd x y = 1) (hyo : y % 2 = 1) (hzpos : 0 < z) :
  h.is_primitive_classified :=
begin[ntac]
  NTAC_focus1_str {
  by_cases h0 : x = 0, { exact h.is_primitive_classified_of_coprime_of_zero_left hc h0 },
  }"If $x=0$, $x=2*1*0, y=1^1-0^0, z=1^1+0^0$ satisfies $(x, y, z) = (0, 1, 1)$, so we assume $x≠0$. " tt tt,
  NTAC_focus1_list {
  let v := (x : ℚ) / z,
  let w := (y : ℚ) / z,
  }  ["let ",v = x/z,",",  w = y/z,". "] ff tt,
  have hp : (⟨v, w⟩ : ℚ × ℚ) ∈ {p : ℚ × ℚ | p.1^2 + p.2^2 = 1 ∧ p.2 ≠ -1},
  NTAC_focus1_list{
  have hz : z ≠ 0, apply ne_of_gt hzpos,
  have hq : v ^ 2 + w ^ 2 = 1,
  { field_simp [hz, sq], norm_cast, exact h },
  have hvz : v ≠ 0, { field_simp [hz], exact h0 },
  have hw1 : w ≠ -1,
  { contrapose! hvz with hw1,
    rw [hw1, neg_sq, one_pow, add_left_eq_self] at hq,
    exact pow_eq_zero hq, },
    exact ⟨hq, hw1⟩}
    ["Since ",z≠0, ", we have ", v^2+w^2=1," and ", w≠-1,". "] tt tt,

  have hQ : ∀ x : ℚ, 1 + x^2 ≠ 0,
  NTAC_solve1_auto
  { intro q, apply ne_of_gt, exact lt_add_of_pos_of_le zero_lt_one (sq_nonneg q) },
  NTAC_focus1_str {
  let q := (circle_equiv_gen hQ).symm ⟨⟨v, w⟩, hp⟩}
  "The point $(v, w)$ is on unit circle except (0, -1), so we have a rational number $q$ defined as the y coordinate of the intersection of x-axis and the line from this point to (0, -1). " tt ff,
  have ht4 : v = 2 * q / (1 + q ^ 2) ∧ w = (1 - q ^ 2) / (1 + q ^ 2),
  NTAC_focus1_list
  {apply prod.mk.inj,
    have := ((circle_equiv_gen hQ).apply_symm_apply ⟨⟨v, w⟩, hp⟩).symm,
    exact congr_arg subtype.val this,}
    ["If we express $v$, $w$ by $q$, we have ",v = 2*q / (1 + q^2), ",",w = (1 - q^2) / (1 + q^2),". "] tt tt,
  NTAC_focus1_str {
  let m := (q.denom : ℤ),
  let n := q.num,
  }"Let $m$ and $n$ be the denominator and numerator of the irredducible fraction $q$. " tt ff,

  have hq2 : q = n / m := (rat.num_div_denom q).symm,
  have hm2n2 : 0 < m ^ 2 + n ^ 2,
  NTAC_focus1_list
  {{ apply lt_add_of_pos_of_le _ (sq_nonneg n),
    have hm0 : m ≠ 0, { norm_cast, apply rat.denom_ne_zero q },
    exact lt_of_le_of_ne (sq_nonneg m) (ne.symm (pow_ne_zero 2 hm0)) }}
    ["Since ", m ≠ 0, ",we have ", 0 < m ^ 2 + n ^ 2, " ."] tt tt

    ,
  have hw2 : w = (m ^ 2 - n ^ 2) / (m ^ 2 + n ^ 2),
  NTAC_focus1_list{ rw [ht4.2, hq2], field_simp [hm2n2, rat.denom_ne_zero q, -rat.num_div_denom] }
  ["Then ",w = (m^2 - n^2) / (m^2 + n^2)," and "] tt tt,
  have hv2 : v = 2 * m * n / (m ^ 2 + n ^ 2),
  NTAC_focus1_list
  {
    have hm2n20 : (m : ℚ) ^ 2 + (n : ℚ) ^ 2 ≠ 0,
  { norm_cast, simpa only [int.coe_nat_pow] using ne_of_gt hm2n2 },
    apply eq.symm, apply (div_eq_iff hm2n20).mpr, rw [ht4.1], field_simp [hQ q],
    rw [hq2] {occs := occurrences.pos [2, 3]},
    field_simp [rat.denom_ne_zero q, -rat.num_div_denom],
    ring } [v = (2*↑m*↑n / (↑m^2 + ↑n^2)),"holds. "] tt tt,
  NTAC_focus1_str {
  have hnmcp : int.gcd n m = 1 := q.cop,
  have hmncp : int.gcd m n = 1, { rw int.gcd_comm, exact hnmcp },
  cases int.mod_two_eq_zero_or_one m with hm2 hm2;
    cases int.mod_two_eq_zero_or_one n with hn2 hn2,
    } "Prove by cases. " tt tt,
    NTAC_focus1_str{ -- m even, n even
    exfalso,
    have h1 : 2 ∣ (int.gcd n m : ℤ),
    { exact int.dvd_gcd (int.dvd_of_mod_eq_zero hn2) (int.dvd_of_mod_eq_zero hm2) },
    rw hnmcp at h1, revert h1, norm_num }
    "Since $m$ and $n$ are coprime, not both $m$ and $n$ are even. " tt tt,
  NTAC_focus1_str
  { -- m even, n odd
    apply h.is_primitive_classified_aux hc hzpos hm2n2 hv2 hw2 _ hmncp,
    { apply or.intro_left, exact and.intro hm2 hn2 },
    { apply coprime_sq_sub_sq_add_of_even_odd hmncp hm2 hn2 } }
    "If $m$ is even and $n$ is odd, $m, n$ satisfy the condition. " tt tt ,
  NTAC_focus1_str
  { -- m odd, n even
    apply h.is_primitive_classified_aux hc hzpos hm2n2 hv2 hw2 _ hmncp,
    { apply or.intro_right, exact and.intro hm2 hn2 },
    apply coprime_sq_sub_sq_add_of_odd_even hmncp hm2 hn2 }
  "Same for the case $m$ is odd and $n$ is even. " tt tt,
  NTAC_focus1_list { -- m odd, n odd
    exfalso,
    have h1 : 2 ∣ m ^ 2 + n ^ 2 ∧ 2 ∣ m ^ 2 - n ^ 2
      ∧ ((m ^ 2 - n ^ 2) / 2) % 2 = 0 ∧ int.gcd ((m ^ 2 - n ^ 2) / 2) ((m ^ 2 + n ^ 2) / 2) = 1,
    { exact coprime_sq_sub_sq_sum_of_odd_odd hmncp hm2 hn2 },
    have h2 : y = (m ^ 2 - n ^ 2) / 2 ∧ z = (m ^ 2 + n ^ 2) / 2,
    { apply rat.div_int_inj hzpos _ (h.coprime_of_coprime hc) h1.2.2.2,
      { show w = _, rw [←rat.mk_eq_div, ←(rat.div_mk_div_cancel_left (by norm_num : (2 : ℤ) ≠ 0))],
        rw [int.div_mul_cancel h1.1, int.div_mul_cancel h1.2.1, hw2], norm_cast },
      { apply (mul_lt_mul_right (by norm_num : 0 < (2 : ℤ))).mp,
        rw [int.div_mul_cancel h1.1, zero_mul], exact hm2n2 } },
    rw [h2.1, h1.2.2.1] at hyo,
    revert hyo,
    norm_num }
    ["In the case both are odd, since the square of an odd number is congruent to $1$ modulo $4$, ",z = (m^2 + n^2)/2,",", y = (m^2 - n^2)/2 ,", which contradicts the assumption that $y$ is odd. (Note that $y$ and $z$ are coprime). "] tt tt,
    trace_state, trace_proof LANG_en, trace_proof_file LANG_en
end

theorem is_primitive_classified_of_coprime_of_pos (hc : int.gcd x y = 1) (hzpos : 0 < z):
  h.is_primitive_classified :=
begin
  cases h.even_odd_of_coprime hc with h1 h2,
  { exact (h.is_primitive_classified_of_coprime_of_odd_of_pos hc h1.right hzpos) },
  rw int.gcd_comm at hc,
  obtain ⟨m, n, H⟩ := (h.symm.is_primitive_classified_of_coprime_of_odd_of_pos hc h2.left hzpos),
  use [m, n], tauto
end

theorem is_primitive_classified_of_coprime (hc : int.gcd x y = 1) : h.is_primitive_classified :=
begin
  by_cases hz : 0 < z,
  { exact h.is_primitive_classified_of_coprime_of_pos hc hz },
  have h' : pythagorean_triple x y (-z),
  { simpa [pythagorean_triple, neg_mul_neg] using h.eq, },
  apply h'.is_primitive_classified_of_coprime_of_pos hc,
  apply lt_of_le_of_ne _ (h'.ne_zero_of_coprime hc).symm,
  exact le_neg.mp (not_lt.mp hz)
end

theorem classified : h.is_classified :=
begin
  by_cases h0 : int.gcd x y = 0,
  { have hx : x = 0, { apply int.nat_abs_eq_zero.mp, apply nat.eq_zero_of_gcd_eq_zero_left h0 },
    have hy : y = 0, { apply int.nat_abs_eq_zero.mp, apply nat.eq_zero_of_gcd_eq_zero_right h0 },
    use [0, 1, 0], norm_num [hx, hy], },
  apply h.is_classified_of_normalize_is_primitive_classified,
  apply h.normalize.is_primitive_classified_of_coprime,
  apply int.gcd_div_gcd_div_gcd (nat.pos_of_ne_zero h0),
end

omit h

theorem coprime_classification :
  pythagorean_triple x y z ∧ int.gcd x y = 1 ↔
  ∃ m n, ((x = m ^ 2 - n ^ 2 ∧ y = 2 * m * n) ∨
            (x = 2 * m * n ∧ y = m ^ 2 - n ^ 2))
          ∧ (z = m ^ 2 + n ^ 2 ∨ z = - (m ^ 2 + n ^ 2))
          ∧ int.gcd m n = 1
          ∧ ((m % 2 = 0 ∧ n % 2 = 1) ∨ (m % 2 = 1 ∧ n % 2 = 0)) :=
begin
  split,
  { intro h,
    obtain ⟨m, n, H⟩ := h.left.is_primitive_classified_of_coprime h.right,
    use [m, n],
    rcases H with ⟨⟨rfl, rfl⟩ | ⟨rfl, rfl⟩, co, pp⟩,
    { refine ⟨or.inl ⟨rfl, rfl⟩, _, co, pp⟩,
      have : z ^ 2 = (m ^ 2 + n ^ 2) ^ 2,
      { rw [sq, ← h.left.eq], ring },
      simpa using eq_or_eq_neg_of_sq_eq_sq _ _ this },
    { refine ⟨or.inr ⟨rfl, rfl⟩, _, co, pp⟩,
      have : z ^ 2 = (m ^ 2 + n ^ 2) ^ 2,
      { rw [sq, ← h.left.eq], ring },
      simpa using eq_or_eq_neg_of_sq_eq_sq _ _ this } },
  { delta pythagorean_triple,
    rintro ⟨m, n, ⟨rfl, rfl⟩ | ⟨rfl, rfl⟩, rfl | rfl, co, pp⟩;
    { split, { ring }, exact coprime_sq_sub_mul co pp }
    <|>
    { split, { ring }, rw int.gcd_comm, exact coprime_sq_sub_mul co pp } }
end

/-- by assuming `x` is odd and `z` is positive we get a slightly more precise classification of
the pythagorean triple `x ^ 2 + y ^ 2 = z ^ 2`-/
theorem coprime_classification' {x y z : ℤ} (h : pythagorean_triple x y z)
  (h_coprime : int.gcd x y = 1) (h_parity : x % 2 = 1) (h_pos : 0 < z) :
  ∃ m n,  x = m ^ 2 - n ^ 2
        ∧ y = 2 * m * n
        ∧ z = m ^ 2 + n ^ 2
        ∧ int.gcd m n = 1
        ∧ ((m % 2 = 0 ∧ n % 2 = 1) ∨ (m % 2 = 1 ∧ n % 2 = 0))
        ∧ 0 ≤ m :=
begin
  obtain ⟨m, n, ht1, ht2, ht3, ht4⟩ :=
    pythagorean_triple.coprime_classification.mp (and.intro h h_coprime),
  cases le_or_lt 0 m with hm hm,
  { use [m, n],
    cases ht1 with h_odd h_even,
    { apply and.intro h_odd.1,
      apply and.intro h_odd.2,
      cases ht2 with h_pos h_neg,
      { apply and.intro h_pos (and.intro ht3 (and.intro ht4 hm)) },
      { exfalso, revert h_pos, rw h_neg,
        exact imp_false.mpr (not_lt.mpr (neg_nonpos.mpr (add_nonneg (sq_nonneg m)
          (sq_nonneg n)))) } },
    exfalso,
    rcases h_even with ⟨rfl, -⟩,
    rw [mul_assoc, int.mul_mod_right] at h_parity,
    exact zero_ne_one h_parity },
  { use [-m, -n],
    cases ht1 with h_odd h_even,
    { rw [neg_sq m],
      rw [neg_sq n],
      apply and.intro h_odd.1,
      split, { rw h_odd.2, ring },
      cases ht2 with h_pos h_neg,
      { apply and.intro h_pos,
        split,
        { delta int.gcd, rw [int.nat_abs_neg, int.nat_abs_neg], exact ht3 },
        { rw [int.neg_mod_two, int.neg_mod_two],
          apply and.intro ht4, linarith } },
      { exfalso, revert h_pos, rw h_neg,
        exact imp_false.mpr (not_lt.mpr (neg_nonpos.mpr (add_nonneg (sq_nonneg m)
          (sq_nonneg n)))) } },
    exfalso,
    rcases h_even with ⟨rfl, -⟩,
    rw [mul_assoc, int.mul_mod_right] at h_parity,
    exact zero_ne_one h_parity }
end

/-- **Formula for Pythagorean Triples** -/
theorem classification :
  pythagorean_triple x y z ↔
  ∃ k m n, ((x = k * (m ^ 2 - n ^ 2) ∧ y = k * (2 * m * n)) ∨
            (x = k * (2 * m * n) ∧ y = k * (m ^ 2 - n ^ 2)))
          ∧ (z = k * (m ^ 2 + n ^ 2) ∨ z = - k * (m ^ 2 + n ^ 2)) :=
begin
  split,
  { intro h,
    obtain ⟨k, m, n, H⟩ := h.classified,
    use [k, m, n],
    rcases H with ⟨rfl, rfl⟩ | ⟨rfl, rfl⟩,
    { refine ⟨or.inl ⟨rfl, rfl⟩, _⟩,
      have : z ^ 2 = (k * (m ^ 2 + n ^ 2)) ^ 2,
      { rw [sq, ← h.eq], ring },
      simpa using eq_or_eq_neg_of_sq_eq_sq _ _ this },
    { refine ⟨or.inr ⟨rfl, rfl⟩, _⟩,
      have : z ^ 2 = (k * (m ^ 2 + n ^ 2)) ^ 2,
      { rw [sq, ← h.eq], ring },
      simpa using eq_or_eq_neg_of_sq_eq_sq _ _ this } },
  { rintro ⟨k, m, n, ⟨rfl, rfl⟩ | ⟨rfl, rfl⟩, rfl | rfl⟩;
    delta pythagorean_triple; ring }
end

end pythagorean_triple