[libmath-prime-util-perl] 63/181: Shuffle functions between XS/Util/PP; significant XS changes
Partha P. Mukherjee
ppm-guest at moszumanska.debian.org
Thu May 21 18:51:07 UTC 2015
This is an automated email from the git hooks/post-receive script.
ppm-guest pushed a commit to annotated tag v0.36
in repository libmath-prime-util-perl.
commit 0d7529aa685cbe178fbf9c7f51febdbfc1f5e489
Author: Dana Jacobsen <dana at acm.org>
Date: Mon Dec 30 01:22:08 2013 -0800
Shuffle functions between XS/Util/PP; significant XS changes
---
XS.xs | 582 ++++++++++++++++++----------------------------
lib/Math/Prime/Util.pm | 120 ++--------
lib/Math/Prime/Util/PP.pm | 98 ++++++++
3 files changed, 340 insertions(+), 460 deletions(-)
diff --git a/XS.xs b/XS.xs
index 1576e72..8fa41fc 100644
--- a/XS.xs
+++ b/XS.xs
@@ -68,10 +68,6 @@
#endif
-static int pbrent_factor_a1(UV n, UV *factors, UV maxrounds) {
- return pbrent_factor(n, factors, maxrounds, 1);
-}
-
#if BITS_PER_WORD == 32
static const unsigned int uvmax_maxlen = 10;
static const unsigned int ivmax_maxlen = 10;
@@ -91,12 +87,14 @@ static int pbrent_factor_a1(UV n, UV *factors, UV maxrounds) {
*/
static int _validate_int(SV* n, int negok)
{
- dTHX;
+ pTHX_;
const char* maxstr;
char* ptr;
STRLEN i, len, maxlen;
int ret, isneg = 0;
+ /* TODO: magic, grok_number, etc. */
+ if (SvGAMAGIC(n)) return 0; /* Leave while we still can */
if (!SvOK(n)) croak("Parameter must be defined");
if (SvIOK(n)) { /* If defined as number, use that */
if (SvIsUV(n) || SvIV(n) >= 0) return 1;
@@ -132,38 +130,16 @@ static int _validate_int(SV* n, int negok)
return ret; /* value = UV_MAX/UV_MIN. That's ok */
}
-/* Call a Perl sub to handle work for us.
- * The input is a single SV on the top of the stack.
- * The output is a single mortal SV that is on the stack.
- */
-static void _vcallsub(const char* name)
+/* Call a Perl sub to handle work for us. */
+static int _vcallsubn(I32 flags, const char* name, int nargs)
{
- dTHX;
- dSP; /* Local copy of stack pointer */
- int count;
- SV* arg;
-
- ENTER; /* Start wrapper */
- SAVETMPS; /* Start (2) */
-
- arg = POPs; /* Get argument value from stack */
- PUSHMARK(SP); /* Start args: note our SP */
- XPUSHs(arg);
- PUTBACK; /* End args: set global SP to ours */
-
- count = call_pv(name, G_SCALAR); /* Call the sub */
- SPAGAIN; /* refresh local stack pointer */
-
- if (count != 1)
- croak("callback sub should return one value");
-
- TOPs = SvREFCNT_inc(TOPs); /* Make sure FREETMPS doesn't kill it */
-
- PUTBACK;
- FREETMPS; /* End wrapper */
- LEAVE; /* End (2) */
- TOPs = sv_2mortal(TOPs); /* mortalize it. refcnt will be 1. */
+ pTHX_;
+ dSP;
+ PUSHMARK(SP-nargs);
+ PUTBACK;
+ return call_pv(name, flags);
}
+#define _vcallsub(func) (void)_vcallsubn(G_SCALAR, func, 1)
#if BITS_PER_WORD == 64
static const UV _max_prime = UVCONST(18446744073709551557);
@@ -267,37 +243,10 @@ _XS_prime_maxbits()
SV*
sieve_primes(IN UV low, IN UV high)
- PREINIT:
- AV* av = newAV();
- CODE:
- if (low <= high) {
- START_DO_FOR_EACH_PRIME(low, high) {
- av_push(av,newSVuv(p));
- } END_DO_FOR_EACH_PRIME
- }
- RETVAL = newRV_noinc( (SV*) av );
- OUTPUT:
- RETVAL
-
-
-SV*
-trial_primes(IN UV low, IN UV high)
- PREINIT:
- UV p;
- AV* av = newAV();
- CODE:
- if (low <= high) {
- if (low >= 2) low--; /* Make sure low gets included */
- for (p = _XS_next_prime(low); p <= high && p != 0; p = _XS_next_prime(p)) {
- av_push(av,newSVuv(p));
- }
- }
- RETVAL = newRV_noinc( (SV*) av );
- OUTPUT:
- RETVAL
-
-SV*
-segment_primes(IN UV low, IN UV high);
+ ALIAS:
+ trial_primes = 1
+ erat_primes = 2
+ segment_primes = 3
PREINIT:
AV* av = newAV();
CODE:
@@ -306,102 +255,39 @@ segment_primes(IN UV low, IN UV high);
if ((low <= 5) && (high >= 5)) { av_push(av, newSVuv( 5 )); }
if (low < 7) low = 7;
if (low <= high) {
- unsigned char* segment;
- UV seg_base, seg_low, seg_high;
- void* ctx = start_segment_primes(low, high, &segment);
- while (next_segment_primes(ctx, &seg_base, &seg_low, &seg_high)) {
- START_DO_FOR_EACH_SIEVE_PRIME( segment, seg_low - seg_base, seg_high - seg_base )
- av_push(av,newSVuv( seg_base + p ));
- END_DO_FOR_EACH_SIEVE_PRIME
- }
- end_segment_primes(ctx);
- }
- RETVAL = newRV_noinc( (SV*) av );
- OUTPUT:
- RETVAL
-
-SV*
-erat_primes(IN UV low, IN UV high)
- PREINIT:
- unsigned char* sieve;
- AV* av = newAV();
- CODE:
- if (low <= high) {
- sieve = sieve_erat30(high);
- if (sieve == 0) {
- croak("Could not generate sieve for %"UVuf, high);
- } else {
- if ((low <= 2) && (high >= 2)) { av_push(av, newSVuv( 2 )); }
- if ((low <= 3) && (high >= 3)) { av_push(av, newSVuv( 3 )); }
- if ((low <= 5) && (high >= 5)) { av_push(av, newSVuv( 5 )); }
- if (low < 7) { low = 7; }
+ if (ix == 0) { /* Sieve */
+ START_DO_FOR_EACH_PRIME(low, high) {
+ av_push(av,newSVuv(p));
+ } END_DO_FOR_EACH_PRIME
+ } else if (ix == 1) { /* Trial */
+ for (low = _XS_next_prime(low-1);
+ low <= high && low != 0;
+ low = _XS_next_prime(low) ) {
+ av_push(av,newSVuv(low));
+ }
+ } else if (ix == 2) { /* Erat */
+ unsigned char* sieve = sieve_erat30(high);
+ if (sieve == 0) croak("Could not generate sieve for %"UVuf, high);
START_DO_FOR_EACH_SIEVE_PRIME( sieve, low, high ) {
av_push(av,newSVuv(p));
} END_DO_FOR_EACH_SIEVE_PRIME
Safefree(sieve);
+ } else if (ix == 3) { /* Segment */
+ unsigned char* segment;
+ UV seg_base, seg_low, seg_high;
+ void* ctx = start_segment_primes(low, high, &segment);
+ while (next_segment_primes(ctx, &seg_base, &seg_low, &seg_high)) {
+ START_DO_FOR_EACH_SIEVE_PRIME( segment, seg_low - seg_base, seg_high - seg_base )
+ av_push(av,newSVuv( seg_base + p ));
+ END_DO_FOR_EACH_SIEVE_PRIME
+ }
+ end_segment_primes(ctx);
}
}
RETVAL = newRV_noinc( (SV*) av );
OUTPUT:
RETVAL
-
-#define SIMPLE_FACTOR(func, n, arg1) \
- if (n <= 1) { \
- if (n == 0) \
- XPUSHs(sv_2mortal(newSVuv( n ))); \
- } else { \
- while ( (n% 2) == 0 ) { n /= 2; XPUSHs(sv_2mortal(newSVuv( 2 ))); } \
- while ( (n% 3) == 0 ) { n /= 3; XPUSHs(sv_2mortal(newSVuv( 3 ))); } \
- while ( (n% 5) == 0 ) { n /= 5; XPUSHs(sv_2mortal(newSVuv( 5 ))); } \
- if (n == 1) { /* done */ } \
- else if (_XS_is_prime(n)) { XPUSHs(sv_2mortal(newSVuv( n ))); } \
- else { \
- UV factors[MPU_MAX_FACTORS+1]; \
- int i, nfactors; \
- nfactors = func(n, factors, arg1); \
- for (i = 0; i < nfactors; i++) { \
- XPUSHs(sv_2mortal(newSVuv( factors[i] ))); \
- } \
- } \
- }
-#define SIMPLE_FACTOR_2ARG(func, n, arg1, arg2) \
- /* Stupid MSVC won't bring its C compiler out of the 1980s. */ \
- if (n <= 1) { \
- if (n == 0) \
- XPUSHs(sv_2mortal(newSVuv( n ))); \
- } else { \
- while ( (n% 2) == 0 ) { n /= 2; XPUSHs(sv_2mortal(newSVuv( 2 ))); } \
- while ( (n% 3) == 0 ) { n /= 3; XPUSHs(sv_2mortal(newSVuv( 3 ))); } \
- while ( (n% 5) == 0 ) { n /= 5; XPUSHs(sv_2mortal(newSVuv( 5 ))); } \
- if (n == 1) { /* done */ } \
- else if (_XS_is_prime(n)) { XPUSHs(sv_2mortal(newSVuv( n ))); } \
- else { \
- UV factors[MPU_MAX_FACTORS+1]; \
- int i, nfactors; \
- nfactors = func(n, factors, arg1, arg2); \
- for (i = 0; i < nfactors; i++) { \
- XPUSHs(sv_2mortal(newSVuv( factors[i] ))); \
- } \
- } \
- }
-
-void
-_XS_factor(IN UV n)
- PREINIT:
- UV factors[MPU_MAX_FACTORS+1];
- int i, nfactors;
- PPCODE:
- nfactors = factor(n, factors);
- if (GIMME_V == G_SCALAR) {
- PUSHs(sv_2mortal(newSVuv(nfactors)));
- } else if (GIMME_V == G_ARRAY) {
- EXTEND(SP, nfactors);
- for (i = 0; i < nfactors; i++) {
- PUSHs(sv_2mortal(newSVuv( factors[i] )));
- }
- }
-
void
_XS_factor_exp(IN UV n)
PREINIT:
@@ -437,51 +323,54 @@ _XS_divisors(IN UV n)
}
void
-trial_factor(IN UV n, IN UV maxfactor = 0)
- PPCODE:
- SIMPLE_FACTOR(trial_factor, n, maxfactor);
-
-void
-fermat_factor(IN UV n, IN UV maxrounds = 64*1024*1024)
- PPCODE:
- SIMPLE_FACTOR(fermat_factor, n, maxrounds);
-
-void
-holf_factor(IN UV n, IN UV maxrounds = 8*1024*1024)
- PPCODE:
- SIMPLE_FACTOR(holf_factor, n, maxrounds);
-
-void
-squfof_factor(IN UV n, IN UV maxrounds = 4*1024*1024)
- PPCODE:
- SIMPLE_FACTOR(squfof_factor, n, maxrounds);
-
-void
-rsqufof_factor(IN UV n, IN UV maxrounds = 4*1024*1024)
- PPCODE:
- SIMPLE_FACTOR(racing_squfof_factor, n, maxrounds);
-
-void
-pbrent_factor(IN UV n, IN UV maxrounds = 4*1024*1024)
- PPCODE:
- SIMPLE_FACTOR(pbrent_factor_a1, n, maxrounds);
-
-void
-prho_factor(IN UV n, IN UV maxrounds = 4*1024*1024)
- PPCODE:
- SIMPLE_FACTOR(prho_factor, n, maxrounds);
-
-void
-pminus1_factor(IN UV n, IN UV B1 = 1*1024*1024, IN UV B2 = 0)
- PPCODE:
- if (B2 == 0)
- B2 = 10*B1;
- SIMPLE_FACTOR_2ARG(pminus1_factor, n, B1, B2);
-
-void
-pplus1_factor(IN UV n, IN UV B = 200)
+trial_factor(IN UV n, ...)
+ ALIAS:
+ fermat_factor = 1
+ holf_factor = 2
+ squfof_factor = 3
+ rsqufof_factor = 4
+ pbrent_factor = 5
+ prho_factor = 6
+ pplus1_factor = 7
+ pminus1_factor = 8
PPCODE:
- SIMPLE_FACTOR(pplus1_factor, n, B);
+ if (n == 0) XSRETURN_UV(0);
+ while ( (n% 2) == 0 ) { n /= 2; XPUSHs(sv_2mortal(newSVuv( 2 ))); }
+ while ( (n% 3) == 0 ) { n /= 3; XPUSHs(sv_2mortal(newSVuv( 3 ))); }
+ while ( (n% 5) == 0 ) { n /= 5; XPUSHs(sv_2mortal(newSVuv( 5 ))); }
+ if (n == 1) { /* done */ }
+ else if (_XS_is_prime(n)) { XPUSHs(sv_2mortal(newSVuv( n ))); }
+ else {
+ UV arg1, arg2, factors[MPU_MAX_FACTORS+1];
+ int i, nfactors = 0;
+ switch (ix) {
+ case 0: arg1 = (items < 2) ? 0 : SvUV(ST(1));
+ nfactors = trial_factor (n, factors, arg1); break;
+ case 1: arg1 = (items < 2) ? 64*1024*1024 : SvUV(ST(1));
+ nfactors = fermat_factor (n, factors, arg1); break;
+ case 2: arg1 = (items < 2) ? 8*1024*1024 : SvUV(ST(1));
+ nfactors = holf_factor (n, factors, arg1); break;
+ case 3: arg1 = (items < 2) ? 4*1024*1024 : SvUV(ST(1));
+ nfactors = squfof_factor (n, factors, arg1); break;
+ case 4: arg1 = (items < 2) ? 4*1024*1024 : SvUV(ST(1));
+ nfactors = racing_squfof_factor(n, factors, arg1); break;
+ case 5: arg1 = (items < 2) ? 4*1024*1024 : SvUV(ST(1));
+ arg2 = (items < 3) ? 1 : SvUV(ST(2));
+ nfactors = pbrent_factor (n, factors, arg1, arg2); break;
+ case 6: arg1 = (items < 2) ? 4*1024*1024 : SvUV(ST(1));
+ nfactors = prho_factor (n, factors, arg1); break;
+ case 7: arg1 = (items < 2) ? 200 : SvUV(ST(1));
+ nfactors = pplus1_factor (n, factors, arg1); break;
+ case 8: arg1 = (items < 2) ? 1*1024*1024 : SvUV(ST(1));
+ arg2 = (items < 3) ? 0 : SvUV(ST(2));
+ if (arg2 == 0) arg2 = 10*arg1; /* default B2 */
+ nfactors = pminus1_factor(n, factors, arg1, arg2); break;
+ default: break;
+ }
+ EXTEND(SP, nfactors);
+ for (i = 0; i < nfactors; i++)
+ PUSHs(sv_2mortal(newSVuv( factors[i] )));
+ }
int
_XS_is_pseudoprime(IN UV n, IN UV a)
@@ -558,7 +447,7 @@ _XS_is_almost_extra_strong_lucas_pseudoprime(IN UV n, IN UV increment = 1)
OUTPUT:
RETVAL
-int
+void
is_prime(IN SV* svn)
ALIAS:
is_prob_prime = 1
@@ -608,6 +497,47 @@ next_prime(IN SV* n)
XSRETURN(1);
void
+factor(IN SV* svn)
+ PREINIT:
+ int status, i, nfactors;
+ PPCODE:
+ status = _validate_int(svn, 0);
+ if (status == 1) {
+ UV n, factors[MPU_MAX_FACTORS+1];
+ set_val_from_sv(n, svn);
+ nfactors = factor(n, factors);
+ if (GIMME_V == G_SCALAR) {
+ PUSHs(sv_2mortal(newSVuv(nfactors)));
+ } else if (GIMME_V == G_ARRAY) {
+ EXTEND(SP, nfactors);
+ for (i = 0; i < nfactors; i++) {
+ PUSHs(sv_2mortal(newSVuv( factors[i] )));
+ }
+ }
+ } else {
+ XSRETURN(_vcallsubn(GIMME_V, "Math::Prime::Util::_generic_factor", 1));
+ }
+
+
+void
+znorder(IN SV* sva, IN SV* svn)
+ PREINIT:
+ int astatus, nstatus;
+ PPCODE:
+ astatus = _validate_int(sva, 0);
+ nstatus = _validate_int(svn, 0);
+ if (astatus == 1 && nstatus == 1) {
+ UV a, n, order;
+ set_val_from_sv(a, sva);
+ set_val_from_sv(n, svn);
+ order = znorder(a, n);
+ if (order == 0) XSRETURN_UNDEF;
+ XSRETURN_UV(order);
+ } else {
+ XSRETURN( _vcallsubn(G_SCALAR, "Math::Prime::Util::_generic_znorder", 2) );
+ }
+
+void
znprimroot(IN SV* svn)
PREINIT:
int status;
@@ -625,224 +555,158 @@ znprimroot(IN SV* svn)
_vcallsub("Math::Prime::Util::_generic_znprimroot");
XSRETURN(1);
-int
+void
kronecker(IN SV* sva, IN SV* svb)
PREINIT:
int astatus, bstatus;
- CODE:
+ PPCODE:
astatus = _validate_int(sva, 2);
bstatus = _validate_int(svb, 2);
if (astatus == 1 && bstatus == 1) {
UV a, b;
set_val_from_sv(a, sva);
set_val_from_sv(b, svb);
- RETVAL = kronecker_uu(a, b);
+ XSRETURN_IV( kronecker_uu(a, b) );
} else if (astatus != 0 && SvIOK(sva) && !SvIsUV(sva) &&
bstatus != 0 && SvIOK(svb) && !SvIsUV(svb) ) {
IV a, b;
set_sval_from_sv(a, sva);
set_sval_from_sv(b, svb);
- RETVAL = kronecker_ss(a, b);
+ XSRETURN_IV( kronecker_ss(a, b) );
} else {
- dTHX;
- dSP;
- int count;
- ENTER;
- SAVETMPS;
- PUSHMARK(SP);
- XPUSHs(sva);
- XPUSHs(svb);
- PUTBACK;
- count = call_pv("Math::Prime::Util::_generic_kronecker", G_SCALAR);
- SPAGAIN;
- if (count != 1) croak("callback sub should return one value");
- RETVAL = POPi;
- PUTBACK;
- FREETMPS;
- LEAVE;
+ XSRETURN( _vcallsubn(G_SCALAR, "Math::Prime::Util::_generic_kronecker", 2) );
}
- OUTPUT:
- RETVAL
double
-_XS_ExponentialIntegral(IN double x)
+_XS_ExponentialIntegral(IN SV* x)
ALIAS:
_XS_LogarithmicIntegral = 1
_XS_RiemannZeta = 2
_XS_RiemannR = 3
+ _XS_chebyshev_theta = 4
+ _XS_chebyshev_psi = 5
PREINIT:
double ret;
CODE:
switch (ix) {
- case 0: ret = _XS_ExponentialIntegral(x); break;
- case 1: ret = _XS_LogarithmicIntegral(x); break;
- case 2: ret = (double) ld_riemann_zeta(x); break;
- case 3: ret = _XS_RiemannR(x); break;
- default: croak("_XS_ExponentialIntegral: Unknown function alias"); break;
- }
- RETVAL = ret;
- OUTPUT:
- RETVAL
-
-double
-_XS_chebyshev_theta(IN UV n)
- ALIAS:
- _XS_chebyshev_psi = 1
- PREINIT:
- double ret;
- CODE:
- switch (ix) {
- case 0: ret = _XS_chebyshev_theta(n); break;
- case 1: ret = _XS_chebyshev_psi(n); break;
- default: croak("_XS_chebyshev_theta: Unknown function alias"); break;
+ case 0: ret = _XS_ExponentialIntegral(SvNV(x)); break;
+ case 1: ret = _XS_LogarithmicIntegral(SvNV(x)); break;
+ case 2: ret = (double) ld_riemann_zeta(SvNV(x)); break;
+ case 3: ret = _XS_RiemannR(SvNV(x)); break;
+ case 4: ret = _XS_chebyshev_theta(SvUV(x)); break;
+ case 5: ret = _XS_chebyshev_psi(SvUV(x)); break;
+ default: break;
}
RETVAL = ret;
OUTPUT:
RETVAL
void
-_XS_totient(IN UV lo, IN UV hi = 0)
+euler_phi(IN SV* svlo, ...)
PREINIT:
- UV i;
+ int lostatus, histatus;
+ UV i, lo, hi;
PPCODE:
- if (hi != lo && hi != 0) { /* Totients in a range, returns array */
- UV* totients;
- if (hi < lo) XSRETURN_EMPTY;
- if (lo < 2) {
- if (lo <= 0 ) XPUSHs(sv_2mortal(newSVuv(0)));
- if (lo <= 1 && hi >= 1) XPUSHs(sv_2mortal(newSVuv(1)));
- lo = 2;
+ if (items == 1) {
+ int lostatus = _validate_int(svlo, 1);
+ if (lostatus == -1) { /* I like SAGE's decision that */
+ XSRETURN_UV(0); /* totient(n) = 0 if n <= 0 */
+ } else if (lostatus == 1) {
+ UV lo;
+ set_val_from_sv(lo, svlo);
+ XSRETURN_UV(totient(lo));
+ } else {
+ XSRETURN( _vcallsubn(G_SCALAR, "Math::Prime::Util::_generic_euler_phi", 1) );
}
- if (hi >= lo) {
- totients = _totient_range(lo, hi);
- /* Extend the stack to handle how many items we'll return */
- EXTEND(SP, hi-lo+1);
- for (i = lo; i <= hi; i++)
- PUSHs(sv_2mortal(newSVuv(totients[i-lo])));
- Safefree(totients);
+ } else if (items == 2) {
+ SV* svhi = ST(1);
+ int lostatus = _validate_int(svlo, 1);
+ int histatus = _validate_int(svhi, 1);
+ if (lostatus == 1 && histatus == 1) {
+ UV lo, hi;
+ set_val_from_sv(lo, svlo);
+ set_val_from_sv(hi, svhi);
+ if (hi < lo) XSRETURN_EMPTY;
+ if (lo < 2) {
+ if (lo <= 0 ) XPUSHs(sv_2mortal(newSVuv(0)));
+ if (lo <= 1 && hi >= 1) XPUSHs(sv_2mortal(newSVuv(1)));
+ lo = 2;
+ }
+ if (hi >= lo) {
+ UV* totients = _totient_range(lo, hi);
+ /* Extend the stack to handle how many items we'll return */
+ EXTEND(SP, hi-lo+1);
+ for (i = lo; i <= hi; i++)
+ PUSHs(sv_2mortal(newSVuv(totients[i-lo])));
+ Safefree(totients);
+ }
+ } else {
+ XSRETURN( _vcallsubn(G_ARRAY, "Math::Prime::Util::_generic_euler_phi", 2) );
}
} else {
- XSRETURN_UV(totient(lo));
- }
-
-void
-carmichael_lambda(IN SV* svn)
- PREINIT:
- int status;
- PPCODE:
- status = _validate_int(svn, 0);
- if (status == 1) {
- UV n;
- set_val_from_sv(n, svn);
- XSRETURN_UV(carmichael_lambda(n));
- }
- _vcallsub("Math::Prime::Util::_generic_carmichael_lambda");
- XSRETURN(1);
-
-int
-znorder(IN SV* sva, IN SV* svn)
- PREINIT:
- int astatus, nstatus;
- PPCODE:
- astatus = _validate_int(sva, 0);
- nstatus = _validate_int(svn, 0);
- if (astatus == 1 && nstatus == 1) {
- UV a, n, order;
- set_val_from_sv(a, sva);
- set_val_from_sv(n, svn);
- order = znorder(a, n);
- if (order == 0) XSRETURN_UNDEF;
- XSRETURN_UV(order);
- } else {
- dTHX;
- dSP;
- int count;
- ENTER;
- SAVETMPS;
- (void) POPs; (void) POPs;
- PUSHMARK(SP);
- XPUSHs(sva);
- XPUSHs(svn);
- PUTBACK;
- count = call_pv("Math::Prime::Util::_generic_znorder", G_SCALAR);
- SPAGAIN;
- if (count != 1) croak("callback sub should return one value");
- TOPs = SvREFCNT_inc(TOPs);
- PUTBACK;
- FREETMPS;
- LEAVE;
- TOPs = sv_2mortal(TOPs);
- XSRETURN(1);
+ croak("Usage: euler_phi(n) or euler_phi(1o,hi)");
}
-
+
void
-_XS_moebius(IN UV lo, IN UV hi = 0)
- PREINIT:
- UV i;
+moebius(IN SV* svlo, ...)
PPCODE:
- if (hi != lo && hi != 0) { /* mobius in a range */
- signed char* mu = _moebius_range(lo, hi);
- MPUassert( mu != 0, "_moebius_range returned 0" );
- EXTEND(SP, hi-lo+1);
- for (i = lo; i <= hi; i++)
- PUSHs(sv_2mortal(newSViv(mu[i-lo])));
- Safefree(mu);
+ if (items == 1) {
+ int nstatus = _validate_int(svlo, 0);
+ if (nstatus == 1) {
+ UV n;
+ set_val_from_sv(n, svlo);
+ XSRETURN_IV(moebius(n));
+ } else {
+ XSRETURN(_vcallsubn(G_SCALAR, "Math::Prime::Util::_generic_moebius",1));
+ }
+ } else if (items == 2) {
+ SV* svhi = ST(1);
+ int lostatus = _validate_int(svlo, 0);
+ int histatus = _validate_int(svhi, 0);
+ if (lostatus == 1 && histatus == 1) {
+ UV i, lo, hi;
+ set_val_from_sv(lo, svlo);
+ set_val_from_sv(hi, svhi);
+ if (hi < lo) XSRETURN_EMPTY;
+ if (hi >= lo) {
+ signed char* mu = _moebius_range(lo, hi);
+ MPUassert( mu != 0, "_moebius_range returned 0" );
+ EXTEND(SP, hi-lo+1);
+ for (i = lo; i <= hi; i++)
+ PUSHs(sv_2mortal(newSViv(mu[i-lo])));
+ Safefree(mu);
+ }
+ } else {
+ XSRETURN(_vcallsubn(G_ARRAY, "Math::Prime::Util::_generic_moebius",2));
+ }
} else {
- UV factors[MPU_MAX_FACTORS+1];
- UV nfactors;
- UV n = lo;
-
- if (n <= 1)
- XSRETURN_IV(n);
-
- if ( (!(n% 4) && n >= 4) || (!(n% 9) && n >= 9) ||
- (!(n%25) && n >= 25) || (!(n%49) && n >= 49) )
- XSRETURN_IV(0);
-
- nfactors = factor(n, factors);
- for (i = 1; i < nfactors; i++)
- if (factors[i] == factors[i-1])
- XSRETURN_IV(0);
-
- XSRETURN_IV( (nfactors % 2) ? -1 : 1 );
+ croak("Usage: moebius(n) or moebius(1o,hi)");
}
IV
_XS_mertens(IN UV n)
+
void
-exp_mangoldt(IN SV* svn)
+carmichael_lambda(IN SV* svn)
+ ALIAS:
+ exp_mangoldt = 1
PREINIT:
int status;
UV n;
PPCODE:
- status = _validate_int(svn, 1);
+ status = _validate_int(svn, (ix == 0) ? 0 : 1);
if (status == -1) {
XSRETURN_UV(1);
} else if (status == 1) {
+ UV n;
set_val_from_sv(n, svn);
- if (n <= 1)
- XSRETURN_UV(1);
- else if ((n & (n-1)) == 0) /* Power of 2 */
- XSRETURN_UV(2);
- else if ((n & 1) == 0) /* Even number */
- XSRETURN_UV(1);
- else {
- UV factors[MPU_MAX_FACTORS+1];
- UV nfactors, i;
- /* We could try a partial factor, e.g. looking for two small factors */
- /* We could also check powers of primes searching for n */
- nfactors = factor(n, factors);
- for (i = 1; i < nfactors; i++) {
- if (factors[i] != factors[0])
- XSRETURN_UV(1);
- }
- XSRETURN_UV(factors[0]);
- }
- } else {
- _vcallsub("Math::Prime::Util::_generic_exp_mangoldt");
- XSRETURN(1);
+ if (ix == 0) XSRETURN_UV(carmichael_lambda(n));
+ else XSRETURN_UV(exp_mangoldt(n));
}
+ _vcallsub( (ix == 0) ? "Math::Prime::Util::_generic_carmichael_lambda"
+ : "Math::Prime::Util::_generic_exp_mangoldt" );
+ XSRETURN(1);
int
_validate_num(SV* n, ...)
diff --git a/lib/Math/Prime/Util.pm b/lib/Math/Prime/Util.pm
index fa11fa9..0fc5705 100644
--- a/lib/Math/Prime/Util.pm
+++ b/lib/Math/Prime/Util.pm
@@ -60,7 +60,6 @@ sub import {
sub _import_nobigint {
$_Config{'nobigint'} = 1;
return unless $_Config{'xs'};
- undef *factor; *factor = \&_XS_factor;
undef *factor_exp; *factor_exp = \&_XS_factor_exp;
undef *divisors; *divisors = \&_XS_divisors;
#undef *prime_count; *prime_count = \&_XS_prime_count;
@@ -69,7 +68,6 @@ sub _import_nobigint {
undef *is_strong_pseudoprime; *is_strong_pseudoprime = \&_XS_miller_rabin;
undef *moebius; *moebius = \&_XS_moebius;
undef *mertens; *mertens = \&_XS_mertens;
- undef *euler_phi; *euler_phi = \&_XS_totient;
undef *chebyshev_theta; *chebyshev_theta = \&_XS_chebyshev_theta;
undef *chebyshev_psi; *chebyshev_psi = \&_XS_chebyshev_psi;
# These should be fast anyway, but this skips validation.
@@ -105,10 +103,13 @@ BEGIN {
*next_prime = \&Math::Prime::Util::_generic_next_prime;
*prev_prime = \&Math::Prime::Util::_generic_prev_prime;
*exp_mangoldt = \&Math::Prime::Util::_generic_exp_mangoldt;
+ *euler_phi = \&Math::Prime::Util::_generic_euler_phi;
+ *moebius = \&Math::Prime::Util::_generic_moebius;
*carmichael_lambda = \&Math::Prime::Util::_generic_carmichael_lambda;
*kronecker = \&Math::Prime::Util::_generic_kronecker;
*znorder = \&Math::Prime::Util::_generic_znorder;
*znprimroot = \&Math::Prime::Util::_generic_znprimroot;
+ *factor = \&Math::Prime::Util::_generic_factor;
*forprimes = sub (&$;$) { _generic_forprimes(@_); }; ## no critic qw(ProhibitSubroutinePrototypes)
*fordivisors = sub (&$) { _generic_fordivisors(@_); }; ## no critic qw(ProhibitSubroutinePrototypes)
*forcomposites = sub (&$) { _generic_forcomposites(@_); }; ## no critic qw(ProhibitSubroutinePrototypes)
@@ -219,7 +220,11 @@ sub prime_set_config {
sub _validate_positive_integer {
my($n, $min, $max) = @_;
- # We've gone through _validate_num already, so we just need to handle bigints
+ # We need to handle bigints, magic variables, and coderefs
+ if (ref($n) eq 'CODE') {
+ $_[0] = $_[0]->();
+ $n = $_[0];
+ }
croak "Parameter '$n' must be a positive integer"
if ref($n) eq 'Math::BigInt' && $n->sign() ne '+';
croak "Parameter '$n' must be >= $min" if defined $min && $n < $min;
@@ -1257,58 +1262,15 @@ sub divisors {
# alias the old "all_factors" to the new name: divisors
*all_factors = \&divisors;
-
# A008683 Moebius function mu(n)
# A030059, A013929, A030229, A002321, A005117, A013929 all relate.
-sub moebius {
+sub _generic_moebius {
my($n, $nend) = @_;
+ return 0 if defined $n && $n < 0;
_validate_num($n) || _validate_positive_integer($n);
-
- if (defined $nend) {
- _validate_num($nend) || _validate_positive_integer($nend);
- return if $nend < $n;
- } else {
- $nend = $n;
- }
- return _XS_moebius($n, $nend) if $nend <= $_XS_MAXVAL;
-
- # Moebius over a range.
- if ($nend != $n) {
- my ($lo,$hi) = ($n,$nend);
- my @mu = map { 1 } $lo .. $hi;
- $mu[0] = 0 if $lo == 0;
- my $sqrtn = int(sqrt($hi)+0.5);
- forprimes {
- my $p = $_;
- my $i = $p * $p;
- $i = $i * int($lo/$i) + (($lo % $i) ? $i : 0) if $i < $lo;
- while ($i <= $hi) {
- $mu[$i-$lo] = 0;
- $i += $p * $p;
- }
- $i = $p;
- $i = $i * int($lo/$i) + (($lo % $i) ? $i : 0) if $i < $lo;
- while ($i <= $hi) {
- $mu[$i-$lo] *= -$p;
- $i += $p;
- }
- } $sqrtn;
- foreach my $i ($lo .. $hi) {
- my $m = $mu[$i-$lo];
- $m *= -1 if abs($m) != $i;
- $mu[$i-$lo] = ($m>0) - ($m<0);
- }
- return @mu;
- }
-
- return $n if $n <= 1;
- # Quick check for small replicated factors
- return 0 if ($n >= 25) && (!($n % 4) || !($n % 9) || !($n % 25));
- my @factors = factor($n);
- foreach my $i (1 .. $#factors) {
- return 0 if $factors[$i] == $factors[$i-1];
- }
- return (((scalar @factors) % 2) == 0) ? 1 : -1;
+ return Math::Prime::Util::PP::moebius($n) if !defined $nend;
+ _validate_num($nend) || _validate_positive_integer($nend);
+ return Math::Prime::Util::PP::moebius_range($n, $nend);
}
# A002321 Mertens' function. mertens(n) = sum(moebius(1,n))
@@ -1346,56 +1308,13 @@ sub mertens {
# A000010 Euler Phi, aka Euler Totient
-sub euler_phi {
+sub _generic_euler_phi {
my($n, $nend) = @_;
- # SAGE defines this to be 0 for all n <= 0. Others choose differently.
- # I am following SAGE's decision for n <= 0.
return 0 if defined $n && $n < 0;
_validate_num($n) || _validate_positive_integer($n);
- if (defined $nend) {
- _validate_num($nend) || _validate_positive_integer($nend);
- return if $nend < $n;
- } else {
- $nend = $n;
- }
- return _XS_totient($n, $nend) if $nend <= $_XS_MAXVAL;
-
- # Totient over a range. Could be improved, as this can use huge memory.
- if ($nend != $n) {
- return () if $nend < $n;
- my @totients = (0 .. $nend);
- foreach my $i (2 .. $nend) {
- next unless $totients[$i] == $i;
- $totients[$i] = $i-1;
- foreach my $j (2 .. int($nend / $i)) {
- $totients[$i*$j] -= $totients[$i*$j]/$i;
- }
- }
- splice(@totients, 0, $n) if $n > 0;
- return @totients;
- }
-
- return $n if $n <= 1;
-
- my @pe = factor_exp($n);
- my $totient = $n - $n + 1;
-
- if (ref($n) ne 'Math::BigInt') {
- foreach my $f (@pe) {
- my ($p, $e) = @$f;
- $totient *= $p - 1;
- $totient *= $p for 2 .. $e;
- }
- } else {
- my $zero = $n->copy->bzero;
- foreach my $f (@pe) {
- my ($p, $e) = @$f;
- $p = $zero->copy->badd("$p");
- $totient->bmul($p->copy->bdec());
- $totient->bmul($p) for 2 .. $e;
- }
- }
- return $totient;
+ return Math::Prime::Util::PP::euler_phi($n) if !defined $nend;
+ _validate_num($nend) || _validate_positive_integer($nend);
+ return Math::Prime::Util::PP::euler_phi_range($n, $nend);
}
# Jordan's totient -- a generalization of Euler's totient.
@@ -1600,8 +1519,7 @@ sub _generic_exp_mangoldt {
sub liouville {
my($n) = @_;
_validate_num($n) || _validate_positive_integer($n);
- my $l = ($n <= $_XS_MAXVAL) ? (-1) ** scalar _XS_factor($n)
- : (-1) ** scalar factor($n);
+ my $l = (-1) ** scalar factor($n);
return $l;
}
@@ -1891,7 +1809,7 @@ sub nth_prime {
return Math::Prime::Util::PP::nth_prime($n);
}
-sub factor {
+sub _generic_factor {
my($n) = @_;
_validate_num($n) || _validate_positive_integer($n);
diff --git a/lib/Math/Prime/Util/PP.pm b/lib/Math/Prime/Util/PP.pm
index 1f0b817..d5383bb 100644
--- a/lib/Math/Prime/Util/PP.pm
+++ b/lib/Math/Prime/Util/PP.pm
@@ -447,6 +447,104 @@ sub prev_prime {
#$d*30+$m;
}
+sub euler_phi {
+ my($n) = @_;
+ return 0 if $n < 0;
+ return $n if $n <= 1;
+
+ my @pe = Math::Prime::Util::factor_exp($n);
+ my $totient = $n - $n + 1;
+
+ if (ref($n) ne 'Math::BigInt') {
+ foreach my $f (@pe) {
+ my ($p, $e) = @$f;
+ $totient *= $p - 1;
+ $totient *= $p for 2 .. $e;
+ }
+ } else {
+ my $zero = $n->copy->bzero;
+ foreach my $f (@pe) {
+ my ($p, $e) = @$f;
+ $p = $zero->copy->badd("$p");
+ $totient->bmul($p->copy->bdec());
+ $totient->bmul($p) for 2 .. $e;
+ }
+ }
+ return $totient;
+}
+
+sub euler_phi_range {
+ my($n, $nend) = @_;
+ return () if $nend < $n;
+ return euler_phi($n) if $n == $nend;
+ my @totients;
+ if ($nend > 2**30) {
+ while ($n < $nend) {
+ push @totients, euler_phi($n++);
+ }
+ } else {
+ @totients = (0 .. $nend);
+ foreach my $i (2 .. $nend) {
+ next unless $totients[$i] == $i;
+ $totients[$i] = $i-1;
+ foreach my $j (2 .. int($nend / $i)) {
+ $totients[$i*$j] -= $totients[$i*$j]/$i;
+ }
+ }
+ splice(@totients, 0, $n) if $n > 0;
+ }
+ return @totients;
+}
+
+sub moebius {
+ my($n) = @_;
+ return 0 if $n <= 0;
+ return 1 if $n == 1;
+ return 0 if ($n >= 25) && (!($n % 4) || !($n % 9) || !($n % 25));
+ my @factors = Math::Prime::Util::factor($n);
+ foreach my $i (1 .. $#factors) {
+ return 0 if $factors[$i] == $factors[$i-1];
+ }
+ return (((scalar @factors) % 2) == 0) ? 1 : -1;
+}
+
+sub moebius_range {
+ my($lo, $hi) = @_;
+ return () if $hi < $lo;
+ return moebius($lo) if $lo == $hi;
+ if ($hi > 2**32) {
+ my @mu;
+ while ($lo < $hi) {
+ push @mu, moebius($lo++);
+ }
+ return @mu;
+ }
+ my @mu = map { 1 } $lo .. $hi;
+ $mu[0] = 0 if $lo == 0;
+ my($p, $sqrtn) = (2, int(sqrt($hi)+0.5));
+ while ($p <= $sqrtn) {
+ my $i = $p * $p;
+ $i = $i * int($lo/$i) + (($lo % $i) ? $i : 0) if $i < $lo;
+ while ($i <= $hi) {
+ $mu[$i-$lo] = 0;
+ $i += $p * $p;
+ }
+ $i = $p;
+ $i = $i * int($lo/$i) + (($lo % $i) ? $i : 0) if $i < $lo;
+ while ($i <= $hi) {
+ $mu[$i-$lo] *= -$p;
+ $i += $p;
+ }
+ $p = next_prime($p);
+ }
+ foreach my $i ($lo .. $hi) {
+ my $m = $mu[$i-$lo];
+ $m *= -1 if abs($m) != $i;
+ $mu[$i-$lo] = ($m>0) - ($m<0);
+ }
+ return @mu;
+}
+
#############################################################################
# Lehmer prime count
#
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-perl/packages/libmath-prime-util-perl.git
More information about the Pkg-perl-cvs-commits
mailing list