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 if (items < 4) 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)); 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 AV *av = newAV(); 63 64 CODE: 65 if (SvUOK(x) && SvUV(x) < XS_BASE) 66 { 67 /* shortcut for integer arguments */ 68 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 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 */ 83 part_len = (STRLEN) XS_BASE_LEN; 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 { 94 av_push (av, newSVpvn(cur, part_len) ); 95 } 96 } 97 } 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 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()); 117 av_extend (a2, elems); /* pre-padd */ 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 SSize_t elems; 148 SSize_t 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 SSize_t elems; 193 SSize_t 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 */ 206 sv_setnv (temp, SvNV(temp)-1); /* decrement */ 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 SSize_t elems; 237 SSize_t 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 ############################################################################## 266 267 SV * 268 _zero(class) 269 ALIAS: 270 _one = 1 271 _two = 2 272 _ten = 10 273 PREINIT: 274 AV *av = newAV(); 275 CODE: 276 av_push (av, newSViv( ix )); 277 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 a = (AV*)SvRV(x); /* ref to aray, don't check ref */ 294 temp = *av_fetch(a, 0, 0); /* fetch first element */ 295 ST(0) = sv_2mortal(boolSV((SvIV(temp) & 1) == ix)); 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 a = (AV*)SvRV(x); /* ref to aray, don't check ref */ 311 if ( av_len(a) != 0) 312 { 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); 319 } 320 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 a = (AV*)SvRV(x); /* ref to aray, don't check ref */ 335 elems = av_len(a); /* number of elems in array */ 336 temp = *av_fetch(a, elems, 0); /* fetch last element */ 337 SvPV(temp, len); /* convert to string & store length */ 338 len += (IV) XS_BASE_LEN * elems; 339 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 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 { 367 RETURN_MORTAL_INT(1); /* len differs: X > Y */ 368 } 369 else if (diff < 0) 370 { 371 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 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 = (SSize_t)lenx - (SSize_t)leny; 380 if (diff_str > 0) 381 { 382 RETURN_MORTAL_INT(1); /* same len, but first elems differs in len */ 383 } 384 if (diff_str < 0) 385 { 386 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 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 { 403 RETURN_MORTAL_INT(1); 404 } 405 if (diff_nv < 0) 406 { 407 RETURN_MORTAL_INT(-1); 408 } 409 ST(0) = sv_2mortal(newSViv(0)); /* X and Y are equal */ 410