chiark / gitweb /
catacomb/pwsafe.py: Use `binascii' for Base64 conversion.
[catacomb-python] / pgen.c
1 /* -*-c-*-
2  *
3  * Prime number generation
4  *
5  * (c) 2005 Straylight/Edgeware
6  */
7
8 /*----- Licensing notice --------------------------------------------------*
9  *
10  * This file is part of the Python interface to Catacomb.
11  *
12  * Catacomb/Python is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * Catacomb/Python 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 General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with Catacomb/Python; if not, write to the Free Software Foundation,
24  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25  */
26
27 /*----- Header files ------------------------------------------------------*/
28
29 #include "catacomb-python.h"
30
31 /*----- Filters -----------------------------------------------------------*/
32
33 PyTypeObject *pfilt_pytype;
34
35 static PyObject *pfilt_pywrap(pfilt *f)
36 {
37   pfilt_pyobj *o = 0;
38   o = PyObject_New(pfilt_pyobj, pfilt_pytype);
39   o->f = *f;
40   o->st = pfilt_step(f, 0);
41   return ((PyObject *)o);
42 }
43
44 static PyObject *pfilt_pymake(PyTypeObject *ty, PyObject *xobj)
45 {
46   mp *x = 0;
47   pfilt_pyobj *o = 0;
48
49   if (PFILT_PYCHECK(xobj)) RETURN_OBJ(xobj);
50   if ((x = getmp(xobj)) == 0) goto end;
51   o = (pfilt_pyobj *)ty->tp_alloc(ty, 0);
52   o->st = pfilt_create(&o->f, x);
53 end:
54   mp_drop(x);
55   return ((PyObject *)o);
56 }
57
58 static PyObject *pfilt_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
59 {
60   static const char *const kwlist[] = { "x", 0 };
61   PyObject *xobj;
62
63   if (!PyArg_ParseTupleAndKeywords(arg, kw, "O:new", KWLIST, &xobj))
64     return (0);
65   return (pfilt_pymake(ty, xobj));
66 }
67
68 static void pfilt_pydealloc(PyObject *me)
69   { pfilt_destroy(PFILT_F(me)); FREEOBJ(me); }
70
71 static PyObject *pfmeth_step(PyObject *me, PyObject *arg)
72 {
73   mpw x;
74
75   if (!PyArg_ParseTuple(arg, "O&:step", convmpw, &x)) return (0);
76   PFILT_ST(me) = pfilt_step(PFILT_F(me), x);
77   RETURN_ME;
78 }
79
80 static PyObject *pfmeth_muladd(PyObject *me, PyObject *arg)
81 {
82   mpw m, a;
83   pfilt_pyobj *o = 0;
84
85   if (!PyArg_ParseTuple(arg, "O&O&:muladd", convmpw, &m, convmpw, &a))
86     return (0);
87   o = PyObject_New(pfilt_pyobj, pfilt_pytype);
88   o->st = pfilt_muladd(&o->f, PFILT_F(me), m, a);
89   return ((PyObject *)o);
90 }
91
92 static CONVFUNC(pfilt, pfilt *, PFILT_F)
93
94 static PyObject *pfmeth_jump(PyObject *me, PyObject *arg)
95 {
96   pfilt *f;
97
98   if (!PyArg_ParseTuple(arg, "O&:jump", convpfilt, &f)) return (0);
99   PFILT_ST(me) = pfilt_jump(PFILT_F(me), f);
100   RETURN_ME;
101 }
102
103 static PyObject *pfmeth_smallfactor(PyObject *me, PyObject *arg)
104 {
105   mp *x = 0;
106   PyObject *rc = 0;
107
108   if (!PyArg_ParseTuple(arg, "O&:smallfactor", convmp, &x)) goto end;
109   rc = PyInt_FromLong(pfilt_smallfactor(x));
110 end:
111   mp_drop(x);
112   return (rc);
113 }
114
115 static int pfilt_pynonzerop(PyObject *me)
116   { return (PFILT_ST(me) != PGEN_FAIL); }
117
118 static PyObject *pfilt_pyint(PyObject *me)
119 {
120   long l;
121   PyObject *rc = 0;
122
123   if (!mp_tolong_checked(PFILT_F(me)->m, &l, 0)) rc = PyInt_FromLong(l);
124   else rc = mp_topylong(PFILT_F(me)->m);
125   return (rc);
126 }
127
128 static PyObject *pfilt_pylong(PyObject *me)
129   { return (mp_topylong(PFILT_F(me)->m)); }
130
131 static PyObject *pfget_x(PyObject *me, void *hunoz)
132   { return (mp_pywrap(MP_COPY(PFILT_F(me)->m))); }
133
134 static PyObject *pfget_status(PyObject *me, void *hunoz)
135   { return (PyInt_FromLong(PFILT_ST(me))); }
136
137 static const PyGetSetDef pfilt_pygetset[] = {
138 #define GETSETNAME(op, name) pf##op##_##name
139   GET   (x,             "F.x -> current position of filter")
140   GET   (status,        "F.status -> primality status of filter")
141 #undef GETSETNAME
142   { 0 }
143 };
144
145 static const PyMethodDef pfilt_pymethods[] = {
146 #define METHNAME(name) pfmeth_##name
147   METH  (step,          "F.step(N)")
148   METH  (muladd,        "F.muladd(M, A)")
149   METH  (jump,          "F.jump(FF)")
150   SMTH  (smallfactor,   "smallfactor(X) -> PGST")
151 #undef METHNAME
152   { 0 }
153 };
154
155 static const PyNumberMethods pfilt_pynumber = {
156   0,                                    /* @nb_add@ */
157   0,                                    /* @nb_subtract@ */
158   0,                                    /* @nb_multiply@ */
159   0,                                    /* @nb_divide@ */
160   0,                                    /* @nb_remainder@ */
161   0,                                    /* @nb_divmod@ */
162   0,                                    /* @nb_power@ */
163   0,                                    /* @nb_negative@ */
164   0,                                    /* @nb_positive@ */
165   0,                                    /* @nb_absolute@ */
166   pfilt_pynonzerop,                     /* @nb_nonzero@ */
167   0,                                    /* @nb_invert@ */
168   0,                                    /* @nb_lshift@ */
169   0,                                    /* @nb_rshift@ */
170   0,                                    /* @nb_and@ */
171   0,                                    /* @nb_xor@ */
172   0,                                    /* @nb_or@ */
173   0,                                    /* @nb_coerce@ */
174   pfilt_pyint,                          /* @nb_int@ */
175   pfilt_pylong,                         /* @nb_long@ */
176   0,                                    /* @nb_float@ */
177   0,                                    /* @nb_oct@ */
178   0,                                    /* @nb_hex@ */
179
180   0,                                    /* @nb_inplace_add@ */
181   0,                                    /* @nb_inplace_subtract@ */
182   0,                                    /* @nb_inplace_multiply@ */
183   0,                                    /* @nb_inplace_divide@ */
184   0,                                    /* @nb_inplace_remainder@ */
185   0,                                    /* @nb_inplace_power@ */
186   0,                                    /* @nb_inplace_lshift@ */
187   0,                                    /* @nb_inplace_rshift@ */
188   0,                                    /* @nb_inplace_and@ */
189   0,                                    /* @nb_inplace_xor@ */
190   0,                                    /* @nb_inplace_or@ */
191
192   0,                                    /* @nb_floor_divide@ */
193   0,                                    /* @nb_true_divide@ */
194   0,                                    /* @nb_inplace_floor_divide@ */
195   0,                                    /* @nb_inplace_true_divide@ */
196 };
197
198 static const PyTypeObject pfilt_pytype_skel = {
199   PyVarObject_HEAD_INIT(0, 0)           /* Header */
200   "PrimeFilter",                        /* @tp_name@ */
201   sizeof(pfilt_pyobj),                  /* @tp_basicsize@ */
202   0,                                    /* @tp_itemsize@ */
203
204   pfilt_pydealloc,                      /* @tp_dealloc@ */
205   0,                                    /* @tp_print@ */
206   0,                                    /* @tp_getattr@ */
207   0,                                    /* @tp_setattr@ */
208   0,                                    /* @tp_compare@ */
209   0,                                    /* @tp_repr@ */
210   PYNUMBER(pfilt),                      /* @tp_as_number@ */
211   0,                                    /* @tp_as_sequence@ */
212   0,                                    /* @tp_as_mapping@ */
213   0,                                    /* @tp_hash@ */
214   0,                                    /* @tp_call@ */
215   0,                                    /* @tp_str@ */
216   0,                                    /* @tp_getattro@ */
217   0,                                    /* @tp_setattro@ */
218   0,                                    /* @tp_as_buffer@ */
219   Py_TPFLAGS_DEFAULT |                  /* @tp_flags@ */
220     Py_TPFLAGS_BASETYPE,
221
222   /* @tp_doc@ */
223   "PrimeFilter(X): small-primes filter.",
224
225   0,                                    /* @tp_traverse@ */
226   0,                                    /* @tp_clear@ */
227   0,                                    /* @tp_richcompare@ */
228   0,                                    /* @tp_weaklistoffset@ */
229   0,                                    /* @tp_iter@ */
230   0,                                    /* @tp_iternext@ */
231   PYMETHODS(pfilt),                     /* @tp_methods@ */
232   0,                                    /* @tp_members@ */
233   PYGETSET(pfilt),                      /* @tp_getset@ */
234   0,                                    /* @tp_base@ */
235   0,                                    /* @tp_dict@ */
236   0,                                    /* @tp_descr_get@ */
237   0,                                    /* @tp_descr_set@ */
238   0,                                    /* @tp_dictoffset@ */
239   0,                                    /* @tp_init@ */
240   PyType_GenericAlloc,                  /* @tp_alloc@ */
241   pfilt_pynew,                          /* @tp_new@ */
242   0,                                    /* @tp_free@ */
243   0                                     /* @tp_is_gc@ */
244 };
245
246 /*----- Rabin-Miller testing ----------------------------------------------*/
247
248 typedef struct rabin_pyobj {
249   PyObject_HEAD
250   rabin r;
251 } rabin_pyobj;
252
253 static PyTypeObject *rabin_pytype;
254 #define RABIN_R(o) (&((rabin_pyobj *)(o))->r)
255
256 static PyObject *rabin_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
257 {
258   mp *x = 0;
259   rabin_pyobj *o = 0;
260   static const char *const kwlist[] = { "x", 0 };
261
262   if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", KWLIST, convmp, &x))
263     goto end;
264   if (!MP_POSP(x) || MP_EVENP(x)) VALERR("must be positive and odd");
265   o = (rabin_pyobj *)ty->tp_alloc(ty, 0);
266   rabin_create(&o->r, x);
267 end:
268   return ((PyObject *)o);
269 }
270
271 static void rabin_pydealloc(PyObject *me)
272 {
273   rabin_destroy(RABIN_R(me));
274   FREEOBJ(me);
275 }
276
277 static PyObject *rmeth_test(PyObject *me, PyObject *arg)
278 {
279   mp *w = 0;
280   PyObject *rc = 0;
281
282   if (!PyArg_ParseTuple(arg, "O&:test", convmp, &w)) goto end;
283   rc = PyInt_FromLong(rabin_test(RABIN_R(me), w));
284 end:
285   mp_drop(w);
286   return (rc);
287 }
288
289 static PyObject *rmeth_rtest(PyObject *me, PyObject *arg)
290 {
291   mp *w = 0;
292   PyObject *rc = 0;
293
294   if (!PyArg_ParseTuple(arg, "O&:rtest", convmp, &w)) goto end;
295   rc = PyInt_FromLong(rabin_rtest(RABIN_R(me), w));
296 end:
297   mp_drop(w);
298   return (rc);
299 }
300
301 static PyObject *rget_niters(PyObject *me, void *hunoz)
302   { return (PyInt_FromLong(rabin_iters(mp_bits(RABIN_R(me)->mm.m)))); }
303
304 static PyObject *rget_x(PyObject *me, void *hunoz)
305   { return (mp_pywrap(MP_COPY(RABIN_R(me)->mm.m))); }
306
307 static PyObject *rmeth_iters(PyObject *me, PyObject *arg)
308 {
309   unsigned n;
310
311   if (!PyArg_ParseTuple(arg, "O&:iters", convuint, &n)) return (0);
312   return (PyInt_FromLong(rabin_iters(n)));
313 }
314
315 static const PyGetSetDef rabin_pygetset[] = {
316 #define GETSETNAME(op, name) r##op##_##name
317   GET   (x,             "R.x -> number under test")
318   GET   (niters,        "R.niters -> suggested number of tests")
319 #undef GETSETNAME
320   { 0 }
321 };
322
323 static const PyMethodDef rabin_pymethods[] = {
324 #define METHNAME(name) rmeth_##name
325   METH  (test,          "R.test(W) -> PGST")
326   METH  (rtest,         "R.rtest(W) -> PGST")
327   SMTH  (iters,         "iters(NBITS) -> NITERS")
328 #undef METHNAME
329   { 0 }
330 };
331
332 static const PyTypeObject rabin_pytype_skel = {
333   PyVarObject_HEAD_INIT(0, 0)           /* Header */
334   "RabinMiller",                        /* @tp_name@ */
335   sizeof(rabin_pyobj),                  /* @tp_basicsize@ */
336   0,                                    /* @tp_itemsize@ */
337
338   rabin_pydealloc,                      /* @tp_dealloc@ */
339   0,                                    /* @tp_print@ */
340   0,                                    /* @tp_getattr@ */
341   0,                                    /* @tp_setattr@ */
342   0,                                    /* @tp_compare@ */
343   0,                                    /* @tp_repr@ */
344   0,                                    /* @tp_as_number@ */
345   0,                                    /* @tp_as_sequence@ */
346   0,                                    /* @tp_as_mapping@ */
347   0,                                    /* @tp_hash@ */
348   0,                                    /* @tp_call@ */
349   0,                                    /* @tp_str@ */
350   0,                                    /* @tp_getattro@ */
351   0,                                    /* @tp_setattro@ */
352   0,                                    /* @tp_as_buffer@ */
353   Py_TPFLAGS_DEFAULT |                  /* @tp_flags@ */
354     Py_TPFLAGS_BASETYPE,
355
356   /* @tp_doc@ */
357   "RabinMiller(X): Rabin-Miller strong primality test.",
358
359   0,                                    /* @tp_traverse@ */
360   0,                                    /* @tp_clear@ */
361   0,                                    /* @tp_richcompare@ */
362   0,                                    /* @tp_weaklistoffset@ */
363   0,                                    /* @tp_iter@ */
364   0,                                    /* @tp_iternext@ */
365   PYMETHODS(rabin),                     /* @tp_methods@ */
366   0,                                    /* @tp_members@ */
367   PYGETSET(rabin),                      /* @tp_getset@ */
368   0,                                    /* @tp_base@ */
369   0,                                    /* @tp_dict@ */
370   0,                                    /* @tp_descr_get@ */
371   0,                                    /* @tp_descr_set@ */
372   0,                                    /* @tp_dictoffset@ */
373   0,                                    /* @tp_init@ */
374   PyType_GenericAlloc,                  /* @tp_alloc@ */
375   rabin_pynew,                          /* @tp_new@ */
376   0,                                    /* @tp_free@ */
377   0                                     /* @tp_is_gc@ */
378 };
379
380 /*----- Events ------------------------------------------------------------*/
381
382 typedef struct pgevent_pyobj {
383   PyObject_HEAD
384   PyObject *r;
385   pgen_event *ev;
386 } pgevent_pyobj;
387
388 static PyTypeObject *pgevent_pytype;
389 #define PGEVENT_EV(o) (((pgevent_pyobj *)(o))->ev)
390
391 static PyObject *pgevent_pywrap(pgen_event *ev)
392 {
393   pgevent_pyobj *o = PyObject_New(pgevent_pyobj, pgevent_pytype);
394   o->ev = ev; o->r = 0;
395   return ((PyObject *)o);
396 }
397
398 static CONVFUNC(pgevent, pgen_event *, PGEVENT_EV)
399
400 static void pgevent_kill(PyObject *me)
401 {
402   pgevent_pyobj *ev = (pgevent_pyobj *)me;
403
404   ev->ev = 0;
405   if (ev->r) GRAND_R(ev->r) = 0;
406 }
407
408 static void pgevent_pydealloc(PyObject *me)
409 {
410   pgevent_pyobj *ev = (pgevent_pyobj *)me;
411   Py_XDECREF(ev->r); FREEOBJ(me);
412 }
413
414 #define PGEVENT_CHECK(me) do {                                          \
415   if (!PGEVENT_EV(me)) {                                                \
416     PyErr_SetString(PyExc_ValueError, "event object is no longer valid"); \
417     return (0);                                                         \
418   }                                                                     \
419 } while (0)
420
421 static PyObject *peget_name(PyObject *me, void *hunoz)
422   { PGEVENT_CHECK(me); return (TEXT_FROMSTR(PGEVENT_EV(me)->name)); }
423
424 static PyObject *peget_x(PyObject *me, void *hunoz)
425   { PGEVENT_CHECK(me); return (mp_pywrap(MP_COPY(PGEVENT_EV(me)->m))); }
426
427 static PyObject *peget_steps(PyObject *me, void *hunoz)
428   { PGEVENT_CHECK(me); return (PyInt_FromLong(PGEVENT_EV(me)->steps)); }
429
430 static PyObject *peget_tests(PyObject *me, void *hunoz)
431   { PGEVENT_CHECK(me); return (PyInt_FromLong(PGEVENT_EV(me)->tests)); }
432
433 static PyObject *peget_rng(PyObject *me, void *hunoz)
434 {
435   pgevent_pyobj *ev = (pgevent_pyobj *)me;
436
437   PGEVENT_CHECK(me);
438   if (!ev->r) ev->r = grand_pywrap(ev->ev->r, 0);
439   Py_INCREF(ev->r); return ((PyObject *)ev->r);
440 }
441
442 static int peset_x(PyObject *me, PyObject *xobj, void *hunoz)
443 {
444   mp *x = 0;
445   pgen_event *ev = PGEVENT_EV(me);
446   int rc = -1;
447   if (!xobj) NIERR("__del__");
448   PGEVENT_CHECK(me);
449   if ((x = getmp(xobj)) == 0) goto end;
450   mp_drop(ev->m);
451   ev->m = MP_COPY(x);
452   rc = 0;
453 end:
454   mp_drop(x);
455   return (rc);
456 }
457
458 static const PyGetSetDef pgevent_pygetset[] = {
459 #define GETSETNAME(op, name) pe##op##_##name
460   GET   (name,          "EV.name -> value being generated")
461   GETSET(x,             "EV.x -> value under test")
462   GET   (steps,         "EV.steps -> number of steps left")
463   GET   (tests,         "EV.tests -> tests before passing")
464   GET   (rng,           "EV.rng -> (noncrypto) random number generator")
465 #undef GETSETNAME
466   { 0 }
467 };
468
469 static const PyTypeObject pgevent_pytype_skel = {
470   PyVarObject_HEAD_INIT(0, 0)           /* Header */
471   "PrimeGenEvent",                      /* @tp_name@ */
472   sizeof(pgevent_pyobj),                /* @tp_basicsize@ */
473   0,                                    /* @tp_itemsize@ */
474
475   pgevent_pydealloc,                    /* @tp_dealloc@ */
476   0,                                    /* @tp_print@ */
477   0,                                    /* @tp_getattr@ */
478   0,                                    /* @tp_setattr@ */
479   0,                                    /* @tp_compare@ */
480   0,                                    /* @tp_repr@ */
481   0,                                    /* @tp_as_number@ */
482   0,                                    /* @tp_as_sequence@ */
483   0,                                    /* @tp_as_mapping@ */
484   0,                                    /* @tp_hash@ */
485   0,                                    /* @tp_call@ */
486   0,                                    /* @tp_str@ */
487   0,                                    /* @tp_getattro@ */
488   0,                                    /* @tp_setattro@ */
489   0,                                    /* @tp_as_buffer@ */
490   Py_TPFLAGS_DEFAULT |                  /* @tp_flags@ */
491     Py_TPFLAGS_BASETYPE,
492
493   /* @tp_doc@ */
494   "Prime-generation event.",
495
496   0,                                    /* @tp_traverse@ */
497   0,                                    /* @tp_clear@ */
498   0,                                    /* @tp_richcompare@ */
499   0,                                    /* @tp_weaklistoffset@ */
500   0,                                    /* @tp_iter@ */
501   0,                                    /* @tp_iternext@ */
502   0,                                    /* @tp_methods@ */
503   0,                                    /* @tp_members@ */
504   PYGETSET(pgevent),                    /* @tp_getset@ */
505   0,                                    /* @tp_base@ */
506   0,                                    /* @tp_dict@ */
507   0,                                    /* @tp_descr_get@ */
508   0,                                    /* @tp_descr_set@ */
509   0,                                    /* @tp_dictoffset@ */
510   0,                                    /* @tp_init@ */
511   PyType_GenericAlloc,                  /* @tp_alloc@ */
512   abstract_pynew,                       /* @tp_new@ */
513   0,                                    /* @tp_free@ */
514   0                                     /* @tp_is_gc@ */
515 };
516
517 /*----- Event handlers ----------------------------------------------------*/
518
519 PyTypeObject *pgev_pytype;
520
521 typedef struct pgstep_pyobj {
522   PGEV_HEAD
523   pgen_filterctx f;
524 } pgstep_pyobj;
525
526 static PyTypeObject *pgstep_pytype;
527 #define PGSTEP_STEP(o) (((pgstep_pyobj *)(o))->f.step)
528
529 typedef struct pgjump_pyobj {
530   PGEV_HEAD
531   PyObject *fobj;
532   pgen_jumpctx j;
533 } pgjump_pyobj;
534
535 static PyTypeObject *pgjump_pytype;
536 #define PGJUMP_FOBJ(o) (((pgjump_pyobj *)(o))->fobj)
537 #define PGJUMP_J(o) (&((pgjump_pyobj *)(o))->j)
538
539 typedef struct pgtest_pyobj {
540   PGEV_HEAD
541   rabin r;
542 } pgtest_pyobj;
543
544 static PyTypeObject *pgtest_pytype;
545
546 static int pgev_python(int rq, pgen_event *ev, void *p)
547 {
548   pypgev *pg = p;
549   PyObject *pyev = 0;
550   PyObject *rc = 0;
551   int st = PGEN_ABORT;
552   long l;
553   static const char *const meth[] =
554     { "pg_abort", "pg_done", "pg_begin", "pg_try", "pg_fail", "pg_pass" };
555
556   rq++;
557   if (rq > N(meth)) SYSERR("event code out of range");
558   pyev = pgevent_pywrap(ev);
559   if ((rc = PyObject_CallMethod(pg->obj, (/*unconst*/ char *)meth[rq],
560                                 "(O)", pyev)) == 0)
561     goto end;
562   if (rc == Py_None)
563     st = PGEN_TRY;
564   else if ((l = PyInt_AsLong(rc)) == -1 && PyErr_Occurred())
565     goto end;
566   else if (l < PGEN_ABORT || l > PGEN_PASS)
567     VALERR("return code out of range");
568   else
569     st = l;
570 end:
571   if (PyErr_Occurred())
572     stash_exception(pg->exc, "exception from `pgen' handler");
573   if (pyev) {
574     pgevent_kill(pyev);
575     Py_DECREF(pyev);
576   }
577   Py_XDECREF(rc);
578   return (st);
579 }
580
581 static PyObject *pgev_pywrap(const pgev *pg)
582 {
583   pgev_pyobj *o;
584
585   o = PyObject_New(pgev_pyobj, pgev_pytype);
586   o->pg = *pg;
587   return ((PyObject *)o);
588 }
589
590 int convpgev(PyObject *o, void *p)
591 {
592   pypgev *pg = p;
593
594   if (PGEV_PYCHECK(o))
595     pg->ev = *PGEV_PG(o);
596   else {
597     pg->ev.proc = pgev_python;
598     pg->ev.ctx = pg;
599     pg->obj = o; Py_INCREF(o);
600   }
601   return (1);
602 }
603
604 void droppgev(pypgev *pg)
605 {
606   if (pg->ev.proc == pgev_python)
607     { assert(pg->ev.ctx == pg); Py_DECREF(pg->obj); }
608 }
609
610 static PyObject *pgmeth_common(PyObject *me, PyObject *arg, int rq)
611 {
612   pgen_event *ev;
613   pgev *pg = PGEV_PG(me);
614   PyObject *rc = 0;
615
616   if (!PyArg_ParseTuple(arg, "O&", convpgevent, &ev)) goto end;
617   rc = PyInt_FromLong(!pg->proc ? rq : pg->proc(rq, ev, pg->ctx));
618 end:
619   return (rc);
620 }
621
622 #define PGMETH(lc, uc)                                                  \
623   static PyObject *pgmeth_pg_##lc(PyObject *me, PyObject *arg)          \
624     { return pgmeth_common(me, arg, PGEN_##uc); }
625 PGMETH(abort,   ABORT)
626 PGMETH(done,    DONE)
627 PGMETH(begin,   BEGIN)
628 PGMETH(try,     TRY)
629 PGMETH(pass,    PASS)
630 PGMETH(fail,    FAIL)
631 #undef PGMETH
632
633 static PyObject *pgev_stdev(pgen_proc *proc)
634   { pgev pg; pg.proc = proc; pg.ctx = 0; return (pgev_pywrap(&pg)); }
635
636 static const PyMethodDef pgev_pymethods[] = {
637 #define METHNAME(name) pgmeth_##name
638   METH  (pg_abort,      "E.pg_abort(EV) -> PGST -- prime generation aborted")
639   METH  (pg_done,       "E.pg_done(EV) -> PGST -- prime generation finished")
640   METH  (pg_begin,     "E.pg_begin(EV) -> PGST -- commence stepping/testing")
641   METH  (pg_try,        "E.pg_try(EV) -> PGST -- found new candidate")
642   METH  (pg_pass,       "E.pg_pass(EV) -> PGST -- passed primality test")
643   METH  (pg_fail,       "E.pg_fail(EV) -> PGST -- failed primality test")
644 #undef METHNAME
645   { 0 }
646 };
647
648 static const PyTypeObject pgev_pytype_skel = {
649   PyVarObject_HEAD_INIT(0, 0)           /* Header */
650   "PrimeGenBuiltinHandler",             /* @tp_name@ */
651   sizeof(pgev_pyobj),                   /* @tp_basicsize@ */
652   0,                                    /* @tp_itemsize@ */
653
654   0,                                    /* @tp_dealloc@ */
655   0,                                    /* @tp_print@ */
656   0,                                    /* @tp_getattr@ */
657   0,                                    /* @tp_setattr@ */
658   0,                                    /* @tp_compare@ */
659   0,                                    /* @tp_repr@ */
660   0,                                    /* @tp_as_number@ */
661   0,                                    /* @tp_as_sequence@ */
662   0,                                    /* @tp_as_mapping@ */
663   0,                                    /* @tp_hash@ */
664   0,                                    /* @tp_call@ */
665   0,                                    /* @tp_str@ */
666   0,                                    /* @tp_getattro@ */
667   0,                                    /* @tp_setattro@ */
668   0,                                    /* @tp_as_buffer@ */
669   Py_TPFLAGS_DEFAULT |                  /* @tp_flags@ */
670     Py_TPFLAGS_BASETYPE,
671
672   /* @tp_doc@ */
673   "Built-in prime-generation event handler, base class.",
674
675   0,                                    /* @tp_traverse@ */
676   0,                                    /* @tp_clear@ */
677   0,                                    /* @tp_richcompare@ */
678   0,                                    /* @tp_weaklistoffset@ */
679   0,                                    /* @tp_iter@ */
680   0,                                    /* @tp_iternext@ */
681   PYMETHODS(pgev),                      /* @tp_methods@ */
682   0,                                    /* @tp_members@ */
683   0,                                    /* @tp_getset@ */
684   0,                                    /* @tp_base@ */
685   0,                                    /* @tp_dict@ */
686   0,                                    /* @tp_descr_get@ */
687   0,                                    /* @tp_descr_set@ */
688   0,                                    /* @tp_dictoffset@ */
689   0,                                    /* @tp_init@ */
690   PyType_GenericAlloc,                  /* @tp_alloc@ */
691   abstract_pynew,                       /* @tp_new@ */
692   0,                                    /* @tp_free@ */
693   0                                     /* @tp_is_gc@ */
694 };
695
696 static PyObject *pgstep_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
697 {
698   mpw s;
699   pgstep_pyobj *rc = 0;
700   static const char *const kwlist[] = { "step", 0 };
701
702   if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&:new", KWLIST, convmpw, &s))
703     goto end;
704   rc = (pgstep_pyobj *)ty->tp_alloc(ty, 0);
705   rc->f.step = s;
706   rc->pg.proc = pgen_filter;
707   rc->pg.ctx = &rc->f;
708 end:
709   return ((PyObject *)rc);
710 }
711
712 static PyObject *psget_step(PyObject *me, void *hunoz)
713   { return (PyInt_FromLong(PGSTEP_STEP(me))); }
714
715 static const PyGetSetDef pgstep_pygetset[] = {
716 #define GETSETNAME(op, name) ps##op##_##name
717   GET   (step,          "S.step -> step size for the stepper")
718 #undef GETSETNAME
719   { 0 }
720 };
721
722 static const PyTypeObject pgstep_pytype_skel = {
723   PyVarObject_HEAD_INIT(0, 0)           /* Header */
724   "PrimeGenStepper",                    /* @tp_name@ */
725   sizeof(pgstep_pyobj),                 /* @tp_basicsize@ */
726   0,                                    /* @tp_itemsize@ */
727
728   0,                                    /* @tp_dealloc@ */
729   0,                                    /* @tp_print@ */
730   0,                                    /* @tp_getattr@ */
731   0,                                    /* @tp_setattr@ */
732   0,                                    /* @tp_compare@ */
733   0,                                    /* @tp_repr@ */
734   0,                                    /* @tp_as_number@ */
735   0,                                    /* @tp_as_sequence@ */
736   0,                                    /* @tp_as_mapping@ */
737   0,                                    /* @tp_hash@ */
738   0,                                    /* @tp_call@ */
739   0,                                    /* @tp_str@ */
740   0,                                    /* @tp_getattro@ */
741   0,                                    /* @tp_setattro@ */
742   0,                                    /* @tp_as_buffer@ */
743   Py_TPFLAGS_DEFAULT |                  /* @tp_flags@ */
744     Py_TPFLAGS_BASETYPE,
745
746   /* @tp_doc@ */
747   "PrimeGenStepper(STEP): simple stepper with small-factors filter.",
748
749   0,                                    /* @tp_traverse@ */
750   0,                                    /* @tp_clear@ */
751   0,                                    /* @tp_richcompare@ */
752   0,                                    /* @tp_weaklistoffset@ */
753   0,                                    /* @tp_iter@ */
754   0,                                    /* @tp_iternext@ */
755   0,                                    /* @tp_methods@ */
756   0,                                    /* @tp_members@ */
757   PYGETSET(pgstep),                     /* @tp_getset@ */
758   0,                                    /* @tp_base@ */
759   0,                                    /* @tp_dict@ */
760   0,                                    /* @tp_descr_get@ */
761   0,                                    /* @tp_descr_set@ */
762   0,                                    /* @tp_dictoffset@ */
763   0,                                    /* @tp_init@ */
764   PyType_GenericAlloc,                  /* @tp_alloc@ */
765   pgstep_pynew,                         /* @tp_new@ */
766   0,                                    /* @tp_free@ */
767   0                                     /* @tp_is_gc@ */
768 };
769
770 static PyObject *pgjump_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
771 {
772   PyObject *o, *fobj;
773   pgjump_pyobj *rc = 0;
774   static const char *const kwlist[] = { "jump", 0 };
775
776   if (!PyArg_ParseTupleAndKeywords(arg, kw, "O:new", KWLIST, &o) ||
777       (fobj = pfilt_pymake(pfilt_pytype, o)) == 0)
778     goto end;
779   rc = (pgjump_pyobj *)ty->tp_alloc(ty, 0);
780   rc->fobj = fobj;
781   rc->j.j = PFILT_F(fobj);
782   rc->pg.proc = pgen_jump;
783   rc->pg.ctx = &rc->j;
784 end:
785   return ((PyObject *)rc);
786 }
787
788 static void pgjump_pydealloc(PyObject *me)
789 {
790   Py_DECREF(PGJUMP_FOBJ(me));
791   FREEOBJ(me);
792 }
793
794 static PyObject *pjget_jump(PyObject *me, void *hunoz)
795   { RETURN_OBJ(PGJUMP_FOBJ(me)); }
796
797 static const PyGetSetDef pgjump_pygetset[] = {
798 #define GETSETNAME(op, name) pj##op##_##name
799   GET   (jump,          "S.jump -> jump size for the stepper")
800 #undef GETSETNAME
801   { 0 }
802 };
803
804 static const PyTypeObject pgjump_pytype_skel = {
805   PyVarObject_HEAD_INIT(0, 0)           /* Header */
806   "PrimeGenJumper",                     /* @tp_name@ */
807   sizeof(pgjump_pyobj),                 /* @tp_basicsize@ */
808   0,                                    /* @tp_itemsize@ */
809
810   pgjump_pydealloc,                     /* @tp_dealloc@ */
811   0,                                    /* @tp_print@ */
812   0,                                    /* @tp_getattr@ */
813   0,                                    /* @tp_setattr@ */
814   0,                                    /* @tp_compare@ */
815   0,                                    /* @tp_repr@ */
816   0,                                    /* @tp_as_number@ */
817   0,                                    /* @tp_as_sequence@ */
818   0,                                    /* @tp_as_mapping@ */
819   0,                                    /* @tp_hash@ */
820   0,                                    /* @tp_call@ */
821   0,                                    /* @tp_str@ */
822   0,                                    /* @tp_getattro@ */
823   0,                                    /* @tp_setattro@ */
824   0,                                    /* @tp_as_buffer@ */
825   Py_TPFLAGS_DEFAULT |                  /* @tp_flags@ */
826     Py_TPFLAGS_BASETYPE,
827
828   /* @tp_doc@ */
829   "PrimeGenJumper(JUMP): "
830                        "stepper for larger steps with small-factors filter.",
831
832   0,                                    /* @tp_traverse@ */
833   0,                                    /* @tp_clear@ */
834   0,                                    /* @tp_richcompare@ */
835   0,                                    /* @tp_weaklistoffset@ */
836   0,                                    /* @tp_iter@ */
837   0,                                    /* @tp_iternext@ */
838   0,                                    /* @tp_methods@ */
839   0,                                    /* @tp_members@ */
840   PYGETSET(pgjump),                     /* @tp_getset@ */
841   0,                                    /* @tp_base@ */
842   0,                                    /* @tp_dict@ */
843   0,                                    /* @tp_descr_get@ */
844   0,                                    /* @tp_descr_set@ */
845   0,                                    /* @tp_dictoffset@ */
846   0,                                    /* @tp_init@ */
847   PyType_GenericAlloc,                  /* @tp_alloc@ */
848   pgjump_pynew,                         /* @tp_new@ */
849   0,                                    /* @tp_free@ */
850   0                                     /* @tp_is_gc@ */
851 };
852
853 static PyObject *pgtest_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
854 {
855   pgtest_pyobj *rc = 0;
856   static const char *const kwlist[] = { 0 };
857
858   if (!PyArg_ParseTupleAndKeywords(arg, kw, ":new", KWLIST)) goto end;
859   rc = (pgtest_pyobj *)ty->tp_alloc(ty, 0);
860   rc->pg.proc = pgen_test;
861   rc->pg.ctx = &rc->r;
862 end:
863   return ((PyObject *)rc);
864 }
865
866 static const PyTypeObject pgtest_pytype_skel = {
867   PyVarObject_HEAD_INIT(0, 0)           /* Header */
868   "PrimeGenTester",                     /* @tp_name@ */
869   sizeof(pgtest_pyobj),                 /* @tp_basicsize@ */
870   0,                                    /* @tp_itemsize@ */
871
872   0,                                    /* @tp_dealloc@ */
873   0,                                    /* @tp_print@ */
874   0,                                    /* @tp_getattr@ */
875   0,                                    /* @tp_setattr@ */
876   0,                                    /* @tp_compare@ */
877   0,                                    /* @tp_repr@ */
878   0,                                    /* @tp_as_number@ */
879   0,                                    /* @tp_as_sequence@ */
880   0,                                    /* @tp_as_mapping@ */
881   0,                                    /* @tp_hash@ */
882   0,                                    /* @tp_call@ */
883   0,                                    /* @tp_str@ */
884   0,                                    /* @tp_getattro@ */
885   0,                                    /* @tp_setattro@ */
886   0,                                    /* @tp_as_buffer@ */
887   Py_TPFLAGS_DEFAULT |                  /* @tp_flags@ */
888     Py_TPFLAGS_BASETYPE,
889
890   /* @tp_doc@ */
891   "PrimeGenTester(): Rabin-Miller tester.",
892
893   0,                                    /* @tp_traverse@ */
894   0,                                    /* @tp_clear@ */
895   0,                                    /* @tp_richcompare@ */
896   0,                                    /* @tp_weaklistoffset@ */
897   0,                                    /* @tp_iter@ */
898   0,                                    /* @tp_iternext@ */
899   0,                                    /* @tp_methods@ */
900   0,                                    /* @tp_members@ */
901   0,                                    /* @tp_getset@ */
902   0,                                    /* @tp_base@ */
903   0,                                    /* @tp_dict@ */
904   0,                                    /* @tp_descr_get@ */
905   0,                                    /* @tp_descr_set@ */
906   0,                                    /* @tp_dictoffset@ */
907   0,                                    /* @tp_init@ */
908   PyType_GenericAlloc,                  /* @tp_alloc@ */
909   pgtest_pynew,                         /* @tp_new@ */
910   0,                                    /* @tp_free@ */
911   0                                     /* @tp_is_gc@ */
912 };
913
914 /*----- Prime generation functions ----------------------------------------*/
915
916 void pgenerr(struct excinfo *exc)
917 {
918   if (exc->ty) RESTORE_EXCINFO(exc);
919   else PyErr_SetString(PyExc_ValueError, "prime generation failed");
920 }
921
922 static PyObject *meth_pgen(PyObject *me, PyObject *arg, PyObject *kw)
923 {
924   mp *x = 0;
925   mp *r = 0;
926   PyObject *rc = 0;
927   char *p = "p";
928   pgen_filterctx fc = { 2 };
929   rabin tc;
930   struct excinfo exc = EXCINFO_INIT;
931   pypgev step = { { 0 } }, test = { { 0 } }, evt = { { 0 } };
932   unsigned nsteps = 0, ntests = 0;
933   static const char *const kwlist[] =
934     { "start", "name", "stepper", "tester", "event", "nsteps", "ntests", 0 };
935
936   step.exc = &exc; step.ev.proc = pgen_filter; step.ev.ctx = &fc;
937   test.exc = &exc; test.ev.proc = pgen_test; test.ev.ctx = &tc;
938   evt.exc = &exc;
939   if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|sO&O&O&O&O&:pgen", KWLIST,
940                                    convmp, &x, &p, convpgev, &step,
941                                    convpgev, &test, convpgev, &evt,
942                                    convuint, &nsteps, convuint, &ntests))
943     goto end;
944   if (!ntests) ntests = rabin_iters(mp_bits(x));
945   if ((r = pgen(p, MP_NEW, x, evt.ev.proc, evt.ev.ctx,
946                 nsteps, step.ev.proc, step.ev.ctx,
947                 ntests, test.ev.proc, test.ev.ctx)) == 0)
948     PGENERR(&exc);
949   rc = mp_pywrap(r); r = 0;
950 end:
951   mp_drop(r); mp_drop(x);
952   droppgev(&step); droppgev(&test); droppgev(&evt);
953   return (rc);
954 }
955
956 static PyObject *meth_strongprime_setup(PyObject *me,
957                                         PyObject *arg, PyObject *kw)
958 {
959   mp *x = 0;
960   pfilt f;
961   grand *r = &rand_global;
962   unsigned nbits;
963   char *name = "p";
964   unsigned n = 0;
965   struct excinfo exc = EXCINFO_INIT;
966   pypgev evt = { { 0 } };
967   PyObject *rc = 0;
968   static const char *const kwlist[] =
969     { "nbits", "name", "event", "rng", "nsteps", 0 };
970
971   evt.exc = &exc;
972   if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|sO&O&O&", KWLIST,
973                                    convuint, &nbits, &name,
974                                    convpgev, &evt, convgrand, &r,
975                                    convuint, &n))
976     goto end;
977   if ((x = strongprime_setup(name, MP_NEW, &f, nbits,
978                              r, n, evt.ev.proc, evt.ev.ctx)) == 0)
979     PGENERR(&exc);
980   rc = Py_BuildValue("(NN)", mp_pywrap(x), pfilt_pywrap(&f));
981   x = 0;
982 end:
983   mp_drop(x);
984   droppgev(&evt);
985   return (rc);
986 }
987
988 static PyObject *meth_strongprime(PyObject *me, PyObject *arg, PyObject *kw)
989 {
990   mp *x = 0;
991   grand *r = &rand_global;
992   unsigned nbits;
993   char *name = "p";
994   unsigned n = 0;
995   struct excinfo exc = EXCINFO_INIT;
996   pypgev evt = { { 0 } };
997   PyObject *rc = 0;
998   static const char *const kwlist[] =
999     { "nbits", "name", "event", "rng", "nsteps", 0 };
1000
1001   evt.exc = &exc;
1002   if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|sO&O&O&", KWLIST,
1003                                    convuint, &nbits, &name,
1004                                    convpgev, &evt, convgrand, &r,
1005                                    convuint, &n))
1006     goto end;
1007   if ((x = strongprime(name, MP_NEW, nbits,
1008                        r, n, evt.ev.proc, evt.ev.ctx)) == 0)
1009     PGENERR(&exc);
1010   rc = mp_pywrap(x);
1011   x = 0;
1012 end:
1013   mp_drop(x);
1014   droppgev(&evt);
1015   return (rc);
1016 }
1017
1018 static PyObject *meth_limlee(PyObject *me, PyObject *arg, PyObject *kw)
1019 {
1020   char *p = "p";
1021   struct excinfo exc = EXCINFO_INIT;
1022   pypgev ie = { { 0 } }, oe = { { 0 } };
1023   unsigned ql, pl;
1024   grand *r = &rand_global;
1025   unsigned on = 0;
1026   size_t i, nf = 0;
1027   PyObject *rc = 0, *vec;
1028   static const char *const kwlist[] =
1029     { "pbits", "qbits", "name", "event", "ievent", "rng", "nsteps", 0 };
1030   mp *x = 0, **v = 0;
1031
1032   ie.exc = oe.exc = &exc;
1033   if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&O&|sO&O&O&O&:limlee", KWLIST,
1034                                    convuint, &pl, convuint, &ql,
1035                                    &p, convpgev, &oe, convpgev, &ie,
1036                                    convgrand, &r, convuint, &on))
1037     goto end;
1038   if ((x = limlee(p, MP_NEW, MP_NEW, ql, pl, r, on,
1039                   oe.ev.proc, oe.ev.ctx, ie.ev.proc, ie.ev.ctx,
1040                   &nf, &v)) == 0)
1041     PGENERR(&exc);;
1042   vec = PyList_New(nf);
1043   for (i = 0; i < nf; i++)
1044     PyList_SET_ITEM(vec, i, mp_pywrap(v[i]));
1045   xfree(v);
1046   rc = Py_BuildValue("(NN)", mp_pywrap(x), vec);
1047 end:
1048   droppgev(&oe); droppgev(&ie);
1049   return (rc);
1050 }
1051
1052 /*----- Global stuff ------------------------------------------------------*/
1053
1054 static const struct nameval consts[] = {
1055   CONST(PGEN_PASS), CONST(PGEN_FAIL), CONST(PGEN_BEGIN), CONST(PGEN_TRY),
1056   CONST(PGEN_DONE), CONST(PGEN_ABORT),
1057   { 0 }
1058 };
1059
1060 static const PyMethodDef methods[] = {
1061 #define METHNAME(name) meth_##name
1062   KWMETH(pgen,
1063        "pgen(START, [name = 'p'], [stepper = PrimeGenStepper(2)],\n"
1064        "     [tester = PrimeGenTester()], [event = pgen_nullev],\n"
1065        "     [nsteps = 0], [ntests = RabinMiller.iters(START.nbits)]) -> P")
1066   KWMETH(strongprime_setup,
1067        "strongprime_setup(NBITS, [name = 'p'], [event = pgen_nullev],\n"
1068        "                  [rng = rand], [nsteps = 0]) -> (START, JUMP)")
1069   KWMETH(strongprime,
1070        "strongprime(NBITS, [name = 'p'], [event = pgen_nullev],\n"
1071        "            [rng = rand], [nsteps = 0]) -> P")
1072   KWMETH(limlee,
1073        "limlee(PBITS, QBITS, [name = 'p'], [event = pgen_nullev],\n"
1074        "       [ievent = pgen_nullev], [rng = rand], [nsteps = 0]) "
1075                                                           "-> (P, [Q, ...])")
1076 #undef METHNAME
1077   { 0 }
1078 };
1079
1080 void pgen_pyinit(void)
1081 {
1082   INITTYPE(pfilt, root);
1083   INITTYPE(rabin, root);
1084   INITTYPE(pgevent, root);
1085   INITTYPE(pgev, root);
1086   INITTYPE(pgstep, pgev);
1087   INITTYPE(pgjump, pgev);
1088   INITTYPE(pgtest, pgev);
1089   addmethods(methods);
1090 }
1091
1092 static PyObject *obj;
1093
1094 void pgen_pyinsert(PyObject *mod)
1095 {
1096   INSERT("PrimeFilter", pfilt_pytype);
1097   INSERT("RabinMiller", rabin_pytype);
1098   INSERT("PrimeGenEvent", pgevent_pytype);
1099   INSERT("PrimeGenBuiltinHandler", pgev_pytype);
1100   INSERT("PrimeGenStepper", pgstep_pytype);
1101   INSERT("PrimeGenJumper", pgjump_pytype);
1102   INSERT("PrimeGenTester", pgtest_pytype);
1103   INSERT("pgen_nullev", obj = pgev_stdev(0));
1104   INSERT("pgen_stdev", pgev_stdev(pgen_ev));
1105   INSERT("pgen_spinev", pgev_stdev(pgen_evspin));
1106   INSERT("pgen_subev", pgev_stdev(pgen_subev));
1107   setconstants(mod, consts);
1108 }
1109
1110 /*----- That's all, folks -------------------------------------------------*/