function [stat, cfg] = statistics_wrapper(cfg, varargin)

% STATISTICS_WRAPPER performs the selection of the biological data for
% timelock, frequency or source data and sets up the design vector or
% matrix.
%
% The specific configuration options for selecting timelock, frequency
% of source data are described in FT_TIMELOCKSTATISTICS, FT_FREQSTATISTICS and
% FT_SOURCESTATISTICS, respectively.
%
% After selecting the data, control is passed on to a data-independent
% statistical subfunction that computes test statistics plus their associated
% significance probabilities and critical values under some null-hypothesis. The statistical
% subfunction that is called is FT_STATISTICS_xxx, where cfg.method='xxx'. At
% this moment, we have implemented two statistical subfunctions:
% FT_STATISTICS_ANALYTIC, which calculates analytic significance probabilities and critical
% values (exact or asymptotic), and FT_STATISTICS_MONTECARLO, which calculates
% Monte-Carlo approximations of the significance probabilities and critical values.
%
% The specific configuration options for the statistical test are
% described in FT_STATISTICS_xxx.

% This function depends on PREPARE_TIMEFREQ_DATA which has the following options:
% cfg.avgoverchan
% cfg.avgoverfreq
% cfg.avgovertime
% cfg.channel
% cfg.channelcmb
% cfg.datarepresentation (set in STATISTICS_WRAPPER cfg.datarepresentation = 'concatenated')
% cfg.frequency
% cfg.latency
% cfg.precision
% cfg.previous (set in STATISTICS_WRAPPER cfg.previous = [])
% cfg.version (id and name set in STATISTICS_WRAPPER)

% Copyright (C) 2005-2006, 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$

% check if the input cfg is valid for this function
cfg = ft_checkconfig(cfg, 'renamed',     {'approach',   'method'});
cfg = ft_checkconfig(cfg, 'required',    {'method'});
cfg = ft_checkconfig(cfg, 'forbidden',   {'transform'});

% set the defaults
cfg.latency     = ft_getopt(cfg, 'latency', 'all');
cfg.frequency   = ft_getopt(cfg, 'frequency', 'all');
cfg.roi         = ft_getopt(cfg, 'roi',       []);
cfg.avgovertime = ft_getopt(cfg, 'avgovertime', 'no');
cfg.avgoverfreq = ft_getopt(cfg, 'avgoverfreq', 'no');
cfg.avgoverroi  = ft_getopt(cfg, 'avgoverroi',  'no');

% determine the type of the input and hence the output data
if ~exist('OCTAVE_VERSION', 'builtin')
  [s, i] = dbstack;
  if length(s)>1
    [caller_path, caller_name, caller_ext] = fileparts(s(2).name);
  else
    caller_path = '';
    caller_name = '';
    caller_ext  = '';
  end
  % evalin('caller', 'mfilename') does not work for Matlab 6.1 and 6.5
  istimelock = strcmp(caller_name,'ft_timelockstatistics');
  isfreq     = strcmp(caller_name,'ft_freqstatistics');
  issource   = strcmp(caller_name,'ft_sourcestatistics');
else
  % cannot determine the calling function in Octave, try looking at the
  % data instead
  istimelock  = isfield(varargin{1},'time') && ~isfield(varargin{1},'freq') && isfield(varargin{1},'avg');
  isfreq      = isfield(varargin{1},'time') && isfield(varargin{1},'freq');
  issource    = isfield(varargin{1},'pos') || isfield(varargin{1},'transform');
end

if (istimelock+isfreq+issource)~=1
  ft_error('Could not determine the type of the input data');
end

if istimelock || isfreq,
  % these defaults only apply to channel level data
  cfg.channel     = ft_getopt(cfg, 'channel',     'all');
  cfg.avgoverchan = ft_getopt(cfg, 'avgoverchan', 'no');
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% collect the biological data (the dependent parameter)
%  and the experimental design (the independent parameter)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if issource

  % test that all source inputs have the same dimensions and are spatially aligned
  for i=2:length(varargin)
    if isfield(varargin{1}, 'dim') && (length(varargin{i}.dim)~=length(varargin{1}.dim) || ~all(varargin{i}.dim==varargin{1}.dim))
      ft_error('dimensions of the source reconstructions do not match, use FT_VOLUMENORMALISE first');
    end
    if isfield(varargin{1}, 'pos') && (length(varargin{i}.pos(:))~=length(varargin{1}.pos(:)) || ~all(varargin{i}.pos(:)==varargin{1}.pos(:)))
      ft_error('grid locations of the source reconstructions do not match, use FT_VOLUMENORMALISE first');
    end
    if isfield(varargin{1}, 'transform') && ~all(varargin{i}.transform(:)==varargin{1}.transform(:))
      ft_error('spatial coordinates of the source reconstructions do not match, use FT_VOLUMENORMALISE first');
    end
  end

  Nsource = length(varargin);
  Nvoxel  = length(varargin{1}.inside) + length(varargin{1}.outside);

  if ~isempty(cfg.roi)
    if ischar(cfg.roi)
      cfg.roi = {cfg.roi};
    end
    % the source representation should specify the position of each voxel in MNI coordinates
    x = varargin{1}.pos(:,1);  % this is from left (negative) to right (positive)
    % determine the mask to restrict the subsequent analysis
    % process each of the ROIs, and optionally also left and/or right separately
    roimask  = {};
    roilabel = {};
    for i=1:length(cfg.roi)
      if islogical(cfg.roi{i})
        tmp = cfg.roi{i};
      else
        tmpcfg.roi = cfg.roi{i};
        tmpcfg.inputcoord = cfg.inputcoord;
        tmpcfg.atlas = cfg.atlas;
        tmp = ft_volumelookup(tmpcfg, varargin{1});
      end
      if strcmp(cfg.avgoverroi, 'no') && ~isfield(cfg, 'hemisphere')
        % no reason to deal with separated left/right hemispheres
        cfg.hemisphere = 'combined';
      end

      if     strcmp(cfg.hemisphere, 'left')
        tmp(x>=0)    = 0;  % exclude the right hemisphere
        roimask{end+1}  = tmp;
        roilabel{end+1} = ['Left '  cfg.roi{i}];

      elseif strcmp(cfg.hemisphere, 'right')
        tmp(x<=0)    = 0;  % exclude the right hemisphere
        roimask{end+1}  = tmp;
        roilabel{end+1} = ['Right ' cfg.roi{i}];

      elseif strcmp(cfg.hemisphere, 'both')
        % deal separately with the voxels on the left and right side of the brain
        tmpL = tmp; tmpL(x>=0) = 0;  % exclude the right hemisphere
        tmpR = tmp; tmpR(x<=0) = 0;  % exclude the left hemisphere
        roimask{end+1}  = tmpL;
        roimask{end+1}  = tmpR;
        roilabel{end+1} = ['Left '  cfg.roi{i}];
        roilabel{end+1} = ['Right ' cfg.roi{i}];
        clear tmpL tmpR

      elseif strcmp(cfg.hemisphere, 'combined')
        % all voxels of the ROI can be combined
        roimask{end+1}  = tmp;
        if ischar(cfg.roi{i})
          roilabel{end+1} = cfg.roi{i};
        else
          roilabel{end+1} = ['ROI ' num2str(i)];
        end

      else
        ft_error('incorrect specification of cfg.hemisphere');
      end
      clear tmp
    end % for each roi

    % note that avgoverroi=yes is implemented differently at a later stage
    % avgoverroi=no is implemented using the inside/outside mask
    if strcmp(cfg.avgoverroi, 'no')
      for i=2:length(roimask)
        % combine them all in the first mask
        roimask{1} = roimask{1} | roimask{i};
      end
      roimask = roimask{1};  % only keep the combined mask
      % the source representation should have an inside and outside vector containing indices
      sel = find(~roimask);
      varargin{1}.inside  = setdiff(varargin{1}.inside, sel);
      varargin{1}.outside = union(varargin{1}.outside, sel);
      clear roimask roilabel
    end % if avgoverroi=no
  end

  % get the source parameter on which the statistic should be evaluated
  if strcmp(cfg.parameter, 'mom') && isfield(varargin{1}, 'avg') && isfield(varargin{1}.avg, 'csdlabel') && isfield(varargin{1}, 'cumtapcnt')
    [dat, cfg] = get_source_pcc_mom(cfg, varargin{:});
  elseif strcmp(cfg.parameter, 'mom') && isfield(varargin{1}, 'avg') && ~isfield(varargin{1}.avg, 'csdlabel')
    [dat, cfg] = get_source_lcmv_mom(cfg, varargin{:});
  elseif isfield(varargin{1}, 'trial')
    [dat, cfg] = get_source_trial(cfg, varargin{:});
  else
    [dat, cfg] = get_source_avg(cfg, varargin{:});
  end
  cfg.dimord = 'voxel';

  % note that avgoverroi=no is implemented differently at an earlier stage
  if strcmp(cfg.avgoverroi, 'yes')
    tmp = zeros(length(roimask), size(dat,2));
    for i=1:length(roimask)
      % the data only reflects those points that are inside the brain,
      % the atlas-based mask reflects points inside and outside the brain
      roi = roimask{i}(varargin{1}.inside);
      tmp(i,:) = mean(dat(roi,:), 1);
    end
    % replace the original data with the average over each ROI
    dat = tmp;
    clear tmp roi roimask
    % remember the ROIs
    cfg.dimord = 'roi';
  end

  % check whether the original input data contains a dim, which would allow
  % for 3D reshaping and clustering with bwlabeln
  if isfield(varargin{1}, 'transform') || ((isfield(varargin{1}, 'dim') && prod(varargin{1}.dim)==size(varargin{1}.pos,1)))
    cfg.connectivity = 'bwlabeln';
  end

elseif isfreq || istimelock
  % get the ERF/TFR data by means of PREPARE_TIMEFREQ_DATA
  cfg.datarepresentation = 'concatenated';
  [cfg, data] = prepare_timefreq_data(cfg, varargin{:});
  cfg = rmfield(cfg, 'datarepresentation');

  dim = size(data.biol);
  if length(dim)<3
    % seems to be singleton frequency and time dimension
    dim(3)=1;
    dim(4)=1;
  elseif length(dim)<4
    % seems to be singleton time dimension
    dim(4)=1;
  end
  cfg.dimord = 'chan_freq_time';

  % the dimension of the original data (excluding the replication dimension) has to be known for clustering
  cfg.dim = dim(2:end);
  % all dimensions have to be concatenated except the replication dimension and the data has to be transposed
  dat = transpose(reshape(data.biol, dim(1), prod(dim(2:end))));

  % remove to save some memory
  data.biol = [];

  % verify that neighbours are present in case needed (not needed when
  % averaging over channels)
  if ~(isfield(cfg, 'avgoverchan') && istrue(cfg.avgoverchan)) &&...
      ~isfield(cfg,'neighbours') && isfield(cfg, 'correctm') && strcmp(cfg.correctm, 'cluster')
    ft_error('You need to specify a neighbourstructure');
    %cfg.neighbours = ft_neighbourselection(cfg,varargin{1});
  end

