% ----------------------------------------------------------------------------
% function hfssSetColor(fid, Objects, Color)
%
% Description :
% -------------
% Creates the VBScript necessary to set an object's color in HFSS.
%
% Parameters :
% ------------
% fid     - file identifier of the HFSS script file.
% Objects - cell array with name(s) of the object(s) whose color require(s)
%           to be changed.
% Color   - [3x1 vector] represents the [R, G, B] components of the color.
%
% Note :
% ------
%
% Example :
% ---------
% fid = fopen('myantenna.vbs', 'wt');
% ...
% hfssSetColor(fid, 'Substrate', [0, 64, 0]);
% hfssSetColor(fid, {'Sub1', 'Sub2'}, [0, 64, 0]);
%
% ----------------------------------------------------------------------------

% ----------------------------------------------------------------------------
% CHANGELOG
%
% ??-???-????: *Initial release.
% 09-Sep-2020: *It can set a color to several objects simultaneously.
% ----------------------------------------------------------------------------

% ----------------------------------------------------------------------------
% This file is part of HFSS-MATLAB-API.
%
% HFSS-MATLAB-API is free software; you can redistribute it and/or modify it
% under the terms of the GNU General Public License as published by the Free
% Software Foundation; either version 2 of the License, or (at your option)
% any later version.
%
% HFSS-MATLAB-API 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
% Foobar; if not, write to the Free Software Foundation, Inc., 59 Temple
% Place, Suite 330, Boston, MA  02111-1307  USA
%
% Copyright 2004, Vijay Ramasami (rvc@ku.edu)
% ----------------------------------------------------------------------------
function hfssSetColor(fid, Objects, Color)

if (~iscell(Objects))
    Objects = cellstr(Objects);
end

fprintf(fid, '\n');
fprintf(fid, 'oEditor.ChangeProperty _\n');
fprintf(fid, '\tArray("NAME:AllTabs", _\n');
fprintf(fid, '\t\tArray("NAME:Geometry3DAttributeTab", _\n');
fprintf(fid, '\t\t\tArray("NAME:PropServers", ');

for n = 1:numel(Objects)-1
    fprintf(fid, '"%s", ', Objects{n});
end
fprintf(fid, '"%s"), _\n', Objects{end});

fprintf(fid, '\t\t\tArray("NAME:ChangedProps",  _\n');
fprintf(fid, '\t\t\t\tArray("NAME:Color", "R:=", %d, "G:=", %d, "B:=", %d) _\n', ...
        Color(1), Color(2), Color(3));
fprintf(fid, '\t\t\t) _\n');
fprintf(fid, '\t\t) _\n');
fprintf(fid, '\t) \n');


function updateLineseries(obj,plotIndex)

%----SCATTER FIELDS----%

% x - [DONE]
% y - [DONE]
% r - [HANDLED BY SCATTER]
% t - [HANDLED BY SCATTER]
% mode - [DONE]
% name - [NOT SUPPORTED IN MATLAB]
% text - [DONE]
% error_y - [HANDLED BY ERRORBAR]
% error_x - [NOT SUPPORTED IN MATLAB]
% connectgaps - [NOT SUPPORTED IN MATLAB]
% fill - [HANDLED BY AREA]
% fillcolor - [HANDLED BY AREA]
% opacity --- [TODO]
% textfont - [NOT SUPPORTED IN MATLAB]
% textposition - [NOT SUPPORTED IN MATLAB]
% xaxis [DONE]
% yaxis [DONE]
% showlegend [DONE]
% stream - [HANDLED BY PLOTLYSTREAM]
% visible [DONE]
% type [DONE]

% MARKER
% marler.color - [DONE]
% marker.size - [DONE]
% marker.line.color - [DONE]
% marker.line.width - [DONE]
% marker.line.dash - [NOT SUPPORTED IN MATLAB]
% marker.line.opacity - [NOT SUPPORTED IN MATLAB]
% marker.line.smoothing - [NOT SUPPORTED IN MATLAB]
% marker.line.shape - [NOT SUPPORTED IN MATLAB]
% marker.opacity --- [TODO]
% marker.colorscale - [NOT SUPPORTED IN MATLAB]
% marker.sizemode - [NOT SUPPORTED IN MATLAB]
% marker.sizeref - [NOT SUPPORTED IN MATLAB]
% marker.maxdisplayed - [NOT SUPPORTED IN MATLAB]

% LINE

% line.color - [DONE]
% line.width - [DONE]
% line.dash - [DONE]
% line.opacity --- [TODO]
% line.smoothing - [NOT SUPPORTED IN MATLAB]
% line.shape - [NOT SUPPORTED IN MATLAB]

%-------------------------------------------------------------------------%

%-AXIS INDEX-%
axIndex = obj.getAxisIndex(obj.State.Plot(plotIndex).AssociatedAxis);

%-PLOT DATA STRUCTURE- %
plot_data = get(obj.State.Plot(plotIndex).Handle);

%-CHECK FOR MULTIPLE AXES-%
[xsource, ysource] = findSourceAxis(obj,axIndex);

%-AXIS DATA-%
eval(['xaxis = obj.layout.xaxis' num2str(xsource) ';']);
eval(['yaxis = obj.layout.yaxis' num2str(ysource) ';']);

%-------------------------------------------------------------------------%

%-scatter xaxis-%
obj.data{plotIndex}.xaxis = ['x' num2str(xsource)];

%-------------------------------------------------------------------------%

%-scatter yaxis-%
obj.data{plotIndex}.yaxis = ['y' num2str(ysource)];

%-------------------------------------------------------------------------%

%-scatter type-%
obj.data{plotIndex}.type = 'scatter';

%-------------------------------------------------------------------------%

%-scatter visible-%
obj.data{plotIndex}.visible = strcmp(plot_data.Visible,'on');

%-------------------------------------------------------------------------%

%-scatter x-%
obj.data{plotIndex}.x = plot_data.XData;

%-------------------------------------------------------------------------%

%-scatter y-%
obj.data{plotIndex}.y = plot_data.YData;

%-------------------------------------------------------------------------%

if isfield(plot_data,'ZData')
    if any(plot_data.ZData)
        %-scatter z-%
        obj.data{plotIndex}.z = plot_data.ZData;

        %overwrite type
        obj.data{plotIndex}.type = 'scatter3d';
    end
end

%-------------------------------------------------------------------------%

%-scatter name-%
obj.data{plotIndex}.name = plot_data.DisplayName;

%-------------------------------------------------------------------------%

%-scatter mode-%
if ~strcmpi('none', plot_data.Marker) ...
        && ~strcmpi('none', plot_data.LineStyle)
    mode = 'lines+markers';
elseif ~strcmpi('none', plot_data.Marker)
    mode = 'markers';
elseif ~strcmpi('none', plot_data.LineStyle)
    mode = 'lines';
else
    mode = 'none';
end

obj.data{plotIndex}.mode = mode;

%-------------------------------------------------------------------------%

%-scatter line-%
obj.data{plotIndex}.line = extractLineLine(plot_data);

%-------------------------------------------------------------------------%

%-scatter marker-%
obj.data{plotIndex}.marker = extractLineMarker(plot_data);

%-------------------------------------------------------------------------%

%-scatter showlegend-%
leg = get(plot_data.Annotation);
legInfo = get(leg.LegendInformation);

switch legInfo.IconDisplayStyle
    case 'on'
        showleg = true;
    case 'off'
        showleg = false;
end

obj.data{plotIndex}.showlegend = showleg;

%-------------------------------------------------------------------------%

end


function a = full(a)
%FULL         Convert Taylor to full
%

% written  05/21/09     S.M. Rump
%

  a.t = full(a.t);


function net = net_deploy(net,regression)
%CNN_IMAGENET_DEPLOY  Deploy a CNN

isDag = isa(net, 'dagnn.DagNN') ;
if isDag
  dagRemoveLayersOfType(net, 'dagnn.Loss') ;
  dagRemoveLayersOfType(net, 'dagnn.DropOut') ;
else
  net = simpleRemoveLayersOfType(net, 'softmaxloss') ;
  net = simpleRemoveLayersOfType(net, 'pdist') ;
  net = simpleRemoveLayersOfType(net, 'dropout') ;
end

if regression == 0
    if isDag
        net.addLayer('prob', dagnn.SoftMax(), 'prediction', 'prob', {}) ;
    else
        net.layers{end+1} = struct('name', 'prob', 'type', 'softmax') ;
    end
end

if isDag
  dagMergeBatchNorm(net) ;
  dagRemoveLayersOfType(net, 'dagnn.BatchNorm') ;
else
  net = simpleMergeBatchNorm(net) ;
  net = simpleRemoveLayersOfType(net, 'bnorm') ;
end

if ~isDag
  net = simpleRemoveMomentum(net) ;
end

