function update_temporal_parallel(obj, use_parallel, use_c_hat)
%% update the the temporal components for all neurons
% input:
%   use_parallel: boolean, do initialization in patch mode or not.
%       default(true); we recommend you to set it false only when you want to debug the code.
%   use_c_hat: use the previous estimation of C

%% Author: Pengcheng Zhou, Columbia University, 2017
%% email: zhoupc1988@gmail.com

%% process parameters

try
    % map data
    mat_data = obj.P.mat_data;

    % folders and files for saving the results
    log_file =  obj.P.log_file;
    flog = fopen(log_file, 'a');
    log_data = matfile(obj.P.log_data, 'Writable', true); %#ok<NASGU>

    % dimension of data
    dims = mat_data.dims;
    d1 = dims(1);
    d2 = dims(2);
    T = dims(3);
    obj.options.d1 = d1;
    obj.options.d2 = d2;

    % parameters for patching information
    patch_pos = mat_data.patch_pos;
    block_pos = mat_data.block_pos;

    % number of patches
    [nr_patch, nc_patch] = size(patch_pos);
catch
    error('No data file selected');
end
fprintf('\n-----------------UPDATE TEMPORAL---------------------------\n');

% frames to be loaded
frame_range = obj.frame_range;
T = diff(frame_range) + 1;

% threshold for detecting large residuals
thresh_outlier = obj.options.thresh_outlier;

% use parallel or not
if ~exist('use_parallel', 'var')||isempty(use_parallel)
    use_parallel = true; %don't save initialization procedure
end

% use previous estimation of C
if ~exist('use_c_hat', 'var') || isempty(use_c_hat)
    use_c_hat = true;
end
% options
options = obj.options;
bg_model = options.background_model;
maxIter = options.maxIter;

%% identify existing neurons within each patch
A = cell(nr_patch, nc_patch);
C = cell(nr_patch, nc_patch);
% if strcmpi(bg_model, 'ring')
    A_prev = A;
    C_prev = C;
% end
sn = cell(nr_patch, nc_patch);
ind_neurons = cell(nr_patch, nc_patch);

AA = cell(nr_patch, nc_patch);   % save the ai^T*ai for each neuron

for mpatch=1:(nr_patch*nc_patch)
    if strcmpi(bg_model, 'ring')
        tmp_block = block_pos{mpatch};
    else
        tmp_block = patch_pos{mpatch};
    end
    % find the neurons that are within the block
    mask = zeros(d1, d2);
    mask(tmp_block(1):tmp_block(2), tmp_block(3):tmp_block(4)) = 1;
    ind = (reshape(mask(:), 1, [])* obj.A>0);
    A{mpatch}= obj.A(logical(mask), ind);
    sn{mpatch} = obj.P.sn(logical(mask));
    C{mpatch} = obj.C(ind, :);
    ind_neurons{mpatch} = find(ind);    % indices of the neurons within each patch

    if strcmpi(bg_model, 'ring')
        ind = find(reshape(mask(:)==1, 1, [])* full(obj.A_prev)>0);
        A_prev{mpatch}= obj.A_prev((mask>0), ind);
        C_prev{mpatch} = obj.C_prev(ind, :);
    end
end
%% prepare for the variables for computing the background.
bg_model = obj.options.background_model;
bg_ssub = obj.options.bg_ssub;
W = obj.W;
b0 = obj.b0;
b = obj.b;
f = obj.f;

%% start updating temporal components
C_raw_new = C;
deconv_flag = obj.options.deconv_flag;
if options.deconv_flag
    deconv_options = obj.options.deconv_options;
else
    deconv_options = [];
end
if use_parallel
    parfor mpatch=1:(nr_patch*nc_patch)
        % no neurons within the patch
        [r, c] = ind2sub([nr_patch, nc_patch], mpatch);
        tmp_patch = patch_pos{mpatch};     %[r0, r1, c0, c1], patch location
        if strcmpi(bg_model, 'ring')
            tmp_block = block_pos{mpatch};
        else
            tmp_block = patch_pos{mpatch};
        end
        C_patch = C{mpatch};                % previous estimation of neural activity

        if isempty(C_patch)
            fprintf('Patch (%2d, %2d) is done. %2d X %2d patches in total. \n', r, c, nr_patch, nc_patch);
            continue;
        end
        A_patch = A{mpatch};

        % use ind_patch to indicate pixels within the patch
        ind_patch = false(diff(tmp_block(1:2))+1, diff(tmp_block(3:4))+1);
        ind_patch((tmp_patch(1):tmp_patch(2))-tmp_block(1)+1, (tmp_patch(3):tmp_patch(4))-tmp_block(3)+1) = true;

        % get data
        if strcmpi(bg_model, 'ring')
            % including areas outside of the patch for recorving background
            % in the ring model
            Ypatch = get_patch_data(mat_data, tmp_patch, frame_range, true);
        else
            Ypatch = get_patch_data(mat_data, tmp_patch, frame_range, false);
        end
        [nr_block, nc_block, ~] = size(Ypatch);

        % get background
        if strcmpi(bg_model, 'ring')
            A_patch_prev = A_prev{mpatch};
            C_patch_prev = C_prev{mpatch};
            W_ring = W{mpatch};
            b0_ring = b0{mpatch};
            Ypatch = reshape(Ypatch, [], T);
            tmp_Y = double(Ypatch)-A_patch_prev*C_patch_prev;
            if bg_ssub==1
                Ypatch = bsxfun(@minus, double(Ypatch(ind_patch,:))- W_ring*tmp_Y, b0_ring-W_ring*mean(tmp_Y, 2));
            else
                % get the dimension of the downsampled data
                [d1s, d2s] = size(imresize(zeros(nr_block, nc_block), 1/bg_ssub));
                % downsample data and reconstruct B^f
                temp = reshape(bsxfun(@minus, tmp_Y, mean(tmp_Y, 2)), nr_block, nc_block, []);
                temp = imresize(temp, 1./bg_ssub);
                Bf = reshape(W_ring*reshape(temp, [], T), d1s, d2s, T);
                Bf = imresize(Bf, [nr_block, nc_block]);
                Bf = reshape(Bf, [], T);

                Ypatch = bsxfun(@minus, double(Ypatch(ind_patch, :)) - Bf(ind_patch, :), b0_ring);
            end
        elseif strcmpi(bg_model, 'nmf')
            b_nmf = b{mpatch};
            f_nmf = f{mpatch};
            Ypatch = double(reshape(Ypatch, [], T))- b_nmf*f_nmf;
        else
            b_svd = b{mpatch};
            f_svd = f{mpatch};
            b0_svd = b0{mpatch};
            Ypatch = double(reshape(Ypatch, [], T)) - bsxfun(@plus, b_svd*f_svd, b0_svd);
        end

        % using HALS to update temporal components
        if ~use_c_hat
            [AA{mpatch}, C_raw_new{mpatch}] = fast_temporal(Ypatch, A_patch(ind_patch,:));
        else
            [~, C_raw_new{mpatch}] = HALS_temporal(Ypatch, A_patch(ind_patch,:), C_patch, maxIter, deconv_options);
            AA{mpatch}= sum(A_patch(ind_patch,:).^2, 1);
        end

        fprintf('Patch (%2d, %2d) is done. %2d X %2d patches in total. \n', r, c, nr_patch, nc_patch);
    end
else
    for mpatch=1:(nr_patch*nc_patch)
        % no neurons within the patch
        [r, c] = ind2sub([nr_patch, nc_patch], mpatch);
        tmp_patch = patch_pos{mpatch};     %[r0, r1, c0, c1], patch location
        if strcmpi(bg_model, 'ring')
            tmp_block = block_pos{mpatch};
        else
            tmp_block = patch_pos{mpatch};
        end

        C_patch = C{mpatch};                % previous estimation of neural activity

        if isempty(C_patch)
            fprintf('Patch (%2d, %2d) is done. %2d X %2d patches in total. \n', r, c, nr_patch, nc_patch);
            continue;
        end
        A_patch = A{mpatch};

        % use ind_patch to indicate pixels within the patch
        ind_patch = false(diff(tmp_block(1:2))+1, diff(tmp_block(3:4))+1);
        ind_patch((tmp_patch(1):tmp_patch(2))-tmp_block(1)+1, (tmp_patch(3):tmp_patch(4))-tmp_block(3)+1) = true;

        % get data
        if strcmpi(bg_model, 'ring')
            % including areas outside of the patch for recorving background
            % in the ring model
            Ypatch = get_patch_data(mat_data, tmp_patch, frame_range, true);
        else
            Ypatch = get_patch_data(mat_data, tmp_patch, frame_range, false);
        end
        [nr_block, nc_block, ~] = size(Ypatch);

        % get background
        if strcmpi(bg_model, 'ring')
            A_patch_prev = A_prev{mpatch};
            C_patch_prev = C_prev{mpatch};
            W_ring = W{mpatch};
            b0_ring = b0{mpatch};
            Ypatch = reshape(Ypatch, [], T);
            tmp_Y = double(Ypatch)-A_patch_prev*C_patch_prev;
            if bg_ssub==1
                Ypatch = bsxfun(@minus, double(Ypatch(ind_patch,:))- W_ring*tmp_Y, b0_ring-W_ring*mean(tmp_Y, 2));
            else
                % get the dimension of the downsampled data
                [d1s, d2s] = size(imresize(zeros(nr_block, nc_block), 1/bg_ssub));
                % downsample data and reconstruct B^f
                temp = reshape(bsxfun(@minus, tmp_Y, mean(tmp_Y, 2)), nr_block, nc_block, []);
                temp = imresize(temp, 1./bg_ssub);
                Bf = reshape(W_ring*reshape(temp, [], T), d1s, d2s, T);
                Bf = imresize(Bf, [nr_block, nc_block]);
                Bf = reshape(Bf, [], T);

                Ypatch = bsxfun(@minus, double(Ypatch(ind_patch, :)) - Bf(ind_patch, :), b0_ring);
            end
        elseif strcmpi(bg_model, 'nmf')
            b_nmf = b{mpatch};
            f_nmf = f{mpatch};
            Ypatch = double(reshape(Ypatch, [], T))- b_nmf*f_nmf;
        else
            b_svd = b{mpatch};
            f_svd = f{mpatch};
            b0_svd = b0{mpatch};
            Ypatch = double(reshape(Ypatch, [], T)) - bsxfun(@plus, b_svd*f_svd, b0_svd);
        end

        % using HALS to update temporal components
        if ~use_c_hat
            [AA{mpatch}, C_raw_new{mpatch}] = fast_temporal(Ypatch, A_patch(ind_patch,:));
        else
            [~, C_raw_new{mpatch}] = HALS_temporal(Ypatch, A_patch(ind_patch,:), C_patch, maxIter, deconv_options);
            AA{mpatch}= sum(A_patch(ind_patch,:).^2, 1);
        end

        fprintf('Patch (%2d, %2d) is done. %2d X %2d patches in total. \n', r, c, nr_patch, nc_patch);
    end
end
%% collect results
K = size(obj.C, 1);
C_new = zeros(K, T);
aa = zeros(K, 1);
fprintf('Collect results from all small patches...\n');
for mpatch=1:(nr_patch*nc_patch)
    C_raw_patch = C_raw_new{mpatch};
    ind_patch = ind_neurons{mpatch};
    aa_patch = AA{mpatch};
    for m=1:length(ind_patch)
        k = ind_patch(m);
        C_new(k, :) = C_new(k, :) + C_raw_patch(m,:) * aa_patch(m);
        aa(k) = aa(k) + aa_patch(m);
    end
end
aa(aa==0) = 1;
obj.C_raw = bsxfun(@times, C_new, 1./aa);
fprintf('Deconvolve and denoise all temporal traces again...\n');
if deconv_flag
    obj.C = obj.deconvTemporal();
else
    obj.C_raw = bsxfun(@minus, obj.C_raw, min(obj.C_raw,[],2));
    obj.C = obj.C_raw;
end
fprintf('Done!\n');

%% upadte b0
if strcmpi(bg_model, 'ring')
    fprintf('Update the constant baselines for all pixels..\n');
    obj.b0_new = cell2mat(obj.P.Ymean)-obj.reshape(obj.A*mean(obj.C,2), 2); -obj.reconstruct_b0();
    fprintf('Done!\n');
end

%% save the results to log
fprintf(flog, '[%s]\b', get_minute());
fprintf(flog, 'Finished updating temporal components.\n');
if obj.options.save_intermediate
    temporal.C_raw = obj.C_raw;
    temporal.ids = obj.ids;
    temporal.C = obj.C;
    temporal.S = obj.S;
    temporal.P.kernel_pars = obj.P.kernel_pars;
    temporal.b0_new = obj.b0_new;
    tmp_str = get_date();
    tmp_str=strrep(tmp_str, '-', '_');
    eval(sprintf('log_data.temporal_%s = temporal;', tmp_str));
    fprintf(flog, '\tThe results were saved as intermediate_results.temporal_%s\n\n', tmp_str);