end

% get the design from the information in cfg and data.
if ~isfield(cfg,'design')
  ft_warning('Please think about how you would create cfg.design.  Soon the call to prepare_design will be deprecated')
  cfg.design = data.design;
  [cfg] = prepare_design(cfg);
end

if size(cfg.design,2)~=size(dat,2)
  cfg.design = transpose(cfg.design);
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% compute the statistic, using the data-independent statistical subfunction
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% determine the function handle to the intermediate-level statistics function
if exist(['ft_statistics_' cfg.method], 'file')
  statmethod = str2func(['ft_statistics_' cfg.method]);
else
  ft_error('could not find the corresponding function for cfg.method="%s"\n', cfg.method);
end
fprintf('using "%s" for the statistical testing\n', func2str(statmethod));

% check that the design completely describes the data
if size(dat,2) ~= size(cfg.design,2)
  ft_error('the size of the design matrix (%d) does not match the number of observations in the data (%d)', size(cfg.design,2), size(dat,2));
end

% determine the number of output arguments
try
  % the nargout function in Matlab 6.5 and older does not work on function handles
  num = nargout(statmethod);
catch
  num = 1;
end

design = cfg.design;
cfg    = rmfield(cfg,'design'); % to not confuse lower level functions with both cfg.design and design input

% perform the statistical test
if strcmp(func2str(statmethod),'ft_statistics_montecarlo') % because ft_statistics_montecarlo (or to be precise, clusterstat) requires to know whether it is getting source data,
                                                        % the following (ugly) work around is necessary
  if num>1
    [stat, cfg] = statmethod(cfg, dat, design);
  else
    [stat] = statmethod(cfg, dat, design);
  end
else
  if num>1
    [stat, cfg] = statmethod(cfg, dat, design);
  else
    [stat] = statmethod(cfg, dat, design);
  end
end

if isstruct(stat)
  % the statistical output contains multiple elements, e.g. F-value, beta-weights and probability
  statfield = fieldnames(stat);
else
  % only the probability was returned as a single matrix, reformat into a structure
  dum = stat; stat = []; % this prevents a Matlab warning that appears from release 7.0.4 onwards
  stat.prob = dum;
  statfield = fieldnames(stat);
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% add descriptive information to the output and rehape into the input format
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if issource
   if ~isfield(varargin{1},'dim') % FIX ME; this is added temporarily (20110427) to cope with ft_sourceanalysis output not having a dim field since r3273
        varargin{1}.dim = [Nvoxel 1];
   end
  if isempty(cfg.roi) || strcmp(cfg.avgoverroi, 'no')
    % remember the definition of the volume, assume that they are identical for all input arguments
    try stat.dim       = varargin{1}.dim;        end
    try stat.xgrid     = varargin{1}.xgrid;      end
    try stat.ygrid     = varargin{1}.ygrid;      end
    try stat.zgrid     = varargin{1}.zgrid;      end
    try stat.inside    = varargin{1}.inside;     end
    try stat.outside   = varargin{1}.outside;    end
    try stat.pos       = varargin{1}.pos;        end
    try stat.transform = varargin{1}.transform;  end
    try stat.freq      = varargin{1}.freq;       end
    try stat.time      = varargin{1}.time;       end
  else
    stat.inside  = 1:length(roilabel);
    stat.outside = [];
    stat.label   = roilabel(:);
  end
  for i=1:length(statfield)
    tmp = getsubfield(stat, statfield{i});
    if isfield(varargin{1}, 'inside') && numel(tmp)==length(varargin{1}.inside)
      % the statistic was only computed on voxels that are inside the brain
      % sort the inside and outside voxels back into their original place
      if islogical(tmp)
        tmp(varargin{1}.inside)  = tmp;
        tmp(varargin{1}.outside) = false;
      else
        tmp(varargin{1}.inside)  = tmp;
        tmp(varargin{1}.outside) = nan;
      end
      extradim = 1;
    elseif isfield(varargin{1}, 'inside') && isfield(varargin{1}, 'freq') && isfield(varargin{1}, 'time') && numel(tmp)==length(varargin{1}.inside)*length(varargin{1}.freq)*length(varargin{1}.time)
      % the statistic is a higher dimensional matrix (here as a function of freq) computed only on the
      % inside voxels
      newtmp = zeros(length(varargin{1}.inside)+length(varargin{1}.outside), length(varargin{1}.freq), length(varargin{1}.time));
      if islogical(tmp)
        newtmp(varargin{1}.inside, :, :)  = reshape(tmp, length(varargin{1}.inside), length(varargin{1}.freq), []);
        newtmp(varargin{1}.outside, :, :) = false;
      else
        newtmp(varargin{1}.inside, :, :)  = reshape(tmp, length(varargin{1}.inside), length(varargin{1}.freq), []);
        newtmp(varargin{1}.outside, :, :) = nan;
      end
      tmp = newtmp; clear newtmp;
      extradim = length(varargin{1}.freq);
    elseif isfield(varargin{1}, 'inside') && isfield(varargin{1}, 'freq') && numel(tmp)==length(varargin{1}.inside)*length(varargin{1}.freq)
      % the statistic is a higher dimensional matrix (here as a function of freq) computed only on the
      % inside voxels
      newtmp = zeros(length(varargin{1}.inside)+length(varargin{1}.outside), length(varargin{1}.freq));
      if islogical(tmp)
        newtmp(varargin{1}.inside, :)  = reshape(tmp, length(varargin{1}.inside), []);
        newtmp(varargin{1}.outside, :) = false;
      else
        newtmp(varargin{1}.inside, :)  = reshape(tmp, length(varargin{1}.inside), []);
        newtmp(varargin{1}.outside, :) = nan;
      end
      tmp = newtmp; clear newtmp;
      extradim = length(varargin{1}.freq);
    elseif isfield(varargin{1}, 'inside') && isfield(varargin{1}, 'time') && numel(tmp)==length(varargin{1}.inside)*length(varargin{1}.time)
      % the statistic is a higher dimensional matrix (here as a function of time) computed only on the
      % inside voxels
      newtmp = zeros(length(varargin{1}.inside)+length(varargin{1}.outside), length(varargin{1}.time));
      if islogical(tmp)
        newtmp(varargin{1}.inside, :)  = reshape(tmp, length(varargin{1}.inside), []);
        newtmp(varargin{1}.outside, :) = false;
      else
        newtmp(varargin{1}.inside, :)  = reshape(tmp, length(varargin{1}.inside), []);
        newtmp(varargin{1}.outside, :) = nan;
      end
      tmp = newtmp; clear newtmp;
      extradim = length(varargin{1}.time);
    else
      extradim = 1;
    end
    if numel(tmp)==prod(varargin{1}.dim)
      % reshape the statistical volumes into the original format
      stat = setsubfield(stat, statfield{i}, reshape(tmp, varargin{1}.dim));
    else
      stat = setsubfield(stat, statfield{i}, tmp);
    end
  end
else
  haschan    = isfield(data, 'label');    % this one remains relevant, even after averaging over channels
  haschancmb = isfield(data, 'labelcmb'); % this one remains relevant, even after averaging over channels
  hasfreq = strcmp(cfg.avgoverfreq, 'no') && ~any(isnan(data.freq));
  hastime = strcmp(cfg.avgovertime, 'no') && ~any(isnan(data.time));
  stat.dimord = '';

  if haschan
    stat.dimord = [stat.dimord 'chan_'];
    stat.label  = data.label;
    chandim = dim(2);
  elseif haschancmb
    stat.dimord   = [stat.dimord 'chancmb_'];
    stat.labelcmb = data.labelcmb;
    chandim = dim(2);
  end

  if hasfreq
    stat.dimord = [stat.dimord 'freq_'];
    stat.freq   = data.freq;
    freqdim = dim(3);
  end

  if hastime
    stat.dimord = [stat.dimord 'time_'];
    stat.time   = data.time;
    timedim = dim(4);
  end

  if ~isempty(stat.dimord)
    % remove the last '_'
    stat.dimord = stat.dimord(1:(end-1));
  end

  for i=1:length(statfield)
    try
      % reshape the fields that have the same dimension as the input data
      if     strcmp(stat.dimord, 'chan')
        stat = setfield(stat, statfield{i}, reshape(getfield(stat, statfield{i}), [chandim 1]));
      elseif strcmp(stat.dimord, 'chan_time')
        stat = setfield(stat, statfield{i}, reshape(getfield(stat, statfield{i}), [chandim timedim]));
      elseif strcmp(stat.dimord, 'chan_freq')
        stat = setfield(stat, statfield{i}, reshape(getfield(stat, statfield{i}), [chandim freqdim]));
      elseif strcmp(stat.dimord, 'chan_freq_time')
        stat = setfield(stat, statfield{i}, reshape(getfield(stat, statfield{i}), [chandim freqdim timedim]));
      elseif strcmp(stat.dimord, 'chancmb_time')
        stat = setfield(stat, statfield{i}, reshape(getfield(stat, statfield{i}), [chandim timedim]));
      elseif strcmp(stat.dimord, 'chancmb_freq')
        stat = setfield(stat, statfield{i}, reshape(getfield(stat, statfield{i}), [chandim freqdim]));
      elseif strcmp(stat.dimord, 'chancmb_freq_time')
        stat = setfield(stat, statfield{i}, reshape(getfield(stat, statfield{i}), [chandim freqdim timedim]));
      elseif strcmp(stat.dimord, 'time')
        stat = setfield(stat, statfield{i}, reshape(getfield(stat, statfield{i}), [1 timedim]));
      elseif strcmp(stat.dimord, 'freq')
        stat = setfield(stat, statfield{i}, reshape(getfield(stat, statfield{i}), [1 freqdim]));
      elseif strcmp(stat.dimord, 'freq_time')
        stat = setfield(stat, statfield{i}, reshape(getfield(stat, statfield{i}), [freqdim timedim]));
      end
    end
  end
end

