import errorCalculation from './errorCalculation';
import step from './step';

/**
 * Curve fitting algorithm
 * @param {{x:Array<number>, y:Array<number>}} data - Array of points to fit in the format [x1, x2, ... ], [y1, y2, ... ]
 * @param {function} parameterizedFunction - The parameters and returns a function with the independent variable as a parameter
 * @param {object} [options] - Options object
 * @param {number} [options.damping] - Levenberg-Marquardt parameter
 * @param {number} [options.gradientDifference = 10e-2] - Adjustment for decrease the damping parameter
 * @param {Array<number>} [options.initialValues] - Array of initial parameter values
 * @param {number} [options.maxIterations = 100] - Maximum of allowed iterations
 * @param {number} [options.errorTolerance = 10e-3] - Minimum uncertainty allowed for each point
 * @return {{parameterValues: Array<number>, parameterError: number, iterations: number}}
 */
export default function levenbergMarquardt(
  data,
  parameterizedFunction,
  options = {}
) {
  let {
    maxIterations = 100,
    gradientDifference = 10e-2,
    damping = 0,
    errorTolerance = 10e-3,
    initialValues
  } = options;

  if (damping <= 0) {
    throw new Error('The damping option must be a positive number');
  } else if (!data.x || !data.y) {
    throw new Error('The data parameter must have x and y elements');
  } else if (
    !Array.isArray(data.x) ||
    data.x.length < 2 ||
    !Array.isArray(data.y) ||
    data.y.length < 2
  ) {
    throw new Error(
      'The data parameter elements must be an array with more than 2 points'
    );
  } else {
    let dataLen = data.x.length;
    if (dataLen !== data.y.length) {
      throw new Error('The data parameter elements must have the same size');
    }
  }

  var parameters =
    initialValues || new Array(parameterizedFunction.length).fill(1);

  if (!Array.isArray(parameters)) {
    throw new Error('initialValues must be an array');
  }

  var error = errorCalculation(data, parameters, parameterizedFunction);

  var converged = error <= errorTolerance;

  for (
    var iteration = 0;
    iteration < maxIterations && !converged;
    iteration++
  ) {
    parameters = step(
      data,
      parameters,
      damping,
      gradientDifference,
      parameterizedFunction
    );
    error = errorCalculation(data, parameters, parameterizedFunction);
    if (isNaN(error)) break;
    converged = error <= errorTolerance;
  }

  return {
    parameterValues: parameters,
    parameterError: error,
    iterations: iteration
  };
}

@@ -1108,16 +1108,44 @@
 ng = 0,%0A
+    maxValue,%0A    minValue,%0A
     erro
@@ -1639,58 +1639,25 @@
 lse 
-%7B%0A    let dataLen = data.x.length;%0A    if (dataLen
+if (data.x.length
  !==
@@ -1674,18 +1674,16 @@
 ngth) %7B%0A
-  
     thro
@@ -1754,14 +1754,8 @@
 ');%0A
-    %7D%0A
   %7D%0A
@@ -1773,20 +1773,16 @@
 meters =
-%0A   
  initial
@@ -1839,16 +1839,304 @@
 fill(1);
+%0A  let parLen = parameters.length;%0A  maxValue = maxValue %7C%7C new Array(parLen).fill(Number.MAX_SAFE_INTEGER);%0A  minValue = minValue %7C%7C new Array(parLen).fill(Number.MIN_SAFE_INTEGER);%0A%0A  if (maxValue.length !== minValue.length) %7B%0A    throw new Error('coutes should has the same size');%0A  %7D
 %0A%0A  if (
@@ -2570,16 +2570,146 @@
 %0A    );%0A
+%0A    for (let k = 0; k %3C parLen; k++) %7B%0A      parameters%5Bk%5D = Math.min(Math.max(minValue%5Bk%5D, parameters%5Bk%5D), maxValue%5Bk%5D);%0A    %7D%0A%0A
     erro

add boundaries option for parameters