function res = saveastiff(data, path, options)
% options.color
%   : true or FALSE
%   : If this is true, third dimension should be 3 and the data is saved as a color image.
% options.compress
%   : 'no', 'lzw', 'jpeg' or 'adobe'.
%     Compression type.
%       'no'    : Uncompressed(Default)
%       'lzw'   : lossless LZW
%       'jpeg'  : lossy JPEG (When using JPEG compression, ImageWidth,
%                 ImageLength, and RowsPerStrip must be multiples of 16.)
%       'adobe' : lossless Adobe-style
% options.message
%   : TRUE or false.
%     If this is false, all messages are skipped.
% options.append
%   : true or FALSE
%     If path is exist, the data is appended to an existing file.
%     If path is not exist, this options is ignored.
% options.overwrite
%   : true or FALSE
%     Overwrite to an existing file.
% options.big
%   : true or FALSE,
%     Use 64 bit addressing and allows for files > 4GB
%
% Defalut value of 'options' is
%     options.color     = false;
%     options.compress  = 'no';
%     options.message   = true;
%     options.append    = false;
%     options.overwrite = false;
%     options.big       = false;
%
% res : Return value. It is 0 when the function is finished with no error.
%       If an error is occured in the function, it will have a positive
%       number (error code).
%
% Copyright (c) 2012, YoonOh Tak
% All rights reserved.
%
% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are
% met:
%
%     * Redistributions of source code must retain the above copyright
%       notice, this list of conditions and the following disclaimer.
%     * Redistributions in binary form must reproduce the above copyright
%       notice, this list of conditions and the following disclaimer in
%       the documentation and/or other materials provided with the distribution
%     * Neither the name of the Gwangju Institute of Science and Technology (GIST), Republic of Korea nor the names
%       of its contributors may be used to endorse or promote products derived
%       from this software without specific prior written permission.
%
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
% POSSIBILITY OF SUCH DAMAGE.

tStart = tic;
errcode = 0;
try
%% Init options parameter
if nargin < 3 % Use default options
    options.color = false;
    options.compress = 'no';
    options.message = true;
    options.append = false;
    options.overwrite = false;
end
if ~isfield(options, 'message'),   options.message   = true; end
if ~isfield(options, 'append'),    options.append    = false; end
if ~isfield(options, 'compress'),  options.compress  = 'no';  end
if ~isfield(options, 'color'),     options.color     = false; end
if ~isfield(options, 'overwrite'), options.overwrite = false; end
if  isfield(options, 'big') == 0,  options.big       = false; end

if isempty(data), errcode = 1; assert(false); end
if (options.color == false && ndims(data) > 3) || ...
   (options.color == true && ndims(data) > 4)
    % Maximum dimension of a grayscale image is 3 of [height, width, frame]
    % Maximum dimension of a color image is 4 of [height, width, color, frame]
    errcode = 2; assert(false);
end

%% Get image informations
% http://www.awaresystems.be/imaging/tiff/tifftags/photometricinterpretation.html
if ~options.color
    if ndims(data) >= 4, errcode = 2; assert(false); end;
    [height, width, depth] = size(data);
    tagstruct.Photometric = Tiff.Photometric.MinIsBlack;
%     tagstruct.Photometric = Tiff.Photometric.MinIsWhite;
%     tagstruct.Photometric = Tiff.Photometric.Mask;
%     tagstruct.Photometric = Tiff.Photometric.Separated;
else
    if ndims(data) >= 5, errcode = 2; assert(false); end;
    [height, width, cc, depth] = size(data); % cc: color channels. 3: rgb, 4: rgb with alpha channel
    if cc ~= 3 && cc ~= 4, errcode = 3; assert(false); end;
    tagstruct.Photometric = Tiff.Photometric.RGB;
%     tagstruct.Photometric = Tiff.Photometric.CIELab;
%     tagstruct.Photometric = Tiff.Photometric.ICCLab;
%     tagstruct.Photometric = Tiff.Photometric.ITULab;
%     (Unsupported)tagstruct.Photometric = Tiff.Photometric.Palette;
%     (Unsupported)tagstruct.Photometric = Tiff.Photometric.YCbCr;
end
tagstruct.ImageLength = height;
tagstruct.ImageWidth = width;
tagstruct.PlanarConfiguration = Tiff.PlanarConfiguration.Chunky; % (RGB RGB,RGB RGB,RGB RGB), http://www.awaresystems.be/imaging/tiff/tifftags/planarconfiguration.html
% tagstruct.PlanarConfiguration = Tiff.PlanarConfiguration.Separate; % (RRR RRR, GGG GGG, BBB BBB), % http://www.awaresystems.be/imaging/tiff/tifftags/planarconfiguration.html

%% Complex number
% http://www.awaresystems.be/imaging/tiff/tifftags/samplesperpixel.html
if ~options.color && isreal(data) % Grayscale image with real numbers
    tagstruct.SamplesPerPixel = 1;
    data = reshape(data, height, width, 1, depth);
elseif ~options.color && ~isreal(data) % Grayscale image with complex numbers
    tagstruct.SamplesPerPixel = 2;
    data = reshape([real(data) imag(data)], height, width, 2, depth);
elseif options.color && isreal(data) % Color image with real numbers
    tagstruct.SamplesPerPixel = cc;
    if cc == 4
        tagstruct.ExtraSamples = Tiff.ExtraSamples.AssociatedAlpha; % The forth channel is alpha channel
    end
    data = reshape(data, height, width, cc, depth);
elseif options.color && ~isreal(data) % Color image with complex numbers
    tagstruct.SamplesPerPixel = cc * 2;
    if cc == 3
        tagstruct.ExtraSamples = repmat(Tiff.ExtraSamples.Unspecified, 1, 3); % 3(real)+3(imag) = 6 = 3(rgb) + 3(Extra)
    else
        tagstruct.ExtraSamples = repmat(Tiff.ExtraSamples.Unspecified, 1, 5); % 4(real)+4(imag) = 8 = 3(rgb) + 5(Extra)
    end
    data = reshape([real(data) imag(data)], height, width, cc*2, depth);
end

%% Image compression
% http://www.awaresystems.be/imaging/tiff/tifftags/compression.html
switch lower(options.compress)
    case 'no'
        tagstruct.Compression = Tiff.Compression.None;
    case 'lzw'
        tagstruct.Compression = Tiff.Compression.LZW;
    case 'jpeg'
        tagstruct.Compression = Tiff.Compression.JPEG;
    case 'adobe'
        tagstruct.Compression = Tiff.Compression.AdobeDeflate;
    otherwise
        % Use tag nubmer in http://www.awaresystems.be/imaging/tiff/tifftags/compression.html
        tagstruct.Compression = options.compress;
end

%% Sample format
% http://www.awaresystems.be/imaging/tiff/tifftags/sampleformat.html
switch class(data)
    % Unsupported Matlab data type: char, logical, cell, struct, function_handle, class.
    case {'uint8', 'uint16', 'uint32'}
        tagstruct.SampleFormat = Tiff.SampleFormat.UInt;
    case {'int8', 'int16', 'int32'}
        tagstruct.SampleFormat = Tiff.SampleFormat.Int;
        if options.color
            errcode = 4; assert(false);
        end
    case {'single', 'double', 'uint64', 'int64'}
        tagstruct.SampleFormat = Tiff.SampleFormat.IEEEFP;
    otherwise
        % (Unsupported)Void, ComplexInt, ComplexIEEEFP
        errcode = 5; assert(false);
end

%% Bits per sample
% http://www.awaresystems.be/imaging/tiff/tifftags/bitspersample.html
switch class(data)
    case {'uint8', 'int8'}
        tagstruct.BitsPerSample = 8;
    case {'uint16', 'int16'}
        tagstruct.BitsPerSample = 16;
    case {'uint32', 'int32'}
        tagstruct.BitsPerSample = 32;
    case {'single'}
        tagstruct.BitsPerSample = 32;
    case {'double', 'uint64', 'int64'}
        tagstruct.BitsPerSample = 64;
    otherwise
        errcode = 5; assert(false);
end

%% Rows per strip
maxstripsize = 8*1024;
tagstruct.RowsPerStrip = ceil(maxstripsize/(width*(tagstruct.BitsPerSample/8)*size(data,3))); % http://www.awaresystems.be/imaging/tiff/tifftags/rowsperstrip.html
if tagstruct.Compression == Tiff.Compression.JPEG
    tagstruct.RowsPerStrip = max(16,round(tagstruct.RowsPerStrip/16)*16);
end

%% Overwrite check
if exist(path, 'file') && ~options.append
    if ~options.overwrite
        errcode = 6; assert(false);
    end
end

%% Save path configuration
path_parent = pwd;
[pathstr, fname, fext] = fileparts(path);
if ~isempty(pathstr)
    if ~exist(pathstr, 'dir')
        mkdir(pathstr);
    end
    cd(pathstr);
end

%% Write image data to a file
file_opening_error_count = 0;
while ~exist('tfile', 'var')
    try
        if ~options.append % Make a new file
            s=whos('data');
            if s.bytes > 2^32-1 || options.big
                tfile = Tiff([fname, fext], 'w8'); % Big Tiff file
            else
                tfile = Tiff([fname, fext], 'w');
            end
        else
            if ~exist([fname, fext], 'file') % Make a new file
                s=whos('data');
                if s.bytes > 2^32-1 || options.big
                    tfile = Tiff([fname, fext], 'w8'); % Big Tiff file
                else
                    tfile = Tiff([fname, fext], 'w');
                end
            else % Append to an existing file
                tfile = Tiff([fname, fext], 'r+');
                while ~tfile.lastDirectory(); % Append a new image to the last directory of an exiting file
                    tfile.nextDirectory();
                end
                tfile.writeDirectory();
            end
        end
    catch
        file_opening_error_count = file_opening_error_count + 1;
        pause(0.1);
        if file_opening_error_count > 5 % automatically retry to open for 5 times.
            reply = input('Failed to open the file. Do you wish to retry? Y/n: ', 's');
            if isempty(reply) || any(upper(reply) == 'Y')
                file_opening_error_count = 0;
            else
                errcode = 7;
                assert(false);
            end
        end
    end
end

for d = 1:depth
    tfile.setTag(tagstruct);
    tfile.write(data(:, :, :, d));
    if d ~= depth
       tfile.writeDirectory();
    end
end

tfile.close();
if exist('path_parent', 'var'), cd(path_parent); end

tElapsed = toc(tStart);
if options.message
    display(sprintf('The file was saved successfully. Elapsed time : %.3f s.', tElapsed));
end

