use crate::scalar::{ExactFieldScalar, Fp, Fpn};
pub trait FiniteChar2Field: ExactFieldScalar + Copy {
fn characteristic_prime() -> u128 {
2
}
fn field_order() -> u128;
fn is_supported_char2_field() -> bool;
fn from_index(i: u128) -> Self;
fn artin_schreier_class(x: Self) -> u128;
fn ensure_supported() -> Option<()> {
Self::is_supported_char2_field().then_some(())
}
}
impl FiniteChar2Field for Fp<2> {
fn field_order() -> u128 {
2
}
fn is_supported_char2_field() -> bool {
Fp::<2>::modulus_is_prime()
}
fn from_index(i: u128) -> Self {
Fp::<2>::from_u128(i)
}
fn artin_schreier_class(x: Self) -> u128 {
x.value() & 1
}
}
impl<const N: usize> FiniteChar2Field for Fpn<2, N> {
fn field_order() -> u128 {
Fpn::<2, N>::field_order()
}
fn is_supported_char2_field() -> bool {
Fpn::<2, N>::is_supported_field()
}
fn from_index(i: u128) -> Self {
let mut digits = [0u128; N];
let mut x = i;
for d in digits.iter_mut() {
*d = x & 1;
x >>= 1;
}
Fpn::<2, N>::from_coeffs(&digits)
}
fn artin_schreier_class(x: Self) -> u128 {
use crate::scalar::FieldExtension;
x.trace().value()
}
}
pub fn artin_schreier_class_finite<F: FiniteChar2Field>(x: F) -> u128 {
assert!(
F::is_supported_char2_field(),
"characteristic-2 finite-field form theory needs a supported char-2 field"
);
F::artin_schreier_class(x)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::scalar::Scalar;
#[test]
fn f2_class_is_the_identity() {
assert_eq!(Fp::<2>::artin_schreier_class(Fp::<2>::from_int(0)), 0);
assert_eq!(Fp::<2>::artin_schreier_class(Fp::<2>::from_int(1)), 1);
assert_eq!(Fp::<2>::field_order(), 2);
assert!(Fp::<2>::is_supported_char2_field());
}
#[test]
fn f4_artin_schreier_class_matches_solvability() {
type F4 = Fpn<2, 2>;
let expect = [(0u128, 0u128), (1, 0), (2, 1), (3, 1)]; for (i, c) in expect {
assert_eq!(F4::artin_schreier_class(F4::from_index(i)), c, "index {i}");
}
assert_eq!(F4::field_order(), 4);
for i in 0..4u128 {
let x = F4::from_index(i);
let solvable = (0..4u128)
.map(F4::from_index)
.any(|y| y.mul(&y).add(&y) == x);
assert_eq!(
F4::artin_schreier_class(x) == 0,
solvable,
"AS solvability {i}"
);
}
}
#[test]
fn class_is_f2_linear() {
type F8 = Fpn<2, 3>;
for i in 0..8u128 {
for j in 0..8u128 {
let (x, y) = (F8::from_index(i), F8::from_index(j));
assert_eq!(
F8::artin_schreier_class(x.add(&y)),
F8::artin_schreier_class(x) ^ F8::artin_schreier_class(y),
"additivity at ({i},{j})"
);
}
}
let ones = (0..8u128)
.filter(|&i| F8::artin_schreier_class(F8::from_index(i)) == 1)
.count();
assert_eq!(ones, 4);
}
#[test]
fn generic_helper_is_usable() {
fn class_one_count<F: FiniteChar2Field>() -> u128 {
(0..F::field_order())
.filter(|&i| artin_schreier_class_finite(F::from_index(i)) == 1)
.count() as u128
}
assert_eq!(class_one_count::<Fp<2>>(), Fp::<2>::field_order() / 2);
assert_eq!(
class_one_count::<Fpn<2, 2>>(),
Fpn::<2, 2>::field_order() / 2
);
assert_eq!(
class_one_count::<Fpn<2, 3>>(),
Fpn::<2, 3>::field_order() / 2
);
}
}