function varargout = process_cohere1n_2021( varargin )
% PROCESS_COHERE1N_2021: Compute the coherence between all the pairs of signals, in one file.
%
% USAGE:   OutputFiles = process_cohere1n_2021('Run', sProcess, sInputA)
%                        process_cohere1n_2021('Test')

% @=============================================================================
% 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-2021
%          Hossein Shahabi, 2019-2020
%          Raymundo Cassani, 2021-2022

eval(macro_method);
end

%% ===== GET DESCRIPTION =====
function sProcess = GetDescription() %#ok<DEFNU>
    % Description the process
    sProcess.Comment     = 'Coherence NxN [2021]';
    sProcess.Category    = 'Custom';
    sProcess.SubGroup    = 'Connectivity';
    sProcess.Index       = 656;
    sProcess.Description = 'https://neuroimage.usc.edu/brainstorm/Tutorials/Connectivity';
    % Definition of the input accepted by this process
    sProcess.InputTypes  = {'data',     'results',  'matrix'};
    sProcess.OutputTypes = {'timefreq', 'timefreq', 'timefreq'};
    sProcess.nInputs     = 1;
    sProcess.nMinFiles   = 1;

    % === CONNECT INPUT
    sProcess = process_corr1n('DefineConnectOptions', sProcess, 1);
    % === REMOVE EVOKED REPONSE
    sProcess.options.removeevoked.Comment = 'Remove evoked response from each trial';
    sProcess.options.removeevoked.Type    = 'checkbox';
    sProcess.options.removeevoked.Value   = 0;
    sProcess.options.removeevoked.Group   = 'input';
    % === COHERENCE METHOD
    sProcess.options.cohmeasure.Comment = {...
        ['Magnitude-squared Coherence
' ...
        '|C|^2 = |Cxy|^2/(Cxx*Cyy)'], ...
        ['Imaginary Coherence (2019)
' ...
        'IC    = |imag(C)|'], ...
        ['Lagged Coherence (2019)
' ...
        'LC    = |imag(C)|/sqrt(1-real(C)^2)'], ...
        [' Imaginary Coherence (before 2019)
' ...
        ' IC    = imag(C)^2 / (1-real(C)^2) ']; ...
        'mscohere', 'icohere2019', 'lcohere2019', 'icohere'};
    sProcess.options.cohmeasure.Type    = 'radio_label';
    sProcess.options.cohmeasure.Value   = 'mscohere';
    % === WINDOW LENGTH
    sProcess.options.win_length.Comment = 'Window length for PSD estimation:';
    sProcess.options.win_length.Type    = 'value';
    sProcess.options.win_length.Value   = {1, 's', []};
    % === OVERLAP
    sProcess.options.overlap.Comment = 'Overlap for PSD estimation:' ;
    sProcess.options.overlap.Type    = 'value';
    sProcess.options.overlap.Value   = {50, '%', []};
    % === HIGHEST FREQUENCY OF INTEREST
    sProcess.options.maxfreq.Comment = 'Highest frequency of interest:';
    sProcess.options.maxfreq.Type    = 'value';
    sProcess.options.maxfreq.Value   = {60,'Hz',2};
    % === OUTPUT MODE 2021
    sProcess.options.outputmode.Comment = {'Save individual results (one output file per input file)', 'Average cross-spectra of input files (one output file)'; ...
                                           'input', 'avgcoh'};
    sProcess.options.outputmode.Type    = 'radio_label';
    sProcess.options.outputmode.Value   = 'input';
    sProcess.options.outputmode.Group   = 'output';
end

%% ===== FORMAT COMMENT =====
function Comment = FormatComment(sProcess) %#ok<DEFNU>
    Comment = sProcess.Comment;
end

%% ===== RUN =====
function OutputFiles = Run(sProcess, sInputA) %#ok<DEFNU>
    % Input options
    OPTIONS = process_corr1n('GetConnectOptions', sProcess, sInputA);
    if isempty(OPTIONS)
        OutputFiles = {};
        return
    end

    % Metric options
    OPTIONS.Method = 'cohere';
    OPTIONS.RemoveEvoked  = sProcess.options.removeevoked.Value;
    OPTIONS.WinLen        = sProcess.options.win_length.Value{1};
    OPTIONS.MaxFreq       = sProcess.options.maxfreq.Value{1};
    OPTIONS.CohOverlap    = 0.50;  % First pre-define the overlap
    OPTIONS.CohMeasure    = sProcess.options.cohmeasure.Value;

    % Change the overlap if it is specified
    if isfield(sProcess.options, 'overlap') && isfield(sProcess.options.overlap, 'Value') && ...
       iscell(sProcess.options.overlap.Value) && ~isempty(sProcess.options.overlap.Value) && ~isempty(sProcess.options.overlap.Value{1})
        OPTIONS.CohOverlap = sProcess.options.overlap.Value{1}/100 ;
    end

    % Compute metric
    OutputFiles = bst_connectivity({sInputA.FileName}, [], OPTIONS);
end

%% ===== TEST FUNCTION =====
function Test() %#ok<DEFNU>
    % Start a new report
    bst_report('Start');
    % Get test datasets
    sFile = process_simulate_ar('Test'); % Fs = 1200 Hz
    % NOTES:
    % bst_cohn.m (2019) uses 2^nextpow2(round(Fs / MaxFreqRes)) samples
    % of DATA for the FFT, there is no zero padding
    %
    % bst_cohn_2021.m uses nWinLen = round(WinLen * Fs) samples of DATA,
    % that are zero padded to 2^nextpow2(nWinLen * 2) for the FFT
    %
    % To get the similar results with bst_cohn and bst_cohn_2021:
    % 1. Select a MaxFreqRes that leads to a power-of-2 number of samples,
    % thus, we can be sure that no extra data is used in the FFT.
    % 2. Use the duration associated to that MaxFreqRes as WinLen parameter
    % for bst_cohn_2021, with this we will compute coherence on the same data

    Fs = 1200;      % Default Fs for process_simulate_ar('Test')
    nSamples = 512; % Desired number of samples
    MaxFreqRes = Fs/nSamples; % Hz ==> round(Fs / MaxFreqRes) = 512 samples
    WinLen = 1 / MaxFreqRes;  % s  ==> 512 samples zero-padded to 1024

    % Coherence process with bst_cohn.m (2019)
    tic;
    sTmp = bst_process('CallProcess', 'process_cohere1n', sFile, [], ...
        'timewindow',   [], ...          % All the time in input
        'cohmeasure',   'mscohere', ...  % 1=Magnitude-squared, 2=Imaginary
        'overlap',      50, ...          % 50%
        'maxfreqres',   MaxFreqRes, ...  % VARIES
        'maxfreq',      [], ...          % No maximum frequency
        'outputmode',   1);              % Save individual results (one file per input file)
    t = toc;
    % Execution time
    bst_report('Info', 'process_cohere1n', sFile, sprintf('Execution time: %1.6f seconds', t));
    % Add tag
    bst_process('CallProcess', 'process_add_tag', sTmp.FileName, [], 'tag', '(2019)' );
    % Snapshot: spectrum
    bst_process('CallProcess', 'process_snapshot', sTmp, [], ...
        'target',       11, ...  % Connectivity matrix (image)
        'modality',     1, 'orient', 1, 'time', 0, 'contact_time', [-40, 110], 'contact_nimage', 16, ...
        'Comment',      [sTmp.Comment, ': (2019)']);

    % Coherence process with bst_cohn_2021.m
    tic;
    sTmp = bst_process('CallProcess', 'process_cohere1n_2021', sFile, [], ...
        'timewindow',   [], ...          % All the time in input
        'includebad',   1, ...
        'removeevoked', 0, ...
        'cohmeasure',   'mscohere', ...  % Magnitude-squared Coherence|C|^2 = |Cxy|^2/(Cxx*Cyy)
        'win_length',   WinLen, ...
        'overlap',      50, ...          % 50%
        'maxfreq',      [], ...
        'outputmode',   'input');        % Save individual results (one output file per input file)
    t = toc;
    % Execution time
    bst_report('Info', 'process_cohere1n_2021', sFile, sprintf('Execution time: %1.6f seconds', t));
    % Snapshot: spectrum
    bst_process('CallProcess', 'process_snapshot', sTmp, [], ...
        'target',         11, ...  % Connectivity matrix (image)
        'modality',       1, ...
        'orient',         1, ...
        'contact_nimage', 16, ...
        'Comment',      [sTmp.Comment, ': (2021)']);

    % Save and display report
    ReportFile = bst_report('Save', sTmp);
    bst_report('Open', ReportFile);
end


 function ob = Gtomo3(sys_type, mask, nx, ny, nz, varargin)
%function ob = Gtomo3(sys_type, mask, nx, ny, nz, options)
%|
%| Construct Gtomo3 object, for 3D tomographic image reconstruction,
%| especially for SPECT and perhaps PET.  (For CT use Gcone; see below.)
%| This object does 3d forward and backprojection using mex files.
%| See Gtomo3_test.m for example usage.
%|
%| Basically, you create an object calling:
%|		A = Gtomo3(sys_type, mask)
%| and then you can use it thereafter by typing commands like
%|		y = A * x;
%| which will auto-magically do the multiplication by calling a mex file.
%|
%| in
%|	sys_type	string	see 3D ASPIRE User's Guide
%|	mask	[nx ny nz] or string
%|		mask can be a filename, in which case the file is read.
%|		mask can be empty (for some system types, including 3s),
%|		in which case it is determined from backprojecting a "1" vector.
%|		Else, mask must be [nx ny nz] binary array of object support.
%|
%| options
%|	'nthread' 1, 2...	1, unless you have a multi-processor system
%|	'chat' 0|1		verbosity.  default: 1.
%|	'checkmask' 0|1		verify that the mask is correct.  default: 1.
%|	'view2d' 0|1		for 2z and 2dsc system types, use 2d views
%|				instead of sinograms, for use with OS.
%|				default: 0
%|	'permute213' 0|1	convert from 123 to 213 output order
%|				(e.g., to overcome vu vs st order of 3l@)
%|				default: 0
%|	'class' ''		default 'fatrix2'
%|	'scale' [1]		scale factor.  default '' (none)
%|
%| out
%|	ob [nd np]	np = sum(mask(:)), so it is already "masked"
%|
%| For more help, type 'f3d_mex' and see Gtomo3_test.m
%|
%| For X-ray CT, using Gcone() is recommended instead of Gtomo3().
%| Nevertheless, the following alternate usage is permitted for CT here:
%| function ob = Gtomo3(cg, ig, options)
%|
%| Copyright 01-04-22, Jeff Fessler, University of Michigan

if nargin == 1 && streq(sys_type, 'test'), Gtomo3_test, return, end
if nargin < 1, ir_usage, end

% handle new usage (for CT)
% (cg, ig, [options])
if isa(sys_type, 'strum')
	cg = sys_type;
	ig = mask;
	options = {};
	if isvar('nx')
		options = {nx};
		if isvar('ny')
			options = {nx, ny};
			if isvar('nz')
				options = {nx, ny, nz};
			end
		end
	end
	options = {options{:}, varargin{:}};

	sys_type = aspire_pair(cg, ig, 'system', '3l');
	ob = Gtomo3(sys_type, ig.mask, ig.nx, ig.ny, ig.nz, options{:});
return
end

arg.sys_type = sys_type;
arg.nx = nx;
arg.ny = ny;
arg.nz = nz;
arg.nxyz = nx*ny*nz;
arg.mask = logical(mask);

% defaults
arg.nthread = 1;
arg.chat = true;
arg.checkmask = false;
arg.scale = [];
arg.view2d = false;
arg.permute213 = false;
arg.class = 'fatrix2';
% arg.class = 'fatrix2';
arg.nthread_max = 8; % > 8 threads may crash matlab in Gtomo3_test for '3s'
		% due to f3d_mex bugs?  increase this at your own risk!

if ischar(arg.mask)
	arg.mask = fld_read(arg.mask); % read mask file
end

% options
arg = vararg_pair(arg, varargin);

if arg.nthread > arg.nthread_max
	warn('%d threads downgraded to %d, proceed with caution!', ...
		arg.nthread, arg.nthread_max)
	arg.nthread = arg.nthread_max;
end

if arg.view2d && ~streq(arg.sys_type, '2', 1)
	fail 'view2d option only relevant to 2dsc and 2z system types'
end
if arg.permute213 && ~streq(arg.sys_type, '3l', 2)
	warn 'permute213 designed only for "3l" system model!?'
end

% if no mask, then backproject to find it!
if isempty(arg.mask)
	arg.optr = f3d_mex('init', arg.sys_type, uint8(ones(nx,ny,nz)), ...
		int32(arg.nx), int32(arg.ny), int32(arg.nz), ...
		int32(arg.nthread), int32(arg.chat))
	warn('creating mask by summing will be a bit slow')
	t = double(f3d_mex('dims', arg.optr)); % data dimensions, matlab wants doubles
	arg.mask = f3d_mex('back', ones(t, 'single'), ...
			int32(0), int32(1), arg.optr, int32(0)) > 0;
	f3d_mex('free:1', arg.optr)
end

% initialize mex file
arg.optr = f3d_mex('init', arg.sys_type, uint8(arg.mask), ...
	int32(arg.nx), int32(arg.ny), int32(arg.nz), ...
	int32(arg.nthread), int32(arg.chat));
if arg.chat
%	printm('arg.optr = %lX', arg.optr)
end

arg.odim = Gtomo3_nn(arg);
arg.nd = prod(arg.odim);
arg.np = sum(arg.mask(:));
dim = [arg.nd arg.np]; % trick: make it masked by default!

