use std::f64::consts::TAU;
use crate::{Vec3, Star};
use crate::{rpot_grad, rpot_val, set_earth};
pub fn linmin(q: f64, star: Star, spin: f64, cosi: f64, sini: f64, p: &Vec3, phi: &mut f64, lam: &mut f64, mut dphi: f64, mut dlam: f64, phi1: f64, phi2: f64, lam1: f64, lam2: f64, pref: f64, acc: f64) -> (f64, bool) {
let mut jammed = false;
let make_func = |phi0: f64, lam0: f64, dphi0: f64, dlam0: f64| {
let func = move |x: f64| {
let earth: Vec3 = set_earth(cosi, sini, phi0 + dphi0 * x);
rpot_val(q, star, spin, &earth, p, lam0 + dlam0 * x)
};
let dfunc = move |x: f64| {
let earth: Vec3 = set_earth(cosi, sini, phi0 + dphi0 * x);
let (dp, dl) = rpot_grad(q, star, spin, &earth, p, lam0 + dlam0 * x);
dp * dphi0 + dl * dlam0
};
(func, dfunc)
};
let (mut func, mut dfunc) = make_func(*phi, *lam, dphi, dlam);
let mut xmax: f64 = 1.0e30;
let mut nbound: i32 = 0;
let mut check = |bound: f64, val: f64, d: f64, id: i32| {
if d != 0.0 {
let x: f64 = (bound - val) / d;
if x > 0.0 && x < xmax {
xmax = x;
nbound = id;
}
}
};
check(phi1, *phi, dphi, 1);
check(phi2, *phi, dphi, 2);
check(lam1, *lam, dlam, 3);
check(lam2, *lam, dlam, 4);
let xa: f64 = 0.0;
let fa: f64 = func(xa);
let mut xb: f64 = 1e-8 * xmax;
let mut fb: f64 = func(xb);
let mut nten: i32 = 0;
const NTEN: i32 = 7;
while (fb >= fa || xa == xb) && nten < NTEN {
nten += 1;
xb *= 10.0;
fb = func(xb);
}
if fb <= pref {
*phi += dphi * xb;
*lam += dlam * xb;
return (fb, jammed);
}
if fb >= fa {
return (fa, jammed);
}
let mut bracketted = false;
let mut xc: f64 = 0.0;
let mut fc: f64 = 0.0;
let xbold: f64 = xb;
let fbold: f64 = fb;
xmax -= xb;
const NTRY: i32 = 5;
let xmax_rem: f64 = xmax - xb;
for n in 1..=NTRY {
xc = xbold + xmax_rem * (n as f64) / (NTRY as f64);
fc = func(xc);
if fc <= pref {
*phi += dphi * xc;
*lam += dlam * xc;
return (fc, jammed);
}
if fc < fb {
xb = xc;
fb = fc;
} else {
bracketted = true;
break;
}
}
jammed = false;
if !bracketted {
let dc: f64 = dfunc(xc);
if dc > 0.0 {
xb = xbold;
let db: f64 = dfunc(xb);
if db < 0.0 {
let mut xm: f64 = (xb+xc)/2.0;
while (xc-xb) > 1.0e-8*xc {
xm = (xb+xc)/2.0;
if dfunc(xm) > 0.0 {
xc = xm;
} else {
xb = xm;
}
}
let fm: f64 = func(xm);
if fm <= pref {
*phi += dphi*xm;
*lam += dlam*xm;
return (fm, jammed);
}
if fm < fc && fm < fbold {
xb = xm;
} else {
panic!("Roche::linmin: failed to bracket minimum, error 3");
}
} else {
panic!("Roche::linmin: failed to bracket minimum, error 1");
}
} else {
jammed = true;
*phi += dphi*xmax;
*lam += dlam*xmax;
xmax = 1.0;
(dphi, dlam) = rpot_grad(q, star, spin, &set_earth(cosi, sini, *phi), p, *lam);
match nbound {
1 => {
*phi = phi1;
dphi = 0.0;
if dlam > 0.0 {
dlam = lam1 - *lam;
} else {
dlam = lam2 - *lam;
}
}
2 => {
*phi = phi2;
dphi = 0.0;
if dlam > 0.0 {
dlam = lam1 - *lam;
} else {
dlam = lam2 - *lam;
}
}
3 => {
*lam = lam1;
dlam = 0.0;
if dphi > 0.0 {
dphi = phi1 - *phi;
} else {
dphi = phi2 - *phi;
}
}
4 => {
*lam = lam1;
dlam = 0.0;
if dphi > 0.0 {
dphi = phi1 - *phi;
} else {
dphi = phi2 - *phi;
}
}
_ => {}
}
(func, dfunc) = make_func(*phi, *lam, dphi, dlam);
nten = 0;
xb = 1.0e-6;
fb = func(xb);
while fb >= fa && nten < NTEN {
xb *= 10.0;
nten += 1;
fb = func(xb);
}
if fb <= pref {
*phi += dphi*xb;
*lam += dlam*xb;
return (fb, jammed);
}
if fb >= fa {
return (fa, jammed);
}
bracketted = false;
for n in 1..=NTRY {
xc = xmax*n as f64/NTRY as f64;
fc = func(xc);
if fc <= pref {
*phi += dphi*xc;
*lam += dlam*xc;
return (fc, jammed)
} else {
bracketted = true;
break;
}
}
if !bracketted {
if dfunc(xc) > 0.0 {
panic!("Roche::linmin: failed to bracket minimum, error 2");
} else {
*phi += dphi*xmax;
*lam += dlam*xmax;
return (fc, jammed);
}
}
}
}
let xacc: f64 = acc / ((TAU*dphi).powi(2) + dlam*dlam).sqrt();
let (xmin, pmin) = dbrent(xa, xb, xc, &func, &dfunc, xacc, true, pref).unwrap();
*phi += dphi * xmin;
*lam += dlam * xmin;
(pmin, jammed)
}
pub fn pot_min(q: f64, star: Star, spin: f64, cosi: f64, sini: f64, p: &Vec3, phi1: f64, phi2: f64, lam1: f64, lam2: f64, rref: f64, pref: f64, acc: f64, phi: &mut f64, lam: &mut f64) -> bool {
*phi = (phi1 + phi2)/2.;
*lam = (lam1 + lam2)/2.;
let mut rp: f64 = TAU * *phi;
let (sinp, cosp) = rp.sin_cos();
let mut earth: Vec3 = Vec3::new(sini*cosp, -sini*sinp, cosi);
let mut pot: f64;
let mut dphi: f64;
let mut dlam: f64;
pot = rpot_val(q, star, spin, &earth, p, *lam);
(dphi, dlam) = rpot_grad(q, star, spin, &earth, p, *lam);
if pot <= pref {
return true
};
let mut gdphi: f64 = -dphi;
let mut gdlam: f64 = -dlam;
dphi = gdphi;
let mut hdphi: f64 = gdphi;
dlam = gdlam;
let mut hdlam: f64 = gdlam;
const ITMAX: i32 = 200;
let delphi: f64 = q/(1.0 + q)*((acc*acc)/(rref*rref))/2.0;
let mut pmin: f64;
let mut gam: f64;
let mut dgg: f64;
let mut gg: f64;
let mut jammed: bool;
for _ in 0..ITMAX {
(pmin, jammed) = linmin(q, star, spin, cosi, sini, p, phi, lam, dphi, dlam, phi1, phi2, lam1, lam2, pref, acc);
if pmin <= pref {
return true
}
if jammed || (pmin - pot).abs() < delphi {
return false
}
pot = pmin;
rp = TAU * *phi;
let (sinp, cosp) = rp.sin_cos();
earth.set(sini*cosp, -sini*sinp, cosi);
(dphi, dlam) = rpot_grad(q, star, spin, &earth, p, *lam);
gg = gdphi*gdphi + gdlam*gdlam;
if gg == 0. {
return false
}
dgg = (dphi+gdphi)*dphi + (dlam+gdlam)*dlam;
gam = dgg/gg;
gdphi = -dphi;
gdlam = -dlam;
dphi = gdphi + gam*hdphi;
hdphi = gdphi + gam*hdphi;
dlam = gdlam + gam*hdlam;
hdlam = gdlam + gam*hdlam;
}
panic!("Too many iterations.")
}
pub fn dbrent<F, G>(
ax: f64,
bx: f64,
cx: f64,
func: F,
dfunc: G,
acc: f64,
stopfast: bool,
fref: f64,
) -> Result<(f64, f64), &'static str>
where
F: Fn(f64) -> f64,
G: Fn(f64) -> f64,
{
const ITMAX: usize = 100;
let mut a: f64 = ax.min(cx);
let mut b: f64 = ax.max(cx);
let mut x: f64 = bx;
let mut w: f64 = bx;
let mut v: f64 = bx;
let mut fx: f64 = func(x);
let mut fw: f64 = fx;
let mut fv: f64 = fx;
if stopfast && fx < fref {
return Ok((x, fx));
}
let mut dx: f64 = dfunc(x);
let mut dw: f64 = dx;
let mut dv: f64 = dx;
let mut e: f64 = 0.0;
let mut d: f64 = 0.0;
for _ in 0..ITMAX {
let xm: f64 = 0.5 * (a + b);
let tol1: f64 = acc;
let tol2: f64 = 2.0 * tol1;
if (x - xm).abs() <= (tol2 - 0.5 * (b - a)) {
return Ok((x, fx));
}
let mut d1: f64;
let mut d2: f64;
if e.abs() > tol1 {
d1 = 2.0 * (b - a);
d2 = d1;
if dw != dx {
d1 = (w - x) * dx / (dx - dw);
}
if dv != dx {
d2 = (v - x) * dx / (dx - dv);
}
let u1: f64 = x + d1;
let u2: f64 = x + d2;
let ok1: bool = (a - u1) * (u1 - b) > 0.0 && dx * d1 <= 0.0;
let ok2: bool = (a - u2) * (u2 - b) > 0.0 && dx * d2 <= 0.0;
let olde: f64 = e;
e = d;
if ok1 || ok2 {
d = if ok1 && ok2 {
if d1.abs() < d2.abs() { d1 } else { d2 }
} else if ok1 {
d1
} else {
d2
};
if d.abs() <= 0.5 * olde.abs() {
let u: f64 = x + d;
if (u - a) < tol2 || (b - u) < tol2 {
d = tol1.copysign(xm - x);
}
} else {
e = if dx >= 0.0 { a - x } else { b - x };
d = 0.5 * e;
}
} else {
e = if dx >= 0.0 { a - x } else { b - x };
d = 0.5 * e;
}
} else {
e = if dx >= 0.0 { a - x } else { b - x };
d = 0.5 * e;
}
let u: f64;
let fu: f64;
if d.abs() >= tol1 {
u = x + d;
fu = func(u);
if stopfast && fu < fref {
return Ok((u, fu));
}
} else {
u = x + tol1.copysign(d);
fu = func(u);
if stopfast && fu < fref {
return Ok((u, fu));
}
if fu > fx {
return Ok((x, fx));
}
}
let du: f64 = dfunc(u);
if fu <= fx {
if u >= x { a = x } else { b = x };
v = w; fv = fw; dv = dw;
w = x; fw = fx; dw = dx;
x = u; fx = fu; dx = du;
} else {
if u < x { a = u } else { b = u };
if fu <= fw || w == x {
v = w; fv = fw; dv = dw;
w = u; fw = fu; dw = du;
} else if fu < fv || v == x || v == w {
v = u; fv = fu; dv = du;
}
}
}
Err("dbrent: too many iterations")
}