%  Author: Relja Arandjelovic (relja@relja.info)

% This file contains a few examples of how to train and test CNNs for place recognition, refer to README.md for setup instructions, and to our project page for all relevant information (e.g. our paper): http://www.di.ens.fr/willow/research/netvlad/

%  The code samples use the GPU by default, if you want to use the CPU instead (very slow especially for training!), add `'useGPU', false` to the affected function calls (trainWeakly, addPCA, serialAllFeats, computeRepresentation)

% For a tiny example of running the training on a small dataset, which takes only a few minutes to run, refer to the end of this file.

% Set the MATLAB paths
setup;

error('Don''t run this script, it is only meant as a collection of useful commands');

% ---------- Use/test our networks

% Load our network
netID= 'vd16_tokyoTM_conv5_3_vlad_preL2_intra_white';
paths= localPaths();
load( sprintf('%s%s.mat', paths.ourCNNs, netID), 'net' );
net= relja_simplenn_tidy(net);

%  Compute the image representation by simply running the forward pass using
% the network `net` on the appropriately normalized image
% (see `computeRepresentation.m`).

im= vl_imreadjpeg({which('football.jpg')}); im= im{1}; % slightly convoluted because we need the full image path for `vl_imreadjpeg`, while `imread` is not appropriate - see `help computeRepresentation`
feats= computeRepresentation(net, im); % add `'useGPU', false` if you want to use the CPU

% To compute representations for many images, use the `serialAllFeats` function
% which is much faster as it uses batches and it moves the network to
% the GPU only once:
%
%          serialAllFeats(net, imPath, imageFns, outputFn);
%
%  `imageFns` is a cell array containing image file names relative to the `imPath` (i.e. `[imPath, imageFns{i}]` is a valid JPEG image), the representations are saved in binary format (single 4-byte floats). Batch size used for computing the forward pass can be changed by adding the `batchSize` parameter, e.g. `'batchSize', 10`. Note that if your input images are not all of same size (they are in place recognition datasets), you should set `batchSize` to 1.

%  To test the network on a place recognition dataset, set up the test dataset
dbTest= dbTokyo247();

% Set the output filenames for the database/query image representations
paths= localPaths();
dbFeatFn= sprintf('%s%s_%s_db.bin', paths.outPrefix, netID, dbTest.name);
qFeatFn = sprintf('%s%s_%s_q.bin', paths.outPrefix, netID, dbTest.name);

% Compute db/query image representations
serialAllFeats(net, dbTest.dbPath, dbTest.dbImageFns, dbFeatFn, 'batchSize', 10); % adjust batchSize depending on your GPU / network size
serialAllFeats(net, dbTest.qPath, dbTest.qImageFns, qFeatFn, 'batchSize', 1); % Tokyo 24/7 query images have different resolutions so batchSize is constrained to 1

% Measure recall@N
[recall, ~, ~, opts]= testFromFn(dbTest, dbFeatFn, qFeatFn);
plot(opts.recallNs, recall, 'ro-'); grid on; xlabel('N'); ylabel('Recall@N');

% ---------- Full train and test example: Tokyo
% Train: Tokyo Time Machine, Test: Tokyo 24/7

% Set up the train/val datasets
dbTrain= dbTokyoTimeMachine('train');
dbVal= dbTokyoTimeMachine('val');
lr= 0.0001;

% --- Train the VGG-16 network + NetVLAD, tuning down to conv5_1
sessionID= trainWeakly(dbTrain, dbVal, ...
    'netID', 'vd16', 'layerName', 'conv5_3', 'backPropToLayer', 'conv5_1', ...
    'method', 'vlad_preL2_intra', ...
    'learningRate', lr, ...
    'doDraw', true);

% Get the best network
% This can be done even if training is not finished, it will find the best network so far
[~, bestNet]= pickBestNet(sessionID);

% Either use the above network as the image representation extractor (do: finalNet= bestNet), or do whitening (recommended):
finalNet= addPCA(bestNet, dbTrain, 'doWhite', true, 'pcaDim', 4096);

% --- Test

% Set up the test dataset
dbTest= dbTokyo247();

% Set the output filenames for the database/query image representations
paths= localPaths();
dbFeatFn= sprintf('%s%s_ep%06d_%s_db.bin', paths.outPrefix, finalNet.meta.sessionID, finalNet.meta.epoch, dbTest.name);
qFeatFn = sprintf('%s%s_ep%06d_%s_q.bin', paths.outPrefix, finalNet.meta.sessionID, finalNet.meta.epoch, dbTest.name);

% Compute db/query image representations
serialAllFeats(finalNet, dbTest.dbPath, dbTest.dbImageFns, dbFeatFn, 'batchSize', 10); % adjust batchSize depending on your GPU / network size
serialAllFeats(finalNet, dbTest.qPath, dbTest.qImageFns, qFeatFn, 'batchSize', 1); % Tokyo 24/7 query images have different resolutions so batchSize is constrained to 1

% Measure recall@N
[recall, ~, ~, opts]= testFromFn(dbTest, dbFeatFn, qFeatFn);
plot(opts.recallNs, recall, 'ro-'); grid on; xlabel('N'); ylabel('Recall@N');

% --- Test smaller dimensionalities:

% All that needs to be done (only valid for NetVLAD+whitening networks!)
% to reduce the dimensionality of the NetVLAD representation below 4096 to D
% is to keep the first D dimensions and L2-normalize.
% This is done automatically in `testFromFn` using the `cropToDim` option:

cropToDims= [64, 128, 256, 512, 1024, 2048, 4096];
recalls= [];
plotN= 5;
figure;

for iCropToDim= 1:length(cropToDims)
    cropToDim= cropToDims(iCropToDim);
    relja_display('D= %d', cropToDim);
    [recall, ~, ~, opts]= testFromFn(dbTest, dbFeatFn, qFeatFn, [], 'cropToDim', cropToDim);

    whichRecall= find(opts.recallNs==plotN);
    recalls= [recalls, recall(whichRecall)];
    hold off;
    semilogx( cropToDims(1:iCropToDim), recalls, 'bo-');
    set(gca, 'XTick', cropToDims(1:iCropToDim));
    xlabel('Number of dimensions'); ylabel(sprintf('Recall@%d', plotN)); grid on;
    drawnow;
end

% ---------- Pittsburgh examples

% Pitts250k or Pitts30k?
doPitts250k= false;

if doPitts250k
    % Pittsburgh 250k
    lr= 0.0001;
else
    % Pittsburgh 30k
    lr= 0.001;
end

dbTrain= dbPitts(doPitts250k, 'train');
dbVal= dbPitts(doPitts250k, 'val');
dbTest= dbPitts(doPitts250k, 'test');

% Now just run the same code as above for Tokyo

% ---------- Miscellaneous

% --- Other potentially useful commands for training

% caffe vlad: backprop down to conv2

sessionID= trainWeakly(dbTrain, dbVal, ...
    'netID', 'caffe', 'layerName', 'conv5', 'backPropToLayer', 'conv2', ...
    'method', 'vlad_preL2_intra', ...
    'learningRate', lr, ...
    'doDraw', true);

% vgg16 max: backprop down to conv5

sessionID= trainWeakly(dbTrain, dbVal, ...
    'netID', 'vd16', 'layerName', 'conv5_3', 'backPropToLayer', 'conv5_1', ...
    'method', 'max', ...
    'learningRate', lr, ...
    'doDraw', true);

% --- Constructing an off-the-shelf network:

dbTrain= dbPitts(false, 'train'); % or dbTokyoTimeMachine('train');
opts.netID= 'vd16'; % or 'caffe'
opts.layerName= 'conv5_3'; % or whatever layer you want, for caffe: 'conv5'
opts.method= 'vlad_preL2_intra'; % or 'max', 'avg'
net= loadNet(opts.netID, opts.layerName); % load the network and crop at desired layer
net= addLayers(net, opts, dbTrain); % add the NetVLAD/Max/Avg layer
net= addPCA(net, dbTrain, 'doWhite', true, 'pcaDim', 4096, 'batchSize', 10); % add whitening (for non-vlad methods pick a smaller pcaDim value)

% ---------- Tiny dummy training example

% This is just to see if you got all the dependencies and configurations set up correctly.
% Get a tiny version of the Tokyo Time Machine dataset from our research page ( www.di.ens.fr/willow/research/netvlad/ ) as well as the dataset specification. Point paths.dsetRootTokyoTM in localPaths.m to its location. Run the code below to train max pooling on top of AlexNet for this tiny dataset:

dbTrain= dbTiny('train'); dbVal= dbTiny('val');

trainWeakly(dbTrain, dbVal, ...
    'netID', 'caffe', 'layerName', 'conv5', ...
    'method', 'max', 'backPropToLayer', 'conv5', ...
    'margin', 0.1, ...
    'batchSize', 4, 'learningRate', 0.01, 'lrDownFreq', 3, 'momentum', 0.9, 'weightDecay', 0.1, 'compFeatsFrequency', 10, ...
    'nNegChoice', 30, 'nNegCap', 10, 'nNegCache', 10, ...
    'nEpoch', 10, ...
    'epochTestFrequency', 1, 'test0', true, ...
    'nTestSample', inf, 'nTestRankSample', 40, ...
    'saveFrequency', 15, 'doDraw', true, ...
    'useGPU', true, 'numThreads', 12, ...
    'info', 'tiny test');


function [clusters, subcl] = extract_contrast_data(P, clusters, varargin)
% This function does not plot,  but separates subclusters using pca / cluster_princomp.m
% based on pattern across all conditions,  covariance (not correlation),
%
% :Usage:
% ::
%
%    function [clusters, subcl] = extract_contrast_data(P, clusters)
%
% :Inputs:
%
%   **P:**
%        cell array of strings containing image file names (data extracted from these)
%
%   **clusters:**
%
% :Optional Inputs:
%
%   **subclusters:**
%        to get sub-clustering based on pca and clustering of voxels
%        add the string 'subclusters' as an input argument
%
%   **cell array:** of strings with condition names
%
%   **split:**
%        value is 1: to split into 2 plots (first half and last half of P)
%
%        value is 2: to plot individual subjects over bars
%
%   **center:**
%        center parmeter values in plot (subtract row means)
%        this gives closer to correct "within subjects" error bars
%        and may be used when the overall parameter values have no meaning
%
%   **covs:**
%        followed by between-subject covariates (e.g.,  behavioral regressors)
%        plots remove these before plotting means and std. errors
%
%   **max:**
%        to make plots based on max z-values within region for each dataset P
%        not compatible with 'split' and 'center' (ignores these commands)
%        right now,  special for inhib - see also inhib2_cluster_barplot (good function)
%
%   **indiv:**
%        to threshold based on individual t-statistics or contrast values
%
%        FOLLOW with cell array of t-images or con images -- usually,  there will be one cell,  with images
%        for each subject in rows,  to define voxels for each ss.
%        BUT Tnames can be the same length as
%        contrast images,  one t-img per subject per contrast,  if
%        desired.
%
% :Outputs:
%
%   clusters struture,  with CONTRAST substructure added
%   substructure contains data extracted and image file names
%
% This program uses XYZmm millimeter coordinates in clusters to find voxels
% So clusters and data files may have different dimensions.
%
% :Examples:
% ::
%
%     cl = extract_contrast_data(EXPT.SNPM.P, cl, 'indiv', EXPT.FILES.Timgs{1});
%     cluster_barplot(EXPT.SNPM.P(7:12), clusters(2:3), {'ObjE' 'AttE' 'InteractE' 'ObjI' 'AttI' 'InteractI'}, 'split')
%     [clusters, subclusters] = cluster_barplot(EXPT.SNPM.P(17:24), clusters, 'subclusters', 'split')
%     RS2_8vs2_placeboCP = cluster_barplot(EXPT.SNPM.P([8 10 12 14 16]), RS2meta, 'indiv', T);
%
% also see mask2clusters.m,  a simpler version that just extracts clusters from a mask file.
%
% ..
%    by Tor Wager,  5/15/03
%    modified 3/19/04 by Tor to add individual subject plots
% ..

diary off

% -------------------------------------------------
% * Set up optional inputs
% -------------------------------------------------
splitat=0; dosubcl = 0; docenter = 0; covs = []; domax = 0; doindiv = 0;
for j = 1:length(P), cnames{j}=num2str(j); end