catch exception
%% Exception management
    if exist('tfile', 'var'), tfile.close(); end
    switch errcode
        case 1
            if options.message, error '''data'' is empty.'; end;
        case 2
            if options.message, error 'Data dimension is too large.'; end;
        case 3
            if options.message, error 'Third dimesion (color depth) should be 3 or 4.'; end;
        case 4
            if options.message, error 'Color image cannot have int8, int16 or int32 format.'; end;
        case 5
            if options.message, error 'Unsupported Matlab data type. (char, logical, cell, struct, function_handle, class)'; end;
        case 6
            if options.message, error 'File already exists.'; end;
        case 7
            if options.message, error(['Failed to open the file ''' path '''.']); end;
        otherwise
            if exist('fname', 'var') && exist('fext', 'var')
                delete([fname fext]);
            end
            if exist('path_parent', 'var'), cd(path_parent); end
            rethrow(exception);
    end
    if exist('path_parent', 'var'), cd(path_parent); end
end
res = errcode;
end


disp('1 end ')
TheImportantValue='';
if (1>2)
	end
disp('2')
% blah blah end FF
disp('3')
disp('4')

a=['a' 'b' 'asd'...
    'c' 'd'];

if (1)
    while(3<2)
        disp('!');
    end
    for i=0:100
        fprintf(num2str(i));
    end
    disp('!!!')
end

disp('5')
disp('6')


% APM Specify Options
%
% response = apm_option(server,app,name,value)
%
% Function apm_option sends an option specification
%   to the APM server with the following arguments:
%
%   server = address of server
%      app = application name
%     name = option name
%    value = option value
%
% A response is returned indicating whether the option
%   was successfully recorded
%
% Either global options (NLC.option_name) or parameter/variable
%   options (NAME.option_name) are able to be specified with this
%   function. A full list of available options with their default
%   values are available here:
%
% Global Options:
%    http://apmonitor.com/wiki/index.php/Main/DbsGlobal
%
% Variable Options:
%    http://apmonitor.com/wiki/index.php/Main/DbsVariable
%
function response = apm_option(server,app,name,value)
   app = lower(deblank(app));
   aline = ['option ' deblank(char(name)) ' = ' num2str(value)];
   response = apm(server,app,aline);


function cvx_pop( p, pid )

global cvx___
try
    np = length( cvx___.problems );
catch
    np = 0;
end
if nargin == 0,
    p = np;
    if p == 0,
        if np == 0, return; end
    elseif p > np,
        return
    else
        pstr = cvx___.problems( p );
        pid = pstr.id;
    end
elseif p > np
    pid = 0;
elseif p == 0
    pid = -1;
else
    pstr = cvx___.problems( p );
    if pstr.id ~= pid, pid = 0; end
end
s1 = evalin( 'caller', 'who' );
s2 = evalin( 'caller', 'cellfun(@eval,who,''UniformOutput'',false)' );
s2 = cellfun( @cvx_id, s2 );
do_timer = false;
if pid <= 0,
    do_erase = ~isempty( cvx___.problems );
    p = +do_erase;
    tt = s2 >= 0;
elseif ~pstr.finished
    tt = s2 >= pid;
    do_erase = true;
elseif isnan( pstr.direction )
    tt = s2 == pid;
    do_erase = false;
elseif pstr.checkpoint(4) >= pstr.checkpoint(1),
    tt = s2 > pid;
    temp = sprintf( '%s,', s1{tt} );
    if ~isempty( temp ),
        temp = temp(1:end-1);
        evalin( 'caller', sprintf( '[%s]=cvx_values(%s);', temp, temp ) );
    end
    do_timer = true;
    tt = s2 == pid;
    do_erase = true;
else
    tt = s2 >= pid;
    do_erase = false;
    if ~isempty( pstr.objective ),
        tt = tt & ( s2 ~= cvx_id( pstr.objective ) );
    end
end
s1 = s1(tt);
if ~isempty(s1)
    temp = sprintf( ' %s', s1{:} );
    evalin( 'caller', [ 'clear', temp ] );
end
if do_erase
    cvx_erase( p );
end
if p > 1
    cvx___.problems( p : end ) = [];
else
    cvx___.problems = [];
    cvx_clearpath( 1 );
end
if pid == 0
    cvx_throw( 'Internal CVX data corruption. Please rebuild your model.' );
end
if do_timer
    tfin = tic;
    try
        tval = tfin - pstr.tictime(1);
        cvx___.timers(2) = cvx___.timers(2) + tval;
        cvx___.timers(3) = cvx___.timers(3) + ( tfin - pstr.tictime(2) );
        for q = 1 : p - 1,
            cvx___.problems(q).tictime = cvx___.problems(q).tictime + tval;
        end
    catch
        tval = double(tfin) - double(pstr.tictime(1));
        cvx___.timers(2) = double(cvx___.timers(2)) + double(tval);
        cvx___.timers(3) = double(cvx___.timers(3)) + ( double(tfin) - double(pstr.tictime(2)) );
        for q = 1 : p - 1,
            cvx___.problems(q).tictime = double(cvx___.problems(q).tictime) + tval;
        end
    end
end

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


function varargout = myComplement(MENUS,menu_defined)
tf = logical(zeros(1,length(MENUS)));
for menu=1:length(menu_defined)
    tf = tf + strcmp(menu_defined(menu),MENUS);
end
varargout{1} = MENUS(logical(1-tf));


% undo(n=1) removes the last n figure items added

function undo(n)

if nargin==0 n=1; end
if ischar(n) n = sscanf(n,'%i'); end

if n<1 error(['Not undoing ' int2str(n) ' items']); end
g=get(gca,'children');
L=length(g);
for i=1:n
	delete(g(i));
end


function varargout = pm_get_defaults(defstr, varargin)

% FORMAT defval = pm_get_defaults(defstr)
% Return the defaults value associated with identifier "defstr".
% Currently, this is a '.' subscript reference into the global
% "defaults" variable defined in spm_defaults.m.
%
% FORMAT spm_get_defaults(defstr, defval)
% Sets the defaults value associated with identifier "defstr". The new
% defaults value applies immediately to:
% * new modules in batch jobs
% * modules in batch jobs that have not been saved yet
% This value will not be saved for future sessions of SPM. To make
% persistent changes, edit spm_defaults.m.
%__________________________________________________________________________
% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging

% Chloe Hutton
% $Id: pm_get_defaults.m 4572 2011-11-23 17:35:10Z chloe $
% ---------------------------------------------------------------------
global pm_def
if isempty(pm_def)
    pm_defaults;
    pm_def.sessname='session';
    pm_def.pedir=2;
elseif ~isfield(pm_def,'pedir');
    pm_def.pedir=2;
elseif ~isfield(pm_def,'sessname');
    pm_def.sessname='session';
end

[default_file_path, tmpname] = fileparts(mfilename('fullpath'));
pm_def.defaultsfilename{1} = sprintf('%s%s%s',default_file_path,filesep,'pm_defaults.m');
if ischar(pm_def.MFLAGS.TEMPLATE)
    pm_def.MFLAGS.TEMPLATE = {pm_def.MFLAGS.TEMPLATE};
end
% tmpTEMPLATE = pm_def.MFLAGS.TEMPLATE;
% pm_def.MFLAGS.TEMPLATE=[];
% pm_def.MFLAGS.TEMPLATE{1}=tmpTEMPLATE;

% construct subscript reference struct from dot delimited tag string
tags = textscan(defstr,'%s', 'delimiter','.');
subs = struct('type','.','subs',tags{1}');

if nargin == 1
    varargout{1} = subsref(pm_def, subs);
else
    pm_def = subsasgn(pm_def, subs, varargin{1});
end


function dataset = voc2012_test(dataset, usage, use_flip)
% Pascal voc 2012 test set
% set opts.imdb_train opts.roidb_train
% or set opts.imdb_test opts.roidb_train

% change to point to your devkit install
devkit                      = voc2012_devkit();

switch usage
    case {'train'}
        dataset.imdb_train    = {  imdb_from_voc(devkit, 'test', '2012', use_flip) };
        dataset.roidb_train   = cellfun(@(x) x.roidb_func(x), dataset.imdb_train, 'UniformOutput', false);
    case {'test'}
        dataset.imdb_test     = imdb_from_voc(devkit, 'test', '2012', use_flip) ;
        dataset.roidb_test    = dataset.imdb_test.roidb_func(dataset.imdb_test);
    otherwise
        error('usage = ''train'' or ''test''');
end

end


