[libmath-prime-util-perl] 15/16: Modify validation to lower overhead

Partha P. Mukherjee ppm-guest at moszumanska.debian.org
Thu May 21 18:46:52 UTC 2015


This is an automated email from the git hooks/post-receive script.

ppm-guest pushed a commit to annotated tag v0.19
in repository libmath-prime-util-perl.

commit a635e5ede543d3d84d734f3f463c21006705e9d3
Author: Dana Jacobsen <dana at acm.org>
Date:   Fri Feb 1 02:12:11 2013 -0800

    Modify validation to lower overhead
---
 Changes                        |  4 ++-
 MANIFEST                       |  2 ++
 examples/bench-mp-nextprime.pl | 28 ++++++++++++++++++++
 examples/bench-mp-psrp.pl      | 29 ++++++++++++++++++++
 lib/Math/Prime/Util.pm         | 60 ++++++++++++++++++++++++++++--------------
 lib/Math/Prime/Util/PP.pm      |  5 +++-
 6 files changed, 106 insertions(+), 22 deletions(-)

diff --git a/Changes b/Changes
index 4d678e8..6e062a8 100644
--- a/Changes
+++ b/Changes
@@ -1,6 +1,6 @@
 Revision history for Perl extension Math::Prime::Util.
 
-0.19  ?? January 2012
+0.19  ?? February 2012
 
     - Update MR bases with newest from http://miller-rabin.appspot.com/.
 
@@ -11,6 +11,8 @@ Revision history for Perl extension Math::Prime::Util.
 
     - Added a few tests to give better coverage.
 
+    - Adjust some validation tests to cut down on overhead.
+
 0.18  14 January 2012
 
     - Add random_strong_prime.
diff --git a/MANIFEST b/MANIFEST
index 2e04349..6f59e04 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -39,6 +39,8 @@ examples/bench-random-prime-bigint.pl
 examples/bench-pp-count.pl
 examples/bench-pp-isprime.pl
 examples/bench-pp-sieve.pl
+examples/bench-mp-nextprime.pl
+examples/bench-mp-psrp.pl
 examples/test-factor-yafu.pl
 examples/test-factor-mpxs.pl
 examples/test-nextprime-yafu.pl
diff --git a/examples/bench-mp-nextprime.pl b/examples/bench-mp-nextprime.pl
new file mode 100644
index 0000000..dca6555
--- /dev/null
+++ b/examples/bench-mp-nextprime.pl
@@ -0,0 +1,28 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Math::Prime::Util;
+use Math::Prime::Util::GMP;
+use Math::Primality;
+use Benchmark qw/:all/;
+my $count = shift || -2;
+srand(29);  # So we have repeatable results
+
+test_at_digits($_, 1000) for (5, 15, 25, 50, 200);
+
+sub test_at_digits {
+  my($digits, $numbers) = @_;
+  die "Digits must be > 0" unless $digits > 0;
+
+  my $start = Math::Prime::Util::random_ndigit_prime($digits) - 3;
+  my $end = $start;
+  $end = Math::Prime::Util::GMP::next_prime($end) for 1 .. $numbers;
+
+  print "next_prime x $numbers starting at $start\n";
+
+  cmpthese($count,{
+    'MP'       => sub { my $n = $start; $n = Math::Primality::next_prime($n) for 1..$numbers; die "MP ended with $n instead of $end" unless $n == $end; },
+    'MPU'      => sub { my $n = $start; $n = Math::Prime::Util::next_prime($n) for 1..$numbers; die "MPU ended with $n instead of $end" unless $n == $end; },
+    'MPU GMP'  => sub { my $n = $start; $n = Math::Prime::Util::GMP::next_prime($n) for 1..$numbers; die "MPU GMP ended with $n instead of $end" unless $n == $end; },
+  });
+}
diff --git a/examples/bench-mp-psrp.pl b/examples/bench-mp-psrp.pl
new file mode 100644
index 0000000..337d5ae
--- /dev/null
+++ b/examples/bench-mp-psrp.pl
@@ -0,0 +1,29 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Math::Prime::Util;
+use Math::Prime::Util::GMP;
+use Math::Primality;
+use Benchmark qw/:all/;
+use List::Util qw/min max/;
+my $count = shift || -2;
+srand(29);  # So we have repeatable results
+
+test_at_digits($_, 1000) for (5, 15, 25, 50, 200);
+
+sub test_at_digits {
+  my($digits, $numbers) = @_;
+  die "Digits must be > 0" unless $digits > 0;
+
+  # We get a mix of primes and non-primes.
+  my @nums = map { Math::Prime::Util::random_ndigit_prime($digits)+2 } 1 .. $numbers;
+  print "is_strong_pseudoprime for $numbers random $digits-digit numbers",
+        " (", min(@nums), " - ", max(@nums), ")\n";
+
+  cmpthese($count,{
+    'MP'      =>sub {Math::Primality::is_strong_pseudoprime($_,3) for @nums;},
+    'MPU'     =>sub {Math::Prime::Util::is_strong_pseudoprime($_,3) for @nums;},
+    'MPU PP'  =>sub {Math::Prime::Util::PP::miller_rabin($_,3) for @nums;},
+    'MPU GMP' =>sub {Math::Prime::Util::GMP::is_strong_pseudoprime($_,3) for @nums;},
+  });
+}
diff --git a/lib/Math/Prime/Util.pm b/lib/Math/Prime/Util.pm
index cc2a033..11aa71a 100644
--- a/lib/Math/Prime/Util.pm
+++ b/lib/Math/Prime/Util.pm
@@ -209,24 +209,36 @@ sub prime_set_config {
   1;
 }
 
