Commit | Line | Data |
---|---|---|
7c420290 NC |
1 | #define PERL_NO_GET_CONTEXT |
2 | ||
062a4e99 T |
3 | #include "EXTERN.h" |
4 | #include "perl.h" | |
5 | #include "XSUB.h" | |
6 | ||
206957a7 SP |
7 | /* for Perl prior to v5.7.1 */ |
8 | #ifndef SvUOK | |
9 | # define SvUOK(sv) SvIOK_UV(sv) | |
10 | #endif | |
11 | ||
824a8d2e PJA |
12 | /* for Perl v5.6 (RT #63859) */ |
13 | #ifndef croak_xs_usage | |
14 | # define croak_xs_usage croak | |
15 | #endif | |
16 | ||
062a4e99 T |
17 | double XS_BASE = 0; |
18 | double XS_BASE_LEN = 0; | |
19 | ||
20 | MODULE = Math::BigInt::FastCalc PACKAGE = Math::BigInt::FastCalc | |
21 | ||
e31720c4 RGS |
22 | PROTOTYPES: DISABLE |
23 | ||
062a4e99 T |
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() | |
7d193e39 T |
35 | # 2007-04-02 0.08 Tels |
36 | # * plug leaks by creating mortals | |
5ed38b1a T |
37 | # 2007-05-27 0.09 Tels |
38 | # * add _new() | |
7d193e39 T |
39 | |
40 | #define RETURN_MORTAL_INT(value) \ | |
41 | ST(0) = sv_2mortal(newSViv(value)); \ | |
42 | XSRETURN(1); | |
43 | ||
c0fdee65 NC |
44 | BOOT: |
45 | { | |
46 | if (items < 4) | |
eff4c632 FR |
47 | croak("Usage: Math::BigInt::FastCalc::BOOT(package, version, base_len, base)"); |
48 | XS_BASE_LEN = SvIV(ST(2)); | |
49 | XS_BASE = SvNV(ST(3)); | |
c0fdee65 | 50 | } |
062a4e99 T |
51 | |
52 | ############################################################################## | |
5ed38b1a T |
53 | # _new |
54 | ||
4019471a | 55 | SV * |
5ed38b1a T |
56 | _new(class, x) |
57 | SV* x | |
58 | INIT: | |
59 | STRLEN len; | |
60 | char* cur; | |
a436f3ee | 61 | STRLEN part_len; |
4019471a | 62 | AV *av = newAV(); |
5ed38b1a T |
63 | |
64 | CODE: | |
206957a7 | 65 | if (SvUOK(x) && SvUV(x) < XS_BASE) |
5ed38b1a T |
66 | { |
67 | /* shortcut for integer arguments */ | |
4019471a | 68 | av_push (av, newSVuv( SvUV(x) )); |
5ed38b1a T |
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 | cur = SvPV(x, len); /* convert to string & store length */ | |
78 | cur += len; /* doing "cur = SvEND(x)" does not work! */ | |
79 | # process the string from the back | |
80 | while (len > 0) | |
81 | { | |
82 | /* use either BASE_LEN or the amount of remaining digits */ | |
a436f3ee | 83 | part_len = (STRLEN) XS_BASE_LEN; |
5ed38b1a T |
84 | if (part_len > len) |
85 | { | |
86 | part_len = len; | |
87 | } | |
88 | /* processed so many digits */ | |
89 | cur -= part_len; | |
90 | len -= part_len; | |
91 | /* 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 | { | |
4019471a | 94 | av_push (av, newSVpvn(cur, part_len) ); |
5ed38b1a T |
95 | } |
96 | } | |
97 | } | |
4019471a | 98 | RETVAL = newRV_noinc((SV *)av); |
5ed38b1a T |
99 | OUTPUT: |
100 | RETVAL | |
101 | ||
102 | ############################################################################## | |
062a4e99 T |
103 | # _copy |
104 | ||
105 | void | |
106 | _copy(class, x) | |
107 | SV* x | |
108 | INIT: | |
109 | AV* a; | |
110 | AV* a2; | |
111 | I32 elems; | |
112 | ||
113 | CODE: | |
114 | a = (AV*)SvRV(x); /* ref to aray, don't check ref */ | |
115 | elems = av_len(a); /* number of elems in array */ | |
116 | a2 = (AV*)sv_2mortal((SV*)newAV()); | |
206957a7 | 117 | av_extend (a2, elems); /* pre-padd */ |
062a4e99 T |
118 | 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 | av_store( a2, elems, newSVnv( SvNV( (SV*)*av_fetch(a, elems, 0) ))); | |
134 | elems--; | |
135 | } | |
136 | 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 | I32 elems; | |
148 | I32 index; | |
149 | ||
150 | CODE: | |
151 | a = (AV*)SvRV(x); /* ref to aray, don't check ref */ | |
152 | elems = av_len(a); /* number of elems in array */ | |
153 | ST(0) = x; /* we return x */ | |
154 | if (elems == -1) | |
155 | { | |
156 | av_push (a, newSViv(0)); /* correct empty arrays */ | |
157 | XSRETURN(1); | |
158 | } | |
159 | if (elems == 0) | |
160 | { | |
161 | XSRETURN(1); /* nothing to do since only one elem */ | |
162 | } | |
163 | index = elems; | |
164 | while (index > 0) | |
165 | { | |
166 | temp = *av_fetch(a, index, 0); /* fetch ptr to current element */ | |
167 | if (SvNV(temp) != 0) | |
168 | { | |
169 | break; | |
170 | } | |
171 | index--; | |
172 | } | |
173 | if (index < elems) | |
174 | { | |
175 | index = elems - index; | |
176 | while (index-- > 0) | |
177 | { | |
178 | av_pop (a); | |
179 | } | |
180 | } | |
181 | 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 | I32 elems; | |
193 | I32 index; | |
194 | NV MAX; | |
195 | ||
196 | CODE: | |
197 | a = (AV*)SvRV(x); /* ref to aray, don't check ref */ | |
198 | elems = av_len(a); /* number of elems in array */ | |
199 | ST(0) = x; /* we return x */ | |
200 | ||
201 | MAX = XS_BASE - 1; | |
202 | index = 0; | |
203 | while (index <= elems) | |
204 | { | |
205 | temp = *av_fetch(a, index, 0); /* fetch ptr to current element */ | |
206957a7 | 206 | sv_setnv (temp, SvNV(temp)-1); /* decrement */ |
062a4e99 T |
207 | if (SvNV(temp) >= 0) |
208 | { | |
209 | break; /* early out */ | |
210 | } | |
211 | sv_setnv (temp, MAX); /* overflow, so set this to $MAX */ | |
212 | index++; | |
213 | } | |
214 | /* do have more than one element? */ | |
215 | /* (more than one because [0] should be kept as single-element) */ | |
216 | if (elems > 0) | |
217 | { | |
218 | temp = *av_fetch(a, elems, 0); /* fetch last element */ | |
219 | if (SvIV(temp) == 0) /* did last elem overflow? */ | |
220 | { | |
221 | av_pop(a); /* yes, so shrink array */ | |
222 | /* aka remove leading zeros */ | |
223 | } | |
224 | } | |
225 | 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 | I32 elems; | |
237 | I32 index; | |
238 | NV BASE; | |
239 | ||
240 | CODE: | |
241 | a = (AV*)SvRV(x); /* ref to aray, don't check ref */ | |
242 | elems = av_len(a); /* number of elems in array */ | |
243 | ST(0) = x; /* we return x */ | |
244 | ||
245 | BASE = XS_BASE; | |
246 | index = 0; | |
247 | while (index <= elems) | |
248 | { | |
249 | temp = *av_fetch(a, index, 0); /* fetch ptr to current element */ | |
250 | sv_setnv (temp, SvNV(temp)+1); | |
251 | if (SvNV(temp) < BASE) | |
252 | { | |
253 | XSRETURN(1); /* return (early out) */ | |
254 | } | |
255 | sv_setiv (temp, 0); /* overflow, so set this elem to 0 */ | |
256 | index++; | |
257 | } | |
258 | temp = *av_fetch(a, elems, 0); /* fetch last element */ | |
259 | if (SvIV(temp) == 0) /* did last elem overflow? */ | |
260 | { | |
261 | av_push(a, newSViv(1)); /* yes, so extend array by 1 */ | |
262 | } | |
263 | XSRETURN(1); /* return x */ | |
264 | ||
265 | ############################################################################## | |
062a4e99 | 266 | |
4019471a | 267 | SV * |
caa64001 | 268 | _zero(class) |
2d032f98 NC |
269 | ALIAS: |
270 | _one = 1 | |
271 | _two = 2 | |
272 | _ten = 10 | |
4019471a NC |
273 | PREINIT: |
274 | AV *av = newAV(); | |
062a4e99 | 275 | CODE: |
4019471a NC |
276 | av_push (av, newSViv( ix )); |
277 | RETVAL = newRV_noinc((SV *)av); | |
caa64001 T |
278 | OUTPUT: |
279 | RETVAL | |
062a4e99 T |
280 | |
281 | ############################################################################## | |
282 | ||
283 | void | |
284 | _is_even(class, x) | |
285 | SV* x | |
2d032f98 NC |
286 | ALIAS: |
287 | _is_odd = 1 | |
062a4e99 T |
288 | INIT: |
289 | AV* a; | |
290 | SV* temp; | |
291 | ||
292 | CODE: | |
293 | a = (AV*)SvRV(x); /* ref to aray, don't check ref */ | |
294 | temp = *av_fetch(a, 0, 0); /* fetch first element */ | |
2d032f98 | 295 | ST(0) = sv_2mortal(boolSV((SvIV(temp) & 1) == ix)); |
062a4e99 T |
296 | |
297 | ############################################################################## | |
298 | ||
299 | void | |
300 | _is_zero(class, x) | |
301 | SV* x | |
2d032f98 NC |
302 | ALIAS: |
303 | _is_one = 1 | |
304 | _is_two = 2 | |
305 | _is_ten = 10 | |
062a4e99 T |
306 | INIT: |
307 | AV* a; | |
062a4e99 T |
308 | |
309 | CODE: | |
310 | a = (AV*)SvRV(x); /* ref to aray, don't check ref */ | |
311 | if ( av_len(a) != 0) | |
312 | { | |
8e1dd0a2 NC |
313 | ST(0) = &PL_sv_no; /* len != 1, can't be '0' */ |
314 | } | |
315 | else | |
316 | { | |
317 | SV *const temp = *av_fetch(a, 0, 0); /* fetch first element */ | |
318 | ST(0) = boolSV(SvIV(temp) == ix); | |
062a4e99 | 319 | } |
824a8d2e | 320 | XSRETURN(1); |
062a4e99 T |
321 | |
322 | ############################################################################## | |
323 | ||
324 | void | |
325 | _len(class,x) | |
326 | SV* x | |
327 | INIT: | |
328 | AV* a; | |
329 | SV* temp; | |
6c0b8e73 | 330 | IV elems; |
062a4e99 T |
331 | STRLEN len; |
332 | ||
333 | CODE: | |
334 | a = (AV*)SvRV(x); /* ref to aray, don't check ref */ | |
6c0b8e73 | 335 | elems = av_len(a); /* number of elems in array */ |
062a4e99 T |
336 | temp = *av_fetch(a, elems, 0); /* fetch last element */ |
337 | SvPV(temp, len); /* convert to string & store length */ | |
6c0b8e73 | 338 | len += (IV) XS_BASE_LEN * elems; |
7d193e39 | 339 | ST(0) = sv_2mortal(newSViv(len)); |
062a4e99 T |
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 | I32 elemsx, elemsy, diff; | |
351 | SV* tempx; | |
352 | SV* tempy; | |
353 | STRLEN lenx; | |
354 | STRLEN leny; | |
355 | NV diff_nv; | |
356 | I32 diff_str; | |
357 | ||
358 | CODE: | |
359 | array_x = (AV*)SvRV(cx); /* ref to aray, don't check ref */ | |
360 | array_y = (AV*)SvRV(cy); /* ref to aray, don't check ref */ | |
361 | elemsx = av_len(array_x); | |
362 | elemsy = av_len(array_y); | |
363 | diff = elemsx - elemsy; /* difference */ | |
364 | ||
365 | if (diff > 0) | |
366 | { | |
7d193e39 | 367 | RETURN_MORTAL_INT(1); /* len differs: X > Y */ |
062a4e99 | 368 | } |
7d193e39 | 369 | else if (diff < 0) |
062a4e99 | 370 | { |
7d193e39 | 371 | RETURN_MORTAL_INT(-1); /* len differs: X < Y */ |
062a4e99 T |
372 | } |
373 | /* both have same number of elements, so check length of last element | |
c4a6f826 | 374 | and see if it differs */ |
062a4e99 T |
375 | tempx = *av_fetch(array_x, elemsx, 0); /* fetch last element */ |
376 | tempy = *av_fetch(array_y, elemsx, 0); /* fetch last element */ | |
377 | SvPV(tempx, lenx); /* convert to string & store length */ | |
378 | SvPV(tempy, leny); /* convert to string & store length */ | |
379 | diff_str = (I32)lenx - (I32)leny; | |
380 | if (diff_str > 0) | |
381 | { | |
7d193e39 | 382 | RETURN_MORTAL_INT(1); /* same len, but first elems differs in len */ |
062a4e99 T |
383 | } |
384 | if (diff_str < 0) | |
385 | { | |
7d193e39 | 386 | RETURN_MORTAL_INT(-1); /* same len, but first elems differs in len */ |
062a4e99 T |
387 | } |
388 | /* same number of digits, so need to make a full compare */ | |
389 | diff_nv = 0; | |
390 | while (elemsx >= 0) | |
391 | { | |
392 | tempx = *av_fetch(array_x, elemsx, 0); /* fetch curr x element */ | |
393 | tempy = *av_fetch(array_y, elemsx, 0); /* fetch curr y element */ | |
394 | diff_nv = SvNV(tempx) - SvNV(tempy); | |
395 | if (diff_nv != 0) | |
396 | { | |
397 | break; | |
398 | } | |
399 | elemsx--; | |
400 | } | |
401 | if (diff_nv > 0) | |
402 | { | |
7d193e39 | 403 | RETURN_MORTAL_INT(1); |
062a4e99 T |
404 | } |
405 | if (diff_nv < 0) | |
406 | { | |
7d193e39 | 407 | RETURN_MORTAL_INT(-1); |
062a4e99 | 408 | } |
7d193e39 | 409 | ST(0) = sv_2mortal(newSViv(0)); /* X and Y are equal */ |
062a4e99 | 410 |