use crate::{SolverError, SolverResult, DEFAULT_ITERMAX, DEFAULT_TOL};
use num_traits::Float;
use std::ops::Fn;
pub struct Secant<T, F> {
f: F,
tolerance: T,
iter_max: usize,
}
impl<T, F> Secant<T, F>
where
T: Float,
F: Fn(T) -> T,
{
pub fn new(f: F) -> Self {
Self {
f,
tolerance: T::from(DEFAULT_TOL).unwrap(),
iter_max: DEFAULT_ITERMAX,
}
}
pub fn with_tol(&mut self, tol: T) -> &mut Self {
self.tolerance = tol;
self
}
pub fn with_itermax(&mut self, max: usize) -> &mut Self {
self.iter_max = max;
self
}
pub fn solve(&self, mut x0: T, mut x1: T) -> SolverResult<T> {
if x0 == x1 {
return Err(SolverError::IncorrectInput {
details: "the input points should be different",
});
};
let mut dx = T::max_value(); let mut iter = 1;
let mut f0 = (self.f)(x0);
let mut f1 = (self.f)(x1);
while dx.abs() > self.tolerance && iter <= self.iter_max {
dx = f1 * (x1 - x0) / (f1 - f0); x0 = x1;
x1 = x1 - dx;
f0 = f1;
f1 = (self.f)(x1);
iter += 1;
}
if iter >= self.iter_max {
return Err(SolverError::MaxIterReached(iter));
}
if x1.is_nan() {
return Err(SolverError::NotANumber);
}
Ok(x1)
}
}