function chandrupatla(
f,
b = 0,
a = 1,
verbose = true,
eps_m = null,
eps_a = null
) {
const { abs, max, min } = Math,
log = verbose ? [] : null,
points = verbose ? [] : null;
let count = 0;
if (a === b) b = a + 1;
let c = a,
fa = f(a),
fb = f(b),
fc = fa,
Nmax = 100;
count += 2;
points.push([b, fb], [a, fa]);
while (fa * fb > 0) {
b += 1.2 * ((fb - fa) / (b - a));
fb = f(b);
count++;
if (verbose) log.push(`BRA b = ${b}`), points.push([b, fb]);
if (Nmax-- === 0) throw "not bracketing";
}
let t = 0.5;
let do_iqi = false;
// jms: some guesses for default values of the eps_m and eps_a settings
// based on machine precision... not sure exactly what to do here
const eps = 1e-16;
if (!eps_m) eps_m = eps;
if (!eps_a) eps_a = 2 * eps;
let xm, fm;
do {
// use t to linearly interpolate between a and b,
// and evaluate this function as our newest estimate xt
const xt = a + t * (b - a),
ft = f(xt);
count++;
if (verbose)
log.push(
`${
do_iqi ? "IQI" : "BIS"
} t=${t} xt=${xt} ft=${ft} a=${a} b=${b} c=${c}`
),
points.push([xt, ft]);
// update our history of the last few points so that
// - a is the newest estimate (we're going to update it from xt)
// - c and b get the preceding two estimates
// - a and b maintain opposite signs for f(a) and f(b)
if (ft * fa >= 0) {
c = a;
fc = fa;
} else {
c = b;
b = a;
fc = fb;
fb = fa;
}
a = xt;
fa = ft;
// set xm so that f(xm) is the minimum magnitude of f(a) and f(b)
if (abs(fa) < abs(fb)) {
xm = a;
fm = fa;
} else {
xm = b;
fm = fb;
}
if (fm == 0) return { x: xm, log, fm, points, count };
// Figure out values xi and phi
// to determine which method we should use next
let tol = 2 * eps_m * abs(xm) + eps_a,
tlim = tol / abs(b - c);
if (tlim > 0.5) return { x: xm, log, fm, points, count };
let xi = (a - b) / (c - b),
phi = (fa - fb) / (fc - fb);
do_iqi = phi ** 2 < xi && (1 - phi) ** 2 < 1 - xi;
// inverse quadratic interpolation
if (do_iqi)
t =
((fa / (fb - fa)) * fc) / (fb - fc) +
(((((c - a) / (b - a)) * fa) / (fc - fa)) * fb) / (fc - fb);
// bisection
else t = 0.5;
// limit to the range (tlim, 1-tlim)
t = min(1 - tlim, max(tlim, t));
} while (Nmax-- > 0);
return { x: xm, log, fm, points, count };
}