use std::fmt;
use std::sync::Arc;
use super::GfTables;
use crate::error::Result;
#[derive(Clone)]
pub struct DynamicGf {
tables: Arc<GfTables>,
}
impl DynamicGf {
pub fn new(order: u32) -> Result<Self> {
let tables = GfTables::new_extension(order)?;
Ok(Self {
tables: Arc::new(tables),
})
}
#[must_use]
pub fn order(&self) -> u32 {
self.tables.order()
}
#[must_use]
pub fn characteristic(&self) -> u32 {
self.tables.characteristic()
}
#[must_use]
pub fn degree(&self) -> u32 {
self.tables.degree()
}
#[must_use]
pub fn element(&self, value: u32) -> GfElement {
GfElement {
value: value % self.tables.order(),
field: self.clone(),
}
}
#[must_use]
pub fn zero(&self) -> GfElement {
self.element(0)
}
#[must_use]
pub fn one(&self) -> GfElement {
self.element(1)
}
pub fn elements(&self) -> impl Iterator<Item = GfElement> + '_ {
(0..self.order()).map(move |v| self.element(v))
}
pub fn units(&self) -> impl Iterator<Item = GfElement> + '_ {
(1..self.order()).map(move |v| self.element(v))
}
pub fn bulk_eval_poly(&self, coeffs: &[u32], points: &[u32], results: &mut [u32]) {
assert_eq!(points.len(), results.len());
if coeffs.is_empty() {
results.fill(0);
return;
}
let last_coeff = coeffs[coeffs.len() - 1];
results.fill(last_coeff);
for &coeff in coeffs.iter().rev().skip(1) {
for i in 0..points.len() {
let x = points[i];
let current_val = results[i];
let mul_res = self.tables.mul(current_val, x);
results[i] = self.tables.add(mul_res, coeff);
}
}
}
pub fn bulk_linear_transform(&self, a: u32, b: u32, points: &[u32], results: &mut [u32]) {
assert_eq!(points.len(), results.len());
for i in 0..points.len() {
let x = points[i];
let ax = self.tables.mul(a, x);
results[i] = self.tables.add(ax, b);
}
}
#[must_use]
pub fn tables(&self) -> &GfTables {
&self.tables
}
}
impl fmt::Debug for DynamicGf {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.degree() == 1 {
write!(f, "GF({})", self.order())
} else {
write!(f, "GF({}^{})", self.characteristic(), self.degree())
}
}
}
impl fmt::Display for DynamicGf {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.degree() == 1 {
write!(f, "GF({})", self.order())
} else {
write!(f, "GF({}^{})", self.characteristic(), self.degree())
}
}
}
#[derive(Clone)]
pub struct GfElement {
value: u32,
field: DynamicGf,
}
impl GfElement {
#[must_use]
pub fn to_u32(&self) -> u32 {
self.value
}
#[must_use]
pub fn value(&self) -> u32 {
self.value
}
#[must_use]
pub fn field(&self) -> &DynamicGf {
&self.field
}
#[must_use]
pub fn is_zero(&self) -> bool {
self.value == 0
}
#[must_use]
pub fn is_one(&self) -> bool {
self.value == 1
}
#[must_use]
pub fn neg(&self) -> Self {
Self {
value: self.field.tables.neg(self.value),
field: self.field.clone(),
}
}
#[must_use]
pub fn inv(&self) -> Self {
assert!(!self.is_zero(), "cannot compute inverse of zero");
Self {
value: self.field.tables.inv(self.value),
field: self.field.clone(),
}
}
#[must_use]
pub fn checked_inv(&self) -> Option<Self> {
if self.is_zero() {
None
} else {
Some(Self {
value: self.field.tables.inv(self.value),
field: self.field.clone(),
})
}
}
#[must_use]
pub fn add(&self, rhs: Self) -> Self {
Self {
value: self.field.tables.add(self.value, rhs.value),
field: self.field.clone(),
}
}
#[must_use]
pub fn sub(&self, rhs: Self) -> Self {
Self {
value: self.field.tables.sub(self.value, rhs.value),
field: self.field.clone(),
}
}
#[must_use]
pub fn mul(&self, rhs: Self) -> Self {
Self {
value: self.field.tables.mul(self.value, rhs.value),
field: self.field.clone(),
}
}
#[must_use]
pub fn div(&self, rhs: Self) -> Self {
assert!(!rhs.is_zero(), "division by zero");
Self {
value: self.field.tables.div(self.value, rhs.value),
field: self.field.clone(),
}
}
#[must_use]
pub fn checked_div(&self, rhs: Self) -> Option<Self> {
if rhs.is_zero() {
None
} else {
Some(Self {
value: self.field.tables.div(self.value, rhs.value),
field: self.field.clone(),
})
}
}
#[must_use]
pub fn pow(&self, exp: u32) -> Self {
Self {
value: self.field.tables.pow(self.value, exp),
field: self.field.clone(),
}
}
}
impl PartialEq for GfElement {
fn eq(&self, other: &Self) -> bool {
self.value == other.value && self.field.order() == other.field.order()
}
}
impl Eq for GfElement {}
impl std::hash::Hash for GfElement {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.value.hash(state);
self.field.order().hash(state);
}
}
impl fmt::Debug for GfElement {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}[{}]", self.field, self.value)
}
}
impl fmt::Display for GfElement {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.value)
}
}
impl Default for GfElement {
fn default() -> Self {
let field = DynamicGf::new(2).expect("GF(2) should always be constructible");
field.zero()
}
}
impl std::ops::Add for GfElement {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
GfElement::add(&self, rhs)
}
}
impl std::ops::Sub for GfElement {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
GfElement::sub(&self, rhs)
}
}
impl std::ops::Mul for GfElement {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
GfElement::mul(&self, rhs)
}
}
impl std::ops::Div for GfElement {
type Output = Self;
fn div(self, rhs: Self) -> Self::Output {
GfElement::div(&self, rhs)
}
}
impl std::ops::Neg for GfElement {
type Output = Self;
fn neg(self) -> Self::Output {
GfElement::neg(&self)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_gf7_creation() {
let gf7 = DynamicGf::new(7).unwrap();
assert_eq!(gf7.order(), 7);
assert_eq!(gf7.characteristic(), 7);
assert_eq!(gf7.degree(), 1);
}
#[test]
fn test_gf9_creation() {
let gf9 = DynamicGf::new(9).unwrap();
assert_eq!(gf9.order(), 9);
assert_eq!(gf9.characteristic(), 3);
assert_eq!(gf9.degree(), 2);
}
#[test]
fn test_invalid_order() {
assert!(DynamicGf::new(6).is_err());
assert!(DynamicGf::new(10).is_err());
assert!(DynamicGf::new(1).is_err());
assert!(DynamicGf::new(0).is_err());
}
#[test]
fn test_element_arithmetic() {
let gf7 = DynamicGf::new(7).unwrap();
let a = gf7.element(3);
let b = gf7.element(5);
assert_eq!(a.add(b.clone()).to_u32(), 1);
assert_eq!(a.sub(b.clone()).to_u32(), 5); assert_eq!(a.mul(b.clone()).to_u32(), 1);
assert_eq!(a.div(b).to_u32(), 2); }
#[test]
fn test_element_operators() {
let gf5 = DynamicGf::new(5).unwrap();
let a = gf5.element(3);
let b = gf5.element(2);
assert_eq!((a.clone() + b.clone()).to_u32(), 0);
assert_eq!((a.clone() - b.clone()).to_u32(), 1);
assert_eq!((a.clone() * b.clone()).to_u32(), 1);
assert_eq!((a / b).to_u32(), 4); }
#[test]
fn test_field_iteration() {
let gf5 = DynamicGf::new(5).unwrap();
let elements: Vec<u32> = gf5.elements().map(|e| e.to_u32()).collect();
assert_eq!(elements, vec![0, 1, 2, 3, 4]);
let units: Vec<u32> = gf5.units().map(|e| e.to_u32()).collect();
assert_eq!(units, vec![1, 2, 3, 4]);
}
#[test]
fn test_power() {
let gf7 = DynamicGf::new(7).unwrap();
let a = gf7.element(3);
assert_eq!(a.pow(0).to_u32(), 1);
assert_eq!(a.pow(1).to_u32(), 3);
assert_eq!(a.pow(2).to_u32(), 2); assert_eq!(a.pow(6).to_u32(), 1); }
#[test]
fn test_display() {
let gf7 = DynamicGf::new(7).unwrap();
assert_eq!(format!("{}", gf7), "GF(7)");
let gf9 = DynamicGf::new(9).unwrap();
assert_eq!(format!("{}", gf9), "GF(3^2)");
let elem = gf7.element(5);
assert_eq!(format!("{}", elem), "5");
}
}