return % statistics_wrapper main()

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% SUBFUNCTION for extracting the data of interest
% data resembles PCC beamed source reconstruction, multiple trials are coded in mom
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [dat, cfg] = get_source_pcc_mom(cfg, varargin)
Nsource = length(varargin);
Nvoxel  = length(varargin{1}.inside) + length(varargin{1}.outside);
Ninside = length(varargin{1}.inside);
dim     = varargin{1}.dim;
for i=1:Nsource
  ntrltap = sum(varargin{i}.cumtapcnt);
  dat{i}  = zeros(Ninside, ntrltap);
  for j=1:Ninside
    k = varargin{1}.inside(j);
    if ischar(varargin{i}.avg.csdlabel{1})
      % this represents the 'old-style' csdlabel, one list for all dipoles
      dipsel  = find(strcmp(varargin{i}.avg.csdlabel, 'scandip'));
    else
      % this represents the 'new-style' csdlabel, one list for each of the dipoles
      dipsel  = find(strcmp(varargin{i}.avg.csdlabel{k}, 'scandip'));
    end
    dat{i}(j,:) = reshape(varargin{i}.avg.mom{k}(dipsel,:), 1, ntrltap);
  end
end
% concatenate the data matrices of the individual input arguments
dat = cat(2, dat{:});
% remember the dimension of the source data
cfg.dim = dim;
% remember which voxels are inside the brain
cfg.inside = varargin{1}.inside;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% SUBFUNCTION for extracting the data of interest
% data resemples LCMV beamed source reconstruction, mom contains timecourse
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [dat, cfg] = get_source_lcmv_mom(cfg, varargin)
Nsource = length(varargin);
Nvoxel  = length(varargin{1}.inside) + length(varargin{1}.outside);
Ntime   = length(varargin{1}.avg.mom{varargin{1}.inside(1)});
Ninside = length(varargin{1}.inside);
dim     = [varargin{1}.dim Ntime];
dat     = zeros(Ninside*Ntime, Nsource);
for i=1:Nsource
  % collect the 4D data of this input argument
  tmp = nan(Ninside, Ntime);
  for j=1:Ninside
    k = varargin{1}.inside(j);
    tmp(j,:) = reshape(varargin{i}.avg.mom{k}, 1, dim(4));
  end
  dat(:,i) = tmp(:);
end
% remember the dimension of the source data
cfg.dim = dim;
% remember which voxels are inside the brain
cfg.inside = varargin{1}.inside;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% SUBFUNCTION for extracting the data of interest
% data contains single-trial or single-subject source reconstructions
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [dat, cfg] = get_source_trial(cfg, varargin)
Nsource = length(varargin);
Nvoxel  = length(varargin{1}.inside) + length(varargin{1}.outside);

for i=1:Nsource
  Ntrial(i) = length(varargin{i}.trial);
end
k = 1;
for i=1:Nsource
  for j=1:Ntrial(i)
    tmp = getsubfield(varargin{i}.trial(j), cfg.parameter);
    if ~iscell(tmp),
      %dim = size(tmp);
      dim = [Nvoxel 1];
    else
      dim = [Nvoxel size(tmp{varargin{i}.inside(1)})];
    end
    if i==1 && j==1 && numel(tmp)~=Nvoxel,
      ft_warning('the input-data contains more entries than the number of voxels in the volume, the data will be concatenated');
      dat    = zeros(prod(dim), sum(Ntrial)); %FIXME this is old code should be removed
    elseif i==1 && j==1 && iscell(tmp),
      ft_warning('the input-data contains more entries than the number of voxels in the volume, the data will be concatenated');
      dat    = zeros(Nvoxel*numel(tmp{varargin{i}.inside(1)}), sum(Ntrial));
    elseif i==1 && j==1,
      dat = zeros(Nvoxel, sum(Ntrial));
    end
    if ~iscell(tmp),
      dat(:,k) = tmp(:);
    else
      Ninside   = length(varargin{i}.inside);
      %tmpvec    = (varargin{i}.inside-1)*prod(size(tmp{varargin{i}.inside(1)}));
      tmpvec    = varargin{i}.inside;
      insidevec = [];
      for m = 1:numel(tmp{varargin{i}.inside(1)})
        insidevec = [insidevec; tmpvec(:)+(m-1)*Nvoxel];
      end
      insidevec = insidevec(:)';
      tmpdat  = reshape(permute(cat(3,tmp{varargin{1}.inside}), [3 1 2]), [Ninside numel(tmp{varargin{1}.inside(1)})]);
      dat(insidevec, k) = tmpdat(:);
    end
    % add original dimensionality of the data to the configuration, is required for clustering
    %FIXME: this was obviously wrong, because often trial-data is one-dimensional, so no dimensional information is present
    %cfg.dim = dim(2:end);
    k = k+1;
  end
end
if isfield(varargin{1}, 'inside')
  fprintf('only selecting voxels inside the brain for statistics (%.1f%%)\n', 100*length(varargin{1}.inside)/prod(dim));
  for j=prod(dim(2:end)):-1:1
    dat((j-1).*dim(1) + varargin{1}.outside, :) = [];
  end
end
% remember the dimension of the source data
if isfield(varargin{1}, 'dim')
  cfg.dim = varargin{1}.dim;
else
  cfg.dim = size(varargin{1}.trial(1).(cfg.parameter));
  cfg.dim(1) = numel(varargin{1}.inside);
  cfg.insideorig = varargin{1}.inside;
  cfg.inside     = varargin{1}.inside;
end

%if ~isfield(cfg, 'dim')
  %warning('for clustering on trial-based data you explicitly have to specify cfg.dim');
%end
% remember which voxels are inside the brain
cfg.inside = varargin{1}.inside;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% SUBFUNCTION for extracting the data of interest
% get the average source reconstructions, the repetitions are in multiple input arguments
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [dat, cfg] = get_source_avg(cfg, varargin)
Nsource = length(varargin);
Nvoxel  = length(varargin{1}.inside) + length(varargin{1}.outside);
if isfield(varargin{1}, 'dim')
  dim    = varargin{1}.dim;
  inside = false(prod(dim),1);
else
  dim    = numel(varargin{1}.inside);
  inside = false(size(varargin{1}.pos,1),1);
end
inside(varargin{1}.inside) = true;

tmp     = getsubfield(varargin{1}, cfg.parameter);
if size(tmp,2)>1
  if isfield(varargin{1}, 'freq')
    Nvoxel = Nvoxel*numel(varargin{1}.freq);
    dim    = [dim numel(varargin{1}.freq)];
    inside = repmat(inside, [1 numel(varargin{1}.freq)]);
  end
  if isfield(varargin{1}, 'time')
    Nvoxel = Nvoxel*numel(varargin{1}.time);
    dim    = [dim numel(varargin{1}.time)];
    if isfield(varargin{1},'freq')
      inside = repmat(inside, [1 1 numel(varargin{1}.time)]);
    else
      inside = repmat(inside, [1 numel(varargin{1}.time)]);
    end
  end
end
dat = zeros(Nvoxel, Nsource);
for i=1:Nsource
  tmp = getsubfield(varargin{i}, cfg.parameter);
  dat(:,i) = tmp(:);
end
insideorig = find(inside(:,1));
inside = find(inside(:));
if isfield(varargin{1}, 'inside')
  if isfield(varargin{1}, 'pos')
    npos = size(varargin{1}.pos,1);
  elseif isfield(varargin{1}, 'dim')
    npos = prod(varargin{1}.dim);
  else
    npos = length(varargin{1}.inside); % uninformative, at least prevents crash
  end
  fprintf('only selecting voxels inside the brain for statistics (%.1f%%)\n', 100*length(varargin{1}.inside)/npos);
  dat = dat(inside,:);
end
% remember the dimension of the source data
cfg.dim = dim;
% remember which voxels are inside the brain
cfg.inside     = inside(:);
cfg.insideorig = insideorig;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% SUBFUNCTION for creating a design matrix
% should be called in the code above, or in prepare_design
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [cfg] = get_source_design_pcc_mom(cfg, varargin)
% should be implemented

function [cfg] = get_source_design_lcmv_mom(cfg, varargin)
% should be implemented

function [cfg] = get_source_design_trial(cfg, varargin)
% should be implemented

function [cfg] = get_source_design_avg(cfg, varargin)
% should be implemented


%PointFeature  PointCorner feature object
%
% A superclass for image corner features.
%
% Methods::
% plot         Plot feature position
% distance     Descriptor distance
% ncc          Descriptor similarity
% uv           Return feature coordinate
% display      Display value
% char         Convert value to string
%
% Properties::
% u             horizontal coordinate
% v             vertical coordinate
% strength      feature strength
% descriptor    feature descriptor (vector)
%
% Properties of a vector of PointFeature objects are returned as a vector.
% If F is a vector (Nx1) of PointFeature objects then F.u is a 2xN matrix
% with each column the corresponding point coordinate.
%
% See also ScalePointFeature, SurfPointFeature, SiftPointFeature.

% Copyright (C) 1993-2011, by Peter I. Corke
%
% This file is part of The Machine Vision Toolbox for Matlab (MVTB).
%
% MVTB 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.
%
% MVTB 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 MVTB.  If not, see <http://www.gnu.org/licenses/>.

