classdef EventRate
%EventRate Event Rate class constructor.
%
%    EventRate is a class that has been developed around plotting earthquake
%    counts - i.e. the rate of events per unit time. It has evolved to compute
%    other metrics such the hourly mean event rate, median event rate, mean
%    magnitude and cumulative magnitude, which are important metrics for an AVO
%    swarm tracking system.
%
%    EventRate can import information from:
%    (1) a Catalog object.
%    (2) a Datascope database written in the "swarms1.0" schema, defined at AVO.
%        This is the format used by the swarm tracking system (Thompson &
%        West, 2010).
%
%    ER = EventRate(Catalog_OBJECT, 'binsize', BINSIZE) creates an eventrate object
%    from a Catalog object using non-overlapping bins of BINSIZE days.
%
%    ER = EventRate(Catalog_OBJECT, 'binsize', BINSIZE, 'stepsize', STEPSIZE) creates an eventrate object
%    using overlapping bins. If omitted STEPSIZE==BINSIZE.
%
%%   EXAMPLES:
%
%       First create a catalog object from the demo database:
%           dbpath = demodb('avo')
%           catalogObject = readEvents('datascope', 'dbpath', dbpath, ...
%                  'dbeval', ...
%                  'deg2km(distance(lat, lon, 60.4853, -152.7431))<15.0' ...
%                  );
%
%       (1) Create an eventrate object using a binsize of 1 day:
%           erobj = catalogObject.eventrate('binsize', 1);
%
%       (2) Create an eventrate object using a binsize of 1 hour:
%           erobj = catalogObject.eventrate('binsize', 1/24);
%
%       (3) Create an eventrate object using a binsize of 1 hour but a stepsize of 5 minutes:
%           erobj = catalogObject.eventrate('binsize', 1/24, 'stepsize', 5/1440);
%
%%   PROPERTIES
%
%    For a list of all properties type properties(EventRate)
%
%    time                % (array) time of the center of each bin as a DATENUM
%
%    METRICS:
%        counts 		     % (array) number of events in each bin
%        mean_rate           % (array) number of events per hour in each bin
%        median_rate	     % (array) reciprocal of the median time interval between events. Represented as an hourly rate.
%        cum_mag		     % (array) total sum of energy in each bin, represented as a magnitude.
%        mean_mag		     % (array) mean magnitude of events in each bin
%        median_mag          % (array) median magnitude of events in each bin
%        min_mag             % (array) smallest magnitude in each bin
%        max_mag             % (array) largest magnitude in each bin
%
%    SUMMARY DATA:
%        numbins             % (scalar) number of bins used for grouping
%                                events
%        total_counts        % (scalar) sum of counts
%        total_mag           % (scalar) total sum of energy of all catalogObjects, represented as a magnitude
%
%    METADATA:
%        etype               % event type/classification.
%        snum                % (scalar) start date/time in DATENUM format
%        enum                % (scalar) end date/time in DATENUM format
%        binsize             % (scalar) bin size in days
%        stepsize            % (scalar) step size in days
%        region              % (4-element vector) [minlon maxlon minlat maxlat]
%        minmag              % (scalar) magnitudes smaller than this were eliminated
%        dbroot              % path to the original data on disk
%        archiveformat       % indicates if the source is a flat file, or
%                              'daily' or 'monthly' volumes
%        auth                % auth of the events
%
%%   METHODS
%
%    For a list of all methods type methods EventRate
%
%
%%   See also Catalog, Catalog_lite
%
%% AUTHOR: Glenn Thompson

% $Date: 2014-05-06 14:52:40 -0800 (Tue, 06 May 2014) $
% $Revision: 404 $

% I don't think these parts work anymore
%       (4) Create a vector of eventrate objects subclassified using event types 'r', 'e', 'l', 'h', 't':
%               erobj = eventrate(catalogObject, 1, 'etypes', 'relht');
%           To plot counts on separate figures:
%               erobj.plot()
%           To plot counts and energy panels, each event type as a separate figure:
%               erobj.plot('metric', {'counts';'energy'});
%           To plot counts and energy panels on separate figures, each event type as panels:
%               erobj.plot('metric', {'counts';'energy'}, 'plotmode', 'panels');
%           To plot counts and energy panels on separate figures, each event type stacked:
%               erobj.plot('metric', {'counts';'energy'}, 'plotmode', 'stacked');
%
%       (5) A full example:
%               catalogObject = catalog(fullfile(MVO_DATA, 'mbwh_catalog'), 'seisan', 'snum', datenum(1996,10,1), 'enum', datenum(2004,3,1), 'region', 'Montserrat')
%               erobj = eventrate(catalogObject, 365/12, 'stepsize', 1, 'etypes', 'thlr');
%               erobj.plot('metric', {'counts';'energy'}, 'plotmode', 'stacked');
%

%% PROPERTIES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    properties(GetAccess = 'public', SetAccess = 'public')
        time = [];          % (array) in datenum format
        counts = []; 		% (array) number of events in each bin
		mean_rate = [];      % (array) number of events per hour in each bin
		median_rate = [];	% (array) reciprocal of the median time interval between events. Represented as an hourly rate.
		cum_mag = [];		% (array) total sum of energy in each bin, represented as a magnitude.
		mean_mag = [];		% (array)
        median_mag = [];     % (array)
        energy = [];
        total_counts = [];   % (scalar) sum of counts
		total_mag = [];      % (scalar) total sum of energy of all catalogObjects, represented as a magnitude
        numbins = [];        % (scalar)
        min_mag = [];
        max_mag = [];
        etype = '*';
        snum = 0;
        enum = now;
        binsize = 1;
        stepsize = 1;
        misc_fields = {};
        misc_values = {};
    end

 %% PUBLIC METHODS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

	methods
        %% CONSTRUCTOR
        function self = EventRate(time, counts, energy, median_energy, ...
                smallest_energy, biggest_energy, median_time_interval, total_counts, ...
                snum, enum, etypes, binsize, stepsize, numbins)
            self.time = time;
            self.counts = counts;
            self.median_rate = 1 ./ (median_time_interval * 24);
            self.median_rate(counts<10) = 0;
            self.median_rate = max([self.counts / (24 * binsize); self.median_rate]);
            self.median_mag = magnitude.eng2mag(median_energy);
            self.energy = energy;
            self.total_counts = total_counts;
            self.numbins = numbins;
            self.min_mag = magnitude.eng2mag(smallest_energy);
            self.max_mag = magnitude.eng2mag(biggest_energy);
            self.etype = etypes;
            self.snum = snum;
            self.enum = enum;
            self.binsize = binsize;
            self.stepsize = stepsize;
            if (enum-snum) < binsize
                error('binsize cannot be bigger than data time range');
            end
        end

        %% ----------------------------------------------
        %% GETTERS
        function cum_mag = get.cum_mag(erobj)
            cum_mag = magnitude.eng2mag(erobj.energy);
        end
        function mean_mag = get.mean_mag(erobj)
            mean_mag = magnitude.eng2mag(erobj.energy./erobj.counts);
        end
        function mean_rate = get.mean_rate(erobj)
            mean_rate = erobj.counts / (24 * erobj.binsize);
        end
        function total_mag = get.total_mag(erobj)
            total_mag = magnitude.eng2mag(sum(erobj.energy));
        end

    end % methods

    methods(Static)
        cookbook()
    end

end


function x = project_sample(x, P)

if ~isempty(P)
    x = cellfun(@(x, P) permute(mtimesx(permute(x, [4 3 1 2]), P, 'speed'), [3 4 2 1]), x, P, 'uniformoutput', false);
end


%% plane_intersect
% Below is a demonstration of the features of the |plane_intersect| function

%%
clear; close all; clc;

%% Syntax
% |X=plane_intersect(V1,V2,V3,N1,N2,N3);|

%% Description
% UNDOCUMENTED
%% Examples
%
%%
%
% <<gibbVerySmall.gif>>
%
% _*GIBBON*_
% <www.gibboncode.org>
%
% _Kevin Mattheus Moerman_, <gibbon.toolbox@gmail.com>
%%
% _*GIBBON footer text*_
%
% License: <https://github.com/gibbonCode/GIBBON/blob/master/LICENSE>
%
% GIBBON: The Geometry and Image-based Bioengineering add-On. A toolbox for
% image segmentation, image-based modeling, meshing, and finite element
% analysis.
%
% Copyright (C) 2006-2022 Kevin Mattheus Moerman and the GIBBON contributors
%
% This program is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program.  If not, see <http://www.gnu.org/licenses/>.


function [c, ceq] = ma_surfaceVelConstraint(stateLog, eventID, lbHorzVel, ubHorzVel, bodyIDApply, celBodyData, maData)
%ma_semiMajorAxisConstraint Summary of this function goes here
%   Detailed explanation goes here

    normFact = 1;

    if(ischar(eventID) && strcmpi(eventID,'final'))
        eventNum = max(stateLog(:,13));
    else
        [~, eventNum] = getEventByID(eventID, maData.script);
    end

    eventLog = stateLog(stateLog(:,13)==eventNum,:);
    finalEntry = eventLog(end,:);

    bodyID = finalEntry(8);

    if(bodyID == bodyIDApply || bodyIDApply==-1)
        horzVel = ma_GALongLatAltTasks(finalEntry, 'horzVel', celBodyData);

        if(lbHorzVel == ubHorzVel)
            c = [0 0];
            ceq(1) = horzVel - ubHorzVel;
        else
            c(1) = lbHorzVel - horzVel;
            c(2) = horzVel - ubHorzVel;
            ceq = [0];
        end
        c = c/normFact;
        ceq = ceq/normFact;
    else
        c = [0 0];
        ceq = [0];
    end
end


function varargout = sinh(varargin)
%SINH   Hyperbolic sine of a DISKFUN.
%
% See also DISKFUN/SIN and DISKFUN/COSH.

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

[varargout{1:nargout}] = sinh@separableApprox(varargin{:});

end


function computeAllFinalRF_HN(pathRF,nBoot,seed)

startpath = pwd;

cd(pathRF), load('training')
nameOutcomes = fieldnames(training.outcomes); nOutcomes = numel(nameOutcomes);
fSetNames = fieldnames(training.textures.(nameOutcomes{1})); nFset = numel(fSetNames);

for o = 1:nOutcomes
    if strcmp(nameOutcomes{o},'DeathSign')
        outcome = training.outcomes.Death;
    else
        outcome = training.outcomes.(nameOutcomes{o});
    end
    for f = 1:nFset
        text = training.textures.(nameOutcomes{o}).(fSetNames{f}); nText = size(text,2);
        indClinic = training.clinical.bestAdd.(nameOutcomes{o}).(fSetNames{f});
        cost = training.cost.(nameOutcomes{o}).(fSetNames{f});
        tableTrain = [text,training.clinical.table(:,indClinic)];
        cat = logical([zeros(1,nText),training.clinical.categories(indClinic)]);
        rng(seed), [RF] = trainRF_table(tableTrain,outcome,cat,nBoot,cost);
        RF = compact(RF); % Compact version
        save(['RF_',[fSetNames{f},'clinic'],'_',nameOutcomes{o}],'RF')
    end
end

cd(startpath)
end