for i = 1:length(varargin),
    if iscell(varargin{i}),  cnames = varargin{i};  %varargin{i} = [];
    elseif strcmp('split', varargin{i}),  if varargin{i+1}==2,  splitat=2; else  splitat=round(length(P)./2);  end
    elseif strcmp('subclusters', varargin{i}),  dosubcl = 1;  disp('Finding sub-clusters')
    elseif strcmp('center', varargin{i}), docenter = 1;
    elseif strcmp('covs', varargin{i}), covs = varargin{i+1};
    elseif strcmp('max', varargin{i}), domax = 1;
    elseif strcmp('indiv', varargin{i}), doindiv = 1;
        Tnames = varargin{i+1}; varargin{i+1} = 'saved.';
    end
end

% convert to old format if necessary
if isa(clusters, 'region')
    clusters = region2struct(clusters);
end

if doindiv
    % replicate names,  in case only one set is entered
    if ~iscell(Tnames),
        Tnamestmp = Tnames; Tnames={};Tnames{1}=Tnamestmp;
    end
        if length(Tnames) < length(cnames),
            disp('Found only one set of T-images; replicating for each set of data extracted.');
        end
        while length(Tnames) < length(cnames), Tnames{end+1} = Tnames{1}; end
end

%if ~isempty(strmatch('split', varargin)),  splitat=round(length(P)./2);
%else  splitat=length(P); end
subcl = [];
if isempty(clusters)
    disp('Clusters is empty: No voxels for which to get data.');
    return
end

% -------------------------------------------------
% * load image files
% -------------------------------------------------
fprintf(1, 'Loading images.')
CLU = clusters2CLU(clusters);
dat = cell(1, length(clusters));
alldat = cell(1, length(clusters));

V = spm_vol(P{1}(1, :));
if any(any(V.mat - CLU.M)),
    disp('CLUSTERS and IMAGE DIMENSIONS DIFFERENT!  Resizing clusters.'),
    %CLU = transform_coordinates(CLU, V.mat);
    V.M = V.mat;
    for i = 1:length(clusters),
        clusters(i).XYZ = mm2voxel(clusters(i).XYZmm, V)'; clusters(i).M=V.mat;
        clusters(i).voxSize = diag(V.M(1:3, 1:3))';
        clusters(i).XYZmm = voxel2mm(clusters(i).XYZ, clusters(i).M);
        clusters(i).Z = ones(1, size(clusters(i).XYZ, 2));
    end
    CLU = clusters2CLU(clusters);
    for i = 1:length(clusters), clusters(i).u=CLU.u;clusters(i).VOX=CLU.VOX; end
    doslow = 1;
else
    doslow=0;
end

% check if any contiguous clusters we lose
%tmpCL = tor_extract_rois([], CLU, CLU);
%if length(tmpCL) < length(clusters),  doslow = 1;  end
%clear tmpCL

% reslice clusters if necessary.
if doslow
    clusters = cluster_interp(clusters, P{1}(1, :), 1);
    doslow = 0;
end

for i = 1:length(P)

    %if ~doslow
        % fast way: if all voxel sizes match
        cl{i} = tor_extract_rois(P{i}, clusters);  %CLU, CLU);
    %end

    % concatenated data for each cluster
    for j = 1:length(clusters)

        %if doslow
            % slow way: to keep clusters separate that otherwise touch
        %    cl{i}(j) = tor_extract_rois(P{i}, clusters(j), CLU);
        %end

        % alldat{cluster} is data x voxels,  data is subj within conditions strung
        % together
        alldat{j} = [alldat{j}; cl{i}(j).all_data];

        if length(cl{i}(j).timeseries) < size(dat{j}, 1),
            cl{i}(j).timeseries = [cl{i}(j).timeseries; NaN*zeros(size(dat{j}, 1)-length(cl{i}(j).timeseries), 1)];
        end

        dat{j} = [dat{j} cl{i}(j).timeseries];

    end

end % loop through P

%V{1}(1).M = V{1}(1).mat;
%imgdims = size(vols{1});
%mask = zeros(imgdims(1:3));

% -------------------------------------------------
% * subclusters
% -------------------------------------------------
for i = 1:length(clusters)

    if dosubcl && clusters(i).numVox > 4,
        % -------------------------------------------------
        % * subclusters
        % -------------------------------------------------

        [subcl{i}] = getsubcl(clusters(i), alldat{i}, length(P), size(P{1}, 1), cnames, splitat, i, docenter, covs);  % does subclusters within it
        if length(subcl{i}) > 1,
            subcluster_montage(subcl{i});    % now plot all subcluster locations
            saveas(gcf, ['cl' num2str(i) '_subcl_montage'], 'fig')
            saveas(gcf, ['cl' num2str(i) '_subcl_montage'], 'tif')
        end
    else

        % no subclusters

        sterr = nanstd(dat{i}) ./ sqrt(size(dat{i}, 1));

        if isfield(clusters, 'shorttitle'), mytit = clusters(i).shorttitle;
        else   mytit = clusters(i).title;
        end

        str = sprintf('%s %2.0f (%3.0f, %3.0f, %3.0f),  %3.0f voxels',  ...
        mytit, i, clusters(i).mm_center(1),  ...
        clusters(i).mm_center(2), clusters(i).mm_center(3),  clusters(i).numVox);

        if domax
            % set up
            len = size(clusters(1).CONTRAST.data, 1);
            tmpcl = []; tmpcl2 = [];
            for j = 1:round(size(alldat{1}, 1) ./ len)
                tmpcl{j}(i) = clusters(i); tmpcl{j}(i).all_data = alldat{i}(1+(j-1)*len:j*len, :);
                tmpcl{j}(i).timeseries = mean(tmpcl{j}(i).all_data, 2);

                [sterr, tmp] = ste(tmpcl{j}(i).all_data);
                clusters(i).Z(j, :) = spm_t2z(tmp, size(tmpcl{j}(i).all_data, 1)-1);

                wh = find(tmp == max(tmp)); wh = wh(1);
                clusters(i).CONTRAST.grp_peakdat(j, :) = tmpcl{j}(i).all_data(:, wh);

                %tmpcl2{j}(i) = cluster_ttest(tmpcl{j}(i), covs);  % add covariate here if necessary
            end
            % needs checking and/or debugging
            %inhib2_barplot(clusters(i), tmpcl2{1}(i), tmpcl2{2}(i), tmpcl2{3}(i), 'common');
            keyboard
        end

        %f = makefigure(dat{i}, str, cnames, splitat, docenter, covs);
        %saveas(gcf, ['cl' num2str(i) '_bar'], 'fig')
        %saveas(gcf, ['cl' num2str(i) '_bar'], 'tif')

        clusters(i).CONTRAST.files = P;
        clusters(i).CONTRAST.data = dat{i};
        clusters(i).CONTRAST.all_data = alldat{i};

        if dosubcl
            disp('Fewer than 5 voxels; skipping subclustering.')

            % make all field names match.
            % won't work if this is the first one!
            subcl{i} = subcl{1}(1);
            N = fieldnames(subcl{i});
            for NN = 1:length(N)
                eval(['subcl{i}(1).' N{NN} ' = [];'])
            end

            N = fieldnames(clusters(i));
            for NN = 1:length(N)
                eval(['subcl{i}(1).' N{NN} ' = clusters(i).' N{NN}])
            end

        end

        %close all

    end

    clusters(i).covs = covs;
end

fprintf(1, '\n')

% extract spatial peak data and locations
for i = 1:length(cl)
    cltmp = extract_ind_peak([], cl{i});
    for j = 1:length(cltmp)
        clusters(j).CONTRAST.peakdata{i} = cltmp(j).peakdata;
        clusters(j).CONTRAST.peakXYZ{i} = cltmp(j).peakXYZ;
        clusters(j).CONTRAST.peakXYZmm{i} = cltmp(j).peakXYZmm;
    end
end

% extract individual significant region center of mass
if doindiv
    disp(['getting individual significance regions'])
    disp(['Found ' num2str(size(Tnames{1}, 1))  ' T-images to get regions from'])
    indpeak = [];

    reverse_vals = input('Enter 0 to get most positive voxels for each image, or 1 to get most negative ones: ');

    for c = 1:size(Tnames, 2)    % for each contrast tested

        for i = 1:size(Tnames{1}, 1) % for each subject

            % pass in clusters with all data saved for contrast c,  Tnames
            % for contrast c; timeseries saves data from this

            [tmp] = cluster_tmask(cl{c}, Tnames{c}(i, :), i, alldat, reverse_vals);
            cl{c} = tmp;

        end

        % put stuff where it belongs
        for j = 1:length(clusters)

            clusters(j).CONTRAST.indiv_data(:, c) = cl{c}(j).timeseries;
            clusters(j).CONTRAST.indivXYZ{c} = cl{c}(j).INDIV.center;
            clusters(j).CONTRAST.indivXYZmm{c} = cl{c}(j).INDIV.mm_center;
            clusters(j).CONTRAST.indiv_sig{c} = cl{c}(j).INDIV.sigt;
            clusters(j).CONTRAST.indivXYZall{c} = cl{c}(j).INDIV.XYZ;
            clusters(j).CONTRAST.tname = cl{c}(j).INDIV.tname;

        end

    end

end

return

