Pollard's rho algorithm for computing discrete logs.
[u/mdw/catacomb] / pgen.c
CommitLineData
0f5ec153 1/* -*-c-*-
2 *
8b021c3f 3 * $Id: pgen.c,v 1.5 2000/06/17 11:52:36 mdw Exp $
0f5ec153 4 *
581c854e 5 * Prime generation glue
0f5ec153 6 *
7 * (c) 1999 Straylight/Edgeware
8 */
9
10/*----- Licensing notice --------------------------------------------------*
11 *
12 * This file is part of Catacomb.
13 *
14 * Catacomb is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU Library General Public License as
16 * published by the Free Software Foundation; either version 2 of the
17 * License, or (at your option) any later version.
18 *
19 * Catacomb is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Library General Public License for more details.
23 *
24 * You should have received a copy of the GNU Library General Public
25 * License along with Catacomb; if not, write to the Free
26 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
27 * MA 02111-1307, USA.
28 */
29
30/*----- Revision history --------------------------------------------------*
31 *
32 * $Log: pgen.c,v $
8b021c3f 33 * Revision 1.5 2000/06/17 11:52:36 mdw
34 * Signal a pgen abort if the jump and base share a common factor.
35 *
581c854e 36 * Revision 1.4 1999/12/22 16:01:11 mdw
37 * Same file, completely different code. Main interface for new prime-
38 * search system.
0f5ec153 39 *
40 */
41
42/*----- Header files ------------------------------------------------------*/
43
581c854e 44#include <assert.h>
45#include <stdio.h>
46#include <stdlib.h>
47#include <string.h>
48
49#include "fibrand.h"
50#include "grand.h"
0f5ec153 51#include "mp.h"
581c854e 52#include "mprand.h"
0f5ec153 53#include "pgen.h"
581c854e 54#include "pfilt.h"
0f5ec153 55#include "rabin.h"
56
581c854e 57/*----- Standard prime filter ---------------------------------------------*/
0f5ec153 58
581c854e 59/* --- @pgen_filter@ --- */
0f5ec153 60
581c854e 61int pgen_filter(int rq, pgen_event *ev, void *p)
0f5ec153 62{
581c854e 63 pgen_filterctx *f = p;
64 int rc = PGEN_ABORT;
65
66 switch (rq) {
67 case PGEN_BEGIN:
68 rc = pfilt_create(&f->f, ev->m);
69 mp_drop(ev->m);
70 break;
71 case PGEN_TRY:
72 mp_drop(ev->m);
73 if (!((f->step | f->f.m->v[0]) & 1))
74 rc = pfilt_step(&f->f, 1);
0f5ec153 75 else
581c854e 76 rc = pfilt_step(&f->f, f->step);
77 break;
78 case PGEN_DONE:
79 pfilt_destroy(&f->f);
80 return (PGEN_DONE);
0f5ec153 81 }
581c854e 82
83 while (rc == PGEN_FAIL)
84 rc = pfilt_step(&f->f, f->step);
85 ev->m = MP_COPY(f->f.m);
0f5ec153 86 return (rc);
87}
88
581c854e 89/* --- @pgen_jump@ --- *
0f5ec153 90 *
581c854e 91 * Similar to the standard @pgen_filter@, but jumps in large steps rather
92 * than small ones.
0f5ec153 93 */
94
581c854e 95int pgen_jump(int rq, pgen_event *ev, void *p)
0f5ec153 96{
581c854e 97 pgen_jumpctx *f = p;
98 int rc = PGEN_ABORT;
0f5ec153 99
581c854e 100 switch (rq) {
8b021c3f 101 case PGEN_BEGIN: {
102 mp *g = MP_NEW;
103 mp_gcd(&g, 0, 0, ev->m, f->j->m);
104 if (MP_CMP(g, >, MP_ONE)) {
105 mp_drop(g);
106 return (PGEN_ABORT);
107 }
108 mp_drop(g);
581c854e 109 rc = pfilt_create(&f->f, ev->m);
110 mp_drop(ev->m);
8b021c3f 111 } break;
581c854e 112 case PGEN_TRY:
113 mp_drop(ev->m);
114 rc = pfilt_jump(&f->f, f->j);
115 break;
116 case PGEN_DONE:
117 pfilt_destroy(&f->f);
118 return (PGEN_DONE);
119 }
120
121 while (rc == PGEN_FAIL)
122 rc = pfilt_jump(&f->f, f->j);
123 ev->m = MP_COPY(f->f.m);
124 return (rc);
125}
0f5ec153 126
581c854e 127/*----- Standard prime test -----------------------------------------------*/
0f5ec153 128
581c854e 129/* --- @pgen_test@ --- */
0f5ec153 130
581c854e 131int pgen_test(int rq, pgen_event *ev, void *p)
132{
133 rabin *r = p;
134 int rc = PGEN_ABORT;
0f5ec153 135
581c854e 136 switch (rq) {
137 case PGEN_BEGIN:
138 rabin_create(r, ev->m);
139 rc = PGEN_TRY;
140 break;
141 case PGEN_TRY: {
142 mp *a = mprand_range(MP_NEW, ev->m, ev->r, 0);
143 rc = rabin_test(r, a);
144 mp_drop(a);
145 } break;
146 case PGEN_DONE:
147 rabin_destroy(r);
148 rc = PGEN_DONE;
149 break;
0f5ec153 150 }
151
0f5ec153 152 return (rc);
153}
154
581c854e 155/*----- The main driver ---------------------------------------------------*/
156
157/* --- @pgen@ --- *
bd98b2df 158 *
581c854e 159 * Arguments: @const char *name@ = name of the value being searched for
160 * @mp *d@ = destination for the result integer
161 * @mp *m@ = start value to pass to stepper
162 * @pgen_proc *event@ = event handler function
163 * @void *ectx@ = context argument for event andler
164 * @unsigned steps@ = number of steps to take in search
165 * @pgen_proc *step@ = stepper function to use
166 * @void *sctx@ = context argument for stepper
167 * @unsigned tests@ = number of tests to make
168 * @pgen_proc *test@ = tester function to use
169 * @void *tctx@ = context argument for tester
bd98b2df 170 *
581c854e 171 * Returns: Pointer to final result, or null.
bd98b2df 172 *
581c854e 173 * Use: A generalized prime-number search skeleton. Yes, that's a
174 * scary number of arguments.
bd98b2df 175 */
176
581c854e 177mp *pgen(const char *name, mp *d, mp *m, pgen_proc *event, void *ectx,
178 unsigned steps, pgen_proc *step, void *sctx,
179 unsigned tests, pgen_proc *test, void *tctx)
bd98b2df 180{
581c854e 181 pgen_event ev;
182 int rq, rc;
183 pgen_proc *proc;
184 void *ctx;
bd98b2df 185
581c854e 186 /* --- Set up the initial event block --- */
bd98b2df 187
581c854e 188 ev.name = name;
189 if (m)
190 ev.m = MP_COPY(m);
191 else
192 ev.m = 0;
193 ev.steps = steps;
194 ev.tests = tests;
195 ev.r = fibrand_create(0);
bd98b2df 196
581c854e 197 /* --- Tell the event handler we're under way --- */
bd98b2df 198
581c854e 199 if (event && event(PGEN_BEGIN, &ev, ectx) == PGEN_ABORT)
200 return (0);
bd98b2df 201
581c854e 202 /* --- Set up for the initial call --- */
bd98b2df 203
581c854e 204 proc = step; ctx = sctx; rq = PGEN_BEGIN;
0f5ec153 205
581c854e 206 /* --- Enter the great maelstrom of state transitions --- */
0f5ec153 207
581c854e 208 for (;;) {
209 unsigned act = 0;
210
211 enum {
212 A_STEP = 1u,
213 A_TEST = 2u,
214 A_EVENT = 4u,
215 A_ENDTEST = 8u,
216 A_ENDSTEP = 16u,
217 A_DONE = 32u
218 };
219
220 /* --- Call the procedure and decide what to do next --- */
221
222 rc = proc(rq, &ev, ctx);
223 switch (rc) {
224 case PGEN_TRY:
225 if (proc == test)
226 rq = PGEN_TRY;
227 else {
228 act |= A_EVENT;
229 proc = test; ctx = tctx;
230 rq = PGEN_BEGIN;
231 }
232 break;
233 case PGEN_PASS:
234 act |= A_TEST | A_EVENT;
235 if (proc == test)
236 rq = PGEN_TRY;
237 else {
238 proc = test; ctx = tctx;
239 rq = PGEN_BEGIN;
240 }
241 break;
242 case PGEN_FAIL:
243 act |= A_STEP;
244 if (proc == test) {
245 act |= A_ENDTEST | A_EVENT;
246 proc = step; ctx = sctx;
247 }
248 rq = PGEN_TRY;
249 break;
250 case PGEN_DONE:
251 act |= A_EVENT | A_DONE | A_ENDSTEP;
252 if (proc == test)
253 act |= A_ENDTEST;
254 break;
255 case PGEN_ABORT:
256 act |= A_EVENT | A_DONE;
257 if (proc == test || rq == PGEN_TRY)
258 act |= A_ENDSTEP;
259 if (proc == test && rq == PGEN_BEGIN)
260 act |= A_ENDTEST;
261 break;
262 default:
263 assert(((void)"Invalid response from function", 0));
264 break;
265 }
0f5ec153 266
581c854e 267 /* --- If decrementing counters is requested, do that --- */
0f5ec153 268
581c854e 269 if ((act & A_STEP) && steps) {
270 ev.steps--;
271 if (!ev.steps) {
272 act |= A_EVENT | A_ENDSTEP | A_DONE;
273 rc = PGEN_ABORT;
274 }
275 ev.tests = tests;
276 }
0f5ec153 277
581c854e 278 if ((act & A_TEST) && tests) {
279 ev.tests--;
280 if (!ev.tests) {
281 act |= A_ENDTEST | A_ENDSTEP | A_DONE;
282 rc = PGEN_DONE;
283 }
0f5ec153 284 }
0f5ec153 285
581c854e 286 /* --- Report an event if so directed --- */
bd98b2df 287
581c854e 288 if ((act & A_EVENT) && event && event(rc, &ev, ectx) == PGEN_ABORT) {
289 rc = PGEN_ABORT;
290 if (!(act & A_DONE)) {
291 act |= A_ENDSTEP | A_DONE;
292 if (proc == test)
293 act |= A_ENDTEST;
294 }
295 }
bd98b2df 296
581c854e 297 /* --- Close down tester and stepper functions --- */
0f5ec153 298
581c854e 299 if (act & A_ENDTEST)
300 test(PGEN_DONE, &ev, tctx);
301 if (act & A_ENDSTEP)
302 step(PGEN_DONE, &ev, sctx);
303
304 /* --- Stop the entire test if necessary --- */
305
306 if (act & A_DONE)
307 break;
308 }
309
310 /* --- Tidy up and return --- */
311
312 if (rc == PGEN_ABORT) {
313 mp_drop(ev.m);
314 ev.m = 0;
315 }
316 ev.r->ops->destroy(ev.r);
317 if (d != MP_NEW)
318 mp_drop(d);
319
320 return (ev.m);
0f5ec153 321}
322
581c854e 323/*----- Test rig ----------------------------------------------------------*/
0f5ec153 324
325#ifdef TEST_RIG
326
327#include <mLib/testrig.h>
328
329static int verify(dstr *v)
330{
331 mp *m = *(mp **)v[0].buf;
581c854e 332 mp *q = *(mp **)v[1].buf;
333 mp *p;
0f5ec153 334 int ok = 1;
335
581c854e 336 pgen_filterctx pf;
337 rabin r;
0f5ec153 338
581c854e 339 pf.step = 2;
340 p = pgen("p", MP_NEW, m, pgen_evspin, 0, 0, pgen_filter, &pf,
341 rabin_iters(mp_bits(m)), pgen_test, &r);
342 if (!p || MP_CMP(p, !=, q)) {
343 fputs("\n*** pgen failed", stderr);
0f5ec153 344 fputs("\nm = ", stderr); mp_writefile(m, stderr, 10);
581c854e 345 fputs("\np = ", stderr); mp_writefile(p, stderr, 10);
346 fputs("\nq = ", stderr); mp_writefile(q, stderr, 10);
0f5ec153 347 fputc('\n', stderr);
348 ok = 0;
349 }
350
351 mp_drop(m);
581c854e 352 mp_drop(q);
353 if (p)
354 mp_drop(p);
d94f85ac 355 assert(mparena_count(MPARENA_GLOBAL) == 0);
0f5ec153 356 return (ok);
357}
358
359static test_chunk tests[] = {
360 { "pgen", verify, { &type_mp, &type_mp, 0 } },
361 { 0, 0, { 0 } }
362};
363
364int main(int argc, char *argv[])
365{
366 sub_init();
367 test_run(argc, argv, tests, SRCDIR "/tests/pgen");
368 return (0);
369}
0f5ec153 370#endif
371
372/*----- That's all, folks -------------------------------------------------*/