import Mathlib.Tactic

-- eps-closeness on rationals (from Section 4.3)
def Rat.Close (ε : ℚ) (x y:ℚ) := |x-y| ≤ ε

namespace Chapter5

-- Section 5.1: Sequence definition
@[ext]
structure Sequence where
  n₀ : ℤ
  seq : ℤ → ℚ
  vanish : ∀ n < n₀, seq n = 0

instance Sequence.instCoeFun : CoeFun Sequence (fun _ ↦ ℤ → ℚ) where
  coe := fun a ↦ a.seq

@[coe]
def Sequence.ofNatFun (a : ℕ → ℚ) : Sequence where
    n₀ := 0
    seq n := if n ≥ 0 then a n.toNat else 0
    vanish := by
      intro n hn
      have : ¬ n ≥ 0 := by exact Int.not_le.mpr hn
      simp [this]

instance : Coe (ℕ → ℚ) Sequence where
  coe := Sequence.ofNatFun

abbrev Sequence.mk' (n₀:ℤ) (a: { n // n ≥ n₀ } → ℚ) : Sequence where
  n₀ := n₀
  seq n := if h : n ≥ n₀ then a ⟨n, h⟩ else 0
  vanish := by
    intro n hn
    have : ¬ n ≥ n₀ := by exact Int.not_le.mpr hn
    simp [this]

lemma Sequence.n0_coe (a: ℕ → ℚ) : (a:Sequence).n₀ = 0 := by rfl

-- ε-steadiness
abbrev Rat.Steady (ε: ℚ) (a: Chapter5.Sequence) : Prop :=
  ∀ n ≥ a.n₀, ∀ m ≥ a.n₀, Rat.Close ε (a n) (a m)

-- start sequence from index n₁
abbrev Sequence.from (a:Sequence) (n₁:ℤ) : Sequence :=
  mk' (max a.n₀ n₁) (fun n ↦ a (n:ℤ))

-- eventually ε-steady
abbrev Rat.EventuallySteady (ε: ℚ) (a: Chapter5.Sequence) : Prop := ∃ N ≥ a.n₀, Rat.Steady ε (a.from N)

-- From Section 5.2: eventually-close sequences (placed early to be available)
abbrev Rat.EventuallyClose (ε: ℚ) (a b: Chapter5.Sequence) : Prop :=
  ∃ N, ∀ n ≥ N, |a n - b n| ≤ ε

-- Cauchy sequences (Section 5.1)
abbrev Sequence.IsCauchy (a:Sequence) : Prop := ∀ ε > (0:ℚ), Rat.EventuallySteady ε a

-- Section 5.3: CauchySequence class and Real construction
@[ext]
class CauchySequence extends Sequence where
  zero : n₀ = 0
  cauchy : toSequence.IsCauchy

theorem CauchySequence.ext' {a b: CauchySequence} (h: a.seq = b.seq) : a = b := by
  apply CauchySequence.ext _ h
  rw [a.zero, b.zero]

-- A sequence starting at 0 that is Cauchy gives a CauchySequence.
abbrev CauchySequence.mk' {a:ℕ → ℚ} (ha: (a:Sequence).IsCauchy) : CauchySequence where
  n₀ := 0
  seq := (a:Sequence).seq
  vanish := by
    intro n hn
    -- use the vanish property of (a:Sequence)
    simpa using ((a:Sequence).vanish n hn)
  zero := rfl
  cauchy := ha

