Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
241782 views
1
/*********************************************************************
2
3
(c) Copyright 2006-2010 Salman Baig and Chris Hall
4
5
This file is part of ELLFF
6
7
ELLFF is free software: you can redistribute it and/or modify
8
it under the terms of the GNU General Public License as published by
9
the Free Software Foundation, either version 3 of the License, or
10
(at your option) any later version.
11
12
ELLFF is distributed in the hope that it will be useful,
13
but WITHOUT ANY WARRANTY; without even the implied warranty of
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
GNU General Public License for more details.
16
17
You should have received a copy of the GNU General Public License
18
along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20
*********************************************************************/
21
22
//
23
// C++ elliptic curve class
24
//
25
// this code offers basic routines for elliptic curves over a finite field.
26
// currently it is limited to curves with a model of the form
27
// y^2=x^3+a4*x+a6, which excludes characteristic two and some curves in
28
// characteristic three. because the latter two characteristics present
29
// other difficulties for the L-function code, we felt it was ok to make
30
// this exclusion.
31
//
32
// our class is modeled after NTL classes such as the finite field class
33
// ZZ_pE. in particular, there is a global elliptic curve which is assumed
34
// to be defined over NTL's global finite field, and one must use context
35
// loading and unloading in order to switch between curves and finite
36
// fields. on the one hand, the SAGE people assure us that this has proven
37
// to be a source of many bugs because one must be very careful about
38
// switching to the appropriate context. on the other hand, we do not want
39
// to rewrite our code using a library other than NTL, so we will have to
40
// deal with this issue regardless of whether or not our own classes use
41
// the model.
42
//
43
// this code is *not* meant to provide full elliptic curve functionality,
44
// rather merely what we need to perform point counting. this means that
45
// we have code for computing P+Q and [m]P for points P,Q and an integer m,
46
// but these routines are only mildly optimized. our experience is that
47
// for a fixed finite field, one really only needs to use this code to
48
// compute the Euler factors for a (semi-)universal family over the j-line,
49
// and then to regard any other family as a combined pullback and quadratic
50
// twist. the point is that the Euler factors for the latter kind of curve
51
// can be computed without any elliptic curve arithmetic given the Euler
52
// factors for the former, and in particular, one only needs the elliptic
53
// curve code when getting started over a new finite field.
54
//
55
56
#include <assert.h>
57
#include <string.h>
58
#include <NTL/lzz_p.h>
59
#include <NTL/lzz_pE.h>
60
#include <NTL/lzz_pX.h>
61
#include <NTL/lzz_pXFactoring.h>
62
#include <NTL/lzz_pX.h>
63
64
NTL_CLIENT
65
66
#include "ell.h"
67
#include "helper.h"
68
#include "lzz_pEExtra.h"
69
70
#define INV_TABLE 1 // precompute a table of inverses in F_q^*?
71
#define X_ORDER 1 // order points by x-coordinates first? (then y?)
72
73
///////////////////////////////////////////////////////////////////////////
74
// elliptic curve parameter class
75
// - we keep track of the coeffs a4,a6, the order of the finite field,
76
// and a table of inverses for F_q^*
77
///////////////////////////////////////////////////////////////////////////
78
79
// constructor
80
81
ell_pEInfoT::ell_pEInfoT(const zz_pE& new_a4, const zz_pE& new_a6)
82
{
83
ref_count = 1;
84
85
a4 = new_a4;
86
a6 = new_a6;
87
q = to_long(zz_pE::cardinality());
88
}
89
90
// destructor
91
92
ell_pEInfoT::~ell_pEInfoT()
93
{
94
}
95
96
// copied from NTL
97
98
static void CopyPointer(ell_pEInfoPtr& dst, ell_pEInfoPtr src)
99
{
100
if (src == dst) return;
101
102
if (dst) {
103
dst->ref_count--;
104
105
if (dst->ref_count < 0)
106
Error("internal error: negative ell_pEContext ref_count");
107
108
if (dst->ref_count == 0) delete dst;
109
}
110
111
if (src) {
112
if (src->ref_count == NTL_MAX_LONG)
113
Error("internal error: ell_pEContext ref_count overflow");
114
115
src->ref_count++;
116
}
117
118
dst = src;
119
}
120
121
////////////////////////////////////////////////////////////
122
// context switching class, shamelessly copied from NTL
123
////////////////////////////////////////////////////////////
124
125
ell_pEInfoPtr ell_pEInfo = NULL; // info for current elliptic curve
126
127
ell_pEContext::ell_pEContext(const zz_pE& a4, const zz_pE& a6)
128
{
129
ptr = new ell_pEInfoT(a4, a6);
130
}
131
132
ell_pEContext::ell_pEContext(const ell_pEContext& a)
133
{
134
ptr = NULL;
135
CopyPointer(ptr, a.ptr);
136
}
137
138
ell_pEContext& ell_pEContext::operator=(const ell_pEContext& a)
139
{
140
CopyPointer(ptr, a.ptr);
141
return *this;
142
}
143
144
ell_pEContext::~ell_pEContext()
145
{
146
CopyPointer(ptr, NULL);
147
}
148
149
void ell_pEContext::save()
150
{
151
CopyPointer(ptr, ell_pEInfo);
152
}
153
154
void ell_pEContext::restore() const
155
{
156
CopyPointer(ell_pEInfo, ptr);
157
}
158
159
//////////////////////////////
160
161
void ell_pE::init(zz_pE& a4, zz_pE& a6)
162
{
163
ell_pEContext c(a4, a6);
164
c.restore();
165
}
166
167
//////////////////////////////
168
169
ellpoint_pE::ellpoint_pE()
170
{
171
x = 0;
172
y = 0;
173
identity_f = 1;
174
}
175
176
ellpoint_pE::ellpoint_pE(const zz_pE& new_x, const zz_pE& new_y)
177
{
178
x = new_x;
179
y = new_y;
180
identity_f = 0;
181
}
182
183
// P + Q = R
184
185
void add(ellpoint_pE& R, const ellpoint_pE& P, const ellpoint_pE& Q)
186
{
187
// temporary variables for calculation
188
static zz_pE *Rx = NULL, *Ry = NULL, *Rz = NULL, *Ru = NULL;
189
static zz_pE *lambda = NULL;
190
191
static long q = -1;
192
193
// reuse point from previous invocation if possible
194
if (ell_pEInfo->q != q) {
195
q = ell_pEInfo->q;
196
if (lambda != NULL) {
197
delete lambda;
198
delete Rx;
199
delete Ry;
200
delete Rz;
201
delete Ru;
202
}
203
// NB: the only way the following are deallocated is if they
204
// are replaced during a later invocation (see previous
205
// lines), so technically this is a tiny memory leak
206
lambda = new zz_pE;
207
Rx = new zz_pE;
208
Ry = new zz_pE;
209
Rz = new zz_pE;
210
Ru = new zz_pE;
211
}
212
213
// check if either is identity
214
if (P.identity_f) { R = Q; return; }
215
if (Q.identity_f) { R = P; return; }
216
217
// calculate slope between lines
218
// - special cases arise when P=+/-Q
219
if (P.x == Q.x) {
220
if (P.y != Q.y || P.y == 0) {
221
R.set();
222
return;
223
}
224
225
// point-doubling
226
if (zz_pEExtraInfo->inv_table != NULL) {
227
// lambda = (3*x(P)^2 + a4)/(2*y(P))
228
sqr(*Rx, P.x);
229
add(*Ry, *Rx, *Rx);
230
add(*Rz, *Rx, *Ry);
231
add(*Rx, *Rz, ell_pEInfo->a4);
232
add(*lambda, P.y, P.y);
233
*Ru = zz_pEExtraInfo->inv_table->table[to_ulong(*lambda)];
234
mul(*lambda, *Rx, *Ru);
235
} else {
236
*lambda = (3*sqr(P.x) + ell_pEInfo->a4) / (2*P.y);
237
}
238
} else {
239
if (zz_pEExtraInfo->inv_table != NULL) {
240
// lambda = (y(P)-y(Q))/(x(P)-x(Q))
241
sub(*Rz, P.x, Q.x);
242
*Ru = zz_pEExtraInfo->inv_table->table[to_ulong(*Rz)];
243
sub(*Rz, P.y, Q.y);
244
mul(*lambda, *Ru, *Rz);
245
} else {
246
*lambda = (P.y - Q.y) / (P.x - Q.x);
247
}
248
}
249
250
// lambda = slope of line between P,Q(,and -R)
251
252
// x(R) = lambda^2 - x(P) - x(Q)
253
sqr(*Ru, *lambda);
254
sub(*Rz, *Ru, P.x);
255
sub(*Rx, *Rz, Q.x);
256
257
// y(R) = lambda*(x(P) - x(R)) - y(P) = -(lambda*(x(R)-x(P))+y(P))
258
sub(*Ru, P.x, *Rx);
259
mul(*Rz, *Ru, *lambda);
260
sub(*Ry, *Rz, P.y);
261
262
// set coordinates of R according to above
263
R.set(*Rx, *Ry);
264
}
265
266
void neg(ellpoint_pE& Q, const ellpoint_pE& P)
267
{
268
Q.set(P.x, -P.y);
269
}
270
271
// P - Q = R
272
273
void sub(ellpoint_pE& R, const ellpoint_pE& P, const ellpoint_pE& Q)
274
{
275
static ellpoint_pE T;
276
neg(T, Q);
277
add(R, P, T);
278
}
279
280
// addition/subtraction
281
ellpoint_pE operator+(const ellpoint_pE& P, const ellpoint_pE& Q)
282
{ ellpoint_pE x; add(x, P, Q); return x; }
283
284
ellpoint_pE operator-(const ellpoint_pE& P, const ellpoint_pE& Q)
285
{ ellpoint_pE x; sub(x, P, Q); return x; }
286
287
// negation
288
ellpoint_pE operator-(const ellpoint_pE& P)
289
{ ellpoint_pE x; x = P; if (x.identity_f == 0) x.y = -x.y; return x; }
290
291
ellpoint_pE& operator+=(ellpoint_pE& P, const ellpoint_pE& Q)
292
{ add(P, P, Q); return P; }
293
294
ellpoint_pE& operator-=(ellpoint_pE& P, const ellpoint_pE& Q)
295
{ sub(P, P, Q); return P; }
296
297
ellpoint_pE operator*(int m, const ellpoint_pE& P)
298
{
299
static ellpoint_pE *result = NULL, *Q = NULL;
300
static long q = -1;
301
long i;
302
303
// reallocate as infrequently as possible
304
if (ell_pEInfo->q != q) {
305
q = ell_pEInfo->q;
306
if (result != NULL) {
307
delete result;
308
delete Q;
309
}
310
// NB: the only way the following are deallocated is if they
311
// are replaced during a later invocation (see previous
312
// lines), so technically this is a tiny memory leak
313
result = new ellpoint_pE;
314
Q = new ellpoint_pE;
315
} else
316
result->set();
317
318
if (m == 0) return *result;
319
if (m < 0) return (-m)*P;
320
321
for (i = 0, *Q = P; (1<<i) <= m; i++) {
322
if (m & (1<<i))
323
*result += *Q;
324
*Q += *Q;
325
}
326
327
return *result;
328
}
329
330
int is_zero(const ellpoint_pE& P) { return P.identity_f; }
331
332
// *slow* but safe routine for computing order of a point
333
334
long ellpoint_pE::order()
335
{
336
ellpoint_pE Q;
337
long o;
338
339
// compute multiples of self until find 0
340
Q = *this;
341
for (o = 1; !is_zero(Q); o++)
342
Q += *this;
343
344
return o;
345
}
346
347
//////////////////////////////
348
349
unsigned long* to_ulong(ellpoint_pE& P)
350
{
351
static unsigned long result[2];
352
if (is_zero(P)) return NULL; // identity
353
result[0] = to_ulong(P.x);
354
result[1] = to_ulong(P.y);
355
return result;
356
}
357
358
//////////////////////////////
359
360
long operator==(const ellpoint_pE& P, const ellpoint_pE& Q)
361
{
362
if (is_zero(P) && is_zero(Q)) return 1;
363
if (is_zero(P) || is_zero(Q)) return 0;
364
return (P.x == Q.x) && (P.y == Q.y);
365
}
366
367
long operator<( ellpoint_pE& P, ellpoint_pE& Q)
368
{
369
// O < anything else
370
if (is_zero(P)) return 1;
371
if (is_zero(Q)) return 0;
372
373
#if X_ORDER
374
if (P.x < Q.x) return 1;
375
if (P.x == Q.x && P.y < Q.y) return 1;
376
#else
377
if (P.y < Q.y) return 1;
378
if (P.y == Q.y && P.x < Q.x) return 1;
379
#endif
380
return 0;
381
}
382
383
long operator<=(ellpoint_pE& P, ellpoint_pE& Q)
384
{ return (P < Q) || (P == Q); }
385
386
long operator>( ellpoint_pE& P, ellpoint_pE& Q)
387
{ return (Q <= P); }
388
389
long operator>=(ellpoint_pE& P, ellpoint_pE& Q)
390
{ return (Q < P); }
391
392
ostream& operator<<(ostream& s, const ellpoint_pE& P)
393
{
394
if (P.identity_f)
395
s << "0";
396
else
397
s << "(" << P.x << ", " << P.y << ")";
398
return s;
399
}
400
401
//////////////////////////////
402
//
403
// routines for calculating group order
404
405
///////////////////////////////////////////////////////////////////////////
406
407
// binary tree code
408
// - used for storing points in bsgs algorithm
409
410
struct node {
411
unsigned long point[2];
412
long i; // logarithm with respect to some point
413
struct node *left;
414
struct node *right;
415
};
416
417
typedef struct node node_t;
418
typedef struct node *node_p;
419
420
// compare two nodes
421
422
int cmp(unsigned long *a, unsigned long *b) {
423
if (a[0] == b[0] && a[1] == b[1]) return 0;
424
if (a[0] < b[0]) return -1;
425
if (a[0] > b[0]) return 1;
426
if (a[1] < b[1]) return -1;
427
return 1;
428
}
429
430
// look to see if node lies in tree
431
432
static long lookup(struct node* node, unsigned long *target) {
433
if (node == NULL)
434
return(-1);
435
else {
436
int r = cmp(node->point, target);
437
if (r == 0)
438
return(node->i);
439
else {
440
if (r > 0)
441
return(lookup(node->left, target));
442
else
443
return(lookup(node->right, target));
444
}
445
}
446
}
447
448
// add node to tree
449
450
void insert(node_p head, node_p node)
451
{
452
node->left = node->right = NULL;
453
454
if (node == head)
455
return;
456
457
int r = cmp(node->point, head->point);
458
459
if (r<0) {
460
if (head->left == NULL)
461
head->left = node;
462
else
463
insert(head->left, node);
464
} else {
465
if (head->right == NULL)
466
head->right = node;
467
else
468
insert(head->right, node);
469
}
470
}
471
472
///////////////////////////////////////////////////////////////////////////
473
//
474
// baby-step giant-step code
475
476
// - used to find an annihilator of a point on an elliptic curve
477
//
478
// input:
479
// P : point on elliptic curve
480
// M : number of baby steps
481
// N : anchor point for giant step
482
//
483
// sketch:
484
//
485
// (1) compute [i]P for i=1..M-1 and return i if find [i]P=O
486
//
487
// (2) compute [N-M^2+j*M]P for j=0..M-1 and return (N-M^2+j*M-i) if
488
// [i]P = [N-M^2+j*M]P
489
//
490
// (3) return -1
491
//
492
// - if terminates in (1), will return smallest possible i
493
// - if terminates in (2), will return smallest possible j
494
// - a return value of -1 implies the search failed
495
// - if called with N=M^2+1 and returns r>0, then r=order(P)
496
497
static node_t *bsgs_annihilate_nodes = NULL;
498
499
long bsgs_annihilate(const ellpoint_pE& P, const long M, const long N)
500
{
501
static ellpoint_pE *R = NULL;
502
static long q = -1, max_M = -1;
503
node_t *nodes = bsgs_annihilate_nodes;
504
long i;
505
506
// create workspace for storing multiples of P, if necessary
507
if (nodes == NULL || ell_pEInfo->q != q || max_M < M) {
508
// either need more workspace or have switched finite fields
509
510
// clean up previous workspace, if necessary
511
if (nodes != NULL) {
512
delete nodes;
513
delete R;
514
}
515
516
// NB: one can clean up the following allocation for nodes using
517
// ell_cleanup(), but the last copy of R used will always
518
// stick around
519
520
// allocate new workspace
521
nodes = bsgs_annihilate_nodes = new(node_t[M]);
522
R = new ellpoint_pE;
523
assert(nodes != NULL && R != NULL);
524
525
// keep track of values current workspace depends on
526
q = ell_pEInfo->q;
527
max_M = M;
528
}
529
530
// baby steps: compute [i]P for i=1..M-1
531
// - obviously [0]P=O, hence the reason we don't consider i=0
532
static unsigned long *point;
533
*R = P;
534
for (long i=1; i < M; i++, *R += P) {
535
// claim: R = [i]P
536
537
// convert R to convenient format for storing in a binary tree
538
point = to_ulong(*R);
539
if (point == NULL)
540
return i; // [i]*P = O
541
542
// add R to our binary tree
543
// - not on tree already because otherwise R=[i]P=[j]P and
544
// hence [i-j]P=O, which we would have seen
545
memcpy(nodes[i-1].point, point, sizeof(unsigned long)*2);
546
nodes[i-1].i = i;
547
insert(nodes, nodes+(i-1));
548
549
// NB: point points to statically allocated memory hence
550
// doesn't need to be freed
551
}
552
553
// claim: R=[M]P
554
555
// compute base point for giant steps
556
ellpoint_pE Q = (N-((M-1)*M))*P;
557
if (Q == *R)
558
return N-(M*M); // claim: order(P) | (N-M^2+M)-M = N^2-M
559
560
// giant steps: compute Q + [M*i]P = for i=1..M
561
for (long j = M-1; j >= 0; j--, Q += *R) {
562
// claim: Q = [N-j*M]P
563
if (is_zero(Q))
564
return N-j*M;
565
566
// check to see if point was computed as a baby step
567
if ((i=lookup(nodes, to_ulong(Q))) != -1)
568
return N-j*M-i; // [N-j*M]P = [i]P
569
570
// don't do the last addition since we don't need it
571
if (j == 0)
572
break;
573
}
574
575
// failed
576
return -1;
577
}
578
579
// baby-step giant-step search for m in [0,M^2-1] so [m]P = Q
580
//
581
// input:
582
// P : base point on elliptic curve
583
// Q : point on to compute log_P for
584
// M : number of baby steps
585
//
586
// sketch:
587
//
588
// (1) compute Q-[i]P for i=0..M-1 and return i if find [i]P=Q
589
//
590
// (2) compute [j*M]P for j=1..M-1 and return (i+j*M) if Q-[i]P=[j*M]P
591
//
592
// (3) return -1
593
//
594
// - if terminates in (1), will return smallest possible i
595
// - if terminates in (2), will return smallest possible i+j*M
596
// - a return value of -1 implies the search failed
597
598
static node_t *bsgs_log_nodes = NULL;
599
600
long bsgs_log(const ellpoint_pE& P, const ellpoint_pE& Q, const long M)
601
{
602
static ellpoint_pE *R = NULL, *S = NULL;
603
static long q = -1, max_M = -1;
604
node_t *nodes = bsgs_log_nodes;
605
long i;
606
607
// create workspace for storing multiples of P, if necessary
608
if(nodes == NULL || ell_pEInfo->q != q || max_M < M) {
609
// either need more workspace or have switched finite fields
610
611
// clean up previous workspace, if necessary
612
if (nodes != NULL)
613
delete nodes;
614
if (R != NULL)
615
delete R;
616
if (S != NULL)
617
delete S;
618
619
// NB: one can clean up the following allocation for nodes using
620
// ell_cleanup(), but the last copies of R,S used will always
621
// stick around
622
623
// allocate new workspace
624
bsgs_log_nodes = nodes = new(node_t[M]);
625
R = new ellpoint_pE;
626
S = new ellpoint_pE;
627
assert(nodes != NULL && R != NULL && S != NULL);
628
629
// keep track of values current workspace depends on
630
q = ell_pEInfo->q;
631
max_M = M;
632
}
633
634
// baby steps: compute Q-[i]P for i=0,...,M-1
635
static unsigned long *point;
636
*R = Q;
637
for(long i = 0; i < M; i++, *R -= P) {
638
// claim: R = Q - [i]P
639
640
// convert R to convenient format for storing in a binary tree
641
point = to_ulong(*R);
642
if (point == NULL)
643
return i; // Q = [i]P
644
645
// add R to our binary tree
646
// - if on tree already, then R=Q-[i]P=Q-[j]P and [i-j]P=O, which
647
// implies we won't find k so [k]P=Q because necessarily k<i-j
648
// and we already noticed [k]P!=Q for all k<i
649
// - rather that noticing we're in previous case, we just keep
650
// running since we expect to call this routine only when the
651
// order(P)>M, hence it can't occur
652
// ==> we can assume [i]P!=[j]P for j<i when necessary
653
memcpy(nodes[i].point, point, sizeof(unsigned long)*2);
654
nodes[i].i = i;
655
insert(nodes, nodes+i);
656
657
// NB: point points to statically allocated memory hence
658
// doesn't need to be freed
659
}
660
661
// giant steps: compute S = [j*M]P for j=1..M-1
662
*R = M*P;
663
*S = *R;
664
for (long j = 1; j < M; j++, *S += *R) {
665
// S = [j*M]P
666
667
// if [j*M]P==0, then Q does not lie in <P> because we already
668
// checked whether [k]P==Q for 0<k<j*M
669
if (is_zero(*S))
670
return -1; // [j*M]P = 0
671
672
i = lookup(nodes, to_ulong(*S));
673
if (i != -1)
674
return i+j*M; // [j*M]P = Q - [i]P
675
676
// don't need last addition, so skip it
677
if (j == M-1 )
678
break;
679
}
680
681
// failed
682
return -1;
683
}
684
685
///////////////////////////////////////////////////////////////////////////
686
687
// compute group size of current elliptic curve
688
// - memoryless, so every invocation does calculation from scratch!!
689
690
long ell_pE::order()
691
{
692
static zz_pE x, y, y_sqr, z;
693
static ellpoint_pE P, Q, R, S;
694
long a_tau_q = 0, q = ell_pEInfo->q;
695
696
// for really small finite fields, do stupid brute-force count
697
if (q <= 36) {
698
x = 0;
699
do {
700
// y^ = (x^2 + a4)*x + a6;
701
sqr(z, x);
702
add(y_sqr, z, ell_pEInfo->a4);
703
mul(z, y_sqr, x);
704
add(y_sqr, z, ell_pEInfo->a6);
705
706
// if y^2=0, then trace contribution is 0
707
if (y_sqr == 0)
708
continue;
709
710
// trace contribution is -chi(x^3+a4*x+a6)
711
// - note, this is trace on H^1, hence the minus sign
712
if (zz_pEExtraInfo->legendre_char(y_sqr) == 1)
713
a_tau_q += -1;
714
else
715
a_tau_q += +1;
716
} while(inc(x) == NO_CARRY);
717
718
return (q+1-a_tau_q);
719
}
720
721
// for larger fields, generate one or more points so can determine
722
// order of group as the unique integer in the Hasse-Weil interval
723
// [q+1-2*sqrt(q),q+1+2*sqrt(q)] satisfying certain properties.
724
//
725
// - in general it suffices to find P so there is a unique multiple of
726
// of |<P>|
727
// - might need to find Q so there's a unique multiple of |<P,Q>|
728
729
long long_two_sqrt_q = (long)floor(2.0*sqrt((double)q));
730
while ((long_two_sqrt_q+1)*(long_two_sqrt_q+1) <= 2*q)
731
long_two_sqrt_q++;
732
733
// integral form of H-W interval
734
long long_l, long_r;
735
long_l = q+1-long_two_sqrt_q;
736
long_r = q+1+long_two_sqrt_q;
737
738
long HW_int = long_r-long_l;
739
long M = (long)ceil(sqrt(HW_int+1));
740
long sqrt_l = (long)ceil(sqrt(long_l));
741
742
long order_P = -1, order_Q, sqrt_order_P;
743
unsigned long ul;
744
745
x = 0; y = 0; z = 0; y_sqr = 0;
746
do {
747
// step 1: see if there is R in E(F_q) so x(R)=x
748
749
// y^2 = (x^2+a4)*x + a6
750
// - a priori, y lives in F_{q^2} and we only know y^2
751
sqr(z, x);
752
add(y_sqr, z, ell_pEInfo->a4);
753
mul(y, y_sqr, x);
754
add(y_sqr, y, ell_pEInfo->a6);
755
756
// check to see if y lives in F_q
757
ul = to_ulong(y_sqr);
758
if (y_sqr != 0 && zz_pEExtraInfo->legendre_char(y_sqr) == 0)
759
continue; // nope, so no such point
760
761
// R=(x,y) is on our curve
762
y = (y_sqr == 0) ? y_sqr : zz_pEExtraInfo->square_root(y_sqr);
763
R.set(x, y);
764
assert(R.on_curve());
765
766
// step 2: check to see if order(R) <= r-l <= M^2
767
long order_R = bsgs_annihilate(R, M, M*M+1);
768
if (order_R == -1 || order_R > HW_int) {
769
// order(R) > r-l so there is a unique multiple in HW interval
770
order_P = bsgs_annihilate(R, M, long_l + M*M);
771
assert(order_P > 0 && order_P <= long_r);
772
return order_P;
773
}
774
775
// step 3: if is first time we made it this far, note the point
776
if (order_P == -1) {
777
P = R; order_P = order_R;
778
order_Q = -1;
779
continue; // continue looking for more points
780
}
781
782
// step 4: if new point has higher order than recorded point, swap
783
if (order_R > order_P) { // && order_Q == -1) {
784
Q = P; order_Q = order_P;
785
P = R; order_P = order_R;
786
} else {
787
Q = R; order_Q = order_R;
788
}
789
sqrt_order_P = (long)ceil(sqrt(order_P));
790
791
// step 5: check if order(P) >= sqrt(l)
792
// - if P has maximal order, this will be satisfied
793
if (order_P < sqrt_l)
794
continue; // no, so continue looking for R which works
795
796
// step 6: determine which multiples of order_P lie in HW interval
797
//
798
//
799
// - is at least one; corresponds to |G|
800
801
// step 6.a: find smallest and largest multiples
802
long min_d = (long)floor(long_l/order_P) - 1, max_d;
803
while ((q+1-order_P*min_d)*(q+1-order_P*min_d) > 4*q)
804
min_d++;
805
max_d = min_d;
806
while ((q+1-order_P*max_d)*(q+1-order_P*max_d) <= 4*q)
807
max_d++;
808
max_d--;
809
810
// claim: if order_P >= sqrt(q+1-2*sqrt(q)) and q>36, then
811
// #multiples <= 1 + (4*sqrt(q)+1)/order_P < 6
812
assert(max_d - min_d < 5);
813
814
// step 6.b: check if had exactly one multiple
815
if (min_d == max_d)
816
return min_d*order_P;
817
818
// prop: q>34 and |<P,Q>| >= q+1-2*sqrt(q) ==> |<P,Q>|/order_P > 4
819
//
820
// pf:
821
// - let n = max_d-min_d
822
//
823
// ==> n*order(P) = (max_d-min_d)*order(P) <= 4*sqrt(q)
824
// ==> min_d >= (q+1-2*sqrt(q))/order(P)
825
// >= n*(q+1-2*sqrt(q))/(4*sqrt(q))
826
//
827
// - q > 34, so min_d >= n*(q+1-2*sqrt(q))/(4*sqrt(q)) > n
828
//
829
// - gcd(d1,d2) <= n for all d1<d2 in [min_d,max_d]
830
//
831
// ==> suffices to show there is a unique d in [min_d,max_d] so
832
// d*Q lies in <P>
833
//
834
// - let i = order(Q mod P) = |<P,Q>|/|<P>|
835
//
836
// - if |<P,Q>| lies in H-W, then i >= min_d > n
837
//
838
// - if d1*Q,d2*Q are both in <P> for d1<=d2 in [min_d,max_d],
839
// then i|gcd(d1,d2), hence d1=d2 or |<P,Q>|<q+1-2*sqrt(q)
840
841
// step 7: check if m*Q lies in <P> for m=1,...,4
842
// - prop implies we can find P,Q so this doesn't happen
843
int num_d = max_d - min_d + 1;
844
if (bsgs_log(P, Q, sqrt_order_P) != -1)
845
continue;
846
if (num_d > 2 && order_Q % 2 == 0 && bsgs_log(P, 2*Q, sqrt_order_P) != -1)
847
continue;
848
if (num_d > 3 && order_Q % 3 == 0 && bsgs_log(P, 3*Q, sqrt_order_P) != -1)
849
continue;
850
if (num_d > 4 && order_Q % 4 == 0 && bsgs_log(P, 4*Q, sqrt_order_P) != -1)
851
continue;
852
853
// step 8: determine if there is unique d in [min_d,max_d] so
854
// d*Q lies in <P>
855
long d, order_G = -1;
856
num_d = 0;
857
for (d = min_d; d <= max_d; d++) {
858
long log_P = bsgs_log(P, GCD(d, order_Q)*Q, sqrt_order_P);
859
if (log_P != -1) {
860
num_d++;
861
order_G = d*order_P;
862
}
863
}
864
assert(num_d == 1);
865
866
return order_G;
867
} while(inc(x) == NO_CARRY /*&& test < MAX_POINT_TEST*/);
868
869
// should never get here!
870
assert(0);
871
872
return -1;
873
}
874
875
void ell_cleanup()
876
{
877
if (bsgs_annihilate_nodes != NULL) {
878
delete bsgs_annihilate_nodes;
879
bsgs_annihilate_nodes = NULL;
880
}
881
if (bsgs_log_nodes != NULL) {
882
delete bsgs_log_nodes;
883
bsgs_log_nodes = NULL;
884
}
885
}
886
887
//////////////////////////////
888
889
#ifdef MAIN
890
int main(int argc, char **argv)
891
{
892
long p = 101, d = 1, q, i;
893
894
if (argc == 2)
895
d = atoi(argv[1]);
896
897
init_NTL_ff(p, d, 0, 0, 0);
898
q = to_ulong(zz_pE::cardinality());
899
assert(q < (1L << 28));
900
901
// precompute list of square roots
902
zz_pE sqr_roots[q], x, x2;
903
do {
904
x2 = x*x;
905
sqr_roots[to_ulong(x2)] = x;
906
} while (inc(x) == NO_CARRY);
907
908
#if 0
909
{
910
zz_pE X;
911
912
X = p-1;
913
inc(X);
914
cout << "X = " << X << "\n";
915
cout << "X^2 = " << (X^2) << "\n";
916
}
917
#endif
918
919
zz_pE one;
920
921
// initialize the curve y^2 = x^3 + x + 1
922
one = 1;
923
ell_pE::init(one /*a4*/, one /*a6*/);
924
925
zz_pE y, y2;
926
927
// brute force points on curve (mostly in pairs) by trying every x
928
// and using model y^2 = x^3 + x + 1
929
do {
930
// try to compute square root of x^3 + x + 1
931
y2 = (sqr(x) + ell_pEInfo->a4)*x + ell_pEInfo->a6;
932
y = sqr_roots[to_ulong(y2)];
933
934
// zero entry in table with y2 = 0 implies no square root
935
if (y2 != 0 && y == 0)
936
continue; // not a square
937
938
cout << x << " " << y << " ";
939
940
// initialize point on current curve
941
ellpoint_pE P(x,y);
942
943
assert(P.on_curve());
944
cout << P << " ";
945
946
long o = P.order();
947
cout << o << "\n";
948
949
// make sure that [o]P = O
950
ellpoint_pE Q;
951
Q = o*P;
952
assert(is_zero(Q));
953
954
// extra sanity check. make sure [i]P != O for 0 < i < o
955
long i;
956
for (i = 1; i < o; i++)
957
assert(!is_zero(i*P));
958
} while (inc(x) == NO_CARRY);
959
960
return 0;
961
}
962
#endif // MAIN
963
964