use enums;
use std::intrinsics::{fabsf64, powf64};
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 { (fabsf64(fp1) + fabsf64(fm1)) * ::DBL_EPSILON };
let e5 = unsafe { 2f64 * (fabsf64(fph) + fabsf64(fmh)) * ::DBL_EPSILON + e3 };
let dy = unsafe { my_max(fabsf64(r3 / h), fabsf64(r5 / h)) * (fabsf64(x) / h) * ::DBL_EPSILON };
*result = r5 / h;
*abs_err_trunc = unsafe { fabsf64((r5 - r3) / h) };
*abs_err_round = unsafe { fabsf64(e5 / h) + 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::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 * powf64(round / (2f64 * trunc), 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 { fabsf64(r_opt - r_0) } < 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 * (fabsf64(f4) + fabsf64(f3) + fabsf64(f2) + fabsf64(f1)) * ::DBL_EPSILON };
let dy = unsafe { my_max(fabsf64(r2 / h), fabsf64(r4 / h)) * (fabsf64(x) / h) * ::DBL_EPSILON };
*result = r4 / h;
*abs_err_trunc = unsafe { fabsf64((r4 - r2) / h) };
*abs_err_round = unsafe { fabsf64(e4 / h) + 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::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 * powf64(round / trunc, 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 { fabsf64(r_opt - r_0) } < 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::Value {
deriv_forward(f, param, x, -h, result, abs_err)
}