File Coverage

XS.xs
Criterion Covered Total %
statement 1390 1589 87.4
branch 1609 2676 60.1
condition n/a
subroutine n/a
pod n/a
total 2999 4265 70.3


line stmt bran cond sub pod time code
1              
2             #define PERL_NO_GET_CONTEXT 1 /* Define at top for more efficiency. */
3              
4             #include "EXTERN.h"
5             #include "perl.h"
6             #include "XSUB.h"
7             #include "multicall.h" /* only works in 5.6 and newer */
8             #include /* For fileno and stdout */
9              
10             #define NEED_newCONSTSUB
11             #define NEED_newRV_noinc
12             #define NEED_sv_2pv_flags
13             #define NEED_HvNAME_get
14             #include "ppport.h"
15              
16             #define FUNC_gcd_ui 1
17             #define FUNC_isqrt 1
18             #define FUNC_ipow 1
19             #define FUNC_popcnt 1
20             #include "ptypes.h"
21             #include "cache.h"
22             #include "sieve.h"
23             #include "sieve_cluster.h"
24             #include "util.h"
25             #include "primality.h"
26             #include "factor.h"
27             #include "lehmer.h"
28             #include "lmo.h"
29             #include "aks.h"
30             #include "constants.h"
31             #include "mulmod.h"
32             #include "entropy.h"
33             #include "csprng.h"
34             #include "random_prime.h"
35             #include "ramanujan_primes.h"
36             #include "prime_nth_count.h"
37              
38             #ifdef FACTORING_HARNESSES
39             #include
40             static double my_difftime (struct timeval * start, struct timeval * end) {
41             double secs, usecs;
42             if (start->tv_sec == end->tv_sec) {
43             secs = 0;
44             usecs = end->tv_usec - start->tv_usec;
45             } else {
46             usecs = 1000000 - start->tv_usec;
47             secs = end->tv_sec - (start->tv_sec + 1);
48             usecs += end->tv_usec;
49             if (usecs >= 1000000) {
50             usecs -= 1000000;
51             secs += 1;
52             }
53             }
54             return secs + usecs / 1000000.;
55             }
56             #endif
57              
58             #if BITS_PER_WORD == 64
59             #if defined(_MSC_VER)
60             #include
61             #define strtoull _strtoui64
62             #define strtoll _strtoi64
63             #endif
64             #define PSTRTOULL(str, end, base) strtoull (str, end, base)
65             #define PSTRTOLL(str, end, base) strtoll (str, end, base)
66             #else
67             #define PSTRTOULL(str, end, base) strtoul (str, end, base)
68             #define PSTRTOLL(str, end, base) strtol (str, end, base)
69             #endif
70             #if defined(_MSC_VER) && !defined(strtold)
71             #define strtold strtod
72             #endif
73              
74             #if PERL_REVISION <= 5 && PERL_VERSION <= 6 && BITS_PER_WORD == 64
75             /* Workaround perl 5.6 UVs and bigints */
76             #define my_svuv(sv) PSTRTOULL(SvPV_nolen(sv), NULL, 10)
77             #define my_sviv(sv) PSTRTOLL(SvPV_nolen(sv), NULL, 10)
78             #elif PERL_REVISION <= 5 && PERL_VERSION < 14 && BITS_PER_WORD == 64
79             /* Workaround RT 49569 in Math::BigInt::FastCalc (pre 5.14.0) */
80             /* TODO: Math::BigInt::Pari has the same problem with negs pre-5.18.0 */
81             #define my_svuv(sv) ( (!SvROK(sv)) ? SvUV(sv) : PSTRTOULL(SvPV_nolen(sv),NULL,10) )
82             #define my_sviv(sv) ( (!SvROK(sv)) ? SvIV(sv) : PSTRTOLL(SvPV_nolen(sv),NULL,10) )
83             #else
84             #define my_svuv(sv) SvUV(sv)
85             #define my_sviv(sv) SvIV(sv)
86             #endif
87              
88             #if PERL_REVISION >=5 && PERL_VERSION >= 9 && PERL_SUBVERSION >= 4
89             #define SVf_MAGTEST SVf_ROK
90             #else
91             #define SVf_MAGTEST SVf_AMAGIC
92             #endif
93              
94             #define SVNUMTEST(n) \
95             ((SvFLAGS(n) & (SVf_IOK | SVf_MAGTEST | SVs_GMG )) == SVf_IOK)
96              
97             /* multicall compatibility stuff */
98             #if (PERL_REVISION <= 5 && PERL_VERSION < 7) || !defined(dMULTICALL)
99             # define USE_MULTICALL 0 /* Too much trouble to work around it */
100             #else
101             # define USE_MULTICALL 1
102             #endif
103             #if PERL_VERSION < 13 || (PERL_VERSION == 13 && PERL_SUBVERSION < 9)
104             # define FIX_MULTICALL_REFCOUNT \
105             if (CvDEPTH(multicall_cv) > 1) SvREFCNT_inc(multicall_cv);
106             #else
107             # define FIX_MULTICALL_REFCOUNT
108             #endif
109              
110             #ifndef CvISXSUB
111             # define CvISXSUB(cv) CvXSUB(cv)
112             #endif
113              
114             /* Not right, but close */
115             #if !defined cxinc && ( (PERL_VERSION == 8 && PERL_SUBVERSION >= 1) || (PERL_VERSION == 10 && PERL_SUBVERSION <= 1) )
116             # define cxinc() Perl_cxinc(aTHX)
117             #endif
118              
119             #if PERL_VERSION < 17 || (PERL_VERSION == 17 && PERL_SUBVERSION < 7)
120             # define SvREFCNT_dec_NN(sv) SvREFCNT_dec(sv)
121             #endif
122              
123             #if BITS_PER_WORD == 32
124             static const unsigned int uvmax_maxlen = 10;
125             static const unsigned int ivmax_maxlen = 10;
126             static const char uvmax_str[] = "4294967295";
127             static const char ivmax_str[] = "2147483648";
128             #else
129             static const unsigned int uvmax_maxlen = 20;
130             static const unsigned int ivmax_maxlen = 19;
131             static const char uvmax_str[] = "18446744073709551615";
132             static const char ivmax_str[] = "9223372036854775808";
133             #endif
134              
135             #define MY_CXT_KEY "Math::Prime::Util::API_guts"
136             #define CINTS 100
137             typedef struct {
138             HV* MPUroot;
139             HV* MPUGMP;
140             HV* MPUPP;
141             SV* const_int[CINTS+1]; /* -1, 0, 1, ..., 99 */
142             void* randcxt; /* per-thread csprng context */
143             uint16_t forcount;
144             char forexit;
145             } my_cxt_t;
146              
147             START_MY_CXT
148              
149 41140           static int _is_sv_bigint(pTHX_ SV* n)
150             {
151 41140 100         if (sv_isobject(n)) {
152 41127 50         const char *hvname = HvNAME_get(SvSTASH(SvRV(n)));
    50          
    50          
    0          
    50          
    50          
153 41127 50         if (hvname != 0) {
154 41127 100         if (strEQ(hvname, "Math::BigInt") || strEQ(hvname, "Math::BigFloat") ||
    50          
    0          
155 0 0         strEQ(hvname, "Math::GMPz") || strEQ(hvname, "Math::GMP") ||
    0          
156 0 0         strEQ(hvname, "Math::GMPq") || strEQ(hvname, "Math::AnyNum") ||
    0          
157 0 0         strEQ(hvname, "Math::Pari") || strEQ(hvname, "Math::BigInt::Lite"))
158 41127           return 1;
159             }
160             }
161 13           return 0;
162             }
163              
164             /* Is this a pedantically valid integer?
165             * Croaks if undefined or invalid.
166             * Returns 0 if it is an object or a string too large for a UV.
167             * Returns 1 if it is good to process by XS.
168             */
169 1054104           static int _validate_int(pTHX_ SV* n, int negok)
170             {
171             const char* maxstr;
172             char* ptr;
173             STRLEN i, len, maxlen;
174 1054104           int ret, isbignum = 0, isneg = 0;
175              
176             /* TODO: magic, grok_number, etc. */
177 1054104 100         if (SVNUMTEST(n)) { /* If defined as number, use it */
178 1005314 100         if (SvIsUV(n) || SvIVX(n) >= 0) return 1; /* The normal case */
    100          
179 26806 100         if (negok) return -1;
180 11           else croak("Parameter '%" SVf "' must be a positive integer", n);
181             }
182 48790 100         if (sv_isobject(n)) {
183 41127           isbignum = _is_sv_bigint(aTHX_ n);
184 41127 50         if (!isbignum) return 0;
185             }
186             /* Without being very careful, don't process magic variables here */
187 48790 100         if (SvGAMAGIC(n) && !isbignum) return 0;
    100          
    50          
    50          
    100          
188 48789 100         if (!SvOK(n)) croak("Parameter must be defined");
    50          
    50          
189 48769 100         ptr = SvPV_nomg(n, len); /* Includes stringifying bigints */
190 48769 100         if (len == 0 || ptr == 0) croak("Parameter must be a positive integer");
    50          
191 48768 100         if (ptr[0] == '-' && negok) {
    100          
192 1449           isneg = 1; ptr++; len--; /* Read negative sign */
193 47319 100         } else if (ptr[0] == '+') {
194 5           ptr++; len--; /* Allow a single plus sign */
195             }
196 48768 100         if (len == 0 || !isDIGIT(ptr[0]))
    100          
197 25           croak("Parameter '%" SVf "' must be a positive integer", n);
198 48981 100         while (len > 0 && *ptr == '0') /* Strip all leading zeros */
    100          
199 238           { ptr++; len--; }
200 48743 100         if (len > uvmax_maxlen) /* Huge number, don't even look at it */
201 36834           return 0;
202 86040 100         for (i = 0; i < len; i++) /* Ensure all characters are digits */
203 74142 100         if (!isDIGIT(ptr[i]))
204 11           croak("Parameter '%" SVf "' must be a positive integer", n);
205 11898 100         if (isneg == 1) /* Negative number (ignore overflow) */
206 1027           return -1;
207 10871 50         ret = isneg ? -1 : 1;
208 10871 50         maxlen = isneg ? ivmax_maxlen : uvmax_maxlen;
209 10871 50         maxstr = isneg ? ivmax_str : uvmax_str;
210 10871 100         if (len < maxlen) /* Valid small integer */
211 10540           return ret;
212 1142 100         for (i = 0; i < maxlen; i++) { /* Check if in range */
213 1137 100         if (ptr[i] < maxstr[i]) return ret;
214 1053 100         if (ptr[i] > maxstr[i]) return 0;
215             }
216 1054036           return ret; /* value = UV_MAX/UV_MIN. That's ok */
217             }
218              
219             #define VCALL_ROOT 0x0
220             #define VCALL_PP 0x1
221             #define VCALL_GMP 0x2
222             /* Call a Perl sub to handle work for us. */
223 38502           static int _vcallsubn(pTHX_ I32 flags, I32 stashflags, const char* name, int nargs, int minversion)
224             {
225 38502           GV* gv = NULL;
226             dMY_CXT;
227 38502           Size_t namelen = strlen(name);
228             /* If given a GMP function, and GMP enabled, and function exists, use it. */
229 38502 100         int use_gmp = stashflags & VCALL_GMP && _XS_get_callgmp() && _XS_get_callgmp() >= minversion;
    50          
    0          
230             assert(!(stashflags & ~(VCALL_PP|VCALL_GMP)));
231 38502 50         if (use_gmp && hv_exists(MY_CXT.MPUGMP,name,namelen)) {
    0          
232 0           GV ** gvp = (GV**)hv_fetch(MY_CXT.MPUGMP,name,namelen,0);
233 0 0         if (gvp) gv = *gvp;
234             }
235 38502 50         if (!gv && (stashflags & VCALL_PP))
    100          
236 30972           perl_require_pv("Math/Prime/Util/PP.pm");
237 38502 50         if (!gv) {
238 38502 100         GV ** gvp = (GV**)hv_fetch(stashflags & VCALL_PP? MY_CXT.MPUPP : MY_CXT.MPUroot, name,namelen,0);
239 38502 50         if (gvp) gv = *gvp;
240             }
241             /* use PL_stack_sp in PUSHMARK macro directly it will be read after
242             the possible mark stack extend */
243 38502 50         PUSHMARK(PL_stack_sp-nargs);
244             /* no PUTBACK bc we didn't move global SP */
245 38502           return call_sv((SV*)gv, flags);
246             }
247             #define _vcallsub(func) (void)_vcallsubn(aTHX_ G_SCALAR, VCALL_ROOT, func, items,0)
248             #define _vcallsub_with_gmp(ver,func) (void)_vcallsubn(aTHX_ G_SCALAR, VCALL_GMP|VCALL_PP, func, items,(int)(100*(ver)))
249             #define _vcallsub_with_pp(func) (void)_vcallsubn(aTHX_ G_SCALAR, VCALL_PP, func, items,0)
250             #define _vcallsub_with_gmpobj(ver,func) (void)_vcallsubn(aTHX_ G_SCALAR, (PERL_REVISION >= 5 && PERL_VERSION > 8) ? VCALL_GMP|VCALL_PP : VCALL_PP, func, items,(int)(100*(ver)))
251              
252             /* In my testing, this constant return works fine with threads, but to be
253             * correct (see perlxs) one has to make a context, store separate copies in
254             * each one, then retrieve them from a struct using a hash index. This
255             * defeats the purpose if only done once. */
256             #define RETURN_NPARITY(ret) \
257             do { int r_ = ret; \
258             dMY_CXT; \
259             if (r_ >= -1 && r_
260             else { XSRETURN_IV(r_); } \
261             } while (0)
262             #define PUSH_NPARITY(ret) \
263             do { int r_ = ret; \
264             if (r_ >= -1 && r_
265             else { PUSHs(sv_2mortal(newSViv(r_))); } \
266             } while (0)
267              
268             #define OBJECTIFY_RESULT(input, output) \
269             if (!sv_isobject(output) && PERL_REVISION >= 5 && PERL_VERSION > 8) { \
270             SV* resptr = output; \
271             const char *iname = (input && sv_isobject(input)) \
272             ? HvNAME_get(SvSTASH(SvRV(input))) : 0; \
273             if (iname == 0 || strEQ(iname, "Math::BigInt")) { \
274             (void)_vcallsubn(aTHX_ G_SCALAR, VCALL_ROOT, "_to_bigint", 1, 0); \
275             } else if (iname == 0 || strEQ(iname, "Math::GMPz")) { \
276             (void)_vcallsubn(aTHX_ G_SCALAR, VCALL_ROOT, "_to_gmpz", 1, 0); \
277             } else if (iname == 0 || strEQ(iname, "Math::GMP")) { \
278             (void)_vcallsubn(aTHX_ G_SCALAR, VCALL_ROOT, "_to_gmp", 1, 0); \
279             } else { /* Return it as: ref(input)->new(result) */ \
280             dSP; (void)POPs; ENTER; PUSHMARK(SP); \
281             XPUSHs(sv_2mortal(newSVpv(iname, 0))); XPUSHs(resptr); \
282             PUTBACK; call_method("new", G_SCALAR); LEAVE; \
283             } \
284             }
285              
286 1           static SV* sv_to_bigint(pTHX_ SV* r) {
287 1 50         dSP; ENTER; PUSHMARK(SP);
288 1 50         XPUSHs(r);
289 1           PUTBACK;
290 1           call_pv("Math::Prime::Util::_to_bigint", G_SCALAR);
291 1           SPAGAIN;
292 1           r = POPs;
293 1           PUTBACK; LEAVE;
294 1           return r;
295             }
296              
297             #define RETURN_128(hi,lo) \
298             do { char str[40]; \
299             int slen = to_string_128(str, hi, lo); \
300             XPUSHs( sv_to_bigint( aTHX_ sv_2mortal(newSVpv(str,slen)) ) ); \
301             XSRETURN(1); } while(0)
302              
303 13           static int arrayref_to_int_array(pTHX_ UV** ret, AV* av, int base)
304             {
305             int len, i;
306 13           UV *r, carry = 0;
307 13 50         if (SvTYPE((SV*)av) != SVt_PVAV)
308 0           croak("fromdigits first argument must be a string or array reference");
309 13           len = 1 + av_len(av);
310 13 50         New(0, r, len, UV);
311 76 100         for (i = len-1; i >= 0; i--) {
312 63           SV** psvd = av_fetch(av, i, 0);
313 63 50         if (_validate_int(aTHX_ *psvd, 1) != 1) break;
314 63 50         r[i] = my_svuv(*psvd) + carry;
315 63 100         if (r[i] >= (UV)base && i > 0) {
    100          
316 11           carry = r[i] / base;
317 11           r[i] -= carry * base;
318             } else {
319 52           carry = 0;
320             }
321             }
322 13 50         if (i >= 0) {
323 0           Safefree(r);
324 0           return -1;
325             }
326             /* printf("array is ["); for(i=0;i
327 13           *ret = r;
328 13           return len;
329             }
330              
331 122           static UV negmod(IV a, UV n) {
332 122           UV negamod = ((UV)(-a)) % n;
333 122 50         return (negamod == 0) ? 0 : n-negamod;
334             }
335              
336 69           static void csprng_init_seed(void* ctx) {
337             unsigned char* data;
338 69           New(0, data, 64, unsigned char);
339 69           get_entropy_bytes(64, data);
340 69           csprng_seed(ctx, 64, data);
341 69           Safefree(data);
342 69           }
343              
344              
345             MODULE = Math::Prime::Util PACKAGE = Math::Prime::Util
346              
347             PROTOTYPES: ENABLE
348              
349             BOOT:
350             {
351             int i;
352 69           SV * sv = newSViv(BITS_PER_WORD);
353 69           HV * stash = gv_stashpv("Math::Prime::Util", TRUE);
354 69           newCONSTSUB(stash, "_XS_prime_maxbits", sv);
355              
356             {
357             MY_CXT_INIT;
358 69           MY_CXT.MPUroot = stash;
359 69           MY_CXT.MPUGMP = gv_stashpv("Math::Prime::Util::GMP", TRUE);
360 69           MY_CXT.MPUPP = gv_stashpv("Math::Prime::Util::PP", TRUE);
361 7038 100         for (i = 0; i <= CINTS; i++) {
362 6969           MY_CXT.const_int[i] = newSViv(i-1);
363 6969           SvREADONLY_on(MY_CXT.const_int[i]);
364             }
365 69           New(0, MY_CXT.randcxt, csprng_context_size(), char);
366 69           csprng_init_seed(MY_CXT.randcxt);
367 69           MY_CXT.forcount = 0;
368 69           MY_CXT.forexit = 0;
369             }
370             }
371              
372             #if defined(USE_ITHREADS) && defined(MY_CXT_KEY)
373              
374             void
375             CLONE(...)
376             PREINIT:
377             int i;
378             PPCODE:
379             {
380             MY_CXT_CLONE; /* possible declaration */
381             MY_CXT.MPUroot = gv_stashpv("Math::Prime::Util", TRUE);
382             MY_CXT.MPUGMP = gv_stashpv("Math::Prime::Util::GMP", TRUE);
383             MY_CXT.MPUPP = gv_stashpv("Math::Prime::Util::PP", TRUE);
384             /* These should be shared between threads, but that's dodgy. */
385             for (i = 0; i <= CINTS; i++) {
386             MY_CXT.const_int[i] = newSViv(i-1);
387             SvREADONLY_on(MY_CXT.const_int[i]);
388             }
389             /* Make a new CSPRNG context for this thread */
390             New(0, MY_CXT.randcxt, csprng_context_size(), char);
391             csprng_init_seed(MY_CXT.randcxt);
392             /* NOTE: There is no thread destroy, so these never get freed... */
393             MY_CXT.forcount = 0;
394             MY_CXT.forexit = 0;
395             }
396             return; /* skip implicit PUTBACK, returning @_ to caller, more efficient*/
397              
398             #endif
399              
400             void
401             END(...)
402             PREINIT:
403             dMY_CXT;
404             int i;
405             PPCODE:
406 69           MY_CXT.MPUroot = NULL;
407 69           MY_CXT.MPUGMP = NULL;
408 69           MY_CXT.MPUPP = NULL;
409 7038 100         for (i = 0; i <= CINTS; i++) {
410 6969           SV * const sv = MY_CXT.const_int[i];
411 6969           MY_CXT.const_int[i] = NULL;
412 6969           SvREFCNT_dec_NN(sv);
413             } /* stashes are owned by stash tree, no refcount on them in MY_CXT */
414 69           Safefree(MY_CXT.randcxt); MY_CXT.randcxt = 0;
415 69           _prime_memfreeall();
416 69           return; /* skip implicit PUTBACK, returning @_ to caller, more efficient*/
417              
418              
419             void csrand(IN SV* seed = 0)
420             PREINIT:
421             unsigned char* data;
422             STRLEN size;
423             dMY_CXT;
424             PPCODE:
425 6 50         if (items == 0) {
426 0           csprng_init_seed(MY_CXT.randcxt);
427 6 50         } else if (_XS_get_secure()) {
428 0           croak("secure option set, manual seeding disabled");
429             } else {
430 6 50         data = (unsigned char*) SvPV(seed, size);
431 6           csprng_seed(MY_CXT.randcxt, size, data);
432             }
433              
434             UV srand(IN UV seedval = 0)
435             PREINIT:
436             dMY_CXT;
437             CODE:
438 5 50         if (_XS_get_secure())
439 0           croak("secure option set, manual seeding disabled");
440 5 100         if (items == 0)
441 1           get_entropy_bytes(sizeof(UV), (unsigned char*) &seedval);
442 5           csprng_srand(MY_CXT.randcxt, seedval);
443 5           RETVAL = seedval;
444             OUTPUT:
445             RETVAL
446              
447             UV irand()
448             ALIAS:
449             irand64 = 1
450             PREINIT:
451             dMY_CXT;
452             CODE:
453 100013 100         if (ix == 0)
454 100005           RETVAL = irand32(MY_CXT.randcxt);
455             else
456             #if BITS_PER_WORD >= 64
457 8           RETVAL = irand64(MY_CXT.randcxt);
458             #else /* TODO: should irand64 on 32-bit perl (1) croak, (2) return 32-bits */
459             RETVAL = irand32(MY_CXT.randcxt);
460             #endif
461             OUTPUT:
462             RETVAL
463              
464             NV drand(NV m = 0.0)
465             ALIAS:
466             rand = 1
467             PREINIT:
468             dMY_CXT;
469             CODE:
470             PERL_UNUSED_VAR(ix);
471 6033           RETVAL = drand64(MY_CXT.randcxt);
472 6033 100         if (m != 0) RETVAL *= m;
473             OUTPUT:
474             RETVAL
475              
476             SV* random_bytes(IN UV n)
477             PREINIT:
478             char* sptr;
479             dMY_CXT;
480             CODE:
481 45 100         RETVAL = newSV(n == 0 ? 1 : n);
482 45           SvPOK_only(RETVAL);
483 45           SvCUR_set(RETVAL, n);
484 45           sptr = SvPVX(RETVAL);
485 45           csprng_rand_bytes(MY_CXT.randcxt, n, (unsigned char*)sptr);
486 45           sptr[n] = '\0';
487             OUTPUT:
488             RETVAL
489              
490             SV* entropy_bytes(IN UV n)
491             PREINIT:
492             char* sptr;
493             CODE:
494 2 50         RETVAL = newSV(n == 0 ? 1 : n);
495 2           SvPOK_only(RETVAL);
496 2           SvCUR_set(RETVAL, n);
497 2           sptr = SvPVX(RETVAL);
498 2           get_entropy_bytes(n, (unsigned char*)sptr);
499 2           sptr[n] = '\0';
500             OUTPUT:
501             RETVAL
502              
503             UV _is_csprng_well_seeded()
504             ALIAS:
505             _XS_get_verbose = 1
506             _XS_get_callgmp = 2
507             _XS_get_secure = 3
508             _XS_set_secure = 4
509             _get_forexit = 5
510             _start_for_loop = 6
511             _get_prime_cache_size = 7
512             CODE:
513 2512           switch (ix) {
514 1           case 0: { dMY_CXT; RETVAL = is_csprng_well_seeded(MY_CXT.randcxt); } break;
515 0           case 1: RETVAL = _XS_get_verbose(); break;
516 0           case 2: RETVAL = _XS_get_callgmp(); break;
517 0           case 3: RETVAL = _XS_get_secure(); break;
518 0           case 4: _XS_set_secure(); RETVAL = 1; break;
519 177           case 5: { dMY_CXT; RETVAL = MY_CXT.forexit; } break;
520 12           case 6: { dMY_CXT; MY_CXT.forcount++; RETVAL = MY_CXT.forexit; MY_CXT.forexit = 0; } break;
521             case 7:
522 2322           default: RETVAL = get_prime_cache(0,0); break;
523             }
524             OUTPUT:
525             RETVAL
526              
527             void prime_memfree()
528              
529             void
530             prime_precalc(IN UV n)
531             ALIAS:
532             _XS_set_verbose = 1
533             _XS_set_callgmp = 2
534             _end_for_loop = 3
535             PPCODE:
536 168           PUTBACK; /* SP is never used again, the 3 next func calls are tailcall
537             friendly since this XSUB has nothing to do after the 3 calls return */
538 168           switch (ix) {
539 76           case 0: prime_precalc(n); break;
540 9           case 1: _XS_set_verbose(n); break;
541 71           case 2: _XS_set_callgmp(n); break;
542             case 3:
543 12           default: { dMY_CXT; MY_CXT.forcount--; MY_CXT.forexit = n; } break;
544             }
545 168           return; /* skip implicit PUTBACK */
546              
547             void
548             prime_count(IN SV* svlo, ...)
549             ALIAS:
550             twin_prime_count = 1
551             ramanujan_prime_count = 2
552             ramanujan_prime_count_approx = 3
553             sum_primes = 4
554             print_primes = 5
555             PREINIT:
556             int lostatus, histatus;
557             UV lo, hi;
558             PPCODE:
559 1176           lostatus = _validate_int(aTHX_ svlo, 0);
560 1176 100         histatus = (items == 1 || _validate_int(aTHX_ ST(1), 0));
    100          
561 1176 100         if (lostatus == 1 && histatus == 1) {
    50          
562 1175           UV count = 0;
563 1175 100         if (items == 1) {
564 1147           lo = 2;
565 1147 50         hi = my_svuv(svlo);
566             } else {
567 28 50         lo = my_svuv(svlo);
568 28 50         hi = my_svuv(ST(1));
569             }
570 1175 100         if (lo <= hi) {
571 1165 100         if (ix == 0) { count = prime_count(lo, hi); }
572 1026 100         else if (ix == 1) { count = twin_prime_count(lo, hi); }
573 1015 100         else if (ix == 2) { count = ramanujan_prime_count(lo, hi); }
574 1005 100         else if (ix == 3) { count = ramanujan_prime_count_approx(hi);
575 2 50         if (lo > 2)
576 2           count -= ramanujan_prime_count_approx(lo-1); }
577 1003 50         else if (ix == 4) {
578             #if BITS_PER_WORD == 64 && HAVE_UINT128
579 1003 50         if (hi >= 29505444491UL && hi-lo > hi/50) {
    0          
580             UV hicount, lo_hic, lo_loc;
581 0           lostatus = sum_primes128(hi, &hicount, &count);
582 0 0         if (lostatus == 1 && lo > 2) {
    0          
583 0           lostatus = sum_primes128(lo-1, &lo_hic, &lo_loc);
584 0           hicount -= lo_hic;
585 0 0         if (count < lo_loc) hicount--;
586 0           count -= lo_loc;
587             }
588 0 0         if (lostatus == 1 && hicount > 0)
    0          
589 0 0         RETURN_128(hicount, count);
590             }
591             #endif
592 1003           lostatus = sum_primes(lo, hi, &count);
593 0 0         } else if (ix == 5) {
594 0 0         int fd = (items < 3) ? fileno(stdout) : my_sviv(ST(2));
    0          
595 0           print_primes(lo, hi, fd);
596 0           XSRETURN_EMPTY;
597             }
598             }
599 1175 50         if (lostatus == 1) XSRETURN_UV(count);
600             }
601 1           switch (ix) {
602 1 50         case 0: _vcallsubn(aTHX_ GIMME_V, VCALL_ROOT, "_generic_prime_count", items, 0); break;
603 0           case 1: _vcallsub_with_pp("twin_prime_count"); break;
604 0           case 2: _vcallsub_with_pp("ramanujan_prime_count"); break;
605 0           case 3: _vcallsub_with_pp("ramanujan_prime_count_approx"); break;
606 0           case 4: _vcallsub_with_pp("sum_primes"); break;
607             case 5:
608 0           default:_vcallsub_with_pp("print_primes"); break;
609             }
610 1           return; /* skip implicit PUTBACK */
611              
612             void random_prime(IN SV* svlo, IN SV* svhi = 0)
613             PREINIT:
614             int lostatus, histatus;
615             UV lo, hi, ret;
616             dMY_CXT;
617             PPCODE:
618 22031           lostatus = _validate_int(aTHX_ svlo, 0);
619 22024 100         histatus = (items == 1 || _validate_int(aTHX_ svhi, 0));
    100          
620 22018 100         if (lostatus == 1 && histatus == 1) {
    50          
621 22017 100         if (items == 1) {
622 12000           lo = 2;
623 12000 50         hi = my_svuv(svlo);
624             } else {
625 10017 50         lo = my_svuv(svlo);
626 10017 50         hi = my_svuv(svhi);
627             }
628 22017           ret = random_prime(MY_CXT.randcxt,lo,hi);
629 22017 100         if (ret) XSRETURN_UV(ret);
630 6           else XSRETURN_UNDEF;
631             }
632 1           _vcallsub_with_gmpobj(0.44,"random_prime");
633 1 50         OBJECTIFY_RESULT(ST(0), ST(0));
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
634 1           XSRETURN(1);
635              
636             UV
637             _LMO_pi(IN UV n)
638             ALIAS:
639             _legendre_pi = 1
640             _meissel_pi = 2
641             _lehmer_pi = 3
642             _LMOS_pi = 4
643             _segment_pi = 5
644             PREINIT:
645             UV ret;
646             CODE:
647 2           switch (ix) {
648 1           case 0: ret = LMO_prime_count(n); break;
649 0           case 1: ret = legendre_prime_count(n); break;
650 0           case 2: ret = meissel_prime_count(n); break;
651 0           case 3: ret = lehmer_prime_count(n); break;
652 0           case 4: ret = LMOS_prime_count(n); break;
653 1           default:ret = segment_prime_count(2,n); break;
654             }
655 2           RETVAL = ret;
656             OUTPUT:
657             RETVAL
658              
659             void
660             sieve_primes(IN UV low, IN UV high)
661             ALIAS:
662             trial_primes = 1
663             erat_primes = 2
664             segment_primes = 3
665             segment_twin_primes = 4
666             _ramanujan_primes = 5
667             _n_ramanujan_primes = 6
668             PREINIT:
669             AV* av;
670             PPCODE:
671 1209           av = newAV();
672             {
673 1209           SV * retsv = sv_2mortal(newRV_noinc( (SV*) av ));
674 1209           PUSHs(retsv);
675 1209           PUTBACK;
676 1209           SP = NULL; /* never use SP again, poison */
677             }
678 1209 100         if ((low <= 2) && (high >= 2) && ix != 4) { av_push(av, newSVuv( 2 )); }
    100          
    100          
679 1209 100         if ((low <= 3) && (high >= 3) && ix != 5) { av_push(av, newSVuv( 3 )); }
    100          
    100          
680 1209 100         if ((low <= 5) && (high >= 5) && ix != 5) { av_push(av, newSVuv( 5 )); }
    100          
    100          
681 1209 100         if (low < 7) low = 7;
682 1209 100         if (low <= high) {
683 1166 100         if (ix == 4) high += 2;
684 1166 100         if (ix == 0) { /* Sieve with primary cache */
685 1213274 50         START_DO_FOR_EACH_PRIME(low, high) {
    50          
    0          
    0          
    100          
    100          
    100          
    100          
    50          
    100          
686 1192348           av_push(av,newSVuv(p));
687 1193446           } END_DO_FOR_EACH_PRIME
688 68 100         } else if (ix == 1) { /* Trial */
689 558 100         for (low = next_prime(low-1);
690 547 50         low <= high && low != 0;
691 547           low = next_prime(low) ) {
692 547           av_push(av,newSVuv(low));
693             }
694 57 100         } else if (ix == 2) { /* Erat with private memory */
695 9           unsigned char* sieve = sieve_erat30(high);
696 665 50         START_DO_FOR_EACH_SIEVE_PRIME( sieve, 0, low, high ) {
    100          
    100          
    100          
    100          
697 538           av_push(av,newSVuv(p));
698 26           } END_DO_FOR_EACH_SIEVE_PRIME
699 9           Safefree(sieve);
700 81 100         } else if (ix == 3 || ix == 4) { /* Segment */
    100          
701             unsigned char* segment;
702 33           UV seg_base, seg_low, seg_high, lastp = 0;
703 33           void* ctx = start_segment_primes(low, high, &segment);
704 70 100         while (next_segment_primes(ctx, &seg_base, &seg_low, &seg_high)) {
705 417230 50         START_DO_FOR_EACH_SIEVE_PRIME( segment, seg_base, seg_low, seg_high )
    100          
    100          
    100          
    100          
706 394200 100         if (ix == 3) av_push(av,newSVuv( p ));
707 86052 100         else if (lastp+2 == p) av_push(av,newSVuv( lastp ));
708 394200           lastp = p;
709 22960           END_DO_FOR_EACH_SIEVE_PRIME
710             }
711 33           end_segment_primes(ctx);
712 15 50         } else if (ix == 5) { /* Ramanujan primes */
713             UV i, beg, end, *L;
714 15           L = ramanujan_primes(&beg, &end, low, high);
715 15 50         if (L && end >= beg)
    100          
716 108 100         for (i = beg; i <= end; i++)
717 94           av_push(av,newSVuv(L[i]));
718 15           Safefree(L);
719 0 0         } else if (ix == 6) { /* Ramanujan primes */
720             UV i, *L;
721 0           L = n_range_ramanujan_primes(low, high);
722 0 0         if (L && high >= low)
    0          
723 0 0         for (i = low; i <= high; i++)
724 0           av_push(av,newSVuv(L[i-low]));
725 0           Safefree(L);
726             }
727             }
728 1209           return; /* skip implicit PUTBACK */
729              
730             void
731             sieve_range(IN SV* svn, IN UV width, IN UV depth)
732             PREINIT:
733             int status;
734             PPCODE:
735 10           status = _validate_int(aTHX_ svn, 0);
736 10 50         if (status == 1) {
737             /* TODO: actually sieve */
738 10 50         UV factors[MPU_MAX_FACTORS+1], i, n = my_svuv(svn);
739 10 50         if (depth == 0) depth = 1; /* Trial factor takes 0 to means sqrt(n) */
740 10 50         if ( (n + width) < n) { /* Overflow */
741 0           status = 0;
742 10 50         } else if (depth <= 100) { /* trial division for each value */
743 1620 100         for (i = (n<2)?2-n:0; i < width; i++)
    100          
744 1610 100         if (trial_factor(n+i, factors, 2, depth) < 2)
745 316 50         XPUSHs(sv_2mortal(newSVuv( i )));
746             } else { /* small trial + factor for each value */
747 10 0         for (i = (n<2)?2-n:0; i < width; i++)
    0          
748 0 0         if (trial_factor(n+i, factors, 2, 100) < 2)
749 0 0         if (factor(n+i,factors) < 2 || factors[0] > depth)
    0          
750 0 0         XPUSHs(sv_2mortal(newSVuv( i )));
751             }
752             }
753 10 50         if (status != 1) {
754 0 0         _vcallsubn(aTHX_ GIMME_V, VCALL_GMP|VCALL_PP, "sieve_range", items, 36);
755 0           return;
756             }
757              
758             void
759             sieve_prime_cluster(IN SV* svlo, IN SV* svhi, ...)
760             PREINIT:
761             uint32_t nc, cl[100];
762             UV i, cval, nprimes, *list;
763             int lostatus, histatus, done;
764             PPCODE:
765 41           nc = items-1;
766 41 50         if (items > 100) croak("sieve_prime_cluster: too many entries");
767 41           cl[0] = 0;
768 256 100         for (i = 1; i < nc; i++) {
769 215 50         if (!_validate_int(aTHX_ ST(1+i), 0)) croak("sieve_prime_cluster: cluster values must be standard integers");
770 215 50         cval = my_svuv(ST(1+i));
771 215 50         if (cval & 1) croak("sieve_prime_cluster: values must be even");
772 215 50         if (cval > 2147483647UL) croak("sieve_prime_cluster: values must be 31-bit");
773 215 50         if (cval <= cl[i-1]) croak("sieve_prime_cluster: values must be increasing");
774 215           cl[i] = cval;
775             }
776 41           lostatus = _validate_int(aTHX_ svlo, 1);
777 41           histatus = _validate_int(aTHX_ svhi, 1);
778 41           done = 0;
779 41 100         if (lostatus == 1 && histatus == 1) {
    50          
780 32 50         UV low = my_svuv(svlo);
781 32 50         UV high = my_svuv(svhi);
782 32           list = sieve_cluster(low, high, nc, cl, &nprimes);
783 32 50         if (list != 0) {
784 32           done = 1;
785 32 50         EXTEND(SP, (IV)nprimes);
    100          
786 12308 100         for (i = 0; i < nprimes; i++)
787 12276           PUSHs(sv_2mortal(newSVuv( list[i] )));
788 32           Safefree(list);
789             }
790             }
791 41 100         if (!done) {
792 9 50         _vcallsubn(aTHX_ GIMME_V, VCALL_GMP|VCALL_PP, "sieve_prime_cluster", items, 34);
793 9           return;
794             }
795              
796             void
797             trial_factor(IN UV n, ...)
798             ALIAS:
799             fermat_factor = 1
800             holf_factor = 2
801             squfof_factor = 3
802             lehman_factor = 4
803             prho_factor = 5
804             pplus1_factor = 6
805             pbrent_factor = 7
806             pminus1_factor = 8
807             ecm_factor = 9
808             PREINIT:
809             UV arg1, arg2;
810             static const UV default_arg1[] =
811             {0, 64000000, 8000000, 4000000, 0, 4000000, 200, 4000000, 1000000};
812             /* Trial, Fermat, Holf, SQUFOF, Lmn, PRHO, P+1, Brent, P-1 */
813             PPCODE:
814 83 50         if (n == 0) XSRETURN_UV(0);
815 83 50         if (ix == 9) { /* We don't have an ecm_factor, call PP. */
816 0 0         _vcallsubn(aTHX_ GIMME_V, VCALL_PP, "ecm_factor", 1, 0);
817 0           return;
818             }
819             /* Must read arguments before pushing anything */
820 83 100         arg1 = (items >= 2) ? my_svuv(ST(1)) : default_arg1[ix];
    50          
821 83 50         arg2 = (items >= 3) ? my_svuv(ST(2)) : 0;
    0          
822             /* Small factors */
823 131 50         while ( (n% 2) == 0 ) { n /= 2; XPUSHs(sv_2mortal(newSVuv( 2 ))); }
    100          
824 131 50         while ( (n% 3) == 0 ) { n /= 3; XPUSHs(sv_2mortal(newSVuv( 3 ))); }
    100          
825 147 50         while ( (n% 5) == 0 ) { n /= 5; XPUSHs(sv_2mortal(newSVuv( 5 ))); }
    100          
826 83 100         if (n == 1) { /* done */ }
827 43 100         else if (is_prime(n)) { XPUSHs(sv_2mortal(newSVuv( n ))); }
    50          
828             else {
829             UV factors[MPU_MAX_FACTORS+1];
830 19           int i, nfactors = 0;
831 19           switch (ix) {
832 5           case 0: nfactors = trial_factor (n, factors, 2, arg1); break;
833 2           case 1: nfactors = fermat_factor (n, factors, arg1); break;
834 2           case 2: nfactors = holf_factor (n, factors, arg1); break;
835 2           case 3: nfactors = squfof_factor (n, factors, arg1); break;
836 0           case 4: nfactors = lehman_factor (n, factors, arg1); break;
837 2           case 5: nfactors = prho_factor (n, factors, arg1); break;
838 2           case 6: nfactors = pplus1_factor (n, factors, arg1); break;
839 2 50         case 7: if (items < 3) arg2 = 1;
840 2           nfactors = pbrent_factor (n, factors, arg1, arg2); break;
841             case 8:
842 2 50         default: if (items < 3) arg2 = 10*arg1;
843 2           nfactors = pminus1_factor(n, factors, arg1, arg2); break;
844             }
845 19 50         EXTEND(SP, nfactors);
    50          
846 58 100         for (i = 0; i < nfactors; i++)
847 39           PUSHs(sv_2mortal(newSVuv( factors[i] )));
848             }
849              
850             void
851             is_strong_pseudoprime(IN SV* svn, ...)
852             ALIAS:
853             is_pseudoprime = 1
854             is_euler_pseudoprime = 2
855             PREINIT:
856 55098           int c, status = 1;
857             PPCODE:
858 55098 100         if (items < 2)
859 1           croak("No bases given to is_strong_pseudoprime");
860             /* Check all arguments */
861 215076 100         for (c = 0; c < items && status == 1; c++)
    100          
862 159979 100         if (_validate_int(aTHX_ ST(c), 0) != 1)
863 260           status = 0;
864 55097 100         if (status == 1) {
865 54837 50         UV n = my_svuv(svn);
866 54837           int b, ret = 1;
867 54837 100         if (n < 4) { /* 0,1 composite; 2,3 prime */
868 7           ret = (n >= 2);
869 54830 100         } else if (ix == 1) { /* Fermat test */
870 168 100         for (c = 1; c < items && ret == 1; c++)
    50          
871 86 50         ret = is_pseudoprime(n, my_svuv(ST(c)));
872 54748 100         } else if (ix == 2) { /* Euler test */
873 218 100         for (c = 1; c < items && ret == 1; c++)
    50          
874 109 50         ret = is_euler_pseudoprime(n, my_svuv(ST(c)));
875 54639 100         } else if ((n % 2) == 0) { /* evens composite */
876 27067           ret = 0;
877             } else {
878             UV bases[32];
879 55142 100         for (c = 1; c < items && ret == 1; ) {
    50          
880 80133 50         for (b = 0; b < 32 && c < items; c++)
    100          
881 52561 50         bases[b++] = my_svuv(ST(c));
882 27572           ret = miller_rabin(n, bases, b);
883             }
884             }
885 54835 50         RETURN_NPARITY(ret);
    50          
886             }
887 260           switch (ix) {
888 260           case 0: _vcallsub_with_gmp(0.00,"is_strong_pseudoprime"); break;
889 0           case 1: _vcallsub_with_gmp(0.20,"is_pseudoprime"); break;
890             case 2:
891 0           default:_vcallsub_with_gmp(0.00,"is_euler_pseudoprime"); break;
892             }
893 260           return; /* skip implicit PUTBACK */
894              
895             void
896             gcd(...)
897             PROTOTYPE: @
898             ALIAS:
899             lcm = 1
900             vecmin = 2
901             vecmax = 3
902             vecsum = 4
903             vecprod = 5
904             PREINIT:
905 20382           int i, status = 1;
906             UV ret, nullv, n;
907             PPCODE:
908 20384 100         if (ix == 2 || ix == 3) {
    100          
909 29           UV retindex = 0;
910 29           int sign, minmax = (ix == 2);
911 29 100         if (items == 0) XSRETURN_UNDEF;
912 27 100         if (items == 1) XSRETURN(1);
913 21           status = _validate_int(aTHX_ ST(0), 2);
914 21 100         if (status != 0 && items > 1) {
    50          
915 19           sign = status;
916 19 50         ret = my_svuv(ST(0));
917 98 100         for (i = 1; i < items; i++) {
918 79           status = _validate_int(aTHX_ ST(i), 2);
919 79 50         if (status == 0) break;
920 79 50         n = my_svuv(ST(i));
921 129 100         if (( (sign == -1 && status == 1) ||
    100          
    100          
    100          
922 50 100         (n >= ret && sign == status)
923             ) ? !minmax : minmax ) {
924 31           sign = status;
925 31           ret = n;
926 31           retindex = i;
927             }
928             }
929             }
930 21 100         if (status != 0) {
931 19           ST(0) = ST(retindex);
932 19           XSRETURN(1);
933             }
934 20353 100         } else if (ix == 4) {
935 2567           UV lo = 0;
936 2567           IV hi = 0;
937 120096 100         for (ret = i = 0; i < items; i++) {
938 118052           status = _validate_int(aTHX_ ST(i), 2);
939 118052 100         if (status == 0) break;
940 117649 100         n = my_svuv(ST(i));
941 117649 100         if (status == 1) {
942 116696           hi += (n > (UV_MAX - lo));
943             } else {
944 953 100         if (UV_MAX-n == (UV)IV_MAX) { status = 0; break; } /* IV Overflow */
945 833           hi -= ((UV_MAX-n) >= lo);
946             }
947 117529           lo += n;
948             }
949 2567 100         if (status != 0 && hi != 0) {
    100          
950 5 100         if (hi == -1 && lo > IV_MAX) XSRETURN_IV((IV)lo);
    50          
951 1 50         else RETURN_128(hi, lo);
952             }
953 2562           ret = lo;
954 17786 100         } else if (ix == 5) {
955 15816           int sign = 1;
956 15816           ret = 1;
957 21452 100         for (i = 0; i < items; i++) {
958 19664           status = _validate_int(aTHX_ ST(i), 2);
959 19664 100         if (status == 0) break;
960 6301 100         n = (status == 1) ? my_svuv(ST(i)) : (UV)-my_sviv(ST(i));
    50          
    50          
961 6301 100         if (ret > 0 && n > UV_MAX/ret) { status = 0; break; }
    100          
962 5636           sign *= status;
963 5636           ret *= n;
964             }
965 15816 100         if (sign == -1 && status != 0) {
    50          
966 5 50         if (ret <= (UV)IV_MAX) XSRETURN_IV(-(IV)ret);
967 15811           else status = 0;
968             }
969             } else {
970             /* For each arg, while valid input, validate+gcd/lcm. Shortcut stop. */
971 1970 100         if (ix == 0) { ret = 0; nullv = 1; }
972 24           else { ret = (items == 0) ? 0 : 1; nullv = 0; }
973 5912 100         for (i = 0; i < items && ret != nullv && status != 0; i++) {
    100          
    100          
974 3949           status = _validate_int(aTHX_ ST(i), 2);
975 3949 100         if (status == 0)
976 7           break;
977 3942 100         n = status * my_svuv(ST(i)); /* n = abs(arg) */
978 3942 100         if (i == 0) {
979 1964           ret = n;
980             } else {
981 1978           UV gcd = gcd_ui(ret, n);
982 1978 100         if (ix == 0) {
983 1950           ret = gcd;
984             } else {
985 28           n /= gcd;
986 28 100         if (n <= (UV_MAX / ret) ) ret *= n;
987 2           else status = 0; /* Overflow */
988             }
989             }
990             }
991             }
992 20345 100         if (status != 0)
993 5783           XSRETURN_UV(ret);
994             /* For min/max, use string compare if not an object */
995 14562 100         if ((ix == 2 || ix == 3) && !sv_isobject(ST(0))) {
    100          
    50          
996 2           int retindex = 0;
997 2           int minmax = (ix == 2);
998             STRLEN alen, blen;
999             char *aptr, *bptr;
1000 2 50         aptr = SvPV(ST(0), alen);
1001 2           (void) strnum_minmax(minmax, 0, 0, aptr, alen);
1002 4 100         for (i = 1; i < items; i++) {
1003 2 50         bptr = SvPV(ST(i), blen);
1004 2 50         if (strnum_minmax(minmax, aptr, alen, bptr, blen)) {
1005 2           aptr = bptr;
1006 2           alen = blen;
1007 2           retindex = i;
1008             }
1009             }
1010 2           ST(0) = ST(retindex);
1011 2           XSRETURN(1);
1012             }
1013 14560           switch (ix) {
1014 6           case 0: _vcallsub_with_gmp(0.17,"gcd"); break;
1015 3           case 1: _vcallsub_with_gmp(0.17,"lcm"); break;
1016 0           case 2: _vcallsub_with_gmp(0.00,"vecmin"); break;
1017 0           case 3: _vcallsub_with_gmp(0.00,"vecmax"); break;
1018 523           case 4: _vcallsub_with_pp("vecsum"); break;
1019             case 5:
1020 14028           default:_vcallsub_with_pp("vecprod"); break;
1021             }
1022 14560           return; /* skip implicit PUTBACK */
1023              
1024             void
1025             vecextract(IN SV* x, IN SV* svm)
1026             PREINIT:
1027             AV* av;
1028 2           UV i = 0;
1029             PPCODE:
1030 2 50         if ((!SvROK(x)) || (SvTYPE(SvRV(x)) != SVt_PVAV))
    50          
1031 0           croak("vecextract first argument must be an array reference");
1032 2           av = (AV*) SvRV(x);
1033 3 100         if (SvROK(svm) && SvTYPE(SvRV(svm)) == SVt_PVAV) {
    50          
1034 1           AV* avm = (AV*) SvRV(svm);
1035 1           int j, mlen = av_len(avm);
1036 6 100         for (j = 0; j <= mlen; j++) {
1037 5           SV** iv = av_fetch(avm, j, 0);
1038 5 50         if (iv && SvTYPE(*iv) == SVt_IV) {
    50          
1039 5 50         SV **v = av_fetch(av, SvIV(*iv), 0);
1040 5 50         if (v) XPUSHs(*v);
    50          
1041             }
1042             }
1043 1 50         } else if (_validate_int(aTHX_ svm, 0)) {
1044 1 50         UV mask = my_svuv(svm);
1045 25 100         while (mask) {
1046 24 100         if (mask & 1) {
1047 13           SV** v = av_fetch(av, i, 0);
1048 13 50         if (v) XPUSHs(*v);
    50          
1049             }
1050 24           i++;
1051 24           mask >>= 1;
1052             }
1053             } else {
1054 0 0         _vcallsubn(aTHX_ GIMME_V, VCALL_PP, "vecextract", items, 0);
1055 0           return;
1056             }
1057              
1058             void
1059             chinese(...)
1060             PROTOTYPE: @
1061             PREINIT:
1062             int i, status;
1063             UV ret, *an;
1064             SV **psva, **psvn;
1065             PPCODE:
1066 25           status = 1;
1067 25 50         New(0, an, 2*items, UV);
1068 25           ret = 0;
1069 69 100         for (i = 0; i < items; i++) {
1070             AV* av;
1071 47 50         if (!SvROK(ST(i)) || SvTYPE(SvRV(ST(i))) != SVt_PVAV || av_len((AV*)SvRV(ST(i))) != 1)
    50          
    50          
1072 0           croak("chinese arguments are two-element array references");
1073 47           av = (AV*) SvRV(ST(i));
1074 47           psva = av_fetch(av, 0, 0);
1075 47           psvn = av_fetch(av, 1, 0);
1076 47 50         if (psva == 0 || psvn == 0 || _validate_int(aTHX_ *psva, 1) != 1 || !_validate_int(aTHX_ *psvn, 0)) {
    50          
    100          
    50          
1077 3           status = 0;
1078 3           break;
1079             }
1080 44 50         an[i+0] = my_svuv(*psva);
1081 44 50         an[i+items] = my_svuv(*psvn);
1082             }
1083 25 100         if (status)
1084 22           ret = chinese(an, an+items, items, &status);
1085 25           Safefree(an);
1086 25 100         if (status == -1) XSRETURN_UNDEF;
1087 20 100         if (status) XSRETURN_UV(ret);
1088 7           psvn = av_fetch((AV*) SvRV(ST(0)), 1, 0);
1089 7           _vcallsub_with_gmpobj(0.32,"chinese");
1090 7 100         OBJECTIFY_RESULT( (psvn ? *psvn : 0), ST(0));
    50          
    50          
    50          
    100          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    0          
    0          
    0          
    50          
    50          
    50          
    50          
    50          
    50          
    0          
    0          
    50          
    50          
    100          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
1091 25           return; /* skip implicit PUTBACK */
1092              
1093             void
1094             lucas_sequence(...)
1095             ALIAS:
1096             lucasu = 1
1097             lucasv = 2
1098             PREINIT:
1099             UV U, V, Qk;
1100             PPCODE:
1101 26644 100         if (ix == 1 || ix == 2) {
    100          
1102 351 50         if (items != 3) croak("lucasu: P, Q, k");
1103 702 50         if (_validate_int(aTHX_ ST(0), 1) && _validate_int(aTHX_ ST(1), 1) &&
1104 351           _validate_int(aTHX_ ST(2), 0)) {
1105 351 50         IV P = my_sviv(ST(0));
1106 351 50         IV Q = my_sviv(ST(1));
1107 351 50         UV k = my_svuv(ST(2));
1108             IV ret;
1109 351 100         int ok = (ix == 1) ? lucasu(&ret, P, Q, k) : lucasv(&ret, P, Q, k);
1110 351 50         if (ok) XSRETURN_IV(ret);
1111             }
1112 0 0         _vcallsub_with_gmpobj(0.29,(ix==1) ? "lucasu" : "lucasv");
1113 0 0         OBJECTIFY_RESULT(ST(2), ST(0));
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
1114 0           return;
1115             }
1116 26293 50         if (items != 4) croak("lucas_sequence: n, P, Q, k");
1117 52584 100         if (_validate_int(aTHX_ ST(0), 0) && _validate_int(aTHX_ ST(1), 1) &&
1118 52582 50         _validate_int(aTHX_ ST(2), 1) && _validate_int(aTHX_ ST(3), 0)) {
1119 26291 50         lucas_seq(&U, &V, &Qk,
    100          
    100          
    50          
1120 210328           my_svuv(ST(0)), my_sviv(ST(1)), my_sviv(ST(2)), my_svuv(ST(3)));
1121 26291           PUSHs(sv_2mortal(newSVuv( U ))); /* 4 args in, 3 out, no EXTEND needed */
1122 26291           PUSHs(sv_2mortal(newSVuv( V )));
1123 26291           PUSHs(sv_2mortal(newSVuv( Qk )));
1124             } else {
1125 2 50         _vcallsubn(aTHX_ GIMME_V, VCALL_PP, "lucas_sequence", items, 0);
1126 26644           return;
1127             }
1128              
1129             void is_prime(IN SV* svn)
1130             ALIAS:
1131             is_prob_prime = 1
1132             is_provable_prime = 2
1133             is_bpsw_prime = 3
1134             is_aks_prime = 4
1135             is_lucas_pseudoprime = 5
1136             is_strong_lucas_pseudoprime = 6
1137             is_extra_strong_lucas_pseudoprime = 7
1138             is_frobenius_underwood_pseudoprime = 8
1139             is_frobenius_khashin_pseudoprime = 9
1140             is_catalan_pseudoprime = 10
1141             is_euler_plumb_pseudoprime = 11
1142             is_ramanujan_prime = 12
1143             is_square_free = 13
1144             is_carmichael = 14
1145             is_quasi_carmichael = 15
1146             is_semiprime = 16
1147             is_square = 17
1148             is_mersenne_prime = 18
1149             is_totient = 19
1150             PREINIT:
1151             int status, ret;
1152             PPCODE:
1153 455142           ret = 0;
1154 455142           status = _validate_int(aTHX_ svn, 1);
1155 455139 100         if (status == 1) {
1156 454811 100         UV n = my_svuv(svn);
1157 454811           switch (ix) {
1158             case 0:
1159             case 1:
1160 425556           case 2: ret = is_prime(n); break;
1161 0           case 3: ret = BPSW(n); break;
1162 7           case 4: ret = is_aks_prime(n); break;
1163 20           case 5: ret = is_lucas_pseudoprime(n, 0); break;
1164 29           case 6: ret = is_lucas_pseudoprime(n, 1); break;
1165 22           case 7: ret = is_lucas_pseudoprime(n, 3); break;
1166 102           case 8: ret = is_frobenius_underwood_pseudoprime(n); break;
1167 102           case 9: ret = is_frobenius_khashin_pseudoprime(n); break;
1168 3           case 10: ret = is_catalan_pseudoprime(n); break;
1169 29           case 11: ret = is_euler_plumb_pseudoprime(n); break;
1170 984           case 12: ret = is_ramanujan_prime(n); break;
1171 28           case 13: ret = is_square_free(n); break;
1172 20000           case 14: ret = is_carmichael(n); break;
1173 5402           case 15: ret = is_quasi_carmichael(n); break;
1174 103           case 16: ret = is_semiprime(n); break;
1175 18           case 17: ret = is_power(n,2); break;
1176 2282 50         case 18: ret = is_mersenne_prime(n); if (ret == -1) status = 0; break;
1177             case 19:
1178 454811           default: ret = is_totient(n); break;
1179             }
1180 328 100         } else if (status == -1) {
1181             /* Result for negative inputs will be zero unless changed here */
1182 36 100         if (ix == 13) {
1183 26 50         IV sn = my_sviv(svn);
1184 26 50         if (sn > -IV_MAX) ret = is_square_free(-sn);
1185 0           else status = 0;
1186             }
1187             }
1188 455139 100         if (status != 0) RETURN_NPARITY(ret);
    50          
    50          
1189 292           switch (ix) {
1190 44           case 0: _vcallsub_with_gmp(0.01,"is_prime"); break;
1191 222           case 1: _vcallsub_with_gmp(0.01,"is_prob_prime"); break;
1192 5           case 2: _vcallsub_with_gmp(0.04,"is_provable_prime"); break;
1193 1           case 3: _vcallsub_with_gmp(0.17,"is_bpsw_prime"); break;
1194 0           case 4: _vcallsub_with_gmp(0.16,"is_aks_prime"); break;
1195 0           case 5: _vcallsub_with_gmp(0.01,"is_lucas_pseudoprime"); break;
1196 0           case 6: _vcallsub_with_gmp(0.01,"is_strong_lucas_pseudoprime"); break;
1197 12           case 7: _vcallsub_with_gmp(0.01,"is_extra_strong_lucas_pseudoprime"); break;
1198 0           case 8: _vcallsub_with_gmp(0.13,"is_frobenius_underwood_pseudoprime"); break;
1199 0           case 9: _vcallsub_with_gmp(0.30,"is_frobenius_khashin_pseudoprime"); break;
1200 0           case 10:_vcallsub_with_gmp(0.00,"is_catalan_pseudoprime"); break;
1201 0           case 11:_vcallsub_with_gmp(0.39,"is_euler_plumb_pseudoprime"); break;
1202 0           case 12:_vcallsub_with_gmp(0.00,"is_ramanujan_prime"); break;
1203 2           case 13:_vcallsub_with_gmp(0.00,"is_square_free"); break;
1204 1           case 14:_vcallsub_with_gmp(0.47,"is_carmichael"); break;
1205 0           case 15:_vcallsub_with_gmp(0.00,"is_quasi_carmichael"); break;
1206 1           case 16:_vcallsub_with_gmp(0.42,"is_semiprime"); break;
1207 1           case 17:_vcallsub_with_gmp(0.47,"is_square"); break;
1208 0           case 18:_vcallsub_with_gmp(0.28,"is_mersenne_prime"); break;
1209             case 19:
1210 3           default:_vcallsub_with_gmp(0.47,"is_totient"); break;
1211             }
1212 292           return; /* skip implicit PUTBACK */
1213              
1214             void is_fundamental(IN SV* svn)
1215             PREINIT:
1216             int status;
1217             PPCODE:
1218 104           status = _validate_int(aTHX_ svn, 1);
1219 104 100         if (status == 1)
1220 52 50         RETURN_NPARITY(is_fundamental(my_svuv(svn), 0));
    50          
    50          
1221 52 100         if (status == -1) {
1222 50 50         IV sn = my_sviv(svn);
1223 50 50         if (sn > -IV_MAX)
1224 50 50         RETURN_NPARITY(is_fundamental(-sn, 1));
    50          
1225             }
1226 2           _vcallsub_with_gmp(0.00,"is_fundamental");
1227 2           return;
1228              
1229              
1230             void
1231             is_power(IN SV* svn, IN UV k = 0, IN SV* svroot = 0)
1232             PREINIT:
1233             int status;
1234             PPCODE:
1235 10660           status = _validate_int(aTHX_ svn, 1);
1236 10660 100         if (status != 0) {
1237 10404           int ret = 0;
1238 10404 100         UV n = my_svuv(svn);
1239 10404 100         if (status == -1) {
1240 65 100         IV sn = my_sviv(svn);
1241 65 100         if (sn <= -IV_MAX) status = 0;
1242 63           else n = -sn;
1243             }
1244 10404 100         if (status == 1 || (status == -1 && (k == 0 || k & 1))) {
    100          
    100          
    100          
1245 10400           ret = is_power(n, k);
1246 10400 100         if (status == -1 && k == 0) {
    100          
1247 59           ret >>= valuation(ret,2);
1248 59 100         if (ret == 1) ret = 0;
1249             }
1250 10400 100         if (ret && svroot != 0) {
    100          
1251 29 100         UV root = rootof(n, k ? k : (UV)ret);
1252 29 50         if (!SvROK(svroot)) croak("is_power: third argument not a scalar reference");
1253 29 100         if (status == 1) sv_setuv(SvRV(svroot), root);
1254 18           else sv_setiv(SvRV(svroot), -root);
1255             }
1256             }
1257 10404 100         if (status != 0) RETURN_NPARITY(ret);
    50          
    50          
1258             }
1259 258 100         if (svroot == 0) { _vcallsub_with_gmp(0.28, "is_power"); }
1260 128           else { _vcallsub_with_pp("is_power"); }
1261 258           return;
1262              
1263             void
1264             is_prime_power(IN SV* svn, IN SV* svroot = 0)
1265             PREINIT:
1266             int status, ret;
1267             UV n, root;
1268             PPCODE:
1269 10254           status = _validate_int(aTHX_ svn, 1);
1270 10254 50         if (status == -1)
1271 0 0         RETURN_NPARITY(0);
    0          
1272 10254 50         if (status != 0) {
1273 10254 50         n = my_svuv(svn);
1274 10254           ret = primepower(n, &root);
1275 10254 100         if (ret && svroot != 0) {
    100          
1276 17 50         if (!SvROK(svroot))croak("is_prime_power: second argument not a scalar reference");
1277 17           sv_setuv(SvRV(svroot), root);
1278             }
1279 10254 50         RETURN_NPARITY(ret);
    50          
1280             }
1281 0 0         (void)_vcallsubn(aTHX_ G_SCALAR, (svroot == 0) ? (VCALL_GMP|VCALL_PP) : (VCALL_PP), "is_prime_power", items, 40);
1282 10254           return;
1283              
1284             void
1285             is_perrin_pseudoprime(IN SV* svn, IN int k = 0)
1286             ALIAS:
1287             is_almost_extra_strong_lucas_pseudoprime = 1
1288             PREINIT:
1289             int status, ret;
1290             PPCODE:
1291 71           ret = 0;
1292 71           status = _validate_int(aTHX_ svn, 1);
1293 71 100         if (status == 1) {
1294 70 50         UV n = my_svuv(svn);
1295 70 100         if (ix == 0) ret = is_perrin_pseudoprime(n, k);
1296 50           else ret = is_almost_extra_strong_lucas_pseudoprime(n, (k < 1) ? 1 : k);
1297             }
1298 71 100         if (status != 0) RETURN_NPARITY(ret);
    50          
    50          
1299 1 50         if (ix == 0)
1300 1 50         _vcallsub_with_gmp( (k == 0) ? 0.20 : 0.40, "is_perrin_pseudoprime");
1301             else
1302 0           _vcallsub_with_gmp(0.13,"is_almost_extra_strong_lucas_pseudoprime");
1303 1           return;
1304              
1305             void
1306             is_frobenius_pseudoprime(IN SV* svn, IN IV P = 0, IN IV Q = 0)
1307             PREINIT:
1308             int status, ret;
1309             PPCODE:
1310 28           ret = 0;
1311 28           status = _validate_int(aTHX_ svn, 1);
1312 28 50         if (status == 1) {
1313 28 50         UV n = my_svuv(svn);
1314 28           ret = is_frobenius_pseudoprime(n, P, Q);
1315             }
1316 28 50         if (status != 0) RETURN_NPARITY(ret);
    50          
    50          
1317 0           _vcallsub_with_gmp(0.24,"is_frobenius_pseudoprime");
1318 0           return;
1319              
1320             void
1321             is_polygonal(IN SV* svn, IN UV k, IN SV* svroot = 0)
1322             PREINIT:
1323             int status, result, overflow;
1324             UV n, root;
1325             PPCODE:
1326 25302 50         if (k < 3) croak("is_polygonal: k must be >= 3");
1327 25302           status = _validate_int(aTHX_ svn, 1);
1328 25302 100         if (status != 0) {
1329 25300           overflow = 0;
1330 25300 50         if (status == -1) {
1331 0           result = 0;
1332             } else {
1333 25300 50         n = my_svuv(svn);
1334 25300           root = polygonal_root(n, k, &overflow);
1335 25300 50         result = (n == 0) || root;
    100          
1336             }
1337 25300 50         if (!overflow) {
1338 25300 100         if (result && svroot != 0) {
    100          
1339 230 50         if (!SvROK(svroot)) croak("is_polygonal: third argument not a scalar reference");
1340 230           sv_setuv(SvRV(svroot), root);
1341             }
1342 25300 50         RETURN_NPARITY(result);
    50          
1343             }
1344             }
1345 2 50         if (items != 3) { _vcallsub_with_gmp(0.47, "is_polygonal"); }
1346 0           else { _vcallsub_with_pp("is_polygonal"); }
1347 25302           return;
1348              
1349             void
1350             logint(IN SV* svn, IN UV k, IN SV* svret = 0)
1351             ALIAS:
1352             rootint = 1
1353             PREINIT:
1354             int status;
1355             UV n, root;
1356             PPCODE:
1357 626           status = _validate_int(aTHX_ svn, 1);
1358 626 100         if (status != 0) {
1359 624 50         n = my_svuv(svn);
1360 624 100         if (svret != 0 && !SvROK(svret))
    50          
1361 0 0         croak("%s: third argument not a scalar reference",(ix==0)?"logint":"rootint");
1362 624 100         if (ix == 0) {
1363 601 50         if (status != 1 || n <= 0) croak("logint: n must be > 0");
    50          
1364 601 50         if (k <= 1) croak("logint: base must be > 1");
1365 601           root = logint(n, k);
1366 601 100         if (svret) sv_setuv(SvRV(svret), ipow(k,root));
1367             } else {
1368 23 50         if (status == -1) croak("rootint: n must be >= 0");
1369 23 50         if (k <= 0) croak("rootint: k must be > 0");
1370 23           root = rootof(n, k);
1371 23 100         if (svret) sv_setuv(SvRV(svret), ipow(root,k));
1372             }
1373 624           XSRETURN_UV(root);
1374             }
1375 2           switch (ix) {
1376 0 0         case 0: (void)_vcallsubn(aTHX_ G_SCALAR, (svret == 0) ? (VCALL_GMP|VCALL_PP) : (VCALL_PP), "logint", items, 47); break;
1377 2 50         case 1: (void)_vcallsubn(aTHX_ G_SCALAR, (svret == 0) ? (VCALL_GMP|VCALL_PP) : (VCALL_PP), "rootint", items, 40); break;
1378 0           default: break;
1379             }
1380 2           return;
1381              
1382             void
1383             miller_rabin_random(IN SV* svn, IN IV bases = 1, IN char* seed = 0)
1384             PREINIT:
1385             int status;
1386             dMY_CXT;
1387             PPCODE:
1388 9           status = _validate_int(aTHX_ svn, 0);
1389 8 100         if (bases < 0) croak("miller_rabin_random: number of bases must be positive");
1390 7 100         if (status != 0 && seed == 0) {
    50          
1391 5 50         UV n = my_svuv(svn);
1392 5 50         RETURN_NPARITY( is_mr_random(MY_CXT.randcxt, n, bases) );
    50          
1393             }
1394 2           _vcallsub_with_gmp(0.46,"miller_rabin_random");
1395 2           return;
1396              
1397             void
1398             next_prime(IN SV* svn)
1399             ALIAS:
1400             prev_prime = 1
1401             nth_prime = 2
1402             nth_prime_upper = 3
1403             nth_prime_lower = 4
1404             nth_prime_approx = 5
1405             inverse_li = 6
1406             nth_twin_prime = 7
1407             nth_twin_prime_approx = 8
1408             nth_ramanujan_prime = 9
1409             nth_ramanujan_prime_upper = 10
1410             nth_ramanujan_prime_lower = 11
1411             nth_ramanujan_prime_approx = 12
1412             prime_count_upper = 13
1413             prime_count_lower = 14
1414             prime_count_approx = 15
1415             ramanujan_prime_count_upper = 16
1416             ramanujan_prime_count_lower = 17
1417             twin_prime_count_approx = 18
1418             urandomm = 19
1419             PPCODE:
1420 10006 100         if (_validate_int(aTHX_ svn, 0)) {
1421 9947 100         UV n = my_svuv(svn);
1422 9951 100         if ( (n >= MPU_MAX_PRIME && ix == 0) ||
    100          
    100          
1423 9943 50         (n >= MPU_MAX_PRIME_IDX && (ix==2 || ix==3 || ix==4 || ix==5 || ix == 6)) ||
    100          
    100          
    50          
    50          
    100          
1424 9939 50         (n >= MPU_MAX_TWIN_PRIME_IDX && (ix==7 || ix==8)) ||
    50          
    100          
1425 41 50         (n >= MPU_MAX_RMJN_PRIME_IDX && (ix==9)) ) {
1426             /* Out of range. Fall through to Perl. */
1427             } else {
1428             UV ret;
1429             /* Prev prime of 2 or less should return undef */
1430 9939 100         if (ix == 1 && n < 3) XSRETURN_UNDEF;
    100          
1431             /* nth_prime(0) and similar should return undef */
1432 9934 100         if (n == 0 && (ix >= 2 && ix <= 9 && ix != 6)) XSRETURN_UNDEF;
    100          
    100          
    100          
1433 9931           switch (ix) {
1434 4036           case 0: ret = next_prime(n); break;
1435 3754 50         case 1: ret = (n < 3) ? 0 : prev_prime(n); break;
1436 1114           case 2: ret = nth_prime(n); break;
1437 39           case 3: ret = nth_prime_upper(n); break;
1438 25           case 4: ret = nth_prime_lower(n); break;
1439 25           case 5: ret = nth_prime_approx(n); break;
1440 53           case 6: ret = inverse_li(n); break;
1441 53           case 7: ret = nth_twin_prime(n); break;
1442 10           case 8: ret = nth_twin_prime_approx(n); break;
1443 75           case 9: ret = nth_ramanujan_prime(n); break;
1444 74           case 10:ret = nth_ramanujan_prime_upper(n); break;
1445 74           case 11:ret = nth_ramanujan_prime_lower(n); break;
1446 2           case 12:ret = nth_ramanujan_prime_approx(n); break;
1447 35           case 13:ret = prime_count_upper(n); break;
1448 35           case 14:ret = prime_count_lower(n); break;
1449 36           case 15:ret = prime_count_approx(n); break;
1450 74           case 16:ret = ramanujan_prime_count_upper(n); break;
1451 74           case 17:ret = ramanujan_prime_count_lower(n); break;
1452 7           case 18:ret = twin_prime_count_approx(n); break;
1453             case 19:
1454 336           default:{ dMY_CXT; ret = urandomm64(MY_CXT.randcxt,n); } break;
1455             }
1456 9931           XSRETURN_UV(ret);
1457             }
1458             }
1459 48 100         if ((ix == 0 || ix == 1) && _XS_get_callgmp() && PERL_REVISION >= 5 && PERL_VERSION > 8) {
    100          
    50          
1460 0 0         _vcallsub_with_gmpobj(0.01, ix ? "prev_prime" : "next_prime");
1461 0 0         OBJECTIFY_RESULT(svn, ST(0));
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
1462 0           return;
1463             }
1464 48           switch (ix) {
1465 7           case 0: _vcallsub_with_pp("next_prime"); break;
1466 2           case 1: _vcallsub_with_pp("prev_prime"); break;
1467 0           case 2: _vcallsub_with_pp("nth_prime"); break;
1468 1           case 3: _vcallsub_with_pp("nth_prime_upper"); break;
1469 3           case 4: _vcallsub_with_pp("nth_prime_lower"); break;
1470 0           case 5: _vcallsub_with_pp("nth_prime_approx"); break;
1471 0           case 6: _vcallsub_with_pp("inverse_li"); break;
1472 0           case 7: _vcallsub_with_pp("nth_twin_prime"); break;
1473 0           case 8: _vcallsub_with_pp("nth_twin_prime_approx"); break;
1474 0           case 9: _vcallsub_with_pp("nth_ramanujan_prime"); break;
1475 0           case 10: _vcallsub_with_pp("nth_ramanujan_prime_upper"); break;
1476 0           case 11: _vcallsub_with_pp("nth_ramanujan_prime_lower"); break;
1477 0           case 12: _vcallsub_with_pp("nth_ramanujan_prime_approx"); break;
1478 8           case 13: _vcallsub_with_pp("prime_count_upper"); break;
1479 8           case 14: _vcallsub_with_pp("prime_count_lower"); break;
1480 4           case 15: _vcallsub_with_pp("prime_count_approx"); break;
1481 0           case 16: _vcallsub_with_pp("ramanujan_prime_count_upper"); break;
1482 0           case 17: _vcallsub_with_pp("ramanujan_prime_count_lower"); break;
1483 0           case 18: _vcallsub_with_pp("twin_prime_count_approx"); break;
1484             case 19:
1485 15           default: _vcallsub_with_gmpobj(0.44,"urandomm");
1486 15 50         OBJECTIFY_RESULT(svn, ST(0));
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
1487 15           break;
1488             }
1489 47           return; /* skip implicit PUTBACK */
1490              
1491             void urandomb(IN UV bits)
1492             ALIAS:
1493             random_ndigit_prime = 1
1494             random_semiprime = 2
1495             random_unrestricted_semiprime = 3
1496             random_nbit_prime = 4
1497             random_shawe_taylor_prime = 5
1498             random_maurer_prime = 6
1499             random_proven_prime = 7
1500             random_strong_prime = 8
1501             PREINIT:
1502             UV res, minarg;
1503             dMY_CXT;
1504             void* cs;
1505             PPCODE:
1506 712           switch (ix) {
1507 23           case 1: minarg = 1; break;
1508 4           case 2: minarg = 4; break;
1509 3           case 3: minarg = 3; break;
1510             case 4:
1511             case 5:
1512             case 6:
1513 263           case 7: minarg = 2; break;
1514 1           case 8: minarg = 128; break;
1515 418           default: minarg = 0; break;
1516             }
1517 712 100         if (minarg > 0 && bits < minarg)
    100          
1518 6           croak("Parameter '%d' must be >= %d", (int)bits, (int)minarg);
1519 706           cs = MY_CXT.randcxt;
1520 706 100         if (bits <= BITS_PER_WORD) {
1521 660           switch (ix) {
1522 383           case 0: res = urandomb(cs,bits); break;
1523 22           case 1: res = random_ndigit_prime(cs,bits); break;
1524 2           case 2: res = random_semiprime(cs,bits); break;
1525 1           case 3: res = random_unrestricted_semiprime(cs,bits); break;
1526             case 4:
1527             case 5:
1528             case 6:
1529             case 7:
1530             case 8:
1531 252           default: res = random_nbit_prime(cs,bits); break;
1532             }
1533 660 100         if (res || ix == 0) XSRETURN_UV(res);
    100          
1534             }
1535 49           switch (ix) {
1536 35           case 0: _vcallsub_with_gmpobj(0.43,"urandomb"); break;
1537 3           case 1: _vcallsub_with_gmpobj(0.42,"random_ndigit_prime"); break;
1538 1           case 2: _vcallsub_with_gmpobj(0.00,"random_semiprime"); break;
1539 1           case 3: _vcallsub_with_gmpobj(0.00,"random_unrestricted_semiprime"); break;
1540 4           case 4: _vcallsub_with_gmpobj(0.42,"random_nbit_prime"); break;
1541 1           case 5: _vcallsub_with_gmpobj(0.43,"random_shawe_taylor_prime"); break;
1542             case 6:
1543 3           case 7: _vcallsub_with_gmpobj(0.43,"random_maurer_prime"); break;
1544             case 8:
1545 1           default: _vcallsub_with_gmpobj(0.43,"random_strong_prime"); break;
1546             }
1547 49 100         OBJECTIFY_RESULT(ST(0), ST(0));
    50          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
1548 49           XSRETURN(1);
1549              
1550             void Pi(IN UV digits = 0)
1551             PREINIT:
1552             #ifdef USE_QUADMATH
1553             #define STRTONV(t) strtoflt128(t,NULL)
1554             const UV mantsize = FLT128_DIG;
1555             const NV pival = 3.141592653589793238462643383279502884197169Q;
1556             #elif defined(USE_LONG_DOUBLE) && defined(HAS_LONG_DOUBLE)
1557             #define STRTONV(t) strtold(t,NULL)
1558             const UV mantsize = LDBL_DIG;
1559             const NV pival = 3.141592653589793238462643383279502884197169L;
1560             #else
1561             #define STRTONV(t) strtod(t,NULL)
1562 1001           const UV mantsize = DBL_DIG;
1563 1001           const NV pival = 3.141592653589793238462643383279502884197169;
1564             #endif
1565             PPCODE:
1566 1001 100         if (digits == 0) {
1567 1           XSRETURN_NV( pival );
1568 1000 100         } else if (digits <= mantsize) {
1569 15           char* out = pidigits(digits);
1570 15           NV pi = STRTONV(out);
1571 15           Safefree(out);
1572 15           XSRETURN_NV( pi );
1573             } else {
1574 985           _vcallsub_with_pp("Pi");
1575 985           return;
1576             }
1577              
1578             void
1579             _pidigits(IN int digits)
1580             PREINIT:
1581             char* out;
1582             PPCODE:
1583 972 50         if (digits <= 0) XSRETURN_EMPTY;
1584 972           out = pidigits(digits);
1585 972 50         XPUSHs(sv_2mortal(newSVpvn(out, digits+1)));
1586 972           Safefree(out);
1587              
1588             void
1589             factor(IN SV* svn)
1590             ALIAS:
1591             factor_exp = 1
1592             divisors = 2
1593             PREINIT:
1594             U32 gimme_v;
1595             int status, i, nfactors;
1596             PPCODE:
1597 1575 50         gimme_v = GIMME_V;
1598 1575           status = _validate_int(aTHX_ svn, 0);
1599 1575 100         if (status == 1) {
1600             UV factors[MPU_MAX_FACTORS+1];
1601             UV exponents[MPU_MAX_FACTORS+1];
1602 1401 100         UV n = my_svuv(svn);
1603 1401 100         if (gimme_v == G_SCALAR) {
1604 52           switch (ix) {
1605 21           case 0: nfactors = factor(n, factors); break;
1606 10           case 1: nfactors = factor_exp(n, factors, 0); break;
1607 21           default: nfactors = divisor_sum(n, 0); break;
1608             }
1609 52           PUSHs(sv_2mortal(newSVuv( nfactors )));
1610 1349 50         } else if (gimme_v == G_ARRAY) {
1611 1349           switch (ix) {
1612 99           case 0: nfactors = factor(n, factors);
1613 99 50         EXTEND(SP, nfactors);
    50          
1614 529 100         for (i = 0; i < nfactors; i++)
1615 430           PUSHs(sv_2mortal(newSVuv( factors[i] )));
1616 99           break;
1617 228           case 1: nfactors = factor_exp(n, factors, exponents);
1618             /* if (n == 1) XSRETURN_EMPTY; */
1619 228 50         EXTEND(SP, nfactors);
    50          
1620 717 100         for (i = 0; i < nfactors; i++) {
1621 489           AV* av = newAV();
1622 489           av_push(av, newSVuv(factors[i]));
1623 489           av_push(av, newSVuv(exponents[i]));
1624 489           PUSHs( sv_2mortal(newRV_noinc( (SV*) av )) );
1625             }
1626 228           break;
1627             default: {
1628             UV ndivisors;
1629 1022           UV* divs = _divisor_list(n, &ndivisors);
1630 1022 50         EXTEND(SP, (IV)ndivisors);
    50          
1631 7383 100         for (i = 0; (UV)i < ndivisors; i++)
1632 6361           PUSHs(sv_2mortal(newSVuv( divs[i] )));
1633 1022           Safefree(divs);
1634             }
1635 1401           break;
1636             }
1637             }
1638             } else {
1639 174           switch (ix) {
1640 93           case 0: _vcallsubn(aTHX_ gimme_v, VCALL_ROOT, "_generic_factor", 1, 0); break;
1641 14           case 1: _vcallsubn(aTHX_ gimme_v, VCALL_ROOT, "_generic_factor_exp", 1, 0); break;
1642 67           default: _vcallsubn(aTHX_ gimme_v, VCALL_GMP|VCALL_PP, "divisors", 1, 0); break;
1643             }
1644 174           return; /* skip implicit PUTBACK */
1645             }
1646              
1647             void
1648             divisor_sum(IN SV* svn, ...)
1649             PREINIT:
1650             SV* svk;
1651             int nstatus, kstatus;
1652             PPCODE:
1653 2331 100         svk = (items > 1) ? ST(1) : 0;
1654 2331           nstatus = _validate_int(aTHX_ svn, 0);
1655 2331 100         kstatus = (items == 1 || (SvIOK(svk) && SvIV(svk) >= 0)) ? 1 : 0;
    100          
    50          
    50          
    0          
1656             /* The above doesn't understand small bigints */
1657 2331 100         if (nstatus == 1 && kstatus == 0 && SvROK(svk) && (sv_isa(svk, "Math::BigInt") || sv_isa(svk, "Math::GMP") || sv_isa(svk, "Math::GMPz")))
    100          
    50          
    50          
    50          
    50          
1658 0           kstatus = _validate_int(aTHX_ svk, 0);
1659 2331 100         if (nstatus == 1 && kstatus == 1) {
    100          
1660 1411 50         UV n = my_svuv(svn);
1661 1411 100         UV k = (items > 1) ? my_svuv(svk) : 1;
    50          
1662 1411           UV sigma = divisor_sum(n, k);
1663 1411 50         if (sigma != 0) XSRETURN_UV(sigma); /* sigma 0 means overflow */
1664             }
1665 920           _vcallsub_with_gmp(0.00,"divisor_sum");
1666 920           return; /* skip implicit PUTBACK */
1667              
1668             void
1669             znorder(IN SV* sva, IN SV* svn)
1670             ALIAS:
1671             binomial = 1
1672             jordan_totient = 2
1673             ramanujan_sum = 3
1674             factorialmod = 4
1675             legendre_phi = 5
1676             PREINIT:
1677             int astatus, nstatus;
1678             PPCODE:
1679 18855 100         astatus = _validate_int(aTHX_ sva, (ix==1) ? 2 : 0);
1680 18855 100         nstatus = _validate_int(aTHX_ svn, (ix==1) ? 2 : 0);
1681 18855 50         if (astatus != 0 && nstatus != 0) {
    100          
1682 18851 50         UV a = my_svuv(sva);
1683 18851 50         UV n = my_svuv(svn);
1684             UV ret;
1685 18851           switch (ix) {
1686 22           case 0: ret = znorder(a, n);
1687 22           break;
1688 16599 100         case 1: if ( (astatus == 1 && (nstatus == -1 || n > a)) ||
    100          
    100          
    100          
1689 37 100         (astatus ==-1 && (nstatus == -1 && n > a)) )
    100          
1690 35           { ret = 0; break; }
1691 16564 100         if (nstatus == -1)
1692 8           n = a - n; /* n<0,k<=n: (-1)^(n-k) * binomial(-k-1,n-k) */
1693 16564 100         if (astatus == -1) {
1694 28 50         ret = binomial( -my_sviv(sva)+n-1, n );
1695 28 50         if (ret > 0 && ret <= (UV)IV_MAX)
    50          
1696 28 100         XSRETURN_IV( (IV)ret * ((n&1) ? -1 : 1) );
1697 0           goto overflow;
1698             } else {
1699 16536           ret = binomial(a, n);
1700 16536 100         if (ret == 0)
1701 5232           goto overflow;
1702             }
1703 11304           break;
1704 490           case 2: ret = jordan_totient(a, n);
1705 490 100         if (ret == 0 && n > 1)
    50          
1706 22           goto overflow;
1707 468           break;
1708 902 100         case 3: if (a < 1 || n < 1) XSRETURN_IV(0);
    100          
1709             {
1710 900           UV g = a / gcd_ui(a,n);
1711 900           int m = moebius(g);
1712 900 100         if (m == 0 || a == g) RETURN_NPARITY(m);
    100          
    50          
    50          
1713 285           XSRETURN_IV( m * (totient(a) / totient(g)) );
1714             }
1715             break;
1716 821           case 4: ret = factorialmod(a, n);
1717 821           break;
1718             case 5:
1719 17           default: ret = legendre_phi(a, n);
1720 17           break;
1721             }
1722 12667 100         if (ret == 0 && ix == 0) XSRETURN_UNDEF; /* not defined */
    100          
1723 12663           XSRETURN_UV(ret);
1724             }
1725             overflow:
1726 5258           switch (ix) {
1727 2           case 0: _vcallsub_with_pp("znorder"); break;
1728 5232           case 1: _vcallsub_with_pp("binomial"); break;
1729 24           case 2: _vcallsub_with_pp("jordan_totient"); break;
1730 0           case 3: _vcallsub_with_pp("ramanujan_sum"); break;
1731 0           case 4: _vcallsub_with_pp("factorialmod"); break;
1732             case 5:
1733 0           default: _vcallsub_with_pp("legendre_phi"); break;
1734             }
1735 5258           return; /* skip implicit PUTBACK */
1736              
1737             void
1738             znlog(IN SV* sva, IN SV* svg, IN SV* svp)
1739             ALIAS:
1740             addmod = 1
1741             mulmod = 2
1742             divmod = 3
1743             powmod = 4
1744             PREINIT:
1745             int astatus, gstatus, pstatus, retundef;
1746             UV ret;
1747             PPCODE:
1748 8623           astatus = _validate_int(aTHX_ sva, (ix == 0) ? 0 : 1);
1749 8623           gstatus = _validate_int(aTHX_ svg, (ix == 0) ? 0 : 1);
1750 8623           pstatus = _validate_int(aTHX_ svp, 0);
1751 8623 100         if (astatus != 0 && gstatus != 0 && pstatus == 1) {
    100          
    100          
1752 1232 100         UV a, g, p = my_svuv(svp);
1753 1232 100         if (p <= 1) XSRETURN_UV(0);
1754 1008           ret = 0;
1755 1008           retundef = 0;
1756 1008 50         a = (astatus == 1) ? my_svuv(sva) : negmod(my_sviv(sva), p);
    100          
    0          
1757 1008 100         g = (gstatus == 1) ? my_svuv(svg) : negmod(my_sviv(svg), p);
    100          
    50          
1758 1008 100         if (a >= p) a %= p;
1759 1008 100         if (g >= p && ix != 4) g %= p;
    100          
1760 1008           switch (ix) {
1761 20           case 0: ret = znlog(a, g, p);
1762 20 100         if (ret == 0 && a > 1) retundef = 1;
    100          
1763 20 100         if (ret == 0 && (a == 0 || g == 0)) retundef = 1;
    50          
    50          
1764 20           break;
1765 69           case 1: ret = addmod(a, g, p); break;
1766 610           case 2: ret = mulmod(a, g, p); break;
1767 61           case 3: g = modinverse(g, p);
1768 61 100         if (g == 0) retundef = 1;
1769 40           else ret = mulmod(a, g, p);
1770 61           break;
1771             case 4:
1772 248 50         default:if (a == 0) {
1773 0           ret = (g == 0);
1774 0           retundef = (gstatus == -1);
1775             } else {
1776 248 100         if (gstatus == -1) {
1777 30           a = modinverse(a, p);
1778 30 100         if (a == 0) retundef = 1;
1779 23 50         else g = -my_sviv(svg);
1780             }
1781 248           ret = powmod(a, g, p);
1782             }
1783 248           break;
1784             }
1785 1008 100         if (retundef) XSRETURN_UNDEF;
1786 977           XSRETURN_UV(ret);
1787             }
1788 7391           switch (ix) {
1789 1           case 0: _vcallsub_with_gmpobj(0.00,"znlog"); break;
1790 0           case 1: _vcallsub_with_gmpobj(0.36,"addmod"); break;
1791 7368           case 2: _vcallsub_with_gmpobj(0.36,"mulmod"); break;
1792 0           case 3: _vcallsub_with_gmpobj(0.36,"divmod"); break;
1793             case 4:
1794 22           default:_vcallsub_with_gmpobj(0.36,"powmod"); break;
1795             }
1796 7391 50         OBJECTIFY_RESULT(svp, ST(items-1));
    50          
    50          
    50          
    50          
    50          
    0          
    50          
    50          
    50          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
1797 7391           return; /* skip implicit PUTBACK */
1798              
1799             void
1800             kronecker(IN SV* sva, IN SV* svb)
1801             ALIAS:
1802             valuation = 1
1803             invmod = 2
1804             sqrtmod = 3
1805             is_primitive_root = 4
1806             PREINIT:
1807             int astatus, bstatus, abpositive, abnegative;
1808             PPCODE:
1809 236           astatus = _validate_int(aTHX_ sva, 2);
1810 234           bstatus = _validate_int(aTHX_ svb, 2);
1811 233 100         if (astatus != 0 && bstatus != 0) {
    100          
1812 227 100         if (ix == 0) {
1813             /* Are both a and b positive? */
1814 171 100         abpositive = astatus == 1 && bstatus == 1;
    100          
1815             /* Will both fit in IVs? We should use a bitmask return. */
1816 171           abnegative = !abpositive
1817 20 50         && (SvIOK(sva) && !SvIsUV(sva))
    100          
1818 191 100         && (SvIOK(svb) && !SvIsUV(svb));
    50          
    100          
1819 171 100         if (abpositive || abnegative) {
    100          
1820 169 100         UV a = my_svuv(sva);
1821 169 100         UV b = my_svuv(svb);
1822 169 100         int k = (abpositive) ? kronecker_uu(a,b) : kronecker_ss(a,b);
1823 169 50         RETURN_NPARITY(k);
    50          
1824             }
1825 56 100         } else if (ix == 1) {
1826 5 100         UV n = (astatus == -1) ? (UV)(-(my_sviv(sva))) : my_svuv(sva);
    50          
    50          
1827 5 50         UV k = (bstatus == -1) ? (UV)(-(my_sviv(svb))) : my_svuv(svb);
    0          
    50          
1828             /* valuation of 0-2 is very common, so return a constant if possible */
1829 5 50         RETURN_NPARITY( valuation(n, k) );
    50          
1830 51 100         } else if (ix == 2) {
1831 12           UV a, n, ret = 0;
1832 12 100         n = (bstatus != -1) ? my_svuv(svb) : (UV)(-(my_sviv(svb)));
    100          
    50          
1833 12 100         if (n > 0) {
1834 10 100         a = (astatus == 1) ? my_svuv(sva) : negmod(my_sviv(sva), n);
    50          
    50          
1835 10 100         if (a > 0) {
1836 9 100         if (n == 1) XSRETURN_UV(0);
1837 8           ret = modinverse(a, n);
1838             }
1839             }
1840 11 100         if (ret == 0) XSRETURN_UNDEF;
1841 7           XSRETURN_UV(ret);
1842 39 100         } else if (ix == 3) {
1843             UV a, n, s;
1844 12 50         n = (bstatus != -1) ? my_svuv(svb) : (UV)(-(my_sviv(svb)));
    50          
    0          
1845 12 100         a = (n == 0) ? 0 : (astatus != -1) ? my_svuv(sva) % n : negmod(my_sviv(sva), n);
    50          
    50          
    0          
1846 12 100         if (is_prob_prime(n)) {
1847 5 50         if (!sqrtmod(&s, a, n)) XSRETURN_UNDEF;
1848             } else {
1849 7 100         if (!sqrtmod_composite(&s, a, n)) XSRETURN_UNDEF;
1850             }
1851 12           XSRETURN_UV(s);
1852             } else {
1853             UV a, n;
1854 27 100         n = (bstatus != -1) ? my_svuv(svb) : (UV)(-(my_sviv(svb)));
    100          
    50          
1855 27 100         a = (n == 0) ? 0 : (astatus != -1) ? my_svuv(sva) % n : negmod(my_sviv(sva), n);
    50          
    50          
    0          
1856 27 50         RETURN_NPARITY( is_primitive_root(a,n,0) );
    50          
1857             }
1858             }
1859 8           switch (ix) {
1860 5           case 0: _vcallsub_with_gmp(0.17,"kronecker"); break;
1861 2           case 1: _vcallsub_with_gmp(0.20,"valuation"); break;
1862 0           case 2: _vcallsub_with_gmp(0.20,"invmod"); break;
1863 1           case 3: _vcallsub_with_gmp(0.36,"sqrtmod"); break;
1864             case 4:
1865 0           default: _vcallsub_with_gmp(0.36,"is_primitive_root"); break;
1866             }
1867 8           return; /* skip implicit PUTBACK */
1868              
1869             void
1870             gcdext(IN SV* sva, IN SV* svb)
1871             PREINIT:
1872             int astatus, bstatus;
1873             PPCODE:
1874 14           astatus = _validate_int(aTHX_ sva, 2);
1875 14           bstatus = _validate_int(aTHX_ svb, 2);
1876             /* TODO: These should be built into validate_int */
1877 14 100         if ( (astatus == 1 && SvIsUV(sva)) || (astatus == -1 && !SvIOK(sva)) )
    100          
    100          
    50          
1878 1           astatus = 0; /* too large */
1879 14 100         if ( (bstatus == 1 && SvIsUV(svb)) || (bstatus == -1 && !SvIOK(svb)) )
    50          
    100          
    50          
1880 0           bstatus = 0; /* too large */
1881 26 100         if (astatus != 0 && bstatus != 0) {
    50          
1882             IV u, v, d;
1883 12 50         IV a = my_sviv(sva);
1884 12 50         IV b = my_sviv(svb);
1885 12           d = gcdext(a, b, &u, &v, 0, 0);
1886 12 50         XPUSHs(sv_2mortal(newSViv( u )));
1887 12 50         XPUSHs(sv_2mortal(newSViv( v )));
1888 12 50         XPUSHs(sv_2mortal(newSViv( d )));
1889             } else {
1890 2 50         _vcallsubn(aTHX_ GIMME_V, VCALL_PP, "gcdext", items, 0);
1891 2           return; /* skip implicit PUTBACK */
1892             }
1893              
1894             void
1895             stirling(IN UV n, IN UV m, IN UV type = 1)
1896             PPCODE:
1897 1543 100         if (type != 1 && type != 2 && type != 3)
    100          
    100          
1898 1           croak("stirling type must be 1, 2, or 3");
1899 1542 100         if (n == m)
1900 63           XSRETURN_UV(1);
1901 1479 100         else if (n == 0 || m == 0 || m > n)
    100          
    100          
1902 124           XSRETURN_UV(0);
1903 1355 100         else if (type == 3) {
1904 190           UV s = stirling3(n, m);
1905 190 100         if (s != 0) XSRETURN_UV(s);
1906 1165 100         } else if (type == 2) {
1907 973           IV s = stirling2(n, m);
1908 973 100         if (s != 0) XSRETURN_IV(s);
1909 192 50         } else if (type == 1) {
1910 192           IV s = stirling1(n, m);
1911 192 100         if (s != 0) XSRETURN_IV(s);
1912             }
1913 516           _vcallsub_with_gmpobj(0.26,"stirling");
1914 516 100         OBJECTIFY_RESULT(ST(0), ST(0));
    50          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
1915 516           return;
1916              
1917             NV
1918             _XS_ExponentialIntegral(IN SV* x)
1919             ALIAS:
1920             _XS_LogarithmicIntegral = 1
1921             _XS_RiemannZeta = 2
1922             _XS_RiemannR = 3
1923             _XS_LambertW = 4
1924             PREINIT:
1925             NV nv, ret;
1926             CODE:
1927 64 100         nv = SvNV(x);
1928 64           switch (ix) {
1929 18           case 0: ret = (NV) Ei(nv); break;
1930 22           case 1: ret = (NV) Li(nv); break;
1931 6           case 2: ret = (NV) ld_riemann_zeta(nv); break;
1932 9           case 3: ret = (NV) RiemannR(nv); break;
1933             case 4:
1934 9           default:ret = (NV) lambertw(nv); break;
1935             }
1936 64           RETVAL = ret;
1937             OUTPUT:
1938             RETVAL
1939              
1940             void
1941             euler_phi(IN SV* svlo, IN SV* svhi = 0)
1942             ALIAS:
1943             moebius = 1
1944             PREINIT:
1945             int lostatus, histatus;
1946             PPCODE:
1947 28191           lostatus = _validate_int(aTHX_ svlo, 2);
1948 28191 100         histatus = (svhi == 0 || _validate_int(aTHX_ svhi, 1));
    100          
1949 28191 100         if (svhi == 0 && lostatus != 0) {
    100          
1950             /* input is a single value and in UV/IV range */
1951 28144 100         if (ix == 0) {
1952 74 100         UV ret = (lostatus == -1) ? 0 : totient(my_svuv(svlo));
    50          
1953 74           XSRETURN_UV(ret);
1954             } else {
1955 28070 100         UV n = (lostatus == -1) ? (UV)(-(my_sviv(svlo))) : my_svuv(svlo);
    50          
    50          
1956 28070 50         RETURN_NPARITY(moebius(n));
    50          
1957             }
1958 79 100         } else if (items == 2 && lostatus == 1 && histatus == 1) {
    100          
    100          
1959             /* input is a range and both lo and hi are non-negative */
1960 32 50         UV lo = my_svuv(svlo);
1961 32 50         UV hi = my_svuv(svhi);
1962 32 100         if (lo <= hi) {
1963             UV i;
1964 31 50         EXTEND(SP, (IV)(hi-lo+1));
    100          
1965 31 100         if (ix == 0) {
1966 8 100         UV arraylo = (lo < 100) ? 0 : lo;
1967 8           UV* totients = _totient_range(arraylo, hi);
1968 294 100         for (i = lo; i <= hi; i++)
1969 286           PUSHs(sv_2mortal(newSVuv(totients[i-arraylo])));
1970 8           Safefree(totients);
1971             } else {
1972 23           signed char* mu = _moebius_range(lo, hi);
1973             dMY_CXT;
1974 27909 100         for (i = lo; i <= hi; i++)
1975 27886 50         PUSH_NPARITY(mu[i-lo]);
    50          
1976 23           Safefree(mu);
1977             }
1978             }
1979             } else {
1980             /* Whatever we didn't handle above */
1981 15 50         U32 gimme_v = GIMME_V;
1982 15           I32 flags = VCALL_PP;
1983 15 100         if (ix == 1 && lostatus == 1 && histatus == 1) flags |= VCALL_GMP;
    50          
    0          
1984 15 100         switch (ix) {
1985 3           case 0: _vcallsubn(aTHX_ gimme_v, flags, "euler_phi", items, 22);break;
1986             case 1:
1987 12           default: _vcallsubn(aTHX_ gimme_v, flags, "moebius", items, 22); break;
1988             }
1989 15           return;
1990             }
1991              
1992             void
1993             carmichael_lambda(IN SV* svn)
1994             ALIAS:
1995             mertens = 1
1996             liouville = 2
1997             chebyshev_theta = 3
1998             chebyshev_psi = 4
1999             factorial = 5
2000             sqrtint = 6
2001             exp_mangoldt = 7
2002             znprimroot = 8
2003             hammingweight = 9
2004             hclassno = 10
2005             is_pillai = 11
2006             ramanujan_tau = 12
2007             PREINIT:
2008             int status;
2009             PPCODE:
2010 1593           status = _validate_int(aTHX_ svn, (ix >= 7) ? 1 : 0);
2011 1593 100         if (status != 0) {
2012 1586 100         UV r, n = my_svuv(svn);
2013 1586           switch (ix) {
2014 210           case 0: XSRETURN_UV(carmichael_lambda(n)); break;
2015 45           case 1: XSRETURN_IV(mertens(n)); break;
2016             case 2: { UV factors[MPU_MAX_FACTORS+1];
2017 60 100         int nfactors = factor(my_svuv(svn), factors);
2018 60 100         RETURN_NPARITY( (nfactors & 1) ? -1 : 1 ); }
    50          
    50          
2019             break;
2020 9           case 3: XSRETURN_NV(chebyshev_function(n, 0)); break;
2021 9           case 4: XSRETURN_NV(chebyshev_function(n, 1)); break;
2022 980           case 5: r = factorial(n);
2023 980 100         if (r != 0) XSRETURN_UV(r);
2024 302           status = 0; break;
2025 113           case 6: XSRETURN_UV(isqrt(n)); break;
2026 21 100         case 7: XSRETURN_UV( (status == -1) ? 1 : exp_mangoldt(n) ); break;
2027 25 100         case 8: if (status == -1) n = -(IV)n;
2028 25           r = znprimroot(n);
2029 25 100         if (r == 0 && n != 1) XSRETURN_UNDEF; /* No root */
    100          
2030 21           XSRETURN_UV(r); break;
2031 7 100         case 9: if (status == -1) n = -(IV)n;
2032 7           XSRETURN_UV(popcnt(n)); break;
2033 97 100         case 10: XSRETURN_IV( (status == -1) ? 0 : hclassno(n) ); break;
2034 0 0         case 11: RETURN_NPARITY( (status == -1) ? 0 : pillai_v(n) ); break;
    0          
    0          
2035             case 12:
2036 10 50         default: { IV tau = (status == 1) ? ramanujan_tau(n) : 0;
2037 10 100         if (tau != 0 || status == -1 || n == 0)
    50          
    100          
2038 6           XSRETURN_IV(tau);
2039             } /* Fall through if n > 0 and we got 0 back */
2040 4           break;
2041             }
2042             }
2043 313           switch (ix) {
2044 2           case 0: _vcallsub_with_gmp(0.22,"carmichael_lambda"); break;
2045 0           case 1: _vcallsub_with_pp("mertens"); break;
2046 2           case 2: _vcallsub_with_gmp(0.22,"liouville"); break;
2047 0           case 3: _vcallsub_with_pp("chebyshev_theta"); break;
2048 0           case 4: _vcallsub_with_pp("chebyshev_psi"); break;
2049 302           case 5: _vcallsub_with_pp("factorial"); break;
2050 0           case 6: _vcallsub_with_pp("sqrtint"); break;
2051 0           case 7: _vcallsub_with_gmp(0.19,"exp_mangoldt"); break;
2052 1           case 8: _vcallsub_with_gmp(0.22,"znprimroot"); break;
2053 2 50         case 9: if (_XS_get_callgmp() >= 47) { /* Very fast */
2054 0           _vcallsub_with_gmp(0.47,"hammingweight");
2055             } else { /* Better than PP */
2056 2 50         char* ptr; STRLEN len; ptr = SvPV(svn, len);
2057 2           XSRETURN_UV(mpu_popcount_string(ptr, len));
2058             }
2059 0           break;
2060 0           case 10: _vcallsub_with_pp("hclassno"); break;
2061 0           case 11: _vcallsub_with_gmp(0.00,"is_pillai"); break;
2062             case 12:
2063 4           default: _vcallsub_with_pp("ramanujan_tau"); break;
2064             }
2065 311           return; /* skip implicit PUTBACK */
2066              
2067             void
2068             numtoperm(IN UV n, IN SV* svk)
2069             PREINIT:
2070             UV k;
2071             int i, S[32];
2072             PPCODE:
2073 6 100         if (n == 0)
2074 1           XSRETURN_EMPTY;
2075 5 50         if (n < 32 && _validate_int(aTHX_ svk, 1) == 1) {
    50          
2076 5 50         k = my_svuv(svk);
2077 5 50         if (num_to_perm(k, n, S)) {
2078             dMY_CXT;
2079 5 50         EXTEND(SP, (IV)n);
    50          
2080 50 100         for (i = 0; i < (int)n; i++)
2081 45 50         PUSH_NPARITY( S[i] );
    50          
2082 5           XSRETURN(n);
2083             }
2084             }
2085 0 0         _vcallsubn(aTHX_ GIMME_V, VCALL_PP|VCALL_GMP, "numtoperm", items, 47);
2086 6           return;
2087              
2088             void
2089             permtonum(IN SV* svp)
2090             PREINIT:
2091             AV *av;
2092             UV val, num;
2093             int plen, i;
2094             PPCODE:
2095 6 50         if ((!SvROK(svp)) || (SvTYPE(SvRV(svp)) != SVt_PVAV))
    50          
2096 0           croak("permtonum argument must be an array reference");
2097 6           av = (AV*) SvRV(svp);
2098 6           plen = av_len(av);
2099 6 50         if (plen < 32) {
2100 6           int V[32], A[32] = {0};
2101 74 100         for (i = 0; i <= plen; i++) {
2102 68           SV **iv = av_fetch(av, i, 0);
2103 68 50         if (iv == 0 || _validate_int(aTHX_ *iv, 1) != 1) break;
    50          
2104 68 50         val = my_svuv(*iv);
2105 68 50         if (val > (UV)plen || A[val] != 0) break;
    50          
2106 68           A[val] = i+1;
2107 68           V[i] = val;
2108             }
2109 6 50         if (i > plen && perm_to_num(plen+1, V, &num))
    100          
2110 6           XSRETURN_UV(num);
2111             }
2112 2           _vcallsub_with_gmpobj(0.47,"permtonum");
2113 2 100         OBJECTIFY_RESULT(ST(0), ST(0));
    50          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
2114 6           XSRETURN(1);
2115              
2116             void
2117             randperm(IN UV n, IN UV k = 0)
2118             PREINIT:
2119             UV i, *S;
2120             dMY_CXT;
2121             PPCODE:
2122 4 100         if (items == 1) k = n;
2123 4 50         if (k > n) k = n;
2124 4 100         if (k == 0) XSRETURN_EMPTY;
2125 3 50         New(0, S, k, UV);
2126 3           randperm(MY_CXT.randcxt, n, k, S);
2127 3 50         EXTEND(SP, (IV)k);
    50          
2128 108 100         for (i = 0; i < k; i++) {
2129 105 50         if (n < 2*CINTS) PUSH_NPARITY(S[i]);
    50          
    50          
2130 0           else PUSHs(sv_2mortal(newSVuv(S[i])));
2131             }
2132 3           Safefree(S);
2133              
2134             void shuffle(...)
2135             PROTOTYPE: @
2136             PREINIT:
2137             int i, j;
2138             void* randcxt;
2139             dMY_CXT;
2140             PPCODE:
2141 3 100         if (items == 0)
2142 1           XSRETURN_EMPTY;
2143 101 100         for (i = 0, randcxt = MY_CXT.randcxt; i < items-1; i++) {
2144 99           j = urandomm32(randcxt, items-i);
2145 99           { SV* t = ST(i); ST(i) = ST(i+j); ST(i+j) = t; }
2146             }
2147 2           XSRETURN(items);
2148              
2149             void
2150             sumdigits(SV* svn, UV ibase = 255)
2151             PREINIT:
2152             UV base, sum;
2153             STRLEN i, len;
2154             const char* s;
2155             PPCODE:
2156 1007 50         base = (ibase == 255) ? 10 : ibase;
2157 1007 50         if (base < 2 || base > 36) croak("sumdigits: invalid base %"UVuf, base);
    50          
2158 1007           sum = 0;
2159             /* faster for integer input in base 10 */
2160 1007 50         if (base == 10 && SVNUMTEST(svn) && (SvIsUV(svn) || SvIVX(svn) >= 0)) {
    100          
    50          
    50          
2161 1001 50         UV n, t = my_svuv(svn);
2162 3894 100         while ((n=t)) {
2163 2893           t = n / base;
2164 2893           sum += n - base*t;
2165             }
2166 1001           XSRETURN_UV(sum);
2167             }
2168 6 100         s = SvPV(svn, len);
2169             /* If no base given and input is 0x... or 0b..., select base. */
2170 6 50         if (ibase == 255 && len > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'b')){
    50          
    100          
    50          
    0          
2171 1 50         base = (s[1] == 'x') ? 16 : 2;
2172 1           s += 2;
2173 1           len -= 2;
2174             }
2175 38640 100         for (i = 0; i < len; i++) {
2176 38634           UV d = 0;
2177 38634           const char c = s[i];
2178 38634 100         if (c >= '0' && c <= '9') { d = c - '0'; }
    100          
2179 5 100         else if (c >= 'a' && c <= 'z') { d = c - 'a' + 10; }
    50          
2180 4 100         else if (c >= 'A' && c <= 'Z') { d = c - 'A' + 10; }
    50          
2181 38634 50         if (d < base)
2182 38634           sum += d;
2183             }
2184 1007           XSRETURN_UV(sum);
2185              
2186             void todigits(SV* svn, int base=10, int length=-1)
2187             ALIAS:
2188             todigitstring = 1
2189             fromdigits = 2
2190             PREINIT:
2191             int i, status;
2192             UV n;
2193             char *str;
2194             PPCODE:
2195 38 50         if (base < 2) croak("invalid base: %d", base);
2196 38           status = 0;
2197 38 100         if (ix == 0 || ix == 1) {
    100          
2198 19           status = _validate_int(aTHX_ svn, 1);
2199 19 100         n = (status == 0) ? 0 : status * my_svuv(svn);
    50          
2200             }
2201             /* todigits with native input */
2202 38 100         if (ix == 0 && status != 0 && length < 128) {
    100          
    50          
2203             int digits[128];
2204 14           IV len = to_digit_array(digits, n, base, length);
2205 14 50         if (len >= 0) {
2206             dMY_CXT;
2207 14 50         EXTEND(SP, len);
    50          
2208 103 100         for (i = 0; i < len; i++)
2209 89 50         PUSH_NPARITY( digits[len-i-1] );
    50          
2210 14           XSRETURN(len);
2211             }
2212             }
2213             /* todigitstring with native input */
2214 24 100         if (ix == 1 && status != 0 && length < 128) {
    50          
    50          
2215             char s[128+1];
2216 3           IV len = to_digit_string(s, n, base, length);
2217 3 50         if (len >= 0) {
2218 3 50         XPUSHs(sv_2mortal(newSVpv(s, len)));
2219 3           XSRETURN(1);
2220             }
2221             }
2222             /* todigits or todigitstring base 10 (large size) */
2223 21 100         if ((ix == 0 || ix == 1) && base == 10 && length < 0) {
    50          
    100          
    50          
2224             STRLEN len;
2225 1 50         str = SvPV(svn, len);
2226 1 50         if (ix == 1) {
2227 0 0         XPUSHs(sv_2mortal(newSVpv(str, len)));
2228 0           XSRETURN(1);
2229             }
2230 1 50         if (len == 1 && str[0] == '0') XSRETURN(0);
    0          
2231             {
2232             dMY_CXT;
2233 1 50         EXTEND(SP, (IV)len);
    50          
2234 46 100         for (i = 0; i < (int)len; i++)
2235 45 50         PUSH_NPARITY(str[i]-'0');
    50          
2236             }
2237 1           XSRETURN(len);
2238             }
2239 20 100         if (ix == 2) { /* fromdigits */
2240 19 100         if (!SvROK(svn)) { /* string */
2241 6 50         if (from_digit_string(&n, SvPV_nolen(svn), base)) {
    100          
2242 5           XSRETURN_UV(n);
2243             }
2244 13 50         } else if (!_is_sv_bigint(aTHX_ svn)) { /* array ref of digits */
2245 13           UV* r = 0;
2246 13           int len = arrayref_to_int_array(aTHX_ &r, (AV*) SvRV(svn), base);
2247 13 50         if (from_digit_to_UV(&n, r, len, base)) {
2248 13           Safefree(r);
2249 13           XSRETURN_UV(n);
2250 0 0         } else if (from_digit_to_str(&str, r, len, base)){
2251 0           Safefree(r);
2252 0 0         XPUSHs( sv_to_bigint(aTHX_ sv_2mortal(newSVpv(str,0))) );
2253 0           Safefree(str);
2254 0           XSRETURN(1);
2255             }
2256 0           Safefree(r);
2257             }
2258             }
2259 2           switch (ix) {
2260 1 50         case 0: _vcallsubn(aTHX_ GIMME_V, VCALL_GMP|VCALL_PP, "todigits", items, 41); break;
2261 0           case 1: _vcallsub_with_gmp(0.00,"todigitstring"); break;
2262             case 2:
2263 1           default: _vcallsub_with_gmp(0.00,"fromdigits"); break;
2264             }
2265 38           return;
2266              
2267             bool
2268             _validate_num(SV* svn, ...)
2269             PREINIT:
2270             SV* sv1;
2271             SV* sv2;
2272             CODE:
2273             /* Internal function. Emulate the PP version of this:
2274             * $is_valid = _validate_num( $n [, $min [, $max] ] )
2275             * Return 0 if we're befuddled by the input.
2276             * Otherwise croak if n isn't >= 0 and integer, n < min, or n > max.
2277             * Small bigints will be converted to scalars.
2278             */
2279 2021           RETVAL = FALSE;
2280 2021 100         if (_validate_int(aTHX_ svn, 0)) {
2281 1875 100         if (SvROK(svn)) { /* Convert small Math::BigInt object into scalar */
2282 78 50         UV n = my_svuv(svn);
2283             #if PERL_REVISION <= 5 && PERL_VERSION < 8 && BITS_PER_WORD == 64
2284             sv_setpviv(svn, n);
2285             #else
2286 78           sv_setuv(svn, n);
2287             #endif
2288             }
2289 1875 100         if (items > 1 && ((sv1 = ST(1)), SvOK(sv1))) {
    50          
    0          
    0          
    50          
2290 3 50         UV n = my_svuv(svn);
2291 3 50         UV min = my_svuv(sv1);
2292 3 50         if (n < min)
2293 0           croak("Parameter '%"UVuf"' must be >= %"UVuf, n, min);
2294 3 50         if (items > 2 && ((sv2 = ST(2)), SvOK(sv2))) {
    0          
    0          
    0          
    0          
2295 0 0         UV max = my_svuv(sv2);
2296 0 0         if (n > max)
2297 0           croak("Parameter '%"UVuf"' must be <= %"UVuf, n, max);
2298 0 0         MPUassert( items <= 3, "_validate_num takes at most 3 parameters");
2299             }
2300             }
2301 1875           RETVAL = TRUE;
2302             }
2303             OUTPUT:
2304             RETVAL
2305              
2306             void
2307             lastfor()
2308             PREINIT:
2309             dMY_CXT;
2310             PPCODE:
2311             /* printf("last for with count = %u\n", MY_CXT.forcount); */
2312 89 50         if (MY_CXT.forcount == 0) croak("lastfor called outside a loop");
2313 89           MY_CXT.forexit = 1;
2314             /* In some ideal world this would also act like a last */
2315 89           return;
2316              
2317             #define START_FORCOUNT \
2318             do { \
2319             oldforloop = ++MY_CXT.forcount; \
2320             oldforexit = MY_CXT.forexit; \
2321             forexit = &MY_CXT.forexit; \
2322             *forexit = 0; \
2323             } while(0)
2324              
2325             #define CHECK_FORCOUNT \
2326             if (*forexit) break;
2327              
2328             #define END_FORCOUNT \
2329             do { \
2330             /* Put back outer loop's exit request, if any. */ \
2331             *forexit = oldforexit; \
2332             /* Ensure loops are nested and not woven. */ \
2333             if (MY_CXT.forcount-- != oldforloop) croak("for loop mismatch"); \
2334             } while (0)
2335              
2336             void
2337             forprimes (SV* block, IN SV* svbeg, IN SV* svend = 0)
2338             PROTOTYPE: &$;$
2339             PREINIT:
2340             GV *gv;
2341             HV *stash;
2342             SV* svarg;
2343             CV *cv;
2344             unsigned char* segment;
2345             UV beg, end, seg_base, seg_low, seg_high;
2346             uint16_t oldforloop;
2347             char oldforexit;
2348             char *forexit;
2349             dMY_CXT;
2350             PPCODE:
2351 73           cv = sv_2cv(block, &stash, &gv, 0);
2352 73 50         if (cv == Nullcv)
2353 0           croak("Not a subroutine reference");
2354              
2355 73 50         if (!_validate_int(aTHX_ svbeg, 0) || (items >= 3 && !_validate_int(aTHX_ svend,0))) {
    100          
    50          
2356 0           _vcallsubn(aTHX_ G_VOID|G_DISCARD, VCALL_ROOT, "_generic_forprimes", items, 0);
2357 0           return;
2358             }
2359              
2360 65 100         if (items < 3) {
2361 37           beg = 2;
2362 37 50         end = my_svuv(svbeg);
2363             } else {
2364 28 50         beg = my_svuv(svbeg);
2365 28 50         end = my_svuv(svend);
2366             }
2367              
2368 65           START_FORCOUNT;
2369 65           SAVESPTR(GvSV(PL_defgv));
2370 65           svarg = newSVuv(beg);
2371 65           GvSV(PL_defgv) = svarg;
2372             /* Handle early part */
2373 187 100         while (beg < 6) {
2374 123 100         beg = (beg <= 2) ? 2 : (beg <= 3) ? 3 : 5;
    100          
2375 123 100         if (beg <= end) {
2376 116           sv_setuv(svarg, beg);
2377 116 50         PUSHMARK(SP);
2378 116           call_sv((SV*)cv, G_VOID|G_DISCARD);
2379 116 100         CHECK_FORCOUNT;
2380             }
2381 122 100         beg += 1 + (beg > 2);
2382             }
2383             #if USE_MULTICALL
2384 123 50         if (!CvISXSUB(cv) && beg <= end) {
    100          
2385             dMULTICALL;
2386 58           I32 gimme = G_VOID;
2387 58 50         PUSH_MULTICALL(cv);
    50          
2388 89 50         if (
2389             #if BITS_PER_WORD == 64
2390 58 0         (beg >= UVCONST( 100000000000000) && end-beg < 100000) ||
    50          
2391 58 0         (beg >= UVCONST( 10000000000000) && end-beg < 40000) ||
    50          
2392 58 0         (beg >= UVCONST( 1000000000000) && end-beg < 17000) ||
    100          
2393             #endif
2394 58           ((end-beg) < 500) ) { /* MULTICALL next prime */
2395 171 100         for (beg = next_prime(beg-1); beg <= end && beg != 0; beg = next_prime(beg)) {
    50          
2396 141 100         CHECK_FORCOUNT;
2397 140           sv_setuv(svarg, beg);
2398 140           MULTICALL;
2399             }
2400             } else { /* MULTICALL segment sieve */
2401 27           void* ctx = start_segment_primes(beg, end, &segment);
2402 29 100         while (next_segment_primes(ctx, &seg_base, &seg_low, &seg_high)) {
2403 27 50         int crossuv = (seg_high > IV_MAX) && !SvIsUV(svarg);
    0          
2404 1310 50         START_DO_FOR_EACH_SIEVE_PRIME( segment, seg_base, seg_low, seg_high )
    100          
    100          
    100          
    100          
2405 1261 100         CHECK_FORCOUNT;
2406             /* sv_setuv(svarg, p); */
2407 1147 100         if (SvTYPE(svarg) != SVt_IV) { sv_setuv(svarg, p); }
2408 982 50         else if (crossuv && p > IV_MAX) { sv_setuv(svarg, p); crossuv=0; }
    0          
2409 982           else { SvUV_set(svarg, p); }
2410 1147           MULTICALL;
2411 133           END_DO_FOR_EACH_SIEVE_PRIME
2412 27 100         CHECK_FORCOUNT;
2413             }
2414 27           end_segment_primes(ctx);
2415             }
2416             FIX_MULTICALL_REFCOUNT;
2417 58 50         POP_MULTICALL;
    50          
2418             }
2419             else
2420             #endif
2421 7 50         if (beg <= end) { /* NO-MULTICALL segment sieve */
2422 0           void* ctx = start_segment_primes(beg, end, &segment);
2423 0 0         while (next_segment_primes(ctx, &seg_base, &seg_low, &seg_high)) {
2424 0 0         START_DO_FOR_EACH_SIEVE_PRIME( segment, seg_base, seg_low, seg_high )
    0          
    0          
    0          
    0          
2425 0 0         CHECK_FORCOUNT;
2426 0           sv_setuv(svarg, p);
2427 0 0         PUSHMARK(SP);
2428 0           call_sv((SV*)cv, G_VOID|G_DISCARD);
2429 0           END_DO_FOR_EACH_SIEVE_PRIME
2430 0 0         CHECK_FORCOUNT;
2431             }
2432 0           end_segment_primes(ctx);
2433             }
2434 65           SvREFCNT_dec(svarg);
2435 65 50         END_FORCOUNT;
2436              
2437             void
2438             forcomposites (SV* block, IN SV* svbeg, IN SV* svend = 0)
2439             ALIAS:
2440             foroddcomposites = 1
2441             PROTOTYPE: &$;$
2442             PREINIT:
2443             UV beg, end;
2444             GV *gv;
2445             HV *stash;
2446             SV* svarg; /* We use svarg to prevent clobbering $_ outside the block */
2447             CV *cv;
2448             uint16_t oldforloop;
2449             char oldforexit;
2450             char *forexit;
2451             dMY_CXT;
2452             PPCODE:
2453 58           cv = sv_2cv(block, &stash, &gv, 0);
2454 58 50         if (cv == Nullcv)
2455 0           croak("Not a subroutine reference");
2456              
2457 58 50         if (!_validate_int(aTHX_ svbeg, 0) || (items >= 3 && !_validate_int(aTHX_ svend,0))) {
    100          
    50          
2458 0 0         _vcallsubn(aTHX_ G_VOID|G_DISCARD, VCALL_ROOT, (ix == 0) ? "_generic_forcomposites" : "_generic_foroddcomposites", items, 0);
2459 0           return;
2460             }
2461              
2462 58 100         if (items < 3) {
2463 56 100         beg = ix ? 8 : 4;
2464 56 50         end = my_svuv(svbeg);
2465             } else {
2466 2 50         beg = my_svuv(svbeg);
2467 2 50         end = my_svuv(svend);
2468             }
2469              
2470 58           START_FORCOUNT;
2471 58           SAVESPTR(GvSV(PL_defgv));
2472 58           svarg = newSVuv(0);
2473 58           GvSV(PL_defgv) = svarg;
2474             #if USE_MULTICALL
2475 114 50         if (!CvISXSUB(cv) && end >= beg) {
    100          
2476             unsigned char* segment;
2477             UV seg_base, seg_low, seg_high, c, cbeg, cend, prevprime, nextprime;
2478             void* ctx;
2479             dMULTICALL;
2480 56           I32 gimme = G_VOID;
2481 56 50         PUSH_MULTICALL(cv);
    50          
2482 111 50         if (beg >= MPU_MAX_PRIME ||
    50          
2483             #if BITS_PER_WORD == 64
2484 56 0         (beg >= UVCONST( 100000000000000) && end-beg < 120000) ||
    50          
2485 56 0         (beg >= UVCONST( 10000000000000) && end-beg < 50000) ||
    50          
2486 56 0         (beg >= UVCONST( 1000000000000) && end-beg < 20000) ||
    100          
2487             #endif
2488 56           end-beg < 1000 ) {
2489 55 100         beg = (beg <= 4) ? 3 : beg-1;
2490 55           nextprime = next_prime(beg);
2491 3958 100         while (beg++ < end) {
2492 3953 100         if (beg == nextprime) nextprime = next_prime(beg);
2493 3046 100         else if (!ix || beg & 1) { sv_setuv(svarg, beg); MULTICALL; }
    100          
2494 3953 100         CHECK_FORCOUNT;
2495             }
2496             } else {
2497 1 50         if (ix) {
2498 1 50         if (beg < 8) beg = 8;
2499 0 0         } else if (beg <= 4) { /* sieve starts at 7, so handle this here */
2500 0           sv_setuv(svarg, 4); MULTICALL;
2501 0           beg = 6;
2502             }
2503             /* Find the two primes that bound their interval. */
2504             /* beg must be < max_prime, and end >= max_prime is special. */
2505 1           prevprime = prev_prime(beg);
2506 1 50         nextprime = (end >= MPU_MAX_PRIME) ? MPU_MAX_PRIME : next_prime(end);
2507 1           ctx = start_segment_primes(beg, nextprime, &segment);
2508 2 100         while (next_segment_primes(ctx, &seg_base, &seg_low, &seg_high)) {
2509 1 50         int crossuv = (seg_high > IV_MAX) && !SvIsUV(svarg);
    0          
2510 6738 50         START_DO_FOR_EACH_SIEVE_PRIME( segment, seg_base, seg_low, seg_high )
    100          
    100          
    100          
    100          
2511 6466 50         CHECK_FORCOUNT;
2512 6466           cbeg = prevprime+1;
2513 6466 50         if (cbeg < beg)
2514 0 0         cbeg = beg - (ix == 1 && (beg % 2));
    0          
2515 6466           prevprime = p;
2516 6466 100         cend = prevprime-1; if (cend > end) cend = end;
2517             /* If ix=1, skip evens by starting 1 farther and skipping by 2 */
2518 32338 100         for (c = cbeg + ix; c <= cend; c=c+1+ix) {
2519 25872 50         if (SvTYPE(svarg) != SVt_IV) { sv_setuv(svarg,c); }
2520 25872 50         else if (crossuv && c > IV_MAX) { sv_setuv(svarg,c); crossuv=0;}
    0          
2521 25872           else { SvUV_set(svarg,c); }
2522 25872           MULTICALL;
2523             }
2524 270           END_DO_FOR_EACH_SIEVE_PRIME
2525             }
2526 1           end_segment_primes(ctx);
2527 1 50         if (end > nextprime) /* Complete the case where end > max_prime */
2528 0 0         while (nextprime++ < end)
2529 0 0         if (!ix || nextprime & 1) {
    0          
2530 0 0         CHECK_FORCOUNT;
2531 0           sv_setuv(svarg, nextprime);
2532 0           MULTICALL;
2533             }
2534             }
2535             FIX_MULTICALL_REFCOUNT;
2536 56 50         POP_MULTICALL;
    50          
2537             }
2538             else
2539             #endif
2540 2 50         if (beg <= end) {
2541 0 0         beg = (beg <= 4) ? 3 : beg-1;
2542 0 0         while (beg++ < end) {
2543 0 0         if ((!ix || beg&1) && !is_prob_prime(beg)) {
    0          
    0          
2544 0           sv_setuv(svarg, beg);
2545 0 0         PUSHMARK(SP);
2546 0           call_sv((SV*)cv, G_VOID|G_DISCARD);
2547 0 0         CHECK_FORCOUNT;
2548             }
2549             }
2550             }
2551 58           SvREFCNT_dec(svarg);
2552 58 50         END_FORCOUNT;
2553              
2554             void
2555             fordivisors (SV* block, IN SV* svn)
2556             PROTOTYPE: &$
2557             PREINIT:
2558             UV i, n, ndivisors;
2559             UV *divs;
2560             GV *gv;
2561             HV *stash;
2562             SV* svarg; /* We use svarg to prevent clobbering $_ outside the block */
2563             CV *cv;
2564             uint16_t oldforloop;
2565             char oldforexit;
2566             char *forexit;
2567             dMY_CXT;
2568             PPCODE:
2569 71           cv = sv_2cv(block, &stash, &gv, 0);
2570 71 50         if (cv == Nullcv)
2571 0           croak("Not a subroutine reference");
2572              
2573 71 50         if (!_validate_int(aTHX_ svn, 0)) {
2574 0           _vcallsubn(aTHX_ G_VOID|G_DISCARD, VCALL_ROOT, "_generic_fordivisors", 2, 0);
2575 0           return;
2576             }
2577              
2578 71 50         n = my_svuv(svn);
2579 71           divs = _divisor_list(n, &ndivisors);
2580              
2581 71           START_FORCOUNT;
2582 71           SAVESPTR(GvSV(PL_defgv));
2583 71           svarg = newSVuv(0);
2584 71           GvSV(PL_defgv) = svarg;
2585             #if USE_MULTICALL
2586 71 50         if (!CvISXSUB(cv)) {
2587             dMULTICALL;
2588 71           I32 gimme = G_VOID;
2589 71 50         PUSH_MULTICALL(cv);
    50          
2590 336 100         for (i = 0; i < ndivisors; i++) {
2591 272           sv_setuv(svarg, divs[i]);
2592 272           MULTICALL;
2593 272 100         CHECK_FORCOUNT;
2594             }
2595             FIX_MULTICALL_REFCOUNT;
2596 71 50         POP_MULTICALL;
    50          
2597             }
2598             else
2599             #endif
2600             {
2601 0 0         for (i = 0; i < ndivisors; i++) {
2602 0           sv_setuv(svarg, divs[i]);
2603 0 0         PUSHMARK(SP);
2604 0           call_sv((SV*)cv, G_VOID|G_DISCARD);
2605 0 0         CHECK_FORCOUNT;
2606             }
2607             }
2608 71           SvREFCNT_dec(svarg);
2609 71           Safefree(divs);
2610 71 50         END_FORCOUNT;
2611              
2612             void
2613             forpart (SV* block, IN SV* svn, IN SV* svh = 0)
2614             ALIAS:
2615             forcomp = 1
2616             PROTOTYPE: &$;$
2617             PREINIT:
2618             UV i, n, amin, amax, nmin, nmax;
2619             int primeq;
2620             GV *gv;
2621             HV *stash;
2622             CV *cv;
2623             SV** svals;
2624             uint16_t oldforloop;
2625             char oldforexit;
2626             char *forexit;
2627             dMY_CXT;
2628             PPCODE:
2629 34           cv = sv_2cv(block, &stash, &gv, 0);
2630 34 50         if (cv == Nullcv)
2631 0           croak("Not a subroutine reference");
2632 34 50         if (!_validate_int(aTHX_ svn, 0)) {
2633 0           _vcallsub_with_pp("forpart");
2634 0           return;
2635             }
2636 34 50         n = my_svuv(svn);
2637 34 50         if (n > (UV_MAX-2)) croak("forpart argument overflow");
2638              
2639 34 50         New(0, svals, n+1, SV*);
2640 707 100         for (i = 0; i <= n; i++) {
2641 673           svals[i] = newSVuv(i);
2642 673           SvREADONLY_on(svals[i]);
2643             }
2644              
2645 34           amin = 1; amax = n; nmin = 1; nmax = n; primeq = -1;
2646 34 100         if (svh != 0) {
2647             HV* rhash;
2648             SV** svp;
2649 16 50         if (!SvROK(svh) || SvTYPE(SvRV(svh)) != SVt_PVHV)
    50          
2650 0           croak("forpart second argument must be a hash reference");
2651 16           rhash = (HV*) SvRV(svh);
2652 16 100         if ((svp = hv_fetchs(rhash, "n", 0)) != NULL)
2653 8 50         { nmin = my_svuv(*svp); nmax = nmin; }
2654 16 100         if ((svp = hv_fetchs(rhash, "amin", 0)) != NULL) amin = my_svuv(*svp);
    50          
2655 16 100         if ((svp = hv_fetchs(rhash, "amax", 0)) != NULL) amax = my_svuv(*svp);
    50          
2656 16 100         if ((svp = hv_fetchs(rhash, "nmin", 0)) != NULL) nmin = my_svuv(*svp);
    50          
2657 16 100         if ((svp = hv_fetchs(rhash, "nmax", 0)) != NULL) nmax = my_svuv(*svp);
    50          
2658 16 100         if ((svp = hv_fetchs(rhash, "prime",0)) != NULL) primeq=my_svuv(*svp);
    50          
2659              
2660 16 50         if (amin < 1) amin = 1;
2661 16 50         if (amax > n) amax = n;
2662 16 50         if (nmin < 1) nmin = 1;
2663 16 50         if (nmax > n) nmax = n;
2664 16 100         if (primeq != 0 && primeq != -1) primeq = 2; /* -1, 0, or 2 */
    100          
2665             }
2666              
2667 34 100         if (primeq == 2) {
2668 2           UV prev = prev_prime(amax+1);
2669 2 100         UV next = amin <= 2 ? 2 : next_prime(amin-1);
2670 2 50         if (amin < next) amin = next;
2671 2 50         if (amax > prev) amax = prev;
2672             }
2673              
2674 34 100         if (n==0 && nmin <= 1) {
    50          
2675 2 50         { dSP; ENTER; PUSHMARK(SP);
2676             /* Nothing */
2677 2           PUTBACK; call_sv((SV*)cv, G_VOID|G_DISCARD); LEAVE;
2678             }
2679             }
2680 34 100         if (n >= nmin && nmin <= nmax && amin <= amax && nmax > 0 && amax > 0)
    50          
    100          
    50          
    50          
2681             { /* RuleAsc algorithm from Kelleher and O'Sullivan 2009/2014) */
2682             UV *a, k, x, y, r;
2683 31 50         New(0, a, n+1, UV);
2684 31           k = 1;
2685 31           a[0] = amin-1;
2686 31           a[1] = n-amin+1;
2687 31           START_FORCOUNT;
2688 15860 100         while (k != 0) {
2689 15843           x = a[k-1]+1;
2690 15843           y = a[k]-1;
2691 15843           k--;
2692 15843 100         r = (ix == 0) ? x : 1;
2693 36217 100         while (r <= y) {
2694 20374           a[k++] = x;
2695 20374           x = r;
2696 20374           y -= x;
2697             }
2698 15843           a[k] = x + y;
2699              
2700             /* ------ length restrictions ------ */
2701 20391 100         while (k+1 > nmax) { /* Skip range if over max size */
2702 4548           a[k-1] += a[k];
2703 4548           k--;
2704             }
2705             /* Look into: quick skip over nmin range */
2706 15843 100         if (k+1 < nmin) { /* Skip if not over min size */
2707 3617 100         if (a[0] >= n-nmin+1 && a[k] > 1) break; /* early exit check */
    50          
2708 3608           continue;
2709             }
2710              
2711             /* ------ value restrictions ------ */
2712 12226 100         if (amin > 1 || amax < n) {
    100          
2713             /* Lexical order allows us to start at amin, and exit early */
2714 3452 100         if (a[0] > amax) break;
2715              
2716 3449 100         if (ix == 0) { /* value restrictions for partitions */
2717 3428 100         if (a[k] > amax) continue;
2718             } else { /* restrictions for compositions */
2719             /* TODO: maybe skip forward? */
2720 58 100         for (i = 0; i <= k; i++)
2721 51 100         if (a[i] < amin || a[i] > amax)
    100          
2722             break;
2723 21 100         if (i <= k) continue;
2724             }
2725             }
2726 11631 100         if (primeq != -1) {
2727 3791 100         for (i = 0; i <= k; i++) if (is_prime(a[i]) != primeq) break;
    100          
2728 2602 100         if (i <= k) continue;
2729             }
2730              
2731 9121 50         { dSP; ENTER; PUSHMARK(SP);
2732 85120 50         EXTEND(SP, (IV)k); for (i = 0; i <= k; i++) { PUSHs(svals[a[i]]); }
    50          
    100          
2733 9121           PUTBACK; call_sv((SV*)cv, G_VOID|G_DISCARD); LEAVE;
2734             }
2735 9121 100         CHECK_FORCOUNT;
2736             }
2737 31           Safefree(a);
2738 31 50         END_FORCOUNT;
2739             }
2740 707 100         for (i = 0; i <= n; i++)
2741 673           SvREFCNT_dec(svals[i]);
2742 34           Safefree(svals);
2743              
2744             void
2745             forcomb (SV* block, IN SV* svn, IN SV* svk = 0)
2746             ALIAS:
2747             forperm = 1
2748             forderange = 2
2749             PROTOTYPE: &$;$
2750             PREINIT:
2751             UV i, n, k, j, m, begk, endk;
2752             GV *gv;
2753             HV *stash;
2754             CV *cv;
2755             SV** svals;
2756             UV* cm;
2757             uint16_t oldforloop;
2758             char oldforexit;
2759             char *forexit;
2760             dMY_CXT;
2761             PPCODE:
2762 24           cv = sv_2cv(block, &stash, &gv, 0);
2763 24 50         if (cv == Nullcv)
2764 0           croak("Not a subroutine reference");
2765 24 100         if (ix > 0 && svk != 0)
    50          
2766 0           croak("Too many arguments for forperm");
2767              
2768 24 50         if (!_validate_int(aTHX_ svn, 0) || (svk != 0 && !_validate_int(aTHX_ svk, 0))) {
    100          
    50          
2769 0 0         _vcallsub_with_pp( (ix == 0) ? "forcomb"
    0          
2770             : (ix == 1) ? "forperm"
2771             : "forderange" );
2772 0           return;
2773             }
2774              
2775 24 50         n = my_svuv(svn);
2776 24 100         if (svk == 0) {
2777 16 100         begk = (ix == 0) ? 0 : n;
2778 16           endk = n;
2779             } else {
2780 8 50         begk = endk = my_svuv(svk);
2781 8 100         if (begk > n)
2782 1           return;
2783             }
2784              
2785 23 50         New(0, svals, n, SV*);
2786 115 100         for (i = 0; i < n; i++) {
2787 92           svals[i] = newSVuv(i);
2788 92           SvREADONLY_on(svals[i]);
2789             }
2790 23 50         New(0, cm, endk+1, UV);
2791              
2792 23           START_FORCOUNT;
2793 47 100         for (k = begk; k <= endk; k++) {
2794 27           cm[0] = UV_MAX;
2795 107 100         for (i = 0; i < k; i++)
2796 80           cm[i] = k-i;
2797 27 100         if (ix == 2 && k >= 2) { /* Make derangements start deranged */
    100          
2798 23 100         for (i = 0; i < k; i++)
2799 19 100         cm[k-i-1] = (i&1) ? i : i+2;
2800 4 100         if (k & 1) {
2801 3           cm[0] = k-2;
2802 3           cm[1] = k;
2803             }
2804             }
2805             while (1) {
2806 22487 100         if (ix < 2 || k != 1) {
    100          
2807 22486 50         dSP; ENTER; PUSHMARK(SP);
2808 22486 50         EXTEND(SP, ((IV)k));
    50          
2809 303640 100         for (i = 0; i < k; i++) { PUSHs(svals[ cm[k-i-1]-1 ]); }
2810 22486           PUTBACK; call_sv((SV*)cv, G_VOID|G_DISCARD); LEAVE;
2811 22486 100         CHECK_FORCOUNT;
2812             }
2813 22484 100         if (ix == 0) {
2814 15534 100         if (cm[0]++ < n) continue; /* Increment last value */
2815 38790 100         for (i = 1; i < k && cm[i] >= n-i; i++) ; /* Find next index to incr */
    100          
2816 11647 100         if (i >= k) break; /* Done! */
2817 11634           cm[i]++; /* Increment this one */
2818 50387 100         while (i-- > 0) cm[i] = cm[i+1] + 1; /* Set the rest */
2819 6950 100         } else if (ix == 1) {
2820 8736 100         for (j = 1; j < k && cm[j] > cm[j-1]; j++) ; /* Find last decrease */
    100          
2821 5086 100         if (j >= k) break; /* Done! */
2822 6898 100         for (m = 0; cm[j] > cm[m]; m++) ; /* Find next greater */
2823 5080           { UV t = cm[j]; cm[j] = cm[m]; cm[m] = t; } /* Swap */
2824 7833 100         for (i = j-1, m = 0; m < i; i--, m++) /* Reverse the end */
2825 2753           { UV t = cm[i]; cm[i] = cm[m]; cm[m] = t; }
2826             } else {
2827             REDERANGE:
2828 5005 100         for (j = 1; j < k && cm[j] > cm[j-1]; j++) ; /* Find last decrease */
    100          
2829 2737 100         if (j >= k) break; /* Done! */
2830 3866 100         for (m = 0; cm[j] > cm[m]; m++) ; /* Find next greater */
2831 2732           { UV t = cm[j]; cm[j] = cm[m]; cm[m] = t; } /* Swap */
2832 2732 100         if (cm[j] == k-j) goto REDERANGE; /* Skip? */
2833 3567 100         for (i = j-1, m = 0; m < i; i--, m++) /* Reverse the end */
2834 1371           { UV t = cm[i]; cm[i] = cm[m]; cm[m] = t; }
2835 17120 100         for (i = 0; i < k; i++) /* Check deranged */
2836 15261 100         if (cm[k-i-1]-1 == i)
2837 337           break;
2838 2196 100         if (i != k) goto REDERANGE;
2839             }
2840 22460           }
2841 27 100         CHECK_FORCOUNT;
2842             }
2843              
2844 23           Safefree(cm);
2845 115 100         for (i = 0; i < n; i++)
2846 92           SvREFCNT_dec(svals[i]);
2847 23           Safefree(svals);
2848 23 50         END_FORCOUNT;
2849              
2850             void
2851             vecreduce(SV* block, ...)
2852             PROTOTYPE: &@
2853             CODE:
2854             { /* This is basically reduce from List::Util. Try to maintain compat. */
2855 4           SV *ret = sv_newmortal();
2856             int i;
2857             GV *agv,*bgv,*gv;
2858             HV *stash;
2859 4           SV **args = &PL_stack_base[ax];
2860 4           CV *cv = sv_2cv(block, &stash, &gv, 0);
2861              
2862 4 50         if (cv == Nullcv) croak("Not a subroutine reference");
2863 4 100         if (items <= 1) XSRETURN_UNDEF;
2864              
2865 3           agv = gv_fetchpv("a", GV_ADD, SVt_PV);
2866 3           bgv = gv_fetchpv("b", GV_ADD, SVt_PV);
2867 3           SAVESPTR(GvSV(agv));
2868 3           SAVESPTR(GvSV(bgv));
2869 3           GvSV(agv) = ret;
2870 3 50         SvSetMagicSV(ret, args[1]);
    50          
2871             #ifdef dMULTICALL
2872 3 50         if (!CvISXSUB(cv)) {
2873             dMULTICALL;
2874 3           I32 gimme = G_SCALAR;
2875 3 50         PUSH_MULTICALL(cv);
    50          
2876 7 100         for (i = 2; i < items; i++) {
2877 4           GvSV(bgv) = args[i];
2878 4           MULTICALL;
2879 4 50         SvSetMagicSV(ret, *PL_stack_sp);
    50          
2880             }
2881             FIX_MULTICALL_REFCOUNT;
2882 3 50         POP_MULTICALL;
    50          
2883             }
2884             else
2885             #endif
2886             {
2887 0 0         for (i = 2; i < items; i++) {
2888 0           dSP;
2889 0           GvSV(bgv) = args[i];
2890 0 0         PUSHMARK(SP);
2891 0           call_sv((SV*)cv, G_SCALAR);
2892 0 0         SvSetMagicSV(ret, *PL_stack_sp);
    0          
2893             }
2894             }
2895 3           ST(0) = ret;
2896 4           XSRETURN(1);
2897             }
2898              
2899             void
2900             vecnone(SV* block, ...)
2901             ALIAS:
2902             vecall = 1
2903             vecany = 2
2904             vecnotall = 3
2905             vecfirst = 4
2906             vecfirstidx = 6
2907             PROTOTYPE: &@
2908             PPCODE:
2909             { /* This is very similar to List::Util. Try to maintain compat. */
2910 27           int ret_true = !(ix & 2); /* return true at end of loop for none/all; false for any/notall */
2911 27           int invert = (ix & 1); /* invert block test for all/notall */
2912             int index;
2913             GV *gv;
2914             HV *stash;
2915 27           SV **args = &PL_stack_base[ax];
2916 27           CV *cv = sv_2cv(block, &stash, &gv, 0);
2917              
2918 27 50         if (cv == Nullcv) croak("Not a subroutine reference");
2919              
2920 27           SAVESPTR(GvSV(PL_defgv));
2921             #ifdef dMULTICALL
2922 27 50         if (!CvISXSUB(cv)) {
2923             dMULTICALL;
2924 27           I32 gimme = G_SCALAR;
2925              
2926 27 50         PUSH_MULTICALL(cv);
    50          
2927 5057 100         for (index = 1; index < items; index++) {
2928 5040           GvSV(PL_defgv) = args[index];
2929 5040           MULTICALL;
2930 5040 50         if (SvTRUEx(*PL_stack_sp) ^ invert)
    50          
    0          
    50          
    0          
    0          
    100          
    50          
    50          
    100          
    50          
    100          
    50          
    50          
    50          
    50          
    0          
    50          
    0          
    100          
2931 10           break;
2932             }
2933             FIX_MULTICALL_REFCOUNT;
2934 27 50         POP_MULTICALL;
    50          
2935             }
2936             else
2937             #endif
2938             {
2939 0 0         for (index = 1; index < items; index++) {
2940 0           dSP;
2941 0           GvSV(PL_defgv) = args[index];
2942 0 0         PUSHMARK(SP);
2943 0           call_sv((SV*)cv, G_SCALAR);
2944 0 0         if (SvTRUEx(*PL_stack_sp) ^ invert)
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
2945 0           break;
2946             }
2947             }
2948              
2949 27 100         if (ix == 4) {
2950 5 100         if (index == items)
2951 2           XSRETURN_UNDEF;
2952 3           ST(0) = ST(index);
2953 3           XSRETURN(1);
2954             }
2955 22 100         if (ix == 6) {
2956 5 100         if (index == items)
2957 2           XSRETURN_IV(-1);
2958 3           XSRETURN_UV(index-1);
2959             }
2960              
2961 17 100         if (index != items) /* We exited the loop early */
2962 4           ret_true = !ret_true;
2963              
2964 17 100         if (ret_true) XSRETURN_YES;
2965 27           else XSRETURN_NO;
2966             }
2967              
2968             #ifdef FACTORING_HARNESSES
2969             void
2970             factor_test_harness1(...)
2971             PROTOTYPE: @
2972             PPCODE:
2973             /* Pass in a big array of numbers, we factor them in a timed loop */
2974             {
2975             UV res, factors[MPU_MAX_FACTORS+1], exponents[MPU_MAX_FACTORS+1], *comp;
2976             struct timeval gstart, gstop;
2977             double t_time;
2978             int i, j, k, correct, nf, num = items;
2979              
2980             //num = (items > 100000) ? 100000 : items;
2981             New(0, comp, num, UV);
2982             for (i = 0; i < num; i++)
2983             comp[i] = my_svuv(ST(i));
2984             gettimeofday(&gstart, NULL);
2985             for (j = 0; j < 1; j++) {
2986             correct = 0;
2987             for (i = 0; i < num; i++) {
2988             nf = factor(comp[i], factors);
2989             //nf = squfof_factor(comp[i], factors, 140000);
2990             //nf = pbrent_factor(comp[i], factors, 500000, 1);
2991             //nf = holf_factor(comp[i], factors, 1000000);
2992             //nf = lehman_factor(comp[i], factors, 1);
2993             //nf = lehman_factor(comp[i], factors, 0); if (nf < 2) nf=pbrent_factor(comp[i], factors, 500000, 3);
2994             //nf = factor63(comp[i], factors);
2995             //nf = pminus1_factor(comp[i], factors, 1000,10000);
2996             //nf = prho_factor(comp[i], factors, 10000);
2997             if (nf >= 2) {
2998             for (res = factors[0], k = 1; k < nf; k++)
2999             res *= factors[k];
3000             if (res == comp[i])
3001             correct++;
3002             }
3003             //printf("%lu:",comp[i]);for(k=0;k
3004             }
3005             }
3006             gettimeofday(&gstop, NULL);
3007             t_time = my_difftime(&gstart, &gstop);
3008             Safefree(comp);
3009             printf("factoring got %d of %d correct in %2.2f sec\n", correct, num, t_time);
3010             printf("percent correct = %.3f\n", 100.0*(double)correct / (double)num);
3011             printf("average time per input = %1.4f ms\n", 1000 * t_time / (double)num);
3012             XSRETURN_UV(correct);
3013             }
3014              
3015             void
3016             factor_test_harness2(IN int count, IN int bits = 63)
3017             PREINIT:
3018             dMY_CXT;
3019             PPCODE:
3020             /* We'll factor -bit numbers */
3021             {
3022             UV factors[MPU_MAX_FACTORS+1], exponents[MPU_MAX_FACTORS+1];
3023             FILE *fid = 0; // fopen("results.txt", "w");
3024             uint64_t n, state = 28953;
3025             int i, nfactors, totfactors = 0;
3026             /* Use Knuth MMIX -- simple and no worse than Chacha20 for this */
3027             for (i = 0; i < count; i++) {
3028             state = 6364136223846793005ULL * state + 1442695040888963407ULL;
3029             n = state >> (64-bits);
3030             nfactors = factor_exp(n, factors, exponents);
3031             if (fid) fprintf(fid, "%llu has %d factors\n", n, nfactors);
3032             totfactors += nfactors;
3033             }
3034             if (fid) fclose(fid);
3035             XSRETURN_IV(totfactors);
3036             }
3037              
3038             void
3039             factor_test_harness3(IN UV start, IN UV end)
3040             PREINIT:
3041             dMY_CXT;
3042             PPCODE:
3043             /* We'll factor -bit numbers */
3044             {
3045             UV totf = 0, i, factors[MPU_MAX_FACTORS];
3046             for (i = start; i < end; i++) {
3047             totf += factor(i, factors);
3048             }
3049             XSRETURN_UV(totf);
3050             }
3051              
3052             #endif