function [source] = ft_dipolefitting(cfg, data)

% FT_DIPOLEFITTING perform grid search and non-linear fit with one or multiple
% dipoles and try to find the location where the dipole model is best able
% to explain the measured EEG or MEG topography.
%
% This function will initially scan the whole brain with a single dipole on
% a regular coarse grid, and subsequently start at the most optimal location
% with a non-linear search. Alternatively you can specify the initial
% location of the dipole(s) and the non-linear search will start from there.
%
% Use as
%   [source] = ft_dipolefitting(cfg, data)
%
% The configuration has the following general fields
%   cfg.numdipoles  = number, default is 1
%   cfg.symmetry    = 'x', 'y' or 'z' symmetry for two dipoles, can be empty (default = [])
%   cfg.channel     = Nx1 cell-array with selection of channels (default = 'all'),
%                     see FT_CHANNELSELECTION for details
%   cfg.gridsearch  = 'yes' or 'no', perform global search for initial
%                     guess for the dipole parameters (default = 'yes')
%   cfg.nonlinear   = 'yes' or 'no', perform nonlinear search for optimal
%                     dipole parameters (default = 'yes')
%
% If you start with a grid search, the complete grid with dipole positions and
% optionally precomputed leadfields is constructed using FT_PREPARE_SOURCEMODEL. It
% can be specified as as a regular 3-D grid that is aligned with the axes of the head
% coordinate system using
%   cfg.xgrid               = vector (e.g. -20:1:20) or 'auto' (default = 'auto')
%   cfg.ygrid               = vector (e.g. -20:1:20) or 'auto' (default = 'auto')
%   cfg.zgrid               = vector (e.g.   0:1:20) or 'auto' (default = 'auto')
%   cfg.resolution          = number (e.g. 1 cm) for automatic grid generation
% If the source model destribes a triangulated cortical sheet, it is described as
%   cfg.sourcemodel.pos     = N*3 matrix with the vertex positions of the cortical sheet
%   cfg.sourcemodel.tri     = M*3 matrix that describes the triangles connecting the vertices
% Alternatively the position of a few dipoles at locations of interest can be
% user-specified, for example obtained from an anatomical or functional MRI
%   cfg.sourcemodel.pos     = N*3 matrix with position of each source
%   cfg.sourcemodel.inside  = N*1 vector with boolean value whether grid point is inside brain (optional)
%   cfg.sourcemodel.dim     = [Nx Ny Nz] vector with dimensions in case of 3-D grid (optional)
%
% If you do not start with a grid search, you have to give a starting location
% for the nonlinear search
%   cfg.dip.pos     = initial dipole position, matrix of Ndipoles x 3
%
% The conventional approach is to fit dipoles to event-related averages, which
% within FieldTrip can be obtained from the FT_TIMELOCKANALYSIS or from
% the FT_TIMELOCKGRANDAVERAGE function. This has the additional options
%   cfg.latency     = [begin end] in seconds or 'all' (default = 'all')
%   cfg.model       = 'moving' or 'regional'
% A moving dipole model has a different position (and orientation) for each
% timepoint, or for each component. A regional dipole model has the same
% position for each timepoint or component, and a different orientation.
%
% You can also fit dipoles to the spatial topographies of an independent
% component analysis, obtained from the FT_COMPONENTANALYSIS function.
% This has the additional options
%   cfg.component   = array with numbers (can be empty -> all)
%
% You can also fit dipoles to the spatial topographies that are present
% in the data in the frequency domain, which can be obtained using the
% FT_FREQANALYSIS function. This has the additional options
%   cfg.frequency   = single number (in Hz)
%
% Low level details of the fitting can be specified in the cfg.dipfit structure
%   cfg.dipfit.display      = level of display, can be 'off', 'iter', 'notify' or 'final' (default = 'iter')
%   cfg.dipfit.optimfun     = function to use, can be 'fminsearch' or 'fminunc' (default is determined automatic)
%   cfg.dipfit.maxiter      = maximum number of function evaluations allowed (default depends on the optimfun)
%   cfg.dipfit.checkinside  = boolean, check that the dipole remains in the source compartment (default = false)
%
% Optionally, you can modify the leadfields by reducing the rank, i.e. remove the weakest orientation
%   cfg.reducerank      = 'no', or number (default = 3 for EEG, 2 for MEG)
%   cfg.normalize       = 'no', 'yes' or 'column'
%   cfg.normalizeparam  = parameter for depth normalization (default = 0.5)
%   cfg.weight          = number or 1xN vector, weight for each dipole position to compensate for the size of the corresponding patch (default = 1)
%   cfg.backproject     = 'yes' (default) or 'no', in the case of a rank reduction this parameter determines whether the result will be backprojected onto the original subspace
%
% The volume conduction model of the head should be specified as
%   cfg.headmodel     = structure with volume conduction model, see FT_PREPARE_HEADMODEL
%
% The EEG or MEG sensor positions can be present in the data or can be specified as
%   cfg.elec          = structure with electrode positions or filename, see FT_READ_SENS
%   cfg.grad          = structure with gradiometer definition or filename, see FT_READ_SENS
%
% To facilitate data-handling and distributed computing you can use
%   cfg.inputfile   =  ...
%   cfg.outputfile  =  ...
% If you specify one of these (or both) the input data will be read from a *.mat
% file on disk and/or the output data will be written to a *.mat file. These mat
% files should contain only a single variable, corresponding with the
% input/output structure.
%
% See also FT_SOURCEANALYSIS, FT_PREPARE_LEADFIELD, FT_PREPARE_HEADMODEL

% TODO change the output format, more suitable would be something like:
% dip.label
% dip.time
% dip.avg (instead of Vdata)
% dip.dip.pos
% dip.dip.mom
% dip.dip.model, or dip.dip.avg
% dip.dimord

% Undocumented local options:
%   cfg.dipfit.constr   = Source model constraints, depends on cfg.symmetry
% Optionally, you can include a noise covariance structure to sphere the data (is useful when using both
% magnetometers and gradiometers to fit your dipole)
%   cfg.dipfit.noisecov       = noise covariance matrix, see e.g. FT_TIMELOCK_ANALYSIS

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

% these are used by the ft_preamble/ft_postamble function and scripts
ft_revision = '$Id$';
ft_nargin   = nargin;
ft_nargout  = nargout;

% do the general setup of the function
ft_defaults
ft_preamble init
ft_preamble debug
ft_preamble loadvar data
ft_preamble provenance data
ft_preamble trackconfig

% the ft_abort variable is set to true or false in ft_preamble_init
if ft_abort
  return
end

% check if the input data is valid for this function
data = ft_checkdata(data, 'datatype', {'comp', 'timelock', 'freq'}, 'feedback', 'yes');

% check if the input cfg is valid for this function
cfg = ft_checkconfig(cfg, 'renamed', {'elecfile', 'elec'});
cfg = ft_checkconfig(cfg, 'renamed', {'gradfile', 'grad'});
cfg = ft_checkconfig(cfg, 'renamed', {'optofile', 'opto'});
cfg = ft_checkconfig(cfg, 'renamed', {'hdmfile', 'headmodel'});
cfg = ft_checkconfig(cfg, 'renamed', {'vol',     'headmodel'});
cfg = ft_checkconfig(cfg, 'renamed', {'grid',    'sourcemodel'});

% get the defaults
cfg.channel         = ft_getopt(cfg, 'channel', 'all');
cfg.component       = ft_getopt(cfg, 'component');        % for comp input
cfg.frequency       = ft_getopt(cfg, 'frequency');        % for freq input
cfg.latency         = ft_getopt(cfg, 'latency', 'all');   % for timeclock input
cfg.feedback        = ft_getopt(cfg, 'feedback', 'text');
cfg.gridsearch      = ft_getopt(cfg, 'gridsearch', 'yes');
cfg.nonlinear       = ft_getopt(cfg, 'nonlinear', 'yes');
cfg.symmetry        = ft_getopt(cfg, 'symmetry');
cfg.dipfit          = ft_getopt(cfg, 'dipfit', []);     % the default for this is handled below
% the following options are for on-the-fly leadfield computation
cfg.reducerank      = ft_getopt(cfg, 'reducerank', []); % the default for this is handled below
cfg.normalize       = ft_getopt(cfg, 'normalize');      % this is better not used in dipole fitting
cfg.normalizeparam  = ft_getopt(cfg, 'normalizeparam'); % this is better not used in dipole fitting
cfg.backproject     = ft_getopt(cfg, 'backproject');    % this is better not used in dipole fitting
cfg.weight          = ft_getopt(cfg, 'weight');         % this is better not used in dipole fitting

cfg = ft_checkconfig(cfg, 'renamed', {'tightgrid', 'tight'}); % this is moved to cfg.sourcemodel.tight by the subsequent createsubcfg
cfg = ft_checkconfig(cfg, 'renamed', {'sourceunits', 'unit'}); % this is moved to cfg.sourcemodel.unit by the subsequent createsubcfg

% put the low-level options pertaining to the sourcemodel in their own field
cfg = ft_checkconfig(cfg, 'createsubcfg', {'sourcemodel'});
% move some fields from cfg.sourcemodel back to the top-level configuration
cfg = ft_checkconfig(cfg, 'createtopcfg', {'sourcemodel'});

% the default for this depends on the data type
if ~isfield(cfg, 'model')
  if ~isempty(cfg.component)
    % each component is fitted independently
    cfg.model = 'moving';
  elseif ~isempty(cfg.frequency)
    % fit the data with a dipole at one location
    cfg.model = 'regional';
  elseif ~isempty(cfg.latency)
    % fit the data with a dipole at one location
    cfg.model = 'regional';
  end
end

if ~isfield(cfg, 'numdipoles')
  if isfield(cfg, 'dip')
    cfg.numdipoles = size(cfg.dip(1).pos,1);
  else
    cfg.numdipoles = 1;
  end
end

% set up the symmetry constraints
if ~isempty(cfg.symmetry)
  if cfg.numdipoles~=2
    ft_error('symmetry constraints are only supported for two-dipole models');
  elseif strcmp(cfg.symmetry, 'x')
    % this structure is passed onto the low-level ft_dipole_fit function
    cfg.dipfit.constr.reduce = [1 2 3];         % select the parameters [x1 y1 z1]
    cfg.dipfit.constr.expand = [1 2 3 1 2 3];   % repeat them as [x1 y1 z1 x1 y1 z1]
    cfg.dipfit.constr.mirror = [1 1 1 -1 1 1];  % multiply each of them with 1 or -1, resulting in [x1 y1 z1 -x1 y1 z1]
  elseif strcmp(cfg.symmetry, 'y')
    % this structure is passed onto the low-level ft_dipole_fit function
    cfg.dipfit.constr.reduce = [1 2 3];         % select the parameters [x1 y1 z1]
    cfg.dipfit.constr.expand = [1 2 3 1 2 3];   % repeat them as [x1 y1 z1 x1 y1 z1]
    cfg.dipfit.constr.mirror = [1 1 1 1 -1 1];  % multiply each of them with 1 or -1, resulting in [x1 y1 z1 x1 -y1 z1]
  elseif strcmp(cfg.symmetry, 'z')
    % this structure is passed onto the low-level ft_dipole_fit function
    cfg.dipfit.constr.reduce = [1 2 3];         % select the parameters [x1 y1 z1]
    cfg.dipfit.constr.expand = [1 2 3 1 2 3];   % repeat them as [x1 y1 z1 x1 y1 z1]
    cfg.dipfit.constr.mirror = [1 1 1 1 1 -1];  % multiply each of them with 1 or -1, resulting in [x1 y1 z1 x1 y1 -z1]
  else
    ft_error('unrecognized symmetry constraint');
  end
elseif ~isfield(cfg, 'dipfit') || ~isfield(cfg.dipfit, 'constr')
  % no symmetry constraints have been specified
  cfg.dipfit.constr = [];
end

if ft_getopt(cfg.dipfit.constr, 'sequential', false) && strcmp(cfg.model, 'moving')
  ft_error('the moving dipole model does not combine with the sequential constraint')
  % see http://bugzilla.fieldtriptoolbox.org/show_bug.cgi?id=3119
end

if isfield(data, 'topolabel')
  % this looks like a component analysis
  iscomp = 1;
  % transform the data into a representation on which the timelocked dipole fit can perform its trick
  data = comp2timelock(cfg, data);
else
  iscomp = 0;
end

if isfield(data, 'freq')
  % this looks like a frequency analysis
  isfreq = 1;
  % transform the data into a representation on which the timelocked dipole fit can perform its trick
  data = freq2timelock(cfg, data);
else
  isfreq = 0;
end

% prepare the volume conduction model and the sensor array
% this updates the configuration with the appropriate fields
[headmodel, sens, cfg] = prepare_headmodel(cfg, data);

% set the default for reducing the rank of the leadfields
if isempty(cfg.reducerank)
  if ft_senstype(sens, 'eeg')
    cfg.reducerank = 'no';    % for EEG
  elseif ft_senstype(sens, 'meg') && ft_headmodeltype(headmodel, 'infinite')
    cfg.reducerank = 'no';    % for MEG with a magnetic dipole, e.g. a HPI coil
  elseif ft_senstype(sens, 'meg')
    cfg.reducerank = 'yes';   % for MEG with a current dipole in a volume conductor
  end
end