% trick: handle 3l@ uv vs st mismatch (if requested) by permuting.
if arg.permute213
%	if ~isempty(after), fail('permute213 and scale not done'), end
%	after = @(x, is_transpose, istart, nblock) permute(x, [2 1 3]);
%	after = {'cascade_after', after};
	warn 'still testing permute213'
end

abs_arg = {'abs', @(ob) ob}; % aij nonnegative, except perhaps SPECT rotate
if streq(arg.sys_type, '3s@', 3)
	abs_arg = {}; % todo: check for spline_filter 3, else ok
end

switch arg.class
case 'fatrix2'
	forw = @(arg, x) Gtomo3_block_forw_array(arg, x, 1, 1);
	back = @(arg, y) Gtomo3_block_back_array(arg, y, 1, 1);
	forw_block = @(arg, x, iblock, nblock) ...
		Gtomo3_block_forw_array(arg, x, iblock, nblock);
	back_block = @(arg, y, iblock, nblock) ...
		Gtomo3_block_back_array(arg, y, iblock, nblock);

	ob = fatrix2('mask', arg.mask, 'arg', arg, abs_arg{:}, ...
		'does_many', 1, ...
		'forw', forw, 'back', back, ...
		'free', @Gtomo3_free, ...
		'forw_block', forw_block, 'back_block', back_block);
	if ~isempty(arg.scale)
		ob = arg.scale * ob;
		arg.scale = [];
	end

case 'Fatrix' % build Fatrix object
	if ~isempty(arg.scale)
		after = {'cascade_after', arg.scale};
	else
		after = {};
	end

	ob = Fatrix(dim, arg, 'caller', 'Gtomo3', abs_arg{:}, ...
		'forw', @Gtomo3_forw_Fatrix, ...
		'back', @Gtomo3_back_Fatrix, ...
		'free', @Gtomo3_free, ...
		'mtimes_block', @Gtomo3_mtimes_block_Fatrix, ...
		after{:});

otherwise
	fail('class %s', arg.class)
end

if arg.chat
	f3d_mex('show:1', arg.optr, int32(arg.chat)); % display header info
end

% check mask
if arg.checkmask
	printm 'Warn checking mask, which may be unnecessary'
	tmp = sum(ob).' > 0;
	tmp = embed(tmp, arg.mask);
	if ~isequal(tmp, arg.mask)
		if exist('imfill') == 2
			tmpfill = imfill(tmp);
			if ~isequal(tmpfill, arg.mask)
				if im
					im plc 1 2
					im(1, arg.mask, 'input mask')
					im(2, tmp, 'computed mask')
				end
				warn('mask-sum mismatch not corrected by fill')
			else
				warn('mask-sum mismatch; corrected by filling')
			end
			printm('Warn: this geometry may have poor sampling')
		else
			fail 'mask inconsistent with sum'
		end
	end
	printm('mask checking completed')
end

% Gtomo3_nn(): data dimensions
function nn = Gtomo3_nn(arg)
nn = double(f3d_mex('dims', arg.optr)); % silly matlab wants size to be double
if arg.view2d
	nn = nn([1 3 2]); % swap projection angle and slice index
end
if arg.permute213
	nn = nn([2 1 3]); % trick: swap vu to st for 3l@
end

% Gtomo3_forw_Fatrix(): y = G * x
% full projection
function y = Gtomo3_forw_Fatrix(arg, x)
y = Gtomo3_mtimes_block_Fatrix(arg, 0, x, 1, 1);

% Gtomo3_back_Fatrix(): x = G' * y
% full backprojection
function x = Gtomo3_back_Fatrix(arg, y)
x = Gtomo3_mtimes_block_Fatrix(arg, 1, y, 1, 1);

% Gtomo3_mtimes_block_Fatrix()
function y = Gtomo3_mtimes_block_Fatrix(arg, is_transpose, x, istart, nblock)
if is_transpose
	y = Gtomo3_block_back_Fatrix(arg, x, istart, nblock);
else
	y = Gtomo3_block_forw_Fatrix(arg, x, istart, nblock);
end

% Gtomo3_block_forw_Fatrix()
function y = Gtomo3_block_forw_Fatrix(arg, x, istart, nblock)

[x ei] = embed_in(x, arg.mask, arg.np);
y = Gtomo3_block_forw_array(arg, x, istart, nblock);
y = ei.shape(y);

% Gtomo3_block_back_Fatrix()
function x = Gtomo3_block_back_Fatrix(arg, y, istart, nblock)

na = arg.odim(end); % last index must be the one over which we subsetize
ia = istart:nblock:na;
[y eo] = embed_out(y, [arg.odim(1:end-1) length(ia)]); % [(M) *L]
x = Gtomo3_block_back_array(arg, y, istart, nblock);
x = eo.shape(x, arg.mask, arg.np);

% Gtomo3_free()
function Gtomo3_free(arg)
if arg.chat
	printm('freeing Gtomo3 object static memory')
	warn('further use of this Gtomo3 object is invalid')
end
f3d_mex('free:1', arg.optr);

%
% fatrix2 versions "_array"
%

% Gtomo3_block_forw_array()
% x: [nx ny nz *L]
% y: [odim(1:end-1) "na" *L] where "na" is # of angles in this block
function y = Gtomo3_block_forw_array(arg, x, istart, nblock)

for i4=1:size(x,4)
	y(:,:,:,i4) = f3d_mex('proj', single(x(:,:,:,i4)), ...
		int32(istart-1), int32(nblock), arg.optr, int32(arg.chat));
end
if arg.view2d % trick: for 2z and 2dsc
	y = permute(y, [1 3 2 4]);
end
if arg.permute213 % trick: for 3l@
	y = permute(y, [2 1 3 4]);
end
na = arg.odim(end); % last index must be the one over which we subsetize
ia = istart:nblock:na;
y = reshapee(y, prod(arg.odim(1:end-1)), na, []);
LL = size(y,3);
y = y(:,ia,:);
y = reshape(y, [arg.odim(1:end-1) length(ia) LL]); % [(M) *L]

% Gtomo3_block_back_array()
function x = Gtomo3_block_back_array(arg, y, istart, nblock)

na = arg.odim(end); % last index must be the one over which we subsetize
ia = istart:nblock:na;

n1 = prod(arg.odim(1:end-1));
y = reshapee(y, n1, length(ia), []);
yy = size(y); yy(2) = na; yy = zeros(yy);
yy(:,ia,:) = y;
dim = num2cell(arg.odim);
y = reshapee(yy, dim{:}, []);

if arg.permute213 % trick: for 3l@
	y = permute(y, [2 1 3 4]);
end
if arg.view2d % trick for 2d
	y = permute(y, [1 3 2 4]);
end
for i4 = 1:size(y,4)
	x(:,:,:,i4) = f3d_mex('back', single(y(:,:,:,i4)), ...
		int32(istart-1), int32(nblock), arg.optr, int32(arg.chat));
end


%% (Internal) Alineate wave positions (onset, peak and offsets commonly)
%
%     slpos_out = groupnlineup_waves(slpos)
%
%
% Arguments:
%
%   + slpos: delineation structure,
%
% Output:
%
%   + slpos_out: delineation structure with all heartbeats lined up with its
%   respective waves
%
% Example:
%
%
% See also alineate_positions
%
% Author: Mariano Llamedo Soria (llamedom at frba.utn.edu.ar)
% Version: 0.1 beta
% Birthdate: 17/05/2016
% Last update: 17/05/2016
% Copyright 2008-2016
%
function slpos_out = groupnlineup_waves(slpos)

cant_leads = length(slpos);
[slpos_mat, slpos_names] = positions2matrix(slpos);

for this_lead = 1:cant_leads

    cant_QRS = length(slpos(this_lead).qrs);
    this_pos_mat = slpos_mat{this_lead};
    cant_anns = size(this_pos_mat,1);

    if( isempty(this_pos_mat) )
        continue
    end

    prev_QRS = 1;

    for ii = 1:cant_QRS

        if( ii == cant_QRS)
            next_QRS = slpos(this_lead).qrs(cant_QRS) + 1.5* (slpos(this_lead).qrs(cant_QRS)-slpos(this_lead).qrs(cant_QRS-1));
        else
            next_QRS = slpos(this_lead).qrs(ii+1);
        end

        slpos_out(this_lead).Pon(ii) = match_value(slpos(this_lead).Pon, slpos(this_lead).qrs(ii), prev_QRS, slpos(this_lead).qrs(ii));
        slpos_out(this_lead).P(ii) = match_value(slpos(this_lead).P, slpos(this_lead).qrs(ii), prev_QRS, slpos(this_lead).qrs(ii));
        slpos_out(this_lead).Poff(ii) = match_value(slpos(this_lead).Poff, slpos(this_lead).qrs(ii), prev_QRS, slpos(this_lead).qrs(ii));
        slpos_out(this_lead).Ptipo(ii) = nan;

        slpos_out(this_lead).QRSon(ii) = match_value(slpos(this_lead).QRSon, slpos(this_lead).qrs(ii), prev_QRS, slpos(this_lead).qrs(ii));
        slpos_out(this_lead).qrs(ii) = slpos(this_lead).qrs(ii);
        slpos_out(this_lead).Q(ii) = match_value(slpos(this_lead).Q, slpos(this_lead).qrs(ii), prev_QRS, next_QRS);
        slpos_out(this_lead).R(ii) = match_value(slpos(this_lead).R, slpos(this_lead).qrs(ii), prev_QRS, next_QRS);
        slpos_out(this_lead).S(ii) = match_value(slpos(this_lead).S, slpos(this_lead).qrs(ii), prev_QRS, next_QRS);
        slpos_out(this_lead).QRSoff(ii) = match_value(slpos(this_lead).QRSoff, slpos(this_lead).qrs(ii), slpos(this_lead).qrs(ii), next_QRS);

        slpos_out(this_lead).Ton(ii) = match_value(slpos(this_lead).Ton, slpos(this_lead).qrs(ii), slpos(this_lead).qrs(ii), next_QRS);
        slpos_out(this_lead).T(ii) = match_value(slpos(this_lead).T, slpos(this_lead).qrs(ii), slpos(this_lead).qrs(ii), next_QRS);
        slpos_out(this_lead).Tprima(ii) = match_value(slpos(this_lead).Tprima, slpos(this_lead).qrs(ii), slpos(this_lead).qrs(ii), next_QRS);
        slpos_out(this_lead).Toff(ii) = match_value(slpos(this_lead).Toff, slpos(this_lead).qrs(ii), slpos(this_lead).qrs(ii), next_QRS);
        slpos_out(this_lead).Ttipo(ii) = nan;

        prev_QRS = slpos(this_lead).qrs(ii);

    end

    if( cant_QRS == 0 )
        slposout_mat = [];
    else
        % group remaining waves if any
        slposout_mat = positions2matrix(slpos_out(this_lead));
    end

    cant_names = length(slpos_names);
    other_waves = cell(1,cant_names);

    for ii = 1:cant_names
        if( all(isnan(this_pos_mat(:,ii))) )
            other_waves{ii} = [];
        else
            if( isempty(slposout_mat) )
                aux_val = this_pos_mat(:,ii);
            else
                aux_val = setdiff(this_pos_mat(:,ii), slposout_mat(:,ii));
            end
            other_waves{ii} = aux_val(~isnan(aux_val));
        end
    end

    if( any(cellfun(@(a)(~isempty(a)), other_waves)) )
        % Group P waves and add as a new heartbeat
        [~, wave_idx] = intersect(slpos_names, {'Pon' 'P' 'Poff'});
        if( any(cellfun(@(a)(~isempty(a)), other_waves(wave_idx) )) )
            other_waves(wave_idx) = alineate_positions( other_waves(wave_idx) );
            aux_val = nan(length(other_waves{wave_idx(1)}),cant_names);
            aux_val(:,wave_idx) = cell2mat(other_waves(wave_idx));
            slposout_mat = [ slposout_mat; aux_val ];
        end

        % Group QRS waves and add as a new heartbeat
        [~, wave_idx] = intersect(slpos_names, {'QRSon' 'qrs' 'Q' 'R' 'S' 'QRSoff'});
        if( any(cellfun(@(a)(~isempty(a)), other_waves(wave_idx) )) )
            other_waves(wave_idx) = alineate_positions( other_waves(wave_idx) );
            aux_val = nan(length(other_waves{wave_idx(1)}),cant_names);
            aux_val(:,wave_idx) = cell2mat(other_waves(wave_idx));
            slposout_mat = [ slposout_mat; aux_val ];
        end

        % Group T waves and add as a new heartbeat
        [~, wave_idx] = intersect(slpos_names, {'Ton' 'T' 'Toff'});
        if( any(cellfun(@(a)(~isempty(a)), other_waves(wave_idx) )) )
            other_waves(wave_idx) = alineate_positions( other_waves(wave_idx) );
            aux_val = nan(length(other_waves{wave_idx(1)}),cant_names);
            aux_val(:,wave_idx) = cell2mat(other_waves(wave_idx));
            slposout_mat = [ slposout_mat; aux_val ];
        end

        slpos_out(this_lead) = matrix2positions(slposout_mat, slpos_names);
    end

    % sort heartbeats and waves and add as a new heartbeat
    [~, aux_idx] = sort((sum(~isnan(this_pos_mat))), 'descend');
    aux_val = slpos_out(this_lead).( slpos_names{aux_idx(1)} );

    [~, aux_idx] = sort(aux_val);

    for fname = slpos_names
        if( isfield(slpos_out(this_lead), fname{1}) )
            aux_val = slpos_out(this_lead).(fname{1});
            if( isempty(aux_val) )
                slpos_out(this_lead).(fname{1}) = nan(cant_anns,1);
            else
                slpos_out(this_lead).(fname{1}) = colvec(aux_val(aux_idx));
            end
        else
            slpos_out(this_lead).(fname{1}) = nan(cant_anns,1);
        end
    end

