use num_bigint::BigInt;
use num_integer::Integer;
use num_traits::{One, Zero};
use crate::domain::Domain;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct FiniteFieldElement {
value: BigInt,
}
impl FiniteFieldElement {
pub fn value(&self) -> &BigInt {
&self.value
}
}
impl std::fmt::Display for FiniteFieldElement {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.value)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FiniteField {
prime: BigInt,
prime_minus_two: BigInt,
#[cfg(feature = "gmp")]
prime_gmp: rug::Integer,
#[cfg(feature = "gmp")]
prime_minus_two_gmp: rug::Integer,
}
impl FiniteField {
pub fn new(prime: BigInt) -> Self {
debug_assert!(prime > BigInt::one(), "modulus must be at least 2");
let prime_minus_two = &prime - BigInt::one() - BigInt::one();
#[cfg(feature = "gmp")]
{
let (sign, bytes) = prime.to_bytes_le();
let mut prime_gmp = rug::Integer::from_digits(&bytes, rug::integer::Order::Lsf);
if sign == num_bigint::Sign::Minus {
prime_gmp = -prime_gmp;
}
let (sign2, bytes2) = prime_minus_two.to_bytes_le();
let mut pmt_gmp = rug::Integer::from_digits(&bytes2, rug::integer::Order::Lsf);
if sign2 == num_bigint::Sign::Minus {
pmt_gmp = -pmt_gmp;
}
Self {
prime,
prime_minus_two,
prime_gmp,
prime_minus_two_gmp: pmt_gmp,
}
}
#[cfg(not(feature = "gmp"))]
Self {
prime,
prime_minus_two,
}
}
pub fn element(&self, value: impl Into<BigInt>) -> FiniteFieldElement {
let value = value.into();
let value = value.mod_floor(&self.prime);
FiniteFieldElement { value }
}
pub fn prime(&self) -> &BigInt {
&self.prime
}
pub fn prime_u64(&self) -> u64 {
self.prime.to_u64_digits().1[0]
}
pub fn to_i64(&self, a: &FiniteFieldElement) -> i64 {
let (_, digits) = a.value.to_u64_digits();
if digits.is_empty() {
0
} else {
digits[0] as i64
}
}
pub fn from_i64(&self, val: i64) -> FiniteFieldElement {
self.element(BigInt::from(val))
}
fn normalize(&self, value: BigInt) -> FiniteFieldElement {
FiniteFieldElement {
value: value.mod_floor(&self.prime),
}
}
#[cfg(feature = "gmp")]
fn bigint_to_rug(value: &BigInt) -> rug::Integer {
let (sign, bytes) = value.to_bytes_le();
let mut inner = rug::Integer::from_digits(&bytes, rug::integer::Order::Lsf);
if sign == num_bigint::Sign::Minus {
inner = -inner;
}
inner
}
#[cfg(feature = "gmp")]
fn rug_to_bigint(value: &rug::Integer) -> BigInt {
use num_bigint::Sign;
if *value == 0 {
return BigInt::ZERO;
}
let num_bytes = value.significant_digits::<u8>();
let mut bytes = vec![0u8; num_bytes];
value.write_digits(&mut bytes, rug::integer::Order::Lsf);
let sign = if value.is_negative() {
Sign::Minus
} else {
Sign::Plus
};
BigInt::from_bytes_le(sign, &bytes)
}
}
impl Domain for FiniteField {
type Element = FiniteFieldElement;
fn zero(&self) -> Self::Element {
self.element(BigInt::zero())
}
fn one(&self) -> Self::Element {
self.element(BigInt::one())
}
fn add(&self, a: &Self::Element, b: &Self::Element) -> Self::Element {
self.normalize(a.value.clone() + b.value.clone())
}
fn sub(&self, a: &Self::Element, b: &Self::Element) -> Self::Element {
self.normalize(a.value.clone() - b.value.clone())
}
fn neg(&self, a: &Self::Element) -> Self::Element {
self.normalize(-a.value.clone())
}
fn mul(&self, a: &Self::Element, b: &Self::Element) -> Self::Element {
self.normalize(a.value.clone() * b.value.clone())
}
fn div(&self, a: &Self::Element, b: &Self::Element) -> Option<Self::Element> {
self.inv(b).map(|inv| self.mul(a, &inv))
}
fn inv(&self, a: &Self::Element) -> Option<Self::Element> {
if a.value.is_zero() {
return None;
}
#[cfg(feature = "gmp")]
{
let base = Self::bigint_to_rug(&a.value);
let result = base
.pow_mod(&self.prime_minus_two_gmp, &self.prime_gmp)
.expect("modpow: modulus must be positive");
Some(FiniteFieldElement {
value: Self::rug_to_bigint(&result),
})
}
#[cfg(not(feature = "gmp"))]
{
Some(self.normalize(a.value.modpow(&self.prime_minus_two, &self.prime)))
}
}
fn is_zero(&self, a: &Self::Element) -> bool {
a.value.is_zero()
}
fn pow(&self, a: &Self::Element, n: u64) -> Self::Element {
if n == 0 {
return self.one();
}
if self.is_zero(a) {
return self.zero();
}
#[cfg(feature = "gmp")]
{
let base = Self::bigint_to_rug(&a.value);
let exp = rug::Integer::from(n);
let modulus = &self.prime_gmp;
let result = base
.pow_mod(&exp, modulus)
.expect("modpow: modulus must be positive");
FiniteFieldElement {
value: Self::rug_to_bigint(&result),
}
}
#[cfg(not(feature = "gmp"))]
{
let exp = BigInt::from(n);
self.normalize(a.value.modpow(&exp, &self.prime))
}
}
}
impl crate::domain::EuclideanDomain for FiniteField {
fn div_rem(
&self,
a: &Self::Element,
b: &Self::Element,
) -> Option<(Self::Element, Self::Element)> {
self.div(a, b).map(|q| (q, self.zero()))
}
fn gcd(&self, a: &Self::Element, b: &Self::Element) -> Self::Element {
if self.is_zero(a) && self.is_zero(b) {
self.zero()
} else {
self.one()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn finite_field_addition() {
let f = FiniteField::new(BigInt::from(7));
let a = f.element(3);
let b = f.element(5);
assert_eq!(f.add(&a, &b), f.element(1));
}
#[test]
fn finite_field_subtraction_wraps() {
let f = FiniteField::new(BigInt::from(7));
let a = f.element(3);
let b = f.element(5);
assert_eq!(f.sub(&a, &b), f.element(5));
}
#[test]
fn finite_field_multiplication() {
let f = FiniteField::new(BigInt::from(7));
let a = f.element(3);
let b = f.element(5);
assert_eq!(f.mul(&a, &b), f.element(1));
}
#[test]
fn finite_field_inverse() {
let f = FiniteField::new(BigInt::from(7));
let a = f.element(3);
let inv = f.inv(&a).unwrap();
assert_eq!(f.mul(&a, &inv), f.one());
}
#[test]
fn finite_field_inverse_of_zero_is_none() {
let f = FiniteField::new(BigInt::from(7));
assert!(f.inv(&f.zero()).is_none());
}
#[test]
fn finite_field_division() {
let f = FiniteField::new(BigInt::from(7));
let a = f.element(5);
let b = f.element(3);
let q = f.div(&a, &b).unwrap();
assert_eq!(f.mul(&q, &b), a);
}
}
#[cfg(test)]
mod proptests {
use super::*;
use proptest::prelude::*;
fn small_field() -> FiniteField {
FiniteField::new(BigInt::from(7))
}
proptest! {
#[test]
fn multiplication_is_commutative(a in 0u64..7, b in 0u64..7) {
let f = small_field();
let x = f.element(a);
let y = f.element(b);
assert_eq!(f.mul(&x, &y), f.mul(&y, &x));
}
#[test]
fn add_then_sub_is_identity(a in 0u64..7, b in 0u64..7) {
let f = small_field();
let x = f.element(a);
let y = f.element(b);
let sum = f.add(&x, &y);
assert_eq!(f.sub(&sum, &y), x);
}
#[test]
fn non_zero_inverse_exists(a in 1u64..7) {
let f = small_field();
let x = f.element(a);
let inv = f.inv(&x).unwrap();
assert_eq!(f.mul(&x, &inv), f.one());
}
}
}