File Coverage

FastCalc.xs
Criterion Covered Total %
statement 125 127 98.4
branch 85 102 83.3
condition n/a
subroutine n/a
pod n/a
total 210 229 91.7


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              
7             /* for Perl prior to v5.7.1 */
8             #ifndef SvUOK
9             # define SvUOK(sv) SvIOK_UV(sv)
10             #endif
11              
12             /* for Perl v5.6 (RT #63859) */
13             #ifndef croak_xs_usage
14             # define croak_xs_usage croak
15             #endif
16              
17             static double XS_BASE = 0;
18             static double XS_BASE_LEN = 0;
19              
20             MODULE = Math::BigInt::FastCalc PACKAGE = Math::BigInt::FastCalc
21              
22             PROTOTYPES: DISABLE
23              
24             #############################################################################
25             # 2002-08-12 0.03 Tels unreleased
26             # * is_zero/is_one/is_odd/is_even/len work now (pass v1.61 tests)
27             # 2002-08-13 0.04 Tels unreleased
28             # * returns no/yes for is_foo() methods to be faster
29             # 2002-08-18 0.06alpha
30             # * added _num(), _inc() and _dec()
31             # 2002-08-25 0.06 Tels
32             # * added __strip_zeros(), _copy()
33             # 2004-08-13 0.07 Tels
34             # * added _is_two(), _is_ten(), _ten()
35             # 2007-04-02 0.08 Tels
36             # * plug leaks by creating mortals
37             # 2007-05-27 0.09 Tels
38             # * add _new()
39              
40             #define RETURN_MORTAL_INT(value) \
41             ST(0) = sv_2mortal(newSViv(value)); \
42             XSRETURN(1);
43              
44             BOOT:
45             {
46 10 50         if (items < 4)
47 0           croak("Usage: Math::BigInt::FastCalc::BOOT(package, version, base_len, base)");
48 10 50         XS_BASE_LEN = SvIV(ST(2));
49 10 50         XS_BASE = SvNV(ST(3));
50             }
51              
52             ##############################################################################
53             # _new
54              
55             SV *
56             _new(class, x)
57             SV* x
58             INIT:
59             STRLEN len;
60             char* cur;
61             STRLEN part_len;
62 80611           AV *av = newAV();
63              
64             CODE:
65 80611 50         if (SvUOK(x) && SvUV(x) < XS_BASE)
    0          
    0          
