use std::mem;
pub struct BracketRes {
pub a:f64,
pub b:f64,
pub c:f64,
pub fa:f64,
pub fb: f64,
pub fc: f64,
pub nr_iterations: usize
}
const GOLD: f64 = 1.618034_f64;
const GLIMIT: f64 = 100.0_f64;
const TINY: f64 = 1.0e-20_f64;
pub fn find_bracket<F: Fn (f64) -> f64>(fun: F, a: f64, b: f64) -> BracketRes {
let mut a = a;
let mut b = b;
let mut fa = fun(a);
let mut fb = fun(b);
if fb > fa {
mem::swap(&mut a, &mut b);
mem::swap(&mut fb, &mut fa);
}
let mut c = b + GOLD*(b - a);
let mut fc = fun(c);
let mut fu: f64;
let mut nr_iterations: usize = 1;
while fb > fc { let r = (b-a)*(fb-fc);
let q = (b-c)*(fb-fa);
let q_r = (q-r).abs().max(TINY);
let q_r = q_r.copysign(q-r);
let mut u = b - ((b-c)*q - (b-a)*r)/(2.0*q_r);
let ulim = b + GLIMIT*(c-b);
if (b-u)*(u-c) > 0.0 { fu = fun(u);
if fu < fc { a = b;
b = u;
fa = fb;
fb = fu;
break;
}
else if fu > fb { c = u;
fc = fu;
break;
}
u = c + GOLD*(c-b);
fu = fun(u);
}
else if (c-u)*(u-ulim) > 0.0 { fu = fun(u);
if fu < fc {
let d = u + GOLD*(u-c);
shft3(&mut b, &mut c, &mut u, d);
shft3(&mut fb, &mut fc, &mut fu, fun(u));
}
}
else if (u-ulim)*(ulim-c) >= 0.0 { u = ulim;
fu = fun(u);
}
else { u = c + GOLD*(c-b);
fu = fun(u);
}
shft3(&mut a, &mut b, &mut c, u);
shft3(&mut fa, &mut fb, &mut fc, fu);
nr_iterations += 1;
}
BracketRes{a, b, c, fa, fb, fc, nr_iterations}
}
#[inline]
pub fn shft2(a: &mut f64, b: &mut f64, c: f64) {
*a = *b;
*b = c;
}
#[inline]
pub fn shft3(a: &mut f64, b: &mut f64, c: &mut f64, d: f64) {
*a = *b;
*b = *c;
*c = d;
}
#[inline]
pub fn mov3(a: &mut f64, b: &mut f64, c: &mut f64, d: f64, e: f64, f: f64) {
*a = d;
*b = e;
*c = f;
}
#[cfg(test)]
#[test]
fn test_poly2() {
let poly2 = |x: f64| (x-1.0)*(x-2.0);
let ranges = vec![(10.0, 20.0), (20.0, 10.0), (-10.0, 0.0),
(-2000.0, -1000.0), (-10_000.0, 30_000.0), (0.0001, 0.0002), (-0.00001, 1.4999)];
for range in ranges {
let bracket = find_bracket(poly2, range.0, range.1);
println!("Bracket: [{:6.2} < {:6.2} < {:6.2}] with values [{:6.2} < {:6.2} < {:6.2}] iterations:{}",
bracket.a, bracket.b, bracket.c,
bracket.fa, bracket.fb, bracket.fc,
bracket.nr_iterations
);
assert!(bracket.fa > bracket.fb && bracket.fb < bracket.fc);
}
}
#[cfg(test)]
#[test]
fn test_cosine() {
let cosine = |x: f64| x.cos();
let ranges = vec![(0.01, 1.0)];
for range in ranges {
let bracket = find_bracket(cosine, range.0, range.1);
println!("Bracket: [{:6.2} < {:6.2} < {:6.2}] with values [{:6.2} < {:6.2} < {:6.2}] iterations:{}",
bracket.a, bracket.b, bracket.c,
bracket.fa, bracket.fb, bracket.fc,
bracket.nr_iterations
);
assert!(bracket.fa > bracket.fb && bracket.fb < bracket.fc);
}
}
#[cfg(test)]
#[test]
fn test_saw() {
let saw = |x: f64| if x >= 0.0 { x*x*x } else { -x / 1000.0 } ;
let ranges = vec![(10.0, 20.0), (20.0, 10.0), (-10.0, 0.0),
(-2000.0, -1000.0), (-10_000.0, 30_000.0), (0.0001, 0.0002), (-0.00001, 1.4999)];
for range in ranges {
let bracket = find_bracket(saw, range.0, range.1);
println!("Bracket: [{:6.2} < {:6.2} < {:6.2}] with values [{:6.2} < {:6.2} < {:6.2}] iterations:{}",
bracket.a, bracket.b, bracket.c,
bracket.fa, bracket.fb, bracket.fc,
bracket.nr_iterations
);
assert!(bracket.fa > bracket.fb && bracket.fb < bracket.fc);
}
}