classdef PointFeature < handle

    properties %(GetAccess=protected)%, Hidden=true)
        u_           % feature x-coordinates
        v_           % feature y-coordinates
        strength_
        descriptor_
        image_id_
    end % properties

    methods

        function f = PointFeature(u, v, strength)
        %PointFeature.PointFeature Create a point feature object
        %
        % F = PointFeature() is a point feature object with null parameters.
        %
        % F = PointFeature(U, V) is a point feature object with specified
        % coordinates.
        %
        % F = PointFeature(U, V, STRENGTH) as above but with specified strength.

            if nargin == 0
                return;
            end
            if nargin >= 2
                f.u_ = u;
                f.v_ = v;
            end
            if nargin == 3
                f.strength_ = strength;
            end
        end

        function val = uv(features)
            val = [[features.u]; [features.v]];
        end

        function plot(features, varargin)
        %PointFeature.plot Plot feature
        %
        % F.plot() overlay a white square marker at the feature position.
        %
        % F.plot(LS) as above but the optional line style arguments LS are
        % passed to plot.
        %
        % If F is a vector then each element is plotted.

            holdon = ishold;
            hold on

            if nargin == 1
                varargin = {'ws'};
            end

            for i=1:length(features)
                plot(features(i).u_, features(i).v_, varargin{:});
            end

            if ~holdon
                hold off
            end
        end % plot

        function s = distance(f1, f2)
        %PointFeature.distance Distance between feature descriptors
        %
        % D = F.distance(F1) is the distance between feature descriptors, the norm
        % of the Euclidean distance.
        %
        % If F is a vector then D is a vector whose elements are the distance between
        % the corresponding element of F and F1.
            for i=1:length(f2)
                s(i) = norm(f1.descriptor-f2(i).descriptor);
            end
        end

        function s = ncc(f1, f2)
        %PointFeature.ncc Feature descriptor similarity
        %
        % S = F.ncc(F1) is the similarty between feature descriptors which is a
        % scalar in the interval -1 to 1, where 1 is perfect match.
        %
        % If F is a vector then D is a vector whose elements are the distance between
        % the corresponding element of F and F1.
            for i=1:length(f2)
                s(i) = dot(f1.descriptor,f2(i).descriptor);
            end
        end

        function [m,corresp] = match(f1, f2, varargin)
        %PointFeature.match Match point features
        %
        % M = F.match(F2, OPTIONS) is a vector of FeatureMatch objects that
        % describe candidate matches between the two vectors of point
        % features F and F2.
        %
        % [M,C] = F.match(F2, OPTIONS) as above but returns a correspodence
        % matrix where each row contains the indices of corresponding features
        % in F and F2  respectively.
        %
        % Options::
        % 'thresh',T    Match threshold (default 0.05)
        % 'median'      Threshold at the median distance
        % 'top',N       Take top N features
        %
        % See also FeatureMatch.

        % TODO: should use a kd-tree or FLANN

            opt.bilateral = false;
            opt = tb_optparse(opt, varargin);

            if opt.bilateral
                % bilateral matching, best match of f1 in f2 must be best match back in f1
                matches = [];
                d = distance([f1.descriptor], [f2.descriptor]);
                for i=1:numrows(d)
                    % find smallest element in the row
                    [best,ii] = min(d(i,:));
                    [~,jj] = min(d(:,ii));
                    if jj == i
                        % we have a match
                        row = d(i,:);
                        row(ii) = Inf;
                        nextbest = min(row);
                        if nextbest < 0.7*best
                            matches = [matches [i; ii]];
                        end
                    end
                end
            else

                [matches,dist,dist2] = closest([f1.descriptor], [f2.descriptor]);
                matches = [1:length(f1); matches];

                % delete matches where distance of closest match is greater than
                % 0.7 of second closest match
                k = dist > 0.7 * dist2;

                matches(:,k) = [];
            dist(k) = [];
            end

            % dist is a 1xM matrix of distance between the matched features, low is good.

            % matches is a 2xM matrix, one column per match, each column
            % is the index of the matching feature in images 1 and 2

            % sort into increasing distance
            [z,k] = sort(dist, 'ascend');
            matches = matches(:,k);
            dist = dist(:,k);

            m = [];
            cor = [];

            for i=1:numcols(matches)
                k1 = matches(1,i);
                k2 = matches(2,i);
                mm = FeatureMatch(f1(k1), f2(k2), dist(i));
                m = [m mm];
                cor(:,i) = [k1 k2]';
            end

            if nargout > 1
                corresp = cor;
            end
        end

        % methods to provide convenient access to properties of object vectors
        function val = u(f)
            val = [f.u_];
        end

        function val = v(f)
            val = [f.v_];
        end

        function val = p(f)
            val = [[f.u_]; [f.v_]];
        end

        function val = strength(f)
            val = [f.strength_];
        end

        function val = descriptor(f)
            val = [f.descriptor_];
        end

        function fp = pick(f, uv)
            %PointFeature.pick Graphically select a feature
            %
            % V = F.pick() is the id of the feature closest to the point clicked
            % by the user on a plot of the image.
            %
            if nargin < 2
                [u,v] = ginput(1);
                uv = f.uv;
            end
            k = closest([u v]', uv);
            fp = f(k);
        end

        function display(f)
        %PointFeature.display Display value
        %
        % F.display() displays a compact human-readable representation of the feature.
        % If F is a vector then the elements are printed one per line.
        %
        % Notes::
        % - This method is invoked implicitly at the command line when the result
        %   of an expression is a PointFeature object and the command has no trailing
        %   semicolon.
        %
        % See also PointFeature.char.

            disp(' ');
            disp([inputname(1), ' = '])
            disp(' ');
            if length(f) > 20
                fprintf('%d features (listing suppressed)\n  Properties:', length(f));
                for property=fieldnames(f)'
                    fprintf(' %s', property{1}(1:end-1));
                end
                fprintf('\n');
            else
                disp( char(f) );
            end
        end % display()

        function ss = char(features)
        %PointFeature.char Convert to string
        %
        % S = F.char() is a compact string representation of the point feature.
        % If F is a vector then the string has multiple lines, one per element.

            ss = [];
            for i=1:length(features)
                f = features(i);
                % display the coordinate
                s = sprintf('  (%g,%g)', f.u_, f.v_);

                % display the other properties
                for property=fieldnames(f)'
                    prop = property{1}; % convert from cell array
                    switch prop
                    case {'u_', 'v_', 'descriptor_'}
                        continue;
                    otherwise
                        val = getfield(f, prop);
                        if ~isempty(val)
                            s = strcat(s, [sprintf(', %s=', prop(1:end-1)), num2str(val, ' %g')]);
                        end
                    end
                end

                % do the descriptor last
                val = getfield(f, 'descriptor_');
                if ~isempty(val)
                    if length(val) == 1
                        % only list scalars or shortish vectors
                        s = strcat(s, [', descrip=', num2str(val, ' %g')]);
                    elseif length(val) < 4
                        % only list scalars or shortish vectors
                        s = strcat(s, [', descrip=(', num2str(val', ' %g'), ')']);
                    else
                        s = strcat(s, ', descrip= ..');
                    end
                end
                ss = strvcat(ss, s);
            end
        end

    end % methods
end % classdef


function F = power( F, G )
%.^ Componentwise power for DISKFUNV.
%   F.^G where F is a DISKFUNV and G is a double returns the result from
%   componentwise powers.
%
%   F.^G where F is a double and G is a CHEBFUN2 returns from componentwise
%   powers.

% Copyright 2017 by The University of Oxford and The Chebfun Developers.
% See http://www.chebfun.org/ for Chebfun information.

% Empty check:
if ( isempty( F ) || isempty( G ) )
    F = diskfunv;
    return
end

if ( isa( F, 'double' ) )       % scalar . ^ DISKFUNV

    if ( numel(F) == 1 )

        scalar = F;
        F = G;
        F.components{1} = power( scalar, G.components{1} );
        F.components{2} = power( scalar, G.components{2} );

    else

        error('CHEBFUN:DISKFUNV:power:double', 'Dimension mismatch.');

    end

elseif ( isa(G, 'double') )      % DISKFUNV . ^ scalar

    if ( numel(G) == 1 )

        scalar = G;
        F.components{1} = power( F.components{1}, scalar );
        F.components{2} = power( F.components{2}, scalar );

    else

        error('CHEBFUN:DISKFUNV:power:double',...
                     'DISKFUNV and scalar dimension mismatch.');

    end

elseif ( isa(F,'diskfunv') && isa(G,'diskfunv') )  % DISKFUNV.^DISKFUNV

    error('CHEBFUN:DISKFUNV:power:size', 'DISKFUNV dimension mismatch.');

else  % error

    error('CHEBFUN:DISKFUNV:power:inputs', 'Unrecognized input arguments.');

end

end


function fp = fmf_write_header( filename, header_size, version, ...
                                f_height, f_width, bytes_per_chunk, ...
                                max_n_frames, data_format)
% fp = fmf_write_header( filename, header_size, version,
%                        f_height, f_width, bytes_per_chunk,
%                        max_n_frames, data_format )
%
% writes FlyMovieFormat header data to FILENAME and returns a file
% pointer FP.
%
% VERSION is the fmf version
% HEADER_SIZE is in bytes for the appropriate file format (fmf VERSION)
% F_HEIGHT and F_WIDTH are the number of pixels in a frame
% BYTES_PER_CHUNK is the number of bytes per frame in the file
% MAX_N_FRAMES is the number of frames in the file
% DATA_FORMAT is the format in which each pixel is stored.
%
% KMB 04/07

fp = fopen( filename, 'w' );
if fp <= 0,
  error(['Could not open file ',filename,' for writing.']);
end;

% write header
if version ~= 1 & version ~= 3,
  error( 'version not supported -- FMF versions 1 and 3 only' );
end;

fwrite(fp,version,'uint32');
if version == 1,
  data_format = 'MONO8';
  bits_per_pixel = 8;
end
if version == 3,
  fwrite(fp,length(data_format),'uint32');
  fwrite(fp,data_format);
  bits_per_pixel = fmf_get_bits_per_pixel(data_format);
  fwrite(fp,bits_per_pixel,'uint32');
end

fwrite(fp,f_height,'uint32');
fwrite(fp,f_width,'uint32');
fwrite(fp,bytes_per_chunk,'integer*8');
fwrite(fp,max_n_frames,'integer*8');
curr = ftell(fp);
if header_size > curr,
  fwrite(fp,zeros(header_size-curr,1), 'uint8');
end;


function plotExperimentalvsPredictedExchange(measuredFluxes, predictedFluxes, otherFluxes, extraFluxes, param)
% plot a comparison of experimental and predicted exchange reaction rates
%
% INPUT
% measuredFluxes:   table with the fluxes obtained from exometabolomics experiments with the following variables
%           * .rxns:    k x 1 cell array of reaction identifiers
%           * .mean:    k x 1 double of measured mean flux
%           * .SD:      k x 1 double standard deviation of the measured flux
%           * .labels:  k x 1 cell array of labels to display on y axis
%           * .Properties.Description: string describing the data, used in plot legend
%
% predictedFluxes:   table with the following variables
%           * .rxns:    k x 1 cell array of reaction identifiers
%           * .v:       k x 1 double of predicted mean flux
%           * .lb:      k x 1 double lower bound on reaction flux
%           * .ub:      k x 1 double upper bound on reaction flux
%           * .labels:  k x 1 cell array of labels to display on y axis
%           * .Properties.Description: string describing the data, used in plot legend
%
% OPTIONAL INPUT
% otherFluxes:  table with the following variables
%           * .rxns:    k x 1 cell array of reaction identifiers
%           * .Properties.Description: sring describing the data, used in plot legend
%           * .labels:  k x 1 cell array of labels to display on y axis
%   and
%           * .mean:    k x 1 double of measured mean flux
%           * .SD:      k x 1 double standard deviation of the measured flux
%
%   or
%           * .v:       k x 1 double of predicted mean flux
%           * .lb:      k x 1 double lower bound on reaction flux
%           * .ub:      k x 1 double upper bound on reaction flux
%
%
% param: paramters structure with the following fields
%           * .measuredFluxes  {(1),0} set to zero to not display experimental fluxes
%
%           * .labelType determines which y axes lables to display
%                       'metabolitePlatform' = platform left and metabolite name right
%                       'metabolite'         = metabolite only right  (default)
%                       'platform'           = platform only left
%           * .expOrder {(1),0}
%                       1  = order reactions by value of experimental flux
%                       0  = order reactions by value of predicted flux (predictedFluxes) for each reaction corresponding to an experimental flux
%           * .saveFigures = 1 saves figure as .fig and .png to current directory
%           * .plotTitle: title of the plot

if exist('measuredFluxes','var')
    expDescription = measuredFluxes.Properties.Description;
else
    error('predictedFluxes missing: must provide a predicted solution');
end

if exist('predictedFluxes','var')
    predDescription = predictedFluxes.Properties.Description;
else
    error('predictedFluxes missing: must provide a predicted solution');
end
if exist('otherFluxes','var') && ~isempty(otherFluxes)
    otherDescription = otherFluxes.Properties.Description;
else
    otherFluxes = [];
    otherDescription = '';
end
if exist('extraFluxes','var') && ~isempty(extraFluxes)
    extraDescription = extraFluxes.Properties.Description;
else
    extraFluxes = [];
    extraDescription = '';
end
if ~exist('param','var')
    param = struct();
end
if ~isfield(param,'measuredFluxes')
    param.measuredFluxes = 1;
end
if ~isfield(param,'labelType')
    param.labelType = 'metabolite';
end
if ~isfield(param,'saveFigures')
    param.saveFigures = 0;
end
if ~isfield(param,'expOrder')
    param.expOrder = 1;
end
if ~isfield(param,'plotTitle')
    param.plotTitle = [];
end
if ~isfield(param,'measuredBounds')
    param.measuredBounds = 1;
end
if ~isfield(param,'predictedBounds')
    param.predictedBounds = 1;
end
if ~isfield(param,'otherBounds')
    param.otherBounds = 1;
end
if ~isfield(param,'extraBounds')
    param.extraBounds = 1;
end
if ~isvar(measuredFluxes,'labels')
    if isvar(measuredFluxes,'metNames')
        measuredFluxes.labels = measuredFluxes.metNames;
    end
end
if isvar(measuredFluxes,'rxns')
    measuredFluxes.rxns = measuredFluxes.rxns;
end
if isvar(predictedFluxes,'rxns')
    predictedFluxes.rxns = predictedFluxes.rxns;
end
if ~isempty(otherFluxes)
    if isvar(otherFluxes,'rxns')
        otherFluxes.rxns = otherFluxes.rxns;
    end
    if ~isvar(otherFluxes,'rxns')
        error('otherFluxes.rxns missing - needed to map to other variables')
    end
end
if ~isempty(extraFluxes)
    if isvar(extraFluxes,'rxns')
        extraFluxes.rxns = extraFluxes.rxns;
    end
    if ~isvar(extraFluxes,'rxns')
        error('extraFluxes.rxns missing - needed to map to other variables')
    end
end
%%%%%
if param.expOrder
    %eliminate NaN experimental data
    measuredFluxes = measuredFluxes(~isnan(measuredFluxes.mean),:);

    %sort by the normal cumulative distribution function that the flux is an uptake (1) rather than a secretion (0)
    zerobool = (measuredFluxes.mean-measuredFluxes.SD)<=0 & (measuredFluxes.mean+measuredFluxes.SD)>=0;
    posbool = sign(measuredFluxes.mean)==1 & ~zerobool;
    negbool = sign(measuredFluxes.mean)==-1 & ~zerobool;

    measuredFluxes.uptakeProbability = normcdf(0,measuredFluxes.mean,measuredFluxes.SD,'upper');

    measuredFluxes.toRankOrder = zeros(length(measuredFluxes.mean),1);
    measuredFluxes.toRankOrder(zerobool) = measuredFluxes.uptakeProbability(zerobool);
    measuredFluxes.toRankOrder(posbool) =  measuredFluxes.mean(posbool)+1;
    measuredFluxes.toRankOrder(negbool) = measuredFluxes.mean(negbool);
    measuredFluxes.zerobool=zerobool;
    measuredFluxes.posbool=posbool;
    measuredFluxes.negbool=negbool;

    if 1
        [sortedUptakeProbability,xi] = sort(measuredFluxes.toRankOrder);
    else
        [sortedUptakeProbability,xi] = sort(measuredFluxes.uptakeProbability,'descend');
    end

    measuredFluxes = measuredFluxes(xi,:);
    zerobool = measuredFluxes.zerobool;
    posbool = measuredFluxes.posbool;
    negbool = measuredFluxes.negbool;

    %place the predicted exchanges in the same order as the measurements
    predictedFluxes = mapAontoB(predictedFluxes.rxns,measuredFluxes.rxns, predictedFluxes);
else
    for i=1:length(predictedFluxes.labels)
        labels2 = strrep(predictedFluxes.labels{i},' for ', ' of ');
        predictedFluxes.labels{i} = [predictedFluxes.labels{i} ', ' labels2];
    end

    %order the predicted exchanges
    predictedFluxes.zerobool = (predictedFluxes.v-1e-5)<=0 & (predictedFluxes.v+1e-5)>=0;
    predictedFluxes.posbool = sign(predictedFluxes.v)==1 & ~predictedFluxes.zerobool;
    predictedFluxes.negbool = sign(predictedFluxes.v)==-1 & ~predictedFluxes.zerobool;
    predictedFluxes.uptakeProbability = normcdf(0,predictedFluxes.v,abs(predictedFluxes.v)/10);

    toRankOrder = zeros(length(predictedFluxes.v),1);
    toRankOrder(predictedFluxes.zerobool) = predictedFluxes.uptakeProbability(predictedFluxes.zerobool);
    toRankOrder(predictedFluxes.posbool) =  predictedFluxes.v(predictedFluxes.posbool)+1;
    toRankOrder(predictedFluxes.negbool)=predictedFluxes.v(predictedFluxes.negbool);

    if 1
        [sortedUptakeProbability,xi] = sort(toRankOrder);
    else
        [sortedUptakeProbability,xi] = sort(predictedFluxes.uptakeProbability,'descend');
    end
    predictedFluxes = predictedFluxes(xi,:);
    zerobool = predictedFluxes.zerobool;
    posbool = predictedFluxes.posbool;
    negbool = predictedFluxes.negbool;

    %place the measurements in the same order as the predictions
    measuredFluxes = mapAontoB(measuredFluxes.rxns,predictedFluxes.rxns,measuredFluxes);
end

%log modulus transformation
x = logmod(measuredFluxes.mean,10);
xl = logmod(measuredFluxes.mean - measuredFluxes.SD,10);
xl = x - xl;
xu = logmod(measuredFluxes.mean + measuredFluxes.SD,10);
xu = xu - x;

%use log modulus transformation
v = logmod(predictedFluxes.v,10);
if isvar(predictedFluxes,'lb') && isvar(predictedFluxes,'ub')
    vl = logmod(predictedFluxes.lb,10);
    vl = v - vl;
    vu = logmod(predictedFluxes.ub,10);
    vu = vu - v;
else
    xl = logmod(measuredFluxes.mean - measuredFluxes.SD,10);
    xl = x - xl;
    xu = logmod(measuredFluxes.mean + measuredFluxes.SD,10);
    xu = xu - x;
end

if isempty(otherFluxes)
    if param.measuredFluxes
        min_v = min([v;x-xl]);
        max_v = max([v;x+xu]);
    else
        min_v = min(v);
        max_v = max(v);
    end
    bool=~isnan(v);
else
    if param.expOrder
        %place the other fluxes in the same order as the measurements
        otherFluxes = mapAontoB(otherFluxes.rxns,measuredFluxes.rxns, otherFluxes);
    else
        %place the other fluxes in the same order as the predictions
        otherFluxes = mapAontoB(otherFluxes.rxns,predictedFluxes.rxns, otherFluxes);
    end

    if isvar(otherFluxes,'v') && isvar(otherFluxes,'lb') && isvar(otherFluxes,'ub')
        %use log modulus transformation
        v2 = logmod(otherFluxes.v,10);
        v2l = logmod(otherFluxes.lb,10);
        v2l = v2 - v2l;
        v2u = logmod(otherFluxes.ub,10);
        v2u = v2u - v2;
    elseif isvar(otherFluxes,'mean') && isvar(otherFluxes,'SD')
        %use log modulus transformation
        v2 = logmod(otherFluxes.mean,10);
        v2l = logmod(otherFluxes.mean - otherFluxes.SD,10);
        v2l = v2 - v2l;
        v2u = logmod(otherFluxes.mean + otherFluxes.SD,10);
        v2u = v2u - v2;
    else
        error('Unrecognised input: otherFluxes')
    end

    %limits
    if param.measuredFluxes
        min_v = min([v;v2;x-xl]);
        max_v = max([v;v2;x+xu]);
    else
        min_v = min([v;v2]);
        max_v = max([v;v2]);
    end
    bool=~isnan(v) & ~isnan(v2);
end

if isempty(extraFluxes)
    if isempty(otherFluxes)
        if param.measuredFluxes
            min_v = min([v;x-xl]);
            max_v = max([v;x+xu]);
        else
            min_v = min(v);
            max_v = max(v);
        end
        bool=~isnan(v);
    else
        if param.measuredFluxes
            min_v = min([v;v2;x-xl]);
            max_v = max([v;v2;x+xu]);
        else
            min_v = min([v;v2]);
            max_v = max([v;v2]);
        end
        bool=~isnan(v) & ~isnan(v2);
    end
else
    if param.expOrder
        %place the other fluxes in the same order as the measurements
        extraFluxes = mapAontoB(extraFluxes.rxns,measuredFluxes.rxns, extraFluxes);
    else
        %place the other fluxes in the same order as the predictions
        extraFluxes = mapAontoB(extraFluxes.rxns,predictedFluxes.rxns, extraFluxes);
    end

    if isvar(extraFluxes,'v') && isvar(extraFluxes,'lb') && isvar(extraFluxes,'ub')
        %use log modulus transformation
        v3 = logmod(extraFluxes.v,10);
        v3l = logmod(extraFluxes.lb,10);
        v3l = v3 - v3l;
        v3u = logmod(extraFluxes.ub,10);
        v3u = v3u - v3;
    elseif isvar(extraFluxes,'mean') && isvar(extraFluxes,'SD')
        %use log modulus transformation
        v3 = logmod(extraFluxes.mean,10);
        v3l = logmod(extraFluxes.mean - extraFluxes.SD,10);
        v3l = v3 - v3l;
        v3u = logmod(extraFluxes.mean + extraFluxes.SD,10);
        v3u = v3u - v3;
    else
        error('Unrecognised input: extraFluxes')
    end

    %limits
    if param.measuredFluxes
        min_v = min([v;v2;v3;x-xl]);
        max_v = max([v;v2;v3;x+xu]);
    else
        min_v = min([v;v2;v3]);
        max_v = max([v;v2;v3]);
    end
    bool=~isnan(v) & ~isnan(v2) & ~isnan(v3);
end

%figure('Renderer', 'painters', 'Position', [10 10 900 1500])
%figure('units','normalized','outerposition',[0 0 1 1])
figure('units','normalized','Position',[0 0 0.5 1.5])
hold on

%patch in the background
X = [min_v max_v max_v min_v];

if param.expOrder
    Y = [nnz(negbool & bool)+nnz(zerobool & bool)+0.5 nnz(negbool & bool)+nnz(zerobool & bool)+0.5 length(measuredFluxes.mean(bool))+0.5 length(measuredFluxes.mean(bool))+0.5];
else
    Y = [nnz(negbool & bool)+nnz(zerobool & bool)+0.5 nnz(negbool & bool)+nnz(zerobool & bool)+0.5 length(predictedFluxes.v(bool))+0.5 length(predictedFluxes.v(bool))+0.5];
end

patch(X,Y,[221/255, 192/255, 237/255],'FaceAlpha',0.5)

Y = [nnz(negbool & bool)+0.5 nnz(negbool & bool)+0.5 nnz(negbool & bool)+nnz(zerobool & bool)+0.5 nnz(negbool & bool)+nnz(zerobool & bool)+0.5];
patch(X,Y,[192/255, 237/255, 207/255],'FaceAlpha',0.5)

Y = [0 0 nnz(negbool & bool)+0.5 nnz(negbool & bool)+0.5];
patch(X,Y,[192/255, 233/255, 237/255],'FaceAlpha',0.9)

if param.measuredFluxes
    if param.measuredBounds
        %errorbar
        errorbar(x(bool),(1:length(x(bool)))+0.2,xl(bool),xu(bool),'horizontal','*','LineWidth',2,'MarkerSize',12)
    else
        plot(x(bool),(1:length(x(bool)))+0.2,'*k','MarkerSize',12)%,'MarkerFaceColor',[1 1 1])
    end
end

if param.predictedBounds
    %predicted fluxes
    errorbar(v(bool),(1:length(v(bool))),vl(bool),vu(bool),'horizontal','dk','LineWidth',1.5,'MarkerSize',10)%,'MarkerFaceColor',[1 1 1])
else
    %predicted fluxes
    plot(v(bool),1:length(v(bool)),'dk','MarkerSize',12)%,'MarkerFaceColor',[1 1 1])