function [Model cfg] = est_fitMVAR(varargin)
%
% Fit (adaptive) multivariate autoregressive model to EEG data. See [1] for
% details on VAR model fitting and implementations.
%
%
% Output:
%
%   Model structure with
%       .Model          (numvars x coeffs) matrix of VAR coefficients
%       .PE             (numvars x coeffs) prediction error (noise covariance) coefficients
%       .algorithm      string denoting algorithm used for estimation
%       .modelclass     string denoting model class (here, 'mvar')
%
% See Also: pop_est_fitMVAR(), pop_pre_prepData()
%
% References:
%
% [1] Mullen T (2010) The Source Information Flow Toolbox (SIFT):
%   Theoretical Handbook and User Manual. Chapters 3,6.
%   Available at: http://www.sccn.ucsd.edu/wiki/Sift/
%
%
% Author: Tim Mullen 2010, SCCN/INC, UCSD.
% Email:  tim@sccn.ucsd.edu

% This function is part of the Source Information Flow Toolbox (SIFT)
%
% This program is free software; you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation; either version 3 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program; if not, write to the Free Software
% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

% pre-cache some frequently used sub-arguments
persistent subargs;
if isempty(subargs)
    subargs.algos = hlp_getMVARalgorithms('defaultNameOnly');
    subargs.all_algos = hlp_getMVARalgorithms;
    subargs.sift_domain = hlp_buildMVARHelpText;
end

verb = arg_extract(varargin,{'verb','VerbosityLevel'},[],2);

haveSigProc = hlp_isToolboxInstalled('Signal Processing Toolbox');
if haveSigProc
    taperFcns = {'rectwin','hamming','hann','bartlett','barthannwin',   ...
                 'blackman','blackmanharris','bohmanwin','chebwin',     ...
                 'flattopwin','gausswin','kaiser','nuttallwin',         ...
                 'parzenwin','taylorwin','tukeywin','triang'};
else
    taperFcns = {'rectwin'};
end

g = arg_define([0 1],varargin, ...
    arg_norep({'EEG','ALLEEG'},mandatory,[],'EEGLAB dataset'), ...
    arg_subswitch({'algorithm','Algorithm'},subargs.algos,subargs.all_algos,{'Select a model fitting algorithm.',subargs.sift_domain},'cat','Modeling Parameters','suppress',{'ModelOrder','OrderSelector','InitialState'}), ...
    arg({'morder','ModelOrder','modelOrder'},10,[1 Inf],'VAR model order.','cat','Modeling Parameters'), ...
    arg_nogui({'winStartIdx','WindowStartIndices'},[],[],'Starting indices for windows. This is a vector of sample points (start of windows) at which to estimate windowed VAR model','cat','Modeling Parameters'), ...
    arg({'winlen','WindowLength'},0.5,[eps Inf],'Sliding window length (sec)','cat','Modeling Parameters'), ...
    arg({'winstep','WindowStepSize'},0.03,[eps Inf],'Window step size (sec)','cat','Modeling Parameters'), ...
    arg({'taperfcn','TaperFunction'},'rectwin',taperFcns,{'Data tapering (windowing) function', ...
                                                            sprintf(['\n' ...
                                                                     'Each data segment (e.g. all data in a sliding window) will be multipled by the data taper to smooth endpoints towards zero.', ...
                                                                     '\n' ...
                                                                     'Available windows are:\n' ...
                                                                          'rectwin \t - Rectangular window. This is equivalent to no taper.\n', ...
                                                                          'hamming \t - Hamming window.\n', ...
                                                                          'hann \t - Hann window.\n', ...
                                                                          'bartlett \t - Bartlett window.\n', ...
                                                                          'barthannwin \t - Modified Bartlett-Hanning window.\n', ...
                                                                          'blackman \t - Blackman window.\n', ...
                                                                          'blackmanharris- Minimum 4-term Blackman-Harris window.\n', ...
                                                                          'bohmanwin \t - Bohman window.\n', ...
                                                                          'chebwin \t - Chebyshev window.\n', ...
                                                                          'flattopwin \t - Flat Top window.\n', ...
                                                                          'gausswin \t - Gaussian window.\n', ...
                                                                          'kaiser \t - Kaiser window.\n', ...
                                                                          'nuttallwin \t - Nuttall defined minimum 4-term Blackman-Harris window.\n', ...
                                                                          'parzenwin \t - Parzen (de la Valle-Poussin) window.\n', ...
                                                                          'taylorwin \t - Taylor window.\n', ...
                                                                          'tukeywin \t - Tukey window.\n', ...
                                                                          'triang \t - Triangular window.\n' ...
                                                                        ])},'cat','Modeling Parameters'), ...
    arg({'epochTimeLims','EpochTimeLimits'},[],[],'Sub-epoch time limits (sec). This is relative to event time (e.g. [-1 2]). Default is the full epoch time range','cat','Modeling Parameters'), ...
    arg({'prctWinToSample','WindowSamplePercent'},100,[1 100],'Percent of windows to sample','cat','Modeling Parameters'), ...
    arg_subtoggle({'normalize','NormalizeData'},[],@pre_normData,'Z-normalize data within windows. Note this is not recommended for short windows','cat','Window Preprocessing'), ...
    arg_subtoggle({'detrend','Detrend'},{}, ...
    {arg({'method','DetrendingMethod'},'constant',{'linear','constant'},{'Detrend data within each window.', ...
    sprintf(['\n' ...
    'Linear: removes the least-squares fit of a straight line.\n' ...
    'Constant: removes the mean from each trial (centering)' ...
    ]) ...
    } ...
    )},'Detrend or center each time window','cat','Window Preprocessing'), ...
    arg({'timer','Timer'},false,[],'Activate timer. Times are stored in EEG.CAT.Model.timeelapsed'), ...
    arg({'setArgDirectMode','SetArgDirectMode'},true,[],'Set arg_direct mode to true. Can improve speed when number of windows is large. Disable if calling this function repeatedly in a tight loop.'), ...
    arg({'verb','VerbosityLevel'},verb,{int32(0) int32(1) int32(2)},'Verbosity level. 0 = no output, 1 = text, 2 = graphical') ...
    );

% commit EEG variable to workspace
[data g] = hlp_splitstruct(g,{'EEG'});
arg_toworkspace(data);
clear data;

% do some error-checking
if isempty(g.epochTimeLims)
    g.epochTimeLims = [EEG.xmin EEG.xmax]; end
if ~(all(g.epochTimeLims>=EEG.xmin) && all(g.epochTimeLims<=EEG.xmax))
    error('Epoch time limits must be within the range [%0.3g %0.3g]',EEG.xmin,EEG.xmax); end
if isempty(g.morder) || length(g.morder)>1
    error('invalid entry for field ''morder.'' Make sure the model order is a single integer.'); end

if nargout > 1, cfg = g; end

%% do some adjustments to parameters

% ensure we are using the right model order
g.algorithm.morder = g.morder;

if rem(g.winstep,1/EEG.srate)
    if g.winstep<1/EEG.srate
        g.winstep = 1/EEG.srate;
    else
        % adjust step size to nearest multiple of sampling interval
        g.winstep = g.winstep-rem(g.winstep,1/EEG.srate);
    end

    if g.verb,
        fprintf('Adjusting window step size to nearest multiple of sampling interval\n');
        fprintf('\tstep size is now %0.5g sec\n',g.winstep);
    end
end

if rem(g.winlen,1/EEG.srate)
    if g.winlen<1/EEG.srate
        g.winlen = 1/EEG.srate;
    else
        % adjust window length to nearest multiple of sampling interval
        g.winlen = g.winlen-rem(g.winlen,1/EEG.srate);
    end

    if g.verb,
        fprintf('Adjusting window length to nearest multiple of sampling interval\n');
        fprintf('\twindow length is now %0.5g sec\n',g.winlen);
    end
end
if g.winlen > EEG.xmax-EEG.xmin
    g.winlen = EEG.xmax-EEG.xmin;
    if g.verb,
        fprintf('Window length exceeds epoch length. Adjusting window length to match epoch length\n');
        fprintf('\twindow length is now %0.5g sec\n',g.winlen);
    end
end
tidx = getindex(EEG.CAT.times,g.epochTimeLims*1000);
if ~all(isequal(EEG.CAT.times(tidx),g.epochTimeLims*1000))

    g.epochTimeLims = EEG.CAT.times(tidx)/1000;

    if g.verb
        fprintf('Adjusting epoch time limits to match sampling interval\n');
        fprintf('\tepoch limits are now [%0.5g, %0.5g] sec\n',g.epochTimeLims(1),g.epochTimeLims(2));
    end
end

winLenPnts  = round(g.winlen*EEG.srate); % window size in points
winStepPnts = round(g.winstep*EEG.srate);

