import Mathlib
import Analysis.Section_5_4
import Analysis.Tags.Section_5_4

namespace Chapter5

-- <CONTEXT>
def Rat.Close (ε : ℚ) (x y : ℚ) := |x - y| ≤ ε

@[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
    grind

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 grind

@[simp]
lemma Sequence.eval_coe (n : ℕ) (a : ℕ → ℚ) : (a : Sequence) n = a n := by
  sorry

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

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

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

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

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

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

abbrev Rat.CloseSeq (ε : ℚ) (a b : Sequence) : Prop :=
  ∀ n, n ≥ a.n₀ → n ≥ b.n₀ → Rat.Close ε (a n) (b n)

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

abbrev Sequence.Equiv (a b : ℕ → ℚ) : Prop :=
  ∀ ε > (0 : ℚ), Rat.EventuallyClose ε (a : Sequence) (b : Sequence)

@[ext]
class CauchySequence extends Sequence where
  zero : n₀ = 0
  cauchy : toSequence.IsCauchy

abbrev CauchySequence.mk' {a : ℕ → ℚ} (ha : (a : Sequence).IsCauchy) : CauchySequence where
  n₀ := 0
  seq := (a : Sequence).seq
  vanish := by
    intro n hn
    simpa using ((a : Sequence).vanish n hn)
  zero := rfl
  cauchy := ha

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

@[simp]
lemma CauchySequence.coe_to_sequence (a : CauchySequence) :
    ((a : ℕ → ℚ) : Sequence) = a.toSequence := by
  sorry

instance CauchySequence.instSetoid : Setoid CauchySequence where
  r := fun a b ↦ Sequence.Equiv a b
  iseqv :=
    { refl := by sorry
      symm := by sorry
      trans := by sorry }

@[simp]
theorem 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))

lemma LIM_def {a : ℕ → ℚ} (ha : (a : Sequence).IsCauchy) :
    LIM a = Quotient.mk _ (CauchySequence.mk' ha) := by
  sorry

theorem Real.LIM_eq_LIM {a b : ℕ → ℚ} (ha : (a : Sequence).IsCauchy) (hb : (b : Sequence).IsCauchy) :
    LIM a = LIM b ↔ Sequence.Equiv a b := by
  sorry

theorem Sequence.IsCauchy.add {a b : ℕ → ℚ} (ha : (a : Sequence).IsCauchy) (hb : (b : Sequence).IsCauchy) :
    ((a + b : ℕ → ℚ) : Sequence).IsCauchy := by
  sorry

theorem Sequence.add_equiv {a b a' b' : ℕ → ℚ} (haa' : Sequence.Equiv a a')
  (hbb' : Sequence.Equiv b b') :
    Sequence.Equiv (a + b) (a' + b') := by
  sorry

noncomputable instance Real.add_inst : Add Real where
  add := fun x y ↦
    Quotient.liftOn₂ x y (fun a b ↦ LIM (a + b))
      (by
        sorry)

noncomputable instance Real.mul_inst : Mul Real where
  mul := fun x y ↦
    Quotient.liftOn₂ x y (fun a b ↦ LIM (a * b))
      (by
        sorry)

instance Real.instRatCast : RatCast Real where
  ratCast := fun q ↦ Quotient.mk _ (CauchySequence.mk' (a := fun _ ↦ q) (by
    simpa using (Sequence.IsCauchy.const q)))

instance Real.instOfNat {n : ℕ} : OfNat Real n where ofNat := ((n : ℚ) : Real)

instance Real.instNatCast : NatCast Real where natCast n := ((n : ℚ) : Real)

instance Real.instIntCast : IntCast Real where intCast n := ((n : ℚ) : Real)

noncomputable instance Real.instNeg : Neg Real where
  neg x := ((-1 : ℚ) : Real) * x

noncomputable instance Real.addGroup_inst : AddGroup Real :=
  AddGroup.ofLeftAxioms (by sorry) (by sorry) (by sorry)

abbrev BoundedAwayZero (a : ℕ → ℚ) : Prop := ∃ c : ℚ, c > 0 ∧ ∀ n, |a n| ≥ c

theorem Real.boundedAwayZero_of_nonzero {x : Real} (hx : x ≠ 0) :
    ∃ a : ℕ → ℚ, (a : Sequence).IsCauchy ∧ BoundedAwayZero a ∧ x = LIM a := by
  sorry

