This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
blead is upstream for Math-BigInt
[perl5.git] / cpan / Math-BigInt-FastCalc / FastCalc.xs
CommitLineData
062a4e99
T
1#include "EXTERN.h"
2#include "perl.h"
3#include "XSUB.h"
4
206957a7
SP
5/* for Perl prior to v5.7.1 */
6#ifndef SvUOK
7# define SvUOK(sv) SvIOK_UV(sv)
8#endif
9
062a4e99
T
10double XS_BASE = 0;
11double XS_BASE_LEN = 0;
12
13MODULE = Math::BigInt::FastCalc PACKAGE = Math::BigInt::FastCalc
14
e31720c4
RGS
15PROTOTYPES: DISABLE
16
062a4e99
T
17 #############################################################################
18 # 2002-08-12 0.03 Tels unreleased
19 # * is_zero/is_one/is_odd/is_even/len work now (pass v1.61 tests)
20 # 2002-08-13 0.04 Tels unreleased
21 # * returns no/yes for is_foo() methods to be faster
22 # 2002-08-18 0.06alpha
23 # * added _num(), _inc() and _dec()
24 # 2002-08-25 0.06 Tels
25 # * added __strip_zeros(), _copy()
26 # 2004-08-13 0.07 Tels
27 # * added _is_two(), _is_ten(), _ten()
7d193e39
T
28 # 2007-04-02 0.08 Tels
29 # * plug leaks by creating mortals
5ed38b1a
T
30 # 2007-05-27 0.09 Tels
31 # * add _new()
7d193e39
T
32
33#define RETURN_MORTAL_INT(value) \
34 ST(0) = sv_2mortal(newSViv(value)); \
35 XSRETURN(1);
36
37#define RETURN_MORTAL_BOOL(temp, comp) \
38 ST(0) = sv_2mortal(boolSV( SvIV(temp) == comp));
39
40#define CONSTANT_OBJ(int) \
41 RETVAL = newAV(); \
42 sv_2mortal((SV*)RETVAL); \
43 av_push (RETVAL, newSViv( int ));
062a4e99
T
44
45void
46_set_XS_BASE(BASE, BASE_LEN)
47 SV* BASE
48 SV* BASE_LEN
49
50 CODE:
51 XS_BASE = SvNV(BASE);
52 XS_BASE_LEN = SvIV(BASE_LEN);
53
54##############################################################################
5ed38b1a
T
55# _new
56
57AV *
58_new(class, x)
59 SV* x
60 INIT:
61 STRLEN len;
62 char* cur;
a436f3ee 63 STRLEN part_len;
5ed38b1a
T
64
65 CODE:
66 /* create the array */
67 RETVAL = newAV();
68 sv_2mortal((SV*)RETVAL);
206957a7 69 if (SvUOK(x) && SvUV(x) < XS_BASE)
5ed38b1a
T
70 {
71 /* shortcut for integer arguments */
4e99e077 72 av_push (RETVAL, newSVuv( SvUV(x) ));
5ed38b1a
T
73 }
74 else
75 {
76 /* split the input (as string) into XS_BASE_LEN long parts */
77 /* in perl:
78 [ reverse(unpack("a" . ($il % $BASE_LEN+1)
79 . ("a$BASE_LEN" x ($il / $BASE_LEN)), $_[1])) ];
80 */
81 cur = SvPV(x, len); /* convert to string & store length */
82 cur += len; /* doing "cur = SvEND(x)" does not work! */
83 # process the string from the back
84 while (len > 0)
85 {
86 /* use either BASE_LEN or the amount of remaining digits */
a436f3ee 87 part_len = (STRLEN) XS_BASE_LEN;
5ed38b1a
T
88 if (part_len > len)
89 {
90 part_len = len;
91 }
92 /* processed so many digits */
93 cur -= part_len;
94 len -= part_len;
95 /* printf ("part '%s' (part_len: %i, len: %i, BASE_LEN: %i)\n", cur, part_len, len, XS_BASE_LEN); */
96 if (part_len > 0)
97 {
98 av_push (RETVAL, newSVpvn(cur, part_len) );
99 }
100 }
101 }
102 OUTPUT:
103 RETVAL
104
105##############################################################################
062a4e99
T
106# _copy
107
108void
109_copy(class, x)
110 SV* x
111 INIT:
112 AV* a;
113 AV* a2;
114 I32 elems;
115
116 CODE:
117 a = (AV*)SvRV(x); /* ref to aray, don't check ref */
118 elems = av_len(a); /* number of elems in array */
119 a2 = (AV*)sv_2mortal((SV*)newAV());
206957a7 120 av_extend (a2, elems); /* pre-padd */
062a4e99
T
121 while (elems >= 0)
122 {
123 /* av_store( a2, elems, newSVsv( (SV*)*av_fetch(a, elems, 0) ) ); */
124
125 /* looking and trying to preserve IV is actually slower when copying */
126 /* temp = (SV*)*av_fetch(a, elems, 0);
127 if (SvIOK(temp))
128 {
129 av_store( a2, elems, newSViv( SvIV( (SV*)*av_fetch(a, elems, 0) )));
130 }
131 else
132 {
133 av_store( a2, elems, newSVnv( SvNV( (SV*)*av_fetch(a, elems, 0) )));
134 }
135 */
136 av_store( a2, elems, newSVnv( SvNV( (SV*)*av_fetch(a, elems, 0) )));
137 elems--;
138 }
139 ST(0) = sv_2mortal( newRV_inc((SV*) a2) );
140
141##############################################################################
142# __strip_zeros (also check for empty arrays from div)
143
144void
145__strip_zeros(x)
146 SV* x
147 INIT:
148 AV* a;
149 SV* temp;
150 I32 elems;
151 I32 index;
152
153 CODE:
154 a = (AV*)SvRV(x); /* ref to aray, don't check ref */
155 elems = av_len(a); /* number of elems in array */
156 ST(0) = x; /* we return x */
157 if (elems == -1)
158 {
159 av_push (a, newSViv(0)); /* correct empty arrays */
160 XSRETURN(1);
161 }
162 if (elems == 0)
163 {
164 XSRETURN(1); /* nothing to do since only one elem */
165 }
166 index = elems;
167 while (index > 0)
168 {
169 temp = *av_fetch(a, index, 0); /* fetch ptr to current element */
170 if (SvNV(temp) != 0)
171 {
172 break;
173 }
174 index--;
175 }
176 if (index < elems)
177 {
178 index = elems - index;
179 while (index-- > 0)
180 {
181 av_pop (a);
182 }
183 }
184 XSRETURN(1);
185
186##############################################################################
187# decrement (subtract one)
188
189void
190_dec(class,x)
191 SV* x
192 INIT:
193 AV* a;
194 SV* temp;
195 I32 elems;
196 I32 index;
197 NV MAX;
198
199 CODE:
200 a = (AV*)SvRV(x); /* ref to aray, don't check ref */
201 elems = av_len(a); /* number of elems in array */
202 ST(0) = x; /* we return x */
203
204 MAX = XS_BASE - 1;
205 index = 0;
206 while (index <= elems)
207 {
208 temp = *av_fetch(a, index, 0); /* fetch ptr to current element */
206957a7 209 sv_setnv (temp, SvNV(temp)-1); /* decrement */
062a4e99
T
210 if (SvNV(temp) >= 0)
211 {
212 break; /* early out */
213 }
214 sv_setnv (temp, MAX); /* overflow, so set this to $MAX */
215 index++;
216 }
217 /* do have more than one element? */
218 /* (more than one because [0] should be kept as single-element) */
219 if (elems > 0)
220 {
221 temp = *av_fetch(a, elems, 0); /* fetch last element */
222 if (SvIV(temp) == 0) /* did last elem overflow? */
223 {
224 av_pop(a); /* yes, so shrink array */
225 /* aka remove leading zeros */
226 }
227 }
228 XSRETURN(1); /* return x */
229
230##############################################################################
231# increment (add one)
232
233void
234_inc(class,x)
235 SV* x
236 INIT:
237 AV* a;
238 SV* temp;
239 I32 elems;
240 I32 index;
241 NV BASE;
242
243 CODE:
244 a = (AV*)SvRV(x); /* ref to aray, don't check ref */
245 elems = av_len(a); /* number of elems in array */
246 ST(0) = x; /* we return x */
247
248 BASE = XS_BASE;
249 index = 0;
250 while (index <= elems)
251 {
252 temp = *av_fetch(a, index, 0); /* fetch ptr to current element */
253 sv_setnv (temp, SvNV(temp)+1);
254 if (SvNV(temp) < BASE)
255 {
256 XSRETURN(1); /* return (early out) */
257 }
258 sv_setiv (temp, 0); /* overflow, so set this elem to 0 */
259 index++;
260 }
261 temp = *av_fetch(a, elems, 0); /* fetch last element */
262 if (SvIV(temp) == 0) /* did last elem overflow? */
263 {
264 av_push(a, newSViv(1)); /* yes, so extend array by 1 */
265 }
266 XSRETURN(1); /* return x */
267
268##############################################################################
269# Make a number (scalar int/float) from a BigInt object
270
271void
272_num(class,x)
273 SV* x
274 INIT:
275 AV* a;
276 NV fac;
277 SV* temp;
278 NV num;
279 I32 elems;
280 I32 index;
281 NV BASE;
282
283 CODE:
284 a = (AV*)SvRV(x); /* ref to aray, don't check ref */
285 elems = av_len(a); /* number of elems in array */
286
287 if (elems == 0) /* only one element? */
288 {
289 ST(0) = *av_fetch(a, 0, 0); /* fetch first (only) element */
290 XSRETURN(1); /* return it */
291 }
292 fac = 1.0; /* factor */
293 index = 0;
294 num = 0.0;
295 BASE = XS_BASE;
296 while (index <= elems)
297 {
298 temp = *av_fetch(a, index, 0); /* fetch current element */
299 num += fac * SvNV(temp);
300 fac *= BASE;
301 index++;
302 }
303 ST(0) = newSVnv(num);
304
305##############################################################################
306
caa64001
T
307AV *
308_zero(class)
062a4e99 309 CODE:
caa64001
T
310 CONSTANT_OBJ(0)
311 OUTPUT:
312 RETVAL
062a4e99
T
313
314##############################################################################
315
caa64001 316AV *
062a4e99 317_one(class)
062a4e99 318 CODE:
caa64001
T
319 CONSTANT_OBJ(1)
320 OUTPUT:
321 RETVAL
062a4e99
T
322
323##############################################################################
324
caa64001 325AV *
062a4e99 326_two(class)
062a4e99 327 CODE:
caa64001
T
328 CONSTANT_OBJ(2)
329 OUTPUT:
330 RETVAL
062a4e99
T
331
332##############################################################################
333
caa64001 334AV *
062a4e99 335_ten(class)
062a4e99 336 CODE:
caa64001
T
337 CONSTANT_OBJ(10)
338 OUTPUT:
339 RETVAL
062a4e99
T
340
341##############################################################################
342
343void
344_is_even(class, x)
345 SV* x
346 INIT:
347 AV* a;
348 SV* temp;
349
350 CODE:
351 a = (AV*)SvRV(x); /* ref to aray, don't check ref */
352 temp = *av_fetch(a, 0, 0); /* fetch first element */
7d193e39 353 ST(0) = sv_2mortal(boolSV((SvIV(temp) & 1) == 0));
062a4e99
T
354
355##############################################################################
356
357void
358_is_odd(class, x)
359 SV* x
360 INIT:
361 AV* a;
362 SV* temp;
363
364 CODE:
365 a = (AV*)SvRV(x); /* ref to aray, don't check ref */
366 temp = *av_fetch(a, 0, 0); /* fetch first element */
7d193e39 367 ST(0) = sv_2mortal(boolSV((SvIV(temp) & 1) != 0));
062a4e99
T
368
369##############################################################################
370
371void
372_is_one(class, x)
373 SV* x
374 INIT:
375 AV* a;
376 SV* temp;
377
378 CODE:
379 a = (AV*)SvRV(x); /* ref to aray, don't check ref */
380 if ( av_len(a) != 0)
381 {
382 ST(0) = &PL_sv_no;
383 XSRETURN(1); /* len != 1, can't be '1' */
384 }
385 temp = *av_fetch(a, 0, 0); /* fetch first element */
7d193e39 386 RETURN_MORTAL_BOOL(temp, 1);
062a4e99
T
387
388##############################################################################
389
390void
391_is_two(class, x)
392 SV* x
393 INIT:
394 AV* a;
395 SV* temp;
396
397 CODE:
398 a = (AV*)SvRV(x); /* ref to aray, don't check ref */
399 if ( av_len(a) != 0)
400 {
401 ST(0) = &PL_sv_no;
402 XSRETURN(1); /* len != 1, can't be '2' */
403 }
404 temp = *av_fetch(a, 0, 0); /* fetch first element */
7d193e39 405 RETURN_MORTAL_BOOL(temp, 2);
062a4e99
T
406
407##############################################################################
408
409void
410_is_ten(class, x)
411 SV* x
412 INIT:
413 AV* a;
414 SV* temp;
415
416 CODE:
417 a = (AV*)SvRV(x); /* ref to aray, don't check ref */
418 if ( av_len(a) != 0)
419 {
420 ST(0) = &PL_sv_no;
421 XSRETURN(1); /* len != 1, can't be '10' */
422 }
423 temp = *av_fetch(a, 0, 0); /* fetch first element */
7d193e39 424 RETURN_MORTAL_BOOL(temp, 10);
062a4e99
T
425
426##############################################################################
427
428void
429_is_zero(class, x)
430 SV* x
431 INIT:
432 AV* a;
433 SV* temp;
434
435 CODE:
436 a = (AV*)SvRV(x); /* ref to aray, don't check ref */
437 if ( av_len(a) != 0)
438 {
439 ST(0) = &PL_sv_no;
440 XSRETURN(1); /* len != 1, can't be '0' */
441 }
442 temp = *av_fetch(a, 0, 0); /* fetch first element */
7d193e39 443 RETURN_MORTAL_BOOL(temp, 0);
062a4e99
T
444
445##############################################################################
446
447void
448_len(class,x)
449 SV* x
450 INIT:
451 AV* a;
452 SV* temp;
6c0b8e73 453 IV elems;
062a4e99
T
454 STRLEN len;
455
456 CODE:
457 a = (AV*)SvRV(x); /* ref to aray, don't check ref */
6c0b8e73 458 elems = av_len(a); /* number of elems in array */
062a4e99
T
459 temp = *av_fetch(a, elems, 0); /* fetch last element */
460 SvPV(temp, len); /* convert to string & store length */
6c0b8e73 461 len += (IV) XS_BASE_LEN * elems;
7d193e39 462 ST(0) = sv_2mortal(newSViv(len));
062a4e99
T
463
464##############################################################################
465
466void
467_acmp(class, cx, cy);
468 SV* cx
469 SV* cy
470 INIT:
471 AV* array_x;
472 AV* array_y;
473 I32 elemsx, elemsy, diff;
474 SV* tempx;
475 SV* tempy;
476 STRLEN lenx;
477 STRLEN leny;
478 NV diff_nv;
479 I32 diff_str;
480
481 CODE:
482 array_x = (AV*)SvRV(cx); /* ref to aray, don't check ref */
483 array_y = (AV*)SvRV(cy); /* ref to aray, don't check ref */
484 elemsx = av_len(array_x);
485 elemsy = av_len(array_y);
486 diff = elemsx - elemsy; /* difference */
487
488 if (diff > 0)
489 {
7d193e39 490 RETURN_MORTAL_INT(1); /* len differs: X > Y */
062a4e99 491 }
7d193e39 492 else if (diff < 0)
062a4e99 493 {
7d193e39 494 RETURN_MORTAL_INT(-1); /* len differs: X < Y */
062a4e99
T
495 }
496 /* both have same number of elements, so check length of last element
497 and see if it differes */
498 tempx = *av_fetch(array_x, elemsx, 0); /* fetch last element */
499 tempy = *av_fetch(array_y, elemsx, 0); /* fetch last element */
500 SvPV(tempx, lenx); /* convert to string & store length */
501 SvPV(tempy, leny); /* convert to string & store length */
502 diff_str = (I32)lenx - (I32)leny;
503 if (diff_str > 0)
504 {
7d193e39 505 RETURN_MORTAL_INT(1); /* same len, but first elems differs in len */
062a4e99
T
506 }
507 if (diff_str < 0)
508 {
7d193e39 509 RETURN_MORTAL_INT(-1); /* same len, but first elems differs in len */
062a4e99
T
510 }
511 /* same number of digits, so need to make a full compare */
512 diff_nv = 0;
513 while (elemsx >= 0)
514 {
515 tempx = *av_fetch(array_x, elemsx, 0); /* fetch curr x element */
516 tempy = *av_fetch(array_y, elemsx, 0); /* fetch curr y element */
517 diff_nv = SvNV(tempx) - SvNV(tempy);
518 if (diff_nv != 0)
519 {
520 break;
521 }
522 elemsx--;
523 }
524 if (diff_nv > 0)
525 {
7d193e39 526 RETURN_MORTAL_INT(1);
062a4e99
T
527 }
528 if (diff_nv < 0)
529 {
7d193e39 530 RETURN_MORTAL_INT(-1);
062a4e99 531 }
7d193e39 532 ST(0) = sv_2mortal(newSViv(0)); /* X and Y are equal */
062a4e99 533