% Switch to use MatConvNet default memory limit for CuDNN (512 MB)
if ~isDag
  for l = simpleFindLayersOfType(net, 'conv')
    net.layers{l}.opts = removeCuDNNMemoryLimit(net.layers{l}.opts) ;
  end
else
  for name = dagFindLayersOfType(net, 'dagnn.Conv')
    l = net.getLayerIndex(char(name)) ;
    net.layers(l).block.opts = removeCuDNNMemoryLimit(net.layers(l).block.opts) ;
  end
end

% -------------------------------------------------------------------------
function opts = removeCuDNNMemoryLimit(opts)
% -------------------------------------------------------------------------
remove = false(1, numel(opts)) ;
for i = 1:numel(opts)
  if isstr(opts{i}) && strcmp(lower(opts{i}), 'CudnnWorkspaceLimit')
    remove([i i+1]) = true ;
  end
end
opts = opts(~remove) ;

% -------------------------------------------------------------------------
function net = simpleRemoveMomentum(net)
% -------------------------------------------------------------------------
for l = 1:numel(net.layers)
  if isfield(net.layers{l}, 'momentum')
    net.layers{l} = rmfield(net.layers{l}, 'momentum') ;
  end
end

% -------------------------------------------------------------------------
function layers = simpleFindLayersOfType(net, type)
% -------------------------------------------------------------------------
layers = find(cellfun(@(x)strcmp(x.type, type), net.layers)) ;

% -------------------------------------------------------------------------
function net = simpleRemoveLayersOfType(net, type)
% -------------------------------------------------------------------------
layers = simpleFindLayersOfType(net, type) ;
net.layers(layers) = [] ;

% -------------------------------------------------------------------------
function layers = dagFindLayersWithOutput(net, outVarName)
% -------------------------------------------------------------------------
layers = {} ;
for l = 1:numel(net.layers)
  if any(strcmp(net.layers(l).outputs, outVarName))
    layers{1,end+1} = net.layers(l).name ;
  end
end

% -------------------------------------------------------------------------
function layers = dagFindLayersOfType(net, type)
% -------------------------------------------------------------------------
layers = [] ;
for l = 1:numel(net.layers)
  if isa(net.layers(l).block, type)
    layers{1,end+1} = net.layers(l).name ;
  end
end

% -------------------------------------------------------------------------
function dagRemoveLayersOfType(net, type)
% -------------------------------------------------------------------------
names = dagFindLayersOfType(net, type) ;
for i = 1:numel(names)
  layer = net.layers(net.getLayerIndex(names{i})) ;
  net.removeLayer(names{i}) ;
  net.renameVar(layer.outputs{1}, layer.inputs{1}, 'quiet', true) ;
end

% -------------------------------------------------------------------------
function dagMergeBatchNorm(net)
% -------------------------------------------------------------------------
names = dagFindLayersOfType(net, 'dagnn.BatchNorm') ;
for name = names
  name = char(name) ;
  layer = net.layers(net.getLayerIndex(name)) ;

  % merge into previous conv layer
  player = dagFindLayersWithOutput(net, layer.inputs{1}) ;
  player = net.layers(net.getLayerIndex(player)) ;
  if ~isa(player.block, 'dagnn.Conv')
    error('Batch normalization cannot be merged as it is not preceded by a conv layer.') ;
  end

  filters = net.getParamIndex(player.params{1}) ;
  biases = net.getParamIndex(player.params{2}) ;
  multipliers = net.getParamIndex(layer.params{1}) ;
  offsets = net.getParamIndex(layer.params{2}) ;
  moments = net.getParamIndex(layer.params{3}) ;

  [filtersValue, biasesValue] = mergeBatchNorm(...
    net.params(filters).value, ...
    net.params(biases).value, ...
    net.params(multipliers).value, ...
    net.params(offsets).value, ...
    net.params(moments).value) ;

  net.params(filters).value = filtersValue ;
  net.params(biases).value = biasesValue ;
end

% -------------------------------------------------------------------------
function net = simpleMergeBatchNorm(net)
% -------------------------------------------------------------------------

for l = 1:numel(net.layers)
  if strcmp(net.layers{l}.type, 'bnorm')
    if ~strcmp(net.layers{l-1}.type, 'conv')
      error('Batch normalization cannot be merged as it is not preceded by a conv layer.') ;
    end
    [filters, biases] = mergeBatchNorm(...
      net.layers{l-1}.weights{1}, ...
      net.layers{l-1}.weights{2}, ...
      net.layers{l}.weights{1}, ...
      net.layers{l}.weights{2}, ...
      net.layers{l}.weights{3}) ;
    net.layers{l-1}.weights = {filters, biases} ;
  end
end