% construct the options for the leadfield computation, the same options are also passed to DIPOLE_FIT
leadfieldopt = {};
leadfieldopt = ft_setopt(leadfieldopt, 'reducerank',     cfg.reducerank);
leadfieldopt = ft_setopt(leadfieldopt, 'normalize',      cfg.normalize);
leadfieldopt = ft_setopt(leadfieldopt, 'normalizeparam', cfg.normalizeparam);
leadfieldopt = ft_setopt(leadfieldopt, 'weight',         cfg.weight);
leadfieldopt = ft_setopt(leadfieldopt, 'backproject',    cfg.backproject);

% select the desired channels, ordered according to the sensor structure
[selsens, seldata] = match_str(sens.label, data.label);
% take the selected channels from the data structure
Vdata = data.avg(seldata, :);

% sphere the date using the noise covariance matrix supplied, if any
% this affects both the gridsearch and the nonlinear optimization
noisecov = ft_getopt(cfg.dipfit, 'noisecov');
if ~isempty(noisecov)
  [u, s] = svd(noisecov);
  tol = max(size(noisecov)) * eps(norm(s, inf));
  s = diag(s);
  r1 = sum(s > tol) + 1;
  s(1:(r1 - 1)) = 1 ./ sqrt(s(1:(r1 - 1)));
  s(r1:end)     = 0;
  sphere = diag(s) * u';
  % apply the sphering to the data
  Vdata = sphere * Vdata;
  % apply the sphering as a pre-multiplication to the sensor definition
  montage = [];
  montage.labelold = cfg.channel;
  montage.labelnew = cfg.channel;
  montage.tra = sphere;
  sens = ft_apply_montage(sens, montage, 'balancename', 'sphering');
end

if iscomp
  % select the desired component topographies
  Vdata = Vdata(:, cfg.component);
elseif isfreq
  % the desired frequencies have already been selected
  Vdata = Vdata(:, :);
else
  % select the desired latencies
  if ischar(cfg.latency) && strcmp(cfg.latency, 'all')
    cfg.latency = data.time([1 end]);
  end
  tbeg = nearest(data.time, cfg.latency(1));
  tend = nearest(data.time, cfg.latency(end));
  cfg.latency = [data.time(tbeg) data.time(tend)];
  Vdata = Vdata(:, tbeg:tend);
end

nchans = size(Vdata,1);
ntime  = size(Vdata,2);
Vmodel = zeros(nchans, ntime);
fprintf('selected %d channels\n', nchans);
fprintf('selected %d topographies\n', ntime);

if nchans<cfg.numdipoles*3
  ft_warning('not enough channels to perform a dipole fit');
end

if ntime<1
  ft_error('no spatial topography selected');
end

% check whether EEG is average referenced
if ft_senstype(sens, 'eeg')
  if any(rv(Vdata, avgref(Vdata))>0.001)
    ft_warning('the EEG data is not average referenced, correcting this');
  end
  Vdata = avgref(Vdata);
end

% set to zeros if no initial dipole was specified
if ~isfield(cfg, 'dip')
  cfg.dip.pos = zeros(cfg.numdipoles, 3);
  cfg.dip.mom = zeros(3*cfg.numdipoles, 1);
end

% set to zeros if no initial dipole position was specified
if ~isfield(cfg.dip, 'pos')
  cfg.dip.pos = zeros(cfg.numdipoles, 3);
end

% set to zeros if no initial dipole moment was specified
if ~isfield(cfg.dip, 'mom')
  cfg.dip.mom = zeros(3*cfg.numdipoles, 1);
end

% check the specified dipole model
if numel(cfg.dip.pos)~=cfg.numdipoles*3 || numel(cfg.dip.mom)~=cfg.numdipoles*3
  ft_error('inconsistent number of dipoles in configuration')
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% perform the dipole scan, this is usefull for generating an initial guess
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if strcmp(cfg.gridsearch, 'yes')
  % test whether we have a valid configuration for dipole scanning
  if cfg.numdipoles==1
    % this is ok
  elseif cfg.numdipoles==2 && ~isempty(cfg.dipfit.constr)
    % this is also ok
  elseif isfield(cfg.sourcemodel, 'pos') && size(cfg.sourcemodel.pos,2)==cfg.numdipoles*3
    % this is also ok
  else
    ft_error('dipole scanning is only possible for a single dipole or a symmetric dipole pair');
  end

  % copy all options that are potentially used in ft_prepare_sourcemodel
  tmpcfg           = keepfields(cfg, {'sourcemodel', 'mri', 'headshape', 'symmetry', 'smooth', 'threshold', 'spheremesh', 'inwardshift', 'xgrid' 'ygrid', 'zgrid', 'resolution', 'tight', 'warpmni', 'template', 'showcallinfo'});
  tmpcfg.headmodel = headmodel;
  if ft_senstype(sens, 'eeg')
    tmpcfg.elec = sens;
  elseif ft_senstype(sens, 'meg')
    tmpcfg.grad = sens;
  end
  % construct the dipole grid on which the gridsearch will be done
  sourcemodel = ft_prepare_sourcemodel(tmpcfg);

  ngrid = size(sourcemodel.pos,1);

  switch cfg.model
    case 'regional'
      sourcemodel.error = nan(ngrid, 1);
    case 'moving'
      sourcemodel.error = nan(ngrid, ntime);
    otherwise
      ft_error('unsupported cfg.model');
  end

  insideindx = find(sourcemodel.inside);
  ft_progress('init', cfg.feedback, 'scanning grid');
  for i=1:length(insideindx)
    ft_progress(i/length(insideindx), 'scanning grid location %d/%d\n', i, length(insideindx));
    thisindx = insideindx(i);
    if isfield(sourcemodel, 'leadfield')
      % reuse the previously computed leadfield
      lf = sourcemodel.leadfield{thisindx};
    else
      lf = ft_compute_leadfield(sourcemodel.pos(thisindx,:), sens, headmodel, leadfieldopt{:});
    end
    % the model is V=lf*mom+noise, therefore mom=pinv(lf)*V estimates the
    % dipole moment this makes the model potential U=lf*pinv(lf)*V and the
    % model error is norm(V-U) = norm(V-lf*pinv(lf)*V) = norm((eye-lf*pinv(lf))*V)
    if any(isnan(lf(:)))
      % this might happen if one of the dipole locations of the grid is
      % outside the brain compartment
      lf(:) = 0;
    end
    switch cfg.model
      case 'regional'
        % sum the error over all latencies
        sourcemodel.error(thisindx,1) = sum(sum(((eye(nchans)-lf*pinv(lf))*Vdata).^2));
      case 'moving'
        % remember the error for each latency independently
        sourcemodel.error(thisindx,:) = sum(((eye(nchans)-lf*pinv(lf))*Vdata).^2);
      otherwise
        ft_error('unsupported cfg.model');
    end % switch model
  end % looping over the grid
  ft_progress('close');

  switch cfg.model
    case 'regional'
      % find the source position with the minimum error
      [err, indx] = min(sourcemodel.error);
      dip.pos = sourcemodel.pos(indx,:);                % note that for a symmetric dipole pair this results in a vector
      dip.pos = reshape(dip.pos,3,cfg.numdipoles)';     % convert to a Nx3 array
      dip.mom = zeros(cfg.numdipoles*3,1);              % set the dipole moment to zero
      if cfg.numdipoles==1
        fprintf('found minimum after scanning on grid point [%g %g %g]\n', dip.pos(1), dip.pos(2), dip.pos(3));
      elseif cfg.numdipoles==2
        fprintf('found minimum after scanning on grid point [%g %g %g; %g %g %g]\n', dip.pos(1), dip.pos(2), dip.pos(3), dip.pos(4), dip.pos(5), dip.pos(6));
      end

    case 'moving'
      for t=1:ntime
        % find the source position with the minimum error
        [err, indx] = min(sourcemodel.error(:,t));
        dip(t).pos = sourcemodel.pos(indx,:);                        % note that for a symmetric dipole pair this results in a vector
        dip(t).pos = reshape(dip(t).pos,3,cfg.numdipoles)';   % convert to a Nx3 array
        dip(t).mom = zeros(cfg.numdipoles*3,1);               % set the dipole moment to zero
        if cfg.numdipoles==1
          fprintf('found minimum after scanning for topography %d on grid point [%g %g %g]\n', t, dip(t).pos(1), dip(t).pos(2), dip(t).pos(3));
        elseif cfg.numdipoles==2
          fprintf('found minimum after scanning for topography %d on grid point [%g %g %g; %g %g %g]\n', t, dip(t).pos(1), dip(t).pos(2), dip(t).pos(3), dip(t).pos(4), dip(t).pos(5), dip(t).pos(6));
        end
      end

    otherwise
      ft_error('unsupported cfg.model');
  end % switch model

elseif strcmp(cfg.gridsearch, 'no')
  % use the initial guess supplied in the configuration for the remainder
  switch cfg.model
    case 'regional'
      dip = struct(cfg.dip);      % ensure that it is a struct, not a config object
    case 'moving'
      for t=1:ntime
        dip(t) = struct(cfg.dip); % ensure that it is a struct, not a config object
      end
    otherwise
      ft_error('unsupported cfg.model');
  end % switch model

end % if gridsearch yes/no
% multiple dipoles can be represented either as a 1x(N*3) vector or as a Nx3 matrix,
% i.e. [x1 y1 z1 x2 y2 z2] or [x1 y1 z1; x2 y2 z2]
switch cfg.model
  case 'regional'
    dip = fixdipole(dip);
  case 'moving'
    for t=1:ntime
      dip(t) = fixdipole(dip(t));
    end
  otherwise
    ft_error('unsupported cfg.model');
end % switch model

% convert the structure with the additional low-level options into key-value pairs
dipfitopt = ft_cfg2keyval(cfg.dipfit);

% add the options for the leadfield computation
dipfitopt = ft_setopt(dipfitopt, 'reducerank',     cfg.reducerank);
dipfitopt = ft_setopt(dipfitopt, 'normalize',      cfg.normalize);
dipfitopt = ft_setopt(dipfitopt, 'normalizeparam', cfg.normalizeparam);
dipfitopt = ft_setopt(dipfitopt, 'weight',         cfg.weight);
dipfitopt = ft_setopt(dipfitopt, 'backproject',    cfg.backproject);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% perform the non-linear fit
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if strcmp(cfg.nonlinear, 'yes')
  switch cfg.model
    case 'regional'
      % perform the non-linear dipole fit for all latencies together
      % catch errors due to non-convergence
      try
        dip = dipole_fit(dip, sens, headmodel, Vdata, dipfitopt{:});
        success = 1;
        if cfg.numdipoles==1
          fprintf('found minimum after non-linear optimization on [%g %g %g]\n', dip.pos(1), dip.pos(2), dip.pos(3));
        elseif cfg.numdipoles==2
          fprintf('found minimum after non-linear optimization on [%g %g %g; %g %g %g]\n', dip.pos(1,1), dip.pos(1,2), dip.pos(1,3), dip.pos(2,1), dip.pos(2,2), dip.pos(2,3));
        end
      catch
        success = 0;
        disp(lasterr);
      end

    case 'moving'
      % perform the non-linear dipole fit for each latency independently
      % instead of using dip(t) = dipole_fit(dip(t),...), I am using temporary variables dipin and dipout
      % to prevent errors like "Subscripted assignment between dissimilar structures"
      dipin = dip;
      for t=1:ntime
        % catch errors due to non-convergence
        try
          dipout(t) = dipole_fit(dipin(t), sens, headmodel, Vdata(:,t), dipfitopt{:});
          success(t) = 1;
          if cfg.numdipoles==1
            fprintf('found minimum after non-linear optimization for topography %d on [%g %g %g]\n', t, dipout(t).pos(1), dipout(t).pos(2), dipout(t).pos(3));
          elseif cfg.numdipoles==2
            fprintf('found minimum after non-linear optimization for topography %d on [%g %g %g; %g %g %g]\n', t, dipout(t).pos(1,1), dipout(t).pos(1,2), dipout(t).pos(1,3), dipout(t).pos(2,1), dipout(t).pos(2,2), dipout(t).pos(2,3));
          end
        catch
          % keep the position and moment according to the initial guess
          dipout(t).pos = dipin(t).pos;
          dipout(t).mom = dipin(t).mom;
          success(t) = 0;
          disp(lasterr);
        end
      end
      dip = dipout;
      clear dipin dipout
    otherwise
      ft_error('unsupported cfg.model');
  end % switch model
end % if nonlinear

if strcmp(cfg.nonlinear, 'no')
  % the optimal dipole positions are either obtained from scanning
  % or from the initial configured specified by the user
  switch cfg.model
    case 'regional'
      success = 1;
    case 'moving'
      success = ones(1,ntime);
    otherwise
      ft_error('unsupported cfg.model');

  end % switch model
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% compute the model potential distribution and the residual variance
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
switch cfg.model
  case 'regional'
    if success
      % re-compute the leadfield in order to compute the model potential and dipole moment
      lf = ft_compute_leadfield(dip.pos, sens, headmodel, leadfieldopt{:});
      if isfield(dip, 'mom') && isfield(dip, 'ampl')
        % the orientation and amplitude have already been estimated, this applies to the case of a fixed dipole orientation
        dip.pot = (lf * dip.mom) * dip.ampl;
      else
        % compute all details of the final dipole model using linear estimation
        dip.mom = pinv(lf)*Vdata;
        dip.pot = lf*dip.mom;
      end
      dip.rv  = rv(Vdata, dip.pot);
      Vmodel  = dip.pot;
    end
  case 'moving'
    for t=1:ntime
      if success(t)
        % re-compute the leadfield in order to compute the model potential and dipole moment
        lf = ft_compute_leadfield(dip(t).pos, sens, headmodel, leadfieldopt{:});
        % compute all details of the final dipole model
        dip(t).mom = pinv(lf)*Vdata(:,t);
        dip(t).pot = lf*dip(t).mom;
        dip(t).rv  = rv(Vdata(:,t), dip(t).pot);
        Vmodel(:,t) = dip(t).pot;
      end
    end
  otherwise
    ft_error('unsupported cfg.model');
