p3_field_testing/
packedfield_testing.rs

1use alloc::vec;
2use alloc::vec::Vec;
3
4use p3_field::{Field, PackedField, PackedValue};
5use rand::distributions::{Distribution, Standard};
6use rand::{Rng, SeedableRng};
7use rand_chacha::ChaCha20Rng;
8
9fn packed_from_random<PV>(seed: u64) -> PV
10where
11    PV: PackedValue,
12    Standard: Distribution<PV::Value>,
13{
14    let mut rng = ChaCha20Rng::seed_from_u64(seed);
15    PV::from_fn(|_| rng.gen())
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: PackedField + Eq,
52    Standard: 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 {}.",
69        i
70    );
71    assert_eq!(
72        res2.as_slice(),
73        &out2,
74        "Error in right output when testing interleave {}.",
75        i
76    );
77}
78
79pub fn test_interleaves<PF>()
80where
81    PF: PackedField + Eq,
82    Standard: Distribution<PF::Scalar>,
83{
84    let mut i = 1;
85    while i <= PF::WIDTH {
86        test_interleave::<PF>(i);
87        i *= 2;
88    }
89}
90
91#[allow(clippy::eq_op)]
92pub fn test_add_neg<PF>(zeros: PF)
93where
94    PF: PackedField + Eq,
95    Standard: Distribution<PF::Scalar>,
96{
97    let vec0 = packed_from_random::<PF>(0x8b078c2b693c893f);
98    let vec1 = packed_from_random::<PF>(0x4ff5dec04791e481);
99    let vec2 = packed_from_random::<PF>(0x5806c495e9451f8e);
100
101    assert_eq!(
102        (vec0 + vec1) + vec2,
103        vec0 + (vec1 + vec2),
104        "Error when testing associativity of add."
105    );
106    assert_eq!(
107        vec0 + vec1,
108        vec1 + vec0,
109        "Error when testing commutativity of add."
110    );
111    assert_eq!(
112        vec0,
113        vec0 + zeros,
114        "Error when testing additive identity right."
115    );
116    assert_eq!(
117        vec0,
118        zeros + vec0,
119        "Error when testing additive identity left."
120    );
121    assert_eq!(
122        vec0 + (-vec0),
123        PF::zero(),
124        "Error when testing additive inverse."
125    );
126    assert_eq!(
127        vec0 - vec0,
128        PF::zero(),
129        "Error when testing subtracting of self."
130    );
131    assert_eq!(
132        vec0 - vec1,
133        -(vec1 - vec0),
134        "Error when testing anticommutativity of sub."
135    );
136    assert_eq!(vec0, vec0 - zeros, "Error when testing subtracting zero.");
137    assert_eq!(
138        -vec0,
139        zeros - vec0,
140        "Error when testing subtracting from zero"
141    );
142    assert_eq!(vec0, -(-vec0), "Error when testing double negation");
143    assert_eq!(
144        vec0 - vec1,
145        vec0 + (-vec1),
146        "Error when testing addition of negation"
147    );
148    assert_eq!(PF::one() + PF::one(), PF::two(), "Error 1 + 1 =/= 2");
149    assert_eq!(PF::neg_one() + PF::two(), PF::one(), "Error -1 + 2 =/= 1");
150    assert_eq!(
151        vec0.double(),
152        vec0 + vec0,
153        "Error when comparing x.double() to x + x"
154    );
155}
156
157pub fn test_mul<PF>(zeros: PF)
158where
159    PF: PackedField + Eq,
160    Standard: Distribution<PF::Scalar>,
161{
162    let vec0 = packed_from_random::<PF>(0x0b1ee4d7c979d50c);
163    let vec1 = packed_from_random::<PF>(0x39faa0844a36e45a);
164    let vec2 = packed_from_random::<PF>(0x08fac4ee76260e44);
165
166    assert_eq!(
167        (vec0 * vec1) * vec2,
168        vec0 * (vec1 * vec2),
169        "Error when testing associativity of mul."
170    );
171    assert_eq!(
172        vec0 * vec1,
173        vec1 * vec0,
174        "Error when testing commutativity of mul."
175    );
176    assert_eq!(
177        vec0,
178        vec0 * PF::one(),
179        "Error when testing multiplicative identity right."
180    );
181    assert_eq!(
182        vec0,
183        PF::one() * vec0,
184        "Error when testing multiplicative identity left."
185    );
186    assert_eq!(
187        vec0 * zeros,
188        PF::zero(),
189        "Error when testing right multiplication by 0."
190    );
191    assert_eq!(
192        zeros * vec0,
193        PF::zero(),
194        "Error when testing left multiplication by 0."
195    );
196    assert_eq!(
197        vec0 * PF::neg_one(),
198        -(vec0),
199        "Error when testing right multiplication by -1."
200    );
201    assert_eq!(
202        PF::neg_one() * vec0,
203        -(vec0),
204        "Error when testing left multiplication by -1."
205    );
206    assert_eq!(
207        vec0.double(),
208        PF::two() * vec0,
209        "Error when comparing x.double() to 2 * x."
210    );
211}
212
213pub fn test_distributivity<PF>()
214where
215    PF: PackedField + Eq,
216    Standard: Distribution<PF::Scalar>,
217{
218    let vec0 = packed_from_random::<PF>(0x278d9e202925a1d1);
219    let vec1 = packed_from_random::<PF>(0xf04cbac0cbad419f);
220    let vec2 = packed_from_random::<PF>(0x76976e2abdc5a056);
221
222    assert_eq!(
223        vec0 * (-vec1),
224        -(vec0 * vec1),
225        "Error when testing distributivity of mul and right neg."
226    );
227    assert_eq!(
228        (-vec0) * vec1,
229        -(vec0 * vec1),
230        "Error when testing distributivity of mul and left neg."
231    );
232
233    assert_eq!(
234        vec0 * (vec1 + vec2),
235        vec0 * vec1 + vec0 * vec2,
236        "Error when testing distributivity of add and left mul."
237    );
238    assert_eq!(
239        (vec0 + vec1) * vec2,
240        vec0 * vec2 + vec1 * vec2,
241        "Error when testing distributivity of add and right mul."
242    );
243    assert_eq!(
244        vec0 * (vec1 - vec2),
245        vec0 * vec1 - vec0 * vec2,
246        "Error when testing distributivity of sub and left mul."
247    );
248    assert_eq!(
249        (vec0 - vec1) * vec2,
250        vec0 * vec2 - vec1 * vec2,
251        "Error when testing distributivity of sub and right mul."
252    );
253}
254
255pub fn test_vs_scalar<PF>(special_vals: PF)
256where
257    PF: PackedField + Eq,
258    Standard: Distribution<PF::Scalar>,
259{
260    let vec0: PF = packed_from_random(0x278d9e202925a1d1);
261    let vec1: PF = packed_from_random(0xf04cbac0cbad419f);
262    let vec_special = special_vals;
263
264    let arr0 = vec0.as_slice();
265    let arr1 = vec1.as_slice();
266
267    let vec_sum = vec0 + vec1;
268    let arr_sum = vec_sum.as_slice();
269    let vec_special_sum_left = vec_special + vec0;
270    let arr_special_sum_left = vec_special_sum_left.as_slice();
271    let vec_special_sum_right = vec1 + vec_special;
272    let arr_special_sum_right = vec_special_sum_right.as_slice();
273
274    let vec_sub = vec0 - vec1;
275    let arr_sub = vec_sub.as_slice();
276    let vec_special_sub_left = vec_special - vec0;
277    let arr_special_sub_left = vec_special_sub_left.as_slice();
278    let vec_special_sub_right = vec1 - vec_special;
279    let arr_special_sub_right = vec_special_sub_right.as_slice();
280
281    let vec_mul = vec0 * vec1;
282    let arr_mul = vec_mul.as_slice();
283    let vec_special_mul_left = vec_special * vec0;
284    let arr_special_mul_left = vec_special_mul_left.as_slice();
285    let vec_special_mul_right = vec1 * vec_special;
286    let arr_special_mul_right = vec_special_mul_right.as_slice();
287
288    let vec_neg = -vec0;
289    let arr_neg = vec_neg.as_slice();
290    let vec_special_neg = -vec_special;
291    let arr_special_neg = vec_special_neg.as_slice();
292
293    let special_vals = special_vals.as_slice();
294    for i in 0..PF::WIDTH {
295        assert_eq!(
296            arr_sum[i],
297            arr0[i] + arr1[i],
298            "Error when testing add consistency of packed and scalar at location {}.",
299            i
300        );
301        assert_eq!(
302            arr_special_sum_left[i],
303            special_vals[i] + arr0[i],
304            "Error when testing consistency of left add for special values for packed and scalar at location {}.",
305            i
306        );
307        assert_eq!(
308            arr_special_sum_right[i],
309            arr1[i] + special_vals[i],
310            "Error when testing consistency of right add for special values for packed and scalar at location {}.",
311            i
312        );
313
314        assert_eq!(
315            arr_sub[i],
316            arr0[i] - arr1[i],
317            "Error when testing sub consistency of packed and scalar at location {}.",
318            i
319        );
320        assert_eq!(arr_special_sub_left[i],
321            special_vals[i] - arr0[i],
322            "Error when testing consistency of left sub for special values for packed and scalar at location {}.",
323            i
324        );
325        assert_eq!(arr_special_sub_right[i],
326            arr1[i] - special_vals[i],
327            "Error when testing consistency of right sub for special values for packed and scalar at location {}.",
328            i
329        );
330
331        assert_eq!(
332            arr_mul[i],
333            arr0[i] * arr1[i],
334            "Error when testing mul consistency of packed and scalar at location {}.",
335            i
336        );
337        assert_eq!(arr_special_mul_left[i],
338            special_vals[i] * arr0[i],
339            "Error when testing consistency of left mul for special values for packed and scalar at location {}.",
340            i
341        );
342        assert_eq!(arr_special_mul_right[i],
343            arr1[i] * special_vals[i],
344            "Error when testing consistency of right mul for special values for packed and scalar at location {}.",
345            i
346        );
347
348        assert_eq!(
349            arr_neg[i], -arr0[i],
350            "Error when testing neg consistency of packed and scalar at location {}.",
351            i
352        );
353        assert_eq!(arr_special_neg[i],
354            -special_vals[i],
355            "Error when testing consistency of neg for special values for packed and scalar at location {}.",
356            i
357        );
358    }
359}
360
361pub fn test_multiplicative_inverse<PF>()
362where
363    PF: PackedField + Eq,
364    Standard: Distribution<PF::Scalar>,
365{
366    let vec: PF = packed_from_random(0xb0c7a5153103c5a8);
367    let arr = vec.as_slice();
368    let vec_inv = PF::from_fn(|i| arr[i].inverse());
369    let res = vec * vec_inv;
370    assert_eq!(
371        res,
372        PF::one(),
373        "Error when testing multiplication by inverse."
374    );
375}
376
377#[macro_export]
378macro_rules! test_packed_field {
379    ($packedfield:ty, $zeros:expr, $specials:expr) => {
380        mod packed_field_tests {
381            use p3_field::AbstractField;
382
383            #[test]
384            fn test_interleaves() {
385                $crate::test_interleaves::<$packedfield>();
386            }
387            #[test]
388            fn test_add_neg() {
389                $crate::test_add_neg::<$packedfield>($zeros);
390            }
391            #[test]
392            fn test_mul() {
393                $crate::test_mul::<$packedfield>($zeros);
394            }
395            #[test]
396            fn test_distributivity() {
397                $crate::test_distributivity::<$packedfield>();
398            }
399            #[test]
400            fn test_vs_scalar() {
401                $crate::test_vs_scalar::<$packedfield>($specials);
402            }
403            #[test]
404            fn test_multiplicative_inverse() {
405                $crate::test_multiplicative_inverse::<$packedfield>();
406            }
407        }
408    };
409}