fminsearch = function(fun, Parm0, x, y, Opt) {
if (!Opt) { Opt = {} };
if (!Opt.maxIter) { Opt.maxIter = 1000 };
if (!Opt.step) {
Opt.step = Parm0.map(function(p) { return p / 100 });
Opt.step = Opt.step.map(function(si) { if (si == 0) { return 1 } else { return si } });
};
if (typeof (Opt.display) == 'undefined') { Opt.display = true };
if (!Opt.objFun) { Opt.objFun = function(y, yp) { return y.map(function(yi, i) { return Math.pow((yi - yp[i]), 2) }).reduce(function(a, b) { return a + b }) } }
if (!Opt.bounds) { Opt.bounds = Parm0.map(function() { return [-Infinity, Infinity] }) };
var cloneVector = function(V) { return V.map(function(v) { return v }) };
var ya, y0, yb, fP0, fP1;
var P0 = cloneVector(Parm0), P1 = cloneVector(Parm0);
var n = P0.length;
let step = cloneVector(Opt.step);
var funParm = function(P) { return Opt.objFun(y, fun(x, P)) }
var applyBounds = function(P, bounds) {
return P.map(function(p, i) {
return Math.max(bounds[i][0], Math.min(bounds[i][1], p));
});
}
for (var i = 0; i < Opt.maxIter; i++) {
for (var j = 0; j < n; j++) {
P1 = cloneVector(P0);
P1[j] += step[j];
P1 = applyBounds(P1, Opt.bounds);
if (funParm(P1) < funParm(P0)) {
step[j] = 1.2 * step[j];
P0 = cloneVector(P1);
}
else {
step[j] = -(0.5 * step[j]);
}
}
if (Opt.display) { if (i > (Opt.maxIter - 10)) { console.log(i + 1, funParm(P0), P0) } }
}
return P0
}