end

%x & y limits

if param.expOrder
    ylim([0,length(measuredFluxes.mean(bool))+0.5])
else
    ylim([0,length(predictedFluxes.v(bool))+0.5])
end

[comparisonStats,comparison] = quanQualAcc(measuredFluxes, predictedFluxes);

%other fluxes
if ~isempty(otherFluxes)
    if param.otherBounds
        errorbar(v2(bool),(1:length(v2(bool)))-0.2,v2l(bool),v2u(bool),'horizontal','o','LineWidth',1.5,'MarkerSize',10, 'MarkerEdgeColor','red','MarkerFaceColor',[1 .6 .6])%,'MarkerFaceColor',[1 1 1])
    else
        %other fluxes
        plot(v2(bool),(1:length(v2(bool)))-0.2,'o','MarkerSize',10,'MarkerEdgeColor','red','MarkerFaceColor',[1 .6 .6])
    end

    [comparisonStats,comparison] = quanQualAcc(measuredFluxes, otherFluxes);

    xlim([min_v max_v])
    %xlim([-0.5 0.5])
else
    xlim([min_v max_v])
    %xlim([-0.5 0.5])
    %title(['Comparison of \color{blue}measured \color{black}and \color{red}predicted \color{black}net exchange reaction rates. (Objective ' solution1 ')'])
