function [beta,r,J] = nlinfit(X,y,model,beta0)
%NLINFIT Nonlinear least-squares data fitting by the Gauss-Newton method.
%   NLINFIT(X,Y,'MODEL',BETA0) finds the coefficients of the nonlinear 
%   function described in MODEL. MODEL is a user supplied function having 
%   the form y = f(beta,x). That is MODEL returns the predicted values of y
%   given initial parameter estimates, beta, and the independent variable, X.   
%   [BETA,R,J] = NLINFIT(X,Y,'MODEL',BETA0) returns the fitted coefficients
%   BETA the residuals, R, and the Jacobian, J, for use with NLINTOOL to
%   produce error estimates on predictions.

%   B.A. Jones 12-06-94.
%   Copyright (c) 1993-96 by The MathWorks, Inc.
%   $Revision: 2.4 $  $Date: 1996/02/21 18:34:00 $

n = length(y);
if min(size(y)) ~= 1
   error('Requires a vector second input argument.');
end
y = y(:);

p = length(beta0);
beta0 = beta0(:);

J = zeros(n,p);
beta = beta0;
betanew = beta + 1;
maxiter = 100;
iter = 0;
betatol = 1.0E-4;
rtol = 1.0E-4;
rmse = 1;
rmseold = rmse;

while (norm((betanew-beta)./(beta+sqrt(eps))) > betatol | abs(rmseold-rmse)/(rmse+sqrt(eps)) > rtol) & iter < maxiter
   if iter > 0, 
      beta = betanew;
   end

   iter = iter + 1;
   evalstr = [model,'(beta,X)'];
   yfit = eval(evalstr);
   r = y - yfit;
   rmseold = std(r);

   for k = 1:p,
      delta = zeros(size(beta));
      delta(k) = sqrt(eps)*beta(k);
      evalstr1 = [model,'(beta+delta,X)'];
      yplus = eval(evalstr1);
      J(:,k) = (yplus - yfit)/(sqrt(eps)*beta(k));
   end

   mj = mean(J);
   mj = mj(ones(n,1),:);
   J = J - mj;
   Jplus = [J;1.0E-2*eye(p)];
   rplus = [r;zeros(p,1)];

   % Levenberg-Marquardt type adjustment 
   % Gauss-Newton step -> J\r
   % LM step -> inv(J'*J+constant*eye(p))*J'*r
   step = Jplus\rplus;
   
   betanew = beta + step;
   evalstrnew = [model,'(betanew,X)'];
   yfitnew = eval(evalstrnew);
   rnew = y - yfitnew;
   rmse = std(rnew);
   nstep = norm(step);
   iter1(iter) = 0;
   if rmse > rmseold & iter1(iter) < 12
      step = step/sqrt(10);
     betanew = beta + step;
      evalstrnew = [model,'(betanew,X)'];
      yfitnew = eval(evalstrnew);
     rnew = y - yfitnew;
     rmse = std(rnew);
     iter1(iter) = iter1(iter) + 1;
   end
end