classdef ciatah < dynamicprops
	% Performs analysis on calcium imaging data. Also performs analysis on behavior (response signals) compared to stimulus or other continuous signals during an imaging session.
	% Biafra Ahanonu
	% started: 2014.07.31 (2020.10.25 [22:30:29])
	% This is a re-write of my old code from controllerAnalysis. Encapsulating the functions and variables as methods and properties in a class should allow easier maintenance/flexibility.
	% inputs
		%
	% outputs
		%

	% changelog
		% updated: 2017.01.15 [01:31:54]
		% 2019.05.08 [10:59:59] - Added check for required toolboxes used in class.
		% 2019.07.25 [09:39:16] - Updated loading so that users only need to be in the root calciumImagingAnalysis path but required folders do not need to be loaded.
		% 2019.08.20 [09:33:32] - Improved loading of folders and Miji to save time.
		% 2019.08.30 [12:58:37] - Added Java heap space memory check on initialization.
		% 2019.09/10 - Added GUI font option and signalExtractionTraceOutputType.
		% 2019.10.15 [21:57:45] - Improved checking for directories that should not be loaded, remove need for verLessThan('matlab','9.0') check.
		% 2019.12.22 [09:01:38] - Re-vamped selection list dialog boxes for methods, cell-extraction, folders, etc. Now loads as a figure and for some (like methods list) has tooltips that describe each list item.
		% 2020.03.23 [22:36:36] - Re-vamped selection dialog so everything is on a single panel and users can interactively alter how they filter folders.
		% 2020.05.07 [15:13:53] - Refactored a bit to remove methods that do not need to be in the main class definition to allow easier updating later.
		% 2021.03.25 [20:15:31] - Use ciapkg.getDir() to ensure using root directory consistently rather than ciapkgRoot.
		% 2021.03.25 [22:46:38] - Moved many smaller helper functions in ciatah.m to their own M-files to make maintenance easier.
		% 2021.06.18 [20:28:28] - Added modelVarsFromFilesCheck support.
		% 2021.06.30 [11:14:12] - Added FRAMES_PER_SECOND_PLAYBACK.
		% 2021.08.08 [19:30:20] - Updated to handle CIAtah v4.0 switch to all functions inside ciapkg package.
		% 2021.10.20 [21:46:52] - Added fontSizeGuiScale.
		% 2022.02.09 [23:51:18] - Misc code fixes to conform to better Matlab language standards.
	% TODO
		%

	% dynamicprops is a subclass of handle, allowing addition of properties

	properties(GetAccess = 'public', SetAccess = 'public')
		% public read and write access.

		% FPS of movie(s) being analyzed
		FRAMES_PER_SECOND =  5;
		% FPS of movie playback
		FRAMES_PER_SECOND_PLAYBACK =  20;
		% Int: what factor temporally are analyzed movie from raw data
		DOWNSAMPLE_FACTOR =  4;
		% Float: estimated um per pixel
		MICRON_PER_PIXEL =  2.51; % 2.37;

		% Int: set the default UI font size
		fontSizeGui = 10;

		% Float: allows font size in GUI to be scaled
		fontSizeGuiScale = 1;

		defaultObjDir = pwd;
		classVersion = ciapkg.version();
		serverPath = '';
		privateSettingsPath = [ciapkg.getDir() filesep 'private' filesep 'settings' filesep 'privateLoadBatchFxns.m'];
		% place where functions can temporarily story user settings
		functionSettings = struct(...
			'null', NaN...
		);

		% user information
		userName = 'USA';
		% Taxonomic species name, defaults to mouse.
		species = 'mus musculus';

		% counters and stores
		% index of current folder
		fileNum = 1;
		% same as fileNum, will transfer to this since more clear
		folderNum = 1;
		% number to current stimulus index
		stimNum = 1;

		% Github repo path, for updating the repository code
		githubUrl = 'https://github.com/bahanonu/calciumImagingAnalysis';
		% Folder to put downloaded external programs
		externalProgramsDir = ciapkg.getDirExternalPrograms();

		% String: name of the analysis file to put in a folder to indicate to other computers the current computer is analyzing the folder and they should skip
		concurrentAnalysisFilename = '_currentlyAnalyzingFolderCheck.mat';

		% Cell array strings: List of methods to fast track folder to analyze dialog or skip altogether
		methodExcludeList = {'showVars','showFolders','setMainSettings','loadDependencies','saveObj','setStimulusSettings','modelDownsampleRawMovies','setMovieInfo','setup','update'};
		methodExcludeListVer2 = {'modelEditStimTable','behaviorProtocolLoad','modelPreprocessMovie','modelModifyMovies','removeConcurrentAnalysisFiles'};
		methodExcludeListStimuli = {'modelVarsFromFiles'};

		% 0 = load variables from disk, reduce RAM usage. 1 = load from disk to ram, faster for analysis.
		loadVarsToRam = 0;
		% show GUI for view functions?
		guiEnabled = 1;
		% indices for folders to analyze, [] = all
		foldersToAnalyze = [];
		% indices for stimuli to analyze, [] = all
		discreteStimuliToAnalyze = [];
		% io settings
		fileFilterRegexp = 'crop';
		% Regular expression for alternative file
		fileFilterRegexpAlt = 'crop';
		% Regular expression for alternative file during cell extraction
		fileFilterRegexpAltCellExtraction = '';
		% raw movie
		fileFilterRegexpRaw = 'concat';
		% behavior video regexp
		behaviorVideoRegexp = '';
		% loop over all files during analysis? 'individual' or 'group'
		analysisType  = 'group';
		% 1 = perform certain analysis on dF/F instead of peaks
		dfofAnalysis = 0;
		% 'filtered' returns auto/manually filtered signals/images, 'raw' returns raw
		modelGetSignalsImagesReturnType = 'filtered'
		% name of input dataset name for preprocessing
		inputDatasetName = '/1';
		% name of output dataset name for preprocessing
		outputDatasetName = '/1';
		%
		stimTriggerOnset = 0;
		% paths for specific types of files
		currentDateTimeSessionStr = datestr(now,'yyyymmdd_HHMMSS_FFF','local');
		currentDateTimeStr = datestr(now,'yyyymmdd','local');
		picsSavePath = [ciapkg.getDir() filesep 'private' filesep 'pics' filesep datestr(now,'yyyymmdd','local') filesep];
		dataSavePath = [ciapkg.getDir() filesep 'private' filesep 'data' filesep datestr(now,'yyyymmdd','local') filesep];
		dataSavePathFixed = [ciapkg.getDir() filesep 'private' filesep 'data' filesep];
		logSavePath = [ciapkg.getDir() filesep 'private' filesep 'logs' filesep datestr(now,'yyyymmdd','local') filesep];
		settingsSavePath = [ciapkg.getDir() filesep 'private' filesep 'settings'];
		%
		dataSaveFilenameModifier = '';
		% table save
		delimiter = ',';
		% name of i/o HDF5 dataset names
		hdf5Datasetname = '/1';
		% type of images to save analysis as '-dpng','-dmeta','-depsc2'
		imgSaveTypes = {'-dpng'};
		% Custom subject regexp find string to pass to getFileInfo
		subjectRegexp = '(m|M|f|F|Mouse|mouse|Mouse_|mouse_)\d+';
		% colormap to be used
		% colormap = customColormap([]);
		% colormap = customColormap({[0 0 0.7],[1 1 1],[0.7 0 0]});
		% colormap = customColormap({[0 0 1],[1 1 1],[0.5 0 0],[1 0 0]});
		% colormapAlt = customColormap({[0 0 0.7],[1 1 1],[0.7 0 0]});
		% colormapAlt2 = diverging_map(linspace(0,1,100),[0 0 0.7],[0.7 0 0]);
		colormap = parula(100);
		colormapAlt = parula(100);
		colormapAlt2 = parula(100);
		% colormap = customColormap({[27 52 93]/256,[1 1 1],[106 41 50]/256},'nPoints',50);
		% use for stimulus related viewing functions
		% frames before/after stimulus to look
		timeSequence = -25:25;
		postStimulusTimeSeq = 0:10;
		% bin analysis, integer only
		binDownsampleAmount = 1;
		% number of standard deviation for threshold crossing
		numStdsForThresh = 2.5;
		%
		stimulusTableValueName = 'frameSessionDownsampled';
		stimulusTableFrameName = 'frameSessionDownsampled';
		stimulusTableResponseName = 'response';
		stimulusTableTimeName = 'time';
		stimulusTableSessionName = 'trial';

		% methods
		currentMethod = 'modelAddNewFolders';

		% Region analysis
		regionModSaveStr = '_regionModSelectUser.mat'

		usrIdxChoiceStr = {...
			'PCAICA',...
			'CNMF',...
			'CNMFE',...
			'ROI',...
			'CELLMax',...
			'EXTRACT',...
			'EM'};
		usrIdxChoiceDisplay = {...
			'PCAICA (Mukamel, 2009)',...
			'CNMF (Giovannucci, 2019)',...
			'CNMF-E (Zhou, 2018)',...
			'ROI',...
			'CELLMax (Kitch/Ahanonu)',...
			'EXTRACT (Inan, 2021)',...
			'CELLMax [EM] (Kitch/Ahanonu)'...
			};

		% PCAICA, EM, EXTRACT, CNMF, CNMFE
		signalExtractionMethod = 'PCAICA';
		% signalExtractionMethod = 'EM';
		% Int: indicates which trace output to use from 1 = primary trace, 2 = secondary trace, etc.
		signalExtractionTraceOutputType = 1;

		extractionMethodSaveStr = struct(...
			'PCAICA', '_pcaicaAnalysis',...
			'EM', '_emAnalysis',...
			'CELLMax', '_cellmaxAnalysis',...
			'EXTRACT', '_extractAnalysis',...
			'CNMF', '_cnmfAnalysis',...
			'CNMFE', '_cnmfeAnalysis',...
			'ROI', '_roiAnalysis'...
		);
		extractionMethodStructSaveStr = struct(...
			'PCAICA', '_pcaicaAnalysis.mat',...
			'EM', '_emAnalysis.mat',...
			'CELLMax', '_cellmaxAnalysis.mat',...
			'EXTRACT', '_extractAnalysis.mat',...
			'CNMF', '_cnmfAnalysis.mat',...
			'CNMFE', '_cnmfeAnalysis.mat',...
			'ROI', '_roiAnalysis.mat'...
		);
		extractionMethodSortedSaveStr = struct(...
			'PCAICA', '_ICdecisions.mat',...
			'EM', '_emAnalysisSorted.mat',...
			'CELLMax', '_cellmaxAnalysisSorted.mat',...
			'EXTRACT', '_extractAnalysisSorted.mat',...
			'CNMF', '_cnmfAnalysisSorted.mat',...
			'CNMFE', '_cnmfeAnalysisSorted.mat',...
			'ROI', '_roiAnalysisSorted.mat'...
		);
		extractionMethodClassifierSaveStr = struct(...
			'PCAICA', '_ICclassifierDecisions.mat',...
			'EM', '_emAnalysisClassifierDecisions.mat',...
			'CELLMax', '_cellmaxAnalysisClassifierDecisions.mat',...
			'EXTRACT', '_extractAnalysisClassifierDecisions.mat',...
			'CNMF', '_cnmfAnalysisClassifierDecisions.mat',...
			'CNMFE', '_cnmfeAnalysisClassifierDecisions.mat',...
			'ROI', '_roiAnalysisClassifierDecisions.mat'...
		);
		extractionMethodValidVarname = struct(...
			'PCAICA', 'valid',...
			'EM', 'validCellMax',...
			'CELLMax', 'validCellMax',...
			'EXTRACT', 'validEXTRACT',...
			'CNMF', 'validCNMF',...
			'CNMFE', 'validCNMFE',...
			'ROI', 'validROI'...
		);
		extractionMethodStructVarname = struct(...
			'PCAICA', 'pcaicaAnalysisOutput',...
			'EM', 'emAnalysisOutput',...
			'CELLMax', 'cellmaxAnalysisOutput',...
			'EXTRACT', 'extractAnalysisOutput',...
			'CNMF', 'cnmfAnalysisOutput',...
			'CNMFE', 'cnmfeAnalysisOutput',...
			'ROI', 'roiAnalysisOutput'...
		);

		% PCAICA names
		rawPCAICAStructSaveStr = '_pcaicaAnalysis.mat';
		rawICfiltersSaveStr = '_ICfilters.mat';
		rawICtracesSaveStr = '_ICtraces.mat';
		sortedICfiltersSaveStr = '_ICfilters_sorted.mat';
		sortedICtracesSaveStr = '_ICtraces_sorted.mat';
		sortedICdecisionsSaveStr = '_ICdecisions.mat';
		classifierICdecisionsSaveStr = '_ICclassifierDecisions.mat';
		structPCAICAVarname = 'pcaicaAnalysisOutput';
		validPCAICAStructVarname = 'valid';
		% ROI names
		rawROIStructSaveStr = '_roiAnalysis.mat';
		sortedRoiStructSaveStr = '_roiAnalysisSorted.mat';
		structROIVarname = 'roiAnalysisOutput';
		classifierRoiStructSaveStr = '_roiAnalysisClassifierDecisions.mat';
		rawROItracesSaveStr = '_ROItraces.mat';
		validRoiStructVarname = 'validROI';
		% EM names
		rawEMStructSaveStr = '_emAnalysis.mat';
		sortedEMStructSaveStr = '_emAnalysisSorted.mat';
		classifierEMStructSaveStr = '_emAnalysisClassifierDecisions.mat';
		structEMVarname = 'emAnalysisOutput';
		validEMStructVarname = 'validCellMax';
		% EXTRACT names
		rawEXTRACTStructSaveStr = '_extractAnalysis.mat';
		sortedEXTRACTStructSaveStr = '_extractAnalysisSorted.mat';
		classifierEXTRACTStructSaveStr = '_extractAnalysisClassifierDecisions.mat';
		validEXTRACTStructVarname = 'validEXTRACT';
		structEXTRACTVarname = 'extractAnalysisOutput';
		% CNMF names
		rawCNMFStructSaveStr = '_cnmfAnalysis.mat';
		sortedCNMFStructSaveStr = '_cnmfAnalysisSorted.mat';
		classifierCNMFStructSaveStr = '_cnmfAnalysisClassifierDecisions.mat';
		validCNMFStructVarname = 'validCNMF';
		structCNMRVarname = 'cnmfAnalysisOutput';

		% NWB-related properties
		% Whether to write or load NWB files, e.g. from modelExtractSignalsFromMovie
		nwbLoadFiles = 0;
		% Str: sub-folder where NWB files are stored. Leave blank to load from current folder.
		nwbFileFolder = 'nwbFiles';
		% Str: blank, use calciumImagingAnalysis regexp, else force use of this regexp for NWB files
		nwbFileRegexp = '';
		% Name of H5 group for movies, images, and signal series in NWB files
		nwbGroupImages = '/processing/ophys/ImageSegmentation/PlaneSegmentation';
		nwbGroupSignalSeries = '/processing/ophys/Fluorescence/RoiResponseSeries';
		nwbGroupMovie = '/acquisition/TwoPhotonSeries/data';

		settingOptions = struct(...
			'analysisType',  {{'group','individual'}},...
			'loadVarsToRam', {{0,1}},...
			'guiEnabled', {{0,1}},...
			'dfofAnalysis', {{0,1}},...
			'picsSavePath', {{['private' filesep 'pics' filesep datestr(now,'yyyymmdd','local') filesep]}},...
			'delimiter', {{',','tab'}},...
			'imgSaveTypes', {{'-dpng','-dmeta','-depsc2'}}...
		);

		filterImageOptions = struct(...
			'minNumPixels', 10,...
			'maxNumPixels', 100,...
			'SNRthreshold', 1.45,...
			'minPerimeter', 5,...
			'maxPerimeter', 50,...
			'minSolidity', 0.8,...
			'minEquivDiameter', 3,...
			'maxEquivDiameter', 30,...
			'slopeRatioThreshold', 0.04...
		);

		downsampleRawOptions = struct(...
			'folderListInfo','USER_PATH',...
			'downsampleSaveFolder','USER_PATH',...
			'downsampleSrcFolder','USER_PATH',...
			'downsampleFactor','4',...
			'fileFilterRegexp','(recording.*.hdf5|recording.*.tif|.*.isxd)',...
			'datasetName','/images',...
			'maxChunkSize','25000',...
			'srcFolderFilterRegexp','201\d',...
			'srcSubfolderFileFilterRegexp','(recording.*.(txt|xml)|*.gpio)',...
			'srcSubfolderFileFilterRegexpExt','(.txt|.xml|.gpio)',...
			'downsampleSaveFolderTwo','',...
			'downsampleFactorTwo','2',...
			'outputDatasetName','/1'...
		);

		% Pre-processing options
		% Empty by default, if user opts to keep pre-process settings, this is saved as a struct.
		preprocessSettings = [];
		saveLoadPreprocessingSettings = 1;
		motionCorrectionRefFrame = 100;

		% io folders
		inputFolders = {};
		videoDir = '';
		videoSaveDir = '';
		trackingDir = '';
		stimulusDir = '';
		% if want to automatically save object to a specific location.
		objSaveLocation = [];

		% signal related
		% either the raw signals (traces) or
		rawSignals = {};
		% secondary either the raw signals (traces) or
		rawSignals2 = {};
		%
		rawImages = {};
		% computed signal peaks/locations, to reduce computation in functions
		signalPeaks = {};
		%
		signalPeaksArray = {};
		% computed centroid locations {[x y],...}
		objLocations = {};
		% mean correlation coefficient between image and movie
		imageMovieCorr = {};
		% cellmaps indicating which cells were filtered
		rawImagesFiltered = {};
		% structure of classifier structures, each field in the property should be named after the folder's subject, e.g. classifierStructs.m667 is the classification structure for subject m667
		classifierStructs = {};
		% structure of classifier structures for each folder, e.g. after running classification on each
		classifierFolderStructs = {};
		% cell array with {signalNo}.signalFeatures, {signalNo}.imageFeatures
		classifierFeatures = {};
		% cell array with {signalNo}.signalFeatures, {signalNo}.imageFeatures
		classifierImageFeaturesNames = {'Eccentricity','EquivDiameter','Area','Orientation','Perimeter','Solidity'};
		% structure for all valid classifications to go
		valid = {};
		% Automated or manual classification
		validManual = {};
		% from automated classification
		validAuto = {};
		% valid cells based on a regional modification
		validRegionMod = {};
		% polygon vertices from previously selected regions
		validRegionModPoly = {};
		% whether or not rawSignals/rawImages have been replaced by only valid signals, hence, ignore validManual/validAuto
		validPurge = 0;
		% ROI to use for exclusion analysis
		analysisROIArray = {};
		% number of expected [PCs ICs] for PCA-ICA, alter for other procedures
		numExpectedSignals = {};

		% subject info
		% all are cell array of strings or numbers as specified in the name
		dataPath = {};
		subjectNum = {};
		subjectStr = {};
		subjectProtocolStr = {};
		assay = {};
		protocol = {};
		assayType = {};
		assayNum = {};
		imagingPlane = {};
		imagingPlaneNum = {};
		date = {};
		fileIDArray = {};
		fileIDNameArray = {};
		folderBaseSaveStr = {};
		folderBasePlaneSaveStr = {};
		folderBaseDisplayStr = {};
		folderBaseSaveStrUnique = {};

		% path to CSV/TAB file or matlab table containing trial information and frames when stimuli occur
		discreteStimulusTable = {};
		% cell array of strings
		stimulusNameArray = {};
		% cell array of strings, used for saving pictures/etc.
		stimulusSaveNameArray = {};
		% cell array of numbered values for stimulus, e.g. {65,10}
		stimulusIdArray = {};
		% [1 numTrialFrames] vectors with 1 for when stimulus occurs
		stimulusVectorArray = {};
		% vector sequence before/after to analyze stimulus
		stimulusTimeSeq = {};

		% path to a CSV/TAB file
		continuousStimulusTable = {};
		% cell array of strings
		continuousStimulusNameArray = {};
		% cell array of strings, used for saving pictures/etc.
		continuousStimulusSaveNameArray = {};
		% cell array of numbered values for stimulus, e.g. {65,10}
		continuousStimulusIdArray = {};
		% [1 numTrialFrames] vectors with 1 for when stimulus occurs
		continuousStimulusVectorArray = {};
		% vector sequence before/after to analyze stimulus
		continuousStimulusTimeSeq = {};

		% session trial names
		sessionTrialNames = '';

		% behavior metrics
		behaviorMetricTable = {};
		behaviorMetricNameArray = {};
		behaviorMetricIdArray = {};

		% List of available calciumImagingAnalysis methods
		methodsList = {...
		'------- SETUP -------';
		'modelAddNewFolders';
		'help';
		'setup';
		'update';
		'loadDependencies';
		'setMovieInfo';
		'resetMijiClass';
		'';
		'------- CLASS/BEHAVIOR -------';
		'showVars';
		'showFolders';
		'saveObj';
		'initializeObj';
		'setMainSettings';
		'';
		'------- DATA CHECK/LOAD/EXPORT -------';
		'modelGetFileInfo';
		'modelVerifyDataIntegrity';
		'modelBatchCopyFiles';
		'modelLoadSaveData';
		'modelExportData';
		'';
		'------- VISUALIZATION/PREPROCESS CHECK -------';
		'viewMovieFiltering';
		'viewMovieRegistrationTest';
		'viewMovie';
		'';
		'------- PREPROCESS -------';
		'modelDownsampleRawMovies';
		'modelPreprocessMovie';
		'modelModifyMovies';
		'removeConcurrentAnalysisFiles';
		'';
		'------- CELL/SIGNAL EXTRACTION -------';
		'modelExtractSignalsFromMovie';
		'viewCellExtractionOnMovie';
		'removeConcurrentAnalysisFiles';
		'';
		'------- LOAD CELL-EXTRACTION/SIGNAL DATA -------';
		'modelVarsFromFiles';
		'';
		'------- SIGNAL SORTING -------';
		'computeManualSortSignals';
		'modelModifyRegionAnalysis';
		'';
		'------- PREPROCESS VERIFICATION -------';
		'viewMovie';
		'viewObjmaps';
		'viewCreateObjmaps';
		'viewMovieCreateSideBySide';
		'';
		'------- ACROSS SESSION ANALYSIS: COMPUTE/VIEW -------';
		'viewSubjectMovieFrames';
		'computeMatchObjBtwnTrials';
		'';
		'viewMatchObjBtwnSessions';
		'modelSaveMatchObjBtwnTrials';
		'computeCellDistances';
		'computeCrossDayDistancesAlignment';
		'';
		'------- TRACKING -------';
		'modelTrackingData';
		'viewOverlayTrackingToVideo'
		};

		% Tooltips for calciumImagingAnalysis methods
		tts = struct(...
			'SETUP', 'Methods for setting up the class.',...
			'modelAddNewFolders', 'Add new folders to calciumImagingAnalysis.',...
			'help', 'Some tips if running into trouble using calciumImagingAnalysis.',...
			'setup', 'Setup calciumImagingAnalysis if running for the 1st time or need to re-install dependencies (e.g. Fiji).',...
			'update', 'Update calciumImagingAnalysis, direct user toward relevant web address.',...
			'loadDependencies', 'Download and setup calciumImagingAnalysis dependencies.',...
			'setMovieInfo', 'Set information',...
			'resetMijiClass', 'Reset Fiji (e.g. Miji) if having problems loading.',...
			'CLASS_BEHAVIOR', 'Methods for modifying calciumImagingAnalysis behavior or saving.',...
			'showVars', 'Show all properties for this instance of calciumImagingAnalysis.',...
			'showFolders', 'Show all folders users have loaded into the class.',...
			'saveObj', 'Save this instance of the object for later loading.',...
			'initializeObj', 'IGNORE.',...
			'setMainSettings', 'IGNORE.',...
			'DATA_CHECK_LOAD_EXPORT', 'Methods for verify data and updating calciumImagingAnalysis meta-data.',...
			'modelGetFileInfo', 'Update information about each folder loaded into the class. Information derived from folder name.',...
			'modelVerifyDataIntegrity', 'Various sub-methods to verify data is valid and that certain files are present in each folder.',...
			'modelBatchCopyFiles', 'Batch copy files from one location to another or to move files to sub-folders within each folder (e.g. if users want to archive particular files).',...
			'modelLoadSaveData', 'IGNORE.',...
			'modelExportData', 'IGNORE.',...
			'PREPROCESS', 'Methods for pre-processing imaging movies.',...
			'modelDownsampleRawMovies', 'Spatially downsample large raw movies. Done in chunks so can process movies larger than available RAM.',...
			'viewMovieFiltering', 'IGNORE.',...
			'viewMovieRegistrationTest', 'Allows users to test several pre-processing setting to choose one that works best for their dataset.<br>Mainly focused on motion correction and spatial filtering.',...
			'viewMovie', 'Allows users to view their movie in several ways for each folder.',...
			'modelPreprocessMovie', 'Main pre-processing method to do motion correction, spatial filtering, calculate relative fluorescence, etc.',...
			'modelModifyMovies', 'Used to add user-defined borders to the movie.<br>e.g. if want to blank out areas outside GRIN lens so they do not interfere with cell extraction, etc.',...
			'removeConcurrentAnalysisFiles', 'Removes temporary files created when running analysis across multiple workstations in "modelPreprocessMovie" or "modelExtractSignalsFromMovie".',...
			'CELL_SIGNAL_EXTRACTION', 'Methods related to cell extraction.',...
			'modelExtractSignalsFromMovie', 'Main method to allow pre-processing',...
			'viewCellExtractionOnMovie', 'Overlays cell-extraction outputs on the imaging movie to allow users to verify cell shape, that cells were not missed, etc.',...
			'LOAD_CELL_EXTRACTION_SIGNAL_DATA', 'Methods relating to loading cell-extraction data into calciumImagingAnalysis.',...
			'modelVarsFromFiles', 'Load cell-extraction data into calciumImagingAnalysis.<br>RUN EACH TIME re-loading calciumImagingAnalysis and have already run cell extraction on the folders added to calciumImagingAnalysis.',...
			'SIGNAL_SORTING', 'Methods relating to sorting/classification of cell-extraction outputs.',...
			'computeManualSortSignals', 'A GUI that allows ',...
			'modelModifyRegionAnalysis', '',...
			'PREPROCESS_VERIFICATION', '',...
			'viewObjmaps', 'Creates several figures displaying cell-extraction outputs from each selected folder.',...
			'viewCreateObjmaps', 'Creates several figures displaying cell-extraction outputs from each selected folder.',...
			'viewSubjectMovieFrames', 'Takes frames or cell-extraction cell maps from each folder associated with an animal and displays them as a movie to help aid in visual inspection of cross-session alignment possibilities.',...
			'viewMovieCreateSideBySide', 'Combines synchronized imaging and behavior or other experimental videos to allow side-by-side comparisons.',...
			'TRACKING', 'Methods related to tracking animals.',...
			'modelTrackingData', 'After running ImageJ-based tracking, use this module to clean-up the data and overlay it on the behavior movie.',...
			'viewOverlayTrackingToVideo', 'Creates a video where the animal''s computed centroid and velocity is overlaid on the video.',...
			'ACROSS_SESSION_ANALYSIS__COMPUTE_VIEW', 'Methods related to aligning cells across imaging sessions.',...
			'computeMatchObjBtwnTrials', 'Main method to match cell-extraction outputs (e.g. cells) across imaging sessions.',...
			'viewMatchObjBtwnSessions', 'Allows users to visualize cross session matches and outputs videos to help with assessment as well.',...
			'modelSaveMatchObjBtwnTrials', 'Will allow users to save output of cross-session matching.',...
			'computeCellDistances', 'Computes the cell-cell distance for all cells in each imaging session and outputs as a CSV table.<br>Should be used to assess the largest distance to still count cells as matching during cross-session matching.',...
			'computeCrossDayDistancesAlignment', 'Computes the cell-cell distance of all cross-session matched cells.<br>e.g. ideally all will be below the cross-session cell distance cutoff.'...
		);
	end
	properties(GetAccess = 'public', SetAccess = 'private')
		% public read access, but private write access.

		% summary statistics data save stores
		sumStats = {};
		detailStats = {};
		tmpStats = {};
		saveDataType = {};

		% counters and stores
		figNames = {};
		figNo = {};
		figNoAll = 777;

		% signal related
		nSignals = {};
		nFrames = {};
		signalPeaksCopy = {};
		alignedSignalArray = {};
		alignedSignalShuffledMeanArray = {};
		alignedSignalShuffledStdArray = {};

		% stimulus
		% reorganize discreteStimulusTable into stimulus structures to reduce memory footprint
		discreteStimulusArray = {};
		discreteStimMetrics = {};

		% stimulus
		% reorganize discreteStimulusTable into stimulus structures to reduce memory footprint
		continuousStimulusArray = {};
		continuousStimMetrics = {};

		% behavior metric
		% reorganize behaviorMetricTable into stimulus structures to reduce memory footprint/provide common IO
		behaviorMetricArray = {};

		% for each folder have a structure containing different metrics.
		neuroBehaviorMetric = {};

		% distance metrics
		distanceMetric = {};
		distanceMetricShuffleMean = {};
		distanceMetricShuffleStd = {};

		% correlation metrics
		corrMatrix = {};

		% significant signals, different variables for controlling which signals are statistically significant, given some test
		currentSignificantArray = [];
		significantArray = {};
		significantStatsArray = {};
		sigModSignals = {};
		sigModSignalsAll = {};
		ttestSignSignals = {};

		% cross session alignment
		globalIDs = [];
		globalIDCoords = {};
		globalIDFolders = {};
		globalIDImages = {};
		globalRegistrationCoords = {};
		globalObjectMapTurboreg = [];
		globalStimMetric = [];
		globalIDStruct = {};
	end
	properties(GetAccess = 'private', SetAccess = 'private')
		% private read and write access
	end
	properties(Constant = true)
		% cannot be changed after object is created
		% MICRON_PER_PIXEL =  2.37;
	end

	methods
		% methods, including the constructor are defined in this block
		function obj = ciatah(varargin)
			% CLASS CONSTRUCTOR
			warning on;
			clc
			% ' Calcium Imaging Analysis Class
			disp([...
			ciapkg.pkgName ' (calciumImagingAnalysis)' 10 ...
			'A software package for analyzing one- and two-photon calcium imaging datasets.' 10 10 ...
			'Biafra Ahanonu <<a href="emailto:bahanonu@alum.mit.edu">bahanonu@alum.mit.edu</a>>' 10 ...
			'Version ' obj.classVersion 10 10 ...
			'Made in USA']);

			obj.usaflag;

			disp(repmat('*',1,42))
			disp(['Constructing ' ciapkg.pkgName ' imaging analysis object...'])

			% Ensure that default directory is the CIAtah repository root
			functionLocation = dbstack('-completenames');
			functionLocation = functionLocation(1).file;
			[functionDir,~,~] = fileparts(functionLocation);
			[functionDir,~,~] = fileparts(functionDir);
			obj.defaultObjDir = functionDir;
			clear functionDir functionLocation;

			% Make sure all functions are loaded
			obj.loadBatchFunctionFolders();
			disp(repmat('*',1,42))

			% Make sure defaults set
			try
				setFigureDefaults();
				obj.classVersion = ciapkg.version();
			catch
			end

			% Because the obj
			%========================
			% obj.exampleOption = '';
			% get options
			obj = ciapkg.io.getOptions(obj,varargin);
			% disp(options)
			% unpack options into current workspace
			% fn=fieldnames(options);
			% for i=1:length(fn)
			%	 eval([fn{i} '=options.' fn{i} ';']);
			% end
			%========================

			obj = initializeObj(obj);

			ciapkg.io.updatePkg('updatePackage',0);

			disp(repmat('*',1,42))
			disp(['Done initializing ' ciapkg.pkgName '!'])
			disp(repmat('*',1,42))

			disp([...
			'Run processing pipeline by typing below (or clicking link) into command window (no semi-colon!):' 10 ...
			'<a href="matlab: obj">obj</a>'])

			% disp([...
			% 'Run processing pipeline by typing into command window:' 10 ...
			% '<a href="">obj.runPipelineProcessing</a>' 10 ...
			% 'or for advanced features: ' 10 ...
			% '<a href="">obj.runPipeline</a>' 10])
		end
		% getter and setter functions
		function dataPath = get.dataPath(obj)
			dataPath = obj.dataPath;
		end
	end
	methods(Static = true)
		% functions that are related but not dependent on instances of the class
		% function obj = loadObj(oldObj)
		%	  [filePath,folderPath,~] = uigetfile('*.*','select text file that points to analysis folders','example.txt');
		%	  % exit if user picks nothing
		%	  % if folderListInfo==0; return; end
		%	  load([folderPath filesep filePath]);
		%	  oldObj = obj;
		%	  obj = calciumImagingAnalysis;
		%	  obj = getOptions(obj,oldObj);
		% end
	end
	methods(Access = private)
		% methods only executed by other class methods

		% model methods, usually for input-output like saving information to files
		% for obtaining the current stim from tables
		[behaviorMetric] = modelGetBehaviorMetric(obj,inputID)
	end
	methods(Access = protected)
		% methods only executed by other class methods, also available to subclasses
	end
	methods(Access = public)
		% these are in separate M-files

		% calciumImagingAnalysis main class methods
		[idNumIdxArray, validFoldersIdx, ok] = ciatahMainGui(obj,fxnsToRun,inputTxt,currentIdx)
		[idNumIdxArray, ok] = pipelineListBox(obj,fxnsToRun,inputTxt,currentIdx)
		obj = computeCrossDayDistancesAlignment(obj)
		obj = computeCellDistances(obj)
		obj = resetMijiClass(obj)
		obj = runPipeline(obj,varargin)
		obj = initializeObj(obj)
		obj = loadBatchFunctionFolders(obj)
		obj = loadDependencies(obj)
		obj = removeConcurrentAnalysisFiles(obj)
		obj = setMovieInfo(obj)
		[validFoldersIdx] = pipelineFolderFilter(obj,useAltValid,validFoldersIdx)

		[output, output2] = modelGetStim(obj,idNum,varargin)

		% view help about the object
		obj = help(obj)

		% specific experiments

		% compute methods, performs some computation and returns calculation to class property
		obj = computeMatchObjBtwnTrials(obj,varargin)
		obj = computeManualSortSignals(obj)
		obj = computeClassifyTrainSignals(obj)

		% just need stimulus files

		% view methods, for displaying charts
		% no prior computation
		obj = viewCreateObjmaps(obj,varargin)

		% pre-processing checking
		obj = viewMovie(obj)
		obj = viewMovieFiltering(obj)
		obj = viewMovieRegistrationTest(obj)
		obj = viewMovieCreateSideBySide(obj)
		obj = modelModifyMovies(obj)
		obj = viewCellExtractionOnMovie(obj,varargin)

		% require pre-computation, individual

		% require pre-computation, group, global alignment
		obj = viewMatchObjBtwnSessions(obj,varargin)
		% movies

		% require pre-computation and behavior metrics, individual

		% requires manual sorting

		% need tracking
		obj = viewOverlayTrackingToVideo(obj)

		% model methods, usually for input-output like saving information to files
		obj = modelAddNewFolders(obj,varargin)
		obj = modelExportData(obj,varargin)
		obj = modelReadTable(obj,varargin)
		obj = modelTableToStimArray(obj,varargin)
		obj = modelGetFileInfo(obj)
		obj = modelVerifyDataIntegrity(obj)
		obj = modelSaveImgToFile(obj,saveFile,thisFigName,thisFigNo,thisFileID,varargin)
		obj = modelSaveSummaryStats(obj,varargin)
		obj = modelSaveDetailedStats(obj)
		obj = modelVarsFromFiles(obj)
		obj = modelModifyRegionAnalysis(obj,varargin)
		obj = modelExtractSignalsFromMovie(obj,varargin)
		obj = modelPreprocessMovie(obj)
		obj = modelDownsampleRawMovies(obj)
		obj = modelBatchCopyFiles(obj)
		obj = modelLoadSaveData(obj)
		obj = modelSaveMatchObjBtwnTrials(obj,varargin)

		% helps clean and load tracking data
		obj = modelTrackingData(obj)

		% helper
		[inputSignals, inputImages, signalPeaks, signalPeaksArray, valid, validType, inputSignals2] = modelGetSignalsImages(obj,varargin)
		[fileIdxArray, idNumIdxArray, nFilesToAnalyze, nFiles] = getAnalysisSubsetsToAnalyze(obj)
		[preprocessSettingStruct, preprocessingSettingsAll] = getRegistrationSettings(obj,inputTitleStr,varargin)

		% set methods, for IO to specific variables in a controlled manner
		obj = setMainSettings(obj)

		% Helper functions
		% obj = delete(obj)
		obj = setup(obj)
		obj = update(obj)
		obj = display(obj)
		obj = showVars(obj)
		obj = downloadLatestGithubVersion(obj)
		obj = showProtocolSubjectsSessions(obj)
		obj = showFolders(obj)
		[valid] = getValid(obj,validType)
		obj = changeCaxis(obj)
		obj = changeFont(obj)
		obj = checkToolboxes(obj)
		obj = GetSize(obj)
		obj = makeFolderDirs(obj)
		[flagOut] = usaflag(obj)
		obj = saveObj(obj)
		obj = modelVarsFromFilesCheck(obj,folderNo,varargin)
	end
