chiark / gitweb /
b20965a90550d69a7d8c2fb89b93ee5856a550f6
[catacomb] / math / limlee.c
1 /* -*-c-*-
2  *
3  * Generate Lim-Lee primes
4  *
5  * (c) 2000 Straylight/Edgeware
6  */
7
8 /*----- Licensing notice --------------------------------------------------*
9  *
10  * This file is part of Catacomb.
11  *
12  * Catacomb is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU Library General Public License as
14  * published by the Free Software Foundation; either version 2 of the
15  * License, or (at your option) any later version.
16  *
17  * Catacomb is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU Library General Public License for more details.
21  *
22  * You should have received a copy of the GNU Library General Public
23  * License along with Catacomb; if not, write to the Free
24  * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
25  * MA 02111-1307, USA.
26  */
27
28 /*----- Header files ------------------------------------------------------*/
29
30 #include <mLib/alloc.h>
31 #include <mLib/dstr.h>
32
33 #include "limlee.h"
34 #include "mpmul.h"
35 #include "mprand.h"
36 #include "pgen.h"
37 #include "rabin.h"
38
39 /*----- Stepping through combinations -------------------------------------*/
40
41 /* --- @comb_init@ --- *
42  *
43  * Arguments:   @octet *c@ = pointer to byte-flag array
44  *              @unsigned n@ = number of items in the array
45  *              @unsigned r@ = number of desired items
46  *
47  * Returns:     ---
48  *
49  * Use:         Initializes a byte-flag array which, under the control of
50  *              @comb_next@, will step through all combinations of @r@ chosen
51  *              elements.
52  */
53
54 static void comb_init(octet *c, unsigned n, unsigned r)
55 {
56   memset(c, 0, n - r);
57   memset(c + (n - r), 1, r);
58 }
59
60 /* --- @comb_next@ --- *
61  *
62  * Arguments:   @octet *c@ = pointer to byte-flag array
63  *              @unsigned n@ = number of items in the array
64  *              @unsigned r@ = number of desired items
65  *
66  * Returns:     Nonzero if another combination was returned, zero if we've
67  *              reached the end.
68  *
69  * Use:         Steps on to the next combination in sequence.
70  */
71
72 static int comb_next(octet *c, unsigned n, unsigned r)
73 {
74   unsigned g = 0;
75
76   /* --- How the algorithm works --- *
77    *
78    * Set bits start at the end and work their way towards the start.
79    * Excepting bits already at the start, we scan for the lowest set bit, and
80    * move it one place nearer the start.  A group of bits at the start are
81    * counted and reset just below the `moved' bit.  If there is no moved bit
82    * then we're done.
83    */
84
85   /* --- Count the group at the start --- */
86
87   for (; *c; c++) {
88     g++;
89     *c = 0;
90   }
91   if (g == r)
92     return (0);
93
94   /* --- Move the next bit down one --- *
95    *
96    * There must be one, because otherwise we'd have counted %$r$% bits
97    * earlier.
98    */
99
100   for (; !*c; c++)
101     ;
102   *c = 0;
103   g++;
104   for (; g; g--)
105     *--c = 1;
106   return (1);
107 }
108
109 /*----- Default prime generator -------------------------------------------*/
110
111 static void llgen(limlee_factor *f, unsigned pl, limlee_stepctx *l)
112 {
113   pgen_filterctx pf;
114   rabin r;
115   mp *p;
116
117 again:
118   p = mprand(l->newp, pl, l->r, 1);
119   pf.step = 2;
120   p = pgen(l->d.buf, p, p, l->iev, l->iec, 0, pgen_filter, &pf,
121            rabin_iters(pl), pgen_test, &r);
122   if (!p)
123     goto again;
124   f->p = p;
125 }
126
127 static void llfree(limlee_factor *f, limlee_stepctx *l)
128 {
129   mp_drop(f->p);
130 }
131
132 static const limlee_primeops primeops_simple = { llgen, llfree };
133
134 /*----- Lim-Lee stepper ---------------------------------------------------*/
135
136 /* --- @init@ --- *
137  *
138  * Arguments:   @pgen_event *ev@ = pointer to event block
139  *              @limlee_stepctx *l@ = pointer to Lim-Lee context
140  *
141  * Returns:     A @PGEN@ result code.
142  *
143  * Use:         Initializes the stepper.
144  */
145
146 static int init(pgen_event *ev, limlee_stepctx *l)
147 {
148   size_t i;
149   unsigned qql;
150
151   /* --- First of all, decide on a number of factors to make --- */
152
153   l->nf = l->pl / l->ql;
154   qql = l->pl % l->ql;
155   if (!l->nf)
156     return (PGEN_ABORT);
157   else if (qql && l->nf > 1) {
158     l->nf--;
159     qql += l->ql;
160   }
161
162   /* --- Now decide on how many primes I'll actually generate --- *
163    *
164    * The formula %$m = \max(3 n + 5, 25)$% comes from GPG's prime generation
165    * library.
166    */
167
168   l->poolsz = l->nf * 3 + 5;
169   if (l->poolsz < 25)
170     l->poolsz = 25;
171
172   /* --- Allocate and initialize the various tables --- */
173
174   l->c = xmalloc(l->poolsz);
175   l->v = xmalloc(l->poolsz * sizeof(limlee_factor));
176   comb_init(l->c, l->poolsz, l->nf);
177   for (i = 0; i < l->poolsz; i++)
178     l->v[i].p = 0;
179
180   /* --- Other bits of initialization --- */
181
182   l->seq = 0;
183   dstr_create(&l->d);
184   if (!l->pops) {
185     l->pops = &primeops_simple;
186     l->pc = 0;
187   }
188
189   /* --- Find a big prime --- */
190
191   if (!qql)
192     l->qq.p = 0;
193   else {
194     dstr_putf(&l->d, "%s*", ev->name);
195     l->pops->pgen(&l->qq, qql, l);
196   }
197
198   return (PGEN_TRY);
199 }
200
201 /* --- @next@ --- *
202  *
203  * Arguments:   @int rq@ = request which triggered this call
204  *              @pgen_event *ev@ = pointer to event block
205  *              @limlee_stepctx *l@ = pointer to Lim-Lee context
206  *
207  * Returns:     A @PGEN@ result code.
208  *
209  * Use:         Initializes the stepper.
210  */
211
212 static int next(int rq, pgen_event *ev, limlee_stepctx *l)
213 {
214   mp *p;
215   int rc;
216
217   mp_drop(ev->m);
218
219   for (;;) {
220     size_t i;
221     mpmul mm = MPMUL_INIT;
222
223     /* --- Step on to next combination --- */
224
225     if (rq == PGEN_TRY && !comb_next(l->c, l->poolsz, l->nf)) {
226       for (i = 0; i < l->poolsz; i++) {
227         l->pops->pfree(&l->v[i], l);
228         l->v[i].p = 0;
229       }
230     }
231     rq = PGEN_TRY; /* For next time through */
232
233     /* --- Gather up some factors --- */
234
235     if (l->qq.p)
236       mpmul_add(&mm, l->qq.p);
237     for (i = 0; i < l->poolsz; i++) {
238       if (!l->c[i])
239         continue;
240       if (!l->v[i].p) {
241         DRESET(&l->d);
242         dstr_putf(&l->d, "%s_%lu", ev->name, l->seq++);
243         l->pops->pgen(&l->v[i], l->ql, l);
244       }
245       mpmul_add(&mm, l->v[i].p);
246     }
247
248     /* --- Check it for small factors --- */
249
250     p = mpmul_done(&mm);
251     p = mp_lsl(p, p, 1);
252     p->v[0] |= 1;
253     if ((rc = pfilt_smallfactor(p)) != PGEN_FAIL)
254       break;
255     mp_drop(p);
256   }
257
258   ev->m = p;
259   return (rc);
260 }
261
262 /* --- @done@ --- *
263  *
264  * Arguments:   @pgen_event *ev@ = pointer to event block
265  *              @limlee_stepctx *l@ = pointer to Lim-Lee context
266  *
267  * Returns:     A @PGEN@ result code.
268  *
269  * Use:         Finalizes the stepper.  The output values in the context
270  *              take on their final results; other resources are discarded.
271  */
272
273 static int done(pgen_event *ev, limlee_stepctx *l)
274 {
275   size_t i, j;
276   limlee_factor *v;
277
278   /* --- If an output vector of factors is wanted, produce one --- */
279
280   if (!(l->f & LIMLEE_KEEPFACTORS))
281     v = 0;
282   else {
283     if (l->qq.p)
284       l->nf++;
285     v = xmalloc(l->nf * sizeof(limlee_factor));
286   }
287
288   for (i = 0, j = 0; i < l->poolsz; i++) {
289     if (v && l->c[i])
290       v[j++] = l->v[i];
291     else if (l->v[i].p)
292       l->pops->pfree(&l->v[i], l);
293   }
294
295   if (l->qq.p) {
296     if (v)
297       v[j++] = l->qq;
298     else
299       l->pops->pfree(&l->qq, l);
300   }
301
302   xfree(l->v);
303   l->v = v;
304
305   /* --- Free other resources --- */
306
307   xfree(l->c);
308   dstr_destroy(&l->d);
309
310   /* --- Done --- */
311
312   return (PGEN_DONE);
313 }
314
315 /* --- @limlee_step@ --- */
316
317 int limlee_step(int rq, pgen_event *ev, void *p)
318 {
319   limlee_stepctx *l = p;
320   int rc;
321
322   switch (rq) {
323     case PGEN_BEGIN:
324       if ((rc = init(ev, l)) != PGEN_TRY)
325         return (rc);
326     case PGEN_TRY:
327       return (next(rq, ev, l));
328     case PGEN_DONE:
329       return (done(ev, l));
330   }
331   return (PGEN_ABORT);
332 }
333
334 /*----- Main code ---------------------------------------------------------*/
335
336 /* --- @limlee@ --- *
337  *
338  * Arguments:   @const char *name@ = pointer to name root
339  *              @mp *d@ = pointer to destination integer
340  *              @mp *newp@ = how to generate factor primes
341  *              @unsigned ql@ = size of individual factors
342  *              @unsigned pl@ = size of large prime
343  *              @grand *r@ = a random number source
344  *              @unsigned on@ = number of outer attempts to make
345  *              @pgen_proc *oev@ = outer event handler function
346  *              @void *oec@ = argument for the outer event handler
347  *              @pgen_proc *iev@ = inner event handler function
348  *              @void *iec@ = argument for the inner event handler
349  *              @size_t *nf@, @mp ***f@ = output array for factors
350  *
351  * Returns:     A Lim-Lee prime, or null if generation failed.
352  *
353  * Use:         Generates Lim-Lee primes.  A Lim-Lee prime %$p$% is one which
354  *              satisfies %$p = 2 \prod_i q_i + 1$%, where all of the %$q_i$%
355  *              are large enough to resist square-root discrete log
356  *              algorithms.
357  *
358  *              If we succeed, and @f@ is non-null, we write the array of
359  *              factors chosen to @f@ for the benefit of the caller.
360  */
361
362 mp *limlee(const char *name, mp *d, mp *newp,
363            unsigned ql, unsigned pl, grand *r,
364            unsigned on, pgen_proc *oev, void *oec,
365            pgen_proc *iev, void *iec,
366            size_t *nf, mp ***f)
367 {
368   limlee_stepctx l;
369   rabin rr;
370
371   l.f = 0; if (f) l.f |= LIMLEE_KEEPFACTORS;
372   l.newp = newp;
373   l.pl = pl; l.ql = ql;
374   l.pops = 0;
375   l.iev = iev;
376   l.iec = iec;
377   l.r = r;
378
379   d = pgen(name, d, 0, oev, oec, on, limlee_step, &l,
380            rabin_iters(pl), pgen_test, &rr);
381
382   if (d && f) {
383     mp **v;
384     size_t i;
385     v = xmalloc(l.nf * sizeof(mp *));
386     for (i = 0; i < l.nf; i++)
387       v[i] = l.v[i].p;
388     xfree(l.v);
389     *f = v;
390     *nf = l.nf;
391   }
392
393   return (d);
394 }
395
396 /*----- That's all, folks -------------------------------------------------*/