use crate::error::Error;
pub trait Field:
Sized
+ Clone
+ PartialEq
+ Eq
+ core::fmt::Debug
+ std::ops::Add<Output = Self>
+ std::ops::Sub<Output = Self>
+ std::ops::Mul<Output = Self>
+ std::ops::Neg<Output = Self>
{
#[must_use]
fn zero() -> Self;
#[must_use]
fn one() -> Self;
fn inv(&self) -> Result<Self, Error>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct F101(u64);
impl F101 {
#[must_use]
pub fn new(value: u64) -> Self {
Self(value % 101)
}
#[must_use]
pub fn value(self) -> u64 {
self.0
}
}
impl core::fmt::Display for F101 {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.0)
}
}
impl std::ops::Add for F101 {
type Output = Self;
fn add(self, rhs: Self) -> Self {
Self((self.0 + rhs.0) % 101)
}
}
impl std::ops::Sub for F101 {
type Output = Self;
fn sub(self, rhs: Self) -> Self {
Self((self.0 + 101 - rhs.0) % 101)
}
}
impl std::ops::Mul for F101 {
type Output = Self;
fn mul(self, rhs: Self) -> Self {
Self((self.0 * rhs.0) % 101)
}
}
impl std::ops::Neg for F101 {
type Output = Self;
fn neg(self) -> Self {
Self((101 - self.0) % 101)
}
}
impl Field for F101 {
fn zero() -> Self {
Self(0)
}
fn one() -> Self {
Self(1)
}
fn inv(&self) -> Result<Self, Error> {
if self.0 == 0 {
Err(Error::DivisionByZero)
} else {
Ok(pow_mod(self.0, 99, 101))
}
}
}
fn pow_mod(base: u64, exp: u64, modulus: u64) -> F101 {
let result = (0..64)
.filter(|i| (exp >> i) & 1 == 1)
.fold((1u64, base), |(acc, _), i| {
let power = (0..i).fold(base, |b, _| (b * b) % modulus);
((acc * power) % modulus, base)
})
.0;
F101(result % modulus)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn zero_is_additive_identity() {
let a = F101::new(42);
assert_eq!(a + F101::zero(), a);
assert_eq!(F101::zero() + a, a);
}
#[test]
fn one_is_multiplicative_identity() {
let a = F101::new(42);
assert_eq!(a * F101::one(), a);
assert_eq!(F101::one() * a, a);
}
#[test]
fn additive_inverse() {
let a = F101::new(42);
assert_eq!(a + (-a), F101::zero());
}
#[test]
fn multiplicative_inverse() -> Result<(), Error> {
let a = F101::new(42);
let a_inv = a.inv()?;
assert_eq!(a * a_inv, F101::one());
Ok(())
}
#[test]
fn inverse_of_zero_fails() {
let result = F101::zero().inv();
assert!(result.is_err());
}
#[test]
fn all_nonzero_elements_have_inverses() -> Result<(), Error> {
for i in 1..101 {
let a = F101::new(i);
let a_inv = a.inv()?;
assert_eq!(a * a_inv, F101::one(), "failed for {i}");
}
Ok(())
}
#[test]
fn subtraction_is_add_neg() {
let a = F101::new(50);
let b = F101::new(30);
assert_eq!(a - b, a + (-b));
}
#[test]
fn multiplication_is_commutative() {
let a = F101::new(7);
let b = F101::new(13);
assert_eq!(a * b, b * a);
}
#[test]
fn distributivity() {
let a = F101::new(3);
let b = F101::new(5);
let c = F101::new(7);
assert_eq!(a * (b + c), a * b + a * c);
}
#[test]
fn new_reduces_mod_101() {
assert_eq!(F101::new(202), F101::new(0));
assert_eq!(F101::new(103), F101::new(2));
}
}