use alloc::vec::Vec;
use num_bigint::BigUint;
use p3_field::{Field, HasSubfield};
use rand::distr::{Distribution, StandardUniform};
use rand::rngs::SmallRng;
use rand::{RngExt, SeedableRng};
const MAX_ENUMERATED_ORDER: u32 = 1 << 8;
const NUM_SAMPLES: usize = 64;
const SLICE_LENS: [usize; 11] = [1, 2, 3, 4, 7, 8, 9, 16, 31, 32, 65];
pub fn test_has_subfield<EF, S>()
where
EF: HasSubfield<S>,
S: Field,
StandardUniform: Distribution<EF> + Distribution<S>,
{
let mut rng = SmallRng::seed_from_u64(1);
let (subfield, enumerated) = subfield_elements::<S>(&mut rng);
let members: Vec<EF> = subfield.iter().map(|&s| EF::from(s)).collect();
let outsiders: Vec<EF> = if S::order() < EF::order() {
members.iter().map(|&m| m + EF::GENERATOR).collect()
} else {
Vec::new()
};
let num_nodes = usize::try_from(EF::order())
.unwrap_or(usize::MAX)
.min(NUM_SAMPLES);
let samples: Vec<EF> = [EF::ZERO, EF::ONE, EF::GENERATOR]
.into_iter()
.chain((0..num_nodes).map(EF::interpolation_node))
.chain((0..NUM_SAMPLES).map(|_| rng.random()))
.collect();
check_embedding::<EF, S>(&subfield);
check_membership(&subfield, &members, &outsiders, &samples, enumerated);
check_subfield_action(&subfield, &samples);
check_all_in_subfield::<EF, S>(&members, &outsiders, &samples);
}
fn subfield_elements<S: Field>(rng: &mut SmallRng) -> (Vec<S>, bool)
where
StandardUniform: Distribution<S>,
{
if S::order() > BigUint::from(MAX_ENUMERATED_ORDER) {
let elements = [S::ZERO, S::ONE, S::GENERATOR]
.into_iter()
.chain((0..NUM_SAMPLES).map(|_| rng.random()))
.collect();
return (elements, false);
}
let order = usize::try_from(S::order()).expect("the order was bounded above");
let mut elements = Vec::with_capacity(order);
elements.push(S::ZERO);
let mut power = S::ONE;
for exponent in 1..order {
elements.push(power);
power *= S::GENERATOR;
assert_eq!(
power == S::ONE,
exponent == order - 1,
"the subfield generator must have order |S| - 1, failing at exponent {exponent}"
);
}
(elements, true)
}
fn check_embedding<EF: HasSubfield<S>, S: Field>(subfield: &[S]) {
assert_eq!(EF::from(S::ZERO), EF::ZERO, "the embedding must fix zero");
assert_eq!(EF::from(S::ONE), EF::ONE, "the embedding must fix one");
for &a in subfield {
for &b in subfield {
assert_eq!(
EF::from(a + b),
EF::from(a) + EF::from(b),
"the embedding must commute with {a} + {b}"
);
assert_eq!(
EF::from(a * b),
EF::from(a) * EF::from(b),
"the embedding must commute with {a} * {b}"
);
}
}
}
fn check_membership<EF: HasSubfield<S>, S: Field>(
subfield: &[S],
members: &[EF],
outsiders: &[EF],
samples: &[EF],
enumerated: bool,
) {
for (&s, &member) in subfield.iter().zip(members) {
assert_eq!(
member.as_subfield(),
Some(s),
"the image of {s} must narrow back to it"
);
}
for &outsider in outsiders {
assert_eq!(
outsider.as_subfield(),
None,
"{outsider} lies outside the subfield"
);
}
for &x in samples {
match x.as_subfield() {
Some(s) => assert_eq!(EF::from(s), x, "{x} narrowed to {s}, whose image differs"),
None if enumerated => assert!(
!members.contains(&x),
"{x} is the image of a subfield element but did not narrow"
),
None => {}
}
}
}
fn check_subfield_action<EF: HasSubfield<S>, S: Field>(subfield: &[S], samples: &[EF]) {
for &x in samples {
for &s in subfield {
let image = EF::from(s);
assert_eq!(x + s, x + image, "the sum must agree at ({x}, {s})");
assert_eq!(x - s, x - image, "the difference must agree at ({x}, {s})");
assert_eq!(x * s, x * image, "the product must agree at ({x}, {s})");
let mut assigned = [x; 3];
assigned[0] += s;
assigned[1] -= s;
assigned[2] *= s;
assert_eq!(
assigned,
[x + image, x - image, x * image],
"the assigning operations must agree at ({x}, {s})"
);
}
}
}
fn check_all_in_subfield<EF: HasSubfield<S>, S: Field>(
members: &[EF],
outsiders: &[EF],
samples: &[EF],
) {
let narrows = |v: &EF| v.as_subfield().is_some();
let elementwise = |values: &[EF]| values.iter().all(narrows);
assert!(
EF::all_in_subfield(&[]),
"an empty slice lies in the subfield"
);
assert!(
EF::all_in_subfield(members),
"every subfield element lies in the subfield"
);
let spoilers: Vec<EF> = outsiders
.iter()
.chain(samples.iter().filter(|v| !narrows(v)))
.copied()
.collect();
for len in SLICE_LENS {
let inside: Vec<EF> = members.iter().copied().cycle().take(len).collect();
assert!(
EF::all_in_subfield(&inside),
"{len} subfield elements lie in the subfield"
);
if !spoilers.is_empty() {
for i in 0..len.max(spoilers.len()) {
let (position, spoiler) = (i % len, spoilers[i % spoilers.len()]);
let mut spoiled = inside.clone();
spoiled[position] = spoiler;
assert!(
!EF::all_in_subfield(&spoiled),
"{spoiler} at position {position} of {len} went unnoticed"
);
}
}
let random: Vec<EF> = samples.iter().copied().cycle().take(len).collect();
assert_eq!(
EF::all_in_subfield(&random),
elementwise(&random),
"all_in_subfield must agree with narrowing each of {len} random elements"
);
}
}