classdef SeqObsModel_Gaussian < SeqObsModel
    % SeqObsModel : defines Gaussian emission model for observed data in HMM
    %   Allows for multivariate observations of any dimension.
    %   Assumes conjugate prior on mean and covariance (Normal-Inverse Wishart).
    %   Extends the base object "SeqObsModel", filling in Gaussian details
    methods
        % ====================================================== CONSTRUCTOR
        function obj = SeqObsModel_Gaussian( K, D)
            obj.K = K;
            obj.D = D;
            obj.theta = repmat( struct('mu',zeros(D,1),'invSigma', zeros(D,D) ), 1, K );
        end

        % setPrior() sets prior on emission parameters to specified values.
        % USAGE:
        %   To set to default values given data object and model params
        %     ObsM = ObsM.setPrior( data, modelParamStruct )
        %     where modelParamStruct has attributes:
        %        degFree : # deg of freedom
        %        precMu  : precision parameter for mean
        %        Scoef   : scalar multiplier for Scale Matrix
        %        doEmpCovScalePrior: boolean.
        %               true  = set ScaleMat to Scoef*emp covariance
        %               false = set ScaleMat to Scoef*eye(D)
        %  To initialize directly to given parameters
        %      ObsM = ObsM.setPrior( mu, precMu, degFree, ScaleMat )
        function obj = setPrior( obj, varargin )
            obj.priorDef = struct();
            obj.priorDef.doEmpCovScalePrior =0;
            obj.priorDef.Scoef = 0;
            if strfind( class(varargin{1} ), 'SeqData' )
                data = varargin{1};
                obsM = varargin{2};
                obj.prior.mu = zeros( data.D, 1 );
                obj.prior.precMu = obsM.precMu;
                obj.prior.degFree = max( obsM.degFree, data.D+2 );
                obj.priorDef.doEmpCovScalePrior = obsM.doEmpCovScalePrior;
                obj.priorDef.Scoef = obsM.Scoef;
                if obsM.doEmpCovScalePrior
                    obj.prior.ScaleMat = obsM.Scoef * cov( data.Xdata', 1);
                else
                    obj.prior.ScaleMat = obsM.Scoef * eye( data.D );
                end
            elseif length( varargin ) == 1
                PStruct = varargin{1};
                obj.prior.mu = PStruct.Mu(:);
                obj.prior.precMu = PStruct.precMu;
                obj.prior.degFree = PStruct.degFree;
                obj.prior.ScaleMat = PStruct.ScaleMat;
            else
                obj.prior.mu = varargin{1}(:);
                obj.prior.precMu = varargin{2};
                obj.prior.degFree = varargin{3};
                obj.prior.ScaleMat = varargin{4};
            end
        end

        % ==================================================== GET Xstats
        function Xstats = getXSuffStats( obj, Xkk )
            Xstats.nObs = size(Xkk,2);
            if Xstats.nObs > 0
                Xstats.Xsum = sum( Xkk, 2);
                Xstats.XXsum = Xkk*Xkk';
            else
                Xstats.Xsum = [];
                Xstats.XXsum = [];
            end
        end

        function obj = incXStats( obj, ii, data, stateSeq, featIDs )
            for kk = featIDs
                Xkk = data.seq(ii);
                Xkk = Xkk(:, stateSeq(ii).z == kk );
                nNew = size(Xkk,2);
                if length(obj.Xstats)<kk || obj.Xstats(kk).nObs == 0
                    obj.Xstats(kk).nObs = nNew;
                    obj.Xstats(kk).Xsum = sum(Xkk,2);
                    obj.Xstats(kk).XXsum = Xkk*Xkk';
                else
                    obj.Xstats(kk).nObs = obj.Xstats(kk).nObs + nNew;
                    obj.Xstats(kk).Xsum = obj.Xstats(kk).Xsum + sum(Xkk,2);
                    obj.Xstats(kk).XXsum = obj.Xstats(kk).XXsum + Xkk*Xkk';
                end
            end
        end

        function obj = decXStats( obj, ii, data, stateSeq, featIDs )
            for kk = featIDs
                Xkk = data.seq(ii);
                Xkk = Xkk(:, stateSeq(ii).z == kk );
                nNew = size(Xkk,2);
                if obj.Xstats(kk).nObs-nNew <= 0
                    obj.Xstats(kk).nObs=0;
                    obj.Xstats(kk).Xsum=[];
                    obj.Xstats(kk).XXsum=[];
                else
                    obj.Xstats(kk).nObs = obj.Xstats(kk).nObs - nNew;
                    obj.Xstats(kk).Xsum = obj.Xstats(kk).Xsum - sum(Xkk,2);
                    obj.Xstats(kk).XXsum = obj.Xstats(kk).XXsum - Xkk*Xkk';
                end
            end
        end

        % ==================================================== GET params
        function retStr = getParamDescr( obj, PP )
            if ~exist('PP','var')
                PP = obj.prior;
            end
            retStr = 'Norm-InvWish. Mu0=0, dFree=%d,';
            if obj.priorDef.doEmpCovScalePrior
                retStr = [retStr ' S0=%.2f*EmpCov.'];
            else
                retStr = [retStr ' S0=%.2f*eye.'];
            end
            retStr = sprintf( retStr, obj.prior.degFree, obj.priorDef.Scoef );
        end

        function PP = getPosteriorParams( obj, Xstats )
            if Xstats.nObs > 0
                N = Xstats.nObs;
                degFreeN = N + obj.prior.degFree;
                precMuN  = N + obj.prior.precMu;
                Xdiff = Xstats.Xsum/N - obj.prior.mu;
                XdiffMat = ( obj.prior.precMu * N  )/( obj.prior.precMu + N ) .* ( Xdiff*Xdiff' );
                CovMat = (Xstats.XXsum - Xstats.Xsum*Xstats.Xsum'/N);
                ScaleMatN = obj.prior.ScaleMat + CovMat + XdiffMat;
                MuN = (Xstats.Xsum + obj.prior.mu*obj.prior.precMu)/ ( precMuN );

                PP.mu = MuN;
                PP.precMu = precMuN;
                PP.degFree = degFreeN;
                PP.ScaleMat = ScaleMatN;
            else
                PP = obj.prior;
            end
        end

        % ==================================================== GET theta mean

        % Retrieve the expected value of theta under the given params
        % In Gaussian case,
        %   mu = PP.mu
        %   Sigma = PP.ScaleMat / (degFree-D-1 )
        function [theta] = getTheta_Mean( obj, PP )
            if ~exist('PP','var')
                PP = obj.prior;
            end
            theta.mu  = PP.mu;
            theta.invSigma = (PP.degFree-obj.D-1)*( PP.ScaleMat \ eye(obj.D) );
        end

        % ==================================================== GET theta samps
        function [theta, PP] = sampleTheta_FromParams( obj, PP )
            if ~exist('PP','var')
                PP = obj.prior;
            end
            [~, sqrtInvSigma] = randiwishart( PP.ScaleMat, PP.degFree );
            theta.mu = ( sqrt( PP.precMu ) .* sqrtInvSigma ) \randn(obj.D,1)  + PP.mu;
            theta.invSigma = sqrtInvSigma'*sqrtInvSigma;
        end

        function [theta, PN] = sampleTheta( obj, Xkk )
            if ~exist( 'Xkk', 'var' )
                Xkk = [];
            end
            Xstats = obj.getXSuffStats( Xkk );
            PN = obj.getPosteriorParams( Xstats );
            [theta] = obj.sampleTheta_FromParams( PN );
        end

        % ==================================================== GET log probs

        % Calc probability of a realization of a emission parameter "theta"
        %   given the provided prior parameter struct "PP"
        function logPr = calcLogPrTheta( obj, theta, PP )
            if ~exist('PP','var')
                PP = obj.prior;
            end
            logPr = calcLogPrNormalInvWishart( theta.mu, theta.invSigma, PP );
        end

        % Calculate soft evidence for particular sequence "ii"
        %   given all available emission parameters (indicated by "kIDs")
        %OUTPUT
        %  logSoftEv : K x T matrix, where entry k,t gives log probability
        %                of the t-th observation under theta(kk)
        function logSoftEv = calcLogSoftEv(obj, ii, data, kIDs )
            if ~exist( 'kIDs', 'var' )
                kIDs = 1:obj.K;
            end
            Xseq = data.seq(ii);
            T = size( Xseq,2);
            logSoftEv = -inf( obj.K, T );
            for kk = kIDs
                cholInvSigma = chol( obj.theta(kk).invSigma );
                logDetInvSigma = 2*sum( log( diag( cholInvSigma) ) );
                XdiffMu = bsxfun(@minus, Xseq, obj.theta(kk).mu );
                U = XdiffMu'*cholInvSigma';
                logSoftEv(kk,:) = 0.5*logDetInvSigma - 0.5*sum( U.^2,2);
            end
            logSoftEv = logSoftEv - 0.5*obj.D*log(2*pi);
        end

        % Calculate marginal probability of observed data
        %  given the state sequence assignments of every observation
        % USAGE:
        %  logPr = ThetaM.calcMargPrData()
        %    calculate using *stored* sufficient statistics in this object
        %  logPr = ThetaM.calcMargPrData( data, stateSeq )
        %    calculate using freshly computed suff stats
        function logPr = calcMargPrData(obj, data, stateSeq, ks )
            if ~exist( 'ks', 'var' )
                ks = 1:obj.K;
            end
            if exist( 'data', 'var' ) && ~isempty( data )
                obj = obj.updateAllXSuffStats(  horzcat(stateSeq(:).z), data, ks);
            end
            LOG_PI =  1.144729885849400;
            P0 = obj.prior;
            D = obj.D;
            logPr = zeros(1, obj.K);
            for kk = ks
                Nkk = obj.Xstats(kk).nObs;
                if Nkk > 0
                    PN = obj.getPosteriorParams( obj.Xstats(kk) );

                    logPr(kk) = ...
                        logMvGamma(0.5*PN.degFree,D) - logMvGamma(0.5*P0.degFree,D) ...
                        + 0.5*P0.degFree*log( det( P0.ScaleMat ) )   ...
                        - 0.5*PN.degFree*log( det( PN.ScaleMat ) ) ...
                        + 0.5*D*log( P0.precMu / PN.precMu );
                end
            end
            N = sum( [obj.Xstats(ks).nObs] );
            logPr = sum(logPr) - 0.5*N*D*LOG_PI;
        end

    end
end


function matrix = genMatCurlVIFE3D(pde,mesh,fem,meshI,femI,option)
%% Generate global matrices and load vector of FEM for 3D ellipic eq
%     -div(A grad u)  = f,    x\in \Omega
% INPUTS:
% pde --- given data function from equation, e.g.
%         pde.A --- diffusion coefficient
%         pde.f --- right hand side function
%         pde.gD --- Dirichlet boundary value function
%         pde.one --- constant function 1.
% mesh --- mesh structure.
% fem --- global degree of freedom of FEM
%
% OUTPUTS:
% matrix.S --- stiffness matrix (w/o boundary condition)
% matrix.A --- final FEM matrix (after boundary condition)
% matrix.rhsF --- load vector (w/o boundary condition)
% matrix.f --- final RHS matrix (after boundary condition)

% Last Modified: 08/07/2020 by Xu Zhang

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
g2ldofNint = femI.g2ldofNint;
node = meshI.node;
gdof = femI.gdof;
NEdof = size(gdof,1);

% %% 1. Stiffness Matrix
feEvalBas1 = @EvalNed1Bas3D;
feEvalBas2 = @EvalNed1Bas3D;

dof1 = 6; dof2 = 6; nloc = dof1*dof2;
ntID = find(mesh.tLoc > 0); ntN = length(ntID);

%% 1. Matrix on noninterface elements
MatInd = 1;
AN = fem.area(ntID);
gxN = fem.gx(ntID,:); gyN = fem.gy(ntID,:); gzN = fem.gz(ntID,:); gw = fem.gw;
XN = zeros(nloc*ntN, 1);

coefN = feval(pde.A,gxN,gyN,gzN);
Ibasx = cell(dof1,1); Ibasy = cell(dof1,1); Ibasz = cell(dof1,1);
Jbasx = cell(dof2,1); Jbasy = cell(dof2,1); Jbasz = cell(dof2,1);
t_e_orit = fem.t_e_orit(ntID,:);

for i = 1:dof1
    Ibasx{i} = feEvalBas1(fem.bas, ntID, gxN, gyN, gzN, i, MatInd, 1).*t_e_orit(:,i);
    Ibasy{i} = feEvalBas1(fem.bas, ntID, gxN, gyN, gzN, i, MatInd, 2).*t_e_orit(:,i);
    Ibasz{i} = feEvalBas1(fem.bas, ntID, gxN, gyN, gzN, i, MatInd, 3).*t_e_orit(:,i);
end
for j = 1:dof2
    Jbasx{j} = feEvalBas2(fem.bas, ntID, gxN, gyN, gzN, j, MatInd, 1).*t_e_orit(:,j);
    Jbasy{j} = feEvalBas2(fem.bas, ntID, gxN, gyN, gzN, j, MatInd, 2).*t_e_orit(:,j);
    Jbasz{j} = feEvalBas2(fem.bas, ntID, gxN, gyN, gzN, j, MatInd, 3).*t_e_orit(:,j);
end

IN = reshape(repmat(g2ldofNint(:,1:6),6,1),nloc*ntN,1);
JN = repmat(reshape(g2ldofNint(:,1:6),dof2*ntN,1),6,1);
ind = 0;
for i = 1:dof1
    for j = 1:dof2
        XN(ind+1:ind+ntN) = AN.*(sum(((Ibasx{i}.*(coefN.*Jbasx{j})).*gw'),2) + ...
            sum(((Ibasy{i}.*(coefN.*Jbasy{j})).*gw'),2) + ...
            sum(((Ibasz{i}.*(coefN.*Jbasz{j})).*gw'),2));
        ind = ind + ntN;
    end
end
ID = find(XN~=0);
SN = sparse(IN(ID),JN(ID),XN(ID),NEdof,NEdof);

%%%%%%

MatInd = 0;
AN = fem.area(ntID);
gxN = fem.gx(ntID,:); gyN = fem.gy(ntID,:); gzN = fem.gz(ntID,:); gw = fem.gw;
XN = zeros(nloc*ntN, 1);

coefN = feval(pde.B,gxN,gyN,gzN);
Ibasx = cell(dof1,1); Ibasy = cell(dof1,1); Ibasz = cell(dof1,1);
Jbasx = cell(dof2,1); Jbasy = cell(dof2,1); Jbasz = cell(dof2,1);

for i = 1:dof1
    Ibasx{i} = feEvalBas1(fem.bas, ntID, gxN, gyN, gzN, i, MatInd, 1).*t_e_orit(:,i);
    Ibasy{i} = feEvalBas1(fem.bas, ntID, gxN, gyN, gzN, i, MatInd, 2).*t_e_orit(:,i);
    Ibasz{i} = feEvalBas1(fem.bas, ntID, gxN, gyN, gzN, i, MatInd, 3).*t_e_orit(:,i);
end
for j = 1:dof2
    Jbasx{j} = feEvalBas2(fem.bas, ntID, gxN, gyN, gzN, j, MatInd, 1).*t_e_orit(:,j);
    Jbasy{j} = feEvalBas2(fem.bas, ntID, gxN, gyN, gzN, j, MatInd, 2).*t_e_orit(:,j);
    Jbasz{j} = feEvalBas2(fem.bas, ntID, gxN, gyN, gzN, j, MatInd, 3).*t_e_orit(:,j);
end

IN = reshape(repmat(g2ldofNint(:,1:6),6,1),nloc*ntN,1);
JN = repmat(reshape(g2ldofNint(:,1:6),dof2*ntN,1),6,1);
ind = 0;
for i = 1:dof1
    for j = 1:dof2
        XN(ind+1:ind+ntN) = AN.*(sum(((Ibasx{i}.*(coefN.*Jbasx{j})).*gw'),2) + ...
            sum(((Ibasy{i}.*(coefN.*Jbasy{j})).*gw'),2) + ...
            sum(((Ibasz{i}.*(coefN.*Jbasz{j})).*gw'),2));
        ind = ind + ntN;
    end
end
ID = find(XN~=0);
MN = sparse(IN(ID),JN(ID),XN(ID),NEdof,NEdof);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

dof = 6;  nloc = dof;
X = zeros(nloc*ntN, 1);

fN1 = feval(pde.f1,gxN,gyN,gzN);
fN2 = feval(pde.f2,gxN,gyN,gzN);
fN3 = feval(pde.f3,gxN,gyN,gzN);
ind = 0;
I = reshape(g2ldofNint(:,1:6),nloc*ntN,1);
for i = 1:dof
    ibas1 = feEvalBas1(fem.bas, ntID, gxN, gyN, gzN, i, 0, 1);
    ibas2 = feEvalBas1(fem.bas, ntID, gxN, gyN, gzN, i, 0, 2);
    ibas3 = feEvalBas1(fem.bas, ntID, gxN, gyN, gzN, i, 0, 3);
    X(ind+1:ind+ntN) = AN.*sum((ibas1.*fN1+ibas2.*fN2+ibas3.*fN3).*gw',2).*t_e_orit(:,i);
    ind = ind + ntN;
end
rhsN = sparse(I,1,X,NEdof,1);

if isfield(option,'HM')
    if option.HM == 1
        Atotal = SN - pde.kappa*MN + femI.KI - pde.kappa*femI.SI;
    end
else
    Atotal = SN + MN + femI.KI + femI.SI;
end

rhs = rhsN + femI.b;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %% 3. Dirichlet Boundary Conditions

% non-interface edges
vSign = meshI.vSign;
eSign = sum(vSign(femI.gdof),2)/2;
eIdm = find(eSign==-1/2); eIdp = find(eSign==1/2);
[gew,gex,gey,gez] = gaussPedge(node(gdof(:,1),:),node(gdof(:,2),:),2);
% tu1 = sum(feval(pde.exactu1,gex,gey,gez).*gew,2);
% tu2 = sum(feval(pde.exactu2,gex,gey,gez).*gew,2);
% tu3 = sum(feval(pde.exactu3,gex,gey,gez).*gew,2);
tu1 = sum(feval(pde.exactu1,gex,gey,gez).*gew,2);
tu2 = sum(feval(pde.exactu2,gex,gey,gez).*gew,2);
tu3 = sum(feval(pde.exactu3,gex,gey,gez).*gew,2);
tu1(eIdm) = sum(feval(pde.um1,gex(eIdm,:),gey(eIdm,:),gez(eIdm,:)).*gew(eIdm,:),2);
tu1(eIdp) = sum(feval(pde.up1,gex(eIdp,:),gey(eIdp,:),gez(eIdp,:)).*gew(eIdp,:),2);
tu2(eIdm) = sum(feval(pde.um2,gex(eIdm,:),gey(eIdm,:),gez(eIdm,:)).*gew(eIdm,:),2);
tu2(eIdp) = sum(feval(pde.up2,gex(eIdp,:),gey(eIdp,:),gez(eIdp,:)).*gew(eIdp,:),2);
tu3(eIdm) = sum(feval(pde.um3,gex(eIdm,:),gey(eIdm,:),gez(eIdm,:)).*gew(eIdm,:),2);
tu3(eIdp) = sum(feval(pde.up3,gex(eIdp,:),gey(eIdp,:),gez(eIdp,:)).*gew(eIdp,:),2);
tgt = node(gdof(:,2),:) - node(gdof(:,1),:);
tgt = tgt./sum(tgt.^2,2).^(1/2);
tu = tu1.*tgt(:,1) + tu2.*tgt(:,2) + tu3.*tgt(:,3);

bcind = fem.bcind;
[bc,mapper] = boundaryEdge3D(node,gdof,bcind);
bdidx = zeros(NEdof,1);
isBdEdge = true(NEdof,1);
isBdEdge(mapper) = false;
bdidx(isBdEdge) = 1;
Tbd = spdiags(bdidx,0,NEdof,NEdof);
T = spdiags(1-bdidx,0,NEdof,NEdof);
A = T*Atotal*T + Tbd;

ub = tu;
ub(mapper) = 0;
rhsB = Atotal*ub;
f = rhs - rhsB;
f(isBdEdge) = tu(isBdEdge);

%% Outputs
matrix = struct('A', A, 'f', f,'tu',tu,'isBdEdge',isBdEdge);


% creates cell masks for fluorescence computation
% also creates exclusion mask for neuropil (cellPix)
function [stat, centerMasks, surroundMasks, neuropMasks] = ...
    createCenterSurroundMasks2(ops, stat, Ny, Nx, allow_overlap, radius0)

if nargin > 4
    aov = allow_overlap;
else
    aov = 0;
end

if nargin > 4
    radius = radius0;
else
    radius = 3;
end

Nk = length(stat);
cellPix = zeros(Ny, Nx);
centerMasks = zeros(Nk, Ny, Nx, 'single');
surroundMasks = zeros(Nk, Ny, Nx, 'single');

for k = 1:Nk
    % only use non-overlapping pixels of cell
    temp = zeros(Ny, Nx);
    if aov
        ipix = stat(k).ipix(stat(k).isoverlap==0);
        ypix = stat(k).ypix(stat(k).isoverlap==0);
        xpix = stat(k).xpix(stat(k).isoverlap==0);
        lam  = stat(k).lam(stat(k).isoverlap==0);
    else
        ipix = stat(k).ipix;
        ypix = stat(k).ypix;
        xpix = stat(k).xpix;
        lam  = stat(k).lam;
    end

    % fit MV gaussian to cell mask
    % define cell as all pixels within 2 std's of lambda
    if ~isempty(ypix)
        params      = FitMVGaus(ypix, xpix, lam, 2);
        % cell radius
        stat(k).radius = sqrt(mean(params.eval));

        % add masks for cells with radius greater than
        temp        = zeros(Ny, Nx);
        tempS       = zeros(Ny, Nx);
        if stat(k).radius >= radius
            pdist = ((stat(k).ypix - stat(k).med(1)).^2 + ...
                (stat(k).xpix - stat(k).med(2)).^2);
            temp(stat(k).ipix( pdist   < (stat(k).radius*.7)^2)) = 1;
            tempS(stat(k).ipix(pdist >= (stat(k).radius*.7)^2)) = 1;
        end

        centerMasks(k, :, :)      = temp / sum(temp(:));
        surroundMasks(k, :, :)    = tempS / sum(tempS(:));
    else
        stat(k).radius = 0;
    end

    % use all pixels for neuropil mask computation
    temp        = zeros(Ny, Nx);
    ipix = stat(k).ipix;
    lam  = stat(k).lam;
    temp(ipix)  = lam;

    % input thresholded pixels to cellPix to exclude in neuropil computation
    cellPix = cellPix + (temp > 0);
end

% create surround neuropil masks
[~, neuropMasks] = createNeuropilMasks(ops, stat, cellPix);


function [imageCha, ssim_map, metrics] = algo_FCSA_WaTMRI_2D_real( obj,input )
% 2D variant of WaTMRI
% based on Huang et al. paper on WaTMRI
%
% input:
% obj           CS reconstruction object (holding all parameters)
% input         struct containing recon parameters and image
%
% output:
% imageCha      reconstructed channel individual image
% ssim_map      structural similarity map
% metrics       evaluation metrics
%
% (c) Marc Fischer, Thomas Kuestner, May 2015
% -------------------------------------------------------------------------

%%
timer_WaT = tic;

%% variables:
% internal flags
flag_wavetree = true;
flag_fast = true;
flag_extendImage = true;

% internal variables:
mue = 0.001;
L = 1;
t_old = 1;
NLTV_struct.kernelratio = 3;
NLTV_struct.windowratio = 6;
NLTV_struct.nThreads = 1;  % mind this option if used with -singleCompThread on BWCluster
itrNLTV = obj.iNINNER - 5;
% chambolle tv:
parsin.MAXITER=100; parsin.tv='iso'; % 'iso' or 'l1'

% from obj:
maxitr = obj.iNINNER;
% n1 = obj.measPara.dim(1);
% n2 = obj.measPara.dim(2);
n1 = input.n1;
n2 = input.n2;
% nSlices = obj.measPara.dim(3);
nCha = obj.measPara.dim(5);
lambdaWave = obj.lambda;
lambdaTV = obj.lambdaTV;
lambdaGroup = obj.lambdaGroup;
NLTV_struct.filterstrength = obj.lambdaNLTV_h; % 0.03 % converted from NLTV h = 0.01 %old: used: 3e-10
lambdaNLTV = obj.lambdaNLTV;
lambdaNLTV_h = obj.lambdaNLTV_h;
regularizerWeights = obj.regularizerWeights;
flagTV = obj.flagTV;
flagTV_iso = obj.flagTV_iso;
flagWave = obj.flagWave;
flagGroup = obj.flagGroup;
flagNLTV = obj.flagNLTV;
flagSBNLTV = obj.flagSBNLTV;
waveletStages = obj.trafo.waveletStages;
waveletFilterName_l1 = obj.trafo.waveletFilterName_l1;
waveletFilterName_l12 = obj.trafo.waveletFilterName_l12;

% from input:
b=input.b;
mask = input.mask;
G_wat = input.G_wat;
Gt_wat = input.Gt_wat;
% groupnorm_index = input.groupnorm_index;
waveS_l1 = input.waveS_l1;
waveS_l12 = input.waveS_l12;
waveS_WaT = input.waveS_WaT;
WaT_extend_y = waveS_WaT(waveletStages+2,1) - waveS_l12(waveletStages+2,1);
WaT_extend_x = waveS_WaT(waveletStages+2,2) - waveS_l12(waveletStages+2,2);
z_WaT = cell(1,nCha);

% im_ref = input.im_ref;
% im_ref_full = zeros(n1,n2);
% for j = 1:nCha
%     im_ref_full = im_ref_full + abs(im_ref{1,j}).^2;
% end;
% im_ref_full = sqrt(im_ref_full);
clear input

% initialize cells/vectors
for j=1:nCha
    FTb{1,j} = real(iFFT2D(b{1,j}));
    % y{1,j} = real(FTb{1,j});
    % y{1,j+nCha} = imag(FTb{1,j});
end;
z = FTb; % starting point
y = z;

x_wave = cell(1,nCha);
x_helper = x_wave;
x_wave_helper = x_wave;
x_tv = x_wave;
x_nltv = x_wave;
for j=1:nCha
    x_nltv{1,j} = 0;
end;
x_g = x_wave;
x_g_helper = x_wave;
x_g_WaT = x_wave;

%% MAD dependent lambdas:
flag_MAD = true;
if flag_MAD
    x_wavedec = cell(1,nCha);
    threshold = zeros(1:nCha);
    for j=1:nCha % 2*nCha
        x_wavedec{1,j} = wavedec2(z{1,j},waveletStages,waveletFilterName_l1); % atm only based on l1-daubechie
        x_wave_fine_scale = size(x_wavedec{1,j},2) - (3*waveS_l1(waveletStages+1,1)*waveS_l1(waveletStages+1,2));
        threshold(j) = mad(x_wavedec{1,j}(x_wave_fine_scale:end),1);
    end;
    clear x_wavedec
else
    threshold(1:nCha) = 1;
end;

threshold_wave(j) = lambdaWave * threshold(j) * 2/L;
threshold_TV(j) = lambdaTV * threshold(j) * 2/L;
threshold_group(j) = lambdaGroup * threshold(j) * 2/L;
threshold_NLTV(j) = lambdaNLTV; % * threshold(j) * 2/L;
threshold_NLTV_h(j) = lambdaNLTV_h; %*threshold(j); % adjust carefully or NLTV won't find a solution. lambdaNLTV_h should be < 0.01

for j = 1:nCha
    x_max(j) = max(max(z{1,1}))*1.0;
    x_min(j) = min(min(z{1,1}))*1.0;
end;

%% initialize metrics:
    itr = 0;
   	metrics.xtime(itr+1)= 0;
%     [metrics, ssim_map{1,1}] = get_metrics_itr( im_ref, im_ref_full, z, itr, maxitr, nCha, n1, n2, metrics, obj.K_1, obj.K_2, obj.W_size, obj.W_sigma );
    ssim_map = [];

%% recon
dispProgress('Proximal Average', 0, maxitr);
for itr = 1:maxitr  % total iter counter

    t_new = (1+sqrt(1+4*t_old^2))/2;
    t_old = t_new;

    y_old = z;   % y_old = y for complex case

%% WaTMRI-Step
    if itr > 1
        if flagGroup
            for j = 1:nCha
                if flag_extendImage
                    z_WaT{1,j} = extend_image(y{1,j}, waveS_WaT, waveletStages, WaT_extend_y, WaT_extend_x);
                    x_g_helper{1,j} = wavedec2(z_WaT{1,j},waveletStages,waveletFilterName_l12);
                else
                    x_g_helper{1,j} = wavedec2(z{1,j},waveletStages,waveletFilterName_l12);
                end;

                if flag_wavetree
                    x_g_helper{2,j} = (G_wat*x_g_helper{1,j}')';
                else
                    x_g_helper{2,j} = zeros(1,size(x_g_helper{1,j},2));
                end;
            end;

                x_g_helper = softthresh_group_2vec(x_g_helper,threshold_group(j),nCha); % threshold_wave(j)/threshold_group(j) acc. to author

            for j = 1:nCha
                x_g_WaT{1,j} = waverec2(x_g_helper{1,j}+(Gt_wat*x_g_helper{2,j}')',waveS_WaT,waveletFilterName_l12);
                x_g{1,j} =  x_g_WaT{1,j}(1:end-WaT_extend_y,1:end-WaT_extend_x);
            end;
        end;
    else
        for j = 1:nCha
            x_g{1,j} = zeros(n1,n2);
            z_WaT{1,j} = zeros(n1,n2);
        end;
    end;

%% landweber step + WaTMRI-part
    for j = 1:nCha
        x_helper{1,j} = real(iFFT2D(FFT2D_mask(z{1,j},mask))) -FTb{1,j} ;
        if itr > 1  % Phi'*G'*G*Phi*z{1,j}
            z_WaT{1,j} = extend_image(z{1,j}, waveS_WaT, waveletStages, WaT_extend_y, WaT_extend_x);
            z_WaT{1,j} = trafo_WaT(z_WaT{1,j},waveletStages,waveletFilterName_l12,G_wat,Gt_wat);
            z_WaT{1,j} = z_WaT{1,j}(1:end-WaT_extend_y,1:end-WaT_extend_x);
        end;
        z{1,j} = z{1,j} - (x_helper{1,j}  + mue*(z_WaT{1,j} - x_g{1,j}))/L; % mue used as tradeoff parameter.
    end;

%% l1-Wavelet
    if flagWave
            for j = 1:nCha % 2*nCha
                    x_wave_helper{1,j} = wavedec2(z{1,j},waveletStages,waveletFilterName_l1);
                    x_wave_helper{1,j} = softthresh_real(x_wave_helper{1,j},threshold_wave(j));
                    x_wave{1,j} = waverec2(x_wave_helper{1,j},waveS_l1,waveletFilterName_l1);
            end;
    end;

%% TV
    if flagTV
        if ~flagTV_iso
            for j = 1:nCha
                    x_tv{1,j} = MTV_2D(z{1,j},threshold_TV(j),n1,n2);
            end;
        else
            for j = 1:nCha
                if (itr==1)
                    [x_tv{1,j}, P]=denoise_TV_One((z{1,j}), threshold_TV(j),-inf,inf,[],parsin);
                else
                    [x_tv{1,j}, P]=denoise_TV_One((z{1,j}), threshold_TV(j),-inf,inf,P,parsin);
                end;
            end;
        end;
    end;

%% NLTV
    if flagNLTV
        if itr >= itrNLTV
            if flagSBNLTV
                for j = 1:nCha
                    x_nltv{1,j} = SB_NLTVfunc_slim_rescale(z{1,j},n1,n2, threshold_NLTV(j), threshold_NLTV_h(j) );
                end;
            else
                for j = 1:nCha
                    % if mod(itr,5) == 0 || itr == 1
                        x_nltv{1,j} = NLMF(z{1,j},NLTV_struct);
                        x_nltv{1,j} = (L.*z{1,j} + 2*threshold_NLTV(j)*x_nltv{1,j})./(L+2*threshold_NLTV(j));
                    % end;
                end;
            end;
        else
            x_nltv{1,j} = 0;
       end;
    end;

%% add prox(.)
    for j = 1:nCha
        y{1,j} = zeros(n1,n2);
        if flagWave y{1,j} = y{1,j} + x_wave{1,j}.*regularizerWeights(1); end;
        if flagTV y{1,j} = y{1,j} + x_tv{1,j}.*regularizerWeights(2); end;
        % if flagGroup y{1,j} = y{1,j} + x_g{1,j}.*regularizerWeights(3); end;
        if flagNLTV y{1,j} = y{1,j} + x_nltv{1,j}.*regularizerWeights(4); end;

        if ~flagWave && ~flagTV && ~flagNLTV
            y{1,j} = z{1,j};
            y{1,j}(y{1,j} > x_max(j)) = x_max;
            y{1,j}(y{1,j} < x_min(j)) = x_min;
        else
            if itr < itrNLTV
                y{1,j} = y{1,j}/(flagTV.*regularizerWeights(2) + flagWave.*regularizerWeights(1)); % flagGroup.*regularizerWeights(3));
            else
                y{1,j} = y{1,j}/(flagTV.*regularizerWeights(2) + flagNLTV.*regularizerWeights(4) + flagWave.*regularizerWeights(1)); % + flagGroup.*regularizerWeights(3));
            end;
        end;

        flag_fast = true;
        if flag_fast
            z{1,j}=y{1,j}+((t_old-1)/t_new).*(y{1,j}-y_old{1,j});
        end;
    end;

%% metrics of current itr:
%     disp(itr);
    dispProgress('Proximal Average', itr/maxitr);

   	metrics.xtime(itr+1)= toc(timer_WaT);
    for j = 1:nCha
        z{1,j} = z{1,j};
    end;
%     [metrics, ssim_map{1,2}] = get_metrics_itr( im_ref, im_ref_full, z, itr, maxitr, nCha, n1, n2, metrics, obj.K_1, obj.K_2, obj.W_size, obj.W_sigma );

end;
dispProgress('Proximal Average', 'Close');

    imageCha = z;
    for j = 1:nCha
        imageCha{1,j} = turn_image( imageCha{1,j} );
    end;
%     for j = 1:nCha+1
%         ssim_map{1,1}{1,j} = turn_image( ssim_map{1,1}{1,j} );
%         ssim_map{1,2}{1,j} = turn_image( ssim_map{1,2}{1,j} );
%     end;

end


%% This function will display the ODE function.

% Last Updated: 2019/05/08
% Coded By: K

function ODEs=Print_ODEs(ODE,n_state,n_control,disp,actual)

z_vars=sym('z',[n_state;1]);
u_vars=sym('u',[n_control;1]);
d_vars=sym('dz',[n_state;1]);

if isa(ODE,'function_handle')
    if n_control~=0
        ODEs=ODE(0,z_vars,u_vars);
    else
        ODEs=ODE(0,z_vars);
    end
end

if disp==1
    if actual==1
        fprintf('\v The actual ODE of the system is/are :\n')
    else
        fprintf('\v The discovered ODE of the system is/are :\n')
    end
     for i=1:n_state
         digits(4)
          fprintf(strcat('\t',char(d_vars(i,1)),'=',char(vpa(ODEs(i,1))),'\n'));
     end
end


function [g,a] = wpfbt2filterbank( wt, varargin)
%WPFBT2FILTERBANK  WPFBT equivalent non-iterated filterbank
%   Usage: [g,a] = wpfbt2filterbank(wt)
%
%   Input parameters:
%         wt : Wavelet filter tree definition
%
%   Output parameters:
%         g   : Cell array containing filters
%         a   : Vector of sub-/upsampling factors
%
%   `wpfbt2filterbank(wt)` calculates the impulse responses *g* and the
%   subsampling factors *a* of non-iterated filterbank, which is equivalent
%   to the wavelet packet filterbank tree described by *wt*. The returned
%   parameters can be used directly in |filterbank|, |ufilterbank| or
%   |filterbank|.
%
%   Please see help on |wfbt| for description of *wt*. The function
%   additionally support the following flags:
%
%   `'freq'`(default),`'nat'`
%      The filters are ordered to produce subbands in the same order as
%      |wpfbt| with the same flag.
%
%   `'intsqrt'`(default),`'intnoscale'`, `'intscale'`
%      The filters in the filterbank tree are scaled to reflect the
%      behavior of |wpfbt| and |iwpfbt| with the same flags.
%
%   `'scaling_notset'`(default),`'noscale'`,`'scale'`,`'sqrt'`
%     Support for scaling flags as described in |uwpfbt|. By default,
%     the returned filterbank *g* and *a* is equivalent to |wpfbt|,
%     passing any of the non-default flags results in a filterbank
%     equivalent to |uwpfbt| i.e. scaled and with `a(:)=1`.
%
%   Examples:
%   ---------
%
%   The following two examples create a multirate identity filterbank
%   using a tree of depth 3. In the first example, the filterbank is
%   identical to the DWT tree:::
%
%     [g,a] = wpfbt2filterbank({'db10',3,'dwt'});
%     filterbankfreqz(g,a,1024,'plot','linabs','posfreq');
%
%
%   In the second example, the filterbank is identical to the full
%   wavelet tree:::
%
%     [g,a] = wpfbt2filterbank({'db10',3,'full'});
%     filterbankfreqz(g,a,1024,'plot','linabs','posfreq');
%
%   See also: wfbtinit

% AUTHOR: Zdenek Prusa

complainif_notenoughargs(nargin,1,'WPFBT2FILTERBANK');

definput.import = {'wfbtcommon','uwfbtcommon'};
definput.importdefaults = {'scaling_notset'};
definput.flags.interscaling={'intsqrt','intnoscale','intscale'};
[flags]=ltfatarghelper({},definput,varargin);

% build the tree
wt = wfbtinit({'strict',wt},flags.forder);

wt = comp_wpfbtscale(wt,flags.interscaling);

nIdx = nodesLevelsBForder(wt);
% Now we need to walk the tree by levels
g = {};
a = [];
for ii=1:numel(nIdx)
    rangeLoc = cellfun(@(eEl) 1:numel(eEl.h),wt.nodes(nIdx{ii}),...
                       'UniformOutput',0);
    rangeOut = cellfun(@(eEl) numel(eEl.h),wt.nodes(nIdx{ii}));
    rangeOut = mat2cell(1:sum(rangeOut),1,rangeOut);
    [gtmp,atmp] = nodesMultid(nIdx{ii},rangeLoc,rangeOut,wt);
    g(end+1:end+numel(gtmp)) = gtmp;
    a(end+1:end+numel(atmp)) = atmp;
end
g = g(:);
a = a(:);

if ~flags.do_scaling_notset
   g = comp_filterbankscale(g,a,flags.scaling);
   a = ones(numel(g),1);
end

function nodesIdxs = nodesLevelsBForder(treeStruct)

%find root
nodeNo = find(treeStruct.parents==0);
toGoTrough = [nodeNo];
nodesIdxs = {nodeNo};
inLevel = [1];
counter = 0;
level = 2;
chIdxSum = 0;
while ~isempty(toGoTrough)
   chtmp = find(treeStruct.children{toGoTrough(1)}~=0);
   chIdxtmp = treeStruct.children{toGoTrough(1)}(chtmp);
   counter = counter + 1;

   if(length(nodesIdxs)<level&&~isempty(chIdxtmp))
       nodesIdxs = {nodesIdxs{:},[]};
   end

   chIdxSum = chIdxSum + length(chIdxtmp);
   if(~isempty(chIdxtmp))
       nodesIdxs{level} = [nodesIdxs{level},chIdxtmp];
   end

   toGoTrough = [toGoTrough(2:end),chIdxtmp];

   if(counter==inLevel(level-1))
       counter = 0;
       inLevel(level) = chIdxSum;
       level = level + 1;
       chIdxSum = 0;
   end
end


function calpak_test345 ( )

%*****************************************************************************80
%
%% CALPAK_TEST345 tests MONTH_LENGTH_GREEK.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    28 March 2013
%
%  Author:
%
%    John Burkardt
%
  n_test = 2;

  fprintf ( 1, '\n' );
  fprintf ( 1, 'CALPAK_TEST345\n' );
  fprintf ( 1, '  For the Greek calendar,\n' );
  fprintf ( 1, '  MONTH_LENGTH_GREEK returns month lengths.\n' );

  y_test(1) = 3;
  y_test(2) = 4;

  for i_test = 1 : n_test

    y = y_test(i_test);
    sy = y_to_s_greek ( y );
    months = year_length_months_greek ( y );
    days = year_length_greek ( y );

    fprintf ( 1, '\n' );
    fprintf ( 1, '  %d\n', y );
    fprintf ( 1, '  %s\n', sy );
    fprintf ( 1, '  Year length in months = %d\n', months );
    fprintf ( 1, '  Year length in days = %d\n', days );
    fprintf ( 1, '\n' );

    for m = 1 : months
      month_name = month_to_month_name_greek ( y, m );
      fprintf ( 1, '  %12s  %2d\n', month_name, month_length_greek ( y, m ) );
    end

  end

  return
end


function v = thickVehicle(size)

%THICKVEHICLE Create a vehicle graphics 3D object.
%
%   THICKVEHICLE creates a thick triangle representing a vehicle
%   This triangle is heading towards vehicle principal axis
%   and is located at vehicle position. Initial configuration
%   is: position at origin and principal axis aligned with
%   world x axis.
%
%   THICKVEHICLE(SIZE) allows for choosing a vehicle size.
%   Default is 0.5

%   Copyright 2008-2009 Joan Sola @ LAAS-CNRS.

if nargin == 0
    size = 1;
end

v.vert0 = [ 1.0  0.0 0.0
           -0.5  0.5 0.0
           -0.5 -0.5 0.0
            1.0  0.0 0.5
           -0.5  0.5 0.5
           -0.5 -0.5 0.5] * size/1.5;

v.vert = v.vert0;
v.faces = [1 2 3 1
           4 5 6 4
           1 2 5 4
           1 3 6 4
           2 3 6 5];

% ========== End of function - Start GPL license ==========

%   # START GPL LICENSE

%---------------------------------------------------------------------
%
%   This file is part of SLAMTB, a SLAM toolbox for Matlab.
%
%   SLAMTB is free software: you can redistribute it and/or modify
%   it under the terms of the GNU General Public License as published by
%   the Free Software Foundation, either version 3 of the License, or
%   (at your option) any later version.
%
%   SLAMTB is distributed in the hope that it will be useful,
%   but WITHOUT ANY WARRANTY; without even the implied warranty of
%   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%   GNU General Public License for more details.
%
%   You should have received a copy of the GNU General Public License
%   along with SLAMTB.  If not, see <http://www.gnu.org/licenses/>.
%
%---------------------------------------------------------------------

%   SLAMTB is Copyright:
%   Copyright (c) 2008-2010, Joan Sola @ LAAS-CNRS,
%   Copyright (c) 2010-2013, Joan Sola,
%   Copyright (c) 2014-2015, Joan Sola @ IRI-UPC-CSIC,
%   SLAMTB is Copyright 2009
%   by Joan Sola, Teresa Vidal-Calleja, David Marquez and Jean Marie Codol
%   @ LAAS-CNRS.
%   See on top of this file for its particular copyright.

%   # END GPL LICENSE


% Author: Ricardo Baptista and Matthias Poloczek
% Date:   June 2018
%
% See LICENSE.md for copyright information
%

function run_cases(inputs_all, lambda_vals, test_name, n_proc)
% RUN_CASES: Functions runs the discrete optimization algorithms
% on all test cases specified in inputs_all with the lambda values
% prescribed in lambda_vals vector. The results are saved in the
% results/test_name folder.

% Find number of tests
n_test = length(inputs_all);

%% Parallel Problem setup

parpool(n_proc);
parfor t=1:n_test

    % Set test inputs struct
    inputs_t = inputs_all{t};

    % Declare cells to store results
    rnd  = cell(length(lambda_vals));
    sa   = cell(length(lambda_vals));
    bo   = cell(length(lambda_vals));
    ols  = cell(length(lambda_vals));
    smc  = cell(length(lambda_vals));
    smac = cell(length(lambda_vals));

    bayes = struct;
    bayes.stSA1 = cell(length(lambda_vals));
    bayes.stSA2 = cell(length(lambda_vals));
    bayes.stSA3 = cell(length(lambda_vals));
    bayes.sdp   = cell(length(lambda_vals));

    mle = struct;
    mle.stSA1   = cell(length(lambda_vals));
    mle.stSA2   = cell(length(lambda_vals));
    mle.stSA3   = cell(length(lambda_vals));
    mle.sdp     = cell(length(lambda_vals));

    hs = struct;
    hs.stSA1    = cell(length(lambda_vals));
    hs.stSA2    = cell(length(lambda_vals));
    hs.stSA3    = cell(length(lambda_vals));
    hs.sdp      = cell(length(lambda_vals));

    %% Run optimization
    for l=1:length(lambda_vals)

        % Define objective function with penalty term
        inputs_t.lambda = lambda_vals(l);
        penalty   = @(x) inputs_t.lambda*inputs_t.reg_term(x);
        objective = @(x) inputs_t.model(x) + penalty(x);

        fprintf('--------------------------------------------\n')
        fprintf('Test = %d/%d, Lambda = %f\n\n', t, n_test, lambda_vals(l));

        % Run different ML optimization algorithms
        rnd{l} = random_samp(objective, inputs_t);
        fprintf('Random - Runtime: %f\n', sum(rnd{l}.runTime));

        sa{l}  = simulated_annealing(objective, inputs_t);
        fprintf('SA - Runtime = %f\n', sum(sa{l}.runTime));

        bo{l}  = bayes_opt(objective, inputs_t);
        fprintf('BO - Runtime = %f\n', sum(bo{l}.runTime));

        ols{l} = local_search(objective, inputs_t);
        fprintf('OLS - Runtime = %f\n', sum(ols{l}.runTime));

        smc{l} = binary_smc(objective, inputs_t);
        fprintf('SMC - Runtime = %f\n', sum(smc{l}.runTime));

        smac{l} = run_smac(objective, inputs_t);
        fprintf('SMAC - Runtime = %f\n', sum(smac{l}.runTime));

        % Run BOCS with Bayesian model
        inputs_t.estimator = 'bayes';

        bayes.stSA1{l} = BOCS(inputs_t.model, penalty, inputs_t, 1, 'SA');
        fprintf('Bayes.SA1 - Runtime = %f\n', sum(bayes.stSA1{l}.runTime));

        bayes.stSA2{l} = BOCS(inputs_t.model, penalty, inputs_t, 2, 'SA');
        fprintf('Bayes.SA2 - Runtime = %f\n', sum(bayes.stSA2{l}.runTime));

        bayes.stSA3{l} = BOCS(inputs_t.model, penalty, inputs_t, 3, 'SA');
        fprintf('Bayes.SA3 - Runtime = %f\n', sum(bayes.stSA3{l}.runTime));

        bayes.sdp{l} = BOCS(inputs_t.model, penalty, inputs_t, 2, 'sdp');
        fprintf('Bayes.SDP - Runtime = %f\n', sum(bayes.sdp{l}.runTime));

        % Run BOCS with MLE model
        inputs_t.estimator = 'mle';

        mle.stSA1{l} = BOCS(inputs_t.model, penalty, inputs_t, 1, 'SA');
        fprintf('MLE.SA1 - Runtime = %f\n', sum(mle.stSA1{l}.runTime));

        mle.stSA2{l} = BOCS(inputs_t.model, penalty, inputs_t, 2, 'SA');
        fprintf('MLE.SA2 - Runtime = %f\n', sum(mle.stSA2{l}.runTime));

        mle.stSA3{l} = BOCS(inputs_t.model, penalty, inputs_t, 3, 'SA');
        fprintf('MLE.SA3 - Runtime = %f\n', sum(mle.stSA3{l}.runTime));

        mle.sdp{l} = BOCS(inputs_t.model, penalty, inputs_t, 2, 'sdp');
        fprintf('MLE.SDP - Runtime = %f\n', sum(mle.sdp{l}.runTime));

        % Run BOCS with Horseshoe model
        inputs_t.estimator = 'horseshoe';

        hs.stSA1{l} = BOCS(inputs_t.model, penalty, inputs_t, 1, 'SA');
        fprintf('HS.SA1 - Runtime = %f\n', sum(hs.stSA1{l}.runTime));

        hs.stSA2{l} = BOCS(inputs_t.model, penalty, inputs_t, 2, 'SA');
        fprintf('HS.SA2 - Runtime = %f\n', sum(hs.stSA2{l}.runTime));

        hs.stSA3{l} = BOCS(inputs_t.model, penalty, inputs_t, 3, 'SA');
        fprintf('HS.SA3 - Runtime = %f\n', sum(hs.stSA3{l}.runTime));

        hs.sdp{l} = BOCS(inputs_t.model, penalty, inputs_t, 2, 'sdp');
        fprintf('HS.SDP - Runtime = %f\n', sum(hs.sdp{l}.runTime));

        % Save results
        iSave(sprintf(['../results/' test_name '/test%d.mat'], t), ...
            rnd, sa, bo, ols, smc, smac, bayes, mle, hs, inputs_t);

    end
end

delete(gcp('nocreate'))


function [qt]=matrix(qt, sz1, sz2)
%Converts a vector to a matrix in the QTT-Tucker format
%   [QT]=MATRIX(QT, SZ1, SZ2) Makes factors of QTT-Tucker to be TT-matrices
%   SZ1 and SZ2 are cell arrays with N, M dimensions for each factor
%   If they are absent, the square matrices are assumed
%
%
%
% TT-Toolbox 2.2, 2009-2012
%
%This is TT Toolbox, written by Ivan Oseledets et al.
%Institute of Numerical Mathematics, Moscow, Russia
%webpage: http://spring.inm.ras.ru/osel
%
%For all questions, bugs and suggestions please mail
%ivan.oseledets@gmail.com
%---------------------------

if (nargin<3)
    sz1 = [];
    sz2 = [];
end;

d = qt.dphys;
for i=1:d
    if (~isempty(sz1))||(~isempty(sz2))
        qt.tuck{i} = tt_matrix(qt.tuck{i}, sz1{i}, sz2{i});
    else
        cursz = qt.tuck{i}.n;
        cursz = sqrt(cursz);
        qt.tuck{i} = tt_matrix(qt.tuck{i}, cursz, cursz);
    end;
end;

end


function valid_dims = computeValidDimensions(data,options)
% Evaluate a matrix of connections S and return which elements are to be
% analysed for variance and standardisation.
S = options.S;
if ~isfield(options,'distribution') || strcmp(options.distribution,'Gaussian')
    if all(S(:)==1)
        valid_dims = [1:length(S)]; % note this omits first dimension to allow TUDA style use of intercepts
    else
        %implies a TUDA/TUCA style setup:
        lastdatadim = find(diff(any(S==1)),1);
        % check if intercept term present:
        if isstruct(data)
             X = data.X;
        else
             X = data;
        end
        if var(X(:,lastdatadim))==0
            lastdatadim = lastdatadim-1;
        end
        valid_dims = [1:lastdatadim];
    end
else
    valid_dims = [];
end

end


function gp_init_kron(covfun1, covfun2)

% low-level approach..?

cov1 = gp_cov_pp();
cov2 = gp_cov_noise_iso();
cov = gp_cov_sum(cov1,cov2);

gp_moments()

function [mu, Cov] = gp_moments(y,Ky,Kxy,Kx)

% mu = K_xy * (K_y \ y);
% Cov = K_x - K_xy * inv(K_y) * K_yx

if isnumeric(Kxy)
  Kxy = @(x) Kxy*x;
end
if isnumeric(Ky)
  Ky = @(x) linsolve_cov(Ky,x);
end

mu = Kxy( Ky( y ) );
if nargout >= 2
  if isvector(Kx)
    % Evaluate variances
    Cov = Kx -
  else
    % Evaluate covariance
    Kxy = Kxy(speye(size(Ky1)));
    Cov = Kx - Kxy
  end
end

end

% regular gp:

% set up covariance function
%
% hyperparameter stuff should be removed.. GP is always conditional on
% the given hyperparams?
cov1 = gp_cov_pp(pdf_gamma(2,3), pdf_gamma(0.5,0.5), 'jitter', 1e-6); % struct
gp1 = gp_init(cov1);
cov2 = gp_cov_noise_iso(pdf_gamma(1,1), 'jitter', 0); % struct
gp2 = gp_init(cov2);
% cov = gp_cov_sum_sparse(cov1, cov2);

gp_y = gp_sum(gp1,gp2); % ?

gp_y.set_data_inputs(x);

gp = gp_init(cov)

gp.set_data_inputs(x)

gp.set_hyperparameters(logtheta, ..)
sampler = gp.get_hyperparameter_sampler(..)

gp.get_posterior_moments(logtheta,..)
sampler = gp.get_posterior_sampler(logtheta, ..)
f = sampler(y, ..)
gp.get_hyperparameter_optimizer(..)

% or:
%gp = gp_init(prior_covariance, likelihood_covariance);

%%%%

gp.set_data_inputs(..)

gp.optimize_hyperparameters(..) % ?
gp.draw_hyperparameters(..) %

gp.evaluate_prior_covariance(hyperparameters)
gp.evaluate_likelihood_covariance(noise_covariance)
gp.draw_sample(y); % without data draws from prior

%
% CONSTRUCT THE GP
%

% Create covariance structs
cov1 = gp_init_cov_pp(); % struct
cov1.params.prior = pdf_product(pdf_gamma(2,3), pdf_gamma(0.5,0.5)); % struct
cov1.jitter = 1e-6;
% functions:
% cov1.data_inputs(x) - evaluates, e.g., distance matrix
% cov1.evaluate
% cov1.cholesky()
% cov1.linsolve()
% cov1.multiply()
cov2 = gp_init_cov_pp(); % struct
cov2.params.prior = pdf_product(pdf_gamma(2,3), pdf_gamma(0.5,0.5)); % struct
cov2.jitter = 1e-6;

% Create Kronecker product covariance struct
cov = gp_cov_kron(cov1, cov2);

% Also, there could be, e.g., mean function struct?

gp = gp_init(cov); % ????

%
% ASSIGN DATA
%

gp = gp_set_data_inputs(gp, {x1,x2}); % makes it possible to pre-evaluate,
                                      % e.g., distance matrix

gp_set_hyperparameters() % ??

gp_evaluate_prior_covariance(); % evaluate K and possibly L_K ?

gp_observations(Y,s^2);

% when to evaluate preconditioner?

%
% DO INFERENCE
%

gp_sample_cg(); %% ??

f = gp_sample();

% you could also have functions such as gp_mean, gp_variance for VB
% inference..?i

N1 = [];
N2 = [];

K1 = [];
K2 = [];

LD1 = [];
LD2 = [];

L1 = [];
L2 = [];

N1 = 500;
x1 = 1:N1;
K1 = gp_cov_pp(log(10), x1, x1);

N2 = 500;
x2 = 1:N2;
K2 = gp_cov_pp(log(10), x2, x2);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

  function set_parameters(logt1,logt2,x1,x2)
  logtheta1 = logt1;
  X1 = x1;
  N1 = size(x1,2);

  logtheta2 = logt2;
  X2 = x2;
  N2 = size(x2,2);

  evaluate_covariance();
  end

  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

  function rndfunc = get_posterior_sampling_function(Y,noise_std)

  s = noise_std;

  Afun = @(x) (kronprod(K1,K2,x,'vector')+s^2*x);
  Mfun = get_preconditioner();

  rndfunc = @() draw_sample(Y,Afun,Mfun);

  end

  function F = draw_sample()

  Z0 = randn(N1,N2);
  Z = randn(N1,N2);
  F0 = kronprod(L2,L1,Z0);
  G = F0 + s*Z - Y;
  X = reshape( pcg(Afun, G(:), 1e-4, 20, Mfun), N1, N2 );
  F = F0 - kronprod(K1,K2,X);

  end

  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

  function precondfun = get_preconditioner()
  LD_C1 = ldlchol(K1+s*speye(N1));
  LD_C2 = ldlchol(K1+s*speye(N1));
  precondfun = @(x) kronsolve_for_cg2(LD_C1, LD_C2, x);
  end

  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

  function evaluate_covariance()
  K1 = covfun1(logtheta1, X1, X1);
  LD1 = ldlchol(K1);
  [L,D] = ldlsplit(LD1);
  L1 = L*sqrt(D);

  K2 = covfun2(logtheta2, X2, X2);
  LD2 = ldlchol(K2);
  [L,D] = ldlsplit(LD2);
  L2 = L*sqrt(D);
  end

end


% Copyright 2018 Marc René Schädler
%
% This file is part of the mobile hearing aid prototype project
% The the mobile hearing aid prototype project is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
%
% The mobile hearing aid prototype project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License along with the mobile hearing aid prototype project. If not, see http://www.gnu.org/licenses/.

function [signal, fs] = gensweep(type, variable, frequency)
  fs = 48000; % Hz
  reference_level = 93.979; % dB SPL
  sweep_duration = 0.250; % s
  sweep_width = [0.95 1.05];
  flank_duration = 0.010; % s
  sweep_samples = round(fs.*sweep_duration);
  flank_samples = round(fs.*flank_duration);

  % Generate stimulus
  if type > 0
    signal = sinesweepphase(sweep_samples, sweep_width.*frequency./fs, rand(1).*2.*pi);
    signal = normalize(signal, variable - reference_level);
    signal = flank(signal, flank_samples);
  else
    signal = zeros(sweep_samples,1);
  end
end


%% Open-Loop Steering Simple
% This example simulates a simple vehicle with an open loop
% sinusoidal steering input.
%
% <<OpenLoopSteeringSimple.gif>>
%
%% Simulation models and parameters
% First, all classes of the package are imported with
%

clear ; close all ; clc

import VehicleDynamicsLateral.*

%% Tire and vehicle model
% Choosing tire and vehicle model. In this case, the parameters are defined
% by the user.
%

% Choosing tire
TireModel = TirePacejka();
% Choosing vehicle
System = VehicleSimpleNonlinear();

% Tire model and friction
System.tire = TireModel;
System.muy  = 1.0;

%% Simulation parameters
% Choosing simulation time span
%

T = 4;                      % Total simulation time [s]
resol = 50;                 % Resolution
TSPAN = 0:T/resol:T;        % Time span [s]

%% Open-loop steering input
% Steering angle
System.deltaf = 1*pi/180*sin(T^-1*2*pi*TSPAN);

%% Run simulation
% To define a simulation object (simulator) the arguments must be the
% vehicle object and the time span.

simulator = Simulator(System, TSPAN);

% Simulation
simulator.Simulate();

%% Results

% Retrieving states
XT      = simulator.XT;
YT      = simulator.YT;
PSI     = simulator.PSI;
VEL     = simulator.VEL;
ALPHAT  = simulator.ALPHAT;
dPSI    = simulator.dPSI;

figure(1)
plot(TSPAN,180/pi*System.deltaf)
xlabel('time [s]')
ylabel('Steering angle [deg]')

%%
% Frame and animation

g = Graphics(simulator);
g.TractorColor = 'r';

g.Frame();
g.Animation();
% g.Animation('html/OpenLoopSteeringSimple');       % Uncomment to save animation gif

%% See Also
%
% <../../../index.html Home> | <../../OpenLoopSteeringArticulated/html/OpenLoopSteeringArticulated.html Open-Loop Steering Articulated>
%


function lik = lik_coxph(varargin)
%LIK_COXPH    Create a Cox proportional hazard likelihood structure
%
%  Description
%    LIK = LIK_COXPH('PARAM1',VALUE1,'PARAM2,VALUE2,...)
%    creates a proportional hazard model where a piecewise log-constant
%    baseline hazard is assumed.
%
%    The likelihood contribution for the ith observation is
%
%      l_i = h_i(y_i)^(1-z_i)*[exp(-int_0^y_i*h_i dt)],
%
%    where hazard is h_i=h_0(y_i)*exp(f_i). A zero mean Gaussian process
%    prior is placed for f = [f_1, f_2,...,f_n] ~ N(0, C). C is the
%    covariance matrix, whose elements are given as C_ij = c(x_i, x_j |
%    th). The function c(x_i, x_j| th) is covariance function and th its
%    parameters, hyperparameters. We place a hyperprior for
%    hyperparameters, p(th).
%
%    The time axis is partioned into K intervals with equal lengths:
%    0 = s_0 < s_1 < ... < s_K, where s_K > y_i for all i. The baseline
%    hazard rate function h_0 is piecewise constant,
%
%      h_0(t) = la_k,
%
%    when t belongs to the interval (s_{k-1},s_k] and where ft_k=log(la_k).
%    The hazard rate function is smoothed by assuming another Gaussian
%    process prior ft = [ft_1, ft_2,...,ft_K] ~ N(0, C).
%
%    z is a vector of censoring indicators with z = 0 for uncensored event
%    and z = 1 for right censored event.
%
%    When using the Coxph likelihood you need to give the vector z
%    as an extra parameter to each function that requires also y.
%    For example, you should call gpla_e as follows: gpla_e(w, gp,
%    x, y, 'z', z)
%
%  See also
%    GP_SET, LIK_*, PRIOR_*
%

% Copyright (c) 2007-2010 Jarno Vanhatalo & Jouni Hartikainen
% Copyright (c) 2010 Aki Vehtari
% Copyright (c) 2011 Jaakko Riihimäki

% 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 = 'LIK_COXPH';
  ip.addOptional('lik', [], @isstruct);
  ip.addParamValue('S', linspace(0,1.001,50), @(x) isvector(x));
  ip.addParamValue('stratificationVariables', [], @(x) isvector(x) && all(rem(x,1)==0));
  ip.addParamValue('removeStratificationVariables', [], @(x) ismember(x, {'on' 'off'}));
  ip.parse(varargin{:});
  lik=ip.Results.lik;

  if isempty(lik)
    init=true;
    lik.type = 'Coxph';
    lik.nondiagW=true;
  else
    if ~isfield(lik,'type') || ~isequal(lik.type,'Coxph')
      error('First argument does not seem to be a valid likelihood function structure')
    end
    init=false;
  end

  if init || ~ismember('S', ip.UsingDefaults)
    s=ip.Results.S;
    xtmp=zeros(length(s)-1,1);
    for i2=1:(length(s)-1)
      xtmp(i2,1)=mean([s(i2) s(i2+1)]);
    end
    lik.xtime=xtmp;
    lik.stime=s;
  end

  if ~ismember('stratificationVariables', ip.UsingDefaults)
    lik.stratificationVariables = ip.Results.stratificationVariables;
    if ~ismember('stratificationVariables', ip.UsingDefaults)
      if isequal(ip.Results.removeStratificationVariables, 'on')
        lik.removeStratificationVariables=true;
      end
    end
  end

  if init
    % Set the function handles to the nested functions
    lik.fh.pak = @lik_coxph_pak;
    lik.fh.unpak = @lik_coxph_unpak;
    lik.fh.ll = @lik_coxph_ll;
    lik.fh.llg = @lik_coxph_llg;
    lik.fh.llg2 = @lik_coxph_llg2;
    lik.fh.llg3 = @lik_coxph_llg3;
    lik.fh.tiltedMoments = @lik_coxph_tiltedMoments;
    lik.fh.predy = @lik_coxph_predy;
    lik.fh.invlink = @lik_coxph_invlink;
    lik.fh.recappend = @lik_coxph_recappend;
    lik.fh.predcdf= @lik_coxph_predcdf;
  end

  function [w,s] = lik_coxph_pak(lik)
  %LIK_COXPH_PAK  Combine likelihood parameters into one vector.
  %
  %  Description
  %    W = LIK_COXPH_PAK(LIK) takes a likelihood structure LIK and
  %    combines the parameters into a single row vector W. This is
  %    a mandatory subfunction used for example in energy and
  %    gradient computations.
  %
  %       w = log(lik.disper)
  %
  %   See also
  %   LIK_COXPH_UNPAK, GP_PAK

    w=[];s={};
  end

  function [lik, w] = lik_coxph_unpak(lik, w)
  %LIK_COXPH_UNPAK  Extract likelihood parameters from the vector.
  %
  %  Description
  %    [LIK, W] = LIK_COXPH_UNPAK(W, LIK) takes a likelihood
  %    structure LIK and extracts the parameters from the vector W
  %    to the LIK structure. This is a mandatory subfunction used
  %    for example in energy and gradient computations.
  %
  %   Assignment is inverse of
  %       w = log(lik.disper)
  %
  %   See also
  %   LIK_COXPH_PAK, GP_UNPAK

    lik=lik;
    w=w;

  end

  function ll = lik_coxph_ll(lik, y, f, z)
  %LIK_COXPH_LL  Log likelihood
  %
  %  Description
  %    LL = LIK_COXPH_LL(LIK, Y, F, Z) takes a likelihood
  %    structure LIK, incedence counts Y, expected counts Z, and
  %    latent values F. Returns the log likelihood, log p(y|f,z).
  %    This subfunction is needed when using Laplace approximation
  %    or MCMC for inference with non-Gaussian likelihoods. This
  %    subfunction is also used in information criteria (DIC, WAIC)
  %    computations.
  %
  %  See also
  %    LIK_COXPH_LLG, LIK_COXPH_LLG3, LIK_COXPH_LLG2, GPLA_E

    if isempty(z)
      error(['lik_coxph -> lik_coxph_ll: missing z!    '...
             'Coxph likelihood needs the expected number of    '...
             'occurrences as an extra input z. See, for         '...
             'example, lik_coxph and gpla_e.               ']);
    end

    [n,ny]=size(y);
    ntime=size(lik.xtime,1);
    i3v=ones(size(y,1),1);
    if isfield(lik, 'stratificationVariables')
      f1=f(1:ntime*lik.n_u);
      f2=f((ntime*lik.n_u+1):(ntime*lik.n_u+n));
      for i=1:lik.n_u
        i3v(lik.stratind{i})=i;
      end
    else
      f1=f(1:ntime);
      f2=f((ntime+1):(ntime+n));
    end

    la1=exp(f1);
    eta2=exp(f2);

    nu=1-z;
    sd=lik.stime(2)-lik.stime(1);

    if ny==1
      ll=0;
      for i1=1:n
        i3=i3v(i1);
        nft=(i3-1)*ntime;
        si=sum(y(i1)>lik.stime);
        ll=ll + nu(i1).*(f1(si+nft)+f2(i1)) - (y(i1)-lik.stime(si)).*la1(si+nft).*eta2(i1) ...
              - sum(sd.*la1((1+nft):(si-1+nft)).*eta2(i1));
      end
    else

      ll=0;
      sb=sum(bsxfun(@gt,y(:,1),lik.stime),2);
      se=sum(bsxfun(@gt,y(:,2),lik.stime),2);
      for i1=1:n
        i3=i3v(i1);
        nft=(i3-1)*ntime;
        if sb(i1)==0
          ll=ll + nu(i1).*(f1(se(i1)+nft)+f2(i1)) - (y(i1,2)-lik.stime(se(i1))).*la1(se(i1)+nft).*eta2(i1) ...
                - sum(sd.*la1((1+nft):(se(i1)-1+nft)).*eta2(i1));
        else

          if se(i1)==sb(i1)
            ll=ll + nu(i1).*(f1(se(i1)+nft)+f2(i1)) - (y(i1,2)-y(i1,1)).*la1(se(i1)+nft).*eta2(i1);
          else
            ll=ll + nu(i1).*(f1(se(i1)+nft)+f2(i1)) - (y(i1,2)-lik.stime(se(i1))).*la1(se(i1)+nft).*eta2(i1) ...
                - sum(sd.*la1((sb(i1)+1+nft):(se(i1)-1+nft)).*eta2(i1)) - (lik.stime(sb(i1)+1)-y(i1,1)).*la1(sb(i1)+nft).*eta2(i1);
          end
        end
      end
    end
  end

  function llg = lik_coxph_llg(lik, y, f, param, z)
  %LIK_COXPH_LLG  Gradient of the log likelihood
  %
  %  Description
  %    LLG = LIK_COXPH_LLG(LIK, Y, F, PARAM) takes a likelihood
  %    structure LIK, incedence counts Y, expected counts Z and
  %    latent values F. Returns the gradient of the log likelihood
  %    with respect to PARAM. At the moment PARAM can be 'param' or
  %    'latent'. This subfunction is needed when using Laplace
  %    approximation or MCMC for inference with non-Gaussian
  %    likelihoods.
  %
  %  See also
  %    LIK_COXPH_LL, LIK_COXPH_LLG2, LIK_COXPH_LLG3, GPLA_E

    if isempty(z)
      error(['lik_coxph -> lik_coxph_llg: missing z!    '...
             'Coxph likelihood needs the expected number of    '...
             'occurrences as an extra input z. See, for         '...
             'example, lik_coxph and gpla_e.               ']);
    end

    ntime=size(lik.xtime,1);

    [n,ny]=size(y);
    i3v=ones(size(y));
    if isfield(lik, 'stratificationVariables')
      f1=f(1:ntime*lik.n_u);
      f2=f((ntime*lik.n_u+1):(ntime*lik.n_u+n));
      llg=zeros(ntime*lik.n_u+n,1);
      for i=1:lik.n_u
        i3v(lik.stratind{i})=i;
      end
      nf1=lik.n_u*ntime;
    else
      f1=f(1:ntime);
      f2=f((ntime+1):(ntime+n));
      llg=zeros(ntime+n,1);
      nf1=ntime;
    end

    la1=exp(f1(:));
    eta2=exp(f2(:));

    nu=1-z;
    sd=lik.stime(2)-lik.stime(1);

    switch param
      case 'latent'

      if ny==1
        if ~isfield(lik, 'stratificationVariables')
          for i1=1:ntime
            ind=y>=lik.stime(i1) & y<lik.stime(i1+1);
            llg(i1)= sum(nu(ind) - (y(ind)-lik.stime(i1)).*la1(i1).*eta2(ind)) - sum(sum(sd.*la1(i1)).*eta2(~ind & y>=lik.stime(i1+1)));
          end
        else
          for j=1:lik.n_u
            for i1=1:ntime
              ind=y>=lik.stime(i1) & y<lik.stime(i1+1);
              ind2=lik.stratind{j};
              llg(i1+(j-1)*ntime)= sum(nu(ind & ind2) - (y(ind & ind2)-lik.stime(i1)).*la1(i1+(j-1)*ntime).*eta2(ind & ind2)) - sum(sum(sd.*la1(i1+(j-1)*ntime)).*eta2((~ind & ind2) & y>=lik.stime(i1+1)));
            end
          end
        end
        for i1=1:n
          i3=i3v(i1);
          nft=(i3-1)*ntime;
          si=sum(y(i1)>lik.stime);
          llg(i1+nf1)= nu(i1) - (y(i1)-lik.stime(si)).*la1(si+nft).*eta2(i1) - sum(sd.*la1((1+nft):(si-1+nft)).*eta2(i1));
        end
      else
        for i1=1:ntime

          % left truncated + follow-up entry: (1)
          ind_vkst = y(:,1)>=lik.stime(i1) & y(:,1)<lik.stime(i1+1) & y(:,2)>=lik.stime(i1+1);
          % follow-up entry + follow-up exit: (2)
          ind_stsp = y(:,1)>=lik.stime(i1) & y(:,1)<lik.stime(i1+1) & y(:,2)>=lik.stime(i1) & y(:,2)<lik.stime(i1+1);
          % follow-up: (3)
          ind_s = y(:,1)<lik.stime(i1) & y(:,2)>=lik.stime(i1+1);
          % follow-up exit: (4)
          ind_sp = y(:,1)<lik.stime(i1) & y(:,2)>=lik.stime(i1) & y(:,2)<lik.stime(i1+1);

          if ~isfield(lik, 'stratificationVariables')
            % (1)
            s2b=sum(-(lik.stime(i1+1)-y(ind_vkst,1)).*la1(i1).*eta2(ind_vkst));
            % (2)
            s3b=sum(nu(ind_stsp)) - sum((y(ind_stsp,2)-y(ind_stsp,1)).*la1(i1).*eta2(ind_stsp));
            % (3)
            s4= - sum(sd.*la1(i1).*eta2(ind_s));
            % (4)
            s5=sum(nu(ind_sp)) - sum((y(ind_sp,2)-lik.stime(i1)).*la1(i1).*eta2(ind_sp));

            llg(i1) = s2b+s3b+s4+s5;
          else
            for j=1:lik.n_u
              ind2 = lik.stratind{j};
              nft=(j-1)*ntime;
              % (1)
              s2b=sum(-(lik.stime(i1+1)-y(ind_vkst & ind2,1)).*la1(i1+nft).*eta2(ind_vkst & ind2));
              % (2)
              s3b=sum(nu(ind_stsp & ind2)) - sum((y(ind_stsp & ind2,2)-y(ind_stsp & ind2,1)).*la1(i1+nft).*eta2(ind_stsp & ind2));
              % (3)
              s4= - sum(sd.*la1(i1+nft).*eta2(ind_s & ind2));
              % (4)
              s5=sum(nu(ind_sp & ind2)) - sum((y(ind_sp & ind2,2)-lik.stime(i1)).*la1(i1+nft).*eta2(ind_sp & ind2));

              llg(i1+nft) = s2b+s3b+s4+s5;
            end
          end

        end

        sb=sum(bsxfun(@gt,y(:,1),lik.stime),2);
        se=sum(bsxfun(@gt,y(:,2),lik.stime),2);
        for i1=1:n
          i3=i3v(i1);
          nft=(i3-1)*ntime;
          if sb(i1)==0
            llg(i1+nf1)= nu(i1) - (y(i1,2)-lik.stime(se(i1))).*la1(se(i1)+nft).*eta2(i1) - sum(sd.*la1((1+nft):(se(i1)-1+nft)).*eta2(i1));
          else
            if se(i1)==sb(i1)
              llg(i1+nf1) = nu(i1) - (y(i1,2)-y(i1,1)).*la1(se(i1)+nft).*eta2(i1);
            else
              llg(i1+nf1) = nu(i1) - (y(i1,2)-lik.stime(se(i1))).*la1(se(i1)+nft).*eta2(i1) - sum(sd.*la1((sb(i1)+1+nft):(se(i1)-1+nft)).*eta2(i1)) - (lik.stime(sb(i1)+1)-y(i1,1)).*la1(sb(i1)+nft).*eta2(i1);
            end
          end
        end

      end

    end
  end

  function [llg2,llg2mat] = lik_coxph_llg2(lik, y, f, param, z)
  %function [pi_vec, pi_mat] = lik_coxph_llg2(lik, y, ff, param, z)
  %LIK_COXPH_LLG2  Second gradients of the log likelihood
  %
  %  Description
  %    LLG2 = LIK_COXPH_LLG2(LIK, Y, F, PARAM) takes a likelihood
  %    structure LIK, incedence counts Y, expected counts Z, and
  %    latent values F. Returns the Hessian of the log likelihood
  %    with respect to PARAM. At the moment PARAM can be only
  %    'latent'. LLG2 is a vector with diagonal elements of the
  %    Hessian matrix (off diagonals are zero). This subfunction
  %    is needed when using Laplace approximation or EP for inference
  %    with non-Gaussian likelihoods.
  %
  %  See also
  %    LIK_COXPH_LL, LIK_COXPH_LLG, LIK_COXPH_LLG3, GPLA_E

    if isempty(z)
      error(['lik_coxph -> lik_coxph_llg2: missing z!   '...
             'Coxph likelihood needs the expected number of    '...
             'occurrences as an extra input z. See, for         '...
             'example, lik_coxph and gpla_e.               ']);
    end

    [n,ny]=size(y);
    ntime=size(lik.xtime,1);
    i3v=ones(size(y,1),1);
    if isfield(lik, 'stratificationVariables')
      f1=f(1:ntime*lik.n_u);
      f2=f((ntime*lik.n_u+1):(ntime*lik.n_u+n));
      llg2=zeros(ntime*lik.n_u+n,1);
      llg2mat=zeros(ntime*lik.n_u,n);
      for i=1:lik.n_u
        i3v(lik.stratind{i})=i;
      end
      nf1=ntime*lik.n_u;
    else
      f1=f(1:ntime);
      f2=f((ntime+1):(ntime+n));
      llg2=zeros(ntime+n,1);
      llg2mat=zeros(ntime,n);
      nf1=ntime;
    end

    la1=exp(f1);
    eta2=exp(f2);

    %nu=1-z;
    sd=lik.stime(2)-lik.stime(1);
    switch param
      case 'latent'

        if ny==1
          % 11
          if ~isfield(lik, 'stratificationVariables')
            for i1=1:ntime
              ind=y>=lik.stime(i1) & y<lik.stime(i1+1);
              llg2(i1)= sum(-(y(ind)-lik.stime(i1)).*la1(i1).*eta2(ind)) - sum(sum(sd.*la1(i1)).*eta2(~ind & y>=lik.stime(i1+1)));
              %llg2(i1,i1)= sum(-(y(ind)-lik.stime(i1)).*la1(i1).*eta2(ind)) - sum(sum(sd.*la1(i1)).*eta2(~ind & y>=lik.stime(i1+1)));
            end
          else
            for j=1:lik.n_u
              for i1=1:ntime
                ind=y>=lik.stime(i1) & y<lik.stime(i1+1);
                ind2=lik.stratind{j};
                llg2(i1+(j-1)*ntime)= sum(-(y(ind & ind2)-lik.stime(i1)).*la1(i1+ntime*(j-1)).*eta2(ind & ind2)) - sum(sum(sd.*la1(i1+ntime*(j-1))).*eta2((~ind & ind2) & y>=lik.stime(i1+1)));
                %llg2(i1,i1)= sum(-(y(ind)-lik.stime(i1)).*la1(i1).*eta2(ind)) - sum(sum(sd.*la1(i1)).*eta2(~ind & y>=lik.stime(i1+1)));
              end
            end
          end

          % 22
          for i1=1:n
            si=sum(y(i1)>lik.stime);
            i3=i3v(i1);
            llg2(i1+nf1)= -(y(i1)-lik.stime(si)).*la1(si+(i3-1)*ntime).*eta2(i1) - sum(sd.*la1((1+(i3-1)*ntime):(si-1+(i3-1)*ntime)).*eta2(i1));
            %llg2(i1+ntime,i1+ntime)= -(y(i1)-lik.stime(si)).*la1(si).*eta2(i1) - sum(sd.*la1(1:(si-1)).*eta2(i1));
          end

          % derivative wrt f1 and f2:
          if ~isfield(lik, 'stratificationVariables')
            for i1=1:ntime
              ind=y>=lik.stime(i1) & y<lik.stime(i1+1);
              llg2mat(i1,find(ind))= -(y(ind)-lik.stime(i1)).*la1(i1).*eta2(ind);
              llg2mat(i1,find((~ind & y>=lik.stime(i1+1)))) = - sd.*la1(i1).*eta2((~ind & y>=lik.stime(i1+1)));
              %llg2(i1,ntime+find(ind))= -(y(ind)-lik.stime(i1)).*la1(i1).*eta2(ind);
              %llg2(i1,ntime+find((~ind & y>=lik.stime(i1+1)))) = - sd.*la1(i1).*eta2((~ind & y>=lik.stime(i1+1)));
              %llg2(ntime+find(ind),i1)=llg2(i1,ntime+find(ind));
              %llg2(ntime+find((~ind & y>=lik.stime(i1+1))),i1)=llg2(i1,ntime+find((~ind & y>=lik.stime(i1+1))));
            end
          else
            for j=1:lik.n_u
              ind2=lik.stratind{j};
              for i1=1:ntime
                ind=y>=lik.stime(i1) & y<lik.stime(i1+1);
                llg2mat(i1+(j-1)*ntime,find(ind & ind2))= -(y(ind & ind2)-lik.stime(i1)).*la1(i1+(j-1)*ntime).*eta2(ind & ind2);
                llg2mat(i1+(j-1)*ntime,find(((~ind & ind2) & y>=lik.stime(i1+1)))) = - sd.*la1(i1+(j-1)*ntime).*eta2(((~ind & ind2) & y>=lik.stime(i1+1)));
                %llg2(i1,ntime+find(ind))= -(y(ind)-lik.stime(i1)).*la1(i1).*eta2(ind);
                %llg2(i1,ntime+find((~ind & y>=lik.stime(i1+1)))) = - sd.*la1(i1).*eta2((~ind & y>=lik.stime(i1+1)));
                %llg2(ntime+find(ind),i1)=llg2(i1,ntime+find(ind));
                %llg2(ntime+find((~ind & y>=lik.stime(i1+1))),i1)=llg2(i1,ntime+find((~ind & y>=lik.stime(i1+1))));
              end
            end
          end

        else

          % 11
          for i1=1:ntime

            % left truncated + follow-up entry: (1)
            ind_vkst = y(:,1)>=lik.stime(i1) & y(:,1)<lik.stime(i1+1) & y(:,2)>=lik.stime(i1+1);
            % follow-up entry + follow-up exit: (2)
            ind_stsp = y(:,1)>=lik.stime(i1) & y(:,1)<lik.stime(i1+1) & y(:,2)>=lik.stime(i1) & y(:,2)<lik.stime(i1+1);
            % follow-up: (3)
            ind_s = y(:,1)<lik.stime(i1) & y(:,2)>=lik.stime(i1+1);
            % follow-up exit: (4)
            ind_sp = y(:,1)<lik.stime(i1) & y(:,2)>=lik.stime(i1) & y(:,2)<lik.stime(i1+1);

            if ~isfield(lik, 'stratificationVariables')
              % (1)
              s2b=sum(-(lik.stime(i1+1)-y(ind_vkst,1)).*la1(i1).*eta2(ind_vkst));
              % (2)
              s3b=-sum((y(ind_stsp,2)-y(ind_stsp,1)).*la1(i1).*eta2(ind_stsp));
              % (3)
              s4=-sum(sd.*la1(i1).*eta2(ind_s));
              % (4)
              s5=-sum((y(ind_sp,2)-lik.stime(i1)).*la1(i1).*eta2(ind_sp));
              llg2(i1) = s2b+s3b+s4+s5;
            else
              for j=1:lik.n_u
                ind2=lik.stratind{j};
                nft=(j-1)*ntime;
                % (1)
                s2b=sum(-(lik.stime(i1+1)-y(ind_vkst & ind2,1)).*la1(i1+nft).*eta2(ind_vkst & ind2));
                % (2)
                s3b=-sum((y(ind_stsp & ind2,2)-y(ind_stsp & ind2,1)).*la1(i1+nft).*eta2(ind_stsp & ind2));
                % (3)
                s4=-sum(sd.*la1(i1+nft).*eta2(ind_s & ind2));
                % (4)
                s5=-sum((y(ind_sp & ind2,2)-lik.stime(i1)).*la1(i1+nft).*eta2(ind_sp & ind2));
                llg2(i1+nft) = s2b+s3b+s4+s5;
              end
            end
          end

          % 22
          sb=sum(bsxfun(@gt,y(:,1),lik.stime),2);
          se=sum(bsxfun(@gt,y(:,2),lik.stime),2);
          for i1=1:n
            i3=i3v(i1);
            nft=(i3-1)*ntime;
            if sb(i1)==0
              llg2(i1+nf1)= -(y(i1,2)-lik.stime(se(i1))).*la1(se(i1)+nft).*eta2(i1) - sum(sd.*la1((1+nft):(se(i1)-1+nft)).*eta2(i1));
              %llg2(i1+ntime,i1+ntime)= -(y(i1,2)-lik.stime(se)).*la1(se).*eta2(i1) - sum(sd.*la1(1:(se-1)).*eta2(i1));
            else
              if se(i1)==sb(i1)
                llg2(i1+nf1) = -(y(i1,2)-y(i1,1)).*la1(se(i1)+nft).*eta2(i1);
                %llg2(i1+ntime,i1+ntime) = -(y(i1,2)-y(i1,1)).*la1(se).*eta2(i1);
              else
                llg2(i1+nf1) = -(y(i1,2)-lik.stime(se(i1))).*la1(se(i1)+nft).*eta2(i1) - sum(sd.*la1((sb(i1)+1+nft):(se(i1)-1+nft)).*eta2(i1)) - (lik.stime(sb(i1)+1)-y(i1,1)).*la1(sb(i1)+nft).*eta2(i1);
                %llg2(i1+ntime,i1+ntime) = -(y(i1,2)-lik.stime(se)).*la1(se).*eta2(i1) - sum(sd.*la1((sb+1):(se-1)).*eta2(i1)) - (lik.stime(sb+1)-y(i1,1)).*la1(sb).*eta2(i1);
              end
            end
          end

          % derivative wrt f1 and f2:
          for i1=1:ntime

            % left truncated + follow-up entry: (1)
            ind_vkst = y(:,1)>=lik.stime(i1) & y(:,1)<lik.stime(i1+1) & y(:,2)>=lik.stime(i1+1);
            % follow-up entry + follow-up exit: (2)
            ind_stsp = y(:,1)>=lik.stime(i1) & y(:,1)<lik.stime(i1+1) & y(:,2)>=lik.stime(i1) & y(:,2)<lik.stime(i1+1);
            % follow-up: (3)
            ind_s = y(:,1)<lik.stime(i1) & y(:,2)>=lik.stime(i1+1);
            % follow-up exit: (4)
            ind_sp = y(:,1)<lik.stime(i1) & y(:,2)>=lik.stime(i1) & y(:,2)<lik.stime(i1+1);

            if ~isfield(lik, 'stratificationVariables')
              % (1)
              llg2mat(i1,ind_vkst)=-(lik.stime(i1+1)-y(ind_vkst,1)).*la1(i1).*eta2(ind_vkst);
              % (2)
              llg2mat(i1,ind_stsp)=-(y(ind_stsp,2)-y(ind_stsp,1)).*la1(i1).*eta2(ind_stsp);
              % (3)
              llg2mat(i1,ind_s)= -sd.*la1(i1).*eta2(ind_s);
              % (4)
              llg2mat(i1,ind_sp)=-(y(ind_sp,2)-lik.stime(i1)).*la1(i1).*eta2(ind_sp);
            else
              for j=1:lik.n_u
                ind2=lik.stratind{j};
                nft=(j-1)*ntime;
                % (1)
                llg2mat(i1+nft,ind_vkst & ind2)=-(lik.stime(i1+1)-y(ind_vkst & ind2,1)).*la1(i1+nft).*eta2(ind_vkst & ind2);
                % (2)
                llg2mat(i1+nft,ind_stsp & ind2)=-(y(ind_stsp & ind2,2)-y(ind_stsp & ind2,1)).*la1(i1+nft).*eta2(ind_stsp & ind2);
                % (3)
                llg2mat(i1+nft,ind_s & ind2)= -sd.*la1(i1+nft).*eta2(ind_s & ind2);
                % (4)
                llg2mat(i1+nft,ind_sp & ind2)=-(y(ind_sp & ind2,2)-lik.stime(i1)).*la1(i1+nft).*eta2(ind_sp & ind2);
              end
            end
          end
      end
    end
  end

  function [llg3,llg3mat] = lik_coxph_llg3(lik, y, f, param, z, j1)
  %LIK_COXPH_LLG3  Third gradients of the log likelihood
  %
  %  Description
  %    LLG3 = LIK_COXPH_LLG3(LIK, Y, F, PARAM) takes a likelihood
  %    structure LIK, incedence counts Y, expected counts Z and
  %    latent values F and returns the third gradients of the log
  %    likelihood with respect to PARAM. At the moment PARAM can be
  %    only 'latent'. LLG3 is a vector with third gradients. This
  %    subfunction is needed when using Laplace approximation for
  %    inference with non-Gaussian likelihoods.
  %
  %  See also
  %    LIK_COXPH_LL, LIK_COXPH_LLG, LIK_COXPH_LLG2, GPLA_E, GPLA_G

    if isempty(z)
      error(['lik_coxph -> lik_coxph_llg3: missing z!   '...
             'Coxph likelihood needs the expected number of    '...
             'occurrences as an extra input z. See, for         '...
             'example, lik_coxph and gpla_e.               ']);
    end

    ntime=size(lik.xtime,1);

    [n,ny]=size(y);
    if isfield(lik, 'stratificationVariables')
      f1=f(1:ntime*lik.n_u);
      f2=f((ntime*lik.n_u+1):(ntime*lik.n_u+n));
      llg3=zeros(ntime*lik.n_u+n,1);
      llg3mat=zeros(ntime*lik.n_u,n);
      nf1=ntime*lik.n_u;
      if j1>nf1
        for j=1:lik.n_u
          if lik.stratind{j}(j1-nf1)==1;
            i3=j;
            break;
          end
        end
      end
    else
      f1=f(1:ntime);
      f2=f((ntime+1):(ntime+n));
      llg3=zeros(ntime+n,1);
      llg3mat=zeros(ntime,n);
      nf1=ntime;
      i3=1;
    end

    la1=exp(f1);
    eta2=exp(f2);

    %nu=1-z;
    sd=lik.stime(2)-lik.stime(1);

    switch param
      case 'latent'

        if ny==1

          if j1<=nf1

            indt=rem(j1,ntime);
            if indt==0
              indt=ntime;
            end

            % 11
            ind=y>=lik.stime(indt) & y<lik.stime(indt+1);

            if isfield(lik, 'stratificationVariables')
              ind2=lik.stratind{(j1-indt)/ntime+1};
            else
              ind2=ones(size(ind));
            end

            fi=find(ind & ind2);
            fni=find((~ind & ind2) & y>=lik.stime(indt));

            llg3(j1) = sum(-(y(ind & ind2)-lik.stime(indt)).*la1(j1).*eta2(ind & ind2)) - sum(sum(sd.*la1(j1)).*eta2((~ind & ind2) & y>=lik.stime(indt+1)));

            if ~isempty(fi)
              valtmp=(-(y(ind & ind2)-lik.stime(indt)).*la1(j1).*eta2(ind & ind2));
              for m2i=1:length(valtmp)
                llg3( nf1+fi(m2i))  = valtmp(m2i);
              end
            end
            if ~isempty(fni)
              valtmp2=(-sd.*la1(j1).*eta2(((~ind & ind2) & y>=lik.stime(indt+1))));
              for m2i=1:length(valtmp2)
                llg3( nf1+fni(m2i))  = valtmp2(m2i);
              end
            end

            % 12/21
            % derivative wrt f1 and f2:
            val1tmp=-(y(ind & ind2)-lik.stime(indt)).*la1(j1).*eta2(ind & ind2);
            llg3mat(j1,fi)= val1tmp;

            val2tmp = - sd.*la1(j1).*eta2(((~ind & ind2) & y>=lik.stime(indt+1)));
            llg3mat(j1,fni) = val2tmp;

          else

            % 11
            s1=sum(y(j1-nf1)>lik.stime);
            llg3((1+(i3-1)*ntime):(s1-1+(i3-1)*ntime)) = - sd.*la1((1+(i3-1)*ntime):(s1-1+(i3-1)*ntime)).*eta2(j1-nf1);
            llg3(s1+(i3-1)*ntime) = -(y(j1-nf1)-lik.stime(s1)).*la1(s1+(i3-1)*ntime).*eta2(j1-nf1);

            % 22
            llg3(j1) = -(y(j1-nf1)-lik.stime(s1)).*la1(s1+(i3-1)*ntime).*eta2(j1-nf1) ...
                        - sum(sd.*la1((1+(i3-1)*ntime):(s1-1+(i3-1)*ntime)).*eta2(j1-nf1));

            % 12/21
            % derivative wrt f1 and f2:
            val3tmp = - sd.*la1((1+(i3-1)*ntime):(s1-1+(i3-1)*ntime)).*eta2(j1-nf1);
            llg3mat((1+(i3-1)*ntime):(s1-1+(i3-1)*ntime),j1-nf1)= val3tmp;
            llg3mat(s1+(i3-1)*ntime,j1-nf1) = -(y(j1-nf1)-lik.stime(s1)).*la1(s1+(i3-1)*ntime).*eta2(j1-nf1);

          end

        else

          if j1<=nf1

            indt=rem(j1,ntime);
            if indt==0
              indt=ntime;
            end
            if isfield(lik, 'stratificationVariables')
              ind2=lik.stratind{(j1-indt)/ntime+1};
            else
              ind2=ones(size(y,1),1);
            end

            % 11
            % left truncated + follow-up entry: (1)
            ind_vkst = y(:,1)>=lik.stime(indt) & y(:,1)<lik.stime(indt+1) & y(:,2)>=lik.stime(indt+1);
            % follow-up entry + follow-up exit: (2)
            ind_stsp = y(:,1)>=lik.stime(indt) & y(:,1)<lik.stime(indt+1) & y(:,2)>=lik.stime(indt) & y(:,2)<lik.stime(indt+1);
            % follow-up: (3)
            ind_s = y(:,1)<lik.stime(indt) & y(:,2)>=lik.stime(indt+1);
            % follow-up exit: (4)
            ind_sp = y(:,1)<lik.stime(indt) & y(:,2)>=lik.stime(indt) & y(:,2)<lik.stime(indt+1);

            % (1)
            s2b=sum(-(lik.stime(indt+1)-y(ind_vkst & ind2,1)).*la1(j1).*eta2(ind_vkst & ind2));
            % (2)
            s3b=-sum((y(ind_stsp & ind2,2)-y(ind_stsp & ind2,1)).*la1(j1).*eta2(ind_stsp & ind2));
            % (3)
            s4=-sum(sd.*la1(j1).*eta2(ind_s & ind2));
            % (4)
            s5=-sum((y(ind_sp & ind2,2)-lik.stime(indt)).*la1(j1).*eta2(ind_sp & ind2));

            llg3(j1) = s2b+s3b+s4+s5;

            % 22
            % (1)
            llg3(nf1+find(ind_vkst & ind2))=-(lik.stime(indt+1)-y(ind_vkst & ind2,1)).*la1(j1).*eta2(ind_vkst & ind2);
            % (2)
            llg3(nf1+find(ind_stsp & ind2))=-(y(ind_stsp & ind2,2)-y(ind_stsp & ind2,1)).*la1(j1).*eta2(ind_stsp & ind2);
            % (3)
            llg3(nf1+find(ind_s & ind2))= -sd.*la1(j1).*eta2(ind_s & ind2);
            % (4)
            llg3(nf1+find(ind_sp & ind2))=-(y(ind_sp & ind2,2)-lik.stime(indt)).*la1(j1).*eta2(ind_sp & ind2);

            % 12/21
            llg3mat(j1,find(ind_vkst & ind2))=-(lik.stime(indt+1)-y(ind_vkst & ind2,1)).*la1(j1).*eta2(ind_vkst & ind2);
            % (2)
            llg3mat(j1,find(ind_stsp & ind2))=-(y(ind_stsp & ind2,2)-y(ind_stsp & ind2,1)).*la1(j1).*eta2(ind_stsp & ind2);
            % (3)
            llg3mat(j1,find(ind_s & ind2))= -sd.*la1(j1).*eta2(ind_s & ind2);
            % (4)
            llg3mat(j1,find(ind_sp & ind2))=-(y(ind_sp & ind2,2)-lik.stime(indt)).*la1(j1).*eta2(ind_sp & ind2);
          else

            sb=sum(y(j1-nf1,1)>lik.stime); % begin
            se=sum(y(j1-nf1,2)>lik.stime); % end

            nft=(i3-1)*ntime;
            % 11
            if sb==0
              llg3((1+nft):(se-1+nft))= -sd.*la1((1+nft):(se-1+nft)).*eta2(j1-nf1);
              llg3(se+nft)= -(y(j1-nf1,2)-lik.stime(se)).*la1(se+nft).*eta2(j1-nf1);
            else
              if se==sb
                llg3(se+nft) = -(y(j1-nf1,2)-y(j1-nf1,1)).*la1(se+nft).*eta2(j1-nf1);
              else
                llg3(sb+nft) = - (lik.stime(sb+1)-y(j1-nf1,1)).*la1(sb+nft).*eta2(j1-nf1);
                llg3((sb+1+nft):(se-1+nft)) = - sd.*la1((sb+1+nft):(se-1+nft)).*eta2(j1-nf1);
                llg3(se+nft) = -(y(j1-nf1,2)-lik.stime(se)).*la1(se+nft).*eta2(j1-nf1);
              end
            end

            % 12/21
            if sb==0
              llg3mat((1+nft):(se-1+nft),j1-nf1) = -sd.*la1((1+nft):(se-1+nft)).*eta2(j1-nf1);
              llg3mat(se+nft,j1-nf1)= -(y(j1-nf1,2)-lik.stime(se)).*la1(se+nft).*eta2(j1-nf1);
            else
              if se==sb
                llg3mat(se+nft,j1-nf1) = -(y(j1-nf1,2)-y(j1-nf1,1)).*la1(se+nft).*eta2(j1-nf1);
              else
                llg3mat(sb+nft,j1-nf1) = - (lik.stime(sb+1)-y(j1-nf1,1)).*la1(sb+nft).*eta2(j1-nf1);
                llg3mat((sb+1+nft):(se-1+nft),j1-nf1) = - sd.*la1((sb+1+nft):(se-1+nft)).*eta2(j1-nf1);
                llg3mat(se+nft,j1-nf1) = -(y(j1-nf1,2)-lik.stime(se)).*la1(se+nft).*eta2(j1-nf1);
              end
            end

            % 22
            if sb==0
              llg3(j1)= -(y(j1-nf1,2)-lik.stime(se)).*la1(se+nft).*eta2(j1-nf1) - sum(sd.*la1((1+nft):(se-1+nft)).*eta2(j1-nf1));
            else
              if se==sb
                llg3(j1) = -(y(j1-nf1,2)-y(j1-nf1,1)).*la1(se+nft).*eta2(j1-nf1);
              else
                llg3(j1) = -(y(j1-nf1,2)-lik.stime(se)).*la1(se+nft).*eta2(j1-nf1) - sum(sd.*la1((sb+1+nft):(se-1+nft)).*eta2(j1-nf1)) - (lik.stime(sb+1)-y(j1-nf1,1)).*la1(sb+nft).*eta2(j1-nf1);
              end
            end

          end
        end
    end
  end

  function [logM_0, m_1, sigm2hati1] = lik_coxph_tiltedMoments(lik, y, i1, S2_i, M_i, z)
    %LIK_COXPH_TILTEDMOMENTS  Returns the marginal moments for EP algorithm
    %
    %  Description
    %    [M_0, M_1, M2] = LIK_COXPH_TILTEDMOMENTS(LIK, Y, I, S2,
    %    MYY, Z) takes a likelihood structure LIK, incedence counts
    %    Y, expected counts Z, index I and cavity variance S2 and
    %    mean MYY. Returns the zeroth moment M_0, mean M_1 and
    %    variance M_2 of the posterior marginal (see Rasmussen and
    %    Williams (2006): Gaussian processes for Machine Learning,
    %    page 55). This subfunction is needed when using EP for
    %    inference with non-Gaussian likelihoods.
    %
    %  See also
    %    GPEP_E

      [n,ny]=size(y);

      % M_i(end);
      % S2_i(end,end);
      fgrid=M_i(end)+sqrt(S2_i(end,end))*[-6 6];
      fg=linspace(fgrid(1),fgrid(2),15);
      ng=length(fg);

      if isfield(gp.lik, 'stratificationVariables')
        ntime=size(lik.xtime,1)*gp.lik.n_u;
      else
        ntime=size(lik.xtime,1);
      end

      %f11=f(1:ntime);
      %f2=f((ntime+1):(ntime+n));
      %la1=exp(f1);
      %eta2=exp(f2);

      nu=1-z;
      sd=lik.stime(2)-lik.stime(1);
      if ny==1
        sb=1;
        se=sum(bsxfun(@gt,y(i1,1),lik.stime),2);
      end
      indf=sb:se;
      sdvec=[ones(se-1,1)*sd; y(i1)-lik.stime(se)];
      nutmp=zeros(se,1);
      nutmp(se)=nu(i1);

      for j1=1:ng

        % conditional distribution
        myy=M_i(indf)+S2_i(indf,end)*(1./S2_i(end,end))*(fg(j1)-M_i(end));
        myy0=myy;
        Sigm=S2_i(indf,indf)-S2_i(indf,end)*(1./S2_i(end,end))*S2_i(end,indf);
        Sigm0=Sigm;

        nu_prior=Sigm\myy;

        nt=size(myy,1);
        c1=exp(fg(j1));

        % site parameters
        tautilde=zeros(nt,1);
        nutilde=zeros(nt,1);
        ztilde=zeros(nt,1);

        max_small_ep_iter=50;
        tol=1e-9;
        small_ep_iter=1;

        tautilde0=Inf; nutilde0=Inf; ztilde0=Inf;

        logZep_tmp=0; logZep=Inf;
        %while small_ep_iter <= max_small_ep_iter && (sum(abs(tautilde0-tautilde)>tol) || sum(abs(nutilde0-nutilde)>tol) || sum(abs(ztilde0-ztilde)>tol))
        while small_ep_iter<=max_small_ep_iter && abs(logZep_tmp-logZep)>tol
          logZep_tmp=logZep;

          %tautilde0=tautilde; nutilde0=nutilde; ztilde0=ztilde;

          for k1=1:nt

            tau_i=Sigm(k1,k1)^-1-tautilde(k1);
            nu_i = Sigm(k1,k1)^-1*myy(k1)-nutilde(k1);
            myy_i=nu_i/tau_i;
            sigm2_i=tau_i^-1;

            % marginal moments
            [logM0(k1), muhati, sigm2hati] = coxph_tiltedMoments(sigm2_i, myy_i, nutmp(k1), sdvec(k1), c1);
            %[M0, muhati, sigm2hati] = coxph_tiltedMoments(lik, y(i1,:), k1, sigm2_i, myy_i, c1, sd_vec(i1), ztmp);

            deltatautilde=sigm2hati^-1-tau_i-tautilde(k1);
            tautilde(k1)=tautilde(k1)+deltatautilde;
            nutilde(k1)=sigm2hati^-1*muhati-nu_i;

            apu = deltatautilde/(1+deltatautilde*Sigm(k1,k1));
            Sigm = Sigm - apu*(Sigm(:,k1)*Sigm(:,k1)');

            % The below is how Rasmussen and Williams
            % (2006) do the update. The above version is
            % more robust.
            %apu = deltatautilde^-1+Sigm(k1,k1);
            %apu = (Sigm(:,k1)/apu)*Sigm(:,k1)';
            %Sigm = Sigm - apu;
            %Sigm=Sigm-(deltatautilde^-1+Sigm(k1,k1))^-1*(Sigm(:,k1)*Sigm(:,k1)');

            %myy=Sigm*nutilde;
            myy=Sigm*(nutilde+nu_prior);

            muvec_i(k1,1)=myy_i;
            sigm2vec_i(k1,1)=sigm2_i;

          end

          if tautilde > 0
            Stilde=tautilde;
            Stildesqroot=diag(sqrt(tautilde));
            B=eye(nt)+Stildesqroot*Sigm0*Stildesqroot;
            L=chol(B,'lower');

            V=(L\Stildesqroot)*Sigm0;
            Sigm=Sigm0-V'*V;
            %myy=Sigm*nutilde;
            myy=Sigm*(nutilde+nu_prior);

            %Ls = chol(Sigm);

            % Compute the marginal likelihood
            % Direct formula (3.65):
            % Sigmtilde=diag(1./tautilde);
            % mutilde=inv(Stilde)*nutilde;
            %
            % logZep=-0.5*log(det(Sigmtilde+K))-0.5*mutilde'*inv(K+Sigmtilde)*mutilde+
            %         sum(log(normcdf(y.*muvec_i./sqrt(1+sigm2vec_i))))+
            %         0.5*sum(log(sigm2vec_i+1./tautilde))+
            %         sum((muvec_i-mutilde).^2./(2*(sigm2vec_i+1./tautilde)))

            % 4. term & 1. term
            term41=0.5*sum(log(1+tautilde.*sigm2vec_i))-sum(log(diag(L)));

            % 5. term (1/2 element) & 2. term
            T=1./sigm2vec_i;
            Cnutilde = Sigm0*nutilde;
            L2 = V*nutilde;
            term52 = nutilde'*Cnutilde - L2'*L2 - (nutilde'./(T+Stilde)')*nutilde;
            term52 = term52.*0.5;

            % 5. term (2/2 element)
            term5=0.5*muvec_i'.*(T./(Stilde+T))'*(Stilde.*muvec_i-2*nutilde);

            % 3. term
            term3 = sum(logM0);

            V_tmp=(L\Stildesqroot);
            Sigm_inv_tmp=V_tmp'*V_tmp;

            term_add1=-0.5*myy0'*Sigm_inv_tmp*myy0;
            term_add2=myy0'*(eye(nt)-Sigm_inv_tmp*Sigm0)*nutilde;
            logZep = -(term41+term52+term5+term3+term_add1+term_add2);

            %logZep = -(term41+term52+term5+term3);

            small_ep_iter=small_ep_iter+1;
            %iter=iter+1;
          else
            error('tautilde <= 0')
          end
        end

        ZZ(j1,1)=exp(-logZep);
        MM(:,j1)=myy;
        SS2(:,:,j1)=Sigm;

      end

      %m_0=zeros(1,1);
      %m_1=zeros(nt+1,1);
      %sigm2hati1=zeros(nt+1,nt+1);
      % indf

      W=normpdf(fg,M_i(end),sqrt(S2_i(end,end)))*(fg(2)-fg(1));

      qw=W.*ZZ';
      m_0=sum(qw);
      m_1=[sum(bsxfun(@times,qw,MM),2); sum(qw.*fg)]./m_0;

      m_211=zeros(nt,nt);
      for k1=1:ng
        m_211=m_211+qw(k1)*(SS2(:,:,k1)+MM(:,k1)*MM(:,k1)');
      end
      m_212=(qw.*fg)*MM';
      m_222=(qw.*fg)*fg';

      m_2=[m_211 m_212'; m_212 m_222]./m_0;

      sigm2hati1=m_2 - m_1*m_1';
      logM_0 = log(m_0);

      %figure(1),hold on, plot(fg(j1),logZep,'.')
      %figure(2),hold on, plot(fg(j1),exp(-logZep),'.')

  end

  function [lpyt,Ey, Vary] = lik_coxph_predy(lik, Ef, Covf, yt, zt)
  %LIK_COXPH_PREDY  Returns the predictive mean, variance and density of y
  %
  %  Description
  %    [EY, VARY] = LIK_COXPH_PREDY(LIK, EF, VARF) takes a
  %    likelihood structure LIK, posterior mean EF and posterior
  %    Variance VARF of the latent variable and returns the
  %    posterior predictive mean EY and variance VARY of the
  %    observations related to the latent variables. This
  %    subfunction is needed when computing posterior predictive
  %    distributions for future observations.
  %
  %    [Ey, Vary, PY] = LIK_COXPH_PREDY(LIK, EF, VARF YT, ZT)
  %    Returns also the predictive density of YT, that is
  %        p(yt | zt) = \int p(yt | f, zt) p(f|y) df.
  %    This requires also the incedence counts YT, expected counts ZT.
  %    This subfunction is needed when computing posterior predictive
  %    distributions for future observations.
  %
  %  See also
  %    GPLA_PRED, GPEP_PRED, GPMC_PRED

    if isempty(zt)
      error(['lik_coxph -> lik_coxph_predy: missing zt!'...
             'Coxph likelihood needs the expected number of    '...
             'occurrences as an extra input zt. See, for         '...
             'example, lik_coxph and gpla_e.               ']);
    end
    ntime=size(lik.xtime,1);

    ntest=size(zt,1);
    ny=size(yt,2);

    if isfield(lik, 'stratificationVariables')
      nf1=lik.n_u*ntime;
      i3v=zeros(ntest,1);
      for ii=1:length(lik.stratindt)
        i3v(lik.stratindt{ii})=ii;
      end
    else
      nf1=ntime;
      i3v=ones(ntest,1);
    end

    Py = zeros(size(zt));
    %Ey = zeros(size(zt));
    %EVary = zeros(size(zt));
    %VarEy = zeros(size(zt));

    S=10000;
    sd=lik.stime(2)-lik.stime(1);
    nu=1-zt;

    [nn1,nn2,c] =size(Covf);
    if (c>1) || (nn1==nn2)
      mcmc=false;
    else
      mcmc=true;
    end

    for i1=1:ntest
      i3=i3v(i1);
      if mcmc
        Sigm_tmp=([Covf(1:ntime,:); Covf(i1+ntime,:)]);
        f_star=bsxfun(@plus,Ef([(1+(i3-1)*ntime):(i3*ntime) i1+nf1])', ...
          bsxfun(@times,sqrt(Sigm_tmp'),randn(S,nf1+1)));
      else
        Sigm_tmp=Covf([(1+(i3-1)*ntime):(i3*ntime) i1+nf1],[(1+(i3-1)*ntime):(i3*ntime) i1+nf1]);
        Sigm_tmp=(Sigm_tmp+Sigm_tmp')./2;
        f_star=mvnrnd(Ef([(1+(i3-1)*ntime):(i3*ntime) i1+nf1]), Sigm_tmp, S);
      end

      f1=f_star(:,1:ntime);
      f2=f_star(:,(ntime+1):end);

      la1=exp(f1);
      eta2=exp(f2);

      if ny==1
        si=sum(yt(i1)>lik.stime);
        Py(i1)=mean(exp(nu(i1).*(f1(:,si)+f2) - (yt(i1)-lik.stime(si)).*la1(:,si).*eta2 ...
                     - sum(sd.*la1(:,1:(si-1)),2).*eta2));
      else

        sb=sum(bsxfun(@gt,yt(i1,1),lik.stime),2);
        se=sum(bsxfun(@gt,yt(i1,2),lik.stime),2);

        if sb==0
          Py(i1) = mean(exp(nu(i1).*(f1(:,se)+f2) - (yt(i1,2)-lik.stime(se)).*la1(:,se).*eta2 - sum(sd.*la1(:,1:(se-1)),2).*eta2));
        else
          if se==sb
            Py(i1) = mean(exp(nu(i1).*(f1(:,se)+f2) - (yt(i1,2)-yt(i1,1)).*la1(:,se).*eta2));
          else
            Py(i1) = mean(exp(nu(i1).*(f1(:,se)+f2) - (yt(i1,2)-lik.stime(se)).*la1(:,se).*eta2 - sum(sd.*la1(:,(sb+1):(se-1)),2).*eta2 - (lik.stime(sb+1)-yt(i1,1)).*la1(:,sb).*eta2));
          end
        end
      end
    end
    Ey = [];
    Vary = [];
    lpyt=log(Py);

    %     % Evaluate Ey and Vary
%     for i1=1:length(Ef)
%       %%% With quadrature
%       myy_i = Ef(i1);
%       sigm_i = sqrt(Varf(i1));
%       minf=myy_i-6*sigm_i;
%       maxf=myy_i+6*sigm_i;
%
%       F = @(f) exp(log(avgE(i1))+f+norm_lpdf(f,myy_i,sigm_i));
%       Ey(i1) = quadgk(F,minf,maxf);
%
%       F2 = @(f) exp(log(avgE(i1).*exp(f)+((avgE(i1).*exp(f)).^2/r))+norm_lpdf(f,myy_i,sigm_i));
%       EVary(i1) = quadgk(F2,minf,maxf);
%
%       F3 = @(f) exp(2*log(avgE(i1))+2*f+norm_lpdf(f,myy_i,sigm_i));
%       VarEy(i1) = quadgk(F3,minf,maxf) - Ey(i1).^2;
%     end
%     Vary = EVary + VarEy;
%
%     % Evaluate the posterior predictive densities of the given observations
%     if nargout > 2
%       for i1=1:length(Ef)
%         % get a function handle of the likelihood times posterior
%         % (likelihood * posterior = Negative-binomial * Gaussian)
%         % and useful integration limits
%         [pdf,minf,maxf]=init_coxph_norm(...
%           yt(i1),Ef(i1),Varf(i1),avgE(i1),r);
%         % integrate over the f to get posterior predictive distribution
%         Py(i1) = quadgk(pdf, minf, maxf);
%       end
%     end
  end

  function [logM_0, m_1, sigm2hati1] = coxph_tiltedMoments(sigm2_i, myy_i, nutmp, sd, c1)

  integrand = @(f) exp(-c1.*exp(f).*sd + nutmp*(f+log(c1)) - log(sigm2_i)/2 - log(2*pi)/2 - 0.5*(f-myy_i).^2./sigm2_i);
  RTOL = 1.e-6;
  ATOL = 1.e-10;
  minf=myy_i+sqrt(sigm2_i)*(-6);
  maxf=myy_i+sqrt(sigm2_i)*(6);

  [m_0, m_1, m_2] = quad_moments(integrand, minf, maxf, RTOL, ATOL);
  sigm2hati1 = m_2 - m_1.^2;

  % If the second central moment is less than cavity variance
  % integrate more precisely. Theoretically for log-concave
  % likelihood should be sigm2hati1 < sigm2_i.

  if sigm2hati1 >= sigm2_i
    ATOL = ATOL.^2;
    RTOL = RTOL.^2;
    [m_0, m_1, m_2] = quad_moments(tf, minf, maxf, RTOL, ATOL);
    sigm2hati1 = m_2 - m_1.^2;
    if sigm2hati1 >= sigm2_i
      error('lik_poisson_tilted_moments: sigm2hati1 >= sigm2_i');
    end
  end
  logM_0 = log(m_0);
  end

  function [df,minf,maxf] = init_coxph_norm(yy,myy_i,sigm2_i,avgE,r)
  %INIT_COXPH_NORM
  %
  %  Description
  %    Return function handle to a function evaluating
  %    Negative-Binomial * Gaussian which is used for evaluating
  %    (likelihood * cavity) or (likelihood * posterior) Return
  %    also useful limits for integration. This is private function
  %    for lik_coxph.
  %
  %  See also
  %    LIK_COXPH_TILTEDMOMENTS, LIK_COXPH_SITEDERIV,
  %    LIK_COXPH_PREDY

  % avoid repetitive evaluation of constant part
    ldconst = -gammaln(r)-gammaln(yy+1)+gammaln(r+yy)...
              - log(sigm2_i)/2 - log(2*pi)/2;
    % Create function handle for the function to be integrated
    df = @coxph_norm;
    % use log to avoid underflow, and derivates for faster search
    ld = @log_coxph_norm;
    ldg = @log_coxph_norm_g;
    ldg2 = @log_coxph_norm_g2;

    % Set the limits for integration
    % Negative-binomial likelihood is log-concave so the coxph_norm
    % function is unimodal, which makes things easier
    if yy==0
      % with yy==0, the mode of the likelihood is not defined
      % use the mode of the Gaussian (cavity or posterior) as a first guess
      modef = myy_i;
    else
      % use precision weighted mean of the Gaussian approximation
      % of the Negative-Binomial likelihood and Gaussian
      mu=log(yy/avgE);
      s2=(yy+r)./(yy.*r);
      modef = (myy_i/sigm2_i + mu/s2)/(1/sigm2_i + 1/s2);
    end
    % find the mode of the integrand using Newton iterations
    % few iterations is enough, since the first guess in the right direction
    niter=4;       % number of Newton iterations
    mindelta=1e-6; % tolerance in stopping Newton iterations
    for ni=1:niter
      g=ldg(modef);
      h=ldg2(modef);
      delta=-g/h;
      modef=modef+delta;
      if abs(delta)<mindelta
        break
      end
    end
    % integrand limits based on Gaussian approximation at mode
    modes=sqrt(-1/h);
    minf=modef-8*modes;
    maxf=modef+8*modes;
    modeld=ld(modef);
    iter=0;
    % check that density at end points is low enough
    lddiff=20; % min difference in log-density between mode and end-points
    minld=ld(minf);
    step=1;
    while minld>(modeld-lddiff)
      minf=minf-step*modes;
      minld=ld(minf);
      iter=iter+1;
      step=step*2;
      if iter>100
        error(['lik_coxph -> init_coxph_norm: ' ...
               'integration interval minimun not found ' ...
               'even after looking hard!'])
      end
    end
    maxld=ld(maxf);
    step=1;
    while maxld>(modeld-lddiff)
      maxf=maxf+step*modes;
      maxld=ld(maxf);
      iter=iter+1;
      step=step*2;
      if iter>100
        error(['lik_coxph -> init_coxph_norm: ' ...
               'integration interval maximun not found ' ...
               'even after looking hard!'])
      end
    end

%     while minld>(modeld-lddiff)
%       minf=minf-modes;
%       minld=ld(minf);
%       iter=iter+1;
%       if iter>100
%         error(['lik_coxph -> init_coxph_norm: ' ...
%                'integration interval minimun not found ' ...
%                'even after looking hard!'])
%       end
%     end
%     maxld=ld(maxf);
%     while maxld>(modeld-lddiff)
%       maxf=maxf+modes;
%       maxld=ld(maxf);
%       iter=iter+1;
%       if iter>100
%         error(['lik_coxph -> init_coxph_norm: ' ...
%                'integration interval maximum not found ' ...
%                'even after looking hard!'])
%       end
%
%     end

    function integrand = coxph_norm(f)
    % Negative-binomial * Gaussian
      mu = avgE.*exp(f);
      integrand = exp(ldconst ...
                      +yy.*(log(mu)-log(r+mu))+r.*(log(r)-log(r+mu)) ...
                      -0.5*(f-myy_i).^2./sigm2_i);
    end

    function log_int = log_coxph_norm(f)
    % log(Negative-binomial * Gaussian)
    % log_coxph_norm is used to avoid underflow when searching
    % integration interval
      mu = avgE.*exp(f);
      log_int = ldconst...
                +yy.*(log(mu)-log(r+mu))+r.*(log(r)-log(r+mu))...
                -0.5*(f-myy_i).^2./sigm2_i;
    end

    function g = log_coxph_norm_g(f)
    % d/df log(Negative-binomial * Gaussian)
    % derivative of log_coxph_norm
      mu = avgE.*exp(f);
      g = -(r.*(mu - yy))./(mu.*(mu + r)).*mu ...
          + (myy_i - f)./sigm2_i;
    end

    function g2 = log_coxph_norm_g2(f)
    % d^2/df^2 log(Negative-binomial * Gaussian)
    % second derivate of log_coxph_norm
      mu = avgE.*exp(f);
      g2 = -(r*(r + yy))/(mu + r)^2.*mu ...
           -1/sigm2_i;
    end

  end

  function p = lik_coxph_invlink(lik, f, z)
  %LIK_COXPH_INVLINK  Returns values of inverse link function
  %
  %  Description
  %    P = LIK_COXPH_INVLINK(LIK, F) takes a likelihood structure LIK and
  %    latent values F and returns the values of inverse link function P.
  %    This subfunction is needed when using function gp_predprcty.
  %
  %     See also
  %     LIK_COXPH_LL, LIK_COXPH_PREDY

    p = exp(f);
  end

  function [cdf,Ey,Vary] = lik_coxph_predcdf(lik,Ef,Covf,yt)
  %LIK_LOGLOGISTIC_PREDCDF  Returns the predictive cdf evaluated at yt
  %
  %  Description
  %    CDF = LIK_LOGLOGISTIC_PREDCDF(LIK, EF, VARF, YT)
  %    Returns the predictive cdf evaluated at YT given likelihood
  %    structure LIK, posterior mean EF and posterior Variance VARF
  %    of the latent variable. This subfunction is needed when using
  %    functions gp_predcdf or gp_kfcv_cdf.
  %
  %  See also
  %    GP_PREDCDF

    if isfield(lik, 'stratificationVariables')
      ntime=size(lik.xtime,1)*lik.n_u;
    else
      ntime=size(lik.xtime,1);
    end
    Ef1 = Ef(1:ntime); Ef(1:ntime) = []; Ef2 = Ef;
    nsamps = 10000;
    sd=lik.stime(2)-lik.stime(1);
    Sigm_tmp=Covf;
    [nn1,nn2,cc]=size(Sigm_tmp);
    if cc==1 && nn1~=nn2
      f_star=bsxfun(@plus,[Ef1;Ef2]', ...
        bsxfun(@times,sqrt(Sigm_tmp'),randn(nsamps,ntime+size(Ef2,1))));
    else
      Sigm_tmp=(Sigm_tmp+Sigm_tmp')./2;
      % f_star=mvnrnd(Ef1, Sigm_tmp(1:ntime,1:ntime), nsamps);
      f_star=mvnrnd([Ef1;Ef2], Sigm_tmp, nsamps);
    end

    f1=f_star(:,1:ntime);
    f2=f_star(:,(ntime+1):end);

    la1=exp(f1);
    eta2=exp(f2);

    mST=zeros(size(eta2,2),1);
    if size(yt,2) == 1
      % Integrate from zero to yt
      cumsumtmp=cumsum(la1'*sd)';
      %   t=binsgeq(gp.lik.xtime,yt(i));
      for i1=1:size(eta2,2)
        Stime=exp(-bsxfun(@times,cumsumtmp,eta2(:,i1)));
        mStime=mean(Stime);
        % for i=1:size(yt,1)
        mST(i1)=mStime(binsgeq(lik.xtime,yt(i1)));
        %end
      end
      cdf = 1- mST;

    else
      error('Size(yt,2) ~= 1');
    end

  end

  function reclik = lik_coxph_recappend(reclik, ri, lik)
  %RECAPPEND  Append the parameters to the record
  %
  %  Description
  %    RECLIK = GPCF_COXPH_RECAPPEND(RECLIK, RI, LIK) takes a
  %    likelihood record structure RECLIK, record index RI and
  %    likelihood structure LIK with the current MCMC samples of
  %    the parameters. Returns RECLIK which contains all the old
  %    samples and the current samples from LIK. This subfunction
  %    is needed when using MCMC sampling (gp_mc).
  %
  %  See also
  %    GP_MC

  % Initialize record
    if nargin == 2
      reclik=ri;

      % Set the function handles
      reclik.fh.pak = @lik_coxph_pak;
      reclik.fh.unpak = @lik_coxph_unpak;
      reclik.fh.lp = @lik_coxph_lp;
      reclik.fh.lpg = @lik_coxph_lpg;
      reclik.fh.ll = @lik_coxph_ll;
      reclik.fh.llg = @lik_coxph_llg;
      reclik.fh.llg2 = @lik_coxph_llg2;
      reclik.fh.llg3 = @lik_coxph_llg3;
      reclik.fh.tiltedMoments = @lik_coxph_tiltedMoments;
      reclik.fh.predy = @lik_coxph_predy;
      reclik.fh.invlink = @lik_coxph_invlink;
      reclik.fh.recappend = @lik_coxph_recappend;
      return
    else

      reclik.xtime=lik.xtime;
      reclik.stime=lik.stime;

      if isfield(lik, 'stratificationVariables')
        reclik.stratificationVariables=lik.stratificationVariables;
        if isfield(lik,removeStratificationVariables)
          reclik.removeStratificationVariables=lik.removeStratificationVariables;
        end
      end
    end
  end

end


t=0:1:17
delta=2
tau=1.05
n=3;
hrf=gamma1D(t,delta,tau,n); hrf(1)=0;


function K = symbol_string(kern,dat1,dat2,ind1,ind2,kerParam)
% --- conversion to sequences------------------
D= []; Xf = get_x(dat1); Xs = get_x(dat2); X1 = {}; X2 = {};
for i = 1:size(Xf,1), tmp = Xf(i,:);  X1{i} = tmp(tmp > 0); end
for i = 1:size(Xs,1),  tmp = Xs(i,:);  X2{i} = tmp(tmp > 0);end

n = kerParam{1};
lambda = kerParam{2};

for i = 1:size(X1,1)
    for j = i:size(X2,1)
       K(i,j) =  k(X1{i},X2{j},n,lambda)
    end
end

%------------------------------------------------------
function kret = k(s,t,n,lambda)
        if min(length(s),length(t)) < n
            kret = 0; return
        else
            kret = k(s(1:end-1),t,n,lambda);
            ind = find(t == s(end));
            for i = 1:length(ind)
                kret = kret + k_prime();
            end
        end

%----------------------------------------------------------
function ret = k_prime(s,t,lambda,i)
    if n == 0
        ret = 1; return;
    elseif min(length(s),length(t)) < i
        ret = 0; return;
    end