aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--BinaryHeap.lean3
-rw-r--r--BinaryHeap/CompleteTree.lean1131
-rw-r--r--BinaryHeap/CompleteTree/AdditionalProofs.lean84
-rw-r--r--BinaryHeap/CompleteTree/Basic.lean48
-rw-r--r--BinaryHeap/CompleteTree/HeapOperations.lean352
-rw-r--r--BinaryHeap/CompleteTree/HeapProofs.lean687
-rw-r--r--BinaryHeap/CompleteTree/Lemmas.lean37
-rw-r--r--BinaryHeap/CompleteTree/NatLemmas.lean (renamed from BinaryHeap/NatLemmas.lean)8
-rw-r--r--BinaryHeap/HeapPredicate.lean (renamed from BinaryHeap/Basic.lean)18
9 files changed, 1220 insertions, 1148 deletions
diff --git a/BinaryHeap.lean b/BinaryHeap.lean
index cd0e63f..9d1da1b 100644
--- a/BinaryHeap.lean
+++ b/BinaryHeap.lean
@@ -1,3 +1,6 @@
+/-
+ This file contains the Binary Heap type and its basic operations.
+-/
import BinaryHeap.CompleteTree
structure BinaryHeap (α : Type u) (le : α → α → Bool) (n : Nat) where
diff --git a/BinaryHeap/CompleteTree.lean b/BinaryHeap/CompleteTree.lean
index 4781358..fd9fb3c 100644
--- a/BinaryHeap/CompleteTree.lean
+++ b/BinaryHeap/CompleteTree.lean
@@ -1,1132 +1,11 @@
-import BinaryHeap.NatLemmas
-import BinaryHeap.Basic
+import BinaryHeap.HeapPredicate
+import BinaryHeap.CompleteTree.Basic
+import BinaryHeap.CompleteTree.HeapOperations
+import BinaryHeap.CompleteTree.HeapProofs
+import BinaryHeap.CompleteTree.AdditionalProofs
namespace BinaryHeap
-/-- Same as CompleteTree.root, but a bit more ergonomic to use. However, CompleteTree.root is better suited for proofs-/
-def CompleteTree.root' (tree : CompleteTree α (n+1)) : α := tree.root (Nat.zero_lt_succ n)
-
-/-- Helper to rw away root, because Lean 4.9 makes it unnecessarily hard to deal with match in tactics mode... -/
-theorem CompleteTree.root_unfold {α : Type u} {o p : Nat} (v : α) (l : CompleteTree α o) (r : CompleteTree α p) (h₁ : p ≤ o) (h₂ : o < 2 * (p + 1)) (h₃ : (o + 1).isPowerOfTwo ∨ (p + 1).isPowerOfTwo) (h₄ : o + p + 1 > 0): (CompleteTree.branch v l r h₁ h₂ h₃).root h₄ = v := rfl
-
-def reflexive_le {α : Type u} {le : α → α → Bool} (h₁ : total_le le) (a : α) : le a a := by
- unfold total_le at h₁
- have h₁ := h₁ a a
- cases h₁ <;> assumption
-
-def not_le_imp_le {α : Type u} {le : α → α → Bool} (h₁ : total_le le) : ∀(a b : α), ¬le a b → le b a := by
- intros a b h₂
- have h₁ := h₁ a b
- cases h₁
- . contradiction
- . trivial
-
-/-- Extracts the element count. For when pattern matching is too much work. -/
-def CompleteTree.length : CompleteTree α n → Nat := λ_ ↦ n
-
-/-- Returns the lenght of the left sub-tree. Mostly exists as a helper for expressing the return type of CompleteTree.left -/
-def CompleteTree.leftLen (tree : CompleteTree α n) (_ : n > 0) : Nat := match n, tree with
-| (_+_), .branch _ l _ _ _ _ => l.length
-
-def CompleteTree.leftLen' (tree : CompleteTree α (n+1)) : Nat := tree.leftLen (Nat.zero_lt_succ n)
-
-/-- Returns the lenght of the right sub-tree. Mostly exists as a helper for expressing the return type of CompleteTree.right -/
-def CompleteTree.rightLen (tree : CompleteTree α n) (_ : n > 0) : Nat := match n, tree with
-| (_+_), .branch _ _ r _ _ _ => r.length
-
-def CompleteTree.rightLen' (tree : CompleteTree α (n+1)) : Nat := tree.rightLen (Nat.zero_lt_succ n)
-
-def CompleteTree.left (tree : CompleteTree α n) (h₁ : n > 0) : CompleteTree α (tree.leftLen h₁) := match n, tree with
-| (_+_), .branch _ l _ _ _ _ => l
-
-def CompleteTree.left' (tree : CompleteTree α (n+1)) : CompleteTree α (tree.leftLen (Nat.zero_lt_succ n)) := tree.left (Nat.zero_lt_succ n)
-
-/-- Helper to rw away left, because Lean 4.9 makes it unnecessarily hard to deal with match in tactics mode... -/
-theorem CompleteTree.left_unfold {α : Type u} {o p : Nat} (v : α) (l : CompleteTree α o) (r : CompleteTree α p) (h₁ : p ≤ o) (h₂ : o < 2 * (p + 1)) (h₃ : (o + 1).isPowerOfTwo ∨ (p + 1).isPowerOfTwo) (h₄ : o + p + 1 > 0): (CompleteTree.branch v l r h₁ h₂ h₃).left h₄ = l := rfl
-
-def CompleteTree.right (tree : CompleteTree α n) (h₁ : n > 0) : CompleteTree α (tree.rightLen h₁) := match n, tree with
-| (_+_), .branch _ _ r _ _ _ => r
-
-def CompleteTree.right' (tree : CompleteTree α (n+1)) : CompleteTree α (tree.rightLen (Nat.zero_lt_succ n)) := tree.right (Nat.zero_lt_succ n)
-
-/-- Helper to rw away right, because Lean 4.9 makes it unnecessarily hard to deal with match in tactics mode... -/
-theorem CompleteTree.right_unfold {α : Type u} {o p : Nat} (v : α) (l : CompleteTree α o) (r : CompleteTree α p) (h₁ : p ≤ o) (h₂ : o < 2 * (p + 1)) (h₃ : (o + 1).isPowerOfTwo ∨ (p + 1).isPowerOfTwo) (h₄ : o + p + 1 > 0): (CompleteTree.branch v l r h₁ h₂ h₃).right h₄ = r := rfl
-
-/--Creates an empty CompleteTree. Needs the heap predicate as parameter.-/
-abbrev CompleteTree.empty {α : Type u} := CompleteTree.leaf (α := α)
-
-theorem CompleteTree.emptyIsHeap {α : Type u} (le : α → α → Bool) : HeapPredicate CompleteTree.empty le := by trivial
-
-private theorem power_of_two_mul_two_lt {n m : Nat} (h₁ : m.isPowerOfTwo) (h₂ : n < 2*m) (h₃ : ¬(n+1).isPowerOfTwo) : n+1 < 2*m :=
- if h₄ : n+1 > 2*m then by
- have h₂ := Nat.succ_le_of_lt h₂
- rewrite[←Nat.not_ge_eq] at h₂
- simp_arith at h₄
- contradiction
- else if h₅ : n+1 = 2*m then by
- have h₆ := Nat.mul2_isPowerOfTwo_of_isPowerOfTwo h₁
- rewrite[Nat.mul_comm 2 m] at h₅
- rewrite[←h₅] at h₆
- contradiction
- else by
- simp_arith at h₄
- exact Nat.lt_of_le_of_ne h₄ h₅
-
-private theorem power_of_two_mul_two_le {n m : Nat} (h₁ : (n+1).isPowerOfTwo) (h₂ : n < 2*(m+1)) (h₃ : ¬(m+1).isPowerOfTwo) (h₄ : m > 0): n < 2*m :=
- if h₅ : n > 2*m then by
- simp_arith at h₂
- simp_arith at h₅
- have h₆ : n+1 = 2*(m+1) := by simp_arith[Nat.le_antisymm h₂ h₅]
- rewrite[h₆] at h₁
- rewrite[←(Nat.mul2_ispowerOfTwo_iff_isPowerOfTwo (m+1))] at h₁
- contradiction
- else if h₆ : n = 2*m then by
- -- since (n+1) is a power of 2, n cannot be an even number, but n = 2*m means it's even
- -- ha, thought I wouldn't see that, didn't you? Thought you're smarter than I, computer?
- have h₇ : n > 0 := by rewrite[h₆]
- apply Nat.mul_lt_mul_of_pos_left h₄ (by decide :2 > 0)
- have h₈ : n ≠ 0 := Ne.symm $ Nat.ne_of_lt h₇
- have h₉ := Nat.isPowerOfTwo_even_or_one h₁
- simp[h₈] at h₉
- have h₉ := Nat.pred_even_odd h₉ (by simp_arith[h₇])
- simp at h₉
- have h₁₀ := Nat.mul_2_is_even h₆
- have h₁₀ := Nat.even_not_odd_even.mp h₁₀
- contradiction
- else by
- simp[Nat.not_gt_eq] at h₅
- have h₅ := Nat.eq_or_lt_of_le h₅
- simp[h₆] at h₅
- assumption
-
-/--Adds a new element to a given CompleteTree.-/
-def CompleteTree.heapPush (le : α → α → Bool) (elem : α) (heap : CompleteTree α o) : CompleteTree α (o+1) :=
- match o, heap with
- | 0, .leaf => CompleteTree.branch elem (CompleteTree.leaf) (CompleteTree.leaf) (by simp) (by simp) (by simp[Nat.one_isPowerOfTwo])
- | (n+m+1), .branch a left right p max_height_difference subtree_complete =>
- let (elem, a) := if le elem a then (a, elem) else (elem, a)
- -- okay, based on n and m we know if we want to add left or right.
- -- the left tree is full, if (n+1) is a power of two AND n != m
- let leftIsFull := (n+1).nextPowerOfTwo = n+1
- if r : m < n ∧ leftIsFull then
- have s : (m + 1 < n + 1) = (m < n) := by simp_arith
- have q : m + 1 ≤ n := by apply Nat.le_of_lt_succ
- rewrite[Nat.succ_eq_add_one]
- rewrite[s]
- simp[r]
- have difference_decreased := Nat.le_succ_of_le $ Nat.le_succ_of_le max_height_difference
- let result := branch a left (right.heapPush le elem) (q) difference_decreased (by simp[(Nat.power_of_two_iff_next_power_eq (n+1)), r])
- result
- else
- have q : m ≤ n+1 := by apply (Nat.le_of_succ_le)
- simp_arith[p]
- have right_is_power_of_two : (m+1).isPowerOfTwo :=
- if s : n = m then by
- rewrite[s] at subtree_complete
- simp at subtree_complete
- exact subtree_complete
- else if h₁ : leftIsFull then by
- rewrite[Decidable.not_and_iff_or_not (m<n) leftIsFull] at r
- cases r
- case inl h₂ => rewrite[Nat.not_lt_eq] at h₂
- have h₃ := Nat.not_le_of_gt $ Nat.lt_of_le_of_ne h₂ s
- contradiction
- case inr h₂ => simp at h₂
- contradiction
- else by
- simp[leftIsFull] at h₁
- rewrite[←Nat.power_of_two_iff_next_power_eq] at h₁
- cases subtree_complete
- case inl => contradiction
- case inr => trivial
- have still_in_range : n + 1 < 2 * (m + 1) := by
- rewrite[Decidable.not_and_iff_or_not (m<n) leftIsFull] at r
- cases r
- case inl h₁ => rewrite[Nat.not_gt_eq n m] at h₁
- simp_arith[Nat.le_antisymm h₁ p]
- case inr h₁ => simp[←Nat.power_of_two_iff_next_power_eq, leftIsFull] at h₁
- simp[h₁] at subtree_complete
- exact power_of_two_mul_two_lt subtree_complete max_height_difference h₁
- let result := branch a (left.heapPush le elem) right q still_in_range (Or.inr right_is_power_of_two)
- have letMeSpellItOutForYou : n + 1 + m + 1 = n + m + 1 + 1 := by simp_arith
- letMeSpellItOutForYou ▸ result
-
-private theorem CompleteTree.rootSeesThroughCast
- (n m : Nat)
- (h₁ : n + 1 + m = n + m + 1)
- (h₂ : 0 < n + 1 + m)
- (h₃ : 0 < n + m + 1)
- (heap : CompleteTree α (n+1+m)) : (h₁▸heap).root h₃ = heap.root h₂ := by
- induction m generalizing n
- case zero => simp
- case succ o ho =>
- have h₄ := ho (n+1)
- have h₅ : n + 1 + 1 + o = n + 1 + (Nat.succ o) := by simp_arith
- have h₆ : n + 1 + o + 1 = n + (Nat.succ o + 1) := by simp_arith
- rewrite[h₅, h₆] at h₄
- revert heap h₁ h₂ h₃
- assumption
-
---- Same as rootSeesThroughCast, but in the other direction.
-private theorem CompleteTree.rootSeesThroughCast2
- (n m : Nat)
- (h₁ : n + m + 1 = n + 1 + m)
- (h₂ : 0 < n + m + 1)
- (h₃ : 0 < n + 1 + m)
- (heap : CompleteTree α (n+m+1)) : (h₁▸heap).root h₃ = heap.root h₂ := by
- induction m generalizing n
- case zero => simp
- case succ mm mh =>
- have h₄ := mh (n+1)
- have h₅ : n + 1 + mm + 1 = n + Nat.succ mm + 1 := by simp_arith
- have h₆ : n + 1 + 1 + mm = n + 1 + Nat.succ mm := by simp_arith
- rw[h₅, h₆] at h₄
- revert heap h₁ h₂ h₃
- assumption
-
-theorem CompleteTree.heapPushRootSameOrElem (elem : α) (heap : CompleteTree α o) (lt : α → α → Bool) (h₂ : 0 < o): (CompleteTree.root (heap.heapPush lt elem) (Nat.succ_pos o) = elem) ∨ (CompleteTree.root (heap.heapPush lt elem) (Nat.succ_pos o) = CompleteTree.root heap h₂) := by
- unfold heapPush
- split --match o, heap
- contradiction
- simp
- rename_i n m v l r _ _ _
- split -- if h : m < n ∧ Nat.nextPowerOfTwo (n + 1) = n + 1 then
- case h_2.isTrue h =>
- cases (lt elem v) <;> simp[instDecidableEqBool, Bool.decEq, CompleteTree.root]
- case h_2.isFalse h =>
- rw[rootSeesThroughCast n (m+1) (by simp_arith) (by simp_arith) (by simp_arith)]
- cases (lt elem v)
- <;> simp[instDecidableEqBool, Bool.decEq, CompleteTree.root]
-
-theorem CompleteTree.heapPushEmptyElem (elem : α) (heap : CompleteTree α o) (lt : α → α → Bool) (h₂ : ¬0 < o) : (CompleteTree.root (heap.heapPush lt elem) (Nat.succ_pos o) = elem) :=
- have : o = 0 := Nat.eq_zero_of_le_zero $ (Nat.not_lt_eq 0 o).mp h₂
- match o, heap with
- | 0, .leaf => by simp[CompleteTree.heapPush, root]
-
-
-theorem HeapPredicate.leOrLeaf_transitive (h₁ : transitive_le le) : le a b → HeapPredicate.leOrLeaf le b h → HeapPredicate.leOrLeaf le a h := by
- unfold leOrLeaf
- intros h₂ h₃
- rename_i n
- cases n <;> simp
- apply h₁ a b _
- exact ⟨h₂, h₃⟩
-
-private theorem HeapPredicate.seesThroughCast
- (n m : Nat)
- (lt : α → α → Bool)
- (h₁ : n+1+m+1=n+m+1+1)
- (h₂ : 0<n+1+m+1)
- (h₃ : 0<n+m+1+1)
- (heap : CompleteTree α (n+1+m+1)) : HeapPredicate heap lt → HeapPredicate (h₁▸heap) lt := by
- unfold HeapPredicate
- intro h₄
- induction m generalizing n
- case zero => simp[h₄]
- case succ o ho =>
- have h₅ := ho (n+1)
- have h₆ : n+1+1+o+1 = n+1+(Nat.succ o)+1 := by simp_arith
- rw[h₆] at h₅
- have h₆ : n + 1 + o + 1 + 1 = n + (Nat.succ o + 1 + 1) := by simp_arith
- rewrite[h₆] at h₅
- revert heap h₁ h₂ h₃
- assumption
-
-mutual
-/--
- Helper for CompleteTree.heapPushIsHeap, to make one function out of both branches.
- Sorry for the ugly signature, but this was the easiest thing to extract.
- -/
-private theorem CompleteTree.heapPushIsHeapAux {α : Type u} (le : α → α → Bool) (n m : Nat) (v elem : α) (l : CompleteTree α n) (r : CompleteTree α m) (h₁ : HeapPredicate l le ∧ HeapPredicate r le ∧ HeapPredicate.leOrLeaf le v l ∧ HeapPredicate.leOrLeaf le v r) (h₂ : transitive_le le) (h₃ : total_le le): HeapPredicate l le ∧
- let smaller := (if le elem v then elem else v)
- let larger := (if le elem v then v else elem)
- HeapPredicate (heapPush le larger r) le
- ∧ HeapPredicate.leOrLeaf le smaller l
- ∧ HeapPredicate.leOrLeaf le smaller (heapPush le larger r)
- := by
- cases h₆ : (le elem v) <;> simp only [Bool.false_eq_true, reduceIte]
- case true =>
- have h₇ : (HeapPredicate (CompleteTree.heapPush le v r) le) := CompleteTree.heapPushIsHeap h₁.right.left h₂ h₃
- simp only [true_and, h₁, h₇, HeapPredicate.leOrLeaf_transitive h₂ h₆ h₁.right.right.left]
- cases m
- case zero =>
- have h₈ := heapPushEmptyElem v r le (Nat.not_lt_zero 0)
- simp only [HeapPredicate.leOrLeaf, Nat.succ_eq_add_one, Nat.reduceAdd, h₈]
- assumption
- case succ _ =>
- simp only [HeapPredicate.leOrLeaf]
- cases heapPushRootSameOrElem v r le (Nat.succ_pos _)
- <;> rename_i h₇
- <;> rw[h₇]
- . assumption
- apply h₂ elem v
- exact ⟨h₆, h₁.right.right.right⟩
- case false =>
- have h₇ : (HeapPredicate (CompleteTree.heapPush le elem r) le) := CompleteTree.heapPushIsHeap h₁.right.left h₂ h₃
- simp only [true_and, h₁, h₇]
- have h₈ : le v elem := not_le_imp_le h₃ elem v (by simp only [h₆, Bool.false_eq_true, not_false_eq_true])
- cases m
- case zero =>
- have h₇ := heapPushEmptyElem elem r le (Nat.not_lt_zero 0)
- simp only [HeapPredicate.leOrLeaf, Nat.succ_eq_add_one, Nat.reduceAdd, h₇]
- assumption
- case succ _ =>
- cases heapPushRootSameOrElem elem r le (Nat.succ_pos _)
- <;> rename_i h₉
- <;> simp only [HeapPredicate.leOrLeaf, Nat.succ_eq_add_one, h₈, h₉]
- exact h₁.right.right.right
-
-theorem CompleteTree.heapPushIsHeap {α : Type u} {elem : α} {heap : CompleteTree α o} {le : α → α → Bool} (h₁ : HeapPredicate heap le) (h₂ : transitive_le le) (h₃ : total_le le) : HeapPredicate (heap.heapPush le elem) le := by
- unfold heapPush
- split -- match o, heap with
- trivial
- case h_2 n m v l r m_le_n _ _ =>
- simp
- split -- if h₅ : m < n ∧ Nat.nextPowerOfTwo (n + 1) = n + 1 then
- case' isTrue =>
- have h := heapPushIsHeapAux le n m v elem l r h₁ h₂ h₃
- case' isFalse =>
- apply HeapPredicate.seesThroughCast <;> try simp_arith[h₂] --gets rid of annoying cast.
- have h := heapPushIsHeapAux le m n v elem r l (And.intro h₁.right.left $ And.intro h₁.left $ And.intro h₁.right.right.right h₁.right.right.left) h₂ h₃
- all_goals
- unfold HeapPredicate
- cases h₆ : (le elem v)
- <;> simp only [h₆, Bool.false_eq_true, reduceIte] at h
- <;> simp only [instDecidableEqBool, Bool.decEq, h, and_self]
-end
-
-/--Helper function for CompleteTree.indexOf.-/
-def CompleteTree.indexOfAux {α : Type u} (heap : CompleteTree α o) (pred : α → Bool) (currentIndex : Nat) : Option (Fin (o+currentIndex)) :=
- match o, heap with
- | 0, .leaf => none
- | (n+m+1), .branch a left right _ _ _ =>
- have sum_n_m_succ_curr : n + m.succ + currentIndex > 0 := Nat.add_pos_left (Nat.add_pos_right n (Nat.succ_pos m)) currentIndex
- if pred a then
- let result := Fin.ofNat' currentIndex sum_n_m_succ_curr
- some result
- else
- let found_left := left.indexOfAux pred (currentIndex + 1)
- let found_left : Option (Fin (n+m+1+currentIndex)) := found_left.map λ a ↦ Fin.ofNat' a sum_n_m_succ_curr
- let found_right :=
- found_left
- <|>
- (right.indexOfAux pred (currentIndex + n + 1)).map ((λ a ↦ Fin.ofNat' a sum_n_m_succ_curr) : _ → Fin (n+m+1+currentIndex))
- found_right
-
-/--Finds the first occurance of a given element in the heap and returns its index. Indices are depth first.-/
-def CompleteTree.indexOf {α : Type u} (heap : CompleteTree α o) (pred : α → Bool) : Option (Fin o) :=
- indexOfAux heap pred 0
-
-/--Returns the lement at the given index. Indices are depth first.-/
-def CompleteTree.get {α : Type u} {n : Nat} (index : Fin (n+1)) (heap : CompleteTree α (n+1)) : α :=
- match h₁ : index, h₂ : n, heap with
- | 0, (_+_), .branch v _ _ _ _ _ => v
- | ⟨j+1,h₃⟩, (o+p), .branch _ l r _ _ _ =>
- if h₄ : j < o then
- match o with
- | (oo+1) => get ⟨j, h₄⟩ l
- else
- have h₅ : n - o = p := Nat.sub_eq_of_eq_add $ (Nat.add_comm o p).subst h₂
- have : p ≠ 0 :=
- have h₆ : o < n := Nat.lt_of_le_of_lt (Nat.ge_of_not_lt h₄) (Nat.lt_of_succ_lt_succ h₃)
- h₅.symm.substr $ Nat.sub_ne_zero_of_lt h₆
- have h₆ : j - o < p := h₅.subst $ Nat.sub_lt_sub_right (Nat.ge_of_not_lt h₄) $ Nat.lt_of_succ_lt_succ h₃
- have _termination : j - o < index.val := (Fin.val_inj.mpr h₁).substr (Nat.sub_lt_succ j o)
- match p with
- | (pp + 1) => get ⟨j - o, h₆⟩ r
-
-/-- Helper for heapRemoveLastAux -/
-private theorem CompleteTree.removeRightRightNotEmpty {n m : Nat} (m_gt_0_or_rightIsFull : m > 0 ∨ ((m+1).nextPowerOfTwo = m+1 : Bool)) (h₁ : 0 ≠ n + m) (h₂ : ¬(m < n ∧ ((m+1).nextPowerOfTwo = m+1 : Bool))) : m > 0 :=
- match m_gt_0_or_rightIsFull with
- | Or.inl h => h
- | Or.inr h => by
- simp only [h, and_true, Nat.not_lt] at h₂
- cases n
- case zero => exact Nat.zero_lt_of_ne_zero $ (Nat.zero_add m).subst (motive := (·≠0)) h₁.symm
- case succ q =>
- cases m
- . exact absurd h₂ $ Nat.not_succ_le_zero q
- . exact Nat.succ_pos _
-
-/-- Helper for heapRemoveLastAux -/
-private theorem CompleteTree.removeRightLeftIsFull {n m : Nat} (r : ¬(m < n ∧ ((m+1).nextPowerOfTwo = m+1 : Bool))) (m_le_n : m ≤ n) (subtree_complete : (n + 1).isPowerOfTwo ∨ (m + 1).isPowerOfTwo) : (n+1).isPowerOfTwo := by
- rewrite[Decidable.not_and_iff_or_not] at r
- cases r
- case inl h₁ => rewrite[Nat.not_lt_eq] at h₁
- have h₂ := Nat.le_antisymm h₁ m_le_n
- rewrite[←h₂] at subtree_complete
- simp at subtree_complete
- assumption
- case inr h₁ => simp(config := {zetaDelta := true })[←Nat.power_of_two_iff_next_power_eq] at h₁
- simp[h₁] at subtree_complete
- assumption
-
-/-- Helper for heapRemoveLastAux -/
-private theorem CompleteTree.stillInRange {n m : Nat} (r : ¬(m < n ∧ ((m+1).nextPowerOfTwo = m+1 : Bool))) (m_le_n : m ≤ n) (m_gt_0 : m > 0) (leftIsFull : (n+1).isPowerOfTwo) (max_height_difference: n < 2 * (m + 1)) : n < 2*m := by
- rewrite[Decidable.not_and_iff_or_not] at r
- cases r with
- | inl h₁ => have m_eq_n : m = n := Nat.le_antisymm m_le_n (Nat.not_lt.mp h₁)
- have m_lt_2_m : m < 2 * m := (Nat.one_mul m).subst (motive := λ x ↦ x < 2 * m) $ Nat.mul_lt_mul_of_pos_right Nat.one_lt_two m_gt_0
- exact m_eq_n.subst (motive := λx ↦ x < 2 * m) m_lt_2_m
- | inr h₁ => simp (config := { zetaDelta := true }) only [← Nat.power_of_two_iff_next_power_eq, decide_eq_true_eq] at h₁
- apply power_of_two_mul_two_le <;> assumption
-
-private def CompleteTree.heapRemoveLastAux
-{α : Type u}
-{β : Nat → Type u}
-{o : Nat}
-(heap : CompleteTree α (o+1))
-(aux0 : α → (β 1))
-(auxl : {prev_size curr_size : Nat} → β prev_size → (h₁ : prev_size < curr_size) → β curr_size)
-(auxr : {prev_size curr_size : Nat} → β prev_size → (left_size : Nat) → (h₁ : prev_size + left_size < curr_size) → β curr_size)
-: (CompleteTree α o × (β (o+1)))
-:=
- match o, heap with
- | (n+m), .branch a (left : CompleteTree α n) (right : CompleteTree α m) m_le_n max_height_difference subtree_complete =>
- if p : 0 = (n+m) then
- (p▸CompleteTree.leaf, p▸aux0 a)
- else
- let rightIsFull : Bool := (m+1).nextPowerOfTwo = m+1
- have m_gt_0_or_rightIsFull : m > 0 ∨ rightIsFull := by cases m <;> simp (config := { ground:=true })[rightIsFull]
- if r : m < n ∧ rightIsFull then
- --remove left
- match n, left with
- | (l+1), left =>
- let ((newLeft : CompleteTree α l), res) := left.heapRemoveLastAux aux0 auxl auxr
- have q : l + m + 1 = l + 1 + m := Nat.add_right_comm l m 1
- have s : m ≤ l := Nat.le_of_lt_succ r.left
- have rightIsFull : (m+1).isPowerOfTwo := (Nat.power_of_two_iff_next_power_eq (m+1)).mpr $ decide_eq_true_eq.mp r.right
- have l_lt_2_m_succ : l < 2 * (m+1) := Nat.lt_of_succ_lt max_height_difference
- let res := auxl res (by simp_arith)
- (q▸CompleteTree.branch a newLeft right s l_lt_2_m_succ (Or.inr rightIsFull), res)
- else
- --remove right
- have m_gt_0 : m > 0 := removeRightRightNotEmpty m_gt_0_or_rightIsFull p r
- let l := m.pred
- have h₂ : l.succ = m := (Nat.succ_pred $ Nat.not_eq_zero_of_lt (Nat.gt_of_not_le $ Nat.not_le_of_gt m_gt_0))
- let ((newRight : CompleteTree α l), res) := (h₂.symm▸right).heapRemoveLastAux aux0 auxl auxr
- have leftIsFull : (n+1).isPowerOfTwo := removeRightLeftIsFull r m_le_n subtree_complete
- have still_in_range : n < 2 * (l+1) := h₂.substr (p := λx ↦ n < 2 * x) $ stillInRange r m_le_n m_gt_0 leftIsFull max_height_difference
- let res := auxr res n (by omega)
- (h₂▸CompleteTree.branch a left newRight (Nat.le_of_succ_le (h₂▸m_le_n)) still_in_range (Or.inl leftIsFull), res)
-
-private def CompleteTree.heapRemoveLast {α : Type u} {o : Nat} (heap : CompleteTree α (o+1)) : (CompleteTree α o × α) :=
- heap.heapRemoveLastAux id (λ(a : α) _ ↦ a) (λa _ _ ↦ a)
-
-private def CompleteTree.heapRemoveLastWithIndex {α : Type u} {o : Nat} (heap : CompleteTree α (o+1)) : (CompleteTree α o × α × Fin (o+1)) :=
- heap.heapRemoveLastAux (β := λn ↦ α × Fin n)
- (λ(a : α) ↦ (a, Fin.mk 0 (Nat.succ_pos 0)))
- (λ(a, prev_idx) h₁ ↦ (a, prev_idx.succ.castLE $ Nat.succ_le_of_lt h₁) )
- (λ(a, prev_idx) left_size h₁ ↦ (a, (prev_idx.addNat left_size).succ.castLE $ Nat.succ_le_of_lt h₁))
-
-private theorem CompleteTree.heqSameLeftLen {α : Type u} {n m : Nat} {a : CompleteTree α n} {b : CompleteTree α m} (h₁ : n = m) (h₂ : n > 0) (h₃ : HEq a b) : a.leftLen h₂ = b.leftLen (h₁.subst h₂) := by
- subst n
- have h₃ : a = b := eq_of_heq h₃
- subst a
- rfl
-
-private theorem CompleteTree.heqSameRightLen {α : Type u} {n m : Nat} {a : CompleteTree α n} {b : CompleteTree α m} (h₁ : n = m) (h₂ : n > 0) (h₃ : HEq a b) : a.rightLen h₂ = b.rightLen (h₁.subst h₂) := by
- subst n
- have h₃ : a = b := eq_of_heq h₃
- subst a
- rfl
-
-/--Shows that the index and value returned by heapRemoveLastWithIndex are consistent.-/
-private theorem CompleteTree.heapRemoveLastWithIndexReturnsItemAtIndex {α : Type u} {o : Nat} (heap : CompleteTree α (o+1)) : heap.get heap.heapRemoveLastWithIndex.snd.snd = heap.heapRemoveLastWithIndex.snd.fst := by
- unfold heapRemoveLastWithIndex heapRemoveLastAux
- split
- rename_i n m v l r m_le_n max_height_difference subtree_full
- simp only [Nat.add_eq, Fin.zero_eta, Fin.isValue, decide_eq_true_eq, Fin.castLE_succ]
- split
- case isTrue n_m_zero =>
- unfold get
- split
- case h_1 nn mm vv ll rr mm_le_nn _ _ _ _ he₁ he₂ =>
- have h₁ : n = 0 := And.left $ Nat.add_eq_zero.mp n_m_zero.symm
- have h₂ : m = 0 := And.right $ Nat.add_eq_zero.mp n_m_zero.symm
- have h₃ : nn = 0 := And.left (Nat.add_eq_zero.mp $ Eq.symm $ (Nat.zero_add 0).subst (motive := λx ↦ x = nn + mm) $ h₂.subst (motive := λx ↦ 0 + x = nn + mm) (h₁.subst (motive := λx ↦ x + m = nn + mm) he₁))
- have h₄ : mm = 0 := And.right (Nat.add_eq_zero.mp $ Eq.symm $ (Nat.zero_add 0).subst (motive := λx ↦ x = nn + mm) $ h₂.subst (motive := λx ↦ 0 + x = nn + mm) (h₁.subst (motive := λx ↦ x + m = nn + mm) he₁))
- subst n m nn mm
- exact And.left $ CompleteTree.branch.inj (eq_of_heq he₂.symm)
- case h_2 =>
- omega -- to annoying to deal with Fin.ofNat... There's a hypothesis that says 0 = ⟨1,_⟩.
- case isFalse n_m_not_zero =>
- unfold get
- split
- case h_1 nn mm vv ll rr mm_le_nn max_height_difference_2 subtree_full2 _ he₁ he₂ he₃ =>
- --aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
- --okay, I know that he₁ is False.
- --but reducing this wall of text to something the computer understands - I am frightened.
- exfalso
- revert he₁
- split
- case' isTrue => cases l; case leaf hx => exact absurd hx.left $ Nat.not_lt_zero m
- all_goals
- apply Fin.ne_of_val_ne
- simp only [Fin.isValue, Fin.val_succ, Fin.coe_castLE, Fin.coe_addNat, Nat.add_one_ne_zero, not_false_eq_true]
- --okay, this wasn't that bad
- case h_2 j j_lt_n_add_m nn mm vv ll rr mm_le_nn max_height_difference_2 subtree_full2 heap he₁ he₂ he₃ =>
- --he₁ relates j to the other indices. This is the important thing here.
- --it should be reducible to j = (l or r).heap.heapRemoveLastWithIndex.snd.snd
- --or something like it.
-
- --but first, let's get rid of mm and nn, and vv while at it.
- -- which are equal to m, n, v, but we need to deduce this from he₃...
- have : n = nn := heqSameLeftLen (congrArg (·+1) he₂) (by simp_arith) he₃
- have : m = mm := heqSameRightLen (congrArg (·+1) he₂) (by simp_arith) he₃
- subst nn mm
- simp only [heq_eq_eq, branch.injEq] at he₃
- -- yeah, no more HEq fuglyness!
- have : v = vv := he₃.left
- have : l = ll := he₃.right.left
- have : r = rr := he₃.right.right
- subst vv ll rr
- split at he₁
- <;> rename_i goLeft
- <;> simp only [goLeft, and_self, ↓reduceDite, Fin.isValue]
- case' isTrue =>
- cases l;
- case leaf => exact absurd goLeft.left $ Nat.not_lt_zero m
- rename_i o p _ _ _ _ _ _ _
- case' isFalse =>
- cases r;
- case leaf => simp (config := { ground := true }) only [and_true, Nat.not_lt, Nat.le_zero_eq] at goLeft;
- exact absurd ((Nat.add_zero n).substr goLeft.symm) n_m_not_zero
- all_goals
- have he₁ := Fin.val_eq_of_eq he₁
- simp only [Fin.isValue, Fin.val_succ, Fin.coe_castLE, Fin.coe_addNat, Nat.reduceEqDiff] at he₁
- have : max_height_difference_2 = max_height_difference := rfl
- have : subtree_full2 = subtree_full := rfl
- subst max_height_difference_2 subtree_full2
- rename_i del1 del2
- clear del1 del2
- case' isTrue =>
- have : j < o + p + 1 := by omega --from he₁. It has j = (blah : Fin (o+p+1)).val
- case' isFalse =>
- have : ¬j<n := by omega --from he₁. It has j = something + n.
- all_goals
- simp only [this, ↓reduceDite, Nat.pred_succ, Fin.isValue]
- subst j -- overkill, but unlike rw it works
- simp only [Nat.pred_succ, Fin.isValue, Nat.add_sub_cancel, Fin.eta]
- apply heapRemoveLastWithIndexReturnsItemAtIndex
- done
-
-private theorem CompleteTree.castZeroHeap (n m : Nat) (heap : CompleteTree α 0) (h₁ : 0=n+m) {le : α → α → Bool} : HeapPredicate (h₁ ▸ heap) le := by
- have h₂ : heap = (CompleteTree.empty : CompleteTree α 0) := by
- simp[empty]
- match heap with
- | .leaf => trivial
- have h₂ : HeapPredicate heap le := by simp[h₂, empty, emptyIsHeap]
- cases m
- case succ => contradiction
- case zero =>
- cases n
- case succ => contradiction
- case zero =>
- simp[h₁, h₂]
-
-private theorem HeapPredicate.seesThroughCast2
- (n m : Nat)
- (lt : α → α → Bool)
- (h₁ : n+m+1=n+1+m)
- (h₂ : 0<n+1+m)
- (h₃ : 0<n+m+1)
- (heap : CompleteTree α (n+m+1)) : HeapPredicate heap lt → HeapPredicate (h₁▸heap) lt := by
- unfold HeapPredicate
- intro h₄
- induction m generalizing n
- case zero => simp[h₄]
- case succ o ho =>
- have h₅ := ho (n+1)
- have h₆ : n+1+o+1 = n+(Nat.succ o)+1 := by simp_arith
- rw[h₆] at h₅
- have h₆ : n + 1 + 1 + o = n + 1 + Nat.succ o := by simp_arith
- rewrite[h₆] at h₅
- revert heap h₁ h₂ h₃
- assumption
-
--- If there is only one element left, the result is a leaf.
-private theorem CompleteTree.heapRemoveLastAuxLeaf
-{α : Type u}
-{β : Nat → Type u}
-(heap : CompleteTree α 1)
-(aux0 : α → (β 1))
-(auxl : {prev_size curr_size : Nat} → β prev_size → (h₁ : prev_size < curr_size) → β curr_size)
-(auxr : {prev_size curr_size : Nat} → β prev_size → (left_size : Nat) → (h₁ : prev_size + left_size < curr_size) → β curr_size)
-: (heap.heapRemoveLastAux aux0 auxl auxr).fst = CompleteTree.leaf := by
- let l := (heap.heapRemoveLastAux aux0 auxl auxr).fst
- have h₁ : l = CompleteTree.leaf := match l with
- | .leaf => rfl
- exact h₁
-
-private theorem CompleteTree.heapRemoveLastAuxLeavesRoot
-{α : Type u}
-{β : Nat → Type u}
-(heap : CompleteTree α (n+1))
-(aux0 : α → (β 1))
-(auxl : {prev_size curr_size : Nat} → β prev_size → (h₁ : prev_size < curr_size) → β curr_size)
-(auxr : {prev_size curr_size : Nat} → β prev_size → (left_size : Nat) → (h₁ : prev_size + left_size < curr_size) → β curr_size)
-(h₁ : n > 0)
-: heap.root (Nat.zero_lt_of_ne_zero $ Nat.succ_ne_zero n) = (heap.heapRemoveLastAux aux0 auxl auxr).fst.root (h₁) := by
- unfold heapRemoveLastAux
- split
- rename_i o p v l r _ _ _
- have h₃ : (0 ≠ o + p) := Ne.symm $ Nat.not_eq_zero_of_lt h₁
- simp[h₃]
- exact
- if h₄ : p < o ∧ Nat.nextPowerOfTwo (p + 1) = p + 1 then by
- simp[h₄]
- cases o
- case zero => exact absurd h₄.left $ Nat.not_lt_zero p
- case succ oo _ _ _ =>
- simp -- redundant, but makes goal easier to read
- rw[rootSeesThroughCast2 oo p _ (by simp_arith) _]
- apply root_unfold
- else by
- simp[h₄]
- cases p
- case zero =>
- simp_arith at h₁ -- basically o ≠ 0
- simp_arith (config := {ground := true})[h₁] at h₄ -- the second term in h₄ is decidable and True. What remains is ¬(0 < o), or o = 0
- case succ pp hp =>
- simp_arith
- apply root_unfold
-
-private theorem CompleteTree.heapRemoveLastAuxIsHeap
-{α : Type u}
-{β : Nat → Type u}
-{heap : CompleteTree α (o+1)}
-{le : α → α → Bool}
-(aux0 : α → (β 1))
-(auxl : {prev_size curr_size : Nat} → β prev_size → (h₁ : prev_size < curr_size) → β curr_size)
-(auxr : {prev_size curr_size : Nat} → β prev_size → (left_size : Nat) → (h₁ : prev_size + left_size < curr_size) → β curr_size)
-(h₁ : HeapPredicate heap le) (h₂ : transitive_le le) (h₃ : total_le le) : HeapPredicate ((heap.heapRemoveLastAux aux0 auxl auxr).fst) le := by
- unfold heapRemoveLastAux
- split
- rename_i n m v l r _ _ _
- exact
- if h₄ : 0 = (n+m) then by
- simp only [h₄, reduceDite, castZeroHeap]
- else by
- simp[h₄]
- exact
- if h₅ : (m<n ∧ Nat.nextPowerOfTwo (m+1) = m+1) then by
- simp only [h₅, and_self, ↓reduceDite]
- cases n
- case zero =>
- exact absurd h₅.left $ Nat.not_lt_zero m
- case succ ll h₆ h₇ h₈ =>
- simp only
- apply HeapPredicate.seesThroughCast2 <;> try simp_arith
- cases ll
- case a.zero => -- if ll is zero, then (heapRemoveLast l).snd is a leaf.
- have h₆ := heapRemoveLastAuxLeaf l aux0 auxl auxr
- rw[h₆]
- unfold HeapPredicate at *
- have h₇ : HeapPredicate .leaf le := by trivial
- have h₈ : HeapPredicate.leOrLeaf le v .leaf := by trivial
- exact ⟨h₇,h₁.right.left, h₈, h₁.right.right.right⟩
- case a.succ nn => -- if ll is not zero, then the root element before and after heapRemoveLast is the same.
- unfold HeapPredicate at *
- simp only [heapRemoveLastAuxIsHeap aux0 auxl auxr h₁.left h₂ h₃, h₁.right.left, h₁.right.right.right, and_true, true_and]
- unfold HeapPredicate.leOrLeaf
- simp only
- rw[←heapRemoveLastAuxLeavesRoot]
- exact h₁.right.right.left
- else by
- simp[h₅]
- cases m
- case zero =>
- simp only [Nat.add_zero] at h₄ -- n ≠ 0
- simp (config := { ground := true }) only [Nat.zero_add, and_true, Nat.not_lt, Nat.le_zero_eq, Ne.symm h₄] at h₅ -- the second term in h₅ is decidable and True. What remains is ¬(0 < n), or n = 0
- case succ mm h₆ h₇ h₈ =>
- unfold HeapPredicate at *
- simp only [h₁, heapRemoveLastAuxIsHeap aux0 auxl auxr h₁.right.left h₂ h₃, true_and]
- unfold HeapPredicate.leOrLeaf
- exact match mm with
- | 0 => rfl
- | o+1 =>
- have h₉ : le v ((r.heapRemoveLastAux _ _ _).fst.root (Nat.zero_lt_succ o)) := by
- rw[←heapRemoveLastAuxLeavesRoot]
- exact h₁.right.right.right
- h₉
-
-private theorem CompleteTree.heapRemoveLastIsHeap {α : Type u} {heap : CompleteTree α (o+1)} {le : α → α → Bool} (h₁ : HeapPredicate heap le) (h₂ : transitive_le le) (h₃ : total_le le) : HeapPredicate (heap.heapRemoveLast.fst) le :=
- heapRemoveLastAuxIsHeap _ _ _ h₁ h₂ h₃
-
-private theorem CompleteTree.heapRemoveLastWithIndexIsHeap {α : Type u} {heap : CompleteTree α (o+1)} {le : α → α → Bool} (h₁ : HeapPredicate heap le) (h₂ : transitive_le le) (h₃ : total_le le) : HeapPredicate (heap.heapRemoveLastWithIndex.fst) le :=
- heapRemoveLastAuxIsHeap _ _ _ h₁ h₂ h₃
-
-/--
- Helper for CompleteTree.heapUpdateAt. Makes proofing heap predicate work in Lean 4.9
- -/
-def CompleteTree.heapUpdateRoot {α : Type u} {n : Nat} (le : α → α → Bool) (value : α) (heap : CompleteTree α n) (_ : n > 0) : CompleteTree α n × α :=
-match n, heap with
- | (o+p+1), .branch v l r h₃ h₄ h₅ =>
- if h₆ : o = 0 then
- -- have : p = 0 := (Nat.le_zero_eq p).mp $ h₇.subst h₃ --not needed, left here for reference
- (.branch value l r h₃ h₄ h₅, v)
- else
- have h₇ : o > 0 := Nat.zero_lt_of_ne_zero h₆
- let lr := l.root h₇
- if h₈ : p = 0 then
- if le value lr then
- (.branch value l r h₃ h₄ h₅, v)
- else
- -- We would not need to recurse further, because we know o = 1.
- -- However, that would introduce type casts, what makes proving harder...
- -- have h₉: o = 1 := Nat.le_antisymm (by simp_arith[h₈] at h₄; exact h₄) (Nat.succ_le_of_lt h₇)
- let ln := heapUpdateRoot le value l h₇
- (.branch ln.snd ln.fst r h₃ h₄ h₅, v)
- else
- have h₉ : p > 0 := Nat.zero_lt_of_ne_zero h₈
- let rr := r.root h₉
- if le value lr ∧ le value rr then
- (.branch value l r h₃ h₄ h₅, v)
- else if le lr rr then -- value is gt either left or right root. Move it down accordingly
- let ln := heapUpdateRoot le value l h₇
- (.branch ln.snd ln.fst r h₃ h₄ h₅, v)
- else
- let rn := heapUpdateRoot le value r h₉
- (.branch rn.snd l rn.fst h₃ h₄ h₅, v)
-/--
- Helper for CompleteTree.heapRemoveAt.
- Removes the element at index, and instead inserts the given value.
- Returns the element at index, and the resulting tree.
- -/
-def CompleteTree.heapUpdateAt {α : Type u} {n : Nat} (le : α → α → Bool) (index : Fin n) (value : α) (heap : CompleteTree α n) (h₁ : n > 0) : CompleteTree α n × α :=
- if h₂ : index == ⟨0,h₁⟩ then
- heapUpdateRoot le value heap h₁
- else
- match n, heap with
- | (o+p+1), .branch v l r h₃ h₄ h₅ =>
- let (v, value) := if le v value then (v, value) else (value, v)
- if h₆ : index ≤ o then
- have h₇ : Nat.pred index.val < o := Nat.lt_of_lt_of_le (Nat.pred_lt $ Fin.val_ne_of_ne (ne_of_beq_false $ Bool.of_not_eq_true h₂)) h₆
- let index_in_left : Fin o := ⟨index.val.pred, h₇⟩
- have h₈ : 0 < o := Nat.zero_lt_of_lt h₇
- let result := heapUpdateAt le index_in_left value l h₈
- (.branch v result.fst r h₃ h₄ h₅, result.snd)
- else
- have h₇ : index.val - (o + 1) < p :=
- -- tactic rewrite failed, result is not type correct.
- have h₈ : index.val < p + o + 1 := index.isLt
- |> (Nat.add_assoc o p 1).subst
- |> (Nat.add_comm p 1).subst (motive := λx ↦ index.val < o + x)
- |> (Nat.add_assoc o 1 p).symm.subst
- |> (Nat.add_comm (o+1) p).subst
- Nat.sub_lt_of_lt_add h₈ $ (Nat.not_le_eq index.val o).mp h₆
- let index_in_right : Fin p := ⟨index.val - o - 1, h₇⟩
- have h₈ : 0 < p := Nat.zero_lt_of_lt h₇
- let result := heapUpdateAt le index_in_right value r h₈
- (.branch v l result.fst h₃ h₄ h₅, result.snd)
-
-private theorem CompleteTree.heapUpdateRootReturnsRoot {α : Type u} {n : Nat} (le : α → α → Bool) (value : α) (heap : CompleteTree α n) (h₁ : n > 0) : (heap.heapUpdateRoot le value h₁).snd = heap.root h₁ := by
- unfold heapUpdateRoot
- split
- rename_i o p v l r h₃ h₄ h₅ h₁
- simp
- cases o <;> simp
- case zero =>
- exact root_unfold v l r h₃ h₄ h₅ h₁
- case succ =>
- cases p <;> simp
- case zero =>
- cases le value (root l _)
- <;> simp only [Bool.false_eq_true, ↓reduceIte, root_unfold]
- case succ =>
- cases le value (root l _) <;> cases le value (root r _)
- <;> cases le (root l _) (root r _)
- <;> simp only [Bool.false_eq_true, and_self, and_true, and_false, ↓reduceIte, root_unfold]
-
-private theorem CompleteTree.heapUpdateRootPossibleRootValuesAuxL {α : Type u} (heap : CompleteTree α n) (h₁ : n > 1) : 0 < heap.leftLen (Nat.lt_trans (Nat.lt_succ_self 0) h₁) :=
- match h₅: n, heap with
- | (o+p+1), .branch v l r h₂ h₃ h₄ => by
- simp[leftLen, length]
- cases o
- case zero => rewrite[(Nat.le_zero_eq p).mp h₂] at h₁; contradiction
- case succ q => exact Nat.zero_lt_succ q
-
-private theorem CompleteTree.heapUpdateRootPossibleRootValuesAuxR {α : Type u} (heap : CompleteTree α n) (h₁ : n > 2) : 0 < heap.rightLen (Nat.lt_trans (Nat.lt_succ_self 0) $ Nat.lt_trans (Nat.lt_succ_self 1) h₁) :=
- match h₅: n, heap with
- | (o+p+1), .branch v l r h₂ h₃ h₄ => by
- simp[rightLen, length]
- cases p
- case zero => simp_arith at h₁; simp at h₃; exact absurd h₁ (Nat.not_le_of_gt h₃)
- case succ q => exact Nat.zero_lt_succ q
-
-private theorem CompleteTree.heapUpdateRootPossibleRootValues1 {α : Type u} (le : α → α → Bool) (value : α) (heap : CompleteTree α n) (h₁ : n = 1) : (heap.heapUpdateRoot le value (h₁.substr (Nat.lt_succ_self 0))).fst.root (h₁.substr (Nat.lt_succ_self 0)) = value := by
- unfold heapUpdateRoot
- generalize (h₁.substr (Nat.lt_succ_self 0) : n > 0) = hx
- split
- rename_i o p v l r _ _ _ h₁
- have h₃ : o = 0 := (Nat.add_eq_zero.mp $ Nat.succ.inj h₁).left
- simp[h₃, root_unfold]
-
-private theorem CompleteTree.heapUpdateRootPossibleRootValues2 {α : Type u} (le : α → α → Bool) (value : α) (heap : CompleteTree α n) (h₁ : n = 2) :
-have h₂ : 0 < n := Nat.lt_trans (Nat.lt_succ_self 0) $ h₁.substr (Nat.lt_succ_self 1)
-have h₃ : 0 < leftLen heap h₂ := heapUpdateRootPossibleRootValuesAuxL heap (h₁.substr (Nat.lt_succ_self 1))
-(heap.heapUpdateRoot le value h₂).fst.root h₂ = value
-∨ (heap.heapUpdateRoot le value h₂).fst.root h₂ = (heap.left h₂).root h₃
-:= by
- simp
- unfold heapUpdateRoot
- generalize (Nat.lt_trans (Nat.lt_succ_self 0) (Eq.substr h₁ (Nat.lt_succ_self 1)) : 0 < n) = h₂
- split
- rename_i o p v l r h₃ h₄ h₅ h₂
- cases o <;> simp
- case zero => simp only[root, true_or]
- case succ oo =>
- have h₆ : p = 0 := by simp at h₁; omega
- simp only [h₆, ↓reduceDite]
- cases le value (l.root _)
- <;> simp[heapUpdateRootReturnsRoot, root_unfold, left_unfold]
-
-private theorem CompleteTree.heapUpdateRootPossibleRootValues3 {α : Type u} (le : α → α → Bool) (value : α) (heap : CompleteTree α n) (h₁ : n > 2) :
-have h₂ : 0 < n := Nat.lt_trans (Nat.lt_succ_self 0) $ Nat.lt_trans (Nat.lt_succ_self 1) h₁
-have h₃ : 0 < leftLen heap h₂ := heapUpdateRootPossibleRootValuesAuxL heap $ Nat.lt_trans (Nat.lt_succ_self 1) h₁
-have h₄ : 0 < rightLen heap h₂ := heapUpdateRootPossibleRootValuesAuxR heap h₁
-(heap.heapUpdateRoot le value h₂).fst.root h₂ = value
-∨ (heap.heapUpdateRoot le value h₂).fst.root h₂ = (heap.left h₂).root h₃
-∨ (heap.heapUpdateRoot le value h₂).fst.root h₂ = (heap.right h₂).root h₄
-:= by
- simp only
- unfold heapUpdateRoot
- generalize (Nat.lt_trans (Nat.lt_succ_self 0) (Nat.lt_trans (Nat.lt_succ_self 1) h₁) : 0 < n) = h₂
- split
- rename_i o p v l r h₃ h₄ h₅ h₂
- cases o
- case zero => simp only[root, true_or]
- case succ oo =>
- have h₆ : p ≠ 0 := by simp at h₁; omega
- simp only [Nat.add_one_ne_zero, ↓reduceDite, h₆]
- cases le value (l.root _) <;> simp
- rotate_right
- cases le value (r.root _) <;> simp
- case true.true => simp[root]
- case false | true.false =>
- cases le (l.root _) (r.root _)
- <;> simp only [Bool.false_eq_true, ↓reduceIte, heapUpdateRootReturnsRoot, root_unfold, left_unfold, right_unfold, true_or, or_true]
-
-private theorem CompleteTree.heapUpdateRootIsHeapLeRootAux {α : Type u} (le : α → α → Bool) (value : α) (heap : CompleteTree α n) (h₁ : HeapPredicate heap le) (h₂ : n > 0) (h₃ : le (root heap h₂) value) : HeapPredicate.leOrLeaf le (root heap h₂) (heapUpdateRoot le value heap h₂).fst :=
- if h₄ : n = 1 then by
- have h₅ : le (heap.root h₂) ( (heapUpdateRoot le value heap h₂).fst.root h₂) := by simp only[h₃, h₄, heapUpdateRootPossibleRootValues1]
- unfold HeapPredicate.leOrLeaf
- split
- · rfl
- · exact h₅
- else if h₅ : n = 2 then by
- have h₆ := heapUpdateRootPossibleRootValues2 le value heap h₅
- cases h₆
- case inl h₆ =>
- have h₇ : le (heap.root h₂) ( (heapUpdateRoot le value heap h₂).fst.root h₂) := by simp only [h₆, h₃]
- unfold HeapPredicate.leOrLeaf
- split
- · rfl
- · exact h₇
- case inr h₆ =>
- unfold HeapPredicate.leOrLeaf
- unfold HeapPredicate at h₁
- split at h₁
- case h_1 => contradiction
- case h_2 o p v l r h₇ h₈ h₉ =>
- have h₁₁ : p = 0 := by
- simp at h₅
- cases o; simp only [Nat.le_zero_eq] at h₇; exact h₇; simp_arith[Nat.add_eq_zero] at h₅; exact h₅.right
- have h₁₀ : o = 1 := by simp_arith[h₁₁] at h₅; assumption
- simp only
- rw[h₆]
- have h₁₂ := h₁.right.right.left
- unfold HeapPredicate.leOrLeaf at h₁₂
- cases o ; contradiction;
- case succ =>
- exact h₁₂
- else by
- have h₆ : n > 2 := by omega
- have h₇ := heapUpdateRootPossibleRootValues3 le value heap h₆
- simp at h₇
- unfold HeapPredicate at h₁
- cases h₇
- case inl h₇ =>
- have h₈ : le (heap.root h₂) ( (heapUpdateRoot le value heap h₂).fst.root h₂) := by simp only [h₇, h₃]
- unfold HeapPredicate.leOrLeaf
- split
- · rfl
- · exact h₈
- case inr h₇ =>
- cases h₇
- case inl h₇ | inr h₇ =>
- unfold HeapPredicate.leOrLeaf
- split at h₁
- contradiction
- simp_all
- case h_2 o p v l r _ _ _ =>
- cases o
- omega
- cases p
- omega
- have h₈ := h₁.right.right.left
- have h₉ := h₁.right.right.right
- assumption
-
-private theorem CompleteTree.heapUpdateRootIsHeapLeRootAuxLe {α : Type u} (le : α → α → Bool) {a b c : α} (h₁ : transitive_le le) (h₂ : total_le le) (h₃ : le b c) : ¬le a c ∨ ¬ le a b → le b a
-| .inr h₅ => not_le_imp_le h₂ _ _ h₅
-| .inl h₅ => h₁ b c a ⟨h₃,not_le_imp_le h₂ _ _ h₅⟩
-
-theorem CompleteTree.heapUpdateRootIsHeap {α : Type u} {n: Nat} (le : α → α → Bool) (value : α) (heap : CompleteTree α n) (h₁ : n > 0) (h₂ : HeapPredicate heap le) (h₃ : transitive_le le) (h₄ : total_le le) : HeapPredicate (heap.heapUpdateRoot le value h₁).fst le := by
- unfold heapUpdateRoot
- split
- rename_i o p v l r h₇ h₈ h₉ heq
- exact
- if h₁₀ : o = 0 then by
- simp only [Nat.add_eq, Nat.succ_eq_add_one, h₁₀, ↓reduceDite]
- unfold HeapPredicate at h₂ ⊢
- simp only [h₂, true_and]
- unfold HeapPredicate.leOrLeaf
- have : p = 0 := by rw[h₁₀, Nat.le_zero_eq] at h₇; assumption
- apply And.intro
- case left => match o, l with
- | Nat.zero, _ => trivial
- case right => match p, r with
- | Nat.zero, _ => trivial
- else if h₁₁ : p = 0 then by
- simp only [↓reduceDite, h₁₀, h₁₁]
- cases h₉ : le value (root l (_ : 0 < o)) <;> simp
- case true =>
- unfold HeapPredicate at *
- simp only [h₂, true_and]
- unfold HeapPredicate.leOrLeaf
- apply And.intro
- case right => match p, r with
- | Nat.zero, _ => trivial
- case left => match o, l with
- | Nat.succ _, _ => assumption
- case false =>
- rw[heapUpdateRootReturnsRoot]
- have h₁₂ : le (l.root (Nat.zero_lt_of_ne_zero h₁₀)) value := by simp[h₉, h₄, not_le_imp_le]
- have h₁₃ : o = 1 := Nat.le_antisymm (by simp_arith[h₁₁] at h₈; exact h₈) (Nat.succ_le_of_lt (Nat.zero_lt_of_ne_zero h₁₀))
- unfold HeapPredicate at *
- simp only [h₂, true_and] --closes one sub-goal
- apply And.intro <;> try apply And.intro
- case right.right => match p, r with
- | 0, .leaf => simp[HeapPredicate.leOrLeaf]
- case right.left =>
- simp only [HeapPredicate.leOrLeaf]
- cases o <;> simp only [Nat.succ_eq_add_one, heapUpdateRootPossibleRootValues1, h₁₃, h₁₂]
- case left =>
- apply heapUpdateRootIsHeap
- exact h₂.left
- exact h₃
- exact h₄
- else if h₁₂ : le value (root l (Nat.zero_lt_of_ne_zero h₁₀)) ∧ le value (root r (Nat.zero_lt_of_ne_zero h₁₁)) then by
- unfold HeapPredicate at *
- simp only [↓reduceDite, and_self, ↓reduceIte, true_and, h₁₀, h₁₁, h₁₂, h₂]
- unfold HeapPredicate.leOrLeaf
- cases o
- · contradiction
- · cases p
- · contradiction
- · assumption
- else by
- simp only [↓reduceDite, ↓reduceIte, h₁₀, h₁₁, h₁₂]
- have h₁₃ : ¬le value (root l _) ∨ ¬le value (root r _) := (Decidable.not_and_iff_or_not (le value (root l (Nat.zero_lt_of_ne_zero h₁₀)) = true) (le value (root r (Nat.zero_lt_of_ne_zero h₁₁)) = true)).mp h₁₂
- cases h₁₄ : le (root l (_ : 0 < o)) (root r (_ : 0 < p))
- <;> simp only [Bool.false_eq_true, ↓reduceIte]
- <;> unfold HeapPredicate at *
- <;> simp only [true_and, h₂]
- <;> apply And.intro
- <;> try apply And.intro
- case false.left | true.left =>
- apply heapUpdateRootIsHeap
- <;> simp only [h₂, h₃, h₄]
- case false.right.left =>
- unfold HeapPredicate.leOrLeaf
- have h₁₅ : le (r.root _) (l.root _) = true := not_le_imp_le h₄ (l.root _) (r.root _) $ (Bool.not_eq_true $ le (root l (_ : 0 < o)) (root r (_ : 0 < p))).substr h₁₄
- simp only[heapUpdateRootReturnsRoot]
- cases o <;> simp only[h₁₅]
- case true.right.right =>
- unfold HeapPredicate.leOrLeaf
- simp only[heapUpdateRootReturnsRoot]
- cases p <;> simp only[h₁₄]
- case false.right.right =>
- have h₁₅ : le (r.root _) (l.root _) = true := not_le_imp_le h₄ (l.root _) (r.root _) $ (Bool.not_eq_true $ le (root l (_ : 0 < o)) (root r (_ : 0 < p))).substr h₁₄
- have h₁₆ : le (r.root _) value := heapUpdateRootIsHeapLeRootAuxLe le h₃ h₄ h₁₅ h₁₃
- simp only [heapUpdateRootReturnsRoot, heapUpdateRootIsHeapLeRootAux, h₂, h₁₆]
- case true.right.left =>
- have h₁₆ : le (l.root _) value := heapUpdateRootIsHeapLeRootAuxLe le h₃ h₄ h₁₄ h₁₃.symm
- simp only [heapUpdateRootReturnsRoot, heapUpdateRootIsHeapLeRootAux, h₂, h₁₆]
-
-private theorem CompleteTree.heapUpdateAtIsHeapLeRootAux_RootLeValue {α : Type u} {n : Nat} (le : α → α → Bool) (index : Fin n) (value : α) (heap : CompleteTree α n) (h₁ : HeapPredicate heap le) (h₂ : n > 0) (h₃ : le (root heap h₂) value) (h₄ : total_le le) : HeapPredicate.leOrLeaf le (root heap h₂) (heapUpdateAt le index value heap h₂).fst := by
- unfold heapUpdateAt
- split
- case isTrue => exact heapUpdateRootIsHeapLeRootAux le value heap h₁ h₂ h₃
- case isFalse hi =>
- split
- rename_i o p v l r h₆ h₇ h₈ index h₁ h₅
- cases h₉ : le v value <;> simp (config := { ground := true }) only
- case false => rw[root_unfold] at h₃; exact absurd h₃ ((Bool.not_eq_true (le v value)).substr h₉)
- case true =>
- rw[root_unfold]
- split
- <;> simp![reflexive_le, h₄]
-
-private theorem CompleteTree.heapUpdateAtIsHeapLeRootAux_ValueLeRoot {α : Type u} {n : Nat} (le : α → α → Bool) (index : Fin n) (value : α) (heap : CompleteTree α n) (h₁ : HeapPredicate heap le) (h₂ : n > 0) (h₃ : le value (root heap h₂)) (h₄ : total_le le) (h₅ : transitive_le le) : HeapPredicate.leOrLeaf le value (heapUpdateAt le index value heap h₂).fst := by
- unfold heapUpdateAt
- split
- <;> rename_i h₉
- case isTrue =>
- unfold heapUpdateRoot
- split
- rename_i o p v l r h₆ h₇ h₈ h₂
- cases o <;> cases p <;> simp only [↓reduceDite,HeapPredicate.leOrLeaf, root_unfold, h₄, reflexive_le]
- <;> unfold HeapPredicate at h₁
- <;> have h₁₀ : le value $ l.root (by omega) := h₅ value v (l.root _) ⟨h₃, h₁.right.right.left⟩
- simp only [↓reduceIte, Nat.add_zero, h₁₀, root_unfold, h₄, reflexive_le]
- have h₁₁ : le value $ r.root (by omega) := h₅ value v (r.root _) ⟨h₃, h₁.right.right.right⟩
- simp only [↓reduceIte, h₁₀, h₁₁, and_self, root_unfold, h₄, reflexive_le]
- case isFalse =>
- split
- rename_i o p v l r h₆ h₇ h₈ index h₂ hi
- cases le v value
- <;> simp (config := { ground := true }) only [root_unfold, Nat.pred_eq_sub_one] at h₃ ⊢
- <;> split
- <;> unfold HeapPredicate.leOrLeaf
- <;> simp only [root_unfold, h₃, h₄, reflexive_le]
-
-theorem CompleteTree.heapUpdateAtIsHeap {α : Type u} {n : Nat} (le : α → α → Bool) (index : Fin n) (value : α) (heap : CompleteTree α n) (h₁ : n > 0) (h₂ : HeapPredicate heap le) (h₃ : transitive_le le) (h₄ : total_le le) : HeapPredicate (heap.heapUpdateAt le index value h₁).fst le := by
- unfold heapUpdateAt
- split
- case isTrue h₅ =>
- exact heapUpdateRootIsHeap le value heap h₁ h₂ h₃ h₄
- case isFalse h₅ =>
- split
- rename_i o p v l r h₆ h₇ h₈ index h₁ h₅
- cases h₁₀ : le v value <;> simp (config := {ground := true}) -- this could probably be solved without this split, but readability...
- <;> split
- <;> rename_i h -- h is the same name as used in the function
- <;> unfold HeapPredicate at h₂ ⊢
- <;> simp only [h₂, and_true, true_and]
- case false.isFalse =>
- have h₁₀ := not_le_imp_le h₄ v value (Bool.eq_false_iff.mp h₁₀)
- have h₁₄ : p > 0 := by cases p; exact absurd (Nat.lt_succ.mp index.isLt) h; exact Nat.zero_lt_succ _
- apply And.intro <;> try apply And.intro
- case left => exact heapUpdateAtIsHeap le ⟨index.val - o - 1, _⟩ v r h₁₄ h₂.right.left h₃ h₄
- case right.left => exact HeapPredicate.leOrLeaf_transitive h₃ h₁₀ h₂.right.right.left
- case right.right =>
- have h₁₁: HeapPredicate (heapUpdateAt le ⟨index.val - o - 1, (by omega)⟩ v r h₁₄).fst le :=
- (heapUpdateAtIsHeap le ⟨index.val - o - 1, (by omega)⟩ v r _ h₂.right.left h₃ h₄)
- cases h₁₂ : le v (r.root h₁₄)
- case false =>
- cases p
- exact absurd (Nat.lt_succ.mp index.isLt) h
- exact absurd h₂.right.right.right ((Bool.eq_false_iff).mp h₁₂)
- case true =>
- have h₁₃ := heapUpdateAtIsHeapLeRootAux_ValueLeRoot le ⟨index.val - o - 1, (by omega)⟩ v r h₂.right.left (by omega) h₁₂ h₄ h₃
- apply HeapPredicate.leOrLeaf_transitive h₃ _ h₁₃
- exact h₁₀
- case false.isTrue =>
- have h₁₀ := not_le_imp_le h₄ v value (Bool.eq_false_iff.mp h₁₀)
- have h₁₄ : o > 0 := by cases o; simp at h₅ h; exact absurd (Fin.val_inj.mp h : index = 0) h₅; exact Nat.zero_lt_succ _
- apply And.intro <;> try apply And.intro
- case left => exact heapUpdateAtIsHeap le ⟨index.val - 1, _⟩ v l h₁₄ h₂.left h₃ h₄
- case right.right => exact HeapPredicate.leOrLeaf_transitive h₃ h₁₀ h₂.right.right.right
- case right.left =>
- have h₁₁: HeapPredicate (heapUpdateAt le ⟨index.val - 1, (_)⟩ v l h₁₄).fst le :=
- (heapUpdateAtIsHeap le ⟨index.val - 1, (by omega)⟩ v l _ h₂.left h₃ h₄)
- cases h₁₂ : le v (l.root h₁₄)
- case false =>
- cases o
- contradiction -- h₁₄ is False
- exact absurd h₂.right.right.left ((Bool.eq_false_iff).mp h₁₂)
- case true =>
- have h₁₃ := heapUpdateAtIsHeapLeRootAux_ValueLeRoot le ⟨index.val - 1, (by omega)⟩ v l h₂.left (by omega) h₁₂ h₄ h₃
- apply HeapPredicate.leOrLeaf_transitive h₃ _ h₁₃
- exact h₁₀
- case true.isFalse =>
- have h₁₄ : p > 0 := by cases p; exact absurd (Nat.lt_succ.mp index.isLt) h; exact Nat.zero_lt_succ _
- apply And.intro
- case left => exact heapUpdateAtIsHeap le ⟨index.val - o - 1, _⟩ value r h₁₄ h₂.right.left h₃ h₄
- case right =>
- have h₁₁: HeapPredicate (heapUpdateAt le ⟨index.val - o - 1, (by omega)⟩ v r (h₁₄)).fst le :=
- (heapUpdateAtIsHeap le ⟨index.val - o - 1, (by omega)⟩ v r _ h₂.right.left h₃ h₄)
- cases h₁₂ : le value (r.root h₁₄)
- case false =>
- have h₁₃ := heapUpdateAtIsHeapLeRootAux_RootLeValue le ⟨index.val - o - 1, (by omega)⟩ value r h₂.right.left (by omega) (not_le_imp_le h₄ value (r.root h₁₄) (Bool.eq_false_iff.mp h₁₂)) h₄
- apply HeapPredicate.leOrLeaf_transitive h₃ _ h₁₃
- cases p
- contradiction -- h₁₄ is False
- exact h₂.right.right.right
- case true =>
- have h₁₃ := heapUpdateAtIsHeapLeRootAux_ValueLeRoot le ⟨index.val - o - 1, (by omega)⟩ value r h₂.right.left (by omega) h₁₂ h₄ h₃
- apply HeapPredicate.leOrLeaf_transitive h₃ _ h₁₃
- exact h₁₀
- case true.isTrue =>
- have h₁₄ : o > 0 := by cases o; simp at h₅ h; exact absurd (Fin.val_inj.mp h : index = 0) h₅; exact Nat.zero_lt_succ _
- apply And.intro
- case left => exact heapUpdateAtIsHeap le ⟨index.val - 1, _⟩ value l h₁₄ h₂.left h₃ h₄
- case right =>
- have h₁₁: HeapPredicate (heapUpdateAt le ⟨index.val - 1, (by omega)⟩ v l h₁₄).fst le :=
- (heapUpdateAtIsHeap le ⟨index.val - 1, (by omega)⟩ v l _ h₂.left h₃ h₄)
- cases h₁₂ : le value (l.root h₁₄)
- case false =>
- have h₁₃ := heapUpdateAtIsHeapLeRootAux_RootLeValue le ⟨index.val - 1, (by omega)⟩ value l h₂.left (by omega) (not_le_imp_le h₄ value (l.root h₁₄) (Bool.eq_false_iff.mp h₁₂)) h₄
- apply HeapPredicate.leOrLeaf_transitive h₃ _ h₁₃
- cases o
- contradiction -- h₁₄ is False
- exact h₂.right.right.left
- case true =>
- have h₁₃ := heapUpdateAtIsHeapLeRootAux_ValueLeRoot le ⟨index.val - 1, (by omega)⟩ value l h₂.left (by omega) h₁₂ h₄ h₃
- apply HeapPredicate.leOrLeaf_transitive h₃ _ h₁₃
- exact h₁₀
-
-def CompleteTree.heapPop {α : Type u} {n : Nat} (le : α → α → Bool) (heap : CompleteTree α (n+1)) : CompleteTree α n × α :=
- let l := heap.heapRemoveLast
- if p : n > 0 then
- heapUpdateRoot le l.snd l.fst p
- else
- l
-
-theorem CompleteTree.heapPopIsHeap {α : Type u} {n : Nat} (le : α → α → Bool) (heap : CompleteTree α (n+1)) (h₁ : HeapPredicate heap le) (wellDefinedLe : transitive_le le ∧ total_le le) : HeapPredicate (heap.heapPop le).fst le := by
- have h₂ : HeapPredicate heap.heapRemoveLast.fst le := heapRemoveLastIsHeap h₁ wellDefinedLe.left wellDefinedLe.right
- unfold heapPop
- cases n <;> simp[h₂, heapUpdateRootIsHeap, wellDefinedLe]
-
-/--Removes the element at a given index. Use `CompleteTree.indexOf` to find the respective index.-/
-def CompleteTree.heapRemoveAt {α : Type u} {n : Nat} (le : α → α → Bool) (index : Fin (n+1)) (heap : CompleteTree α (n+1)) : CompleteTree α n × α :=
- --Since we cannot even temporarily break the completeness property, we go with the
- --version from Wikipedia: We first remove the last element, and then update values in the tree
- --indices are depth first, but "last" means last element of the complete tree.
- --sooo:
- if index_ne_zero : index = 0 then
- heapPop le heap
- else
- let (remaining_tree, removed_element, removed_index) := heap.heapRemoveLastWithIndex
- if p : index = removed_index then
- (remaining_tree, removed_element)
- else
- have n_gt_zero : n > 0 := by
- cases n
- case succ nn => exact Nat.zero_lt_succ nn
- case zero => omega
- if index_lt_lastIndex : index ≥ removed_index then
- let index := index.pred index_ne_zero
- heapUpdateAt le index removed_element remaining_tree n_gt_zero
- else
- let h₁ : index < n := by omega
- let index : Fin n := ⟨index, h₁⟩
- heapUpdateAt le index removed_element remaining_tree n_gt_zero
-
-theorem CompleteTree.heapRemoveAtIsHeap {α : Type u} {n : Nat} (le : α → α → Bool) (index : Fin (n+1)) (heap : CompleteTree α (n+1)) (h₁ : HeapPredicate heap le) (wellDefinedLe : transitive_le le ∧ total_le le) : HeapPredicate (heap.heapRemoveAt le index).fst le := by
- have h₂ : HeapPredicate heap.heapRemoveLastWithIndex.fst le := heapRemoveLastWithIndexIsHeap h₁ wellDefinedLe.left wellDefinedLe.right
- unfold heapRemoveAt
- split
- case isTrue => exact heapPopIsHeap le heap h₁ wellDefinedLe
- case isFalse h₃ =>
- cases h: (index = heap.heapRemoveLastWithIndex.snd.snd : Bool)
- <;> simp_all
- split
- <;> apply heapUpdateAtIsHeap <;> simp_all
-
-------------------------------------------------------------------------------------------------------
/-
diff --git a/BinaryHeap/CompleteTree/AdditionalProofs.lean b/BinaryHeap/CompleteTree/AdditionalProofs.lean
new file mode 100644
index 0000000..128dcd0
--- /dev/null
+++ b/BinaryHeap/CompleteTree/AdditionalProofs.lean
@@ -0,0 +1,84 @@
+import BinaryHeap.CompleteTree.Lemmas
+import BinaryHeap.CompleteTree.HeapOperations
+
+namespace BinaryHeap
+
+/--Shows that the index and value returned by heapRemoveLastWithIndex are consistent.-/
+private theorem CompleteTree.heapRemoveLastWithIndexReturnsItemAtIndex {α : Type u} {o : Nat} (heap : CompleteTree α (o+1)) : heap.get heap.heapRemoveLastWithIndex.snd.snd = heap.heapRemoveLastWithIndex.snd.fst := by
+ unfold CompleteTree.heapRemoveLastWithIndex CompleteTree.Internal.heapRemoveLastAux
+ split
+ rename_i n m v l r m_le_n max_height_difference subtree_full
+ simp only [Nat.add_eq, Fin.zero_eta, Fin.isValue, decide_eq_true_eq, Fin.castLE_succ]
+ split
+ case isTrue n_m_zero =>
+ unfold get
+ split
+ case h_1 nn mm vv ll rr mm_le_nn _ _ _ _ he₁ he₂ =>
+ have h₁ : n = 0 := And.left $ Nat.add_eq_zero.mp n_m_zero.symm
+ have h₂ : m = 0 := And.right $ Nat.add_eq_zero.mp n_m_zero.symm
+ have h₃ : nn = 0 := And.left (Nat.add_eq_zero.mp $ Eq.symm $ (Nat.zero_add 0).subst (motive := λx ↦ x = nn + mm) $ h₂.subst (motive := λx ↦ 0 + x = nn + mm) (h₁.subst (motive := λx ↦ x + m = nn + mm) he₁))
+ have h₄ : mm = 0 := And.right (Nat.add_eq_zero.mp $ Eq.symm $ (Nat.zero_add 0).subst (motive := λx ↦ x = nn + mm) $ h₂.subst (motive := λx ↦ 0 + x = nn + mm) (h₁.subst (motive := λx ↦ x + m = nn + mm) he₁))
+ subst n m nn mm
+ exact And.left $ CompleteTree.branch.inj (eq_of_heq he₂.symm)
+ case h_2 =>
+ omega -- to annoying to deal with Fin.ofNat... There's a hypothesis that says 0 = ⟨1,_⟩.
+ case isFalse n_m_not_zero =>
+ unfold get
+ split
+ case h_1 nn mm vv ll rr mm_le_nn max_height_difference_2 subtree_full2 _ he₁ he₂ he₃ =>
+ --aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+ --okay, I know that he₁ is False.
+ --but reducing this wall of text to something the computer understands - I am frightened.
+ exfalso
+ revert he₁
+ split
+ case' isTrue => cases l; case leaf hx => exact absurd hx.left $ Nat.not_lt_zero m
+ all_goals
+ apply Fin.ne_of_val_ne
+ simp only [Fin.isValue, Fin.val_succ, Fin.coe_castLE, Fin.coe_addNat, Nat.add_one_ne_zero, not_false_eq_true]
+ --okay, this wasn't that bad
+ case h_2 j j_lt_n_add_m nn mm vv ll rr mm_le_nn max_height_difference_2 subtree_full2 heap he₁ he₂ he₃ =>
+ --he₁ relates j to the other indices. This is the important thing here.
+ --it should be reducible to j = (l or r).heap.heapRemoveLastWithIndex.snd.snd
+ --or something like it.
+
+ --but first, let's get rid of mm and nn, and vv while at it.
+ -- which are equal to m, n, v, but we need to deduce this from he₃...
+ have : n = nn := heqSameLeftLen (congrArg (·+1) he₂) (by simp_arith) he₃
+ have : m = mm := heqSameRightLen (congrArg (·+1) he₂) (by simp_arith) he₃
+ subst nn mm
+ simp only [heq_eq_eq, branch.injEq] at he₃
+ -- yeah, no more HEq fuglyness!
+ have : v = vv := he₃.left
+ have : l = ll := he₃.right.left
+ have : r = rr := he₃.right.right
+ subst vv ll rr
+ split at he₁
+ <;> rename_i goLeft
+ <;> simp only [goLeft, and_self, ↓reduceDite, Fin.isValue]
+ case' isTrue =>
+ cases l;
+ case leaf => exact absurd goLeft.left $ Nat.not_lt_zero m
+ rename_i o p _ _ _ _ _ _ _
+ case' isFalse =>
+ cases r;
+ case leaf => simp (config := { ground := true }) only [and_true, Nat.not_lt, Nat.le_zero_eq] at goLeft;
+ exact absurd ((Nat.add_zero n).substr goLeft.symm) n_m_not_zero
+ all_goals
+ have he₁ := Fin.val_eq_of_eq he₁
+ simp only [Fin.isValue, Fin.val_succ, Fin.coe_castLE, Fin.coe_addNat, Nat.reduceEqDiff] at he₁
+ have : max_height_difference_2 = max_height_difference := rfl
+ have : subtree_full2 = subtree_full := rfl
+ subst max_height_difference_2 subtree_full2
+ rename_i del1 del2
+ clear del1 del2
+ case' isTrue =>
+ have : j < o + p + 1 := by omega --from he₁. It has j = (blah : Fin (o+p+1)).val
+ case' isFalse =>
+ have : ¬j<n := by omega --from he₁. It has j = something + n.
+ all_goals
+ simp only [this, ↓reduceDite, Nat.pred_succ, Fin.isValue]
+ subst j -- overkill, but unlike rw it works
+ simp only [Nat.pred_succ, Fin.isValue, Nat.add_sub_cancel, Fin.eta]
+ apply heapRemoveLastWithIndexReturnsItemAtIndex
+ done
diff --git a/BinaryHeap/CompleteTree/Basic.lean b/BinaryHeap/CompleteTree/Basic.lean
new file mode 100644
index 0000000..52ea93d
--- /dev/null
+++ b/BinaryHeap/CompleteTree/Basic.lean
@@ -0,0 +1,48 @@
+namespace BinaryHeap
+
+inductive CompleteTree (α : Type u) : Nat → Type u
+ | leaf : CompleteTree α 0
+ | branch :
+ (val : α)
+ → (left : CompleteTree α n)
+ → (right : CompleteTree α m)
+ → m ≤ n
+ → n < 2*(m+1)
+ → (n+1).isPowerOfTwo ∨ (m+1).isPowerOfTwo
+ → CompleteTree α (n+m+1)
+deriving Repr
+
+/--Creates an empty CompleteTree.-/
+abbrev CompleteTree.empty {α : Type u} := CompleteTree.leaf (α := α)
+
+/-- Returns the element stored in the root node. -/
+def CompleteTree.root (tree : CompleteTree α n) (_ : 0 < n) : α := match tree with
+| .branch a _ _ _ _ _ => a
+
+/-- Same as CompleteTree.root, but a bit more ergonomic to use. However, CompleteTree.root is better suited for proofs-/
+def CompleteTree.root' (tree : CompleteTree α (n+1)) : α := tree.root (Nat.zero_lt_succ n)
+
+/-- Extracts the element count. For when pattern matching is too much work. -/
+def CompleteTree.length : CompleteTree α n → Nat := λ_ ↦ n
+
+/-- Returns the lenght of the left sub-tree. Mostly exists as a helper for expressing the return type of CompleteTree.left -/
+def CompleteTree.leftLen (tree : CompleteTree α n) (_ : n > 0) : Nat := match n, tree with
+| (_+_), .branch _ l _ _ _ _ => l.length
+
+def CompleteTree.leftLen' (tree : CompleteTree α (n+1)) : Nat := tree.leftLen (Nat.zero_lt_succ n)
+
+/-- Returns the lenght of the right sub-tree. Mostly exists as a helper for expressing the return type of CompleteTree.right -/
+def CompleteTree.rightLen (tree : CompleteTree α n) (_ : n > 0) : Nat := match n, tree with
+| (_+_), .branch _ _ r _ _ _ => r.length
+
+def CompleteTree.rightLen' (tree : CompleteTree α (n+1)) : Nat := tree.rightLen (Nat.zero_lt_succ n)
+
+def CompleteTree.left (tree : CompleteTree α n) (h₁ : n > 0) : CompleteTree α (tree.leftLen h₁) := match n, tree with
+| (_+_), .branch _ l _ _ _ _ => l
+
+def CompleteTree.left' (tree : CompleteTree α (n+1)) : CompleteTree α (tree.leftLen (Nat.zero_lt_succ n)) := tree.left (Nat.zero_lt_succ n)
+
+def CompleteTree.right (tree : CompleteTree α n) (h₁ : n > 0) : CompleteTree α (tree.rightLen h₁) := match n, tree with
+| (_+_), .branch _ _ r _ _ _ => r
+
+def CompleteTree.right' (tree : CompleteTree α (n+1)) : CompleteTree α (tree.rightLen (Nat.zero_lt_succ n)) := tree.right (Nat.zero_lt_succ n)
diff --git a/BinaryHeap/CompleteTree/HeapOperations.lean b/BinaryHeap/CompleteTree/HeapOperations.lean
new file mode 100644
index 0000000..f16d89d
--- /dev/null
+++ b/BinaryHeap/CompleteTree/HeapOperations.lean
@@ -0,0 +1,352 @@
+import BinaryHeap.CompleteTree.Basic
+import BinaryHeap.CompleteTree.NatLemmas
+
+namespace BinaryHeap
+
+----------------------------------------------------------------------------------------------
+-- heapPush
+
+/-Lemma needed to show completeness of tree in heapPush.-/
+private theorem power_of_two_mul_two_lt {n m : Nat} (h₁ : m.isPowerOfTwo) (h₂ : n < 2*m) (h₃ : ¬(n+1).isPowerOfTwo) : n+1 < 2*m :=
+ if h₄ : n+1 > 2*m then
+ absurd h₄ ((Nat.not_lt_eq (2*m) (n+1)).substr $ Nat.succ_le_of_lt h₂)
+ else if h₅ : n+1 = 2*m then
+ have h₆ : (2*m).isPowerOfTwo := (Nat.mul_comm 2 m).substr $ Nat.mul2_isPowerOfTwo_of_isPowerOfTwo h₁
+ have h₇ : (n+1).isPowerOfTwo := h₅.substr h₆
+ absurd h₇ h₃
+ else
+ Nat.lt_of_le_of_ne ((Nat.not_lt_eq _ _).mp h₄) h₅
+
+/--Adds a new element to a given CompleteTree.-/
+def CompleteTree.heapPush (le : α → α → Bool) (elem : α) (heap : CompleteTree α o) : CompleteTree α (o+1) :=
+ match o, heap with
+ | 0, .leaf => CompleteTree.branch elem (CompleteTree.leaf) (CompleteTree.leaf) (by simp) (by simp) (by simp[Nat.one_isPowerOfTwo])
+ | (n+m+1), .branch a left right p max_height_difference subtree_complete =>
+ let (elem, a) := if le elem a then (a, elem) else (elem, a)
+ -- okay, based on n and m we know if we want to add left or right.
+ -- the left tree is full, if (n+1) is a power of two AND n != m
+ let leftIsFull := (n+1).nextPowerOfTwo = n+1
+ if r : m < n ∧ leftIsFull then
+ have s : (m + 1 < n + 1) = (m < n) := by simp_arith
+ have q : m + 1 ≤ n := by apply Nat.le_of_lt_succ
+ rewrite[Nat.succ_eq_add_one]
+ rewrite[s]
+ simp[r]
+ have difference_decreased := Nat.le_succ_of_le $ Nat.le_succ_of_le max_height_difference
+ let result := branch a left (right.heapPush le elem) (q) difference_decreased (by simp[(Nat.power_of_two_iff_next_power_eq (n+1)), r])
+ result
+ else
+ have q : m ≤ n+1 := by apply (Nat.le_of_succ_le)
+ simp_arith[p]
+ have right_is_power_of_two : (m+1).isPowerOfTwo :=
+ if s : n = m then by
+ rewrite[s] at subtree_complete
+ simp at subtree_complete
+ exact subtree_complete
+ else if h₁ : leftIsFull then by
+ rewrite[Decidable.not_and_iff_or_not (m<n) leftIsFull] at r
+ cases r
+ case inl h₂ => rewrite[Nat.not_lt_eq] at h₂
+ have h₃ := Nat.not_le_of_gt $ Nat.lt_of_le_of_ne h₂ s
+ contradiction
+ case inr h₂ => simp at h₂
+ contradiction
+ else by
+ simp[leftIsFull] at h₁
+ rewrite[←Nat.power_of_two_iff_next_power_eq] at h₁
+ cases subtree_complete
+ case inl => contradiction
+ case inr => trivial
+ have still_in_range : n + 1 < 2 * (m + 1) := by
+ rewrite[Decidable.not_and_iff_or_not (m<n) leftIsFull] at r
+ cases r
+ case inl h₁ => rewrite[Nat.not_gt_eq n m] at h₁
+ simp_arith[Nat.le_antisymm h₁ p]
+ case inr h₁ => simp[←Nat.power_of_two_iff_next_power_eq, leftIsFull] at h₁
+ simp[h₁] at subtree_complete
+ exact power_of_two_mul_two_lt subtree_complete max_height_difference h₁
+ let result := branch a (left.heapPush le elem) right q still_in_range (Or.inr right_is_power_of_two)
+ have letMeSpellItOutForYou : n + 1 + m + 1 = n + m + 1 + 1 := by simp_arith
+ letMeSpellItOutForYou ▸ result
+
+
+----------------------------------------------------------------------------------------------
+-- indexOf
+
+/--Helper function for CompleteTree.indexOf.-/
+private def CompleteTree.indexOfAux {α : Type u} (heap : CompleteTree α o) (pred : α → Bool) (currentIndex : Nat) : Option (Fin (o+currentIndex)) :=
+ match o, heap with
+ | 0, .leaf => none
+ | (n+m+1), .branch a left right _ _ _ =>
+ have sum_n_m_succ_curr : n + m.succ + currentIndex > 0 := Nat.add_pos_left (Nat.add_pos_right n (Nat.succ_pos m)) currentIndex
+ if pred a then
+ let result := Fin.ofNat' currentIndex sum_n_m_succ_curr
+ some result
+ else
+ let found_left := left.indexOfAux pred (currentIndex + 1)
+ let found_left : Option (Fin (n+m+1+currentIndex)) := found_left.map λ a ↦ Fin.ofNat' a sum_n_m_succ_curr
+ let found_right :=
+ found_left
+ <|>
+ (right.indexOfAux pred (currentIndex + n + 1)).map ((λ a ↦ Fin.ofNat' a sum_n_m_succ_curr) : _ → Fin (n+m+1+currentIndex))
+ found_right
+
+/--Finds the first occurance of a given element in the heap and returns its index. Indices are depth first.-/
+def CompleteTree.indexOf {α : Type u} (heap : CompleteTree α o) (pred : α → Bool) : Option (Fin o) :=
+ indexOfAux heap pred 0
+
+
+----------------------------------------------------------------------------------------------
+-- get
+
+/--Returns the lement at the given index. Indices are depth first.-/
+def CompleteTree.get {α : Type u} {n : Nat} (index : Fin (n+1)) (heap : CompleteTree α (n+1)) : α :=
+ match h₁ : index, h₂ : n, heap with
+ | 0, (_+_), .branch v _ _ _ _ _ => v
+ | ⟨j+1,h₃⟩, (o+p), .branch _ l r _ _ _ =>
+ if h₄ : j < o then
+ match o with
+ | (oo+1) => get ⟨j, h₄⟩ l
+ else
+ have h₅ : n - o = p := Nat.sub_eq_of_eq_add $ (Nat.add_comm o p).subst h₂
+ have : p ≠ 0 :=
+ have h₆ : o < n := Nat.lt_of_le_of_lt (Nat.ge_of_not_lt h₄) (Nat.lt_of_succ_lt_succ h₃)
+ h₅.symm.substr $ Nat.sub_ne_zero_of_lt h₆
+ have h₆ : j - o < p := h₅.subst $ Nat.sub_lt_sub_right (Nat.ge_of_not_lt h₄) $ Nat.lt_of_succ_lt_succ h₃
+ have _termination : j - o < index.val := (Fin.val_inj.mpr h₁).substr (Nat.sub_lt_succ j o)
+ match p with
+ | (pp + 1) => get ⟨j - o, h₆⟩ r
+
+----------------------------------------------------------------------------------------------
+-- heapRemoveLast (with Index)
+
+/-- Helper for heapRemoveLastAux -/
+private theorem power_of_two_mul_two_le {n m : Nat} (h₁ : (n+1).isPowerOfTwo) (h₂ : n < 2*(m+1)) (h₃ : ¬(m+1).isPowerOfTwo) (h₄ : m > 0): n < 2*m :=
+ if h₅ : n > 2*m then
+ have h₆ : n + 1 = 2*(m+1) := congrArg Nat.succ $ Nat.eq_of_le_of_lt_succ h₅ h₂
+ have h₇ : (2*(m+1)).isPowerOfTwo := h₆.subst h₁
+ have h₈ : (m+1).isPowerOfTwo := (Nat.mul2_ispowerOfTwo_iff_isPowerOfTwo (m+1)).mpr h₇
+ absurd h₈ h₃
+ else if h₆ : n = 2*m then
+ -- since (n+1) is a power of 2, n cannot be an even number, but n = 2*m means it's even
+ -- ha, thought I wouldn't see that, didn't you? Thought you're smarter than I, computer?
+ have h₇ : n > 0 := h₆.substr $ (Nat.mul_lt_mul_left (Nat.succ_pos 1 : 2 > 0)).mpr h₄
+ have h₈ : n ≠ 0 := Ne.symm $ Nat.ne_of_lt h₇
+ have h₉ : (n+1).isEven := (Nat.isPowerOfTwo_even_or_one h₁).resolve_left
+ $ if h : n+1=1 then absurd (Nat.succ.inj h) h₈ else h
+ have h₉ : n.isOdd := Nat.pred_even_odd h₉ (by simp_arith[h₇])
+ have h₁₀ : ¬n.isOdd := Nat.even_not_odd_even.mp $ Nat.mul_2_is_even h₆
+ absurd h₉ h₁₀
+ else
+ have h₅ : n ≤ 2*m := (Nat.not_gt_eq _ _).mp h₅
+ have h₅ : n=2*m ∨ n < 2*m := Nat.eq_or_lt_of_le h₅
+ h₅.resolve_left h₆
+
+/-- Helper for heapRemoveLastAux -/
+private theorem CompleteTree.removeRightRightNotEmpty {n m : Nat} (m_gt_0_or_rightIsFull : m > 0 ∨ ((m+1).nextPowerOfTwo = m+1 : Bool)) (h₁ : 0 ≠ n + m) (h₂ : ¬(m < n ∧ ((m+1).nextPowerOfTwo = m+1 : Bool))) : m > 0 :=
+ match m_gt_0_or_rightIsFull with
+ | Or.inl h => h
+ | Or.inr h => by
+ simp only [h, and_true, Nat.not_lt] at h₂
+ cases n
+ case zero => exact Nat.zero_lt_of_ne_zero $ (Nat.zero_add m).subst (motive := (·≠0)) h₁.symm
+ case succ q =>
+ cases m
+ . exact absurd h₂ $ Nat.not_succ_le_zero q
+ . exact Nat.succ_pos _
+
+/-- Helper for heapRemoveLastAux -/
+private theorem CompleteTree.removeRightLeftIsFull {n m : Nat} (r : ¬(m < n ∧ ((m+1).nextPowerOfTwo = m+1 : Bool))) (m_le_n : m ≤ n) (subtree_complete : (n + 1).isPowerOfTwo ∨ (m + 1).isPowerOfTwo) : (n+1).isPowerOfTwo := by
+ rewrite[Decidable.not_and_iff_or_not] at r
+ cases r
+ case inl h₁ => rewrite[Nat.not_lt_eq] at h₁
+ have h₂ := Nat.le_antisymm h₁ m_le_n
+ rewrite[←h₂] at subtree_complete
+ simp at subtree_complete
+ assumption
+ case inr h₁ => simp(config := {zetaDelta := true })[←Nat.power_of_two_iff_next_power_eq] at h₁
+ simp[h₁] at subtree_complete
+ assumption
+
+/-- Helper for heapRemoveLastAux -/
+private theorem CompleteTree.stillInRange {n m : Nat} (r : ¬(m < n ∧ ((m+1).nextPowerOfTwo = m+1 : Bool))) (m_le_n : m ≤ n) (m_gt_0 : m > 0) (leftIsFull : (n+1).isPowerOfTwo) (max_height_difference: n < 2 * (m + 1)) : n < 2*m := by
+ rewrite[Decidable.not_and_iff_or_not] at r
+ cases r with
+ | inl h₁ => have m_eq_n : m = n := Nat.le_antisymm m_le_n (Nat.not_lt.mp h₁)
+ have m_lt_2_m : m < 2 * m := (Nat.one_mul m).subst (motive := λ x ↦ x < 2 * m) $ Nat.mul_lt_mul_of_pos_right Nat.one_lt_two m_gt_0
+ exact m_eq_n.subst (motive := λx ↦ x < 2 * m) m_lt_2_m
+ | inr h₁ => simp (config := { zetaDelta := true }) only [← Nat.power_of_two_iff_next_power_eq, decide_eq_true_eq] at h₁
+ apply power_of_two_mul_two_le <;> assumption
+
+/--
+ Removes the last element, and computes additional values via the supplied lambdas.
+
+ **BEWARE** that "last" here means the underlying complete tree. It is *not* the elemenent
+ at the largest index, nor is it the largest element in the heap.
+-/
+protected def CompleteTree.Internal.heapRemoveLastAux
+{α : Type u}
+{β : Nat → Type u}
+{o : Nat}
+(heap : CompleteTree α (o+1))
+(aux0 : α → (β 1))
+(auxl : {prev_size curr_size : Nat} → β prev_size → (h₁ : prev_size < curr_size) → β curr_size)
+(auxr : {prev_size curr_size : Nat} → β prev_size → (left_size : Nat) → (h₁ : prev_size + left_size < curr_size) → β curr_size)
+: (CompleteTree α o × (β (o+1)))
+:=
+ match o, heap with
+ | (n+m), .branch a (left : CompleteTree α n) (right : CompleteTree α m) m_le_n max_height_difference subtree_complete =>
+ if p : 0 = (n+m) then
+ (p▸CompleteTree.leaf, p▸aux0 a)
+ else
+ let rightIsFull : Bool := (m+1).nextPowerOfTwo = m+1
+ have m_gt_0_or_rightIsFull : m > 0 ∨ rightIsFull := by cases m <;> simp (config := { ground:=true })[rightIsFull]
+ if r : m < n ∧ rightIsFull then
+ --remove left
+ match n, left with
+ | (l+1), left =>
+ let ((newLeft : CompleteTree α l), res) := Internal.heapRemoveLastAux left aux0 auxl auxr
+ have q : l + m + 1 = l + 1 + m := Nat.add_right_comm l m 1
+ have s : m ≤ l := Nat.le_of_lt_succ r.left
+ have rightIsFull : (m+1).isPowerOfTwo := (Nat.power_of_two_iff_next_power_eq (m+1)).mpr $ decide_eq_true_eq.mp r.right
+ have l_lt_2_m_succ : l < 2 * (m+1) := Nat.lt_of_succ_lt max_height_difference
+ let res := auxl res (by simp_arith)
+ (q▸CompleteTree.branch a newLeft right s l_lt_2_m_succ (Or.inr rightIsFull), res)
+ else
+ --remove right
+ have m_gt_0 : m > 0 := removeRightRightNotEmpty m_gt_0_or_rightIsFull p r
+ let l := m.pred
+ have h₂ : l.succ = m := (Nat.succ_pred $ Nat.not_eq_zero_of_lt (Nat.gt_of_not_le $ Nat.not_le_of_gt m_gt_0))
+ let ((newRight : CompleteTree α l), res) := Internal.heapRemoveLastAux (h₂.symm▸right : CompleteTree α l.succ) aux0 auxl auxr
+ have leftIsFull : (n+1).isPowerOfTwo := removeRightLeftIsFull r m_le_n subtree_complete
+ have still_in_range : n < 2 * (l+1) := h₂.substr (p := λx ↦ n < 2 * x) $ stillInRange r m_le_n m_gt_0 leftIsFull max_height_difference
+ let res := auxr res n (by omega)
+ (h₂▸CompleteTree.branch a left newRight (Nat.le_of_succ_le (h₂▸m_le_n)) still_in_range (Or.inl leftIsFull), res)
+
+/--
+ Removes the last element in the complete Tree. This is **NOT** the element with the
+ largest index, nor is it the largest element in the heap.
+-/
+protected def CompleteTree.Internal.heapRemoveLast {α : Type u} {o : Nat} (heap : CompleteTree α (o+1)) : (CompleteTree α o × α) :=
+ Internal.heapRemoveLastAux heap id (λ(a : α) _ ↦ a) (λa _ _ ↦ a)
+
+/--
+ Removes the last element in the complete Tree. This is **NOT** the element with the
+ largest index, nor is it the largest element in the heap.
+
+ Also returns the index of the removed element.
+-/
+protected def CompleteTree.heapRemoveLastWithIndex {α : Type u} {o : Nat} (heap : CompleteTree α (o+1)) : (CompleteTree α o × α × Fin (o+1)) :=
+ Internal.heapRemoveLastAux heap (β := λn ↦ α × Fin n)
+ (λ(a : α) ↦ (a, Fin.mk 0 (Nat.succ_pos 0)))
+ (λ(a, prev_idx) h₁ ↦ (a, prev_idx.succ.castLE $ Nat.succ_le_of_lt h₁) )
+ (λ(a, prev_idx) left_size h₁ ↦ (a, (prev_idx.addNat left_size).succ.castLE $ Nat.succ_le_of_lt h₁))
+
+----------------------------------------------------------------------------------------------
+-- heapUpdateRoot
+
+/--
+ Helper for CompleteTree.heapUpdateAt. Makes proofing heap predicate work in Lean 4.9
+ -/
+def CompleteTree.heapUpdateRoot {α : Type u} {n : Nat} (le : α → α → Bool) (value : α) (heap : CompleteTree α n) (_ : n > 0) : CompleteTree α n × α :=
+match n, heap with
+ | (o+p+1), .branch v l r h₃ h₄ h₅ =>
+ if h₆ : o = 0 then
+ -- have : p = 0 := (Nat.le_zero_eq p).mp $ h₇.subst h₃ --not needed, left here for reference
+ (.branch value l r h₃ h₄ h₅, v)
+ else
+ have h₇ : o > 0 := Nat.zero_lt_of_ne_zero h₆
+ let lr := l.root h₇
+ if h₈ : p = 0 then
+ if le value lr then
+ (.branch value l r h₃ h₄ h₅, v)
+ else
+ -- We would not need to recurse further, because we know o = 1.
+ -- However, that would introduce type casts, what makes proving harder...
+ -- have h₉: o = 1 := Nat.le_antisymm (by simp_arith[h₈] at h₄; exact h₄) (Nat.succ_le_of_lt h₇)
+ let ln := heapUpdateRoot le value l h₇
+ (.branch ln.snd ln.fst r h₃ h₄ h₅, v)
+ else
+ have h₉ : p > 0 := Nat.zero_lt_of_ne_zero h₈
+ let rr := r.root h₉
+ if le value lr ∧ le value rr then
+ (.branch value l r h₃ h₄ h₅, v)
+ else if le lr rr then -- value is gt either left or right root. Move it down accordingly
+ let ln := heapUpdateRoot le value l h₇
+ (.branch ln.snd ln.fst r h₃ h₄ h₅, v)
+ else
+ let rn := heapUpdateRoot le value r h₉
+ (.branch rn.snd l rn.fst h₃ h₄ h₅, v)
+
+----------------------------------------------------------------------------------------------
+-- heapRemoveAt
+
+/--
+ Helper for CompleteTree.heapRemoveAt.
+ Removes the element at index, and instead inserts the given value.
+ Returns the element at index, and the resulting tree.
+ -/
+def CompleteTree.heapUpdateAt {α : Type u} {n : Nat} (le : α → α → Bool) (index : Fin n) (value : α) (heap : CompleteTree α n) (h₁ : n > 0) : CompleteTree α n × α :=
+ if h₂ : index == ⟨0,h₁⟩ then
+ heapUpdateRoot le value heap h₁
+ else
+ match n, heap with
+ | (o+p+1), .branch v l r h₃ h₄ h₅ =>
+ let (v, value) := if le v value then (v, value) else (value, v)
+ if h₆ : index ≤ o then
+ have h₇ : Nat.pred index.val < o := Nat.lt_of_lt_of_le (Nat.pred_lt $ Fin.val_ne_of_ne (ne_of_beq_false $ Bool.of_not_eq_true h₂)) h₆
+ let index_in_left : Fin o := ⟨index.val.pred, h₇⟩
+ have h₈ : 0 < o := Nat.zero_lt_of_lt h₇
+ let result := heapUpdateAt le index_in_left value l h₈
+ (.branch v result.fst r h₃ h₄ h₅, result.snd)
+ else
+ have h₇ : index.val - (o + 1) < p :=
+ -- tactic rewrite failed, result is not type correct.
+ have h₈ : index.val < p + o + 1 := index.isLt
+ |> (Nat.add_assoc o p 1).subst
+ |> (Nat.add_comm p 1).subst (motive := λx ↦ index.val < o + x)
+ |> (Nat.add_assoc o 1 p).symm.subst
+ |> (Nat.add_comm (o+1) p).subst
+ Nat.sub_lt_of_lt_add h₈ $ (Nat.not_le_eq index.val o).mp h₆
+ let index_in_right : Fin p := ⟨index.val - o - 1, h₇⟩
+ have h₈ : 0 < p := Nat.zero_lt_of_lt h₇
+ let result := heapUpdateAt le index_in_right value r h₈
+ (.branch v l result.fst h₃ h₄ h₅, result.snd)
+
+----------------------------------------------------------------------------------------------
+-- heapPop
+
+def CompleteTree.heapPop {α : Type u} {n : Nat} (le : α → α → Bool) (heap : CompleteTree α (n+1)) : CompleteTree α n × α :=
+ let l := Internal.heapRemoveLast heap
+ if p : n > 0 then
+ heapUpdateRoot le l.snd l.fst p
+ else
+ l
+
+----------------------------------------------------------------------------------------------
+-- heapRemoveAt
+
+/--Removes the element at a given index. Use `CompleteTree.indexOf` to find the respective index.-/
+def CompleteTree.heapRemoveAt {α : Type u} {n : Nat} (le : α → α → Bool) (index : Fin (n+1)) (heap : CompleteTree α (n+1)) : CompleteTree α n × α :=
+ --Since we cannot even temporarily break the completeness property, we go with the
+ --version from Wikipedia: We first remove the last element, and then update values in the tree
+ --indices are depth first, but "last" means last element of the complete tree.
+ --sooo:
+ if index_ne_zero : index = 0 then
+ heapPop le heap
+ else
+ let (remaining_tree, removed_element, removed_index) := heap.heapRemoveLastWithIndex
+ if p : index = removed_index then
+ (remaining_tree, removed_element)
+ else
+ have n_gt_zero : n > 0 := by
+ cases n
+ case succ nn => exact Nat.zero_lt_succ nn
+ case zero => omega
+ if index_lt_lastIndex : index ≥ removed_index then
+ let index := index.pred index_ne_zero
+ heapUpdateAt le index removed_element remaining_tree n_gt_zero
+ else
+ let h₁ : index < n := by omega
+ let index : Fin n := ⟨index, h₁⟩
+ heapUpdateAt le index removed_element remaining_tree n_gt_zero
diff --git a/BinaryHeap/CompleteTree/HeapProofs.lean b/BinaryHeap/CompleteTree/HeapProofs.lean
new file mode 100644
index 0000000..d56e5b5
--- /dev/null
+++ b/BinaryHeap/CompleteTree/HeapProofs.lean
@@ -0,0 +1,687 @@
+import BinaryHeap.CompleteTree.HeapOperations
+import BinaryHeap.CompleteTree.Lemmas
+
+namespace BinaryHeap
+
+theorem CompleteTree.emptyIsHeap {α : Type u} (le : α → α → Bool) : HeapPredicate CompleteTree.empty le := by trivial
+
+----------------------------------------------------------------------------------------------
+-- heapPush
+
+private theorem CompleteTree.rootSeesThroughCast
+ (n m : Nat)
+ (h₁ : n + 1 + m = n + m + 1)
+ (h₂ : 0 < n + 1 + m)
+ (h₃ : 0 < n + m + 1)
+ (heap : CompleteTree α (n+1+m)) : (h₁▸heap).root h₃ = heap.root h₂ := by
+ induction m generalizing n
+ case zero => simp
+ case succ o ho =>
+ have h₄ := ho (n+1)
+ have h₅ : n + 1 + 1 + o = n + 1 + (Nat.succ o) := by simp_arith
+ have h₆ : n + 1 + o + 1 = n + (Nat.succ o + 1) := by simp_arith
+ rewrite[h₅, h₆] at h₄
+ revert heap h₁ h₂ h₃
+ assumption
+
+private theorem HeapPredicate.seesThroughCast
+ (n m : Nat)
+ (lt : α → α → Bool)
+ (h₁ : n+1+m+1=n+m+1+1)
+ (h₂ : 0<n+1+m+1)
+ (h₃ : 0<n+m+1+1)
+ (heap : CompleteTree α (n+1+m+1)) : HeapPredicate heap lt → HeapPredicate (h₁▸heap) lt := by
+ unfold HeapPredicate
+ intro h₄
+ induction m generalizing n
+ case zero => simp[h₄]
+ case succ o ho =>
+ have h₅ := ho (n+1)
+ have h₆ : n+1+1+o+1 = n+1+(Nat.succ o)+1 := by simp_arith
+ rw[h₆] at h₅
+ have h₆ : n + 1 + o + 1 + 1 = n + (Nat.succ o + 1 + 1) := by simp_arith
+ rewrite[h₆] at h₅
+ revert heap h₁ h₂ h₃
+ assumption
+
+theorem CompleteTree.heapPushRootSameOrElem (elem : α) (heap : CompleteTree α o) (lt : α → α → Bool) (h₂ : 0 < o): (CompleteTree.root (heap.heapPush lt elem) (Nat.succ_pos o) = elem) ∨ (CompleteTree.root (heap.heapPush lt elem) (Nat.succ_pos o) = CompleteTree.root heap h₂) := by
+ unfold heapPush
+ split --match o, heap
+ contradiction
+ simp
+ rename_i n m v l r _ _ _
+ split -- if h : m < n ∧ Nat.nextPowerOfTwo (n + 1) = n + 1 then
+ case h_2.isTrue h =>
+ cases (lt elem v) <;> simp[instDecidableEqBool, Bool.decEq, CompleteTree.root]
+ case h_2.isFalse h =>
+ rw[rootSeesThroughCast n (m+1) (by simp_arith) (by simp_arith) (by simp_arith)]
+ cases (lt elem v)
+ <;> simp[instDecidableEqBool, Bool.decEq, CompleteTree.root]
+
+theorem CompleteTree.heapPushEmptyElem (elem : α) (heap : CompleteTree α o) (lt : α → α → Bool) (h₂ : ¬0 < o) : (CompleteTree.root (heap.heapPush lt elem) (Nat.succ_pos o) = elem) :=
+ have : o = 0 := Nat.eq_zero_of_le_zero $ (Nat.not_lt_eq 0 o).mp h₂
+ match o, heap with
+ | 0, .leaf => by simp[CompleteTree.heapPush, root]
+
+theorem HeapPredicate.leOrLeaf_transitive (h₁ : transitive_le le) : le a b → HeapPredicate.leOrLeaf le b h → HeapPredicate.leOrLeaf le a h := by
+ unfold leOrLeaf
+ intros h₂ h₃
+ rename_i n
+ cases n <;> simp
+ apply h₁ a b _
+ exact ⟨h₂, h₃⟩
+
+mutual
+/--
+ Helper for CompleteTree.heapPushIsHeap, to make one function out of both branches.
+ Sorry for the ugly signature, but this was the easiest thing to extract.
+ -/
+private theorem CompleteTree.heapPushIsHeapAux {α : Type u} (le : α → α → Bool) (n m : Nat) (v elem : α) (l : CompleteTree α n) (r : CompleteTree α m) (h₁ : HeapPredicate l le ∧ HeapPredicate r le ∧ HeapPredicate.leOrLeaf le v l ∧ HeapPredicate.leOrLeaf le v r) (h₂ : transitive_le le) (h₃ : total_le le): HeapPredicate l le ∧
+ let smaller := (if le elem v then elem else v)
+ let larger := (if le elem v then v else elem)
+ HeapPredicate (heapPush le larger r) le
+ ∧ HeapPredicate.leOrLeaf le smaller l
+ ∧ HeapPredicate.leOrLeaf le smaller (heapPush le larger r)
+ := by
+ cases h₆ : (le elem v) <;> simp only [Bool.false_eq_true, reduceIte]
+ case true =>
+ have h₇ : (HeapPredicate (CompleteTree.heapPush le v r) le) := CompleteTree.heapPushIsHeap h₁.right.left h₂ h₃
+ simp only [true_and, h₁, h₇, HeapPredicate.leOrLeaf_transitive h₂ h₆ h₁.right.right.left]
+ cases m
+ case zero =>
+ have h₈ := heapPushEmptyElem v r le (Nat.not_lt_zero 0)
+ simp only [HeapPredicate.leOrLeaf, Nat.succ_eq_add_one, Nat.reduceAdd, h₈]
+ assumption
+ case succ _ =>
+ simp only [HeapPredicate.leOrLeaf]
+ cases heapPushRootSameOrElem v r le (Nat.succ_pos _)
+ <;> rename_i h₇
+ <;> rw[h₇]
+ . assumption
+ apply h₂ elem v
+ exact ⟨h₆, h₁.right.right.right⟩
+ case false =>
+ have h₇ : (HeapPredicate (CompleteTree.heapPush le elem r) le) := CompleteTree.heapPushIsHeap h₁.right.left h₂ h₃
+ simp only [true_and, h₁, h₇]
+ have h₈ : le v elem := not_le_imp_le h₃ elem v (by simp only [h₆, Bool.false_eq_true, not_false_eq_true])
+ cases m
+ case zero =>
+ have h₇ := heapPushEmptyElem elem r le (Nat.not_lt_zero 0)
+ simp only [HeapPredicate.leOrLeaf, Nat.succ_eq_add_one, Nat.reduceAdd, h₇]
+ assumption
+ case succ _ =>
+ cases heapPushRootSameOrElem elem r le (Nat.succ_pos _)
+ <;> rename_i h₉
+ <;> simp only [HeapPredicate.leOrLeaf, Nat.succ_eq_add_one, h₈, h₉]
+ exact h₁.right.right.right
+
+theorem CompleteTree.heapPushIsHeap {α : Type u} {elem : α} {heap : CompleteTree α o} {le : α → α → Bool} (h₁ : HeapPredicate heap le) (h₂ : transitive_le le) (h₃ : total_le le) : HeapPredicate (heap.heapPush le elem) le := by
+ unfold heapPush
+ split -- match o, heap with
+ trivial
+ case h_2 n m v l r m_le_n _ _ =>
+ simp
+ split -- if h₅ : m < n ∧ Nat.nextPowerOfTwo (n + 1) = n + 1 then
+ case' isTrue =>
+ have h := heapPushIsHeapAux le n m v elem l r h₁ h₂ h₃
+ case' isFalse =>
+ apply HeapPredicate.seesThroughCast <;> try simp_arith[h₂] --gets rid of annoying cast.
+ have h := heapPushIsHeapAux le m n v elem r l (And.intro h₁.right.left $ And.intro h₁.left $ And.intro h₁.right.right.right h₁.right.right.left) h₂ h₃
+ all_goals
+ unfold HeapPredicate
+ cases h₆ : (le elem v)
+ <;> simp only [h₆, Bool.false_eq_true, reduceIte] at h
+ <;> simp only [instDecidableEqBool, Bool.decEq, h, and_self]
+end
+
+----------------------------------------------------------------------------------------------
+-- heapRemoveLast
+
+--- Same as rootSeesThroughCast, but in the other direction.
+private theorem CompleteTree.rootSeesThroughCast2
+ (n m : Nat)
+ (h₁ : n + m + 1 = n + 1 + m)
+ (h₂ : 0 < n + m + 1)
+ (h₃ : 0 < n + 1 + m)
+ (heap : CompleteTree α (n+m+1)) : (h₁▸heap).root h₃ = heap.root h₂ := by
+ induction m generalizing n
+ case zero => simp
+ case succ mm mh =>
+ have h₄ := mh (n+1)
+ have h₅ : n + 1 + mm + 1 = n + Nat.succ mm + 1 := by simp_arith
+ have h₆ : n + 1 + 1 + mm = n + 1 + Nat.succ mm := by simp_arith
+ rw[h₅, h₆] at h₄
+ revert heap h₁ h₂ h₃
+ assumption
+
+private theorem HeapPredicate.seesThroughCast2
+ (n m : Nat)
+ (lt : α → α → Bool)
+ (h₁ : n+m+1=n+1+m)
+ (h₂ : 0<n+1+m)
+ (h₃ : 0<n+m+1)
+ (heap : CompleteTree α (n+m+1)) : HeapPredicate heap lt → HeapPredicate (h₁▸heap) lt := by
+ unfold HeapPredicate
+ intro h₄
+ induction m generalizing n
+ case zero => simp[h₄]
+ case succ o ho =>
+ have h₅ := ho (n+1)
+ have h₆ : n+1+o+1 = n+(Nat.succ o)+1 := by simp_arith
+ rw[h₆] at h₅
+ have h₆ : n + 1 + 1 + o = n + 1 + Nat.succ o := by simp_arith
+ rewrite[h₆] at h₅
+ revert heap h₁ h₂ h₃
+ assumption
+
+private theorem CompleteTree.castZeroHeap (n m : Nat) (heap : CompleteTree α 0) (h₁ : 0=n+m) {le : α → α → Bool} : HeapPredicate (h₁ ▸ heap) le := by
+ have h₂ : heap = (CompleteTree.empty : CompleteTree α 0) := by
+ simp[empty]
+ match heap with
+ | .leaf => trivial
+ have h₂ : HeapPredicate heap le := by simp[h₂, empty, emptyIsHeap]
+ cases m
+ case succ => contradiction
+ case zero =>
+ cases n
+ case succ => contradiction
+ case zero =>
+ simp[h₁, h₂]
+
+-- If there is only one element left, the result is a leaf.
+private theorem CompleteTree.heapRemoveLastAuxLeaf
+{α : Type u}
+{β : Nat → Type u}
+(heap : CompleteTree α 1)
+(aux0 : α → (β 1))
+(auxl : {prev_size curr_size : Nat} → β prev_size → (h₁ : prev_size < curr_size) → β curr_size)
+(auxr : {prev_size curr_size : Nat} → β prev_size → (left_size : Nat) → (h₁ : prev_size + left_size < curr_size) → β curr_size)
+: (Internal.heapRemoveLastAux heap aux0 auxl auxr).fst = CompleteTree.leaf := by
+ let l := (Internal.heapRemoveLastAux heap aux0 auxl auxr).fst
+ have h₁ : l = CompleteTree.leaf := match l with
+ | .leaf => rfl
+ exact h₁
+
+private theorem CompleteTree.heapRemoveLastAuxLeavesRoot
+{α : Type u}
+{β : Nat → Type u}
+(heap : CompleteTree α (n+1))
+(aux0 : α → (β 1))
+(auxl : {prev_size curr_size : Nat} → β prev_size → (h₁ : prev_size < curr_size) → β curr_size)
+(auxr : {prev_size curr_size : Nat} → β prev_size → (left_size : Nat) → (h₁ : prev_size + left_size < curr_size) → β curr_size)
+(h₁ : n > 0)
+: heap.root (Nat.zero_lt_of_ne_zero $ Nat.succ_ne_zero n) = (Internal.heapRemoveLastAux heap aux0 auxl auxr).fst.root (h₁) := by
+ unfold CompleteTree.Internal.heapRemoveLastAux
+ split
+ rename_i o p v l r _ _ _
+ have h₃ : (0 ≠ o + p) := Ne.symm $ Nat.not_eq_zero_of_lt h₁
+ simp[h₃]
+ exact
+ if h₄ : p < o ∧ Nat.nextPowerOfTwo (p + 1) = p + 1 then by
+ simp[h₄]
+ cases o
+ case zero => exact absurd h₄.left $ Nat.not_lt_zero p
+ case succ oo _ _ _ =>
+ simp -- redundant, but makes goal easier to read
+ rw[rootSeesThroughCast2 oo p _ (by simp_arith) _]
+ apply root_unfold
+ else by
+ simp[h₄]
+ cases p
+ case zero =>
+ simp_arith at h₁ -- basically o ≠ 0
+ simp_arith (config := {ground := true})[h₁] at h₄ -- the second term in h₄ is decidable and True. What remains is ¬(0 < o), or o = 0
+ case succ pp hp =>
+ simp_arith
+ apply root_unfold
+
+private theorem CompleteTree.heapRemoveLastAuxIsHeap
+{α : Type u}
+{β : Nat → Type u}
+{heap : CompleteTree α (o+1)}
+{le : α → α → Bool}
+(aux0 : α → (β 1))
+(auxl : {prev_size curr_size : Nat} → β prev_size → (h₁ : prev_size < curr_size) → β curr_size)
+(auxr : {prev_size curr_size : Nat} → β prev_size → (left_size : Nat) → (h₁ : prev_size + left_size < curr_size) → β curr_size)
+(h₁ : HeapPredicate heap le) (h₂ : transitive_le le) (h₃ : total_le le) : HeapPredicate ((Internal.heapRemoveLastAux heap aux0 auxl auxr).fst) le := by
+ unfold CompleteTree.Internal.heapRemoveLastAux
+ split
+ rename_i n m v l r _ _ _
+ exact
+ if h₄ : 0 = (n+m) then by
+ simp only [h₄, reduceDite, castZeroHeap]
+ else by
+ simp[h₄]
+ exact
+ if h₅ : (m<n ∧ Nat.nextPowerOfTwo (m+1) = m+1) then by
+ simp only [h₅, and_self, ↓reduceDite]
+ cases n
+ case zero =>
+ exact absurd h₅.left $ Nat.not_lt_zero m
+ case succ ll h₆ h₇ h₈ =>
+ simp only
+ apply HeapPredicate.seesThroughCast2 <;> try simp_arith
+ cases ll
+ case a.zero => -- if ll is zero, then (heapRemoveLast l).snd is a leaf.
+ have h₆ := heapRemoveLastAuxLeaf l aux0 auxl auxr
+ rw[h₆]
+ unfold HeapPredicate at *
+ have h₇ : HeapPredicate .leaf le := by trivial
+ have h₈ : HeapPredicate.leOrLeaf le v .leaf := by trivial
+ exact ⟨h₇,h₁.right.left, h₈, h₁.right.right.right⟩
+ case a.succ nn => -- if ll is not zero, then the root element before and after heapRemoveLast is the same.
+ unfold HeapPredicate at *
+ simp only [heapRemoveLastAuxIsHeap aux0 auxl auxr h₁.left h₂ h₃, h₁.right.left, h₁.right.right.right, and_true, true_and]
+ unfold HeapPredicate.leOrLeaf
+ simp only
+ rw[←heapRemoveLastAuxLeavesRoot]
+ exact h₁.right.right.left
+ else by
+ simp[h₅]
+ cases m
+ case zero =>
+ simp only [Nat.add_zero] at h₄ -- n ≠ 0
+ simp (config := { ground := true }) only [Nat.zero_add, and_true, Nat.not_lt, Nat.le_zero_eq, Ne.symm h₄] at h₅ -- the second term in h₅ is decidable and True. What remains is ¬(0 < n), or n = 0
+ case succ mm h₆ h₇ h₈ =>
+ unfold HeapPredicate at *
+ simp only [h₁, heapRemoveLastAuxIsHeap aux0 auxl auxr h₁.right.left h₂ h₃, true_and]
+ unfold HeapPredicate.leOrLeaf
+ exact match mm with
+ | 0 => rfl
+ | o+1 =>
+ have h₉ : le v ((Internal.heapRemoveLastAux r _ _ _).fst.root (Nat.zero_lt_succ o)) := by
+ rw[←heapRemoveLastAuxLeavesRoot]
+ exact h₁.right.right.right
+ h₉
+
+private theorem CompleteTree.heapRemoveLastIsHeap {α : Type u} {heap : CompleteTree α (o+1)} {le : α → α → Bool} (h₁ : HeapPredicate heap le) (h₂ : transitive_le le) (h₃ : total_le le) : HeapPredicate (Internal.heapRemoveLast heap).fst le :=
+ heapRemoveLastAuxIsHeap _ _ _ h₁ h₂ h₃
+
+private theorem CompleteTree.heapRemoveLastWithIndexIsHeap {α : Type u} {heap : CompleteTree α (o+1)} {le : α → α → Bool} (h₁ : HeapPredicate heap le) (h₂ : transitive_le le) (h₃ : total_le le) : HeapPredicate (heap.heapRemoveLastWithIndex.fst) le :=
+ heapRemoveLastAuxIsHeap _ _ _ h₁ h₂ h₃
+
+----------------------------------------------------------------------------------------------
+-- heapUpdateRoot
+
+private theorem CompleteTree.heapUpdateRootReturnsRoot {α : Type u} {n : Nat} (le : α → α → Bool) (value : α) (heap : CompleteTree α n) (h₁ : n > 0) : (heap.heapUpdateRoot le value h₁).snd = heap.root h₁ := by
+ unfold heapUpdateRoot
+ split
+ rename_i o p v l r h₃ h₄ h₅ h₁
+ simp
+ cases o <;> simp
+ case zero =>
+ exact root_unfold v l r h₃ h₄ h₅ h₁
+ case succ =>
+ cases p <;> simp
+ case zero =>
+ cases le value (root l _)
+ <;> simp only [Bool.false_eq_true, ↓reduceIte, root_unfold]
+ case succ =>
+ cases le value (root l _) <;> cases le value (root r _)
+ <;> cases le (root l _) (root r _)
+ <;> simp only [Bool.false_eq_true, and_self, and_true, and_false, ↓reduceIte, root_unfold]
+
+private theorem CompleteTree.heapUpdateRootPossibleRootValuesAuxL {α : Type u} (heap : CompleteTree α n) (h₁ : n > 1) : 0 < heap.leftLen (Nat.lt_trans (Nat.lt_succ_self 0) h₁) :=
+ match h₅: n, heap with
+ | (o+p+1), .branch v l r h₂ h₃ h₄ => by
+ simp[leftLen, length]
+ cases o
+ case zero => rewrite[(Nat.le_zero_eq p).mp h₂] at h₁; contradiction
+ case succ q => exact Nat.zero_lt_succ q
+
+private theorem CompleteTree.heapUpdateRootPossibleRootValuesAuxR {α : Type u} (heap : CompleteTree α n) (h₁ : n > 2) : 0 < heap.rightLen (Nat.lt_trans (Nat.lt_succ_self 0) $ Nat.lt_trans (Nat.lt_succ_self 1) h₁) :=
+ match h₅: n, heap with
+ | (o+p+1), .branch v l r h₂ h₃ h₄ => by
+ simp[rightLen, length]
+ cases p
+ case zero => simp_arith at h₁; simp at h₃; exact absurd h₁ (Nat.not_le_of_gt h₃)
+ case succ q => exact Nat.zero_lt_succ q
+
+private theorem CompleteTree.heapUpdateRootPossibleRootValues1 {α : Type u} (le : α → α → Bool) (value : α) (heap : CompleteTree α n) (h₁ : n = 1) : (heap.heapUpdateRoot le value (h₁.substr (Nat.lt_succ_self 0))).fst.root (h₁.substr (Nat.lt_succ_self 0)) = value := by
+ unfold heapUpdateRoot
+ generalize (h₁.substr (Nat.lt_succ_self 0) : n > 0) = hx
+ split
+ rename_i o p v l r _ _ _ h₁
+ have h₃ : o = 0 := (Nat.add_eq_zero.mp $ Nat.succ.inj h₁).left
+ simp[h₃, root_unfold]
+
+private theorem CompleteTree.heapUpdateRootPossibleRootValues2 {α : Type u} (le : α → α → Bool) (value : α) (heap : CompleteTree α n) (h₁ : n = 2) :
+have h₂ : 0 < n := Nat.lt_trans (Nat.lt_succ_self 0) $ h₁.substr (Nat.lt_succ_self 1)
+have h₃ : 0 < leftLen heap h₂ := heapUpdateRootPossibleRootValuesAuxL heap (h₁.substr (Nat.lt_succ_self 1))
+(heap.heapUpdateRoot le value h₂).fst.root h₂ = value
+∨ (heap.heapUpdateRoot le value h₂).fst.root h₂ = (heap.left h₂).root h₃
+:= by
+ simp
+ unfold heapUpdateRoot
+ generalize (Nat.lt_trans (Nat.lt_succ_self 0) (Eq.substr h₁ (Nat.lt_succ_self 1)) : 0 < n) = h₂
+ split
+ rename_i o p v l r h₃ h₄ h₅ h₂
+ cases o <;> simp
+ case zero => simp only[root, true_or]
+ case succ oo =>
+ have h₆ : p = 0 := by simp at h₁; omega
+ simp only [h₆, ↓reduceDite]
+ cases le value (l.root _)
+ <;> simp[heapUpdateRootReturnsRoot, root_unfold, left_unfold]
+
+private theorem CompleteTree.heapUpdateRootPossibleRootValues3 {α : Type u} (le : α → α → Bool) (value : α) (heap : CompleteTree α n) (h₁ : n > 2) :
+have h₂ : 0 < n := Nat.lt_trans (Nat.lt_succ_self 0) $ Nat.lt_trans (Nat.lt_succ_self 1) h₁
+have h₃ : 0 < leftLen heap h₂ := heapUpdateRootPossibleRootValuesAuxL heap $ Nat.lt_trans (Nat.lt_succ_self 1) h₁
+have h₄ : 0 < rightLen heap h₂ := heapUpdateRootPossibleRootValuesAuxR heap h₁
+(heap.heapUpdateRoot le value h₂).fst.root h₂ = value
+∨ (heap.heapUpdateRoot le value h₂).fst.root h₂ = (heap.left h₂).root h₃
+∨ (heap.heapUpdateRoot le value h₂).fst.root h₂ = (heap.right h₂).root h₄
+:= by
+ simp only
+ unfold heapUpdateRoot
+ generalize (Nat.lt_trans (Nat.lt_succ_self 0) (Nat.lt_trans (Nat.lt_succ_self 1) h₁) : 0 < n) = h₂
+ split
+ rename_i o p v l r h₃ h₄ h₅ h₂
+ cases o
+ case zero => simp only[root, true_or]
+ case succ oo =>
+ have h₆ : p ≠ 0 := by simp at h₁; omega
+ simp only [Nat.add_one_ne_zero, ↓reduceDite, h₆]
+ cases le value (l.root _) <;> simp
+ rotate_right
+ cases le value (r.root _) <;> simp
+ case true.true => simp[root]
+ case false | true.false =>
+ cases le (l.root _) (r.root _)
+ <;> simp only [Bool.false_eq_true, ↓reduceIte, heapUpdateRootReturnsRoot, root_unfold, left_unfold, right_unfold, true_or, or_true]
+
+private theorem CompleteTree.heapUpdateRootIsHeapLeRootAux {α : Type u} (le : α → α → Bool) (value : α) (heap : CompleteTree α n) (h₁ : HeapPredicate heap le) (h₂ : n > 0) (h₃ : le (root heap h₂) value) : HeapPredicate.leOrLeaf le (root heap h₂) (heapUpdateRoot le value heap h₂).fst :=
+ if h₄ : n = 1 then by
+ have h₅ : le (heap.root h₂) ( (heapUpdateRoot le value heap h₂).fst.root h₂) := by simp only[h₃, h₄, heapUpdateRootPossibleRootValues1]
+ unfold HeapPredicate.leOrLeaf
+ split
+ · rfl
+ · exact h₅
+ else if h₅ : n = 2 then by
+ have h₆ := heapUpdateRootPossibleRootValues2 le value heap h₅
+ cases h₆
+ case inl h₆ =>
+ have h₇ : le (heap.root h₂) ( (heapUpdateRoot le value heap h₂).fst.root h₂) := by simp only [h₆, h₃]
+ unfold HeapPredicate.leOrLeaf
+ split
+ · rfl
+ · exact h₇
+ case inr h₆ =>
+ unfold HeapPredicate.leOrLeaf
+ unfold HeapPredicate at h₁
+ split at h₁
+ case h_1 => contradiction
+ case h_2 o p v l r h₇ h₈ h₉ =>
+ have h₁₁ : p = 0 := by
+ simp at h₅
+ cases o; simp only [Nat.le_zero_eq] at h₇; exact h₇; simp_arith[Nat.add_eq_zero] at h₅; exact h₅.right
+ have h₁₀ : o = 1 := by simp_arith[h₁₁] at h₅; assumption
+ simp only
+ rw[h₆]
+ have h₁₂ := h₁.right.right.left
+ unfold HeapPredicate.leOrLeaf at h₁₂
+ cases o ; contradiction;
+ case succ =>
+ exact h₁₂
+ else by
+ have h₆ : n > 2 := by omega
+ have h₇ := heapUpdateRootPossibleRootValues3 le value heap h₆
+ simp at h₇
+ unfold HeapPredicate at h₁
+ cases h₇
+ case inl h₇ =>
+ have h₈ : le (heap.root h₂) ( (heapUpdateRoot le value heap h₂).fst.root h₂) := by simp only [h₇, h₃]
+ unfold HeapPredicate.leOrLeaf
+ split
+ · rfl
+ · exact h₈
+ case inr h₇ =>
+ cases h₇
+ case inl h₇ | inr h₇ =>
+ unfold HeapPredicate.leOrLeaf
+ split at h₁
+ contradiction
+ simp_all
+ case h_2 o p v l r _ _ _ =>
+ cases o
+ omega
+ cases p
+ omega
+ have h₈ := h₁.right.right.left
+ have h₉ := h₁.right.right.right
+ assumption
+
+private theorem CompleteTree.heapUpdateRootIsHeapLeRootAuxLe {α : Type u} (le : α → α → Bool) {a b c : α} (h₁ : transitive_le le) (h₂ : total_le le) (h₃ : le b c) : ¬le a c ∨ ¬ le a b → le b a
+| .inr h₅ => not_le_imp_le h₂ _ _ h₅
+| .inl h₅ => h₁ b c a ⟨h₃,not_le_imp_le h₂ _ _ h₅⟩
+
+theorem CompleteTree.heapUpdateRootIsHeap {α : Type u} {n: Nat} (le : α → α → Bool) (value : α) (heap : CompleteTree α n) (h₁ : n > 0) (h₂ : HeapPredicate heap le) (h₃ : transitive_le le) (h₄ : total_le le) : HeapPredicate (heap.heapUpdateRoot le value h₁).fst le := by
+ unfold heapUpdateRoot
+ split
+ rename_i o p v l r h₇ h₈ h₉ heq
+ exact
+ if h₁₀ : o = 0 then by
+ simp only [Nat.add_eq, Nat.succ_eq_add_one, h₁₀, ↓reduceDite]
+ unfold HeapPredicate at h₂ ⊢
+ simp only [h₂, true_and]
+ unfold HeapPredicate.leOrLeaf
+ have : p = 0 := by rw[h₁₀, Nat.le_zero_eq] at h₇; assumption
+ apply And.intro
+ case left => match o, l with
+ | Nat.zero, _ => trivial
+ case right => match p, r with
+ | Nat.zero, _ => trivial
+ else if h₁₁ : p = 0 then by
+ simp only [↓reduceDite, h₁₀, h₁₁]
+ cases h₉ : le value (root l (_ : 0 < o)) <;> simp
+ case true =>
+ unfold HeapPredicate at *
+ simp only [h₂, true_and]
+ unfold HeapPredicate.leOrLeaf
+ apply And.intro
+ case right => match p, r with
+ | Nat.zero, _ => trivial
+ case left => match o, l with
+ | Nat.succ _, _ => assumption
+ case false =>
+ rw[heapUpdateRootReturnsRoot]
+ have h₁₂ : le (l.root (Nat.zero_lt_of_ne_zero h₁₀)) value := by simp[h₉, h₄, not_le_imp_le]
+ have h₁₃ : o = 1 := Nat.le_antisymm (by simp_arith[h₁₁] at h₈; exact h₈) (Nat.succ_le_of_lt (Nat.zero_lt_of_ne_zero h₁₀))
+ unfold HeapPredicate at *
+ simp only [h₂, true_and] --closes one sub-goal
+ apply And.intro <;> try apply And.intro
+ case right.right => match p, r with
+ | 0, .leaf => simp[HeapPredicate.leOrLeaf]
+ case right.left =>
+ simp only [HeapPredicate.leOrLeaf]
+ cases o <;> simp only [Nat.succ_eq_add_one, heapUpdateRootPossibleRootValues1, h₁₃, h₁₂]
+ case left =>
+ apply heapUpdateRootIsHeap
+ exact h₂.left
+ exact h₃
+ exact h₄
+ else if h₁₂ : le value (root l (Nat.zero_lt_of_ne_zero h₁₀)) ∧ le value (root r (Nat.zero_lt_of_ne_zero h₁₁)) then by
+ unfold HeapPredicate at *
+ simp only [↓reduceDite, and_self, ↓reduceIte, true_and, h₁₀, h₁₁, h₁₂, h₂]
+ unfold HeapPredicate.leOrLeaf
+ cases o
+ · contradiction
+ · cases p
+ · contradiction
+ · assumption
+ else by
+ simp only [↓reduceDite, ↓reduceIte, h₁₀, h₁₁, h₁₂]
+ have h₁₃ : ¬le value (root l _) ∨ ¬le value (root r _) := (Decidable.not_and_iff_or_not (le value (root l (Nat.zero_lt_of_ne_zero h₁₀)) = true) (le value (root r (Nat.zero_lt_of_ne_zero h₁₁)) = true)).mp h₁₂
+ cases h₁₄ : le (root l (_ : 0 < o)) (root r (_ : 0 < p))
+ <;> simp only [Bool.false_eq_true, ↓reduceIte]
+ <;> unfold HeapPredicate at *
+ <;> simp only [true_and, h₂]
+ <;> apply And.intro
+ <;> try apply And.intro
+ case false.left | true.left =>
+ apply heapUpdateRootIsHeap
+ <;> simp only [h₂, h₃, h₄]
+ case false.right.left =>
+ unfold HeapPredicate.leOrLeaf
+ have h₁₅ : le (r.root _) (l.root _) = true := not_le_imp_le h₄ (l.root _) (r.root _) $ (Bool.not_eq_true $ le (root l (_ : 0 < o)) (root r (_ : 0 < p))).substr h₁₄
+ simp only[heapUpdateRootReturnsRoot]
+ cases o <;> simp only[h₁₅]
+ case true.right.right =>
+ unfold HeapPredicate.leOrLeaf
+ simp only[heapUpdateRootReturnsRoot]
+ cases p <;> simp only[h₁₄]
+ case false.right.right =>
+ have h₁₅ : le (r.root _) (l.root _) = true := not_le_imp_le h₄ (l.root _) (r.root _) $ (Bool.not_eq_true $ le (root l (_ : 0 < o)) (root r (_ : 0 < p))).substr h₁₄
+ have h₁₆ : le (r.root _) value := heapUpdateRootIsHeapLeRootAuxLe le h₃ h₄ h₁₅ h₁₃
+ simp only [heapUpdateRootReturnsRoot, heapUpdateRootIsHeapLeRootAux, h₂, h₁₆]
+ case true.right.left =>
+ have h₁₆ : le (l.root _) value := heapUpdateRootIsHeapLeRootAuxLe le h₃ h₄ h₁₄ h₁₃.symm
+ simp only [heapUpdateRootReturnsRoot, heapUpdateRootIsHeapLeRootAux, h₂, h₁₆]
+
+----------------------------------------------------------------------------------------------
+-- heapUpdateAt
+
+private theorem CompleteTree.heapUpdateAtIsHeapLeRootAux_RootLeValue {α : Type u} {n : Nat} (le : α → α → Bool) (index : Fin n) (value : α) (heap : CompleteTree α n) (h₁ : HeapPredicate heap le) (h₂ : n > 0) (h₃ : le (root heap h₂) value) (h₄ : total_le le) : HeapPredicate.leOrLeaf le (root heap h₂) (heapUpdateAt le index value heap h₂).fst := by
+ unfold heapUpdateAt
+ split
+ case isTrue => exact heapUpdateRootIsHeapLeRootAux le value heap h₁ h₂ h₃
+ case isFalse hi =>
+ split
+ rename_i o p v l r h₆ h₇ h₈ index h₁ h₅
+ cases h₉ : le v value <;> simp (config := { ground := true }) only
+ case false => rw[root_unfold] at h₃; exact absurd h₃ ((Bool.not_eq_true (le v value)).substr h₉)
+ case true =>
+ rw[root_unfold]
+ split
+ <;> simp![reflexive_le, h₄]
+
+private theorem CompleteTree.heapUpdateAtIsHeapLeRootAux_ValueLeRoot {α : Type u} {n : Nat} (le : α → α → Bool) (index : Fin n) (value : α) (heap : CompleteTree α n) (h₁ : HeapPredicate heap le) (h₂ : n > 0) (h₃ : le value (root heap h₂)) (h₄ : total_le le) (h₅ : transitive_le le) : HeapPredicate.leOrLeaf le value (heapUpdateAt le index value heap h₂).fst := by
+ unfold heapUpdateAt
+ split
+ <;> rename_i h₉
+ case isTrue =>
+ unfold heapUpdateRoot
+ split
+ rename_i o p v l r h₆ h₇ h₈ h₂
+ cases o <;> cases p <;> simp only [↓reduceDite,HeapPredicate.leOrLeaf, root_unfold, h₄, reflexive_le]
+ <;> unfold HeapPredicate at h₁
+ <;> have h₁₀ : le value $ l.root (by omega) := h₅ value v (l.root _) ⟨h₃, h₁.right.right.left⟩
+ simp only [↓reduceIte, Nat.add_zero, h₁₀, root_unfold, h₄, reflexive_le]
+ have h₁₁ : le value $ r.root (by omega) := h₅ value v (r.root _) ⟨h₃, h₁.right.right.right⟩
+ simp only [↓reduceIte, h₁₀, h₁₁, and_self, root_unfold, h₄, reflexive_le]
+ case isFalse =>
+ split
+ rename_i o p v l r h₆ h₇ h₈ index h₂ hi
+ cases le v value
+ <;> simp (config := { ground := true }) only [root_unfold, Nat.pred_eq_sub_one] at h₃ ⊢
+ <;> split
+ <;> unfold HeapPredicate.leOrLeaf
+ <;> simp only [root_unfold, h₃, h₄, reflexive_le]
+
+theorem CompleteTree.heapUpdateAtIsHeap {α : Type u} {n : Nat} (le : α → α → Bool) (index : Fin n) (value : α) (heap : CompleteTree α n) (h₁ : n > 0) (h₂ : HeapPredicate heap le) (h₃ : transitive_le le) (h₄ : total_le le) : HeapPredicate (heap.heapUpdateAt le index value h₁).fst le := by
+ unfold heapUpdateAt
+ split
+ case isTrue h₅ =>
+ exact heapUpdateRootIsHeap le value heap h₁ h₂ h₃ h₄
+ case isFalse h₅ =>
+ split
+ rename_i o p v l r h₆ h₇ h₈ index h₁ h₅
+ cases h₁₀ : le v value <;> simp (config := {ground := true}) -- this could probably be solved without this split, but readability...
+ <;> split
+ <;> rename_i h -- h is the same name as used in the function
+ <;> unfold HeapPredicate at h₂ ⊢
+ <;> simp only [h₂, and_true, true_and]
+ case false.isFalse =>
+ have h₁₀ := not_le_imp_le h₄ v value (Bool.eq_false_iff.mp h₁₀)
+ have h₁₄ : p > 0 := by cases p; exact absurd (Nat.lt_succ.mp index.isLt) h; exact Nat.zero_lt_succ _
+ apply And.intro <;> try apply And.intro
+ case left => exact heapUpdateAtIsHeap le ⟨index.val - o - 1, _⟩ v r h₁₄ h₂.right.left h₃ h₄
+ case right.left => exact HeapPredicate.leOrLeaf_transitive h₃ h₁₀ h₂.right.right.left
+ case right.right =>
+ have h₁₁: HeapPredicate (heapUpdateAt le ⟨index.val - o - 1, (by omega)⟩ v r h₁₄).fst le :=
+ (heapUpdateAtIsHeap le ⟨index.val - o - 1, (by omega)⟩ v r _ h₂.right.left h₃ h₄)
+ cases h₁₂ : le v (r.root h₁₄)
+ case false =>
+ cases p
+ exact absurd (Nat.lt_succ.mp index.isLt) h
+ exact absurd h₂.right.right.right ((Bool.eq_false_iff).mp h₁₂)
+ case true =>
+ have h₁₃ := heapUpdateAtIsHeapLeRootAux_ValueLeRoot le ⟨index.val - o - 1, (by omega)⟩ v r h₂.right.left (by omega) h₁₂ h₄ h₃
+ apply HeapPredicate.leOrLeaf_transitive h₃ _ h₁₃
+ exact h₁₀
+ case false.isTrue =>
+ have h₁₀ := not_le_imp_le h₄ v value (Bool.eq_false_iff.mp h₁₀)
+ have h₁₄ : o > 0 := by cases o; simp at h₅ h; exact absurd (Fin.val_inj.mp h : index = 0) h₅; exact Nat.zero_lt_succ _
+ apply And.intro <;> try apply And.intro
+ case left => exact heapUpdateAtIsHeap le ⟨index.val - 1, _⟩ v l h₁₄ h₂.left h₃ h₄
+ case right.right => exact HeapPredicate.leOrLeaf_transitive h₃ h₁₀ h₂.right.right.right
+ case right.left =>
+ have h₁₁: HeapPredicate (heapUpdateAt le ⟨index.val - 1, (_)⟩ v l h₁₄).fst le :=
+ (heapUpdateAtIsHeap le ⟨index.val - 1, (by omega)⟩ v l _ h₂.left h₃ h₄)
+ cases h₁₂ : le v (l.root h₁₄)
+ case false =>
+ cases o
+ contradiction -- h₁₄ is False
+ exact absurd h₂.right.right.left ((Bool.eq_false_iff).mp h₁₂)
+ case true =>
+ have h₁₃ := heapUpdateAtIsHeapLeRootAux_ValueLeRoot le ⟨index.val - 1, (by omega)⟩ v l h₂.left (by omega) h₁₂ h₄ h₃
+ apply HeapPredicate.leOrLeaf_transitive h₃ _ h₁₃
+ exact h₁₀
+ case true.isFalse =>
+ have h₁₄ : p > 0 := by cases p; exact absurd (Nat.lt_succ.mp index.isLt) h; exact Nat.zero_lt_succ _
+ apply And.intro
+ case left => exact heapUpdateAtIsHeap le ⟨index.val - o - 1, _⟩ value r h₁₄ h₂.right.left h₃ h₄
+ case right =>
+ have h₁₁: HeapPredicate (heapUpdateAt le ⟨index.val - o - 1, (by omega)⟩ v r (h₁₄)).fst le :=
+ (heapUpdateAtIsHeap le ⟨index.val - o - 1, (by omega)⟩ v r _ h₂.right.left h₃ h₄)
+ cases h₁₂ : le value (r.root h₁₄)
+ case false =>
+ have h₁₃ := heapUpdateAtIsHeapLeRootAux_RootLeValue le ⟨index.val - o - 1, (by omega)⟩ value r h₂.right.left (by omega) (not_le_imp_le h₄ value (r.root h₁₄) (Bool.eq_false_iff.mp h₁₂)) h₄
+ apply HeapPredicate.leOrLeaf_transitive h₃ _ h₁₃
+ cases p
+ contradiction -- h₁₄ is False
+ exact h₂.right.right.right
+ case true =>
+ have h₁₃ := heapUpdateAtIsHeapLeRootAux_ValueLeRoot le ⟨index.val - o - 1, (by omega)⟩ value r h₂.right.left (by omega) h₁₂ h₄ h₃
+ apply HeapPredicate.leOrLeaf_transitive h₃ _ h₁₃
+ exact h₁₀
+ case true.isTrue =>
+ have h₁₄ : o > 0 := by cases o; simp at h₅ h; exact absurd (Fin.val_inj.mp h : index = 0) h₅; exact Nat.zero_lt_succ _
+ apply And.intro
+ case left => exact heapUpdateAtIsHeap le ⟨index.val - 1, _⟩ value l h₁₄ h₂.left h₃ h₄
+ case right =>
+ have h₁₁: HeapPredicate (heapUpdateAt le ⟨index.val - 1, (by omega)⟩ v l h₁₄).fst le :=
+ (heapUpdateAtIsHeap le ⟨index.val - 1, (by omega)⟩ v l _ h₂.left h₃ h₄)
+ cases h₁₂ : le value (l.root h₁₄)
+ case false =>
+ have h₁₃ := heapUpdateAtIsHeapLeRootAux_RootLeValue le ⟨index.val - 1, (by omega)⟩ value l h₂.left (by omega) (not_le_imp_le h₄ value (l.root h₁₄) (Bool.eq_false_iff.mp h₁₂)) h₄
+ apply HeapPredicate.leOrLeaf_transitive h₃ _ h₁₃
+ cases o
+ contradiction -- h₁₄ is False
+ exact h₂.right.right.left
+ case true =>
+ have h₁₃ := heapUpdateAtIsHeapLeRootAux_ValueLeRoot le ⟨index.val - 1, (by omega)⟩ value l h₂.left (by omega) h₁₂ h₄ h₃
+ apply HeapPredicate.leOrLeaf_transitive h₃ _ h₁₃
+ exact h₁₀
+
+----------------------------------------------------------------------------------------------
+-- heapPop
+
+theorem CompleteTree.heapPopIsHeap {α : Type u} {n : Nat} (le : α → α → Bool) (heap : CompleteTree α (n+1)) (h₁ : HeapPredicate heap le) (wellDefinedLe : transitive_le le ∧ total_le le) : HeapPredicate (heap.heapPop le).fst le := by
+ have h₂ : HeapPredicate (Internal.heapRemoveLast heap).fst le := heapRemoveLastIsHeap h₁ wellDefinedLe.left wellDefinedLe.right
+ unfold heapPop
+ cases n <;> simp[h₂, heapUpdateRootIsHeap, wellDefinedLe]
+
+----------------------------------------------------------------------------------------------
+-- heapRemoveAt
+
+theorem CompleteTree.heapRemoveAtIsHeap {α : Type u} {n : Nat} (le : α → α → Bool) (index : Fin (n+1)) (heap : CompleteTree α (n+1)) (h₁ : HeapPredicate heap le) (wellDefinedLe : transitive_le le ∧ total_le le) : HeapPredicate (heap.heapRemoveAt le index).fst le := by
+ have h₂ : HeapPredicate heap.heapRemoveLastWithIndex.fst le := heapRemoveLastWithIndexIsHeap h₁ wellDefinedLe.left wellDefinedLe.right
+ unfold heapRemoveAt
+ split
+ case isTrue => exact heapPopIsHeap le heap h₁ wellDefinedLe
+ case isFalse h₃ =>
+ cases h: (index = heap.heapRemoveLastWithIndex.snd.snd : Bool)
+ <;> simp_all
+ split
+ <;> apply heapUpdateAtIsHeap <;> simp_all
diff --git a/BinaryHeap/CompleteTree/Lemmas.lean b/BinaryHeap/CompleteTree/Lemmas.lean
new file mode 100644
index 0000000..2e4bda1
--- /dev/null
+++ b/BinaryHeap/CompleteTree/Lemmas.lean
@@ -0,0 +1,37 @@
+import BinaryHeap.CompleteTree.Basic
+import BinaryHeap.HeapPredicate
+
+namespace BinaryHeap
+
+def reflexive_le {α : Type u} {le : α → α → Bool} (h₁ : total_le le) (a : α) : le a a := by
+ unfold total_le at h₁
+ have h₁ := h₁ a a
+ cases h₁ <;> assumption
+
+def not_le_imp_le {α : Type u} {le : α → α → Bool} (h₁ : total_le le) : ∀(a b : α), ¬le a b → le b a := by
+ intros a b h₂
+ have h₁ := h₁ a b
+ cases h₁
+ . contradiction
+ . trivial
+
+/-- Helper to rw away root, because Lean 4.9 makes it unnecessarily hard to deal with match in tactics mode... -/
+theorem CompleteTree.root_unfold {α : Type u} {o p : Nat} (v : α) (l : CompleteTree α o) (r : CompleteTree α p) (h₁ : p ≤ o) (h₂ : o < 2 * (p + 1)) (h₃ : (o + 1).isPowerOfTwo ∨ (p + 1).isPowerOfTwo) (h₄ : o + p + 1 > 0): (CompleteTree.branch v l r h₁ h₂ h₃).root h₄ = v := rfl
+
+/-- Helper to rw away left, because Lean 4.9 makes it unnecessarily hard to deal with match in tactics mode... -/
+theorem CompleteTree.left_unfold {α : Type u} {o p : Nat} (v : α) (l : CompleteTree α o) (r : CompleteTree α p) (h₁ : p ≤ o) (h₂ : o < 2 * (p + 1)) (h₃ : (o + 1).isPowerOfTwo ∨ (p + 1).isPowerOfTwo) (h₄ : o + p + 1 > 0): (CompleteTree.branch v l r h₁ h₂ h₃).left h₄ = l := rfl
+
+/-- Helper to rw away right, because Lean 4.9 makes it unnecessarily hard to deal with match in tactics mode... -/
+theorem CompleteTree.right_unfold {α : Type u} {o p : Nat} (v : α) (l : CompleteTree α o) (r : CompleteTree α p) (h₁ : p ≤ o) (h₂ : o < 2 * (p + 1)) (h₃ : (o + 1).isPowerOfTwo ∨ (p + 1).isPowerOfTwo) (h₄ : o + p + 1 > 0): (CompleteTree.branch v l r h₁ h₂ h₃).right h₄ = r := rfl
+
+theorem CompleteTree.heqSameLeftLen {α : Type u} {n m : Nat} {a : CompleteTree α n} {b : CompleteTree α m} (h₁ : n = m) (h₂ : n > 0) (h₃ : HEq a b) : a.leftLen h₂ = b.leftLen (h₁.subst h₂) := by
+ subst n
+ have h₃ : a = b := eq_of_heq h₃
+ subst a
+ rfl
+
+theorem CompleteTree.heqSameRightLen {α : Type u} {n m : Nat} {a : CompleteTree α n} {b : CompleteTree α m} (h₁ : n = m) (h₂ : n > 0) (h₃ : HEq a b) : a.rightLen h₂ = b.rightLen (h₁.subst h₂) := by
+ subst n
+ have h₃ : a = b := eq_of_heq h₃
+ subst a
+ rfl
diff --git a/BinaryHeap/NatLemmas.lean b/BinaryHeap/CompleteTree/NatLemmas.lean
index 4ba88a6..33e67eb 100644
--- a/BinaryHeap/NatLemmas.lean
+++ b/BinaryHeap/CompleteTree/NatLemmas.lean
@@ -1,10 +1,6 @@
namespace Nat
-theorem Nat.power_of_two_go_eq_eq (n : Nat) (p : n >0) : Nat.nextPowerOfTwo.go n n p = n := by
- unfold Nat.nextPowerOfTwo.go
- simp_arith
-
-theorem Nat.smaller_smaller_exp {n m o : Nat} (p : o ^ n < o ^ m) (q : o > 0) : n < m :=
+theorem smaller_smaller_exp {n m o : Nat} (p : o ^ n < o ^ m) (q : o > 0) : n < m :=
if h₁ : m ≤ n then
by have h₂ := Nat.pow_le_pow_of_le_right (q) h₁
have h₃ := Nat.lt_of_le_of_lt h₂ p
@@ -24,7 +20,7 @@ private theorem mul2_isPowerOfTwo_smaller_smaller_equal (n : Nat) (power : Nat)
apply (Nat.pow_le_pow_of_le_right h₆)
rewrite [h₅] at h₃
rewrite [h₄] at h₃
- have h₃ := Nat.smaller_smaller_exp h₃
+ have h₃ := smaller_smaller_exp h₃
simp_arith at h₃
assumption
diff --git a/BinaryHeap/Basic.lean b/BinaryHeap/HeapPredicate.lean
index d0c950a..493447c 100644
--- a/BinaryHeap/Basic.lean
+++ b/BinaryHeap/HeapPredicate.lean
@@ -1,24 +1,10 @@
+import BinaryHeap.CompleteTree.Basic
+
namespace BinaryHeap
def transitive_le {α : Type u} (le : α → α → Bool) : Prop := ∀(a b c : α), (le a b) ∧ (le b c) → le a c
def total_le {α : Type u} (le : α → α → Bool) : Prop := ∀(a b : α), le a b ∨ le b a
-inductive CompleteTree (α : Type u) : Nat → Type u
- | leaf : CompleteTree α 0
- | branch :
- (val : α)
- → (left : CompleteTree α n)
- → (right : CompleteTree α m)
- → m ≤ n
- → n < 2*(m+1)
- → (n+1).isPowerOfTwo ∨ (m+1).isPowerOfTwo
- → CompleteTree α (n+m+1)
-deriving Repr
-
-/-- Returns the element stored in the root node. -/
-def CompleteTree.root (tree : CompleteTree α n) (_ : 0 < n) : α := match tree with
-| .branch a _ _ _ _ _ => a
-
def HeapPredicate {α : Type u} {n : Nat} (heap : CompleteTree α n) (le : α → α → Bool) : Prop :=
match heap with
| .leaf => True