end


function verbose = prefsFormatCheck
% checks 'VISTA' preference groupfor a file format setting
%
%   verbose = prefsFormatCheck;
%
% This variable determines how many data files (including t1-weighted
% anatomies, segmentation files, and parameter maps) are saved in mrVista.
%
% There are two possible values allowed:
%	'default': This indicates that the pre-existing, specialized formats
%	adopted by mrLoadRet/mrVista will be used:
%		volume anatomies will use the mrGray *.dat format;
%		inplane anatomies will be saved as Inplane/anat.mat in each
%		session;
%		segmentation files will be mrGray *.class / *.gray files;
%		parameter maps will be saved as .mat files.
%
%	'nifti': these formats will all employ NIFTI files. The segmentation
%	files (normally kept in .class files for each hemisphere) should be
%	saved as a labeled NIFTI file from ITKGray; anatomies and parameter
%	maps will be loaded/saved as compressed NIFTI. ***as of 6/2008, this
%	functionality is not fully implemented.***
%		* The 'grow_gray' MEX file needs to be tested and stable on all
%		machines: whereas before mrGray was a Windows-only bottleneck, once
%		the gray matter graph was saved, it could be read from any system.
%		The grow_gray would need to be employed everytime a segmentation is
%		installed, and is currently not stable enough to rely on it;
%		* The inplane anatomy/paramter map code needs to be updated to
%		allow for NIFTI files.
%
% ras, 09/2006.

