function dfdocontext(varargin)
%DFDOCONTEXT Perform context menu actions for distribution fitting tool

% Copyright 2001-2004 The MathWorks, Inc.
% $Revision: 1.1.6.7 $  $Date: 2004/03/09 16:17:04 $
import com.mathworks.toolbox.stats.*;

% Special action to create context menus
if isequal(varargin{1},'create')
   makecontextmenu(varargin{2});
   return
end

% Get information about what invoked this function
obj = gcbo;
action = get(obj,'Tag');
h = gco;
if isempty(h), return; end
dffig = gcbf;

% Set up variables that define some menu items
[sizes styles markers] = getmenuitems;
styles{end+1} = 'none';

changed = true;   % did a line property change?

switch action

 % This case is triggered when we display the menu
 case {'fitcontext' 'datacontext'}
   % Store a handle to the object that triggered this menu
   set(obj,'UserData',h);

   hObject = get(h,'UserData');
   c = findall(obj,'Type','uimenu');
   hBounds = findall(c,'flat','Tag','confbounds');
   ftype = dfgetset('ftype');

   % Enable or disable as appropriate
   hMarker = findall(c,'flat','Tag','marker');
   if isequal(action,'datacontext')
      hLineStyle = findall(c,'flat','Tag','linestyle');
      hLineWidth = findall(c,'flat','Tag','linewidth');
      hBinRules = findall(c,'flat','Tag','binrules');
      if isequal(ftype,'probplot')
         set(hMarker,'Enable','on');
         set(hLineStyle,'Enable','off');
         set(hLineWidth,'Enable','off');
         set(hBinRules,'Enable','off');
      elseif isequal(ftype,'pdf')
         set(hMarker,'Enable','off');
         set(hLineStyle,'Enable','on');
         set(hLineWidth,'Enable','on');
         set(hBinRules,'Enable','on');
      else
         set(hMarker,'Enable','off');
         set(hLineStyle,'Enable','on');
         set(hLineWidth,'Enable','on');
         set(hBinRules,'Enable','off');
      end
   else
      if ~hObject.iscontinuous && isequal(ftype,'pdf')
         set(hMarker,'Enable','on');
      else
         set(hMarker,'Enable','off');
      end
   end
   try
      hasconfbounds = hObject.distspec.hasconfbounds;
   catch
      hasconfbounds = false;
   end
   if isequal(ftype,'pdf') || isequal(ftype,'probplot') || ...
      (isequal(action,'fitcontext') && ~hasconfbounds) || ...
      (isequal(action,'datacontext') && isequal(ftype,'icdf'))
      set(hBounds,'Enable','off');
   else
      set(hBounds,'Enable','on');
   end

   set(c,'Checked','off');

   % Fix check mark for confidence bounds
   if hObject.showbounds
      set(hBounds,'Checked','on');
   end

   % Fix check marks on line width and line style cascading menus
   w = get(h,'LineWidth');
   u = findall(c,'flat','Tag',num2str(w));
   if ~isempty(u)
      set(u,'Checked','on');
   end
   w = get(h,'LineStyle');
   u = findall(c,'flat','Tag',w);
   if ~isempty(u)
      set(u,'Checked','on');
   end
   w = get(h,'Marker');
   u = findall(c,'flat','Tag',w);
   if ~isempty(u)
      set(u,'Checked','on');
   end
   return

 % Remaining cases are triggered by selecting menu items
 case 'confbounds'
   hObject = get(h,'UserData');
   hObject.showbounds = ~hObject.showbounds;
   nm = get(hObject,'name');
   htag = get(h,'Tag');
   if isequal(htag,'dfdata')
      DataSetsManager.getDataSetsManager.dataSetChanged(java(hObject),nm,nm);
   else
      FitsManager.getFitsManager.fitChanged(java(hObject),nm,nm);
   end

 case 'color'
   oldcolor = get(h,'Color');
   newcolor = uisetcolor(oldcolor);
   if ~isequal(oldcolor,newcolor)
      set(h,'Color',newcolor);
   end

 case styles
   set(h,'LineStyle',action);

 case markers
   if isequal(action,'point')
      msize = 12;
   else
      msize = 6;
   end
   set(h,'Marker',action,'MarkerSize',msize);

 % Either delete a fit, or a hide a fit or data set
 case {'hidecurve' 'deletefit'}
   htag = get(h,'Tag');
   if isequal(htag,'distfit') || isequal(htag,'dfdata')
      hndl = get(h,'UserData');
      if isequal(action,'hidecurve')
         hndl.plot = 0;
         nm = get(hndl,'name');
      else
         % The delete action appears on the fit menu only, not the data set menu
         FitsManager.getFitsManager.deleteFits(java(hndl));
      end
   end
   changed = false;

 % Edit a fit
 case 'editfit'
   htag = get(h,'Tag');
   if isequal(htag,'distfit')  % should always be true
      hndl = get(h,'UserData');
      FitsManager.getFitsManager.editFit(hndl.name);
   end
   changed = false;

 % Bring up the "Set Bin Width Rules" dialog for this data set
 case 'binrules'
   htag = get(h,'Tag');
   if isequal(htag,'dfdata')
      hndl = get(h,'UserData');
      nm = get(hndl,'name');
      bw = com.mathworks.toolbox.stats.BinWidth.getBinWidth; % get dialog
      bw.displayBinWidth(nm); % display dialog for this data set
   end
   changed = false;

 % If the menu item is a number, it is a line width
 otherwise
   j = str2num(action);
   if ~isempty(j)
      set(h,'LineWidth',j);
   end

end

if changed
   % Save plot info in the fit or data set object
   hObject = get(h,'UserData');
   savelineproperties(hObject);

   % Update legend
   dfupdatelegend(dffig);
end

% ---------------------- helper to make context menu
function makecontextmenu(dffig)
%MAKECONTEXTMENU Creates context menu for curve fitting figure

% Create context menus for fits, data curve, probability plot data curves
cFit = uicontextmenu('Parent',dffig,'Tag','fitcontext','Callback',@dfdocontext);
uimenu(cFit,'Label','Color...','Tag','color','Callback',@dfdocontext);

% Add menu items for line and marker control
uwidth = uimenu(cFit,'Label','Line &Width','Tag','linewidth');
ustyle = uimenu(cFit,'Label','Line &Style','Tag','linestyle');
umark = uimenu(cFit,'Label','Marker','Tag','marker','Position',2);

% Add menu items to control confidence bounds
uimenu(cFit,'Label','Confidence &Bounds','Callback',@dfdocontext,...
            'Tag','confbounds');

% Get menu item labels and tags
[sizes styles markers slabels mlabels] = getmenuitems;

for j=1:length(markers)
   uimenu(umark,'Label',mlabels{j},'Callback',@dfdocontext,'Tag',markers{j});
end

% Sub-menus for line widths
for i = 1:length(sizes)
   val = num2str(sizes(i));
   uimenu(uwidth,'Label',val,'Callback',@dfdocontext,'Tag',val);
end

% Sub-menus for line styles
for j=1:length(styles)
   uimenu(ustyle,'Label',slabels{j},'Callback',@dfdocontext,'Tag',styles{j});
end

% Copy the fit menu to create a data menu
cData = copyobj(cFit,dffig);
set(cData,'Tag','datacontext')

% Add items for fit menus only
uimenu(cFit,'Label','&Hide Fit','Tag','hidecurve','Callback',@dfdocontext,...
       'Separator','on');
uimenu(cFit,'Label','&Delete Fit','Tag','deletefit','Callback',@dfdocontext);
uimenu(cFit,'Label','&Edit Fit','Tag','editfit','Callback',@dfdocontext);

% Add items for data menus only
uimenu(cData,'Label','&Hide Data','Tag','hidecurve',...
       'Callback',@dfdocontext,'Separator','on');
uimenu(cData,'Label','Set Bin &Rules','Tag','binrules',...
       'Callback',@dfdocontext,'Separator','on');

% -------------- helper to get menu item labels
function [sizes,styles,markers,slabels,mlabels] = getmenuitems
%GETMENUITEMS Get items for curve fitting context menus
sizes = [0.5 1 2 3 4 5 6 7 8 9 10];
styles = {'-' '--' ':' '-.'};
markers = {'+' 'o' '*' '.' 'x' 'square' 'diamond' ...
        'v' '^' '<' '>' 'pentagram' 'hexagram'};
slabels = {'solid' 'dash' 'dot' 'dash-dot'};
mlabels = {'plus' 'circle' 'star' 'point' 'x-mark' 'square' 'diamond' ...
           'triangle (down)' 'triangle (up)' 'triangle (left)' ...
           'triangle (right)' 'pentagram' 'hexagram'};


function tsss = tsss_config
% configuration file for cropping
%__________________________________________________________________________
% Copyright (C) 2014 Wellcome Trust Centre for Neuroimaging

% Vladimir Litvak
% $Id: tsss_config.m 7703 2019-11-22 12:06:29Z guillaume $

%--------------------------------------------------------------------------
% D
%--------------------------------------------------------------------------
D        = cfg_files;
D.tag    = 'D';
D.name   = 'File Name';
D.filter = 'mat';
D.num    = [1 1];
D.help   = {'Select the M/EEG mat file.'};

%--------------------------------------------------------------------------
% temporal
%--------------------------------------------------------------------------

temporal = cfg_menu;
temporal.tag = 'temporal';
temporal.name = 'Do temporal denoising';
temporal.labels = {'Yes', 'No'};
temporal.val = {1};
temporal.values = {1,0};
temporal.help = {'Determines whether to use temporal denoising (tSSS)',...
    'if not, just SSS is done.'};

%--------------------------------------------------------------------------
% realign
%--------------------------------------------------------------------------
Dref        = cfg_files;
Dref.tag    = 'Dref';
Dref.name   = 'Reference dataset';
Dref.filter = 'mat';
Dref.num    = [0 1];
Dref.val    = {[]};
Dref.help   = {'Select the M/EEG mat file.',...
    'Leave empty to realign within dataset'};

refind         = cfg_entry;
refind.tag     = 'refind';
refind.name    = 'Reference index';
refind.help    = {'Index of the reference sensors within dataset',...
    'Normally should be 1. Set to 0 for not realigning a composite dataset'};
refind.strtype = 'w';
refind.num     = [1 1];
refind.val     = {1};

realign         = cfg_branch;
realign.tag      = 'realign';
realign.name     = 'Realign head location';
realign.val      = {Dref, refind};
realign.help     = {'Realign head location to another dataset'}';

%--------------------------------------------------------------------------
% timewin
%--------------------------------------------------------------------------
timewin         = cfg_entry;
timewin.tag     = 'timewin';
timewin.name    = 'Time window';
timewin.help    = {'Time window (in sec) for temporal correlation',...
    'Ignored for epoched data'};
timewin.strtype = 'r';
timewin.num     = [1 1];
timewin.val     = {1};

%--------------------------------------------------------------------------
% corrlimit
%--------------------------------------------------------------------------
corrlimit         = cfg_entry;
corrlimit.tag     = 'corrlimit';
corrlimit.name    = 'Correlation limit';
corrlimit.help    = {'Correlation limit parameter for tSSS'};
corrlimit.strtype = 'r';
corrlimit.num     = [1 1];
corrlimit.val     = {0.98};

%--------------------------------------------------------------------------
% Lin
%--------------------------------------------------------------------------
Lin         = cfg_entry;
Lin.tag     = 'Lin';
Lin.name    = 'Inner dimension';
Lin.help    = {'Order if the inner SSS basis'};
Lin.strtype = 'n';
Lin.num     = [1 1];
Lin.val     = {8};

%--------------------------------------------------------------------------
% Lout
%--------------------------------------------------------------------------
Lout         = cfg_entry;
Lout.tag     = 'Lout';
Lout.name    = 'Outer dimension';
Lout.help    = {'Order if the outer SSS basis'};
Lout.strtype = 'n';
Lout.num     = [1 1];
Lout.val     = {3};

%--------------------------------------------------------------------------
% condthresh
%--------------------------------------------------------------------------
condthresh         = cfg_entry;
condthresh.tag     = 'condthresh';
condthresh.name    = 'Condition number threshold';
condthresh.help    = {'Threshold on condition number applied for basis regularisation'};
condthresh.strtype = 'r';
condthresh.num     = [1 1];
condthresh.val     = {80};

%--------------------------------------------------------------------------
% ospace
%--------------------------------------------------------------------------

ospace = cfg_menu;
ospace.tag = 'ospace';
ospace.name = 'Output space';
ospace.labels = {'sensor', 'SSS'};
ospace.val = {0};
ospace.values = {0,1};
ospace.help = {'Determines whether the output file is in sensor space',...
    'or has virtual montage trasforming to SSS space.'};

%--------------------------------------------------------------------------
% prefix
%--------------------------------------------------------------------------
prefix         = cfg_entry;
prefix.tag     = 'prefix';
prefix.name    = 'Filename Prefix';
prefix.help    = {'Specify the string to be prepended to the filenames of the output dataset. Default prefix is ''tsss_''.'};
prefix.strtype = 's';
prefix.num     = [1 Inf];
prefix.val     = {'tsss_'};

%--------------------------------------------------------------------------
% tsss
%--------------------------------------------------------------------------
tsss          = cfg_exbranch;
tsss.tag      = 'tsss';
tsss.name     = 'TSSS denoising';
tsss.val      = {D, temporal, realign, timewin, corrlimit, Lin, Lout, condthresh, ospace, prefix};
tsss.help     = {'TSSS clean-up for Neuromag data'}';
tsss.prog     = @eeg_tsss;
tsss.vout     = @vout_eeg_tsss;
tsss.modality = {'EEG'};

%==========================================================================
function out = eeg_tsss(job)
% construct the S struct
S = [];
S.D = char(job.D);
S.tsss       = job.temporal;
S.Dref       = char(job.realign.Dref);
S.refind     = job.realign.refind;
S.t_window   = job.timewin;
S.corr_limit = job.corrlimit;
S.Lin        = job.Lin;
S.Lout       = job.Lout;
S.cond_threshold = job.condthresh;
S.xspace     = job.ospace;
S.prefix     = job.prefix;
out.D        = tsss_spm_enm(S);
out.Dfname   = {fullfile(out.D)};

%==========================================================================
function dep = vout_eeg_tsss(job)
% return dependencies
dep(1)            = cfg_dep;
dep(1).sname      = 'TSSS-ed MEG data';
dep(1).src_output = substruct('.','D');
dep(1).tgt_spec   = cfg_findspec({{'strtype','e'}});

dep(2)            = cfg_dep;
dep(2).sname      = 'TSSS-ed MEG datafile';
dep(2).src_output = substruct('.','Dfname');
dep(2).tgt_spec   = cfg_findspec({{'filter','mat'}});


function output = callpenlab(model)

model = yalmip2nonlinearsolver(model);

if ~model.derivative_available
    disp('Derivate-free call to penlab not supported')
    error('Derivate-free call to penlab not implemented')
end
if model.options.savedebug
    save penlabdebug model
end

penm = [];
penm.Nx=length(model.linearindicies);

if length(nnz(model.K.s)>0)
    top = 1 + model.K.f + model.K.l + sum(model.K.q);
    for i = 1:length(model.K.s)
        model.vecF{i} = model.F_struc(top:top+model.K.s(i)^2-1,:);
        top = top + model.K.s(i)^2;
    end
end

showprogress('Calling PENLAB',model.options.showprogress);

% These are needed to avoid recomputation due to penlab double call to get
% f and df, and g and dg
global latest_x_f
global latest_x_g
global latest_df
global latest_f
global latest_G
global latest_g
global latest_xevaled
global latest_x_xevaled
latest_G = [];
latest_g = [];
latest_x_f = [];
latest_x_g = [];
latest_xevaled = [];
latest_x_xevaled = [];

penm.userdata=model;

penm.objfun =  @(x,Y,userdata) penlab_callback_f(x,userdata);
penm.objgrad = @(x,Y,userdata) penlab_callback_df(x,userdata);
penm.objhess = @(x,Y,userdata) penlab_callback_df2(x,userdata);

if length(model.b)>0 | length(model.beq)>0
    penm.lbg = [model.beq;model.b-inf];
    penm.ubg = [model.beq;model.b];
    penm.NgLIN = length(model.beq) + length(model.b);
    penm.confun =  @(x,Y,userdata) penlab_callback_con(x,userdata);
    penm.congrad = @(x,Y,userdata) penlab_callback_dcon(x,userdata);
    penm.conhess = @(x,Y,k,userdata) penlab_callback_dcon2(x,k,userdata);
end

if model.K.s(1)>0
    penm.NALIN=length(model.K.s);
    penm.lbA=zeros(find(model.K.s)>0,1);
    penm.mconfun  = @(x,Y,k,userdata) penlab_callback_matrixG(x, k,userdata);
    penm.mcongrad = @(x,Y,k,i,userdata) penlab_callback_matrixdG(x,k,i,userdata);
end

prob=penlab(penm);

solvertime = tic;
prob.solve();
solvertime = toc(solvertime);
x = prob.x;

% Duals currently not supported
lambda = [];

switch 0
    case {0,1}
        problem = 0;
    case {2}
        problem = 1;
    case {-1}
        problem = 3;
    case {3,4,-2,-3}
        problem = 4;
    case {-11,-12,-13}
        problem = 7;
    case {-10,-100,-101,-102,-199}
        problem = 11;
    otherwise
        problem = -1;
end

% Internal format for duals
D_struc = [];

% Save all data sent to solver?
if model.options.savesolverinput
    solverinput.model = model;
else
    solverinput = [];
end

% Save all data from the solver?
if model.options.savesolveroutput
    solveroutput.x = xout;
    solveroutput.info = info;
else
    solveroutput = [];
end

% Standard interface
output = createoutput(x,D_struc,[],problem,'PENLAB',solverinput,solveroutput,solvertime);


function [cdf] = gp_kfcv_cdf(gp,x,y,varargin)
%GP_KFCV_CDF  K-fold cross validation to predict CDF for GP model
%
%  Description
%    [cdf] = GP_KFCV_CDF(GP, X, Y, OPTIONS)
%    Performs K-fold cross-validation for a GP model given input matrix X
%    and target vector Y.
%
%    OPTIONS is optional parameter-value pair
%      z          - optional observed quantity in triplet (x_i,y_i,z_i)
%                   Some likelihoods may use this. For example, in
%                   case of Poisson likelihood we have z_i=E_i,
%                   that is, expected value for ith case.
%      yt         - optional observed yt in test points.
%                   Default option for yt is yt=y.
%      inf_method - inference method. Possible methods are
%                    'MAP'      parameters optimized to MAP (default)
%                    'MCMC'     MCMC sampling using GP_MC
%                    'IA'       integration approximation using GP_IA
%                    'fixed'    parameters are fixed, it either use MAP
%                               or integration approximation, depending if
%                               GP is a single GP structure or a GP array
%                               (for example from GP_IA)
%      optimf     - function handle for an optimization function, which is
%                   assumed to have similar input and output arguments
%                   as usual fmin*-functions. Default is @fminscg.
%      opt        - options for the inference method. If 'MAP' is used
%                   use optimset to set options for optimization.
%                   Default options for optimization are 'GradObj'
%                   is 'on', 'LargeScale' is 'off', 'Display' is 'off'
%      k          - number of folds in CV, default k=10
%      rstream    - number of a random stream to be used for
%                   permuting the data befor division. This way
%                   same permutation can be obtained for different
%                   models. Default is 1. See doc RandStream for
%                   more information.
%      trindex    - k-fold CV training indices. A cell array with k
%                   fields each containing index vector for respective
%                   training set.
%      tstindex   - k-fold CV test indices. A cell array with k
%                   fields each containing index vector for
%                   respective test set.
%      display    - defines if messages are displayed.
%                   - 'iter' displays output at each iteration
%
%    The output argument is
%
%           cdf- Predictive Cumulative Distribiton function evaluated in y
%           (default) or yt.
%
%
%  See also
%    DEMO_MODELCOMPARISON1, DEMO_MODELCOMPARISON2
%
% Copyright (c) 2009-2010 Jarno Vanhatalo
% Copyright (c) 2010-2011 Aki Vehtari
% Copyright (c) 2012 Ernesto Ulloa

% 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 = 'GP_KFCV';
ip.addRequired('gp',@(x) isstruct(x) || iscell(x));
ip.addRequired('x', @(x) ~isempty(x) && isreal(x) && all(isfinite(x(:))))
ip.addRequired('y', @(x) ~isempty(x) && isreal(x) && all(isfinite(x(:))))
ip.addParamValue('z', [], @(x) isreal(x) && all(isfinite(x(:))))
ip.addParamValue('yt', [], @(x) isreal(x) && all(isfinite(x(:))))
ip.addParamValue('inf_method', 'MAP', @(x) ...
  ismember(x,{'MAP' 'LOO' 'KFCV' 'WAIC' 'WAICV' 'WAICG' 'MCMC' 'IA' 'fixed'}))
ip.addParamValue('optimf', @fminscg, @(x) isa(x,'function_handle'))
ip.addParamValue('opt', struct(), @isstruct)
ip.addParamValue('k', 10, @(x) isreal(x) && isscalar(x) && isfinite(x) && x>0)
ip.addParamValue('rstream', 1, @(x) isreal(x) && isscalar(x) && isfinite(x) && x>0)
ip.addParamValue('trindex', [], @(x) isempty(x) || iscell(x))
ip.addParamValue('tstindex', [], @(x) isempty(x) || iscell(x))
ip.addParamValue('display', 'on', @(x) islogical(x) || ...
  ismember(x,{'iter' 'fold'}))
ip.parse(gp, x, y, varargin{:});
z=ip.Results.z;
yt=ip.Results.yt;
inf_method=ip.Results.inf_method;
optimf=ip.Results.optimf;
opt=ip.Results.opt;
k=ip.Results.k;
rstream=ip.Results.rstream;
trindex=ip.Results.trindex;
tstindex=ip.Results.tstindex;
display = ip.Results.display;
if isequal(display,'fold');display='iter';end

[n,nin] = size(x);

gp_orig = gp;

if ismember(inf_method,{'MAP' 'LOO' 'KFCV' 'WAIC' 'WAICV' 'WAICG'})
  optdefault=struct('Display','off');
  opt=optimset(optdefault,opt);
end

if (isempty(trindex) && ~isempty(tstindex)) || (~isempty(trindex) && isempty(tstindex))
  error('gp_kfcv: If you give cross-validation indexes, you need to provide both trindex and tstindex.')
end

if isempty(trindex) || isempty(tstindex)
  [trindex, tstindex] = cvit(n, k, rstream);
end

% *** note: yt must can be: a scalar or a vector of size(y)x1 or even a matrix of
% size(y)xN

if ~isempty(yt)
  if size(yt,1)==size(y,1)
    yt=yt;
  elseif size(yt,1)==1
    yt=bsxfun(@times,ones(size(y)),yt);
  else
    error('size of yt does not match y nor is it a scalar');
  end
  ytflag=1;
else
  ytflag=0;
  yt=y;
end

cvws=[];
trw=[];
% loop over the crossvalidation sets

for i=1:length(trindex)
  if isempty(tstindex{i})
    continue
  end

  if isequal(display,'iter')
    fprintf(' The CV-fold number: %d/%d \n', i, k)
  end

  % Set the training and test sets for i'th cross-validation set
  xtr = x(trindex{i},:);
  ytr = y(trindex{i},:);
  yttr= yt(trindex{i},:);

  xtst = x(tstindex{i},:);
  ytst = y(tstindex{i},:);
  yttst = yt(tstindex{i},:);

  if ~isempty(z)
    ztr = z(trindex{i},:);
    zt = z;
    flagz=1;
    %* yt = y;
    %       opt_tst.zt = z;
    %       opt_tst.yt = y;
  else
    flagz=0;
    ztr = [];
    %*yt = y;
    zt = [];
    %       opt_tr = struct();
    %       opt_tst.yt = y;
  end

  gp = gp_orig;

  if iscell(gp)
    gptype=gp{1}.type;
  else
    gptype=gp.type;
  end
  tstind2 = [];

  switch gptype
    case {'FIC' 'CS+FIC'}
      tstind2 = trindex{i};
    case 'PIC'
      % Set the block indices for the cv set of data points. Variable
      % naming(e.g tstind2) because parfor loop.
      ntr = size(xtr,1);
      ntst = size(xtst,1);
      trind2 = [];
      for i1=1:length(gp.tr_index)
        tstind2{i1} = [];
        trind2{i1} = [];
        for j1 = 1:length(gp.tr_index{i1})
          indtmp = find( sum((xtr - repmat(x(gp.tr_index{i1}(j1),:),ntr,1)).^2,2) == 0 );
          if isempty( indtmp )
            indtmp = find( sum((xtst - repmat(x(gp.tr_index{i1}(j1),:),ntst,1)).^2,2) == 0 );
            tstind2{i1} = [tstind2{i1} indtmp];
          else
            trind2{i1} = [trind2{i1} indtmp];
          end
        end
      end
      if iscell(gp)
        for j=1:numel(gp)
          gp{j}.tr_index=trind2;
        end
      else
        gp.tr_index = trind2;
      end
  end

  % Conduct inference
  switch inf_method
    case 'MAP'
      if flagz
        gp=gp_optim(gp,xtr,ytr,'z',ztr(:,size(z,2)),'opt',opt, 'optimf', optimf);
        w=gp_pak(gp);
        cvws(i,:)=w;
      else
        gp=gp_optim(gp,xtr,ytr,'z',ztr,'opt',opt, 'optimf', optimf);
        w=gp_pak(gp);
        cvws(i,:)=w;
      end
    case {'LOO' 'KFCV' 'WAIC' 'WAICV' 'WAICG'}
      if ismember('optimf',ip.UsingDefaults)

        if flagz
          gp=gp_optim(gp,xtr,ytr,'z',ztr(:,size(z,2)),'opt',opt,'loss',inf_method);
        else
          gp=gp_optim(gp,xtr,ytr,'z',ztr,'opt',opt,'loss',inf_method);
        end

      else
        if flagz
          gp=gp_optim(gp,xtr,ytr,'z',ztr(:,size(z,2)),'opt',opt,'loss',inf_method, 'optimf', optimf);
        else
          gp=gp_optim(gp,xtr,ytr,'z',ztr,'opt',opt,'loss',inf_method, 'optimf', optimf);
        end
      end
      w=gp_pak(gp);
      cvws(i,:)=w;
    case 'MCMC'
      if numel(gp.jitterSigma2)>1
        gp=thin(gp,numel(gp.jitterSigma2)-1);
      end
      % Scaled mixture noise model is a special case
      % where we need to modify the noiseSigmas2 vector
      % to a right length
      if isequal(gp.lik.type, 'lik_smt')
        gp.lik.noiseSigmas2 = gp_orig.lik.noiseSigmas2(trindex{i});
        gp.lik.r = gp_orig.lik.r(trindex{i});
        gp.lik.U = gp_orig.lik.U(trindex{i});
        gp.lik.ndata = length(trindex{i});
      end
      % Pick latent values for the training set in this fold
      if isfield(gp,'latentValues')
        if (~isfield(gp.lik, 'nondiagW') || ismember(gp.lik.type, {'Softmax', 'Multinom', ...
            'LGP', 'LGPC'}))
          latentValues=reshape(gp_orig.latentValues, size(y,1), size(y,2));
          gp.latentValues=reshape(latentValues(trindex{i},:), size(y,2)*length(trindex{i}), 1);
          % gp.latentValues=gp_orig.latentValues(trindex{i});
        else
          if ~isfield(gp.lik, 'xtime')
            nl=length(gp.comp_cf);
            gp.latentValues=gp_orig.latentValues(trindex{i}+(0:nl-1)*n);
          else
            ntime=size(gp.lik.xtime,1);
            gp.latentValues=gp_orig.latentValues([1:ntime, (ntime+trindex{i})]);
          end
        end
      end
      if flagz
        gp = gp_mc(gp, xtr, ytr, 'z', ztr(:,size(z,2)), opt);
      else
        gp = gp_mc(gp, xtr, ytr, 'z', ztr, opt);
      end
      nburnin = floor(length(gp.etr)/3);
      gp = thin(gp,nburnin);
    case 'IA'
      if flagz
        [gp,P_TH] = gp_ia(gp, xtr, ytr, 'z', ztr(:,size(z,2)), opt);
      else
        [gp,P_TH] = gp_ia(gp, xtr, ytr, 'z', ztr, opt);
      end
    case 'fixed'
      % nothing to do here
  end

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

  switch inf_method
    case {'LOO' 'KFCV' 'WAIC' 'WAICV' 'WAICG' 'MAP'}
      if ~isfield(gplik.fh,'trcov') && isfield(gp.lik.fh,'predcdf')
        for it=1:size(yt,2)
          [Eft, Varft] = gp_pred(gp, xtr, ytr, x, 'tstind', tstind2, 'z', ztr(:,it), 'yt', yt(:,it), 'zt', zt(:,it));
          cdftemp{it}= gp.lik.fh.predcdf(gplik, Eft, Varft,yt(:,it));
        end
      elseif isfield(gplik.fh,'trcov') && isfield(gp.lik.fh,'predcdf')
        for it=1:size(yt,2)
          [Eft, Varft] = gp_pred(gp, xtr, ytr, x, 'tstind', tstind2,'yt', yt(:,it));
          cdftemp{it}= gp.lik.fh.predcdf(gplik, Eft, Varft,yt(:,it));
        end
      else
        error('This likelihood has not been implemented for this function')
      end
    case 'MCMC'
      nsamples=size(gp.etr,1);
      for i2=1:nsamples
        Gp=take_nth(gp,i2);
        gplik=Gp.lik;
        if ~isfield(gplik.fh,'trcov') && isfield(gp.lik.fh,'predcdf')
          for it=1:size(yt,2)
            [Eft, Varft] = gp_pred(Gp, xtr, ytr, x, 'tstind', tstind2, 'z', ztr(:,it), 'yt', yt(:,it), 'zt', zt(:,it));
            cdftemp{it,i2}= gp.lik.fh.predcdf(gplik, Eft, Varft,yt(:,it));
          end
        elseif isfield(gplik.fh,'trcov') && isfield(gp.lik.fh,'predcdf')
          for it=1:size(yt,2)
            [Eft, Varft] = gp_pred(gp, xtr, ytr, x, 'tstind', tstind2,'yt', yt(:,it));
            cdftemp{it,i2}= gp.lik.fh.predcdf(gplik, Eft, Varft,yt(:,it));
          end
        else
          error('This likelihood has not been implemented for this function')
        end
      end
      cdftemp = mat2cell(mean(cell2mat(cdftemp),2),repmat(1043,1,10),1);
    case 'IA'
      nsamples=length(gp);
      for i2=1:nsamples
        Gp=gp{i2};
        gplik=Gp.lik;
        if ~isfield(gplik.fh,'trcov') && isfield(gplik.fh,'predcdf')
          for it=1:size(yt,2)
            [Eft, Varft] = gp_pred(Gp, xtr, ytr, x, 'tstind', tstind2, 'z', ztr(:,it), 'yt', yt(:,it), 'zt', zt(:,it));
            cdftemp{it,i2}= gplik.fh.predcdf(gplik, Eft, Varft,yt(:,it));
          end
        elseif isfield(gplik.fh,'trcov') && isfield(gplik.fh,'predcdf')
          for it=1:size(yt,2)
            [Eft, Varft] = gp_pred(gp, xtr, ytr, x, 'tstind', tstind2,'yt', yt(:,it));
            cdftemp{it,i2}= gplik.fh.predcdf(gplik, Eft, Varft,yt(:,it));
          end
        else
          error('This likelihood has not been implemented for this function')
        end
      end
      cdftemp = mat2cell(sum(bsxfun(@times,cell2mat(cdftemp),P_TH'),2),repmat(1043,1,10),1);
  end

  for it=1:size(yt,2)
    cdf_cv(tstindex{i},it)=cdftemp{it}(tstindex{i},:);
  end

end

cdf=cdf_cv;
end


function estimate = spm_cfg_dcm_est
% SPM Configuration file for DCM estimation
%__________________________________________________________________________
% Copyright (C) 2008-2017 Wellcome Trust Centre for Neuroimaging

% Guillaume Flandin & Peter Zeidman
% $Id: spm_cfg_dcm_est.m 7479 2018-11-09 14:17:33Z peter $

% -------------------------------------------------------------------------
% dcmmat Select DCM_*.mat
% -------------------------------------------------------------------------
dcmmat         = cfg_files;
dcmmat.tag     = 'dcmmat';
dcmmat.name    = 'Select DCM_*.mat';
dcmmat.help    = {'Select DCM_*.mat files.'};
dcmmat.filter  = 'mat';
dcmmat.ufilter = '^DCM_.*\.mat$';
dcmmat.num     = [1 Inf];

% -------------------------------------------------------------------------
% dcmmat Select GCM_.*.mat
% -------------------------------------------------------------------------
gcmmat         = cfg_files;
gcmmat.tag     = 'gcmmat';
gcmmat.name    = 'Select GCM_*.mat';
gcmmat.help    = {'Select GCM_*.mat files.'};
gcmmat.filter  = 'mat';
gcmmat.ufilter = '^GCM_.*\.mat$';
gcmmat.num     = [1 1];

%--------------------------------------------------------------------------
% dir Directory
%--------------------------------------------------------------------------
dir         = cfg_files;
dir.tag     = 'dir';
dir.name    = 'Directory';
dir.help    = {'Select the directory where the output will be written.'};
dir.filter  = 'dir';
dir.ufilter = '.*';
dir.num     = [1 1];

% -------------------------------------------------------------------------
% name Model name
%--------------------------------------------------------------------------
name         = cfg_entry;
name.tag     = 'name';
name.name    = 'Name';
name.help    = {['Specify a name for the group DCM file. The prefix GCM_' ...
                'and suffix .mat are automatically added']};
name.strtype = 's';
name.num     = [0 Inf];

% -------------------------------------------------------------------------
% subj Create single subject
%--------------------------------------------------------------------------
subj      = cfg_branch;
subj.tag  = 'subj';
subj.name = 'Subject';
subj.val  = {dcmmat};
subj.help = {'Subject with one or more models.'};

% -------------------------------------------------------------------------
% multiple_models Create set of subjects
%--------------------------------------------------------------------------
subjects        = cfg_repeat;
subjects.tag    = 'subjects';
subjects.name   = 'Per subject';
subjects.values = {subj};
subjects.help   = {'Create the subjects and select the models for each'};
subjects.num    = [1 Inf];

% -------------------------------------------------------------------------
% subj Create single model
%--------------------------------------------------------------------------
model      = cfg_branch;
model.tag  = 'model';
model.name = 'Model';
model.val  = {dcmmat};
model.help = {'Corresponding model for each subject'};

% -------------------------------------------------------------------------
% subjects Create set of models
%--------------------------------------------------------------------------
models        = cfg_repeat;
models.tag    = 'models';
models.name   = 'Per model';
models.values = {model};
models.help   = {'Select DCM.mat files per model'};
models.num    = [1 Inf];

% -------------------------------------------------------------------------
% output_single Output one .mat file for the group
%--------------------------------------------------------------------------
output_single         = cfg_branch;
output_single.tag     = 'single';
output_single.name    = 'Create group GCM_*.mat file';
output_single.val     = { dir name };
output_single.help    = {['Creates a single group-level DCM file ' ...
                          'containing a subjects x models cell array.']};

% -------------------------------------------------------------------------
% output_overwrite_gcm Output a GCM file with existing name
%--------------------------------------------------------------------------
output_overwrite_gcm         = cfg_branch;
output_overwrite_gcm.tag     = 'overwrite_gcm';
output_overwrite_gcm.name    = 'Overwrite existing GCM/DCM files';
output_overwrite_gcm.val     = {};
output_overwrite_gcm.help    = {['Overwrites existing group-level DCM file ' ...
                                 'with estimated models.']};

% -------------------------------------------------------------------------
% output_separate Output one .mat file per model
%--------------------------------------------------------------------------
output_separate         = cfg_branch;
output_separate.tag     = 'separate';
output_separate.name    = 'Only save individual DCM files';
output_separate.val     = {};
output_separate.help    = {'Updated existing individual DCM.mat files'};

% -------------------------------------------------------------------------
% output Choice of how many DCM.mat files to output
%--------------------------------------------------------------------------
output         = cfg_choice;
output.tag     = 'output';
output.name    = 'Output';
output.values  = { output_single output_overwrite_gcm output_separate };
output.val     = { output_single };
output.help    = {'How to store the estimated DCMs. The options are: ' ...
                  ['1. Create group GCM_*.mat file - this will create a single '...
                   'file containing a cell array, with one row per subject ' ...
                   'and one column per DCM, containing the filenames of ' ...
                   'the DCMs. The original DCM files will be overwritten ' ...
                   'with the outcome of the estimation. ' ...
                   '(Alternatively, if the input is a GCM file containing ' ...
                   'DCM structures rather than filenames, then the output ' ...
                   'will also be an array containing DCM structures.)'] ...
                  ['2. Overwrite existing GCM/DCM files - as above but with ' ...
                   'with an existing GCM filename.'] ...
                  ['3. Only save individual DCM files - does not change the ' ...
                   'GCM file and simply overwrites each subject''s DCM ' ...
                   'with estimated values.']};

% -------------------------------------------------------------------------
% way Choice of ways to select DCMs (nested models)
%--------------------------------------------------------------------------
dcms        = cfg_choice;
dcms.tag    = 'dcms';
dcms.name   = 'Select DCMs';
dcms.values = {models subjects gcmmat};
dcms.val    = {gcmmat};
dcms.help   = {['Select one DCM per subject, multiple DCMs per subject ' ...
                  'or an existing group DCM file.'] ...
                 ['If multiple DCMs are selected per subject, then ' ...
                  'the first DCM for each subject should a ''full'' ' ...
                  'model containing all connections of interest. Subsequent ' ...
                  '(nested) DCMs will have certain connections switched ' ...
                  'off.']};

% -------------------------------------------------------------------------
% est_type Estimation type
%--------------------------------------------------------------------------
est_type        = cfg_menu;
est_type.tag    = 'est_type';
est_type.name   = 'Estimation type';
est_type.labels = {'Full + BMR (default)',...
                   'Full + BMR PEB (more accurate but slower)',...
                   'Full (not recommended)',...
                   'None (collate only)'};
est_type.values = {1,2,3,4};
est_type.val    = {1};
est_type.help   = {['Full + BMR: Estimates the full (first) model for ' ...
                    'each subject then uses Bayesian Model Reduction (BMR) '...
                    'to rapidly infer the evidence / parameters for any ' ...
                    'subsequent nested models.'] ...
                   ['Full + BMR PEB: Iteratively estimates the full (first) '...
                    'model for each subject, then sets the priors on each' ...
                    'each parameter to the group mean (from a PEB model)' ...
                    'then re-estimates. This improves estimation '...
                    'by overcoming local optima, but takes longer.' ] ...
                   ['Full: Estimates all models individually. Provided for '...
                    'backward compatibility.']...
                   ['None: Creates a group level DCM file without ' ...
                    'performing estimation']};

% -------------------------------------------------------------------------
% analysis Analysis
% -------------------------------------------------------------------------
fmri_analysis         = cfg_menu;
fmri_analysis.tag     = 'analysis';
fmri_analysis.name    = 'Analysis';
fmri_analysis.labels  = {'default (time series)','cross-spectral densities'};
fmri_analysis.values  = {'time','csd'};
fmri_analysis.val     = {'time'};
fmri_analysis.help    = {['Whether to analyse in the time domain (for task-' ...
                     'based studies or stochastic DCM) or in the frequency '...
                     'domain (for resting state analysis with DCM for CSD']};

fmri         = cfg_branch;
fmri.tag     = 'fmri';
fmri.name    = 'MRI specific options';
fmri.val     = {fmri_analysis};
fmri.help    = {'MRI specific options'};

% -------------------------------------------------------------------------
% estimate Estimate
% -------------------------------------------------------------------------
estimate      = cfg_exbranch;
estimate.tag  = 'estimate';
estimate.name = 'DCM estimation';
estimate.val  = { dcms output est_type fmri };
estimate.help = {['Estimate the parameters and free energy (log model ' ...
                  'evidence) of first level DCMs for fMRI. Models ' ...
                  'are assembled into a Subjects x Models array and ' ...
                  'saved in group GCM_*.mat file']};
estimate.prog = @spm_run_dcm_est;
estimate.vout = @vout_dcm;

% -------------------------------------------------------------------------
% fmri Dynamic Causal Model for fMRI
% -------------------------------------------------------------------------
est         = cfg_choice;
est.tag     = 'est';
est.name    = 'DCM estimation';
est.help    = {'Estimation of Dynamic Causal Models.'};
est.values  = { estimate };

%==========================================================================
function out = spm_run_dcm_est(job)
%==========================================================================

dcms = job.dcms;

% Get selected estimation option
EST_FULL_BMR     = 1;
EST_FULL_BMR_PEB = 2;
EST_FULL         = 3;
EST_NONE         = 4;

est_type = job.est_type;

% Get selected input option
INPUT_DCM_BY_MODEL   = 1;
INPUT_DCM_BY_SUBJECT = 2;
INPUT_GCM            = 3;

if isfield(dcms,'model')
    input_type = INPUT_DCM_BY_MODEL;
elseif isfield(dcms,'subj')
    input_type = INPUT_DCM_BY_SUBJECT;
elseif isfield(dcms,'gcmmat')
    input_type = INPUT_GCM;
else
    error('Unknown input type');
end

% Get selected output option
OUTPUT_GCM_NEW       = 1;
OUTPUT_GCM_OVERWRITE = 2;
OUTPUT_DCM           = 3;

if isfield(job.output,'single')
    output_type = OUTPUT_GCM_NEW;
elseif isfield(job.output,'overwrite_gcm')
    output_type = OUTPUT_GCM_OVERWRITE;
elseif isfield(job.output,'separate')
    output_type = OUTPUT_DCM;
else
    error('Unknown output type');
end

if (output_type == OUTPUT_GCM_OVERWRITE && input_type ~= INPUT_GCM)
    error('To overwrite an existing GCM file, the input must be a GCM');
end

% Build subjects x models filename matrix
switch input_type
    case INPUT_DCM_BY_MODEL
        ns = length(dcms.model(1).dcmmat);
        nm = length(dcms.model);
        P  = cell(ns,nm);

        for m = 1:nm
            if length(dcms.model(m).dcmmat) ~= ns
                error(['Please ensure all models have the same number of ' ...
                       'subjects']);
            end

            P(:,m) = dcms.model(m).dcmmat;
        end

    case INPUT_DCM_BY_SUBJECT
        ns  = length(dcms.subj);
        nm  = length(dcms.subj(1).dcmmat);
        P = cell(ns,nm);

        for s = 1:ns
            if length(dcms.subj(s).dcmmat) ~= nm
                error(['Please ensure all subjects have the same number of ' ...
                       'models']);
            end

            P(s,:) = dcms.subj(s).dcmmat';
        end

    case INPUT_GCM
        GCM = load(dcms.gcmmat{1});
        GCM = GCM.GCM;
        ns = size(GCM,1);
        nm = size(GCM,2);

        if ischar(GCM{1})
            P = GCM;
        else
            P = '';
        end
end

% Validate
if output_type == OUTPUT_DCM && isempty(P)
    error(['Cannot save individual DCM files when the input is a GCM ' ...
           'array of DCM structures']);
end

% Load all models into memory
if ~isempty(P)
    GCM = spm_dcm_load(P);
end

% Set timeseries or CSD estimation (fMRI)
for s = 1:ns
    for m = 1:nm
        if strcmpi(job.fmri.analysis,'CSD')
            GCM{s,m}.options.analysis   = 'CSD';
            GCM{s,m}.options.induced    = 1;
            GCM{s,m}.options.stochastic = 0;
        else
            if isfield(GCM{s,m},'options') && isfield(GCM{s,m},'analysis')
                GCM{s,m}.options = rmfield(GCM{s,m}.options,'analysis');
            end
            GCM{s,m}.options.induced = 0;
        end
    end
end

% Check that models 2-N are nested forms of the full model
if nm > 1 && (est_type == EST_FULL_BMR || est_type == EST_FULL_BMR_PEB)
    idx1 = spm_find_pC(GCM{1});
    for m = 2:nm
        idx = spm_find_pC(GCM{1,m});
        if any(setdiff(idx,idx1))
            error(['Model %d is not a nested model of model 1. This is ' ...
                  'required for Bayesian Model Reduction (BMR). Please ' ...
                  'introduce a full model as Model 1, or switch estimation ' ...
                  'type to ''full'''],m);
        end
    end
end

% Estimate models if requested
switch est_type
    case EST_FULL_BMR
        GCM(:,1) = spm_dcm_fit(GCM(:,1));

        if nm > 1
            GCM = spm_dcm_bmr(GCM);
        end
    case EST_FULL_BMR_PEB
        GCM = spm_dcm_peb_fit(GCM);
    case EST_FULL
        GCM = spm_dcm_fit(GCM);
    case EST_NONE
        % Do nothing
end

% Save individual DCM .mat files if requested
if ~isempty(P) && (est_type ~= EST_NONE)
    for s = 1:ns
        for m = 1:nm
            DCM = GCM{s,m};
            F   = DCM.F;
            Ep  = DCM.Ep;
            Cp  = DCM.Cp;
            save(P{s,m}, 'DCM', 'F', 'Ep', 'Cp', ...
                spm_get_defaults('mat.format'));
        end
    end
end

% If filenames were provided, set the GCM to contain the filenames
if ~isempty(P)
    GCM = P; %#ok<NASGU>
end

% Save GCM
if output_type == OUTPUT_GCM_NEW
    % Create single GCM mat file
    dir  = job.output.single.dir{1};
    name = ['GCM_' job.output.single.name '.mat'];
    filename = fullfile(dir,name);
    save(filename,'GCM', spm_get_defaults('mat.format'));

    out.gcmmat = {filename};
elseif output_type == OUTPUT_GCM_OVERWRITE
    % Update existing gcm file
    filename = dcms.gcmmat{1};
    save(filename,'GCM', spm_get_defaults('mat.format'));

    out.gcmmat = {filename};
end

if ~isempty(P)
    out.dcmmat = P(:,1);
end

%==========================================================================
function dep = vout_dcm(job)
%==========================================================================
dep(1)            = cfg_dep;
dep(1).sname      = 'DCM mat File(s) - full models';
dep(1).src_output = substruct('.','dcmmat');
dep(1).tgt_spec   = cfg_findspec({{'filter','mat','strtype','e'}});

if isfield(job.output,'single') || ...
        isfield(job.output,'overwrite_gcm')
    dep(2)            = cfg_dep;
    dep(2).sname      = 'GCM mat File(s)';
    dep(2).src_output = substruct('.','gcmmat');
    dep(2).tgt_spec   = cfg_findspec({{'filter','mat','strtype','e'}});
end


function [hmm,Gamma,vpath,Xi,LL,datat] = hmmdual(data,T,hmm,Gamma,Xi,residuals)
%
% Dual estimation of the HMM, first Gamma and then the HMM structure
%
% INPUTS:
%
% data          observations - a struct with X (time series) and C (classes; optional)
% T             Number of time points for each time series
% hmm           hmm structure with options specified in hmm.train
% Gamma         Initial state courses
% Xi            joint probability of past and future states conditioned on data
% residuals     in case we train on residuals, the value of those.
%
% OUTPUTS
% hmm           estimated HMMMAR model
% Gamma         estimated p(state | data)
% vpath            estimated Viterbi path
%
% Author: Diego Vidaurre, OHBA, University of Oxford (2019)
%
% edits to work for gradient computation (for Fisher kernel):
% Christine Ahrends, Aarhus University 2022

% to fix potential compatibility issues with previous versions
hmm = versCompatibilityFix(hmm);

if nargin<6, residuals = []; end
if nargin<5, Xi = []; end
if nargin<4, Gamma = []; end

if iscell(T)
    for i = 1:length(T)
        if size(T{i},1)==1, T{i} = T{i}'; end
    end
    if size(T,1)==1, T = T'; end
    T = cell2mat(T);
end
checkdatacell;
N = length(T);
p = hmm.train.lowrank; do_HMM_pca = (p > 0);

train = hmm.train;
checkdatacell;
data = data2struct(data,T,train);
% if train.standardise
%     disp('Option standardise should be zero in hmmdual.')
%     disp('Standardization should be done separately and using the entire data set.')
% end
% Standardise data and control for ackward trials
valid_dims = computeValidDimensions(data,train);
data = standardisedata(data,T,train.standardise,valid_dims);
% Filtering
if ~isempty(train.filter)
    data = filterdata(data,T,train.Fs,train.filter);
end
% Detrend data
if train.detrend
    data = detrenddata(data,T);
end
% Leakage correction
if train.leakagecorr ~= 0
    data = leakcorr(data,T,train.leakagecorr);
end
% Hilbert envelope
if train.onpower
    data = rawsignal2power(data,T);
end
% Leading Phase Eigenvectors
if train.leida
    data = leadingPhEigenvector(data,T);
end
% pre-embedded  PCA transform
if length(train.pca_spatial) > 1 || train.pca_spatial > 0
    if isfield(train,'As')
        data.X = bsxfun(@minus,data.X,mean(data.X));
        data.X = data.X * train.As;
    else
        [train.As,data.X] = highdim_pca(data.X,T,train.pca_spatial);
    end
end
% Embedding
if length(train.embeddedlags) > 1
    [data,T] = embeddata(data,T,train.embeddedlags);
end
% PCA transform
if length(train.pca) > 1 || train.pca > 0
    if isfield(train,'A')
        data.X = bsxfun(@minus,data.X,mean(data.X));
        data.X = data.X * train.A;
    else
        error('PCA cannot be recomputed within hmmdual, use parameter A instead')
        %[train.A,data.X] = highdim_pca(data.X,T,train.pca,0,0,0,train.varimax);
    end
    % Standardise principal components and control for ackward trials
    data = standardisedata(data,T,train.standardise_pc);
    train.ndim = size(train.A,2);
    train.S = ones(train.ndim);
    orders = formorders(train.order,train.orderoffset,train.timelag,train.exptimelag);
    train.Sind = formindexes(orders,train.S) == 1;
end
% Downsampling
if train.downsample > 0
    [data,T] = downsampledata(data,T,train.downsample,train.Fs);
end

if isempty(residuals) && ~do_HMM_pca
    if ~isfield(hmm.train,'Sind')
        orders = formorders(hmm.train.order,hmm.train.orderoffset,hmm.train.timelag,hmm.train.exptimelag);
        hmm.train.Sind = formindexes(orders,hmm.train.S) == 1;
    end
    residuals =  getresiduals(data.X,T,hmm.train.S,hmm.train.maxorder,hmm.train.order,...
        hmm.train.orderoffset,hmm.train.timelag,hmm.train.exptimelag,hmm.train.zeromean);
end

if isempty(Gamma)
    [Gamma,~,Xi,LL] = hsinference(data,T,hmm,residuals);
elseif isempty(Xi)
    Xi = approximateXi(Gamma,T,hmm);
end
setxx;

hmm = obsupdate(Gamma,hmm,residuals,XX,XXGXX);
hmm = hsupdate(Xi,Gamma,T,hmm);

if nargout > 1
    Gamma = hsinference(data,T,hmm,residuals);
end
if nargout > 2
    vpath = hmmdecode(data,T,hmm,1,residuals,0);
end
datat = data;

end


function [gKern, gVarmeans, gVarcovars, gInd] = rbfardjitVardistPsi2Gradient(rbfardKern, vardist, Z, covGrad, learnInducing)

% RBFARDJITVARDISTPSI2GRADIENT description.

% VARGPLVM

if nargin < 5
    learnInducing = 1;
end

[gKern, gVarmeans, gVarcovars, gInd] = rbfard2VardistPsi2Gradient(rbfardKern, vardist, Z, covGrad, learnInducing);


function [freq] = ft_datatype_freq(freq, varargin)

% FT_DATATYPE_FREQ describes the FieldTrip MATLAB structure for freq data
%
% The freq data structure represents frequency or time-frequency decomposed
% channel-level data. This data structure is usually generated with the
% FT_FREQANALYSIS function.
%
% An example of a freq data structure containing the powerspectrum for 306 channels
% and 120 frequencies is
%
%       dimord: 'chan_freq'          defines how the numeric data should be interpreted
%    powspctrm: [306x120 double]     the power spectum
%        label: {306x1 cell}         the channel labels
%         freq: [1x120 double]       the frequencies expressed in Hz
%          cfg: [1x1 struct]         the configuration used by the function that generated this data structure
%
% An example of a freq data structure containing the time-frequency resolved
% spectral estimates of power (i.e. TFR) for 306 channels, 120 frequencies
% and 60 timepoints is
%
%       dimord: 'chan_freq_time'     defines how the numeric data should be interpreted
%    powspctrm: [306x120x60 double]  the power spectum
%        label: {306x1 cell}         the channel labels
%         freq: [1x120 double]       the frequencies, expressed in Hz
%         time: [1x60 double]        the time, expressed in seconds
%          cfg: [1x1 struct]         the configuration used by the function that generated this data structure
%
% Required fields:
%   - freq, dimord, label or labelcmb
%
% Optional fields:
%   - powspctrm, fouriesspctrm, csdspctrm, cohspctrm, time, grad, elec, cumsumcnt, cumtapcnt, trialinfo
%
% Deprecated fields:
%   - <none>
%
% Obsoleted fields:
%   - <none>
%
% Revision history:
%
% (2011/latest) The description of the sensors has changed, see FT_DATATYPE_SENS
% for further information.
%
% (2008) The presence of labelcmb in case of crsspctrm became optional,
% from now on the crsspctrm can also be represented as Nchan * Nchan.
%
% (2006) The fourierspctrm field was added as alternative to powspctrm and
% crsspctrm. The fields foi and toi were renamed to freq and time.
%
% (2003v2) The fields sgn and sgncmb were renamed into label and labelcmb.
%
% (2003v1) The initial version was defined.
%
% See also FT_DATATYPE, FT_DATATYPE_COMP, FT_DATATYPE_DIP, FT_DATATYPE_FREQ,
% FT_DATATYPE_MVAR, FT_DATATYPE_RAW, FT_DATATYPE_SOURCE, FT_DATATYPE_SPIKE,
% FT_DATATYPE_TIMELOCK, FT_DATATYPE_VOLUME

% Copyright (C) 2011, Robert Oostenveld
%
% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org
% for the documentation and details.
%
%    FieldTrip 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.
%
%    FieldTrip 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 FieldTrip. If not, see <http://www.gnu.org/licenses/>.
%
% $Id$

% get the optional input arguments, which should be specified as key-value pairs
version = ft_getopt(varargin, 'version', 'latest');

if strcmp(version, 'latest')
  version = '2011';
end

if isempty(freq)
  return;
end

% do some sanity checks
assert(isfield(freq, 'freq') && (isfield(freq, 'label') || isfield(freq, 'labelcmb')), 'inconsistent freq data structure, some field is missing');
if isfield(freq, 'label')
  % it could also be that it has labelcmb instead of label
  assert(length(unique(freq.label))==length(freq.label), 'channel labels must be unique');
end

% ensure consistency between the dimord string and the axes that describe the data dimensions
freq = fixdimord(freq);

if ~isrow(freq.freq)
  freq.freq = freq.freq';
end
if isfield(freq, 'label') && ~iscolumn(freq.label)
  % this is not present if the dimord is chancmb_freq or chancmb_freq_time
  freq.label = freq.label';
end
if isfield(freq, 'time') && ~isrow(freq.time)
  freq.time = freq.time';
end
if ~isfield(freq, 'label') && ~isfield(freq, 'labelcmb')
  ft_warning('data structure is incorrect since it has no channel labels');
end

switch version
  case '2011'
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % ensure that the sensor structures are up to date
    if isfield(freq, 'grad')
      freq.grad = ft_datatype_sens(freq.grad);
    end
    if isfield(freq, 'elec')
      freq.elec = ft_datatype_sens(freq.elec);
    end
    if isfield(freq, 'opto')
      freq.opto = ft_datatype_sens(freq.opto);
    end

    if isfield(freq, 'foi') && ~isfield(freq, 'freq')
      % this was still the case in early 2006
      freq.freq = freq.foi;
      freq = rmfield(freq, 'foi');
    end

    if isfield(freq, 'toi') && ~isfield(freq, 'time')
      % this was still the case in early 2006
      freq.time = freq.toi;
      freq = rmfield(freq, 'toi');
    end

    if isfield(freq, 'cumtapcnt') && isvector(freq.cumtapcnt)
      % ensure that it is a column vector
      freq.cumtapcnt = freq.cumtapcnt(:);
    end

    if isfield(freq, 'cumsumcnt') && isvector(freq.cumsumcnt)
      % ensure that it is a column vector
      freq.cumsumcnt = freq.cumsumcnt(:);
    end

    % ensure that the structure has all required fields
    % note that dimord is listed as required field, but it might also be xxxdimord, or dynamically determined with GETDIMORD
    for required={'freq'}
      assert(isfield(freq, required), 'required field "%s" is missing', required{:});
    end
    % either label or labelcmb should be present
    assert(any(ismember({'label', 'labelcmb'}, fieldnames(freq))), 'required field "label" or "labelcmb" is missing');

  case '2008'
    % there are no known conversions for backward or forward compatibility support

  case '2006'
    % there are no known conversions for backward or forward compatibility support

  case '2003v2'
    % there are no known conversions for backward or forward compatibility support

  case '2003v1'
    % there are no known conversions for backward or forward compatibility support

  otherwise
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    ft_error('unsupported version "%s" for freq datatype', version);
end


function [mimgR,mimgG] = red_channel_mean3(ops)

% numPlanes = length(ops.planesToProcess);

% build file list with red channel

if (isfield(ops, 'SubDirsRed') && ~isempty(ops.SubDirsRed))
    subDirsRed = ops.SubDirsRed;
else
    if (isfield(ops, 'expred') && ~isempty(ops.expred))
        for i = 1:length(ops.expred)
            subDirsRed{i} = sprintf('%d', ops.expred(i));
        end
    else
        warning('could not find red channel info, returning...')
        return;
    end
end
%
% build file list
fsRED = dir(fullfile(ops.RootDir, subDirsRed{1}, '*.tif'));
for k = 1:length(fsRED)
    fsRED(k).name = fullfile(ops.RootDir, subDirsRed{1}, fsRED(k).name);
end

root = ops.ResultsSavePath;
fregops =  sprintf('regops_%s_%s.mat', ops.mouse_name, ops.date);
if exist(fullfile(root, fregops), 'file')
    load(fullfile(root, fregops))
else
    ops1 = cell(ops.nplanes, 1);
    for j = 1:ops.nplanes

        fname = sprintf('regops_%s_%s_plane%d.mat', ops.mouse_name, ops.date, j);
        dat = load(fullfile(root, fname));
        ops1{j} = dat.ops;
        ops1{j}.useGPU = ops.useGPU;
    end
end

%
ntf0 = 0;
numPlanes = ops.nplanes;
%iplane0 = 1:1:ops.nplanes;

totFrames=0;
for k = 1:length(fsRED)
    %iplane0 = mod(iplane0-1, numPlanes) + 1;
    startPlane=((mod(totFrames+1,ops.nplanes*2)-1)/2)+1;

    nFr = nFramesTiff(fsRED(k).name);
    totFrames=totFrames+nFr;
    data = loadFramesBuff(fsRED(k).name, 1, nFr, 1, ops.temp_tiff);

    if ~exist('mimgR', 'var')
        [Ly, Lx, ~] = size(data);
        mimgR = zeros(Ly, Lx, ops.nplanes);
    end
    if ~exist('mimgG', 'var')
        [Ly, Lx, ~] = size(data);
        mimgG = zeros(Ly, Lx, ops.nplanes);
    end
    %

    for iPlane=1:ops.nplanes

        idx0=mod((ops.nplanes-startPlane+iPlane)*2,ops.nplanes*2);
        planesG=(idx0+1):(2*ops.nplanes):nFr;
        planesR=(idx0+2):(2*ops.nplanes):nFr;
        dataG0=data(:,:,planesG);
        dataR0=data(:,:,planesR);

        BiDiPhase = ops1{iPlane}.BiDiPhase;
        if abs(BiDiPhase) > 0
            yrange = 2:2:Ly;
            if BiDiPhase>0
                dataG0(yrange,(1+BiDiPhase):Lx,:,:) = dataG0(yrange, 1:(Lx-BiDiPhase),:,:);
                dataR0(yrange,(1+BiDiPhase):Lx,:,:) = dataR0(yrange, 1:(Lx-BiDiPhase),:,:);
            else
                dataG0(yrange,1:Lx+BiDiPhase,:,:)   = dataG0(yrange, 1-BiDiPhase:Lx,:,:);
                dataR0(yrange,1:Lx+BiDiPhase,:,:)   = dataR0(yrange, 1-BiDiPhase:Lx,:,:);
            end
        end

        [ds, ~]  = regoffKriging(dataG0, ops1{iPlane}, 0);
        %[ds, ~]  = registration_offsets(dataG0, ops1{iPlane}, 0);

        if k==1
            ds(1,:) = 0;
        end
        dataR       = ...
            register_movie(dataR0, ops1{iPlane}, ds);
        dataG     = ...
            register_movie(dataG0, ops1{iPlane}, ds);

        mimgR(:,:,iPlane) = mimgR(:,:,iPlane) + mean(dataR, 3);
        mimgG(:,:,iPlane) = mimgG(:,:,iPlane) + mean(dataG, 3);

    end

    ntf0 = ntf0 + 1;

    %iplane0 = iplane0 - nFr/ops.nchannels_red;
    fprintf('processing tiff %d/%d\n',k,length(fsRED))
end

mimgR = mimgR/ntf0;
mimgG = mimgG/ntf0;


function L = co_reggui(action,varargin)
% GUI for Manual co-registration of different image modalities to go with
% CERR
% - Detailed Description: this is a manual co-registration routine for two
% set of images (2d/3d) assuming that a possible resmapling and affine
% transformation are sufficient for alignment. The quality of registration is measured by
% using the mutual information criterion (MI)
%
% Written By: Issam El Naqa    Date: 08/28/03
% Revised by:                  Date:
%
% Copyright 2010, Joseph O. Deasy, on behalf of the CERR development team.
%
% This file is part of The Computational Environment for Radiotherapy Research (CERR).
%
% CERR development has been led by:  Aditya Apte, Divya Khullar, James Alaly, and Joseph O. Deasy.
%
% CERR has been financially supported by the US National Institutes of Health under multiple grants.
%
% CERR is distributed under the terms of the Lesser GNU Public License.
%
%     This version of CERR 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.
%
% CERR 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 CERR.  If not, see <http://www.gnu.org/licenses/>.

if nargin<1,
    action='Initializeco_reggui';
end;

switch action

    case 'Initializeco_reggui'   %Initialization
        Initializeco_reggui;

    case 'RegisterMode'
        mode = get(findobj('Tag', 'dispregpopupmenu'),'Value');
        switch mode
            case 1 %manual
            case 2 %ctrl points
            case 3 %auto control points
        end

    case 'LoadRefImage'
        set(findobj(gcbf, 'Tag', 'StatusText'), 'String', 'Loading images...');
        h = get(gcbf,'Userdata');
        pathname 	= h.pathname;
        [filename, pathname] = uigetfile('*.*', 'Load reference image');
        load([pathname,filename]);
        x = cropImage(x);
        h.refimage=min_maxnorm(x, 0:255);
        set(gcbf,'Userdata',h);
        DispImage('axref',x);
        drawnow

    case 'LoadPlanCUnregImage'

    case 'LoadPlanCRefImage'

    case 'LoadUnregImage'
        set(findobj(gcbf, 'Tag', 'StatusText'), 'String', 'Loading images...');
        h = get(gcbf,'Userdata');
        pathname 	= h.pathname;
        [filename, pathname] = uigetfile('*.*', 'Load unregistered image');
        load([pathname,filename]);
        x = cropImage(x);
        h.unregimage=min_maxnorm(x, 0:255);
        h.regimage=h.unregimage; % initailize registered image to unregistered one!
        set(gcbf,'Userdata',h);
        DispImage('axunreg',x);
        drawnow
        set(findobj(gcbf, 'Tag', 'StatusText'), 'String', 'Set registration parameters...');
    case 'EditRegParam'
        h = get(gcbf,'Userdata');
        h.Tx=str2num(get(findobj(gcbf,'Tag','TXed'),'String'));
        h.Ty=str2num(get(findobj(gcbf,'Tag','TYed'),'String'));
        h.Tz=str2num(get(findobj(gcbf,'Tag','TZed'),'String'));
        h.rot=str2num(get(findobj(gcbf,'Tag','Roted'),'String'));
        h.Sx=str2num(get(findobj(gcbf,'Tag','SXed'),'String'));
        h.Sy=str2num(get(findobj(gcbf,'Tag','SYed'),'String'));
        h.Sz=str2num(get(findobj(gcbf,'Tag','SZed'),'String'));
        h.samp=str2num(get(findobj(gcbf,'Tag','Resamped'),'String'));
        set(findobj(gcbf, 'Tag', 'regpushbutton'), 'Enable', 'on');
        set(findobj(gcbf, 'Tag', 'dispmodepopupmenu'), 'Enable', 'on');
        set(findobj(gcbf, 'Tag', 'StatusText'), 'String', 'Press register button for processing...');
        set(gcbf,'UserData',h);
    case 'DisplayMode'
        setDisplayMode;

    case 'enlargeOverlay'
        h = get(gcbf,'Userdata');
        displayOverlays(h.refimage, h.regimage);

    case 'ApplyRegistration'
        h = get(gcbf,'Userdata');

        mode = get(findobj('Tag', 'dispregpopupmenu'),'Value');
        switch mode
            case 1 %manual
                tempimage=imtranslate2d(h.unregimage,h.Tx,h.Ty);
                tempimage=imrotate2d(tempimage,h.rot);
                tempimage=imscale2d(tempimage,h.Sx,h.Sy);
                tempimage=imresample2d(tempimage,h.samp);
                h.regimage=tempimage;
            case 2 %ctrl points
                getControlPoints('init');
                getControlPoints('load', h.refimage, h.unregimage);
                waitfor(findobj('Tag', 'RegGui'), 'Tag', 'RegGuiDone');
                [ref_pts, target_pts] = getControlPoints('getpoints', h.refimage, h.unregimage);
                delete(findobj('Tag', 'RegGuiDone'));
                [tempimage,A]=compute_aff_transform(h.unregimage, ref_pts, target_pts);
                h.regimage=tempimage;
            case 3
                getControlPoints('init');
                getControlPoints('load', h.refimage, h.unregimage);
                waitfor(findobj('Tag', 'RegGui'), 'Tag', 'RegGuiDone');
                [ref_pts, target_pts] = getControlPoints('getpoints', h.refimage, h.unregimage);
                delete(findobj('Tag', 'RegGuiDone'));
                [tempimage,A]=compute_perspect_transform(h.unregimage, ref_pts, target_pts);
                h.regimage=tempimage;
            case 4 %auto control points
                numPoints = 20;
                sigma = 1;
                get_control_points(h.unregimage, h.refimage, numPoints, sigma);

        end

        % make joint histogram plot
        [h.mi, h.jhist]=get_mutualinfo(h.refimage,h.regimage);
        %set(findobj(gcbf, 'Tag', 'mitext'), 'Visible', 'on');
        %set(findobj(gcbf, 'Tag', 'mivalue'), 'Visible', 'on');
        % make a cross-correlation image
        h.xcorr=fxcorr(h.refimage,h.regimage);
        % save current data
        set(gcbf,'UserData',h);
        % display images
        setDisplayMode;
        DisplayJHist(h.jhist);
        set(findobj(gcbf, 'Tag', 'mivalue'), 'String', num2str(h.mi));
        DispImage('axcorr',h.xcorr);
        drawnow
    case 'info'
        helpwin co_reggui;
    case 'close'
        close(gcbf);
end

return

% supplmentary routines

function setDisplayMode() % set display mode for registration
h = get(gcbf,'Userdata');
h.displaymode = get(findobj(gcbf,'Tag','dispmodepopupmenu'),'Value');
switch h.displaymode
    case 1  % Single registered
        DispImage('axreg',h.regimage);
    case 2  % Overlayed images
        alpha=0.8; % transperancy factor
        alternate_time=0.5; % refresh time
        AlternateOverlayed(h.refimage,h.regimage,'axreg',alpha,alternate_time);
    case 3  % Sliceomatic
end
set(gcbf,'UserData',h);
return

% image display function
function DispImage(imTag,x)
set(gcbf,'CurrentAxes',findobj(gcbf,'Tag',imTag));
cla, h=imagesc(x), axis image, colormap('hot'), axis ij, axis off
set(h, 'ButtonDownFcn', ['co_reggui(''Ctrl_' imTag ''');']);
return

% image translation function
function fh=imtranslate2d(f,xoff,yoff)
[h,w]=size(f);
[x,y]=meshgrid([1:1:h],[1:1:w]);
xd=x(:)+xoff; yd=y(:)+yoff;
fh=reshape(bilinear_interpolation(f,xd,yd),w,h)';
return

% image rotation function around the middle of the image
function fh=imrotate2d(f,ang)
[h,w]=size(f);
phi = ang*pi/180; % Convert to radians
vx=[-floor(h/2):ceil(h/2)-1]; % center around the middle
vy=[-floor(w/2):ceil(w/2)-1];

[x,y]=meshgrid(vx,vy);
x=x(:); y=y(:);
xd=x*cos(phi)+y*sin(phi)+floor(h/2)+1;
yd=-x*sin(phi)+y*cos(phi)+floor(w/2)+1;
fh=reshape(bilinear_interpolation(f,xd,yd),w,h)';
return

% image scaling function (exapnsion/shrinking)
function fh=imscale2d(f,Sx,Sy)
[h,w]=size(f);
[x,y]=meshgrid([1:1:h],[1:1:w]);
xd=x(:)*Sx; yd=y(:)*Sy;
fh=reshape(bilinear_interpolation(f,xd,yd),w,h)';
return

% image resampling function
function fh=imresample2d(f,q)
[h,w]=size(f);
% use a binomial filter of order 5 for smoothing
d=[1 4 6 4 1]/16; B=d'*d;
fh=f;
if q==1
    return
elseif q>1 % upsample
    N=round(q);
    y = zeros(N*h,N*w);
    y(1:N:end,1:N:end)=fh;
    fh=conv2(y,B,'same');
elseif (q<1 & q>0) % downsample
    D=round(1/q);
    fh=conv2(fh,B,'same');
    fh=fh(1:D:end,1:D:end);
else
    errordlg('The resampling factor should a positive number!', 'co_reggui Error', 'replace');
end

% perform fast cross-correlation in frequency domain
function c=fxcorr(x,y)
Fsize=size(x)+size(y)-1;
Fx = fft2(rot90(x,2),Fsize(1),Fsize(2));
Fy = fft2(y,Fsize(1),Fsize(2));
c = real(ifft2(Fx .* Fy));
return

% compute the mutual information using the joint histogram
function [mi, histxy]=get_mutualinfo(x,y)
% x, y : the two images,
siz=min([size(x);size(y)]); % if sizes are different
nbits=8; ngray=2^nbits; % assume 256 levels is sufficient approximation!
x=double(uint8(double(x)+1)); y=double(uint8(double(y)+1)); % convert to 8 bits
histxy=zeros(ngray,ngray);

[iM,jM] = meshgrid(1:siz(1),1:siz(2));

indV = (jM(:) - 1) * siz(1) + iM(:);

xV =double(x(indV));
yV = double(y(indV));

ind2V = (yV - 1) * ngray + xV;

for i=1:length(ind2V)
    histxy(ind2V(i)) = histxy(ind2V(i)) + 1;
end

%for i=1:siz(1)
%    for j=1:siz(2)
%        histxy(x(i,j),y(i,j))= histxy(x(i,j),y(i,j))+1;
%    end
%end

histxy=histxy/sum(histxy(:)); % normalize
% compute marginal distributions
histx=sum(histxy,2); histy=sum(histxy,1); % by integrating out the joint
mi=sum(sum(histxy.*log2(histxy./(histx*histy+eps)+eps)));
return

% joint histogram display
function DisplayJHist(jhist)
set(gcbf,'CurrentAxes',findobj(gcbf,'Tag','axjhist'));
set(findobj(gcbf,'Tag','axjhist'),'Box','off');
cla, view(-37.5,30), colormap('hot'), mesh(jhist);
return

% display transparent in a cyclic fashion
function DisplayOverlayed(x,y,imTag,alpha)
% try alphas, or linear combinations...
%mask = ones(size(y));
%mask(find(y<1)) = alpha;
set(gcbf,'CurrentAxes',findobj(gcbf,'Tag',imTag));
sx=size(x); sy=size(y); % linear combination
siz=max([sx;sy]);
xa=zeros(siz); xa(1:sx(1),1:sx(2))=x;
ya=zeros(siz); ya(1:sy(1),1:sy(2))=y;
cla, imagesc(double(xa)+0.75*double(ya), 'ButtonDownFcn', 'co_reggui(''enlargeOverlay'');'), axis image, colormap('hot'), axis ij, axis off
% hold on
% hi=imagesc();
% axis image, colormap('hot'), axis ij, axis off;
%set(hi,'AlphaData',mask);
return

function AlternateOverlayed(x,y,imTag,alpha, atime)
DisplayOverlayed(x,y,imTag,alpha);
% pause(atime);
% DisplayOverlayed(y,x,imTag,alpha);
return

function x = cropImage(x)
    minCol = 1, minRow = 1;
    [maxCol, maxRow] = size(x);
    rows = find(~max(x'));
    cols = find(~max(x));
    for i=1:length(rows)
        if rows(i) ~= i
            minRow = rows(i-1)+1;
            maxRow = rows(i)-1;
            break;
        end
    end
    for i=1:length(cols)
        if cols(i) ~= i
            minCol = cols(i-1)+1;
            maxCol = cols(i)-1;
            break;
        end
    end
    x = imcrop(x,[minCol, minRow,  maxCol-minCol, maxRow-minRow]);


function g = lmcKernGradient(kern, X, X2, covGrad)

% LMCKERNGRADIENT Gradient of LMC kernel's parameters.
% FORMAT
% DESC computes the gradient of parameters associated to the LMC kernel.
% As well as the kernel structure and the input positions, the user
% provides a matrix COVGRAD which gives the partial derivatives of the
% function with respect to the relevant elements of the kernel matrix.
% RETURN g:  gradients of the function of interest with respect to the
% kernel parameters. The ordering of the vector should match that
% provided by the function kernExtractParam.
% ARG kern : the kernel structure for which the gradients are being
% computed.
% ARG X : the input locations for which the gradients are being computed.
% ARG covGrad : matrix of partial derivatives of the function of
% interest with respect to the kernel matrix. The argument takes the
% form of a square matrix of dimension  numData, where numData is the
% number of rows in X.
%
% FORMAT
% DESC  computes the derivatives as above, but input locations are now
% provided in two matrices associated with rows and columns of the kernel
% matrix.
% RETURN g : gradients of the function of interest with respect to the
% kernel parameters.
% ARG kern : the kernel structure for which the gradients are being
% computed.
% ARG X : the input locations associated with the rows of the kernel matrix.
% ARG X2 : the input locations associated with the columns of the kernel
% matrix.
% ARG covGrad : matrix of partial derivatives of the function of interest
% with respect to the kernel matrix. The matrix should have the same number
% of rows as X1 and the same number of columns as X2 has rows.
%
% COPYRIGHT : Mauricio A. Alvarez, 2010.

% KERN

fhandle = str2func([kern.basicKernelType 'KernCompute']);
fhandleGrad = str2func([kern.basicKernelType 'KernGradient']);
gBK = zeros(1, kern.nParamsBK);
gPartialB = zeros(kern.nout);

if iscell(X)
    if nargin>3 && ~iscell(X2)
        error('Time course information is not matched in Cell format!');
    end
    % Collate arguments.
    dim1 = zeros(1, kern.nout);
    dim2 = zeros(1, kern.nout);
    for i=1:kern.nout
        dim1(i) = size(X{i}, 1);
        if nargin > 3
            dim2(i) = size(X2{i}, 1);
        else
            dim2(i) = dim1(i);
            covGrad = X2;
        end
    end
    for i = 1:kern.nout
        startOne = sum(dim1(1:(i-1)))+1;
        endOne = sum(dim1(1:i));
        startThree = sum(dim2(1:(i-1))) + 1;
        endThree = sum(dim2(1:i));
        if nargin > 3
            gBK = gBK + kern.B(i,i)*fhandleGrad(kern, X{i}, X2{i}, covGrad(startOne:endOne, ...
                startThree:endThree));
            basicK = fhandle(kern, X{i}, X2{i});
        else
            gBK = gBK + kern.B(i,i)*fhandleGrad(kern, X{i}, covGrad(startOne:endOne,...
                startThree:endThree));
            basicK = fhandle(kern, X{i});
        end
        gPartialB(i,i) = sum(sum(covGrad(startOne:endOne, startThree:endThree).*basicK));
        for j = 1:i-1
            startTwo = sum(dim2(1:(j-1))) + 1;
            endTwo =  sum(dim2(1:j));
            if nargin > 3
                g2  = kern.B(i,j)*fhandleGrad(kern, X{i}, X2{j}, covGrad(startOne:endOne, ...
                    startTwo:endTwo));
                basicK = fhandle(kern, X{i}, X2{j});
            else
                g2  = kern.B(i,j)*fhandleGrad(kern, X{i}, X{j}, covGrad(startOne:endOne, ...
                    startTwo:endTwo));
                basicK = fhandle(kern, X{i}, X{j});
            end
            gBK = gBK + 2*g2;
            gPartialB(i,j) = sum(sum(covGrad(startOne:endOne, startTwo:endTwo).*basicK));
            gPartialB(j,i) = gPartialB(i,j);
        end
    end
else
    if nargin < 4
        covGrad = X2;
        X2 = X;
    end
    basicK = fhandle(kern, X, X2);
    startOne = 1;
    endOne = 0;
    startThree = 1;
    endThree = 0;
    for i=1:kern.nout
        endOne = endOne + size(X,1);
        endThree = endThree + size(X2,1);
        gBK = gBK + kern.B(i,i)*fhandleGrad(kern, X, X2, ...
            covGrad(startOne:endOne, startThree:endThree));
        gPartialB(i,i) = sum(sum(covGrad(startOne:endOne, startThree:endThree).*basicK));
        startTwo = 1;
        endTwo = 0;
        startFour = 1;
        endFour = 0;
        for j=1:i-1
            endTwo = endTwo + size(X2,1);
            g2  = kern.B(i,j)*fhandleGrad(kern, X, X2, covGrad(startOne:endOne, ...
                startTwo:endTwo));
            gBK = gBK + g2;
            gPartialB(i,j) = sum(sum(covGrad(startOne:endOne, startTwo:endTwo).*basicK));
            if nargin < 3
                gBK = gBK + g2;
                gPartialB(j,i) = gPartialB(i,j);
            else
                endFour = endFour + size(X,1);
                g3 = kern.B(j,i)*fhandleGrad(kern, X, X2, covGrad(startFour:endFour, ...
                    startThree:endThree));
                gBK = gBK + g3;
                gPartialB(j,i) = sum(sum(covGrad(startFour:endFour, startThree:endThree).*basicK));
                startFour = endFour + 1;
            end
            startTwo = endTwo + 1;
        end
        startOne = endOne + 1;
        startThree = endThree + 1;
    end
end

Jij = zeros(kern.nout, kern.rankCorregMatrix);
Jji = zeros(kern.rankCorregMatrix, kern.nout);
gB = zeros(kern.nout, kern.rankCorregMatrix);
for i=1:kern.nout
    for j=1:kern.rankCorregMatrix
        Jij(i,j) = 1; Jji(j,i) = 1;
        gB(i,j) = sum(sum((kern.A*Jij' + Jji'*kern.A').*gPartialB));
        Jij(i,j) = 0; Jji(j,i) = 0;
    end
end

g = [gBK gB(:)'];


function batch_data=batch_do_data_crop(train_opts, imdb, work_info_epoch, batch_data, work_info_batch, work_info)

data_crop_config=train_opts.data_crop_config;

if work_info.ref.run_eva || ~data_crop_config.do_crop
    batch_data.crop_info=[];
    return;
end

img_data=batch_data.img_data;
mask_data=batch_data.label_data;
img_size=size(img_data);
img_size=img_size(1:2);

if any(size(mask_data)~=img_size)
    mask_data=imresize(mask_data, img_size, 'nearest');
end

min_edge_size=min(img_size);
need_changed_flags=mod(min_edge_size,2)~=0;
min_edge_size(need_changed_flags)=min_edge_size(need_changed_flags)-1;

crop_box_size=data_crop_config.crop_box_size;
crop_box_size=min(min_edge_size, crop_box_size);

gen_crop_point_type=data_crop_config.gen_crop_point_type;

start_point=[];

if strcmp(gen_crop_point_type, 'random')
    start_point=gen_crop_point_random(data_crop_config, imdb, work_info_batch, mask_data, crop_box_size);

elseif strcmp(gen_crop_point_type, 'class_sample')

    start_point=gen_crop_point_class_sample(data_crop_config, imdb, work_info_batch, mask_data, crop_box_size);
    if isempty(start_point)
        start_point=gen_crop_point_random(data_crop_config, imdb, work_info_batch, mask_data, crop_box_size);
    end

else

	error('gen_crop_point_type not support!');
end

assert(~isempty(start_point));

stop_point1=start_point(1)+crop_box_size-1;
if stop_point1>img_size(1)
    stop_point1=img_size(1);
    start_point(1)=stop_point1-crop_box_size+1;
end

stop_point2=start_point(2)+crop_box_size-1;
if stop_point2>img_size(2)
    stop_point2=img_size(2);
    start_point(2)=stop_point2-crop_box_size+1;
end

row_idxes=start_point(1):stop_point1;
col_idxes=start_point(2):stop_point2;

crop_img_data=img_data(row_idxes, col_idxes, :);
crop_mask_data=mask_data(row_idxes, col_idxes);

crop_info=[];
crop_info.img_size=img_size;
crop_info.row_idxes=row_idxes;
crop_info.col_idxes=col_idxes;
crop_info.crop_mask_data=crop_mask_data;
crop_info.crop_img_data=crop_img_data;
crop_info.mask_data=mask_data;
crop_info.img_data=img_data;
crop_info.do_crop=true;

batch_data.crop_info=crop_info;

batch_data.img_data=crop_img_data;
batch_data.label_data=crop_mask_data;

crop_img_size=size(crop_img_data);
crop_img_size=crop_img_size(1:2);
batch_data.img_size=crop_img_size;

end

function start_point=gen_crop_point_random(data_crop_config, imdb, work_info_batch, mask_data, crop_box_size)

img_size=size(mask_data);

crop_box_step_ratio=data_crop_config.crop_box_step_ratio;
if ~isempty(crop_box_step_ratio)
    step_size=round(crop_box_size*crop_box_step_ratio);
    step_size=max(step_size, 1);
else
    step_size=1;
end

max_range=img_size-crop_box_size+1;

can_row_points=[1:step_size:max_range(1) max_range(1)];
can_col_points=[1:step_size:max_range(2) max_range(2)];
start_point=[my_random_sample(can_row_points, 1) my_random_sample(can_col_points, 1)];

end

function start_point=gen_crop_point_class_sample(data_crop_config, imdb, work_info_batch, mask_data, crop_box_size)

if ~isfield(imdb.ref, 'crop_cache_info') || isempty(imdb.ref.crop_cache_info)
    imdb.ref.crop_cache_info=gen_crop_cache_info(work_info_batch.ref.train_opts, imdb.ref.ds_info);
end

crop_cache_info=imdb.ref.crop_cache_info;
task_idx=work_info_batch.ref.task_idxes;
class_idxes=crop_cache_info.class_idxes_imgs{task_idx};

start_point=[];

if isempty(class_idxes)
    disp('################# class_sample crop: empty class_idxes!');
    return;
end

one_class_idx=my_random_sample(class_idxes, 1);
valid_flags=mask_data==one_class_idx;

valid_idxes=find(valid_flags);

if isempty(valid_idxes)
    disp('################# class_sample crop: no points with the selected class label are found!');
    return;
end

tmp_point_idx=my_random_sample(valid_idxes, 1);
[start_point(1), start_point(2)]=ind2sub(size(valid_flags), tmp_point_idx);

assert(~isempty(start_point));

start_point=round(start_point-crop_box_size./2);
start_point=max(start_point, 1);

end

function crop_cache_info=gen_crop_cache_info(train_opts, ds_info)

disp('gen_crop_cache_info...');
class_idxes_imgs=ds_info.class_idxes_imgs;

void_class_idxes=ds_info.class_info.void_class_idxes;
for img_idx=1:length(class_idxes_imgs)
    class_idxes=class_idxes_imgs{img_idx};
    assert(max(class_idxes)<256);
    if ~isempty(void_class_idxes)
        class_idxes=class_idxes(class_idxes~=void_class_idxes);
    end
    class_idxes_imgs{img_idx}=uint8(class_idxes);
end

crop_cache_info=[];
crop_cache_info.class_idxes_imgs=class_idxes_imgs;

end


function poseRefPoint = getPoseRefPoint(keypointsAll,pidxsHead)

poseRefPoint = zeros(size(keypointsAll,2),2);
for clusidx = 1:size(keypointsAll,2)
    pp = [];
    % reference point is head center
%     pidxHC = 8; % head center
%     pidxN = 15; % neck
%     pidxHT = 16; % head top
    for pidx = pidxsHead
        pp = [pp; keypointsAll{pidx,clusidx}];
    end
    if (isempty(pp))
        pp = [-inf -inf];
    end
    poseRefPoint(clusidx,:) = mean(pp,1);
end

end


function [m, symbol, filterind] = model_add_terminal(m, varargin)
% Add a filter to the model.
%   [m, symbol, filterind] = model_add_terminal(m, varargin)
%
% Return values
%   m         Updated model
%   symbol    Newly created terminal symbol
%   filterind Index of the newly created filter associated with the terminal
%
% Arguments
%   m         Model to update
%   varargin  (key, value) pairs that can specify the following:
%   key                         value
%   ---                         -----
%   w                           Filter coefficients
%   flip                        True or false (default)
%   blocklabel                  model.blocks index
%   mirror_terminal             Terminal symbol to horizontally mirror

% AUTORIGHTS
% -------------------------------------------------------
% Copyright (C) 2009-2012 Ross Girshick
%
% This file is part of the voc-releaseX code
% (http://people.cs.uchicago.edu/~rbg/latent/)
% and is available under the terms of an MIT-like license
% provided in COPYING. Please retain this notice and
% COPYING if you use this file (or a portion of it) in
% your project.
% -------------------------------------------------------

valid_opts = {'w', 'blocklabel', 'flip', 'mirror_terminal'};
opts = getopts(varargin, valid_opts);

if opts.isKey('mirror_terminal')
  src_terminal = opts('mirror_terminal');
  fi = m.symbols(src_terminal).filter;
  opts('blocklabel') = m.filters(fi).blocklabel;
  opts('w')          = flipfeat(model_get_block(m, m.filters(fi)));
  opts('flip')       = ~m.filters(fi).flip;
end

if opts.isKey('w')
  w = opts('w');
else
  error('argument ''w'' is required');
end

if opts.isKey('blocklabel')
  blocklabel = opts('blocklabel');
else
  [m, blocklabel] = model_add_block(m, ...
                                    'type', block_types.Filter, ...
                                    'w', w);
end

if opts.isKey('flip')
  flip = opts('flip');
else
  flip = false;
end

% get index for new filter
j = m.numfilters + 1;
m.numfilters = j;

m.filters(j).blocklabel = blocklabel;
m.filters(j).size       = [size(w, 1) size(w, 2)];
m.filters(j).flip       = flip;

% new symbol for terminal associated with filter f
[m, i] = model_add_symbol(m, 'T');
m.symbols(i).filter = j;
m.filters(j).symbol = i;

filterind = j;
symbol = i;


function vis_grammar(model)

% AUTORIGHTS
% -------------------------------------------------------
% Copyright (C) 2011-2012 Ross Girshick
%
% This file is part of the voc-releaseX code
% (http://people.cs.uchicago.edu/~rbg/latent/)
% and is available under the terms of an MIT-like license
% provided in COPYING. Please retain this notice and
% COPYING if you use this file (or a portion of it) in
% your project.
% -------------------------------------------------------

% visualize random derivations...forever
while true
  f = vis_grammar_rand(model);
  visualizeHOG(max(0, f));
  pause;
end

function f = vis_grammar_rand(model, s, p, f)

conf = voc_config();

if nargin < 2
  s = model.start;
  p = [0 0 0];
  f = zeros([0 0 conf.features.dim]);
end

if model.symbols(s).type == 'T'
  w = model_get_block(model, model.filters(model.symbols(s).filter));
  wsz = size(w);
  fsz = size(f);
  req_fsz = [p(2) + wsz(1), p(1) + wsz(2), wsz(3)];
  pad = max(0, req_fsz - fsz);
  f = padarray(f, pad, 0, 'post');
  f(1+p(2):1+p(2)+wsz(1)-1, 1+p(1):1+p(1)+wsz(2)-1, :) = ...
    f(1+p(2):1+p(2)+wsz(1)-1, 1+p(1):1+p(1)+wsz(2)-1, :) + w;
else
  % sample a rule weighted by production score
  len = length(model.rules{s});
  z = zeros(len,1);
  for i = 1:len
    z(i) = model_get_block(model, model.rules{s}(i).offset);
  end
  z = exp(z);
  Z = sum(z);
  if Z ~= 0
    r = find(mnrnd(1, z./Z) == 1);
  else
    r = ceil(rand*length(model.rules{s}));
  end

  if model.rules{s}(r).type == 'D'
    cs = model.rules{s}(r).rhs(1);
    f = vis_grammar_rand(model, cs, p, f);
  else
    for i = 1:length(model.rules{s}(r).rhs)
      cs = model.rules{s}(r).rhs(i);
      anchor = model.rules{s}(r).anchor{i};
      f = vis_grammar_rand(model, cs, p + anchor, f);
    end
  end
end


function corrderived_plot_swarm_history(c,varargin)

%CORRDERIVED_PLOT_SWARM_HISTORY plots relevent details of seismic swarms
% CORRDERIVED_PLOT_SWARM_HISTORY(C)reads correlation object C and plots various
% parameters to characterize the swarm. Typical input will be a correlation
% object containing waveforms from a single station for numerous events.
% This function requires that the CLUST and WAVEFORM fields of the
% correlation object be filled. The heavily lifting will have already been
% done by the LINKAGE and CLUSTER functions. This is predominantly a
% plotting function. Plots include:
%    - A histogram of all events and clustered events
%    - An amplitude measure of all events
%    - A plot showing the "lifespan" of each cluster
%
% CORRDERIVED_PLOT_SWARM_HISTORY(C,CLUSTERSIZE) specifies the minimum number of
% events to be counted as a cluster. The default is 5. Note that the
% clusters defined in the correaltion object may have a few as 1 event. For
% analysis purposes however, the user will often want to set a minimum
% size for significant clusters. CLUSTERSIZE is this value.
%
% The trace amplitude plot is based on the maximum value of the hilbert
% transform of the input data. The user may wish to narrow the window of
% data to a portion of the input trace by using the cop function. Example
%     c1 = crop(c,-1,3)
%     corrderived_plot_swarm_history(c1,10)
%
% See also correlation/linkage correlation/cluster

% Author: Michael West, Geophysical Institute, Univ. of Alaska Fairbanks
% $Date$
% $Revision$

% CHECK INPUTS
if nargin>2
    error('Incorrect number of inputs');
end
if ~isa(c,'correlation')
    error('First argument must be a correlation object');
end

if isempty(get(c,'CLUST'))
    error('CLUSTER field must be filled in input argument. See HELP CLUSTER');
end

if numel(varargin)==1
    clusterSize = varargin{1};
else
    clusterSize = 5;
end

% FIND EVENTS IN/OUT OF CLUSTERS
inMult = find(c,'BIG',clusterSize);
% find non-multiplets
for n = 1:get(c,'TRACES')
    if numel(find(inMult==n))==0
        notInMult(n) = 1;
    else
        notInMult(n) = 0;
    end
end
notInMult = find(notInMult)';
inMult = sort(inMult);

% GET STA_CHAN
if ~check(c,'STA') | ~check(c,'CHAN')
   warning('This function was written assuming data from a single sta_chan');
end
wtmp = waveform(c);
wtmp = wtmp(1);
nscl = [ get(wtmp,'NETWORK') '_' get(wtmp,'STATION') '_' get(wtmp,'CHANNEL') '_' get(wtmp,'LOCATION') ];

% PLOT IT
figure('Color','w','Position',[0 0 1100 850]);
box on; hold on;
set(gcf,'DefaultLineLineWidth',0.1);
set(gcf,'DefaultAxesFontSize',12);

% PLOT EVENT RATES
subplot(3,1,1)
disp('Preparing event rate histograms ...');
c1 = subset(c,inMult);
c1 = sort(c1);
trigMult = get(c1,'TRIG');
trigAll = get(c,'TRIG');
edges = [floor(min(trigAll)):1/24:ceil(max(trigAll))];
nMult = histc(trigMult,edges);
nAll = histc(trigAll,edges);
%
bar(edges,nAll,'y');
%h = findobj(gca,'Type','patch');
%set(h,'FaceColor',[.7 .7 .7])
hold on;
bar(edges,nMult,'r');
h = findobj(gca,'Type','patch');
set(h,'EdgeColor',[0 0 0],'LineWidth',0.1)
xlim([[min(trigAll)-1/24 max(trigAll)+1/24]]);
datetick('x','KeepLimits');
legend('All events','Clustered events');

title('Event rates');
ylabel('Events per hour');

% AMPLITUDE PLOT
disp('Preparing amplitude measures ...');
subplot(3,1,2)
c1 = crop(c,-1,4);
w = waveform(c1);
w = hilbert(w);
amp = max(double(w));
trig = get(c1,'TRIG');
plot(trig(notInMult),amp(notInMult),'ko','MarkerFaceColor','y','MarkerSize',4);
hold on;
plot(trig(inMult),amp(inMult),'ko','MarkerFaceColor','r','MarkerSize',4);
set(gca,'YScale','log');
xlim([[min(trigAll)-1/24 max(trigAll)+1/24]]);
ylim([min(amp) max(amp)]);
datetick('x','KeepLimits');
%set(gca,'YGrid','on')
ylabel('amplitude (nm/s)')
legend('All events','Clustered events');
title(['Event amplitudes    (derived from  ' nscl ')'],'Interpreter','none');

% PLOT EVENT RATES
subplot(3,1,3)
disp('Preparing cluster lifespan plot ...');
family = getclusterstat(c);
index = find(family.numel>=clusterSize);
[tmp,index] = sort(family.begin(index));

box on; hold on;
for n = 1:numel(index)
    plot([ family.begin(index(n)) family.finish(index(n)) ],[n n],'-','Color',[0.7 0.7 0.7],'LineWidth',1);
    plot(family.trig{index(n)},repmat(n,family.numel(index(n)),1),'o','Color',[0 0 0],'MarkerFaceColor','r','MarkerSize',4);

end
xlim([[min(trigAll)-1/24 max(trigAll)+1/24]]);
ylim([0 numel(index)+1]);
datetick('x','KeepLimits');
set(gca,'YTick',[1:numel(index)]);
set(gca,'YTickLabel',index);
ylabel('cluster rank')
title(['Cluster lifespan  (clusters of ' num2str(clusterSize) ' or more events)']);

%PRINT OUT FIGURE
set(gcf, 'paperorientation', 'portrait');
set(gcf, 'paperposition', [.25 .25 8 10.5] );
print(gcf,'-dpsc2','FIG_SWARM_HISTORY.ps');