% -----------------------------------------------------------------------------
% * sub-functions
% -----------------------------------------------------------------------------

    function [subclusters] = getsubcl(clusters, adat, numimglists, numimgs, varargin)
    % always pass in only one cluster in clusters!! (vec of length 1)
    % if > 1 output,  subclusters is created and plotted here
    % if subclusters,  pass in cnames and splitat

    if length(varargin) > 0,  cnames = varargin{1};  splitat = varargin{2};
        clnum=varargin{3};  docenter = varargin{4};
    end
    if length(varargin) > 4,  covs = varargin{5}; else  covs = []; end

            % don't scale if this is across subjects,  because voxels
            % with more variation across ss SHOULD drive the pca
            %adat{j} = scale(adat{j});   % scale so high-noise voxels
            %                            % don't drive the pca

        % separate subclusters
        subclusters.CONTRAST = [];   % add for consistency of field names
        clusters.all_data = adat;

        [clusters, subclusters] = cluster_princomp(clusters, []);   %  , 0, 0, 1);

        subclusters = subclusters{1};

        % re-format averages within sub-regions for plotting

            for j = 1:length(subclusters)
                vind = 1;
                if size(subclusters(j).all_data, 2) > 1,
                    tmp = nanmean(subclusters(j).all_data')';
                else
                    tmp = subclusters(j).all_data;
                end

                for k = 1:numimglists
                    subclusters(j).CONTRAST.dat(:, vind) = ...
                        tmp((k-1)*numimgs+1:k*numimgs);
                        vind = vind + 1;
                end

                % now plot it
                sterr = nanstd(subclusters(j).CONTRAST.dat) ./ sqrt(size(subclusters(j).CONTRAST.dat, 1));
                str = sprintf('Cl %3.0f (%4.0f vox) subcl at (%3.0f, %3.0f, %3.0f),  %3.0f voxels',  ...
                clnum, clusters.numVox, subclusters(j).mm_center(1),  ...
                subclusters(j).mm_center(2), subclusters(j).mm_center(3),  subclusters(j).numVox);

                f = makefigure(subclusters(j).CONTRAST.dat, str, cnames, splitat, docenter, covs);
                saveas(gcf, ['cl' num2str(clnum) '_subc' num2str(j) '_bar'], 'fig')
                saveas(gcf, ['cl' num2str(clnum) '_subc' num2str(j) '_bar'], 'tif')
            end

    return

    function f = makefigure(dat, varargin)

    if length(varargin) > 1, cnames=varargin{2}; else  cnames = []; end
    if length(varargin) > 2, splitat=varargin{3}; else  splitat = 0; end
    if length(varargin) > 3, docenter=varargin{4}; else  docenter = 0; end
    if length(varargin) > 4, covs = varargin{5}; covs(:, end+1) = 1; else  covs = []; end
    if covs == 1,  covs = []; end    % if empty,  make it empty!

    dat1 = dat;     % save original data for ind subj plot

    if ~isempty(covs),
        covs(:, 1:end-1) = scale(covs(:, 1:end-1));
        for i = 1:size(dat, 2),
            b = pinv(covs) * dat(:, i);
            r = dat(:, i) - covs * b + b(end);
            dat(:, i) = r;
        end
    end

    if docenter,
        disp('Centering rows of data for plotting.')
        dat = dat - repmat(nanmean(dat')', 1, size(dat, 2));

    end

    doprintt = 1;

    if doprintt
        % print t values
        for i = 1:size(dat, 2)
            [h, p, ci, stats] = ttest(dat(:, i));
            nums{i} = sprintf('%3.2f', stats.tstat);
        end
    end

    sterr = nanstd(dat) ./ sqrt(size(dat, 1));
    dat = nanmean(dat);
    mysum = sign(dat) .* (sterr + abs(dat) + .15 .* abs(dat));

    if splitat > 2,
        f = figure('Color', 'w');

        h(1) = subplot(1, 2, 1); set(gca, 'FontSize', 16); hold on; grid on;
        bar(dat(1:splitat)); tor_bar_steplot(dat(1:splitat), sterr(1:splitat), {'b'});
        set(gca, 'XTick', 1:splitat)
        xlabel('Conditions', 'FontSize', 18), ylabel('fMRI Signal', 'FontSize', 18)
        if length(varargin) > 1,  set(gca, 'XTickLabel', varargin{2}(1:splitat)), end
        if length(varargin) > 0,  title(varargin{1}, 'FontSize', 20), end

        for i = 1:splitat,  text(i-.5, mysum(i), nums{i}, 'Color', 'k', 'FontWeight', 'b', 'FontSize', 14); end

        h(2) = subplot(1, 2, 2); set(gca, 'FontSize', 16); hold on; grid on;
        bar(dat(splitat+1:end)); tor_bar_steplot(dat(splitat+1:end), sterr(splitat+1:end), {'b'});
        set(gca, 'XTick', 1:length(dat)-splitat)
        xlabel('Conditions', 'FontSize', 18)
        if length(varargin) > 1,  set(gca, 'XTickLabel', varargin{2}(splitat+1:end)), end

        for i = 1:length(dat)-splitat,  text(i, mysum(splitat+i), nums{splitat+i}, 'Color', 'k', 'FontWeight', 'b', 'FontSize', 14); end

        equalize_axes(h);
        set(gcf, 'Position', [195         308        1259         674]),  drawnow

    elseif splitat == 2

        % ------------------------
        % this plot does individual subject estimates
        % ------------------------
        barplot_columns(dat1)

        % add reg lines,  if covs and sig
        for i = 1:size(dat1, 2)
            [B, dev, stat]=glmfit(covs(:, 1), dat1(:, i)); tmp = corrcoef(covs(:, 1), dat1(:, i));
            fprintf(1, 'Cond. %3.0f: Bo: t = %3.2f,  se = %3.4f,  p = %3.4f  B1: r = %3.2f,  t = %3.2f,  se = %3.4f,  p = %3.4f\n',  ...
                i, stat.t(1), stat.se(1), stat.p(1),  tmp(1, 2),  stat.t(2), stat.se(2), stat.p(2));

        end

        if ~isempty(cnames),  set(gca, 'XTickLabel', cnames), end

    else

        % ------------------------
        % standard bar plot
        % ------------------------

        f = figure('Color', 'w'); set(gca, 'FontSize', 16); %hold on; grid on;
        h = bar(dat); set(h, 'FaceColor', [.7 .7 .7])
        tor_bar_steplot(dat, sterr, {'k'});

        doinhib2 = 0;
        if doinhib2
            % special insert for inhib2 (triple inhibition)
            set(gca, 'XTickLabel', {'GNG' 'Flanker' 'SRC' 'Saccade'})
        end

        dointext = 0;
        if dointext
            % special insert for intext
            cla
            xp = dat; h = bar([xp(1:3);xp(4:6)], 'grouped'); cm = [0 1 0;1 0 0; 0 0 1];colormap(cm)
            xe = sterr;
            tor_bar_steplot([xp(1:3)],  xe(1:3), {'k'}, .55, .225)
            tor_bar_steplot([xp(4:6)],  xe(4:6), {'k'}, 1.55, .225)
            set(gca, 'FontSize', 16, 'XTickLabel', {'External' 'Internal'});legend(h, {'Object switching' 'Attribute Switching' 'Interaction'})
            ylabel('BOLD Contrast')
        end

        %set(gca, 'XTick', 1:size(dat, 2))
        %xlabel('Conditions', 'FontSize', 18), ylabel('fMRI Signal', 'FontSize', 18)
        %if length(varargin) > 1,  set(gca, 'XTickLabel', varargin{2}), end
        if length(varargin) > 0,  title(varargin{1}, 'FontSize', 20), end

        set(gcf, 'Position', [464   283   930   827]),  drawnow

    end

    return


function out_projector_fif(FifFile, ChannelNames, Projectors)
% OUT_PROJECTOR_FIF: Saves a list of SSP projectors in Brainstorm format in a FIF file.

% @=============================================================================
% This function is part of the Brainstorm software:
% https://neuroimage.usc.edu/brainstorm
%
% Copyright (c) University of Southern California & McGill University
% This software is distributed under the terms of the GNU General Public License
% as published by the Free Software Foundation. Further details on the GPLv3
% license can be found at http://www.gnu.org/copyleft/gpl.html.
%
% FOR RESEARCH PURPOSES ONLY. THE SOFTWARE IS PROVIDED "AS IS," AND THE
% UNIVERSITY OF SOUTHERN CALIFORNIA AND ITS COLLABORATORS DO NOT MAKE ANY
% WARRANTY, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF
% MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, NOR DO THEY ASSUME ANY
% LIABILITY OR RESPONSIBILITY FOR THE USE OF THIS SOFTWARE.
%
% For more information type "brainstorm license" at command prompt.
% =============================================================================@
%
% Authors: Francois Tadel, 2012

% Get fiff constants
global FIFF;
if isempty(FIFF)
    FIFF = fiff_define_constants();
end

% Convert projectors to FIF format
projs = repmat(struct('kind',[], 'active',[], 'desc',[], 'data', []), 0);
% Loop on all the projector categories
for iCat = 1:length(Projectors)
    % Skip projectors that are not selected
    if (Projectors(iCat).Status == 0)
        continue;
    end
    % Get the selected components for this projector category
    iSelComp = find(Projectors(iCat).CompMask);
    % Save each component as a projector in the FIF file
    for i = 1:length(iSelComp)
        iComp = iSelComp(i);
        iProj = length(projs) + 1;
        projData = Projectors(iCat).Components(:,iComp)';
        iRows    = find(projData);
        projs(iProj).kind   = FIFF.FIFFV_PROJ_ITEM_FIELD;
        projs(iProj).active = (Projectors(iCat).Status == 2);
        projs(iProj).desc   = Projectors(iCat).Comment;
        projs(iProj).data.nrow      = 1;
        projs(iProj).data.ncol      = length(iRows);
        projs(iProj).data.row_names = [];
        projs(iProj).data.col_names = ChannelNames(iRows);
        projs(iProj).data.data      = projData(iRows);
    end
end

% Write FIF file
fid = fiff_start_file(FifFile);
fiff_write_proj(fid, projs);
fiff_end_file(fid);


function metGroupCont = createGroupContributionStruct(primaryFile, pH, secondaryFile)
% Generates a matlab structure out of the tab delimited group contribuion data
%
% The matlab structure with the group contibution data for each
% metabolite uses the `primaryFile` file in preference to the `secondaryFile` file but
% these can be any first and second preference files as long as they are
% in the correct format see `webCGMtoTabDelimitedFile.m`
%
% USAGE:
%
%    metGroupCont = createGroupContributionStruct(primaryFile, pH, secondaryFile)
%
% INPUT:
%    primaryFile:     tab delimited text file with group contribution data
%                     (`Janowski et al Biophysical Journal 95:1487-1499 (2008)`)
%                     i.e. output such as `webCGM.txt` from `webCGMtoTabDelimitedFile.m`
%
% OPTIONAL INPUTS:
%    pH:              ph at which group contribution data given for, `default = 7`
%
%    secondaryFile:    tab delimited text file with group contribution data
%                     (`Janowski et al Biophysical Journal 95:1487-1499 (2008)`)
%                     i.e. output such as `webCGM.txt` from `webCGMtoTabDelimitedFile.m`
%                     If the same metabolite abbreviation occurs in both files,
%                     then the data in the primary file takes precedence.
%
%
% Comment on input file format - the first two text columns in both files should correspond to:
% `abbreviation`, `formulaMarvin`,
% the next three columns in both files should correspond to:
% `delta_G_formation`, `delta_G_formation_Uncertainty`, `chargeMarvin`
%
% OUTPUTS:
%    metGroupCont:    structure with fields:
%
%                       * metGroupCont(m).abbreviation - metabolite abbreviation
%                       * metGroupCont(m).formulaMarvin - metabolite formula (Marvin)
%                       * metGroupCont(m).delta_G_formation
%                       * metGroupCont(m).delta_G_formation_uncertainty
%                       * metGroupCont(m).chargeMarvin - metabolite charge (Marvin)
%                       * metGroupCont(m).pH
%                       * metGroupCont(m).file - file data came from
%
% .. Author: - Ronan M. T. Fleming 9 July 2009

if ~exist('pH','var')
    pH=7;
end

fid=fopen(primaryFile,'r');
if fid==-1
    error(['Cannot open ' primaryFile]);
else
    fprintf('%s\n',['Reading primary metabolite group contribution data from: ' primaryFile]);
end
C = textscan(fid,'%s %s %f %f %f');
primaryAbbr=C{1};
C2=C{2};
C3=C{3};
C4=C{4};
C5=C{5};

%
for m=1:length(primaryAbbr)
    metGroupCont(m,1).abbreviation=primaryAbbr{m};
    metGroupCont(m,1).formulaMarvin=C2{m};
    metGroupCont(m,1).delta_G_formation=C3(m,1);
    metGroupCont(m,1).delta_G_formation_uncertainty=C4(m,1);
    metGroupCont(m,1).chargeMarvin=C5(m);
    metGroupCont(m,1).pH=pH;
    metGroupCont(m,1).file=primaryFile;
end

if exist('secondaryFile','var')
    fid=fopen(secondaryFile,'r');
    if fid==-1
        error(['Cannot open ' secondaryFile]);
    else
        fprintf('%s\n',['Reading secondary metabolite group contribution data from: ' secondaryFile]);
    end
    C = textscan(fid,'%s %s %f %f %f');
    secondaryAbbr=C{1};
    C2=C{2};
    C3=C{3};
    C4=C{4};
    C5=C{5};

    m=length(primaryAbbr)+1;
    for n=1:length(secondaryAbbr)
        ind=strcmp(secondaryAbbr{n},primaryAbbr);
        %add the data from the secondary group contribution file if missing from
        %the primary file
        if ~any(ind)
            metGroupCont(m,1).abbreviation=secondaryAbbr{n};
            metGroupCont(m,1).formulaMarvin=C2{n};
            metGroupCont(m,1).delta_G_formation=C3(n);
            metGroupCont(m,1).delta_G_formation_uncertainty=C4(n);
            metGroupCont(m,1).chargeMarvin=C5(n);
            metGroupCont(m,1).pH=pH;
            metGroupCont(m,1).file=secondaryFile;
            m=m+1;
        end
    end
end


function [Flag,jAngle]=dialog_jointAnlgesInput()
% Dialog box used by the user to enter the values of the joint angles of the robot.

% Copyright: Mohammad SAFEEA, 2018-July-20

global daHandleof_dialog_jointAnlgesInput;
global dialog_jointAnlgesInput_button_hit;
global dialog_jointAnlgesInput_flag;

dialog_jointAnlgesInput_button_hit=0;
dialog_jointAnlgesInput_flag=false;
dialog_jointAnlgesInput_jangle={};
%% Interface container
dialog_width=540;
dialog_height=500;
dialog_x0=10;
dialog_y0=100;
daCaption='Joint angles (degree)';
jointAnglesDialogHandle=figure('Name',daCaption,'rend','painters','pos',[dialog_x0 dialog_y0 dialog_width dialog_height]);
daHandleof_dialog_jointAnlgesInput=jointAnglesDialogHandle;
%% Button dimension
enter_buttons_separator=40;
button_width=100;
button_height=50;
%% Add ok button
fcn=@callback_dialog_jointAnlgesInput_okBtn;
btn_ok_h= uicontrol(jointAnglesDialogHandle,'Style', 'pushbutton', 'String', 'OK',...
                'Units','pixels',...
                'Callback', fcn);
x0=round((dialog_width-enter_buttons_separator-button_width*2)/2);
xtemp=x0;
y0=enter_buttons_separator;
ytemp=y0;
btn_ok_h.HorizontalAlignment='center';
btn_ok_h.Position=[x0 y0 button_width button_height];
%% Add cancel button
fcn=@callback_dialog_jointAnlgesInput_cancelBtn;
btn_cancel_h= uicontrol(jointAnglesDialogHandle,'Style', 'pushbutton', 'String', 'Cancel',...
                'Units','pixels',...
                'Callback', fcn);
x0=xtemp+enter_buttons_separator+button_width;
y0=ytemp;
btn_cancel_h.HorizontalAlignment='center';
btn_cancel_h.Position=[x0 y0 button_width button_height];
%% Dimensions of lables and sliders,
labelsNum=7;
enter_controls_horizontal_margin=10;
x0=enter_controls_horizontal_margin;
y0=ytemp+button_height;

labels_width=100;
enter_labels_vertical_margin=30;
labels_height=(dialog_height-enter_labels_vertical_margin*(labelsNum+1)-y0)/labelsNum;

grandSliderWidth=280;
bigSliderWidth=250;
smallSliderWidth=220;
sliderHeight=labels_height;
slider_mid_coordinate=275;
%% Sliders handles
sliders_handles=[];
angle_value_texts_handles=[];
%% Add lables and sliders,
h=daHandleof_dialog_jointAnlgesInput;
labels_text_array={'J1','J2','J3','J4','J5','J6','J7'};
for i=1:7
    labels_text=labels_text_array{7-i+1};
    lbl_handle= uicontrol(h,'Style', 'text', 'String', labels_text,...
            'Units','pixels');
    x_labels_0=x0;
    y_labels_0=y0+(enter_labels_vertical_margin)*i+(labels_height)*(i-1);
    lbl_handle.HorizontalAlignment='center';
    lbl_handle.Position=[x_labels_0 y_labels_0 labels_width labels_height];
    lbl_handle.Enable='inactive';

    %% Add slider and its labels
    if i==1
        slider_width=grandSliderWidth;
        min=-175;
        max=175;
    elseif mod(i,2)==1
        slider_width=bigSliderWidth;
        min=-170;
        max=170;
    else
        slider_width=smallSliderWidth;
        min=-120;
        max=120;
    end
    x_Slider=getXCoordiantesFromMidWidth(slider_mid_coordinate,slider_width);
    y_Slider=y_labels_0;
    silder_handle = uicontrol('Parent',h,'Style','slider',...
         'Units','pixels', ...
        'Position',[x_Slider,y_Slider,slider_width,sliderHeight],...
              'min',min, 'max',max);
    sliders_handles{i}=silder_handle;
          %% Min labels
    minmaxLablesWidth=50;
    minmaxLabelHeight=sliderHeight;
    min_labels_x=x_Slider - minmaxLablesWidth;
    tempPos=[min_labels_x,y_Slider,minmaxLablesWidth,minmaxLabelHeight];
    bl1 = uicontrol(h,'Style','text',...
        'Units','pixels', ...
        'Position',tempPos,...
        'String',[num2str(min),char(176)]);

          %% Max labels
    max_labels_x=x_Slider +slider_width ;
    tempPos=[max_labels_x,y_Slider,minmaxLablesWidth,minmaxLabelHeight];
    bl1 = uicontrol(h,'Style','text',...
        'Units','pixels', ...
        'Position',tempPos,...
        'String',[num2str(max),char(176)]);

    %% Angle value text
    labelsWidth=80;
    angle_val_label_x=dialog_width-labelsWidth ;
    tempPos=[angle_val_label_x,...
        y_Slider,...
        labelsWidth,minmaxLabelHeight];
    angle_value_text_handle = uicontrol(h,'Style','text',...
        'Units','pixels', ...
        'Position',tempPos,...
        'String',num2str(0.0));
           angle_value_texts_handles{i} =angle_value_text_handle;
end

tempJointAngles={1,2,3,4,5,6,7};
dialog_jointAnlgesInput_jangle={0,0,0,0,0,0,0};
while(dialog_jointAnlgesInput_button_hit==0)
    figure(daHandleof_dialog_jointAnlgesInput)
    pause(0.1);
    try
    for i=1:7
        % get value of the slider
        temp_handle=sliders_handles{i};
        val=get(temp_handle,'Value');
        tempJointAngles{7-i+1}=val*pi/180;
        % through the value into the textbox
        temp_handle=angle_value_texts_handles{i} ;
%         numString=num2str(val)
        numString=sprintf('%0.2f',val);
        set(temp_handle,'String',numString);
        dialog_jointAnlgesInput_jangle=tempJointAngles;
    end
    catch
        break;
    end
end
jAngle=dialog_jointAnlgesInput_jangle;
Flag=dialog_jointAnlgesInput_flag;

clear daHandleof_dialog_jointAnlgesInput;
clear dialog_jointAnlgesInput_button_hit;
clear dialog_jointAnlgesInput_flag;
clear dialog_jointAnlgesInput_jangle;
end

function callback_dialog_jointAnlgesInput_okBtn(PushButton, EventData)
    global daHandleof_dialog_jointAnlgesInput;
    global dialog_jointAnlgesInput_button_hit;
    global dialog_jointAnlgesInput_flag;

    close(daHandleof_dialog_jointAnlgesInput);
    dialog_jointAnlgesInput_flag=true;
    dialog_jointAnlgesInput_button_hit=1;
end

function callback_dialog_jointAnlgesInput_cancelBtn(PushButton, EventData)
    global daHandleof_dialog_jointAnlgesInput;
    global dialog_jointAnlgesInput_button_hit;
    global dialog_jointAnlgesInput_flag;

    close(daHandleof_dialog_jointAnlgesInput);
    dialog_jointAnlgesInput_flag=false;
    dialog_jointAnlgesInput_button_hit=1;
end

function x=getXCoordiantesFromMidWidth(mid,width)
x=mid-width/2;
end


function xyl = Xl(s)
x = 0 ;
y = s ;

xyl = [x ; y] ;


function SDataOut = DicomImport(sPath, lRecursive, csAdditionalTags)
% DICOMIMPORT Import DICOM series with preview.
%
%   SDATA = DICOMIMPORT(sPATH, lRECURSIVE) Scans the folder specified by
%   sPATH for DICOM files. If lRECURSIVE is true, DICOMIMPORT
%   hierarchically scans all subolders of sPATH. Valid DICOM files are
%   sorted into series according the DICOM tag SeriesInstanceUID and the
%   folder in which the files were found. For each series, a thumbnail
%   image is created and an overview of the obtained series is displayed in
%   a GUI that lets you select the series to load. Select series as if you
%   would in a file browser by klicking and using the SHIFT and CNTL keys
%   to select multiple series Close the GUI by pressing <ENTER> or <ESCAPE>
%   (the latter returns an empty array). Output SDATA is a struct where
%   length(SDATA) equals the amout of selected series. SDATA contains at
%   least the following fields:
%
%       Img:                The image volume
%       SeriesDescription:  The contents of the corresponding DICOM tag
%       Orientation:        The most prominent image volume orientation
%       Aspect:             The pixel spacing in all 3 directions
%       ImageOrientation:   The direction cosines (compare DICOM standard)
%       ImagePosition:      The coordinates of the upper left corner of
%                           each image slice (compare DICOM standard)
%
%   The tags specified in csADDITIONALTAGS (see below) are appended to this
%   structure.
%
%
%   DICOMIMPORT lets you select the base folder in a dialog and asks if the
%   sobfolders are to be scanned.
%
%   DICOMIMPORT(sPATH, lRECURSIVE, csADDITIONALTAGS) Lets you specify
%   additional DICOM tags you can use for further processing in the cell
%   array of strings csADDITIONALTAGS
%
%
% Copyright 2013 Christian Wuerslin, University of Tuebingen and
% University of Stuttgart, Germany.
% Contact: christian.wuerslin@med.uni-tuebingen.de

% =========================================================================
% *** FUNCTION DicomImport
% ***
% *** Main GUI function. See above for description.
% ***
% =========================================================================

% -------------------------------------------------------------------------
% Control the figure's appearence
SAp.sTITLE              = 'Dicom Import [press enter to confirm]';
SAp.iTHUMBNAILSIZE      = 200;
SAp.iINFOBARHEIGHT      = 125;
SAp.dBGCOLOR            = [0.1 0.2 0.3];
SAp.iNROWS              = 2;
SAp.iNCOLS              = 4;
SAp.iTITLEHEIGHT        = 16;
SAp.dOPACITY            = 0.5;
% -------------------------------------------------------------------------

% -------------------------------------------------------------------------
% Define a list of DICOM tags to read (those that I really need)
csTags = {'SeriesInstanceUID',       'Rows',                 'Columns', ...
          'ImageOrientationPatient', 'ImagePositionPatient', 'PixelSpacing', ...
          'SeriesDescription', 'Modality'};
% -------------------------------------------------------------------------

% -------------------------------------------------------------------------
% Parse input arguments
if nargin
    if ~ischar(sPath), error('Input argument must be a string!'); end
else
    sPath = uigetdir;
    if isnumeric(sPath)
        SDataOut = [];
        return
    end
end

if nargin < 2, lRecursive = strcmp(questdlg('Search subdirectories for DICOM files?', 'Stupid question', 'Yup', 'Noop', 'Yup'), 'Yup'); end
if nargin > 2, csTags = [csTags, csAdditionalTags]; end
% -------------------------------------------------------------------------

% -------------------------------------------------------------------------
% Get all DICOM files and their headers
fprintf(1, 'Scanning for DICOM files...');
SData = fGetDicomFiles(sPath, csTags, lRecursive);
fprintf(1, 'done.\n');
if isempty(SData), return, end
% -------------------------------------------------------------------------

% -------------------------------------------------------------------------
% Sort the series into a struct and get a thumbnail of each set
fprintf(1, 'Creating thumbnails...');
iHashs = [SData.iHash];
iSeriesHash = fUnique(iHashs);
iNDatasets = length(iSeriesHash);
iThumbnails = zeros(SAp.iTHUMBNAILSIZE, SAp.iTHUMBNAILSIZE, iNDatasets);
for iDataset = 1:iNDatasets
    iThisSeries = find([SData.iHash] == iSeriesHash(iDataset));
    SSeries(iDataset).SData = SData(iThisSeries);

    % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    % Load the center image and create thumbnail
    iInd = iThisSeries(round(length(iThisSeries)./2));
    dImg = double(dicomread(SData(iInd).sFilename));
    if isempty(dImg), continue, end

    dImg = dImg - min(dImg(:));
    dX = linspace(1, length(dImg), SAp.iTHUMBNAILSIZE);
    [dYY, dXX] = meshgrid(dX, dX);
    dImg = interp2(dImg, dYY, dXX, 'linear*', 0);
    if max(dImg(:)), dImg = dImg./max(dImg(:)); end
    iThumbnails(:,:,iDataset) = dImg;
    % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
end
lSelected = false(iNDatasets, 1);
fprintf(1, 'done.\n');
fprintf(1, 'Found %u files in %u datasets!\n', length(SData), iNDatasets);
clear iDataset iThisSeries iInd dImg dX dXX dYY
% -------------------------------------------------------------------------

% -------------------------------------------------------------------------
% Create the figure.
dFigureWidth  = (SAp.iTHUMBNAILSIZE + 1).*SAp.iNCOLS;
dFigureHeight = (SAp.iTHUMBNAILSIZE + SAp.iTITLEHEIGHT + 1).*SAp.iNROWS + SAp.iINFOBARHEIGHT + 2;
dScreenSize = get(0, 'MonitorPositions');
try
    hF = figure(...
        'Position'             , [(dScreenSize(3) - dFigureWidth )./2, ...
        (dScreenSize(4) - dFigureHeight)./2, ...
        dFigureWidth, dFigureHeight], ...
        'Units'                , 'pixels', ...
        'Color'                , SAp.dBGCOLOR, ...
        'Resize'               , 'off', ...
        'DockControls'         , 'off', ...
        'MenuBar'              , 'none', ...
        'Name'                 , SAp.sTITLE, ...
        'NumberTitle'          , 'off', ...
        'BusyAction'           , 'cancel', ...
        'KeyPressFcn'          , @fKeyPressFcn, ...
        'CloseRequestFcn'      , @fCloseGUI, ...
        'WindowButtonDownFcn'  , @fWindowButtonDownFcn, ...
        'WindowButtonMotionFcn', @fWindowMouseMoveFcn, ...
        'WindowScrollWheelFcn' , @fWindowScrollWheelFcn);
catch %#ok<CTCH>
    hF = figure(...
        'Position'             , [(dScreenSize(3) - dFigureWidth )./2, ...
        (dScreenSize(4) - dFigureHeight)./2, ...
        dFigureWidth, dFigureHeight], ...
        'Units'                , 'pixels', ...
        'Color'                , SAp.dBGCOLOR, ...
        'Resize'               , 'off', ...
        'DockControls'         , 'off', ...
        'MenuBar'              , 'none', ...
        'Name'                 , SAp.sTITLE, ...
        'NumberTitle'          , 'off', ...
        'BusyAction'           , 'cancel', ...
        'KeyPressFcn'          , @fKeyPressFcn, ...
        'CloseRequestFcn'      , @fCloseGUI, ...
        'WindowButtonDownFcn'  , @fWindowButtonDownFcn, ...
        'WindowButtonMotionFcn', @fWindowMouseMoveFcn);
end
clear dScreenSize
% -------------------------------------------------------------------------

% -------------------------------------------------------------------------
% Create the axes
hAxes = zeros(SAp.iNCOLS.*SAp.iNROWS, 1);
hImg  = zeros(SAp.iNCOLS.*SAp.iNROWS, 1);
hText = zeros(SAp.iNCOLS.*SAp.iNROWS, 1);
for iM = 1:SAp.iNROWS
    for iN = 1:SAp.iNCOLS
        iLinInd = (iM - 1).*SAp.iNCOLS + iN;
        dVertPos = dFigureHeight - iM.*(SAp.iTHUMBNAILSIZE + SAp.iTITLEHEIGHT + 1) + 1;
        iPosition = [(iN - 1).*(SAp.iTHUMBNAILSIZE + 1) + 2, dVertPos, SAp.iTHUMBNAILSIZE, SAp.iTHUMBNAILSIZE];
        hAxes(iLinInd) = axes('Parent' , hF, 'Units', 'pixels', 'Position', iPosition);
        hImg(iLinInd) = image(zeros(SAp.iTHUMBNAILSIZE, SAp.iTHUMBNAILSIZE, 3), 'Parent', hAxes(iLinInd));
        hText(iLinInd) = uicontrol(...
            'Parent'            , hF, ...
            'Style'             , 'text', ...
            'Units'             , 'pixels', ...
            'Position'          , [iPosition(1), iPosition(2) + iPosition(4), SAp.iTHUMBNAILSIZE, SAp.iTITLEHEIGHT], ...
            'BackgroundColor'   , SAp.dBGCOLOR./2, ...
            'ForegroundColor'   , 'w', ...
            'HorizontalAlign'   , 'left', ...
            'FontUnits'         , 'pixels', ...
            'FontSize'          , 12);
    end
end
axis(hAxes, 'off');
clear iM iN
% -------------------------------------------------------------------------

% -------------------------------------------------------------------------
% Create the info text objects
uicontrol(...
    'Parent'            , hF, ...
    'Style'             , 'text', ...
    'Units'             , 'pixels', ...
    'Position'          , [2, 2, 200, SAp.iINFOBARHEIGHT], ...
    'BackgroundColor'   , SAp.dBGCOLOR./2, ...
    'ForegroundColor'   , 'w', ...
    'HorizontalAlign'   , 'left', ...
    'FontUnits'         , 'pixels', ...
    'FontSize'          , 14, ...
    'String'            , {'Series Description:', 'Path:', 'Modality:', 'Rows:', 'Columns:', 'Images:', 'Pixel Spacing:'});
hInfoText = uicontrol(...
    'Parent'            , hF, ...
    'Style'             , 'text', ...
    'Units'             , 'pixels', ...
    'Position'          , [202, 2, dFigureWidth - 202, SAp.iINFOBARHEIGHT], ...
    'BackgroundColor'   , SAp.dBGCOLOR./2, ...
    'ForegroundColor'   , 'w', ...
    'HorizontalAlign'   , 'left', ...
    'FontUnits'         , 'pixels', ...
    'FontSize'          , 14, ...
    'String'            , '');
% -------------------------------------------------------------------------

% -------------------------------------------------------------------------
% Create a beatuiful mask for the selection highlight
dMask = repmat(permute(SAp.dBGCOLOR, [1 3 2]) , [SAp.iTHUMBNAILSIZE, SAp.iTHUMBNAILSIZE, 1]);
dMask = dMask.*repmat(linspace(1, 0.5, SAp.iTHUMBNAILSIZE)', [1, SAp.iTHUMBNAILSIZE, 3]);
dMask = dMask + 0.05.*rand(size(dMask));
% -------------------------------------------------------------------------

% -------------------------------------------------------------------------
% Fill the figure
iStartSeries = 1;
iLastSeries = 0;
sResult = 'esc';
fFillPanels();
% -------------------------------------------------------------------------

uiwait(hF);
% -------------------------------------------------------------------------
% ~~~~~~~~~~~~ A lot of user-friendly GUI interaction going on ~~~~~~~~~~~~
% -------------------------------------------------------------------------

% The GUI was closed in some way, continue execution

% -------------------------------------------------------------------------
% Dialog was aborted: Seriously, who does that?
if strcmp(sResult, 'esc')
    SDataOut = [];
    try delete(hF); end
    return
end
% -------------------------------------------------------------------------

% -------------------------------------------------------------------------
% No seriers selected? - Return
iInd = find(lSelected);
if isempty(iInd)
    SDataOut = [];
    delete(hF);
    return
end
% -------------------------------------------------------------------------

% -------------------------------------------------------------------------
% Load the selected series
fprintf(1, 'Loading slected DICOM files...');
for i = 1:length(iInd)
    % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    % Load the specified images
    SThisData = SSeries(iInd(i)).SData;
    iNImages = length(SThisData);
    iImg = zeros(SThisData(1).Rows, SThisData(1).Columns, iNImages, 'uint16');
    for j = 1:iNImages
        iImg(:,:,j) = dicomread(SThisData(j).sFilename);
    end
    % Now images are in iImg, headers in SThisData
    % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    % Find out the major orientation
    dImageOrientation = reshape(SThisData(1).ImageOrientationPatient, [3, 2])'; % Should be the same for a volume
    dImagePosition = [SThisData.ImagePositionPatient]';

    dOrientIndicator = sum(abs(dImageOrientation));
    [temp, iMinInd] = min(dOrientIndicator); %#ok<ASGLU>
    d3rdDimInd = dImagePosition(:, iMinInd);
    [temp, iSortInd] = sort(d3rdDimInd, 'ascend'); %#ok<ASGLU>
    iImg = iImg(:,:,iSortInd);
    iImageOrientation = round(dImageOrientation);
    switch(iMinInd)
        case 1 % The x coordinate changes least in both directions -> Sagittal
            SDataOut(i).Orientation = 'Sag'; % -> x: a->p=pos, y: h->f=neg
            if iImageOrientation(1, 2) == -1, flipdim(iImg, 2); end % <- y-coordinate was flipped
            if iImageOrientation(2, 3) ==  1, flipdim(iImg, 1); end % <- z-coordinate was flipped

        case 2 % The y coordinate changes least in both directions -> Coronal
            SDataOut(i).Orientation = 'Cor'; % -> x: r->l=pos, y: h->f=neg
            if iImageOrientation(1, 1) == -1, flipdim(iImg, 2); end % <- x-coordinate was flipped
            if iImageOrientation(2, 3) ==  1, flipdim(iImg, 1); end % <- z-coordinate was flipped

        case 3 % The z coordinate changes least in both directions -> Transversal
            SDataOut(i).Orientation = 'Tra'; % -> x: r->l=pos, y: a->p=pos
            if iImageOrientation(1, 1) == -1, flipdim(iImg, 2); end % <- x-coordinate was flipped
            if iImageOrientation(2, 2) == -1, flipdim(iImg, 1); end % <- Y-coordinate was flipped
    end
    SDataOut(i).Img = iImg;
    % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    % Save the most important infos to the ouput variable
    SDataOut(i).SeriesDescriptions = SThisData(1).SeriesDescription;
    SDataOut(i).Aspect = zeros(3, 1);
    SDataOut(i).Aspect(1:2) = SThisData(1).PixelSpacing;
    if ~isscalar(d3rdDimInd), SDataOut(i).Aspect(3)   = abs(d3rdDimInd(2) - d3rdDimInd(1)); end
    SDataOut(i).ImageOrientation = dImageOrientation;
    SDataOut(i).ImagePosition = dImagePosition(iSortInd, :);
    if nargin == 3
        for j = 1:length(csAdditionalTags) % The additionally requested infos
            sTag = csAdditionalTags{j};
            eval(['xData = [SThisData.', sTag, ']'';']);
            xData = xData(iSortInd);
            eval(['SDataOut(i).', sTag, ' = xData;']);
        end
    end
    % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
end
% -------------------------------------------------------------------------
fprintf(1, 'done.\n');

delete(hF);
% =========================================================================
% ***
% *** The 'end' of the DICOMIMPORT main function.
% ***
% =========================================================================

    % = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    % * * NESTED FUNCTION fCloseGUI (nested in DicomImport)
    % * *
    % * * Figure callback
    % * *
    % * * Closes the figure.
    % * *
    % = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    function fCloseGUI(hObject, eventdata) %#ok<*INUSD> eventdata is repeatedly unused
        uiresume(hObject);
        delete(hObject);
    end
    % = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    % * * END NESTED FUNCTION fCloseGUI
	% = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

    % = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    % * * NESTED FUNCTION fWindowMouseMoveFcn (nested in DicomImport)
    % * *
    % * * Figure callback
    % * *
    % * * Displays informations about the series under the mouse cursor in
    % * * the texts at the bottom of the figure.
    % * *
    % = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    function fWindowMouseMoveFcn(hObject, eventdata)
        if ~exist('hImg', 'var'), return, end; % Return if called during GUI startup

        iAxisInd = fGetAxes();
        iSeriesInd = iAxisInd + iStartSeries -1;

        % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        % Return if not over axes or out of data bounds
        if ~iAxisInd, set(hInfoText, 'String', ''); return, end
        if iSeriesInd > length(lSelected),  set(hInfoText, 'String', ''); return, end
        % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        % Print Information
        sText = SSeries(iSeriesInd).SData(1).SeriesDescription;
        if isempty(sText), sText = 'N/A'; end, csText(1) = {sText};

        sText = fileparts(SSeries(iSeriesInd).SData(1).sFilename);
        csText(2) = {sText};

        sText = SSeries(iSeriesInd).SData(1).Modality;
        if isempty(sText), sText = 'N/A'; end, csText(3) = {sText};

        sText = SSeries(iSeriesInd).SData(1).Rows;
        if isempty(sText), sText = 'N/A'; end, csText(4) = {sText};

        sText = SSeries(iSeriesInd).SData(1).Columns;
        if isempty(sText), sText = 'N/A'; end, csText(5) = {sText};

        sText = num2str(length(SSeries(iSeriesInd).SData));
        if isempty(sText), sText = 'N/A'; end, csText(6) = {sText};

        dPixelSpacing =  SSeries(iSeriesInd).SData(1).PixelSpacing;
        if ~isempty(dPixelSpacing)
            sText = sprintf('%2.2f x %2.2f mm', dPixelSpacing(1), dPixelSpacing(2));
        else
            sText = 'N/A';
        end
        csText(7) = {sText};

        set(hInfoText, 'String', csText);
        % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    end
    % = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    % * * END NESTED FUNCTION fWindowMouseMoveFcn
	% = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

    % = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    % * * NESTED FUNCTION fWindowButtonDownFcn (nested in DicomImport)
    % * *
    % * * Figure callback
    % * *
    % * * Starting callback for mouse button actions. Manage the selection
    % * * of the series.
    % * *
    % = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    function fWindowButtonDownFcn(hObject, eventdata)
        iPanelInd = fGetAxes();
        iSeriesInd = iPanelInd + iStartSeries - 1;

        if ~iPanelInd, return, end % Exit if Event didn't occurr in axes
        if iSeriesInd > length(lSelected), return, end

        switch get(hF, 'SelectionType')

            % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            % Left mouse button without any key
            case 'normal'
                iNSelected = sum(lSelected);
                lState = lSelected(iSeriesInd);
                lSelected = false(size(lSelected));
                lSelected(iSeriesInd) = ~lState || iNSelected > 1;
                if ~lState, iLastSeries = iSeriesInd; end
            % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

            % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            % Right mouse button/shift key
            case 'extend'
                if iLastSeries
                    iInd = sort([iLastSeries, iSeriesInd], 'ascend');
                    lSelected(iInd(1):iInd(2)) = true;
                else
                    lSelected(iSeriesInd) = true;
                    iLastSeries = iSeriesInd;
                end
            % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

            % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            % Middle mouse button/alt key
            case 'alt'
                lState = lSelected(iSeriesInd);
                lSelected(iSeriesInd) = ~lState;
                if ~lState, iLastSeries = iSeriesInd; end
            % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        end
        fFillPanels;
    end
    % = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    % * * END NESTED FUNCTION fWindowButtonDownFcn
	% = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

    % = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    % * * NESTED FUNCTION fKeyPressFcn (nested in DicomImport)
    % * *
    % * * Figure callback
    % * *
    % * * Callback for keyboard actions.
    % * *
    % = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    function fKeyPressFcn(hObject, eventdata) %#ok<INUSL>

        switch eventdata.Key

            case 'uparrow'
                iStartSeries = max([1, iStartSeries - SAp.iNCOLS]);
                fFillPanels;

            case 'downarrow'
                if iStartSeries + SAp.iNCOLS < length(lSelected),
                    iStartSeries = iStartSeries + SAp.iNCOLS;
                end

            case 'return' % Return and load the selected series
                sResult = 'OK';
                uiresume(hF);

            case 'escape' % Return and do nothing
                uiresume(hF);

        end % switch

        fFillPanels;
        fWindowMouseMoveFcn(hF, []); % Make sure the info at the bottom is valid

    end
    % = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    % * * END NESTED FUNCTION fKeyPressFcn
	% = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

    % = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    % * * NESTED FUNCTION fWindowScrollWheelFcn (nested in DicomImport)
    % * *
    % * * Figure callback
    % * *
    % * * Callback for keyboard actions.
    % * *
    % = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    function fWindowScrollWheelFcn(hObject, eventdata)
        if eventdata.VerticalScrollCount > 0
            SEData.Key = 'downarrow';
        else
            SEData.Key = 'uparrow';
        end
        fKeyPressFcn(hObject, SEData); % This is the laszy way: just call the other callback
    end
    % = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    % * * END NESTED FUNCTION fWindowScrollWheelFcn
	% = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

    % = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    % * *
    % * * NESTED FUNCTION fFillPanels (nested in DicomImport)
    % * *
    % * * Display the current data in all axes.
	% * *
    % = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    function fFillPanels()
        for iI = 1:length(hImg);
            iSeriesInd = iI + iStartSeries - 1;
            if iSeriesInd > length(lSelected)
                set(hImg(iI), 'CData', dMask./3);
                set(hText(iI), 'String', '<no data>');
            else
                dImg = repmat(iThumbnails(:,:,iSeriesInd), [1 1 3]);
                if lSelected(iSeriesInd),  dImg = 1 - ((1 - dImg).*(1 - SAp.dOPACITY.*dMask)); end
                set(hImg(iI), 'CData', dImg);
                sTitle = SSeries(iSeriesInd).SData(1).SeriesDescription;
                if length(sTitle) > 22, sTitle = [sTitle(1:21), '...']; end
                if isempty(sTitle), sTitle = '<no description>'; end
                set(hText(iI), 'String', sprintf('[%u]: %s', iSeriesInd, sTitle));
            end
        end
        drawnow expose
    end
    % = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    % * * END NESTED FUNCTION fFillPanels
	% = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

    % = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    % * *
    % * * NESTED FUNCTION fGetAxes (nested in DicomImport)
    % * *
    % * * Determine the axes number under the mouse cursor. Returns 0 if
    % * * not over an axes at all.
	% * *
    % = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    function iPanelInd = fGetAxes()
        iCursorPos = get(hF, 'CurrentPoint');
        iPanelInd = uint8(0);
        for iI = 1:length(hAxes)
            dPos = get(hAxes(iI), 'Position');
            if ((iCursorPos(1) >= dPos(1)) && (iCursorPos(1) < dPos(1) + dPos(3)) && ...
                (iCursorPos(2) >= dPos(2)) && (iCursorPos(2) < dPos(2) + dPos(4)))
                iPanelInd = uint8(iI);
            end
        end
    end
    % = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    % * * END NESTED FUNCTION fGetAxes
	% = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

    % = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    % * *
    % * * NESTED FUNCTION fGetDicomFiles (nested in DicomImport)
    % * *
    % * * Finds and creates a database of DICOM files
	% * *
    % = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    function SD = fGetDicomFiles(sFolder, csTags, lRecursive)
        SD = [];
        SDir = dir(sFolder);
        for iI = 1:length(SDir)
            sName = SDir(iI).name;

            if (SDir(iI).isdir) && (lRecursive)
                if sName(1) == '.', continue, end
                SD = [SD, fGetDicomFiles([sFolder, filesep, SDir(iI).name], csTags, lRecursive)];
            else
                sFilename = [sFolder, filesep, SDir(iI).name];
                try
                    SHeader = dicominfo(sFilename);
                catch %#ok<CTCH>
                    continue
                end
                SRecord = [];
                for iJ = 1:length(csTags)
                    sTag = csTags{iJ};
                    try
                        eval(['SRecord.', sTag, ' = SHeader.', sTag, ';']);
                    catch %#ok<CTCH>
                        eval(['SRecord.', sTag, ' = [];']);
                    end
                end
                if isfield(SHeader, 'SeriesInstanceUID')
                    SRecord.iHash = fHash([sFolder, SHeader.SeriesInstanceUID]);
                    SRecord.sFilename = sFilename;
                    SD = [SD, SRecord];
                end
            end
        end
    end
    % = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    % * * END NESTED FUNCTION fGetDicomFiles
	% = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

    % = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    % * *
    % * * NESTED FUNCTION fHash (nested in DicomImport)
    % * *
    % * * Return a hash number
	% * *
    % = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    function iHashVal = fHash(sString)
        dString = double(sString);
        dHashVal = 5381;
        for iI = 1:length(dString)
            dHashVal = mod(dHashVal * 33 + dString(iI), 2.^32 - 1);
        end
        iHashVal = uint32(dHashVal);
    end
    % = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    % * * END NESTED FUNCTION fHash
	% = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

    % = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    % * *
    % * * NESTED FUNCTION fUnique (nested in DicomImport)
    % * *
    % * * Returns unique values in order of appearance
	% * *
    % = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    function xUniqueVals = fUnique(xArray)
        xUniqueVals = zeros(length(xArray), 1, class(xArray));
        iInd = 0;
        while ~isempty(xArray)
            iInd = iInd + 1;
            xUniqueVals(iInd) = xArray(1);
            xArray(xArray == xArray(1)) = [];
        end
        xUniqueVals = xUniqueVals(1:iInd);
    end
    % = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    % * * END NESTED FUNCTION fUnique
	% = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

end


%%% void show_3dvideo(3dmatrix)
%
function show_3dvideo(V)
  for i = 1 : size(V,3)
    frame = V(:,:,i);
    imshow(frame,[],'InitialMagnification','fit');
    disp(i);
    pause(0.01);
  end
end


function test_pull1377

% MEM 12gb
% WALLTIME 1:30:00
% DEPENDENCY ft_prepare_sourcemodel headsurface ft_prepare_leadfield ft_freqanalysis ft_sourceanalysis

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% This function creates a set of input-structures to be used for testing source
% analysis steps prior to inverse solution. The structure of this script is more or less
% 1. get the data (MEG and EEG)
% 2. preprocess the data (MEG and EEG)
%   a. timelock with covariance for lcmv and mne
%   b. timelock without keeptrials for dipolefitting
%   c. freq for dics and pcc
% 3. create leadfield
%   a. internally computed leadfield (prepare volume conductor first and leadfield afterwards (MEG localspheres, EEG single sphere))
%   b. load externally computed leadfield and convert to appropriate format
% 4. perform source analysis (MEG and EEG): LCMV, MNE, DICS, PCC, dipolefitting

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% In general, variables in one function workspace are not available to other
% functions. However, nested functions can access and modify variables in the
% workspaces of the functions that contain them.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% prevent errors from cfg.mne.keepleadfield, etc
global ft_default
ft_default.checkconfig = 'loose';

datameg = []; % touch these variables to make them shared between the main function and the nexted function
dataeeg = [];

loading_raw_data

headmodel_megsphere = [];
headmodel_eegsphere = [];
sourcemodel_megsphere = [];
sourcemodel_eegsphere = [];
sourcemodel_precomputed_meg = [];
sourcemodel_precomputed_eeg = [];
headmodel_simbio = [];
sourcemodel_simbio = [];
headmodel_openmeeg = [];
sourcemodel_openmeeg = [];

create_leadfields

MEG_tlck = [];
MEG_tlck_df = [];
MEG_freq = [];
EEG_tlck = [];
EEG_tlck_df = [];
EEG_freq = [];

preprocessing

sourcemodel_meg = sourcemodel_megsphere;
headmodel_meg = headmodel_megsphere;
sourcemodel_eeg = sourcemodel_eegsphere;
headmodel_eeg = headmodel_eegsphere;
perform_source_analysis

sourcemodel_meg = sourcemodel_precomputed_meg;
headmodel_meg = [];
sourcemodel_eeg = sourcemodel_precomputed_eeg;
headmodel_eeg = [];
perform_source_analysis

sourcemodel_meg = [];
headmodel_meg = [];
sourcemodel_eeg = sourcemodel_simbio;
headmodel_eeg = headmodel_simbio;
perform_source_analysis

sourcemodel_meg = [];
headmodel_meg = [];
sourcemodel_eeg = sourcemodel_openmeeg;
headmodel_eeg = headmodel_openmeeg;
perform_source_analysis

% !! create worse-case scenario, whereby order and nr of chans don't match across inputs (grad/elec and leadfields)
% Step 1: remove 2-3 random chans from both MEG and EEG raw data
% Step 2: shuffle the order of the channels (note that this breaks the mapping between channels and data)
cfg = [];
cfg.channel = randperm(length(datameg.label)-2);
datameg = ft_selectdata(cfg, datameg);
datameg.label = datameg.label(randperm(length(datameg.label)));

cfg.channel = randperm(length(dataeeg.label)-2);
dataeeg = ft_selectdata(cfg, dataeeg);
dataeeg.label = dataeeg.label(randperm(length(dataeeg.label)));

preprocessing

sourcemodel_meg = sourcemodel_megsphere;
headmodel_meg = headmodel_megsphere;
sourcemodel_eeg = sourcemodel_eegsphere;
headmodel_eeg = headmodel_eegsphere;
perform_source_analysis

sourcemodel_meg = sourcemodel_precomputed_meg;
headmodel_meg = [];
sourcemodel_eeg = sourcemodel_precomputed_eeg;
headmodel_eeg = [];
perform_source_analysis

sourcemodel_meg = [];
headmodel_meg = [];
sourcemodel_eeg = sourcemodel_simbio;
headmodel_eeg = headmodel_simbio;
perform_source_analysis

sourcemodel_meg = [];
headmodel_meg = [];
sourcemodel_eeg = sourcemodel_openmeeg;
headmodel_eeg = headmodel_openmeeg;
perform_source_analysis

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 1. get the data
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  function loading_raw_data

    clc
    disp('1: Get the raw data')

    % for MEG data + sensor info

    load(dccnpath('/home/common/matlab/fieldtrip/data/test/latest/raw/meg/preproc_ctf151.mat'), 'data');
    datameg = data;
    clear data

    % for EEG data + channel info

    cfg = [];
    cfg.dataset = dccnpath('/home/common/matlab/fieldtrip/data/ftp/tutorial/preprocessing_erp/s04.eeg');
    hdr = ft_read_header(cfg.dataset);
    event = ft_read_event(cfg.dataset);
    EVsample = [event.sample]';
    EVvalue = {event.value}';
    Word = find(strcmp('S141', EVvalue)==1);

    begsample = EVsample(Word) - 10;
    endsample = EVsample(Word) + 500;
    offset = -100*ones(size(endsample));
    cfg.trl = [begsample endsample offset];

    dataeeg = ft_preprocessing(cfg);

    % Fix EEG channel names, but note that the actual recording was not done with a Biosemi system
    cfg = [];
    cfg.layout = 'biosemi64.lay';
    lay = ft_prepare_layout(cfg);
    dataeeg.label = lay.label(1:end-2);
    clear lay

    dataeeg.elec = ft_read_sens(dccnpath('/home/common/matlab/fieldtrip/template/electrode/standard_1020.elc'), 'senstype', 'eeg');

  end % loding raw data

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 2. preprocess the data
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  function preprocessing

    clc
    disp('2: preprocess the data')

    % create timelock structure with covariance for lcmv and mne
    % and timeloch without keeptrials for dipolefitting

    % for MEG
    cfg = [];
    cfg.covariance = 'yes';
    cfg.keeptrials = 'yes';
    cfg.channel = 'MEG';
    MEG_tlck = ft_timelockanalysis(cfg, datameg);

    cfg = [];
    cfg.covariance = 'yes';
    % cfg.keeptrials = 'yes'; %if this is not commented, the .avg field necessary in dipolefitting is missing
    cfg.channel = 'MEG';
    MEG_tlck_df = ft_timelockanalysis(cfg, datameg);

    % for EEG
    cfg = [];
    cfg.covariance = 'yes';
    cfg.keeptrials = 'yes';
    EEG_tlck = ft_timelockanalysis(cfg, dataeeg);

    % for EEG (dimension problems in dipolefitting!!)
    cfg = [];
    cfg.covariance = 'yes';
    % cfg.keeptrials = 'yes';
    EEG_tlck_df = ft_timelockanalysis(cfg, dataeeg);

    % create freq structure for dics beamforming and pcc beamforming
    % for MEG
    cfg = [];
    cfg.method = 'mtmfft';
    cfg.output = 'fourier';
    cfg.tapsmofrq = 4;
    cfg.foilim = [0 20];
    cfg.channel = 'MEG';
    MEG_freq = ft_freqanalysis(cfg, datameg);

    % for EEG
    cfg = [];
    cfg.method = 'mtmfft';
    cfg.output = 'fourier';
    cfg.tapsmofrq = 4;
    cfg.foilim = [0 20];
    EEG_freq = ft_freqanalysis(cfg, dataeeg);

  end % function preprocessing

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 3. create leadfield
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  function create_leadfields

    clc
    disp('3: create leadfield')

    %% 3.a internally

    % get volume conductor model
    % for MEG, localsphere
    headmodel_megsphere = ft_read_headmodel(dccnpath('/home/common/matlab/fieldtrip/data/test/latest/vol/Subject01vol_localspheres.mat'));

    % for EEG, singlesphere
    headmodel_eegsphere.o = [0 0 2];
    headmodel_eegsphere.r = 8;
    headmodel_eegsphere.c = 1;
    headmodel_eegsphere.unit = 'cm';

    % create leadfield (internally to ft)
    % for MEG
    cfg = [];
    cfg.grad = datameg.grad;
    cfg.headmodel = headmodel_megsphere;
    cfg.channel = 'MEG';
    cfg.resolution = 1.5;
    sourcemodel_megsphere = ft_prepare_leadfield(cfg);

    cfg = [];
    cfg.elec = dataeeg.elec;
    cfg.headmodel = headmodel_eegsphere;
    cfg.channel = 'EEG';
    cfg.resolution = 1.5;
    sourcemodel_eegsphere = ft_prepare_leadfield(cfg); % inconsistent with dataeeg.labels

    %% 3.b externally (mimic externally created leadfields)
    clc;
    disp('3b: Externally computed leadfields for EEG and MEG')

    Ndip = 1000;

    % for MEG
    % here we are assuming that the externally computed leadfield has the structure Ndip x Nsens
    leadf = randn(3*Ndip, length(datameg.grad.label));

    sourcemodel_precomputed_meg = [];
    sourcemodel_precomputed_meg.leadfield = cell(Ndip,1);
    sourcemodel_precomputed_meg.unit = 'cm';
    sourcemodel_precomputed_meg.leadfielddimord = '{pos}_chan_ori';
    sourcemodel_precomputed_meg.label = datameg.grad.label;

    for d =1:Ndip
      sourcemodel_precomputed_meg.leadfield{d} = [leadf(d,:); leadf(d+Ndip,:); leadf(d+2*Ndip,:)]';
    end

    sourcemodel_precomputed_meg.inside = ones(size(sourcemodel_precomputed_meg.leadfield));
    sourcemodel_precomputed_meg.pos = randn(length(sourcemodel_precomputed_meg.leadfield),3);

    % for EEG
    % here we are assuming that the externally computed leadfield has the structure Ndip x Nsens
    leadf = randn(3*Ndip, length(dataeeg.elec.label));

    sourcemodel_precomputed_eeg = [];
    sourcemodel_precomputed_eeg.leadfield = cell(Ndip,1);
    sourcemodel_precomputed_eeg.unit = 'cm';
    sourcemodel_precomputed_eeg.leadfielddimord = '{pos}_chan_ori';
    sourcemodel_precomputed_eeg.label = dataeeg.elec.label;

    for d =1:Ndip
      sourcemodel_precomputed_eeg.leadfield{d,1} = [leadf(d,:); leadf(d+Ndip,:); leadf(d+2*Ndip,:)]';
    end

    sourcemodel_precomputed_eeg.inside = ones(size(sourcemodel_precomputed_eeg.leadfield));
    sourcemodel_precomputed_eeg.pos = randn(length(sourcemodel_precomputed_eeg.leadfield),3);

    %% 3.c simbio leadfield for EEG

    segm = [];
    segm.brain = logical(floor(3*rand(10,10,10)));
    segm.dim = size(segm.brain);
    segm.unit = 'mm';
    segm.coordsys = 'ctf';
    segm.transform = eye(4);

    cfg = [];
    cfg.method = 'hexahedral';
    cfg.resolution = 1; % this is in mm
    mesh_vol = ft_prepare_mesh(cfg, segm);

    ft_plot_mesh(mesh_vol, 'surfaceonly', false);
    view(120, 30)

    cfg = [];
    cfg.method ='simbio';
    cfg.conductivity = 0.33; % order follows mesh.tissuelabel
    headmodel_simbio = ft_prepare_headmodel(cfg, mesh_vol);

    cfg = [];
    cfg.elec = dataeeg.elec;
    cfg.headmodel = headmodel_simbio;
    cfg.channel = 'EEG';
    cfg.resolution = 1.5;
    sourcemodel_simbio = ft_prepare_leadfield(cfg); % inconsistent with dataeeg.labels

    %% 3.c OpenMEEG leadfield for EEG

    load(dccnpath('/home/common/matlab/fieldtrip/data/ftp/test/ctf/SubjectSEF_vol.mat'), 'vol')
    bnd_openmeeg = vol;

    cfg = [];
    cfg.conductivity = 0.33;
    cfg.method = 'openmeeg';
    headmodel_openmeeg = ft_prepare_headmodel(cfg, bnd_openmeeg);

    cfg = [];
    cfg.elec = dataeeg.elec;
    cfg.headmodel = headmodel_openmeeg;
    cfg.channel = 'EEG';
    cfg.resolution = 1.5;
    sourcemodel_openmeeg = ft_prepare_leadfield(cfg); % inconsistent with dataeeg.labels

  end % function create_leadfields

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 4. perform source analysis
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  function perform_source_analysis

    clc;
    disp('4: inverse solution, ft_sourceanalysis and ft_dipolefitting')

    %% for MEG
    if ~isempty(sourcemodel_meg) || ~isempty(headmodel_meg)

      % do LCMV beamforming
      cfg = [];
      cfg.method = 'lcmv';
      cfg.keepleadfield = 'yes';
      cfg.lcmv.keepfilter = 'yes';
      cfg.lcmv.keepcov = 'yes';
      cfg.lcmv.lambda = '5%';
      cfg.sourcemodel = sourcemodel_meg;
      cfg.headmodel = headmodel_meg;
      sourcelcmv3d1 = ft_sourceanalysis(cfg, MEG_tlck);
      sourcelcmv3d1 = ft_datatype_source(sourcelcmv3d1, 'version', 'latest');

      % project through computed filter
      cfg.rawtrial = 'yes';
      cfg.sourcemodel = keepfields(sourcelcmv3d1, {'pos', 'tri', 'dim', 'inside', 'leadfield', 'leadfielddimord', 'label'});
      ft_sourceanalysis(cfg, MEG_tlck);

      % do MNE
      cfg = [];
      cfg.method = 'mne';
      cfg.mne.keepleadfield = 'yes';
      cfg.mne.keepfilter = 'yes';
      cfg.mne.lambda = 1e4;
      cfg.sourcemodel = sourcemodel_meg;
      cfg.headmodel = headmodel_meg;
      sourcemne3d1 = ft_sourceanalysis(cfg, MEG_tlck);
      sourcemne3d1 = ft_datatype_source(sourcemne3d1, 'version', 'latest');

      cfg.rawtrial = 'yes';
      cfg.sourcemodel = keepfields(sourcemne3d1, {'pos', 'tri', 'dim', 'inside', 'leadfield', 'leadfielddimord', 'label'});
      ft_sourceanalysis(cfg, MEG_tlck);

      % do DICS
      cfg = [];
      cfg.method = 'dics';
      cfg.dics.keepfilter = 'yes';
      cfg.dics.keepleadfield = 'yes';
      cfg.dics.keepcsd = 'yes';
      cfg.dics.lambda = '5%';
      cfg.frequency = 10;
      cfg.sourcemodel = sourcemodel_meg;
      cfg.headmodel = headmodel_meg;
      sourcedics3d1 = ft_sourceanalysis(cfg, MEG_freq);
      sourcedics3d1 = ft_datatype_source(sourcedics3d1, 'version', 'latest');

      cfg.rawtrial = 'yes';
      cfg.sourcemodel = keepfields(sourcedics3d1, {'pos', 'tri', 'dim', 'inside', 'leadfield', 'leadfielddimord', 'label'});
      ft_sourceanalysis(cfg, MEG_freq);

      % do PCC
      cfg = [];
      cfg.method = 'pcc';
      cfg.pcc.keepfilter = 'yes';
      cfg.pcc.keepleadfield = 'yes';
      cfg.pcc.keepcsd = 'yes';
      cfg.pcc.keepmom = 'yes';
      cfg.pcc.lambda = '5%';
      cfg.frequency = 10;
      cfg.sourcemodel = sourcemodel_meg;
      cfg.headmodel = headmodel_meg;
      ft_sourceanalysis(cfg, MEG_freq);

      % do dipolefit
      cfg = [];
      cfg.numdipoles = 1;
      % cfg.nonlinear = 'no';
      cfg.sourcemodel = sourcemodel_meg;
      cfg.headmodel = headmodel_meg;
      cfg.latency = 0.025;
      cfg.channel = [1 3:151];
      ft_dipolefitting(cfg, MEG_tlck_df);
    end

    %% EEG
    if ~isempty(sourcemodel_eeg) || isempty(headmodel_eeg)

      % do LCMV beamforming
      cfg = [];
      cfg.method = 'lcmv';
      cfg.lcmv.keepleadfield = 'yes';
      cfg.lcmv.keepfilter = 'yes';
      cfg.lcmv.keepcov = 'yes';
      cfg.lcmv.lambda = '5%';
      cfg.sourcemodel = sourcemodel_eeg;
      cfg.headmodel = headmodel_eeg;
      sourcelcmv3d1 = ft_sourceanalysis(cfg, EEG_tlck);
      sourcelcmv3d1 = ft_datatype_source(sourcelcmv3d1, 'version', 'latest');

      % project through computed filter
      cfg.rawtrial = 'yes';
      cfg.sourcemodel = keepfields(sourcelcmv3d1, {'pos', 'tri', 'dim', 'inside', 'leadfield', 'leadfielddimord', 'label'});
      ft_sourceanalysis(cfg, EEG_tlck);

      % do MNE
      cfg = [];
      cfg.method = 'mne';
      cfg.mne.keepleadfield = 'yes';
      cfg.mne.keepfilter = 'yes';
      cfg.mne.lambda = 1e4;
      cfg.sourcemodel = sourcemodel_eeg;
      cfg.headmodel = headmodel_eeg;
      sourcemne3d1 = ft_sourceanalysis(cfg, EEG_tlck);
      sourcemne3d1 = ft_datatype_source(sourcemne3d1, 'version', 'latest');

      cfg.rawtrial = 'yes';
      cfg.sourcemodel = keepfields(sourcemne3d1, {'pos', 'tri', 'dim', 'inside', 'leadfield', 'leadfielddimord', 'label'});
      ft_sourceanalysis(cfg, EEG_tlck);

      % do DICS
      cfg = [];
      cfg.method = 'dics';
      cfg.dics.keepfilter = 'yes';
      cfg.dics.keepleadfield = 'yes';
      cfg.dics.keepcsd = 'yes';
      cfg.dics.lambda = '5%';
      cfg.frequency = 10;
      cfg.sourcemodel = sourcemodel_eeg;
      cfg.headmodel = headmodel_eeg;
      sourcedics3d1 = ft_sourceanalysis(cfg, EEG_freq);

      cfg.rawtrial = 'yes';
      cfg.sourcemodel = keepfields(sourcedics3d1, {'pos', 'tri', 'dim', 'inside', 'leadfield', 'leadfielddimord', 'label'});
      ft_sourceanalysis(cfg, EEG_freq);

      % do PCC
      cfg = [];
      cfg.method = 'pcc';
      cfg.pcc.keepfilter = 'yes';
      cfg.pcc.keepleadfield = 'yes';
      cfg.pcc.keepcsd = 'yes';
      cfg.pcc.keepmom = 'yes';
      cfg.pcc.lambda = '5%';
      cfg.frequency = 10;
      cfg.sourcemodel = sourcemodel_eeg;
      cfg.headmodel = headmodel_eeg;
      ft_sourceanalysis(cfg, EEG_freq);

      % do dipolefit
      cfg = [];
      cfg.numdipoles = 1;
      % cfg.nonlinear = 'no';
      cfg.sourcemodel = sourcemodel_eeg;
      cfg.headmodel = headmodel_eeg;
      cfg.latency = 0.025;
      ft_dipolefitting(cfg, EEG_tlck_df);
    end

  end % function perform_source_analysis

end % main function


function x = cvx_r2c( x, dim )

%
% Quick exit for complex quantities
%

if ~isreal( x ),
    error( 'Matrix must be real.' );
end

%
% Determine expansion dimension
%

sx = size( x );
if nargin < 2,
    dim = [ find( sx > 1 ), 1 ];
    dim = dim( 1 );
elseif ~isnumeric( dim ) || dim <= 0 || dim ~= floor( dim ),
    error( 'Second argument must be a dimension.' );
end
nd = length( sx );
if nd < dim || rem( sx( dim ), 2 ) ~= 0,
    error( 'The size of the array along the key dimension must be even.' );
end

%
% Extract the real and imaginary halves
%

[ ndxs{ 1 : nd } ] = deal( ':' );
ndxs{ dim } = 1 : 2 : sx( dim );
xr = cvx_subsref( x, ndxs{:} );
ndxs{ dim } = 2 : 2 : sx( dim );
xi = cvx_subsref( x, ndxs{:} );

%
% Combine
%

x = xr + 1i * xi;

% Copyright 2010 Michael C. Grant and Stephen P. Boyd.
% See the file COPYING.txt for full copyright information.
% The command 'cvx_where' will show where this file is located.


function handle = inventory(game, player)

%% DISPLAY CARDS
res = get(0, 'ScreenSize');
handle = ...
    figure('Name', 'Inventory', ...
           'Units', 'pixels', ...
           'MenuBar', 'none', ...
           'NumberTitle', 'off', ...
           'Position', [res(3:4)/3, 500, 200]);

Infantry = ...
    axes  ('Parent', handle, ...
           'Position', [0.075, 0.1, 0.25, 0.8]);

Cavallery = ...
    axes  ('Parent', handle, ...
           'Position', [0.375, 0.1, 0.25, 0.8]);

Artillery = ...
    axes  ('Parent', handle, ...
           'Position', [0.675, 0.1, 0.25, 0.8]);

imshow('cards/infantry.jpg', 'Parent', Infantry)
imshow('cards/cavallery.jpg', 'Parent', Cavallery)
imshow('cards/artillery.jpg', 'Parent', Artillery)

%% DISPLAY NUMBERS
ud = get(game.Players(player), 'UserData');
n  = ud(3:7);
N  = [sum(n == 1), sum(n == 2), sum(n == 3)];

for i = 1:3
    uicontrol('style', 'text', ...
              'Parent', handle, ...
              'units', 'normalized', ...
              'FontSize', 18, ...
              'FontWeight', 'bold', ...
              'Position', [0.15 + (i-1) * 0.3, 0.2, 0.1, 0.12], ...
              'BackgroundColor', [245 228 156]/255, ...
              'String', num2str(N(i)));
end


% textsc() - places text in screen coordinates and places
%            a title at the top of the figure.
%
% Usage:
%   H = textsc(X,Y,TXT) places the text string, TXT
%   at the normalized coordinates X and Y.  H is the
%   handle to the text object.
%
%   H = textsc(TXT,'title') places a title at the top
%   of the figure window.  This is useful when you
%   want to place a single title above multiple
%   subplots.
%
% Notes: textsc creates an invisible AXES which occupies
% the entire FIGURE.  The units of the AXES are
% normalized (range from 0 to 1).  textsc checks
% all the children of the current FIGURE to determine
% if an AXES exist which meets these criteria already
% exist.  If one does, then it places the text relative
% to that AXES.
%
% Author: John L. Galenski, January 21, 1994

% Written by John L. Galenski III
% All Rights Reserved  January 21, 1994
% LDM031695jlg

function H = textsc(x,y,txt);

% Basic error checking
if nargin < 2
    y = 'title';
end

% Check to see if AXES already exist
ch = get(gcf,'Children');
ax = findobj(gcf,'Type','axes','Tag','TEXTSC');
if isempty(ax)
  ax = axes('Units','Normal','Position',[0 0 1 1], ...
            'Visible','Off','Tag','TEXTSC');
else
  axes(ax)
end

% Place the text
if isstr(y) && isstr(x) && strcmp(lower(y),'title')  % Subplot title
  txt = x;
  x = .5;
  tmp = text('Units','normal','String','tmp','Position',[0 0 0]);
  ext = get(tmp,'Extent');
  delete(tmp)
  H = ext(4);
  y = 1 - .60*H;
end
h = text(x,y,txt,'VerticalAlignment','Middle', ...
         'HorizontalAlignment','Center','interpreter', 'none' );

% Make the original AXES current
if ~isempty(ch)
  set(gcf,'CurrentAxes',min(ch))
end

% Check for output
if nargout == 1
  H = h;
end


function n = ndims( x )
n = length( size( x ) );

% Copyright 2010 Michael C. Grant and Stephen P. Boyd.
% See the file COPYING.txt for full copyright information.
% The command 'cvx_where' will show where this file is located.


function results = run_for_parameters(f, params, varargin)
%RUN_FOR_PARAMETERS Evaluate the given function for different paramters.
%Syntax:
%   results = RUN_FOR_PARAMETERS(f, params[, ...]);
%Inputs:
%   f - Function handle whose must return a single value of the same type
%       each time it is called.
%   params - An n x k matrix or cell array where each row represents a
%            parameter vector. The given function handle should accept
%            k parameters.
%   ... - Options:
%           'Parallel' - Whether to evaluate the function in parallel.
%                        Default value is false.
%           'ParallelLevel' - Set to 0 to run each task for each parameter
%                             vector. Set to 1 to run each task for each
%                             repetition. Default value is 0.
%           'Repeat' - Specify how many times the function will be
%                      evaluated for each parameter vector. This is useful
%                      in running Monte Carlo simulations. Default value is
%                      one.
%           'ShowProgress' - Shows a progress bar. Default value is false.
%Output:
%   results - Depending on the return value of the function. Let r be the
%             number of repeats.
%             1. If f returns a scalar, the returned value will be an n x r
%                matrix;
%             2. If f returns a struct, the returned value will be an n x r
%                struct;
%             3. Otherwise, the returned value will be an n x r cell array.
use_parallel = false;
parallel_level = 0;
show_progress = false;
repeat = 1;
for ii = 1:2:nargin-2
    option_name = varargin{ii};
    option_value = varargin{ii+1};
    switch lower(option_name)
        case 'parallel'
            use_parallel = option_value;
        case 'parallellevel'
            parallel_level = option_value;
        case 'repeat'
            repeat = option_value;
        case 'showprogress'
            show_progress = option_value;
        otherwise
            error('Unknown option ''%s''.', option_name);
    end
end

results = [];
n = size(params, 1);
is_params_cell = iscell(params);
first_run = true;
if use_parallel
    pool = gcp();
    if show_progress
        if parallel_level == 0
            progressbar('reset', n);
        else
            progressbar('reset', n*repeat);
        end
    end
    first_run = true;
    if parallel_level == 0
        % one task per parameter
        % TODO: split into batches when n is large
        for nn = 1:n
            if is_params_cell
                cur_args = params(nn, :);
            else
                cur_args = num2cell(params(nn, :));
            end
            ff(nn) = parfeval(pool, @run_repetitions , 1, f, cur_args, repeat);
        end
        % fetch the results
        for nn = 1:n
            [idx, cur_results] = fetchNext(ff);
            % we need to store a batch of results here
            if first_run
                results = allocate_results(cur_results(1), n, repeat);
                first_run = false;
            end
            results(idx,:) = cur_results;
            if show_progress
                progressbar('advance');
            end
        end
    else
        % one task per repetition
        for nn = 1:n
            if is_params_cell
                cur_args = params(nn, :);
            else
                cur_args = num2cell(params(nn, :));
            end
            for rr = 1:repeat
                ff(rr) = parfeval(pool, f, 1, cur_args{:});
            end
            % we need to fetch the results for each repetition here
            for rr = 1:repeat
                [idx, cur_result] = fetchNext(ff);
                if first_run
                    results = allocate_results(cur_result, n, repeat);
                    first_run = false;
                end
                if isnumeric(results) || isstruct(results)
                    results(nn,idx) = cur_result;
                else
                    results{nn,idx} = cur_result;
                end
                if show_progress
                    progressbar('advance');
                end
            end
        end
    end
    if show_progress
        progressbar('end');
    end
else
    if show_progress
        progressbar('reset', n * repeat);
    end
    for nn = 1:n
        if is_params_cell
            cur_args = params(nn, :);
        else
            cur_args = num2cell(params(nn, :));
        end
        for rr = 1:repeat
            cur_result = f(cur_args{:});
            if first_run
                results = allocate_results(cur_result, n, repeat);
                first_run = false;
            end
            if isnumeric(results) || isstruct(results)
                results(nn,rr) = cur_result;
            else
                results{nn,rr} = cur_result;
            end
            if show_progress
                progressbar('advance');
            end
        end
    end
    if show_progress
        progressbar('end');
    end
end

end

function results = run_repetitions(f, args, repeat)
% A wrapper function to evaluate f multiple times.
first_run = true;
for rr = 1:repeat
    cur_result = f(args{:});
    if first_run
        results = allocate_results(cur_result, 1, repeat);
        first_run = false;
    end
    if isnumeric(results) || isstruct(results)
        results(rr) = cur_result;
    else
        results{rr} = cur_result;
    end
end
end

function results = allocate_results(sample_result, n, r)
% determine return type
if isnumeric(sample_result) && isscalar(sample_result)
    results = zeros(n, r);
elseif isstruct(sample_result)
    field_names = fieldnames(sample_result);
    args = reshape([field_names'; cell(1, length(field_names))], 1, []);
    results = repmat(struct(args{:}), n, r);
else
    results = cell(n, r);
end
end