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