#![cfg_attr(not(any(feature = "std", test)), no_std)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
use core::ops::{Add, Mul};
use num_traits::{MulAdd, Zero};
#[inline]
#[doc(hidden)]
pub fn mul_add<T: MulAdd<Output = T>>(x: T, a: T, b: T) -> T {
x.mul_add(a, b)
}
pub fn horner<T>(x: T, coeffs: &[T]) -> T
where
T: Zero,
T: for<'a> Add<&'a T, Output = T>,
T: for<'a> Mul<&'a T, Output = T>,
{
coeffs.iter().rfold(T::zero(), |acc: T, c: &T| acc * &x + c)
}
pub fn horner_array<T, const N: usize>(x: T, coeffs: &[T; N]) -> T
where
T: Zero,
T: for<'a> Add<&'a T, Output = T>,
T: for<'a> Mul<&'a T, Output = T>,
{
coeffs.iter().rfold(T::zero(), |acc: T, c: &T| acc * &x + c)
}
#[macro_export]
macro_rules! horner {
($x:expr; [$($coeffs:expr),+ $(,)?]) => { $crate::horner!($x; $($coeffs),*) };
($x:expr; $a:expr $(,)?) => { $a };
($x:expr; $a:expr, $($rest:expr),+ $(,)?) => { $a + $x * $crate::horner!($x; $($rest),+) };
(let $x:expr; $($t:tt)*) => {{
let x = $x;
$crate::horner!(x; $($t)*)
}};
}
#[macro_export]
macro_rules! horner_fma {
($x:expr; [$($coeffs:expr),+ $(,)?]) => { $crate::horner_fma!($x; $($coeffs),*) };
($x:expr; $a:expr $(,)?) => { $a };
($x:expr; $a:expr, $($rest:expr),+ $(,)?) => { $crate::mul_add($crate::horner_fma!($x; $($rest),+), $x, $a) };
(let $x:expr; $($t:tt)*) => {{
let x = $x;
$crate::horner_fma!(x; $($t)*)
}};
}
#[macro_export]
macro_rules! estrin {
($x:expr; [$($coeffs:expr),* $(,)?]) => { $crate::estrin!($x; $($coeffs),*) };
($x:expr; $a:expr $(,)?) => { $a };
($x:expr; $a0:expr, $a1:expr $(,)?) => { $a0 + $x * $a1 };
($x:expr; $($coeffs:expr),+ $(,)?) => { $crate::estrin!($x; $($coeffs),+; ) };
(let $x:expr; $($t:tt)*) => {{
let x = $x;
$crate::estrin!(x; $($t)*)
}};
($x:expr; $a0:expr; $($out:expr),+) => {{
let x = $x*$x;
$crate::estrin!(x; $($out),+, $a0)
}};
($x:expr; $a0:expr, $a1:expr; $($out:expr),+) => {{
let x = $x*$x;
$crate::estrin!(x; $($out),+, $a0 + $x * $a1)
}};
($x:expr; $a0:expr, $a1:expr, $($rest:expr),+; ) => {
$crate::estrin!($x; $($rest),+; $a0 + $x * $a1)
};
($x:expr; $a0:expr, $a1:expr, $($rest:expr),+; $($out:expr),+) => {
$crate::estrin!($x; $($rest),+; $($out),+, $a0 + $x * $a1)
};
}
#[macro_export]
macro_rules! estrin_fma {
($x:expr; [$($coeffs:expr),* $(,)?]) => { $crate::estrin_fma!($x; $($coeffs),*) };
($x:expr; $a:expr $(,)?) => { $a };
($x:expr; $a0:expr, $a1:expr $(,)?) => { $crate::mul_add($x, $a1, $a0) };
($x:expr; $($coeffs:expr),+ $(,)?) => { $crate::estrin_fma!($x; $($coeffs),+; ) };
(let $x:expr; $($t:tt)*) => {{
let x = $x;
$crate::estrin_fma!(x; $($t)*)
}};
($x:expr; $a0:expr; $($out:expr),+) => {{
let x = $x*$x;
$crate::estrin_fma!(x; $($out),+, $a0)
}};
($x:expr; $a0:expr, $a1:expr; $($out:expr),+) => {{
let x = $x*$x;
$crate::estrin_fma!(x; $($out),+, $crate::mul_add($x, $a1, $a0))
}};
($x:expr; $a0:expr, $a1:expr, $($rest:expr),+; ) => {
$crate::estrin_fma!($x; $($rest),+; $crate::mul_add($x, $a1, $a0))
};
($x:expr; $a0:expr, $a1:expr, $($rest:expr),+; $($out:expr),+) => {
$crate::estrin_fma!($x; $($rest),+; $($out),+, $crate::mul_add($x, $a1, $a0))
};
}
#[cfg(test)]
mod tests {
#[test]
fn test_horner() {
use super::horner;
for x in 0..32 {
assert_eq!(horner(x, &[]), 0);
assert_eq!(horner(x, &[1]), 1);
assert_eq!(horner(x, &[1, 2]), 1 + 2 * x);
assert_eq!(
horner(x, &[1, 2, 3, 4, 5]),
1 + x * (2 + x * (3 + x * (4 + x * 5)))
);
}
for x in 0..32 {
let x = x as f32;
assert_eq!(horner(x, &[1.]), 1.);
assert_eq!(horner(x, &[1., 2.]), 1. + 2. * x);
assert_eq!(
horner(x, &[1., 2., 3., 4., 5.]),
1. + x * (2. + x * (3. + x * (4. + x * 5.)))
);
}
}
#[test]
fn test_horner_array() {
use super::horner_array;
for x in 0..32 {
assert_eq!(horner_array(x, &[]), 0);
assert_eq!(horner_array(x, &[1]), 1);
assert_eq!(horner_array(x, &[1, 2]), 1 + 2 * x);
assert_eq!(
horner_array(x, &[1, 2, 3, 4, 5]),
1 + x * (2 + x * (3 + x * (4 + x * 5)))
);
}
for x in 0..32 {
let x = x as f32;
assert_eq!(horner_array(x, &[1.]), 1.);
assert_eq!(horner_array(x, &[1., 2.]), 1. + 2. * x);
assert_eq!(
horner_array(x, &[1., 2., 3., 4., 5.]),
1. + x * (2. + x * (3. + x * (4. + x * 5.)))
);
assert_eq!(
horner_array::<_, 5>(x, &[1., 2., 3., 4., 5.]),
1. + x * (2. + x * (3. + x * (4. + x * 5.)))
);
assert_eq!(
horner_array::<f32, 5>(x, &[1., 2., 3., 4., 5.]),
1. + x * (2. + x * (3. + x * (4. + x * 5.)))
);
}
}
#[test]
fn test_macro_horner() {
for x in 0..32 {
assert_eq!(horner!(x; 1), 1);
assert_eq!(horner!(x; 1,), 1);
assert_eq!(horner!(x; 1, 2), 1 + 2 * x);
assert_eq!(horner!(x; 1, 2,), 1 + 2 * x);
assert_eq!(
horner!(x; 1, 2, 3, 4, 5),
1 + x * (2 + x * (3 + x * (4 + x * 5)))
);
}
for x in 0..32 {
let x = x as f32;
assert_eq!(horner!(x; 1.), 1.);
assert_eq!(horner!(x; 1.,), 1.);
assert_eq!(horner!(x; 1., 2.), 1. + 2. * x);
assert_eq!(horner!(x; 1., 2.,), 1. + 2. * x);
assert_eq!(
horner!(x; 1., 2., 3., 4., 5.),
1. + x * (2. + x * (3. + x * (4. + x * 5.)))
);
}
}
#[cfg(any(feature = "std", feature = "lib"))]
#[test]
fn test_macro_horner_fma() {
for x in 0..32 {
assert_eq!(horner_fma!(x; 1), 1);
assert_eq!(horner_fma!(x; 1,), 1);
assert_eq!(horner_fma!(x; 1, 2), 1 + 2 * x);
assert_eq!(horner_fma!(x; 1, 2,), 1 + 2 * x);
assert_eq!(
horner_fma!(x; 1, 2, 3, 4, 5),
1 + x * (2 + x * (3 + x * (4 + x * 5)))
);
}
for x in 0..32 {
let x = x as f32;
assert_eq!(horner_fma!(x; 1.), 1.);
assert_eq!(horner_fma!(x; 1.,), 1.);
assert_eq!(horner_fma!(x; 1., 2.), 1. + 2. * x);
assert_eq!(horner_fma!(x; 1., 2.,), 1. + 2. * x);
assert_eq!(
horner_fma!(x; 1., 2., 3., 4., 5.),
1. + x * (2. + x * (3. + x * (4. + x * 5.)))
);
}
}
#[test]
fn test_macro_estrin() {
for x in 0..32 {
assert_eq!(estrin!(x; 1), 1);
assert_eq!(estrin!(x; 1,), 1);
assert_eq!(estrin!(x; 1, 2), 1 + 2 * x);
assert_eq!(estrin!(x; 1, 2,), 1 + 2 * x);
assert_eq!(
estrin!(x; 1, 2, 3, 4, 5),
1 + x * (2 + x * (3 + x * (4 + x * 5)))
);
}
for x in 0..32 {
let x = x as f32;
assert_eq!(estrin!(x; 1.), 1.);
assert_eq!(estrin!(x; 1.,), 1.);
assert_eq!(estrin!(x; 1., 2.), 1. + 2. * x);
assert_eq!(estrin!(x; 1., 2.,), 1. + 2. * x);
assert_eq!(
estrin!(x; 1., 2., 3., 4., 5.),
1. + x * (2. + x * (3. + x * (4. + x * 5.)))
);
}
}
#[cfg(any(feature = "std", feature = "lib"))]
#[test]
fn test_macro_estrin_fma() {
for x in 0..32 {
assert_eq!(estrin_fma!(x; 1), 1);
assert_eq!(estrin_fma!(x; 1,), 1);
assert_eq!(estrin_fma!(x; 1, 2), 1 + 2 * x);
assert_eq!(estrin_fma!(x; 1, 2,), 1 + 2 * x);
assert_eq!(
estrin_fma!(x; 1, 2, 3, 4, 5),
1 + x * (2 + x * (3 + x * (4 + x * 5)))
);
}
for x in 0..32 {
let x = x as f32;
assert_eq!(estrin_fma!(x; 1.), 1.);
assert_eq!(estrin_fma!(x; 1.,), 1.);
assert_eq!(estrin_fma!(x; 1., 2.), 1. + 2. * x);
assert_eq!(estrin_fma!(x; 1., 2.,), 1. + 2. * x);
assert_eq!(
estrin_fma!(x; 1., 2., 3., 4., 5.),
1. + x * (2. + x * (3. + x * (4. + x * 5.)))
);
}
}
fn make_callable_only_once() -> impl FnMut() -> i32 {
let mut called = false;
move || {
if !called {
called = true;
1
} else {
panic!("function called more than once")
}
}
}
#[test]
#[should_panic(expected = "function called more than once")]
fn test_macro_horner_cannot_call_more_than_once() {
let mut f = make_callable_only_once();
assert_eq!(horner!(f(); 1, 2, 3), 6);
}
#[test]
fn test_macro_horner_can_call_more_than_once() {
let mut f = make_callable_only_once();
assert_eq!(horner!(let f(); 1, 2, 3), 6);
let mut f = make_callable_only_once();
assert_eq!(horner!(let f(); [1, 2, 3]), 6);
}
#[cfg(any(feature = "std", feature = "lib"))]
#[test]
#[should_panic(expected = "function called more than once")]
fn test_macro_horner_fma_cannot_call_more_than_once() {
let mut f = make_callable_only_once();
assert_eq!(horner_fma!(f(); 1, 2, 3), 6);
}
#[cfg(any(feature = "std", feature = "lib"))]
#[test]
fn test_macro_horner_fma_can_call_more_than_once() {
let mut f = make_callable_only_once();
assert_eq!(horner_fma!(let f(); 1, 2, 3), 6);
let mut f = make_callable_only_once();
assert_eq!(horner_fma!(let f(); [1, 2, 3]), 6);
}
#[test]
#[should_panic(expected = "function called more than once")]
fn test_macro_estrin_cannot_call_more_than_once() {
let mut f = make_callable_only_once();
assert_eq!(estrin!(f(); 1, 2, 3), 6);
}
#[test]
fn test_macro_estrin_can_call_more_than_once() {
let mut f = make_callable_only_once();
assert_eq!(estrin!(let f(); 1, 2, 3), 6);
let mut f = make_callable_only_once();
assert_eq!(estrin!(let f(); [1, 2, 3]), 6);
}
#[cfg(any(feature = "std", feature = "lib"))]
#[test]
#[should_panic(expected = "function called more than once")]
fn test_macro_estrin_fma_cannot_call_more_than_once() {
let mut f = make_callable_only_once();
assert_eq!(estrin_fma!(f(); 1, 2, 3), 6);
}
#[cfg(any(feature = "std", feature = "lib"))]
#[test]
fn test_macro_estrin_fma_can_call_more_than_once() {
let mut f = make_callable_only_once();
assert_eq!(estrin_fma!(let f(); 1, 2, 3), 6);
let mut f = make_callable_only_once();
assert_eq!(estrin_fma!(let f(); [1, 2, 3]), 6);
}
}