Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
241818 views
1
#include <stdlib.h>
2
#include <stdio.h>
3
#include <string.h>
4
#include <math.h>
5
#include "gmp.h"
6
#include "mpzutil.h"
7
#include "ffwrapper.h"
8
#include "ffpoly.h"
9
#include "jac.h"
10
#include "jacorder.h"
11
#include "hecurve.h"
12
#include "smalljac.h"
13
#include "smalljactab.h"
14
#if SMALLJAC_GENUS > 1
15
#include "smalljac_g23.h"
16
#endif
17
#include "smalljac_internal.h"
18
#include "pointcount.h"
19
#include "cstd.h"
20
21
/*
22
Copyright 2007=2008 Andrew V. Sutherland
23
24
This file is part of smalljac.
25
26
smalljac is free software: you can redistribute it and/or modify
27
it under the terms of the GNU General Public License as published by
28
the Free Software Foundation, either version 2 of the License, or
29
(at your option) any later version.
30
31
smalljac is distributed in the hope that it will be useful,
32
but WITHOUT ANY WARRANTY; without even the implied warranty of
33
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
34
GNU General Public License for more details.
35
36
You should have received a copy of the GNU General Public License
37
along with smalljac. If not, see <http://www.gnu.org/licenses/>.
38
*/
39
40
/*
41
Main smalljac module.
42
Still some work to be done in genus > 1 but the interface should remain unchanged.
43
Genus 2 support for curves with a single pt at infinity (y^2=f(x) with f deg 5) is essentially complete,
44
but still need to add support for curves with zero or two pts at infty (f deg 6).
45
46
Lot's of work still to do in genus 3, currently only handles generic curves (minimal endomorphism ring)
47
with a single pt at infinity reliably (results are always provably correct, but it may fail frequently for
48
exceptional curves).
49
50
Not currently recommend for general use in genus 3 to compute Lpolys, but will compute a_p reliably.
51
*/
52
53
#define SMALLJAC_MAX_CONSTRAINTS 64
54
55
int cornacchia (long *x, long *y, long p, long d); // used to handle curves of the form y^2=x^6+a
56
57
static int _smalljac_initted;
58
void smalljac_init (void)
59
{
60
if ( _smalljac_initted ) return;
61
smalljac_table_alloc (SMALLJAC_TABBITS);
62
pointcount_init (SMALLJAC_INIT_COUNT_P);
63
_ff_wrapper_init();
64
_smalljac_initted = 1;
65
}
66
67
68
int smalljac_Lpoly (long a[], char *curve, unsigned long q, unsigned long flags)
69
{
70
static mpz_t P;
71
static int init;
72
smalljac_Qcurve *qc;
73
unsigned long p;
74
long t, t1, tnm1, tn;
75
int i, n, h, error;
76
77
if ( ! init ) { smalljac_init(); mpz_init (P); init = 1; }
78
if ( (flags&SMALLJAC_A1_ONLY) && (flags&SMALLJAC_GROUP) ) return SMALLJAC_INVALID_FLAGS;
79
80
qc = smalljac_Qcurve_init (curve, &error);
81
if ( ! qc ) return error;
82
if ( qc->genus != SMALLJAC_GENUS ) { error = SMALLJAC_WRONG_GENUS; goto done; }
83
error = 0;
84
85
// check for prime powers
86
mpz_set_ui (P, q);
87
h = mpz_pp_base (P, P);
88
if ( ! h ) { error = SMALLJAC_INVALID_PP; goto done; }
89
if ( h > 1 ) {
90
if ( (flags&SMALLJAC_GROUP) ) { error = SMALLJAC_INVALID_PP; goto done; } // group computation only supported for prime fields at the moment
91
if ( (flags&SMALLJAC_A1_ONLY) ) { flags &= ~SMALLJAC_A1_ONLY; n = qc->genus; } // turn off A1_ONLY flag if q is a prime power
92
}
93
94
p = mpz_get_ui (P);
95
if ( p <= SMALLJAC_TINY_P ) {
96
n = smalljac_tiny_Lpoly (a, qc, p, flags);
97
goto done;
98
}
99
if ( mpz_divisible_ui_p (qc->D, p) ) { n = 0; goto done; }
100
101
// Precompute delta values for pointcounting if needed
102
if ( smalljac_Qcurve_degree(qc) == 6 || p <= SMALLJAC_COUNT_P ) {
103
smalljac_Qcurve_init_Deltas(qc);
104
pointcount_precompute (qc->Deltas, qc->f, qc->degree);
105
qc->flags |= SMALLJAC_QCURVE_DELTA;
106
}
107
n = smalljac_internal_Lpoly (a, qc, p, flags);
108
if ( n <= 0 ) { error = SMALLJAC_INTERNAL_ERROR; goto done; }
109
done:
110
smalljac_Qcurve_clear (qc);
111
if ( h > 1 ) if ( ! smalljac_Lpoly_extend (a, n, p, h) ) error = SMALLJAC_INTERNAL_ERROR;
112
return ( error ? error : n);
113
}
114
115
116
long smalljac_Lpolys (smalljac_Qcurve_t curve, unsigned long start, unsigned long end, unsigned long flags,
117
int (*callback)(smalljac_Qcurve_t curve, unsigned long p, int good, long a[], int n, void *arg), void *arg)
118
{
119
static mpz_t P;
120
static int init;
121
smalljac_Qcurve *qc;
122
prime_enum_ctx_t *ctx;
123
unsigned long h[SMALLJAC_MAX_BAD_PRIMES];
124
register unsigned long p, pbitmask, pbits;
125
int i, filter, good, good_only, error;
126
127
if ( ! init ) { smalljac_init(); mpz_init (P); init = 1; }
128
if ( (flags&SMALLJAC_A1_ONLY) && (flags&SMALLJAC_GROUP) ) return SMALLJAC_INVALID_FLAGS;
129
if ( end < start || end > SMALLJAC_MAX_END ) return SMALLJAC_INVALID_INTERVAL;
130
qc = (smalljac_Qcurve *)curve;
131
132
// Check that curve genus matches compiled genus - this restriction should go away eventually
133
if ( qc->genus < 4 ) {
134
if ( qc->genus != SMALLJAC_GENUS ) return SMALLJAC_WRONG_GENUS;
135
} else {
136
if ( ! (flags&SMALLJAC_A1_ONLY) ) return SMALLJAC_WRONG_GENUS;
137
}
138
139
good_only = (flags&SMALLJAC_GOOD_ONLY);
140
filter = (flags&SMALLJAC_FILTER);
141
142
// Precompute delta values for pointcounting, if needed
143
if ( (smalljac_Qcurve_degree(qc)==6||start <= SMALLJAC_COUNT_P) && ! (qc->flags&SMALLJAC_QCURVE_DELTA) ) {
144
smalljac_Qcurve_init_Deltas(qc);
145
pointcount_precompute (qc->Deltas, qc->f, qc->degree);
146
qc->flags |= SMALLJAC_QCURVE_DELTA;
147
}
148
149
pbitmask = (flags&SMALLJAC_SPLIT)>>SMALLJAC_SPLIT_SHIFT;
150
pbits = (flags&SMALLJAC_HIGH)>>SMALLJAC_HIGH_SHIFT;
151
// First deal with tiny primes
152
if ( start <= SMALLJAC_TINY_P ) {
153
if ( ! start ) p = 2; else p = ui_small_prime(ui_small_prime_index(start-1)+1); // set start to first prime >= start
154
for ( ; p <= SMALLJAC_TINY_P ; p = ui_small_prime(ui_small_prime_index(p)+1) ) {
155
if ( p > end ) return end;
156
if ( (p&pbitmask) != pbits ) continue;
157
if ( filter && ! (*callback) (curve, p, -1, 0, 0, arg) ) continue;
158
qc->n = smalljac_tiny_Lpoly (qc->a, qc, qc->p=p, flags); // note tiny_Lpoly handles bad reduction check
159
if ( qc->n < 0 ) return SMALLJAC_INTERNAL_ERROR;
160
if ( qc->n > 0 || ! good_only ) {
161
if ( ! (*callback) (curve, p, (qc->n > 0 ? 1 : 0), qc->a, qc->n, arg) ) return p;
162
}
163
}
164
if ( p > end ) return end;
165
start = p;
166
}
167
error = 0;
168
// use fast prime enumeration (based on a wheeled sieve) if possible - we should extend this to cover all cases
169
// the code duplication below is intentional
170
if ( end <= MPZ_MAX_ENUM_PRIME ) {
171
ctx = fast_prime_enum_start (start, end, 0);
172
while ( (p = fast_prime_enum(ctx)) ) {
173
if ( (p&pbitmask) != pbits ) continue;
174
if ( filter && ! (*callback) (curve, p, -1, 0, 0, arg) ) continue;
175
if ( mpz_divisible_ui_p (qc->D,p) ) {
176
if ( ! good_only ) if ( ! (*callback) (curve, p, 0, 0, 0, arg) ) break;
177
continue;
178
}
179
qc->n = smalljac_internal_Lpoly (qc->a, qc, qc->p=p, flags);
180
if ( qc->n <= 0 ) { error = 1; break; }
181
if ( ! (*callback) (curve, p, 1, qc->a, qc->n, arg) ) break;
182
}
183
fast_prime_enum_end (ctx);
184
if ( ! p ) p = end;
185
} else {
186
// using nextprime here is a very slow -- not an issue in genus >1, but in genus 1 we could do better by extending fast prime enum code
187
mpz_set_ui (P, start-1);
188
for ( mpz_nextprime (P,P) ; (p = mpz_get_ui(P)) <= end ; mpz_nextprime (P,P) ) {
189
if ( (p&pbitmask) != pbits ) continue;
190
if ( filter && ! (*callback) (curve, p, -1, 0, 0, arg) ) continue;
191
if ( mpz_divisible_ui_p (qc->D,p) ) {
192
if ( ! good_only ) if ( ! (*callback) (curve, p, 0, 0, 0, arg) ) break;
193
continue;
194
}
195
qc->n = smalljac_internal_Lpoly (qc->a, qc, qc->p=p, flags);
196
if ( qc->n <= 0 ) { error = 1; break; }
197
if ( ! (*callback) (curve, p, 1, qc->a, qc->n, arg) ) break;
198
}
199
if ( p > end ) p = end;
200
}
201
if ( error ) { printf ("smalljac internal error at p=%lu\n", p); return SMALLJAC_INTERNAL_ERROR; }
202
return (long)p;
203
}
204
205
/*
206
smalljac_internal_Lpoly assumes p > qc->degree and good reduction.
207
It computes the coefficients of L_p(T) for the curve specified by F/denom and degree.
208
The array D0 holds precomputed delta values used for pointcounting which will be
209
used to determine a1 if p < SMALLJAC_COUNT_P.
210
211
If p >= SMALLJAC_PADIC_P, a p-adic computation will be used to compute the
212
coefficients of L_p(T) modulo p (or p^N in genus > 3), using Harvey's frobenius
213
code, followed by a generic group computation to get the exact function.
214
215
Otherwise, a generic computation is performed, possibly using the a1 value
216
obtained from pointcounting.
217
218
The return value is either 0 (error) or the number of coefficients computed.
219
If a1_only is set AND if pointcounting is performed (not necessarily the case) then
220
this will be 1, otherwise it will be g. Note that for large p it may be more efficient
221
to compute a1 via a method that computes all the coefficients rather than
222
pointcounting, in this case all the coefficients are returned (may as well...).
223
*/
224
225
int smalljac_internal_Lpoly (long a[], smalljac_Qcurve *qc, unsigned long p, unsigned long flags)
226
{
227
curve_t c;
228
229
qc->pts = 0;
230
if ( qc->genus >2 && (flags&SMALLJAC_A1_ONLY) ) {
231
qc->pts = smalljac_pointcount (qc, p);
232
if ( ! qc->pts ) return 0;
233
a[0] = (long)qc->pts - (long)(p+1);
234
return 1;
235
}
236
// avoid requiring p-adic code to be compiled in genus < 3
237
#if SMALLJAC_GENUS > 2
238
if ( p >= SMALLJAC_PADIC_P ) return smalljac_padic_Lpoly (a, qc, p, flags);
239
#endif
240
241
// invoke special code for y^2=x^6+a curves
242
if ( !(flags&SMALLJAC_GROUP) && smalljac_Qcurve_special(qc) == SMALLJAC_SPECIAL_X6PA ) return smalljac_x6pa_Lpoly (a, qc, p, flags);
243
244
if ( smalljac_Qcurve_degree(qc)==6 || p <= SMALLJAC_COUNT_P ) {
245
qc->pts = smalljac_pointcount (qc, p);
246
if ( ! qc->pts ) { printf ("smalljac_pointcount failed at p=%lu\n", p); return 0; }
247
if ( (flags&SMALLJAC_A1_ONLY) ) { a[0] = (long)qc->pts - (long)(p+1); return 1; }
248
if ( qc->genus == 1 ) { // in genus 1, we're basically done, but we might need to do a group structure computation
249
// this code is used in genus 1 only for very small p, in fact if SMALLJAC_COUNT_P <= SMALLJAC_TINY_P we never get here
250
if ( ! (flags&SMALLJAC_GROUP) ) { a[0] = (long)qc->pts - (long)(p+1); return 1; }
251
ff_setup_ui (p);
252
if ( ! ff_poly_set_rational_mpz (c.f, qc->f, (c.d=qc->degree), qc->denom) ) return 0;
253
return hecurve_g1_group_structure (a,qc->pts,0,c.f); // we don't have any torsion info so specify d=0
254
}
255
}
256
return smalljac_generic_Lpoly (a, qc, p, qc->pts, flags);
257
}
258
259
260
unsigned long smalljac_pointcount (smalljac_Qcurve *qc, unsigned long p)
261
{
262
unsigned long Deltaf0[SMALLJAC_MAX_DEGREE+1];
263
264
if ( ! ui_poly_set_rational_mpz_mod_p (Deltaf0, qc->Deltas, qc->degree, qc->denom, p) ) return 0;
265
if ( p < SMALLJAC_BIG_COUNT_P ) {
266
switch (qc->degree) {
267
case 3: return pointcount_g1 (Deltaf0,p);
268
case 5: return pointcount_g2 (Deltaf0,p);
269
case 6:
270
if ( mpz_cmp_ui(qc->denom,1) != 0 ) { err_printf ("Non-integral coefficients in degree 6 poly unexpected, denom=%Zd\n",qc->denom); return 0; }
271
return pointcount_g2d6 (Deltaf0,p,mpz_fdiv_ui(qc->f[6],p));
272
case 7: return pointcount_g3 (Deltaf0,p);
273
case 9: return pointcount_g4 (Deltaf0,p);
274
}
275
} else {
276
switch (qc->degree) {
277
case 3: return pointcount_big_g1 (Deltaf0,p);
278
case 5: return pointcount_big_g2 (Deltaf0,p);
279
case 6:
280
if ( mpz_cmp_ui(qc->denom,1) != 0 ) { printf ("Non-integral coefficients in degree 6 poly unexpected\n"); return 0; }
281
return pointcount_big_g2d6 (Deltaf0,p,mpz_fdiv_ui(qc->f[6],p));
282
case 7: return pointcount_big_g3 (Deltaf0,p);
283
case 9: return pointcount_big_g4 (Deltaf0,p);
284
}
285
}
286
return 0;
287
}
288
289
// Computes L-poly coefficients or group structure (or both) using generic group algorithms
290
// Returns 0 for errors, otherwise the number of entries in a which will be g for L-poly coefficients, or the rank of the group.
291
int smalljac_generic_Lpoly (long a[], smalljac_Qcurve *qc, unsigned long p, unsigned long pts, unsigned long flags)
292
{
293
curve_t c, twist;
294
unsigned long Min, Max;
295
unsigned long e, te, o, P1, PN1;
296
unsigned long m, gops;
297
long a1, d, tcon;
298
int constraints[SMALLJAC_MAX_CONSTRAINTS+1];
299
int i, j, k, tk, n, sts;
300
double x, t;
301
302
ff_setup_ui (p);
303
if ( ! ff_poly_set_rational_mpz (c.f, qc->f, (c.d=qc->degree), qc->denom) ) return 0;
304
#if SMALLJAC_GENUS == 1
305
// Use faster genus 1 code in hecurve1x instead of general jac functions
306
if ( pts ) { a[0] = (long)pts - (long)(p+1); return qc->genus; }
307
P1 = hecurve_g1_order (&d,c.f);
308
if ( (flags&SMALLJAC_GROUP) ) {
309
return hecurve_g1_group_structure (a,P1,d,c.f);
310
} else {
311
a[0] = (long)(P1) - (long)(p+1);
312
return 1;
313
}
314
#endif
315
x = sqrt((double)p);
316
Min = (unsigned long) (floor(pow(x-1.0, 2.0*SMALLJAC_GENUS)));
317
Max = (unsigned long) (ceil(pow(x+1.0, 2.0*SMALLJAC_GENUS)));
318
if ( pts ) {
319
a1 = (long)pts - (long)(p+1);
320
} else {
321
a1 = JAC_INVALID_A1;
322
}
323
k = jac_order (&P1, Min, Max, a1, 0, &c, flags&SMALLJAC_SGROUP);
324
if ( k <= 0 ) return 0;
325
if ( k > SMALLJAC_MAX_CONSTRAINTS ) { printf ("%7lu: Exceeded SMALLJAC_MAX_CONSTRAINTS", p); return 0; }
326
#if SMALLJAC_GENUS == 2
327
if ( k > 1 ) { printf ("%lu: Ambiguous result in genus 2 not handled\n", p); return 0; } // should be impossible provided pointcounting is used for p < SMALLJAC_TINY_P
328
if ( (flags&SMALLJAC_GROUP) ) return jac_structure (a, &c, P1, flags&SMALLJAC_SGROUP);
329
if ( pts ) {
330
a[0] = (long)pts - (long)(p+1);
331
a[1] = (long)(P1) - ((long)p*p+1)-(long)(p+1)*a[0];
332
return qc->genus;
333
} else {
334
if ( ! smalljac_genus2_charpoly_from_P1 (a, P1, Min, Max, &c) ) {
335
// If we can't deduce the charpoly from P1 (very rare) go ahead and compute PN1 from scratch
336
// This is a bit silly, since we know the value of PN1 mod 2(p+1) and could use this knowledge to speed things up,
337
// but it happens so rarely that we don't bother
338
ff_poly_twist (twist.f, c.f, c.d);
339
twist.d = c.d;
340
k = jac_order (&PN1, Min, Max, JAC_INVALID_A1, 0, &twist, 0);
341
if ( k <= 0 ) { printf ("%lu: Attempted twist order computation failed\n", p); return 0; }
342
if ( k > 1 ) { printf ("%lu: Ambiguous result in genus 2 not handled\n", p); return 0; } // should be impossible
343
if ( ! smalljac_genus2_charpoly (a, p, x, P1, PN1) ) { err_printf ("%lu: smalljac_genus2_charpoly failed\n", p); return 0; }
344
return qc->genus;
345
}
346
return qc->genus;
347
}
348
#endif
349
#if SMALLJAC_GENUS == 3
350
if ( k == 1 ) {
351
if ( (flags&SMALLJAC_GROUP) ) return jac_structure (a, &c, P1, flags&SMALLJAC_SGROUP);
352
return ( smalljac_genus3_charpoly_from_P1 (a, P1, pts, Min, Max, &c) ? qc->genus : 0 );
353
} else {
354
unsigned long unique_P1, unique_PN1;
355
356
ff_poly_twist (twist.f, c.f, c.d);
357
twist.d = c.d;
358
m = 2*(p+1);
359
e = P1;
360
P1 = _ui_ceil_ratio(Min,e)*e;
361
for ( i = 0 ; i < k ; i++ ) {
362
tcon = 2*(p*p*p+1) - P1;
363
constraints[i] = (tcon < 0 ? (int) (m - ((-tcon)%m)) : (int) (tcon%m) );
364
P1 += e;
365
}
366
constraints[k] = -1;
367
tk = jac_order (&PN1, Min, Max, (pts ? -a1 : JAC_INVALID_A1), constraints, &twist, 0);
368
if ( tk <= 0 ) { printf ("%lu: Attempted twist order computation failed\n", p); return 0; }
369
P1 = _ui_ceil_ratio(Min,e)*e;
370
te = PN1;
371
n = 0;
372
for ( i = 0 ; i < k ; i++ ) {
373
PN1 = _ui_ceil_ratio(Min,te)*te;
374
for ( j = 0 ; j < tk ; j++ ) {
375
sts = smalljac_genus3_charpoly (a, p, x, P1, PN1, pts);
376
if ( sts ) {
377
if ( ! n ) { unique_P1 = P1; unique_PN1 = PN1; }
378
n++;
379
}
380
PN1 += te;
381
}
382
P1 += e;
383
}
384
if ( n != 1 ) { printf ("%lu: Unable to resolve ambiguity, primary subgroup %lu, twist subgroup %lu, pts = %lu, n = %d, giving up\n", p, e, te, pts, n); return 0; }
385
if ( (flags&SMALLJAC_GROUP) ) return jac_structure (a, &c, unique_P1, 0);
386
return ( smalljac_genus3_charpoly (a, p, x, unique_P1, unique_PN1, pts) ? qc->genus : 0 );
387
}
388
#endif
389
err_printf ("Fell through to unreachable code - compiled genus %d\n", SMALLJAC_GENUS); exit (0);
390
}
391
392
#if SMALLJAC_GENUS > 2
393
// Computes Lpoly coefficients or group structure using p-adic computations to determine L_p(T) mod p
394
// by calling David Harvey's frobenius() function. If group structure is requested, uses #J(C/F) = L_p(1)
395
// to compute the group order. Returns 0 if an error occurs, the number of entries in a otherwise (either g or the rank of the group)
396
int smalljac_padic_Lpoly (long a[], smalljac_Qcurve *qc, unsigned long p, unsigned long flags)
397
{
398
curve_t c;
399
long f[SMALLJAC_MAX_DEGREE+1];
400
int i;
401
402
ff_setup_ui (p);
403
if ( ! ff_poly_set_rational_mpz (c.f, qc->f, qc->degree, qc->denom) ) return 0;
404
c.d = qc->degree;
405
// this is slightly annoying, but we need to convert to standard integers (mod p) and ff uses Montogemery
406
for ( i = 0 ; i <= qc->degree ; i++ ) f[i] = _ff_get_ui (c.f[i]);
407
if ( ! padic_charpoly (a, f, qc->degree, p) ) { err_printf ("%lu: padic_charpoly failed\n", p); return 0; }
408
switch ( qc->genus) {
409
case 2:
410
if ( ! smalljac_genus2_charpoly_from_Pmodp (a, &c) ) return 0;
411
break;
412
case 3:
413
if ( ! smalljac_genus3_charpoly_from_Pmodp (a, &c) ) return 0;
414
break;
415
default:
416
err_printf ("Invalid genus %d in smalljac_padic_lpoly\n", qc->genus);
417
exit (0);
418
}
419
if ( (flags&SMALLJAC_GROUP) ) {
420
return jac_structure (a, &c, smalljac_Lp1_ui (a, qc->genus, p), flags&SMALLJAC_SGROUP);
421
} else {
422
return qc->genus;
423
}
424
}
425
#endif
426
427
// table of Lpoly a1 values for all possible genus 1 Q curve reductions over F_2. -9 denotes singular cases
428
// table is indexed by n = a1+2*a2+4*a3+8*a4+16*a6 with a_i reduced mod 2.
429
char ws2a1tab[32] = { -9, -9, -9, -9, 0, -9, 2, 1,
430
-9, 1, -9, -1, 2, -9, 0, 1,
431
-9, 1, -9, -1, 0, -1, -2, -9,
432
-9, -9, -9, -9, -2, -1, 0, -9 };
433
434
// Handle general Weierstrass case for p=3
435
int smalljac_Lpoly3_ws (long a[], smalljac_Qcurve *qc)
436
{
437
register int a1, a2, a3, a4, a6, x, y, z, f0, f1, f2, f3;
438
register unsigned ws;
439
register int i, pts;
440
441
pts = 1;
442
ws = qc->ws3;
443
a1 = ws&0x3; ws >>= 2; a2 = ws&0x3; ws >>= 2; a3 = ws&0x3; ws >>= 2; a4 = ws&0x3; ws >>= 2; a6 = ws&0x3;
444
f0 = a2+a1*a1; f1 = a4+2*a1*a3; f2 = a6+a3*a3; // f0 = c2', f1 = c4', f2 = c6' after completing the square in char 3
445
z = f0*f0*f1*f1 - f0*f0*f0*f2 - f1*f1*f1; // discriminant of transformed poly (in char 3)
446
if ( z%3 == 0 ) return 0;
447
for ( x = 0 ; x <= 2 ; x++ ) {
448
for ( y = 0 ; y <= 2 ; y++ ) {
449
z = x*x*x + a2*x*x + a4*x +a6 - y*y - a1*x*y - a3*y;
450
if ( z%3 == 0 ) pts++;
451
}
452
}
453
a[0] = pts - 4;
454
return 1;
455
}
456
457
// Computes either Lpoly coefficients or group structure for tiny values of p. Returns 0 for error, number of entries in a otherwise
458
int smalljac_tiny_Lpoly (long a[], smalljac_Qcurve *qc, int p, unsigned long flags)
459
{
460
unsigned long f[SMALLJAC_MAX_DEGREE+1];
461
curve_t c;
462
long P1,pts;
463
int i;
464
465
// handle special cases for 2 and 3 first
466
if ( p == 2 ) {
467
// note that except for genus 1 curves in general Weierstrass form, we only support curves y^2 = f(x) which all have bad reduction at 2
468
if ( ! (qc->flags&SMALLJAC_QCURVE_WS) ) return 0;
469
if ( ws2a1tab[qc->ws2] < -2 ) return 0;
470
a[0] = ws2a1tab[qc->ws2];
471
if ( (flags&SMALLJAC_GROUP) ) a[0] += 3; // Happily, the Jacobian over F_2 is always cyclic
472
return 1;
473
} else if ( p == 3 && (qc->flags&SMALLJAC_QCURVE_WS) ) {
474
// there are 8 reduced general Weierstrass forms with non-cyclic groups over F_3 (Z_2 x Z_2 is the only possibility)
475
// we handle these cases explicitly, and otherwise assume the group is cyclic.
476
if ( (flags&SMALLJAC_GROUP) ) {
477
switch (qc->ws3) {
478
case 0x22a: // [2,2,2,0,2] note the encoding is a1 in bits 0-1, a2 in bits 2-3, ..., a6 in bits 8-9.
479
case 0x219: // [1,2,1,0,2]
480
case 0x269: // [1,2,2,1,2]
481
case 0x25a: // [2,2,1,1,2]
482
case 0x8a: // [2,2,0,2,0]
483
case 0x89: // [1,2,0,2,0]
484
case 0x290: // [0,0,1,2,2]
485
case 0x2a0: // [0,0,2,2,2]
486
case 0x80: // [0,0,0,2,0]
487
a[0] = a[1] = 2; return 2;
488
}
489
}
490
if ( ! smalljac_Lpoly3_ws (a, qc) ) return 0;
491
if ( (flags&SMALLJAC_GROUP) ) a[0] += 4;
492
return 1;
493
}
494
// Check reduction at p - we need to be careful here, since we may have modified the curve to clear the 2g coefficient
495
// leaving the curve undefined over F_p if p divides (2g+1), even though the original curve may have had good reduction at p
496
if ( /*(qc->flags & SMALLJAC_QCURVE_2G) &&*/ !(qc->degree%p) ) { // actually, we may as well always do this and not worry about setting SMALLJAC_QCURVE_2G
497
if ( mpz_divisible_ui_p(qc->disc, p) ) return 0;
498
if ( ! ui_poly_expr_mod_p (f, qc->degree, qc->str, p) ) return 0; // use original curve string reduced mod p
499
} else {
500
if ( mpz_divisible_ui_p(qc->D, p) ) return 0;
501
if ( ! ui_poly_set_rational_mpz_mod_p (f, qc->f, qc->degree, qc->denom, p) ) return 0;
502
}
503
qc->pts = pointcount_tiny (f, qc->degree, p);
504
if ( qc->genus == 1 ) {
505
if ( (flags&SMALLJAC_GROUP) ) {
506
ff_setup_ui (p);
507
for ( i = 0, c.d = qc->degree ; i <= c.d ; i++ ) _ff_set_ui (c.f[i], f[i]);
508
return hecurve_g1_group_structure (a,qc->pts,0,c.f); // we don't have any torsion info so specify d=0
509
}
510
a[0] = qc->pts-p-1;
511
return 1;
512
}
513
a[0] = qc->pts-p-1;
514
if ( (flags&SMALLJAC_A1_ONLY) ) return 1;
515
pts = pointcount_tiny_d2 (f, qc->degree, p);
516
a[1] = (pts - p*p - 1+a[0]*a[0]) / 2;
517
if ( qc->genus == 2 ) {
518
if ( (flags&SMALLJAC_GROUP) ) {
519
P1 = smalljac_Lp1_ui (a, qc->genus, p);
520
ff_setup_ui (p);
521
for ( i = 0, c.d = qc->degree ; i <= c.d ; i++ ) _ff_set_ui (c.f[i], f[i]);
522
return jac_structure (a, &c, P1, flags&SMALLJAC_SGROUP);
523
} else {
524
return 2;
525
}
526
}
527
528
pts = pointcount_tiny_d3 (f, qc->degree, p);
529
a[2] = (pts - (long)p*p*p - 1 - a[0]*a[0]*a[0] + 3*a[0]*a[1]) / 3;
530
if ( qc->genus == 3 ) {
531
if ( (flags&SMALLJAC_GROUP) ) {
532
P1 = smalljac_Lp1_ui (a, qc->genus, p);
533
ff_setup_ui (p);
534
for ( i = 0, c.d = qc->degree ; i <= c.d ; i++ ) _ff_set_ui (c.f[i], f[i]);
535
return jac_structure (a, &c, P1, flags&SMALLJAC_SGROUP);
536
} else {
537
return 3;
538
}
539
}
540
err_printf ("Unhandled genus in smalljac_Lpoly_tiny for p=%d\n", p);
541
return 0;
542
}
543
544
545
// Computes either Lpoly coefficients for curves y^2=x^6+a, doesn't compute group structure
546
int smalljac_x6pa_Lpoly (long a[], smalljac_Qcurve *qc, long p, unsigned long flags)
547
{
548
long x, y, k, m, n, ap, a2;
549
int i, split;
550
ff_t s, t, u;
551
ff_t f[7];
552
553
// f(x) must be of the form x^6+a
554
if ( qc->degree != 6 ) { err_printf ("Invalid curve degree in smalljac_x6pa_Lpoly\n"); return 0; }
555
if ( mpz_cmp_ui(qc->f[6],1) != 0 ) { err_printf ("Invalid curve in smalljac_x6pa_Lpoly\n"); return 0; }
556
for ( i = 1 ; i < 6 ; i++ ) if ( mpz_sgn(qc->f[i]) ) { err_printf ("Invalid curve in smalljac_x6pa_Lpoly\n"); return 0; }
557
558
// If p is not 1 mod 3, the Hasse-witt matrix is 0, so a_1=a_2=0 mod p, and the curve is supersingular
559
// by Thm 6.1 of Galbraith "Supersingular curves in cryptography", and the L-poly must be (pT^2+1)^2.
560
if ( p%3 != 1 ) { a[0] = 0; a[1] = 2*p; return 2; }
561
562
// p = 6m+1
563
// the Hasse Witt matrix is diagonal, with entries binom(3m,m)a^{2m} and binom(3m,m)a^m
564
// we compute (3m,m) by applying formula 9.1 (p. 453) of "Binomial coefficients and Jacobi Sums"
565
// by Hudson and Williams.
566
ff_setup_ui (p);
567
if ( ! cornacchia (&x, &y, p, 3) ) { printf ("%d is not a prime of the form x^2 + ny^2\n", p); return 0; }
568
if ( x*x+3*y*y != p ) { printf ("cornacchia failed: %d != %d^2 +3*d^2\n", p, x, y); return 0; }
569
if ( (x%3) != 1 ) x = -x; // make sure x is 1 mod 3
570
// binom(3m,m) = 2x
571
_ff_set_i (u, mpz_get_i(qc->f[0]));
572
k = (p-1)/6;
573
ff_exp_ui (&s, &u, k);
574
_ff_square (t, s);
575
_ff_set_i (u, x);
576
_ff_x2 (u);
577
_ff_mult(s,s,u);
578
_ff_mult(t,t,u);
579
_ff_add(u,s,t);
580
ap = _ff_get_ui(u);
581
_ff_mult(u,s,t);
582
a2 = _ff_get_ui(u);
583
if ( ap > p/2 ) ap -= p;
584
// We now know the L-poly mod p, we just need to nail a_2
585
586
m = 2*p + (ap*ap)/4;
587
if ( m > a2 ) m -= (m-a2)%p; else m -= p - ((a2-m)%p);
588
n = p*p+1-(p+1)*ap;
589
if ( mpz_cmp_ui(qc->f[0],1)==0 ) {
590
k=0;
591
} else {
592
_ff_set_one (f[6]); _ff_set_mpz(f[0],qc->f[0]);
593
for ( i = 1 ; i < 6 ; i++ ) _ff_set_zero (f[i]);
594
k = ( ff_poly_factors (f, 6) > 2 ? 0 : 3); // NOTE: 3-3 split does NOT yield 2-torsion. 1-5 and 2-4 splits can't happen
595
}
596
597
// We now know Lp(1)=k mod 6 which uniquely determines a_2
598
599
while ( (m+n-k)%6 ) m -= p;
600
a[0] = -ap;
601
a[1] = m;
602
return 2;
603
}
604
605
606
607
smalljac_Qcurve *smalljac_Qcurve_alloc (void)
608
{
609
smalljac_Qcurve *qc;
610
611
qc = mem_alloc (sizeof(*qc));
612
mpz_init (qc->disc);
613
mpz_init (qc->denom);
614
mpz_init (qc->D);
615
return qc;
616
}
617
618
619
smalljac_Qcurve_t smalljac_Qcurve_init (char *str, int *err)
620
{
621
smalljac_Qcurve *qc;
622
623
qc = smalljac_Qcurve_alloc();
624
if ( ! smalljac_Qcurve_set_str (qc, str, err) ) { smalljac_Qcurve_clear ((smalljac_Qcurve_t)qc); return (smalljac_Qcurve_t)0; }
625
return (smalljac_Qcurve_t) qc;
626
}
627
628
629
int smalljac_Qcurve_set_str (smalljac_Qcurve *qc, char *str, int *err)
630
{
631
static mpz_t W[5];
632
static mpq_t F[SMALLJAC_MAX_DEGREE+1], DQ;
633
static int init;
634
int i, degree, ws;
635
636
if ( ! init ) { smalljac_init(); for ( i = 0 ; i < 5 ; i++ ) mpz_init (W[i]); for ( i = 0 ; i <= SMALLJAC_MAX_DEGREE ; i++ ) mpq_init (F[i]); mpq_init(DQ); init = 1; }
637
if ( err ) *err = 0;
638
if ( strlen(str)+1 > sizeof(qc->str) ) { if ( err ) *err = SMALLJAC_PARSE_ERROR; return 0; }
639
strcpy (qc->str, str);
640
641
degree = poly_expr_degree (str, &ws);
642
if ( degree > SMALLJAC_MAX_DEGREE ) { if ( err ) *err = SMALLJAC_UNSUPPORTED_CURVE; return 0; }
643
644
qc->flags = qc->ws2 = qc->ws3 = 0;
645
646
if ( ws ) {
647
if ( gmp_sscanf (str, "[%Zd,%Zd,%Zd,%Zd,%Zd]", W[0], W[1], W[2], W[3], W[4]) != 5 ) { if ( err ) *err = SMALLJAC_PARSE_ERROR; return 0; }
648
mpq_poly_weierstrass (F, W);
649
for ( i = 4 ; i >= 0 ; i-- ) {
650
if ( i < 4 ) { qc->ws2 <<= 1; qc->ws3 <<= 2; }
651
qc->ws2 |= mpz_tstbit(W[i],0);
652
qc->ws3 |= mpz_fdiv_ui (W[i],3);
653
}
654
qc->flags |= SMALLJAC_QCURVE_WS;
655
} else {
656
if ( mpq_poly_expr (F, degree, str) != degree ) { if ( err ) *err = SMALLJAC_PARSE_ERROR; return 0; }
657
if ( (degree&1) && mpq_poly_standardize (F, degree) ) qc->flags |= SMALLJAC_QCURVE_2G; // note if curve was modified to make 2g coefficient zero - only used for odd degree
658
}
659
660
// Filter out unsupported curves here rather than above so we can be a little more informative about parsing errors
661
if ( degree < 3 || (degree!=6 && ! (degree&1)) ) { if ( err ) *err = SMALLJAC_UNSUPPORTED_CURVE; return 0; } // degree 6 is currently only supported even degree
662
smalljac_Qcurve_init_f (qc, degree);
663
mpq_poly_discriminant (DQ, F, degree);
664
if ( ! mpq_sgn (DQ) ) {if ( err ) *err = SMALLJAC_SINGULAR_CURVE; return 0; }
665
mpz_poly_set_mpq (qc->denom, qc->f, F, degree);
666
mpz_set (qc->disc, mpq_numref(DQ));
667
mpz_mul (qc->D, qc->denom, qc->disc); // could use lcm here
668
if ( degree == 6 ) {
669
if ( mpz_cmp_ui(qc->f[6],1)==0 ) {
670
for ( i = 1 ; i < 6 ; i++ ) if ( mpz_sgn(qc->f[i]) ) break;
671
if ( i == 6 ) qc->special = SMALLJAC_SPECIAL_X6PA;
672
}
673
}
674
return 1;
675
}
676
677
char *smalljac_Qcurve_str (smalljac_Qcurve_t curve) { return ((smalljac_Qcurve*)curve)->str; }
678
int smalljac_Qcurve_genus (smalljac_Qcurve_t curve) { return ((smalljac_Qcurve*)curve)->genus; }
679
680
int smalljac_Qcurve_set_mpz (smalljac_Qcurve *qc, mpz_t f[], int degree, mpz_t denom, mpz_t disc, char *str)
681
{
682
char *t;
683
int i;
684
685
if ( degree < 3 || degree > SMALLJAC_MAX_DEGREE || (! (degree&1)&&degree!=6) || ! mpz_sgn(denom) || ! mpz_sgn(disc) ) return 0;
686
qc->flags = qc->ws2 = qc->ws3 = 0;
687
smalljac_Qcurve_init_f (qc, degree);
688
for ( i = 0 ; i <= degree ; i++ ) mpz_set (qc->f[i], f[i]);
689
mpz_set (qc->disc, disc);
690
mpz_set (qc->denom, denom);
691
mpz_mul (qc->D, disc, denom);
692
if ( degree == 6 ) {
693
if ( mpz_cmp_ui(f[6],1)==0 ) {
694
for ( i = 1 ; i < 6 ; i++ ) if ( mpz_sgn(f[i]) ) break;
695
if ( i == 6 ) qc->special = SMALLJAC_SPECIAL_X6PA;
696
}
697
}
698
// trust that str actually matches the curve - the idea is to avoid recomputing the discriminant - may not be worth the trouble...
699
strcpy (qc->str, str);
700
return 1;
701
}
702
703
int smalljac_Qcurve_set_i (smalljac_Qcurve *qc, long f[], int degree, char *str)
704
{
705
char *t;
706
int i;
707
708
if ( degree < 3 || degree > SMALLJAC_MAX_DEGREE || (! (degree&1)&&degree!=6) ) return 0;
709
qc->flags = qc->ws2 = qc->ws3 = 0;
710
smalljac_Qcurve_init_f (qc, degree);
711
for ( i = 0 ; i <= degree ; i++ ) mpz_set_i (qc->f[i], f[i]);
712
mpz_set_ui (qc->denom, 1);
713
mpz_poly_discriminant (qc->disc, qc->f, degree);
714
mpz_mul (qc->D, qc->disc, qc->denom);
715
if ( degree == 6 ) {
716
if ( mpz_cmp_ui(qc->f[6],1)==0 ) {
717
for ( i = 1 ; i < 6 ; i++ ) if ( mpz_sgn(qc->f[i]) ) break;
718
if ( i == 6 ) qc->special = SMALLJAC_SPECIAL_X6PA;
719
}
720
}
721
strcpy (qc->str, str);
722
return 1;
723
}
724
725
void smalljac_Qcurve_clear (smalljac_Qcurve_t curve)
726
{
727
smalljac_Qcurve *qc;
728
int i;
729
730
qc = (smalljac_Qcurve *)curve;
731
mpz_clear (qc->D);
732
mpz_clear (qc->denom);
733
for ( i = 0 ; i < qc->f_inits ; i++ ) mpz_clear (qc->f[i]);
734
for ( i = 0 ; i < qc->Delta_inits ; i++ ) mpz_clear (qc->Deltas[i]);
735
mem_free (qc);
736
}
737
738
739
int smalljac_Lpoly_extend (long a[], int n, unsigned long p, int h)
740
{
741
register long t, t1, tn, tnm1;
742
register int i;
743
744
if ( h < 1 ) return 0;
745
if ( h == 1 ) return 1;
746
switch (n) {
747
case 1:
748
tnm1 = t1 = -a[0]; tn = t1*t1-2*(long)p;
749
for ( i = 2 ; i < h ; i++ ) { t = t1*tn - p*tnm1; tnm1 = tn; tn = t; }
750
a[0] = -tn;
751
break;
752
default:
753
return 0; // add support for genus > 1 later
754
}
755
return 1;
756
}
757
758
// implementation of Cornacchia's algorithm to find (x,y) s.t. x^2+dy^2 = p see Cohen, Alg. 1.5.2
759
// returns 0 if no solution exists.
760
int cornacchia (long *x, long *y, long p, long d)
761
{
762
ff_t k, t;
763
long a, b, c, r, L;
764
765
_ff_set_i (t, -d);
766
if ( ! ff_sqrt (&k, &t) ) return 0;
767
b = _ff_get_ui(k);
768
if ( b < (p>>1) ) b = p-b;
769
a = p;
770
L = sqrt(p);
771
while ( b > L ) { r = a%b; a=b; b=r; }
772
r = p-b*b;
773
c = r/d;
774
if ( c*d != r ) return 0;
775
if ( (*y=ui_sqrt(c)) < 0 ) return 0;
776
*x=b;
777
return 1;
778
}
779
780