use crate::scalar::{
nim_square, nim_trace, Fp, Fpn, Nimber, Ordered, Qq, Scalar, Surcomplex, WittVec,
};
pub trait FieldExtension: Scalar {
type Base: Scalar;
fn extension_degree() -> usize;
fn embed(base: &Self::Base) -> Self;
fn trace(&self) -> Self::Base;
fn norm(&self) -> Self::Base;
}
impl<S: Ordered> FieldExtension for Surcomplex<S> {
type Base = S;
fn extension_degree() -> usize {
2
}
fn embed(base: &S) -> Self {
Surcomplex::new(base.clone(), S::zero())
}
fn trace(&self) -> S {
self.re.add(&self.re)
}
fn norm(&self) -> S {
self.re.mul(&self.re).add(&self.im.mul(&self.im))
}
}
impl<const P: u128, const N: usize> FieldExtension for Fpn<P, N> {
type Base = Fp<P>;
fn extension_degree() -> usize {
N
}
fn embed(base: &Fp<P>) -> Self {
Fpn::<P, N>::constant(base.value())
}
fn trace(&self) -> Fp<P> {
use crate::scalar::FiniteField;
Fp::<P>::from_u128(self.relative_trace(1).coeff(0))
}
fn norm(&self) -> Fp<P> {
use crate::scalar::FiniteField;
Fp::<P>::from_u128(self.relative_norm(1).coeff(0))
}
}
fn witt_frobenius<const P: u128, const N: usize, const F: usize>(
w: WittVec<P, N, F>,
) -> WittVec<P, N, F> {
use crate::scalar::FiniteField;
let comps: Vec<Fpn<P, F>> = w.witt_components().iter().map(|c| c.frobenius()).collect();
WittVec::<P, N, F>::from_witt_components(&comps)
}
fn qq_frobenius<const P: u128, const N: usize, const F: usize>(x: &Qq<P, N, F>) -> Qq<P, N, F> {
match x.valuation() {
None => Qq::zero(),
Some(v) => Qq::<P, N, F>::from_p_power(v).mul(&Qq::from_witt(witt_frobenius(x.unit()))),
}
}
fn qq_to_base<const P: u128, const N: usize, const F: usize>(x: &Qq<P, N, F>) -> Qq<P, N, 1> {
match x.valuation() {
None => Qq::zero(),
Some(v) => {
let u = x.unit();
debug_assert!(
u.0[1..].iter().all(|&c| c == 0),
"trace/norm of Q_q/Q_p must land in the Q_p subfield"
);
Qq::<P, N, 1>::from_p_power(v).mul(&Qq::from_witt(WittVec::<P, N, 1>([u.0[0]])))
}
}
}
impl<const P: u128, const N: usize, const F: usize> FieldExtension for Qq<P, N, F> {
type Base = Qq<P, N, 1>;
fn extension_degree() -> usize {
F
}
fn embed(base: &Qq<P, N, 1>) -> Self {
match base.valuation() {
None => Qq::zero(),
Some(v) => {
let mut arr = [0u128; F];
if F > 0 {
arr[0] = base.unit().0[0];
}
Qq::<P, N, F>::from_p_power(v).mul(&Qq::from_witt(WittVec::<P, N, F>(arr)))
}
}
}
fn trace(&self) -> Qq<P, N, 1> {
let mut conj = *self;
let mut tr = Qq::<P, N, F>::zero();
for _ in 0..F {
tr = tr.add(&conj);
conj = qq_frobenius(&conj);
}
qq_to_base(&tr)
}
fn norm(&self) -> Qq<P, N, 1> {
let mut conj = *self;
let mut nm = Qq::<P, N, F>::one();
for _ in 0..F {
nm = nm.mul(&conj);
conj = qq_frobenius(&conj);
}
qq_to_base(&nm)
}
}
impl FieldExtension for Nimber {
type Base = Fp<2>;
fn extension_degree() -> usize {
128
}
fn embed(base: &Fp<2>) -> Self {
Nimber(base.value())
}
fn trace(&self) -> Fp<2> {
Fp::<2>::from_u128(nim_trace(self.0, 128))
}
fn norm(&self) -> Fp<2> {
Fp::<2>::from_u128(u128::from(self.0 != 0))
}
}
pub trait CyclicGaloisExtension: FieldExtension {
fn basis() -> Vec<Self>;
fn sigma(&self) -> Self;
fn sigma_power(&self, k: usize) -> Self {
let mut x = self.clone();
for _ in 0..k {
x = x.sigma();
}
x
}
}
impl<S: Ordered> CyclicGaloisExtension for Surcomplex<S> {
fn basis() -> Vec<Self> {
vec![
Surcomplex::new(S::one(), S::zero()),
Surcomplex::new(S::zero(), S::one()),
]
}
fn sigma(&self) -> Self {
self.conj()
}
}
impl<const P: u128, const N: usize> CyclicGaloisExtension for Fpn<P, N> {
fn basis() -> Vec<Self> {
(0..N)
.map(|j| {
let mut a = [0u128; N];
a[j] = 1;
Fpn::<P, N>::from_coeffs(&a)
})
.collect()
}
fn sigma(&self) -> Self {
use crate::scalar::FiniteField;
self.frobenius()
}
}
impl<const P: u128, const N: usize, const F: usize> CyclicGaloisExtension for Qq<P, N, F> {
fn basis() -> Vec<Self> {
(0..F)
.map(|j| {
let mut a = [0u128; F];
a[j] = 1;
Qq::<P, N, F>::teichmuller(Fpn::<P, F>::from_coeffs(&a))
})
.collect()
}
fn sigma(&self) -> Self {
qq_frobenius(self)
}
}
impl CyclicGaloisExtension for Nimber {
fn basis() -> Vec<Self> {
(0..128).map(|i| Nimber(1u128 << i)).collect()
}
fn sigma(&self) -> Self {
Nimber(nim_square(self.0))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::scalar::{FiniteField, Rational, Surreal};
#[test]
fn gaussian_trace_and_norm() {
type G = Surcomplex<Rational>;
let z = Surcomplex::new(Rational::from_int(2), Rational::from_int(1)); assert_eq!(<G as FieldExtension>::extension_degree(), 2);
assert_eq!(z.trace(), Rational::from_int(4)); assert_eq!(z.norm(), Rational::from_int(5)); assert_eq!(z.mul(&z.conj()).re, z.norm());
assert_eq!(z.add(&z.conj()).re, z.trace());
let e = <G as FieldExtension>::embed(&Rational::from_int(3));
assert_eq!(
e,
Surcomplex::new(Rational::from_int(3), Rational::from_int(0))
);
assert_eq!(e.norm(), Rational::from_int(9));
let w = Surcomplex::new(Rational::from_int(1), Rational::from_int(-2));
assert_eq!(z.mul(&w).norm(), z.norm().mul(&w.norm()));
}
#[test]
fn surreal_complex_trace_and_norm() {
let z = Surcomplex::new(Surreal::omega(), Surreal::one());
assert_eq!(
z.norm(),
Surreal::omega().mul(&Surreal::omega()).add(&Surreal::one())
);
assert_eq!(z.trace(), Surreal::omega().add(&Surreal::omega()));
}
#[test]
fn finite_field_trace_norm_match_the_galois_machinery() {
type F9 = Fpn<3, 2>;
assert_eq!(<F9 as FieldExtension>::extension_degree(), 2);
for code in 0..9u128 {
let x = Fpn::<3, 2>::from_coeffs(&[code % 3, code / 3]);
assert_eq!(
FieldExtension::trace(&x),
Fp::<3>::from_u128(x.relative_trace(1).coeff(0))
);
assert_eq!(
FieldExtension::norm(&x),
Fp::<3>::from_u128(x.relative_norm(1).coeff(0))
);
assert_eq!(
FieldExtension::norm(&x),
Fp::<3>::from_u128(x.mul(&x.frobenius()).coeff(0))
);
}
let a = Fpn::<3, 2>::from_coeffs(&[1, 1]);
let b = Fpn::<3, 2>::from_coeffs(&[2, 1]);
assert_eq!(
FieldExtension::norm(&a.mul(&b)),
FieldExtension::norm(&a).mul(&FieldExtension::norm(&b))
);
let c = <F9 as FieldExtension>::embed(&Fp::<3>::from_u128(2));
assert_eq!(
FieldExtension::norm(&c),
Fp::<3>::from_u128(2).mul(&Fp::<3>::from_u128(2))
);
}
#[test]
fn unramified_local_trace_and_norm() {
type Q9 = Qq<3, 3, 2>; assert_eq!(<Q9 as FieldExtension>::extension_degree(), 2);
let g = Fpn::<3, 2>::from_coeffs(&[0, 1]); let x = Q9::from_witt(WittVec::<3, 3, 2>(g.into_coeffs()));
let n = FieldExtension::norm(&x); assert_eq!(
n.unit().0[0] % 3,
FieldExtension::norm(&g).value(),
"norm residue = N_{{F9/F3}}(g)"
);
type Q3 = Qq<3, 3, 1>;
let y = Q3::from_int(7);
assert_eq!(FieldExtension::norm(&y), y);
assert_eq!(FieldExtension::trace(&y), y);
let a = Q9::from_witt(WittVec::<3, 3, 2>([1, 1]));
let b = Q9::from_witt(WittVec::<3, 3, 2>([2, 1]));
assert_eq!(
FieldExtension::norm(&a.mul(&b)),
FieldExtension::norm(&a).mul(&FieldExtension::norm(&b))
);
assert_eq!(
FieldExtension::trace(&a.add(&b)),
FieldExtension::trace(&a).add(&FieldExtension::trace(&b))
);
let p = Q9::from_int(3);
assert_eq!(FieldExtension::norm(&p).valuation(), Some(2));
}
#[test]
fn field_extension_is_generic() {
fn norm_is_multiplicative<E: FieldExtension>(a: &E, b: &E)
where
E::Base: PartialEq + std::fmt::Debug,
{
assert_eq!(a.mul(b).norm(), a.norm().mul(&b.norm()));
}
norm_is_multiplicative(
&Surcomplex::new(Rational::from_int(2), Rational::from_int(1)),
&Surcomplex::new(Rational::from_int(1), Rational::from_int(3)),
);
norm_is_multiplicative(
&Fpn::<3, 2>::from_coeffs(&[1, 2]),
&Fpn::<3, 2>::from_coeffs(&[2, 2]),
);
}
#[test]
fn nimber_is_a_field_extension_of_f2() {
assert_eq!(<Nimber as FieldExtension>::extension_degree(), 128);
assert_eq!(
<Nimber as FieldExtension>::embed(&Fp::<2>::from_u128(1)),
Nimber(1)
);
assert_eq!(
<Nimber as FieldExtension>::embed(&Fp::<2>::from_u128(0)),
Nimber(0)
);
let a = Nimber(0b1011);
let b = Nimber(0b0110);
assert_eq!(
FieldExtension::trace(&a),
Fp::<2>::from_u128(nim_trace(a.0, 128))
);
assert_eq!(
FieldExtension::trace(&a.add(&b)),
FieldExtension::trace(&a).add(&FieldExtension::trace(&b))
);
assert_eq!(FieldExtension::norm(&Nimber(0)), Fp::<2>::from_u128(0));
for x in [Nimber(1), Nimber(2), Nimber(0xabc), Nimber(u128::MAX)] {
assert_eq!(
FieldExtension::norm(&x),
Fp::<2>::from_u128(1),
"norm of {x:?}"
);
}
}
#[test]
fn cyclic_galois_surcomplex() {
type G = Surcomplex<Rational>;
let basis = <G as CyclicGaloisExtension>::basis();
assert_eq!(
basis,
vec![
Surcomplex::new(Rational::from_int(1), Rational::from_int(0)),
Surcomplex::new(Rational::from_int(0), Rational::from_int(1)),
]
);
let z = Surcomplex::new(Rational::from_int(2), Rational::from_int(3));
assert_eq!(z.sigma(), z.conj()); assert_eq!(z.sigma_power(2), z); }
#[test]
fn cyclic_galois_fpn() {
type F9 = Fpn<3, 2>;
let basis = <F9 as CyclicGaloisExtension>::basis();
assert_eq!(
basis,
vec![
Fpn::<3, 2>::from_coeffs(&[1, 0]),
Fpn::<3, 2>::from_coeffs(&[0, 1])
]
);
let x = Fpn::<3, 2>::from_coeffs(&[1, 2]);
assert_eq!(x.sigma(), x.frobenius()); assert_eq!(x.sigma_power(2), x); }
#[test]
fn cyclic_galois_qq() {
type Q9 = Qq<3, 3, 2>;
let basis = <Q9 as CyclicGaloisExtension>::basis();
assert_eq!(basis.len(), 2);
assert_eq!(basis[0], Q9::one());
assert_eq!(
basis[1].unit_residue(),
Some(Fpn::<3, 2>::from_coeffs(&[0, 1]))
);
let x = Q9::teichmuller(Fpn::<3, 2>::from_coeffs(&[1, 1]));
assert_eq!(x.sigma(), qq_frobenius(&x)); assert_eq!(x.sigma_power(2), x);
let over_base = <Q9 as FieldExtension>::embed(&Qq::<3, 3, 1>::from_int(5));
assert_eq!(over_base.sigma(), over_base); }
#[test]
fn cyclic_galois_nimber() {
let basis = <Nimber as CyclicGaloisExtension>::basis();
assert_eq!(basis.len(), 128);
assert_eq!(basis[0], Nimber(1));
assert_eq!(basis[7], Nimber(128)); let x = Nimber(0b1101);
assert_eq!(x.sigma(), Nimber(nim_square(x.0))); assert_eq!(x.sigma_power(128), x); }
}