1 //==============================================================================================
2 //
3 // This file is part of LiDIA --- a library for computational number theory
4 //
5 // Copyright (c) 1994--2001 the LiDIA Group. All rights reserved.
6 //
7 // See http://www.informatik.tu-darmstadt.de/TI/LiDIA/
8 //
9 //----------------------------------------------------------------------------------------------
10 //
11 // $Id$
12 //
13 // Author : Nigel Smart, John Cremona
14 // Adaption of John Cremona's code; some code came
15 // originally from Oisin McGuiness
16 // Changes : See CVS log
17 //
18 //==============================================================================================
19
20
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
24 #include "LiDIA/bigint.h"
25 #include "LiDIA/bigfloat.h"
26 #include "LiDIA/nmbrthry_functions.h"
27 #include "LiDIA/base_vector.h"
28
29 #include "LiDIA/elliptic_curve_bigint.h"
30 #include "LiDIA/point_bigint.h"
31
32
33
34 #ifdef LIDIA_NAMESPACE
35 namespace LiDIA {
36 #endif
37
38
39
40 //
41 // ctors' and d'tor
42 //
43
point()44 point< bigint >::point()
45 : x(0),
46 y(0),
47 z(0),
48 height(0),
49 ord(0)
50 {
51 ec = new elliptic_curve< bigint > ();
52 }
53
54
55
point(const bigint & xp,const bigint & yp,const elliptic_curve<bigint> & ecp)56 point< bigint >::point(const bigint & xp,
57 const bigint & yp,
58 const elliptic_curve< bigint > &ecp)
59
60 : x(xp),
61 y(yp),
62 z(1),
63 height(-1),
64 ord(0)
65
66 {
67 ec = new elliptic_curve< bigint > (ecp);
68 }
69
70
71
point(const bigint & xp,const bigint & yp,const bigint & zp,const elliptic_curve<bigint> & ecp)72 point< bigint >::point(const bigint & xp,
73 const bigint & yp,
74 const bigint & zp,
75 const elliptic_curve< bigint > &ecp)
76
77 : x(xp),
78 y(yp),
79 z(zp),
80 height(-1),
81 ord(0)
82
83 {
84 ec = new elliptic_curve< bigint > (ecp);
85 reduce();
86 }
87
88
89
point(const point<bigint> & P)90 point< bigint >::point(const point< bigint > & P)
91
92 : x(P.x),
93 y(P.y),
94 z(P.z),
95 height(P.height),
96 ord(P.ord)
97
98 {
99 ec = new elliptic_curve< bigint > (*(P.ec));
100 }
101
102
103
point(const elliptic_curve<bigint> & e)104 point< bigint >::point(const elliptic_curve< bigint > &e)
105
106 : x(0),
107 y(1),
108 z(0),
109 height(0),
110 ord(1)
111
112 {
113 ec = new elliptic_curve< bigint > (e);
114 }
115
116
117
~point()118 point< bigint >::~point()
119 {
120 delete ec;
121 }
122
123
124
125 //
126 // Access
127 //
128
129 elliptic_curve< bigint >
get_curve() const130 point< bigint >::get_curve() const
131 {
132 return *ec;
133 }
134
135
136
137 //
138 // Assignment
139 //
140
141 void
assign_zero(const elliptic_curve<bigint> & e)142 point< bigint >::assign_zero(const elliptic_curve< bigint > & e)
143 {
144 ec->assign(e);
145 z.assign_zero();
146 y.assign_one();
147 x.assign_zero();
148 height = 0;
149 ord = 1;
150 }
151
152
153
154 void
assign(const point<bigint> & P)155 point< bigint >::assign(const point< bigint > & P)
156 {
157 if (&P != this) {
158 ec->assign(*(P.ec));
159 x = P.x;
160 y = P.y;
161 z = P.z;
162 height = P.height;
163 ord = P.ord;
164 }
165 }
166
167
168
169 void
assign(const bigint & xx,const bigint & yy,const elliptic_curve<bigint> & e)170 point< bigint >::assign(const bigint & xx, const bigint & yy, const
171 elliptic_curve< bigint > & e)
172 {
173 ec->assign(e);
174 x = xx;
175 y = yy;
176 z.assign_one();
177 height = -1;
178 ord = 0;
179 }
180
181
182
183 void
assign(const bigint & xx,const bigint & yy,const bigint & zz,const elliptic_curve<bigint> & e)184 point< bigint >::assign(const bigint & xx, const bigint & yy, const bigint &zz,
185 const elliptic_curve< bigint > & e)
186 {
187 ec->assign(e);
188 x = xx;
189 y = yy;
190 z = zz;
191 height = -1;
192 ord = 0;
193 }
194
195
196
197 // assign on last curve (use with care of course)
198 void
assign_zero()199 point< bigint >::assign_zero()
200 {
201 z.assign_zero();
202 y.assign_one();
203 x.assign_zero();
204 height = 0;
205 ord = 1;
206 }
207
208
209
210 void
assign(const bigint & xx,const bigint & yy)211 point< bigint >::assign(const bigint & xx, const bigint & yy)
212 {
213 x = xx;
214 y = yy;
215 z.assign_one();
216 height = -1;
217 ord = 0;
218 }
219
220
221
222 void
assign(const bigint & xx,const bigint & yy,const bigint & zz)223 point< bigint >::assign(const bigint & xx, const bigint & yy, const bigint &zz)
224 {
225 x = xx;
226 y = yy;
227 z = zz;
228 height = -1;
229 ord = 0;
230 }
231
232
233
234 void
swap(point<bigint> & P)235 point< bigint >::swap(point< bigint > & P)
236 {
237 LiDIA::swap(x, P.x);
238 LiDIA::swap(y, P.y);
239 LiDIA::swap(z, P.z);
240 LiDIA::swap(height, P.height);
241
242 int te = P.ord;
243 P.ord = ord;
244 ord = te;
245
246 elliptic_curve< bigint > * e = P.ec;
247 P.ec = ec;
248 ec = e;
249 }
250
251
252
253 void
reduce()254 point< bigint >::reduce()
255 {
256 if (z.is_zero()) {
257 x = 0;
258 y = 1;
259 return;
260 }
261 if (z.is_one()) {
262 return;
263 }
264
265 bigint d = gcd(x, y);
266 if (!d.is_one())
267 d = gcd(d, z); // now d = gcd(x, y, z)
268 if (d.is_zero()) {
269 lidia_error_handler("point< bigint >", "reduce::Invalid point");
270 }
271 if (!d.is_one()) {
272 x /= d;
273 y /= d;
274 z /= d;
275 }
276 if (z.is_lt_zero()) {
277 x.negate();
278 y.negate();
279 z.negate();
280 }
281 }
282
283
284
285 //
286 // Testing points
287 //
288
289 bool
is_equal(const point<bigint> & P) const290 point< bigint >::is_equal (const point< bigint > & P) const
291 {
292 if (*(P.ec) != *(ec))
293 return false;
294
295 bigint temp(P.x*z - P.z*x);
296
297 if (!temp.is_zero()) {
298 return false;
299 }
300
301 temp = P.y*z - P.z*y;
302 if (!temp.is_zero()) {
303 return false;
304 }
305
306 temp = P.y*x - P.x*y;
307 return temp.is_zero();
308 }
309
310
311
312 bool
on_curve() const313 point< bigint >::on_curve() const
314 {
315 if (x.is_zero() && y.is_zero() && z.is_zero()) {
316 return 0;
317 }
318 if (z.is_zero()) {
319 return 1;
320 }
321 // should calculate
322 // y^2 +a1 x y + a3 y
323 // and
324 // x^3 + a2 x^2 + a4 x + a6
325 // where
326 // x = X/Z, y = Y/Z
327 // and verify equality.
328 //
329 // In homogeneous coordinates:
330 //
331 // Lhs = Y^2 Z + a1 XYZ + a3 YZ^2 = (YZ) *(Y + a1 X + a3 Z)
332 //
333 //
334 // Rhs = X^3 +a2 X^2 Z + a4 X Z^2 + a6 Z^3
335 //
336 const bigint& Lhs = y*z*(y + ec->get_a1()*x + ec->get_a3()*z);
337 const bigint& Rhs = ec->get_a6()*z*z*z+x*(ec->get_a4()*z*z+x*(ec->get_a2()*z + x));
338 return Lhs == Rhs;
339 }
340
341
342
343 //
344 // Arithmetic
345 //
346
347 void
add(point<bigint> & R,const point<bigint> & P,const point<bigint> & Q)348 add(point< bigint > & R,
349 const point< bigint > & P,
350 const point< bigint > & Q)
351 {
352 if (*(P.ec) != *(Q.ec))
353 lidia_error_handler("point< bigint >", "add::different elliptic curves");
354
355 if (Q.z.is_zero()) {
356 R.assign(P);
357 return;
358 }
359 if (P.z.is_zero()) {
360 R.assign(Q);
361 return;
362 }
363 if (P == Q)
364 (P.ec)->get_point_operations()._mult_by_2(R, P);
365 else
366 (P.ec)->get_point_operations()._add(R, P, Q);
367 }
368
369
370
371 void
subtract(point<bigint> & R,const point<bigint> & P,const point<bigint> & Q)372 subtract(point< bigint > & R,
373 const point< bigint > & P,
374 const point< bigint > & Q)
375 {
376 point< bigint > H;
377 negate(H, Q);
378 add(R, P, H);
379 }
380
381
382
383 void
multiply_by_2(point<bigint> & R,const point<bigint> & P)384 multiply_by_2(point< bigint > & R, const point< bigint > & P)
385 {
386 if (P.z.is_zero()) {
387 R.assign(P);
388 return;
389 }
390 (P.ec)->get_point_operations()._mult_by_2(R, P);
391 }
392
393
394
395 void
multiply(point<bigint> & R,const bigint & mm,const point<bigint> & P)396 multiply (point< bigint > & R, const bigint & mm, const point< bigint > & P)
397 {
398 bigint m(mm);
399 point< bigint > Z(P);
400
401 R.assign(P);
402
403 if (m.is_negative()) {
404 m.negate();
405 negate(Z, Z);
406 negate(R, R);
407 }
408
409 if (P.z.is_zero() || mm.is_zero()) {
410 R.assign(*(P.ec));
411 return;
412 }
413
414 if (mm.is_one())
415 return;
416
417 int i = static_cast<int>(m.bit_length())-2; // note m >= 2
418
419 while (i >= 0) {
420 multiply_by_2(R, R);
421 if (m.bit(i))
422 add(R, R, Z);
423 i--;
424 }
425 }
426
427
428
429 void
negate(point<bigint> & r,const point<bigint> & x)430 negate(point< bigint > & r, const point< bigint > & x)
431 {
432 (r.ec)->assign(*(x.ec));
433 (x.ec)->get_point_operations()._negate(r, x);
434 }
435
436
437
438 // Advanced Functions
439
440 bigfloat
get_naive_height() const441 point< bigint >::get_naive_height() const
442 {
443 if (z.is_zero()) {
444 return 0;
445 }
446 bigint g = gcd(x, z);
447 bigfloat xx = abs(x/g);
448 if (abs(z/g) > abs(x/g)) {
449 xx = abs(z/g);
450 }
451 return log(xx);
452 }
453
454
455
456 bigfloat
get_height()457 point< bigint >::get_height()
458 {
459 // WARNING -- no check made of validity of point on curve
460 if (z.is_zero())
461 return bigfloat(0.0);
462 if (height >= 0.0) return height; // already calculated it
463 // zero height if torsion
464 if (get_order() > 0) {
465 height = bigfloat(0.0);
466 return height;
467 }
468
469 // N.B. So if we ever ask a point its height it will compute its order.
470 // otherwise need to calculate it
471
472 // Add local heights at finite primes dividing discr(E) OR denom(P).
473 // The components for primes dividing denom(P) add to log(denom(x(P)));
474 // since P=(XZ:Y:Z^3), denom(P)=Z=gcd(XZ,Z^3), called "zroot" here,
475 // and so the contribution is log(denom(x(P))) = 2*log(zroot).
476 // This avoids factorizing the denominator.
477
478 bigint p;
479 bigint zroot(gcd(x, z)); // = cube root of Z
480 bigfloat ans(get_realheight() + 2*log(bigfloat(zroot)));
481
482 long i;
483 base_vector< bigint > bp = ec->get_bad_primes();
484
485 for (i = 0; i < bp.get_size(); i++) {
486 p = bp[i];
487 if (!is_divisor(p, zroot))
488 ans += get_pheight(p);
489 }
490
491 height = ans;
492 return ans;
493 }
494
495
496
497 bigfloat
get_pheight(const bigint & pr) const498 point< bigint >::get_pheight(const bigint& pr) const
499 {
500 bigint a1, a2, a3, a4, a6, b2, b4, b6, b8, c4, c6, discr;
501
502 ec->get_ai(a1, a2, a3, a4, a6);
503 ec->get_bi(b2, b4, b6, b8);
504 ec->get_ci(c4, c6);
505
506 discr = ec->discriminant();
507 long n = valuation(pr, discr);
508
509 const bigint& zroot = gcd(x, z); // = cube root of z
510 long vpz = 3*valuation(pr, zroot);
511
512 const bigint& x2 = x*x;
513 const bigint& z2 = z*z;
514 const bigint& xz = x*z;
515 const bigint& yz = y*z;
516 long a = valuation(pr, 3*x2 + 2*a2*xz + a4*z2 - a1*yz) - 2*vpz;
517 long b = valuation(pr, 2*y + a1*x + a3*z) - vpz;
518 long c = valuation(pr, 3*x2*x2 + b2*x2*xz + 3*b4*x2*z2 + 3*b6*xz*z2 + b8*z2*z2) -4*vpz;
519
520 // some obvious changes enable calculation of lambda as a rational
521 // some improvements can be made if this is never to be done
522 // eg in the above, no need to work with projective coords, just use real x/z
523 bigfloat halfn = n; halfn /= 2;
524 bigfloat lambda;
525
526 if ((a <= 0) || (b <= 0)) {
527 lambda = vpz - valuation(pr, x);
528 if (lambda < 0)
529 lambda = 0;
530 }
531 else if (!is_divisor(pr, c4)) {
532 bigfloat m(b);
533 if (halfn < m)
534 m = halfn; // m = min(b , halfn);
535 lambda = (m*(m-n)) / n;
536 }
537 else if (c >= (3*b))
538 lambda = (-2*b) / bigfloat(3.0);
539 else
540 lambda = -c / bigfloat(4.0);
541
542 bigfloat ans(lambda * log(bigfloat(pr)));
543 return ans;
544 }
545
546
547
548 bigfloat
get_realheight() const549 point< bigint >::get_realheight() const
550 {
551 bigfloat xx(bigfloat(x)/bigfloat(z));
552 return real_height(*ec, xx);
553 }
554
555
556
557 int
get_order()558 point< bigint >::get_order()
559 {
560 if (ord) {
561 return ord;
562 }
563 if (z.is_zero()) {
564 ord = 1;
565 return 1;
566 }
567 point< bigint > q = (*this);
568 ord = 1;
569 bigint eight(8);
570 while (!q.is_zero() && (q.z <= eight)) {
571 // (worst denom of a torsion point)
572 ord++; add(q, q, (*this));
573 }
574 if (!q.is_zero())
575 ord = -1;
576 return ord;
577 }
578
579
580
581 int
order(point<bigint> & P,base_vector<point<bigint>> & list)582 order(point< bigint > & P, base_vector< point < bigint > > & list)
583 {
584 list.set_size(0);
585 list.set_capacity(12);
586 // NB Don't set P.ord until its correct value is known,
587 // otherwise Q's ord gets set wrongly
588 list[0] = point< bigint > (*(P.ec));
589 if (P.is_zero()) {
590 P.ord = 1;
591 return 1;
592 }
593 long ord = 2; list[1] = P;
594 bigint eight(8);
595 point< bigint > Q = P;
596 while (!(Q.is_zero()) && (Q.z <= eight) && (ord < 13)) {
597 add(Q, Q, P);
598 if (!(Q.is_zero()))
599 list[ord++] = Q;
600 }
601
602 if (!(Q.is_zero()))
603 ord = -1;
604 P.ord = ord;
605 return ord;
606 }
607
608
609
610 //
611 // I/O
612 //
613
614 void
read(std::istream & in)615 point< bigint >::read (std::istream & in)
616 {
617 char c;
618 in.flags(in.flags() | std::ios::dec); //force decimal input (bug fix)
619 in >> c;
620 switch(c) {
621 case '(':
622 in >> x >> c >> y >> c;
623 z.assign_one();
624 break;
625 case '[':
626 in >> x >> c >> y >> c >> z >> c;
627 break;
628 }
629 ord = 0; height = -1.0;
630 if (on_curve())
631 reduce();
632 else
633 lidia_error_handler("point< bigint >", "read >>::not on curve");
634 }
635
636
637
638 void
write(std::ostream & out) const639 point< bigint >::write (std::ostream & out) const
640 {
641 out << "[" << x << " : " << y << " : " << z << "]";
642 }
643
644
645
646 #ifdef LIDIA_NAMESPACE
647 } // end of namespace LiDIA
648 #endif
649