end
fclose(flog);

function [aa, C_raw] = fast_temporal(Y, A)
%% estimate temporal components using the mean fluorescence
% input:
%   Y:  d*T, fluorescence data
%   A:  d*K, spatial components
% output:
%   aa: K*1, energy of each Ai
%   C_raw: K*T, the temporal components before thresholded or being
%   denoised.
% Author: Pengcheng Zhou, Columbia University, 2017
% zhoupc1988@gmail.com

%% options

%% initialization
% roughly initialize C
tmpA = bsxfun(@times, A, 1./max(A, [], 1));
ind_max = bsxfun(@ge, tmpA, 0.5); %max(tmpA, [], 2));
tmp_A = A.*double(ind_max);
aa = sum(tmp_A.^2, 1);
ind = (aa==0);
aa(ind) = inf;
C_raw = bsxfun(@times, tmp_A'*Y, 1./aa');
aa(ind) = 0;
% if any(ind)  % explain the residual using weak neurons
%     tmp_A = A(:, ind);
%     C_raw(ind, :) = (tmp_A'*tmp_A)\(tmp_A'*Y - tmp_A'*A*C_raw);
%     aa(ind) = sum(tmp_A.^2, 1);
% end


% STD_PREPARE_NEIGHBORS - prepare Fieldtrip channel neighbor structure.
%                           Only prepare the structure if necessary based
%                           on statistical options in STUDY.etc.statistics.
%                           Use the 'force' option to force preparing the
%                           matrix.
%
% Usage:
%   >> [STUDY neighbors] = std_prepare_neighbors( STUDY, ALLEEG, 'key', val)
%
% Inputs:
%   STUDY        - an EEGLAB STUDY set of loaded EEG structures
%   ALLEEG       - ALLEEG vector of one or more loaded EEG dataset structures
%
% Optional inputs:
%   'force'        - ['on'|'off'] force generating the structure irrespective
%                    of the statistics options selected. Default is 'off'.
%   'channels'     - [cell] list of channels to include in the matrix
%   'method'       - [string] method for preparing. See ft_prepare_neighbors
%   'neighbordist' - [float] max distance. See ft_prepare_neighbors
%
% Note: other ft_prepare_neighbors fields such as template, layout may
%       also be used as optional keywords.
%
% Outputs:
%   STUDY       - an EEGLAB STUDY set of loaded EEG structures
%   neighbors   - Fieldtrip channel neighbour structure
%   limostruct  - structure compatible with LIMO (need to be saved in a file)
%
% Author: Arnaud Delorme, SCCN, UCSD, 2012-
%
% See also: STATCONDFIELDTRIP

% Copyright (C) Arnaud Delorme
%
% This file is part of EEGLAB, see http://www.eeglab.org
% for the documentation and details.
%
% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are met:
%
% 1. Redistributions of source code must retain the above copyright notice,
% this list of conditions and the following disclaimer.
%
% 2. Redistributions in binary form must reproduce the above copyright notice,
% this list of conditions and the following disclaimer in the documentation
% and/or other materials provided with the distribution.
%
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
% THE POSSIBILITY OF SUCH DAMAGE.

function [STUDY neighbors limostruct] = std_prepare_neighbors(STUDY, ALLEEG, varargin);

neighbors = [];
if nargin < 2
    return;
end

[opt addopts] = finputcheck( varargin, {  'force'    'string'  { 'on','off' }   'off';
                                          'channels' 'cell'    {}               {} }, 'std_stat', 'ignore');

if strcmpi(opt.force, 'on') || (strcmpi(STUDY.etc.statistics.fieldtrip.mcorrect, 'cluster') && ...
        strcmpi(STUDY.etc.statistics.mode, 'fieldtrip') && (strcmpi(STUDY.etc.statistics.groupstats, 'on') || strcmpi(STUDY.etc.statistics.condstats, 'on')))

    EEG = eeg_emptyset;
    EEG.chanlocs = eeg_mergelocs(ALLEEG.chanlocs);
    if isempty(EEG.chanlocs)
        disp('std_prepare_neighbors: cannot prepare channel neighbour structure because of empty channel structures');
        return;
    end

    if ~isempty(STUDY.etc.statistics.fieldtrip.channelneighbor) && isempty(addopts) && ...
        length(STUDY.etc.statistics.fieldtrip.channelneighbor) == length(EEG.chanlocs)

        disp('Using stored channel neighbour structure');
        neighbors = STUDY.etc.statistics.fieldtrip.channelneighbor;

    else

        if ~isempty(opt.channels)
            indChans = eeg_chaninds(EEG, opt.channels);
            EEG.chanlocs = EEG.chanlocs(indChans);
        end

        EEG.nbchan   = length(EEG.chanlocs);
        EEG.data     = zeros(EEG.nbchan,100,1);
        EEG.trials   = 1;
        EEG.pnts     = 100;
        EEG.xmin     = 0;
        EEG.srate    = 1;
        EEG.xmax     = 99;
        EEG = eeg_checkset(EEG);
        tmpcfg = eeglab2fieldtrip(EEG, 'preprocessing', 'none');

        % call the function that find channel neighbors
        % ---------------------------------------------
        addparams = eval( [ '{' STUDY.etc.statistics.fieldtrip.channelneighborparam '}' ]);
        for index = 1:2:length(addparams)
            tmpcfg = setfield(tmpcfg, addparams{index}, addparams{index+1});
        end
        for index = 1:2:length(addopts)
            tmpcfg = setfield(tmpcfg, addopts{index}, addopts{index+1});
        end
        warning off;
        if isfield(EEG.chanlocs, 'theta') && ~isempty(EEG.chanlocs(1).theta)
            tmpcfg = rmfield(tmpcfg, 'cfg');
            tmpcfg2 = tmpcfg;
            tmpcfg  = rmfield(tmpcfg, 'label'); % first input must not be data
            tmpcfg2 = rmfield(tmpcfg2, 'method'); % second input must not be method
            if isfield(tmpcfg, 'trialinfo')
                tmpcfg  = rmfield(tmpcfg, 'trialinfo'); % first input must not be data
            end
            % tmpcfg = rmfield(tmpcfg, 'label');
            % --> removing label seems to make ft_prepare_neighbours to crash
            cfg.neighbors = ft_prepare_neighbours(tmpcfg, tmpcfg2);
            neighbors = cfg.neighbors;
        else
            neighbors = [];
        end
        warning on;

    end

    STUDY.etc.statistics.fieldtrip.channelneighbor = neighbors;

    if nargout > 2
        limostruct.expected_chanlocs = EEG.chanlocs;

        if ~isempty(neighbors)
            allLabels = { neighbors.label };
            limostruct.channeighbstructmat = zeros(length(EEG.chanlocs));
            for iN = 1:length(neighbors)
                if ~isequal(neighbors(iN).label, limostruct.expected_chanlocs(iN).labels)
                    error('Wrong label');
                else
                    [tmp posChan] = intersect( allLabels, neighbors(iN).neighblabel);
                    limostruct.channeighbstructmat(iN,posChan) = 1;
                    limostruct.channeighbstructmat(posChan,iN) = 1;
                end
            end
        else
            limostruct.channeighbstructmat = ones(length(EEG.chanlocs));
        end
    end
end


%RevoluteMDH Robot manipulator Revolute link class for MDH convention
%
% A subclass of the Link class: holds all information related to a robot
% link such as kinematics parameters, rigid-body inertial parameters, motor
% and transmission parameters.
%
% Notes::
% - This is reference class object
% - Link class objects can be used in vectors and arrays
% - Modified Denavit-Hartenberg parameters are used
%
% References::
% - Robotics, Vision & Control, Chap 7
%   P. Corke, Springer 2011.
%
% See also Link, Prismatic, SerialLink.
% See also Link, PrismaticMDH, Revolute, SerialLink.

% Copyright (C) 1993-2015, by Peter I. Corke
%
% This file is part of The Robotics Toolbox for MATLAB (RTB).
%
% RTB is free software: you can redistribute it and/or modify
% it under the terms of the GNU Lesser General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% RTB is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU Lesser General Public License for more details.
%
% You should have received a copy of the GNU Leser General Public License
% along with RTB.  If not, see <http://www.gnu.org/licenses/>.
%
% http://www.petercorke.com

classdef RevoluteMDH < Link
    methods
        function L = RevoluteMDH(varargin)
            L = L@Link(varargin{:});

            if nargin == 0
                L.theta = [];
            end
            L.sigma = 0;
            if isempty(L.d)
                L.d = 0;
            end
            L.mdh = 1;
            if ~isempty(L.theta)
                error('theta cannot be specified for a revolute link');
            end
        end

    end
end


function P = subsref(P,String)
%subsref           Overloaded indexing

if length(String)>1
    R = subsref(P,String(1));
    P = subsref(R,String(2:end));
    return
end
switch String.type
    case '.'
        switch String.subs
            case 'Constraints'
                P = P.Constraints;
            case 'Objective'
                P = P.Objective;
            case 'Options'
                P = P.Options;
            case 'maximize'
                maximize(P);
            case 'minimize'
                minimize(P);
            otherwise
                error('Indexing type not supported');
        end
    case '()'
        if isa(String.subs{1},'struct')
            P.Options = String.subs{1};
        elseif isa(String.subs{1},'lmi') | isa(String.subs{1},'constraint')
            P = [P, String.subs{1}];
        else
            error('Indexing type not supported');
        end
    otherwise
        error('Indexing type not supported');
end


clear all;
clc;

addpath('../utils');
addpath('../models');
addpath('../vital');

run ../matconvnet/matlab/vl_setupnn ;

global gpu;
gpu=true;

test_seq='Bolt';
conf = genConfig('otb',test_seq);

net=fullfile('../models/otbModel.mat');

result = vital_run(conf.imgList, conf.gt(1,:), net, true);


function [cfg] = ft_topoplotCC(cfg, freq)

% FT_TOPOPLOTCC plots the coherence between channel pairs
%
% Use as
%  ft_topoplotCC(cfg, freq)
%
% The configuration should contain:
%   cfg.feedback    = string (default = 'textbar')
%   cfg.layout      = specification of the layout, see FT_PREPARE_LAYOUT
%   cfg.foi         = the frequency of interest which is to be plotted (default is the first frequency bin)
%   cfg.widthparam  = string, parameter to be used to control the line width (see below)
%   cfg.alphaparam  = string, parameter to be used to control the opacity (see below)
%   cfg.colorparam  = string, parameter to be used to control the line color
%
% The widthparam should be indicated in pixels, e.g. usefull numbers are 1
% and larger.
%
% The alphaparam should be indicated as opacity between 0 (fully transparent)
% and 1 (fully opaque).
%
% The default is to plot the connections as lines, but you can also use
% bidirectional arrows:
%    cfg.arrowhead    = string, 'none', 'stop', 'start', 'both' (default = 'none')
%    cfg.arrowsize    = scalar, size of the arrow head in figure units,
%                       i.e. the same units as the layout (default is automatically determined)
%    cfg.arrowoffset  = scalar, amount that the arrow is shifted to the side in figure units,
%                       i.e. the same units as the layout (default is automatically determined)
%    cfg.arrowlength  = scalar, amount by which the length is reduced relative to the complete line (default = 0.8)
%
% To facilitate data-handling and distributed computing you can use
%   cfg.inputfile   =  ...
% If you specify this option the input data will be read from a *.mat
% file on disk. This mat files should contain only a single variable named 'data',
% corresponding to the input structure. For this particular function, the input should be
% structured as a cell-array.
%
% See also FT_PREPARE_LAYOUT, FT_MULTIPLOTCC, FT_CONNECTIVITYPLOT

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

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

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

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

% check if the input data is valid for this function
freq = ft_checkdata(freq, 'cmbrepresentation', 'sparse');

% check if the input cfg is valid for this function
cfg = ft_checkconfig(cfg, 'required', {'foi', 'layout'});

% set the defaults
cfg.feedback    = ft_getopt(cfg, 'feedback',    'text');
cfg.alphaparam  = ft_getopt(cfg, 'alphaparam',  []);
cfg.widthparam  = ft_getopt(cfg, 'widthparam',  []);
cfg.colorparam  = ft_getopt(cfg, 'colorparam',  'cohspctrm');
cfg.newfigure   = ft_getopt(cfg, 'newfigure',   'yes');
cfg.arrowhead   = ft_getopt(cfg, 'arrowhead',   'none');  % none, stop, start, both
cfg.arrowsize   = ft_getopt(cfg, 'arrowsize',   nan);     % length of the arrow head, should be in in figure units, i.e. the same units as the layout
cfg.arrowoffset = ft_getopt(cfg, 'arrowoffset', nan);     % absolute, should be in figure units, i.e. the same units as the layout
cfg.arrowlength = ft_getopt(cfg, 'arrowlength', 0.8);     % relative to the complete line
cfg.linestyle   = ft_getopt(cfg, 'linestyle',   []);
cfg.colormap    = ft_getopt(cfg, 'colormap',    colormap);
cfg.renderer    = ft_getopt(cfg, 'renderer',    []);      % let MATLAB decide on the default

tmpcfg = keepfields(cfg, {'layout', 'rows', 'columns', 'commentpos', 'scalepos', 'elec', 'grad', 'opto', 'showcallinfo'});
lay = ft_prepare_layout(tmpcfg, freq);

beglabel = freq.labelcmb(:,1);
endlabel = freq.labelcmb(:,2);
ncmb     = size(freq.labelcmb,1);

% select the data to be used in the figure
fbin = nearest(freq.freq, cfg.foi);

if isfield(freq, cfg.widthparam)
  widthparam = freq.(cfg.widthparam)(:,fbin);
else
  widthparam = ones(ncmb,1);
end

if isfield(freq, cfg.alphaparam)
  alphaparam = freq.(cfg.alphaparam)(:,fbin);
else
  alphaparam = [];
end

if isfield(freq, cfg.colorparam)
  colorparam = freq.(cfg.colorparam)(:,fbin);
else
  colorparam = [];
end

if strcmp(cfg.newfigure, 'yes')
  figure
end

hold on
axis equal

if isnan(cfg.arrowsize)
  % use the size of the figure to estimate a decent number
  siz = axis;
  cfg.arrowsize = (siz(2) - siz(1))/50;
  ft_warning('using an arrowsize of %f', cfg.arrowsize);
end

if isnan(cfg.arrowoffset)
  % use the size of the figure to estimate a decent number
  siz = axis;
  cfg.arrowoffset = (siz(2) - siz(1))/100;
  ft_warning('using an arrowoffset of %f', cfg.arrowoffset);
end

rgb = cfg.colormap;
if ~isempty(colorparam)
  cmin = min(colorparam(:));
  cmax = max(colorparam(:));

  % this line creates a sorting vector that cause the most extreme valued arrows to be plotted last
  [srt, srtidx] = sort(abs(colorparam));

  colorparam = (colorparam - cmin)./(cmax-cmin);
  colorparam = round(colorparam * (size(rgb,1)-1) + 1);
end

if strcmp(cfg.newfigure, 'yes')
  ft_plot_layout(lay, 'label', 'no', 'box', 'off');
end

% fix the limits for the axis
axis(axis);

if ~exist('srtidx', 'var')
  srtidx = 1:ncmb;
end

ft_progress('init', cfg.feedback, 'plotting connections...');

for i=srtidx(:)'

  if strcmp(beglabel{i}, endlabel{i})
    % skip autocombinations
    continue
  end

  if widthparam(i)>0 && (isempty(alphaparam)||alphaparam(i)>0)
    ft_progress(i/ncmb, 'plotting connection %d from %d (%s -> %s)\n', i, ncmb, beglabel{i}, endlabel{i});

    begindx = strcmp(beglabel{i}, lay.label);
    endindx = strcmp(endlabel{i}, lay.label);
    xbeg = lay.pos(begindx,1);
    ybeg = lay.pos(begindx,2);
    xend = lay.pos(endindx,1);
    yend = lay.pos(endindx,2);

    if isempty(cfg.linestyle)
      if strcmp(cfg.arrowhead, 'none')
        x = [xbeg xend]';
        y = [ybeg yend]';
        % h = line(x, y);
        h = patch(x, y, 1);
      else
        arrowbeg  = [xbeg ybeg];
        arrowend  = [xend yend];
        center    = (arrowbeg+arrowend)/2;
        direction = (arrowend - arrowbeg);
        direction = direction/norm(direction);
        offset    = [direction(2) -direction(1)];
        arrowbeg  = cfg.arrowlength * (arrowbeg-center) + center + cfg.arrowoffset * offset;
        arrowend  = cfg.arrowlength * (arrowend-center) + center + cfg.arrowoffset * offset;

        h = arrow(arrowbeg, arrowend, 'Ends', cfg.arrowhead, 'length', 0.05);

      end % if arrow
      if ~isempty(widthparam)
        set(h, 'LineWidth', widthparam(i));
      end

      if ~isempty(alphaparam)
        set(h, 'EdgeAlpha', alphaparam(i));
        set(h, 'FaceAlpha', alphaparam(i)); % for arrowheads
      end

      if ~isempty(colorparam)
        set(h, 'EdgeColor', rgb(colorparam(i),:));
        set(h, 'FaceColor', rgb(colorparam(i),:)); % for arrowheads
      end
    elseif ~isempty(cfg.linestyle)

      % new style of plotting, using curved lines, this does not allow for
      % alpha mapping on the line segment
      switch cfg.linestyle
        case 'curve'
          tmp = cscvn([xbeg mean([xbeg,xend])*0.8 xend;ybeg mean([ybeg,yend])*0.8 yend]);
          pnt = fnplt(tmp);
          h   = line(pnt(1,:)', pnt(2,:)');

          if ~isempty(widthparam)
            set(h, 'LineWidth', widthparam(i));
          end
          if ~isempty(colorparam)
            set(h, 'Color', rgb(colorparam(i),:));
          end

          % deal with the arrow
          if ~strcmp(cfg.arrowhead, 'none')
            arrowbeg  = pnt(:,1)';
            arrowend  = pnt(:,end)';
            directionbeg = (arrowbeg - pnt(:,2)');
            directionend = (arrowend - pnt(:,end-1)');

            directionbeg = directionbeg/norm(directionbeg);
            directionend = directionend/norm(directionend);

            switch cfg.arrowhead
              case 'stop'
                pnt1 = arrowend - 0.05*directionend + 0.02*[directionend(2) -directionend(1)];
                pnt2 = arrowend;
                pnt3 = arrowend - 0.05*directionend - 0.02*[directionend(2) -directionend(1)];
                h_arrow = patch([pnt1(1) pnt2(1) pnt3(1)]', [pnt1(2) pnt2(2) pnt3(2)]', [0 0 0]);

              case 'start'
                pnt1 = arrowbeg - 0.05*directionbeg + 0.02*[directionbeg(2) -directionbeg(1)];
                pnt2 = arrowbeg;
                pnt3 = arrowbeg - 0.05*directionbeg - 0.02*[directionbeg(2) -directionbeg(1)];
                h_arrow = patch([pnt1(1) pnt2(1) pnt3(1)]', [pnt1(2) pnt2(2) pnt3(2)]', [0 0 0]);

              case 'both'
                pnt1 = arrowbeg - 0.05*directionbeg + 0.02*[directionbeg(2) -directionbeg(1)];
                pnt2 = arrowbeg;
                pnt3 = arrowbeg - 0.05*directionbeg - 0.02*[directionbeg(2) -directionbeg(1)];
                h_arrow(1) = patch([pnt1(1) pnt2(1) pnt3(1)]', [pnt1(2) pnt2(2) pnt3(2)]', [0 0 0]);

                pnt1 = arrowend - 0.05*directionend + 0.02*[directionend(2) -directionend(1)];
                pnt2 = arrowend;
                pnt3 = arrowend - 0.05*directionend - 0.02*[directionend(2) -directionend(1)];
                h_arrow(2) = patch([pnt1(1) pnt2(1) pnt3(1)]', [pnt1(2) pnt2(2) pnt3(2)]', [0 0 0]);

            end
          else
            h_arrow = [];
          end

          if ~isempty(widthparam)
            set(h, 'LineWidth', widthparam(i));
            if ~isempty(h_arrow)
              set(h_arrow, 'LineWidth', widthparam(i));
            end
          end
          if ~isempty(colorparam)
            set(h, 'Color', rgb(colorparam(i),:));
            if ~isempty(h_arrow)
              set(h_arrow, 'Edgecolor', rgb(colorparam(i),:));
              set(h_arrow, 'Facecolor', rgb(colorparam(i),:));
            end
          end

          if ~isempty(alphaparam)
            if ~isempty(h_arrow)
              set(h_arrow, 'EdgeAlpha', alphaparam(i));
              set(h_arrow, 'FaceAlpha', alphaparam(i)); % for arrowheads
            end
          end

        otherwise
          ft_error('unsupported linestyle specified');
      end

    end % if empty linestyle
  end
end % for srtindx

ft_progress('close');

% this is needed for the figure title
if isfield(cfg, 'dataname') && ~isempty(cfg.dataname)
  dataname = cfg.dataname;
elseif isfield(cfg, 'inputfile') && ~isempty(cfg.inputfile)
  dataname = cfg.inputfile;
elseif nargin>1
  dataname = arrayfun(@inputname, 2:nargin, 'UniformOutput', false);
else
  dataname = {};
end

% set the figure window title
if ~isempty(dataname)
  set(gcf, 'Name', sprintf('%d: %s: %s', double(gcf), mfilename, join_str(', ', dataname)));
else
  set(gcf, 'Name', sprintf('%d: %s', double(gcf), mfilename));
end
set(gcf, 'NumberTitle', 'off');

% set renderer if specified
if ~isempty(cfg.renderer)
  set(gcf, 'renderer', cfg.renderer)
end

% improve the fit in the axis
axis tight

% do the general cleanup and bookkeeping at the end of the function
ft_postamble debug
ft_postamble trackconfig
ft_postamble previous freq
ft_postamble provenance
ft_postamble savefig

% add a menu to the figure, but only if the current figure does not have subplots
menu_fieldtrip(gcf, cfg, false);

if ~ft_nargout
  % don't return anything
  clear cfg
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% SUBFUNCTION for plotting arrows, see also fieldtrip/private/arrow
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function h = arrow(arrowbeg, arrowend, varargin)
ends   = ft_getopt(varargin, 'ends');
length = ft_getopt(varargin, 'length'); % the length of the arrow head, in figure units
color  = [0 0 0]; % in RGB

direction = (arrowend - arrowbeg);
direction = direction/norm(direction);
offset    = [direction(2) -direction(1)];

pnt1 = arrowbeg;
pnt2 = arrowend;
h = patch([pnt1(1) pnt2(1)], [pnt1(2) pnt2(2)], color);

switch ends
  case 'stop'
    pnt1 = arrowend - length*direction + 0.4*length*offset;
    pnt2 = arrowend;
    pnt3 = arrowend - length*direction - 0.4*length*offset;
    h(end+1) = patch([pnt1(1) pnt2(1) pnt3(1)]', [pnt1(2) pnt2(2) pnt3(2)]', color);

  case 'start'
    pnt1 = arrowbeg + length*direction + 0.4*length*offset;
    pnt2 = arrowbeg;
    pnt3 = arrowbeg + length*direction - 0.4*length*offset;
    h(end+1) = patch([pnt1(1) pnt2(1) pnt3(1)]', [pnt1(2) pnt2(2) pnt3(2)]', color);

  case 'both'
    pnt1 = arrowend - length*direction + 0.4*length*offset;
    pnt2 = arrowend;
    pnt3 = arrowend - length*direction - 0.4*length*offset;
    h(end+1) = patch([pnt1(1) pnt2(1) pnt3(1)]', [pnt1(2) pnt2(2) pnt3(2)]', color);

    pnt1 = arrowbeg + length*direction + 0.4*length*offset;
    pnt2 = arrowbeg;
    pnt3 = arrowbeg + length*direction - 0.4*length*offset;
    h(end+1) = patch([pnt1(1) pnt2(1) pnt3(1)]', [pnt1(2) pnt2(2) pnt3(2)]', color);

  case 'none'
    % don't draw arrow heads
end


filename_bag='experimento_from_min_sgo.bag'
filename_mat='experimento_from_min_sgo.mat'
topic = '/robot/joint_states'
bagMsgs=rosbag(filename_bag);
bagselect = select(bagMsgs,'Topic',topic);

msgStructs = readMessages(bagselect);
pathq=[];
pathqd=[];
for i = 1:size(msgStructs,1)
    msg = msgStructs{i};
    q=msg.Position;
    q_s=q(2:8);
    pathq = [pathq q_s];

    qd=msg.Velocity;
    qd_s=qd(2:8);
    pathqd = [pathqd qd_s];
end
figure, plot(pathq')
save(filename_mat)
'ended'


function [a,varargout] = Insets(location,s,varargin)

%Insets - Create insets in current axes.
%
%  Create insets (= small figures) in current axes. Insets are laid out side
%  by side from left to right. The number of insets is determined by the
%  number of output parameters.
%
%  USAGE
%
%    [a,b,...] = Insets(location,size,<options>)
%
%    location       'topleft', 'top', 'topright', 'left', 'center', 'right',
%                   'bottomleft', 'bottom', or 'bottomright'
%    size           total [h v] size of insets, as proportions of current axes
%                   (after subtracting margins)
%    <options>      optional list of property-value pairs (see table below)
%
%    =========================================================================
%     Properties    Values
%    -------------------------------------------------------------------------
%     'margin'      margin around insets, as a proportion of figure size
%                   (default = 0.1)
%    =========================================================================
%
%  EXAMPLE
%
%    % Create a figure and add the main plot
%    figure;plot(x,y);
%    % Create 3 insets in the top right corner, leaving 10% of the available
%    % width and height for margins (default). In total, the insets will occupy
%    % half of the remaining width, and one quarter of the remaining height.
%    [a,b,c] = Insets('topright',[0.5 0.25]);
%    % Plot data in each of the insets
%    axes(a);plot(x,z1);
%    axes(b);plot(x,z2);
%    axes(c);plot(x,z3);

% Copyright (C) 2009-2011 by Michaël Zugaro
%
% This program is free software; you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation; either version 3 of the License, or
% (at your option) any later version.

% Default values
margin = 0.1;

% Check number of parameters
if nargin < 2,
  error('Incorrect number of parameters (type ''help Insets'' for details).');
end

% Check parameters
if any(s>1|s<0),
  error('Sizes must be expressed as proportion [0..1] of total figure size (type ''help Insets'' for details).');
end
location = lower(location);
if ~isstring_FMAT(location,'topleft','top','topright','left','center','right','bottomleft','bottom','bottomright'),
  error('Incorrect location (type ''help Insets'' for details).');
end

% Parse parameter list
for i = 1:2:length(varargin),
	if ~ischar(varargin{i}),
		error(['Parameter ' num2str(i+2) ' is not a property (type ''help Insets'' for details).']);
	end
	switch(lower(varargin{i})),
		case 'margin',
			margin = lower(varargin{i+1});
			if ~isdscalar(margin,'>=0','<=1'),
				error('Incorrect value for property ''margin'' (type ''help Insets'' for details).');
			end
		otherwise,
			error(['Unknown property ''' num2str(varargin{i}) ''' (type ''help Insets'' for details).']);
	end
end

% Position and size of current axes
p = get(gca,'position');
axesBottom = p(2);
axesTop = p(2)+p(4);
axesLeft = p(1);
axesRight = p(1)+p(3);
axesWidth = p(3);
axesHeight = p(4);

% Inset margins
hMargin = margin*axesWidth;
vMargin = margin*axesHeight;

% Size of each mini figure
n = nargout;
dx = s(1)/n*(axesWidth-2*hMargin);
dy = s(2)*(axesHeight-2*vMargin);

% Start point
switch(lower(location)),
	case 'topleft',
		x0 = axesLeft+hMargin;
		y0 = axesTop-dy-vMargin;
	case 'top',
		x0 = axesLeft+hMargin+dx*n/2;
		y0 = axesTop-dy-vMargin;
	case 'topright',
		x0 = axesRight-hMargin-dx*n;
		y0 = axesTop-dy-vMargin;
	case 'left',
		x0 = axesLeft+hMargin;
		y0 = axesBottom+vMargin+dy/2;
	case 'center',
		x0 = axesLeft+hMargin+dx*n/2;
		y0 = axesBottom+vMargin+dy/2;
	case 'right',
		x0 = axesRight-hMargin-dx*n;
		y0 = axesBottom+vMargin+dy/2;
	case 'bottomleft',
		x0 = axesLeft+hMargin;
		y0 = axesBottom+vMargin;
	case 'bottom',
		x0 = axesLeft+hMargin+dx*n/2;
		y0 = axesBottom+vMargin;
	case 'bottomright',
		x0 = axesRight-hMargin-dx*n;
		y0 = axesBottom+vMargin;
end

% Create all axes
for i = 1:n,
	varargout{i} = axes('position',[x0+(i-1)*dx y0 dx dy]);
end

% Matlab cannot return only varargout, it needs at least one explicit parameter...
a = varargout{1};
varargout = {varargout{2:end}};


function H= gridutil_addScale(mnt, varargin)
%GRIDUTIL_ADDSCALE - Add a Scale for X- and Y-Axis Units to a a Grid Plot
%
%Synopsis:
% H= gridutil_addScale(MNT, <OPT>)
%
%Input:
% MNT: struct for electrode montage, see setElectrodeMontage
% OPT: struct or property/value list of optional properties:
%  .XUnit - Unit of X-Axis, default: 'ms'
%  .YUnit - Unit of Y-Axis, default: '\muV'
%
% Usually called by grid_plot

props = {'XUnit',                   'ms',                   'CHAR';
         'YUnit',                   '\muV',                 'CHAR';
         'YDir',                    'normal',               'CHAR';
         'ScaleHPos',               'zeroleft',             'CHAR';
         'ScaleVPos',               'zeromiddle',           'CHAR';
         'ScaleFontSize',           get(gca, 'FontSize'),   'DOUBLE';
         'ScaleShowOrientation',    1                       'BOOL'};

if nargin==0,
  H= props; return
end
opt= opt_proplistToStruct(varargin{:});
opt= opt_setDefaults(opt, props);
opt_checkProplist(opt, props);

xLim= get(gca, 'xLim');
yLim= get(gca, 'yLim');
xt= get(gca, 'xTick');
xx= median(diff(xt));
yt= get(gca, 'yTick');
yy= median(diff(yt));

x= [mnt.box(1,:) mnt.scale_box(1)];
y= [mnt.box(2,:) mnt.scale_box(2)];
w= [mnt.box_sz(1,:) mnt.scale_box_sz(1)];
h= [mnt.box_sz(2,:) mnt.scale_box_sz(2)];
bs= 0.005;
siz= (1+2*bs)*[max(x+w) - min(x) max(y+h) - min(y)];
pos= [mnt.scale_box(1)-min(x) mnt.scale_box(2)-min(y) ...
      mnt.scale_box_sz(1) mnt.scale_box_sz(2)]./[siz siz];
pos= pos + [bs bs 0 0];
H.ax= axes('position', pos);
ud= struct('type','ERP plus', 'chan','scale');
set(H.ax, 'xLim',xLim, 'yLim',yLim, 'userData',ud);

if strncmp('zero', opt.ScaleHPos, 4),
  if xLim(1)<0-0.1*diff(xLim) & xLim(2)>xx,
    opt.ScaleHPos= 'Zero';
  else
    opt.ScaleHPos= opt.ScaleHPos(5:end);
    if isempty(opt.ScaleHPos), opt.ScaleHPos= 'middle'; end
  end
end
if strncmpi('zero', opt.ScaleVPos, 4),
  if yLim(1)<0-0.1*diff(yLim) & yLim(2)>yy+0.1*diff(yLim),
    opt.ScaleVPos= 'Zero';
  else
    opt.ScaleVPos= opt.ScaleVPos(5:end);
    if isempty(opt.ScaleVPos), opt.ScaleVPos= 'middle'; end
  end
end

if isfield(opt, 'CLim')
   set(H.ax, 'CLim', opt.CLim')
   if isfield(opt, 'colormap')
      colormap(opt.Colormap);
   end
   cb = Colorbar;
   opt.ScaleHPos = 'left';
   ap = get(H.ax, 'position');
   set(cb, 'position', [ap(1)+0.8*ap(3) ap(2)+0.1*ap(4) 0.1*ap(3) 0.8*ap(4)])
  set(get(cb,'yLabel'),'String', opt.zUnit)
end

switch(opt.ScaleHPos),
 case 'Zero',
  x0= 0;
 case 'left',
  x0= xLim(1) + 0.05*diff(xLim);
 case 'middle',
  x0= mean(xLim) - xx/2;
 case 'right',
  x0= xLim(2) - xx - 0.05*diff(xLim);
 otherwise,
  error('unimplemented choice for opt.ScaleHPos');
end
switch(opt.ScaleVPos),
 case 'Zero',
  y0= 0;
% case 'top',
%  y0= yLim(2),
 case 'middle',
  y0= mean(yLim) - yy/2;
% case 'bottom',
%  y0= yLim(1);
 otherwise,
  error('unimplemented choice for opt.ScaleVPos');
end

H.vline= line([x0 x0], [y0 y0+yy]);
set(H.vline, 'LineWidth',2, 'Color','k');
H.text_YUnit= text(x0, y0+yy/2, sprintf(' %g %s', yy, opt.YUnit));
set(H.text_YUnit, 'verticalAli','middle', 'FontSize',opt.ScaleFontSize);
if opt.ScaleShowOrientation,
  if strcmpi(opt.YDir, 'reverse'),
    sgn= '-';
  else
    sgn= '+';
  end
  H.text_sgn= text(x0, y0+yy, [' ' sgn]);
  set(H.text_sgn, 'verticalAli','bottom', 'FontSize',opt.ScaleFontSize);
end

H.hline= line([x0 x0+xx], [y0 y0]);
set(H.hline, 'LineWidth',2, 'Color','k');
yt= y0-0.03*diff(yLim);
H.text_XUnit= text(x0+xx/2, yt, ...
                   sprintf('%g %s', xx, opt.XUnit));
set(H.text_XUnit, 'horizontalAli','center', 'verticalAli','top', ...
                  'FontSize',opt.ScaleFontSize);
if x0==xLim(1), %% leftmost position
  set(H.text_XUnit, 'position',[x0 yt 0], 'horizontalAli','left');
end

axis off;

if nargout==0,
  clear H;
end


function isDataReal = isreal(this)
% Checks whether all data elements in image are real
%
%   Y = MrImage();
%   isDataReal = isreal(Y)
%
%
% This is a method of class MrImage.
%
% IN
%
% OUT
%   isDataReal            true, if all elements in MrImage.data are real
%                         false, otherwise (i.e. complex entries)
%
% EXAMPLE
%   isDataReal = isreal(Y)
%
%   See also MrImage

% Author:   Saskia Klein & Lars Kasper
% Created:  2014-12-01
% Copyright (C) 2014 Institute for Biomedical Engineering
%                    University of Zurich and ETH Zurich
%
% This file is part of the TAPAS UniQC Toolbox, which is released
% under the terms of the GNU General Public Licence (GPL), version 3.
% You can redistribute it and/or modify it under the terms of the GPL
% (either version 3 or, at your option, any later version).
% For further details, see the file COPYING or
%  <http://www.gnu.org/licenses/>.
%

isDataReal = isreal(this.data);


function [hdr,be] = read_hdr_raw(hname)
% Read a NIFTI header
% FORMAT [hdr,be] = read_hdr_raw(hname)
% hname - filename of image's header
% hdr   - a structure containing header info
% be    - whether big-endian or not
%__________________________________________________________________________
% Copyright (C) 2005-2017 Wellcome Trust Centre for Neuroimaging

%
% $Id: read_hdr_raw.m 6995 2017-01-27 15:57:40Z guillaume $

hdr = [];
be  = [];
sts = true;

%-Open header file
%--------------------------------------------------------------------------
fp  = fopen(hname,'r','native');
if fp==-1
    return;
end

%-Detect endianness and file format
%--------------------------------------------------------------------------
[unused,unused,mach] = fopen(fp);
if strncmp(mach,'ieee-be',7)
    be = true;
else
    be = false;
end

sb  = false;
fseek(fp,0,'bof');
d   = fread(fp,1,'*int32');
if isempty(d), fclose(fp); return; end
if d == 348
    fmt = 'nifti1';
elseif swapbytes(d) == 348
    fmt = 'nifti1';
    sb = true;
elseif d == 540
    fmt = 'nifti2';
elseif swapbytes(d) == 540
    fmt = 'nifti2';
    sb = true;
else
    %fclose(fp); return;
    warning('Cannot recognise format. Trying Analyze.');
    fmt = 'analyze';
end

%-Swap bytes if necessary
%--------------------------------------------------------------------------
if sb
    be = ~be;
    if be, mach = 'ieee-be';
    else   mach = 'ieee-le';
    end
    fclose(fp);
    fp = fopen(hname,'r',mach);
    if fp==-1
        return;
    end
end

%-Read magic string
%--------------------------------------------------------------------------
fseek(fp,0,'bof');
switch fmt
    case {'analyze','nifti1'}
        fseek(fp,344,'bof');
        mgc = fread(fp,4,'uint8')';
        if numel(mgc)~=4, fclose(fp); return; end
        switch char(mgc(1:3))
            case {'ni1','n+1'}
                org = niftistruc('nifti1');
            otherwise
                org = mayostruc;
        end
    case 'nifti2'
        fseek(fp,4,'bof');
        mgc = fread(fp,8,'uint8')';
        if numel(mgc)~=8 || ...
           ~ismember(char(mgc(1:3)),{'ni2','n+2'}) || ...
           ~isequal(mgc(5:8),[13 10 26 10]) % uint8(sprintf('\r\n\032\n'))
            fclose(fp);
            return;
        end
        org = niftistruc('nifti2');
end

%-Read header fields
%--------------------------------------------------------------------------
fseek(fp,0,'bof');
for i=1:length(org)
    field = fread(fp,org(i).len,['*' org(i).dtype.prec])';
    if numel(field) ~= org(i).len
        disp([length(field) org(i).len]);
        field = org(i).def;
        sts = false;
    end
    hdr.(org(i).label) = feval(org(i).dtype.conv,field);
end

%-Read header extensions
%--------------------------------------------------------------------------
try
    ext = read_extensions(fp,hdr);
    if ~isempty(ext)
        hdr.ext = ext;
    end
end

%-Close header file
%--------------------------------------------------------------------------
try, fclose(fp); catch, sts = false; end

%-Report problems
%--------------------------------------------------------------------------
if ~sts
    fprintf('There was a problem reading the header of\n');
    fprintf('"%s".\n', hname);
end

%==========================================================================
function ext = read_extensions(fp,hdr)

%-Read first header extension (if any)
%--------------------------------------------------------------------------
ext = []; n = 0;
exts = fread(fp,4,'*uint8');
if numel(exts) ~= 4, exts = uint8([0 0 0 0]); end
if exts(1) ~= 0
    n = n + 1;
    ext(n).esize = fread(fp,1,'*int32');
    ext(n).ecode = fread(fp,1,'*int32');
    ext(n).edata = fread(fp,ext(n).esize-8,'*uint8');
    if numel(ext(n).edata) ~= ext(n).esize-8
        fprintf('Cannot read header extension.');
        ext(n)   = [];
        n        = n - 1;
    else
        while ext(n).edata(end) == 0, ext(n).edata(end) = []; end
    end
end


function outNii = niftiSelect(nii, varargin)
% Preserve data within a spatial cube and zero everything outside it
%
%    boxNii = niftiSelect(nii, varargin)
%
% Typical uses
%    Select a portion of a volume in a white matter mask
%    Extract certain volumes from a 4D data set (e.g., bvals = 2000)
%
% This function writes and saves a new nifti where specified dimensions of
% the data are overwritten with zeros. Useful for unit testing.
%
%  Parameters
%   'nii'          filename or nii struct
%   'saveFlag'   logical
%   'niiNewName' string
%   'keepLR'     vector
%   'keepPA'     vector
%   'keepIS'     vector
%
% Outputs
%   boxNii: the new nifti file (in the form of readFileNifti)
%
% Inputs
%   nii: the nifti file to be copied, and an output of readFileNifti
%   keepLR: the dimensions to keep in the 1st dimension
%   keepPA: the dimensions to keep in the 2nd dimension
%   keepIS: the dimensions to keep in the 3rd dimension
%   niiNewName: Full path to the new nifti file
%   saveFlag:  Save the nii as niiNewName (default: boxNifti)
%
% Examples
%  Extract a small volume from a T1
%    nii = niftiRead(fullfile(vistaRootPath,'rdt','inplane.nii.gz'));
%    niftiView(nii);
%    clear p;  p.keepLR = 16:64; p.keepPA = 96:159; p.keepIS = 5:15;  p.saveFlag = false;
%    boxNii = niftiSelect(nii,p);
%    niftiView(boxNii);
%
%    p.saveFlag = true; p.niiNewName = 'deleteMe';
%    niftiSelect(nii,p);  boxNii = niftiRead(p.niiNewName); niftiView(boxNii);
%
% RL/BW Vistasoft Team, 2016

%% Parameters
p = inputParser;

% Could be a file name or a nifti struct
vFunc = @(x)((ischar(x) && exist(x,'file')) || (isstruct(x) && isfield(x,'nifti_type')));
p.addRequired('nii',vFunc);
if ischar(nii), nii = niftiRead(nii); end

p.addParameter('saveFlag',true,@islogical);
p.addParameter('niiNewName','boxNifti',@ischar);

sz = size(nii.data);
p.addParameter('keepLR',1:sz(1),@isvector);
p.addParameter('keepPA',1:sz(2),@isvector);
p.addParameter('keepIS',1:sz(3),@isvector);

p.parse(nii,varargin{:});
niiNewName = p.Results.niiNewName;
saveFlag   = p.Results.saveFlag;
keepLR = p.Results.keepLR;
keepPA = p.Results.keepPA;
keepIS = p.Results.keepIS;

%% the original nifti
niiDims = nii.dim;

%% the dimension we want to ZERO out
zeroLR = setdiff(1:niiDims(1), keepLR);
zeroPA = setdiff(1:niiDims(2), keepPA);
zeroIS = setdiff(1:niiDims(3), keepIS);

% volume of the original and the cropped
% vol = prod(niiDims);
% volCropped = length(keepLR) * length(keepPA) * length(keepIS);
% fracCrop = volCropped/vol;
% display(['The cropped volume is ' num2str(fracCrop) ' of the original. '])

%% the new data field
newData = nii.data;
newData(zeroLR,:, :) = 0;
newData(:,zeroPA, :) = 0;
newData(:,:, zeroIS) = 0;

%% Make the new nifti struct
outNii = nii;
outNii.fname = niiNewName;
outNii.data  = newData;

if saveFlag, niftiWrite(outNii); end

end


function [band_offsets, quantif] = ReadS2RadiometricOffset(FilesInfo)
% This funciton is to read the Sentinel-2's radiometric offset for the
% version with Basline 4.00
%
% Return Empty means old version data
%
% Created on 16/12/2021

    filepath_meta = dir(fullfile(FilesInfo.InspireXML, 'M*L1C.xml')); % which is same location as inspire xml
    S = xml2struct(fullfile(FilesInfo.InspireXML, filepath_meta.name));
    quantif = str2num(S.n1_colon_Level_dash_1C_User_Product.n1_colon_General_Info.Product_Image_Characteristics.QUANTIFICATION_VALUE.Text);
    try
        Radiometric_Offset_List = S.n1_colon_Level_dash_1C_User_Product.n1_colon_General_Info.Product_Image_Characteristics.Radiometric_Offset_List.RADIO_ADD_OFFSET;

        band_offsets = zeros(length(Radiometric_Offset_List), 2);
        for i = 1: length(Radiometric_Offset_List)
            offset_value = str2num(Radiometric_Offset_List{1, i}.Text);
            band_id = str2num(Radiometric_Offset_List{1, i}.Attributes.band_id);
            band_offsets(i,1) = band_id;
            band_offsets(i,2) = offset_value;
        end
    catch
        band_offsets = [];
    end
end


function status = isequalfigure(a, b)

% ISEQUALFIGURE compares two figures and returns a boolean
%
% Use as
%  isequalfigure(a, b)
% with a and b being the figure handles.

fna = [tempname, '.png'];
fnb = [tempname, '.png'];

print(a, '-dpng', fna);
print(b, '-dpng', fnb);

mata = imread(fna);
matb = imread(fnb);

delete(fna);
delete(fnb);

status = isequal(mata, matb);

% the following is too sensitive
% md5a = ft_hash(fna);
% md5b = ft_hash(fnb);
% status = isequal(md5a, md5b);


function save_image(M, name, options)

% save_image - save an image or a set of images
%
%   save_image(M, name, options);
%
%   M and name can be cell arrays.
%
%   set options.clamp and options.rescale to clamp/rescale the image prior
%   to saving.
%
%   set options.format='bmp', 'jpg' ... to set the file format.
%
%   set options.base_str for the string that is appened to each filename.
%
%   Copyright (c) 2008 Gabriel Peyre

options.null = 0;
if iscell(M)
    if not(iscell(M)) || length(name)~=length(M)
        error('name should be a cell array as M');
    end
    for i=1:length(M)
        save_image(M{i}, name{i}, options);
    end
    return;
end

% trying to guess format
s = strfind(name, '.');
fmt = 'png';
if not(isempty(s))
    fmt = name(s(1)+1:end);
end
fmt = getoptions(options, 'format', fmt);

if iscell(fmt)
    for i=1:length(fmt)
        options.format = fmt{i};
        save_image(M, name, options);
    end
    return;
end

c = getoptions(options, 'clamp', []);
r = getoptions(options, 'rescale', []);
if c==1
    c = [0 1];
end
if r==1
    r = [0 1];
end
if not(isempty(c))
    M = clamp(M, c(1), c(2));
end
if not(isempty(r))
    M = rescale(M, r(1), r(2));
end
base_str = getoptions(options, 'base_str', '');

if strcmp(fmt, 'eps')
    warning('eps is unsuported for now');
    return;
    % detection of image magick
    global image_magick_path;
    if isempty(image_magick_path)
        image_magick_path = '/Applications/ImageMagick-6.4.3/bin/';
    end
    if not(exist([image_magick_path 'convert']))
        warning('Image magick not installed, you need to specify global image_magick_path');
    end

    warning off;
    imwrite(M, [base_str name '.png'],'png');
    warning on;
    system([ image_magick_path 'convert ./' base_str name '.png ./' base_str name '.eps' ]);
    delete( [base_str name '.png'] );
    return;
end

warning off;
imwrite(M, [base_str name '.' fmt], fmt);
warning on;


function s = get_limited_categories(categorical_obj, lenlimit)
    % get_limited_categories gets the categories for a categorical, but designed for display
    % which means you limit the character length.
    %
    % s = get_limited_categories(categorical_obj) will get a string contianning the first few and
    % last few categories with ellipses in between.
    % default length is 80

    c = categories(categorical_obj);
    if nargin == 1
        lenlimit = 80;
    end

    s = strjoin(c,', ');
    if numel(s) > lenlimit
        commas = find(s==',');
        break1=max(commas(commas<25));
        break2=min(commas(commas>(length(s)-25)));
        s=[s(1:break1),'...',s(break2:end)];
    end
end


function [tt1]=conj(tt)
%Compute a complex conjugate of TT-tensor
%   [TT1]=CONJ(TT)
%
%
% TT-Toolbox 2.2, 2009-2012
%
%This is TT Toolbox, written by Ivan Oseledets et al.
%Institute of Numerical Mathematics, Moscow, Russia
%webpage: http://spring.inm.ras.ru/osel
%
%For all questions, bugs and suggestions please mail
%ivan.oseledets@gmail.com
%---------------------------

tt1=tt;
tt1.core=conj(tt.core);
return
end


function [targets,expdirs_chosen,framestarts,nframesperseg] = ...
  ChooseExampleIntervals(expdirs,classifierparamsfile,varargin)

if ~iscell(expdirs)
  expdirs = {expdirs};
end
nexps = numel(expdirs);

score_norm_prctile = 80;

% hysteresis for counting bouts
min_score_close = -.1;
max_dist_close = 5;
max_dist_open = 5;
max_score_open = .25;
nframesperseg = 500;
behaviorsuse = [];

% for weighing number of bouts of different behaviors
weightlogoff = .1;

% for weighting the same fly, same sex
downweight_samefly = 5;
downweight_samesex = 2;

% scheme for weighting
weightingscheme = 'sumlogsumscore';

score_thresh = .25;
[nintervals,score_norm_prctile,...
  min_score_close,max_dist_close,...
  max_dist_open,max_score_open,...
  nframesperseg,...
  downweight_samefly,...
  downweight_samesex,...
  weightlogoff,...
  behaviorsuse,...
  weightingscheme] = ...
  myparse(varargin,'nintervals',1,...
  'score_norm_prctile',score_norm_prctile,...
  'min_score_close',min_score_close,...
  'max_dist_close',max_dist_close,...
  'max_dist_open',max_dist_open,...
  'max_score_open',max_score_open,...
  'nframesperseg',nframesperseg,...
  'downweight_samefly',downweight_samefly,...
  'downweight_samesex',downweight_samesex,...
  'weightlogoff',weightlogoff,...
  'behaviorsuse',behaviorsuse,...
  'weightingscheme',weightingscheme);

classifierparams = ReadClassifierParamsFile(classifierparamsfile);
nbehaviors = numel(classifierparams);
behaviors = cell(1,nbehaviors);
scorefns = cell(1,nbehaviors);
for i = 1:nbehaviors,
  if iscell(classifierparams(i).behaviors.names),
    behaviors{i} = sprintf('%s_',classifierparams(i).behaviors.names{:});
    behaviors{i} = behaviors{i}(1:end-1);
  else
    behaviors{i} = classifierparams(i).behaviors.names;
  end
  behaviors{i} = regexprep(behaviors{i},'(_|^)([a-z])','${upper($2)}');
  scorefn = classifierparams(i).file.scorefilename;
  scorefns{i} = regexprep(scorefn,'\.mat$','');
end

%% create trx structure

%global STORED_TRX;
%if isempty(STORED_TRX),

trx = Trx('trxfilestr',classifierparams(1).file.trxfilename,...
  'moviefilestr',classifierparams(1).file.moviefilename,...
  'perframedir',classifierparams(1).file.perframedir);
if isfield(classifierparams,'perframe') && isfield(classifierparams(1).perframe,'params'),
  trx.SetPerFrameParams(classifierparams(1).perframe.params);
end
if isfield(classifierparams,'perframe') && isfield(classifierparams(1).perframe,'landmark_params'),
  trx.SetLandmarkParams(classifierparams(1).perframe.landmark_params);
end

for i = 1:nexps,
  trx.AddExpDir(expdirs{i},'openmovie',false);
end

% STORED_TRX = trx;
% else
%   trx = STORED_TRX;
% end

%% process scores

scores = cell(1,trx.nflies);
for fly = 1:trx.nflies,
  scores{fly} = nan(nbehaviors,trx(fly).nframes);
  for behi = 1:nbehaviors,
    scores{fly}(behi,:) = trx(fly).(scorefns{behi});
  end
end

% normalize
score_norms = nan(nbehaviors,1);
for behi = 1:nbehaviors,
  tmp = cell2mat(cellfun(@(x) x(behi,:),scores,'UniformOutput',false));
  score_norms(behi) = prctile(abs(tmp),score_norm_prctile);
end
for fly = 1:trx.nflies,
  for behi = 1:nbehaviors,
    scores{fly}(behi,:) = max(-1,min(1,scores{fly}(behi,:)/score_norms(behi,:)));
  end
end

labels = cell(1,trx.nflies);
for fly = 1:trx.nflies,

  labels{fly} = false(nbehaviors,trx(fly).nframes);
  for behi = 1:nbehaviors,
    label0 = scores{fly}(behi,:) > score_thresh;

    % close small holes where score >= min_score_close
    label_lenient = scores{fly}(behi,:) >= min_score_close;
    [t0s,t1s] = get_interval_ends(label0); t1s = t1s-1;
    gapdist = t0s(2:end)-t1s(1:end-1)-1;
    issmallgap = gapdist <= max_dist_close;
    label1 = label0;
    for i = find(issmallgap),
      doclose = all(label_lenient(t1s(i)+1:t0s(i+1)-1));
      if doclose,
        label1(t1s(i)+1:t0s(i+1)-1) = true;
      end
    end

    % remove short bouts where max score <= max_score_open
    [t0s,t1s] = get_interval_ends(label1); t1s = t1s-1;
    label_strict = scores{fly}(behi,:) > max_score_open;
    gapdist = t1s-t0s+1;
    issmallgap = gapdist <= max_dist_open;
    label2 = label1;
    for i = find(issmallgap),
      doopen = ~any(label_strict(t0s(i):t1s(i)));
      if doopen,
        label2(t0s(i):t1s(i)) = false;
      end
    end
    labels{fly}(behi,:) = label2;

  end
end

%%

% count the number of bout starts and ends in each interval
fil = ones(1,nframesperseg);
nbouts = cell(1,trx.nflies);
w0 = cell(1,trx.nflies);
for fly = 1:trx.nflies,

  isstart = double(~labels{fly}(:,1:end-1) & labels{fly}(:,2:end));
  isend = double(labels{fly}(:,1:end-1) & ~labels{fly}(:,2:end));
  nbouts{fly} = .5*imfilter(isstart,fil,0,'same') + ...
    .5*imfilter(isend,fil,0,'same');

  switch weightingscheme,
    case 'sumlogsumscore',
      logsumscores = nan(size(labels{fly}));
      minv = log(weightlogoff);
      logsumscores(:) = minv;
      for behi = 1:nbehaviors,
        [t0s,t1s] = get_interval_ends(labels{fly}(behi,:)); t1s=t1s-1;
        % csscores(i) is the sum of scores from 1:i-1
        csscores = [0,cumsum(scores{fly}(behi,:))];
        sumscores = csscores(t1s+1)-csscores(t0s);
        lsscurr = log(max(0,sumscores)+weightlogoff)./(t1s-t0s+1);
        for i = 1:numel(t0s),
          logsumscores(behi,t0s(i):t1s(i)) = lsscurr(i);
        end
      end
      logsumscores = logsumscores-minv;
      sumlogsumscore = imfilter(logsumscores,fil,0,'same');
      w0{fly} = sumlogsumscore;
    case 'lognbouts',
      w0{fly} = log(nbouts{fly}+weightlogoff);
    case 'isbout',
      w0{fly} = double(nbouts{fly} >= 1);
    case 'none',
      w0{fly} = ones(size(nbouts{fly}));
    case 'random',
      w0{fly} = rand(size(nbouts{fly}));
  end
end

% make sure weights are non-negative
if ~strcmpi(weightingscheme,'none'),
  tmp = min(min(cell2mat(w0),[],1),[],2);
  w0 = cellfun(@(x) x-tmp,w0,'UniformOutput',false);
end
%
% % normalize per-behavior
% Z = zeros(nbehaviors,1);
% for behi = 1:nbehaviors,
%   Z(behi) = sum(cell2mat(cellfun(@(x) x(behi,:),w0,'UniformOutput',false)));
% end
% Z = Z / max(Z);
% w0 = cellfun(@(x) bsxfun(@rdivide,x,Z),w0,'UniformOutput',false);

% make sure sex classification is uniform for each bout
ismale = cellfun(@(x) strcmpi(x,'M'),trx.sex,'UniformOutput',false);
isallowed = cell(1,trx.nflies);
for fly = 1:trx.nflies,
  nmale = imfilter(double(ismale{fly}),fil,nan,'same');
  isallowed{fly} = nmale == 0 | nmale == nframesperseg;
end

w0mat = cell2mat(w0);
w0mat(:,~cell2mat(isallowed)) = nan;
nboutsmat = cell2mat(nbouts);
ismalemat = cell2mat(ismale);
flyidx = zeros(1,numel(ismalemat));
flyoffs = cumsum([0,trx.nfliespermovie(1:end-1)]);
flystarts = cumsum([1,trx(1:trx.nflies-1).nframes]);
flyidx(flystarts) = 1;
flyidx = cumsum(flyidx);

framestarts = nan(1,nintervals);
targets = nan(1,nintervals);
expis = nan(1,nintervals);

% only care about some behaviors
if ~isempty(behaviorsuse)
  behaviorsuseidx = ismember(behaviors,behaviorsuse);
else
  behaviorsuseidx = true(1,nbehaviors);
end
for segi = 1:nintervals,

  % choose the highest weight interval
  [~,i] = max(sum(w0mat(behaviorsuseidx,:),1));
  ismalecurr = ismalemat(i);
  fly = flyidx(i);
  i0 = i-flystarts(fly)+1-floor(nframesperseg/2);
  t0 = i0-trx(fly).off;
  framestarts(segi) = t0;
  expi = trx.fly2exp(fly);
  flyi = fly-flyoffs(expi);
  targets(segi) = flyi;
  expis(segi) = expi;

  % downweight this fly
  idx = flyidx == fly;
  w0mat(:,idx) = w0mat(:,idx) / downweight_samefly;

  % downweight this fly's sex
  idx = ismalemat == ismalecurr;
  w0mat(:,idx) = w0mat(:,idx) / downweight_samesex;

  % downweight behaviors selected
  w0mat = bsxfun(@rdivide,w0mat,(nboutsmat(:,i)+1));

  % don't allow overlapping bouts
  i00 = max(i0-nframesperseg,1)+flystarts(fly)-1;
  i10 = min(i0+2*nframesperseg,trx(fly).nframes)+flystarts(fly)-1;
  w0mat(:,i00:i10) = nan;

end

expdirs_chosen = expdirs(expis);


if 0
    %% Set RTLD_NOW and RTLD_DEEPBIND
    %https://nl.mathworks.com/matlabcentral/answers/327193-calling-python-module-from-matlab-causes-segmentation-fault-in-h5py
    py.sys.setdlopenflags(int32(10));
else
    %PythonEnvironment objects contain information about the settings and status of the Python® interpreter.
    %MATLAB® communicates with the interpreter when you call a py. command.
    %Python environment information is persistent across different MATLAB sessions.

    % Out-of-Process Execution of Python Functionality
    % MATLAB® can run Python scripts and functions in a separate process. Running Python in a separate process enables you to:
    % Use some third-party libraries in the Python code that are not compatible with MATLAB.
    % Isolate the MATLAB process from crashes in the Python code.
    PythonEnvironment = pyenv("ExecutionMode","OutOfProcess");
end

try
  py.list('x','y',1)
catch e
  e.message
  if(isa(e,'matlab.exception.PyException'))
    e.ExceptionObject
  end
end

try
  py.help('pandas.read_csv')
catch e
  e.message
  if(isa(e,'matlab.exception.PyException'))
    e.ExceptionObject
  end
end

%pandas.read_csv function signature
%pandas.read_csv = read_csv(filepath_or_buffer: Union[str, pathlib.Path, IO[~AnyStr]], sep=',', delimiter=None, header='infer', names=None, index_col=None, usecols=None, squeeze=False, prefix=None, mangle_dupe_cols=True, dtype=None, engine=None, converters=None, true_values=None, false_values=None, skipinitialspace=False, skiprows=None, skipfooter=0, nrows=None, na_values=None, keep_default_na=True, na_filter=True, verbose=False, skip_blank_lines=True, parse_dates=False, infer_datetime_format=False, keep_date_col=False, date_parser=None, dayfirst=False, cache_dates=True, iterator=False, chunksize=None, compression='infer', thousands=None, decimal: str = '.', lineterminator=None, quotechar='"', quoting=0, doublequote=True, escapechar=None, comment=None, encoding=None, dialect=None, error_bad_lines=True, warn_bad_lines=True, delim_whitespace=False, low_memory=True, memory_map=False, float_precision=None)

%pyargs https://nl.mathworks.com/help/matlab/ref/pyargs.html
% Python command:
% db = pd.read_csv('./data/cache_compounds_20160818.csv',index_col='compound_id')
% MATLAB command using pyargs
db = py.pandas.read_csv(pyargs('filepath_or_buffer',[pyLibraryFolder filesep 'data' filesep 'test_compounds.csv'],'index_col','compound_id'));

order = py.dict(pyargs('soup',3.57,'bread',2.29,'bacon',3.91,'salad',5.00))
% A dictionary has pairs of keys and values. Display the menu items in the variable order using the Python keys function.
keys(order)
values(order)

if count(py.sys.path,'') == 0
    insert(py.sys.path,int32(0),'');
end

mymod = py.importlib.import_module('mymod')

N = py.list({'Jones','Johnson','James'})

names = py.mymod.search(N)


function zx = cvx_expand_dim( z, dim, nx )

%CVX_EXPAND_DIM   Expands an N-D array along a specified dimension.
%   CVX_EXPAND_DIM( X, DIM, NX ) stacks NX copies of the matrix X along the
%   dimension NX. It is equivalent to CAT( DIM, X, X, ..., X ), where X is
%   repeated NX times.
%
%   This is an internal CVX function, and as such no checking is performed to
%   insure that the arguments are valid.

zdims = cell( 1, max( ndims(z), dim ) );
[ zdims{:} ] = deal( ':' );
zdims{dim} = ones( 1, nx );
zx = z( zdims{:} );

% 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 this = parse_input_parameter(this, param)
%PARSE_INPUT_PARAMETER parses and set parameters in the struct PARAM
%
%   This is a member function of the class 'alt_ba_optical_flow'.
%
% Authors: Deqing Sun, Department of Computer Science, Brown University
% Contact: dqsun@cs.brown.edu
% $Date: 2009 $
% $Revision: $
%
% Copyright 2009-2010, Brown University, Providence, RI. USA
%
%                          All Rights Reserved
%
% All commercial use of this software, whether direct or indirect, is
% strictly prohibited including, without limitation, incorporation into in
% a commercial product, use in a commercial service, or production of other
% artifacts for commercial purposes.
%
% Permission to use, copy, modify, and distribute this software and its
% documentation for research purposes is hereby granted without fee,
% provided that the above copyright notice appears in all copies and that
% both that copyright notice and this permission notice appear in
% supporting documentation, and that the name of the author and Brown
% University not be used in advertising or publicity pertaining to
% distribution of the software without specific, written prior permission.
%
% For commercial uses contact the Technology Venture Office of Brown University
%
% THE AUTHOR AND BROWN UNIVERSITY DISCLAIM ALL WARRANTIES WITH REGARD TO
% THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
% FITNESS FOR ANY PARTICULAR PURPOSE.  IN NO EVENT SHALL THE AUTHOR OR
% BROWN UNIVERSITY BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
% DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
% PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
% ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
% THIS SOFTWARE.

if length(param) ==1
    param = param{1};
end;

if mod(length(param), 2) ~=0
    error('Parse_input_parameter: Input parameters must be given in pairs (name and value)!');
end;

i = 1;

while (i <= length(param))

    if ischar(param{i+1})
        param{i+1} = str2num(param{i+1});
    end;

    switch lower(param{i})

        case 'lambda'
            this.lambda         = param{i+1};
            this.lambda_q       = param{i+1};

        case 'pyramid_levels'
            this.pyramid_levels = param{i+1};
            this.auto_level     = false;

        case 'pyramid_spacing'
            this.pyramid_spacing        = param{i+1};

        case 'gnc_pyramid_levels'
            this.gnc_pyramid_levels     = param{i+1};

        case 'gnc_pyramid_spacing'
            this.gnc_pyramid_spacing    = param{i+1};

        case 'sigma_d'
            for j = 1:length(this.spatial_filters);
                this.rho_spatial_u{j}.param   = param{i+1};
                this.rho_spatial_v{j}.param   = param{i+1};
            end;

        case 'sigma_s'
            this.rho_data.param         = param{i+1};

    end;

    i = i+2;
end;


function nav_o = gnss_outage(nav, times_out, ACTION)
% gnss_outage: removes or gets navigation data from navigation data structure
% in GNSS outages periods.
%
% INPUT
%   nav, navigation data structure.
%   times_out, Nx1 vector with outage times where times_out(1) is the start
%     time of 1st GNSS OUTAGE, times_out(2) is the end of 1st GNSS OUTAGE,
%     times_out(3) is the start of the 2nd GNSS OUTAGE, and so on. N should
%     be even.
%   ACTION, 'REMOVE' (default) or 'GET' (string).
%
% OUTPUT
%   nav, same navigation data structure.
%
%   Copyright (C) 2014, Rodrigo Gonzalez, all rights reserved.
%
%   This file is part of NaveGo, an open-source MATLAB toolbox for
%   simulation of integrated navigation systems.
%
%   NaveGo is free software: you can redistribute it and/or modify
%   it under the terms of the GNU Lesser General Public License (LGPL)
%   version 3 as published by the Free Software Foundation.
%
%   This program is distributed in the hope that it will be useful,
%   but WITHOUT ANY WARRANTY; without even the implied warranty of
%   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%   GNU Lesser General Public License for more details.
%
%   You should have received a copy of the GNU Lesser General Public
%   License along with this program. If not, see
%   <http://www.gnu.org/licenses/>.
%
% Version: 001
% Date:    2021/12/14
% Author:  Rodrigo Gonzalez <rodralez@frm.utn.edu.ar>
% URL:     https://github.com/rodralez/navego

if (nargin < 3)

    ACTION = 'REMOVE';
end

OUTAGE_N = max(size(times_out)) / 2;

%% INIT DATA

if (strcmp(ACTION,'REMOVE'))

    fprintf('gnss_outage: %d GNSS outages are forced... \n', OUTAGE_N)

    nav_o = nav;

elseif(strcmp(ACTION,'GET'))

    nav_o.t = [];
    nav_o.lat = [];
    nav_o.lon = [];
    nav_o.h = [];
    nav_o.vel = [];

    if (isfield(nav,'roll'))
        nav_o.roll = [];
    end

    if (isfield(nav,'pitch'))
        nav_o.pitch = [];
    end

    if (isfield(nav,'yaw'))
        nav_o.yaw = [];
    end
else

    error('gnss_outage: action is not defined')
end

%% REMOVE OR GET

for k = 1:OUTAGE_N

    if (strcmp(ACTION,'REMOVE'))

        idx  = find(nav_o.t >= times_out(2*k-1), 1, 'first' );
        fdx  = find(nav_o.t <= times_out(2*k), 1, 'last' );

        if(isempty(idx) || isempty(fdx))
            error('gnss_outage: outage empty index')
        end

        if(idx >= fdx)
            error('gnss_outage: indexes inconsistency')
        end

        nav_o.t  (idx:fdx)    = [];
        nav_o.lat(idx:fdx)    = [];
        nav_o.lon(idx:fdx)    = [];
        nav_o.h  (idx:fdx)    = [];
        nav_o.vel(idx:fdx, :) = [];

        if (isfield(nav,'roll'))
            nav_o.roll(idx:fdx)  = [];
        end

        if (isfield(nav,'pitch'))
            nav_o.pitch(idx:fdx) = [];
        end

        if (isfield(nav,'yaw'))
            nav_o.yaw(idx:fdx)   = [];
        end

    elseif(strcmp(ACTION,'GET'))

        idx  = find(nav.t >= times_out(2*k-1), 1, 'first' );
        fdx  = find(nav.t <= times_out(2*k), 1, 'last' );

        if(isempty(idx) || isempty(fdx))
            error('gnss_outage: outage empty index')
        end

        if(idx >= fdx)
            error('gnss_outage: indexes inconsistency')
        end

        nav_o.t     = [nav_o.t   ; nav.t(idx:fdx)] ;
        nav_o.lat   = [nav_o.lat ; nav.lat(idx:fdx)];
        nav_o.lon   = [nav_o.lon ; nav.lon(idx:fdx)];
        nav_o.h     = [nav_o.h   ; nav.h(idx:fdx) ];
        nav_o.vel   = [nav_o.vel ; nav.vel(idx:fdx, :)];

        if (isfield(nav,'roll'))
            nav_o.roll = [nav_o.roll ; nav.roll(idx:fdx)];
        end

        if (isfield(nav,'pitch'))
            nav_o.pitch = [nav_o.pitch ; nav.pitch(idx:fdx)];
        end

        if (isfield(nav,'yaw'))
            nav_o.yaw = [nav_o.yaw ; nav.yaw(idx:fdx)];
        end

    else

        error('gnss_outage: action is not defined')
    end

end

end


classdef ALGORITHM < handle & matlab.mixin.Heterogeneous
%ALGORITHM - The superclass of algorithms.
%
%   This is the superclass of algorithms. An object of ALGORITHM stores the
%   settings of the algorithm and the data generated in current execution.
%
% ALGORITHM properties:
%   parameter       <any>       parameters of the algorithm
%   save            <scalar>    number of populations saved in an execution
%   outputFcn       <function>	function called after each generation
%   pro             <class>     problem solved in current execution
%   result          <cell>      populations saved in current execution
%   metric          <struct>    metric values of current populations
%
% ALGORITHM methods:
%   ALGORITHM       <protected> the constructor setting all the properties specified by user
%   Solve           <public>    use the algorithm to solve a problem
%   main            <public>	the main function of the algorithm
%   NotTerminated 	<protected>	the function called after each generation of the execution
%   ParameterSet	<protected>	obtain the parameters of the algorithm

%------------------------------- Copyright --------------------------------
% Copyright (c) 2023 BIMK Group. You are free to use the PlatEMO for
% research purposes. All publications which use this platform or any code
% in the platform should acknowledge the use of "PlatEMO" and reference "Ye
% Tian, Ran Cheng, Xingyi Zhang, and Yaochu Jin, PlatEMO: A MATLAB platform
% for evolutionary multi-objective optimization [educational forum], IEEE
% Computational Intelligence Magazine, 2017, 12(4): 73-87".
%--------------------------------------------------------------------------

    properties(SetAccess = protected)
        parameter = {};                 % Parameters of the algorithm
        save      = -10;            	% Number of populations saved in an execution
        outputFcn = @DefaultOutput;     % Function called after each generation
        pro;                            % Problem solved in current execution
        result;                         % Populations saved in current execution
        metric;                         % Metric values of current populations
    end
    methods(Access = protected)
        function obj = ALGORITHM(varargin)
        %ALGORITHM - The constructor of ALGORITHM.
        %
        %   Alg = algName('Name',Value,'Name',Value,...) generates an
        %   object with the properties specified by the inputs. algName is
        %   a subclass of ALGORITHM, while ALGORITHM cannot be instantiated
        %   directly.
        %
        %   Example:
        %        Algorithm = MOEAD('parameter',4,'save',1)

            isStr = find(cellfun(@ischar,varargin(1:end-1))&~cellfun(@isempty,varargin(2:end)));
            for i = isStr(ismember(varargin(isStr),{'parameter','save','outputFcn'}))
                obj.(varargin{i}) = varargin{i+1};
            end
        end
    end
    methods(Sealed)
        function Solve(obj,Problem)
        %Solve - Use the algorithm to solve a problem.
        %
        %   obj.Solve(Pro) uses the algorithm to solve a problem, where Pro
        %   is a PROBLEM object.
        %
        %   In terms of the default obj.outputFcn, the result will be
        %   displayed when obj.save = 0 and saved when obj.save > 0.
        %
        %   Example:
        %       Algorithm.Solve(Problem)

            try
                obj.result = {};
                obj.metric = struct('runtime',0);
                obj.pro    = Problem;
                obj.pro.FE = 0;
                addpath(fileparts(which(class(obj))));
                addpath(fileparts(which(class(obj.pro))));
                tic; obj.main(obj.pro);
            catch err
                if ~strcmp(err.identifier,'PlatEMO:Termination')
                    rethrow(err);
                end
            end
        end
    end
    methods
        function main(obj,Problem)
        %main - The main function of the algorithm.
        %
        %   This function is expected to be implemented in each subclass of
        %   ALGORITHM, which is usually called by ALGORITHM.Solve.
        end
    end
    methods(Access = protected, Sealed)
        function nofinish = NotTerminated(obj,Population)
        %NotTerminated - The function called after each generation of the
        %execution.
        %
        %   obj.NotTerminated(P) stores the population P as the result of
        %   the current execution, and returns true if the algorithm should
        %   be terminated, i.e., the number of function evaluations or
        %   runtime exceeds.
        %
        %   obj.outputFcn is called here, whose runtime will not be counted
        %   in the runtime of current execution.
        %
        %   Example:
        %       while Algorithm.NotTerminated(Population)
        %           ... ...
        %       end

            obj.metric.runtime = obj.metric.runtime + toc;
            if obj.pro.maxRuntime < inf
                obj.pro.maxFE = obj.pro.FE*obj.pro.maxRuntime/obj.metric.runtime;
            end
            num   = max(1,abs(obj.save));
            index = max(1,min(min(num,size(obj.result,1)+1),ceil(num*obj.pro.FE/obj.pro.maxFE)));
            obj.result(index,:) = {obj.pro.FE,Population};
            drawnow('limitrate');
            obj.outputFcn(obj,obj.pro);
            nofinish = obj.pro.FE < obj.pro.maxFE;
            assert(nofinish,'PlatEMO:Termination',''); tic;
        end
        function varargout = ParameterSet(obj,varargin)
        %ParameterSet - Obtain the parameters of the algorithm.
        %
        %   [p1,p2,...] = obj.ParameterSet(v1,v2,...) sets the values of
        %   parameters p1, p2, ..., where each parameter is set to the
        %   value given in obj.parameter if obj.parameter is specified, and
        %   set to the value given in v1, v2, ... otherwise.
        %
        %   Example:
        %       [p1,p2,p3] = Algorithm.ParameterSet(1,2,3)

            varargout = varargin;
            specified = ~cellfun(@isempty,obj.parameter);
            varargout(specified) = obj.parameter(specified);
        end
    end
    methods(Sealed)
        function Scores = CalMetric(obj,metName)
        % Calculate metric values

            if ~isfield(obj.metric,metName)
                obj.metric.(metName) = [cell2mat(obj.result(:,1)),cellfun(@(S)obj.pro.CalMetric(metName,S),obj.result(:,2))];
            end
            Scores = obj.metric.(metName);
        end
    end
end

function DefaultOutput(Algorithm,Problem)
% The default output function of ALGORITHM

    clc; fprintf('%s on %d-objective %d-variable %s (%6.2f%%), %.2fs passed...\n',class(Algorithm),Problem.M,Problem.D,class(Problem),Problem.FE/Problem.maxFE*100,Algorithm.metric.runtime);
    if Problem.FE >= Problem.maxFE
        if Algorithm.save < 0
            if Problem.M > 1
                Population = Algorithm.result{end};
                if length(Population) >= size(Problem.optimum,1); name = 'HV'; else; name = 'IGD'; end
                value = Algorithm.CalMetric(name);
                figure('NumberTitle','off','Name',sprintf('%s : %.4e  Runtime : %.2fs',name,value(end),Algorithm.CalMetric('runtime')));
                title(sprintf('%s on %s',class(Algorithm),class(Problem)),'Interpreter','none');
                top = uimenu(gcf,'Label','Data source');
                g   = uimenu(top,'Label','Population (obj.)','CallBack',{@(h,~,Pro,P)eval('Draw(gca);Pro.DrawObj(P);cb_menu(h);'),Problem,Population});
                uimenu(top,'Label','Population (dec.)','CallBack',{@(h,~,Pro,P)eval('Draw(gca);Pro.DrawDec(P);cb_menu(h);'),Problem,Population});
                uimenu(top,'Label','True Pareto front','CallBack',{@(h,~,P)eval('Draw(gca);Draw(P,{''\it f\rm_1'',''\it f\rm_2'',''\it f\rm_3''});cb_menu(h);'),Problem.optimum});
                cellfun(@(s)uimenu(top,'Label',s,'CallBack',{@(h,~,A)eval('Draw(gca);Draw(A.CalMetric(h.Label),''-k.'',''LineWidth'',1.5,''MarkerSize'',10,{''Number of function evaluations'',strrep(h.Label,''_'','' ''),[]});cb_menu(h);'),Algorithm}),{'IGD','HV','GD','Feasible_rate'});
                set(top.Children(4),'Separator','on');
                g.Callback{1}(g,[],Problem,Population);
            else
                best = Algorithm.CalMetric('Min_value');
                if isempty(best); best = nan; end
                figure('NumberTitle','off','Name',sprintf('Min value : %.4e  Runtime : %.2fs',best(end),Algorithm.CalMetric('runtime')));
                title(sprintf('%s on %s',class(Algorithm),class(Problem)),'Interpreter','none');
                top = uimenu(gcf,'Label','Data source');
                uimenu(top,'Label','Population (dec.)','CallBack',{@(h,~,Pro,P)eval('Draw(gca);Pro.DrawDec(P);cb_menu(h);'),Problem,Algorithm.result{end}});
                cellfun(@(s)uimenu(top,'Label',s,'CallBack',{@(h,~,A)eval('Draw(gca);Draw(A.CalMetric(h.Label),''-k.'',''LineWidth'',1.5,''MarkerSize'',10,{''Number of function evaluations'',strrep(h.Label,''_'','' ''),[]});cb_menu(h);'),Algorithm}),{'Min_value','Feasible_rate'});
                set(top.Children(2),'Separator','on');
                top.Children(2).Callback{1}(top.Children(2),[],Algorithm);
            end
        elseif Algorithm.save > 0
            folder = fullfile('Data',class(Algorithm));
            [~,~]  = mkdir(folder);
            file   = fullfile(folder,sprintf('%s_%s_M%d_D%d_',class(Algorithm),class(Problem),Problem.M,Problem.D));
            runNo  = 1;
            while exist([file,num2str(runNo),'.mat'],'file') == 2
                runNo = runNo + 1;
            end
            result = Algorithm.result;
            metric = Algorithm.metric;
            save([file,num2str(runNo),'.mat'],'result','metric');
        end
    end
end

function cb_menu(h)
% Switch between the selected menu

    set(get(get(h,'Parent'),'Children'),'Checked','off');
    set(h,'Checked','on');
end


function [FigData, iDS, iFig] = gui_figure_data( hFig, varargin )
% GUI_FIGURE_DATA: Retrieve the time series displayed in figure hFig.
%
% USAGE:  FigData = gui_figure_data( hFig )
%         FigData = gui_figure_data( ..., 'SelectedChannels' )
%         FigData = gui_figure_data( ..., 'SelectedTime' )
%
% INPUT:
%    - hFig : handle to a brainstorm figure
%    - 'SelectedChannels' : if some channels are highlighted in the figure, return only them
%    - 'SelectedTime'     : if a time segment is highlighted in the figure, return only this segment
% OUTPUT:
%    - FigData : structure

% @=============================================================================
% 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, 2008-2018

% === INITIALIZATIONS ===
global GlobalData;
% Parse inputs
isSelectedChannels = any(strcmpi(varargin, 'SelectedChannels'));
isSelectedTime     = any(strcmpi(varargin, 'SelectedTime'));
% Initialize save TimeSeries matrix
FigData = struct('F',          [], ...
                 'Time',       [], ...
                 'FigTitle',   get(hFig, 'Name'), ...
                 'FigType',    [], ...
                 'AxesTitle',  [], ...
                 'AxesLegend', [], ...
                 'Events',     repmat(db_template('Event'),0));
FigData.F = {};
FigData.AxesTitle = {};
FigData.AxesLegend = {};

% === GET FIGURE INFORMATION ===
% Get figure description
[hFig, iFig, iDS] = bst_figures('GetFigure', hFig);
Handles = GlobalData.DataSet(iDS).Figure(iFig).Handles;
% Check downsampling factor
if (Handles.DownsampleFactor > 1)
    error(['The signals were downsampled before being displayed and cannot be exported from the figure.' 10 ...
        'For continuous files: import the recordings first.' 10 ...
        'For imported time series: read directly the corresponding .mat files.' 10 ...
        'For scouts: use the proces "Extract > Scout time series".' ]);
end
% Get y-factor and offsets
Factor = Handles(1).DisplayFactor;
if isfield(Handles(1), 'ChannelOffsets')
    ChannelOffsets = Handles(1).ChannelOffsets;
else
    ChannelOffsets = [];
end
FigData.FigType = GlobalData.DataSet(iDS).Figure(iFig).Id.Type;

% === FIND CHANNELS INDICES ===
% Only valid for data time series (real channels)
if strcmpi(GlobalData.DataSet(iDS).Figure(iFig).Id.Type, 'DataTimeSeries')
    iChannel = GlobalData.DataSet(iDS).Figure(iFig).SelectedChannels;
    Channel = GlobalData.DataSet(iDS).Channel(iChannel);
else
    Channel = [];
end

% Find selected indices
if isSelectedTime
    timeSelection = getappdata(hFig, 'GraphSelection');
    if ~isempty(timeSelection) && ~isinf(timeSelection(2))
        selTime = [min(timeSelection), max(timeSelection)];
        % Get time vector
        if ~isempty(GlobalData.DataSet(iDS).Measures.Time)
            TimeVector = bst_memory('GetTimeVector', iDS, [], 'UserTimeWindow');
        % If no time window defined: use the X values from the lines in axes
        elseif ~isempty(Handles(1).hLines)
            iValidLine = find(Handles(1).hLines > 0, 1);
            hLine = Handles(1).hLines(iValidLine);
            TimeVector = get(hLine, 'XData');
        else
            error('You must define a channel file to use this feature.');
        end
        % Get time indices to save
        iTimeBounds = bst_closest(selTime, TimeVector);
        iTime = iTimeBounds(1):iTimeBounds(2);
    else
        iTime = []; % Select all time indices
    end
else
    iTime = [];
end

% Process axes : one cell by axes displayed
for iAxes = 1:length(Handles)
    % === DATA ===
    % Get lines handles
    iDispChannels = find(ishandle(Handles(iAxes).hLines));
    hLines = Handles(iAxes).hLines(iDispChannels);
    % If no data in these axes => next ones
    if isempty(hLines)
        continue
    end

    % Keep only selected channels
    iSel = [];
    if isSelectedChannels
        % Get display width of all lines
        linesWidth = get(hLines, 'LineWidth');
        if iscell(linesWidth)
            linesWidth = [linesWidth{:}];
        end
        % Selected ones have a width > 1
        iSel = find(linesWidth > 1);
    end
    % If no channel selected: use all the channels
    if isempty(iSel)
        iSel = 1:length(hLines);
    end

    % Extract F (Y-values)
    linesY = get(hLines(iSel), 'YData');
    if iscell(linesY)
        FigData.F{iAxes} = cat(1,linesY{:});
    else
        FigData.F{iAxes} = linesY;
    end

    % Remove offsets
    if ~isempty(ChannelOffsets)
        chOffset = ChannelOffsets(iDispChannels(iSel));
        % Channel with negative offsets: ignore them (set them to zero)
        FigData.F{iAxes}(chOffset < 0, :) = 0;
        % Positive offsets: remove them
        iPosOffset = find(chOffset > 0);
        if ~isempty(iPosOffset)
            % Apply offsets
            FigData.F{iAxes}(iPosOffset,:) = FigData.F{iAxes}(iPosOffset,:) - repmat(chOffset, 1, size(FigData.F{iAxes},2));
        end
    end
    % Remove display factor
    FigData.F{iAxes} = FigData.F{iAxes} ./ Factor;
    % Undo the magnetometer / gradiometer scaling (CTF + Neuromag)
    if ~isempty(Channel)
        FigData.F{iAxes} = bst_scale_gradmag( FigData.F{iAxes}, Channel(iDispChannels(iSel)), 'reverse' );
    end

    % Keep only selected time indices
    if isSelectedTime && ~isempty(iTime)
        FigData.F{iAxes} = FigData.F{iAxes}(:,iTime);
    end

    % If some sensors are not displayed: recreate a full matrix
    if (length(iDispChannels) ~= length(Handles(iAxes).hLines)) && (length(iSel) == length(hLines))
        Ffull = zeros(length(Handles(iAxes).hLines), size(FigData.F{iAxes},2));
        Ffull(iDispChannels(iSel),:) = FigData.F{iAxes};
        FigData.F{iAxes} = Ffull;
    end

    % === COMMENTS ===
    % Get title for these axes
    FigData.AxesTitle{iAxes} = get(get(Handles(iAxes).hAxes, 'Title'), 'String');
    % Get legend of these axes (newer matlab create legend if does not exist: avoided with this test)
    if (bst_get('MatlabVersion') >= 903) && isempty(Handles(iAxes).hAxes.Legend)
        hLegend = [];
    else
        hLegend = legend(Handles(iAxes).hAxes);
    end
    if ~isempty(hLegend)
        if (bst_get('MatlabVersion') < 804)
            legendText = get(findobj(hLegend, 'type','text'), 'string');
            FigData.AxesLegend{iAxes} = bst_flip(legendText,1);
        else
            FigData.AxesLegend{iAxes} = get(hLegend, 'String');
        end
    % Legend are not accessible in the figure: need to reconstruct them
    elseif ~isempty(Channel)
        FigData.AxesLegend{iAxes} = {Channel(iDispChannels).Name};
    else
        FigData.AxesLegend{iAxes} = {};
    end

    % === TIME ===
    % Extract Time (X-values)
    if (iAxes == 1)
        FigData.Time = get(hLines(iSel(1)), 'XData');
        % If subselection of the time indices
        if isSelectedTime && ~isempty(iTime)
            FigData.Time = FigData.Time(iTime);
        end
    end
end
% If no data was extracted : exit
if isempty(FigData.F)
    FigData = [];
    return
end

% ===== GET EVENTS =====
hEventsBar = findobj(hFig, '-depth', 1, 'Tag', 'AxesEventsBar');
if (iAxes == 1) && ~isempty(hEventsBar) && ~isempty(GlobalData.DataSet(iDS).Measures.sFile) && ~isempty(GlobalData.DataSet(iDS).Measures.sFile.events)
    % Get events in current time window
    events = panel_record('GetEventsInTimeWindow', hFig);
    % Change the time vector
    FigData.Events = panel_record('ChangeTimeVector', events, 1./GlobalData.DataSet(iDS).Measures.SamplingRate, FigData.Time);
end


function test_issue623

% MEM 2gb
% WALLTIME 00:20:00
% DEPENCENCY

%%

filename = dccnpath('/home/common/matlab/fieldtrip/data/test/original/eeg/brainvision/Mischa.vhdr');

ft_hastoolbox('compat/obsolete', 1); % ft_qualitycheck has moved to be obsolete

%%

cfg = [];
cfg.dataset = filename;
cfg.saveplot = 'no';
cfg.savemat = 'no';
[info, timelock, freq, summary] = ft_qualitycheck(cfg);


function c=vertcat(varargin)
%
%           vertical concatination of the psfMatrix objects
%
%  Input:  any number of psfMatrix objects, or a multiPsfMatrix object
%
%  Output:  c is a multiPsfMatrix object of size (number of argin) by 1
%

%  K. Lee  2/28/02

if isa( varargin{1}, 'cell')
  A=varargin{1};  			   % A is a cell
  A=A{1};         			   % A is a psfMatrix object
  bdry=A.boundary;
  Apsf=A.psf;        			   % Apsf is a cell
  [r,d]=imageSize(A);
else
  A=varargin{1};
  bdry=A.boundary;
  Apsf=A.psf;
  [r,d]=imageSize(A);
end
c=multiPsfMatrix(varargin{1});
for i=2:nargin
  A=varargin{i};
  [r1,c1]=imageSize(A);
  bdry2=A.boundary;
    if ~isa(varargin{i},'psfMatrix'),
      error('psfMatrix objects can only be concatenated with other psfMatrix objects')
  elseif  (strcmp(bdry,bdry2)==1)
        c=multiPsfMatrix(c,varargin{i});
    else
      error('psfMatrix objects must have the same boundary')

    end
end


function x = i4vec_decrement ( n, x )

%*****************************************************************************80
%
%% I4VEC_DECREMENT decrements an I4VEC.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    18 July 2014
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer N, the number of entries in the array.
%
%    Input/output, integer X(N), the array to be decremented.
%
  x(1:n) = x(1:n) - 1;

  return
end


classdef(Abstract) AbstractCurveElement < matlab.mixin.SetGet & matlab.mixin.Copyable & matlab.mixin.Heterogeneous
    %AbstractCurveElement Summary of this class goes here
    %   Detailed explanation goes here

    properties
        indepVar(1,1) double %independent variable, such as indepVar
        depVar(1,1) double   %dependendent variable, such as throttleModifier
    end

    properties(Abstract, Constant)
        minIndepValue
        maxIndepValue

        minDepValue
        maxDepValue

        mandatoryIndepValues
    end

    methods(Abstract)
        indepVarName = getIndepVarName(obj)
        indepVarUnit = getIndepVarUnit(obj)
        depVarName = getDepVarName(obj)
        depVarUnit = getDepVarUnit(obj)
    end
end