function [D alph xmin xmax P Alphx] = powerlaw_fit(X, m, Alph0)
%[D alph xmin xmax P] Alphx = powerlaw_fit(X, m, Alph0)
%
%   1) Evaluation of power laws
%
%       Inputs,
%       X,     input distribution
%       m,     size of system (xmax will be fixed as m)
%
%       Outputs,
%       D,     Kolmogorov-Smirnov statistic
%       alph,  exponent
%       xmin,  lower bound on distribution
%       xmax,  upper bound on distribution
%       P,     probability distribution for perfect power-law
%
%   2) Generation of Hurwitz-zeta (zeta(alpha,x)) tables
%
%       Inputs,
%       X,     filename to save table
%       m,     maximum upper bound of x
%       Alph0, range of exponents [alph_min:increment:alph_max]
%       [e.g. powerlaw_fit('zeta_table',5000,1.01:0.01:5);]
%
%   3) Loading of Hurwitz-zeta (zeta(alpha,x)) tables
%
%       Input,
%       X,     filename to load table
%

persistent Alph la lx Zeta_ax

%preparation of reference zeta tables
if ischar(X)
    if nargin==1;                                                   %load tables
        disp('Loading reference table')
        load(X, 'Zeta_ax','Alph','lx','la')

    else                                                            %make tables
        disp('Generating reference table')
        Alph=Alph0;                                                 %Alph0 allows to declare persistent Alph
        lx=m;
        la=length(Alph);

        Zeta_ax=zeros(la,lx);                                       %table of zeta(alph,xmin)
        for i=1:la
            xpa=[0 (1:lx-1).^(-Alph(i))];
            Zeta_ax(i,:) = zeta(Alph(i)) - cumsum(xpa);
        end

        disp('Saving reference table')         	%save tables
        save(X, 'Zeta_ax','Alph','lx','la')
    end
    return
end

if isempty(Alph)
    error('Load reference table: powerlaw_fit(table_filename)');
end

%for each xmin, the best alpha maximizes:
%-alph*sum(ln(X)) - n*ln(zeta(alph,xmin,m+1))
%where zeta(alph,xmin,m+1)=zeta(alph,xmin)-zeta(alph,m+1)

[F C lim]= histi(int32(X));                                         %distributions and their range
if lim(2)>m
    error('Distribution maximum exceeds system size')
end
F=[  zeros(1,lim(1)-1) F zeros(1,m-lim(2))];                        %ensure distributions range from 1 to m
C=[C(ones(1,lim(1)-1)) C zeros(1,m-lim(2))];
r=ceil(lim(2)/10);                                                  %maximum of xmin
xmax=m;                                                             %fix xmax as the system size

%get best alph for each xmin
CLn_g=fliplr(cumsum(log(m:-1:1).*F(end:-1:1)));                     %cumulative sum Ln(X) (X>=x, 1<=x<=m)
[ignore Ai] = max( -Alph(ones(1,r),:).'.*CLn_g(ones(1,la),1:r)      ...
    -C(ones(1,la),1:r).*log((Zeta_ax(:,1:r)-Zeta_ax(:,m(ones(1,r))+1))) );

%get best xmin
Zeta_n=Zeta_ax(Ai,1:m)-Zeta_ax(Ai,m(ones(1,m))+1);                  %zeta(alph,1:m,m+1) matrix
Zeta_d=diag(Zeta_ax(Ai,1:r))-Zeta_ax(Ai,m+1);                       %zeta(alph,xmin,m+1) vector for alph/xmin pairs
[D xmin]=min(max(triu(abs(                              ...         %KS statistic and corresponding best xmin
    C(ones(1,r),:)./C(ones(1,m),1:r).' -                ...         %observed cumulative distribution
    Zeta_n./Zeta_d(:,ones(1,m))                         ...         %zeta(alph,1:m,m+1)/zeta(alph,xmin,m+1)
    )),[],2));

alph=Alph(Ai(xmin));                                                %best alph for best xmin
Alphx=Alph(Ai);                                                     %best alph for all xmin
P=[Zeta_n(xmin,:)./Zeta_d(xmin) 0];                                 %CDF: P=zeta(alph,1:m,m+1)./zeta(alph,xmin,m+1)