use crate::Value;
use ffi::FFI;
use sys;
use sys::libc::{c_double, c_void};
ffi_wrapper!(
RootFSolverType,
*const sys::gsl_root_fsolver_type,
"The root bracketing algorithms described in this section require an initial interval which is
guaranteed to contain a root—if a and b are the endpoints of the interval then f (a) must
differ in sign from f (b). This ensures that the function crosses zero at least once in the
interval. If a valid initial interval is used then these algorithm cannot fail, provided the
function is well-behaved.
Note that a bracketing algorithm cannot find roots of even degree, since these do not
cross the x-axis."
);
impl RootFSolverType {
#[doc(alias = "gsl_root_fsolver_bisection")]
pub fn bisection() -> RootFSolverType {
ffi_wrap!(gsl_root_fsolver_bisection)
}
#[doc(alias = "gsl_root_fsolver_brent")]
pub fn brent() -> RootFSolverType {
ffi_wrap!(gsl_root_fsolver_brent)
}
#[doc(alias = "gsl_root_fsolver_falsepos")]
pub fn falsepos() -> RootFSolverType {
ffi_wrap!(gsl_root_fsolver_falsepos)
}
}
ffi_wrapper!(
RootFSolver<'a>,
*mut sys::gsl_root_fsolver,
gsl_root_fsolver_free
;inner_call: sys::gsl_function_struct => sys::gsl_function_struct { function: None, params: std::ptr::null_mut() };
;inner_closure: Option<Box<dyn Fn(f64) -> f64 + 'a>> => None;,
"This is a workspace for finding roots using methods which do not require derivatives."
);
impl<'a> RootFSolver<'a> {
#[doc(alias = "gsl_root_fsolver_alloc")]
pub fn new(t: RootFSolverType) -> Option<RootFSolver<'a>> {
let tmp = unsafe { sys::gsl_root_fsolver_alloc(t.unwrap_shared()) };
if tmp.is_null() {
None
} else {
Some(RootFSolver::wrap(tmp))
}
}
#[doc(alias = "gsl_root_fsolver_set")]
pub fn set<F: Fn(f64) -> f64 + 'a>(
&mut self,
f: F,
x_lower: f64,
x_upper: f64,
) -> Result<(), Value> {
self.inner_call = wrap_callback!(f, F + 'a);
self.inner_closure = Some(Box::new(f));
let ret = unsafe {
sys::gsl_root_fsolver_set(self.unwrap_unique(), &mut self.inner_call, x_lower, x_upper)
};
result_handler!(ret, ())
}
#[doc(alias = "gsl_root_fsolver_iterate")]
pub fn iterate(&mut self) -> Result<(), Value> {
let ret = unsafe { sys::gsl_root_fsolver_iterate(self.unwrap_unique()) };
result_handler!(ret, ())
}
#[doc(alias = "gsl_root_fsolver_name")]
pub fn name(&self) -> String {
unsafe {
let tmp = sys::gsl_root_fsolver_name(self.unwrap_shared());
String::from_utf8_lossy(::std::ffi::CStr::from_ptr(tmp).to_bytes()).to_string()
}
}
#[doc(alias = "gsl_root_fsolver_root")]
pub fn root(&self) -> f64 {
unsafe { sys::gsl_root_fsolver_root(self.unwrap_shared()) }
}
#[doc(alias = "gsl_root_fsolver_x_lower")]
pub fn x_lower(&self) -> f64 {
unsafe { sys::gsl_root_fsolver_x_lower(self.unwrap_shared()) }
}
#[doc(alias = "gsl_root_fsolver_x_upper")]
pub fn x_upper(&self) -> f64 {
unsafe { sys::gsl_root_fsolver_x_upper(self.unwrap_shared()) }
}
}
ffi_wrapper!(
RootFdfSolverType,
*const sys::gsl_root_fdfsolver_type,
"The root polishing algorithms described in this section require an initial guess for the
location of the root. There is no absolute guarantee of convergence—the function must be
suitable for this technique and the initial guess must be sufficiently close to the root
for it to work. When these conditions are satisfied then convergence is quadratic.
These algorithms make use of both the function and its derivative."
);
impl RootFdfSolverType {
#[doc(alias = "gsl_root_fdfsolver_newton")]
pub fn newton() -> RootFdfSolverType {
ffi_wrap!(gsl_root_fdfsolver_newton)
}
#[doc(alias = "gsl_root_fdfsolver_secant")]
pub fn secant() -> RootFdfSolverType {
ffi_wrap!(gsl_root_fdfsolver_secant)
}
#[doc(alias = "gsl_root_fdfsolver_steffenson")]
pub fn steffenson() -> RootFdfSolverType {
ffi_wrap!(gsl_root_fdfsolver_steffenson)
}
}
ffi_wrapper!(
RootFdfSolver<'a>,
*mut sys::gsl_root_fdfsolver,
gsl_root_fdfsolver_free
;inner_call: sys::gsl_function_fdf_struct => sys::gsl_function_fdf_struct{f: None, df: None, fdf: None, params: std::ptr::null_mut()};
;inner_f_closure: Option<Box<dyn Fn(f64) -> f64 + 'a>> => None;
;inner_df_closure: Option<Box<dyn Fn(f64) -> f64 + 'a>> => None;
;inner_fdf_closure: Option<Box<dyn Fn(f64, &mut f64, &mut f64) + 'a>> => None;,
"This is a workspace for finding roots using methods which do require derivatives."
);
impl<'a> RootFdfSolver<'a> {
#[doc(alias = "gsl_root_fdfsolver_alloc")]
pub fn new(t: RootFdfSolverType) -> Option<RootFdfSolver<'a>> {
let tmp = unsafe { sys::gsl_root_fdfsolver_alloc(t.unwrap_shared()) };
if tmp.is_null() {
None
} else {
Some(RootFdfSolver::wrap(tmp))
}
}
#[doc(alias = "gsl_root_fdfsolver_set")]
pub fn set<
F: Fn(f64) -> f64 + 'a,
DF: Fn(f64) -> f64 + 'a,
FDF: Fn(f64, &mut f64, &mut f64) + 'a,
>(
&mut self,
f: F,
df: DF,
fdf: FDF,
root: f64,
) -> Result<(), Value> {
unsafe extern "C" fn inner_f<'a, F: Fn(f64) -> f64 + 'a>(
x: c_double,
params: *mut c_void,
) -> f64 {
let f: &F = &*(params as *const F);
f(x)
}
unsafe extern "C" fn inner_df<'a, DF: Fn(f64) -> f64 + 'a>(
x: c_double,
params: *mut c_void,
) -> f64 {
let df: &DF = &*(params as *const DF);
df(x)
}
unsafe extern "C" fn inner_fdf<'a, FDF: Fn(f64, &mut f64, &mut f64) + 'a>(
x: c_double,
params: *mut c_void,
y: *mut c_double,
dy: *mut c_double,
) {
let fdf: &FDF = &*(params as *const FDF);
fdf(x, &mut *y, &mut *dy);
}
self.inner_call = sys::gsl_function_fdf {
f: Some(inner_f::<F>),
df: Some(inner_df::<DF>),
fdf: Some(inner_fdf::<FDF>),
params: &(&f, &df, &fdf) as *const _ as *mut _,
};
self.inner_f_closure = Some(Box::new(f));
self.inner_df_closure = Some(Box::new(df));
self.inner_fdf_closure = Some(Box::new(fdf));
let ret = unsafe {
sys::gsl_root_fdfsolver_set(self.unwrap_unique(), &mut self.inner_call, root)
};
result_handler!(ret, ())
}
#[doc(alias = "gsl_root_fdfsolver_iterate")]
pub fn iterate(&mut self) -> Result<(), Value> {
let ret = unsafe { sys::gsl_root_fdfsolver_iterate(self.unwrap_unique()) };
result_handler!(ret, ())
}
#[doc(alias = "gsl_root_fdfsolver_name")]
pub fn name(&self) -> Option<&str> {
unsafe {
let ptr = sys::gsl_root_fdfsolver_name(self.unwrap_shared());
if ptr.is_null() {
return None;
}
let mut len = 0;
while *ptr.add(len) != 0 {
len += 1;
}
let slice = std::slice::from_raw_parts(ptr as *const _, len);
std::str::from_utf8(slice).ok()
}
}
#[doc(alias = "gsl_root_fdfsolver_root")]
pub fn root(&self) -> f64 {
unsafe { sys::gsl_root_fdfsolver_root(self.unwrap_shared()) }
}
}
#[cfg(any(test, doctest))]
mod test {
use super::*;
use roots::{test_delta, test_interval};
fn quadratic_test_fn(x: f64) -> f64 {
x.powf(2.0) - 5.0
}
fn quadratic_test_fn_df(x: f64) -> f64 {
2.0 * x
}
fn quadratic_test_fn_fdf(x: f64, y: &mut f64, dy: &mut f64) {
*y = x.powf(2.0) - 5.0;
*dy = 2.0 * x;
}
#[test]
fn test_root() {
let mut root = RootFSolver::new(RootFSolverType::brent()).unwrap();
root.set(quadratic_test_fn, 0.0, 5.0).unwrap();
let max_iter = 10usize;
let epsabs = 0.0001;
let epsrel = 0.0000001;
let mut status = Value::Continue;
let mut iter = 0usize;
println!("Testing: {}", root.name());
println!("iter, \t [x_lo, x_hi], \t min, \t error");
while matches!(status, Value::Continue) && iter < max_iter {
root.iterate().unwrap();
let r = root.root();
let x_lo = root.x_lower();
let x_hi = root.x_upper();
status = test_interval(x_lo, x_hi, epsabs, epsrel);
if status == Value::Success {
println!("Converged");
}
println!(
"{} \t [{:.5}, {:.5}] \t {:.5} \t {:.5}",
iter,
x_lo,
x_hi,
r,
x_hi - x_lo
);
iter += 1;
}
assert!(matches!(status, Value::Success))
}
#[test]
fn test_root_fdf() {
let r_expected = 5.0_f64.sqrt();
let guess_value = 1.0;
let mut root = RootFdfSolver::new(RootFdfSolverType::steffenson()).unwrap();
root.set(
quadratic_test_fn,
quadratic_test_fn_df,
quadratic_test_fn_fdf,
guess_value,
)
.unwrap();
let max_iter = 20usize;
let epsabs = 0.0001;
let epsrel = 0.0000001;
let mut status = Value::Continue;
let mut iter = 0usize;
println!("Testing: {}", root.name().unwrap());
println!("iter, \t root, \t rel error \t abs error");
let mut x = guess_value;
while matches!(status, Value::Continue) && iter < max_iter {
root.iterate().unwrap();
let x_0 = x;
x = root.root();
status = test_delta(x, x_0, epsabs, epsrel);
if matches!(status, Value::Success) {
println!("Converged");
}
println!(
"{} \t {:.5} \t {:.5} \t {:.5}",
iter,
x,
x - x_0,
x - r_expected
);
iter += 1;
}
assert!(matches!(status, Value::Success))
}
}