use crate::array::Array;
use crate::error::Result;
use num_traits::{One, Zero};
use std::ops::{Add, Div, Mul, Sub};
#[derive(Clone, Debug)]
pub struct Polynomial<T> {
pub(crate) coefficients: Vec<T>,
}
impl<T> Polynomial<T>
where
T: Clone + Zero + PartialEq,
{
pub fn new(coefficients: Vec<T>) -> Self {
let mut coefs = coefficients;
while coefs.len() > 1 && coefs[0] == T::zero() {
coefs.remove(0);
}
Polynomial {
coefficients: coefs,
}
}
pub fn degree(&self) -> usize {
if self.coefficients.is_empty()
|| (self.coefficients.len() == 1 && self.coefficients[0] == T::zero())
{
0
} else {
self.coefficients.len() - 1
}
}
pub fn coefficients(&self) -> &[T] {
&self.coefficients
}
pub fn to_array(&self) -> Array<T> {
Array::from_vec(self.coefficients.clone())
}
}
impl<T> Polynomial<T>
where
T: Clone + Zero + One + Add<Output = T> + Mul<Output = T> + PartialEq,
{
pub fn monomial(degree: usize) -> Self {
let mut coefficients = vec![T::zero(); degree + 1];
coefficients[0] = T::one();
Polynomial { coefficients }
}
pub fn zero() -> Self {
Polynomial {
coefficients: vec![T::zero()],
}
}
pub fn one() -> Self {
Polynomial {
coefficients: vec![T::one()],
}
}
pub fn evaluate(&self, x: T) -> T {
if self.coefficients.is_empty() {
return T::zero();
}
let mut result = self.coefficients[0].clone();
for i in 1..self.coefficients.len() {
result = result * x.clone() + self.coefficients[i].clone();
}
result
}
pub fn evaluate_array(&self, x: &Array<T>) -> Result<Array<T>> {
let x_vec = x.to_vec();
let mut result = Vec::with_capacity(x_vec.len());
for x_val in &x_vec {
result.push(self.evaluate(x_val.clone()));
}
Ok(Array::from_vec(result))
}
}
impl<T> Polynomial<T>
where
T: Clone
+ Zero
+ One
+ Add<Output = T>
+ Mul<Output = T>
+ Sub<Output = T>
+ Div<Output = T>
+ From<i32>
+ PartialEq
+ std::ops::Neg<Output = T>,
{
pub fn derivative(&self) -> Self {
if self.degree() == 0 {
return Polynomial::zero();
}
let n = self.coefficients.len();
let mut derivative_coeffs = Vec::with_capacity(n - 1);
for i in 0..(n - 1) {
let degree = n - 1 - i;
let coef = self.coefficients[i].clone();
let term = coef * T::from(degree as i32);
derivative_coeffs.push(term);
}
Polynomial::new(derivative_coeffs)
}
pub fn integral(&self) -> Self {
let n = self.coefficients.len();
let mut integral_coeffs = Vec::with_capacity(n + 1);
for i in 0..n {
let degree = n - i;
let coef = self.coefficients[i].clone();
let term = coef / T::from(degree as i32);
integral_coeffs.push(term);
}
integral_coeffs.push(T::zero());
Polynomial::new(integral_coeffs)
}
pub fn definite_integral(&self, a: T, b: T) -> T {
let integral = self.integral();
integral.evaluate(b) - integral.evaluate(a)
}
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
#[test]
fn test_polynomial_creation() {
let p = Polynomial::new(vec![3.0, 2.0, 1.0]);
assert_eq!(p.degree(), 2);
assert_eq!(p.coefficients(), &[3.0, 2.0, 1.0]);
}
#[test]
fn test_polynomial_evaluation() {
let p = Polynomial::new(vec![3.0, 2.0, 1.0]);
assert_relative_eq!(p.evaluate(0.0), 1.0);
assert_relative_eq!(p.evaluate(1.0), 6.0);
assert_relative_eq!(p.evaluate(2.0), 17.0);
}
#[test]
fn test_polynomial_derivative() {
let p = Polynomial::new(vec![3.0, 2.0, 1.0, 1.0]);
let dp = p.derivative();
assert_eq!(dp.degree(), 2);
assert_eq!(dp.coefficients(), &[9.0, 4.0, 1.0]);
assert_relative_eq!(dp.evaluate(0.0), 1.0, epsilon = 1e-10);
assert_relative_eq!(dp.evaluate(1.0), 14.0, epsilon = 1e-10);
}
#[test]
fn test_polynomial_integral() {
let p = Polynomial::new(vec![2.0, 1.0]);
let int_p = p.integral();
assert_eq!(int_p.degree(), 2);
assert_eq!(int_p.coefficients(), &[1.0, 1.0, 0.0]);
assert_relative_eq!(int_p.evaluate(0.0), 0.0, epsilon = 1e-10);
assert_relative_eq!(int_p.evaluate(1.0), 2.0, epsilon = 1e-10);
assert_relative_eq!(int_p.evaluate(2.0), 6.0, epsilon = 1e-10);
assert_relative_eq!(p.definite_integral(0.0, 1.0), 2.0, epsilon = 1e-10);
assert_relative_eq!(p.definite_integral(1.0, 2.0), 4.0, epsilon = 1e-10);
}
}