66             {
67             /* shortcut for integer arguments */
68 0 0         av_push (av, newSVuv( SvUV(x) ));
69             }
70             else
71             {
72             /* split the input (as string) into XS_BASE_LEN long parts */
73             /* in perl:
74             [ reverse(unpack("a" . ($il % $BASE_LEN+1)
75             . ("a$BASE_LEN" x ($il / $BASE_LEN)), $_[1])) ];
76             */
77 80611 100         cur = SvPV(x, len); /* convert to string & store length */
78 80611           cur += len; /* doing "cur = SvEND(x)" does not work! */
79 296456 100         # process the string from the back
80             while (len > 0)
81             {
82 215845           /* use either BASE_LEN or the amount of remaining digits */
83 215845 100         part_len = (STRLEN) XS_BASE_LEN;
84             if (part_len > len)
85 78557           {
86             part_len = len;
87             }
88 215845           /* processed so many digits */
89 215845           cur -= part_len;
90             len -= part_len;
91 215845 50         /* printf ("part '%s' (part_len: %i, len: %i, BASE_LEN: %i)\n", cur, part_len, len, XS_BASE_LEN); */
92             if (part_len > 0)
93 215845           {
94             av_push (av, newSVpvn(cur, part_len) );
95             }
96             }
97 80611           }
98             RETVAL = newRV_noinc((SV *)av);
99             OUTPUT:
100             RETVAL
101              
102             ##############################################################################
103             # _copy
104              
105             void
106             _copy(class, x)
107             SV* x
108             INIT:
109             AV* a;
110             AV* a2;
111             SSize_t elems;
112              
113             CODE:
114 40537           a = (AV*)SvRV(x); /* ref to aray, don't check ref */
115 40537           elems = av_len(a); /* number of elems in array */
116 40537           a2 = (AV*)sv_2mortal((SV*)newAV());
117 40537           av_extend (a2, elems); /* pre-padd */
118 196601 100         while (elems >= 0)
119             {
120             /* av_store( a2, elems, newSVsv( (SV*)*av_fetch(a, elems, 0) ) ); */
121              
122             /* looking and trying to preserve IV is actually slower when copying */
123             /* temp = (SV*)*av_fetch(a, elems, 0);
124             if (SvIOK(temp))
125             {
126             av_store( a2, elems, newSViv( SvIV( (SV*)*av_fetch(a, elems, 0) )));
127             }
128             else
129             {
130             av_store( a2, elems, newSVnv( SvNV( (SV*)*av_fetch(a, elems, 0) )));
131             }
132             */
133 156064 100         av_store( a2, elems, newSVnv( SvNV( (SV*)*av_fetch(a, elems, 0) )));
134 156064           elems--;
135             }
136 40537           ST(0) = sv_2mortal( newRV_inc((SV*) a2) );
137              
138             ##############################################################################
139             # __strip_zeros (also check for empty arrays from div)
140              
141             void
142             __strip_zeros(x)
143             SV* x
144             INIT:
145             AV* a;
146             SV* temp;
147             SSize_t elems;
148             SSize_t index;
149              
150             CODE:
151 9           a = (AV*)SvRV(x); /* ref to aray, don't check ref */
152 9           elems = av_len(a); /* number of elems in array */
153 9           ST(0) = x; /* we return x */
154 9 100         if (elems == -1)
155             {
156 1           av_push (a, newSViv(0)); /* correct empty arrays */
157 1           XSRETURN(1);
158             }
159 8 100         if (elems == 0)
160             {
161 2           XSRETURN(1); /* nothing to do since only one elem */
162             }
163 6           index = elems;
164 15 100         while (index > 0)
165             {
166 14           temp = *av_fetch(a, index, 0); /* fetch ptr to current element */
167 14 50         if (SvNV(temp) != 0)
    100          
168             {
169 5           break;
170             }
171 9           index--;
172             }
173 6 100         if (index < elems)
174             {
175 4           index = elems - index;
176 13 100         while (index-- > 0)
177             {
178 9           av_pop (a);
179             }
180             }
181 6           XSRETURN(1);
182              
183             ##############################################################################
184             # decrement (subtract one)
185              
186             void
187             _dec(class,x)
188             SV* x
189             INIT:
190             AV* a;
191             SV* temp;
192             SSize_t elems;
193             SSize_t index;
194             NV MAX;
195              
196             CODE:
197 303           a = (AV*)SvRV(x); /* ref to aray, don't check ref */
198 303           elems = av_len(a); /* number of elems in array */
199 303           ST(0) = x; /* we return x */
200              
201 303           MAX = XS_BASE - 1;
202 303           index = 0;
203 319 50         while (index <= elems)
204             {
205 319           temp = *av_fetch(a, index, 0); /* fetch ptr to current element */
206 319 100         sv_setnv (temp, SvNV(temp)-1); /* decrement */
207 319 50         if (SvNV(temp) >= 0)
    100          
208             {
209 303           break; /* early out */
210             }
211 16           sv_setnv (temp, MAX); /* overflow, so set this to $MAX */
212 16           index++;
213             }
214             /* do have more than one element? */
215             /* (more than one because [0] should be kept as single-element) */
216 303 100         if (elems > 0)
217             {
218 165           temp = *av_fetch(a, elems, 0); /* fetch last element */
219 165 100         if (SvIV(temp) == 0) /* did last elem overflow? */
    100          
220             {
221 3           av_pop(a); /* yes, so shrink array */
222             /* aka remove leading zeros */
223             }
224             }
225 303           XSRETURN(1); /* return x */
226              
227             ##############################################################################
228             # increment (add one)
229              
230             void
231             _inc(class,x)
232             SV* x
233             INIT:
234             AV* a;
235             SV* temp;
236             SSize_t elems;
237             SSize_t index;
238             NV BASE;
239              
240             CODE:
241 2296           a = (AV*)SvRV(x); /* ref to aray, don't check ref */
242 2296           elems = av_len(a); /* number of elems in array */
243 2296           ST(0) = x; /* we return x */
244              
245 2296           BASE = XS_BASE;
246 2296           index = 0;
247 2312 100         while (index <= elems)
248             {
249 2309           temp = *av_fetch(a, index, 0); /* fetch ptr to current element */
250 2309 100         sv_setnv (temp, SvNV(temp)+1);
251 2309 50         if (SvNV(temp) < BASE)
    100          
252             {
253 2293           XSRETURN(1); /* return (early out) */
254             }
255 16           sv_setiv (temp, 0); /* overflow, so set this elem to 0 */
256 16           index++;
257             }
258 3           temp = *av_fetch(a, elems, 0); /* fetch last element */
259 3 50         if (SvIV(temp) == 0) /* did last elem overflow? */
    50          
