use alloc::vec::Vec;
use p3_binary_field::TowerLevel;
#[must_use]
pub fn domain_point<F: TowerLevel>(index: usize) -> F {
let mut point = F::ZERO;
let mut remaining = index;
let mut r = 0;
while remaining != 0 {
if remaining & 1 == 1 {
point += F::cantor_basis(r);
}
remaining >>= 1;
r += 1;
}
point
}
#[must_use]
pub fn domain_point_steps<F: TowerLevel>(count: usize) -> Vec<F> {
let mut point = F::ZERO;
(0..count)
.map(|r| {
point += F::cantor_basis(r + 1);
point
})
.collect()
}
#[must_use]
pub fn subspace_polynomial<F: TowerLevel>(j: usize, x: F) -> F {
let mut value = x;
for _ in 0..j {
value = value.square() + value;
}
value
}
#[cfg(test)]
mod tests {
use p3_binary_field::{BinaryField8, BinaryField16, BinaryField128, TowerLevel};
use p3_field::PrimeCharacteristicRing;
use super::{domain_point, domain_point_steps, subspace_polynomial};
fn check_steps_chain_to_the_walk<F: TowerLevel>(count: usize) {
let steps = domain_point_steps::<F>(count);
let mut point = F::ZERO;
assert_eq!(point, domain_point::<F>(0));
for index in 1..1usize << count {
point += steps[index.trailing_zeros() as usize];
assert_eq!(point, domain_point::<F>(index << 1), "index={index}");
}
}
#[test]
fn domain_point_images_are_the_cantor_basis() {
for r in 0..8 {
assert_eq!(
domain_point::<BinaryField8>(1 << r),
BinaryField8::cantor_basis(r),
"r={r}"
);
}
for r in 0..usize::BITS as usize {
assert_eq!(
domain_point::<BinaryField128>(1 << r),
BinaryField128::cantor_basis(r),
"r={r}"
);
}
}
#[test]
#[should_panic = "Cantor basis index out of range"]
fn domain_point_rejects_an_index_past_the_basis() {
let _ = domain_point::<BinaryField8>(1 << 8);
}
#[test]
fn domain_point_is_linear_in_the_index() {
for a in 0..1usize << 8 {
for b in 0..1usize << 8 {
assert_eq!(
domain_point::<BinaryField8>(a ^ b),
domain_point::<BinaryField8>(a) + domain_point::<BinaryField8>(b)
);
}
}
}
#[test]
fn domain_point_steps_chain_to_the_even_domain_points() {
check_steps_chain_to_the_walk::<BinaryField8>(7);
check_steps_chain_to_the_walk::<BinaryField16>(10);
check_steps_chain_to_the_walk::<BinaryField128>(12);
}
#[test]
fn subspace_polynomial_vanishes_on_its_subspace() {
for j in 0..=8 {
for m in 0..1usize << 8 {
let value = subspace_polynomial::<BinaryField8>(j, domain_point(m));
assert_eq!(value == BinaryField8::ZERO, m < 1 << j, "j={j} m={m}");
}
}
}
#[test]
fn subspace_polynomial_shifts_the_basis() {
for j in 0..16 {
for i in j..16 {
assert_eq!(
subspace_polynomial::<BinaryField16>(j, BinaryField16::cantor_basis(i)),
BinaryField16::cantor_basis(i - j),
"j={j} i={i}"
);
}
}
}
#[test]
fn domain_points_agree_across_levels() {
for m in 0..1usize << 8 {
let small: u128 = domain_point::<BinaryField8>(m).to_repr().into();
let large: u128 = domain_point::<BinaryField128>(m).to_repr();
assert_eq!(small, large, "index {m}");
}
}
}