+my $_bigint_small;
 sub _validate_positive_integer {
   my($n, $min, $max) = @_;
   croak "Parameter must be defined" if !defined $n;
-  croak "Parameter '$n' must be a positive integer"
-        if ref($n) ne 'Math::BigInt' && $n =~ tr/0123456789//c;
+  if (ref($n) eq 'Math::BigInt') {
+    croak "Parameter '$n' must be a positive integer" unless $n->sign() eq '+';
+  } else {
+    croak "Parameter '$n' must be a positive integer"
+          if $n eq '' || $n =~ tr/0123456789//c;
+  }
   croak "Parameter '$n' must be >= $min" if defined $min && $n < $min;
   croak "Parameter '$n' must be <= $max" if defined $max && $n > $max;
-  # The second term is used instead of '<=' to fix strings like ~0+delta.
-  # The third works around a rare BigInt bug (e.g. 23 > 18446744073709551615 !!)
-  if ($n < $_Config{'maxparam'} || int($n) eq $_Config{'maxparam'} || "$n" < $_Config{'maxparam'}) {
-    $_[0] = $_[0]->as_number() if ref($_[0]) eq 'Math::BigFloat';
-    $_[0] = int($_[0]->bstr) if ref($_[0]) eq 'Math::BigInt';
-  } elsif (ref($n) ne 'Math::BigInt') {
-    croak "Parameter '$n' outside of integer range" if !defined $bigint::VERSION;
-    $_[0] = Math::BigInt->new("$n"); # Make $n a proper bigint object
-    $_[0]->upgrade(undef) if $_[0]->upgrade();  # Stop BigFloat upgrade
+
+  if (ref($_[0])) {
+    $_[0] = Math::BigInt->new("$_[0]") unless ref($_[0]) eq 'Math::BigInt';
+    $_bigint_small = Math::BigInt->new("$_Config{'maxparam'}")
+                   unless defined $_bigint_small;
+    if ($_[0]->bacmp($_bigint_small) <= 0) {
+      $_[0] = int($_[0]->bstr);
+    } else {
+      $_[0]->upgrade(undef) if $_[0]->upgrade();  # Stop BigFloat upgrade
+    }
   } else {
-    $_[0]->upgrade(undef) if $_[0]->upgrade();  # Stop BigFloat upgrade
+    # The second term is used instead of '<=' to fix strings like ~0+delta.
+    if ( ! ($n < $_Config{'maxparam'} || int($n) eq $_Config{'maxparam'}) ) {
+      # We were handed a string representing a big number.
+      croak "Parameter '$n' outside of integer range" if !defined $bigint::VERSION;
+      $_[0] = Math::BigInt->new("$n"); # Make $n a proper bigint object
+      $_[0]->upgrade(undef) if $_[0]->upgrade();  # Stop BigFloat upgrade
+    }
   }
   # One of these will be true:
   #     1) $n <= max and $n is not a bigint
@@ -554,7 +566,7 @@ sub primes {
     }
 
     $low-- if $low == 2;  # Low of 2 becomes 1 for our program.
-    croak "Invalid _random_prime parameters" if ($low % 2) == 0 || ($high % 2) == 0;
+    confess "Invalid _random_prime parameters: $low, $high" if ($low % 2) == 0 || ($high % 2) == 0;
 
     # We're going to look at the odd numbers only.
     #my $range = $high - $low + 1;
@@ -1204,7 +1216,7 @@ sub is_prime {
   return 0 if defined $n && $n < 2;
   _validate_positive_integer($n);
 
-  return _XS_is_prime($n) if $n <= $_XS_MAXVAL;
+  return _XS_is_prime($n) if ref($n) ne 'Math::BigInt' && $n <= $_XS_MAXVAL;
   return Math::Prime::Util::GMP::is_prime($n) if $_HAVE_GMP;
   return is_prob_prime($n);
 }
@@ -1226,7 +1238,7 @@ sub next_prime {
   _validate_positive_integer($n);
 
   # If we have XS and n is either small or bigint is unknown, then use XS.
-  return _XS_next_prime($n) if $n <= $_XS_MAXVAL
+  return _XS_next_prime($n) if ref($n) ne 'Math::BigInt' && $n <= $_XS_MAXVAL
              && (!defined $bigint::VERSION || $n < $_Config{'maxprime'} );
 
   # Try to stick to the plan with respect to maximum return values.
@@ -1245,7 +1257,7 @@ sub prev_prime {
   my($n) = @_;
   _validate_positive_integer($n);
 
-  return _XS_prev_prime($n) if $n <= $_XS_MAXVAL;
+  return _XS_prev_prime($n) if ref($n) ne 'Math::BigInt' && $n <= $_XS_MAXVAL;
   if ($_HAVE_GMP) {
     # If $n is a bigint object, try to make the return value the same
     return (ref($n) eq 'Math::BigInt')
@@ -1302,7 +1314,7 @@ sub factor {
   my($n) = @_;
   _validate_positive_integer($n);
 
-  return _XS_factor($n) if $n <= $_XS_MAXVAL;
+  return _XS_factor($n) if ref($n) ne 'Math::BigInt' && $n <= $_XS_MAXVAL;
 
   if ($_HAVE_GMP) {
     my @factors = Math::Prime::Util::GMP::factor($n);
@@ -1319,7 +1331,7 @@ sub is_strong_pseudoprime {
   my($n) = shift;
   _validate_positive_integer($n);
   # validate bases?
-  return _XS_miller_rabin($n, @_) if $n <= $_XS_MAXVAL;
+  return _XS_miller_rabin($n, @_) if ref($n) ne 'Math::BigInt' && $n <= $_XS_MAXVAL;
   return Math::Prime::Util::GMP::is_strong_pseudoprime($n, @_) if $_HAVE_GMP;
   return Math::Prime::Util::PP::miller_rabin($n, @_);
 }
@@ -1365,7 +1377,7 @@ sub is_prob_prime {
   return 0 if defined $n && $n < 2;
   _validate_positive_integer($n);
 
-  return _XS_is_prob_prime($n) if $n <= $_XS_MAXVAL;
+  return _XS_is_prob_prime($n) if ref($n) ne 'Math::BigInt' && $n <= $_XS_MAXVAL;
   return Math::Prime::Util::GMP::is_prob_prime($n) if $_HAVE_GMP;
 
   return 2 if $n == 2 || $n == 3 || $n == 5 || $n == 7;
@@ -1407,7 +1419,7 @@ sub is_provable_prime {
   _validate_positive_integer($n);
 
   # Shortcut some of the calls.
-  return _XS_is_prime($n) if $n <= $_XS_MAXVAL;
+  return _XS_is_prime($n) if ref($n) ne 'Math::BigInt' && $n <= $_XS_MAXVAL;
   return Math::Prime::Util::GMP::is_provable_prime($n) if $_HAVE_GMP
                        && defined &Math::Prime::Util::GMP::is_provable_prime;
 
@@ -2903,6 +2915,14 @@ Print some primes above 64-bit range:
     # perl -MMath::Pari=:int,PARI,nextprime -E 'my $start = PARI "100000000000000000000"; my $end = $start+1000; my $p=nextprime($start); while ($p <= $end) { say $p; $p = nextprime($p+1); }'
 
 
+Project Euler, problem 10.  Solution in under 50 milliseconds:
+
+  use Math::Prime::Util qw/primes/;
+  my $sum = 0;
+  $sum += $_ for @{primes(2_000_000)};
+  say $sum;
+
+
 =head1 LIMITATIONS
 
 I have not completed testing all the functions near the word size limit
diff --git a/lib/Math/Prime/Util/PP.pm b/lib/Math/Prime/Util/PP.pm
index b17689c..b69b4b4 100644
--- a/lib/Math/Prime/Util/PP.pm
+++ b/lib/Math/Prime/Util/PP.pm
@@ -879,7 +879,10 @@ sub is_strong_lucas_pseudoprime {
   # It's now time to perform the Lucas pseudoprimality test using $D.
 
   if (ref($n) ne 'Math::BigInt') {
-    require Math::BigInt;
+    if (!defined $MATH::BigInt::VERSION) {
+      eval { require Math::BigInt;  Math::BigInt->import(try=>'GMP,Pari'); 1; }
+      or do { croak "Cannot load Math::BigInt "; }
+    }
     $n = Math::BigInt->new("$n");
   }
 

-- 
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