use enums;
fn my_max(x1: f64, x2: f64) -> f64 {
if x1 > x2 {
x1
} else {
x2
}
}
fn central_deriv<T>(f: ::function<T>, param: &mut T, x: f64, h: f64, result: &mut f64, abs_err_round: &mut f64, abs_err_trunc: &mut f64) {
let fm1 = f(x - h, param);
let fp1 = f(x + h, param);
let fmh = f(x - h / 2f64, param);
let fph = f(x + h / 2f64, param);
let r3 = 0.5f64 * (fp1 - fm1);
let r5 = (4f64 / 3f64) * (fph - fmh) - (1f64 / 3f64) * r3;
let e3 = unsafe { (fp1.abs() + fm1.abs()) * ::DBL_EPSILON };
let e5 = unsafe { 2f64 * (fph.abs() + fmh.abs()) * ::DBL_EPSILON + e3 };
let dy = unsafe { my_max((r3 / h).abs(), (r5 / h).abs()) * (x.abs() / h) * ::DBL_EPSILON };
*result = r5 / h;
*abs_err_trunc = unsafe { ((r5 - r3) / h).abs() };
*abs_err_round = unsafe { (e5 / h).abs() + dy };
}
pub fn deriv_central<T>(f: ::function<T>, param: &mut T, x: f64, h: f64, result: &mut f64, abs_err: &mut f64) -> enums::Value {
let mut r_0 = 0f64;
let mut round = 0f64;
let mut trunc = 0f64;
central_deriv(f, param, x, h, &mut r_0, &mut round, &mut trunc);
let mut error = round + trunc;
if round < trunc && (round > 0f64 && trunc > 0f64) {
let mut r_opt = 0f64;
let mut round_opt = 0f64;
let mut trunc_opt = 0f64;
let h_opt = unsafe { h * (round / (2f64 * trunc)).powf(1f64 / 3f64) };
central_deriv(f, param, x, h_opt, &mut r_opt, &mut round_opt, &mut trunc_opt);
let error_opt = round_opt + trunc_opt;
if error_opt < error && unsafe { (r_opt - r_0).abs() } < 4f64 * error {
r_0 = r_opt;
error = error_opt;
}
}
*result = r_0;
*abs_err = error;
::Value::Success
}
fn forward_deriv<T>(f: ::function<T>, param: &mut T, x: f64, h: f64, result: &mut f64, abs_err_round: &mut f64, abs_err_trunc: &mut f64) {
let f1 = f(x + h / 4f64, param);
let f2 = f(x + h / 2f64, param);
let f3 = f(x + (3f64 / 4f64) * h, param);
let f4 = f(x + h, param);
let r2 = 2f64 * (f4 - f2);
let r4 = (22f64 / 3f64) * (f4 - f3) - (62f64 / 3f64) * (f3 - f2) + (52f64 / 3f64) * (f2 - f1);
let e4 = unsafe { 2f64 * 20.67f64 * (f4.abs() + f3.abs() + f2.abs() + f1.abs()) * ::DBL_EPSILON };
let dy = unsafe { my_max((r2 / h).abs(), (r4 / h).abs()) * (x.abs() / h) * ::DBL_EPSILON };
*result = r4 / h;
*abs_err_trunc = unsafe { ((r4 - r2) / h).abs() };
*abs_err_round = unsafe { (e4 / h).abs() + dy };
}
pub fn deriv_forward<T>(f: ::function<T>, param: &mut T, x: f64, h: f64, result: &mut f64, abs_err: &mut f64) -> enums::Value {
let mut r_0 = 0f64;
let mut round = 0f64;
let mut trunc = 0f64;
forward_deriv(f, param, x, h, &mut r_0, &mut round, &mut trunc);
let mut error = round + trunc;
if round < trunc && (round > 0f64 && trunc > 0f64) {
let mut r_opt = 0f64;
let mut round_opt = 0f64;
let mut trunc_opt = 0f64;
let h_opt = unsafe { h * (round / trunc).powf(1f64 / 2f64) };
forward_deriv(f, param, x, h_opt, &mut r_opt, &mut round_opt, &mut trunc_opt);
let error_opt = round_opt + trunc_opt;
if error_opt < error && unsafe { (r_opt - r_0).abs() } < 4f64 * error {
r_0 = r_opt;
error = error_opt;
}
}
*result = r_0;
*abs_err = error;
::Value::Success
}
pub fn deriv_backward<T>(f: ::function<T>, param: &mut T, x: f64, h: f64, result: &mut f64, abs_err: &mut f64) -> enums::Value {
deriv_forward(f, param, x, -h, result, abs_err)
}