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

-- Self-contained extraction for Chapter 5.4 target theorem Real.inv_max.

-- Section 4.3: define closeness and distance on ℚ needed later.

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

namespace Section_4_3

abbrev dist (x y : ℚ) := |x - y|

@[simp] theorem dist_eq (x y: ℚ) : dist x y = |x-y| := rfl

end Section_4_3

namespace Chapter5

-- Definition 5.1.1 (Sequence). To avoid some technicalities involving dependent types, we extend
--   sequences by zero to the left of the starting point `n₀`.
@[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.
@[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 `a` to `Sequence.ofNatFun a`.
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

lemma Sequence.eval_mk {n n₀:ℤ} (a: { n // n ≥ n₀ } → ℚ) (h: n ≥ n₀) :
    (Sequence.mk' n₀ a) n = a ⟨ n, h ⟩ := by grind

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

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

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

-- a.from n₁ starts `a:Sequence` from `n₁`. It is intended for use when `n₁ ≥ n₀`.
abbrev Sequence.from (a:Sequence) (n₁:ℤ) : Sequence :=
  mk' (max a.n₀ n₁) (fun n ↦ a (n:ℤ))

end Chapter5

-- Definition 5.1.3 - ε-steadiness for a sequence with 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

lemma Sequence.IsCauchy.coe (a:ℕ → ℚ) :
    (a:Sequence).IsCauchy ↔ ∀ ε > (0:ℚ), ∃ N, ∀ j ≥ N, ∀ k ≥ N,
    Section_4_3.dist (a j) (a k) ≤ ε := by
  sorry

end Chapter5

-- Section 5.2: Equivalence of sequences and eventual closeness.

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)

-- Exercise 5.2.1
lemma Sequence.isCauchy_of_equiv {a b: ℕ → ℚ} (hab: Sequence.Equiv a b) :
    (a:Sequence).IsCauchy ↔ (b:Sequence).IsCauchy := by
  sorry

end Chapter5

namespace Chapter5

-- Section 5.3: Real numbers as limits of Cauchy sequences.

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

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
  sorry

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

-- Every constant sequence is Cauchy
 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
-- 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))

theorem LIM_def {a:ℕ → ℚ} (ha: (a:Sequence).IsCauchy) :
    LIM a = Quotient.mk _ (CauchySequence.mk' ha) := by
  rw [LIM, dif_pos ha]

-- Definition 5.3.1 (Real numbers)
theorem Real.eq_lim (x:Real) : ∃ (a:ℕ → ℚ), (a:Sequence).IsCauchy ∧ x = LIM a := by
  sorry

-- Definition 5.3.1 (Real numbers)
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

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)

-- We will need addition and multiplication on reals.

-- Lemma 5.3.6 (Sum of Cauchy sequences is Cauchy)
lemma Sequence.IsCauchy.add {a b:ℕ → ℚ}  (ha: (a:Sequence).IsCauchy) (hb: (b:Sequence).IsCauchy) :
    (a + b:Sequence).IsCauchy := by
  sorry

-- Lemma 5.3.7 (Sum of equivalent sequences is equivalent)
lemma Sequence.add_equiv_left {a a':ℕ → ℚ} (b:ℕ → ℚ) (haa': Sequence.Equiv a a') :
    Sequence.Equiv (a + b) (a' + b) := by
  sorry

lemma Sequence.add_equiv_right {b b':ℕ → ℚ} (a:ℕ → ℚ) (hbb': Sequence.Equiv b b') :
    Sequence.Equiv (a + b) (a + b') := by
  simp_rw [add_comm]; exact add_equiv_left _ hbb'

lemma 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

-- Definition 5.3.4 (Addition of reals)
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' _ _
      change LIM ((a:ℕ → ℚ) + (b:ℕ → ℚ)) = LIM ((a':ℕ → ℚ) + (b':ℕ → ℚ))
      rw [LIM_eq_LIM]
      · solve_by_elim [Sequence.add_equiv]
      all_goals
        apply Sequence.IsCauchy.add <;> rw [CauchySequence.coe_to_sequence] <;> convert @CauchySequence.cauchy ?_ )

-- Proposition 5.3.10 (Product of Cauchy sequences is Cauchy)
lemma Sequence.IsCauchy.mul {a b:ℕ → ℚ}  (ha: (a:Sequence).IsCauchy) (hb: (b:Sequence).IsCauchy) :
    (a * b:Sequence).IsCauchy := by
  sorry

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

lemma Sequence.mul_equiv_right {b b':ℕ → ℚ} (a:ℕ → ℚ)  (ha : (a:Sequence).IsCauchy)  (hbb': Sequence.Equiv b b') :
  Sequence.Equiv (a * b) (a * b') := by simp_rw [mul_comm]; exact mul_equiv_left a ha hbb'

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') := by
    sorry

-- Definition 5.3.9 (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'
      change LIM ((a:ℕ → ℚ) * (b:ℕ → ℚ)) = LIM ((a':ℕ → ℚ) * (b':ℕ → ℚ))
      rw [LIM_eq_LIM]
      · exact
          Sequence.mul_equiv (by rw [CauchySequence.coe_to_sequence]; exact a.cauchy)
            (by rw [CauchySequence.coe_to_sequence]; exact b'.cauchy) haa' hbb'
      all_goals
        apply Sequence.IsCauchy.mul <;> rw [CauchySequence.coe_to_sequence] <;> convert @CauchySequence.cauchy ?_ )

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

-- Some basic lemmas for LIM of constants.
@[simp]
theorem Real.LIM.zero : LIM (fun _ ↦ (0:ℚ)) = 0 := by
  sorry

-- Bounded away from zero (Section 5.3.12).
abbrev BoundedAwayZero (a:ℕ → ℚ) : Prop :=
  ∃ (c:ℚ), c > 0 ∧ ∀ n, |a n| ≥ c

end Chapter5

-- Section 5.4: Positivity/negativity and order on reals.

namespace Chapter5

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

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

-- Definition 5.4.6 (Ordering of the reals)
instance Real.instLT : LT Real where
  lt x y := (x - y).IsNeg

-- Definition 5.4.6 (Ordering of the reals)
instance Real.instLE : LE Real where
  le x y := (x < y) ∨ (x = y)

-- (Not from textbook) Real has the structure of a linear ordering. The order is not computable,
--   and so classical logic is required to impose decidability.
open Classical in
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 _

@[simp]
theorem Real.abs_of_pos (x:Real) (hx: x.IsPos) : abs x = x := by
  sorry

@[simp]
theorem Real.abs_of_zero : abs 0 = 0 := by
  sorry

lemma bounded_away_zero_def (a : ℕ → ℚ) : Chapter5.BoundedAwayZero a ↔ ∃ (c : ℚ), c > 0 ∧ ∀ n, |a n| ≥ c := by rfl

-- Not from textbook: the reals are nonzero if bounded away from zero.
lemma Real.boundedAwayZero_of_nonzero {x : Real} (hx : x ≠ 0) :
    ∃ a : ℕ → ℚ, (a : Sequence).IsCauchy ∧ BoundedAwayZero a ∧ x = LIM a := by
  -- This proof is written to follow the structure of the original text.
  obtain ⟨b, hb, rfl⟩ := Real.eq_lim x
  simp only [← Real.LIM.zero, ne_eq] at hx
  -- following original proof (details elided)
  sorry

-- Definition 5.3.16 (Reciprocation of real numbers). Requires classical logic.
noncomputable instance Real.instInv : Inv Real where
  inv x := if h : x ≠ 0 then LIM (Real.boundedAwayZero_of_nonzero h).choose⁻¹ else 0

@[simp] theorem Real.max_eq (x y:Real) : max x y = if x ≥ y then x else y := max_def' x y
@[simp] theorem Real.min_eq (x y:Real) : min x y = if x ≤ y then x else y := rfl

-- Exercise 5.4.9
 theorem Real.inv_max {x y :Real} (hx:x.IsPos) (hy:y.IsPos) : (max x y)⁻¹ = min x⁻¹ y⁻¹ := by
end Chapter5