File Coverage

XS.xs
Criterion Covered Total %
statement 104 130 80.0
branch 82 142 57.7
condition n/a
subroutine n/a
pod n/a
total 186 272 68.3


line stmt bran cond sub pod time code
1             #define PERL_NO_GET_CONTEXT
2             #include "EXTERN.h"
3             #include "perl.h"
4             #include "XSUB.h"
5              
6             #include "ppport.h"
7              
8             #define iParent(i) (((i)-1) / 2)
9             #define iLeftChild(i) ((2*(i)) + 1)
10             #define iRightChild(i) ((2*(i)) + 2)
11              
12             #define OUT_OF_ORDER(a,tmpsv,child_is_magic,parent_is_magic,child,parent,is_min) \
13             ( ( ( (child_is_magic) || (parent_is_magic) ) \
14             ? (((tmpsv) = amagic_call((a)[(child)], (a)[(parent)], is_min & 2 ? sgt_amg : gt_amg, 0)) && SvTRUE((tmpsv))) \
15             : ( ((is_min & 2) ? Perl_sv_cmp(aTHX_ (a)[(child)], a[(parent)]) \
16             : my_Perl_do_ncmp(aTHX_ (a)[(child)], a[(parent)])) > 0) )\
17             ? !(is_min & 1) : (is_min & 1) )
18              
19             #define FORCE_SCALAR(fakeop) \
20             STMT_START { \
21             SAVEOP(); \
22             Copy(PL_op, &fakeop, 1, OP); \
23             fakeop.op_flags = OPf_WANT_SCALAR; \
24             PL_op = &fakeop; \
25             } STMT_END
26              
27              
28             #ifdef Perl_do_ncmp
29             #define my_Perl_do_ncmp Perl_do_ncmp
30             #else
31             /* compare left and right SVs. Returns:
32             * -1: <
33             * 0: ==
34             * 1: >
35             * 2: left or right was a NaN
36             */
37             I32
38 124           my_Perl_do_ncmp(pTHX_ SV* const left, SV * const right)
39             {
40             PERL_ARGS_ASSERT_DO_NCMP;
41             #ifdef PERL_PRESERVE_IVUV
42             /* Fortunately it seems NaN isn't IOK */
43 124 50         if (SvIV_please_nomg(right) && SvIV_please_nomg(left)) {
    0          
    100          
    100          
    50          
    100          
44 117 50         if (!SvIsUV(left)) {
45 117           const IV leftiv = SvIVX(left);
46 117 50         if (!SvIsUV(right)) {
47             /* ## IV <=> IV ## */
48 117           const IV rightiv = SvIVX(right);
49 117           return (leftiv > rightiv) - (leftiv < rightiv);
50             }
51             /* ## IV <=> UV ## */
52 0 0         if (leftiv < 0)
53             /* As (b) is a UV, it's >=0, so it must be < */
54 0           return -1;
55             {
56 0           const UV rightuv = SvUVX(right);
57 0           return ((UV)leftiv > rightuv) - ((UV)leftiv < rightuv);
58             }
59             }
60              
61 0 0         if (SvIsUV(right)) {
62             /* ## UV <=> UV ## */
63 0           const UV leftuv = SvUVX(left);
64 0           const UV rightuv = SvUVX(right);
65 0           return (leftuv > rightuv) - (leftuv < rightuv);
66             }
67             /* ## UV <=> IV ## */
68             {
69 0           const IV rightiv = SvIVX(right);
70 0 0         if (rightiv < 0)
71             /* As (a) is a UV, it's >=0, so it cannot be < */
72 0           return 1;
73             {
74 0           const UV leftuv = SvUVX(left);
75 0           return (leftuv > (UV)rightiv) - (leftuv < (UV)rightiv);
76             }
77             }
78             NOT_REACHED; /* NOTREACHED */
79             }
80             #endif
81             {
82 7           NV const lnv = SvNV_nomg(left);
83 7           NV const rnv = SvNV_nomg(right);
84              
85             #if defined(NAN_COMPARE_BROKEN) && defined(Perl_isnan)
86             if (Perl_isnan(lnv) || Perl_isnan(rnv)) {
87             return 2;
88             }
89             return (lnv > rnv) - (lnv < rnv);
90             #else
91 7 100         if (lnv < rnv)
92 5           return -1;
93 2 50         if (lnv > rnv)
94 2           return 1;
95 0 0         if (lnv == rnv)
96 0           return 0;
97 0           return 2;
98             #endif
99             }
100             }
101             #endif
102              
103              
104 8           I32 sift_up(pTHX_ SV **a, ssize_t start, ssize_t end, I32 is_min) {
105             /*start represents the limit of how far up the heap to sift.
106             end is the node to sift up. */
107 8           ssize_t child = end;
108 8           SV *tmpsv = NULL;
109             I32 child_is_magic;
110 8           I32 swapped = 0;
111 8           SvGETMAGIC(a[child]);
112 8 50         child_is_magic= SvAMAGIC(a[child]);
    0          
    0          
113              
114 22 100         while (child > start) {
115 18           ssize_t parent = iParent(child);
116             I32 parent_is_magic;
117 18           SvGETMAGIC(a[parent]);
118 18 50         parent_is_magic= SvAMAGIC(a[parent]);
    0          
    0          
119 18 50         if ( OUT_OF_ORDER(a,tmpsv,child_is_magic,parent_is_magic,child,parent,is_min) ) {
    50          
    0          
    0          
    0          
    50          
    100          
    100          
120 14           SV *swap_tmp= a[parent];
121 14           a[parent]= a[child];
122 14           a[child]= swap_tmp;
123              
124 14           child = parent; /* repeat to continue sifting up the parent now */
125 14           child_is_magic= parent_is_magic;
126 14           swapped++;
127             }
128             else {
129 4           return swapped;
130             }
131             }
132 4           return swapped;
133             }
134              
135             /*Repair the heap whose root element is at index 'start', assuming the heaps rooted at its children are valid*/
136 75468           I32 sift_down(pTHX_ SV **a, ssize_t start, ssize_t end, I32 is_min) {
137 75468           ssize_t root = start;
138 75468 100         I32 root_is_magic = SvAMAGIC(a[root]);
    50          
    50          
139 75468           I32 swapped = 0;
140              
141 186150 100         while (iLeftChild(root) <= end) { /* While the root has at least one child */
142 140247           ssize_t child = iLeftChild(root); /* Left child of root */
143 140247 100         I32 child_is_magic = SvAMAGIC(a[child]);
    50          
    50          
144 140247           ssize_t swap = root; /* Keeps track of child to swap with */
145 140247           I32 swap_is_magic = root_is_magic;
146 140247           SV *tmpsv = NULL;
147              
148             /* if the root is smaller than the left child
149             * then the swap is with the left child */
150 140247 100         if ( OUT_OF_ORDER(a,tmpsv,child_is_magic,swap_is_magic,child,swap,is_min) ) {
    50          
    50          
    50          
    100          
    50          
    100          
    100          
151 94227           swap = child;
152 94227           swap_is_magic = child_is_magic;
153             }
154             /* if there is a right child and the right child is larger than the root or the left child
155             * then the swap is with the right child */
156 140247 100         if (child+1 <= end) {
157 139637 100         child_is_magic = SvAMAGIC(a[child+1]);
    50          
    50          
158 139637 100         if ( OUT_OF_ORDER(a,tmpsv,child_is_magic,swap_is_magic,child+1,swap,is_min) ) {
    50          
    50          
    50          
    100          
    50          
    100          
    100          
159 54926           swap = child + 1;
160 54926           swap_is_magic = child_is_magic;
161             }
162             }
163             /* check if we need to swap or if this tree is in heap-order */
164 140247 100         if (swap == root) {
165             /* The root is larger than both children, and as we assume the heaps rooted at the children are valid
166             * then we know we can stop. */
167 29565           return swapped;
168             } else {
169             /* swap the root with the largest child */
170 110682           SV *tmp= a[root];
171 110682           a[root]= a[swap];
172 110682           a[swap]= tmp;
173             /* continue sifting down the child by setting the root to the chosen child
174             * effectively we sink down the tree towards the leafs */
175 110682           root = swap;
176 110682           root_is_magic = swap_is_magic;
177 110682           swapped++;
178             }
179             }
180 45903           return swapped;
181             }
182              
183             /* this is O(N log N) */
184 0           void heapify_with_sift_up(pTHX_ SV **a, ssize_t count, I32 is_min) {
185 0           ssize_t end = 1; /* end is assigned the index of the first (left) child of the root */
186              
187 0 0         while (end < count) {
188             /*sift up the node at index end to the proper place such that all nodes above
189             the end index are in heap order */
190 0           (void)sift_up(aTHX_ a, 0, end, is_min);
191 0           end++;
192             }
193             /* after sifting up the last node all nodes are in heap order */
194 0           }
195              
196             /* this is O(N) */
197 310           void heapify_with_sift_down(pTHX_ SV **a, ssize_t count, I32 is_min) {
198             /*start is assigned the index in 'a' of the last parent node
199             the last element in a 0-based array is at index count-1; find the parent of that element */
200 310           ssize_t start = iParent(count-1);
201              
202 75470 100         while (start >= 0) {
203             /* sift down the node at index 'start' to the proper place such that all nodes below
204             the start index are in heap order */
205 75160           (void)sift_down(aTHX_ a, start, count - 1, is_min);
206             /* go to the next parent node */
207 75160           start--;
208             }
209             /* after sifting down the root all nodes/elements are in heap order */
210 310           }
211              
212              
213             MODULE = Algorithm::Heapify::XS PACKAGE = Algorithm::Heapify::XS
214              
215             void
216             max_heapify(av)
217             AV *av
218             PROTOTYPE: \@
219             ALIAS:
220             max_heapify = 0
221             min_heapify = 1
222             maxstr_heapify = 2
223             minstr_heapify = 3
224             PREINIT:
225             OP fakeop;
226             I32 count;
227             PPCODE:
228 310           FORCE_SCALAR(fakeop);
229 310 50         count = av_top_index(av)+1;
230 310 50         if ( count ) {
231 310           heapify_with_sift_down(aTHX_ AvARRAY(av),count,ix);
232 310           ST(0)= AvARRAY(av)[0];
233 310           XSRETURN(1);
234             }
235             else {
236 0           XSRETURN(0);
237             }
238              
239             void
240             max_heap_shift(av)
241             AV *av
242             PROTOTYPE: \@
243             ALIAS:
244             max_heap_shift = 0
245             min_heap_shift = 1
246             maxstr_heap_shift = 2
247             minstr_heap_shift = 3
248             PREINIT:
249             OP fakeop;
250             I32 top;
251             I32 count;
252             PPCODE:
253 324           FORCE_SCALAR(fakeop);
254 324 50         top= av_top_index(av);
255 324           count= top+1;
256 324 50         if (count) {
257 324           SV *tmp= AvARRAY(av)[0];
258 324           AvARRAY(av)[0]= AvARRAY(av)[top];
259 324           AvARRAY(av)[top]= tmp;
260 324           ST(0)= av_pop(av);
261 324 100         if (count > 2)
262 304           sift_down(aTHX_ AvARRAY(av),0,top-1,ix);
263 324           XSRETURN(1);
264             }
265             else {
266 0           XSRETURN(0);
267             }
268              
269             void
270             max_heap_push(av,sv)
271             AV *av
272             SV *sv
273             PROTOTYPE: \@$
274             ALIAS:
275             max_heap_push = 0
276             min_heap_push = 1
277             maxstr_heap_push = 2
278             minstr_heap_push = 3
279             PREINIT:
280             OP fakeop;
281             I32 top;
282             I32 count;
283             PPCODE:
284 4           FORCE_SCALAR(fakeop);
285 4           av_push(av,newSVsv(sv));
286 4 50         top= av_top_index(av);
287 4           count= top+1;
288 4           sift_up(aTHX_ AvARRAY(av),0,top,ix);
289 4           ST(0)= AvARRAY(av)[0];
290 4           XSRETURN(1);
291              
292             void
293             max_heap_adjust_top(av)
294             AV *av
295             PROTOTYPE: \@
296             ALIAS:
297             max_heap_adjust_top = 0
298             min_heap_adjust_top = 1
299             maxstr_heap_adjust_top = 2
300             minstr_heap_adjust_top = 3
301             PREINIT:
302             OP fakeop;
303             I32 top;
304             I32 count;
305             PPCODE:
306 2           FORCE_SCALAR(fakeop);
307 2 50         top= av_top_index(av);
308 2           count= top+1;
309 2 50         if ( count ) {
310 2           (void)sift_down(aTHX_ AvARRAY(av),0,top,ix);
311 2           ST(0)= AvARRAY(av)[0];
312 2           XSRETURN(1);
313             } else {
314 0           XSRETURN(0);
315             }
316              
317             void
318             max_heap_adjust_item(av,idx=0)
319             AV *av
320             I32 idx;
321             PROTOTYPE: \@;$
322             ALIAS:
323             max_heap_adjust_item = 0
324             min_heap_adjust_item = 1
325             maxstr_heap_adjust_item = 2
326             minstr_heap_adjust_item = 3
327             PREINIT:
328             OP fakeop;
329             I32 top;
330             I32 count;
331             PPCODE:
332 4           FORCE_SCALAR(fakeop);
333 4 50         top= av_top_index(av);
334 4           count= top+1;
335 4 50         if ( idx < count ) {
336 4 50         if (!idx || !sift_up(aTHX_ AvARRAY(av),0,idx,ix))
    100          
337 2           (void)sift_down(aTHX_ AvARRAY(av),idx,top,ix);
338 4           ST(0)= AvARRAY(av)[0];
339 4           XSRETURN(1);
340             } else {
341 0           XSRETURN(0);
342             }