Skip to content

Commit

Permalink
Add pairing heap module
Browse files Browse the repository at this point in the history
  • Loading branch information
klange committed Dec 21, 2023
1 parent 2685f49 commit 8075459
Show file tree
Hide file tree
Showing 2 changed files with 419 additions and 0 deletions.
104 changes: 104 additions & 0 deletions modules/pheap.krk
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
'''
@brief Pairing heap.
@author K. Lange <klange@toaruos.org>
Provides a simple min-heap with insert, pop, peek, and visit.
A Kuroko implementation is provided as a backup alongside a
faster C implementation in a shared object module.
'''

def __make_pheap():
def pheap_meld(left, right, comp):
if left is None:
return right
if right is None:
return left
if comp(left[0],right[0]):
if left[1]:
right[2] = left[1]
left[1] = right
return left
else:
if right[1]:
left[2] = right[1]
right[1] = left
return right

def pheap_merge_pairs(lst, comp):
if lst is None:
return None
else if lst[2] is None:
return lst
else:
let next = lst[2]
lst[2] = None
let rest = next[2]
next[2] = None
return pheap_meld(pheap_meld(lst,next,comp), pheap_merge_pairs(rest, comp), comp)

def pheap_delete_min(heap, comp):
let subs = heap[1]
return pheap_merge_pairs(subs, comp)

def pheap_visit_heap(heap, func):
if not heap: return
func(heap)
pheap_visit_heap(heap[1], func)
pheap_visit_heap(heap[2], func)

def pheap_visit_heap_after(heap, func):
if not heap: return
pheap_visit_heap(heap[1], func)
pheap_visit_heap(heap[2], func)
func(heap)

class PHeap:
def __init__(self, comp):
'''Create a new pairing heap governed by the given comparator function.'''
self.heap = None
self.comp = comp
self.count = 0

def insert(self, value):
'''Insert a new element into the heap.'''
self.heap = pheap_meld(self.heap, [value, None, None], self.comp)
self.count += 1

def peek(self):
'''Retrieve the root (smallest) element of the heap, or None if it is empty.'''
return self.heap[0] if self.heap else None

def pop(self):
'''Remove and return the root (smallest) element of the heap. If the heap is empty, IndexError is raised.'''
let out = self.heap
if not out:
raise IndexError('pop from empty heap')
self.heap = pheap_delete_min(self.heap, self.comp)
self.count -= 1
return out[0] if out else None

def __bool__(self):
return self.heap is not None

def __len__(self):
return self.count

def visit(self, func, after=False):
'''Call a function for each element of the heap.'''
(pheap_visit_heap_after if after else pheap_visit_heap)(self.heap, func)

# Clean up qualified name.
PHeap.__qualname__ = 'PHeap'

return PHeap

# Keep the Kuroko version available for testing.
let PHeap_krk = __make_pheap()
let PHeap = PHeap_krk

# Try to load the C implementation.
try:
import _pheap
PHeap = _pheap.PHeap

Loading

0 comments on commit 8075459

Please sign in to comment.