import Mathlib.Tactic

-- From Section 4.3
-- This definition needs to be made outside of the Section 4.3 namespace for technical reasons.
def Rat.Close (ε : ℚ) (x y:ℚ) := |x-y| ≤ ε

namespace Chapter5

@[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]
-- 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`.
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 `a` to `Sequence.ofNatFun a`.
instance : Coe (ℕ → ℚ) Sequence where
  coe := Sequence.ofNatFun

@[simp]
lemma Sequence.eval_coe_at_int (n:ℤ) (a: ℕ → ℚ) : (a:Sequence) n = if n ≥ 0 then a n.toNat else 0 := by norm_cast

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

-- a.from n₁ starts `a:Sequence` from `n₁`.  It is intended for use when `n₁ ≥ n₀`, but returns
-- the "junk" value of the original sequence `a` otherwise.
abbrev Sequence.from (a:Sequence) (n₁:ℤ) : Sequence :=
  mk' (max a.n₀ n₁) (fun n ↦ a (n:ℤ))

end Chapter5

-- A slight generalization of Definition 5.1.3 - definition of ε-steadiness for a sequence with an arbitrary starting point n₀
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

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

end Chapter5

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)

namespace Chapter5

-- Definition 5.2.6 (Equivalent sequences)
abbrev Sequence.Equiv (a b: ℕ → ℚ) : Prop :=
  ∀ ε > (0:ℚ), ε.EventuallyClose (a:Sequence) (b:Sequence)

@[ext]
-- A class of Cauchy sequences that start at zero
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

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

-- Proposition 5.3.3 / Exercise 5.3.1
instance CauchySequence.instSetoid : Setoid CauchySequence where
  r := fun a b ↦ Sequence.Equiv a b
  iseqv := {
     refl := sorry
     symm := sorry
     trans := sorry
  }

-- The type of real numbers as a quotient of Cauchy sequences
abbrev Real := Quotient CauchySequence.instSetoid

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

-- Canonical `ℚ`-algebra structure on reals via constant sequences
instance Real.instRatCast : RatCast Real where
  ratCast := fun q ↦
    Quotient.mk _ (CauchySequence.mk' (a := fun _ ↦ q) (Sequence.IsCauchy.const q))

-- Exercise 5.3.3
@[simp]
theorem Real.ratCast_inj (q r:ℚ) : (q:Real) = (r:Real) ↔ q = r := by
end Chapter5