use crate::scalar::{is_prime_u128, Scalar};
use std::fmt;
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct Fp<const P: u128>(u128);
pub(crate) fn add_mod<const P: u128>(a: u128, b: u128) -> u128 {
debug_assert!(P > 0 && a < P && b < P);
if a >= P - b {
a - (P - b)
} else {
a + b
}
}
pub(crate) fn mul_mod<const P: u128>(mut a: u128, mut b: u128) -> u128 {
debug_assert!(P > 0 && a < P && b < P);
let mut acc = 0u128;
while b > 0 {
if b & 1 == 1 {
acc = add_mod::<P>(acc, a);
}
b >>= 1;
if b > 0 {
a = add_mod::<P>(a, a);
}
}
acc
}
impl<const P: u128> Fp<P> {
pub fn modulus_is_prime() -> bool {
is_prime_u128(P)
}
pub fn assert_supported_params() {
assert!(Self::modulus_is_prime(), "Fp<P> needs prime P, got {P}");
}
pub fn from_u128(n: u128) -> Self {
Self::assert_supported_params();
Fp(n % P)
}
pub fn value(self) -> u128 {
self.0
}
}
impl<const P: u128> fmt::Display for Fp<P> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl<const P: u128> fmt::Debug for Fp<P> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl<const P: u128> Scalar for Fp<P> {
fn zero() -> Self {
Self::assert_supported_params();
Fp(0)
}
fn one() -> Self {
Self::assert_supported_params();
Fp(1 % P)
}
fn add(&self, rhs: &Self) -> Self {
Self::assert_supported_params();
Fp(add_mod::<P>(self.0, rhs.0))
}
fn neg(&self) -> Self {
Self::assert_supported_params();
if self.0 == 0 {
Fp(0)
} else {
Fp(P - self.0)
}
}
fn mul(&self, rhs: &Self) -> Self {
Self::assert_supported_params();
Fp(mul_mod::<P>(self.0, rhs.0))
}
fn characteristic() -> u128 {
Self::assert_supported_params();
P
}
fn inv(&self) -> Option<Self> {
Self::assert_supported_params();
if self.0 == 0 {
return None;
}
Some(self.pow(P - 2))
}
fn from_int(n: i128) -> Self {
Self::assert_supported_params();
let v = if n >= 0 {
(n as u128) % P
} else {
let r = n.unsigned_abs() % P;
if r == 0 {
0
} else {
P - r
}
};
Fp(v)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::clifford::{CliffordAlgebra, Metric};
fn elems<const P: u128>() -> Vec<Fp<P>> {
(0..P).map(Fp::<P>::from_u128).collect()
}
fn check_field_axioms<const P: u128>() {
let es = elems::<P>();
for &a in &es {
for &b in &es {
assert_eq!(a.add(&b), b.add(&a));
assert_eq!(a.mul(&b), b.mul(&a));
for &c in &es {
assert_eq!(a.add(&b).add(&c), a.add(&b.add(&c)));
assert_eq!(a.mul(&b).mul(&c), a.mul(&b.mul(&c)));
assert_eq!(a.mul(&b.add(&c)), a.mul(&b).add(&a.mul(&c)));
}
}
assert_eq!(a.add(&Fp::<P>::zero()), a);
assert_eq!(a.add(&a.neg()), Fp::<P>::zero());
if !a.is_zero() {
let ai = a.inv().expect("nonzero is invertible in a field");
assert_eq!(a.mul(&ai), Fp::<P>::one());
} else {
assert!(a.inv().is_none());
}
}
}
#[test]
fn field_axioms_f5_f7() {
check_field_axioms::<5>();
check_field_axioms::<7>();
check_field_axioms::<13>();
}
#[test]
fn inverse_matches_brute_force() {
for a in elems::<11>() {
let brute = elems::<11>()
.into_iter()
.find(|b| a.mul(b) == Fp::<11>::one());
assert_eq!(a.inv(), brute);
}
}
#[test]
fn negation_is_genuine() {
let one = Fp::<5>::one();
assert_eq!(one.neg(), Fp::<5>::from_u128(4));
assert_ne!(one.neg(), one);
assert_eq!(Fp::<5>::from_int(-1), Fp::<5>::from_u128(4));
assert_eq!(Fp::<5>::characteristic(), 5);
}
#[test]
fn clifford_over_f3_monomorphises() {
let alg = CliffordAlgebra::new(
2,
Metric::diagonal(vec![Fp::<3>::from_u128(1), Fp::<3>::from_u128(2)]),
);
let (e0, e1) = (alg.e(0), alg.e(1));
assert_eq!(alg.mul(&e0, &e0), alg.scalar(Fp::<3>::from_u128(1)));
assert_eq!(alg.mul(&e1, &e1), alg.scalar(Fp::<3>::from_u128(2)));
assert_eq!(
alg.mul(&e0, &e1),
alg.scalar_mul(&Fp::<3>::from_int(-1), &alg.mul(&e1, &e0))
);
let e0e1 = alg.mul(&e0, &e1);
assert_eq!(alg.mul(&e0e1, &e0e1), alg.scalar(Fp::<3>::from_u128(1)));
}
#[test]
fn composite_modulus_is_rejected() {
assert!(std::panic::catch_unwind(Fp::<4>::one).is_err());
assert!(std::panic::catch_unwind(|| Fp::<9>::from_int(2)).is_err());
}
}