/-- Shows the correctness of `compExactValue` in case the continued fraction
`GeneralizedContinuedFraction.of v` did not terminate at position `n`. That is, we obtain the
value `v` if we pass the two successive (auxiliary) continuants at positions `n` and `n + 1` as well
as the fractional part at `IntFractPair.stream n` to `compExactValue`.

The correctness might be seen more readily if one uses `convergents'` to evaluate the continued
fraction. Here is an example to illustrate the idea:

Let `(v : ℚ) := 3.4`. We have
- `GeneralizedContinuedFraction.IntFractPair.stream v 0 = some ⟨3, 0.4⟩`, and
- `GeneralizedContinuedFraction.IntFractPair.stream v 1 = some ⟨2, 0.5⟩`.
Now `(GeneralizedContinuedFraction.of v).convergents' 1 = 3 + 1/2`, and our fractional term at
position `2` is `0.5`. We hence have `v = 3 + 1/(2 + 0.5) = 3 + 1/2.5 = 3.4`. This computation
corresponds exactly to the one using the recurrence equation in `compExactValue`.
-/
theorem compExactValue_correctness_of_stream_eq_some :
    ∀ {ifp_n : IntFractPair K}, IntFractPair.stream v n = some ifp_n →
      v = compExactValue ((of v).continuantsAux n) ((of v).continuantsAux <| n + 1) ifp_n.fr := by
  let g := of v
  induction' n with n IH
  · intro ifp_zero stream_zero_eq
    -- Nat.zero
    have : IntFractPair.of v = ifp_zero := by
      have : IntFractPair.stream v 0 = some (IntFractPair.of v) := rfl
      simpa only [Nat.zero_eq, this, Option.some.injEq] using stream_zero_eq
    cases this
    cases' Decidable.em (Int.fract v = 0) with fract_eq_zero fract_ne_zero
    -- Int.fract v = 0; we must then have `v = ⌊v⌋`
    · suffices v = ⌊v⌋ by
        -- Porting note: was `simpa [continuantsAux, fract_eq_zero, compExactValue]`
        field_simp [nextContinuants, nextNumerator, nextDenominator, compExactValue]
        have : (IntFractPair.of v).fr = Int.fract v := rfl
        rwa [this, if_pos fract_eq_zero]
      calc
        v = Int.fract v + ⌊v⌋ := by rw [Int.fract_add_floor]
        _ = ⌊v⌋ := by simp [fract_eq_zero]
    -- Int.fract v ≠ 0; the claim then easily follows by unfolding a single computation step
    · field_simp [continuantsAux, nextContinuants, nextNumerator, nextDenominator,
        of_h_eq_floor, compExactValue]
      -- Porting note: this and the if_neg rewrite are needed
      have : (IntFractPair.of v).fr = Int.fract v := rfl
      rw [this, if_neg fract_ne_zero, Int.floor_add_fract]
  · intro ifp_succ_n succ_nth_stream_eq
    -- Nat.succ
    obtain ⟨ifp_n, nth_stream_eq, nth_fract_ne_zero, -⟩ :
      ∃ ifp_n, IntFractPair.stream v n = some ifp_n ∧
        ifp_n.fr ≠ 0 ∧ IntFractPair.of ifp_n.fr⁻¹ = ifp_succ_n
    exact IntFractPair.succ_nth_stream_eq_some_iff.1 succ_nth_stream_eq
    -- introduce some notation
    let conts := g.continuantsAux (n + 2)
    set pconts := g.continuantsAux (n + 1) with pconts_eq
    set ppconts := g.continuantsAux n with ppconts_eq
    cases' Decidable.em (ifp_succ_n.fr = 0) with ifp_succ_n_fr_eq_zero ifp_succ_n_fr_ne_zero
    -- ifp_succ_n.fr = 0
    · suffices v = conts.a / conts.b by simpa [compExactValue, ifp_succ_n_fr_eq_zero]
      -- use the IH and the fact that ifp_n.fr⁻¹ = ⌊ifp_n.fr⁻¹⌋ to prove this case
      obtain ⟨ifp_n', nth_stream_eq', ifp_n_fract_inv_eq_floor⟩ :
        ∃ ifp_n, IntFractPair.stream v n = some ifp_n ∧ ifp_n.fr⁻¹ = ⌊ifp_n.fr⁻¹⌋
      exact IntFractPair.exists_succ_nth_stream_of_fr_zero succ_nth_stream_eq ifp_succ_n_fr_eq_zero
      have : ifp_n' = ifp_n := by injection Eq.trans nth_stream_eq'.symm nth_stream_eq
      cases this
      have s_nth_eq : g.s.get? n = some ⟨1, ⌊ifp_n.fr⁻¹⌋⟩ :=
        get?_of_eq_some_of_get?_intFractPair_stream_fr_ne_zero nth_stream_eq nth_fract_ne_zero
      rw [← ifp_n_fract_inv_eq_floor] at s_nth_eq
      suffices v = compExactValue ppconts pconts ifp_n.fr by
        simpa [continuantsAux, s_nth_eq, compExactValue, nth_fract_ne_zero] using this
      exact IH nth_stream_eq
    -- ifp_succ_n.fr ≠ 0
    · -- use the IH to show that the following equality suffices
      suffices
        compExactValue ppconts pconts ifp_n.fr = compExactValue pconts conts ifp_succ_n.fr by
        have : v = compExactValue ppconts pconts ifp_n.fr := IH nth_stream_eq
        conv_lhs => rw [this]
        assumption
      -- get the correspondence between ifp_n and ifp_succ_n
      obtain ⟨ifp_n', nth_stream_eq', ifp_n_fract_ne_zero, ⟨refl⟩⟩ :
        ∃ ifp_n, IntFractPair.stream v n = some ifp_n ∧
          ifp_n.fr ≠ 0 ∧ IntFractPair.of ifp_n.fr⁻¹ = ifp_succ_n
      exact IntFractPair.succ_nth_stream_eq_some_iff.1 succ_nth_stream_eq
      have : ifp_n' = ifp_n := by injection Eq.trans nth_stream_eq'.symm nth_stream_eq
      cases this
      -- get the correspondence between ifp_n and g.s.nth n
      have s_nth_eq : g.s.get? n = some ⟨1, (⌊ifp_n.fr⁻¹⌋ : K)⟩ :=
        get?_of_eq_some_of_get?_intFractPair_stream_fr_ne_zero nth_stream_eq ifp_n_fract_ne_zero
      -- the claim now follows by unfolding the definitions and tedious calculations
      -- some shorthand notation
      let ppA := ppconts.a
      let ppB := ppconts.b
      let pA := pconts.a
      let pB := pconts.b
      have : compExactValue ppconts pconts ifp_n.fr =
          (ppA + ifp_n.fr⁻¹ * pA) / (ppB + ifp_n.fr⁻¹ * pB) := by
        -- unfold compExactValue and the convergent computation once
        field_simp [ifp_n_fract_ne_zero, compExactValue, nextContinuants, nextNumerator,
          nextDenominator]
        ac_rfl
      rw [this]
      -- two calculations needed to show the claim
      have tmp_calc :=
        compExactValue_correctness_of_stream_eq_some_aux_comp pA ppA ifp_succ_n_fr_ne_zero
      have tmp_calc' :=
        compExactValue_correctness_of_stream_eq_some_aux_comp pB ppB ifp_succ_n_fr_ne_zero
      let f := Int.fract (1 / ifp_n.fr)
      have f_ne_zero : f ≠ 0 := by simpa using ifp_succ_n_fr_ne_zero
      rw [inv_eq_one_div] at tmp_calc tmp_calc'
      -- Porting note: the `tmp_calc`s need to be massaged, and some processing after `ac_rfl` done,
      -- because `field_simp` is not as powerful
      have hA : (↑⌊1 / ifp_n.fr⌋ * pA + ppA) + pA * f = pA * (1 / ifp_n.fr) + ppA := by
        have := congrFun (congrArg HMul.hMul tmp_calc) f
        rwa [right_distrib, div_mul_cancel (h := f_ne_zero),
          div_mul_cancel (h := f_ne_zero)] at this
      have hB : (↑⌊1 / ifp_n.fr⌋ * pB + ppB) + pB * f = pB * (1 / ifp_n.fr) + ppB := by
        have := congrFun (congrArg HMul.hMul tmp_calc') f
        rwa [right_distrib, div_mul_cancel (h := f_ne_zero),
          div_mul_cancel (h := f_ne_zero)] at this
      -- now unfold the recurrence one step and simplify both sides to arrive at the conclusion
      field_simp [compExactValue, continuantsAux_recurrence s_nth_eq ppconts_eq pconts_eq,
        nextContinuants, nextNumerator, nextDenominator]
      

Proof state:
case succ.intro.intro.intro.inr.intro.intro.intro.refl.refl
K : Type u_1
inst✝¹ : LinearOrderedField K
v : K
n✝ : ℕ
inst✝ : FloorRing K
g : GeneralizedContinuedFraction K := of v
n : ℕ
ifp_n : IntFractPair K
nth_stream_eq : IntFractPair.stream v n = some ifp_n
nth_fract_ne_zero : ifp_n.fr ≠ 0
conts : Pair K := continuantsAux g (n + 2)
pconts : Pair K := continuantsAux g (n + 1)
pconts_eq : pconts = continuantsAux g (n + 1)
ppconts : Pair K := continuantsAux g n
IH : ∀ {ifp_n : IntFractPair K}, IntFractPair.stream v n = some ifp_n → v = compExactValue ppconts pconts ifp_n.fr
ppconts_eq : ppconts = continuantsAux g n
nth_stream_eq' : IntFractPair.stream v n = some ifp_n
ifp_n_fract_ne_zero : ifp_n.fr ≠ 0
succ_nth_stream_eq : IntFractPair.stream v (Nat.succ n) = some (IntFractPair.of ifp_n.fr⁻¹)
ifp_succ_n_fr_ne_zero : ¬(IntFractPair.of ifp_n.fr⁻¹).fr = 0
s_nth_eq : Stream'.Seq.get? g.s n = some { a := 1, b := ↑⌊ifp_n.fr⁻¹⌋ }
ppA : K := ppconts.a
ppB : K := ppconts.b
pA : K := pconts.a
pB : K := pconts.b
this : compExactValue ppconts pconts ifp_n.fr = (ppA + ifp_n.fr⁻¹ * pA) / (ppB + ifp_n.fr⁻¹ * pB)
tmp_calc :
  (↑⌊1 / ifp_n.fr⌋ * pA + ppA) / Int.fract (1 / ifp_n.fr) + pA = (pA * (1 / ifp_n.fr) + ppA) / Int.fract (1 / ifp_n.fr)
tmp_calc' :
  (↑⌊1 / ifp_n.fr⌋ * pB + ppB) / Int.fract (1 / ifp_n.fr) + pB = (pB * (1 / ifp_n.fr) + ppB) / Int.fract (1 / ifp_n.fr)
f : K := Int.fract (1 / ifp_n.fr)
f_ne_zero : f ≠ 0
hA : ↑⌊1 / ifp_n.fr⌋ * pA + ppA + pA * f = pA * (1 / ifp_n.fr) + ppA
hB : ↑⌊1 / ifp_n.fr⌋ * pB + ppB + pB * f = pB * (1 / ifp_n.fr) + ppB
⊢ ((continuantsAux (of v) n).a * ifp_n.fr + (continuantsAux (of v) (n + 1)).a) /
      ((continuantsAux (of v) n).b * ifp_n.fr + (continuantsAux (of v) (n + 1)).b) =
    if (IntFractPair.of (1 / ifp_n.fr)).fr = 0 then
      (↑⌊1 / ifp_n.fr⌋ * (continuantsAux (of v) (n + 1)).a + (continuantsAux (of v) n).a) /
        (↑⌊1 / ifp_n.fr⌋ * (continuantsAux (of v) (n + 1)).b + (continuantsAux (of v) n).b)
    else
      (↑⌊1 / ifp_n.fr⌋ * (continuantsAux (of v) (n + 1)).a + (continuantsAux (of v) n).a +
          (continuantsAux (of v) (n + 1)).a * (IntFractPair.of (1 / ifp_n.fr)).fr) /
        (↑⌊1 / ifp_n.fr⌋ * (continuantsAux (of v) (n + 1)).b + (continuantsAux (of v) n).b +
          (continuantsAux (of v) (n + 1)).b * (IntFractPair.of (1 / ifp_n.fr)).fr)

Next tactic:
have hfr : (IntFractPair.of (1 / ifp_n.fr)).fr = f := rfl