l3lab/ntp-mathlib huggingface dataset:
/-- Shows that the recurrence relation (`convergents`) and direct evaluation (`convergents'`) of the
gcf coincide at position `n` if the sequence of fractions contains strictly positive values only.
Requiring positivity of all values is just one possible condition to obtain this result.
For example, the dual - sequences with strictly negative values only - would also work.

In practice, one most commonly deals with (regular) continued fractions, which satisfy the
positivity criterion required here. The analogous result for them
(see `ContinuedFractions.convergents_eq_convergents`) hence follows directly from this theorem.
-/
theorem convergents_eq_convergents' [LinearOrderedField K]
    (s_pos : ∀ {gp : Pair K} {m : ℕ}, m < n → g.s.get? m = some gp → 0 < gp.a ∧ 0 < gp.b) :
    g.convergents n = g.convergents' n := by
  induction' n with n IH generalizing g
  case zero => simp
  case succ =>
    let g' := squashGCF g n
    -- first replace the rhs with the squashed computation
    suffices g.convergents (n + 1) = g'.convergents' n by
      rwa [succ_nth_convergent'_eq_squashGCF_nth_convergent']
    cases' Decidable.em (TerminatedAt g n) with terminated_at_n not_terminated_at_n
    · have g'_eq_g : g' = g := squashGCF_eq_self_of_terminated terminated_at_n
      rw [convergents_stable_of_terminated n.le_succ terminated_at_n, g'_eq_g, IH _]
      intro _ _ m_lt_n s_mth_eq
      exact s_pos (Nat.lt.step m_lt_n) s_mth_eq
    · suffices g.convergents (n + 1) = g'.convergents n by
        -- invoke the IH for the squashed gcf
        

Proof state:
K : Type u_1
s : Stream'.Seq (Pair K)
inst✝ : LinearOrderedField K
n : ℕ
IH :
  ∀ {g : GeneralizedContinuedFraction K},
    (∀ {gp : Pair K} {m : ℕ}, m < n → Stream'.Seq.get? g.s m = some gp → 0 < gp.a ∧ 0 < gp.b) →
      convergents g n = convergents' g n
g : GeneralizedContinuedFraction K
s_pos : ∀ {gp : Pair K} {m : ℕ}, m < Nat.succ n → Stream'.Seq.get? g.s m = some gp → 0 < gp.a ∧ 0 < gp.b
g' : GeneralizedContinuedFraction K := squashGCF g n
not_terminated_at_n : ¬TerminatedAt g n
this : convergents g (n + 1) = convergents g' n
⊢ convergents g (n + 1) = convergents' g' n

Next tactic:
rwa [← IH]
/-- Shows that the recurrence relation (`convergents`) and direct evaluation (`convergents'`) of the
gcf coincide at position `n` if the sequence of fractions contains strictly positive values only.
Requiring positivity of all values is just one possible condition to obtain this result.
For example, the dual - sequences with strictly negative values only - would also work.

In practice, one most commonly deals with (regular) continued fractions, which satisfy the
positivity criterion required here. The analogous result for them
(see `ContinuedFractions.convergents_eq_convergents`) hence follows directly from this theorem.
-/
theorem convergents_eq_convergents' [LinearOrderedField K]
    (s_pos : ∀ {gp : Pair K} {m : ℕ}, m < n → g.s.get? m = some gp → 0 < gp.a ∧ 0 < gp.b) :
    g.convergents n = g.convergents' n := by
  induction' n with n IH generalizing g
  case zero => simp
  case succ =>
    let g' := squashGCF g n
    -- first replace the rhs with the squashed computation
    suffices g.convergents (n + 1) = g'.convergents' n by
      rwa [succ_nth_convergent'_eq_squashGCF_nth_convergent']
    cases' Decidable.em (TerminatedAt g n) with terminated_at_n not_terminated_at_n
    · have g'_eq_g : g' = g := squashGCF_eq_self_of_terminated terminated_at_n
      rw [convergents_stable_of_terminated n.le_succ terminated_at_n, g'_eq_g, IH _]
      intro _ _ m_lt_n s_mth_eq
      exact s_pos (Nat.lt.step m_lt_n) s_mth_eq
    · suffices g.convergents (n + 1) = g'.convergents n by
        -- invoke the IH for the squashed gcf
        rwa [← IH]
        

Proof state:
K : Type u_1
s : Stream'.Seq (Pair K)
inst✝ : LinearOrderedField K
n : ℕ
IH :
  ∀ {g : GeneralizedContinuedFraction K},
    (∀ {gp : Pair K} {m : ℕ}, m < n → Stream'.Seq.get? g.s m = some gp → 0 < gp.a ∧ 0 < gp.b) →
      convergents g n = convergents' g n
g : GeneralizedContinuedFraction K
s_pos : ∀ {gp : Pair K} {m : ℕ}, m < Nat.succ n → Stream'.Seq.get? g.s m = some gp → 0 < gp.a ∧ 0 < gp.b
g' : GeneralizedContinuedFraction K := squashGCF g n
not_terminated_at_n : ¬TerminatedAt g n
this : convergents g (n + 1) = convergents g' n
⊢ ∀ {gp : Pair K} {m : ℕ}, m < n → Stream'.Seq.get? g'.s m = some gp → 0 < gp.a ∧ 0 < gp.b

Next tactic:
intro gp' m m_lt_n s_mth_eq'
/-- Shows that the recurrence relation (`convergents`) and direct evaluation (`convergents'`) of the
gcf coincide at position `n` if the sequence of fractions contains strictly positive values only.
Requiring positivity of all values is just one possible condition to obtain this result.
For example, the dual - sequences with strictly negative values only - would also work.

In practice, one most commonly deals with (regular) continued fractions, which satisfy the
positivity criterion required here. The analogous result for them
(see `ContinuedFractions.convergents_eq_convergents`) hence follows directly from this theorem.
-/
theorem convergents_eq_convergents' [LinearOrderedField K]
    (s_pos : ∀ {gp : Pair K} {m : ℕ}, m < n → g.s.get? m = some gp → 0 < gp.a ∧ 0 < gp.b) :
    g.convergents n = g.convergents' n := by
  induction' n with n IH generalizing g
  case zero => simp
  case succ =>
    let g' := squashGCF g n
    -- first replace the rhs with the squashed computation
    suffices g.convergents (n + 1) = g'.convergents' n by
      rwa [succ_nth_convergent'_eq_squashGCF_nth_convergent']
    cases' Decidable.em (TerminatedAt g n) with terminated_at_n not_terminated_at_n
    · have g'_eq_g : g' = g := squashGCF_eq_self_of_terminated terminated_at_n
      rw [convergents_stable_of_terminated n.le_succ terminated_at_n, g'_eq_g, IH _]
      intro _ _ m_lt_n s_mth_eq
      exact s_pos (Nat.lt.step m_lt_n) s_mth_eq
    · suffices g.convergents (n + 1) = g'.convergents n by
        -- invoke the IH for the squashed gcf
        rwa [← IH]
        intro gp' m m_lt_n s_mth_eq'
        -- case distinction on m + 1 = n or m + 1 < n
        

Proof state:
K : Type u_1
s : Stream'.Seq (Pair K)
inst✝ : LinearOrderedField K
n : ℕ
IH :
  ∀ {g : GeneralizedContinuedFraction K},
    (∀ {gp : Pair K} {m : ℕ}, m < n → Stream'.Seq.get? g.s m = some gp → 0 < gp.a ∧ 0 < gp.b) →
      convergents g n = convergents' g n
g : GeneralizedContinuedFraction K
s_pos : ∀ {gp : Pair K} {m : ℕ}, m < Nat.succ n → Stream'.Seq.get? g.s m = some gp → 0 < gp.a ∧ 0 < gp.b
g' : GeneralizedContinuedFraction K := squashGCF g n
not_terminated_at_n : ¬TerminatedAt g n
this : convergents g (n + 1) = convergents g' n
gp' : Pair K
m : ℕ
m_lt_n : m < n
s_mth_eq' : Stream'.Seq.get? g'.s m = some gp'
⊢ 0 < gp'.a ∧ 0 < gp'.b

Next tactic:
cases' m_lt_n with n succ_m_lt_n
/-- Shows that the recurrence relation (`convergents`) and direct evaluation (`convergents'`) of the
gcf coincide at position `n` if the sequence of fractions contains strictly positive values only.
Requiring positivity of all values is just one possible condition to obtain this result.
For example, the dual - sequences with strictly negative values only - would also work.

In practice, one most commonly deals with (regular) continued fractions, which satisfy the
positivity criterion required here. The analogous result for them
(see `ContinuedFractions.convergents_eq_convergents`) hence follows directly from this theorem.
-/
theorem convergents_eq_convergents' [LinearOrderedField K]
    (s_pos : ∀ {gp : Pair K} {m : ℕ}, m < n → g.s.get? m = some gp → 0 < gp.a ∧ 0 < gp.b) :
    g.convergents n = g.convergents' n := by
  induction' n with n IH generalizing g
  case zero => simp
  case succ =>
    let g' := squashGCF g n
    -- first replace the rhs with the squashed computation
    suffices g.convergents (n + 1) = g'.convergents' n by
      rwa [succ_nth_convergent'_eq_squashGCF_nth_convergent']
    cases' Decidable.em (TerminatedAt g n) with terminated_at_n not_terminated_at_n
    · have g'_eq_g : g' = g := squashGCF_eq_self_of_terminated terminated_at_n
      rw [convergents_stable_of_terminated n.le_succ terminated_at_n, g'_eq_g, IH _]
      intro _ _ m_lt_n s_mth_eq
      exact s_pos (Nat.lt.step m_lt_n) s_mth_eq
    · suffices g.convergents (n + 1) = g'.convergents n by
        -- invoke the IH for the squashed gcf
        rwa [← IH]
        intro gp' m m_lt_n s_mth_eq'
        -- case distinction on m + 1 = n or m + 1 < n
        cases' m_lt_n with n succ_m_lt_n
        · -- the difficult case at the squashed position: we first obtain the values from
          -- the sequence
          

Proof state:
case refl
K : Type u_1
s : Stream'.Seq (Pair K)
inst✝ : LinearOrderedField K
g : GeneralizedContinuedFraction K
gp' : Pair K
m : ℕ
IH :
  ∀ {g : GeneralizedContinuedFraction K},
    (∀ {gp : Pair K} {m_1 : ℕ}, m_1 < Nat.succ m → Stream'.Seq.get? g.s m_1 = some gp → 0 < gp.a ∧ 0 < gp.b) →
      convergents g (Nat.succ m) = convergents' g (Nat.succ m)
s_pos :
  ∀ {gp : Pair K} {m_1 : ℕ}, m_1 < Nat.succ (Nat.succ m) → Stream'.Seq.get? g.s m_1 = some gp → 0 < gp.a ∧ 0 < gp.b
g' : GeneralizedContinuedFraction K := squashGCF g (Nat.succ m)
not_terminated_at_n : ¬TerminatedAt g (Nat.succ m)
this : convergents g (Nat.succ m + 1) = convergents g' (Nat.succ m)
s_mth_eq' : Stream'.Seq.get? g'.s m = some gp'
⊢ 0 < gp'.a ∧ 0 < gp'.b

Next tactic:
obtain ⟨gp_succ_m, s_succ_mth_eq⟩ : ∃ gp_succ_m, g.s.get? (m + 1) = some gp_succ_m
/-- Shows that the recurrence relation (`convergents`) and direct evaluation (`convergents'`) of the
gcf coincide at position `n` if the sequence of fractions contains strictly positive values only.
Requiring positivity of all values is just one possible condition to obtain this result.
For example, the dual - sequences with strictly negative values only - would also work.

In practice, one most commonly deals with (regular) continued fractions, which satisfy the
positivity criterion required here. The analogous result for them
(see `ContinuedFractions.convergents_eq_convergents`) hence follows directly from this theorem.
-/
theorem convergents_eq_convergents' [LinearOrderedField K]
    (s_pos : ∀ {gp : Pair K} {m : ℕ}, m < n → g.s.get? m = some gp → 0 < gp.a ∧ 0 < gp.b) :
    g.convergents n = g.convergents' n := by
  induction' n with n IH generalizing g
  case zero => simp
  case succ =>
    let g' := squashGCF g n
    -- first replace the rhs with the squashed computation
    suffices g.convergents (n + 1) = g'.convergents' n by
      rwa [succ_nth_convergent'_eq_squashGCF_nth_convergent']
    cases' Decidable.em (TerminatedAt g n) with terminated_at_n not_terminated_at_n
    · have g'_eq_g : g' = g := squashGCF_eq_self_of_terminated terminated_at_n
      rw [convergents_stable_of_terminated n.le_succ terminated_at_n, g'_eq_g, IH _]
      intro _ _ m_lt_n s_mth_eq
      exact s_pos (Nat.lt.step m_lt_n) s_mth_eq
    · suffices g.convergents (n + 1) = g'.convergents n by
        -- invoke the IH for the squashed gcf
        rwa [← IH]
        intro gp' m m_lt_n s_mth_eq'
        -- case distinction on m + 1 = n or m + 1 < n
        cases' m_lt_n with n succ_m_lt_n
        · -- the difficult case at the squashed position: we first obtain the values from
          -- the sequence
          obtain ⟨gp_succ_m, s_succ_mth_eq⟩ : ∃ gp_succ_m, g.s.get? (m + 1) = some gp_succ_m
          exact Option.ne_none_iff_exists'.mp not_terminated_at_n
          

Proof state:
case refl.intro
K : Type u_1
s : Stream'.Seq (Pair K)
inst✝ : LinearOrderedField K
g : GeneralizedContinuedFraction K
gp' : Pair K
m : ℕ
IH :
  ∀ {g : GeneralizedContinuedFraction K},
    (∀ {gp : Pair K} {m_1 : ℕ}, m_1 < Nat.succ m → Stream'.Seq.get? g.s m_1 = some gp → 0 < gp.a ∧ 0 < gp.b) →
      convergents g (Nat.succ m) = convergents' g (Nat.succ m)
s_pos :
  ∀ {gp : Pair K} {m_1 : ℕ}, m_1 < Nat.succ (Nat.succ m) → Stream'.Seq.get? g.s m_1 = some gp → 0 < gp.a ∧ 0 < gp.b
g' : GeneralizedContinuedFraction K := squashGCF g (Nat.succ m)
not_terminated_at_n : ¬TerminatedAt g (Nat.succ m)
this : convergents g (Nat.succ m + 1) = convergents g' (Nat.succ m)
s_mth_eq' : Stream'.Seq.get? g'.s m = some gp'
gp_succ_m : Pair K
s_succ_mth_eq : Stream'.Seq.get? g.s (m + 1) = some gp_succ_m
⊢ 0 < gp'.a ∧ 0 < gp'.b

Next tactic:
obtain ⟨gp_m, mth_s_eq⟩ : ∃ gp_m, g.s.get? m = some gp_m
/-- Shows that the recurrence relation (`convergents`) and direct evaluation (`convergents'`) of a
(regular) continued fraction coincide. -/
nonrec theorem convergents_eq_convergents' [LinearOrderedField K] {c : ContinuedFraction K} :
    (↑c : GeneralizedContinuedFraction K).convergents =
    (↑c : GeneralizedContinuedFraction K).convergents' := by
  

Proof state:
K : Type u_1
n : ℕ
inst✝ : LinearOrderedField K
c : ContinuedFraction K
⊢ convergents ↑↑c = convergents' ↑↑c

Next tactic:
ext n
/-- Shows that the recurrence relation (`convergents`) and direct evaluation (`convergents'`) of a
(regular) continued fraction coincide. -/
nonrec theorem convergents_eq_convergents' [LinearOrderedField K] {c : ContinuedFraction K} :
    (↑c : GeneralizedContinuedFraction K).convergents =
    (↑c : GeneralizedContinuedFraction K).convergents' := by
  ext n
  

Proof state:
case a
K : Type u_1
n✝ : ℕ
inst✝ : LinearOrderedField K
c : ContinuedFraction K
n : ℕ
⊢ Stream'.get (convergents ↑↑c) n = Stream'.get (convergents' ↑↑c) n

Next tactic:
apply convergents_eq_convergents'
/-- Shows that the recurrence relation (`convergents`) and direct evaluation (`convergents'`) of a
(regular) continued fraction coincide. -/
nonrec theorem convergents_eq_convergents' [LinearOrderedField K] {c : ContinuedFraction K} :
    (↑c : GeneralizedContinuedFraction K).convergents =
    (↑c : GeneralizedContinuedFraction K).convergents' := by
  ext n
  apply convergents_eq_convergents'
  

Proof state:
case a.s_pos
K : Type u_1
n✝ : ℕ
inst✝ : LinearOrderedField K
c : ContinuedFraction K
n : ℕ
⊢ ∀ {gp : Pair K} {m : ℕ}, m < n → Stream'.Seq.get? (↑↑c).s m = some gp → 0 < gp.a ∧ 0 < gp.b

Next tactic:
intro gp m _ s_nth_eq
/-- Shows that the recurrence relation (`convergents`) and direct evaluation (`convergents'`) of a
(regular) continued fraction coincide. -/
nonrec theorem convergents_eq_convergents' [LinearOrderedField K] {c : ContinuedFraction K} :
    (↑c : GeneralizedContinuedFraction K).convergents =
    (↑c : GeneralizedContinuedFraction K).convergents' := by
  ext n
  apply convergents_eq_convergents'
  intro gp m _ s_nth_eq
  

Proof state:
case a.s_pos
K : Type u_1
n✝ : ℕ
inst✝ : LinearOrderedField K
c : ContinuedFraction K
n : ℕ
gp : Pair K
m : ℕ
a✝ : m < n
s_nth_eq : Stream'.Seq.get? (↑↑c).s m = some gp
⊢ 0 < gp.a ∧ 0 < gp.b

Next tactic:
exact ⟨zero_lt_one.trans_le ((c : SimpleContinuedFraction K).property m gp.a
    (part_num_eq_s_a s_nth_eq)).symm.le, c.property m gp.b <| part_denom_eq_s_b s_nth_eq⟩
/-- Core operation for binary heaps, expressed directly on arrays.
Given an array which is a max-heap, push item `i` down to restore the max-heap property. -/
def heapifyDown (lt : α → α → Bool) (a : Array α) (i : Fin a.size) :
    {a' : Array α // a'.size = a.size} :=
  let left := 2 * i.1 + 1
  let right := left + 1
  have left_le : i ≤ left := Nat.le_trans
    (by 

Proof state:
α : Type ?u.325
lt : α → α → Bool
a : Array α
i : Fin (Array.size a)
left : Nat := 2 * i.val + 1
right : Nat := left + 1
⊢ i.val ≤ 2 * i.val

Next tactic:
rw [Nat.succ_mul, Nat.one_mul]
/-- Core operation for binary heaps, expressed directly on arrays.
Given an array which is a max-heap, push item `i` down to restore the max-heap property. -/
def heapifyDown (lt : α → α → Bool) (a : Array α) (i : Fin a.size) :
    {a' : Array α // a'.size = a.size} :=
  let left := 2 * i.1 + 1
  let right := left + 1
  have left_le : i ≤ left := Nat.le_trans
    (by rw [Nat.succ_mul, Nat.one_mul]; 

Proof state:
α : Type ?u.325
lt : α → α → Bool
a : Array α
i : Fin (Array.size a)
left : Nat := 2 * i.val + 1
right : Nat := left + 1
⊢ i.val ≤ i.val + i.val

Next tactic:
exact Nat.le_add_left i i
/-- Core operation for binary heaps, expressed directly on arrays.
Given an array which is a max-heap, push item `i` down to restore the max-heap property. -/
def heapifyDown (lt : α → α → Bool) (a : Array α) (i : Fin a.size) :
    {a' : Array α // a'.size = a.size} :=
  let left := 2 * i.1 + 1
  let right := left + 1
  have left_le : i ≤ left := Nat.le_trans
    (by rw [Nat.succ_mul, Nat.one_mul]; exact Nat.le_add_left i i)
    (Nat.le_add_right ..)
  have right_le : i ≤ right := Nat.le_trans left_le (Nat.le_add_right ..)
  have i_le : i ≤ i := Nat.le_refl _
  have j : {j : Fin a.size // i ≤ j} := if h : left < a.size then
    if lt (a.get i) (a.get ⟨left, h⟩) then ⟨⟨left, h⟩, left_le⟩ else ⟨i, i_le⟩ else ⟨i, i_le⟩
  have j := if h : right < a.size then
    if lt (a.get j) (a.get ⟨right, h⟩) then ⟨⟨right, h⟩, right_le⟩ else j else j
  if h : i.1 = j then ⟨a, rfl⟩ else
    let a' := a.swap i j
    let j' := ⟨j, by 

Proof state:
α : Type ?u.325
lt : α → α → Bool
a : Array α
i : Fin (Array.size a)
left : Nat := 2 * i.val + 1
right : Nat := left + 1
left_le : i.val ≤ left
right_le : i.val ≤ right
i_le : i ≤ i
j✝ j : { j // i ≤ j }
h : ¬i.val = j.val.val
a' : Array α := Array.swap a i j.val
⊢ j.val.val < Array.size a'

Next tactic:
rw [a.size_swap i j]
/-- Core operation for binary heaps, expressed directly on arrays.
Given an array which is a max-heap, push item `i` down to restore the max-heap property. -/
def heapifyDown (lt : α → α → Bool) (a : Array α) (i : Fin a.size) :
    {a' : Array α // a'.size = a.size} :=
  let left := 2 * i.1 + 1
  let right := left + 1
  have left_le : i ≤ left := Nat.le_trans
    (by rw [Nat.succ_mul, Nat.one_mul]; exact Nat.le_add_left i i)
    (Nat.le_add_right ..)
  have right_le : i ≤ right := Nat.le_trans left_le (Nat.le_add_right ..)
  have i_le : i ≤ i := Nat.le_refl _
  have j : {j : Fin a.size // i ≤ j} := if h : left < a.size then
    if lt (a.get i) (a.get ⟨left, h⟩) then ⟨⟨left, h⟩, left_le⟩ else ⟨i, i_le⟩ else ⟨i, i_le⟩
  have j := if h : right < a.size then
    if lt (a.get j) (a.get ⟨right, h⟩) then ⟨⟨right, h⟩, right_le⟩ else j else j
  if h : i.1 = j then ⟨a, rfl⟩ else
    let a' := a.swap i j
    let j' := ⟨j, by rw [a.size_swap i j]; 

Proof state:
α : Type ?u.325
lt : α → α → Bool
a : Array α
i : Fin (Array.size a)
left : Nat := 2 * i.val + 1
right : Nat := left + 1
left_le : i.val ≤ left
right_le : i.val ≤ right
i_le : i ≤ i
j✝ j : { j // i ≤ j }
h : ¬i.val = j.val.val
a' : Array α := Array.swap a i j.val
⊢ j.val.val < Array.size a

Next tactic:
exact j.1.2
/-- Core operation for binary heaps, expressed directly on arrays.
Given an array which is a max-heap, push item `i` down to restore the max-heap property. -/
def heapifyDown (lt : α → α → Bool) (a : Array α) (i : Fin a.size) :
    {a' : Array α // a'.size = a.size} :=
  let left := 2 * i.1 + 1
  let right := left + 1
  have left_le : i ≤ left := Nat.le_trans
    (by rw [Nat.succ_mul, Nat.one_mul]; exact Nat.le_add_left i i)
    (Nat.le_add_right ..)
  have right_le : i ≤ right := Nat.le_trans left_le (Nat.le_add_right ..)
  have i_le : i ≤ i := Nat.le_refl _
  have j : {j : Fin a.size // i ≤ j} := if h : left < a.size then
    if lt (a.get i) (a.get ⟨left, h⟩) then ⟨⟨left, h⟩, left_le⟩ else ⟨i, i_le⟩ else ⟨i, i_le⟩
  have j := if h : right < a.size then
    if lt (a.get j) (a.get ⟨right, h⟩) then ⟨⟨right, h⟩, right_le⟩ else j else j
  if h : i.1 = j then ⟨a, rfl⟩ else
    let a' := a.swap i j
    let j' := ⟨j, by rw [a.size_swap i j]; exact j.1.2⟩
    have : a'.size - j < a.size - i := by
      

Proof state:
α : Type ?u.325
lt : α → α → Bool
a : Array α
i : Fin (Array.size a)
left : Nat := 2 * i.val + 1
right : Nat := left + 1
left_le : i.val ≤ left
right_le : i.val ≤ right
i_le : i ≤ i
j✝ j : { j // i ≤ j }
h : ¬i.val = j.val.val
a' : Array α := Array.swap a i j.val
j' : Fin (Array.size a') := { val := j.val.val, isLt := ?m.2241 }
⊢ Array.size a' - j.val.val < Array.size a - i.val

Next tactic:
rw [a.size_swap i j]
/-- Core operation for binary heaps, expressed directly on arrays.
Given an array which is a max-heap, push item `i` down to restore the max-heap property. -/
def heapifyDown (lt : α → α → Bool) (a : Array α) (i : Fin a.size) :
    {a' : Array α // a'.size = a.size} :=
  let left := 2 * i.1 + 1
  let right := left + 1
  have left_le : i ≤ left := Nat.le_trans
    (by rw [Nat.succ_mul, Nat.one_mul]; exact Nat.le_add_left i i)
    (Nat.le_add_right ..)
  have right_le : i ≤ right := Nat.le_trans left_le (Nat.le_add_right ..)
  have i_le : i ≤ i := Nat.le_refl _
  have j : {j : Fin a.size // i ≤ j} := if h : left < a.size then
    if lt (a.get i) (a.get ⟨left, h⟩) then ⟨⟨left, h⟩, left_le⟩ else ⟨i, i_le⟩ else ⟨i, i_le⟩
  have j := if h : right < a.size then
    if lt (a.get j) (a.get ⟨right, h⟩) then ⟨⟨right, h⟩, right_le⟩ else j else j
  if h : i.1 = j then ⟨a, rfl⟩ else
    let a' := a.swap i j
    let j' := ⟨j, by rw [a.size_swap i j]; exact j.1.2⟩
    have : a'.size - j < a.size - i := by
      rw [a.size_swap i j]; 

Proof state:
α : Type ?u.325
lt : α → α → Bool
a : Array α
i : Fin (Array.size a)
left : Nat := 2 * i.val + 1
right : Nat := left + 1
left_le : i.val ≤ left
right_le : i.val ≤ right
i_le : i ≤ i
j✝ j : { j // i ≤ j }
h : ¬i.val = j.val.val
a' : Array α := Array.swap a i j.val
j' : Fin (Array.size a') := { val := j.val.val, isLt := ?m.2241 }
⊢ Array.size a - j.val.val < Array.size a - i.val

Next tactic:
exact Nat.sub_lt_sub_left i.2 <| Nat.lt_of_le_of_ne j.2 h
/-- Core operation for binary heaps, expressed directly on arrays.
Given an array which is a max-heap, push item `i` down to restore the max-heap property. -/
def heapifyDown (lt : α → α → Bool) (a : Array α) (i : Fin a.size) :
    {a' : Array α // a'.size = a.size} :=
  let left := 2 * i.1 + 1
  let right := left + 1
  have left_le : i ≤ left := Nat.le_trans
    (by rw [Nat.succ_mul, Nat.one_mul]; exact Nat.le_add_left i i)
    (Nat.le_add_right ..)
  have right_le : i ≤ right := Nat.le_trans left_le (Nat.le_add_right ..)
  have i_le : i ≤ i := Nat.le_refl _
  have j : {j : Fin a.size // i ≤ j} := if h : left < a.size then
    if lt (a.get i) (a.get ⟨left, h⟩) then ⟨⟨left, h⟩, left_le⟩ else ⟨i, i_le⟩ else ⟨i, i_le⟩
  have j := if h : right < a.size then
    if lt (a.get j) (a.get ⟨right, h⟩) then ⟨⟨right, h⟩, right_le⟩ else j else j
  if h : i.1 = j then ⟨a, rfl⟩ else
    let a' := a.swap i j
    let j' := ⟨j, by rw [a.size_swap i j]; exact j.1.2⟩
    have : a'.size - j < a.size - i := by
      rw [a.size_swap i j]; exact Nat.sub_lt_sub_left i.2 <| Nat.lt_of_le_of_ne j.2 h
    let ⟨a₂, h₂⟩ := heapifyDown lt a' j'
    ⟨a₂, h₂.trans (a.size_swap i j)⟩
termination_by _ => a.size - i
decreasing_by 

Proof state:
α : Type u_1
a : Array α
i : Fin (Array.size a)
left : Nat := 2 * i.val + 1
right : Nat := left + 1
left_le : i.val ≤ left
right_le : i.val ≤ right
i_le : i ≤ i
j : { j // i ≤ j }
h : ¬i.val = j.val.val
a' : Array α := Array.swap a i j.val
j' : Fin (Array.size a') := { val := j.val.val, isLt := (_ : j.val.val < Array.size a') }
this : Array.size a' - j.val.val < Array.size a - i.val
⊢ (invImage (fun a => PSigma.casesOn a fun a snd => Array.size a - snd.val) instWellFoundedRelation).1
    { fst := a', snd := j' } { fst := a, snd := i }

Next tactic:
assumption
/-- Core operation for binary heaps, expressed directly on arrays.
Given an array which is a max-heap, push item `i` up to restore the max-heap property. -/
def heapifyUp (lt : α → α → Bool) (a : Array α) (i : Fin a.size) :
    {a' : Array α // a'.size = a.size} :=
if i0 : i.1 = 0 then ⟨a, rfl⟩ else
  have : (i.1 - 1) / 2 < i := Nat.lt_of_le_of_lt (Nat.div_le_self ..) $
    Nat.sub_lt (Nat.pos_of_ne_zero i0) Nat.zero_lt_one
  let j := ⟨(i.1 - 1) / 2, Nat.lt_trans this i.2⟩
  if lt (a.get j) (a.get i) then
    let a' := a.swap i j
    let ⟨a₂, h₂⟩ := heapifyUp lt a' ⟨j.1, by 

Proof state:
α : Type ?u.5387
lt : α → α → Bool
a : Array α
i : Fin (Array.size a)
i0 : ¬i.val = 0
this : (i.val - 1) / 2 < i.val
j : Fin (Array.size a) := { val := (i.val - 1) / 2, isLt := (_ : (i.val - 1) / 2 < Array.size a) }
a' : Array α := Array.swap a i j
⊢ j.val < Array.size a'

Next tactic:
rw [a.size_swap i j]