diff --git a/Complexity.lean b/Complexity.lean index 189a3474b..f42ac9f5c 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -16,6 +16,7 @@ public import Complexity.Algorithms.Lean.FusionTree.Sketch public import Complexity.Algorithms.Lean.FusionTree.Tree public import Complexity.Algorithms.Lean.FusionTree.XorCorrection public import Complexity.Algorithms.Lean.IndexedMinPQ +public import Complexity.Algorithms.Lean.MinPQ public import Complexity.Algorithms.Lean.Queue public import Complexity.Algorithms.Lean.Retroactive public import Complexity.Algorithms.Lean.SearchTree.Basic @@ -310,6 +311,7 @@ public import Complexity.Algorithms.WordRAM.PriorityQueue.FibonacciHeap.BasicOps public import Complexity.Algorithms.WordRAM.PriorityQueue.FibonacciHeap.Consolidate public import Complexity.Algorithms.WordRAM.PriorityQueue.FibonacciHeap.Cut public import Complexity.Algorithms.WordRAM.PriorityQueue.FibonacciHeap.Forest +public import Complexity.Algorithms.WordRAM.PriorityQueue.FibonacciHeap.MinPQ public import Complexity.Algorithms.WordRAM.PriorityQueue.FibonacciHeap.Model public import Complexity.Algorithms.WordRAM.PriorityQueue.FibonacciHeap.Program public import Complexity.Algorithms.WordRAM.PriorityQueue.FibonacciHeap.Trees diff --git a/Complexity/Algorithms/Lean/MinPQ.lean b/Complexity/Algorithms/Lean/MinPQ.lean new file mode 100644 index 000000000..f424ca976 --- /dev/null +++ b/Complexity/Algorithms/Lean/MinPQ.lean @@ -0,0 +1,522 @@ +/- +Copyright (c) 2026 Renfei Zhou. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Renfei Zhou +-/ + +module +-- upstream: candidate + +public import Complexity.Init +public import Complexity.Foundations.Semantics.Stateful.Relational +public import Complexity.Algorithms.Lean.IndexedMinPQ + +/-! +# Relational Priority Queue with Handles + +This module defines the pure relational specification `Cslib.Algorithms.Lean.MinPQ` of a +bounded-capacity min-priority queue storing key–value entries `(u, v)` (`u < U`, `v < V`) addressed +by handles `h : Fin n_max` allocated by the data structure. + +Unlike `Cslib.Algorithms.Lean.IndexedMinPQ`, where the caller chooses the vertex index `v : Fin n` +on `insert v k` (as required by graph algorithms such as Dijkstra's algorithm), here the caller +passes only a key–value pair `(u, v)` to `insert u v` and receives a handle `h : Fin n_max` chosen +by the data structure: + +* `insert u v` (`size s < n_max ∧ u < U ∧ v < V`): nondeterministically selects *any* unused handle + `h : Fin n_max` (`s h = none`), stores `some (u, v)` at `h`, and returns `some h`; +* `extractMin`: returns `none` when the queue is empty (`dom s = ∅`), or nondeterministically + selects *any* stored element `(u, v, h)` attaining the minimum key (`isMinEntry s h u v`), + removes `h`, and returns `some (u, v, h)`; +* `decreaseKey h u'` (`∃ u v, s h = some (u, v) ∧ u' ≤ u`): lowers the key of active handle `h` to + `u'` while preserving its satellite value `v`; +* `isEmpty` and `get h`: read-only queries returning `decide (dom s = ∅)` and `s h`. + +This module also defines the deterministic free-list handle allocator (`AllocState`, `stepAlloc`), +proves that `stepAlloc` refines `MinPQ.spec` while preserving `AllocState.WellFormed`, and provides +the key-projection lemmas `toIndexed_update_some` and `toIndexed_update_none` connecting `State` to +`IndexedMinPQ.State`. + +## Main definitions + +* `Cslib.Algorithms.Lean.MinPQ.State`: abstract priority-queue state `Fin n_max → Option (ℕ × ℕ)`. +* `Cslib.Algorithms.Lean.MinPQ.dom`, `size`: active handle set and current number of stored + elements. +* `Cslib.Algorithms.Lean.MinPQ.Op`, `Op.Result`: priority-queue operations and return types. +* `Cslib.Algorithms.Lean.MinPQ.isMinEntry`: predicate that a stored handle `h` attains the minimum + key in `s`. +* `Cslib.Algorithms.Lean.MinPQ.spec`: relational specification (`Stateful.RelSpec`). +* `Cslib.Algorithms.Lean.MinPQ.AllocState`, `stepAlloc`: deterministic free-list handle allocator + and lexicographic tie-breaking transition refining `spec`. + +## Main results + +* `Cslib.Algorithms.Lean.MinPQ.instTotalSpec`: `MinPQ.spec n_max U V` is total (`RelSpec.Total`). +* `Cslib.Algorithms.Lean.MinPQ.stepAlloc_refines_spec`: every valid `stepAlloc` transition satisfies + `(spec n_max U V).step` and preserves `AllocState.WellFormed`. +* `Cslib.Algorithms.Lean.MinPQ.toIndexed_update_some`, `toIndexed_update_none`: `toIndexed` + commutes with `Function.update` on active and cleared handles. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.Lean.MinPQ + +open Cslib.Stateful + +/-- Abstract state of a min-priority queue of capacity `n_max`: each handle `h : Fin n_max` is +either unused (`none`) or stores a key–value pair `some (u, v)`. -/ +abbrev State (n_max : ℕ) : Type := Fin n_max → Option (ℕ × ℕ) + +/-- Initial empty priority queue with all handles unused. -/ +def empty (n_max : ℕ) : State n_max := fun _ => none + +/-- The finite set of currently active handles in `s`. -/ +def dom {n_max : ℕ} (s : State n_max) : Finset (Fin n_max) := + Finset.univ.filter fun h => (s h).isSome + +/-- The current number of stored elements in `s`. -/ +def size {n_max : ℕ} (s : State n_max) : ℕ := + (dom s).card + +/-- Operations of a min-priority queue with handles of capacity `n_max`. -/ +inductive Op (n_max : ℕ) : Type where + /-- Test whether the priority queue currently stores no elements. -/ + | isEmpty + /-- Query the `(key, value)` pair currently stored at handle `h` (`none` if `h` is inactive). -/ + | get (h : Fin n_max) + /-- Insert a new entry with key `u` and value `v`, returning its allocated handle `some h`. -/ + | insert (u v : ℕ) + /-- Decrease the key at active handle `h` to `u'`. -/ + | decreaseKey (h : Fin n_max) (u' : ℕ) + /-- Remove and return a minimum-key entry `some (u, v, h)`, or `none` if empty. -/ + | extractMin + deriving DecidableEq + +/-- Operation return types for `Op n_max`. -/ +abbrev Op.Result {n_max : ℕ} : Op n_max → Type + | .isEmpty => Bool + | .get _ => Option (ℕ × ℕ) + | .insert _ _ => Option (Fin n_max) + | .decreaseKey _ _ => Unit + | .extractMin => Option (ℕ × ℕ × Fin n_max) + +/-! ## Domain, element count, and minimum-entry lemmas -/ + +@[simp] +theorem empty_apply (n_max : ℕ) (h : Fin n_max) : empty n_max h = none := rfl + +@[simp] +theorem mem_dom {n_max : ℕ} {s : State n_max} {h : Fin n_max} : + h ∈ dom s ↔ (s h).isSome = true := by + simp only [dom, Finset.mem_filter, Finset.mem_univ, true_and] + +theorem mem_dom_iff {n_max : ℕ} {s : State n_max} {h : Fin n_max} : + h ∈ dom s ↔ ∃ u v, s h = some (u, v) := by + rw [mem_dom, Option.isSome_iff_exists] + exact ⟨fun ⟨⟨u, v⟩, H⟩ => ⟨u, v, H⟩, fun ⟨u, v, H⟩ => ⟨(u, v), H⟩⟩ + +theorem notMem_dom_iff {n_max : ℕ} {s : State n_max} {h : Fin n_max} : + h ∉ dom s ↔ s h = none := by + rw [mem_dom, Bool.not_eq_true, Option.isSome_eq_false_iff, Option.isNone_iff_eq_none] + +@[simp] +theorem dom_empty (n_max : ℕ) : dom (empty n_max) = ∅ := by + ext h + simp only [mem_dom, empty_apply, Option.isSome_none, Bool.false_eq_true, + Finset.notMem_empty] + +@[simp] +theorem size_empty (n_max : ℕ) : size (empty n_max) = 0 := by + simp only [size, dom_empty, Finset.card_empty] + +theorem size_le {n_max : ℕ} (s : State n_max) : size s ≤ n_max := by + simpa only [size, Fintype.card_fin] using (dom s).card_le_univ + +theorem dom_eq_empty_iff {n_max : ℕ} {s : State n_max} : + dom s = ∅ ↔ ∀ h, s h = none := by + rw [Finset.eq_empty_iff_forall_notMem] + exact forall_congr' fun _ => notMem_dom_iff + +@[simp] +theorem size_eq_zero_iff {n_max : ℕ} {s : State n_max} : + size s = 0 ↔ dom s = ∅ := + Finset.card_eq_zero + +theorem dom_update_some {n_max : ℕ} (s : State n_max) (h : Fin n_max) (u v : ℕ) : + dom (Function.update s h (some (u, v))) = insert h (dom s) := by + ext x + simp only [mem_dom, Finset.mem_insert] + by_cases hxh : x = h + · subst hxh; simp only [Function.update_self, Option.isSome_some, true_or] + · simp only [Function.update_of_ne hxh, hxh, false_or] + +theorem dom_update_none {n_max : ℕ} (s : State n_max) (h : Fin n_max) : + dom (Function.update s h none) = (dom s).erase h := by + ext x + simp only [mem_dom, Finset.mem_erase] + by_cases hxh : x = h + · subst hxh + simp only [Function.update_self, Option.isSome_none, Bool.false_eq_true, ne_eq, + not_true_eq_false, false_and] + · simp only [Function.update_of_ne hxh, ne_eq, hxh, not_false_eq_true, true_and] + +theorem size_update_some_of_none {n_max : ℕ} {s : State n_max} {h : Fin n_max} + (hh : s h = none) (u v : ℕ) : + size (Function.update s h (some (u, v))) = size s + 1 := by + simp only [size, dom_update_some] + exact Finset.card_insert_of_notMem (notMem_dom_iff.mpr hh) + +theorem size_update_some_of_some {n_max : ℕ} {s : State n_max} {h : Fin n_max} {u v : ℕ} + (hh : s h = some (u, v)) (u' v' : ℕ) : + size (Function.update s h (some (u', v'))) = size s := by + simp only [size, dom_update_some] + rw [Finset.insert_eq_of_mem (mem_dom_iff.mpr ⟨u, v, hh⟩)] + +theorem size_update_none_of_some {n_max : ℕ} {s : State n_max} {h : Fin n_max} {u v : ℕ} + (hh : s h = some (u, v)) : + size (Function.update s h none) = size s - 1 := by + simp only [size, dom_update_none] + exact Finset.card_erase_of_mem (mem_dom_iff.mpr ⟨u, v, hh⟩) + +/-- If the current number of stored elements `size s` is strictly below `n_max`, at least one handle +`h : Fin n_max` is unused (`s h = none`). -/ +theorem exists_none_of_size_lt {n_max : ℕ} {s : State n_max} (h : size s < n_max) : + ∃ i : Fin n_max, s i = none := by + by_contra hall + have huniv : dom s = Finset.univ := by + ext i + simp only [mem_dom, Finset.mem_univ, iff_true, Option.isSome_iff_ne_none] + exact fun hi => hall ⟨i, hi⟩ + have hcard : size s = n_max := by + simp only [size, huniv, Finset.card_univ, Fintype.card_fin] + omega + +/-- Project a key–value priority-queue state to an `IndexedMinPQ.State n_max` by retaining only the +key at each handle. -/ +def toIndexed {n_max : ℕ} (s : State n_max) : IndexedMinPQ.State n_max := + fun h => (s h).map Prod.fst + +@[simp] +theorem toIndexed_empty (n_max : ℕ) : toIndexed (empty n_max) = IndexedMinPQ.empty n_max := + rfl + +@[simp] +theorem toIndexed_apply {n_max : ℕ} (s : State n_max) (h : Fin n_max) : + toIndexed s h = (s h).map Prod.fst := rfl + +@[simp] +theorem dom_toIndexed {n_max : ℕ} (s : State n_max) : + IndexedMinPQ.dom (toIndexed s) = dom s := by + ext h + simp only [IndexedMinPQ.dom, Finset.mem_filter, Finset.mem_univ, true_and, + mem_dom, toIndexed_apply, Option.isSome_map] + +theorem toIndexed_update_some {n_max : ℕ} (s : State n_max) (h : Fin n_max) (u v : ℕ) : + toIndexed (Function.update s h (some (u, v))) = + Function.update (toIndexed s) h (some u) := by + ext x + by_cases hxh : x = h + · subst hxh; simp only [toIndexed_apply, Function.update_self, Option.map_some] + · simp only [toIndexed_apply, Function.update_of_ne hxh] + +theorem toIndexed_update_none {n_max : ℕ} (s : State n_max) (h : Fin n_max) : + toIndexed (Function.update s h none) = Function.update (toIndexed s) h none := by + ext x + by_cases hxh : x = h + · subst hxh; simp only [toIndexed_apply, Function.update_self, Option.map_none] + · simp only [toIndexed_apply, Function.update_of_ne hxh] + +/-- Handle `h` is active in `s` with entry `(u, v)` and `u` is less than or equal to the key of +every active entry in `s`. -/ +def isMinEntry {n_max : ℕ} (s : State n_max) (h : Fin n_max) (u v : ℕ) : Prop := + s h = some (u, v) ∧ ∀ h' u' v', s h' = some (u', v') → u ≤ u' + +/-- Deterministic minimum-entry selector breaking key ties by smallest handle index via +`IndexedMinPQ.findMin (toIndexed s)`. -/ +def findMin {n_max : ℕ} (s : State n_max) : Option (ℕ × ℕ × Fin n_max) := + match IndexedMinPQ.findMin (toIndexed s) with + | none => none + | some (h, u) => + match s h with + | some (_, v) => some (u, v, h) + | none => none + +@[simp] +theorem findMin_eq_none_iff {n_max : ℕ} {s : State n_max} : + findMin s = none ↔ dom s = ∅ := by + unfold findMin + rcases hfm : IndexedMinPQ.findMin (toIndexed s) with _ | ⟨h, u⟩ + · simp only [true_iff] + rw [← dom_toIndexed, ← IndexedMinPQ.findMin_eq_none_iff] + exact hfm + · have hmem : toIndexed s h = some u := (IndexedMinPQ.findMin_eq_some_iff.mp hfm).1 + simp only [toIndexed_apply, Option.map_eq_some_iff] at hmem + obtain ⟨⟨_, v⟩, hsh, rfl⟩ := hmem + dsimp only + rw [hsh] + simp only [reduceCtorEq, false_iff] + intro hemp + have : h ∉ dom s := hemp ▸ Finset.notMem_empty h + exact this (mem_dom_iff.mpr ⟨_, v, hsh⟩) + +theorem findMin_eq_some_of_indexed {n_max : ℕ} {s : State n_max} {h : Fin n_max} {u : ℕ} + (hfm : IndexedMinPQ.findMin (toIndexed s) = some (h, u)) : + ∃ v, s h = some (u, v) ∧ findMin s = some (u, v, h) ∧ isMinEntry s h u v := by + obtain ⟨hmem, hle⟩ := IndexedMinPQ.findMin_eq_some_iff.mp hfm + simp only [toIndexed_apply, Option.map_eq_some_iff] at hmem + obtain ⟨⟨u₀, v⟩, hsh, rfl⟩ := hmem + refine ⟨v, hsh, ?_, hsh, ?_⟩ + · simp only [findMin, hfm, hsh] + · intro h' u' v' hsh' + have hidx' : toIndexed s h' = some u' := by simp only [toIndexed_apply, hsh', Option.map_some] + rcases hle h' u' hidx' with hlt | ⟨rfl, _⟩ + · exact Nat.le_of_lt hlt + · exact le_rfl + +theorem exists_isMinEntry_of_dom_ne_empty {n_max : ℕ} {s : State n_max} (hne : dom s ≠ ∅) : + ∃ h u v, isMinEntry s h u v := by + have hfm_ne : IndexedMinPQ.findMin (toIndexed s) ≠ none := by + rw [ne_eq, IndexedMinPQ.findMin_eq_none_iff, dom_toIndexed] + exact hne + obtain ⟨⟨h, u⟩, hfm⟩ := Option.ne_none_iff_exists'.mp hfm_ne + obtain ⟨v, _, _, hmin⟩ := findMin_eq_some_of_indexed hfm + exact ⟨h, u, v, hmin⟩ + +/-! ## Relational specification `MinPQ.spec` and totality -/ + +/-- Pure relational specification of a capacity-`n_max` min-priority queue with handles storing keys +`u < U` and values `v < V`: +* `insert u v` nondeterministically chooses *any* unused handle `h : Fin n_max` (`s h = none`); +* `extractMin` nondeterministically removes and returns *any* stored entry `(u, v, h)` attaining + the minimum key (`isMinEntry s h u v`), or `none` when `dom s = ∅`. -/ +def spec (n_max U V : ℕ) : RelSpec where + State := State n_max + Op := Op n_max + Result := Op.Result (n_max := n_max) + valid s + | .isEmpty => True + | .get _ => True + | .insert u v => size s < n_max ∧ u < U ∧ v < V + | .decreaseKey h u' => ∃ u v, s h = some (u, v) ∧ u' ≤ u + | .extractMin => True + step s o s' res := + match o with + | .isEmpty => s' = s ∧ res = decide (dom s = ∅) + | .get h => s' = s ∧ res = s h + | .insert u v => + ∃ h : Fin n_max, s h = none ∧ s' = Function.update s h (some (u, v)) ∧ res = some h + | .decreaseKey h u' => + ∃ u v, s h = some (u, v) ∧ s' = Function.update s h (some (u', v)) ∧ res = () + | .extractMin => + (dom s = ∅ ∧ s' = s ∧ res = none) ∨ + ∃ h u v, isMinEntry s h u v ∧ s' = Function.update s h none ∧ res = some (u, v, h) + +/-- Every admissible `MinPQ` operation has at least one valid relational transition: +`insert` finds an unused handle by pigeonhole (`exists_none_of_size_lt`), and `extractMin` on a +nonempty queue selects a minimum-key entry (`exists_isMinEntry_of_dom_ne_empty`). -/ +instance instTotalSpec (n_max U V : ℕ) : (spec n_max U V).Total where + step_total s o hv := by + cases o with + | isEmpty => exact ⟨s, decide (dom s = ∅), rfl, rfl⟩ + | get h => exact ⟨s, s h, rfl, rfl⟩ + | insert u v => + obtain ⟨h, hh⟩ := exists_none_of_size_lt hv.1 + exact ⟨Function.update s h (some (u, v)), some h, h, hh, rfl, rfl⟩ + | decreaseKey h u' => + obtain ⟨u, v, hh, _⟩ := hv + exact ⟨Function.update s h (some (u', v)), (), u, v, hh, rfl, rfl⟩ + | extractMin => + by_cases hemp : dom s = ∅ + · exact ⟨s, none, Or.inl ⟨hemp, rfl, rfl⟩⟩ + · obtain ⟨h, u, v, hmin⟩ := exists_isMinEntry_of_dom_ne_empty hemp + exact ⟨Function.update s h none, some (u, v, h), Or.inr ⟨h, u, v, hmin, rfl, rfl⟩⟩ + +/-! ## Deterministic free-list handle allocator refining `MinPQ.spec` -/ + +/-- Internal deterministic allocator state refining `State n_max`: combines the active entry map +`entries` with a LIFO recycled-handle stack `freeList` and a bump pointer `fresh ≤ n_max`. -/ +structure AllocState (n_max : ℕ) where + /-- Active key–value entries indexed by handle `h : Fin n_max`. -/ + entries : State n_max + /-- Recycled unused handles `< fresh`, ordered from top of stack (`head`) to bottom. -/ + freeList : List (Fin n_max) + /-- Bump pointer: all handles `h` with `fresh ≤ h.val` are unused and absent from `freeList`. -/ + fresh : ℕ + +/-- Initial empty allocator state (`entries = empty n_max`, `freeList = []`, `fresh = 0`). -/ +def AllocState.empty (n_max : ℕ) : AllocState n_max := + ⟨MinPQ.empty n_max, [], 0⟩ + +/-- Invariant of the free-list + bump-pointer handle allocator: +1. `s.fresh ≤ n_max`; +2. `s.freeList.Nodup`; +3. every `h ∈ s.freeList` has `h.val < s.fresh` and `s.entries h = none`; +4. every `h : Fin n_max` with `s.fresh ≤ h.val` has `s.entries h = none`; +5. `size s.entries + s.freeList.length = s.fresh`. -/ +def AllocState.WellFormed {n_max : ℕ} (s : AllocState n_max) : Prop := + s.fresh ≤ n_max ∧ + s.freeList.Nodup ∧ + (∀ h ∈ s.freeList, h.val < s.fresh ∧ s.entries h = none) ∧ + (∀ h : Fin n_max, s.fresh ≤ h.val → s.entries h = none) ∧ + size s.entries + s.freeList.length = s.fresh + +theorem AllocState.wellFormed_empty (n_max : ℕ) : (AllocState.empty n_max).WellFormed := + ⟨Nat.zero_le _, List.nodup_nil, fun _ h => absurd h List.not_mem_nil, + fun _ _ => rfl, by simp only [AllocState.empty, size_empty, List.length_nil, Nat.add_zero]⟩ + +/-- Deterministic handle allocation: pops the head of `s.freeList` when nonempty, or allocates the +bump-pointer handle `⟨s.fresh, _⟩` and increments `fresh` when `s.freeList = []` and +`s.fresh < n_max`. -/ +def allocHandle {n_max : ℕ} (s : AllocState n_max) : + Option (Fin n_max × List (Fin n_max) × ℕ) := + match s.freeList with + | h :: tl => some (h, tl, s.fresh) + | [] => + if hlt : s.fresh < n_max then + some (⟨s.fresh, hlt⟩, [], s.fresh + 1) + else + none + +/-- Deterministic transition on `AllocState n_max` using `allocHandle` for `insert` and +`findMin s.entries` (lexicographic `(key, handle)` tie-breaking) for `extractMin`. -/ +def stepAlloc {n_max : ℕ} (s : AllocState n_max) (o : Op n_max) : + AllocState n_max × o.Result := + match o with + | .isEmpty => (s, decide (dom s.entries = ∅)) + | .get h => (s, s.entries h) + | .insert u v => + match allocHandle s with + | some (h, freeList', fresh') => + (⟨Function.update s.entries h (some (u, v)), freeList', fresh'⟩, some h) + | none => (s, none) + | .decreaseKey h u' => + match s.entries h with + | some (_, v) => (⟨Function.update s.entries h (some (u', v)), s.freeList, s.fresh⟩, ()) + | none => (s, ()) + | .extractMin => + match findMin s.entries with + | none => (s, none) + | some (u, v, h) => + (⟨Function.update s.entries h none, h :: s.freeList, s.fresh⟩, some (u, v, h)) + +/-- On any well-formed `AllocState` with `size s.entries < n_max`, `allocHandle s` succeeds, +returns an unused handle `h` (`s.entries h = none`), and preserves `AllocState.WellFormed` after +storing `some (u, v)` at `h`. -/ +theorem allocHandle_spec {n_max : ℕ} {s : AllocState n_max} + (hwf : s.WellFormed) (hsz : size s.entries < n_max) (u v : ℕ) : + ∃ h freeList' fresh', + allocHandle s = some (h, freeList', fresh') ∧ + s.entries h = none ∧ + (AllocState.mk (Function.update s.entries h (some (u, v))) freeList' fresh').WellFormed := by + obtain ⟨hfr_le, hnd, hfree, hbump, hsum⟩ := hwf + rcases hfl : s.freeList with _ | ⟨h, tl⟩ + · have hfr_lt : s.fresh < n_max := by + rw [hfl, List.length_nil, Nat.add_zero] at hsum + omega + let hfin : Fin n_max := ⟨s.fresh, hfr_lt⟩ + have hh_none : s.entries hfin = none := hbump hfin le_rfl + refine ⟨hfin, [], s.fresh + 1, ?_, hh_none, ?_⟩ + · simp only [allocHandle, hfl, hfr_lt, ↓reduceDIte, hfin] + · dsimp only [AllocState.WellFormed] + refine ⟨by omega, List.nodup_nil, fun _ hm => absurd hm List.not_mem_nil, ?_, ?_⟩ + · intro x hx + have hxh : x ≠ hfin := fun e => by subst e; change s.fresh + 1 ≤ s.fresh at hx; omega + rw [Function.update_of_ne hxh] + exact hbump x (by omega) + · rw [size_update_some_of_none hh_none, List.length_nil, Nat.add_zero] + rw [hfl, List.length_nil, Nat.add_zero] at hsum + omega + · have hh_mem : h ∈ s.freeList := hfl ▸ List.mem_cons_self + obtain ⟨hh_lt, hh_none⟩ := hfree h hh_mem + obtain ⟨hh_not_tl, htl_nd⟩ := List.nodup_cons.mp (hfl ▸ hnd) + refine ⟨h, tl, s.fresh, ?_, hh_none, ?_⟩ + · simp only [allocHandle, hfl] + · dsimp only [AllocState.WellFormed] + refine ⟨hfr_le, htl_nd, ?_, ?_, ?_⟩ + · intro x hx + have hxh : x ≠ h := fun e => hh_not_tl (e ▸ hx) + have hx_mem : x ∈ s.freeList := hfl ▸ List.mem_cons_of_mem h hx + exact ⟨(hfree x hx_mem).1, by rw [Function.update_of_ne hxh]; exact (hfree x hx_mem).2⟩ + · intro x hx + have hxh : x ≠ h := fun e => by subst e; omega + rw [Function.update_of_ne hxh] + exact hbump x hx + · rw [size_update_some_of_none hh_none] + rw [hfl, List.length_cons] at hsum + omega + +/-- Every valid operation on a well-formed `AllocState` stepped by `stepAlloc`: +1. satisfies the relational transition `(spec n_max U V).step s.entries o (stepAlloc s o).1.entries + (stepAlloc s o).2`, and +2. preserves `AllocState.WellFormed`. -/ +theorem stepAlloc_refines_spec {n_max U V : ℕ} {s : AllocState n_max} (hwf : s.WellFormed) + (o : Op n_max) (hv : (spec n_max U V).valid s.entries o) : + (spec n_max U V).step s.entries o (stepAlloc s o).1.entries (stepAlloc s o).2 ∧ + (stepAlloc s o).1.WellFormed := by + cases o with + | isEmpty => + exact ⟨⟨rfl, rfl⟩, hwf⟩ + | get h => + exact ⟨⟨rfl, rfl⟩, hwf⟩ + | insert u v => + obtain ⟨h, freeList', fresh', halloc, hh_none, hwf'⟩ := allocHandle_spec hwf hv.1 u v + simp only [stepAlloc, halloc] + exact ⟨⟨h, hh_none, rfl, rfl⟩, hwf'⟩ + | decreaseKey h u' => + obtain ⟨u, v, hsh, _⟩ := hv + obtain ⟨hfr_le, hnd, hfree, hbump, hsum⟩ := hwf + simp only [stepAlloc, hsh] + dsimp only [AllocState.WellFormed] + refine ⟨⟨u, v, hsh, rfl, rfl⟩, hfr_le, hnd, ?_, ?_, ?_⟩ + · intro x hx + have hxh : x ≠ h := fun e => by + subst e + rw [(hfree x hx).2] at hsh + cases hsh + exact ⟨(hfree x hx).1, by rw [Function.update_of_ne hxh]; exact (hfree x hx).2⟩ + · intro x hx + have hxh : x ≠ h := fun e => by + subst e + rw [hbump x hx] at hsh + cases hsh + rw [Function.update_of_ne hxh] + exact hbump x hx + · rw [size_update_some_of_some hsh] + exact hsum + | extractMin => + obtain ⟨hfr_le, hnd, hfree, hbump, hsum⟩ := hwf + rcases hfm : IndexedMinPQ.findMin (toIndexed s.entries) with _ | ⟨h, u⟩ + · have hemp : dom s.entries = ∅ := by + rw [← dom_toIndexed, ← IndexedMinPQ.findMin_eq_none_iff] + exact hfm + have hfm' : findMin s.entries = none := findMin_eq_none_iff.mpr hemp + simp only [stepAlloc, hfm'] + exact ⟨Or.inl ⟨hemp, rfl, rfl⟩, hfr_le, hnd, hfree, hbump, hsum⟩ + · obtain ⟨v, hsh, hfm', hmin⟩ := findMin_eq_some_of_indexed hfm + simp only [stepAlloc, hfm'] + have hh_not_free : h ∉ s.freeList := fun hm => by + rw [(hfree h hm).2] at hsh + cases hsh + have hh_lt : h.val < s.fresh := by + by_contra hge + rw [hbump h (Nat.le_of_not_lt hge)] at hsh + cases hsh + have hsz_pos : 1 ≤ size s.entries := + Finset.card_pos.mpr ⟨h, mem_dom_iff.mpr ⟨u, v, hsh⟩⟩ + dsimp only [AllocState.WellFormed] + refine ⟨Or.inr ⟨h, u, v, hmin, rfl, rfl⟩, + hfr_le, List.nodup_cons.mpr ⟨hh_not_free, hnd⟩, ?_, ?_, ?_⟩ + · intro x hx + rcases List.mem_cons.mp hx with rfl | hx_tl + · exact ⟨hh_lt, Function.update_self _ _ _⟩ + · have hxh : x ≠ h := fun e => hh_not_free (e ▸ hx_tl) + exact ⟨(hfree x hx_tl).1, by rw [Function.update_of_ne hxh]; exact (hfree x hx_tl).2⟩ + · intro x hx + have hxh : x ≠ h := fun e => by subst e; omega + rw [Function.update_of_ne hxh] + exact hbump x hx + · rw [size_update_none_of_some hsh, List.length_cons] + omega + +end Cslib.Algorithms.Lean.MinPQ diff --git a/Complexity/Algorithms/WordRAM/PriorityQueue/FibonacciHeap.lean b/Complexity/Algorithms/WordRAM/PriorityQueue/FibonacciHeap.lean index 4ecef9d46..e436031d1 100644 --- a/Complexity/Algorithms/WordRAM/PriorityQueue/FibonacciHeap.lean +++ b/Complexity/Algorithms/WordRAM/PriorityQueue/FibonacciHeap.lean @@ -10,36 +10,45 @@ module public import Complexity.Algorithms.WordRAM.PriorityQueue.FibonacciHeap.BasicOps public import Complexity.Algorithms.WordRAM.PriorityQueue.FibonacciHeap.Cut public import Complexity.Algorithms.WordRAM.PriorityQueue.FibonacciHeap.Consolidate +public import Complexity.Algorithms.WordRAM.PriorityQueue.FibonacciHeap.MinPQ /-! # Static-array Word-RAM Fibonacci heap This file assembles the standalone static-array Word-RAM Fibonacci heap [Fredman and Tarjan][FredmanTarjan1987] [Cormen et al., Chapter 19][CLRS2009] over -`Cslib.WordRAM.Basis.exact` (and lifted to `Basis.standard`) for -`Cslib.Algorithms.Lean.IndexedMinPQ.spec`. +`Cslib.WordRAM.Basis.exact` (and lifted to `Basis.standard`) for both: -Over `n` vertex handles in a contiguous memory block `[base, base + 9 * n + 1)` with concrete -integer potential `potential x = 80 * (numRoots + 2 * numMarked)` (`initPotential = 0`, +* the fixed-vertex priority queue `Cslib.Algorithms.Lean.IndexedMinPQ.spec` (used by graph + algorithms such as Dijkstra's algorithm), in `9 * n + 1` words at `[base, base + 9 * n + 1)`; and +* the relational key–value priority queue with handles `Cslib.Algorithms.Lean.MinPQ.spec` (where + `insert u v` allocates and returns a handle `h : Fin n_max`), in `11 * n_max + 1` words at + `[base, base + 11 * n_max + 1)` under the `FibonacciHeap.MinPQ` namespace (and top-level aliases + `relSubroutineDS`, `relDynamicProgram`, `relCertificate`, `relAmortized`). + +Over capacity `n` (`n_max`) with concrete integer potential +`potential x = 80 * (numRoots + 2 * numMarked)` (`initPotential = 0`, [Fredman and Tarjan][FredmanTarjan1987], Section 2, p. 604 (the potential defined before Theorem 1); [Cormen et al.][CLRS2009], Section 19.1, eq. (19.1)): -* `initFresh` initializes an empty heap on zeroed `[base, base + 9 * n + 1)` in `O(1)` (`≤ 26`) +* `initFresh` initializes an empty heap on zeroed memory in `O(1)` instructions; +* `initClear` initializes an empty heap on arbitrary dirty `w`-bit memory in `O(n + 1)` instructions; -* `initClear` initializes an empty heap on arbitrary dirty `[base, base + 9 * n + 1)` in `O(n)` - (`≤ 36 * n + 36`) instructions; -* `isEmpty` and `getKey` run in `O(1)` worst-case time (`≤ 12` and `≤ 16` instructions); -* `insert` and `decreaseKey` (with cascading cuts) run in `O(1)` amortized time (`≤ 130` and - `≤ 340` instructions); -* `extractMin` (with degree-table consolidation and deterministic lexicographic `(key, handle)` - tie-breaking) runs in `O(log n)` amortized time (`≤ 420 * (Nat.log 2 n + 1)` instructions). +* `isEmpty` and `getKey` / `get` run in `O(1)` worst-case time; +* `insert` and `decreaseKey` (with cascading cuts) run in `O(1)` amortized time; +* `extractMin` (with degree-table consolidation) runs in `O(log |s| + 1)` amortized time where + `|s|` is the current number of stored elements (`(IndexedMinPQ.dom s).card` or `MinPQ.size s`). ## Main definitions * `Cslib.Algorithms.WordRAM.PriorityQueue.FibonacciHeap.subroutineDS`: `SubroutineDS` certificate - over `Basis.exact`. + over `Basis.exact` for `IndexedMinPQ.spec`. * `Cslib.Algorithms.WordRAM.PriorityQueue.FibonacciHeap.pkg`, `pkgStd`: `PQPackage` bundles over `Basis.exact` and `Basis.standard` for plug-and-play graph algorithms (such as Dijkstra). +* `Cslib.Algorithms.WordRAM.PriorityQueue.FibonacciHeap.relSubroutineDS`, + `relDynamicProgram`, `relCertificate`, `MinPQ.run_ops_exact_fresh`, + `MinPQ.run_ops_exact_reusable`: `RelSubroutineDS` and flat-program realizations of the key–value + Fibonacci heap with handles (`Cslib.Algorithms.Lean.MinPQ.spec`). * `Cslib.Algorithms.WordRAM.PriorityQueue.FibonacciHeap.initFreshProg`, `initClearProg`, `opProg`, `initRegs`, `prepareRegs`, `RunsOps`: compiled flat `Program .exact rFib` artifacts, register calling conventions, and sequential execution predicate for standalone multi-operation execution. @@ -186,7 +195,7 @@ def pkg : PQPackage .exact rFib where charge_getKey _ _ _ _ := le_rfl charge_insert _ _ _ _ _ := le_rfl charge_decreaseKey _ _ _ _ _ := le_rfl - charge_extractMin _ _ _ := le_rfl + charge_extractMin _ _ s := charge_extractMin_le_tExt _ s /-- Parametric `PQPackage .standard rFib` bundle lifted to `Basis.standard`. -/ abbrev pkgStd : PQPackage .standard rFib := pkg.toStandard @@ -337,10 +346,11 @@ private theorem seq_runsOps {n K_max w base : ℕ} (hS : Fits w (base + space n) step_program_halt dispatchCode outFlag y obtain ⟨z, rest, hrest, hinv_z, hpot_rest⟩ := seq_runsOps hS hK ops hinv_y hv_ops refine ⟨z, (m + 1) + rest, .cons hruns ⟨_, hhalt⟩ hdec hmod hrest, hinv_z, ?_⟩ + have hce := charge_extractMin_le_tExt n s cases o <;> simp only [charge, tEmpty, tGet, tIns, tDec, tExt, numQueries, numInserts, numDecreaseKeys, numExtractMins, - Nat.add_mul, Nat.one_mul] at hpot_m ⊢ <;> + Nat.add_mul, Nat.one_mul] at hce hpot_m ⊢ <;> omega private theorem initRegs_bounded {n base w : ℕ} (hS : Fits w (base + space n)) @@ -425,4 +435,36 @@ theorem run_ops_exact_reusable {n K_max w base : ℕ} (hS : Fits w (base + space · change potential x₀ ≤ 0 at hpot₀ omega +/-! ## Top-level aliases for the key–value priority-queue realization (`MinPQ`) -/ + +/-- `RelSubroutineDS` certificate for the key–value Fibonacci heap with handles (`MinPQ.spec`). -/ +abbrev relSubroutineDS {n_max U V w base : ℕ} + (hS : Fits w (base + MinPQ.space n_max)) (hU : Fits w U) (hV : Fits w V) : + RelSubroutineDS MinPQ.subroutineCode (Cslib.Algorithms.Lean.MinPQ.spec n_max U V) w base + ![n_max] (Cslib.Algorithms.Lean.MinPQ.empty n_max) := + MinPQ.relSubroutineDS hS hU hV + +/-- Standalone `DynamicProgram` for the key–value Fibonacci heap with handles (`MinPQ.spec`). -/ +abbrev relDynamicProgram {n_max U V w base : ℕ} + (hS : Fits w (base + MinPQ.space n_max)) (hU : Fits w U) (hV : Fits w V) : + DynamicProgram (Cslib.Algorithms.Lean.MinPQ.spec n_max U V) MinPQ.rMinPQ := + MinPQ.relDynamicProgram hS hU hV + +/-- Standalone `RelDynamicCertificate` for the key–value Fibonacci heap with handles +(`MinPQ.spec`). -/ +abbrev relCertificate {n_max U V w base : ℕ} + (hS : Fits w (base + MinPQ.space n_max)) (hU : Fits w U) (hV : Fits w V) : + RelDynamicCertificate (relDynamicProgram hS hU hV) w := + MinPQ.relCertificate hS hU hV + +/-- Standalone `Stateful.RelAmortized` theorem for the key–value Fibonacci heap with handles +(`MinPQ.spec`). -/ +theorem relAmortized {n_max U V w base : ℕ} + (hS : Fits w (base + MinPQ.space n_max)) (hU : Fits w U) (hV : Fits w V) : + Stateful.RelAmortized (relCertificate hS hU hV).realization + DynamicProgram.OperationTrace.instructions + (fun _ x => MinPQ.potential x) + (fun s o => MinPQ.charge n_max s o + 1) := + MinPQ.relAmortized hS hU hV + end Cslib.Algorithms.WordRAM.PriorityQueue.FibonacciHeap diff --git a/Complexity/Algorithms/WordRAM/PriorityQueue/FibonacciHeap/Consolidate.lean b/Complexity/Algorithms/WordRAM/PriorityQueue/FibonacciHeap/Consolidate.lean index fbc84d4b1..dfd1fc415 100644 --- a/Complexity/Algorithms/WordRAM/PriorityQueue/FibonacciHeap/Consolidate.lean +++ b/Complexity/Algorithms/WordRAM/PriorityQueue/FibonacciHeap/Consolidate.lean @@ -12,13 +12,15 @@ public import Complexity.Algorithms.WordRAM.PriorityQueue.FibonacciHeap.BasicOps /-! # Degree-indexed root consolidation and `extractMin` for a Fibonacci heap -This file proves the correctness, memory-safety, and `O(log n)` amortized instruction bound of -`scatterRootCode` and `extractMinCode` [Fredman and Tarjan][FredmanTarjan1987] +This file proves the correctness, memory-safety, and `O(log |dom s| + 1)` amortized instruction +bound of `scatterRootCode` and `extractMinCode` in the current number of stored elements +`sz = (IndexedMinPQ.dom s).card` [Fredman and Tarjan][FredmanTarjan1987] [Cormen et al., Section 19.2][CLRS2009]: * **Degree-table invariant (`SlotInv`):** Tracks the subset `slotTrees` of consolidated root trees - currently stored in `cons[0..maxDeg]`, maintaining pairwise-distinct degrees, `maxDeg ≤ - 2 * Nat.log 2 n + 1`, and zeroing of all unoccupied entries `cons[d]` (`d ≤ n`). + currently stored in `cons[0..maxDeg]` when consolidating at most `sz` nodes, maintaining + pairwise-distinct degrees, `maxDeg ≤ 2 * Nat.log 2 sz + 1`, and zeroing of all unoccupied entries + `cons[d]` (`d ≤ n`). * **Root scattering (`scatterRootCode_exec`):** Repeatedly links equal-degree roots via `ValidRoot.link` under lexicographic `IndexedMinPQ.PrioLT` using internal potential `50 * slotTrees.length`, @@ -26,9 +28,9 @@ This file proves the correctness, memory-safety, and `O(log n)` amortized instru other live nodes. * **Minimum extraction (`extractMinCode_exec`):** Removes `z = minRoot - 1` (agreeing with `IndexedMinPQ.findMin`), scatters `z`'s children and the `numRoots - 1` remaining roots into - `cons`, and scans only `0 .. maxDeg` (`≤ 2 * Nat.log 2 n + 2` slots) to rebuild the root circular - list and `minRootReg` while restoring `cons = 0` in `t + potential y ≤ 410 * (Nat.log 2 n + 1) + - potential x` instructions. + `cons`, and scans only `0 .. maxDeg` (`≤ 2 * Nat.log 2 (IndexedMinPQ.dom s).card + 2` slots) to + rebuild the root circular list and `minRootReg` while restoring `cons = 0` in + `t + potential y ≤ 410 * (Nat.log 2 (IndexedMinPQ.dom s).card + 1) + potential x` instructions. ## Main definitions @@ -39,8 +41,9 @@ This file proves the correctness, memory-safety, and `O(log n)` amortized instru * `Cslib.Algorithms.WordRAM.PriorityQueue.FibonacciHeap.scatterRootCode_exec`: amortized root scattering and equal-degree linking specification. -* `Cslib.Algorithms.WordRAM.PriorityQueue.FibonacciHeap.extractMinCode_exec`: `O(log n)` amortized - `extractMin` specification. +* `Cslib.Algorithms.WordRAM.PriorityQueue.FibonacciHeap.extractMinCode_exec`: + `O(log |dom s| + 1)` amortized `extractMin` specification in the current number of stored + elements. ## References @@ -97,11 +100,11 @@ theorem ValidRoot.frame_nodes {n : ℕ} {st st' : RawState n} {T : FHTree n} let hu' := h u (T.nodes_eq ▸ List.mem_cons_of_mem T.root hu) hu'.2.2.2.2.2.2) -/-- Every valid subtree `T` disjoint from at least one other handle `z : Fin n` satisfies both -`T.deg ≤ 2 * Nat.log 2 n + 1` and `T.deg + 2 ≤ n`. -/ -theorem deg_bounds_of_extra {n : ℕ} {st : RawState n} {T : FHTree n} {z : Fin n} - (hT : ValidSubtree st none T) (hnd : (z :: T.nodes).Nodup) : - T.deg ≤ 2 * Nat.log 2 n + 1 ∧ T.deg + 2 ≤ n := by +/-- Every valid subtree `T` with `T.size ≤ sz` disjoint from at least one other handle `z : Fin n` +satisfies both `T.deg ≤ 2 * Nat.log 2 sz + 1` and `T.deg + 2 ≤ n`. -/ +theorem deg_bounds_of_extra {n sz : ℕ} {st : RawState n} {T : FHTree n} {z : Fin n} + (hsz : T.size ≤ sz) (hT : ValidSubtree st none T) (hnd : (z :: T.nodes).Nodup) : + T.deg ≤ 2 * Nat.log 2 sz + 1 ∧ T.deg + 2 ≤ n := by have hlen : 1 + T.size ≤ n := by have h : (z :: T.nodes).length ≤ n := by simpa using hnd.length_le_card simp only [List.length_cons] at h @@ -109,8 +112,7 @@ theorem deg_bounds_of_extra {n : ℕ} {st : RawState n} {T : FHTree n} {z : Fin omega have hfib : Nat.fib (T.deg + 2) ≤ T.size := hT.fib_le_size have hd_size : T.deg + 1 ≤ T.size := (deg_add_one_le_fib T.deg).trans hfib - have hd_log : T.deg ≤ 2 * Nat.log 2 n + 1 := - (hT.deg_bounds (m := n) (by omega)).1 + have hd_log : T.deg ≤ 2 * Nat.log 2 sz + 1 := (hT.deg_bounds hsz).1 exact ⟨hd_log, by omega⟩ private theorem nodup_eraseIdx {α : Type*} : @@ -144,18 +146,18 @@ private theorem length_eraseIdx_add_one {α : Type*} : /-! ## Consolidation table invariant `SlotInv` -/ /-- Invariant for the degree consolidation table `st.cons` holding `slotTrees` within -`0 .. maxDeg`. -/ -def SlotInv (n : ℕ) (st : RawState n) (slotTrees : List (FHTree n)) (maxDeg : ℕ) : Prop := +`0 .. maxDeg`, where `sz` bounds the total number of nodes being consolidated. -/ +def SlotInv (n sz : ℕ) (st : RawState n) (slotTrees : List (FHTree n)) (maxDeg : ℕ) : Prop := (slotTrees.map FHTree.deg).Nodup ∧ (∀ T ∈ slotTrees, ValidRoot st none T ∧ T.deg ≤ maxDeg) ∧ (∀ T ∈ slotTrees, st.cons T.deg = T.root.val + 1) ∧ (∀ d ≤ n, (∀ T ∈ slotTrees, T.deg ≠ d) → st.cons d = 0) ∧ - maxDeg ≤ 2 * Nat.log 2 n + 1 ∧ + maxDeg ≤ 2 * Nat.log 2 sz + 1 ∧ (maxDeg = 0 ∨ maxDeg + 2 ≤ n) /-- Initial empty consolidation table when `∀ d ≤ n, st.cons d = 0`. -/ -theorem SlotInv.empty {n : ℕ} {st : RawState n} (hcons : ∀ d ≤ n, st.cons d = 0) : - SlotInv n st [] 0 := +theorem SlotInv.empty {n sz : ℕ} {st : RawState n} (hcons : ∀ d ≤ n, st.cons d = 0) : + SlotInv n sz st [] 0 := ⟨List.nodup_nil, fun _ h => absurd h List.not_mem_nil, fun _ h => absurd h List.not_mem_nil, @@ -163,11 +165,11 @@ theorem SlotInv.empty {n : ℕ} {st : RawState n} (hcons : ∀ d ≤ n, st.cons by omega, Or.inl rfl⟩ /-- Framing `SlotInv` across a transition preserving `cons` and `ValidRoot` on `slotTrees`. -/ -theorem SlotInv.frame {n maxDeg : ℕ} {st st' : RawState n} {slotTrees : List (FHTree n)} - (hs : SlotInv n st slotTrees maxDeg) +theorem SlotInv.frame {n sz maxDeg : ℕ} {st st' : RawState n} {slotTrees : List (FHTree n)} + (hs : SlotInv n sz st slotTrees maxDeg) (hcons : st'.cons = st.cons) (hvalid : ∀ T ∈ slotTrees, ValidRoot st' none T) : - SlotInv n st' slotTrees maxDeg := by + SlotInv n sz st' slotTrees maxDeg := by obtain ⟨hnd, htr, hhit, hemp, hmd1, hmd2⟩ := hs exact ⟨hnd, fun T hT => ⟨hvalid T hT, (htr T hT).2⟩, @@ -178,8 +180,8 @@ theorem SlotInv.frame {n maxDeg : ℕ} {st st' : RawState n} {slotTrees : List ( /-- Lookup in `st.cons d` (`d ≤ n`): either `st.cons d = 0` and no tree in `slotTrees` has degree `d`, or a unique index `i < slotTrees.length` has `(slotTrees[i]).deg = d` and `st.cons d = (slotTrees[i]).root.val + 1`. -/ -theorem SlotInv.lookup {n maxDeg : ℕ} {st : RawState n} {slotTrees : List (FHTree n)} - (hs : SlotInv n st slotTrees maxDeg) {d : ℕ} (hd : d ≤ n) : +theorem SlotInv.lookup {n sz maxDeg : ℕ} {st : RawState n} {slotTrees : List (FHTree n)} + (hs : SlotInv n sz st slotTrees maxDeg) {d : ℕ} (hd : d ≤ n) : (st.cons d = 0 ∧ ∀ T ∈ slotTrees, T.deg ≠ d) ∨ ∃ (i : ℕ) (hi : i < slotTrees.length), (slotTrees[i]).deg = d ∧ @@ -221,13 +223,13 @@ private theorem getElem_mem_eraseIdx_of_ne {α : Type*} : fun e => hki (congrArg Nat.succ e)) /-- Clearing slot `d = (slotTrees[i]).deg` (`d ≤ n`) removes `slotTrees[i]` from `SlotInv`. -/ -theorem SlotInv.step_collision {n maxDeg : ℕ} {st st' : RawState n} {slotTrees : List (FHTree n)} - (hs : SlotInv n st slotTrees maxDeg) {i d : ℕ} (hi : i < slotTrees.length) +theorem SlotInv.step_collision {n sz maxDeg : ℕ} {st st' : RawState n} {slotTrees : List (FHTree n)} + (hs : SlotInv n sz st slotTrees maxDeg) {i d : ℕ} (hi : i < slotTrees.length) (hdeg_i : (slotTrees[i]).deg = d) (_hd : d ≤ n) (hno_other : ∀ R ∈ slotTrees.eraseIdx i, R.deg ≠ d) (hcons' : st'.cons = Function.update st.cons d 0) (hvalid' : ∀ R ∈ slotTrees.eraseIdx i, ValidRoot st' none R) : - SlotInv n st' (slotTrees.eraseIdx i) maxDeg := by + SlotInv n sz st' (slotTrees.eraseIdx i) maxDeg := by obtain ⟨hnd, htr, hhit, hemp, hmd1, hmd2⟩ := hs refine ⟨by rw [map_eraseIdx_eq]; exact nodup_eraseIdx i hnd, ?_, ?_, ?_, hmd1, hmd2⟩ · intro R hR @@ -250,13 +252,13 @@ theorem SlotInv.step_collision {n maxDeg : ℕ} {st st' : RawState n} {slotTrees /-- Storing a consolidated tree `T_cur` of unoccupied degree `d` into `st.cons d` and updating `maxDeg` to `max maxDeg d` inserts `T_cur` into `SlotInv`. -/ -theorem SlotInv.insert_empty {n maxDeg : ℕ} {st : RawState n} {slotTrees : List (FHTree n)} - (hs : SlotInv n st slotTrees maxDeg) {T_cur : FHTree n} {d : ℕ} - (_hd_n : d + 2 ≤ n) (hd_log : d ≤ 2 * Nat.log 2 n + 1) +theorem SlotInv.insert_empty {n sz maxDeg : ℕ} {st : RawState n} {slotTrees : List (FHTree n)} + (hs : SlotInv n sz st slotTrees maxDeg) {T_cur : FHTree n} {d : ℕ} + (_hd_n : d + 2 ≤ n) (hd_log : d ≤ 2 * Nat.log 2 sz + 1) (hT_deg : T_cur.deg = d) (hnone : ∀ R ∈ slotTrees, R.deg ≠ d) (hT_valid : ValidRoot st none T_cur) : - SlotInv n { st with cons := Function.update st.cons d (T_cur.root.val + 1) } + SlotInv n sz { st with cons := Function.update st.cons d (T_cur.root.val + 1) } (T_cur :: slotTrees) (max maxDeg d) := by obtain ⟨hnd, htr, hhit, hemp, hmd1, _⟩ := hs let st' : RawState n := { st with cons := Function.update st.cons d (T_cur.root.val + 1) } @@ -287,8 +289,8 @@ theorem SlotInv.insert_empty {n maxDeg : ℕ} {st : RawState n} {slotTrees : Lis /-- Once every degree `< maxDeg + 1` has been cleared from `SlotInv`, `slotTrees = []` and all entries `st.cons d` (`d ≤ n`) are `0`. -/ -theorem SlotInv.done_of_cleared {n maxDeg : ℕ} {st : RawState n} {slotTrees : List (FHTree n)} - (hs : SlotInv n st slotTrees maxDeg) +theorem SlotInv.done_of_cleared {n sz maxDeg : ℕ} {st : RawState n} {slotTrees : List (FHTree n)} + (hs : SlotInv n sz st slotTrees maxDeg) (hcleared : ∀ d < maxDeg + 1, ∀ T ∈ slotTrees, T.deg ≠ d) : slotTrees = [] ∧ ∀ d ≤ n, st.cons d = 0 := by obtain ⟨_, htr, _, hemp, _, _⟩ := hs @@ -357,12 +359,12 @@ private abbrev scatterBody : Code .exact rFib := `T_slot` (`s5`) under `IndexedMinPQ.PrioLT`, links the loser as newest child of the winner via `ValidRoot.link`, advances `curD` to `curD + 1`, and decreases `slotTrees.length` by `1` in `≤ 43` steps. -/ -private theorem scatterBody_exec {n base w maxDeg curD : ℕ} {a : Data rFib} +private theorem scatterBody_exec {n sz base w maxDeg curD : ℕ} {a : Data rFib} {T_cur : FHTree n} {slotTrees : List (FHTree n)} {otherNodes : List (Fin n)} {z : Fin n} (hb : a.Bounded w) (hbr : BaseRegs n base a) (hz_mem : z ∈ otherNodes) (hnd : (T_cur.nodes ++ (forestNodes slotTrees ++ otherNodes)).Nodup) - (hs : SlotInv n (RawState.ofData n base a) slotTrees maxDeg) + (hs : SlotInv n sz (RawState.ofData n base a) slotTrees maxDeg) (hT_cur : ValidRoot (RawState.ofData n base a) none T_cur) (hcur_deg : T_cur.deg = curD) (ha_s4 : a.regs s4 = T_cur.root.val) @@ -379,7 +381,7 @@ private theorem scatterBody_exec {n base w maxDeg curD : ℕ} {a : Data rFib} slotTrees'.length + 1 = slotTrees.length ∧ (T_next.nodes ++ forestNodes slotTrees').Perm (T_cur.nodes ++ forestNodes slotTrees) ∧ (T_next.nodes ++ (forestNodes slotTrees' ++ otherNodes)).Nodup ∧ - SlotInv n (RawState.ofData n base a') slotTrees' maxDeg ∧ + SlotInv n sz (RawState.ofData n base a') slotTrees' maxDeg ∧ ValidRoot (RawState.ofData n base a') none T_next ∧ T_next.deg = curD + 1 ∧ a'.regs s4 = T_next.root.val ∧ @@ -409,7 +411,8 @@ private theorem scatterBody_exec {n base w maxDeg curD : ℕ} {a : Data rFib} have hnd_cur_rest := List.nodup_append.mp hnd have hz_in_ro : z ∈ forestNodes slotTrees ++ otherNodes := List.mem_append_right _ hz_mem have hz_not_cur : z ∉ T_cur.nodes := fun hm => hnd_cur_rest.2.2 z hm z hz_in_ro rfl - have hdeg_bds := deg_bounds_of_extra hT_cur.1 (List.nodup_cons.mpr ⟨hz_not_cur, hnd_cur_rest.1⟩) + have hdeg_bds := + deg_bounds_of_extra le_rfl hT_cur.1 (List.nodup_cons.mpr ⟨hz_not_cur, hnd_cur_rest.1⟩) rw [hcur_deg] at hdeg_bds have hd_le_n : curD ≤ n := by omega have hd1_le_n : curD + 1 ≤ n := by omega @@ -1155,7 +1158,7 @@ private theorem scatterBody_exec {n base w maxDeg curD : ℕ} {a : Data rFib} exact ((hperm12.append_right (forestNodes slotTrees' ++ otherNodes)).nodup_iff).mpr hnd_assoc have hdisj_ro : ∀ u ∈ forestNodes slotTrees' ++ otherNodes, u ∉ T₁.nodes ++ T₂.nodes := fun u hu hm => (List.nodup_append.mp (hT_next_nodes ▸ hnd_next)).2.2 u hm u hu rfl - have hs6 : SlotInv n (RawState.ofData n base a6) slotTrees' maxDeg := by + have hs6 : SlotInv n sz (RawState.ofData n base a6) slotTrees' maxDeg := by rw [hst6] refine hs.step_collision hidx hslot_deg hd_le_n hno_other hco5 fun R hR => ?_ obtain ⟨j, hj, _, hR_eq⟩ := List.mem_eraseIdx_iff_getElem.mp hR @@ -1196,13 +1199,13 @@ private theorem scatterBody_exec {n base w maxDeg curD : ℕ} {a : Data rFib} /-- Full `while zeroP < s3 do scatterBody` loop by induction on `slotTrees.length`: terminates at an unoccupied degree slot `curD_f` in `t + 50 * slotTrees_f.length ≤ 2 + 50 * slotTrees.length` instructions. -/ -private theorem scatterLoop_exec {n base w maxDeg : ℕ} {otherNodes : List (Fin n)} {z : Fin n} +private theorem scatterLoop_exec {n sz base w maxDeg : ℕ} {otherNodes : List (Fin n)} {z : Fin n} (hz_mem : z ∈ otherNodes) : ∀ (k : ℕ) {a : Data rFib} {T_cur : FHTree n} {slotTrees : List (FHTree n)} {curD : ℕ}, slotTrees.length = k → a.Bounded w → BaseRegs n base a → (T_cur.nodes ++ (forestNodes slotTrees ++ otherNodes)).Nodup → - SlotInv n (RawState.ofData n base a) slotTrees maxDeg → + SlotInv n sz (RawState.ofData n base a) slotTrees maxDeg → ValidRoot (RawState.ofData n base a) none T_cur → T_cur.deg = curD → a.regs s4 = T_cur.root.val → @@ -1219,7 +1222,7 @@ private theorem scatterLoop_exec {n base w maxDeg : ℕ} {otherNodes : List (Fin z_lp.regs maxDegReg = maxDeg ∧ (T_f.nodes ++ forestNodes slotTrees_f).Perm (T_cur.nodes ++ forestNodes slotTrees) ∧ (T_f.nodes ++ (forestNodes slotTrees_f ++ otherNodes)).Nodup ∧ - SlotInv n (RawState.ofData n base z_lp) slotTrees_f maxDeg ∧ + SlotInv n sz (RawState.ofData n base z_lp) slotTrees_f maxDeg ∧ ValidRoot (RawState.ofData n base z_lp) none T_f ∧ T_f.deg = curD_f ∧ z_lp.regs s4 = T_f.root.val ∧ @@ -1274,12 +1277,13 @@ private theorem scatterLoop_exec {n base w maxDeg : ℕ} {otherNodes : List (Fin /-- `scatterRootCode` inserts root `T₀` (`iterCur = T₀.root.val`) into `cons`, linking equal-degree roots and updating `maxDegReg` in `t + 50 * slotTrees'.length ≤ 63 + 50 * slotTrees₀.length` instructions while preserving all registers `< 33` and framing `otherNodes`. -/ -theorem scatterRootCode_exec {n base w maxDeg₀ : ℕ} {x : Data rFib} +theorem scatterRootCode_exec {n sz base w maxDeg₀ : ℕ} {x : Data rFib} {T₀ : FHTree n} {slotTrees₀ : List (FHTree n)} {otherNodes : List (Fin n)} {z : Fin n} (hb : x.Bounded w) (hbr : BaseRegs n base x) (hz_mem : z ∈ otherNodes) (hnd : (T₀.nodes ++ (forestNodes slotTrees₀ ++ otherNodes)).Nodup) - (hs : SlotInv n (RawState.ofData n base x) slotTrees₀ maxDeg₀) + (hsz : (T₀.nodes ++ (forestNodes slotTrees₀ ++ otherNodes)).length ≤ sz) + (hs : SlotInv n sz (RawState.ofData n base x) slotTrees₀ maxDeg₀) (hT₀ : ValidRoot (RawState.ofData n base x) none T₀) (hx_ic : x.regs iterCur = T₀.root.val) (hx_md : x.regs maxDegReg = maxDeg₀) : @@ -1292,7 +1296,7 @@ theorem scatterRootCode_exec {n base w maxDeg₀ : ℕ} {x : Data rFib} y.regs maxDegReg = maxDeg' ∧ (forestNodes slotTrees').Perm (T₀.nodes ++ forestNodes slotTrees₀) ∧ (forestNodes slotTrees' ++ otherNodes).Nodup ∧ - SlotInv n (RawState.ofData n base y) slotTrees' maxDeg' ∧ + SlotInv n sz (RawState.ofData n base y) slotTrees' maxDeg' ∧ (let st := RawState.ofData n base x let st' := RawState.ofData n base y st'.key = st.key ∧ st'.mark = st.mark ∧ st'.inHeap = st.inHeap ∧ @@ -1310,7 +1314,7 @@ theorem scatterRootCode_exec {n base w maxDeg₀ : ℕ} {x : Data rFib} have hnd_0 := List.nodup_append.mp hnd have hz_not_0 : z ∉ T₀.nodes := fun hm => hnd_0.2.2 z hm z (List.mem_append_right _ hz_mem) rfl - have hd0_bds := deg_bounds_of_extra hT₀.1 (List.nodup_cons.mpr ⟨hz_not_0, hnd_0.1⟩) + have hd0_bds := deg_bounds_of_extra le_rfl hT₀.1 (List.nodup_cons.mpr ⟨hz_not_0, hnd_0.1⟩) have hd0_le_n : d0 ≤ n := by omega have hdeg_u0 : st.deg u0 = d0 := by cases hT₀.1 with @@ -1394,7 +1398,12 @@ theorem scatterRootCode_exec {n base w maxDeg₀ : ℕ} {x : Data rFib} have hnd_f := List.nodup_append.mp hnd_lp have hz_not_f : z ∉ T_f.nodes := fun hm => hnd_f.2.2 z hm z (List.mem_append_right _ hz_mem) rfl - have hdf_bds := deg_bounds_of_extra hTf.1 (List.nodup_cons.mpr ⟨hz_not_f, hnd_f.1⟩) + have hTf_sz : T_f.size ≤ sz := by + have hp_len := (hperm_lp.append_right otherNodes).length_eq + simp only [List.length_append] at hp_len hsz + unfold FHTree.size + omega + have hdf_bds := deg_bounds_of_extra hTf_sz hTf.1 (List.nodup_cons.mpr ⟨hz_not_f, hnd_f.1⟩) rw [hdeg_f] at hdf_bds have hdf_le_n : curD_f ≤ n := by omega have hcons_f_zero : st_lp.cons curD_f = 0 := by @@ -1565,14 +1574,15 @@ private abbrev childScatterLoop : Code .exact rFib := /-- Phase A1 loop: detaches each child in `c_trees` along `c_order`, clears its `par` and `mark` (decrementing `numMarkedReg` when marked), and scatters it into `cons`. -/ -private theorem scatterChildrenLoop_exec {n base w : ℕ} +private theorem scatterChildrenLoop_exec {n sz base w : ℕ} (c_order : List (Fin n)) : ∀ (a : Data rFib) (c_trees slotTrees : List (FHTree n)) (fixedNodes : List (Fin n)) (z : Fin n) (maxDeg : ℕ), a.Bounded w → BaseRegs n base a → z ∈ fixedNodes → (forestNodes c_trees ++ (forestNodes slotTrees ++ fixedNodes)).Nodup → - SlotInv n (RawState.ofData n base a) slotTrees maxDeg → + (forestNodes c_trees ++ (forestNodes slotTrees ++ fixedNodes)).length ≤ sz → + SlotInv n sz (RawState.ofData n base a) slotTrees maxDeg → (∀ c ∈ c_trees, ValidSubtree (RawState.ofData n base a) none c) → c_order.Perm (c_trees.map FHTree.root) → RightChain (RawState.ofData n base a).right c_order → @@ -1590,7 +1600,7 @@ private theorem scatterChildrenLoop_exec {n base w : ℕ} b.regs maxDegReg = maxDeg' ∧ (forestNodes slotTrees').Perm (forestNodes c_trees ++ forestNodes slotTrees) ∧ (forestNodes slotTrees' ++ fixedNodes).Nodup ∧ - SlotInv n (RawState.ofData n base b) slotTrees' maxDeg' ∧ + SlotInv n sz (RawState.ofData n base b) slotTrees' maxDeg' ∧ (let st := RawState.ofData n base a let st' := RawState.ofData n base b st'.key = st.key ∧ st'.inHeap = st.inHeap ∧ @@ -1602,7 +1612,7 @@ private theorem scatterChildrenLoop_exec {n base w : ℕ} st'.deg v = st.deg v ∧ st'.mark v = st.mark v) := by induction c_order with | nil => - intro a c_trees slotTrees fixedNodes z maxDeg hb hbr _ hnd hs _ hperm _ hrem hmd hnm _ + intro a c_trees slotTrees fixedNodes z maxDeg hb hbr _ hnd _ hs _ hperm _ hrem hmd hnm _ have hz0 : a.regs zeroP = 0 := hbr.2.2.2.2.2.2.2.2.2.2.2.1 have hc : ¬(Cond.lt zeroP remReg).Holds a := by simp only [Cond.Holds, hz0, hrem, List.length_nil, Nat.lt_irrefl, not_false_eq_true] @@ -1618,8 +1628,8 @@ private theorem scatterChildrenLoop_exec {n base w : ℕ} by simpa only [forestNodes, List.nil_append] using hnd, hs, ⟨rfl, rfl, rfl, rfl, rfl, hnm, le_rfl, fun _ _ => ⟨rfl, rfl, rfl, rfl, rfl, rfl⟩⟩⟩ | cons u c_tl ih => - intro a c_trees slotTrees fixedNodes z maxDeg hb hbr hz_mem hnd hs hsub hperm hchain hrem hmd - hnm hic + intro a c_trees slotTrees fixedNodes z maxDeg hb hbr hz_mem hnd hsz hs hsub hperm hchain hrem + hmd hnm hic set st := RawState.ofData n base a with hst_def have hfit_stop := hbr.fits_stop hb have hz0 : a.regs zeroP = 0 := hbr.2.2.2.2.2.2.2.2.2.2.2.1 @@ -1864,7 +1874,7 @@ private theorem scatterChildrenLoop_exec {n base w : ℕ} exact ⟨by rw [hin_m], hm_other x hxu, by rw [hd_m], by rw [hch_m], by rw [hk_m]⟩ · have hxu : x ≠ u := fun e => hu_not_desc (e ▸ hx) exact ⟨by rw [hpar_m, Function.update_of_ne hxu], by rw [hl_m], by rw [hr_m], Iff.rfl⟩ - have hs_mid : SlotInv n (RawState.ofData n base a_mid) slotTrees maxDeg := by + have hs_mid : SlotInv n sz (RawState.ofData n base a_mid) slotTrees maxDeg := by refine hs.frame hcons_m fun R hR => (hs.2.1 R hR).1.frame_nodes fun x hx => ?_ have hxu : x ≠ u := fun e => hu_not_other (List.mem_append_right _ (List.mem_append_left _ @@ -1876,6 +1886,12 @@ private theorem scatterChildrenLoop_exec {n base w : ℕ} (T_u.nodes ++ (forestNodes slotTrees ++ (forestNodes c_rest ++ fixedNodes))).Nodup := (perm_reassoc_mid T_u.nodes (forestNodes c_rest) (forestNodes slotTrees) fixedNodes).nodup_iff.mpr hnd_split + have hsz_for_sc : + (T_u.nodes ++ (forestNodes slotTrees ++ (forestNodes c_rest ++ fixedNodes))).length ≤ + sz := by + have hp := (perm_reassoc_mid T_u.nodes (forestNodes c_rest) (forestNodes slotTrees) + fixedNodes).trans (hperm_fn.append_right _) + exact hp.length_eq.le.trans hsz have hmid_ic : a_mid.regs iterCur = T_u.root.val := by rw [hregs_mid iterCur (by decide) (by decide)] simp only [a1, Data.set_regs, Data.write_regs, @@ -1892,7 +1908,7 @@ private theorem scatterChildrenLoop_exec {n base w : ℕ} hmd_sc, hperm_sc, hnd_sc, hs_sc, hk_sc, hmk_sc, hin_sc, hmin_sc, hnr_sc, hnm_sc, hsz_sc, ho_sc⟩ := scatterRootCode_exec hb_mid hbr_mid (List.mem_append_right _ hz_mem) - hnd_for_sc hs_mid hTu_mid hmid_ic hmid_md + hnd_for_sc hsz_for_sc hs_mid hTu_mid hmid_ic hmid_md -- Step 4 (`a_sc → a_next`, 2 steps): `iterCur ← iterNxt; remReg ← remReg - oneP` have hsc_nxt : a_sc.regs iterNxt = nxtVal := by rw [hlow_sc iterNxt (by decide), hregs_mid iterNxt (by decide) (by decide)] @@ -1932,7 +1948,14 @@ private theorem scatterChildrenLoop_exec {n base w : ℕ} have hnd_next : (forestNodes c_rest ++ (forestNodes slotTrees_1 ++ fixedNodes)).Nodup := (perm_swap_front (forestNodes slotTrees_1) (forestNodes c_rest) fixedNodes).nodup_iff.mp hnd_sc - have hs_next : SlotInv n (RawState.ofData n base a_next) slotTrees_1 maxDeg_1 := by + have hsz_next : + (forestNodes c_rest ++ (forestNodes slotTrees_1 ++ fixedNodes)).length ≤ sz := by + have hp1 := perm_swap_front (forestNodes slotTrees_1) (forestNodes c_rest) fixedNodes + have hp2 := hperm_sc.append_right (forestNodes c_rest ++ fixedNodes) + have hp_len := (hp1.symm.trans hp2).length_eq + simp only [List.length_append] at hp_len hsz_for_sc ⊢ + omega + have hs_next : SlotInv n sz (RawState.ofData n base a_next) slotTrees_1 maxDeg_1 := by rw [hst_next]; exact hs_sc have hsub_next : ∀ c ∈ c_rest, ValidSubtree (RawState.ofData n base a_next) none c := by intro c hc_mem @@ -1976,8 +1999,8 @@ private theorem scatterChildrenLoop_exec {n base w : ℕ} obtain ⟨b, slotTrees', maxDeg', t_tl, he_tl, ht_tl, hb_b, hbr_b, hmod_tl, hregs_tl, hmd_b, hperm_b, hnd_b, hs_b, hk_b, hin_b, hmin_b, hnr_b, hsz_b, hnm_b, hnm_le_b, ho_b⟩ := - ih a_next c_rest slotTrees_1 fixedNodes z maxDeg_1 hb_next hbr_next hz_mem hnd_next hs_next - hsub_next hperm_tl hchain_next + ih a_next c_rest slotTrees_1 fixedNodes z maxDeg_1 hb_next hbr_next hz_mem hnd_next hsz_next + hs_next hsub_next hperm_tl hchain_next (by simp only [a_next, Data.set_regs, Function.update_self]) (by simp only [a_next, Data.set_regs, Function.update_of_ne (by decide : maxDegReg ≠ remReg), @@ -2036,14 +2059,15 @@ private abbrev rootScatterLoop : Code .exact rFib := .loop (.lt zeroP remReg) rootScatterBody /-- Phase A2 loop: scatters each remaining root in `r_trees` along `r_order` into `cons`. -/ -private theorem scatterRootsLoop_exec {n base w : ℕ} +private theorem scatterRootsLoop_exec {n sz base w : ℕ} (r_order : List (Fin n)) : ∀ (a : Data rFib) (r_trees slotTrees : List (FHTree n)) (fixedNodes : List (Fin n)) (z : Fin n) (maxDeg : ℕ), a.Bounded w → BaseRegs n base a → z ∈ fixedNodes → (forestNodes r_trees ++ (forestNodes slotTrees ++ fixedNodes)).Nodup → - SlotInv n (RawState.ofData n base a) slotTrees maxDeg → + (forestNodes r_trees ++ (forestNodes slotTrees ++ fixedNodes)).length ≤ sz → + SlotInv n sz (RawState.ofData n base a) slotTrees maxDeg → (∀ R ∈ r_trees, ValidRoot (RawState.ofData n base a) none R) → r_order.Perm (r_trees.map FHTree.root) → RightChain (RawState.ofData n base a).right r_order → @@ -2060,7 +2084,7 @@ private theorem scatterRootsLoop_exec {n base w : ℕ} b.regs maxDegReg = maxDeg' ∧ (forestNodes slotTrees').Perm (forestNodes r_trees ++ forestNodes slotTrees) ∧ (forestNodes slotTrees' ++ fixedNodes).Nodup ∧ - SlotInv n (RawState.ofData n base b) slotTrees' maxDeg' ∧ + SlotInv n sz (RawState.ofData n base b) slotTrees' maxDeg' ∧ (let st := RawState.ofData n base a let st' := RawState.ofData n base b st'.key = st.key ∧ st'.mark = st.mark ∧ st'.inHeap = st.inHeap ∧ @@ -2071,7 +2095,7 @@ private theorem scatterRootsLoop_exec {n base w : ℕ} st'.left v = st.left v ∧ st'.right v = st.right v ∧ st'.deg v = st.deg v) := by induction r_order with | nil => - intro a r_trees slotTrees fixedNodes _ maxDeg hb hbr _ hnd hs _ hperm _ hrem hmd _ + intro a r_trees slotTrees fixedNodes _ maxDeg hb hbr _ hnd _ hs _ hperm _ hrem hmd _ have hz0 : a.regs zeroP = 0 := hbr.2.2.2.2.2.2.2.2.2.2.2.1 have hc : ¬(Cond.lt zeroP remReg).Holds a := by simp only [Cond.Holds, hz0, hrem, List.length_nil, Nat.lt_irrefl, not_false_eq_true] @@ -2087,7 +2111,7 @@ private theorem scatterRootsLoop_exec {n base w : ℕ} by simpa only [forestNodes, List.nil_append] using hnd, hs, ⟨rfl, rfl, rfl, rfl, rfl, rfl, rfl, fun _ _ => ⟨rfl, rfl, rfl, rfl, rfl⟩⟩⟩ | cons u r_tl ih => - intro a r_trees slotTrees fixedNodes z maxDeg hb hbr hz_mem hnd hs hroots hperm hchain hrem + intro a r_trees slotTrees fixedNodes z maxDeg hb hbr hz_mem hnd hsz hs hroots hperm hchain hrem hmd hic set st := RawState.ofData n base a with hst_def have hfit_stop := hbr.fits_stop hb @@ -2108,6 +2132,12 @@ private theorem scatterRootsLoop_exec {n base w : ℕ} (T_u.nodes ++ (forestNodes slotTrees ++ (forestNodes r_rest ++ fixedNodes))).Nodup := (perm_reassoc_mid T_u.nodes (forestNodes r_rest) (forestNodes slotTrees) fixedNodes).nodup_iff.mpr hnd_split + have hsz_for_sc : + (T_u.nodes ++ (forestNodes slotTrees ++ (forestNodes r_rest ++ fixedNodes))).length ≤ + sz := by + have hp := (perm_reassoc_mid T_u.nodes (forestNodes r_rest) (forestNodes slotTrees) + fixedNodes).trans (hperm_fn.append_right _) + exact hp.length_eq.le.trans hsz -- Step 1 (`a → a1`, 2 steps): `s0 ← rightBase + iterCur; iterNxt ← *s0` let nxtVal := st.right u let a1 := (a.set s0 (base + 4 * n + u.val)).set iterNxt nxtVal @@ -2135,7 +2165,7 @@ private theorem scatterRootsLoop_exec {n base w : ℕ} hmd_sc, hperm_sc, hnd_sc, hs_sc, hk_sc, hmk_sc, hin_sc, hmin_sc, hnr_sc, hnm_sc, hsz_sc, ho_sc⟩ := scatterRootCode_exec hb1 hbr1 (List.mem_append_right _ hz_mem) - hnd_for_sc (by rw [hst1]; exact hs) (by rw [hst1]; exact hTu_valid) + hnd_for_sc hsz_for_sc (by rw [hst1]; exact hs) (by rw [hst1]; exact hTu_valid) (by simp only [a1, Data.set_regs, Function.update_of_ne (by decide : iterCur ≠ iterNxt), Function.update_of_ne (by decide : iterCur ≠ s0), hu_ic, hTu_r]) (by simp only [a1, Data.set_regs, Function.update_of_ne (by decide : maxDegReg ≠ iterNxt), @@ -2173,6 +2203,13 @@ private theorem scatterRootsLoop_exec {n base w : ℕ} have hnd_next : (forestNodes r_rest ++ (forestNodes slotTrees_1 ++ fixedNodes)).Nodup := (perm_swap_front (forestNodes slotTrees_1) (forestNodes r_rest) fixedNodes).nodup_iff.mp hnd_sc + have hsz_next : + (forestNodes r_rest ++ (forestNodes slotTrees_1 ++ fixedNodes)).length ≤ sz := by + have hp1 := perm_swap_front (forestNodes slotTrees_1) (forestNodes r_rest) fixedNodes + have hp2 := hperm_sc.append_right (forestNodes r_rest ++ fixedNodes) + have hp_len := (hp1.symm.trans hp2).length_eq + simp only [List.length_append] at hp_len hsz_for_sc ⊢ + omega have hroots_next : ∀ R ∈ r_rest, ValidRoot (RawState.ofData n base a_next) none R := by intro R hR_mem obtain ⟨j, hj, _, hR_eq⟩ := List.mem_eraseIdx_iff_getElem.mp hR_mem @@ -2206,7 +2243,7 @@ private theorem scatterRootsLoop_exec {n base w : ℕ} obtain ⟨b, slotTrees', maxDeg', t_tl, he_tl, ht_tl, hb_b, hbr_b, hmod_tl, hregs_tl, hmd_b, hperm_b, hnd_b, hs_b, hk_b, hmk_b, hin_b, hmin_b, hnr_b, hnm_b, hsz_b, ho_b⟩ := - ih a_next r_rest slotTrees_1 fixedNodes z maxDeg_1 hb_next hbr_next hz_mem hnd_next + ih a_next r_rest slotTrees_1 fixedNodes z maxDeg_1 hb_next hbr_next hz_mem hnd_next hsz_next (by rw [hst_next]; exact hs_sc) hroots_next hperm_tl hchain_next (by simp only [a_next, Data.set_regs, Function.update_self]) (by simp only [a_next, Data.set_regs, @@ -2455,14 +2492,14 @@ private abbrev gatherSlotsLoop : Code .exact rFib := /-- Phase B loop: scans `curD` up to `maxDeg + 1` (`curD + rem = maxDeg + 1`), clearing every occupied `cons[curD]` and splicing its consolidated root into `accRoots` via `addRootAndUpdateMin iterCur`. -/ -private theorem gatherSlotsLoop_exec {n base w maxDeg : ℕ} (hn_pos : 1 ≤ n) +private theorem gatherSlotsLoop_exec {n sz base w maxDeg : ℕ} (hn_pos : 1 ≤ n) (rem : ℕ) : ∀ (a : Data rFib) (curD : ℕ) (slotTrees accRoots : List (FHTree n)) (z : Fin n), curD + rem = maxDeg + 1 → a.Bounded w → BaseRegs n base a → a.regs curDReg = curD → a.regs maxDegReg = maxDeg + 1 → - SlotInv n (RawState.ofData n base a) slotTrees maxDeg → + SlotInv n sz (RawState.ofData n base a) slotTrees maxDeg → (∀ d < curD, ∀ T ∈ slotTrees, T.deg ≠ d) → (forestNodes slotTrees ++ (z :: forestNodes accRoots)).Nodup → (∀ T ∈ accRoots, ValidRoot (RawState.ofData n base a) none T) → @@ -2702,7 +2739,7 @@ private theorem gatherSlotsLoop_exec {n base w maxDeg : ℕ} (hn_pos : 1 ≤ n) ofData_set_scratch a4 (by decide : 17 ≤ curDReg.val) (curD + 1) have hmod5 : Modifies (Block.mk base (space n)).footprint a.mem a5.mem := modifies_block_trans (Modifies.write_of_owns ho_cons 0) hmod_add - have hs5 : SlotInv n (RawState.ofData n base a5) slotRest maxDeg := by + have hs5 : SlotInv n sz (RawState.ofData n base a5) slotRest maxDeg := by rw [hst5] refine hs.step_collision hidx hslot_deg hd_le_n hno_other (by rw [hcons4, hst3]) fun R hR => ?_ @@ -2795,8 +2832,9 @@ private theorem execWithin_seq₃ {B : Basis} {r w t₁ t₂ : ℕ} {a b c d : C rcases h₂ with ⟨_, hm₂, hd⟩ exact ⟨_, by omega, ha.seq (hb.seq (hc.seq hd))⟩ -/-- `extractMinCode` implements `.extractMin` on `FibInv` within `410 * (Nat.log 2 n + 1)` -amortized instructions (`t + potential y ≤ 410 * (Nat.log 2 n + 1) + potential x`). -/ +/-- `extractMinCode` implements `.extractMin` on `FibInv` within +`410 * (Nat.log 2 (IndexedMinPQ.dom s).card + 1)` amortized instructions +(`t + potential y ≤ 410 * (Nat.log 2 (IndexedMinPQ.dom s).card + 1) + potential x`). -/ theorem extractMinCode_exec {n K_max w base : ℕ} {s : IndexedMinPQ.State n} {x : Data rFib} (hinv : FibInv n K_max w base s x) : ∃ y t, ExecWithin w extractMinCode x y t ∧ @@ -2804,7 +2842,8 @@ theorem extractMinCode_exec {n K_max w base : ℕ} {s : IndexedMinPQ.State n} {x some ((IndexedMinPQ.spec n K_max).step s .extractMin).2 ∧ FibInv n K_max w base ((IndexedMinPQ.spec n K_max).step s .extractMin).1 y ∧ Modifies (Block.mk base (space n)).footprint x.mem y.mem ∧ - t + potential y ≤ 410 * (Nat.log 2 n + 1) + potential x := by + t + potential y ≤ + 410 * (Nat.log 2 (IndexedMinPQ.dom s).card + 1) + potential x := by obtain ⟨hb, hbr, hraw⟩ := hinv set st := RawState.ofData n base x with hst_def have hfit_stop := hbr.fits_stop hb @@ -2853,14 +2892,19 @@ theorem extractMinCode_exec {n K_max w base : ℕ} {s : IndexedMinPQ.State n} {x have hrtail_len : rtail.length = r_trees.length := by simpa only [List.length_map] using hperm_rtail.length_eq have hTz_val : ValidRoot st none T_z := hroots T_z (List.getElem_mem hidx_z) - have hts_len_le_n : (forestNodes ts).length ≤ n := by simpa using hnd.length_le_card - have hTz_size_le_n : T_z.size ≤ n := by + have hts_len_eq : (forestNodes ts).length = (IndexedMinPQ.dom s).card := by + rw [← List.toFinset_card_of_nodup hnd] + congr 1 + ext v + simp only [List.mem_toFinset, IndexedMinPQ.dom, Finset.mem_filter, Finset.mem_univ, + true_and, hdom v] + have hTz_size_le_sz : T_z.size ≤ (IndexedMinPQ.dom s).card := by have h1 := hperm_ts.length_eq simp only [List.length_append] at h1 - change (ts[idx_z]).nodes.length ≤ n + change (ts[idx_z]).nodes.length ≤ (IndexedMinPQ.dom s).card omega - have hTz_deg_log : T_z.deg ≤ 2 * Nat.log 2 n + 1 := - (hTz_val.1.deg_bounds hTz_size_le_n).1 + have hTz_deg_log : T_z.deg ≤ 2 * Nat.log 2 (IndexedMinPQ.dom s).card + 1 := + (hTz_val.1.deg_bounds hTz_size_le_sz).1 rcases hTz_eq : T_z with ⟨z', c_trees⟩ have hzz' : z' = z := by have hr : T_z.root = z := hTz_root @@ -3075,6 +3119,14 @@ theorem extractMinCode_exec {n K_max w base : ℕ} {s : IndexedMinPQ.State n} {x simp only [List.singleton_append, List.append_assoc] at hp rw [List.cons_append] at hnd_split exact hp.nodup_iff.mp hnd_split + have hsz_crz : + (forestNodes c_trees ++ ([] ++ (forestNodes r_trees ++ [z]))).length ≤ + (IndexedMinPQ.dom s).card := by + have hp0 : (T_z.nodes ++ forestNodes r_trees).Perm (forestNodes ts) := hperm_ts + have hp_len := hp0.length_eq + simp only [hTz_eq, FHTree.nodes, List.length_append, List.length_cons, + List.length_nil] at hp_len ⊢ + omega -- Phase A1 (`x_pre → x_A1`) have hA1 : ∃ x_A1 slotTrees_1 maxDeg_1 t_A1, ExecWithin w @@ -3090,7 +3142,7 @@ theorem extractMinCode_exec {n K_max w base : ℕ} {s : IndexedMinPQ.State n} {x x_A1.regs maxDegReg = maxDeg_1 ∧ (forestNodes slotTrees_1).Perm (forestNodes c_trees) ∧ (forestNodes slotTrees_1 ++ (forestNodes r_trees ++ [z])).Nodup ∧ - SlotInv n (RawState.ofData n base x_A1) slotTrees_1 maxDeg_1 ∧ + SlotInv n (IndexedMinPQ.dom s).card (RawState.ofData n base x_A1) slotTrees_1 maxDeg_1 ∧ (let st' := RawState.ofData n base x_A1 st'.key = st.key ∧ st'.inHeap = Function.update st.inHeap z 0 ∧ st'.minRoot = st.minRoot ∧ st'.numRoots = st.numRoots ∧ st'.sz = st.sz - 1 ∧ @@ -3203,6 +3255,7 @@ theorem extractMinCode_exec {n K_max w base : ℕ} {s : IndexedMinPQ.State n} {x hk_sc1, hin_sc1, hmin_sc1, hnr_sc1, hsz_sc1, hnm_sc1, hnm_le1, ho_sc1⟩ := scatterChildrenLoop_exec (h_c :: c_tail) x_c0 c_trees [] (forestNodes r_trees ++ [z]) z 0 hb_c0 hbr_c0 (List.mem_append_right _ (List.mem_cons_self : z ∈ [z])) hnd_crz + hsz_crz (SlotInv.empty (by rw [hst_c0, hst_pre]; exact hcons)) hsub_c0 hperm_c (by rw [hst_c0, hst_pre]; exact hcirc_c.rightChain) (by simp only [x_c0, x_pre, Data.set_regs, @@ -3276,6 +3329,14 @@ theorem extractMinCode_exec {n K_max w base : ℕ} {s : IndexedMinPQ.State n} {x -- Phase A2 (`x_tr → x_A2`, `rootScatterLoop`) have hnd_tr : (forestNodes r_trees ++ (forestNodes slotTrees_1 ++ [z])).Nodup := (perm_swap_front (forestNodes slotTrees_1) (forestNodes r_trees) [z]).nodup_iff.mp hnd_A1 + have hsz_tr : + (forestNodes r_trees ++ (forestNodes slotTrees_1 ++ [z])).length ≤ + (IndexedMinPQ.dom s).card := by + have hp1 := perm_swap_front (forestNodes slotTrees_1) (forestNodes r_trees) [z] + have hp2 := hperm_A1.append_right (forestNodes r_trees ++ [z]) + have hlen := (hp1.symm.trans hp2).length_eq + simp only [List.nil_append] at hsz_crz + omega have hroots_tr : ∀ R ∈ r_trees, ValidRoot (RawState.ofData n base x_tr) none R := by intro R hR obtain ⟨j, hj, _, hR_eq⟩ := List.mem_eraseIdx_iff_getElem.mp hR @@ -3307,7 +3368,8 @@ theorem extractMinCode_exec {n K_max w base : ℕ} {s : IndexedMinPQ.State n} {x hregs_A2, hmd_A2, hperm_A2, hnd_A2, hs_A2, hk_A2, hmk_A2, hin_A2, _, _, hnm_A2, hsz_A2, _⟩ := scatterRootsLoop_exec rtail x_tr r_trees slotTrees_1 [z] z maxDeg_1 hb_tr hbr_tr - (List.mem_cons_self : z ∈ [z]) hnd_tr (hst_tr ▸ hs_A1) hroots_tr hperm_rtail hchain_tr + (List.mem_cons_self : z ∈ [z]) hnd_tr hsz_tr (hst_tr ▸ hs_A1) hroots_tr hperm_rtail + hchain_tr (by simp only [x_tr, Data.set_regs, Function.update_of_ne (by decide : remReg ≠ iterCur), Function.update_self]) (by simp only [x_tr, Data.set_regs, @@ -3316,7 +3378,7 @@ theorem extractMinCode_exec {n K_max w base : ℕ} {s : IndexedMinPQ.State n} {x hic_tr rw [hst_tr] at hk_A2 hmk_A2 hin_A2 hnm_A2 hsz_A2 -- Phase B preamble (`x_A2 → x_B0`, 4 ops) - have hmd2_log : maxDeg_2 ≤ 2 * Nat.log 2 n + 1 := hs_A2.2.2.2.2.1 + have hmd2_log : maxDeg_2 ≤ 2 * Nat.log 2 (IndexedMinPQ.dom s).card + 1 := hs_A2.2.2.2.2.1 have hmd2_le_n : maxDeg_2 + 1 ≤ n := by rcases hs_A2.2.2.2.2.2 with h0 | h2 <;> omega let x_B0 := (((x_A2.set minRootReg 0).set numRootsReg 0).set maxDegReg @@ -3360,7 +3422,9 @@ theorem extractMinCode_exec {n K_max w base : ℕ} {s : IndexedMinPQ.State n} {x simp only [x_B0, ofData_set_scratch _ (by decide : 17 ≤ curDReg.val), ofData_set_scratch _ (by decide : 17 ≤ maxDegReg.val), ofData_set_numRoots, ofData_set_minRoot] - have hs_B0 : SlotInv n (RawState.ofData n base x_B0) slotTrees_2 maxDeg_2 := by + have hs_B0 : + SlotInv n (IndexedMinPQ.dom s).card (RawState.ofData n base x_B0) + slotTrees_2 maxDeg_2 := by rw [hst_B0] exact hs_A2.frame rfl fun T hT => (hs_A2.2.1 T hT).1.frame_nodes fun _ _ => ⟨rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl⟩ diff --git a/Complexity/Algorithms/WordRAM/PriorityQueue/FibonacciHeap/MinPQ.lean b/Complexity/Algorithms/WordRAM/PriorityQueue/FibonacciHeap/MinPQ.lean new file mode 100644 index 000000000..97d0431e1 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/PriorityQueue/FibonacciHeap/MinPQ.lean @@ -0,0 +1,2004 @@ +/- +Copyright (c) 2026 Renfei Zhou. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Renfei Zhou +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.Lean.MinPQ +public import Complexity.Computability.WordRAM.RelSubroutine +public import Complexity.Algorithms.WordRAM.PriorityQueue.FibonacciHeap.BasicOps +public import Complexity.Algorithms.WordRAM.PriorityQueue.FibonacciHeap.Cut +public import Complexity.Algorithms.WordRAM.PriorityQueue.FibonacciHeap.Consolidate + +/-! +# Fibonacci Heap with Handles and Satellite Values + +This file realizes the relational priority-queue specification `Cslib.Algorithms.Lean.MinPQ.spec` +(returning an opaque handle `h : Fin n_max` on `insert` and `(u, v, h)` on `extractMin`) as a +Word-RAM `RelSubroutineDS` over `Cslib.WordRAM.Basis.exact` (and lifted to `Basis.standard`), +layered on top of `Cslib.Algorithms.WordRAM.PriorityQueue.FibonacciHeap`. + +Over capacity `n_max` in a contiguous memory block `[base, base + 11 * n_max + 1)`: +* `[base, base + 9 * n_max + 1)` holds the inner Fibonacci heap arrays (`key`, `par`, `child`, + `left`, `right`, `deg`, `mark`, `inHeap`, `cons`); +* `[base + (9 * n_max + 1), base + (10 * n_max + 1))` holds satellite values `val[0..n_max-1]`; +* `[base + (10 * n_max + 1), base + (11 * n_max + 1))` holds the recycled-handle stack + `freeStack[0..n_max-1]`, paired with persistent bump pointer `freshReg` and stack height + `freeTopReg`. + +## Main definitions + +* `Cslib.Algorithms.WordRAM.PriorityQueue.FibonacciHeap.MinPQ.rMinPQ`, `space`, `subroutineCode`: + `48`-register, `(11 * n_max + 1)`-word `SubroutineCode .exact rMinPQ 1`. +* `Cslib.Algorithms.WordRAM.PriorityQueue.FibonacciHeap.MinPQ.Rep`, `charge`: relational + representation invariant and per-operation amortized instruction charges (`O(log n + 1)` on + `extractMin` in the current number of stored elements `n = MinPQ.size s`). +* `Cslib.Algorithms.WordRAM.PriorityQueue.FibonacciHeap.MinPQ.relSubroutineDS`: + `RelSubroutineDS` certificate over `Basis.exact`. + +## Main results + +* `Cslib.Algorithms.WordRAM.PriorityQueue.FibonacciHeap.MinPQ.relAmortized`, + `run_client_amortized`: standalone `Stateful.RelAmortized` certificate and adaptive-client bound + over `Basis.standard`. +* `Cslib.Algorithms.WordRAM.PriorityQueue.FibonacciHeap.MinPQ.run_ops_exact_fresh`, + `run_ops_exact_reusable`: standalone `Basis.exact` flat-program execution theorems. + +## References + +* [M. L. Fredman and R. E. Tarjan, *Fibonacci heaps and their uses in improved network optimization + algorithms*][FredmanTarjan1987] +* [T. H. Cormen, C. E. Leiserson, R. L. Rivest and C. Stein, *Introduction to Algorithms*, + Chapter 19][CLRS2009] +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.PriorityQueue.FibonacciHeap.MinPQ + +open Cslib.WordRAM +open Cslib.Algorithms.Lean +open scoped Cslib.WordRAM + +/-! ## Register file (`rMinPQ = 48`), embedding `f42`, and footprint size -/ + +/-- Total register count of the key–value Fibonacci heap with handle allocation. -/ +abbrev rMinPQ : ℕ := 48 + +/-- Outer bump-pointer frontier register (`base` before `allocSpan`, `base + space n_max` after). -/ +abbrev outerFrontierReg : Fin rMinPQ := ⟨1, by decide⟩ +/-- Persistent base address of `val[0..n_max-1]` (`base + 9 * n_max + 1`). Also serves as the +relocated image of the inner `FibonacciHeap.frontierReg`. -/ +abbrev valBaseReg : Fin rMinPQ := ⟨42, by decide⟩ +/-- Persistent base address of `freeStack[0..n_max-1]` (`base + 10 * n_max + 1`). -/ +abbrev freeBaseReg : Fin rMinPQ := ⟨43, by decide⟩ +/-- Persistent bump-pointer watermark `fresh` for never-allocated handles. -/ +abbrev freshReg : Fin rMinPQ := ⟨44, by decide⟩ +/-- Persistent stack height `freeTop` for recycled handles in `freeStack`. -/ +abbrev freeTopReg : Fin rMinPQ := ⟨45, by decide⟩ +/-- Caller input register carrying the satellite value `v` on `insert`. -/ +abbrev inValReg : Fin rMinPQ := ⟨46, by decide⟩ +/-- Output register carrying the satellite value `v` on `get` and `extractMin`. -/ +abbrev outValReg : Fin rMinPQ := ⟨47, by decide⟩ + +/-- Injective register embedding from the inner `rFib = 42` Fibonacci heap into `rMinPQ = 48`, +mapping `FibonacciHeap.frontierReg` (`R[1]`) to `valBaseReg` (`R[42]`) so `outerFrontierReg` +(`R[1]`) remains outside `Set.range f42`. -/ +def f42 (i : Fin rFib) : Fin rMinPQ := + if i = FibonacciHeap.frontierReg then valBaseReg else Fin.castLE (by decide) i + +theorem f42_injective : Function.Injective f42 := by + decide + +theorem outerFrontierReg_notMem_range : outerFrontierReg ∉ Set.range f42 := by + decide + +theorem freeBaseReg_notMem_range : freeBaseReg ∉ Set.range f42 := by + decide + +theorem freshReg_notMem_range : freshReg ∉ Set.range f42 := by + decide + +theorem freeTopReg_notMem_range : freeTopReg ∉ Set.range f42 := by + decide + +theorem inValReg_notMem_range : inValReg ∉ Set.range f42 := by + decide + +theorem outValReg_notMem_range : outValReg ∉ Set.range f42 := by + decide + +/-- Persistent register holding handle capacity `n_max` (`R[0]`). -/ +abbrev nReg : Fin rMinPQ := ⟨0, by decide⟩ +/-- Persistent base address of `key[0..n_max-1]` (`R[2]`). -/ +abbrev baseReg : Fin rMinPQ := ⟨2, by decide⟩ +/-- Persistent constant register `0` (`R[11]`). -/ +abbrev zeroP : Fin rMinPQ := ⟨11, by decide⟩ +/-- Persistent constant register `1` (`R[12]`). -/ +abbrev oneP : Fin rMinPQ := ⟨12, by decide⟩ +/-- Caller high tag bit (`R[17]`, `≠ 0` selects `extractMin`). -/ +abbrev tag2 : Fin rMinPQ := ⟨17, by decide⟩ +/-- Caller middle tag bit (`R[18]`). -/ +abbrev tag1 : Fin rMinPQ := ⟨18, by decide⟩ +/-- Caller low tag bit (`R[19]`). -/ +abbrev tag0 : Fin rMinPQ := ⟨19, by decide⟩ +/-- Handle register (`R[20]`, input for `get`/`decreaseKey`, scratch for `insert`). -/ +abbrev argV : Fin rMinPQ := ⟨20, by decide⟩ +/-- Key input register (`R[21]`, `u` for `insert`, `u'` for `decreaseKey`). -/ +abbrev argK : Fin rMinPQ := ⟨21, by decide⟩ +/-- Primary output flag register (`R[22]`). -/ +abbrev outFlag : Fin rMinPQ := ⟨22, by decide⟩ +/-- Output handle register (`R[23]`, `h.val` for `insert` and non-empty `extractMin`). -/ +abbrev outV : Fin rMinPQ := ⟨23, by decide⟩ +/-- Output key register (`R[24]`, `u` for present `get` and non-empty `extractMin`). -/ +abbrev outK : Fin rMinPQ := ⟨24, by decide⟩ +/-- Scratch pointer register for `zeroFillCode` (`R[25]`). -/ +abbrev curReg : Fin rMinPQ := ⟨25, by decide⟩ +/-- Scratch zero register for `zeroFillCode` (`R[26]`). -/ +abbrev zeroReg : Fin rMinPQ := ⟨26, by decide⟩ +/-- Scratch one register for `zeroFillCode` (`R[27]`). -/ +abbrev oneReg : Fin rMinPQ := ⟨27, by decide⟩ +/-- Scratch address register (`R[35]`). -/ +abbrev s0 : Fin rMinPQ := ⟨35, by decide⟩ + +/-- Persistent registers are `0..16` and `42..45` (`false`); caller/scratch registers are `17..41` +and `46..47` (`true`). -/ +def inputReg (i : Fin rMinPQ) : Bool := + decide ((17 ≤ i.val ∧ i.val < 42) ∨ 46 ≤ i.val) + +theorem inputReg_f42_of_false : + ∀ i : Fin rFib, FibonacciHeap.inputReg i = false → inputReg (f42 i) = false := by + decide + +/-- Static memory footprint on capacity `n_max`: `11 * n_max + 1` words (`9 * n_max + 1` for the +inner Fibonacci heap plus `n_max` for `val` and `n_max` for `freeStack`). -/ +def space (n_max : ℕ) : ℕ := + 11 * n_max + 1 + +theorem space_eq_fib_add (n_max : ℕ) : + space n_max = FibonacciHeap.space n_max + n_max + n_max := by + unfold space FibonacciHeap.space + omega + +theorem space_lower (n_max : ℕ) : n_max + 1 ≤ space n_max := by + unfold space + omega + +/-- Fresh-initialization instruction bound (`allocSpan` 15 + `initSetup` 18 = 33). -/ +def tInit (_n_max : ℕ) : ℕ := 33 +/-- Instruction bound for `isEmpty` (including dispatch). -/ +def tEmpty (_n_max : ℕ) : ℕ := 12 +/-- Instruction bound for `get` (including dispatch and value read). -/ +def tGet (_n_max : ℕ) : ℕ := 20 +/-- Amortized instruction bound for `insert` (including handle allocation, value write, and `+80` +root potential). -/ +def tIns (_n_max : ℕ) : ℕ := 140 +/-- Amortized instruction bound for `decreaseKey` (including dispatch and cascading cuts). -/ +def tDec (_n_max : ℕ) : ℕ := 340 +/-- Amortized instruction bound for `extractMin` (`420 * (Nat.log 2 n + 1)`). -/ +def tExt (n : ℕ) : ℕ := 420 * (Nat.log 2 n + 1) + +/-- Per-operation amortized instruction charge for the key–value Fibonacci heap, charging +`tExt (MinPQ.size s)` on `.extractMin` in terms of the current number of stored elements +`MinPQ.size s`. -/ +def charge (n_max : ℕ) (s : Cslib.Algorithms.Lean.MinPQ.State n_max) : + Cslib.Algorithms.Lean.MinPQ.Op n_max → ℕ + | .isEmpty => tEmpty n_max + | .get _ => tGet n_max + | .insert _ _ => tIns n_max + | .decreaseKey _ _ => tDec n_max + | .extractMin => tExt (Cslib.Algorithms.Lean.MinPQ.size s) + +/-- Since `MinPQ.size s ≤ n_max`, `charge n_max s .extractMin ≤ tExt n_max`. -/ +theorem charge_extractMin_le_tExt (n_max : ℕ) (s : Cslib.Algorithms.Lean.MinPQ.State n_max) : + charge n_max s .extractMin ≤ tExt n_max := by + simp only [charge, tExt] + have hlog : Nat.log 2 (Cslib.Algorithms.Lean.MinPQ.size s) ≤ Nat.log 2 n_max := + Nat.log_mono_right (Cslib.Algorithms.Lean.MinPQ.size_le s) + omega + +/-- Concrete integer potential `80 * (numRoots + 2 * numMarked)` inherited from the inner +Fibonacci heap. -/ +def potential (x : Data rMinPQ) : ℕ := + FibonacciHeap.potential (x.comap f42) + +/-! ## Structured `Basis.exact` subprograms and `subroutineCode` -/ + +/-- Copy `outerFrontierReg` into `valBaseReg`, run `FibonacciHeap.allocSpan.rename f42` to set +`baseReg = base` and `valBaseReg = base + 9 * n_max + 1`, then advance `freeBaseReg` and +`outerFrontierReg` by `n_max` each (`15` steps total). -/ +def allocSpan : Code .exact rMinPQ := + ram_exact[ + valBaseReg ← outerFrontierReg; + ⟪FibonacciHeap.allocSpan.rename f42⟫; + freeBaseReg ← valBaseReg + nReg; + outerFrontierReg ← freeBaseReg + nReg + ] + +/-- Initialize `freeBaseReg = outerFrontierReg - nReg`, `valBaseReg = freeBaseReg - nReg`, +`freshReg = 0`, `freeTopReg = 0`, and run `FibonacciHeap.initSetup.rename f42` (`18` steps +total). -/ +def initSetup : Code .exact rMinPQ := + ram_exact[ + freeBaseReg ← outerFrontierReg - nReg; + valBaseReg ← freeBaseReg - nReg; + freshReg ← #0; + freeTopReg ← #0; + ⟪FibonacciHeap.initSetup.rename f42⟫ + ] + +/-- `isEmpty`: delegates directly to `FibonacciHeap.isEmptyCode.rename f42`. -/ +def isEmptyCode : Code .exact rMinPQ := + FibonacciHeap.isEmptyCode.rename f42 + +/-- `get h`: runs `FibonacciHeap.getKeyCode.rename f42` and, when `outFlag ≠ 0`, loads the +satellite value `outValReg ← val[argV]`. -/ +def getCode : Code .exact rMinPQ := + ram_exact[ + ⟪FibonacciHeap.getKeyCode.rename f42⟫; + if outFlag = zeroP then [ + s0 ← zeroP + ] else [ + s0 ← valBaseReg + argV; + outValReg ← *s0 + ] + ] + +/-- Handle-allocation branch of `insertCode`: pops a recycled handle from +`freeStack[freeTopReg - 1]` when `freeTopReg ≠ 0`, or allocates `freshReg` and increments +`freshReg` when `freeTopReg = 0`. -/ +def allocHandleBranch : Code .exact rMinPQ := + ram_exact[ + if freeTopReg = zeroP then [ + argV ← freshReg; + freshReg ← freshReg + oneP + ] else [ + freeTopReg ← freeTopReg - oneP; + s0 ← freeBaseReg + freeTopReg; + argV ← *s0 + ] + ] + +/-- `insert u v`: allocates a handle `argV` from `freeStack` (if `freeTopReg > 0`) or bumps +`freshReg` via `allocHandleBranch`, stores `val[argV] ← inValReg`, runs +`FibonacciHeap.insertCode.rename f42` with key `argK = u`, and outputs `outV ← argV`. -/ +def insertCode : Code .exact rMinPQ := + ram_exact[ + ⟪allocHandleBranch⟫; + s0 ← valBaseReg + argV; + *s0 ← inValReg; + ⟪FibonacciHeap.insertCode.rename f42⟫; + outV ← argV + ] + +/-- `decreaseKey h u'`: delegates directly to `FibonacciHeap.decreaseKeyCode.rename f42`. -/ +def decreaseKeyCode : Code .exact rMinPQ := + FibonacciHeap.decreaseKeyCode.rename f42 + +/-- `extractMin`: runs `FibonacciHeap.extractMinCode.rename f42` and, when `outFlag ≠ 0`, loads +`outValReg ← val[outV]` and pushes `outV` onto `freeStack[freeTopReg]`. -/ +def extractMinCode : Code .exact rMinPQ := + ram_exact[ + ⟪FibonacciHeap.extractMinCode.rename f42⟫; + if outFlag = zeroP then [ + s0 ← zeroP + ] else [ + s0 ← valBaseReg + outV; + outValReg ← *s0; + s0 ← freeBaseReg + freeTopReg; + *s0 ← outV; + freeTopReg ← freeTopReg + oneP + ] + ] + +/-- Unified dispatcher over 1-bit tags `(tag2, tag1, tag0)`. -/ +def dispatchCode : Code .exact rMinPQ := + ram_exact[ + if tag2 = zeroP then [ + if tag1 = zeroP then [ + if tag0 = zeroP then [ + ⟪isEmptyCode⟫ + ] else [ + ⟪getCode⟫ + ] + ] else [ + if tag0 = zeroP then [ + ⟪insertCode⟫ + ] else [ + ⟪decreaseKeyCode⟫ + ] + ] + ] else [ + ⟪extractMinCode⟫ + ] + ] + +/-- Uniform `SubroutineCode .exact rMinPQ 1` for the key–value Fibonacci heap with handles. -/ +def subroutineCode : SubroutineCode .exact rMinPQ 1 where + allocSpan := allocSpan + initSetup := initSetup + code := dispatchCode + frontierReg := outerFrontierReg + baseReg := baseReg + initParams := ![nReg] + curReg := curReg + zeroReg := zeroReg + oneReg := oneReg + incCur := .exactBin .add curReg curReg oneReg + input := inputReg + output := outFlag + +/-! ## Calling convention: `encode`, `argMatch`, `decode` -/ + +/-- Raw register encoding for `Cslib.Algorithms.Lean.MinPQ.Op n_max` using 1-bit tags +`(tag2, tag1, tag0)`. -/ +def rawEncode {n_max : ℕ} : Cslib.Algorithms.Lean.MinPQ.Op n_max → Fin rMinPQ → ℕ + | .isEmpty => fun _ => 0 + | .get h => fun i => if i = tag0 then 1 else if i = argV then h.val else 0 + | .insert u v => fun i => + if i = tag1 then 1 else if i = argK then u else if i = inValReg then v else 0 + | .decreaseKey h u' => fun i => + if i = tag1 then 1 else if i = tag0 then 1 + else if i = argV then h.val else if i = argK then u' else 0 + | .extractMin => fun i => if i = tag2 then 1 else 0 + +/-- Register encoding for `Cslib.Algorithms.Lean.MinPQ.Op n_max` bounded by `U` on `decreaseKey` +so `arguments_fit` holds on arbitrary states; coincides with `rawEncode` on every represented +state (`encode_eq_rawEncode_of_rep`). -/ +def encode {n_max : ℕ} (U : ℕ) : Cslib.Algorithms.Lean.MinPQ.Op n_max → Fin rMinPQ → ℕ + | .decreaseKey h u' => fun i => + if i = tag1 then 1 else if i = tag0 then 1 + else if i = argV then h.val else if i = argK then (if u' < U then u' else 0) else 0 + | o => rawEncode o + +/-- Standard argument-matching predicate for `Cslib.Algorithms.Lean.MinPQ.Op n_max`. -/ +def rawArgMatch {n_max : ℕ} : Cslib.Algorithms.Lean.MinPQ.Op n_max → (Fin rMinPQ → ℕ) → Prop + | .isEmpty => fun regs => regs tag2 = 0 ∧ regs tag1 = 0 ∧ regs tag0 = 0 + | .get h => fun regs => + regs tag2 = 0 ∧ regs tag1 = 0 ∧ regs tag0 ≠ 0 ∧ regs argV = h.val + | .insert u v => fun regs => + regs tag2 = 0 ∧ regs tag1 ≠ 0 ∧ regs tag0 = 0 ∧ regs argK = u ∧ regs inValReg = v + | .decreaseKey h u' => fun regs => + regs tag2 = 0 ∧ regs tag1 ≠ 0 ∧ regs tag0 ≠ 0 ∧ regs argV = h.val ∧ regs argK = u' + | .extractMin => fun regs => regs tag2 ≠ 0 + +/-- Argument-matching predicate paired with `encode U` for `RelSubroutineDS`. -/ +def argMatch {n_max : ℕ} (U : ℕ) : Cslib.Algorithms.Lean.MinPQ.Op n_max → (Fin rMinPQ → ℕ) → Prop + | .decreaseKey h u' => fun regs => + regs tag2 = 0 ∧ regs tag1 ≠ 0 ∧ regs tag0 ≠ 0 ∧ regs argV = h.val ∧ + regs argK = (if u' < U then u' else 0) + | o => rawArgMatch o + +/-- Output decoder for `Cslib.Algorithms.Lean.MinPQ.spec n_max U V`. -/ +def decode {n_max U V : ℕ} : + (o : (Cslib.Algorithms.Lean.MinPQ.spec n_max U V).Op) → (Fin rMinPQ → ℕ) → + Option ((Cslib.Algorithms.Lean.MinPQ.spec n_max U V).Result o) + | .isEmpty, regs => some (decide (regs outFlag ≠ 0)) + | .get _, regs => + if regs outFlag = 0 then some none else some (some (regs outK, regs outValReg)) + | .insert _ _, regs => + if h : regs outV < n_max then some (some ⟨regs outV, h⟩) else none + | .decreaseKey _ _, _ => some () + | .extractMin, regs => + if regs outFlag = 0 then some none + else if h : regs outV < n_max then some (some (regs outK, regs outValReg, ⟨regs outV, h⟩)) + else none + +theorem arguments_fit {n_max U V w base : ℕ} + (hS : Fits w (base + space n_max)) (hU : Fits w U) (hV : Fits w V) + {s : Cslib.Algorithms.Lean.MinPQ.State n_max} (o : Cslib.Algorithms.Lean.MinPQ.Op n_max) + (hv : (Cslib.Algorithms.Lean.MinPQ.spec n_max U V).valid s o) (i : Fin rMinPQ) : + Fits w (encode U o i) := by + have hsp := space_lower n_max + have h0 : Fits w 0 := hS.of_le (Nat.zero_le _) + have h1 : Fits w 1 := hS.of_le (by omega) + cases o with + | isEmpty => exact h0 + | get h => + simp only [encode, rawEncode] + split_ifs + · exact h1 + · exact hS.of_le (by have := h.isLt; omega) + · exact h0 + | insert u v => + obtain ⟨_, hu, hv_lt⟩ := hv + simp only [encode, rawEncode] + split_ifs + · exact h1 + · exact hU.of_le (Nat.le_of_lt hu) + · exact hV.of_le (Nat.le_of_lt hv_lt) + · exact h0 + | decreaseKey h u' => + simp only [encode] + split_ifs with _ _ _ _ hu' + · exact h1 + · exact h1 + · exact hS.of_le (by have := h.isLt; omega) + · exact hU.of_le (Nat.le_of_lt hu') + · exact h0 + · exact h0 + | extractMin => + simp only [encode, rawEncode] + split_ifs + · exact h1 + · exact h0 + +theorem encode_matches {n_max U : ℕ} (o : Cslib.Algorithms.Lean.MinPQ.Op n_max) + (regs : Fin rMinPQ → ℕ) : + argMatch U o (fun i => if subroutineCode.input i then encode U o i else regs i) := by + cases o with + | isEmpty => + refine ⟨rfl, rfl, rfl⟩ + | get h => + refine ⟨rfl, rfl, Nat.one_ne_zero, rfl⟩ + | insert u v => + refine ⟨rfl, Nat.one_ne_zero, rfl, rfl, rfl⟩ + | decreaseKey h u' => + refine ⟨rfl, Nat.one_ne_zero, Nat.one_ne_zero, rfl, rfl⟩ + | extractMin => + exact Nat.one_ne_zero + + +/-! ## Representation invariant (`AllocRep`, `Rep`) and framing lemmas -/ + +/-- Full machine-state representation invariant for an internal allocator state +`s : Cslib.Algorithms.Lean.MinPQ.AllocState n_max`. -/ +def AllocRep (n_max U V w base : ℕ) (s : Cslib.Algorithms.Lean.MinPQ.AllocState n_max) + (x : Data rMinPQ) : Prop := + x.Bounded w ∧ + FibonacciHeap.FibInv n_max U w base (Cslib.Algorithms.Lean.MinPQ.toIndexed s.entries) + (x.comap f42) ∧ + s.WellFormed ∧ + x.regs outerFrontierReg = base + space n_max ∧ + x.regs freeBaseReg = base + FibonacciHeap.space n_max + n_max ∧ + x.regs freshReg = s.fresh ∧ + x.regs freeTopReg = s.freeList.length ∧ + (∀ (h : Fin n_max) (u v : ℕ), s.entries h = some (u, v) → + u < U ∧ v < V ∧ x.mem (base + FibonacciHeap.space n_max + h.val) = v) ∧ + (∀ (i : ℕ) (hi : i < s.freeList.length), + x.mem (base + FibonacciHeap.space n_max + n_max + (s.freeList.length - 1 - i)) = + (s.freeList[i]).val) + +/-- Existential relational representation invariant over abstract state +`e : Cslib.Algorithms.Lean.MinPQ.State n_max`. -/ +def Rep (n_max U V w base : ℕ) (e : Cslib.Algorithms.Lean.MinPQ.State n_max) + (x : Data rMinPQ) : Prop := + ∃ s : Cslib.Algorithms.Lean.MinPQ.AllocState n_max, + s.entries = e ∧ AllocRep n_max U V w base s x + +theorem owns_val_addr {n_max base idx : ℕ} (hidx : idx < n_max) : + (Block.mk base (space n_max)).Owns (base + FibonacciHeap.space n_max + idx) := by + dsimp only [Block.Owns, space, FibonacciHeap.space] + omega + +theorem owns_free_addr {n_max base idx : ℕ} (hidx : idx < n_max) : + (Block.mk base (space n_max)).Owns (base + FibonacciHeap.space n_max + n_max + idx) := by + dsimp only [Block.Owns, space, FibonacciHeap.space] + omega + +theorem notMem_fib_footprint_of_ge {n_max base a : ℕ} (ha : base + FibonacciHeap.space n_max ≤ a) : + a ∉ (Block.mk base (FibonacciHeap.space n_max)).footprint := by + rw [Block.mem_footprint, Block.Owns] + dsimp only + omega + +theorem fib_footprint_subset {n_max base : ℕ} : + (Block.mk base (FibonacciHeap.space n_max)).footprint ⊆ + (Block.mk base (space n_max)).footprint := by + intro a ha + rw [Block.mem_footprint, Block.Owns] at ha ⊢ + dsimp only [space, FibonacciHeap.space] at ha ⊢ + omega + +theorem modifies_of_fib {n_max base : ℕ} {m₁ m₂ : ℕ → ℕ} + (h : Modifies (Block.mk base (FibonacciHeap.space n_max)).footprint m₁ m₂) : + Modifies (Block.mk base (space n_max)).footprint m₁ m₂ := + fun a ha => h a fun ha' => ha (fib_footprint_subset ha') + +theorem AllocRep.supported {n_max U V w base : ℕ} + {s : Cslib.Algorithms.Lean.MinPQ.AllocState n_max} {x x' : Data rMinPQ} + (hreg : ∀ i : Fin rMinPQ, inputReg i = false → x'.regs i = x.regs i) + (hmem : ∀ a ∈ (Block.mk base (space n_max)).footprint, x'.mem a = x.mem a) + (hb' : x'.Bounded w) (h : AllocRep n_max U V w base s x) : + AllocRep n_max U V w base s x' := by + obtain ⟨_, hfib, hwf, hfr, hfb, hfresh, htop, hval, hfree⟩ := h + refine ⟨hb', ?_, hwf, + (hreg outerFrontierReg (by decide)).trans hfr, + (hreg freeBaseReg (by decide)).trans hfb, + (hreg freshReg (by decide)).trans hfresh, + (hreg freeTopReg (by decide)).trans htop, ?_, ?_⟩ + · refine FibonacciHeap.FibInv.supported (fun j hj => ?_) (fun a ha => ?_) (hb'.comap f42) hfib + · exact hreg (f42 j) (inputReg_f42_of_false j hj) + · exact hmem a (fib_footprint_subset ha) + · intro h u v hsh + obtain ⟨hu, hv, hm⟩ := hval h u v hsh + refine ⟨hu, hv, ?_⟩ + rw [hmem _ (Block.mem_footprint.mpr (owns_val_addr h.isLt))] + exact hm + · intro i hi + have hlt : s.freeList.length - 1 - i < n_max := by + have := hwf.1 + have := hwf.2.2.2.2 + omega + rw [hmem _ (Block.mem_footprint.mpr (owns_free_addr hlt))] + exact hfree i hi + +theorem Rep.supported {n_max U V w base : ℕ} + {e : Cslib.Algorithms.Lean.MinPQ.State n_max} {x x' : Data rMinPQ} + (hreg : ∀ i : Fin rMinPQ, inputReg i = false → x'.regs i = x.regs i) + (hmem : ∀ a ∈ (Block.mk base (space n_max)).footprint, x'.mem a = x.mem a) + (hb' : x'.Bounded w) (h : Rep n_max U V w base e x) : + Rep n_max U V w base e x' := by + obtain ⟨s, rfl, hs⟩ := h + exact ⟨s, rfl, hs.supported hreg hmem hb'⟩ + +theorem potential_supported {x x' : Data rMinPQ} + (hreg : ∀ i : Fin rMinPQ, inputReg i = false → x'.regs i = x.regs i) : + potential x' = potential x := + FibonacciHeap.potential_supported fun j hj => hreg (f42 j) (inputReg_f42_of_false j hj) + +theorem rawArgMatch_of_argMatch {n_max U V w base : ℕ} + {s : Cslib.Algorithms.Lean.MinPQ.AllocState n_max} {x : Data rMinPQ} + {o : Cslib.Algorithms.Lean.MinPQ.Op n_max} + (hrep : AllocRep n_max U V w base s x) + (hv : (Cslib.Algorithms.Lean.MinPQ.spec n_max U V).valid s.entries o) + (harg : argMatch U o x.regs) : + rawArgMatch o x.regs := by + cases o with + | isEmpty | get _ | insert _ _ | extractMin => exact harg + | decreaseKey h u' => + obtain ⟨u, v, hsh, hu'⟩ := hv + have hu_lt : u < U := (hrep.2.2.2.2.2.2.2.1 h u v hsh).1 + have hu'_lt : u' < U := Nat.lt_of_le_of_lt hu' hu_lt + simp only [argMatch, hu'_lt, ↓reduceIte] at harg + exact harg + +theorem encode_eq_rawEncode_of_rep {n_max U V w base : ℕ} + {e : Cslib.Algorithms.Lean.MinPQ.State n_max} {x : Data rMinPQ} + {o : Cslib.Algorithms.Lean.MinPQ.Op n_max} + (hr : Rep n_max U V w base e x) + (hv : (Cslib.Algorithms.Lean.MinPQ.spec n_max U V).valid e o) : + encode U o = rawEncode o := by + cases o with + | isEmpty | get _ | insert _ _ | extractMin => rfl + | decreaseKey h u' => + obtain ⟨s, rfl, hrep⟩ := hr + obtain ⟨u, v, hsh, hu'⟩ := hv + have hu_lt : u < U := (hrep.2.2.2.2.2.2.2.1 h u v hsh).1 + have hu'_lt : u' < U := Nat.lt_of_le_of_lt hu' hu_lt + funext i + simp only [encode, rawEncode, hu'_lt, ↓reduceIte] + +/-! ## Initialization: `incCur_eval`, `allocSpan_exec`, `initSetup_exec` -/ + +theorem incCur_eval {w : ℕ} {x : Data rMinPQ} (h1 : x.regs oneReg = 1) + (hfit : Fits w (x.regs curReg + 1)) : + (Op.exactBin .add curReg curReg oneReg).eval w x = + some (x.set curReg (x.regs curReg + 1)) := by + rw [Op.eval_exactBin] + simp only [BinOp.evalExact, h1, Option.bind_some] + exact Data.put_of_fits hfit + +theorem allocSpan_exec {n_max w base : ℕ} (hS : Fits w (base + space n_max)) + {x : Data rMinPQ} (hx : x.Bounded w) (hfr : x.regs outerFrontierReg = base) + (hpar : ∀ j : Fin 1, x.regs (![nReg] j) = ![n_max] j) : + ∃ x₁, ExecWithin w allocSpan x x₁ 15 ∧ x₁.mem = x.mem ∧ + x₁.regs baseReg = base ∧ x₁.regs outerFrontierReg = base + space n_max ∧ + (∀ j : Fin 1, x₁.regs (![nReg] j) = ![n_max] j) := by + have hn : x.regs nReg = n_max := hpar 0 + have hS_fib : Fits w (base + FibonacciHeap.space n_max) := by + rw [space_eq_fib_add] at hS + exact hS.of_le (by omega) + let x0 := x.set valBaseReg base + have e0 : ExecWithin (B := .exact) w (.op (.copy valBaseReg outerFrontierReg)) x x0 1 := by + simpa only [hfr] using + (ExecWithin.copy (B := .exact) (w := w) (x := x) (d := valBaseReg) (s := outerFrontierReg)) + have hx0_b : (x0.comap f42).Bounded w := (e0.bounded hx).comap f42 + have hx0_fr : (x0.comap f42).regs FibonacciHeap.frontierReg = base := by + change x0.regs valBaseReg = base + simp only [x0, Data.set_regs, Function.update_self] + have hx0_par : ∀ j : Fin 1, (x0.comap f42).regs (![FibonacciHeap.nReg] j) = ![n_max] j := by + intro j + fin_cases j + change x0.regs nReg = n_max + simp only [x0, Data.set_regs, Function.update_of_ne (by decide : nReg ≠ valBaseReg), hn] + obtain ⟨y_fib, he_fib, hm_fib, hb_fib, hfr_fib, hpar_fib⟩ := + FibonacciHeap.allocSpan_exec hS_fib hx0_b hx0_fr hx0_par + let x1 := y_fib.extend f42 x0 + have e1 : ExecWithin w (FibonacciHeap.allocSpan.rename f42) x0 x1 12 := + he_fib.rename f42_injective + have hx1_vb : x1.regs valBaseReg = base + FibonacciHeap.space n_max := by + change (x1.comap f42).regs FibonacciHeap.frontierReg = base + FibonacciHeap.space n_max + rw [Data.comap_extend f42_injective] + exact hfr_fib + have hx1_b : x1.regs baseReg = base := by + change (x1.comap f42).regs FibonacciHeap.baseReg = base + rw [Data.comap_extend f42_injective] + exact hb_fib + have hx1_n : x1.regs nReg = n_max := by + change (x1.comap f42).regs FibonacciHeap.nReg = n_max + rw [Data.comap_extend f42_injective] + exact hpar_fib 0 + let x2 := x1.set freeBaseReg (base + FibonacciHeap.space n_max + n_max) + have e2 : ExecWithin w (.op (.exactBin .add freeBaseReg valBaseReg nReg)) x1 x2 1 := by + simpa only [hx1_vb, hx1_n] using + FibonacciHeap.execWithin_exactAdd (x := x1) (d := freeBaseReg) (i := valBaseReg) (j := nReg) + (by rw [hx1_vb, hx1_n]; rw [space_eq_fib_add] at hS; exact hS.of_le (by omega)) + have hx2_fb : x2.regs freeBaseReg = base + FibonacciHeap.space n_max + n_max := by + simp only [x2, Data.set_regs, Function.update_self] + have hx2_n : x2.regs nReg = n_max := by + simp only [x2, Data.set_regs, Function.update_of_ne (by decide : nReg ≠ freeBaseReg), hx1_n] + let x3 := x2.set outerFrontierReg (base + space n_max) + have e3 : ExecWithin w (.op (.exactBin .add outerFrontierReg freeBaseReg nReg)) x2 x3 1 := by + have heq : base + FibonacciHeap.space n_max + n_max + n_max = base + space n_max := by + rw [space_eq_fib_add]; omega + simpa only [hx2_fb, hx2_n, heq] using + FibonacciHeap.execWithin_exactAdd + (x := x2) (d := outerFrontierReg) (i := freeBaseReg) (j := nReg) + (by rw [hx2_fb, hx2_n, heq]; exact hS) + have he : ExecWithin w allocSpan x x3 15 := by + unfold allocSpan + exact (e0.seq (e1.seq (e2.seq e3))).mono (by omega) + refine ⟨x3, he, hm_fib, ?_, ?_, ?_⟩ + · simp only [x3, x2, Data.set_regs, + Function.update_of_ne (by decide : baseReg ≠ outerFrontierReg), + Function.update_of_ne (by decide : baseReg ≠ freeBaseReg), hx1_b] + · simp only [x3, Data.set_regs, Function.update_self] + · intro j + fin_cases j + change x3.regs nReg = n_max + simp only [x3, Data.set_regs, + Function.update_of_ne (by decide : nReg ≠ outerFrontierReg), hx2_n] + +theorem initSetup_exec {n_max U V w base : ℕ} {x₁ : Data rMinPQ} + (hx₁ : x₁.Bounded w) (hb₁ : x₁.regs baseReg = base) + (hfr₁ : x₁.regs outerFrontierReg = base + space n_max) + (hpar₁ : ∀ j : Fin 1, x₁.regs (![nReg] j) = ![n_max] j) + (hz : ∀ a ∈ (Block.mk base (space n_max)).footprint, x₁.mem a = 0) : + ∃ y, ExecWithin w initSetup x₁ y 18 ∧ y.regs outerFrontierReg = base + space n_max ∧ + Rep n_max U V w base (Cslib.Algorithms.Lean.MinPQ.empty n_max) y ∧ potential y ≤ 0 ∧ + Modifies (Block.mk base (space n_max)).footprint x₁.mem y.mem := by + have hn : x₁.regs nReg = n_max := hpar₁ 0 + have hS : Fits w (base + space n_max) := hfr₁ ▸ hx₁.1 outerFrontierReg + have hfit0 : Fits w 0 := hS.of_le (Nat.zero_le _) + let s1 := x₁.set freeBaseReg (base + FibonacciHeap.space n_max + n_max) + have e1 : ExecWithin w (.op (.exactBin .sub freeBaseReg outerFrontierReg nReg)) x₁ s1 1 := by + have hsub : base + space n_max - n_max = base + FibonacciHeap.space n_max + n_max := by + rw [space_eq_fib_add]; omega + simpa only [hfr₁, hn, hsub] using + FibonacciHeap.execWithin_exactSub + (x := x₁) (d := freeBaseReg) (i := outerFrontierReg) (j := nReg) + (by rw [hfr₁, hn, space_eq_fib_add]; omega) + (by rw [hfr₁, hn, hsub]; rw [space_eq_fib_add] at hS; exact hS.of_le (by omega)) + have hs1_fb : s1.regs freeBaseReg = base + FibonacciHeap.space n_max + n_max := by + simp only [s1, Data.set_regs, Function.update_self] + have hs1_n : s1.regs nReg = n_max := by + simp only [s1, Data.set_regs, Function.update_of_ne (by decide : nReg ≠ freeBaseReg), hn] + let s2 := s1.set valBaseReg (base + FibonacciHeap.space n_max) + have e2 : ExecWithin w (.op (.exactBin .sub valBaseReg freeBaseReg nReg)) s1 s2 1 := by + simpa only [hs1_fb, hs1_n, Nat.add_sub_cancel] using + FibonacciHeap.execWithin_exactSub (x := s1) (d := valBaseReg) (i := freeBaseReg) (j := nReg) + (by rw [hs1_fb, hs1_n]; omega) + (by + rw [hs1_fb, hs1_n, Nat.add_sub_cancel] + rw [space_eq_fib_add] at hS + exact hS.of_le (by omega)) + let s3 := s2.set freshReg 0 + have e3 : ExecWithin (B := .exact) w (.op (.const freshReg 0)) s2 s3 1 := + ExecWithin.const hfit0 + let s4 := s3.set freeTopReg 0 + have e4 : ExecWithin (B := .exact) w (.op (.const freeTopReg 0)) s3 s4 1 := + ExecWithin.const hfit0 + have hs4_b : s4.Bounded w := e4.bounded (e3.bounded (e2.bounded (e1.bounded hx₁))) + have hs4_base : (s4.comap f42).regs FibonacciHeap.baseReg = base := by + change s4.regs baseReg = base + simp only [s4, s3, s2, s1, Data.set_regs, + Function.update_of_ne (by decide : baseReg ≠ freeTopReg), + Function.update_of_ne (by decide : baseReg ≠ freshReg), + Function.update_of_ne (by decide : baseReg ≠ valBaseReg), + Function.update_of_ne (by decide : baseReg ≠ freeBaseReg), hb₁] + have hs4_fr : (s4.comap f42).regs FibonacciHeap.frontierReg = + base + FibonacciHeap.space n_max := by + change s4.regs valBaseReg = base + FibonacciHeap.space n_max + simp only [s4, s3, s2, Data.set_regs, + Function.update_of_ne (by decide : valBaseReg ≠ freeTopReg), + Function.update_of_ne (by decide : valBaseReg ≠ freshReg), + Function.update_self] + have hs4_par : ∀ j : Fin 1, (s4.comap f42).regs (![FibonacciHeap.nReg] j) = ![n_max] j := by + intro j + fin_cases j + change s4.regs nReg = n_max + simp only [s4, s3, s2, Data.set_regs, + Function.update_of_ne (by decide : nReg ≠ freeTopReg), + Function.update_of_ne (by decide : nReg ≠ freshReg), + Function.update_of_ne (by decide : nReg ≠ valBaseReg), hs1_n] + have hz_fib : ∀ a ∈ (Block.mk base (FibonacciHeap.space n_max)).footprint, + (s4.comap f42).mem a = 0 := + fun a ha => hz a (fib_footprint_subset ha) + obtain ⟨y_fib, he_fib, hfr_fib, hinv_fib, hpot_fib, hmod_fib⟩ := + FibonacciHeap.initSetup_exec (K_max := U) (hs4_b.comap f42) hs4_base hs4_fr hs4_par hz_fib + let y := y_fib.extend f42 s4 + have e5 : ExecWithin w (FibonacciHeap.initSetup.rename f42) s4 y 14 := + he_fib.rename f42_injective + have he : ExecWithin w initSetup x₁ y 18 := by + unfold initSetup + exact (e1.seq (e2.seq (e3.seq (e4.seq e5)))).mono (by omega) + have hy_ofr : y.regs outerFrontierReg = base + space n_max := by + rw [Data.extend_regs_of_notMem y_fib s4 outerFrontierReg_notMem_range] + simp only [s4, s3, s2, s1, Data.set_regs, + Function.update_of_ne (by decide : outerFrontierReg ≠ freeTopReg), + Function.update_of_ne (by decide : outerFrontierReg ≠ freshReg), + Function.update_of_ne (by decide : outerFrontierReg ≠ valBaseReg), + Function.update_of_ne (by decide : outerFrontierReg ≠ freeBaseReg), hfr₁] + refine ⟨y, he, hy_ofr, + ⟨Cslib.Algorithms.Lean.MinPQ.AllocState.empty n_max, rfl, + he.bounded hx₁, ?_, + Cslib.Algorithms.Lean.MinPQ.AllocState.wellFormed_empty n_max, + hy_ofr, ?_, ?_, ?_, ?_, + fun _ hi => absurd hi (Nat.not_lt_zero _)⟩, + ?_, modifies_of_fib hmod_fib⟩ + · rw [Data.comap_extend f42_injective] + exact hinv_fib + · rw [Data.extend_regs_of_notMem y_fib s4 freeBaseReg_notMem_range] + simp only [s4, s3, s2, Data.set_regs, + Function.update_of_ne (by decide : freeBaseReg ≠ freeTopReg), + Function.update_of_ne (by decide : freeBaseReg ≠ freshReg), + Function.update_of_ne (by decide : freeBaseReg ≠ valBaseReg), hs1_fb] + · rw [Data.extend_regs_of_notMem y_fib s4 freshReg_notMem_range] + simp only [s4, s3, Data.set_regs, + Function.update_of_ne (by decide : freshReg ≠ freeTopReg), Function.update_self] + rfl + · rw [Data.extend_regs_of_notMem y_fib s4 freeTopReg_notMem_range] + simp only [s4, Data.set_regs, Function.update_self] + rfl + · rintro _ _ _ ⟨⟩ + · unfold potential + rw [Data.comap_extend f42_injective] + exact hpot_fib + +/-! ## Operation specifications: `isEmptyCode`, `getCode`, `insertCode` -/ + +theorem AllocRep.fits_stop {n_max U V w base : ℕ} + {s : Cslib.Algorithms.Lean.MinPQ.AllocState n_max} {x : Data rMinPQ} + (hrep : AllocRep n_max U V w base s x) : Fits w (base + space n_max) := + hrep.2.2.2.1 ▸ hrep.1.1 outerFrontierReg + +theorem AllocRep.valBase_eq {n_max U V w base : ℕ} + {s : Cslib.Algorithms.Lean.MinPQ.AllocState n_max} {x : Data rMinPQ} + (hrep : AllocRep n_max U V w base s x) : + x.regs valBaseReg = base + FibonacciHeap.space n_max := + hrep.2.1.2.1.2.1 + +theorem AllocRep.zeroP_eq {n_max U V w base : ℕ} + {s : Cslib.Algorithms.Lean.MinPQ.AllocState n_max} {x : Data rMinPQ} + (hrep : AllocRep n_max U V w base s x) : x.regs zeroP = 0 := + hrep.2.1.2.1.2.2.2.2.2.2.2.2.2.2.2.1 + +theorem AllocRep.oneP_eq {n_max U V w base : ℕ} + {s : Cslib.Algorithms.Lean.MinPQ.AllocState n_max} {x : Data rMinPQ} + (hrep : AllocRep n_max U V w base s x) : x.regs oneP = 1 := + hrep.2.1.2.1.2.2.2.2.2.2.2.2.2.2.2.2 + +theorem isEmptyCode_exec {n_max U V w base : ℕ} + {s : Cslib.Algorithms.Lean.MinPQ.AllocState n_max} {x : Data rMinPQ} + (hrep : AllocRep n_max U V w base s x) : + ∃ y t, ExecWithin w isEmptyCode x y t ∧ + decode (n_max := n_max) (U := U) (V := V) .isEmpty y.regs = + some (Cslib.Algorithms.Lean.MinPQ.stepAlloc s .isEmpty).2 ∧ + AllocRep n_max U V w base (Cslib.Algorithms.Lean.MinPQ.stepAlloc s .isEmpty).1 y ∧ + Modifies (Block.mk base (space n_max)).footprint x.mem y.mem ∧ + t + potential y ≤ 3 + potential x := by + obtain ⟨hb, hfib, hwf, hfr, hfb, hfresh, htop, hval, hfree⟩ := hrep + obtain ⟨y_fib, t, he_fib, hdec_fib, hinv_fib, hmod_fib, hpot_fib⟩ := + FibonacciHeap.isEmptyCode_exec hfib + let y := y_fib.extend f42 x + have he : ExecWithin w isEmptyCode x y t := he_fib.rename f42_injective + have hmem_ge : ∀ a, base + FibonacciHeap.space n_max ≤ a → y.mem a = x.mem a := + fun a ha => hmod_fib a (notMem_fib_footprint_of_ge ha) + refine ⟨y, t, he, ?_, + ⟨he.bounded hb, ?_, hwf, + (Data.extend_regs_of_notMem y_fib x outerFrontierReg_notMem_range).trans hfr, + (Data.extend_regs_of_notMem y_fib x freeBaseReg_notMem_range).trans hfb, + (Data.extend_regs_of_notMem y_fib x freshReg_notMem_range).trans hfresh, + (Data.extend_regs_of_notMem y_fib x freeTopReg_notMem_range).trans htop, + fun h u v hsh => ⟨(hval h u v hsh).1, (hval h u v hsh).2.1, + (hmem_ge _ (Nat.le_add_right _ _)).trans (hval h u v hsh).2.2⟩, + fun i hi => (hmem_ge _ (by omega)).trans (hfree i hi)⟩, + modifies_of_fib hmod_fib, ?_⟩ + · simp only [PQCode.decode, FibonacciHeap.code, Cslib.Algorithms.Lean.MinPQ.dom_toIndexed] + at hdec_fib + have hfl_eq : y.regs outFlag = y_fib.regs FibonacciHeap.outFlag := by + change (y.comap f42).regs FibonacciHeap.outFlag = y_fib.regs FibonacciHeap.outFlag + rw [Data.comap_extend f42_injective] + simp only [decode, Cslib.Algorithms.Lean.MinPQ.stepAlloc, hfl_eq] + exact hdec_fib + · simp only [Cslib.Algorithms.Lean.MinPQ.stepAlloc] + rw [Data.comap_extend f42_injective] + exact hinv_fib + · unfold potential + rw [Data.comap_extend f42_injective] + exact hpot_fib + +theorem getCode_exec {n_max U V w base : ℕ} + {s : Cslib.Algorithms.Lean.MinPQ.AllocState n_max} {x : Data rMinPQ} + (h : Fin n_max) (hrep : AllocRep n_max U V w base s x) (hv_reg : x.regs argV = h.val) : + ∃ y t, ExecWithin w getCode x y t ∧ + decode (n_max := n_max) (U := U) (V := V) (.get h) y.regs = + some (Cslib.Algorithms.Lean.MinPQ.stepAlloc s (.get h)).2 ∧ + AllocRep n_max U V w base (Cslib.Algorithms.Lean.MinPQ.stepAlloc s (.get h)).1 y ∧ + Modifies (Block.mk base (space n_max)).footprint x.mem y.mem ∧ + t + potential y ≤ 11 + potential x := by + have hfit_stop := hrep.fits_stop + obtain ⟨hb, hfib, hwf, hfr, hfb, hfresh, htop, hval, hfree⟩ := hrep + obtain ⟨y_fib, t1, he_fib, hdec_fib, hinv_fib, hmod_fib, hpot_fib⟩ := + FibonacciHeap.getKeyCode_exec h hfib hv_reg + let x1 := y_fib.extend f42 x + have he1 : ExecWithin w (FibonacciHeap.getKeyCode.rename f42) x x1 t1 := + he_fib.rename f42_injective + have hmem1_ge : ∀ a, base + FibonacciHeap.space n_max ≤ a → x1.mem a = x.mem a := + fun a ha => hmod_fib a (notMem_fib_footprint_of_ge ha) + have hrep1 : AllocRep n_max U V w base s x1 := + ⟨he1.bounded hb, by rw [Data.comap_extend f42_injective]; exact hinv_fib, hwf, + (Data.extend_regs_of_notMem y_fib x outerFrontierReg_notMem_range).trans hfr, + (Data.extend_regs_of_notMem y_fib x freeBaseReg_notMem_range).trans hfb, + (Data.extend_regs_of_notMem y_fib x freshReg_notMem_range).trans hfresh, + (Data.extend_regs_of_notMem y_fib x freeTopReg_notMem_range).trans htop, + fun h' u' v' hsh' => ⟨(hval h' u' v' hsh').1, (hval h' u' v' hsh').2.1, + (hmem1_ge _ (Nat.le_add_right _ _)).trans (hval h' u' v' hsh').2.2⟩, + fun i hi => (hmem1_ge _ (by omega)).trans (hfree i hi)⟩ + have hx1_z0 : x1.regs zeroP = 0 := hrep1.zeroP_eq + have hx1_vb : x1.regs valBaseReg = base + FibonacciHeap.space n_max := hrep1.valBase_eq + have hx1_v : x1.regs argV = h.val := by + change (x1.comap f42).regs FibonacciHeap.argV = h.val + rw [Data.comap_extend f42_injective] + exact (he_fib.regs_eq (by decide)).trans hv_reg + have hx1_fl : x1.regs outFlag = y_fib.regs FibonacciHeap.outFlag := by + change (x1.comap f42).regs FibonacciHeap.outFlag = y_fib.regs FibonacciHeap.outFlag + rw [Data.comap_extend f42_injective] + have hx1_ok : x1.regs outK = y_fib.regs FibonacciHeap.outK := by + change (x1.comap f42).regs FibonacciHeap.outK = y_fib.regs FibonacciHeap.outK + rw [Data.comap_extend f42_injective] + cases hsh : s.entries h with + | none => + have hfl0 : x1.regs outFlag = 0 := by + rw [hx1_fl] + simp only [PQCode.decode, FibonacciHeap.code, Cslib.Algorithms.Lean.MinPQ.toIndexed_apply, + hsh, Option.map_none] at hdec_fib + by_contra hne + simp only [hne, ↓reduceIte] at hdec_fib + nomatch hdec_fib + have hc : (Cond.eq outFlag zeroP).Holds x1 := by simp only [Cond.Holds, hx1_z0, hfl0] + let y := x1.set s0 0 + have he2 : ExecWithin (B := .exact) w + (.branch (.eq outFlag zeroP) (.op (.copy s0 zeroP)) + (.seq (.op (.exactBin .add s0 valBaseReg argV)) (.op (.load outValReg s0)))) x1 y 2 := + ExecWithin.branch_pos hc (by simpa only [hx1_z0] using + (ExecWithin.copy (B := .exact) (w := w) (x := x1) (d := s0) (s := zeroP))) + have hreg_y : ∀ i : Fin rMinPQ, inputReg i = false → y.regs i = x1.regs i := fun i hi => by + have hne : i ≠ s0 := fun e => by subst e; cases hi + simp only [y, Data.set_regs, Function.update_of_ne hne] + refine ⟨y, t1 + 2, he1.seq he2, ?_, + hrep1.supported hreg_y (fun _ _ => rfl) (he2.bounded hrep1.1), + modifies_of_fib hmod_fib, ?_⟩ + · simp only [decode, Cslib.Algorithms.Lean.MinPQ.stepAlloc, hsh, y, Data.set_regs, + Function.update_of_ne (by decide : outFlag ≠ s0), hfl0, ↓reduceIte] + rfl + · rw [potential_supported hreg_y] + change t1 + 2 + FibonacciHeap.potential (x1.comap f42) ≤ 11 + potential x + rw [Data.comap_extend f42_injective] + change t1 + FibonacciHeap.potential y_fib ≤ 7 + FibonacciHeap.potential (x.comap f42) + at hpot_fib + unfold potential + omega + | some uv => + rcases uv with ⟨u, v⟩ + simp only [PQCode.decode, FibonacciHeap.code, Cslib.Algorithms.Lean.MinPQ.toIndexed_apply, + hsh, Option.map_some] at hdec_fib + have hfl_ne : x1.regs outFlag ≠ 0 := by + rw [hx1_fl] + intro h0 + simp only [h0, ↓reduceIte] at hdec_fib + nomatch hdec_fib + have hok_eq : x1.regs outK = u := by + rw [hx1_ok] + simp only [hx1_fl ▸ hfl_ne, ↓reduceIte] at hdec_fib + exact Option.some.inj (Option.some.inj hdec_fib) + have hc : ¬(Cond.eq outFlag zeroP).Holds x1 := by simp only [Cond.Holds, hx1_z0]; exact hfl_ne + let y := (x1.set s0 (base + FibonacciHeap.space n_max + h.val)).set outValReg v + have he2 : ExecWithin (B := .exact) w + (.branch (.eq outFlag zeroP) (.op (.copy s0 zeroP)) + (.seq (.op (.exactBin .add s0 valBaseReg argV)) (.op (.load outValReg s0)))) x1 y 4 := by + have hh_lt := h.isLt + have ea : ExecWithin w (.op (.exactBin .add s0 valBaseReg argV)) x1 + (x1.set s0 (base + FibonacciHeap.space n_max + h.val)) 1 := by + simpa only [hx1_vb, hx1_v] using + FibonacciHeap.execWithin_exactAdd (x := x1) (d := s0) (i := valBaseReg) (j := argV) + (by rw [hx1_vb, hx1_v]; rw [space_eq_fib_add] at hfit_stop; + exact hfit_stop.of_le (by omega)) + have el : ExecWithin (B := .exact) w (.op (.load outValReg s0)) + (x1.set s0 (base + FibonacciHeap.space n_max + h.val)) y 1 := by + have hm_v : x1.mem (base + FibonacciHeap.space n_max + h.val) = v := + (hrep1.2.2.2.2.2.2.2.1 h u v hsh).2.2 + simpa only [Data.set_mem, Data.set_regs, Function.update_self, hm_v] using + (ExecWithin.load (B := .exact) (w := w) + (x := x1.set s0 (base + FibonacciHeap.space n_max + h.val)) + (d := outValReg) (a := s0)) + exact ExecWithin.branch_neg hc (ea.seq el) + have hreg_y : ∀ i : Fin rMinPQ, inputReg i = false → y.regs i = x1.regs i := fun i hi => by + have h1 : i ≠ outValReg := fun e => by subst e; cases hi + have h2 : i ≠ s0 := fun e => by subst e; cases hi + simp only [y, Data.set_regs, Function.update_of_ne h1, Function.update_of_ne h2] + refine ⟨y, t1 + 4, he1.seq he2, ?_, + hrep1.supported hreg_y (fun _ _ => rfl) (he2.bounded hrep1.1), + modifies_of_fib hmod_fib, ?_⟩ + · simp only [decode, Cslib.Algorithms.Lean.MinPQ.stepAlloc, hsh, y, Data.set_regs, + Function.update_of_ne (by decide : outFlag ≠ outValReg), + Function.update_of_ne (by decide : outFlag ≠ s0), + Function.update_of_ne (by decide : outK ≠ outValReg), + Function.update_of_ne (by decide : outK ≠ s0), + Function.update_self, hfl_ne, hok_eq, ↓reduceIte] + rfl + · rw [potential_supported hreg_y] + change t1 + 4 + FibonacciHeap.potential (x1.comap f42) ≤ 11 + potential x + rw [Data.comap_extend f42_injective] + change t1 + FibonacciHeap.potential y_fib ≤ 7 + FibonacciHeap.potential (x.comap f42) + at hpot_fib + unfold potential + omega + +private theorem allocHandleBranch_exec {n_max U V w base u v : ℕ} + {s : Cslib.Algorithms.Lean.MinPQ.AllocState n_max} {x : Data rMinPQ} + (hrep : AllocRep n_max U V w base s x) + (hsz : Cslib.Algorithms.Lean.MinPQ.size s.entries < n_max) : + ∃ h freeList' fresh' x1, + Cslib.Algorithms.Lean.MinPQ.allocHandle s = some (h, freeList', fresh') ∧ + s.entries h = none ∧ + (Cslib.Algorithms.Lean.MinPQ.AllocState.mk + (Function.update s.entries h (some (u, v))) freeList' fresh').WellFormed ∧ + ExecWithin w allocHandleBranch x x1 5 ∧ + x1.Bounded w ∧ + x1.mem = x.mem ∧ + x1.regs argV = h.val ∧ + x1.regs freshReg = fresh' ∧ + x1.regs freeTopReg = freeList'.length ∧ + (∀ i : Fin rMinPQ, i ≠ argV → i ≠ freshReg → i ≠ freeTopReg → i ≠ s0 → + x1.regs i = x.regs i) ∧ + (∀ (i : ℕ) (hi : i < freeList'.length), + x.mem (base + FibonacciHeap.space n_max + n_max + (freeList'.length - 1 - i)) = + (freeList'[i]).val) := by + have hfit_stop := hrep.fits_stop + have hz0 := hrep.zeroP_eq + have ho1 := hrep.oneP_eq + obtain ⟨hb, _, hwf, _, hfb, hfresh, htop, _, hfree⟩ := hrep + obtain ⟨h, freeList', fresh', halloc, hh_none, hwf'⟩ := + Cslib.Algorithms.Lean.MinPQ.allocHandle_spec hwf hsz u v + have halloc_eq := halloc + rcases hfl : s.freeList with _ | ⟨hd, tl⟩ + · -- Bump-pointer case (`freeList = []`) + have hfr_lt : s.fresh < n_max := by + have hsum := hwf.2.2.2.2 + rw [hfl, List.length_nil, Nat.add_zero] at hsum + omega + simp only [Cslib.Algorithms.Lean.MinPQ.allocHandle, hfl, hfr_lt, ↓reduceDIte, + Option.some.injEq, Prod.mk.injEq] at halloc + obtain ⟨rfl, rfl, rfl⟩ := halloc + have hc : (Cond.eq freeTopReg zeroP).Holds x := by + simp only [Cond.Holds, htop, hfl, List.length_nil, hz0] + let x1 := (x.set argV s.fresh).set freshReg (s.fresh + 1) + have e1 : ExecWithin (B := .exact) w (.op (.copy argV freshReg)) x (x.set argV s.fresh) 1 := by + simpa only [hfresh] using + (ExecWithin.copy (B := .exact) (w := w) (x := x) (d := argV) (s := freshReg)) + have e2 : ExecWithin w (.op (.exactBin .add freshReg freshReg oneP)) + (x.set argV s.fresh) x1 1 := by + have hfr1 : (x.set argV s.fresh).regs freshReg = s.fresh := by + simp only [Data.set_regs, Function.update_of_ne (by decide : freshReg ≠ argV), hfresh] + have ho1' : (x.set argV s.fresh).regs oneP = 1 := by + simp only [Data.set_regs, Function.update_of_ne (by decide : oneP ≠ argV), ho1] + simpa only [hfr1, ho1'] using + FibonacciHeap.execWithin_exactAdd + (x := x.set argV s.fresh) (d := freshReg) (i := freshReg) (j := oneP) + (by rw [hfr1, ho1']; have := space_lower n_max; exact hfit_stop.of_le (by omega)) + have he : ExecWithin w allocHandleBranch x x1 5 := by + unfold allocHandleBranch + exact (ExecWithin.branch_pos hc (e1.seq e2)).mono (by omega) + refine ⟨⟨s.fresh, hfr_lt⟩, [], s.fresh + 1, x1, halloc_eq, hh_none, hwf', he, + he.bounded hb, rfl, + by simp only [x1, Data.set_regs, Function.update_of_ne (by decide : argV ≠ freshReg), + Function.update_self], + by simp only [x1, Data.set_regs, Function.update_self], + by simp only [x1, Data.set_regs, Function.update_of_ne (by decide : freeTopReg ≠ freshReg), + Function.update_of_ne (by decide : freeTopReg ≠ argV), htop, hfl, List.length_nil], + fun i h1 h2 _ _ => by simp only [x1, Data.set_regs, Function.update_of_ne h2, + Function.update_of_ne h1], + fun _ hi => absurd hi (Nat.not_lt_zero _)⟩ + · -- Free-stack pop case (`freeList = hd :: tl`) + simp only [Cslib.Algorithms.Lean.MinPQ.allocHandle, hfl, Option.some.injEq, + Prod.mk.injEq] at halloc + obtain ⟨rfl, rfl, rfl⟩ := halloc + have htop_eq : x.regs freeTopReg = tl.length + 1 := by + rw [htop, hfl, List.length_cons] + have hc : ¬(Cond.eq freeTopReg zeroP).Holds x := by + simp only [Cond.Holds, htop_eq, hz0]; omega + have htl_lt : tl.length < n_max := by + have hsum := hwf.2.2.2.2 + have hfr_le := hwf.1 + rw [hfl, List.length_cons] at hsum + omega + have hm_top : x.mem (base + FibonacciHeap.space n_max + n_max + tl.length) = hd.val := by + have h0 := hfree 0 (by simp only [hfl, List.length_cons]; omega) + simpa only [hfl, List.length_cons, Nat.add_sub_cancel, Nat.sub_zero, + List.getElem_cons_zero] using h0 + let a1 := x.set freeTopReg tl.length + let a2 := a1.set s0 (base + FibonacciHeap.space n_max + n_max + tl.length) + let x1 := a2.set argV hd.val + have e1 : ExecWithin w (.op (.exactBin .sub freeTopReg freeTopReg oneP)) x a1 1 := by + simpa only [htop_eq, ho1, Nat.add_sub_cancel] using + FibonacciHeap.execWithin_exactSub (x := x) (d := freeTopReg) (i := freeTopReg) (j := oneP) + (by rw [htop_eq, ho1]; omega) + (by rw [htop_eq, ho1, Nat.add_sub_cancel]; have := space_lower n_max; + exact hfit_stop.of_le (by omega)) + have e2 : ExecWithin w (.op (.exactBin .add s0 freeBaseReg freeTopReg)) a1 a2 1 := by + have ha1_fb : a1.regs freeBaseReg = base + FibonacciHeap.space n_max + n_max := by + simp only [a1, Data.set_regs, + Function.update_of_ne (by decide : freeBaseReg ≠ freeTopReg), hfb] + have ha1_top : a1.regs freeTopReg = tl.length := by + simp only [a1, Data.set_regs, Function.update_self] + simpa only [ha1_fb, ha1_top] using + FibonacciHeap.execWithin_exactAdd (x := a1) (d := s0) (i := freeBaseReg) (j := freeTopReg) + (by rw [ha1_fb, ha1_top]; rw [space_eq_fib_add] at hfit_stop; + exact hfit_stop.of_le (by omega)) + have e3 : ExecWithin (B := .exact) w (.op (.load argV s0)) a2 x1 1 := by + simpa only [a2, a1, Data.set_mem, Data.set_regs, Function.update_self, hm_top] using + (ExecWithin.load (B := .exact) (w := w) (x := a2) (d := argV) (a := s0)) + have he : ExecWithin w allocHandleBranch x x1 5 := by + unfold allocHandleBranch + exact ExecWithin.branch_neg hc (e1.seq (e2.seq e3)) + refine ⟨hd, tl, s.fresh, x1, halloc_eq, hh_none, hwf', he, he.bounded hb, rfl, + by simp only [x1, Data.set_regs, Function.update_self], + by simp only [x1, a2, a1, Data.set_regs, + Function.update_of_ne (by decide : freshReg ≠ argV), + Function.update_of_ne (by decide : freshReg ≠ s0), + Function.update_of_ne (by decide : freshReg ≠ freeTopReg), hfresh], + by simp only [x1, a2, a1, Data.set_regs, + Function.update_of_ne (by decide : freeTopReg ≠ argV), + Function.update_of_ne (by decide : freeTopReg ≠ s0), Function.update_self], + fun i h1 _ h3 h4 => by simp only [x1, a2, a1, Data.set_regs, + Function.update_of_ne h1, Function.update_of_ne h4, Function.update_of_ne h3], + fun i hi => ?_⟩ + have hi' : i + 1 < s.freeList.length := by simp only [hfl, List.length_cons]; omega + have hfi := hfree (i + 1) hi' + have hidx : s.freeList.length - 1 - (i + 1) = tl.length - 1 - i := by + simp only [hfl, List.length_cons]; omega + have hget : (s.freeList[i + 1]'hi') = tl[i] := by simp only [hfl, List.getElem_cons_succ] + rw [hidx, hget] at hfi + exact hfi + +theorem insertCode_exec {n_max U V w base u v : ℕ} + {s : Cslib.Algorithms.Lean.MinPQ.AllocState n_max} {x : Data rMinPQ} + (hrep : AllocRep n_max U V w base s x) + (hv : (Cslib.Algorithms.Lean.MinPQ.spec n_max U V).valid s.entries (.insert u v)) + (hk_reg : x.regs argK = u) (hval_reg : x.regs inValReg = v) : + ∃ y t, ExecWithin w insertCode x y t ∧ + decode (n_max := n_max) (U := U) (V := V) (.insert u v) y.regs = + some (Cslib.Algorithms.Lean.MinPQ.stepAlloc s (.insert u v)).2 ∧ + AllocRep n_max U V w base (Cslib.Algorithms.Lean.MinPQ.stepAlloc s (.insert u v)).1 y ∧ + Modifies (Block.mk base (space n_max)).footprint x.mem y.mem ∧ + t + potential y ≤ 129 + potential x := by + have hfit_stop := hrep.fits_stop + have hvb := hrep.valBase_eq + obtain ⟨hsz, hu_lt, hv_lt⟩ := hv + obtain ⟨h, freeList', fresh', x1, halloc, hh_none, hwf', he_alloc, hb1, hm1, + hx1_v, hx1_fresh, hx1_top, hx1_regs, hfree'⟩ := + allocHandleBranch_exec (u := u) (v := v) hrep hsz + have hh_lt := h.isLt + have ho_val := owns_val_addr (base := base) hh_lt + have hx1_vb : x1.regs valBaseReg = base + FibonacciHeap.space n_max := + (hx1_regs valBaseReg (by decide) (by decide) (by decide) (by decide)).trans hvb + have hx1_inVal : x1.regs inValReg = v := + (hx1_regs inValReg (by decide) (by decide) (by decide) (by decide)).trans hval_reg + let x2 := (x1.set s0 (base + FibonacciHeap.space n_max + h.val)).write + (base + FibonacciHeap.space n_max + h.val) v + have ew1 : ExecWithin w (.op (.exactBin .add s0 valBaseReg argV)) x1 + (x1.set s0 (base + FibonacciHeap.space n_max + h.val)) 1 := by + simpa only [hx1_vb, hx1_v] using + FibonacciHeap.execWithin_exactAdd (x := x1) (d := s0) (i := valBaseReg) (j := argV) + (by rw [hx1_vb, hx1_v]; rw [space_eq_fib_add] at hfit_stop; + exact hfit_stop.of_le (by omega)) + have ew2 : ExecWithin (B := .exact) w (.op (.store s0 inValReg)) + (x1.set s0 (base + FibonacciHeap.space n_max + h.val)) x2 1 := by + simpa only [Data.set_regs, Function.update_self, + Function.update_of_ne (by decide : inValReg ≠ s0), hx1_inVal] using + (ExecWithin.store (B := .exact) (w := w) + (x := x1.set s0 (base + FibonacciHeap.space n_max + h.val)) (a := s0) (s := inValReg)) + have hb2 : x2.Bounded w := ew2.bounded (ew1.bounded hb1) + have hreg2 : ∀ j : Fin rFib, FibonacciHeap.inputReg j = false → + (x2.comap f42).regs j = (x.comap f42).regs j := fun j hj => by + have hj_in : inputReg (f42 j) = false := inputReg_f42_of_false j hj + change x2.regs (f42 j) = x.regs (f42 j) + have h1 : f42 j ≠ argV := fun e => by rw [e] at hj_in; cases hj_in + have h2 : f42 j ≠ freshReg := fun e => freshReg_notMem_range ⟨j, e⟩ + have h3 : f42 j ≠ freeTopReg := fun e => freeTopReg_notMem_range ⟨j, e⟩ + have h4 : f42 j ≠ s0 := fun e => by rw [e] at hj_in; cases hj_in + simp only [x2, Data.write_regs, Data.set_regs, Function.update_of_ne h4] + exact hx1_regs (f42 j) h1 h2 h3 h4 + have hmem2_fib : ∀ a ∈ (Block.mk base (FibonacciHeap.space n_max)).footprint, + (x2.comap f42).mem a = (x.comap f42).mem a := fun a ha => by + have hne : a ≠ base + FibonacciHeap.space n_max + h.val := fun e => + notMem_fib_footprint_of_ge (by omega) (e ▸ ha) + simp only [x2, Data.comap, Data.write_mem, Data.set_mem, Function.update_of_ne hne, hm1] + have hinv2 : FibonacciHeap.FibInv n_max U w base + (Cslib.Algorithms.Lean.MinPQ.toIndexed s.entries) (x2.comap f42) := + FibonacciHeap.FibInv.supported hreg2 hmem2_fib (hb2.comap f42) hrep.2.1 + have hpot2 : potential x2 = potential x := + FibonacciHeap.potential_supported hreg2 + have hx2_v : (x2.comap f42).regs FibonacciHeap.argV = h.val := by + change x2.regs argV = h.val + simp only [x2, Data.write_regs, Data.set_regs, + Function.update_of_ne (by decide : argV ≠ s0), hx1_v] + have hx2_k : (x2.comap f42).regs FibonacciHeap.argK = u := by + change x2.regs argK = u + simp only [x2, Data.write_regs, Data.set_regs, + Function.update_of_ne (by decide : argK ≠ s0)] + exact (hx1_regs argK (by decide) (by decide) (by decide) (by decide)).trans hk_reg + have hval_fib : Cslib.Algorithms.Lean.MinPQ.toIndexed s.entries h = none ∧ u ≤ U := by + simp only [Cslib.Algorithms.Lean.MinPQ.toIndexed_apply, hh_none, Option.map_none, true_and] + exact Nat.le_of_lt hu_lt + obtain ⟨y_fib, t_fib, he_fib, hinv_fib, hmod_fib, hpot_fib⟩ := + FibonacciHeap.insertCode_exec h u hinv2 hval_fib hx2_v hx2_k + let x3 := y_fib.extend f42 x2 + have he3 : ExecWithin w (FibonacciHeap.insertCode.rename f42) x2 x3 t_fib := + he_fib.rename f42_injective + have hx3_v : x3.regs argV = h.val := by + change (x3.comap f42).regs FibonacciHeap.argV = h.val + rw [Data.comap_extend f42_injective] + exact (he_fib.regs_eq (by decide)).trans hx2_v + let y := x3.set outV h.val + have he4 : ExecWithin (B := .exact) w (.op (.copy outV argV)) x3 y 1 := by + simpa only [hx3_v] using + (ExecWithin.copy (B := .exact) (w := w) (x := x3) (d := outV) (s := argV)) + have he : ExecWithin w insertCode x y (5 + 1 + 1 + t_fib + 1) := by + unfold insertCode + exact (he_alloc.seq (ew1.seq (ew2.seq (he3.seq he4)))).mono (by omega) + have hby : y.Bounded w := he4.bounded (he3.bounded hb2) + have hreg_y3 : ∀ i : Fin rMinPQ, inputReg i = false → y.regs i = x3.regs i := fun i hi => by + have hne : i ≠ outV := fun e => by subst e; cases hi + simp only [y, Data.set_regs, Function.update_of_ne hne] + have hmem3_ge : ∀ a, base + FibonacciHeap.space n_max ≤ a → x3.mem a = x2.mem a := + fun a ha => hmod_fib a (notMem_fib_footprint_of_ge ha) + have hmod_all : Modifies (Block.mk base (space n_max)).footprint x.mem y.mem := by + have hm_x2 : Modifies (Block.mk base (space n_max)).footprint x.mem x2.mem := by + change Modifies (Block.mk base (space n_max)).footprint x.mem + (Function.update x1.mem (base + FibonacciHeap.space n_max + h.val) v) + rw [hm1] + exact Modifies.write_of_owns ho_val v + exact FibonacciHeap.modifies_block_trans hm_x2 (modifies_of_fib hmod_fib) + refine ⟨y, 5 + 1 + 1 + t_fib + 1, he, ?_, ?_, hmod_all, ?_⟩ + · simp only [decode, Cslib.Algorithms.Lean.MinPQ.stepAlloc, halloc, y, Data.set_regs, + Function.update_self, hh_lt, ↓reduceDIte, Fin.eta] + rfl + · simp only [Cslib.Algorithms.Lean.MinPQ.stepAlloc, halloc] + refine AllocRep.supported hreg_y3 (fun _ _ => rfl) hby ⟨he3.bounded hb2, ?_, hwf', + ?_, ?_, ?_, ?_, ?_, ?_⟩ + · dsimp only + rw [Data.comap_extend f42_injective, + Cslib.Algorithms.Lean.MinPQ.toIndexed_update_some s.entries h u v] + exact hinv_fib + · rw [Data.extend_regs_of_notMem y_fib x2 outerFrontierReg_notMem_range] + simp only [x2, Data.write_regs, Data.set_regs, + Function.update_of_ne (by decide : outerFrontierReg ≠ s0)] + exact (hx1_regs outerFrontierReg (by decide) (by decide) (by decide) (by decide)).trans + hrep.2.2.2.1 + · rw [Data.extend_regs_of_notMem y_fib x2 freeBaseReg_notMem_range] + simp only [x2, Data.write_regs, Data.set_regs, + Function.update_of_ne (by decide : freeBaseReg ≠ s0)] + exact (hx1_regs freeBaseReg (by decide) (by decide) (by decide) (by decide)).trans + hrep.2.2.2.2.1 + · rw [Data.extend_regs_of_notMem y_fib x2 freshReg_notMem_range] + simp only [x2, Data.write_regs, Data.set_regs, + Function.update_of_ne (by decide : freshReg ≠ s0), hx1_fresh] + · rw [Data.extend_regs_of_notMem y_fib x2 freeTopReg_notMem_range] + simp only [x2, Data.write_regs, Data.set_regs, + Function.update_of_ne (by decide : freeTopReg ≠ s0), hx1_top] + · intro h' u' v' hsh' + rw [hmem3_ge _ (Nat.le_add_right _ _)] + by_cases hh' : h' = h + · subst hh' + simp only [Function.update_self, Option.some.injEq, Prod.mk.injEq] at hsh' + obtain ⟨rfl, rfl⟩ := hsh' + exact ⟨hu_lt, hv_lt, by simp only [x2, Data.write_mem, Function.update_self]⟩ + · simp only [Function.update_of_ne hh'] at hsh' + have hne_addr : base + FibonacciHeap.space n_max + h'.val ≠ + base + FibonacciHeap.space n_max + h.val := by + have := Fin.val_ne_of_ne hh'; omega + obtain ⟨hu', hv', hm'⟩ := hrep.2.2.2.2.2.2.2.1 h' u' v' hsh' + exact ⟨hu', hv', by simp only [x2, Data.write_mem, Data.set_mem, + Function.update_of_ne hne_addr, hm1, hm']⟩ + · intro i hi + rw [hmem3_ge _ (by omega)] + have hne_addr : + base + FibonacciHeap.space n_max + n_max + (freeList'.length - 1 - i) ≠ + base + FibonacciHeap.space n_max + h.val := by omega + simp only [x2, Data.write_mem, Data.set_mem, Function.update_of_ne hne_addr, hm1] + exact hfree' i hi + · rw [potential_supported hreg_y3] + change 5 + 1 + 1 + t_fib + 1 + FibonacciHeap.potential (x3.comap f42) ≤ 129 + potential x + rw [Data.comap_extend f42_injective] + change t_fib + FibonacciHeap.potential y_fib ≤ 121 + FibonacciHeap.potential (x2.comap f42) + at hpot_fib + change FibonacciHeap.potential (x2.comap f42) = potential x at hpot2 + omega + +theorem decreaseKeyCode_exec {n_max U V w base : ℕ} + {s : Cslib.Algorithms.Lean.MinPQ.AllocState n_max} {x : Data rMinPQ} + (h : Fin n_max) (u' : ℕ) (hrep : AllocRep n_max U V w base s x) + (hv : (Cslib.Algorithms.Lean.MinPQ.spec n_max U V).valid s.entries (.decreaseKey h u')) + (hv_reg : x.regs argV = h.val) (hk_reg : x.regs argK = u') : + ∃ y t, ExecWithin w decreaseKeyCode x y t ∧ + decode (n_max := n_max) (U := U) (V := V) (.decreaseKey h u') y.regs = + some (Cslib.Algorithms.Lean.MinPQ.stepAlloc s (.decreaseKey h u')).2 ∧ + AllocRep n_max U V w base (Cslib.Algorithms.Lean.MinPQ.stepAlloc s (.decreaseKey h u')).1 y ∧ + Modifies (Block.mk base (space n_max)).footprint x.mem y.mem ∧ + t + potential y ≤ 330 + potential x := by + obtain ⟨hb, hfib, hwf, hfr, hfb, hfresh, htop, hval, hfree⟩ := hrep + obtain ⟨u, v, hsh, hu'_le⟩ := hv + obtain ⟨hu_lt, hv_lt, _⟩ := hval h u v hsh + have hu'_lt : u' < U := Nat.lt_of_le_of_lt hu'_le hu_lt + have hval_fib : + (∃ k₀, Cslib.Algorithms.Lean.MinPQ.toIndexed s.entries h = some k₀ ∧ u' ≤ k₀) ∧ u' ≤ U := by + refine ⟨⟨u, ?_, hu'_le⟩, Nat.le_of_lt hu'_lt⟩ + simp only [Cslib.Algorithms.Lean.MinPQ.toIndexed_apply, hsh, Option.map_some] + obtain ⟨y_fib, t, he_fib, hinv_fib, hmod_fib, hpot_fib⟩ := + FibonacciHeap.decreaseKeyCode_exec h u' hfib hval_fib hv_reg hk_reg + let y := y_fib.extend f42 x + have he : ExecWithin w decreaseKeyCode x y t := he_fib.rename f42_injective + have hmem_ge : ∀ a, base + FibonacciHeap.space n_max ≤ a → y.mem a = x.mem a := + fun a ha => hmod_fib a (notMem_fib_footprint_of_ge ha) + have hwf' := + (Cslib.Algorithms.Lean.MinPQ.stepAlloc_refines_spec (U := U) (V := V) hwf + (.decreaseKey h u') ⟨u, v, hsh, hu'_le⟩).2 + refine ⟨y, t, he, rfl, ?_, modifies_of_fib hmod_fib, ?_⟩ + · refine ⟨he.bounded hb, ?_, hwf', + (Data.extend_regs_of_notMem y_fib x outerFrontierReg_notMem_range).trans hfr, + (Data.extend_regs_of_notMem y_fib x freeBaseReg_notMem_range).trans hfb, + by simp only [Cslib.Algorithms.Lean.MinPQ.stepAlloc, hsh]; + exact (Data.extend_regs_of_notMem y_fib x freshReg_notMem_range).trans hfresh, + by simp only [Cslib.Algorithms.Lean.MinPQ.stepAlloc, hsh]; + exact (Data.extend_regs_of_notMem y_fib x freeTopReg_notMem_range).trans htop, + ?_, ?_⟩ + · simp only [Cslib.Algorithms.Lean.MinPQ.stepAlloc, hsh] + rw [Data.comap_extend f42_injective, + Cslib.Algorithms.Lean.MinPQ.toIndexed_update_some s.entries h u' v] + exact hinv_fib + · intro h' u₁ v₁ hsh' + simp only [Cslib.Algorithms.Lean.MinPQ.stepAlloc, hsh] at hsh' + rw [hmem_ge _ (Nat.le_add_right _ _)] + by_cases hh' : h' = h + · subst hh' + simp only [Function.update_self, Option.some.injEq, Prod.mk.injEq] at hsh' + obtain ⟨rfl, rfl⟩ := hsh' + exact ⟨hu'_lt, hv_lt, (hval h' u v hsh).2.2⟩ + · simp only [Function.update_of_ne hh'] at hsh' + exact hval h' u₁ v₁ hsh' + · intro i hi + simp only [Cslib.Algorithms.Lean.MinPQ.stepAlloc, hsh] at hi ⊢ + rw [hmem_ge _ (by omega)] + exact hfree i hi + · unfold potential + rw [Data.comap_extend f42_injective] + exact hpot_fib + +theorem extractMinCode_exec {n_max U V w base : ℕ} + {s : Cslib.Algorithms.Lean.MinPQ.AllocState n_max} {x : Data rMinPQ} + (hrep : AllocRep n_max U V w base s x) : + ∃ y t, ExecWithin w extractMinCode x y t ∧ + decode (n_max := n_max) (U := U) (V := V) .extractMin y.regs = + some (Cslib.Algorithms.Lean.MinPQ.stepAlloc s .extractMin).2 ∧ + AllocRep n_max U V w base (Cslib.Algorithms.Lean.MinPQ.stepAlloc s .extractMin).1 y ∧ + Modifies (Block.mk base (space n_max)).footprint x.mem y.mem ∧ + t + potential y ≤ + 418 * (Nat.log 2 (Cslib.Algorithms.Lean.MinPQ.size s.entries) + 1) + potential x := by + have hfit_stop := hrep.fits_stop + obtain ⟨hb, hfib, hwf, hfr, hfb, hfresh, htop, hval, hfree⟩ := hrep + obtain ⟨y_fib, t1, he_fib, hdec_fib, hinv_fib, hmod_fib, hpot_fib⟩ := + FibonacciHeap.extractMinCode_exec hfib + rw [Cslib.Algorithms.Lean.MinPQ.dom_toIndexed] at hpot_fib + change t1 + FibonacciHeap.potential y_fib ≤ + 410 * (Nat.log 2 (Cslib.Algorithms.Lean.MinPQ.size s.entries) + 1) + + FibonacciHeap.potential (x.comap f42) at hpot_fib + let x1 := y_fib.extend f42 x + have he1 : ExecWithin w (FibonacciHeap.extractMinCode.rename f42) x x1 t1 := + he_fib.rename f42_injective + have hb1 : x1.Bounded w := he1.bounded hb + have hmem1_ge : ∀ a, base + FibonacciHeap.space n_max ≤ a → x1.mem a = x.mem a := + fun a ha => hmod_fib a (notMem_fib_footprint_of_ge ha) + have hx1_z0 : x1.regs zeroP = 0 := by + change (x1.comap f42).regs FibonacciHeap.zeroP = 0 + rw [Data.comap_extend f42_injective] + exact hinv_fib.2.1.2.2.2.2.2.2.2.2.2.2.2.1 + have hx1_o1 : x1.regs oneP = 1 := by + change (x1.comap f42).regs FibonacciHeap.oneP = 1 + rw [Data.comap_extend f42_injective] + exact hinv_fib.2.1.2.2.2.2.2.2.2.2.2.2.2.2 + have hx1_vb : x1.regs valBaseReg = base + FibonacciHeap.space n_max := by + change (x1.comap f42).regs FibonacciHeap.frontierReg = base + FibonacciHeap.space n_max + rw [Data.comap_extend f42_injective] + exact hinv_fib.2.1.2.1 + have hx1_fb : x1.regs freeBaseReg = base + FibonacciHeap.space n_max + n_max := + (Data.extend_regs_of_notMem y_fib x freeBaseReg_notMem_range).trans hfb + have hx1_fresh : x1.regs freshReg = s.fresh := + (Data.extend_regs_of_notMem y_fib x freshReg_notMem_range).trans hfresh + have hx1_top : x1.regs freeTopReg = s.freeList.length := + (Data.extend_regs_of_notMem y_fib x freeTopReg_notMem_range).trans htop + have hx1_ofr : x1.regs outerFrontierReg = base + space n_max := + (Data.extend_regs_of_notMem y_fib x outerFrontierReg_notMem_range).trans hfr + have hx1_fl : x1.regs outFlag = y_fib.regs FibonacciHeap.outFlag := by + change (x1.comap f42).regs FibonacciHeap.outFlag = y_fib.regs FibonacciHeap.outFlag + rw [Data.comap_extend f42_injective] + have hx1_ov : x1.regs outV = y_fib.regs FibonacciHeap.outV := by + change (x1.comap f42).regs FibonacciHeap.outV = y_fib.regs FibonacciHeap.outV + rw [Data.comap_extend f42_injective] + have hx1_ok : x1.regs outK = y_fib.regs FibonacciHeap.outK := by + change (x1.comap f42).regs FibonacciHeap.outK = y_fib.regs FibonacciHeap.outK + rw [Data.comap_extend f42_injective] + have hwf' := + (Cslib.Algorithms.Lean.MinPQ.stepAlloc_refines_spec (U := U) (V := V) hwf .extractMin + trivial).2 + rcases hfm : IndexedMinPQ.findMin (Cslib.Algorithms.Lean.MinPQ.toIndexed s.entries) with + _ | ⟨h, u⟩ + · -- Empty queue (`findMin = none`) + simp only [PQCode.decode, FibonacciHeap.code, IndexedMinPQ.spec, hfm] at hdec_fib hinv_fib + have hfl0 : x1.regs outFlag = 0 := by + rw [hx1_fl] + by_contra hne + simp only [hne, ↓reduceIte] at hdec_fib + by_cases hlt : y_fib.regs FibonacciHeap.outV < n_max <;> + simp only [hlt, ↓reduceDIte] at hdec_fib <;> + nomatch hdec_fib + have hc : (Cond.eq outFlag zeroP).Holds x1 := by simp only [Cond.Holds, hx1_z0, hfl0] + let y := x1.set s0 0 + have he2 : ExecWithin (B := .exact) w + (.branch (.eq outFlag zeroP) (.op (.copy s0 zeroP)) + (.seq (.op (.exactBin .add s0 valBaseReg outV)) <| + .seq (.op (.load outValReg s0)) <| + .seq (.op (.exactBin .add s0 freeBaseReg freeTopReg)) <| + .seq (.op (.store s0 outV)) + (.op (.exactBin .add freeTopReg freeTopReg oneP)))) x1 y 2 := + ExecWithin.branch_pos hc (by simpa only [hx1_z0] using + (ExecWithin.copy (B := .exact) (w := w) (x := x1) (d := s0) (s := zeroP))) + have hreg_y : ∀ i : Fin rMinPQ, inputReg i = false → y.regs i = x1.regs i := fun i hi => by + have hne : i ≠ s0 := fun e => by subst e; cases hi + simp only [y, Data.set_regs, Function.update_of_ne hne] + have hrep1 : AllocRep n_max U V w base s x1 := + ⟨hb1, by rw [Data.comap_extend f42_injective]; exact hinv_fib, hwf, + hx1_ofr, hx1_fb, hx1_fresh, hx1_top, + fun h' u' v' hsh' => ⟨(hval h' u' v' hsh').1, (hval h' u' v' hsh').2.1, + (hmem1_ge _ (Nat.le_add_right _ _)).trans (hval h' u' v' hsh').2.2⟩, + fun i hi => (hmem1_ge _ (by omega)).trans (hfree i hi)⟩ + refine ⟨y, t1 + 2, he1.seq he2, ?_, ?_, modifies_of_fib hmod_fib, ?_⟩ + · have hfm' : Cslib.Algorithms.Lean.MinPQ.findMin s.entries = none := by + simp only [Cslib.Algorithms.Lean.MinPQ.findMin, hfm] + simp only [decode, Cslib.Algorithms.Lean.MinPQ.stepAlloc, hfm', y, Data.set_regs, + Function.update_of_ne (by decide : outFlag ≠ s0), hfl0, ↓reduceIte] + rfl + · have hfm' : Cslib.Algorithms.Lean.MinPQ.findMin s.entries = none := by + simp only [Cslib.Algorithms.Lean.MinPQ.findMin, hfm] + simp only [Cslib.Algorithms.Lean.MinPQ.stepAlloc, hfm'] + exact hrep1.supported hreg_y (fun _ _ => rfl) (he2.bounded hb1) + · rw [potential_supported hreg_y] + change t1 + 2 + FibonacciHeap.potential (x1.comap f42) ≤ _ + potential x + rw [Data.comap_extend f42_injective] + unfold potential + omega + · -- Non-empty queue (`findMin = some (h, u)`) + obtain ⟨v, hsh, hfm', _⟩ := Cslib.Algorithms.Lean.MinPQ.findMin_eq_some_of_indexed hfm + simp only [PQCode.decode, FibonacciHeap.code, IndexedMinPQ.spec, hfm] at hdec_fib hinv_fib + have hfl_ne : x1.regs outFlag ≠ 0 := by + rw [hx1_fl] + intro h0 + simp only [h0, ↓reduceIte] at hdec_fib + nomatch hdec_fib + simp only [hx1_fl ▸ hfl_ne, ↓reduceIte] at hdec_fib + have hlt_ov : y_fib.regs FibonacciHeap.outV < n_max := by + by_contra hge + simp only [hge, ↓reduceDIte] at hdec_fib + nomatch hdec_fib + simp only [hlt_ov, ↓reduceDIte, Option.some.injEq, Prod.mk.injEq, Fin.ext_iff] at hdec_fib + have hov_eq : x1.regs outV = h.val := hx1_ov.trans hdec_fib.1 + have hok_eq : x1.regs outK = u := hx1_ok.trans hdec_fib.2 + have hc : ¬(Cond.eq outFlag zeroP).Holds x1 := by simp only [Cond.Holds, hx1_z0]; exact hfl_ne + have hh_lt := h.isLt + have hsz_pos : 1 ≤ Cslib.Algorithms.Lean.MinPQ.size s.entries := + Finset.card_pos.mpr ⟨h, Cslib.Algorithms.Lean.MinPQ.mem_dom_iff.mpr ⟨u, v, hsh⟩⟩ + have hfl_lt : s.freeList.length < n_max := by + have := hwf.1 + have := hwf.2.2.2.2 + omega + have ho_free := owns_free_addr (base := base) hfl_lt + let a1 := x1.set s0 (base + FibonacciHeap.space n_max + h.val) + let a2 := a1.set outValReg v + let a3 := a2.set s0 (base + FibonacciHeap.space n_max + n_max + s.freeList.length) + let a4 := a3.write (base + FibonacciHeap.space n_max + n_max + s.freeList.length) h.val + let y := a4.set freeTopReg (s.freeList.length + 1) + have eb1 : ExecWithin w (.op (.exactBin .add s0 valBaseReg outV)) x1 a1 1 := by + simpa only [hx1_vb, hov_eq] using + FibonacciHeap.execWithin_exactAdd (x := x1) (d := s0) (i := valBaseReg) (j := outV) + (by rw [hx1_vb, hov_eq]; rw [space_eq_fib_add] at hfit_stop; + exact hfit_stop.of_le (by omega)) + have eb2 : ExecWithin (B := .exact) w (.op (.load outValReg s0)) a1 a2 1 := by + have hm_v : x1.mem (base + FibonacciHeap.space n_max + h.val) = v := + (hmem1_ge _ (Nat.le_add_right _ _)).trans (hval h u v hsh).2.2 + simpa only [a1, Data.set_mem, Data.set_regs, Function.update_self, hm_v] using + (ExecWithin.load (B := .exact) (w := w) (x := a1) (d := outValReg) (a := s0)) + have eb3 : ExecWithin w (.op (.exactBin .add s0 freeBaseReg freeTopReg)) a2 a3 1 := by + have ha2_fb : a2.regs freeBaseReg = base + FibonacciHeap.space n_max + n_max := by + simp only [a2, a1, Data.set_regs, + Function.update_of_ne (by decide : freeBaseReg ≠ outValReg), + Function.update_of_ne (by decide : freeBaseReg ≠ s0), hx1_fb] + have ha2_top : a2.regs freeTopReg = s.freeList.length := by + simp only [a2, a1, Data.set_regs, + Function.update_of_ne (by decide : freeTopReg ≠ outValReg), + Function.update_of_ne (by decide : freeTopReg ≠ s0), hx1_top] + simpa only [ha2_fb, ha2_top] using + FibonacciHeap.execWithin_exactAdd (x := a2) (d := s0) (i := freeBaseReg) (j := freeTopReg) + (by rw [ha2_fb, ha2_top]; rw [space_eq_fib_add] at hfit_stop; + exact hfit_stop.of_le (by omega)) + have eb4 : ExecWithin (B := .exact) w (.op (.store s0 outV)) a3 a4 1 := by + have ha3_ov : a3.regs outV = h.val := by + simp only [a3, a2, a1, Data.set_regs, + Function.update_of_ne (by decide : outV ≠ s0), + Function.update_of_ne (by decide : outV ≠ outValReg), hov_eq] + simpa only [a3, Data.set_regs, Function.update_self, ha3_ov] using + (ExecWithin.store (B := .exact) (w := w) (x := a3) (a := s0) (s := outV)) + have eb5 : ExecWithin w (.op (.exactBin .add freeTopReg freeTopReg oneP)) a4 y 1 := by + have ha4_top : a4.regs freeTopReg = s.freeList.length := by + simp only [a4, a3, a2, a1, Data.write_regs, Data.set_regs, + Function.update_of_ne (by decide : freeTopReg ≠ s0), + Function.update_of_ne (by decide : freeTopReg ≠ outValReg), hx1_top] + have ha4_o1 : a4.regs oneP = 1 := by + simp only [a4, a3, a2, a1, Data.write_regs, Data.set_regs, + Function.update_of_ne (by decide : oneP ≠ s0), + Function.update_of_ne (by decide : oneP ≠ outValReg), hx1_o1] + simpa only [ha4_top, ha4_o1] using + FibonacciHeap.execWithin_exactAdd (x := a4) (d := freeTopReg) (i := freeTopReg) (j := oneP) + (by rw [ha4_top, ha4_o1]; have := space_lower n_max; + exact hfit_stop.of_le (by omega)) + have he2 : ExecWithin (B := .exact) w + (.branch (.eq outFlag zeroP) (.op (.copy s0 zeroP)) + (.seq (.op (.exactBin .add s0 valBaseReg outV)) <| + .seq (.op (.load outValReg s0)) <| + .seq (.op (.exactBin .add s0 freeBaseReg freeTopReg)) <| + .seq (.op (.store s0 outV)) + (.op (.exactBin .add freeTopReg freeTopReg oneP)))) x1 y 7 := + ExecWithin.branch_neg hc (eb1.seq (eb2.seq (eb3.seq (eb4.seq eb5)))) + have hby : y.Bounded w := he2.bounded hb1 + have hreg_fib : ∀ j : Fin rFib, FibonacciHeap.inputReg j = false → + (y.comap f42).regs j = (x1.comap f42).regs j := fun j hj => by + have hj_in : inputReg (f42 j) = false := inputReg_f42_of_false j hj + change y.regs (f42 j) = x1.regs (f42 j) + have h1 : f42 j ≠ freeTopReg := fun e => freeTopReg_notMem_range ⟨j, e⟩ + have h2 : f42 j ≠ s0 := fun e => by rw [e] at hj_in; cases hj_in + have h3 : f42 j ≠ outValReg := fun e => outValReg_notMem_range ⟨j, e⟩ + simp only [y, a4, a3, a2, a1, Data.set_regs, Data.write_regs, + Function.update_of_ne h1, Function.update_of_ne h2, Function.update_of_ne h3] + have hmem_fib : ∀ a ∈ (Block.mk base (FibonacciHeap.space n_max)).footprint, + (y.comap f42).mem a = (x1.comap f42).mem a := fun a ha => by + have hne : a ≠ base + FibonacciHeap.space n_max + n_max + s.freeList.length := fun e => + notMem_fib_footprint_of_ge (by omega) (e ▸ ha) + simp only [y, a4, a3, a2, a1, Data.comap, Data.set_mem, Data.write_mem, + Function.update_of_ne hne] + have hmod_all : Modifies (Block.mk base (space n_max)).footprint x.mem y.mem := by + have hm_y : Modifies (Block.mk base (space n_max)).footprint x1.mem y.mem := + Modifies.write_of_owns ho_free h.val + exact FibonacciHeap.modifies_block_trans (modifies_of_fib hmod_fib) hm_y + refine ⟨y, t1 + 7, he1.seq he2, ?_, ?_, hmod_all, ?_⟩ + · simp only [decode, Cslib.Algorithms.Lean.MinPQ.stepAlloc, hfm', y, a4, a3, a2, a1, + Data.set_regs, Data.write_regs, + Function.update_of_ne (by decide : outFlag ≠ freeTopReg), + Function.update_of_ne (by decide : outFlag ≠ s0), + Function.update_of_ne (by decide : outFlag ≠ outValReg), + Function.update_of_ne (by decide : outV ≠ freeTopReg), + Function.update_of_ne (by decide : outV ≠ s0), + Function.update_of_ne (by decide : outV ≠ outValReg), + Function.update_of_ne (by decide : outK ≠ freeTopReg), + Function.update_of_ne (by decide : outK ≠ s0), + Function.update_of_ne (by decide : outK ≠ outValReg), + Function.update_of_ne (by decide : outValReg ≠ freeTopReg), + Function.update_of_ne (by decide : outValReg ≠ s0), + Function.update_self, hfl_ne, hov_eq, hok_eq, hh_lt, ↓reduceIte, ↓reduceDIte, Fin.eta] + rfl + · simp only [Cslib.Algorithms.Lean.MinPQ.stepAlloc, hfm'] at hwf' ⊢ + refine ⟨hby, ?_, hwf', ?_, ?_, ?_, ?_, ?_, ?_⟩ + · refine FibonacciHeap.FibInv.supported hreg_fib hmem_fib (hby.comap f42) ?_ + dsimp only + rw [Data.comap_extend f42_injective, + Cslib.Algorithms.Lean.MinPQ.toIndexed_update_none s.entries h] + exact hinv_fib + · simp only [y, a4, a3, a2, a1, Data.set_regs, Data.write_regs, + Function.update_of_ne (by decide : outerFrontierReg ≠ freeTopReg), + Function.update_of_ne (by decide : outerFrontierReg ≠ s0), + Function.update_of_ne (by decide : outerFrontierReg ≠ outValReg), hx1_ofr] + · simp only [y, a4, a3, a2, a1, Data.set_regs, Data.write_regs, + Function.update_of_ne (by decide : freeBaseReg ≠ freeTopReg), + Function.update_of_ne (by decide : freeBaseReg ≠ s0), + Function.update_of_ne (by decide : freeBaseReg ≠ outValReg), hx1_fb] + · simp only [y, a4, a3, a2, a1, Data.set_regs, Data.write_regs, + Function.update_of_ne (by decide : freshReg ≠ freeTopReg), + Function.update_of_ne (by decide : freshReg ≠ s0), + Function.update_of_ne (by decide : freshReg ≠ outValReg), hx1_fresh] + · simp only [y, Data.set_regs, Function.update_self, List.length_cons] + · intro h' u' v' hsh' + by_cases hh' : h' = h + · subst hh'; simp only [Function.update_self, reduceCtorEq] at hsh' + · simp only [Function.update_of_ne hh'] at hsh' + have hne_addr : base + FibonacciHeap.space n_max + h'.val ≠ + base + FibonacciHeap.space n_max + n_max + s.freeList.length := by + have := h'.isLt; omega + obtain ⟨hu', hv', hm'⟩ := hval h' u' v' hsh' + refine ⟨hu', hv', ?_⟩ + simp only [y, a4, a3, a2, a1, Data.set_mem, Data.write_mem, + Function.update_of_ne hne_addr] + exact (hmem1_ge _ (Nat.le_add_right _ _)).trans hm' + · intro i hi + simp only [List.length_cons] at hi + rcases i with _ | j + · simp only [List.length_cons, Nat.add_sub_cancel, Nat.sub_zero, List.getElem_cons_zero, + y, a4, a3, a2, a1, Data.set_mem, Data.write_mem, Function.update_self] + · have hj : j < s.freeList.length := by omega + have hidx : (h :: s.freeList).length - 1 - (j + 1) = s.freeList.length - 1 - j := by + simp only [List.length_cons]; omega + have hne_addr : + base + FibonacciHeap.space n_max + n_max + (s.freeList.length - 1 - j) ≠ + base + FibonacciHeap.space n_max + n_max + s.freeList.length := by omega + rw [hidx] + simp only [List.getElem_cons_succ, y, a4, a3, a2, a1, Data.set_mem, Data.write_mem, + Function.update_of_ne hne_addr] + exact (hmem1_ge _ (by omega)).trans (hfree j hj) + · have hp_y : potential y = FibonacciHeap.potential y_fib := by + unfold potential + rw [FibonacciHeap.potential_supported hreg_fib, Data.comap_extend f42_injective] + rw [hp_y] + unfold potential + omega + +/-! ## Unified dispatcher and `RelSubroutineDS` certificate -/ + +/-- Unified dispatcher `dispatchCode` implements all five `MinPQ` operations within +`charge n_max e o` amortized instructions over `Basis.exact`. -/ +theorem dispatchCode_exec_raw {n_max U V w base : ℕ} + {e : Cslib.Algorithms.Lean.MinPQ.State n_max} {x : Data rMinPQ} + (o : Cslib.Algorithms.Lean.MinPQ.Op n_max) (hr : Rep n_max U V w base e x) + (hv : (Cslib.Algorithms.Lean.MinPQ.spec n_max U V).valid e o) + (harg : rawArgMatch o x.regs) : + ∃ y s' a m, Exec w dispatchCode x y m ∧ + (Cslib.Algorithms.Lean.MinPQ.spec n_max U V).step e o s' a ∧ + decode (n_max := n_max) (U := U) (V := V) o y.regs = some a ∧ + Rep n_max U V w base s' y ∧ + Modifies (Block.mk base (space n_max)).footprint x.mem y.mem ∧ + m + potential y ≤ charge n_max e o + potential x := by + obtain ⟨s, rfl, hrep⟩ := hr + have hz0 : x.regs zeroP = 0 := hrep.zeroP_eq + obtain ⟨hstep_s, _⟩ := + Cslib.Algorithms.Lean.MinPQ.stepAlloc_refines_spec (U := U) (V := V) hrep.2.2.1 o hv + cases o with + | isEmpty => + obtain ⟨ht2, ht1, ht0⟩ := harg + have c2 : (Cond.eq tag2 zeroP).Holds x := by simp only [Cond.Holds, hz0]; exact ht2 + have c1 : (Cond.eq tag1 zeroP).Holds x := by simp only [Cond.Holds, hz0]; exact ht1 + have c0 : (Cond.eq tag0 zeroP).Holds x := by simp only [Cond.Holds, hz0]; exact ht0 + obtain ⟨y, t, ⟨m, hm, he⟩, hdec, hrep_y, hmod, hpot⟩ := isEmptyCode_exec hrep + refine ⟨y, _, _, m + 1 + 1 + 1, + Exec.branch_pos c2 (Exec.branch_pos c1 (Exec.branch_pos c0 he)), + hstep_s, hdec, ⟨_, rfl, hrep_y⟩, hmod, ?_⟩ + simp only [charge, tEmpty] + omega + | get h => + obtain ⟨ht2, ht1, ht0, hv_reg⟩ := harg + have c2 : (Cond.eq tag2 zeroP).Holds x := by simp only [Cond.Holds, hz0]; exact ht2 + have c1 : (Cond.eq tag1 zeroP).Holds x := by simp only [Cond.Holds, hz0]; exact ht1 + have c0 : ¬(Cond.eq tag0 zeroP).Holds x := by simp only [Cond.Holds, hz0]; exact ht0 + obtain ⟨y, t, ⟨m, hm, he⟩, hdec, hrep_y, hmod, hpot⟩ := getCode_exec h hrep hv_reg + refine ⟨y, _, _, m + 2 + 1 + 1, + Exec.branch_pos c2 (Exec.branch_pos c1 (Exec.branch_neg c0 he)), + hstep_s, hdec, ⟨_, rfl, hrep_y⟩, hmod, ?_⟩ + simp only [charge, tGet] + omega + | insert u v => + obtain ⟨ht2, ht1, ht0, hk_reg, hval_reg⟩ := harg + have c2 : (Cond.eq tag2 zeroP).Holds x := by simp only [Cond.Holds, hz0]; exact ht2 + have c1 : ¬(Cond.eq tag1 zeroP).Holds x := by simp only [Cond.Holds, hz0]; exact ht1 + have c0 : (Cond.eq tag0 zeroP).Holds x := by simp only [Cond.Holds, hz0]; exact ht0 + obtain ⟨y, t, ⟨m, hm, he⟩, hdec, hrep_y, hmod, hpot⟩ := + insertCode_exec hrep hv hk_reg hval_reg + refine ⟨y, _, _, m + 1 + 2 + 1, + Exec.branch_pos c2 (Exec.branch_neg c1 (Exec.branch_pos c0 he)), + hstep_s, hdec, ⟨_, rfl, hrep_y⟩, hmod, ?_⟩ + simp only [charge, tIns] + omega + | decreaseKey h u' => + obtain ⟨ht2, ht1, ht0, hv_reg, hk_reg⟩ := harg + have c2 : (Cond.eq tag2 zeroP).Holds x := by simp only [Cond.Holds, hz0]; exact ht2 + have c1 : ¬(Cond.eq tag1 zeroP).Holds x := by simp only [Cond.Holds, hz0]; exact ht1 + have c0 : ¬(Cond.eq tag0 zeroP).Holds x := by simp only [Cond.Holds, hz0]; exact ht0 + obtain ⟨y, t, ⟨m, hm, he⟩, hdec, hrep_y, hmod, hpot⟩ := + decreaseKeyCode_exec h u' hrep hv hv_reg hk_reg + refine ⟨y, _, _, m + 2 + 2 + 1, + Exec.branch_pos c2 (Exec.branch_neg c1 (Exec.branch_neg c0 he)), + hstep_s, hdec, ⟨_, rfl, hrep_y⟩, hmod, ?_⟩ + simp only [charge, tDec] + omega + | extractMin => + have c2 : ¬(Cond.eq tag2 zeroP).Holds x := by simp only [Cond.Holds, hz0]; exact harg + obtain ⟨y, t, ⟨m, hm, he⟩, hdec, hrep_y, hmod, hpot⟩ := extractMinCode_exec hrep + refine ⟨y, _, _, m + 2, Exec.branch_neg c2 he, hstep_s, hdec, ⟨_, rfl, hrep_y⟩, hmod, ?_⟩ + simp only [charge, tExt] + omega + +theorem dispatchCode_exec {n_max U V w base : ℕ} + {e : Cslib.Algorithms.Lean.MinPQ.State n_max} {x : Data rMinPQ} + (o : Cslib.Algorithms.Lean.MinPQ.Op n_max) (hr : Rep n_max U V w base e x) + (hv : (Cslib.Algorithms.Lean.MinPQ.spec n_max U V).valid e o) + (harg : argMatch U o x.regs) : + ∃ y s' a m, Exec w dispatchCode x y m ∧ + (Cslib.Algorithms.Lean.MinPQ.spec n_max U V).step e o s' a ∧ + decode (n_max := n_max) (U := U) (V := V) o y.regs = some a ∧ + Rep n_max U V w base s' y ∧ + Modifies (Block.mk base (space n_max)).footprint x.mem y.mem ∧ + m + potential y ≤ charge n_max e o + potential x := by + obtain ⟨s, rfl, hrep⟩ := hr + exact dispatchCode_exec_raw o ⟨s, rfl, hrep⟩ hv (rawArgMatch_of_argMatch hrep hv harg) + +/-- Unified standalone and inline-subroutine relational certificate for the key–value +Fibonacci heap with handles over `Basis.exact`. -/ +def relSubroutineDS {n_max U V w base : ℕ} + (hS : Fits w (base + space n_max)) (hU : Fits w U) (hV : Fits w V) : + RelSubroutineDS subroutineCode (Cslib.Algorithms.Lean.MinPQ.spec n_max U V) w base ![n_max] + (Cslib.Algorithms.Lean.MinPQ.empty n_max) where + space := space n_max + spanTime := 15 + setupTime := 18 + initPotential := 0 + charge := charge n_max + rep := Rep n_max U V w base + potential := potential + encode := encode U + argMatch := argMatch U + decode := decode + bounded := fun ⟨_, _, h⟩ => h.1 + arguments_fit o hv i _ := arguments_fit hS hU hV o hv i + encode_matches := encode_matches + rep_supported := Rep.supported + potential_supported hreg _ _ := potential_supported hreg + clear_nodup := by decide + params_not_clear := by decide + incCur_eval := incCur_eval + span_exec := allocSpan_exec hS + setup_exec := initSetup_exec + step_exec := dispatchCode_exec + +/-! ## Standalone `Basis.standard` `DynamicProgram` and `Stateful.RelAmortized` realizations -/ + +/-- Standalone `DynamicProgram` realization of the key–value Fibonacci heap over +`Basis.standard`. -/ +def relDynamicProgram {n_max U V w base : ℕ} + (hS : Fits w (base + space n_max)) (hU : Fits w U) (hV : Fits w V) : + DynamicProgram (Cslib.Algorithms.Lean.MinPQ.spec n_max U V) rMinPQ := + (relSubroutineDS (n_max := n_max) (U := U) (V := V) hS hU hV).toStandard.toDynamicProgram + +/-- Standalone `RelDynamicCertificate` for the key–value Fibonacci heap over `Basis.standard`. -/ +def relCertificate {n_max U V w base : ℕ} + (hS : Fits w (base + space n_max)) (hU : Fits w U) (hV : Fits w V) : + RelDynamicCertificate (relDynamicProgram hS hU hV) w := + (relSubroutineDS hS hU hV).toStandard.toRelCertificate + +/-- Standalone `Stateful.RelAmortized` certificate for the key–value Fibonacci heap over +`Basis.standard`, charging `+ 1` per operation for the trailing `halt` instruction. -/ +theorem relAmortized {n_max U V w base : ℕ} + (hS : Fits w (base + space n_max)) (hU : Fits w U) (hV : Fits w V) : + Stateful.RelAmortized (relCertificate hS hU hV).realization + DynamicProgram.OperationTrace.instructions + (fun _ x => potential x) + (fun s o => charge n_max s o + 1) := + (relSubroutineDS hS hU hV).toStandard.toRelAmortized + +/-- Adaptive-client amortized execution bound for any demonically admissible `MinPQ` client `p` +over `Basis.standard`. -/ +theorem run_client_amortized {n_max U V w base : ℕ} + (hS : Fits w (base + space n_max)) (hU : Fits w U) (hV : Fits w V) + {α : Type*} (p : (Cslib.Algorithms.Lean.MinPQ.spec n_max U V).Client α) + {s : Cslib.Algorithms.Lean.MinPQ.State n_max} {x : Data rMinPQ} + (hr : Rep n_max U V w base s x) + (hv : (Cslib.Algorithms.Lean.MinPQ.spec n_max U V).DemonicValid s p) : + ∃ a y traces s' tr, + Stateful.Executes ((relDynamicProgram hS hU hV).ExecOp w) p x a y traces ∧ + (Cslib.Algorithms.Lean.MinPQ.spec n_max U V).ClientRun s p a s' tr ∧ + Rep n_max U V w base s' y ∧ + traces.length = tr.length ∧ + Stateful.total DynamicProgram.OperationTrace.instructions traces + potential y ≤ + (Cslib.Algorithms.Lean.MinPQ.spec n_max U V).totalCost + (fun st o => charge n_max st o + 1) s tr + potential x := + (relSubroutineDS hS hU hV).toStandard.run_client_amortized p hr hv + +/-! ## Standalone end-to-end `Basis.exact` flat-program operation-sequence theorems -/ + +/-- Compiled flat `Program .exact rMinPQ` for `O(1)` fresh zeroed-memory initialization. -/ +def initFreshProg : Program .exact rMinPQ := subroutineCode.initFreshProg + +/-- Compiled flat `Program .exact rMinPQ` for `O(n_max)` dirty-memory reusable initialization. -/ +def initClearProg : Program .exact rMinPQ := subroutineCode.initClearProg + +/-- Compiled flat `Program .exact rMinPQ` serving all five priority-queue operations. -/ +def opProg : Program .exact rMinPQ := subroutineCode.opProg + +/-- Initial register assignment supplying `n_max` in `nReg` (`R[0]`), `base` in `outerFrontierReg` +(`R[1]`), and `0` elsewhere. -/ +def initRegs (n_max base : ℕ) : Fin rMinPQ → ℕ := + fun i => if i = nReg then n_max else if i = outerFrontierReg then base else 0 + +/-- Deliver operation `o`'s 3-bit tag into `tag2..tag0` (`R[17..19]`), handle/key arguments into +`argV, argK` (`R[20..21]`), and satellite value into `inValReg` (`R[46]`), leaving all other +registers unchanged. -/ +def prepareRegs {n_max : ℕ} (o : Cslib.Algorithms.Lean.MinPQ.Op n_max) + (regs : Fin rMinPQ → ℕ) : Fin rMinPQ → ℕ := + fun i => + if i = tag2 ∨ i = tag1 ∨ i = tag0 ∨ i = argV ∨ i = argK ∨ i = inValReg then + rawEncode o i + else + regs i + +/-- Count `isEmpty` and `get` queries in an operation sequence. -/ +def numQueries {n_max : ℕ} : List (Cslib.Algorithms.Lean.MinPQ.Op n_max) → ℕ + | [] => 0 + | .isEmpty :: ops | .get _ :: ops => 1 + numQueries ops + | _ :: ops => numQueries ops + +/-- Count `insert` operations in an operation sequence. -/ +def numInserts {n_max : ℕ} : List (Cslib.Algorithms.Lean.MinPQ.Op n_max) → ℕ + | [] => 0 + | .insert _ _ :: ops => 1 + numInserts ops + | _ :: ops => numInserts ops + +/-- Count `decreaseKey` operations in an operation sequence. -/ +def numDecreaseKeys {n_max : ℕ} : List (Cslib.Algorithms.Lean.MinPQ.Op n_max) → ℕ + | [] => 0 + | .decreaseKey _ _ :: ops => 1 + numDecreaseKeys ops + | _ :: ops => numDecreaseKeys ops + +/-- Count `extractMin` operations in an operation sequence. -/ +def numExtractMins {n_max : ℕ} : List (Cslib.Algorithms.Lean.MinPQ.Op n_max) → ℕ + | [] => 0 + | .extractMin :: ops => 1 + numExtractMins ops + | _ :: ops => numExtractMins ops + +/-- Sum of `Nat.log 2 (MinPQ.size s) + 1` over all `extractMin` steps along a relational +transcript `tr` starting from state `s`, where `MinPQ.size s` is the number of stored elements +immediately before each `extractMin`. -/ +def extractLogSum {n_max U V : ℕ} : + Cslib.Algorithms.Lean.MinPQ.State n_max → + (Cslib.Algorithms.Lean.MinPQ.spec n_max U V).Transcript → ℕ + | _, [] => 0 + | s, e :: tr => + (match e.op with + | .extractMin => Nat.log 2 (Cslib.Algorithms.Lean.MinPQ.size s) + 1 + | _ => 0) + extractLogSum e.nextState tr + +theorem extractLogSum_le_numExtractMins {n_max U V : ℕ} : + ∀ (tr : (Cslib.Algorithms.Lean.MinPQ.spec n_max U V).Transcript) + (s : Cslib.Algorithms.Lean.MinPQ.State n_max), + extractLogSum s tr ≤ numExtractMins tr.ops * (Nat.log 2 n_max + 1) + | [], _ => Nat.zero_le _ + | ⟨o, res, s₁⟩ :: tr, s => by + have ih := extractLogSum_le_numExtractMins tr s₁ + have hlog : Nat.log 2 (Cslib.Algorithms.Lean.MinPQ.size s) ≤ Nat.log 2 n_max := + Nat.log_mono_right (Cslib.Algorithms.Lean.MinPQ.size_le s) + cases o with + | extractMin => + simp only [extractLogSum, Stateful.RelSpec.Transcript.ops_cons, numExtractMins, + Nat.add_mul, Nat.one_mul] + omega + | _ => + simp only [extractLogSum, Stateful.RelSpec.Transcript.ops_cons, numExtractMins, + Nat.zero_add] + omega + +/-- Sequential flat-program execution of `ops` by repeated calls to `opProg`: each call prepares +input registers via `prepareRegs`, runs `opProg` from `pc = 0` to `halt`, decodes a valid +`MinPQ.spec` transition `(s₁, a)`, modifies memory only within `[base, base + space n_max)`, +records the resulting transcript `tr`, and accumulates total instructions executed (including +each trailing `halt`). -/ +inductive RunsOps {n_max U V : ℕ} (w base : ℕ) : + Cslib.Algorithms.Lean.MinPQ.State n_max → Data rMinPQ → + List (Cslib.Algorithms.Lean.MinPQ.Op n_max) → + (Cslib.Algorithms.Lean.MinPQ.spec n_max U V).Transcript → + Cslib.Algorithms.Lean.MinPQ.State n_max → Data rMinPQ → ℕ → Prop + | nil {s x} : RunsOps w base s x [] [] s x 0 + | cons {s s₁ s₂ x o a ops tr pc y z steps rest} : + Runs w opProg ⟨0, ⟨prepareRegs o x.regs, x.mem⟩⟩ ⟨pc, y⟩ steps → + (∃ out, step w opProg ⟨pc, y⟩ = .halt out) → + (Cslib.Algorithms.Lean.MinPQ.spec n_max U V).step s o s₁ a → + decode (n_max := n_max) (U := U) (V := V) o y.regs = some a → + Modifies (Block.mk base (space n_max)).footprint x.mem y.mem → + RunsOps w base s₁ y ops tr s₂ z rest → + RunsOps w base s x (o :: ops) (⟨o, a, s₁⟩ :: tr) s₂ z ((steps + 1) + rest) + +private theorem step_program_halt {w : ℕ} (c : Code .exact rMinPQ) (out : Fin rMinPQ) + (y : Data rMinPQ) : step w (c.program out) ⟨0 + c.size, y⟩ = .halt (y.regs out) := + step_halt (by + simp only [Code.program, Nat.zero_add, + List.getElem?_append_right (Code.length_assemble c 0).le, + Code.length_assemble, Nat.sub_self, List.getElem?_cons_zero]) + +private theorem seq_runsOps {n_max U V w base : ℕ} + (hS : Fits w (base + space n_max)) (hU : Fits w U) (hV : Fits w V) : + ∀ (ops : List (Cslib.Algorithms.Lean.MinPQ.Op n_max)) + {s : Cslib.Algorithms.Lean.MinPQ.State n_max} {x : Data rMinPQ}, + Rep n_max U V w base s x → + (Cslib.Algorithms.Lean.MinPQ.spec n_max U V).ValidOpSeq s ops → + ∃ tr s_fin z total, + RunsOps (U := U) (V := V) w base s x ops tr s_fin z total ∧ + tr.ops = ops ∧ + (Cslib.Algorithms.Lean.MinPQ.spec n_max U V).ValidTranscript s tr s_fin ∧ + Rep n_max U V w base s_fin z ∧ + total + potential z ≤ + 421 * (numQueries ops + numInserts ops + numDecreaseKeys ops + + extractLogSum s tr) + potential x + | [], s, x, hr, _ => + ⟨[], s, x, 0, .nil, rfl, .nil (D := Cslib.Algorithms.Lean.MinPQ.spec n_max U V) s, hr, + by simp only [numQueries, numInserts, numDecreaseKeys, extractLogSum]; omega⟩ + | o :: ops, s, x, hr, ⟨hv_o, hv_rest⟩ => by + let x' : Data rMinPQ := ⟨prepareRegs o x.regs, x.mem⟩ + have hbx : x.Bounded w := hr.choose_spec.2.1 + have henc_eq : encode U o = rawEncode o := encode_eq_rawEncode_of_rep hr hv_o + have hb' : x'.Bounded w := by + refine ⟨fun i => ?_, hbx.2⟩ + change Fits w (if _ then rawEncode o i else x.regs i) + split_ifs + · rw [← henc_eq] + exact arguments_fit hS hU hV o hv_o i + · exact hbx.1 i + have hreg' : ∀ i, inputReg i = false → x'.regs i = x.regs i := fun i hi => by + have hne : ¬(i = tag2 ∨ i = tag1 ∨ i = tag0 ∨ i = argV ∨ i = argK ∨ i = inValReg) := by + rintro (rfl | rfl | rfl | rfl | rfl | rfl) <;> exact absurd hi (by decide) + simp only [x', prepareRegs, hne, ↓reduceIte] + have hr' : Rep n_max U V w base s x' := hr.supported hreg' (fun _ _ => rfl) hb' + have hpot' : potential x' = potential x := potential_supported hreg' + have harg' : rawArgMatch o x'.regs := by + cases o with + | isEmpty => exact ⟨rfl, rfl, rfl⟩ + | get _ => exact ⟨rfl, rfl, Nat.one_ne_zero, rfl⟩ + | insert _ _ => exact ⟨rfl, Nat.one_ne_zero, rfl, rfl, rfl⟩ + | decreaseKey _ _ => exact ⟨rfl, Nat.one_ne_zero, Nat.one_ne_zero, rfl, rfl⟩ + | extractMin => exact Nat.one_ne_zero + obtain ⟨y, s₁, a, m, he_op, hstep, hdec, hr_y, hmod, hpot_m⟩ := + dispatchCode_exec_raw o hr' hv_o harg' + rw [hpot'] at hpot_m + have hruns : Runs w opProg ⟨0, x'⟩ ⟨0 + dispatchCode.size, y⟩ m := + he_op.runs (pc := 0) (p := opProg) (Program.Contains.self _).append_left + have hhalt : step w opProg ⟨0 + dispatchCode.size, y⟩ = .halt (y.regs outFlag) := + step_program_halt dispatchCode outFlag y + obtain ⟨tr, s_fin, z, rest, hrest, hops, htr, hr_z, hpot_rest⟩ := + seq_runsOps hS hU hV ops hr_y (hv_rest s₁ a hstep) + refine ⟨⟨o, a, s₁⟩ :: tr, s_fin, z, (m + 1) + rest, + .cons hruns ⟨_, hhalt⟩ hstep hdec hmod hrest, + congrArg (o :: ·) hops, .cons ⟨o, a, s₁⟩ hv_o hstep htr, hr_z, ?_⟩ + cases o <;> + simp only [charge, tEmpty, tGet, tIns, tDec, tExt, + numQueries, numInserts, numDecreaseKeys, extractLogSum] at hpot_m ⊢ <;> + omega + +private theorem initRegs_bounded {n_max base w : ℕ} (hS : Fits w (base + space n_max)) + {mem₀ : ℕ → ℕ} (hmem : ∀ a, Fits w (mem₀ a)) : + (⟨initRegs n_max base, mem₀⟩ : Data rMinPQ).Bounded w := by + refine ⟨fun i => ?_, hmem⟩ + simp only [initRegs] + split_ifs + · exact hS.of_le (by have := space_lower n_max; omega) + · exact hS.of_le (Nat.le_add_right base _) + · exact hS.of_le (Nat.zero_le _) + +/-- Standalone `Basis.exact` operation-sequence theorem with `O(1)` fresh initialization on zeroed +`[base, base + 11 * n_max + 1)` and per-extraction `O(log n + 1)` amortized cost in the current +number of stored elements `n = MinPQ.size s`. -/ +theorem run_ops_exact_fresh {n_max U V w base : ℕ} + (hS : Fits w (base + space n_max)) (hU : Fits w U) (hV : Fits w V) + {mem₀ : ℕ → ℕ} (hmem : ∀ a, Fits w (mem₀ a)) + (hzero : ∀ a ∈ (Block.mk base (space n_max)).footprint, mem₀ a = 0) + (ops : List (Cslib.Algorithms.Lean.MinPQ.Op n_max)) + (hv : (Cslib.Algorithms.Lean.MinPQ.spec n_max U V).ValidOpSeq + (Cslib.Algorithms.Lean.MinPQ.empty n_max) ops) : + ∃ pc₀ x₀ t_init tr s_fin z t_ops, + Runs w initFreshProg ⟨0, ⟨initRegs n_max base, mem₀⟩⟩ ⟨pc₀, x₀⟩ t_init ∧ + (∃ out, step w initFreshProg ⟨pc₀, x₀⟩ = .halt out) ∧ + Modifies (Block.mk base (space n_max)).footprint mem₀ x₀.mem ∧ + RunsOps (U := U) (V := V) w base (Cslib.Algorithms.Lean.MinPQ.empty n_max) x₀ ops tr + s_fin z t_ops ∧ + tr.ops = ops ∧ + (Cslib.Algorithms.Lean.MinPQ.spec n_max U V).ValidTranscript + (Cslib.Algorithms.Lean.MinPQ.empty n_max) tr s_fin ∧ + t_init + 1 ≤ 421 ∧ + t_ops ≤ + 421 * (numQueries ops + numInserts ops + numDecreaseKeys ops + + extractLogSum (Cslib.Algorithms.Lean.MinPQ.empty n_max) tr) ∧ + t_ops ≤ + 421 * (numQueries ops + numInserts ops + numDecreaseKeys ops + + numExtractMins ops * (Nat.log 2 n_max + 1)) := by + let d₀ : Data rMinPQ := ⟨initRegs n_max base, mem₀⟩ + have hfr_eq : d₀.regs outerFrontierReg = base := by + simp only [d₀, initRegs, show outerFrontierReg ≠ nReg by decide, ↓reduceIte] + have hpar : ∀ j : Fin 1, d₀.regs (![nReg] j) = ![n_max] j := fun j => by + fin_cases j; rfl + obtain ⟨x₀, ⟨t_init, ht_init, he_init⟩, _, hr₀, hpot₀, hmod₀⟩ := + (relSubroutineDS hS hU hV).initFresh_exec (initRegs_bounded hS hmem) hfr_eq hpar hzero + have hruns₀ : Runs w initFreshProg ⟨0, d₀⟩ + ⟨0 + subroutineCode.initFresh.size, x₀⟩ t_init := + he_init.runs (pc := 0) (p := initFreshProg) (Program.Contains.self _).append_left + have hhalt₀ : step w initFreshProg ⟨0 + subroutineCode.initFresh.size, x₀⟩ = + .halt (x₀.regs outFlag) := + step_program_halt subroutineCode.initFresh outFlag x₀ + obtain ⟨tr, s_fin, z, t_ops, hruns_ops, hops, htr, _, hbound⟩ := + seq_runsOps hS hU hV ops hr₀ hv + have hlog_le := extractLogSum_le_numExtractMins tr (Cslib.Algorithms.Lean.MinPQ.empty n_max) + rw [hops] at hlog_le + refine ⟨_, x₀, t_init, tr, s_fin, z, t_ops, hruns₀, ⟨_, hhalt₀⟩, hmod₀, hruns_ops, + hops, htr, ?_, ?_, ?_⟩ + · change t_init ≤ 33 at ht_init + omega + · change potential x₀ ≤ 0 at hpot₀ + omega + · change potential x₀ ≤ 0 at hpot₀ + omega + +/-- Standalone `Basis.exact` operation-sequence theorem with `O(n_max)` reusable initialization on +arbitrary `w`-bounded memory `mem₀` and per-extraction `O(log n + 1)` amortized cost in the current +number of stored elements `n = MinPQ.size s`. -/ +theorem run_ops_exact_reusable {n_max U V w base : ℕ} + (hS : Fits w (base + space n_max)) (hU : Fits w U) (hV : Fits w V) + {mem₀ : ℕ → ℕ} (hmem : ∀ a, Fits w (mem₀ a)) + (ops : List (Cslib.Algorithms.Lean.MinPQ.Op n_max)) + (hv : (Cslib.Algorithms.Lean.MinPQ.spec n_max U V).ValidOpSeq + (Cslib.Algorithms.Lean.MinPQ.empty n_max) ops) : + ∃ pc₀ x₀ t_init tr s_fin z t_ops, + Runs w initClearProg ⟨0, ⟨initRegs n_max base, mem₀⟩⟩ ⟨pc₀, x₀⟩ t_init ∧ + (∃ out, step w initClearProg ⟨pc₀, x₀⟩ = .halt out) ∧ + Modifies (Block.mk base (space n_max)).footprint mem₀ x₀.mem ∧ + RunsOps (U := U) (V := V) w base (Cslib.Algorithms.Lean.MinPQ.empty n_max) x₀ ops tr + s_fin z t_ops ∧ + tr.ops = ops ∧ + (Cslib.Algorithms.Lean.MinPQ.spec n_max U V).ValidTranscript + (Cslib.Algorithms.Lean.MinPQ.empty n_max) tr s_fin ∧ + t_init + 1 ≤ 421 * (n_max + 1) ∧ + t_ops ≤ + 421 * (numQueries ops + numInserts ops + numDecreaseKeys ops + + extractLogSum (Cslib.Algorithms.Lean.MinPQ.empty n_max) tr) ∧ + t_ops ≤ + 421 * (numQueries ops + numInserts ops + numDecreaseKeys ops + + numExtractMins ops * (Nat.log 2 n_max + 1)) := by + let d₀ : Data rMinPQ := ⟨initRegs n_max base, mem₀⟩ + have hfr_eq : d₀.regs outerFrontierReg = base := by + simp only [d₀, initRegs, show outerFrontierReg ≠ nReg by decide, ↓reduceIte] + have hpar : ∀ j : Fin 1, d₀.regs (![nReg] j) = ![n_max] j := fun j => by + fin_cases j; rfl + obtain ⟨x₀, ⟨t_init, ht_init, he_init⟩, _, hr₀, hpot₀, hmod₀⟩ := + (relSubroutineDS hS hU hV).initClear_exec (initRegs_bounded hS hmem) hfr_eq hpar + have hruns₀ : Runs w initClearProg ⟨0, d₀⟩ + ⟨0 + subroutineCode.initClear.size, x₀⟩ t_init := + he_init.runs (pc := 0) (p := initClearProg) (Program.Contains.self _).append_left + have hhalt₀ : step w initClearProg ⟨0 + subroutineCode.initClear.size, x₀⟩ = + .halt (x₀.regs outFlag) := + step_program_halt subroutineCode.initClear outFlag x₀ + obtain ⟨tr, s_fin, z, t_ops, hruns_ops, hops, htr, _, hbound⟩ := + seq_runsOps hS hU hV ops hr₀ hv + have hlog_le := extractLogSum_le_numExtractMins tr (Cslib.Algorithms.Lean.MinPQ.empty n_max) + rw [hops] at hlog_le + refine ⟨_, x₀, t_init, tr, s_fin, z, t_ops, hruns₀, ⟨_, hhalt₀⟩, hmod₀, hruns_ops, + hops, htr, ?_, ?_, ?_⟩ + · change t_init ≤ 33 + 4 * (11 * n_max + 1) + 6 at ht_init + omega + · change potential x₀ ≤ 0 at hpot₀ + omega + · change potential x₀ ≤ 0 at hpot₀ + omega + +end Cslib.Algorithms.WordRAM.PriorityQueue.FibonacciHeap.MinPQ diff --git a/Complexity/Algorithms/WordRAM/PriorityQueue/FibonacciHeap/Program.lean b/Complexity/Algorithms/WordRAM/PriorityQueue/FibonacciHeap/Program.lean index f891a9fe6..4fe3a7979 100644 --- a/Complexity/Algorithms/WordRAM/PriorityQueue/FibonacciHeap/Program.lean +++ b/Complexity/Algorithms/WordRAM/PriorityQueue/FibonacciHeap/Program.lean @@ -672,20 +672,33 @@ def tGet (_n : ℕ) : ℕ := 16 def tIns (_n : ℕ) : ℕ := 130 /-- Amortized instruction bound for `decreaseKey` (including dispatch and cascading cuts). -/ def tDec (_n : ℕ) : ℕ := 340 -/-- Amortized instruction bound for `extractMin` (`O(log n)`). -/ +/-- Amortized instruction bound for `extractMin` (`O(log n + 1)`). -/ def tExt (n : ℕ) : ℕ := 420 * (Nat.log 2 n + 1) -/-- Per-operation amortized instruction charge for `FibonacciHeap`. -/ -def charge (n : ℕ) (_s : IndexedMinPQ.State n) : IndexedMinPQ.Op n → ℕ +/-- Per-operation amortized instruction charge for `FibonacciHeap`, charging +`tExt (IndexedMinPQ.dom s).card` on `.extractMin` in terms of the current number of stored +elements `(IndexedMinPQ.dom s).card`. -/ +def charge (n : ℕ) (s : IndexedMinPQ.State n) : IndexedMinPQ.Op n → ℕ | .isEmpty => tEmpty n | .getKey _ => tGet n | .insert _ _ => tIns n | .decreaseKey _ _ => tDec n - | .extractMin => tExt n + | .extractMin => tExt (IndexedMinPQ.dom s).card + +/-- The current number of stored elements `(IndexedMinPQ.dom s).card` is at most `n`, so +`charge n s .extractMin ≤ tExt n`. -/ +theorem charge_extractMin_le_tExt (n : ℕ) (s : IndexedMinPQ.State n) : + charge n s .extractMin ≤ tExt n := by + simp only [charge, tExt] + have hcard : (IndexedMinPQ.dom s).card ≤ n := by + simpa using (IndexedMinPQ.dom s).card_le_univ + have hlog : Nat.log 2 (IndexedMinPQ.dom s).card ≤ Nat.log 2 n := + Nat.log_mono_right hcard + omega /-! ## Exact-ALU step rules, footprint framing, and `RawState.ofData` update lemmas -/ -theorem execWithin_exactAdd {w : ℕ} {x : Data rFib} {d i j : Fin rFib} +theorem execWithin_exactAdd {w r : ℕ} {x : Data r} {d i j : Fin r} (hfit : Fits w (x.regs i + x.regs j)) : ExecWithin (B := .exact) w (.op (.exactBin .add d i j)) x (x.set d (x.regs i + x.regs j)) 1 := @@ -694,7 +707,7 @@ theorem execWithin_exactAdd {w : ℕ} {x : Data rFib} {d i j : Fin rFib} simp only [BinOp.evalExact, Option.bind_some] exact Data.put_of_fits hfit) -theorem execWithin_exactSub {w : ℕ} {x : Data rFib} {d i j : Fin rFib} +theorem execWithin_exactSub {w r : ℕ} {x : Data r} {d i j : Fin r} (hle : x.regs j ≤ x.regs i) (hfit : Fits w (x.regs i - x.regs j)) : ExecWithin (B := .exact) w (.op (.exactBin .sub d i j)) x (x.set d (x.regs i - x.regs j)) 1 := diff --git a/ComplexityTests/Abstracts/FibonacciHeapTests.lean b/ComplexityTests/Abstracts/FibonacciHeapTests.lean index 55799938e..64150fe04 100644 --- a/ComplexityTests/Abstracts/FibonacciHeapTests.lean +++ b/ComplexityTests/Abstracts/FibonacciHeapTests.lean @@ -13,86 +13,112 @@ import ComplexityAbstracts.FibonacciHeapAbstract Instantiates both `fibonacciHeap_amortized` and `fibonacciHeap_amortized_reusable` on a concrete non-empty operation sequence exercising all five priority-queue operations and checks memory -confinement outside `[base, base + 9 n + 1)`. +confinement outside `[base, base + 11 * n_max + 1)`. -/ namespace ComplexityTests.Abstracts.FibonacciHeapTests open ComplexityAbstracts.FibonacciHeapAbstract -/-- Concrete 6-operation sequence on `Fin 4` exercising all five `Op` constructors. -/ -def sampleOps : List (Op 4) := - [.insert 2 30, .insert 1 30, .decreaseKey 2 10, .getKey 2, .extractMin, .isEmpty] +/-- Concrete 6-operation sequence on capacity `n_max = 2` exercising all five `Op` constructors: +two insertions fill all `2` handles with key `30` (and satellite values `100`, `200`), so handle +`0` is guaranteed to hold key `30` regardless of which handle the relational specification +allocated first. -/ +def sampleOps : List (Op 2) := + [.insert 30 100, .insert 30 200, .decreaseKey 0 10, .get 0, .extractMin, .isEmpty] -theorem sampleOps_valid : ValidSeq 50 (emptyQueue 4) sampleOps := by - refine ⟨⟨rfl, by decide⟩, ⟨⟨by decide, by decide⟩, ⟨⟨⟨30, rfl, by decide⟩, by decide⟩, - ⟨trivial, ⟨trivial, ⟨trivial, trivial⟩⟩⟩⟩⟩⟩ +theorem sampleOps_valid : ValidSeq 50 500 (emptyQueue 2) sampleOps := by + refine ⟨⟨by decide, by decide, by decide⟩, ?_⟩ + rintro q₁ _ ⟨h₁, _, rfl, rfl⟩ + fin_cases h₁ + · refine ⟨⟨by decide, by decide, by decide⟩, ?_⟩ + rintro q₂ _ ⟨h₂, hh₂, rfl, rfl⟩ + fin_cases h₂ + · simp [Function.update] at hh₂ + · refine ⟨⟨30, 100, rfl, by decide⟩, ?_⟩ + exact fun _ _ _ => + ⟨trivial, fun _ _ _ => ⟨trivial, fun _ _ _ => ⟨trivial, fun _ _ _ => trivial⟩⟩⟩ + · refine ⟨⟨by decide, by decide, by decide⟩, ?_⟩ + rintro q₂ _ ⟨h₂, hh₂, rfl, rfl⟩ + fin_cases h₂ + · refine ⟨⟨30, 200, rfl, by decide⟩, ?_⟩ + exact fun _ _ _ => + ⟨trivial, fun _ _ _ => ⟨trivial, fun _ _ _ => ⟨trivial, fun _ _ _ => trivial⟩⟩⟩ + · simp [Function.update] at hh₂ private theorem unchanged_trans {base len : ℕ} {m₁ m₂ m₃ : ℕ → ℕ} (h₁ : UnchangedOutside base len m₁ m₂) (h₂ : UnchangedOutside base len m₂ m₃) : UnchangedOutside base len m₁ m₃ := fun a ha => (h₂ a ha).trans (h₁ a ha) -private theorem runsOps_unchanged {r n w : ℕ} {p : Program r} {base len : ℕ} - {q : Queue n} {reg reg' : Fin r → ℕ} {mem mem' : ℕ → ℕ} {ops : List (Op n)} {total : ℕ} - (h : RunsOps w p base len q reg mem ops reg' mem' total) : +private theorem runsOps_unchanged {r n_max w : ℕ} {p : Program r} {base len : ℕ} + {q q' : Queue n_max} {reg reg' : Fin r → ℕ} {mem mem' : ℕ → ℕ} {ops : List (Op n_max)} + {total extLog : ℕ} + (h : RunsOps w p base len q reg mem ops q' reg' mem' total extLog) : UnchangedOutside base len mem mem' := by induction h with | nil => intro _ _; rfl - | cons _ _ _ hmod _ ih => exact unchanged_trans hmod ih + | cons _ _ _ _ hmod _ ih => exact unchanged_trans hmod ih /-- Non-vacuity of `fibonacciHeap_amortized` (`O(1)` fresh initialization): runs `sampleOps` on -`[10, 47)` at word size `10` with sentinel `77` outside `[10, 47)`, preserving `mem_f 9 = 77` and -`mem_f 47 = 77`. -/ +`[10, 33)` (`11 * 2 + 1 = 23` words) at word size `10` with sentinel `77` outside `[10, 33)`, +preserving `mem_f 9 = 77` and `mem_f 33 = 77`. -/ example : - ∃ r, ∃ p_init p_op : Program r, ∃ K t₀ t_init reg_f mem_f t_ops, - let mem₀ : ℕ → ℕ := fun a => if a < 10 ∨ 47 ≤ a then 77 else 0 - Runs 10 p_init ⟨0, initRegs 4 10, mem₀⟩ t₀ t_init ∧ + ∃ r, ∃ p_init p_op : Program r, ∃ K t₀ t_init q_f reg_f mem_f t_ops extLog, + let mem₀ : ℕ → ℕ := fun a => if a < 10 ∨ 33 ≤ a then 77 else 0 + Runs 10 p_init ⟨0, initRegs 2 10, mem₀⟩ t₀ t_init ∧ (∃ out, step 10 p_init t₀ = .halt out) ∧ - RunsOps 10 p_op 10 37 (emptyQueue 4) t₀.reg t₀.mem sampleOps reg_f mem_f t_ops ∧ + RunsOps 10 p_op 10 23 (emptyQueue 2) t₀.reg t₀.mem sampleOps q_f reg_f mem_f t_ops extLog ∧ mem_f 9 = 77 ∧ - mem_f 47 = 77 ∧ + mem_f 33 = 77 ∧ t_init + 1 ≤ K ∧ - t_ops ≤ K * 8 := by + t_ops ≤ K * (5 + extLog) ∧ + extLog ≤ 2 := by obtain ⟨r, p_init, p_op, K, hfib⟩ := fibonacciHeap_amortized - let mem₀ : ℕ → ℕ := fun a => if a < 10 ∨ 47 ≤ a then 77 else 0 + let mem₀ : ℕ → ℕ := fun a => if a < 10 ∨ 33 ≤ a then 77 else 0 have hmem : ∀ a, mem₀ a < 2 ^ 10 := fun a => by dsimp only [mem₀]; split_ifs <;> decide - have hzero : ∀ a, 10 ≤ a → a < 10 + (9 * 4 + 1) → mem₀ a = 0 := fun a h1 h2 => by + have hzero : ∀ a, 10 ≤ a → a < 10 + (11 * 2 + 1) → mem₀ a = 0 := fun a h1 h2 => by dsimp only [mem₀] - have : ¬(a < 10 ∨ 47 ≤ a) := by omega + have : ¬(a < 10 ∨ 33 ≤ a) := by omega simp [this] - obtain ⟨t₀, t_init, reg_f, mem_f, t_ops, hr₀, hh₀, hu₀, hruns, ht_init, ht_ops⟩ := - hfib 4 50 10 10 mem₀ sampleOps (by decide) (by decide) hmem hzero sampleOps_valid + obtain ⟨t₀, t_init, q_f, reg_f, mem_f, t_ops, extLog, + hr₀, hh₀, hu₀, hruns, ht_init, ht_ops, hext⟩ := + hfib 2 50 500 10 10 mem₀ sampleOps (by decide) (by decide) (by decide) hmem hzero + sampleOps_valid have hu_all := unchanged_trans hu₀ (runsOps_unchanged hruns) have hm9 : mem_f 9 = 77 := hu_all 9 (Or.inl (by decide)) - have hm47 : mem_f 47 = 77 := hu_all 47 (Or.inr (by decide)) - have hlog : Nat.log 2 4 = 2 := Nat.log_eq_of_pow_le_of_lt_pow (by decide) (by decide) - simp only [sampleOps, numQueries, numInserts, numDecreaseKeys, numExtractMins, hlog] at ht_ops - exact ⟨r, p_init, p_op, K, t₀, t_init, reg_f, mem_f, t_ops, hr₀, hh₀, hruns, - hm9, hm47, ht_init, ht_ops⟩ + have hm33 : mem_f 33 = 77 := hu_all 33 (Or.inr (by decide)) + have hlog : Nat.log 2 2 = 1 := Nat.log_eq_of_pow_le_of_lt_pow (by decide) (by decide) + simp only [sampleOps, numQueries, numInserts, numDecreaseKeys, numExtractMins, hlog] + at ht_ops hext + exact ⟨r, p_init, p_op, K, t₀, t_init, q_f, reg_f, mem_f, t_ops, extLog, + hr₀, hh₀, hruns, hm9, hm33, ht_init, ht_ops, hext⟩ -/-- Non-vacuity of `fibonacciHeap_amortized_reusable` (`O(n)` clear initialization): runs -`sampleOps` on `[10, 47)` starting from dirty memory `mem₀ = fun _ => 99` everywhere, preserving -`mem_f 9 = 99` and `mem_f 47 = 99`. -/ +/-- Non-vacuity of `fibonacciHeap_amortized_reusable` (`O(n_max + 1)` clear initialization): runs +`sampleOps` on `[10, 33)` starting from dirty memory `mem₀ = fun _ => 99` everywhere, preserving +`mem_f 9 = 99` and `mem_f 33 = 99`. -/ example : - ∃ r, ∃ p_init p_op : Program r, ∃ K t₀ t_init reg_f mem_f t_ops, - Runs 10 p_init ⟨0, initRegs 4 10, fun _ => 99⟩ t₀ t_init ∧ + ∃ r, ∃ p_init p_op : Program r, ∃ K t₀ t_init q_f reg_f mem_f t_ops extLog, + Runs 10 p_init ⟨0, initRegs 2 10, fun _ => 99⟩ t₀ t_init ∧ (∃ out, step 10 p_init t₀ = .halt out) ∧ - RunsOps 10 p_op 10 37 (emptyQueue 4) t₀.reg t₀.mem sampleOps reg_f mem_f t_ops ∧ + RunsOps 10 p_op 10 23 (emptyQueue 2) t₀.reg t₀.mem sampleOps q_f reg_f mem_f t_ops extLog ∧ mem_f 9 = 99 ∧ - mem_f 47 = 99 ∧ - t_init + 1 ≤ K * 5 ∧ - t_ops ≤ K * 8 := by + mem_f 33 = 99 ∧ + t_init + 1 ≤ K * 3 ∧ + t_ops ≤ K * (5 + extLog) ∧ + extLog ≤ 2 := by obtain ⟨r, p_init, p_op, K, hfib⟩ := fibonacciHeap_amortized_reusable - obtain ⟨t₀, t_init, reg_f, mem_f, t_ops, hr₀, hh₀, hu₀, hruns, ht_init, ht_ops⟩ := - hfib 4 50 10 10 (fun _ => 99) sampleOps (by decide) (by decide) (fun _ => by decide) - sampleOps_valid + obtain ⟨t₀, t_init, q_f, reg_f, mem_f, t_ops, extLog, + hr₀, hh₀, hu₀, hruns, ht_init, ht_ops, hext⟩ := + hfib 2 50 500 10 10 (fun _ => 99) sampleOps (by decide) (by decide) (by decide) + (fun _ => by decide) sampleOps_valid have hu_all := unchanged_trans hu₀ (runsOps_unchanged hruns) have hm9 : mem_f 9 = 99 := hu_all 9 (Or.inl (by decide)) - have hm47 : mem_f 47 = 99 := hu_all 47 (Or.inr (by decide)) - have hlog : Nat.log 2 4 = 2 := Nat.log_eq_of_pow_le_of_lt_pow (by decide) (by decide) - simp only [sampleOps, numQueries, numInserts, numDecreaseKeys, numExtractMins, hlog] at ht_ops - exact ⟨r, p_init, p_op, K, t₀, t_init, reg_f, mem_f, t_ops, hr₀, hh₀, hruns, - hm9, hm47, ht_init, ht_ops⟩ + have hm33 : mem_f 33 = 99 := hu_all 33 (Or.inr (by decide)) + have hlog : Nat.log 2 2 = 1 := Nat.log_eq_of_pow_le_of_lt_pow (by decide) (by decide) + simp only [sampleOps, numQueries, numInserts, numDecreaseKeys, numExtractMins, hlog] + at ht_ops hext + exact ⟨r, p_init, p_op, K, t₀, t_init, q_f, reg_f, mem_f, t_ops, extLog, + hr₀, hh₀, hruns, hm9, hm33, ht_init, ht_ops, hext⟩ end ComplexityTests.Abstracts.FibonacciHeapTests diff --git a/ComplexityTests/WordRAMFibonacciHeap.lean b/ComplexityTests/WordRAMFibonacciHeap.lean index 1c3f0e812..d393f96bd 100644 --- a/ComplexityTests/WordRAMFibonacciHeap.lean +++ b/ComplexityTests/WordRAMFibonacciHeap.lean @@ -270,4 +270,123 @@ example : rw [hp₀, Nat.add_zero] at hcost exact ⟨y₀, y, traces, hr₀, hp₀, hex, hcost⟩ +/-! ### 5. Key–value Fibonacci heap with handles (`FibonacciHeap.MinPQ`) -/ + +/-- Tabulate registers `0..rMinPQ - 1` (`48`) and memory `0..memCap - 1` into flat lists between +interpreter calls for `FibonacciHeap.MinPQ`. -/ +def compactDataMinPQ (memCap : ℕ) (d : Data MinPQ.rMinPQ) : Option (Data MinPQ.rMinPQ) := do + let rs ← forceList ((List.finRange MinPQ.rMinPQ).map d.regs) + let ms ← forceList ((List.range memCap).map d.mem) + let tail ← forceNat (d.mem memCap) + pure + { regs := fun i => rs.getD i.val 0 + mem := fun a => if a < memCap then ms.getD a 0 else tail } + +/-- Run a single `Cslib.Algorithms.Lean.MinPQ.Op` via `MinPQ.dispatchCode.eval`. -/ +def stepEvalMinPQ {n_max : ℕ} (w fuel : ℕ) (dt : Data MinPQ.rMinPQ × ℕ) + (o : Cslib.Algorithms.Lean.MinPQ.Op n_max) : + Option (Data MinPQ.rMinPQ × ℕ) := do + let (d', t) ← MinPQ.dispatchCode.eval w fuel ⟨MinPQ.prepareRegs o dt.1.regs, dt.1.mem⟩ + let dc ← compactDataMinPQ 80 d' + pure (dc, dt.2 + t) + +/-- Concrete interpreter test for `FibonacciHeap.MinPQ`: +* `insert 30 300` allocates handle `0`, `insert 20 200` allocates handle `1`; +* `get 0` returns key `30` and satellite value `300`; +* `decreaseKey 0 5` lowers handle `0`'s key to `5`; +* `extractMin` returns `(u = 5, v = 300, h = 0)` and pushes handle `0` onto the free stack; +* `insert 15 150` reuses handle `0` from the free stack; +* two more `extractMin`s return `(15, 150, 0)` and `(20, 200, 1)`, followed by `none`. -/ +example : + (do + let d0 ← MinPQ.subroutineCode.initFresh.eval 12 16 ⟨MinPQ.initRegs 4 10, fun _ => 0⟩ + let d1 ← stepEvalMinPQ (n_max := 4) 12 16 d0 (.insert 30 300) + let d2 ← stepEvalMinPQ (n_max := 4) 12 16 d1 (.insert 20 200) + let d3 ← stepEvalMinPQ (n_max := 4) 12 16 d2 (.get 0) + let d4 ← stepEvalMinPQ (n_max := 4) 12 16 d3 (.decreaseKey 0 5) + let d5 ← stepEvalMinPQ (n_max := 4) 12 16 d4 .extractMin + let d6 ← stepEvalMinPQ (n_max := 4) 12 16 d5 (.insert 15 150) + let d7 ← stepEvalMinPQ (n_max := 4) 12 16 d6 .extractMin + let d8 ← stepEvalMinPQ (n_max := 4) 12 16 d7 .extractMin + let d9 ← stepEvalMinPQ (n_max := 4) 12 16 d8 .extractMin + pure + [d1.1.regs MinPQ.outV, d2.1.regs MinPQ.outV, + d3.1.regs MinPQ.outFlag, d3.1.regs MinPQ.outK, d3.1.regs MinPQ.outValReg, + d5.1.regs MinPQ.outFlag, d5.1.regs MinPQ.outK, d5.1.regs MinPQ.outValReg, + d5.1.regs MinPQ.outV, + d6.1.regs MinPQ.outV, + d7.1.regs MinPQ.outK, d7.1.regs MinPQ.outValReg, d7.1.regs MinPQ.outV, + d8.1.regs MinPQ.outK, d8.1.regs MinPQ.outValReg, d8.1.regs MinPQ.outV, + d9.1.regs MinPQ.outFlag]) = + some [0, 1, 1, 30, 300, 1, 5, 300, 0, 0, 15, 150, 0, 20, 200, 1, 0] := by + decide +kernel + +/-- Non-vacuity of `FibonacciHeap.relAmortized` over `Basis.standard`. -/ +example : + let hS : Fits 10 (8 + MinPQ.space 4) := by unfold MinPQ.space Fits; decide + let hU : Fits 10 50 := by decide + let hV : Fits 10 500 := by decide + Stateful.RelAmortized (relCertificate hS hU hV).realization + DynamicProgram.OperationTrace.instructions + (fun _ x => MinPQ.potential x) + (fun s o => MinPQ.charge 4 s o + 1) := + relAmortized _ _ _ + +/-- Lift a single `Cslib.Algorithms.Lean.MinPQ.Op` into an adaptive `Client`. -/ +abbrev callMinPQOp (o : Cslib.Algorithms.Lean.MinPQ.Op 4) : + (Cslib.Algorithms.Lean.MinPQ.spec 4 50 500).Client + ((Cslib.Algorithms.Lean.MinPQ.spec 4 50 500).Result o) := + PFunctor.FreeM.lift (P := (Cslib.Algorithms.Lean.MinPQ.spec 4 50 500).signature) o + +/-- Concrete adaptive client that inserts `(30, 300)`, receives the allocated handle `h`, lowers +`h`'s key to `5`, and extracts the minimum entry. -/ +def sampleMinPQClient : + (Cslib.Algorithms.Lean.MinPQ.spec 4 50 500).Client (Option (ℕ × ℕ × Fin 4)) := do + let hOpt ← callMinPQOp (.insert 30 300) + match hOpt with + | some h => + callMinPQOp (.decreaseKey h 5) + callMinPQOp .extractMin + | none => + callMinPQOp .extractMin + +/-- Non-vacuity of `MinPQ.relSubroutineDS`, `MinPQ.Rep`, and `MinPQ.run_client_amortized` for an +adaptive handle-passing client over `Basis.standard`. -/ +example : + let hS : Fits 10 (8 + MinPQ.space 4) := by unfold MinPQ.space Fits; decide + let hU : Fits 10 50 := by decide + let hV : Fits 10 500 := by decide + ∃ a y₀ y traces s' tr, + MinPQ.Rep 4 50 500 10 8 (Cslib.Algorithms.Lean.MinPQ.empty 4) y₀ ∧ + MinPQ.potential y₀ = 0 ∧ + Stateful.Executes ((relDynamicProgram hS hU hV).ExecOp 10) sampleMinPQClient y₀ + a y traces ∧ + (Cslib.Algorithms.Lean.MinPQ.spec 4 50 500).ClientRun + (Cslib.Algorithms.Lean.MinPQ.empty 4) sampleMinPQClient a s' tr ∧ + Stateful.total DynamicProgram.OperationTrace.instructions traces + MinPQ.potential y ≤ + (Cslib.Algorithms.Lean.MinPQ.spec 4 50 500).totalCost + (fun st o => MinPQ.charge 4 st o + 1) + (Cslib.Algorithms.Lean.MinPQ.empty 4) tr := by + intro hS hU hV + let x₀ : Data MinPQ.rMinPQ := ⟨MinPQ.initRegs 4 8, fun _ => 0⟩ + have hx₀ : x₀.Bounded 10 := + ⟨fun i => by fin_cases i <;> decide, fun _ => show Fits 10 0 by decide⟩ + have hfr : x₀.regs MinPQ.outerFrontierReg = 8 := by decide + have hpar : ∀ j : Fin 1, x₀.regs (![MinPQ.nReg] j) = ![4] j := fun j => by fin_cases j; rfl + have hz : ∀ a ∈ (Block.mk 8 (MinPQ.space 4)).footprint, x₀.mem a = 0 := fun _ _ => rfl + obtain ⟨y₀, _, _, hr₀, hpot₀, _⟩ := + (relSubroutineDS hS hU hV).initFresh_exec hx₀ hfr hpar hz + have hp₀ : MinPQ.potential y₀ = 0 := Nat.eq_zero_of_le_zero hpot₀ + have hv : (Cslib.Algorithms.Lean.MinPQ.spec 4 50 500).DemonicValid + (Cslib.Algorithms.Lean.MinPQ.empty 4) sampleMinPQClient := by + refine .liftBind _ _ ⟨by decide, by decide, by decide⟩ ?_ + rintro s₁ _ ⟨h, _, rfl, rfl⟩ + refine .liftBind _ _ ⟨30, 300, by simp [Function.update], by decide⟩ ?_ + rintro s₂ _ _ + exact .liftBind _ _ trivial fun s₃ a _ => .pure s₃ a + obtain ⟨a, y, traces, s', tr, hex, hrun, _, _, hcost⟩ := + MinPQ.run_client_amortized hS hU hV sampleMinPQClient hr₀ hv + rw [hp₀, Nat.add_zero] at hcost + exact ⟨a, y₀, y, traces, s', tr, hr₀, hp₀, hex, hrun, hcost⟩ + end WordRAMFibonacciHeapTests diff --git a/abstracts/ComplexityAbstracts/FibonacciHeapAbstract.lean b/abstracts/ComplexityAbstracts/FibonacciHeapAbstract.lean index 4170ec8ac..8b75a789e 100644 --- a/abstracts/ComplexityAbstracts/FibonacciHeapAbstract.lean +++ b/abstracts/ComplexityAbstracts/FibonacciHeapAbstract.lean @@ -11,30 +11,34 @@ import ComplexityAbstracts.FibonacciHeapBridge /-! # Fibonacci heaps: amortized priority queues on the word RAM -An indexed min-priority queue maintains a subset of the handles `0, …, n - 1` (the type `Fin n`), -each associated with a natural-number key at most `K_max`. It supports membership/key queries -(`isEmpty`, `getKey`), inserting an absent handle (`insert`), lowering the key of a present handle -(`decreaseKey`), and removing the present `(handle, key)` pair of minimum key (`extractMin`), -breaking equal-key ties by smallest handle index. Fredman and Tarjan's Fibonacci heap supports -queries in `O(1)` time, `insert` and `decreaseKey` in `O(1)` amortized time, and `extractMin` in -`O(log n)` amortized time. +A min-priority queue with capacity `n_max`, key bound `U`, and value bound `V` maintains at most +`n_max` elements, where each stored element is a key–value pair `(u, v)` with `u < U` and `v < V` +identified by a handle `h : Fin n_max` allocated by the data structure. It supports emptiness and +handle-lookup queries (`isEmpty`, `get h`), inserting a new `(key, value)` pair (`insert u v`, which +allocates and returns an unused handle `h : Fin n_max`), lowering the key of a currently stored +handle (`decreaseKey h u'`), and removing a stored element of minimum key (`extractMin`, which +returns `some (u, v, h)` or `none` when empty). Fredman and Tarjan's Fibonacci heap supports queries +in `O(1)` time, `insert` and `decreaseKey` in `O(1)` amortized time, and `extractMin` in +`O(log n + 1)` amortized time, where `n` is the current number of stored elements. The theorems below state this guarantee on the word RAM over the exact basis of operations, inside a -caller-chosen contiguous block `[base, base + 9 n + 1)` of `9 n + 1` words: - -* `fibonacciHeap_amortized` initializes an empty heap on zeroed `[base, base + 9 n + 1)` in `O(1)` - instructions and executes any valid sequence of `m_q` queries, `m_ins` insertions, `m_dec` key - decreases, and `m_ext` minimum extractions in - `O(m_q + m_ins + m_dec + m_ext (log n + 1))` total instructions; +caller-chosen contiguous block `[base, base + 11 * n_max + 1)` of `11 * n_max + 1` words: + +* `fibonacciHeap_amortized` initializes an empty heap on zeroed `[base, base + 11 * n_max + 1)` in + `O(1)` instructions and executes any admissible sequence `ops` of `m_q` queries, `m_ins` + insertions, `m_dec` key decreases, and `m_ext` minimum extractions in + `O(m_q + m_ins + m_dec + ∑ (log n_t + 1))` total instructions, where `n_t = size q_t` is the + number of stored elements immediately before the `t`-th `extractMin` (bounded in turn by + `m_ext * (log n_max + 1)`); * `fibonacciHeap_amortized_reusable` initializes an empty heap on arbitrary `w`-bit memory in - `O(n + 1)` instructions and achieves the same amortized operation bound. + `O(n_max + 1)` instructions and achieves the same amortized operation bound. In both theorems, neither initialization nor any operation modifies memory outside -`[base, base + 9 n + 1)`. Every concept in the statements—the priority queue, its tie-breaking -minimum, admissible operation sequences, the exact word RAM, the register calling convention and -memory confinement—is defined from scratch in this file. Lean checks the proofs through -`FibonacciHeapBridge`, and `#check_abstract` verifies mechanically that no definition or theorem -statement here depends on implementation modules. +`[base, base + 11 * n_max + 1)`. Every concept in the statements—the priority queue, its relational +transition specification, admissible operation sequences, the exact word RAM, the register calling +convention and memory confinement—is defined from scratch in this file. Lean checks the proofs +through `FibonacciHeapBridge`, and `#check_abstract` verifies mechanically that no definition or +theorem statement here depends on implementation modules. ## References @@ -47,131 +51,133 @@ statement here depends on implementation modules. namespace ComplexityAbstracts.FibonacciHeapAbstract -/-! ## Indexed priority queues +/-! ## Priority queues with handles -A priority-queue state `q : Queue n` maps each handle `v : Fin n` to `none` when `v` is absent and -to `some k` when `v` is present with key `k`. To make `extractMin` a deterministic function of `q` -alone (independent of insertion history), `PrioLE` orders `(handle, key)` pairs lexicographically by -`(key, handle)`. An operation sequence is admissible (`ValidSeq K_max`) when every `insert v k` acts -on an absent handle with `k ≤ K_max` and every `decreaseKey v k` lowers the current key of a present -handle with `k ≤ K_max`. +A priority-queue state `q : Queue n_max` maps each handle `h : Fin n_max` to `none` when `h` is +currently unused and to `some (u, v)` when `h` is present with key `u` and satellite value `v`. The +current number of stored elements is `size q`. Because the data structure chooses which unused +handle to allocate on `insert u v` and which minimum-key entry to remove on `extractMin` when keys +tie, transitions are specified relationally by `StepRel`. An operation sequence is admissible +(`ValidSeq U V`) when every operation is valid in the current state and the remaining suffix is +admissible after every valid relational outcome. -/ -/-- Operations on an indexed min-priority queue over handles `Fin n` and natural-number keys. -/ -inductive Op (n : ℕ) +/-- Operations on a min-priority queue with capacity `n_max`, natural-number keys, and +natural-number satellite values. -/ +inductive Op (n_max : ℕ) | isEmpty - | getKey (v : Fin n) - | insert (v : Fin n) (k : ℕ) - | decreaseKey (v : Fin n) (k : ℕ) + | get (h : Fin n_max) + | insert (u v : ℕ) + | decreaseKey (h : Fin n_max) (u' : ℕ) | extractMin -/-- Abstract state of an indexed min-priority queue on `Fin n`: `none` when absent, `some k` when -present with key `k`. -/ -abbrev Queue (n : ℕ) := Fin n → Option ℕ - -/-- The empty priority queue on `Fin n`. -/ -def emptyQueue (n : ℕ) : Queue n := fun _ => none - -/-- Lexicographic priority order on `(handle, key)` pairs: smaller key first, breaking equal-key -ties by smaller handle index. -/ -abbrev PrioLE {n : ℕ} (a b : Fin n × ℕ) : Prop := - a.2 < b.2 ∨ (a.2 = b.2 ∧ (a.1 : ℕ) ≤ (b.1 : ℕ)) +/-- Abstract state of a min-priority queue with capacity `n_max`: `none` when handle `h` is unused, +`some (u, v)` when `h` stores key `u` and satellite value `v`. -/ +abbrev Queue (n_max : ℕ) := Fin n_max → Option (ℕ × ℕ) -/-- Smaller of two `(handle, key)` pairs under `PrioLE`. -/ -def chooseMin {n : ℕ} (a b : Fin n × ℕ) : Fin n × ℕ := - if PrioLE a b then a else b +/-- The empty priority queue of capacity `n_max`. -/ +def emptyQueue (n_max : ℕ) : Queue n_max := fun _ => none -/-- Minimum `(handle, key)` pair in a list under `PrioLE`, or `none` on the empty list. -/ -def foldMin {n : ℕ} : List (Fin n × ℕ) → Option (Fin n × ℕ) - | [] => none - | a :: l => - match foldMin l with - | none => some a - | some b => some (chooseMin a b) +/-- Current number of elements stored in `q`. -/ +def size {n_max : ℕ} (q : Queue n_max) : ℕ := + (Finset.univ.filter fun h => (q h).isSome).card -/-- Present `(handle, key)` pair minimizing `(key, handle)` in `q`, or `none` when `q` is empty. -/ -def findMin {n : ℕ} (q : Queue n) : Option (Fin n × ℕ) := - foldMin ((List.finRange n).filterMap fun v => (q v).map (v, ·)) +/-- Handle `h` is present in `q` with `(u, v)` and `u` is a minimum key among all stored elements. +-/ +def IsMinEntry {n_max : ℕ} (q : Queue n_max) (h : Fin n_max) (u v : ℕ) : Prop := + q h = some (u, v) ∧ ∀ h' u' v', q h' = some (u', v') → u ≤ u' /-- Return type of each priority-queue operation. -/ -def OpResult {n : ℕ} : Op n → Type +def OpResult {n_max : ℕ} : Op n_max → Type | .isEmpty => Bool - | .getKey _ => Option ℕ - | .insert _ _ => Unit + | .get _ => Option (ℕ × ℕ) + | .insert _ _ => Option (Fin n_max) | .decreaseKey _ _ => Unit - | .extractMin => Option (Fin n × ℕ) - -/-- Abstract state transition and return value of operation `o` on queue `q`. -/ -def stepOp {n : ℕ} (q : Queue n) : (o : Op n) → Queue n × OpResult o - | .isEmpty => (q, decide (∀ v, q v = none)) - | .getKey v => (q, q v) - | .insert v k => (Function.update q v (some k), ()) - | .decreaseKey v k => (Function.update q v (some k), ()) - | .extractMin => - match findMin q with - | none => (q, none) - | some (u, k) => (Function.update q u none, some (u, k)) - -/-- Precondition for a single operation under key bound `K_max`: `insert` requires an absent handle -and `k ≤ K_max`; `decreaseKey` requires a present handle whose key does not increase and -`k ≤ K_max`. -/ -def ValidOp {n : ℕ} (K_max : ℕ) (q : Queue n) : Op n → Prop - | .isEmpty | .getKey _ | .extractMin => True - | .insert v k => q v = none ∧ k ≤ K_max - | .decreaseKey v k => (∃ k₀, q v = some k₀ ∧ k ≤ k₀) ∧ k ≤ K_max - -/-- Sequential admissibility of an operation list starting from queue `q`. -/ -def ValidSeq {n : ℕ} (K_max : ℕ) : Queue n → List (Op n) → Prop + | .extractMin => Option (ℕ × ℕ × Fin n_max) + +/-- Relational transition and return value of operation `o` on queue `q`: `insert u v` allocates any +currently unused handle `h : Fin n_max` and stores `(u, v)` at `h`; `extractMin` removes and returns +any stored entry `(u, v, h)` of minimum key `u` (or `none` when `q` is empty). -/ +def StepRel {n_max : ℕ} (q : Queue n_max) : (o : Op n_max) → Queue n_max → OpResult o → Prop + | .isEmpty, q', res => q' = q ∧ res = decide (∀ h, q h = none) + | .get h, q', res => q' = q ∧ res = q h + | .insert u v, q', res => + ∃ h : Fin n_max, q h = none ∧ q' = Function.update q h (some (u, v)) ∧ res = some h + | .decreaseKey h u', q', res => + (∃ u₀ v, q h = some (u₀, v) ∧ q' = Function.update q h (some (u', v))) ∧ res = () + | .extractMin, q', res => + ((∀ h, q h = none) ∧ q' = q ∧ res = none) ∨ + (∃ h u v, IsMinEntry q h u v ∧ q' = Function.update q h none ∧ res = some (u, v, h)) + +/-- Precondition for a single operation under key bound `U` and value bound `V`: `insert u v` +requires `size q < n_max`, `u < U`, and `v < V`; `decreaseKey h u'` requires `h` to be present with +some current key `u₀ ≥ u'`. -/ +def ValidOp {n_max : ℕ} (U V : ℕ) (q : Queue n_max) : Op n_max → Prop + | .isEmpty | .get _ | .extractMin => True + | .insert u v => size q < n_max ∧ u < U ∧ v < V + | .decreaseKey h u' => ∃ u₀ v, q h = some (u₀, v) ∧ u' ≤ u₀ + +/-- Sequential admissibility of an operation list starting from queue `q`: each operation is valid +in the current state, and the remaining suffix is valid after every relational transition `StepRel` +that the operation may take. -/ +def ValidSeq {n_max : ℕ} (U V : ℕ) : Queue n_max → List (Op n_max) → Prop | _, [] => True - | q, o :: ops => ValidOp K_max q o ∧ ValidSeq K_max (stepOp q o).1 ops + | q, o :: ops => ValidOp U V q o ∧ ∀ q' res, StepRel q o q' res → ValidSeq U V q' ops -/-- Number of `isEmpty` and `getKey` queries in `ops`. -/ -def numQueries {n : ℕ} : List (Op n) → ℕ +/-- Number of `isEmpty` and `get` queries in `ops`. -/ +def numQueries {n_max : ℕ} : List (Op n_max) → ℕ | [] => 0 | .isEmpty :: ops => 1 + numQueries ops - | .getKey _ :: ops => 1 + numQueries ops + | .get _ :: ops => 1 + numQueries ops | .insert _ _ :: ops => numQueries ops | .decreaseKey _ _ :: ops => numQueries ops | .extractMin :: ops => numQueries ops /-- Number of `insert` operations in `ops`. -/ -def numInserts {n : ℕ} : List (Op n) → ℕ +def numInserts {n_max : ℕ} : List (Op n_max) → ℕ | [] => 0 | .isEmpty :: ops => numInserts ops - | .getKey _ :: ops => numInserts ops + | .get _ :: ops => numInserts ops | .insert _ _ :: ops => 1 + numInserts ops | .decreaseKey _ _ :: ops => numInserts ops | .extractMin :: ops => numInserts ops /-- Number of `decreaseKey` operations in `ops`. -/ -def numDecreaseKeys {n : ℕ} : List (Op n) → ℕ +def numDecreaseKeys {n_max : ℕ} : List (Op n_max) → ℕ | [] => 0 | .isEmpty :: ops => numDecreaseKeys ops - | .getKey _ :: ops => numDecreaseKeys ops + | .get _ :: ops => numDecreaseKeys ops | .insert _ _ :: ops => numDecreaseKeys ops | .decreaseKey _ _ :: ops => 1 + numDecreaseKeys ops | .extractMin :: ops => numDecreaseKeys ops /-- Number of `extractMin` operations in `ops`. -/ -def numExtractMins {n : ℕ} : List (Op n) → ℕ +def numExtractMins {n_max : ℕ} : List (Op n_max) → ℕ | [] => 0 | .isEmpty :: ops => numExtractMins ops - | .getKey _ :: ops => numExtractMins ops + | .get _ :: ops => numExtractMins ops | .insert _ _ :: ops => numExtractMins ops | .decreaseKey _ _ :: ops => numExtractMins ops | .extractMin :: ops => 1 + numExtractMins ops +/-- Logarithmic cost factor `Nat.log 2 n + 1` in the current number of stored elements `n = size q` +for an `extractMin` step from queue `q`, and `0` for all other operations. -/ +def extractStepLog {n_max : ℕ} (q : Queue n_max) : Op n_max → ℕ + | .extractMin => Nat.log 2 (size q) + 1 + | _ => 0 + /-! ## Memory layout, calling convention and cost -The data structure occupies `9 n + 1` words at addresses `[base, base + 9 n + 1)`, assuming -`base + (9 n + 1) < 2 ^ w`, `K_max < 2 ^ w`, and every initial memory cell holds a `w`-bit word -(`< 2 ^ w`). Initialization (`p_init`) starts at `pc = 0` with `R[0] = n`, `R[1] = base`, and `0` in -all other registers (`initRegs`). Each subsequent operation call (`p_op`) starts at `pc = 0` after -writing the 3-bit operation tag and `(handle, key)` arguments into `R[17..21]` while leaving all -other registers unchanged (`prepareRegs`), runs to a `halt` instruction, and returns its observation -in `R[22..24]` (`decodeResult`), leaving every memory cell outside `[base, base + 9 n + 1)` -unchanged (`UnchangedOutside`). Every instruction costs one unit of time, each `halt` included. The -appendix defines the exact word RAM (`Program`, `State`, `step`, `Runs`) and the register and memory +The data structure occupies `11 * n_max + 1` words at addresses `[base, base + 11 * n_max + 1)`, +assuming `base + (11 * n_max + 1) < 2 ^ w`, `U < 2 ^ w`, `V < 2 ^ w`, and every initial memory cell +holds a `w`-bit word (`< 2 ^ w`). Initialization (`p_init`) starts at `pc = 0` with `R[0] = n_max`, +`R[1] = base`, and `0` in all other registers (`initRegs`). Each subsequent operation call (`p_op`) +starts at `pc = 0` after writing the 3-bit operation tag, handle/key arguments into `R[17..21]`, and +satellite value argument into `R[46]` while leaving all other registers unchanged (`prepareRegs`), +runs to a `halt` instruction, and returns its observation in `R[22..24]` and `R[47]` +(`decodeResult`), leaving every memory cell outside `[base, base + 11 * n_max + 1)` unchanged +(`UnchangedOutside`). Every instruction costs one unit of time, each `halt` included. The appendix +defines the exact word RAM (`Program`, `State`, `step`, `Runs`) and the register and memory conventions. -/ @@ -261,49 +267,55 @@ def step (w : ℕ) (p : Program r) (s : State r) : Outcome r := def Runs (w : ℕ) (p : Program r) : State r → State r → ℕ → Prop := Relation.RelatesInSteps fun s t => step w p s = .next t -/-! **Calling convention and memory confinement.** Registers `R[0..16]` hold persistent heap state -across calls (`p_init` reads `n` in `R[0]` and `base` in `R[1]`), `R[17..21]` carry each operation's -3-bit tag and `(handle, key)` arguments, `R[22..24]` return its result, and `R[25..]` are scratch +/-! **Calling convention and memory confinement.** Registers `R[0..16]` and `R[42..45]` hold +persistent heap and allocator state across calls (`p_init` reads `n_max` in `R[0]` and `base` in +`R[1]`), `R[17..21]` and `R[46]` carry each operation's 3-bit tag and `(handle, key, value)` +arguments, `R[22..24]` and `R[47]` return its result, and the remaining registers are scratch registers. -/ -/-- Initial register assignment for `p_init`: `R[0] = n`, `R[1] = base`, and `0` elsewhere. -/ -def initRegs (n base : ℕ) (i : Fin r) : ℕ := - if (i : ℕ) = 0 then n else if (i : ℕ) = 1 then base else 0 +/-- Initial register assignment for `p_init`: `R[0] = n_max`, `R[1] = base`, and `0` elsewhere. -/ +def initRegs (n_max base : ℕ) (i : Fin r) : ℕ := + if (i : ℕ) = 0 then n_max else if (i : ℕ) = 1 then base else 0 -/-- Write `(t₂, t₁, t₀, a, b)` into `R[17..21]`, leaving every other register unchanged. -/ -def setArgs (t₂ t₁ t₀ a b : ℕ) (reg : Fin r → ℕ) (i : Fin r) : ℕ := +/-- Write `(t₂, t₁, t₀, a, b)` into `R[17..21]` and `val` into `R[46]`, leaving every other register +unchanged. -/ +def setArgs (t₂ t₁ t₀ a b val : ℕ) (reg : Fin r → ℕ) (i : Fin r) : ℕ := if (i : ℕ) = 17 then t₂ else if (i : ℕ) = 18 then t₁ else if (i : ℕ) = 19 then t₀ else if (i : ℕ) = 20 then a else if (i : ℕ) = 21 then b + else if (i : ℕ) = 46 then val else reg i /-- Prepare registers before calling `p_op`: write operation `o`'s tag and arguments into -`R[17..21]` and leave all other registers intact. -/ -def prepareRegs {n : ℕ} : Op n → (Fin r → ℕ) → Fin r → ℕ - | .isEmpty, reg => setArgs 0 0 0 0 0 reg - | .getKey v, reg => setArgs 0 0 1 v 0 reg - | .insert v k, reg => setArgs 0 1 0 v k reg - | .decreaseKey v k, reg => setArgs 0 1 1 v k reg - | .extractMin, reg => setArgs 1 0 0 0 0 reg +`R[17..21]` and `R[46]`, leaving all other registers intact. -/ +def prepareRegs {n_max : ℕ} : Op n_max → (Fin r → ℕ) → Fin r → ℕ + | .isEmpty, reg => setArgs 0 0 0 0 0 0 reg + | .get h, reg => setArgs 0 0 1 h 0 0 reg + | .insert u v, reg => setArgs 0 1 0 0 u v reg + | .decreaseKey h u', reg => setArgs 0 1 1 h u' 0 reg + | .extractMin, reg => setArgs 1 0 0 0 0 0 reg /-- Read register `idx`, or `0` when `r ≤ idx`. -/ def regAt (reg : Fin r → ℕ) (idx : ℕ) : ℕ := if h : idx < r then reg ⟨idx, h⟩ else 0 -/-- Decode operation `o`'s return value from `R[22..24]`: `R[22]` is the boolean result for -`isEmpty` and the presence flag for `getKey`/`extractMin`, `R[23]` is the extracted handle index, -and `R[24]` is the queried or extracted key. -/ -def decodeResult {n : ℕ} : (o : Op n) → (Fin r → ℕ) → Option (OpResult o) +/-- Decode operation `o`'s return value from `R[22..24]` and `R[47]`: `R[22]` is the boolean result +for `isEmpty` and the presence flag for `get` and `extractMin`, `R[23]` is the allocated or +extracted handle index, `R[24]` is the queried or extracted key, and `R[47]` is the queried or +extracted satellite value. -/ +def decodeResult {n_max : ℕ} : (o : Op n_max) → (Fin r → ℕ) → Option (OpResult o) | .isEmpty, reg => some (decide (regAt reg 22 ≠ 0)) - | .getKey _, reg => - if regAt reg 22 = 0 then some none else some (some (regAt reg 24)) - | .insert _ _, _ => some () + | .get _, reg => + if regAt reg 22 = 0 then some none else some (some (regAt reg 24, regAt reg 47)) + | .insert _ _, reg => + if h : regAt reg 23 < n_max then some (some ⟨regAt reg 23, h⟩) else none | .decreaseKey _ _, _ => some () | .extractMin, reg => if regAt reg 22 = 0 then some none - else if h : regAt reg 23 < n then some (some (⟨regAt reg 23, h⟩, regAt reg 24)) + else if h : regAt reg 23 < n_max then + some (some (regAt reg 24, regAt reg 47, ⟨regAt reg 23, h⟩)) else none /-- Every memory address outside `[base, base + len)` retains its previous value. -/ @@ -312,48 +324,59 @@ def UnchangedOutside (base len : ℕ) (m m' : ℕ → ℕ) : Prop := /-! ## Multi-operation execution and main theorems -/ -/-- `RunsOps w p base len q reg mem ops reg' mem' total`: starting from abstract queue `q`, -registers `reg`, and memory `mem`, executing `ops` sequentially by preparing `R[17..21]` -(`prepareRegs`), running `p` from `pc = 0` to `halt`, decoding the exact `stepOp` return value -(`decodeResult`), and modifying memory only within `[base, base + len)` (`UnchangedOutside`) reaches -registers `reg'` and memory `mem'` in `total` instructions (each operation's `halt` included). -/ -inductive RunsOps {n : ℕ} (w : ℕ) (p : Program r) (base len : ℕ) : - Queue n → (Fin r → ℕ) → (ℕ → ℕ) → List (Op n) → (Fin r → ℕ) → (ℕ → ℕ) → ℕ → Prop - | nil {q reg mem} : RunsOps w p base len q reg mem [] reg mem 0 - | cons {q reg mem o ops t reg' mem' steps rest} : +/-- `RunsOps w p base len q reg mem ops q' reg' mem' total extLog`: starting from abstract queue +`q`, registers `reg`, and memory `mem`, executing `ops` sequentially by preparing `R[17..21]` and +`R[46]` (`prepareRegs`), running `p` from `pc = 0` to `halt`, advancing `q` to some next queue state +`q₁` with return value `res` satisfying `StepRel q o q₁ res` and `decodeResult o t.reg = some res`, +and modifying memory only within `[base, base + len)` (`UnchangedOutside`) reaches final queue `q'`, +registers `reg'`, and memory `mem'` in `total` instructions (each operation's `halt` included) with +cumulative extraction log cost `extLog` (the sum of `Nat.log 2 (size q_t) + 1` over all `extractMin` +steps along the execution). -/ +inductive RunsOps {n_max : ℕ} (w : ℕ) (p : Program r) (base len : ℕ) : + Queue n_max → (Fin r → ℕ) → (ℕ → ℕ) → List (Op n_max) → + Queue n_max → (Fin r → ℕ) → (ℕ → ℕ) → ℕ → ℕ → Prop + | nil {q reg mem} : RunsOps w p base len q reg mem [] q reg mem 0 0 + | cons {q q₁ q' reg mem o res ops t reg' mem' steps rest extRest} : Runs w p ⟨0, prepareRegs o reg, mem⟩ t steps → (∃ out, step w p t = .halt out) → - decodeResult o t.reg = some (stepOp q o).2 → + StepRel q o q₁ res → + decodeResult o t.reg = some res → UnchangedOutside base len mem t.mem → - RunsOps w p base len (stepOp q o).1 t.reg t.mem ops reg' mem' rest → - RunsOps w p base len q reg mem (o :: ops) reg' mem' ((steps + 1) + rest) + RunsOps w p base len q₁ t.reg t.mem ops q' reg' mem' rest extRest → + RunsOps w p base len q reg mem (o :: ops) q' reg' mem' + ((steps + 1) + rest) (extractStepLog q o + extRest) /-- **Fibonacci heap (`O(1)` fresh initialization).** There exist a register count `r`, flat programs `p_init` and `p_op` over `r` registers on the exact word RAM, and a constant `K` such that -for all `n`, `K_max`, `base`, `w`, `w`-bit memory `mem₀` zeroed on `[base, base + 9 n + 1)` with -`base + (9 n + 1) < 2 ^ w` and `K_max < 2 ^ w`, and every admissible sequence `ops` starting from -`emptyQueue n`: `p_init` halts in `t_init + 1 ≤ K` instructions without modifying memory outside -`[base, base + 9 n + 1)`, and `p_op` executes `ops` (with `m_q = numQueries ops`, -`m_ins = numInserts ops`, `m_dec = numDecreaseKeys ops`, and `m_ext = numExtractMins ops`) in -`t_ops ≤ K (m_q + m_ins + m_dec + m_ext (Nat.log 2 n + 1))` total instructions while producing every -exact `stepOp` result and leaving memory outside `[base, base + 9 n + 1)` unchanged. -/ +for all `n_max`, `U`, `V`, `base`, `w`, `w`-bit memory `mem₀` zeroed on +`[base, base + 11 * n_max + 1)` with `base + (11 * n_max + 1) < 2 ^ w`, `U < 2 ^ w`, `V < 2 ^ w`, +and every admissible sequence `ops` starting from `emptyQueue n_max`: `p_init` halts in +`t_init + 1 ≤ K` instructions without modifying memory outside `[base, base + 11 * n_max + 1)`, and +`p_op` executes `ops` (with `m_q = numQueries ops`, `m_ins = numInserts ops`, +`m_dec = numDecreaseKeys ops`, and `m_ext = numExtractMins ops`) in +`t_ops ≤ K * (m_q + m_ins + m_dec + extLog)` total instructions, where `extLog` is the sum of +`Nat.log 2 (size q_t) + 1` in the current number of stored elements `size q_t` over all `extractMin` +steps (satisfying `extLog ≤ m_ext * (Nat.log 2 n_max + 1)`), while producing valid relational +`StepRel` results and leaving memory outside `[base, base + 11 * n_max + 1)` unchanged. -/ theorem fibonacciHeap_amortized : ∃ r, ∃ p_init p_op : Program r, ∃ K, - ∀ (n K_max base w : ℕ) (mem₀ : ℕ → ℕ) (ops : List (Op n)), - base + (9 * n + 1) < 2 ^ w → - K_max < 2 ^ w → + ∀ (n_max U V base w : ℕ) (mem₀ : ℕ → ℕ) (ops : List (Op n_max)), + base + (11 * n_max + 1) < 2 ^ w → + U < 2 ^ w → + V < 2 ^ w → (∀ a, mem₀ a < 2 ^ w) → - (∀ a, base ≤ a → a < base + (9 * n + 1) → mem₀ a = 0) → - ValidSeq K_max (emptyQueue n) ops → - ∃ t₀ t_init reg_f mem_f t_ops, - Runs w p_init ⟨0, initRegs n base, mem₀⟩ t₀ t_init ∧ + (∀ a, base ≤ a → a < base + (11 * n_max + 1) → mem₀ a = 0) → + ValidSeq U V (emptyQueue n_max) ops → + ∃ t₀ t_init q_f reg_f mem_f t_ops extLog, + Runs w p_init ⟨0, initRegs n_max base, mem₀⟩ t₀ t_init ∧ (∃ out, step w p_init t₀ = .halt out) ∧ - UnchangedOutside base (9 * n + 1) mem₀ t₀.mem ∧ - RunsOps w p_op base (9 * n + 1) (emptyQueue n) t₀.reg t₀.mem ops reg_f mem_f t_ops ∧ + UnchangedOutside base (11 * n_max + 1) mem₀ t₀.mem ∧ + RunsOps w p_op base (11 * n_max + 1) (emptyQueue n_max) t₀.reg t₀.mem ops + q_f reg_f mem_f t_ops extLog ∧ t_init + 1 ≤ K ∧ t_ops ≤ - K * (numQueries ops + numInserts ops + numDecreaseKeys ops + - numExtractMins ops * (Nat.log 2 n + 1)) := + K * (numQueries ops + numInserts ops + numDecreaseKeys ops + extLog) ∧ + extLog ≤ numExtractMins ops * (Nat.log 2 n_max + 1) := FibonacciHeapBridge.amortized_fresh ⟨AluOp, .add, .sub, .mul, .div, .mod, .and, .or, .xor, .shl, .shr, @AluOp.rec, AluOp.eval, Instr, .const, .copy, .alu, .width, .load, .store, .jump, .branchEq, .branchLt, .halt, @@ -363,49 +386,74 @@ theorem fibonacciHeap_amortized : fun _ _ => True⟩ (by constructor <;> simp_all [step, ComplexityTests.Abstracts.WordRAMBridge.haltsVia, AluOp.eval]) - ⟨Op, .isEmpty, .getKey, .insert, .decreaseKey, .extractMin, @Op.rec, @chooseMin, @foldMin, - @findMin, fun q o => (stepOp q o).1, fun q o reg => decodeResult o reg = some (stepOp q o).2, + ⟨Op, .isEmpty, .get, .insert, .decreaseKey, .extractMin, @Op.rec, @size, @extractStepLog, + fun q o q' => ∃ res, StepRel q o q' res, + fun q o q' reg => ∃ res, StepRel q o q' res ∧ decodeResult o reg = some res, @ValidOp, @ValidSeq, @numQueries, @numInserts, @numDecreaseKeys, @numExtractMins, @initRegs, - @prepareRegs 42, @State.reg, @State.mem, @RunsOps⟩ - ⟨fun _ _ => ⟨fun h => by simp [chooseMin, h], fun h => by simp [chooseMin, h]⟩, - rfl, fun _ _ => rfl, fun _ => rfl, - fun _ => rfl, fun _ _ => rfl, fun _ _ _ => rfl, fun _ _ _ => rfl, - fun q => by simp only [stepOp]; cases findMin q <;> rfl, - fun _ _ h => congrArg some (decide_eq_decide.mpr h), - fun _ _ _ h => h, fun _ _ _ _ => rfl, fun _ _ _ _ => rfl, - fun q _ h => h.trans (congrArg some (by simp only [stepOp]; cases findMin q <;> rfl)), - fun _ _ _ h => h, fun _ _ _ h => h, fun _ _ _ h => h, + @prepareRegs 48, @State.reg, @State.mem, @RunsOps⟩ + ⟨fun _ => rfl, + fun _ => rfl, fun _ _ => rfl, fun _ _ _ => rfl, fun _ _ _ => rfl, fun _ => rfl, + fun _ => ⟨_, rfl, rfl⟩, + fun _ _ => ⟨_, rfl, rfl⟩, + fun _ _ _ h hnone => ⟨some h, h, hnone, rfl, rfl⟩, + fun _ _ _ u₀ v hq => ⟨(), ⟨u₀, v, hq, rfl⟩, rfl⟩, + fun _ hemp => ⟨none, Or.inl ⟨hemp, rfl, rfl⟩⟩, + fun _ h u v hq hmin => ⟨some (u, v, h), Or.inr ⟨h, u, v, ⟨hq, hmin⟩, rfl, rfl⟩⟩, + fun q _ h => ⟨decide (∀ h, q h = none), ⟨rfl, rfl⟩, congrArg some (decide_eq_decide.mpr h)⟩, + fun q h _ hd => ⟨q h, ⟨rfl, rfl⟩, hd⟩, + fun _ _ _ h _ hnone hv => + ⟨some h, ⟨h, hnone, rfl, rfl⟩, by + simp only [decodeResult, regAt, show (23 : ℕ) < 48 by decide, ↓reduceDIte, hv, + Fin.is_lt, Fin.eta]⟩, + fun _ _ _ u₀ v _ hq => ⟨(), ⟨⟨u₀, v, hq, rfl⟩, rfl⟩, rfl⟩, + fun _ _ hemp hflag => + ⟨none, Or.inl ⟨hemp, rfl, rfl⟩, by + simp only [decodeResult, regAt, show (22 : ℕ) < 48 by decide, ↓reduceDIte, hflag, + ↓reduceIte]⟩, + fun _ h u v _ hq hmin hflag hv hk hval => + ⟨some (u, v, h), Or.inr ⟨h, u, v, ⟨hq, hmin⟩, rfl, rfl⟩, by + simp only [decodeResult, regAt, show (22 : ℕ) < 48 by decide, + show (23 : ℕ) < 48 by decide, show (24 : ℕ) < 48 by decide, + show (47 : ℕ) < 48 by decide, ↓reduceDIte, hflag, ↓reduceIte, + hv, hk, hval, Fin.is_lt, Fin.eta]⟩, + fun _ _ _ h => h, + fun _ _ _ h => h, + fun _ _ _ h => ⟨h.1, fun q' hex => hex.elim fun res hstep => h.2 q' res hstep⟩, fun _ => rfl, fun _ _ => rfl, fun _ _ _ => rfl, fun _ _ _ => rfl, fun _ => rfl, rfl, fun _ => rfl, fun _ _ => rfl, fun _ _ _ => rfl, fun _ _ _ => rfl, fun _ => rfl, rfl, fun _ => rfl, fun _ _ => rfl, fun _ _ _ => rfl, fun _ _ _ => rfl, fun _ => rfl, rfl, fun _ => rfl, fun _ _ => rfl, fun _ _ _ => rfl, fun _ _ _ => rfl, fun _ => rfl, rfl, fun _ _ _ => rfl, fun _ _ => rfl, fun _ _ _ => rfl, fun _ _ _ _ => rfl, fun _ _ _ _ => rfl, fun _ _ => rfl, fun _ _ _ => rfl, fun _ _ _ => rfl, - RunsOps.nil, RunsOps.cons⟩ - -/-- **Fibonacci heap (`O(n)` reusable initialization on arbitrary memory).** Under the same -word-size conditions (`base + (9 n + 1) < 2 ^ w`, `K_max < 2 ^ w`, and every cell of `mem₀` below -`2 ^ w`), without assuming `[base, base + 9 n + 1)` is initially zeroed, `p_init` clears the block -and initializes an empty heap in `t_init + 1 ≤ K (n + 1)` instructions, and `p_op` executes `ops` in -`t_ops ≤ K (numQueries ops + numInserts ops + numDecreaseKeys ops + -numExtractMins ops * (Nat.log 2 n + 1))` instructions while preserving memory -outside `[base, base + 9 n + 1)`. -/ + RunsOps.nil, + fun hr hh hd hmod hrest => hd.elim fun _ hsd => RunsOps.cons hr hh hsd.1 hsd.2 hmod hrest⟩ + +/-- **Fibonacci heap (`O(n_max + 1)` reusable initialization on arbitrary memory).** Under the same +word-size conditions (`base + (11 * n_max + 1) < 2 ^ w`, `U < 2 ^ w`, `V < 2 ^ w`, and every cell of +`mem₀` below `2 ^ w`), without assuming `[base, base + 11 * n_max + 1)` is initially zeroed, +`p_init` clears the block and initializes an empty heap in `t_init + 1 ≤ K * (n_max + 1)` +instructions, and `p_op` executes `ops` in +`t_ops ≤ K * (numQueries ops + numInserts ops + numDecreaseKeys ops + extLog)` instructions with +`extLog ≤ numExtractMins ops * (Nat.log 2 n_max + 1)` while preserving memory outside +`[base, base + 11 * n_max + 1)`. -/ theorem fibonacciHeap_amortized_reusable : ∃ r, ∃ p_init p_op : Program r, ∃ K, - ∀ (n K_max base w : ℕ) (mem₀ : ℕ → ℕ) (ops : List (Op n)), - base + (9 * n + 1) < 2 ^ w → - K_max < 2 ^ w → + ∀ (n_max U V base w : ℕ) (mem₀ : ℕ → ℕ) (ops : List (Op n_max)), + base + (11 * n_max + 1) < 2 ^ w → + U < 2 ^ w → + V < 2 ^ w → (∀ a, mem₀ a < 2 ^ w) → - ValidSeq K_max (emptyQueue n) ops → - ∃ t₀ t_init reg_f mem_f t_ops, - Runs w p_init ⟨0, initRegs n base, mem₀⟩ t₀ t_init ∧ + ValidSeq U V (emptyQueue n_max) ops → + ∃ t₀ t_init q_f reg_f mem_f t_ops extLog, + Runs w p_init ⟨0, initRegs n_max base, mem₀⟩ t₀ t_init ∧ (∃ out, step w p_init t₀ = .halt out) ∧ - UnchangedOutside base (9 * n + 1) mem₀ t₀.mem ∧ - RunsOps w p_op base (9 * n + 1) (emptyQueue n) t₀.reg t₀.mem ops reg_f mem_f t_ops ∧ - t_init + 1 ≤ K * (n + 1) ∧ + UnchangedOutside base (11 * n_max + 1) mem₀ t₀.mem ∧ + RunsOps w p_op base (11 * n_max + 1) (emptyQueue n_max) t₀.reg t₀.mem ops + q_f reg_f mem_f t_ops extLog ∧ + t_init + 1 ≤ K * (n_max + 1) ∧ t_ops ≤ - K * (numQueries ops + numInserts ops + numDecreaseKeys ops + - numExtractMins ops * (Nat.log 2 n + 1)) := + K * (numQueries ops + numInserts ops + numDecreaseKeys ops + extLog) ∧ + extLog ≤ numExtractMins ops * (Nat.log 2 n_max + 1) := FibonacciHeapBridge.amortized_reusable ⟨AluOp, .add, .sub, .mul, .div, .mod, .and, .or, .xor, .shl, .shr, @AluOp.rec, AluOp.eval, Instr, .const, .copy, .alu, .width, .load, .store, .jump, .branchEq, .branchLt, .halt, @@ -415,29 +463,51 @@ theorem fibonacciHeap_amortized_reusable : fun _ _ => True⟩ (by constructor <;> simp_all [step, ComplexityTests.Abstracts.WordRAMBridge.haltsVia, AluOp.eval]) - ⟨Op, .isEmpty, .getKey, .insert, .decreaseKey, .extractMin, @Op.rec, @chooseMin, @foldMin, - @findMin, fun q o => (stepOp q o).1, fun q o reg => decodeResult o reg = some (stepOp q o).2, + ⟨Op, .isEmpty, .get, .insert, .decreaseKey, .extractMin, @Op.rec, @size, @extractStepLog, + fun q o q' => ∃ res, StepRel q o q' res, + fun q o q' reg => ∃ res, StepRel q o q' res ∧ decodeResult o reg = some res, @ValidOp, @ValidSeq, @numQueries, @numInserts, @numDecreaseKeys, @numExtractMins, @initRegs, - @prepareRegs 42, @State.reg, @State.mem, @RunsOps⟩ - ⟨fun _ _ => ⟨fun h => by simp [chooseMin, h], fun h => by simp [chooseMin, h]⟩, - rfl, fun _ _ => rfl, fun _ => rfl, - fun _ => rfl, fun _ _ => rfl, fun _ _ _ => rfl, fun _ _ _ => rfl, - fun q => by simp only [stepOp]; cases findMin q <;> rfl, - fun _ _ h => congrArg some (decide_eq_decide.mpr h), - fun _ _ _ h => h, fun _ _ _ _ => rfl, fun _ _ _ _ => rfl, - fun q _ h => h.trans (congrArg some (by simp only [stepOp]; cases findMin q <;> rfl)), - fun _ _ _ h => h, fun _ _ _ h => h, fun _ _ _ h => h, + @prepareRegs 48, @State.reg, @State.mem, @RunsOps⟩ + ⟨fun _ => rfl, + fun _ => rfl, fun _ _ => rfl, fun _ _ _ => rfl, fun _ _ _ => rfl, fun _ => rfl, + fun _ => ⟨_, rfl, rfl⟩, + fun _ _ => ⟨_, rfl, rfl⟩, + fun _ _ _ h hnone => ⟨some h, h, hnone, rfl, rfl⟩, + fun _ _ _ u₀ v hq => ⟨(), ⟨u₀, v, hq, rfl⟩, rfl⟩, + fun _ hemp => ⟨none, Or.inl ⟨hemp, rfl, rfl⟩⟩, + fun _ h u v hq hmin => ⟨some (u, v, h), Or.inr ⟨h, u, v, ⟨hq, hmin⟩, rfl, rfl⟩⟩, + fun q _ h => ⟨decide (∀ h, q h = none), ⟨rfl, rfl⟩, congrArg some (decide_eq_decide.mpr h)⟩, + fun q h _ hd => ⟨q h, ⟨rfl, rfl⟩, hd⟩, + fun _ _ _ h _ hnone hv => + ⟨some h, ⟨h, hnone, rfl, rfl⟩, by + simp only [decodeResult, regAt, show (23 : ℕ) < 48 by decide, ↓reduceDIte, hv, + Fin.is_lt, Fin.eta]⟩, + fun _ _ _ u₀ v _ hq => ⟨(), ⟨⟨u₀, v, hq, rfl⟩, rfl⟩, rfl⟩, + fun _ _ hemp hflag => + ⟨none, Or.inl ⟨hemp, rfl, rfl⟩, by + simp only [decodeResult, regAt, show (22 : ℕ) < 48 by decide, ↓reduceDIte, hflag, + ↓reduceIte]⟩, + fun _ h u v _ hq hmin hflag hv hk hval => + ⟨some (u, v, h), Or.inr ⟨h, u, v, ⟨hq, hmin⟩, rfl, rfl⟩, by + simp only [decodeResult, regAt, show (22 : ℕ) < 48 by decide, + show (23 : ℕ) < 48 by decide, show (24 : ℕ) < 48 by decide, + show (47 : ℕ) < 48 by decide, ↓reduceDIte, hflag, ↓reduceIte, + hv, hk, hval, Fin.is_lt, Fin.eta]⟩, + fun _ _ _ h => h, + fun _ _ _ h => h, + fun _ _ _ h => ⟨h.1, fun q' hex => hex.elim fun res hstep => h.2 q' res hstep⟩, fun _ => rfl, fun _ _ => rfl, fun _ _ _ => rfl, fun _ _ _ => rfl, fun _ => rfl, rfl, fun _ => rfl, fun _ _ => rfl, fun _ _ _ => rfl, fun _ _ _ => rfl, fun _ => rfl, rfl, fun _ => rfl, fun _ _ => rfl, fun _ _ _ => rfl, fun _ _ _ => rfl, fun _ => rfl, rfl, fun _ => rfl, fun _ _ => rfl, fun _ _ _ => rfl, fun _ _ _ => rfl, fun _ => rfl, rfl, fun _ _ _ => rfl, fun _ _ => rfl, fun _ _ _ => rfl, fun _ _ _ _ => rfl, fun _ _ _ _ => rfl, fun _ _ => rfl, fun _ _ _ => rfl, fun _ _ _ => rfl, - RunsOps.nil, RunsOps.cons⟩ + RunsOps.nil, + fun hr hh hd hmod hrest => hd.elim fun _ hsd => RunsOps.cons hr hh hsd.1 hsd.2 hmod hrest⟩ /-- -info: abstract audit passed for `ComplexityAbstracts.FibonacciHeapAbstract`: 308 root -declarations, 259 dependencies checked +info: abstract audit passed for `ComplexityAbstracts.FibonacciHeapAbstract`: 303 root +declarations, 260 dependencies checked -/ #guard_msgs (whitespace := lax) in #check_abstract ComplexityAbstracts.FibonacciHeapAbstract diff --git a/abstracts/ComplexityAbstracts/FibonacciHeapBridge.lean b/abstracts/ComplexityAbstracts/FibonacciHeapBridge.lean index 16e635bae..4d61ce1cb 100644 --- a/abstracts/ComplexityAbstracts/FibonacciHeapBridge.lean +++ b/abstracts/ComplexityAbstracts/FibonacciHeapBridge.lean @@ -12,24 +12,24 @@ import Complexity.Algorithms.WordRAM.PriorityQueue.FibonacciHeap /-! # Bridge for the Fibonacci-heap formal abstract -`FibonacciHeapAbstract.lean` defines an indexed min-priority queue from scratch, together with its -own exact word-RAM presentation, register calling convention, memory-confinement predicate and -multi-operation execution relation `RunsOps`. This file proves, for every word-RAM presentation `S` -satisfying `WordRAMBridge.Laws S` and every priority-queue presentation `P` satisfying `PQLaws S P`, -that the compiled library programs `initFreshProg`, `initClearProg` and `opProg` of -`Cslib.Algorithms.WordRAM.PriorityQueue.FibonacciHeap` realize `RunsOps` within the claimed -`O(1)` / `O(n)` initialization bounds and `O(m_q + m_ins + m_dec + m_ext (log n + 1))` amortized -operation bound (for `m_q` queries, `m_ins` insertions, `m_dec` key decreases, and `m_ext` minimum -extractions). +`FibonacciHeapAbstract.lean` defines a min-priority queue with handles storing key–value pairs from +scratch, together with its own exact word-RAM presentation, register calling convention, +memory-confinement predicate and multi-operation execution relation `RunsOps`. This file proves, for +every word-RAM presentation `S` satisfying `WordRAMBridge.Laws S` and every priority-queue +presentation `P` satisfying `PQLaws S P`, that the compiled library programs `initFreshProg`, +`initClearProg` and `opProg` of `Cslib.Algorithms.WordRAM.PriorityQueue.FibonacciHeap.MinPQ` realize +`RunsOps` within the claimed `O(1)` / `O(n_max + 1)` initialization bounds and +`O(m_q + m_ins + m_dec + ∑ (log n_t + 1))` amortized operation bound in the current number of +stored elements `n_t`. ## Main results -* `PQPresentation.runsOps_map`: multi-operation runs of `FibonacciHeap.opProg` over `Basis.exact` - translate step-for-step to `P.RunsOps` via `WordRAMBridge.Machine.runs_map`. -* `amortized_fresh`: transfers `FibonacciHeap.run_ops_exact_fresh` (`O(1)` fresh initialization on - zeroed `[base, base + 9 n + 1)`) to any `S` and `P`. -* `amortized_reusable`: transfers `FibonacciHeap.run_ops_exact_reusable` (`O(n)` initialization on - arbitrary `w`-bit memory) to any `S` and `P`. +* `PQPresentation.runsOps_map`: multi-operation runs of `FibonacciHeap.MinPQ.opProg` over + `Basis.exact` translate step-for-step to `P.RunsOps` via `WordRAMBridge.Machine.runs_map`. +* `amortized_fresh`: transfers `FibonacciHeap.MinPQ.run_ops_exact_fresh` (`O(1)` fresh + initialization on zeroed `[base, base + 11 * n_max + 1)`) to any `S` and `P`. +* `amortized_reusable`: transfers `FibonacciHeap.MinPQ.run_ops_exact_reusable` (`O(n_max + 1)` + initialization on arbitrary `w`-bit memory) to any `S` and `P`. ## Syntax in the imports @@ -50,200 +50,223 @@ open Cslib.Algorithms.WordRAM.PriorityQueue open ComplexityTests.Abstracts open WordRAMBridge -/-! ## Priority-queue presentation and agreement with `IndexedMinPQ` -/ +/-! ## Priority-queue presentation and agreement with `MinPQ` -/ /-- Polymorphic signature of the priority-queue definitions in `FibonacciHeapAbstract`, specialized -at register count `FibonacciHeap.rFib` (`42`) where the concrete programs live. -/ +at register count `FibonacciHeap.MinPQ.rMinPQ` (`48`) where the concrete programs live. -/ structure PQPresentation (S : WordRAMBridge.Presentation) where Op : ℕ → Type - isEmpty {n : ℕ} : Op n - getKey {n : ℕ} : Fin n → Op n - insert {n : ℕ} : Fin n → ℕ → Op n - decreaseKey {n : ℕ} : Fin n → ℕ → Op n - extractMin {n : ℕ} : Op n - op_induction {n : ℕ} {P : Op n → Prop} : - P isEmpty → (∀ v, P (getKey v)) → (∀ v k, P (insert v k)) → - (∀ v k, P (decreaseKey v k)) → P extractMin → ∀ o, P o - chooseMin {n : ℕ} : Fin n × ℕ → Fin n × ℕ → Fin n × ℕ - foldMin {n : ℕ} : List (Fin n × ℕ) → Option (Fin n × ℕ) - findMin {n : ℕ} : (Fin n → Option ℕ) → Option (Fin n × ℕ) - nextQueue {n : ℕ} : (Fin n → Option ℕ) → Op n → (Fin n → Option ℕ) - DecodesStep {n : ℕ} : (Fin n → Option ℕ) → Op n → (Fin 42 → ℕ) → Prop - ValidOp {n : ℕ} : ℕ → (Fin n → Option ℕ) → Op n → Prop - ValidSeq {n : ℕ} : ℕ → (Fin n → Option ℕ) → List (Op n) → Prop - numQueries {n : ℕ} : List (Op n) → ℕ - numInserts {n : ℕ} : List (Op n) → ℕ - numDecreaseKeys {n : ℕ} : List (Op n) → ℕ - numExtractMins {n : ℕ} : List (Op n) → ℕ + isEmpty {n_max : ℕ} : Op n_max + get {n_max : ℕ} : Fin n_max → Op n_max + insert {n_max : ℕ} : ℕ → ℕ → Op n_max + decreaseKey {n_max : ℕ} : Fin n_max → ℕ → Op n_max + extractMin {n_max : ℕ} : Op n_max + op_induction {n_max : ℕ} {P : Op n_max → Prop} : + P isEmpty → (∀ h, P (get h)) → (∀ u v, P (insert u v)) → + (∀ h u', P (decreaseKey h u')) → P extractMin → ∀ o, P o + size {n_max : ℕ} : (Fin n_max → Option (ℕ × ℕ)) → ℕ + extractStepLog {n_max : ℕ} : (Fin n_max → Option (ℕ × ℕ)) → Op n_max → ℕ + StepState {n_max : ℕ} : + (Fin n_max → Option (ℕ × ℕ)) → Op n_max → (Fin n_max → Option (ℕ × ℕ)) → Prop + StepDecoded {n_max : ℕ} : + (Fin n_max → Option (ℕ × ℕ)) → Op n_max → (Fin n_max → Option (ℕ × ℕ)) → (Fin 48 → ℕ) → Prop + ValidOp {n_max : ℕ} : ℕ → ℕ → (Fin n_max → Option (ℕ × ℕ)) → Op n_max → Prop + ValidSeq {n_max : ℕ} : ℕ → ℕ → (Fin n_max → Option (ℕ × ℕ)) → List (Op n_max) → Prop + numQueries {n_max : ℕ} : List (Op n_max) → ℕ + numInserts {n_max : ℕ} : List (Op n_max) → ℕ + numDecreaseKeys {n_max : ℕ} : List (Op n_max) → ℕ + numExtractMins {n_max : ℕ} : List (Op n_max) → ℕ initRegs {r : ℕ} : ℕ → ℕ → Fin r → ℕ - prepareRegs {n : ℕ} : Op n → (Fin 42 → ℕ) → Fin 42 → ℕ + prepareRegs {n_max : ℕ} : Op n_max → (Fin 48 → ℕ) → Fin 48 → ℕ stateReg {r : ℕ} : S.State r → Fin r → ℕ stateMem {r : ℕ} : S.State r → ℕ → ℕ - RunsOps {r n : ℕ} : - ℕ → List (S.Instr r) → ℕ → ℕ → (Fin n → Option ℕ) → (Fin r → ℕ) → (ℕ → ℕ) → - List (Op n) → (Fin r → ℕ) → (ℕ → ℕ) → ℕ → Prop + RunsOps {r n_max : ℕ} : + ℕ → List (S.Instr r) → ℕ → ℕ → (Fin n_max → Option (ℕ × ℕ)) → (Fin r → ℕ) → (ℕ → ℕ) → + List (Op n_max) → (Fin n_max → Option (ℕ × ℕ)) → (Fin r → ℕ) → (ℕ → ℕ) → ℕ → ℕ → Prop -/-- Equational laws connecting a `PQPresentation` to the definitions in `FibonacciHeapAbstract`. -/ +/-- Equational and relational laws connecting a `PQPresentation` to the definitions in +`FibonacciHeapAbstract`. -/ structure PQLaws (S : WordRAMBridge.Presentation) (P : PQPresentation S) : Prop where - chooseMin_spec : ∀ {n} (a b : Fin n × ℕ), - ((a.2 < b.2 ∨ (a.2 = b.2 ∧ (a.1 : ℕ) ≤ (b.1 : ℕ))) → P.chooseMin a b = a) ∧ - (¬(a.2 < b.2 ∨ (a.2 = b.2 ∧ (a.1 : ℕ) ≤ (b.1 : ℕ))) → P.chooseMin a b = b) - foldMin_nil : ∀ {n}, P.foldMin (n := n) [] = none - foldMin_cons : ∀ {n} (a : Fin n × ℕ) (l : List (Fin n × ℕ)), - P.foldMin (a :: l) = - match P.foldMin l with - | none => some a - | some b => some (P.chooseMin a b) - findMin_eq : ∀ {n} (q : Fin n → Option ℕ), - P.findMin q = P.foldMin ((List.finRange n).filterMap fun v => (q v).map (v, ·)) - next_isEmpty : ∀ {n} (q : Fin n → Option ℕ), P.nextQueue q P.isEmpty = q - next_getKey : ∀ {n} (q : Fin n → Option ℕ) (v : Fin n), P.nextQueue q (P.getKey v) = q - next_insert : ∀ {n} (q : Fin n → Option ℕ) (v : Fin n) (k : ℕ), - P.nextQueue q (P.insert v k) = Function.update q v (some k) - next_decreaseKey : ∀ {n} (q : Fin n → Option ℕ) (v : Fin n) (k : ℕ), - P.nextQueue q (P.decreaseKey v k) = Function.update q v (some k) - next_extractMin : ∀ {n} (q : Fin n → Option ℕ), - P.nextQueue q P.extractMin = - match P.findMin q with - | none => q - | some (u, _) => Function.update q u none - decodes_isEmpty : ∀ {n} (q : Fin n → Option ℕ) (reg : Fin 42 → ℕ), - (reg ⟨22, by decide⟩ ≠ 0 ↔ ∀ v, q v = none) → P.DecodesStep q P.isEmpty reg - decodes_getKey : ∀ {n} (q : Fin n → Option ℕ) (v : Fin n) (reg : Fin 42 → ℕ), - (if reg ⟨22, by decide⟩ = 0 then some none else some (some (reg ⟨24, by decide⟩))) = - some (q v) → P.DecodesStep q (P.getKey v) reg - decodes_insert : ∀ {n} (q : Fin n → Option ℕ) (v : Fin n) (k : ℕ) (reg : Fin 42 → ℕ), - P.DecodesStep q (P.insert v k) reg - decodes_decreaseKey : ∀ {n} (q : Fin n → Option ℕ) (v : Fin n) (k : ℕ) (reg : Fin 42 → ℕ), - P.DecodesStep q (P.decreaseKey v k) reg - decodes_extractMin : ∀ {n} (q : Fin n → Option ℕ) (reg : Fin 42 → ℕ), + size_eq : ∀ {n_max} (q : Fin n_max → Option (ℕ × ℕ)), + P.size q = (Finset.univ.filter fun h => (q h).isSome).card + extractStepLog_isEmpty : ∀ {n_max} (q : Fin n_max → Option (ℕ × ℕ)), + P.extractStepLog q P.isEmpty = 0 + extractStepLog_get : ∀ {n_max} (q : Fin n_max → Option (ℕ × ℕ)) (h : Fin n_max), + P.extractStepLog q (P.get h) = 0 + extractStepLog_insert : ∀ {n_max} (q : Fin n_max → Option (ℕ × ℕ)) (u v : ℕ), + P.extractStepLog q (P.insert u v) = 0 + extractStepLog_decreaseKey : ∀ {n_max} (q : Fin n_max → Option (ℕ × ℕ)) (h : Fin n_max) (u' : ℕ), + P.extractStepLog q (P.decreaseKey h u') = 0 + extractStepLog_extractMin : ∀ {n_max} (q : Fin n_max → Option (ℕ × ℕ)), + P.extractStepLog q P.extractMin = Nat.log 2 (P.size q) + 1 + stepState_isEmpty : ∀ {n_max} (q : Fin n_max → Option (ℕ × ℕ)), + P.StepState q P.isEmpty q + stepState_get : ∀ {n_max} (q : Fin n_max → Option (ℕ × ℕ)) (h : Fin n_max), + P.StepState q (P.get h) q + stepState_insert : ∀ {n_max} (q : Fin n_max → Option (ℕ × ℕ)) (u v : ℕ) (h : Fin n_max), + q h = none → P.StepState q (P.insert u v) (Function.update q h (some (u, v))) + stepState_decreaseKey : + ∀ {n_max} (q : Fin n_max → Option (ℕ × ℕ)) (h : Fin n_max) (u' u₀ v : ℕ), + q h = some (u₀, v) → P.StepState q (P.decreaseKey h u') (Function.update q h (some (u', v))) + stepState_extractMin_empty : ∀ {n_max} (q : Fin n_max → Option (ℕ × ℕ)), + (∀ h, q h = none) → P.StepState q P.extractMin q + stepState_extractMin_some : + ∀ {n_max} (q : Fin n_max → Option (ℕ × ℕ)) (h : Fin n_max) (u v : ℕ), + q h = some (u, v) → (∀ h' u' v', q h' = some (u', v') → u ≤ u') → + P.StepState q P.extractMin (Function.update q h none) + decodes_isEmpty : ∀ {n_max} (q : Fin n_max → Option (ℕ × ℕ)) (reg : Fin 48 → ℕ), + (reg ⟨22, by decide⟩ ≠ 0 ↔ ∀ h, q h = none) → P.StepDecoded q P.isEmpty q reg + decodes_get : ∀ {n_max} (q : Fin n_max → Option (ℕ × ℕ)) (h : Fin n_max) (reg : Fin 48 → ℕ), (if reg ⟨22, by decide⟩ = 0 then some none - else if h : reg ⟨23, by decide⟩ < n then - some (some (⟨reg ⟨23, by decide⟩, h⟩, reg ⟨24, by decide⟩)) - else none) = - some (match P.findMin q with | none => none | some (u, k) => some (u, k)) → - P.DecodesStep q P.extractMin reg - valid_insert : ∀ {n K_max} (q : Fin n → Option ℕ) (v : Fin n) (k : ℕ), - P.ValidOp K_max q (P.insert v k) → q v = none ∧ k ≤ K_max - valid_decreaseKey : ∀ {n K_max} (q : Fin n → Option ℕ) (v : Fin n) (k : ℕ), - P.ValidOp K_max q (P.decreaseKey v k) → (∃ k₀, q v = some k₀ ∧ k ≤ k₀) ∧ k ≤ K_max - validSeq_cons : ∀ {n K_max} (q : Fin n → Option ℕ) (o : P.Op n) (ops : List (P.Op n)), - P.ValidSeq K_max q (o :: ops) → P.ValidOp K_max q o ∧ P.ValidSeq K_max (P.nextQueue q o) ops - queries_isEmpty : ∀ {n} (ops : List (P.Op n)), + else some (some (reg ⟨24, by decide⟩, reg ⟨47, by decide⟩))) = + some (q h) → P.StepDecoded q (P.get h) q reg + decodes_insert : + ∀ {n_max} (q : Fin n_max → Option (ℕ × ℕ)) (u v : ℕ) (h : Fin n_max) (reg : Fin 48 → ℕ), + q h = none → reg ⟨23, by decide⟩ = (h : ℕ) → + P.StepDecoded q (P.insert u v) (Function.update q h (some (u, v))) reg + decodes_decreaseKey : + ∀ {n_max} (q : Fin n_max → Option (ℕ × ℕ)) (h : Fin n_max) (u' u₀ v : ℕ) (reg : Fin 48 → ℕ), + q h = some (u₀, v) → + P.StepDecoded q (P.decreaseKey h u') (Function.update q h (some (u', v))) reg + decodes_extractMin_empty : ∀ {n_max} (q : Fin n_max → Option (ℕ × ℕ)) (reg : Fin 48 → ℕ), + (∀ h, q h = none) → reg ⟨22, by decide⟩ = 0 → P.StepDecoded q P.extractMin q reg + decodes_extractMin_some : + ∀ {n_max} (q : Fin n_max → Option (ℕ × ℕ)) (h : Fin n_max) (u v : ℕ) (reg : Fin 48 → ℕ), + q h = some (u, v) → (∀ h' u' v', q h' = some (u', v') → u ≤ u') → + reg ⟨22, by decide⟩ ≠ 0 → reg ⟨23, by decide⟩ = (h : ℕ) → + reg ⟨24, by decide⟩ = u → reg ⟨47, by decide⟩ = v → + P.StepDecoded q P.extractMin (Function.update q h none) reg + valid_insert : ∀ {n_max U V} (q : Fin n_max → Option (ℕ × ℕ)) (u v : ℕ), + P.ValidOp U V q (P.insert u v) → P.size q < n_max ∧ u < U ∧ v < V + valid_decreaseKey : ∀ {n_max U V} (q : Fin n_max → Option (ℕ × ℕ)) (h : Fin n_max) (u' : ℕ), + P.ValidOp U V q (P.decreaseKey h u') → ∃ u₀ v, q h = some (u₀, v) ∧ u' ≤ u₀ + validSeq_cons : + ∀ {n_max U V} (q : Fin n_max → Option (ℕ × ℕ)) (o : P.Op n_max) (ops : List (P.Op n_max)), + P.ValidSeq U V q (o :: ops) → + P.ValidOp U V q o ∧ ∀ q', P.StepState q o q' → P.ValidSeq U V q' ops + queries_isEmpty : ∀ {n_max} (ops : List (P.Op n_max)), P.numQueries (P.isEmpty :: ops) = 1 + P.numQueries ops - queries_getKey : ∀ {n} (v : Fin n) (ops : List (P.Op n)), - P.numQueries (P.getKey v :: ops) = 1 + P.numQueries ops - queries_insert : ∀ {n} (v : Fin n) (k : ℕ) (ops : List (P.Op n)), - P.numQueries (P.insert v k :: ops) = P.numQueries ops - queries_decreaseKey : ∀ {n} (v : Fin n) (k : ℕ) (ops : List (P.Op n)), - P.numQueries (P.decreaseKey v k :: ops) = P.numQueries ops - queries_extractMin : ∀ {n} (ops : List (P.Op n)), + queries_get : ∀ {n_max} (h : Fin n_max) (ops : List (P.Op n_max)), + P.numQueries (P.get h :: ops) = 1 + P.numQueries ops + queries_insert : ∀ {n_max} (u v : ℕ) (ops : List (P.Op n_max)), + P.numQueries (P.insert u v :: ops) = P.numQueries ops + queries_decreaseKey : ∀ {n_max} (h : Fin n_max) (u' : ℕ) (ops : List (P.Op n_max)), + P.numQueries (P.decreaseKey h u' :: ops) = P.numQueries ops + queries_extractMin : ∀ {n_max} (ops : List (P.Op n_max)), P.numQueries (P.extractMin :: ops) = P.numQueries ops - queries_nil : ∀ {n}, P.numQueries (n := n) [] = 0 - inserts_isEmpty : ∀ {n} (ops : List (P.Op n)), + queries_nil : ∀ {n_max}, P.numQueries (n_max := n_max) [] = 0 + inserts_isEmpty : ∀ {n_max} (ops : List (P.Op n_max)), P.numInserts (P.isEmpty :: ops) = P.numInserts ops - inserts_getKey : ∀ {n} (v : Fin n) (ops : List (P.Op n)), - P.numInserts (P.getKey v :: ops) = P.numInserts ops - inserts_insert : ∀ {n} (v : Fin n) (k : ℕ) (ops : List (P.Op n)), - P.numInserts (P.insert v k :: ops) = 1 + P.numInserts ops - inserts_decreaseKey : ∀ {n} (v : Fin n) (k : ℕ) (ops : List (P.Op n)), - P.numInserts (P.decreaseKey v k :: ops) = P.numInserts ops - inserts_extractMin : ∀ {n} (ops : List (P.Op n)), + inserts_get : ∀ {n_max} (h : Fin n_max) (ops : List (P.Op n_max)), + P.numInserts (P.get h :: ops) = P.numInserts ops + inserts_insert : ∀ {n_max} (u v : ℕ) (ops : List (P.Op n_max)), + P.numInserts (P.insert u v :: ops) = 1 + P.numInserts ops + inserts_decreaseKey : ∀ {n_max} (h : Fin n_max) (u' : ℕ) (ops : List (P.Op n_max)), + P.numInserts (P.decreaseKey h u' :: ops) = P.numInserts ops + inserts_extractMin : ∀ {n_max} (ops : List (P.Op n_max)), P.numInserts (P.extractMin :: ops) = P.numInserts ops - inserts_nil : ∀ {n}, P.numInserts (n := n) [] = 0 - dec_isEmpty : ∀ {n} (ops : List (P.Op n)), + inserts_nil : ∀ {n_max}, P.numInserts (n_max := n_max) [] = 0 + dec_isEmpty : ∀ {n_max} (ops : List (P.Op n_max)), P.numDecreaseKeys (P.isEmpty :: ops) = P.numDecreaseKeys ops - dec_getKey : ∀ {n} (v : Fin n) (ops : List (P.Op n)), - P.numDecreaseKeys (P.getKey v :: ops) = P.numDecreaseKeys ops - dec_insert : ∀ {n} (v : Fin n) (k : ℕ) (ops : List (P.Op n)), - P.numDecreaseKeys (P.insert v k :: ops) = P.numDecreaseKeys ops - dec_decreaseKey : ∀ {n} (v : Fin n) (k : ℕ) (ops : List (P.Op n)), - P.numDecreaseKeys (P.decreaseKey v k :: ops) = 1 + P.numDecreaseKeys ops - dec_extractMin : ∀ {n} (ops : List (P.Op n)), + dec_get : ∀ {n_max} (h : Fin n_max) (ops : List (P.Op n_max)), + P.numDecreaseKeys (P.get h :: ops) = P.numDecreaseKeys ops + dec_insert : ∀ {n_max} (u v : ℕ) (ops : List (P.Op n_max)), + P.numDecreaseKeys (P.insert u v :: ops) = P.numDecreaseKeys ops + dec_decreaseKey : ∀ {n_max} (h : Fin n_max) (u' : ℕ) (ops : List (P.Op n_max)), + P.numDecreaseKeys (P.decreaseKey h u' :: ops) = 1 + P.numDecreaseKeys ops + dec_extractMin : ∀ {n_max} (ops : List (P.Op n_max)), P.numDecreaseKeys (P.extractMin :: ops) = P.numDecreaseKeys ops - dec_nil : ∀ {n}, P.numDecreaseKeys (n := n) [] = 0 - ext_isEmpty : ∀ {n} (ops : List (P.Op n)), + dec_nil : ∀ {n_max}, P.numDecreaseKeys (n_max := n_max) [] = 0 + ext_isEmpty : ∀ {n_max} (ops : List (P.Op n_max)), P.numExtractMins (P.isEmpty :: ops) = P.numExtractMins ops - ext_getKey : ∀ {n} (v : Fin n) (ops : List (P.Op n)), - P.numExtractMins (P.getKey v :: ops) = P.numExtractMins ops - ext_insert : ∀ {n} (v : Fin n) (k : ℕ) (ops : List (P.Op n)), - P.numExtractMins (P.insert v k :: ops) = P.numExtractMins ops - ext_decreaseKey : ∀ {n} (v : Fin n) (k : ℕ) (ops : List (P.Op n)), - P.numExtractMins (P.decreaseKey v k :: ops) = P.numExtractMins ops - ext_extractMin : ∀ {n} (ops : List (P.Op n)), + ext_get : ∀ {n_max} (h : Fin n_max) (ops : List (P.Op n_max)), + P.numExtractMins (P.get h :: ops) = P.numExtractMins ops + ext_insert : ∀ {n_max} (u v : ℕ) (ops : List (P.Op n_max)), + P.numExtractMins (P.insert u v :: ops) = P.numExtractMins ops + ext_decreaseKey : ∀ {n_max} (h : Fin n_max) (u' : ℕ) (ops : List (P.Op n_max)), + P.numExtractMins (P.decreaseKey h u' :: ops) = P.numExtractMins ops + ext_extractMin : ∀ {n_max} (ops : List (P.Op n_max)), P.numExtractMins (P.extractMin :: ops) = 1 + P.numExtractMins ops - ext_nil : ∀ {n}, P.numExtractMins (n := n) [] = 0 - initRegs_spec : ∀ n base (i : Fin 42), - P.initRegs n base i = if (i : ℕ) = 0 then n else if (i : ℕ) = 1 then base else 0 - prep_isEmpty : ∀ {n} (reg : Fin 42 → ℕ) (i : Fin 42), - P.prepareRegs (n := n) P.isEmpty reg i = + ext_nil : ∀ {n_max}, P.numExtractMins (n_max := n_max) [] = 0 + initRegs_spec : ∀ n_max base (i : Fin 48), + P.initRegs n_max base i = if (i : ℕ) = 0 then n_max else if (i : ℕ) = 1 then base else 0 + prep_isEmpty : ∀ {n_max} (reg : Fin 48 → ℕ) (i : Fin 48), + P.prepareRegs (n_max := n_max) P.isEmpty reg i = if (i : ℕ) = 17 then 0 else if (i : ℕ) = 18 then 0 else if (i : ℕ) = 19 then 0 - else if (i : ℕ) = 20 then 0 else if (i : ℕ) = 21 then 0 else reg i - prep_getKey : ∀ {n} (v : Fin n) (reg : Fin 42 → ℕ) (i : Fin 42), - P.prepareRegs (P.getKey v) reg i = + else if (i : ℕ) = 20 then 0 else if (i : ℕ) = 21 then 0 else if (i : ℕ) = 46 then 0 + else reg i + prep_get : ∀ {n_max} (h : Fin n_max) (reg : Fin 48 → ℕ) (i : Fin 48), + P.prepareRegs (P.get h) reg i = if (i : ℕ) = 17 then 0 else if (i : ℕ) = 18 then 0 else if (i : ℕ) = 19 then 1 - else if (i : ℕ) = 20 then (v : ℕ) else if (i : ℕ) = 21 then 0 else reg i - prep_insert : ∀ {n} (v : Fin n) (k : ℕ) (reg : Fin 42 → ℕ) (i : Fin 42), - P.prepareRegs (P.insert v k) reg i = + else if (i : ℕ) = 20 then (h : ℕ) else if (i : ℕ) = 21 then 0 else if (i : ℕ) = 46 then 0 + else reg i + prep_insert : ∀ {n_max} (u v : ℕ) (reg : Fin 48 → ℕ) (i : Fin 48), + P.prepareRegs (n_max := n_max) (P.insert u v) reg i = if (i : ℕ) = 17 then 0 else if (i : ℕ) = 18 then 1 else if (i : ℕ) = 19 then 0 - else if (i : ℕ) = 20 then (v : ℕ) else if (i : ℕ) = 21 then k else reg i - prep_decreaseKey : ∀ {n} (v : Fin n) (k : ℕ) (reg : Fin 42 → ℕ) (i : Fin 42), - P.prepareRegs (P.decreaseKey v k) reg i = + else if (i : ℕ) = 20 then 0 else if (i : ℕ) = 21 then u else if (i : ℕ) = 46 then v + else reg i + prep_decreaseKey : ∀ {n_max} (h : Fin n_max) (u' : ℕ) (reg : Fin 48 → ℕ) (i : Fin 48), + P.prepareRegs (P.decreaseKey h u') reg i = if (i : ℕ) = 17 then 0 else if (i : ℕ) = 18 then 1 else if (i : ℕ) = 19 then 1 - else if (i : ℕ) = 20 then (v : ℕ) else if (i : ℕ) = 21 then k else reg i - prep_extractMin : ∀ {n} (reg : Fin 42 → ℕ) (i : Fin 42), - P.prepareRegs (n := n) P.extractMin reg i = + else if (i : ℕ) = 20 then (h : ℕ) else if (i : ℕ) = 21 then u' else if (i : ℕ) = 46 then 0 + else reg i + prep_extractMin : ∀ {n_max} (reg : Fin 48 → ℕ) (i : Fin 48), + P.prepareRegs (n_max := n_max) P.extractMin reg i = if (i : ℕ) = 17 then 1 else if (i : ℕ) = 18 then 0 else if (i : ℕ) = 19 then 0 - else if (i : ℕ) = 20 then 0 else if (i : ℕ) = 21 then 0 else reg i - stateReg_eq : ∀ (pc : ℕ) (reg : Fin 42 → ℕ) (m : ℕ → ℕ), P.stateReg (S.state pc reg m) = reg - stateMem_eq : ∀ (pc : ℕ) (reg : Fin 42 → ℕ) (m : ℕ → ℕ), P.stateMem (S.state pc reg m) = m - runsOps_nil : ∀ {n w} {p : List (S.Instr 42)} {base len} {q : Fin n → Option ℕ} - {reg : Fin 42 → ℕ} {mem : ℕ → ℕ}, - P.RunsOps w p base len q reg mem [] reg mem 0 - runsOps_cons : ∀ {n w} {p : List (S.Instr 42)} {base len} {q : Fin n → Option ℕ} - {reg : Fin 42 → ℕ} {mem : ℕ → ℕ} {o : P.Op n} {ops : List (P.Op n)} {t : S.State 42} - {reg' : Fin 42 → ℕ} {mem' : ℕ → ℕ} {steps rest : ℕ}, + else if (i : ℕ) = 20 then 0 else if (i : ℕ) = 21 then 0 else if (i : ℕ) = 46 then 0 + else reg i + stateReg_eq : ∀ (pc : ℕ) (reg : Fin 48 → ℕ) (m : ℕ → ℕ), P.stateReg (S.state pc reg m) = reg + stateMem_eq : ∀ (pc : ℕ) (reg : Fin 48 → ℕ) (m : ℕ → ℕ), P.stateMem (S.state pc reg m) = m + runsOps_nil : ∀ {n_max w} {p : List (S.Instr 48)} {base len} + {q : Fin n_max → Option (ℕ × ℕ)} {reg : Fin 48 → ℕ} {mem : ℕ → ℕ}, + P.RunsOps w p base len q reg mem [] q reg mem 0 0 + runsOps_cons : ∀ {n_max w} {p : List (S.Instr 48)} {base len} + {q q₁ q' : Fin n_max → Option (ℕ × ℕ)} {reg : Fin 48 → ℕ} {mem : ℕ → ℕ} + {o : P.Op n_max} {ops : List (P.Op n_max)} {t : S.State 48} + {reg' : Fin 48 → ℕ} {mem' : ℕ → ℕ} {steps rest extRest : ℕ}, Relation.RelatesInSteps (fun a b => S.step w p a = S.next b) (S.state 0 (P.prepareRegs o reg) mem) t steps → (∃ out, S.step w p t = S.halted out) → - P.DecodesStep q o (P.stateReg t) → + P.StepDecoded q o q₁ (P.stateReg t) → (∀ a, a < base ∨ base + len ≤ a → P.stateMem t a = mem a) → - P.RunsOps w p base len (P.nextQueue q o) (P.stateReg t) (P.stateMem t) ops reg' mem' rest → - P.RunsOps w p base len q reg mem (o :: ops) reg' mem' ((steps + 1) + rest) + P.RunsOps w p base len q₁ (P.stateReg t) (P.stateMem t) ops q' reg' mem' rest extRest → + P.RunsOps w p base len q reg mem (o :: ops) q' reg' mem' + ((steps + 1) + rest) (P.extractStepLog q o + extRest) -theorem forall_none_iff_dom_empty {n : ℕ} (q : Fin n → Option ℕ) : - (∀ v, q v = none) ↔ IndexedMinPQ.dom q = ∅ := by +theorem forall_none_iff_dom_empty {n_max : ℕ} (q : Fin n_max → Option (ℕ × ℕ)) : + (∀ h, q h = none) ↔ MinPQ.dom q = ∅ := by rw [Finset.eq_empty_iff_forall_notMem] - exact forall_congr' fun v => IndexedMinPQ.notMem_dom_iff.symm + exact forall_congr' fun h => MinPQ.notMem_dom_iff.symm -theorem unchangedOutside_of_modifies {base n : ℕ} {m m' : ℕ → ℕ} - (hm : Modifies (Block.mk base (FibonacciHeap.space n)).footprint m m') : - ∀ a, a < base ∨ base + (9 * n + 1) ≤ a → m' a = m a := by +theorem unchangedOutside_of_modifies {base n_max : ℕ} {m m' : ℕ → ℕ} + (hm : Modifies (Block.mk base (FibonacciHeap.MinPQ.space n_max)).footprint m m') : + ∀ a, a < base ∨ base + (11 * n_max + 1) ≤ a → m' a = m a := by intro a ha apply hm a rw [Block.mem_footprint, Block.Owns] - dsimp only [FibonacciHeap.space] + dsimp only [FibonacciHeap.MinPQ.space] omega namespace PQPresentation variable {S : WordRAMBridge.Presentation} (P : PQPresentation S) -/-- Translate a library `IndexedMinPQ.Op n` into `P.Op n`. -/ -def opOf {n : ℕ} : IndexedMinPQ.Op n → P.Op n +/-- Translate a library `MinPQ.Op n_max` into `P.Op n_max`. -/ +def opOf {n_max : ℕ} : MinPQ.Op n_max → P.Op n_max | .isEmpty => P.isEmpty - | .getKey v => P.getKey v - | .insert v k => P.insert v k - | .decreaseKey v k => P.decreaseKey v k + | .get h => P.get h + | .insert u v => P.insert u v + | .decreaseKey h u' => P.decreaseKey h u' | .extractMin => P.extractMin -theorem opOf_surjective {n : ℕ} (o : P.Op n) : ∃ g : IndexedMinPQ.Op n, P.opOf g = o := - P.op_induction (P := fun o => ∃ g, P.opOf g = o) ⟨.isEmpty, rfl⟩ (fun v => ⟨.getKey v, rfl⟩) - (fun v k => ⟨.insert v k, rfl⟩) (fun v k => ⟨.decreaseKey v k, rfl⟩) ⟨.extractMin, rfl⟩ o +theorem opOf_surjective {n_max : ℕ} (o : P.Op n_max) : ∃ g : MinPQ.Op n_max, P.opOf g = o := + P.op_induction (P := fun o => ∃ g, P.opOf g = o) ⟨.isEmpty, rfl⟩ (fun h => ⟨.get h, rfl⟩) + (fun u v => ⟨.insert u v, rfl⟩) (fun h u' => ⟨.decreaseKey h u', rfl⟩) + ⟨.extractMin, rfl⟩ o -theorem list_opOf_surjective {n : ℕ} (ops : List (P.Op n)) : - ∃ gs : List (IndexedMinPQ.Op n), gs.map P.opOf = ops := by - choose f hf using P.opOf_surjective (n := n) +theorem list_opOf_surjective {n_max : ℕ} (ops : List (P.Op n_max)) : + ∃ gs : List (MinPQ.Op n_max), gs.map P.opOf = ops := by + choose f hf using P.opOf_surjective (n_max := n_max) refine ⟨ops.map f, ?_⟩ rw [List.map_map] conv_rhs => rw [← List.map_id ops] @@ -256,198 +279,262 @@ section WithLaws variable {P} (hP : PQLaws S P) include hP -theorem chooseMin_eq {n : ℕ} (a b : Fin n × ℕ) : - P.chooseMin a b = IndexedMinPQ.chooseMin a b := by - by_cases h : IndexedMinPQ.PrioLE a b - · simp only [IndexedMinPQ.chooseMin, h, ↓reduceIte] - exact (hP.chooseMin_spec a b).1 h - · simp only [IndexedMinPQ.chooseMin, h, ↓reduceIte] - exact (hP.chooseMin_spec a b).2 h - -theorem foldMin_eq {n : ℕ} (l : List (Fin n × ℕ)) : - P.foldMin l = IndexedMinPQ.foldMin l := by - induction l with - | nil => exact hP.foldMin_nil - | cons a l ih => - rw [hP.foldMin_cons, ih] - simp only [IndexedMinPQ.foldMin] - cases IndexedMinPQ.foldMin l with - | none => rfl - | some b => - dsimp only - rw [chooseMin_eq hP] - -theorem findMin_eq {n : ℕ} (q : Fin n → Option ℕ) : - P.findMin q = IndexedMinPQ.findMin q := by - rw [hP.findMin_eq, foldMin_eq hP, IndexedMinPQ.findMin] - -theorem nextQueue_eq {n K_max : ℕ} (q : Fin n → Option ℕ) (g : IndexedMinPQ.Op n) : - P.nextQueue q (P.opOf g) = ((IndexedMinPQ.spec n K_max).step q g).1 := by +theorem size_eq_lib {n_max : ℕ} (q : Fin n_max → Option (ℕ × ℕ)) : + P.size q = MinPQ.size q := + hP.size_eq q + +theorem extractStepLog_eq {n_max : ℕ} (q : Fin n_max → Option (ℕ × ℕ)) (g : MinPQ.Op n_max) : + P.extractStepLog q (P.opOf g) = + match g with + | .extractMin => Nat.log 2 (MinPQ.size q) + 1 + | _ => 0 := by + cases g with + | isEmpty => exact hP.extractStepLog_isEmpty q + | get h => exact hP.extractStepLog_get q h + | insert u v => exact hP.extractStepLog_insert q u v + | decreaseKey h u' => exact hP.extractStepLog_decreaseKey q h u' + | extractMin => + rw [opOf, hP.extractStepLog_extractMin, size_eq_lib hP] + +theorem stepState_of_lib {n_max U V : ℕ} {q q' : Fin n_max → Option (ℕ × ℕ)} + {g : MinPQ.Op n_max} {a : (MinPQ.spec n_max U V).Result g} + (hs : (MinPQ.spec n_max U V).step q g q' a) : + P.StepState q (P.opOf g) q' := by cases g with - | isEmpty => exact hP.next_isEmpty q - | getKey v => exact hP.next_getKey q v - | insert v k => exact hP.next_insert q v k - | decreaseKey v k => exact hP.next_decreaseKey q v k + | isEmpty => + rcases hs with ⟨hqq, _⟩ + subst q' + exact hP.stepState_isEmpty q + | get h => + rcases hs with ⟨hqq, _⟩ + subst q' + exact hP.stepState_get q h + | insert u v => + rcases hs with ⟨h, hnone, hqq, _⟩ + subst q' + exact hP.stepState_insert q u v h hnone + | decreaseKey h u' => + rcases hs with ⟨u₀, v, hq, hqq, _⟩ + subst q' + exact hP.stepState_decreaseKey q h u' u₀ v hq | extractMin => - rw [opOf, hP.next_extractMin, findMin_eq hP] - simp only [IndexedMinPQ.spec] - cases IndexedMinPQ.findMin q <;> rfl - -theorem decodesStep_of_lib {n K_max : ℕ} (q : Fin n → Option ℕ) (g : IndexedMinPQ.Op n) - (reg : Fin 42 → ℕ) - (hd : FibonacciHeap.code.decode (n := n) (K_max := K_max) g reg = - some ((IndexedMinPQ.spec n K_max).step q g).2) : - P.DecodesStep q (P.opOf g) reg := by + rcases hs with ⟨hemp, hqq, _⟩ | ⟨h, u, v, ⟨hq, hmin⟩, hqq, _⟩ + · subst q' + exact hP.stepState_extractMin_empty q ((forall_none_iff_dom_empty q).mpr hemp) + · subst q' + exact hP.stepState_extractMin_some q h u v hq hmin + +theorem stepDecoded_of_lib {n_max U V : ℕ} {q q' : Fin n_max → Option (ℕ × ℕ)} + {g : MinPQ.Op n_max} {a : (MinPQ.spec n_max U V).Result g} {reg : Fin 48 → ℕ} + (hs : (MinPQ.spec n_max U V).step q g q' a) + (hd : FibonacciHeap.MinPQ.decode (n_max := n_max) (U := U) (V := V) g reg = some a) : + P.StepDecoded q (P.opOf g) q' reg := by cases g with | isEmpty => + dsimp only [MinPQ.spec, MinPQ.Op.Result] at a hs hd + rcases hs with ⟨hqq, rfl⟩ + subst q' apply hP.decodes_isEmpty - dsimp only [PQCode.decode, FibonacciHeap.code, FibonacciHeap.outFlag, - IndexedMinPQ.spec] at hd - have hd' : decide (reg ⟨22, _⟩ ≠ 0) = decide (IndexedMinPQ.dom q = ∅) := - Option.some.inj hd + dsimp only [FibonacciHeap.MinPQ.decode, FibonacciHeap.MinPQ.outFlag] at hd + have hd' : decide (reg ⟨22, _⟩ ≠ 0) = decide (MinPQ.dom q = ∅) := Option.some.inj hd exact (decide_eq_decide.mp hd').trans (forall_none_iff_dom_empty q).symm - | getKey v => - apply hP.decodes_getKey - dsimp only [PQCode.decode, FibonacciHeap.code, FibonacciHeap.outFlag, FibonacciHeap.outK, - IndexedMinPQ.spec] at hd + | get h => + dsimp only [MinPQ.spec, MinPQ.Op.Result] at a hs hd + rcases hs with ⟨hqq, rfl⟩ + subst q' + apply hP.decodes_get exact hd - | insert v k => exact hP.decodes_insert q v k reg - | decreaseKey v k => exact hP.decodes_decreaseKey q v k reg + | insert u v => + dsimp only [MinPQ.spec, MinPQ.Op.Result] at a hs hd + rcases hs with ⟨h, hnone, hqq, rfl⟩ + subst q' + dsimp only [FibonacciHeap.MinPQ.decode, FibonacciHeap.MinPQ.outV] at hd + by_cases hlt : reg ⟨23, by decide⟩ < n_max + · simp only [hlt, ↓reduceDIte] at hd + have hh : (⟨reg ⟨23, by decide⟩, hlt⟩ : Fin n_max) = h := + Option.some.inj (Option.some.inj hd) + exact hP.decodes_insert q u v h reg hnone (congrArg Fin.val hh) + · simp only [hlt, ↓reduceDIte] at hd + cases hd + | decreaseKey h u' => + rcases hs with ⟨u₀, v, hq, hqq, _⟩ + subst q' + exact hP.decodes_decreaseKey q h u' u₀ v reg hq | extractMin => - apply hP.decodes_extractMin - rw [findMin_eq hP] - dsimp only [PQCode.decode, FibonacciHeap.code, FibonacciHeap.outFlag, FibonacciHeap.outV, - FibonacciHeap.outK, IndexedMinPQ.spec] at hd - rcases hm : IndexedMinPQ.findMin q with _ | ⟨u, k⟩ <;> rw [hm] at hd <;> exact hd - -theorem initRegs_eq (n base : ℕ) : - P.initRegs (r := 42) n base = FibonacciHeap.initRegs n base := by + dsimp only [MinPQ.spec, MinPQ.Op.Result] at a hs hd + dsimp only [FibonacciHeap.MinPQ.decode, FibonacciHeap.MinPQ.outFlag, + FibonacciHeap.MinPQ.outV, FibonacciHeap.MinPQ.outK, + FibonacciHeap.MinPQ.outValReg] at hd + rcases hs with ⟨hemp, hqq, rfl⟩ | ⟨h, u, v, ⟨hq, hmin⟩, hqq, rfl⟩ + · subst q' + by_cases hf : reg ⟨22, by decide⟩ = 0 + · exact hP.decodes_extractMin_empty q reg ((forall_none_iff_dom_empty q).mpr hemp) hf + · simp only [hf, ↓reduceIte] at hd + by_cases hlt : reg ⟨23, by decide⟩ < n_max + · simp only [hlt, ↓reduceDIte] at hd + cases Option.some.inj hd + · simp only [hlt, ↓reduceDIte] at hd + cases hd + · subst q' + by_cases hf : reg ⟨22, by decide⟩ = 0 + · simp only [hf, ↓reduceIte] at hd + cases Option.some.inj hd + · simp only [hf, ↓reduceIte] at hd + by_cases hlt : reg ⟨23, by decide⟩ < n_max + · simp only [hlt, ↓reduceDIte] at hd + have hprod := Option.some.inj (Option.some.inj hd) + simp only [Prod.mk.injEq] at hprod + rcases hprod with ⟨hk, hval, hh⟩ + exact hP.decodes_extractMin_some q h u v reg hq hmin hf + (congrArg Fin.val hh) hk hval + · simp only [hlt, ↓reduceDIte] at hd + cases hd + +theorem initRegs_eq (n_max base : ℕ) : + P.initRegs (r := 48) n_max base = FibonacciHeap.MinPQ.initRegs n_max base := by funext i rw [hP.initRegs_spec] - simp only [FibonacciHeap.initRegs, FibonacciHeap.nReg, FibonacciHeap.frontierReg, Fin.ext_iff] + simp only [FibonacciHeap.MinPQ.initRegs, FibonacciHeap.MinPQ.nReg, + FibonacciHeap.MinPQ.outerFrontierReg, Fin.ext_iff] -theorem prepareRegs_eq {n : ℕ} (g : IndexedMinPQ.Op n) (reg : Fin 42 → ℕ) : - P.prepareRegs (P.opOf g) reg = FibonacciHeap.prepareRegs g reg := by +theorem prepareRegs_eq {n_max : ℕ} (g : MinPQ.Op n_max) (reg : Fin 48 → ℕ) : + P.prepareRegs (P.opOf g) reg = FibonacciHeap.MinPQ.prepareRegs g reg := by funext i cases g with | isEmpty => rw [opOf, hP.prep_isEmpty] - simp only [FibonacciHeap.prepareRegs, PQCode.encode, - FibonacciHeap.tag2, FibonacciHeap.tag1, FibonacciHeap.tag0, - FibonacciHeap.argV, FibonacciHeap.argK, Fin.ext_iff] + simp only [FibonacciHeap.MinPQ.prepareRegs, FibonacciHeap.MinPQ.rawEncode, + FibonacciHeap.MinPQ.tag2, FibonacciHeap.MinPQ.tag1, FibonacciHeap.MinPQ.tag0, + FibonacciHeap.MinPQ.argV, FibonacciHeap.MinPQ.argK, FibonacciHeap.MinPQ.inValReg, + Fin.ext_iff] split_ifs <;> omega - | getKey v => - rw [opOf, hP.prep_getKey] - simp only [FibonacciHeap.prepareRegs, PQCode.encode, FibonacciHeap.code, - FibonacciHeap.tag2, FibonacciHeap.tag1, FibonacciHeap.tag0, - FibonacciHeap.argV, FibonacciHeap.argK, Fin.ext_iff] + | get h => + rw [opOf, hP.prep_get] + simp only [FibonacciHeap.MinPQ.prepareRegs, FibonacciHeap.MinPQ.rawEncode, + FibonacciHeap.MinPQ.tag2, FibonacciHeap.MinPQ.tag1, FibonacciHeap.MinPQ.tag0, + FibonacciHeap.MinPQ.argV, FibonacciHeap.MinPQ.argK, FibonacciHeap.MinPQ.inValReg, + Fin.ext_iff] split_ifs <;> omega - | insert v k => + | insert u v => rw [opOf, hP.prep_insert] - simp only [FibonacciHeap.prepareRegs, PQCode.encode, FibonacciHeap.code, - FibonacciHeap.tag2, FibonacciHeap.tag1, FibonacciHeap.tag0, - FibonacciHeap.argV, FibonacciHeap.argK, Fin.ext_iff] + simp only [FibonacciHeap.MinPQ.prepareRegs, FibonacciHeap.MinPQ.rawEncode, + FibonacciHeap.MinPQ.tag2, FibonacciHeap.MinPQ.tag1, FibonacciHeap.MinPQ.tag0, + FibonacciHeap.MinPQ.argV, FibonacciHeap.MinPQ.argK, FibonacciHeap.MinPQ.inValReg, + Fin.ext_iff] split_ifs <;> omega - | decreaseKey v k => + | decreaseKey h u' => rw [opOf, hP.prep_decreaseKey] - simp only [FibonacciHeap.prepareRegs, PQCode.encode, FibonacciHeap.code, - FibonacciHeap.tag2, FibonacciHeap.tag1, FibonacciHeap.tag0, - FibonacciHeap.argV, FibonacciHeap.argK, Fin.ext_iff] + simp only [FibonacciHeap.MinPQ.prepareRegs, FibonacciHeap.MinPQ.rawEncode, + FibonacciHeap.MinPQ.tag2, FibonacciHeap.MinPQ.tag1, FibonacciHeap.MinPQ.tag0, + FibonacciHeap.MinPQ.argV, FibonacciHeap.MinPQ.argK, FibonacciHeap.MinPQ.inValReg, + Fin.ext_iff] split_ifs <;> omega | extractMin => rw [opOf, hP.prep_extractMin] - simp only [FibonacciHeap.prepareRegs, PQCode.encode, FibonacciHeap.code, - FibonacciHeap.tag2, FibonacciHeap.tag1, FibonacciHeap.tag0, - FibonacciHeap.argV, FibonacciHeap.argK, Fin.ext_iff] + simp only [FibonacciHeap.MinPQ.prepareRegs, FibonacciHeap.MinPQ.rawEncode, + FibonacciHeap.MinPQ.tag2, FibonacciHeap.MinPQ.tag1, FibonacciHeap.MinPQ.tag0, + FibonacciHeap.MinPQ.argV, FibonacciHeap.MinPQ.argK, FibonacciHeap.MinPQ.inValReg, + Fin.ext_iff] split_ifs <;> omega -theorem validSeq_to_lib {n K_max : ℕ} : - ∀ (gs : List (IndexedMinPQ.Op n)) (q : Fin n → Option ℕ), - P.ValidSeq K_max q (gs.map P.opOf) → (IndexedMinPQ.spec n K_max).ValidHistory q gs +theorem validSeq_to_lib {n_max U V : ℕ} : + ∀ (gs : List (MinPQ.Op n_max)) (q : Fin n_max → Option (ℕ × ℕ)), + P.ValidSeq U V q (gs.map P.opOf) → (MinPQ.spec n_max U V).ValidOpSeq q gs | [], _, _ => trivial | g :: gs, q, hv => by obtain ⟨hv_g, hv_gs⟩ := hP.validSeq_cons q (P.opOf g) (gs.map P.opOf) hv - rw [nextQueue_eq hP (K_max := K_max)] at hv_gs - refine ⟨?_, validSeq_to_lib gs _ hv_gs⟩ + refine ⟨?_, fun q' a hs => validSeq_to_lib gs q' (hv_gs q' (stepState_of_lib hP hs))⟩ cases g with - | isEmpty | getKey _ | extractMin => trivial - | insert v k => exact hP.valid_insert q v k hv_g - | decreaseKey v k => exact hP.valid_decreaseKey q v k hv_g - -theorem numQueries_eq {n : ℕ} : - ∀ (gs : List (IndexedMinPQ.Op n)), - FibonacciHeap.numQueries gs = P.numQueries (gs.map P.opOf) + | isEmpty | get _ | extractMin => trivial + | insert u v => + have h := hP.valid_insert q u v hv_g + rw [size_eq_lib hP] at h + exact h + | decreaseKey h u' => exact hP.valid_decreaseKey q h u' hv_g + +theorem numQueries_eq {n_max : ℕ} : + ∀ (gs : List (MinPQ.Op n_max)), + FibonacciHeap.MinPQ.numQueries gs = P.numQueries (gs.map P.opOf) | [] => hP.queries_nil.symm | g :: gs => by have ih := numQueries_eq gs cases g <;> - simp only [List.map_cons, opOf, FibonacciHeap.numQueries, ih, - hP.queries_isEmpty, hP.queries_getKey, hP.queries_insert, + simp only [List.map_cons, opOf, FibonacciHeap.MinPQ.numQueries, ih, + hP.queries_isEmpty, hP.queries_get, hP.queries_insert, hP.queries_decreaseKey, hP.queries_extractMin] -theorem numInserts_eq {n : ℕ} : - ∀ (gs : List (IndexedMinPQ.Op n)), - FibonacciHeap.numInserts gs = P.numInserts (gs.map P.opOf) +theorem numInserts_eq {n_max : ℕ} : + ∀ (gs : List (MinPQ.Op n_max)), + FibonacciHeap.MinPQ.numInserts gs = P.numInserts (gs.map P.opOf) | [] => hP.inserts_nil.symm | g :: gs => by have ih := numInserts_eq gs cases g <;> - simp only [List.map_cons, opOf, FibonacciHeap.numInserts, ih, - hP.inserts_isEmpty, hP.inserts_getKey, hP.inserts_insert, + simp only [List.map_cons, opOf, FibonacciHeap.MinPQ.numInserts, ih, + hP.inserts_isEmpty, hP.inserts_get, hP.inserts_insert, hP.inserts_decreaseKey, hP.inserts_extractMin] -theorem numDecreaseKeys_eq {n : ℕ} : - ∀ (gs : List (IndexedMinPQ.Op n)), - FibonacciHeap.numDecreaseKeys gs = P.numDecreaseKeys (gs.map P.opOf) +theorem numDecreaseKeys_eq {n_max : ℕ} : + ∀ (gs : List (MinPQ.Op n_max)), + FibonacciHeap.MinPQ.numDecreaseKeys gs = P.numDecreaseKeys (gs.map P.opOf) | [] => hP.dec_nil.symm | g :: gs => by have ih := numDecreaseKeys_eq gs cases g <;> - simp only [List.map_cons, opOf, FibonacciHeap.numDecreaseKeys, ih, - hP.dec_isEmpty, hP.dec_getKey, hP.dec_insert, + simp only [List.map_cons, opOf, FibonacciHeap.MinPQ.numDecreaseKeys, ih, + hP.dec_isEmpty, hP.dec_get, hP.dec_insert, hP.dec_decreaseKey, hP.dec_extractMin] -theorem numExtractMins_eq {n : ℕ} : - ∀ (gs : List (IndexedMinPQ.Op n)), - FibonacciHeap.numExtractMins gs = P.numExtractMins (gs.map P.opOf) +theorem numExtractMins_eq {n_max : ℕ} : + ∀ (gs : List (MinPQ.Op n_max)), + FibonacciHeap.MinPQ.numExtractMins gs = P.numExtractMins (gs.map P.opOf) | [] => hP.ext_nil.symm | g :: gs => by have ih := numExtractMins_eq gs cases g <;> - simp only [List.map_cons, opOf, FibonacciHeap.numExtractMins, ih, - hP.ext_isEmpty, hP.ext_getKey, hP.ext_insert, + simp only [List.map_cons, opOf, FibonacciHeap.MinPQ.numExtractMins, ih, + hP.ext_isEmpty, hP.ext_get, hP.ext_insert, hP.ext_decreaseKey, hP.ext_extractMin] -theorem runsOps_map (hS : WordRAMBridge.Laws S) {n K_max w base : ℕ} : - ∀ {q : Fin n → Option ℕ} {x z : Data 42} {gs : List (IndexedMinPQ.Op n)} {total : ℕ}, - FibonacciHeap.RunsOps (K_max := K_max) w base q x gs z total → - P.RunsOps w (S.programOf FibonacciHeap.opProg) base (9 * n + 1) q x.regs x.mem - (gs.map P.opOf) z.regs z.mem total - | _, _, _, _, _, .nil => hP.runsOps_nil - | q, x, z, g :: gs, _, - @FibonacciHeap.RunsOps.cons _ _ _ _ _ _ _ _ pc y _ steps rest hr ⟨out, hh⟩ hdec hmod hrest => +theorem runsOps_map (hS : WordRAMBridge.Laws S) {n_max U V w base : ℕ} : + ∀ {q q' : Fin n_max → Option (ℕ × ℕ)} {x z : Data 48} {gs : List (MinPQ.Op n_max)} + {tr : (MinPQ.spec n_max U V).Transcript} {total : ℕ}, + FibonacciHeap.MinPQ.RunsOps (U := U) (V := V) w base q x gs tr q' z total → + P.RunsOps w (S.programOf FibonacciHeap.MinPQ.opProg) base (11 * n_max + 1) q x.regs x.mem + (gs.map P.opOf) q' z.regs z.mem total (FibonacciHeap.MinPQ.extractLogSum q tr) + | _, _, _, _, _, _, _, .nil => hP.runsOps_nil + | q, q', x, z, g :: gs, _, _, + @FibonacciHeap.MinPQ.RunsOps.cons _ _ _ _ _ _ q₁ _ _ _ a _ tr_rest pc y _ steps rest + hr ⟨out, hh⟩ hstep hdec hmod hrest => by have hr' : Relation.RelatesInSteps - (fun a b => S.step w (S.programOf FibonacciHeap.opProg) a = S.next b) + (fun a b => S.step w (S.programOf FibonacciHeap.MinPQ.opProg) a = S.next b) (S.state 0 (P.prepareRegs (P.opOf g) x.regs) x.mem) (S.state pc y.regs y.mem) steps := by rw [prepareRegs_eq hP] exact Machine.runs_map hS hr - have hh' : S.step w (S.programOf FibonacciHeap.opProg) (S.state pc y.regs y.mem) = + have hh' : S.step w (S.programOf FibonacciHeap.MinPQ.opProg) (S.state pc y.regs y.mem) = S.halted out := Machine.step_halt hS hh - have hdec' : P.DecodesStep q (P.opOf g) (P.stateReg (S.state pc y.regs y.mem)) := by + have hdec' : P.StepDecoded q (P.opOf g) q₁ (P.stateReg (S.state pc y.regs y.mem)) := by rw [hP.stateReg_eq] - exact decodesStep_of_lib hP q g y.regs hdec - have hmod' : ∀ a, a < base ∨ base + (9 * n + 1) ≤ a → + exact stepDecoded_of_lib hP hstep hdec + have hmod' : ∀ a, a < base ∨ base + (11 * n_max + 1) ≤ a → P.stateMem (S.state pc y.regs y.mem) a = x.mem a := by rw [hP.stateMem_eq] exact unchangedOutside_of_modifies hmod have ih := runsOps_map hS hrest - rw [← nextQueue_eq hP (K_max := K_max)] at ih - refine hP.runsOps_cons hr' ⟨out, hh'⟩ hdec' hmod' ?_ - rw [hP.stateReg_eq, hP.stateMem_eq] - exact ih + have hstep_Res := hP.runsOps_cons hr' ⟨out, hh'⟩ hdec' hmod' + (by rw [hP.stateReg_eq, hP.stateMem_eq]; exact ih) + have hext : FibonacciHeap.MinPQ.extractLogSum q + ({ op := g, result := a, nextState := q₁ } :: tr_rest) = + P.extractStepLog q (P.opOf g) + FibonacciHeap.MinPQ.extractLogSum q₁ tr_rest := by + cases g <;> + simp only [FibonacciHeap.MinPQ.extractLogSum, opOf, + hP.extractStepLog_isEmpty, hP.extractStepLog_get, hP.extractStepLog_insert, + hP.extractStepLog_decreaseKey, hP.extractStepLog_extractMin, size_eq_lib hP] + rw [List.map_cons, hext] + exact hstep_Res end WithLaws @@ -455,88 +542,99 @@ end PQPresentation /-! ## Transfer of the standalone Fibonacci-heap theorems -/ -/-- Transfer of `FibonacciHeap.run_ops_exact_fresh` (`O(1)` fresh initialization on zeroed -`[base, base + 9 n + 1)`) to any exact word-RAM presentation `S` and priority-queue presentation -`P`. -/ +/-- Transfer of `FibonacciHeap.MinPQ.run_ops_exact_fresh` (`O(1)` fresh initialization on zeroed +`[base, base + 11 * n_max + 1)`) to any exact word-RAM presentation `S` and priority-queue +presentation `P`. -/ theorem amortized_fresh (S : WordRAMBridge.Presentation) (hS : WordRAMBridge.Laws S) (P : PQPresentation S) (hP : PQLaws S P) : ∃ r, ∃ p_init p_op : List (S.Instr r), ∃ K, - ∀ (n K_max base w : ℕ) (mem₀ : ℕ → ℕ) (ops : List (P.Op n)), - base + (9 * n + 1) < 2 ^ w → - K_max < 2 ^ w → + ∀ (n_max U V base w : ℕ) (mem₀ : ℕ → ℕ) (ops : List (P.Op n_max)), + base + (11 * n_max + 1) < 2 ^ w → + U < 2 ^ w → + V < 2 ^ w → (∀ a, mem₀ a < 2 ^ w) → - (∀ a, base ≤ a → a < base + (9 * n + 1) → mem₀ a = 0) → - P.ValidSeq K_max (fun _ => none) ops → - ∃ t₀ t_init reg_f mem_f t_ops, + (∀ a, base ≤ a → a < base + (11 * n_max + 1) → mem₀ a = 0) → + P.ValidSeq U V (fun _ => none) ops → + ∃ t₀ t_init q_f reg_f mem_f t_ops extLog, Relation.RelatesInSteps (fun a b => S.step w p_init a = S.next b) - (S.state 0 (P.initRegs n base) mem₀) t₀ t_init ∧ + (S.state 0 (P.initRegs n_max base) mem₀) t₀ t_init ∧ (∃ out, S.step w p_init t₀ = S.halted out) ∧ - (∀ a, a < base ∨ base + (9 * n + 1) ≤ a → P.stateMem t₀ a = mem₀ a) ∧ - P.RunsOps w p_op base (9 * n + 1) (fun _ => none) (P.stateReg t₀) (P.stateMem t₀) ops - reg_f mem_f t_ops ∧ + (∀ a, a < base ∨ base + (11 * n_max + 1) ≤ a → P.stateMem t₀ a = mem₀ a) ∧ + P.RunsOps w p_op base (11 * n_max + 1) (fun _ => none) (P.stateReg t₀) (P.stateMem t₀) + ops q_f reg_f mem_f t_ops extLog ∧ t_init + 1 ≤ K ∧ t_ops ≤ - K * (P.numQueries ops + P.numInserts ops + P.numDecreaseKeys ops + - P.numExtractMins ops * (Nat.log 2 n + 1)) := by - refine ⟨42, S.programOf FibonacciHeap.initFreshProg, S.programOf FibonacciHeap.opProg, 421, - fun n K_max base w mem₀ ops hsp hK hmem hzero hv => ?_⟩ + K * (P.numQueries ops + P.numInserts ops + P.numDecreaseKeys ops + extLog) ∧ + extLog ≤ P.numExtractMins ops * (Nat.log 2 n_max + 1) := by + refine ⟨48, S.programOf FibonacciHeap.MinPQ.initFreshProg, + S.programOf FibonacciHeap.MinPQ.opProg, 421, + fun n_max U V base w mem₀ ops hsp hU hV hmem hzero hv => ?_⟩ obtain ⟨gs, rfl⟩ := P.list_opOf_surjective ops - have hzero' : ∀ a ∈ (Block.mk base (FibonacciHeap.space n)).footprint, mem₀ a = 0 := by + have hzero' : ∀ a ∈ (Block.mk base (FibonacciHeap.MinPQ.space n_max)).footprint, mem₀ a = 0 := by intro a ha rw [Block.mem_footprint, Block.Owns] at ha exact hzero a ha.1 ha.2 - obtain ⟨pc₀, x₀, t_init, z, t_ops, hr₀, ⟨out, hh₀⟩, hmod₀, hruns, ht_init, ht_ops⟩ := - FibonacciHeap.run_ops_exact_fresh (K_max := K_max) hsp hK hmem hzero' gs + obtain ⟨pc₀, x₀, t_init, tr, q_f, z, t_ops, hr₀, ⟨out, hh₀⟩, hmod₀, hruns, hops, _, + ht_init, ht_ops, _⟩ := + FibonacciHeap.MinPQ.run_ops_exact_fresh (U := U) (V := V) hsp hU hV hmem hzero' gs (P.validSeq_to_lib hP gs _ hv) - refine ⟨S.state pc₀ x₀.regs x₀.mem, t_init, z.regs, z.mem, t_ops, ?_, - ⟨out, Machine.step_halt hS hh₀⟩, ?_, ?_, ht_init, ?_⟩ + have hlog_le := FibonacciHeap.MinPQ.extractLogSum_le_numExtractMins tr (MinPQ.empty n_max) + rw [hops, P.numExtractMins_eq hP] at hlog_le + refine ⟨S.state pc₀ x₀.regs x₀.mem, t_init, q_f, z.regs, z.mem, t_ops, + FibonacciHeap.MinPQ.extractLogSum (MinPQ.empty n_max) tr, ?_, + ⟨out, Machine.step_halt hS hh₀⟩, ?_, ?_, ht_init, ?_, hlog_le⟩ · rw [P.initRegs_eq hP] exact Machine.runs_map hS hr₀ · rw [hP.stateMem_eq] exact unchangedOutside_of_modifies hmod₀ · rw [hP.stateReg_eq, hP.stateMem_eq] exact P.runsOps_map hP hS hruns - · rw [← P.numQueries_eq hP, ← P.numInserts_eq hP, ← P.numDecreaseKeys_eq hP, - ← P.numExtractMins_eq hP] + · rw [← P.numQueries_eq hP, ← P.numInserts_eq hP, ← P.numDecreaseKeys_eq hP] exact ht_ops -/-- Transfer of `FibonacciHeap.run_ops_exact_reusable` (`O(n)` clear initialization on arbitrary -`w`-bit memory) to any exact word-RAM presentation `S` and priority-queue presentation `P`. -/ +/-- Transfer of `FibonacciHeap.MinPQ.run_ops_exact_reusable` (`O(n_max + 1)` clear initialization +on arbitrary `w`-bit memory) to any exact word-RAM presentation `S` and priority-queue presentation +`P`. -/ theorem amortized_reusable (S : WordRAMBridge.Presentation) (hS : WordRAMBridge.Laws S) (P : PQPresentation S) (hP : PQLaws S P) : ∃ r, ∃ p_init p_op : List (S.Instr r), ∃ K, - ∀ (n K_max base w : ℕ) (mem₀ : ℕ → ℕ) (ops : List (P.Op n)), - base + (9 * n + 1) < 2 ^ w → - K_max < 2 ^ w → + ∀ (n_max U V base w : ℕ) (mem₀ : ℕ → ℕ) (ops : List (P.Op n_max)), + base + (11 * n_max + 1) < 2 ^ w → + U < 2 ^ w → + V < 2 ^ w → (∀ a, mem₀ a < 2 ^ w) → - P.ValidSeq K_max (fun _ => none) ops → - ∃ t₀ t_init reg_f mem_f t_ops, + P.ValidSeq U V (fun _ => none) ops → + ∃ t₀ t_init q_f reg_f mem_f t_ops extLog, Relation.RelatesInSteps (fun a b => S.step w p_init a = S.next b) - (S.state 0 (P.initRegs n base) mem₀) t₀ t_init ∧ + (S.state 0 (P.initRegs n_max base) mem₀) t₀ t_init ∧ (∃ out, S.step w p_init t₀ = S.halted out) ∧ - (∀ a, a < base ∨ base + (9 * n + 1) ≤ a → P.stateMem t₀ a = mem₀ a) ∧ - P.RunsOps w p_op base (9 * n + 1) (fun _ => none) (P.stateReg t₀) (P.stateMem t₀) ops - reg_f mem_f t_ops ∧ - t_init + 1 ≤ K * (n + 1) ∧ + (∀ a, a < base ∨ base + (11 * n_max + 1) ≤ a → P.stateMem t₀ a = mem₀ a) ∧ + P.RunsOps w p_op base (11 * n_max + 1) (fun _ => none) (P.stateReg t₀) (P.stateMem t₀) + ops q_f reg_f mem_f t_ops extLog ∧ + t_init + 1 ≤ K * (n_max + 1) ∧ t_ops ≤ - K * (P.numQueries ops + P.numInserts ops + P.numDecreaseKeys ops + - P.numExtractMins ops * (Nat.log 2 n + 1)) := by - refine ⟨42, S.programOf FibonacciHeap.initClearProg, S.programOf FibonacciHeap.opProg, 421, - fun n K_max base w mem₀ ops hsp hK hmem hv => ?_⟩ + K * (P.numQueries ops + P.numInserts ops + P.numDecreaseKeys ops + extLog) ∧ + extLog ≤ P.numExtractMins ops * (Nat.log 2 n_max + 1) := by + refine ⟨48, S.programOf FibonacciHeap.MinPQ.initClearProg, + S.programOf FibonacciHeap.MinPQ.opProg, 421, + fun n_max U V base w mem₀ ops hsp hU hV hmem hv => ?_⟩ obtain ⟨gs, rfl⟩ := P.list_opOf_surjective ops - obtain ⟨pc₀, x₀, t_init, z, t_ops, hr₀, ⟨out, hh₀⟩, hmod₀, hruns, ht_init, ht_ops⟩ := - FibonacciHeap.run_ops_exact_reusable (K_max := K_max) hsp hK hmem gs + obtain ⟨pc₀, x₀, t_init, tr, q_f, z, t_ops, hr₀, ⟨out, hh₀⟩, hmod₀, hruns, hops, _, + ht_init, ht_ops, _⟩ := + FibonacciHeap.MinPQ.run_ops_exact_reusable (U := U) (V := V) hsp hU hV hmem gs (P.validSeq_to_lib hP gs _ hv) - refine ⟨S.state pc₀ x₀.regs x₀.mem, t_init, z.regs, z.mem, t_ops, ?_, - ⟨out, Machine.step_halt hS hh₀⟩, ?_, ?_, ht_init, ?_⟩ + have hlog_le := FibonacciHeap.MinPQ.extractLogSum_le_numExtractMins tr (MinPQ.empty n_max) + rw [hops, P.numExtractMins_eq hP] at hlog_le + refine ⟨S.state pc₀ x₀.regs x₀.mem, t_init, q_f, z.regs, z.mem, t_ops, + FibonacciHeap.MinPQ.extractLogSum (MinPQ.empty n_max) tr, ?_, + ⟨out, Machine.step_halt hS hh₀⟩, ?_, ?_, ht_init, ?_, hlog_le⟩ · rw [P.initRegs_eq hP] exact Machine.runs_map hS hr₀ · rw [hP.stateMem_eq] exact unchangedOutside_of_modifies hmod₀ · rw [hP.stateReg_eq, hP.stateMem_eq] exact P.runsOps_map hP hS hruns - · rw [← P.numQueries_eq hP, ← P.numInserts_eq hP, ← P.numDecreaseKeys_eq hP, - ← P.numExtractMins_eq hP] + · rw [← P.numQueries_eq hP, ← P.numInserts_eq hP, ← P.numDecreaseKeys_eq hP] exact ht_ops end ComplexityAbstracts.FibonacciHeapBridge