lambdaworks_math/field/fields/fft_friendly/
babybear_u32.rs1use crate::field::{
2 fields::u32_montgomery_backend_prime_field::U32MontgomeryBackendPrimeField, traits::IsFFTField,
3};
4
5pub type Babybear31PrimeField = U32MontgomeryBackendPrimeField<2013265921>;
7
8#[cfg(not(feature = "cuda"))]
16impl IsFFTField for Babybear31PrimeField {
17 const TWO_ADICITY: u64 = 24;
18
19 const TWO_ADIC_PRIMITVE_ROOT_OF_UNITY: Self::BaseType = 21;
20
21 fn field_name() -> &'static str {
22 "babybear31"
23 }
24}
25
26#[cfg(test)]
27mod tests {
28 use super::*;
29 mod test_babybear_31_ops {
30 use super::*;
31 use crate::{
32 errors::CreationError,
33 field::{element::FieldElement, errors::FieldError, traits::IsPrimeField},
34 traits::ByteConversion,
35 };
36 type FE = FieldElement<Babybear31PrimeField>;
37
38 #[test]
39 fn two_plus_one_is_three() {
40 let a = FE::from(2);
41 let b = FE::one();
42 let res = FE::from(3);
43
44 assert_eq!(a + b, res)
45 }
46
47 #[test]
48 fn one_minus_two_is_minus_one() {
49 let a = FE::from(2);
50 let b = FE::one();
51 let res = FE::from(2013265920);
52 assert_eq!(b - a, res)
53 }
54
55 #[test]
56 fn mul_by_zero_is_zero() {
57 let a = FE::from(2);
58 let b = FE::zero();
59 assert_eq!(a * b, b)
60 }
61
62 #[test]
63 fn neg_zero_is_zero() {
64 let zero = FE::from(0);
65
66 assert_eq!(-&zero, zero);
67 }
68
69 #[test]
70 fn doubling() {
71 assert_eq!(FE::from(2).double(), FE::from(2) + FE::from(2),);
72 }
73
74 const ORDER: usize = 2013265921;
75
76 #[test]
77 fn order_is_0() {
78 assert_eq!(FE::from((ORDER - 1) as u64) + FE::from(1), FE::from(0));
79 }
80
81 #[test]
82 fn when_comparing_13_and_13_they_are_equal() {
83 let a: FE = FE::from(13);
84 let b: FE = FE::from(13);
85 assert_eq!(a, b);
86 }
87
88 #[test]
89 fn when_comparing_13_and_8_they_are_different() {
90 let a: FE = FE::from(13);
91 let b: FE = FE::from(8);
92 assert_ne!(a, b);
93 }
94
95 #[test]
96 fn mul_neutral_element() {
97 let a: FE = FE::from(1);
98 let b: FE = FE::from(2);
99 assert_eq!(a * b, FE::from(2));
100 }
101
102 #[test]
103 fn mul_2_3_is_6() {
104 let a: FE = FE::from(2);
105 let b: FE = FE::from(3);
106 assert_eq!(a * b, FE::from(6));
107 }
108
109 #[test]
110 fn mul_order_minus_1() {
111 let a: FE = FE::from((ORDER - 1) as u64);
112 let b: FE = FE::from((ORDER - 1) as u64);
113 assert_eq!(a * b, FE::from(1));
114 }
115
116 #[test]
117 fn inv_0_error() {
118 let result = FE::from(0).inv();
119 assert!(matches!(result, Err(FieldError::InvZeroError)))
120 }
121
122 #[test]
123 fn inv_2_mul_2_is_1() {
124 let a: FE = FE::from(2);
125 assert_eq!(a * a.inv().unwrap(), FE::from(1));
126 }
127
128 #[test]
129 fn square_2_is_4() {
130 assert_eq!(FE::from(2).square(), FE::from(4))
131 }
132
133 #[test]
134 fn pow_2_3_is_8() {
135 assert_eq!(FE::from(2).pow(3_u64), FE::from(8))
136 }
137
138 #[test]
139 fn pow_p_minus_1() {
140 assert_eq!(FE::from(2).pow(ORDER - 1), FE::from(1))
141 }
142
143 #[test]
144 fn div_1() {
145 assert_eq!((FE::from(2) / FE::from(1)).unwrap(), FE::from(2))
146 }
147
148 #[test]
149 fn div_4_2() {
150 assert_eq!((FE::from(4) / FE::from(2)).unwrap(), FE::from(2))
151 }
152
153 #[test]
154 fn two_plus_its_additive_inv_is_0() {
155 let two = FE::from(2);
156
157 assert_eq!(two + (-&two), FE::from(0))
158 }
159
160 #[test]
161 fn four_minus_three_is_1() {
162 let four = FE::from(4);
163 let three = FE::from(3);
164
165 assert_eq!(four - three, FE::from(1))
166 }
167
168 #[test]
169 fn zero_minus_1_is_order_minus_1() {
170 let zero = FE::from(0);
171 let one = FE::from(1);
172
173 assert_eq!(zero - one, FE::from((ORDER - 1) as u64))
174 }
175
176 #[test]
177 fn babybear_uses_31_bits() {
178 assert_eq!(Babybear31PrimeField::field_bit_size(), 31);
179 }
180
181 #[test]
182 fn montgomery_backend_prime_field_compute_mu_parameter() {
183 let mu_expected: u32 = 2281701377;
184 assert_eq!(Babybear31PrimeField::MU, mu_expected);
185 }
186
187 #[test]
188 fn montgomery_backend_prime_field_compute_r2_parameter() {
189 let r2_expected: u32 = 1172168163;
190 assert_eq!(Babybear31PrimeField::R2, r2_expected);
191 }
192
193 #[test]
194 #[cfg(feature = "alloc")]
195 fn from_hex_bigger_than_u64_returns_error() {
196 let x = FE::from_hex("5f103b0bd4397d4df560eb559f38353f80eeb6");
197 assert!(matches!(x, Err(CreationError::InvalidHexString)))
198 }
199
200 #[test]
201 #[cfg(feature = "alloc")]
202 fn to_bytes_from_bytes_be_is_the_identity() {
203 let x = FE::from_hex("5f103b").unwrap();
204 assert_eq!(FE::from_bytes_be(&x.to_bytes_be()).unwrap(), x);
205 }
206
207 #[test]
208 #[cfg(feature = "alloc")]
209 fn from_bytes_to_bytes_be_is_the_identity() {
210 let bytes = [0, 0, 0, 1];
211 assert_eq!(FE::from_bytes_be(&bytes).unwrap().to_bytes_be(), bytes);
212 }
213
214 #[test]
215 #[cfg(feature = "alloc")]
216 fn to_bytes_from_bytes_le_is_the_identity() {
217 let x = FE::from_hex("5f103b").unwrap();
218 assert_eq!(FE::from_bytes_le(&x.to_bytes_le()).unwrap(), x);
219 }
220
221 #[test]
222 #[cfg(feature = "alloc")]
223 fn from_bytes_to_bytes_le_is_the_identity_4_bytes() {
224 let bytes = [1, 0, 0, 0];
225 assert_eq!(FE::from_bytes_le(&bytes).unwrap().to_bytes_le(), bytes);
226 }
227
228 #[test]
229 #[cfg(feature = "alloc")]
230 fn byte_serialization_for_a_number_matches_with_byte_conversion_implementation_le() {
231 let element = FE::from_hex("0123456701234567").unwrap();
232 let bytes = element.to_bytes_le();
233 let expected_bytes: [u8; 4] = ByteConversion::to_bytes_le(&element).try_into().unwrap();
234 assert_eq!(bytes, expected_bytes);
235 }
236
237 #[test]
238 #[cfg(feature = "alloc")]
239 fn byte_serialization_for_a_number_matches_with_byte_conversion_implementation_be() {
240 let element = FE::from_hex("0123456701234567").unwrap();
241 let bytes = element.to_bytes_be();
242 let expected_bytes: [u8; 4] = ByteConversion::to_bytes_be(&element).try_into().unwrap();
243 assert_eq!(bytes, expected_bytes);
244 }
245
246 #[test]
247 fn byte_serialization_and_deserialization_works_le() {
248 let element = FE::from_hex("0x7654321076543210").unwrap();
249 let bytes = element.to_bytes_le();
250 let from_bytes = FE::from_bytes_le(&bytes).unwrap();
251 assert_eq!(element, from_bytes);
252 }
253
254 #[test]
255 fn byte_serialization_and_deserialization_works_be() {
256 let element = FE::from_hex("7654321076543210").unwrap();
257 let bytes = element.to_bytes_be();
258 let from_bytes = FE::from_bytes_be(&bytes).unwrap();
259 assert_eq!(element, from_bytes);
260 }
261 }
262
263 #[cfg(all(feature = "std", not(feature = "instruments")))]
264 mod test_babybear_31_fft {
265 use super::*;
266 #[cfg(not(feature = "cuda"))]
267 use crate::fft::cpu::roots_of_unity::{
268 get_powers_of_primitive_root, get_powers_of_primitive_root_coset,
269 };
270 use crate::field::element::FieldElement;
271 #[cfg(not(feature = "cuda"))]
272 use crate::field::traits::{IsFFTField, RootsConfig};
273 use crate::polynomial::Polynomial;
274 use proptest::{collection, prelude::*, std_facade::Vec};
275
276 #[cfg(not(feature = "cuda"))]
277 fn gen_fft_and_naive_evaluation<F: IsFFTField>(
278 poly: Polynomial<FieldElement<F>>,
279 ) -> (Vec<FieldElement<F>>, Vec<FieldElement<F>>) {
280 let len = poly.coeff_len().next_power_of_two();
281 let order = len.trailing_zeros();
282 let twiddles =
283 get_powers_of_primitive_root(order.into(), len, RootsConfig::Natural).unwrap();
284
285 let fft_eval = Polynomial::evaluate_fft::<F>(&poly, 1, None).unwrap();
286 let naive_eval = poly.evaluate_slice(&twiddles);
287
288 (fft_eval, naive_eval)
289 }
290
291 #[cfg(not(feature = "cuda"))]
292 fn gen_fft_coset_and_naive_evaluation<F: IsFFTField>(
293 poly: Polynomial<FieldElement<F>>,
294 offset: FieldElement<F>,
295 blowup_factor: usize,
296 ) -> (Vec<FieldElement<F>>, Vec<FieldElement<F>>) {
297 let len = poly.coeff_len().next_power_of_two();
298 let order = (len * blowup_factor).trailing_zeros();
299 let twiddles =
300 get_powers_of_primitive_root_coset(order.into(), len * blowup_factor, &offset)
301 .unwrap();
302
303 let fft_eval =
304 Polynomial::evaluate_offset_fft::<F>(&poly, blowup_factor, None, &offset).unwrap();
305 let naive_eval = poly.evaluate_slice(&twiddles);
306
307 (fft_eval, naive_eval)
308 }
309
310 #[cfg(not(feature = "cuda"))]
311 fn gen_fft_and_naive_interpolate<F: IsFFTField>(
312 fft_evals: &[FieldElement<F>],
313 ) -> (Polynomial<FieldElement<F>>, Polynomial<FieldElement<F>>) {
314 let order = fft_evals.len().trailing_zeros() as u64;
315 let twiddles =
316 get_powers_of_primitive_root(order, 1 << order, RootsConfig::Natural).unwrap();
317
318 let naive_poly = Polynomial::interpolate(&twiddles, fft_evals).unwrap();
319 let fft_poly = Polynomial::interpolate_fft::<F>(fft_evals).unwrap();
320
321 (fft_poly, naive_poly)
322 }
323
324 #[cfg(not(feature = "cuda"))]
325 fn gen_fft_and_naive_coset_interpolate<F: IsFFTField>(
326 fft_evals: &[FieldElement<F>],
327 offset: &FieldElement<F>,
328 ) -> (Polynomial<FieldElement<F>>, Polynomial<FieldElement<F>>) {
329 let order = fft_evals.len().trailing_zeros() as u64;
330 let twiddles = get_powers_of_primitive_root_coset(order, 1 << order, offset).unwrap();
331
332 let naive_poly = Polynomial::interpolate(&twiddles, fft_evals).unwrap();
333 let fft_poly = Polynomial::interpolate_offset_fft(fft_evals, offset).unwrap();
334
335 (fft_poly, naive_poly)
336 }
337
338 #[cfg(not(feature = "cuda"))]
339 fn gen_fft_interpolate_and_evaluate<F: IsFFTField>(
340 poly: Polynomial<FieldElement<F>>,
341 ) -> (Polynomial<FieldElement<F>>, Polynomial<FieldElement<F>>) {
342 let eval = Polynomial::evaluate_fft::<F>(&poly, 1, None).unwrap();
343 let new_poly = Polynomial::interpolate_fft::<F>(&eval).unwrap();
344
345 (poly, new_poly)
346 }
347
348 prop_compose! {
349 fn powers_of_two(max_exp: u8)(exp in 1..max_exp) -> usize { 1 << exp }
350 }
353 prop_compose! {
354 fn field_element()(num in any::<u64>().prop_filter("Avoid null coefficients", |x| x != &0)) -> FieldElement<Babybear31PrimeField> {
355 FieldElement::<Babybear31PrimeField>::from(num)
356 }
357 }
358 prop_compose! {
359 fn offset()(num in any::<u64>(), factor in any::<u64>()) -> FieldElement<Babybear31PrimeField> { FieldElement::<Babybear31PrimeField>::from(num).pow(factor) }
360 }
361 prop_compose! {
362 fn field_vec(max_exp: u8)(vec in collection::vec(field_element(), 0..1 << max_exp)) -> Vec<FieldElement<Babybear31PrimeField>> {
363 vec
364 }
365 }
366 prop_compose! {
367 fn non_power_of_two_sized_field_vec(max_exp: u8)(vec in collection::vec(field_element(), 2..1<<max_exp).prop_filter("Avoid polynomials of size power of two", |vec| !vec.len().is_power_of_two())) -> Vec<FieldElement<Babybear31PrimeField>> {
368 vec
369 }
370 }
371 prop_compose! {
372 fn poly(max_exp: u8)(coeffs in field_vec(max_exp)) -> Polynomial<FieldElement<Babybear31PrimeField>> {
373 Polynomial::new(&coeffs)
374 }
375 }
376 prop_compose! {
377 fn poly_with_non_power_of_two_coeffs(max_exp: u8)(coeffs in non_power_of_two_sized_field_vec(max_exp)) -> Polynomial<FieldElement<Babybear31PrimeField>> {
378 Polynomial::new(&coeffs)
379 }
380 }
381
382 proptest! {
383 #[test]
385 #[cfg(not(feature = "cuda"))]
386 fn test_fft_matches_naive_evaluation(poly in poly(8)) {
387 let (fft_eval, naive_eval) = gen_fft_and_naive_evaluation(poly);
388 prop_assert_eq!(fft_eval, naive_eval);
389 }
390
391 #[test]
393 #[cfg(not(feature = "cuda"))]
394 fn test_fft_coset_matches_naive_evaluation(poly in poly(4), offset in offset(), blowup_factor in powers_of_two(4)) {
395 let (fft_eval, naive_eval) = gen_fft_coset_and_naive_evaluation(poly, offset, blowup_factor);
396 prop_assert_eq!(fft_eval, naive_eval);
397 }
398
399 #[test]
401 #[cfg(not(feature = "cuda"))]
402 fn test_fft_interpolate_matches_naive(fft_evals in field_vec(4)
403 .prop_filter("Avoid polynomials of size not power of two",
404 |evals| evals.len().is_power_of_two())) {
405 let (fft_poly, naive_poly) = gen_fft_and_naive_interpolate(&fft_evals);
406 prop_assert_eq!(fft_poly, naive_poly);
407 }
408
409 #[test]
411 #[cfg(not(feature = "cuda"))]
412 fn test_fft_interpolate_coset_matches_naive(offset in offset(), fft_evals in field_vec(4)
413 .prop_filter("Avoid polynomials of size not power of two",
414 |evals| evals.len().is_power_of_two())) {
415 let (fft_poly, naive_poly) = gen_fft_and_naive_coset_interpolate(&fft_evals, &offset);
416 prop_assert_eq!(fft_poly, naive_poly);
417 }
418
419 #[test]
421 #[cfg(not(feature = "cuda"))]
422 fn test_fft_interpolate_is_inverse_of_evaluate(
423 poly in poly(4).prop_filter("Avoid non pows of two", |poly| poly.coeff_len().is_power_of_two())) {
424 let (poly, new_poly) = gen_fft_interpolate_and_evaluate(poly);
425 prop_assert_eq!(poly, new_poly);
426 }
427 }
428 }
429}