use crate::erf::erf;
use crate::gamma::gamma;
use crate::hypergeometric::hyp1f1;
use crate::{bessel, erf as erf_mod};
#[derive(Debug, Clone)]
pub struct PowerSeries {
pub center: f64,
pub coefficients: Vec<f64>,
pub order: usize,
}
impl PowerSeries {
pub fn new(center: f64, coefficients: Vec<f64>) -> Self {
let order = coefficients.len().saturating_sub(1);
Self {
center,
coefficients,
order,
}
}
pub fn eval(&self, x: f64) -> f64 {
let t = x - self.center;
let mut result = 0.0f64;
for &coeff in self.coefficients.iter().rev() {
result = result * t + coeff;
}
result
}
pub fn diff(&self) -> PowerSeries {
if self.coefficients.len() <= 1 {
return PowerSeries::new(self.center, vec![0.0]);
}
let new_coeffs: Vec<f64> = self
.coefficients
.iter()
.enumerate()
.skip(1) .map(|(k, &a)| (k as f64) * a)
.collect();
PowerSeries::new(self.center, new_coeffs)
}
pub fn truncate(&self, n: usize) -> PowerSeries {
let coeffs = self.coefficients[..n.min(self.coefficients.len())].to_vec();
PowerSeries::new(self.center, coeffs)
}
pub fn compose(&self, g: &PowerSeries) -> PowerSeries {
let out_order = self.order.min(g.order);
let mut result = vec![0.0f64; out_order + 1];
let mut g_pow = vec![vec![0.0f64; out_order + 1]; out_order + 1];
g_pow[0][0] = 1.0;
for k in 1..=out_order {
if k < g.coefficients.len() {
g_pow[1][k] = g.coefficients[k];
}
}
for j in 2..=out_order {
for i in 0..=out_order {
for l in 0..=out_order {
if i + l <= out_order {
let v = g_pow[j - 1][i] * g_pow[1][l];
g_pow[j][i + l] += v;
}
}
}
}
for (j, &aj) in self.coefficients.iter().enumerate() {
if j > out_order {
break;
}
for k in 0..=out_order {
result[k] += aj * g_pow[j][k];
}
}
PowerSeries::new(g.center, result)
}
}
fn complex_step_kth_deriv<F>(f: &F, x: f64, k: usize) -> f64
where
F: Fn(f64) -> f64,
{
if k == 0 {
return f(x);
}
if k == 1 {
let h = 1e-6_f64;
return (-f(x + 2.0 * h) + 8.0 * f(x + h) - 8.0 * f(x - h) + f(x - 2.0 * h)) / (12.0 * h);
}
let h = 1e-3_f64;
let prev_deriv = |t: f64| complex_step_kth_deriv(f, t, k - 1);
(-prev_deriv(x + 2.0 * h) + 8.0 * prev_deriv(x + h) - 8.0 * prev_deriv(x - h)
+ prev_deriv(x - 2.0 * h))
/ (12.0 * h)
}
fn taylor_from_fn<F>(f: F, x0: f64, order: usize) -> PowerSeries
where
F: Fn(f64) -> f64,
{
let mut coeffs = Vec::with_capacity(order + 1);
let mut factorial = 1.0_f64;
for k in 0..=order {
if k > 0 {
factorial *= k as f64;
}
let dk = complex_step_kth_deriv(&f, x0, k);
coeffs.push(dk / factorial);
}
PowerSeries::new(x0, coeffs)
}
pub fn taylor_gamma(x0: f64, order: usize) -> PowerSeries {
let safe_order = order.min(6);
taylor_from_fn(gamma, x0, safe_order)
}
pub fn taylor_erf(x0: f64, order: usize) -> PowerSeries {
if x0 == 0.0 {
let two_over_sqrt_pi = 2.0 / std::f64::consts::PI.sqrt();
let mut coeffs = vec![0.0f64; order + 1];
let mut factorial_n = 1.0_f64;
for n in 0..=order / 2 {
if n > 0 {
factorial_n *= n as f64;
}
let k = 2 * n + 1;
if k <= order {
let sign = if n % 2 == 0 { 1.0 } else { -1.0 };
coeffs[k] = two_over_sqrt_pi * sign / (factorial_n * (2 * n + 1) as f64);
}
}
PowerSeries::new(0.0, coeffs)
} else {
let safe_order = order.min(6);
taylor_from_fn(erf, x0, safe_order)
}
}
pub fn taylor_bessel_j(n: i32, x0: f64, order: usize) -> PowerSeries {
if x0 == 0.0 && n >= 0 {
let nu = n as usize;
let mut coeffs = vec![0.0f64; order + 1];
let mut factorial_k = 1.0_f64;
let mut gamma_nk1 = gamma_at_integer(nu + 1); for k in 0..=order {
let power = nu + 2 * k;
if power <= order {
let sign = if k % 2 == 0 { 1.0 } else { -1.0 };
let two_pow = (2.0_f64).powi((nu + 2 * k) as i32);
coeffs[power] = sign / (factorial_k * gamma_nk1 * two_pow);
}
if k > 0 {
factorial_k *= (k + 1) as f64;
} else {
factorial_k = 1.0;
}
gamma_nk1 *= (nu + k + 1) as f64; }
PowerSeries::new(0.0, coeffs)
} else {
let safe_order = order.min(6);
let f = |x: f64| bessel::jn(n, x);
taylor_from_fn(f, x0, safe_order)
}
}
fn gamma_at_integer(n: usize) -> f64 {
let mut result = 1.0_f64;
for i in 1..n {
result *= i as f64;
}
result
}
pub fn taylor_1f1(a: f64, b: f64, z0: f64, order: usize) -> PowerSeries {
if z0 == 0.0 {
let mut coeffs = Vec::with_capacity(order + 1);
let mut rising_a = 1.0_f64; let mut rising_b = 1.0_f64; let mut factorial_n = 1.0_f64; for n in 0..=order {
coeffs.push(rising_a / (rising_b * factorial_n));
rising_a *= a + n as f64;
rising_b *= b + n as f64;
factorial_n *= (n + 1) as f64;
}
PowerSeries::new(0.0, coeffs)
} else {
let safe_order = order.min(6);
let f = |z: f64| hyp1f1(a, b, z).unwrap_or(f64::NAN);
taylor_from_fn(f, z0, safe_order)
}
}