end % switch model

switch cfg.model
  case 'regional'
    if isfreq
      % the matrix with the dipole moment is encrypted and cannot be interpreted straight away
      % reconstruct the frequency representation of the data at the source level
      if isfield(dip, 'mom') && isfield(dip, 'ampl')
        % this applies to the case of a fixed dipole orientation
        [dip.pow, dip.csd, dip.fourier] = timelock2freq(dip.mom * dip.ampl);
      else
        [dip.pow, dip.csd, dip.fourier] = timelock2freq(dip.mom);
      end
    end
  case 'moving'
    if isfreq
      % although this is technically possible so far, it does not make any sense
      ft_warning('a moving dipole model in the frequency domain is not supported');
    end
  otherwise
    ft_error('unsupported cfg.model');
end % switch model

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% collect the results
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
source.label  = cfg.channel; % these channels were used in fitting
source.dip    = dip;
source.Vdata  = Vdata;  % FIXME this should be renamed (if possible w.r.t. EEGLAB)
source.Vmodel = Vmodel; % FIXME this should be renamed (if possible w.r.t. EEGLAB)

% the units of the fitted source are the same as the units of the headmodel and the sensor array
for i=1:length(source.dip)
  source.dip(i).unit = headmodel.unit;
end

% assign a latency, frequeny or component axis to the output
if iscomp
  source.component = cfg.component;
  % FIXME assign Vdata to an output variable, idem for the model potential
elseif isfreq
  source.freq   = cfg.frequency;
  source.dimord = 'chan_freq';
  % FIXME assign Vdata to an output variable, idem for the model potential
else
  tbeg = nearest(data.time, cfg.latency(1));
  tend = nearest(data.time, cfg.latency(end));
  source.time   = data.time(tbeg:tend);
  source.dimord = 'chan_time';
end

% do the general cleanup and bookkeeping at the end of the function
ft_postamble debug
ft_postamble trackconfig
ft_postamble previous   data
ft_postamble provenance source
ft_postamble history    source
ft_postamble savevar    source


%train
%dataset_dir = '/home/gyeongsikmoon/workspace/Data/Hand_pose_estimation/NYU/dataset/train/';
%save_dir = '/home/gyeongsikmoon/workspace/Data/Hand_pose_estimation/NYU/dataset/train/parsed/';
%tot_frame_num = 72757;

%test
dataset_dir = '/home/gyeongsikmoon/workspace/Data/Hand_pose_estimation/NYU/dataset/test/';
save_dir = '/home/gyeongsikmoon/workspace/Data/Hand_pose_estimation/NYU/dataset/test/parsed/';
tot_frame_num = 8252;

kinect_index = 1;

for image_index = 1:tot_frame_num
    filename_prefix = sprintf('%d_%07d', kinect_index, image_index);

    if exist([dataset_dir, 'depth_', filename_prefix, '.png'], 'file')

        %% Load and display a depth example
        % The top 8 bits of depth are packed into green and the lower 8 bits into blue.
        depth = imread([dataset_dir, 'depth_', filename_prefix, '.png']);
        depth = uint16(depth(:,:,3)) + bitsll(uint16(depth(:,:,2)), 8);

        fp_save = fopen([save_dir, 'depth_', filename_prefix, '.bin'],'w');
        fwrite(fp_save,permute(depth,[2,1,3]),'float');
        fclose(fp_save);

        %delete(strcat(folderpath,img_name));
    end

end


function [image] = LoadImage(directory, timestamp, LUT)

% LoadImage - load a rectified image from disk
%
% [image] = LoadImage(directory, timestamp, LUT)
%
% eg.
% timestamps = dlmread('<dataset_root>/stereo.timestamps');
% [ ~, ~, ~, ~, ~, LUT] = ...
%     ReadCameraModel('<models_dir>/stereo_wide_left_undistortion.bin');
% image = LoadImage('<dataset_root>/stereo/left', timestamps(100,1), LUT);
%
% INPUTS:
%   directory: directory containing images named <timestamp>.png
%   timestamp: timestamp of image to load
%   LUT (optional): lookup table for image rectification, as returned from
%     ReadCameraModel. If not supplied, original distorted image will be
%     returned.
%     See ReadCameraModel and UndistortImage
%
% OUTPUTS:
%   image: image at the given timestamp. If an undistortion lookup table is
%     supplied, image will be undistorted.
%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Copyright (c) 2016 University of Oxford
% Authors:
%  Geoff Pascoe (gmp@robots.ox.ac.uk)
%  Will Maddern (wm@robots.ox.ac.uk)
%
% This work is licensed under the Creative Commons
% Attribution-NonCommercial-ShareAlike 4.0 International License.
% To view a copy of this license, visit
% http://creativecommons.org/licenses/by-nc-sa/4.0/ or send a letter to
% Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

  if directory(end) ~= '/'
    directory = [directory '/'];
  end

  path = [directory num2str(timestamp) '.png'];
  if ~exist(path, 'file')
    image = false;
    return;
  end

  if regexp(directory, 'stereo')
    bayer_pattern = 'gbrg';
  else
    bayer_pattern = 'rggb';
  end

  image = demosaic(imread(path), bayer_pattern);

  if exist('LUT', 'var')
    image = UndistortImage(image, LUT);
  end

end


% Run setupThermoModel for the E. coli metabolic reconstruction iAF1260

% Requires an installation of:
% OpenBabel

%% Add required fields and directories to path
initVonBertalanffy

%todo, clean up this so it is not needed
global CBTDIR
pth=which('initCobraToolbox.m');
CBTDIR = pth(1:end-(length('initCobraToolbox.m')+1));

cd([CBTDIR filesep 'test' filesep 'testVonBertalanffy'])

%% Configure inputs
load iAF1260
if model.S(952,350)==0
    model.S(952,350)=1; % One reaction needing mass balancing in iAF1260
end
model.metCharges(strcmp('asntrna[c]',model.mets))=0; % One reaction needing charge balancing

molfileDir = 'iAF1260Molfiles'; % Directory containing molfiles

cid = []; % KEGG Compound identifiers. Not required since molfile directory is specified.

T = 310.15; % Temperature in Kelvin
cellCompartments = ['c'; 'e'; 'p']; % Cell compartment identifiers
ph = [7.7; 7.7; 7.7]; % Compartment specific pH
is = [0.25; 0.25; 0.25]; % Compartment specific ionic strength in mol/L
chi = [0; 90; 90]; % Compartment specific electrical potential relative to cytosol in mV

xmin = 1e-5*ones(size(model.mets)); % Lower bounds on metabolite concentrations in mol/L
xmax = 0.02*ones(size(model.mets)); % Upper bounds on metabolite concentrations in mol/L

confidenceLevel = 0.95; % Confidence level for estimated standard transformed reaction Gibbs energies. Used to quantitatively assign reaction directionality.

%% Call setupThermoModel
modelT = setupThermoModel(model,molfileDir,cid,T,cellCompartments,ph,is,chi,xmin,xmax,confidenceLevel);

save('iAF1260Thermo_test.mat', 'modelT', '-v7');

%% Compare test results to expected results
clear all;

old = load('iAF1260Thermo.mat');
new = load('iAF1260Thermo_test.mat');

% Check for differences in estimated standard transformed Gibbs energies of formation
fig = figure(1);
subplot(1,3,1);
rmse1 = sqrt(mean( (new.modelT.DfGt0 - old.modelT.DfGt0).^2 ));
fprintf('RMSE difference between the old and new DfGt0: %g\n', rmse1);
cdfplot(abs((new.modelT.DfGt0 - old.modelT.DfGt0)));
xlabel('|D_f G^{\prime\circ}(new) - D_f G^{\prime\circ}(old)|');
title(['\Delta_f G^{\prime\circ} RMSE = ' sprintf('%g', rmse1)]);

% Check for differences in estimated standard transformed reaction Gibbs energies
subplot(1,3,2);
rmse2 = sqrt(mean( (new.modelT.DrGt0 - old.modelT.DrGt0).^2 ));
fprintf('RMSE difference between the old and new DrGt0: %g\n', rmse2);
cdfplot(abs((new.modelT.DrGt0 - old.modelT.DrGt0)));
xlabel('|D_r G^{\prime\circ}(new) - D_r G^{\prime\circ}(old)|');
title(['\Delta_r G^{\prime\circ} RMSE = ' sprintf('%g', rmse2)]);

% Check for differences in uncertainty levels - indicative of differences in coverage
subplot(1,3,3);
rmse3 = sqrt(mean( (new.modelT.uf - old.modelT.uf).^2 ));
fprintf('RMSE difference between the old and new uf: %g\n', rmse3);
cdfplot(abs((new.modelT.uf - old.modelT.uf)));
xlabel('|U_f (new) - U_f (old)|');
title(['U_f RMSE = ' sprintf('%g', rmse3)]);

print(fig, 'iAF1260_compare.eps', '-deps');


function kern = componentKernReadParamsFromFID(kern, FID, version)

% COMPONENTKERNREADPARAMSFROMFID Read a component based kernel from a C++ file.
% FORMAT
% DESC reads the components fo a kernel from a file written by C++ code.
% ARG kern : the base kernel to add components to.
% ARG FID : the input file stream to add from.
% RETURN kern : the kernel with the components added in.
%
% SEEALSO : modelReadFromFID, kernReadFromFID
%
% COPYRIGHT : Neil D. Lawrence, 2005, 2006, 2008

% KERN

kern.inputDimension = readIntFromFID(FID, 'inputDim');
numParams = readIntFromFID(FID, 'numParams');
numKerns = readIntFromFID(FID, 'numKerns');

for i=1:numKerns
  if version > 0.11
    kern.comp{i} = modelReadFromFID(FID);
  else
    kern.comp{i} = kernReadFromFID(FID, version);
  end
end

for i = 1:length(kern.comp)
  kern.nParams = kern.nParams + kern.comp{i}.nParams;
  kern.comp{i}.index = [];
end
kern.paramGroups = speye(kern.nParams);

if strcmp(kern.type, 'cmpnd')
  % Summarise the total white variance in the field whiteVariance.
  kern.whiteVariance = 0;
  for i = 1:length(kern.comp)
    if strcmp(kern.comp{i}.type, 'white')
      kern.whiteVariance = kern.whiteVariance + kern.comp{i}.variance;
    else
      if(isfield(kern.comp{i}, 'whiteVariance'))
        kern.whiteVariance = kern.whiteVariance + ...
            kern.comp{i}.whiteVariance;
      end
    end
  end
end


