File Coverage

SkewHeap.xs
Criterion Covered Total %
statement 181 228 79.3
branch 67 114 58.7
condition n/a
subroutine n/a
pod n/a
total 248 342 72.5


line stmt bran cond sub pod time code
1             #define PERL_NO_GET_CONTEXT
2              
3             #include "EXTERN.h"
4             #include "perl.h"
5             #include "XSUB.h"
6             #include "ppport.h"
7              
8             /*
9             * Allocate memory with Newx if it's
10             * available - if it's an older perl
11             * that doesn't have Newx then we
12             * resort to using New.
13             */
14             #ifndef Newx
15             #define Newx(v, n, t) New(0, v, n, t)
16             #endif
17              
18             /*
19             * perl object ref to skewheap_t*
20             */
21             #ifndef SKEW
22             #define SKEW(obj) ((skewheap_t*) SvIV(SvRV(obj)))
23             #endif
24              
25             /*
26             * Thanks again, MLEHMANN:
27             * http://grokbase.com/t/perl/perl5-porters/097tr5nw6b/perl-67894-multicall-push-requires-perl-core
28             */
29             #ifndef cxinc
30             #define cxinc() Perl_cxinc(aTHX)
31             #endif
32              
33              
34             typedef struct SkewNode {
35             struct SkewNode *left;
36             struct SkewNode *right;
37             SV *value;
38             } skewnode_t;
39              
40             typedef struct SkewHeap {
41             skewnode_t *root;
42             IV size;
43             SV *cmp;
44             } skewheap_t;
45              
46              
47             static
48 84           skewnode_t* new_node(pTHX_ SV *value) {
49             skewnode_t *node;
50 84           Newx(node, 1, skewnode_t);
51 84           node->left = NULL;
52 84           node->right = NULL;
53 84           node->value = newSVsv(value);
54 84           return node;
55             }
56              
57             static
58 48           skewnode_t* clone_node(pTHX_ skewnode_t *node) {
59 48 100         if (node == NULL) {
60 30           return NULL;
61             }
62              
63             skewnode_t *new_node;
64              
65 18           Newx(new_node, 1, skewnode_t);
66 18           new_node->value = newSVsv(node->value);
67 18           new_node->left = clone_node(aTHX_ node->left);
68 18           new_node->right = clone_node(aTHX_ node->right);
69              
70 18           return new_node;
71             }
72              
73             static
74 102           void free_node(pTHX_ skewnode_t *node) {
75 102 100         if (node->left != NULL) free_node(aTHX_ node->left);
76 102 100         if (node->right != NULL) free_node(aTHX_ node->right);
77 102           SvREFCNT_dec(node->value);
78 102           Safefree(node);
79 102           }
80              
81              
82             static
83 24           SV* new(pTHX_ const char *class, SV *cmp) {
84             skewheap_t *heap;
85             SV *obj;
86             SV *ref;
87              
88 24           Newx(heap, 1, skewheap_t);
89 24           heap->root = NULL;
90 24           heap->size = 0;
91 24           heap->cmp = cmp;
92 24           SvREFCNT_inc(heap->cmp);
93              
94 24           obj = newSViv((IV) heap);
95 24           ref = newRV_noinc(obj);
96 24           sv_bless(ref, gv_stashpv(class, GV_ADD));
97 24           SvREADONLY_on(obj);
98              
99 24           return ref;
100             }
101              
102             static
103 24           void DESTROY(pTHX_ SV *ref) {
104 24 50         skewheap_t *heap = SKEW(ref);
105 24 100         if (heap->root != NULL) free_node(aTHX_ heap->root);
106 24           SvREFCNT_dec(heap->cmp);
107 24           Safefree(heap);
108 24           }
109              
110              
111             static
112 0           size_t walk_tree(skewnode_t *node, skewnode_t *nodes[], size_t idx) {
113 0           size_t inc = 0;
114 0           nodes[ idx ] = node;
115 0           ++inc;
116              
117 0 0         if (node->left != NULL) {
118 0           inc += walk_tree(node->left, nodes, idx + inc);
119             }
120              
121 0 0         if (node->right != NULL) {
122 0           inc += walk_tree(node->right, nodes, idx + inc);
123             }
124              
125 0           return inc;
126             }
127              
128             static
129 0           SV* to_array(pTHX_ SV *ref) {
130 0 0         skewheap_t *heap = SKEW(ref);
131 0           skewnode_t *nodes[ heap->size ];
132 0           AV *array = newAV();
133             size_t i;
134              
135 0           walk_tree(heap->root, nodes, 0);
136              
137 0 0         for (i = 0; i < heap->size; ++i) {
138 0           av_push(array, newSVsv( nodes[i]->value ));
139             }
140              
141 0           return newRV_noinc( (SV*) array );
142             }
143              
144             static
145 70           void sort_nodes(pTHX_ skewnode_t *nodes[], int length, SV *cmp) {
146             skewnode_t *tmp, *x;
147             int p, j;
148 70           int start = 0;
149 70           int end = length - 1;
150 70           int top = 1;
151 70           int stack[end - start + 1];
152              
153 70           stack[0] = start;
154 70           stack[1] = end;
155              
156             // set up multicall
157 70           dSP;
158             GV *agv, *bgv, *gv;
159             HV *stash;
160              
161             // code value from sv code ref
162 70           CV *cv = sv_2cv(cmp, &stash, &gv, 0);
163              
164 70 50         if (cv == Nullcv) {
165 0           croak("Not a subroutine reference");
166             }
167              
168 70           agv = gv_fetchpv("main::a", GV_ADD, SVt_PV);
169 70           bgv = gv_fetchpv("main::b", GV_ADD, SVt_PV);
170 70           SAVESPTR(GvSV(agv));
171 70           SAVESPTR(GvSV(bgv));
172              
173             dMULTICALL;
174 70           I8 gimme = G_SCALAR;
175              
176 70 50         PUSH_MULTICALL(cv);
    50          
177             // multicall ready
178              
179 161 100         while (top >= 0) {
180 91           end = stack[top--];
181 91           start = stack[top--];
182              
183 91           x = nodes[end];
184 91           p = start - 1;
185              
186 238 100         for (j = start; j <= end - 1; ++j) {
187 147           GvSV(agv) = nodes[j]->value;
188 147           GvSV(bgv) = x->value;
189 147           MULTICALL;
190              
191 147 50         int test = SvIV(*PL_stack_sp);
192              
193 147 100         if (test < 1) {
194 84           p++;
195 84           tmp = nodes[p];
196 84           nodes[p] = nodes[j];
197 84           nodes[j] = tmp;
198             }
199             }
200              
201 91           tmp = nodes[++p];
202 91           nodes[p] = nodes[end];
203 91           nodes[end] = tmp;
204              
205 91 100         if (p - 1 > start) {
206 19           stack[++top] = start;
207 19           stack[++top] = p - 1;
208             }
209              
210 91 100         if (p + 1 < end) {
211 2           stack[++top] = p + 1;
212 2           stack[++top] = end;
213             }
214             }
215              
216 70 50         POP_MULTICALL;
    50          
217 70           }
218              
219             static
220 3           void _merge(pTHX_ SV *heap_ref, SV *heap_ref_a, SV *heap_ref_b) {
221 3 50         skewheap_t *heap = SKEW(heap_ref);
222              
223 3 50         skewheap_t *heap_a = SKEW(heap_ref_a);
224 3 50         skewheap_t *heap_b = SKEW(heap_ref_b);
225 3           skewnode_t *a = heap_a->root;
226 3           skewnode_t *b = heap_b->root;
227              
228 3           size_t size = heap_a->size + heap_b->size;
229              
230 3           skewnode_t *todo[size];
231 3           skewnode_t *nodes[size];
232             skewnode_t *node, *prev, *tmp_node;
233              
234 3           int tidx = 0;
235 3           int nidx = 0;
236             int i;
237              
238             // Set the new heap's size
239 3           heap->size = size;
240              
241             // Cut the right subtree from each path
242 3 50         if (a != NULL) todo[tidx++] = a;
243 3 50         if (b != NULL) todo[tidx++] = b;
244              
245 15 100         while (tidx > 0) {
246 12           node = todo[--tidx];
247              
248 12           tmp_node = new_node(aTHX_ node->value);
249 12           tmp_node->left = clone_node(aTHX_ node->left);
250              
251 12 100         if (node->right != NULL) {
252 6           todo[tidx] = node->right;
253 6           ++tidx;
254             }
255              
256 12           nodes[nidx] = tmp_node;
257 12           ++nidx;
258             }
259              
260 3 50         if (nidx > 0) {
261             // Sort the subtrees
262 3 50         if (nidx > 1) {
263 3           sort_nodes(aTHX_ nodes, nidx, heap->cmp);
264             }
265              
266             // Recombine subtrees
267 12 100         for (i = nidx; i > 1; --i) {
268 9           node = nodes[i - 1]; // last node
269 9           prev = nodes[i - 2]; // second to last node
270              
271             // Set penultimate node's right child to its left (and only) subtree
272 9 50         if (prev->left != NULL) {
273 9           prev->right = prev->left;
274             }
275              
276             // Set its left child to the ultimate node
277 9           prev->left = node;
278             }
279              
280 3           heap->root = nodes[0];
281             }
282              
283 3           return;
284             }
285              
286             static
287 77           void _merge_destructive(pTHX_ skewheap_t *heap, skewnode_t *a, skewnode_t *b) {
288 77           skewnode_t* todo[heap->size];
289 77           skewnode_t* nodes[heap->size];
290             skewnode_t* node;
291             skewnode_t* prev;
292 77           int tidx = 0;
293 77           int nidx = 0;
294             int i;
295              
296 77 100         if (a == NULL) {
297 5           heap->root = b;
298 5           return;
299             }
300 72 100         else if (b == NULL) {
301 5           heap->root = a;
302 5           return;
303             }
304              
305             // Cut the right subtree from each path
306 67           todo[tidx++] = a;
307 67           todo[tidx++] = b;
308              
309 243 100         while (tidx > 0) {
310 176           node = todo[--tidx];
311              
312 176 100         if (node->right != NULL) {
313 42           todo[tidx++] = node->right;
314 42           node->right = NULL;
315             }
316              
317 176           nodes[nidx++] = node;
318             }
319              
320 67 50         if (nidx == 0) {
321 0           heap->root = NULL;
322             }
323             else {
324             // Sort the subtrees
325 67 50         if (nidx > 1) {
326 67           sort_nodes(aTHX_ nodes, nidx, heap->cmp);
327             }
328              
329             // Recombine subtrees
330 176 100         for (i = nidx; i > 1; --i) {
331 109           node = nodes[i - 1]; // last node
332 109           prev = nodes[i - 2]; // second to last node
333              
334             // Set penultimate node's right child to its left (and only) subtree
335 109 100         if (prev->left != NULL) {
336 67           prev->right = prev->left;
337             }
338              
339             // Set its left child to the ultimate node
340 109           prev->left = node;
341             }
342              
343 77 100         heap->root = nodes[0];
344             }
345             }
346              
347             static
348 72           IV put_one(pTHX_ SV *ref, SV *value) {
349 72 50         skewheap_t *heap = SKEW(ref);
350             skewnode_t *node;
351              
352 72           node = new_node(aTHX_ value);
353 72           ++heap->size;
354              
355 72 100         if (heap->root == NULL) {
356 17           heap->root = node;
357             } else {
358 55           _merge_destructive(aTHX_ heap, heap->root, node);
359             }
360              
361 72           return heap->size;
362             }
363              
364             static
365 22           SV* take(pTHX_ SV *ref) {
366 22 50         skewheap_t *heap = SKEW(ref);
367 22           skewnode_t *root = heap->root;
368             SV *item;
369              
370 22 50         if (root != NULL) {
371 22           item = newSVsv(root->value);
372 22           --heap->size;
373 22           _merge_destructive(aTHX_ heap, root->left, root->right);
374 22           root->left = NULL;
375 22           root->right = NULL;
376 22           free_node(aTHX_ root);
377             }
378             else {
379 0           item = &PL_sv_undef;
380             }
381              
382 22           return item;
383             }
384              
385             static
386 8           SV* top(pTHX_ SV *ref) {
387 8 50         skewheap_t *heap = SKEW(ref);
388 8           return heap->root == NULL
389             ? &PL_sv_undef
390 8 100         : newSVsv(heap->root->value);
391             }
392              
393             static
394 19           IV size(pTHX_ SV *ref) {
395 19 50         skewheap_t *heap = SKEW(ref);
396 19           return heap->size;
397             }
398              
399             static
400 3           SV* merge(pTHX_ SV *heap_a, SV *heap_b) {
401 3 50         SV *new_heap = new(aTHX_ "SkewHeap", SKEW(heap_a)->cmp);
402 3           _merge(aTHX_ new_heap, heap_a, heap_b);
403 3           return new_heap;
404             }
405              
406             static
407 0           void _explain(pTHX_ SV *out, skewnode_t *node, int depth) {
408             int i;
409              
410 0 0         for (i = 0; i < depth; ++i) sv_catpvn(out, "--", 2);
411 0           sv_catpvf(out, "NODE<%p>\n", (void*)node);
412 0           ++depth;
413              
414 0 0         for (i = 0; i < depth; ++i) sv_catpvn(out, "--", 2);
415 0           sv_catpvf(out, "VALUE<%p>: ", (void*)node->value);
416 0           sv_catsv(out, sv_mortalcopy(node->value));
417 0           sv_catpvn(out, "\n", 1);
418              
419 0 0         if (node->left != NULL) {
420 0 0         for (i = 0; i < depth; ++i) sv_catpvn(out, "--", 2);
421 0           sv_catpvn(out, "LEFT:\n", 6);
422 0           _explain(aTHX_ out, node->left, depth + 1);
423             }
424              
425 0 0         if (node->right != NULL) {
426 0 0         for (i = 0; i < depth; ++i) sv_catpvn(out, "--", 2);
427 0           sv_catpvn(out, "RIGHT:\n", 7);
428 0           _explain(aTHX_ out, node->right, depth + 1);
429             }
430 0           }
431              
432             static
433 0           SV* explain(pTHX_ SV *ref) {
434 0 0         skewheap_t *heap = SKEW(ref);
435 0           SV *out = newSVpvn("", 0);
436              
437 0           sv_catpvn(out, "SKEWHEAP:\n", 10);
438              
439 0 0         if (heap->root != NULL) {
440 0           _explain(aTHX_ out, heap->root, 2);
441             }
442              
443 0           return out;
444             }
445              
446              
447             MODULE = SkewHeap PACKAGE = SkewHeap
448              
449             PROTOTYPES: ENABLE
450              
451             VERSIONCHECK: ENABLE
452              
453             SV* new(const char *class, SV *cmp)
454             PROTOTYPE: $&
455             CODE:
456 2           RETVAL = new(aTHX_ class, cmp);
457             OUTPUT:
458             RETVAL
459              
460             SV* skewheap(SV *cmp)
461             PROTOTYPE: &
462             CODE:
463 19           RETVAL = new(aTHX_ "SkewHeap", cmp);
464             OUTPUT:
465             RETVAL
466              
467             void DESTROY(SV *heap)
468             CODE:
469 24           DESTROY(aTHX_ heap);
470              
471             IV put_one(SV *heap, SV *value)
472             CODE:
473 0           RETVAL = put_one(aTHX_ heap, value);
474             OUTPUT:
475             RETVAL
476              
477             IV put(SV *heap, ...)
478             CODE:
479             size_t i;
480 97 100         for (i = 1; i < items; ++i) {
481 72           RETVAL = put_one(aTHX_ heap, ST(i));
482             }
483             OUTPUT:
484             RETVAL
485              
486             SV* take(SV *heap)
487             CODE:
488 22           RETVAL = take(aTHX_ heap);
489             OUTPUT:
490             RETVAL
491              
492             IV size(SV *heap)
493             CODE:
494 19           RETVAL = size(aTHX_ heap);
495             OUTPUT:
496             RETVAL
497              
498             SV* merge(SV *heap_a, SV *heap_b)
499             CODE:
500 3           RETVAL = merge(aTHX_ heap_a, heap_b);
501             OUTPUT:
502             RETVAL
503              
504             SV* top(SV *heap)
505             CODE:
506 8           RETVAL = top(aTHX_ heap);
507             OUTPUT:
508             RETVAL
509              
510             SV* to_array(SV *heap)
511             CODE:
512 0           RETVAL = to_array(aTHX_ heap);
513             OUTPUT:
514             RETVAL
515              
516             SV* explain(SV *heap)
517             CODE:
518 0           RETVAL = explain(aTHX_ heap);
519             OUTPUT:
520             RETVAL
521              
522