module__pheap.c
Go to the documentation of this file.
1 
25 #include <assert.h>
26 #include <stdlib.h>
27 #include <kuroko/vm.h>
28 #include <kuroko/util.h>
29 
30 static KrkClass * PHeapClass;
31 
39 typedef struct PHeap PHeap;
40 struct PHeap {
41  struct PHeap_Obj * owner;
42  KrkValue value;
43  PHeap * subheaps; /* Left pointer to first child, if any. */
44  PHeap * next; /* Right pointer to next sibling, if any. */
45 };
46 
61 typedef int (*pheap_comparator_func)(PHeap *, PHeap *);
62 
73 static PHeap * pheap_meld(PHeap * left, PHeap * right, pheap_comparator_func comparator) {
74  /*
75  * If either of the heaps is "empty" (represented by NULL),
76  * then simply return the other one.
77  */
78  if (!left) {
79  return right;
80  }
81  if (!right) {
82  return left;
83  }
84 
85  /*
86  * Otherwise, pull the 'smaller' of the two up and add the 'larger'
87  * to the front of the subheap list of the smaller one. We use
88  * intrusive lists within our Heap struct, so each Heap is also
89  * a List node (with a `next` pointer).
90  */
91  if (comparator(left, right)) {
92  /* Turns `left` into Heap(left→value, right :: left→subheaps) */
93  if (left->subheaps) {
94  right->next = left->subheaps;
95  }
96  left->subheaps = right;
97  return left;
98  } else {
99  /* Turns `right` into Heap(right→value, left :: right→subheaps) */
100  if (right->subheaps) {
101  left->next = right->subheaps;
102  }
103  right->subheaps = left;
104  return right;
105  }
106 }
107 
117 static PHeap * pheap_merge_pairs(PHeap * list, pheap_comparator_func comparator) {
118  if (!list) {
119  /* An empty list is represented by NULL, and yields an empty Heap,
120  * which is also represented by NULL... */
121  return NULL;
122  } else if (list->next == NULL) {
123  /* If a list entry doesn't have a next, it has a size of one,
124  * and we can just return this heap directly. */
125  return list;
126  } else {
127  /* Otherwise we meld the first two, then meld them with the result of
128  * recursively melding the rest, which performs our left-right /
129  * right-left two-stage merge. */
130  PHeap * next = list->next;
131  list->next = NULL;
132  PHeap * rest = next->next;
133  next->next = NULL;
134  return pheap_meld(pheap_meld(list, next, comparator), pheap_merge_pairs(rest, comparator), comparator);
135  }
136 }
137 
148 static PHeap * pheap_delete_min(PHeap * heap, pheap_comparator_func comparator) {
149  PHeap * subs = heap->subheaps;
150  return pheap_merge_pairs(subs, comparator);
151 }
152 
162 static void pheap_visit_heap(PHeap * heap, void (*func)(PHeap *, void*), void* extra) {
163  if (!heap) return;
164  func(heap, extra);
165  pheap_visit_heap(heap->subheaps, func, extra);
166  pheap_visit_heap(heap->next, func, extra);
167 }
168 
179 static void pheap_visit_heap_after(PHeap * heap, void (*func)(PHeap *, void*), void* extra) {
180  if (!heap) return;
181  pheap_visit_heap_after(heap->subheaps, func, extra);
182  pheap_visit_heap_after(heap->next, func, extra);
183  func(heap, extra);
184 }
185 
186 struct PHeap_Obj {
187  KrkInstance inst;
188  KrkValue comparator;
189  PHeap * heap;
190  size_t count;
191 };
192 
193 #define IS_PHeap(o) (krk_isInstanceOf(o,PHeapClass))
194 #define AS_PHeap(o) ((struct PHeap_Obj*)AS_OBJECT(o))
195 #define CURRENT_CTYPE struct PHeap_Obj *
196 #define CURRENT_NAME self
197 
198 KRK_Method(PHeap,__init__) {
199  KrkValue comparator;
200  if (!krk_parseArgs(".V:PHeap", (const char*[]){"comp"}, &comparator)) return NONE_VAL();
201  self->comparator = comparator;
202  return NONE_VAL();
203 }
204 
205 static int run_comparator(PHeap * left, PHeap * right) {
206  assert(left->owner == right->owner);
207  krk_push(left->owner->comparator);
208  krk_push(left->value);
209  krk_push(right->value);
210  KrkValue result = krk_callStack(2);
211  if (!IS_BOOLEAN(result)) return 0;
212  return AS_BOOLEAN(result);
213 }
214 
215 KRK_Method(PHeap,insert) {
216  KrkValue value;
217  if (!krk_parseArgs(".V",(const char*[]){"value"}, &value)) return NONE_VAL();
218  struct PHeap * node = calloc(sizeof(struct PHeap), 1);
219  node->owner = self;
220  node->value = value;
221  self->heap = pheap_meld(self->heap, node, run_comparator);
222  self->count += 1;
223  return NONE_VAL();
224 }
225 
226 KRK_Method(PHeap,peek) {
227  if (self->heap) return self->heap->value;
228  return NONE_VAL();
229 }
230 
231 KRK_Method(PHeap,pop) {
232  PHeap * old = self->heap;
233  if (!old) return krk_runtimeError(vm.exceptions->indexError, "pop from empty heap");
234  self->heap = pheap_delete_min(self->heap, run_comparator);
235  self->count -= 1;
236  KrkValue out = old->value;
237  free(old);
238  return out;
239 }
240 
241 KRK_Method(PHeap,__bool__) {
242  return BOOLEAN_VAL(self->heap != NULL);
243 }
244 
245 KRK_Method(PHeap,__len__) {
246  return INTEGER_VAL(self->count);
247 }
248 
249 static void run_visitor(PHeap * heap, void * visitor) {
250  krk_push(*(KrkValue*)visitor);
251  krk_push(heap->value);
252  krk_callStack(1);
253 }
254 
255 KRK_Method(PHeap,visit) {
256  KrkValue func;
257  int after = 0;
258  if (!krk_parseArgs(".V|p",(const char*[]){"func","after"},
259  &func, &after)) return NONE_VAL();
260 
261  (after ? pheap_visit_heap_after : pheap_visit_heap)(self->heap, run_visitor, &func);
262 
263  return NONE_VAL();
264 }
265 
266 static void _scan_one(PHeap * heap, void * unused) {
267  krk_markValue(heap->value);
268 }
269 
270 static void _pheap_scan(KrkInstance * _self) {
271  struct PHeap_Obj * self = (void*)_self;
272  krk_markValue(self->comparator);
273  pheap_visit_heap(self->heap, _scan_one, NULL);
274 }
275 
276 static void _free_one(PHeap * heap, void * unused) {
277  free(heap);
278 }
279 
280 static void _pheap_sweep(KrkInstance * _self) {
281  struct PHeap_Obj * self = (void*)_self;
282  pheap_visit_heap_after(self->heap,_free_one, NULL);
283 }
284 
285 KRK_Method(PHeap,comp) {
286  return self->comparator;
287 }
288 
289 KRK_Module(_pheap) {
290  KRK_DOC(module, "Pairing heap with simple insert and pop-min operations.");
291 
292  KrkClass * PHeap = krk_makeClass(module, &PHeapClass, "PHeap", vm.baseClasses->objectClass);
293  KRK_DOC(PHeap,"Pairing heap with simple insert and pop-min operations.");
294  PHeap->allocSize = sizeof(struct PHeap_Obj);
295  PHeap->_ongcscan = _pheap_scan;
296  PHeap->_ongcsweep = _pheap_sweep;
297 
298  KRK_DOC(BIND_METHOD(PHeap,__init__),
299  "@arguments comp\n\n"
300  "Create a new pairing heap governed by the given comparator function.");
301  KRK_DOC(BIND_METHOD(PHeap,insert),
302  "@arguments value\n\n"
303  "Insert a new element into the heap.");
304  KRK_DOC(BIND_METHOD(PHeap,peek),
305  "Retrieve the root (smallest) element of the heap, or None if it is empty.");
306  KRK_DOC(BIND_METHOD(PHeap,pop),
307  "Remove and return the root (smallest) element of the heap. If the heap is empty, IndexError is raised.");
308  BIND_METHOD(PHeap,__bool__);
309  BIND_METHOD(PHeap,__len__);
310  KRK_DOC(BIND_METHOD(PHeap,visit),
311  "@arguments func,after=False\n\n"
312  "Call a function for each element of the heap.");
313  BIND_PROP(PHeap,comp);
314  krk_finalizeClass(PHeapClass);
315 }
KrkValue krk_runtimeError(KrkClass *type, const char *fmt,...)
Produce and raise an exception with a formatted message.
Definition: exceptions.c:460
void krk_markValue(KrkValue value)
During a GC scan cycle, mark a value as used.
Definition: memory.c:334
int(* pheap_comparator_func)(PHeap *, PHeap *)
Heap comparator function.
Definition: module__pheap.c:61
KRK_Module(socket)
Type object.
Definition: object.h:215
KrkClass * krk_makeClass(KrkInstance *module, KrkClass **_class, const char *name, KrkClass *base)
Convenience function for creating new types.
Definition: vm.c:164
void krk_finalizeClass(KrkClass *_class)
Finalize a class by collecting pointers to core methods.
Definition: vm.c:189
An object of a class.
Definition: object.h:281
Stack reference or primative value.
Utilities for creating native bindings.
#define krk_parseArgs(f, n,...)
Parse arguments to a function while accepting keyword arguments.
Definition: util.h:360
#define KRK_DOC(thing, text)
Attach documentation to a thing of various types.
Definition: util.h:304
Core API for the bytecode virtual machine.
KrkValue krk_callStack(int argCount)
Call a callable on the stack with argCount arguments.
Definition: vm.c:732
#define vm
Convenience macro for namespacing.
Definition: vm.h:257
void krk_push(KrkValue value)
Push a stack value.
Definition: vm.c:118