use std::collections::HashMap;
use crate::numerical::elementary::eval_expr;
use crate::symbolic::core::Expr;
pub fn sum_series_numerical(
term_expr: &Expr,
var: &str,
start_n: usize,
max_terms: usize,
tolerance: f64,
) -> Result<f64, String> {
let mut sum = 0.0;
let mut vars = HashMap::new();
for i in start_n..(start_n + max_terms) {
vars.insert(var.to_string(), i as f64);
let term_val = eval_expr(term_expr, &vars)?;
if term_val.abs() < tolerance {
break;
}
sum += term_val;
}
Ok(sum)
}
#[must_use]
pub fn aitken_acceleration(sequence: &[f64]) -> Vec<f64> {
if sequence.len() < 3 {
return vec![];
}
let mut accelerated_seq = Vec::new();
for i in 0..(sequence.len() - 2) {
let s_n = sequence[i];
let s_n1 = sequence[i + 1];
let s_n2 = sequence[i + 2];
let denominator = 2.0f64.mul_add(-s_n1, s_n2) + s_n;
if denominator.abs() > 1e-9 {
let aitken_s = s_n - (s_n1 - s_n).powi(2) / denominator;
accelerated_seq.push(aitken_s);
}
}
accelerated_seq
}
pub fn find_sequence_limit(
term_expr: &Expr,
var: &str,
max_terms: usize,
tolerance: f64,
) -> Result<f64, String> {
let mut sequence = Vec::new();
let mut vars = HashMap::new();
for i in 0..max_terms {
vars.insert(var.to_string(), i as f64);
sequence.push(eval_expr(term_expr, &vars)?);
}
let mut accelerated = aitken_acceleration(&sequence);
while accelerated.len() > 1 {
let last = match accelerated.last() {
| Some(l) => l,
| None => {
return Err("Unexpected empty \
sequence in \
convergence loop."
.to_string());
},
};
let second_last = accelerated[accelerated.len() - 2];
if (last - second_last).abs() < tolerance {
return Ok(*last);
}
accelerated = aitken_acceleration(&accelerated);
}
accelerated
.last()
.copied()
.ok_or_else(|| "Convergence not found".to_string())
}
#[must_use]
pub fn richardson_extrapolation(sequence: &[f64]) -> Vec<f64> {
if sequence.is_empty() {
return vec![];
}
let n = sequence.len();
let mut table = vec![vec![0.0; n]; n];
for (i, &val) in sequence.iter().enumerate() {
table[i][0] = val;
}
for j in 1..n {
for i in j..n {
let power_of_4 = 4.0f64.powi(j as i32);
table[i][j] =
power_of_4.mul_add(table[i][j - 1], -table[i - 1][j - 1]) / (power_of_4 - 1.0);
}
}
(0..n).map(|i| table[i][i]).collect()
}
#[must_use]
pub fn wynn_epsilon(sequence: &[f64]) -> Vec<f64> {
let n = sequence.len();
if n < 3 {
return Vec::from(sequence);
}
let mut eps = vec![vec![0.0; n]; n + 1];
eps[0][..n].copy_from_slice(&sequence[..n]);
for k in 0..n - 1 {
for i in 0..n - k - 1 {
let numerator = 1.0;
let denominator = eps[k][i + 1] - eps[k][i];
if denominator.abs() < 1e-12 {
eps[k + 1][i] = 1e12; } else {
let prev_term = if k == 0 {
0.0
} else {
eps[k - 1][i + 1]
};
eps[k + 1][i] = prev_term + numerator / denominator;
}
}
}
let mut diag = Vec::new();
let mut k = 0;
loop {
if k >= n {
break;
}
diag.push(eps[k][0]);
k += 2;
}
if diag.is_empty() {
Vec::from(sequence)
} else {
diag
}
}