open Classical in
noncomputable instance Real.instInv : Inv Real where
  inv x := if h : x ≠ 0 then LIM (Real.boundedAwayZero_of_nonzero h).choose⁻¹ else 0

noncomputable instance Real.instHDiv : HDiv Real Real Real where hDiv x y := x * y⁻¹

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

open Classical in
noncomputable abbrev Real.abs (x : Real) : Real := if x.IsPos then x else (if x.IsNeg then -x else 0)

instance Real.instLT : LT Real where lt x y := (x - y).IsNeg

instance Real.instLE : LE Real where le x y := (x < y) ∨ (x = y)

noncomputable instance Real.instLinearOrder : LinearOrder Real where
  le_refl := by sorry
  le_trans := by sorry
  lt_iff_le_not_ge := by sorry
  le_antisymm := by sorry
  le_total := by sorry
  toDecidableLE := Classical.decRel _

theorem Real.upperBound_def (E: Set Real) (M: Real) : M ∈ upperBounds E ↔ ∀ x ∈ E, x ≤ M := by
  sorry

theorem Real.lowerBound_def (E: Set Real) (M: Real) : M ∈ lowerBounds E ↔ ∀ x ∈ E, x ≥ M := by
  sorry

theorem Real.Icc_def (x y:Real) : .Icc x y = { z | x ≤ z ∧ z ≤ y } := by
  sorry

theorem Real.mem_Icc (x y z:Real) : z ∈ Set.Icc x y ↔ x ≤ z ∧ z ≤ y := by
  sorry

theorem Real.Ioi_def (x:Real) : .Ioi x = { z | z > x } := by
  sorry

theorem Real.upperBound_upper {M M': Real} (h: M ≤ M') {E: Set Real} (hb: M ∈ upperBounds E) :
    M' ∈ upperBounds E := by
  sorry

theorem Real.isLUB_def (E: Set Real) (M: Real) :
    IsLUB E M ↔ M ∈ upperBounds E ∧ ∀ M' ∈ upperBounds E, M' ≥ M := by
  sorry

theorem Real.isGLB_def (E: Set Real) (M: Real) :
    IsGLB E M ↔ M ∈ lowerBounds E ∧ ∀ M' ∈ lowerBounds E, M' ≤ M := by
  sorry

theorem Real.LUB_unique {E: Set Real} {M M': Real} (h1: IsLUB E M) (h2: IsLUB E M') : M = M' := by
  sorry

theorem Real.bddAbove_def (E: Set Real) : BddAbove E ↔ ∃ M, M ∈ upperBounds E := by
  sorry

theorem Real.bddBelow_def (E: Set Real) : BddBelow E ↔ ∃ M, M ∈ lowerBounds E := by
  sorry

theorem Sequence.IsCauchy.abs {a:ℕ → ℚ} (ha: (a:Sequence).IsCauchy):
  ((|a| : ℕ → ℚ) : Sequence).IsCauchy := by
  sorry

theorem Real.LIM.abs_eq {a b:ℕ → ℚ} (ha: (a: Sequence).IsCauchy)
    (hb: (b: Sequence).IsCauchy) (h: LIM a = LIM b): LIM |a| = LIM |b| := by
  sorry

theorem Real.LIM.abs_eq_pos {a: ℕ → ℚ} (h: LIM a > 0) (ha: (a:Sequence).IsCauchy):
    LIM a = LIM |a| := by
  sorry

theorem Real.LIM_abs {a:ℕ → ℚ} (ha: (a:Sequence).IsCauchy): |LIM a| = LIM |a| := by
  sorry

theorem Real.LIM_of_le' {x:Real} {a:ℕ → ℚ} (hcauchy: (a:Sequence).IsCauchy)
    (h: ∃ N, ∀ n ≥ N, a n ≤ x) : LIM a ≤ x := by
  sorry
-- </CONTEXT>
-- <TARGET_THEOREM>
theorem Real.LIM_of_Cauchy {q:ℕ → ℚ} (hq: ∀ M, ∀ n ≥ M, ∀ n' ≥ M, |q n - q n'| ≤ 1 / (M+1)) :
    (q:Sequence).IsCauchy ∧ ∀ M, |q M - LIM q| ≤ 1 / (M+1) := by
-- </TARGET_THEOREM>

end Chapter5