From afffe962f92aff598cf1aa2082c792669209cadf Mon Sep 17 00:00:00 2001 From: mandragore Date: Wed, 2 May 2018 01:55:38 +0300 Subject: [PATCH 1/5] Implement Product trait for Scalar --- src/scalar.rs | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/scalar.rs b/src/scalar.rs index d41d22b..eb14546 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -19,6 +19,8 @@ use core::ops::{Sub, SubAssign}; use core::ops::{Mul, MulAssign}; use core::ops::{Index}; use core::cmp::{Eq, PartialEq}; +use core::iter::Product; +use core::borrow::Borrow; #[cfg(feature = "std")] use rand::Rng; @@ -293,6 +295,18 @@ impl<'de> Deserialize<'de> for Scalar { } } +impl Product for Scalar +where + T: Borrow +{ + fn product(iter: I) -> Self + where + I: Iterator + { + iter.fold(Scalar::one(), |acc, item| acc * item.borrow()) + } +} + impl Scalar { /// Return a `Scalar` chosen uniformly at random using a user-provided RNG. /// @@ -912,6 +926,37 @@ mod test { assert_eq!(should_be_X_times_Y, X_TIMES_Y); } + #[allow(non_snake_case)] + #[test] + fn impl_product() { + // Test that product works for non-empty iterators + let X_Y_vector = vec![X, Y]; + let should_be_X_times_Y: Scalar = X_Y_vector.iter().product(); + assert_eq!(should_be_X_times_Y, X_TIMES_Y); + + // Test that product works for the empty iterator + let one = Scalar::one(); + let empty_vector = vec![]; + let should_be_one: Scalar = empty_vector.iter().product(); + assert_eq!(should_be_one, one); + + // Test that product works for iterators where Item = Scalar + let xs = [Scalar::from_u64(2); 10]; + let ys = [Scalar::from_u64(3); 10]; + // now zs is an iterator with Item = Scalar + let zs = xs.iter().zip(ys.iter()).map(|(x,y)| x * y); + + let x_prod: Scalar = xs.iter().product(); + let y_prod: Scalar = ys.iter().product(); + let z_prod: Scalar = zs.product(); + + assert_eq!(x_prod, Scalar::from_u64(1024)); + assert_eq!(y_prod, Scalar::from_u64(59049)); + assert_eq!(z_prod, Scalar::from_u64(60466176)); + assert_eq!(x_prod * y_prod, z_prod); + + } + #[test] fn square() { let expected = &X * &X; From 3ab087ea7f0ebcb7d0e3c71bb6a6a22feda0884a Mon Sep 17 00:00:00 2001 From: mandragore Date: Wed, 2 May 2018 01:56:23 +0300 Subject: [PATCH 2/5] Implement Sum trait for Scalar --- src/scalar.rs | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/src/scalar.rs b/src/scalar.rs index eb14546..300abf7 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -19,7 +19,7 @@ use core::ops::{Sub, SubAssign}; use core::ops::{Mul, MulAssign}; use core::ops::{Index}; use core::cmp::{Eq, PartialEq}; -use core::iter::Product; +use core::iter::{Product, Sum}; use core::borrow::Borrow; #[cfg(feature = "std")] @@ -307,6 +307,18 @@ where } } +impl Sum for Scalar +where + T: Borrow +{ + fn sum(iter: I) -> Self + where + I: Iterator + { + iter.fold(Scalar::zero(), |acc, item| acc + item.borrow()) + } +} + impl Scalar { /// Return a `Scalar` chosen uniformly at random using a user-provided RNG. /// @@ -957,6 +969,37 @@ mod test { } + #[test] + fn impl_sum() { + + // Test that sum works for non-empty iterators + let two = Scalar::from_u64(2); + let one_vector = vec![Scalar::one(), Scalar::one()]; + let should_be_two: Scalar = one_vector.iter().sum(); + assert_eq!(should_be_two, two); + + // Test that sum works for the empty iterator + let zero = Scalar::zero(); + let empty_vector = vec![]; + let should_be_zero: Scalar = empty_vector.iter().sum(); + assert_eq!(should_be_zero, zero); + + // Test that sum works for owned types + let xs = [Scalar::from_u64(1); 10]; + let ys = [Scalar::from_u64(2); 10]; + // now zs is an iterator with Item = Scalar + let zs = xs.iter().zip(ys.iter()).map(|(x,y)| x + y); + + let x_sum: Scalar = xs.iter().sum(); + let y_sum: Scalar = ys.iter().sum(); + let z_sum: Scalar = zs.sum(); + + assert_eq!(x_sum, Scalar::from_u64(10)); + assert_eq!(y_sum, Scalar::from_u64(20)); + assert_eq!(z_sum, Scalar::from_u64(30)); + assert_eq!(x_sum + y_sum, z_sum); + } + #[test] fn square() { let expected = &X * &X; From 01fd44ffd2cf6bb608b421c622202cf214571077 Mon Sep 17 00:00:00 2001 From: mandragore Date: Wed, 2 May 2018 01:57:24 +0300 Subject: [PATCH 3/5] Remove trailing whitespaces --- src/scalar.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/scalar.rs b/src/scalar.rs index 300abf7..f1b03a7 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -566,7 +566,7 @@ impl Scalar { /// $$ /// k = \sum\_{i=0}\^m k\_i 2^i, /// $$ - /// and split the sum as + /// and split the sum as /// $$ /// k = \sum\_{i=0}^{w-1} k\_i 2^i + 2^w \sum\_{i=0} k\_{i+w} 2^i /// $$ @@ -607,7 +607,7 @@ impl Scalar { let width = 1 << w; let window_mask = width - 1; - + let mut pos = 0; let mut carry = 0; while pos < 256 { From aff69a58a46b8f8daf3ee385ddecfeca45251963 Mon Sep 17 00:00:00 2001 From: mandragore Date: Thu, 3 May 2018 01:41:05 +0300 Subject: [PATCH 4/5] Implement Sum trait for EdwardsPoint --- src/edwards.rs | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/edwards.rs b/src/edwards.rs index 8487073..5ba490c 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -98,6 +98,7 @@ use core::iter::Iterator; use core::ops::{Add, Sub, Neg}; use core::ops::{AddAssign, SubAssign}; use core::ops::{Mul, MulAssign}; +use core::iter::Sum; use core::borrow::Borrow; use subtle::ConditionallyAssignable; @@ -427,6 +428,19 @@ impl<'b> SubAssign<&'b EdwardsPoint> for EdwardsPoint { define_sub_assign_variants!(LHS = EdwardsPoint, RHS = EdwardsPoint); +impl Sum for EdwardsPoint +where + T: Borrow +{ + fn sum(iter: I) -> Self + where + I: Iterator + { + iter.fold(EdwardsPoint::identity(), |acc, item| acc + item.borrow()) + } +} + + // ------------------------------------------------------------------------ // Negation // ------------------------------------------------------------------------ @@ -1092,6 +1106,38 @@ mod test { assert_eq!(bp16.compress(), BASE16_CMPRSSD); } + #[test] + fn impl_sum() { + + // Test that sum works for non-empty iterators + let BASE = constants::ED25519_BASEPOINT_POINT; + + let s1 = Scalar::from_u64(999); + let P1 = &BASE * &s1; + + let s2 = Scalar::from_u64(333); + let P2 = &BASE * &s2; + + let vec = vec![P1.clone(), P2.clone()]; + let sum: EdwardsPoint = vec.iter().sum(); + + assert_eq!(sum, P1 + P2); + + // Test that sum works for the empty iterator + let empty_vector: Vec = vec![]; + let sum: EdwardsPoint = empty_vector.iter().sum(); + + assert_eq!(sum, EdwardsPoint::identity()); + + // Test that sum works on owning iterators + let s = Scalar::from_u64(2); + let mapped = vec.iter().map(|x| x * &s); + let sum: EdwardsPoint = mapped.sum(); + + assert_eq!(sum, &P1 * &s + &P2 * &s); + } + + /// Test that the conditional assignment trait works for AffineNielsPoints. #[test] fn conditional_assign_for_affine_niels_point() { From 96d2fe49053853a9ae91338dd87b771e9e0e8056 Mon Sep 17 00:00:00 2001 From: mandragore Date: Thu, 3 May 2018 01:41:32 +0300 Subject: [PATCH 5/5] Implement Sum trait for RistrettoPoint Closes #131. --- src/ristretto.rs | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/ristretto.rs b/src/ristretto.rs index 2d89f84..c5b86f6 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -169,6 +169,7 @@ use core::fmt::Debug; use core::ops::{Add, Sub, Neg}; use core::ops::{AddAssign, SubAssign}; use core::ops::{Mul, MulAssign}; +use core::iter::Sum; use core::borrow::Borrow; #[cfg(feature = "std")] @@ -732,6 +733,18 @@ impl<'b> SubAssign<&'b RistrettoPoint> for RistrettoPoint { define_sub_assign_variants!(LHS = RistrettoPoint, RHS = RistrettoPoint); +impl Sum for RistrettoPoint +where + T: Borrow +{ + fn sum(iter: I) -> Self + where + I: Iterator + { + iter.fold(RistrettoPoint::identity(), |acc, item| acc + item.borrow()) + } +} + impl<'a> Neg for &'a RistrettoPoint { type Output = RistrettoPoint; @@ -1033,6 +1046,37 @@ mod test { assert!(P1.compress().as_bytes() == P2.compress().as_bytes()); } + #[test] + fn impl_sum() { + + // Test that sum works for non-empty iterators + let BASE = constants::RISTRETTO_BASEPOINT_POINT; + + let s1 = Scalar::from_u64(999); + let P1 = &BASE * &s1; + + let s2 = Scalar::from_u64(333); + let P2 = &BASE * &s2; + + let vec = vec![P1.clone(), P2.clone()]; + let sum: RistrettoPoint = vec.iter().sum(); + + assert_eq!(sum, P1 + P2); + + // Test that sum works for the empty iterator + let empty_vector: Vec = vec![]; + let sum: RistrettoPoint = empty_vector.iter().sum(); + + assert_eq!(sum, RistrettoPoint::identity()); + + // Test that sum works on owning iterators + let s = Scalar::from_u64(2); + let mapped = vec.iter().map(|x| x * &s); + let sum: RistrettoPoint = mapped.sum(); + + assert_eq!(sum, &P1 * &s + &P2 * &s); + } + #[test] fn decompress_negative_s_fails() { // constants::d is neg, so decompression should fail as |d| != d.