Add implementation of batch normalization for projective elements.
This commit is contained in:
parent
15b9d024cf
commit
450587f19b
|
@ -42,6 +42,11 @@ fn criterion_benchmark(c: &mut Criterion) {
|
|||
let a = G1Projective::generator();
|
||||
let a_affine = G1Affine::generator();
|
||||
let s = Scalar::from_raw([1, 2, 3, 4]);
|
||||
|
||||
const N: usize = 10000;
|
||||
let v = vec![G1Projective::generator(); N];
|
||||
let mut q = vec![G1Affine::identity(); N];
|
||||
|
||||
c.bench_function(&format!("{} check on curve", name), move |b| {
|
||||
b.iter(|| black_box(a).is_on_curve())
|
||||
});
|
||||
|
@ -63,6 +68,12 @@ fn criterion_benchmark(c: &mut Criterion) {
|
|||
c.bench_function(&format!("{} scalar multiplication", name), move |b| {
|
||||
b.iter(|| black_box(a) * black_box(s))
|
||||
});
|
||||
c.bench_function(&format!("{} batch to affine n={}", name, N), move |b| {
|
||||
b.iter(|| {
|
||||
G1Projective::batch_normalize(black_box(&v), black_box(&mut q));
|
||||
black_box(&q)[0]
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// G2Affine
|
||||
|
@ -100,6 +111,11 @@ fn criterion_benchmark(c: &mut Criterion) {
|
|||
let a = G2Projective::generator();
|
||||
let a_affine = G2Affine::generator();
|
||||
let s = Scalar::from_raw([1, 2, 3, 4]);
|
||||
|
||||
const N: usize = 10000;
|
||||
let v = vec![G2Projective::generator(); N];
|
||||
let mut q = vec![G2Affine::identity(); N];
|
||||
|
||||
c.bench_function(&format!("{} check on curve", name), move |b| {
|
||||
b.iter(|| black_box(a).is_on_curve())
|
||||
});
|
||||
|
@ -121,6 +137,12 @@ fn criterion_benchmark(c: &mut Criterion) {
|
|||
c.bench_function(&format!("{} scalar multiplication", name), move |b| {
|
||||
b.iter(|| black_box(a) * black_box(s))
|
||||
});
|
||||
c.bench_function(&format!("{} batch to affine n={}", name, N), move |b| {
|
||||
b.iter(|| {
|
||||
G2Projective::batch_normalize(black_box(&v), black_box(&mut q));
|
||||
black_box(&q)[0]
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
79
src/g1.rs
79
src/g1.rs
|
@ -737,6 +737,46 @@ impl G1Projective {
|
|||
acc
|
||||
}
|
||||
|
||||
/// Converts a batch of `G1Projective` elements into `G1Affine` elements. This
|
||||
/// function will panic if `p.len() != q.len()`.
|
||||
pub fn batch_normalize(p: &[Self], q: &mut [G1Affine]) {
|
||||
assert_eq!(p.len(), q.len());
|
||||
|
||||
let mut acc = Fp::one();
|
||||
for (p, q) in p.iter().zip(q.iter_mut()) {
|
||||
// We use the `x` field of `G1Affine` to store the product
|
||||
// of previous z-coordinates seen.
|
||||
q.x = acc;
|
||||
|
||||
// We will end up skipping all identities in p
|
||||
acc = Fp::conditional_select(&(acc * p.z), &acc, p.is_identity());
|
||||
}
|
||||
|
||||
// This is the inverse, as all z-coordinates are nonzero and the ones
|
||||
// that are not are skipped.
|
||||
acc = acc.invert().unwrap();
|
||||
|
||||
for (p, q) in p.iter().rev().zip(q.iter_mut().rev()) {
|
||||
let skip = p.is_identity();
|
||||
|
||||
// Compute tmp = 1/z
|
||||
let tmp = q.x * acc;
|
||||
|
||||
// Cancel out z-coordinate in denominator of `acc`
|
||||
acc = Fp::conditional_select(&(acc * p.z), &acc, skip);
|
||||
|
||||
// Set the coordinates to the correct value
|
||||
let tmp2 = tmp.square();
|
||||
let tmp3 = tmp2 * tmp;
|
||||
|
||||
q.x = p.x * tmp2;
|
||||
q.y = p.y * tmp3;
|
||||
q.infinity = Choice::from(0u8);
|
||||
|
||||
*q = G1Affine::conditional_select(&q, &G1Affine::identity(), skip);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if this element is the identity (the point at infinity).
|
||||
#[inline]
|
||||
pub fn is_identity(&self) -> Choice {
|
||||
|
@ -1264,3 +1304,42 @@ fn test_is_torsion_free() {
|
|||
assert!(bool::from(G1Affine::identity().is_torsion_free()));
|
||||
assert!(bool::from(G1Affine::generator().is_torsion_free()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_batch_normalize() {
|
||||
let a = G1Projective::generator().double();
|
||||
let b = a.double();
|
||||
let c = b.double();
|
||||
|
||||
for a_identity in (0..1).map(|n| n == 1) {
|
||||
for b_identity in (0..1).map(|n| n == 1) {
|
||||
for c_identity in (0..1).map(|n| n == 1) {
|
||||
let mut v = [a, b, c];
|
||||
if a_identity {
|
||||
v[0] = G1Projective::identity()
|
||||
}
|
||||
if b_identity {
|
||||
v[1] = G1Projective::identity()
|
||||
}
|
||||
if c_identity {
|
||||
v[2] = G1Projective::identity()
|
||||
}
|
||||
|
||||
let mut t = [
|
||||
G1Affine::identity(),
|
||||
G1Affine::identity(),
|
||||
G1Affine::identity(),
|
||||
];
|
||||
let expected = [
|
||||
G1Affine::from(v[0]),
|
||||
G1Affine::from(v[1]),
|
||||
G1Affine::from(v[2]),
|
||||
];
|
||||
|
||||
G1Projective::batch_normalize(&v[..], &mut t[..]);
|
||||
|
||||
assert_eq!(&t[..], &expected[..]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
79
src/g2.rs
79
src/g2.rs
|
@ -829,6 +829,46 @@ impl G2Projective {
|
|||
acc
|
||||
}
|
||||
|
||||
/// Converts a batch of `G2Projective` elements into `G2Affine` elements. This
|
||||
/// function will panic if `p.len() != q.len()`.
|
||||
pub fn batch_normalize(p: &[Self], q: &mut [G2Affine]) {
|
||||
assert_eq!(p.len(), q.len());
|
||||
|
||||
let mut acc = Fp2::one();
|
||||
for (p, q) in p.iter().zip(q.iter_mut()) {
|
||||
// We use the `x` field of `G2Affine` to store the product
|
||||
// of previous z-coordinates seen.
|
||||
q.x = acc;
|
||||
|
||||
// We will end up skipping all identities in p
|
||||
acc = Fp2::conditional_select(&(acc * p.z), &acc, p.is_identity());
|
||||
}
|
||||
|
||||
// This is the inverse, as all z-coordinates are nonzero and the ones
|
||||
// that are not are skipped.
|
||||
acc = acc.invert().unwrap();
|
||||
|
||||
for (p, q) in p.iter().rev().zip(q.iter_mut().rev()) {
|
||||
let skip = p.is_identity();
|
||||
|
||||
// Compute tmp = 1/z
|
||||
let tmp = q.x * acc;
|
||||
|
||||
// Cancel out z-coordinate in denominator of `acc`
|
||||
acc = Fp2::conditional_select(&(acc * p.z), &acc, skip);
|
||||
|
||||
// Set the coordinates to the correct value
|
||||
let tmp2 = tmp.square();
|
||||
let tmp3 = tmp2 * tmp;
|
||||
|
||||
q.x = p.x * tmp2;
|
||||
q.y = p.y * tmp3;
|
||||
q.infinity = Choice::from(0u8);
|
||||
|
||||
*q = G2Affine::conditional_select(&q, &G2Affine::identity(), skip);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if this element is the identity (the point at infinity).
|
||||
#[inline]
|
||||
pub fn is_identity(&self) -> Choice {
|
||||
|
@ -1512,3 +1552,42 @@ fn test_is_torsion_free() {
|
|||
assert!(bool::from(G2Affine::identity().is_torsion_free()));
|
||||
assert!(bool::from(G2Affine::generator().is_torsion_free()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_batch_normalize() {
|
||||
let a = G2Projective::generator().double();
|
||||
let b = a.double();
|
||||
let c = b.double();
|
||||
|
||||
for a_identity in (0..1).map(|n| n == 1) {
|
||||
for b_identity in (0..1).map(|n| n == 1) {
|
||||
for c_identity in (0..1).map(|n| n == 1) {
|
||||
let mut v = [a, b, c];
|
||||
if a_identity {
|
||||
v[0] = G2Projective::identity()
|
||||
}
|
||||
if b_identity {
|
||||
v[1] = G2Projective::identity()
|
||||
}
|
||||
if c_identity {
|
||||
v[2] = G2Projective::identity()
|
||||
}
|
||||
|
||||
let mut t = [
|
||||
G2Affine::identity(),
|
||||
G2Affine::identity(),
|
||||
G2Affine::identity(),
|
||||
];
|
||||
let expected = [
|
||||
G2Affine::from(v[0]),
|
||||
G2Affine::from(v[1]),
|
||||
G2Affine::from(v[2]),
|
||||
];
|
||||
|
||||
G2Projective::batch_normalize(&v[..], &mut t[..]);
|
||||
|
||||
assert_eq!(&t[..], &expected[..]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue