classdef vfa_t1 < AbstractModel
% vfa_t1: Compute a T1 map using Variable Flip Angle
%
% Assumptions:
%
% Inputs:
%   VFAData         Spoiled Gradient echo data, 4D volume with different flip angles in time dimension
%   (B1map)         Normalized transmit excitation field map (B1+). B1+ is defined
%                   as a  normalized multiplicative factor such that:
%                   FA_actual = B1+ * FA_nominal. (OPTIONAL).
%   (Mask)          Binary mask to accelerate the fitting. (OPTIONAL)
%
% Outputs:
%   T1              Longitudinal relaxation time [s]
%   M0              Equilibrium magnetization
%
% Protocol:
%   VFAData Array [nbFA x 2]:
%       [FA1 TR1; FA2 TR2;...]      flip angle [degrees] TR [s]
%
% Options:
%   None
%
% Example of command line usage:
%   Model = vfa_t1;  % Create class from model
%   Model.Prot.VFAData.Mat=[3 0.015; 20 0.015]; %Protocol: 2 different FAs
%   data = struct;  % Create data structure
%   data.VFAData = load_nii_data('VFAData.nii.gz');
%   data.B1map = load_nii_data('B1map.nii.gz');
%   FitResults = FitData(data,Model); %fit data
%   FitResultsSave_mat(FitResults);
%
%   For more examples: qMRusage(vfa_t1)
%
%
% Author: Ian Gagnon, 2017
%
% References:
%   Please cite the following if you use this module:
%     Fram, E.K., Herfkens, R.J., Johnson, G.A., Glover, G.H., Karis, J.P.,
%     Shimakawa, A., Perkins, T.G., Pelc, N.J., 1987. Rapid calculation of
%     T1 using variable flip angle gradient refocused imaging. Magn. Reson.
%     Imaging 5, 201?208
%   In addition to citing the package:
%     Karakuzu A., Boudreau M., Duval T.,Boshkovski T., Leppert I.R., Cabana J.F.,
%     Gagnon I., Beliveau P., Pike G.B., Cohen-Adad J., Stikov N. (2020), qMRLab:
%     Quantitative MRI analysis, under one umbrella doi: 10.21105/joss.02343

properties (Hidden=true)
 onlineData_url = 'https://osf.io/7wcvh/download?version=3';
end

    properties
        MRIinputs = {'VFAData','B1map','Mask'};
        xnames = {'M0','T1'};
        voxelwise = 0;

        % Protocol
        Prot  = struct('VFAData',struct('Format',{{'FlipAngle' 'TR'}},...
                                         'Mat', [3 0.015; 20 0.015])); % You can define a default protocol here.

        % fitting options
        st           = [2000 0.7]; % starting point
        lb           = [0   0.00001]; % lower bound
        ub           = [6000   5]; % upper bound
        fx           = [0     0]; % fix parameters

        % Model options
        buttons = {};
        options= struct(); % structure filled by the buttons. Leave empty in the code

        % Simulation Options
        Sim_Single_Voxel_Curve_buttons = {'SNR',50};
        Sim_Optimize_Protocol_buttons = {'# of volumes',5,'Population size',100,'# of migrations',100};
    end

methods (Hidden=true)
% Hidden methods goes here.
end

    methods

        function obj = vfa_t1()
            obj.options = button2opts(obj.buttons);
        end

        function Smodel = equation(obj,x)
            % Generates a VFA signal based on input parameters
            x = mat2struct(x,obj.xnames); % if x is a structure, convert to vector

            % Equation: S=M0sin(a)*(1-E)/(1-E)cos(a); E=exp(-TR/T1)
            flipAngles = (obj.Prot.VFAData.Mat(:,1))';
            TR = obj.Prot.VFAData.Mat(1,2);
            E = exp(-TR/x.T1);
            Smodel = x.M0*sin(flipAngles/180*pi)*(1-E)./(1-E*cos(flipAngles/180*pi));
        end

       function FitResult = fit(obj,data)
            % T1 and M0
            flipAngles = (obj.Prot.VFAData.Mat(:,1))';
            TR = obj.Prot.VFAData.Mat(:,2);
            if obj.voxelwise == 0
                if (length(unique(TR))~=1), error('VFA data must have same TR'); end
                if ~isfield(data, 'B1map'), data.B1map = []; end
                if ~isfield(data, 'Mask'), data.Mask = []; end
                [FitResult.T1, FitResult.M0] = Compute_M0_T1_OnSPGR(double(data.VFAData), flipAngles, TR(1), data.B1map, data.Mask);
            elseif obj.voxelwise == 1
                if ~isfield(data,'B1map'), data.B1map=1; end
                [m0, t1] = mtv_compute_m0_t1(double(data.VFAData), flipAngles, TR(1), data.B1map);
                FitResult.T1 = t1;
                FitResult.M0 = m0;
            end
       end

       function plotModel(obj,x,data)
           if nargin<2 || isempty(x), x = obj.st; end
           x = mat2struct(x,obj.xnames);
           disp(x)
           flipAngles = obj.Prot.VFAData.Mat(:,1)';
           TR = obj.Prot.VFAData.Mat(1,2)';
           subplot(2,1,1)
           if exist('data','var')
               if isfield(data,'B1map')
                  if ~isempty(data.B1map)
                   B1map=data.B1map;
                  end
               else
                   B1map=1;
               end
                % Plot data and fitted signal
                plot(flipAngles,data.VFAData,'.','MarkerSize',16)
            else
                B1map=1;
            end
            E = exp(-TR/x.T1);
            Smodel = x.M0*sin(flipAngles/180*pi*B1map)*(1-E)./(1-E*cos(flipAngles/180*pi*B1map));
            hold on
            plot(flipAngles,Smodel,'x','MarkerSize',16)
            hold off
            title('Data points','FontSize',14);
            xlabel('Flip Angle [deg]','FontSize',12);
            ylabel('Signal','FontSize',12);
            legend('data', 'fitted','Location','best')
            set(gca,'FontSize',12)

            % Plot linear fit
            subplot(2,1,2)
            if exist('data','var')
                ydata = data.VFAData./sin(flipAngles/180*pi*B1map)';
                xdata = data.VFAData./tan(flipAngles/180*pi*B1map)';
                plot(xdata,ydata,'xb','MarkerSize',16)
                hold on
            end
            slope = exp(-TR/x.T1);
            intercept = x.M0*(1-slope);
            X=Smodel./tan(flipAngles/180*pi*B1map);
            mval = min(X);
            Mval = max(X);
            plot([mval Mval],intercept+slope*[mval Mval],'-r');
            hold off
            title(sprintf('Linear Fit: T1=%0.4f s; M0=%0.0f;',x.T1,x.M0),'FontSize',14);
            xlabel('[au]','FontSize',12);
            ylabel('[au]','FontSize',12);
            legend('linearized data', 'linear fit','Location','best')
            %txt=strcat('T1=',num2str(x.T1),'s M0=',num2str(x.M0));
            set(gca,'FontSize',12)

%             h = plot( fitresult, xData, yData,'+');
%             set(h,'MarkerSize',30)
%             legend( h, 'y vs. x', 'untitled fit 1', 'Location', 'NorthEast' );
%             p11 = predint(fitresult,x,0.95,'observation','off');
%             hold on
%             plot(x,p11,'m--'); drawnow;
%             hold off
%             % Label axes
%             xlabel( 'x' );
%             ylabel( 'y' );
%             grid on
%             saveas(gcf,['temp.jpg']);
       end
       function [FitResults, data] = Sim_Single_Voxel_Curve(obj, x, Opt,display)
           % Simulates Single Voxel
           %
           % :param x: [struct] fit parameters
           % :param Opt.SNR: [struct] signal to noise ratio to use
           % :param display: 1=display, 0=nodisplay
           % :returns: [struct] FitResults, data (noisy dataset)

           if ~exist('display','var'), display = 1; end
           Smodel = equation(obj, x);
           sigma = max(abs(Smodel))/Opt.SNR;
           data.VFAData = ricernd(Smodel,sigma)';
           data.B1map = 1;

           FitResults = fit(obj,data);
           if display
               plotModel(obj, FitResults, data);
           end
       end

       function SimVaryResults = Sim_Sensitivity_Analysis(obj, OptTable, Opt)
           % SimVaryGUI
           SimVaryResults = SimVary(obj, Opt.Nofrun, OptTable, Opt);
       end

       function SimRndResults = Sim_Multi_Voxel_Distribution(obj, RndParam, Opt)
           % SimVaryGUI
           SimRndResults = SimRnd(obj, RndParam, Opt);
       end

    end

    % CLI-only implemented static methods. Can be called directly from
    % class - no object needed.
    methods(Static)
        function Mz = analytical_solution(params)
            %ANALYTICAL_SOLUTION  Analytical equations for the longitudinal magnetization of
            %steady-state gradient echo experiments.
            %
            %   Reference: Stikov, N. , Boudreau, M. , Levesque, I. R.,
            %   Tardif, C. L., Barral, J. K. and Pike, G. B. (2015), On the
            %   accuracy of T1 mapping: Searching for common ground. Magn.
            %   Reson. Med., 73: 514-522. doi:10.1002/mrm.25135
            %
            %   params: Struct.
            %           Properties: T1, TR, EXC_FA, constant (optional)
            %

            Mz = vfa_equation(params);

        end

        function signMaxAngle = ernst_angle(params)
            %ERNST_ANGLE  Analytical equations for the longitudinal magnetization of
            %steady-state gradient echo experiments.
            %
            %   Reference: Ernst, R. R. (1966). "Application of Fourier
            %   transform spectroscopy to magnetic resonance". Review of
            %   Scientific Instruments. 37: 93. doi:10.1063/1.171996
            %
            %   params: Struct.
            %           Properties: T1, TR
            %

            try
                T1 = params.T1;
                TR = params.TR;
            catch
                error('vfa_t1.ernst_equation: Incorrect parameters.  Run `help vfa_t1.ernst_angle` for more info.')
            end

            signMaxAngle = acosd(exp(-TR./T1));

        end

        function [Mz, Msig] = bloch_sim(params)
            %BLOCH_SIM Bloch simulations of the GRE-IR pulse sequence.
            % Simulates 100 spins params. Nex repetitions of the IR pulse
            % sequences.
            %
            % params: Struct with the following fields:
            %   EXC_FA: Excitation pulse flip angle in degrees.
            %   TI: Inversion time (ms).
            %   TR: Repetition time (ms).
            %   TE: Echo time (ms).
            %   T1: Longitudinal relaxation time (ms).
            %   T2: Transverse relaxation time (ms).
            %   Nex: Number of excitations
            %
            %   (optional)
            %       df: Off-resonance frequency of spins relative to excitation pulse (in Hz)
            %       crushFlag: Numeric flag for perfect spoiling (1) or partial spoiling (2).
            %       partialDephasingFlag: do partialDephasing (see below)
            %       partialDephasing: Partial dephasing fraction (between [0, 1]). 1 = no dephasing, 0 = complete dephasing (sele
            %       inc: Phase spoiling increment in degrees.
            %
            % Outputs:
            %   Mz: Longitudinal magnetization at just prior to excitation pulse.
            %   Msig: Complex signal produced by the transverse magnetization at time TE after excitation.
            %

            %% Setup parameters
            %

            alpha = deg2rad(params.EXC_FA);
            TR = params.TR;
            T1 = params.T1;

            TE = params.TE;
            T2 = params.T2;

            Nex = params.Nex;

            %% Optional parameers

            if isfield(params, 'df')
                df = params.df;
            else
                df = 0;
            end

            if isfield(params, 'crushFlag')
                crushFlag = params.crushFlag;
            else
                crushFlag = 1;
            end

            if isfield(params, 'partialDephasingFlag')
                partialDephasingFlag = params.partialDephasingFlag;
            else
                partialDephasingFlag = 0;
            end

            if isfield(params, 'partialDephasing')
                partialDephasing = params.partialDephasing;
            else
                partialDephasing = 1;
            end

            if isfield(params, 'inc')
                inc = deg2rad(params.inc);
            else
                inc = 0;
            end

            %% Simulate for every flip angless
            %

            for ii = 1:length(alpha)

                [Msig(ii), Mz(ii)] = vfa_blochsim(                  ...
                    alpha(ii),            ...
                    T1,                   ...
                    T2,                   ...
                    TE,                   ...
                    TR,                   ...
                    crushFlag,            ...
                    partialDephasingFlag, ...
                    partialDephasing,     ...
                    df,                   ...
                    Nex,                  ...
                    inc                   ...
                    );

            end
        end

        function EXC_FA = find_two_optimal_flip_angles(params, sigFigs)
            %FIND_TWO_OPTIMAL_FLIP_ANGLES Calculate the two optimal flip
            %angles (having signals 71% of the signal at the Ernst angle).
            %
            % Simulates 100 spins params. Nex repetitions of the IR pulse
            % sequences.
            %
            % References:
            %
            % Deoni, S. C., Rutt, B. K. and Peters, T. M. (2003), Rapid
            % combined T1 and T2 mapping using gradient recalled
            % acquisition in the steady state. Magn. Reson. Med., 49:
            % 515-526.
            %
            % Schabel, M.C. & Morrell, G.R., 2009. Uncertainty in T(1)
            % mapping using the variable flip angle method with two flip
            % angles. Physics in medicine and biology, 54(1), pp.N1?8.

            if ~exist('sigFigs','var')
                sigFigs = 0;
            end

            % Set up search space of flip angle and signal values
            flipAngleSearchSpace = 0:1*10^(-sigFigs):90;

            params.EXC_FA = flipAngleSearchSpace;
            signals = vfa_t1.analytical_solution(params);

            % Get the Angle, find index in search space
            maxAngle = vfa_t1.ernst_angle(params);
            [~,maxRangeIndex] = min(abs(flipAngleSearchSpace-maxAngle));

            % Calculate signal at optimal flip angle values
            optAngleSignal= 0.71*signals(maxRangeIndex);

            % Calculate indices of optimal flip angles (smaller and larger
            % than the Ernst angle)
            [~,optRangeIndex_small] = min(abs(signals(1:maxRangeIndex)-optAngleSignal));
            [~,optRangeIndex_large_rel] = min(abs(signals(maxRangeIndex+1:end)-optAngleSignal));
            optRangeIndex_large = maxRangeIndex+optRangeIndex_large_rel;

            % Output optimal flip angle values
            EXC_FA = [flipAngleSearchSpace(optRangeIndex_small), flipAngleSearchSpace(optRangeIndex_large)];

        end
    end
end


%==============================================================================
%
% function [yc,His] = lBFGS(fctn,yc,varargin)
%
% limited BFGS optimizer
%
% Input:
% ------
%   fctn           function handle
%   yc          starting guess
%   varargin    optional parameter, see below
%
% Output:
% -------
%   yc          numerical optimizer (current iterate)
%   His         iteration history
%==============================================================================

function [yc,His] = lBFGS(fctn,yc,varargin)

if nargin ==0, % help and minimal example
  help(mfilename);
  E9_MRIhead_MLIRlBFGS_NGF_mbElas;
  yc = 'endOfMinimalExample';
  His = [];
  return;
end;

% parameter initialization -----------------------------------------------
maxIter      = 10;              % maximum number of iterations
tolJ         = 1e-3;            % for stopping, objective function
tolY         = 1e-2;            %   - " -     , current value
tolG         = 1e-2;            %   - " -     , norm of gradient
solver       = regularizer;
maxLBFGS     = 5;              % maximum number of BFGS vectors
LSmaxIter    = 10;              % maximum number of line search iterations
LSreduction  = 1e-4;            % minimal reduction in line search
vecNorm      = @norm;           % norm to be used for dJ and dy
yStop        = [];              % used for stopping in multi-level framework
Jstop        = [];              %
Plots        = @(iter,para) []; % for plots;

for k=1:2:length(varargin),     % overwrites default parameter
  eval([varargin{k},'=varargin{',int2str(k+1),'};']);
end;
objFctn = fctn([]);

if isempty(yStop), yStop  = yc; end; % yStop used for stopping only
% -- end parameter set-up   ----------------------------------------------

% some output
FAIRmessage([mfilename '(JM 2009/01/31)']);
fprintf('[ maxIter=%d / maxLBFGS=%d / tolJ=%s / tolY=%s / tolG=%s / length(yc)=%d ]\n',...
  maxIter,maxLBFGS,num2str(tolJ),num2str(tolY),num2str(tolG),length(yc));

% -- initialize  ---------------------------------------------------------
STOP = zeros(5,1);

zBFGS = [];     % memory for BFGS gradient directions
sBFGS = [];     % memory for BFGS directions
nBFGS  = 0;     % counter for number of limited BFGS directions

if isempty(Jstop),
  % evaluate objective function for stopping values and plots
  [Jstop,para] = fctn(yStop); Jstop = abs(Jstop) + (Jstop == 0);
  Plots('stop',para);
end;

% evaluate objective function for starting values and plots
[Jc,para,dJ,H0] = fctn(yc);
Plots('start',para);
iter = 0; yOld = 0*yc; Jold = Jc; y0 = yc;

hisStr    = {'iter','J','Jold-J','|\nabla J|','|dy|','LS'};
his        = zeros(maxIter+2,6);
his(1,1:3) = [-1,Jstop,Jstop-Jc];
his(2,:)   = [0,Jc,Jstop-Jc,vecNorm(dJ),vecNorm(yc-yStop),0];

% some output
fprintf('%4s %-12s %-12s %-12s %-12s %4s\n%s\n',...
  hisStr{:},char(ones(1,64)*'-'));
dispHis = @(var) ...
  fprintf('%4d %-12.4e %-12.3e %-12.3e %-12.3e %4d\n',var);
dispHis(his(1,:));
% -- end initialization   ------------------------------------------------

% ==============================================================================
% MAIN LOOP
% ==============================================================================
while 1,
  % check stopping rules
  STOP(1) = abs(Jold-Jc)  <= tolJ*(1+abs(Jstop));
  STOP(2) = (iter>0) && (norm(yc-yOld) <= tolY*(1+norm(y0)));
  STOP(3) = norm(dJ)      <= tolG*(1+abs(Jstop));
  STOP(4) = norm(dJ)      <= 1e3*eps;
  STOP(5) = (iter >= maxIter);
  if all(STOP(1:3)) || any(STOP(4:5)), break;  end;

  iter      = iter + 1;

  % update at most maxLBFGS BFGS directions
  if iter > 1,
    zz = (dJ - dJold)';
    ss =  yc - yOld;
    if zz'*ss > 0,
      start = 2-(nBFGS<maxLBFGS);
      zBFGS = [zBFGS(:,start:end),zz];
      sBFGS = [sBFGS(:,start:end),ss];
      nBFGS = min(maxLBFGS,nBFGS+1);
    else
      warning('   y''*s < 0, skip update');
    end;
  end;

  % compute search direction using recursive and limited BFGS
  dy = bfgsrec(solver,nBFGS,sBFGS,zBFGS,H0,-dJ');
  if iter == 1,
    if strcmp(objFctn,'NPIR'),
      m     = para.m;
      omega = para.omega;
      dim   = length(para.omega)/2;
      maxdy = norm(sqrt(sum(reshape(center(dy,m),[],dim).^2,2)),'inf');
      fac   = 0.5*max((omega(2:2:end)-omega(1:2:end)));
      if maxdy>fac,
        dy = (fac/maxdy)*dy;
        %H0 = fac*H0;%speye(length(dy),length(dy));
      end;
    else
      H0 = norm(dJ) * speye(length(dy),length(dy));
    end;
  end;
  % perform Armijo line-search
  [t,yt,LSiter] = Armijo(fctn,yc,dy,Jc,dJ,...
        'LSmaxIter',LSmaxIter,'LSreduction',LSreduction);
  if (t == 0),
    break;
  end; % break if line-search fails

  % update variables
  yOld = yc; Jold = Jc; dJold = dJ; yc = yt;
  [Jc,para,dJ] = fctn(yc);  % evaluate objective function

  % some output
  his(iter+2,:) = [iter,Jc,Jold-Jc,vecNorm(dJ),vecNorm(yc-yOld),LSiter];
  dispHis(his(iter+1,:));
  para.normdY = vecNorm(yc - yOld);
  Plots(iter,para);

end;%while; % end of iteration loop
% ==============================================================================

% clean up
His.str = hisStr;
His.his = his(1:iter+2,:);
fprintf('STOPPING:\n');
fprintf('%d[ %-10s=%16.8e <= %-25s=%16.8e]\n',STOP(1),...
  '(Jold-Jc)',(Jold-Jc),'tolJ*(1+|Jstop|)',tolJ*(1+abs(Jstop)));
fprintf('%d[ %-10s=%16.8e <= %-25s=%16.8e]\n',STOP(2),...
  '|yc-yOld|',norm(yc-yOld),'tolY*(1+norm(yc)) ',tolY*(1+norm(yc)));
fprintf('%d[ %-10s=%16.8e <= %-25s=%16.8e]\n',STOP(3),...
  '|dJ|',norm(dJ),'tolG*(1+abs(Jstop)',tolG*(1+abs(Jstop)));
fprintf('%d[ %-10s=%16.8e <= %-25s=%16.8e]\n',STOP(4),...
  'norm(dJ)',norm(dJ),'eps',1e3*eps);
fprintf('%d[ %-10s=  %-14d >= %-25s=  %-14d]\n',STOP(5),...
  'iter',iter,'maxIter',maxIter);

FAIRmessage([mfilename,' : done !']);

%------------------------------------------------------------------------------

function[d] = bfgsrec(solver,n,S,Z,H,d)

maxIterCG = 10; tolCG = 1e-2;
if isempty(solver)
  if isstruct(H),
    if isfield(H,'solver'),
      solver = H.solver;
    elseif isfield(H,'d2S') && isfield(H.d2S,'solver'),
      solver = H.d2S.solver;
    else
      error('solver has not been defined')
    end;

  end
end

if n == 0,
  switch solver,
    case {'MG-elastic'}
        d = MGsolver(d,H);
    case {'pcg'}
      L   = tril(H); % Symmetric Gauss Seidel Preconditioning,
      D   = diag(H); % L is lower, D is diagonal, U = L'
      SGS = @(x) L\(D.*(L'\x));
      d   = pcg(H,d,tolCG,maxIterCG,SGS);
    case 'PCG-curvature',
      M     = @(x) H.d2D.M;
      Afctn = @(x) M(x) + H.d2S.d2S(x,H.omega,H.m);
      % preconditioner
      Ddiag = H.d2D.M;
      D = Ddiag  +  H.d2S.diag(H.omega,H.m);
      PC = @(x) D.\x; % Jacobi preconditioner
      [d,flag,relres,iter] = pcg(Afctn,d,tolCG,maxIterCG,PC);

    otherwise,
      if isnumeric(H),
        % if H is a matrix, solve the linear system using MATLAB's backslash
        d = H\d;
      else
        error('nyi - solver %s', solver)
      end;
  end;
else
  alpha = (S(:,n)'*d)/(Z(:,n)'*S(:,n));
  d     = d - alpha*Z(:,n);
  d     = bfgsrec(solver,n-1,S,Z,H,d);
  d     = d + (alpha - (Z(:,n)'*d)/(Z(:,n)'*S(:,n)))*S(:,n);
end;
%==============================================================================


function F_enhance= RSWHEg_gray( F )
% Bi Bi histogram equalization with recursively seperated weighted histgram
% equalization with mean and mean
% clear all;
% close all;
% clc;
%close figure;
% x0=imread('pout.tif');
%x0=imread('eswar.jpg');
% x0=imread('54.tif');
% x0=imread('source24_1.tif');
% figure;
% imshow(x0);
% x0=imread('untitled.bmp');
 %x0=imread('plane.bmp');
%x0=uint8(x0);
%
%  %x0=imread('12.bmp');
% %u=double(i);
% h = [0.0086,0.0856,0.0086;
%      0.0856,0.8561,0.0856;
%      0.0086,0.0856,0.0086];
% h = h/sum1(h(:));  % Normalize the filter
% uSmooth = conv2padded(double(x0),double(h));
%figure(1);
%imshow(uint8(uSmooth));
 %luma=uint8(uSmooth(:,:,1));
%  XM=0;
% y0=rgb2ycbcr(x0);
luma=F;%(y0(:,:,1));
%luma=wiener2(luma);
%luma=luma-60;
[m,n]=size(luma);
%luma=luma;
% cb=y0(:,:,2);
% cr=y0(:,:,3);
k=mean(mean(luma));
XM=k;
XG=round(255/2);
[pixelCounts bins] = imhist(luma, 256);
[m12,xmax]=max(imhist(luma,256));
[m21,xmin]=min(imhist(luma,256));
Pmax=(max(imhist(luma,256))/(m*n));
Pmin=(min(imhist(luma,256))/(m*n));
beta=Pmax*((abs(XM-XG))/(xmax-xmin));
r=round(k);
l=length(luma(luma<=r));
         listindex=find(luma<=r);
 k1=reshape(luma(listindex),l,1);%obtained below fist mean values put it into an arry
 k2=mean(k1);%find mean of mean in first half
 k3=round(k2);%round to nearest integer to find pdf and cdf till first quarter
  sum1=0;
 l1=length(luma(luma<=k3));%find all  values in luma falls below fist mean
 listindex1=find(luma<=k3);%all  values in luma falls below fist mean given in their locations
 k4=reshape(luma(listindex1),l1,1);%arrange all fist mean values in a vector
     xpdf=hist(k4,[0:k3]);%pdf from 0:r
     xpdf=xpdf/(m*n);
     %alpa0=max(cumsum(xpdf/(m*n)));%from line 39 to 48 new rswhe code add this to other quarters
     alpa=(cumsum(xpdf));
     alpa0=alpa(end);
      %xpdf=xpdf/l1;%normalized pdf to get nk/n,l=sum1 of xpdf,total no of pixels form 0 to r.
      sum1=0;
      for l2=0:k3

          pw(l2+1)=(Pmax*((xpdf(l2+1)-Pmin)/(Pmax-Pmin))).^alpa0+beta;
          sum1=sum1+pw(l2+1);
      end
       for l2=0:k3
          pwn(l2+1)=pw(l2+1)/(sum1);
      end
      %plot(pwn);
      %(imhist(luma));
      %xlabel('gray levels up to mean');
      %ylabel('pdf up to mean');
      %title('histogram for half an image up to mean');
      %sk=xpdf*triu(ones(k3+1));
      sk=pwn*triu(ones(k3+1));
%       figure(2);
%       plot(sk);
%       xlabel('gray levels upto 1st mean');
%       ylabel('cdf upto mean');
%       title('cdf for half  of an image up to ist mean');
     % alpha=0.7;
      for l2=0:k3
          if(xpdf(l2+1)>0)
              list1=find(k4==l2);%find value in an vector i.e converted from matrix
              %list(list1)=alpha*sk(l2+1)*(k3+1)+(1-alpha)*(k3+1);
              list(list1)=(sk(l2+1)*(k3+1));%map dont disturb to get bhe as
              %list(list1)=alpha*sk(l2+1)*(k3+1)+(1-alpha)*(k3+1);
              %it is 13/3/2011
              ert(l2+1)=(sk(l2+1)*(k3+1));
          end
      end
          p=zeros(m,n);
      p(listindex1)= list; %listindex1 mxn locations list transformed values.
%      figure(3);
%      imshow(p);
%      xlabel('gray levels up to first mean');
%       ylabel('luma component equilized image up to first mean');
%      title('processed luma image up to first mean');
     k=mean(mean(luma));
     %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%2nd quarter
r=round(k);
l=length(luma(luma<=r));
        listindex=find(luma<=r);
 k1=reshape(luma(listindex),l,1);%obtained below fist mean values put it into an arry
 k2=mean(k1);%find mean of mean in first half
 k3=round(k2);%round to nearest integer to find pdf and cdf till first quarter
%   sum1=0;
   b=k3;
%  l2=length(luma(luma>k3));%find all  values in luma falls below fist mean
  listindex2=find((luma>k3)&(luma<=r));%all  values in luma falls below fist mean given in their locations
  l2=length(listindex2);
  k5=reshape(luma(listindex2),l2,1);%arrange all fist mean values in a vector
      x2pdf=hist(k5,[k3+1:r]);%pdf from 0:r
       x2pdf=x2pdf/(m*n);%normalized pdf to get nk/n,l=sum1 of xpdf,total no of pixels form 0 to r.
       %alpa1=max(cumsum(x2pdf/(m*n)));%from line 39 to 48 new rswhe code add this to other quarters
       alpa=(cumsum(x2pdf));%/(m*n)))
       alpa1=alpa(end);
      %x2pdf=x2pdf/l2;%normalized pdf to get nk/n,l=sum1 of xpdf,total no of pixels form 0 to r.
      sum1=0;
      for l2k=0:r-(k3+1)
          pw1(l2k+1)=(Pmax*((x2pdf(l2k+1)-Pmin)/(Pmax-Pmin))).^alpa1+beta;
          sum1=sum1+pw1(l2k+1);
      end
       for l2k=0:r-(k3+1)
          pw1n(l2k+1)=pw1(l2k+1)/(sum1);
      end
%        figure(4);
%        plot(x2pdf);
%        xlabel('gray levels 2nd mean');
%        ylabel('pdf of 2nd mean');
%        title('histogram for 2nd mean');
       sk2=pw1n*triu(ones(r-k3));
%        figure(5);
%        plot(sk2);
%        xlabel('gray levels of 2nd mean');
%        ylabel('cdf upto mean');
%        title('cdf for half  of an image of 2nd mean');
       k2u=1;
       for l3=k3+1:r
           if(pw1n(k2u)>0)
               list2=find(k5==l3);%find value in an vector i.e converted from matrix
               %list1(list2)=alpha*(sk2(k2u)*(r-k3)+(k3+1))+(1-alpha)*(k3+1);
               list1(list2)=((k3+1)+(sk2(k2u))*(r-k3-1));%map dont disturb to
               %list1(list2)=alpha*(sk2(k2u)*(r-k3)+(k3+1))+(1-alpha)*(k3+1);
               %get BHE 13/3/2011
               ert(l3)=((k3+1)+(sk2(k2u))*(r-k3-1));
           end
               k2u=k2u+1;

       end

       p(listindex2)= list1;
%       figure(6);
%       imshow(p);
%      xlabel('gray levels up to first 2nd mean');
%       ylabel('luma component equilized image 2nd mean');
%      title('processed luma image up to 2nd mean');
 %lupper30=length(luma(luma>r);
%  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 lupper30=length(luma(luma>r));
%for i=0:r
    %if(luma(luma<=r))
        listindexupper3=find(luma>r);
   % end
%end
 k1upper=reshape(luma(listindexupper3),lupper30,1);
 mean3=mean(k1upper);
  r3=round(mean3);
 %length30=length((luma<=r3)&(luma>r));
  listindexupper30=find((luma>r)&(luma<=r3));
  length30=length(listindexupper30);
  k30upper=reshape(luma(listindexupper30),length30,1);

  %length30=length((luma<=r3)&(luma>r));
%  sum1=0;
alpa=0;
      xpdfupper30=hist(k30upper,[r+1:r3]);%pdf from r+1:r3
      xpdfupper30=xpdfupper30/(m*n);%normalized pdf to get nk/n,l=sum1 of xpdf,total no of pixels form r+1 to 255.
%alpa2=max(cumsum(xpdfupper30/(m*n)));%from line 39 to 48 new rswhe code add this to other quarters
alpa=(cumsum(xpdfupper30));%/(m*n)))
alpa2=alpa(end);
      %x3pdf=xpdfupper30/length30;%normalized pdf to get nk/n,l=sum1 of xpdf,total no of pixels form 0 to r.
sum1=0;
      for l2k=0:r3-(r+1)
          %pw2(l2k+1)=(Pmax*((x3pdf(l2k+1)-Pmin)/(Pmax-Pmin))).^alpa2+beta;
          pw2(l2k+1)=(Pmax*((xpdfupper30(l2k+1)-Pmin)/(Pmax-Pmin))).^alpa2+beta;
          sum1=sum1+pw2(l2k+1);
      end
       for l2k=0:r3-(r+1)
          pw2n(l2k+1)=pw2(l2k+1)/(sum1);
      end
% figure(7);
%      plot(pw2n);
%      xlabel('gray levels   3rd mean');
%      ylabel('pdf of 3rd mean');
%      title('histogram for upper half an image  3rd mean');
      skupper30=pw2n*triu(ones(r3-r));
%       figure(8);
%       plot(skupper30);
%       xlabel('gray levels after mean');
%       ylabel('cdf after mean');
%       title('cdf for upper half  of an image after mean');
      k3u=1;
      for k3upper=(r+1):r3
          if(pw2n(k3u)>0)
              list1upper30=find(k30upper==k3upper);%find value in an vector i.e converted from matrix
              listnew(list1upper30)=((r+1)+skupper30(k3u)*(r3-r-1));%map dont
              %listnew(list1upper30)=alpha*(skupper30(k3u)*(r3-r)+(r+1))+(1-alpha)*(r+1);
              %disturg to get original heq 14/3/2011
              %listnew(list1upper30)=alpha*(skupper30(k3u)*(r3-r)+(r+1))+(1-alpha)*(r+1);
              ert(k3upper)=((r+1)+skupper30(k3u)*(r3-r-1));
          end
         k3u=k3u+1;

     end

%           p2=zeros(m,n);

     p(listindexupper30)= listnew;
%     figure(9);
%      imshow(p);
%      %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     listindexupper40=find(luma>r3);
     lupper40=length(listindexupper40);
   % end
%end
 k1upper40=reshape(luma(listindexupper40),lupper40,1);
 %sum1=0;
 %for i=0:r
 alpa=0;
     xpdfupper40=hist(k1upper40,[r3+1:255]);%pdf from r+1:255
     xpdfupper40=xpdfupper40/(m*n);%normalized pdf to get nk/n,l=sum1 of xpdf,total no of pixels form r+1 to 255.
alpa=(cumsum(xpdfupper40));%/(m*n)));%from line 39 to 48 new rswhe code add this to other quarters
alpa3=alpa(end);
     % x4pdf=xpdfupper40/lupper40;%normalized pdf to get nk/n,l=sum1 of xpdf,total no of pixels form 0 to r.
sum1=0;
      for l2k=0:(255-(r3+1))
          pw3(l2k+1)=(Pmax*((xpdfupper40(l2k+1)-Pmin)/(Pmax-Pmin))).^alpa3+beta;
          sum1=sum1+pw3(l2k+1);
      end
       for l2k=0:(255-(r3+1))
          pw3n(l2k+1)=pw3(l2k+1)/(sum1);
      end
% figure(10);
%      plot(xpdfupper40);
%      xlabel('gray levels after 4mean');
%      ylabel('pdf after 4mean');
%      title('histogram for upper half an image after 4mean');
     skupper40=pw3n*triu(ones(255-r3));
%      figure(11);
%      plot(skupper40);
%      xlabel('gray levels after 4mean');
%      ylabel('cdf after 4mean');
%      title('cdf for upper half  of an image after 4mean');
     k4u=1;
     for k4upper=(r3+1):255
         if(pw3n(k4u)>0)
             list1upper40=find(k1upper40==k4upper);%find value in an vector i.e converted from matrix
             %for k2u=1:58
             %listnew4(list1upper40)=alpha*(198+skupper40(k4u))*(255-r3)+(1-alpha)*(r3+1);
             listnew4(list1upper40)=(r3+1)+skupper40(k4u)*(255-r3-1);%map
             %listnew4(list1upper40)=alpha*((r3+1+skupper40(k4u))*(255-r3))+(1-alpha)*(r3+1);
                    % listnew4(list1upper40)=alpha*(198+skupper40(k4u)*(255-r3))+(1-alpha)*(r3+1);
             %dont disturb to get original bbhe 14/3/2011
         %end
         ert(k4upper)=(r3+1)+skupper40(k4u)*(255-r3-1);
         end
         k4u=k4u+1;
     end

%      for i=0:l-1
          %p3=zeros(m,n);
%          if (p(listindex))
%              p(:)=list;
%          end
%      end

     p(listindexupper40)= listnew4;
%     figure(12);
%     imshow(p);
%     xlabel('gray levels after 4mean');
%      ylabel('luma component equilized image after 4mean');
%      title('processed luma image after 4mean');
     ommmmm=p;
     F_enhance=ommmmm;
     end
     %ommmmm=p1+p;
%     figure(13);
%     imshow(ommmmm);
%      colormap('gray');
%       xlabel('gray level');
%      ylabel('combined lower and upper half luma component equilized image');
%      title('combined luma image');
% %     figure(8);
%     image(om);
% %     for j1=0:255
% %         count=0;
% %         for i1=0:m*n-1
% %             if om(i1+1)==j1
% %                 count=count+1;
% %             end
% %         end
% %             prob(j1+1)=count/m*n;
% %     end
% %     figure(16);
% %     plot(prob);
% %
% %    for j2=0:255
% %         count1=0;
% %         for i2=0:m*n-1
% %             if luma(i2+1)==j2
% %                 count1=count1+1;
% %             end
% %         end
% %             prob2(j2+1)=count1/m*n;
% %     end
% %     figure(17);
% %     plot(prob2);
% %     xlabel('gray levels after mean');
% %      ylabel('luma component equilized image after mean');
% %      title('processed luma image after mean');
% %     ommmmm=p1+p;
% %     figure(7);
% %      colormap('gray');
% %       xlabel('gray level');
% %      ylabel('combined lower and upper half luma component equilized image');
% %      title('combined luma image');
% %     image(ommmmm);
%k=imshow(ommmmm);
%      cat1=cat(3,ommmmm,cb,cr);
% %     figure(14);
% %     imshow(cat1);
% % %     xlabel('gray level(ycbcr)');
% %      ylabel('combined lower and upper half luma,cromablue,croma red component equilized image');
% %      title('luma croma b and r color processed image');
%     catconversion=ycbcr2rgb(uint8(cat1));
%     figure(15);
%     imshow(uint8(ommmmm));
%     xlabel('gray level(rgb)');
%      ylabel('combined lower and upper half  RGB component equilized image');
%      title('converted from ycbcr2rgb color(RGB) processed image');
% YM=0;
%      YM=mean(mean(ommmmm));
%      AMBE=abs(XM-YM);
%      disp('Absolute mean brightness error=');
%      disp(AMBE);
     %MSE = meanSquareError(luma, ommmmm);
    % figure(16);
%disp('mean Square Error = ');
%disp(MSE);
%PSNR1 = PSNR(luma, ommmmm);
%figure(17);
% disp('Peak Signal to Noise Ratio = ');
% disp(PSNR1);
% E=entropy(uint8(ommmmm));
% disp('Entropy=');
% disp(E);
%      figure(16);
%      (imhist(uint8(ommmmm)));


 function X = nufft(x, st)
%function X = nufft(x, st)
%|
%| Compute d-dimensional NUFFT of signal/image x
%|
%| in
%|	x	[N1 N2 ... Nd (L)]	L input image(s) of size
%|						N1 x N2 x ... x Nd
%|	st	structure		precomputed by nufft_init()
%| out
%|	X	[M (L)]			output spectra
%|
%| Copyright 2003-5-30, Jeff Fessler, University of Michigan

% if no arguments, then run some self tests
if (nargin < 1) || (nargin == 1 && streq(x, 'test'))
	help([mfilename '.m'])

	printm('Starting nufft() self test; after a wait, shows small errors')
	if 0
		Jd = [5 4 4];
		Nd = [23 15 19];
		alpha_user = {1, 1, 1}; % default alpha, beta
		beta_user = {0.5, 0.5, 0.5};
	else
		Jd = [5 6];
		Nd = [60 75];
		alpha_user = {1, 1};
		beta_user = {0.5, 0.5};
	end
	Kd = 2 * Nd;
	gam = 2*pi ./ Kd;
	n_shift = zeros(size(Nd));

	if 1
		oldarg = {Nd(1), Nd(2), Jd(1), Jd(2), Kd(1), Kd(2)};
		printm('err alf1 %g best %g', ...
			max(col(nufft2_err_mm('all', oldarg{:}, 1))), ...
			max(col(nufft2_err_mm('all', oldarg{:}, 'best'))) )
	end
	rng(0)
	x = randn(Nd);