@[simp]
theorem CauchySequence.coe_eq {a:ℕ → ℚ} (ha: (a:Sequence).IsCauchy) :
    (mk' ha).toSequence = (a:Sequence) := rfl

instance CauchySequence.instCoeFun : CoeFun CauchySequence (fun _ ↦ ℕ → ℚ) where
  coe a n := a.toSequence (n:ℤ)

@[simp]
theorem CauchySequence.coe_to_sequence (a: CauchySequence) :
    ((a:ℕ → ℚ):Sequence) = a.toSequence := by
  apply Sequence.ext (by simp [Sequence.n0_coe, a.zero])
  funext n; by_cases h:n ≥ 0
  · simp [Sequence.ofNatFun, h]
  · have hn : n < 0 := lt_of_not_ge h
    have hna : n < a.n₀ := by simpa [a.zero] using hn
    have hv : a.toSequence n = 0 := by simpa using (a.vanish n hna)
    simp [Sequence.ofNatFun, h, hv]

@[simp]
theorem CauchySequence.coe_coe {a:ℕ → ℚ} (ha: (a:Sequence).IsCauchy) : mk' ha = a := by rfl

-- helper: coe CauchySequence to Sequence is Cauchy
lemma CauchySequence.cauchy_coe (a : CauchySequence) : ((a:ℕ → ℚ):Sequence).IsCauchy := by
  simpa [CauchySequence.coe_to_sequence] using a.cauchy

-- From Section 5.2: equivalence of sequences
abbrev Sequence.Equiv (a b: ℕ → ℚ) : Prop :=
  ∀ ε > (0:ℚ), (Rat.EventuallyClose ε (a:Sequence) (b:Sequence))

-- Transitivity of sequence equivalence (from 5.3, proof omitted).
lemma Sequence.equiv_trans {a b c:ℕ → ℚ} (hab: Sequence.Equiv a b) (hbc: Sequence.Equiv b c) :
  Sequence.Equiv a c := by
  sorry

-- Setoid of Cauchy sequences (proofs omitted, as in source).
instance CauchySequence.instSetoid : Setoid CauchySequence where
  r := fun a b ↦ Sequence.Equiv a b
  iseqv :=
    { refl := by sorry
      symm := by sorry
      trans := by sorry }

-- Every constant sequence is Cauchy (from 5.3).
lemma Sequence.IsCauchy.const (a:ℚ) : ((fun _:ℕ ↦ a):Sequence).IsCauchy := by
  sorry

instance CauchySequence.instZero : Zero CauchySequence where
  zero := CauchySequence.mk' (a := fun _: ℕ ↦ 0) (Sequence.IsCauchy.const (0:ℚ))

abbrev Real := Quotient CauchySequence.instSetoid

open Classical in
noncomputable abbrev LIM (a:ℕ → ℚ) : Real :=
  Quotient.mk _ (if h : (a:Sequence).IsCauchy then CauchySequence.mk' h else (0:CauchySequence))

-- Equality of reals via equality of limits (from 5.3).
lemma Real.LIM_eq_LIM {a b:ℕ → ℚ} (ha: (a:Sequence).IsCauchy) (hb: (b:Sequence).IsCauchy) :
  LIM a = LIM b ↔ Sequence.Equiv a b := by
  sorry

-- Product of Cauchy sequences is Cauchy (from 5.3, proof omitted).
lemma Sequence.IsCauchy.mul {a b:ℕ → ℚ}  (ha: (a:Sequence).IsCauchy) (hb: (b:Sequence).IsCauchy) :
    (a * b:Sequence).IsCauchy := by
  sorry

-- Product respects sequence equivalence (left) (from 5.3, proof omitted).
lemma Sequence.mul_equiv_left {a a':ℕ → ℚ} (b:ℕ → ℚ) (hb : (b:Sequence).IsCauchy) (haa': Sequence.Equiv a a') :
  Sequence.Equiv (a * b) (a' * b) := by
  sorry

-- Product respects sequence equivalence (right) (from 5.3).
lemma Sequence.mul_equiv_right {b b':ℕ → ℚ} (a:ℕ → ℚ)  (ha : (a:Sequence).IsCauchy)  (hbb': Sequence.Equiv b b') :
  Sequence.Equiv (a * b) (a * b') := by
  simpa [mul_comm] using (Sequence.mul_equiv_left a ha hbb')

-- Product respects sequence equivalence (both sides) (from 5.3).
lemma Sequence.mul_equiv
  {a b a' b':ℕ → ℚ}
  (ha : (a:Sequence).IsCauchy)
  (hb' : (b':Sequence).IsCauchy)
  (haa': Sequence.Equiv a a')
  (hbb': Sequence.Equiv b b') : Sequence.Equiv (a * b) (a' * b') :=
    Sequence.equiv_trans (mul_equiv_right _ ha hbb') (mul_equiv_left _ hb' haa')

-- Product of reals
noncomputable instance Real.mul_inst : Mul Real where
  mul := fun x y ↦
    Quotient.liftOn₂ x y (fun a b ↦ LIM (a * b)) (by
      intro a b a' b' haa' hbb'
      -- Cauchy hypotheses for all pieces
      have haC : ((a:ℕ → ℚ):Sequence).IsCauchy := CauchySequence.cauchy_coe a
      have hbC : ((b:ℕ → ℚ):Sequence).IsCauchy := CauchySequence.cauchy_coe b
      have haC' : ((a':ℕ → ℚ):Sequence).IsCauchy := CauchySequence.cauchy_coe a'
      have hbC' : ((b':ℕ → ℚ):Sequence).IsCauchy := CauchySequence.cauchy_coe b'
      -- equivalence of products
      have hEquiv : Sequence.Equiv ((a:ℕ → ℚ) * (b:ℕ → ℚ)) ((a':ℕ → ℚ) * (b':ℕ → ℚ)) :=
        Sequence.mul_equiv haC hbC' haa' hbb'
      -- conclude equality of limits
      exact (Real.LIM_eq_LIM (Sequence.IsCauchy.mul haC hbC) (Sequence.IsCauchy.mul haC' hbC')).2 hEquiv
      )

-- Section 5.4: positivity on reals via Cauchy sequences
abbrev BoundedAwayPos (a:ℕ → ℚ) : Prop :=
  ∃ (c:ℚ), c > 0 ∧ ∀ n, a n ≥ c

abbrev BoundedAwayNeg (a:ℕ → ℚ) : Prop :=
  ∃ (c:ℚ), c > 0 ∧ ∀ n, a n ≤ -c

abbrev Real.IsPos (x:Real) : Prop :=
  ∃ a:ℕ → ℚ, BoundedAwayPos a ∧ (a:Sequence).IsCauchy ∧ x = LIM a

abbrev Real.IsNeg (x:Real) : Prop :=
  ∃ a:ℕ → ℚ, BoundedAwayNeg a ∧ (a:Sequence).IsCauchy ∧ x = LIM a

-- TARGET THEOREM
 theorem Real.pos_mul {x y:Real} (hx: x.IsPos) (hy: y.IsPos) : (x*y).IsPos := by
end Chapter5