use alloc::vec::Vec;
use p3_field::extension::HasFrobenius;
use p3_field::{ExtensionField, Field, PackedFieldExtension, PackedValue};
use proptest::prelude::*;
use rand::distr::{Distribution, StandardUniform};
use rand::rngs::SmallRng;
use rand::{RngExt, SeedableRng};
use crate::exp_biguint;
pub fn test_to_from_extension_field<F, EF>()
where
F: Field,
EF: ExtensionField<F>,
StandardUniform: Distribution<F> + Distribution<EF>,
{
let mut rng = SmallRng::seed_from_u64(1);
let base_elem: F = rng.random();
let base_elem_in_ext: EF = base_elem.into();
assert!(base_elem_in_ext.is_in_basefield());
assert_eq!(base_elem_in_ext.as_base(), Some(base_elem));
let extension_elem: EF = rng.random();
let ext_degree = EF::DIMENSION;
if ext_degree == 1 {
assert!(
extension_elem.is_in_basefield(),
"The element {extension_elem} does not lie in the base field, but it should.",
);
} else {
assert!(
!extension_elem.is_in_basefield(),
"The randomly chosen element {extension_elem} lies in the base field, but it (likely) should not.",
);
assert!(extension_elem.as_base().is_none());
}
}
pub fn test_galois_extension<F, EF>()
where
F: Field,
EF: ExtensionField<F>,
StandardUniform: Distribution<EF>,
{
let mut rng = SmallRng::seed_from_u64(1);
let extension_elem = rng.random();
let ext_degree = EF::DIMENSION;
let field_order = F::order();
let (trace, norm, power) = (1..ext_degree).fold(
(extension_elem, extension_elem, extension_elem),
|(acc, prod, power), _| {
let next_power = exp_biguint(power, &field_order);
(acc + next_power, prod * next_power, next_power)
},
);
let ext_power_p_d = exp_biguint(power, &field_order);
assert!(
norm.is_in_basefield(),
"The product of Galois conjugates {norm} of the element {extension_elem} does not lie in the base field.",
);
assert!(
trace.is_in_basefield(),
"The sum of Galois conjugates {trace} of the element {extension_elem} does not lie in the base field.",
);
assert_eq!(
extension_elem, ext_power_p_d,
"The element {extension_elem} raised to the power of p^d does not equal itself.",
);
}
pub fn test_packed_extension<F, EF>()
where
F: Field,
EF: ExtensionField<F>,
StandardUniform: Distribution<EF>,
{
let mut rng = SmallRng::seed_from_u64(1);
let width = F::Packing::WIDTH;
let extension_elements: Vec<EF> = (0..width).map(|_| rng.random()).collect();
let packed_extension = EF::ExtensionPacking::from_ext_slice(&extension_elements);
let unpacked_extension: Vec<EF> =
EF::ExtensionPacking::to_ext_iter([packed_extension]).collect();
assert_eq!(extension_elements, unpacked_extension);
let base_powers = extension_elements[0].powers().collect_n(10 * width);
let packed_powers = EF::ExtensionPacking::packed_ext_powers(extension_elements[0]);
let unpacked_powers: Vec<EF> = EF::ExtensionPacking::to_ext_iter(packed_powers)
.take(10 * width)
.collect();
assert_eq!(base_powers, unpacked_powers);
let packed_powers_capped =
EF::ExtensionPacking::packed_ext_powers_capped(extension_elements[0], 10 * width);
let unpacked_powers: Vec<EF> =
EF::ExtensionPacking::to_ext_iter(packed_powers_capped).collect();
assert_eq!(base_powers, unpacked_powers);
}
pub fn test_frobenius_fixes_base_field<F, EF>()
where
F: Field,
EF: ExtensionField<F> + HasFrobenius<F>,
StandardUniform: Distribution<F>,
{
let mut rng = SmallRng::seed_from_u64(1);
let base_elem: F = rng.random();
let base_elem_in_ext: EF = base_elem.into();
assert_eq!(
base_elem_in_ext.frobenius(),
base_elem_in_ext,
"Frobenius should fix base field elements"
);
assert_eq!(EF::ZERO.frobenius(), EF::ZERO);
assert_eq!(EF::ONE.frobenius(), EF::ONE);
assert_eq!(EF::TWO.frobenius(), EF::TWO);
assert_eq!(EF::NEG_ONE.frobenius(), EF::NEG_ONE);
}
pub fn test_frobenius_proptest<F, EF>()
where
F: Field,
EF: ExtensionField<F> + HasFrobenius<F> + core::fmt::Debug + 'static,
StandardUniform: Distribution<EF>,
{
let config = ProptestConfig::with_cases(256);
let arb_ef = || {
any::<u64>().prop_map(|seed| {
let mut rng = SmallRng::seed_from_u64(seed);
rng.random::<EF>()
})
};
proptest!(config, |(a in arb_ef(), b in arb_ef())| {
prop_assert_eq!(
(a + b).frobenius(),
a.frobenius() + b.frobenius(),
"Frobenius additivity"
);
prop_assert_eq!(
(a * b).frobenius(),
a.frobenius() * b.frobenius(),
"Frobenius multiplicativity"
);
});
}