l3lab/ntp-mathlib huggingface dataset:
def find (self : UnionFind α) (x : Fin self.size) :
    (s : UnionFind α) × (root : Fin s.size) ×'
    s.size = self.size ∧ (s.arr.get root).parent = root :=
  let ⟨s, root, H⟩ := self.findAux x
  have : _ ∧ s.size = self.size ∧ s[root.1].parent = root :=
    let ⟨n, _, m', hm, hm', _, ⟨_, hr⟩, _⟩ := H
    ⟨⟨n, m', hm'⟩, hm'.size_eq.symm.trans hm.size_eq, by 

Proof state:
α : Type ?u.82972
self : UnionFind α
x : Fin (size self)
s : Array (UFNode α)
root : Fin (Array.size s)
H :
  ∃ 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
n : ℕ
w✝¹ m' : UFModel n
hm : UFModel.Models self.arr w✝¹
hm' : UFModel.Models s m'
left✝ : m'.rank = w✝¹.rank
w✝ : ↑root < n
hr : ↑(UFModel.parent m' { val := ↑root, isLt := w✝ }) = ↑root
right✝ : UFModel.rank w✝¹ ↑x ≤ UFModel.rank w✝¹ ↑root
⊢ s[↑root].parent = ↑root

Next tactic:
rwa [hm'.parent_eq]
def link (self : UnionFind α) (x y : Fin self.size)
    (yroot : (self.arr.get y).parent = y) : UnionFind α := by
  

Proof state:
α : Type ?u.85186
self : UnionFind α
x y : Fin (size self)
yroot : (Array.get self.arr y).parent = ↑y
⊢ UnionFind α

Next tactic:
refine if ne : x.1 = y then self else
    let nx := self.arr[x]
    let ny := self.arr[y]
    if h : ny.rank < nx.rank then
      ⟨self.arr.set y {ny with parent := x}, ?a⟩
    else
      let arr₁ := self.arr.set x {nx with parent := y}
      let arr₂ := if nx.rank = ny.rank then
        arr₁.set ⟨y, by simp; exact y.2⟩ {ny with rank := ny.rank + 1}
      else arr₁
      ⟨arr₂, ?b⟩
def link (self : UnionFind α) (x y : Fin self.size)
    (yroot : (self.arr.get y).parent = y) : UnionFind α := by
  refine if ne : x.1 = y then self else
    let nx := self.arr[x]
    let ny := self.arr[y]
    if h : ny.rank < nx.rank then
      ⟨self.arr.set y {ny with parent := x}, ?a⟩
    else
      let arr₁ := self.arr.set x {nx with parent := y}
      let arr₂ := if nx.rank = ny.rank then
        arr₁.set ⟨y, by 

Proof state:
α : Type ?u.85186
self : UnionFind α
x y : Fin (size self)
yroot : (Array.get self.arr y).parent = ↑y
ne : ¬↑x = ↑y
nx : UFNode α := self.arr[x]
ny : UFNode α := self.arr[y]
h : ¬ny.rank < nx.rank
arr₁ : Array (UFNode α) := Array.set self.arr x { parent := ↑y, value := nx.value, rank := nx.rank }
⊢ ↑y < Array.size arr₁

Next tactic:
simp
def link (self : UnionFind α) (x y : Fin self.size)
    (yroot : (self.arr.get y).parent = y) : UnionFind α := by
  refine if ne : x.1 = y then self else
    let nx := self.arr[x]
    let ny := self.arr[y]
    if h : ny.rank < nx.rank then
      ⟨self.arr.set y {ny with parent := x}, ?a⟩
    else
      let arr₁ := self.arr.set x {nx with parent := y}
      let arr₂ := if nx.rank = ny.rank then
        arr₁.set ⟨y, by simp; 

Proof state:
α : Type ?u.85186
self : UnionFind α
x y : Fin (size self)
yroot : (Array.get self.arr y).parent = ↑y
ne : ¬↑x = ↑y
nx : UFNode α := self.arr[x]
ny : UFNode α := self.arr[y]
h : ¬ny.rank < nx.rank
arr₁ : Array (UFNode α) := Array.set self.arr x { parent := ↑y, value := nx.value, rank := nx.rank }
⊢ ↑y < Array.size self.arr

Next tactic:
exact y.2
def link (self : UnionFind α) (x y : Fin self.size)
    (yroot : (self.arr.get y).parent = y) : UnionFind α := by
  refine if ne : x.1 = y then self else
    let nx := self.arr[x]
    let ny := self.arr[y]
    if h : ny.rank < nx.rank then
      ⟨self.arr.set y {ny with parent := x}, ?a⟩
    else
      let arr₁ := self.arr.set x {nx with parent := y}
      let arr₂ := if nx.rank = ny.rank then
        arr₁.set ⟨y, by simp; exact y.2⟩ {ny with rank := ny.rank + 1}
      else arr₁
      ⟨arr₂, ?b⟩
  -- start proof
  

Proof state:
case a
α : Type ?u.85186
self : UnionFind α
x y : Fin (size self)
yroot : (Array.get self.arr y).parent = ↑y
ne : ¬↑x = ↑y
nx : UFNode α := self.arr[x]
ny : UFNode α := self.arr[y]
h : ny.rank < nx.rank
⊢ ∃ n m, UFModel.Models (Array.set self.arr y { parent := ↑x, value := ny.value, rank := ny.rank }) m
case b
α : Type ?u.85186
self : UnionFind α
x y : Fin (size self)
yroot : (Array.get self.arr y).parent = ↑y
ne : ¬↑x = ↑y
nx : UFNode α := self.arr[x]
ny : UFNode α := self.arr[y]
h : ¬ny.rank < nx.rank
arr₁ : Array (UFNode α) := Array.set self.arr x { parent := ↑y, value := nx.value, rank := nx.rank }
arr₂ : Array (UFNode α) :=
  if nx.rank = ny.rank then
    Array.set arr₁ { val := ↑y, isLt := (_ : ↑y < Array.size arr₁) }
      { parent := ny.parent, value := ny.value, rank := ny.rank + 1 }
  else arr₁
⊢ ∃ n m, UFModel.Models arr₂ m

Next tactic:
case a =>
    let ⟨m, hm⟩ := self.model'
    exact ⟨_, _, hm.setParent y x (by simpa [hm.rank_eq] using h) _ _ rfl rfl⟩
def link (self : UnionFind α) (x y : Fin self.size)
    (yroot : (self.arr.get y).parent = y) : UnionFind α := by
  refine if ne : x.1 = y then self else
    let nx := self.arr[x]
    let ny := self.arr[y]
    if h : ny.rank < nx.rank then
      ⟨self.arr.set y {ny with parent := x}, ?a⟩
    else
      let arr₁ := self.arr.set x {nx with parent := y}
      let arr₂ := if nx.rank = ny.rank then
        arr₁.set ⟨y, by simp; exact y.2⟩ {ny with rank := ny.rank + 1}
      else arr₁
      ⟨arr₂, ?b⟩
  -- start proof
  

Proof state:
α : Type ?u.85186
self : UnionFind α
x y : Fin (size self)
yroot : (Array.get self.arr y).parent = ↑y
ne : ¬↑x = ↑y
nx : UFNode α := self.arr[x]
ny : UFNode α := self.arr[y]
h : ny.rank < nx.rank
⊢ ∃ n m, UFModel.Models (Array.set self.arr y { parent := ↑x, value := ny.value, rank := ny.rank }) m

Next tactic:
case a =>
    let ⟨m, hm⟩ := self.model'
    exact ⟨_, _, hm.setParent y x (by simpa [hm.rank_eq] using h) _ _ rfl rfl⟩
def link (self : UnionFind α) (x y : Fin self.size)
    (yroot : (self.arr.get y).parent = y) : UnionFind α := by
  refine if ne : x.1 = y then self else
    let nx := self.arr[x]
    let ny := self.arr[y]
    if h : ny.rank < nx.rank then
      ⟨self.arr.set y {ny with parent := x}, ?a⟩
    else
      let arr₁ := self.arr.set x {nx with parent := y}
      let arr₂ := if nx.rank = ny.rank then
        arr₁.set ⟨y, by simp; exact y.2⟩ {ny with rank := ny.rank + 1}
      else arr₁
      ⟨arr₂, ?b⟩
  -- start proof
  case a =>
    

Proof state:
α : Type ?u.85186
self : UnionFind α
x y : Fin (size self)
yroot : (Array.get self.arr y).parent = ↑y
ne : ¬↑x = ↑y
nx : UFNode α := self.arr[x]
ny : UFNode α := self.arr[y]
h : ny.rank < nx.rank
⊢ ∃ n m, UFModel.Models (Array.set self.arr y { parent := ↑x, value := ny.value, rank := ny.rank }) m

Next tactic:
let ⟨m, hm⟩ := self.model'
def link (self : UnionFind α) (x y : Fin self.size)
    (yroot : (self.arr.get y).parent = y) : UnionFind α := by
  refine if ne : x.1 = y then self else
    let nx := self.arr[x]
    let ny := self.arr[y]
    if h : ny.rank < nx.rank then
      ⟨self.arr.set y {ny with parent := x}, ?a⟩
    else
      let arr₁ := self.arr.set x {nx with parent := y}
      let arr₂ := if nx.rank = ny.rank then
        arr₁.set ⟨y, by simp; exact y.2⟩ {ny with rank := ny.rank + 1}
      else arr₁
      ⟨arr₂, ?b⟩
  -- start proof
  case a =>
    let ⟨m, hm⟩ := self.model'
    

Proof state:
α : Type ?u.85186
self : UnionFind α
x y : Fin (size self)
yroot : (Array.get self.arr y).parent = ↑y
ne : ¬↑x = ↑y
nx : UFNode α := self.arr[x]
ny : UFNode α := self.arr[y]
h : ny.rank < nx.rank
m : UFModel (Array.size self.arr)
hm : UFModel.Models self.arr m
⊢ ∃ n m, UFModel.Models (Array.set self.arr y { parent := ↑x, value := ny.value, rank := ny.rank }) m

Next tactic:
exact ⟨_, _, hm.setParent y x (by simpa [hm.rank_eq] using h) _ _ rfl rfl⟩
def link (self : UnionFind α) (x y : Fin self.size)
    (yroot : (self.arr.get y).parent = y) : UnionFind α := by
  refine if ne : x.1 = y then self else
    let nx := self.arr[x]
    let ny := self.arr[y]
    if h : ny.rank < nx.rank then
      ⟨self.arr.set y {ny with parent := x}, ?a⟩
    else
      let arr₁ := self.arr.set x {nx with parent := y}
      let arr₂ := if nx.rank = ny.rank then
        arr₁.set ⟨y, by simp; exact y.2⟩ {ny with rank := ny.rank + 1}
      else arr₁
      ⟨arr₂, ?b⟩
  -- start proof
  case a =>
    let ⟨m, hm⟩ := self.model'
    exact ⟨_, _, hm.setParent y x (by 

Proof state:
α : Type ?u.85186
self : UnionFind α
x y : Fin (size self)
yroot : (Array.get self.arr y).parent = ↑y
ne : ¬↑x = ↑y
nx : UFNode α := self.arr[x]
ny : UFNode α := self.arr[y]
h : ny.rank < nx.rank
m : UFModel (Array.size self.arr)
hm : UFModel.Models self.arr m
⊢ UFModel.rank m ↑y < UFModel.rank m ↑x

Next tactic:
simpa [hm.rank_eq] using h
def link (self : UnionFind α) (x y : Fin self.size)
    (yroot : (self.arr.get y).parent = y) : UnionFind α := by
  refine if ne : x.1 = y then self else
    let nx := self.arr[x]
    let ny := self.arr[y]
    if h : ny.rank < nx.rank then
      ⟨self.arr.set y {ny with parent := x}, ?a⟩
    else
      let arr₁ := self.arr.set x {nx with parent := y}
      let arr₂ := if nx.rank = ny.rank then
        arr₁.set ⟨y, by simp; exact y.2⟩ {ny with rank := ny.rank + 1}
      else arr₁
      ⟨arr₂, ?b⟩
  -- start proof
  case a =>
    let ⟨m, hm⟩ := self.model'
    exact ⟨_, _, hm.setParent y x (by simpa [hm.rank_eq] using h) _ _ rfl rfl⟩
  

Proof state:
case b
α : Type ?u.85186
self : UnionFind α
x y : Fin (size self)
yroot : (Array.get self.arr y).parent = ↑y
ne : ¬↑x = ↑y
nx : UFNode α := self.arr[x]
ny : UFNode α := self.arr[y]
h : ¬ny.rank < nx.rank
arr₁ : Array (UFNode α) := Array.set self.arr x { parent := ↑y, value := nx.value, rank := nx.rank }
arr₂ : Array (UFNode α) :=
  if nx.rank = ny.rank then
    Array.set arr₁ { val := ↑y, isLt := (_ : ↑y < Array.size arr₁) }
      { parent := ny.parent, value := ny.value, rank := ny.rank + 1 }
  else arr₁
⊢ ∃ n m, UFModel.Models arr₂ m

Next tactic:
case b =>
    let ⟨m, hm⟩ := self.model'; let n := self.size
    refine ⟨_, m.setParentBump x y (by simpa [hm.rank_eq] using h)
      (by simpa [← hm.parent_eq'] using yroot), ?_⟩
    let parent (i : Fin n) := (if x.1 = i then y else m.parent i).1
    have : UFModel.Agrees arr₁ (·.parent) parent :=
      hm.1.set (fun i h ↦ by simp; rw [if_neg h.symm]) (fun _ ↦ by simp)
    have H1 : UFModel.Agrees arr₂ (·.parent) parent := by
      simp; split
      · exact this.set (fun i h ↦ by simp [h.symm]) (fun h ↦ by simp [ne, hm.parent_eq'])
      · exact this
    have : UFModel.Agrees arr₁ (·.rank) (fun i : Fin n ↦ m.rank i) :=
      hm.2.set (fun i _ ↦ by simp) (fun _ ↦ by simp [hm.rank_eq])
    let rank (i : Fin n) := if y.1 = i ∧ m.rank x = m.rank y then m.rank y + 1 else m.rank i
    have H2 : UFModel.Agrees arr₂ (·.rank) rank := by
      simp; split <;> (rename_i xy; simp [hm.rank_eq] at xy; simp [xy])
      · exact this.set (fun i h ↦ by rw [if_neg h.symm]) (fun h ↦ by simp [hm.rank_eq])
      · exact this
    exact ⟨H1, H2⟩
def link (self : UnionFind α) (x y : Fin self.size)
    (yroot : (self.arr.get y).parent = y) : UnionFind α := by
  refine if ne : x.1 = y then self else
    let nx := self.arr[x]
    let ny := self.arr[y]
    if h : ny.rank < nx.rank then
      ⟨self.arr.set y {ny with parent := x}, ?a⟩
    else
      let arr₁ := self.arr.set x {nx with parent := y}
      let arr₂ := if nx.rank = ny.rank then
        arr₁.set ⟨y, by simp; exact y.2⟩ {ny with rank := ny.rank + 1}
      else arr₁
      ⟨arr₂, ?b⟩
  -- start proof
  case a =>
    let ⟨m, hm⟩ := self.model'
    exact ⟨_, _, hm.setParent y x (by simpa [hm.rank_eq] using h) _ _ rfl rfl⟩
  

Proof state:
α : Type ?u.85186
self : UnionFind α
x y : Fin (size self)
yroot : (Array.get self.arr y).parent = ↑y
ne : ¬↑x = ↑y
nx : UFNode α := self.arr[x]
ny : UFNode α := self.arr[y]
h : ¬ny.rank < nx.rank
arr₁ : Array (UFNode α) := Array.set self.arr x { parent := ↑y, value := nx.value, rank := nx.rank }
arr₂ : Array (UFNode α) :=
  if nx.rank = ny.rank then
    Array.set arr₁ { val := ↑y, isLt := (_ : ↑y < Array.size arr₁) }
      { parent := ny.parent, value := ny.value, rank := ny.rank + 1 }
  else arr₁
⊢ ∃ n m, UFModel.Models arr₂ m

Next tactic:
case b =>
    let ⟨m, hm⟩ := self.model'; let n := self.size
    refine ⟨_, m.setParentBump x y (by simpa [hm.rank_eq] using h)
      (by simpa [← hm.parent_eq'] using yroot), ?_⟩
    let parent (i : Fin n) := (if x.1 = i then y else m.parent i).1
    have : UFModel.Agrees arr₁ (·.parent) parent :=
      hm.1.set (fun i h ↦ by simp; rw [if_neg h.symm]) (fun _ ↦ by simp)
    have H1 : UFModel.Agrees arr₂ (·.parent) parent := by
      simp; split
      · exact this.set (fun i h ↦ by simp [h.symm]) (fun h ↦ by simp [ne, hm.parent_eq'])
      · exact this
    have : UFModel.Agrees arr₁ (·.rank) (fun i : Fin n ↦ m.rank i) :=
      hm.2.set (fun i _ ↦ by simp) (fun _ ↦ by simp [hm.rank_eq])
    let rank (i : Fin n) := if y.1 = i ∧ m.rank x = m.rank y then m.rank y + 1 else m.rank i
    have H2 : UFModel.Agrees arr₂ (·.rank) rank := by
      simp; split <;> (rename_i xy; simp [hm.rank_eq] at xy; simp [xy])
      · exact this.set (fun i h ↦ by rw [if_neg h.symm]) (fun h ↦ by simp [hm.rank_eq])
      · exact this
    exact ⟨H1, H2⟩
def link (self : UnionFind α) (x y : Fin self.size)
    (yroot : (self.arr.get y).parent = y) : UnionFind α := by
  refine if ne : x.1 = y then self else
    let nx := self.arr[x]
    let ny := self.arr[y]
    if h : ny.rank < nx.rank then
      ⟨self.arr.set y {ny with parent := x}, ?a⟩
    else
      let arr₁ := self.arr.set x {nx with parent := y}
      let arr₂ := if nx.rank = ny.rank then
        arr₁.set ⟨y, by simp; exact y.2⟩ {ny with rank := ny.rank + 1}
      else arr₁
      ⟨arr₂, ?b⟩
  -- start proof
  case a =>
    let ⟨m, hm⟩ := self.model'
    exact ⟨_, _, hm.setParent y x (by simpa [hm.rank_eq] using h) _ _ rfl rfl⟩
  case b =>
    

Proof state:
α : Type ?u.85186
self : UnionFind α
x y : Fin (size self)
yroot : (Array.get self.arr y).parent = ↑y
ne : ¬↑x = ↑y
nx : UFNode α := self.arr[x]
ny : UFNode α := self.arr[y]
h : ¬ny.rank < nx.rank
arr₁ : Array (UFNode α) := Array.set self.arr x { parent := ↑y, value := nx.value, rank := nx.rank }
arr₂ : Array (UFNode α) :=
  if nx.rank = ny.rank then
    Array.set arr₁ { val := ↑y, isLt := (_ : ↑y < Array.size arr₁) }
      { parent := ny.parent, value := ny.value, rank := ny.rank + 1 }
  else arr₁
⊢ ∃ n m, UFModel.Models arr₂ m

Next tactic:
let ⟨m, hm⟩ := self.model'
def link (self : UnionFind α) (x y : Fin self.size)
    (yroot : (self.arr.get y).parent = y) : UnionFind α := by
  refine if ne : x.1 = y then self else
    let nx := self.arr[x]
    let ny := self.arr[y]
    if h : ny.rank < nx.rank then
      ⟨self.arr.set y {ny with parent := x}, ?a⟩
    else
      let arr₁ := self.arr.set x {nx with parent := y}
      let arr₂ := if nx.rank = ny.rank then
        arr₁.set ⟨y, by simp; exact y.2⟩ {ny with rank := ny.rank + 1}
      else arr₁
      ⟨arr₂, ?b⟩
  -- start proof
  case a =>
    let ⟨m, hm⟩ := self.model'
    exact ⟨_, _, hm.setParent y x (by simpa [hm.rank_eq] using h) _ _ rfl rfl⟩
  case b =>
    let ⟨m, hm⟩ := self.model'; 

Proof state:
α : Type ?u.85186
self : UnionFind α
x y : Fin (size self)
yroot : (Array.get self.arr y).parent = ↑y
ne : ¬↑x = ↑y
nx : UFNode α := self.arr[x]
ny : UFNode α := self.arr[y]
h : ¬ny.rank < nx.rank
arr₁ : Array (UFNode α) := Array.set self.arr x { parent := ↑y, value := nx.value, rank := nx.rank }
arr₂ : Array (UFNode α) :=
  if nx.rank = ny.rank then
    Array.set arr₁ { val := ↑y, isLt := (_ : ↑y < Array.size arr₁) }
      { parent := ny.parent, value := ny.value, rank := ny.rank + 1 }
  else arr₁
m : UFModel (Array.size self.arr)
hm : UFModel.Models self.arr m
⊢ ∃ n m, UFModel.Models arr₂ m

Next tactic:
let n := self.size
def link (self : UnionFind α) (x y : Fin self.size)
    (yroot : (self.arr.get y).parent = y) : UnionFind α := by
  refine if ne : x.1 = y then self else
    let nx := self.arr[x]
    let ny := self.arr[y]
    if h : ny.rank < nx.rank then
      ⟨self.arr.set y {ny with parent := x}, ?a⟩
    else
      let arr₁ := self.arr.set x {nx with parent := y}
      let arr₂ := if nx.rank = ny.rank then
        arr₁.set ⟨y, by simp; exact y.2⟩ {ny with rank := ny.rank + 1}
      else arr₁
      ⟨arr₂, ?b⟩
  -- start proof
  case a =>
    let ⟨m, hm⟩ := self.model'
    exact ⟨_, _, hm.setParent y x (by simpa [hm.rank_eq] using h) _ _ rfl rfl⟩
  case b =>
    let ⟨m, hm⟩ := self.model'; let n := self.size
    

Proof state:
α : Type ?u.85186
self : UnionFind α
x y : Fin (size self)
yroot : (Array.get self.arr y).parent = ↑y
ne : ¬↑x = ↑y
nx : UFNode α := self.arr[x]
ny : UFNode α := self.arr[y]
h : ¬ny.rank < nx.rank
arr₁ : Array (UFNode α) := Array.set self.arr x { parent := ↑y, value := nx.value, rank := nx.rank }
arr₂ : Array (UFNode α) :=
  if nx.rank = ny.rank then
    Array.set arr₁ { val := ↑y, isLt := (_ : ↑y < Array.size arr₁) }
      { parent := ny.parent, value := ny.value, rank := ny.rank + 1 }
  else arr₁
m : UFModel (Array.size self.arr)
hm : UFModel.Models self.arr m
n : ℕ := size self
⊢ ∃ n m, UFModel.Models arr₂ m

Next tactic:
refine ⟨_, m.setParentBump x y (by simpa [hm.rank_eq] using h)
      (by simpa [← hm.parent_eq'] using yroot), ?_⟩
def link (self : UnionFind α) (x y : Fin self.size)
    (yroot : (self.arr.get y).parent = y) : UnionFind α := by
  refine if ne : x.1 = y then self else
    let nx := self.arr[x]
    let ny := self.arr[y]
    if h : ny.rank < nx.rank then
      ⟨self.arr.set y {ny with parent := x}, ?a⟩
    else
      let arr₁ := self.arr.set x {nx with parent := y}
      let arr₂ := if nx.rank = ny.rank then
        arr₁.set ⟨y, by simp; exact y.2⟩ {ny with rank := ny.rank + 1}
      else arr₁
      ⟨arr₂, ?b⟩
  -- start proof
  case a =>
    let ⟨m, hm⟩ := self.model'
    exact ⟨_, _, hm.setParent y x (by simpa [hm.rank_eq] using h) _ _ rfl rfl⟩
  case b =>
    let ⟨m, hm⟩ := self.model'; let n := self.size
    refine ⟨_, m.setParentBump x y (by 

Proof state:
α : Type ?u.85186
self : UnionFind α
x y : Fin (size self)
yroot : (Array.get self.arr y).parent = ↑y
ne : ¬↑x = ↑y
nx : UFNode α := self.arr[x]
ny : UFNode α := self.arr[y]
h : ¬ny.rank < nx.rank
arr₁ : Array (UFNode α) := Array.set self.arr x { parent := ↑y, value := nx.value, rank := nx.rank }
arr₂ : Array (UFNode α) :=
  if nx.rank = ny.rank then
    Array.set arr₁ { val := ↑y, isLt := (_ : ↑y < Array.size arr₁) }
      { parent := ny.parent, value := ny.value, rank := ny.rank + 1 }
  else arr₁
m : UFModel (Array.size self.arr)
hm : UFModel.Models self.arr m
n : ℕ := size self
⊢ UFModel.rank m ↑x ≤ UFModel.rank m ↑y

Next tactic:
simpa [hm.rank_eq] using h
def link (self : UnionFind α) (x y : Fin self.size)
    (yroot : (self.arr.get y).parent = y) : UnionFind α := by
  refine if ne : x.1 = y then self else
    let nx := self.arr[x]
    let ny := self.arr[y]
    if h : ny.rank < nx.rank then
      ⟨self.arr.set y {ny with parent := x}, ?a⟩
    else
      let arr₁ := self.arr.set x {nx with parent := y}
      let arr₂ := if nx.rank = ny.rank then
        arr₁.set ⟨y, by simp; exact y.2⟩ {ny with rank := ny.rank + 1}
      else arr₁
      ⟨arr₂, ?b⟩
  -- start proof
  case a =>
    let ⟨m, hm⟩ := self.model'
    exact ⟨_, _, hm.setParent y x (by simpa [hm.rank_eq] using h) _ _ rfl rfl⟩
  case b =>
    let ⟨m, hm⟩ := self.model'; let n := self.size
    refine ⟨_, m.setParentBump x y (by simpa [hm.rank_eq] using h)
      (by 

Proof state:
α : Type ?u.85186
self : UnionFind α
x y : Fin (size self)
yroot : (Array.get self.arr y).parent = ↑y
ne : ¬↑x = ↑y
nx : UFNode α := self.arr[x]
ny : UFNode α := self.arr[y]
h : ¬ny.rank < nx.rank
arr₁ : Array (UFNode α) := Array.set self.arr x { parent := ↑y, value := nx.value, rank := nx.rank }
arr₂ : Array (UFNode α) :=
  if nx.rank = ny.rank then
    Array.set arr₁ { val := ↑y, isLt := (_ : ↑y < Array.size arr₁) }
      { parent := ny.parent, value := ny.value, rank := ny.rank + 1 }
  else arr₁
m : UFModel (Array.size self.arr)
hm : UFModel.Models self.arr m
n : ℕ := size self
⊢ ↑(UFModel.parent m y) = ↑y

Next tactic:
simpa [← hm.parent_eq'] using yroot
def link (self : UnionFind α) (x y : Fin self.size)
    (yroot : (self.arr.get y).parent = y) : UnionFind α := by
  refine if ne : x.1 = y then self else
    let nx := self.arr[x]
    let ny := self.arr[y]
    if h : ny.rank < nx.rank then
      ⟨self.arr.set y {ny with parent := x}, ?a⟩
    else
      let arr₁ := self.arr.set x {nx with parent := y}
      let arr₂ := if nx.rank = ny.rank then
        arr₁.set ⟨y, by simp; exact y.2⟩ {ny with rank := ny.rank + 1}
      else arr₁
      ⟨arr₂, ?b⟩
  -- start proof
  case a =>
    let ⟨m, hm⟩ := self.model'
    exact ⟨_, _, hm.setParent y x (by simpa [hm.rank_eq] using h) _ _ rfl rfl⟩
  case b =>
    let ⟨m, hm⟩ := self.model'; let n := self.size
    refine ⟨_, m.setParentBump x y (by simpa [hm.rank_eq] using h)
      (by simpa [← hm.parent_eq'] using yroot), ?_⟩
    

Proof state:
α : Type ?u.85186
self : UnionFind α
x y : Fin (size self)
yroot : (Array.get self.arr y).parent = ↑y
ne : ¬↑x = ↑y
nx : UFNode α := self.arr[x]
ny : UFNode α := self.arr[y]
h : ¬ny.rank < nx.rank
arr₁ : Array (UFNode α) := Array.set self.arr x { parent := ↑y, value := nx.value, rank := nx.rank }
arr₂ : Array (UFNode α) :=
  if nx.rank = ny.rank then
    Array.set arr₁ { val := ↑y, isLt := (_ : ↑y < Array.size arr₁) }
      { parent := ny.parent, value := ny.value, rank := ny.rank + 1 }
  else arr₁
m : UFModel (Array.size self.arr)
hm : UFModel.Models self.arr m
n : ℕ := size self
⊢ UFModel.Models arr₂
    (UFModel.setParentBump m x y (_ : UFModel.rank m ↑x ≤ UFModel.rank m ↑y) (_ : ↑(UFModel.parent m y) = ↑y))

Next tactic:
let parent (i : Fin n) := (if x.1 = i then y else m.parent i).1
def link (self : UnionFind α) (x y : Fin self.size)
    (yroot : (self.arr.get y).parent = y) : UnionFind α := by
  refine if ne : x.1 = y then self else
    let nx := self.arr[x]
    let ny := self.arr[y]
    if h : ny.rank < nx.rank then
      ⟨self.arr.set y {ny with parent := x}, ?a⟩
    else
      let arr₁ := self.arr.set x {nx with parent := y}
      let arr₂ := if nx.rank = ny.rank then
        arr₁.set ⟨y, by simp; exact y.2⟩ {ny with rank := ny.rank + 1}
      else arr₁
      ⟨arr₂, ?b⟩
  -- start proof
  case a =>
    let ⟨m, hm⟩ := self.model'
    exact ⟨_, _, hm.setParent y x (by simpa [hm.rank_eq] using h) _ _ rfl rfl⟩
  case b =>
    let ⟨m, hm⟩ := self.model'; let n := self.size
    refine ⟨_, m.setParentBump x y (by simpa [hm.rank_eq] using h)
      (by simpa [← hm.parent_eq'] using yroot), ?_⟩
    let parent (i : Fin n) := (if x.1 = i then y else m.parent i).1
    

Proof state:
α : Type ?u.85186
self : UnionFind α
x y : Fin (size self)
yroot : (Array.get self.arr y).parent = ↑y
ne : ¬↑x = ↑y
nx : UFNode α := self.arr[x]
ny : UFNode α := self.arr[y]
h : ¬ny.rank < nx.rank
arr₁ : Array (UFNode α) := Array.set self.arr x { parent := ↑y, value := nx.value, rank := nx.rank }
arr₂ : Array (UFNode α) :=
  if nx.rank = ny.rank then
    Array.set arr₁ { val := ↑y, isLt := (_ : ↑y < Array.size arr₁) }
      { parent := ny.parent, value := ny.value, rank := ny.rank + 1 }
  else arr₁
m : UFModel (Array.size self.arr)
hm : UFModel.Models self.arr m
n : ℕ := size self
parent : Fin n → ℕ := fun i => ↑(if ↑x = ↑i then y else UFModel.parent m i)
⊢ UFModel.Models arr₂
    (UFModel.setParentBump m x y (_ : UFModel.rank m ↑x ≤ UFModel.rank m ↑y) (_ : ↑(UFModel.parent m y) = ↑y))

Next tactic:
have : UFModel.Agrees arr₁ (·.parent) parent :=
      hm.1.set (fun i h ↦ by simp; rw [if_neg h.symm]) (fun _ ↦ by simp)