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 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.backproject   = 'yes' or 'no',  determines when reducerank is applied whether the
%                       lower rank leadfield is projected back onto the original linear
%                       subspace, or not (default = 'yes')
%
% 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

% 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, 'forbidden',  {'channels'}); % prevent accidental typos, see issue 1729
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', 'all');   % for comp input
cfg.frequency       = ft_getopt(cfg, 'frequency');          % for freq input
cfg.latency         = ft_getopt(cfg, 'latency', 'all');     % for timelock 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

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'});

% determine data type
iscomp = ft_datatype(data, 'comp');           % it can also be raw+comp, timelock+comp or freq+comp
isfreq = ft_datatype(data, 'freq');           % it might also be freq+comp, in that case it should be treated as component data
istimelock = ft_datatype(data, 'timelock');   % it might also be timelock+comp, in that case it should be treated as component data

% the default for this depends on the data type
if ~isfield(cfg, 'model')
  if iscomp
    % each component is fitted independently
    cfg.model = 'moving';
  elseif isfreq
    % fit the data with a dipole at one location
    cfg.model = 'regional';
  elseif istimelock
    % 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_INVERSE_DIPOLEFIT 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_INVERSE_DIPOLEFIT 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_INVERSE_DIPOLEFIT 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 iscomp
  % transform the data into a representation on which the timelocked dipole fit can perform its trick
  data = comp2timelock(cfg, data);

  % default component selection is all components
  if ischar(cfg.component) && strcmp(cfg.component, 'all')
    cfg.component = (1:size(data.avg, 2));
  end
elseif isfreq
  % transform the data into a representation on which the timelocked dipole fit can perform its trick
  data = freq2timelock(cfg, data);
elseif istimelock
  % no transformation is needed
end

% collect and preprocess the electrodes/gradiometer and head model
% this will also update cfg.channel to match the electrodes/gradiometers
[headmodel, sens, cfg] = prepare_headmodel(cfg, data);

% construct the low-level options for the leadfield computation as key-value pairs, these are passed to FT_COMPUTE_LEADFIELD and FT_INVERSE_DIPOLEFIT
leadfieldopt = {};
leadfieldopt = ft_setopt(leadfieldopt, 'reducerank',     ft_getopt(cfg, 'reducerank'));
leadfieldopt = ft_setopt(leadfieldopt, 'backproject',    ft_getopt(cfg, 'backproject'));
leadfieldopt = ft_setopt(leadfieldopt, 'normalize',      ft_getopt(cfg, 'normalize'));
leadfieldopt = ft_setopt(leadfieldopt, 'normalizeparam', ft_getopt(cfg, 'normalizeparam'));
leadfieldopt = ft_setopt(leadfieldopt, 'weight',         ft_getopt(cfg, 'weight'));

% construct the low-level options for the dipole fitting as key-value pairs, these are passed to FT_INVERSE_DIPOLEFIT
dipfitopt = ft_cfg2keyval(cfg.dipfit);

% select the desired channels, ordered according to the sensor structure or configuration
[selcfg, seldata] = match_str(cfg.channel, 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(:, :);
elseif istimelock
  % 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);
