Skip to content

Commit

Permalink
fromdigits should allow unstringified bigints
Browse files Browse the repository at this point in the history
  • Loading branch information
danaj committed Aug 15, 2016
1 parent 5e56bd5 commit eea9ff9
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 14 deletions.
7 changes: 7 additions & 0 deletions Changes
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
Revision history for Perl module Math::Prime::Util

0.60 2016-?

[FUNCTIONALITY AND PERFORMANCE]

- fromdigits works with bigint first arg, no need to stringify.
Slightly faster for bigints, but slower than desired.

0.59 2016-08-03

[ADDED]
Expand Down
35 changes: 25 additions & 10 deletions XS.xs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,20 @@ typedef struct {

START_MY_CXT

static int _is_sv_bigint(pTHX_ SV* n)
{
if (sv_isobject(n)) {
const char *hvname = HvNAME_get(SvSTASH(SvRV(n)));
if (hvname != 0) {
if (strEQ(hvname, "Math::BigInt") || strEQ(hvname, "Math::BigFloat") ||
strEQ(hvname, "Math::GMPz") || strEQ(hvname, "Math::GMP") ||
strEQ(hvname, "Math::Pari") )
return 1;
}
}
return 0;
}

/* Is this a pedantically valid integer?
* Croaks if undefined or invalid.
* Returns 0 if it is an object or a string too large for a UV.
Expand All @@ -135,15 +149,8 @@ static int _validate_int(pTHX_ SV* n, int negok)
else croak("Parameter '%" SVf "' must be a positive integer", n);
}
if (sv_isobject(n)) {
const char *hvname = HvNAME_get(SvSTASH(SvRV(n)));
if (hvname == 0)
return 0;
if (strEQ(hvname, "Math::BigInt") || strEQ(hvname, "Math::BigFloat") ||
strEQ(hvname, "Math::GMPz") || strEQ(hvname, "Math::GMP") ||
strEQ(hvname, "Math::Pari") )
isbignum = 1;
else
return 0;
isbignum = _is_sv_bigint(aTHX_ n);
if (!isbignum) return 0;
}
/* Without being very careful, don't process magic variables here */
if (SvGAMAGIC(n) && !isbignum) return 0;
Expand Down Expand Up @@ -466,6 +473,7 @@ sieve_primes(IN UV low, IN UV high)
segment_primes = 3
segment_twin_primes = 4
_ramanujan_primes = 5
n_ramanujan_primes = 6
PREINIT:
AV* av;
PPCODE:
Expand Down Expand Up @@ -517,6 +525,13 @@ sieve_primes(IN UV low, IN UV high)
for (i = beg; i <= end; i++)
av_push(av,newSVuv(L[i]));
Safefree(L);
} else if (ix == 6) { /* Ramanujan primes */
UV i, *L;
L = n_range_ramanujan_primes(low, high);
if (L && high >= low)
for (i = low; i <= high; i++)
av_push(av,newSVuv(L[i-low]));
Safefree(L);
}
}
return; /* skip implicit PUTBACK */
Expand Down Expand Up @@ -1794,7 +1809,7 @@ void todigits(SV* svn, int base=10, int length=-1)
if (from_digit_string(&n, SvPV_nolen(svn), base)) {
XSRETURN_UV(n);
}
} else { /* array ref of digits */
} else if (!_is_sv_bigint(aTHX_ svn)) { /* array ref of digits */
UV* r = 0;
int len = arrayref_to_int_array(aTHX_ &r, (AV*) SvRV(svn), base);
if (from_digit_to_UV(&n, r, len, base)) {
Expand Down
37 changes: 33 additions & 4 deletions lib/Math/Prime/Util/PP.pm
Original file line number Diff line number Diff line change
Expand Up @@ -1977,6 +1977,26 @@ sub sum_primes {
$sum = BZERO->copy if ( (MPU_32BIT && $high > 323_380) ||
(MPU_64BIT && $high > 29_505_444_490) );

if (0 && $low <= 2) {
my $n = $high;
my $r = sqrtint($n);
my @V = map { int($n/$_) } 1 .. $r+1;
my $l = $V[-1];
push @V, map{ $l-$_ } 1 .. $l;
my %S = map { $_ => ($_*($_+1)>>1) - 1 } @V;
for my $p (2 .. $r) {
if ($S{$p} > $S{$p-1}) {
my $sp = $S{$p-1};
my $p2 = $p*$p;
for my $v (@V) {
last if $v < $p2;
$S{$v} -= $p * ($S{int($v/$p)} - $sp);
}
}
}
return $S{$n};
}

# It's very possible we're here because they've counted too high. Skip fwd.
if ($low <= 2 && $high >= 29505444491) {
$low = 29505444503;
Expand Down Expand Up @@ -2601,19 +2621,28 @@ sub todigitstring {
sub fromdigits {
my($r, $base) = @_;
$base = 10 unless defined $base;
return $r if $base == 10 && ref($r) =~ /^Math::/;
my $n = BZERO->copy;
$base = BZERO + $base;
if (ref($r)) {
if (ref($r) && ref($r) !~ /^Math::/) {
croak "fromdigits first argument must be a string or array reference"
unless ref($r) eq 'ARRAY';
for my $d (@$r) {
$n = $n * $base + $d;
}
} else {
$r =~ s/^0*//;
for my $d (map { $_mapdigit{$_} } split(//,$r)) {
croak "Invalid digit for base $base" unless defined $d && $d < $base;
$n = $n * $base + $d;
#for my $d (map { $_mapdigit{$_} } split(//,$r)) {
# croak "Invalid digit for base $base" unless defined $d && $d < $base;
# $n = $n * $base + $d;
#}
for my $c (split(//, lc($r))) {
$n->bmul($base);
if ($c ne '0') {
my $d = index("0123456789abcdefghijklmnopqrstuvwxyz", $c);
croak "Invalid digit for base $base" unless $d >= 0;
$n->badd($d);
}
}
}
$n = _bigint_to_int($n) if $n->bacmp(BMAX) <= 0;
Expand Down

0 comments on commit eea9ff9

Please sign in to comment.