260             {
261 3           av_push(a, newSViv(1)); /* yes, so extend array by 1 */
262             }
263 3           XSRETURN(1); /* return x */
264              
265             ##############################################################################
266              
267             SV *
268             _zero(class)
269             ALIAS:
270             _one = 1
271             _two = 2
272             _ten = 10
273             PREINIT:
274 29366           AV *av = newAV();
275             CODE:
276 29366           av_push (av, newSViv( ix ));
277 29366           RETVAL = newRV_noinc((SV *)av);
278             OUTPUT:
279             RETVAL
280              
281             ##############################################################################
282              
283             void
284             _is_even(class, x)
285             SV* x
286             ALIAS:
287             _is_odd = 1
288             INIT:
289             AV* a;
290             SV* temp;
291              
292             CODE:
293 219           a = (AV*)SvRV(x); /* ref to aray, don't check ref */
294 219           temp = *av_fetch(a, 0, 0); /* fetch first element */
295 219 100         ST(0) = sv_2mortal(boolSV((SvIV(temp) & 1) == ix));
    100          
296              
297             ##############################################################################
298              
299             void
300             _is_zero(class, x)
301             SV* x
302             ALIAS:
303             _is_one = 1
304             _is_two = 2
305             _is_ten = 10
306             INIT:
307             AV* a;
308              
309             CODE:
310 176590           a = (AV*)SvRV(x); /* ref to aray, don't check ref */
311 176590 100         if ( av_len(a) != 0)
312             {
313 85162           ST(0) = &PL_sv_no; /* len != 1, can't be '0' */
314             }
315             else
316             {
317 91428           SV *const temp = *av_fetch(a, 0, 0); /* fetch first element */
318 91428 100         ST(0) = boolSV(SvIV(temp) == ix);
    100          
319             }
320 176590           XSRETURN(1);
321              
322             ##############################################################################
323              
324             void
325             _len(class,x)
326             SV* x
327             INIT:
328             AV* a;
329             SV* temp;
330             IV elems;
331             STRLEN len;
332              
333             CODE:
334 48310           a = (AV*)SvRV(x); /* ref to aray, don't check ref */
335 48310           elems = av_len(a); /* number of elems in array */
336 48310           temp = *av_fetch(a, elems, 0); /* fetch last element */
337 48310 100         SvPV(temp, len); /* convert to string & store length */
338 48310           len += (IV) XS_BASE_LEN * elems;
339 48310           ST(0) = sv_2mortal(newSViv(len));
340              
341             ##############################################################################
342              
343             void
344             _acmp(class, cx, cy);
345             SV* cx
346             SV* cy
347             INIT:
348             AV* array_x;
349             AV* array_y;
350             SSize_t elemsx, elemsy, diff;
351             SV* tempx;
352             SV* tempy;
353             STRLEN lenx;
354             STRLEN leny;
355             NV diff_nv;
356             SSize_t diff_str;
357              
358             CODE:
359 45409           array_x = (AV*)SvRV(cx); /* ref to aray, don't check ref */
360 45409           array_y = (AV*)SvRV(cy); /* ref to aray, don't check ref */
361 45409           elemsx = av_len(array_x);
362 45409           elemsy = av_len(array_y);
363 45409           diff = elemsx - elemsy; /* difference */
364              
365 45409 100         if (diff > 0)
366             {
367 27077           RETURN_MORTAL_INT(1); /* len differs: X > Y */
368             }
369 44916 100         else if (diff < 0)
370             {
371 41           RETURN_MORTAL_INT(-1); /* len differs: X < Y */
372             }
373             /* both have same number of elements, so check length of last element
374             and see if it differs */
375 44875           tempx = *av_fetch(array_x, elemsx, 0); /* fetch last element */
376 44875           tempy = *av_fetch(array_y, elemsx, 0); /* fetch last element */
377 44875 100         SvPV(tempx, lenx); /* convert to string & store length */
378 44875 100         SvPV(tempy, leny); /* convert to string & store length */
379 44875           diff_str = (SSize_t)lenx - (SSize_t)leny;
380 44875 100         if (diff_str > 0)
381             {
382 10834           RETURN_MORTAL_INT(1); /* same len, but first elems differs in len */
383             }
384 34041 100         if (diff_str < 0)
385             {
386 1365           RETURN_MORTAL_INT(-1); /* same len, but first elems differs in len */
387             }
388             /* same number of digits, so need to make a full compare */
389 32676           diff_nv = 0;
390 58674 100         while (elemsx >= 0)
391             {
392 40342           tempx = *av_fetch(array_x, elemsx, 0); /* fetch curr x element */
393 40342           tempy = *av_fetch(array_y, elemsx, 0); /* fetch curr y element */
394 40342 100         diff_nv = SvNV(tempx) - SvNV(tempy);
    100          
395 40342 100         if (diff_nv != 0)
396             {
397 14344           break;
398             }
399 25998           elemsx--;
400             }
401 32676 100         if (diff_nv > 0)
402             {
403 12621           RETURN_MORTAL_INT(1);
404             }
405 20055 100         if (diff_nv < 0)
406             {
407 1723           RETURN_MORTAL_INT(-1);
408             }
409 18332           ST(0) = sv_2mortal(newSViv(0)); /* X and Y are equal */