use std::fmt;
use crate::Exceptions;
use crate::common::Result;
use super::{GenericGF, GenericGFRef};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GenericGFPoly {
field: GenericGFRef,
coefficients: Vec<i32>,
}
impl GenericGFPoly {
pub fn new(field: GenericGFRef, coefficients: &[i32]) -> Result<Self> {
if coefficients.is_empty() {
return Err(Exceptions::illegal_argument_with(
"coefficients cannot be empty",
));
}
Ok(Self {
field,
coefficients: {
let coefficients_length = coefficients.len();
if coefficients_length > 1 && coefficients[0] == 0 {
let mut first_non_zero = 1;
while first_non_zero < coefficients_length && coefficients[first_non_zero] == 0
{
first_non_zero += 1;
}
if first_non_zero == coefficients_length {
vec![0]
} else {
let mut new_coefficients = vec![0; coefficients_length - first_non_zero];
let l = new_coefficients.len() - 1;
new_coefficients[0..=l].clone_from_slice(&coefficients[first_non_zero..]);
new_coefficients
}
} else {
coefficients.to_vec()
}
},
})
}
pub fn getCoefficients(&self) -> &Vec<i32> {
&self.coefficients
}
pub fn getDegree(&self) -> usize {
self.coefficients.len() - 1
}
pub fn isZero(&self) -> bool {
self.coefficients[0] == 0
}
pub fn getCoefficient(&self, degree: usize) -> i32 {
self.coefficients[self.coefficients.len() - 1 - degree]
}
pub fn evaluateAt(&self, a: usize) -> i32 {
if a == 0 {
return self.getCoefficient(0);
}
if a == 1 {
let mut result = 0;
for coefficient in &self.coefficients {
result = GenericGF::addOrSubtract(result, *coefficient);
}
return result;
}
let mut result = self.coefficients[0];
let size = self.coefficients.len();
for i in 1..size {
result = GenericGF::addOrSubtract(
self.field.multiply(a as i32, result),
self.coefficients[i],
);
}
result
}
pub fn addOrSubtract(&self, other: &GenericGFPoly) -> Result<GenericGFPoly> {
if self.field != other.field {
return Err(Exceptions::illegal_argument_with(
"GenericGFPolys do not have same GenericGF field",
));
}
if self.isZero() {
return Ok(other.clone());
}
if other.isZero() {
return Ok(self.clone());
}
let mut smallerCoefficients = self.coefficients.clone();
let mut largerCoefficients = other.coefficients.clone();
if smallerCoefficients.len() > largerCoefficients.len() {
std::mem::swap(&mut smallerCoefficients, &mut largerCoefficients)
}
let mut sumDiff = vec![0; largerCoefficients.len()];
let lengthDiff = largerCoefficients.len() - smallerCoefficients.len();
sumDiff[0..lengthDiff].clone_from_slice(&largerCoefficients[0..lengthDiff]);
for i in lengthDiff..largerCoefficients.len() {
sumDiff[i] = GenericGF::addOrSubtract(
smallerCoefficients[i - lengthDiff],
largerCoefficients[i],
);
}
GenericGFPoly::new(self.field, &sumDiff)
}
pub fn multiply(&self, other: &GenericGFPoly) -> Result<GenericGFPoly> {
if self.field != other.field {
return Err(Exceptions::illegal_argument_with(
"GenericGFPolys do not have same GenericGF field",
));
}
if self.isZero() || other.isZero() {
return Ok(self.getZero());
}
let aCoefficients = self.coefficients.clone();
let aLength = aCoefficients.len();
let bCoefficients = other.coefficients.clone();
let bLength = bCoefficients.len();
let mut product = vec![0; aLength + bLength - 1];
for i in 0..aLength {
let aCoeff = aCoefficients[i];
for j in 0..bLength {
product[i + j] = GenericGF::addOrSubtract(
product[i + j],
self.field.multiply(aCoeff, bCoefficients[j]),
);
}
}
GenericGFPoly::new(self.field, &product)
}
pub fn multiply_with_scalar(&self, scalar: i32) -> GenericGFPoly {
if scalar == 0 {
return self.getZero();
}
if scalar == 1 {
return self.clone();
}
let size = self.coefficients.len();
let mut product = vec![0; size];
for (i, prod) in product.iter_mut().enumerate().take(size) {
*prod = self.field.multiply(self.coefficients[i], scalar);
}
GenericGFPoly::new(self.field, &product).unwrap()
}
pub fn getZero(&self) -> Self {
GenericGFPoly::new(self.field, &[0]).unwrap()
}
pub fn getOne(&self) -> Self {
GenericGFPoly::new(self.field, &[1]).unwrap()
}
pub fn multiply_by_monomial(&self, degree: usize, coefficient: i32) -> Result<GenericGFPoly> {
if coefficient == 0 {
return Ok(self.getZero());
}
let size = self.coefficients.len();
let mut product = vec![0; size + degree];
for (i, prod) in product.iter_mut().enumerate().take(size) {
*prod = self.field.multiply(self.coefficients[i], coefficient);
}
GenericGFPoly::new(self.field, &product)
}
pub fn divide(&self, other: &GenericGFPoly) -> Result<(GenericGFPoly, GenericGFPoly)> {
if self.field != other.field {
return Err(Exceptions::illegal_argument_with(
"GenericGFPolys do not have same GenericGF field",
));
}
if other.isZero() {
return Err(Exceptions::illegal_argument_with("Divide by 0"));
}
let mut quotient = self.getZero();
let mut remainder = self.clone();
let denominator_leading_term = other.getCoefficient(other.getDegree());
let inverse_denominator_leading_term = match self.field.inverse(denominator_leading_term) {
Ok(val) => val,
Err(_issue) => return Err(Exceptions::illegal_argument_with("arithmetic issue")),
};
while remainder.getDegree() >= other.getDegree() && !remainder.isZero() {
let degree_difference = remainder.getDegree() - other.getDegree();
let scale = self.field.multiply(
remainder.getCoefficient(remainder.getDegree()),
inverse_denominator_leading_term,
);
let term = other.multiply_by_monomial(degree_difference, scale)?;
let iteration_quotient = GenericGF::buildMonomial(self.field, degree_difference, scale);
quotient = quotient.addOrSubtract(&iteration_quotient)?;
remainder = remainder.addOrSubtract(&term)?;
}
Ok((quotient, remainder))
}
}
impl fmt::Display for GenericGFPoly {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.isZero() {
return write!(f, "0");
}
let mut result = String::with_capacity(8 * self.getDegree());
for degree in (0..=self.getDegree()).rev() {
let mut coefficient = self.getCoefficient(degree);
if coefficient != 0 {
if coefficient < 0 {
if degree == self.getDegree() {
result.push('-');
} else {
result.push_str(" - ");
}
coefficient = -coefficient;
} else if !result.is_empty() {
result.push_str(" + ");
}
if degree == 0 || coefficient != 1 {
if let Ok(alpha_power) = self.field.log(coefficient) {
if alpha_power == 0 {
result.push('1');
} else if alpha_power == 1 {
result.push('a');
} else {
result.push_str("a^");
result.push_str(&format!("{alpha_power}"));
}
}
}
if degree != 0 {
if degree == 1 {
result.push('x');
} else {
result.push_str("x^");
result.push_str(&format!("{degree}"));
}
}
}
}
write!(f, "{result}")
}
}