function [K] = ku0u0(x, xp, hyp, ubar, ubarp, dt, i)

logsigma = hyp(1);
logtheta = hyp(2);

a1 = hyp(3);
a2 = hyp(4);
a3 = hyp(5);

n_x = size(x,1);
n_xp = size(xp,1);

x = repmat(x,1,n_xp);
xp = repmat(xp',n_x,1);

ubar = repmat(ubar,1,n_xp);
ubarp = repmat(ubarp',n_x,1);

switch i

case 0

K=exp(1).^(logsigma+(-8).*logtheta+(-1/2).*exp(1).^((-1).*logtheta).*(x+( ...
  -1).*xp).^2).*(exp(1).^(4.*logtheta).*(exp(1).^(2.*logtheta).*(exp(1).^( ...
  2.*logtheta)+a1.*dt.*exp(1).^logtheta.*(a1.*dt.*ubar.*ubarp+(-1).*(ubar+ ...
  (-1).*ubarp).*(x+(-1).*xp))+(-1).*a1.^2.*dt.^2.*ubar.*ubarp.*(x+(-1).* ...
  xp).^2)+a2.*dt.*exp(1).^logtheta.*((-2).*exp(1).^(2.*logtheta)+exp(1) ...
  .^logtheta.*(3.*a1.*dt.*(ubar+(-1).*ubarp)+2.*(x+(-1).*xp)).*(x+(-1).* ...
  xp)+(-1).*a1.*dt.*(ubar+(-1).*ubarp).*(x+(-1).*xp).^3)+a2.^2.*dt.^2.*( ...
  3.*exp(1).^(2.*logtheta)+(-6).*exp(1).^logtheta.*(x+(-1).*xp).^2+(x+(-1) ...
  .*xp).^4))+a3.*dt.*exp(1).^(2.*logtheta).*((-2).*a2.*dt.*(15.*exp(1).^( ...
  3.*logtheta)+(-45).*exp(1).^(2.*logtheta).*(x+(-1).*xp).^2+15.*exp(1) ...
  .^logtheta.*(x+(-1).*xp).^4+(-1).*(x+(-1).*xp).^6)+exp(1).^logtheta.*( ...
  6.*exp(1).^(3.*logtheta)+(-3).*exp(1).^(2.*logtheta).*(5.*a1.*dt.*(ubar+ ...
  (-1).*ubarp)+4.*(x+(-1).*xp)).*(x+(-1).*xp)+(-1).*a1.*dt.*(ubar+(-1).* ...
  ubarp).*(x+(-1).*xp).^5+2.*exp(1).^logtheta.*(x+(-1).*xp).^3.*(5.*a1.* ...
  dt.*(ubar+(-1).*ubarp)+x+(-1).*xp)))+a3.^2.*dt.^2.*(105.*exp(1).^(4.* ...
  logtheta)+(-420).*exp(1).^(3.*logtheta).*(x+(-1).*xp).^2+210.*exp(1).^( ...
  2.*logtheta).*(x+(-1).*xp).^4+(-28).*exp(1).^logtheta.*(x+(-1).*xp).^6+( ...
  x+(-1).*xp).^8));

case 1 % logsigma

K=exp(1).^(logsigma+(-8).*logtheta+(-1/2).*exp(1).^((-1).*logtheta).*(x+( ...
  -1).*xp).^2).*(exp(1).^(4.*logtheta).*(exp(1).^(2.*logtheta).*(exp(1).^( ...
  2.*logtheta)+a1.*dt.*exp(1).^logtheta.*(a1.*dt.*ubar.*ubarp+(-1).*(ubar+ ...
  (-1).*ubarp).*(x+(-1).*xp))+(-1).*a1.^2.*dt.^2.*ubar.*ubarp.*(x+(-1).* ...
  xp).^2)+a2.*dt.*exp(1).^logtheta.*((-2).*exp(1).^(2.*logtheta)+exp(1) ...
  .^logtheta.*(3.*a1.*dt.*(ubar+(-1).*ubarp)+2.*(x+(-1).*xp)).*(x+(-1).* ...
  xp)+(-1).*a1.*dt.*(ubar+(-1).*ubarp).*(x+(-1).*xp).^3)+a2.^2.*dt.^2.*( ...
  3.*exp(1).^(2.*logtheta)+(-6).*exp(1).^logtheta.*(x+(-1).*xp).^2+(x+(-1) ...
  .*xp).^4))+a3.*dt.*exp(1).^(2.*logtheta).*((-2).*a2.*dt.*(15.*exp(1).^( ...
  3.*logtheta)+(-45).*exp(1).^(2.*logtheta).*(x+(-1).*xp).^2+15.*exp(1) ...
  .^logtheta.*(x+(-1).*xp).^4+(-1).*(x+(-1).*xp).^6)+exp(1).^logtheta.*( ...
  6.*exp(1).^(3.*logtheta)+(-3).*exp(1).^(2.*logtheta).*(5.*a1.*dt.*(ubar+ ...
  (-1).*ubarp)+4.*(x+(-1).*xp)).*(x+(-1).*xp)+(-1).*a1.*dt.*(ubar+(-1).* ...
  ubarp).*(x+(-1).*xp).^5+2.*exp(1).^logtheta.*(x+(-1).*xp).^3.*(5.*a1.* ...
  dt.*(ubar+(-1).*ubarp)+x+(-1).*xp)))+a3.^2.*dt.^2.*(105.*exp(1).^(4.* ...
  logtheta)+(-420).*exp(1).^(3.*logtheta).*(x+(-1).*xp).^2+210.*exp(1).^( ...
  2.*logtheta).*(x+(-1).*xp).^4+(-28).*exp(1).^logtheta.*(x+(-1).*xp).^6+( ...
  x+(-1).*xp).^8));

case 2 % logtheta

K=exp(1).^(logsigma+(-8).*logtheta+(-1/2).*exp(1).^((-1).*logtheta).*(x+( ...
  -1).*xp).^2).*(exp(1).^(5.*logtheta).*(6.*a2.^2.*dt.^2.*(exp(1) ...
  .^logtheta+(-1).*(x+(-1).*xp).^2)+exp(1).^logtheta.*(4.*exp(1).^(2.* ...
  logtheta)+3.*a1.*dt.*exp(1).^logtheta.*(a1.*dt.*ubar.*ubarp+(-1).*(ubar+ ...
  (-1).*ubarp).*(x+(-1).*xp))+(-2).*a1.^2.*dt.^2.*ubar.*ubarp.*(x+(-1).* ...
  xp).^2)+a2.*dt.*((-6).*exp(1).^(2.*logtheta)+2.*exp(1).^logtheta.*(3.* ...
  a1.*dt.*(ubar+(-1).*ubarp)+2.*(x+(-1).*xp)).*(x+(-1).*xp)+(-1).*a1.*dt.* ...
  (ubar+(-1).*ubarp).*(x+(-1).*xp).^3))+4.*exp(1).^(4.*logtheta).*(exp(1) ...
  .^(2.*logtheta).*(exp(1).^(2.*logtheta)+a1.*dt.*exp(1).^logtheta.*(a1.* ...
  dt.*ubar.*ubarp+(-1).*(ubar+(-1).*ubarp).*(x+(-1).*xp))+(-1).*a1.^2.* ...
  dt.^2.*ubar.*ubarp.*(x+(-1).*xp).^2)+a2.*dt.*exp(1).^logtheta.*((-2).* ...
  exp(1).^(2.*logtheta)+exp(1).^logtheta.*(3.*a1.*dt.*(ubar+(-1).*ubarp)+ ...
  2.*(x+(-1).*xp)).*(x+(-1).*xp)+(-1).*a1.*dt.*(ubar+(-1).*ubarp).*(x+(-1) ...
  .*xp).^3)+a2.^2.*dt.^2.*(3.*exp(1).^(2.*logtheta)+(-6).*exp(1) ...
  .^logtheta.*(x+(-1).*xp).^2+(x+(-1).*xp).^4))+2.*a3.*dt.*exp(1).^(2.* ...
  logtheta).*((-2).*a2.*dt.*(15.*exp(1).^(3.*logtheta)+(-45).*exp(1).^(2.* ...
  logtheta).*(x+(-1).*xp).^2+15.*exp(1).^logtheta.*(x+(-1).*xp).^4+(-1).*( ...
  x+(-1).*xp).^6)+exp(1).^logtheta.*(6.*exp(1).^(3.*logtheta)+(-3).*exp(1) ...
  .^(2.*logtheta).*(5.*a1.*dt.*(ubar+(-1).*ubarp)+4.*(x+(-1).*xp)).*(x+( ...
  -1).*xp)+(-1).*a1.*dt.*(ubar+(-1).*ubarp).*(x+(-1).*xp).^5+2.*exp(1) ...
  .^logtheta.*(x+(-1).*xp).^3.*(5.*a1.*dt.*(ubar+(-1).*ubarp)+x+(-1).*xp)) ...
  )+(exp(1).^(4.*logtheta).*(exp(1).^(2.*logtheta).*(exp(1).^(2.*logtheta) ...
  +a1.*dt.*exp(1).^logtheta.*(a1.*dt.*ubar.*ubarp+(-1).*(ubar+(-1).*ubarp) ...
  .*(x+(-1).*xp))+(-1).*a1.^2.*dt.^2.*ubar.*ubarp.*(x+(-1).*xp).^2)+a2.* ...
  dt.*exp(1).^logtheta.*((-2).*exp(1).^(2.*logtheta)+exp(1).^logtheta.*( ...
  3.*a1.*dt.*(ubar+(-1).*ubarp)+2.*(x+(-1).*xp)).*(x+(-1).*xp)+(-1).*a1.* ...
  dt.*(ubar+(-1).*ubarp).*(x+(-1).*xp).^3)+a2.^2.*dt.^2.*(3.*exp(1).^(2.* ...
  logtheta)+(-6).*exp(1).^logtheta.*(x+(-1).*xp).^2+(x+(-1).*xp).^4))+a3.* ...
  dt.*exp(1).^(2.*logtheta).*((-2).*a2.*dt.*(15.*exp(1).^(3.*logtheta)+( ...
  -45).*exp(1).^(2.*logtheta).*(x+(-1).*xp).^2+15.*exp(1).^logtheta.*(x+( ...
  -1).*xp).^4+(-1).*(x+(-1).*xp).^6)+exp(1).^logtheta.*(6.*exp(1).^(3.* ...
  logtheta)+(-3).*exp(1).^(2.*logtheta).*(5.*a1.*dt.*(ubar+(-1).*ubarp)+ ...
  4.*(x+(-1).*xp)).*(x+(-1).*xp)+(-1).*a1.*dt.*(ubar+(-1).*ubarp).*(x+(-1) ...
  .*xp).^5+2.*exp(1).^logtheta.*(x+(-1).*xp).^3.*(5.*a1.*dt.*(ubar+(-1).* ...
  ubarp)+x+(-1).*xp)))+a3.^2.*dt.^2.*(105.*exp(1).^(4.*logtheta)+(-420).* ...
  exp(1).^(3.*logtheta).*(x+(-1).*xp).^2+210.*exp(1).^(2.*logtheta).*(x+( ...
  -1).*xp).^4+(-28).*exp(1).^logtheta.*(x+(-1).*xp).^6+(x+(-1).*xp).^8)).* ...
  ((-8)+(1/2).*exp(1).^((-1).*logtheta).*(x+(-1).*xp).^2)+28.*a3.^2.* ...
  dt.^2.*exp(1).^logtheta.*(15.*exp(1).^(3.*logtheta)+(-45).*exp(1).^(2.* ...
  logtheta).*(x+(-1).*xp).^2+15.*exp(1).^logtheta.*(x+(-1).*xp).^4+(-1).*( ...
  x+(-1).*xp).^6)+a3.*dt.*exp(1).^(3.*logtheta).*(6.*exp(1).^(3.*logtheta) ...
  +(-30).*a2.*dt.*(3.*exp(1).^(2.*logtheta)+(-6).*exp(1).^logtheta.*(x+( ...
  -1).*xp).^2+(x+(-1).*xp).^4)+2.*exp(1).^logtheta.*(9.*exp(1).^(2.* ...
  logtheta)+(-3).*exp(1).^logtheta.*(5.*a1.*dt.*(ubar+(-1).*ubarp)+4.*(x+( ...
  -1).*xp)).*(x+(-1).*xp)+(x+(-1).*xp).^3.*(5.*a1.*dt.*(ubar+(-1).*ubarp)+ ...
  x+(-1).*xp))+(-3).*exp(1).^(2.*logtheta).*(5.*a1.*dt.*(ubar+(-1).*ubarp) ...
  +4.*(x+(-1).*xp)).*(x+(-1).*xp)+(-1).*a1.*dt.*(ubar+(-1).*ubarp).*(x+( ...
  -1).*xp).^5+2.*exp(1).^logtheta.*(x+(-1).*xp).^3.*(5.*a1.*dt.*(ubar+(-1) ...
  .*ubarp)+x+(-1).*xp)));

case 3 % a1

K=dt.*exp(1).^(logsigma+(-5).*logtheta+(-1/2).*exp(1).^((-1).*logtheta).*( ...
  x+(-1).*xp).^2).*(exp(1).^(2.*logtheta).*(exp(1).^logtheta.*(2.*a1.*dt.* ...
  ubar.*ubarp.*(exp(1).^logtheta+(-1).*(x+(-1).*xp).^2)+(-1).*exp(1) ...
  .^logtheta.*(ubar+(-1).*ubarp).*(x+(-1).*xp))+(-1).*a2.*dt.*(ubar+(-1).* ...
  ubarp).*((-3).*exp(1).^logtheta+(x+(-1).*xp).^2).*(x+(-1).*xp))+a3.*dt.* ...
  (ubar+(-1).*ubarp).*((-15).*exp(1).^(2.*logtheta)+10.*exp(1).^logtheta.* ...
  (x+(-1).*xp).^2+(-1).*(x+(-1).*xp).^4).*(x+(-1).*xp));

case 4 % a2

K=dt.*exp(1).^(logsigma+(-6).*logtheta+(-1/2).*exp(1).^((-1).*logtheta).*( ...
  x+(-1).*xp).^2).*(exp(1).^(2.*logtheta).*(exp(1).^logtheta.*((-2).*exp( ...
  1).^(2.*logtheta)+exp(1).^logtheta.*(3.*a1.*dt.*(ubar+(-1).*ubarp)+2.*( ...
  x+(-1).*xp)).*(x+(-1).*xp)+(-1).*a1.*dt.*(ubar+(-1).*ubarp).*(x+(-1).* ...
  xp).^3)+2.*a2.*dt.*(3.*exp(1).^(2.*logtheta)+(-6).*exp(1).^logtheta.*(x+ ...
  (-1).*xp).^2+(x+(-1).*xp).^4))+(-2).*a3.*dt.*(15.*exp(1).^(3.*logtheta)+ ...
  (-45).*exp(1).^(2.*logtheta).*(x+(-1).*xp).^2+15.*exp(1).^logtheta.*(x+( ...
  -1).*xp).^4+(-1).*(x+(-1).*xp).^6));

case 5 % a3

K=dt.*exp(1).^(logsigma+(-8).*logtheta+(-1/2).*exp(1).^((-1).*logtheta).*( ...
  x+(-1).*xp).^2).*(exp(1).^(2.*logtheta).*((-2).*a2.*dt.*(15.*exp(1).^( ...
  3.*logtheta)+(-45).*exp(1).^(2.*logtheta).*(x+(-1).*xp).^2+15.*exp(1) ...
  .^logtheta.*(x+(-1).*xp).^4+(-1).*(x+(-1).*xp).^6)+exp(1).^logtheta.*( ...
  6.*exp(1).^(3.*logtheta)+(-3).*exp(1).^(2.*logtheta).*(5.*a1.*dt.*(ubar+ ...
  (-1).*ubarp)+4.*(x+(-1).*xp)).*(x+(-1).*xp)+(-1).*a1.*dt.*(ubar+(-1).* ...
  ubarp).*(x+(-1).*xp).^5+2.*exp(1).^logtheta.*(x+(-1).*xp).^3.*(5.*a1.* ...
  dt.*(ubar+(-1).*ubarp)+x+(-1).*xp)))+2.*a3.*dt.*(105.*exp(1).^(4.* ...
  logtheta)+(-420).*exp(1).^(3.*logtheta).*(x+(-1).*xp).^2+210.*exp(1).^( ...
  2.*logtheta).*(x+(-1).*xp).^4+(-28).*exp(1).^logtheta.*(x+(-1).*xp).^6+( ...
  x+(-1).*xp).^8));

otherwise

        K = zeros(n_x, n_xp);
end

if K == 0

    K = zeros(n_x, n_xp);

end

end


function kern = sqexpKernParamInit(kern)

% SQEXPKERNPARAMINIT SQEXP kernel parameter initialisation.
% This kernel is a 'pre-packaged' compound kernel of the form
% {'rbf', 'lin', 'bias', 'white'}. Using this kernel removes
% the overhead of mutliple calls through the 'cmpnd' kernel.
%
% SEEALSO sqexpKernParamInit
%
% FORMAT
% DESC initialises the pre-built compound squared exponential
%  kernel structure with some default parameters.
% ARG kern : the kernel structure which requires initialisation.
% RETURN kern : the kernel structure with the default parameters placed in.
%
% SEEALSO : kernCreate, kernParamInit
%
% COPYRIGHT : Neil D. Lawrence, 2004

% KERN

kern.inverseWidth = 1;
kern.rbfVariance = 1;
kern.whiteVariance = 1;
kern.biasVariance = 1;
kern.nParams = 4;

kern.transforms(1).index = [1 2 3 4];
kern.transforms(1).type = optimiDefaultConstraint('positive');

kern.isStationary = false;


%TRAINED_MAPPING Define trained mapping
%
%   W = TRAINED_MAPPING(A,DATA,DIM)
%
% INPUT
%   A      - Dataset used for training
%   DATA   - Data (cell araay or structure) to be stored in the data-field
%            of the mapping in order to transfer it to the execution part
%   DIM    - Dimensionality of output space.
%
% OUTPUT
%   W      - Mapping
%
% DESCRIPTION
% This is a simplified version of the definition of a trained mapping. It
% calls PRMAPPING and derives all needed information from the dataset A used
% for training the mapping. In DATA everything should be stored needed for
% the execution of the mapping, either in a structure or by a cell array.
%
% SEE ALSO (PRTools Guide)
% MAPPINGS, PRMAPPING, TRAINED_CLASSIFIER, DEFINE_MAPPING, MAPPING_TASK

% Copyright: Robert P.W. Duin, prtools@rduin.nl

function w = trained_mapping(varargin)

  [a,data,out_size] = setdefaults(varargin,[],[],0);
  fname = callername;
  mapname = getname(feval(fname));
  if isdataset(a)
    if out_size == 0, out_size = getsize(a,3); end
    w = prmapping(fname,'trained',data,getlablist(a),size(a,2),out_size);
  else
    if out_size == 0, out_size = 1; end
    w = prmapping(fname,'trained',data,[],size(a,2),out_size);
  end
  w = setname(w,mapname);

return

%CALLERNAME
%
%	NAME = CALLERNAME
%
% Returns the name the calling function

function name = callername

[ss ,i] = dbstack;
if length(ss) < 3
	name = [];
else
	name = ss(3).name;
end


function test_tutorial_clusterpermutationtimelock(dataset, datadir)

% MEM 3gb
% WALLTIME 00:10:00
% DEPENDENCY ft_timelockanalysis ft_multiplotER ft_singleplotER ft_timelockstatistics ft_topoplotER ft_clusterplot ft_megplanar ft_combineplanar

if nargin<1 || isempty(dataset)
  dataset = dccnpath('/home/common/matlab/fieldtrip/data/ftp/test/ctf/Subject01.ds');
end
if nargin<2
  datadir = dccnpath('/home/common/matlab/fieldtrip/data/ftp/tutorial/cluster_permutation_timelock');
end

cfg                         = [];
cfg.dataset                 = dataset;
cfg.trialfun                = 'ft_trialfun_general'; % this is the default
cfg.trialdef.eventtype      = 'backpanel trigger';
cfg.trialdef.eventvalue     = [3 5 9]; % the values of the stimulus trigger for the three conditions
% 3 = fully incongruent (FIC), 5 = initially congruent (IC), 9 = fully congruent (FC)
cfg.trialdef.prestim        = 1; % in seconds
cfg.trialdef.poststim       = 2; % in seconds

cfg = ft_definetrial(cfg);

% remove the trials that have artifacts from the trl
cfg.trl([2, 5, 6, 8, 9, 10, 12, 39, 43, 46, 49, 52, 58, 84, 102, 107, 114, 115, 116, 119, 121, 123, 126, 127, 128, 133, 137, 143, 144, 147, 149, 158, 181, 229, 230, 233, 241, 243, 245, 250, 254, 260],:) = [];

% preprocess the data
cfg.channel    = {'MEG', '-MLP31', '-MLO12'};        % read all MEG channels except MLP31 and MLO12
cfg.demean     = 'yes';
cfg.baselinewindow  = [-0.2 0];
cfg.lpfilter   = 'yes';                              % apply lowpass filter
cfg.lpfreq     = 35;                                 % lowpass at 35 Hz.

data_all = ft_preprocessing(cfg);

cfg        = [];
cfg.trials = data_all.trialinfo == 3;
dataFIC_LP = ft_redefinetrial(cfg, data_all);

cfg = [];
cfg.trials = data_all.trialinfo == 9;
dataFC_LP = ft_redefinetrial(cfg, data_all);

%save dataFIC_LP dataFIC_LP
%save dataFC_LP dataFC_LP

cfg = [];
cfg.keeptrials = 'yes';
timelockFIC    = ft_timelockanalysis(cfg, dataFIC_LP);
timelockFC     = ft_timelockanalysis(cfg, dataFC_LP);

%% ## Permutation test

cfg                  = [];
cfg.method           = 'montecarlo'; % use the Monte Carlo Method to calculate the significance probability
cfg.statistic        = 'indepsamplesT'; % use the independent samples T-statistic as a measure to
                                   % evaluate the effect at the sample level
cfg.correctm         = 'cluster';
cfg.clusteralpha     = 0.05;       % alpha level of the sample-specific test statistic that
                                   % will be used for thresholding
cfg.clusterstatistic = 'maxsum';   % test statistic that will be evaluated under the
                                   % permutation distribution.
cfg.minnbchan        = 2;          % minimum number of neighborhood channels that is
                                   % required for a selected sample to be included
                                   % in the clustering algorithm (default=0).
% cfg.neighbours     = neighbours; % see below
cfg.tail             = 0;          % -1, 1 or 0 (default = 0); one-sided or two-sided test
cfg.clustertail      = 0;
cfg.alpha            = 0.025;      % alpha level of the permutation test
cfg.numrandomization = 100;        % number of draws from the permutation distribution

n_fc  = size(timelockFC.trial, 1);
n_fic = size(timelockFIC.trial, 1);

cfg.design           = [ones(1,n_fic), ones(1,n_fc)*2]; % design matrix
cfg.ivar             = 1; % number or list with indices indicating the independent variable(s)

cfg_neighb        = [];
cfg_neighb.method = 'distance';
neighbours        = ft_prepare_neighbours(cfg_neighb, dataFC_LP);

cfg.neighbours    = neighbours;  % the neighbours specify for each sensor with
                                 % which other sensors it can form clusters
cfg.channel       = {'MEG'};     % cell-array with selected channel labels
cfg.latency       = [0 1];       % time interval over which the experimental
                                 % conditions must be compared (in seconds)
[stat] = ft_timelockstatistics(cfg, timelockFIC, timelockFC);

% Save the output to disk:
%save stat_ERF_axial_FICvsFC stat;

%% ## Plotting of the results
%
% To plot the results of the permutation test, we use the plotting function **[ft_topoplotER](https://github.com/fieldtrip/fieldtrip/blob/release/ft_topoplotER.m)**. In doing so, we will plot a topography of the difference between the two experimental conditions (FIC and FC). On top of that, and for each timestep of interest, we will highlight the sensors which are members of significant clusters. First, however, we must calculate the difference between conditions using **[ft_math](https://github.com/fieldtrip/fieldtrip/blob/release/ft_math.m)**.
%
cfg    = [];
avgFIC = ft_timelockanalysis(cfg, dataFIC_LP);
avgFC  = ft_timelockanalysis(cfg, dataFC_LP);

% Then take the difference of the averages using ft_math
cfg           = [];
cfg.operation = 'subtract';
cfg.parameter = 'avg';
raweffectFICvsFC = ft_math(cfg, avgFIC, avgFC);

% We then construct a boolean matrix indicating whether a channel/time point belongs to a cluster that we deem interesting to inspect. This matrix has size [Number_of_MEG_channels x Number_of_time_samples], like stat.posclusterslabelmat. We'll make two such matrices: one for positive clusters (named pos), and one for negative (neg). All (channel,time)-pairs belonging to the large clusters whose probability of occurrence is sufficiently low in relation to the associated randomization distribution of clusterstats will be coded in the new boolean matrix as 1, and all those that don't will be coded as 0.
%
%
pos_cluster_pvals = [stat.posclusters(:).prob];

% Then, find which clusters are deemed interesting to visualize, here we use a cutoff criterion based on the
% cluster-associated p-value, and take a 5% two-sided cutoff (i.e. 0.025 for the positive and negative clusters,
% respectively
pos_clust = find(pos_cluster_pvals < 0.025);
pos       = ismember(stat.posclusterslabelmat, pos_clust);

% and now for the negative clusters...
neg_cluster_pvals = [stat.negclusters(:).prob];
neg_clust         = find(neg_cluster_pvals < 0.025);
neg               = ismember(stat.negclusterslabelmat, neg_clust);

% Alternatively, we can manually select which clusters we want to plot. If we only want to see the extext of the first (i.e. most significant) positive and negative clusters, for instance, we can do so as follows:
%
pos = stat.posclusterslabelmat == 1; % or == 2, or 3, etc.
neg = stat.negclusterslabelmat == 1;

% To plot a sequence of twenty topographic plots equally spaced between 0 and 1 second, we define the vector j of time steps. These time intervals correspond to the samples m in stat and in the variables pos and neg. m and j must, therefore, have the same length.
%
% To be sure that your sample-based time windows align with your time windows in seconds, check the following:
%
timestep      = 0.05; % timestep between time windows for each subplot (in seconds)
sampling_rate = dataFC_LP.fsample; % Data has a temporal resolution of 300 Hz
sample_count  = length(stat.time);
% number of temporal samples in the statistics object
j = [0:timestep:1]; % Temporal endpoints (in seconds) of the ERP average computed in each subplot
m = [1:timestep*sampling_rate:sample_count]; % temporal endpoints in M/EEG samples

% To plot the data use the following for-loop:
%
% First ensure the channels to have the same order in the average and in the statistical output.
% This might not be the case, because ft_math might shuffle the order
[i1,i2] = match_str(raweffectFICvsFC.label, stat.label);

for k = 1:20
   subplot(4,5,k);
   cfg = [];
   cfg.xlim = [j(k) j(k+1)];   % time interval of the subplot
   cfg.zlim = [-2.5e-13 2.5e-13];
   % If a channel is in a to-be-plotted cluster, then
   % the element of pos_int with an index equal to that channel
   % number will be set to 1 (otherwise 0).

   % Next, check which channels are in the clusters over the
   % entire time interval of interest.
   pos_int = zeros(numel(raweffectFICvsFC.label),1);
   neg_int = zeros(numel(raweffectFICvsFC.label),1);
   pos_int(i1) = all(pos(i2, m(k):m(k+1)), 2);
   neg_int(i1) = all(neg(i2, m(k):m(k+1)), 2);

   cfg.highlight   = 'on';
   % Get the index of the to-be-highlighted channel
   cfg.highlightchannel = find(pos_int | neg_int);
   cfg.comment     = 'xlim';
   cfg.commentpos  = 'title';
   cfg.layout      = 'CTF151_helmet.mat';
   cfg.interactive = 'no';
   cfg.figure      = 'gca';
   ft_topoplotER(cfg, raweffectFICvsFC);
end

%% ## Using planar gradient data
%
% To perform the permutation test using synthetic planar gradient data, the data must first be converted using the functions **[ft_megplanar](https://github.com/fieldtrip/fieldtrip/blob/release/ft_megplanar.m)** and **[ft_combineplanar](https://github.com/fieldtrip/fieldtrip/blob/release/ft_combineplanar.m)**. These functions were described in the tutorial on event-related fields. After running these functions, the statistical analysis using **[ft_timelockstatistics ](https://github.com/fieldtrip/fieldtrip/blob/release/ft_timelockstatistics.m)** involves the same configuration options as for ordinary event-related averages (no synthetic planar gradients). There is only one additional step, which is needed to add the gradiometer structure to one of the planar gradient data sets.
%
cfg = [];
cfg.planarmethod   = 'sincos';
cfg.neighbours     = neighbours; % also here, neighbouring sensors needs to be defined
timelockFIC_planar = ft_megplanar(cfg, timelockFIC);
timelockFC_planar  = ft_megplanar(cfg, timelockFC);

timelockFIC_planar_cmb = ft_combineplanar(cfg, timelockFIC_planar);
timelockFC_planar_cmb  = ft_combineplanar(cfg, timelockFC_planar);

timelockFIC_planar_cmb.grad = timelockFIC.grad;  % add the gradiometer structure
timelockFC_planar_cmb.grad  = timelockFC.grad;

% Having calculated synthetic planar gradient data, one can use the same configuration parameters as used for the analysis of the original data.
%
cfg                  = [];
cfg.channel          = {'MEG'};
cfg.latency          = [0 1];
cfg.neighbours       = neighbours;
cfg.method           = 'montecarlo';
cfg.statistic        = 'indepsamplesT';
cfg.correctm         = 'cluster';
cfg.clusteralpha     = 0.05;
cfg.clusterstatistic = 'maxsum';
cfg.minnbchan        = 2;
cfg.tail             = 0;
cfg.clustertail      = 0;
cfg.alpha            = 0.025;
cfg.numrandomization = 100;

n_fc  = size(timelockFC_planar_cmb.trial, 1);
n_fic = size(timelockFIC_planar_cmb.trial, 1);

cfg.design           = [ones(1,n_fic), ones(1,n_fc)*2]; % design matrix
cfg.ivar             = 1; % number or list with indices indicating the independent variable(s)

cfg.ivar = 1;

[stat] = ft_timelockstatistics(cfg, timelockFIC_planar_cmb, timelockFC_planar_cmb);

%save stat_ERF_planar_FICvsFC stat

% The output can also be obtained from [ftp://ftp.fieldtriptoolbox.org/pub/fieldtrip/tutorial/cluster_permutation_timelock/stat_ERF_planar_FICvsFC.mat](ftp://ftp.fieldtriptoolbox.org/pub/fieldtrip/tutorial/cluster_permutation_timelock/stat_ERF_planar_FICvsFC.mat). If you need to reload the statistics output, use:
%
%load stat_ERF_planar_FICvsFC

% We now calculate the raw effect in the average with planar gradient data using the following configuration:
%
cfg = [];
cfg.keeptrials = 'no';   % now only the average, not the single trials
avgFIC_planar  = ft_timelockanalysis(cfg, timelockFIC_planar);
avgFC_planar   = ft_timelockanalysis(cfg, timelockFC_planar);
cfg = [];
avgFIC_planar_cmb = ft_combineplanar(cfg, avgFIC_planar);
avgFC_planar_cmb  = ft_combineplanar(cfg, avgFC_planar);

% subtract avgFC from avgFIC
cfg = [];
cfg.operation    = 'subtract';
cfg.parameter    = 'avg';
raweffectFICvsFC = ft_math(cfg, avgFIC_planar_cmb, avgFC_planar_cmb);

% Using the following configuration for **[ft_topoplotER](https://github.com/fieldtrip/fieldtrip/blob/release/ft_topoplotER.m)** we can plot the raw effect and highlight the channels contributing to the largest cluster
%
figure;
timestep      = 0.05; %(in seconds)
sampling_rate = dataFC_LP.fsample;
sample_count  = length(stat.time);
j = [0:timestep:1]; % Temporal endpoints (in seconds) of the ERP average computed in each subplot
m = [1:timestep*sampling_rate:sample_count]; % temporal endpoints in M/EEG samples

pos_cluster_pvals = [stat.posclusters(:).prob];
pos_clust         = find(pos_cluster_pvals < 0.025);
pos               = ismember(stat.posclusterslabelmat, pos_clust);

% Remember to do the same for negative clusters if you want them!

% First ensure the channels to have the same order in the average and in the statistical output.
% This might not be the case, because ft_math might shuffle the order
[i1,i2] = match_str(raweffectFICvsFC.label, stat.label);

for k = 1:20
   subplot(4,5,k);
   cfg = [];
   cfg.xlim =[j(k) j(k+1)];
   cfg.zlim = [-1.0e-13 1.0e-13];
   pos_int = zeros(numel(raweffectFICvsFC.label),1);
   pos_int(i1) = all(pos(i2, m(k):m(k+1)), 2);
   cfg.highlight = 'on';
   cfg.highlightchannel = find(pos_int);
   cfg.comment = 'xlim';
   cfg.commentpos = 'title';
   cfg.layout = 'CTF151_helmet.mat';
   cfg.figure = 'gca';
   ft_topoplotER(cfg, raweffectFICvsFC);
end

%% # Within-subjects experiments
load(fullfile(datadir, 'ERF_orig.mat'));

% The configuration looks as follows:
cfg         = [];
cfg.channel = {'MEG'};
cfg.latency = [0 1];

cfg.method           = 'montecarlo';
cfg.statistic        = 'depsamplesT';
cfg.correctm         = 'cluster';
cfg.clusteralpha     = 0.05;
cfg.clusterstatistic = 'maxsum';
cfg.minnbchan        = 2;
cfg.neighbours       = neighbours;  % same as defined for the between-trials experiment
cfg.tail             = 0;
cfg.clustertail      = 0;
cfg.alpha            = 0.025;
cfg.numrandomization = 500;

Nsubj  = 10;
design = zeros(2, Nsubj*2);
design(1,:) = [1:Nsubj 1:Nsubj];
design(2,:) = [ones(1,Nsubj) ones(1,Nsubj)*2];

cfg.design = design;
cfg.uvar   = 1;
cfg.ivar   = 2;

[stat] = ft_timelockstatistics(cfg, allsubjFIC{:}, allsubjFC{:});

%%save stat_ERF_planar_FICvsFC_GA stat

% The output can also be obtained from [stat_ERF_planar_FICvsFC_GA.mat](ftp://ftp.fieldtriptoolbox.org/pub/fieldtrip/tutorial/cluster_permutation_timelock/stat_ERF_planar_FICvsFC_GA.mat). If you need to reload the statistics output, use:
%load stat_ERF_planar_FICvsFC_GA

%% ## Plotting the results
% load individual subject data
load(fullfile(datadir, 'ERF_orig.mat'));

% calculate the grand average for each condition
cfg = [];
cfg.channel   = 'all';
cfg.latency   = 'all';
cfg.parameter = 'avg';
GA_FIC        = ft_timelockgrandaverage(cfg, allsubjFIC{:});
GA_FC         = ft_timelockgrandaverage(cfg, allsubjFC{:});

cfg = [];
cfg.operation = 'subtract';
cfg.parameter = 'avg';
GA_FICvsFC    = ft_math(cfg, GA_FIC, GA_FC);

figure;
% define parameters for plotting
timestep      = 0.05; %(in seconds)
sampling_rate = dataFIC_LP.fsample;
sample_count  = length(stat.time);
j = [0:timestep:1];   % Temporal endpoints (in seconds) of the ERP average computed in each subplot
m = [1:timestep*sampling_rate:sample_count];  % temporal endpoints in M/EEG samples

% get relevant values
pos_cluster_pvals = [stat.posclusters(:).prob];
pos_clust = find(pos_cluster_pvals < 0.025);
pos       = ismember(stat.posclusterslabelmat, pos_clust);

% First ensure the channels to have the same order in the average and in the statistical output.
% This might not be the case, because ft_math might shuffle the order
[i1,i2] = match_str(GA_FICvsFC.label, stat.label);

% plot
for k = 1:20
   subplot(4,5,k);
   cfg            = [];
   cfg.xlim       = [j(k) j(k+1)];
   cfg.zlim       = [-5e-14 5e-14];
   pos_int        = zeros(numel(GA_FICvsFC.label),1);
   pos_int(i1)    = all(pos(i2, m(k):m(k+1)), 2);
   cfg.highlight  = 'on';
   cfg.highlightchannel = find(pos_int);
   cfg.comment    = 'xlim';
   cfg.commentpos = 'title';
   cfg.layout     = 'CTF151_helmet.mat';
   cfg.figure     = 'gca';
   ft_topoplotER(cfg, GA_FICvsFC);
end


function output = callcsdp(interfacedata);

% Retrieve needed data
options = interfacedata.options;
F_struc = interfacedata.F_struc;
c       = interfacedata.c;
K       = interfacedata.K;
x0      = interfacedata.x0;
pars   = interfacedata.options.csdp;
ub      = interfacedata.ub;
lb      = interfacedata.lb;

% Bounded variables converted to constraints
if ~isempty(ub)
    [F_struc,K] = addStructureBounds(F_struc,K,ub,lb);
end

pars.printlevel=options.verbose;

if options.savedebug
    save csdpdebug F_struc c K pars
end

if options.showprogress;showprogress(['Calling ' interfacedata.solver.tag],options.showprogress);end
solvertime = tic;
if options.verbose==0 % to fix display bug reported from user
    evalc('[x_s,y_s,z_s,info]=csdp(-F_struc(:,2:end),-c,F_struc(:,1),K,pars);');
else
    [x_s,y_s,z_s,info]=csdp(-F_struc(:,2:end),-full(c),F_struc(:,1),K,pars);
end
solvertime = toc(solvertime);

% We solve dual problem with CSDP
Dual = x_s;
x    = y_s;

switch info
case 0
    problem = 0;
case 1
    problem = 2;
case 2
    problem = 1;
case {3,5,6,7,8,9}
    problem = 4;
case 4
    problem = 3;
otherwise
    problem = 9;
end
infostr = yalmiperror(problem,interfacedata.solver.tag);

% Save ALL data sent to solver
if options.savesolverinput
    solverinput.A = -F_struc(:,2:end);
    solverinput.c = F_struc(:,1);
    solverinput.b = -c;
    solverinput.K = K;
    solverinput.pars = pars;
else
    solverinput = [];
end

% Save ALL data from the solution?
if options.savesolveroutput
    solveroutput.x = x_s;
    solveroutput.y = y_s;
    solveroutput.info = info;
else
    solveroutput = [];
end

% Standard interface
output = createOutputStructure(x,Dual,z_s,problem,infostr,solverinput,solveroutput,solvertime);


function ret = svargplvmScales(method, model, varargin)

% SVARGPLVMSCALES A small utility to get or set the ARD weights (scales) for an svargplvm model.
%
% COPYRIGHT : Andreas C. Damianou, 2011

% VARGPLVM

if ~isfield(model, 'numModels') && isfield(model, 'M')
    model.numModels = model.M;
end

if strcmp(method, 'get')
    for i=1:model.numModels
        if strcmp(model.comp{i}.kern.type, 'rbfardjit')
            scales{i} = model.comp{i}.kern.inputScales;
        else
            scales{i} = model.comp{i}.kern.comp{1}.inputScales;
        end
    end
    ret = scales;
elseif strcmp(method, 'set')
    if nargin < 2
        error('Function requires at least 3 arguments with the set method')
    end
    scales = varargin{1};
    if length(scales) == 1
        scales = repmat(median(scales{1}), 1, model.q);
    end

    for i=1:model.numModels
        if strcmp(model.comp{i}.kern.type, 'rbfardjit')
            model.comp{i}.kern.inputScales = scales;
        else
            model.comp{i}.kern.comp{1}.inputScales = scales;
        end
    end
    ret = model;
end


function obj = t2_pnp_func_new(in1,in2,in3)
coefs_tq1_1 = in2(1);
coefs_tq1_2 = in2(4);
coefs_tq2_1 = in2(2);
coefs_tq1_3 = in2(7);
coefs_tq2_2 = in2(5);
coefs_tq3_1 = in2(3);
coefs_tq1_4 = in2(10);
coefs_tq2_3 = in2(8);
coefs_tq3_2 = in2(6);
coefs_tq1_5 = in2(13);
coefs_tq2_4 = in2(11);
coefs_tq3_3 = in2(9);
coefs_tq1_6 = in2(16);
coefs_tq2_5 = in2(14);
coefs_tq3_4 = in2(12);
coefs_tq1_7 = in2(19);
coefs_tq2_6 = in2(17);
coefs_tq3_5 = in2(15);
coefs_tq1_8 = in2(22);
coefs_tq2_7 = in2(20);
coefs_tq3_6 = in2(18);
coefs_tq1_9 = in2(25);
coefs_tq2_8 = in2(23);
coefs_tq3_7 = in2(21);
coefs_tq2_9 = in2(26);
coefs_tq3_8 = in2(24);
coefs_tq3_9 = in2(27);
coefs_tq1_10 = in2(28);
coefs_tq2_10 = in2(29);
coefs_tq3_10 = in2(30);
pinvG2_1 = in1(2);
pinvG2_2 = in1(5);
pinvG2_3 = in1(8);
q0 = in3(1,:);
q1 = in3(2,:);
q2 = in3(3,:);
q3 = in3(4,:);
obj = q0.^2.*(coefs_tq1_1.*pinvG2_1+coefs_tq2_1.*pinvG2_2+coefs_tq3_1.*pinvG2_3)+q1.^2.*(coefs_tq1_5.*pinvG2_1+coefs_tq2_5.*pinvG2_2+coefs_tq3_5.*pinvG2_3)+q2.^2.*(coefs_tq1_8.*pinvG2_1+coefs_tq2_8.*pinvG2_2+coefs_tq3_8.*pinvG2_3)+q3.^2.*(coefs_tq1_10.*pinvG2_1+coefs_tq2_10.*pinvG2_2+coefs_tq3_10.*pinvG2_3)+q0.*q1.*(coefs_tq1_2.*pinvG2_1+coefs_tq2_2.*pinvG2_2+coefs_tq3_2.*pinvG2_3)+q0.*q2.*(coefs_tq1_3.*pinvG2_1+coefs_tq2_3.*pinvG2_2+coefs_tq3_3.*pinvG2_3)+q0.*q3.*(coefs_tq1_4.*pinvG2_1+coefs_tq2_4.*pinvG2_2+coefs_tq3_4.*pinvG2_3)+q1.*q2.*(coefs_tq1_6.*pinvG2_1+coefs_tq2_6.*pinvG2_2+coefs_tq3_6.*pinvG2_3)+q1.*q3.*(coefs_tq1_7.*pinvG2_1+coefs_tq2_7.*pinvG2_2+coefs_tq3_7.*pinvG2_3)+q2.*q3.*(coefs_tq1_9.*pinvG2_1+coefs_tq2_9.*pinvG2_2+coefs_tq3_9.*pinvG2_3);


function l = GridLength(G)
% return number of points

l = cellfun('prodofsize',{G.points});


%io_loadspec_sdat.m
%Jamie Near, McGill University 2014.
%Georg Oeltzschner, Johns Hopkins University 2018.
%
% USAGE:
% out=io_loadspec_sdat(filename,subspecs);
%
% DESCRIPTION:
% Reads in Philips MRS data (.spar and .sdat files) using code adapted from
% PhilipsRead.m, provided as part of the Gannet software package by Richard
% Edden (gabamrs.com).
%
% io_loadspec_sdat outputs the data in structure format, with fields
% corresponding to time scale, fids, frequency scale, spectra, and header
% fields containing information about the acquisition.  The resulting
% matlab structure can be operated on by the other functions in this MRS
% toolbox. ALSO:  This code is not currently smart enough to parse out all
% of the relevant information from the header file, such as the number of
% subspectra.  So for now, these details must be passed to the function as
% input arguments.  Help implementing these improvements are most welcome!!
%
% INPUTS:
% filename   = filename of Philips sdat file to be loaded.
% subspecs   = number of subspectra in the data (from spectral editing, ISIS, etc.)
%
% OUTPUTS:
% out        = Input dataset in FID-A structure format.

function out = io_loadspec_sdat(filename,subspecs)

% Read in the data and header information
[data, header] = philipsLoad(filename);

% Determine the dimensions of the data
data_size       = size(data);
dims.t          = find(data_size == header.samples);
dims.averages   = find(data_size == header.rows);
dims.coils      = 0; % SDAT is already coil-combined
% Now arrange in the standard order (samples-avgs-subspecs):
data = permute(data ,[dims.t dims.averages]);
dims.t = 1;
dims.averages = 2;
dims.extras = 0;

% We have no way of actually knowing the number of sub-spectra (e.g. for
% MEGA-PRESS or HERMES acquisitions, so we will split the averages
% according to the 'subspecs' input.
% Initialize fids array:
fids = squeeze(zeros(header.samples, header.rows/subspecs, subspecs));
if subspecs == 2
    %Split the subspectra out of the "averages" dimension:
    fids(:,:,1) = data(:,[1:2:end]);
    fids(:,:,2) = data(:,[2:2:end]);
elseif subspecs == 4
    fids(:,:,1) = data(:,[1:4:end]);
    fids(:,:,2) = data(:,[2:4:end]);
    fids(:,:,3) = data(:,[3:4:end]);
    fids(:,:,4) = data(:,[4:4:end]);
else
    fids = data;
end

if subspecs > 1
    dims.subSpecs = 3;
else
    dims.subSpecs = 0;
end

sz = size(fids);

% Fill in the header information
txfrq = header.synthesizer_frequency; % transmitter frequency [Hz]
Bo = txfrq/42577000; % B0 [T]
averages = size(fids,2)*size(fids,3); % number of rows in the file
rawAverages = averages;
spectralwidth = header.sample_frequency; % bandwidth [Hz]
dwelltime = 1/spectralwidth; % dwelltime [s]
te = header.echo_time; % echo time [ms]
tr = header.repetition_time; % repetition time [ms]
sequence = header.scan_id; % sequence type
Ncoils = 1; % SDAT already coil-combined
rawSubspecs = subspecs;
date = header.scan_date; % scan date [yyyy.mm.dd hh:mm:ss]

% Produce specs
specs = fftshift(ifft(fids,[],dims.t),dims.t);
% Calculate t and ppm arrays using the calculated parameters:
f = [(-spectralwidth/2)+(spectralwidth/(2*sz(1))):spectralwidth/(sz(1)):(spectralwidth/2)-(spectralwidth/(2*sz(1)))];
ppm = -f/(Bo*42.577);
ppm = ppm+4.65;
t = [0:dwelltime:(sz(1)-1)*dwelltime];

%FILLING IN DATA STRUCTURE
out.fids=fids;
out.specs=specs;
out.sz=sz;
out.ppm=ppm;
out.t=t;
out.spectralwidth=spectralwidth;
out.dwelltime=dwelltime;
out.txfrq=txfrq;
out.date=date;
out.dims=dims;
out.Bo=Bo;
out.averages=averages;
out.rawAverages=rawAverages;
out.subspecs=subspecs;
out.rawSubspecs=rawSubspecs;
out.seq=sequence;
out.te=te;
out.tr=tr;
out.pointsToLeftshift=0;

%FILLING IN THE FLAGS
out.flags.writtentostruct=1;
out.flags.gotparams=1;
out.flags.leftshifted=0;
out.flags.filtered=0;
out.flags.zeropadded=0;
out.flags.freqcorrected=0;
out.flags.phasecorrected=0;
out.flags.averaged=0;
out.flags.addedrcvrs=1;
out.flags.subtracted=0;
out.flags.writtentotext=0;
out.flags.downsampled=0;
if out.dims.subSpecs==0
    out.flags.isFourSteps=0;
else
    out.flags.isFourSteps=(out.sz(out.dims.subSpecs)==4);
end

%DONE


function Dnew = spm_eeg_remove_spikes(S)
% Use Will Pennys robust GLM code to remove 'spikes' from continuous data.
% Such spikes occur in EEG data recorded with the CTF MEG system at FIL
% due to some obscure electrical problem.
%
% FORMAT Dnew = spm_eeg_remove_spikes(S)
%
% S         - struct (optional)
% (optional) fields of S:
% S.D      - meeg object or filename
% S.logbf  - clean a block if log bayes factor in favour of spike model is
%            bigger than this (default - 3)
% S.hpf    - high-pass frequency above which to look for spikes (default 40 Hz)
% S.fast   - option to speed up the function by only using GLM if there is
%            threshold crossing ('yes', or check all the data with GLM - 'no')
% S.fasthresh - threshold for the fast option (in STD) - default 4
% S.trialbased - use trials in the data as they are ('yes') or break them
%              into sub-blocks ('no' - default)
% S.channels - channels to clean up (default 'gui' - brings up a GUI for
%             channel choice.
%
% Output:
% Dnew  - MEEG object with data cleaned of spikes.
%
%
% Disclaimer: this code is provided as an example and is not guaranteed to work
% with data on which it was not tested. If it does not work for you, feel
% free to improve it and contribute your improvements to the MEEGtools toolbox
% in SPM (http://www.fil.ion.ucl.ac.uk/spm)
%
% _______________________________________________________________________
% Copyright (C) 2008 Institute of Neurology, UCL

% Vladimir Litvak, Will Penny
% $Id: spm_eeg_remove_spikes.m 5640 2013-09-18 12:02:29Z vladimir $

if nargin == 0
    S = [];
end

% clean a block if log bayes factor in favour of spike model is bigger than this
if ~isfield(S, 'logbf'),             S.logbf = 3;                            end
% Have signals up to this freq in design matrix
if ~isfield(S, 'hpf'),               S.hpf = 40;                             end
% Use 1s blocks
if ~isfield(S, 'fast'),              S.fast='yes';                           end
if ~isfield(S, 'fasthresh'),         S.fasthresh=4;                          end
if ~isfield(S, 'trialbased'),        S.trialbased='no';                      end
if ~isfield(S, 'channels'),          S.channels='gui';                       end

try
    D = S.D;
catch
    [D,sts] = spm_select(1, 'mat', 'Select M/EEG mat file');
    if ~sts, D = []; return; end
    S.D = D;
end

D = spm_eeg_load(D);

if ~isfield(S, 'blocksize'),         S.blocksize = D.fsample;                end

spm('Pointer', 'Watch');drawnow;

if ~isfield(S, 'channels')
    chansel = D.indchantype({'MEEG', 'LFP'});
    S.channels = D.chanlabels(chansel);
elseif ischar(S.channels) && strcmp(S.channels, 'gui')
    chansel = listdlg('ListString', D.chanlabels, 'SelectionMode', 'multiple' ,'Name', 'Select channels' , 'ListSize', [400 300]);
    S.channels = D.chanlabels(chansel);
else
    chansel     = spm_match_str(D.chanlabels, S.channels);
end

nchan = length(chansel);    % number of channels

if strcmpi(S.trialbased, 'no') || (D.ntrials == 1)
    Nblocks=floor(D.nsamples./S.blocksize);
    block_size=S.blocksize;
    trialbased=0;
else
    Nblocks=D.ntrials;
    block_size=D.nsamples;
    trialbased=1;
end

% Make design matrix
K(1).row=[1:block_size];
K(1).RT=1/D.fsample;
K(1).HParam=1/S.hpf;
K = spm_filter(K);
X=K.X0;
X=[X ,ones(size(X,1),1)];

if ~trialbased && (D.ntrials > 1)
    rptnum = D.ntrials;
else
    rptnum = 1;
end

% generate new meeg object with new filenames
Dnew = clone(D, ['S' fnamedat(D)], [D.nchannels D.nsamples D.ntrials]);

for i = 1:D.nchannels
    Dnew(i, :, :) = D(i, :, :);
end

for r = 1:rptnum
    for ch=1:nchan
        for n=1:Nblocks,
            if trialbased
                lfp=squeeze(D(chansel(ch), :, n));
            else
                si=(n-1)*block_size+1;
                ei=si+block_size-1;
                lfp=squeeze(D(chansel(ch), si:ei, r));
            end
            m_lfp=mean(lfp);
            s_lfp=std(lfp);
            if s_lfp==0
                continue;
            end

            lfp=(lfp-m_lfp)/s_lfp;

            if ~(strcmpi(S.fast, 'yes') && max(abs(lfp))<S.fasthresh)
                glm=spm_rglm(lfp,X,1);
                [rglm,lfp_clean]=spm_rglm(lfp,X,2);
                if rglm.fm-S.logbf > glm.fm
                    if trialbased
                        Dnew(chansel(ch), :, n)=s_lfp*lfp_clean'+m_lfp;
                    else
                        Dnew(chansel(ch), si:ei, r) = s_lfp*lfp_clean'+m_lfp;
                    end
                    descr='spiky';
                else
                    descr='clean';
                end
                disp(sprintf('Channel %d out of %d, trial %d out of %d, block %d out of %d %s',ch,nchan, r, rptnum, n,Nblocks,descr));
            end
        end
    end
end

Dnew = history(Dnew, 'spm_eeg_remove_spikes', S);

save(Dnew);

spm('Pointer', 'Arrow');drawnow;


function [c,cl] = cluster_nmdsfig(cl,fldname,colm,varargin)
% function [c,cl] = cluster_nmdsfig(cl,fldname,colm,[behavior],[ancova var],[covs of no interest],[overlay],[niter])
%
% Makes a multidim scaling figure from clusters
% Plus some other plots
%
% Example:
% c = cluster_nmdsfig(cl,'BARPLOT.data',2);
%
% c is a structure with lots of info
% cl has names appended, if not entered previously
%
% behavioral input is of three types:
% outcome       covariate of interest (1 only)
% ancova_var        categorical ANCOVA coding var (2-levels, one only);
%                   generally not of interest
%                   IF ancova_var is entered, "PPI" of group x brain
%                   covariance interaction will be computed
% covs of no int    of no interest; will be removed from brain and behavior
%                   before performing ANCOVA (for simplicity); 1 or more OK
%
% Tor Wager
%
% Examples:
%
% Use average data from cl.timeseries and correlate with EXPT.behavior.
% Covary out effects of order before running models; no ANCOVA
% [c,cl] = cluster_nmdsfig(cl,'timeseries',1,EXPT.behavior',[],EXPT.order2);
%
% More examples:
% c =
% cluster_nmdsfig(cl,'CONTRAST.yadj',1,repmat(EXPT.cov(:,1),4,1),[],[],EXPT.overlay,1000);
% [c,cl] = cluster_nmdsfig(cl,'CONTRAST.data',1,R.X(:,1),[],R.X(:,2),EXPT.overlay,200);
% [c,cl] = cluster_nmdsfig(cl,'timeseries',1,SETUP.Y,[],SETUP.X,EXPT.overlay,1000);

% subdirectory: prompt
dosubdir = input('Create a subdirectory for png images? (type name or return to use current dir) ','s');
if ~isempty(dosubdir)
    mkdir(dosubdir)
    cd(dosubdir)
end

diary cluster_nmdsfig_output.txt
doranks = 1;
addbtwn = 0;

if length(varargin) > 0, c.outcome = varargin{1}; else  c.outcome = [];end
if length(varargin) > 1, c.ancova_codes = varargin{2}; else  c.ancova_codes = [];end
if length(varargin) > 2, c.covs_nointerest = varargin{3}; else  c.covs_nointerest = [];end
if length(varargin) > 3, overlay = varargin{4}; else  overlay = [];end
if length(varargin) > 4, niter = varargin{5}; else  niter = 1000;end

cl(1).outcome = c.outcome; cl(1).ancova_codes = c.ancova_codes; cl(1).covs_nointerest=c.covs_nointerest;

c.X = [c.outcome, c.covs_nointerest];
contrasts = [];

c.colors = {'ro' 'go' 'bo' 'yo' 'co' 'mo' 'ko' 'r^' 'g^' 'b^' 'y^' 'c^' 'm^' 'k^'};

if ~isfield(c,'btwn_names') || isempty(c.btwn_names)
    c.btwn_names = {'Behav'};
end

% -----------------------------------------------------------------------
% get data from selected field
% remove covariates of no interest from predictors and data
% -----------------------------------------------------------------------

c = nmdsfig_tools('get_data_matrix',c,cl,fldname,colm,contrasts,doranks);

if addbtwn
    % add between-subjects behavioral scores to dat; just to get stim coords; remove later...
    c.dat = [c.outcome c.dat];
end

% -----------------------------------------------------------------------
% get names
% -----------------------------------------------------------------------

cl = cluster_names(cl,1);
for i = 1:length(cl), c.names{i} = cl(i).shorttitle; end

% -----------------------------------------------------------------------
% get correlations, significance, and number of dimensions
% If groups are entered and ANCOVA is specified, handle that
% Also get c.D, dissimilarity matrix for MDS
% -----------------------------------------------------------------------

c = nmdsfig_tools('get_correlations',c);

% MDS decomposition and plotting
% -----------------------------------------------------------------------
% get number of dimensions that carry most of the variance.
% This is to simplify the space and make it less sparse --
% expected to give better clustering results.
%
% Reduce to distances in ndims space, check this against actual distances
% -----------------------------------------------------------------------

% graphic check on number of dimensions, and MDS
[c.GroupSpace,c.obs,c.implied_dissim] = shepardplot(c.D,[]);
drawnow
c.ndims = size(c.GroupSpace,2); % set here; graphical decision within shepardplot

nclust = 2:(size(c.GroupSpace,1) / 2)+1;     % choose number of clusters to test

save_figure('cluster_nmdsfig_shepardplot');

if addbtwn
    % get coordinates for behavior along with the others
    fprintf(1,'Using between-subjects covariates in MDS model.\n');

    % save coordinates in MDS space for btwn data
    c.btwn_coords = c.GroupSpace(1,:);

    % remove from GroupSpace
    c.GroupSpace = c.GroupSpace(2:end,:);

    % remove from r
    c.r = c.r(2:end,2:end);
end

% -----------------------------------------------------------------------
% testcluster:
%
% Permute objects in space, get null hypothesis cluster quality, and test
% observed cluster solution against this.
% -----------------------------------------------------------------------

% pval is p-values for each number of clusters tested
% classes = class ("network") assignments for best clustering solution
[c.ClusterSolution.pvals,c.ClusterSolution.classes, c.ClusterSolution.classnames ...
    c.ClusterSolution.X,c.ClusterSolution.include,c.ClusterSolution.names]= ...
    testclustnew(c.GroupSpace,nclust,c.ndims,niter,c.names,'keep','average');

if ~any(c.ClusterSolution.pvals < .05)
    % nothing significant
    c.ClusterSolution.classes = ones(size(c.ClusterSolution.classes));
end

%[bestpval,bestXc,bestnames,bestX,where,clustnames]=testcluster(X,clust,[r],[nperm],[names],[remove],[linkagetype]);

save_figure('cluster_nmdsfig_testcluster');

% -----------------------------------------------------------------------
% factor scores with reduced dimensions
% -----------------------------------------------------------------------
r = c.r;
[tmp c.eigenvalues] = cmdscale(c.D);
sqL = sqrt(diag(c.eigenvalues(1:c.ndims)));
B = inv(r) * c.GroupSpace(:,1:c.ndims) * sqL;   % inv(r) * factor loading matrix A = VsqrtL
c.factor_scores = c.dat * B;

% -----------------------------------------------------------------------
% Stepwise regression of dimensions on behavior
% -----------------------------------------------------------------------

if ~isempty(c.outcome)
    fprintf(1,'\n');
    fprintf(1,'Prediction of behavior with component scores (from classic MDS)\n');

    DATA.INDSCAL.STEPWISE = stepwise_tor(c.factor_scores,c.outcome);
    fprintf(1,'\n');
end

% -----------------------------------------------------------------------
% % plot nmds figures of this
% -----------------------------------------------------------------------
dosideplots = 0;
figname = nmdsfig_tools('nmdsfig_plot',c, addbtwn, dosideplots, 'nofill');

save_figure(figname);

% -----------------------------------------------------------------------
% separate plots of high/low behavior
% -----------------------------------------------------------------------
if ~isempty(c.outcome)  % plot interactions between beh and regional covariance
    create_wide_figure;
    subplot(1,2,1);
    [r,p]=corrcoef(c.dat(c.outcome>median(c.outcome),:)); r(r>.9999) = 1; sig = sign(r) .* (p < .05);
    nmdsfig(c.GroupSpace,'classes',c.ClusterSolution.classes,'names',c.names,'sig',sig);
    title('High behavior')

    subplot(1,2,2)
    [r,p]=corrcoef(c.dat(c.outcome<=median(c.outcome),:)); r(r>.9999) = 1; sig = sign(r) .* (p < .05);
    nmdsfig(c.GroupSpace,'classes',c.ClusterSolution.classes,'names',c.names,'sig',sig);
    title('Low behavior')
    drawnow
end

if ~isempty(c.ancova_codes)  % plot interactions between beh and regional covariance
    tor_fig(1,3); subplot(1,3,1);
    [r,p]=corrcoef(c.dat(c.ancova_codes>median(c.ancova_codes),:)); r(r>.9999) = 1; sig = sign(r) .* (p < .05);
    nmdsfig(c.GroupSpace,'classes',c.ClusterSolution.classes,'names',c.names,'sig',sig);
    title('High ANCOVA group')

    subplot(1,3,2)
    [r,p]=corrcoef(c.dat(c.ancova_codes<median(c.ancova_codes),:)); r(r>.9999) = 1; sig = sign(r) .* (p < .05);
    nmdsfig(c.GroupSpace,'classes',c.ClusterSolution.classes,'names',c.names,'sig',sig);
    title('Low ANCOVA group')
    drawnow

    subplot(1,3,3)
    [r,p]=corrcoef(c.dat(c.ancova_codes<median(c.ancova_codes),:)); r(r>.9999) = 1; sig = sign(r) .* (p < .05);
    nmdsfig(c.GroupSpace,'classes',c.ClusterSolution.classes,'names',c.names,'sig',c.STATS.siginteract);
    title('Interactions')

end

% -----------------------------------------------------------------------
% Optional plots of interactions and correlations
% -----------------------------------------------------------------------
if c.doancova && ~isempty(c.ancova_codes)
    [i,j] = find(triu(c.STATS.siginteract));
    if ~isempty(i),
        go = input([num2str(length(i)) ' significant behavior x covariance interactions.  Plot? ']);
        if go
            for ind = 1:length(i)
                cluster_orthviews(cl(i(ind)),{[1 0 0]});
                cluster_orthviews(cl(j(ind)),{[0 0 1]},'add');
                h = axes('Position',[.55 .05 .42 .42]);
                [b,t,p] = ancova(c.ancova_codes,c.dat(:,i(ind)),c.dat(:,j(ind)),1);
                xlabel(cl(i(ind)).shorttitle); ylabel(cl(j(ind)).shorttitle);
                title([]);
                input('Save and press a key for the next one')
            end
        end
    end
end

if c.doancova, c.doancova = 'Yes'; else c.doancova = 'No'; end

if ~any(c.ClusterSolution.pvals < .05)
    go  = input('No significant clustering.  Proceed with analyses anyway?');
    if ~go, return, end
end

% -----------------------------------------------------------------------
% Get average correlations within and between clusters
%
% Apply this clustering solution to the clusters
% Get individual 'network' scores and use them as predictors of behavior
% in multiple regression
% -----------------------------------------------------------------------

c = nmdsfig_tools('apply_clusters',c);

c = nmdsfig_tools('predict_behavior',c,'regions');
c = nmdsfig_tools('predict_behavior',c,'classes');

% -----------------------------------------------------------------------
% brain plots
% -----------------------------------------------------------------------
if isempty(overlay)
    cont = input('Show brain slices? ');
    if ~cont
        diary off

        save nmds_output c cl
        cd ..
        return
    end

    overlay = spm_get(1,'*img','Select overlay image');
else
    % we have overlay, implies we want slices

    cluster_orthviews_classes(cl,c.ClusterSolution.classes,overlay,'axial',0);
    save_figure('cluster_slices_axial');

    %     cluster_orthviews_showcenters(cl,'coronal',overlay);
    %     save_figure('cluster_slices_coronal');
    %
    %     cluster_orthviews_showcenters(cl,'saggital',overlay);
    %     save_figure('cluster_slices_saggital');
end

diary off

save nmds_output c cl
cd ..

return

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

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

% Sub-functions

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

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

% -----------------------------------------------------------------------
% Utility functions
% -----------------------------------------------------------------------
function save_figure(myname)
f1 = gcf;
scn_export_papersetup;
saveas(f1,myname,'png');
return

function f1 = create_wide_figure
scnsize = get(0,'ScreenSize');
f1 = figure('position',[50 50 scnsize(3)-100 scnsize(4)/2],'color','white');
return

function [pthr,sig] = fdr_correct_pvals(p,r)

psq = p; psq(find(eye(size(p,1)))) = 0;
psq = squareform(psq);
pthr = FDR(p,.05);
if isempty(pthr), pthr = 0; end

sig = sign(r) .* (p < pthr);

return


function [A, vA, vB, bb_rel] = crop_borders(A, bcol, padding, crop_amounts)
%CROP_BORDERS Crop the borders of an image or stack of images
%
%   [B, vA, vB, bb_rel] = crop_borders(A, bcol, [padding])
%
%IN:
%   A - HxWxCxN stack of images.
%   bcol - Cx1 background colour vector.
%   padding - scalar indicating how much padding to have in relation to
%             the cropped-image-size (0<=padding<=1). Default: 0
%   crop_amounts - 4-element vector of crop amounts: [top,right,bottom,left]
%             where NaN/Inf indicate auto-cropping, 0 means no cropping,
%             and any other value mean cropping in pixel amounts.
%
%OUT:
%   B - JxKxCxN cropped stack of images.
%   vA     - coordinates in A that contain the cropped image
%   vB     - coordinates in B where the cropped version of A is placed
%   bb_rel - relative bounding box (used for eps-cropping)

%{
% 06/03/15: Improved image cropping thanks to Oscar Hartogensis
% 08/06/15: Fixed issue #76: case of transparent figure bgcolor
% 21/02/16: Enabled specifying non-automated crop amounts
% 04/04/16: Fix per Luiz Carvalho for old Matlab releases
% 23/10/16: Fixed issue #175: there used to be a 1px minimal padding in case of crop, now removed
% 15/05/22: Fixed EPS bounding box (issue #356)
%}

    if nargin < 3
        padding = 0;
    end
    if nargin < 4
        crop_amounts = nan(1,4);  % =auto-cropping
    end
    crop_amounts(end+1:4) = NaN;  % fill missing values with NaN

    [h, w, c, n] = size(A);
    if isempty(bcol)  % case of transparent bgcolor
        bcol = A(ceil(end/2),1,:,1);
    end
    if isscalar(bcol)
        bcol = bcol(ones(c, 1));
    end

    % Crop margin from left
    if ~isfinite(crop_amounts(4))
        bail = false;
        for l = 1:w
            for a = 1:c
                if ~all(col(A(:,l,a,:)) == bcol(a))
                    bail = true;
                    break;
                end
            end
            if bail
                break;
            end
        end
    else
        l = 1 + abs(crop_amounts(4));
    end

    % Crop margin from right
    if ~isfinite(crop_amounts(2))
        bcol = A(ceil(end/2),w,:,1);
        bail = false;
        for r = w:-1:l
            for a = 1:c
                if ~all(col(A(:,r,a,:)) == bcol(a))
                    bail = true;
                    break;
                end
            end
            if bail
                break;
            end
        end
    else
        r = w - abs(crop_amounts(2));
    end

    % Crop margin from top
    if ~isfinite(crop_amounts(1))
        bcol = A(1,ceil(end/2),:,1);
        bail = false;
        for t = 1:h
            for a = 1:c
                if ~all(col(A(t,:,a,:)) == bcol(a))
                    bail = true;
                    break;
                end
            end
            if bail
                break;
            end
        end
    else
        t = 1 + abs(crop_amounts(1));
    end

    % Crop margin from bottom
    bcol = A(h,ceil(end/2),:,1);
    if ~isfinite(crop_amounts(3))
        bail = false;
        for b = h:-1:t
            for a = 1:c
                if ~all(col(A(b,:,a,:)) == bcol(a))
                    bail = true;
                    break;
                end
            end
            if bail
                break;
            end
        end
    else
        b = h - abs(crop_amounts(3));
    end

    if padding == 0  % no padding
        % Issue #175: there used to be a 1px minimal padding in case of crop, now removed
        %{
        if ~isequal([t b l r], [1 h 1 w]) % Check if we're actually croppping
            padding = 1; % Leave one boundary pixel to avoid bleeding on resize
            bcol(:) = nan;  % make the 1px padding transparent
        end
        %}
    elseif abs(padding) < 1  % pad value is a relative fraction of image size
        padding = sign(padding)*round(mean([b-t r-l])*abs(padding)); % ADJUST PADDING
    else  % pad value is in units of 1/72" points
        padding = round(padding);  % fix cases of non-integer pad value
    end

    if padding > 0  % extra padding
        % Create an empty image, containing the background color, that has the
        % cropped image size plus the padded border
        B = repmat(bcol,[(b-t)+1+padding*2,(r-l)+1+padding*2,1,n]);  % Fix per Luiz Carvalho
        % vA - coordinates in A that contain the cropped image
        vA = [t b l r];
        % vB - coordinates in B where the cropped version of A will be placed
        vB = [padding+1, (b-t)+1+padding, padding+1, (r-l)+1+padding];
        % Place the original image in the empty image
        B(vB(1):vB(2), vB(3):vB(4), :, :) = A(vA(1):vA(2), vA(3):vA(4), :, :);
        A = B;
    else  % extra cropping
        vA = [t-padding b+padding l-padding r+padding];
        A = A(vA(1):vA(2), vA(3):vA(4), :, :);
        vB = [NaN NaN NaN NaN];
    end

    % For EPS cropping, determine the relative BoundingBox - bb_rel
    bb_pixels = [l-1 h-b-1 r+1 h-t+1];  %[LowerLeftXY, UpperRightXY]
    bb_pixels(bb_pixels<0) = 0;
    bb_pixels = min(bb_pixels, [w h w h]);
    bb_rel = bb_pixels ./ [w h w h];
end

function A = col(A)
    A = A(:);
end


function outfig=view(Tree,varargin)
%VIEW View classification or regression tree graphically.
%   VIEW(T) displays the decision tree T as computed by the CLASSREGTREE
%   constructor in a figure window.  Each branch in the tree is labeled
%   with its decision rule, and each terminal node is labeled with the
%   predicted value for that node.  Click on any node to get more
%   information about it.  The information displayed is specified by
%   leftmost pop-up menu at the top of the figure.
%
%   VIEW(T,'PARAM1',val1,'PARAM2',val2,...) specifies optional
%   parameter name/value pairs:
%
%      'names'       A cell array of names for the predictor variables,
%                    in the order in which they appear in the X matrix
%                    from which the tree was created (see TREEFIT)
%      'prunelevel'  Initial pruning level to display
%
%   For each branch node, the left child node corresponds to the points
%   that satisfy the condition, and the right child node corresponds to
%   the points that do not satisfy the condition.
%
%   Example:  Create and graph classification tree for Fisher's iris data.
%             The names are abbreviations for the column contents (sepal
%             length, sepal width, petal length, and petal width).
%      load fisheriris;
%      t = classregtree(meas, species);
%      view(t,'names',{'SL' 'SW' 'PL' 'PW'});
%
%   See also CLASSREGTREE, CLASSREGTREE/EVAL, CLASSREGTREE/TEST, CLASSREGTREE/PRUNE.

%   Copyright 2006-2009 The MathWorks, Inc.
%   $Revision: 1.1.6.7 $  $Date: 2009/05/07 18:32:53 $

% Process inputs
nvars = max(abs(Tree.var));
names = Tree.names;
okargs =   {'names' 'prunelevel'};
defaults = {names   0};
[eid,emsg,names,curlevel] = getargs(okargs,defaults,varargin{:});
if ~isempty(eid)
   error(sprintf('stats:treedisp:%s',eid),emsg);
end
if isempty(names)
   names = cell(nvars,1);
   for j=1:nvars
      names{j} = sprintf('x%d',j);
   end
elseif length(names)<nvars
   error('stats:treedisp:InvalidNames',...
         'Incorrect number of variable names.');
end

doclass = isequal(Tree.method,'classification');

% Create empty figure and axes to receive tree display
fig = setupfigure(doclass);
try
   adjustmenu(fig);
catch
end

% Draw tree
[X,Y,Tree] = drawtree(Tree,fig,names,curlevel);

% Save information for call-backs
fulltree = Tree;
set(fig,'ButtonDownFcn',@removelabels, ...
        'UserData',{X Y 0 names doclass fulltree curlevel});

% Update ui elements
updateenable(fig);
updatelevel(fig,curlevel,fulltree);

if nargout>0
    outfig = fig;
end

% ----------------------------------------------
function [X,Y,Tree] = drawtree(Tree,fig,names,curlevel)
%DRAWTREE Draw decision tree into specified figure.

ax = get(fig,'CurrentAxes');
splitvar = Tree.var;
cutoff = Tree.cut;
parentnode = Tree.parent;
nonroot = parentnode~=0;

% Get coordinates of nodes
isleaf = splitvar==0;
[X,Y] = layouttree(Tree,isleaf);

% Which leaves and branches are pruned?
if ~isempty(Tree.prunelist)
   isbranch = (Tree.prunelist>curlevel);
else
   isbranch = ~isleaf;
end
if any(isbranch)
   isleaf = false(size(isbranch));
   c = Tree.children(isbranch,:);
   c = c(c>0);
   isleaf(c) = 1;
   isleaf = isleaf & ~isbranch;
else
   isleaf = ~nonroot;
end
pruned = ~(isleaf | isbranch);

branchnodes = find(isbranch);
leafnodes = find(isleaf);
doclass = isequal(Tree.method,'classification');

% Get coordinates of connecting lines
p = parentnode(nonroot & ~pruned);
x = [X(nonroot & ~pruned)'; X(p)'; NaN+p'];
y = [Y(nonroot & ~pruned)'; Y(p)'; NaN+p'];

% Plot nodes and connections for unpruned nodes, but stop listening to axis
% and remember some things that may get changed during the plotting
axislistener(ax,false);
xlim = get(ax,'XLim');
ylim = get(ax,'YLim');
ud = get(ax,'UserData');
h = plot(X(branchnodes),Y(branchnodes),'b^', ...
         X(leafnodes),Y(leafnodes),'b.', ...
         x(:),y(:),'b-','Parent',ax);
set(ax,'UserData',ud,'Visible','off','XLim',xlim,'YLim',ylim);
axislistener(ax,true);

% Same for pruned nodes
t = nonroot & pruned;
p = parentnode(t);
x = [X(t)'; X(p)'; NaN+p'];
y = [Y(t)'; Y(p)'; NaN+p'];
line('Parent',ax,'XData',X(pruned),'YData',Y(pruned),'Tag','prunednode',...
     'Marker','o','Color',[.2 .2 .2],'Linestyle','none','HitTest','off');
line('Parent',ax,'XData',x(:),'YData',y(:),'Tag','prunedconnection',...
     'Marker','none','LineStyle',':','Color',[.2 .2 .2],'HitTest','off');
if length(h)==3
   set(h(1),'ButtonDownFcn',@labelpoint,'Tag','branch','MarkerSize',10);
   set(h(2),'ButtonDownFcn',@labelpoint,'Tag','leaf','MarkerSize',20);
   set(h(end),'HitTest','off','Tag','connection');
else
   set(h,'ButtonDownFcn',@labelpoint,'Tag','leaf','MarkerSize',20);
end

% Label leaf nodes with class, branch nodes with split rule
if doclass
   cnames = Tree.classname;
   ctext = cnames(Tree.class(leafnodes));
else
   ctext = num2str(Tree.class(leafnodes));
end

h = findobj(fig,'Tag','menuleaf');
vis = get(h,'Checked');
text(X(leafnodes),Y(leafnodes),ctext,'HitTest','off','Parent',ax,...
         'VerticalAlignment','top','HorizontalAlignment','center',...
          'Tag','leaflabel','Clipping','on','Visible',vis,'Interpreter','none');

lefttext = cell(length(branchnodes),1);
righttext = cell(length(branchnodes),1);
for j=1:length(branchnodes)
   k = branchnodes(j);
   cut = cutoff{k};
   if splitvar(k)>0
      varname = names{splitvar(k)};
      lefttext{j} = sprintf('%s < %g   ',varname,cut);
      righttext{j} = sprintf('  %s >= %g',varname,cut);
   else
      varname = names{-splitvar(k)};
      cats = cut{1};
      if length(cats)==1
         lefttext{j} = sprintf('%s = %s   ',varname,num2str(cats,'%g '));
      else
         lefttext{j} = sprintf('%s in (%s)   ',varname,num2str(cats,'%g '));
      end
      cats = cut{2};
      if length(cats)==1
         righttext{j} = sprintf('   %s = %s',varname,num2str(cats,'%g '));
      else
         righttext{j} = sprintf('   %s in (%s)',varname,num2str(cats,'%g '));
      end
   end
end

h = findobj(fig,'Tag','menubr');
vis = get(h,'Checked');
text(X(branchnodes),Y(branchnodes),lefttext,'HitTest','off','Parent',ax,...
     'Tag','branchlabel','Clipping','on','Visible',vis,'Interpreter','none',...
     'HorizontalAlignment','right');
text(X(branchnodes),Y(branchnodes),righttext,'HitTest','off','Parent',ax,...
     'Tag','branchlabel','Clipping','on','Visible',vis,'Interpreter','none',...
     'HorizontalAlignment','left');

% Show pruned nodes or not as desired
doprunegraph(fig);

% Adjust axes contents
dozoom(fig);

% Adjust layout of controls to fit figure
layoutfig(fig);

% ---------------------------------------------
function growprune(varargin)
%GROWPRUNE Expand or contract tree using optimal pruning sequence.

% Fetch stored information
h = gcbo;
fig = gcbf;
ud = get(fig,'UserData');
names    = ud{4};
doclass  = ud{5};
curlevel = ud{7};
fulltree = ud{6};

% Get optimal pruning sequence and current pruning level
prunelist = fulltree.prunelist;
if isequal(get(h,'Tag'),'prune')
   curlevel = min(max(prunelist),curlevel+1);
else
   curlevel = max(0,curlevel-1);
end

% Clear axes, then draw at new pruning level
ax = get(fig,'CurrentAxes');
delete(get(ax,'Children'));
[X,Y] = drawtree(fulltree,fig,names,curlevel);

% Remember everything
set(fig,'ButtonDownFcn',@removelabels, ...
        'UserData',{X Y 0 names doclass fulltree curlevel});

updateenable(fig);
updatelevel(fig,curlevel,fulltree);

% ----------------------------------------------
function updatelevel(fig,curlevel,Tree)
%UPDATELEVEL Update text display of current pruning level.

if ~isempty(Tree.prunelist)
   maxlevel = max(Tree.prunelist);
else
   maxlevel = 0;
end
h = findobj(fig,'Tag','prunelev');
set(h,'String',sprintf('%d of %d',curlevel,maxlevel));
e = get(h,'Extent');
p = get(h,'Position');
p(3) = e(3);
set(h,'Position',p);

% ----------------------------------------------
function updateenable(fig)
%UPDATEENABLE Update enabled/disabled status of buttons in figure

ud = get(fig,'UserData');
curlevel = ud{7};
fulltree = ud{6};
enableg = 'on';     % enable grow button?
enablep = 'on';     % enable prune button?
if isempty(fulltree.prunelist)
   enableg = 'off';
   enablep = 'off';
else
   maxlevel = max(fulltree.prunelist);
   if curlevel >= maxlevel
      enablep = 'off';
   end
   if curlevel <= 0;
      enableg = 'off';
   end
end
set(findobj(fig,'tag','prune'),'Enable',enablep);
set(findobj(fig,'tag','grow'),'Enable',enableg);

% ----------------------------------------------
function labelpoint(varargin)
%LABELPOINT Label point on tree in response to mouse click.

h = gcbo;
f = gcbf;
stype = get(f,'SelectionType');
if ~isequal(stype,'alt') && ~isequal(stype,'extend')
   removelabels;
end
t = get(h,'Tag');
if isequal(t,'branch') || isequal(t,'leaf')
   ud = get(f,'UserData');
   X = ud{1};       % x coordinates
   Y = ud{2};       % y coordinates
   names = ud{4};   % variable names
   doclass = ud{5}; % classification, not regression
   Tree = ud{6};    % complete tree

   if doclass
      cnames = Tree.classname;
   end
   splitvar = Tree.var;
   cutoff = Tree.cut;

   % Find closest point
   ax = get(f,'CurrentAxes');
   cp = get(ax,'CurrentPoint');
   [ignore,node] = min(abs(X-cp(1,1)) + abs(Y-cp(1,2)));

   uih = findobj(f,'Tag','clicklist');
   labeltype = get(uih,'Value');

   if isequal(labeltype,4) && doclass
      % Show fitted class probabilities
      P = Tree.classprob;
      txt = 'Class probabilities:';
      for j=1:size(P,2);
         txt = sprintf('%s\n%s = %.3g',txt,cnames{j},P(node,j));
      end

   elseif isequal(labeltype,3) && ~doclass
      % Show node statistics
      xbar = Tree.class(node);
      Nk = Tree.nodesize(node);
      if Nk > 1
         s = sqrt((Tree.nodeerr(node) * Nk) / (Nk - 1));
         txt = sprintf('N = %d\nMean = %g\nStd. dev. = %g',Nk,xbar,s);
      else
         txt = sprintf('N = %d\nMean = %g',Nk,xbar);
      end

   elseif isequal(labeltype,3) && doclass
      % Show class membership in data
      C = Tree.classcount;
      N = Tree.nodesize(node);
      txt = sprintf('Total data points = %d',N);
      for j=1:size(C,2);
         txt = sprintf('%s\n%d %s',txt,C(node,j),cnames{j});
      end

   elseif isequal(labeltype,1)
      % Get a display of the split rule at branch nodes,
      % or the majority class at leaf nodes
      if ~isequal(t,'branch')
         if doclass
            txt = sprintf('Node %d (leaf)\nClass: %s',node,cnames{Tree.class(node)});
         else
            txt = sprintf('Node %d (leaf)\nPrediction: %g',node,Tree.class(node));
         end
      elseif splitvar(node)>0
         txt = sprintf('Node %d (branch)\nRule:  %s < %g',...
                       node,names{splitvar(node)},cutoff{node});
      else
         cut = cutoff{node};
         cats = cut{1};
         if length(cats)==1
            txt = sprintf('Node %d (branch)\nRule:  %s = %s',node,...
                          names{-splitvar(node)},num2str(cats,'%g '));
         else
            txt = sprintf('Node %d (branch)\nRule:  %s in (%s)',node,...
                          names{-splitvar(node)},num2str(cats,'%g '));
         end
      end
   elseif isequal(labeltype,2)
      % Get the conditions satisfied by points in this node
      if node==1
         txt = 'Root of tree';
      else
         % Find limits for each variable along the path to this node
         nvars = max(abs(splitvar(:)));
         lims = cell(nvars,3);
         lims(:,1) = {-Inf};               % lower limit
         lims(:,2) = {Inf};                % upper limit
         lims(:,3) = num2cell((1:nvars)'); % variable number
         p = Tree.parent(node);
         c = node;
         while(p>0)
            leftright = 1 + (Tree.children(p,2)==c);
            vnum = splitvar(p);
            vcut = cutoff{p};
            if vnum>0
               if isinf(lims{vnum,3-leftright})
                  lims{vnum,3-leftright} = vcut;
               end
            else
               if ~iscell(lims{-vnum,1})
                  lims{-vnum,1} = vcut{leftright};
               end
            end
            c = p;
            p = Tree.parent(p);
         end

         % Create label listing any variable with finite limits
         txt = 'At this node:';
         for j=1:size(lims,1);
            L1 = lims{j,1};
            L2 = lims{j,2};
            if ~iscell(L1) && isinf(L1) && isinf(L2)
               continue
            end
            vnum = lims{j,3};

            if iscell(L1)
               cats = L1{1};
               if length(cats)==1
                  txt = sprintf('%s\n%s = %s',txt,names{vnum},num2str(cats,'%g '));
               else
                  txt = sprintf('%s\n%s \\in (%s)',txt,names{vnum},num2str(cats,'%g '));
               end
            elseif isinf(L1)
               txt = sprintf('%s\n%s < %g',txt,names{vnum},L2);
            elseif isinf(L2)
               txt = sprintf('%s\n%g < %s',txt,L1,names{vnum});
            else
               txt = sprintf('%s\n%g < %s < %g',txt,L1,names{vnum},L2);
            end
         end
      end
   else
      txt = '';
   end

   % Add label
   if ~isempty(txt)
      x = X(node);
      y = Y(node);
      xlim = get(gca,'xlim');
      ylim = get(gca,'ylim');
      if x<mean(xlim)
         halign = 'left';
         dx = 0.02;
      else
         halign = 'right';
         dx = -0.02;
      end
      if y<mean(ylim)
         valign = 'bottom';
         dy = 0.02;
      else
         valign = 'top';
         dy = -0.02;
      end
      h = text(x+dx*diff(xlim),y+dy*diff(ylim),txt,'Interpreter','none');
      yellow = [1 1 .85];
      set(h,'backgroundcolor',yellow,'margin',3,'edgecolor','k',...
            'HorizontalAlignment',halign,'VerticalAlignment',valign,...
            'tag','LinescanText','ButtonDownFcn',@startmovetips);
      line(x,y,'Color',yellow,'Marker','.','MarkerSize',20,...
           'Tag','LinescanMarker');
   end
end

% ----------------------------------------------
function startmovetips(varargin)
%STARTMOVETIPS Start movement of node tips.

f = gcbf;
set(f,'WindowButtonUpFcn',@donemovetips,...
      'WindowButtonMotionFcn',@showmovetips,...
      'Interruptible','off','BusyAction','queue');

o = gcbo;
p1 = get(f,'CurrentPoint');
a = get(f,'CurrentAxes');
ud = get(a,'UserData');
ud(1:2) = {o p1};
set(a,'UserData',ud);

% ----------------------------------------------
function showmovetips(varargin)
%SHOWMOVETIPS Show current movement of node tips.
domovetips(0,varargin{:});

% ----------------------------------------------
function donemovetips(varargin)
%DONEMOVETIPS Finish movement of node tips.
domovetips(1,varargin{:});

% ----------------------------------------------
function domovetips(alldone,varargin)
%DOMOVETIPS Carry out movement of node tips.

f = gcbf;
if alldone
   set(f,'WindowButtonUpFcn','','WindowButtonMotionFcn','');
end
a = get(f,'CurrentAxes');
ud = get(a,'UserData');
o = ud{1};
p1 = ud{2};
p2 = get(f,'CurrentPoint');
p0 = get(a,'Position');
pos = get(o,'Position');
dx = (p2(1)-p1(1)) * diff(get(a,'XLim')) / p0(3);
dy = (p2(2)-p1(2)) * diff(get(a,'YLim')) / p0(4);
pos(1) = pos(1) + dx;
pos(2) = pos(2) + dy;
set(o,'Position',pos);
ud{2} = p2;
set(a,'UserData',ud);

% ----------------------------------------------
function resize(varargin)
%RESIZE Resize figure showing decision tree.
layoutfig(gcbf)

% ----------------------------------------------
function layoutfig(f)
%LAYOUTFIG Layout figure contents

set(f,'Units','points');
fpos = get(f,'Position');

% Resize frame
h = findobj(f,'Tag','frame');
frpos = get(h,'Position');
frpos(2) = fpos(4) - frpos(4);
frpos(3) = fpos(3);
set(h,'Position',frpos);

% Resize controls inside frame
tags = {'clicktext'  'clicklist'  'magtext' 'maglist' ...
        'pruneframe' 'prunelabel' 'prunelev'};
mult = [1.6          1.35         1.6       1.35      ...
        1.7          1.6          1.6];
for j=1:length(tags)
   h = findobj(f,'Tag',tags{j});
   p = get(h,'Position');
   if j==1, theight = p(4); end
   p(2) = fpos(4) - mult(j)*theight;
   set(h,'Position',p);
end

h = findobj(f,'Tag','grow');
p = get(h,'Position');
p(2) = frpos(2)+2;
set(h,'Position',p);
h = findobj(f,'Tag','prune');
p(2) = p(2)+p(4);
set(h,'Position',p);

% Resize sliders
hh = findobj(f,'Tag','hslider');
hv = findobj(f,'Tag','vslider');
p1 = get(hh,'Position');
sw = p1(4);
p1(3) = frpos(3) - sw - 1;
set(hh,'Position',p1);
p2 = get(hv,'Position');
p2(1) = frpos(3) - sw - 1;
p2(4) = frpos(2) - sw - 1;
set(hv,'Position',p2);
if isequal(get(hh,'Visible'),'off')
   sw = 0;
end

% Resize axes
h = get(f,'CurrentAxes');
p = [0, sw, frpos(3)-sw, frpos(2)-sw];
set(h,'Position',p);

% ----------------------------------------------
function removelabels(varargin)
%REMOVELABELS Remove any labels remaining on the graph.

f = gcbf;
delete(findall(f,'Tag','LinescanMarker'));
delete(findall(f,'Tag','LinescanText'));

% ----------------------------------------------
function [X,Y] = layouttree(Tree,isleaf)
%LAYOUTTREE Select x,y coordinates of tree elements.
n = length(Tree.node);
X = zeros(n,1);
Y = X;
layoutstyle = 1;

% Plot top node on one level, its children at next level, etc.
for j=1:n
   p = Tree.parent(j);
   if p>0
      Y(j) = Y(p)+1;
   end
end
if layoutstyle==1
   % Layout style 1
   % Place terminal nodes, then put parents above them

   % First get preliminary placement, used to position leaf nodes
   for j=1:n
      p = Tree.parent(j);
      if p==0
         X(j) = 0.5;
      else
         dx = 2^-(Y(j)+1);
         if j==Tree.children(p,1)
            X(j) = X(p) - dx;
         else
            X(j) = X(p) + dx;
         end
      end
   end

   % Now make leaf nodes equally spaced, preserving their order
   leaves = find(isleaf);
   nleaves = length(leaves);
   [ignore,b] = sort(X(leaves));
   X(leaves(b)) = (1:nleaves) / (nleaves+1);

   % Position parent nodes above and between their children
   for j=max(Y):-1:0
      a = find(~isleaf & Y==j);
      c = Tree.children(a,:);
      X(a) = (X(c(:,1))+X(c(:,2)))/2;
   end
else
   % Layout style 2
   % Spread out the branch nodes, somewhat under their parent nodes
   X(Y==0) = 0.5;
   for j=1:max(Y)
      vis = (Y==j);                  % real nodes at this level
      invis = (Y==(j-1) & isleaf);   % invisible nodes to improve layout
      visrows = find(vis);
      nvis = sum(vis);
      nboth = nvis + sum(invis);
      x = [X(Tree.parent(vis))+1e-10*(1:nvis)'; X(invis)];
      [xx,xidx] = sort(x);
      xx(xidx) = 1:nboth;
      X(visrows) = (xx(1:nvis) / (nboth+1));
   end
end

k = max(Y);
Y = 1 - (Y+0.5)/(k+1);

% ---------------------------------------
function fig = setupfigure(doclass)
%SETUPFIGURE Set up uicontrols on decision tree figure.

fig = figure('IntegerHandle','off', 'NumberTitle','off', ...
             'Units','points','PaperPositionMode','auto',...
             'Tag','tree viewer');
ax = axes('Parent',fig,'UserData',cell(1,4),'XLim',0:1,'YLim',0:1);

% Set default print options
pt = printtemplate;
pt.PrintUI = 0;
set(fig,'PrintTemplate',pt)

if doclass
   figtitle = 'Classification tree viewer';
else
   figtitle = 'Regression tree viewer';
end

% Arrange figure contents
pos = [0 0 1 1];
set(ax,'Visible','off','XLim',0:1,'YLim',0:1,'Position',pos);
set(ax,'Units','points');
apos = get(ax,'Position');
fpos = get(fig,'Position');
hframe = uicontrol(fig,'Units','points','Style','frame',...
                   'Position',[0 0 1 1],'Tag','frame');

% Tip-related items
h=uicontrol(fig,'units','points','Tag','clicktext',...
            'String','Click to display:', 'style','text',...
            'HorizontalAlignment','left','FontWeight','bold');
extent = get(h,'Extent');
theight = extent(4);
aheight = apos(4);
tbottom = aheight - 1.5*theight;
posn = [2, tbottom, 150, theight];
set(h,'Position',posn);
textpos = posn;
e = get(h,'Extent');
if doclass
   choices = 'Identity|Variable ranges|Class membership|Estimated probabilities';
else
   choices = 'Identity|Variable ranges|Node statistics';
end
posn = [e(1)+e(3)+2, aheight-1.25*theight, 100, theight];
uicontrol(fig,'units','points','position',posn,'Tag','clicklist',...
            'String',choices, 'Style','pop','BackgroundColor',ones(1,3),...
            'Callback',@removelabels);
set(ax,'Position',[0 0 apos(3) tbottom]);
set(fig,'Toolbar','figure', 'Name',figtitle,'HandleVisibility','callback');

% Magnification items
textpos(1) = posn(1) + posn(3) + 10;
h=uicontrol(fig,'units','points','Tag','magtext','Position',textpos,...
            'String','Magnification:', 'style','text',...
            'HorizontalAlignment','left','FontWeight','bold');
e = get(h,'Extent');
textpos(3) = e(3);
set(h,'Position',textpos);
posn = [textpos(1)+textpos(3)+2, posn(2), 55, posn(4)];
h=uicontrol(fig,'units','points','position',posn,'Tag','maglist',...
            'String','x', 'Style','pop','BackgroundColor',ones(1,3),...
            'Callback',@domagnif);
adjustcustomzoom(h,false);

% Prune-related items
textpos(1) = posn(1) + posn(3) + 10;
h = uicontrol(fig,'units','points','position',textpos,'Tag','prunelabel',...
              'Style','text','HorizontalAlignment','left',...
              'FontWeight','bold','String','Pruning level:');
e = get(h,'Extent');
textpos(3) = e(3);
set(h,'Position',textpos);

posn(1) = textpos(1) + textpos(3) + 5;
posn(2) = posn(2) - 0.25*e(4);
posn(3) = 60;
posn(4) = 1.5*e(4);
textpos(1) = posn(1) + 3;
textpos(3) = posn(3) - 6;
uicontrol(fig,'Style','frame','Units','points','Position',posn,...
              'Tag','pruneframe');
uicontrol(fig,'units','points','position',textpos,'Tag','prunelev',...
              'Style','text','HorizontalAlignment','left',...
              'FontWeight','bold','String','1234 of 9999');

% Create an arrow for labeling button controls
fcolor = get(fig,'Color');
ar = ...
[1 1 1 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1 1 1 1
 1 0 0 0 0 0 0 0 0 0 0 0 1
 1 1 0 0 0 0 0 0 0 0 0 1 1
 1 1 1 0 0 0 0 0 0 0 1 1 1
 1 1 1 1 0 0 0 0 0 1 1 1 1
 1 1 1 1 1 0 0 0 1 1 1 1 1
 1 1 1 1 1 1 0 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1 1 1 1];
ar = repmat(ar,[1 1 3]);
ar(:,:,1) = min(ar(:,:,1),fcolor(1));
ar(:,:,2) = min(ar(:,:,2),fcolor(2));
ar(:,:,3) = min(ar(:,:,3),fcolor(3));

posn(1) = posn(1) + posn(3);
posn(4) = posn(4)/2;
posn(3) = posn(4);
uicontrol(fig,'units','points','position',posn,'Tag','prune',...
              'CData',ar(end:-1:1,:,:),...
              'Style','pushbutton','Callback',@growprune);
posn(2) = posn(2) + posn(4);
uicontrol(fig,'units','points','position',posn,'Tag','grow',...
              'CData',ar,...
              'Style','pushbutton','Callback',@growprune);

if posn(1)+posn(3) > fpos(3)
   fpos(3) = posn(1)+posn(3);
   set(fig,'Position',fpos);
   apos(3) = fpos(3);
   set(ax,'Position',apos);
end

% Adjust frame position
lowest = min(posn(2),textpos(2))-2;
frpos = apos;
frpos(4) = 1.1*(apos(4)-lowest);
frpos(2) = apos(4) - frpos(4);
set(hframe,'Position',frpos);

% Add scroll bars, start out invisible
h1 = uicontrol(fig,'Style','slider','Tag','hslider','Visible','off',...
               'Units','points','Callback',@dopan);
p1 = get(h1,'Position');
sw = p1(4);               % default slider width
p1(1:2) = 1;
p1(3) = fpos(3)-sw;
set(h1,'Position',p1);
p2 = [fpos(3)-sw, sw, sw, frpos(2)-sw];
uicontrol(fig,'Style','slider','Tag','vslider','Visible','off',...
               'Units','points','Position',p2,'Callback',@dopan);

% Add new menu before the Window menu
hw = findall(fig,'Type','uimenu', 'Label','&Window');
h0 = uimenu(fig,'Label','T&ree','Position',get(hw,'Position'));
uimenu(h0, 'Label','Show &Full Tree', 'Position',1,'Tag','menufull',...
           'Checked','on','Callback',@domenu);
uimenu(h0, 'Label','Show &Unpruned Nodes', 'Position',2,'Tag','menuunpr',...
           'Checked','off','Callback',@domenu);
uimenu(h0, 'Label','Label &Branch Nodes', 'Position',3,'Tag','menubr',...
           'Checked','on','Callback',@domenu,'Separator','on');
uimenu(h0, 'Label','Label &Leaf Nodes', 'Position',4,'Tag','menuleaf',...
           'Checked','on','Callback',@domenu);

set(fig,'ResizeFcn',@resize);

% ------------------------------------------
function domenu(varargin)
%DOMENU Carry out menu actions for tree viewer.

o = gcbo;
f = gcbf;
t = get(o,'Tag');
switch(t)
 % Change display from full tree to unpruned nodes or vice versa
 case {'menufull' 'menuunpr'}
   ischecked = isequal(get(o,'Checked'),'on');
   isfull = isequal(t,'menufull');
   if isfull
      dofull = ~ischecked;
   else
      dofull = ischecked;
   end
   mfull = findobj(f,'Type','uimenu','Tag','menufull');
   munpr = findobj(f,'Type','uimenu','Tag','menuunpr');
   if dofull
      set(mfull,'Checked','on');
      set(munpr,'Checked','off');
   else
      set(mfull,'Checked','off');
      set(munpr,'Checked','on');
   end
   doprunegraph(f,dofull);
   dozoom(f);

 % Turn on/off branch labels
 case 'menubr'
   curval = get(o,'Checked');
   if isequal(curval,'on')
      set(o,'Checked','off');
      h = findobj(f,'Type','text','Tag','branchlabel');
      set(h,'Visible','off');
   else
      set(o,'Checked','on');
      h = findobj(f,'Type','text','Tag','branchlabel');
      set(h,'Visible','on');
   end

 % Turn on/off leaf labels
 case 'menuleaf'
   curval = get(o,'Checked');
   if isequal(curval,'on')
      set(o,'Checked','off');
      h = findobj(f,'Type','text','Tag','leaflabel');
      set(h,'Visible','off');
   else
      set(o,'Checked','on');
      h = findobj(f,'Type','text','Tag','leaflabel');
      set(h,'Visible','on');
   end
end

% ------------------------------------------
function doprunegraph(f,dofull)
%DOPRUNEGRAPH Adjust graph to show full/pruned setting

a = get(f,'CurrentAxes');
h1 = findobj(a,'Type','line','Tag','prunednode');
h2 = findobj(a,'Type','line','Tag','prunedconnection');

% Figure out whether to show full tree
if nargin<2
   o = findobj(f,'Type','uimenu','Tag','menufull');
   dofull = isequal(get(o,'Checked'),'on');
end

% Adjust graph
if dofull
   set(h1,'Visible','on');
   set(h2,'Visible','on');
   xlim = get(a,'XLim');
   ylim = get(a,'YLim');
   bigxlim = 0:1;
   bigylim = 0:1;
else
   set(h1,'Visible','off');
   set(h2,'Visible','off');
   h1 = findobj(f,'Type','line','Tag','leaf');
   h2 = findobj(f,'Type','line','Tag','branch');
   x1 = get(h1,'XData');
   y1 = get(h1,'YData');
   y2 = get(h2,'YData');
   dx = 1 / (1+length(x1));
   ally = sort(unique([y1(:); y2(:)]));
   if length(ally)>1
      dy = 0.5 * (ally(2)-ally(1));
   else
      dy = 1-ally;
   end
   xlim = [min(x1)-dx, max(x1)+dx];
   ylim = [min(ally)-dy, max(ally)+dy];
   bigxlim = 0:1;
   bigylim = [ylim(1) 1];
end
axislistener(a,false);
set(a,'XLim',xlim,'YLim',ylim);
axislistener(a,true);
hh = findobj(f,'Tag','hslider');
set(hh,'UserData',bigxlim);
hv = findobj(f,'Tag','vslider');
set(hv,'UserData',bigylim);

% ------------------------------------------
function domagnif(varargin)
%DOMAGNIF React to magnification level change

f = gcbf;
o = gcbo;

% We need sliders if the magnification level is > 100%
h = [findobj(f,'Tag','hslider'), findobj(f,'Tag','vslider')];
maglevel = get(o,'Value');
if maglevel==1
   set(h,'Visible','off');
else
   set(h,'Visible','on');
end

% Adjust layout if necessary
resize;

% Adjust axes contents
dozoom(f);

% Remove custom zoom amount from list if not in use
if maglevel<=4
   adjustcustomzoom(o,false);
end

% Turn off any manual zooming
zoom(f,'off');

% --------------------------------------------------
function adjustcustomzoom(o,add)
%ADJUSTCUSTOMZOOM Add or remove special custom magnification level
nchoices = size(get(o,'String'),1);
choices = '100%|200%|400%|800%';
if ~add && nchoices~=4
   set(o,'String',choices);
elseif add && nchoices~=5
   choices = [choices '|Custom'];
   set(o,'String',choices);
end

% ------------------------------------------
function dozoom(f)
%DOZOOM Adjust axes contents to match magnification settings

a = get(f,'CurrentAxes');
hh = findobj(f,'Tag','hslider');
hv = findobj(f,'Tag','vslider');
hm = findobj(f,'Tag','maglist');

% Get information about x/y ranges and current midpoint
bigxlim = get(hh,'UserData');
bigylim = get(hv,'UserData');
xlim = get(a,'XLim');
ylim = get(a,'YLim');
currx = (xlim(1)+xlim(2))/2;
curry = (ylim(1)+ylim(2))/2;

% How much are we magnifying each axis?
magfact = [1 2 4 8];
mag = get(hm,'Value');
if mag<=4
   magfact = magfact(mag)*ones(1,2);
else
   magfact = [diff(bigxlim)/diff(xlim), diff(bigylim)/diff(ylim)];
end
magfact = max(magfact,1);

if all(magfact==1)                 % no magnification
   xlim = bigxlim;
   ylim = bigylim;
else                               % magnify by showing a subset of range
   magfact = max(magfact,1.01);
   dx = diff(bigxlim)/magfact(1);
   dy = diff(bigylim)/magfact(2);
   xval = max(bigxlim(1), min(bigxlim(2)-dx, currx-dx/2));
   xlim = xval + [0,dx];
   yval = max(bigylim(1), min(bigylim(2)-dy, curry-dy/2));
   ylim = yval + [0,dy];
   set(hh,'Min',bigxlim(1),'Max',bigxlim(2)-dx,'Value',xval);
   set(hv,'Min',bigylim(1),'Max',bigylim(2)-dy,'Value',yval);
end
axislistener(a,false);
set(a,'XLim',xlim,'YLim',ylim);
axislistener(a,true);

% ------------------------------------------
function dopan(varargin)
%DOPAN Pan around magnified tree display

f = gcbf;
a = get(f,'CurrentAxes');
o = gcbo;
val = get(o,'Value');

axislistener(a,false);
if isequal(get(o,'Tag'),'hslider')
   xlim = get(a,'XLim');
   xlim = xlim + (val-xlim(1));
   set(a,'XLim',xlim);
else
   ylim = get(a,'YLim');
   ylim = ylim + (val-ylim(1));
   set(a,'YLim',ylim);
end
axislistener(a,true);

% -----------------------------------------
function axislistener(a,enable)
%AXISLISTENER Enable or disable listening to axis limit changes

f = get(a,'Parent');
ud = get(a,'UserData');
if enable
   % Start listening to axis limit changes
   list1 = addlistener(a, 'XLim', 'PostSet', @(src,evt) customzoom(f));
   list2 = addlistener(a, 'YLim', 'PostSet', @(src,evt) customzoom(f));
   ud(3:4) = {list1 list2};
else
   % Delete listeners
   for j=3:4
      lstnr = ud{j};
      if ~isempty(lstnr) && ishandle(lstnr), delete(lstnr); end
   end
   ud(3:4) = {[]};
end
set(a,'UserData',ud);

% -----------------------------------------
function customzoom(f)
%CUSTOMPAN Deal with panning of a zoomed tree view

a = get(f,'CurrentAxes');
xlim = get(a,'XLim');
ylim = get(a,'YLim');

hh = findobj(f,'Tag','hslider');
hv = findobj(f,'Tag','vslider');
hm = findobj(f,'Tag','maglist');

bigxlim = get(hh,'UserData');
bigylim = get(hv,'UserData');
magfact = [1 2 4 8];

% Figure out if we have a standard zoom amount (100%, 200%, etc.) or
% a custom zoom amount
xratio = diff(bigxlim) / diff(xlim);
yratio = diff(bigylim) / diff(ylim);
standard = abs(xratio-yratio)<=0.02 & abs(xratio-round(xratio))<=0.02 ...
                                    & abs(yratio-round(yratio))<=0.02;
if standard
   xratio = round(xratio);
   standard = ismember(xratio,magfact);
end

% Update the magnification setting
if standard
   set(hm,'Value',find(magfact==xratio));
   adjustcustomzoom(hm,false);
   if xratio==1
      h = [findobj(f,'Tag','hslider'), findobj(f,'Tag','vslider')];
      set(h,'Visible','off');
   end
else
   adjustcustomzoom(hm,true);
   set(hm,'Value',5);
   h = [findobj(f,'Tag','hslider'), findobj(f,'Tag','vslider')];
   set(h,'Visible','on');
end

dozoom(f);

% ---------------------- helper to fix menu contents
function adjustmenu(fig)
%ADJUSTMENU Adjust contents of curve fit plot menus and toolbar

% Remove some menus entirely]
badmenus = {'&Edit' '&View' '&Insert'};
h = findall(fig, 'Type','uimenu', 'Parent',fig);
for j=1:length(badmenus)
   h0 = findall(h,'flat', 'Label',badmenus{j});
   if (~isempty(h0))
      j = find(h==h0);
      delete(h0);
      h(j) = [];
   end
end

% Add or remove some items from other menus
% Fix FILE menu
h0 = findall(h,'flat', 'Label','&File');
h1 = findall(h0, 'Type','uimenu', 'Parent',h0);
for j=length(h1):-1:1
   mlabel = get(h1(j),'Label');
   if isempty(findstr(mlabel,'Print...'))
      delete(h1(j));
      h1(j) = [];
   end
end

% Fix TOOLS menu
h0 = findall(h,'flat', 'Label','&Tools');
h1 = findall(h0, 'Type','uimenu', 'Parent',h0);
for j=length(h1):-1:1
   mlabel = get(h1(j),'Label');
   if (isempty(findstr(mlabel,'Zoom'))&& isempty(findstr(mlabel,'Pan')))
     delete(h1(j));
     h1(j) = [];
   end
end

% Fix HELP menu
h0 = findall(h,'flat', 'Label','&Help');
h1 = findall(h0, 'Type','uimenu', 'Parent',h0);
delete(h1);
uimenu(h0, 'Label', '&Help Tree Viewer', 'Position',1,'Callback',@helpviewer);

% Remove icons that don't apply here.  Keep zoom, PAN and print only.
h0 = findall(fig,'Type','uitoolbar');
h1 = findall(h0,'Parent',h0);
for j=length(h1):-1:1
   mlabel = get(h1(j),xlate('TooltipString'));
   if isempty(findstr(mlabel,'Zoom')) && isempty(findstr(mlabel,'Print')) && isempty(findstr(mlabel,'Pan'))
      delete(h1(j));
   end
end

% ---------------------- callback to display help
function helpviewer(varargin)
mapfilename = [docroot '/toolbox/stats/stats.map'];
helpview(mapfilename, 'view_classregtree_ref');


function writePOLY_triangle(varargin)
  % WRITEPOLY_TRIANGLE prints a vertices to a .poly file, with E connecting
  % those vertices OR triangels made from F. Suitable for use with TRIANGLE
  %
  % writePOLY_triangle(filename,poly_struct)
  % writePOLY_triangle(filename,V,E,H)
  %
  % Input
  %   filename:  name of output file as string (caution! will clobber
  %                    existing)
  %   poly:      struct array where each element contains fields:
  %                    x,y,hole
  %     OR
  %   V  #V by dim=2 list of vertex positions
  %   E  #E by 2 list of edges E
  %   H  #H by dim list of hole positions
  %     OR
  %   V  #V by dim list of vertex positions
  %   F  #F struct containing polygon information arrays
  %     .facets  a #facets list of facets, each facet is a again a list of
  %       polygons (currently all polygons of a facet must be same valence)
  %     .boundary_markers a #facets list of boundary_markers
  %     .holes  a #facets list of holes, each holes entry is again a list for
  %       each facet
  %   H  #H by dim list of hole positions
  %
  % Copyright 2011, Alec Jacobson (jacobson@inf.ethz.ch)
  %
  % See also: png2objandtga, png2poly, writePOLY_tetgen
  %

  if(nargin == 4)
    filename = varargin{1};
    V = varargin{2};
    E = varargin{3};
    H = varargin{4};
  elseif(nargin == 2)
    filename = varargin{1};
    poly = varargin{2};
    [V,E,H] = poly2VEH(poly);
  else
    error('Wrong number of inputs');
  end
  % open file for writing
  poly_file_handle = fopen(filename,'w');

  % dimensions in V, should be 2: triangle format
  dim = size(V,2);
  if isempty(V)
    if isstruct(E)
      % min polygon size
      ms = min(cell2mat(cellfun(@size,E.facets,'UniformOutput',false)),[],1);
      dim = ms(2);
    else
      dim = size(E,2);
    end
    if isempty(E)
        dim = 2;
    end
  end

  if dim ~= 2
    error('writePOLY_triangle is for 2d meshes. Try writePOLY_tetgen etc.');
  end

  % vertices section
  format = '%d %.17g %.17g\n';

  fprintf(poly_file_handle,'# Part 1 - node list\n');
  fprintf(poly_file_handle,'%d %d 0 0\n', size(V,1),size(V,2));
  if ~isempty(V)
    fprintf(poly_file_handle,format,[1:size(V,1);V']);
  end
  % The following became convoluted because of ancient merge and unmerge with
  % writePOLY_tetgen.m
  if isstruct(E)
    F = E;
  else
    F = [];
    F.facets = mat2cell(E,ones(size(E,1),1),size(E,2));
    F.boundary_marker = ones(size(E,1),1);
    F.holes = cell(numel(F.facets),1);
  end
  fprintf(poly_file_handle,'# Part 2 - facet list\n');
  % for now, always include boundary markers
  % [num facets] [boundary markers]
  assert(isempty(F.facets) || iscell(F.facets));
  fprintf(poly_file_handle,'%d %d\n',numel(F.facets),1);
  % the triangle .poly format is slightly different
  fprintf(poly_file_handle,'%d %d %d %d\n', ...
    [1:numel(F.facets); [cell2mat(F.facets) F.boundary_marker]']);
  % [num holes]
  fprintf(poly_file_handle,'# Part 3 - hole list\n');
  fprintf(poly_file_handle,'%d\n',size(H,1));
  if ~isempty(H)
    assert(isempty(V) || size(H,2) == size(V,2));
    fprintf(poly_file_handle,format,[1:size(H,1);H']);
  end
  % not supported
  fprintf(poly_file_handle,'# Part 4 - region list\n');
  fprintf(poly_file_handle,'0\n');

  fprintf(poly_file_handle,'\n');
  fclose(poly_file_handle);
end


function [ state ] = nonBlocking_movePTPJointSpace( t , jPos, relVel)
%% This function is used for performing non-blocking point to point motion in joint space,
% for the KUKA iiwa 7 R 800.

%% Syntax:
% [ state ] = nonBlocking_movePTPJointSpace( t , jPos, relVel)

%% About:
% This function is used to move the robot point to point in joint space.

%% Arreguments:
% t: is the TCP/IP connection
% jPos: is the destanation position in joint space, it is 1x7 cell array,
% joint angles are in radians.
% relVel: is a double, from zero to one, specifies the over-ride relative
% velocity.

% Copy right, Mohammad SAFEEA, 9th of May 2017

% check also the function: nonBlocking_isGoalReached()

    global paramName;
    global paramVal;
    paramName='jpos';
    paramVal=jPos;

    theCommand=['jRelVel_',num2str(relVel),'_']; % set over ride.
    fprintf(t, theCommand);
    message=fgets(t);

    sendJointsPositions( t ,jPos); % send destination joint positions.

    theCommand='doPTPinJS';
    fprintf(t, theCommand); % start the point to point motion.
    message=fgets(t);
    state=checkAcknowledgment(message);

end


close all;
jobNames = {'SM+zDD+AnnealLin', 'SM+zDD', 'SM+cDD+AnnealLin', 'SM+cDD'};
jobIDs = [2134873 2134874 2134875 2134876 ];

for jj = jobIDs
    findBestAndWorstMCMCRunByLogPr(jj, 25);
end

plotLogPrVsTime( jobIDs(1:2), 1:25, jobNames(1:2) );
ylim( [-5.55 -5.34]*1e4 ); xlim( [0 35000]);
set(gcf,'Name', 'LogPr_zDD' );

plotLogPrVsTime( jobIDs(3:4), 1:25, jobNames(3:4) );
ylim( [-5.55 -5.34]*1e4 ); xlim( [0 35000]);
set(gcf,'Name', 'LogPr_cDD' );

plotLogPrVsTime( jobIDs(1:4), 1:25, jobNames(1:4) );
ylim( [-5.55 -5.34]*1e4 ); xlim( [0 35000]);
set(gcf,'Name', 'LogPr_CompareAnnealingInference' );

for jj = 1:length( jobNames )
    plotStateSeq(  jobIDs(jj), 'best' );
    set(gcf, 'Name', sprintf('StateSeq_best_%s', jobNames{jj})  );
end

plotHammDistVsTime( jobIDs(1:4), 1:25, jobNames(1:4) );

plotHammDistVsTime( jobIDs([1:2 4]), 1:25, jobNames([1:2 4]) );