use std::{
ops::Mul,
fmt::Display,
iter::{once, repeat_n},
};
use itertools::Itertools;
use num::BigInt;
use crate::{
error::AdicResult,
local_num::{LocalInteger, LocalOne, LocalZero},
mapping::{IndexedMapping, Mapping},
normed::Normed,
};
use super::{euclid, factorization};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Polynomial <T>
where T: Clone + LocalZero {
coefficients: Vec<T>,
}
impl<T> Polynomial<T>
where T: Clone + LocalZero {
pub fn new(coefficients: Vec<T>) -> Self {
let mut coefficients = coefficients;
while coefficients.last().is_some_and(T::is_local_zero) {
coefficients.pop();
}
Self {
coefficients,
}
}
pub fn new_big(coefficients: Vec<T>) -> Polynomial<BigInt>
where T: Into<BigInt> {
Polynomial::new(coefficients.into_iter().map(Into::into).collect())
}
pub fn monomial(degree: usize, coefficient: T) -> Self {
if coefficient.is_local_zero() {
Self::new(vec![])
} else {
let zero = coefficient.local_zero();
let mut coefficients = vec![zero; degree];
coefficients.push(coefficient);
Self::new(coefficients)
}
}
pub fn nth_root_polynomial(a: T, n: u32) -> Self
where T: LocalOne + std::ops::Neg<Output=T> {
let n = usize::try_from(n).expect("Cannot convert u32 to usize");
let coefficients = if n.is_local_zero() {
vec![]
} else {
let zero = a.local_zero();
let one = a.local_one();
once(-a)
.chain(repeat_n(zero, n - 1))
.chain(once(one))
.collect::<Vec<_>>()
};
Self {
coefficients,
}
}
pub fn degree(&self) -> Option<usize> {
self.coefficients.iter().rposition(|a| !a.is_local_zero())
}
pub fn lowest_degree(&self) -> Option<usize> {
self.coefficients.iter().position(|a| !a.is_local_zero())
}
pub fn is_constant(&self) -> bool {
self.degree().is_none_or(|d| d == 0)
}
pub fn coefficients(&self) -> impl ExactSizeIterator<Item=&T> + DoubleEndedIterator {
self.coefficients.iter()
}
pub fn into_coefficients(self) -> impl ExactSizeIterator<Item=T> + DoubleEndedIterator {
self.coefficients.into_iter()
}
pub fn degree_and_leading_coefficient(&self) -> Option<(usize, T)> {
self.coefficients().enumerate().rfind(|c| !c.1.is_local_zero()).map(|(d, c)| (d, c.clone()))
}
pub fn leading_coefficient(&self) -> Option<T> {
self.degree_and_leading_coefficient().map(|c| c.1)
}
#[must_use]
pub fn map_coefficients<U, F>(self, f: F) -> Polynomial<U>
where U: Clone + LocalZero, F: FnMut(T) -> U {
Polynomial::<U>::new(self.into_coefficients().map(f).collect())
}
#[must_use]
pub fn mul_coefficients(self, m: T) -> Self
where T: Mul<Output=T> {
self.map_coefficients(move |c| m.clone() * c)
}
#[must_use]
pub fn factor_coefficients(self) -> Self
where T: LocalInteger {
if let Some(zero) = self.coefficients.first().map(LocalZero::local_zero) {
let g = self.coefficients.iter().fold(zero, |acc, c| acc.gcd(c));
self.map_coefficients(|c| c / g.clone())
} else {
self
}
}
#[must_use]
pub fn primitive_monic(self) -> Self
where T: LocalInteger + Normed, T::Unit: LocalOne {
let factored = self.factor_coefficients();
if let Some(leading) = factored.leading_coefficient() {
let leading_unit = leading.unit().expect("Polynomial should have nonzero leading unit");
let unit_one = leading_unit.local_one();
let lu = T::from_unit(leading_unit);
let factored = factored.map_coefficients(|c| c / lu.clone());
let mut coeffs = factored.into_coefficients().collect::<Vec<_>>();
if let Some(mc) = coeffs.last_mut() {
*mc = T::from_norm_and_unit(leading.norm(), unit_one.local_one());
}
Polynomial::new(coeffs)
} else {
factored
}
}
#[must_use]
pub fn gcd(&self, other: &Self) -> Self
where T: LocalInteger {
euclid::factored_pseudo_rem_gcd(self.clone(), other.clone())
}
#[must_use]
pub fn primitive_gcd(&self, other: &Self) -> Self
where T: LocalInteger + Normed, T::Unit: LocalOne {
euclid::primitive_pseudo_rem_gcd(self.clone(), other.clone())
}
#[must_use]
pub fn raw_square_free_decomposition(&self) -> Vec<Self>
where T: LocalInteger {
factorization::raw_square_free_decomposition(self)
}
#[must_use]
pub fn primitive_square_free_decomposition(&self) -> Vec<Self>
where T: LocalInteger + Normed, T::Unit: LocalOne {
factorization::primitive_square_free_decomposition(self)
}
}
impl <T, U> Mapping<U> for Polynomial<T>
where T: Clone + LocalZero, U: Clone + LocalZero + LocalOne + Mul<T, Output=U> {
type Output = U;
fn eval(&self, input: U) -> AdicResult<U> {
let mut output = input.local_zero();
let mut input_to_the_n = input.local_one();
for coefficient in self.coefficients.clone() {
output = output + input_to_the_n.clone() * coefficient;
input_to_the_n = input_to_the_n * input.clone();
}
Ok(output)
}
}
impl <T, U> IndexedMapping<U> for Polynomial<T>
where T: Clone + LocalZero, U: Clone + LocalZero + LocalOne + Mul<T, Output=U> {
fn eval_finite(&self, input: U, num_terms: usize) -> AdicResult<U> {
let mut output = input.local_zero();
let mut input_to_the_n = input.local_one();
for coefficient in self.coefficients.iter().take(num_terms).cloned() {
output = output + input_to_the_n.clone() * coefficient;
input_to_the_n = input_to_the_n * input.clone();
}
Ok(output)
}
}
impl<T> Display for Polynomial<T>
where T: Clone + LocalZero + Display {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}",
self.coefficients
.iter()
.enumerate()
.rev()
.map(|(deg, coeff)| format!("{coeff}x^{deg}"))
.join(" + ")
)
}
}