use crate::scalar::{mod_inverse_u128, Fp, Rational, Scalar};
use std::fmt;
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct Qp<const P: u128, const K: u128> {
unit: u128,
val: i128,
}
fn p_pow<const P: u128>(e: u128) -> u128 {
let mut acc = 1u128;
for _ in 0..e {
acc = acc.checked_mul(P).expect("Qp: p-power exceeds u128");
}
acc
}
impl<const P: u128, const K: u128> Qp<P, K> {
pub fn assert_supported_params() {
assert!(
Fp::<P>::modulus_is_prime() && K > 0,
"Qp<P,K> needs prime P and positive precision K, got P={P}, K={K}"
);
let mut acc = 1u128;
for _ in 0..K {
acc = acc.checked_mul(P).expect("Qp modulus exceeds u128");
assert!(
acc <= i128::MAX as u128,
"Qp<P,K> modulus must fit i128-backed embeddings, got P={P}, K={K}"
);
}
}
pub fn modulus() -> u128 {
Self::assert_supported_params();
p_pow::<P>(K)
}
fn normalized(unit_raw: u128, val: i128) -> Self {
let m = Self::modulus();
let mut u = unit_raw % m;
if u == 0 {
return Qp { unit: 0, val: 0 };
}
let mut v = val;
while u.is_multiple_of(P) {
u /= P;
v += 1;
}
Qp { unit: u, val: v }
}
pub fn from_int(n: i128) -> Self {
Self::assert_supported_params();
if n == 0 {
return Qp { unit: 0, val: 0 };
}
let pp = P as i128;
let mut w = 0i128;
let mut nn = n;
while nn % pp == 0 {
nn /= pp;
w += 1;
}
let m = Self::modulus() as i128;
let unit = (((nn % m) + m) % m) as u128;
Qp { unit, val: w }
}
pub fn from_p_power(v: i128) -> Self {
Self::assert_supported_params();
Qp {
unit: 1 % Self::modulus(),
val: v,
}
}
pub fn from_rational(q: &Rational) -> Self {
let num = Self::from_int(q.numer());
let den = Self::from_int(q.denom());
num.mul(&den.inv().expect("Qp::from_rational: nonzero denominator"))
}
pub fn valuation(&self) -> Option<i128> {
Self::assert_supported_params();
if self.unit == 0 {
None
} else {
Some(self.val)
}
}
pub fn unit(&self) -> u128 {
Self::assert_supported_params();
self.unit
}
}
impl<const P: u128, const K: u128> fmt::Display for Qp<P, K> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.unit == 0 {
return write!(f, "0 (Q_{})", P);
}
if self.val == 0 {
write!(f, "{} (mod {}^{})", self.unit, P, K)
} else {
write!(f, "{}·{}^{} (mod {}^{})", self.unit, P, self.val, P, K)
}
}
}
impl<const P: u128, const K: u128> fmt::Debug for Qp<P, K> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl<const P: u128, const K: u128> Scalar for Qp<P, K> {
fn zero() -> Self {
Self::assert_supported_params();
Qp { unit: 0, val: 0 }
}
fn one() -> Self {
Self::assert_supported_params();
Qp {
unit: 1 % Self::modulus(),
val: 0,
}
}
fn add(&self, rhs: &Self) -> Self {
Self::assert_supported_params();
if self.unit == 0 {
return *rhs;
}
if rhs.unit == 0 {
return *self;
}
let m = Self::modulus();
let (lo, hi) = if self.val <= rhs.val {
(self, rhs)
} else {
(rhs, self)
};
let d = (hi.val - lo.val) as u128;
let shifted = if d >= K {
0 } else {
crate::scalar::mul_mod_u128(p_pow::<P>(d), hi.unit, m)
};
let b = lo
.unit
.checked_add(shifted)
.expect("Qp addition mantissa sum exceeds u128")
% m;
if b == 0 {
return Qp { unit: 0, val: 0 }; }
Self::normalized(b, lo.val)
}
fn neg(&self) -> Self {
Self::assert_supported_params();
if self.unit == 0 {
return *self;
}
Qp {
unit: Self::modulus() - self.unit,
val: self.val,
}
}
fn mul(&self, rhs: &Self) -> Self {
Self::assert_supported_params();
if self.unit == 0 || rhs.unit == 0 {
return Qp { unit: 0, val: 0 };
}
let m = Self::modulus();
Qp {
unit: crate::scalar::mul_mod_u128(self.unit, rhs.unit, m),
val: self
.val
.checked_add(rhs.val)
.expect("Qp multiplication valuation exceeds i128"),
}
}
fn characteristic() -> u128 {
Self::assert_supported_params();
0 }
fn inv(&self) -> Option<Self> {
Self::assert_supported_params();
if self.unit == 0 {
return None;
}
let uinv = mod_inverse_u128(self.unit, Self::modulus())?;
Some(Qp {
unit: uinv,
val: self
.val
.checked_neg()
.expect("Qp inversion valuation negation exceeds i128"),
})
}
fn from_int(n: i128) -> Self {
Qp::<P, K>::from_int(n)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::scalar::Scalar;
type Q5 = Qp<5, 4>; type Q2 = Qp<2, 6>; type Q3 = Qp<3, 3>;
#[test]
fn one_over_p_exists_and_is_a_field() {
let p = Q5::from_int(5);
let pinv = p.inv().unwrap();
assert_eq!(pinv, Q5::from_p_power(-1));
assert_eq!(p.mul(&pinv), Q5::one());
assert_eq!(pinv.valuation(), Some(-1));
assert_eq!(Q5::zero().inv(), None);
}
#[test]
fn h2_large_precision_mul_does_not_overflow() {
type Q3Big = Qp<3, 80>; let x = Q3Big::from_int(-1); assert_eq!(x.mul(&x), Q3Big::one()); let _ = x.mul(&Q3Big::from_int(7)); let _ = x.add(&x); }
#[test]
fn from_rational_matches_integer_embedding_and_denominator_inverse() {
let x = Q5::from_rational(&Rational::new(50, 3));
let expected = Q5::from_int(50).mul(&Q5::from_int(3).inv().unwrap());
assert_eq!(x, expected);
assert_eq!(x.valuation(), Some(2));
let y = Q5::from_rational(&Rational::new(3, 50));
assert_eq!(y.valuation(), Some(-2));
assert_eq!(x.mul(&y), Q5::one());
}
#[test]
fn every_nonzero_inverts() {
for u in 1..Q3::modulus() {
if u % 3 == 0 {
continue; }
for v in -2i128..=2 {
let x = Qp::<3, 3>::normalized(u, v);
let xi = x.inv().expect("Q_p: every nonzero inverts");
assert_eq!(x.mul(&xi), Q3::one(), "x·x⁻¹ ≠ 1 for {x:?}");
}
}
}
#[test]
fn valuation_is_additive_under_multiplication() {
let a = Q2::from_int(12); let b = Q2::from_int(20); assert_eq!(a.valuation(), Some(2));
assert_eq!(b.valuation(), Some(2));
assert_eq!(a.mul(&b).valuation(), Some(4));
assert_eq!(a.mul(&Q2::from_p_power(-1)).valuation(), Some(1));
}
#[test]
fn characteristic_is_zero_not_the_modulus() {
assert_eq!(Q5::characteristic(), 0);
assert_eq!(Q2::characteristic(), 0);
}
#[test]
fn invalid_parameters_are_rejected() {
assert!(std::panic::catch_unwind(Qp::<4, 3>::one).is_err());
assert!(std::panic::catch_unwind(Qp::<5, 0>::one).is_err());
assert!(std::panic::catch_unwind(Qp::<2, 127>::one).is_err());
}
#[test]
fn multiplication_is_an_exact_abelian_group_on_nonzero() {
let es: Vec<Q3> = {
let mut v = Vec::new();
for u in (1..Q3::modulus()).filter(|u| u % 3 != 0) {
for val in -2i128..=2 {
v.push(Qp::<3, 3>::normalized(u, val));
}
}
v
};
let one = Q3::one();
for a in &es {
assert_eq!(a.mul(&one), *a);
assert_eq!(a.mul(&a.inv().unwrap()), one);
for b in &es {
assert_eq!(a.mul(b), b.mul(a)); for c in &es {
assert_eq!(a.mul(b).mul(c), a.mul(&b.mul(c))); }
}
}
}
#[test]
fn addition_exact_facts_hold() {
let es: Vec<Q3> = (0..Q3::modulus() as i128)
.map(Qp::<3, 3>::from_int)
.collect();
let zero = Q3::zero();
for a in &es {
assert_eq!(a.add(&zero), *a);
assert_eq!(a.add(&a.neg()), zero);
for b in &es {
assert_eq!(a.add(b), b.add(a)); }
}
}
#[test]
fn relative_precision_cancellation_is_intended() {
let one = Q3::one();
let almost = Qp::<3, 3>::from_int(Q3::modulus() as i128 - 1); assert_eq!(one.add(&almost), Q3::zero());
assert_eq!(one.add(&Qp::<3, 3>::from_int(3)), Qp::<3, 3>::from_int(4));
}
#[test]
fn p_times_one_over_p_is_one_each_prime() {
assert_eq!(Q2::from_int(2).mul(&Q2::from_p_power(-1)), Q2::one());
assert_eq!(Q3::from_int(3).mul(&Q3::from_p_power(-1)), Q3::one());
assert_eq!(Q5::from_int(5).mul(&Q5::from_p_power(-1)), Q5::one());
}
}