if isempty(g.winStartIdx)
    % starting point of each window (points)
    g.winStartIdx  = tidx(1):winStepPnts:(tidx(2)-winLenPnts)+1;
    %g.winStartIdx  =  round((double(g.epochTimeLims(1):g.winstep:g.epochTimeLims(2)-g.winlen)*EEG.srate)+1;
end

if g.prctWinToSample<100
    % randomly select percentage of windows to work with
    randwin = randperm(length(g.winStartIdx));
    randwin = sort(randwin(1:ceil(length(g.winStartIdx)*g.prctWinToSample/100)));
    g.winStartIdx = g.winStartIdx(randwin);
end

numWins   = length(g.winStartIdx);

% construct the data taper
g.taper = window_func(g.taperfcn,winLenPnts).';

%% Prepare data for model fitting

% initialize results arrays
[AR PE RC mu th lambdaOpt]  = deal(cell(1,numWins));

if g.verb==2
    waitbarTitle = sprintf('Fitting VAR[%d] model (%s)...', ...
                        g.morder, ...
                        num2str(g.algorithm.arg_selection));

    multiWaitbar(waitbarTitle,'Reset');
    multiWaitbar(waitbarTitle, ...
                 'Color', hlp_getNextUniqueColor, ...
                 'CanCancel','on', ...
                 'CancelFcn',@(a,b) disp('[Cancel requested. Please wait...]'));
end

if g.detrend.arg_selection
    % detrend each window separately
    if g.verb, fprintf('%s detrending each window...\n',firstcaps(g.detrend.method)); end
    for ch=1:EEG.CAT.nbchan
        EEG.CAT.srcdata(ch,:,:) = locdetrend_siftmod(squeeze(EEG.CAT.srcdata(ch,:,:)), ...
            EEG.srate,[g.winlen g.winstep],g.detrend.method);
    end
    if g.verb, fprintf('done.\n'); end
end

if g.timer
    timeElapsed = nan(1,numWins);
else
    timeElapsed = [];
end

%% Main loop: fit MVAR models to each window

algFcnName = hlp_nanocache('algos',10,@hlp_getMVARalgorithms,'mfileNameOnly',g.algorithm.arg_selection);

if g.setArgDirectMode && ~strcmp(algFcnName,'mvar_glADMM') % not necessary for glADMM
    % set the arg_direct flag
    % to improve speed
   g = arg_setdirect(g,true);
end

for t=1:numWins

    if g.timer, tic; end

    % get data for current window
    data = EEG.CAT.srcdata(:,g.winStartIdx(t):g.winStartIdx(t)+winLenPnts-1,:);
    if g.normalize.arg_selection
        % normalize the data window
        data = pre_normData('data',data,'method',g.normalize.method,'verb',0,'arg_direct',true);
    end

    % execute the model-fitting algorithm
    switch nargout(algFcnName)
        case 2
            [AR{t} PE{t}] = feval(algFcnName, ...
                'data',bsxfun(@times,g.taper,data), ...
                g.algorithm,'arg_direct',true);
        case 3
            [AR{t} PE{t} argsout] = feval(algFcnName, ...
                                   'data',bsxfun(@times,g.taper,data),...
                                    g.algorithm,'arg_direct',true);
            if isstruct(argsout)
                % store contents of argsout fields in cell array at index t
                % e.g. fieldname{t} = argsout.(fieldname)
                fnames = fieldnames(argsout);
                for k=1:length(fnames)
                    eval([fnames{k} '{t}=argsout.(''' fnames{k} ''');']);
                end
            end
        otherwise
            error('SIFT:est_fitMVAR:badAlgArgs','%s must output either 2 or 3 arguments',algFcnName);
    end

    if g.verb==2
        drawnow;
        % graphical waitbar
        cancel = multiWaitbar(waitbarTitle,t/numWins);
        if cancel
            if strcmpi('yes',questdlg2( ...
                            'Are you sure you want to cancel?', ...
                            'Model Fitting','Yes','No','No'));
                Model = [];
                multiWaitbar(waitbarTitle,'Close');
                return;
            else
                multiWaitbar(waitbarTitle,'ResetCancel',true);
            end
        end
    end

    if g.timer, timeElapsed(t) = toc; end
end

%% Do some cleanup
clear('-regexp','mvar_*')
if g.verb==2
    multiWaitbar(waitbarTitle,'Close');
end

%% Construct Model object
Model = hlp_sift_emptymodel;

Model.AR = AR;
Model.PE = PE;
Model.RC = RC;
Model.mu = mu;
Model.th = th;
Model.lambdaOpt = lambdaOpt;
Model.winStartTimes = (g.winStartIdx-1)/EEG.srate;
Model.morder        = g.morder;
Model.winstep       = g.winstep;
Model.winlen        = g.winlen;
Model.algorithm     = g.algorithm.arg_selection;
Model.modelclass    = 'mvar';
Model.timeelapsed   = timeElapsed;
Model.normalize     = g.normalize;
Model.modelapproach = 'Segmentation VAR';
Model.taperFcn      = g.taperfcn;

switch lower(g.algorithm.arg_selection)
    case 'group lasso dal/scsa'
        %     Model.ww = ww;
        Model.lambda = g.algorithm.dal_args.lambda;
    case 'group lasso (admm)'
        Model.lambda = g.algorithm.admm_args.lambda;
        Model.rho    = g.algorithm.admm_args.rho;
        Model.alpha  = g.algorithm.admm_args.alpha;
end


function [dat_sf,W] = proc_multiBandSpatialFilter(dat,method)
%PROC_MULTIBANDSPATIALFILTER - Apply spatial filtering method to multiple
%frequency bands
%
%Synopsis:
% [DAT_SF, W] = proc_multiBandSpatialFilter(DAT, METHOD);
%
%Arguments:
% DAT    - data structure of epoched and pre-filtered data
% METHOD - cell array containing in the first entry a function handle to
%          the spatial filtering method and (optionally) in the following
%          entries parameters for that function
%
%Returns:
% DAT_SF - updated data structure, containing the spatially filtered data
%          of all bands, appended as channels
% W      - cell array with each entry containing the spatial filters (in
%          the columns) of each band
%
%Description:
% The input data structure is assumed to contain pre-filtered channels as
% returned by the function proc_filterbank. proc_multiBandSpatialFilter can
% be used either alone or as a processing step in the crossvalidation
% function.
%
%See also: crossvalidation proc_filterbank proc_multiBandLinearDerivation

% 10-2015: schultze-kraft@tu-berlin.de

misc_checkType(dat,'STRUCT(x clab y)');
misc_checkType(method,'CELL');
procFunc = method{1};
misc_checkType(procFunc,'!FUNC');
if length(method)>1
    procPar = method(2:end);
else
    procPar = {};
end

% get number of frequency bands
band_ix = zeros(1,length(dat.clab));
flt_ix = cellfun(@(x) strfind(x,'flt'),dat.clab);
for ii = 1:length(dat.clab)
    band_ix(ii) = str2double(dat.clab{ii}(flt_ix(ii)+3:end));
end
n_bands = max(band_ix);

% band-wise apply spatial filtering method
W = cell(1,n_bands);
dat_sf = [];
for bi = 1:n_bands
    dat2 = proc_selectChannels(dat,sprintf('*flt%d',bi));
    [dat2,W{bi}] = procFunc(dat2,procPar{:});
    dat_sf = proc_appendChannels(dat_sf,dat2);
end


classdef SecondOrderGravOnlyPropagator < AbstractPropagator
    %ForceModelPropagator Summary of this class goes here
    %   Detailed explanation goes here

    properties
        forceModels ForceModelsEnum = [ForceModelsEnum.Gravity];
    end

    properties(Constant)
        propagatorEnum = PropagatorEnum.SecOrdGravOnly;
    end

    methods
        function obj = SecondOrderGravOnlyPropagator()

        end

        function [t,y,te,ye,ie] = propagate(obj, integrator, tspan, eventInitStateLogEntry, ...
                                            eventTermCondFuncHandle, termCondDir, maxT, checkForSoITrans, nonSeqTermConds, nonSeqTermCauses, minAltitude, celBodyData, ...
                                            tStartPropTime, maxPropTime)

            if(not(isa(integrator, 'AbstractSecondOrderIntegrator')))
                error('The selected integrator must be a second order integrator in order to use this propagator.');
            end

            plugins = eventInitStateLogEntry.lvdData.plugins;

            %Create function handles
            odefun = obj.getOdeFunctionHandle(eventInitStateLogEntry);
            evtsFunc = obj.getOdeEventsFunctionHandle(eventInitStateLogEntry, eventTermCondFuncHandle, termCondDir, maxT, checkForSoITrans, nonSeqTermConds, nonSeqTermCauses, minAltitude, celBodyData);
            odeOutputFun = obj.getOdeOutputFunctionHandle(tStartPropTime, maxPropTime, eventInitStateLogEntry, plugins);

            %Propagate!
            [t0,y0,yp0] = eventInitStateLogEntry.getSecondOrderIntegratorStateRepresentation();

            if(eventInitStateLogEntry.isHoldDownEnabled())
                %Integrate in the body-fixed frame with zero rates
                %For performance reasons
                bodyInfo = eventInitStateLogEntry.centralBody;
                [rVectECEF, vVectECEF] = getFixedFrameVectFromInertialVect(t0, y0(1:3)', bodyInfo, yp0(1:3)');
                y0 = [rVectECEF', vVectECEF'];

                [t,y,yp,te,ye,ype,ie] = integrator.integrate(odefun, tspan, y0, yp0, evtsFunc, odeOutputFun);

                [rVectECI, vVectECI] = getInertialVectFromFixedFrameVect(t, y(:,1:3)', bodyInfo, yp(:,1:3)');
                y = [rVectECI', vVectECI'];

                if(~isempty(ye))
                    [rVectECIe, vVectECIe] = getInertialVectFromFixedFrameVect(te, ye(:,1:3)', bodyInfo, ype(:,1:3)');
                    ye = [rVectECIe', vVectECIe'];
                end
            else
                [t, y, yp, te, ye, ype, ie] = integrator.integrate(odefun, tspan, y0, yp0, evtsFunc, odeOutputFun);
            end

            y = horzcat(y,yp);
            ye = horzcat(ye,ype);
        end

        function odeFH = getOdeFunctionHandle(obj, eventInitStateLogEntry)
            tankStates = eventInitStateLogEntry.getAllActiveTankStates();
            dryMass = eventInitStateLogEntry.getTotalVehicleDryMass();
            pwrStorageStates = eventInitStateLogEntry.getAllActivePwrStorageStates();
            odeFH = @(t,y) SecondOrderGravOnlyPropagator.odefun(t,y, eventInitStateLogEntry, tankStates, dryMass, pwrStorageStates, obj.forceModels);
        end

        function odeEventsFH = getOdeEventsFunctionHandle(~, eventInitStateLogEntry, eventTermCondFuncHandle, termCondDir, maxT, checkForSoITrans, nonSeqTermConds, nonSeqTermCauses, minAltitude, celBodyData)
            odeEventsFH = @(t,y,yp) AbstractPropagator.odeEvents(t,vertcat(y,yp), eventInitStateLogEntry, eventTermCondFuncHandle, termCondDir, maxT, checkForSoITrans, nonSeqTermConds, nonSeqTermCauses, minAltitude, celBodyData);
        end

        function odeOutputFH = getOdeOutputFunctionHandle(~, tStartPropTime, maxPropTime, eventInitStateLogEntry, plugins)
            odeOutputFH = @(t,y,yp,flag) SecondOrderGravOnlyPropagator.odeOutput(t,y,yp,flag, tStartPropTime, maxPropTime, eventInitStateLogEntry, plugins);
        end

        function [value,isterminal,direction,causes] = callEventsFcn(obj, odeEventsFun, stateLogEntry)
            [t,y,yp] = stateLogEntry.getSecondOrderIntegratorStateRepresentation();
            [value,isterminal,direction,causes] = odeEventsFun(t,y,yp);
        end

        function openOptionsDialog(obj)
            fms = obj.forceModels;

            fmArr = ForceModelsEnum.getEnumsOfDisablableForceModels();
            fmArr = fmArr([fmArr.allowedForSecondOrder] == true);

            [~,initSelInds] = ismember(fms, fmArr);
            initSelInds = initSelInds(initSelInds > 0);

            out = AppDesignerGUIOutput();
            listdlgARH_App('ListString',{fmArr.name}, ...
                            'SelectionMode', 'multiple', ...
                            'ListSize', [300, 300], ...
                            'Name', 'Select Force Models', ...
                            'PromptString', {'Select the Force Models you wish to have enabled during this','event.  Gravity is always enabled.  Disabling Thrust during','periods of coasting may improve performance considerably.'}, ...
                            'InitialValue', initSelInds, ...
                            'out',out);
            Selection = out.output{1};
            ok = out.output{2};

            if(ok == 1)
                obj.forceModels = [ForceModelsEnum.getAllForceModelsThatCannotBeDisabled(), fmArr(Selection)'];
            end
        end

        function tf = canProduceThrust(obj)
            tf = false;
        end
    end

    methods(Static)
        function [ut, rVect] = decomposeIntegratorTandY(t,y)
            ut = t;
            rVect = y(1:3);
        end
    end

    methods(Static, Access=private)
        %%%
        %ODE Function
        %%%
        function d2ydt2 = odefun(t,y, eventInitStateLogEntry, tankStates, dryMass, powerStorageStates, fmEnums)
            bodyInfo = eventInitStateLogEntry.centralBody;
            if(isstruct(bodyInfo.celBodyData) || isempty(bodyInfo.celBodyData))
                bodyInfo.celBodyData = eventInitStateLogEntry.celBodyData;
            end

            [ut, rVect] = SecondOrderGravOnlyPropagator.decomposeIntegratorTandY(t,y);
            vVect = [0;0;0]; %placeholder - this ODE function can't be a function of velocity, only position
            altitude = norm(rVect) - bodyInfo.radius;

            holdDownEnabled = eventInitStateLogEntry.isHoldDownEnabled();

            d2ydt2 = zeros(length(y),1);
            if(holdDownEnabled)
                %launch clamp is enabled, only motion is circular motion
                %(fixed to body)
                %In this case, we are integrating in the body-fixed frame,
                %so all rates are effectively zero
                d2ydt2(1:3) = [0;0;0];
            else
                %launch clamp disabled, propagate like normal
                if(altitude <= 0 && any(fmEnums == ForceModelsEnum.Normal))
                    rswVVect = rotateVectorFromEciToRsw(vVect, rVect, vVect);
                    rswVVect(1) = 0; %kill vertical velocity because we don't want to go throught the surface of the planet
                    vVect = rotateVectorFromRsw2Eci(rswVVect, rVect, vVect);
                end

                thirdBodyGravity = eventInitStateLogEntry.thirdBodyGravity;

                totalMass = eventInitStateLogEntry.getTotalVehicleMass(); %this isn't the total mass but because we can't

                if(totalMass > 0)
                    [forceSum] = TotalForceModel.getForce(fmEnums, ut, rVect, vVect, totalMass, bodyInfo, [], [], [], [], [], [], dryMass, [], thirdBodyGravity, [], []);
                    accelVect = forceSum/totalMass;
                else
                    accelVect = zeros(3,1);
                end

                d2ydt2(1:3) = accelVect;
            end
        end

        %%%
        %ODE Output
        %%%
        function status = odeOutput(t,y,yp,flag, intStartTime, maxIntegrationDuration, eventInitStateLogEntry, plugins)
            y = vertcat(y,yp);
            plugins.executePluginsAfterTimeStepOdeOutputFcn(t,y,flag, eventInitStateLogEntry);

            integrationDuration = toc(intStartTime);

            status = 0;
            if(integrationDuration > maxIntegrationDuration)
                status = 1;
            end
        end
    end
end


% REMOVE_KS2_DUPLICATE_SPIKES2 Double-counted spikes are hard to avoid with
% Kilosort's template matching algorithm since the overall fit can be
% improved by having multiple templates jointly account for a single variable waveform.
%
% This function takes the kilosort2 output rez and identifies pair of
% spikes that are close together in time and space. The temporal threshold
% is give by the parameter OVERLAP_S which is 0.5ms by default and
% the spatial threshold (applied to the template primary sites) is given by
% CHANNEL_SEPARATION_UM and is 100 by default.
%
% From these spike pairs, it identifies the pair with the larger template as
% being the "main" or "reference" cluster and the duplicate spikes from the
% other cluster are removed.
%
% All spike pairs are considered, not just those from CCG-contaminated
% pairs, as in REMOVE_KS2_DUPLICATE_SPIKES2.
%
% Adrian Bondy, 2020
%
%=INPUT
%
%   rez structure
%
%=OPTIONAL INPUT, NAME-VALUE PAIRS
%
%   overlap_s
%       the time interval, in second, within which a sequence of spikes are
%       vetted for duplicates.
%
%   channel_separation_um
%       When the primay channels of two spikes are within this distance, in
%       microns, then the two spikes are vetted for duplicate.
%
%=EXAMPLE
%
%   >> rez = remove_ks2_duplicate_spikes(rez)
function rez = remove_ks2_duplicate_spikes(rez, varargin)
    input_parser = inputParser;
    addParameter(input_parser, 'overlap_s', 5e-4, @(x) (isnumeric(x))) % the temporal window within which pairs of spikes will be considered duplicates (if they are also within the spatial window)
    addParameter(input_parser, 'channel_separation_um', 100, @(x) (ischar(x))) % the spatial window within which pairs of spikes will be considered duplicates (if they are also within the temporal window)
    parse(input_parser, varargin{:});
    P = input_parser.Results;

    spike_times = uint64(rez.st3(:,1));
    spike_templates = uint32(rez.st3(:,2));

    rez.U=gather(rez.U);
    rez.W = gather(rez.W);
    templates = zeros(rez.ops.Nchan, size(rez.W,1), size(rez.W,2), 'single');
    for iNN = 1:size(templates,3)
       templates(:,:,iNN) = squeeze(rez.U(:,iNN,:)) * squeeze(rez.W(:,iNN,:))';
    end
    templates = permute(templates, [3 2 1]); % now it's nTemplates x nSamples x nChannels
    n_spikes=numel(spike_times);
    %% Make sure that the spike times are sorted
    if ~issorted(spike_times)
        [spike_times, spike_idx] = sort(spike_times);
        spike_templates = spike_templates(spike_idx);
    else
        spike_idx=(1:n_spikes)';
    end
    %% deal with cluster 0
    if any(spike_templates==0)
        error('Currently this function can''t deal with existence of cluster 0. Should be OK since it ought to be run first in the post-processing.');
    end
    %% Determine the channel where each spike had that largest amplitude (i.e., the primary) and determine the template amplitude of each cluster
    whiteningMatrix = rez.Wrot/rez.ops.scaleproc;
    whiteningMatrixInv = whiteningMatrix^-1;

    % here we compute the amplitude of every template...
    % unwhiten all the templates
    tempsUnW = zeros(size(templates));
    for t = 1:size(templates,1)
        tempsUnW(t,:,:) = squeeze(templates(t,:,:))*whiteningMatrixInv;
    end

    % The amplitude on each channel is the positive peak minus the negative
    tempChanAmps = squeeze(max(tempsUnW,[],2))-squeeze(min(tempsUnW,[],2));

    % The template amplitude is the amplitude of its largest channel
    [tempAmpsUnscaled,template_primary] = max(tempChanAmps,[],2);
    %without undoing the whitening
    %template_amplitude = squeeze(max(templates, [], 2) - min(templates, [], 2));
    %[~, template_primary] = max(template_amplitude, [], 2);

    template_primary = cast(template_primary, class(spike_templates));
    spike_primary = template_primary(spike_templates);

    %% Number of samples in the overlap
    n_samples_overlap = round(P.overlap_s * rez.ops.fs);
    n_samples_overlap = cast(n_samples_overlap, class(spike_times));
    %% Distance between each channel
    chan_dist = ((rez.xcoords - rez.xcoords').^2 + (rez.ycoords - rez.ycoords').^2).^0.5;
    n_spikes=numel(spike_times);
    remove_idx = [];
    reference_idx = [];
    % check pairs of spikes in the time-ordered list for being close together in space and time.
    % Check pairs that are separated by N other spikes,
    % starting with N=0. Increasing N until there are no spikes within the temporal overlap window.
    % This means only ever computing a vector operation (i.e. diff(spike_times))
    % rather than a matrix one (i.e. spike_times - spike_times').
    diff_order=0;
    while 1==1
        diff_order=diff_order+1;
        fprintf('Now comparing spikes separated by %g other spikes.\n',diff_order-1);
        isis=spike_times(1+diff_order:end) - spike_times(1:end-diff_order);
        simultaneous = isis<n_samples_overlap;
        if any(isis<0)
            error('ISIs less than zero? Something is wrong because spike times should be sorted.');
        end
        if ~any(simultaneous)
            fprintf('No remaining simultaneous spikes.\n');
            break
        end
        nearby = chan_dist(sub2ind(size(chan_dist),spike_primary(1:end-diff_order),spike_primary(1+diff_order:end)))<P.channel_separation_um;
        first_duplicate = find(simultaneous & nearby); % indexes the first (earliest in time) member of the pair
        n_duplicates = length(first_duplicate);
        if ~isempty(first_duplicate)
            fprintf('On iteration %g, %g duplicate spike pairs were identified.\n',diff_order,n_duplicates);
            amps_to_compare=tempAmpsUnscaled(spike_templates([first_duplicate first_duplicate(:)+diff_order]));
            if length(first_duplicate)==1
                amps_to_compare = amps_to_compare(:)'; % special case requiring a dimension change
            end
            first_is_bigger =  diff(amps_to_compare,[],2)<=0;
            remove_idx = [remove_idx ; spike_idx([first_duplicate(~first_is_bigger);(first_duplicate(first_is_bigger)+diff_order)])];
            reference_idx = [reference_idx ; spike_idx([(first_duplicate(~first_is_bigger)+diff_order);first_duplicate(first_is_bigger)])];
        end
    end
    [remove_idx,idx] = unique(remove_idx);
    reference_idx = reference_idx(idx);
    logical_remove_idx = ismember((1:n_spikes)',remove_idx);
    rez = remove_spikes(rez,logical_remove_idx,'duplicate','reference_time',spike_times(reference_idx),...
        'reference_cluster',spike_templates(reference_idx));
end


%This is an example of using the matlab scripts for drawing MEMS fabrication process cross-section. Only support deposit, lithography, and etch(isotropic and anisotropic is not differentied here.). Materials can be used is Si, SiNi,SiO2, metal, PR(photoresistor).

% basic syntax:
% 1. deposit ('materialName', layerThickness), where materialName is one of Si, SiNi, SiO2, metal, PR. layerThickness is an integer, normally it is between 1 and 20.
% 2. liga(mask), where mask is an series of char combined by ' ' and '-', this function lithography photoresistor, so it should used after an layer of PR is deposited.
% 3. etch('materialName'): material which is appointed will be etched from top to bottom until meet an fully cover other material layer.
% 4. removepr('PR'): remove all the photoresistor.
% 5. drawlayers(); draw current result.
% DATE: 30/8/2007
% muwn.gu@gmail.com
%========donot modify the head
clear all;
clear global;
loadlayerprofile('MEMS');
global layerMatrix;
global Layerprofile;
figure(1);
%===================

%*******************replace below to your own process
mask1 = ' --     ---     --      -  ';	% metal 1
mask2 = '    -----------            ';   % SiO2 beam
mask3 = '  ---------------          ';
mask4 = '                       --- ';  % SiO2
mask5 = '                      ---  ';	% metal 2

deposit ('Si', 10);
deposit('SiO2', 20);
deposit('SiNi', 5);

% electrode
deposit('metal', 5); % metal 1 thick
deposit('PR', 10);
liga(mask1);
etch ('metal');
removepr('PR');
drawlayers();

% beams
deposit('SiO2', 5);
deposit('PR', 10);
liga(mask2);
etch('SiO2');
removepr('PR');
drawlayers();

deposit('PoSi', 10);
deposit('PR', 10);
liga(mask3);
etch('PoSi');
removepr('PR');
drawlayers();
etch('SiO2');

% electrode
deposit('metal', 2); % metal 2 thin
deposit('PR', 10);
liga(mask4);
etch ('metal');
removepr('PR');
drawlayers();
%**********************************************

%=================donot modify below===================
axis ([15 80 0 Layerprofile.currentY])


function test_bug1508

% MEM 2gb
% WALLTIME 00:10:00
% DEPENDENCY ft_freqanalysis

% Stan reported a strange error caused by the following:
% if the cfg.channel in a call to ft_freqanalysis contains channels that
% are not in the data, the function crashes on the second trial

%% Try to reproduce
data = [];
data.trial = {randn(3,100) randn(3,100)};
data.time  = {1:100 1:100};
data.label = {'chan1';'chan2';'chan3'};

cfg = [];
cfg.method  = 'mtmfft';
cfg.channel = {'chan4'};
cfg.output  = 'pow';
cfg.taper   = 'hanning';

try
  freq = ft_freqanalysis(cfg, data);
catch err
  if strcmp(err.message,'no channels were selected')
    % error is expected handling
  else
    error('test doesn''t result in expected error message')
  end
end


function DoTrajK(handles)

global VObj;
global VMag;
global VCtl;
global VSig;
global VCoi;

%preserve VObj VMag & VCoi
VTmpObj=VObj;
VTmpMag=VMag;
VTmpCoi=VCoi;

handles.Simuh=guidata(handles.Simuh.SimuPanel_figure);
DoDisableButton([],[],handles.Simuh);
%% Do K-Space Traj
try
    % Read tab parameters
    fieldname=fieldnames(handles.Attrh2);
    for i=1:length(fieldname)/2
        try
            eval(['TK.' fieldname{i*2} '=[' get(handles.Attrh2.(fieldname{i*2}),'String') '];']);
        catch me
            TAttr=get(handles.Attrh2.(fieldname{i*2}),'String');
            eval(['TK.' fieldname{i*2} '=''' TAttr{get(handles.Attrh2.(fieldname{i*2}),'Value')}  ''';']);
        end
    end

    % Prescan config
    DoPreScan(handles.Simuh);

    % Create SpinWatcher VOtk, VMtk & VCtk
    VOtk=VObj;
    VOtk.SpinNum=1;
    VOtk.TypeNum=1;
    VOtk.Rho=1;
    VOtk.T1=1;
    VOtk.T2=0.1;
    VOtk.T2Star=0.01;
    VOtk.XDim=1;
    VOtk.YDim=1;
    VOtk.ZDim=1;
    VOtk.Mx= 0;
    VOtk.My= 0;
    VOtk.Mz= 1;

    % Gradient Grid
    VMtk=VMag;
    VMtk.Gxgrid=0;
    VMtk.Gygrid=0;
    VMtk.Gzgrid=0;
    VMtk.dB0=0;
    VMtk.dWRnd=0;
    VMtk.FRange=1;

    % Coil
    VCtk=VCoi;
    VCtk.TxCoilmg=1;
    VCtk.TxCoilpe=0;
    VCtk.TxCoilNum=1;
    VCtk.RxCoilx=1;
    VCtk.RxCoily=0;
    VCtk.RxCoilNum=1;

    %% Spin execution
    VObj=VOtk;
    VMag=VMtk;
    VCoi=VCtk;

    % Generate Pulse line
    DoPulseGen(handles);

    % Simulation Process
    try
        VCtl.CS=double(VObj.ChemShift*VCtl.B0);
        VCtl.RunMode=int32(0); % Image scan
        VCtl.MaxThreadNum=int32(handles.Simuh.CPUInfo.NumThreads);
        DoDataTypeConv(handles.Simuh);
        DoScanAtCPU; % global (VSeq,VObj,VCtl,VMag,VCoi,VVar,VSig) are needed
    catch me
        error_msg{1,1}='ERROR!!! Scan process aborted.';
        error_msg{2,1}=me.message;
        errordlg(error_msg);
        %recover VObj
        VObj=VTmpObj;
        VMag=VTmpMag;
        VCoi=VTmpCoi;
        return;
    end
catch me
    error_msg{1,1}='ERROR!!! Spin execution process aborted.';
    error_msg{2,1}=me.message;
    errordlg(error_msg);
    %recover VObj
    VObj=VTmpObj;
    VMag=VTmpMag;
    VCoi=VTmpCoi;
    return;
end

Kx=VSig.Kx;
Ky=VSig.Ky;
Kz=VSig.Kz;

%recover VObj
VObj=VTmpObj;
VMag=VTmpMag;
VCoi=VTmpCoi;

pause(0.1);

switch TK.RenderMode
    case 'VTK'
        % VTK 3D rendering
        switch TK.RenderPoint
            case 'off'
                DoKSpaceTrajVTK([Kx(:)';Ky(:)';Kz(:)'],ones(size(Kx(:)'))*1,0);
            case 'on'
                DoKSpaceTrajVTK([Kx(:)';Ky(:)';Kz(:)'],ones(size(Kx(:)'))*1,1);
        end
    case 'Matlab'
        % Matlab 3D plot
        figure('Color','k');
        switch TK.RenderPoint
            case 'off'
                Ktraj=plot3(Kx(:),Ky(:),Kz(:),'w-');
                grid on;
            case 'on'
                Ktraj=quiver3(Kx(:),Ky(:),Kz(:),[diff(Kx(:));0],[diff(Ky(:));0],[diff(Kz(:));0],'w-');
                set(Ktraj,'AutoScale','off');
        end

                set(gca,'Color','k','xcolor','c','ycolor','c','zcolor','c');
        set(gca,'ydir','reverse');
        xlabel('Kx','Color','w');
        ylabel('Ky','Color','w');
        zlabel('Kz','Color','w');
        title('K-space Traj.','Color','w');
end

end


function prfParams = hrfSet(prfParams,hrfParam,varargin)
% hrfSave - save and update HRF parameters in pRF params structure
%
% prfParams = hrfSave(prfParams,hrfParam,valargin)
%
% 2009/03 SOD: wrote it.

if ~exist('prfParams','var') || isempty(prfParams), error('Need prfParams'); end
if ~exist('hrfParam','var') || isempty(hrfParam),   error('Need hrfParam');  end

nScans = length(prfParams.stim);

% loop over options
switch lower(hrfParam)
    case {'hrftype'}
        val = varargin2val(varargin{1},nScans);
        for n = 1:nScans
            switch lower(val{n})
                case {'one gamma (boynton style)','o','one gamma' 'b' 'boynton'}
                    prfParams.stim(n).hrfType = 'one gamma (Boynton style)';
                case {'two gammas (spm style)' 't' 'two gammas' 'spm'}
                    prfParams.stim(n).hrfType = 'two gammas (SPM style)';
                case {'impulse' 'no hrf' 'none'}
                    prfParams.stim(n).hrfType = 'impulse';
            end
        end

    case {'hrfparams' 'hrfparam'}
        val = varargin2val(varargin{1},nScans);
        for n = 1:nScans
            switch prfParams.stim(n).hrfType
                case {'one gamma (Boynton style)'}
                    prfParams.stim(n).hrfParams{1} = val{n};
                case {'two gammas (SPM style)'}
                    prfParams.stim(n).hrfParams{2} = val{n};

%                 case {'impulse' 'no hrf' 'none'}
%                     prfParams.stim(n).hrfParams{3} = val{n};
                otherwise
            end
        end

    case {'hrf'}
        hrfParams = hrfGet(prfParams,'hrfparams');
        for n = 1:nScans
            % compute hrf
            [tmp tmphrf peak] = rfConvolveTC([1 zeros(1,prfParams.stim(n).nFrames-1)],...
                prfParams.stim(n).framePeriod,...
                prfParams.stim(n).hrfType,...
                hrfParams{n});

            % we need to store the HRF for each scan because they might have
            % different TRs. All other hrf parameters are independent of the TR.
            prfParams.analysis.Hrf{n}    = tmphrf(:);

            % rfConvolveTC normalizes the hrf to the volume of the
            % response. We save the peak amplitude so we can give the output
            % in % BOLD relative to the maximum response as well.
            prfParams.analysis.HrfMaxResponse = peak;
        end;

    otherwise
        fprintf(1,'[%s]:Unknown parameter (%s)',mfilename,hrfParam);
end

return

function val=varargin2val(val,nScans)
if ~iscell(val)
    tmp = val;
    val = cell(nScans,1);
    for n = 1:nScans
        val{n} = tmp;
    end
end
return


function Data = ParamStructData(S)

%FUNCTION:
%   This function takes a data struct, where every field contains a (N x M)
%   matrix of numbers, and store the names of each field, the size of each
%   field, and the total number of elements. This is then later used to
%   pack everything into a struct.
%
%INPUTS:
%   S = the data struct of interest. Each field must contain a matrix of
%       numbers (no strings or other weird data types)
%
%OUTUTS:
%   Data = struct with the added fields
%       Data.Names = a (L x 1) cell array of field names
%       Data.Size = a (L x 2) matrix of the size of these fields
%       Data.Length = a scalar number of elements in S

    Names = fieldnames(S);
    Sizes = zeros(length(Names),2);
    Idx = zeros(length(Names),2);

    for i=1:length(Names)
       Sizes(i,:) = size(S.(Names{i}));
    end

    %Figure out the start and end indicies of each field.
    L = Sizes(:,1).*Sizes(:,2);
    Idx(1,1) = 1;
    for i=2:length(L)
       Idx(i-1,2) = Idx(i-1,1)-1+L(i-1);
       Idx(i,1) = Idx(i-1,2) + 1;
    end
    Idx(end,2) = sum(L);

    %Store the results:
    Data.Names = Names;
    Data.Sizes = Sizes;
    Data.Idx = Idx;

end


function writePOLY_pyramid(filename,V,E,F,H)
  % WRITEPOLY_PYRAMID prints vertices, segments and facets to a .poly suitable
  % for use with PYRAMID
  %
  % writePOLY_pyramid(filename,V,E,F,H)
  %
  % Inputs:
  %   V  #V by dim=3 list of vertex positions
  %   E  #E by 2+boundary_markers list of segment indices, indexing V, and
  %     optional boundary markers
  %   F  #F struct containing polygon information arrays
  %     .facets  a #facets list of facets,  each facet is a polygon
  %       **NOTE: facets index E *not* V, contrary to typical (V,F) meshes and
  %       contrary to writePOLY_tetgen prototype
  %     .boundary_markers a #facets list of boundary_markers
  %     OR
  %   F  #F by constant-degree+boundary_markers  list of facets
  %
  % Example:
  %   % Mesh in (V,F)
  %   % Gather all edges
  %   E = [F(:,[2 3]);F(:,[3 1]);F(:,[1 2])];
  %   [~,IE,IuE] = unique(sort(E,2),'rows');
  %   % unique edges
  %   uE = E(IE,:);
  %   % reindex F into E
  %   FinE = reshape(IuE,size(F));
  %   Facets = [];
  %   Facets.facets = mat2cell(FinE,ones(size(FinE,1),1),[3]);
  %   Facets.boundary_marker = -ones(size(FinE,1),1);
  %   writePOLY_pyramid(path,V,uE,Facets,[]);
  %
  % See also: cdt, tetgen, writePOLY_triangle, writePOLY_tetgen
  %
  %

  % open file for writing
  poly_file_handle = fopen(filename,'w');

  dim = size(V,2);

  if dim ~= 3
    error('writePOLY_pyramid is for 3d meshes. Try writePOLY_triangle etc.');
  end

  % vertices section
  fprintf(poly_file_handle,'# Part 1 - node list\n');
  format = '%d %.17g %.17g %.17g\n';
  fprintf(poly_file_handle,'%d %d 0 0\n', size(V,1),size(V,2));
  if ~isempty(V)
    fprintf(poly_file_handle,format,[1:size(V,1);V']);
  end

  fprintf(poly_file_handle,'# Part 2 - segment list\n');
  fprintf(poly_file_handle,'%d %d\n',size(E,1),size(E,2)-2);
  format = ['%d %d %d' repmat(' %d',1,size(E,2)-2) '\n'];
  fprintf(poly_file_handle,format, [1:size(E,1);E']);

  fprintf(poly_file_handle,'# Part 2 - facet list\n');
  % for now, always include boundary markers
  % [num facets] [boundary markers]
  assert(isempty(F) || isempty(F.facets) || iscell(F.facets));
  if isempty(F)
    fprintf(poly_file_handle,'0\n');
  else
      fprintf(poly_file_handle,'%d %d\n',numel(F.facets),~isempty(F.boundary_marker));
      fs = cell2mat(cellfun(@size,F.facets,'UniformOutput',false));
      % Try to print all at once if facets are all the same size
      if ~isempty(fs) && all(fs(:,1) == 1) && all(fs(:,2) == fs(1,2))
          % build format
          fformat = [ ...
              ... % index and size
              '%d ' num2str(fs(1,2)) ...
              ... % facet indices into segments
              repmat(' %d',1,fs(1,2)) ...
              ... % boundary markers
              repmat(' %d',1,size(F.boundary_marker,2)) '\n'];
          % print all at once
          fprintf(poly_file_handle,fformat, ...
              [1:size(F.facets,1);[cell2mat(F.facets) F.boundary_marker]']);
      else
          % irregular face valences
          for f=1:numel(F.facets)
              % 1d list
              assert(size(F.facets{f},1)==1 || size(F.facets{f},2) == 1);
              fprintf('%d %d',f, numel(F.facets{f}));
              % print indices
              for p=1:numel(F.facets{f})
                  fprintf(poly_file_handle,' %d',F.facets{f}(p));
              end
              fprintf(poly_file_handle,'\n');
          end
      end
  end

  % [num holes]
  fprintf(poly_file_handle,'# Part 3 - hole list\n');
  fprintf(poly_file_handle,'%d\n',size(H,1));
  if ~isempty(H)
    assert(isempty(V) || size(H,2) == size(V,2));
    fprintf(poly_file_handle,'%0.17g %0.17g %0.17g\n',[1:size(H,1);H']);
  end

end


function [Data, para] = LoadWavMask_Simu_CHiME4(para, step, dataset)
nCh = 6;
wavlist_noisy = unique(my_cat('local/wavlist_tr05_simu'));
wavlist_clean = findFiles(para.local.wavroot_clean, 'wav');

for i=1:length(wavlist_clean)   % build an index of clean files so we can find them quickly by utterance ID
    [~,curr_uttID] = fileparts(wavlist_clean{i});
    words = ExtractWordsFromString_v2(curr_uttID, '_');
    clean_struct.(['U_' words{2}]) = wavlist_clean{i};
end

wavlist_noisy = wavlist_noisy(1:step:end);  % choose a portion of the data
if strcmpi(dataset,'train')     % split to training and cross validation data
    wavlist_noisy(50:50:end) = [];
else
    wavlist_noisy = wavlist_noisy(50:50:end);
end

% Load data file list
for i=1:length(wavlist_noisy)
    PrintProgress(i, length(wavlist_noisy), 500);
    % provide the file name of the noisy speech,
    if para.topology.nChMask>1
        ch_idx = randperm(nCh);
        ch_idx(ch_idx==2) = [];     % we don't use channel 2
        ch_idx = sort(ch_idx(1:para.topology.nChMask));
        clear wav wavfile
        for j=1:length(ch_idx)
            wavfile{j} = [para.local.wavroot_noisy '/' wavlist_noisy{i} 'CH' num2str(ch_idx(j)) '.wav'];
            if j==1 || ~para.local.useFileName
                [wav(:,j),fs] = audioread(wavfile{j});
            end
        end
        wav_noisy = wav(:,1);
    else
        wavfile{1} = [para.local.wavroot_noisy '/' wavlist_noisy{i}];
        [wav, fs] = audioread(wavfile{1});
        wav_noisy = wav;
    end

    [~,curr_uttID] = fileparts(wavlist_noisy{i});
    words = ExtractWordsFromString_v2(curr_uttID, '_');
    clean_uttID = words{2};
    wavfile_clean = clean_struct.(['U_' clean_uttID]);
    wav_clean = audioread(wavfile_clean);

    % Compute the mask based on local SNR estimate

    % The clean signal and noisy signal have different gains, so we need to
    % first roughly normalize the absolute power of clean speech in wav_noisy and
    % wav_clean
    noise_power0 = mean(wav_noisy(1:fs*0.2).^2);
    noisy_power0 = mean(wav_noisy.^2);
    clean_power0 = noisy_power0 - noise_power0;
    clean_power_true = mean(wav_clean.^2);
    scale = sqrt(max(0,clean_power0) / clean_power_true);
    scale = max(scale, 1);
    wav_clean = wav_clean*scale;

    [~,spec_clean] = wav2abs(wav_clean,fs);
    [~,spec_noisy] = wav2abs(wav_noisy,fs);
    nFr = min(size(spec_clean,2), size(spec_noisy,2));
    spec_clean = spec_clean(1:257,1:nFr);
    spec_noisy = spec_noisy(1:257,1:nFr);
    spec_noise = spec_noisy-spec_clean;
    power_clean = abs(spec_clean).^2;
    power_noisy = abs(spec_noisy).^2;
    if 0
        power_noise = power_noisy - power_clean;       % use the noisy-clean as noise estimate. This is not stable as sometimes the noisy and clean are not in the same scale
    else
        power_noise = repmat(mean(abs(spec_noisy(:,1:20)).^2,2), 1, nFr);   % use the first 20 frames as noise estimate
    end
    SNR = 10*log10(power_clean ./ power_noise);
    mask{i} = logical(SNR>5);   % 0dB threshold gives us too many speech TF bins. So 5dB is used as threshold. The threshold may not be critical, as we are only using the mask to initialize the mask subnet.
    if mod(i,100)==0
        subplot(5,1,1:2); imagesc(log([abs(spec_clean).^2; abs(spec_noisy).^2; power_noise]));
        subplot(5,1,3); imagesc(SNR);
        subplot(5,1,4); imagesc(mask{i});
        subplot(5,1,5); plot(wav_noisy); hold on; plot(wav_clean,'r'); hold off;
        pause(.01);
    end

    % make sure that the features generated from the wave have the same
    % length as the mask
    nSampleRequired = DecideWavLen4XFrames(nFr, para.topology.frame_len, para.topology.frame_shift);
    if para.local.useFileName
        for j=1:length(wavfile)
            wavfile{j} = sprintf('%s 0 %2.3f', wavfile{j}, nSampleRequired/fs);
        end
        wavInt{i} = wavfile;
    else
        wavInt{i} = StoreWavInt16(wav(1:nSampleRequired,:))';      % note that the input waveform to the network should be a row vector
    end
end

Data(1).data = wavInt;
Data(2).data = mask;
para.IO.context = [1 1];    % context size (for splicing) of data streams
para.IO.sparse = [0 0];     % is the streams stored in sparse format?
para.IO.DataSyncSet{1} = [];    % do we need to synchronize any two streams?
para.IO.frame_rate = [16000 100];   % frame rate of the streams, i.e. how many frames per second.
para.IO.isTensor = [1 1];   % whether we intend to use the input streams as tensor or just matrix.

if para.local.useFileName
    para.IO.inputFeature = [0 1];
    para.IO.fileReader(1).name = 'wavfile';     % define the configurations used for reading the data from files
    para.IO.fileReader(1).multiArrayFiles = 1;
    para.IO.fileReader(1).array = 1;
    para.IO.fileReader(1).fs = 16000;
    para.IO.fileReader(1).precision = 'int16';  % store waveforms in int16 rather than floating points to save space.
    para.IO.fileReader(2).name = '';
else
    para.IO.inputFeature = [1 1];
end
end


function imData=read_file_old(path_to_file,sframe,num2read)

% Reads uncompressed multipage .tiff, .hdf5, or .avi files
% Usage:  my_data=read_file('path_to_data_file, start frame, num to read);

% INPUTS:
% path_to_file:     location of file to be read
% sframe:           first frame to read (optional, default: 1)
% num2read:         number of frames to read (optional, default: read the whole file)

% OUTPUT:
% imData:           data in array format

% Written by Eftychios A. Pnevmatikakis, Simons Foundation

if nargin<2 || isempty(sframe); sframe = 1; end

[~,~,ext] = fileparts(path_to_file);

if strcmpi(ext,'.tiff') || strcmpi(ext,'.tif');

    %get image info
    tiffInfo = imfinfo(path_to_file);
    T = length(tiffInfo);
    if nargin < 3 || isempty(num2read); num2read = T - sframe + 1; end
    num2read = min(num2read,T - sframe + 1);

    Y1 = imread(path_to_file,'Index',sframe,'Info',tiffInfo);
    imData = zeros([size(Y1),num2read],'like',Y1);
    nd = ndims(Y1);
    if nd == 2
        imData(:,:,1) = Y1;
        for t = sframe+1:sframe+num2read-1
            imData(:,:,t-sframe+1) = imread(path_to_file,'Index',t,'Info',tiffInfo);
        end
    elseif nd == 3
        imData(:,:,:,1) = Y1;
        for t = sframe+1:sframe+num2read-1
            imData(:,:,:,t-sframe+1) = imread(path_to_file,'Index',t,'Info',tiffInfo);
        end
    end

elseif strcmpi(ext,'.hdf5') || strcmpi(ext,'.h5');
    info = hdf5info(path_to_file);
    dims = info.GroupHierarchy.Datasets.Dims;
    name = info.GroupHierarchy.Datasets.Name;
    if nargin < 3
        num2read = dims(end)-sframe+1;
    end
    num2read = min(num2read,dims(end)-sframe+1);
    imData = h5read(path_to_file,name,[ones(1,length(dims)-1),sframe],[dims(1:end-1),num2read]);
elseif strcmpi(ext,'.avi')
    v = VideoReader(path_to_file);
    if nargin < 3
        num2read = v.Duration*v.FrameRate-sframe+1;
    end
    Y1 = readFrame(v);
    imData = zeros(v.Height,v.Width,num2read,'like',Y1);
    i = 0;
    while hasFrame(v)
        video = readFrame(v);
        i = i + 1;
        if i >= sframe
            imData(:,:,i-sframe+1) = video;
        end
        if i - sframe + 1 >= num2read
            break;
        end
    end
else
    error('Unknown file extension. Only .tiff, .avi and .hdf5 files are currently supported');
end


function out = spm_run_fmri_data(job)
% Set up the design matrix and run a design
% SPM job execution function
% takes a harvested job data structure and call SPM functions to perform
% computations on the data.
% Input:
% job    - harvested job data structure (see matlabbatch help)
% Output:
% out    - computation results, usually a struct variable.
%__________________________________________________________________________
% Copyright (C) 2005-2019 Wellcome Trust Centre for Neuroimaging

% $Id: spm_run_fmri_data.m 7739 2019-12-02 14:00:18Z guillaume $

original_dir = pwd;
cd(spm_file(job.spmmat{1},'fpath'));

load(fullfile(pwd,'SPM.mat'));

%-Image filenames
%--------------------------------------------------------------------------
SPM.xY.P = char(job.scans);

%-Let SPM configure the design
%--------------------------------------------------------------------------
SPM = spm_fmri_spm_ui(SPM);

if ~isempty(job.mask{1})
    SPM.xM.VM         = spm_data_hdr_read(job.mask{:});
    SPM.xM.xs.Masking = [SPM.xM.xs.Masking, '+explicit mask'];
end

%-Save SPM.mat
%--------------------------------------------------------------------------
fprintf('%-40s: ','Saving SPM configuration')                           %-#
fmt = spm_get_defaults('mat.format');
s = whos('SPM');
if s.bytes > 2147483647, fmt = '-v7.3'; end
save('SPM.mat','SPM', fmt);
fprintf('%30s\n','...SPM.mat saved')                                    %-#

out.spmmat{1} = fullfile(pwd, 'SPM.mat');

cd(original_dir);


function ft_plot_montage(dat, varargin)

% FT_PLOT_MONTAGE makes a montage of a 3-D array by selecting slices at regular distances
% and combining them in one large 2-D image.  Note that the montage of MRI slices is not to
% be confused with the EEG montage, which is a way of specifying the reference scheme
% between electrodes.
%
% Use as
%   ft_plot_montage(dat, ...)
% where dat is a 3-D array.
%
% Additional options should be specified in key-value pairs and can be
%   'transform'     = 4x4 homogeneous transformation matrix specifying the mapping from voxel space to the coordinate system in which the data are plotted.
%   'location'      = 1x3 vector specifying a point on the plane which will be plotted, the coordinates are expressed in the coordinate system in which the data will be plotted. location defines the origin of the plane
%   'orientation'   = 1x3 vector specifying the direction orthogonal through the plane which will be plotted (default = [0 0 1])
%   'srange'        =
%   'slicesize'     =
%   'nslice'        = scalar, number of slices
%   'maskstyle'     = string, 'opacity' or 'colormix', defines the rendering
%   'background'    = needed when maskstyle is 'colormix', 3D-matrix with
%                     the same size as the data matrix, serving as
%                     grayscale image that provides the background
%
% See also FT_PLOT_ORTHO, FT_PLOT_SLICE, FT_SOURCEPLOT

% undocumented, these are passed on to FT_PLOT_SLICE
%   'intersectmesh'  = triangulated mesh through which the intersection of the plane will be plotted (e.g. cortical sheet)
%   'intersectcolor' = color for the intersection

% Copyrights (C) 2012, Jan-Mathijs Schoffelen
%
% 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$

transform       = ft_getopt(varargin, 'transform', eye(4));
loc             = ft_getopt(varargin, 'location');
ori             = ft_getopt(varargin, 'orientation');
srange          = ft_getopt(varargin, 'slicerange');
slicesize       = ft_getopt(varargin, 'slicesize');
nslice          = ft_getopt(varargin, 'nslice');
backgroundcolor = ft_getopt(varargin, 'backgroundcolor', [0 0 0]);

% the intersectmesh and plotmarker options are passed on to FT_PLOT_SLICE
dointersect = ~isempty(ft_getopt(varargin, 'intersectmesh'));
domarker    = ~isempty(ft_getopt(varargin, 'plotmarker'));

% set the location if empty
if isempty(loc) && (isempty(transform) || isequal(transform, eye(4)))
  % go to the middle of the volume if the data seem to be in voxel coordinates
  loc = size(dat)./2;
elseif isempty(loc)
  % otherwise take the origin of the coordinate system
  loc = [0 0 0];
end

% check compatibility of inputs
if size(loc, 1) == 1 && isempty(nslice)
  nslice = 20;
elseif size(loc, 1) == 1 && ~isempty(nslice)
  % this is not a problem, slice spacing will be determined
elseif size(loc, 1) > 1 && isempty(nslice)
  % this is not a problem, number of slices is determined by loc
  nslice = size(loc, 1);
elseif size(loc, 1) > 1 && ~isempty(nslice)
  if size(loc, 1) ~= nslice
    ft_error('you should either specify a set of locations or a single location with a number of slices');
  end
end

% set the orientation if empty
if isempty(ori)
  ori = [0 0 1];
end

% ensure the ori to have unit norm
for k = 1:size(ori,1)
  ori(k,:) = ori(k,:)./norm(ori(k,:));
end

% determine the slice range
if size(loc, 1) == 1 && nslice > 1
  if isempty(srange) || (ischar(srange) && strcmp(srange, 'auto'))
    srange = [-50 70];
  else
  end
  loc = repmat(loc, [nslice 1]) + linspace(srange(1),srange(2),nslice)'*ori;
end

% ensure that the ori has the same size as the loc
if size(ori,1)==1 && size(loc,1)>1
  ori = repmat(ori, size(loc,1), 1);
end

div     = [ceil(sqrt(nslice)) ceil(sqrt(nslice))];
optarg  = varargin;
corners = [inf -inf inf -inf inf -inf]; % get the corners for the axis specification

for k = 1:nslice
  % define 'x' and 'y' axis in projection plane, the definition of x and y is more or less arbitrary
  [x, y] = projplane(ori(k,:)); % z = ori

  % get the transformation matrix to project onto the xy-plane
  T  = [x(:) y(:) ori(k,:)' loc(k,:)'; 0 0 0 1];

  optarg = ft_setopt(optarg, 'location',    loc(k,:));
  optarg = ft_setopt(optarg, 'orientation', ori(k,:));
  ix     = mod(k-1, div(1));
  iy     = floor((k-1)/div(1));
  h(k)   = ft_plot_slice(dat, optarg{:}); % FIXME is it safe to pass all optinoal inputs?

  xtmp = get(h(k), 'xdata');
  ytmp = get(h(k), 'ydata');
  ztmp = get(h(k), 'zdata');
  siz  = size(xtmp);
  if k==1 && isempty(slicesize)
    slicesize = siz;
  end

  % project the positions onto the xy-plane
  pos = [xtmp(:) ytmp(:) ztmp(:)];
  pos = ft_warp_apply(inv(T), pos);

  xtmp = reshape(pos(:,1), siz);
  ytmp = reshape(pos(:,2), siz);
  ztmp = reshape(pos(:,3), siz);

  % add some offset in the x and y directions to create the montage
  offset(1) = iy*(slicesize(1)-1);
  offset(2) = ix*(slicesize(2)-1);

  % update the specification of the corners of the montage plot
  if ~isempty(xtmp)
    c1 = offset(1) + min(xtmp(:));
    c2 = offset(1) + max(xtmp(:));
    c3 = offset(2) + min(ytmp(:));
    c4 = offset(2) + max(ytmp(:));
    c5 = min(ztmp(:));
    c6 = max(ztmp(:));
  end
  corners = [min(corners(1),c1) max(corners(2),c2) min(corners(3),c3) max(corners(4),c4) min(corners(5),c5) max(corners(6),c6)];

  % update the positions
  set(h(k), 'ydata', offset(1) + xtmp);
  set(h(k), 'xdata', offset(2) + ytmp);
  set(h(k), 'zdata',         0 * ztmp);

  if dointersect || domarker
    if ~exist('pprevious', 'var'), pprevious = []; end
    p = setdiff(findobj(gcf, 'type', 'patch'), pprevious);
    for kk = 1:numel(p)
      xtmp = get(p(kk), 'xdata');
      ytmp = get(p(kk), 'ydata');
      ztmp = get(p(kk), 'zdata');
      siz2 = size(xtmp);

      pos = [xtmp(:) ytmp(:) ztmp(:)];
      pos = ft_warp_apply(inv(T), pos);

      xtmp = reshape(pos(:,1), siz2);
      ytmp = reshape(pos(:,2), siz2);
      ztmp = reshape(pos(:,3), siz2);

      % update the positions
      set(p(kk), 'ydata', offset(1) + xtmp);
      set(p(kk), 'xdata', offset(2) + ytmp);
      set(p(kk), 'zdata',         0.0001 * ztmp);
    end
    pprevious = [pprevious(:);p(:)];
  end
  % drawnow; %this statement slows down the process big time on some file
  %systems. I don't know what's going on there, but the statement is not
  %really necessary, so commented out.
end
set(gcf, 'color', backgroundcolor);
set(gca, 'zlim', [0 1]);
%axis equal;
axis off;
view([0 90]);
axis(corners([3 4 1 2]));

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% SUBFUNCTION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [x, y] = projplane(z)
[u, s, v] = svd([eye(3) z(:)]);
x = u(:, 2)';
y = u(:, 3)';


function ans = calc_minDose(doseBinsMidPtsV, volsHistV, volumeType)
% Calculates the minimum dose for a given DVH
% The last argument 'volumeType' is ignored in this instance
%
%  MODIFICATION ALERT:  THIS FUNCTION IS UTILIZED BY THE DREXLER CODEBASE
%
%  Last modified: AJH 11/05
%
%  Usage:  calc_minDose(doseBinsV, volsHistV)
%
% 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/>.

ind = find(volsHistV~=0,1,'first');
ans = doseBinsMidPtsV(ind);

return;


function [H,Rest,activeUEs] = functionExampleSetup_Quadriga(L,Kdrop,B,noiseVariancedBm,Kmax,f,M,polarizations)
%This function generates the channel realizations between UEs at random
%locations and the BSs in the running example, defined in Section 4.1.3.
%BSs with cylindrical arrays and channel are generated using QuaDRiGa from
%the Fraunhofer Heinrich Hertz Institute. The Urban Microcell NLOS scenario
%is used for channel modeling. QuaDRiGa needs to be installed separately
%(http://www.quadriga-channel-model.de) and is delivered with a separate
%license. This function has been tested using QuaDRiGa version 1.4.8-571.
%
%INPUT:
%L                = Number of BSs and cells
%Kdrop            = Number of UEs to be dropped in the square around a BS
%B                = Bandwidth in Hz
%noiseVariancedBm = Noise variance in dBm
%Kmax             = Maximum number of UEs served by a BS
%f                = Pilot reuse factor, giving pilot length Kmax*f
%M                = Number of BS antennas
%polarizations    = Select number of antenna polarizations (1 or 2)
%
%OUTPUT:
%H         = M x 400 x K x L x L matrix with the channel realizations over
%            400 subcarriers at one time instance
%Rest      = M x M x K x L x L matrix with estimates of the spatial
%            correlation matrices for all UEs in the network.
%            Rest(:,:,k,j,l) is the correlation matrix for the channel
%            between UE k in cell j and the BS in cell l.
%activeUEs = Kmax x L with zeros and ones. activeUEs(k,l)==1 means that
%            pilot k is used by a UE in cell l
%
%
%This Matlab function was developed to generate simulation results to:
%
%Emil Bjornson, Jakob Hoydis and Luca Sanguinetti (2017),
%"Massive MIMO Networks: Spectral, Energy, and Hardware Efficiency",
%Foundations and Trends in Signal Processing: Vol. 11, No. 3-4,
%pp. 154-655. DOI: 10.1561/2000000093.
%
%For further information, visit: https://www.massivemimobook.com
%
%This is version 1.0 (Last edited: 2017-11-04)
%
%License: This code is licensed under the GPLv2 license. If you in any way
%use this code for research that results in publications, please cite our
%monograph as described above.

%% Create new Quadriga layout

%Set irrelevant parameters
s = simulation_parameters;
s.sample_density = 1;
s.use_absolute_delays = 1;

%Set center frequency
center_frequency = 2e9;
s.center_frequency = center_frequency;

%Number of subcarriers
nbrOfSubcarriers = 400;

%Create new layout from general parameters
lay = layout(s);

%Generate BSs
lay.no_tx = L;

%Set BS heights
lay.tx_position(3,:) = 25;

%Set the length in meters of the total square area
squareLength = 1000;

%Number of BSs per dimension
nbrBSsPerDim = sqrt(L);

%% Deploy BSs

%Minimum distance between BSs and UEs
minDistance = 35;

%Distance between BSs in vertical/horizontal direction
interSiteDistance = squareLength/nbrBSsPerDim;

%Deploy BSs on the grid
locationsGridHorizontal = repmat(interSiteDistance/2:interSiteDistance:squareLength-interSiteDistance/2,[nbrBSsPerDim 1]);
locationsGridVertical = locationsGridHorizontal';
BSpositions = locationsGridHorizontal(:) + 1i*locationsGridVertical(:);

for j = 1:length(BSpositions)

    lay.tx_position(1:2,j) = [real(BSpositions(j)); imag(BSpositions(j))];

end

%% Create a circular antenna array for each BS
M_V = 5; %Number of vertical antennas
M_H = M/M_V; %Number of antennas on each horizontal circle

%Define the antenna spacing (in number of wavelengths)
antennaSpacing = 1/2; %Half wavelength distance

if polarizations == 1
    PolarizationIndicator = 1; %Single polarization antennas switching between vertical and horizontal
elseif polarizations == 2
    PolarizationIndicator = 3; %Dual +/-45deg polarized antennas
end

%Compute height of array
arrayHeight = (M_V-1)*antennaSpacing*3e8/center_frequency;

if (PolarizationIndicator==1) %Single polarized elements

    circumference = M_H*antennaSpacing*3e8/center_frequency;
    radius = circumference/(2*pi);
    delta_angle = 2*pi/M_H;

    %Go through all BSs
    for b = 1:L

        %Create rectangular array of size M_V x M_H
        lay.tx_array(b).generate('3gpp-3d', 1, M_V, M_H, center_frequency, PolarizationIndicator, 0, antennaSpacing);

        %Place antennas on a circle and rotate radiation patters
        for i = 1:M_V
            for j = 1:M_H
                indices = (i-1)*M_H + j;
                angle = (j-1)*delta_angle;
                lay.tx_array(b).element_position(1, indices) = radius*cos(angle);
                lay.tx_array(b).element_position(2, indices) = radius*sin(angle);
                lay.tx_array(b).element_position(3, indices) = (i-1)*antennaSpacing*3e8/center_frequency - arrayHeight/2;
                lay.tx_array(b).rotate_pattern(rad2deg(angle), 'z', indices, 0);

                if mod(indices,2) == 0 %Switch between vertical and horizontal polarization
                    lay.tx_array(b).rotate_pattern(90, 'y', indices, 2);
                end
            end
        end
    end

elseif (PolarizationIndicator==3) %Dual polarized elements

    circumference = M_H/2*antennaSpacing*3e8/center_frequency;
    radius = circumference/(2*pi);
    delta_angle = 2*pi/(M_H/2);

    %Go through all BSs
    for b = 1:L

        %Create rectangular array of size M_V x M_H/2
        lay.tx_array(b).generate('3gpp-3d', 1, M_V, M_H/2, center_frequency, PolarizationIndicator, 0, antennaSpacing);

        %Place antennas on a circle and rotate radiation patters (while keeping co-located antennas together)
        for i = 1:M_V
            for j = 1:M_H/2
                indices = (i-1)*M_H + 2*j-1 : (i-1)*M_H + 2*j;
                angle = (j-1)*delta_angle;
                lay.tx_array(b).element_position(1, indices) = radius*cos(angle);
                lay.tx_array(b).element_position(2, indices) = radius*sin(angle);
                lay.tx_array(b).element_position(3, indices) = (i-1)*antennaSpacing*3e8/center_frequency - arrayHeight/2;
                lay.tx_array(b).rotate_pattern(rad2deg(angle), 'z', indices, 0);
            end
        end
    end
end

%Compute all nine alternatives of the BS locations when using wrap around
wrapHorizontal = repmat([-squareLength 0 squareLength],[3 1]);
wrapVertical = wrapHorizontal';
wrapLocations = wrapHorizontal(:)' + 1i*wrapVertical(:)';

%Compute the exact dimension of the square where the users are located
maxDistance = interSiteDistance;

%Prepare to put out UEs in the cells
UEpositions = zeros(Kdrop,L);
UEpositionsWrapped = zeros(Kdrop,L,length(wrapLocations));
perBS = zeros(L,1);

%Go through all the cells
for l = 1:L

    %Put out K UEs in the cell, uniformly at random. The procedure is
    %iterative since UEs that do not satisfy the minimum distance are
    %replaced with new UEs
    while perBS(l)<Kdrop

        %Put out users
        UEremaining = Kdrop-perBS(l);
        posX = rand(UEremaining,1)*maxDistance - maxDistance/2;
        posY = rand(UEremaining,1)*maxDistance - maxDistance/2;
        posXY = posX + 1i*posY;

        %Keep those that satisfy the minimum distance
        posXY = posXY(abs(posXY)>=minDistance);

        %Store new UEs
        UEpositions(perBS(l)+1:perBS(l)+length(posXY),l) = posXY + BSpositions(l);
        perBS(l) = perBS(l)+length(posXY);

    end

    %Create alternative UE positions using wrap around
    for k = 1:Kdrop

        UEpositionsWrapped(k,l,:) = UEpositions(k,l) + wrapLocations;

    end

end

%% Configure UEs

Ktotal = Kdrop*L*length(wrapLocations); %Total number of UEs

%Define UE heights
UE_heights = 1.5*ones(Kdrop,L);
UE_heightsWrapped = repmat(UE_heights,[1 1 length(wrapLocations)]);

%Generate UEs
lay.no_rx = Ktotal;

%Define UE antennas
lay.rx_array.generate('omni');

%% Simulate channels

% Randomly distribute UEs
lay.rx_position =  [real(UEpositionsWrapped(:))'; imag(UEpositionsWrapped(:))'; UE_heightsWrapped(:)'];

%Define tracks for each UE, assuming a fixed UE location
for k=1:Ktotal
    lay.track(k).generate('linear',0,0) %Define a linear track consisting of only one position
    lay.track(k).scenario = '3GPP_3D_UMi_NLOS'; %Select the Urban Microcell NLOS scenario
end

%Generate pilot patterns
if f == 1

    pilotPattern = ones(L,1);

elseif f == 2 %Only works for 16 BSs

    pilotPattern = kron(ones(2,1),[1; 2; 1; 2; 2; 1; 2; 1]);

end

%Randomize pilot allocation in each cell
randOrder = zeros(Kmax*f,L);

for j = 1:L

    randOrder(1+(pilotPattern(j)-1)*Kmax:pilotPattern(j)*Kmax,j) = randperm(Kmax)+(pilotPattern(j)-1)*Kmax;

end

%Compute variance and standard deviation of the noise
noiseVar = 10^(noiseVariancedBm/10);
noiseStd = sqrt(noiseVar);

%Prepare to store channel realizations
H = zeros(M,nbrOfSubcarriers,Kmax,L,L);
Rest = zeros(M,M,Kmax,L,L);
perBS = zeros(L,1);
activeUEs = zeros(Kmax,L);

%% Go through all cells
for j = 1:L

    %Output simulation progress
    disp([num2str(j) ' cells generated out of ' num2str(L)]);

    %Go through all UEs
    for k = 1:Kdrop

        Huser = zeros(M,nbrOfSubcarriers,1,1,L);
        Ruser = zeros(M,M,1,1,L);

        %Extract the channels to all BSs
        for l = 1:L

            [~,minr] = min(abs(UEpositionsWrapped(k,j,:)-BSpositions(l)));

            userind = k+(j-1)*Kdrop+(minr-1)*Kdrop*L;

            [ h_channel, ~ ] = lay.get_channels_seg(l, userind);
            Hextract = h_channel.fr(B, nbrOfSubcarriers);

            Huser(:,:,1,1,l) = reshape(Hextract,[M nbrOfSubcarriers])/noiseStd;
            Ruser(:,:,1,1,l) = diag(mean(abs(Huser(:,:,1,1,l)).^2,2)/noiseVar);

        end

        %Determine which BS should serve the UE
        [~,bestBS] = max(mean(sum(abs(Huser(:,:,1,1,:)).^2,1),2));

        %Check if the selected BS has pilots available
        if perBS(bestBS)<Kmax

            %Add the UE to the cell of the selected BS
            perBS(bestBS) = perBS(bestBS) + 1;
            H(:,:,randOrder(perBS(bestBS)+(pilotPattern(bestBS)-1)*Kmax,bestBS),bestBS,:) = Huser;
            Rest(:,:,randOrder(perBS(bestBS)+(pilotPattern(bestBS)-1)*Kmax,bestBS),bestBS,:) = Ruser;
            activeUEs(randOrder(perBS(bestBS)+(pilotPattern(bestBS)-1)*Kmax,bestBS),bestBS) = 1;

        end

    end

end


function varargout = process_scale( varargin )
% PROCESS_SCALE: Sacle values by a constant factor.

% @=============================================================================
% 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

eval(macro_method);
end

%% ===== GET DESCRIPTION =====
function sProcess = GetDescription() %#ok<DEFNU>
    % Description the process
    sProcess.Comment     = 'Scale values';
    sProcess.FileTag     = 'scale';
    sProcess.Category    = 'Filter';
    sProcess.SubGroup    = 'Pre-process';
    sProcess.Index       = 75;
    sProcess.Description = '';
    % Definition of the input accepted by this process
    sProcess.InputTypes  = {'data', 'results', 'timefreq', 'raw', 'matrix'};
    sProcess.OutputTypes = {'data', 'results', 'timefreq', 'raw', 'matrix'};
    sProcess.nInputs     = 1;
    sProcess.nMinFiles   = 1;
    sProcess.isSeparator = 1;
    % Default values for some options
    sProcess.processDim  = 2;    % Process time by time
    % === Factor
    sProcess.options.factor.Comment = 'Multiplication factor: ';
    sProcess.options.factor.Type    = 'value';
    sProcess.options.factor.Value   = {1, '', 4};
    % === Sensor types
    sProcess.options.sensortypes.Comment = 'Sensor types or names (empty=all): ';
    sProcess.options.sensortypes.Type    = 'text';
    sProcess.options.sensortypes.Value   = 'MEG, EEG';
    sProcess.options.sensortypes.InputTypes = {'data', 'raw'};
end

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

%% ===== RUN =====
function sInput = Run(sProcess, sInput) %#ok<DEFNU>
    % Get factor
    factor = sProcess.options.factor.Value{1};
    % Opposite values
    sInput.A = factor * sInput.A;
    % Do not keep the Std field in the output
    if isfield(sInput, 'Std') && ~isempty(sInput.Std)
        sInput.Std = [];
    end
end


function response = Golden(n,p)

persistent mems
if n==1; mems=0; end

if p==0 || p==3 || n==1
    response = 'cooperate';
else
    response = 'defect';
end

if length(mems)>=4
    if isequal( mems(end-3:end), [5 0 5 0] ); response = 'cooperate'; end
end
mems(n)=p;


function roi = dtiRoiFromImg(roiImg, imgXform, bb)
%
% roi = dtiRoiFromImg(roiImg, imgXform, bb)
%
% If not the identity matrix, imgXform is typically set to xformToAcpc. This
% has the effect of forcing the roiImg to have the same voxel size as the
% image that xformToAcpc is based on.
%
% bb is the bounding box, defined in the xformed spaced. Defaults to
% [min(coords)-10; max(coords)+10].
%
% [roiImg, imgXform, bb] = dtiRoiToImg(roi);
% % Do some processing on the ROI
% perimImg = bwperim(roiImg);
% perimRoi = dtiRoiFromImg(roiImg, imgXform, bb);
%
% HISTORY:
% 2010.03.11 RFD wrote it.

[coords(:,1), coords(:,2), coords(:,3)] = ind2sub(size(roiImg), find(roiImg));
coords(:,1) = coords(:,1) + bb(1,1) - 1;
coords(:,2) = coords(:,2) + bb(1,2) - 1;
coords(:,3) = coords(:,3) + bb(1,3) - 1;
coords = mrAnatXformCoords(inv(imgXform), coords);
roi = dtiNewRoi('roiFromImg','g', coords);

return;