use crate::final_tagless::traits::{MathExpr, NumericType};
use std::ops::{Add, Mul, Sub};
pub fn horner<E: MathExpr, T>(coeffs: &[T], x: E::Repr<T>) -> E::Repr<T>
where
T: NumericType + Clone + Add<Output = T> + Mul<Output = T>,
E::Repr<T>: Clone,
{
if coeffs.is_empty() {
return E::constant(T::default());
}
if coeffs.len() == 1 {
return E::constant(coeffs[0].clone());
}
let mut result = E::constant(coeffs[coeffs.len() - 1].clone());
for coeff in coeffs.iter().rev().skip(1) {
result = E::add(E::mul(result, x.clone()), E::constant(coeff.clone()));
}
result
}
pub fn horner_expr<E: MathExpr, T>(coeffs: &[E::Repr<T>], x: E::Repr<T>) -> E::Repr<T>
where
T: NumericType + Add<Output = T> + Mul<Output = T>,
E::Repr<T>: Clone,
{
if coeffs.is_empty() {
return E::constant(T::default());
}
if coeffs.len() == 1 {
return coeffs[0].clone();
}
let mut result = coeffs[coeffs.len() - 1].clone();
for coeff in coeffs.iter().rev().skip(1) {
result = E::add(E::mul(result, x.clone()), coeff.clone());
}
result
}
pub fn from_roots<E: MathExpr, T>(roots: &[T], x: E::Repr<T>) -> E::Repr<T>
where
T: NumericType + Clone + Sub<Output = T> + num_traits::One,
E::Repr<T>: Clone,
{
if roots.is_empty() {
return E::constant(num_traits::One::one());
}
let mut result = E::sub(x.clone(), E::constant(roots[0].clone()));
for root in roots.iter().skip(1) {
let factor = E::sub(x.clone(), E::constant(root.clone()));
result = E::mul(result, factor);
}
result
}
pub fn horner_derivative<E: MathExpr, T>(coeffs: &[T], x: E::Repr<T>) -> E::Repr<T>
where
T: NumericType + Clone + Add<Output = T> + Mul<Output = T> + num_traits::FromPrimitive,
E::Repr<T>: Clone,
{
if coeffs.len() <= 1 {
return E::constant(T::default());
}
let mut deriv_coeffs = Vec::with_capacity(coeffs.len() - 1);
for (i, coeff) in coeffs.iter().enumerate().skip(1) {
let power = num_traits::FromPrimitive::from_usize(i).unwrap_or_else(|| T::default());
deriv_coeffs.push(coeff.clone() * power);
}
horner::<E, T>(&deriv_coeffs, x)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::final_tagless::{DirectEval, PrettyPrint};
#[test]
fn test_horner_polynomial() {
let coeffs = [1.0, 2.0, 3.0];
let result = horner::<DirectEval, f64>(&coeffs, 2.0);
assert_eq!(result, 17.0);
}
#[test]
fn test_horner_pretty_print() {
let coeffs = [1.0, 2.0, 3.0];
let x = PrettyPrint::var(0);
let result = horner::<PrettyPrint, f64>(&coeffs, x);
assert!(result.contains("var_0"));
}
#[test]
fn test_polynomial_from_roots() {
let roots = [1.0, 2.0];
let result = from_roots::<DirectEval, f64>(&roots, 0.0);
assert_eq!(result, 2.0);
}
#[test]
fn test_horner_expr() {
let coeffs = [
DirectEval::constant(1.0),
DirectEval::constant(3.0),
DirectEval::constant(2.0),
];
let result = horner_expr::<DirectEval, f64>(&coeffs, 2.0);
assert_eq!(result, 15.0);
}
#[test]
fn test_horner_derivative() {
let coeffs = [1.0, 3.0, 2.0];
let result = horner_derivative::<DirectEval, f64>(&coeffs, 2.0);
assert_eq!(result, 11.0); }
#[test]
fn test_empty_polynomial() {
let coeffs: [f64; 0] = [];
let result = horner::<DirectEval, f64>(&coeffs, 5.0);
assert_eq!(result, 0.0);
}
#[test]
fn test_single_coefficient() {
let coeffs = [42.0];
let result = horner::<DirectEval, f64>(&coeffs, 5.0);
assert_eq!(result, 42.0);
}
#[test]
fn test_polynomial_from_empty_roots() {
let roots: [f64; 0] = [];
let result = from_roots::<DirectEval, f64>(&roots, 5.0);
assert_eq!(result, 1.0);
}
#[test]
fn test_derivative_of_constant() {
let coeffs = [42.0]; let result = horner_derivative::<DirectEval, f64>(&coeffs, 5.0);
assert_eq!(result, 0.0); }
#[test]
fn test_derivative_of_linear() {
let coeffs = [1.0, 3.0]; let result = horner_derivative::<DirectEval, f64>(&coeffs, 5.0);
assert_eq!(result, 3.0); }
}