% IO utility functions to
% - save data to files.
% - read, edit mat file timestamps
%
% Binary Format for matrices and cell arrays (with matrix and cell array
% elements) inspired by Arvid Bottiger's write_matrix_bin,
% 1. 1*uint32               Indicator of cell array
% 2. 1*uint32               Dimensions of array
% 3. dimensions*uint32      Lengths of dimensions
% 4. a. Cell Array => recurse
%    b. Matrix
%       1*uint32            Indicator of character array
%       uint32 or float32   Dat%
%
%
%References:
%===============
%- write_matrix_bin by Arvid Bottiger
%   http://www.mathworks.com/matlabcentral/fileexchange/24483
%
% Author: Jonathan Karr
% Affilitation: Covert Lab, Department of Bioengineering, Stanford University
% Last updated: 7/14/2009
classdef IOUtil
    properties (Constant = true)
        memmapfileHeaderSize = 1024
        memmapfileDataField = 'data'
    end

    methods (Static)
        %Writes data to disk in binary format
        function writeBinary(filename, data)
            import edu.stanford.covert.util.IOUtil;

            %open file
            fid = fopen(filename, 'w');

            %write data
            IOUtil.writeDataBinary(fid, data);

            %close file
            fclose(fid);
        end

        %Reads data from disk in binary format
        function data = readBinary(filename)
            import edu.stanford.covert.util.IOUtil;

            %open file
            fid = fopen(filename, 'r');

            %read data
            data = IOUtil.readDataBinary(fid);

            %close file
            fclose(fid);
        end
    end

    methods (Static, Access = protected)
        %Writes data to disk in binary format
        function writeDataBinary(fid, data)
            import edu.stanford.covert.util.IOUtil;

            %cell array, number of dimensions, size
            dataIsCell = iscell(data);
            dataSize = size(data);
            dimensions = length(dataSize);
            fwrite(fid, [dataIsCell dimensions dataSize], 'uint32');

            %reshape data
            data = reshape(data,1,[]);

            %write data
            if dataIsCell
                for i = 1:length(data)
                    IOUtil.writeDataBinary(fid, data{i});
                end
            else
                switch class(data)
                    case 'char',    dataType = 1;
                    case 'uint8',   dataType = 2;
                    case 'uint16',  dataType = 3;
                    case 'uint32',  dataType = 4;
                    case 'uint64',  dataType = 5;
                    case 'int8',    dataType = 6;
                    case 'int16',   dataType = 7;
                    case 'int32',   dataType = 8;
                    case 'int64',   dataType = 9;
                    case 'single',  dataType = 10;
                    case 'double',  dataType = 11;
                    case 'logical', dataType = 12;
                    otherwise
                        throw(MException('IOUtil:unsupportedDatatype', 'Data type ''%s'' not supported', class(data)));
                end

                %character, write no. dimensions, size
                fwrite(fid,  dataType, 'uint32');

                %write data
                switch dataType
                    case 1,  fwrite(fid, data+0, 'uint16');
                    case 2,  fwrite(fid, data, 'uint16');  %Note not using uint8
                    case 3,  fwrite(fid, data, 'uint16');
                    case 4,  fwrite(fid, data, 'uint32');
                    case 5,  fwrite(fid, data, 'float64'); %Note not using uint64
                    case 6,  fwrite(fid, data, 'int16');   %Note not using int8
                    case 7,  fwrite(fid, data, 'int16');
                    case 8,  fwrite(fid, data, 'int32');
                    case 9,  fwrite(fid, data, 'float64'); %Note not using int64
                    case 10, fwrite(fid, data, 'float32');
                    case 11, fwrite(fid, data, 'float64');
                    case 12, fwrite(fid, data, 'uint16');  %Note not using ubit1
                    otherwise
                        throw(MException('IOUtil:unsupportedDatatype', 'Data type ''%s'' not supported', dataType));
                end
            end
        end

        %Reads data from disk in binary format
        function data = readDataBinary(fid)
            import edu.stanford.covert.util.IOUtil;

            %cell array, number of dimensions, size
            dataIsCell = fread(fid, 1, 'uint32');
            dimensions = fread(fid, 1, 'uint32');
            dataSize = fread(fid, dimensions, 'uint32')';
            count = prod(dataSize);

            %read data
            if dataIsCell
                data = cell(count, 1);
                for i = 1:count
                    data{i} = IOUtil.readDataBinary(fid);
                end
            else
                dataType = fread(fid, 1, 'uint32');
                switch dataType
                    case 1,  data = char(fread(fid, count, 'uint16')); %#ok<FREAD>
                    case 2,  data = uint8(fread(fid, count, 'uint16'));
                    case 3,  data = uint16(fread(fid, count, 'uint16'));
                    case 4,  data = uint32(fread(fid, count, 'uint32'));
                    case 5,  data = uint64(fread(fid, count, 'uint64'));
                    case 6,  data = int8(fread(fid, count, 'int16'));
                    case 7,  data = int16(fread(fid, count, 'int16'));
                    case 8,  data = int32(fread(fid, count, 'int32'));
                    case 9,  data = int64(fread(fid, count, 'int64'));
                    case 10, data = single(fread(fid, count, 'float32'));
                    case 11, data = double(fread(fid, count, 'float64'));
                    case 12, data = logical(fread(fid, count, 'uint16'));
                    otherwise
                        throw(MException('IOUtil:unsupportedDatatype', 'Data type ''%d'' not supported', dataType));
                end
            end

            if dimensions > 1
                data = reshape(data, dataSize);
            end
        end
    end

    methods (Static)
        function writeMemmapFile(data, fileName)
            import edu.stanford.covert.util.IOUtil;

            %write header
            format = {class(data) size(data) IOUtil.memmapfileDataField};
            IOUtil.writeBinary(fileName, format);

            %write spacing between header and data, and data
            fileInfo = dir(fileName);
            if fileInfo.bytes > IOUtil.memmapfileHeaderSize
                throw(MException('IOUtil:invalidHeader', 'Header size cannot be greater than %d bytes', IOUtil.memmapfileHeaderSize));
            end

            fid = fopen(fileName, 'a');
            fwrite(fid, zeros(IOUtil.memmapfileHeaderSize - fileInfo.bytes, 1, 'uint8'), 'uint8'); %spacing
            fwrite(fid, data, class(data)); %data
            fclose(fid);
        end

        function result = readMemmapFile(fileName)
            import edu.stanford.covert.util.IOUtil;

            format = IOUtil.readBinary(fileName);
            if all(format{2})
                result = memmapfile(fileName, ...
                    'Offset', IOUtil.memmapfileHeaderSize, ...
                    'Format', format, ...
                    'Repeat', 1, ...
                    'Writable', false);
            else
                result = struct('Data', struct(format{3}, []));
                result.Data.(format{3}) = zeros(format{2}, format{1});
            end
        end
    end

    methods (Static)
        function directories = getDirectoryNamesRecursively(directory)
            import edu.stanford.covert.util.IOUtil;
            if directory(end) == '/' || directory(end) == '\'
                directory = directory(1:end-1);
            end
            directories = {directory};
            files = dir(directory);
			files = files([files.isdir]);
            for i = 1:numel(files)
                if files(i).name(1) ~= '.'
                    directories = [directories;
                        IOUtil.getDirectoryNamesRecursively([directory filesep files(i).name])]; %#ok<AGROW>
                end
            end
        end
    end
end