ft_info('selected %d channels\n', nchans);
ft_info('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

  if isfield(cfg.sourcemodel, 'leadfield')
    ft_notice('using precomputed leadfields for the gridsearch');

    sourcemodel = keepfields(cfg.sourcemodel, {'pos', 'tri', 'dim', 'inside', 'leadfield', 'leadfielddimord', 'label'});

    % select the channels corresponding to the data and the user configuration
    tmpcfg = keepfields(cfg, 'channel');
    sourcemodel = ft_selectdata(tmpcfg, sourcemodel);

    % sort the channels to be consistent with the data
    [dum, chansel] = match_str(data.label, sourcemodel.label);
    sourcemodel.label = sourcemodel.label(chansel);
    for i=1:numel(sourcemodel.leadfield)
      if ~isempty(sourcemodel.leadfield{i})
        sourcemodel.leadfield{i} = sourcemodel.leadfield{i}(chansel, :);
      end
    end

    % ensure that the channels are consistent with the data
    assert(isequal(sourcemodel.label, cfg.channel), 'cannot match the channels in the sourcemodel to those in the data')

  else
    ft_notice('computing the leadfields for the gridsearch on the fly');

    % construct the dipole positions on which the source reconstruction will be done
    tmpcfg           = keepfields(cfg, {'sourcemodel', 'mri', 'headshape', 'symmetry', 'smooth', 'threshold', 'spheremesh', 'inwardshift', 'xgrid' 'ygrid', 'zgrid', 'resolution', 'tight', 'warpmni', 'template', 'showcallinfo', 'trackcallinfo', 'trackusage', 'trackdatainfo', 'trackmeminfo', 'tracktimeinfo', 'checksize'});
    tmpcfg.headmodel = headmodel;
    if ft_senstype(sens, 'eeg')
      tmpcfg.elec = sens;
    elseif ft_senstype(sens, 'meg')
      tmpcfg.grad = sens;
    end
    sourcemodel = ft_prepare_sourcemodel(tmpcfg);

  end % if precomputed leadfield or not

  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
        ft_info('found minimum after scanning on grid point [%g %g %g]\n', dip.pos(1), dip.pos(2), dip.pos(3));
      elseif cfg.numdipoles==2
        ft_info('found minimum after scanning on grid point [%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

    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
          ft_info('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
          ft_info('found minimum after scanning for topography %d on grid point [%g %g %g; %g %g %g]\n', t, dip(t).pos(1,1), dip(t).pos(1,2), dip(t).pos(1,3), dip(t).pos(2,1), dip(t).pos(2,2), dip(t).pos(2,3));
        end
      end

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

elseif strcmp(cfg.gridsearch, 'no')
  % there is no grid needed for dipole scanning
  sourcemodel = [];
  % use the initial guess supplied in the configuration for the remainder
  switch cfg.model
    case 'regional'
      dip = cfg.dip;
    case 'moving'
      for t=1:ntime
        dip(t) = cfg.dip;
      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

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 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 = ft_inverse_dipolefit(dip, sens, headmodel, Vdata, dipfitopt{:}, leadfieldopt{:});
        success = 1;
        if cfg.numdipoles==1
          ft_info('found minimum after non-linear optimization on [%g %g %g]\n', dip.pos(1), dip.pos(2), dip.pos(3));
        elseif cfg.numdipoles==2
          ft_info('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) = ft_inverse_dipolefit(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) = ft_inverse_dipolefit(dipin(t), sens, headmodel, Vdata(:,t), dipfitopt{:}, leadfieldopt{:});
          success(t) = 1;
          if cfg.numdipoles==1
            ft_info('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
            ft_info('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)
  if isfield(headmodel, 'unit')
    source.dip(i).unit = headmodel.unit;
  elseif isfield(sourcemodel, 'unit')
    source.dip(i).unit = sourcemodel.unit;
  end
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
elseif istimelock
  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 previous   data
ft_postamble provenance source
ft_postamble history    source
ft_postamble savevar    source


function by = cvx_getlog( bx )

% WARNING: This assumes that there is exactly one non-zero element per
% column, that all non-zeros are positive, and that any element appearing
% in rows greater than 1 has a non-zero entry in cvx___.logarithm.

global cvx___
nb = size( bx, 2 );
[ rx, cx, vx ] = find( bx );
tt = rx > 1;
ry = rx(tt);
cy = cx(tt);
logs = cvx___.logarithm( ry, 1 );
nl = max([1,max(logs)]);
by = sparse( logs, cy, 1, nl, nb ) + sparse( 1, cx, log(vx), nl, nb );

% Copyright 2005-2014 CVX Research, Inc.
% See the file LICENSE.tx for full copyright information.
% The command 'cvx_where' will show where this file is located.


function TooltipString=DoTellMeInfo(var)

Units = [];
Tips = '';
switch var

    %% Imaging Tab
    case 'BandWidth'
        Units ='Hz';
        Tips ='Full receiver bandwidth';
    case 'FOVFreq'
        Units ='m';
        Tips ='Field of view in the frequency encoding direction';
    case 'FOVPhase'
        Units ='m';
        Tips ='Field of view in the first phase encoding direction';
    case 'FlipAng'
        Units ='Degree';
        Tips ='Nominal flip angle of excitation pulse';
    case 'FreqDir'
        Tips ='Frequency encoding direction';
    case 'ResFreq'
        Tips ='Number of voxels in frequency encoding direction';
    case 'ResPhase'
        Tips ='Number of voxels in the first phase encoding direction';
    case 'ScanPlane'
        Tips ='The scanning plane';
    case 'SliceNum'
        Tips ='The number of encoding slice';
    case 'SliceThick'
        Units ='m';
        Tips ='The thickness of one slice';
    case 'TE'
        Units ='s';
        Tips ='The time of echo';
    case 'TEPerTR'
        Tips ='The number of echoes in multiple echo mode, using a number greater than one requires ''MultiEcho'' tab to be loaded';
    case 'TR'
        Units ='s';
        Tips ='The time of repetition';
        %% Advanced Tab
    case 'MasterTxCoil'
        Tips ='The master transmitting coil ID in multi RF transmitting mode';
    case 'MultiTransmit'
        Tips ='The flag for turning on and off multi RF transmitting mode, default mode is ''off'' for single RF transmitting';
    case 'NEX'
        Tips ='The number of excitation';
    case 'NoFreqAlias'
        Tips ='The flag for avoiding aliasing in frequency encoding direction, default ''on'' truncates object outside field of view in frequency encoding direction';
    case 'NoPhaseAlias'
        Tips ='The flag for avoiding aliasing in the first phase encoding direction, default ''on'' truncates object outside field of view in the first phase encoding direction';
    case 'NoSliceAlias'
        Tips ='The flag for avoiding aliasing in the second phase encoding (i.e. slice encoding) direction, default ''on'' truncates object outside field of view in slice encoding direction';
    case 'Shim'
        Tips ='Main static field shimming';
    case 'TEAnchor'
        Tips ='The flag for choosing TE time offset regarding the excitation RF pulse';
        %% Hardware Tab
    case 'B0'
        Units ='T';
        Tips ='Main static magnetic field strength';
    case 'B1Level'
        Units ='T';
        Tips ='A linear scale factor for B1. The input B1+ field with magnitude of this number produces nominal flip angle';
    case 'E1Level'
        Units ='T';
        Tips ='A linear scale factor for E1. When calculating spatial SAR, the input E1+ field is scaled by a factor of nominal RF amplitude divided by this number';
    case 'MaxGrad'
        Units ='T/m';
        Tips ='Maximum allowable gradient strength';
    case 'MaxSlewRate'
        Units ='T/m/s';
        Tips ='Maximum allowable gradient slew rate';
    case 'MinUpdRate'
        Units ='s';
        Tips ='Minimum update time on generating sequence waveform';
    case 'Model'
        Tips ='Model type';
    case 'NoiseLevel'
        Tips ='The level of adjustable noise, the higher the number, the more noise';
    case 'PulseType'
        Tips ='The type of generated sequence pulse';
    case 'SpinPerVoxel'
        Tips ='The number of spins in each voxel. Default one spin per voxel treats T2* equal to T2, use a number greater than one to simulate T2* effect based on T2Star input (linear simulation time cost)';
        %% Recon Tab
    case 'AutoRecon'
        Tips ='The flag for turning on and off automatic image reconstruction after MR signal acquisition';
    case 'ExternalEng'
        Tips ='User defined script for image reconstruction';
    case 'OutputType'
        Tips ='The type of output data including both simulated image and signal';
    case 'ReconEng'
        Tips ='The image reconstruction engine, choosing ''External'' uses external engine which requires ''ExternalEng'' to be provided';
    case 'ReconType'
        Tips ='The type of image reconstruction';
        %% CV Tab
    case 'CV1'
        Tips ='Controllable variable 1';
    case 'CV2'
        Tips ='Controllable variable 2';
    case 'CV3'
        Tips ='Controllable variable 3';
    case 'CV4'
        Tips ='Controllable variable 4';
    case 'CV5'
        Tips ='Controllable variable 5';
    case 'CV6'
        Tips ='Controllable variable 6';
    case 'CV7'
        Tips ='Controllable variable 7';
    case 'CV8'
        Tips ='Controllable variable 8';
    case 'CV9'
        Tips ='Controllable variable 9';
    case 'CV10'
        Tips ='Controllable variable 10';
    case 'CV11'
        Tips ='Controllable variable 11';
    case 'CV12'
        Tips ='Controllable variable 12';
    case 'CV13'
        Tips ='Controllable variable 13';
    case 'CV14'
        Tips ='Controllable variable 14';
        %% SpecialTech Tab
    case 'GRAPPA'
        Tips ='GRAPPA';
    case 'GM'
        Tips ='Generalized Multi-pool exchanging simulation';
    case 'FSE'
        Tips ='Fast Spin Echo';
    case 'EPI'
        Tips ='Echo Planar Imaging';
    case 'DummyPulse'
        Tips ='Dummy pulse';
    case 'CEST'
        Tips ='Chemical Exchange Saturation Transfer simulation';
    case 'PartialEcho'
        Tips ='Partial echo';
    case 'MultiEcho'
        Tips ='Multi echo';
    case 'MT'
        Tips ='Magnetization Transfer simulation';
    case 'ME'
        Tips ='Multiple pool spin Exchange simulation';
    case 'IRPrep'
        Tips ='Inversion recovery preparation';
    case 'T2Prep'
        Tips ='T2 decay preparation';
    case 'Gridding'
        Tips ='Non-Cartesian gridding';
    case 'Spiral'
        Tips ='Spiral imaging';
    case 'SENSE'
        Tips ='SENSE';
    case 'Radial'
        Tips ='Radial imaging';
    case 'RTRecon'
        Tips ='Real Time reconstruction';
    case 'ZeroFilling'
        Tips ='Zero filling k-space';
    case 'VIPR'
        Tips ='VIPR';
    case 'DP_Flag'
        Tips ='The flag for turning on and off dummy pulse';
    case 'DP_FlipAng'
        Units ='Degree';
        Tips ='The flip angle of excitation pulse for dummy pulse';
    case 'DP_Num'
        Tips ='The number of TRs for dummy pulse';
    case 'DP_TR'
        Units ='s';
        Tips ='The time of repetition for dummy pulse';
    case 'EPI_ESP'
        Units ='s';
        Tips ='The echo spacing for EPI';
    case 'EPI_ETL'
        Tips ='The echo train length for EPI';
    case 'EPI_EchoShifting'
        Tips ='The flag for turning on and off echo shifting';
    case 'EPI_ShotNum'
        Tips ='The number of EPI shots, multi shot EPI uses interleave mode';
    case 'FSE_ESP'
        Units ='s';
        Tips ='The echo spacing for FSE';
    case 'FSE_ETL'
        Tips ='The echo train length for FSE';
    case 'FSE_ShotNum'
        Tips ='The number of FSE shots, multi shot FSE uses interleave mode';
    case 'G_Deapodization'
        Tips ='The flag for turning on and off kernel deapodization (i.e. dividing reconstructed image with the iFFT of the gridding kernel)';
    case 'G_KernelSample'
        Tips ='The number of kernel sample point, the more sample points, the better kernel approximation';
    case 'G_KernelWidth'
        Tips ='The full width of kernel in the unit of gridding grid';
    case 'G_OverGrid'
        Tips ='The over gridding factor';
    case 'G_Truncation'
        Tips ='The flag for turning on and off image truncation for reconstructed image';
    case 'TI'
        Units ='s';
        Tips ='The time of inversion recovery';
    case 'MT_Flag'
        Tips ='The flag for turning on and off Magnetization Transfer simulation';
    case 'ME_Flag'
        Tips ='The flag for turning on and off Multiple pool spin Exchange simulation';
    case 'CEST_Flag'
        Tips ='The flag for turning on and off Chemical Exchange Saturation Transfer simulation';
    case 'GM_Flag'
        Tips ='The flag for turning on and off Generalized Multi-pool exchanging simulation';
    case 'RTR_Flag'
        Tips ='The flag for turning on and off real time reconstruction';
    case 'PlotK_Flag'
        Tips ='The flag for turning on and off real time k-space plotting';
    case 'DelayTime'
        Tips ='The delay time for refreshing graphics';
    case 'ME_TEs'
        Units ='s';
        Tips ='An array of multiple echo values';
    case 'R_AngPattern'
        Tips ='The pattern for sampling the angle in k-space';
    case 'R_AngRange'
        Tips ='The range of sampling angle';
    case 'R_SampPerSpoke'
        Tips ='The number of sampling points in each spoke';
    case 'R_SpokeNum'
        Tips ='The number of sampling spokes';
    case 'Sh_X'
        Tips ='The constant for X term';
    case 'Sh_Y'
        Tips ='The constant for Y term';
    case 'Sh_Z'
        Tips ='The constant for Z term';
    case 'Sh_ZX'
        Tips ='The constant for ZX term';
    case 'Sh_ZY'
        Tips ='The constant for ZY term';
    case 'Sh_Z2'
        Tips ='The constant for Z^2 term';
    case 'Sh_XYZ'
        Tips ='The constant for XYZ term';
    case 'Sh_X2_Y2'
        Tips ='The constant for (X^2)(Y^2) term';
    case 'S_ShotNum'
        Tips ='The number of spiral interleaves';
    case 'S_GradientEff'
        Tips ='A linear scale factor for adjusting maximum allowable gradient in spiral design';
    case 'S_F1'
        Tips ='A scale factor for varying FOV with k-space radius r, as FOV(r) = F0 + F1*r + F2*r*r (in variable density spiral design)';
    case 'S_F2'
        Tips ='A scale factor for varying FOV with k-space radius r, as FOV(r) = F0 + F1*r + F2*r*r (in variable density spiral design)';
    case 'tT2Prep'
        Units ='s';
        Tips ='The time of T2 decay preparation';
    case 'ZF_Kz'
        Tips ='The zero filling factor in Kz';
    case 'ZF_Ky'
        Tips ='The number of point in Ky after zero filling';
    case 'ZF_Kx'
        Tips ='The number of point in Kx after zero filling';
    case 'ChemShift'
        Units ='Hz/T';
        Tips ='The chemical shift of the spin';
    case 'Gyro'
        Units ='rad/s/T';
        Tips ='The gyromagnetic ratio of the spin';
    case 'Rho'
        Tips ='The spin density of the spin';
    case 'T1'
        Units ='s';
        Tips ='The longitudinal relaxation time';
    case 'T2'
        Units ='s';
        Tips ='The transverse relaxation time';
    case 'TypeNum'
        Tips ='The number of spin species';
    case 'ZCenter'
        Tips ='The index of the centeral spin in Z direction';
    case 'ZSpin'
        Tips ='The number of the spins in Z direction';
    case 'ZSpinGap'
        Units ='m';
        Tips ='The distance between adjacent spins in Z direction';
    case 'XCenter'
        Tips ='The index of the centeral spin in X direction';
    case 'XSpin'
        Tips ='The number of the spins in X direction';
    case 'XSpinGap'
        Units ='m';
        Tips ='The distance between adjacent spins in X direction';
    case 'Spat_Flag'
        Tips ='The flag to turn on and off 2D spatial RF analysis';
    case 'YCenter'
        Tips ='The index of the centeral spin in Y direction';
    case 'YSpin'
        Tips ='The number of the spins in Y direction';
    case 'YSpinGap'
        Units ='m';
        Tips ='The distance between adjacent spins in Y direction';
    case 'FreqRes'
        Tips ='The number of linear frequency sample points';
    case 'FreqUpLimit'
        Units ='Hz';
        Tips ='The upper limit of frequency range';
    case 'FreqDownLimit'
        Units ='Hz';
        Tips ='The lower limit of frequency range';
    case 'Freq_Flag'
        Tips ='The flag to turn on and off Spatial-Spectral RF analysis';
    case 'ConstantGrad'
        Units ='T/m';
        Tips ='The constant gradient applied when gradient tab is empty';
    case 'dB0'
        Units ='T';
        Tips ='The main static magnetic field offset';
        %% Pulse Waveform
    case 'tS'
        Units ='s';
        Tips ='The starting time point in TR section, any waveform timing in this pulse group is relative to this time point';
    case 'tE'
        Units ='s';
        Tips ='The ending time point in TR section, any waveform timing in this pulse group will be truncated after this time point';
    case 'TRStart'
        Tips ='The starting TR number';
    case 'TREnd'
        Tips ='The ending TR number';
    case 'Freq'
        Tips ='The occurrence frequency (e.g. 1 means occurring every TR section, 5 means occurring every 5 TR sections)';
    case 'Moments'
        Tips ='The flag for turning on and off the zeroth moment display for the gradient';
    case 'LineMarker'
        Tips ='The flag for turning on and off waveform line marker';
    case 'RenderMode'
        Tips ='The k-space rendering mode';
    case 'RenderPoint'
        Tips ='The flag for turning on and off k-space point rendering';
    case 'Tar'
        Tips ='The flag for turning on and off sequence deployment for Toppe';
    case 'PlayToppeMovie'
        Tips ='The flag for turning on and off Toppe movie playback';
    case 'NumTRSkip'
        Tips ='The number of TR sections to skip during Toppe movie playback';
    case 'PlayPulseqMovie'
        Tips ='The flag for turning on and off Pulseq movie playback';
    case 'ShowUnit'
        Tips ='The Pulseq display time unit';
    case 'ShowNum'
        Tips ='The number of time sections to show during Pulseq movie playback';
    case 'Apod'
        Tips ='Apodization methods for RF pulse';
    case 'FA'
        Units ='Degree';
        Tips ='Prescribed flip angle';
    case 'TBP'
        Tips ='The time bandwidth product of RF pulse';
    case 'dt'
        Units ='s';
        Tips ='The time interval of sample points';
    case 'rfPhase'
        Units ='rad';
        Tips ='RF pulse phase';
    case 'rfFreq'
        Units ='Hz';
        Tips ='RF pulse frequency offset';
    case 'tStart'
        Units ='s';
        Tips ='The starting time';
    case 'tEnd'
        Units ='s';
        Tips ='The ending time';
    case 'Switch'
        Tips ='The flag for turning on and off this pulse';
    case 'AnchorTE'
        Tips ='The flag for turning on and off TE reference, TE is calculated from this RF pulse if this flag is turned on';
    case 'Duplicates'
        Tips ='The number of the pulse duplicates, used for creating multiple pulses with the same shape';
    case 'DupSpacing'
        Units ='s';
        Tips ='The time spacing between pulse duplicates';
    case 'CoilID'
        Tips ='The ID of the coil element';
    case 'Notes'
        Tips ='The notes of this object';
    case 'PW'
        Tips ='The measure of the pulse width in Fermi RF pulse';
    case 'SLRPulseType'
        Tips ='The type of this SLR pulse, including ''st''(small tip angle pulse), ''ex''(excitation pulse), ''se''(spin-echo pulse), ''sat''(saturation pulse) and ''inv''(inversion pulse)';
    case 'FilterType'
        Tips ='The type of the applied filter design method, including ''ls''(least squares), ''min''(minimum phase), ''max''(maximum phase), ''pm''(Parks-McClellan equal ripple), and ''ms''(Hamming windowed sinc)';
    case 'PRipple'
        Tips ='The ripple factor at passband';
    case 'SRipple'
        Tips ='The ripple factor at stopband';
    case 'Adiab'
        Tips ='The adiabatic factor';
    case 'MaxB1'
        Units ='T';
        Tips ='The maximum B1 field';
    case 'MaxFreq'
        Units ='Hz';
        Tips ='The maximum RF frequency';
    case 'Lambda'
        Tips ='The lambda adiabatic factor';
    case 'Beta'
        Tips ='The beta adiabatic factor';
    case 'BIRFlag'
        Tips ='The type of BIR pulse, including ''BIR-1'', ''BIR-2'' and ''BIR-4''';
    case 'BIREFFlag'
        Tips ='The type of BIREF pulse, including ''BIREF-1'', ''BIREF-2a'' and ''BIREF-2b''';
    case 'rfGain'
        Tips ='The standard deviation of the normal distribution';
    case 'rfFile'
        Tips ='The path to the file that stores the RF pulse data, quoted using single quotes';
    case 't2Start'
        Units ='s';
        Tips ='The second gradient pulse starting time';
    case 't2End'
        Units ='s';
        Tips ='The second gradient pulse ending time';
    case 'tRamp'
        Units ='s';
        Tips ='The pulse ramp time from zero to plateau, assume symmetric ramp on both side';
    case 'GzAmp'
        Units ='T';
        Tips ='The amplitude of the Gz pulse';
    case 'Gz1Sign'
        Tips ='The polarity of the first gradient pulse, set 0 for nulling';
    case 'Gz2Sign'
        Tips ='The polarity of the second gradient pulse, set 0 for nulling';
    case 'Gz3Sign'
        Tips ='The polarity of the last gradient pulse, set 0 for nulling';
    case 'sRamp'
        Tips ='The sample points on the ramp, use the value of 2 for ignoring the area under the ramp, use values greater than 2 for counting the ramp area';
    case 'Area'
        Units ='1/m';
        Tips ='The area under this gradient pulse';
    case 'nCycles'
        Tips ='The number of cycles of phase across the pixel size';
    case 't1Start'
        Units ='s';
        Tips ='The first gradient pulse starting time';
    case 't1End'
        Units ='s';
        Tips ='The first gradient pulse ending time';
    case 'GzFile'
        Tips ='The path to the file that stores the Gz pulse data, quoted using single quotes';
    case 'GyFile'
        Tips ='The path to the file that stores the Gy pulse data, quoted using single quotes';
    case 'GxFile'
        Tips ='The path to the file that stores the Gx pulse data, quoted using single quotes';
    case 'ADCFile'
        Tips ='The path to the file that stores the ADC pulse data, quoted using single quotes';
    case 'GyAmp'
        Units ='T';
        Tips ='The amplitude of the Gy pulse';
    case 'Gy1Sign'
        Tips ='The polarity of the first gradient pulse, set 0 for nulling';
    case 'Gy2Sign'
        Tips ='The polarity of the second gradient pulse, set 0 for nulling';
    case 'Gy3Sign'
        Tips ='The polarity of the last gradient pulse, set 0 for nulling';
    case 't2Middle'
        Units ='s';
        Tips ='The second encoding gradient pulse middle time';
    case 't3Start'
        Units ='s';
        Tips ='The last gradient pulse starting time';
    case 'tMiddle'
        Units ='s';
        Tips ='The middle time of the pulse';
    case 'tOffset'
        Units ='s';
        Tips ='The time offset of the gradient pulse';
    case 'tGy1'
        Units ='s';
        Tips ='The duration of the first gradient pulse';
    case 'tGy2'
        Units ='s';
        Tips ='The duration of the second gradient pulse';
    case 'GxAmp'
        Units ='T';
        Tips ='The amplitude of the Gx pulse';
    case 'Gx1Sign'
        Tips ='The polarity of the first gradient pulse, set 0 for nulling';
    case 'Gx2Sign'
        Tips ='The polarity of the second gradient pulse, set 0 for nulling';
    case 'Gx3Sign'
        Tips ='The polarity of the last gradient pulse, set 0 for nulling';
    case 'sSample'
        Tips ='The number of linear sample points when ADC flag is 1';
    case 'Ext'
        Tips ='The Ext flag';
    case 'isVardens'
        Tips ='The flag for turning on and off variable density spiral design';
    case 'InOut'
        Tips ='Spiral in or out';
        %% Others
    case 'Name'
        Tips ='The name of the structure';
    case 'Type'
        Tips ='A description about the phantom type';
    case 'XDim'
        Tips ='The number of voxels in X direction';
    case 'YDim'
        Tips ='The number of voxels in Y direction';
    case 'ZDim'
        Tips ='The number of voxels in Z direction';
    case 'XDimRes'
        Units ='m';
        Tips ='The spatial resolution in X direction';
    case 'YDimRes'
        Units ='m';
        Tips ='The spatial resolution in Y direction';
    case 'ZDimRes'
        Units ='m';
        Tips ='The spatial resolution in Z direction';
    case 'Grid'
        Tips ='Turn on and off grid';
    case 'Box'
        Tips ='Turn on and off boundary box';
    case 'CameraTool'
        Tips ='Hide or show Matlab camera tool';
    case 'Color'
        Tips ='The display color';
    case 'Alpha'
        Tips ='The display transparency';
    case 'Radius'
        Units ='m';
        Tips ='The radius of the object';
    case 'CenterX'
        Units ='m';
        Tips ='The X coordinate of the object center';
    case 'CenterY'
        Units ='m';
        Tips ='The Y coordinate of the object center';
    case 'CenterZ'
        Units ='m';
        Tips ='The Z coordinate of the object center';
    case 'FaceNum'
        Tips ='The number of the faces for the object';
    case 'TypeIdx'
        Tips ='An index number of the spin species, used when the phantom has multiple spin species. The index must not exceed the ''TypeNum''';
    case 'TypeFlag'
        Tips ='A flag number for describing the type of the spin, 0 for free pool and 1 for bound pool';
    case 'LineShapeFlag'
        Tips ='A flag number for describing RF saturation line shape for bound proton pool, 0 for super-Lorentzian and 1 for Gaussian (:ToDo), the flag is ignored for free pool';
    case 'ECon'
        Units ='S/m';
        Tips ='A array with size of [1 3] for tissue electrical conductivity (optional)';
    case 'MassDen'
        Units ='kg/m^3';
        Tips ='The tissue mass density (optional)';
    case 'T2Star'
        Units ='s';
        Tips ='The T2* relaxation time';
    case 'K'
        Units ='1/s';
        Tips ='A array with the size of [1 TypeNum] for describing the exchange rate of the spin, ignored for regular phantom';
    case 'RadiusX'
        Units ='m';
        Tips ='The X semi-axis length of the ellipsoid';
    case 'RadiusY'
        Units ='m';
        Tips ='The Y semi-axis length of the ellipsoid';
    case 'RadiusZ'
        Units ='m';
        Tips ='The Z semi-axis length of the ellipsoid';
    case 'Length'
        Units ='m';
        Tips ='The length of the object';
    case 'Height'
        Units ='m';
        Tips ='The height of the pyramid';
    case 'Colormap'
        Tips ='The colormap for the field';
    case 'CLimDown'
        Tips ='The lower bound of color limits';
    case 'CLimUp'
        Tips ='The upper bound of color limits';
    case 'CoilDisplay'
        Tips ='The flag for turning on and off coil display';
    case 'CoilShow'
        Tips ='The flag for choosing active coil for field display';
    case 'Mode'
        Tips ='The B1 field display mode';
    case 'FieldType'
        Tips ='The flag to choose B1 field or E1 field, note E1 field only support ''Magnitude'' display mode';
    case 'Plane'
        Tips ='The flag for activating field slicing plane';
    case 'Azimuth'
        Units ='rad';
        Tips ='The azimuth angle of the plane';
    case 'Elevation'
        Units ='rad';
        Tips ='The elevation angle of the plane';
    case 'PosZ'
        Units ='m';
        Tips ='The Z position of object center';
    case 'PosY'
        Units ='m';
        Tips ='The Y position of object center';
    case 'PosX'
        Units ='m';
        Tips ='The X position of object center';
    case 'CurrentDir'
        Tips ='The current direction in the coil circle, 1 for clockwise, -1 for counterclockwise';
    case 'Scale'
        Tips ='The scale factor for the field amplitude';
    case 'Segment'
        Tips ='The number of line segments for approximating circle, MRiLab requires the same ''Segment'' for each coil circle';
    case 'Width'
        Units ='m';
        Tips ='The width of the object';
    case 'B1File'
        Tips ='The path to the file that stores the B1 field data, quoted using single quotes';
    case 'E1File'
        Tips ='The path to the file that stores the E1 field data, quoted using single quotes';
    case 'Interp'
        Tips ='The interpolation method';
    case 'GradZ'
        Tips ='The linear gradient in Z direction';
    case 'GradY'
        Tips ='The linear gradient in Y direction';
    case 'GradX'
        Tips ='The linear gradient in X direction';
    case 'DeltaZ'
        Units ='m';
        Tips ='The width of Gaussian function in Z direction';
    case 'DeltaY'
        Units ='m';
        Tips ='The width of Gaussian function in Y direction';
    case 'DeltaX'
        Units ='m';
        Tips ='The width of Gaussian function in X direction';
    case 'Equation'
        Tips ='A field described with a symbolic equation';
    case 'MagFile'
        Tips ='The path to the file that stores the dB0 field data, quoted using single quotes';
    case 'GradLine'
        Tips ='The gradient sequence line';
    case 'DispMode'
        Tips ='The display mode';
    case 'GradZEqu'
        Tips ='A symbolic equation for gradient field vector in Z direction';
    case 'GradYEqu'
        Tips ='A symbolic equation for gradient field vector in Y direction';
    case 'GradXEqu'
        Tips ='A symbolic equation for gradient field vector in X direction';
    case 'GradFile'
        Tips ='The path to the file that stores the gradient field data, quoted using single quotes';
    case 'Object'
        Tips ='The object model, currently only supports ''Sphere''';
    case 'ViewPoint'
        Tips ='A default view point';
    case 'ZoomOut'
        Tips ='A factor of view zoom out';
    case 'Sample'
        Tips ='The sample steps between two adjacent positions during movement';
    case 'Repeat'
        Tips ='The repeat time of playback';
    case 'Direction'
        Tips ='A vector describing translation direction in 3D space';
    case 'Displacement'
        Units ='m';
        Tips ='An equation of translation displacement pattern with respect to time';
    case 'Axis'
        Tips ='A vector describing rotation axis in 3D space';
    case 'Angle'
        Units ='rad';
        Tips ='An equation of rotation angle with respect to time';
    case 'LocZ'
        Tips ='The Z location of the selected voxel';
    case 'LocY'
        Tips ='The Y location of the selected voxel';
    case 'LocX'
        Tips ='The X location of the selected voxel';
    case 'WindowSize'
        Tips ='The window width of the spin evolution plot';
    case 'ISOHighlight'
        Tips ='The flag for turning on and off isocenter mark';
    case 'Axes'
        Tips ='The flag for turning on and off axes label';
    case 'N_Gram'
        Units ='g';
        Tips ='The number to specify averaged N-gram SAR, set to 0 indicating unaveraged spatial SAR';
    case 'N_Second'
        Units ='s';
        Tips ='The nominal time window for SAR calculation';
end

if ~isempty(Units)
    TooltipString = [var '(' Units '): ' Tips];
else
    TooltipString = [var ': ' Tips];
end

end


function Y = vl_nnreshape(X,dest_size,dzdy)
% VL_NNRESHAPE CNN reshapes input to des_size
%
%    DEST_SIZE: desired size
%
%    DZDX = VL_NNRESHAPE(X, DEST_SIZE, DZDY) computes the derivative DZDX
%    of the CNN with respect to the input X given the derivative DZDY
%    with respect to the block output Y. DZDX has the same dimension
%    as X.

% Copyright (C) 2015 Tuan-Hung VU.
% All rights reserved.
%
% This file is made available under the terms of the BSD license (see the COPYING file).

if nargin <= 2
    sz = [size(X,1) size(X,2) size(X,3) size(X,4)] ;
    Y = reshape(X, [size(X,1) size(X,2) dest_size(2) size(X,3)*size(X,4)/dest_size(2)]);
else
    Y = reshape(dzdy, size(X));
end


classdef StressVoigt2TensorConverterPS < SecondOrderVoigt2TensorConverterPS

    properties
    end

    methods (Access = public)

        function obj = StressVoigt2TensorConverterPS(tensor)
           obj.computeConversion(tensor)
        end

    end

    methods (Access = protected)

        function factor = computeVoigtFactor(obj)
            factor = 1;
        end

        function selectTensorClass(obj)
            obj.tensor = StressPlaneStressTensor();
        end
    end

end


% writelocs() - write a file containing channel location, type and gain information
%
% Usage:
%   >> writelocs( chanstruct, filename );
%   >> writelocs( chanstruct, filename, 'key', 'val' );
%
% Inputs:
%   chanstruct - EEG.chanlocs data structure returned by readlocs() containing
%                channel location, type and gain information.
%   filename   - File name for saving channel location, type and gain information
%
% Optional inputs:
%   'filetype'  - ['loc'|'sph'|'sfp'|'xyz'|'polhemus'|'besa'|'chanedit'|'custom']
%                 Type of the file to write. By default the file type is indicated
%                 by the file extension.
%                  'loc' - An EEGLAB 2-D polar coordinates channel locations file
%                          Coordinates are theta and radius (see definitions below).
%                  'sph' - A Matlab spherical coordinates file (Note: spherical
%                          coordinates used by Matlab functions are different
%                          from spherical coordinates used in BESA - see below).
%                  'sfp' - EGI cartesian coordinates (not Matlab cartesian - see below).
%                  'xyz' - MATLAB/EEGLAB cartesian coordinates (Not EGI cartesian;
%                          z is toward nose; y is toward left ear; z is toward vertex).
%                  'polhemus' or 'polhemusx' - Polhemus electrode location file recorded with
%                          'X' on sensor pointing to subject (see below and readelp()).
%                  'polhemusy' - Polhemus electrode location file recorded with
%                          'Y' on sensor pointing to subject (see below and readelp()).
%                  'besa' - BESA'(.elp') spherical coordinate file. (Not MATLAB spherical
%                           - see below).
%                  'chanedit' - EEGLAB channel location files created by pop_chanedit().
%                  'custom' - Ascii files with columns in user-defined 'format' (see below).
%   'format'    - [cell array] Format of a 'custom' channel location file (see above).
%                          Default if no file type is defined. The cell array contains
%                          labels defining the meaning of each column of the input file.
%                           'channum'   [positive integer] channel number
%                           'labels'    [string] channel name (no spaces)
%                           'theta'     [real degrees] 2-D angle in polar coordinates.
%                                       positive => rotating from nose (0) toward left ear
%                           'radius'    [real] radius in 2-D polar coords (0.5 is disk limits)
%                           'X'         [real] Matlab-cartesian X coordinate (to nose)
%                           'Y'         [real] Matlab-cartesian Y coordinate (to left ear)
%                           'Z'         [real] Matlab-cartesian Z coordinate (to vertex)
%                           '-X','-Y','-Z' Matlab-cartesian coordinates pointing away from above
%                           'sph_theta' [real degrees] Matlab spherical horizontal angle.
%                                       positive => rotating from nose (0) toward left ear.
%                           'sph_phi'   [real degrees] Matlab spherical elevation angle;
%                                       positive => rotating from horizontal (0) upwards.
%                           'sph_radius' [real] distance from head center (unused)
%                           'sph_phi_besa' [real degrees] BESA phi angle from vertical.
%                                       positive => rotating from vertex (0) towards right ear.
%                           'sph_theta_besa' [real degrees] BESA theta horiz/azimuthal angle.
%                                       positive => rotating from right ear (0) toward nose.
%     The input file may also contain other channel information fields
%                           'type'      channel type: 'EEG', 'MEG', 'EMG', 'ECG', others ...
%                           'calib'     [real near 1.0] channel calibration value.
%                           'gain'      [real > 1] channel gain.
%                           'custom1'   custom field #1.
%                           'custom2', 'custom3', 'custom4' more custom fields.
%   'unicoord'     - ['on'|'off'] Uniformize all coordinates. Default 'on'.
%   'header'       - ['on'|'off'] Add a header comment line with the name of each column.
%                             Comment lines begin with '%'. Default is 'off'.
%   'customheader' - [string] Add a custom header at the beginning of the file and
%                             preceded by '%'.  If used with 'header' set to 'on',
%                             the column names will be insterted after the custom header.
%   'elecind' - [integer array] Indices of channels to export.
%                             Default is all channels.
%
% Note: for file formats, see readlocs() help  (>> help readlocs)
%
% Author: Arnaud Delorme, Salk Institute, 16 Dec 2002
%
% See also: readlocs(), readelp()

% Copyright (C) Arnaud Delorme, CNL / Salk Institute, 28 Feb 2002
%
% This program 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 2 of the License, or
% (at your option) any later version.
%
% This program 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 program; if not, write to the Free Software
% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

function writelocs( chans, filename, varargin );

if nargin < 2
	help writelocs;
	return;
end;

% get infos from readlocs
% -----------------------
%[listtype formatinfo listcolformat formatskip] = readlocs('getinfos');
[chanformat listcolformat] = readlocs('getinfos');
indformat  = [];
for index = 1:length(chanformat),
    if ~isstr(chanformat(index).importformat)
        indformat = [ indformat index ];
    end;
    if isempty(chanformat(index).skipline), chanformat(index).skipline = 0; end;
end;
listtype   = { chanformat(indformat).type };
formatinfo = { chanformat(indformat).importformat };
formatskip = [ chanformat(indformat).skipline ];

g = finputcheck( varargin, ...
                 { 'filetype'	  'string'	 listtype 			'loc';
                   'header'       'string'   { 'on' 'off' } 	'off';
                   'customheader' 'string'   [] 					'';
                   'elecind'      'integer'  [1 Inf]				[];
                   'unicoord'     'string'   { 'on' 'off' } 	'on';
                   'format'		  'cell'	 []					{} }, 'writelocs');
if isstr(g), error(g); end;

if strcmpi(g.unicoord, 'on')
    disp('Uniformizing coordinates');
    chans = convertlocs(chans, 'auto', 'verbose', 'off');
end;

% select channels
% ---------------
if ~isempty(g.elecind)
	chans = chans(g.elecind);
end;

% finding types of input
% ----------------------
if isempty(g.format)
   indexformat = strmatch(lower(g.filetype), listtype, 'exact');
   g.format = formatinfo{indexformat};
   g.skipline = formatskip(indexformat);
else
   g.skipline = 0;
end;

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

% exporting header
% ----------------
if ~isempty(g.customheader)
    allstrs = cellstr(g.customheader);
    for index=1:length(allstrs)
        fprintf(fid, '%s\n', allstrs{index});
    end;
end;
if  strcmpi(g.header, 'on') | g.skipline == 2
   for index=1:length(g.format)
      fprintf(fid, '%8s\t', g.format{index});
   end;
   fprintf(fid, '\n');
   for index=1:length(g.format)
      fprintf(fid, '%8s\t', char(ones(1,8)*45));
   end;
   fprintf(fid, '\n');
end;
if g.skipline == 1
   fprintf(fid, '%d\n', length(chans));
end;

% writing infos
% -------------
for indexchan = 1:length(chans)
   for index=1:length(g.format)
      [str, mult] = checkformat(g.format{index});
      if strcmpi(str, 'channum')
         fprintf(fid, '%d', indexchan);
      else
         if ~isfield(chans, str)
            error([ 'Non-existant field: ''' str '''' ]);
         end;
         eval( [ 'chanval = chans(indexchan).' str ';' ] );
         if   isstr(chanval), fprintf(fid, '%8s', chanval);
         else
             if abs(mult*chanval) > 1E-10
                 fprintf(fid, '%8s', num2str(mult*chanval,5));
             else
                 fprintf(fid, '%8s', '0');
             end;
         end;
      end;
      if index ~= length(g.format)
         fprintf(fid, '\t');
      end;
   end;
   fprintf(fid, '\n');
end;
fclose(fid);

return;

% check field format
% ------------------
function [str, mult] = checkformat(str)
	mult = 1;
	if strcmpi(str, 'labels'), str = lower(str); return; end;
	if strcmpi(str, 'channum'), str = lower(str); return; end;
	if strcmpi(str, 'theta'), str = lower(str); return; end;
	if strcmpi(str, 'radius'), str = lower(str); return; end;
	if strcmpi(str, 'sph_theta'), str = lower(str); return; end;
	if strcmpi(str, 'sph_phi'), str = lower(str); return; end;
	if strcmpi(str, 'sph_radius'), str = lower(str); return; end;
	if strcmpi(str, 'sph_theta_besa'), str = lower(str); return; end;
	if strcmpi(str, 'sph_phi_besa'), str = lower(str); return; end;
	if strcmpi(str, 'gain'), str = lower(str); return; end;
	if strcmpi(str, 'calib'), str = lower(str); return; end;
	if strcmpi(str, 'type') , str = lower(str); return; end;
	if strcmpi(str, 'X'), str = upper(str); return; end;
	if strcmpi(str, 'Y'), str = upper(str); return; end;
	if strcmpi(str, 'Z'), str = upper(str); return; end;
	if strcmpi(str, '-X'), str = upper(str(2:end)); mult = -1; return; end;
	if strcmpi(str, '-Y'), str = upper(str(2:end)); mult = -1; return; end;
	if strcmpi(str, '-Z'), str = upper(str(2:end)); mult = -1; return; end;
	if strcmpi(str, 'custum1'), return; end;
	if strcmpi(str, 'custum2'), return; end;
	if strcmpi(str, 'custum3'), return; end;
	if strcmpi(str, 'custum4'), return; end;
   error(['writelocs: undefined field ''' str '''']);


function z = rsum(fit)
%
% function to extract log-likelihood and degrees-of-freedom
% from locfit fit.
%
% order of returned vector:   df0 df1 llk.
%

fp = fit.fit_points;
gf = fp.kappa;

z = gf([2 3 1]);

return;


function [roiImg, imgXform, bb] = dtiRoiToImg(coords, imgXform, bb)
%
% [roiImg, imgXform, bb] = dtiRoiToImg(roi, [imgXform=eye(4)], [bb])
%
% If not the identity matrix, imgXform is typically set to xformToAcpc. This
% has the effect of forcing the roiImg to have the same voxel size as the
% image that xformToAcpc is based on.
%
% bb is the bounding box, defined in the xformed spaced. Defaults to
% [min(coords)-10; max(coords)+10].
%
% [roiImg, imgXform, bb] = dtiRoiToImg(roi);
% % Do some processing on the ROI
% % perimImg = bwperim(roiImg);
% perimRoi = dtiRoiFromImg(roiImg, imgXform, bb);
%
% HISTORY:
% 2009.08.19 RFD wrote it.

if(isstruct(coords))
    coords = coords.coords;
end

if(~exist('bb','var')||isempty(bb))
    bb = [min(coords)-10; max(coords)+10];
    if(~exist('imgXform','var')||isempty(imgXform))
        imgXform = eye(4);
        imgXform(1:3,4) = bb(1,:)'-1;
    end
end
if(~exist('imgXform','var')||isempty(imgXform))
    imgXform = eye(4);
end

sz = abs(diff(ceil(mrAnatXformCoords(inv(imgXform), bb))))+1;
roiImg = false((sz));

% Remove coords outside the bounding box
badCoords = coords(:,1)<bb(1,1) | coords(:,1)>bb(2,1) ...
          | coords(:,2)<bb(1,2) | coords(:,2)>bb(2,2) ...
          | coords(:,3)<bb(1,3) | coords(:,3)>bb(2,3);
coords = coords(~badCoords,:);

%coords(:,1) = coords(:,1) - bb(1,1) + 1;
%coords(:,2) = coords(:,2) - bb(1,2) + 1;
%coords(:,3) = coords(:,3) - bb(1,3) + 1;
coords = round(mrAnatXformCoords(inv(imgXform), coords));
roiImg(sub2ind(size(roiImg), coords(:,1), coords(:,2), coords(:,3))) = true;

return;


function [p] = registerjacobian(p)
%REGISTERJACOBIAN Register jacobians to an expression

if ~isfield(p.extra, 'jacobian')
    x = recover(depends(p));
    p.extra.jacobian = jacobian(p,x);
end


%% Copyright (C) 2017, 2019 Colin B. Macdonald
%%
%% This file is part of OctSymPy.
%%
%% OctSymPy 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 software 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 software; see the file COPYING.
%% If not, see <http://www.gnu.org/licenses/>.

%% -*- texinfo -*-
%% @deftypefun {@var{y} =} double_to_sym_exact (@var{x})
%% Convert a double value to the equivalent rational sym
%%
%% Private helper function.
%%
%% @end deftypefun

function y = double_to_sym_exact (x)
  if (isnan (x))
    y = pycall_sympy__ ('return S.NaN');
  elseif (isinf (x) && x < 0)
    y = pycall_sympy__ ('return -S.Infinity');
  elseif (isinf (x))
    y = pycall_sympy__ ('return S.Infinity');
  else
    %% Rational will exactly convert from a float
    y = pycall_sympy__ ('return Rational(_ins[0])', x);
  end
end


function model = ivmAddPoint(model, i)

% IVMADDPOINT Add a point into the IVM representation.
% FORMAT
% DESC incorporates the ith point from the data set into the IVM
% model.
% ARG model : the model to which the point is to be added.
% ARG index : the index of the point in the training data which is
% to be added.
% RETURN model : the returned model with the point added in.
%
% SEEALSO : ivmUpdateSites, ivmUpdateM, ivmUpdateNuG, ivmSelectPoint,
% ivmRemovePoint, ivmCreate
%
% COPYRIGHT : Neil D. Lawrence, 2004, 2005

% IVM

index = find(model.J == i);
if isempty(index)
  error(['Point ' num2str(i) ' is not in inactive set'])
end

%/~model = ivmUpdateNuG(model, i);
%~/
model = ivmUpdateSites(model, i);
model = ivmUpdateM(model, i);

% Remove point from the non-active set and place in the active.
model.J(index) = [];
model.I = [model.I; i];

model = ivmUpdateNuG(model, model.J);


function [volume, sagSize, numSlices, calc, dataRange] = mrLoadUnfVol()
%
%  [volume, sagSize, numSlices, calc, dataRange] = mrLoadUnfVol()
%
% AUTHOR:  Engel, Boynton, Wandell
%
% Loads in anatomy in mrUnfold format.
%
% TODO:
%   Returns vSize, not sagSize,numSlices
%   Separate out the loading of anatomy and gray matter
%   This routine should be called through the preferences at
%    start up, and it should run only once for each scan
%   The information obtained here goes with a subject's brain.
%    So, this routine should probably just take a subject's identifier
%    as input and go to the right directory and get the relevant
%    information all at once instead of bothering me.  That directory
%    should contain all the relevant information about the anatomies.

% Ask user for the volume anatomy data file
%
volumeDataFile = input('Enter volume anatomy data file: ','s');

% Convert the volume anatomy into the vector format
%
[volume vSize] = readVolume(volumeDataFile);

volume = createVolumeVector(volume');
sagSize = [ vSize(1) vSize(2)];
numSlices = vSize(3);

% This loads the locations of the gray matter within
% the anatomical volume.  We are not currently using gray-matter,
% though we will.  This should be around in a separate module.
%
%grayDataFile = input('Enter gray matter data file: ','s');
%[calc vSize] = readVolume(grayDataFile);
%calc = replaceValue(calc,2,1);
%calc = createVolumeVector(calc');
calc = [];

% This is probably unnecessary ... we would like to make this
% variable go away.
%
dataRange = [1,numSlices];


function planC = createStapleStruct(structNumV,probCutoff,structureName,planC)
% function planC = createStapleStruct(structNumV,probCutoff,structureName,planC)
%
% Function to create STAPLE agreement structure at the passed probability
% probCutoff.
%
% APA, 11/13/2017

if ~exist('planC','var')
    global planC
end
indexS = planC{end};

scanNum = getStructureAssociatedScan(structNumV(1),planC);
numObs = length(structNumV);
siz = getUniformScanSize(planC{indexS.scan}(scanNum));
rateMatM = false(prod(siz),numObs);
for i=1:numObs
    mask3M = getUniformStr(structNumV(i),planC);
    rateMatM(:,i) = mask3M(:);
end
indV = sum(rateMatM,2) > 0;
rateMatM = rateMatM(indV,:);

iterlim=100;
senstart=0.9999*ones(1,numObs);
specstart=0.9999*ones(1,numObs);
%[stapleV, sen, spec, Sall] = staple(rateMatM,iterlim, single(senstart), single(specstart));
[stapleV, sen, spec, Sall] = gpuStaple(rateMatM,iterlim, single(senstart), single(specstart));

staple3M = zeros(siz);
staple3M(indV) = stapleV;
newStrMask3M = staple3M >= probCutoff;

planC = maskToCERRStructure(newStrMask3M, 1, scanNum, structureName, planC);


% Astar
% A* navigation class
%
% A concrete subclass of the Navigation class that implements the A*
% navigation algorithm. Methods included are for the standard case,
% multiobjective optimization (MOO) -- i.e. optimizes over several
% objectives/criteria -- and the A*-PO algorithms for MOO that utilizes
% Pareto optimality.
%
% Methods:
% 	plan            Compute the cost map given a goal and map
% 	path            Compute a path to the goal
% 	visualize       Display the obstacle map (deprecated)
% 	plot            Display the obstacle map
% 	costmap_modify 	Modify the costmap
% 	costmap_get     Return the current costmap
% 	costmap_set     Set the current costmap
% 	display         Print the parameters in human readable form
% 	char            Convert to string
%
% Properties:
% TBD
%
% Example 1::
%        load map1              % load map
%        goal = [50;30];
%        start=[20;10];
%        as = Astar(map);       % create Navigation object
%        as.plan(goal,2,3,0);   % setup costmap for specified goal;
%                               % standard D* algorithm w/ 2 objectives
%                               % and 3 costmap layers
%        as.path(start);        % plan solution path start-to-goal, animate
%        P = as.path(start);    % plan solution path start-to-goal, return
%                               % path
% Example 2::
%        goal = [100;100];
%        start = [1;1];
%        as = Astar(0);          % create Navigation object with pseudo-
%                                % random occupancy grid
%        ds.addCost(terrain);    % terrain is a 100x100 matrix of
%                                % elevations [0,1]
% 	     ds.plan(goal,3,4,0);    % setup costmap for specified goal
%                                % (3 and 4 include the added terrain cost)
%        as.path(start);         % plan solution path start-goal, animate
%        P = as.path(start);     % plan solution path start-goal, return
%                                % path
%
% Notes
% - Obstacles are represented by Inf in the costmap.
%
% References
% - A Pareto Optimal D* Search Algorithm for Multiobjective Path Planning,
%   A. Lavin.
% - A Pareto Front-Based Multiobjective Path Planning Algorithm, A. Lavin.
% - Robotics, Vision & Control, Sec 5.2.2, Peter Corke, Springer, 2011.
%
% See Also Navigation, Dstar

% Copyright (C) 1993-2017, by Peter I. Corke
%
% This file is part of The Robotics Toolbox for MATLAB (RTB).
%
% RTB is free software: you can redistribute it and/or modify
% it under the terms of the GNU Lesser General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% RTB 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 Lesser General Public License for more details.
%
% You should have received a copy of the GNU Leser General Public License
% along with RTB.  If not, see <http://www.gnu.org/licenses/>.
%
% http://www.petercorke.com

% Implementation notes:
%
% X is an index into the array of states.
% State pointers are kept as matlab array index rather than row,col format.

classdef Astar < Navigation

    properties (SetAccess=private, GetAccess=private)

        % essential world info
        costmap   % cost layers (1st is map)
        G         % index of goal point
        N         % number of objectives
        L         % number of cost layers

        % info kept per cell (state):
        b         % backpointer (0 means not set)
        t         % tag: NEW/OPEN/CLOSED

        algorithm % A*, A*-MOO, or A*-PO
        tie
        openlist  % priority queue with states and their costs
        niter
        changed
        openlist_maxlen
        quiet     % specifies verbosity

        % tag state values
        NEW = 0;
        OPEN = 1;
        CLOSED = 2;
    end

    methods  % start of public methods

        function as = Astar(world, varargin)
            %Astar.Astar A* constructor
            %
            % AS = Astar(MAP, OPTIONS) is a A* navigation object, and MAP
            % is an occupancy grid, a representation of a planar world as
            % a matrix whose elements are 0 (free space) or 1 (occupied).
            % The occupancy grid is coverted to a costmap with a unit cost
            % for traversing a cell.
            %
            % Options::
            % 'world' = 0   will call for a pseudo-random occupancy grid
            % 'goal',G      Specify the goal point (2x1)
            % 'metric',M    Specify the distance metric as 'Euclidean'
            %               (default) or 'cityblock'
            % 'inflate',K   Inflate all obstacles by K cells
            % 'quiet'       Don't display the progress spinner
            %
            % Other options are supported by the Navigation superclass.
            %
            % See also Navigation.Navigation.

            % Invoke the superclass constructor
            as = as@Navigation(world, varargin{:});  % includes the occgrid

            % Options
            opt.quiet = false;
            opt = tb_optparse(opt, varargin);
            as.quiet = opt.quiet;

            as.occgrid2costmap(as.occgrid);

            % Initialize A* state variables
            as.reset();
            if ~isempty(as.goal)
                as.goal_change();
            end
            as.changed = false;
        end

        function reset(as)
            %Astar.reset Reset the planner
            %
            % AS.reset() resets the A* planner.  The next instantiation
            % of AS.plan() will perform a global replan.

            % Build the matrices required to hold the state of each cell
            as.b = zeros(size(as.occgrid), 'uint32');     % backpointers
            as.t = zeros(size(as.occgrid), 'uint8');      % tags, all NEW=0
            as.costmap(:,:,2) = zeros(size(as.occgrid));  % path cost g
            as.costmap(:,:,3) = zeros(size(as.occgrid));  % path cost h

            % Priority queue has col for each open state, one row for the
            % state location, and rows for each cost layer
            as.openlist = zeros(as.L+1,0);
            as.openlist_maxlen = -Inf;
        end

        function goal_change(as)
            %Astar.goal_change Changes the costlayers due to new goal
            %position
            if isempty(as.b)
                return;
            end
            goal = as.goal;

            % Keep goal in index rather than row,col format
            as.G = sub2ind(size(as.occgrid), goal(2), goal(1));
            as.INSERT(as.G, as.projectCost(as.G), 'goalset');
            as.costmap(goal(2),goal(1),2) = 0;

            % If new goal modifies costs for a layer, recalculate here
            as.calcHeuristic(as.occgrid, as.goal);
        end

        function s = char(as)
            %Astar.char Convert Navigation object to string
            %
            % AS.char() is a string representing the state of the Astar
            % object in human-readable form.
            %
            % See also Astar.display, Navigation.char.

            % Work is done by the superclass
            s = char@Navigation(as);
        end

        function plot(as, varargin)
            %Astar.plot Visualize navigation environment
            %
            % AS.plot() displays the occupancy grid and the goal distance
            % in a new figure.  The goal distance is shown by intensity
            % which increases with distance from the goal.  Obstacles are
            % overlaid and shown in red.
            %
            % AS.plot(P) as above but also overlays a path given by the set
            % of points P (Mx2).
            %
            % See also Navigation.plot.

            plot@Navigation(as, 'distance', as.costmap(:,:,3), varargin{:});
        end

        function n = next(as, current)
            % Invoked by Navigation.step
            % Backpropagate from goal to start
            % Return [col;row] of previous step
            if as.changed
                error('Cost map has changed, replan');
            end
            X = sub2ind(size(as.occgrid), current(2), current(1));
            % Set X as the backpointer of X
            X = as.b(X);
            if X == 0
                % Goal (no further backpointer)
                n = [];
            else
                [r,c] = ind2sub(size(as.occgrid), X);
                n = [c;r];
            end
        end

        function plan(as, goal, N, layers, algorithm)
            %Astar.plan Prep the grid for planning.
            %
            % AS.plan() updates AS with a costmap of distance to the
            % goal from every non-obstacle point in the map.  The goal is
            % as specified to the constructor.
            %
            % Inputs:
            %   goal: goal state coordinates
            %   N: number of optimization objectives; standard A* is 2
            %   (i.e. distance and heuristic)
            %   layers: number of cost layers in costmap
            %   algorithm: specify standard A*(0), A*-MOO (1), A*-PO (2)

            % Setup parameters
            if nargin < 3
                N = 2;
            end
            if nargin < 4
                layers = 3;
            end
            if nargin < 5
                algorithm = 0;
            end
            as.N = N;
            as.L = layers;
            as.algorithm = algorithm;
            as.openlist = zeros(as.L+1,0);

            % Initialize cost layers
            for a = 2:as.L
                as.costmap(:,:,a) = zeros(size(as.occgrid));
            end

            % Cost priority/tiebreaker: layer 2 (distance to node)
            as.tie = 2;

            % Set goal
            if nargin > 1
                as.goal = goal;  % invokes superclass method set.goal()
            end
            if isempty(as.goal)
                error('must specify a goal point');
            end

            % Populate heuristic cost layer
            as.calcHeuristic(as.occgrid, as.goal);
        end

        function P = path(as, start)
        %Astar.path Find a path between two points
        %
        % AS.path(START) finds and displays a path from START to GOAL
        % which is overlaid on the occupancy grid.
        %
        % P = AS.path(START) returns the path (2xM) from START to GOAL.
            if nargin < 1
                error('must specify start state');
            end

            % Invoke the superclass path function, which iterates on our
            % next method
%             start = [start; 1];  % specifies backpropogation for NAV.path()
%             temp = start;
%             start = as.goal;
%             as.goal = temp;

            if nargout == 0
                path@Navigation(as, start);
            else
                P = path@Navigation(as, start);
            end
        end

        % Handler invoked by Navigation.path() to start the navigation
        % process -- calculate the solution path.
        % Line comments Ln reference A* pseudocode in Lavin's "A Pareto
        % Front-Based Multiobjective Path Planning Algorithm" where n is
        % the line number.
        function navigate_init(as, start)
            as.openlist = zeros(as.L+1,0);  % openlist must be empty
            % Begin search with the start node
            start = sub2ind(size(as.occgrid), start(2), start(1));
            as.openlist(1,1) = start;
            as.t(start) = as.OPEN;

            % Plan the A* path
            as.niter = 0; flag = 0;
            while ~isempty(as.openlist)                              % L4
                % Normalize costs on the open list, choose expansion state
                queue = normc(as.openlist(2:size(as.openlist,1),:)');
                if as.algorithm == 2
                    % Get Pareto optimal point off the open list
                    front = as.openlist(:,paretofront(queue));
                    [~,col] = min(front(as.tie+1,:));
                    X = front(1,col);                                % L5
                else
                    [~,ind]=min(sum(queue,2));
                    X = as.openlist(1,ind);                          % L5
                end
                    as.DELETE(X);                                    % L6

                as.niter = as.niter + 1;
                if ~as.quiet && mod(as.niter, 20) == 0
                    as.spinner();
                end

                % Populate the openlist
                for Y=as.neighbors(X)                                % L7,8
                    if(Y==as.G)                                      % L9
                        as.b(Y) = X;
                        as.updateCosts(Y,X,as.N)
                        flag = 1;  % flag for goal
                        break;
                    end
                    if as.t(Y)==as.NEW && as.costmap(Y)~=Inf
                        as.b(Y) = X;
                        as.updateCosts(Y,X,as.N);
                        % Project node's costs into objective space:
                        objspace = as.projectCost(Y,X);
                        as.INSERT(Y, objspace, '');
                    end
                end
                if as.verbose
                    disp(' ')
                end
                if flag==1 % goal found
                    break;
                end
            end
            if ~as.quiet
                fprintf('\r');
            end
            as.changed = false;
        end

        function layer = cost_get(as, layer)
        %Astar.cost_get Get the specified cost layer
            layer = as.costmap(:,:,layer);
        end

        function c = heurstic_get(as)
        %Astar.heuristice_get Get the current heuristic map
        %
        % C = AS.heuristice_get() is the current heuristic layer. It is
        % computed in Astar.plan.
        %
        % See also Astar.plan.
            c = as.costmap(:,:,3);
        end

        function c = costmap_get(as)
        %Astar.costmap_get Get the current costmap
        %
        % C = AS.costmap_get() is the current costmap.
        % The value of each element represents the cost of traversing the
        % cell.  It is autogenerated by the class constructor from the
        % occupancy grid such that:
        % - free cell (occupancy 0) has a cost of 1
        % - occupied cell (occupancy >0) has a cost of Inf
        %
        % See also Astar.costmap_set, Astar.costmap_modify.
            c = as.costmap;
        end

        function costmap_set(as, costmap)
        %Astar.costmap_set Set the current costmap
        %
        % AS.costmap_set(C) sets the current costmap.
        % This method accepts the full costmap -- i.e. all layers.
        %
        % Notes:
        % - After the cost map is changed the path should be replanned by
        %   calling AS.plan().
        %
        % See also Astar.costmap_get, Astar.costmap_modify.
            [i,j,k] = size(costmap);
            if ~all([i,j] == size(as.occgrid))
                error('costmap must be same size as occupancy grid');
            end
            as.L = k;  % set the number of cost layers
            as.costmap = costmap;
            as.changed = true;
        end

        function costmap_modify(as, point, newcost)
        %Astar.costmap_modify Modify cost map
        %
        % AS.costmap_modify(P, NEW) modifies the cost map at P=[X,Y] to
        % have the value NEW.  If P (2xM) and NEW (1xM) then the cost of
        % the points defined by the columns of P are set to the corresponding
        % elements of NEW.
        %
        % Notes::
        % - After one or more point costs have been updated the path
        %   should be replanned by calling AS.plan().
        %
        % See also Astar.costmap_set, Astar.costmap_get.
            if (newcost < 0) || (1 < newcost)
                error('new cost value must be normlaized [0,1]')
            end

            [i,j,k] = size(as.costmap);
            if (point(1) < 0) || (point(1) > i)
                error('1st dimension of point is out of bounds')
            end
            if (point(2) < 0) || (point(2) > j)
                error('2nd dimension of point is out of bounds')
            end
            if (point(2) < 0) || (point(3) > k)
                error('3rd dimension of point is out of bounds')
            end

            as.costmap(point) = newcost;
        end

        function addCost(as, values)
        %Astar.addCost Add an additional cost layer
        %
        % AS.addCost(values) adds the matrix specified by values as a
        % cost layer.
        % Inputs
        %   values: normalized matrix the size of the environment
            [i,j,k] = size(as.costmap);

            if [i,j]~=size(as.occgrid)
                error('layer size does not match the environment')
            end
            if max(max(values))~=1 || min(min(values))~=0
                error('layer values are not normalized [0,1]')
            end

            as.costmap(:,:,k+1) = values;
        end

        function flag = backProp(as)
            flag = 1;
        end

    end  % end of public methods

    methods (Access=protected)  % start of private methods

        function occgrid2costmap(as, og, cost)
            if nargin < 3
                cost = 1;
            end
            og(og==1) = Inf;  % occupied cells -> infinite path cost
            og(og==0) = cost;  % unoccupied cells -> path cost
            as.costmap(:,:,1) = og;
        end

        function calcHeuristic(as, grid, goal)
            as.costmap(:,:,3) = zeros(size(grid));
            for ii=1:size(grid,1)
                for jj=1:size(grid,2)
                    as.costmap(ii,jj,3) = sqrt((ii-goal(1))^2+(jj-goal(2))^2);
                end
            end
        end

        function k_new = updateCosts(as, a, b, obj)
            % NOTE: Only for costs that accumulate (i.e. sum) over the
            % path, and for dynamic costs.
            % E.g. the heuristic parameter only needs updating when the
            % goal state changes; its values are stored for each cell.
            %
            % Location moving from state b to a.
            %
            % The costs are coded to be (1) distance, (2) heuristic, (3)
            % elevation, (4) solar deviation, and (5) risk. If deviating
            % from these costs (in this order) you MUST EDIT THIS METHOD.
            [i,j,~] = size(as.costmap);

            if nargout > 0
                k_new = as.costmap(i*j+b) + as.dc(b,a);
                return
            end
            if obj == 0
                % Return what the new priority cost would be (k_new)
                return
            end
            if obj > 1
                % Standard A* search
                as.costmap(i*j+a) = as.costmap(i*j+b) + as.dc(b,a);
                % (no heuristic update needed)
            end
            if obj > 2
                % W/ elevation costs
                % (no elevation update needed)
            end
            if obj > 3
                % W/ solar costs
                % Rotate the solar vector 1rad per 100 steps
                sV = [cos(as.niter/100);sin(as.niter/100)];
                as.costmap(4*i*j+a) = dot(sV,as.vc(b,a));
            end
            if obj > 4
                % W/ risk costs
                % (no risk update needed)
            end
        end

        function pt = projectCost(as, a, b)
            % Returns the projection of state a into objective space. If
            % specified, location is moving from b to a (case 3).
            [i,j,k] = size(as.costmap);
            pt(1) = as.costmap(a);
            switch nargin
                case 2
                    pt(2) = as.costmap(i*j+a);
                case 3
                    pt(2) = as.costmap(i*j+b) + as.dc(a,b);
                otherwise
                    return
            end
            for n=3:k
                pt(n) = as.costmap((n-1)*i*j+a);
            end
        end

        function INSERT(as, X, pt, where)
            % Add state X to the openlist with objective space values
            % specified by pt.

            if nargin>2
                as.message('insert (%s) %d = %f\n', where, X, pt);
            end

            i = find(as.openlist(1,:) == X);
            if length(i) > 1
                error('A*:INSERT: state in open list %d times', X);
            end

            [i,j,~] = size(as.costmap);
            if (as.t(X) == as.OPEN || as.CLOSED) && ...
               (pt(as.tie) > as.costmap((as.tie-1)*i*j+X))
                % L13/14: If a node with same position as successor is in
                % the OPEN/CLOSED list & has a lower f than successor,
                % then skip this successor.
            else
                % Add a new column to the open list for this node
                as.openlist = [as.openlist [X; pt(:)]];
            end

            % Keep track of the max length of the openlist
            if numcols(as.openlist) > as.openlist_maxlen
                as.openlist_maxlen = numcols(as.openlist);
            end

            % Tag state X as open
            as.t(X) = as.OPEN;
        end

        function DELETE(as, X)
            as.message('delete %d\n', X);
            i = find(as.openlist(1,:) == X);
            if length(i) ~= 1
                error('A*:DELETE: state %d does not exist', X);
            end

            % Remove the column, close the state
            as.openlist(:,i) = [];
            as.t(X) = as.CLOSED;
        end

        function cost = dc(as, X, Y)
            % Return the distance cost of moving from state X to state Y
            [r,c] = ind2sub(size(as.occgrid), [X; Y]);
            dist = sqrt(sum(diff([r c]).^2));
            dcost = (as.costmap(X) + as.costmap(Y))/2;

            cost = dist * dcost;
        end

        function vector = vc(as, X, Y)
            % Return the robot unit vector -- direction of moving from
            % state X to state Y
            [Xi,Xj] = ind2sub(size(as.occgrid),X);
            [Yi,Yj] = ind2sub(size(as.occgrid),Y);
            vector = [Yi-Xi;Yj-Xj];
            vector = vector/norm(vector);
        end

        function Y = neighbors(as, X)
            % Return indices of neighbor states (max 8) as a row vector
            dims = size(as.occgrid);
            [r,c] = ind2sub(dims, X);

            % Of 8-way neighbors, only use those w/in grid bounds
            Y = [r-1 r-1 r-1 r r  r+1 r+1 r+1; c-1 c c+1 c-1 c+1 c-1 c c+1];
            k = (min(Y)>0) & (Y(1,:)<=dims(1)) & (Y(2,:)<=dims(2));
            Y = Y(:,k);
            Y = sub2ind(dims, Y(1,:)', Y(2,:)')';
        end

    end  % end of private methods

end