if ~ispref('VISTA', 'fileFormat') | ~ismember( getpref('VISTA', 'fileFormat'), {'default' 'nifti'} )
    setpref('VISTA', 'fileFormat', 'default');
    fprintf('[%s]: ', mfilename);
    fprintf('Initializing VISTA preference ''fileFormat'' to ''default''. \n');
    fprintf('This will cause files to be saved and loaded in the \n');
	fprintf('legacy, specialized mrVista format: \n');
	fprintf('\tAnatomy files are in mrGray *.dat format;\n');
	fprintf('\tSegmentation files are in mrGray *.class and *.gray formats;\n');
	fprintf('\tInplane anatomies are saved as Inplane/anat.mat;\n');
	fprintf('\tParameter maps are saved as .mat files.\n');
	fprintf('To save these files in NIFTI format, run: \n');
	fprintf('setpref(''VISTA'', ''fileFormat'', ''nifti''). \n');
end

verbose = getpref('VISTA', 'fileFormat');

return


function success=testClassifierImport()

testDataDirName='/groups/branson/bransonlab/projects/JAABA/test_data';
jabFileName=fullfile(testDataDirName,'larping.jab');
nameOfJabFileWithClassifierToImport=fullfile(testDataDirName,'larping_different_features.jab');
nameOfExpDir='GMR_71G01_AE_01_TrpA_Rig1Plate15BowlA_20120316T144027';
gtMode=false;
data=JLabelData('setstatusfn',@(str)(fprintf('%s\n',str)), ...
                'clearstatusfn',@()(nop()), ...
                'isInteractive',true);
data.openJabFile(jabFileName,gtMode);

iExp=whichstr(nameOfExpDir,data.expnames);
if isempty(iExp)
  error('The experiment seems to be missing.');
elseif length(iExp)>1
  error('The experiment seems to be present more than once in the JLabelData object.');
end
iFly=1;
data.setCurrentTarget(iExp,iFly);

% The prediction should be larping for these frames
ts=3900:3940;
larpingBehavior=whichstri('larping',data.labelnames);
data.Predict(iExp,iFly,ts(1),ts(end));  % Make sure the prediction has been done
pred=data.GetPredictedIdx(iExp,iFly,ts(1),ts(end));
if all(pred.predictedidx==larpingBehavior)
  fprintf('Predictions are correct prior to importing classifier!\n');
else
  error('Predictions are wrong prior to importing classifier');
end

% Do the import
data.importClassifier(nameOfJabFileWithClassifierToImport);

% The prediction should be none for these frames
noneBehavior=whichstri('none',data.labelnames);
data.Predict(iExp,iFly,ts(1),ts(end));  % Make sure the prediction has been done
pred=data.GetPredictedIdx(iExp,iFly,ts(1),ts(end));
if all(pred.predictedidx==noneBehavior)
  fprintf('Predictions are correct after importing classifier!\n');
else
  error('Predictions are wrong after importing classifier');
end

data.closeJabFile();
data=[];  %#ok
success=true;

end


function vw = makeROIfromIndices(vw,ind,name,select,color,comments)
% makeROIfromIndices - make ROI from indices
%
% vw = makeROIfromIndices(vw,ind)
%
% 2010 SD & WZ: wrote it.

if notDefined('vw'),        error('Need view struct');   end
if notDefined('ind'),       error('Need indices');       end
if notDefined('name'),      name    = [];           end
if notDefined('select'),    select  = true;         end
if notDefined('color'),     color   = [];           end
if notDefined('comments'),  comments= [];           end

% define coordinates from indices
coords = viewGet(vw,'coords');
coords = coords(:,logical(ind));

% make new ROI
vw = newROI(vw,name,select,color,coords, comments);

return


classdef SplittingAlgorithmFactory < handle

   methods (Access = public, Static)

       function obj = create(cParams)
           switch cParams.type
               case 'ForwardBackward'
                   obj = ForwardBackward(cParams);
               case 'AcceleratedForwardBackward'
                   obj = AcceleratedForwardBackward(cParams);
           end

       end

   end

end


function [out1,out2,out3] = myginput(arg1)
% almost the same as matlab's ginput, but does not change the mouse pointer

out1 = []; out2 = []; out3 = []; y = [];
c = computer;
if ~strcmp(c(1:2),'PC')
   tp = get(0,'TerminalProtocol');
else
   tp = 'micro';
end

if ~strcmp(tp,'none') & ~strcmp(tp,'x') & ~strcmp(tp,'micro'),
   if nargout == 1,
      if nargin == 1,
         out1 = trmginput(arg1);
      else
         out1 = trmginput;
      end
   elseif nargout == 2 | nargout == 0,
      if nargin == 1,
         [out1,out2] = trmginput(arg1);
      else
         [out1,out2] = trmginput;
      end
      if  nargout == 0
         out1 = [ out1 out2 ];
      end
   elseif nargout == 3,
      if nargin == 1,
         [out1,out2,out3] = trmginput(arg1);
      else
         [out1,out2,out3] = trmginput;
      end
   end
else

   fig = gcf;
   figure(gcf);

   if nargin == 0
      how_many = -1;
      b = [];
   else
      how_many = arg1;
      b = [];
      if  isstr(how_many) ...
            | size(how_many,1) ~= 1 | size(how_many,2) ~= 1 ...
            | ~(fix(how_many) == how_many) ...
            | how_many < 0
         error('Requires a positive integer.')
      end
      if how_many == 0
         ptr_fig = 0;
         while(ptr_fig ~= fig)
            ptr_fig = get(0,'PointerWindow');
         end
         scrn_pt = get(0,'PointerLocation');
         loc = get(fig,'Position');
         pt = [scrn_pt(1) - loc(1), scrn_pt(2) - loc(2)];
         out1 = pt(1); y = pt(2);
      elseif how_many < 0
         error('Argument must be a positive integer.')
      end
   end

   % Remove figure button functions
   state = uisuspend(fig);
   pointer = get(gcf,'pointer');
   %set(gcf,'pointer','fullcrosshair');
   fig_units = get(fig,'units');
   char = 0;

   while how_many ~= 0
      % Use no-side effect WAITFORBUTTONPRESS
      waserr = 0;
      try
	keydown = wfbp;
      catch
	waserr = 1;
      end
      if(waserr == 1)
         if(ishandle(fig))
            set(fig,'units',fig_units);
	    uirestore(state);
            error('Interrupted');
         else
            error('Interrupted by figure deletion');
         end
      end

      ptr_fig = get(0,'CurrentFigure');
      if(ptr_fig == fig)
         if keydown
            char = get(fig, 'CurrentCharacter');
            button = abs(get(fig, 'CurrentCharacter'));
            scrn_pt = get(0, 'PointerLocation');
            set(fig,'units','pixels')
            loc = get(fig, 'Position');
            pt = [scrn_pt(1) - loc(1), scrn_pt(2) - loc(2)];
            set(fig,'CurrentPoint',pt);
         else
            button = get(fig, 'SelectionType');
            if strcmp(button,'open')
               button = b(length(b));
            elseif strcmp(button,'normal')
               button = 1;
            elseif strcmp(button,'extend')
               button = 2;
            elseif strcmp(button,'alt')
               button = 3;
            else
               error('Invalid mouse selection.')
            end
         end
         pt = get(gca, 'CurrentPoint');

         how_many = how_many - 1;

         if(char == 13) % & how_many ~= 0)
            % if the return key was pressed, char will == 13,
            % and that's our signal to break out of here whether
            % or not we have collected all the requested data
            % points.
            % If this was an early breakout, don't include
            % the <Return> key info in the return arrays.
            % We will no longer count it if it's the last input.
            break;
         end

         out1 = [out1;pt(1,1)];
         y = [y;pt(1,2)];
         b = [b;button];
      end
   end

   uirestore(state);
   set(fig,'units',fig_units);

   if nargout > 1
      out2 = y;
      if nargout > 2
         out3 = b;
      end
   else
      out1 = [out1 y];
   end

end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function key = wfbp
%WFBP   Replacement for WAITFORBUTTONPRESS that has no side effects.

fig = gcf;
current_char = [];

