l3lab/ntp-mathlib huggingface dataset:
theorem set {arr : Array α} {n} {m : Fin n → β} (H : Agrees arr f m)
    {i : Fin arr.size} {x} {m' : Fin n → β}
  (hm₁ : ∀ (j : Fin n), j.1 ≠ i → m' j = m j)
  (hm₂ : ∀ (h : i < n), f x = m' ⟨i, h⟩) : Agrees (arr.set i x) f m' := by
  cases H
  

Proof state:
case mk
α : Type u_1
β : Sort u_2
f : α → β
arr : Array α
i : Fin (Array.size arr)
x : α
m' : Fin (Array.size arr) → β
hm₂ : ∀ (h : ↑i < Array.size arr), f x = m' { val := ↑i, isLt := h }
hm₁ : ∀ (j : Fin (Array.size arr)), ↑j ≠ ↑i → m' j = (fun i => f (Array.get arr i)) j
⊢ Agrees (Array.set arr i x) f m'

Next tactic:
refine mk' (by simp) fun j hj₁ hj₂ ↦ ?_
theorem set {arr : Array α} {n} {m : Fin n → β} (H : Agrees arr f m)
    {i : Fin arr.size} {x} {m' : Fin n → β}
  (hm₁ : ∀ (j : Fin n), j.1 ≠ i → m' j = m j)
  (hm₂ : ∀ (h : i < n), f x = m' ⟨i, h⟩) : Agrees (arr.set i x) f m' := by
  cases H
  refine mk' (by 

Proof state:
α : Type u_1
β : Sort u_2
f : α → β
arr : Array α
i : Fin (Array.size arr)
x : α
m' : Fin (Array.size arr) → β
hm₂ : ∀ (h : ↑i < Array.size arr), f x = m' { val := ↑i, isLt := h }
hm₁ : ∀ (j : Fin (Array.size arr)), ↑j ≠ ↑i → m' j = (fun i => f (Array.get arr i)) j
⊢ Array.size arr = Array.size (Array.set arr i x)

Next tactic:
simp
theorem set {arr : Array α} {n} {m : Fin n → β} (H : Agrees arr f m)
    {i : Fin arr.size} {x} {m' : Fin n → β}
  (hm₁ : ∀ (j : Fin n), j.1 ≠ i → m' j = m j)
  (hm₂ : ∀ (h : i < n), f x = m' ⟨i, h⟩) : Agrees (arr.set i x) f m' := by
  cases H
  refine mk' (by simp) fun j hj₁ hj₂ ↦ ?_
  

Proof state:
case mk
α : Type u_1
β : Sort u_2
f : α → β
arr : Array α
i : Fin (Array.size arr)
x : α
m' : Fin (Array.size arr) → β
hm₂ : ∀ (h : ↑i < Array.size arr), f x = m' { val := ↑i, isLt := h }
hm₁ : ∀ (j : Fin (Array.size arr)), ↑j ≠ ↑i → m' j = (fun i => f (Array.get arr i)) j
j : ℕ
hj₁ : j < Array.size (Array.set arr i x)
hj₂ : j < Array.size arr
⊢ f (Array.get (Array.set arr i x) { val := j, isLt := hj₁ }) = m' { val := j, isLt := hj₂ }

Next tactic:
suffices f (Array.set arr i x)[j] = m' ⟨j, hj₂⟩ by simp_all [Array.get_set]
theorem set {arr : Array α} {n} {m : Fin n → β} (H : Agrees arr f m)
    {i : Fin arr.size} {x} {m' : Fin n → β}
  (hm₁ : ∀ (j : Fin n), j.1 ≠ i → m' j = m j)
  (hm₂ : ∀ (h : i < n), f x = m' ⟨i, h⟩) : Agrees (arr.set i x) f m' := by
  cases H
  refine mk' (by simp) fun j hj₁ hj₂ ↦ ?_
  suffices f (Array.set arr i x)[j] = m' ⟨j, hj₂⟩ by 

Proof state:
α : Type u_1
β : Sort u_2
f : α → β
arr : Array α
i : Fin (Array.size arr)
x : α
m' : Fin (Array.size arr) → β
hm₂ : ∀ (h : ↑i < Array.size arr), f x = m' { val := ↑i, isLt := h }
hm₁ : ∀ (j : Fin (Array.size arr)), ↑j ≠ ↑i → m' j = (fun i => f (Array.get arr i)) j
j : ℕ
hj₁ : j < Array.size (Array.set arr i x)
hj₂ : j < Array.size arr
this : f (Array.set arr i x)[j] = m' { val := j, isLt := hj₂ }
⊢ f (Array.get (Array.set arr i x) { val := j, isLt := hj₁ }) = m' { val := j, isLt := hj₂ }

Next tactic:
simp_all [Array.get_set]
theorem set {arr : Array α} {n} {m : Fin n → β} (H : Agrees arr f m)
    {i : Fin arr.size} {x} {m' : Fin n → β}
  (hm₁ : ∀ (j : Fin n), j.1 ≠ i → m' j = m j)
  (hm₂ : ∀ (h : i < n), f x = m' ⟨i, h⟩) : Agrees (arr.set i x) f m' := by
  cases H
  refine mk' (by simp) fun j hj₁ hj₂ ↦ ?_
  suffices f (Array.set arr i x)[j] = m' ⟨j, hj₂⟩ by simp_all [Array.get_set]
  

Proof state:
case mk
α : Type u_1
β : Sort u_2
f : α → β
arr : Array α
i : Fin (Array.size arr)
x : α
m' : Fin (Array.size arr) → β
hm₂ : ∀ (h : ↑i < Array.size arr), f x = m' { val := ↑i, isLt := h }
hm₁ : ∀ (j : Fin (Array.size arr)), ↑j ≠ ↑i → m' j = (fun i => f (Array.get arr i)) j
j : ℕ
hj₁ : j < Array.size (Array.set arr i x)
hj₂ : j < Array.size arr
⊢ f (Array.set arr i x)[j] = m' { val := j, isLt := hj₂ }

Next tactic:
by_cases h : i = j
theorem set {arr : Array α} {n} {m : Fin n → β} (H : Agrees arr f m)
    {i : Fin arr.size} {x} {m' : Fin n → β}
  (hm₁ : ∀ (j : Fin n), j.1 ≠ i → m' j = m j)
  (hm₂ : ∀ (h : i < n), f x = m' ⟨i, h⟩) : Agrees (arr.set i x) f m' := by
  cases H
  refine mk' (by simp) fun j hj₁ hj₂ ↦ ?_
  suffices f (Array.set arr i x)[j] = m' ⟨j, hj₂⟩ by simp_all [Array.get_set]
  by_cases h : i = j
  · 

Proof state:
case pos
α : Type u_1
β : Sort u_2
f : α → β
arr : Array α
i : Fin (Array.size arr)
x : α
m' : Fin (Array.size arr) → β
hm₂ : ∀ (h : ↑i < Array.size arr), f x = m' { val := ↑i, isLt := h }
hm₁ : ∀ (j : Fin (Array.size arr)), ↑j ≠ ↑i → m' j = (fun i => f (Array.get arr i)) j
j : ℕ
hj₁ : j < Array.size (Array.set arr i x)
hj₂ : j < Array.size arr
h : ↑i = j
⊢ f (Array.set arr i x)[j] = m' { val := j, isLt := hj₂ }

Next tactic:
subst h
theorem set {arr : Array α} {n} {m : Fin n → β} (H : Agrees arr f m)
    {i : Fin arr.size} {x} {m' : Fin n → β}
  (hm₁ : ∀ (j : Fin n), j.1 ≠ i → m' j = m j)
  (hm₂ : ∀ (h : i < n), f x = m' ⟨i, h⟩) : Agrees (arr.set i x) f m' := by
  cases H
  refine mk' (by simp) fun j hj₁ hj₂ ↦ ?_
  suffices f (Array.set arr i x)[j] = m' ⟨j, hj₂⟩ by simp_all [Array.get_set]
  by_cases h : i = j
  · subst h; 

Proof state:
case pos
α : Type u_1
β : Sort u_2
f : α → β
arr : Array α
i : Fin (Array.size arr)
x : α
m' : Fin (Array.size arr) → β
hm₂ : ∀ (h : ↑i < Array.size arr), f x = m' { val := ↑i, isLt := h }
hm₁ : ∀ (j : Fin (Array.size arr)), ↑j ≠ ↑i → m' j = (fun i => f (Array.get arr i)) j
hj₁ : ↑i < Array.size (Array.set arr i x)
hj₂ : ↑i < Array.size arr
⊢ f (Array.set arr i x)[↑i] = m' { val := ↑i, isLt := hj₂ }

Next tactic:
rw [Array.get_set_eq, ← hm₂]
theorem set {arr : Array α} {n} {m : Fin n → β} (H : Agrees arr f m)
    {i : Fin arr.size} {x} {m' : Fin n → β}
  (hm₁ : ∀ (j : Fin n), j.1 ≠ i → m' j = m j)
  (hm₂ : ∀ (h : i < n), f x = m' ⟨i, h⟩) : Agrees (arr.set i x) f m' := by
  cases H
  refine mk' (by simp) fun j hj₁ hj₂ ↦ ?_
  suffices f (Array.set arr i x)[j] = m' ⟨j, hj₂⟩ by simp_all [Array.get_set]
  by_cases h : i = j
  · subst h; rw [Array.get_set_eq, ← hm₂]
  · 

Proof state:
case neg
α : Type u_1
β : Sort u_2
f : α → β
arr : Array α
i : Fin (Array.size arr)
x : α
m' : Fin (Array.size arr) → β
hm₂ : ∀ (h : ↑i < Array.size arr), f x = m' { val := ↑i, isLt := h }
hm₁ : ∀ (j : Fin (Array.size arr)), ↑j ≠ ↑i → m' j = (fun i => f (Array.get arr i)) j
j : ℕ
hj₁ : j < Array.size (Array.set arr i x)
hj₂ : j < Array.size arr
h : ¬↑i = j
⊢ f (Array.set arr i x)[j] = m' { val := j, isLt := hj₂ }

Next tactic:
rw [arr.get_set_ne _ _ _ h, hm₁ ⟨j, _⟩ (Ne.symm h)]
theorem set {arr : Array α} {n} {m : Fin n → β} (H : Agrees arr f m)
    {i : Fin arr.size} {x} {m' : Fin n → β}
  (hm₁ : ∀ (j : Fin n), j.1 ≠ i → m' j = m j)
  (hm₂ : ∀ (h : i < n), f x = m' ⟨i, h⟩) : Agrees (arr.set i x) f m' := by
  cases H
  refine mk' (by simp) fun j hj₁ hj₂ ↦ ?_
  suffices f (Array.set arr i x)[j] = m' ⟨j, hj₂⟩ by simp_all [Array.get_set]
  by_cases h : i = j
  · subst h; rw [Array.get_set_eq, ← hm₂]
  · rw [arr.get_set_ne _ _ _ h, hm₁ ⟨j, _⟩ (Ne.symm h)]; 

Proof state:
case neg
α : Type u_1
β : Sort u_2
f : α → β
arr : Array α
i : Fin (Array.size arr)
x : α
m' : Fin (Array.size arr) → β
hm₂ : ∀ (h : ↑i < Array.size arr), f x = m' { val := ↑i, isLt := h }
hm₁ : ∀ (j : Fin (Array.size arr)), ↑j ≠ ↑i → m' j = (fun i => f (Array.get arr i)) j
j : ℕ
hj₁ : j < Array.size (Array.set arr i x)
hj₂ : j < Array.size arr
h : ¬↑i = j
⊢ f arr[j] = (fun i => f (Array.get arr i)) { val := j, isLt := hj₂ }
α : Type u_1
β : Sort u_2
f : α → β
arr : Array α
i : Fin (Array.size arr)
x : α
m' : Fin (Array.size arr) → β
hm₂ : ∀ (h : ↑i < Array.size arr), f x = m' { val := ↑i, isLt := h }
hm₁ : ∀ (j : Fin (Array.size arr)), ↑j ≠ ↑i → m' j = (fun i => f (Array.get arr i)) j
j : ℕ
hj₁ : j < Array.size (Array.set arr i x)
hj₂ : j < Array.size arr
h : ¬↑i = j
⊢ j < Array.size arr
α : Type u_1
β : Sort u_2
f : α → β
arr : Array α
i : Fin (Array.size arr)
x : α
m' : Fin (Array.size arr) → β
hm₂ : ∀ (h : ↑i < Array.size arr), f x = m' { val := ↑i, isLt := h }
hm₁ : ∀ (j : Fin (Array.size arr)), ↑j ≠ ↑i → m' j = (fun i => f (Array.get arr i)) j
j : ℕ
hj₁ : j < Array.size (Array.set arr i x)
hj₂ : j < Array.size arr
h : ¬↑i = j
⊢ j < Array.size arr

Next tactic:
rfl
theorem rank_eq {arr : Array (UFNode α)} {n} {m : UFModel n} (H : m.Models arr) (i : Nat)
    (h : i < arr.size) : arr[i].rank = m.rank i :=
  H.2.get_eq _ _ (by 

Proof state:
α : Type u_1
arr : Array (UFNode α)
n : ℕ
m : UFModel n
H : Models arr m
i : ℕ
h : i < Array.size arr
⊢ i < n

Next tactic:
rw [H.size_eq]
theorem rank_eq {arr : Array (UFNode α)} {n} {m : UFModel n} (H : m.Models arr) (i : Nat)
    (h : i < arr.size) : arr[i].rank = m.rank i :=
  H.2.get_eq _ _ (by rw [H.size_eq]; 

Proof state:
α : Type u_1
arr : Array (UFNode α)
n : ℕ
m : UFModel n
H : Models arr m
i : ℕ
h : i < Array.size arr
⊢ i < Array.size arr

Next tactic:
exact h
theorem push {arr : Array (UFNode α)} {n} {m : UFModel n} (H : m.Models arr)
    (k) (hk : k = n + 1) (x) :
    (m.push k (hk ▸ Nat.le_add_right ..)).Models (arr.push ⟨n, x, 0⟩) := by
  

Proof state:
α : Type u_1
arr : Array (UFNode α)
n : ℕ
m : UFModel n
H : Models arr m
k : ℕ
hk : k = n + 1
x : α
⊢ Models (Array.push arr { parent := n, value := x, rank := 0 }) (UFModel.push m k (_ : n ≤ k))

Next tactic:
apply H.imp
theorem push {arr : Array (UFNode α)} {n} {m : UFModel n} (H : m.Models arr)
    (k) (hk : k = n + 1) (x) :
    (m.push k (hk ▸ Nat.le_add_right ..)).Models (arr.push ⟨n, x, 0⟩) := by
  apply H.imp <;>
  · 

Proof state:
case f
α : Type u_1
arr : Array (UFNode α)
n : ℕ
m : UFModel n
H : Models arr m
k : ℕ
hk : k = n + 1
x : α
⊢ (Agrees arr (fun x => x.parent) fun i => ↑(parent m i)) →
    Agrees (Array.push arr { parent := n, value := x, rank := 0 }) (fun x => x.parent) fun i =>
      ↑(parent (UFModel.push m k (_ : n ≤ k)) i)

Next tactic:
intro H
theorem push {arr : Array (UFNode α)} {n} {m : UFModel n} (H : m.Models arr)
    (k) (hk : k = n + 1) (x) :
    (m.push k (hk ▸ Nat.le_add_right ..)).Models (arr.push ⟨n, x, 0⟩) := by
  apply H.imp <;>
  · intro H
    

Proof state:
case f
α : Type u_1
arr : Array (UFNode α)
n : ℕ
m : UFModel n
H✝ : Models arr m
k : ℕ
hk : k = n + 1
x : α
H : Agrees arr (fun x => x.parent) fun i => ↑(parent m i)
⊢ Agrees (Array.push arr { parent := n, value := x, rank := 0 }) (fun x => x.parent) fun i =>
    ↑(parent (UFModel.push m k (_ : n ≤ k)) i)

Next tactic:
refine H.push _ hk _ _ (fun i h ↦ ?_) (fun h ↦ ?_)
theorem push {arr : Array (UFNode α)} {n} {m : UFModel n} (H : m.Models arr)
    (k) (hk : k = n + 1) (x) :
    (m.push k (hk ▸ Nat.le_add_right ..)).Models (arr.push ⟨n, x, 0⟩) := by
  apply H.imp <;>
  · intro H
    refine H.push _ hk _ _ (fun i h ↦ ?_) (fun h ↦ ?_) <;>
    

Proof state:
case f.refine_1
α : Type u_1
arr : Array (UFNode α)
n : ℕ
m : UFModel n
H✝ : Models arr m
k : ℕ
hk : k = n + 1
x : α
H : Agrees arr (fun x => x.parent) fun i => ↑(parent m i)
i : Fin k
h : ↑i < n
⊢ ↑(parent (UFModel.push m k (_ : n ≤ k)) i) = ↑(parent m { val := ↑i, isLt := h })

Next tactic:
simp [UFModel.push, h, lt_irrefl]
theorem push {arr : Array (UFNode α)} {n} {m : UFModel n} (H : m.Models arr)
    (k) (hk : k = n + 1) (x) :
    (m.push k (hk ▸ Nat.le_add_right ..)).Models (arr.push ⟨n, x, 0⟩) := by
  apply H.imp <;>
  · intro H
    refine H.push _ hk _ _ (fun i h ↦ ?_) (fun h ↦ ?_) <;>
    

Proof state:
case f.refine_2
α : Type u_1
arr : Array (UFNode α)
n : ℕ
m : UFModel n
H✝ : Models arr m
k : ℕ
hk : k = n + 1
x : α
H : Agrees arr (fun x => x.parent) fun i => ↑(parent m i)
h : n < k
⊢ { parent := n, value := x, rank := 0 }.parent = ↑(parent (UFModel.push m k (_ : n ≤ k)) { val := n, isLt := h })

Next tactic:
simp [UFModel.push, h, lt_irrefl]
theorem push {arr : Array (UFNode α)} {n} {m : UFModel n} (H : m.Models arr)
    (k) (hk : k = n + 1) (x) :
    (m.push k (hk ▸ Nat.le_add_right ..)).Models (arr.push ⟨n, x, 0⟩) := by
  apply H.imp <;>
  · 

Proof state:
case g
α : Type u_1
arr : Array (UFNode α)
n : ℕ
m : UFModel n
H : Models arr m
k : ℕ
hk : k = n + 1
x : α
⊢ (Agrees arr (fun x => x.rank) fun i => rank m ↑i) →
    Agrees (Array.push arr { parent := n, value := x, rank := 0 }) (fun x => x.rank) fun i =>
      rank (UFModel.push m k (_ : n ≤ k)) ↑i

Next tactic:
intro H
theorem push {arr : Array (UFNode α)} {n} {m : UFModel n} (H : m.Models arr)
    (k) (hk : k = n + 1) (x) :
    (m.push k (hk ▸ Nat.le_add_right ..)).Models (arr.push ⟨n, x, 0⟩) := by
  apply H.imp <;>
  · intro H
    

Proof state:
case g
α : Type u_1
arr : Array (UFNode α)
n : ℕ
m : UFModel n
H✝ : Models arr m
k : ℕ
hk : k = n + 1
x : α
H : Agrees arr (fun x => x.rank) fun i => rank m ↑i
⊢ Agrees (Array.push arr { parent := n, value := x, rank := 0 }) (fun x => x.rank) fun i =>
    rank (UFModel.push m k (_ : n ≤ k)) ↑i

Next tactic:
refine H.push _ hk _ _ (fun i h ↦ ?_) (fun h ↦ ?_)
theorem push {arr : Array (UFNode α)} {n} {m : UFModel n} (H : m.Models arr)
    (k) (hk : k = n + 1) (x) :
    (m.push k (hk ▸ Nat.le_add_right ..)).Models (arr.push ⟨n, x, 0⟩) := by
  apply H.imp <;>
  · intro H
    refine H.push _ hk _ _ (fun i h ↦ ?_) (fun h ↦ ?_) <;>
    

Proof state:
case g.refine_1
α : Type u_1
arr : Array (UFNode α)
n : ℕ
m : UFModel n
H✝ : Models arr m
k : ℕ
hk : k = n + 1
x : α
H : Agrees arr (fun x => x.rank) fun i => rank m ↑i
i : Fin k
h : ↑i < n
⊢ rank (UFModel.push m k (_ : n ≤ k)) ↑i = rank m ↑{ val := ↑i, isLt := h }

Next tactic:
simp [UFModel.push, h, lt_irrefl]
theorem push {arr : Array (UFNode α)} {n} {m : UFModel n} (H : m.Models arr)
    (k) (hk : k = n + 1) (x) :
    (m.push k (hk ▸ Nat.le_add_right ..)).Models (arr.push ⟨n, x, 0⟩) := by
  apply H.imp <;>
  · intro H
    refine H.push _ hk _ _ (fun i h ↦ ?_) (fun h ↦ ?_) <;>
    

Proof state:
case g.refine_2
α : Type u_1
arr : Array (UFNode α)
n : ℕ
m : UFModel n
H✝ : Models arr m
k : ℕ
hk : k = n + 1
x : α
H : Agrees arr (fun x => x.rank) fun i => rank m ↑i
h : n < k
⊢ { parent := n, value := x, rank := 0 }.rank = rank (UFModel.push m k (_ : n ≤ k)) ↑{ val := n, isLt := h }

Next tactic:
simp [UFModel.push, h, lt_irrefl]
theorem setParent {arr : Array (UFNode α)} {n} {m : UFModel n} (hm : m.Models arr)
    (i j H hi x) (hp : x.parent = j.1) (hrk : x.rank = arr[i].rank) :
    (m.setParent i j H).Models (arr.set ⟨i.1, hi⟩ x) :=
  ⟨hm.1.set
      (fun k (h : (k:ℕ) ≠ i) ↦ by 

Proof state:
α : Type u_1
arr : Array (UFNode α)
n : ℕ
m : UFModel n
hm : Models arr m
i j : Fin n
H : rank m ↑i < rank m ↑j
hi : ↑i < Array.size arr
x : UFNode α
hp : x.parent = ↑j
hrk : x.rank = arr[i].rank
k : Fin n
h : ↑k ≠ ↑i
⊢ ↑(parent (UFModel.setParent m i j H) k) = ↑(parent m k)

Next tactic:
simp [UFModel.setParent, h.symm]
theorem setParent {arr : Array (UFNode α)} {n} {m : UFModel n} (hm : m.Models arr)
    (i j H hi x) (hp : x.parent = j.1) (hrk : x.rank = arr[i].rank) :
    (m.setParent i j H).Models (arr.set ⟨i.1, hi⟩ x) :=
  ⟨hm.1.set
      (fun k (h : (k:ℕ) ≠ i) ↦ by simp [UFModel.setParent, h.symm])
      (fun _ ↦ by 

Proof state:
α : Type u_1
arr : Array (UFNode α)
n : ℕ
m : UFModel n
hm : Models arr m
i j : Fin n
H : rank m ↑i < rank m ↑j
hi : ↑i < Array.size arr
x : UFNode α
hp : x.parent = ↑j
hrk : x.rank = arr[i].rank
x✝ : ↑{ val := ↑i, isLt := hi } < n
⊢ x.parent = ↑(parent (UFModel.setParent m i j H) { val := ↑{ val := ↑i, isLt := hi }, isLt := x✝ })

Next tactic:
simp [UFModel.setParent, hp]
theorem model' (self : UnionFind α) : ∃ (m : UFModel self.arr.size), m.Models self.arr := by
  

Proof state:
α : Type u_1
self : UnionFind α
⊢ ∃ m, UFModel.Models self.arr m

Next tactic:
let ⟨n, m, hm⟩ := self.model
theorem model' (self : UnionFind α) : ∃ (m : UFModel self.arr.size), m.Models self.arr := by
  let ⟨n, m, hm⟩ := self.model; 

Proof state:
α : Type u_1
self : UnionFind α
n : ℕ
m : UFModel n
hm : UFModel.Models self.arr m
⊢ ∃ m, UFModel.Models self.arr m

Next tactic:
cases hm.size_eq
theorem model' (self : UnionFind α) : ∃ (m : UFModel self.arr.size), m.Models self.arr := by
  let ⟨n, m, hm⟩ := self.model; cases hm.size_eq; 

Proof state:
case refl
α : Type u_1
self : UnionFind α
m : UFModel (Array.size self.arr)
hm : UFModel.Models self.arr m
⊢ ∃ m, UFModel.Models self.arr m

Next tactic:
exact ⟨m, hm⟩
def rankMaxAux (self : UnionFind α) : ∀ (i : Nat),
    {k : Nat // ∀ j < i, ∀ h, (self.arr.get ⟨j, h⟩).rank ≤ k}
  | 0 => ⟨0, λ.⟩
  | i+1 => by
    

Proof state:
α : Type ?u.46743
self : UnionFind α
i : ℕ
⊢ { k // ∀ (j : ℕ), j < i + 1 → ∀ (h : j < Array.size self.arr), (Array.get self.arr { val := j, isLt := h }).rank ≤ k }

Next tactic:
let ⟨k, H⟩ := rankMaxAux self i
def rankMaxAux (self : UnionFind α) : ∀ (i : Nat),
    {k : Nat // ∀ j < i, ∀ h, (self.arr.get ⟨j, h⟩).rank ≤ k}
  | 0 => ⟨0, λ.⟩
  | i+1 => by
    let ⟨k, H⟩ := rankMaxAux self i
    

Proof state:
α : Type ?u.46743
self : UnionFind α
i k : ℕ
H : ∀ (j : ℕ), j < i → ∀ (h : j < Array.size self.arr), (Array.get self.arr { val := j, isLt := h }).rank ≤ k
⊢ { k // ∀ (j : ℕ), j < i + 1 → ∀ (h : j < Array.size self.arr), (Array.get self.arr { val := j, isLt := h }).rank ≤ k }

Next tactic:
refine ⟨max k (if h : _ then (self.arr.get ⟨i, h⟩).rank else 0), fun j hj h ↦ ?_⟩
def rankMaxAux (self : UnionFind α) : ∀ (i : Nat),
    {k : Nat // ∀ j < i, ∀ h, (self.arr.get ⟨j, h⟩).rank ≤ k}
  | 0 => ⟨0, λ.⟩
  | i+1 => by
    let ⟨k, H⟩ := rankMaxAux self i
    refine ⟨max k (if h : _ then (self.arr.get ⟨i, h⟩).rank else 0), fun j hj h ↦ ?_⟩
    

Proof state:
α : Type ?u.46743
self : UnionFind α
i k : ℕ
H : ∀ (j : ℕ), j < i → ∀ (h : j < Array.size self.arr), (Array.get self.arr { val := j, isLt := h }).rank ≤ k
j : ℕ
hj : j < i + 1
h : j < Array.size self.arr
⊢ (Array.get self.arr { val := j, isLt := h }).rank ≤
    max k (if h : i < Array.size self.arr then (Array.get self.arr { val := i, isLt := h }).rank else 0)

Next tactic:
match j, lt_or_eq_of_le (Nat.le_of_lt_succ hj) with
    | j, Or.inl hj => exact le_trans (H _ hj h) (le_max_left _ _)
    | _, Or.inr rfl => simp [h, le_max_right]
def rankMaxAux (self : UnionFind α) : ∀ (i : Nat),
    {k : Nat // ∀ j < i, ∀ h, (self.arr.get ⟨j, h⟩).rank ≤ k}
  | 0 => ⟨0, λ.⟩
  | i+1 => by
    let ⟨k, H⟩ := rankMaxAux self i
    refine ⟨max k (if h : _ then (self.arr.get ⟨i, h⟩).rank else 0), fun j hj h ↦ ?_⟩
    match j, lt_or_eq_of_le (Nat.le_of_lt_succ hj) with
    | j, Or.inl hj => 

Proof state:
α : Type ?u.46743
self : UnionFind α
i k : ℕ
H : ∀ (j : ℕ), j < i → ∀ (h : j < Array.size self.arr), (Array.get self.arr { val := j, isLt := h }).rank ≤ k
j✝ j : ℕ
hj : j < i
hj✝ : j < i + 1
h : j < Array.size self.arr
⊢ (Array.get self.arr { val := j, isLt := h }).rank ≤
    max k (if h : i < Array.size self.arr then (Array.get self.arr { val := i, isLt := h }).rank else 0)

Next tactic:
exact le_trans (H _ hj h) (le_max_left _ _)
def rankMaxAux (self : UnionFind α) : ∀ (i : Nat),
    {k : Nat // ∀ j < i, ∀ h, (self.arr.get ⟨j, h⟩).rank ≤ k}
  | 0 => ⟨0, λ.⟩
  | i+1 => by
    let ⟨k, H⟩ := rankMaxAux self i
    refine ⟨max k (if h : _ then (self.arr.get ⟨i, h⟩).rank else 0), fun j hj h ↦ ?_⟩
    match j, lt_or_eq_of_le (Nat.le_of_lt_succ hj) with
    | j, Or.inl hj => exact le_trans (H _ hj h) (le_max_left _ _)
    | _, Or.inr rfl => 

Proof state:
α : Type ?u.46743
self : UnionFind α
i k : ℕ
H : ∀ (j : ℕ), j < i → ∀ (h : j < Array.size self.arr), (Array.get self.arr { val := j, isLt := h }).rank ≤ k
j : ℕ
hj : i < i + 1
h : i < Array.size self.arr
⊢ (Array.get self.arr { val := i, isLt := h }).rank ≤
    max k (if h : i < Array.size self.arr then (Array.get self.arr { val := i, isLt := h }).rank else 0)

Next tactic:
simp [h, le_max_right]
theorem lt_rankMax (self : UnionFind α) (i : Nat) : self.rank i < self.rankMax := by
  

Proof state:
α : Type u_1
self : UnionFind α
i : ℕ
⊢ rank self i < rankMax self

Next tactic:
simp [rank]
theorem lt_rankMax (self : UnionFind α) (i : Nat) : self.rank i < self.rankMax := by
  simp [rank]; 

Proof state:
α : Type u_1
self : UnionFind α
i : ℕ
⊢ (if h : i < size self then self.arr[i].rank else 0) < rankMax self

Next tactic:
split
theorem lt_rankMax (self : UnionFind α) (i : Nat) : self.rank i < self.rankMax := by
  simp [rank]; split; 

Proof state:
case inl
α : Type u_1
self : UnionFind α
i : ℕ
h✝ : i < size self
⊢ self.arr[i].rank < rankMax self
case inr α : Type u_1 self : UnionFind α i : ℕ h✝ : ¬i < size self ⊢ 0 < rankMax self

Next tactic:
{apply lt_rankMax'}
theorem lt_rankMax (self : UnionFind α) (i : Nat) : self.rank i < self.rankMax := by
  simp [rank]; split; {

Proof state:
case inl
α : Type u_1
self : UnionFind α
i : ℕ
h✝ : i < size self
⊢ self.arr[i].rank < rankMax self

Next tactic:
apply lt_rankMax'
theorem lt_rankMax (self : UnionFind α) (i : Nat) : self.rank i < self.rankMax := by
  simp [rank]; split; {apply lt_rankMax'}; 

Proof state:
case inr
α : Type u_1
self : UnionFind α
i : ℕ
h✝ : ¬i < size self
⊢ 0 < rankMax self

Next tactic:
apply Nat.succ_pos
theorem rank_eq (self : UnionFind α) {n} {m : UFModel n} (H : m.Models self.arr)
    {i} (h : i < self.size) : self.rank i = m.rank i := by
  

Proof state:
α : Type u_1
self : UnionFind α
n : ℕ
m : UFModel n
H : UFModel.Models self.arr m
i : ℕ
h : i < size self
⊢ rank self i = UFModel.rank m i

Next tactic:
simp [rank, h, H.rank_eq]
theorem rank_lt (self : UnionFind α) {i : Nat} (h) : self.arr[i].parent ≠ i →
    self.rank i < self.rank self.arr[i].parent := by
  

Proof state:
α : Type u_1
self : UnionFind α
i : ℕ
h : i < Array.size self.arr
⊢ self.arr[i].parent ≠ i → rank self i < rank self self.arr[i].parent

Next tactic:
let ⟨m, hm⟩ := self.model'
theorem rank_lt (self : UnionFind α) {i : Nat} (h) : self.arr[i].parent ≠ i →
    self.rank i < self.rank self.arr[i].parent := by
  let ⟨m, hm⟩ := self.model'
  

Proof state:
α : Type u_1
self : UnionFind α
i : ℕ
h : i < Array.size self.arr
m : UFModel (Array.size self.arr)
hm : UFModel.Models self.arr m
⊢ self.arr[i].parent ≠ i → rank self i < rank self self.arr[i].parent

Next tactic:
simpa [hm.parent_eq, hm.rank_eq, rank, size, h, (m.parent ⟨i, h⟩).2] using m.rank_lt ⟨i, h⟩
theorem parent_lt (self : UnionFind α) (i : Nat) (h) : self.arr[i].parent < self.size := by
  

Proof state:
α : Type u_1
self : UnionFind α
i : ℕ
h : i < Array.size self.arr
⊢ self.arr[i].parent < size self

Next tactic:
let ⟨m, hm⟩ := self.model'
theorem parent_lt (self : UnionFind α) (i : Nat) (h) : self.arr[i].parent < self.size := by
  let ⟨m, hm⟩ := self.model'
  

Proof state:
α : Type u_1
self : UnionFind α
i : ℕ
h : i < Array.size self.arr
m : UFModel (Array.size self.arr)
hm : UFModel.Models self.arr m
⊢ self.arr[i].parent < size self

Next tactic:
simp [hm.parent_eq, size, (m.parent ⟨i, h⟩).2, h]
def findAux (self : UnionFind α) (x : Fin self.size) :
    (s : Array (UFNode α)) ×' (root : Fin s.size) ×'
    ∃ n, ∃ (m : UFModel n) (m' : UFModel n),
      m.Models self.arr ∧ m'.Models s ∧ m'.rank = m.rank ∧
      (∃ hr, (m'.parent ⟨root, hr⟩).1 = root) ∧
      m.rank x ≤ m.rank root := by
  

Proof state:
α : Type ?u.57384
self : UnionFind α
x : Fin (size self)
⊢ (s : Array (UFNode α)) ×'
    (root : Fin (Array.size s)) ×'
      ∃ n m m',
        UFModel.Models self.arr m ∧
          UFModel.Models s m' ∧
            m'.rank = m.rank ∧
              (∃ hr, ↑(UFModel.parent m' { val := ↑root, isLt := hr }) = ↑root) ∧
                UFModel.rank m ↑x ≤ UFModel.rank m ↑root

Next tactic:
let y := self.arr[x].parent
def findAux (self : UnionFind α) (x : Fin self.size) :
    (s : Array (UFNode α)) ×' (root : Fin s.size) ×'
    ∃ n, ∃ (m : UFModel n) (m' : UFModel n),
      m.Models self.arr ∧ m'.Models s ∧ m'.rank = m.rank ∧
      (∃ hr, (m'.parent ⟨root, hr⟩).1 = root) ∧
      m.rank x ≤ m.rank root := by
  let y := self.arr[x].parent
  

Proof state:
α : Type ?u.57384
self : UnionFind α
x : Fin (size self)
y : ℕ := self.arr[x].parent
⊢ (s : Array (UFNode α)) ×'
    (root : Fin (Array.size s)) ×'
      ∃ n m m',
        UFModel.Models self.arr m ∧
          UFModel.Models s m' ∧
            m'.rank = m.rank ∧
              (∃ hr, ↑(UFModel.parent m' { val := ↑root, isLt := hr }) = ↑root) ∧
                UFModel.rank m ↑x ≤ UFModel.rank m ↑root

Next tactic:
refine if h : y = x then ⟨self.arr, x, ?a'⟩ else
    have := Nat.sub_lt_sub_left (self.lt_rankMax x) (self.rank_lt _ h)
    let ⟨arr₁, root, H⟩ := self.findAux ⟨y, self.parent_lt _ x.2⟩
    have hx := ?hx
    let arr₂ := arr₁.set ⟨x, hx⟩ {arr₁.get ⟨x, hx⟩ with parent := root}
    ⟨arr₂, ⟨root, by simp [root.2]⟩, ?b'⟩