From 4a23a8644774a775aefe96a87d0c57588a898d04 Mon Sep 17 00:00:00 2001 From: Thomas Coratger Date: Fri, 2 Feb 2024 13:20:03 +0100 Subject: [PATCH 1/7] add mul method for BigInteger --- ff/src/biginteger/mod.rs | 43 +++++++++++++++++++++++++++++++++++++- ff/src/biginteger/tests.rs | 28 +++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/ff/src/biginteger/mod.rs b/ff/src/biginteger/mod.rs index 1a0aa9219..d0822d538 100644 --- a/ff/src/biginteger/mod.rs +++ b/ff/src/biginteger/mod.rs @@ -421,6 +421,26 @@ impl BigInteger for BigInt { } } + #[inline] + fn mul(&mut self, other: &Self) { + if self.is_zero() || other.is_zero() { + *self = Self::zero(); + return; + } + + let mut res = Self::zero(); + let mut carry = 0; + + for i in 0..N { + for j in 0..(N - i) { + res.0[i + j] = mac_with_carry!(res.0[i + j], self.0[i], other.0[j], &mut carry); + } + carry = 0; + } + + *self = res + } + #[inline] fn div2(&mut self) { let mut t = 0; @@ -897,7 +917,7 @@ pub type BigInteger768 = BigInt<12>; pub type BigInteger832 = BigInt<13>; #[cfg(test)] -mod tests; +pub mod tests; /// This defines a `BigInteger`, a smart wrapper around a /// sequence of `u64` limbs, least-significant limb first. @@ -1041,6 +1061,27 @@ pub trait BigInteger: #[deprecated(since = "0.4.2", note = "please use the operator `>>` instead")] fn muln(&mut self, amt: u32); + /// Multiplies this [`BigInteger`] by another `BigInteger`, storing the result in `self`. + /// Overflow is ignored. + /// + /// # Example + /// + /// ``` + /// use ark_ff::{biginteger::BigInteger64 as B, BigInteger as _}; + /// + /// // Basic + /// let mut a = B::from(42u64); + /// let b = B::from(3u64); + /// a.mul(&b); + /// assert_eq!(a, B::from(126u64)); + /// + /// // Edge-Case + /// let mut zero = B::from(0u64); + /// zero.mul(5); + /// assert_eq!(zero, B::from(0u64)); + /// ``` + fn mul(&mut self, other: &Self); + /// Performs a rightwise bitshift of this number, effectively dividing /// it by 2. /// # Example diff --git a/ff/src/biginteger/tests.rs b/ff/src/biginteger/tests.rs index 9c65eb4c3..4182641fc 100644 --- a/ff/src/biginteger/tests.rs +++ b/ff/src/biginteger/tests.rs @@ -1,5 +1,6 @@ use crate::{biginteger::BigInteger, UniformRand}; use num_bigint::BigUint; +// use std::println; // Test elementary math operations for BigInteger. fn biginteger_arithmetic_test(a: B, b: B, zero: B) { @@ -49,6 +50,33 @@ fn biginteger_arithmetic_test(a: B, b: B, zero: B) { let mut a_plus_a = a; a_plus_a.add_with_carry(&a); // Won't assert anything about carry bit. assert_eq!(a_mul2, a_plus_a); + + // a * 1 = a + let mut a_mul = a; + a_mul.mul(&B::from(1u64)); + assert_eq!(a_mul, a); + + // a * 2 = a + a_mul.mul(&B::from(2u64)); + assert_eq!(a_mul, a_plus_a); + + // a * 2 * b = b * 2 * a + a_mul.mul(&b); + let mut b_mul = b; + b_mul.mul(&B::from(2u64)); + b_mul.mul(&a); + assert_eq!(a_mul, b_mul); + + // a * 2 * b * 0 = 0 + a_mul.mul(&zero); + assert!(a_mul.is_zero()); + + // a * 2 * ... * 2 = a * 2^n + let mut a_mul_n = a; + for _ in 0..20 { + a_mul_n.mul(&B::from(2u64)); + } + assert_eq!(a_mul_n, a << 20); } fn biginteger_shr() { From 34785af73588de5d36b3d7541f11ba48cce6eae9 Mon Sep 17 00:00:00 2001 From: Thomas Coratger Date: Fri, 2 Feb 2024 13:31:10 +0100 Subject: [PATCH 2/7] clean up --- ff/src/biginteger/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ff/src/biginteger/mod.rs b/ff/src/biginteger/mod.rs index d0822d538..237382079 100644 --- a/ff/src/biginteger/mod.rs +++ b/ff/src/biginteger/mod.rs @@ -917,7 +917,7 @@ pub type BigInteger768 = BigInt<12>; pub type BigInteger832 = BigInt<13>; #[cfg(test)] -pub mod tests; +mod tests; /// This defines a `BigInteger`, a smart wrapper around a /// sequence of `u64` limbs, least-significant limb first. From 511b2ad71285ae74bc3279dc62b09f925fc776ff Mon Sep 17 00:00:00 2001 From: Thomas Coratger Date: Fri, 2 Feb 2024 13:32:42 +0100 Subject: [PATCH 3/7] changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c1228e38..452e8113c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Pending +- [\#772](https://github.com/arkworks-rs/algebra/pull/772) (`ark-ff`) Implementation of `mul` method for `BigInteger`. + ### Breaking changes - [\#577](https://github.com/arkworks-rs/algebra/pull/577) (`ark-ff`, `ark-ec`) Add `AdditiveGroup`, a trait for additive groups (equipped with scalar field). From 3556d51263542930c3ce753edf80285df1b7bb5e Mon Sep 17 00:00:00 2001 From: Thomas Coratger Date: Fri, 2 Feb 2024 13:59:57 +0100 Subject: [PATCH 4/7] fix doc --- ff/src/biginteger/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ff/src/biginteger/mod.rs b/ff/src/biginteger/mod.rs index 237382079..4d1035b74 100644 --- a/ff/src/biginteger/mod.rs +++ b/ff/src/biginteger/mod.rs @@ -1077,7 +1077,7 @@ pub trait BigInteger: /// /// // Edge-Case /// let mut zero = B::from(0u64); - /// zero.mul(5); + /// zero.mul(&B::from(5u64)); /// assert_eq!(zero, B::from(0u64)); /// ``` fn mul(&mut self, other: &Self); From b2ab64cb5ec3e29b087d42836d2ce4948ac528b8 Mon Sep 17 00:00:00 2001 From: Thomas Coratger <60488569+tcoratger@users.noreply.github.com> Date: Mon, 5 Feb 2024 08:44:59 +0100 Subject: [PATCH 5/7] Update ff/src/biginteger/tests.rs Co-authored-by: Pratyush Mishra --- ff/src/biginteger/tests.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/ff/src/biginteger/tests.rs b/ff/src/biginteger/tests.rs index 4182641fc..8936ff53c 100644 --- a/ff/src/biginteger/tests.rs +++ b/ff/src/biginteger/tests.rs @@ -1,6 +1,5 @@ use crate::{biginteger::BigInteger, UniformRand}; use num_bigint::BigUint; -// use std::println; // Test elementary math operations for BigInteger. fn biginteger_arithmetic_test(a: B, b: B, zero: B) { From 49b967f83db6ea5556b26e2438cf7fe0f60d1a8b Mon Sep 17 00:00:00 2001 From: Thomas Coratger Date: Mon, 5 Feb 2024 11:40:16 +0100 Subject: [PATCH 6/7] fix review --- ff/src/biginteger/mod.rs | 83 ++++++++++++++++++++++++++++++++++++-- ff/src/biginteger/tests.rs | 63 ++++++++++++++++++++--------- 2 files changed, 124 insertions(+), 22 deletions(-) diff --git a/ff/src/biginteger/mod.rs b/ff/src/biginteger/mod.rs index a70f00d32..ba3d9b460 100644 --- a/ff/src/biginteger/mod.rs +++ b/ff/src/biginteger/mod.rs @@ -422,7 +422,29 @@ impl BigInteger for BigInt { } #[inline] - fn mul(&mut self, other: &Self) { + fn mul(&self, other: &Self) -> (Self, Self) { + if self.is_zero() || other.is_zero() { + let zero = Self::zero(); + return (zero, zero); + } + + let mut r = crate::const_helpers::MulBuffer::::zeroed(); + + let mut carry = 0; + + for i in 0..N { + for j in 0..N { + r[i + j] = mac_with_carry!(r[i + j], self.0[i], other.0[j], &mut carry); + } + r.b1[i] = carry; + carry = 0; + } + + return (Self(r.b0), Self(r.b1)); + } + + #[inline] + fn mul_low(&mut self, other: &Self) { if self.is_zero() || other.is_zero() { *self = Self::zero(); return; @@ -441,6 +463,11 @@ impl BigInteger for BigInt { *self = res } + #[inline] + fn mul_high(&self, other: &Self) -> Self { + self.mul(other).1 + } + #[inline] fn div2(&mut self) { let mut t = 0; @@ -1072,15 +1099,63 @@ pub trait BigInteger: /// // Basic /// let mut a = B::from(42u64); /// let b = B::from(3u64); - /// a.mul(&b); + /// a.mul_low(&b); /// assert_eq!(a, B::from(126u64)); /// /// // Edge-Case /// let mut zero = B::from(0u64); - /// zero.mul(&B::from(5u64)); + /// zero.mul_low(&B::from(5u64)); /// assert_eq!(zero, B::from(0u64)); /// ``` - fn mul(&mut self, other: &Self); + fn mul_low(&mut self, other: &Self); + + /// Multiplies this [`BigInteger`] by another `BigInteger`, returning the high bits of the result. + /// + /// # Example + /// + /// ``` + /// use ark_ff::{biginteger::BigInteger64 as B, BigInteger as _}; + /// + /// // Basic + /// let (one, x) = (B::from(1u64), B::from(2u64)); + /// let r = x.mul_high(&one); + /// assert_eq!(r, B::from(0u64)); + /// + /// // Edge-Case + /// let mut x = B::from(u64::MAX); + /// let r = x.mul_high(&B::from(2u64)); + /// assert_eq!(r, B::from(1u64)) + /// ``` + fn mul_high(&self, other: &Self) -> Self; + + /// Multiplies this [`BigInteger`] by another `BigInteger`, returning both low and high bits of the result. + /// + /// # Example + /// + /// ``` + /// use ark_ff::{biginteger::BigInteger64 as B, BigInteger as _}; + /// + /// // Basic + /// let mut a = B::from(42u64); + /// let b = B::from(3u64); + /// let (low_bits, high_bits) = a.mul(&b); + /// assert_eq!(low_bits, B::from(126u64)); + /// assert_eq!(high_bits, B::from(0u64)); + /// + /// // Edge-Case + /// let mut x = B::from(u64::MAX); + /// let (low_bits, high_bits) = x.mul(&B::from(2u64)); + /// assert_eq!(low_bits, B::from(u64::MAX)); + /// assert_eq!(high_bits, B::from(u64::MAX)); + /// + /// let mut x = B::from(u64::MAX); + /// let mut max_plus_max = x; + /// max_plus_max.add_with_carry(&x); + /// let (low_bits, high_bits) = x.mul(&B::from(2u64)); + /// assert_eq!(low_bits, max_plus_max); + /// assert_eq!(high_bits, B::from(1u64)); + /// ``` + fn mul(&self, other: &Self) -> (Self, Self); /// Performs a rightwise bitshift of this number, effectively dividing /// it by 2. diff --git a/ff/src/biginteger/tests.rs b/ff/src/biginteger/tests.rs index 8936ff53c..83767aaf5 100644 --- a/ff/src/biginteger/tests.rs +++ b/ff/src/biginteger/tests.rs @@ -2,7 +2,7 @@ use crate::{biginteger::BigInteger, UniformRand}; use num_bigint::BigUint; // Test elementary math operations for BigInteger. -fn biginteger_arithmetic_test(a: B, b: B, zero: B) { +fn biginteger_arithmetic_test(a: B, b: B, zero: B, max: B) { // zero == zero assert_eq!(zero, zero); @@ -47,35 +47,62 @@ fn biginteger_arithmetic_test(a: B, b: B, zero: B) { let mut a_mul2 = a; a_mul2.mul2(); let mut a_plus_a = a; - a_plus_a.add_with_carry(&a); // Won't assert anything about carry bit. + let carry_a_plus_a = a_plus_a.add_with_carry(&a); // Won't assert anything about carry bit. assert_eq!(a_mul2, a_plus_a); // a * 1 = a let mut a_mul = a; - a_mul.mul(&B::from(1u64)); + a_mul.mul_low(&B::from(1u64)); assert_eq!(a_mul, a); // a * 2 = a - a_mul.mul(&B::from(2u64)); + a_mul.mul_low(&B::from(2u64)); assert_eq!(a_mul, a_plus_a); // a * 2 * b = b * 2 * a - a_mul.mul(&b); + a_mul.mul_low(&b); let mut b_mul = b; - b_mul.mul(&B::from(2u64)); - b_mul.mul(&a); + b_mul.mul_low(&B::from(2u64)); + b_mul.mul_low(&a); assert_eq!(a_mul, b_mul); // a * 2 * b * 0 = 0 - a_mul.mul(&zero); + a_mul.mul_low(&zero); assert!(a_mul.is_zero()); // a * 2 * ... * 2 = a * 2^n let mut a_mul_n = a; for _ in 0..20 { - a_mul_n.mul(&B::from(2u64)); + a_mul_n.mul_low(&B::from(2u64)); } assert_eq!(a_mul_n, a << 20); + + // a * 0 = (0, 0) + assert_eq!(a.mul(&zero), (zero, zero)); + + // a * 1 = (a, 0) + assert_eq!(a.mul(&B::from(1u64)), (a, zero)); + + // a * 1 = 0 (high part of the result) + assert_eq!(a.mul_high(&B::from(1u64)), (zero)); + + // a * 0 = 0 (high part of the result) + assert!(a.mul_high(&zero).is_zero()); + + // If a + a has a carry + if carry_a_plus_a { + // a + a has a carry: high part of a * 2 is not zero + assert_ne!(a.mul_high(&B::from(2u64)), zero); + } else { + // a + a has no carry: high part of a * 2 is zero + assert_eq!(a.mul_high(&B::from(2u64)), zero); + } + + // max + max = max * 2 + let mut max_plus_max = max; + max_plus_max.add_with_carry(&max); + assert_eq!(max.mul(&B::from(2u64)), (max_plus_max, B::from(1u64))); + assert_eq!(max.mul_high(&B::from(2u64)), B::from(1u64)); } fn biginteger_shr() { @@ -201,11 +228,11 @@ fn biginteger_conversion_test() { } // Wrapper test function for BigInteger -fn test_biginteger(zero: B) { +fn test_biginteger(max: B, zero: B) { let mut rng = ark_std::test_rng(); let a: B = UniformRand::rand(&mut rng); let b: B = UniformRand::rand(&mut rng); - biginteger_arithmetic_test(a, b, zero); + biginteger_arithmetic_test(a, b, zero, max); biginteger_bits_test::(); biginteger_conversion_test::(); biginteger_bitwise_ops_test::(); @@ -216,41 +243,41 @@ fn test_biginteger(zero: B) { #[test] fn test_biginteger64() { use crate::biginteger::BigInteger64 as B; - test_biginteger(B::new([0u64; 1])); + test_biginteger(B::new([u64::MAX; 1]), B::new([0u64; 1])); } #[test] fn test_biginteger128() { use crate::biginteger::BigInteger128 as B; - test_biginteger(B::new([0u64; 2])); + test_biginteger(B::new([u64::MAX; 2]), B::new([0u64; 2])); } #[test] fn test_biginteger256() { use crate::biginteger::BigInteger256 as B; - test_biginteger(B::new([0u64; 4])); + test_biginteger(B::new([u64::MAX; 4]), B::new([0u64; 4])); } #[test] fn test_biginteger384() { use crate::biginteger::BigInteger384 as B; - test_biginteger(B::new([0u64; 6])); + test_biginteger(B::new([u64::MAX; 6]), B::new([0u64; 6])); } #[test] fn test_biginteger448() { use crate::biginteger::BigInteger448 as B; - test_biginteger(B::new([0u64; 7])); + test_biginteger(B::new([u64::MAX; 7]), B::new([0u64; 7])); } #[test] fn test_biginteger768() { use crate::biginteger::BigInteger768 as B; - test_biginteger(B::new([0u64; 12])); + test_biginteger(B::new([u64::MAX; 12]), B::new([0u64; 12])); } #[test] fn test_biginteger832() { use crate::biginteger::BigInteger832 as B; - test_biginteger(B::new([0u64; 13])); + test_biginteger(B::new([u64::MAX; 13]), B::new([0u64; 13])); } From e24a2366238b29a45067e2e6d00e832e34e68fe3 Mon Sep 17 00:00:00 2001 From: Thomas Coratger Date: Mon, 5 Feb 2024 12:16:23 +0100 Subject: [PATCH 7/7] fix doc --- ff/src/biginteger/mod.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/ff/src/biginteger/mod.rs b/ff/src/biginteger/mod.rs index ba3d9b460..e0d025965 100644 --- a/ff/src/biginteger/mod.rs +++ b/ff/src/biginteger/mod.rs @@ -1144,11 +1144,6 @@ pub trait BigInteger: /// /// // Edge-Case /// let mut x = B::from(u64::MAX); - /// let (low_bits, high_bits) = x.mul(&B::from(2u64)); - /// assert_eq!(low_bits, B::from(u64::MAX)); - /// assert_eq!(high_bits, B::from(u64::MAX)); - /// - /// let mut x = B::from(u64::MAX); /// let mut max_plus_max = x; /// max_plus_max.add_with_carry(&x); /// let (low_bits, high_bits) = x.mul(&B::from(2u64));