1
1
// TODO:
2
2
//
3
+ // Optimise Sqrt Velu, current implementation is very slow!
4
+ //
3
5
// Cofactor clearing. At the moment clearing the cofactor means multiplying by each ell_i e_i times,
4
6
// which seems silly. I think we should probably have some function which converts the prime factorisation into &[u64; ...]
5
7
// and then we use these limbs to do var time point multiplication.
6
8
9
+ // use std::time::Instant;
10
+
7
11
use fp2:: traits:: Fp as FqTrait ;
8
12
9
13
use crate :: polynomial_ring:: poly:: Poly ;
@@ -318,6 +322,10 @@ impl<Fq: FqTrait> Curve<Fq> {
318
322
/// Compute roots of the polynomial \Prod (Z - x(Q)) for Q in the set
319
323
/// I = {2b(2i + 1) | 0 <= i < c}
320
324
fn precompute_hi_roots ( hI_roots : & mut [ Fq ] , A24 : & Fq , C24 : & Fq , kernel : & PointX < Fq > , b : usize ) {
325
+ // For now, we collect the points in hI_points, then normalise these in a batch
326
+ // then set hI_roots to the x-coordinates.
327
+ let mut hI_points = vec ! [ PointX :: INFINITY ; hI_roots. len( ) ] ;
328
+
321
329
// Set Q = [2b]K
322
330
let mut Q = Self :: xmul_proj_u64_vartime ( A24 , C24 , kernel, ( b + b) as u64 ) ;
323
331
@@ -326,47 +334,27 @@ impl<Fq: FqTrait> Curve<Fq> {
326
334
Self :: xdbl_proj ( A24 , C24 , & mut step. X , & mut step. Z ) ;
327
335
let mut diff = Q ;
328
336
329
- // TODO: work projectively here to avoid the inversion for each point?
330
- for r in hI_roots. iter_mut ( ) {
331
- * r = Q . x ( ) ;
332
- // TODO: we can skip the last addition
337
+ let n = hI_roots. len ( ) - 1 ;
338
+ for ( i, P ) in hI_points. iter_mut ( ) . enumerate ( ) {
339
+ * P = Q ;
340
+
341
+ // For the last step, we don't need to do the diff add
342
+ if i == n {
343
+ break ;
344
+ }
345
+
333
346
let R = Self :: xdiff_add ( & Q , & step, & diff) ;
334
347
diff = Q ;
335
348
Q = R ;
336
349
}
337
- }
338
-
339
- /// Given a point (X : Z) compute the three elliptic resultants on the Montgomery
340
- /// curve, given by (with `a = X / Z` for now...).
341
- /// -
342
- /// - `f1 = (x * QZ - QX)^2
343
- /// - `f2 = -2((xa + 1)(x + a) + 2Axa)`
344
- /// - `f3 = -2* (2*A*QX*QZ*x + (QX*x + QZ)*(QZ*x + QX))
345
- fn _old_elliptic_resultants < P : Poly < Fq > > ( A : & Fq , Q : & PointX < Fq > ) -> ( P , P , P ) {
346
- // TODO: is it better to invert A / C to be used here, or work with (A : C)
347
- // projectively? The cost balance is 1 inversion (~30M) or the cost of
348
- // ~sqrt(degree) * n multiplications. Where n is the number of multiplications
349
- // by C in this function.
350
-
351
- // TODO: do operation counting to see if this is the fastest way
352
- // to compute these three polynomials...
353
- let QX_sqr = Q . X . square ( ) ;
354
- let QZ_sqr = Q . Z . square ( ) ;
355
- let QXQZ = Q . X * Q . Z ;
356
- let QXQZ_m2 = -QXQZ . mul2 ( ) ;
357
-
358
- // f1 = (x * QZ - QX)^2
359
- let f1 = P :: new_from_slice ( & [ QX_sqr , QXQZ_m2 , QZ_sqr ] ) ;
360
-
361
- // f2 = -2*A*QX*QZ*x - 2*(QX*x + QZ)*(QZ*x + QX)
362
- // = -2 * (QX*QZ*x^2 + (A*QX*QZ + QX^2 + QZ^2)*x + QX*QZ)
363
- let f2_linear = -( * A * QXQZ + QX_sqr + QZ_sqr ) . mul2 ( ) ;
364
- let f2 = P :: new_from_slice ( & [ QXQZ_m2 , f2_linear, QXQZ_m2 ] ) ;
365
350
366
- // f3 = (x * QX - QZ)^2
367
- let f3 = P :: new_from_slice ( & [ QZ_sqr , QXQZ_m2 , QX_sqr ] ) ;
351
+ // Normalise all the points with only one inversion using Montgomery's trick
352
+ PointX :: batch_normalise ( & mut hI_points ) ;
368
353
369
- ( f1, f2, f3)
354
+ // Now set hI_roots to the x-coordinates of the points
355
+ for ( r, P ) in hI_roots. iter_mut ( ) . zip ( hI_points. iter ( ) ) {
356
+ * r = P . X
357
+ }
370
358
}
371
359
372
360
/// Given a point (X : Z) we want to compute three elliptic resultants on the Montgomery
@@ -398,7 +386,7 @@ impl<Fq: FqTrait> Curve<Fq> {
398
386
ker : & PointX < Fq > ,
399
387
) {
400
388
// TODO: this is dumb, but we need A rather than (A24 : C24) for the
401
- // current formula.. .
389
+ // current formula, or we work projectively in elliptic_resultants .
402
390
let A = ( * A24 / * C24 ) . mul4 ( ) - Fq :: TWO ;
403
391
404
392
// Initalise values for the addition chain, we repeatedly add [2]Q for each step
@@ -417,6 +405,7 @@ impl<Fq: FqTrait> Curve<Fq> {
417
405
}
418
406
}
419
407
408
+ /// Precompute the set of elements [i]ker for i in the set K
420
409
fn precompute_hK_points ( hK_points : & mut [ PointX < Fq > ] , A24 : & Fq , C24 : & Fq , kernel : & PointX < Fq > ) {
421
410
let mut Q = * kernel;
422
411
Self :: xdbl_proj ( A24 , C24 , & mut Q . X , & mut Q . Z ) ;
@@ -433,6 +422,8 @@ impl<Fq: FqTrait> Curve<Fq> {
433
422
}
434
423
}
435
424
425
+ /// Understanding the polynomial hK = prod(x * PZ - PX) for the set in hK, then
426
+ /// evaluate this polynomial at alpha = 1 and alpha = -1
436
427
fn hK_codomain ( hK_points : & [ PointX < Fq > ] ) -> ( Fq , Fq ) {
437
428
let mut h1 = Fq :: ONE ;
438
429
let mut h2 = Fq :: ONE ;
@@ -443,6 +434,8 @@ impl<Fq: FqTrait> Curve<Fq> {
443
434
( h1, h2)
444
435
}
445
436
437
+ /// Understanding the polynomial hK = prod(x * PZ - PX) for the set in hK, then
438
+ /// evaluate this polynomial at alpha and 1/alpha (projectively)
446
439
fn hK_eval ( hK_points : & [ PointX < Fq > ] , alpha : & Fq ) -> ( Fq , Fq ) {
447
440
let mut h1 = Fq :: ONE ;
448
441
let mut h2 = Fq :: ONE ;
@@ -453,6 +446,9 @@ impl<Fq: FqTrait> Curve<Fq> {
453
446
( h1, h2)
454
447
}
455
448
449
+ /// Compute an isogeny using the sqrt-velu algorithm O(\sqrt{ell}) complexity.
450
+ /// https://velusqrt.isogeny.org
451
+ /// WARNING: this function is grossly underperforming due to slow polynomial arithmetic.
456
452
pub fn sqrt_velu_odd_isogeny_proj < P : Poly < Fq > > (
457
453
A24 : & mut Fq ,
458
454
C24 : & mut Fq ,
@@ -462,21 +458,35 @@ impl<Fq: FqTrait> Curve<Fq> {
462
458
) {
463
459
// baby step, giant step values
464
460
// TODO: better sqrt?
465
- let b = ( ( ( degree + 1 ) as f64 ) . sqrt ( ) as usize ) / 2 ;
466
- let c = ( degree + 1 ) / ( 4 * b ) ;
467
- let stop = degree - 4 * b * c ;
461
+ let size_J = ( ( ( degree + 1 ) as f64 ) . sqrt ( ) as usize ) / 2 ;
462
+ let size_I = ( degree + 1 ) / ( 4 * size_J ) ;
463
+ let size_K = ( degree - 4 * size_J * size_I - 1 ) / 2 ;
468
464
469
- // Precompute the roots of the polynomial Prod(x - [i]P.x()) for i in the set I
470
- let mut hI_roots = vec ! [ Fq :: ZERO ; c ] ;
471
- Self :: precompute_hi_roots ( & mut hI_roots , A24 , C24 , kernel , b ) ;
465
+ // println!("b = {}", size_J);
466
+ // println!("c = {}", size_I) ;
467
+ // println!("stop = {}", size_K );
472
468
473
- // Precompute three polynomials for each point [j]P for j in J
474
- let mut eJ_coeffs = vec ! [ ( Fq :: ZERO , Fq :: ZERO , Fq :: ZERO , Fq :: ZERO ) ; b] ;
469
+ // Precompute the roots of the polynomial Prod(x - [i]P.x()) for i in the set I
470
+ // let start = Instant::now();
471
+ let mut hI_roots = vec ! [ Fq :: ZERO ; size_I] ;
472
+ Self :: precompute_hi_roots ( & mut hI_roots, A24 , C24 , kernel, size_J) ;
473
+ // let hi_time = start.elapsed();
474
+ // println!("hi precomp time: {:?}", hi_time);
475
+
476
+ // Precompute coefficients for three polynomials for each point [j]P for j in J
477
+ let mut eJ_coeffs = vec ! [ ( Fq :: ZERO , Fq :: ZERO , Fq :: ZERO , Fq :: ZERO ) ; size_J] ;
475
478
Self :: precompute_eJ_coeffs ( & mut eJ_coeffs, A24 , C24 , kernel) ;
479
+ // let ej_time = start.elapsed();
480
+ // println!("ej precomp time: {:?}", ej_time - hi_time);
476
481
477
482
// Precompute the polynomial (x - [k]P.x()) for k in the set K
478
- let mut hK_points = vec ! [ PointX :: INFINITY ; ( stop - 1 ) / 2 ] ;
483
+ let mut hK_points = vec ! [ PointX :: INFINITY ; size_K ] ;
479
484
Self :: precompute_hK_points ( & mut hK_points, A24 , C24 , kernel) ;
485
+ // let hk_time = start.elapsed();
486
+ // println!("hk precomp time: {:?}", hk_time - ej_time);
487
+
488
+ // let precomp = start.elapsed();
489
+ // println!("precomp time: {:?}", precomp);
480
490
481
491
// Compute prod(f0 + f1 + f2) and prod(f0 - f1 + f2) which we will compute
482
492
// resultants of with hI_roots.
@@ -497,9 +507,16 @@ impl<Fq: FqTrait> Curve<Fq> {
497
507
E1J *= t1;
498
508
}
499
509
510
+ // let e_comp = start.elapsed();
511
+ // println!("E comp time: {:?}", e_comp - precomp);
512
+
500
513
// Compute the codomain.
501
514
let r0 = E0J . resultant_from_roots ( & hI_roots) ;
502
515
let r1 = E1J . resultant_from_roots ( & hI_roots) ;
516
+
517
+ // let two_res = start.elapsed();
518
+ // println!("2x res time: {:?}", two_res - e_comp);
519
+
503
520
let ( m0, m1) = Self :: hK_codomain ( & hK_points) ;
504
521
505
522
// Compute (ri * mi)^8 * (A ∓ 2)^degree
@@ -571,6 +588,9 @@ impl<Fq: FqTrait> Curve<Fq> {
571
588
// Convert back to Montgomery (A24 : C24)
572
589
* A24 = A_ed ;
573
590
* C24 = A_ed - D_ed ;
591
+
592
+ // let stop = start.elapsed();
593
+ // println!("time for all: {:?}", stop);
574
594
}
575
595
576
596
// ============================================================
0 commit comments