function benchNewAlg(benchdir,pres,alg)
% benchNewAlg(benchdir,pres,alg)
%
% benchdir = root directory of benchmark results
% pres = presentation, 'gray' or 'color'
% alg = algorithm directory containing *.bmp pb files
%
% Things you should do before running this script:
%
% (1) Download and untar BSDS images and human data.
% (2) Download the code (you must have done that if you're
%     reading this!
% (3) Edit Dataset/bsdsRoot.m script to point to your BSDS
%     directory.
% (4) Run 'gmake install' from the code directory.
% (5) Create a benchmark directory e.g. 'bench' and untar the human
%     benchmark data into it.  That should create 'bench/gray'
%     and 'bench/color' sub-directories, each with a 'human'
%     subdirectory.
% (6) Put your algorithm's pb files into the directory under the
%     proper 'gray' or 'color' directory.
% (7) Create the name.txt and about.html files in your algorithm
%     directory.

algdir = fullfile(benchdir,pres,alg);

fprintf(2,'Benchmarking algorithm (takes a while!)...\n');
boundaryBench(algdir,pres);

fprintf(2,'Generating benchmark graphs for this algorithm...\n');
boundaryBenchGraphs(algdir);

fprintf(2,'Generating benchmark graphs to compare algorithms...\n');
boundaryBenchGraphsMulti(benchdir);

fprintf(2,'Generating benchmark web pages...\n');
boundaryBenchHtml(benchdir);


function createSBMLFiles(refinedFolder, sbmlFolder)
% Creates SBML files for the created refined reconstructions. This may be
% time-consuming.
%
% USAGE
%   createTestSBMLFiles(refinedFolder, sbmlFolder)
%
% INPUTS
% refinedFolder            Folder with refined COBRA models generated by
%                          the refinement pipeline
% sbmlFolder               Folder where SBML files, if desired, will be saved
%
% AUTHOR:
%       - Almut Heinken, 03/2020

dInfo = dir(sbmlFolder);
alreadyCreated={dInfo.name};
alreadyCreated=alreadyCreated';
alreadyCreated(~contains(alreadyCreated(:,1),'.xml'),:)=[];
alreadyCreated=strrep(alreadyCreated,'.xml','.mat');

dInfo = dir(refinedFolder);
modelList={dInfo.name};
modelList=modelList';
modelList(find(strcmp(modelList(:,1),'.')),:)=[];
modelList(find(strcmp(modelList(:,1),'..')),:)=[];

for i=1:length(modelList)
    if ~any(strcmp(modelList{i},alreadyCreated))
    model = readCbModel([refinedFolder filesep modelList{i}]);
    writeCbModel(model, 'format', 'sbml', 'fileName', [sbmlFolder filesep strrep(modelList{i},'.mat','')]);
    end
end

end


%==========================================================================
% Walks through a tree structure data. Whenever it finds a structure, which
% have field named 'import' it assumes that in that field is a cell array
% and merges all structures found in that array. Parameter verb is used for
% debugging purposes.
%==========================================================================
function result = mergeimports(data, verb)
    if ~exist('verb','var')
        verb = 0;
    end;
    result = recurse(data, 0, [], verb);
end

%--------------------------------------------------------------------------
% Recursion dispatcher, calls appropriate method for cell/structure or
% displays data if the parameter data is not of mentioned type.
%   addit ... for possible future use, now unused
%   verb  ... for debugging
%
function result = recurse(data, level, addit, verb)
    indent = repmat(' | ',1,level); % for debugging
    if iscell(data)
        result = iter_cell(data, level, addit, verb);
    elseif isstruct(data)
        result = iter_struct(data, level, addit, verb);
    else
        if any(verb == 1) % for debugging
            fprintf([indent,'Some data: ']);
            disp(data);
        end;
        result = data;
    end;
end

%--------------------------------------------------------------------------
% Walks through a cell array and calls recurse on every field.
%   data        ... Assumed to be a cell. Data to be walked.
%   level       ... Level in the tree, root has zero.
%   addit, verb ... for debugging
%
function result = iter_cell(data, level, addit, verb)
    indent = repmat(' | ',1,level); % for debugging
    result = {};
    if any(verb == 1); fprintf([indent,'cell {\n']); end; % for debugging
    for i = 1:length(data)
        itemcontent = recurse(data{i}, level + 1, addit, verb);
        result{end + 1} = itemcontent;
    end;
    if any(verb == 1); fprintf([indent,'} cell\n']); end; % for debugging
end

%--------------------------------------------------------------------------
% Walks through a struct and calls recurse on every field. If there is a
% field called 'import' it calls process_import_field on its content. Then
% merges processed import with the rest of the structure. Meaning of all
% parameters is similar to those of iter_cell.
%
function result = iter_struct(data, level, addit, verb)
    indent = repmat(' | ',1,level); % for debugging
    result = struct();
    collected_imports = {};
    if any(verb == 1); fprintf([indent,'struct {\n']); end; % for debugging
    for i = fields(data)'
        fld = char(i);
        if any(verb == 1); fprintf([indent,' +-field ',fld,':\n']); end; % for debugging
        result.(fld) = recurse(data.(fld), level + 1, addit, verb);
        % Tree back-pass - all potential underlying imports were processed,
        % so process import here, if needed.
        if isequal(fld, 'import')
            processed_import = process_import_field(result.(fld));
            result = rmfield(result, 'import');
            if isstruct(processed_import)
                collected_imports{end+1} = processed_import;
                % It is maybe useless to collect imports to the array since
                % there can be only one field named 'import' per structure.
                % collected_imports is proposed to be changed to a simple
                % variable.
            else
                % One of imports was not a struct. In following versions it
                % probably won't be an error and will merge to a cell with
                % structs.
                disp(processed_import);
                error('Expected struct, otherwise it cannot be merged with the rest.');
            end;
        end;
    end;
    for i = 1:length(collected_imports)
        result = merge_struct(result, collected_imports{i}, {}, 'deep');
    end;
    if any(verb == 1); fprintf([indent,'} struct\n']); end; % for debugging
end

%--------------------------------------------------------------------------
% Walks through the data parameter, which is assumed to be a cell. Merges
% all structures in that cell and returns them as a struct or possibly as a
% cell of merged struct and unmeregeable data.
%
function result = process_import_field(data)
    if iscell(data)
        merged_structs = struct();
        collected_nonstruct = {};
        for i = 1:length(data)
            if isstruct(data{i})
                merged_structs = merge_struct(merged_structs, data{i}, {}, 'deep');
            else
                collected_nonstruct{end+1} = data{i};
            end;
        end;
        if isempty(collected_nonstruct)
            result = merged_structs;
        elseif isempty(merged_structs)
            result = collected_nonstruct;
        else
            result = {merged_structs; collected_nonstruct};
        end;
    else
        % For clarity and simplicity, the whole transformation is done so
        % that every import field in a struct is cell array even there is
        % only one object to be imported.
        error('BUG: import field should always contain a cell.');
    end;
end

%==========================================================================


function opts = getGlobalDefaults()
% Returns a structure with default option values for Manopt.
%
% function opts = getGlobalDefaults()
%
% Returns a structure opts containing the global default options such as
% verbosity level etc. Typically, global defaults are overwritten by solver
% defaults, which are in turn overwritten by user-specified options.
% See the online Manopt documentation for details on options.
%
% See also: mergeOptions

% This file is part of Manopt: www.manopt.org.
% Original author: Nicolas Boumal, Dec. 30, 2012.
% Contributors:
% Change log:

    % There should be no reason to modify this file.
    % For better compatibility with future Manopt versions,
    % use the options structure of solvers.
    %
    % Really: don't modify it.

    % Verbosity level: 0 is no output at all. The higher the verbosity, the
    % more info is printed / displayed during solver execution.
    opts.verbosity = 3;

    % If debug is set to true, additional computations may be performed and
    % debugging information is outputed during solver execution.
    opts.debug = false;

    % Maximum number of store structures to store. If set to 0, caching
    % capabilities are not disabled, but the cache will be emptied at each
    % iteration of iterative solvers (more specifically: every time the
    % solver calls to purge the storedb).
    opts.storedepth = 20;

    % Maximum amount of time a solver may execute, in seconds.
    opts.maxtime = inf;

end


%  INTERNAL FUNCTION: Set initial conditions to the order of the solution of
%  the model (going from the Alphabetical order)
%
%


function bst_startup(BrainstormHomeDir, GuiLevel, BrainstormDbDir)
% BST_STARTUP: Start a new Brainstorm Session.
%
% USAGE:  bst_startup(BrainstormHomeDir, GuiLevel=1, BrainstormDbDir=[])
%
% INPUTS:
%    - BrainstormHomeDir : Path to the brainstorm3 folder
%    - GuiLevel          : -1=server, 0=nogui, 1=normal, 2=autopilot
%    - BrainstormDbDir   : Database folder to use by default in this session

% @=============================================================================
% 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: Sylvain Baillet, John C. Mosher, 1999
%          Francois Tadel, 2008-2023

%% ===== MATLAB CHECK =====
% Parse inputs
if (nargin < 3) || isempty(BrainstormDbDir)
    BrainstormDbDir = [];
end
% If version is too old
MatlabVersion = bst_get('MatlabVersion');
if (MatlabVersion < 701)
    error('Brainstorm needs a version of Matlab >= 7.1');
end
% Is Matlab running (if not it is a compiled version)
isCompiled = bst_iscompiled();
% Compiled version: Force system look and feel
if isCompiled
    try
        javax.swing.UIManager.setLookAndFeel(javax.swing.UIManager.getSystemLookAndFeelClassName());
    catch
        % Whatever....
    end
end

% Startup message
disp(' ');
disp('BST> Starting Brainstorm:');
disp('BST> =================================');

%% ===== BRAINSTORM VERIFICATIONS =====
% Check that no interface is already running
if isappdata(0, 'BrainstormRunning')
    disp('BST> Brainstorm is already running. Restarting...');
    bst_exit();
end
% Initialize shared structure
global GlobalData;
GlobalData = db_template('GlobalData');
GlobalData.Program.GuiLevel = GuiLevel;
GlobalData.DataBase.LastSavedTime = tic();   % Save the current time, to know when to save the database
% Save the software home directory
bst_set('BrainstormHomeDir', BrainstormHomeDir);
% Debugging: show path in compiled application
if isCompiled
    disp(['BST> BrainstormHomeDir = ' BrainstormHomeDir]);
end
% Test for headless mode
if (GuiLevel >= 0) && (java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment.isHeadless() || java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment.isHeadlessInstance())
    disp(' ');
    error(['Cannot create graphic interface.' 10 'If running Brainstorm in headless mode on a distant server, run "brainstorm server".']);
end
% Splash screen
if (GuiLevel == 1)
    bst_splash('show');
end

% === BRAINSTORM VERSION ===
try
    % Get doc folder
    docDir = bst_get('BrainstormDocDir');
    % Open "version.txt"
    fid = fopen(bst_fullfile(docDir, 'version.txt'), 'rt');
    % Get program name
    Name = fgetl(fid);
    Name = Name(3:end); %trim the comment
    % Get version and date
    strVer = fgetl(fid);
    cellVer = textscan(strVer(3:end), 'v. %s %s');
    Version = cellVer{1}{1};
    Release = cellVer{1}{1}(3:end);
    Date = cellVer{2}{1}(2:end-1);
    % Try to get GIT commit
    Commit = fgetl(fid);
    if ischar(Commit) && (length(Commit) >= 40)
        Commit = Commit(10:end);
    else
        Commit = [];
    end
    % Close file
    fclose(fid);
catch
    Name = 'Brainstorm';
    Version = '?';
    Release = '??????';
    Date    = '?';
    Commit  = [];
end
% If the commit is not available from the version.txt file, try to get it from the .git folder (if cloned from github)
if isempty(Commit)
    gitMaster = bst_fullfile(BrainstormHomeDir, '.git', 'refs', 'heads', 'master');
    if exist(gitMaster, 'file')
        fid = fopen(gitMaster, 'rt');
        if (fid >= 0)
            strGit = fgetl(fid);
            if ischar(strGit) && (length(strGit) >= 30)
                Commit = strGit;
            end
            fclose(fid);
        end
    end
end
% Save version in matlab preferences
bstVersion = struct('Name',    Name, ...
                    'Version', Version, ...
                    'Release', Release, ...
                    'Date',    Date, ...
                    'Commit',  Commit);
bst_set('Version', bstVersion);
% Display version number
disp(['BST> Version: ' Date ]);
% Get release date
localRel.year  = str2num(Release(1:2));
localRel.month = str2num(Release(3:4));
localRel.day   = str2num(Release(5:6));

% Check Matlab version
if (MatlabVersion <= 803)
    disp('BST> Warning: For better graphics, use Matlab >= 2014b');
end

%% ===== FORCE COMPILATION OF SOME INTERFACE FILES =====
if (GuiLevel == 1)
    disp('BST> Compiling main interface files...');
    tree_callbacks();
    bst_figures();
    figure_topo();
    figure_3d();
    figure_mri();
    figure_timeseries();
    figure_timefreq();
    bst_colormaps();
    bst_memory();
    bst_navigator();
end

%% ===== EMPTY REPORTS DIRECTORY =====
% Get reports directory
reportsDir = bst_get('UserReportsDir');
% If directory exists
if isdir(reportsDir)
    disp('BST> Deleting old process reports...');
    % List contents of folder
    listDir = dir(bst_fullfile(reportsDir, 'report_*.mat'));
    % If there are files in this folder (older versions of Matlab do not have this datenum field)
    if ~isempty(listDir) && isfield(listDir, 'datenum')
        % Get files that are older than 15 days
        iOldFiles = find(now() - [listDir.datenum] > 15);
        % Delete contents of directory
        for iFile = 1:length(iOldFiles)
            file_delete(bst_fullfile(reportsDir, listDir(iOldFiles(iFile)).name), 1);
        end
    end
end

%% ===== LOAD CONFIG FILE =====
disp('BST> Loading configuration file...');
% Get user database file : brainstorm.mat
dbFile = bst_get('BrainstormDbFile');
% Current DB version
CurrentDbVersion = 5.02;
% Get default colormaps list
sDefColormaps = bst_colormaps('Initialize');
isDbLoaded = 0;
% If file exists: load it
if file_exist(dbFile)
    % Load database file
    try
        bstOptions = load(dbFile);
    catch
        bst_splash('hide');
        java_dialog('msgbox', [...
            'Error: The database file was not saved properly.' 10 10 ...
            'Possible reason: Your hard drive is full or your quota exceeded.' 10 ...
            'Your user options are lost, but your database is probably safe:' 10 ...
            ' - Try to delete files in your home folder' 10 ...
            ' - Change the temporary folder in the Brainstorm preferences' 10 ...
            ' - Import again your database folder (File > Import database).'], 'Database error');
        bstOptions = [];
    end
    % Invalid structure read from dbFile
    if any(~isfield(bstOptions, {'iProtocol', 'ProtocolsListInfo', 'ProtocolsListSubjects', 'ProtocolsListStudies', 'BrainStormDbDir'}))
        disp(['BST> Warning: Ignoring corrupted options file: ' dbFile]);
        bstOptions = [];
    end
else
    bstOptions = [];
end
% Copy saved preferences to current instance
if ~isempty(bstOptions)
    % Add its contents in root app data
    if isfield(bstOptions, 'iProtocol')
        GlobalData.DataBase.iProtocol          = bstOptions.iProtocol;
        GlobalData.DataBase.ProtocolInfo       = bstOptions.ProtocolsListInfo;
        GlobalData.DataBase.ProtocolSubjects   = bstOptions.ProtocolsListSubjects;
        GlobalData.DataBase.ProtocolStudies    = bstOptions.ProtocolsListStudies;
        GlobalData.DataBase.BrainstormDbDir    = bstOptions.BrainStormDbDir;
        GlobalData.DataBase.isProtocolModified = zeros(1, length(bstOptions.ProtocolsListInfo));
        if isfield(bstOptions, 'DbVersion') && ~isempty(bstOptions.DbVersion)
            GlobalData.DataBase.DbVersion = bstOptions.DbVersion;
        end
        if isfield(bstOptions, 'isProtocolLoaded') && ~isempty(bstOptions.isProtocolLoaded)
            GlobalData.DataBase.isProtocolLoaded = bstOptions.isProtocolLoaded;
        else
            GlobalData.DataBase.isProtocolLoaded = ones(1, length(bstOptions.ProtocolsListInfo));
        end
        isDbLoaded = 1;
    end
    % Get saved colormaps
    if isfield(bstOptions, 'Colormaps') && isstruct(bstOptions.Colormaps)
        fNames = fieldnames(sDefColormaps);
        if (length(fieldnames(bstOptions.Colormaps)) ~= length(fNames)) || ~isequal(fieldnames(bstOptions.Colormaps.(fNames{1})), fieldnames(sDefColormaps.(fNames{1})))
            disp('BST> Colormap structure was updated. Fixing...');
        else
            GlobalData.Colormaps = bstOptions.Colormaps;
        end
    end
    % Clone control
    if isfield(bstOptions, 'CloneLock') && ~isempty(bstOptions.CloneLock)
        GlobalData.Program.CloneLock = bstOptions.CloneLock;
    end
    % Get saved preferences
    if isfield(bstOptions, 'Preferences') && isstruct(bstOptions.Preferences)
        GlobalData.Preferences = struct_copy_fields(GlobalData.Preferences, bstOptions.Preferences, 0);
    end
    % Get saved montages
    if isfield(bstOptions, 'ChannelMontages') && isstruct(bstOptions.ChannelMontages) && ...
            all(isfield(bstOptions.ChannelMontages, fieldnames(GlobalData.ChannelMontages))) && ...
            (length(bstOptions.ChannelMontages.Montages) > 20)
        GlobalData.ChannelMontages = bstOptions.ChannelMontages;
        % Reset butterfly plot selection
        panel_montage('SetCurrentMontage', 'MEG', []);
    end
    % Get saved process pipelines
    if isfield(bstOptions, 'Pipelines') && isstruct(bstOptions.Pipelines) && ...
            all(isfield(bstOptions.Pipelines, {'Name', 'Processes'}))
       GlobalData.Processes.Pipelines = bstOptions.Pipelines;
    end
    % Get saved searches
    if isfield(bstOptions, 'Searches') && isstruct(bstOptions.Searches) && ...
            all(isfield(bstOptions.Searches, {'Name', 'Search'}))
       GlobalData.DataBase.Searches.All = bstOptions.Searches;
    end
    % Reset current search filter
    if isfield(GlobalData.Preferences, 'NodelistOptions') && isfield(GlobalData.Preferences.NodelistOptions, 'String') && ~isempty(GlobalData.Preferences.NodelistOptions.String)
        GlobalData.Preferences.NodelistOptions.String = '';
    end
    % Reset previous exploration mode
    if isfield(GlobalData.Preferences, 'Layout') && isfield(GlobalData.Preferences.Layout, 'PreviousExplorationMode')
        GlobalData.Preferences.Layout.PreviousExplorationMode = GlobalData.Preferences.Layout.ExplorationMode;
    end
    % Check database structure for updates
    db_update(CurrentDbVersion);
end
if GlobalData.DataBase.DbVersion == 0
    % Database version is not defined, so it up-to-date
    GlobalData.DataBase.DbVersion = CurrentDbVersion;
end
% Check that Colormaps are defined
if isempty(GlobalData.Colormaps)
    GlobalData.Colormaps = sDefColormaps;
end
% Check that default montages are loaded
if (length(GlobalData.ChannelMontages.Montages) < 5) || any(~ismember({'CTF LF', 'Bad channels', 'Average reference (L -> R)', 'Scalp current density', 'Scalp current density (L -> R)', 'Head distance'}, {GlobalData.ChannelMontages.Montages.Name}))
    disp('BST> Loading default montages...');
    % Load default selections
    panel_montage('LoadDefaultMontages');
end

%% ===== AUTOMATIC UPDATES =====
% Automatic updates disabled: do not check for internet connection
if ~bst_get('AutoUpdates')
    disp('BST> Warning: Automatic updates are disabled.');
    disp('BST> Warning: Make sure your version of Brainstorm is up to date.');
% Matlab is running: check for updates
elseif ~isCompiled && (GuiLevel == 1)
    % Check internect connection
    fprintf(1, 'BST> Checking internet connectivity... ');
    [GlobalData.Program.isInternet, onlineRel] = bst_check_internet();
    % If no internet connection
    if ~GlobalData.Program.isInternet
        disp('failed');
        disp('BST> Could not check for Brainstorm updates.')
    else
        disp('ok');
        % Determine if release is old (local version > 30 days older than online version)
        daysOnline = onlineRel.year*365 + onlineRel.month*30 + onlineRel.day;
        daysLocal  =  localRel.year*365 +  localRel.month*30 +  localRel.day;
        isOld = ((daysOnline - daysLocal) > 30);
        % Display online version number
        if ((daysOnline - daysLocal) > 1)
            strOnline = datestr([2000+onlineRel.year, onlineRel.month, onlineRel.day, 0, 0, 0]);
            disp(['BST> Update available online: ' strOnline '']);
        end
        % Checking version: download if more then one month old
        if isOld
            disp('BST> Your version of brainstorm is old. Update is required.');
            % Check access rights
            if ~file_attrib(bst_fileparts(BrainstormHomeDir, 1), 'w') || ~file_attrib(BrainstormHomeDir, 'w')
                disp('BST> Brainstorm installation folder is read-only. Cannot update...');
            else
                % Hide splash screen
                bst_splash('hide');
                % Update brainstorm
                isUpdated = bst_update(1);
                % If update successful: Matlab or Brainstorm restart
                if isUpdated
                    return
                end
            end
        end
    end
% Check online connectivity
else
    [GlobalData.Program.isInternet, onlineRel] = bst_check_internet();
end

%% ===== START BRAINSTORM GUI =====
% Get screen configuration
GlobalData.Program.ScreenDef = gui_layout('GetScreenClientArea');
% Create main window (skipped in server mode)
if (GuiLevel >= 0)
    disp('BST> Initializing user interface...');
end
gui_initialize();
% Abort if something went wrong
if isempty(GlobalData.Program.GUI)
    return;
end

%% ===== INITIALIZE DATABASE =====
if ~isDbLoaded
    % Initialize structures
    GlobalData.DataBase.iProtocol          = 0;
    GlobalData.DataBase.ProtocolInfo       = repmat(db_template('ProtocolInfo'), 0);
    GlobalData.DataBase.ProtocolSubjects   = repmat(db_template('ProtocolSubjects'), 0);
    GlobalData.DataBase.ProtocolStudies    = repmat(db_template('ProtocolStudies'), 0);
    GlobalData.DataBase.isProtocolLoaded   = [];
    GlobalData.DataBase.isProtocolModified = [];
end

%% ===== CHECK FOR EEGLAB INSTALL =====
fminPath = lower(which('fminsearch'));
if ~isempty(strfind(fminPath, 'eeglab'))
    strProg = 'EEGLAB';
elseif ~isempty(strfind(fminPath, 'spm'))
    strProg = 'SPM';
elseif ~isempty(strfind(fminPath, 'fieldtrip'))
    strProg = 'FieldTrip';
else
    strProg = [];
end
if ~isempty(strProg)
    if (GuiLevel <= 0)
        disp(['BST> Warning: Some ' strProg ' functions shadow Matlab''s standard functions.']);
        disp(['BST> Warning: Please remove ' strProg ' from your Matlab path.']);
    else
        bst_splash('hide');
        java_dialog('warning', [strProg ' is installed on your system and shadows some standard Matlab functions.' 10 ...
                                'Without access to the function fminsearch, Brainstorm will not run properly.' 10 10 ...
                                'Please remove ' strProg ' from your Matlab path and restart Brainstorm.']);
    end
end

%% ===== START OPENGL =====
if (GuiLevel >= 0)
    fprintf(1, 'BST> Starting OpenGL engine... ');
    [isOpenGL, DisableOpenGL] = panel_options('StartOpenGL');
    % If OpenGL cannot be used: display a warning message
    if ~isOpenGL
        disp('BST> Warning: No OpenGL support available for this computer.');
        disp('BST>          Display will be slow and ugly.');
    % If OpenGL is manually disabled
    elseif isOpenGL && (DisableOpenGL == 1)
        disp('BST> Warning: ');
        disp('BST>    * Using this option causes the display to be slow and ugly.');
        disp('BST>    * Select only if you are experiencing serious display bugs with ');
        disp('BST>    * the full hardware acceleration. To edit this option: ');
        disp('BST>    * Menu: File > Set preferences... > Disable OpenGL rendering.');
    end
end

%% ===== LOAD PLUGINS =====
% Get installed plugins
[InstPlugs, AllPlugs] = bst_plugin('GetInstalled');
% Check installed plugins
if ~isempty(InstPlugs)
    % Display the plugins that are using custom installed path
    iPlugCustom = find([InstPlugs.isLoaded] & ~[InstPlugs.isManaged]);
    for iPlug = iPlugCustom
        disp(['BST> Plugin ' InstPlugs(iPlug).Name ': ' InstPlugs(iPlug).Path]);
        if strcmpi(InstPlugs(iPlug).Name, 'spm12') && isempty(strfind(spm('ver'), 'SPM12'))
            disp(['BST> ** WARNING: Installed version is not SPM12: ' spm('ver') ' **']);
        end
    end
    % Load plugins that should be loaded automatically at startup
    iPlugLoad = find([InstPlugs.AutoLoad] & ~[InstPlugs.isLoaded]);
    if ~isempty(iPlugLoad)
        fprintf('BST> Loading plugins... ');
    end
    for iPlug = iPlugLoad
        bst_plugin('Load', InstPlugs(iPlug), 0);
        fprintf([InstPlugs(iPlug).Name, ' ']);
    end
    if ~isempty(iPlugLoad)
        fprintf('\n');
    end
end

%% ===== PARSE PROCESS FOLDER =====
% Parse process folder
disp('BST> Reading process folder...');
panel_process_select('ParseProcessFolder', 1);

%% ===== INSTALL ANATOMY TEMPLATE =====
% Download ICBM152 template if missing (e.g. when cloning from GitHub)
TemplateDir = fullfile(BrainstormHomeDir, 'defaults', 'anatomy', 'ICBM152');
if ~isCompiled && ~exist(TemplateDir, 'file')
    TemplateName = 'ICBM152_2023b';
    isSkipTemplate = 0;
    % Template file
    ZipFile = bst_fullfile(bst_get('UserDefaultsDir'), 'anatomy', [TemplateName '.zip']);
    % If template is not downloaded yet: download it
    if ~exist(ZipFile, 'file')
        disp('BST> Downloading ICBM152 template...');
        % Download file
        errMsg = gui_brainstorm('DownloadFile', ['http://neuroimage.usc.edu/bst/getupdate.php?t=' TemplateName], ZipFile, 'Download template');
        % Error message
        if ~isempty(errMsg)
            disp(['BST> Error: Could not download template: ' errMsg]);
            isSkipTemplate = 1;
        end
    end
    % If the template is available as a zip file
    if ~isSkipTemplate
        disp('BST> Installing ICBM152 template...');
        % Create folder
        mkdir(TemplateDir);
        % URL: Download zip file
        try
            unzip(ZipFile, TemplateDir);
        catch
            disp(['BST> Error: Could not unzip anatomy template: ' lasterr]);
        end
    end
end

%% ===== LICENSE AGREEMENT =====
if (GuiLevel == 1)
    % Number of days to allow as grace period for renewing license
    GRACE = 15;
    % Get previous agreement date (default: current date)
    if isfield(GlobalData, 'Preferences') && isfield(GlobalData.Preferences, 'DateofAgreement') && ~isempty(GlobalData.Preferences.DateofAgreement)
        DateofAgreement = GlobalData.Preferences.DateofAgreement;
    else
        DateofAgreement = datestr(floor(now) - GRACE - 1);
    end
    % Get number of days since last agreement
    DaysSinceAgree = etime(datevec(now),datevec(DateofAgreement));
    DaysSinceAgree = DaysSinceAgree/(60*60*24); % convert seconds to days

    % If user did not agree to Brainstorm license rencently
    if (DaysSinceAgree >= GRACE)
        % Hide splash screen
        bst_splash('hide');
        % Show license agreement panel
        isOk = bst_license();
        % If user did not agree: exit
        if ~isOk
            clear all
            disp('BST> License agreement unsatisfied. Closing Brainstorm...');
            disp('BST> Type ''brainstorm'' to restart Brainstorm.');
            % Release Brainstorm global mutex
            bst_mutex('release', 'Brainstorm');
            return;
        % Else accept validation for 15 days
        else
            disp('BST> License accepted.');
            GlobalData.Preferences.DateofAgreement = datestr(floor(now));
        end
    end
end

%% ===== TEST MEMORY =====
if ispc && (MatlabVersion >= 706) && (GuiLevel == 1)
    try
        % Get Matlab memory
        usermem = memory();
        % Minimum: 1Gb RAM contiguous
        maxsize = round(usermem.MaxPossibleArrayBytes / 1024 / 1024);
        if (maxsize < 1024) && ~bst_get('IgnoreMemoryWarnings')
            disp(sprintf('BST> Warning: Maximum variable size: %d Mb', maxsize));
            % Hide splash screen
            bst_splash('hide');
            % Display warning
            java_dialog('msgbox', ...
                ['Your system reports to be short on memory.' 10 10 ...
                 'The maximum block of memory that Matlab can allocate is: ' num2str(maxsize) ' Mb.' 10 ...
                 'The recommended minimum for running Brainstorm is 1024 Mb.' 10 ...
                 'Below this limit, you may experiment a lot of "out of memory" errors.' 10 10 ...
                 'Close all the other applications running on this computer to free some memory.' 10 ...
                 'To ignore this message permanently: File > Preferences > Ignore memory warnings.'], ...
                'Memory warning');
        end
    catch
        % Whatever...
    end
end

%% ===== SET DATABASE DIRECTORY =====
isImportDb = 0;
% Get user dir
BrainstormUserDir = bst_get('BrainstormUserDir');
% Get database folder
if isempty(BrainstormDbDir)
    BrainstormDbDir = bst_get('BrainstormDbDir');
% If database folder was passed in input
else
    if isequal(BrainstormDbDir, 'local')
        BrainstormDbDir = bst_fullfile(BrainstormUserDir, 'local_db');
    end
    % Save brainstorm directory
    bst_set('BrainstormDbDir', BrainstormDbDir);
    disp(['BST> Database folder: ' BrainstormDbDir]);
end
% If folder is not defined yet: ask user to set it
if isempty(BrainstormDbDir)
    % Hide splash screen
    bst_splash('hide');
    % Display message: first startup
    java_dialog('msgbox', ['It is the first time you run Brainstorm on this Matlab installation.' 10 10 ...
                           'First of all, you need to create a new directory to store the Brainstorm database,' 10 ...
                           'called for instance "brainstorm_db".' 10 10 ...
                           'IMPORTANT NOTES: ' 10 ...
                           '- Do not create this database directory in the Brainstorm program directory' 10 ...
                           '- The database directory must contain only files created by Brainstorm.' 10 ...
                           '- Do not put your original data files and personal results in the database directory.' 10 ...
                           '- Do not put any file in the Brainstorm program directory.'], 'Brainstorm setup');
    % Set database folder
    BrainstormDbDir = gui_brainstorm('SetDatabaseFolder');
    % If no directory selected : exit
    if isempty(BrainstormDbDir)
        % Release Brainstorm global mutex
        bst_mutex('release', 'Brainstorm');
        return
    end
    % Check if there are protocols in this folder
    if ~isempty(file_find(BrainstormDbDir, 'brainstormsubject*.mat', 4))
        % Ask if user wants to import all the database
        isImportDb = java_dialog('confirm', ['This folder already contains Brainstorm protocols.' 10 10 ...
                                             'Load all these protocols now ?' 10 10]);
    end
end

%% ===== INITIALIZATION DONE =====
disp('BST> Loading current protocol...');
% Get handle to the main window
jFrame = bst_get('BstFrame');
% If the GUI is requested by the user
if (GuiLevel == 1)
    % Show main Brainstorm window
    jFrame.setVisible(1);
    % Weird thing with some macs: by default, window is "always on top"
    if strncmp(computer,'MAC',3)
        jFrame.setVisible(0);
        jFrame.setAlwaysOnTop(0);
        jFrame.setVisible(1);
    end
end
% Headless mode: load protocol data
if (GuiLevel == -1)
    if ~bst_get('isProtocolLoaded')
        db_load_protocol(GlobalData.DataBase.iProtocol);
    end
% Regular GUI: Prepare protocol explorer panel
else
    % Display Brainstorm version
    jFrame.setTitle(['Brainstorm ' Date]);
    % Read the protocols list in UserDataBase
    gui_brainstorm('UpdateProtocolsList');
    % Load the selected protocol
    gui_brainstorm('SetCurrentProtocol', GlobalData.DataBase.iProtocol);
    % Update permanent panels (to disable them)
    panel_surface('UpdatePanel');
    panel_scout('UpdatePanel');
    panel_cluster('UpdatePanel');
end
% Get decoration size
GlobalData.Program.DecorationSize = gui_layout('GetDecorationSize', jFrame);
disp('BST> =================================');
disp(' ');
% Set a flag to mark that brainstorm is now running
setappdata(0, 'BrainstormRunning', 1);
% Hide spash screen
bst_splash('hide');
% Make sure that figure named "Brainstorm" is hidden
hMutex = bst_mutex('get', 'Brainstorm');
set(hMutex, 'Visible', 'off');

%% ===== DELETE OLD PLUGINS =====
% Start with GUI
if (GuiLevel == 1)
    % Add bst_duneuro (now called 'duneuro')
    AllPlugs(end+1).Name = 'bst_duneuro';
    AllPlugs(end+1).Name = 'nirstorm';
    iOldInstall = find(cellfun(@(c)exist(fullfile(BrainstormUserDir,c),'file'), {AllPlugs.Name}));
    % Some old plugins were detected: ask user
    if ~isempty(iOldInstall)
        OldPlugPath = cellfun(@(c)fullfile(BrainstormUserDir,c), {AllPlugs(iOldInstall).Name}, 'UniformOutput', 0);
        isConfirm = java_dialog('confirm', ['The plugin system was updated.' 10 10 ...
            'All the Brainstorm plugins are now managed from the menu "Plugins".' 10 ...
            'The old plugins cannot be used anymore and need to be deleted.' 10 ...
            'Next time you need them, they will be downloaded again automatically.' 10 10 ...
            'Delete the old plugins listed below?' 10 ...
            sprintf(' - %s\n', OldPlugPath{:})], 'Plugin manager');
        % Delete plugin
        if isConfirm
            % Delete files in $HOME/.brainstorm/plugname
            file_delete(OldPlugPath, 1, 3);
            % NIRSTORM: Call uninstall function (it previously installed functions in various $HOME/.brainstorm subfolders)
            if any(strcmpi({AllPlugs(iOldInstall).Name},'nirstorm')) && exist('uninstall_nirstorm')
                cur_dir=pwd;
                cd(bst_get('UserProcessDir'));
                uninstall_nirstorm();
                file_delete(fullfile(bst_get('UserProcessDir'),'uninstall_nirstorm.m'),1);
                file_delete(fullfile(bst_get('UserProcessDir'),{'dg_voronoi.mexa64','dg_voronoi.mexglx'}),1);
                cd(cur_dir);
            end
        end
    end
end

%% ===== RELOAD ALL DATABASE =====
% Create file to indicate that Brainstorm was started
StartFile = bst_fullfile(BrainstormUserDir, 'is_started.txt');
% If import database was mandatory
if isImportDb
    disp('BST> Reloading database...');
    db_import(BrainstormDbDir);
% Check if the program was closed unexpectedly
elseif file_exist(StartFile) && (GuiLevel == 1)
    % Delete this file
    if (file_delete(StartFile, 1) == 1) && ~file_exist(StartFile)
        isReloadCurrent = java_dialog('confirm', ...
            ['Brainstorm was not closed properly, some modifications to the current' 10 ...
             'protocol might not have been saved properly in the dabase.' 10 10 ...
             'Reload the current protocol ?' 10 10]);
        if isReloadCurrent
            db_reload_database(GlobalData.DataBase.iProtocol);
        end
    else
        disp('BST> Warning: Brainstorm is already running from a different Matlab session.');
    end
end
% Open a new file to track if Brainstorm is opened
fid = fopen(StartFile, 'w');
fwrite(fid, ['Brainstorm started: ' datestr(now) 10]);
fwrite(fid, ['User: ' char(java.lang.System.getProperty('user.name')) 10]);
try
    jLocalHost = java.net.InetAddress.getLocalHost();
    fwrite(fid, ['Host: ' char(jLocalHost.getHostName()) ' / ' char(jLocalHost.getHostAddress()) ' (' char(java.lang.System.getProperty('os.name')) ' ' char(java.lang.System.getProperty('os.version')) ')' 10]);
catch
    fwrite(fid, ['Host: Unknown (' char(java.lang.System.getProperty('os.name')) ' ' char(java.lang.System.getProperty('os.version')) ')' 10]);
end
fclose(fid);

%% ===== SET TEMPORARY FOLDER =====
% Get temp folder
TmpDir = bst_get('BrainstormTmpDir');
% If folder is not defined yet: ask user to set it
if ~isempty(strfind(TmpDir, '/home/bic/'))
    % Hide splash screen
    bst_splash('hide');
    % Display message: first startup
    java_dialog('msgbox', [...
        'Warning: You should change the temporary directory.' 10 10 ...
        'The temporary folder used by Brainstorm is set by default to:' 10 '    ' TmpDir 10 ...
        'You only have 1Gb of available space in the folder /home/bic/, ' 10 ...
        'this is might not be enough for Brainstorm to work properly.' 10 10 ...
        'Please create a folder "brainstorm_tmp" on your local hard drive:' 10 ...
        '    /export01/data/username/brainstorm_tmp or ' 10 ...
        '    /export02/username/brainstorm_tmp', 10 10 ...
        'Then change the temporary directory in the next window.' 10 ], ...
       'BIC workstation: Incorrect temporary folder');
    % Edit preferences
    gui_show('panel_options', 'JavaWindow', 'Brainstorm preferences', [], 1, 0, 0);
end

% Empty temporary folder with confirmation (if nogui/server: display warning)
gui_brainstorm('EmptyTempFolder', 1);

%% ===== PREPARE BUG REPORTING =====
% % Get current configuration
% BugReportOptions = bst_get('BugReportOptions');
% % If incomplete: ask user to complete it
% if BugReportOptions.isEnabled && (isempty(BugReportOptions.SmtpServer) || isempty(BugReportOptions.UserEmail))
%     gui_show_dialog('Bug reporting', @panel_bug);
% end

%% ===== COMPILED MODE: WAIT =====
if isCompiled
    %     % Loop to wait for the end
    %     while brainstorm('status')
    %         pause(2);
    %     end
    %     % Exit Matlab
    %     quit force;

    % Wait until the main Brainstorm mutex gets deleted
    waitfor(bst_mutex('get', 'Brainstorm'));
end


function res = indchannel(this, label)
% Method for getting channel indices based on channel labels
% FORMAT  res = indchannel(this, label)
% this       - MEEG object
% label      - string or cell array of labels
%
% res        - vector of channel indices matching labels
%__________________________________________________________________________
% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging

% Vladimir Litvak
% $Id: indchannel.m 3254 2009-07-07 15:18:54Z vladimir $

if ischar(label)
    label = {label};
end

[junk, res] = match_str(label, chanlabels(this));


function slines = readln(fid,nl)
%
% slines = readln(fid,<nlines>)
%
%
% $Id: readln.m,v 1.1 2004/03/11 19:41:26 sayres Exp $

F = fread(fid);
lF = length(F);

if( isempty(F) )
  warning('File appears to be empty');
  slines = [];
  return;
end

indNL = find(F == 10); % indicies of new lines
nNL = length(indNL);

if(max(indNL) == lF) % last char is NL
  nLines = nNL;
else
  nLines = nNL + 1;
end

if(nLines == 1)
  slines = setstr(F');
  return;
end

if(nargin == 2) nlmax = nl;
else            nlmax = nLines;
end

if(nlmax > nLines) nlmax = nLines; end

sF = setstr(F');
clear F;
slines = [];
b = 1;
for n = 1:nlmax

  % need this in case file does not end in a newline
  if(n > nNL) e = lF;
  else        e = indNL(n);
  end

  if(e > b + 1)  % skip lines with only NLs
    s = sF(b:e);
    slines = strvcat(slines,s);
  end

  if(n <= nNL) b = indNL(n) + 1; end

end

return;


function obj=MOcovMFile(fn)
% instantiate MOcov m-file representation of line coverage
%
% obj=MOcovMFile(fn)
%
% Input:
%   fn                  Filename of .m file
%
% Output:
%   obj                 MOcovMFile instance that contains, internally,
%                       the filename, each line of the m-file, which lines
%                       are executable, and an array counting how often
%                       each line has been executed.

    props=get_mfile_props(fn);
    obj=class(props,'MOcovMFile');

function props=get_mfile_props(fn)
    % get properties of a matlab file. The output has
    %   .filename: filename of mfile
    %   .executable:        Nx1 boolean array for N lines,
    %                       indicating which lines can be executed
    %   .executed_count:    Nx1 false array
    %   .lines              Nx1 cellstr with lines of mfile

    % read the matlab file
    fid=fopen(fn);
    cleaner=onCleanup(@()fclose(fid));
    s=fread(fid,inf,'char=>char')';

    % split by newline character
    lines=regexp(s,sprintf('\n'),'split');

    % state variables
    in_line_continuation = false;  % whether the previous line contained a
                                   % line continuation, i.e. ended with '...'

    inside_class = false;  % the file contains a `classdef` statement

    inside_properties = false; % a `properties` section was opened
                               % but not closed.

    % see which lines are executable
    n=numel(lines);
    executable=false(n,1);
    for k=1:n
        line=lines{k};

        line_without_quotes=regexprep(line,'''.*''','');

        comment_start=find(line_without_quotes=='%',1);
        if isempty(comment_start)
            line_code=line_without_quotes;
        else
            line_code=line_without_quotes(1:(comment_start-1));
        end

        line_code_trimmed=regexprep(line_code,'\s','');
        if isempty(line_code_trimmed)
            continue;
        end

        % Check for statements that ~changes/depends on~ the 'parser' state
        classdef_statement = line_is_class_def(line_code_trimmed);
        inside_class = inside_class || classdef_statement;

        properties_section = line_opens_properties_section(...
            line_code_trimmed, inside_class);
        % When a properties section is opened set the inside_properties flag
        inside_properties = inside_properties || properties_section;
        % When inside_properties is set and an 'end' appears, the section
        % is closed.
        % This can be considered a hack, since assumes no code that includes
        % an `end` is allowed inside properties.
        inside_properties = inside_properties && ...
            ~line_ends_with_end_statement(line_code_trimmed);

        % classify line
        executable(k)=~(...
            in_line_continuation || ...
            line_is_function_def(line_code_trimmed) || ...
            classdef_statement || ...
            line_opens_methods_section(line_code_trimmed, inside_class) || ...
            inside_properties || ...  % Arbitrary code cannot run inside
            ...                       % properties section, just a subset
            line_is_case_statement(line_code) || ...
            line_is_elseif_statement(line_code) || ...
            line_is_else_statement(line_code) || ...
            line_is_sole_end_statement(line_code_trimmed));

        in_line_continuation=line_has_line_continuation(line_code);
    end

    % put results in struct
    props=struct();
    props.filename=mocov_get_absolute_path(fn);
    props.executable=executable;
    props.executed_count=zeros(size(props.executable));
    props.lines=lines;

function tf=line_is_function_def(line)
    % returns true if the string in line indicates a function definition
    newline=sprintf('\n');
    pat=[newline '\s*function([\[\]\w ,~]*=)?[ ]*'...
                 '(?<name>[a-zA-Z]\w*)(\([^\)]*\))?'];

    tf=~isempty(regexp([newline line],pat,'once'));

function tf=line_is_class_def(line)
    % returns true if the line opens a class definition
    tf=~isempty(regexp(line,'^\s*classdef\W*','once'));

function tf=line_opens_methods_section(line, inside_class)
    % returns true if the line opens a method section inside class definition
    tf=inside_class && ...
       ~isempty(regexp(line,'^\s*methods\s*(\([^\(\)]*\))?\s*$','once'));

function tf=line_opens_properties_section(line, inside_class)
    % returns true if the line opens a properties section inside class definition
    tf=inside_class && ...
       ~isempty(regexp(line,'^\s*properties\s*(\([^\(\)]*\))?\s*$','once'));

function tf=line_is_sole_end_statement(line)
    % returns true if the string in line is just an end statement
    tf=isempty(regexprep(line,'([\s,;]|^)?end([\s,;]|$)?',''));

function tf=line_is_case_statement(line)
    tf=~isempty(regexp(line,'^\s*case\W*','once'));

function tf=line_is_elseif_statement(line)
    tf=~isempty(regexp(line,'^\s*elseif\W*','once'));

function tf=line_is_else_statement(line)
    tf=~isempty(regexp(line,'^\s*else\W*','once'));

function tf=line_ends_with_end_statement(line)
    % returns true if the line has a finishing 'end'
    tf=~isempty(regexp(line,'(^|\W)end\s*[,;]?\s*$','once'));

function tf=line_has_line_continuation(line)
    % returns true if the line contains a line continuation
    tf=~isempty(regexp(line,'\.\.\.','once'));


function [rxnAssoc, metAssoc] = updateAnimalAnnotations(animalGEM, speciesSpecRxns, speciesSpecMets);
% updateAnimalAnnotations
%   Update animal GEM annotation by integrating annotiation of Human-GEM
%   and species-specific rxns/mets
%
%   Input:
%   animalGEM            a given animal GEM
%
%   speciesSpecRxns      a structure of species-specific reactions
%   speciesSpecMets      a structure of species-specific metabolites
%
%
%   Output:
%   rxnAssoc             a structure containing reaction annotations
%   metAssoc             a structure containing metabolite annotations
%
%   NOTE: The detail specification for speciesSpecRxns and speciesSpecMets
%          structures are described in addMetabolicNetwork function.
%
%   Usage: [rxnAssoc, metAssoc] = updateAnimalAnnotations(animalGEM, speciesSpecRxns, speciesSpecMets)
%

% handle input arguments
if nargin < 3 || nargin < 2
    error('Missing species-specific annotations!');
end

% checks

%% Load Human-GEM annotation files

% get model path
[ST, I]=dbstack('-completenames');
modelPath=fileparts(fileparts(ST(I).file));

% Human-GEM has to be included into Matlab path
rxnAnnotation=fullfile(modelPath,'model','reactions.tsv');
metAnnotation=fullfile(modelPath,'model','metabolites.tsv');
if isfile(rxnAnnotation) && isfile(metAnnotation)
    fprintf('Load Human-GEM annotation files.\n');
    rxnAssocHuman = importTsvFile(rxnAnnotation);
    metAssocHuman = importTsvFile(metAnnotation);
else
    error('ERROR: No annotation file(s) are found!');
end

%% integrate human and species-specific annotations

% append species-specific annotiaons
rxnAssoc = integrateAnnotation(rxnAssocHuman, speciesSpecRxns, 'rxns');
metAssoc = integrateAnnotation(metAssocHuman, speciesSpecMets, 'mets');

% remove non-existing elements and order by the fields in model
rxnAssoc = sortCleanArray(rxnAssoc, animalGEM.rxns, 'rxns');
metAssoc = sortCleanArray(metAssoc, animalGEM.mets, 'mets');

end

%% sub-functions
% integrate human and species-specific annotations
function joinedArray= integrateAnnotation(baseArray, uniqueArray, fieldName);

    joinedArray = baseArray;  % human annotation data
    nNewElements = length(uniqueArray.(fieldName));
    baseArrayFields = fieldnames(baseArray);
    uniqueArrayFields = fieldnames(uniqueArray);

    % combine annotation information
    for i = 1:length(baseArrayFields)
        n = baseArrayFields{i};
        if ismember(n,intersect(baseArrayFields, uniqueArrayFields))
            joinedArray.(n) = [baseArray.(n); uniqueArray.(n)];
        else
            if isnumeric(joinedArray.(n)(1))
                joinedArray.(n) = [baseArray.(n); zeros(nNewElements,1)];
            else
                joinedArray.(n) = [baseArray.(n); repmat({''},nNewElements,1)];
            end
        end
    end

end

% sort and clean annotation cell array according to model rxns/mets
function sortedArray= sortCleanArray(inputArray, inputCell, fieldName);

% model components (rxns or mets) must be included in the inputArray
if all(ismember(inputCell, inputArray.(fieldName)))
    idToRemove = setdiff(inputArray.(fieldName), inputCell);

    % identifiy human rxns/mets that are not in animal GEM
    if ~isempty(idToRemove)
        [~, indexToRemove] = ismember(idToRemove, inputArray.(fieldName));
        inputArray.(fieldName)(indexToRemove) = [];  %remove non-existing elements
    end

    % get the index for aligning annoation array with model rxns/mets
    [~, sortedIndex] = ismember(inputCell, inputArray.(fieldName));
    sortedArray = {};
    sortedArray.(fieldName) = inputArray.(fieldName)(sortedIndex);

    arrayFields = setdiff(fieldnames(inputArray), fieldName);
    for i = 1:length(arrayFields)
        n = arrayFields{i};
        if ~isempty(idToRemove)
	        inputArray.(n)(indexToRemove) = [];  %remove non-existing elements
        end
        sortedArray.(n) = inputArray.(n)(sortedIndex);
    end
else
    error('There are model components that are from unknown source.');
end

end


function niftiWrite(ni,fName)
%  Writes a nifti structure to disk.
%
%   niftiWrite(ni,[fName])
%
% Wrapper to writeFileNifti mex file, which writes a VISTASOFT nifti
% structure to a NIFTI-1 file.
%
% INPUTS
%   ni - a nifti structure
%   fName - output file name  (defaults to ni.fname if empty).
%
% FILE FORMATS:
%   fName extension should be either '.nii' or 'nii.gz'
%
% EXAMPLE:
%   dFolder = mrtInstallSampleData('functional','mrBOLD_01');
%   pth =  fullfile(dFolder, 'Raw', 'T1_Inplane.nii.gz');
%   ni  = niftiRead(pth);
%   fName = fullfile(tempdir, 'myFile.nii.gz');
%   niftiWrite(ni,fName);
%
% See also:  niftiRead
%
% (C) Stanford VISTA, 2012-2015
%

if exist('fName', 'var') && ~isempty(fName)
    ni.fname = fName;
elseif ~isfield(ni,'fname') || isempty(ni.fname)
    error('A filename is required to save a NIFTI-1 structure to file.');
end

% EDIT GLU 2020-01-28
% We want to be able to write Nifti-2 files. If the the field version is not in
% the ni struct, or it is = 1, write a Nifti-1 file using the old mex code, to
% maximize backwards compatibility. If the version field is 2, write a Nifti-2
% using the nii_tool (https://github.com/xiangruili/dicm2nii)
if isfield(ni, 'version')
    if ni.version == 2
        % It will take file name from struct, and it will use the NIfti-2
        % from the struct as well.
        %
        % Modify it to format as nii_tool wants it
        ni2                = struct();
        ni2.img            = ni.data;
        ni                 = rmfield(ni, 'data');
        ni2.hdr            = ni;
        ni2.hdr.file_name  = ni2.hdr.fname;
        ni2.hdr            = rmfield(ni2.hdr, 'fname');
        % Use a function to setup the xyzt_units field
        ni2.hdr.xyzt_units = setSpaceTimeUnits(ni2.hdr.xyz_units, ni2.hdr.time_units);
        ni2.hdr            = rmfield(ni2.hdr, 'xyz_units');
        ni2.hdr            = rmfield(ni2.hdr, 'time_units');

        % Adapt back the rest of the fields using writeFileNifti code:
        ni2.hdr.pixdim              = zeros(1,1+numel(ni.pixdim));
        ni2.hdr.pixdim(2:1+numel(ni.pixdim)) = ni.pixdim;
        ni2.hdr.datatype            = ni.nifti_type;
        ni2.hdr                     = rmfield(ni2.hdr, 'nifti_type');
        ni2.hdr.pixdim(1)           = ni.qfac;
        ni2.hdr                     = rmfield(ni2.hdr, 'qfac');

        % When reading files we need to add 1 voxel to the header information to
        % complaint with matlab 1-based indexing.
        %
        % Here we undo such operation, we subtract one before writing the file to disk.
        if (~all(ni.qto_xyz(1:9)==0))
            ni.qto_ijk(1:3,4) = ni.qto_ijk(1:3,4) - 1;
            q_xyz = inv(ni.qto_ijk);
            q = matToQuat(q_xyz);
            ni2.hdr.qoffset_x = q.quatern_x;
            ni2.hdr.qoffset_y = q.quatern_y;
            ni2.hdr.qoffset_z = q.quatern_z;
        else
            ni2.hdr.qoffset_x = 0;
            ni2.hdr.qoffset_y = 0;
            ni2.hdr.qoffset_z = 0;
        end

        if (~all(ni.sto_xyz(1:9)==0))
            sto_ijk = ni.sto_ijk;
            sto_ijk(1:3,4) = ni.sto_ijk(1:3,4) - 1;
            sto_xyz = inv(sto_ijk);
            ni2.hdr.srow_x = sto_xyz(1,:);
            ni2.hdr.srow_y = sto_xyz(2,:);
            ni2.hdr.srow_z = sto_xyz(3,:);
        else
            ni2.hdr.srow_x = 0;
            ni2.hdr.srow_y = 0;
            ni2.hdr.srow_z = 0;
        end

        % --- nk fields
        % The fields freq_dim, phase_dim, slice_dim are all squished into the single
        % byte field dim_info (2 bits each, since the values for each field are
        % limited to the range 0..3):
        % ni.freq_dim =  bitand(uint8(3),          uint8(nii.hdr.hk.dim_info)    );
        % ni.phase_dim = bitand(uint8(3), bitshift(uint8(nii.hdr.hk.dim_info),-2));
        % ni.slice_dim = bitand(uint8(3), bitshift(uint8(nii.hdr.hk.dim_info),-4));
        % Here we undo the previous operations perfrmed during the file load.

        ni2.hdr.dim_info = ni2.hdr.freq_dim + 4*ni2.hdr.phase_dim + 16*ni2.hdr.slice_dim;

        % Save it.
        nii_tool('save', ni2);
    else
        writeFileNifti(ni);
    end
else
    writeFileNifti(ni);
end

return
end

% GLU 2020-01-28 added this function from Matlab's nifti implementation to
% convert back the spaceTime units to the nifti field required by
    function xyztCode = setSpaceTimeUnits(spaceUnitText, timeUnitText)
        %setSpaceTimeUnits: convert simplified space time units to standard
        %form.
        %    This is a helper function to convert simplified space and time
        %    units to the raw format as specified in the NIfTI header.

        spaceKey   = {0, 1, 2, 3};
        spaceValue = {'unknown', 'meter', 'mm', 'micron'};

        spaceMap = containers.Map(spaceValue, spaceKey);

        if isempty(find(strcmp(spaceValue, spaceUnitText), 1))
            error(message('images:nifti:spaceUnitNotSupported'));
        end

        spaceUnits = spaceMap(spaceUnitText);

        timeValue = {'unknown', 'sec', 'msec', 'usec', 'hz', 'ppm', 'rads'};
        timeKey = {0, 8, 16, 24, 32, 40, 48};

        timeMap = containers.Map(timeValue, timeKey);

        if isempty(find(strcmp(timeValue, timeUnitText), 1))
            error(message('images:nifti:timeUnitNotSupported'));
        end

        timeUnits = timeMap(timeUnitText);

        spaceUnitCode = bitand(uint8(spaceUnits),uint8(7));
        timeUnitCode  = bitand(uint8(timeUnits),uint8(56)); % 0x38

        xyztCode = bitor(spaceUnitCode, timeUnitCode);

    end

%% Old code
% The old code was not a wrapper to writeFileNifti, but instead converted
% the VISTASOFT nifti struct to a nifti-1 struct using the function
% niftiVista2ni, and then write the nifti-1 struct to file using the Shen
% function save_nii.
%
% We prefer the mex file writeFileNifti, which is fast which does the
% inverse conversion of the mex function readFileNifti (wrapped in the m
% file niftiRead).

% %% Set defaults
% compress = true;
% fname_supplied = false;
%
%
% %% Check inputs
% if nargin==0
%     help(mfilename)
%     return
% end
%
%
%
% if notDefined('fName')
%     fName = '';
% else
%     fname_supplied = true;
% end
%
%
% %% Check file extension
%
% % If no name was supplied check the nifti structure for the name
% if isempty(fName)
%     if isfield(ni,'fname') && ~isempty(ni.fname)
%         fName = ni.fname;
%     else
%         error('A filename is required to save a NIFTI-1 structure to file.');
%     end
% end
%
% % Handle the file extension (must be '.nii' for save_nii)
% [p, f, e] = fileparts(fName);
% switch e
%     case '.gz'
%         fName = fullfile(p,f);
%     case '.nii'
%         % If a filename was passed in and it was something'.nii' then we
%         % assume the user does not want the file compressed.
%         if fname_supplied; compress = false; end
%     otherwise
%         fName = fullfile(p,[f '.nii']);
% end
%
%
% %% Deal with old VISTASOFT nifti structures.
% if isfield(ni,'data')
%   % This is likely to be a old VISTASOFT nifti-1 structure, see
%   % niftiVista2ni.m
%   ni = niftiVista2ni(ni);
% end
%
%
% %% Save the file to disk using Shen's code.
% save_nii(ni,fName);
%
%
% %% GZip the file
% if compress
%     gzip(fName);
%
%     % Delete the unzipped file created by save_nii.m:
%     delete(fName);
% end
%
% return


function sVF = uminus(sVF)
%
% Syntax
%   sVF = -sVF
%

sVF.sF = -sVF.sF;

end


function demo_snirf()

% Syntax:
%    demo_snirf()
%
% Description:
%    Convert sample .nirs files included in the Homer3 project to SNIRF files (*.snirf) and
%    compare the saved and loaded SnirfClass objects for each coberted file to make sure
%    saving and loading of the .snirf is working correctly.
%

% Find the Homer3 Examples folder and cd to it
examplesDir = findexamplesdir();

% Delete any previously generated .snirf files to make sure to start from scratch
DeleteDataFiles(examplesDir, '.snirf', 'standalone');

% Start with .nirs files
nirsfiles = mydir([examplesDir, '*.nirs']);

fprintf('\n')

% Go through all the sample .nirs files
for ii=1:length(nirsfiles)

    % Convert sample .nirs file to SNIRF file (*.snirf)
    [pname,fname,ext] = fileparts([examplesDir, nirsfiles(ii).name]);
    pname = filesepStandard(pname);
    fprintf('Converting %s to %s\n', [pname, fname, ext], [pname, fname, '.snirf']);
    [snirf_saved, snirf_loaded] = snirf_load_save([pname, nirsfiles(ii).name]);

    % Compare the saved and loaded SnirfClass objects, using overloaded == operator
    if snirf_saved == snirf_loaded
        fprintf('Saved and loaded SnirfClass objects for %s are equal\n', [fname,'.snirf']);
    else
        fprintf('ERROR: Saved and loaded SnirfClass objects for %s are NOT equal\n', [fname,'.snirf']);
    end
    fprintf('\n');

end


function model = modelLoadResult(type, dataSet, number, dataLoaderStr)

% MODELLOADRESULT Load a previously saved result.
% FORMAT
% DESC loads a previously saved model result.
% ARG type : the type of model to load.
% ARG dataSet : the name of the data set to load.
% ARG number : the number of the model run to load.
% RETURN model : the saved model.
%
% SEEALSO : modelWriteResult
%
% COPYRIGHT : Neil D. Lawrence, 2009

% MLTOOLS
  if nargin < 4
    dataLoaderStr = 'lvmLoadData';
  end
  dataLoaderHandle = str2func(dataLoaderStr);
  origDataSet = dataSet;
  dataSet(1) = upper(dataSet(1));
  origType = type;

  type(1) = upper(type(1));
  fileName = ['dem' dataSet type num2str(number)];

  fhandle = [origType 'LoadResult'];
  if exist(fhandle)==2
    % There is load result code, use it.
    fhandle = str2func(fhandle);
    if nargin > 3
      model = fhandle(origDataSet, number, dataLoaderStr);
    else
      model = fhandle(origDataSet, number);
    end
  else
    fhandle = [origType 'Reconstruct'];
    if exist(fhandle)==2
      % There is reconstruct code, use it to reconstruct.
      [Y, lbls] = dataLoaderHandle(origDataSet);
      load(fileName);
      varName = [origType 'Info'];
      eval(['model = ' fhandle '(' varName ', Y);'])
    else
      % No code is provided, just load the file.
      load(fileName);
    end
  end
end


function [OutputError] = PIVlab_capture_OPTOcam_save(OPTOcam_vid,nr_of_images,ImagePath,frame_nr_display,bitmode)
if bitmode==8
	bitmultiplicator=1;
elseif bitmode==12
	bitmultiplicator = 16; %bring 12bit data to 16 bits full histogram, otherwise images outside Matlab are not displayed correctly (too dark).
end
hgui=getappdata(0,'hgui');
OutputError=0;
OPTOcam_frames_to_capture = nr_of_images*2;
if getappdata(hgui,'cancel_capture') ~=1 %capture was not cancelled --> save images from RAM to disk
	OPTOcam_data = getdata(OPTOcam_vid,OPTOcam_frames_to_capture); %ruft alle Frames in RAM ab. Frame 1,2,3 sind müll
	cntr=0;
	for image_save_number=1:2:size(OPTOcam_data,4)
		imgA_path=fullfile(ImagePath,['PIVlab_' sprintf('%4.4d',cntr) '_A.tif']);
		imgB_path=fullfile(ImagePath,['PIVlab_' sprintf('%4.4d',cntr) '_B.tif']);
		imwrite(OPTOcam_data(:,:,:,image_save_number)*bitmultiplicator,imgA_path,'compression','none'); %tif file saving seems to be the fastest method for saving data...
		imwrite(OPTOcam_data(:,:,:,image_save_number+1)*bitmultiplicator,imgB_path,'compression','none');
		cntr=cntr+1;
		set(frame_nr_display,'String',['Saving images to disk: Image pair ' num2str(cntr) ' of ' num2str(size(OPTOcam_data,4)/2)]);
		drawnow limitrate;
	end
end


%Enumerates the different types of data in the WDK and provides
%functionality to convert them to strings
classdef DataType
    properties (Access = public, Constant)
        kDataFile = 0;
        kSignal = 1;
        kSignal2 = 2;
        kSignal3 = 3;
        kSignalN = 4;
        kEvent = 5;
        kSegment = 6;
        kFeature = 7;
        kFeatureVector = 8;
        kTable = 9;
        kTableSet = 10;
        kLabels = 11;
        kAny = 12;
        kNull = 13;
        kAnnotation = 14;
        kClassificationResult = 15;
        kRegressionResult = 16;
    end

    properties (Access = public, Constant)
        TypeStrings = {'DataFile' , 'Signal (1D)', 'Signal (2D)', 'Signal (3D)',...
            'Signal', 'Event', 'Segment', 'Feature', 'FeatureVector', ...
            'Table','TableSet', 'Labels', 'Any', 'Null', 'Annotation', 'ClassificationResult','RegressionResult'};
    end

    methods (Access = public, Static)
        function str = DataTypeToString(type)
            str = DataType.TypeStrings{type+1};
        end
    end
end


function [string,p_st,p_ed]=position(str_long,str_ID)
% Retrieve the value of an attribute (`str_ID`) in the line (`str_long`) of the
% XML file and identify the starting and ending indices of the value in the
% attribute line of the XML file.
%
% USAGE:
%
%    [string, p_st, p_ed] = position(str_long, str_ID)
%
% INPUTS:
%    str_long:       A string of the line of the XML file
%    str_ID:         The name of the attribute
%
% OUTPUTS:
%    string:         The value of the attribute
%    p_st:           The starting index of the value
%    p_ed:           The ending index of the value
%
% .. Author: - Longfei Mao Oct/2014

        ind_pos=strfind(str_long,str_ID); % name='metaid';
        l=length(str_ID)+2;
        p_st=ind_pos{1}+l
       % end_rem=strfind(str_long{1}(ind_pos{1}:end),'"');
        end_rem=strfind(str_long{1}(p_st:end),'"');
        % disp(str_long{1}(ind_pos{1}:end));
        disp(str_long{1}(p_st:end));

%         if size(end_rem)
%         end_rem{1}=[];
%         end

        p_ed=end_rem(1)+p_st-2;
        disp(p_ed);

       string=str_long{1}(p_st:p_ed);

end


function tagS = content_identification_module_tags
%"content_identification_module_tags"
%   Return the tags used to represent content as specified by C.10.9 in
%   PS3.3 of 2006 DICOM specification. Tags are returned in a struct array
%   with 3 fields:
%
%   Tag: String containing hex DICOM tag of a field.
%  Type: String describing type of field, with 5 options:
%         '1' Field must exist, data must exist and be valid.
%         '2' Field must exist, data can exist and be valid, or be NULL.
%         '3' Field is optional, if the field exists data can exist and be
%             valid or be NULL.
%         '1C' Field must exist under certain conditions and contain valid
%             data.
%         '2C' Field must exist under certain conditions and can contain
%             valid data or be NULL.
%Children: For sequences, a tagS with the same format as this struct array.
%
%JRA 06/06/06
%YWU Modified 03/01/08
%
%Usage:
%   tagS = patient_module_tags
%
% Copyright 2010, Joseph O. Deasy, on behalf of the CERR development team.
%
% This file is part of The Computational Environment for Radiotherapy Research (CERR).
%
% CERR development has been led by:  Aditya Apte, Divya Khullar, James Alaly, and Joseph O. Deasy.
%
% CERR has been financially supported by the US National Institutes of Health under multiple grants.
%
% CERR is distributed under the terms of the Lesser GNU Public License.
%
%     This version of CERR 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.
%
% CERR 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 CERR.  If not, see <http://www.gnu.org/licenses/>.

tagS = content_identification_macro_tags;

%
% %Initialize the tagS structure.
% tagS = struct('tag', {}, 'type', {}, 'children', {});
%
% %Create an empty tagS template for sequence creation.
% template = tagS;
%
% %Add tags based on PS3.3 attribute lists.
%
% %Instance Number
% tagS(end+1) = struct('tag', ['00200013'], 'type', ['1'], 'children', []);
%
% %Content Label
% tagS(end+1) = struct('tag', ['00700080'], 'type', ['1'], 'children', []);
%
% %Content Description
% tagS(end+1) = struct('tag', ['00700081'], 'type', ['2'], 'children', []);
%
% %Content Creator's Name
% tagS(end+1) = struct('tag', ['00700084'], 'type', ['2'], 'children', []);
%


classdef SettingsLevelSetSmoothRectangleInclusion < SettingsLevelSetCreator

    properties (Access = public)
        widthH
        widthV
        pnorm
    end

    methods (Access = public)

        function obj = SettingsLevelSetSmoothRectangleInclusion(varargin)
            obj.loadParams('paramsLevelSetCreator_SmoothRectangle.json')
            if nargin == 1
                obj.loadParams(varargin{1})
            end
        end

    end

end


classdef source < iosr.dsp.audio
%SOURCE Class of sound source separation source.
%
%   iosr.bss.source objects contain information about a sound source
%   (including its spatial location). iosr.bss.source objects are passed to
%   iosr.bss.mixture objects in order to create a mixture.
%
%   IOSR.BSS.SOURCE is a subclass of IOSR.DSP.AUDIO.
%
%   IOSR.BSS.SOURCE properties:
%       azimuth     - Azimuth of the source
%       elevation   - Elevation of the source
%       numchans    - Number of audio channels in the source
%       precomposed - Logical flag indicating whether the source should be
%                     spatialised
%
%   IOSR.BSS.SOURCE methods:
%       source      - Create a source object
%       copy        - Create an independent copy of the source (but not its
%                     audio file)
%       write       - Write the source audio file (and resample/downmix)
%
%   See also IOSR.DSP.AUDIO, IOSR.BSS.MIXTURE.

%   Copyright 2016 University of Surrey.

    properties (AbortSet)
        azimuth     % Azimuth of the source
        elevation   % Elevation of the source
        numchans    % Number of audio channels in the source
        precomposed % Logical flag indicating whether the source should be spatialised
    end

    properties (Dependent, SetAccess = protected)
        signal      % The sampled data (read-only)
    end

    properties (GetAccess = private, SetAccess = public)
         parent
    end

    methods

        % constructor
        function obj = source(filename,varargin)
        %SOURCE Create a source object
        %
        %   OBJ = IOSR.BSS.SOURCE(FILENAME) creates a source whose signal
        %   is contained in the audio file FILENAME. The source will have
        %   an azimuth and elevation of 0. The number of channels and
        %   sampling frequency will be determined by the audio file.
        %
        %   OBJ = IOSR.BSS.SOURCE(...,'PARAMETER',VALUE) allows additional
        %   options to be specified. The options are ({} indicate
        %   defaults):
        %
        %       'azimuth'       : {0} | scalar
        %           The azimuth of the source for rendering.
        %       'elevation'     : {0} | scalar
        %           The elevation of the source for rendering.
        %       'fs'            : {fs of FILENAME} | scalar
        %           The sampling frequency of the source. If the sampling
        %           frequency of the audio file FILENAME does not match,
        %           the audio file will be resampled each time the signal
        %           is requested.
        %       'precomposed'   : {false} | true
        %           Logical flag indicating whether the source should be
        %           spatialised. By default it is assumed that the source
        %           is a point source (irrespective of its channel count),
        %           and it will be spatialised. When set to true, the
        %           source will be summed directly with the spatial signal.
        %       'numchans'      : {numchans of FILENAME} | scalar
        %           The number of channels in the source. If this does not
        %           match the channel count of the audio file FILENAME, the
        %           audio file will be up-/down-mixed each time the signal
        %           is requested.
        %
        %   The FS and NUMCHANS parameters do not affect the underlying
        %   audio file, but are implemented on-the-fly each time the signal
        %   is requested. To render the changes to a new audio file, use
        %   the write() method.
        %
        %   OBJ = IOSR.BSS.SOURCE creates an empty source object.
        %
        %   Note that this is a handle class. Sources are hence passed by
        %   reference. Use the COPY() method to create an independent copy
        %   of the source.
        %
        %   See also IOSR.DSP.AUDIO.UP_DOWN_MIX.

            if nargin > 0

                propNames = {'azimuth','elevation','fs','precomposed','numchans'};

                obj.filename = filename;
                info = audioinfo(obj.filename);

                % defaults
                obj.azimuth = 0;
                obj.elevation = 0;
                obj.precomposed = false;
                obj.fs = info.SampleRate;
                obj.numchans = info.NumChannels;
                obj.rendered = false;

                % read parameter/value inputs
                if nargin > 1 % if parameters are specified
                    obj.set_properties(varargin,propNames)
                end

            end

        end

        function write(obj,filename)
        %WRITE Write the source audio file (and resample/downmix)
        %
        %   IOSR.BSS.SOURCE.WRITE() overwrites the source's audio file. If
        %   the source audio file's sampling rate and/or channel count do
        %   not match the object's properties, then the audio file will be
        %   resampled and/or up- or down-mixed accordingly.
        %
        %   IOSR.BSS.SOURCE.WRITE(FILENAME) writes the audio file to the
        %   specified file FILENAME and updates SOURCE.FILENAME.
        %
        %   See also IOSR.DSP.AUDIO.UP_DOWN_MIX.

            % overwrite filename if one is specified
            if exist('filename','var')==1
                obj.filename = filename;
            end

            assert(~isempty(obj.filename), 'iosr:source:invalidFile', 'SOURCE.FILENAME is empty.')

            % ensure path exists
            obj.ensure_path(obj.filename)

            % write new file and update filename
            audiowrite(obj.filename,obj.normalize(obj.signal),obj.fs);
            obj.rendered = true;

        end

        % validate properties

        % validate azimuth
        function set.azimuth(obj,val)
            assert(isscalar(val), 'iosr:source:invalidAzimuth', 'AZIMUTH must be a scalar')
            obj.azimuth = val;
            obj.property_changed('azimuth',val);
        end

        % validate elevation
        function set.elevation(obj,val)
            assert(isscalar(val), 'iosr:source:invalidElevation', 'ELEVATION must be a scalar')
            obj.elevation = val;
            obj.property_changed('elevation',val);
        end

        % validate numchans
        function set.numchans(obj,val)
            assert(isscalar(val), 'iosr:source:invalidNumchans', 'NUMCHANS must be a scalar')
            obj.numchans = val;
            obj.property_changed('numchans',val);
        end

        % validate precomposed
        function set.precomposed(obj,val)
            assert(islogical(val) && numel(val)==1, 'iosr:source:invalidPrecomposed', 'PRECOMPOSED property must be true or false')
            obj.precomposed = val;
            obj.property_changed('precomposed',val);
        end

        % dependent properties

        % get dependent property signal
        function signal = get.signal(obj)

            % read audio file
            [signal,fs2] = audioread(obj.filename);

            % do more stuff if differences not rendered
            if ~obj.rendered
                N = size(signal,2);

                if fs2~=obj.fs % resample
                    signal = resample(signal,obj.fs,fs2);
                end
                if N~=obj.numchans % downmix
                    signal = iosr.dsp.audio.up_down_mix(signal,obj.numchans);
                end
            end

        end

    end

    methods(Access = protected)

        function property_changed(obj,name,val)
        %PROPERTY_CHANGED handle property changes

            obj.rendered = false;

            switch lower(name)
                case 'fs'
                    if isa(obj.parent,'mixture') && obj.parent.fs~=val
                        obj.parent.fs = val;
                    end
            end

        end

        function cpObj = copyElement(obj)
        %COPYELEMENT Overload copy method with additional functionality

            % Make a shallow copy of all properties
            cpObj = copyElement@iosr.dsp.audio(obj);

        end

    end

end


% UTILS Utility package for MATLAB xUnit Test Framework
%
% Array Comparison
%   compareFloats            - Compare floating-point arrays using tolerance
%
% Test Case Discovery Functions
%   isTestCaseSubclass       - True for name of TestCase subclass
%
% String Functions
%   arrayToString            - Convert array to string for display
%   comparisonMessage        - Assertion message string for comparing two arrays
%   containsRegexp           - True if string contains regular expression
%   isSetUpString            - True for string that looks like a setup function
%   isTearDownString         - True for string that looks like teardown function
%   isTestString             - True for string that looks like a test function
%   stringToCellArray        - Convert string to cell array of strings
%
% Miscellaneous Functions
%   generateDoc              - Publish test scripts in mtest/doc
%   parseFloatAssertInputs   - Common input-parsing logic for several functions

%   Steven L. Eddins
%   Copyright 2008-2009 The MathWorks, Inc.


function boxes = masksToBoxes( segs, props )
    r = cell(length(segs));
    for i = 1:length(segs)
        r{i} = maskToBox( segs{i}, props{i} );
    end
    boxes = cat(1,r);
end


function setLastLineStyle(h, line_style)

% setLastLineStyle get all the lines handler and change them to a <linestyle> value
%
% SINTAX:
%   setLastLineStyle(<h>, line_style)
%   setLastLineStyle(line_style)
%
% EXAMPLE:
%   setLastLineStyle(gcf, ':');
%   setLastLineStyle(':');
%
% INPUT:
%   h          = handler to the figure to modify        <optional argument>
%   line_style = requested line style for the line
%
% DEFAULT VALUES:
%   h       = gcf
%

%--- * --. --- --. .--. ... * ---------------------------------------------
%               ___ ___ ___
%     __ _ ___ / __| _ | __|
%    / _` / _ \ (_ |  _|__ \
%    \__, \___/\___|_| |___/
%    |___/                    v 1.0RC1
%
%--------------------------------------------------------------------------
%  Copyright (C) 2021 Geomatics Research & Development srl (GReD)
%  Written by:       Andrea Gatti
%  Contributors:     ...
%  A list of all the historical goGPS contributors is in CREDITS.nfo
%--------------------------------------------------------------------------
%
%   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/>.
%
%--------------------------------------------------------------------------
% 01100111 01101111 01000111 01010000 01010011
%--------------------------------------------------------------------------

if nargin < 2
    line_style = h;
    h = gcf;
end
hline = findobj(h, 'type', 'line');
set(hline(1), 'LineStyle', line_style);


function [temp_planC] = loadPlanC_temp(fname,file)
%
%   Load and return a planC given a fullpath filename.  planC is returned as
%   temp_planC in the case where another planC is already open. The filename can
%   be a .mat or .mat.bz2 file. Bz2 files are extracted to a temporary .mat
%   file, loaded, and then the temporary .mat file is deleted.
%
%   18 Mar 06, KU
%
% Copyright 2010, Joseph O. Deasy, on behalf of the CERR development team.
%
% This file is part of The Computational Environment for Radiotherapy Research (CERR).
%
% CERR development has been led by:  Aditya Apte, Divya Khullar, James Alaly, and Joseph O. Deasy.
%
% CERR has been financially supported by the US National Institutes of Health under multiple grants.
%
% CERR is distributed under the terms of the Lesser GNU Public License.
%
%     This version of CERR 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.
%
% CERR 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 CERR.  If not, see <http://www.gnu.org/licenses/>.

planC = [];
temp_planC  = [];

h = waitbar(100,'Loading selected study...');

bzFile = 0;

% Test for compressed file (*.gz), GNUZIP (freeware).  Code by A. Blanco.
if ~isempty(strfind(file,'.bz2'))
    bzFile = 1;
    disp(['Uncompressing file ', fname]);
    outstr = gnuCERRCompression(file, 'uncompress',tempdir);
    file = file(1:end-3);
end

disp(['Loading file ', fname]);
planC = load(file,'planC');

temp_planC = planC.planC;   %Conversion from struct created by load

%Remove unzipped file after loading.
if bzFile
    delete(file);
end

close(h)

if ~exist('planC');
    error('.mat or .mat.bz2 file does not contain a planC variable.');
    return;
end


function reproducescript(reproducescript_dir, filename, highest_ft, cfg, outputcfg)

% This is a helper function to create a script that reproduces the analysis. It
% appends the configuration and the function call to a MATLAB script.

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

if isfield(cfg, 'callinfo') && isfield(cfg.callinfo, 'usercfg')
  tmpcfg = removefields(cfg.callinfo.usercfg, ignorefields('reproducescript'));
else
  tmpcfg = removefields(cfg, ignorefields('reproducescript'));
end

tmpcfg = copyfields(cfg, tmpcfg, {'inputfile', 'outputfile'});
tmpcfg = printstruct('cfg', tmpcfg);

% We're going to explicitly mention any input files going into the pipeline
% at this point in a comment in the script file. We should mention any
% files that (1) have not been produced as an output in the same pipeline
% and that (2) have not been used as an input before. Do this by simply
% checking any .mat file references containing "input" in the stringified cfg against any .mat
% file references already in the script file.
re = ['(?<=' regexptranslate('escape', reproducescript_dir) '[/\\]{1})(\w+_input_\w+(\.mat){1})'];
if exist(filename, 'file')
  script = fileread(filename);
  existing_infiles = regexp(script, re, 'match');
else
  existing_infiles = {};
end
new_infiles = regexp(tmpcfg, re, 'match');
new_infiles = unique(setdiff(new_infiles, existing_infiles));

ft_info('writing script to file ''%s''\n', filename);
fid = fopen(filename, 'a+');
fprintf(fid, '%%%%\n\n');

for k = 1:numel(new_infiles)
  fprintf(fid, '%% a new input variable is entering the pipeline here: %s\n', new_infiles{k});
end
if numel(new_infiles) > 0
  fprintf(fid, '\n');
end

fprintf(fid, 'cfg = [];\n');
fprintf(fid, '%s\n', tmpcfg);

if outputcfg
  % this is for ft_definetrial, ft_artifact_zvalue, etc.
  fprintf(fid, 'cfg = %s(cfg);\n\n', highest_ft);
else
  fprintf(fid, '%s(cfg);\n\n', highest_ft);
end

fclose(fid);


function [success] = saveNeurodataWithoutBorders(image_masks,roi_response_data,algorithm,outputFilePath,varargin)
	% Takes cell extraction outputs and saves then in NWB format per format in https://github.com/schnitzer-lab/nwb_schnitzer_lab.
	% Biafra Ahanonu
	% started: 2020.04.03 [16:01:45]
	% Based on mat2nwb in https://github.com/schnitzer-lab/nwb_schnitzer_lab.
	% inputs
		% image_masks - [x y z] matrix
		% roi_response_data - {1 N} cell with N = number of different signal traces for that algorithm. Make sure each signal trace matrix is in form of [nSignals nFrames].
		% algorithm - Name of the algorithm.
		% outputFilePath - file path to save NWB file to.
	% outputs
		%

	% changelog
		% 2020.07.01 [09:40:20] - Convert roi_response_data to cell if user inputs only a matrix.
		% 2020.09.15 [20:30:32] - Automatically creates directory where file is to be stored if it is not present.
		% 2021.02.01 [?15:14:40] - Function checks that yaml, matnwb, and nwb_schnitzer_lab loaded, else tries to load to make sure all dependencies are present and active.
		% 2021.02.01 [15:19:40] - Update `_external_programs` to call ciapkg.getDirExternalPrograms() to standardize call across all functions.
		% 2021.02.03 [12:34:06] - Added a check for inputs with a single signal and function returns as it is not supported.
		% 2021.03.20 [19:35:28] - Update to checking if only a single signal input.
	% TODO
		%

	[success] = ciapkg.nwb.saveNeurodataWithoutBorders(image_masks,roi_response_data,algorithm,outputFilePath,'passArgs', varargin);
end


function clear_class(class_name)
  % CLEAR_CLASS  clears all variables of a specific class
  %
  % clear_class(class_name)
  %
  % Inputs:
  %   class_name  name of class to clear as string
  %
  % See also: clear
  %

  % Get a cell list of all variable names as strings
  s_v = who;
  s_v
  % loop over variabls
  for ii = 1:numel(s_v)
    % Get this variable as string
    s_s = s_v{ii};
    s_s
    s_d = eval(s_s);
    class(s_d)
    if strcmp(class(s_d),class_name)
      fprintf('clearing: %s...',s_s);
      clear(s_s);
    end
  end

end


function fields = splitString(string, delimiter)
% Splits a string Perl style
%
% USAGE:
%
%     fields = splitString(string, delimiter)
%
% INPUTS:
%    string:       Either a single string or a cell array of strings
%    delimiter:    Splitting delimiter
%
% OUTPUT:
%    fields:       Either a single cell array of fields or a cell array of cell
%                  arrays of fields
%
% Default delimiter is '\s' (whitespace)
% Delimiters are perl regular expression style, e.g. '|' has to be expressed
% as '\|'
% Results are returned in the cell array fields
%
% .. Authors:  Markus Herrgard 07/14/04

if nargin < 2
    delimiter = '\s';
end

% Check if this is a list of strings or just a single string
if iscell(string)
    stringList = string;
    for i = 1:length(stringList)
        fields{i} = splitOneString(stringList{i}, delimiter);
    end
else
    fields = splitOneString(string, delimiter);
end

fields = columnVector(fields);

function fields = splitOneString(string, delimiter)
% Internal function that splits one string

[startIndex, endIndex] = regexp(string, delimiter);

if ~isempty(startIndex)
    cnt = 0;
    for i = 1:length(startIndex) + 1
        if i == 1
            if endIndex(i) > 1
                cnt = cnt + 1;
                fields{cnt} = string(1:endIndex(i) - 1);
            end
        elseif i == length(startIndex) + 1
            if startIndex(i - 1) < length(string)
                cnt = cnt + 1;
                fields{cnt} = string(startIndex(i - 1) + 1:end);
            end
        else
            cnt = cnt + 1;
            fields{cnt} = string(startIndex(i - 1) + 1:endIndex(i) - 1);
        end
    end
else
    fields{1} = string;
end

fieldsOut = {};
cnt = 0;
for i = 1:length(fields)
    if ~isempty(fields{i})
        cnt = cnt + 1;
        fieldsOut{cnt} = fields{i};
    end
end
fields = fieldsOut;


classdef SmoothingExponentComputer < handle

    properties (Access = protected)
        value
    end

    methods (Access = public, Static)

        function obj = create(cParams)
            f = SmoothingExponentComputerFactory();
            obj = f.create(cParams);
        end

    end

    methods (Access = public)

        function q = compute(obj)
           obj.computeExponent();
           q = obj.value;
        end

    end

    methods (Access = protected, Abstract)
       computeExponent(obj);
    end

end


function obj = setDimension(obj, dim)
    % Sets the dimension of the NLP variable vector
    %
    % Parameters:
    % dim: the dimension of the vector @type double

    assert(isscalar(dim) && dim >=0, 'The dimension must be a scalar positive value.');

    obj.Dimension = dim;

end


function json=moxunit_fieldtrip_util_struct2json(s)
% convert struct to json string representation
%
% json=moxunit_fieldtrip_util_struct2json(s)
%
% Inputs:
%   s                   struct for which the values must all be strings,
%                       or scalar logical or double arrays
%
% Output:
%   json                JSON string representation of the input
%

    if ~isstruct(s)
        error('input must be a struct');
    end

    keys=fieldnames(s);
    n=numel(keys);
    args=cell(2,n);
    for k=1:n
        key=keys{k};
        raw_value=s.(key);

        if ischar(raw_value)
            value=sprintf('"%s"',escape(raw_value));

        elseif isnumeric(raw_value) && numel(raw_value)==1
            value=sprintf('%d',raw_value);

        elseif islogical(raw_value) && numel(raw_value)==1
            if raw_value
                value='true';
            else
                value='false';
            end

        else
            error('Unsupported input for key %s: <class %s>',...
                            key, class(raw_value));
        end

        args{1,k}=sprintf('"%s":%s',key,value);

        if k<n
            sep=',';
        else
            sep='';
        end
        args{2,k}=sep;
    end

    json=sprintf('{%s}',sprintf('%s',args{:}));

function s=escape(s)
    s=regexprep(s,'"','\\"');


function generateDoc
%generateDoc Publish the example scripts in the doc directory

%   Steven L. Eddins
%   Copyright 2008-2009 The MathWorks, Inc.

doc_dir = fullfile(fileparts(which('mtest')), '..', 'doc');
addpath(doc_dir);
cd(doc_dir)
mfiles = dir('*.m');
for k = 1:numel(mfiles)
    publish(mfiles(k).name);
    cd(doc_dir)
end