Imports
CLRS Section 6.5 - Priority Queues
This file gives a functional priority-queue interface on top of the Chapter 6 descending-list heap model, then refines the main CLRS array operations for maximum, increase-key, extract-max, and delete. The functional interface remains a compact scaffold; the reader-facing array theorems track parent/child indices, key mutation, bubbling, heap-prefix size, length, and permutation.
Main results:
-
Theorem
heapInsert_orderedDesc: inserting into a heap preserves the heap invariant. -
Theorem
heapInsert_perm: insertion adds exactly the inserted key. -
Theorem
heapIncreaseKey_orderedDesc: increasing one occurrence and rebuilding produces a heap. -
Theorem
heapDelete_orderedDesc: deleting one occurrence and rebuilding produces a heap. -
Theorem
arrayHeapMaximum?_max: the root returned by the array-level maximum operation bounds every key in the heap prefix. -
Theorems
ArrayMaxHeap.set_increased_except_upandArrayMaxHeapExceptUp.bubble_step: the upward-bubbling proof spine for array-levelHEAP-INCREASE-KEY. -
Theorem
arrayHeapIncreaseKey?_state_correct: the full fuelled array-levelHEAP-INCREASE-KEYwrapper writes a larger key, repeatedly bubbles it toward the root, and returns a max-heap with the same backing-list length and swapped multiset. -
Theorem
arrayHeapIncreaseKeyNoBubble?_state_correct: the no-bubble branch of CLRSHEAP-INCREASE-KEYremains as a small readable corollary for the immediate-stop case. -
Theorem
arrayHeapExtractMax?_state_correct: the CLRS array-level extract-max step swaps the root with the last heap cell, shrinks the heap prefix, repairs the new root, and returns a state whose prefix is again a max-heap while the extracted key is the old maximum. -
Theorem
arrayHeapDelete?_state_correct: index-based CLRSHEAP-DELETE, implemented by raising the target cell to the current root maximum and then extracting the maximum, returns a shrunk heap prefix and records the deleted key.
Current gaps:
-
Runtime bounds and RAM semantics are deferred.
namespace CLRSnamespace Chapter06Functional priority-queue operations
Insert a key into the functional max-priority queue.
def heapInsert (x : Nat) (h : List Nat) : List Nat :=
insertDesc x h
Increase one occurrence of old to new, then rebuild the abstract heap.
If old is absent this inserts new; this total behavior avoids exceptions in
the mathematical interface.
def heapIncreaseKey (old new : Nat) (h : List Nat) : List Nat :=
buildMaxHeap (new :: h.erase old)
Delete one occurrence of key, then rebuild the abstract heap.
def heapDelete (key : Nat) (h : List Nat) : List Nat :=
buildMaxHeap (h.erase key)Correctness theorems
Priority-queue insertion preserves the heap invariant.
theorem heapInsert_orderedDesc {x : Nat} {h : List Nat}
(hh : OrderedDesc h) : OrderedDesc (heapInsert x h) :=
Priority-queue insertion adds exactly the inserted key.
theorem heapInsert_perm (x : Nat) (h : List Nat) :
(heapInsert x h).Perm (x :: h) :=
The maximum after insertion is maximal among the old keys and the new key.
theorem heapInsert_max {x m : Nat} {h : List Nat}
(hh : OrderedDesc h) (hmax : heapMaximum? (heapInsert x h) = some m) :
∀ y ∈ x :: h, y ≤ m :=
Increasing a key and rebuilding returns a heap.
theorem heapIncreaseKey_orderedDesc (old new : Nat) (h : List Nat) :
OrderedDesc (heapIncreaseKey old new h) :=
Increasing a key preserves exactly the rebuilt multiset specification.
theorem heapIncreaseKey_perm (old new : Nat) (h : List Nat) :
(heapIncreaseKey old new h).Perm (new :: h.erase old) :=
Deleting one key occurrence and rebuilding returns a heap.
theorem heapDelete_orderedDesc (key : Nat) (h : List Nat) :
OrderedDesc (heapDelete key h) :=
Deleting one key occurrence preserves exactly the rebuilt multiset specification.
theorem heapDelete_perm (key : Nat) (h : List Nat) :
(heapDelete key h).Perm (h.erase key) :=
Array-level maximum operation
Array-level HEAP-MAXIMUM: return the root when the heap prefix is nonempty
and within the backing list.
def arrayHeapMaximum? (a : List Nat) (heapSize : Nat) : Option Nat :=
if h : 0 < heapSize ∧ heapSize ≤ a.length then
some (a[0]'(Nat.lt_of_lt_of_le h.1 h.2))
else
noneThe array-level maximum returned from the root bounds every heap element.
exact hheap.getElem_le_root hi Array-level no-bubble increase-key branch
Reading the cell just written by List.set.
theorem valAt_set_self {a : List Nat} {i x : Nat} (hi : i < a.length) :
valAt (a.set i x) i = x := by
simp [valAt, List.getElem?_set_self hi]
Reading any other cell after List.set.
theorem valAt_set_of_ne {a : List Nat} {i k x : Nat} (hki : k ≠ i) :
valAt (a.set i x) k = valAt a k := by
simp [valAt, show i ≠ k from Ne.symm hki]
For HEAP-INCREASE-KEY, the possible violation moves upward. Unlike
MAX-HEAPIFY, where the possibly bad obligations are the outgoing child
edges of a parent, here the possibly bad obligation is the incoming edge to the
key currently bubbling up.
All heap edges are valid except possibly the edge whose child is
badChild. The extra field says that the bad child's own children are
already bounded by its parent; this is exactly the fact needed after swapping
the bad child with its parent.
structure ArrayMaxHeapExceptUp (a : List Nat) (heapSize badChild : Nat) : Prop where
heapSize_le_length : heapSize ≤ a.length
left_le : ∀ {j : Nat}, j < heapSize → left j < heapSize → left j ≠ badChild →
valAt a (left j) ≤ valAt a j
right_le : ∀ {j : Nat}, j < heapSize → right j < heapSize → right j ≠ badChild →
valAt a (right j) ≤ valAt a j
bad_children_le_parent : 0 < badChild → badChild < heapSize →
(∀ _ : left badChild < heapSize,
valAt a (left badChild) ≤ valAt a (parent badChild)) ∧
(∀ _ : right badChild < heapSize,
valAt a (right badChild) ≤ valAt a (parent badChild))In an upward-exception heap, every non-exempt child is bounded by its parent.
theorem ArrayMaxHeapExceptUp.valAt_le_parent_of_ne {a : List Nat}
{heapSize badChild i : Nat}
(h : ArrayMaxHeapExceptUp a heapSize badChild)
(hi : i < heapSize) (hpos : 0 < i) (hne : i ≠ badChild) :
valAt a i ≤ valAt a (parent i) := by
let p := parent i
have hpheap : p < heapSize := Nat.lt_trans (parent_lt_self hpos) hi
rcases eq_left_or_right_parent hpos with hleft | hright
· have hchild : left p < heapSize := by simpa [p, hleft.symm] using hi
have hchild_ne : left p ≠ badChild := by
simpa [p, hleft.symm] using hne
have hle := h.left_le hpheap hchild hchild_ne
simpa [p, hleft.symm] using hle
· have hchild : right p < heapSize := by simpa [p, hright.symm] using hi
have hchild_ne : right p ≠ badChild := by
simpa [p, hright.symm] using hne
have hle := h.right_le hpheap hchild hchild_ne
simpa [p, hright.symm] using hle If the upward exception is absent or already bounded by its parent, the heap is global.
theorem ArrayMaxHeapExceptUp.to_global {a : List Nat} {heapSize badChild : Nat}
(h : ArrayMaxHeapExceptUp a heapSize badChild)
(hbad : badChild = 0 ∨ valAt a badChild ≤ valAt a (parent badChild)) :
ArrayMaxHeap a heapSize := by
refine ⟨h.heapSize_le_length, ?_, ?_⟩
· intro j hj hl
have hchild_len : left j < a.length := Nat.lt_of_lt_of_le hl h.heapSize_le_length
have hparent_len : j < a.length := Nat.lt_of_lt_of_le hj h.heapSize_le_length
have hval : valAt a (left j) ≤ valAt a j := by
by_cases hchild : left j = badChild
· rcases hbad with hroot | hle
· have hzero : left j = 0 := by simpa [hroot] using hchild
unfold left at hzero
omega
· have hp : parent badChild = j := by
rw [← hchild, parent_left ]
simpa [hchild, hp] using hle
· exact h.left_le hj hl hchild
rw [valAt_eq_getElem a hchild_len, valAt_eq_getElem a hparent_len ] at hval
exact hval
· intro j hj hr
have hchild_len : right j < a.length := Nat.lt_of_lt_of_le hr h.heapSize_le_length
have hparent_len : j < a.length := Nat.lt_of_lt_of_le hj h.heapSize_le_length
have hval : valAt a (right j) ≤ valAt a j := by
by_cases hchild : right j = badChild
· rcases hbad with hroot | hle
· have hzero : right j = 0 := by simpa [hroot] using hchild
unfold right at hzero
omega
· have hp : parent badChild = j := by
rw [← hchild, parent_right ]
simpa [hchild, hp] using hle
· exact h.right_le hj hr hchild
rw [valAt_eq_getElem a hchild_len, valAt_eq_getElem a hparent_len ] at hval
exact hval In a global heap, any positive node is bounded by its parent.
theorem ArrayMaxHeap.valAt_le_parent {a : List Nat} {heapSize i : Nat}
(hheap : ArrayMaxHeap a heapSize) (hi : i < heapSize) (hpos : 0 < i) :
valAt a i ≤ valAt a (parent i) := by
let p := parent i
have hpheap : p < heapSize := Nat.lt_trans (parent_lt_self hpos) hi
have hp_len : p < a.length := Nat.lt_of_lt_of_le hpheap hheap.heapSize_le_length
rcases eq_left_or_right_parent hpos with hleft | hright
· have hchild : left p < heapSize := by simpa [p, hleft.symm] using hi
have hchild_len : left p < a.length :=
Nat.lt_of_lt_of_le hchild hheap.heapSize_le_length
have hle := hheap.left_le hpheap hchild
have hleVal : valAt a (left p) ≤ valAt a p := by
rw [valAt_eq_getElem a hchild_len, valAt_eq_getElem a hp_len ]
exact hle
simpa [p, hleft.symm] using hleVal
· have hchild : right p < heapSize := by simpa [p, hright.symm] using hi
have hchild_len : right p < a.length :=
Nat.lt_of_lt_of_le hchild hheap.heapSize_le_length
have hle := hheap.right_le hpheap hchild
have hleVal : valAt a (right p) ≤ valAt a p := by
rw [valAt_eq_getElem a hchild_len, valAt_eq_getElem a hp_len ]
exact hle
simpa [p, hright.symm] using hleVal
After increasing a key at i, all heap edges are still valid except
possibly the incoming edge to i. This is the invariant entry point for
the CLRS upward bubbling loop.
theorem ArrayMaxHeap.set_increased_except_up {a : List Nat} {heapSize i key : Nat}
(hheap : ArrayMaxHeap a heapSize) (hi : i < heapSize)
(hraise : valAt a i ≤ key) :
ArrayMaxHeapExceptUp (a.set i key) heapSize i := by
have hi_len : i < a.length := Nat.lt_of_lt_of_le hi hheap.heapSize_le_length
have hlen : heapSize ≤ (a.set i key).length := by
simpa [List.length_set] using hheap.heapSize_le_length
have leftVal : ∀ {j : Nat}, j < heapSize → left j < heapSize →
valAt a (left j) ≤ valAt a j := by
intro j hj hl
have hchild_len : left j < a.length :=
Nat.lt_of_lt_of_le hl hheap.heapSize_le_length
have hparent_len : j < a.length :=
Nat.lt_of_lt_of_le hj hheap.heapSize_le_length
have hold := hheap.left_le hj hl
rw [← valAt_eq_getElem a hchild_len,
← valAt_eq_getElem a hparent_len ] at hold
exact hold
have rightVal : ∀ {j : Nat}, j < heapSize → right j < heapSize →
valAt a (right j) ≤ valAt a j := by
intro j hj hr
have hchild_len : right j < a.length :=
Nat.lt_of_lt_of_le hr hheap.heapSize_le_length
have hparent_len : j < a.length :=
Nat.lt_of_lt_of_le hj hheap.heapSize_le_length
have hold := hheap.right_le hj hr
rw [← valAt_eq_getElem a hchild_len,
← valAt_eq_getElem a hparent_len ] at hold
exact hold
refine ⟨hlen, ?_, ?_, ?_⟩
· intro j hj hl hchild_ne
by_cases hparent : j = i
· subst j
rw [valAt_set_of_ne (a := a) (i := i) (k := left i) (x := key)
(left_ne_self i),
valAt_set_self hi_len ]
exact Nat.le_trans (leftVal hi hl) hraise
· rw [valAt_set_of_ne (a := a) (i := i) (k := left j) (x := key) hchild_ne,
valAt_set_of_ne (a := a) (i := i) (k := j) (x := key) hparent ]
exact leftVal hj hl
· intro j hj hr hchild_ne
by_cases hparent : j = i
· subst j
rw [valAt_set_of_ne (a := a) (i := i) (k := right i) (x := key)
(right_ne_self i),
valAt_set_self hi_len ]
exact Nat.le_trans (rightVal hi hr) hraise
· rw [valAt_set_of_ne (a := a) (i := i) (k := right j) (x := key) hchild_ne,
valAt_set_of_ne (a := a) (i := i) (k := j) (x := key) hparent ]
exact rightVal hj hr
· intro hpos _
have hle_parent := hheap.valAt_le_parent hi hpos
have hp_ne : parent i ≠ i := ne_of_lt (parent_lt_self hpos)
constructor
· intro hl
rw [valAt_set_of_ne (a := a) (i := i) (k := left i) (x := key)
(left_ne_self i),
valAt_set_of_ne (a := a) (i := i) (k := parent i) (x := key) hp_ne ]
exact Nat.le_trans (leftVal hi hl) hle_parent
· intro hr
rw [valAt_set_of_ne (a := a) (i := i) (k := right i) (x := key)
(right_ne_self i),
valAt_set_of_ne (a := a) (i := i) (k := parent i) (x := key) hp_ne ]
exact Nat.le_trans (rightVal hi hr) hle_parent
One CLRS upward bubbling swap moves the only possible bad incoming edge from
i to parent i.
theorem ArrayMaxHeapExceptUp.bubble_step {a : List Nat} {heapSize i : Nat}
(h : ArrayMaxHeapExceptUp a heapSize i) (hi : i < heapSize) (hpos : 0 < i)
(hswap : valAt a (parent i) < valAt a i) :
ArrayMaxHeapExceptUp (swapAt a i (parent i)) heapSize (parent i) := by
let p := parent i
have hpheap : p < heapSize := Nat.lt_trans (parent_lt_self hpos) hi
have hpi : p < i := parent_lt_self hpos
have hi_len : i < a.length := Nat.lt_of_lt_of_le hi h.heapSize_le_length
have hp_len : p < a.length := Nat.lt_of_lt_of_le hpheap h.heapSize_le_length
have hlen : heapSize ≤ (swapAt a i p).length := by
simpa [p, swapAt_length] using h.heapSize_le_length
have hp_ne_i : p ≠ i := ne_of_lt hpi
have hchildren := h.bad_children_le_parent hpos hi
have hp_old_le_parent : ∀ (hp_pos : 0 < p), valAt a p ≤ valAt a (parent p) := by
intro hp_pos
exact h.valAt_le_parent_of_ne hpheap hp_pos hp_ne_i
refine ⟨hlen, ?_, ?_, ?_⟩
· intro j hj hl hchild_ne_p
by_cases hchild_i : left j = i
· have hjp : j = p := by
calc
j = parent (left j) := (parent_left j).symm
_ = parent i := by rw [hchild_i ]
_ = p := rfl
rw [hchild_i, hjp ]
rw [valAt_swapAt_left hi_len hp_len, valAt_swapAt_right hi_len hp_len ]
exact Nat.le_of_lt hswap
· by_cases hj_i : j = i
· subst j
have hleft_ne_p : left i ≠ p := by
unfold left p parent
omega
rw [valAt_swapAt_of_ne hi_len hp_len (left_ne_self i) hleft_ne_p,
valAt_swapAt_left hi_len hp_len ]
exact hchildren.1 hl
· by_cases hj_p : j = p
· subst j
rw [valAt_swapAt_of_ne hi_len hp_len hchild_i hchild_ne_p,
valAt_swapAt_right hi_len hp_len ]
have hold := h.left_le hpheap hl hchild_i
exact Nat.le_trans hold (Nat.le_of_lt hswap)
· rw [valAt_swapAt_of_ne hi_len hp_len hchild_i hchild_ne_p,
valAt_swapAt_of_ne hi_len hp_len hj_i hj_p ]
exact h.left_le hj hl hchild_i
· intro j hj hr hchild_ne_p
by_cases hchild_i : right j = i
· have hjp : j = p := by
calc
j = parent (right j) := (parent_right j).symm
_ = parent i := by rw [hchild_i ]
_ = p := rfl
rw [hchild_i, hjp ]
rw [valAt_swapAt_left hi_len hp_len, valAt_swapAt_right hi_len hp_len ]
exact Nat.le_of_lt hswap
· by_cases hj_i : j = i
· subst j
have hright_ne_p : right i ≠ p := by
unfold right p parent
omega
rw [valAt_swapAt_of_ne hi_len hp_len (right_ne_self i) hright_ne_p,
valAt_swapAt_left hi_len hp_len ]
exact hchildren.2 hr
· by_cases hj_p : j = p
· subst j
rw [valAt_swapAt_of_ne hi_len hp_len hchild_i hchild_ne_p,
valAt_swapAt_right hi_len hp_len ]
have hold := h.right_le hpheap hr hchild_i
exact Nat.le_trans hold (Nat.le_of_lt hswap)
· rw [valAt_swapAt_of_ne hi_len hp_len hchild_i hchild_ne_p,
valAt_swapAt_of_ne hi_len hp_len hj_i hj_p ]
exact h.right_le hj hr hchild_i
· intro hp_pos _
have hparentp_ne_i : parent p ≠ i := by
have hlt : parent p < i := Nat.lt_trans (parent_lt_self hp_pos) hpi
exact ne_of_lt hlt
have hparentp_ne_p : parent p ≠ p := ne_of_lt (parent_lt_self hp_pos)
have hp_le_gp := hp_old_le_parent hp_pos
constructor
· intro hl
by_cases hchild_i : left p = i
· rw [hchild_i, valAt_swapAt_left hi_len hp_len ]
rw [valAt_swapAt_of_ne hi_len hp_len hparentp_ne_i hparentp_ne_p ]
exact hp_le_gp
· have hleft_ne_p : left p ≠ p := left_ne_self p
rw [valAt_swapAt_of_ne hi_len hp_len hchild_i hleft_ne_p,
valAt_swapAt_of_ne hi_len hp_len hparentp_ne_i hparentp_ne_p ]
have hold := h.left_le hpheap hl hchild_i
exact Nat.le_trans hold hp_le_gp
· intro hr
by_cases hchild_i : right p = i
· rw [hchild_i, valAt_swapAt_left hi_len hp_len ]
rw [valAt_swapAt_of_ne hi_len hp_len hparentp_ne_i hparentp_ne_p ]
exact hp_le_gp
· have hright_ne_p : right p ≠ p := right_ne_self p
rw [valAt_swapAt_of_ne hi_len hp_len hchild_i hright_ne_p,
valAt_swapAt_of_ne hi_len hp_len hparentp_ne_i hparentp_ne_p ]
have hold := h.right_le hpheap hr hchild_i
exact Nat.le_trans hold hp_le_gp
Fuelled upward bubbling loop for array-level HEAP-INCREASE-KEY. The
fuel is bounded by the starting index, since each swap moves to the strict
parent.
def arrayHeapIncreaseKeyBubbleUpFuel : Nat → List Nat → Nat → Nat → List Nat
| 0, a, _heapSize, _i => a
| fuel + 1, a, heapSize, i =>
if _ : 0 < i then
if valAt a (parent i) < valAt a i then
arrayHeapIncreaseKeyBubbleUpFuel fuel (swapAt a i (parent i)) heapSize (parent i)
else
a
else
aThe upward bubbling loop preserves the backing-list length.
theorem arrayHeapIncreaseKeyBubbleUpFuel_length (fuel : Nat) (a : List Nat)
(heapSize i : Nat) :
(arrayHeapIncreaseKeyBubbleUpFuel fuel a heapSize i).length = a.length := by
induction fuel generalizing a i with
| zero =>
simp [arrayHeapIncreaseKeyBubbleUpFuel]
| succ fuel ih =>
by_cases hpos : 0 < i
· by_cases hswap : valAt a (parent i) < valAt a i
· simp [arrayHeapIncreaseKeyBubbleUpFuel, hpos, hswap,
ih (a := swapAt a i (parent i)) (i := parent i), swapAt_length]
· simp [arrayHeapIncreaseKeyBubbleUpFuel, hpos, hswap]
· simp [arrayHeapIncreaseKeyBubbleUpFuel, hpos] The upward bubbling loop only swaps cells, so it preserves the multiset.
theorem arrayHeapIncreaseKeyBubbleUpFuel_perm (fuel : Nat) (a : List Nat)
(heapSize i : Nat) :
(arrayHeapIncreaseKeyBubbleUpFuel fuel a heapSize i).Perm a := by
induction fuel generalizing a i with
| zero =>
simp [arrayHeapIncreaseKeyBubbleUpFuel]
| succ fuel ih =>
by_cases hpos : 0 < i
· by_cases hswap : valAt a (parent i) < valAt a i
· have hrec := ih (a := swapAt a i (parent i)) (i := parent i)
exact (by
simpa [arrayHeapIncreaseKeyBubbleUpFuel, hpos, hswap] using
hrec.trans (swapAt_perm a i (parent i)) )
· simp [arrayHeapIncreaseKeyBubbleUpFuel, hpos, hswap]
· simp [arrayHeapIncreaseKeyBubbleUpFuel, hpos] Enough upward-bubbling fuel discharges the upward exception and restores a global heap.
theorem ArrayMaxHeapExceptUp.bubbleUpFuel_global {fuel : Nat} {a : List Nat}
{heapSize i : Nat} (h : ArrayMaxHeapExceptUp a heapSize i)
(hi : i < heapSize) (hfuel : i ≤ fuel) :
ArrayMaxHeap (arrayHeapIncreaseKeyBubbleUpFuel fuel a heapSize i) heapSize := by
induction fuel generalizing a heapSize i with
| zero =>
have hzero : i = 0 := by omega
have hglobal : ArrayMaxHeap a heapSize := h.to_global (Or.inl hzero)
simpa [arrayHeapIncreaseKeyBubbleUpFuel] using hglobal
| succ fuel ih =>
by_cases hpos : 0 < i
· by_cases hswap : valAt a (parent i) < valAt a i
· have hpheap : parent i < heapSize := Nat.lt_trans (parent_lt_self hpos) hi
have hp_le_fuel : parent i ≤ fuel := by
have hpi : parent i < i := parent_lt_self hpos
omega
have hnext : ArrayMaxHeapExceptUp (swapAt a i (parent i)) heapSize (parent i) :=
h.bubble_step hi hpos hswap
have hrec := ih (a := swapAt a i (parent i)) (heapSize := heapSize)
(i := parent i) hnext hpheap hp_le_fuel
simpa [arrayHeapIncreaseKeyBubbleUpFuel, hpos, hswap] using hrec
· have hle : valAt a i ≤ valAt a (parent i) := Nat.le_of_not_lt hswap
have hglobal : ArrayMaxHeap a heapSize := h.to_global (Or.inr hle)
simpa [arrayHeapIncreaseKeyBubbleUpFuel, hpos, hswap] using hglobal
· have hzero : i = 0 := Nat.eq_zero_of_not_pos hpos
have hglobal : ArrayMaxHeap a heapSize := h.to_global (Or.inl hzero)
simpa [arrayHeapIncreaseKeyBubbleUpFuel, hpos] using hglobal
Array-level CLRS HEAP-INCREASE-KEY: write the key and bubble it upward.
def arrayHeapIncreaseKey? (a : List Nat) (heapSize i key : Nat) : Option (List Nat) :=
if _h : i < heapSize ∧ heapSize ≤ a.length ∧ valAt a i ≤ key then
some (arrayHeapIncreaseKeyBubbleUpFuel i (a.set i key) heapSize i)
else
none
State-correctness theorem for array-level HEAP-INCREASE-KEY.
theorem arrayHeapIncreaseKey?_state_correct {a rest : List Nat} {heapSize i key : Nat}
(hheap : ArrayMaxHeap a heapSize)
(hres : arrayHeapIncreaseKey? a heapSize i key = some rest) :
i < heapSize ∧
heapSize ≤ a.length ∧
valAt a i ≤ key ∧
ArrayMaxHeap rest heapSize ∧
rest.length = a.length ∧
rest.Perm (a.set i key) := by
unfold arrayHeapIncreaseKey? at hres
by_cases hcond : i < heapSize ∧ heapSize ≤ a.length ∧ valAt a i ≤ key
· simp [hcond] at hres
subst rest
have hentry : ArrayMaxHeapExceptUp (a.set i key) heapSize i :=
hheap.set_increased_except_up hcond.1 hcond.2.2
have hrest_heap :
ArrayMaxHeap (arrayHeapIncreaseKeyBubbleUpFuel i (a.set i key) heapSize i)
heapSize :=
hentry.bubbleUpFuel_global hcond.1 (Nat.le_refl i)
have hlen := arrayHeapIncreaseKeyBubbleUpFuel_length i (a.set i key) heapSize i
have hperm := arrayHeapIncreaseKeyBubbleUpFuel_perm i (a.set i key) heapSize i
refine ⟨hcond.1, hcond.2.1, hcond.2.2, hrest_heap, ?_, hperm⟩
simpa [List.length_set] using hlen
· simp [hcond] at hres
Core no-bubble lemma for CLRS HEAP-INCREASE-KEY. If increasing a heap
cell leaves it below its parent, the array is already a max-heap after the
write, so the upward while-loop would stop immediately.