end

function close_val = get_closer(values, reference)

    close_val = values( min_index(abs(values-reference)) );

function vals_in_range = get_values_in_range(values, start_range, end_range)

    vals_in_range = values( values >= start_range & values <= end_range );

function matched_val = match_value(values, reference, start_range, end_range)

    aux_val = get_values_in_range(values, start_range, end_range);

    if( isempty(aux_val) )
        matched_val = nan;
    else
        matched_val = get_closer(aux_val, reference);
        if( isempty(matched_val) )
            matched_val = nan;
        end
    end


function [s, cfg] = ft_statfun_indepsamplesZcoh(cfg, dat, design)

% FT_STATFUN_INDEPSAMPLESCOHZ calculates the independent samples coherence
% Z-statistic on the biological data in dat (the dependent variable), using the
% information on the independent variable (ivar) in design.
%
% Use this function by calling one of the high-level statistics functions as
%   [stat] = ft_timelockstatistics(cfg, timelock1, timelock2, ...)
%   [stat] = ft_freqstatistics(cfg, freq1, freq2, ...)
%   [stat] = ft_sourcestatistics(cfg, source1, source2, ...)
% with the following configuration option
%   cfg.statistic = 'ft_statfun_indepsamplesZcoh'
%
% The samples-dimension of the dat-variable must be the result of a reshaping
% operation applied to a data structure with dimord chan_(freq_time) or
% pos_(freq_time). The configuration must contain channel labels in cfg.label or
% position information in cfg.pos. This information is used to determine the number
% of channels. The dimord of the output fields is [prod(nchancmb,nfreq,ntime),1]. The
% channel combinations are the elements of the lower diagonal of the cross-spectral
% density matrix.
%
% Configuration options
%   cfg.computestat    = 'yes' or 'no', calculate the statistic (default='yes')
%   cfg.computecritval = 'yes' or 'no', calculate the critical values of the test statistics (default='no')
%   cfg.computeprob    = 'yes' or 'no', calculate the p-values (default='no')
%
% The following options are relevant if cfg.computecritval='yes' and/or
% cfg.computeprob='yes'.
%   cfg.alpha = critical alpha-level of the statistical test (default=0.05)
%   cfg.tail  = -1, 0, or 1, left, two-sided, or right (default=1)
%               cfg.tail in combination with cfg.computecritval='yes'
%               determines whether the critical value is computed at
%               quantile cfg.alpha (with cfg.tail=-1), at quantiles
%               cfg.alpha/2 and (1-cfg.alpha/2) (with cfg.tail=0), or at
%               quantile (1-cfg.alpha) (with cfg.tail=1).
%
% Design specification
%   cfg.ivar  = column number of the design that contains the labels of the conditions that
%               must be compared (default=1). The labels are the numbers 1 and 2.
%
% See also FT_TIMELOCKSTATISTICS, FT_FREQSTATISTICS or FT_SOURCESTATISTICS

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

% set the defaults
if ~isfield(cfg, 'computestat'),       cfg.computestat='yes';     end
if ~isfield(cfg, 'computecritval'),    cfg.computecritval='no';   end
if ~isfield(cfg, 'computeprob'),       cfg.computeprob='no';      end
if ~isfield(cfg, 'alpha'),             cfg.alpha=0.05;            end
if ~isfield(cfg, 'tail'),              cfg.tail=1;                end

% perform some checks on the configuration
if strcmp(cfg.computeprob,'yes') && strcmp(cfg.computestat,'no')
    ft_error('P-values can only be calculated if the test statistics are calculated.');
end
if isfield(cfg,'uvar') && ~isempty(cfg.uvar)
    ft_error('cfg.uvar should not exist for an independent samples statistic');
end
% if ~isfield(cfg, 'label') && ~isfield(cfg, 'pos')
%   ft_error('the configuration needs to contain either a label or a pos field');
% elseif isfield(cfg, 'label') && isfield(cfg, 'pos') && ~isempty(cfg.label) && ~isempty(cfg.pos)
%   ft_error('the configuration needs to contain either a non-empty label or a non-empty pos field');
% elseif isfield(cfg, 'label') && ~isempty(cfg.label)
%   nchan = length(cfg.label);
% elseif isfield(cfg, 'pos') && ~isempty(cfg.pos)
%   nchan = size(cfg.pos,1);
% end
nchan = cfg.dim(1);

% perform some checks on the design
selc1 = find(design(cfg.ivar,:)==1);
selc2 = find(design(cfg.ivar,:)==2);
nreplc1 = length(selc1);
nreplc2 = length(selc2);
nrepl = nreplc1 + nreplc2;
if nrepl<size(design,1)
  ft_error('Invalid specification of the independent variable in the design array.');
end
if nreplc1<2 || nreplc2<2
    ft_error('Every condition must contain at least two trials/tapers.');
end
dfc1 = nreplc1*2;
dfc2 = nreplc2*2;

if strcmp(cfg.computestat, 'yes')
  % compute the statistic
  nsamples = size(dat,1);
  %nchan = length(cfg.label); %this is computed earlier
  chancmbsel = find(tril(ones(nchan),-1));
  nfreqtim = nsamples/nchan;
  nchancmb = length(chancmbsel);
  nnewsamples = nchancmb*nfreqtim;
  s.stat = zeros(nnewsamples,1);
  for freqtimindx=1:nfreqtim
    chansel=((freqtimindx-1)*nchan + 1):(freqtimindx*nchan);
    csdc1=dat(chansel,selc1)*dat(chansel,selc1)'/nreplc1;
    powerc1=diag(csdc1);
    normmat=diag(powerc1.^(-1/2));
    csdc1=normmat*csdc1*normmat;
    csdc2=dat(chansel,selc2)*dat(chansel,selc2)'/nreplc2;
    powerc2=diag(csdc2);
    normmat=diag(powerc2.^(-1/2));
    csdc2=normmat*csdc2*normmat;
    biasc1=1./(dfc1-2);
    biasc2=1./(dfc2-2);
    denomZ=sqrt(1./(dfc1-2) + 1./(dfc2-2));
    tempstat=(atanh(abs(csdc1))-biasc1-atanh(abs(csdc2))+biasc2)./denomZ;
    s.stat(((freqtimindx-1)*nchancmb + 1):(freqtimindx*nchancmb))=tempstat(chancmbsel);
  end
  s.stat = reshape(s.stat, [nchancmb nfreqtim]);
end

if strcmp(cfg.computecritval,'yes')
  % also compute the critical values
  if cfg.tail==-1
    s.critval = norminv(cfg.alpha);
  elseif  cfg.tail==0
    s.critval = [norminv(cfg.alpha/2),norminv(1-cfg.alpha/2)];
  elseif cfg.tail==1
    s.critval = norminv(1-cfg.alpha);
  end
end

if strcmp(cfg.computeprob,'yes')
  % also compute the p-values
  if cfg.tail==-1
    s.prob = normcdf(s.stat);
  elseif  cfg.tail==0
    s.prob = 2*normcdf(-abs(s.stat));
  elseif cfg.tail==1
    s.prob = 1-normcdf(s.stat);
  end
  s.prob = reshape(s.prob, [nchancmb nfreqtim]);
end

% adjust the dimord
if strcmp(cfg.dimord(1:3), 'pos')
  cfg.dimord = ['poscmb',cfg.dimord(4:end)];
elseif strcmp(cfg.dimord(1:4), 'chan')
  cfg.dimord = ['chancmb',cfg.dimord(5:end)];
end

