use crate::scalar::{ExactFieldScalar, Fp, Fpn, Laurent, Qp, Qq, Scalar, Valued};
pub trait ResidueField: Valued {
type Residue: ExactFieldScalar;
fn residue(&self) -> Option<Self::Residue>;
fn residue_unit(&self) -> Option<Self::Residue>;
fn teichmuller(residue: Self::Residue) -> Self;
}
impl<const P: u128, const K: u128> ResidueField for Qp<P, K> {
type Residue = Fp<P>;
fn residue(&self) -> Option<Fp<P>> {
match self.valuation() {
None => Some(Fp::<P>::zero()), Some(v) if v < 0 => None, Some(0) => Some(Fp::<P>::from_u128(self.unit() % P)),
Some(_) => Some(Fp::<P>::zero()), }
}
fn residue_unit(&self) -> Option<Fp<P>> {
self.valuation()
.map(|_| Fp::<P>::from_u128(self.unit() % P))
}
fn teichmuller(residue: Fp<P>) -> Self {
Qp::teichmuller(residue)
}
}
impl<const P: u128, const N: usize, const F: usize> ResidueField for Qq<P, N, F> {
type Residue = Fpn<P, F>;
fn residue(&self) -> Option<Fpn<P, F>> {
match self.valuation() {
None => Some(Fpn::<P, F>::zero()),
Some(v) if v < 0 => None,
Some(0) => self.unit_residue(),
Some(_) => Some(Fpn::<P, F>::zero()),
}
}
fn residue_unit(&self) -> Option<Fpn<P, F>> {
self.unit_residue()
}
fn teichmuller(residue: Fpn<P, F>) -> Self {
Qq::teichmuller(residue)
}
}
impl<S: ExactFieldScalar, const K: usize> ResidueField for Laurent<S, K> {
type Residue = S;
fn residue(&self) -> Option<S> {
match self.valuation() {
None => Some(S::zero()),
Some(v) if v < 0 => None,
Some(0) => self.leading_coeff(), Some(_) => Some(S::zero()),
}
}
fn residue_unit(&self) -> Option<S> {
self.leading_coeff()
}
fn teichmuller(residue: S) -> Self {
Laurent::from_base(residue)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::scalar::{Fpn, Rational};
#[test]
fn qp_residue_and_angular_component() {
type Q5 = Qp<5, 4>;
let u = Q5::from_int(7); assert_eq!(u.residue(), Some(Fp::<5>::from_int(2)));
assert_eq!(u.residue_unit(), Some(Fp::<5>::from_int(2)));
let p = Q5::from_int(5); assert_eq!(p.valuation(), Some(1));
assert_eq!(p.residue(), Some(Fp::<5>::zero()));
assert_eq!(p.residue_unit(), Some(Fp::<5>::from_int(1)));
let inv_p = Q5::from_p_power(-1);
assert_eq!(inv_p.residue(), None);
assert_eq!(inv_p.residue_unit(), Some(Fp::<5>::from_int(1)));
assert_eq!(Q5::zero().residue(), Some(Fp::<5>::zero()));
assert_eq!(Q5::zero().residue_unit(), None);
}
#[test]
fn qq_residue_lands_in_the_extension_field() {
type Q9 = Qq<3, 3, 2>;
let g = Q9::from_witt(crate::scalar::WittVec::<3, 3, 2>([0, 1])); assert_eq!(g.residue(), Some(Fpn::<3, 2>::from_coeffs(&[0, 1])));
assert_eq!(g.residue_unit(), Some(Fpn::<3, 2>::from_coeffs(&[0, 1])));
let pg = g.mul(&Q9::from_int(3));
assert_eq!(pg.valuation(), Some(1));
assert_eq!(pg.residue(), Some(Fpn::<3, 2>::zero()));
assert_eq!(pg.residue_unit(), Some(Fpn::<3, 2>::from_coeffs(&[0, 1])));
}
#[test]
fn laurent_residue_is_evaluation_at_zero() {
type L = Laurent<Rational, 6>;
let r = |n: i128| Rational::from_int(n);
let a = Laurent::<Rational, 6>::from_coeffs(vec![r(3), r(2)], 0);
assert_eq!(a.residue(), Some(r(3)));
assert_eq!(a.residue_unit(), Some(r(3)));
let b = Laurent::<Rational, 6>::from_coeffs(vec![r(5), r(2)], 1);
assert_eq!(b.valuation(), Some(1));
assert_eq!(b.residue(), Some(Rational::zero()));
assert_eq!(b.residue_unit(), Some(r(5)));
assert_eq!(L::t().inv().unwrap().residue(), None);
assert_eq!(L::t().inv().unwrap().residue_unit(), Some(r(1)));
}
#[test]
fn residue_field_is_generic() {
fn angular_is_some_for_nonzero<K: ResidueField>(x: &K) {
if !x.is_zero() {
assert!(x.residue_unit().is_some());
}
}
angular_is_some_for_nonzero(&Qp::<7, 3>::from_int(14));
angular_is_some_for_nonzero(&Laurent::<Rational, 6>::t());
}
#[test]
fn teichmuller_section_lifts_residues() {
for a in 0..5u128 {
let r = Fp::<5>::from_u128(a);
let t = <Qp<5, 4> as ResidueField>::teichmuller(r);
assert_eq!(t.residue(), Some(r));
}
type Q9 = Qq<3, 3, 2>;
let g = Fpn::<3, 2>::from_coeffs(&[0, 1]);
let tg = <Q9 as ResidueField>::teichmuller(g);
assert_eq!(tg.residue(), Some(g));
let a = Rational::new(3, 2);
let ta = <Laurent<Rational, 6> as ResidueField>::teichmuller(a.clone());
assert_eq!(ta.residue(), Some(a));
}
#[test]
fn teichmuller_section_is_multiplicative_not_additive() {
type Q5 = Qp<5, 4>;
let a = Fp::<5>::from_u128(1);
let b = Fp::<5>::from_u128(1);
let ta = <Q5 as ResidueField>::teichmuller(a);
let tb = <Q5 as ResidueField>::teichmuller(b);
assert_eq!(
ta.mul(&tb).residue(),
Some(a.mul(&b)),
"Ο(ab) and Ο(a)Ο(b) reduce to the same residue"
);
let lhs = <Q5 as ResidueField>::teichmuller(a.add(&b));
let rhs = ta.add(&tb);
assert_eq!(lhs.residue(), rhs.residue());
assert_ne!(lhs, rhs, "Teichmuller lifts are not generally additive");
}
}