%	x = [[1:Nd(1)]'*ones(1,3), ones(Nd(1), Nd(2)-3)]; % test signal
%	x = zeros(N1, N2); x(1,1) = 1;
	if 0 % test with uniform frequency locations
		o1 = 2 * pi * [0:(N1-1)]' / N1;
		o2 = 2 * pi * [0:(N2-1)]' / N2;
		[o1, o2] = ndgrid(o1, o2);
		Xf = fft2(x);
	else
		if length(Nd) == 3 % nonuniform frequencies
			[o1, o2, o3] = ndgrid(linspace(0,gam(1),11), ...
				linspace(0,gam(2),13), linspace(0,gam(3),5));
			om = [	[o1(:); [0 7.2 2.6 3.3]'], ...
				[o2(:); [0 4.2 -1. 5.5]'], ...
				[o3(:); [0 1.1 -2. 3.4]'] ];
		else
			[o1, o2] = ndgrid(linspace(-3*gam(1),3*gam(1),41), ...
				linspace(-2*gam(2),gam(2),31));
			om = [	[o1(:); [0 7.2 2.6 3.3]'], ...
				[o2(:); [0 4.2 -1. 5.5]'] ];
		end
		Y.d = dtft(x, om, 'n_shift', n_shift);
	end

	s.tab = nufft_init(om, Nd, Jd, Kd, n_shift, 'table', 2^12, 'minmax:kb');
	Y.tab = nufft(x, s.tab);
	printm('table0		max%%diff = %g', max_percent_diff(Y.d, Y.tab))

	s.mmkb = nufft_init(om, Nd, Jd, Kd, n_shift, 'minmax:kb');
	Y.mmkb = nufft(x, s.mmkb);
	printm('minmax:kb	max%%diff = %g', max_percent_diff(Y.d, Y.mmkb))

	if 0 % test multiple input case
		xx = x; xx(:,:,:,2) = x; xx(:,:,:,3) = x;
		Y.tmp = nufft(xx, s.mmkb);
		Y.tmp = Y.tmp(:,1);
		printm('multi	max%%diff = %g', max_percent_diff(Y.mmkb, Y.tmp))
	return
	end

	% kaiser with minmax best alpha,m
	s.kb = nufft_init(om, Nd, Jd, Kd, n_shift, 'kaiser');
	Y.kb = nufft(x, s.kb);
	printm('kaiser		max%%diff = %g', max_percent_diff(Y.d, Y.kb))

	% kaiser with user-specified supoptimal alpha,m for comparison
	s.ku = nufft_init(om, Nd, Jd, Kd, n_shift, 'kaiser', ...
		s.kb.kb_alf + 0.1*ones(size(s.kb.kb_alf)), s.kb.kb_m);
	Y.ku = nufft(x, s.ku);
	printm('kaiser-user	max%%diff = %g', max_percent_diff(Y.d, Y.ku))

	s.mmtu = nufft_init(om, Nd, Jd, Kd, n_shift, 'minmax:tuned');
	Y.mmtu = nufft(x, s.mmtu);
	printm('minmax:tuned	max%%diff = %g', max_percent_diff(Y.d, Y.mmtu))

	s.mm = nufft_init(om, Nd, Jd, Kd, n_shift, 'minmax:user', ...
		alpha_user, beta_user);
	Y.mm = nufft(x, s.mm);
	printm('minmax:user	max%%diff = %g', max_percent_diff(Y.d, Y.mm))

	if 0 % test 'uniform' scaling
		s.un = nufft_init(om, Nd, Jd, Kd, n_shift, 'uniform')
		Y.un = nufft(x, s.un);
		printm('user-unif max%%diff = %g', max_percent_diff(Y.mm, Y.un))
	return
	end

%	s1 = nufft_init(om, Nd, Jd, Kd, n_shift, 'loop', 'kaiser');
%	printm('kb loop max %% difference = %g', max_percent_diff(sn.p,s1.p))

 if 0 % test against old 2D version
	s2 = nufft2_init_kb(om, oldarg{:}, n_shift, 'kaiser');
%	Y2 = nufft2(x, s2);
%	printm('KB 2d vs nd max%%diff = %g', max_percent_diff(Y2, Y.kb))
	printm('KB 2d vs nd max%%diff = %g', max_percent_diff(s2.p,s.kb.p))

	s2 = nufft2_init(om, oldarg{:}, n_shift, 0, 'best');
	printm('tuned 2d vs nd max%%diff = %g', max_percent_diff(s2.p,s.mmtu.p))

	s2 = nufft2_init(om, oldarg{:}, n_shift, 0); % minmax
	printm('user 2d vs nd max%%diff = %g', max_percent_diff(s2.p,s.mm.p))

 end
return
end

Nd = st.Nd;
Kd = st.Kd;

dims = size(x);
dd = length(Nd);
if ndims(x) < dd, fail 'input signal has too few dimensions', end
if any(dims(1:dd) ~= Nd), fail 'input signal has wrong size', end

% the usual case is where L=1, i.e., there is just one input signal.
if ndims(x) == dd
	x = x .* st.sn; % apply scaling factors
	Xk = col(fftn_fast(x, Kd)); % [*Kd] oversampled FFT, padded at end

% otherwise, collapse all excess dimensions into just one
else
	xx = reshape(x, [prod(Nd) prod(dims((dd+1):end))]); % [*Nd *L]
	L = size(xx, 2);
	Xk = zeros(prod(Kd), L, class(xx)); % [*Kd *L]
	for ll=1:L
		xl = reshape(xx(:,ll), [Nd 1]); % l'th signal
		xl = xl .* st.sn; % scaling factors
		Xk(:,ll) = col(fftn_fast(xl, [Kd 1]));
	end
end

% interpolate using precomputed sparse matrix
% or with tabulated interpolator
if ~isfield(st, 'interp_table')
	X = st.p * Xk; % [M *L]
else
	X = feval(st.interp_table, st, Xk);
end

if ndims(x) > dd
	X = reshape(X, [st.M dims((dd+1):end)]); % [M (L)]
end


function [clusterID,EventType,AlgoInfo] = calc_decluster_gardKnop(mCatalog,nMethod,SetMain)
	% decluster earthquake catalog using the Windowing technique in space and time (Knopoff & Gardner)
	%
	% [mCatDecluster, mCatAfter, vCluster, vCl, vMainCluster] = CALC_DECLUSTER_GARDKNOP(mCatalog,nMethod)
	% ----------------------------------------------------------------------------------------------------------
	%
	% Decluster earthquake catalog using the Windowing technique in space and time by
	% Knopoff & Gardner, GJR astr. Soc, 28, 311-313, 1972
	% Gardner & Knopoff, BSSA, 64,5, 1363-1367, 1974
	% using different windows
	%
	% Incoming variables
	% mCatalog : Incoming earthquake catalog (ZMAP format)
	% nMethod  : Window length for declustering (see calc_windows.m)
	%            1: Gardener & Knopoff, 1974
	%            2: Gruenthal pers. communication
	%            3: Urhammer, 1986
	%
	% Outgoing variables:
	% mCatDecluster : Declustered earthquake catalog
	% mCatAfter     : Catalog of aftershocks (and foreshocks)
	% vCluster      : Vector indicating only aftershocks/foreshocls in cluster using a cluster number
	% vCl           : Vector indicating all events in clusters using a cluster number
	% vMainCluster  : Vector indicating mainshocks of clusters using a cluster number
	%
	% J. Woessner, woessner@seismo.ifg.ethz.ch
	% last update: 29.08.02

	%% Added:
	% 31.07.02 Correction for problem of mainshocks with a cluster number as aftershocks belong to two sequences
	% 31.07.02 Corrected fMaxClusterMag(nMagCount) to fMaxClusterMag, since counting variable not needed
	% 31.07.02 Improved resizing time window by adding time difference from initial event to bigger aftershock
	% 13.08.02 Added waitbars
	% 28.08.02 Changed distance determination using now distance and repmat
	% 29.08.02 Cluster determination strategy change: Now selecting all aftershocks using the window of the first shock,
	%          adding the events from the bigger aftershocks (later labelled mainshocks); calc_decluster_ver3.m keeps
	%          resizing technique

	%%% Remember: Improve zero length cluster problem which might appear

	%% Initialize Vectors and Matrices

	import mapseis.declustering.calc_windows_gardKnop;

	%edited for MapSeis: DE 2011
	%This is the first release of the Gardner Knopoff method for mapseis, the code is mainly the same as the zmap version, there might be
	%lot to gain performancewise in case of a rewriting of the code, but for know this should be alright.

	if nargin<3
		SetMain=false;
	end

	mCatDecluster = [];
	mCatAfter = [];
	vCluster = zeros(length(mCatalog),1); % Initialize all events as mainshock
	vCl = zeros(length(mCatalog),1); % Initialize all events as mainshock
	vSel = zeros(length(mCatalog),1); % Initialize all events as mainshock
	vMainCluster = zeros(length(mCatalog),1); % Initialize

	%[B,MagSortID] = sort(mCatalog(:,6),'descend');

	[nXSize, nYsize] = size(mCatalog);
	if nXSize == 0
	    disp('Load new catalog');
	    return
	end

	vDecDate = mCatalog(:,3);
	nCount = 0;    % Variable of cluster number

	fMagThreshold = min(mCatalog(:,6)); % Set Threshold to minimum magnitude of catalog
	hWaitbar1 = waitbar(0,'Identifying clusters...');
	set(hWaitbar1,'Numbertitle','off','Name','Decluster percentage');
	for nEvent=1:length(mCatalog(:,6))
	    %nEvent
	    %nCount
	    %nEvent=MagSortID(iCount);
	    if vCluster(nEvent) == 0

	        fMagnitude(nEvent) = mCatalog(nEvent, 6);

	                  %% Define first aftershock zone and determine magnitude of strongest aftershock
	            fMag = fMagnitude(nEvent);
	            [fSpace, fTime] = calc_windows_gardKnop(fMagnitude(nEvent), nMethod);
	            fSpaceDeg = km2deg(fSpace);

	            %% This first if is for events with no location given
	            if isnan(mCatalog(nEvent,1)) %Does this ever happen??? Maybe in a special study (DE)
	                %Select what's on the same time??? (DE)
	                vSel = (mCatalog(:,3) == mCatalog(nEvent,3));

	            else
	            	%Select what is in range (DE)
	                mPos = [mCatalog(nEvent, 1) mCatalog(nEvent,2)];
	                mPos = repmat(mPos,length(mCatalog(:,1)), 1);
	                mDist = abs(distance(mCatalog(:,1), mCatalog(:,2), mPos(:,1), mPos(:,2)));
	                vSel = ((mDist <= fSpaceDeg) & (vDecDate(:,1)-vDecDate(nEvent,1) >= 0) &...
	                    (vDecDate(:,1)-vDecDate(nEvent,1) <= fTime) & vCluster(nEvent) == 0);

	            end;% End of isnan(mCatalog)

	            %maybe here and especially later is a change needed, because it might be expensive to find the "positon"
	            %in th array later (DE)
	            mTmp = mCatalog(vSel,:);

	            if length(mTmp(:,1)) == 1  % Only one event thus no cluster; IF to determine cluster or not
	                fMaxClusterMag = fMag;

	            else
	                fMaxClusterMag = max(mTmp(:,6));
	                [nIndiceMaxMag] = find(mTmp(:,6) == fMaxClusterMag);
	                fTimeMaxClusterMag = mTmp(max(nIndiceMaxMag),3);

	                %can't stand while loops, try to eliminate it (DE) %probably just sort the whole catalog with magnitude
	                %The while looks ugly but the time it uses is small
	                % Search for event with bigger magnitude in cluster and add to cluster
	                while fMaxClusterMag-fMag > 0
	                    [fSpace, fTime] = calc_windows_gardKnop(fMaxClusterMag, nMethod);
	                    fSpaceDeg = km2deg(fSpace);

	                    %% Adding aftershocks from bigger aftershock
	                    mPos = [mTmp(min(nIndiceMaxMag),1) mTmp(min(nIndiceMaxMag),2)];
	                    mPos = repmat(mPos,length(mCatalog(:,1)), 1);
	                    mDist = abs(distance(mCatalog(:,1), mCatalog(:,2), mPos(:,1), mPos(:,2)));
	                    vSel2 = ((mDist <= fSpaceDeg) & (vDecDate(:,1)-mTmp(min(nIndiceMaxMag),3) >= 0) &...
	                        (vDecDate(:,1)-mTmp(min(nIndiceMaxMag),3) <= fTime) & vCluster == 0);
	                    mTmp = mCatalog(vSel2,:);
	                    %vSel = (vSel > 0 | vSel2 > 0); % Actual addition %>0 not needed both are logical vectors (DE)
	                    vSel = vSel|vSel2;
	                    if isempty(mTmp) % no events in aftershock zone
	                        break;
	                    end;

	                    fMag = fMaxClusterMag;
	                    fMaxClusterMag = max(mTmp(:,6));
	                    [nIndiceMaxMag] = find(mTmp(:,6) == fMaxClusterMag);
	                    fTimeMaxClusterMag = mTmp(max(nIndiceMaxMag),3);

	                    if fMaxClusterMag - fMag == 0 % no bigger event in aftershock zone
	                        break;
	                    end;

	                end;  % End of while

	                nCount = nCount + 1; % Set cluster number

	            end; % End of if length(mTmp)

	            [vIndice]=find(vSel); % Vector of indices with Clusters
	            vTmpCluster(vIndice,:) = nCount;
	            %length(vTmpCluster(vIndice,:));
	            nI=1; % Variable counting the length of the cluster
	            % Select the right numbers for the cluster using the indice vector vIndice
	            % First: Insert cluster number after check for length
	            % Second: Check if it's a mainshock
	            % Third: Keep the former cluster indice;
	            while nI <= length(vIndice)
	                if (~isempty(vTmpCluster(vIndice(nI))) & length(vTmpCluster(vIndice,:)) > 1 & vCluster(vIndice(nI)) == 0)
	                    vCluster(vIndice(nI)) = vTmpCluster(vIndice(nI));
	                    %vEventnr(vIndice,:) = nEvent;
	                elseif  (~isempty(vTmpCluster(vIndice(nI))) & length(vTmpCluster(vIndice,:)) == 1 & vCluster(vIndice(nI)) == 0)
	                    vCluster(vIndice(nI)) = 0;
	                else
	                    vCluster(vIndice(nI)) = vCluster(vIndice(nI));
	                end;
	                nI=nI+1;
	            end; %End of while nI
	            %                 nCount = nCount + 1; % Set cluster number %% Watch
	            %             end; % End of if to determine cluster or not %% Watch
	            %%% Check if the Cluster is not just one event which can happen in case of keeping the former
	            %%% cluster number in preceeding while-Loop
	            vSelSingle = (vCluster == nCount);
	            [vIndiceSingle] = find(vSelSingle);
	            %vTmpSingle(vIndiceSingle,:);
	            if length(vIndiceSingle) == 1
	                %nCount
	                %vIndiceSingle
	                vCluster(vIndiceSingle)=0; % Set the event as mainsock
	                nCount = nCount-1; % Correct the cluster number down by one
	            end;

	    end; % End of if checking if vCluster == 0
	    if rem(nEvent,100) == 0
	        waitbar(nEvent/length(mCatalog(:,6)))
	    end; % End updating waitbar
	end; % End of FOR over mCatalog
	close(hWaitbar1);
	%nCount
	%% vCL Cluster vector with mainshocks in it; vCluster is now modified to get rid of mainshocks
	vCl = vCluster;

	%% Matrix with cluster indice, magnitude and time
	mTmpCat = [vCluster mCatalog(:,6) mCatalog(:,3)];
	%% Delete largest event from cluster series and add to mainshock catalog
	hWaitbar2 = waitbar(0,'Identifying mainshocks in clusters...');
	set(hWaitbar2,'Numbertitle','off','Name','Mainshock identification ');
	for nCevent = 1:nCount
	    %nCevent
	    vSel4 = (mTmpCat(:,1) == nCevent); % Select cluster
	    mTmpCat2 = mCatalog(vSel4,:);
	    fTmpMaxMag = max(mTmpCat2(:,6)); % Select max magnitude of cluster
	    vSelMag = (mTmpCat2(:,6) == fTmpMaxMag);
	    [nMag] = find(vSelMag);
	    if length(nMag) == 1
	        vSel5 = (mTmpCat(:,1) == nCevent & mTmpCat(:,2) == fTmpMaxMag); % Select the event
	        [vIndiceMag] = find(vSel5); % Find indice
	        vCluster(vIndiceMag) = 0;  % Set cluster value to zero, so it is a mainshock
	        vMainCluster(vIndiceMag) = nCevent; % Set mainshock vector to cluster number
	    elseif length(nMag) == 0
	        disp('Nothing in ')
	        nCevent
	    else
	        vSel = (mTmpCat(:,1) == nCevent & mTmpCat(:,2) == fTmpMaxMag);
	        mTmpCat3 = mCatalog(vSel,:);
	        [vIndiceMag] = min(find(vSel)); % Find minimum indice of event with max magnitude in cluster
	        vCluster(vIndiceMag) = 0;  % Set cluster value to zero, so it is a mainshock
	        vMainCluster(vIndiceMag) = nCevent;  % Set mainshock vector to cluster number
	    end;
	    if rem(nCevent,20) == 0
	        waitbar(nCevent/nCount)
	    end; % End updating waitbar
	end; % End of For nCevent
	close(hWaitbar2);
	%% Create a catalog of aftershocks (mCatAfter) and of declustered catalog (mCatDecluster)
	vSel = (vCluster(:,1) > 0);
	%mCatDecluster=mCatalog(~vSel,:);
	%mCatAfter = mCatalog(vSel,:);

	%create the wanted output for mapseis
	%clusterID,EventType,AlgoInfo
	EventType = categorical(ones(length(mCatalog,1)),...
		[0 1 2 3],...
		{'unclassified','single event','mainshock','aftershock'});
	clusterID=vCl;

	%the type first
	EventType(vCl~=0)='mainshock';
	EventType(vCluster~=0)='aftershock';
	clusterID(clusterID==0)=NaN;

	%The original does not set a mainshock if all earhquakes in a cluster have the same Magnitude, this part will
	%if SetMain is set to true choose the first eq as mainshock, which is as the catalog should be sorted by time
	%the first in the list
	%Also does the original code determine the length of the clusters, this here will do it
	uniqueClust=unique(clusterID(~isnan(clusterID)));
	ClusterLength=zeros(numel(uniqueClust),1);
	if SetMain
		for i=1:numel(ClusterLength);
			ClusterLength(i)=sum(clusterID==uniqueClust(i));

			if all(EventType(clusterID==uniqueClust(i))=='aftershock')
				TheInder=find(clusterID==uniqueClust(i));
				EventType(TheInder(1))='mainshock';
			end
		end

	else
		for i=1:numel(ClusterLength);
			ClusterLength(i)=sum(clusterID==uniqueClust(i));
		end

	end

	CalcParameter.Method=nMethod;
	CalcParameter.SetMain=SetMain;

	AlgoInfo.Type='Gardner-Knopoff-mapseis-v1';
	AlgoInfo.UsedConfig=CalcParameter;
	AlgoInfo.CalculationDate=date;
	AlgoInfo.ClusterLengths=ClusterLength;
	AlgoInfo.largest_Eq=[];

end


function [gp, diagnosis] = svigp(gp, x, y, varargin)
%SVIGP  Stochastic variational inference for GP
%
%  Description
%    GP = SVIGP(GP, X, Y, OPTIONS) optimises the variational, likelihood
%    and covariance function parameters of a sparse SVI GP model
%    given matrix X of training inputs and vector Y of training targets.
%    The model follows the description in Hensman, Fusi and Lawrence
%    (2013). Supported likelihood functions are gaussian (for regression)
%    and probit (for classification).
%
%    [GP, DIAGNOSIS] = SVIGP(GP, X, Y, OPTIONS) also returns values
%    monitored during the iteration. The structure DIAGNOSIS contains
%    the energy in the field e, the likelihood and covariance function
%    parameters in the field w, and the mean log predictive density (if xt
%    and yt is provided) in the field mlpd. The first dimension corresponds
%    to the main iteration in all these arrays. The second dimension
%    corresponds to the minibatch iteration in e and w. The third dimension
%    corresponds to the different parameters in w.
%
%    OPTIONS is optional parameter-value pair
%      xt               - test inputs. Used for monitoring the mean log
%                         predictive density, N.B. the monitoring needs
%                         both xt and yt.
%      yt               - observed yt in test points. Used for monitoring
%                         the mean log predictive density, N.B. the
%                         monitoring needs both xt and yt.
%      z                - optional observed quantity in triplet
%                         (x_i,y_i,z_i) Some likelihoods may use this. For
%                         example, in case of Poisson likelihood we have
%                         z_i=E_i, that is, expected value for ith case.
%      zt               - optional observed quantity in triplet
%                         (xt_i,yt_i,zt_i) Some likelihoods may use this.
%                         For example, in case of Poisson likelihood we
%                         have z_i=E_i, that is, the expected value for the
%                         ith case. N.B. used only in the monitoring of the
%                         mean log predictive density.
%      X_u              - inducing inputs. If omitted, kmeans clustering is
%                         applied and the resulting cluster centroids are
%                         selected. The number of inducing variables is
%                         then controlled by the parameter nu (see below).
%      nu               - the number of inducing variables if X_u is
%                         omitted. Can be given as an absolute value or
%                         relative to the input size. The default is
%                         min(floor(0.1*n),1500), where n is the number
%                         of training inputs.
%      m                - initial mean of the inducing variables. The
%                         default is zero vector.
%      S                - initial covariance of the inducing variables. The
%                         default is 0.1 times identity matrix.
%      n_minibatch      - absolute or relative size of the minibatches
%                         (relative to the number of the training inputs).
%                         Does not have to be divisible with the number of
%                         training inputs. The default is 0.1 (relative).
%      maxiter          - the maximum number of iterations (default 5000).
%                         Providing 0 initialises the gp structure
%                         parameters without optimisation.
%      momentum         - momentum term for the covariance function
%                         parameters (default 0.9)
%      mu1              - initial step size of the variational parameters
%                         (default 0.01).
%      mu2              - initial step size of the likelihood and
%                         covariance function parameters (default 1e-5)
%      tol              - tolerance of energy for the convergence
%                         (default 1e-6).
%      step_size        - function handle for the step size as a function
%                         of the iteration. The default function is f(i) =
%                         1/(1+i/n_minibatch), where n_minibatch is the
%                         size of the minibatches.
%      lik_sigma2       - likelihood variance in the case of non-gaussian
%                         likelihood (default 0.1).
%      lik_sigma2_prior - prior for the likelihood variance in the case of
%                         non-gaussian likelihood. The default is
%                         prior_loggaussian('mu',-2, 's2', 0.5).
%      display          - Control the amount of diagnostic verbosity.
%                         'off' displays nothing, 'final' display the
%                         final output, and 'iter' displays output at
%                         each iteration. Alternatively by providing a
%                         scalar value, fewer iterations can be displayed,
%                         e.g. value 10 displays only every tenth
%                         value (default behaviour).
%
%  See also
%    GP_SET, GPSVI_PRED, GPSVI_PREDGRAD, GPSVI_E, GPSVI_G, DEMO_SVI*
%
%  References:
%    Hensman, J., Fusi, N. and Lawrence, N. D. (2013). Gaussian processes
%    for big data. arXiv preprint arXiv:1309.6835.

% Copyright (c) 2014 Ville Tolvanen
% Copyright (c) 2014 Tuomas Sivula

% This software is distributed under the GNU General Public
% License (version 3 or later); please refer to the file
% License.txt, included with the software, for details.

ip=inputParser;
ip.FunctionName = 'SVIGP';
ip.addRequired('gp',@isstruct);
ip.addRequired('x', @(x) ~isempty(x) && isreal(x) && all(isfinite(x(:))))
ip.addRequired('y', @(x) ~isempty(x) && isreal(x) && all(isfinite(x(:))))
ip.addParamValue('xt', [], @(x) isnumeric(x) && isreal(x) && all(isfinite(x(:))))
ip.addParamValue('yt', [], @(x) isnumeric(x) && isreal(x) && all(isfinite(x(:))))
ip.addParamValue('z', [], @(x) isnumeric(x) && isreal(x) && all(isfinite(x(:))))
ip.addParamValue('zt', [], @(x) isnumeric(x) && isreal(x) && all(isfinite(x(:))))
ip.addParamValue('X_u', [], @(x) isnumeric(x) && isreal(x) && all(isfinite(x(:))))
ip.addParamValue('nu', [], @(x) isreal(x) && isscalar(x) && x > 0)
ip.addParamValue('m', [], @(x) isnumeric(x) && isreal(x) && all(isfinite(x(:))))
ip.addParamValue('S', [], @(x) isnumeric(x) && isreal(x) && all(isfinite(x(:))))
ip.addParamValue('n_minibatch', 0.1, @(x) isreal(x) && isscalar(x) && x > 0)
ip.addParamValue('maxiter', 2000, @(x) isreal(x) && isscalar(x) && x >= 0)
ip.addParamValue('momentum', 0.9, @(x) isreal(x) && isscalar(x) && x > 0)
ip.addParamValue('mu1', 0.01, @(x) isreal(x) && isscalar(x) && x > 0)
ip.addParamValue('mu2', 1e-5, @(x) isreal(x) && isscalar(x) && x > 0)
ip.addParamValue('tol', 1e-6, @(x) isreal(x) && isscalar(x))
ip.addParamValue('step_size', [], @(x) isa(x,'function_handle'))
ip.addParamValue('lik_sigma2',0.1, @(x) isscalar(x) && x>=0);
ip.addParamValue('lik_sigma2_prior',prior_loggaussian('mu',-2, 's2', 0.5), ...
  @(x) isstruct(x) || isempty(x));
ip.addParamValue('display', 10, @(x) ismember(x,{'final','iter','off'}) ...
  || (isreal(x) && isscalar(x) && x > 1) )

ip.parse(gp, x,y,varargin{:});
x=ip.Results.x;
y=ip.Results.y;
z=ip.Results.z;
xt=ip.Results.xt;
yt=ip.Results.yt;
zt=ip.Results.zt;
X_u=ip.Results.X_u;
nu=ip.Results.nu;
m=ip.Results.m;
S=ip.Results.S;
n_minibatch=ip.Results.n_minibatch;
momentum=ip.Results.momentum;
mu1=ip.Results.mu1;
mu2=ip.Results.mu2;
step_size=ip.Results.step_size;
maxiter=ip.Results.maxiter;
tol=ip.Results.tol;
lik_sigma2 = ip.Results.lik_sigma2;
lik_sigma2_prior = ip.Results.lik_sigma2_prior;
display = ip.Results.display;

% Initialise the diagnosis output structure
diagnosis = struct();

% Check if latent method SVI has been set
if ~isfield(gp, 'latent_method') || ~isequal(gp.latent_method, 'SVI')
  gp=gp_set(gp, 'latent_method', 'SVI');
end

% Check if latent method is not gaussian or probit
if ~strcmp(gp.lik.type, 'Gaussian') && ~strcmp(gp.lik.type, 'Probit')
  error('Supported likelihoods for SVIGP are gaussian and probit.')
end

% Process parameters
if xor(isempty(xt), isempty(yt))
  warning('Need both xt and yt for monitoring mean log predictive density.');
end
n=size(x,1);

if n_minibatch < 1
  n_minibatch = max(floor(n_minibatch*n),1);
end
if n_minibatch > n
  n_minibatch = max(floor(0.1*n),1);
  warning('Too many minibatches, using floor(0.1*n) = %d instead.', ...
    n_minibatch)
end

if isempty(step_size)
  step_size=@(iter) 1/(1+iter./n_minibatch);
end

% Handle the inducing inputs
if ~ismember('X_u',ip.UsingDefaults)
  gp.X_u = X_u;
end
if isempty(gp.X_u)
  % Assign X_u by clustering
  if isempty(nu)
    nu = min(max(floor(0.1.*n),1),1500);
  elseif nu < 1
    nu = max(floor(nu.*n),1);
  end
  fprintf('Assign inducing inputs by clustering\n')
  Sw=warning('off','stats:kmeans:EmptyCluster');
  [~,X_u] = kmeans(x, nu,'Start','uniform',...
    'EmptyAction','singleton');
  warning(Sw);
  gp.X_u = X_u;
end
gp.nind = size(gp.X_u,1);

% Handle the rest of the parameters
if ~ismember('m',ip.UsingDefaults)
  if length(m) == gp.nind
    gp.m = m;
  else
    error('The size of m does not match with X_u')
  end
elseif ~isfield(gp, 'm') || length(gp.m) ~= gp.nind
  gp.m = zeros(gp.nind,1);
end

if ~ismember('S',ip.UsingDefaults)
  if ismatrix(S) && all(size(S) == gp.nind)
    gp.S = S;
  else
    error('The size of S does not match with X_u')
  end
elseif ~isfield(gp, 'S') || any(size(gp.S) ~= gp.nind)
  gp.S = 0.1*eye(gp.nind);
  % gp.S = gp_trcov(gp,gp.X_u);
end
gp.t1=gp.S\gp.m;
gp.t2=-0.5.*inv(gp.S);

% Handle the likelihood variance
if ~isfield(gp.lik, 'sigma2')
  gp.lik.sigma2 = lik_sigma2;
  gp.lik.p.sigma2 = lik_sigma2_prior;
  gp.lik.fh.lp = @lik_lp;
  gp.lik.fh.lpg = @lik_lpg;
end

% Return prematurely if only initialising
if maxiter == 0
  return
end

% Parameters
w = gp_pak(gp);
w0 =w;
nh1 = numel(gp.t1) + numel(gp.t2);
nh2 = length(w)-nh1;

% Initial step-size vector
mu0 = mu1.*ones(size(w));
mu0(end-nh2+1:end)=mu2;

% Size of minibatches
nb=n_minibatch;
% Number of minibatches
nbb=ceil(n/nb);

% Monitored values
if nargout > 1
  e_all = zeros(maxiter,nbb);
  w_all = zeros(maxiter,nbb,nh2);
  if ~isempty(xt) && ~isempty(yt)
    mlpd_all = zeros(maxiter,1);
    rmse_all = zeros(maxiter,1);
  end
end
g_old=zeros(size(w));
momentum=momentum.*ones(size(w));
etot_old=Inf;

% Preprocess conditions for iteration
disp_iter = strcmp(display, 'iter');
disp_i = 0;
disp_count = display;

% Iterate until convergence or maxiter
converged = 0;
try_fix_mu2 = 0;
for iter = 1:maxiter

  % Divide the data into minibatches
  inds = cell(nbb,1);
  ind = randperm(n);
  for i=1:nbb-1
    inds{i} = ind((i-1)*nb+1:i*nb);
  end
  inds{nbb} = ind((nbb-1)*nb+1:end);

  % Compute the step-size
  mu = mu0.*step_size(iter);

  % Iterate all the minibatches
  etot = 0;
  broken = 0;
  for i=1:nbb
    if isempty(z)
      zi = [];
    else
      zi = z(inds{i},:);
    end
    gp.data_prop=length(inds{i})./n;
    [e,~,~,param] = gpsvi_e(w,gp,x(inds{i},:),y(inds{i},:), 'z', zi);
    etot = etot+e;
    g = gpsvi_g(w,gp,x(inds{i},:),y(inds{i},:), 'z', zi, ...
      'gpsvi_e_param', param);
    g = mu.*g + momentum.*g_old;
    g_old = g;
    w = w+g;
    if nargout > 1
      e_all(iter,i) = e;
      w_all(iter,i,:) = w(end-nh2+1:end);
    end
    if ~isnan(etot) ...
        && all(~isinf(exp(w(end-nh2+1:end)))) ...
        && all(exp(w(end-nh2+1:end))~=0) ...
        && ~any(isnan(g)) ...
        && ~isnan(gpsvi_e(w+g,gp,x(inds{i},:),y(inds{i},:), 'z', zi))
      gp = gp_unpak(gp,w);
    elseif ~try_fix_mu2
      fprintf('Bad parameter values, decreasing mu2.\n');
      try_fix_mu2 = 1;
      mu2 = 0.1*mu2;
      mu0(end-nh2+1:end)=mu2;
      w = w0;
      g_old = zeros(1,nh1+nh2);
      broken = 1;
      break
    else
      fprintf('Bad parameter values, decreasing step-size and momentum.\n');
      mu0 = 0.1.*mu0;
      momentum = 0.5*momentum;
      w = w0;
      g_old = zeros(1,nh1+nh2);
      broken = 1;
      break
    end
    gpsvi_e('clearcache',gp);
  end
  if broken
    continue
  end
  etot=etot/nbb;

  % Analyse and print
  if isscalar(display)
    if disp_count == display
      disp_i = 1;
      disp_count = 1;
    else
      disp_i = 0;
      disp_count = disp_count +1;
    end
  end
  if ~isempty(xt) && ~isempty(yt) ...
      && ( disp_iter || nargout > 1 || disp_i)
    [Eft,~,lpyt] = gpsvi_pred(gp,x,y,xt,'yt',yt, 'z', zi, 'zt', zt);
    lpyt = mean(lpyt);
    if nargout > 1
      mlpd_all(iter) = lpyt;
      rmse = sqrt(mean((yt-Eft).^2));
      rmse_all(iter) = rmse;
    end
    if disp_iter || disp_i
      fprintf('iter=%d/%d, e=%.3f, mlpd=%.4g, rmse=%.4g, de=%.5g\n', ...
        iter, maxiter, etot, lpyt, rmse, abs(etot-etot_old));
    end
  elseif disp_iter || disp_i
    fprintf('iter=%d/%d, e=%.3f, de=%.5g\n', ...
      iter, maxiter, etot, abs(etot-etot_old));
  end

  % Check for convergence
  if abs(etot-etot_old)<tol
    converged = 1;
    if strcmp(display, 'iter')
      fprintf('Energy converged\n')
    end
    break
  end
  etot_old=etot;

end

% Display results
if ~converged
  fprintf('Iteration limit %d reached while optimising the parameters\n', ...
    maxiter)
elseif ~strcmp(display, 'off')
  fprintf('Tolerance reached in %d iterations\n', iter)
end
if strcmp(display, 'final') || isscalar(display)
  if ~isempty(xt) && ~isempty(yt) && ( nargout > 1 || disp_i)
    fprintf(['Final values:\n' ...
      'e=%.3f, mlpd=%.4g, rmse=%.4g\n'], etot, lpyt, rmse);
  elseif ~isempty(xt) && ~isempty(yt)
    [Eft,~,lpyt] = gpsvi_pred(gp,x,y,xt,'yt',yt, 'z', z, 'zt', zt);
    fprintf(['Final values:\n' ...
      'e=%.3f, mlpd=%.4g, rmse=%.4g\n'], ...
      etot, mean(lpyt)), sqrt(mean((yt-Eft).^2));
  else
    fprintf('Final energy: %.3f\n', etot);
  end
end

% Save the monitored values
if nargout > 1
  diagnosis.e = e_all(1:iter,:);
  diagnosis.w = w_all(1:iter,:,:);
  if ~isempty(xt) && ~isempty(yt)
    diagnosis.mlpd = mlpd_all(1:iter);
    diagnosis.rmse = rmse_all(1:iter);
  end
end

end

function lp = lik_lp(lik, varargin)
%LIK_LP  log(prior) of the likelihood parameters
%
%  Description
%    LP = LIK_LP(LIK) takes a likelihood structure LIK and
%    returns log(p(th)), where th collects the parameters. This
%    subfunction is needed if there are likelihood parameters.
%    Added for non-gaussian likelihoods in SVIGP.
%
%  See also
%    LIK_*, SVIGP

% If prior for sigma2sion parameter, add its contribution
lp=0;
if ~isempty(lik.p.sigma2)
  lp = lik.p.sigma2.fh.lp(lik.sigma2, lik.p.sigma2) +log(lik.sigma2);
end
end

function lpg = lik_lpg(lik)
%LIK_LPG  d log(prior)/dth of the likelihood parameters
%
%  Description
%    E = LIK_NEGBIN_LPG(LIK) takes a likelihood structure LIK and
%    returns d log(p(th))/dth, where th collects the parameters.
%    This subfunction is needed if there are likelihood parameters.
%    Added for non-gaussian likelihoods in SVIGP.
%
%  See also
%    LIK_*, SVIGP

lpg=[];
if ~isempty(lik.p.sigma2)
  % Evaluate the gprior with respect to sigma2
  ggs = lik.p.sigma2.fh.lpg(lik.sigma2, lik.p.sigma2);
  lpg = ggs(1).*lik.sigma2 + 1;
  if length(ggs) > 1
    lpg = [lpg ggs(2:end)];
  end
end
end


function [a,n,r,epe,eph,epw,usol_c,vsol_c] = mg_ns_q1cd(xy,ev,flowsol,domain,lncoarse,lnfine,outbnd)
%mg_ns_q1cd   convection-diffusion matrix generator for GMG (Navier-Stokes)
%   [a,n,r,epe,eph,epw,usol_c,vsol_c] = mg_ns_q1cd(xy,ev,flowsol,domain,lncoarse,lnfine,outbnd)
%   input
%           xy         vertex coordinate vector
%           ev         element mapping matrix
%           flowsol    current velocity field
%           domain     domain parameter, 1 for square, 3 for step
%           lncoarse   coarse grid index, log2(nc) for nc x nc square grid
%           lnfine     finest grid index, log2(nf) for nf x nf square grid
%           outbnd     location of outflow boundary
%   output
%           a          discrete diffusion operator
%           n          discrete convection operator
%           r          mass matrix
%           epe        viscosity normalised element peclet numbers
%           eph        flow specific element lengths
%           epw        centroid evaluated wind
%           usol_c     first convection coefficient on coarse grid nodes
%           vsol_c     second convection coefficient on coarse grid nodes
%
%   IFISS function: HCE; 18 March 2005.
% Copyright (c) 2005 D.J. Silvester, H.C. Elman, A. Ramage

% Modification of DJS routine mg_q1cd to allow convection coefficients to
% be passed via flowsol.  Analogous to femq1_cd.

% Square domain
if domain==1,
% Size of fine grid, obtained from underlying flow solution flowsol
   nfine  = length(flowsol)/2;
   nf = sqrt(nfine);
   x=xy(:,1); y=xy(:,2);
   nvtx=length(x);   nel=length(ev(:,1));

% Get flow values at coarse grid points
   lshift = lnfine - lncoarse;
   usol=flowsol(1:nfine);
   usol_grid = reshape(usol,nf,nf);
   usol_grid_c = usol_grid(1:2^lshift:nf,1:2^lshift:nf);
   usol_c = reshape(usol_grid_c,nvtx,1);
   vsol=flowsol(nfine+1:2*nfine);
   vsol_grid = reshape(vsol,nf,nf);
   vsol_grid_c = vsol_grid(1:2^lshift:nf,1:2^lshift:nf);
   vsol_c = reshape(vsol_grid_c,nvtx,1);

% Step domain
elseif domain==3,
% Various counts of fine/coarse points in small part (1) and large part (2) of step
% Logic borrowed from AR routine step_mg_prolong
   nelf = 2^lnfine;
   nfine = (((outbnd+1)/2)*nelf+1)*(nelf+1) - (nelf/2)^2;
   nelc = 2^lncoarse;
   ncoarse = (((outbnd+1)/2)*nelc+1)*(nelc+1) - (nelc/2)^2;
   nfh1 = (nelf/2);     nfv1 = (nelf/2+1);  nf1 = nfh1*nfv1;
   nfh2 = (outbnd*nelf/2+1); nfv2 = nelf+1; nf2 = nfh2*nfv2;
   nch1 = (nelc/2);     ncv1 = (nelc/2+1);  nc1 = nch1*ncv1;
   nch2 = (outbnd*nelc/2+1); ncv2 = nelc+1; nc2 = nch2*ncv2;

% Get flow values at coarse grid points
   lshift = lnfine-lncoarse;
   usol=flowsol(1:nfine);
   usol1_grid = reshape(usol(1:nf1),nfh1,nfv1);
   usol1_grid_c = usol1_grid(1:2^lshift:nfh1,1:2^lshift:nfv1);
   usol1_c = reshape(usol1_grid_c,nc1,1);
   usol2_grid = reshape(usol(nf1+1:nfine),nfh2,nfv2);
   usol2_grid_c = usol2_grid(1:2^lshift:nfh2,1:2^lshift:nfv2);
   usol2_c = reshape(usol2_grid_c,nc2,1);
   usol_c = [usol1_c;usol2_c];
   vsol=flowsol(nfine+1:2*nfine);
   vsol1_grid = reshape(vsol(1:nf1),nfh1,nfv1);
   vsol1_grid_c = vsol1_grid(1:2^lshift:nfh1,1:2^lshift:nfv1);
   vsol1_c = reshape(vsol1_grid_c,nc1,1);
   vsol2_grid = reshape(vsol(nf1+1:nfine),nfh2,nfv2);
   vsol2_grid_c = vsol2_grid(1:2^lshift:nfh2,1:2^lshift:nfv2);
   vsol2_c = reshape(vsol2_grid_c,nc2,1);
   vsol_c = [vsol1_c;vsol2_c];
end

%
x=xy(:,1); y=xy(:,2);
nvtx=length(x);
nel=length(ev(:,1));
lx=max(x)-min(x); ly=max(y)-min(y);
hx=max(diff(x)); hy=max(diff(y));
%
% Initialise global matrices
a = sparse(nvtx,nvtx);
n = sparse(nvtx,nvtx);
r = sparse(nvtx,nvtx);
%
% Set up 2x2 Gauss points
gpt=1.0e0/sqrt(3.0e0);
s(1) = -gpt;  t(1) = -gpt;   wt(1) = 1;
s(2) =  gpt;  t(2) = -gpt;   wt(2) = 1;
s(3) =  gpt;  t(3) =  gpt;   wt(3) = 1;
s(4) = -gpt;  t(4) =  gpt;   wt(4) = 1;

%
% Inner loop over elements
for ivtx = 1:4
   xl_v(:,ivtx) = x(ev(:,ivtx));
   yl_v(:,ivtx) = y(ev(:,ivtx));
   xsl(:,ivtx) = usol_c(ev(:,ivtx));
   ysl(:,ivtx) = vsol_c(ev(:,ivtx));
end
ae = zeros(nel,4,4);
ne = zeros(nel,4,4);
re = zeros(nel,4,4);
% Loop over 2x2 Gauss points
for igpt = 1:4
   sigpt=s(igpt);
   tigpt=t(igpt);
   wght=wt(igpt);
%  Evaluate derivatives etc
   [jac,invjac,phi,dphidx,dphidy] = deriv(sigpt,tigpt,xl_v,yl_v);
   u_x = zeros(nel,1); u_y=zeros(nel,1);
   for k=1:4
      u_x(:) = u_x(:) + xsl(:,k) .* phi(:,k);
      u_y(:) = u_y(:) + ysl(:,k) .* phi(:,k);
   end
   for j = 1:4
      for i = 1:4
          ae(:,i,j) = ae(:,i,j)  + dphidx(:,i).*dphidx(:,j) .* invjac(:);
          ae(:,i,j) = ae(:,i,j)  + dphidy(:,i).*dphidy(:,j) .* invjac(:);
          re(:,i,j) = re(:,i,j)  + phi(:,i).*phi(:,j) .* jac(:);
%%%       ne(:,i,j) = ne(:,i,j) + flowx(:) .* phi(:,i) .* dphidx(:,j);
%%%       ne(:,i,j) = ne(:,i,j) + flowy(:) .* phi(:,i) .* dphidy(:,j);
          ne(:,i,j)  = ne(:,i,j)  + wght*u_x(:).*phi(:,i).*dphidx(:,j);
          ne(:,i,j)  = ne(:,i,j)  + wght*u_y(:).*phi(:,i).*dphidy(:,j);
       end
	end
% End of Gauss point loop
end
%
% Perform assembly of global matrix  and source vector
for krow=1:4
   nrow=ev(:,krow);
   for kcol=1:4
      ncol=ev(:,kcol);
      a = a + sparse(nrow,ncol,ae(:,krow,kcol),nvtx,nvtx);
      r = r + sparse(nrow,ncol,re(:,krow,kcol),nvtx,nvtx);
      n = n + sparse(nrow,ncol,ne(:,krow,kcol),nvtx,nvtx);
   end
end
%
% Computation of element Peclet number (at the centroid)
% Rectangle specific calculation here
hx=abs(xl_v(:,2)-xl_v(:,1)); hy=abs(yl_v(:,3)-yl_v(:,2));
[jac,invjac,phi,dphidx,dphidy] = deriv(0,0,xl_v,yl_v);
u_x = zeros(nel,1); u_y=zeros(nel,1);
for k=1:4
   u_x(:) = u_x(:) + xsl(:,k) .* phi(:,k);
   u_y(:) = u_y(:) + ysl(:,k) .* phi(:,k);
end
flowx = u_x(:);   flowy = u_y(:);
flow_l2 = sqrt(flowx(:) .* flowx(:) + flowy(:) .* flowy(:));
if all(flowx==0), flow_h=hy;
elseif all(flowy==0), flow_h=hx;
else
   angle = atan(abs(flowy./flowx));
   flow_h = min([hx./cos(angle),hy./sin(angle)],[],2);
end
eph = flow_h;
epe = flow_h.*flow_l2/2;
epw = flow_l2;


function [Ef, Varf, xtnn, xt1, xt2] = gp_cpred(gp,x,y,xt,ind,varargin)
%GP_CPRED Conditional predictions using specific covariates
%
%  Description
%    GP_CPRED(GP,X,Y,XT,IND,OPTIONS) does predictions using only
%    covariates specified in vector IND. Other covariates are fixed to
%    either mean, median or values chosen by user. Returns predictions for
%    latent values, variance and corresponding inputs. If IND=0, only time
%    is used as a covariate for Cox-PH model.
%
%   OPTIONS is optional parameter-value pair
%      method - which value to fix the not used covariates, 'median'
%               (default), 'mean' or 'mode'
%      var    - vector specifying optional values for not used covariates,
%               elements corresponding to mean/median values should
%               be set to NaN.
%      plot   - option for plotting, 'off' (default) or 'on'
%      normdata - a structure with fields xmean, xstd, ymean, and ystd
%               to allow plotting in the original data scale (see
%               functions normdata and denormdata)
%      target - option for choosing what is computed 'mu' (default),
%               'f' or 'cdf'
%      tr     - Euclidean distance treshold for not using grid points when
%               doing predictions with 2 covariates, default 0.25
%      predcf - an index vector telling which covariance functions are
%               used for prediction. Default is all (1:gpcfn).
%               See additional information below.
%      z      - optional observed quantity in triplet (x_i,y_i,z_i)
%               Some likelihoods may use this. For example, in case of
%               Poisson likelihood we have z_i=E_i, that is, expected value
%               for ith case.
%      zt     - optional observed quantity in triplet (xt_i,yt_i,zt_i)
%               Some likelihoods may use this. For example, in case of
%               Poisson likelihood we have z_i=E_i, that is, the expected
%               value for the ith case.

ip=inputParser;
ip.FunctionName = 'GP_CPRED';
ip.addRequired('gp',@(x) isstruct(x) || iscell(x));
ip.addRequired('x', @(x) ~isempty(x) && isreal(x) && all(isfinite(x(:))))
ip.addRequired('y', @(x) ~isempty(x) && isreal(x) && all(isfinite(x(:))))
ip.addRequired('xt',  @(x) ~isempty(x) && isreal(x) && all(isfinite(x(:))))
ip.addParamValue('yt', [], @(x) isreal(x) && all(isfinite(x(:))))
ip.addParamValue('zt', [], @(x) isreal(x) && all(isfinite(x(:))))
ip.addRequired('ind', @(x) ~isempty(x) && isvector(x))
ip.addParamValue('var',  [], @(x) isreal(x))
ip.addParamValue('z', [], @(x) isreal(x) && all(isfinite(x(:))))
ip.addParamValue('predcf', [], @(x) isempty(x) || ...
                 isvector(x) && isreal(x) && all(isfinite(x)&x>0))
ip.addParamValue('tstind', [], @(x) isempty(x) || iscell(x) ||...
                 (isvector(x) && isreal(x) && all(isfinite(x)&x>0)))
ip.addParamValue('method', 'median', @(x)  ismember(x, {'median', 'mean' 'mode'}))
ip.addParamValue('plot', 'off', @(x)  ismember(x, {'on', 'off'}))
ip.addParamValue('tr', 0.25, @(x) isreal(x) && all(isfinite(x(:))))
ip.addParamValue('target', 'mu', @(x) ismember(x,{'f','mu','cdf'}))
ip.addParamValue('prct', [5 50 95], @(x) isreal(x) && all(isfinite(x(:))))
ip.addParamValue('normdata', struct(), @(x) isempty(x) || isstruct(x))
ip.addParamValue('xlabels', [], @(x) isempty(x) || iscell(x));
ip.parse(gp, x, y, xt, ind, varargin{:});
zt=ip.Results.zt;
options=struct();
options.predcf=ip.Results.predcf;
options.prct=ip.Results.prct;
options.tstind=ip.Results.tstind;
method = ip.Results.method;
vars = ip.Results.var;
plot_results = ip.Results.plot;
tr = ip.Results.tr;
target = ip.Results.target;
if strcmp(target,'f')
    options = rmfield(options,'prct');
end
yt=ip.Results.yt;
if ~isempty(yt)
  options.yt=yt;
end
z=ip.Results.z;
if ~isempty(z)
  options.z=z;
end
if ~isempty(zt)
  options.zt=zt;
end
if isempty(zt)
  options.zt=z;
end
% normdata
nd=ip.Results.normdata;
ipnd=inputParser;
ipnd.FunctionName = 'normdata';
ipnd.addParamValue('xmean',zeros(1,size(x,2)),@(x) isempty(x) || (isreal(x) && all(isfinite(x(:)))));
ipnd.addParamValue('xstd',ones(1,size(x,2)),@(x) isempty(x) || (isreal(x) && all(isfinite(x(:)))));
ipnd.addParamValue('xlog',zeros(1,size(x,2)),@(x) isempty(x) || (isreal(x) && all(isfinite(x(:)))));
ipnd.addParamValue('ymean',0,@(x) isempty(x) || (isreal(x) && all(isfinite(x(:)))));
ipnd.addParamValue('ystd',1,@(x) isempty(x) || (isreal(x) && all(isfinite(x(:)))));
ipnd.addParamValue('ylog',0,@(x) isempty(x) || (isreal(x) && all(isfinite(x(:)))));
ipnd.parse(nd);
nd=ipnd.Results;

[tmp, nin] = size(x);

if iscell(gp)
  liktype=gp{1}.lik.type;
else
  liktype=gp.lik.type;
end

if isequal(liktype, 'Coxph') && isequal(target,'mu')
    target='f';
    warning('GP_CPRED: Target ''mu'' not applicable for a Cox-PH model. Switching to target ''f''')
end

if ~isempty(vars) && (~isvector(vars) || length(vars) ~= nin)
  error('Vector defining fixed variable values must be same length as number of covariates')
end

xto=xt; [n,tmp]=size(xto);

if length(ind)==1

  if ind~=0
    [xtnn, iu] = unique(xt(:,ind));
  else
    xtnn = xt(1,:);
    iu = 1;
  end
  if ~isempty(z)
    options.zt = options.zt(iu);
  end
  if ~isempty(yt)
    options.yt = options.yt(iu);
  end
  xt = repmat(feval(method,xt), size(xtnn,1), 1);
  if ~isempty(vars)
    xt(:,~isnan(vars)) = repmat(vars(~isnan(vars)), length(xtnn), 1);
  end

  if ind>0
    xt(:,ind) = xtnn;
  end
  if ~strcmp(liktype, 'Coxph')
    switch target
      case 'f'
        [Ef, Varf] = gp_pred(gp, x, y, xt, options);
      case 'mu'
        prctmu = denormdata(gp_predprctmu(gp, x, y, xt, options),nd.ymean,nd.ystd);
        Ef = prctmu; Varf = [];
      case 'cdf'
        cdf = gp_predcdf(gp, x, y, xt, options);
        Ef = cdf; Varf = [];
    end
  else
    [Ef1,Ef2,Covf] = pred_coxph(gp,x,y,xt,'z',options.z,'zt',options.zt);
    nt=size(Ef1,1);
    if ind>0
      % conditional posterior given Ef1=E[Ef1]
      Ef = Ef2;
      Varf = diag(Covf(nt+1:end,nt+1:end)-Covf(nt+1:end,1:nt)*(Covf(1:nt,1:nt)\Covf(1:nt,nt+1:end)));
    else
      % conditional posterior given Ef2=E[Ef2]
      Ef = Ef1;
      Varf = diag(Covf(1:nt, 1:nt)-Covf(1:nt,nt+1:end)*(Covf(nt+1:end,nt+1:end)\Covf(nt+1:end,1:nt)));
      xtnn = gp.lik.xtime;
    end
  end
  if isequal(plot_results, 'on')
    if ind>0
      if ind>=1&numel(nd.xmean)>=ind
        xtnn=denormdata(xtnn,nd.xmean(ind),nd.xstd(ind));
      end
      deltadist=gp_finddeltadist(gp);
      if ~ismember(ind,deltadist)
        switch target
          case 'f'
            plot(xtnn, Ef, 'ob', xtnn, Ef, '-k', xtnn, Ef-1.64*sqrt(Varf), '--b', xtnn, Ef+1.64*sqrt(Varf), '--b')
          case 'mu'
            plot(xtnn, prctmu(:,2), 'ob', xtnn, prctmu(:,2), '-k', xtnn, prctmu(:,1), '--b', xtnn, prctmu(:,3), '--b')
          case 'cdf'
            plot(xtnn, Ef, 'o-b')
        end
      else
        switch target
          case 'f'
            plot(xtnn, Ef, 'ob', [xtnn xtnn]',[Ef-1.64*sqrt(Varf) Ef+1.64*sqrt(Varf)]', '-b')
            xlim([1.5*xtnn(1)-0.5*xtnn(2) 1.5*xtnn(end)-.5*xtnn(end-1)])
            set(gca,'xtick',xtnn)
          case 'mu'
            plot(xtnn, prctmu(:,2), 'ob', [xtnn xtnn]',[prctmu(:,1) prctmu(:,3)]', '-b')
            xlim([1.5*xtnn(1)-0.5*xtnn(2) 1.5*xtnn(end)-.5*xtnn(end-1)])
            set(gca,'xtick',xtnn)
          case 'cdf'
            plot(xtnn, Ef, 'ob')
        end
      end
    else
      % use stairs for piecewise constant baseline hazard
      xtnn = gp.lik.stime;
      [xx,yy]=stairs(xtnn, [Ef;Ef(end)]);
      [xx,yyl]=stairs(xtnn, [Ef-1.64*sqrt(Varf);Ef(end)-1.64*sqrt(Varf(end))]);
      [xx,yyu]=stairs(xtnn, [Ef+1.64*sqrt(Varf);Ef(end)+1.64*sqrt(Varf(end))]);
      plot(xx, yy, '-k', xx, yyl, '--b', xx, yyu, '--b')
    end
  end

elseif length(ind)==2

  uu1=unique(xt(:,ind(1)));
  uu2=unique(xt(:,ind(2)));
  nu1=numel(uu1);
  nu2=numel(uu2);
  if nu1==2 || nu2==2
    % First or second covariate binary

    if nu1>2 && nu2==2
      % switch indeces, so that binary covariate is first
      tmp=ind(1);ind(1)=ind(2);ind(2)=tmp;
      tmp=uu1;uu1=uu2;uu2=tmp;
    end

    xt1=xt(xt(:,ind(1))==uu1(1),:);
    xt2=xt(xt(:,ind(1))==uu1(2),:);
    [xtnn1, iu1] = unique(xt1(:,ind(2)));
    [xtnn2, iu2] = unique(xt2(:,ind(2)));

    options1=options;
    options2=options;
    if ~isempty(z)
      options1.zt = options.zt(iu1);
      options2.zt = options.zt(iu2);
    end

    xt1 = repmat(feval(method,xt1), length(xtnn1), 1);
    xt2 = repmat(feval(method,xt2), length(xtnn2), 1);
    if ~isempty(vars)
      xt1(:,~isnan(vars)) = repmat(vars(~isnan(vars)), length(xtnn1), 1);
      xt2(:,~isnan(vars)) = repmat(vars(~isnan(vars)), length(xtnn2), 1);
    end
    xt1(:,ind(1)) = uu1(1); xt1(:,ind(2)) = xtnn1;
    xt2(:,ind(1)) = uu1(2); xt2(:,ind(2)) = xtnn2;

    if ~strcmp(liktype, 'Coxph')
      switch target
        case 'f'
          [Ef1, Varf1] = gp_pred(gp, x, y, xt1);
          [Ef2, Varf2] = gp_pred(gp, x, y, xt2);
        case 'mu'
          prctmu1 = denormdata(gp_predprctmu(gp, x, y, xt1, options1),nd.ymean,nd.ystd);
          prctmu2 = denormdata(gp_predprctmu(gp, x, y, xt2, options2),nd.ymean,nd.ystd);
      end
    else
      [Ef11,Ef12,Covf] = pred_coxph(gp,x,y,xt1, 'z', z);
      Ef1 = Ef12; Varf1 = diag(Covf(size(Ef11,1)+1:end,size(Ef11,1)+1:end));
      [Ef21,Ef22,Covf] = pred_coxph(gp,x,y,xt2, 'z', z);
      Ef2 = Ef22; Varf2 = diag(Covf(size(Ef21,1)+1:end,size(Ef21,1)+1:end));
    end

    if isequal(plot_results, 'on')
      xtnn1=denormdata(xtnn1,nd.xmean(ind(2)),nd.xstd(ind(2)));
      xtnn2=denormdata(xtnn2,nd.xmean(ind(2)),nd.xstd(ind(2)));
      if nu1>2 && nu2==2
        lstyle10='or';lstyle11='-r';lstyle12='--r';
        lstyle20='ob';lstyle21='-b';lstyle22='--b';
      else
        lstyle10='ob';lstyle11='-b';lstyle12='--b';
        lstyle20='or';lstyle21='-r';lstyle22='--r';
      end
      deltadist=gp_finddeltadist(gp);
      if ~ismember(ind(2),deltadist)
        switch target
          case 'f'
            plot(xtnn1, Ef1, lstyle10, xtnn1, Ef1, lstyle11, xtnn1, Ef1-1.64*sqrt(Varf1), lstyle12, xtnn1, Ef1+1.64*sqrt(Varf1), lstyle12); hold on;
            plot(xtnn2, Ef2, lstyle20, xtnn2, Ef2, lstyle21, xtnn2, Ef2-1.64*sqrt(Varf2), lstyle22, xtnn2, Ef2+1.64*sqrt(Varf2), lstyle22); hold off;
          case 'mu'
            plot(xtnn1, prctmu1(:,2), lstyle10, xtnn1, prctmu1(:,2), lstyle11, xtnn1, prctmu1(:,1), lstyle12, xtnn1, prctmu1(:,3), lstyle12); hold on;
            plot(xtnn2, prctmu2(:,2), lstyle20, xtnn2, prctmu2(:,2), lstyle21, xtnn2, prctmu2(:,1), lstyle22, xtnn2, prctmu2(:,3), lstyle22); hold off;
        end
      else
        delta=(diff(xtnn1(1:2))/10);
        switch target
          case 'f'
            plot(xtnn1-delta, Ef1, lstyle10, [xtnn1 xtnn1]'-delta, [Ef1-1.64*sqrt(Varf1) Ef1+1.64*sqrt(Varf1)]', lstyle11); hold on;
            plot(xtnn2+delta, Ef2, lstyle20, [xtnn2 xtnn2]'+delta, [Ef2-1.64*sqrt(Varf2) Ef2+1.64*sqrt(Varf2)]', lstyle21); hold off;
            xlim([1.5*xtnn1(1)-0.5*xtnn1(2) 1.5*xtnn1(end)-.5*xtnn1(end-1)])
          case 'mu'
            plot(xtnn1-delta, prctmu1(:,2), lstyle10, [xtnn1 xtnn1]'-delta, [prctmu1(:,1) prctmu1(:,3)]', lstyle11); hold on;
            plot(xtnn2+delta, prctmu2(:,2), lstyle20, [xtnn2 xtnn2]'+delta, [prctmu2(:,1) prctmu2(:,3)]', lstyle21); hold off;
            xlim([1.5*xtnn1(1)-0.5*xtnn1(2) 1.5*xtnn1(end)-.5*xtnn1(end-1)])
        end
      end
    end
    switch target
      case 'f'
        Ef = {Ef1  Ef2}; Varf = {Varf1 Varf2}; xtnn={xtnn1 xtnn2};
      case 'mu'
        Ef = {prctmu1 prctmu2}; Varf = {[] []}; xtnn={xtnn1 xtnn2};
    end

  else
    % both the first and the second covariate are non-binary
    deltadist=gp_finddeltadist(gp);
    if ~ismember(ind(1),deltadist)
      xtnn1 = linspace(min(xt(:,ind(1))), max(xt(:,ind(1))), 20);
    else
      xtnn1 = unique(xt(:,ind(1)));
    end
    if ~ismember(ind(2),deltadist)
      xtnn2 = linspace(min(xt(:,ind(2))), max(xt(:,ind(2))), 20);
    else
      xtnn2 = unique(xt(:,ind(2)));
    end
    [XT1, XT2] = meshgrid(xtnn1, xtnn2); XT1=XT1(:); XT2=XT2(:);
    xtnn1=denormdata(xtnn1,nd.xmean(ind(1)),nd.xstd(ind(1)));
    xtnn2=denormdata(xtnn2,nd.xmean(ind(2)),nd.xstd(ind(2)));
    if ~isempty(z)
      options.zt = repmat(options.zt(1), size(XT1));
    end
    xt = repmat(feval(method,xt), length(XT1), 1);
    if ~isempty(vars)
      xt(:,~isnan(vars)) = repmat(vars(~isnan(vars)), length(XT1), 1);
    end
    xt(:,ind) = [XT1 XT2];
    if ~strcmp(liktype, 'Coxph')
      switch target
        case 'f'
          [Ef, Varf] = gp_pred(gp, x, y, xt);
        case 'mu'
          prctmu = gp_predprctmu(gp, x, y, xt, options, 'prct', 50);
          Ef = prctmu; Varf = [];
        case 'cdf'
          cdf = gp_predcdf(gp, x, y, xt, options);
          Ef = cdf; Varf = [];
      end
    else
      [Ef1,Ef2,Covf] = pred_coxph(gp,x,y,xt, options);
      Ef = Ef2; Varf = diag(Covf(size(Ef1,1)+1:end,size(Ef1,1)+1:end));
    end

    indd = zeros(size(Ef));

    for i2=1:n
      for i3=1:numel(XT1)
        if sqrt(sum((xto(i2,ind)-xt(i3,ind)).^2)) < tr
          indd(i3) = 1;
        end
      end
    end

    XT1(indd==0) = NaN; XT2(indd==0) = NaN; Ef(indd==0) = NaN; Varf(indd==0) = NaN;

    if isequal(plot_results, 'on')
      xtnn1=denormdata(xtnn1,nd.xmean(ind(1)),nd.xstd(ind(1)));
      xtnn2=denormdata(xtnn2,nd.xmean(ind(2)),nd.xstd(ind(2)));
      surf(reshape(XT1,numel(xtnn2),numel(xtnn1)), reshape(XT2,numel(xtnn2),numel(xtnn1)), reshape(Ef,numel(xtnn2),numel(xtnn1)))
      view(2)
      axis tight
      shading flat
      colormap(mapcolor(Ef,repmat(nanmedian(Ef(:)),[1 2])))
      colorbar('EastOutside')
    end

    %xtnn = [XT1(indd==1), XT2(indd==1)]; Ef = Ef(indd==1); Varf = Varf(indd==1);
    xtnn = [XT1, XT2];
  end

else
  error('Only 1 or 2 covariates can be defined for predicting')
end

end


function [f,J,Q] = spm_fx_cmm(x,u,P,M)
% state equations for canonical neural-mass and mean-field models
% FORMAT [f,J,Q] = spm_fx_cmm(x,u,P,M)
%
% x - states and covariances
%
% x(i,j,k)        - k-th state of j-th population of i-th source
%                   i.e., running over sources, pop. and states
%
%   population: 1 - excitatory spiny stellate cells (input cells)
%               2 - superficial pyramidal cells     (forward output cells)
%               3 - inhibitory interneurons         (intrisic interneuons)
%               4 - deep pyramidal cells            (backward output cells)
%
%        state: 1 V  - voltage
%               2 gE - conductance (excitatory)
%               3 gI - conductance (inhibitory)
%
%--------------------------------------------------------------------------
% refs:
%
% Marreiros et al (2008) Population dynamics under the Laplace assumption
%
% See also:
%
% Friston KJ.
% The labile brain. I. Neuronal transients and nonlinear coupling. Philos
% Trans R Soc Lond B Biol Sci. 2000 Feb 29;355(1394):215-36.
%
% McCormick DA, Connors BW, Lighthall JW, Prince DA.
% Comparative electrophysiology of pyramidal and sparsely spiny stellate
% neurons of the neocortex. J Neurophysiol. 1985 Oct;54(4):782-806.
%
% Brunel N, Wang XJ.
% What determines the frequency of fast network oscillations with irregular
% neural discharges? I. Synaptic dynamics and excitation-inhibition
% balance. J Neurophysiol. 2003 Jul;90(1):415-30.
%
% Brunel N, Wang XJ.
% Effects of neuromodulation in a cortical network model of object working
% memory dominated by recurrent inhibition. J Comput Neurosci. 2001
% Jul-Aug;11(1):63-85.
%
%__________________________________________________________________________
% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging

% Karl Friston
% $Id: spm_fx_cmm.m 5369 2013-03-28 20:09:27Z karl $

% get dimensions and configure state variables
%--------------------------------------------------------------------------
ns   = size(M.x,1);                      % number of sources
np   = size(M.x,2);                      % number of populations per source
nk   = size(M.x,3);                      % number of states per population
x    = reshape(x,ns,np,nk);              % hidden states

% extrinsic connection strengths
%==========================================================================

% exponential transform to ensure positivity constraints
%--------------------------------------------------------------------------
A{1} = exp(P.A{1});                      % forward
A{2} = exp(P.A{2});                      % backward
C    = exp(P.C);                         % subcortical

% detect and reduce the strength of reciprocal (lateral) connections
%--------------------------------------------------------------------------
for i = 1:length(A)
    L    = (A{i} > exp(-8)) & (A{i}' > exp(-8));
    A{i} = A{i}./(1 + 8*L);
end

% intrinsic connection strengths
%==========================================================================

% condition specific effects
%--------------------------------------------------------------------------
G    = full(P.H);
if any(P.G)
    G(2,2,:) = squeeze(G(2,2,:)) + P.G;
end
G(2,2,:) = 0;
G    = exp(G);

% connectivity switches
%==========================================================================
% 1 - excitatory spiny stellate cells (granular input cells)
% 2 - superficial pyramidal cells     (forward  output cells)
% 3 - inhibitory interneurons         (intrisic interneuons)
% 4 - deep pyramidal cells            (backward output cells)

% extrinsic connections (F B) - from superficial and deep pyramidal cells
%--------------------------------------------------------------------------
SA   = [1   0 ;
        0   1 ;
        0   2 ;
        0   0]/8;

% intrinsic connections (np x np) - excitatory
%--------------------------------------------------------------------------
GE   = [ 0    0    0    0
         4    0    0    0
         4    0    0    2
         0    4    0    0];

% intrinsic connections (np x np) - inhibitory
%--------------------------------------------------------------------------
GI   = [ 8    2    2    0
         0    8    2    0
         0    0   32    0
         0    0    8  128];

% rate constants (ns x np) (excitatory 4ms, inhibitory 16ms)
%--------------------------------------------------------------------------
KE   = 1000/4;                             % excitatory rate constants
KI   = exp(-P.T)*[1/64 1/32 1/16 1/16]*1000; % inhibitory rate constants

% Voltages
%--------------------------------------------------------------------------
VL   = -70;                               % reversal  potential leak (K)
VE   =  60;                               % reversal  potential excite (Na)
VI   = -90;                               % reversal  potential inhib (Cl)
VR   = -40;                               % threshold potential

CV   = exp(P.CV).*[16 16 32 16]/1000;     % membrane capacitance
GL   = 1;                                 % leak conductance

% mean-field effects:
%==========================================================================

% neural-mass approximation to covariance of states
%----------------------------------------------------------------------
Vx   = exp(P.S)*32;

% mean population firing and afferent extrinsic input
%--------------------------------------------------------------------------
m      = spm_Ncdf_jdw(x(:,:,1),VR,Vx);    % mean firing rate
a(:,1) = A{1}*m(:,2);                     % forward afference
a(:,2) = A{2}*m(:,4);                     % backward afference

% Averge background activity and exogenous input
%==========================================================================
BE     = exp(P.E)/2;

% input
%--------------------------------------------------------------------------
if isfield(M,'u')

    % endogenous input
    %----------------------------------------------------------------------
    U = u(:)*8/1000;

else

    % exogenous input
    %----------------------------------------------------------------------
    U = C*u(:)/1000;

end

% flow over every (ns x np) subpopulation
%==========================================================================
f     = x;
for i = 1:ns

        % intrinsic coupling
        %------------------------------------------------------------------
        E = (G(:,:,i).*GE)*m(i,:)';
        I = (G(:,:,i).*GI)*m(i,:)';

        % extrinsic coupling (excitatory only) and background activity
        %------------------------------------------------------------------
        E = E + BE + SA*a(i,:)';

        % and exogenous input(U)
        %------------------------------------------------------------------
        E(1) = E(1) + U(i);

        % Voltage
        %==================================================================
        f(i,:,1) =          (GL*(VL - x(i,:,1)) + ...
                      x(i,:,2).*(VE - x(i,:,1)) + ...
                      x(i,:,3).*(VI - x(i,:,1)) )./CV;

        % Conductance
        %==================================================================
        f(i,:,2) = (E' - x(i,:,2)).*KE;
        f(i,:,3) = (I' - x(i,:,3)).*KI(i,:);

end

% vectorise equations of motion
%==========================================================================
f = spm_vec(f);

if nargout < 2, return, end

% Jacobian
%==========================================================================
J = spm_cat(spm_diff(M.f,x,u,P,M,1));

if nargout < 3, return, end

% Delays
%==========================================================================
% Delay differential equations can be integrated efficiently (but
% approximately) by absorbing the delay operator into the Jacobian
%
%    dx(t)/dt     = f(x(t - d))
%                 = Q(d)f(x(t))
%
%    J(d)         = Q(d)df/dx
%--------------------------------------------------------------------------
% [specified] fixed parameters
%--------------------------------------------------------------------------
D  = [2 16];

d  = -D.*exp(P.D)/1000;
Sp = kron(ones(nk,nk),kron( eye(np,np),eye(ns,ns)));  % states: same pop.
Ss = kron(ones(nk,nk),kron(ones(np,np),eye(ns,ns)));  % states: same source

Dp = ~Ss;                            % states: different sources
Ds = ~Sp & Ss;                       % states: same source different pop.
D  = d(2)*Dp + d(1)*Ds;

% Implement: dx(t)/dt = f(x(t - d)) = inv(1 - D.*dfdx)*f(x(t))
%                     = Q*f = Q*J*x(t)
%--------------------------------------------------------------------------
Q  = spm_inv(speye(length(J)) - D.*J);