_PolyStretch
Description
_PolyStretch finds the stretch around a point on which a polynomial keeps rising (or keeps falling), i.e. the nearest turning points on each side. PolyInv uses it so that an inverse prediction stays on the same branch of the calibration curve.
Used by: PolyInv
Calls: _Bisect
Source Code
_PolyStretch = LAMBDA(
b, // polynomial coefficients as a row, b_0 to b_p
x0, // a point inside the stretch
xmin, // smallest x in the data
xmax, // largest x in the data
LET(
p, COLUMNS(b) - 1,
R, xmax - xmin,
// slope of the polynomial, elementwise in x (avoids 0^0, an error in Excel)
dydx, LAMBDA(x,
IF(
p = 1,
INDEX(b, 1, 2) + 0 * x,
MMULT(HSTACK(1 + 0 * x, x ^ SEQUENCE(1, p - 1)), TRANSPOSE(SEQUENCE(1, p) * DROP(b, , 1)))
)
),
// grid: linear across the data range +/- one range, then geometric
// from R/10 out to 1e6*R beyond each end; x0 itself is included
tail, R / 10 * 10 ^ (7 * SEQUENCE(500, 1, 0) / 499),
xs, SORT(VSTACK(xmin - R - tail, SEQUENCE(2000, 1, xmin - R, 3 * R / 1999), xmax + R + tail, x0)),
n, ROWS(xs),
idx, SEQUENCE(n),
i0, XMATCH(x0, xs),
s0, SIGN(INDEX(dydx(x0), 1, 1)),
bad, SIGN(dydx(xs)) <> s0,
// nearest grid points where the slope has the other sign
lo_i, MAX(FILTER(idx, bad * (idx < i0), 0)),
hi_i, MIN(FILTER(idx, bad * (idx > i0), n + 1)),
lo_end, IF(lo_i = 0, INDEX(xs, 1), _Bisect(dydx, INDEX(xs, lo_i + 1), INDEX(xs, lo_i))),
hi_end, IF(hi_i = n + 1, INDEX(xs, n), _Bisect(dydx, INDEX(xs, hi_i - 1), INDEX(xs, hi_i))),
HSTACK(lo_end, hi_end)
)
);