end

%extra fluxes
if ~isempty(extraFluxes)
    if param.extraBounds
        errorbar(v3(bool),(1:length(v3(bool)))-0.4,v3l(bool),v3u(bool),'horizontal','x','LineWidth',1.5,'MarkerSize',10, 'MarkerEdgeColor','magenta','MarkerFaceColor',[1 .6 .6])%,'MarkerFaceColor',[1 1 1])
    else
        %extra fluxes
        plot(v3(bool),(1:length(v3(bool)))-0.4,'x','MarkerSize',10,'MarkerEdgeColor','magenta','MarkerFaceColor',[1 .6 .6])
    end

    [comparisonStats,comparison] = quanQualAcc(measuredFluxes, extraFluxes);

    xlim([min_v max_v])
    %xlim([-0.5 0.5])
else
    xlim([min_v max_v])
    %xlim([-0.5 0.5])
    %title(['Comparison of \color{blue}measured \color{black}and \color{red}predicted \color{black}net exchange reaction rates. (Objective ' solution1 ')'])
end

if isempty(param.plotTitle)
    if 0
    if param.measuredFluxes
        if isempty(otherFluxes)
            title(['Measured versus predicted (<>)' predDescription ' net exchange reaction rates.'])
        else
            if isempty(extraFluxes)
                title(['Measured versus predicted (<>)' predDescription ' and ' otherDescription '(O) net exchange reaction rates.'])
            else
                title(['Measured versus predicted (<>)' predDescription ' and ' otherDescription '(O), and ' extraDescription '(X) net exchange reaction rates.'])
            end
        end
    else
        title(['Predicted net reaction rates: ' predDescription ' vs ' otherDescription])
    end
    end
else
    title(param.plotTitle);
end

if param.measuredFluxes
    if isempty(otherFluxes)
        legend({'measured secretion','uncertain','measured uptake',expDescription,predDescription},'location','northwest');
    else
        if isempty(extraFluxes)
            legend({'measured secretion','uncertain','measured uptake',expDescription,predDescription,otherDescription},'location','northwest');
        else
            legend({'measured secretion','uncertain','measured uptake',expDescription,predDescription,otherDescription,extraDescription},'location','northwest');
        end
    end
else
    if isempty(otherFluxes)
        legend({predDescription},'location','northwest');
    else
        if isempty(extraFluxes)
            legend(['measured secretion','uncertain','measured uptake',predDescription,otherDescription],'location','northwest');
        else
            legend(['measured secretion','uncertain','measured uptake',predDescription,otherDescription,extraDescription],'location','northwest');
        end
    end
end
xlabel('Net flux (uMol/gDW/hr) on $\log_{10}$ scale: $\textrm{sign}(x) \times \log_{10}(1+x)$','interpreter','latex');

%labels
ax = gca;

%xtick=[-100:10:100];
%xticklab = cellstr(num2str(sign(xtick).*log10(1+abs(xtick)), '%d'));
%set(gca,'XTick',xtick,'XTickLabel',xticklab,'TickLabelInterpreter','tex')

if param.expOrder
    ax.YTick = 1:length(measuredFluxes.mean(bool));
    ax.YTickLabel = measuredFluxes.labels(bool);
else
    ax.YTick = 1:length(predictedFluxes.v(bool));
    ax.YTickLabel = predictedFluxes.labels(bool);
end

%hack to fix some metabolite lables
ax.YTickLabel = strrep(ax.YTickLabel,'Acetyl Isoleucine (Chemspider Id: 9964364)','Acetyl Isoleucine');
ax.YTickLabel = strrep(ax.YTickLabel,'Exchange of ','');
ax.YTickLabel = strrep(ax.YTickLabel,'3, 4-dihydroxy','3,4-dihydroxy');
ax.YTickLabel = strrep(ax.YTickLabel,'N-Acetyl-Tyrosine','N-Acetyl-L-tyrosine');
ax.YTickLabel = strrep(ax.YTickLabel,'Oxugen','Oxygen');
ax.YTickLabel = strrep(ax.YTickLabel,'Excahnge of ','');
ax.YTickLabel = strrep(ax.YTickLabel,'CYOR_u10mi','Oxidative phosphorylation (CYOR_u10mi)');
ax.YTickLabel = strrep(ax.YTickLabel,'NADH2_u10mi','NADH Dehydrogenase, Mitochondrial (NADH2_u10m)');

ax.TickLabelInterpreter='none';
ax.FontSize = 14;
switch param.labelType
    case 'metabolitePlatform'
        ax.YAxisLocation = 'right';
    case 'metabolite'
        ax.YAxisLocation = 'right';
    case 'platform'
        ax.YAxisLocation = 'left';
end

%         %right labels
%         plot(v,1:nnz(predictedExLOCB),'*r')
%         axL = gca;
%         axL.YAxisLocation = 'right';
%         axL.Color = 'none';
%         axL.Box = 'off';
%         yticks(1:length(measuredFluxes.mean))
%         axL.TickLabelInterpreter='latex';
%         axL.YColor ='black';
%         axL.FontSize = 10;
%         yt = yticks;
%         yticklabels(measuredFluxes.labelsR)

grid on
ax = gca;
ax.GridColor = [0 .5 .5];
ax.GridLineStyle = '--';
ax.GridAlpha = 0.5;
ax.Layer = 'top';

if 0
    xlim([-0.25 0.15])
    ylim([15.5 69.5])
    zoomed = 'zoom';
else
    zoomed = '';
end
if isempty(otherFluxes)
    descriptions = predDescription;
else
    descriptions = [predDescription '_vs_' otherDescription];
end

%%%% TODO %%% take the model name from the model structure
%     savefig(['iDN1_measured_vs_predicted_exchanges_' names '_' labelType zoomed])
%     saveas(gcf,['iDN1_measured_vs_predicted_exchanges_' names '_' labelType zoomed],'png')
if param.saveFigures
    savefig(['measured_vs_predicted_exchanges_' names '_' param.labelType zoomed])
    saveas(gcf,['measured_vs_predicted_exchanges_' names '_' param.labelType zoomed],'png')
end


function bouts = SegsToBouts(segs, moat, flength)
if ~exist('flength', 'var') || isempty(flength)
    flength = segs(end,2);
end
segsPlus = bsxfun(@plus, segs, [-moat moat]);
% remove gaps bigger than moat
candStarts = segsPlus(:,1);
candEnds = segsPlus(:,2);
dontStop = candStarts(2:end)<candEnds(1:end-1);
candEnds(find(dontStop)) = [];
candStarts(find(dontStop)+1) = [];
bouts = [candStarts candEnds];
% bouts(find((segsPlus(2:end,1) - segsPlus(1:end-1,2))<0),:) = []; % gap is too big
bouts(:,1) = max(1,bouts(:,1));
bouts(:,2) = min(flength,bouts(:,2));


function imdb = setup_imdb_0131(datasetDir, varargin)

suffix = '.jpg';
allframes_subdir = 'allFrames';

imdb.imageDir = datasetDir;
imdb.meta.sets = {'train', 'val', 'test'};

seqs =dir(datasetDir);
seqs = {seqs([seqs.isdir]).name};
seqs = setdiff(seqs, {'.', '..'});

