import Mathlib.Tactic

/-!
Self-contained slice for Chapter 5.3 target theorem Real.LIM.harmonic.
We include only the minimal dependencies from Sections 5.1, 5.2, 5.3, and 4.3
needed for parsing and typechecking the target statement.
-/

/-- From Section 4.3: definition of ε-closeness on ℚ. -/
def Rat.Close (ε : ℚ) (x y : ℚ) := |x - y| ≤ ε

namespace Chapter5

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

/-- Sequences can be thought of as functions from ℤ to ℚ. -/
instance Sequence.instCoeFun : CoeFun Sequence (fun _ ↦ ℤ → ℚ) where
  coe := fun a ↦ a.seq

/--
Functions from ℕ to ℚ can be thought of as sequences starting from 0; `ofNatFun` performs this conversion.

The `coe` attribute allows the delaborator to print `Sequence.ofNatFun f` as `↑f`, which is more concise; you may safely remove this if you prefer the more explicit notation.
-/
@[coe]
def Sequence.ofNatFun (a : ℕ → ℚ) : Sequence where
    n₀ := 0
    seq n := if n ≥ 0 then a n.toNat else 0
    vanish := by
      grind

/-- If `a : ℕ → ℚ` is used in a context where a `Sequence` is expected, automatically coerce. -/
instance : Coe (ℕ → ℚ) Sequence where
  coe := Sequence.ofNatFun

/-- A convenient constructor for sequences starting at an arbitrary index. -/
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 grind

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

@[simp]
lemma Sequence.n0_coe (a: ℕ → ℚ) : (a:Sequence).n₀ = 0 := rfl

@[aesop safe apply]
lemma Sequence.vanish_coe (a:ℕ→ℚ) : ∀ n, n < (0:ℤ) → (a:Sequence).seq n = 0 := by
  intro n h; simp [Sequence.ofNatFun, h]

end Chapter5

/-- From Section 5.2: auxiliary notions for eventual closeness of sequences. -/
abbrev Rat.CloseSeq (ε: ℚ) (a b: Chapter5.Sequence) : Prop :=
  ∀ n, n ≥ a.n₀ → n ≥ b.n₀ → ε.Close (a n) (b n)

abbrev Rat.EventuallyClose (ε: ℚ) (a b: Chapter5.Sequence) : Prop :=
  ∃ N, ε.CloseSeq (a.from N) (b.from N)

/-- A slight generalization of Definition 5.1.3: ε-steadiness for sequences. -/
abbrev Rat.Steady (ε: ℚ) (a: Chapter5.Sequence) : Prop :=
  ∀ n ≥ a.n₀, ∀ m ≥ a.n₀, ε.Close (a n) (a m)

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

namespace Chapter5

/-- Section 5.1: definition of Cauchy sequences (for a sequence starting at arbitrary n₀). -/
abbrev Sequence.IsCauchy (a:Sequence) : Prop := ∀ ε > (0:ℚ), ε.EventuallySteady a

/-- Section 5.2: definition of equivalent sequences (eventual closeness). -/
abbrev Sequence.Equiv (a b: ℕ → ℚ) : Prop :=
  ∀ ε > (0:ℚ), ε.EventuallyClose (a:Sequence) (b:Sequence)

/-- A class of Cauchy sequences that start at zero. -/
@[ext]
class CauchySequence extends Sequence where
  zero : n₀ = 0
  cauchy : toSequence.IsCauchy

/-- A sequence starting at zero that is Cauchy can be viewed as a Cauchy sequence. -/
abbrev CauchySequence.mk' {a:ℕ → ℚ} (ha: (a:Sequence).IsCauchy) : CauchySequence where
  n₀ := 0
  seq := (a:Sequence).seq
  vanish := by aesop
  zero := rfl
  cauchy := ha

/-- Coerce a Cauchy sequence to its underlying function ℕ → ℚ. -/
instance CauchySequence.instCoeFun : CoeFun CauchySequence (fun _ ↦ ℕ → ℚ) where
  coe a n := a.toSequence (n:ℤ)

/-- Proposition: every constant sequence is Cauchy. -/
lemma Sequence.IsCauchy.const (a:ℚ) : ((fun _ : ℕ ↦ a):Sequence).IsCauchy := by
  -- exact proof omitted in this slice
  sorry

/-- The equivalence relation identifying equivalent Cauchy sequences. -/
instance CauchySequence.instSetoid : Setoid CauchySequence where
  r := fun a b ↦ Sequence.Equiv a b
  iseqv :=
    { refl := by
        -- proof omitted in this slice
        sorry
      symm := by
        -- proof omitted in this slice
        sorry
      trans := by
        -- proof omitted in this slice
        sorry }

/-- Zero Cauchy sequence (the constant zero sequence). -/
instance CauchySequence.instZero : Zero CauchySequence where
  zero := CauchySequence.mk' (a := fun _ : ℕ ↦ 0) (Sequence.IsCauchy.const (0:ℚ))

/-- The real numbers are (quotients of) Cauchy sequences modulo equivalence. -/
abbrev Real := Quotient CauchySequence.instSetoid

open Classical in
/--
  It is convenient in Lean to assign the "dummy" value of 0 to `LIM a` when `a` is not Cauchy.
  This requires Classical logic, because the property of being Cauchy is not computable or
  decidable.
-/
noncomputable abbrev LIM (a:ℕ → ℚ) : Real :=
  Quotient.mk _ (if h : (a:Sequence).IsCauchy then CauchySequence.mk' h else (0:CauchySequence))

/-- Embed the rationals into the reals via constant Cauchy sequences. -/
instance Real.instRatCast : RatCast Real where
  ratCast := fun q ↦
    Quotient.mk _ (CauchySequence.mk' (a := fun _ ↦ q) (Sequence.IsCauchy.const q))

/-- Provide numerals (in particular 0) for Real via casting from ℚ. -/
instance Real.instOfNat {n:ℕ} : OfNat Real n where
  ofNat := ((n:ℚ):Real)

/-- Exercise 5.3.5 -/
 theorem Real.LIM.harmonic : LIM (fun n ↦ 1/((n:ℚ)+1)) = 0 := by
end Chapter5