From 081c8d82dce8fbe164d42f3489618c115366ea7f Mon Sep 17 00:00:00 2001 From: jotabulacios Date: Mon, 2 Dec 2024 15:43:17 -0300 Subject: [PATCH 01/18] WIP --- .../field/fields/fft_friendly/babybear_u32.rs | 283 ++++ math/src/field/fields/fft_friendly/mod.rs | 3 + math/src/field/fields/mod.rs | 1 + .../u32_montgomery_backend_prime_field.rs | 1343 +++++++++++++++++ 4 files changed, 1630 insertions(+) create mode 100644 math/src/field/fields/fft_friendly/babybear_u32.rs create mode 100644 math/src/field/fields/u32_montgomery_backend_prime_field.rs diff --git a/math/src/field/fields/fft_friendly/babybear_u32.rs b/math/src/field/fields/fft_friendly/babybear_u32.rs new file mode 100644 index 000000000..c6f29222c --- /dev/null +++ b/math/src/field/fields/fft_friendly/babybear_u32.rs @@ -0,0 +1,283 @@ +use crate::{ + field::{ + element::FieldElement, + fields::u32_montgomery_backend_prime_field::U32MontgomeryBackendPrimeField, + traits::IsFFTField, + }, + unsigned_integer::element::{UnsignedInteger, U64}, +}; + +pub type Babybear31PrimeField = U32MontgomeryBackendPrimeField<2013265921>; + +//a two-adic primitive root of unity is 21^(2^24) +// 21^(2^24)=1 mod 2013265921 +// 2^27(2^4-1)+1 where n=27 (two-adicity) and k=2^4+1 +//In the future we should allow this with metal and cuda feature, and just dispatch it to the CPU until the implementation is done +#[cfg(any(not(feature = "metal"), not(feature = "cuda")))] +impl IsFFTField for Babybear31PrimeField { + const TWO_ADICITY: u64 = 24; + + const TWO_ADIC_PRIMITVE_ROOT_OF_UNITY: Self::BaseType = 21; + + fn field_name() -> &'static str { + "babybear31" + } +} + +#[cfg(test)] +mod tests { + use super::*; + + mod test_babybear_31_bytes_ops { + use super::*; + use crate::{field::element::FieldElement, traits::ByteConversion}; + + #[test] + fn two_plus_one_is_three() { + let a = FieldElement::::from(2); + let b = FieldElement::::one(); + let res = FieldElement::::from(3); + + assert_eq!(a + b, res) + } + + #[test] + fn one_minus_two_is_minus_one() { + let a = FieldElement::::from(2); + let b = FieldElement::::one(); + let res = FieldElement::::from(2013265921); + let zero = FieldElement::::zero(); + + println!("Zero: {:?}", zero); + println!("Zero representative: {:?}", zero.representative()); + println!("Mod: {:?}", res); + println!("Mod representative: {:?}", res.representative()); + println!("-B: {:?}", (-b).representative()); + println!("A: {:?}", a); + println!("A representative: {:?}", a.representative()); + println!("MOD plus 1: {:?}", (res + b).representative()); + assert_eq!(b - a, res) + } + + #[test] + fn mul_by_zero_is_zero() { + let a = FieldElement::::from(2); + let b = FieldElement::::zero(); + assert_eq!(a * b, b) + } + + #[test] + fn neg_zero_is_zero() { + let zero = FieldElement::::from(0); + + assert_eq!(-&zero, zero); + } + + #[test] + #[cfg(feature = "alloc")] + fn byte_serialization_for_a_number_matches_with_byte_conversion_implementation_le() { + let element = + FieldElement::::from_hex("0x0123456701234567").unwrap(); + let bytes = element.to_bytes_le(); + let expected_bytes: [u8; 8] = ByteConversion::to_bytes_le(&element).try_into().unwrap(); + assert_eq!(bytes, expected_bytes); + } + + #[test] + #[cfg(feature = "alloc")] + fn byte_serialization_for_a_number_matches_with_byte_conversion_implementation_be() { + let element = + FieldElement::::from_hex("0123456701234567").unwrap(); + let bytes = element.to_bytes_be(); + let expected_bytes: [u8; 8] = ByteConversion::to_bytes_be(&element).try_into().unwrap(); + assert_eq!(bytes, expected_bytes); + } + + #[test] + fn byte_serialization_and_deserialization_works_le() { + let element = + FieldElement::::from_hex("0x7654321076543210").unwrap(); + let bytes = element.to_bytes_le(); + let from_bytes = FieldElement::::from_bytes_le(&bytes).unwrap(); + assert_eq!(element, from_bytes); + } + + #[test] + fn byte_serialization_and_deserialization_works_be() { + let element = + FieldElement::::from_hex("7654321076543210").unwrap(); + let bytes = element.to_bytes_be(); + let from_bytes = FieldElement::::from_bytes_be(&bytes).unwrap(); + assert_eq!(element, from_bytes); + } + } + + #[cfg(all(feature = "std", not(feature = "instruments")))] + mod test_babybear_31_fft { + use super::*; + #[cfg(not(any(feature = "metal", feature = "cuda")))] + use crate::fft::cpu::roots_of_unity::{ + get_powers_of_primitive_root, get_powers_of_primitive_root_coset, + }; + #[cfg(not(any(feature = "metal", feature = "cuda")))] + use crate::field::element::FieldElement; + #[cfg(not(any(feature = "metal", feature = "cuda")))] + use crate::field::traits::{IsFFTField, RootsConfig}; + use crate::polynomial::Polynomial; + use proptest::{collection, prelude::*, std_facade::Vec}; + + #[cfg(not(any(feature = "metal", feature = "cuda")))] + fn gen_fft_and_naive_evaluation( + poly: Polynomial>, + ) -> (Vec>, Vec>) { + let len = poly.coeff_len().next_power_of_two(); + let order = len.trailing_zeros(); + let twiddles = + get_powers_of_primitive_root(order.into(), len, RootsConfig::Natural).unwrap(); + + let fft_eval = Polynomial::evaluate_fft::(&poly, 1, None).unwrap(); + let naive_eval = poly.evaluate_slice(&twiddles); + + (fft_eval, naive_eval) + } + + #[cfg(not(any(feature = "metal", feature = "cuda")))] + fn gen_fft_coset_and_naive_evaluation( + poly: Polynomial>, + offset: FieldElement, + blowup_factor: usize, + ) -> (Vec>, Vec>) { + let len = poly.coeff_len().next_power_of_two(); + let order = (len * blowup_factor).trailing_zeros(); + let twiddles = + get_powers_of_primitive_root_coset(order.into(), len * blowup_factor, &offset) + .unwrap(); + + let fft_eval = + Polynomial::evaluate_offset_fft::(&poly, blowup_factor, None, &offset).unwrap(); + let naive_eval = poly.evaluate_slice(&twiddles); + + (fft_eval, naive_eval) + } + + #[cfg(not(any(feature = "metal", feature = "cuda")))] + fn gen_fft_and_naive_interpolate( + fft_evals: &[FieldElement], + ) -> (Polynomial>, Polynomial>) { + let order = fft_evals.len().trailing_zeros() as u64; + let twiddles = + get_powers_of_primitive_root(order, 1 << order, RootsConfig::Natural).unwrap(); + + let naive_poly = Polynomial::interpolate(&twiddles, fft_evals).unwrap(); + let fft_poly = Polynomial::interpolate_fft::(fft_evals).unwrap(); + + (fft_poly, naive_poly) + } + + #[cfg(not(any(feature = "metal", feature = "cuda")))] + fn gen_fft_and_naive_coset_interpolate( + fft_evals: &[FieldElement], + offset: &FieldElement, + ) -> (Polynomial>, Polynomial>) { + let order = fft_evals.len().trailing_zeros() as u64; + let twiddles = get_powers_of_primitive_root_coset(order, 1 << order, offset).unwrap(); + + let naive_poly = Polynomial::interpolate(&twiddles, fft_evals).unwrap(); + let fft_poly = Polynomial::interpolate_offset_fft(fft_evals, offset).unwrap(); + + (fft_poly, naive_poly) + } + + #[cfg(not(any(feature = "metal", feature = "cuda")))] + fn gen_fft_interpolate_and_evaluate( + poly: Polynomial>, + ) -> (Polynomial>, Polynomial>) { + let eval = Polynomial::evaluate_fft::(&poly, 1, None).unwrap(); + let new_poly = Polynomial::interpolate_fft::(&eval).unwrap(); + + (poly, new_poly) + } + + prop_compose! { + fn powers_of_two(max_exp: u8)(exp in 1..max_exp) -> usize { 1 << exp } + // max_exp cannot be multiple of the bits that represent a usize, generally 64 or 32. + // also it can't exceed the test field's two-adicity. + } + prop_compose! { + fn field_element()(num in any::().prop_filter("Avoid null coefficients", |x| x != &0)) -> FieldElement { + FieldElement::::from(num) + } + } + prop_compose! { + fn offset()(num in any::(), factor in any::()) -> FieldElement { FieldElement::::from(num).pow(factor) } + } + prop_compose! { + fn field_vec(max_exp: u8)(vec in collection::vec(field_element(), 0..1 << max_exp)) -> Vec> { + vec + } + } + prop_compose! { + fn non_power_of_two_sized_field_vec(max_exp: u8)(vec in collection::vec(field_element(), 2..1< Vec> { + vec + } + } + prop_compose! { + fn poly(max_exp: u8)(coeffs in field_vec(max_exp)) -> Polynomial> { + Polynomial::new(&coeffs) + } + } + prop_compose! { + fn poly_with_non_power_of_two_coeffs(max_exp: u8)(coeffs in non_power_of_two_sized_field_vec(max_exp)) -> Polynomial> { + Polynomial::new(&coeffs) + } + } + + proptest! { + // Property-based test that ensures FFT eval. gives same result as a naive polynomial evaluation. + #[test] + #[cfg(not(any(feature = "metal",feature = "cuda")))] + fn test_fft_matches_naive_evaluation(poly in poly(8)) { + let (fft_eval, naive_eval) = gen_fft_and_naive_evaluation(poly); + prop_assert_eq!(fft_eval, naive_eval); + } + + // Property-based test that ensures FFT eval. with coset gives same result as a naive polynomial evaluation. + #[test] + #[cfg(not(any(feature = "metal",feature = "cuda")))] + fn test_fft_coset_matches_naive_evaluation(poly in poly(4), offset in offset(), blowup_factor in powers_of_two(4)) { + let (fft_eval, naive_eval) = gen_fft_coset_and_naive_evaluation(poly, offset, blowup_factor); + prop_assert_eq!(fft_eval, naive_eval); + } + + // #[cfg(not(any(feature = "metal"),not(feature = "cuda")))] + // Property-based test that ensures FFT interpolation is the same as naive.. + #[test] + #[cfg(not(any(feature = "metal",feature = "cuda")))] + fn test_fft_interpolate_matches_naive(fft_evals in field_vec(4) + .prop_filter("Avoid polynomials of size not power of two", + |evals| evals.len().is_power_of_two())) { + let (fft_poly, naive_poly) = gen_fft_and_naive_interpolate(&fft_evals); + prop_assert_eq!(fft_poly, naive_poly); + } + + // Property-based test that ensures FFT interpolation with an offset is the same as naive. + #[test] + #[cfg(not(any(feature = "metal",feature = "cuda")))] + fn test_fft_interpolate_coset_matches_naive(offset in offset(), fft_evals in field_vec(4) + .prop_filter("Avoid polynomials of size not power of two", + |evals| evals.len().is_power_of_two())) { + let (fft_poly, naive_poly) = gen_fft_and_naive_coset_interpolate(&fft_evals, &offset); + prop_assert_eq!(fft_poly, naive_poly); + } + + // Property-based test that ensures interpolation is the inverse operation of evaluation. + #[test] + #[cfg(not(any(feature = "metal",feature = "cuda")))] + fn test_fft_interpolate_is_inverse_of_evaluate( + poly in poly(4).prop_filter("Avoid non pows of two", |poly| poly.coeff_len().is_power_of_two())) { + let (poly, new_poly) = gen_fft_interpolate_and_evaluate(poly); + prop_assert_eq!(poly, new_poly); + } + } + } +} diff --git a/math/src/field/fields/fft_friendly/mod.rs b/math/src/field/fields/fft_friendly/mod.rs index 535b94ecc..10e7541b7 100644 --- a/math/src/field/fields/fft_friendly/mod.rs +++ b/math/src/field/fields/fft_friendly/mod.rs @@ -10,3 +10,6 @@ pub mod stark_252_prime_field; pub mod u64_goldilocks; /// Implemenation of the Mersenne Prime field p = 2^31 - 1 pub mod u64_mersenne_montgomery_field; + +/// Inmplementation of the Babybear Prime Field p = 2^31 - 2^27 + 1 using u32 +pub mod babybear_u32; diff --git a/math/src/field/fields/mod.rs b/math/src/field/fields/mod.rs index 7e9307f20..46e15d0d9 100644 --- a/math/src/field/fields/mod.rs +++ b/math/src/field/fields/mod.rs @@ -13,6 +13,7 @@ pub mod secp256k1_field; pub mod secp256k1_scalarfield; /// Implementation of secp256r1 base field. pub mod secp256r1_field; +pub mod u32_montgomery_backend_prime_field; /// Implementation of the u64 Goldilocks Prime field (p = 2^64 - 2^32 + 1) pub mod u64_goldilocks_field; /// Implementation of prime fields over 64 bit unsigned integers. diff --git a/math/src/field/fields/u32_montgomery_backend_prime_field.rs b/math/src/field/fields/u32_montgomery_backend_prime_field.rs new file mode 100644 index 000000000..6817bce12 --- /dev/null +++ b/math/src/field/fields/u32_montgomery_backend_prime_field.rs @@ -0,0 +1,1343 @@ +use crate::errors::CreationError; +use crate::field::element::FieldElement; +use crate::field::errors::FieldError; +use crate::field::traits::IsPrimeField; +#[cfg(feature = "alloc")] +use crate::traits::AsBytes; +use crate::traits::ByteConversion; +use crate::{field::traits::IsField, unsigned_integer::element::UnsignedInteger}; + +use core::fmt::Debug; + +#[cfg_attr( + any( + feature = "lambdaworks-serde-binary", + feature = "lambdaworks-serde-string" + ), + derive(serde::Serialize, serde::Deserialize) +)] +#[derive(Clone, Debug, Hash, Copy)] +pub struct U32MontgomeryBackendPrimeField; + +impl U32MontgomeryBackendPrimeField { + pub const R2: u32 = 1172168163; + pub const MU: u32 = 2281701377; + pub const ZERO: u32 = 0; + pub const ONE: u32 = MontgomeryAlgorithms::cios(&1, &Self::R2, &MODULUS, &Self::MU); + const MODULUS_HAS_ONE_SPARE_BIT: bool = true; + + /// Computes `- modulus^{-1} mod 2^{64}` + /// This algorithm is given by Dussé and Kaliski Jr. in + /// "S. R. Dussé and B. S. Kaliski Jr. A cryptographic library for the Motorola + /// DSP56000. In I. Damgård, editor, Advances in Cryptology – EUROCRYPT’90, + /// volume 473 of Lecture Notes in Computer Science, pages 230–244. Springer, + /// Heidelberg, May 1991." + // const fn compute_mu_parameter() -> u32 { + // let mut y: u32 = 1; + // let word_size = 32; + // let mut i: usize = 2; + // while i <= word_size { + // let (_, lo) = &MODULUS.overflowing_mul(y); + // let least_significant_limb = lo.limbs[0]; + // if (least_significant_limb << (word_size - i)) >> (word_size - i) != 1 { + // y += 1 << (i - 1); + // } + // i += 1; + // } + // y.wrapping_neg() + // } + + /// Computes 2^{384 * 2} modulo `modulus` + // const fn compute_r2_parameter() -> u32 { + // let word_size = 64; + // let mut l: usize = 0; + // let zero: u32 = 0; + // // Define `c` as the largest power of 2 smaller than `modulus` + // while l < word_size { + // if &MODULUS << l != 0 { + // break; + // } + // l += 1; + // } + // let mut c: u32 = 1 << l; + + // // Double `c` and reduce modulo `modulus` until getting + // // `2^{2 * number_limbs * word_size}` mod `modulus` + // let mut i: usize = 1; + // while i <= 2 * word_size - l { + // let (double_c, overflow) = &c.overflowing_add(c); + // c = if (&MODULUS <= &double_c) || *overflow { + // double_c.overflowing_sub(MODULUS).0 + // } else { + // *double_c + // }; + // i += 1; + // } + // c + // } + + /// Checks whether the most significant limb of the modulus is ats + /// most `0x7FFFFFFFFFFFFFFE`. This check is useful since special + /// optimizations exist for this kind of moduli. + #[inline(always)] + const fn modulus_has_one_spare_bit() -> bool { + MODULUS < (1u32 << 31) - 1 + } +} + +impl IsField for U32MontgomeryBackendPrimeField { + type BaseType = u32; + + #[inline(always)] + fn add(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { + let (sum, overflow) = a.overflowing_add(*b); + if Self::MODULUS_HAS_ONE_SPARE_BIT { + if sum >= MODULUS { + sum - MODULUS + } else { + sum + } + } else if overflow || sum >= MODULUS { + sum - MODULUS + } else { + sum + } + } + + #[inline(always)] + fn mul(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { + if Self::MODULUS_HAS_ONE_SPARE_BIT { + MontgomeryAlgorithms::cios_optimized_for_moduli_with_one_spare_bit( + *a, + *b, + MODULUS, + Self::MU, + ) + } else { + MontgomeryAlgorithms::cios(a, b, &MODULUS, &Self::MU) + } + } + + #[inline(always)] + fn square(a: &Self::BaseType) -> Self::BaseType { + MontgomeryAlgorithms::sos_square(*a, MODULUS, &Self::MU) + } + + #[inline(always)] + fn sub(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { + if b <= a { + a - b + } else { + MODULUS - (b - a) + } + } + + #[inline(always)] + fn neg(a: &Self::BaseType) -> Self::BaseType { + if a == &Self::ZERO { + *a + } else { + MODULUS - a + } + } + + #[inline(always)] + fn inv(a: &Self::BaseType) -> Result { + if a == &Self::ZERO { + Err(FieldError::InvZeroError) + } else { + // Guajardo Kumar Paar Pelzl + // Efficient Software-Implementation of Finite Fields with Applications to + // Cryptography + // Algorithm 16 (BEA for Inversion in Fp) + + //These can be done with const functions + let modulus_has_spare_bits = MODULUS >> 31 == 0; + + let mut u: u32 = *a; + let mut v = MODULUS; + let mut b = Self::R2; // Avoids unnecessary reduction step. + let mut c = Self::zero(); + + while u != 1 && v != 1 { + while u & 1 == 0 { + u >>= 1; + if b & 1 == 0 { + b >>= 1; + } else { + let carry; + (b, carry) = b.overflowing_add(MODULUS); + b >>= 1; + if !modulus_has_spare_bits && carry { + b |= 1 << 31; + } + } + } + + while v & 1 == 0 { + v >>= 1; + + if c & 1 == 0 { + c >>= 1; + } else { + let carry; + (c, carry) = c.overflowing_add(MODULUS); + c >>= 1; + if !modulus_has_spare_bits && carry { + c |= 1 << 31; + } + } + } + + if v <= u { + u = u - v; + if b < c { + b = MODULUS - c + b; + } else { + b = b - c; + } + } else { + v = v - u; + if c < b { + c = MODULUS - b + c; + } else { + c = c - b; + } + } + } + + if u == 1 { + Ok(b) + } else { + Ok(c) + } + } + } + + #[inline(always)] + fn div(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { + Self::mul(a, &Self::inv(b).unwrap()) + } + + #[inline(always)] + fn eq(a: &Self::BaseType, b: &Self::BaseType) -> bool { + a == b + } + + #[inline(always)] + fn zero() -> Self::BaseType { + Self::ZERO + } + + #[inline(always)] + fn one() -> Self::BaseType { + Self::ONE + } + + #[inline(always)] + fn from_u64(x: u64) -> Self::BaseType { + let x_u32 = x as u32; + MontgomeryAlgorithms::cios(&x_u32, &Self::R2, &MODULUS, &Self::MU) + } + + #[inline(always)] + fn from_base_type(x: Self::BaseType) -> Self::BaseType { + MontgomeryAlgorithms::cios(&x, &Self::R2, &MODULUS, &Self::MU) + } +} + +impl IsPrimeField for U32MontgomeryBackendPrimeField { + type RepresentativeType = Self::BaseType; + + fn representative(x: &Self::BaseType) -> Self::RepresentativeType { + MontgomeryAlgorithms::cios(x, &1u32, &MODULUS, &Self::MU) + } + + fn field_bit_size() -> usize { + let mut evaluated_bit = 32 - 1; + let max_element = &MODULUS - 1; + + while ((max_element >> evaluated_bit) & 1) != 1 { + evaluated_bit -= 1; + } + + evaluated_bit + 1 + } + + fn from_hex(hex_string: &str) -> Result { + let mut hex_string = hex_string; + // Remove 0x if it's on the string + let mut char_iterator = hex_string.chars(); + if hex_string.len() > 2 + && char_iterator.next().unwrap() == '0' + && char_iterator.next().unwrap() == 'x' + { + hex_string = &hex_string[2..]; + } + let integer = + u32::from_str_radix(hex_string, 16).map_err(|_| CreationError::InvalidHexString)?; + + Ok(MontgomeryAlgorithms::cios( + &integer, + &Self::R2, + &MODULUS, + &Self::MU, + )) + } + + #[cfg(feature = "std")] + fn to_hex(x: &Self::BaseType) -> String { + format!("{:x}", x) + } +} + +impl FieldElement> {} + +impl ByteConversion for FieldElement> { + #[cfg(feature = "alloc")] + fn to_bytes_be(&self) -> alloc::vec::Vec { + MontgomeryAlgorithms::cios( + self.value(), + &1, + &MODULUS, + &U32MontgomeryBackendPrimeField::::MU, + ) + .to_be_bytes() + .to_vec() + } + + #[cfg(feature = "alloc")] + fn to_bytes_le(&self) -> alloc::vec::Vec { + MontgomeryAlgorithms::cios( + self.value(), + &1u32, + &MODULUS, + &U32MontgomeryBackendPrimeField::::MU, + ) + .to_le_bytes() + .to_vec() + } + + fn from_bytes_be(bytes: &[u8]) -> Result { + let value = u32::from_be_bytes(bytes.try_into().unwrap()); + Ok(Self::new(value)) + } + + fn from_bytes_le(bytes: &[u8]) -> Result { + let value = u32::from_le_bytes(bytes.try_into().unwrap()); + Ok(Self::new(value)) + } +} + +#[cfg(feature = "alloc")] +impl AsBytes for FieldElement> { + fn as_bytes(&self) -> alloc::vec::Vec { + self.value().to_be_bytes().to_vec() + } +} + +#[cfg(feature = "alloc")] +impl From>> + for alloc::vec::Vec +{ + fn from(value: FieldElement>) -> alloc::vec::Vec { + value.value().to_be_bytes().to_vec() + } +} + +pub struct MontgomeryAlgorithms; +impl MontgomeryAlgorithms { + /// Compute CIOS multiplication of `a` * `b` + /// `q` is the modulus + /// `mu` is the inverse of -q modulo 2^{32} + /// Notice CIOS stands for Coarsely Integrated Operand Scanning + /// For more information see section 2.3.2 of Tolga Acar's thesis + /// https://www.microsoft.com/en-us/research/wp-content/uploads/1998/06/97Acar.pdf + #[inline(always)] + pub const fn cios(a: &u32, b: &u32, q: &u32, mu: &u32) -> u32 { + let t = *a as u64 * *b as u64; + let m = ((t as u32).wrapping_mul(*mu)) as u64; + + let t = t + m * (*q as u64); + + let c = t >> 32; + let mut result = c as u32; + if result >= *q { + result = result.wrapping_sub(*q); + } + result + } + + /// Compute CIOS multiplication of `a` * `b` + /// This is the Algorithm 2 described in the paper + /// "EdMSM: Multi-Scalar-Multiplication for SNARKs and Faster Montgomery multiplication" + /// https://eprint.iacr.org/2022/1400.pdf. + /// It is only suited for moduli with `q[0]` smaller than `2^63 - 1`. + /// `q` is the modulus + /// `mu` is the inverse of -q modulo 2^{64} -> change this (Juan) + #[inline(always)] + pub fn cios_optimized_for_moduli_with_one_spare_bit(a: u32, b: u32, q: u32, mu: u32) -> u32 { + let t: u64 = (a as u64) * (b as u64); + + let m = ((t as u32).wrapping_mul(mu)) as u64; + + let t = t + m * (q as u64); + + let c = t >> 32; + let mut result = c as u32; + + if result >= q { + result = result.wrapping_sub(q); + } + result + } + + // Separated Operand Scanning Method (2.3.1) + #[inline(always)] + pub fn sos_square(a: u32, q: u32, mu: &u32) -> u32 { + // NOTE: we use explicit `while` loops in this function because profiling pointed + // at iterators of the form `(..).rev()` as the main performance bottleneck. + let t: u64 = (a as u64) * (a as u64); + let m = ((t as u32).wrapping_mul(*mu)) as u64; + let t = t + m * (q as u64); + + let c = t >> 32; + let mut result = c as u32; + + if result >= q { + result = result.wrapping_sub(q); + } + + result + } +} + +// #[cfg(test)] +// mod tests_u384_prime_fields { +// use crate::field::element::FieldElement; +// use crate::field::errors::FieldError; +// use crate::field::fields::fft_friendly::babybear::Babybear31PrimeField; +// use crate::field::fields::fft_friendly::stark_252_prime_field::Stark252PrimeField; + +// use crate::field::fields::montgomery_backed_prime_fields::{ +// IsModulus, U256PrimeField, U384PrimeField, +// }; +// use crate::field::traits::IsField; +// use crate::field::traits::IsPrimeField; +// #[cfg(feature = "alloc")] +// use crate::traits::ByteConversion; +// use crate::unsigned_integer::element::U384; +// use crate::unsigned_integer::element::{UnsignedInteger, U256}; + +// type F = U384PrimeField; +// type U384F23Element = FieldElement; + +// #[test] +// fn u384_mod_23_uses_5_bits() { +// assert_eq!(U384F23::field_bit_size(), 5); +// } + +// #[test] +// fn stark_252_prime_field_uses_252_bits() { +// assert_eq!(Stark252PrimeField::field_bit_size(), 252); +// } + +// #[test] +// fn u256_mod_2_uses_1_bit() { +// #[derive(Clone, Debug)] +// struct U256Modulus1; +// impl IsModulus for U256Modulus1 { +// const MODULUS: U256 = UnsignedInteger::from_u64(2); +// } +// type U256OneField = U256PrimeField; +// assert_eq!(U256OneField::field_bit_size(), 1); +// } + +// #[test] +// fn u256_with_first_bit_set_uses_256_bit() { +// #[derive(Clone, Debug)] +// struct U256ModulusBig; +// impl IsModulus for U256ModulusBig { +// const MODULUS: U256 = UnsignedInteger::from_hex_unchecked( +// "F0000000F0000000F0000000F0000000F0000000F0000000F0000000F0000000", +// ); +// } +// type U256OneField = U256PrimeField; +// assert_eq!(U256OneField::field_bit_size(), 256); +// } + +// #[test] +// fn montgomery_backend_primefield_compute_r2_parameter() { +// let r2: U384 = UnsignedInteger { +// limbs: [0, 0, 0, 0, 0, 6], +// }; +// assert_eq!(U384F23::R2, r2); +// } + +// #[test] +// fn montgomery_backend_primefield_compute_mu_parameter() { +// assert_eq!(U384F23::MU, 3208129404123400281); +// } + +// #[test] +// fn montgomery_backend_primefield_compute_zero_parameter() { +// let zero: U384 = UnsignedInteger { +// limbs: [0, 0, 0, 0, 0, 0], +// }; +// assert_eq!(U384F23::ZERO, zero); +// } + +// #[test] +// fn montgomery_backend_primefield_from_u64() { +// let a: U384 = UnsignedInteger { +// limbs: [0, 0, 0, 0, 0, 17], +// }; +// assert_eq!(U384F23::from_u64(770_u64), a); +// } + +// #[test] +// fn montgomery_backend_primefield_representative() { +// let a: U384 = UnsignedInteger { +// limbs: [0, 0, 0, 0, 0, 11], +// }; +// assert_eq!(U384F23::representative(&U384F23::from_u64(770_u64)), a); +// } + +// #[test] +// fn montgomery_backend_multiplication_works_0() { +// let x = U384F23Element::from(11_u64); +// let y = U384F23Element::from(10_u64); +// let c = U384F23Element::from(110_u64); +// assert_eq!(x * y, c); +// } + +// #[test] +// #[cfg(feature = "lambdaworks-serde-string")] +// fn montgomery_backend_serialization_deserialization() { +// let x = U384F23Element::from(11_u64); +// let x_serialized = serde_json::to_string(&x).unwrap(); +// let x_deserialized: U384F23Element = serde_json::from_str(&x_serialized).unwrap(); +// // assert_eq!(x_serialized, "{\"value\":\"0xb\"}"); // serialization is no longer as hex string +// assert_eq!(x_deserialized, x); +// } + +// #[test] +// fn doubling() { +// assert_eq!( +// U384F23Element::from(2).double(), +// U384F23Element::from(2) + U384F23Element::from(2), +// ); +// } + +// const ORDER: usize = 23; +// #[test] +// fn two_plus_one_is_three() { +// assert_eq!( +// U384F23Element::from(2) + U384F23Element::from(1), +// U384F23Element::from(3) +// ); +// } + +// #[test] +// fn max_order_plus_1_is_0() { +// assert_eq!( +// U384F23Element::from((ORDER - 1) as u64) + U384F23Element::from(1), +// U384F23Element::from(0) +// ); +// } + +// #[test] +// fn when_comparing_13_and_13_they_are_equal() { +// let a: U384F23Element = U384F23Element::from(13); +// let b: U384F23Element = U384F23Element::from(13); +// assert_eq!(a, b); +// } + +// #[test] +// fn when_comparing_13_and_8_they_are_different() { +// let a: U384F23Element = U384F23Element::from(13); +// let b: U384F23Element = U384F23Element::from(8); +// assert_ne!(a, b); +// } + +// #[test] +// fn mul_neutral_element() { +// let a: U384F23Element = U384F23Element::from(1); +// let b: U384F23Element = U384F23Element::from(2); +// assert_eq!(a * b, U384F23Element::from(2)); +// } + +// #[test] +// fn mul_2_3_is_6() { +// let a: U384F23Element = U384F23Element::from(2); +// let b: U384F23Element = U384F23Element::from(3); +// assert_eq!(a * b, U384F23Element::from(6)); +// } + +// #[test] +// fn mul_order_minus_1() { +// let a: U384F23Element = U384F23Element::from((ORDER - 1) as u64); +// let b: U384F23Element = U384F23Element::from((ORDER - 1) as u64); +// assert_eq!(a * b, U384F23Element::from(1)); +// } + +// #[test] +// fn inv_0_error() { +// let result = U384F23Element::from(0).inv(); +// assert!(matches!(result, Err(FieldError::InvZeroError))) +// } + +// #[test] +// fn inv_2() { +// let a: U384F23Element = U384F23Element::from(2); +// assert_eq!(&a * a.inv().unwrap(), U384F23Element::from(1)); +// } + +// #[test] +// fn pow_2_3() { +// assert_eq!(U384F23Element::from(2).pow(3_u64), U384F23Element::from(8)) +// } + +// #[test] +// fn pow_p_minus_1() { +// assert_eq!( +// U384F23Element::from(2).pow(ORDER - 1), +// U384F23Element::from(1) +// ) +// } + +// #[test] +// fn div_1() { +// assert_eq!( +// U384F23Element::from(2) / U384F23Element::from(1), +// U384F23Element::from(2) +// ) +// } + +// #[test] +// fn div_4_2() { +// assert_eq!( +// U384F23Element::from(4) / U384F23Element::from(2), +// U384F23Element::from(2) +// ) +// } + +// #[test] +// fn three_inverse() { +// let a = U384F23Element::from(3); +// let expected = U384F23Element::from(8); +// assert_eq!(a.inv().unwrap(), expected) +// } + +// #[test] +// fn div_4_3() { +// assert_eq!( +// U384F23Element::from(4) / U384F23Element::from(3) * U384F23Element::from(3), +// U384F23Element::from(4) +// ) +// } + +// #[test] +// fn two_plus_its_additive_inv_is_0() { +// let two = U384F23Element::from(2); + +// assert_eq!(&two + (-&two), U384F23Element::from(0)) +// } + +// #[test] +// fn four_minus_three_is_1() { +// let four = U384F23Element::from(4); +// let three = U384F23Element::from(3); + +// assert_eq!(four - three, U384F23Element::from(1)) +// } + +// #[test] +// fn zero_minus_1_is_order_minus_1() { +// let zero = U384F23Element::from(0); +// let one = U384F23Element::from(1); + +// assert_eq!(zero - one, U384F23Element::from((ORDER - 1) as u64)) +// } + +// #[test] +// fn neg_zero_is_zero() { +// let zero = U384F23Element::from(0); + +// assert_eq!(-&zero, zero); +// } + +// // FP1 +// #[derive(Clone, Debug)] +// struct U384ModulusP1; +// impl IsModulus for U384ModulusP1 { +// const MODULUS: U384 = UnsignedInteger { +// limbs: [ +// 0, +// 0, +// 0, +// 3450888597, +// 5754816256417943771, +// 15923941673896418529, +// ], +// }; +// } + +// type U384FP1 = U384PrimeField; +// type U384FP1Element = FieldElement; + +// #[test] +// fn montgomery_prime_field_from_bad_hex_errs() { +// assert!(U384FP1Element::from_hex("0xTEST").is_err()); +// } + +// #[test] +// fn montgomery_prime_field_addition_works_0() { +// let x = U384FP1Element::new(UnsignedInteger::from_hex_unchecked( +// "05ed176deb0e80b4deb7718cdaa075165f149c", +// )); +// let y = U384FP1Element::new(UnsignedInteger::from_hex_unchecked( +// "5f103b0bd4397d4df560eb559f38353f80eeb6", +// )); +// let c = U384FP1Element::new(UnsignedInteger::from_hex_unchecked( +// "64fd5279bf47fe02d4185ce279d8aa55e00352", +// )); +// assert_eq!(x + y, c); +// } + +// #[test] +// fn montgomery_prime_field_multiplication_works_0() { +// let x = U384FP1Element::new(UnsignedInteger::from_hex_unchecked( +// "05ed176deb0e80b4deb7718cdaa075165f149c", +// )); +// let y = U384FP1Element::new(UnsignedInteger::from_hex_unchecked( +// "5f103b0bd4397d4df560eb559f38353f80eeb6", +// )); +// let c = U384FP1Element::new(UnsignedInteger::from_hex_unchecked( +// "73d23e8d462060dc23d5c15c00fc432d95621a3c", +// )); +// assert_eq!(x * y, c); +// } + +// // FP2 +// #[derive(Clone, Debug)] +// struct U384ModulusP2; +// impl IsModulus for U384ModulusP2 { +// const MODULUS: U384 = UnsignedInteger { +// limbs: [ +// 18446744073709551615, +// 18446744073709551615, +// 18446744073709551615, +// 18446744073709551615, +// 18446744073709551615, +// 18446744073709551275, +// ], +// }; +// } + +// type U384FP2 = U384PrimeField; +// type U384FP2Element = FieldElement; + +// #[test] +// fn montgomery_prime_field_addition_works_1() { +// let x = U384FP2Element::new(UnsignedInteger::from_hex_unchecked( +// "05ed176deb0e80b4deb7718cdaa075165f149c", +// )); +// let y = U384FP2Element::new(UnsignedInteger::from_hex_unchecked( +// "5f103b0bd4397d4df560eb559f38353f80eeb6", +// )); +// let c = U384FP2Element::new(UnsignedInteger::from_hex_unchecked( +// "64fd5279bf47fe02d4185ce279d8aa55e00352", +// )); +// assert_eq!(x + y, c); +// } + +// #[test] +// fn montgomery_prime_field_multiplication_works_1() { +// let x = U384FP2Element::one(); +// let y = U384FP2Element::new(UnsignedInteger::from_hex_unchecked( +// "5f103b0bd4397d4df560eb559f38353f80eeb6", +// )); +// assert_eq!(&y * x, y); +// } + +// #[test] +// #[cfg(feature = "alloc")] +// fn to_bytes_from_bytes_be_is_the_identity() { +// let x = U384FP2Element::new(UnsignedInteger::from_hex_unchecked( +// "5f103b0bd4397d4df560eb559f38353f80eeb6", +// )); +// assert_eq!(U384FP2Element::from_bytes_be(&x.to_bytes_be()).unwrap(), x); +// } + +// #[test] +// #[cfg(feature = "alloc")] +// fn from_bytes_to_bytes_be_is_the_identity_for_one() { +// let bytes = [ +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, +// ]; +// assert_eq!( +// U384FP2Element::from_bytes_be(&bytes).unwrap().to_bytes_be(), +// bytes +// ); +// } + +// #[test] +// #[cfg(feature = "alloc")] +// fn to_bytes_from_bytes_le_is_the_identity() { +// let x = U384FP2Element::new(UnsignedInteger::from_hex_unchecked( +// "5f103b0bd4397d4df560eb559f38353f80eeb6", +// )); +// assert_eq!(U384FP2Element::from_bytes_le(&x.to_bytes_le()).unwrap(), x); +// } + +// #[test] +// #[cfg(feature = "alloc")] +// fn from_bytes_to_bytes_le_is_the_identity_for_one() { +// let bytes = [ +// 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// ]; +// assert_eq!( +// U384FP2Element::from_bytes_le(&bytes).unwrap().to_bytes_le(), +// bytes +// ); +// } +// } + +// #[cfg(test)] +// mod tests_u256_prime_fields { +// use crate::field::element::FieldElement; +// use crate::field::errors::FieldError; +// use crate::field::fields::montgomery_backed_prime_fields::{IsModulus, U256PrimeField}; +// use crate::field::traits::IsField; +// use crate::field::traits::IsPrimeField; +// #[cfg(feature = "alloc")] +// use crate::traits::ByteConversion; +// use crate::unsigned_integer::element::U256; +// use crate::unsigned_integer::element::{UnsignedInteger, U64}; +// use proptest::prelude::*; + +// use super::U64PrimeField; + +// #[derive(Clone, Debug)] +// struct U256Modulus29; +// impl IsModulus for U256Modulus29 { +// const MODULUS: U256 = UnsignedInteger::from_u64(29); +// } + +// type U256F29 = U256PrimeField; +// type U256F29Element = FieldElement; + +// #[test] +// fn montgomery_backend_primefield_compute_r2_parameter() { +// let r2: U256 = UnsignedInteger { +// limbs: [0, 0, 0, 24], +// }; +// assert_eq!(U256F29::R2, r2); +// } + +// #[test] +// fn montgomery_backend_primefield_compute_mu_parameter() { +// // modular multiplicative inverse +// assert_eq!(U256F29::MU, 14630176334321368523); +// } + +// #[test] +// fn montgomery_backend_primefield_compute_zero_parameter() { +// let zero: U256 = UnsignedInteger { +// limbs: [0, 0, 0, 0], +// }; +// assert_eq!(U256F29::ZERO, zero); +// } + +// #[test] +// fn montgomery_backend_primefield_from_u64() { +// // (770*2**(256))%29 +// let a: U256 = UnsignedInteger { +// limbs: [0, 0, 0, 24], +// }; +// assert_eq!(U256F29::from_u64(770_u64), a); +// } + +// #[test] +// fn montgomery_backend_primefield_representative() { +// // 770%29 +// let a: U256 = UnsignedInteger { +// limbs: [0, 0, 0, 16], +// }; +// assert_eq!(U256F29::representative(&U256F29::from_u64(770_u64)), a); +// } + +// #[test] +// fn montgomery_backend_multiplication_works_0() { +// let x = U256F29Element::from(11_u64); +// let y = U256F29Element::from(10_u64); +// let c = U256F29Element::from(110_u64); +// assert_eq!(x * y, c); +// } + +// #[test] +// fn doubling() { +// assert_eq!( +// U256F29Element::from(2).double(), +// U256F29Element::from(2) + U256F29Element::from(2), +// ); +// } + +// const ORDER: usize = 29; +// #[test] +// fn two_plus_one_is_three() { +// assert_eq!( +// U256F29Element::from(2) + U256F29Element::from(1), +// U256F29Element::from(3) +// ); +// } + +// #[test] +// fn max_order_plus_1_is_0() { +// assert_eq!( +// U256F29Element::from((ORDER - 1) as u64) + U256F29Element::from(1), +// U256F29Element::from(0) +// ); +// } + +// #[test] +// fn when_comparing_13_and_13_they_are_equal() { +// let a: U256F29Element = U256F29Element::from(13); +// let b: U256F29Element = U256F29Element::from(13); +// assert_eq!(a, b); +// } + +// #[test] +// fn when_comparing_13_and_8_they_are_different() { +// let a: U256F29Element = U256F29Element::from(13); +// let b: U256F29Element = U256F29Element::from(8); +// assert_ne!(a, b); +// } + +// #[test] +// fn mul_neutral_element() { +// let a: U256F29Element = U256F29Element::from(1); +// let b: U256F29Element = U256F29Element::from(2); +// assert_eq!(a * b, U256F29Element::from(2)); +// } + +// #[test] +// fn mul_2_3_is_6() { +// let a: U256F29Element = U256F29Element::from(2); +// let b: U256F29Element = U256F29Element::from(3); +// assert_eq!(a * b, U256F29Element::from(6)); +// } + +// #[test] +// fn mul_order_minus_1() { +// let a: U256F29Element = U256F29Element::from((ORDER - 1) as u64); +// let b: U256F29Element = U256F29Element::from((ORDER - 1) as u64); +// assert_eq!(a * b, U256F29Element::from(1)); +// } + +// #[test] +// fn inv_0_error() { +// let result = U256F29Element::from(0).inv(); +// assert!(matches!(result, Err(FieldError::InvZeroError))); +// } + +// #[test] +// fn inv_2() { +// let a: U256F29Element = U256F29Element::from(2); +// assert_eq!(&a * a.inv().unwrap(), U256F29Element::from(1)); +// } + +// #[test] +// fn pow_2_3() { +// assert_eq!(U256F29Element::from(2).pow(3_u64), U256F29Element::from(8)) +// } + +// #[test] +// fn pow_p_minus_1() { +// assert_eq!( +// U256F29Element::from(2).pow(ORDER - 1), +// U256F29Element::from(1) +// ) +// } + +// #[test] +// fn div_1() { +// assert_eq!( +// U256F29Element::from(2) / U256F29Element::from(1), +// U256F29Element::from(2) +// ) +// } + +// #[test] +// fn div_4_2() { +// let a = U256F29Element::from(4); +// let b = U256F29Element::from(2); +// assert_eq!(a / &b, b) +// } + +// #[test] +// fn div_4_3() { +// assert_eq!( +// U256F29Element::from(4) / U256F29Element::from(3) * U256F29Element::from(3), +// U256F29Element::from(4) +// ) +// } + +// #[test] +// fn two_plus_its_additive_inv_is_0() { +// let two = U256F29Element::from(2); + +// assert_eq!(&two + (-&two), U256F29Element::from(0)) +// } + +// #[test] +// fn four_minus_three_is_1() { +// let four = U256F29Element::from(4); +// let three = U256F29Element::from(3); + +// assert_eq!(four - three, U256F29Element::from(1)) +// } + +// #[test] +// fn zero_minus_1_is_order_minus_1() { +// let zero = U256F29Element::from(0); +// let one = U256F29Element::from(1); + +// assert_eq!(zero - one, U256F29Element::from((ORDER - 1) as u64)) +// } + +// #[test] +// fn neg_zero_is_zero() { +// let zero = U256F29Element::from(0); + +// assert_eq!(-&zero, zero); +// } + +// // FP1 +// #[derive(Clone, Debug)] +// struct U256ModulusP1; +// impl IsModulus for U256ModulusP1 { +// const MODULUS: U256 = UnsignedInteger { +// limbs: [ +// 8366, +// 8155137382671976874, +// 227688614771682406, +// 15723111795979912613, +// ], +// }; +// } + +// type U256FP1 = U256PrimeField; +// type U256FP1Element = FieldElement; + +// #[test] +// fn montgomery_prime_field_addition_works_0() { +// let x = U256FP1Element::new(UnsignedInteger::from_hex_unchecked( +// "93e712950bf3fe589aa030562a44b1cec66b09192c4bcf705a5", +// )); +// let y = U256FP1Element::new(UnsignedInteger::from_hex_unchecked( +// "10a712235c1f6b4172a1e35da6aef1a7ec6b09192c4bb88cfa5", +// )); +// let c = U256FP1Element::new(UnsignedInteger::from_hex_unchecked( +// "a48e24b86813699a0d4213b3d0f3a376b2d61232589787fd54a", +// )); +// assert_eq!(x + y, c); +// } + +// #[test] +// fn montgomery_prime_field_multiplication_works_0() { +// let x = U256FP1Element::new(UnsignedInteger::from_hex_unchecked( +// "93e712950bf3fe589aa030562a44b1cec66b09192c4bcf705a5", +// )); +// let y = U256FP1Element::new(UnsignedInteger::from_hex_unchecked( +// "10a712235c1f6b4172a1e35da6aef1a7ec6b09192c4bb88cfa5", +// )); +// let c = U256FP1Element::new(UnsignedInteger::from_hex_unchecked( +// "7808e74c3208d9a66791ef9cc15a46acc9951ee312102684021", +// )); +// assert_eq!(x * y, c); +// } + +// // FP2 +// #[derive(Clone, Debug)] +// struct ModulusP2; +// impl IsModulus for ModulusP2 { +// const MODULUS: U256 = UnsignedInteger { +// limbs: [ +// 18446744073709551615, +// 18446744073709551615, +// 18446744073709551615, +// 18446744073709551427, +// ], +// }; +// } + +// type FP2 = U256PrimeField; +// type FP2Element = FieldElement; + +// #[test] +// fn montgomery_prime_field_addition_works_1() { +// let x = FP2Element::new(UnsignedInteger::from_hex_unchecked( +// "acbbb7ca01c65cfffffc72815b397fff9ab130ad53a5ffffffb8f21b207dfedf", +// )); +// let y = FP2Element::new(UnsignedInteger::from_hex_unchecked( +// "d65ddbe509d3fffff21f494c588cbdbfe43e929b0543e3ffffffffffffffff43", +// )); +// let c = FP2Element::new(UnsignedInteger::from_hex_unchecked( +// "831993af0b9a5cfff21bbbcdb3c63dbf7eefc34858e9e3ffffb8f21b207dfedf", +// )); +// assert_eq!(x + y, c); +// } + +// #[test] +// fn montgomery_prime_field_multiplication_works_1() { +// let x = FP2Element::new(UnsignedInteger::from_hex_unchecked( +// "acbbb7ca01c65cfffffc72815b397fff9ab130ad53a5ffffffb8f21b207dfedf", +// )); +// let y = FP2Element::new(UnsignedInteger::from_hex_unchecked( +// "d65ddbe509d3fffff21f494c588cbdbfe43e929b0543e3ffffffffffffffff43", +// )); +// let c = FP2Element::new(UnsignedInteger::from_hex_unchecked( +// "2b1e80d553ecab2e4d41eb53c4c8ad89ebacac6cf6b91dcf2213f311093aa05d", +// )); +// assert_eq!(&y * x, c); +// } + +// #[test] +// #[cfg(feature = "alloc")] +// fn to_bytes_from_bytes_be_is_the_identity() { +// let x = FP2Element::new(UnsignedInteger::from_hex_unchecked( +// "5f103b0bd4397d4df560eb559f38353f80eeb6", +// )); +// assert_eq!(FP2Element::from_bytes_be(&x.to_bytes_be()).unwrap(), x); +// } + +// #[test] +// #[cfg(feature = "alloc")] +// fn from_bytes_to_bytes_be_is_the_identity_for_one() { +// let bytes = [ +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 1, +// ]; +// assert_eq!( +// FP2Element::from_bytes_be(&bytes).unwrap().to_bytes_be(), +// bytes +// ); +// } + +// #[test] +// #[cfg(feature = "alloc")] +// fn to_bytes_from_bytes_le_is_the_identity() { +// let x = FP2Element::new(UnsignedInteger::from_hex_unchecked( +// "5f103b0bd4397d4df560eb559f38353f80eeb6", +// )); +// assert_eq!(FP2Element::from_bytes_le(&x.to_bytes_le()).unwrap(), x); +// } + +// #[test] +// #[cfg(feature = "alloc")] +// fn from_bytes_to_bytes_le_is_the_identity_for_one() { +// let bytes = [ +// 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, +// ]; +// assert_eq!( +// FP2Element::from_bytes_le(&bytes).unwrap().to_bytes_le(), +// bytes +// ); +// } + +// #[test] +// #[cfg(feature = "alloc")] +// fn creating_a_field_element_from_its_representative_returns_the_same_element_1() { +// let change = U256::from_u64(1); +// let f1 = U256FP1Element::new(U256ModulusP1::MODULUS + change); +// let f2 = U256FP1Element::new(f1.representative()); +// assert_eq!(f1, f2); +// } + +// #[test] +// fn creating_a_field_element_from_its_representative_returns_the_same_element_2() { +// let change = U256::from_u64(27); +// let f1 = U256F29Element::new(U256Modulus29::MODULUS + change); +// let f2 = U256F29Element::new(f1.representative()); +// assert_eq!(f1, f2); +// } + +// #[test] +// fn creating_a_field_element_from_hex_works_1() { +// let a = U256FP1Element::from_hex_unchecked("eb235f6144d9e91f4b14"); +// let b = U256FP1Element::new(U256 { +// limbs: [0, 0, 60195, 6872850209053821716], +// }); +// assert_eq!(a, b); +// } + +// #[test] +// fn creating_a_field_element_from_hex_too_big_errors() { +// let a = U256FP1Element::from_hex(&"f".repeat(65)); +// assert!(a.is_err()); +// assert_eq!( +// a.unwrap_err(), +// crate::errors::CreationError::HexStringIsTooBig +// ) +// } + +// #[test] +// fn creating_a_field_element_from_hex_works_on_the_size_limit() { +// let a = U256FP1Element::from_hex(&"f".repeat(64)); +// assert!(a.is_ok()); +// } + +// #[test] +// fn creating_a_field_element_from_hex_works_2() { +// let a = U256F29Element::from_hex_unchecked("aa"); +// let b = U256F29Element::from(25); +// assert_eq!(a, b); +// } + +// #[test] +// fn creating_a_field_element_from_hex_works_3() { +// let a = U256F29Element::from_hex_unchecked("1d"); +// let b = U256F29Element::zero(); +// assert_eq!(a, b); +// } + +// #[cfg(feature = "std")] +// #[test] +// fn to_hex_test_works_1() { +// let a = U256FP1Element::from_hex_unchecked("eb235f6144d9e91f4b14"); +// let b = U256FP1Element::new(U256 { +// limbs: [0, 0, 60195, 6872850209053821716], +// }); + +// assert_eq!(U256FP1Element::to_hex(&a), U256FP1Element::to_hex(&b)); +// } + +// #[cfg(feature = "std")] +// #[test] +// fn to_hex_test_works_2() { +// let a = U256F29Element::from_hex_unchecked("1d"); +// let b = U256F29Element::zero(); + +// assert_eq!(U256F29Element::to_hex(&a), U256F29Element::to_hex(&b)); +// } + +// #[cfg(feature = "std")] +// #[test] +// fn to_hex_test_works_3() { +// let a = U256F29Element::from_hex_unchecked("aa"); +// let b = U256F29Element::from(25); + +// assert_eq!(U256F29Element::to_hex(&a), U256F29Element::to_hex(&b)); +// } + +// // Goldilocks +// #[derive(Clone, Debug)] +// struct GoldilocksModulus; +// impl IsModulus for GoldilocksModulus { +// const MODULUS: U64 = UnsignedInteger { +// limbs: [18446744069414584321], +// }; +// } + +// type GoldilocksField = U64PrimeField; +// type GoldilocksElement = FieldElement; + +// #[derive(Clone, Debug)] +// struct SecpModulus; +// impl IsModulus for SecpModulus { +// const MODULUS: U256 = UnsignedInteger::from_hex_unchecked( +// "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", +// ); +// } +// type SecpMontField = U256PrimeField; +// type SecpMontElement = FieldElement; + +// #[test] +// fn secp256k1_minus_three_pow_2_is_9_with_all_operations() { +// let minus_3 = -SecpMontElement::from_hex_unchecked("0x3"); +// let minus_3_mul_minus_3 = &minus_3 * &minus_3; +// let minus_3_squared = minus_3.square(); +// let minus_3_pow_2 = minus_3.pow(2_u32); +// let nine = SecpMontElement::from_hex_unchecked("0x9"); + +// assert_eq!(minus_3_mul_minus_3, nine); +// assert_eq!(minus_3_squared, nine); +// assert_eq!(minus_3_pow_2, nine); +// } + +// #[test] +// fn secp256k1_inv_works() { +// let a = SecpMontElement::from_hex_unchecked("0x456"); +// let a_inv = a.inv().unwrap(); + +// assert_eq!(a * a_inv, SecpMontElement::one()); +// } + +// #[test] +// fn test_cios_overflow_case() { +// let a = GoldilocksElement::from(732582227915286439); +// let b = GoldilocksElement::from(3906369333256140342); +// let expected_sum = GoldilocksElement::from(4638951561171426781); +// assert_eq!(a + b, expected_sum); +// } + +// // Tests for the Montgomery Algorithms +// proptest! { +// #[test] +// fn cios_vs_cios_optimized(a in any::<[u64; 6]>(), b in any::<[u64; 6]>()) { +// let x = U384::from_limbs(a); +// let y = U384::from_limbs(b); +// let m = U384::from_hex_unchecked("cdb061954fdd36e5176f50dbdcfd349570a29ce1"); // this is prime +// let mu: u64 = 16085280245840369887; // negative of the inverse of `m` modulo 2^{64} +// assert_eq!( +// MontgomeryAlgorithms::cios(&x, &y, &m, &mu), +// MontgomeryAlgorithms::cios_optimized_for_moduli_with_one_spare_bit(&x, &y, &m, &mu) +// ); +// } + +// #[test] +// fn cios_vs_sos_square(a in any::<[u64; 6]>()) { +// let x = U384::from_limbs(a); +// let m = U384::from_hex_unchecked("cdb061954fdd36e5176f50dbdcfd349570a29ce1"); // this is prime +// let mu: u64 = 16085280245840369887; // negative of the inverse of `m` modulo 2^{64} +// assert_eq!( +// MontgomeryAlgorithms::cios(&x, &x, &m, &mu), +// MontgomeryAlgorithms::sos_square(&x, &m, &mu) +// ); +// } +// } +// #[test] +// fn montgomery_multiplication_works_0() { +// let x = U384::from_u64(11_u64); +// let y = U384::from_u64(10_u64); +// let m = U384::from_u64(23_u64); // +// let mu: u64 = 3208129404123400281; // negative of the inverse of `m` modulo 2^{64}. +// let c = U384::from_u64(13_u64); // x * y * (r^{-1}) % m, where r = 2^{64 * 6} and r^{-1} mod m = 2. +// assert_eq!(MontgomeryAlgorithms::cios(&x, &y, &m, &mu), c); +// } + +// #[test] +// fn montgomery_multiplication_works_1() { +// let x = U384::from_hex_unchecked("05ed176deb0e80b4deb7718cdaa075165f149c"); +// let y = U384::from_hex_unchecked("5f103b0bd4397d4df560eb559f38353f80eeb6"); +// let m = U384::from_hex_unchecked("cdb061954fdd36e5176f50dbdcfd349570a29ce1"); // this is prime +// let mu: u64 = 16085280245840369887; // negative of the inverse of `m` modulo 2^{64} +// let c = U384::from_hex_unchecked("8d65cdee621682815d59f465d2641eea8a1274dc"); // x * y * (r^{-1}) % m, where r = 2^{64 * 6} +// assert_eq!(MontgomeryAlgorithms::cios(&x, &y, &m, &mu), c); +// } + +// #[test] +// fn montgomery_multiplication_works_2() { +// let x = U384::from_hex_unchecked("8d65cdee621682815d59f465d2641eea8a1274dc"); +// let m = U384::from_hex_unchecked("cdb061954fdd36e5176f50dbdcfd349570a29ce1"); // this is prime +// let r_mod_m = U384::from_hex_unchecked("58dfb0e1b3dd5e674bdcde4f42eb5533b8759d33"); +// let mu: u64 = 16085280245840369887; // negative of the inverse of `m` modulo 2^{64} +// let c = U384::from_hex_unchecked("8d65cdee621682815d59f465d2641eea8a1274dc"); +// assert_eq!(MontgomeryAlgorithms::cios(&x, &r_mod_m, &m, &mu), c); +// } +// } From de417ae7db2b56bd4682f8d6939bb60b2e8a459d Mon Sep 17 00:00:00 2001 From: Nicole Date: Mon, 2 Dec 2024 18:51:25 -0300 Subject: [PATCH 02/18] add benches. Change cios for mul and then reduction --- math/Cargo.toml | 3 +- math/benches/criterion_field.rs | 6 +- math/benches/fields/baby_bear.rs | 217 ++++++++++++++++++ math/benches/fields/mod.rs | 1 + .../field/fields/fft_friendly/babybear_u32.rs | 204 ++++++++++++---- .../u32_montgomery_backend_prime_field.rs | 86 ++++--- 6 files changed, 432 insertions(+), 85 deletions(-) create mode 100644 math/benches/fields/baby_bear.rs diff --git a/math/Cargo.toml b/math/Cargo.toml index 9a6838a10..ba873f93c 100644 --- a/math/Cargo.toml +++ b/math/Cargo.toml @@ -39,7 +39,8 @@ const-random = "0.1.15" iai-callgrind.workspace = true proptest = "1.1.0" pprof = { version = "0.13.0", features = ["criterion", "flamegraph"] } - +p3-baby-bear = { git = "https://github.com/Plonky3/Plonky3" } +p3-field = { git = "https://github.com/Plonky3/Plonky3" } [features] default = ["parallel", "std"] std = ["alloc", "serde?/std", "serde_json?/std"] diff --git a/math/benches/criterion_field.rs b/math/benches/criterion_field.rs index 6e41cbb4b..e790f38cd 100644 --- a/math/benches/criterion_field.rs +++ b/math/benches/criterion_field.rs @@ -5,13 +5,15 @@ mod fields; use fields::mersenne31::{mersenne31_extension_ops_benchmarks, mersenne31_ops_benchmarks}; use fields::mersenne31_montgomery::mersenne31_mont_ops_benchmarks; use fields::{ - stark252::starkfield_ops_benchmarks, u64_goldilocks::u64_goldilocks_ops_benchmarks, + baby_bear::{babybear_ops_benchmarks, babybear_ops_benchmarks_f64, babybear_p3_ops_benchmarks}, + stark252::starkfield_ops_benchmarks, + u64_goldilocks::u64_goldilocks_ops_benchmarks, u64_goldilocks_montgomery::u64_goldilocks_montgomery_ops_benchmarks, }; criterion_group!( name = field_benches; config = Criterion::default().with_profiler(PProfProfiler::new(100, Output::Flamegraph(None))); - targets = mersenne31_ops_benchmarks, mersenne31_extension_ops_benchmarks, mersenne31_mont_ops_benchmarks, starkfield_ops_benchmarks, u64_goldilocks_ops_benchmarks, u64_goldilocks_montgomery_ops_benchmarks + targets =babybear_ops_benchmarks,babybear_ops_benchmarks_f64, babybear_p3_ops_benchmarks ); criterion_main!(field_benches); diff --git a/math/benches/fields/baby_bear.rs b/math/benches/fields/baby_bear.rs new file mode 100644 index 000000000..f01770b9a --- /dev/null +++ b/math/benches/fields/baby_bear.rs @@ -0,0 +1,217 @@ +use criterion::Criterion; +use std::hint::black_box; + +use lambdaworks_math::field::{ + element::FieldElement, + fields::{ + fft_friendly::babybear::Babybear31PrimeField, + u32_montgomery_backend_prime_field::U32MontgomeryBackendPrimeField, + }, +}; + +use p3_baby_bear::BabyBear; +use p3_field::extension::BinomialExtensionField; +use p3_field::{Field, FieldAlgebra}; + +use rand::random; +use rand::Rng; + +pub type U32Babybear31PrimeField = U32MontgomeryBackendPrimeField<2013265921>; +pub type F = FieldElement; +pub type F64 = FieldElement; + +pub fn rand_field_elements(num: usize) -> Vec<(F, F)> { + let mut result = Vec::with_capacity(num); + for _ in 0..result.capacity() { + result.push((F::from(random::()), F::from(random::()))); + } + result +} + +fn rand_babybear_elements_p3(num: usize) -> Vec<(BabyBear, BabyBear)> { + let mut rng = rand::thread_rng(); + (0..num) + .map(|_| (rng.gen::(), rng.gen::())) + .collect() +} + +pub fn babybear_ops_benchmarks(c: &mut Criterion) { + let input: Vec> = [1000000] + .into_iter() + .map(rand_field_elements) + .collect::>(); + let mut group = c.benchmark_group("BabyBear operations using Lambdaworks u32"); + + for i in input.clone().into_iter() { + group.bench_with_input(format!("Addition {:?}", &i.len()), &i, |bench, i| { + bench.iter(|| { + for (x, y) in i { + black_box(black_box(x) + black_box(y)); + } + }); + }); + } + + for i in input.clone().into_iter() { + group.bench_with_input(format!("Multiplication {:?}", &i.len()), &i, |bench, i| { + bench.iter(|| { + for (x, y) in i { + black_box(black_box(x) * black_box(y)); + } + }); + }); + } + + for i in input.clone().into_iter() { + group.bench_with_input(format!("Square {:?}", &i.len()), &i, |bench, i| { + bench.iter(|| { + for (x, _) in i { + black_box(black_box(x).square()); + } + }); + }); + } + + for i in input.clone().into_iter() { + group.bench_with_input(format!("Inverse {:?}", &i.len()), &i, |bench, i| { + bench.iter(|| { + for (x, _) in i { + black_box(black_box(x).inv().unwrap()); + } + }); + }); + } + + for i in input.clone().into_iter() { + group.bench_with_input(format!("Division {:?}", &i.len()), &i, |bench, i| { + bench.iter(|| { + for (x, y) in i { + black_box(black_box(x) / black_box(y)); + } + }); + }); + } +} + +pub fn rand_field_elements_u64(num: usize) -> Vec<(F64, F64)> { + let mut result = Vec::with_capacity(num); + for _ in 0..result.capacity() { + result.push((F64::from(random::()), F64::from(random::()))); + } + result +} +pub fn babybear_ops_benchmarks_f64(c: &mut Criterion) { + let input: Vec> = [1000000] + .into_iter() + .map(rand_field_elements_u64) + .collect::>(); + let mut group = c.benchmark_group("BabyBear operations using Lambdaworks u64"); + + for i in input.clone().into_iter() { + group.bench_with_input(format!("Addition {:?}", &i.len()), &i, |bench, i| { + bench.iter(|| { + for (x, y) in i { + black_box(black_box(x) + black_box(y)); + } + }); + }); + } + + for i in input.clone().into_iter() { + group.bench_with_input(format!("Multiplication {:?}", &i.len()), &i, |bench, i| { + bench.iter(|| { + for (x, y) in i { + black_box(black_box(x) * black_box(y)); + } + }); + }); + } + + for i in input.clone().into_iter() { + group.bench_with_input(format!("Square {:?}", &i.len()), &i, |bench, i| { + bench.iter(|| { + for (x, _) in i { + black_box(black_box(x).square()); + } + }); + }); + } + + for i in input.clone().into_iter() { + group.bench_with_input(format!("Inverse {:?}", &i.len()), &i, |bench, i| { + bench.iter(|| { + for (x, _) in i { + black_box(black_box(x).inv().unwrap()); + } + }); + }); + } + + for i in input.clone().into_iter() { + group.bench_with_input(format!("Division {:?}", &i.len()), &i, |bench, i| { + bench.iter(|| { + for (x, y) in i { + black_box(black_box(x) / black_box(y)); + } + }); + }); + } +} + +pub fn babybear_p3_ops_benchmarks(c: &mut Criterion) { + let input: Vec> = [1000000] + .into_iter() + .map(rand_babybear_elements_p3) + .collect::>(); + + let mut group = c.benchmark_group("BabyBear operations using Plonky3"); + + for i in input.clone().into_iter() { + group.bench_with_input(format!("Addition {:?}", &i.len()), &i, |bench, i| { + bench.iter(|| { + for (x, y) in i { + black_box(black_box(*x) + black_box(*y)); + } + }); + }); + } + + for i in input.clone().into_iter() { + group.bench_with_input(format!("Multiplication {:?}", &i.len()), &i, |bench, i| { + bench.iter(|| { + for (x, y) in i { + black_box(black_box(*x) * black_box(*y)); + } + }); + }); + } + + for i in input.clone().into_iter() { + group.bench_with_input(format!("Square {:?}", &i.len()), &i, |bench, i| { + bench.iter(|| { + for (x, _) in i { + black_box(black_box(x).square()); + } + }); + }); + } + for i in input.clone().into_iter() { + group.bench_with_input(format!("Inverse {:?}", &i.len()), &i, |bench, i| { + bench.iter(|| { + for (x, _) in i { + black_box(black_box(x).inverse()); + } + }); + }); + } + + for i in input.clone().into_iter() { + group.bench_with_input(format!("Division {:?}", &i.len()), &i, |bench, i| { + bench.iter(|| { + for (x, y) in i { + black_box(black_box(*x) / black_box(*y)); + } + }); + }); + } +} diff --git a/math/benches/fields/mod.rs b/math/benches/fields/mod.rs index a28773c6c..b9107b974 100644 --- a/math/benches/fields/mod.rs +++ b/math/benches/fields/mod.rs @@ -1,3 +1,4 @@ +pub mod baby_bear; pub mod mersenne31; pub mod mersenne31_montgomery; pub mod stark252; diff --git a/math/src/field/fields/fft_friendly/babybear_u32.rs b/math/src/field/fields/fft_friendly/babybear_u32.rs index c6f29222c..37437dc8a 100644 --- a/math/src/field/fields/fft_friendly/babybear_u32.rs +++ b/math/src/field/fields/fft_friendly/babybear_u32.rs @@ -30,86 +30,198 @@ mod tests { mod test_babybear_31_bytes_ops { use super::*; - use crate::{field::element::FieldElement, traits::ByteConversion}; + use crate::{ + field::{element::FieldElement, errors::FieldError, traits::IsField}, + traits::ByteConversion, + }; + type FE = FieldElement; #[test] fn two_plus_one_is_three() { - let a = FieldElement::::from(2); - let b = FieldElement::::one(); - let res = FieldElement::::from(3); + let a = FE::from(2); + let b = FE::one(); + let res = FE::from(3); assert_eq!(a + b, res) } #[test] fn one_minus_two_is_minus_one() { - let a = FieldElement::::from(2); - let b = FieldElement::::one(); - let res = FieldElement::::from(2013265921); - let zero = FieldElement::::zero(); - - println!("Zero: {:?}", zero); - println!("Zero representative: {:?}", zero.representative()); - println!("Mod: {:?}", res); - println!("Mod representative: {:?}", res.representative()); - println!("-B: {:?}", (-b).representative()); - println!("A: {:?}", a); - println!("A representative: {:?}", a.representative()); - println!("MOD plus 1: {:?}", (res + b).representative()); + let a = FE::from(2); + let b = FE::one(); + let res = FE::from(2013265920); assert_eq!(b - a, res) } #[test] fn mul_by_zero_is_zero() { - let a = FieldElement::::from(2); - let b = FieldElement::::zero(); + let a = FE::from(2); + let b = FE::zero(); assert_eq!(a * b, b) } #[test] fn neg_zero_is_zero() { - let zero = FieldElement::::from(0); + let zero = FE::from(0); assert_eq!(-&zero, zero); } #[test] - #[cfg(feature = "alloc")] - fn byte_serialization_for_a_number_matches_with_byte_conversion_implementation_le() { - let element = - FieldElement::::from_hex("0x0123456701234567").unwrap(); - let bytes = element.to_bytes_le(); - let expected_bytes: [u8; 8] = ByteConversion::to_bytes_le(&element).try_into().unwrap(); - assert_eq!(bytes, expected_bytes); + fn doubling() { + assert_eq!(FE::from(2).double(), FE::from(2) + FE::from(2),); + } + + const ORDER: usize = 2013265921; + + #[test] + fn max_order_pis_0() { + assert_eq!(FE::from((ORDER - 1) as u64) + FE::from(1), FE::from(0)); + } + + #[test] + fn when_comparing_13_and_13_they_are_equal() { + let a: FE = FE::from(13); + let b: FE = FE::from(13); + assert_eq!(a, b); + } + + #[test] + fn when_comparing_13_and_8_they_are_different() { + let a: FE = FE::from(13); + let b: FE = FE::from(8); + assert_ne!(a, b); + } + + #[test] + fn mul_neutral_element() { + let a: FE = FE::from(1); + let b: FE = FE::from(2); + assert_eq!(a * b, FE::from(2)); + } + + #[test] + fn mul_2_3_is_6() { + let a: FE = FE::from(2); + let b: FE = FE::from(3); + assert_eq!(a * b, FE::from(6)); + } + + #[test] + fn mul_order_minus_1() { + let a: FE = FE::from((ORDER - 1) as u64); + let b: FE = FE::from((ORDER - 1) as u64); + assert_eq!(a * b, FE::from(1)); + } + + #[test] + fn inv_0_error() { + let result = FE::from(0).inv(); + assert!(matches!(result, Err(FieldError::InvZeroError))) + } + + #[test] + fn inv_2() { + let a: FE = FE::from(2); + assert_eq!(&a * a.inv().unwrap(), FE::from(1)); } #[test] - #[cfg(feature = "alloc")] - fn byte_serialization_for_a_number_matches_with_byte_conversion_implementation_be() { - let element = - FieldElement::::from_hex("0123456701234567").unwrap(); - let bytes = element.to_bytes_be(); - let expected_bytes: [u8; 8] = ByteConversion::to_bytes_be(&element).try_into().unwrap(); - assert_eq!(bytes, expected_bytes); + fn square_2_is_4() { + assert_eq!(FE::from(2).square(), FE::from(4)) } #[test] - fn byte_serialization_and_deserialization_works_le() { - let element = - FieldElement::::from_hex("0x7654321076543210").unwrap(); - let bytes = element.to_bytes_le(); - let from_bytes = FieldElement::::from_bytes_le(&bytes).unwrap(); - assert_eq!(element, from_bytes); + fn pow_2_3() { + assert_eq!(FE::from(2).pow(3_u64), FE::from(8)) } #[test] - fn byte_serialization_and_deserialization_works_be() { - let element = - FieldElement::::from_hex("7654321076543210").unwrap(); - let bytes = element.to_bytes_be(); - let from_bytes = FieldElement::::from_bytes_be(&bytes).unwrap(); - assert_eq!(element, from_bytes); + fn pow_p_minus_1() { + assert_eq!(FE::from(2).pow(ORDER - 1), FE::from(1)) } + + #[test] + fn div_1() { + assert_eq!(FE::from(2) / FE::from(1), FE::from(2)) + } + + #[test] + fn div_4_2() { + assert_eq!(FE::from(4) / FE::from(2), FE::from(2)) + } + + #[test] + fn three_inverse_mul_three_is_one() { + let a = FE::from(3); + assert_eq!(a.inv().unwrap() * a, FE::one()) + } + + #[test] + fn div_4_3() { + assert_eq!(FE::from(4) / FE::from(3) * FE::from(3), FE::from(4)) + } + + #[test] + fn two_plus_its_additive_inv_is_0() { + let two = FE::from(2); + + assert_eq!(&two + (-&two), FE::from(0)) + } + + #[test] + fn four_minus_three_is_1() { + let four = FE::from(4); + let three = FE::from(3); + + assert_eq!(four - three, FE::from(1)) + } + + #[test] + fn zero_minus_1_is_order_minus_1() { + let zero = FE::from(0); + let one = FE::from(1); + + assert_eq!(zero - one, FE::from((ORDER - 1) as u64)) + } + + // #[test] + // #[cfg(feature = "alloc")] + // fn byte_serialization_for_a_number_matches_with_byte_conversion_implementation_le() { + // let element = + // FieldElement::::from_hex("0x0123456701234567").unwrap(); + // let bytes = element.to_bytes_le(); + // let expected_bytes: [u8; 8] = ByteConversion::to_bytes_le(&element).try_into().unwrap(); + // assert_eq!(bytes, expected_bytes); + // } + + // #[test] + // #[cfg(feature = "alloc")] + // fn byte_serialization_for_a_number_matches_with_byte_conversion_implementation_be() { + // let element = + // FieldElement::::from_hex("0123456701234567").unwrap(); + // let bytes = element.to_bytes_be(); + // let expected_bytes: [u8; 8] = ByteConversion::to_bytes_be(&element).try_into().unwrap(); + // assert_eq!(bytes, expected_bytes); + // } + + // #[test] + // fn byte_serialization_and_deserialization_works_le() { + // let element = + // FieldElement::::from_hex("0x7654321076543210").unwrap(); + // let bytes = element.to_bytes_le(); + // let from_bytes = FieldElement::::from_bytes_le(&bytes).unwrap(); + // assert_eq!(element, from_bytes); + // } + + // #[test] + // fn byte_serialization_and_deserialization_works_be() { + // let element = + // FieldElement::::from_hex("7654321076543210").unwrap(); + // let bytes = element.to_bytes_be(); + // let from_bytes = FieldElement::::from_bytes_be(&bytes).unwrap(); + // assert_eq!(element, from_bytes); + // } } #[cfg(all(feature = "std", not(feature = "instruments")))] diff --git a/math/src/field/fields/u32_montgomery_backend_prime_field.rs b/math/src/field/fields/u32_montgomery_backend_prime_field.rs index 6817bce12..97f9db5b8 100644 --- a/math/src/field/fields/u32_montgomery_backend_prime_field.rs +++ b/math/src/field/fields/u32_montgomery_backend_prime_field.rs @@ -90,38 +90,55 @@ impl IsField for U32MontgomeryBackendPrimeField { #[inline(always)] fn add(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - let (sum, overflow) = a.overflowing_add(*b); - if Self::MODULUS_HAS_ONE_SPARE_BIT { - if sum >= MODULUS { - sum - MODULUS - } else { - sum - } - } else if overflow || sum >= MODULUS { - sum - MODULUS - } else { - sum + let mut sum = a + b; + let (corr_sum, over) = sum.overflowing_sub(MODULUS); + if !over { + sum = corr_sum; } + sum + + // let (sum, overflow) = a.overflowing_add(*b); + // if Self::MODULUS_HAS_ONE_SPARE_BIT { + // if sum >= MODULUS { + // sum - MODULUS + // } else { + // sum + // } + // } else if overflow || sum >= MODULUS { + // sum - MODULUS + // } else { + // sum + // } } + /* + fn add(self, rhs: Self) -> Self { + let mut sum = self.value + rhs.value; + let (corr_sum, over) = sum.overflowing_sub(FP::PRIME); + if !over { + sum = corr_sum; + } + Self::new_monty(sum) + } + */ #[inline(always)] fn mul(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - if Self::MODULUS_HAS_ONE_SPARE_BIT { - MontgomeryAlgorithms::cios_optimized_for_moduli_with_one_spare_bit( - *a, - *b, - MODULUS, - Self::MU, - ) - } else { - MontgomeryAlgorithms::cios(a, b, &MODULUS, &Self::MU) - } + // if Self::MODULUS_HAS_ONE_SPARE_BIT { + // MontgomeryAlgorithms::cios_optimized_for_moduli_with_one_spare_bit( + // *a, + // *b, + // MODULUS, + // Self::MU, + // ) + // } else { + MontgomeryAlgorithms::cios(a, b, &MODULUS, &Self::MU) + // } } - #[inline(always)] - fn square(a: &Self::BaseType) -> Self::BaseType { - MontgomeryAlgorithms::sos_square(*a, MODULUS, &Self::MU) - } + // #[inline(always)] + // fn square(a: &Self::BaseType) -> Self::BaseType { + // MontgomeryAlgorithms::sos_square(*a, MODULUS, &Self::MU) + // } #[inline(always)] fn sub(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { @@ -355,17 +372,14 @@ impl MontgomeryAlgorithms { /// https://www.microsoft.com/en-us/research/wp-content/uploads/1998/06/97Acar.pdf #[inline(always)] pub const fn cios(a: &u32, b: &u32, q: &u32, mu: &u32) -> u32 { - let t = *a as u64 * *b as u64; - let m = ((t as u32).wrapping_mul(*mu)) as u64; - - let t = t + m * (*q as u64); - - let c = t >> 32; - let mut result = c as u32; - if result >= *q { - result = result.wrapping_sub(*q); - } - result + let x = *a as u64 * *b as u64; + let t = x.wrapping_mul(*mu as u64) & (u32::MAX as u64); + let u = t * (*q as u64); + + let (x_sub_u, over) = x.overflowing_sub(u); + let x_sub_u_hi = (x_sub_u >> 32) as u32; + let corr = if over { q } else { &0 }; + x_sub_u_hi.wrapping_add(*corr) } /// Compute CIOS multiplication of `a` * `b` From c7d7aafabd1b87aa6cc14c70b42b0a5d8e6bc670 Mon Sep 17 00:00:00 2001 From: Nicole Date: Tue, 3 Dec 2024 16:48:23 -0300 Subject: [PATCH 03/18] try another version of inv. Serialization tests not working. --- .../field/fields/fft_friendly/babybear_u32.rs | 71 +++++++++---------- .../u32_montgomery_backend_prime_field.rs | 54 +++++++++++--- 2 files changed, 80 insertions(+), 45 deletions(-) diff --git a/math/src/field/fields/fft_friendly/babybear_u32.rs b/math/src/field/fields/fft_friendly/babybear_u32.rs index 37437dc8a..403358ab7 100644 --- a/math/src/field/fields/fft_friendly/babybear_u32.rs +++ b/math/src/field/fields/fft_friendly/babybear_u32.rs @@ -185,43 +185,40 @@ mod tests { assert_eq!(zero - one, FE::from((ORDER - 1) as u64)) } - // #[test] - // #[cfg(feature = "alloc")] - // fn byte_serialization_for_a_number_matches_with_byte_conversion_implementation_le() { - // let element = - // FieldElement::::from_hex("0x0123456701234567").unwrap(); - // let bytes = element.to_bytes_le(); - // let expected_bytes: [u8; 8] = ByteConversion::to_bytes_le(&element).try_into().unwrap(); - // assert_eq!(bytes, expected_bytes); - // } - - // #[test] - // #[cfg(feature = "alloc")] - // fn byte_serialization_for_a_number_matches_with_byte_conversion_implementation_be() { - // let element = - // FieldElement::::from_hex("0123456701234567").unwrap(); - // let bytes = element.to_bytes_be(); - // let expected_bytes: [u8; 8] = ByteConversion::to_bytes_be(&element).try_into().unwrap(); - // assert_eq!(bytes, expected_bytes); - // } - - // #[test] - // fn byte_serialization_and_deserialization_works_le() { - // let element = - // FieldElement::::from_hex("0x7654321076543210").unwrap(); - // let bytes = element.to_bytes_le(); - // let from_bytes = FieldElement::::from_bytes_le(&bytes).unwrap(); - // assert_eq!(element, from_bytes); - // } - - // #[test] - // fn byte_serialization_and_deserialization_works_be() { - // let element = - // FieldElement::::from_hex("7654321076543210").unwrap(); - // let bytes = element.to_bytes_be(); - // let from_bytes = FieldElement::::from_bytes_be(&bytes).unwrap(); - // assert_eq!(element, from_bytes); - // } + #[test] + #[cfg(feature = "alloc")] + fn byte_serialization_for_a_number_matches_with_byte_conversion_implementation_le() { + let element = FieldElement::::from_hex("0123456701234567").unwrap(); + let bytes = element.to_bytes_le(); + let expected_bytes: [u8; 8] = ByteConversion::to_bytes_le(&element).try_into().unwrap(); + assert_eq!(bytes, expected_bytes); + } + + #[test] + #[cfg(feature = "alloc")] + fn byte_serialization_for_a_number_matches_with_byte_conversion_implementation_be() { + let element = FieldElement::::from_hex("01234567").unwrap(); + println!("ELEMENT: {:?}", element); + let bytes = element.to_bytes_be(); + let expected_bytes: [u8; 8] = ByteConversion::to_bytes_be(&element).try_into().unwrap(); + assert_eq!(bytes, expected_bytes); + } + + #[test] + fn byte_serialization_and_deserialization_works_le() { + let element = FieldElement::::from_hex("0x76543210").unwrap(); + let bytes = element.to_bytes_le(); + let from_bytes = FieldElement::::from_bytes_le(&bytes).unwrap(); + assert_eq!(element, from_bytes); + } + + #[test] + fn byte_serialization_and_deserialization_works_be() { + let element = FieldElement::::from_hex("76543210").unwrap(); + let bytes = element.to_bytes_be(); + let from_bytes = FieldElement::::from_bytes_be(&bytes).unwrap(); + assert_eq!(element, from_bytes); + } } #[cfg(all(feature = "std", not(feature = "instruments")))] diff --git a/math/src/field/fields/u32_montgomery_backend_prime_field.rs b/math/src/field/fields/u32_montgomery_backend_prime_field.rs index 97f9db5b8..21c458434 100644 --- a/math/src/field/fields/u32_montgomery_backend_prime_field.rs +++ b/math/src/field/fields/u32_montgomery_backend_prime_field.rs @@ -160,6 +160,35 @@ impl IsField for U32MontgomeryBackendPrimeField { #[inline(always)] fn inv(a: &Self::BaseType) -> Result { + // if a == &Self::ZERO { + // return Err(FieldError::InvZeroError); + // } + + // // From Fermat's little theorem, in a prime field `F_p`, the inverse of `a` is `a^(p-2)`. + // // Here p-2 = 2013265919 = 1110111111111111111111111111111_2. + // // Uses 30 Squares + 7 Multiplications => 37 Operations total. + // let p100000000 = MontgomeryAlgorithms::exp_power_of_2(a, 8, &MODULUS) as u64; + // let p100000001 = p100000000 * a; + // let p10000000000000000 = MontgomeryAlgorithms::exp_power_of_2(&p100000000, 8, &MODULUS); + // let p10000000100000001 = p10000000000000000 * p100000001; + // let p10000000100000001000 = + // MontgomeryAlgorithms::exp_power_of_2(&p10000000100000001, 3, &MODULUS); + // let p1000000010000000100000000 = + // MontgomeryAlgorithms::exp_power_of_2(&p10000000100000001000, 5, &MODULUS); + // let p1000000010000000100000001 = p1000000010000000100000000 * a; + // let p1000010010000100100001001 = p1000000010000000100000001 * p10000000100000001000; + // let p10000000100000001000000010 = p1000000010000000100000001.pow(2); + // let p11000010110000101100001011 = p10000000100000001000000010 * p1000010010000100100001001; + // let p100000001000000010000000100 = p10000000100000001000000010.pow(2); + // let p111000011110000111100001111 = + // p100000001000000010000000100 * p11000010110000101100001011; + // let p1110000111100001111000011110000 = + // MontgomeryAlgorithms::exp_power_of_2(&p111000011110000111100001111, 4, &MODULUS); + // let p1110111111111111111111111111111 = + // p1110000111100001111000011110000 * p111000011110000111100001111; + + // Ok(p1110111111111111111111111111111 as u32) + if a == &Self::ZERO { Err(FieldError::InvZeroError) } else { @@ -291,15 +320,16 @@ impl IsPrimeField for U32MontgomeryBackendPrimeField u32 { + let mut res = a.clone(); + for _ in 0..power_log { + res = Self::cios(&res, &res, q, &2281701377); + } + res + } + /// Compute CIOS multiplication of `a` * `b` /// This is the Algorithm 2 described in the paper /// "EdMSM: Multi-Scalar-Multiplication for SNARKs and Faster Montgomery multiplication" From 171d739048415782fd634b20f2d46683749aba96 Mon Sep 17 00:00:00 2001 From: jotabulacios Date: Tue, 3 Dec 2024 17:27:23 -0300 Subject: [PATCH 04/18] fix from_hex behaviour when working with larger values --- math/src/field/fields/u32_montgomery_backend_prime_field.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/math/src/field/fields/u32_montgomery_backend_prime_field.rs b/math/src/field/fields/u32_montgomery_backend_prime_field.rs index 21c458434..cf37bfeac 100644 --- a/math/src/field/fields/u32_montgomery_backend_prime_field.rs +++ b/math/src/field/fields/u32_montgomery_backend_prime_field.rs @@ -320,8 +320,12 @@ impl IsPrimeField for U32MontgomeryBackendPrimeField Date: Tue, 3 Dec 2024 18:15:42 -0300 Subject: [PATCH 05/18] fix serialization tests --- math/src/field/fields/fft_friendly/babybear_u32.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/math/src/field/fields/fft_friendly/babybear_u32.rs b/math/src/field/fields/fft_friendly/babybear_u32.rs index 403358ab7..456d0b352 100644 --- a/math/src/field/fields/fft_friendly/babybear_u32.rs +++ b/math/src/field/fields/fft_friendly/babybear_u32.rs @@ -188,9 +188,10 @@ mod tests { #[test] #[cfg(feature = "alloc")] fn byte_serialization_for_a_number_matches_with_byte_conversion_implementation_le() { - let element = FieldElement::::from_hex("0123456701234567").unwrap(); + let element = + FieldElement::::from_hex("0123456701234567").unwrap(); let bytes = element.to_bytes_le(); - let expected_bytes: [u8; 8] = ByteConversion::to_bytes_le(&element).try_into().unwrap(); + let expected_bytes: [u8; 4] = ByteConversion::to_bytes_le(&element).try_into().unwrap(); assert_eq!(bytes, expected_bytes); } @@ -200,7 +201,7 @@ mod tests { let element = FieldElement::::from_hex("01234567").unwrap(); println!("ELEMENT: {:?}", element); let bytes = element.to_bytes_be(); - let expected_bytes: [u8; 8] = ByteConversion::to_bytes_be(&element).try_into().unwrap(); + let expected_bytes: [u8; 4] = ByteConversion::to_bytes_be(&element).try_into().unwrap(); assert_eq!(bytes, expected_bytes); } From 1f0967136854c69f4224b5611d5ba3eb627fefbc Mon Sep 17 00:00:00 2001 From: jotabulacios Date: Tue, 3 Dec 2024 18:52:51 -0300 Subject: [PATCH 06/18] try plonky3 algorithm for inv --- .../u32_montgomery_backend_prime_field.rs | 236 +++++++++++------- 1 file changed, 141 insertions(+), 95 deletions(-) diff --git a/math/src/field/fields/u32_montgomery_backend_prime_field.rs b/math/src/field/fields/u32_montgomery_backend_prime_field.rs index cf37bfeac..9eb33c410 100644 --- a/math/src/field/fields/u32_montgomery_backend_prime_field.rs +++ b/math/src/field/fields/u32_montgomery_backend_prime_field.rs @@ -159,107 +159,153 @@ impl IsField for U32MontgomeryBackendPrimeField { } #[inline(always)] + fn inv(a: &Self::BaseType) -> Result { - // if a == &Self::ZERO { - // return Err(FieldError::InvZeroError); - // } + if *a == Self::ZERO { + return Err(FieldError::InvZeroError); + } - // // From Fermat's little theorem, in a prime field `F_p`, the inverse of `a` is `a^(p-2)`. - // // Here p-2 = 2013265919 = 1110111111111111111111111111111_2. - // // Uses 30 Squares + 7 Multiplications => 37 Operations total. - // let p100000000 = MontgomeryAlgorithms::exp_power_of_2(a, 8, &MODULUS) as u64; - // let p100000001 = p100000000 * a; - // let p10000000000000000 = MontgomeryAlgorithms::exp_power_of_2(&p100000000, 8, &MODULUS); - // let p10000000100000001 = p10000000000000000 * p100000001; - // let p10000000100000001000 = - // MontgomeryAlgorithms::exp_power_of_2(&p10000000100000001, 3, &MODULUS); - // let p1000000010000000100000000 = - // MontgomeryAlgorithms::exp_power_of_2(&p10000000100000001000, 5, &MODULUS); - // let p1000000010000000100000001 = p1000000010000000100000000 * a; - // let p1000010010000100100001001 = p1000000010000000100000001 * p10000000100000001000; - // let p10000000100000001000000010 = p1000000010000000100000001.pow(2); - // let p11000010110000101100001011 = p10000000100000001000000010 * p1000010010000100100001001; - // let p100000001000000010000000100 = p10000000100000001000000010.pow(2); - // let p111000011110000111100001111 = - // p100000001000000010000000100 * p11000010110000101100001011; - // let p1110000111100001111000011110000 = - // MontgomeryAlgorithms::exp_power_of_2(&p111000011110000111100001111, 4, &MODULUS); - // let p1110111111111111111111111111111 = - // p1110000111100001111000011110000 * p111000011110000111100001111; - - // Ok(p1110111111111111111111111111111 as u32) + let p100000000 = MontgomeryAlgorithms::exp_power_of_2(a, 8, &MODULUS); - if a == &Self::ZERO { - Err(FieldError::InvZeroError) - } else { - // Guajardo Kumar Paar Pelzl - // Efficient Software-Implementation of Finite Fields with Applications to - // Cryptography - // Algorithm 16 (BEA for Inversion in Fp) - - //These can be done with const functions - let modulus_has_spare_bits = MODULUS >> 31 == 0; - - let mut u: u32 = *a; - let mut v = MODULUS; - let mut b = Self::R2; // Avoids unnecessary reduction step. - let mut c = Self::zero(); - - while u != 1 && v != 1 { - while u & 1 == 0 { - u >>= 1; - if b & 1 == 0 { - b >>= 1; - } else { - let carry; - (b, carry) = b.overflowing_add(MODULUS); - b >>= 1; - if !modulus_has_spare_bits && carry { - b |= 1 << 31; - } - } - } - - while v & 1 == 0 { - v >>= 1; - - if c & 1 == 0 { - c >>= 1; - } else { - let carry; - (c, carry) = c.overflowing_add(MODULUS); - c >>= 1; - if !modulus_has_spare_bits && carry { - c |= 1 << 31; - } - } - } - - if v <= u { - u = u - v; - if b < c { - b = MODULUS - c + b; - } else { - b = b - c; - } - } else { - v = v - u; - if c < b { - c = MODULUS - b + c; - } else { - c = c - b; - } - } - } + let p100000001 = Self::mul(&p100000000, a); - if u == 1 { - Ok(b) - } else { - Ok(c) - } - } + let p10000000000000000 = MontgomeryAlgorithms::exp_power_of_2(&p100000000, 8, &MODULUS); + + let p10000000100000001 = Self::mul(&p10000000000000000, &p100000001); + + let p10000000100000001000 = + MontgomeryAlgorithms::exp_power_of_2(&p10000000100000001, 3, &MODULUS); + + let p1000000010000000100000000 = + MontgomeryAlgorithms::exp_power_of_2(&p10000000100000001000, 5, &MODULUS); + + let p1000000010000000100000001 = Self::mul(&p1000000010000000100000000, a); + + let p1000010010000100100001001 = + Self::mul(&p1000000010000000100000001, &p10000000100000001000); + + let p10000000100000001000000010 = Self::square(&p1000000010000000100000001); + + let p11000010110000101100001011 = + Self::mul(&p10000000100000001000000010, &p1000010010000100100001001); + + let p100000001000000010000000100 = Self::square(&p10000000100000001000000010); + + let p111000011110000111100001111 = + Self::mul(&p100000001000000010000000100, &p11000010110000101100001011); + + let p1110000111100001111000011110000 = + MontgomeryAlgorithms::exp_power_of_2(&p111000011110000111100001111, 4, &MODULUS); + + let p1110111111111111111111111111111 = Self::mul( + &p1110000111100001111000011110000, + &p111000011110000111100001111, + ); + + Ok(p1110111111111111111111111111111) } + /*fn inv(a: &Self::BaseType) -> Result { + // if a == &Self::ZERO { + // return Err(FieldError::InvZeroError); + // } + + // // From Fermat's little theorem, in a prime field `F_p`, the inverse of `a` is `a^(p-2)`. + // // Here p-2 = 2013265919 = 1110111111111111111111111111111_2. + // // Uses 30 Squares + 7 Multiplications => 37 Operations total. + // let p100000000 = MontgomeryAlgorithms::exp_power_of_2(a, 8, &MODULUS) as u64; + // let p100000001 = p100000000 * a; + // let p10000000000000000 = MontgomeryAlgorithms::exp_power_of_2(&p100000000, 8, &MODULUS); + // let p10000000100000001 = p10000000000000000 * p100000001; + // let p10000000100000001000 = + // MontgomeryAlgorithms::exp_power_of_2(&p10000000100000001, 3, &MODULUS); + // let p1000000010000000100000000 = + // MontgomeryAlgorithms::exp_power_of_2(&p10000000100000001000, 5, &MODULUS); + // let p1000000010000000100000001 = p1000000010000000100000000 * a; + // let p1000010010000100100001001 = p1000000010000000100000001 * p10000000100000001000; + // let p10000000100000001000000010 = p1000000010000000100000001.pow(2); + // let p11000010110000101100001011 = p10000000100000001000000010 * p1000010010000100100001001; + // let p100000001000000010000000100 = p10000000100000001000000010.pow(2); + // let p111000011110000111100001111 = + // p100000001000000010000000100 * p11000010110000101100001011; + // let p1110000111100001111000011110000 = + // MontgomeryAlgorithms::exp_power_of_2(&p111000011110000111100001111, 4, &MODULUS); + // let p1110111111111111111111111111111 = + // p1110000111100001111000011110000 * p111000011110000111100001111; + + // Ok(p1110111111111111111111111111111 as u32) + + if a == &Self::ZERO { + Err(FieldError::InvZeroError) + } else { + // Guajardo Kumar Paar Pelzl + // Efficient Software-Implementation of Finite Fields with Applications to + // Cryptography + // Algorithm 16 (BEA for Inversion in Fp) + + //These can be done with const functions + let modulus_has_spare_bits = MODULUS >> 31 == 0; + + let mut u: u32 = *a; + let mut v = MODULUS; + let mut b = Self::R2; // Avoids unnecessary reduction step. + let mut c = Self::zero(); + + while u != 1 && v != 1 { + while u & 1 == 0 { + u >>= 1; + if b & 1 == 0 { + b >>= 1; + } else { + let carry; + (b, carry) = b.overflowing_add(MODULUS); + b >>= 1; + if !modulus_has_spare_bits && carry { + b |= 1 << 31; + } + } + } + + while v & 1 == 0 { + v >>= 1; + + if c & 1 == 0 { + c >>= 1; + } else { + let carry; + (c, carry) = c.overflowing_add(MODULUS); + c >>= 1; + if !modulus_has_spare_bits && carry { + c |= 1 << 31; + } + } + } + + if v <= u { + u = u - v; + if b < c { + b = MODULUS - c + b; + } else { + b = b - c; + } + } else { + v = v - u; + if c < b { + c = MODULUS - b + c; + } else { + c = c - b; + } + } + } + + if u == 1 { + Ok(b) + } else { + Ok(c) + } + } + } + */ #[inline(always)] fn div(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { Self::mul(a, &Self::inv(b).unwrap()) From a3543d6c648ebe690a7d07999ef306cae6830bee Mon Sep 17 00:00:00 2001 From: jotabulacios Date: Wed, 4 Dec 2024 11:25:20 -0300 Subject: [PATCH 07/18] add new mul function --- .../u32_montgomery_backend_prime_field.rs | 46 ++++++++++++------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/math/src/field/fields/u32_montgomery_backend_prime_field.rs b/math/src/field/fields/u32_montgomery_backend_prime_field.rs index 9eb33c410..cd7293a53 100644 --- a/math/src/field/fields/u32_montgomery_backend_prime_field.rs +++ b/math/src/field/fields/u32_montgomery_backend_prime_field.rs @@ -121,25 +121,39 @@ impl IsField for U32MontgomeryBackendPrimeField { Self::new_monty(sum) } */ + #[inline(always)] - fn mul(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - // if Self::MODULUS_HAS_ONE_SPARE_BIT { - // MontgomeryAlgorithms::cios_optimized_for_moduli_with_one_spare_bit( - // *a, - // *b, - // MODULUS, - // Self::MU, - // ) - // } else { - MontgomeryAlgorithms::cios(a, b, &MODULUS, &Self::MU) - // } + fn mul(lhs: &u32, rhs: &u32) -> u32 { + let mut o64: u64 = (*lhs as u64).wrapping_mul(*rhs as u64); + let low: u32 = 0u32.wrapping_sub(o64 as u32); + let red = &Self::MU.wrapping_mul(low); + o64 = o64.wrapping_add((*red as u64).wrapping_mul(MODULUS as u64)); + let ret = (o64 >> 32) as u32; + if ret >= MODULUS { + ret.wrapping_sub(MODULUS) + } else { + ret + } } + /* + fn mul(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { + // if Self::MODULUS_HAS_ONE_SPARE_BIT { + // MontgomeryAlgorithms::cios_optimized_for_moduli_with_one_spare_bit( + // *a, + // *b, + // MODULUS, + // Self::MU, + // ) + // } else { + MontgomeryAlgorithms::cios(a, b, &MODULUS, &Self::MU) + // } + } - // #[inline(always)] - // fn square(a: &Self::BaseType) -> Self::BaseType { - // MontgomeryAlgorithms::sos_square(*a, MODULUS, &Self::MU) - // } - + // #[inline(always)] + // fn square(a: &Self::BaseType) -> Self::BaseType { + // MontgomeryAlgorithms::sos_square(*a, MODULUS, &Self::MU) + // } + */ #[inline(always)] fn sub(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { if b <= a { From 85a82d9c6805173f7c7626afa9901b764efe69c8 Mon Sep 17 00:00:00 2001 From: Nicole Date: Wed, 4 Dec 2024 12:01:06 -0300 Subject: [PATCH 08/18] add const functions for mu and r2 parameters --- .../u32_montgomery_backend_prime_field.rs | 97 +++++++++---------- 1 file changed, 46 insertions(+), 51 deletions(-) diff --git a/math/src/field/fields/u32_montgomery_backend_prime_field.rs b/math/src/field/fields/u32_montgomery_backend_prime_field.rs index cd7293a53..84c80fbe6 100644 --- a/math/src/field/fields/u32_montgomery_backend_prime_field.rs +++ b/math/src/field/fields/u32_montgomery_backend_prime_field.rs @@ -20,61 +20,56 @@ use core::fmt::Debug; pub struct U32MontgomeryBackendPrimeField; impl U32MontgomeryBackendPrimeField { - pub const R2: u32 = 1172168163; - pub const MU: u32 = 2281701377; + pub const R2: u32 = Self::compute_r2_parameter(); + pub const MU: u32 = Self::compute_mu_parameter(); pub const ZERO: u32 = 0; pub const ONE: u32 = MontgomeryAlgorithms::cios(&1, &Self::R2, &MODULUS, &Self::MU); const MODULUS_HAS_ONE_SPARE_BIT: bool = true; - /// Computes `- modulus^{-1} mod 2^{64}` - /// This algorithm is given by Dussé and Kaliski Jr. in - /// "S. R. Dussé and B. S. Kaliski Jr. A cryptographic library for the Motorola - /// DSP56000. In I. Damgård, editor, Advances in Cryptology – EUROCRYPT’90, - /// volume 473 of Lecture Notes in Computer Science, pages 230–244. Springer, - /// Heidelberg, May 1991." - // const fn compute_mu_parameter() -> u32 { - // let mut y: u32 = 1; - // let word_size = 32; - // let mut i: usize = 2; - // while i <= word_size { - // let (_, lo) = &MODULUS.overflowing_mul(y); - // let least_significant_limb = lo.limbs[0]; - // if (least_significant_limb << (word_size - i)) >> (word_size - i) != 1 { - // y += 1 << (i - 1); - // } - // i += 1; - // } - // y.wrapping_neg() - // } - - /// Computes 2^{384 * 2} modulo `modulus` - // const fn compute_r2_parameter() -> u32 { - // let word_size = 64; - // let mut l: usize = 0; - // let zero: u32 = 0; - // // Define `c` as the largest power of 2 smaller than `modulus` - // while l < word_size { - // if &MODULUS << l != 0 { - // break; - // } - // l += 1; - // } - // let mut c: u32 = 1 << l; - - // // Double `c` and reduce modulo `modulus` until getting - // // `2^{2 * number_limbs * word_size}` mod `modulus` - // let mut i: usize = 1; - // while i <= 2 * word_size - l { - // let (double_c, overflow) = &c.overflowing_add(c); - // c = if (&MODULUS <= &double_c) || *overflow { - // double_c.overflowing_sub(MODULUS).0 - // } else { - // *double_c - // }; - // i += 1; - // } - // c - // } + // Computes `modulus^{-1} mod 2^{32}` + // Algorithm adapted from `compute_mu_parameter()` from `montgomery_backed_prime_fields.rs` in Lambdaworks. + // E.g, in Baby Bear field MU = 2281701377. + const fn compute_mu_parameter() -> u32 { + let mut y = 1; + let word_size = 32; + let mut i: usize = 2; + while i <= word_size { + let mul_result = (MODULUS as u64 * y as u64) as u32; + if (mul_result << (word_size - i)) >> (word_size - i) != 1 { + y += 1 << (i - 1); + } + i += 1; + } + y + } + + // Computes `2^{2 * 32} mod modulus`. + // Algorithm adapted from `compute_r2_parameter()` from `montgomery_backed_prime_fields.rs` in Lambdaworks. + // E.g, in Baby Bear field R2 = 1172168163. + const fn compute_r2_parameter() -> u32 { + let word_size = 32; + let mut l: usize = 0; + + // Find the largest power of 2 smaller than modulus + while l < word_size && (MODULUS >> l) == 0 { + l += 1; + } + let mut c: u32 = 1 << l; + + // Double c and reduce modulo `MODULUS` until getting + // `2^{2 * word_size}` mod `MODULUS`. + let mut i: usize = 1; + while i <= 2 * word_size - l { + let double_c = c << 1; + c = if double_c >= MODULUS { + double_c - MODULUS + } else { + double_c + }; + i += 1; + } + c + } /// Checks whether the most significant limb of the modulus is ats /// most `0x7FFFFFFFFFFFFFFE`. This check is useful since special From 9e529710fe9911b75f7739a3f65394c31aec2bd3 Mon Sep 17 00:00:00 2001 From: jotabulacios Date: Wed, 4 Dec 2024 14:43:48 -0300 Subject: [PATCH 09/18] remove commented code and refactor functions --- .../field/fields/fft_friendly/babybear_u32.rs | 15 +- .../u32_montgomery_backend_prime_field.rs | 1206 +---------------- 2 files changed, 31 insertions(+), 1190 deletions(-) diff --git a/math/src/field/fields/fft_friendly/babybear_u32.rs b/math/src/field/fields/fft_friendly/babybear_u32.rs index 456d0b352..8fd56349e 100644 --- a/math/src/field/fields/fft_friendly/babybear_u32.rs +++ b/math/src/field/fields/fft_friendly/babybear_u32.rs @@ -1,10 +1,5 @@ -use crate::{ - field::{ - element::FieldElement, - fields::u32_montgomery_backend_prime_field::U32MontgomeryBackendPrimeField, - traits::IsFFTField, - }, - unsigned_integer::element::{UnsignedInteger, U64}, +use crate::field::{ + fields::u32_montgomery_backend_prime_field::U32MontgomeryBackendPrimeField, traits::IsFFTField, }; pub type Babybear31PrimeField = U32MontgomeryBackendPrimeField<2013265921>; @@ -31,7 +26,7 @@ mod tests { mod test_babybear_31_bytes_ops { use super::*; use crate::{ - field::{element::FieldElement, errors::FieldError, traits::IsField}, + field::{element::FieldElement, errors::FieldError}, traits::ByteConversion, }; type FE = FieldElement; @@ -123,7 +118,7 @@ mod tests { #[test] fn inv_2() { let a: FE = FE::from(2); - assert_eq!(&a * a.inv().unwrap(), FE::from(1)); + assert_eq!(a * a.inv().unwrap(), FE::from(1)); } #[test] @@ -166,7 +161,7 @@ mod tests { fn two_plus_its_additive_inv_is_0() { let two = FE::from(2); - assert_eq!(&two + (-&two), FE::from(0)) + assert_eq!(two + (-&two), FE::from(0)) } #[test] diff --git a/math/src/field/fields/u32_montgomery_backend_prime_field.rs b/math/src/field/fields/u32_montgomery_backend_prime_field.rs index 84c80fbe6..75377e4d6 100644 --- a/math/src/field/fields/u32_montgomery_backend_prime_field.rs +++ b/math/src/field/fields/u32_montgomery_backend_prime_field.rs @@ -1,14 +1,13 @@ use crate::errors::CreationError; use crate::field::element::FieldElement; use crate::field::errors::FieldError; +use crate::field::traits::IsField; use crate::field::traits::IsPrimeField; #[cfg(feature = "alloc")] use crate::traits::AsBytes; use crate::traits::ByteConversion; -use crate::{field::traits::IsField, unsigned_integer::element::UnsignedInteger}; use core::fmt::Debug; - #[cfg_attr( any( feature = "lambdaworks-serde-binary", @@ -23,8 +22,7 @@ impl U32MontgomeryBackendPrimeField { pub const R2: u32 = Self::compute_r2_parameter(); pub const MU: u32 = Self::compute_mu_parameter(); pub const ZERO: u32 = 0; - pub const ONE: u32 = MontgomeryAlgorithms::cios(&1, &Self::R2, &MODULUS, &Self::MU); - const MODULUS_HAS_ONE_SPARE_BIT: bool = true; + pub const ONE: u32 = MontgomeryAlgorithms::mul(&1, &Self::R2, &MODULUS, &Self::MU); // Computes `modulus^{-1} mod 2^{32}` // Algorithm adapted from `compute_mu_parameter()` from `montgomery_backed_prime_fields.rs` in Lambdaworks. @@ -70,14 +68,6 @@ impl U32MontgomeryBackendPrimeField { } c } - - /// Checks whether the most significant limb of the modulus is ats - /// most `0x7FFFFFFFFFFFFFFE`. This check is useful since special - /// optimizations exist for this kind of moduli. - #[inline(always)] - const fn modulus_has_one_spare_bit() -> bool { - MODULUS < (1u32 << 31) - 1 - } } impl IsField for U32MontgomeryBackendPrimeField { @@ -91,64 +81,13 @@ impl IsField for U32MontgomeryBackendPrimeField { sum = corr_sum; } sum - - // let (sum, overflow) = a.overflowing_add(*b); - // if Self::MODULUS_HAS_ONE_SPARE_BIT { - // if sum >= MODULUS { - // sum - MODULUS - // } else { - // sum - // } - // } else if overflow || sum >= MODULUS { - // sum - MODULUS - // } else { - // sum - // } } - /* - - fn add(self, rhs: Self) -> Self { - let mut sum = self.value + rhs.value; - let (corr_sum, over) = sum.overflowing_sub(FP::PRIME); - if !over { - sum = corr_sum; - } - Self::new_monty(sum) - } - */ #[inline(always)] - fn mul(lhs: &u32, rhs: &u32) -> u32 { - let mut o64: u64 = (*lhs as u64).wrapping_mul(*rhs as u64); - let low: u32 = 0u32.wrapping_sub(o64 as u32); - let red = &Self::MU.wrapping_mul(low); - o64 = o64.wrapping_add((*red as u64).wrapping_mul(MODULUS as u64)); - let ret = (o64 >> 32) as u32; - if ret >= MODULUS { - ret.wrapping_sub(MODULUS) - } else { - ret - } + fn mul(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { + MontgomeryAlgorithms::mul(a, b, &MODULUS, &Self::MU) } - /* - fn mul(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - // if Self::MODULUS_HAS_ONE_SPARE_BIT { - // MontgomeryAlgorithms::cios_optimized_for_moduli_with_one_spare_bit( - // *a, - // *b, - // MODULUS, - // Self::MU, - // ) - // } else { - MontgomeryAlgorithms::cios(a, b, &MODULUS, &Self::MU) - // } - } - // #[inline(always)] - // fn square(a: &Self::BaseType) -> Self::BaseType { - // MontgomeryAlgorithms::sos_square(*a, MODULUS, &Self::MU) - // } - */ #[inline(always)] fn sub(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { if b <= a { @@ -167,154 +106,43 @@ impl IsField for U32MontgomeryBackendPrimeField { } } + /// Computes multiplicative inverse using Fermat's Little Theorem + /// It states that for any non-zero element a in field F_p: a^(p-1) ≡ 1 (mod p) + /// Therefore: a^(p-2) * a ≡ 1 (mod p), so a^(p-2) is the multiplicative inverse + /// Implementation inspired by Plonky3's work. + /// #[inline(always)] - fn inv(a: &Self::BaseType) -> Result { if *a == Self::ZERO { return Err(FieldError::InvZeroError); } - let p100000000 = MontgomeryAlgorithms::exp_power_of_2(a, 8, &MODULUS); - let p100000001 = Self::mul(&p100000000, a); - let p10000000000000000 = MontgomeryAlgorithms::exp_power_of_2(&p100000000, 8, &MODULUS); - let p10000000100000001 = Self::mul(&p10000000000000000, &p100000001); - let p10000000100000001000 = MontgomeryAlgorithms::exp_power_of_2(&p10000000100000001, 3, &MODULUS); - let p1000000010000000100000000 = MontgomeryAlgorithms::exp_power_of_2(&p10000000100000001000, 5, &MODULUS); - let p1000000010000000100000001 = Self::mul(&p1000000010000000100000000, a); - let p1000010010000100100001001 = Self::mul(&p1000000010000000100000001, &p10000000100000001000); - let p10000000100000001000000010 = Self::square(&p1000000010000000100000001); let p11000010110000101100001011 = Self::mul(&p10000000100000001000000010, &p1000010010000100100001001); - let p100000001000000010000000100 = Self::square(&p10000000100000001000000010); - let p111000011110000111100001111 = Self::mul(&p100000001000000010000000100, &p11000010110000101100001011); - let p1110000111100001111000011110000 = MontgomeryAlgorithms::exp_power_of_2(&p111000011110000111100001111, 4, &MODULUS); - let p1110111111111111111111111111111 = Self::mul( &p1110000111100001111000011110000, &p111000011110000111100001111, ); - Ok(p1110111111111111111111111111111) } - /*fn inv(a: &Self::BaseType) -> Result { - // if a == &Self::ZERO { - // return Err(FieldError::InvZeroError); - // } - - // // From Fermat's little theorem, in a prime field `F_p`, the inverse of `a` is `a^(p-2)`. - // // Here p-2 = 2013265919 = 1110111111111111111111111111111_2. - // // Uses 30 Squares + 7 Multiplications => 37 Operations total. - // let p100000000 = MontgomeryAlgorithms::exp_power_of_2(a, 8, &MODULUS) as u64; - // let p100000001 = p100000000 * a; - // let p10000000000000000 = MontgomeryAlgorithms::exp_power_of_2(&p100000000, 8, &MODULUS); - // let p10000000100000001 = p10000000000000000 * p100000001; - // let p10000000100000001000 = - // MontgomeryAlgorithms::exp_power_of_2(&p10000000100000001, 3, &MODULUS); - // let p1000000010000000100000000 = - // MontgomeryAlgorithms::exp_power_of_2(&p10000000100000001000, 5, &MODULUS); - // let p1000000010000000100000001 = p1000000010000000100000000 * a; - // let p1000010010000100100001001 = p1000000010000000100000001 * p10000000100000001000; - // let p10000000100000001000000010 = p1000000010000000100000001.pow(2); - // let p11000010110000101100001011 = p10000000100000001000000010 * p1000010010000100100001001; - // let p100000001000000010000000100 = p10000000100000001000000010.pow(2); - // let p111000011110000111100001111 = - // p100000001000000010000000100 * p11000010110000101100001011; - // let p1110000111100001111000011110000 = - // MontgomeryAlgorithms::exp_power_of_2(&p111000011110000111100001111, 4, &MODULUS); - // let p1110111111111111111111111111111 = - // p1110000111100001111000011110000 * p111000011110000111100001111; - - // Ok(p1110111111111111111111111111111 as u32) - - if a == &Self::ZERO { - Err(FieldError::InvZeroError) - } else { - // Guajardo Kumar Paar Pelzl - // Efficient Software-Implementation of Finite Fields with Applications to - // Cryptography - // Algorithm 16 (BEA for Inversion in Fp) - - //These can be done with const functions - let modulus_has_spare_bits = MODULUS >> 31 == 0; - - let mut u: u32 = *a; - let mut v = MODULUS; - let mut b = Self::R2; // Avoids unnecessary reduction step. - let mut c = Self::zero(); - - while u != 1 && v != 1 { - while u & 1 == 0 { - u >>= 1; - if b & 1 == 0 { - b >>= 1; - } else { - let carry; - (b, carry) = b.overflowing_add(MODULUS); - b >>= 1; - if !modulus_has_spare_bits && carry { - b |= 1 << 31; - } - } - } - - while v & 1 == 0 { - v >>= 1; - - if c & 1 == 0 { - c >>= 1; - } else { - let carry; - (c, carry) = c.overflowing_add(MODULUS); - c >>= 1; - if !modulus_has_spare_bits && carry { - c |= 1 << 31; - } - } - } - - if v <= u { - u = u - v; - if b < c { - b = MODULUS - c + b; - } else { - b = b - c; - } - } else { - v = v - u; - if c < b { - c = MODULUS - b + c; - } else { - c = c - b; - } - } - } - - if u == 1 { - Ok(b) - } else { - Ok(c) - } - } - } - */ #[inline(always)] fn div(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { Self::mul(a, &Self::inv(b).unwrap()) @@ -338,12 +166,12 @@ impl IsField for U32MontgomeryBackendPrimeField { #[inline(always)] fn from_u64(x: u64) -> Self::BaseType { let x_u32 = x as u32; - MontgomeryAlgorithms::cios(&x_u32, &Self::R2, &MODULUS, &Self::MU) + MontgomeryAlgorithms::mul(&x_u32, &Self::R2, &MODULUS, &Self::MU) } #[inline(always)] fn from_base_type(x: Self::BaseType) -> Self::BaseType { - MontgomeryAlgorithms::cios(&x, &Self::R2, &MODULUS, &Self::MU) + MontgomeryAlgorithms::mul(&x, &Self::R2, &MODULUS, &Self::MU) } } @@ -351,7 +179,7 @@ impl IsPrimeField for U32MontgomeryBackendPrimeField Self::RepresentativeType { - MontgomeryAlgorithms::cios(x, &1u32, &MODULUS, &Self::MU) + MontgomeryAlgorithms::mul(x, &1u32, &MODULUS, &Self::MU) } fn field_bit_size() -> usize { @@ -367,7 +195,6 @@ impl IsPrimeField for U32MontgomeryBackendPrimeField Result { let mut hex_string = hex_string; - // Remove 0x if it's on the string let mut char_iterator = hex_string.chars(); if hex_string.len() > 2 && char_iterator.next().unwrap() == '0' @@ -380,15 +207,6 @@ impl IsPrimeField for U32MontgomeryBackendPrimeField FieldElement> { impl ByteConversion for FieldElement> { #[cfg(feature = "alloc")] fn to_bytes_be(&self) -> alloc::vec::Vec { - MontgomeryAlgorithms::cios( + MontgomeryAlgorithms::mul( self.value(), &1, &MODULUS, @@ -414,7 +232,7 @@ impl ByteConversion for FieldElement alloc::vec::Vec { - MontgomeryAlgorithms::cios( + MontgomeryAlgorithms::mul( self.value(), &1u32, &MODULUS, @@ -453,1002 +271,30 @@ impl From u32 { - let x = *a as u64 * *b as u64; + const fn monty_reduce(x: u64, mu: &u32, q: &u32) -> u32 { let t = x.wrapping_mul(*mu as u64) & (u32::MAX as u64); let u = t * (*q as u64); - let (x_sub_u, over) = x.overflowing_sub(u); let x_sub_u_hi = (x_sub_u >> 32) as u32; let corr = if over { q } else { &0 }; x_sub_u_hi.wrapping_add(*corr) } - pub fn exp_power_of_2(a: &u32, power_log: usize, q: &u32) -> u32 { - let mut res = a.clone(); - for _ in 0..power_log { - res = Self::cios(&res, &res, q, &2281701377); - } - res - } - - /// Compute CIOS multiplication of `a` * `b` - /// This is the Algorithm 2 described in the paper - /// "EdMSM: Multi-Scalar-Multiplication for SNARKs and Faster Montgomery multiplication" - /// https://eprint.iacr.org/2022/1400.pdf. - /// It is only suited for moduli with `q[0]` smaller than `2^63 - 1`. - /// `q` is the modulus - /// `mu` is the inverse of -q modulo 2^{64} -> change this (Juan) #[inline(always)] - pub fn cios_optimized_for_moduli_with_one_spare_bit(a: u32, b: u32, q: u32, mu: u32) -> u32 { - let t: u64 = (a as u64) * (b as u64); - - let m = ((t as u32).wrapping_mul(mu)) as u64; - - let t = t + m * (q as u64); - - let c = t >> 32; - let mut result = c as u32; - - if result >= q { - result = result.wrapping_sub(q); - } - result + pub const fn mul(a: &u32, b: &u32, q: &u32, mu: &u32) -> u32 { + let x = *a as u64 * *b as u64; + Self::monty_reduce(x, mu, q) } - // Separated Operand Scanning Method (2.3.1) - #[inline(always)] - pub fn sos_square(a: u32, q: u32, mu: &u32) -> u32 { - // NOTE: we use explicit `while` loops in this function because profiling pointed - // at iterators of the form `(..).rev()` as the main performance bottleneck. - let t: u64 = (a as u64) * (a as u64); - let m = ((t as u32).wrapping_mul(*mu)) as u64; - let t = t + m * (q as u64); - - let c = t >> 32; - let mut result = c as u32; - - if result >= q { - result = result.wrapping_sub(q); + pub fn exp_power_of_2(a: &u32, power_log: usize, q: &u32) -> u32 { + let mut res = *a; + for _ in 0..power_log { + res = Self::mul(&res, &res, q, &2281701377); } - - result + res } } - -// #[cfg(test)] -// mod tests_u384_prime_fields { -// use crate::field::element::FieldElement; -// use crate::field::errors::FieldError; -// use crate::field::fields::fft_friendly::babybear::Babybear31PrimeField; -// use crate::field::fields::fft_friendly::stark_252_prime_field::Stark252PrimeField; - -// use crate::field::fields::montgomery_backed_prime_fields::{ -// IsModulus, U256PrimeField, U384PrimeField, -// }; -// use crate::field::traits::IsField; -// use crate::field::traits::IsPrimeField; -// #[cfg(feature = "alloc")] -// use crate::traits::ByteConversion; -// use crate::unsigned_integer::element::U384; -// use crate::unsigned_integer::element::{UnsignedInteger, U256}; - -// type F = U384PrimeField; -// type U384F23Element = FieldElement; - -// #[test] -// fn u384_mod_23_uses_5_bits() { -// assert_eq!(U384F23::field_bit_size(), 5); -// } - -// #[test] -// fn stark_252_prime_field_uses_252_bits() { -// assert_eq!(Stark252PrimeField::field_bit_size(), 252); -// } - -// #[test] -// fn u256_mod_2_uses_1_bit() { -// #[derive(Clone, Debug)] -// struct U256Modulus1; -// impl IsModulus for U256Modulus1 { -// const MODULUS: U256 = UnsignedInteger::from_u64(2); -// } -// type U256OneField = U256PrimeField; -// assert_eq!(U256OneField::field_bit_size(), 1); -// } - -// #[test] -// fn u256_with_first_bit_set_uses_256_bit() { -// #[derive(Clone, Debug)] -// struct U256ModulusBig; -// impl IsModulus for U256ModulusBig { -// const MODULUS: U256 = UnsignedInteger::from_hex_unchecked( -// "F0000000F0000000F0000000F0000000F0000000F0000000F0000000F0000000", -// ); -// } -// type U256OneField = U256PrimeField; -// assert_eq!(U256OneField::field_bit_size(), 256); -// } - -// #[test] -// fn montgomery_backend_primefield_compute_r2_parameter() { -// let r2: U384 = UnsignedInteger { -// limbs: [0, 0, 0, 0, 0, 6], -// }; -// assert_eq!(U384F23::R2, r2); -// } - -// #[test] -// fn montgomery_backend_primefield_compute_mu_parameter() { -// assert_eq!(U384F23::MU, 3208129404123400281); -// } - -// #[test] -// fn montgomery_backend_primefield_compute_zero_parameter() { -// let zero: U384 = UnsignedInteger { -// limbs: [0, 0, 0, 0, 0, 0], -// }; -// assert_eq!(U384F23::ZERO, zero); -// } - -// #[test] -// fn montgomery_backend_primefield_from_u64() { -// let a: U384 = UnsignedInteger { -// limbs: [0, 0, 0, 0, 0, 17], -// }; -// assert_eq!(U384F23::from_u64(770_u64), a); -// } - -// #[test] -// fn montgomery_backend_primefield_representative() { -// let a: U384 = UnsignedInteger { -// limbs: [0, 0, 0, 0, 0, 11], -// }; -// assert_eq!(U384F23::representative(&U384F23::from_u64(770_u64)), a); -// } - -// #[test] -// fn montgomery_backend_multiplication_works_0() { -// let x = U384F23Element::from(11_u64); -// let y = U384F23Element::from(10_u64); -// let c = U384F23Element::from(110_u64); -// assert_eq!(x * y, c); -// } - -// #[test] -// #[cfg(feature = "lambdaworks-serde-string")] -// fn montgomery_backend_serialization_deserialization() { -// let x = U384F23Element::from(11_u64); -// let x_serialized = serde_json::to_string(&x).unwrap(); -// let x_deserialized: U384F23Element = serde_json::from_str(&x_serialized).unwrap(); -// // assert_eq!(x_serialized, "{\"value\":\"0xb\"}"); // serialization is no longer as hex string -// assert_eq!(x_deserialized, x); -// } - -// #[test] -// fn doubling() { -// assert_eq!( -// U384F23Element::from(2).double(), -// U384F23Element::from(2) + U384F23Element::from(2), -// ); -// } - -// const ORDER: usize = 23; -// #[test] -// fn two_plus_one_is_three() { -// assert_eq!( -// U384F23Element::from(2) + U384F23Element::from(1), -// U384F23Element::from(3) -// ); -// } - -// #[test] -// fn max_order_plus_1_is_0() { -// assert_eq!( -// U384F23Element::from((ORDER - 1) as u64) + U384F23Element::from(1), -// U384F23Element::from(0) -// ); -// } - -// #[test] -// fn when_comparing_13_and_13_they_are_equal() { -// let a: U384F23Element = U384F23Element::from(13); -// let b: U384F23Element = U384F23Element::from(13); -// assert_eq!(a, b); -// } - -// #[test] -// fn when_comparing_13_and_8_they_are_different() { -// let a: U384F23Element = U384F23Element::from(13); -// let b: U384F23Element = U384F23Element::from(8); -// assert_ne!(a, b); -// } - -// #[test] -// fn mul_neutral_element() { -// let a: U384F23Element = U384F23Element::from(1); -// let b: U384F23Element = U384F23Element::from(2); -// assert_eq!(a * b, U384F23Element::from(2)); -// } - -// #[test] -// fn mul_2_3_is_6() { -// let a: U384F23Element = U384F23Element::from(2); -// let b: U384F23Element = U384F23Element::from(3); -// assert_eq!(a * b, U384F23Element::from(6)); -// } - -// #[test] -// fn mul_order_minus_1() { -// let a: U384F23Element = U384F23Element::from((ORDER - 1) as u64); -// let b: U384F23Element = U384F23Element::from((ORDER - 1) as u64); -// assert_eq!(a * b, U384F23Element::from(1)); -// } - -// #[test] -// fn inv_0_error() { -// let result = U384F23Element::from(0).inv(); -// assert!(matches!(result, Err(FieldError::InvZeroError))) -// } - -// #[test] -// fn inv_2() { -// let a: U384F23Element = U384F23Element::from(2); -// assert_eq!(&a * a.inv().unwrap(), U384F23Element::from(1)); -// } - -// #[test] -// fn pow_2_3() { -// assert_eq!(U384F23Element::from(2).pow(3_u64), U384F23Element::from(8)) -// } - -// #[test] -// fn pow_p_minus_1() { -// assert_eq!( -// U384F23Element::from(2).pow(ORDER - 1), -// U384F23Element::from(1) -// ) -// } - -// #[test] -// fn div_1() { -// assert_eq!( -// U384F23Element::from(2) / U384F23Element::from(1), -// U384F23Element::from(2) -// ) -// } - -// #[test] -// fn div_4_2() { -// assert_eq!( -// U384F23Element::from(4) / U384F23Element::from(2), -// U384F23Element::from(2) -// ) -// } - -// #[test] -// fn three_inverse() { -// let a = U384F23Element::from(3); -// let expected = U384F23Element::from(8); -// assert_eq!(a.inv().unwrap(), expected) -// } - -// #[test] -// fn div_4_3() { -// assert_eq!( -// U384F23Element::from(4) / U384F23Element::from(3) * U384F23Element::from(3), -// U384F23Element::from(4) -// ) -// } - -// #[test] -// fn two_plus_its_additive_inv_is_0() { -// let two = U384F23Element::from(2); - -// assert_eq!(&two + (-&two), U384F23Element::from(0)) -// } - -// #[test] -// fn four_minus_three_is_1() { -// let four = U384F23Element::from(4); -// let three = U384F23Element::from(3); - -// assert_eq!(four - three, U384F23Element::from(1)) -// } - -// #[test] -// fn zero_minus_1_is_order_minus_1() { -// let zero = U384F23Element::from(0); -// let one = U384F23Element::from(1); - -// assert_eq!(zero - one, U384F23Element::from((ORDER - 1) as u64)) -// } - -// #[test] -// fn neg_zero_is_zero() { -// let zero = U384F23Element::from(0); - -// assert_eq!(-&zero, zero); -// } - -// // FP1 -// #[derive(Clone, Debug)] -// struct U384ModulusP1; -// impl IsModulus for U384ModulusP1 { -// const MODULUS: U384 = UnsignedInteger { -// limbs: [ -// 0, -// 0, -// 0, -// 3450888597, -// 5754816256417943771, -// 15923941673896418529, -// ], -// }; -// } - -// type U384FP1 = U384PrimeField; -// type U384FP1Element = FieldElement; - -// #[test] -// fn montgomery_prime_field_from_bad_hex_errs() { -// assert!(U384FP1Element::from_hex("0xTEST").is_err()); -// } - -// #[test] -// fn montgomery_prime_field_addition_works_0() { -// let x = U384FP1Element::new(UnsignedInteger::from_hex_unchecked( -// "05ed176deb0e80b4deb7718cdaa075165f149c", -// )); -// let y = U384FP1Element::new(UnsignedInteger::from_hex_unchecked( -// "5f103b0bd4397d4df560eb559f38353f80eeb6", -// )); -// let c = U384FP1Element::new(UnsignedInteger::from_hex_unchecked( -// "64fd5279bf47fe02d4185ce279d8aa55e00352", -// )); -// assert_eq!(x + y, c); -// } - -// #[test] -// fn montgomery_prime_field_multiplication_works_0() { -// let x = U384FP1Element::new(UnsignedInteger::from_hex_unchecked( -// "05ed176deb0e80b4deb7718cdaa075165f149c", -// )); -// let y = U384FP1Element::new(UnsignedInteger::from_hex_unchecked( -// "5f103b0bd4397d4df560eb559f38353f80eeb6", -// )); -// let c = U384FP1Element::new(UnsignedInteger::from_hex_unchecked( -// "73d23e8d462060dc23d5c15c00fc432d95621a3c", -// )); -// assert_eq!(x * y, c); -// } - -// // FP2 -// #[derive(Clone, Debug)] -// struct U384ModulusP2; -// impl IsModulus for U384ModulusP2 { -// const MODULUS: U384 = UnsignedInteger { -// limbs: [ -// 18446744073709551615, -// 18446744073709551615, -// 18446744073709551615, -// 18446744073709551615, -// 18446744073709551615, -// 18446744073709551275, -// ], -// }; -// } - -// type U384FP2 = U384PrimeField; -// type U384FP2Element = FieldElement; - -// #[test] -// fn montgomery_prime_field_addition_works_1() { -// let x = U384FP2Element::new(UnsignedInteger::from_hex_unchecked( -// "05ed176deb0e80b4deb7718cdaa075165f149c", -// )); -// let y = U384FP2Element::new(UnsignedInteger::from_hex_unchecked( -// "5f103b0bd4397d4df560eb559f38353f80eeb6", -// )); -// let c = U384FP2Element::new(UnsignedInteger::from_hex_unchecked( -// "64fd5279bf47fe02d4185ce279d8aa55e00352", -// )); -// assert_eq!(x + y, c); -// } - -// #[test] -// fn montgomery_prime_field_multiplication_works_1() { -// let x = U384FP2Element::one(); -// let y = U384FP2Element::new(UnsignedInteger::from_hex_unchecked( -// "5f103b0bd4397d4df560eb559f38353f80eeb6", -// )); -// assert_eq!(&y * x, y); -// } - -// #[test] -// #[cfg(feature = "alloc")] -// fn to_bytes_from_bytes_be_is_the_identity() { -// let x = U384FP2Element::new(UnsignedInteger::from_hex_unchecked( -// "5f103b0bd4397d4df560eb559f38353f80eeb6", -// )); -// assert_eq!(U384FP2Element::from_bytes_be(&x.to_bytes_be()).unwrap(), x); -// } - -// #[test] -// #[cfg(feature = "alloc")] -// fn from_bytes_to_bytes_be_is_the_identity_for_one() { -// let bytes = [ -// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -// ]; -// assert_eq!( -// U384FP2Element::from_bytes_be(&bytes).unwrap().to_bytes_be(), -// bytes -// ); -// } - -// #[test] -// #[cfg(feature = "alloc")] -// fn to_bytes_from_bytes_le_is_the_identity() { -// let x = U384FP2Element::new(UnsignedInteger::from_hex_unchecked( -// "5f103b0bd4397d4df560eb559f38353f80eeb6", -// )); -// assert_eq!(U384FP2Element::from_bytes_le(&x.to_bytes_le()).unwrap(), x); -// } - -// #[test] -// #[cfg(feature = "alloc")] -// fn from_bytes_to_bytes_le_is_the_identity_for_one() { -// let bytes = [ -// 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -// ]; -// assert_eq!( -// U384FP2Element::from_bytes_le(&bytes).unwrap().to_bytes_le(), -// bytes -// ); -// } -// } - -// #[cfg(test)] -// mod tests_u256_prime_fields { -// use crate::field::element::FieldElement; -// use crate::field::errors::FieldError; -// use crate::field::fields::montgomery_backed_prime_fields::{IsModulus, U256PrimeField}; -// use crate::field::traits::IsField; -// use crate::field::traits::IsPrimeField; -// #[cfg(feature = "alloc")] -// use crate::traits::ByteConversion; -// use crate::unsigned_integer::element::U256; -// use crate::unsigned_integer::element::{UnsignedInteger, U64}; -// use proptest::prelude::*; - -// use super::U64PrimeField; - -// #[derive(Clone, Debug)] -// struct U256Modulus29; -// impl IsModulus for U256Modulus29 { -// const MODULUS: U256 = UnsignedInteger::from_u64(29); -// } - -// type U256F29 = U256PrimeField; -// type U256F29Element = FieldElement; - -// #[test] -// fn montgomery_backend_primefield_compute_r2_parameter() { -// let r2: U256 = UnsignedInteger { -// limbs: [0, 0, 0, 24], -// }; -// assert_eq!(U256F29::R2, r2); -// } - -// #[test] -// fn montgomery_backend_primefield_compute_mu_parameter() { -// // modular multiplicative inverse -// assert_eq!(U256F29::MU, 14630176334321368523); -// } - -// #[test] -// fn montgomery_backend_primefield_compute_zero_parameter() { -// let zero: U256 = UnsignedInteger { -// limbs: [0, 0, 0, 0], -// }; -// assert_eq!(U256F29::ZERO, zero); -// } - -// #[test] -// fn montgomery_backend_primefield_from_u64() { -// // (770*2**(256))%29 -// let a: U256 = UnsignedInteger { -// limbs: [0, 0, 0, 24], -// }; -// assert_eq!(U256F29::from_u64(770_u64), a); -// } - -// #[test] -// fn montgomery_backend_primefield_representative() { -// // 770%29 -// let a: U256 = UnsignedInteger { -// limbs: [0, 0, 0, 16], -// }; -// assert_eq!(U256F29::representative(&U256F29::from_u64(770_u64)), a); -// } - -// #[test] -// fn montgomery_backend_multiplication_works_0() { -// let x = U256F29Element::from(11_u64); -// let y = U256F29Element::from(10_u64); -// let c = U256F29Element::from(110_u64); -// assert_eq!(x * y, c); -// } - -// #[test] -// fn doubling() { -// assert_eq!( -// U256F29Element::from(2).double(), -// U256F29Element::from(2) + U256F29Element::from(2), -// ); -// } - -// const ORDER: usize = 29; -// #[test] -// fn two_plus_one_is_three() { -// assert_eq!( -// U256F29Element::from(2) + U256F29Element::from(1), -// U256F29Element::from(3) -// ); -// } - -// #[test] -// fn max_order_plus_1_is_0() { -// assert_eq!( -// U256F29Element::from((ORDER - 1) as u64) + U256F29Element::from(1), -// U256F29Element::from(0) -// ); -// } - -// #[test] -// fn when_comparing_13_and_13_they_are_equal() { -// let a: U256F29Element = U256F29Element::from(13); -// let b: U256F29Element = U256F29Element::from(13); -// assert_eq!(a, b); -// } - -// #[test] -// fn when_comparing_13_and_8_they_are_different() { -// let a: U256F29Element = U256F29Element::from(13); -// let b: U256F29Element = U256F29Element::from(8); -// assert_ne!(a, b); -// } - -// #[test] -// fn mul_neutral_element() { -// let a: U256F29Element = U256F29Element::from(1); -// let b: U256F29Element = U256F29Element::from(2); -// assert_eq!(a * b, U256F29Element::from(2)); -// } - -// #[test] -// fn mul_2_3_is_6() { -// let a: U256F29Element = U256F29Element::from(2); -// let b: U256F29Element = U256F29Element::from(3); -// assert_eq!(a * b, U256F29Element::from(6)); -// } - -// #[test] -// fn mul_order_minus_1() { -// let a: U256F29Element = U256F29Element::from((ORDER - 1) as u64); -// let b: U256F29Element = U256F29Element::from((ORDER - 1) as u64); -// assert_eq!(a * b, U256F29Element::from(1)); -// } - -// #[test] -// fn inv_0_error() { -// let result = U256F29Element::from(0).inv(); -// assert!(matches!(result, Err(FieldError::InvZeroError))); -// } - -// #[test] -// fn inv_2() { -// let a: U256F29Element = U256F29Element::from(2); -// assert_eq!(&a * a.inv().unwrap(), U256F29Element::from(1)); -// } - -// #[test] -// fn pow_2_3() { -// assert_eq!(U256F29Element::from(2).pow(3_u64), U256F29Element::from(8)) -// } - -// #[test] -// fn pow_p_minus_1() { -// assert_eq!( -// U256F29Element::from(2).pow(ORDER - 1), -// U256F29Element::from(1) -// ) -// } - -// #[test] -// fn div_1() { -// assert_eq!( -// U256F29Element::from(2) / U256F29Element::from(1), -// U256F29Element::from(2) -// ) -// } - -// #[test] -// fn div_4_2() { -// let a = U256F29Element::from(4); -// let b = U256F29Element::from(2); -// assert_eq!(a / &b, b) -// } - -// #[test] -// fn div_4_3() { -// assert_eq!( -// U256F29Element::from(4) / U256F29Element::from(3) * U256F29Element::from(3), -// U256F29Element::from(4) -// ) -// } - -// #[test] -// fn two_plus_its_additive_inv_is_0() { -// let two = U256F29Element::from(2); - -// assert_eq!(&two + (-&two), U256F29Element::from(0)) -// } - -// #[test] -// fn four_minus_three_is_1() { -// let four = U256F29Element::from(4); -// let three = U256F29Element::from(3); - -// assert_eq!(four - three, U256F29Element::from(1)) -// } - -// #[test] -// fn zero_minus_1_is_order_minus_1() { -// let zero = U256F29Element::from(0); -// let one = U256F29Element::from(1); - -// assert_eq!(zero - one, U256F29Element::from((ORDER - 1) as u64)) -// } - -// #[test] -// fn neg_zero_is_zero() { -// let zero = U256F29Element::from(0); - -// assert_eq!(-&zero, zero); -// } - -// // FP1 -// #[derive(Clone, Debug)] -// struct U256ModulusP1; -// impl IsModulus for U256ModulusP1 { -// const MODULUS: U256 = UnsignedInteger { -// limbs: [ -// 8366, -// 8155137382671976874, -// 227688614771682406, -// 15723111795979912613, -// ], -// }; -// } - -// type U256FP1 = U256PrimeField; -// type U256FP1Element = FieldElement; - -// #[test] -// fn montgomery_prime_field_addition_works_0() { -// let x = U256FP1Element::new(UnsignedInteger::from_hex_unchecked( -// "93e712950bf3fe589aa030562a44b1cec66b09192c4bcf705a5", -// )); -// let y = U256FP1Element::new(UnsignedInteger::from_hex_unchecked( -// "10a712235c1f6b4172a1e35da6aef1a7ec6b09192c4bb88cfa5", -// )); -// let c = U256FP1Element::new(UnsignedInteger::from_hex_unchecked( -// "a48e24b86813699a0d4213b3d0f3a376b2d61232589787fd54a", -// )); -// assert_eq!(x + y, c); -// } - -// #[test] -// fn montgomery_prime_field_multiplication_works_0() { -// let x = U256FP1Element::new(UnsignedInteger::from_hex_unchecked( -// "93e712950bf3fe589aa030562a44b1cec66b09192c4bcf705a5", -// )); -// let y = U256FP1Element::new(UnsignedInteger::from_hex_unchecked( -// "10a712235c1f6b4172a1e35da6aef1a7ec6b09192c4bb88cfa5", -// )); -// let c = U256FP1Element::new(UnsignedInteger::from_hex_unchecked( -// "7808e74c3208d9a66791ef9cc15a46acc9951ee312102684021", -// )); -// assert_eq!(x * y, c); -// } - -// // FP2 -// #[derive(Clone, Debug)] -// struct ModulusP2; -// impl IsModulus for ModulusP2 { -// const MODULUS: U256 = UnsignedInteger { -// limbs: [ -// 18446744073709551615, -// 18446744073709551615, -// 18446744073709551615, -// 18446744073709551427, -// ], -// }; -// } - -// type FP2 = U256PrimeField; -// type FP2Element = FieldElement; - -// #[test] -// fn montgomery_prime_field_addition_works_1() { -// let x = FP2Element::new(UnsignedInteger::from_hex_unchecked( -// "acbbb7ca01c65cfffffc72815b397fff9ab130ad53a5ffffffb8f21b207dfedf", -// )); -// let y = FP2Element::new(UnsignedInteger::from_hex_unchecked( -// "d65ddbe509d3fffff21f494c588cbdbfe43e929b0543e3ffffffffffffffff43", -// )); -// let c = FP2Element::new(UnsignedInteger::from_hex_unchecked( -// "831993af0b9a5cfff21bbbcdb3c63dbf7eefc34858e9e3ffffb8f21b207dfedf", -// )); -// assert_eq!(x + y, c); -// } - -// #[test] -// fn montgomery_prime_field_multiplication_works_1() { -// let x = FP2Element::new(UnsignedInteger::from_hex_unchecked( -// "acbbb7ca01c65cfffffc72815b397fff9ab130ad53a5ffffffb8f21b207dfedf", -// )); -// let y = FP2Element::new(UnsignedInteger::from_hex_unchecked( -// "d65ddbe509d3fffff21f494c588cbdbfe43e929b0543e3ffffffffffffffff43", -// )); -// let c = FP2Element::new(UnsignedInteger::from_hex_unchecked( -// "2b1e80d553ecab2e4d41eb53c4c8ad89ebacac6cf6b91dcf2213f311093aa05d", -// )); -// assert_eq!(&y * x, c); -// } - -// #[test] -// #[cfg(feature = "alloc")] -// fn to_bytes_from_bytes_be_is_the_identity() { -// let x = FP2Element::new(UnsignedInteger::from_hex_unchecked( -// "5f103b0bd4397d4df560eb559f38353f80eeb6", -// )); -// assert_eq!(FP2Element::from_bytes_be(&x.to_bytes_be()).unwrap(), x); -// } - -// #[test] -// #[cfg(feature = "alloc")] -// fn from_bytes_to_bytes_be_is_the_identity_for_one() { -// let bytes = [ -// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -// 0, 0, 1, -// ]; -// assert_eq!( -// FP2Element::from_bytes_be(&bytes).unwrap().to_bytes_be(), -// bytes -// ); -// } - -// #[test] -// #[cfg(feature = "alloc")] -// fn to_bytes_from_bytes_le_is_the_identity() { -// let x = FP2Element::new(UnsignedInteger::from_hex_unchecked( -// "5f103b0bd4397d4df560eb559f38353f80eeb6", -// )); -// assert_eq!(FP2Element::from_bytes_le(&x.to_bytes_le()).unwrap(), x); -// } - -// #[test] -// #[cfg(feature = "alloc")] -// fn from_bytes_to_bytes_le_is_the_identity_for_one() { -// let bytes = [ -// 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -// 0, 0, 0, -// ]; -// assert_eq!( -// FP2Element::from_bytes_le(&bytes).unwrap().to_bytes_le(), -// bytes -// ); -// } - -// #[test] -// #[cfg(feature = "alloc")] -// fn creating_a_field_element_from_its_representative_returns_the_same_element_1() { -// let change = U256::from_u64(1); -// let f1 = U256FP1Element::new(U256ModulusP1::MODULUS + change); -// let f2 = U256FP1Element::new(f1.representative()); -// assert_eq!(f1, f2); -// } - -// #[test] -// fn creating_a_field_element_from_its_representative_returns_the_same_element_2() { -// let change = U256::from_u64(27); -// let f1 = U256F29Element::new(U256Modulus29::MODULUS + change); -// let f2 = U256F29Element::new(f1.representative()); -// assert_eq!(f1, f2); -// } - -// #[test] -// fn creating_a_field_element_from_hex_works_1() { -// let a = U256FP1Element::from_hex_unchecked("eb235f6144d9e91f4b14"); -// let b = U256FP1Element::new(U256 { -// limbs: [0, 0, 60195, 6872850209053821716], -// }); -// assert_eq!(a, b); -// } - -// #[test] -// fn creating_a_field_element_from_hex_too_big_errors() { -// let a = U256FP1Element::from_hex(&"f".repeat(65)); -// assert!(a.is_err()); -// assert_eq!( -// a.unwrap_err(), -// crate::errors::CreationError::HexStringIsTooBig -// ) -// } - -// #[test] -// fn creating_a_field_element_from_hex_works_on_the_size_limit() { -// let a = U256FP1Element::from_hex(&"f".repeat(64)); -// assert!(a.is_ok()); -// } - -// #[test] -// fn creating_a_field_element_from_hex_works_2() { -// let a = U256F29Element::from_hex_unchecked("aa"); -// let b = U256F29Element::from(25); -// assert_eq!(a, b); -// } - -// #[test] -// fn creating_a_field_element_from_hex_works_3() { -// let a = U256F29Element::from_hex_unchecked("1d"); -// let b = U256F29Element::zero(); -// assert_eq!(a, b); -// } - -// #[cfg(feature = "std")] -// #[test] -// fn to_hex_test_works_1() { -// let a = U256FP1Element::from_hex_unchecked("eb235f6144d9e91f4b14"); -// let b = U256FP1Element::new(U256 { -// limbs: [0, 0, 60195, 6872850209053821716], -// }); - -// assert_eq!(U256FP1Element::to_hex(&a), U256FP1Element::to_hex(&b)); -// } - -// #[cfg(feature = "std")] -// #[test] -// fn to_hex_test_works_2() { -// let a = U256F29Element::from_hex_unchecked("1d"); -// let b = U256F29Element::zero(); - -// assert_eq!(U256F29Element::to_hex(&a), U256F29Element::to_hex(&b)); -// } - -// #[cfg(feature = "std")] -// #[test] -// fn to_hex_test_works_3() { -// let a = U256F29Element::from_hex_unchecked("aa"); -// let b = U256F29Element::from(25); - -// assert_eq!(U256F29Element::to_hex(&a), U256F29Element::to_hex(&b)); -// } - -// // Goldilocks -// #[derive(Clone, Debug)] -// struct GoldilocksModulus; -// impl IsModulus for GoldilocksModulus { -// const MODULUS: U64 = UnsignedInteger { -// limbs: [18446744069414584321], -// }; -// } - -// type GoldilocksField = U64PrimeField; -// type GoldilocksElement = FieldElement; - -// #[derive(Clone, Debug)] -// struct SecpModulus; -// impl IsModulus for SecpModulus { -// const MODULUS: U256 = UnsignedInteger::from_hex_unchecked( -// "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", -// ); -// } -// type SecpMontField = U256PrimeField; -// type SecpMontElement = FieldElement; - -// #[test] -// fn secp256k1_minus_three_pow_2_is_9_with_all_operations() { -// let minus_3 = -SecpMontElement::from_hex_unchecked("0x3"); -// let minus_3_mul_minus_3 = &minus_3 * &minus_3; -// let minus_3_squared = minus_3.square(); -// let minus_3_pow_2 = minus_3.pow(2_u32); -// let nine = SecpMontElement::from_hex_unchecked("0x9"); - -// assert_eq!(minus_3_mul_minus_3, nine); -// assert_eq!(minus_3_squared, nine); -// assert_eq!(minus_3_pow_2, nine); -// } - -// #[test] -// fn secp256k1_inv_works() { -// let a = SecpMontElement::from_hex_unchecked("0x456"); -// let a_inv = a.inv().unwrap(); - -// assert_eq!(a * a_inv, SecpMontElement::one()); -// } - -// #[test] -// fn test_cios_overflow_case() { -// let a = GoldilocksElement::from(732582227915286439); -// let b = GoldilocksElement::from(3906369333256140342); -// let expected_sum = GoldilocksElement::from(4638951561171426781); -// assert_eq!(a + b, expected_sum); -// } - -// // Tests for the Montgomery Algorithms -// proptest! { -// #[test] -// fn cios_vs_cios_optimized(a in any::<[u64; 6]>(), b in any::<[u64; 6]>()) { -// let x = U384::from_limbs(a); -// let y = U384::from_limbs(b); -// let m = U384::from_hex_unchecked("cdb061954fdd36e5176f50dbdcfd349570a29ce1"); // this is prime -// let mu: u64 = 16085280245840369887; // negative of the inverse of `m` modulo 2^{64} -// assert_eq!( -// MontgomeryAlgorithms::cios(&x, &y, &m, &mu), -// MontgomeryAlgorithms::cios_optimized_for_moduli_with_one_spare_bit(&x, &y, &m, &mu) -// ); -// } - -// #[test] -// fn cios_vs_sos_square(a in any::<[u64; 6]>()) { -// let x = U384::from_limbs(a); -// let m = U384::from_hex_unchecked("cdb061954fdd36e5176f50dbdcfd349570a29ce1"); // this is prime -// let mu: u64 = 16085280245840369887; // negative of the inverse of `m` modulo 2^{64} -// assert_eq!( -// MontgomeryAlgorithms::cios(&x, &x, &m, &mu), -// MontgomeryAlgorithms::sos_square(&x, &m, &mu) -// ); -// } -// } -// #[test] -// fn montgomery_multiplication_works_0() { -// let x = U384::from_u64(11_u64); -// let y = U384::from_u64(10_u64); -// let m = U384::from_u64(23_u64); // -// let mu: u64 = 3208129404123400281; // negative of the inverse of `m` modulo 2^{64}. -// let c = U384::from_u64(13_u64); // x * y * (r^{-1}) % m, where r = 2^{64 * 6} and r^{-1} mod m = 2. -// assert_eq!(MontgomeryAlgorithms::cios(&x, &y, &m, &mu), c); -// } - -// #[test] -// fn montgomery_multiplication_works_1() { -// let x = U384::from_hex_unchecked("05ed176deb0e80b4deb7718cdaa075165f149c"); -// let y = U384::from_hex_unchecked("5f103b0bd4397d4df560eb559f38353f80eeb6"); -// let m = U384::from_hex_unchecked("cdb061954fdd36e5176f50dbdcfd349570a29ce1"); // this is prime -// let mu: u64 = 16085280245840369887; // negative of the inverse of `m` modulo 2^{64} -// let c = U384::from_hex_unchecked("8d65cdee621682815d59f465d2641eea8a1274dc"); // x * y * (r^{-1}) % m, where r = 2^{64 * 6} -// assert_eq!(MontgomeryAlgorithms::cios(&x, &y, &m, &mu), c); -// } - -// #[test] -// fn montgomery_multiplication_works_2() { -// let x = U384::from_hex_unchecked("8d65cdee621682815d59f465d2641eea8a1274dc"); -// let m = U384::from_hex_unchecked("cdb061954fdd36e5176f50dbdcfd349570a29ce1"); // this is prime -// let r_mod_m = U384::from_hex_unchecked("58dfb0e1b3dd5e674bdcde4f42eb5533b8759d33"); -// let mu: u64 = 16085280245840369887; // negative of the inverse of `m` modulo 2^{64} -// let c = U384::from_hex_unchecked("8d65cdee621682815d59f465d2641eea8a1274dc"); -// assert_eq!(MontgomeryAlgorithms::cios(&x, &r_mod_m, &m, &mu), c); -// } -// } From 982b31c381cb6ad1e78d5b041cce770ba60216cc Mon Sep 17 00:00:00 2001 From: Nicole Date: Wed, 4 Dec 2024 15:01:38 -0300 Subject: [PATCH 10/18] tests big hex and more than 4 bytes failing --- .../field/fields/fft_friendly/babybear_u32.rs | 122 ++++++++++++++---- 1 file changed, 96 insertions(+), 26 deletions(-) diff --git a/math/src/field/fields/fft_friendly/babybear_u32.rs b/math/src/field/fields/fft_friendly/babybear_u32.rs index 8fd56349e..f2a5db9bf 100644 --- a/math/src/field/fields/fft_friendly/babybear_u32.rs +++ b/math/src/field/fields/fft_friendly/babybear_u32.rs @@ -23,10 +23,14 @@ impl IsFFTField for Babybear31PrimeField { mod tests { use super::*; - mod test_babybear_31_bytes_ops { + mod test_babybear_31_ops { use super::*; use crate::{ - field::{element::FieldElement, errors::FieldError}, + field::{ + element::FieldElement, + errors::FieldError, + traits::{IsField, IsPrimeField}, + }, traits::ByteConversion, }; type FE = FieldElement; @@ -70,7 +74,7 @@ mod tests { const ORDER: usize = 2013265921; #[test] - fn max_order_pis_0() { + fn order_is_0() { assert_eq!(FE::from((ORDER - 1) as u64) + FE::from(1), FE::from(0)); } @@ -116,9 +120,9 @@ mod tests { } #[test] - fn inv_2() { + fn inv_2_mul_2_is_1() { let a: FE = FE::from(2); - assert_eq!(a * a.inv().unwrap(), FE::from(1)); + assert_eq!(&a * a.inv().unwrap(), FE::from(1)); } #[test] @@ -127,7 +131,7 @@ mod tests { } #[test] - fn pow_2_3() { + fn pow_2_3_is_8() { assert_eq!(FE::from(2).pow(3_u64), FE::from(8)) } @@ -146,22 +150,11 @@ mod tests { assert_eq!(FE::from(4) / FE::from(2), FE::from(2)) } - #[test] - fn three_inverse_mul_three_is_one() { - let a = FE::from(3); - assert_eq!(a.inv().unwrap() * a, FE::one()) - } - - #[test] - fn div_4_3() { - assert_eq!(FE::from(4) / FE::from(3) * FE::from(3), FE::from(4)) - } - #[test] fn two_plus_its_additive_inv_is_0() { let two = FE::from(2); - assert_eq!(two + (-&two), FE::from(0)) + assert_eq!(&two + (-&two), FE::from(0)) } #[test] @@ -180,11 +173,89 @@ mod tests { assert_eq!(zero - one, FE::from((ORDER - 1) as u64)) } + #[test] + fn babybear_uses_31_bits() { + assert_eq!(Babybear31PrimeField::field_bit_size(), 31); + } + + #[test] + fn montgomery_backend_prime_field_compute_mu_parameter() { + let mu_expected: u32 = 2281701377; + assert_eq!(Babybear31PrimeField::MU, mu_expected); + } + + #[test] + fn montgomery_backend_prime_field_compute_r2_parameter() { + let r2_expected: u32 = 1172168163; + assert_eq!(Babybear31PrimeField::R2, r2_expected); + } + + #[test] + #[cfg(feature = "alloc")] + fn to_bytes_from_bytes_be_is_the_identity_big_hex() { + let x = FE::from_hex("5f103b0bd4397d4df560eb559f38353f80eeb6").unwrap(); + assert_eq!(FE::from_bytes_be(&x.to_bytes_be()).unwrap(), x); + } + + #[test] + #[cfg(feature = "alloc")] + fn to_bytes_from_bytes_be_is_the_identity_small_hex() { + let x = FE::from_hex("5f103b").unwrap(); + assert_eq!(FE::from_bytes_be(&x.to_bytes_be()).unwrap(), x); + } + + #[test] + #[cfg(feature = "alloc")] + fn from_bytes_to_bytes_be_is_the_identity_48_bytes() { + let bytes = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + ]; + assert_eq!(FE::from_bytes_be(&bytes).unwrap().to_bytes_be(), bytes); + } + + #[test] + #[cfg(feature = "alloc")] + fn from_bytes_to_bytes_be_is_the_identity_4_bytes() { + let bytes = [0, 0, 0, 1]; + assert_eq!(FE::from_bytes_be(&bytes).unwrap().to_bytes_be(), bytes); + } + + #[test] + #[cfg(feature = "alloc")] + fn to_bytes_from_bytes_le_is_the_identity_big_hex() { + let x = FE::from_hex("5f103b0bd4397d4df560eb559f38353f80eeb6").unwrap(); + assert_eq!(FE::from_bytes_le(&x.to_bytes_le()).unwrap(), x); + } + + #[test] + #[cfg(feature = "alloc")] + fn to_bytes_from_bytes_le_is_the_identity_small_hex() { + let x = FE::from_hex("5f103b").unwrap(); + assert_eq!(FE::from_bytes_le(&x.to_bytes_le()).unwrap(), x); + } + + #[test] + #[cfg(feature = "alloc")] + fn from_bytes_to_bytes_le_is_the_identity_48_bytes() { + let bytes = [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + assert_eq!(FE::from_bytes_le(&bytes).unwrap().to_bytes_le(), bytes); + } + + #[test] + #[cfg(feature = "alloc")] + fn from_bytes_to_bytes_le_is_the_identity_4_bytes() { + let bytes = [1, 0, 0, 0]; + assert_eq!(FE::from_bytes_le(&bytes).unwrap().to_bytes_le(), bytes); + } + #[test] #[cfg(feature = "alloc")] fn byte_serialization_for_a_number_matches_with_byte_conversion_implementation_le() { - let element = - FieldElement::::from_hex("0123456701234567").unwrap(); + let element = FE::from_hex("0123456701234567").unwrap(); let bytes = element.to_bytes_le(); let expected_bytes: [u8; 4] = ByteConversion::to_bytes_le(&element).try_into().unwrap(); assert_eq!(bytes, expected_bytes); @@ -193,8 +264,7 @@ mod tests { #[test] #[cfg(feature = "alloc")] fn byte_serialization_for_a_number_matches_with_byte_conversion_implementation_be() { - let element = FieldElement::::from_hex("01234567").unwrap(); - println!("ELEMENT: {:?}", element); + let element = FE::from_hex("0123456701234567").unwrap(); let bytes = element.to_bytes_be(); let expected_bytes: [u8; 4] = ByteConversion::to_bytes_be(&element).try_into().unwrap(); assert_eq!(bytes, expected_bytes); @@ -202,17 +272,17 @@ mod tests { #[test] fn byte_serialization_and_deserialization_works_le() { - let element = FieldElement::::from_hex("0x76543210").unwrap(); + let element = FE::from_hex("0x7654321076543210").unwrap(); let bytes = element.to_bytes_le(); - let from_bytes = FieldElement::::from_bytes_le(&bytes).unwrap(); + let from_bytes = FE::from_bytes_le(&bytes).unwrap(); assert_eq!(element, from_bytes); } #[test] fn byte_serialization_and_deserialization_works_be() { - let element = FieldElement::::from_hex("76543210").unwrap(); + let element = FE::from_hex("7654321076543210").unwrap(); let bytes = element.to_bytes_be(); - let from_bytes = FieldElement::::from_bytes_be(&bytes).unwrap(); + let from_bytes = FE::from_bytes_be(&bytes).unwrap(); assert_eq!(element, from_bytes); } } From 65ae39c89a198c7e0a82b7f666444d533cbc61a5 Mon Sep 17 00:00:00 2001 From: jotabulacios Date: Wed, 4 Dec 2024 15:39:56 -0300 Subject: [PATCH 11/18] add fuzzer for babybear --- fuzz/no_gpu_fuzz/Cargo.toml | 10 ++- .../fuzz_targets/field/babybear.rs | 81 +++++++++++++++++++ 2 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 fuzz/no_gpu_fuzz/fuzz_targets/field/babybear.rs diff --git a/fuzz/no_gpu_fuzz/Cargo.toml b/fuzz/no_gpu_fuzz/Cargo.toml index 265ecbdd0..0032023f7 100644 --- a/fuzz/no_gpu_fuzz/Cargo.toml +++ b/fuzz/no_gpu_fuzz/Cargo.toml @@ -16,6 +16,8 @@ num-traits = "0.2" ibig = "0.3.6" p3-goldilocks = { git = "https://github.com/Plonky3/Plonky3", rev = "41cd843" } p3-mersenne-31 = { git = "https://github.com/Plonky3/Plonky3", rev = "41cd843" } +p3-field = { git = "https://github.com/Plonky3/Plonky3" } +p3-baby-bear = { git = "https://github.com/Plonky3/Plonky3" } [[bin]] name = "curve_bls12_381" @@ -53,6 +55,12 @@ path = "fuzz_targets/field/mersenne31.rs" test = false doc = false +[[bin]] +name = "babybear" +path = "fuzz_targets/field/babybear.rs" +test = false +doc = false + [[bin]] name = "mini_goldilocks" path = "fuzz_targets/field/mini_goldilocks.rs" @@ -83,5 +91,3 @@ name = "deserialize_stark_proof" path = "fuzz_targets/deserialize_stark_proof.rs" test = false doc = false - - diff --git a/fuzz/no_gpu_fuzz/fuzz_targets/field/babybear.rs b/fuzz/no_gpu_fuzz/fuzz_targets/field/babybear.rs new file mode 100644 index 000000000..51c349a27 --- /dev/null +++ b/fuzz/no_gpu_fuzz/fuzz_targets/field/babybear.rs @@ -0,0 +1,81 @@ +#![no_main] + +use lambdaworks_math::field::{ + element::FieldElement, + fields::u32_montgomery_backend_prime_field::U32MontgomeryBackendPrimeField, +}; +use libfuzzer_sys::fuzz_target; +use p3_baby_bear::BabyBear; +use p3_field::{Field, FieldAlgebra, PrimeField32}; + +pub type U32Babybear31PrimeField = U32MontgomeryBackendPrimeField<2013265921>; +pub type F = FieldElement; + +fuzz_target!(|values: (u32, u32)| { + // Note: we filter values outside of order as it triggers an assert within plonky3 disallowing values n >= Self::Order + + let (value_u32_a, value_u32_b) = values; + + if value_u32_a >= 2013265921 || value_u32_b >= 2013265921 { + // Ignorar casos fuera del rango del campo + return; + } + let a = F::from(value_u32_a as u64); + let b = F::from(value_u32_b as u64); + + // Note: if we parse using from_canonical_u32 fails due to check that n < Self::Order + let a_expected = BabyBear::from_canonical_u32(value_u32_a); + let b_expected = BabyBear::from_canonical_u32(value_u32_b); + + let add_u32 = &a + &b; + let addition = a_expected + b_expected; + assert_eq!(add_u32.representative(), addition.as_canonical_u32()); + + let sub_u32 = &a - &b; + let substraction = a_expected - b_expected; + assert_eq!(sub_u32.representative(), substraction.as_canonical_u32()); + + let mul_u32 = &a * &b; + let multiplication = a_expected * b_expected; + assert_eq!(mul_u32.representative(), multiplication.as_canonical_u32()); + + // Axioms soundness + let one = F::one(); + let zero = F::zero(); + + assert_eq!(&a + &zero, a, "Neutral add element a failed"); + assert_eq!(&b + &zero, b, "Neutral mul element b failed"); + assert_eq!(&a * &one, a, "Neutral add element a failed"); + assert_eq!(&b * &one, b, "Neutral mul element b failed"); + + assert_eq!(&a + &b, &b + &a, "Commutative add property failed"); + assert_eq!(&a * &b, &b * &a, "Commutative mul property failed"); + + let c = &a * &b; + assert_eq!( + (&a + &b) + &c, + &a + (&b + &c), + "Associative add property failed" + ); + assert_eq!( + (&a * &b) * &c, + &a * (&b * &c), + "Associative mul property failed" + ); + + assert_eq!( + &a * (&b + &c), + &a * &b + &a * &c, + "Distributive property failed" + ); + + assert_eq!(&a - &a, zero, "Inverse add a failed"); + assert_eq!(&b - &b, zero, "Inverse add b failed"); + + if a != zero { + assert_eq!(&a * a.inv().unwrap(), one, "Inverse mul a failed"); + } + if b != zero { + assert_eq!(&b * b.inv().unwrap(), one, "Inverse mul b failed"); + } +}); From 3ec7cb0472be5adab04baf6acefc790bfba72650 Mon Sep 17 00:00:00 2001 From: Nicole Date: Thu, 5 Dec 2024 11:46:37 -0300 Subject: [PATCH 12/18] fix tests from_hex for numbers bigger than u64 --- .../field/fields/fft_friendly/babybear_u32.rs | 58 +++++-------------- 1 file changed, 16 insertions(+), 42 deletions(-) diff --git a/math/src/field/fields/fft_friendly/babybear_u32.rs b/math/src/field/fields/fft_friendly/babybear_u32.rs index f2a5db9bf..e89738747 100644 --- a/math/src/field/fields/fft_friendly/babybear_u32.rs +++ b/math/src/field/fields/fft_friendly/babybear_u32.rs @@ -2,12 +2,16 @@ use crate::field::{ fields::u32_montgomery_backend_prime_field::U32MontgomeryBackendPrimeField, traits::IsFFTField, }; +// Babybear Prime p = 2^31 - 2^27 + 1 = 0x78000001 = 2013265921 pub type Babybear31PrimeField = U32MontgomeryBackendPrimeField<2013265921>; -//a two-adic primitive root of unity is 21^(2^24) -// 21^(2^24)=1 mod 2013265921 -// 2^27(2^4-1)+1 where n=27 (two-adicity) and k=2^4+1 -//In the future we should allow this with metal and cuda feature, and just dispatch it to the CPU until the implementation is done +// p = 2^31 - 2^27 + 1 = 2^27 * (2^4-1) + 1, then +// there is a gruop in the field of order 2^27. +// Since we want to have margin to be able to define a bigger group (blow-up group), +// we define TWO_ADICITY as 24 (so the blow-up factor can be 2^3 = 8). +// A two-adic primitive root of unity is 21^(2^24) because +// 21^(2^24)=1 mod 2013265921. +// In the future we should allow this with metal and cuda feature, and just dispatch it to the CPU until the implementation is done #[cfg(any(not(feature = "metal"), not(feature = "cuda")))] impl IsFFTField for Babybear31PrimeField { const TWO_ADICITY: u64 = 24; @@ -26,11 +30,8 @@ mod tests { mod test_babybear_31_ops { use super::*; use crate::{ - field::{ - element::FieldElement, - errors::FieldError, - traits::{IsField, IsPrimeField}, - }, + errors::CreationError, + field::{element::FieldElement, errors::FieldError, traits::IsPrimeField}, traits::ByteConversion, }; type FE = FieldElement; @@ -192,59 +193,32 @@ mod tests { #[test] #[cfg(feature = "alloc")] - fn to_bytes_from_bytes_be_is_the_identity_big_hex() { - let x = FE::from_hex("5f103b0bd4397d4df560eb559f38353f80eeb6").unwrap(); - assert_eq!(FE::from_bytes_be(&x.to_bytes_be()).unwrap(), x); + fn from_hex_bigger_than_u64_returns_error() { + let x = FE::from_hex("5f103b0bd4397d4df560eb559f38353f80eeb6"); + assert!(matches!(x, Err(CreationError::InvalidHexString))) } #[test] #[cfg(feature = "alloc")] - fn to_bytes_from_bytes_be_is_the_identity_small_hex() { + fn to_bytes_from_bytes_be_is_the_identity() { let x = FE::from_hex("5f103b").unwrap(); assert_eq!(FE::from_bytes_be(&x.to_bytes_be()).unwrap(), x); } #[test] #[cfg(feature = "alloc")] - fn from_bytes_to_bytes_be_is_the_identity_48_bytes() { - let bytes = [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - ]; - assert_eq!(FE::from_bytes_be(&bytes).unwrap().to_bytes_be(), bytes); - } - - #[test] - #[cfg(feature = "alloc")] - fn from_bytes_to_bytes_be_is_the_identity_4_bytes() { + fn from_bytes_to_bytes_be_is_the_identity() { let bytes = [0, 0, 0, 1]; assert_eq!(FE::from_bytes_be(&bytes).unwrap().to_bytes_be(), bytes); } #[test] #[cfg(feature = "alloc")] - fn to_bytes_from_bytes_le_is_the_identity_big_hex() { - let x = FE::from_hex("5f103b0bd4397d4df560eb559f38353f80eeb6").unwrap(); - assert_eq!(FE::from_bytes_le(&x.to_bytes_le()).unwrap(), x); - } - - #[test] - #[cfg(feature = "alloc")] - fn to_bytes_from_bytes_le_is_the_identity_small_hex() { + fn to_bytes_from_bytes_le_is_the_identity() { let x = FE::from_hex("5f103b").unwrap(); assert_eq!(FE::from_bytes_le(&x.to_bytes_le()).unwrap(), x); } - #[test] - #[cfg(feature = "alloc")] - fn from_bytes_to_bytes_le_is_the_identity_48_bytes() { - let bytes = [ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ]; - assert_eq!(FE::from_bytes_le(&bytes).unwrap().to_bytes_le(), bytes); - } - #[test] #[cfg(feature = "alloc")] fn from_bytes_to_bytes_le_is_the_identity_4_bytes() { From 4a7d70ed9f75c116baf6d87f2db23935d76e1b29 Mon Sep 17 00:00:00 2001 From: jotabulacios Date: Thu, 5 Dec 2024 12:15:15 -0300 Subject: [PATCH 13/18] remove comments and refactor some functions --- .../fuzz_targets/field/babybear.rs | 2 - .../u32_montgomery_backend_prime_field.rs | 52 +++++++------------ 2 files changed, 19 insertions(+), 35 deletions(-) diff --git a/fuzz/no_gpu_fuzz/fuzz_targets/field/babybear.rs b/fuzz/no_gpu_fuzz/fuzz_targets/field/babybear.rs index 51c349a27..9483781e3 100644 --- a/fuzz/no_gpu_fuzz/fuzz_targets/field/babybear.rs +++ b/fuzz/no_gpu_fuzz/fuzz_targets/field/babybear.rs @@ -13,11 +13,9 @@ pub type F = FieldElement; fuzz_target!(|values: (u32, u32)| { // Note: we filter values outside of order as it triggers an assert within plonky3 disallowing values n >= Self::Order - let (value_u32_a, value_u32_b) = values; if value_u32_a >= 2013265921 || value_u32_b >= 2013265921 { - // Ignorar casos fuera del rango del campo return; } let a = F::from(value_u32_a as u64); diff --git a/math/src/field/fields/u32_montgomery_backend_prime_field.rs b/math/src/field/fields/u32_montgomery_backend_prime_field.rs index 75377e4d6..3e1eb3885 100644 --- a/math/src/field/fields/u32_montgomery_backend_prime_field.rs +++ b/math/src/field/fields/u32_montgomery_backend_prime_field.rs @@ -116,14 +116,15 @@ impl IsField for U32MontgomeryBackendPrimeField { if *a == Self::ZERO { return Err(FieldError::InvZeroError); } - let p100000000 = MontgomeryAlgorithms::exp_power_of_2(a, 8, &MODULUS); + let p100000000 = MontgomeryAlgorithms::exp_power_of_2(a, 8, &MODULUS, &Self::MU); let p100000001 = Self::mul(&p100000000, a); - let p10000000000000000 = MontgomeryAlgorithms::exp_power_of_2(&p100000000, 8, &MODULUS); + let p10000000000000000 = + MontgomeryAlgorithms::exp_power_of_2(&p100000000, 8, &MODULUS, &Self::MU); let p10000000100000001 = Self::mul(&p10000000000000000, &p100000001); let p10000000100000001000 = - MontgomeryAlgorithms::exp_power_of_2(&p10000000100000001, 3, &MODULUS); + MontgomeryAlgorithms::exp_power_of_2(&p10000000100000001, 3, &MODULUS, &Self::MU); let p1000000010000000100000000 = - MontgomeryAlgorithms::exp_power_of_2(&p10000000100000001000, 5, &MODULUS); + MontgomeryAlgorithms::exp_power_of_2(&p10000000100000001000, 5, &MODULUS, &Self::MU); let p1000000010000000100000001 = Self::mul(&p1000000010000000100000000, a); let p1000010010000100100001001 = Self::mul(&p1000000010000000100000001, &p10000000100000001000); @@ -134,8 +135,12 @@ impl IsField for U32MontgomeryBackendPrimeField { let p100000001000000010000000100 = Self::square(&p10000000100000001000000010); let p111000011110000111100001111 = Self::mul(&p100000001000000010000000100, &p11000010110000101100001011); - let p1110000111100001111000011110000 = - MontgomeryAlgorithms::exp_power_of_2(&p111000011110000111100001111, 4, &MODULUS); + let p1110000111100001111000011110000 = MontgomeryAlgorithms::exp_power_of_2( + &p111000011110000111100001111, + 4, + &MODULUS, + &Self::MU, + ); let p1110111111111111111111111111111 = Self::mul( &p1110000111100001111000011110000, &p111000011110000111100001111, @@ -183,30 +188,15 @@ impl IsPrimeField for U32MontgomeryBackendPrimeField usize { - let mut evaluated_bit = 32 - 1; - let max_element = &MODULUS - 1; - - while ((max_element >> evaluated_bit) & 1) != 1 { - evaluated_bit -= 1; - } - - evaluated_bit + 1 + 32 - (MODULUS - 1).leading_zeros() as usize } - fn from_hex(hex_string: &str) -> Result { - let mut hex_string = hex_string; - let mut char_iterator = hex_string.chars(); - if hex_string.len() > 2 - && char_iterator.next().unwrap() == '0' - && char_iterator.next().unwrap() == 'x' - { - hex_string = &hex_string[2..]; - } - let value = - u64::from_str_radix(hex_string, 16).map_err(|_| CreationError::InvalidHexString)?; + fn from_hex(hex_string: &str) -> Result { + let hex = hex_string.strip_prefix("0x").unwrap_or(hex_string); - let reduced_value = (value % MODULUS as u64) as u32; - Ok(reduced_value) + u64::from_str_radix(hex, 16) + .map_err(|_| CreationError::InvalidHexString) + .map(|value| ((value % MODULUS as u64) as u32)) } #[cfg(feature = "std")] @@ -290,11 +280,7 @@ impl MontgomeryAlgorithms { Self::monty_reduce(x, mu, q) } - pub fn exp_power_of_2(a: &u32, power_log: usize, q: &u32) -> u32 { - let mut res = *a; - for _ in 0..power_log { - res = Self::mul(&res, &res, q, &2281701377); - } - res + pub fn exp_power_of_2(a: &u32, power_log: usize, q: &u32, mu: &u32) -> u32 { + (0..power_log).fold(*a, |res, _| Self::mul(&res, &res, q, mu)) } } From d0fe76e354405fa4c98424a15ead9ac1ece1d46c Mon Sep 17 00:00:00 2001 From: jotabulacios Date: Thu, 5 Dec 2024 12:42:57 -0300 Subject: [PATCH 14/18] fix cargo clippy --- math/benches/criterion_field.rs | 3 ++- math/benches/fields/baby_bear.rs | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/math/benches/criterion_field.rs b/math/benches/criterion_field.rs index e790f38cd..e7eac6fb2 100644 --- a/math/benches/criterion_field.rs +++ b/math/benches/criterion_field.rs @@ -14,6 +14,7 @@ use fields::{ criterion_group!( name = field_benches; config = Criterion::default().with_profiler(PProfProfiler::new(100, Output::Flamegraph(None))); - targets =babybear_ops_benchmarks,babybear_ops_benchmarks_f64, babybear_p3_ops_benchmarks + targets =babybear_ops_benchmarks,babybear_ops_benchmarks_f64, babybear_p3_ops_benchmarks,mersenne31_extension_ops_benchmarks,mersenne31_ops_benchmarks, + starkfield_ops_benchmarks,u64_goldilocks_ops_benchmarks,u64_goldilocks_montgomery_ops_benchmarks,mersenne31_mont_ops_benchmarks ); criterion_main!(field_benches); diff --git a/math/benches/fields/baby_bear.rs b/math/benches/fields/baby_bear.rs index f01770b9a..647426db3 100644 --- a/math/benches/fields/baby_bear.rs +++ b/math/benches/fields/baby_bear.rs @@ -10,7 +10,6 @@ use lambdaworks_math::field::{ }; use p3_baby_bear::BabyBear; -use p3_field::extension::BinomialExtensionField; use p3_field::{Field, FieldAlgebra}; use rand::random; From 0045ec8192717c31015f6dc7f16eb2daa588b4a8 Mon Sep 17 00:00:00 2001 From: jotabulacios Date: Thu, 5 Dec 2024 12:58:15 -0300 Subject: [PATCH 15/18] fix clippy --- math/src/field/fields/fft_friendly/babybear_u32.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/math/src/field/fields/fft_friendly/babybear_u32.rs b/math/src/field/fields/fft_friendly/babybear_u32.rs index e89738747..a7ff2954e 100644 --- a/math/src/field/fields/fft_friendly/babybear_u32.rs +++ b/math/src/field/fields/fft_friendly/babybear_u32.rs @@ -123,7 +123,7 @@ mod tests { #[test] fn inv_2_mul_2_is_1() { let a: FE = FE::from(2); - assert_eq!(&a * a.inv().unwrap(), FE::from(1)); + assert_eq!(a * a.inv().unwrap(), FE::from(1)); } #[test] @@ -155,7 +155,7 @@ mod tests { fn two_plus_its_additive_inv_is_0() { let two = FE::from(2); - assert_eq!(&two + (-&two), FE::from(0)) + assert_eq!(two + (-&two), FE::from(0)) } #[test] From 64eaacff7d360f1e49e59dff48bee43f4c35d991 Mon Sep 17 00:00:00 2001 From: jotabulacios Date: Thu, 5 Dec 2024 14:54:46 -0300 Subject: [PATCH 16/18] fix clippy metal --- math/src/field/fields/fft_friendly/babybear_u32.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/math/src/field/fields/fft_friendly/babybear_u32.rs b/math/src/field/fields/fft_friendly/babybear_u32.rs index a7ff2954e..5338674c6 100644 --- a/math/src/field/fields/fft_friendly/babybear_u32.rs +++ b/math/src/field/fields/fft_friendly/babybear_u32.rs @@ -26,7 +26,6 @@ impl IsFFTField for Babybear31PrimeField { #[cfg(test)] mod tests { use super::*; - mod test_babybear_31_ops { use super::*; use crate::{ @@ -268,7 +267,6 @@ mod tests { use crate::fft::cpu::roots_of_unity::{ get_powers_of_primitive_root, get_powers_of_primitive_root_coset, }; - #[cfg(not(any(feature = "metal", feature = "cuda")))] use crate::field::element::FieldElement; #[cfg(not(any(feature = "metal", feature = "cuda")))] use crate::field::traits::{IsFFTField, RootsConfig}; From 32ed5e385ac1f9fe3cbc7c76f2b61a46f25271a3 Mon Sep 17 00:00:00 2001 From: jotabulacios Date: Fri, 20 Dec 2024 16:53:55 -0300 Subject: [PATCH 17/18] rename function --- math/src/field/fields/u32_montgomery_backend_prime_field.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/math/src/field/fields/u32_montgomery_backend_prime_field.rs b/math/src/field/fields/u32_montgomery_backend_prime_field.rs index 3e1eb3885..90afc96bb 100644 --- a/math/src/field/fields/u32_montgomery_backend_prime_field.rs +++ b/math/src/field/fields/u32_montgomery_backend_prime_field.rs @@ -265,7 +265,7 @@ impl MontgomeryAlgorithms { /// /// Converts a value from Montgomery domain using reductions mod p #[inline(always)] - const fn monty_reduce(x: u64, mu: &u32, q: &u32) -> u32 { + const fn montgomery_reduction(x: u64, mu: &u32, q: &u32) -> u32 { let t = x.wrapping_mul(*mu as u64) & (u32::MAX as u64); let u = t * (*q as u64); let (x_sub_u, over) = x.overflowing_sub(u); @@ -277,7 +277,7 @@ impl MontgomeryAlgorithms { #[inline(always)] pub const fn mul(a: &u32, b: &u32, q: &u32, mu: &u32) -> u32 { let x = *a as u64 * *b as u64; - Self::monty_reduce(x, mu, q) + Self::montgomery_reduction(x, mu, q) } pub fn exp_power_of_2(a: &u32, power_log: usize, q: &u32, mu: &u32) -> u32 { From 06fe23d0014c6147f6fb896fdb34a9140366204f Mon Sep 17 00:00:00 2001 From: jotabulacios Date: Mon, 6 Jan 2025 11:05:58 -0300 Subject: [PATCH 18/18] Fix overflow in shift ops --- .../u32_montgomery_backend_prime_field.rs | 57 ++++++++++++------- 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/math/src/field/fields/u32_montgomery_backend_prime_field.rs b/math/src/field/fields/u32_montgomery_backend_prime_field.rs index 90afc96bb..f06fd363f 100644 --- a/math/src/field/fields/u32_montgomery_backend_prime_field.rs +++ b/math/src/field/fields/u32_montgomery_backend_prime_field.rs @@ -19,32 +19,36 @@ use core::fmt::Debug; pub struct U32MontgomeryBackendPrimeField; impl U32MontgomeryBackendPrimeField { - pub const R2: u32 = Self::compute_r2_parameter(); - pub const MU: u32 = Self::compute_mu_parameter(); + pub const R2: u32 = match Self::compute_r2_parameter() { + Ok(value) => value, + Err(_) => panic!("Failed to compute R2 parameter"), + }; + pub const MU: u32 = match Self::compute_mu_parameter() { + Ok(value) => value, + Err(_) => panic!("Failed to compute MU parameter"), + }; pub const ZERO: u32 = 0; pub const ONE: u32 = MontgomeryAlgorithms::mul(&1, &Self::R2, &MODULUS, &Self::MU); - // Computes `modulus^{-1} mod 2^{32}` - // Algorithm adapted from `compute_mu_parameter()` from `montgomery_backed_prime_fields.rs` in Lambdaworks. - // E.g, in Baby Bear field MU = 2281701377. - const fn compute_mu_parameter() -> u32 { + const fn compute_mu_parameter() -> Result { let mut y = 1; let word_size = 32; let mut i: usize = 2; while i <= word_size { let mul_result = (MODULUS as u64 * y as u64) as u32; if (mul_result << (word_size - i)) >> (word_size - i) != 1 { - y += 1 << (i - 1); + let (shifted, overflowed) = 1u32.overflowing_shl((i - 1) as u32); + if overflowed { + return Err("Overflow occurred while computing mu parameter"); + } + y += shifted; } i += 1; } - y + Ok(y) } - // Computes `2^{2 * 32} mod modulus`. - // Algorithm adapted from `compute_r2_parameter()` from `montgomery_backed_prime_fields.rs` in Lambdaworks. - // E.g, in Baby Bear field R2 = 1172168163. - const fn compute_r2_parameter() -> u32 { + const fn compute_r2_parameter() -> Result { let word_size = 32; let mut l: usize = 0; @@ -52,13 +56,20 @@ impl U32MontgomeryBackendPrimeField { while l < word_size && (MODULUS >> l) == 0 { l += 1; } - let mut c: u32 = 1 << l; + let (initial_shifted, overflowed) = 1u32.overflowing_shl(l as u32); + if overflowed { + return Err("Overflow occurred during initial shift in compute_r2_parameter"); + } + let mut c: u32 = initial_shifted; // Double c and reduce modulo `MODULUS` until getting // `2^{2 * word_size}` mod `MODULUS`. let mut i: usize = 1; while i <= 2 * word_size - l { - let double_c = c << 1; + let (double_c, overflowed) = c.overflowing_shl(1); + if overflowed { + return Err("Overflow occurred while doubling in compute_r2_parameter"); + } c = if double_c >= MODULUS { double_c - MODULUS } else { @@ -66,7 +77,7 @@ impl U32MontgomeryBackendPrimeField { }; i += 1; } - c + Ok(c) } } @@ -261,22 +272,28 @@ impl From u32 { let t = x.wrapping_mul(*mu as u64) & (u32::MAX as u64); let u = t * (*q as u64); let (x_sub_u, over) = x.overflowing_sub(u); - let x_sub_u_hi = (x_sub_u >> 32) as u32; + let x_sub_u_bytes = x_sub_u.to_be_bytes(); + // We take the four most significant bytes of `x_sub_u` and convert them into an u32. + let x_sub_u_hi = u32::from_be_bytes([ + x_sub_u_bytes[0], + x_sub_u_bytes[1], + x_sub_u_bytes[2], + x_sub_u_bytes[3], + ]); let corr = if over { q } else { &0 }; x_sub_u_hi.wrapping_add(*corr) } #[inline(always)] pub const fn mul(a: &u32, b: &u32, q: &u32, mu: &u32) -> u32 { - let x = *a as u64 * *b as u64; + let x = (*a as u64) * (*b as u64); Self::montgomery_reduction(x, mu, q) }