import Mathlib.Tactic
import Mathlib.Algebra.Group.MinimalAxioms

open Lean Elab Tactic in
elab "grind" : tactic => do
  evalTactic (← `(tactic| first | intros | skip))
  evalTactic (← `(tactic| simp [*] at *))
  pure ()

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]
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

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

lemma Sequence.from_eval (a : Sequence) {n₁ n : ℤ} (hn : n ≥ n₁) :
    (a.from n₁) n = a n := by
  unfold Sequence.from
  by_cases hmax : n ≥ max a.n₀ n₁
  · simp [Sequence.mk', hmax]
  · have hlt : n < max a.n₀ n₁ := lt_of_not_ge hmax
    have hlt' : n < a.n₀ :=
      (lt_max_iff.mp hlt).resolve_right (not_lt.mpr hn)
    have hv : a.seq n = 0 := a.vanish n hlt'
    simp [Sequence.mk', hmax, hv]

end Chapter5

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

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

abbrev Rat.EventuallyClose (ε : ℚ) (a b : Chapter5.Sequence) : Prop :=
  ∃ N, ∀ n, n ≥ (a.from N).n₀ → n ≥ (b.from N).n₀ → ε.Close (a.from N n) (b.from N n)

namespace Chapter5

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

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

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

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

abbrev CauchySequence.mk' {a : ℕ → ℚ} (ha : (a : Sequence).IsCauchy) : CauchySequence where
  n₀ := 0
  seq := (a : Sequence).seq
  vanish := by
    intro n hn
    change (if n ≥ 0 then a n.toNat else 0) = 0
    have : ¬ n ≥ 0 := by exact not_le.mpr hn
    simp [this]
  zero := rfl
  cauchy := ha

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

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))

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

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' _ _
      sorry)

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

instance Real.instOfNat {n : ℕ} : OfNat Real n where
  ofNat := ((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 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

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)
theorem Real.trichotomous' (x y : Real) : x > y ∨ x < y ∨ x = y := by
end Chapter5