% ------------------------------------------------------------------------
%  Copyright (C)
%  Universitat Politecnica de Catalunya BarcelonaTech (UPC) - Spain
%  University of California Berkeley (UCB) - USA
%
%  Jordi Pont-Tuset <jordi.pont@upc.edu>
%  Pablo Arbelaez <arbelaez@berkeley.edu>
%  June 2014
% ------------------------------------------------------------------------
% This file is part of the MCG package presented in:
%    Arbelaez P, Pont-Tuset J, Barron J, Marques F, Malik J,
%    "Multiscale Combinatorial Grouping,"
%    Computer Vision and Pattern Recognition (CVPR) 2014.
% Please consider citing the paper if you use this code.
% ------------------------------------------------------------------------
%
% This function builds all the MEX files needed.
% Dependencies needed to build: Boost C++ libraries (http://www.boost.org)
%
% ------------------------------------------------------------------------
function build()
% Check that 'root_dir' has been set
if ~exist(root_dir,'dir')
    error('Error building MCG, try updating the value of root_dir in the file "root_dir.m"')
end

%% Include the generic paths and files to compile
include{1} = fullfile(root_dir, 'src', 'aux');  % To get matlab_multiarray.hpp
if (strcmp(computer(),'PCWIN64') || strcmp(computer(),'PCWIN32'))
    include{2} = 'C:\Program Files\boost_1_55_0';  % Boost libraries (change it if necessary)
else
    include{2} = '/opt/local/include/';  % Boost libraries (change it if necessary)
end
include{3} = fullfile(root_dir, 'src', 'external','piotr_toolbox'); % To build Piotr toolbox

include_str = '';
for ii=1:length(include)
    include_str = [include_str ' -I''' include{ii} '''']; %#ok<AGROW>
end

build_file{1}     = fullfile(root_dir, 'src', 'cands'    ,'mex_assess_one_sel.cpp');
build_file{end+1} = fullfile(root_dir, 'src', 'cands'    ,'mex_base_perimeters.cpp');
build_file{end+1} = fullfile(root_dir, 'src', 'cands'    ,'mex_fast_features.cpp');
build_file{end+1} = fullfile(root_dir, 'src', 'cands'    ,'mex_fast_intersections.cpp');
build_file{end+1} = fullfile(root_dir, 'src', 'cands'    ,'mex_fast_reduction.cpp');
build_file{end+1} = fullfile(root_dir, 'src', 'cands'    ,'mex_get_tree_cands.cpp');
build_file{end+1} = fullfile(root_dir, 'src', 'cands'    ,'mex_prune_tree_to_regions.cpp');
build_file{end+1} = fullfile(root_dir, 'src', 'cands'    ,'mex_max_margin.cpp');
build_file{end+1} = fullfile(root_dir, 'src', 'cands'    ,'mex_hole_filling.cpp');
build_file{end+1} = fullfile(root_dir, 'src', 'aux'      ,'mex_intersect_hierarchies.cpp');
build_file{end+1} = fullfile(root_dir, 'src', 'aux'      ,'mex_ucm2hier.cpp');
build_file{end+1} = fullfile(root_dir, 'src', 'aux'      ,'mex_cands2masks.cpp');
build_file{end+1} = fullfile(root_dir, 'src', 'aux'      ,'mex_cands2labels.cpp');
build_file{end+1} = fullfile(root_dir, 'src', 'benchmark','mex_eval_masks.cpp');
build_file{end+1} = fullfile(root_dir, 'src', 'benchmark','mex_eval_labels.cpp');
build_file{end+1} = fullfile(root_dir, 'src', 'external' ,'paretofront','paretofront.cpp');

%% Build everything
if ~exist(fullfile(root_dir, 'lib'),'dir')
    mkdir(fullfile(root_dir, 'lib'))
end

for ii=1:length(build_file)
    eval(['mex ''' build_file{ii} ''' -outdir ''' fullfile(root_dir, 'lib') '''' include_str])
end

%% Build random forest files
file1   = fullfile(root_dir, 'src', 'external', 'RF_Reg_C', 'src', 'mex_regressionRF_train.cpp');
file2   = fullfile(root_dir, 'src', 'external', 'RF_Reg_C', 'src', 'mex_regressionRF_predict.cpp');
dep1    = fullfile(root_dir, 'src', 'external', 'RF_Reg_C', 'src', 'cokus.cpp');
dep2    = fullfile(root_dir, 'src', 'external', 'RF_Reg_C', 'src', 'reg_RF.cpp');
o_file1 = fullfile(root_dir, 'lib', 'mexRF_train');
o_file2 = fullfile(root_dir, 'lib', 'mexRF_predict');

eval(['mex ' file1 ' ' dep1 ' ' dep2 ' -output ' o_file1 ' -DMATLAB -O'])
eval(['mex ' file2 ' ' dep1 ' ' dep2 ' -output ' o_file2 ' -DMATLAB -O'])

%% Build structured forest files
eval(['mex ' fullfile(root_dir, 'src', 'external','structured_forest', 'edgesDetectMex.cpp') ' -outdir ' fullfile(root_dir, 'lib') include_str])

%% Build piotr_toolbox files
eval(['mex ' fullfile(root_dir, 'src', 'external','piotr_toolbox',     'convConst.cpp') ' -outdir ' fullfile(root_dir, 'lib') include_str])
eval(['mex ' fullfile(root_dir, 'src', 'external','piotr_toolbox',   'gradientMex.cpp') ' -outdir ' fullfile(root_dir, 'lib') include_str])
eval(['mex ' fullfile(root_dir, 'src', 'external','piotr_toolbox',      'imPadMex.cpp') ' -outdir ' fullfile(root_dir, 'lib') include_str])
eval(['mex ' fullfile(root_dir, 'src', 'external','piotr_toolbox', 'imResampleMex.cpp') ' -outdir ' fullfile(root_dir, 'lib') include_str])
eval(['mex ' fullfile(root_dir, 'src', 'external','piotr_toolbox', 'rgbConvertMex.cpp') ' -outdir ' fullfile(root_dir, 'lib') include_str])

%% Build BSR-related files
% 'ucm_mean_pb'
eval(['mex ' fullfile(root_dir, 'src', 'bsr', 'ucm_mean_pb.cpp') ' -outdir ' fullfile(root_dir, 'lib')])

% 'buildW'
eval(['mex ' fullfile(root_dir, 'src', 'bsr', 'buildW.cpp') ' -outdir ' fullfile(root_dir, 'lib'),...
            ' -I' fullfile(root_dir,'src','external','BSR','buildW') ' -I' fullfile(root_dir,'src','external','BSR','buildW','util'),...
            '   ' fullfile(root_dir,'src','external','BSR','buildW','smatrix.cc'),...
            '   ' fullfile(root_dir,'src','external','BSR','buildW','ic.cc'),...
            '   ' fullfile(root_dir,'src','external','BSR','buildW','affinity.cc'),...
            ])

% 'mex_contour_sides'
eval(['mex ' fullfile(root_dir, 'src', 'bsr', 'mex_contour_sides.cpp') ' -outdir ' fullfile(root_dir, 'lib'),...
            ' -I' fullfile(root_dir,'src','external','BSR','include'),...
            '   ' fullfile(root_dir,'src','external','BSR','src','concurrent','threads','child_thread.cc'),...
            '   ' fullfile(root_dir,'src','external','BSR','src','concurrent','threads','runnable.cc'),...
            '   ' fullfile(root_dir,'src','external','BSR','src','concurrent','threads','thread.cc'),...
            '   ' fullfile(root_dir,'src','external','BSR','src','concurrent','threads','synchronization','synchronizables','synchronizable.cc'),...
            '   ' fullfile(root_dir,'src','external','BSR','src','concurrent','threads','synchronization','synchronizables','unsynchronized.cc'),...
            '   ' fullfile(root_dir,'src','external','BSR','src','lang','exceptions','ex_bad_cast.cc'),...
            '   ' fullfile(root_dir,'src','external','BSR','src','lang','exceptions','ex_not_found.cc'),...
            '   ' fullfile(root_dir,'src','external','BSR','src','lang','exceptions','ex_not_implemented.cc'),...
            '   ' fullfile(root_dir,'src','external','BSR','src','lang','exceptions','ex_index_out_of_bounds.cc'),...
            '   ' fullfile(root_dir,'src','external','BSR','src','lang','exceptions','ex_invalid_argument.cc'),...
            '   ' fullfile(root_dir,'src','external','BSR','src','lang','exceptions','ex_null_pointer_dereference.cc'),...
            '   ' fullfile(root_dir,'src','external','BSR','src','lang','exceptions','exception.cc'),...
            '   ' fullfile(root_dir,'src','external','BSR','src','lang','exceptions','throwable.cc'),...
            '   ' fullfile(root_dir,'src','external','BSR','src','lang','array.cc'),...
            '   ' fullfile(root_dir,'src','external','BSR','src','math','random','generators','rand_gen_uniform.cc'),...
            '   ' fullfile(root_dir,'src','external','BSR','src','math','random','sources','rand_source_default.cc'),...
            '   ' fullfile(root_dir,'src','external','BSR','src','math','random','sources','rand_source.cc'),...
            '   ' fullfile(root_dir,'src','external','BSR','src','math','random','sources','mersenne_twister_64.cc'),...
            '   ' fullfile(root_dir,'src','external','BSR','src','math','random','sources','rand_source_64.cc'),...
            '   ' fullfile(root_dir,'src','external','BSR','src','math','random','sources','system_entropy.cc'),...
            '   ' fullfile(root_dir,'src','external','BSR','src','math','random','util','randperm.cc'),...
            '   ' fullfile(root_dir,'src','external','BSR','src','math','matrices','matrix.cc'),...
            '   ' fullfile(root_dir,'src','external','BSR','src','math','matrices','exceptions','ex_matrix_dimension_mismatch.cc'),...
            '   ' fullfile(root_dir,'src','external','BSR','src','math','libraries','lib_image.cc'),...
            '   ' fullfile(root_dir,'src','external','BSR','src','math','libraries','lib_signal.cc'),...
            '   ' fullfile(root_dir,'src','external','BSR','src','math','math.cc'),...
            '   ' fullfile(root_dir,'src','external','BSR','src','math','exact.cc'),...
            '   ' fullfile(root_dir,'src','external','BSR','src','math','geometry','point_2D.cc'),...
            '   ' fullfile(root_dir,'src','external','BSR','src','math','geometry','seg_intersect.cc'),...
            '   ' fullfile(root_dir,'src','external','BSR','src','math','geometry','triangulation.cc'),...
            '   ' fullfile(root_dir,'src','external','BSR','src','math','geometry','triangle_2D.cc'),...
            '   ' fullfile(root_dir,'src','external','BSR','src','mlearning','clustering','clusterers','abstract','clusterer.cc'),...
            '   ' fullfile(root_dir,'src','external','BSR','src','mlearning','clustering','clusterers','abstract','weighted_clusterer.cc'),...
            '   ' fullfile(root_dir,'src','external','BSR','src','mlearning','clustering','clusterers','kmeans','basic_clusterer.cc'),...
            ]);

%% Clear variables
clear build_file file1 file2 dep1 dep2 o_file1 o_file2 ii include include_str

%% Show message
disp('-- Successful compilation of MCG. Enjoy! --')


function y = sdpcone(varargin)
%SDPCONE Low-level operator to define several SDP constraints in vectorized
%form
%
% Input
%    X       : Linear SDPVAR object of size n^2 x N
%
% Example
%
% The typical use is when we want to define a very large number of LMI
% constraints with reduced overhead (no analysis or check for symmetry etc)
%
% This operator is very specialized and low-level, and not normally used...
%
% X = sdpvar(5);Y = sdpvar(5);
% F = sdpcone([X(:) Y(:)]); % Equivalent to [X>=0, Y>=0]
%
% See also  @SDPVAR/CONE, @SDPVAR/RCONE

y.typeflag = 57;
y=lmi(y);


function inv_mix = spm_cfg_eeg_inv_mix
% Configuration file for merging (using a new inversion) a number of
% imaging source inversion reconstructions
%__________________________________________________________________________
% Copyright (C) 2010 Wellcome Trust Centre for Neuroimaging

% Gareth Barnes
% $Id: spm_cfg_eeg_inv_mix.m 5924 2014-03-19 14:59:12Z gareth $

D = cfg_files;
D.tag = 'D';
D.name = 'M/EEG datasets';
D.filter = 'mat';
D.num = [1 Inf];
D.help = {'Select the M/EEG mat files or .mat files containing inverses'};

val = cfg_entry;
val.tag = 'val';
val.name = 'Inversion index';
val.strtype = 'n';
val.help = {'Index of the cell in D.inv (same for all files) where the forward model can be found and the results will be stored.'};
val.val = {1};

prefix = cfg_entry;
prefix.tag = 'prefix';
prefix.name = 'Merged file prefix';
prefix.strtype = 's';
prefix.val={'merged'};
prefix.help = {'Prefix for the new filename that will contain the merged inversion results'};

inv_mix = cfg_exbranch;
inv_mix.tag = 'inv_mix';
inv_mix.name = 'Merge source estimates from multiple inversions';
inv_mix.val = {D, val,prefix};
inv_mix.help = {'To merge different source level variance estimates based on the same data'};
inv_mix.prog = @run_inv_mix;
inv_mix.vout = @vout_inv_mix;
inv_mix.modality = {'MEG'};

function  out = run_inv_mix(job)

inverse = [];
if numel(job.D)<1,
    error('Need to add a number of files to combine');
end;

%% first compile multiple inversions
disp('Loading inversions');
allID=[];
allJ=[];
allqC=[];
allF=[];

surfdir=[];
for j=1:numel(job.D), %% move through files- assume that changing directory means surface(i.e. lead field also changes)
    [a1,b1,c1]=fileparts(job.D{j});
    surfdir=strvcat(surfdir,a1);
end;

gainfiles=[];
for j=1:numel(job.D), %% move through files- assume that changing directory means surface(i.e. lead field also changes)

    try
        spmfilename=job.D{j};
        D = spm_eeg_load(spmfilename);
        inv=D.inv{job.val};
    catch
        dum=load(job.D{j});
        spmfilename=dum.spmfilename;
        [a0,b1,c1]=fileparts(spmfilename);
        [a1,b0,c0]=fileparts(job.D{j});
        D=spm_eeg_load([a1 filesep b1 c1]);
        inv=dum.inv;
    end;

    allF(j)=inv.inverse.F;
    allqC(j,:)=inv.inverse.qC;

    allmesh{j}.M   = inv.mesh.tess_mni;
    allcortex{j}=inv.mesh.tess_ctx;
    %% transform back to temporal subspace of original data
    %% (as Us will be different for different surfaces- but just rotations)
    check_data(j,:)=inv.inverse.U{1}'*inv.inverse.Y(:,1);

    disp('Loading SPM gain matrices from surface directories');

    [L,D]=spm_eeg_lgainmat(D);
    gainfile=[deblank(surfdir(j,:)) filesep D.inv{D.val}.gainmat];
    gainfiles = strvcat(gainfiles,gainfile);
    allL1(j,:)=L(1,:);

end;

%% check original data was the same
if numel(job.D)>1,
    if max(std(check_data))>max(std(check_data'))/1e6,
        error('data is not the same for these files');
    end;
end;

%%NB OCCAMS RAZOR AT 3
useind=find(allF>max(allF)-3); %%
allqC=allqC(useind,:);
allmesh=allmesh(useind);
allcortex=allcortex(useind);
gainfiles=gainfiles(useind,:);
allL1=allL1(useind,:);
%% only take unique lead fields out
[dum,fileind,surfind]=unique(allL1,'rows');
ugainfiles=gainfiles(fileind,:);

[a1,b1,c1]=fileparts(spmfilename);
outdir=surfdir(1,1:max(strfind(surfdir(1,:),filesep))); %% go up one directory
outfilename=[outdir filesep job.prefix b1 c1];
disp(sprintf('Copying and renaming original SPM file to %s',outfilename));

D2=copy(D,outfilename);

if ~isfield(D2.inv{D2.val},'inverse'),
    disp('No inversion parameters in file, taking from last inversion');
    D2.inv{D2.val}.inverse=inv.inverse;
end;

clear dum inverse D

vert=[];
face=[];
cmap1=[];
cortexstr='';
for j=1:length(fileind),
    offset=uint32(size(vert,1).*ones(size(allmesh{fileind(j)}.M.face)));
    col=j*10;
    vert=[vert ;allmesh{fileind(j)}.M.vert];
    face=[face ;allmesh{fileind(j)}.M.face+offset];
    cmap1=[cmap1 ;repmat(col,size(allmesh{fileind(j)}.M.face,1),1)];

    cortexstr=[cortexstr sprintf('%s',allcortex{fileind(j)})];
    if j<length(fileind),
        cortexstr=[cortexstr ';'];
    end;
end;
figure;
h=trisurf(face,vert(:,1),vert(:,2),vert(:,3),cmap1)
set(h,'Linestyle','none');
alpha(0.1);

% UPDATE MNI MESH- MAY NEED TO UPDATE OTHERS TOO
 D2.inv{D2.val}.mesh.tess_mni.vert=vert;
 D2.inv{D2.val}.mesh.tess_mni.face=face;
 D2.inv{D2.val}.mesh.tess_ctx=cortexstr;

%D2.save;
clear vert face cmap1 offset

D2=spm_eeg_invert_classic_mix(D2,D2.val,allqC,surfind,ugainfiles);

disp(sprintf('Improvement in model evidence over best single solution %3.2f',D2.inv{D2.val}.inverse.F-max(allF)));

if ~iscell(D2)
    D2 = {D2};
end

for i = 1:numel(D2)
    save(D2{i});
end

out.D = {outfilename};

function dep = vout_inv_mix(job)
% Output is always in field "D", no matter how job is structured
dep = cfg_dep;
dep.sname = 'M/EEG dataset(s) after imaging source reconstruction';
% reference field "D" from output
dep.src_output = substruct('.','D');
% this can be entered into any evaluated input
dep.tgt_spec   = cfg_findspec({{'filter','mat'}});


function [noFluxRxns, noFluxRxnsRelaxed, subGraphs, notProducedMets, minToConnect,...
    neededForProductionMat, canProduceWithoutInput, canConsumeWithoutOutput, ...
    connectedFromTemplates, addedFromTemplates]=gapReport(model, templateModels)
% gapReport
%   Performs a gap analysis and summarizes the results
%
%   model                       a model structure
%   templateModels              a cell array of template models to use for
%                               gap filling (opt)
%
%   noFluxRxns                  cell array with reactions that cannot carry
%                               flux
%   noFluxRxnsRelaxed           cell array with reactions that cannot carry
%                               flux even if the mass balance constraint is
%                               relaxed so that it is allowed to have
%                               net production of all metabolites
%   subGraphs                   structure with the metabolites in each of
%                               the isolated sub networks
%   notProducedMets             cell array with the metabolites that
%                               couldn't have net production
%   minToConnect                structure with the minimal number of
%                               metabolites that need to be connected in
%                               order to be able to produce all other
%                               metabolites and which metabolites each of
%                               them connects
%   neededForProductionMat      matrix where n x m is true if metabolite n
%                               allows for production of metabolite m
%   canProduceWithoutInput      cell array with metabolites that could be
%                               produced even when there is no input to the
%                               model
%   canConsumeWithoutOutput     cell array with metabolites that could be
%                               consumed even when there is no output from
%                               the model
%   connectedFromTemplates      cell array with the reactions that could be
%                               connected using the template models
%   addedFromTemplates          structure with the reactions that were
%                               added from the template models and which
%                               model they were added from
%
%   Usage: [noFluxRxns, noFluxRxnsRelaxed, subGraphs, notProducedMets, minToConnect,...
%    neededForProductionMat, connectedFromTemplates, addedFromTemplates]=...
%    gapReport(model, templateModels)

if nargin<2
    templateModels=[];
    connectedFromTemplates=[];
    addedFromTemplates=[];
end

fprintf(['Gap analysis for ' model.id ' - ' model.name '\n\n']);
if isfield(model,'unconstrained')
    calculateINOUT=true;
    closedModel=model;
    model=simplifyModel(model);
else
    canConsumeWithoutOutput={};
    canProduceWithoutInput={};
    calculateINOUT=false;
end

model2=model;
model2.b=[model2.b inf(numel(model2.mets),1)];
I=haveFlux(model);
noFluxRxns=model.rxns(~I);
J=haveFlux(model2);
noFluxRxnsRelaxed=model2.rxns(~J);
bModel=removeReactions(model,~I,true,true);
cModel=removeReactions(model2,~J,true,true);
fprintf('***Overview\n');
fprintf([num2str(numel(model.rxns)-sum(I)) ' out of ' num2str(numel(model.rxns))...
    ' reactions cannot carry flux (' num2str(numel(model.rxns)-sum(J)) ' if net production of all metabolites is allowed)\n']);
fprintf([num2str(numel(model.mets)-numel(bModel.mets)) ' out of ' num2str(numel(model.mets))...
    ' metabolites are unreachable (' num2str(numel(model.mets)-numel(cModel.mets)) ' if net production of all metabolites is allowed)\n']);

fprintf('\n***Isolated subnetworks\n');
subGraphs=getAllSubGraphs(model);
fprintf(['A total of ' num2str(size(subGraphs,2)) ' isolated sub-networks are present in the model\n']);
for i=1:size(subGraphs,2)
    fprintf(['\t' num2str(i) '. ' num2str(sum(subGraphs(:,i))) ' metabolites\n']);
end

fprintf('\n***Metabolite connectivity\n');
[notProducedMets, ~, neededForProductionMat,minToConnect]=checkProduction(model,true,model.comps,false);
fprintf(['To enable net production of all metabolites, a total of ' num2str(numel(minToConnect)) ' metabolites must be connected\n']);
fprintf('Top 10 metabolites to connect:\n');
for i=1:min(10,numel(minToConnect))
    fprintf(['\t' num2str(i) '. ' minToConnect{i} '\n']);
end

if calculateINOUT==true
    fprintf('\n***Mass balancing\n');
    produced=canProduce(closedModel);
    canProduceWithoutInput=closedModel.mets(produced);
    consumed=canConsume(closedModel);
    canConsumeWithoutOutput=closedModel.mets(consumed);
    fprintf([num2str(numel(canConsumeWithoutOutput)) ' metabolites could be consumed without any outputs\n' num2str(numel(canProduceWithoutInput)) ' metabolites could be produced without any inputs\n']);
end

if ~isempty(templateModels)
    fprintf('\n***Automated gap-filling\n');
    [connectedFromTemplates, ~, addedFromTemplates]=fillGaps(model,templateModels);
    t=templateModels{1}.id;
    for i=2:numel(templateModels)
        t=[t ', ' templateModels{i}.id];
    end
    fprintf([num2str(numel(connectedFromTemplates)) ' unconnected reactions can be connected by including ' num2str(numel(addedFromTemplates)) ' reactions from\n' t '\n']);
end
end


function res = vl_simplenn(net, x, dzdy, res, varargin)
%VL_SIMPLENN  Evaluate a SimpleNN network.
%   RES = VL_SIMPLENN(NET, X) evaluates the convnet NET on data X.
%   RES = VL_SIMPLENN(NET, X, DZDY) evaluates the convnent NET and its
%   derivative on data X and output derivative DZDY (foward+bacwkard pass).
%   RES = VL_SIMPLENN(NET, X, [], RES) evaluates the NET on X reusing the
%   structure RES.
%   RES = VL_SIMPLENN(NET, X, DZDY, RES) evaluates the NET on X and its
%   derivatives reusing the structure RES.
%
%   This function process networks using the SimpleNN wrapper
%   format. Such networks are 'simple' in the sense that they consist
%   of a linear sequence of computational layers. You can use the
%   `dagnn.DagNN` wrapper for more complex topologies, or write your
%   own wrapper around MatConvNet computational blocks for even
%   greater flexibility.
%
%   The format of the network structure NET and of the result
%   structure RES are described in some detail below. Most networks
%   expect the input data X to be standardized, for example by
%   rescaling the input image(s) and subtracting a mean. Doing so is
%   left to the user, but information on how to do this is usually
%   contained in the `net.meta` field of the NET structure (see
%   below).
%
%   The NET structure needs to be updated as new features are
%   introduced in MatConvNet; use the `VL_SIMPLENN_TIDY()` function
%   to make an old network current, as well as to cleanup and check
%   the structure of an existing network.
%
%   Networks can run either on the CPU or GPU. Use VL_SIMPLENN_MOVE()
%   to move the network parameters between these devices.
%
%   To print or obtain summary of the network structure, use the
%   VL_SIMPLENN_DISPLAY() function.
%
%   VL_SIMPLENN(NET, X, DZDY, RES, 'OPT', VAL, ...) takes the following
%   options:
%
%   `Mode`:: `'normal'`
%      Specifies the mode of operation. It can be either `'normal'` or
%      `'test'`. In test mode, dropout and batch-normalization are
%      bypassed. Note that, when a network is deployed, it may be
%      preferable to *remove* such blocks altogether.
%
%   `ConserveMemory`:: `false`
%      Aggressively delete intermediate results. This in practice has
%      a very small performance hit and allows training much larger
%      models. However, it can be useful to disable it for
%      debugging. Keeps the values in `res(1)` (input) and `res(end)`
%      (output) with the outputs of `loss` and `softmaxloss` layers.
%      It is also possible to preserve individual layer outputs
%      by setting `net.layers{...}.precious` to `true`.
%      For back-propagation, keeps only the derivatives with respect to
%      weights.
%
%   `CuDNN`:: `true`
%      Use CuDNN when available.
%
%   `Accumulate`:: `false`
%      Accumulate gradients in back-propagation instead of rewriting
%      them. This is useful to break the computation in sub-batches.
%      The gradients are accumulated to the provided RES structure
%      (i.e. to call VL_SIMPLENN(NET, X, DZDY, RES, ...).
%
%   `BackPropDepth`:: `inf`
%      Limit the back-propagation to top-N layers.
%
%   `SkipForward`:: `false`
%      Reuse the output values from the provided RES structure and compute
%      only the derivatives (backward pass).
%
%   ## The result format
%
%   SimpleNN returns the result of its calculations in the RES
%   structure array. RES(1) contains the input to the network, while
%   RES(2), RES(3), ... contain the output of each layer, from first
%   to last. Each entry has the following fields:
%
%   - `res(i+1).x`: the output of layer `i`. Hence `res(1).x` is the
%     network input.
%
%   - `res(i+1).aux`: any auxiliary output data of layer i. For example,
%     dropout uses this field to store the dropout mask.
%
%   - `res(i+1).dzdx`: the derivative of the network output relative
%     to the output of layer `i`. In particular `res(1).dzdx` is the
%     derivative of the network output with respect to the network
%     input.
%
%   - `res(i+1).dzdw`: a cell array containing the derivatives of the
%     network output relative to the parameters of layer `i`. It can
%     be a cell array for multiple parameters.
%
%   ## The network format
%
%   The network is represented by the NET structure, which contains
%   two fields:
%
%   - `net.layers` is a cell array with the CNN layers.
%
%   - `net.meta` is a grab-bag of auxiliary application-dependent
%     information, including for example details on how to normalize
%     input data, the class names for a classifiers, or details of
%     the learning algorithm. The content of this field is ignored by
%     VL_SIMPLENN().
%
%   SimpleNN is aware of the following layers:
%
%   Convolution layer::
%     The convolution layer wraps VL_NNCONV(). It has fields:
%
%     - `layer.type` contains the string `'conv'`.
%     - `layer.weights` is a cell array with filters and biases.
%     - `layer.stride` is the sampling stride (e.g. 1).
%     - `layer.pad` is the padding (e.g. 0).
%     - `layer.dilate` is the dilation factor (e.g. 1).
%
%   Convolution transpose layer::
%     The convolution transpose layer wraps VL_NNCONVT(). It has fields:
%
%     - `layer.type` contains the string `'convt'`.
%     - `layer.weights` is a cell array with filters and biases.
%     - `layer.upsample` is the upsampling factor (e.g. 1).
%     - `layer.crop` is the amount of output cropping (e.g. 0).
%
%   Max pooling layer::
%     The max pooling layer wraps VL_NNPOOL(). It has fields:
%
%     - `layer.type` contains the string `'pool'`.
%     - `layer.method` is the pooling method (either 'max' or 'avg').
%     - `layer.pool` is the pooling size (e.g. 3).
%     - `layer.stride` is the sampling stride (usually 1).
%     - `layer.pad` is the padding (usually 0).
%
%   Normalization (LRN) layer::
%     The normalization layer wraps VL_NNNORMALIZE(). It has fields:
%
%     - `layer.type` contains the string `'normalize'` or `'lrn'`.
%     - `layer.param` contains the normalization parameters (see VL_NNNORMALIZE()).
%
%   Spatial normalization layer::
%     The spatial normalization layer wraps VL_NNSPNORM(). It has fields:
%
%     - `layer.type` contains the string `'spnorm'`.
%     - `layer.param` contains the normalization parameters (see VL_NNSPNORM()).
%
%   Batch normalization layer::
%     This layer wraps VL_NNBNORM(). It has fields:
%
%     - `layer.type` contains the string `'bnorm'`.
%     - `layer.weights` contains is a cell-array with, multiplier and
%       biases, and moments parameters
%
%     Note that moments are used only in `'test'` mode to bypass batch
%     normalization.
%
%   ReLU and Sigmoid layers::
%     The ReLU layer wraps VL_NNRELU(). It has fields:
%
%     - `layer.type` contains the string `'relu'`.
%     - `layer.leak` is the leak factor (e.g. 0).
%
%     The sigmoid layer is the same, but for the sigmoid function,
%     with `relu` replaced by `sigmoid` and no leak factor.
%
%   Dropout layer::
%     The dropout layer wraps VL_NNDROPOUT(). It has fields:
%
%     - `layer.type` contains the string `'dropout'`.
%     - `layer.rate` is the dropout rate (e.g. 0.5).
%
%     Note that the block is bypassed in `test` mode.
%
%   Softmax layer::
%     The softmax layer wraps VL_NNSOFTMAX(). It has fields
%
%     - `layer.type` contains the string`'softmax'`.
%
%   Log-loss layer and softmax-log-loss::
%     The log-loss layer wraps VL_NNLOSS(). It has fields:
%
%     - `layer.type` contains `'loss'`.
%     - `layer.class` contains the ground-truth class labels.
%
%     The softmax-log-loss layer wraps VL_NNSOFTMAXLOSS() instead. it
%     has the same parameters, but `type` contains the `'softmaxloss'`
%     string.
%
%   P-dist layer::
%     The p-dist layer wraps VL_NNPDIST(). It has fields:
%
%     - `layer.type` contains the string  `'pdist'`.
%     - `layer.p` is the P parameter of the P-distance (e.g. 2).
%     - `layer.noRoot` it tells whether to raise the distance to
%     the P-th power (e.g. `false`).
%     - `layer.epsilon` is the regularization parameter for the derivatives.
%
%   Custom layer::
%     This can be used to specify custom layers.
%
%     - `layer.type` contains the string `'custom'`.
%     - `layer.forward` is  a function handle computing the block.
%     - `layer.backward` is a function handle computing the block derivative.
%
%     The first function is called as
%
%          res(i+1) = layer.forward(layer, res(i), res(i+1))
%
%     where RES is the structure array specified before. The second function is
%     called as
%
%          res(i) = layer.backward(layer, res(i), res(i+1))
%
%     Note that the `layer` structure can contain additional custom
%     fields if needed.
%
%   See also: dagnn.DagNN, VL_SIMPLENN_TIDY(),
%   VL_SIMPLENN_DISPLAY(), VL_SIMPLENN_MOVE().

% Copyright (C) 2014-15 Andrea Vedaldi.
% All rights reserved.
%
% This file is part of the VLFeat library and is made available under
% the terms of the BSD license (see the COPYING file).

opts.conserveMemory = false ;
opts.sync = false ;
opts.mode = 'normal' ;
opts.accumulate = false ;
opts.cudnn = true ;
opts.backPropDepth = +inf ;
opts.skipForward = false ;
opts.parameterServer = [] ;
opts.holdOn = false ;
opts = vl_argparse(opts, varargin);

n = numel(net.layers) ;
assert(opts.backPropDepth > 0, 'Invalid `backPropDepth` value (!>0)');
backPropLim = max(n - opts.backPropDepth + 1, 1);

if (nargin <= 2) || isempty(dzdy)
  doder = false ;
  if opts.skipForward
    error('simplenn:skipForwardNoBackwPass', ...
      '`skipForward` valid only when backward pass is computed.');
  end
else
  doder = true ;
end

if opts.cudnn
  cudnn = {'CuDNN'} ;
  bnormCudnn = {'NoCuDNN'} ; % ours seems slighty faster
else
  cudnn = {'NoCuDNN'} ;
  bnormCudnn = {'NoCuDNN'} ;
end

switch lower(opts.mode)
  case 'normal'
    testMode = false ;
  case 'test'
    testMode = true ;
  otherwise
    error('Unknown mode ''%s''.', opts. mode) ;
end

gpuMode = isa(x, 'gpuArray') ;

if nargin <= 3 || isempty(res)
  if opts.skipForward
    error('simplenn:skipForwardEmptyRes', ...
    'RES structure must be provided for `skipForward`.');
  end
  res = struct(...
    'x', cell(1,n+1), ...
    'dzdx', cell(1,n+1), ...
    'dzdw', cell(1,n+1), ...
    'aux', cell(1,n+1), ...
    'stats', cell(1,n+1), ...
    'time', num2cell(zeros(1,n+1)), ...
    'backwardTime', num2cell(zeros(1,n+1))) ;
end

if ~opts.skipForward
  res(1).x = x ;
end

% -------------------------------------------------------------------------
%                                                              Forward pass
% -------------------------------------------------------------------------

for i=1:n
  if opts.skipForward, break; end;
  l = net.layers{i} ;
  res(i).time = tic ;
  switch l.type
    case 'conv'
      res(i+1).x = vl_nnconv(res(i).x, l.weights{1}, l.weights{2}, ...
        'pad', l.pad, ...
        'stride', l.stride, ...
        'dilate', l.dilate, ...
        l.opts{:}, ...
        cudnn{:}) ;

    case 'convt'
      res(i+1).x = vl_nnconvt(res(i).x, l.weights{1}, l.weights{2}, ...
        'crop', l.crop, ...
        'upsample', l.upsample, ...
        'numGroups', l.numGroups, ...
        l.opts{:}, ...
        cudnn{:}) ;

    case 'pool'
      res(i+1).x = vl_nnpool(res(i).x, l.pool, ...
        'pad', l.pad, 'stride', l.stride, ...
        'method', l.method, ...
        l.opts{:}, ...
        cudnn{:}) ;

    case {'normalize', 'lrn'}
      res(i+1).x = vl_nnnormalize(res(i).x, l.param) ;

    case 'softmax'
      res(i+1).x = vl_nnsoftmax(res(i).x) ;

    case 'loss'
      res(i+1).x = vl_nnloss(res(i).x, l.class) ;

    case 'softmaxloss'
      res(i+1).x = vl_nnsoftmaxloss(res(i).x, l.class) ;

    case 'relu'
      if l.leak > 0, leak = {'leak', l.leak} ; else leak = {} ; end
      res(i+1).x = vl_nnrelu(res(i).x,[],leak{:}) ;

    case 'sigmoid'
      res(i+1).x = vl_nnsigmoid(res(i).x) ;

    case 'noffset'
      res(i+1).x = vl_nnnoffset(res(i).x, l.param) ;

    case 'spnorm'
      res(i+1).x = vl_nnspnorm(res(i).x, l.param) ;

    case 'dropout'
      if testMode
        res(i+1).x = res(i).x ;
      else
        [res(i+1).x, res(i+1).aux] = vl_nndropout(res(i).x, 'rate', l.rate) ;
      end

    case 'bnorm'
      if testMode
        res(i+1).x = vl_nnbnorm(res(i).x, l.weights{1}, l.weights{2}, ...
                                'moments', l.weights{3}, ...
                                'epsilon', l.epsilon, ...
                                bnormCudnn{:}) ;
      else
        res(i+1).x = vl_nnbnorm(res(i).x, l.weights{1}, l.weights{2}, ...
                                'epsilon', l.epsilon, ...
                                bnormCudnn{:}) ;
      end

    case 'pdist'
      res(i+1).x = vl_nnpdist(res(i).x, l.class, l.p, ...
        'noRoot', l.noRoot, ...
        'epsilon', l.epsilon, ...
        'aggregate', l.aggregate, ...
        'instanceWeights', l.instanceWeights) ;

    case 'custom'
      res(i+1) = l.forward(l, res(i), res(i+1)) ;

    otherwise
      error('Unknown layer type ''%s''.', l.type) ;
  end

  % optionally forget intermediate results
  needsBProp = doder && i >= backPropLim;
  forget = opts.conserveMemory && ~needsBProp ;
  if i > 1
    lp = net.layers{i-1} ;
    % forget RELU input, even for BPROP
    forget = forget && (~needsBProp || (strcmp(l.type, 'relu') && ~lp.precious)) ;
    forget = forget && ~(strcmp(lp.type, 'loss') || strcmp(lp.type, 'softmaxloss')) ;
    forget = forget && ~lp.precious ;
  end
  if forget
    res(i).x = [] ;
  end

  if gpuMode && opts.sync
    wait(gpuDevice) ;
  end
  res(i).time = toc(res(i).time) ;
end

% -------------------------------------------------------------------------
%                                                             Backward pass
% -------------------------------------------------------------------------

if doder
  res(n+1).dzdx = dzdy ;
  for i=n:-1:backPropLim
    l = net.layers{i} ;
    res(i).backwardTime = tic ;
    switch l.type

      case 'conv'
        [res(i).dzdx, dzdw{1}, dzdw{2}] = ...
          vl_nnconv(res(i).x, l.weights{1}, l.weights{2}, res(i+1).dzdx, ...
          'pad', l.pad, ...
          'stride', l.stride, ...
          'dilate', l.dilate, ...
          l.opts{:}, ...
          cudnn{:}) ;

      case 'convt'
        [res(i).dzdx, dzdw{1}, dzdw{2}] = ...
          vl_nnconvt(res(i).x, l.weights{1}, l.weights{2}, res(i+1).dzdx, ...
          'crop', l.crop, ...
          'upsample', l.upsample, ...
          'numGroups', l.numGroups, ...
          l.opts{:}, ...
          cudnn{:}) ;

      case 'pool'
        res(i).dzdx = vl_nnpool(res(i).x, l.pool, res(i+1).dzdx, ...
                                'pad', l.pad, 'stride', l.stride, ...
                                'method', l.method, ...
                                l.opts{:}, ...
                                cudnn{:}) ;

      case {'normalize', 'lrn'}
        res(i).dzdx = vl_nnnormalize(res(i).x, l.param, res(i+1).dzdx) ;

      case 'softmax'
        res(i).dzdx = vl_nnsoftmax(res(i).x, res(i+1).dzdx) ;

      case 'loss'
        res(i).dzdx = vl_nnloss(res(i).x, l.class, res(i+1).dzdx) ;

      case 'softmaxloss'
        res(i).dzdx = vl_nnsoftmaxloss(res(i).x, l.class, res(i+1).dzdx) ;

      case 'relu'
        if l.leak > 0, leak = {'leak', l.leak} ; else leak = {} ; end
        if ~isempty(res(i).x)
          res(i).dzdx = vl_nnrelu(res(i).x, res(i+1).dzdx, leak{:}) ;
        else
          % if res(i).x is empty, it has been optimized away, so we use this
          % hack (which works only for ReLU):
          res(i).dzdx = vl_nnrelu(res(i+1).x, res(i+1).dzdx, leak{:}) ;
        end

      case 'sigmoid'
        res(i).dzdx = vl_nnsigmoid(res(i).x, res(i+1).dzdx) ;

      case 'noffset'
        res(i).dzdx = vl_nnnoffset(res(i).x, l.param, res(i+1).dzdx) ;

      case 'spnorm'
        res(i).dzdx = vl_nnspnorm(res(i).x, l.param, res(i+1).dzdx) ;

      case 'dropout'
        if testMode
          res(i).dzdx = res(i+1).dzdx ;
        else
          res(i).dzdx = vl_nndropout(res(i).x, res(i+1).dzdx, ...
                                     'mask', res(i+1).aux) ;
        end

      case 'bnorm'
        [res(i).dzdx, dzdw{1}, dzdw{2}, dzdw{3}] = ...
          vl_nnbnorm(res(i).x, l.weights{1}, l.weights{2}, res(i+1).dzdx, ...
                     'epsilon', l.epsilon, ...
                     bnormCudnn{:}) ;
        % multiply the moments update by the number of images in the batch
        % this is required to make the update additive for subbatches
        % and will eventually be normalized away
        dzdw{3} = dzdw{3} * size(res(i).x,4) ;

      case 'pdist'
        res(i).dzdx = vl_nnpdist(res(i).x, l.class, ...
          l.p, res(i+1).dzdx, ...
          'noRoot', l.noRoot, ...
          'epsilon', l.epsilon, ...
          'aggregate', l.aggregate, ...
          'instanceWeights', l.instanceWeights) ;

      case 'custom'
        res(i) = l.backward(l, res(i), res(i+1)) ;

    end % layers

    switch l.type
      case {'conv', 'convt', 'bnorm'}
        if ~opts.accumulate
          res(i).dzdw = dzdw ;
        else
          for j=1:numel(dzdw)
            res(i).dzdw{j} = res(i).dzdw{j} + dzdw{j} ;
          end
        end
        dzdw = [] ;
        if ~isempty(opts.parameterServer) && ~opts.holdOn
          for j = 1:numel(res(i).dzdw)
            opts.parameterServer.push(sprintf('l%d_%d',i,j),res(i).dzdw{j}) ;
            res(i).dzdw{j} = [] ;
          end
        end
    end
    if opts.conserveMemory && ~net.layers{i}.precious && i ~= n
      res(i+1).dzdx = [] ;
      res(i+1).x = [] ;
    end
    if gpuMode && opts.sync
      wait(gpuDevice) ;
    end
    res(i).backwardTime = toc(res(i).backwardTime) ;
  end
  if i > 1 && i == backPropLim && opts.conserveMemory && ~net.layers{i}.precious
    res(i).dzdx = [] ;
    res(i).x = [] ;
  end
end


% *************************************************************************************
% * DESCRIPTION:                                                                      *
% * This script computes the experiments performed in ref. [1].                       *
% * --------------------------------------------------------------------------------- *
% * REFERENCE:                                                                        *
% * [1] Vallieres, M. et al. (2015). A radiomics model from joint FDG-PET and         *
% *     MRI texture features for the prediction of lung metastases in soft-tissue     *
% *     sarcomas of the extremities. Physics in Medicine and Biology, 60(14),         *
% *     5471-5496. doi:10.1088/0031-9155/60/14/5471                                   *
% * --------------------------------------------------------------------------------- *
% * AUTHOR(S): Martin Vallieres <mart.vallieres@gmail.com>                            *
% * --------------------------------------------------------------------------------- *
% * HISTORY:                                                                          *
% * - Creation: May 2015                                                              *
% * --------------------------------------------------------------------------------- *
% * STATEMENT:                                                                        *
% * This file is part of <https://github.com/mvallieres/radiomics/>,                  *
% * a package providing MATLAB programming tools for radiomics analysis.              *
% * --> Copyright (C) 2015  Martin Vallieres                                          *
% *                                                                                *
% *   This package is free software: you can redistribute it and/or modify            *
% *   it under the terms of the GNU General Public License as published by            *
% *   the Free Software Foundation, either version 3 of the License, or               *
% *   (at your option) any later version.                                             *
% *                                                                                *
% *   This package is distributed in the hope that it will be useful,                 *
% *   but WITHOUT ANY WARRANTY; without even the implied warranty of                  *
% *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                   *
% *   GNU General Public License for more details.                                    *
% *                                                                                *
% *   You should have received a copy of the GNU General Public License               *
% *   along with this package.  If not, see <http://www.gnu.org/licenses/>.           *
% *************************************************************************************

fprintf('\n')
help masterScript_STS
fprintf('\n')
warning off

% INITIALIZATION
pathWORK = pwd;
roiNumb = load('contour_Mass'); roiNumb = struct2cell(roiNumb); roiNumb = roiNumb{1};
outcome = load('outcome'); outcome = struct2cell(outcome); outcome = outcome{1};
nPatient = numel(outcome);

% TEXTURE EXTRACTION PARAMETERS AND DEGREES OF FREEDOM
MRIinv_cell = {'NoInv','Inv'};
MRIweight_mat = [1/4,1/3,1/2,2/3,3/4];
R_mat = [1/2,2/3,1,3/2,2];
scale_cell = {'pixelW',1,2,3,4,5};
algo_cell = {'Equal','Lloyd'};
Ng_mat = [8,16,32,64];
paramSEP = {R_mat,scale_cell,algo_cell,Ng_mat};                           % NOTE: paramSEP and paramFUS must always be of the same format and size for SEPARATE
paramFUS = {MRIinv_cell,MRIweight_mat,R_mat,scale_cell,algo_cell,Ng_mat}; % and FUSED SCANS as presented here, with the same ordering of different extraction parameters.
baselineSEP = [3,1,2,3]; % As defined in ref. [1]
baselineFUS = [1,3,3,1,2,3]; % As defined in ref. [1]
freedomSEP = [unique(perms([0 0 0 0]),'rows');unique(perms([0 0 0 1]),'rows');...
              unique(perms([0 0 1 1]),'rows');unique(perms([0 1 1 1]),'rows');...
              unique(perms([1 1 1 1]),'rows')];
freedomFUS = [unique(perms([0 0 0 0 0 0]),'rows');unique(perms([0 0 0 0 0 1]),'rows');...
              unique(perms([0 0 0 0 1 1]),'rows');unique(perms([0 0 0 1 1 1]),'rows');...
              unique(perms([0 0 1 1 1 1]),'rows');unique(perms([0 1 1 1 1 1]),'rows');...
              unique(perms([1 1 1 1 1 1]),'rows')];

% MULTIVARIABLE ANALYSIS PARAMETERS
nBoot = 1000;
alpha = 0.5; delta = 0.5;
setSize = 25;
fSetName = {'PET','SEPARATE','FUSED'};
maxOrder = 10;

% ************** START COMPUTATION **************

% 1. READ DATA DOWNLOADED FROM THE TCIA WEBSITE (http://dx.doi.org/10.7937/K9/TCIA.2015.7GO2GSKS)
fprintf('\n\n*********************** ORGANIZING AND PROCESSING DICOM DATA FROM TCIA WEBSITE ***********************\n')
readAllDICOM_STS([pathWORK,'/Soft-tissue-Sarcoma'],nPatient)

% 2. COMPUTE NON-TEXTURE FEATURES
fprintf('\n\n*********************** COMPUTING NON-TEXTURE FEATURES ***********************\n')
calcAllNonTextureFeatures_STS(pathWORK,nPatient,roiNumb,outcome)

% 3. COMPUTE AND ORGANIZING ALL TEXTURE FEATURES
mkdir('TEXTURES'), fprintf('\n')
calcAllSeparateTextures_STS(pathWORK,nPatient,roiNumb,R_mat,scale_cell,algo_cell,Ng_mat)
organizeSeparateTextures_STS(pathWORK,nPatient,R_mat,scale_cell,algo_cell,Ng_mat)
calcAllFusedTextures_STS(pathWORK,nPatient,roiNumb,MRIinv_cell,MRIweight_mat,R_mat,scale_cell,algo_cell,Ng_mat)
organizeFusedTextures_STS(pathWORK,nPatient,MRIinv_cell,MRIweight_mat,R_mat,scale_cell,algo_cell,Ng_mat)

% 5. PERFORM FEATURE SET REDUCTION
mkdir('FSET')
nonTextures = load('nonTextures'); nonTextures = struct2cell(nonTextures); nonTextures = nonTextures{1};
PET  = load('textures_PET');  PET  = struct2cell(PET);  PET  = PET{1};
T1   = load('textures_T1');   T1   = struct2cell(T1);   T1   = T1{1};
T2FS = load('textures_T2FS'); T2FS = struct2cell(T2FS); T2FS = T2FS{1};
PET_T1   = load('textures_PET_T1');   PET_T1   = struct2cell(PET_T1);   PET_T1   = PET_T1{1};
PET_T2FS = load('textures_PET_T2FS'); PET_T2FS = struct2cell(PET_T2FS); PET_T2FS = PET_T2FS{1};

tic
fprintf('\n\nFinding the path to the ''MINE.jar'' application on the system ... ')
pathMINE = findMINE('Linux');
fprintf('DONE\n')
toc

% For Feature set 1: PET
fprintf('\n*********************** PERFORMING FEATURE SET REDUCTION FOR ''PET'' FEATURE SET ***********************\n')
calcAllFeatureSets_STS(pathWORK,pathMINE,fSetName{1},outcome,setSize,nonTextures,{PET},{'PET'},paramSEP,freedomSEP,baselineSEP,alpha,delta,nBoot)

% For Feature set 2: PET, T1, T2FS
fprintf('\n*********************** PERFORMING FEATURE SET REDUCTION FOR ''SEPARATE'' FEATURE SET **********************\n')
calcAllFeatureSets_STS(pathWORK,pathMINE,fSetName{2},outcome,setSize,nonTextures,{PET,T1,T2FS},{'PET','T1','T2FS'},paramSEP,freedomSEP,baselineSEP,alpha,delta,nBoot)

% For Feature set 3: PET_T1, PET_T2FS
fprintf('\n*********************** PERFORMING FEATURE SET REDUCTION FOR ''FUSED'' FEATURE SET ***********************\n')
calcAllFeatureSets_STS(pathWORK,pathMINE,fSetName{3},outcome,setSize,nonTextures,{PET_T1,PET_T2FS},{'PET_T1','PET_T2FS'},paramFUS,freedomFUS,baselineFUS,alpha,delta,nBoot)

% 6. PERFORM FEATURE SET SELECTION
mkdir('MODELS')
fprintf('\n\nFinding the path to''fastAUC.cpp'' on the system --> COMPILATION ... ')
try
    compileFastAUC('Linux')
    fprintf('DONE\n')
catch
    fprintf('FAILED (AUC computations will be slower)\n')
end

% For Feature set 1: PET
fprintf('\n*********************** PERFORMING FEATURE SELECTION FOR ''PET'' FEATURE SET ***********************\n')
computeAllModelChoice_STS(pathWORK,fSetName{1},outcome,freedomSEP,maxOrder,nBoot)

% For Feature set 2: PET, T1, T2FS
fprintf('\n*********************** PERFORMING FEATURE SELECTION  FOR ''SEPARATE'' FEATURE SET ***********************\n')
computeAllModelChoice_STS(pathWORK,fSetName{2},outcome,freedomSEP,maxOrder,nBoot)

% For Feature set 3: PET_T1, PET_T2FS
fprintf('\n*********************** PERFORMING FEATURE SELECTION  FOR ''FUSED'' FEATURE SET ***********************\n')
computeAllModelChoice_STS(pathWORK,fSetName{3},outcome,freedomFUS,maxOrder,nBoot)

% 7. PERFORM PREDICTION PERFORMANCE ESTIMATION
mkdir('RESULTS'), fprintf('\n')

% For Feature set 1: PET
fprintf('\n*********************** PERFORMING PREDICTION PERFORMANCE ESTIMATION FOR ''PET'' FEATURE SET ***********************\n')
computeAllPrediction_STS(pathWORK,fSetName{1},outcome,freedomSEP,maxOrder,nBoot)

% For Feature set 2: PET, T1, T2FS
fprintf('\n*********************** PERFORMING PREDICTION PERFORMANCE ESTIMATION FOR ''SEPARATE'' FEATURE SET ***********************\n')
computeAllPrediction_STS(pathWORK,fSetName{2},outcome,freedomSEP,maxOrder,nBoot)

% For Feature set 3: PET_T1, PET_T2FS
fprintf('\n*********************** PERFORMING PREDICTION PERFORMANCE ESTIMATION FOR ''FUSED'' FEATURE SET ***********************\n')
computeAllPrediction_STS(pathWORK,fSetName{3},outcome,freedomFUS,maxOrder,nBoot)

% Finding the best combinations of model order and texture extraction parameter degree of freedom for all feature set types
groupExperiments_STS(pathWORK,fSetName{1},freedomSEP,maxOrder)
groupExperiments_STS(pathWORK,fSetName{2},freedomSEP,maxOrder)
groupExperiments_STS(pathWORK,fSetName{3},freedomFUS,maxOrder)

% 8. CHOICE OF BEST PARSIMONIOUS MODEL (requires user input)
fprintf('\n')
plotPredictionResults_STS([pathWORK,'/RESULTS'],fSetName,{'AUC632','Sensitivity632','Specificity632'},maxOrder)
while 1
    set = input(['\nWhich feature set provides the best parsimonious model? \n' ...
                 '--> For the ',fSetName{1},' feature set, type ''1'' and press ENTER \n' ...
                 '--> For the ',fSetName{2},' feature set, type ''2'' and press ENTER \n' ...
                 '--> For the ',fSetName{3},' feature set, type ''3'' and press ENTER \n' ...
                 'ANSWER: ']);
    fprintf('\n')
    if isnumeric(set) && (set == 1 || set == 2 || set == 3)
        break
    end
end
while 1
    order = input(['Which model order of the ',fSetName{set},' feature set provides the best parsimonious model? \n' ...
                   '--> Type a number between 1 to ',num2str(maxOrder),' and press ENTER \n' ...
                   'ANSWER: ']);
    fprintf('\n')
    if isnumeric(order) && order <= 10 && order >= 1
        break
    end
end
cd([pathWORK,'/RESULTS'])
results = load(['RESULTS_',fSetName{set},'_BEST']); results = struct2cell(results); results = results{1};
finalModel = results.(['Order',num2str(order)]);
cd(pathWORK), mkdir('FINAL_MODEL'), cd('FINAL_MODEL'), save('finalModel', 'finalModel')

% 9. COMPUTING THE LOGISTIC REGRESSION COEFFICIENTS AND BOOTSTRAP CONFIDENCE INTERVALS OF THE FINAL MODEL
fprintf('\n\nCOMPUTING THE LOGISTIC REGRESSION COEFFICIENTS OF THE FINAL MODEL ... ')
tic
[coeff,response,modelCI] = computeModelCoefficients(finalModel.Data,outcome,'IABR');
save('coeff','coeff'), save('response','response'), save('modelCI','modelCI')
fprintf('DONE\n')
toc

% 10. DISPLAYING THE FINAL MODEL AND CORRESPONDING PREDICTION PERFORMANCE ESTIMATION
plotSigmoidalResponse(response,outcome,modelCI,'LungMets')
fprintf(['\n\n\n --> THE FINAL MULTIVARIABLE MODEL IS:\n\n'...
         '               g(x) =               \n'])
for i = 1:order
    fprintf([num2str(coeff(i)),' X ',finalModel.Name{i},'\n'])
    fprintf('                    +               \n')
end
fprintf(['                   ',num2str(coeff(end)),'\n'])
fprintf('\nWITH CORRESPONDING PREDICTION PERFORMANCE ESTIMATION:\n')
fprintf(['AUC = ',num2str(roundsd(finalModel.AUC632,ceil(log10(finalModel.AUC632/roundsd(finalModel.SE_AUC632,1))))),' ± ',num2str(roundsd(finalModel.SE_AUC632,1)),'\n'])
fprintf(['Sensitivity = ',num2str(roundsd(finalModel.Sensitivity632,ceil(log10(finalModel.Sensitivity632/roundsd(finalModel.SE_Sensitivity632,1))))),' ± ',num2str(roundsd(finalModel.SE_Sensitivity632,1)),'\n'])
fprintf(['Specificity = ',num2str(roundsd(finalModel.Specificity632,ceil(log10(finalModel.Specificity632/roundsd(finalModel.SE_Specificity632,1))))),' ± ',num2str(roundsd(finalModel.SE_Specificity632,1)),'\n'])
fprintf('\n')
cd(pathWORK)


clear all; close all; clc

global VAR; %#ok<NUSED>
load_dir = GetOutputDataDir();
load(fullfile(load_dir,'VAR_new.mat'),'VAR'); % stores all clustering indices

%% Init load
hfig = figure;
InitializeAppData(hfig);
ResetDisplayParams(hfig);

%% batch
totaltime = tic; %#ok<NASGU>
isFullData = 1;
data_masterdir = GetCurrentDataDir();

M_stimrange = GetStimRange();

range_fish = [12:15,17:18];

%% custom params here:
% numK1 = 20;
masterthres = 0.7;

%%
M_regthres = {0.7,0.5};
M_place = {1,2,3,1,4,5,6,7};
M_stimname = {'4x4','PT','OMR','defS','Spt','DF','Lm','Dot'};
%%
for i_count = 1,%%%%%%%%%
    masterthres = M_regthres{i_count};
%     clusParams = struct('merge',masterthres,'cap',masterthres,'reg1',masterthres,...
%         'reg2',masterthres,'minSize',10,'k1',numK1);

    for i_stimrange = 1:7
        if i_stimrange == 1,
            M_stimrange = GetStimRange('5');
        elseif i_stimrange == 2,
            M_stimrange = GetStimRange('P');
        elseif i_stimrange == 3,
            M_stimrange = GetStimRange('O');
        elseif i_stimrange == 4,
            M_stimrange = GetStimRange('M');
        elseif i_stimrange == 5,
            M_stimrange = GetStimRange('S');
        elseif i_stimrange == 6,
            M_stimrange = GetStimRange('D');
        elseif i_stimrange == 7,
            M_stimrange = GetStimRange('L');
%         elseif i_stimrange == 8,
%             M_stimrange = GetStimRange('Y');
        end

        for i = 1:length(range_fish),
            i_fish = range_fish(i);
            disp(i_fish);

            % check this loop
            stimrange = M_stimrange{i_fish};
            if isempty(stimrange),
                continue;
            end

            % Load fish
            LoadFullFish(hfig,i_fish,isFullData);

            %% 1.
            % setup
            absIX = getappdata(hfig,'absIX');

            i_ClusGroup = 2;
            i_Cluster = 1;

            % Load cluster data
            [cIX_load,gIX] = LoadCluster_Direct(i_fish,i_ClusGroup,i_Cluster,absIX);

            %% partitions for CV
            timelists = getappdata(hfig,'timelists');
            timelists_names = getappdata(hfig,'timelists_names');
            periods = getappdata(hfig,'periods');

            M_stim = M_stimrange{i_fish};

            timelistsCV_raw = cell(length(M_stim),2);
            timelistsCV = cell(1,2);

            for k_stim = 1:length(M_stim), % :3
                i_stim = M_stim(k_stim);
                TL = timelists{i_stim};
                seq = randperm(size(TL,2));
                halfpoint = floor(size(TL,2)/2);
                timelistsCV_raw{k_stim,1} = TL(seq(1:halfpoint));
                timelistsCV_raw{k_stim,2} = TL(seq(1+halfpoint:2*halfpoint));
%                 period = periods(i_stim);
%                 nrep = size(TL,2)/periods(i_stim); % integer
%                 n = floor(nrep/2);
%                 if n>0,
%                     timelistsCV_raw{k_stim,1} = TL(1:n*period);
%                     timelistsCV_raw{k_stim,2} = TL(1+n*period:2*n*period);% before 12/5/16: TL(1+n*period):TL(2*n*period);
%                 else % for spont, only one period
%                     halfperiod = floor(period/2);
%                     timelistsCV_raw{k_stim,1} = TL(1:halfperiod);
%                     timelistsCV_raw{k_stim,2} = TL(1+halfperiod:2*halfperiod);
%                 end
            end
            timelistsCV{1} = horzcat(timelistsCV_raw{:,1});
            timelistsCV{2} = horzcat(timelistsCV_raw{:,2});
            assert(length(timelistsCV{1})==length(timelistsCV{2}));

            %%
            for k = 1:2,% CV halves
                tIX = timelistsCV{k};
                M = GetTimeIndexedData_Default_Direct(hfig,cIX_load,tIX);
                M_0 = GetTimeIndexedData_Default_Direct(hfig,[],tIX,'isAllCells');

                % ------custom code here---------
                isWkmeans = true;
                isMakeFoxels = true;

                [cIX,gIX] = AutoClustering(cIX_load,gIX,M_0,cIX_load,isWkmeans,[],...
                    isMakeFoxels,masterthres);

                % save cluster
                name = ['Auto_',M_stimname{i_stimrange},'_M',num2str(masterthres),'_CV',num2str(k)];
                clusgroupID = 15+k; % 16 and 17 %13+k; % 14 and 15
                clusIDoverride = M_place{i_stimrange};
                SaveCluster_Direct(cIX,gIX,absIX,i_fish,name,clusgroupID,clusIDoverride);
            end
        end
    end
end
SaveVARwithBackup();
totaltime = toc;
disp(totaltime);