#![warn(
rustdoc::missing_crate_level_docs,
missing_debug_implementations,
missing_docs,
unreachable_pub
)]
#[cfg(test)]
#[macro_use]
extern crate approx;
use std::{
fmt::{Debug, Formatter},
ops::{Add, Div, Index, IndexMut, Mul, Neg, Sub},
};
pub mod arithmetic;
mod calculus;
mod complex;
mod convex_hull;
mod eval;
mod fft;
mod gcd;
mod norms;
mod roots;
mod traits;
pub use eval::eval_poly_ratio;
pub use traits::*;
#[derive(Debug, PartialEq, Clone)]
pub struct Poly<T> {
coeffs: Vec<T>,
}
#[macro_export]
macro_rules! poly {
($($c:expr),+ $(,)*) => {
$crate::Poly::new_from_coeffs(&[$($c,)*])
};
}
impl<T> Poly<T> {
fn len(&self) -> usize {
self.coeffs.len()
}
#[must_use]
pub fn as_slice(&self) -> &[T] {
self.as_ref()
}
}
impl<T: Clone> Poly<T> {
#[must_use]
pub fn coeffs(&self) -> Vec<T> {
self.coeffs.clone()
}
}
impl<T: Clone + PartialEq + Zero> Poly<T> {
pub fn new_from_coeffs(coeffs: &[T]) -> Self {
let p = Self {
coeffs: coeffs.into(),
};
p.trim()
}
pub fn new_from_coeffs_iter<II>(coeffs: II) -> Self
where
II: IntoIterator<Item = T>,
{
let p = Self {
coeffs: coeffs.into_iter().collect(),
};
p.trim()
}
#[must_use]
fn trim(mut self) -> Self {
self.trim_mut();
self
}
fn trim_mut(&mut self) {
if let Some(p) = self.coeffs.iter().rposition(|c| c != &T::zero()) {
let new_length = p + 1;
debug_assert!(new_length > 0);
self.coeffs.truncate(new_length);
} else {
self.coeffs.resize(1, T::zero());
}
debug_assert!(!self.coeffs.is_empty());
}
#[must_use]
pub fn degree(&self) -> Option<usize> {
debug_assert!(
!self.coeffs.is_empty(),
"Degree is not defined on empty polynomial"
);
if self.is_zero() {
None
} else {
Some(self.coeffs.len() - 1)
}
}
pub fn extend(&mut self, degree: usize) {
match self.degree() {
None => self.coeffs.resize(degree + 1, T::zero()),
Some(d) if degree > d => self.coeffs.resize(degree + 1, T::zero()),
Some(_) => (),
};
debug_assert!(!self.coeffs.is_empty());
}
}
impl<T: Clone + PartialEq + Zero> Poly<T> {
pub(crate) fn truncate(&mut self, degree: usize) {
if Some(degree) < self.degree() {
self.coeffs.truncate(degree + 1);
}
}
}
impl<T> Poly<T> {
fn map_reduce<M, R, U>(&self, map: M, reduce: R) -> U
where
M: FnMut(&T) -> U,
R: FnMut(U, U) -> U,
{
self.coeffs.iter().map(map).reduce(reduce).unwrap()
}
}
impl<T: Clone + Div<Output = T> + One + PartialEq + Zero> Poly<T> {
#[must_use]
pub fn monic(&self) -> (Self, T) {
let lc = self.leading_coeff();
let monic_poly = self / lc.clone();
(monic_poly, lc)
}
}
impl<T: Clone + Div<Output = T> + One + PartialEq + Zero> Poly<T> {
pub fn monic_mut(&mut self) -> T {
let lc = self.leading_coeff();
self.div_mut(&lc);
lc
}
}
impl<T: Clone + One> Poly<T> {
#[must_use]
pub fn leading_coeff(&self) -> T {
self.coeffs.last().unwrap_or(&T::one()).clone()
}
}
impl<T: Add<Output = T> + Clone + Mul<Output = T> + Neg<Output = T> + One + PartialEq + Zero>
Poly<T>
{
pub fn new_from_roots(roots: &[T]) -> Self {
let p = roots.iter().fold(Self::one(), |acc, r| {
acc * Self {
coeffs: vec![-r.clone(), T::one()],
}
});
p.trim()
}
pub fn new_from_roots_iter<II>(roots: II) -> Self
where
II: IntoIterator<Item = T>,
{
let p = roots.into_iter().fold(Self::one(), |acc, r| {
acc * Self {
coeffs: vec![-r, T::one()],
}
});
p.trim()
}
}
impl<T: Abs + Clone + PartialEq + PartialOrd + Zero> Poly<T> {
pub fn roundoff(&self, atol: &T) -> Self {
let atol = atol.abs();
let new_coeff = self
.coeffs
.iter()
.map(|c| if c.abs() < atol { T::zero() } else { c.clone() });
Poly::new_from_coeffs_iter(new_coeff)
}
pub fn roundoff_mut(&mut self, atol: &T) {
let atol = atol.abs();
for c in &mut self.coeffs {
if c.abs() < atol {
*c = T::zero();
}
}
self.trim_mut();
}
}
impl<T> Index<usize> for Poly<T> {
type Output = T;
fn index(&self, i: usize) -> &Self::Output {
&self.coeffs[i]
}
}
impl<T> IndexMut<usize> for Poly<T> {
fn index_mut(&mut self, i: usize) -> &mut Self::Output {
&mut self.coeffs[i]
}
}
impl<T: PartialEq + Zero> Zero for Poly<T> {
fn zero() -> Self {
Self {
coeffs: vec![T::zero()],
}
}
fn is_zero(&self) -> bool {
self.coeffs.len() == 1 && self.coeffs[0] == T::zero()
}
}
impl<T: One + PartialEq> One for Poly<T> {
fn one() -> Self {
Self {
coeffs: vec![T::one()],
}
}
fn is_one(&self) -> bool {
self.coeffs.len() == 1 && self.coeffs[0] == T::one()
}
}
macro_rules! display {
($trait:path) => {
impl<T: $trait + PartialOrd + Zero> $trait for Poly<T> {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
debug_assert!(!self.coeffs.is_empty());
if self.len() == 1 {
return self[0].fmt(f);
}
let iter = self
.coeffs
.iter()
.enumerate()
.filter(|(_, x)| !x.is_zero())
.enumerate();
for (i, (n, c)) in iter {
match (i, f.sign_plus(), c < &T::zero()) {
(0, _, _) => (),
(_, false, false) => write!(f, " +")?,
(_, _, _) => write!(f, " ")?,
}
if n == 0 {
c.fmt(f)?;
} else if n == 1 {
c.fmt(f)?;
write!(f, "s")?;
} else {
c.fmt(f)?;
write!(f, "s^")?;
write!(f, "{}", n)?;
}
}
write!(f, "")
}
}
};
}
display!(std::fmt::Binary);
display!(std::fmt::Display);
display!(std::fmt::LowerExp);
display!(std::fmt::LowerHex);
display!(std::fmt::Octal);
display!(std::fmt::UpperExp);
display!(std::fmt::UpperHex);
impl<T> AsRef<[T]> for Poly<T> {
fn as_ref(&self) -> &[T] {
self.coeffs.as_ref()
}
}
pub fn complex_quadratic_roots<T>(b: T, c: T) -> ((T, T), (T, T))
where
T: Abs
+ Add<Output = T>
+ Clone
+ Div<Output = T>
+ Inv
+ Mul<Output = T>
+ Neg<Output = T>
+ One
+ PartialOrd
+ Pow
+ Sign
+ Sqrt
+ Sub<Output = T>
+ Zero,
{
let (r1, r2) = roots::complex_quadratic_roots_impl(b, c);
((r1.re.x, r1.im.x), (r2.re.x, r2.im.x))
}
pub fn real_quadratic_roots<T>(b: T, c: T) -> Option<(T, T)>
where
T: Add<Output = T>
+ Clone
+ Div<Output = T>
+ Mul<Output = T>
+ Neg<Output = T>
+ One
+ Pow
+ Sign
+ Sqrt
+ Sub<Output = T>
+ Zero,
{
roots::real_quadratic_roots_impl(b, c)
}
#[cfg(test)]
mod tests {
use proptest::prelude::*;
use super::*;
#[test]
fn poly_formatting() {
assert_eq!("0", format!("{}", Poly::<i16>::zero()));
assert_eq!("0", format!("{}", Poly::<u16>::new_from_coeffs(&[])));
assert_eq!("1 +2s^3 -4s^4", format!("{}", poly!(1, 0, 0, 2, -4)));
assert_eq!("1.235", format!("{:.3}", Poly::new_from_coeffs(&[1.23456])));
let p = poly!(1.2345, -5.4321, 13.1234);
assert_eq!("+1.23 -5.43s +13.12s^2", format!("{:+.2}", &p));
assert_eq!("1.23 -5.43s +13.12s^2", format!("{:.2}", &p));
assert_eq!("1.2345e0 -5.4321e0s +1.31234e1s^2", format!("{:e}", &p));
}
#[test]
fn poly_creation_coeffs() {
let c = [4.3, 5.32];
for (c1, c2) in c.iter().zip(Poly::new_from_coeffs(&c).coeffs) {
assert_relative_eq!(*c1, c2);
}
let c2 = [0., 1., 1., 0., 0., 0.];
for (i, c) in c2[..3].iter().zip(Poly::new_from_coeffs(&c2).coeffs) {
assert_relative_eq!(*i, c);
}
let zero: [f64; 1] = [0.];
for (z, c) in zero.iter().zip(poly!(0., 0.).coeffs) {
assert_relative_eq!(*z, c);
}
let int = [1, 2, 3, 4, 5];
assert_eq!(int, Poly::new_from_coeffs(&int).coeffs.as_slice());
let float = [0.1_f32, 0.34, 3.43];
for (f, c) in float.iter().zip(Poly::new_from_coeffs(&float).coeffs) {
assert_relative_eq!(*f, c);
}
assert_eq!(
Poly::new_from_coeffs(&[1, 2, 3]),
Poly::new_from_coeffs_iter(1..=3)
);
}
#[test]
fn coeffs() {
let int = [1, 2, 3, 4, 5];
let p = Poly::new_from_coeffs(&int);
assert_eq!(int, p.coeffs().as_slice());
}
#[test]
fn as_slice() {
let int = [1, 2, 3, 4, 5];
let p = Poly::new_from_coeffs(&int);
assert_eq!(int, p.as_slice());
}
#[test]
fn poly_creation_roots() {
assert_eq!(poly!(4.0_f64, 4., 1.), Poly::new_from_roots(&[-2., -2.]));
assert_eq!(poly!(4, 4, 1), Poly::new_from_roots(&[-2, -2]));
assert!(vec![-2., -2.]
.iter()
.zip(
Poly::new_from_roots(&[-2.0_f64, -2.])
.real_roots()
.unwrap()
.iter()
)
.map(|(x, y): (&f64, &f64)| (x - y).abs())
.all(|x| x < 0.000_001));
assert!(vec![1.0_f32, 2., 3.]
.iter()
.zip(
Poly::new_from_roots(&[1.0_f32, 2., 3.])
.real_roots()
.unwrap()
.iter()
)
.map(|(x, y): (&f32, &f32)| (x - y).abs())
.all(|x| x < 0.000_01));
assert_eq!(
poly!(0., -2., 1., 1.),
Poly::new_from_roots(&[-0., -2., 1.])
);
let v = vec![1, 2, -3];
assert_eq!(Poly::new_from_roots(&v), Poly::new_from_roots_iter(v));
}
#[test]
fn len() {
let p = Poly::new_from_coeffs(&[1., 2., 3.]);
assert_eq!(3, p.len());
}
#[test]
fn degree() {
let p = Poly::new_from_coeffs(&[1., 2., 3.]);
assert_eq!(Some(2), p.degree());
let p2 = Poly::new_from_coeffs(&[0.]);
assert_eq!(None, p2.degree());
}
#[test]
fn extend_less() {
let mut p1 = poly!(3, 4, 2);
let p2 = p1.clone();
p1.extend(1);
assert_eq!(p1, p2);
}
#[test]
fn extend_more() {
let mut p1 = poly!(3, 4, 2);
let p2 = Poly {
coeffs: vec![3, 4, 2, 0, 0, 0, 0],
};
p1.extend(6);
assert_eq!(p1, p2);
}
#[test]
fn extend_zero() {
let mut p1 = Poly::<u32>::zero();
let p2 = Poly {
coeffs: vec![0, 0, 0, 0],
};
p1.extend(3);
assert_eq!(p1, p2);
}
#[test]
fn truncate() {
let mut p1 = poly!(3, 4, 2);
let p2 = poly!(3, 4);
p1.truncate(1);
assert_eq!(p1, p2);
}
#[test]
fn indexing() {
assert_abs_diff_eq!(3., poly!(1., 3.)[1], epsilon = 0.);
let mut p = Poly::new_from_roots(&[1., 4., 5.]);
p[2] = 3.;
assert_eq!(poly!(-20., 29., 3., 1.), p);
}
#[test]
fn float_coeffs_identities() {
assert!(Poly::<f64>::zero().is_zero());
assert!(Poly::<f64>::one().is_one());
assert!(Poly::<f32>::zero().is_zero());
assert!(Poly::<f32>::one().is_one());
}
#[test]
fn integer_coeffs_identities() {
assert!(Poly::<i8>::zero().is_zero());
assert!(Poly::<i8>::one().is_one());
assert!(Poly::<u8>::zero().is_zero());
assert!(Poly::<u8>::one().is_one());
assert!(Poly::<i16>::zero().is_zero());
assert!(Poly::<i16>::one().is_one());
assert!(Poly::<u16>::zero().is_zero());
assert!(Poly::<u16>::one().is_one());
assert!(Poly::<i32>::zero().is_zero());
assert!(Poly::<i32>::one().is_one());
assert!(Poly::<u32>::zero().is_zero());
assert!(Poly::<u32>::one().is_one());
assert!(Poly::<i64>::zero().is_zero());
assert!(Poly::<i64>::one().is_one());
assert!(Poly::<u64>::zero().is_zero());
assert!(Poly::<u64>::one().is_one());
assert!(Poly::<i128>::zero().is_zero());
assert!(Poly::<i128>::one().is_one());
assert!(Poly::<u128>::zero().is_zero());
assert!(Poly::<u128>::one().is_one());
assert!(Poly::<isize>::zero().is_zero());
assert!(Poly::<isize>::one().is_one());
assert!(Poly::<usize>::zero().is_zero());
assert!(Poly::<usize>::one().is_one());
}
proptest! {
#[test]
fn qc_leading_coefficient(c: i8) {
prop_assume!(c != 0);
assert_eq!(c, poly!(1, -5, c).leading_coeff());
}
}
#[test]
fn monic_poly() {
let p = poly!(-3., 6., 9.);
let (p2, c) = p.monic();
assert_relative_eq!(9., c);
assert_relative_eq!(1., p2.leading_coeff());
}
#[test]
fn monic_mutable_poly() {
let mut p = poly!(-3., 6., 9.);
let c = p.monic_mut();
assert_relative_eq!(9., c);
assert_relative_eq!(1., p.leading_coeff());
}
#[test]
fn conversion_into_slice() {
assert_eq!(&[3, -6, 8], poly!(3, -6, 8).as_ref());
}
#[test]
fn round_off_coefficients() {
let p = Poly::new_from_coeffs(&[1., 0.002, 1., -0.0001]);
let actual = p.roundoff(&0.01);
let expected = Poly::new_from_coeffs(&[1., 0., 1.]);
assert_eq!(expected, actual);
}
#[test]
fn round_off_zero() {
let zero = Poly::zero();
assert_eq!(zero, zero.roundoff(&0.001));
}
#[test]
fn round_off_returns_zero() {
let p = Poly::new_from_coeffs(&[0.0032, 0.002, -0.0023, -0.0001]);
let actual = p.roundoff(&-0.01);
assert_eq!(Poly::zero(), actual);
}
#[test]
fn round_off_coefficients_mut() {
let mut p = Poly::new_from_coeffs(&[1., 0.002, 1., -0.0001]);
p.roundoff_mut(&0.01);
let expected = Poly::new_from_coeffs(&[1., 0., 1.]);
assert_eq!(expected, p);
}
}
mod compile_fail_test {
#[allow(dead_code)]
fn a() {}
#[allow(dead_code)]
fn b() {}
}