p3_field_testing/
lib.rs

1//! Utilities for testing field implementations.
2
3#![no_std]
4
5extern crate alloc;
6
7pub mod bench_func;
8pub mod packedfield_testing;
9
10pub use bench_func::*;
11use num_bigint::BigUint;
12use num_traits::identities::One;
13use p3_field::{
14    cyclic_subgroup_coset_known_order, cyclic_subgroup_known_order, two_adic_coset_zerofier,
15    two_adic_subgroup_zerofier, ExtensionField, Field, TwoAdicField,
16};
17pub use packedfield_testing::*;
18use rand::distributions::{Distribution, Standard};
19use rand::Rng;
20
21#[allow(clippy::eq_op)]
22pub fn test_add_neg_sub_mul<F: Field>()
23where
24    Standard: Distribution<F>,
25{
26    let mut rng = rand::thread_rng();
27    let x = rng.gen::<F>();
28    let y = rng.gen::<F>();
29    let z = rng.gen::<F>();
30    assert_eq!(x + (-x), F::zero());
31    assert_eq!(-x, F::zero() - x);
32    assert_eq!(x + x, x * F::two());
33    assert_eq!(x, x.halve() * F::two());
34    assert_eq!(x * (-x), -x.square());
35    assert_eq!(x + y, y + x);
36    assert_eq!(x * y, y * x);
37    assert_eq!(x * (y * z), (x * y) * z);
38    assert_eq!(x - (y + z), (x - y) - z);
39    assert_eq!((x + y) - z, x + (y - z));
40    assert_eq!(x * (y + z), x * y + x * z);
41    assert_eq!(
42        x + y + z + x + y + z,
43        [x, x, y, y, z, z].iter().cloned().sum()
44    );
45}
46
47pub fn test_inv_div<F: Field>()
48where
49    Standard: Distribution<F>,
50{
51    let mut rng = rand::thread_rng();
52    let x = rng.gen::<F>();
53    let y = rng.gen::<F>();
54    let z = rng.gen::<F>();
55    assert_eq!(x * x.inverse(), F::one());
56    assert_eq!(x.inverse() * x, F::one());
57    assert_eq!(x.square().inverse(), x.inverse().square());
58    assert_eq!((x / y) * y, x);
59    assert_eq!(x / (y * z), (x / y) / z);
60    assert_eq!((x * y) / z, x * (y / z));
61}
62
63pub fn test_inverse<F: Field>()
64where
65    Standard: Distribution<F>,
66{
67    assert_eq!(None, F::zero().try_inverse());
68
69    assert_eq!(Some(F::one()), F::one().try_inverse());
70
71    let mut rng = rand::thread_rng();
72    for _ in 0..1000 {
73        let x = rng.gen::<F>();
74        if !x.is_zero() && !x.is_one() {
75            let z = x.inverse();
76            assert_ne!(x, z);
77            assert_eq!(x * z, F::one());
78        }
79    }
80}
81
82pub fn test_multiplicative_group_factors<F: Field>() {
83    let product: BigUint = F::multiplicative_group_factors()
84        .into_iter()
85        .map(|(factor, exponent)| factor.pow(exponent as u32))
86        .product();
87    assert_eq!(product + BigUint::one(), F::order());
88}
89
90pub fn test_two_adic_subgroup_zerofier<F: TwoAdicField>() {
91    for log_n in 0..5 {
92        let g = F::two_adic_generator(log_n);
93        for x in cyclic_subgroup_known_order(g, 1 << log_n) {
94            let zerofier_eval = two_adic_subgroup_zerofier(log_n, x);
95            assert_eq!(zerofier_eval, F::zero());
96        }
97    }
98}
99
100pub fn test_two_adic_coset_zerofier<F: TwoAdicField>() {
101    for log_n in 0..5 {
102        let g = F::two_adic_generator(log_n);
103        let shift = F::generator();
104        for x in cyclic_subgroup_coset_known_order(g, shift, 1 << log_n) {
105            let zerofier_eval = two_adic_coset_zerofier(log_n, shift, x);
106            assert_eq!(zerofier_eval, F::zero());
107        }
108    }
109}
110
111pub fn test_two_adic_generator_consistency<F: TwoAdicField>() {
112    let log_n = F::TWO_ADICITY;
113    let g = F::two_adic_generator(log_n);
114    for bits in 0..=log_n {
115        assert_eq!(g.exp_power_of_2(bits), F::two_adic_generator(log_n - bits));
116    }
117}
118
119pub fn test_ef_two_adic_generator_consistency<
120    F: TwoAdicField,
121    EF: TwoAdicField + ExtensionField<F>,
122>() {
123    assert_eq!(
124        EF::from_base(F::two_adic_generator(F::TWO_ADICITY)),
125        EF::two_adic_generator(F::TWO_ADICITY)
126    );
127}
128
129#[macro_export]
130macro_rules! test_field {
131    ($field:ty) => {
132        mod field_tests {
133            #[test]
134            fn test_add_neg_sub_mul() {
135                $crate::test_add_neg_sub_mul::<$field>();
136            }
137            #[test]
138            fn test_inv_div() {
139                $crate::test_inv_div::<$field>();
140            }
141            #[test]
142            fn test_inverse() {
143                $crate::test_inverse::<$field>();
144            }
145            #[test]
146            fn test_multiplicative_group_factors() {
147                $crate::test_multiplicative_group_factors::<$field>();
148            }
149        }
150    };
151}
152
153#[macro_export]
154macro_rules! test_two_adic_field {
155    ($field:ty) => {
156        mod two_adic_field_tests {
157            #[test]
158            fn test_two_adic_field_subgroup_zerofier() {
159                $crate::test_two_adic_subgroup_zerofier::<$field>();
160            }
161            #[test]
162            fn test_two_adic_coset_zerofier() {
163                $crate::test_two_adic_coset_zerofier::<$field>();
164            }
165            #[test]
166            fn test_two_adic_consisitency() {
167                $crate::test_two_adic_generator_consistency::<$field>();
168            }
169        }
170    };
171}
172
173#[macro_export]
174macro_rules! test_two_adic_extension_field {
175    ($field:ty, $ef:ty) => {
176        use $crate::test_two_adic_field;
177
178        test_two_adic_field!($ef);
179
180        mod two_adic_extension_field_tests {
181
182            #[test]
183            fn test_ef_two_adic_generator_consistency() {
184                $crate::test_ef_two_adic_generator_consistency::<$field, $ef>();
185            }
186        }
187    };
188}
189
190#[cfg(test)]
191mod tests {
192    use alloc::vec;
193    use alloc::vec::Vec;
194
195    use p3_baby_bear::BabyBear;
196    use p3_field::extension::{BinomialExtensionField, HasFrobenius};
197    use p3_field::{binomial_expand, eval_poly, AbstractExtensionField, AbstractField};
198    use rand::random;
199
200    use super::*;
201
202    #[test]
203    fn test_minimal_poly() {
204        type F = BabyBear;
205        type EF = BinomialExtensionField<F, 4>;
206        for _ in 0..1024 {
207            let x: EF = random();
208            let m: Vec<EF> = x.minimal_poly().into_iter().map(EF::from_base).collect();
209            assert!(eval_poly(&m, x).is_zero());
210        }
211    }
212
213    #[test]
214    fn test_binomial_expand() {
215        type F = BabyBear;
216        // (x - 1)(x - 2) = x^2 - 3x + 2
217        assert_eq!(
218            binomial_expand(&[F::one(), F::two()]),
219            vec![F::two(), -F::from_canonical_usize(3), F::one()]
220        );
221    }
222}