% -------------------------------------------------------------------------
function [filters, biases] = mergeBatchNorm(filters, biases, multipliers, offsets, moments)
% -------------------------------------------------------------------------
% wk / sqrt(sigmak^2 + eps)
% bk - wk muk / sqrt(sigmak^2 + eps)
a = multipliers(:) ./ moments(:,2) ;
b = offsets(:) - moments(:,1) .* a ;
biases(:) = biases(:) + b(:) ;
sz = size(filters) ;
numFilters = sz(4) ;
filters = reshape(bsxfun(@times, reshape(filters, [], numFilters), a'), sz) ;


function save_em_history_data(fileName, data)
%     NeuroSLAM System Copyright (C) 2018-2019
%     NeuroSLAM: A Brain inspired SLAM System for 3D Environments
%
%     Fangwen Yu (www.yufangwen.com), Jianga Shang, Youjian Hu, Michael Milford(www.michaelmilford.com)
%
%     The NeuroSLAM V1.0 (MATLAB) was developed based on the OpenRatSLAM (David et al. 2013).
%     The RatSLAM V0.3 (MATLAB) developed by David Ball, Michael Milford and Gordon Wyeth in 2008.
%
%     Reference:
%     Ball, David, Scott Heath, Janet Wiles, Gordon Wyeth, Peter Corke, and Michael Milford.
%     "OpenRatSLAM: an open source brain-based SLAM system." Autonomous Robots 34, no. 3 (2013): 149-176.
%
%     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.
%
%     This program is distributed in the hope that it will be useful,
%     but WITHOUT ANY WARRANTY; without even the implied warranty of
%     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%     GNU General Public License for more details.
%
%     You should have received a copy of the GNU General Public License
%     along with this program.  If not, see <http://www.gnu.org/licenses/>.

    fileID = fopen(fileName,'w');
    len = length(data(:,1));
    for ind = 1 : len
        fprintf(fileID,'%d %d \r\n',ind,data(ind, :));
    end
    fclose(fileID);
end


function db_update(LatestDbVersion, sProtocol, saveMetadata)
% DB_UPDATE: Updates any existing Brainstorm database to the current database structure.
%
% USAGE:  db_update(LatestDbVersion);              % Updates all the protocols of the database loaded in GlobalData
%         db_update(LatestDbVersion, iProtocols);  % Updates specific protocols
%         db_update(LatestDbVersion, sProtocol);   % Updates a new protocol
%
% INPUT:
%     - LatestDbVersion : The latest version number of the database structure
%     - sProtocol : Protocol structure, with the following fields (some fields are ignored, depending on the action)
%          |- Comment  : Name of the protocol
%          |- SUBJECTS : Directory that contains the anatomies of the subjects (MRI + surfaces)
%          |- STUDIES  : Directory that contains the functional data (recordings, sensors, sources...)
%          |- iStudy   : Ignored
%          |- UseDefaultAnat    : Ignored
%          |- UseDefaultChannel : Ignored
%     - saveMetadata : Whether to save the modifications of the protocol metadata (default: yes)
%
% @=============================================================================
% 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, 2009-2013; Martin Cousineau, 2019

global GlobalData;

%% Parse inputs
if nargin < 2 || isempty(sProtocol)
    iProtocols = 1:length(GlobalData.DataBase.ProtocolInfo);
    sProtocol = [];
elseif isnumeric(sProtocol)
    iProtocols = sProtocol;
    sProtocol = [];
end
if nargin < 3 || isempty(saveMetadata)
    saveMetadata = 1;
end
if isempty(sProtocol)
    % We are updating the GlobalData database
    isGlobal = 1;
    ProtocolsListInfos    = GlobalData.DataBase.ProtocolInfo(iProtocols);
    ProtocolsListStudies  = GlobalData.DataBase.ProtocolStudies(iProtocols);
    ProtocolsListSubjects = GlobalData.DataBase.ProtocolSubjects(iProtocols);
    CurrentDbVersion      = GlobalData.DataBase.DbVersion;
else
    % We are updating a specific protocol
    isGlobal = 0;
    [ProtocolMat, ProtocolFile] = GetProtocolMat(sProtocol);
    if isempty(ProtocolMat)
        % If not enough information to update protocol, return
        return;
    end
    ProtocolsListInfos    = sProtocol;
    ProtocolsListStudies  = ProtocolMat.ProtocolStudies;
    ProtocolsListSubjects = ProtocolMat.ProtocolSubjects;
    CurrentDbVersion      = ProtocolMat.DbVersion;
end

%% Check whether the update is necessary
if abs(CurrentDbVersion - LatestDbVersion) < 1e-8
    % Already up to date, return
    return;
elseif CurrentDbVersion > LatestDbVersion
    % The database is more up to date than the software, prompt for BST update
    if isGlobal
        bst_splash('hide');
    end
    errMsg = 'You are trying to load a protocol that was created with a newer version of Brainstorm.';
    [res, isCancel] = java_dialog('question', [errMsg 10 'Would you like to update Brainstorm now to continue?'], 'Update database');
    if ~isCancel && strcmpi(res, 'yes')
        bst_update();
        return;
    else
        error([errMsg 10 'Please update Brainstorm to continue.']);
    end
end

isFirstWarning = 1;

%% ===== UPDATE 31-Jul-2009 =====
% Modification: Add 'Image' field to the studies
function sStudy = AddImage(sStudy)
    sStudy.Image = repmat(db_template('Image'), 0);
end
% Perform update
if ~isempty(ProtocolsListStudies) && ~isempty(ProtocolsListStudies(1).Study) && ~isfield(ProtocolsListStudies(1).Study, 'Image')
    disp('BST> Database structure: Adding image support...');
    ApplyToDatabase(@AddImage);
    disp('BST> Database structure: Done.');
end

%% ===== UPDATE 16-Oct-2009 =====
% Modification: Add 'NoiseCov' field to the studies
function sStudy = AddNoiseCov(sStudy)
    sStudy.NoiseCov = repmat(db_template('NoiseCov'), 0);
end
% Perform update
if ~isempty(ProtocolsListStudies) && ~isempty(ProtocolsListStudies(1).Study) && ~isfield(ProtocolsListStudies(1).Study, 'NoiseCov')
    disp('BST> Database structure: Adding noise covariance matrix...');
    ApplyToDatabase(@AddNoiseCov);
    disp('BST> Database structure: Done.');
end

%% ===== UPDATE 20-Mar-2010 =====
% Modification: Add 'Dipoles' field to the studies
function sStudy = AddDipoles(sStudy)
    sStudy.Dipoles = repmat(db_template('Dipoles'), 0);
end
% Perform update
if ~isempty(ProtocolsListStudies) && ~isempty(ProtocolsListStudies(1).Study) && ~isfield(ProtocolsListStudies(1).Study, 'Dipoles')
    disp('BST> Database structure: Adding dipoles...');
    ApplyToDatabase(@AddDipoles);
    disp('BST> Database structure: Done.');
end

%% ===== UPDATE 15-Apr-2010 =====
% Modification: Add 'Timefreq' field to the studies
function sStudy = AddTimefreq(sStudy)
    sStudy.Timefreq = repmat(db_template('Timefreq'), 0);
end
% Perform update
if ~isempty(ProtocolsListStudies) && ~isempty(ProtocolsListStudies(1).Study) && ~isfield(ProtocolsListStudies(1).Study, 'Timefreq')
    disp('BST> Database structure: Adding time-frequency...');
    ApplyToDatabase(@AddTimefreq);
    disp('BST> Database structure: Done.');
end

%% ===== UPDATE 25-Jun-2010 =====
% Modifications: - Rename 'HeadModelName' field in 'Comment'
%                - Remove GridName field
function sStudy = UpdateHeadmodel(sStudy)
    % If no head models
    if isempty(sStudy.HeadModel)
        sStudy.HeadModel = repmat(db_template('headmodel'), 0);
    else
        if isfield(sStudy.HeadModel, 'HeadModelName')
            % For each headmodel in this study
            for iHeadModel = 1:length(sStudy.HeadModel)
                % Copy HeadModelName to Comment
                sStudy.HeadModel(iHeadModel).Comment = sStudy.HeadModel(iHeadModel).HeadModelName;
            end
            % Remove HeadModelName field
            sStudy.HeadModel = rmfield(sStudy.HeadModel, 'HeadModelName');
        end
        % Remove GridName field
        if isfield(sStudy.HeadModel, 'GridName')
            sStudy.HeadModel = rmfield(sStudy.HeadModel, 'GridName');
        end
    end
end
% If update was not performed yet
if ~isempty(ProtocolsListStudies) && ~isempty(ProtocolsListStudies(1).DefaultStudy) && isfield(ProtocolsListStudies(1).DefaultStudy.HeadModel, 'GridName')
    disp('BST> Database structure: Updating headmodel definition...');
    ApplyToDatabase(@UpdateHeadmodel);
    disp('BST> Database structure: Done.');
end

%% ===== UPDATE 25-Jun-2010 =====
% Modification: Add 'HeadModelType' to the Results structure
function sStudy = AddHeadModelType(sStudy)
    if ~isempty(sStudy.Result)
        [sStudy.Result.HeadModelType] = deal('surface');
    else
        sStudy.Result = repmat(db_template('results'), 0);
    end
end
% If update was not performed yet
if ~isempty(ProtocolsListStudies) && ~isempty(ProtocolsListStudies(1).AnalysisStudy) && ~isfield(ProtocolsListStudies(1).AnalysisStudy.Result, 'HeadModelType')
    disp('BST> Database structure: Adding HeadModelType to the Results structures...');
    ApplyToDatabase(@AddHeadModelType);
    disp('BST> Database structure: Done.');
end

%% ===== UPDATE 12-Jul-2010 =====
% Modification: Add 'BadTrial' and 'DataType' to the Data structure
% Function to update structure
function sStudy = AddBadTrial(sStudy)
    if ~isempty(sStudy.Data)
        [sStudy.Data.DataType] = deal('recordings');
        [sStudy.Data.BadTrial] = deal(0);
    else
        sStudy.Data = repmat(db_template('data'), 0);
    end
end
% If update was not performed yet
if ~isempty(ProtocolsListStudies) && ~isempty(ProtocolsListStudies(1).AnalysisStudy) && ~isfield(ProtocolsListStudies(1).AnalysisStudy.Data, 'DataType')
    disp('BST> Database structure: Adding BadTrial definition...');
    ApplyToDatabase(@AddBadTrial);
    disp('BST> Database structure: Done.');
end

%% ===== UPDATE 14-Jul-2010 =====
% Modification: Remove the "linkresults" files, now links are represented by filenames of the type "link|resultsfile.mat|datafile.mat"
if (CurrentDbVersion < 3)
    % Hide splash screen
    bst_splash('hide');
    % A full reload of the database is necessary to update all the links
    java_dialog('msgbox', ['Brainstorm was updated: the database structure changed.' 10 10 ...
                           'Please click on "Ok" and wait while all the database is being reloaded.' 10 ...
                           'Be patient, it might take several minutes...' 10 10], ...
                           'Database update');
    disp('BST> Reloading database... This might take several minutes...');
    % Reload database
    db_reload_database();
    % Get again the list of protocols for successive updates
    SaveProtocolStudies();
end

%% ===== UPDATE 07-October-2010 =====
% Modification: Add 'Matrix' field to the studies
function sStudy = AddMatrix(sStudy)
    sStudy.Matrix = repmat(db_template('Matrix'), 0);
end
% Perform update
if ~isempty(ProtocolsListStudies) && ~isempty(ProtocolsListStudies(1).Study) && ~isfield(ProtocolsListStudies(1).Study, 'Matrix')
    disp('BST> Database structure: Adding custom "matrix" file type...');
    ApplyToDatabase(@AddMatrix);
    disp('BST> Database structure: Done.');
end

%% ===== UPDATE 03-March-2011 =====
% Modification: Setting the subject file of "inter-subject" data to the default anatomy
if (CurrentDbVersion < 3.2)
    disp('BST> Database update: Setting the inter-subject nodes anatomy to the default anatomy.');
    % Set it protocol by protocol (if not set)
    for i = 1:length(ProtocolsListStudies)
        defSubjFile = bst_fullfile(bst_get('DirDefaultSubject'), 'brainstormsubject.mat');
        defSubjFileFull = bst_fullfile(ProtocolsListInfos(i).SUBJECTS, defSubjFile);
        if ~isempty(ProtocolsListStudies(i).AnalysisStudy) && isempty(ProtocolsListStudies(i).AnalysisStudy.BrainStormSubject) && file_exist(defSubjFileFull)
            ProtocolsListStudies(i).AnalysisStudy.BrainStormSubject = file_win2unix(defSubjFile);
        end
    end
    % Save database
    SaveProtocolStudies();
end

%% ===== UPDATE 15-March-2011 =====
% Database update: Changing the time reference in RAW FIF files from relative to absolute (the first sample is no longer t=0).
function sStudy = UpdateFifTimeRef(sStudy)
    for iData = 1:length(sStudy.Data)
        if strcmpi(sStudy.Data(iData).DataType, 'raw')
            try
                DataFile = file_fullpath(sStudy.Data(iData).FileName);
                disp(['UPDATE> Checking file: ' DataFile]);
                % Load file header
                DataMat = in_bst_data(DataFile);
                % If it is a RAW FIF file: Update time definition
                if strcmpi(DataMat.F.format, 'FIF') && isfield(DataMat.F.header,'raw') && (DataMat.F.header.raw.first_samp ~= 0) && (DataMat.Time(1) == 0)
                    disp('UPDATE> Updating file...');
                    addSamples = double(DataMat.F.header.raw.first_samp);
                    addTime    = addSamples / DataMat.F.prop.sfreq;
                    DataMat.Time         = DataMat.Time + addTime;
                    DataMat.F.prop.times = DataMat.F.prop.times + addTime;
                    for iEvt = 1:length(DataMat.F.events)
                        DataMat.F.events(iEvt).times = DataMat.F.events(iEvt).times + addTime;
                    end
                    % Save file back
                    bst_save(DataFile, DataMat, 'v6');
                end
            catch
                disp('UPDATE> Error updating file...');
            end
        end
    end
end
if (CurrentDbVersion < 3.3)
    disp('BST> Database update: Changing the time reference in RAW FIF files from relative to absolute (the first sample is no longer t=0).');
    ApplyToDatabase(@UpdateFifTimeRef);
    disp('BST> Database update: Done.');
end

%% ===== UPDATE 07-Dec-2011 =====
% Modification: Subject name is now defined based on the folder name (previously: "Name" folder)
if (CurrentDbVersion < 3.4)
    disp('BST> Database update: Forcing the subjects names to match the folder name.');
    % Update protocol by protocol
    for i = 1:length(ProtocolsListSubjects)
        % Process each subject
        for iSubj = 1:length(ProtocolsListSubjects(i).Subject)
            % Update name based on folder name
            ProtocolsListSubjects(i).Subject(iSubj).Name = bst_fileparts(ProtocolsListSubjects(i).Subject(iSubj).FileName);
        end
    end
    % Save database
    SaveProtocolSubjects();
end

%% ===== UPDATE 31-Jan-2013 =====
% Modified lots of things in the pipeline editor: resetting the options
if (CurrentDbVersion < 3.51)
    disp('BST> Software update: Resetting the process options...');
    % Reset all the saved options
    bst_set('ProcessOptions', []);
end

%% ===== UPDATE 21-Fev-2013 =====
% Function to update structure
function sStudy = AddIntraElectrodes(sStudy)
    if ~isempty(sStudy.HeadModel)
        [sStudy.HeadModel.ECOGMethod] = deal('');
        [sStudy.HeadModel.SEEGMethod] = deal('');
    else
        sStudy.Data = repmat(db_template('headmodel'), 0);
    end
end
% Modification: Add 'SEEGMethod' and 'ECOGMethod' to the Headmodel structure
if (CurrentDbVersion < 3.6) && ~isempty(ProtocolsListStudies) && ~isfield(ProtocolsListStudies(1).AnalysisStudy.HeadModel, 'ECOGMethod')
    disp('BST> Database structure: Adding intra-cranial electrodes definition...');
    ApplyToDatabase(@AddIntraElectrodes);
    disp('BST> Database structure: Done.');
    % Reset all the saved options
    bst_set('ProcessOptions', []);
end

%% ===== UPDATES: 7-May-2019, 22-May-2019, 31-Aug-2019 =====
% Modification: add fibers and FEM objects
if (CurrentDbVersion < 5.02)
    disp('BST> Database update: Adding support for fibers and FEM...');
    for iProt = 1:length(ProtocolsListSubjects)
        subjFields = fieldnames(ProtocolsListSubjects(iProt));
        for iField = 1:length(subjFields)
            subjField = subjFields{iField};
            nSubjects = length(ProtocolsListSubjects(iProt).(subjField));
            sSubjects = repmat(db_template('subject'), 1, nSubjects);
            for iSubj = 1:nSubjects
                % Add iFibers to subject
                sSubjects(iSubj) = struct_copy_fields(db_template('subject'), ProtocolsListSubjects(iProt).(subjField)(iSubj));
                % Add Fibers to subjectmat
                SubjectFile = bst_fullfile(ProtocolsListInfos(iProt).SUBJECTS, sSubjects(iSubj).FileName);
                if file_exist(SubjectFile)
                    SubjectMat = load(SubjectFile);
                    SubjectMat = struct_copy_fields(db_template('subjectmat'), SubjectMat);
                    bst_save(SubjectFile, SubjectMat, 'v7');
                end
            end
            ProtocolsListSubjects(iProt).(subjField) = sSubjects;
        end
    end
    disp('BST> Database update: Done.');
end

%% ===== JUST BEFORE RETURNING TO STARTUP FUNCTION =====
% Save the new database version
if saveMetadata
    if isGlobal
        GlobalData.DataBase.ProtocolInfo(iProtocols)     = ProtocolsListInfos;
        GlobalData.DataBase.ProtocolSubjects(iProtocols) = ProtocolsListSubjects;
        GlobalData.DataBase.ProtocolStudies(iProtocols)  = ProtocolsListStudies;
        GlobalData.DataBase.DbVersion        = LatestDbVersion;
        db_save();
    else
        ProtocolMat.ProtocolInfo     = ProtocolsListInfos;
        ProtocolMat.ProtocolSubjects = ProtocolsListSubjects;
        ProtocolMat.ProtocolStudies  = ProtocolsListStudies;
        ProtocolMat.DbVersion        = LatestDbVersion;
        bst_save(ProtocolFile, ProtocolMat, 'v7');
    end
end

%% ===================================================================================================
%  ===== HELPER FUNCTIONS ============================================================================
%  ===================================================================================================
%% ===== APPLY FUNCTION TO DATABASE =====
    function ApplyToDatabase(UpdateFcn)
        % ===== WARNING/BACKUP =====
        % Display warning before updating database
        if isFirstWarning
            % Hide splash screen
            bst_splash('hide');
            % Display warning message
            msgWarning = ['If after the update you cannot start Brainstorm anymore: ' 10 ...
                          '  - Do not try to run a previous version of the software, it would not help.' 10 ...
                          '  - Type "brainstorm reset", start Brainstorm, then re-import your protocols.' 10 ...
                          '  - Report the bug on the user forum'];
            disp(['*********************************************************************' 10 ...
                  msgWarning 10 ...
                  '*********************************************************************']);
            isOk = java_dialog('confirm', ...
                ['Warning: You are running a newer version of Brainstorm.' 10 ...
                 'Some modifications have to be performed on your database.' 10 10 ...
                 msgWarning  10 10 ...
                 'Update database now?' 10 10], 'Database update');
            if ~isOk
                error(['User cancelled database update.' 10 'This version of Brainstorm cannot be started, please try your previous version.']);
            end
            % Make a backup copy of the current database
            c = clock;
            DbFile = bst_get('BrainstormDbFile');
            BakDbFile = file_unique(strrep(DbFile, '.mat', sprintf('_%02.0f%02.0f%02.0f_%02.0f%02.0f.bak', c(1)-2000, c(2:5))));
            try
                file_copy(DbFile, BakDbFile);
            catch
                disp(['UPDATE> Cannot write backup file "' BakDbFile '".']);
            end
            % Do not display the warning again
            isFirstWarning = 0;
        end

        % ===== PROCESSING ======
        % Progress bar
        bst_progress('start', 'Update database', 'Updating database structure...');
        % For each protocol
        for ip = 1:length(ProtocolsListStudies)
            % If no study in the protocol
            if isempty(ProtocolsListStudies(ip).Study)
                %ProtocolsListStudies(ip).Study = repmat(db_template('Study'), 0);
                ProtocolsListStudies(ip).Study(1).Comment = 'update';
                ProtocolsListStudies(ip).Study = repmat(UpdateFcn(ProtocolsListStudies(ip).Study), 0);
            else
                % Update each study in this protocol
                ProtocolStudies = repmat(ProtocolsListStudies(ip).Study, 0);
                for is = 1:length(ProtocolsListStudies(ip).Study)
                    ProtocolStudies(is) = UpdateFcn(ProtocolsListStudies(ip).Study(is));
                end
                ProtocolsListStudies(ip).Study = ProtocolStudies;
            end
            if ~isempty(ProtocolsListStudies(ip).DefaultStudy)
                ProtocolsListStudies(ip).DefaultStudy = UpdateFcn(ProtocolsListStudies(ip).DefaultStudy);
            end
            if ~isempty(ProtocolsListStudies(ip).AnalysisStudy)
                ProtocolsListStudies(ip).AnalysisStudy = UpdateFcn(ProtocolsListStudies(ip).AnalysisStudy);
            end
        end
        % Save database
        SaveProtocolStudies();
        bst_progress('stop');
    end

    %% ===== SAVE CHANGES TO DATABASE =====
    function SaveProtocolStudies()
        if saveMetadata
            if isGlobal
                GlobalData.DataBase.ProtocolStudies(iProtocols) = ProtocolsListStudies;
            else
                ProtocolMat.ProtocolStudies = ProtocolsListStudies;
                bst_save(ProtocolFile, ProtocolMat, 'v7');
            end
        end
    end
    function SaveProtocolSubjects()
        if saveMetadata
            if isGlobal
                GlobalData.DataBase.ProtocolSubjects(iProtocols) = ProtocolsListSubjects;
            else
                ProtocolMat.ProtocolSubjects = ProtocolsListSubjects;
                bst_save(ProtocolFile, ProtocolMat, 'v7');
            end
        end
    end

end

function [ProtocolMat, ProtocolFile] = GetProtocolMat(sProtocol)
    ProtocolMat  = [];
    ProtocolFile = [];

    if isstruct(sProtocol) && isfield(sProtocol, 'STUDIES') && ~isempty(sProtocol.STUDIES)
        ProtocolFile = bst_fullfile(sProtocol.STUDIES, 'protocol.mat');
        if file_exist(ProtocolFile)
            ProtMat = load(ProtocolFile);
            if isfield(ProtMat, 'DbVersion')
                ProtocolMat = ProtMat;
            end
        end
    end
end


function planC = postProcStruct(planC,userOptS)
% planC = postProcStruct(planC,userOptS);
% Function to post-process segementations.
%-------------------------------------------------------------------------
%
%
% AI 10/14/19

% Get list of structures to be post-processed
postS = [];
if isfield(userOptS,'postProc')
    postS = userOptS.postProc;
end

if ~isempty(postS)

    strC = fieldnames(postS);

    indexS = planC{end};

    %Loop over structures from postS
    for iStr = 1:length(strC)

        outMask3M = [];

        methodC = postS.(strC{iStr}); %.method;
        if ~iscell(postS.(strC{iStr})) %.method)
            methodCStruct = methodC;
            methodC = {};
            for i = 1:numel(methodCStruct)
              methodC{i} = methodCStruct(i);
            end
        end

        maskC = cell(length(methodC),1);

        %Loop over post-processing methods
        for iMethod = 1:length(methodC)
            strListC = {planC{indexS.structures}.structureName};
            %get updated structure number in case iMethod > 1
            strNum = getMatchingIndex(strC{iStr},strListC,'EXACT');
            strNum = strNum(end); %Use most recent str if >1 matches found
            scanNum = getStructureAssociatedScan(strNum,planC);

            switch methodC{iMethod}.method

                case 'getLargestConnComps'

                    numConnComponents = methodC{iMethod}.params.numCC;
                    [maskC{iMethod}, planC]= getLargestConnComps(strNum,numConnComponents,planC);

                case 'getLargestOverlappingComp'

                    roiName = methodC{iMethod}.params.roiName;
                    roiStrNum = getMatchingIndex(roiName,strListC,'EXACT');
                    [maskC{iMethod}, planC] = getLargestOverlappingComp(strNum,roiStrNum,planC);

                case 'getSegInROI'

                    roiName = methodC{iMethod}.params.roiName;
                    roiStrNum = getMatchingIndex(roiName,strListC,'EXACT');

                    [roiMask3M, planC] = getStrMask(roiStrNum,planC);
                    [strMask3M, planC] = getStrMask(strNum,planC);

                    maskC{iMethod} = roiMask3M & strMask3M;

                case 'removeBackgroundFP'
                    ctOffset = planC{indexS.scan}(scanNum).scanInfo(1).CTOffset;
                    scan3M = double(getScanArray(scanNum,planC)) - ctOffset;
                    threshold = -400; % default for CT
                    if isfield(methodC{iMethod},'params') && isfield(methodC{iMethod}.params,'threshold')
                        threshold = methodC{iMethod}.params.threshold;
                    end
                    connPtMask3M = getPatientOutline(scan3M,[],threshold);
                    [strMask3M, planC] = getStrMask(strNum,planC);

                    maskC{iMethod} = strMask3M & connPtMask3M;

                case 'none'

                    [maskC{iMethod}, planC] = getStrMask(strNum,planC);

                otherwise
                    %Custom post-processing function
                    customMethod = methodC{iMethod};
                    if isfield(customMethod,'params')
                        paramS = customMethod.params;
                    else
                        paramS = [];
                    end
                    [maskC{iMethod},planC] = feval(customMethod.method,strNum,paramS,planC);

            end

            %Combine masks
            if iMethod>1
                switch lower(methodC{iMethod}.operator)
                    case 'union'
                        outMask3M = or(maskC{iMethod-1},maskC{iMethod});
                        maskC{iMethod} = outMask3M;
                    case 'intersection'
                        outMask3M = and(maskC{iMethod-1},maskC{iMethod});
                        maskC{iMethod} = outMask3M;
                end
            end

            %Replace original structure
            outMask3M = maskC{iMethod};
            planC = maskToCERRStructure(outMask3M,0,scanNum,strC{iStr},planC);
            roiGenAlg = planC{indexS.structures}(strNum).roiGenerationAlgorithm;
            roiGenDesc = planC{indexS.structures}(strNum).roiGenerationDescription;
            planC{indexS.structures}(end).roiGenerationAlgorithm = roiGenAlg;
            planC{indexS.structures}(end).roiGenerationDescription = roiGenDesc;
            planC{indexS.structures}(end).structureDescription = roiGenDesc;
            planC = deleteStructure(planC,strNum);
        end

    end
end


function test_readcompresseddata

% MEM 4gb
% WALLTIME 00:10:00
% DEPENDENCY inflate_file ft_read_data ft_read_header test_readcompresseddata

% test these data sets
datasets = {
  dccnpath('/home/common/matlab/fieldtrip/data/test/original/meg/ctf151/Subject01.ds.zip')
  dccnpath('/home/common/matlab/fieldtrip/data/test/original/eeg/brainvision/MischaCompressed.zip')
};

for k = 1:numel(datasets)
  cfg = [];
  cfg.continuous = 'yes';
  cfg.dataset = datasets{k};
  data = ft_preprocessing(cfg);
end

% and this mri
mri = ft_read_mri(dccnpath('/home/common/matlab/fieldtrip/data/test/latest/mri/dicom/dicomzipped.zip'));

end


function test_model = merge_model_arr(test_model_arr)
    test_model.class = test_model_arr(1).class;
    test_model.S_bar = test_model_arr(1).S_bar;
    test_model.defBasis = test_model_arr(1).defBasis;
    test_model.part_names = test_model_arr(1).part_names;
    test_model.sigma_sq = test_model_arr(1).sigma_sq;
    test_model.points3 = vertcat(test_model_arr(:).points3);
    test_model.points3 = [test_model.points3(1:3:end,:);test_model.points3(2:3:end,:);...
        test_model.points3(3:3:end,:)];
    test_model.rots = [test_model_arr(:).rots];
    test_model.trs = vertcat(test_model_arr(:).trs);
    test_model.def_weights = vertcat(test_model_arr(:).def_weights);
    test_model.voc_image_id = [test_model_arr(:).voc_image_id]';
    if(isfield(test_model_arr(1),'voc_rec_id'))
        test_model.voc_rec_id = vertcat(test_model_arr(:).voc_rec_id);
    end
    test_model.bbox = vertcat(test_model_arr(:).bbox);
    test_model.points2 = vertcat(test_model_arr(:).points2);
    test_model.points2 = [test_model.points2(1:2:end,:);test_model.points2(2:2:end,:)];
    test_model.params = test_model_arr(1).params;
    test_model.c = vertcat(test_model_arr(:).c);
    if(isfield(test_model_arr(1),'flip'))
        test_model.flip = vertcat(test_model_arr(:).flip);
    end

    try
        for i=1:length(test_model_arr)
            test_model.seg.poly_x{i} = test_model_arr(i).seg.poly_x{1};
            test_model.seg.poly_y{i} = test_model_arr(i).seg.poly_y{1};
        end
        test_model.seg.poly_x = test_model.seg.poly_x';
        test_model.seg.poly_y = test_model.seg.poly_y';
    catch
        test_model.seg = {test_model_arr(:).seg}';
    end
end


function [matchedObjMaps euclideanStruct] = displayMatchingObjs(inputImages,globalIDs,varargin)
    % Displays information on matching cells across sessions.
    % Biafra Ahanonu
    % started: 2014.01.03 [19:13:01]
    % inputs
        % inputImages - cell array {1, N} N = each trial of [x y nFilters] matrices containing each set of filters, e.g. {imageSet1, imageSet2,...}
        % globalIDs, [M N] matrix with M = number of global IDs and N = each trial. Each m,n pair specifies the index of that global obj m in the data of trial n. If .globalIDs(m,n)==0, means no match was found.
    % outputs
        %
    % changelog
        % 2017.01.14 [20:06:04] - support switched from [nSignals x y] to [x y nSignals]
        % 2021.08.08 [19:30:20] - Updated to handle CIAtah v4.0 switch to all functions inside ciapkg package.
        % 2021.12.31 [18:59:24] - Updated suptitle to ciapkg.overloaded.suptitle
    % TODO
        %

    import ciapkg.api.* % import CIAtah functions in ciapkg package API.

    %========================
    options.inputSignals = [];
    %
    options.permuteImages = 0;
    %
    options.globalIDCoords = [];
    %
    options.sortGlobalIDs = 1;
    %
    options.fps = 20;
    % get options
    options = getOptions(options,varargin);
    % display(options)
    % unpack options into current workspace
    % fn=fieldnames(options);
    % for i=1:length(fn)
    %   eval([fn{i} '=options.' fn{i} ';']);
    % end
    %========================

    try
        guiRow = 2;
        guiCol = 3;

        matchedObjMaps = [];
        euclideanStruct = struct;
        if options.permuteImages==1
            for imageNo=1:length(inputImages)
                % inputImages{imageNo} = permute(inputImages{imageNo}, [3 1 2]);
            end
        end

        if ~isempty(options.inputSignals)
            [~, ~] = openFigure(292, '');
            [nrows, ncols] = cellfun(@size, options.inputSignals);
            maxCols = max(ncols);
            signalSnrAll = NaN([length(options.inputSignals) maxCols]);
            for j=1:length(options.inputSignals)
                [signalSnr ~] = computeSignalSnr(options.inputSignals{j});
                signalSnrAll(j,1:length(signalSnr)) = signalSnr;
            end
            plot(signalSnrAll');
            % legend(292);
        end

        reverseStr = '';
        nGlobals = size(globalIDs,1);
        nObjPerGlobal = sum(globalIDs>0,2);
        if options.sortGlobalIDs==1
            % sort global IDs by those with most
            [nObjPerGlobalSorted sortedIdx] = sort(nObjPerGlobal,'descend');
            globalIDs = globalIDs(sortedIdx,:);
        end

        for gID = 1:size(globalIDs,2)
            cumProb(gID) = sum(sum(~(globalIDs==0),2)==gID)/size(globalIDs,1);
        end
        [~, ~] = openFigure(1930, '');
        plot(cumProb);box off;
        % set(gca,'yscale','log');
        % set(gca,'xscale','log');
        % [~, ~] = openFigure(8282, '');
        % for i=1:nGlobals
        globalNo = 1;
        saveData = 0;

        [nSignalsSessions, nPointsSessions] = cellfun(@size, options.inputSignals);
        while saveData==0
            nMatchedIDs = sum(globalIDs(globalNo,:)~=0);
            directionOfNextChoice = 1;
            % if globalNo==10
            %     saveData = 1;
            % end

            [~, ~] = openFigure(8282, '');
            for j=1:length(inputImages)
                iIdx = globalIDs(globalNo,j);
                if iIdx==0
                    nullImage = zeros(size(squeeze(inputImages{1}(:,:,1))));
                    nullImage(1,1) = 1;
                    groupImages(:,:,j) = nullImage;
                else
                    % display([num2str(j) ',' num2str(iIdx)])
                    groupImages(:,:,j) = squeeze(inputImages{j}(:,:,iIdx));
                end
                subplot(1,length(inputImages),j)
                imagesc(squeeze(groupImages(:,:,j)));
            end
            % display('')
            % pause

            [~, ~] = openFigure(1929, '');

            if ~isempty(options.inputSignals)
                % compute shuffled signalFeatures pairwise
                % if nMatchedIDs>2
                    nShuffles = 2;
                    reverseStr = '';
                    tmpGlobalIDs = globalIDs;
                    matchIDList = globalIDs(globalNo,:);
                    matchIDIdx = matchIDList~=0;
                    maxGlobalIDs = max(globalIDs,[],1);

                    matchIDNames = matchIDList(matchIDIdx);
                    matchIDNames = cellfun(@num2str,(mat2cell(matchIDNames(:),ones([1 length(matchIDNames)]))),'UniformOutput',false);

                    for shuffleNo = 1:nShuffles
                        % obtain a random ID within the range for each trial...clever clever...jk
                        for maxGlobalShuffleNo = 1:length(maxGlobalIDs)
                            if maxGlobalIDs(maxGlobalShuffleNo)==0
                                shuffledIDs(maxGlobalShuffleNo) = 0;
                            else
                                shuffledIDs(maxGlobalShuffleNo) = randsample(min(nSignalsSessions),1,true);
                            end
                            % if nSignalsSessions
                        end
                        % shuffledIDs = arrayfun(@(x) randsample(x,1,true), maxGlobalIDs);
                        shuffledIDs(~matchIDIdx) = 0;
                        tmpGlobalIDs(globalNo,:) = shuffledIDs(:)';
                        % tmpGlobalIDs
                        [~, matchedSignals] = getGlobalData(inputImages,tmpGlobalIDs,options.inputSignals,globalNo);
                        matchIdx = sum(matchedSignals,2)~=0;
                        [~, ~, signalFeaturesEuclideanShuffle, ~] = getSignalFeatures(matchedSignals(matchIdx,:));
                        shuffledSignalFeaturesEuclidean(shuffleNo) = signalFeaturesEuclideanShuffle;
                        reverseStr = cmdWaitbar(shuffleNo,nShuffles,reverseStr,'inputStr','shuffling euclidean distances','waitbarOn',0,'displayEvery',1);
                    end
                    shuffledSignalFeaturesEuclideanMean(globalNo,1) = nanmean(shuffledSignalFeaturesEuclidean);
                    shuffledSignalFeaturesEuclideanStd(globalNo,1) = nanstd(shuffledSignalFeaturesEuclidean);

                    % matchIdx = sum(matchedSignals,2)~=0;
                    [~, matchedSignals] = getGlobalData(inputImages,globalIDs,options.inputSignals,globalNo);
                    [avgSpikeTrace signalFeatures signalFeaturesEuclideanTmp signalFeaturesNorm] = getSignalFeatures(matchedSignals);
                    signalFeaturesEuclidean(globalNo,1) = signalFeaturesEuclideanTmp;
                % else
                %     shuffledSignalFeaturesEuclideanMean(globalNo,1) = NaN;
                %     shuffledSignalFeaturesEuclideanStd(globalNo,1) = NaN;
                %     signalFeaturesEuclidean(globalNo,1) = NaN;
                % end

                subplot(guiRow,guiCol,1:2)

                    imagesc(signalFeaturesNorm')
                    % add labels for each feature
                    numYTicks = size(signalFeatures,2);
                    L = get(gca,'YLim');
                    set(gca,'YTick',0:1:numYTicks*2)
                    set(gca,'YTickLabel',{'','signalSnr','numOfPeaks','slopeRatio','avgFwhm','avgPeakAmplitude'})

                    % display txt in the boxes
                    sizeX = size(signalFeaturesNorm);
                    idxX = 1:(sizeX(1)*sizeX(2));
                    [I,J] = ind2sub(sizeX,idxX);
                    % round values
                    txtVals = round(signalFeatures .* 10^3) ./ 10^3;
                    text(I,J,num2str(txtVals(:)),'HorizontalAlignment','center')
                    title([num2str(globalNo) '/' num2str(nGlobals) ' globalID list: ' num2str(globalIDs(globalNo,:))])
                    box off;
                    colormap(gca,[customColormap([])]);

                subplot(guiRow,guiCol,4)
                    % title('example trace')
                    peakROI = [-40:40];
                    % set(gca,'ColorOrder',copper(nMatchedIDs));
                    plot(peakROI, avgSpikeTrace');
                    box off;
                    % axis off;
                    % add in zero line
                    hold on;
                    xval = 0;
                    x=[xval,xval];
                    y=[min(avgSpikeTrace(:)) max(avgSpikeTrace(:))];
                    h = plot(x,y,'k'); box off;
                    % uistack(h,'bottom');

                    legend(matchIDNames,'Location','northeast')

                    % xval = 0;
                    % x=[xval,xval];
                    % y=[min(avgSpikeTrace(:)) max(avgSpikeTrace(:))];
                    % h = plot(x,y,'r'); box off;
                    % uistack(h,'bottom');

                    hold off;
                    title(['euclidean distance: ' num2str(signalFeaturesEuclidean(globalNo)) ' shuffled: ' num2str(shuffledSignalFeaturesEuclideanMean(globalNo))])
                % imagesc([0:0.1:1]); colorbar
                % title('reference for main plot')

                subplot(guiRow,guiCol,[5 6])
                    plotSignalsGraph(matchedSignals,'newAxisColorOrder','');
                    legend(matchIDNames)
            end

            % setup the next subplot
            subplot(guiRow,guiCol,3)
            [groupImages matchedSignals] = getGlobalData(inputImages,globalIDs,options.inputSignals,globalNo);
            % groupVector = 1:length(inputImages);
            % [groupedImages] = groupImagesByColor(thresholdImages(groupImages,'binary',1),[]);
            % matchedObjMaps(:,:,globalNo) = createObjMap(groupedImages);
            % imagesc(matchedObjMaps(:,:,globalNo));
            imagesc(squeeze(nansum(thresholdImages(groupImages,'binary',1,'threshold',0.4),3))/nMatchedIDs*100);
            colormap(gca,[0 0 0;customColormap([])]);
            title('heatmap of percent overlap object maps')
            colorbar
            axis equal tight;
            zoom on;
            box off

            ciapkg.overloaded.suptitle(['globalID ' num2str(globalNo) '/' num2str(nGlobals) '   f:finish    left/right: forward/back'])

            set(gcf,'currentch','3');
            % keyIn = get(gcf,'CurrentCharacter');
            keyIn = '3';
            while strcmp(keyIn,'3')
                keyIn = get(gcf,'CurrentCharacter');
                % if frameNo==frameNoMax
                %     frameNo = 1;
                % end
                pause(1/options.fps);
                % frameNo = frameNo + 1;
                % writeVideo(writerObj,getframe(mainFig));
            end

            reply = double(keyIn);
            figure(1929)
            set(gcf,'currentch','3');

            % [x y reply] = ginput(1);
            % reply = 29;
            if isequal(reply, 102)
                % return;
                saveData = 1;
            elseif isequal(reply, 28)
                % go back, left
                directionOfNextChoice=-1;
            elseif isequal(reply, 29)
                % go forward, right
                directionOfNextChoice=1;
            end
            % loop if user gets to either end
            globalNo=globalNo+directionOfNextChoice;
            if globalNo<=0 globalNo = nGlobals; end
            % if globalNo>nGlobals globalNo = 1; end
            if globalNo>nGlobals saveData = 1; end

            clear groupImages
            % if mod(i,7)==0|i==nGlobals
            %     reverseStr = cmdWaitbar(i,nGlobals,reverseStr,'inputStr','creating cell maps');
            % end

        end
        % playMovie(cellmap);
        euclideanStruct.shuffledSignalFeaturesEuclideanMean = shuffledSignalFeaturesEuclideanMean;
        euclideanStruct.shuffledSignalFeaturesEuclideanStd = shuffledSignalFeaturesEuclideanStd;
        euclideanStruct.signalFeaturesEuclidean = signalFeaturesEuclidean;

    catch err
        display(repmat('@',1,7))
        disp(getReport(err,'extended','hyperlinks','on'));
        display(repmat('@',1,7))
    end
end

function [groupImages matchedSignals] = getGlobalData(inputImages,globalIDs,inputSignals,globalNo)
    matchIDList = globalIDs(globalNo,:);
    matchIDIdx = matchIDList~=0;
    nMatchGlobalIDs = sum(matchIDIdx);
    if ~isempty(inputSignals)
        % get max length
        [nrows, ncols] = cellfun(@size, inputSignals);
        maxCols = max(ncols);
        matchedSignals = zeros(length(inputSignals),maxCols);
    end

    idxNo = 1;
    for j=1:length(inputImages)
        iIdx = globalIDs(globalNo,j);
        if iIdx==0
            nullImage = NaN(size(squeeze(inputImages{1}(:,:,1))));
            nullImage(1,1) = 1;
            groupImages(:,:,j) = nullImage;
        else
            % size(inputImages{j})
            % iIdx
            try
                groupImages(:,:,j) = squeeze(inputImages{j}(:,:,iIdx));
            catch
                display([num2str(j) ',' num2str(iIdx)])
            end
            if ~isempty(inputSignals)
                iSignal = inputSignals{j}(iIdx,:);
                matchedSignals(j,1:length(iSignal)) = iSignal;
            end
            idxNo = idxNo + 1;
        end
    end
end

function [avgSpikeTrace signalFeatures signalFeaturesEuclidean signalFeaturesNorm] = getSignalFeatures(matchedSignals)

    % get the SNR for traces
    [signalSnr ~] = computeSignalSnr(matchedSignals,'waitbarOn',0);
    % get the peak statistics
    [peakOutputStat] = computePeakStatistics(matchedSignals,'waitbarOn',0);
    slopeRatio = peakOutputStat.slopeRatio;
    avgFwhm = peakOutputStat.avgFwhm;
    avgPeakAmplitude = peakOutputStat.avgPeakAmplitude;
    avgSpikeTrace = peakOutputStat.avgSpikeTrace;
    % get the number of spikes
    [signalPeaks, signalPeakIdx] = computeSignalPeaks(matchedSignals,'makeSummaryPlots',0,'waitbarOn',0);
    numOfPeaks = sum(signalPeaks,2);
    signalFeatures = horzcat(signalSnr(:),...
        numOfPeaks(:),...
        slopeRatio(:),...
        avgFwhm(:),...
        avgPeakAmplitude(:));

    % signalFeatures(find(isnan(signalFeatures))) = 0;
    % normalize feature matrix
    X = signalFeatures;
    [m n] = size(signalFeatures);
    xMax = repmat(nanmax(X),m,1);
    xMin = repmat(nanmin(X),m,1);
    signalFeaturesNorm = (X - xMin)./(xMax - xMin);

    % compute pairwise distance
    % pdist(signalFeaturesNorm')
    % signalFeaturesEuclidean = nanmean(pdist(signalFeatures));
    signalFeaturesEuclidean = nanmean(pdist(signalFeaturesNorm));
end


function Obj = SOFAgetConventions(sofaconventions,flags,version)
%SOFAgetConventions - Get an empty SOFA object for a given convention
%   Usage: List = SOFAgetConventions()
%          Obj = SOFAgetConventions(conv)
%
%    List = SOFAgetConventions() returns a list with supported conventions.
%
%    Obj = SOFAgetConventions(conv) returns a SOFA object Obj
%    with the predefined mandatory metadata and data for the SOFA convention conv.
%    Obj is empty if conv is not supported. The latest version of the convention
%    conv is provided.
%
%    Obj = SOFAgetConventions(conv, flags) returns only selected
%    metadata with flags encoding the following:
%      'm' : mandatory metadata only (default)
%      'r' : readonly metadata only
%      'a' : all metadata
%
%    SOFAgetConventions(conv, ver) returns the version ver of the convention conv.
%
%    SOFAgetConventions(conv, flag, ver) returns the version ver and selected
%    metadata of the convention conv.

% #Author: Piotr Majdak
% #Author: Michael Mihocic: doc & header documentation updated (28.10.2021)
%
% SOFA Toolbox - function SOFAgetConventions
% Copyright (C) Acoustics Research Institute - Austrian Academy of Sciences
% Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European Commission - subsequent versions of the EUPL (the "License")
% You may not use this work except in compliance with the License.
% You may obtain a copy of the License at: https://joinup.ec.europa.eu/software/page/eupl
% Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
% See the License for the specific language governing  permissions and limitations under the License.

%% Cache
persistent AllObj;

%% If flags not provided, return the conventions with all metadata
if ~exist('version','var')
  if ~exist('flags','var')
    flags='m'; % no flags, no version --> flags = a, version = any.
  else
    switch flags % no version, check if flags is flags or version
      case {'a', 'm', 'r'}
        % flags is flags --> do nothing
      otherwise
        version=flags;  % flags is version --> copy to version
        flags='m';
    end
  end
end

%% If sofaconventions not provided, return the list with supported conventions
if ~exist('sofaconventions','var')
  p=mfilename('fullpath');
  d=dir([p(1:length(p)-length(mfilename)) 'conventions' filesep '*_m_*.mat']);
  Obj={};
  for ii=1:length(d)
    dn=d(ii).name;
    idx=strfind(dn,'_');
    Obj{ii,1}=dn(1:idx(1)-1);
  end
  Obj=unique(Obj);
  AllObj=[];
	return;
end

%% Load cached object

if isfield(AllObj,flags)
  if isfield(AllObj.(flags).Obj,'GLOBAL_SOFAConventions')
    if strcmp(AllObj.(flags).Obj.GLOBAL_SOFAConventions,sofaconventions)
      if ~exist('version','var')
        Obj=AllObj.(flags).Obj; % return cached convention object
      else
        if strcmp(AllObj.(flags).Obj.GLOBAL_SOFAConventionsVersion,version)
          Obj=AllObj.(flags).Obj; % return cached convention object
        end
      end
    end
  end
end

if ~exist('Obj','var')
        % cached object not loaded yet
  p=mfilename('fullpath');
  if exist('version','var')
      % load a specific version but do not store in the cache
    if ~isempty(dir([p(1:length(p)-length(mfilename)) 'conventions' filesep sofaconventions '_' flags '_' version '.mat']))
      load([p(1:length(p)-length(mfilename)) 'conventions' filesep sofaconventions '_' flags '_' version '.mat']);
    else
      error(['Convention ' sofaconventions ' with the version ' version ' not found.']);
    end
  else
    allver=dir(fullfile(p(1:length(p)-length(mfilename)), 'conventions' , [sofaconventions '_' flags '_*.mat']));
    if isempty(allver)
      warning(['Convention ' sofaconventions ' not found.']');
      Obj=[];
    else
      vermax='0.0';
      idxmax=0;
      for ii=1:length(allver)
        x=load([p(1:length(p)-length(mfilename)) 'conventions' filesep allver(ii).name]);
        if compareversions(x.Obj.GLOBAL_SOFAConventionsVersion,vermax)>0
          vermax=x.Obj.GLOBAL_SOFAConventionsVersion;
          idxmax=ii;
        end
      end
        % store in cache
      AllObj.(flags)=load([p(1:length(p)-length(mfilename)) 'conventions' filesep allver(idxmax).name]);
      Obj=AllObj.(flags).Obj; % return the cached version
    end
  end
end

%% Overwrite some special fields
if isfield(Obj,'GLOBAL_DateCreated'), Obj.GLOBAL_DateCreated=datestr(now,SOFAdefinitions('dateFormat')); end
if isfield(Obj,'GLOBAL_APIVersion'), Obj.GLOBAL_APIVersion=SOFAgetVersion; end
if isfield(Obj,'GLOBAL_APIName'), Obj.GLOBAL_APIName=SOFAdefinitions('APIName'); end

end

function res=compareversions(ver1, ver2)

  [~,maj1,min1]=fileparts(ver1);
  [~,maj2,min2]=fileparts(ver2);
  maj1=str2num(maj1);
  min1=str2num(min1(2:end));
  maj2=str2num(maj2);
  min2=str2num(min2(2:end));
  if maj1>maj2
     res=1;   % ver1 > ver2
  elseif maj1==maj2 && min1>min2
     res=1;   % ver1 > ver2
  elseif maj2==maj2 && min1==min2
     res=0;   % ver1 == ver2
  else
     res=-1;  % ver < ver2
  end
end


%SUBSASGN Subscript assignment overload for mappings
%
% This routine enables constructs like W.DATA = {DATA1, DATA2},
% i.e. the direct change of mapping fields.


classdef ThrottleModelEnum < matlab.mixin.SetGet
    %ThrottleModelEnum Summary of this class goes here
    %   Detailed explanation goes here

    enumeration
        PolyModel('Polynominal Model','ThrottlePolyModel',"The vehicle throttle varies as a function of a second degree polynomial.  Can set constant throttle here too.");
        T2WModel('Thrust To Weight Model','T2WThrottleModel',"The vehicle will set the throttle to maintain a fixed thrust to weight ratio.");
    end

    properties
        nameStr char = '';
        classNameStr char = '';
        desc(1,1) string
    end

    methods
        function obj = ThrottleModelEnum(nameStr,classNameStr,desc)
            obj.nameStr = nameStr;
            obj.classNameStr = classNameStr;
            obj.desc = desc;
        end
    end

    methods(Static)
        function [throttleModelNameStrs, m] = getThrottleModelTypeNameStrs()
            [m,~] = enumeration('ThrottleModelEnum');

            throttleModelNameStrs = {};
            for(i=1:length(m)) %#ok<*NO4LP>
                throttleModelNameStrs{end+1} = m(i).nameStr; %#ok<AGROW>
            end
        end

        function ind = getIndOfListboxStrsForThrottleModel(throttleModel)
            [m,~] = enumeration('ThrottleModelEnum');
            inputClass = class(throttleModel);

            ind = -1;
            for(i=1:length(m))
                if(strcmpi(m(i).classNameStr,inputClass))
                    ind = i;
                    break;
                end
            end
        end

        function [enum, ind] = getEnumForListboxStr(nameStr)
            m = enumeration('ThrottleModelEnum');
            ind = find(ismember({m.nameStr},nameStr),1,'first');
            enum = m(ind);
        end
    end
end


%ZIPLOAD   Load compressed data file created with ZIPSAVE
%
%	[data] = zipload( filename )
%	filename: string variable that contains the name of the
%	compressed file (do not include '.zip' extension)
%	Use only with files created with 'zipsave'
%	pkzip25.exe has to be in the matlab path. This file is a compression utility
%	made by Pkware, Inc. It can be dowloaded from:	http://www.pkware.com
%	Or directly from ftp://ftp.pkware.com/pk250c32.exe, for the Windows 95/NT version.
%	This function was tested using 'PKZIP 2.50 Command Line for Windows 9x/NT'
%	It is important to use version 2.5 of the utility. Otherwise the command line below
%	has to be changed to include the proper options of the compression utility you
%	wish to use.
%	This function was tested in MATLAB Version 5.3 under Windows NT.
%	Fernando A. Brucher - May/25/1999
%
%	Example:
%		[loadedData] = zipload('testfile');
%--------------------------------------------------------------------

function [data] = zipload( filename )

%--- Decompress data file by calling pkzip (comand line command) ---
% 	Options used:
%	'extract' = decompress file
%	'silent' = no console output
%	'over=all' = overwrite files

%eval( ['!pkzip25 -extract -silent -over=all ', filename, '.zip'] )
eval( ['!pkzip25 -extract -silent -over=all ', filename, '.zip'] )

%--- Load data from decompressed file ---
%	try, catch takes care of cases when pkzip fails to decompress a
%	valid matlab format file

try
   tmpStruc = load( filename );
   data = tmpStruc.data;
catch, return, end

%--- Delete decompressed file ---

delete( [filename,'.mat'] )


classdef nonEllipsoid < referenceEllipsoid
    % used when coordinate system is cartesian
    methods
        function obj = nonEllipsoid(varargin)
            obj@referenceEllipsoid();
            p = inputParser;
            p.addOptional('Name','XYZ');
            p.addOptional('LengthUnit','meter');
            p.parse(varargin{:});
            obj.LengthUnit = p.Results.LengthUnit;
            obj.Name = p.Results.Name;
        end
    end

end


function f = make(varargin)
%MAKE   Constructor shortcut for TRIGTECH objects.
%   For TRIGTECH methods implemented at the TRIGTECH level, it is not possible
%   to call the class constructor file corresponding to a TRIGTECH object
%   directly. F = MAKE(VARARGIN) allows us to get around this and construct a
%   TRIGTECH.
%
% See also TRIGTECH.

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

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Developer note: This is essentially a "factory method" in the sense of
% _Design Patterns_ by Gamma, Helm, Johnson, and Vlissides.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

f = trigtech(varargin{:});

end


function display( x )
long = ~isequal(get(0,'FormatSpacing'),'compact');
if long, disp( ' ' ); end
disp(x);
if long, disp( ' ' ); end

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


function w2 = waveform_remove_empty(w);
%WAVEFORM_REMOVE_EMPTY remove empty waveform objects from a vector of waveform objects
%   Note that the opposite function, of expanding empty waveform objects can largely
%   be achieved with waveform/pad

%   Glenn Thompson
    e = 1;
    for c=1:length(w)
            nsamp = get(w(c), 'data_length');
            if nsamp > 1 % 0 means blank waveform object, 1 means corrupt waveform object
                    w2(e) = w(c);
                    e = e + 1;
            end
    end
    if e==1
            w2 = [];
    end
end


classdef BodyPropagationTypeEnum < matlab.mixin.SetGet
    %BodyPropagationTypeEnum Summary of this class goes here
    %   Detailed explanation goes here

    enumeration
        TwoBody('Analytic Two Body')
        Numerical('Numerical Integration');
    end

    properties
        name char = '';
    end

    methods
        function obj = BodyPropagationTypeEnum(name)
            obj.name = name;
        end
    end

    methods(Static)
        function listBoxStr = getListBoxStr()
            m = enumeration('BodyPropagationTypeEnum');
            [~,I] = sort({m.name});
            listBoxStr = {m(I).name};
        end

        function [ind, enum] = getIndForName(name)
            m = enumeration('BodyPropagationTypeEnum');
            [~,I] = sort({m.name});
            m = m(I);
            ind = find(ismember({m.name},name),1,'first');
            enum = m(ind);
        end

        function [enum, ind] = getEnumForListboxStr(nameStr)
            m = enumeration('BodyPropagationTypeEnum');
            ind = find(ismember({m.name},nameStr),1,'first');
            enum = m(ind);
        end
    end
end