extern crate smallvec;
use self::smallvec::SmallVec;
#[derive(Debug, Clone)]
pub struct Polynomial {
coefficients: SVec
}
type SVec = SmallVec<[f64; 4]>;
#[inline(always)]
fn fill_with_zeros(vec: &mut SVec, new_size: usize) {
if vec.len() < new_size {
let c = vec.capacity();
if c < new_size {
vec.reserve_exact(new_size - c);
}
let diff = new_size - vec.len();
for _ in 0..diff {
vec.push(0.0);
}
}
}
impl Polynomial {
#[inline(always)]
pub fn new(c: &[f64]) -> Polynomial {
let mut sv = SVec::new();
let cap = sv.capacity();
if cap < c.len() {
sv.reserve_exact(c.len() - cap);
}
for &a in c {
sv.push(a);
}
Polynomial { coefficients: sv }
}
#[inline(always)]
pub fn linear(b: f64, a: f64) -> Polynomial {
let mut sv = SVec::new();
sv.push(b);
sv.push(a);
Polynomial { coefficients: sv }
}
#[inline(always)]
pub fn constant(b: f64) -> Polynomial {
let mut sv = SVec::new();
sv.push(b);
Polynomial { coefficients: sv }
}
#[inline(always)]
pub fn zero() -> Polynomial {
Polynomial::constant(0.0)
}
pub fn degree(&self) -> usize {
self.coefficients.iter()
.rposition(|&a| a != 0.0)
.unwrap_or(0)
}
pub fn bind(&self, x: f64) -> Polynomial {
let mut result: f64 = 0.0;
for &coefficient in self.coefficients.iter().rev() {
result *= x;
result += coefficient;
}
Polynomial::constant(result)
}
pub fn as_f64(&self) -> Result<f64, PolynomialError> {
match self.degree() {
0 => Ok(self.coefficients[0]),
_ => Err(PolynomialError::NonConstantError)
}
}
pub fn as_string(&self, name: &str) -> String {
self.coefficients.iter().enumerate().rev()
.filter_map(|(exponent, &coefficient)| {
if coefficient == 0.0 && exponent > 0 {
None
} else {
match exponent {
0 => Some(coefficient.to_string()),
_ if !coefficient.is_normal() => Some(format!("{}*{}^{}", coefficient, name, exponent)),
1 if coefficient == -1.0 => Some(format!("-{}", name)),
1 if coefficient == 1.0 => Some(format!("{}", name)),
1 if coefficient != 0.0 => Some(format!("{}{}", coefficient, name)),
_ if coefficient == -1.0 => Some(format!("-{}^{}", name, exponent)),
_ if coefficient == 1.0 => Some(format!("{}^{}", name, exponent)),
_ => Some(format!("{}{}^{}", coefficient, name, exponent))
}
}
})
.fold(String::with_capacity(self.coefficients.len() * 5), |mut expr, x| {
if !x.starts_with('-') && !expr.is_empty() {
if x == "0" {
return expr;
}
expr.push('+');
}
expr.push_str(&x); expr
})
}
}
impl Default for Polynomial {
#[inline(always)]
fn default() -> Polynomial {
Polynomial::zero()
}
}
impl From<Polynomial> for String {
fn from(p: Polynomial) -> String {
p.as_string("x")
}
}
use std::fmt;
impl fmt::Display for Polynomial {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(self.as_string("x").as_str())
}
}
use std::ops::*;
impl Index<usize> for Polynomial {
type Output = f64;
fn index(&self, idx: usize) -> &f64 {
&self.coefficients[idx]
}
}
impl IndexMut<usize> for Polynomial {
fn index_mut(&mut self, idx: usize) -> &mut f64 {
if idx >= self.coefficients.len() {
fill_with_zeros(&mut self.coefficients, idx+1);
}
&mut self.coefficients[idx]
}
}
impl<'a, 'b> Add<&'b Polynomial> for &'a mut Polynomial {
type Output = &'a Polynomial;
fn add(self, other: &'b Polynomial) -> &'a Polynomial {
if other.coefficients.len() > self.coefficients.len() {
fill_with_zeros(&mut self.coefficients, other.coefficients.len());
}
for (idx, &coefficient) in other.coefficients.iter().enumerate() {
self.coefficients[idx] += coefficient;
}
self
}
}
impl AddAssign for Polynomial {
fn add_assign(&mut self, other: Polynomial) {
self + &other;
}
}
impl Add for Polynomial {
type Output = Polynomial;
fn add(self, other: Polynomial) -> Polynomial {
let mut c = self.clone();
c += other;
c
}
}
impl<'a, 'b> Sub<&'b Polynomial> for &'a mut Polynomial {
type Output = &'a Polynomial;
fn sub(self, other: &'b Polynomial) -> &'a Polynomial {
if other.coefficients.len() > self.coefficients.len() {
fill_with_zeros(&mut self.coefficients, other.coefficients.len());
}
for (idx, &coefficient) in other.coefficients.iter().enumerate() {
self.coefficients[idx] -= coefficient;
}
self
}
}
impl SubAssign for Polynomial {
fn sub_assign(&mut self, other: Polynomial) {
self - &other;
}
}
impl Sub for Polynomial {
type Output = Polynomial;
fn sub(self, other: Polynomial) -> Polynomial {
let mut c = self.clone();
c -= other;
c
}
}
impl<'a, 'b> Mul<&'b Polynomial> for &'a mut Polynomial {
type Output = &'a Polynomial;
fn mul(self, other: &'b Polynomial) -> &'a Polynomial {
let self_degree = self.degree();
let other_degree = other.degree();
if self_degree == 0 && other_degree == 0 {
self.coefficients[0] *= other.coefficients[0];
} else {
let mut c = SVec::new();
fill_with_zeros(&mut c, self_degree + other_degree + 1);
for a in 0..self_degree+1 {
for b in 0..other_degree+1 {
c[a+b] += self.coefficients[a] * other.coefficients[b];
}
}
self.coefficients = c;
}
self
}
}
impl MulAssign for Polynomial {
fn mul_assign(&mut self, other: Polynomial) {
self * &other;
}
}
impl<'a, 'b> MulAssign<&'b Polynomial> for &'a mut Polynomial {
fn mul_assign(&mut self, other: &'b Polynomial) {
self.mul(other);
}
}
impl Mul for Polynomial {
type Output = Polynomial;
fn mul(self, other: Polynomial) -> Polynomial {
let mut c = self.clone();
c *= other;
c
}
}
impl MulAssign<f64> for Polynomial {
fn mul_assign(&mut self, other: f64) {
let self_degree = self.degree();
for idx in 0..self_degree+1 {
self.coefficients[idx] *= other;
}
}
}
impl<'a, 'b> Div<&'b Polynomial> for &'a mut Polynomial {
type Output = Result<&'a Polynomial, PolynomialError>;
fn div(self, other: &'b Polynomial) -> Result<&'a Polynomial, PolynomialError> {
let mut self_degree = self.degree();
let other_degree = other.degree();
if self_degree < other_degree {
return Err(PolynomialError::DividentDegreeMismatch);
} else
if other_degree == 0 {
if other.coefficients[0] == 0.0 && self_degree > 0 {
return Err(PolynomialError::DivisionByZero);
}
for idx in 0..self_degree+1 {
self.coefficients[idx] /= other.coefficients[0];
}
} else {
let mut q = Polynomial::zero();
fill_with_zeros(&mut q.coefficients, self_degree - other_degree + 2);
while self_degree >= other_degree {
let mut d = Polynomial::zero();
let diff = self_degree - other_degree;
fill_with_zeros(&mut d.coefficients, other_degree + diff + 1);
for (idx, &coefficient) in other.coefficients.iter().enumerate() {
d.coefficients[idx + diff] = coefficient;
}
q.coefficients[diff] = self.coefficients[self_degree] / d.coefficients[self_degree];
d *= q.coefficients[diff];
self.sub_assign(d);
self_degree -= 1;
}
self.coefficients = q.coefficients;
}
Ok(self)
}
}
impl DivAssign for Polynomial {
fn div_assign(&mut self, other: Polynomial) {
let _ = (self / &other).unwrap();
}
}
impl Div for Polynomial {
type Output = Polynomial;
fn div(self, other: Polynomial) -> Polynomial {
let mut c = self.clone();
c /= other;
c
}
}
use std::cmp::{PartialEq, Eq};
impl PartialEq for Polynomial {
fn eq(&self, other: &Polynomial) -> bool {
let self_degree = self.degree();
if other.degree() == self_degree {
other.coefficients[0 .. self_degree+1] == self.coefficients[0 .. self_degree+1]
} else {
false
}
}
}
impl Eq for Polynomial { }
#[derive(Debug, PartialEq)]
pub enum PolynomialError {
NonConstantError,
DivisionByZero,
DividentDegreeMismatch
}
#[cfg(test)]
mod tests {
use polynomial::*;
#[test]
fn test_initialization() {
assert_eq!(Polynomial::new(&[1.0, 2.0, 0.0]).coefficients.to_vec(), &[1.0, 2.0, 0.0]);
assert_eq!(Polynomial::linear(1.0, 2.0).coefficients.to_vec(), &[1.0, 2.0]);
assert_eq!(Polynomial::constant(3.14).coefficients.to_vec(), &[3.14]);
assert_eq!(Polynomial::zero().coefficients.to_vec(), &[0.0]);
}
#[test]
fn test_degree() {
assert_eq!(Polynomial::zero().degree(), 0);
assert_eq!(Polynomial::constant(1.0).degree(), 0);
assert_eq!(Polynomial::linear(1.0, 0.0).degree(), 0);
assert_eq!(Polynomial::linear(1.0, 1.0).degree(), 1);
assert_eq!(Polynomial::new(&[1.0, 0.0, 1.0]).degree(), 2);
}
#[test]
fn test_casting() {
assert_eq!(Polynomial::zero().as_f64(), Ok(0.0));
assert_eq!(Polynomial::constant(3.14).as_f64(), Ok(3.14));
assert_eq!(Polynomial::linear(1.0, 1.0).as_f64(), Err(PolynomialError::NonConstantError));
assert_eq!(Polynomial::linear(0.0, 1.0).as_f64(), Err(PolynomialError::NonConstantError));
}
#[test]
fn test_string_conversion() {
assert_eq!(Polynomial::zero().as_string("x"), "0");
assert_eq!(Polynomial::constant(1.0).as_string("x"), "1");
assert_eq!(Polynomial::linear(0.0, 1.0).as_string("x"), "x");
assert_eq!(Polynomial::linear(0.0, 2.0).as_string("x"), "2x");
assert_eq!(Polynomial::linear(1.0, 2.0).as_string("x"), "2x+1");
assert_eq!(Polynomial::linear(1.0, 2.0).as_string("z"), "2z+1");
assert_eq!(Polynomial::new(&[1.0, 0.0, 2.0]).as_string("x"), "2x^2+1");
assert_eq!(Polynomial::linear(-1.0, 2.0).as_string("x"), "2x-1");
assert_eq!(Polynomial::new(&[1.0, 0.0, -2.0]).as_string("x"), "-2x^2+1");
assert_eq!(Polynomial::new(&[-1.0, 0.0, -2.0]).as_string("x"), "-2x^2-1");
}
#[test]
fn test_indexing() {
assert_eq!(Polynomial::zero()[0], 0.0);
assert_eq!(Polynomial::linear(0.0, 1.0)[1], 1.0);
let mut x = Polynomial::zero();
x[1] = 2.0;
assert_eq!(x[0], 0.0);
assert_eq!(x[1], 2.0);
}
#[test]
#[should_panic]
fn test_indexing_fail() {
assert_eq!(Polynomial::zero()[1], 0.0);
}
#[test]
fn test_bind() {
assert_eq!(Polynomial::constant(1.0).bind(2.0).as_f64(), Ok(1.0));
assert_eq!(Polynomial::linear(0.0, 1.0).bind(2.0).as_f64(), Ok(2.0));
assert_eq!(Polynomial::new(&[0.0, 0.0, 1.0]).bind(2.0).as_f64(), Ok(4.0));
assert_eq!(Polynomial::new(&[0.0, 0.0, 2.0]).bind(2.0).as_f64(), Ok(8.0));
assert_eq!(Polynomial::new(&[1.0, 0.0, 2.0]).bind(2.0).as_f64(), Ok(9.0));
}
#[test]
fn test_assign_add() {
let mut a = Polynomial::constant(1.0);
a += Polynomial::new(&[1.0, 2.0, 3.0]);
assert_eq!(a[0], 2.0);
assert_eq!(a[1], 2.0);
assert_eq!(a[2], 3.0);
}
#[test]
fn test_assign_sub() {
let mut a = Polynomial::constant(1.0);
a -= Polynomial::new(&[1.0, 2.0, 3.0]);
assert_eq!(a[0], 0.0);
assert_eq!(a[1], -2.0);
assert_eq!(a[2], -3.0);
}
#[test]
fn test_assign_mul() {
let mut a = Polynomial::new(&[5.0, 0.0, 10.0, 6.0]);
let b = Polynomial::new(&[1.0, 2.0, 4.0]);
a *= b;
assert_eq!(a[0], 5.0);
assert_eq!(a[1], 10.0);
assert_eq!(a[2], 30.0);
assert_eq!(a[3], 26.0);
assert_eq!(a[4], 52.0);
assert_eq!(a[5], 24.0);
let mut a = Polynomial::constant(2.0);
let b = Polynomial::constant(4.0);
a *= b;
assert_eq!(a.as_f64(), Ok(8.0));
let mut a = Polynomial::linear(1.0, 2.0);
a *= 5.0;
assert_eq!(a[0], 5.0);
assert_eq!(a[1], 10.0);
}
#[test]
fn test_assign_div() {
let mut a = Polynomial::new(&[-10.0, -3.0, 1.0]);
let b = Polynomial::linear(2.0, 1.0);
a /= b;
assert_eq!(a.degree(), 1);
assert_eq!(a[0], -5.0);
assert_eq!(a[1], 1.0);
let mut a = Polynomial::constant(5.0);
let b = Polynomial::constant(0.0);
a /= b;
assert_eq!(a.degree(), 0);
assert!(a[0].is_infinite());
}
#[test]
#[should_panic]
fn test_assign_div_wrong_degree() {
let mut a = Polynomial::linear(-5.0, 1.0);
let b = Polynomial::new(&[1.0, 2.0, 3.0, 4.0]);
a /= b;
}
#[test]
#[should_panic]
fn test_assign_div_zero_division() {
let mut a = Polynomial::linear(-5.0, 1.0);
let b = Polynomial::constant(0.0);
a /= b;
}
#[test]
fn test_equality() {
assert_eq!(Polynomial::new(&[1.0, 2.0, 3.0]), Polynomial::new(&[1.0, 2.0, 3.0]));
assert_eq!(Polynomial::new(&[1.0, 2.0, 3.0, 0.0]), Polynomial::new(&[1.0, 2.0, 3.0]));
assert_eq!(Polynomial::new(&[1.0, 2.0, 3.0]), Polynomial::new(&[1.0, 2.0, 3.0, 0.0]));
assert!(Polynomial::new(&[1.0, 2.0, 3.0]) != Polynomial::new(&[1.0, 2.0, 3.0, 4.0]));
assert!(Polynomial::new(&[1.0, 2.0, 3.0, 4.0]) != Polynomial::new(&[1.0, 2.0, 3.0]));
}
#[test]
fn test_operations() {
assert_eq!(Polynomial::new(&[1.0, 2.0, 3.0]) + Polynomial::new(&[3.0, 2.0, 1.0]), Polynomial::new(&[4.0, 4.0, 4.0]));
assert_eq!(Polynomial::new(&[1.0, 2.0, 3.0]) - Polynomial::new(&[1.0, 2.0, 3.0]), Polynomial::zero());
assert_eq!(Polynomial::new(&[1.0, 2.0, 3.0]) * Polynomial::constant(2.0), Polynomial::new(&[2.0, 4.0, 6.0]));
assert_eq!(Polynomial::constant(2.0) * Polynomial::linear(0.0, 1.0), Polynomial::linear(0.0, 2.0));
assert!((Polynomial::constant(2.0) / Polynomial::constant(0.0)).as_f64().unwrap().is_infinite());
assert_eq!(Polynomial::constant(2.0) / Polynomial::constant(2.0), Polynomial::constant(1.0));
assert_eq!(Polynomial::linear(0.0, 2.0) / Polynomial::linear(0.0, 1.0), Polynomial::constant(2.0));
assert_eq!(Polynomial::linear(0.0, 1.0) / Polynomial::linear(0.0, 2.0), Polynomial::constant(0.5));
}
}