use crate::scalar::{
mul_mod_u128, ExactFieldScalar, Integer, Omnific, Poly, Qp, Qq, Rational, RationalFunction,
Scalar, Surcomplex, Surreal, WittVec, Zp,
};
pub trait HasFractionField: Scalar {
type Frac: Scalar;
fn to_fraction(&self) -> Self::Frac;
}
pub trait HasRingOfIntegers: Scalar {
type Int: HasFractionField<Frac = Self>;
fn is_integral(&self) -> bool;
fn to_integer(&self) -> Option<Self::Int>;
}
impl HasFractionField for Integer {
type Frac = Rational;
fn to_fraction(&self) -> Rational {
Rational::from_int(self.0)
}
}
impl HasRingOfIntegers for Rational {
type Int = Integer;
fn is_integral(&self) -> bool {
self.is_integer()
}
fn to_integer(&self) -> Option<Integer> {
if self.is_integer() {
Some(Integer(self.numer()))
} else {
None
}
}
}
impl HasFractionField for Omnific {
type Frac = Surreal;
fn to_fraction(&self) -> Surreal {
self.inner().clone()
}
}
impl HasRingOfIntegers for Surreal {
type Int = Omnific;
fn is_integral(&self) -> bool {
Omnific::from_surreal(self.clone()).is_some()
}
fn to_integer(&self) -> Option<Omnific> {
Omnific::from_surreal(self.clone())
}
}
impl<const P: u128, const K: u128> HasFractionField for Zp<P, K> {
type Frac = Qp<P, K>;
fn to_fraction(&self) -> Qp<P, K> {
Qp::from_int((self.0 % Zp::<P, K>::modulus()) as i128)
}
}
impl<const P: u128, const K: u128> HasRingOfIntegers for Qp<P, K> {
type Int = Zp<P, K>;
fn is_integral(&self) -> bool {
self.valuation().is_none_or(|v| v >= 0)
}
fn to_integer(&self) -> Option<Zp<P, K>> {
let Some(v) = self.valuation() else {
return Some(Zp(0)); };
if v < 0 {
return None;
}
let m = Qp::<P, K>::modulus();
let mut acc = self.unit() % m;
for _ in 0..v {
acc = mul_mod_u128(acc, P % m, m);
}
Some(Zp(acc))
}
}
impl<const P: u128, const N: usize, const F: usize> HasFractionField for WittVec<P, N, F> {
type Frac = Qq<P, N, F>;
fn to_fraction(&self) -> Qq<P, N, F> {
Qq::from_witt(*self)
}
}
impl<const P: u128, const N: usize, const F: usize> HasRingOfIntegers for Qq<P, N, F> {
type Int = WittVec<P, N, F>;
fn is_integral(&self) -> bool {
self.valuation().is_none_or(|v| v >= 0)
}
fn to_integer(&self) -> Option<WittVec<P, N, F>> {
let Some(v) = self.valuation() else {
return Some(WittVec::zero()); };
if v < 0 {
return None;
}
let p = WittVec::<P, N, F>::from_int(P as i128);
let mut acc = self.unit();
for _ in 0..v {
acc = acc.mul(&p);
}
Some(acc)
}
}
impl<S: ExactFieldScalar> HasFractionField for Poly<S> {
type Frac = RationalFunction<S>;
fn to_fraction(&self) -> RationalFunction<S> {
RationalFunction::from_poly(self.clone())
}
}
impl<S: ExactFieldScalar> HasRingOfIntegers for RationalFunction<S> {
type Int = Poly<S>;
fn is_integral(&self) -> bool {
self.num().rem(self.den()).is_zero()
}
fn to_integer(&self) -> Option<Poly<S>> {
let (quot, rem) = self.num().divrem(self.den());
rem.is_zero().then_some(quot)
}
}
impl<R: HasFractionField> HasFractionField for Surcomplex<R> {
type Frac = Surcomplex<R::Frac>;
fn to_fraction(&self) -> Surcomplex<R::Frac> {
Surcomplex::new(self.re.to_fraction(), self.im.to_fraction())
}
}
impl<K: HasRingOfIntegers> HasRingOfIntegers for Surcomplex<K> {
type Int = Surcomplex<K::Int>;
fn is_integral(&self) -> bool {
self.re.is_integral() && self.im.is_integral()
}
fn to_integer(&self) -> Option<Surcomplex<K::Int>> {
Some(Surcomplex::new(
self.re.to_integer()?,
self.im.to_integer()?,
))
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::fmt::Debug;
fn assert_pairs<R>(r: &R)
where
R: HasFractionField + PartialEq + Debug,
R::Frac: HasRingOfIntegers<Int = R>,
{
let x = r.to_fraction();
assert!(
x.is_integral(),
"embedded ring element must be integral: {x:?}"
);
assert_eq!(
x.to_integer().as_ref(),
Some(r),
"frac∘int round-trip failed"
);
}
#[test]
fn integer_rational_pairing() {
for n in -6i128..=6 {
assert_pairs(&Integer(n));
}
let half = Rational::new(1, 2);
assert!(!half.is_integral());
assert_eq!(half.to_integer(), None);
assert!(Rational::from_int(4).is_integral());
assert_eq!(Rational::from_int(4).to_integer(), Some(Integer(4)));
}
#[test]
fn omnific_surreal_pairing() {
assert_pairs(&Omnific::from_int(3));
assert_pairs(&Omnific::omega()); assert_pairs(&Omnific::from_surreal(Surreal::omega_pow(Surreal::from_int(2))).unwrap());
assert!(!Surreal::epsilon().is_integral());
assert_eq!(Surreal::epsilon().to_integer(), None);
assert!(!Surreal::from_rational(Rational::new(1, 2)).is_integral());
}
#[test]
fn zp_qp_pairing() {
for r in 0..27u128 {
assert_pairs(&Zp::<3, 3>(r));
}
let inv_p = Qp::<3, 3>::from_p_power(-1);
assert!(!inv_p.is_integral());
assert_eq!(inv_p.to_integer(), None);
let p = Qp::<3, 3>::from_int(3);
assert!(p.is_integral());
assert_eq!(p.to_integer(), Some(Zp::<3, 3>(3)));
}
#[test]
fn qp_to_integer_uses_modular_multiplication_at_the_boundary() {
type Q = Qp<3, 80>;
let x = Q::from_int(-1).mul(&Q::from_int(3));
assert_eq!(x.valuation(), Some(1));
assert_eq!(x.to_integer(), Some(Zp::<3, 80>(Q::modulus() - 3)));
}
#[test]
fn wittvec_qq_pairing() {
for code in 0..16u128 {
assert_pairs(&WittVec::<2, 2, 2>([code & 3, (code >> 2) & 3]));
}
let inv_p = Qq::<2, 4, 2>::from_p_power(-1);
assert!(!inv_p.is_integral());
assert_eq!(inv_p.to_integer(), None);
let u = WittVec::<2, 4, 2>([1, 1]);
assert!(u.to_fraction().is_integral());
assert_eq!(u.to_fraction().to_integer(), Some(u));
}
#[test]
fn poly_rational_function_pairing() {
use crate::scalar::Fp;
type P = Poly<Fp<5>>;
let samples = [
P::constant(Fp::<5>::from_int(3)),
P::t(),
Poly::new(vec![
Fp::<5>::from_int(1),
Fp::<5>::from_int(0),
Fp::<5>::from_int(2),
]), ];
for p in &samples {
assert_pairs(p);
}
let inv_t = RationalFunction::<Fp<5>>::t().inv().unwrap();
assert!(!inv_t.is_integral());
assert_eq!(inv_t.to_integer(), None);
let t2_over_t = RationalFunction::new(
vec![
Fp::<5>::from_int(0),
Fp::<5>::from_int(0),
Fp::<5>::from_int(1),
],
vec![Fp::<5>::from_int(0), Fp::<5>::from_int(1)],
);
assert!(t2_over_t.is_integral());
assert_eq!(t2_over_t.to_integer(), Some(P::t()));
}
#[test]
fn surcomplex_transports_the_gaussian_pairing() {
for re in -3i128..=3 {
for im in -3i128..=3 {
assert_pairs(&Surcomplex::new(Integer(re), Integer(im)));
}
}
let half_i = Surcomplex::new(Rational::from_int(0), Rational::new(1, 2));
assert!(!half_i.is_integral());
assert_eq!(half_i.to_integer(), None);
let g = Surcomplex::new(Rational::from_int(3), Rational::from_int(-2));
assert!(g.is_integral());
assert_eq!(
g.to_integer(),
Some(Surcomplex::new(Integer(3), Integer(-2)))
);
}
}