% Now wait for that buttonpress, and check for error conditions
waserr = 0;
try
  h=findall(fig,'type','uimenu','accel','C');   % Disabling ^C for edit menu so the only ^C is for
  set(h,'accel','');                            % interrupting the function.
  keydown = waitforbuttonpress;
  current_char = double(get(fig,'CurrentCharacter')); % Capturing the character.
  if~isempty(current_char) & (keydown == 1)           % If the character was generated by the
	  if(current_char == 3)                       % current keypress AND is ^C, set 'waserr'to 1
		  waserr = 1;                             % so that it errors out.
	  end
  end

  set(h,'accel','C');                                 % Set back the accelerator for edit menu.
catch
  waserr = 1;
end
drawnow;
if(waserr == 1)
   set(h,'accel','C');                                % Set back the accelerator if it errored out.
   error('Interrupted');
end

if nargout>0, key = keydown; end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


% moveaxes() - move, resize, or copy Matlab axes using the mouse
%
% Usage: >> moveaxes
%        >> moveaxes(fig)
%        >> moveaxes off
%
% Note: clicking the left mouse button selects an axis
%       dragging the left mouse button resizes a selected axis
%       dragging the right mouse button copies a selected axis
%       clicking the middle mouse button deselects a selected axis
%
% Authors: Colin Humphries & Scott Makeig, CNL / Salk Institute, La Jolla

% Copyright (C) Colin Humphries & Scott Makeig, SCCN/INC/UCSD, scott@sccn.ucsd.edu
%
% 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 2 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, write to the Free Software
% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

% 01-25-02 reformated help & license -ad

function moveaxes(fig)

if nargin<1
  fig = gcf;
end

if ~isstr(fig)
   tmp = findobj('tag','moveaxes');
   if ~isempty(tmp)   % turn off previous moveaxes Tags
      ax = get(tmp,'children');
      set(ax,'ButtonDownFcn','','Selected','Off');
      set(tmp,'Tag','','UserData',[]);
   end

   ax = findobj('parent',fig,'type','axes');
   for a=1:length(ax)                    % make all axes visible
     axvis = get(ax(a),'visible');
     set(ax(a),'visible','on','UserData',axvis);
   end

   set(ax,'ButtonDownFcn','selectmoveresize');
   set(fig,'UserData',ax,'Tag','moveaxes');

elseif strcmp(fig,'off')
   fig=findobj('Tag','moveaxes');
   ax = get(fig,'UserData');
   for a=1:length(ax)                    % reset original axis visibility
     axvis= get(ax(a),'UserData')
     set(ax(a),'visible',axvis);
   end
   set(ax,'ButtonDownFcn','','Selected','off');
   set(fig,'Tag','','UserData',[]);
end


function N = uplus(N)
%UPLUS   Unitary plus for CHEBOP2 objects.

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


function opt = LegOption
% =======================================================================
% Creates structure of inputs for LegPlot or LegSubplot
% =======================================================================
% opt = LegOption
% -----------------------------------------------------------------------
% OUTPUT
%   - opt.hsize : default 0.01, horizontal size of the box of the legend
%   - opt.vsize : default 0.01, vertical position of the box of the legend
%           (0=bottom, 0.5=middle)
%   - opt.handle : default [], no handle specified
%   - opt.interpreter : default 'Tex', otherwise chamge to 'none'
%   - opt.direction : default 1, horizontal legend
% -----------------------------------------------------------------------
% EXAMPLE
%   plot(1:10);
%   opt = LegOption; opt.interpreter = 'Tex'; opt.hsize = 0.4
%   legpos = LegPlot({'Plot of \alpha'},opt)
% =======================================================================
% VAR Toolbox 3.0
% Ambrogio Cesa-Bianchi
% ambrogiocesabianchi@gmail.com
% March 2015. Updated November 2020
% -----------------------------------------------------------------------

opt.hsize        = 0.01;
opt.vsize        = 0.01;
opt.handle       = [];
opt.interpreter  = 'Tex';
opt.direction    = 1;


function bool = prtUtilIsLogicalScalar(value)
%bool = prtUtilIsLogicalScalar(value)

bool = islogical(value) && isscalar(value);


function roiXformView(vw)

% roiXformView(vw)
%
% If we are in INPLANE view, do nothing. If we are in FLAT,  GRAY, or
% VOLUME, xform the ROI to INPLANE. Open a hidden INPLANE if needed.
% Currently this code is duplicated inside many functions (plotMeanTSeries,
% plotSingleCycle, etc.). Now this will happen in one place (here).
%
% JW 2/2009

mrGlobals;

% ----------------------------------
% xform ROI if not in INPLANE view
% ----------------------------------
scanNum = viewGet(vw, 'curscan');

%Specifics for Flat, Gray, or Inplane views - xform ROI to INPLANE view
switch vw.viewType
    case {'Volume' 'Gray'}   %%%For ROIs in Gray view - xform to inplane
        % If the data do not exist in the inplane try getting the data from the
        % gray view directly.

        %selectedVOLUME = viewSelected('volume');

        %initiate and / or select INPLANE window
        if isempty(INPLANE),
            INPLANE{1} = initHiddenInplane;
            INPLANE{1} = viewSet(INPLANE{1},'name','hidden');
            selectedINPLANE = 1;
        else
            selectedINPLANE = viewSelected('inplane');
            if isempty(INPLANE{selectedINPLANE}),
                for s = 1:length(INPLANE)
                    if ~isempty(INPLANE{s}), selectedINPLANE = s; end
                end
            end
        end

        % Set the Inplane scan number and datatype to match the Volume view.
        try
            curDataType = viewGet(vw,'datatypenumber');
            INPLANE{selectedINPLANE} = viewSet(INPLANE{selectedINPLANE},'datatypenumber',curDataType);
            INPLANE{selectedINPLANE} = viewSet(INPLANE{selectedINPLANE},'currentscan',scanNum);

            %Transfer current VOLUME ROI to INPLANE
            INPLANE{selectedINPLANE} = vol2ipCurROI(vw,INPLANE{selectedINPLANE});
        catch
            %rethrow(lasterror);
        end;

    case {'Flat'} % For ROIs in Flat view - xform to inplane

        selectedFLAT = viewSelected('flat');

        %initiate and / or select VOLUME and INPLANE windows
        if isempty(VOLUME),
            VOLUME{1} = initHiddenGray;
            VOLUME{1} = viewSet(VOLUME{1},'name','hidden');
            selectedVOlUME = 1;
        else
            selectedVOLUME = viewSelected('volume');
        end

        if isempty(INPLANE),
            INPLANE{1} = initHiddenInplane;
            INPLANE{1} = viewSet(INPLANE{1},'name','hidden');
            selectedINPLANE = 1;
        else
            selectedINPLANE = viewSelected('inplane');
        end

        % Set the Inplane scan number and datatype to match the Flat view.
        %         curScan =     viewGet(FLAT{selectedFLAT},'currentscan');
        curDataType = viewGet(FLAT{selectedFLAT},'datatypenumber');
        INPLANE{selectedINPLANE} = viewSet(INPLANE{selectedINPLANE},'datatypenumber',curDataType);
        INPLANE{selectedINPLANE} = viewSet(INPLANE{selectedINPLANE},'currentscan',scanNum);

        %Transfer current FLAT ROI to INPLANE
        INPLANE{selectedINPLANE} = flat2ipCurROI(vw,INPLANE{selectedINPLANE},VOLUME{selectedVOLUME});

    case {'Inplane'}   %%%For ROIs in INPLANE view - select inplane
        selectedINPLANE = viewSelected('inplane');
end

return


function updateContents(folder)
%UPDATECONTENTS Create a Contents.m file including subdirectories
%
%   IOSR.GENERAL.UPDATECONTENTS scans through the current directory, and
%   its subdirectories, and builds a Contents file similar to Matlab's
%   report-generated Contents.m files. Any existing Contents.m file will be
%   overwritten.
%
%   IOSR.GENERAL.UPDATECONTENTS(FOLDER) scans through the directory FOLDER.
%
%   Typing
%
%       help(FOLDER)
%
%   or
%
%       help path/to/folder
%
%   will display Contents.m in the Command Window, and display links to the
%   help for any functions that are in Matlab's search path.
%
%   NB: Do not use Matlab's Contents Report generator to edit the
%   Contents.m file. Execute this function to update it.

