function [alpha,gamma,edeltab,snr,core] = MLfit(infile,fitparameter,varargin)
 
% MLfit evaluate the parameters alpha (¦Á) and gamma (¦Ã) for the inverse power 
% background model using maximum likelihood background estimation. 
%
% Input:
%  infile         Input data, in form of input data matrix or input file name. The input data
%                   matrix is a 2xm matrix, in which the first row stands for the loss energy and 
%                   the second row are the recording background. The input file is a *.dm3 file. 
%                  
% fitparameter = [eprestart, eprewidth, signalstart, signalwidth]
% or fitparameter = [eprestart, eprewidth]
%
%  eprestart      starting energy for the background fitting window : eV
%  eprewidth    width for the backgound fitting window : eV
%  signalstart    starting energy for the signal window : eV. The signal windows is
%                       required to calculate the snr. Default: 467
%  signalwidth  width for the signal window : eV. Default: 12.
%
%  gamma0       intial guess of gamma0
%  min              termination tolerance, default: 10^(-14)
%
%
% Output:
%  alpha,gamma        parameters for the inverse power law background model. B=¦ÁE^(-¦Ã)
%  edeltab                  background fitting error at each energy channel
%  snr                         signal-to-noise ratio
%  core                       signals after removing background
%
% Example:
%  infile = 'S1-T2-2 EELS SI (dark ref crt) aligned sum.dm3' ;
%  eprestart = 454;
%  eprewidth = 10; 
%  fitparameter = [eprestart, eprewidth];
%  [alpha,gamma,snr] = MLfit(infile,fitparameter);
% 
% Ref: Unser et al, Optimal background estimation in EELS, J. Microsc.,
% 1987(145):245-256
% 
%  Note: DM3Import.m (by Robert A. McLeod) is required to input *.dm3 files. 
% 
% Zhu,Guozhen
% Oct 1st, 2011

% check inputs
msg = nargchk(1,4,nargin);
if ~isempty(msg)
    fprintf('Error! Invalid Input!')
    return
end
 
if isnumeric(infile)
    [m,n] = size(infile);
    if m==2
        if n<2||n==2
            fprintf('Input Error! Wrong Input EELS Signal!')
        end
    elseif m>2
        if n==2
            infile =infile';
        else
            fprintf('Input Error! Wrong Input EELS Signal!')
        end
    else
        fprintf('Input Error! Wrong Input EELS Signal!')
    end
    escale = infile(1,:);
    spectrum = infile(2,:); 
elseif isstr(infile)
    spectra_struct = DM3Import(infile);
    spectra =spectra_struct.spectra_data;  % unit:e-
    if size(spectra,2) > 1
        spectrum = spectra{1}.* spectra_struct.intensity.scale;
        edispersion = spectra_struct.xaxis{1}.scale;
        origin = spectra_struct.xaxis{1}.origin;
    else
        spectrum = spectra{1}.* spectra_struct.intensity.scale;
        edispersion = spectra_struct.xaxis.scale;
        origin = spectra_struct.xaxis.origin;
    end
    N = length( spectrum);
    escale = ( (0:N-1)-origin).* edispersion;   % unit:eV
else
    fprintf('Input Error! The first input should be either a 2xm or mx2 matrix or a *.dm3 file!')
end
 
gamma = 2;
min = 10^(-14);
eprewidth = [];
eprestart = [];
plot(escale,spectrum);
axisx = escale;   
spectrum_i = spectrum; 
signalstart = 467;        
signalwidth = 12;        
 
if isnumeric(fitparameter)
    [m,n] = size(fitparameter);
    if n == 1
        fitparameter = fitparameter';
    end
    [m,n] = size(fitparameter);
    if n== 2
        eprestart = fitparameter(1); 
        eprewidth = fitparameter(2);
    elseif n==4
        eprestart = fitparameter(1); 
        eprewidth = fitparameter(2);
        signalstart = fitparameter(3); 
        signalwidth = fitparameter(4);
    else
        fprintf('Input Error! No fitting parameter!')
    end
else
    fprintf('Input Error! No fitting parameter!')
end
        
if (nargin == 3)
    if isnumeric(varargin{nargin-1}) 
        gamma = varargin{nargin-1};
    else
        fprintf('Input Error! The third input is the initial value of gamma. ')
    end
elseif (nargin == 4)
    if isnumeric(varargin{nargin-1})&&isnumeric(varargin{nargin-2})
        min = varargin{nargin-1};
        gamma = varargin{nargin-2};
    else
        fprintf('Input Error! The third input is the initial value r and the fourth input is the termination tolerance.')
    end
elseif (nargin==1)
    temp_input = input('Please input the start pointing for fitting (unit: eV, e.g. 454):', 's');
    eprestart = str2num(temp_input);
    temp_input = input('Please input the wide of fitting window (unit: eV, e.g. 10):', 's');
    eprewidth = str2num(temp_input);
end
 
if ~isempty(eprestart)&&~isempty(eprewidth)
    efilter = ((escale >= eprestart) & (escale <=(eprestart + eprewidth)));
    escale = nonzeros(escale(efilter))';
    spectrum = nonzeros(spectrum(efilter))';
end

% maximum likelihood background estimation   
x = log(escale);
xw = sum(x.*spectrum)/sum(spectrum);
mr0 = sum(exp(-gamma.*x));
mr1 = sum(x.*exp(-gamma.*x));
fr = mr1/mr0-xw;
 
k = 0; 
while (abs(fr)>min)
    mr2 = sum(x.*x.*exp(-gamma.*x));
    dfdr = (mr1/mr0)^2-mr2/mr0;
    gamma = gamma-fr/dfdr;
    mr0 = sum(exp(-gamma.*x));
    mr1 = sum(x.*exp(-gamma.*x)); 
    fr = mr1/mr0-xw;
    k = k+1;     %  to avoid endless loop for bad experimental datasets. 
    if k >= 30
%         fprintf('%g\n', k);
        break;
    end
end
 
alpha = sum(spectrum)/mr0;  
 
back = alpha.*axisx.^(-gamma);    
hold on; plot(axisx, back,'k'); 

% error estimation
x = log(axisx);   
mlamda2 = sum(x(efilter).*x(efilter).*back(efilter))/sum(back(efilter));
mlamda1 = sum(x(efilter).*back(efilter))/sum(back(efilter));
b = back;
dbdr = -x.*back;
temp = back(efilter).*(x(efilter)-mlamda1).*(x(efilter)-mlamda1);
edeltab = sqrt((b.*b.*mlamda2+dbdr.*dbdr+2*b.*dbdr.*mlamda1)/sum(temp));

efilter2 = ((axisx >= signalstart) & (axisx <=(signalstart + signalwidth)));
mlamda2 = sum(x(efilter).*x(efilter).*back(efilter))/sum(back(efilter));
mlamda1 = sum(x(efilter).*back(efilter))/sum(back(efilter));
b = sum(back(efilter2));     
dbdr = sum(-x(efilter2).*back(efilter2)); 
temp = back(efilter2).*(x(efilter2)-mlamda1).*(x(efilter2)-mlamda1); 
edeltabsnr = (b*b*mlamda2+dbdr*dbdr+2*b*dbdr*mlamda1)/sum(temp);
 
t = sum(spectrum_i(efilter2));  
snr = (t-b)/sqrt(t+edeltabsnr);
 
core = spectrum_i - back;