classes = dir(fullfile(datasetDir, seqs{1}));
classes = {classes([classes.isdir]).name};
imdb.meta.classes = setdiff(classes, {'.', '..', allframes_subdir});

imdb.images.name = {};
imdb.images.time = [];
imdb.images.class = [];
for i = 1:numel(seqs),
  seqName = seqs{i};
  imdb.images.time = [imdb.images.time ; parse_seq_name(seqName,30/(24*3600),0,-1)]; % TODO fps wrong at 1st frame
  files = dir(fullfile(datasetDir, seqName, allframes_subdir, ['*' suffix]));
  fileNames = sort({files.name});
	fileNames = fileNames(2:end); % TODO fps wrong at 1st frame
  class_tbl = -1*ones(1,numel(fileNames));
  for c = 1:numel(imdb.meta.classes),
    className = imdb.meta.classes{c};
    files = dir(fullfile(datasetDir, seqName, className, ['*' suffix]));
    class_tbl(ismember(fileNames,{files.name})) = c;
  end
  assert(~any(class_tbl==-1));
  imdb.images.class = [imdb.images.class class_tbl];
  imdb.images.name = [imdb.images.name ...
    cellfun(@(s) fullfile(seqName,allframes_subdir,s), fileNames,'UniformOutput',false)];
end

imdb.images.id = 1:numel(imdb.images.name);

end

function tstr = parse_seq_name(str, off_1, off_2, off_3)

if ~exist('off_1','var') || isempty(off_1), off_1 = 0; end;
if ~exist('off_2','var') || isempty(off_2), off_2 = 0; end;
if ~exist('off_3','var') || isempty(off_3), off_3 = 0; end;

fmt_in = 'yymmddHHMMSS';
fmt_out = 'yyyy-mm-dd HH:MM:SS.FFF';

sep_locs = strfind(str,'_');
assert(numel(sep_locs)==2, 'Wrong format: %s', str);

tnum_1 = datenum(str(1:sep_locs(1)-1), fmt_in) + off_1;
tnum_2 = datenum(str(sep_locs(1)+1:sep_locs(2)-1), fmt_in) + off_2;
N = str2num(str(sep_locs(2)+1:end)) + off_3;
tnums = linspace(tnum_1, tnum_2, N);

tstr = datestr(tnums, fmt_out);

end


function kmlStruct = kml2struct(kmlFile)
% kmlStruct = kml2struct(kmlFile)
%
% Import a .kml file as a vector array of shapefile structs, with Geometry, Name,
% Description, Lon, Lat, and BoundaryBox fields.  Structs may contain a mix
% of points, lines, and polygons.
%
% .kml files with folder structure will not be presented as such, but will
% appear as a single vector array of structs.
%
%

[FID msg] = fopen(kmlFile,'rt');

if FID<0
    error(msg)
end

txt = fread(FID,'uint8=>char')';
fclose(FID);

expr = '<Placemark.+?>.+?</Placemark>';

objectStrings = regexp(txt,expr,'match');

Nos = length(objectStrings);

for ii = 1:Nos
    % Find Object Name Field
    bucket = regexp(objectStrings{ii},'<name.*?>.+?</name>','match');
    if isempty(bucket)
        name = 'undefined';
    else
        % Clip off flags
        name = regexprep(bucket{1},'<name.*?>\s*','');
        name = regexprep(name,'\s*</name>','');
        if strcmp(name(1:2),'<!')
            name = regexprep(name,'<![CDATA[\s*','');
            name = regexprep(name,']]>\s*','');
        end
    end

    % Find Object Description Field
    bucket = regexp(objectStrings{ii},'<description.*?>.+?</description>','match');
    if isempty(bucket)
        desc = '';
    else
        % Clip off flags
        desc = regexprep(bucket{1},'<description.*?>\s*','');
        desc = regexprep(desc,'\s*</description>','');

        % Clip off other flags and rearrange into rows
        if strcmp(desc(1:2),'<!')
            desc = regexprep(desc,'<![CDATA[\s*','');
            desc = regexprep(desc,']]>\s*','');
            % Splitting into rows
            I = find(desc == '<');
            if ~isempty(I)
                desc_c = cell(length(I)+1,1);
                desc_c{1} = desc(1:I(1)-1);
                for i = 2:length(I)
                    desc_c{i} = desc(I(i-1)+4:I(i)-1);
                end
                desc_c{end} = desc(I(end)+4:end);
            end
        end
    end

    % Find style
    bucket = regexp(objectStrings{ii},'<styleUrl.*?>.+?</styleUrl>','match');
    if isempty(bucket)
        StyleUrl = 'undefined';
    else
        % Clip off flags
        StyleUrl = regexprep(bucket{1},'<styleUrl.*?>\s*','');
        StyleUrl = regexprep(StyleUrl,'\s*</styleUrl>','');
    end

    geom = 0;
    % Identify Object Type
    if ~isempty(regexp(objectStrings{ii},'<Point', 'once'))
        geom = 1;
    elseif ~isempty(regexp(objectStrings{ii},'<LineString', 'once'))
        geom = 2;
    elseif ~isempty(regexp(objectStrings{ii},'<Polygon', 'once'))
        geom = 3;
    end

    switch geom
        case 1
            geometry = 'Point';
        case 2
            geometry = 'Line';
        case 3
            geometry = 'Polygon';
        otherwise
            geometry = '';
    end

    % Find Coordinate Field
    bucket = regexp(objectStrings{ii},'<coordinates.*?>.+?</coordinates>','match');
    % Clip off flags
    coordStr = regexprep(bucket{1},'<coordinates.*?>(\s+)*','');
    coordStr = regexprep(coordStr,'(\s+)*</coordinates>','');
    % Split coordinate string by commas or white spaces, and convert string
    % to doubles
    coordMat = str2double(regexp(coordStr,'[,\s]+','split'));
    % Rearrange coordinates to form an x-by-3 matrix

    [m,n] = size(coordMat);
    if m*n >= 3
        coordMat = reshape(coordMat,3,m*n/3)';
    end

    % define polygon in clockwise direction, and terminate
    if exist('poly2ccw')
        [Lat, Lon] = poly2ccw(coordMat(:,2),coordMat(:,1));
    else
        Lat = coordMat(:,2); Lon = coordMat(:,1);
    end
    if geom==3
        Lon = [Lon;NaN];
        Lat = [Lat;NaN];
    end

    % Create structure
    kmlStruct(ii).Geometry = geometry;
    kmlStruct(ii).Name = name;
    if exist('desc_c','var')
        kmlStruct(ii).Description = desc_c;
    else
        kmlStruct(ii).Description = desc;
    end
    kmlStruct(ii).Lon = Lon;
    kmlStruct(ii).Lat = Lat;
    kmlStruct(ii).BoundingBox = [[min(Lon) min(Lat);max(Lon) max(Lat)]];
    kmlStruct(ii).StyleUrl = StyleUrl;
end


function t=timediff(t1,t2)

t=t1.time-t2.time+t1.sec-t2.sec;

return


function [tasklist, taskfile] = LoadTaskFile_Reverb(taskFileRoot, dataset, datatype, distance, nCh, roomID)
switch dataset
    case 'train'
        datasetStr = 'tr';
    case 'dev'
        datasetStr = 'dt';
    case 'eval'
        datasetStr = 'et';
    otherwise
        fprintf('Error: unknown dataset: %s!\n', dataset);
        return;
end
if strcmpi(datatype, 'simu')
    datatypeStr = 'Sim';
else
    datatypeStr = 'Real';
end

chStr = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'};

if strcmp(distance, 'cln')
    if strcmp(dataset, 'train')
        tasklist = {};
    else
        taskfile = [taskFileRoot '/' num2str(nCh) 'ch/' datatypeStr 'Data_' datasetStr '_for_' ...
            distance '_room' num2str(roomID)];
        tasklist = my_cat(taskfile)';
    end
else
    for i=1:nCh
        if strcmp(dataset, 'train')
            taskfile{i} = [taskFileRoot '/' num2str(nCh) 'ch/' datatypeStr 'Data_' datasetStr '_for_' ...
                num2str(nCh) 'ch_' chStr{i}];
        else
            taskfile{i} = [taskFileRoot '/' num2str(nCh) 'ch/' datatypeStr 'Data_' datasetStr '_for_' ...
                num2str(nCh) 'ch_' distance '_room' num2str(roomID) '_' chStr{i}];
        end
        tasklist(i,:) = my_cat(taskfile{i});
    end
end
end


function varargout = lbpfliplr(varargin)
% VL_LBPFLIPLR  Flip LBP features left-right
%   F = VL_LBPFLIPLR(F) flips the LBP features F from left to
%   right.
%
%   Example::
%     The following instructions return results almost identical
%     assuming that the width of I is exactly divisible by the cell
%     size:
%       F = VL_LBP(FLIPLR(I), 8)
%       F_ = VL_LBPFLIPLR(VL_LBP(I), 8)
%
%   See also: VL_LBP(), VL_HELP().
[varargout{1:nargout}] = vl_lbpfliplr(varargin{:});


%% Analyze Sentiment Using FinBERT
% This example shows how to analyze sentiment of financial data using a
% pretrained FinBERT model.
%
% FinBERT is a sentiment analysis model trained on financial text data and
% fine-tuned for sentiment analysis. The model is based on the BERT-Base
% architecture.
%
% This example shows how to classify the sentiment of financial news
% reports using a pretrained FinBERT model.

%% Load Pretrained FinBERT Model
% Load a pretrained FinBERT model using the |finbert| function. The model
% consists of a tokenizer that encodes text as sequences of integers, and
% a structure of parameters.
mdl = finbert

%%
% View the FinBERT model tokenizer. The tokenizer encodes text as sequences
% of integers and holds the details of padding, start, separator and mask
% tokens.
tokenizer = mdl.Tokenizer

%% Analyze Sentiment in Text
% Create an string array of text data.
str = [
    "In an unprecendented move the stock market has hit new records today following news of a new vaccine."
    "Businesses in this sector suffer dramatic losses on the back of the pandemic."
    "The ship unloader is totally enclosed along the entire conveying line to the storage facilities."
    "Experts estimate the value of its remaining stake in the company at $ 27 million."
    "The company said that sales in the three months to the end of March slid to EUR86 .4 m US$ 113.4 m from EUR91 .2 m last year."
    "Finance experts calculate that it has lost EUR 4mn in the failed project."
    "They signed a large deal with an international industrial group which will deliver automation solutions and connectivity services."
    "Operating profit rose to EUR 5mn from EUR 2.8 mn in the fourth quarter of 2008"];

%%
% Encode the text data as a sequence of tokens using the FinBERT model
% tokenizer.
tokens = encode(tokenizer,str);

