Skip to content

Commit d5bfe45

Browse files
committed
batch normalise roots for hI polynomial
1 parent b8d51b6 commit d5bfe45

File tree

2 files changed

+74
-45
lines changed

2 files changed

+74
-45
lines changed

src/elliptic/point.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,15 @@ impl<Fq: FpTrait> PointX<Fq> {
5555
Fq::condswap(&mut P.X, &mut Q.X, ctl);
5656
Fq::condswap(&mut P.Z, &mut Q.Z, ctl);
5757
}
58+
59+
pub fn batch_normalise(points: &mut [Self]) {
60+
let mut zs: Vec<Fq> = points.iter().map(|point| point.Z).collect();
61+
Fq::batch_invert(&mut zs);
62+
for (i, P) in points.iter_mut().enumerate() {
63+
P.X *= zs[i];
64+
P.Z = Fq::ONE;
65+
}
66+
}
5867
}
5968

6069
impl<Fq: FpTrait> ::std::fmt::Display for PointX<Fq> {

src/elliptic/velu.rs

Lines changed: 65 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
// TODO:
22
//
3+
// Optimise Sqrt Velu, current implementation is very slow!
4+
//
35
// Cofactor clearing. At the moment clearing the cofactor means multiplying by each ell_i e_i times,
46
// which seems silly. I think we should probably have some function which converts the prime factorisation into &[u64; ...]
57
// and then we use these limbs to do var time point multiplication.
68

9+
// use std::time::Instant;
10+
711
use fp2::traits::Fp as FqTrait;
812

913
use crate::polynomial_ring::poly::Poly;
@@ -318,6 +322,10 @@ impl<Fq: FqTrait> Curve<Fq> {
318322
/// Compute roots of the polynomial \Prod (Z - x(Q)) for Q in the set
319323
/// I = {2b(2i + 1) | 0 <= i < c}
320324
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+
321329
// Set Q = [2b]K
322330
let mut Q = Self::xmul_proj_u64_vartime(A24, C24, kernel, (b + b) as u64);
323331

@@ -326,47 +334,27 @@ impl<Fq: FqTrait> Curve<Fq> {
326334
Self::xdbl_proj(A24, C24, &mut step.X, &mut step.Z);
327335
let mut diff = Q;
328336

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+
333346
let R = Self::xdiff_add(&Q, &step, &diff);
334347
diff = Q;
335348
Q = R;
336349
}
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]);
365350

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);
368353

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+
}
370358
}
371359

372360
/// Given a point (X : Z) we want to compute three elliptic resultants on the Montgomery
@@ -398,7 +386,7 @@ impl<Fq: FqTrait> Curve<Fq> {
398386
ker: &PointX<Fq>,
399387
) {
400388
// 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.
402390
let A = (*A24 / *C24).mul4() - Fq::TWO;
403391

404392
// Initalise values for the addition chain, we repeatedly add [2]Q for each step
@@ -417,6 +405,7 @@ impl<Fq: FqTrait> Curve<Fq> {
417405
}
418406
}
419407

408+
/// Precompute the set of elements [i]ker for i in the set K
420409
fn precompute_hK_points(hK_points: &mut [PointX<Fq>], A24: &Fq, C24: &Fq, kernel: &PointX<Fq>) {
421410
let mut Q = *kernel;
422411
Self::xdbl_proj(A24, C24, &mut Q.X, &mut Q.Z);
@@ -433,6 +422,8 @@ impl<Fq: FqTrait> Curve<Fq> {
433422
}
434423
}
435424

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
436427
fn hK_codomain(hK_points: &[PointX<Fq>]) -> (Fq, Fq) {
437428
let mut h1 = Fq::ONE;
438429
let mut h2 = Fq::ONE;
@@ -443,6 +434,8 @@ impl<Fq: FqTrait> Curve<Fq> {
443434
(h1, h2)
444435
}
445436

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)
446439
fn hK_eval(hK_points: &[PointX<Fq>], alpha: &Fq) -> (Fq, Fq) {
447440
let mut h1 = Fq::ONE;
448441
let mut h2 = Fq::ONE;
@@ -453,6 +446,9 @@ impl<Fq: FqTrait> Curve<Fq> {
453446
(h1, h2)
454447
}
455448

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.
456452
pub fn sqrt_velu_odd_isogeny_proj<P: Poly<Fq>>(
457453
A24: &mut Fq,
458454
C24: &mut Fq,
@@ -462,21 +458,35 @@ impl<Fq: FqTrait> Curve<Fq> {
462458
) {
463459
// baby step, giant step values
464460
// 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;
468464

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);
472468

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];
475478
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);
476481

477482
// 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];
479484
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);
480490

481491
// Compute prod(f0 + f1 + f2) and prod(f0 - f1 + f2) which we will compute
482492
// resultants of with hI_roots.
@@ -497,9 +507,16 @@ impl<Fq: FqTrait> Curve<Fq> {
497507
E1J *= t1;
498508
}
499509

510+
// let e_comp = start.elapsed();
511+
// println!("E comp time: {:?}", e_comp - precomp);
512+
500513
// Compute the codomain.
501514
let r0 = E0J.resultant_from_roots(&hI_roots);
502515
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+
503520
let (m0, m1) = Self::hK_codomain(&hK_points);
504521

505522
// Compute (ri * mi)^8 * (A ∓ 2)^degree
@@ -571,6 +588,9 @@ impl<Fq: FqTrait> Curve<Fq> {
571588
// Convert back to Montgomery (A24 : C24)
572589
*A24 = A_ed;
573590
*C24 = A_ed - D_ed;
591+
592+
// let stop = start.elapsed();
593+
// println!("time for all: {:?}", stop);
574594
}
575595

576596
// ============================================================

0 commit comments

Comments
 (0)