p3_field_testing/
packedfield_testing.rs

1use alloc::vec;
2use alloc::vec::Vec;
3
4use p3_field::{Field, PackedField, PackedFieldPow2, PackedValue, PrimeCharacteristicRing};
5use rand::distr::{Distribution, StandardUniform};
6use rand::rngs::SmallRng;
7use rand::{Rng, SeedableRng};
8
9fn packed_from_random<PV>(seed: u64) -> PV
10where
11    PV: PackedValue,
12    StandardUniform: Distribution<PV::Value>,
13{
14    let mut rng = SmallRng::seed_from_u64(seed);
15    PV::from_fn(|_| rng.random())
16}
17
18/// Interleave arr1 and arr2 using chunks of size i.
19fn interleave<T: Copy + Default>(arr1: &[T], arr2: &[T], i: usize) -> (Vec<T>, Vec<T>) {
20    let width = arr1.len();
21    assert_eq!(width, arr2.len());
22    assert_eq!(width % i, 0);
23
24    if i == width {
25        return (arr1.to_vec(), arr2.to_vec());
26    }
27
28    let mut outleft = vec![T::default(); width];
29    let mut outright = vec![T::default(); width];
30
31    let mut flag = false;
32
33    for j in 0..width {
34        if j % i == 0 {
35            flag = !flag;
36        }
37        if flag {
38            outleft[j] = arr1[j];
39            outleft[j + i] = arr2[j];
40        } else {
41            outright[j - i] = arr1[j];
42            outright[j] = arr2[j];
43        }
44    }
45
46    (outleft, outright)
47}
48
49fn test_interleave<PF>(i: usize)
50where
51    PF: PackedFieldPow2 + Eq,
52    StandardUniform: Distribution<PF::Scalar>,
53{
54    assert!(PF::WIDTH % i == 0);
55
56    let vec1 = packed_from_random::<PF>(0x4ff5dec04791e481);
57    let vec2 = packed_from_random::<PF>(0x5806c495e9451f8e);
58
59    let arr1 = vec1.as_slice();
60    let arr2 = vec2.as_slice();
61
62    let (res1, res2) = vec1.interleave(vec2, i);
63    let (out1, out2) = interleave(arr1, arr2, i);
64
65    assert_eq!(
66        res1.as_slice(),
67        &out1,
68        "Error in left output when testing interleave {}. Data is: \n {:?} \n {:?} \n {:?} \n {:?} \n {:?} \n {:?}.",
69        i,
70        arr1,
71        arr2,
72        res1,
73        res2,
74        out1,
75        out2,
76    );
77    assert_eq!(
78        res2.as_slice(),
79        &out2,
80        "Error in right output when testing interleave {}.",
81        i
82    );
83}
84
85pub fn test_interleaves<PF>()
86where
87    PF: PackedFieldPow2 + Eq,
88    StandardUniform: Distribution<PF::Scalar>,
89{
90    let mut i = 1;
91    while i <= PF::WIDTH {
92        test_interleave::<PF>(i);
93        i *= 2;
94    }
95}
96
97pub fn test_packed_linear_combination<PF: PackedField + Eq>()
98where
99    StandardUniform: Distribution<PF> + Distribution<PF::Scalar>,
100{
101    let mut rng = SmallRng::seed_from_u64(1);
102    let u: [PF::Scalar; 64] = rng.random();
103    let v: [PF; 64] = rng.random();
104
105    let mut dot = PF::ZERO;
106    assert_eq!(dot, PF::packed_linear_combination::<0>(&u[..0], &v[..0]));
107    dot += v[0] * u[0];
108    assert_eq!(dot, PF::packed_linear_combination::<1>(&u[..1], &v[..1]));
109    dot += v[1] * u[1];
110    assert_eq!(dot, PF::packed_linear_combination::<2>(&u[..2], &v[..2]));
111    dot += v[2] * u[2];
112    assert_eq!(dot, PF::packed_linear_combination::<3>(&u[..3], &v[..3]));
113    dot += v[3] * u[3];
114    assert_eq!(dot, PF::packed_linear_combination::<4>(&u[..4], &v[..4]));
115    dot += v[4] * u[4];
116    assert_eq!(dot, PF::packed_linear_combination::<5>(&u[..5], &v[..5]));
117    dot += v[5] * u[5];
118    assert_eq!(dot, PF::packed_linear_combination::<6>(&u[..6], &v[..6]));
119    dot += v[6] * u[6];
120    assert_eq!(dot, PF::packed_linear_combination::<7>(&u[..7], &v[..7]));
121    dot += v[7] * u[7];
122    assert_eq!(dot, PF::packed_linear_combination::<8>(&u[..8], &v[..8]));
123    dot += v[8] * u[8];
124    assert_eq!(dot, PF::packed_linear_combination::<9>(&u[..9], &v[..9]));
125    dot += v[9] * u[9];
126    assert_eq!(dot, PF::packed_linear_combination::<10>(&u[..10], &v[..10]));
127    dot += v[10] * u[10];
128    assert_eq!(dot, PF::packed_linear_combination::<11>(&u[..11], &v[..11]));
129    dot += v[11] * u[11];
130    assert_eq!(dot, PF::packed_linear_combination::<12>(&u[..12], &v[..12]));
131    dot += v[12] * u[12];
132    assert_eq!(dot, PF::packed_linear_combination::<13>(&u[..13], &v[..13]));
133    dot += v[13] * u[13];
134    assert_eq!(dot, PF::packed_linear_combination::<14>(&u[..14], &v[..14]));
135    dot += v[14] * u[14];
136    assert_eq!(dot, PF::packed_linear_combination::<15>(&u[..15], &v[..15]));
137    dot += v[15] * u[15];
138    assert_eq!(dot, PF::packed_linear_combination::<16>(&u[..16], &v[..16]));
139
140    let dot_64: PF = u
141        .iter()
142        .zip(v.iter())
143        .fold(PF::ZERO, |acc, (&lhs, &rhs)| acc + (rhs * lhs));
144    assert_eq!(dot_64, PF::packed_linear_combination::<64>(&u, &v));
145}
146
147pub fn test_vs_scalar<PF>(special_vals: PF)
148where
149    PF: PackedField + Eq,
150    StandardUniform: Distribution<PF::Scalar>,
151{
152    let vec0: PF = packed_from_random(0x278d9e202925a1d1);
153    let vec1: PF = packed_from_random(0xf04cbac0cbad419f);
154    let vec_special = special_vals;
155
156    let arr0 = vec0.as_slice();
157    let arr1 = vec1.as_slice();
158
159    let vec_sum = vec0 + vec1;
160    let arr_sum = vec_sum.as_slice();
161    let vec_special_sum_left = vec_special + vec0;
162    let arr_special_sum_left = vec_special_sum_left.as_slice();
163    let vec_special_sum_right = vec1 + vec_special;
164    let arr_special_sum_right = vec_special_sum_right.as_slice();
165
166    let vec_sub = vec0 - vec1;
167    let arr_sub = vec_sub.as_slice();
168    let vec_special_sub_left = vec_special - vec0;
169    let arr_special_sub_left = vec_special_sub_left.as_slice();
170    let vec_special_sub_right = vec1 - vec_special;
171    let arr_special_sub_right = vec_special_sub_right.as_slice();
172
173    let vec_mul = vec0 * vec1;
174    let arr_mul = vec_mul.as_slice();
175    let vec_special_mul_left = vec_special * vec0;
176    let arr_special_mul_left = vec_special_mul_left.as_slice();
177    let vec_special_mul_right = vec1 * vec_special;
178    let arr_special_mul_right = vec_special_mul_right.as_slice();
179
180    let vec_neg = -vec0;
181    let arr_neg = vec_neg.as_slice();
182    let vec_special_neg = -vec_special;
183    let arr_special_neg = vec_special_neg.as_slice();
184
185    let vec_exp_3 = vec0.exp_const_u64::<3>();
186    let arr_exp_3 = vec_exp_3.as_slice();
187    let vec_special_exp_3 = vec_special.exp_const_u64::<3>();
188    let arr_special_exp_3 = vec_special_exp_3.as_slice();
189
190    let vec_exp_5 = vec0.exp_const_u64::<5>();
191    let arr_exp_5 = vec_exp_5.as_slice();
192    let vec_special_exp_5 = vec_special.exp_const_u64::<5>();
193    let arr_special_exp_5 = vec_special_exp_5.as_slice();
194
195    let vec_exp_7 = vec0.exp_const_u64::<7>();
196    let arr_exp_7 = vec_exp_7.as_slice();
197    let vec_special_exp_7 = vec_special.exp_const_u64::<7>();
198    let arr_special_exp_7 = vec_special_exp_7.as_slice();
199
200    let special_vals = special_vals.as_slice();
201    for i in 0..PF::WIDTH {
202        assert_eq!(
203            arr_sum[i],
204            arr0[i] + arr1[i],
205            "Error when testing add consistency of packed and scalar at location {}.",
206            i
207        );
208        assert_eq!(
209            arr_special_sum_left[i],
210            special_vals[i] + arr0[i],
211            "Error when testing consistency of left add for special values for packed and scalar at location {}.",
212            i
213        );
214        assert_eq!(
215            arr_special_sum_right[i],
216            arr1[i] + special_vals[i],
217            "Error when testing consistency of right add for special values for packed and scalar at location {}.",
218            i
219        );
220
221        assert_eq!(
222            arr_sub[i],
223            arr0[i] - arr1[i],
224            "Error when testing sub consistency of packed and scalar at location {}.",
225            i
226        );
227        assert_eq!(
228            arr_special_sub_left[i],
229            special_vals[i] - arr0[i],
230            "Error when testing consistency of left sub for special values for packed and scalar at location {}.",
231            i
232        );
233        assert_eq!(
234            arr_special_sub_right[i],
235            arr1[i] - special_vals[i],
236            "Error when testing consistency of right sub for special values for packed and scalar at location {}.",
237            i
238        );
239
240        assert_eq!(
241            arr_mul[i],
242            arr0[i] * arr1[i],
243            "Error when testing mul consistency of packed and scalar at location {}.",
244            i
245        );
246        assert_eq!(
247            arr_special_mul_left[i],
248            special_vals[i] * arr0[i],
249            "Error when testing consistency of left mul for special values for packed and scalar at location {}.",
250            i
251        );
252        assert_eq!(
253            arr_special_mul_right[i],
254            arr1[i] * special_vals[i],
255            "Error when testing consistency of right mul for special values for packed and scalar at location {}.",
256            i
257        );
258
259        assert_eq!(
260            arr_neg[i], -arr0[i],
261            "Error when testing neg consistency of packed and scalar at location {}.",
262            i
263        );
264        assert_eq!(
265            arr_special_neg[i], -special_vals[i],
266            "Error when testing consistency of neg for special values for packed and scalar at location {}.",
267            i
268        );
269        assert_eq!(
270            arr_exp_3[i],
271            arr0[i].exp_const_u64::<3>(),
272            "Error when testing exp_const_u64::<3> consistency of packed and scalar at location {}.",
273            i
274        );
275        assert_eq!(
276            arr_special_exp_3[i],
277            special_vals[i].exp_const_u64::<3>(),
278            "Error when testing consistency of exp_const_u64::<3> for special values for packed and scalar at location {}.",
279            i
280        );
281        assert_eq!(
282            arr_exp_5[i],
283            arr0[i].exp_const_u64::<5>(),
284            "Error when testing exp_const_u64::<5> consistency of packed and scalar at location {}.",
285            i
286        );
287        assert_eq!(
288            arr_special_exp_5[i],
289            special_vals[i].exp_const_u64::<5>(),
290            "Error when testing consistency of exp_const_u64::<5> for special values for packed and scalar at location {}.",
291            i
292        );
293        assert_eq!(
294            arr_exp_7[i],
295            arr0[i].exp_const_u64::<7>(),
296            "Error when testing exp_const_u64::<7> consistency of packed and scalar at location {}.",
297            i
298        );
299        assert_eq!(
300            arr_special_exp_7[i],
301            special_vals[i].exp_const_u64::<7>(),
302            "Error when testing consistency of exp_const_u64::<7> for special values for packed and scalar at location {}.",
303            i
304        );
305    }
306}
307
308pub fn test_multiplicative_inverse<PF>()
309where
310    PF: PackedField + Eq,
311    StandardUniform: Distribution<PF::Scalar>,
312{
313    let vec: PF = packed_from_random(0xb0c7a5153103c5a8);
314    let arr = vec.as_slice();
315    let vec_inv = PF::from_fn(|i| arr[i].inverse());
316    let res = vec * vec_inv;
317    assert_eq!(
318        res,
319        PF::ONE,
320        "Error when testing multiplication by inverse."
321    );
322}
323
324#[macro_export]
325macro_rules! test_packed_field {
326    ($packedfield:ty, $zeros:expr, $ones:expr, $specials:expr) => {
327        mod packed_field_tests {
328            use p3_field::PrimeCharacteristicRing;
329
330            #[test]
331            fn test_interleaves() {
332                $crate::test_interleaves::<$packedfield>();
333            }
334            #[test]
335            fn test_ring_with_eq() {
336                $crate::test_ring_with_eq::<$packedfield>($zeros, $ones);
337            }
338            #[test]
339            fn test_packed_linear_combination() {
340                $crate::test_packed_linear_combination::<$packedfield>();
341            }
342            #[test]
343            fn test_vs_scalar() {
344                $crate::test_vs_scalar::<$packedfield>($specials);
345            }
346            #[test]
347            fn test_multiplicative_inverse() {
348                $crate::test_multiplicative_inverse::<$packedfield>();
349            }
350            #[test]
351            fn test_mul_2exp_u64() {
352                $crate::test_mul_2exp_u64::<$packedfield>();
353            }
354        }
355    };
356}
357
358#[macro_export]
359macro_rules! test_packed_extension_field {
360    ($packedextfield:ty, $zeros:expr, $ones:expr) => {
361        mod packed_field_tests {
362            use p3_field::PrimeCharacteristicRing;
363
364            // TODO: Add more tests for packed extension fields.
365            #[test]
366            fn test_ring_with_eq() {
367                $crate::test_ring_with_eq::<$packedextfield>($zeros, $ones);
368            }
369        }
370    };
371}