X-Git-Url: https://git.distorted.org.uk/~mdw/catacomb-perl/blobdiff_plain/f9952aec1cf6c64a5681308eea817b6113a37433..fcd15e0b7a3d0f0ca2f30953573f8d1f6b8e8bd2:/Catacomb::MP::Prime.pod diff --git a/Catacomb::MP::Prime.pod b/Catacomb::MP::Prime.pod new file mode 100644 index 0000000..8bd543b --- /dev/null +++ b/Catacomb::MP::Prime.pod @@ -0,0 +1,524 @@ +=head1 NAME + +Catacomb::MP::Prime - prime number generation + +=head1 SYNOPSIS + + use Catacomb qw(:const :mp :random :pgen); + + $p = newprime($nbits, [$rng]); + + $filt = Catacomb::MP::Prime::Filter->new($x); + $filt = $x->filter(); + $rc = $filt->status(); + $x = $filt->m(); + $rc = $filt->step($n); + $rc = $filt->jump($jfilt); + $newfilt = $filt->muladd($mul, $add); # integers + + $stepper = Catacomb::MP::Prime::Filter->stepper($step); # integer + $stepper = filterstepper($step); + $jumper = Catacomb::MP::Prime::Filter->stepper($jump); # MP + $jumper = filterjumper($jump); + + $rabin = Catacomb::MP::Prime::Rabin->new($m); + $rabin = $p->rabin(); + $m = $rabin->m(); + $rc = $rabin->test($wit); + $n = $rabin->iters(); + $n = Catacomb::MP::Prime::Rabin->ntests($bits); + $tester = Catacomb::MP::Prime::Rabin->tester(); + $tester = $rabintester; + + $events = Catacomb::MP::Prime::Gen::Proc->ev(); + $events = Catacomb::MP::Prime::Gen::Proc->evspin(); + $events = Catacomb::MP::Prime::Gen::Proc->subev(); + + $p = Catacomb::MP::Prime->gen + ($name, $x, $nsteps, $stepper, $ntests, $tester, [$events]); + $p = primegen + ($name, $x, $nsteps, $stepper, $ntests, $tester, [$events]); + if (($x, $j) = Catacomb::MP::Prime->strongprime_setup + ($name, $nbits, [$rng], [$nsteps], [$subevents])) { + $p = Catacomb::MP::Prime->gen + ($name, $x, $nsteps, $j, $ntests, $tester, [$events]); + } + ($p, @f) = Catacomb::MP::Prime->limlee + ($name, $qbits, $pbits, [$rng], [$on], [$oev], [$iev]); + ($p, @f) = limleegen + ($name, $qbits, $pbits, [$rng], [$on], [$oev], [$iev]); + + package MyPrimeGenObject; + sub new { ... }; + sub BEGIN { ... }; + sub TRY { ... }; + sub FAIL { ... }; + sub PASS { ... }; + sub DONE { ... }; + sub ABORT { ... }; + $name = $ev->name(); + $x = $ev->m([$xx]); + $rng = $ev->rand(); + +=head1 DESCRIPTION + +The B and related packages provide a framework for +generating prime numbers of various special forms. Read Catacomb::MP(3) +for more information about Catacomb's multiprecision integer support. + +=head2 Simple functions + +=over + +=item BIB<,> [I]B<)> + +Returns a random prime between 2^I and 2^(I+1) (unless +you're very unlucky). Here, I must be a random number generator +object: see Catacomb::Crypto(3). + +=back + +=head2 Result codes + +The following result codes are used by various of the prime-number +generation functions. They may be imported using the B<:consts> tag. + +=over + +=item B + +About to test a new candidate prime discovered by filtering. This +shouldn't be returned by functions, but it is used as part of the +event-handling system. + +=item B + +A new candidate has been found by a filter, but has not yet passed a +primality test. + +=item B + +The number is definitely composite. + +=item B + +The number has passed at least one primality test, and is therefore +likely to be prime. + +=item B + +The number is definitely prime. + +=item B + +The search for a prime number has been aborted. + +=back + +=head2 Filtering for small primes + +The class B implements a relatively +efficient technique for finding numbers with no small factors. + +=over + +=item IB<-Esmallfactor()> + +Returns B if I is definitely prime (and therefore fairly +small), B if it has some small factor, or B if it +has no small factors but might be composite anyway. + +=item Bnew(>IB<)> + +=item IB<-Efilter()> + +Returns a new small-primes filter, initially looking at the number +I. + +=item IB<-Em()> + +Returns the current number I in the filter. + +=item IB<-Estatus()> + +Returns the state (a B code) for the current number I in the +filter. This will usually be B if I has a small factor, +or B if not; though B is also possible if I is +small enough. + +=item IB<-Estep(>IB<)> + +Advances the number in the filter by a small step I, returning +the new status. + +=item IB<-Ejump(>IB<)> + +Advances the number in the filter by a large jump, as represented by +another filter I. This is useful for finding a prime which has +the form I I + I for some other prime I -- store 2 I in +a filter and use it as a jump to search for another prime. + +=item IB<-Emuladd(>IB<,> IB<)> + +Returns a new filter whose number is I I + I. + +=back + +=head2 Miller-Rabin primality tests + +Catacomb's main primality test is the Miller-Rabin test. It is a +probabilistic test, with a 1/4 probability that any particular test will +fail to recognize a given number as being composite. However, it is +important to observe that this is not the same as the probability that a +random number is composite given that it passed a test -- this latter is +much smaller. + +=over + +=item Bnew(>IB<)> + +=item IB<-Erabin()> + +Constructs a new Rabin-Miller testing context for the purpose of testing +the candidate I. Returns B if I is negative or even. + +=item IB<-Em()> + +Returns the integer under test in the given context. + +=item IB<-Etest(>IB<)> + +Tests I with witness I. Returns B or B. +The witness should usually be chosen randomly. + +=item IB<-Eiters()> + +Returns the recommended number of tests for the candidate under test, +assuming that we came across the candidate at random. + +=item Bntests(>IB<)> + +Returns the recommended number of tests for a candidate which is +I bits long, assuming that we came across the candidate at +random. + +=back + +=head2 Prime generation concepts + +The prime generator is based on the concepts of I, I +and I. + +=over + +=item * + +Steppers are responsible for producing candidate primes, and (usually) +testing them for small factors (e.g., using B). A +stepper is expected to be relatively fast, and leave the heavy lifting +to testers. + +=item * + +Testers are responsible for applying primality tests to candidates. + +=item * + +Event handlers report interesting events during prime generation, to +maintain a progress display or similar. + +=back + +These various kinds of objects all have essentially the same interface, +which is built from I. An event contains all the useful +information about the current progress of a prime generation task. +Events are objects of type B, though +fortunately one rarely needs to type this. Event objects can't be +created by Perl code. + +=over + +=item IB<-Ename()> + +Returns the name of the prime being generated. + +=item IB<-Em(>[I]B<)> + +Returns the current candidate prime. If I is supplied then it +sets a new candidate prime. This is used by the stepper interface. + +=item IB<-Esteps()> + +Returns the number of steps remaining before the generation task fails. + +=item IB<-Etests()> + +Returns the name of tests remaining before the generation task succeeds. + +=item IB<-Erand()> + +Returns a pseudorandom number generator which is likely to be fast but +not cryptographically strong. + +=back + +=head2 Built-in steppers and testers + +=over + +=item Bstepper(>IB<)> + +=item BIB<)> + +Returns a stepper object which steps on by I (a small Perl +integer) until it finds a number with no small prime factors. + +=item Bjumper(>IB<)> + +=item IB<-Ejumper()> + +Returns a jumper object which jumps on by I (a large +multiprecision integer) until it finds a number with no small prime +factors. + +=item Btester()> + +Returns a tester object which applies an iteration of the Miller-Rabin +probabilistic primality test. In fact, there only needs to be one of +these, so it's called B<$rabintester>. + +=item Bev()> + +A standard event handler which prints the name of the prime being +generated and a string of C<.> and C<+> signs for failures and +successes, such as is relatively commonplace. In fact, there only needs +to be one of these, so it's called B<$pg_events>. + +=item Bevspin()> + +An event handler which shows a spinning baton while it works. This is +mainly used as the subsidiary event handler when generating Lim-Lee +primes. There only needs to be one of these, so it's called +B<$pg_evspin>. + +=item Bsubev()> + +An event handler which works like B above, but puts square +brackets around its output. Also suitable for use in Lim-Lee +generation. There only needs to be one of these, so it's called +B<$pg_subev>. + +=back + +=head2 Prime generation + +Prime generation is driven by the procedure +Bgen()>, which is also named B for +convenience. + +=over + +=item Bgen(>IB<,> IB<,> IB<,> IB<,> IB<,> IB<,> [I]B<)> + +=item BIB<,> IB<,> IB<,> IB<,> IB<,> IB<,> [I]B<)> + +Generates a prime number. It will start with the integer I, and run +the I for at most I times (infinitely if I is +zero), returning B if it fails. Each time the stepper reports a +plausible candidate, it runs the tester up to I times; it +returns the candidate if all these tests succeeded. It will call the +event handler after each step and test. + +=back + +=head2 Writing your own steppers, testers and event handlers + +As mentioned, steppers, testers and event handlers have similar +interfaces. Each is represented by an object whose class is a +descendent (via B<@ISA>) of B. The +prime generator will call methods on your object as required. + +Event handlers are the easiest, so we deal with them first. All methods +are called with one argument which is the event object. The methods +called are as follows. + +=over + +=item B + +Prime generation has begun. + +=item B + +About to start testing a new number. + +=item B + +Number passed a primality test. + +=item B + +Number failed a primality test. + +=item B + +Prime number found successfully. + +=item B + +No prime number found; we gave up. + +=back + +Any of these methods may return B (-1) to abandon prime +searching. This is intended to be used, for example, by a progress +dialogue box if its cancel button is pressed. Any other value is +ignored. + +Here's a simple example of an event handler. + + package Strange::Events; + @ISA = qw(Catacomb::MP::Prime::Gen::Proc); + + sub new { bless {}, $_[0]; } + + sub PG_BEGIN { + my ($me, $ev) = @_; + local $| = 1; + print $ev->name(), " ["; + } + + sub PG_PASS { local $| = 1; print "*"; } + sub PG_FAIL { local $| = 1; print "."; } + sub PG_ABORT { local $| = 1; print "] :-(\n"; } + sub PG_DONE { local $| = 1; print "]\n"; } + +Steppers use fewer methods, but are a bit more involved. + +=over + +=item B + +Initialize the stepper. Read the starting point from the event, or +store a new starting point, as appropriate. Return B if the +starting point is satisfactory, B for failure, or B +to run the tester. + +=item B + +Advance the stepper. Write the new number to the event. Return as for +B. + +=item B + +All is over. Tidy up. + +=back + +Testers are similar. + +=over + +=item B + +Initialize the tester. Read the starting point from the event. Return +B if the starting point is satisfactory, B if +you've actually run a successful test, B to reject the +candidate, or B to proceed to testing. + +=item B + +Run a test. Return B if the number is satisfactory, +B if it passed a test, or B if it failed. + +=item B + +Testing is done. Tidy up. + +=back + +Finally, any method can return B to stop prime generation in +its tracks almost immediately. (The B methods are still called +for the stepper, and for the tester if it's active.) + +It's probably best to do tidying in B rather than leaving it +until B, since testers and steppers and things may get left +around for a while, espectially if they're simple. + +Since steppers and testers often work together, here's an example of a +pair for generating I. A prime number I is +Sophie-Germain prime if 2 I + 1 is also prime. We work by +maintaining separate filters and testing contexts for I and 2 I + +1, and checking both in parallel. Generating Sophie Germain primes is a +long job. + + package SophieGermain::Stepper; + use Catacomb qw(:const :mp :pgen); + @ISA = qw(Catacomb::MP::Prime::Gen::Proc); + + sub new { bless [undef, undef], $_[0]; } + + sub _step { + my ($me, $ev) = @_; + while ($me->[0]->status() == PGEN_FAIL || + $me->[1]->status() == PGEN_FAIL) { + $me->[0]->step(2); + $me->[1]->step(4); + } + $ev->m($me->[0]->m()); + return PGEN_TRY; + } + + sub PG_BEGIN { + my ($me, $ev) = @_; + my $x = $ev->m(); + $me->[0] = $x->filter(); + $me->[1] = (2*$x + 1)->filter(); + _step($me, $ev); + } + + sub PG_TRY { + my ($me, $ev) = @_; + $me->[0]->step(2); + $me->[1]->step(4); + _step($me, $ev); + } + + sub PG_DONE { + my ($me, $ev) = @_; + $me->[0] = $me->[1] = undef; + } + + package SophieGermain::Tester; + use Catacomb qw(:const :mp :pgen); + @ISA = qw(Catacomb::MP::Prime::Gen::Proc); + + sub new { bless [undef, undef], $_[0]; } + + sub PG_BEGIN { + my ($me, $ev) = @_; + my $x = $ev->m(); + $me->[0] = $x->rabin(); + $me->[1] = (2*$x + 1)->rabin(); + return PGEN_TRY; + } + + sub _test { + my ($r, $rabin) = @_; + my $w = $r->mprange($rabin->m()); + return $rabin->test($w) == PGEN_PASS; + } + + sub PG_TRY { + my ($me, $ev) = @_; + my $r = $ev->rand(); + my $w = $r->mprange($me->[0]->m()); + return _test($r, $me->[0]) && _test($r, $me->[1]) ? + PGEN_PASS : PGEN_FAIL; + } + + sub PG_DONE { + my ($me, $ev) = @_; + $me->[0] = $me->[1] = undef; + } +