%   Copyright 2016 University of Surrey.

    % apply function in current directory
    if nargin<1
        folder = cd;
    end

    % check input is valid
    assert(ischar(folder), 'iosr:updateContents:invalidPath', '''directory'' should be a charater array (string)')
    assert(exist(folder,'dir')==7, 'iosr:updateContents:invalidPath', [folder ' does not exist'])

    % check last character in path is not filesep (e.g. '/')
    if strcmp(folder(end),filesep)
        folder = folder(1:end-1);
    end
    fIX = strfind(folder,filesep); fIX = fIX(end);

    % name of file to create
    filename = 'Contents.m';

    % Name of the folder
    [~,name] = fileparts(folder);

    % delete if it already exists
    if exist([folder filesep filename],'file')==2
        delete([folder filesep filename])
    end

    % get subfolders
    dirs = iosr.general.getContents(folder,'filter','folders','rec',true,'path','full','sort',true);
    dirs = [{folder}; dirs];

    % get files
    files = cell(0,1);
    H1_lines = cell(0,1);
    for d = 1:length(dirs)
        temp = iosr.general.getContents(dirs{d},'filter','files','sort',true);
        if ~isempty(temp)
            temp = temp(cellfun(@(x) isempty(strfind(x,'~')),temp)); % remove temporary files
            temp = temp(cellfun(@(x) isempty(strfind(x,'.mex')),temp)); % remove compiled mex files
            temp = temp(cellfun(@(x) ~strcmp(x,filename),temp)); % remove Contents.m
            H1_lines = [H1_lines; {''}; {''}]; %#ok<AGROW> % insert blank lines where no functions will be
            % determine package prefix
            pkgprefix = strrep(dirs{d},[filesep '+'],'.');
            pkgprefix = strrep(pkgprefix,[filesep '@'],'.');
            dots = strfind(pkgprefix,'.');
            if ~isempty(dots)
                pkgprefix = [pkgprefix(dots(1)+1:end) '.'];
            else
                pkgprefix = '';
            end
            for f = 1:length(temp) % read H1 lines
                H1_lines = [H1_lines; {get_H1_line([dirs{d} filesep temp{f}])}]; %#ok<AGROW> % add H1 lines
                % remove extension from and add package prefix to m-files
                [~,fname,ext] = fileparts(temp{f});
                if strcmpi(ext,'.m')
                    temp{f} = [pkgprefix fname];
                end
            end
            files = [files; {''}; {upper(dirs{d}(fIX+1:end))}; temp;]; %#ok<AGROW> % add filenames
        end
    end

    % longest file name (so appropriate space can be added between files and H1 lines
    longest_word = max(cellfun(@length,files(cellfun(@(x) ~isempty(x),H1_lines))));

    % write to output
    nrows = length(files);
    fid = fopen(filename, 'w'); % open file for writing
    fprintf(fid, '%s\n%% \n', ['% ' upper(name)]);
    fprintf(fid, '%s\n', ['%   Contents file for ' upper(folder(fIX+1:end)) ' and its subfolders.']);
    for row=1:nrows
        if isempty(H1_lines{row})
            fprintf(fid, '%s\n', ['%   ' files{row,:}]);
        else
            rowfilename = files{row,:};
            [~,name,ext] = fileparts(rowfilename);
            if strcmpi(ext,'.m') % remove extension from m files
                rowfilename = name;
            end
            fprintf(fid, '%s\n',['%   ' rowfilename repmat(' ',1,longest_word-length(rowfilename)) ' - ' H1_lines{row,:}]);
        end
    end
    fprintf(fid, '%%    \n%%   %s on %s at %s.\n', 'This file was generated by updateContents.m',datestr(datetime('now'),'dd mmm yyyy'),datestr(datetime('now'),'HH:MM:SS'));
    fclose(fid);

end

function H1_line = get_H1_line(filename)
%GET_H1_LINE get the H1 line for a file

    [~,name,ext] = fileparts(filename);
    H1_line = ''; % default output
    if strcmp(ext,'.m')
        fid = fopen(filename); % open file
        tline = fgetl(fid); % read first line
        while ischar(tline)
            k = strfind(tline,'%'); % find comment
            if ~isempty(k) % if it is found
                k = k(1);
                ispercents = false(size(tline(k:end)));
                ispercents(strfind(tline(k:end),'%'))=true;
                start = k+find(~(isspace(tline(k:end)) | ispercents),1,'first')-1;
                if ~isempty(start)
                    tline = tline(start:end); % remove leading space/percent
                    IX = strfind(lower(tline),lower(name));
                    if ~isempty(IX)
                        if IX(1)==1
                            tline = tline(length(name)+1:end); % remove function name
                        end
                        tline = strtrim(tline); % remove any leading/trailing space
                    end
                    H1_line = tline;
                    H1_line = strtrim(H1_line);
                    if ~isempty(H1_line)
                        if strcmp(H1_line(end),'.') % remove trailing period
                            H1_line = H1_line(1:end-1);
                        end
                        H1_line(1) = upper(H1_line(1)); % capitalize first letter
                    end
                end
                tline = -1; % set tline to numeric
            else
                tline = fgetl(fid); % read next line
            end
        end
        fclose(fid);
    end

end


function [ fn ] = getBatchWrapper_ucf101_imgs(opts, numThreads, trainopts)
fn = @(imdb,batch, moreopts) getBatch(imdb,batch,opts,numThreads, trainopts, moreopts) ;

end

% -------------------------------------------------------------------------
function [im,labels] = getBatch(imdb, batch, opts, numThreads, trainopts, moreopts)
% -------------------------------------------------------------------------
opts.nFrames = imdb.images.nFrames(batch);
images = cell(1,numel(opts.nFrames));
labels = imdb.images.label(batch) ;
for k = 1:numel(opts.nFrames)
  images{k}  = strcat([imdb.images.name{batch(k)}(1:end-4) filesep], images{k} ) ;
end
if ~isempty(moreopts) || nargin < 6
  for f = fieldnames(moreopts)'
    f = char(f) ;
    trainopts.(f) = moreopts.(f);
  end
end

opts.nFramesPerVid = trainopts.nFramesPerVid;
opts.numAugments = trainopts.numAugments;
opts.frameSample = trainopts.frameSample;
opts.augmentation =  trainopts.augmentation;
if isfield(trainopts, 'temporalStride')
  opts.temporalStride =  trainopts.temporalStride(randi(numel(trainopts.temporalStride))) ; % shuffle;
end
if isfield(trainopts, 'keepFramesDim')
  keepFramesDim = trainopts.keepFramesDim;
else
  keepFramesDim = false;
end
if isfield(trainopts, 'cheapResize')
  opts.cheapResize =  trainopts.cheapResize;
end
if isfield(trainopts, 'prefetch')
    opts.prefetch = trainopts.prefetch;
end
if isfield(trainopts, 'frameList')
    opts.frameList = trainopts.frameList;
end

im = cnn_ucf101_get_frame_batch(images, opts, ...
                            'numThreads', numThreads, ...
                            'augmentation', opts.augmentation , ...
                          'imageDir', imdb.imageDir) ;

if iscell(im), return; end

labels = imdb.images.label(batch) ;

if nargout == 1 % to work with dag code
  if ndims(im) > 4 && ~keepFramesDim
    sz = size(im);
    nFrames = sz(5:end);
    im = permute(im, [1 2 3 5 4]);
    im = reshape(im, sz(1), sz(2), sz(3), []);
  end
  if size(im,3) > 1
    im = {'input', im(:,:,end-2:end,:,:), 'label', labels, 'input_flow', im(:,:,1:end-3,:,:)} ;
  else
    im = {'input', im, 'label', labels} ;
  end
end

end


function output = isfinite(input)

output = cellfun(@isfinite, input, 'uniformoutput', false);


function genes = drosTargets(tf),

% DROSTARGETS returns a list of hand selected enchanced targets for given tf
%
% Usage:
%   genes = drosTargets(tf)
% where tf is one of 'bin', 'mef2', 'tin', 'twi'
%
% COPYRIGHT : Antti Honkela, 2007

% 'FBgn0029082', 'FBgn0036459',
tingenes1 = {'FBgn0003117', 'FBgn0033459', 'FBgn0051368'};
tingenenames = {'pannier', 'CG12744', 'CG31368'};

tingenes2 = {'FBgn0003117', 'FBgn0015904', 'FBgn0030054', ...
	     'FBgn0027611', 'FBgn0029082'};

mefgenes1 = {'FBgn0000412', ...  % ???
	     'FBgn0000416', ...
	     'FBgn0000497', ...
	     'FBgn0001624', ...  % Twist bound nearby, probably for another gene
	     'FBgn0004401', ...
	     'FBgn0010109'};     % Lots of other activity nearby, probably for other genes

mefgenes2 = {'FBgn0031717', 'FBgn0035158', 'FBgn0036282', 'FBgn0004028', ... % !!!
	     'FBgn0032919', ... % This or CG1099
	     'FBgn0033688'};

twigenes1 = {'FBgn0003062', 'FBgn0003520', 'FBgn0014033', 'FBgn0025776', ...
	     'FBgn0028999', 'FBgn0029146'};
%twigenes1 = {'FBgn0003062', 'FBgn0003313', 'FBgn0004646', 'FBgn0003520', 'FBgn0014033', 'FBgn0020391'};

twigenes2 = {'FBgn0030749', ... % Mef2 and bin activity nearby
	     'FBgn0040108', ... % !!!
	     'FBgn0003062', ... % Mef2 nearby, probably for other genes
	     'FBgn0003308', 'FBgn0003607', 'FBgn0004635'}; % Good targets

bingenes1 = {'FBgn0004880', 'FBgn0010225', 'FBgn0015011', 'FBgn0026562', ...
	     'FBgn0033661', 'FBgn0035091', 'FBgn0038881', 'FBgn0039704'};

bingenes2 = {'FBgn0022073', 'FBgn0037240', 'FBgn0039268', 'FBgn0001197', ...
	     'FBgn0003890', 'FBgn0015011'};

bapgenes1 = {'FBgn0031349'};

switch tf,
 case 'twi',
  %genes = twigenes1([1 2 3 4 6]);
  genes = twigenes2;
 case 'mef2',
  %genes = mefgenes1([2 3 5]);
  %genes = mefgenes2([1 2 3 5 6]);
  genes = mefgenes2;
 case 'tin',
  %genes = tingenes1;
  genes = tingenes2;
 case 'bin',
  %genes = bingenes1(1:6);
  genes = bingenes2;
 %case 'bin1',
 % genes = bingenes1;
 otherwise,
  error('Unknown TF');
end


function a = in( x, y )

b = newcnstr( evalin( 'caller', 'cvx_problem', '[]' ), x, y, '==' );
if nargout, a = b; end

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


function [ output_meta ] = meta2sicd_s1manifest( domnode )
%META2SICD_S1MANIFEST Converts Sentinel-1 manifest.safe description into a SICD-style metadata structure
%
% In general, the Sentinel-1 manifest.safe file doesn't have much real
% metadata directly in it.  However, it is the document that points to all
% of the data and metadata files in the package.
%
% Written by: Wade Schwartzkopf, NGA/IDT
%
% //////////////////////////////////////////
% /// CLASSIFICATION: UNCLASSIFIED       ///
% //////////////////////////////////////////

%% Setup
% Overall wrapping node is in a namespace that makes things messier
% (than they already are) in a XPath call, so we go down one level.
domnode = domnode.getFirstChild;
xp=javax.xml.xpath.XPathFactory.newInstance.newXPath();

%% CollectionInfo
platform_xp_str = ['metadataSection/' ...
    'metadataObject[@ID="platform"]/' ...
    'metadataWrap/' ...
    'xmlData/' ...
    '*[local-name()=''platform'']/'];
output_meta.CollectionInfo.CollectorName = [
    char(xp.evaluate(...
    [platform_xp_str '*[local-name()=''familyName'']'],domnode)) ...
    char(xp.evaluate(...
    [platform_xp_str '*[local-name()=''number'']'],domnode))];
% The following core name is unique within all Sentinel-1 collections.
% Perhaps we would rather use the NGA CoreName standard.
% We also may need to further add to this CoreName, if each collect has
% multiple swaths and bursts which must each go into their own SICD, each
% requiring their own unique CoreNames.
standalone_xp_str = ['metadataSection/' ...
    'metadataObject[@ID="generalProductInformation"]/' ...
    'metadataWrap/' ...
    'xmlData/' ...
    '*[local-name()=''standAloneProductInformation'']/'];
% Since each TOPSAR product has multiple CDPs (bursts), we really don't
% have enough information here to make a unique identifier for each CDP.
% We will defer generating the CoreName until meta2sicd_s1product.m
% output_meta.CollectionInfo.CoreName = char(xp.evaluate(...
%     [standalone_xp_str '*[local-name()=''missionDataTakeID'']'],domnode));
output_meta.CollectionInfo.CollectType='MONOSTATIC';
output_meta.CollectionInfo.RadarMode.ModeID = ...
    char(xp.evaluate(...
    [platform_xp_str ...
    '*[local-name()=''instrument'']/' ...
    '*[local-name()=''extension'']/' ...
    '*[local-name()=''instrumentMode'']/' ...
    '*[local-name()=''mode'']'],domnode));
if strcmp(output_meta.CollectionInfo.RadarMode.ModeID, 'SM')
    output_meta.CollectionInfo.RadarMode.ModeType='STRIPMAP';
else
    % Actually TOPSAR.  Not what we normally think of for DYNAMIC STRIPMAP,
    % but it is definitely not SPOTLIGHT, and doesn't seem to be regular
    % STRIPMAP either.
    output_meta.CollectionInfo.RadarMode.ModeType='DYNAMIC STRIPMAP';
end
output_meta.CollectionInfo.Classification='UNCLASSIFIED';

%% ImageCreation
% These fields describe the final SLC post processing step, not the SLC
% processing itself, although in datasets we have seen, this information
% has been the same.
processing_xp_str = ['metadataSection/' ...
    'metadataObject[@ID="processing"]/' ...
    'metadataWrap/' ...
    'xmlData/' ...
    '*[local-name()=''processing'']/'];
facility_xp_str = [processing_xp_str '*[local-name()=''facility'']/'];
software_xp_str = [facility_xp_str '*[local-name()=''software'']/'];
output_meta.ImageCreation.Application = [...
    char(xp.evaluate([software_xp_str '@name'],domnode)) ' '...
    char(xp.evaluate([software_xp_str '@version'],domnode))];
output_meta.ImageCreation.DateTime = datenum( ...
    char(xp.evaluate([processing_xp_str '@stop'],domnode)), ...
    'yyyy-mm-ddTHH:MM:SS.FFF');
output_meta.ImageCreation.Site = [ ...
    char(xp.evaluate([facility_xp_str '@name'],domnode)) ', ' ...
    char(xp.evaluate([facility_xp_str '@site'],domnode)) ', ' ...
    char(xp.evaluate([facility_xp_str '@country'],domnode))];
output_meta.ImageCreation.Profile='Prototype';

%% Timeline
% The start/stop times given in the manifest file appear to be the zero
% Doppler times for the first and the last lines, not the total collect
% time, which seems to be given by firstLineSensingTime/lastLineSensingTime
% in the annotation XML file. These start/stop times in the manifest file
% also seem to be replicated in the annotation XML file.
% acquistion_xp_str = ['metadataSection/' ...
%     'metadataObject[@ID="acquisitionPeriod"]/' ...
%     'metadataWrap/' ...
%     'xmlData/' ...
%     '*[local-name()=''acquisitionPeriod'']/'];
% char(xp.evaluate(...
%     [acquistion_xp_str '*[local-name()=''startTime'']'],domnode));
% char(xp.evaluate(...
%     [acquistion_xp_str '*[local-name()=''stopTime'']'],domnode));

%% RadarCollection
num_pols = str2double(xp.evaluate(...
    ['count(' standalone_xp_str '*[local-name()=''transmitterReceiverPolarisation''])'],domnode));
for i = 1:num_pols
    pol = char(xp.evaluate(...
        [standalone_xp_str '*[local-name()=''transmitterReceiverPolarisation''][' num2str(i) ']'],domnode));
    output_meta.RadarCollection.RcvChannels.ChanParameters(i).TxRcvPolarization=[pol(1) ':' pol(2)];
end

end

% //////////////////////////////////////////
% /// CLASSIFICATION: UNCLASSIFIED       ///
% //////////////////////////////////////////


function [sFile, ChannelMat] = in_fopen_manscan(DataFile)
% IN_FOPEN_MANSCAN: Open a MANSCAN file (continuous recordings)
%
% USAGE:  [sFile, ChannelMat] = in_fopen_manscan(DataFile)

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

%% ===== READ HEADER =====
% Get text file (.mbi)
MbiFile = strrep(DataFile, '.mb2', '.mbi');
% If doesn't exist: error
if ~file_exist(MbiFile)
    error('Cannot open file: missing text file .mbi');
end
% Initialize header
iEpoch = 1;
hdr.epoch(iEpoch).Comment = {};
hdr.epoch(iEpoch).Channel = [];
hdr.Events = [];
curBlock = '';
% Read file line by line
fid = fopen(MbiFile,'r');
while(1)
    % Reached the end of the file: exit the loop
    if feof(fid)
        break;
    end
    % Read one line
    buf = fgetl(fid);
    if isempty(buf)
        curBlock = '';
        continue;
    end
    % Split line based on space characters
    splitBuf = str_split(buf, [0 9 32]);
    if isempty(splitBuf)
        continue;
    end
    % Current block
    if ~isempty(curBlock)
        switch curBlock
            case 'event'
                % Ignore MANSCAN auto events
                if any(strcmpi(evtProp, 'Channel'))
                    continue;
                end
                % New event
                iEvt = length(hdr.Events) + 1;
                % Read event name and epoch
                hdr.Events(iEvt).Name   = splitBuf{1};
                hdr.Events(iEvt).iEpoch = iEpoch;
                % Read each property
                for iProp = 2:length(evtProp)
                    if strcmpi(evtProp{iProp}, 'Channel')
                        hdr.Events(iEvt).Name = [hdr.Events(iEvt).Name '_' splitBuf{iProp}];
                    elseif strcmpi(evtType{iProp}, 'String')
                        hdr.Events(iEvt).(evtProp{iProp}) = splitBuf{iProp};
                    else
                        hdr.Events(iEvt).(evtProp{iProp}) = str2num(splitBuf{iProp});
                    end
                end

            case 'channel'
                % New channel
                iChan = length(hdr.epoch(iEpoch).Channel) + 1;
                % Read channel name
                hdr.epoch(iEpoch).Channel(iChan).Name = splitBuf{1};
                % Read each property
                for iProp = 2:length(chanProp)
                    if strcmpi(chanProp{iProp}, 'UnitDisplayToFile')
                        propValue = str2num(splitBuf{iProp});
                    else
                        propValue = splitBuf{iProp};
                    end
                    hdr.epoch(iEpoch).Channel(iChan).(chanProp{iProp}) = propValue;
                end
        end
        % Next line
        continue;
    end
    % Detect keyword
    switch lower(splitBuf{1})
        case 'datatypeid'
            iEpoch = iEpoch + 1;
            hdr.epoch(iEpoch).Comment = {};
            hdr.epoch(iEpoch).Channel = [];
        case 'comment'
            hdr.epoch(iEpoch).Comment{end+1} = strtrim(strrep(buf, splitBuf{1}, ''));
        case 'event'
            curBlock = 'event';
            evtProp = splitBuf;
            evtType = str_split(fgetl(fid), [0 9 32]);
        case 'channel'
            curBlock = 'channel';
            chanProp = splitBuf;
        case 'offsetdisplayexperiment'
            hdr.epoch(iEpoch).OffsetDisplay = str2num(splitBuf{2});
        case 'experimenttime'
            hdr.epoch(iEpoch).ExperimentTime = strtrim(strrep(buf, splitBuf{1}, ''));
        case 'worddatafile'
            % Read channel order
            hdr.epoch(iEpoch).ChannelOrder = str_split(fgetl(fid), [0 9 32]);
            % Read record info
            datainfo = str_split(fgetl(fid), [0 9 32]);
            hdr.epoch(iEpoch).StartData1 = str2num(datainfo{1});
            hdr.epoch(iEpoch).StartData2 = str2num(datainfo{2});
            hdr.epoch(iEpoch).Sweeps     = str2num(datainfo{3});
            hdr.epoch(iEpoch).BinFile    = datainfo{4};
    end
end

%% ===== CREATE BRAINSTORM SFILE STRUCTURE =====
% Initialize returned file structure
sFile = db_template('sfile');
% Add information read from header
sFile.byteorder  = 'l';
sFile.filename   = DataFile;
sFile.format = 'EEG-MANSCAN';
sFile.device = 'MANSCAN';
% Comment: short filename
[tmp__, sFile.comment, tmp__] = bst_fileparts(DataFile);
% Consider that the sampling rate of the file is the sampling rate of the first signal
sFile.prop.sfreq = 256;
sFile.prop.nAvg  = 1;
% No info on bad channels
sFile.channelflag = ones(length(hdr.epoch(iEpoch).ChannelOrder), 1);
% Acquisition date
try
    sFile.acq_date = str_date(hdr.epoch.ExperimentTime, 'mm/dd/yy');
catch
end

%% ===== EPOCHS =====
if (length(hdr.epoch) <= 1)
    sFile.prop.times   = [0, hdr.epoch(1).Sweeps-1] ./ sFile.prop.sfreq;
else
    % Build epochs structure
    for iEpoch = 1:length(hdr.epoch)
        sFile.epochs(iEpoch).label   = sprintf('Epoch #%02d', iEpoch);
        sFile.epochs(iEpoch).times   = [0, hdr.epoch(iEpoch).Sweeps-1] ./ sFile.prop.sfreq;
        sFile.epochs(iEpoch).nAvg    = 1;
        sFile.epochs(iEpoch).select  = 1;
        sFile.epochs(iEpoch).bad     = 0;
        sFile.epochs(iEpoch).channelflag = [];

        % Check if all the epochs have the same channel list
        if (iEpoch > 1) && ~isequal(hdr.epoch(iEpoch).ChannelOrder, hdr.epoch(1).ChannelOrder)
            error('Channel list must remain constant across epochs.');
        end
    end
    % Extract global min/max for time and samples indices
    sFile.prop.times   = [min([sFile.epochs.times]),   max([sFile.epochs.times])];
end

%% ===== CREATE EMPTY CHANNEL FILE =====
nChannels = length(hdr.epoch(1).ChannelOrder);
ChannelMat = db_template('channelmat');
ChannelMat.Comment = [sFile.device ' channels'];
ChannelMat.Channel = repmat(db_template('channeldesc'), [1, nChannels]);
hdr.Gains = [];
% For each channel
for iChan = 1:nChannels
    % Find channel in description list
    chName = hdr.epoch(1).ChannelOrder{iChan};
    iDesc = find(strcmpi({hdr.epoch(1).Channel.Name}, chName));
    sDesc = hdr.epoch(1).Channel(iDesc);
    % Type
    if isfield(sDesc, 'IsEEG') && ~isempty(sDesc.IsEEG)
        if strcmpi(sDesc.IsEEG, 'Yes')
            ChannelMat.Channel(iChan).Type = 'EEG';
        else
            ChannelMat.Channel(iChan).Type = 'Misc';
        end
    elseif isfield(sDesc, 'IsEEG')
        ChannelMat.Channel(iChan).Type = 'EEG REF';
    else
        ChannelMat.Channel(iChan).Type = 'EEG';
    end
    % Create structure
    ChannelMat.Channel(iChan).Name    = chName;
    ChannelMat.Channel(iChan).Loc     = [0; 0; 0];
    ChannelMat.Channel(iChan).Orient  = [];
    ChannelMat.Channel(iChan).Weight  = 1;
    ChannelMat.Channel(iChan).Comment = '';
    % Check that this channel has a gain
    if isempty(hdr.epoch(1).Channel(iDesc).UnitDisplayToFile)
        hdr.Gains(iChan,1) = 1;
    else
        hdr.Gains(iChan,1) = hdr.epoch(1).Channel(iDesc).UnitDisplayToFile;
    end
end

%% ===== FORMAT EVENTS =====
if ~isempty(hdr.Events)
    % Get all the epochs names
    uniqueEvt = unique({hdr.Events.Name});
    % Create one category for each event
    for iEvt = 1:length(uniqueEvt)
        % Get all the occurrences
        iOcc = find(strcmpi({hdr.Events.Name}, uniqueEvt{iEvt}));
        % Get the samples for all the occurrences
        sample   = [hdr.Events(iOcc).BeginSample];
        duration = [hdr.Events(iOcc).DurationSamples];
        % Extended event = duration is not 1 for all the markers
        if ~all(duration == 1)
            sample = [sample; sample + duration];
        end
        % Create event structure
        sFile.events(iEvt).label    = uniqueEvt{iEvt};
        sFile.events(iEvt).times    = sample ./ sFile.prop.sfreq;
        sFile.events(iEvt).epochs   = [hdr.Events(iOcc).iEpoch];
        sFile.events(iEvt).select   = 1;
        sFile.events(iEvt).channels = [];
        sFile.events(iEvt).notes    = [];
    end
end
% Save file header
sFile.header = hdr;


function display(pot)

disp('conditional Gaussian potential object');
disp(struct(pot));


function works = test_leap()
%TEST_LEAP Checks whether LEAP is properly installed.
% Usage:
%   test_leap
%   works = test_leap % returns true/false
%
% See also: install_leap

works = true;

% Check if we can import the LEAP package from anywhere
cdCmd = ['cd "' matlabroot '"'];
if ispc(); cdCmd = ['cd /D "' matlabroot '"']; end
[status,msg] = system([cdCmd ' && python -c "import leap"']);

if status ~= 0
    works = false;
end

if nargout == 0
    if works
        printf('Test LEAP successful!')
    else
        disp('Unable to import LEAP python package. Make sure LEAP and its dependencies are installed.')
        disp('Go to the base LEAP directory containing setup.py and run from MATLAB:')
        disp('    !pip install -e .')
        disp('Or try the MATLAB installer:')
        disp('    install_leap')
    end
    clear works
end

end


function [model, specificData, coreRxnAbbr, modelGenerationReport] = growthMediaToModel(model, specificData, param, coreRxnAbbr, modelGenerationReport)
%
% USAGE:
%   [model, specificData, coreRxnAbbr, modelGenerationReport] = growthMediaToModel(model, specificData, param, coreRxnAbbr, modelGenerationReport)
%
% INPUTS:
%  model.rxns:
%  model.rxnNames:
%  model.lb:
%  model.ub:
%  specificData.mediaData:
%  specificData.mediaData.mets:
%  specificData.mediaData.rxns:
%  specificData.exoMet:
%
%  param.metabolomicsBeforeExtraction:
%  param.debug:
%  param.workingDirectory:
%  param.printLevel:
%  param.TolMinBoundary:
%  param.TolMaxBoundary:
%  param.relaxOptions:
%  coreRxnAbbr:
%  modelGenerationReport:
%
% OUTPUTS:
%  model.rxns:
%  model.rxnNames:
%  model.lb:
%  model.ub:
%  specificData.mediaData:
%  specificData.mediaData.mediaData:
%  specificData.mediaData.mets:
%  specificData.mediaData.Properties:
%  specificData.mediaData.rxns:
%  specificData.exoMet:
%  specificData.exoMet.exoMet:
%  specificData.exoMet.ismedia:
%  coreRxnAbbr:
%  modelGenerationReport:
%
% EXAMPLE:
%
% NOTE:
%
% Author(s): Aga W.

if param.metabolomicsBeforeExtraction && param.debug
    save([param.workingDirectory filesep '10.a.debug_prior_to_media_constraints.mat'])
elseif param.debug
    save([param.workingDirectory filesep '22.a.debug_prior_to_media_constraints.mat'])
end

%% 10/22.a. Set growth media constraints (if provided)

if isfield(specificData, 'mediaData') && ~isempty(specificData.mediaData)

    if param.printLevel > 0
        disp('--------------------------------------------------------------')
        disp(' ')
        disp('Adding growth media information...')
    end

    % Constrain media reactions
    if ~ismember('rxns', specificData.mediaData.Properties.VariableNames) && ismember('mets', specificData.mediaData.Properties.VariableNames)
        modelTemp = findSExRxnInd(model);
        allExRxns = model.rxns(modelTemp.ExchRxnBool);
        for i = 1:length(specificData.mediaData.mets)
            if any(contains(allExRxns, strcat('_', specificData.mediaData.mets(i))))
                specificData.mediaData.rxns(i) = allExRxns(contains(allExRxns, strcat('_', specificData.mediaData.mets(i))));
            end
        end
        [model, rxnsConstrained, rxnBoundsCorrected] = constrainRxns(model, specificData, param, 'mediaDataConstraints', param.printLevel);
    elseif ismember('rxns', specificData.mediaData.Properties.VariableNames)
        [model, rxnsConstrained, rxnBoundsCorrected] = constrainRxns(model, specificData, param, 'mediaDataConstraints', param.printLevel);
    end

    % Identify media reactions in metabolomic data
    if isfield(specificData,'exoMet')
        specificData.exoMet.ismedia = false(length(specificData.exoMet.mean), 1);
        specificData.exoMet.ismedia(ismember(specificData.exoMet.rxns, specificData.mediaData.rxns)) = 1;
    end

    if param.printLevel > 0
        disp(' ')
    end
    clear modelTemp

    if ~isempty(rxnsConstrained)
        if param.printLevel > 1
            fprintf('%s\n\n','Growth media bounds were set on the following reactions:')
            rxnsConstrainedBool = ismember(model.rxns, rxnsConstrained);
            table(model.rxns(rxnsConstrainedBool), model.rxnNames(rxnsConstrainedBool),...
                model.lb(rxnsConstrainedBool), model.ub(rxnsConstrainedBool), ...
                printRxnFormula(model, 'rxnAbbrList', model.rxns(rxnsConstrainedBool), ...
                'printFlag', false), 'VariableNames', {'rxnsConstrained', ...
                'Name', 'lb', 'ub', 'equation'})
            %printConstraints(model, options.TolMinBoundary, options.TolMaxBoundary, ismember(model.rxns, rxnsConstrained))
        end
    end

    if ~isempty(rxnBoundsCorrected)
        if param.printLevel > 1
            fprintf('%s\n\n','Bounds were corrected by constrainRxns on the following reactions:')
            printConstraints(model, param.TolMinBoundary, param.TolMaxBoundary, ismember(model.rxns, rxnBoundsCorrected))
        end
    end

    if any(model.lb > model.ub)
        error('lower bounds greater than upper bounds')
    end
end

% Check feasibility
if isfield(specificData, 'mediaData') && ~isempty(specificData.mediaData)
    sol = optimizeCbModel(model);
    if  sol.stat ~=1
        fprintf('%s\n','Infeasible after application of growth media constraints. Trying relaxation...')
        %options.relaxOptions.rxns = unique([options.exoMet.rxns; options.mediaData.rxns]);
        [solution, modelTemp] = relaxedFBA(model, param.relaxOptions);
        if solution.stat==1
            fprintf('%s\n','.. relaxation worked.')
            model = modelTemp;
        else
            error('Infeasible after application of media constraints and relaxation failed.')
        end
    elseif sol.stat ==1
        if param.printLevel>0
            disp(' ')
            fprintf('%s\n\n','Feasible after application of media constraints.')
        end
    end
end