% append an indexing matrix to the cfg to be able to recover the channel
% combinations
chanindx = tril(true(nchan),-1);
cmbindx1 = repmat((1:nchan)', [1 nchan]);
cmbindx2 = repmat((1:nchan),  [nchan 1]);
cfg.chancmbindx(:,1) = cmbindx1(chanindx);
cfg.chancmbindx(:,2) = cmbindx2(chanindx);


function y = vl_nnconcat(inputs, dim, dzdy, varargin)
%VL_NNCONCAT CNN concatenate multiple inputs.
%  Y = VL_NNCONCAT(INPUTS, DIM) concatenates the inputs in the cell
%  array INPUTS along dimension DIM generating an output Y.
%
%  DZDINPUTS = VL_NNCONCAT(INPUTS, DIM, DZDY) computes the derivatives
%  of the block projected onto DZDY. DZDINPUTS has one element for
%  each element of INPUTS, each of which is an array that has the same
%  dimensions of the corresponding array in INPUTS.

% Copyright (C) 2015 Karel Lenc and Andrea Vedaldi.
% All rights reserved.
%
% This file is part of the VLFeat library and is made available under
% the terms of the BSD license (see the COPYING file).

opts.inputSizes = [] ;
opts = vl_argparse(opts, varargin, 'nonrecursive') ;

if nargin < 2, dim = 3; end;
if nargin < 3, dzdy = []; end;

if isempty(dzdy)
  y = cat(dim, inputs{:});
else
  if isempty(opts.inputSizes)
    opts.inputSizes = cellfun(@(inp) [size(inp,1),size(inp,2),size(inp,3),size(inp,4)], inputs, 'UniformOutput', false) ;
  end
  start = 1 ;
  y = cell(1, numel(opts.inputSizes)) ;
  s.type = '()' ;
  s.subs = {':', ':', ':', ':'} ;
  for i = 1:numel(opts.inputSizes)
    stop = start + opts.inputSizes{i}(dim) ;
    s.subs{dim} = start:stop-1 ;
    y{i} = subsref(dzdy,s) ;
    start = stop ;
  end
end


function lifetimes = getStateLifeTimes (Gamma,T,options,threshold,threshold_Gamma,do_concat)
% Computes the state life times for the state time courses, in number of time points
%
% Gamma can be the probabilistic state time courses (time by states),
%   which can contain the probability of all states or a subset of them,
%   or the Viterbi path (time by 1).
% In the first case, threshold_Gamma is used to define when a state is active.
%
% The parameter threshold is used to discard visits that are too
%   short (deemed to be spurious); these are discarded when they are shorter than
%   'threshold' time points
%
% The parameter 'options' must be the same than the one supplied to the
% hmmmar function for training.
%
% lifetimes is then a cell (subjects/segments/trials by states), where each element is a
%   vector of life times
%
% if do_concat is specified, the lifetimes are concatenated across
% segments of data, so that lifetimes is a single cell (1 by states)
%
% Diego Vidaurre, OHBA, University of Oxford (2016)

raise_warning=false;
if nargin<3, options = struct(); options.Fs = 1; options.downsample = 1; end
if ~isfield(options,'Fs'), options.Fs = 1; end
if ~isfield(options,'downsample'), options.downsample = options.Fs; end
if ~isfield(options, 'dropstates'), options.dropstates = 0; end
if nargin<4 || isempty(threshold), threshold = 0; end
if nargin<5 || isempty(threshold_Gamma), raise_warning=true; threshold_Gamma = (2/3); end
if nargin<6 || isempty(do_concat), do_concat = true; end

is_vpath = (size(Gamma,2)==1 && all(rem(Gamma,1)==0));
if iscell(T)
    if size(T,1) == 1, T = T'; end
    for i = 1:length(T)
        if size(T{i},1)==1, T{i} = T{i}'; end
    end
    Nsubj = length(T);
    trials2subjects = zeros(length(cell2mat(T)),1); ii = 1;
    for i = 1:length(T)
        Ntrials = length(T{i});
        trials2subjects(ii:ii+Ntrials-1) = i;
        ii = ii + Ntrials;
    end
    T = cell2mat(T);
else
    Nsubj = length(T);
    trials2subjects = 1:Nsubj;
end
N = length(T);

r = 1;
if isfield(options,'downsample') && options.downsample>0
    r = (options.downsample/options.Fs);
end

if ~isfield(options,'order') && ~isfield(options,'embeddedlags')
   options.order = (sum(T) - size(Gamma,1)) / length(T);
end

if isfield(options,'tuda') && options.tuda | ...
        isfield(options, 'order') && options.order == 0
    T = ceil(r * T);
elseif isfield(options,'order') && options.order > 0
    T = ceil(r * T);
    T = T - options.order;
elseif isfield(options,'embeddedlags') && length(options.embeddedlags) > 1
    d1 = -min(0,options.embeddedlags(1));
    d2 = max(0,options.embeddedlags(end));
    T = T - (d1+d2);
    T = ceil(r * T);
end

if is_vpath % viterbi path
    vpath = Gamma;
    if options.dropstates==1
        K = length(unique(vpath));
    else
        K = options.K;
    end
    Gamma = zeros(length(vpath),K);
    for k = 1:K
       Gamma(vpath==k,k) = 1;
    end
else
    if raise_warning
        warning(['Using the Viterbi path is here recommended instead of the state ' ...
            'probabilistic time courses (Gamma)'])
    end
    K = size(Gamma,2);
    Gamma = Gamma > threshold_Gamma;
end

if ~do_concat
    lifetimes = cell(Nsubj,K);
    for j = 1:N
        t0 = sum(T(1:j-1));
        ind = (1:T(j)) + t0;
        if length(ind)==1, continue; end
        jj = trials2subjects(j);
        for k = 1:K
            lifetimes{jj,k} = [lifetimes{jj,k} aux_k(Gamma(ind,k),threshold)];
        end
    end
else
    lifetimes = cell(1,K);
    for j = 1:N
        t0 = sum(T(1:j-1));
        ind = (1:T(j)) + t0;
        if length(ind)==1, continue; end
        for k = 1:K
            lifetimes{k} = [lifetimes{k} aux_k(Gamma(ind,k),threshold)];
        end
    end
end

end

function lifetimes = aux_k(g,threshold)

lifetimes = [];

while ~isempty(g)

    t = find(g==1,1);
    if isempty(t), break; end
    lt = find(g(t+1:end)==0,1);

    if isempty(lt) % end of trial
        if (length(g)-t+1)>threshold
            lifetimes = [lifetimes (length(g)-t+1)];
        else
            g(t:end) = 0; % too short visit to consider
        end
        break;
    end

    if lt<threshold % too short visit, resetting g and trying again
        g(t:t+lt-1) = 0;
        continue;
    end

    lifetimes = [lifetimes lt];
    tnext = t + lt;
    g = g(tnext:end);

end

end


% TraceHeaderDef : Defines names, position, and precision for Trace Headers
%
% % To get a Matlab structure with trace header definitions call:
% STH==TraceHeaderDef;
% % To get a list fo trace header definision listed on the screen call:
% STH==TraceHeaderDef(1)
%
% See also: ReadSegyTraceHeaderValue, WriteSegyTraceHeaderValue
%
function STH=TraceHeaderDef(print);

if nargin>0
    STH=TraceHeaderDef;
    fn=fieldnames(STH);
    disp(sprintf('%4s %6s %s','POS','PREC','Traece Header Name'))
    for i=1:length(fn)
        disp(sprintf('%4d %6s %s',STH.(fn{i}).pos,STH.(fn{i}).precision,fn{i}))
    end
    return
end

%NEW
STH.TraceSequenceLine.pos=0; STH.TraceSequenceLine.precision='int32';
STH.TraceSequenceFile.pos=4; STH.TraceSequenceFile.precision='int32';
STH.FieldRecord.pos=8;       STH.FieldRecord.precision='int32';
STH.TraceNumber.pos=12;      STH.TraceNumber.precision='int32';
STH.EnergySourcePoint.pos=16;STH.EnergySourcePoint.precision='int32';
STH.cdp.pos=20;              STH.cdp.precision='int32';
STH.cdpTrace.pos=24;         STH.cdpTrace.precision='int32';

STH.TraceIdentifactionCode.pos=28;  STH.TraceIdentifactionCode.precision='int16';
STH.NSummedTraces.pos=30;           STH.NSummedTraces.precision='int16';
STH.NStackedTraces.pos=32;          STH.NStackedTraces.precision='int16';
STH.DataUse.pos=34;                 STH.DataUse.precision='int16';

STH.offset.pos=36;STH.offset.precision='int32';
STH.ReceiverGroupElevation.pos=40;STH.ReceiverGroupElevation.precision='int32';
STH.SourceSurfaceElevation.pos=44;STH.SourceSurfaceElevation.precision='int32';
STH.SourceDepth.pos=48;STH.SourceDepth.precision='int32';
STH.ReceiverDatumElevation.pos=52;STH.ReceiverDatumElevation.precision='int32';
STH.SourceDatumElevation.pos=56;STH.SourceDatumElevation.precision='int32';
STH.SourceWaterDepth.pos=60;STH.SourceWaterDepth.precision='int32';
STH.GroupWaterDepth.pos=64;STH.GroupWaterDepth.precision='int32';

STH.ElevationScalar.pos=68;STH.ElevationScalar.precision='int16';
STH.SourceGroupScalar.pos=70;STH.SourceGroupScalar.precision='int16';

STH.SourceX.pos=72;STH.SourceX.precision='int32';
STH.SourceY.pos=76;STH.SourceY.precision='int32';
STH.GroupX.pos=80;STH.GroupX.precision='int32';
STH.GroupY.pos=84;STH.GroupY.precision='int32';

STH.CoordinateUnits.pos=88;STH.CoordinateUnits.precision='int16';
STH.WeatheringVelocity.pos=90;STH.WeatheringVelocity.precision='int16';
STH.SubWeatheringVelocity.pos=92;STH.SubWeatheringVelocity.precision='int16';
STH.SourceUpholeTime.pos=94;STH.SourceUpholeTime.precision='int16';
STH.GroupUpholeTime.pos=96;STH.GroupUpholeTime.precision='int16';
STH.SourceStaticCorrection.pos=98;STH.SourceStaticCorrection.precision='int16';
STH.GroupStaticCorrection.pos=100;STH.GroupStaticCorrection.precision='int16';
STH.TotalStaticApplied.pos=102;STH.TotalStaticApplied.precision='int16';
STH.LagTimeA.pos=104;STH.LagTimeA.precision='int16';
STH.LagTimeB.pos=106;STH.LagTimeB.precision='int16';
STH.DelayRecordingTime.pos=108;STH.DelayRecordingTime.precision='int16';
STH.MuteTimeStart.pos=110;STH.MuteTimeStart.precision='int16';
STH.MuteTimeEND.pos=112;STH.MuteTimeEND.precision='int16';

STH.ns.pos=114;STH.ns.precision='uint16';
STH.dt.pos=116;STH.dt.precision='uint16';

STH.GainType.pos=118;STH.GainType.precision='int16';
STH.InstrumentGainConstant.pos=120;STH.InstrumentGainConstant.precision='int16';
STH.InstrumentInitialGain.pos=122;STH.InstrumentInitialGain.precision='int16';
STH.Correlated.pos=124;STH.Correlated.precision='int16';
STH.SweepFrequenceStart.pos=126;STH.SweepFrequenceStart.precision='int16';
STH.SweepFrequenceEnd.pos=128;STH.SweepFrequenceEnd.precision='int16';
STH.SweepLength.pos=130;STH.SweepLength.precision='int16';
STH.SweepType.pos=132;STH.SweepType.precision='int16';
STH.SweepTraceTaperLengthStart.pos=134;STH.SweepTraceTaperLengthStart.precision='int16';
STH.SweepTraceTaperLengthEnd.pos=136;STH.SweepTraceTaperLengthEnd.precision='int16';
STH.TaperType.pos=138;STH.TaperType.precision='int16';
STH.AliasFilterFrequency.pos=140;STH.AliasFilterFrequency.precision='int16';
STH.AliasFilterSlope.pos=142;STH.AliasFilterSlope.precision='int16';
STH.NotchFilterFrequency.pos=144;STH.NotchFilterFrequency.precision='int16';
STH.NotchFilterSlope.pos=146;STH.NotchFilterSlope.precision='int16';
STH.LowCutFrequency.pos=148;STH.LowCutFrequency.precision='int16';
STH.HighCutFrequency.pos=150;STH.HighCutFrequency.precision='int16';
STH.LowCutSlope.pos=152;STH.LowCutSlope.precision='int16';
STH.HighCutSlope.pos=154;STH.HighCutSlope.precision='int16';
STH.YearDataRecorded.pos=156;STH.YearDataRecorded.precision='int16';
STH.DayOfYear.pos=158;STH.DayOfYear.precision='int16';
STH.HourOfDay.pos=160;STH.HourOfDay.precision='int16';
STH.MinuteOfHour.pos=162;STH.MinuteOfHour.precision='int16';
STH.SecondOfMinute.pos=164;STH.SecondOfMinute.precision='int16';
STH.TimeBaseCode.pos=166;STH.TimeBaseCode.precision='int16';
%STH.TimeBaseCodeText.pos=168;STH.TimeBaseCodeText.precision='int16';
STH.TraceWeightningFactor.pos=168;STH.TraceWeightningFactor.precision='int16';
STH.GeophoneGroupNumberRoll1.pos=170;STH.GeophoneGroupNumberRoll1.precision='int16';
STH.GeophoneGroupNumberFirstTraceOrigField.pos=172;STH.GeophoneGroupNumberFirstTraceOrigField.precision='int16';
STH.GeophoneGroupNumberLastTraceOrigField.pos=174;STH.GeophoneGroupNumberLastTraceOrigField.precision='int16';
STH.GapSize.pos=176;STH.GapSize.precision='int16';
STH.OverTravel.pos=178;STH.OverTravel.precision='int16';

STH.cdpX.pos=180;STH.cdpX.precision='int32';
STH.cdpY.pos=184;STH.cdpY.precision='int32';
STH.Inline3D.pos=188;STH.Inline3D.precision='int32';
STH.Crossline3D.pos=192;STH.Crossline3D.precision='int32';
STH.ShotPoint.pos=196;STH.ShotPoint.precision='int32';

STH.ShotPointScalar.pos=200;STH.ShotPointScalar.precision='int16';

STH.TraceValueMeasurementUnit.pos=202;STH.TraceValueMeasurementUnit.precision='int16';

STH.TransductionConstantMantissa.pos=204;STH.TransductionConstantMantissa.precision='int32';

STH.TransductionConstantPower.pos=208;STH.TransductionConstantPower.precision='int16';
STH.TransductionUnit.pos=210;STH.TransductionUnit.precision='int16';
STH.TraceIdentifier.pos=212;STH.TraceIdentifier.precision='int16';
STH.ScalarTraceHeader.pos=214;STH.ScalarTraceHeader.precision='int16';
STH.SourceType.pos=216;STH.SourceType.precision='int16';

STH.SourceEnergyDirectionMantissa.pos=218;STH.SourceEnergyDirectionMantissa.precision='int32';

STH.SourceEnergyDirectionExponent.pos=222;STH.SourceEnergyDirectionExponent.precision='int16';

STH.SourceMeasurementMantissa.pos=224;STH.SourceMeasurementMantissa.precision='in32';

STH.SourceMeasurementExponent.pos=228;STH.SourceMeasurementExponent.precision='int16';
STH.SourceMeasurementUnit.pos=230;STH.SourceMeasurementUnit.precision='int16';

STH.UnassignedInt1.pos=232;STH.UnassignedInt1.precision='int32';
STH.UnassignedInt2.pos=236;STH.UnassignedInt2.precision='int32';


function make_fig2(A, y, cuckoo_idx, splunk_idx, t_created, virus, names)

fold = 2;
cutoff = .3;

I_1 = cv_part(y, fold, cutoff, 'standard', t_created, cuckoo_idx, splunk_idx);
I_2 = cv_part(y, fold, cutoff, 'creation', t_created, cuckoo_idx, splunk_idx);
I_3 = cv_part(y, fold, cutoff, 'type', t_created, cuckoo_idx, splunk_idx, virus);

h = figure;
set(h,'Renderer','painters','Position',[100 900 1000 400]);

subaxis(2,4,1,'Margin',0.00,'Padding',-0.00,'PaddingBottom',0.0,'PaddingTop',0.02,'PaddingLeft',0.02,'PaddingRight',-0.01,'MarginBottom',0.08,'MarginLeft',0.03,'MarginRight',0.10,'MarginTop',0.03);

ha = subaxis(2,4,1,1);
[pred02, pred05, logit_model1] = run(A, y, I_1, false, false, t_created, cuckoo_idx, splunk_idx);
pos1 = get(gca,'Position');
annotation(h,'textbox',[pos1(1)-0.05,pos1(2)+pos1(4)-0.01,.1,.05],'String','(A)','fontsize',14,'EdgeColor','none');
ylabel('TPR');

ha = subaxis(2,4,2,1);
[~, ~, logit_model2] = run(A, y, I_2, false, true, t_created, cuckoo_idx, splunk_idx);
pos1 = get(gca,'Position');
annotation(h,'textbox',[pos1(1)-0.05,pos1(2)+pos1(4)-0.01,.1,.05],'String','(B)','fontsize',14,'EdgeColor','none');

ha = subaxis(2,4,3,1);
[~, ~, logit_model3] = run(A, y, I_3, false, false, t_created, cuckoo_idx, splunk_idx);
pos1 = get(gca,'Position');
annotation(h,'textbox',[pos1(1)-0.05,pos1(2)+pos1(4)-0.01,.1,.05],'String','(C)','fontsize',14,'EdgeColor','none');

ha = subaxis(2,4,4,1);
[h1, h2] = virus_names(y, pred02, pred05, virus);
pos1 = get(gca,'Position');
annotation(h,'textbox',[pos1(1)-0.05,pos1(2)+pos1(4)-0.01,.1,.05],'String','(D)','fontsize',14,'EdgeColor','none');
pos1 = get(h1,'Position');
set(h1,'Position', [pos1(1)+0.05, pos1(2), pos1(3), pos1(4)]);
if (h1~=h2)
    pos1 = get(h2,'Position');
    set(h2,'Position', [pos1(1)+0.05, pos1(2), pos1(3), pos1(4)]);

end

ha = subaxis(2,4,1,2);
[pred02, pred05] = run(A, y, I_1, true, false, t_created, cuckoo_idx, splunk_idx, logit_model1);
pos1 = get(gca,'Position');
annotation(h,'textbox',[pos1(1)-0.05,pos1(2)+pos1(4)-0.01,.1,.05],'String','(E)','fontsize',14,'EdgeColor','none');
ylabel('TPR');
xlabel('FPR');

ha = subaxis(2,4,2,2);
run(A, y, I_2, true, true, t_created, cuckoo_idx, splunk_idx, logit_model2);
pos1 = get(gca,'Position');
annotation(h,'textbox',[pos1(1)-0.05,pos1(2)+pos1(4)-0.01,.1,.05],'String','(F)','fontsize',14,'EdgeColor','none');
xlabel('FPR');

ha = subaxis(2,4,3,2);
run(A, y, I_3, true, false, t_created, cuckoo_idx, splunk_idx, logit_model3);
pos1 = get(gca,'Position');
annotation(h,'textbox',[pos1(1)-0.05,pos1(2)+pos1(4)-0.01,.1,.05],'String','(G)','fontsize',14,'EdgeColor','none');
xlabel('FPR');

ha = subaxis(2,4,4,2);
[h1, h2] = virus_names(y, pred02, pred05, virus);
pos1 = get(gca,'Position');
annotation(h,'textbox',[pos1(1)-0.05,pos1(2)+pos1(4)-0.01,.1,.05],'String','(H)','fontsize',14,'EdgeColor','none');
pos1 = get(h1,'Position');
set(h1,'Position', [pos1(1)+0.05, pos1(2), pos1(3), pos1(4)]);
pos1 = get(h2,'Position');
if (h1~=h2)
    set(h2,'Position', [pos1(1)+0.05, pos1(2), pos1(3), pos1(4)]);
end
xlabel('Fraction Detected');

end

function Ic = valid_cuckoo_only(I, cuckoo_idx)

    Ic = {};
    for iter=1:length(I)
      Ic{iter}.training = I{iter}.training | (I{iter}.test & ~cuckoo_idx);
      Ic{iter}.test = I{iter}.test & cuckoo_idx;
   end
end

function [pred02, pred05, logit_model_out] = run(A, y, I, cuckoo_only, time_split, t_created, cuckoo_idx, splunk_idx, logit_model_in)

    A = double(A>0);
    y = double(y>0);

    logit = init();
    bagged = init();
    logit2 = init();
    bagged2 = init();
    logit3 = init();
    bagged3 = init();

    pred02 = NaN*ones(size(y));
    pred05 = NaN*ones(size(y));

    %compute valid
    valid = false(size(y));
    for iter=1:length(I)
        valid = valid | I{iter}.training | I{iter}.test;
    end

    logit_model_out = {};

    for iter=1:length(I)

        I_train = I{iter}.training;
        I_test = I{iter}.test;

        if (time_split)

            t_smallest = min(t_created(I_test & t_created>0 & y>0 & cuckoo_idx));

            I_train = valid & I_train & (y==0 | ~cuckoo_idx | t_created<(t_smallest-86400 * 365 *2));
            I_test = valid & ~I_train & (y==0 | ~cuckoo_idx | t_created<(t_smallest-86400 * 365 *2));
        end

        if (sum(I_train & I_test)>0)
            error('Testing and training intersecting.');
        end

        A_train = A(I_train, :);
        y_train = y(I_train, :);

        if (nargin<9)
            [logit_model, boost_model] = run_pipeline(A_train, y_train);
            logit_model_out{iter} = logit_model;
        else
            logit_model = logit_model_in{iter};
            boost_model = logit_model;
            logit_model_out{iter} = logit_model;
        end

        %graft data
        if (cuckoo_only)
            [A_test, y_test, I_test] = gen_graft_data(A,y,cuckoo_idx,splunk_idx, I_test, logit_model);
        else
            I_test = I_test & ~splunk_idx;
            A_test = A(I_test, :);
            y_test = y(I_test, :);
        end

        [logit, T02, T05] = record_run(A_test, y_test, logit_model, logit);
        [bagged] = record_run(A_test, y_test, boost_model, bagged);

        %record the predictions
        pred02(I_test) = logit_model.predict(A_test)>=T02;
        pred05(I_test) = logit_model.predict(A_test)>=T05;

        if (time_split)

            I_test = valid & ~I_train & (y==0 | ~cuckoo_idx | t_created>=(t_smallest-86400 * 365 * 1));

           if (sum(I_train & I_test)>0)
                error('Testing and training intersecting.');
            end

            %A_train = A(I_train, :);
            %y_train = y(I_train, :);

            %[logit_model2, boost_model2] = run_pipeline(A_train, y_train);
            logit_model2 = logit_model;
            boost_model2 = boost_model;

            %graft data
            if (cuckoo_only)
                [A_test, y_test,I_test] = gen_graft_data(A,y,cuckoo_idx,splunk_idx, I_test, logit_model2);
            else
                I_test = I_test & ~splunk_idx;
                A_test = A(I_test, :);
                y_test = y(I_test, :);
            end

            [logit2] = record_run(A_test, y_test, logit_model2, logit2);
            [bagged2] = record_run(A_test, y_test, boost_model2, bagged2);

            I_test = valid & ~I_train & (y==0 | ~cuckoo_idx | t_created>=(t_smallest-86400 * 365 * 0));

            if (sum(I_train & I_test)>0)
                error('Testing and training intersecting.');
            end

            %A_train = A(I_train, :);
            %y_train = y(I_train, :);

            %[logit_model3, boost_model3] = run_pipeline(A_train, y_train);
            logit_model3 = logit_model;
            boost_model3 = boost_model;

            %graft data
            if (cuckoo_only)
                [A_test, y_test,I_test] = gen_graft_data(A,y,cuckoo_idx,splunk_idx, I_test, logit_model3);
            else
                I_test = I_test & ~splunk_idx;
                A_test = A(I_test, :);
                y_test = y(I_test, :);
            end

            [logit3] = record_run(A_test, y_test, logit_model3, logit3);
            [bagged3] = record_run(A_test, y_test, boost_model3, bagged3);

        end

    end

    if (time_split)
        semilogx(mean(logit.X) , mean(logit.Y),'-k', mean(logit2.X) , mean(logit2.Y),'-b', mean(logit3.X) , mean(logit3.Y),'-r', mean(bagged.X) , mean(bagged.Y), '--k', mean(bagged2.X) , mean(bagged2.Y), '--b', mean(bagged3.X) , mean(bagged3.Y), '--r');
        legend(sprintf('0 year, AUC=%.2f', mean(logit.AUC)), sprintf('1 year, AUC=%.2f', mean(logit2.AUC)), sprintf('2 year, AUC=%.2f', mean(logit3.AUC)), 'Location','SouthEast');
        %plot(mean(logit.X) , mean(logit.Y),'-k', mean(logit2.X) , mean(logit2.Y),'-r');
        %legend(sprintf('LR, AUC=%.2f, 1 year', mean(logit.AUC)), sprintf('LR, AUC=%.2f, 2 years', mean(logit2.AUC)), 'Location','SouthEast');
    else

        semilogx(mean(logit.X) , mean(logit.Y),'-k', mean(bagged.X) , mean(bagged.Y), '--k');
        legend(sprintf('AUC=%.2f', mean(logit.AUC)), 'Location','SouthEast');
        %plot(mean(logit.X) , mean(logit.Y),'-k');
    end

    xlim([2e-4, 0.1]);
    ylim([0, 1.0]);
    set(gca, 'XMinorTick','on','YMinorTick','on','TickLength',[0.025 0.025]);

end

function [base] = init()
    base.X = [];
    base.Y = [];
    base.AUC = [];
    base.AUC_splunk = [];
end

function [base,T02, T05] = record_run(A, y, model, base)

    [X,Y,T,auc] = model.getROCPoints(A, y);

    T02 = T(find(X>=.01,1,'first'));
    T05 = T(find(X>=.001,1,'first'));
    AUC_splunk = 0;

    base.X(end+1,:) = X;
    base.Y(end+1,:) = Y;
    base.AUC(end+1) = auc;
    base.AUC_splunk(end+1) = AUC_splunk;

end

 function [h1, h2] = virus_names(y, pred02, pred05, virus)

    if (sum(~isnan(pred02))==0)
        h1 = gca;
        %h2 = axes();
        h2 = gca;
        return;
    end

  for iter=1:length(virus)
      if (strcmp(virus{iter}, 'Trojan.Win32.Generic'))
          virus{iter} = 'Generic';
      else
        x = strsplit(virus{iter},'.');
        virus{iter} = lower(x{1});
        x = strsplit(virus{iter},'-');
        virus{iter} = lower(x{1});
      end
  end

  [unique_names,~,z] = unique(virus);
  d = hist(max(0, z(y>0 & ~isnan(pred02))),length(unique_names));

  d_after = hist(z(y>0 & pred05>0),length(unique_names));
  d_after2 = hist(z(y>0 & pred02>0),length(unique_names));

  [~,I] = sort(d,'descend');

  I_label = [];
  for iter=1:length(I)
    if (strcmp(unique_names(I(iter)),'<nolabel>')==0 && strcmp(unique_names(I(iter)),'Generic')==0)
      I_label = [I_label I(iter)];
    end

    if (length(I_label)>=10)
      break;
    end
  end

  barh([d_after(I_label)'./d(I_label)', d_after2(I_label)'./d(I_label)']);

  hold on;

  set(gca, 'YDir','reverse');
  set(gca,'YTickLabel',unique_names(I_label), 'XMinorTick','on','TickLength',[0.025 0.025]);
  ylabel('Kaspersky Classification');
  xlim([0, 1.0]);
  h1 = gca;

  %legend('FPR=10^{-2}', 'FPR=10^{-3}','Location','SouthWest' );
  %legend('boxoff');

  h2 = axes('Position',get(gca,'Position'),...
              'XAxisLocation','top',...
              'YAxisLocation','right',...
              'Color','none');

  set(h2, 'Ylim', get(h1,'Ylim'), 'YDir','reverse', 'XTickLabel', [], 'YTickLabel', d(I_label), 'YTick', 1:length(I_label));
  ylabel(h2, '# Observations');
  axes(h1);

 end


function [mask3MU, zValues] = getMask3D(structNum,planC)
%function [mask3MU, zValues] = getMask3D(structNum,planC)
%Assemble the 3-D CT-registered mask (type UINT8).
%JOD.
%Latest modifications:  JOD, 19 Feb 03, added zValues output.
%
% Copyright 2010, Joseph O. Deasy, on behalf of the CERR development team.
%
% This file is part of The Computational Environment for Radiotherapy Research (CERR).
%
% CERR development has been led by:  Aditya Apte, Divya Khullar, James Alaly, and Joseph O. Deasy.
%
% CERR has been financially supported by the US National Institutes of Health under multiple grants.
%
% CERR is distributed under the terms of the Lesser GNU Public License.
%
%     This version of CERR is free software: you can redistribute it and/or modify
%     it under the terms of the GNU General Public License as published by
%     the Free Software Foundation, either version 3 of the License, or
%     (at your option) any later version.
%
% CERR is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
% without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
% See the GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with CERR.  If not, see <http://www.gnu.org/licenses/>.

indexS = planC{end};

scanNum = getStructureAssociatedScan(structNum, planC);

ROIImageSize   = [planC{indexS.scan}(scanNum).scanInfo(1).sizeOfDimension1  planC{indexS.scan}(scanNum).scanInfo(1).sizeOfDimension2];

numSlices = length(planC{indexS.scan}(scanNum).scanInfo);

mask3MU = false(ROIImageSize(1),ROIImageSize(2),numSlices);

zValues = [];

for sliceNum = 1 : numSlices

  z    = planC{indexS.scan}(scanNum).scanInfo(sliceNum).zValue;

  zValues = [zValues, z];

  [segmentsM, planC, isError] = getRasterSegments(structNum, planC);
%  segmentsM = planC{indexS.structures}(structNum).rasterSegments;

  indV = find(segmentsM(:,1) == z);  %mask values on this slice

  segmentsM = segmentsM(indV(:),7:9);     %segments

  %reconstruct the mask:

  for j = 1 : size(segmentsM,1)
    mask3MU(segmentsM(j,1),segmentsM(j,2):segmentsM(j,3),sliceNum) = 1;
  end

end


% APM Solver for simulation, estimation, and optimization with both
%  static (steady-state) and dynamic models. The dynamic modes can solve
%  index 2+ DAEs without numerical differentiation.
%
% y = apm_solve(app,imode)
%
% Function apm_solve uploads the model file (apm) and optionally
%   a data file (csv) with the same name to the web-server and performs
%   a forward-time stepping integration of ODE or DAE equations
%   with the following arguments:
%
%  Input:      app = model (apm) and data file (csv) name
%            imode = simulation mode {1..7}
%                               steady-state  dynamic  sequential
%                    simulate     1             4        7
%                    estimate     2             5        8 (under dev)
%                    optimize     3             6        9 (under dev)
%
% Output: y.names  = names of all variables
%         y.values = tables of values corresponding to y.names
%         y.nvar   = number of variables
%         y.x      = combined variables and values but variable
%                      names may be modified to make them valid MATLAB
%                      characters (e.g. replace '[' with '')
%
function y = apm_solve(app,imode)
    % check for number of arguments
    if nargin==0,
        disp('Please specify the APM model name such as:')
        disp('  y = apm_solve(myModel)')
        y = [];
        return
    end

    % server and application file names
    server = 'http://byu.apmonitor.com';
    app_model = [app '.apm'];
    app_data = [app '.csv'];

    % only one input argument (imode not specified)
    if nargin==1,
        if (exist(app_data,'file')),
            disp('Using default imode of 7 (sequential simulation)')
            imode = 7;
        else
            disp('Using default imode of 3 (steady-state optimization)')
            imode = 3;
        end
    end

    % application name
	app = lower(deblank(app));

    % clear previous application
    apm(server,app,'clear all');

    % check that model file exists (required)
    if (~exist(app_model,'file')),
        disp(['Error: file ' app_model ' does not exist']);
        y = [];
        return
    else
        % load model file
        apm_load(server,app,app_model);
    end

    % check if data file exists (optional)
    if (exist(app_data,'file')),
        % load data file
        csv_load(server,app,app_data);
    end

    % default options
    % use or don't use web viewer
    web = false;
    if web,
        apm_option(server,app,'nlc.web',2);
    else
        apm_option(server,app,'nlc.web',0);
    end
    % internal nodes in the collocation (between 2 and 6)
    apm_option(server,app,'nlc.nodes',3);
    % sensitivity analysis (default: 0 - off)
    apm_option(server,app,'nlc.sensitivity',0);
    % simulation mode (1=ss,  2=mpu, 3=rto)
    %                 (4=sim, 5=est, 6=nlc, 7=sqs)
    apm_option(server,app,'nlc.imode',imode);

    % attempt solution
    solver_output = apm(server,app,'solve');

    % check for successful solution
    status = apm_tag(server,app,'nlc.appstatus');

    if status==1,
        % open web viewer if selected
        if web,
            apm_web(server,app);
        end
        % retrieve solution and solution.csv
        y = apm_sol(server,app);
        return
    else
        apm_web_root(server,app);
        disp(solver_output);
        disp('Error: Did not converge to a solution');
        y = solver_output;
        return
    end


function test_suite=SimTest_noddi_test
try % assignment of 'localfunctions' is necessary in Matlab >= 2016
test_functions=localfunctions();
catch % no problem; early Matlab versions can use initTestSuite fine
end
initTestSuite;

function TestSetup
setenv('ISDISPLAY','0') % go faster! Fit only 2 voxels in FitData.m
setenv('ISCITEST','1')

function test_Sim
disp('===========================================')
disp('Running simulation test for noddi');
disp('testing Simulation Single Voxel Curve...');

Model = str2func('noddi'); Model = Model();
savedModel_fname = fullfile(fileparts(which('qMRLab')),'Test','MoxUnitCompatible','static_savedModelsforRetrocompatibility',['noddi.qmrlab.mat']);
if ~exist(savedModel_fname,'file')
Model.saveObj(savedModel_fname);
else
Model = Model.loadObj(savedModel_fname);
end

disp(class(Model))
try Opt = button2opts(Model.Sim_Single_Voxel_Curve_buttons,1); end
try st = Model.st; catch, try st = mean([Model.lb(:),Model.ub(:)],2); catch, st = ones(length(Model.xnames),1); end; end
if exist('Opt','var') && length(Opt)>1
[Opt(:).SNR] = deal(1000);
else
Opt.SNR=1000;
end
for iopt=1:length(Opt) % Test all simulation options
disp(['Testing ' class(Model) ' simulation option:'])
disp(Opt(iopt))
FitResults = Model.Sim_Single_Voxel_Curve(st,Opt(iopt));
% Compare inputs and outputs
fnm=fieldnames(FitResults);
FitResults = rmfield(FitResults,fnm(~ismember(fnm,Model.xnames))); fnm=fieldnames(FitResults);
[~,FitResults,GroundTruth]=comp_struct(FitResults,mat2struct(st,Model.xnames),[],[],.30);
assertTrue(isempty(FitResults) & isempty(GroundTruth),evalc('FitResults, GroundTruth'))
end
disp ..ok

function TestTeardown
setenv('ISDISPLAY','') % go faster! Fit only 2 voxels in FitData.m
setenv('ISCITEST','')


function [meg] = read_ctf_dat(filename)

% READ_CTF_DAT reads MEG data from an ascii format CTF file
%
% meg = read_ctf_dat(filename)
%
% returns a structure with the following fields:
%   meg.data        Nchans x Ntime
%   meg.time        1xNtime in miliseconds
%   meg.trigger     1xNtime with trigger values
%   meg.label       1xNchans cell-array with channel labels (string)

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

fid = fopen_or_error(filename, 'r');

% read the sample number
line = fgetl(fid);
[tok, rem] = strtok(line, ':');
meg.sample = str2num(rem(2:end));

% read the time of each sample and convert to miliseconds
line = fgetl(fid);
[tok, rem] = strtok(line, ':');
meg.time = 1000*str2num(rem(2:end));

% read the trigger channel
line = fgetl(fid);
[tok, rem] = strtok(line, ':');
meg.trigger = str2num(rem(2:end));

% read the rest of the data
meg.data = [];
meg.label = {};
chan = 0;
while (1)
  line = fgetl(fid);
  if ~isempty(line) && line==-1
    % reached end of file
    break
  end
  [tok, rem] = strtok(line, ':');
  if ~isempty(rem)
    chan = chan + 1;
    meg.data(chan, :) = str2num(rem(2:end));
    meg.label{chan} = fliplr(deblank(fliplr(deblank(tok))));
  end
end

% convert to fT (?)
meg.data = meg.data * 1e15;

% apparently multiple copies of the data can be stored in the file
% representing the average, the standard deviation, etc.
% keep only the first part of the data -> average
tmp = find(diff(meg.time)<0);
if ~isempty(tmp)
  meg.data = meg.data(:,1:tmp(1));
  meg.time = meg.time(1:tmp(1));
  meg.trigger = meg.trigger(1:tmp(1));
  meg.sample = meg.sample(1:tmp(1));
end


function v = niftiCompareRoiPair(roi1,roi1LabelVal,roi2,roi2LabelVal)
%
% function v = niftiCompareRoiPair(roi1,roi1LabelVal,roi2,roi2LabelVal)
%
% This function takes two nifti ROIs and computes volume and a measure of
% overlap (percent agreement) as a means of evaluating reliabitliy in the
% case of two raters drawing the same ROI. The roi.pixdim field is used to
% compute the volume appropriately.
%
% EXAMPLE USAGE:
% v = niftiCompareRoiPair('r1c.nii.gz',1,'r2c.nii.gz',3);
% v =
%         roi1Name: 'r1c.nii.gz'
%          roi1Val: 1
%         roi2Name: 'r2c.nii.gz'
%          roi2Val: 3
%            units: 'mm^3'
%              dim: [1 1 1]
%       roi1Volume: 5265
%       roi2Volume: 4996
%       overlapVol: 4868
%       overlayVol: 5393
%     percentAgree: 90.2652%
%
% HISTORY
% 2011.06.01 LMP: wrote it.
% 2011.06.03 LMP: now calls for a label value for each roi. This allows
%            files with multiple labels to be used. User must specify the
%            value of each. Now uses the dim of the roi itself to calculate
%            the volume.

%%

% Check input arguments and prompt for them if they don't exist
if(~exist('roi1','var') || isempty(roi1))
  dd = pwd; roi1 = mrvSelectFile([],'*.nii.gz','Select ROI 1 File',dd);
end

if(~exist('roi1LabelVal','var')) || isempty(roi1LabelVal)
    roi1LabelVal = str2double(inputdlg('Enter ROI1 Label Value','ROI 1 Label Value'));
end

if(~exist('roi2','var') || isempty(roi2))
  dd = pwd; roi2 = mrvSelectFile([],'*.nii.gz','Select ROI 2 File',dd);
end

if(~exist('roi2LabelVal','var')) || isempty(roi2LabelVal)
    roi2LabelVal = str2double(inputdlg('Enter ROI2 Label Value','ROI 2 Label Value'));
end

% Read in the nifti files
roi1 = niftiRead(roi1);
roi2 = niftiRead(roi2);
if roi1.pixdim==roi1.pixdim
    mmPerVoxel = roi1.pixdim; % Dimensions of the t1 image
else error('ROI dimensions must agree');
end

% Find the points in the data array that make up the ROI
r1Inds = find(roi1.data==roi1LabelVal); % points in roi1 where there is any label >0
r2Inds = find(roi2.data==roi2LabelVal); % points in roi2 where there is any label >0
a = r1Inds(:,1);
b = r2Inds(:,1);
c = ismember(a,b); % Find the overlap indices

% Get the inds of the overlay ROI - that is the ROI that would be created
% if you overlay and combine the two ROIs together (with the overlap only
% being counted once)
d = vertcat(a,b);
e = unique(d); % Overlay indices

% Populate the structure
v.roi1Name = roi1.fname;
v.roi1Val  = roi1LabelVal;
v.roi2Name = roi2.fname;
v.roi2Val  = roi2LabelVal;
v.units = 'mm^3';
v.dim = roi1.pixdim;

% Calculate the volume and percent agreement
v.roi1Volume = length(a)*prod(mmPerVoxel);
v.roi2Volume = length(b)*prod(mmPerVoxel);
v.overlapVol = sum(c)*prod(mmPerVoxel);
v.overlayVol = length(e)*prod(mmPerVoxel);
v.percentAgree = v.overlapVol/v.overlayVol*100;

% Print the results to the screen
fprintf('\n================================\n');
% fprintf('T1 Image        = %s\n',v.imgName);
fprintf('ROI1 Name       = %s\n',v.roi1Name);
fprintf('ROI1 Label Val  = %s\n',num2str(roi1LabelVal));
fprintf('ROI2 Name       = %s\n',v.roi2Name);
fprintf('ROI2 Label Val  = %s\n',num2str(roi2LabelVal));
fprintf('Units           = %s\n',v.units);
fprintf('Voxel Dim       = %s\n',num2str(v.dim));
fprintf('Roi1 Volume     = %s\n',num2str(v.roi1Volume));
fprintf('Roi2 Volume     = %s\n',num2str(v.roi2Volume));
fprintf('Overlap Volume  = %s\n',num2str(v.overlapVol));
fprintf('Overlay Volume  = %s\n',num2str(v.overlayVol));
fprintf('Percent Agree   = %s\n',num2str(v.percentAgree));
fprintf('================================\n');

return


function [labels, conf_map] = ...
    APPtestDirectory(segDensity, vClassifier, hClassifier, ...
        imdir, imsegs, varargin)
% [labels, conf_map] = APPtestDirectory(segDensity, vClassifier,
%                                   hClassifier, imdir, imsegs, varargin)
%
% Gets the geometry for each image with superpixels given by imsegs.
%
% Input:
%   segDensity: structure giving probability of 2 sp having same label
%   vClassifier: segment classifier for ground/vert/sky
%   hClassifier: segment classifier for subclassses of vert
%   imdir: the directory of the images referred to by imsegs
%   imsegs: the superpixel structure for each image
%   varargin{1} (optional): the output directory for displaying results
% Output:
%   labels: structure containing labeling results for each image
%   conf_map: the likelihoods for each sp for each class for each image
%
% Copyright(C) Derek Hoiem, Carnegie Mellon University, 2005
% Permission granted to non-commercial enterprises for
% modification/redistribution under GNU GPL.
% Current Version: 1.0  09/30/2005

DO_PARALLEL = 0; % for running multiple parallel processes on directory

if length(varargin) > 0
    outdir = varargin{1};
end

for f = 1:length(imsegs)

    fn = imsegs(f).imname;
    bn = strtok(fn, '.');

    if ~DO_PARALLEL || ~exist('outdir') || ~exist([outdir '/' bn '.c.mat'])

        if exist('outdir') && DO_PARALLEL % to mark as being processed
            save([outdir '/' bn '.c.mat'], 'fn');
        end

        image = im2double(imread([imdir, '/', fn]));

        if size(image, 3) == 3

            disp(['processing image ' fn]);

            [labels(f), conf_map(f), maps{f}, pmaps(f)] = ...
                    APPtestImage(image, imsegs(f), vClassifier, hClassifier, segDensity);
            % for generating context
            [cimages, cnames] = APPclassifierOutput2confidenceImages(imsegs(f), conf_map(f));

            if length(varargin) > 0
                outdir = varargin{1};

                limage = APPgetLabeledImage(image, imsegs(f), labels(f).vert_labels, labels(f).vert_conf, ...
                    labels(f).horz_labels, labels(f).horz_conf);
                imwrite(limage, [outdir, '/', bn, '.l.jpg']);
                imwrite(image, [outdir, '/', fn]);

                % for generating context
                glabels = labels(f);
                gconf_map = conf_map(f);
                gmaps = maps{f};
                gpmaps = pmaps(f);
                save([outdir '/' bn '.c.mat'], 'glabels', 'gconf_map', 'cimages', 'cnames', 'gmaps', 'gpmaps');

                % make a vrml file
                if 0
                    APPwriteVrmlModel(imdir, imsegs(f), labels(f), outdir);
                end
            %else
            %    disp('warning: set up for ICCV, no output ==> no results')
            end

        end

        pause(0.05);

    end

end

for f = 1:length(imsegs)
    labels(f).imname = imsegs(f).imname;
end


% This code was originally written and distributed as part of the
% PASCAL VOC challenge, with minor modifications for ILSVRC2013
function rec = VOCreadrecxml(path,hash)

x=VOCreadxml(path);
x=x.annotation;

rec.folder=x.folder;
rec.filename=x.filename;
rec.source.database=x.source.database;
%% rec.source.annotation=x.source.annotation;
%% rec.source.image=x.source.image;

rec.size.width=str2double(x.size.width);
rec.size.height=str2double(x.size.height);

rec.imgname=[x.folder x.filename];
rec.imgsize=str2double({x.size.width x.size.height});
rec.database=rec.source.database;

if isfield(x,'object')
    for i=1:length(x.object)
        rec.objects(i)=xmlobjtopas(x.object(i),hash);
    end
else
    rec.objects = [];
end

function p = xmlobjtopas(o,hash)

p.class=o.name;

p.label= get_class2node( hash, p.class );

p.bbox=str2double({o.bndbox.xmin o.bndbox.ymin o.bndbox.xmax o.bndbox.ymax});

p.bndbox.xmin=str2double(o.bndbox.xmin);
p.bndbox.ymin=str2double(o.bndbox.ymin);
p.bndbox.xmax=str2double(o.bndbox.xmax);
p.bndbox.ymax=str2double(o.bndbox.ymax);


function test_pull574_reref

% MEM 2gb
% WALLTIME 01:00:00
% DEPENDENCY ft_preprocessing preproc ft_preproc_reference

% test the ft_preprocessing for re-referencing to rest
% Li Dong $ 2020.3.10, (Lidong@uestc.edu.cn) and J.M.Schoffelen

load(dccnpath('/home/common/matlab/fieldtrip/data/test/pull574.mat')); % load data and leadfield calculated by FieldTrip

% -------------------------------------------------------------------------
% A snippet of code to calculate leadfield using FieldTrip
% cfg                        = [];
% cfg.elec                   = elec_aligned; % aligned eletrode positions
% cfg.grid                   = grid; % dipole positions
% cfg.headmodel              = vol;  % headmodel created by 'ft_prepare_headmodel'
% cfg.grid.resolution        = 6; % number for automatic sourcemodel generation
% cfg.normalize       = 'yes';
% [lf]        = ft_prepare_leadfield(cfg); % calculate leadfield
% -----------
% An full workflow of creating leadfield using real headmodel (BEM and FEM)
% can be seen in:
% http://www.fieldtriptoolbox.org/workshop/baci2017/forwardproblem/

% A full example script for leadfield based on FEM headmodel can be seen in
% http://www.fieldtriptoolbox.org/development/project/example_fem/
% -------------------------------------------------------------------------
% rest re-referencing
cfg           = [];
cfg.reref     = 'yes';
cfg.refmethod = 'rest';     %  if select 'rest','leadfield' is required.
cfg.leadfield = lf;
%              The leadfield can be a matrix (channels X sources)
%              which is calculated by using the forward theory, based on
%              the electrode montage, head model and equivalent source
%              model. It can also be the output of ft_prepare_leadfield.m
%              (e.g. lf.leadfield) based on real head modal using FieldTrip.

% cfg.refchannel = data.label([1:3,5:60],1); % use first 60 channels
cfg.refchannel = {'all'};    % vector with indices of the selected channels
%                                  %  (re-referenced channels), or 'all'.
data_eeg_rest  = ft_preprocessing(cfg,data);
% -------------------------------------------------------------------------
% median re-referencing
cfg = [];
cfg.reref       = 'yes';
cfg.refmethod   = 'median';    % median
cfg.refchannel  = {'all'};     % use first 60 channels

data_eeg_median = ft_preprocessing(cfg,data);

% plot the results

figure;
k = 1; % kth channel
data1 = data_eeg_rest.trial{1,1};
data2 = data_eeg_median.trial{1,1};

plot(data1(k,1:100),'-b');
hold on;
plot(data2(k,1:100),'-r');
plot(data1(k,1:100)-data2(k,1:100),'-.k')
hold off;grid on;
legend('rest','median','difference');


function roi = roiCheckCoords(roi, mr, preserveCoords);
% Check that an ROI's coords match the coordinates
% of an MR object's data, applying transformations if
% needed.
%
% roi = roiCheckCoords(roi, mr, [preserveCoords]);
%
% roi: an ROI struct. See roiCreate.
%
% mr: an mr object, which may be the object on which the ROI is
%     defined, or may be a different object. The ROI can still be used
%     if a common coordinate space can be found (e.g., by scaling the
%     ROI's coords against the MR data, or applying a transformation
%     into a mutual space like Scanner space).
%
% preserveCoords: optional flag; if 1, will have the output roi
% have the same # of columns in roi.coords as the input roi.
% If 0, removes redundant columns. [Default 0]
% NOTE: importantly, setting preserveCoords to 1 risks that some
% functions may not work on the transformed ROI, since it will
% include coordinates which are out-of-range of the mr data.
% If preserveCoords==0, will prune these automatically.
%
%
% ras, 10/2005.
if nargin<2, help(mfilename); error('Not enough args.');    end
if notDefined('preserveCoords'), preserveCoords = 0;        end
if notDefined('roi'), error('Empty ROI.');                  end

mr = mrParse(mr);
roi = roiParse(roi, mr);

% recursively check several ROIs if requested
if length(roi) > 1
    for i = 1:length(roi)
        roi(i) = roiCheckCoords(roi(i), mr, preserveCoords);
    end
    return
end

if isempty(roi.coords)	% no need to check
	return
end

xform = [];

% There are a few ways to check if the ROI matches the MR object.
% First, check if the ROI is defined with respect to one of the
% existing spaces:
if ismember(lower(roi.reference), {'raw data in pixels' lower(mr.name)})
    xform = eye(4);     % defined in same space

elseif ischar(roi.reference)
    % see if the roi and mr share a common space
    % (but ignore "standard" spaces: may have the same name but refer to
    % different coordinates)
    std = {'Raw Data in Pixels' 'Raw Data in mm' 'L/R Flipped'};
    commonSpaces = setdiff({mr.spaces.name}, std);
    if ~isempty( cellfind(commonSpaces, roi.reference) )
        fprintf('Coregistering via common space %s \n', roi.reference);
        I = cellfind({mr.spaces.name}, roi.reference);
        xform = inv(mr.spaces(I).xform);
    end
end

if isempty(xform)
    % still haven't gotten xform: try the next step:
    if isfield(roi,'referenceMR') & ~isempty(roi.referenceMR)
        % ensure the reference MR object is loaded
        roi.referenceMR = mrParse(roi.referenceMR);

        % compare the mr name and the referenceMR name
        if isequal(roi.referenceMR.name, mr.name)
            % great, the ROI was defined on this data! We're done.
            return

        else
            % check the spaces in the reference MR and the mr file
            try
                xform = mrBaseXform(mr, roi.referenceMR);
            catch
                disp(lasterr);
                error('Sorry, can''t use this ROI with that MR data.')
            end

        end

    else
        error('Sorry, can''t use this ROI with that MR data.');
    end
end

% if we got here, we have an xform -- apply it to the
% current ROI coords to get it into the mr data space:
if ~isequal(xform, eye(4)) % don't waste time on a trivial xform
	roi = roiXformCoords(roi, xform, mr.voxelSize(1:3));
end
roi.voxelSize = mr.voxelSize;
roi.dimUnits = mr.dimUnits;

% remove redundant coords, if desired
if ~preserveCoords
    roi.coords = intersectCols(roi.coords, roi.coords);

    % also remove coords out of range of mr data
    ok = find(roi.coords(1,:)>=1 & roi.coords(1,:)<=mr.dims(1) & ...
              roi.coords(2,:)>=1 & roi.coords(2,:)<=mr.dims(2) & ...
              roi.coords(3,:)>=1 & roi.coords(3,:)<=mr.dims(3));
    roi.coords = roi.coords(:,ok);
end

roi.reference = mr.name;

return


classdef magrcros < ZmapVGridFunction
    % MAGRCROS calcualtes Z-values in cross section
    %
    % TODO make this the first x-section function

    properties
        cutoff          datetime                % time of cut
        use_fixed_start logical     = false;
        periodA_start   datetime                % windows start time [where pre-window data starts]
        use_fixed_end   logical     = false;
        periodB_end     datetime                % windows end time [where post-window data ends]
        window_duration duration    = ZmapGlobal.Data.compare_window_dur;
        bin_dur         duration
    end

    properties(Constant)
        PlotTag         = 'zsection';
        ReturnDetails   = cell2table({ ... TODO update this. it hasn't been really done.
            ... VariableNames, VariableDescriptions, VariableUnits
            'AST',                  'Z-value comparing rate before to rate after cutoff','';...
            'LTA',                  'Z-value comparing rate outside window to inside window','';...
            'RUB',                  'Z-value comparing rate before cutoff [before window] to rate inside window','';...
            'PCT',                  'Compare ','';...
            'nBeforeCutoff',        '','';...
            'nAfterCutoff',         '','';...
            'nInWindow',            '','';...
            'nNotInWindow',         '','';...
            'DistAlongStrike',    'Distance along strike','km'...
            }, 'VariableNames', {'Names','Descriptions','Units'});

        CalcFields      = {...
            'AST','LTA','RUB','PCT',...
            'nBeforeCutoff','nAfterCutoff','nInWindow','nNotInWindow'}

        ParameterableProperties = ["cutoff", "use_fixed_start",...
            "periodA_start", "periodB_end", "use_fixed_end", "window_duration", "bin_dur"]

        References="";
        %Negative z-values indicate an increase in the seismicity rate, positive values a decrease.
            unit_options = {'seconds','hours','days','years'};
            unit_functions = {@seconds, @hours, @days, @years};
    end
    methods
        function obj=magrcros(zap,varargin)
            obj@ZmapVGridFunction(zap,'AST');
            report_this_filefun();

            % set the dependent variables here
            obj.periodA_start = min(obj.RawCatalog.Date);
            obj.periodB_end = max(obj.RawCatalog.Date);

            obj.parseParameters(varargin);
            warning('ZMAP:unimplemented','apparently still broken');

            obj.StartProcess();

            % consider this for future: uimenu(op1,'Label','Show Circles ','MenuSelectedFcn',@(~,~)plotcirc)
        end

        function InteractiveSetup(obj)
            % get the grid parameter

            % these are provided by the eventselection
            %ni = 100;
            %ra = ZG.ra;

            if isempty(obj.cutoff)
                % set the default cutoff to the time of the biggest event in catalog.
                % selects first event if multiple events are same size
                biggest=find(obj.RawCatalog.Magnitude==max(obj.RawCatalog.Magnitude) , 1);
                obj.cutoff = obj.RawCatalog.Date(biggest);
            end
            % make the interface
            %
            zdlg = ZmapDialog();
            zdlg.AddEventSelector('evsel', obj.EventSelector);
            if ~isempty(obj.Shape)
                zdlg.AddCheckbox('useGridFromShape', 'Limit grid to polygon', true,[],...
                    'Only evaluate for gridpoints within the polygon region. Does not restrict the catalog');
                zdlg.AddCheckbox('useCatFromShape', 'Limit catalog to polygon', false,[],...
                    'Only evaluate for events within the shape region. Does not restrict the grid');

            end

            % these provided by the grid
            %dd = 1.00;
            %dx = 1.00 ;
            %zdlg.AddEdit('dx_km','Spacing along strike [km]',dx,'spacing in horizontal plane');
            %zdlg.AddEdit('dd_km','Spacing in depth [km]',dd,'spacing in vertical plane');

            %zdlg.AddEdit('bin_dur','Time steps in days',ZG.bin_dur,'time steps in days');

            default_unit = find(obj.unit_options == "days");
            unitizer = obj.unit_functions{default_unit};

            zdlg.AddCheckbox('use_fixed_start', 'Fix StartTime', obj.use_fixed_end, 'fixed_start',...
                'Otherwise, the StartTime will depend on the catalog');

            zdlg.AddEdit('fixed_start',         'Start time',    obj.periodA_start,...
                'window size in specified units');

            zdlg.AddCheckbox('use_fixed_end',   'Fix EndTime',  obj.use_fixed_start, 'fixed_end',...
                'Otherwise, the StartTime will depend on the catalog');

            zdlg.AddEdit('fixed_end',           'End time',     obj.periodB_end,...
                'end time');

            zdlg.AddEdit('cutoff','Please enter date & time of cut:', obj.cutoff, 'Cutoff Date as yyyy-mm-dd hh:MM:ss');

            zdlg.AddEdit('win_dur',             'Window Size',  unitizer(obj.window_duration),...
                'window size in specified units');

            zdlg.AddPopup('win_dur_unit', 'Window Size Units:', obj.unit_options,   default_unit,...
                'Chooose units for window duration');
            zdlg.AddEdit('n_bins_in_window',    'Number of bins within window',...
                round(obj.window_duration/obj.ZG.bin_dur),...
                'Number of windows used to divide up the window [INTEGER]. this determines the bin size');

            [zparam,okPressed]=zdlg.Create('Name', 'Z-value xsection input parameters');
            if ~okPressed
                return
            end

            %dd=zparam.dd_km;
            %dx=zparam.dx_km;

            if isfield(zparam,'useCatFromShape') && zparam.useCatFromShape
                errordlg('not yet implemented: use catalog from shape. First limit catalog by shape, THEN call this function');
                return
            end

            SetValuesFromDialog(obj,zparam);
            obj.doIt();

        end  %  InteractiveSetup

        function SetValuesFromDialog(obj, res)
            if res.use_fixed_start
                obj.periodA_start = min(obj.RawCatalog.Date);
            else
                obj.periodA_start = res.fixed_start;
            end

            if res.use_fixed_end
                obj.periodB_end = max(obj.RawCatalog.Date);
            else
                obj.periodB_end = res.fixed_end;
            end
            unitizer = obj.unit_functions{res.win_dur_unit};
            obj.window_duration = unitizer(res.win_dur);
            obj.bin_dur = obj.window_duration/res.n_bins_in_window;
            obj.cutoff = res.cutoff;
            obj.EventSelector=res.evsel;

            if isfield(res,'useGridFromShape') && res.useGridFromShape
                obj.Grid = obj.Grid.MaskWithShape(obj.Shape);
            end
        end

        function Calculate(obj)

            % this is how to get a xsection grid from ZmapMainWindow (aa is handle to window)
            % xsz  = aa.xsec_zap('A - A''')
            % gr = xsz.Grid.MaskWithShape(aa.shape)
            % and the above should already be pulled into the object.

            assert(obj.cutoff > obj.periodA_start && obj.cutoff < obj.periodB_end,...
                'Cutoff date should be some time after %s and before %s', char(obj.periodA_start), char(obj.periodB_end));
            assert(obj.periodA_start < obj.periodB_end,'Invalid dates: Start date is after End date');
            assert (obj.cutoff + obj.window_duration > obj.periodB_end,...
                'window extends past end date. Manually set end date or change window');

            edges_for_cov = unique([obj.cutoff : - obj.bin_dur : obj.periodA_start, obj.cutoff : obj.bin_dur : obj.periodB_end]);

            obj.gridCalculations(@calculation_function);
            obj.Result.periodA_start = obj.periodA_start;
            obj.Result.periodB_end = obj.periodB_end;
            obj.Result.cutoff = obj.cutoff;
            obj.Result.bin_dur = obj.bin_dur;

            function out = calc_probability(old)
                %calculate probabliity, where old is one of the zmaps.
                % salvaged from vi_cucro
                valueMap = old;
                l = valueMap < 2.57;
                valueMap(l) = 2.65;
                pr = 0.0024 + 0.03*(valueMap - 2.57).^2;
                pr = (1-1./(exp(pr)));
                out = pr;
            end

            % catsave3('magrcros');

            % Plot the results
            %det = 'nop'
            %in2 = 'nocal'
            %menucros() -> which was at one point hooked up to incube, but not while I've been manipulating it. CGR

            function out=calculation_function(catalog)
                if catalog.Count <= 4
                    out = nan(size(obj.CalcFields));
                    return
                end

                %% do prelim calculations, so that they are only done ONCE per grid point

                % this had all been done with histogram and bins, but makes much more sense
                % to let the time periods dictate duration

                nPerBin = histcounts(catalog.Date,edges_for_cov);
                idxBeforeCutoff = edges_for_cov(2:end) <= obj.cutoff;
                idxAfterCutoff = ~idxBeforeCutoff;
                idxInWindow = idxAfterCutoff  & edges_for_cov(2:end) <= obj.cutoff + obj.window_duration;
                idxNotInWindow = ~idxInWindow;

                nBeforeCutoff = nPerBin(idxBeforeCutoff);
                nInWindow = nPerBin(idxInWindow); % NALL(2) : # IN window
                nAfterCutoff = nPerBin(idxAfterCutoff);
                nNotInWindow = nPerBin(idxNotInWindow);

                covBeforeCutoff = cov(nPerBin(idxBeforeCutoff));
                covAfterCutoff = cov(nPerBin(idxAfterCutoff));
                covInWindow = cov(nPerBin(idxInWindow));
                covNotInWindow = cov(nPerBin(idxNotInWindow));

                durBeforeCutoff = sum(idxBeforeCutoff) * obj.bin_dur;
                durAfterCutoff = sum(idxAfterCutoff) * obj.bin_dur;
                durInWindow = sum(idxInWindow) * obj.bin_dur;
                durNotInWindow = sum(idxNotInWindow) * obj.bin_dur;

                meanBeforeCutoff = mean(nBeforeCutoff);
                meanAfterCutoff  = mean(nAfterCutoff);
                meanInWindow = mean(nInWindow);
                meanNotInWindow = mean(nNotInWindow);

                out = [calc_ast() calc_lta() calc_rubberband() calc_percent(),...
                    sum(nBeforeCutoff), sum(nAfterCutoff), sum(nInWindow), sum(nNotInWindow)];

                % now comes the actual calculations for
                function out=calc_percent()
                    out =  -((meanBeforeCutoff-meanAfterCutoff)./meanBeforeCutoff)*100;
                    assert(numel(out)==1)
                end

                function out=calc_rubberband()
                    % loop over all point for rubber band : compare window to before cutoff
                    term1 = covBeforeCutoff / days(durBeforeCutoff);
                    term2 = covInWindow / days(durInWindow);
                    out =  (meanBeforeCutoff - meanInWindow) ./ (sqrt(term1 + term2));
                    assert(numel(out)==1)
                end

                function out=calc_ast()
                    % make the AST function map : compare before and after cutoff
                    term1 =covBeforeCutoff / days(durBeforeCutoff);
                    term2 = covAfterCutoff / days(durAfterCutoff);
                    out = (meanBeforeCutoff - meanAfterCutoff) ./ (sqrt(term1 + term2));
                    assert(numel(out)==1)
                end

                function out=calc_lta()
                    % Calculate LTA: compare window to everything else
                    term1 = covNotInWindow / days(durNotInWindow);
                    term2 = covInWindow  / days(durInWindow);
                    out =  (meanNotInWindow - meanInWindow)./(sqrt(term1+term2));
                    assert(numel(out)==1)
                end

                % removed something that calculated 'maz', since it was never referenced elsewhere, and
                % the original code was seriously convoluted. -CGR

            end %  calculation_function
        end % Calculate

    end % methods
    methods(Static)
        function h = AddMenuItem(parent, zapFcn, varargin) %xsec_zap
            % create a menu item
            label = 'Z-value section map';
            h = uimenu(parent, 'Label', label,...
                'MenuSelectedFcn', @(~,~)XZfun.magrcros(zapFcn()),...
                varargin{:});
        end

        %{
        %% UNMODIFIED/UNIMPLEMENTED BY CGR
        function obj=my_load()
            % Load exist z-grid
            [file1,path1] = uigetfile(['*.mat'],'z-value gridfile');
            if length(path1) > 1

                load([path1 file1])
                det = 'nop'
                in2 = 'nocal'
                menucros
            else
                return
            end
        end
        %}

    end % static methods

end


function [sortedBodyNames, sortedBodyInfo] = ma_getSortedBodyNames(celBodyData, varargin)
%ma_getSortedBodyNames Summary of this function goes here
%   Detailed explanation goes here

    if(isempty(varargin))
        curLevel = 1;
        maxLevel = Inf;
    elseif(length(varargin)==1)
        curLevel = 1;
        maxLevel = varargin{1};
    else
        curLevel = varargin{2};
        maxLevel = varargin{1};
    end

    if(curLevel == 1)
        topLevelBodyInfo = getTopLevelCentralBody(celBodyData);
        curParent = topLevelBodyInfo;
        sortedBodyNames = {curParent.name};
    else
        curParent = varargin{3};
        sortedBodyNames = {};
    end

    if(curLevel < maxLevel)
        numIndents = curLevel;
        childBodies = getChildrenOfParentInfo(celBodyData, curParent.name);
        childBodies = sortChildrenBySma(childBodies);
        for(i=1:length(childBodies)) %#ok<*NO4LP>
            childBody = childBodies{i};
            sortedBodyNames{end+1} = paddStrLeft(childBody.name, length(childBody.name)+numIndents); %#ok<AGROW>

            sortedChildBodyNames = ma_getSortedBodyNames(celBodyData, maxLevel, curLevel+1, childBody);
            for(j=1:length(sortedChildBodyNames))
                sortedBodyNames{end+1} = sortedChildBodyNames{j}; %#ok<AGROW>
            end
        end
    end

    sortedBodyNames = sortedBodyNames';
    sortedBodyInfo = cell(size(sortedBodyNames));
    for(i=1:length(sortedBodyNames))
        sortedBodyInfo{i} = celBodyData.(strtrim(lower(sortedBodyNames{i})));
    end
end

function smaSortedChildren = sortChildrenBySma(childBodies)
    [~,I] = sort(cellfun(@sortSmaCellFun, childBodies));
    smaSortedChildren = childBodies(I);
end

function sma = sortSmaCellFun(bodyInfo)
    sma = bodyInfo.sma;
end


function [caNodeIndices, vResolution_] = ex_CreateIndexCatalog(mCatalog, mPolygon, bMap, nGriddingMode, nNumberEvents, fRadius, fSizeRectHorizontal, fSizeRectDepth)
    % Creates a cell-array with subcatalogs for every grid node defined by mPolygon
    %
    % [caNodeIndices] = ex_CreateIndexCatalog(mCatalog, mPolygon, bMap, nGriddingMode,
    %                                                  nNumberEvents, fRadius, fSizeRectHorizontal, fSizeRectDepth)
    %
    % Creates a cell-array with subcatalogs for every grid node defined by mPolygon. These subcatalogs
    %   contain only indices to the earthquake "rows" in mCatalog.
    %
    % Input parameters:
    %   mCatalog              Earthquake catalog
    %   mPolygon              Polygon (defined by ex_selectgrid)
    %   bMap                  Calculate cell-array for a map (true) or a cross-section (false)
    %   nGriddingMode         Mode of creating grid node subcatalogs
    %                         0: Constant number of events
    %                         1: Constant radius
    %                         2: Rectangular grid node samples
    %   nNumberEvents         Number of events per grid node (nGriddingMode == 0)
    %   fRadius               Radius of grid node sample (nGriddingMode == 1)
    %   fSizeRectHorizontal   Latitude/horizontal size of rectangle (nGriddingMode == 2)
    %   fSizeRectDepth        Longitude/depth size of rectangle (nGriddingMode == 2)
    %
    % Output parameters:
    %   caNodeIndices         Cell-array with index-catalogs per grid node of mPolygon
    %  vResolution_             Cell-array with distance to grid node of mPolygon
    %
    % Danijel Schorlemmer
    % June 17, 2002
    %
    % updates
    % Thomas van Stiphout August 14, 2006
    %  returns the vector vResolution that contains
    % either the maximal radius or the number of earthquake per gridnode
    %

    report_this_filefun();

    % Create the catalogs for each node with pointers to the overall catalog
    nNumberNodes_ = length(mPolygon(:,1));
    caNodeIndices = cell(nNumberNodes_, 1);
    vResolution_ = cell(nNumberNodes_, 1);
    % If cross-section calculate the length along cross-section
    if ~bMap
        nRow_ = mCatalog.Count;
        vXSecX_ = mCatalog(:,nColumn_);  % length along x-section
        vXSecY_ = (-1) * mCatalog.Depth;  % depth of hypocenters
    end
    % Loop over all points of the polygon
    for nNode_ = 1:nNumberNodes_
        % Get the grid node coordinates
        fX_ = mPolygon(nNode_, 1);
        fY_ = mPolygon(nNode_, 2);
        if (nGriddingMode == 0) | (nGriddingMode == 1)  % Fixed radius or fixed number
            % Calculate distance from center point
            if bMap
                %             vDistances_=deg2km(distance(mCatalog.Latitude,mCatalog.Longitude,ones(size(mCatalog,1),1)*fY_,ones(size(mCatalog,1),1)*fX_));
                vDistances_ = sqrt(   ((mCatalog.Longitude-fX_)*cosd(fY_)*111).^2   +     ((mCatalog.Latitude-fY_)*111).^2)  ;
            else
                vDistances_ = sqrt(((vXSecX_ - fX_)).^2 + ((vXSecY_ - fY_)).^2);
            end
            if nGriddingMode == 0 % Fixed number
                if mCatalog.Count == 0
                    caNodeIndices{nNode_} = [];
                elseif nNumberEvents > mCatalog.Count
                    caNodeIndices{nNode_} = vIndices(1:mCatalog.Count);
                    %                 Determine the Resolution i.e., radius
                    vResolution_{nNode_}=vDistances_(caNodeIndices{nNode_});
                else
                    % Use first nNumberEvents events
                    [vTmp, vIndices] = sort(vDistances_);
                    caNodeIndices{nNode_} = vIndices(1:nNumberEvents);
                    %                 Determine the Resolution i.e., radius
                    vResolution_{nNode_}=vDistances_(caNodeIndices{nNode_});
                end
            else % Fixed radius
                % Use all events within fRadius
                caNodeIndices{nNode_} = find(vDistances_ <= fRadius);
                %             Determine the Resolution i.e., radius
                vResolution_{nNode_}=size(caNodeIndices{nNode_},1);
            end
        else % Rectangular gridding (nGriddingMode == 2)
            if bMap
                vSel_ = ((mCatalog.Longitude >= (fX_ - fSizeRectHorizontal/2)) & (mCatalog.Longitude < (fX_ + fSizeRectHorizontal/2)) & ...
                    (mCatalog.Latitude >= (fY_ - fSizeRectDepth/2)) & (mCatalog.Latitude < (fY_ + fSizeRectDepth/2)));
            else
                vSel_ = ((vXSecX_ >= (fX_ - fSizeRectHorizontal/2)) & (vXSecX_ < (fX_ + fSizeRectHorizontal/2)) & ...
                    (vXSecY_ >= (fY_ - fSizeRectDepth/2)) & (vXSecY_ < (fY_ + fSizeRectDepth/2)));
            end
            caNodeIndices{nNode_} = find(vSel_ == 1);
            vResolution_{nNode_}=size(caNodeIndices{nNode_},1);
        end
    end % of for nNode_
end


function display(m,varargin)
% standard output

if check_option(varargin,'onlyShowMiller')
  eps = 1e4;
  % extract coordinates in the correct form
  d = round(m.coordinates * eps)./eps;
  % set up coordinate names
  columnNames = vec2cell(char(m.dispStyle));
  cprintf(d,'-L','  ','-Lc',columnNames);
  return
end

displayClass(m,inputname(1),'moreInfo',char(m.CS,'compact'),varargin{:});

display@vector3d(m,'skipHeader', 'skipCoordinates');

% display coordinates
if length(m) < 20 && ~isempty(m)

  display(m,'onlyShowMiller')

elseif ~getMTEXpref('generatingHelpMode') && ~isempty(m)

  disp(' ')
  s = setappdata(0,'data2beDisplayed',m);
  disp(['  show Miller'])
  disp(' ')

end