use num::Zero;
use crate::{
PolyError, Result, solve,
utils::{check_if_correct_order, check_if_real_coefficients, convert_complex_to_real},
};
#[allow(rustdoc::broken_intra_doc_links)]
#[derive(Clone)]
pub struct Polynomial<T>
where
T: std::fmt::Debug,
{
pub coef: Vec<T>,
}
impl<T> Polynomial<T>
where
T: num::complex::ComplexFloat + std::fmt::Debug,
{
pub fn new() -> Self {
Polynomial {
coef: vec![T::zero()],
}
}
pub fn build(coef: &[T]) -> Result<Self> {
if coef.len().is_zero() {
return Ok(Polynomial::new());
}
match coef.iter().any(|x| x.is_nan() | x.is_infinite()) {
true => Err(PolyError::InvalidCoefficients),
false => Ok(Polynomial {
coef: coef.to_vec(),
}),
}
}
pub fn to_trimmed(&self) -> Self {
if self.coef.len() == 1 {
return self.clone();
}
let mut iter = self.coef.iter().rev().copied().peekable();
let mut new_coeffs = self.coef.clone();
new_coeffs.reverse();
while iter.peek().is_some_and(|c| c.is_zero()) {
new_coeffs.remove(0);
iter.next();
}
new_coeffs.reverse();
Polynomial { coef: new_coeffs }
}
pub fn to_monic(&self) -> Self {
if self.coef.len() == 1 {
return self.clone();
}
let mut monic = self.to_trimmed();
let a = *monic.coef.last().unwrap();
monic.coef.iter_mut().for_each(|e| *e = *e / a);
monic
}
pub fn to_depressed_cubic(&self) -> Result<Polynomial<f64>> {
let poly = self.to_trimmed();
check_if_real_coefficients(&poly.coef)?;
check_if_correct_order(&poly.coef, 3)?;
let mut reals = Vec::<f64>::new();
for c in poly.coef.iter() {
reals.push(convert_complex_to_real(*c)?);
}
let a = reals[3];
let b = reals[2];
let c = reals[1];
let d = reals[0];
let p = (3.0 * a * c - b.powi(2)) / (3.0 * a.powi(2));
let q = (2.0 * b.powi(3) - 9.0 * a * b * c + 27.0 * a.powi(2) * d) / (27.0 * a.powi(3));
Polynomial::build(&[q, p, 0.0, 1.0])
}
#[doc(alias = "gsl_poly_eval")]
pub fn eval(&self, x: T) -> T {
self.coef
.iter()
.rev()
.copied()
.reduce(|res, coef| coef + x * res)
.unwrap_or(T::zero())
}
#[doc(alias = "gsl_poly_eval_derivs")]
pub fn eval_derivs(&self, x: T, n: usize) -> Vec<T> {
let mut res: Vec<T> = vec![T::zero(); n];
let last_idx = self.coef.len() - 1;
let nmax = self.coef.len().min(res.len()) - 1;
res.iter_mut()
.take(nmax + 1)
.for_each(|e| *e = *self.coef.last().unwrap());
for i in 0..last_idx {
let k = last_idx - i;
res[0] = x * res[0] + self.coef[k - 1];
let jmax = if nmax < k { nmax } else { k - 1 };
for j in 1..=jmax {
res[j] = x * res[j] + res[j - 1];
}
}
let mut f = T::one();
for (i, d) in res.iter_mut().enumerate().take(nmax + 1).skip(2) {
f = f * T::from(i).unwrap();
*d = *d * f;
}
res
}
#[doc(alias = "gsl_poly_solve_quadratic")]
pub fn solve_real_quadratic(&self) -> Result<Vec<f64>> {
check_if_correct_order(&self.coef, 2)?;
check_if_real_coefficients(&self.coef)?;
let mut reals = Vec::<f64>::new();
for c in self.coef.iter() {
reals.push(convert_complex_to_real(*c)?);
}
solve::solve_real_quadratic(reals[2], reals[1], reals[0])
}
#[doc(alias = "gsl_poly_solve_cubic")]
pub fn solve_real_cubic(&self) -> Result<Vec<f64>> {
check_if_correct_order(&self.coef, 3)?;
check_if_real_coefficients(&self.coef)?;
let monic = self.to_monic();
let mut reals = Vec::<f64>::new();
for c in monic.coef.iter() {
reals.push(convert_complex_to_real(*c)?);
}
solve::solve_real_cubic(reals[2], reals[1], reals[0])
}
}
impl<T> Default for Polynomial<T>
where
T: num::complex::ComplexFloat + std::fmt::Debug,
{
fn default() -> Self {
Self::new()
}
}
impl<T> std::fmt::Debug for Polynomial<T>
where
T: std::fmt::Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Polynomial")
.field("Coefficients", &self.coef)
.finish()
}
}