%%
% Pad the sequences to have the same length using the |padsequences|
% function. Specify the padding value to match the FinBERT model tokenizer.
X = padsequences(tokens,2,"PaddingValue",mdl.Tokenizer.PaddingCode);

%%
% Evaluate the sentiment and sentiment scores using the
% |finbert.sentimentModel| function.
[sentiment,scores] = finbert.sentimentModel(X,mdl.Parameters)


function varargout = scatter(q,varargin)
% plot function
%
% Input
%  q - Quaternion
%
% Options
%  rodrigues - plot in rodrigues space
%  axisAngle - plot in axis / angle
%

oP = newOrientationPlot(specimenSymmetry,specimenSymmetry,varargin{:});

[varargout{1:nargout}] = oP.plot(q,varargin{:});


function b = eq(f1,f2,varargin)
% ? sS1 == sS2
%
% Input
%  f1, f2 - @fibre
%
% Output
%  b - boolean
%
% Options
%  antipodal - include antipodal symmetry
%

if f1.CS ~= f2.CS || ...
    (length(f1)>1 && length(f2)>1 && any(size(f2)~=size(f1)))
  b = false;
  return
end

b = f1.h == f2.h & f1.r == f2.r;


%{
Copyright (c) 2015, Tom Mertens
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
%}

% This procedure loads a sequence of images
%
% Arguments:
%   'path', refers to a directory which contains a sequence of JPEG or PPM
%   images
%   'reduce' is an optional parameter that controls downsampling, e.g., reduce = .5
%   downsamples all images by a factor of 2.
%
% tom.mertens@gmail.com, August 2007
%

function I = load_images(path, reduce)

if ~exist('reduce')
    reduce = 1;
end

if (reduce > 1 || reduce <= 0)
    error('reduce must fulfill: 0 < reduce <= 1');
end

% find all JPEG or PPM files in directory
files = dir([path '/*.jpg']);
N = length(files);
if (N == 0)
    files = dir([path '/*.ppm']);
    N = length(files);
    if (N == 0)
        error('no files found');
    end
end

% allocate memory
sz = size(imread([path '/' files(1).name]));
r = floor(sz(1)*reduce);
c = floor(sz(2)*reduce);
I = zeros(r,c,3,N);

% read all files
for i = 1:N

    % load image
    filename = [path '/' files(i).name];
    im = double(imread(filename)) / 255;
    if (size(im,1) ~= sz(1) || size(im,2) ~= sz(2))
        error('images must all have the same size');
    end

    % optional downsampling step
    if (reduce < 1)
        im = imresize(im,[r c],'bicubic');
    end

    I(:,:,:,i) = im;
end


function [colors, msh, roiMask] = meshDrawROIs2(msh, rois, nodes, colors, update, edges);
% 'Draw' ROIs on a mesh as colors over different nodes,
% returning the updated mesh colors. Version for mrVista2.
%
% colors = meshDrawROIs2(msh, rois, <oldColors=mesh curvature>, ...
%                          <perim=1>, <update=0>);
%
% INPUTS:
% msh: mesh structure on which to display ROIs.
%
% rois: struct array of ROI objects. NOTE: the .coords field of each ROI
% should be relative to the gray nodes (e.g., I|P|R space or
% VOLUME{1}.coords).
%
% If you provide an ROI with the name 'mask', rather than rendering the
% ROI, it will be used as a mask to show only part of the data on the mesh.
%
% nodes: gray nodes matrix (as in VOLUME{1}.nodes). These can be obtained
% from mrGray .gray graphs (and readGrayGraph) or using mrgGrowGray.
% Apparently the latter fixes an old bug in mapping nodes.
%
% colors: existing mesh colors over which to superimpose the ROIs. If
% omitted, gets from mesh.
%
%
% update: flag to update the mesh colors with the new colors. Default is
% 0, don't update, just return colors.
%
% OUTPUTS:
%   colors: 4 x nVertices set of colors for the mesh, w/ ROI colors added.
%
%   msh: updated mesh structure, including any vertexGrayMaps and
%   connection matrices needed for the computation.
%
%   roiMask: logical mask of size 1 x nVertices indicating where ROIs are.
%
% ras, 07/06.
if isempty(rois), return; end
if ~exist('colors','var') | isempty(colors), colors = meshGet(msh, 'colors'); end
if ~exist('update','var') | isempty(update), update = 0;                      end

% get the ROI mapping mode: may want to make this
% part of mrmPreferences, but maybe this is enough:
prefs = mrmPreferences;
if isequal(prefs.layerMapMode, 'layer1')
    roiMapMode = 'layer1';
else
    roiMapMode = 'any';
end

%%%%%params
nNodes = size(nodes, 2);
nVertices = size(msh.initVertices, 2);
v2g = msh.vertexGrayMap;
if isempty(v2g)
    myErrorDlg('Need Vertex/Gray Map.');
elseif isequal(unique(v2g(:)), 0)
    myErrorDlg('Mesh doesn''t map to any data.');
elseif isequal(roiMapMode, 'any') & size(v2g, 1)==1
	% we only have the mapping to layer 1 -- we need the other layers
	try, vs = msh.mmPerVox; end   % voxel size
	if length(vs) < 3, vs = [1 1 1]; end    % back-compatibility
	msh.vertexGrayMap = mrmMapVerticesToGray(msh.initVertices, nodes, vs, edges);

end

% We need the connection matrix to compute clustering and smoothing, below.
conMat = meshGet(msh,'connectionmatrix');
if isempty(conMat)
    msh = meshSet(msh,'connectionmatrix',1);
    conMat = meshGet(msh,'connectionmatrix');
end

% initialize ROI mask if it's requested as an output:
if nargout>=3
    roiMask = logical(zeros(1, nVertices));
end

%%%%%%Main part:
% Substitute the ROI color for appropriate nearest neighbor vertices
for ii = 1:length(rois)

	%% find indices of those mesh vertices belonging to this ROI:
	roiVertInds = findROIVertices(rois(ii), nodes, v2g, roiMapMode);

	%% adjust the set of ROI vertices to show perimeter / full ROI
	roiVertInds = adjustPerimeter(roiVertInds, msh, prefs, rois(ii).fillMode);

    % update the colors to reflect the ROIs
    if(strcmp(rois(ii).name,'mask'))
        % Don't show the ROI- just use it to mask the data.
        oldColors = mrmGet(msh, 'colors');
        tmp = logical(ones(size(dataMask)));
        tmp(roiVertInds) = 0;
        colors(1:3,tmp) = oldColors(1:3,tmp);
        clear tmp;
    else
        if (ischar(rois(ii).color))
            [stdColorValues, stdColorLabels]  = meshStandardColors;
            jj = findstr(stdColorLabels, rois(ii).color);
            colVal = stdColorValues(:,jj);
        else
            % Check to see if we have a 3x1 vector.
            colVal = rois(ii).color(:);
            if (length(colVal)~=3)
                error('Bad ROI color');
            end
        end
        colors(roiVertInds,1:3) = repmat(colVal'*255, [length(roiVertInds) 1]);

        % build ROI mask if requested
        if nargout>=3
            roiMask(roiVertInds) = true;
        end
    end
end

% round and clip
colors(colors>255) = 255;
colors(colors<0) = 0;
colors = round(colors);

return
% /--------------------------------------------------------------------/ %

% /--------------------------------------------------------------------/ %
function roiVertInds = findROIVertices(roi, nodes, v2g, roiMapMode);
% Returns a list of indices to each vertex belonging to the current ROI.

% find the nodes in the segmentation mapping to this ROI
[x nodeInds] = ismember(round(roi.coords'), nodes([2 1 3],:)', 'rows');
nodeInds = nodeInds(nodeInds>0);

switch lower(roiMapMode)
	case 'layer1'
		% The following will give us *a* vertex index for each gray node.
		% However, the same gray node may map to multiple vertices, and we
		% want them all. (Otherwise, the color overlay will have holes.)
		% So, we loop until we've got them all.
		[nodesToMap roiVertInds] = ismember(nodeInds, v2g(1,:));
		roiVertInds = roiVertInds(roiVertInds>0);
		while(any(nodesToMap))
			v2g(1,roiVertInds) = 0;
			[nodesToMap I] = ismember(nodeInds, v2g(1,:));
			roiVertInds = [roiVertInds; I(I>0)];
		end

	case 'any'
		% if any of the nodes mapping to a given vertex are in the
		% ROI, include that vertex for drawing the ROI
		[I vertInds] = ismember(v2g, nodeInds);
		roiVertInds = find(sum(I)>0); % find columns w/ at least 1 member

	case 'data'
		% take ROI value from the same nodes as the data mapping,
		% rounding up (e.g., for 'mean' data mapping, will behave
		% like 'any'

	otherwise
		error('Invalid ROI Draw Mode preference.')
end

return
% /--------------------------------------------------------------------/ %

% /--------------------------------------------------------------------/ %
function roiVertInds = adjustPerimeter(roiVertInds, msh, prefs, fillMode);
% Sometimes we want to dilate the ROIs a bit. The following does this
% by finding all the neighboring vertices for all the ROI vertices (now
% stored in roiVertInds). The sparse connection matrix comes in handy
% once again, making this a very fast operation.

% We need the connection matrix
conMat = meshGet(msh,'connectionmatrix');
if isempty(conMat)
    msh = meshSet(msh,'connectionmatrix',1);
    conMat = meshGet(msh,'connectionmatrix');
end

%% draw perimeter or fill in the ROI?
if isequal(fillMode, 'perimeter')
	% Draw a perimeter only
	origROI = roiVertInds;

	perimThickness = max(0, prefs.roiDilateIterations);

	if perimThickness > 0
		for t = 1:perimThickness
			neighbors = conMat(:,roiVertInds);
			[roiVertInds cols] = find([neighbors]);
			roiVertInds = unique(roiVertInds);
		end

		% Subtract the original from the dilated version.
		roiVertInds = setdiff(roiVertInds, origROI);

	else    % any way to make the perimeter thinner?
		neighbors = conMat(:,roiVertInds);
		[roiVertInds cols] = find([neighbors]);
		roiVertInds = setdiff(unique(roiVertInds), origROI);

	end

else
	% We can dilate the area if we are not also rendering the perimeter...
	for t = 1:prefs.roiDilateIterations
		neighbors = conMat(:,roiVertInds);
		[roiVertInds cols] = find([neighbors]);
		roiVertInds = unique(roiVertInds);
	end
end

return


function out = getProbCube(outp, imName, numClass)
% function out = getProbCube(outp, i, numClass)
	dt = load(fullfile(outp, strcat(imName, '.mat')));
	for j = 1:size(dt.scores, 1),
		si = dt.scores(j,:);
		out(:,:,j) = si(dt.superpixels);
	end
end