classdef ParadigmFBCSP < ParadigmDataflowSimplified
    % Paradigm for complex oscillatory processes using the filter-bank CSP algorithm.
    % Result = para_multiband_csp(Input-Data, Operation-Mode, Options...)
    %
    % Filter-bank CSP [1,2] is a simple extension of the basic CSP method (see ParadigmCSP), in which for
    % each of several time/frequency filters a set of CSP filters is learned, followed by log-variance
    % feature extraction, concatenation of all features (over all chosen spectral filters) and
    % subsequent machine learning. It is not a general replacement for CSP due to the problem of
    % overfitting, but is very useful whenever oscillatory processes in different frequency bands (and
    % with different spatial topographies) are jointly active, and their concerted behavior must be
    % taken into account for a given prediction task. Filter-bank CSP can also be used to capture
    % oscillations in multiple time windows, instead of frequency windows (for example for the detection
    % of complex event-related dynamics).
    %
    % Since the dimensionality of the feature space is larger than in CSP, and since complex
    % interactions may be present, a more complex classifier than the default LDA may be necessary to
    % learn an appropriate model. On the other hand, more flexibility amplifies the risk of overfitting
    % (especially with only little calibration data), so that the performance should always be compared
    % to standard CSP (and Spec-CSP). Another reason is that complex (relevant) interactions between
    % different frequency bands are seemingly rarely observed in practice. The most important
    % user-configurable parameters are the selection regions in time and frequency and the learner
    % component.
    %
    % Typical applications would be those in which either complex event-related oscillatory dynamics
    % happen (for example when reacting to a particular stimulus) and/or where non-trivial interactions
    % between frequency bands (e.g. alpha/theta) are relevant, such as, for example, in workload
    % measurements.
    %
    % Example: Consider a calibration data set in which a subject is maintaining and updating
    % different number of items in his/her working memory at different times, e.g. while performing
    % the n-Back task [2]. Events with types 'n1','n2','n3' indicate challenge stimuli in which the
    % respective number of items is being processed by the person. The goal is to be able to predict
    % the working-memory load of the person following the presentation of such a memory-related
    % challenge. An epoch of 3 seconds relative to each challenge is selected, and three different
    % regions are chosen, two of them over the entire interval, covering the theta and alpha ryhthm,
    % respectively, and one region that is restricted to a window around the time of heaviest
    % cognitive processing. The three regions are specified as a cell array of flt_select
    % parameters.
    %
    %   data = io_loadset('data sets/mary/nback.eeg')
    %   myapproach = {'FBCSP' 'SignalProcessing',{'EpochExtraction',[-0.5 2.5]}, ...
    %       'Prediction', {'FeatureExtraction',{'FreqWindows',[4 6; 7 15; 7 15],'TimeWindows',[-0.5 2.5; -0.5 2.5; 0.25 1.25]}, ...
    %                      'MachineLearning',{'Learner','logreg'}}}
    %   [loss,model,stats] = bci_train('Data',data, 'Approach','ParadigmFBCSP, 'TargetMarkers',{'n1','n2','n3'})
    %
    % References;
    %   [1] Quadrianto Novi, Cuntai Guan, Tran Huy Dat, and Ping Xue, "Sub-band Common Spatial Pattern (SBCSP) for Brain-Computer Interface"
    %       Proceedings of the 3rd International IEEE EMBS Conference on Neural Engineering Kohala Coast, Hawaii, USA, May 2-5, 2007
    %   [2] Kai K. Ang, Zhang Y. Chin, Haihong Zhang, Cuntai Guan, "Filter Bank Common Spatial Pattern (FBCSP) in Brain-Computer Interface"
    %       In 2008 IEEE International Joint Conference on Neural Networks (IEEE World Congress on Computational Intelligence) (June 2008), pp. 2390-2397.
    %   [3] Owen, A. M., McMillan, K. M., Laird,A. R. & Bullmore, E. "N-back working memory paradigm: A meta-analysis of normative functional neuroimaging studies."
    %       Human Brain Mapping, 25, 46-59, 2005
    %
    % Name:
    %   Filter-Bank CSP
    %
    %                           Christian Kothe, Swartz Center for Computational Neuroscience, UCSD
    %                           2010-04-29

    methods

        function defaults = preprocessing_defaults(self)
            % define the default pre-processing parameters of this paradigm
            defaults = {'EpochExtraction',[0.5 3.5],'Resampling',200};
        end

        function model = feature_adapt(self,varargin)
            % adapt a feature representation using the CSP algorithm
            args = arg_define(varargin, ...
                arg_norep('signal'), ...
                arg({'patterns','PatternPairs'},3,uint32([1 1 64 10000]),'CSP patterns per band (times two).','cat','Feature Extraction'), ...
                arg({'shrinkage_cov','ShrinkageCovariance'},false,[],'Shrinkage covariance estimator. Whether to use shrinkage to estimate the covariance matrices.'), ...
                arg({'robust_cov','RobustCovariance'},false,[],'Robust covariance estimation. Whether to use robust cov estimation.'), ...
                arg({'freqwnds','FreqWindows'},[0.5 3; 4 7; 8 12; 13 30; 31 42],[0 0.5 200 1000],'Frequency bands of interest. Matrix containing one row for the start and end of each frequency band from which CSP patterns shall be computed. Values in Hz.','cat','Feature Extraction'), ...
                arg({'timewnds','TimeWindows'},[],[],'Time windows of interest. Matrix containing one row for the start and end of each time window from which CSP patterns shall be computed. Values in seconds. If both this and the freqwnds parameter are non-empty, they should have the same number of rows.','cat','Feature Extraction'), ...
                arg({'winfunc','WindowFunction'},'rect',{'barthann','bartlett','blackman','blackmanharris','bohman','cheb','flattop','gauss','hamming','hann','kaiser','nuttall','parzen','rect','taylor','triang','tukey'},'Type of window function. Typical choices are rect (rectangular), hann, gauss, blackman and kaiser.'),...
                arg({'winparam','WindowParameter','param'},[],[],'Parameter of the window function. This is mandatory for cheb, kaiser and tukey and optional for some others.','shape','scalar'));

            if args.signal.nbchan == 1
                error('Multi-band CSP does intrinsically not support single-channel data (it is a spatial filter).'); end
            if args.signal.nbchan < args.patterns
                error('Multi-band CSP prefers to work on at least as many channels as you request output patterns. Please reduce the number of pattern pairs.'); end
            if ~isempty(args.freqwnds) && ~isempty(args.timewnds) && size(args.freqwnds,1) ~= size(args.timewnds,1)
                error('If both time and frequency windows are specified, both arrays must have the same number of rows (together they define the windows in time and frequency).'); end
            if isempty(args.timewnds)
                args.timewnds = zeros(size(args.freqwnds,1),0); end
            if isempty(args.freqwnds)
                args.freqwnds = zeros(size(args.timewnds,1),0); end

            filters = [];
            patterns = [];
            % for each window
            for w = 1:max(size(args.freqwnds,1),size(args.timewnds,1))
                % pre-parse arguments for flt_window and flt_spectrum (for fast subsequent online use)
                time_args{w} = arg_report('vals',@flt_window,{'time',{args.timewnds(w,:),args.winfunc,args.winparam}});
                freq_args{w} = arg_report('vals',@flt_spectrum,{'freq',args.freqwnds(w,:)});
                for k=1:2
                    % filter trial subrange in time and frequency
                    data = exp_eval_optimized(flt_spectrum('signal',flt_window('signal',set_picktrials(args.signal,'rank',k),time_args{w}),freq_args{w}));
                    if args.robust_cov
                        covar{k} = hlp_diskcache('featuremodels',@cov_blockgeom,reshape(data.data,size(data.data,1),[])',max([data.nbchan*2,data.srate*2,size(data,3)]));
                    else
                        if args.shrinkage_cov
                            covar{k} = hlp_diskcache('featuremodels',@cov_shrink,reshape(data.data,size(data.data,1),[])');
                        else
                            covar{k} = cov(reshape(data.data,size(data.data,1),[])');
                        end
                    end
                    covar{k}(~isfinite(covar{k})) = 0;
                end
                [V,D] = eig(covar{1},covar{1}+covar{2}); %#ok<NASGU>
                P = inv(V);
                filters = [filters V(:,[1:args.patterns end-args.patterns+1:end])];
                patterns = [patterns P([1:args.patterns end-args.patterns+1:end],:)'];
            end
            model = struct('filters',{filters},'patterns',{patterns},'time_args',{time_args},'freq_args',{freq_args},'chanlocs',{args.signal.chanlocs});
        end

        function features = feature_extract(self,signal,featuremodel)
            W = length(featuremodel.freq_args);
            F = size(featuremodel.filters,2);
            T = size(signal.data,3);
            features = zeros(T,F);
            for w = 1:W
                % filter data in time & frequency
                data = exp_eval_optimized(flt_spectrum('signal',flt_window('signal',signal,featuremodel.time_args{w}),featuremodel.freq_args{w}));
                inds = (w-1)*(F/W)+(1:(F/W));
                for t=1:T
                    features(t,inds) = log(var(data.data(:,:,t)' * featuremodel.filters(:,inds))); end
            end
        end

        function visualize_model(self,varargin) %#ok<*INUSD>
            args = arg_define([0 3],varargin, ...
                arg_norep({'myparent','Parent'},[],[],'Parent figure.'), ...
                arg_norep({'featuremodel','FeatureModel'},[],[],'Feature model. This is the part of the model that describes the feature extraction.'), ...
                arg_norep({'predictivemodel','PredictiveModel'},[],[],'Predictive model. This is the part of the model that describes the predictive mapping.'), ...
                arg({'patterns','PlotPatterns'},true,[],'Plot patterns instead of filters. Whether to plot spatial patterns (forward projections) rather than spatial filters.'), ...
                arg({'weight_scaled','WeightScaled'},false,[],'Scaled by weight. Whether to scale the patterns by weight.'));
            arg_toworkspace(args);

            % find the relevant components
            scores = predictivemodel.model.w;
            scores = sqrt(abs(scores));
            % optionally remove the bias if included in w
            if length(scores) == size(featuremodel.patterns,2)+1
                scores = scores(1:end-1); end
            % frequency labels
            % titles = repmat({'delta','theta','alpha','beta','gamma'},8,1); titles = titles(:);
            % extract relevant patterns
            patterns = featuremodel.patterns(:,find(scores)); %#ok<FNDSB>
            filters = featuremodel.filters(:,find(scores)); %#ok<FNDSB>
            % plot them
            if args.weight_scaled
                if args.patterns
                    topoplot_grid(patterns,featuremodel.chanlocs,'scales',scores(find(scores))/max(scores)*1);
                else
                    topoplot_grid(filters,featuremodel.chanlocs,'scales',scores(find(scores))/max(scores)*1);
                end
            else
                if args.patterns
                    topoplot_grid(patterns,featuremodel.chanlocs);
                else
                    topoplot_grid(filters,featuremodel.chanlocs);
                end
            end
            % figure;
        end

        function layout = dialog_layout_defaults(self)
            % define the default configuration dialog layout
            layout = {'SignalProcessing.Resampling.SamplingRate', 'SignalProcessing.EpochExtraction', '', ...
                'Prediction.FeatureExtraction.FreqWindows', 'Prediction.FeatureExtraction.TimeWindows', ...
                'Prediction.FeatureExtraction.WindowFunction', '', 'Prediction.FeatureExtraction.PatternPairs', '', ...
                'Prediction.MachineLearning.Learner'};
        end

        function tf = needs_voting(self)
            tf = true;
        end
    end
end


%%  Copyright (C) 2011-2012 by Jun He, Laura Balzano, and Arthur Szlam
%
%  This file is part of the GRASTA library.
%  It is provided without any warranty of fitness
%  for any purpose. You can redistribute this file
%  and/or modify it under the terms of the GNU
%  Lesser General Public License (LGPL) as published
%  by the Free Software Foundation, either version 3
%  of the License or (at your option) any later version.
%  (see http://www.opensource.org/licenses for more info)

function [ Unew, STATUSnew, OPTSnew ] = grasta_stream( y_Omega, idx, U0, STATUS,OPTIONS, OPTS)
%  grasta_stream is the streaming version of GRASTA which can be used for
%  online data processing
%
%   [1] Jun He, Laura Balzano, and John C.S. Lui. Online Robust Subspace Tracking from Partial Information
%       http://arxiv.org/abs/1109.3827
%   [2] Jun He, Laura Balzano, and Arthur Szlam. Incremental gradient on the grassmannian for online foreground
%       and background separation in subsampled video. In IEEE Conference on Computer Vision and Pattern Recognition
%       (CVPR), June 2012.
%
%  Usage:
%       [ Unew, STATUSnew, OPTSnew ] = grasta_stream( y_Omega, idx, U0, STATUS, OPTIONS, OPTS)
%   Inputs:
%       y_Omega: current partial observation of a full data vector
%       idx:     the observed indices of y_Omega
%       U0:      the current estimated subspace
%       STATUS:  the current status of GRASTA
%           last_mu: \mu_{t-1} for adaptive step rule
%           step_scale: the estimated step_scale constant for adaptive
%       step-size rule
%           lelve: the current level of the "Multi-Level" adaptive rule
%           last_gamma and last_w: previous gradient = last_gamma*last_w'
%
%       OPTIONS: the options of GRASTA
%           CONSTANT_STEP: default 0, will use multi-level step-size rule; > 0 will
%           use constant step-size rule
%           DIM_M : the ambient dimension
%           RANK  : the estimated low-rank of the underlying system
%           rho : ADMM constant step
%           ITER_MAX: the max_iter for ADMM solver
%           TOL : the stopping tolerance of admm_srp / mex_srp
%
%       OPTS:    the current options for the subproblem of GRASTA
%           refer to mex_srp.cpp or admm_srp.m
%   Outputs:
%       Unew:    the updated subspace
%       STATUSnew: the updated running status
%       OPTSnew: the updated options for the subproblem of GRASTA
%
%  Author: Jun He, Laura Balzano
%  Email:  hejun.zz@gmail.com, sunbeam@ece.wisc.edu
%  Date:   Sept. 01, 2012
%
%

LEVEL_FACTOR        = 2;

if isfield(OPTIONS,'MIN_MU'),
    MIN_MU = OPTIONS.MIN_MU;
else
    MIN_MU = 1;
end

if isfield(OPTIONS,'MAX_MU'),
    MAX_MU = OPTIONS.MAX_MU;
else
    MAX_MU = 15;
end

if isfield(OPTIONS,'USE_MEX'),
    USE_MEX = OPTIONS.USE_MEX;
else
    USE_MEX = 1;
end

if isfield(OPTIONS,'CONSTANT_STEP'),
    CONSTANT_STEP = OPTIONS.CONSTANT_STEP;
else
    CONSTANT_STEP = 0;
end

if isfield(OPTIONS,'DIM_M'),
    DIM_M = OPTIONS.DIM_M;
else
    error('Should specify OPTIONS.DIM_M data ambient dimension!!!\n');
end

if isfield(OPTIONS,'ITER_MIN'),
    MIN_ITER = OPTIONS.ITER_MIN;
else
    MIN_ITER = 5;
end

if isfield(OPTIONS,'ITER_MAX'),
    ITER_MAX = OPTIONS.ITER_MAX;
else
    ITER_MAX = 60;
end

if isfield(OPTIONS,'TOL'),
    TOL = OPTIONS.TOL;
else
    TOL = 1e-6;
end

if isfield(OPTIONS,'MAX_LEVEL'),
    MAX_LEVEL = OPTIONS.MAX_LEVEL;
else
    MAX_LEVEL = 20;
end

if isfield(OPTIONS,'RANK'),
    RANK = OPTIONS.RANK;
else
    error('Should specify OPTIONS.RANK!!!\n');
end

if isfield(OPTIONS,'QUIET'),
    QUIET = OPTIONS.QUIET;
else
    QUIET = 0;
end

DEFAULT_MU_HIGH     = (MAX_MU-1)/2;
DEFAULT_MU_LOW      = MIN_MU + 2;

% If we first call GRASTA_stream then do the following initilization
if STATUS.init == 0,
    STATUS.init         = 1; % Do not enter this initial part any longer
    STATUS.curr_iter    = 0; % For debug

    STATUS.last_mu      = MIN_MU;
    STATUS.level        = 0;
    STATUS.step_scale   = 0;
    STATUS.last_w       = zeros(RANK,1);
    STATUS.last_gamma   = zeros(DIM_M,1);

    OPTS.TOL        = TOL;
    OPTS.MAX_ITER   = MIN_ITER;  % the max iteration of ADMM at level=0
    OPTS.QUIET      = 1;

    if isfield(OPTIONS,'rho'),
        OPTS.RHO = OPTIONS.rho;
    else
        OPTS.RHO = 1.8;
    end

    U0 = orth(randn(DIM_M,RANK));
end

%%%%%%%%%%%%%%%
% main framework of GRASTA
U_Omega = U0(idx,:);

if USE_MEX,
    [s_t, w, ldual] = mex_srp(U_Omega, y_Omega, OPTS);
else
    [s_t, w, ldual] = admm_srp(U_Omega, y_Omega, OPTS);
end

gamma_1 = ldual;% + OPTS.RHO*(U_Omega*w + s_t - y_Omega);
UtDual_omega = U_Omega' * gamma_1;
gamma_2 = U0 * UtDual_omega;
gamma = zeros(DIM_M,1);
gamma(idx) = gamma_1;
gamma = gamma - gamma_2;

gamma_norm = norm(gamma);
w_norm     = norm(w);
sG = gamma_norm * w_norm;

% Here we use the adaptive step-size rule for SGD. The adaptive can work
% well for both dynamic and static subspace tracking tasks

% 1. determine the step scale from the first observation
if ~STATUS.step_scale,
    STATUS.step_scale = 0.5*pi*(1+MIN_MU)/sG;

    if ~QUIET,
        fprintf('Level 0: %.2e\n',STATUS.step_scale);
    end
end

% 2. inner product of previous grad and current grad
grad_ip = trace(STATUS.last_w * (STATUS.last_gamma' * gamma) * w');

%%% avoid inner product too large
normalization = norm(STATUS.last_gamma * STATUS.last_w','fro') * norm(gamma * w','fro');
if normalization == 0,
    grad_ip_normalization = 0;
else
    grad_ip_normalization = grad_ip/normalization;
end
%%%

% 3. if the two consecutive grad in the same direction, we take a larger
% step along the gradient direction, otherwise take a small step along the
% gradient direction
STATUS.last_mu = max(STATUS.last_mu + sigmoid(-grad_ip_normalization) , MIN_MU);

if CONSTANT_STEP > 0,
    t = CONSTANT_STEP;
else
    % should not take a step larger than pi/2
    t = STATUS.step_scale * LEVEL_FACTOR^(-STATUS.level) * sG / (1+STATUS.last_mu);
    if t>=pi/3,
        t = pi/3;
    end

    % Adjust the level
    bShrUpd = 0;
    if STATUS.last_mu <= MIN_MU,
        if STATUS.level > 1,
            bShrUpd = 1;
            STATUS.level = STATUS.level - 1;

            if ~QUIET,
                fprintf('multi-level adaption - decreasing, t:%.2e, vectors: %d, level: %d\n',...
                    t,STATUS.curr_iter, STATUS.level);
            end
            STATUS.curr_iter  = 0;
        end

        STATUS.last_mu    = DEFAULT_MU_LOW;
    elseif STATUS.last_mu > MAX_MU,
        if STATUS.level < MAX_LEVEL,
            bShrUpd = 1;
            STATUS.level = STATUS.level + 1;

            if ~QUIET,
                fprintf('multi-level adaption - increasing, t:%.2e, vectors: %d, level: %d\n',...
                    t, STATUS.curr_iter,  STATUS.level);
            end
            STATUS.curr_iter  = 0;
            STATUS.last_mu    = DEFAULT_MU_HIGH;
        else
            STATUS.last_mu    = MAX_MU;
        end
    end

    if bShrUpd,
        if STATUS.level>=0 && STATUS.level <4,          % [0,4)
            OPTS.MAX_ITER = MIN_ITER;
        elseif STATUS.level>=4 && STATUS.level <7,      % [4,7)
            OPTS.MAX_ITER = min(MIN_ITER*2, ITER_MAX);
        elseif STATUS.level>=7 && STATUS.level <10,     % [7,10)
            OPTS.MAX_ITER = min(MIN_ITER*4, ITER_MAX);
        elseif STATUS.level>=10 && STATUS.level <14,    % [10,14)
            OPTS.MAX_ITER = min(MIN_ITER*8, ITER_MAX);
        else
            OPTS.MAX_ITER = ITER_MAX;               % [14,...)
        end

        if ~QUIET,
            fprintf('Will use %d ADMM iterations in level %d\n',OPTS.MAX_ITER, STATUS.level);
        end
    end

end

% 4. update the gradient for further step size update
STATUS.last_gamma  = gamma;
STATUS.last_w      = w;

STATUS.grad_ip = grad_ip_normalization; % just for debugging

% Take the gradient step along Grassmannian geodesic.
alpha = w/w_norm;
beta  = gamma/gamma_norm;
step = (cos(t)-1)*U0*(alpha*alpha')  - sin(t)*beta*alpha';

U0 = U0 + step;

%%

STATUS.s_t   = s_t;
STATUS.w     = w;
STATUS.ldual = ldual;
STATUS.SCALE = 1;
STATUS.curr_iter = STATUS.curr_iter + 1;

STATUS.grasta_t = t;

%
%%%%%%%%%%%%%%%%%%%%%%

Unew = U0;
STATUSnew = STATUS;
OPTSnew = OPTS;
end

%% Function of Sigmoid

function fval = sigmoid(x)
FMIN = -1; FMAX = 1;
omega = 0.1;

fval = FMIN + (FMAX - FMIN)/(1 - (FMAX/FMIN)*exp(-x/omega));
end


function [formula, protons] = getFormulaFromInChI(InChI)
% Extracts the chemical formula of a given compound from
% the InChI string provided
%
% USAGE:
%
%    [formula, protons] = getFormulaFromInChI(InChI)
%
% INPUT:
%    InChI:      The Inchi String of the chemical formula (e.g. InChI=
%                extract formula from `InChI = 1S/C3H4O3/c1-2(4)3(5)6/h1H3, (H,5,6)/p-1` for pyruvate
%
% OUTPUTS:
%    formula:    The chemical formula (including the protonation state
%    protons:    The total number of protons

[token,rem] = strtok(InChI, '/');
formula=strtok(rem, '/');

%This could be a composite formula, so combine it.
tokens = strsplit(formula,'.');

%The protonation state can also modify the formula! To get it, we remove
%any reconstruction fields, as they do not influence it.
InChI = regexprep(InChI,'/r.*','');
p_layer = regexp(InChI,'/p(.*?)/|/p(.*?)$','tokens');
protonationProtons = 0;
if ~isempty(p_layer)
    individualProtons = cellfun(@(x) {strsplit(x{1},';')},p_layer);
    protonationProtons = cellfun(@(x) sum(cellfun(@(y) eval(y) , x)), individualProtons);
end

%Calc the coefs for all formulas
if (numel(tokens) > 1) || (~isempty(regexp(formula,'(^[0-9]+)'))) || (~isempty(p_layer))
    CoefLists = cellfun(@(x) calcFormula(x), tokens,'UniformOutput',0);
    if ~isempty(p_layer)
        %CoefLists = [CoefLists;{{'H';protonationProtons}}];%was crashing
        %with formula C62H90N13O14P.Co.H2O
        CoefLists{end+1} = {'H';protonationProtons};
    end
    %and now, combine them.
    Elements = {};
    Coefficients = [];
    for i = 1:numel(CoefLists)
        if isempty(CoefLists{i})
            %This should only happen, if there was no actual formula.
            continue
        end
        currentForm = CoefLists{i};
        Elements = [Elements,setdiff(currentForm(1,:),Elements)];
        current_coefs = cell2mat(currentForm(2,:));
        [A,B] = ismember(Elements,currentForm(1,:));
        %Extend the coefficients if necessary
        Coefficients(end+1:numel(Elements)) = 0;
        Coefficients(A) = Coefficients(A)+current_coefs;
    end

    Coefs = num2cell(Coefficients);
    Coefs(cellfun(@(x) x == 1, Coefs)) = {[]};
    Coefs = cellfun(@(x) num2str(x) , Coefs,'UniformOutput',0);
    if nargout > 1
        protons = Coefficients(ismember(Elements,'H'));
    end
    formula = strjoin([Elements , {''}],Coefs);
else
    %had to add this for some inchi, e.g.
    %InChI=1/C21H30O4/c1-19-8-5-14(23)11-13(19)3-4-15-16(19)6-9-20(2)17(15)7-10-21(20,25)18(24)12-22/h11,15-17,22,25H,3-10,12H2,1-2H3/t15-,16+,17+,19+,20+,21+/m1/s1
    protons = numAtomsOfElementInFormula(formula, 'H',0);
end

zero='0';
indZero=strfind(formula,zero);
if ~isempty(indZero)
    if isletter(formula(indZero-1))
        if 0
            warning('Formula contains a zero with a letter preceeding it, replacing with letter.')
            fprintf('%s%s\n','Formula before:, ', formula)
        end
        formula = strrep(formula, formula(indZero-1:indZero), formula(indZero-1));
        if 0
            fprintf('%s%s\n','Formula after:, ', formula)
        end
    end
end

end

function [CoefList] = calcFormula(Formula)
multiplier = 1;
isReplicated = regexp(Formula,'(^[0-9]+)','tokens');
ElementTokens = regexp(Formula,'([A-Z][a-z]?)([0-9]*)','tokens');
Elements = cellfun(@(x) x{1}, ElementTokens,'UniformOutput',0);
Coefs = cellfun(@(x) str2num(x{2}), ElementTokens,'UniformOutput',0);
Coefs(cellfun(@isempty, Coefs)) = {1};

if ~isempty(isReplicated)
    multiplier = str2num(isReplicated{1}{1});
    Coefs = cellfun(@(x) x*multiplier, Coefs,'UniformOutput',0);
end

CoefList = [Elements;Coefs];
end


function [returns,interval] = realized_return_filter(price,time,timeType,samplingType,samplingInterval,subsamples)
% THESE COMMENTS ARE WRONG!!!
% Computes the Quantile Realized Variance of Christensen, Oomen and Podolskij (2008),
% the MinRV and MedRV estimator of Andersen, Dobrev and Shaumberg and the
% general symmetrized version suggested by Sheppard in a discussion of COP
%
% USAGE:
%   [RETURN,INTERVAL] =  realized_return_filter(PRICE,TIME,TIMETYPE,SAMPLINGTYPE,SAMPLINGINTERVAL,SUBSAMPLES)
%
% INPUTS:
%   PRICE            - m by 1 vector of high frequency prices
%   TIME             - m by 1 vector of times where TIME(i) corresponds to PRICE(i)
%   TIMETYPE         - String describing the way times are measured
%                       'wall'    24-hour clock of the form HHMMSS, e.g. 101543 or 153217
%                       'seconds' Time measured in seconds past midnight on the first day.
%                       'unit'  Unit normalized date format, e.g. .1, .234, .9
%                         Unit normalized times are more general than the other types and can be
%                         applied to data from more than one calendar day
%   SAMPLINGTYPE     - String describing the type of sampling to use when
%                        filtering PRICE
%                        'CalendarTime' - Sample in calendar time using observations separated by
%                          SAMPLINGINTERVAL seconds
%                        'CalendarUniform' - Sample in calendar time using SAMPLINGINTERVAL
%                          observations spread uniformly between TIME(1) and TIME(m)
%                        'BusinessTime' - Sample in business (tick) time using observation separated
%                          by SAMPLINGINTERVALticks
%                        'BusinessUniform' - Sample in business (tick) time using observations
%                          uniformly spaced in business time.
%                        'Fixed' - Sample at specific points in time. When using fixed,
%                          SAMPLINGINTERVAL must be a n by 1 vector of times with the same TIMETYPE
%                          as TIME (i.e. seconds if TIME is in seconds)
%   SAMPLINGINTERVAL - Scalar integer or n by 1 vector whose meaning depends on the selected SAMPLINGTYPE
%   SUBSAMPLES       - [OPTIONAL] Integer value containing the number of subsamples to use when
%                        computing RRSS. It must be the case that SUBSAMPLE<SAMPLESPERBIN. Setting
%                        SUBSAMPLES>0 will produce an estimator based on jittering the starting
%                        point over floor(linspace(0,(SUBSAMPLES)/(SUBSAMPLES+1),SUBSAMPLES+1)*SAMPLESPERBIN)+1
%                        For example, if SUBSAMPLES = 3 and SAMPLESPERBIN = 20, then the starting
%                        points for the four RR estimators will be observations 1, 6, 11 and 16.  If
%                        omitted SUBSAMPLE = 0 and RRSS=RR.
%
% OUTPUTS:
%   RETURNS          - Quantile realized variance vector (k by 1) corresponding to the values in QUANTILES
%   INTERVAL         - Subsample based version of RQ.  RQSS = RQ if SUBSAMPLES = 0
%
% COMMENTS:
%
% EXAMPLES:
%
%

% Copyright: Kevin Sheppard
% kevin.sheppard@economics.ox.ac.uk
% Revision: 1    Date: 5/1/2008

%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% InputChecking
%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if nargin<5 || nargin>6
    error('Seven or eight inputs required.')
end

if size(price,2)>size(price,1)
    price=price';
end
if size(price,2)>1
    error('PRICE must be a m by 1 vector.')
end
if size(time,2)>size(time,1)
    time=time';
end
if any(diff(time)<0)
    error('TIME must be sorted and increasing')
end
if size(time,2)>1 || length(time)~=length(price)
    error('TIME must be a m by 1 vector.')
end
% Inserted to protect against inputing integer times
time = double(time);

timeType=lower(timeType);
if ~ismember(timeType,{'wall','seconds','unit'})
    error('TIMETYPE must be one of ''wall'', ''seconds'' or ''unit''.');
end
samplingType=lower(samplingType);
if ~ismember(samplingType,{'calendartime','calendaruniform','businesstime','businessuniform','fixed'})
    error('SAMPLINGTYPE must be one of ''CalendarTime'', ''CalendarUniform'', ''BusinessTime'', ''BusinessUniform'' or ''Fixed''.');
end

m=size(price,1);
t0=time(1);
tT=time(m);
if ismember(samplingType,{'calendartime','calendaruniform','businesstime','businessuniform'})
    % Must be a scalar integer
    if ~isscalar(samplingInterval) || floor(samplingInterval)~=samplingInterval || samplingInterval<1
        error('SAMPLINGINTERVAL must be a positive integer for the SAMPLINGTYPE selected.')
    end
else
    if size(samplingInterval,2)>size(samplingInterval,1)
        samplingInterval=samplingInterval';
    end
    if ~(any(samplingInterval>=t0) && any(samplingInterval<=tT))
        error('At least one sampling interval must be between min(TIME) and max(TIME) when using ''Fixed'' as SAMPLINGTYPE.')
    end
    if any(diff(samplingInterval)<=0)
        error('When using ''Fixed'' as SAMPLINGTYPE the vector of sampling times in SAMPLINGINTERVAL must be sorted and strictly increasing.')
    end
end

% SUBSAMPLES
if nargin==6 && ~isempty(subsamples)
    if ~isscalar(subsamples) || subsamples<0 || floor(subsamples)~=subsamples
        error('SUBSAMPLES must be a non-negative integer.')
    end
else
    subsamples=0;
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Input Checking
%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Convert if wall to compute the interval
if strcmp(timeType,'wall')
    time=wall2seconds(time);
    timeType = 'seconds';
end

% Initialize the return cell arrays and the interval vector
returns=cell(subsamples+1,1);
interval=zeros(subsamples+1,1);
% 1. Filter the price
logPrice = log(price);
[filteredLogPrice,filteredTimes] = realized_price_filter(logPrice ,time,timeType,samplingType,samplingInterval);
returns{1} = diff(filteredLogPrice);
interval(1) = 1;

if subsamples>0
    % Initialize the place holder for rvSS and rvAdjustment
    % Get the number of filtered times
    n = size(filteredTimes,1);
    % Compute the length of time used in computing the realized variance to
    % bias adjust the subsample RVs
    baseDifference = filteredTimes(n) - filteredTimes(1);
    switch lower(samplingType)
        case {'calendartime','calendaruniform','fixed'}
            % Uniform sampling in CT so the gap is constant
            gap=diff(filteredTimes);
            % The stepsize is constant
            step = gap /(subsamples+1);
            if ismember(samplingType,{'calendartime','calendaruniform'})
                step = [step;mean(step)];
            else
                step = [step;step(length(step))];
            end
            for i=1:subsamples
                thisSampleTime = filteredTimes;
                % Need to compute times for this subsample
                thisSampleTime = thisSampleTime(1:n)+i*step;
                % Use these times to filter the price
                filteredLogPrice = realized_price_filter(logPrice,time,'unit','fixed',thisSampleTime);
                % Compute returns
                returns{i+1} = diff(filteredLogPrice);
                % Compute the ith subsample RV
                % The adjustment depends on the amount of time used in this
                % subsample relative to the full sample RV
                interval(i+1) = (thisSampleTime(n-1)-thisSampleTime(1))/baseDifference;
            end
        case {'businesstime', 'businessuniform'}
            % Uniform sampling in business time
            if strcmp(samplingType,'businesstime')
                % If BT then samplingInterval contains the number of ticks to skip
                originalIndices = 1:samplingInterval:m;
                if ~ismember(m,originalIndices)
                    originalIndices = [originalIndices m];
                end
                gap=samplingInterval;
            else
                % If businessuniform then gap will be average
                originalIndices = floor(linspace(1,m,samplingInterval));
                if ~ismember(m,originalIndices)
                    originalIndices = [originalIndices m];
                end
                gap = m/samplingInterval;
            end
            % The step size is constant
            step = gap/(subsamples+1);
            for i=1:subsamples
                % The indices indicate which points to use since the
                % sampling is linear in ticks
                indices = floor(originalIndices + step*i);
                indices(indices>m) = m;
                returns{i+1} = diff(logPrice(indices));
                interval(i+1) = (time(max(indices))-time(min(indices)))/baseDifference;
            end
    end
end


function output = fourier2crsspctrm(cfg, freq)

% FOURIER2CRSSPCTRM transforms a fourier-containing freq-structure
% into a crsspctrm-containing freq-structure, in which the
% powerspectra are also contained in the cross-spectra, being a
% channelcombination of a channel with itself.
%
% Use as
%   [freq] = fourier2crsspctrm(cfg, freq)
%
% where you have the following configuration options:
%   cfg.channel    = cell-array with selection of channels,
%                    see CHANNELSELECTION for details
%   cfg.channelcmb = cell-array with selection of combinations between
%                    channels, see CHANNELCOMBINATION for details
%   cfg.keeptrials = 'yes' or 'no' (default)
%   cfg.foilim     = 2-element vector defining your frequency limits of
%                    interest. By default the whole frequency range of the
%                    input is taken.
%

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

if ~isfield(cfg, 'channel'),     cfg.channel     = {'all'};                       end
if ~isfield(cfg, 'channelcmb'),  cfg.channelcmb  = {};                            end
if ~isfield(cfg, 'foilim'),      cfg.foilim      = [freq.freq(1) freq.freq(end)]; end
if ~isfield(cfg, 'keepfourier'), cfg.keepfourier = 'no';                          end
if ~isfield(cfg, 'feedback'),    cfg.feedback    = 'text';                        end

%select the channels on which the power-spectra will be computed
chn = ft_channelselection(cfg.channel,freq.label);
for j = 1:length(chn)
  chnindx(j,1) = find(strcmp(chn(j), freq.label));
  %chnindx(j,1) = find(strcmp(chn{j}, freq.label));
end

%convert the channelcombinations to indices
chncmb  = ft_channelcombination(cfg.channelcmb, freq.label);
cmbindx = zeros(size(chncmb,1),2);
for j = 1:size(chncmb,1)
  cmbindx(j,1) = find(strcmp(chncmb(j,1), freq.label));
  cmbindx(j,2) = find(strcmp(chncmb(j,2), freq.label));
  %cmbindx(j,1) = find(strcmp(chncmb{j,1}, freq.label));
  %cmbindx(j,2) = find(strcmp(chncmb{j,2}, freq.label));
end

%dimensionality of the input data
Nrpt   = length(freq.cumtapcnt);
Nfrq   = size(freq.fourierspctrm,3);
Ntim   = size(freq.fourierspctrm,4);
Nchn   = length(chnindx);
Ncmb   = size(cmbindx,1);

%%FIXME
%if Ntim>1, ft_error('correct handling of time-frequency data is not yet implemented, no information about tapers is available'); end

%keeping track of the tapers
%in the case of tfr fourier-data cumtapcnt is highly redundant; for each frequency
%the number of tapers is equal, as well as for each trial, thus it is sufficient to
%reduce the original cumtapcnt to a vector of Ntrlx1 containing the number of tapers
cumtapcnt = freq.cumtapcnt;
if ~isempty(strfind(freq.dimord, 'time')),
  %cumtapcnt is NtrlxNfrqxNtim; create just one column-vector
  cumtapcnt = ones(size(cumtapcnt,1),1).*unique(cumtapcnt(~isnan(cumtapcnt(:))));
end
sumtapcnt = cumsum([0; cumtapcnt(:)]);

powspctrm = zeros(Nrpt, Nchn, Nfrq, Ntim);
progress('init', cfg.feedback, 'computing single-trial power-spectral densities');
for j = 1:Nrpt
  progress(j/Nrpt, 'trial %d/%d\n', j, Nrpt);
  tmp1     = freq.fourierspctrm([1+sumtapcnt(j):sumtapcnt(j+1)], chnindx, :, :);
  tmp2     = freq.fourierspctrm([1+sumtapcnt(j):sumtapcnt(j+1)], chnindx, :, :);
  powspctrm(j, :, :, :) = squeeze(sum( tmp1 .* conj(tmp2), 1) ./ size(tmp1,1));
end
progress('close');

crsspctrm = complex(zeros(Nrpt, Ncmb, Nfrq, Ntim), zeros(Nrpt, Ncmb, Nfrq, Ntim));
progress('init', cfg.feedback, 'computing single-trial cross-spectral densities');
for j = 1:Nrpt
  progress(j/Nrpt, 'trial %d/%d\n', j, Nrpt);
  tmp1     = freq.fourierspctrm([1+sumtapcnt(j):sumtapcnt(j+1)], cmbindx(:,1), :, :);
  tmp2     = freq.fourierspctrm([1+sumtapcnt(j):sumtapcnt(j+1)], cmbindx(:,2), :, :);
  crsspctrm(j, :, :, :) = squeeze(sum( tmp1 .* conj(tmp2), 1) ./ size(tmp1,1));
end
progress('close');

output.dimord = freq.dimord;
output.freq   = freq.freq;
output.label  = chn;
output.labelcmb(:,1) = freq.label(cmbindx(:,1));
output.labelcmb(:,2) = freq.label(cmbindx(:,2));
output.cumtapcnt = freq.cumtapcnt;
try, output.grad = freq.grad; end
try, output.time = freq.time; end
output.powspctrm = powspctrm;
output.crsspctrm = crsspctrm;
if strcmp(cfg.keepfourier, 'yes'), output.fourierspctrm = freq.fourierspctrm; end

if isempty(output.crsspctrm), output = rmfield(output, 'crsspctrm'); end
if isempty(output.labelcmb ), output = rmfield(output, 'labelcmb' ); end

% add information about the version of this function to the configuration
cfg.version.name = mfilename('fullpath');
cfg.version.id = '$Id$';

% remember the configuration details of the input data
try, cfg.previous = freq.cfg; end

% remember the exact configuration details in the output
output.cfg = cfg;


function parseOut = parser(lexIn)
%STRCONVPARSER    LL(1) parser for mathematical expressions
%  PARSEOUT = STRCONVPARSER(LEXIN) returns a syntax tree of expressions so that
%  it can be converted to a format Chebfun is able to work with. The input,
%  LEXIN, is the output of the method STRCONVLEXER(), and is a cell array of
%  strings, containaining the tokens of strings.
%
%  This method implements a LL(1) parser, a technique from compiler theory. For
%  more details, see e.g.,
%   [1] Aho, Sethi, Ullman, Compilers: Principles, Techniques, and Tools,
%       Addison-Wesley, 1986.
%
% See also: STRINGPARSER, STRINGPARSER/STR2ANON, STRINGPARSER/LEXER.

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

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Developers note: Usually, a parser relies on having access to pointers, which
% is not possible in MATLAB. We get around this issue using global variables.
%
% TODO: Can we now get around this? See #515.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Initialize all global variables
global NEXT
global COUNTER
global LEX
global STACK

% Enter the main routine
parseMain(lexIn);

% Return the stored stack
parseOut = STACK;

% Clear all global variables
NEXT = [];
COUNTER = [];
LEX = [];
STACK = [];

end

function parseMain(lexIn)
%PARSEMAIN  The main parsing routine, starts the recursive parsing.

global NEXT
global STACK
global LEX
global COUNTER

COUNTER = 1;
LEX = lexIn;
NEXT = char(LEX(COUNTER, 2));
STACK = [];

% Our expression can only start with certain labels, make sure we are
% starting with one of them.
validTypes = {'NUM', 'VAR', 'INDVAR', 'PDEVAR', 'LAMBDA',...
            'FUNC1', 'FUNC2', 'FUNC3', 'UN-', 'UN+', 'LPAR'};

if ( any(strcmp(NEXT, validTypes)) )

    % Enter the recursion and check for successful termination.
    parseExpA();

    % We've put a $ at the end of the lexer output. Hence, we have only
    % successfully parsed our string if the only remaining token left is a
    % $ sign.
    success = match('$');

    if ( ~success )
        reportError('Parse:end', ...
            'Input expression ended in unexpected manner.');
    end

else

    reportError('Parse:start', 'Input field started with unaccepted symbol.');

end

end

% The following is the allowed grammar of mathematical expressions in the
% chebgui (¬ denotes an empty symbol):
%
%   ExpA    -> ExpA   COMMA  Exp0
%   ExpA    -> Exp0
%   Exp0    -> Exp0   OP=    Exp05
%   Exp0    -> Exp05
%   Exp05   -> Exp05  OPREL  Exp1       where OPREL can be OP>, OP>=, OP<, OP<=
%   Exp05   -> Exp1
%   Exp1    -> Exp1   OP+    Exp2
%   Exp1    -> Exp1   OP-    Exp2
%   Exp1    -> Exp2
%   Exp2    -> Exp2   OP*    Exp3
%   Exp2    -> Exp2   OP/    Exp3
%   Exp2    -> Exp3
%   Exp3    -> Exp3   OP^    Exp4
%   Exp3    -> Exp4
%   Exp4    ->        UN+    Exp4
%   Exp4    ->        UN-    Exp4
%   Exp4    -> Exp5
%   Exp5    -> NUM
%   Exp5    -> INDVAR
%   Exp5    -> PDEVAR
%   Exp5    -> LAMBDA
%   Exp5    -> VAR
%   Exp5    -> VAR ExpDer
%   Exp5    -> VAR LPAR ExpList RPAR
%   Exp5    -> VAR ExpDer LPAR ExpList RPAR
%   Exp5    -> FUNC1 LPAR Exp1 RPAR
%   Exp5    -> FUNC2 LPAR Exp1 COMMA Exp1 RPAR]
%   Exp5    -> FUNC3 LPAR Exp1 COMMA Exp1 COMMA Exp1 RPAR
%   Exp5    -> LPAR Exp1 RPAR
%   ExpDer  -> DER
%   ExpDer  -> ¬
%   ExpList -> Exp1
%   ExpList -> Exp1 COMMA STR

function parseExpA()

parseExp0();
parseExpApr();

end

function parseExp0()

parseExp05();
parseExp0pr();

end

function parseExp05()

parseExp1();
parseExp05pr();

end

function parseExp1()

parseExp2();
parseExp1pr();

end

function parseExp2()

parseExp3();
parseExp2pr();

end

function parseExp3()

parseExp4();
parseExp3pr();

end

function parseExp4()

parseExp5();

end

function parseExp5()

global NEXT
global COUNTER
global LEX

% We begin by checking whether we have hit a terminal case. In that case,
% we push that into the stack. We need to treat variables with _ in the
% names separately, as we only allow certain operators around the time
% derivative.
if ( strcmp(NEXT, 'VAR') )
    % Create a new tree for later use, corresponding to the dependent
    % variable
    tempLeaf = struct('center', {{char(LEX(COUNTER)), char(NEXT)}}, ...
        'pdeflag', 0);

    % Push the variable onto the stack so that we can take the correct
    % actions if we have derivatives or expressions on the form u(...)
    % involved. If not, we'll simply pop it later.
    push(tempLeaf);

    % Begin by advancing as usual.
    advance();

    % If there follows a derivative symbol, ', we parse that
    parseExpDer();

    % If we then have a u(3) or u(3,left) situation (or u'(3) etc.), we
    % parse that as required
    parseExpList();

elseif ( any(strcmp(NEXT, {'NUM', 'INDVAR', 'LAMBDA', 'STR'})) )
    newLeaf = struct('center', {{char(LEX(COUNTER)), char(NEXT)}}, ...
        'pdeflag', 0);
    push(newLeaf);
    advance();

elseif ( strcmp(NEXT, 'PDEVAR') )
    newLeaf = struct('center', {{char(LEX(COUNTER)), char(NEXT)}}, ...
        'pdeflag', 1);
    push(newLeaf);
    advance();

elseif ( strcmp(NEXT, 'FUNC1') ) % Functions which take one argument
    parseFunction1();

elseif ( strcmp(NEXT, 'FUNC2') ) % Functions which take two arguments
    parseFunction2();

elseif ( strcmp(NEXT, 'FUNC3') ) % Functions which take three arguments
    parseFunction3();

elseif ( strcmp(NEXT, 'LPAR') )
    advance();
    parseExp05();

    % Check if NEXT symbol is ')' as it should be. If not, there is a
    % parenthesis imbalance in the expression and we return an error.
    m = match('RPAR');
    if ( ~m )
        reportError('Parse:parenths', 'Parenthesis imbalance in input fields.')
    end

elseif  ( strcmp(NEXT, 'UN-') || strcmp(NEXT, 'UN+') || ...
          strcmp(NEXT, 'OP-') || strcmp(NEXT,'OP+') )
    % If + or - reaches this far, we have an unary operator.
    % ['UN', char(NEXT(3))] determines whether we have UN+ or UN-.
    newCenterNode = {{char(LEX(COUNTER)), ['UN', char(NEXT(3))]}};
    advance();
    parseExp4();

    rightArg = pop();

    pdeflag = rightArg.pdeflag;

    newTree = struct('center', newCenterNode, 'right', rightArg, ...
        'pdeflag',pdeflag);
    push(newTree);
else

    reportError('Parse:terminal', ...
        ['Unrecognized character in input field:', NEXT]);
end

end

function parseFunction1()

global COUNTER
global LEX

functionName = char(LEX(COUNTER));
advance();

if ( match('LPAR') )
    parseExp1();

    if ( match('COMMA') )
        reportError('Parse:func1', ...
            ['Method ''', functionName, ''' only takes one input argument.']);
    elseif ( ~match('RPAR') )
        reportError('Parse:parenths', 'Parenthesis imbalance in input fields.')
    end

    rightArg =  pop();
    if ( rightArg.pdeflag )
        error('CHEBFUN:STRINGPARSER:parser:PDE', ...
            'Cannot use time derivative as function arguments.')
    end
    % Can assume no pde if we reach here
    newTree = struct('center', {{functionName, 'FUNC1'}}, ...
        'right', rightArg, 'pdeflag', 0);
    push(newTree);
else
    reportError('Parse:parenths', ...
        'Need parenthesis when using functions in input fields.')
end

end

function parseFunction2()

global NEXT
global COUNTER
global LEX

% Function which allow one or two arguments
oneArgAllowed = {'diff', 'cumsum', 'airy', 'mean'};
functionName = char(LEX(COUNTER));
advance();

% Here we need ( as the next symbol
if ( strcmp(NEXT, 'LPAR') )
    advance();
    parseExp1();

    firstArg =  pop();
    if ( firstArg.pdeflag )
        error('CHEBFUN:STRINGPARSER:parser:PDE', ...
            'Cannot use time derivative as function arguments.')
    end

    % Check whether we have a comma, if so, continue as normal
    if ( match('COMMA') )
        parseExp1();
        m = match('RPAR');
        if ( ~m )
            reportError('Parse:parenths', ...
                'Parenthesis imbalance in input fields.')
        end

        secondArg =  pop();
        if ( secondArg.pdeflag )
            error('CHEBFUN:STRINGPARSER:parser:PDE', ...
                'Cannot use time derivative as function arguments.')
        end

        % Can assume no pde if we reach here
        newTree = struct('left', firstArg, ...
            'center', {{functionName, 'FUNC2'}}, ...
            'right', secondArg,'pdeflag',0);
        push(newTree);

    elseif ( match('RPAR') )
        if ( any(strcmp(functionName, oneArgAllowed)) )
            % Have hit a function which allows one or two args.

            % If we only had one argument, we convert the function to type
            % FUNC1.  Can assume no PDE if we reach here
            newTree = struct('center', {{functionName, 'FUNC1'}}, ...
                'right', firstArg, 'pdeflag', 0);
            push(newTree);
        else
            % We tried to call a method which requires two args with only one.
            reportError('Parse:func2', ['Method ''', functionName, ...
                ''' requires two input arguments.']);
        end

    else
        reportError('Parse:parenths', 'Parenthesis imbalance in input fields.')
    end

else
    reportError('Parse:parenths', ...
        'Need parenthesis when using functions in input fields.')
end

end

function parseFunction3()

global NEXT
global COUNTER
global LEX

% Function which allow one or two arguments
oneArgAllowed = {'sum', 'integral'};
twoArgAllowed = {'feval', 'fred', 'volt'};
functionName = char(LEX(COUNTER));
advance();

% Here we need ( as the next symbol
if ( strcmp(NEXT, 'LPAR') )
    advance();
    parseExp1();
    firstArg = pop();
    if ( firstArg.pdeflag )
        error('CHEBFUN:STRINGPARSER:parser:PDE', ...
            'Cannot use time derivative as function arguments.')
    end

    % Check whether we have a comma, if so, continue as normal
    if ( match('COMMA') )
        parseExp1();
        secondArg = pop();
        if ( secondArg.pdeflag )
            error('CHEBFUN:STRINGPARSER:parser:PDE', ...
                'Cannot use time derivative as function arguments.')
        end

        if ( match('COMMA') ) % and again
            parseExp1();
            thirdArg = pop();

            % Define the new branch
            newTree = struct('left', firstArg, ...
                'center', {{functionName, 'FUNC3'}}, ...
                'right', secondArg, 'arg', thirdArg, 'pdeflag', 0);

            if ( ~match('RPAR') ) % Check the final parenthesis
                reportError('Parse:parenths', ...
                    'Parenthesis imbalance in input fields.')
            end
        elseif ( match('RPAR') && any(strcmp(functionName, twoArgAllowed)) )
            % This was actually a two argument function in disguise
            newTree = struct('left', firstArg, ...
                'center', {{functionName, 'FUNC2'}},...
                'right', secondArg, 'pdeflag', 0);
        else
            reportError('Parse:parenths', ...
                'Parenthesis imbalance in input fields.')
        end
        push(newTree);

    elseif ( match('RPAR') )
        if ( any(strcmp(functionName,oneArgAllowed)) )
            % Have hit a function which allows one or two args.

            % If we only had one argument, convert the function to type FUNC1.
            % Can assume no PDE if we reach here.
            newTree = struct('center', {{functionName, 'FUNC1'}}, ...
                'right', firstArg, 'pdeflag', 0);
            push(newTree);
        else
            % We tried to call a method which requires two args with only one.
            reportError('Parse:func2', ['Method ''', functionName, ...
                ''' requires two input arguments.']);
        end

    else
        reportError('Parse:parenths', 'Parenthesis imbalance in input fields.')
    end

else
    reportError('Parse:parenths', 'Need parenthesis when using functions in input fields.')
end

end

function parseExp1pr()

global NEXT

if ( strcmp(NEXT, 'OP+') )

    advance();
    leftArg  = pop();
    parseExp2();
    rightArg = pop();

    pdeflag = leftArg.pdeflag || rightArg.pdeflag;

    newTree = struct('left', leftArg, 'center', {{'+', 'OP+'}}, ...
        'right', rightArg, 'pdeflag', pdeflag);
    push(newTree);
    parseExp1pr();

elseif ( strcmp(NEXT, 'OP-') )
    advance();
    leftArg  = pop();
    parseExp2();
    rightArg = pop();

    pdeflag = leftArg.pdeflag || rightArg.pdeflag;
    newTree = struct('left', leftArg, 'center', {{'-', 'OP-'}}, ...
        'right', rightArg, 'pdeflag', pdeflag);
    push(newTree);
    parseExp1pr();
elseif ( strcmp(NEXT, 'RPAR') || strcmp(NEXT, '$') || strcmp(NEXT, 'OP=') )
	% Do nothing
else % If we don't have ) or the end symbol now something has gone wrong.
%     reportError('Parse:end','Syntax error in input fields.')
end

end

function parseExp2pr()

global NEXT

if ( strcmp(NEXT,'OP*') )
    leftArg  = pop();   % Pop from the stack the left argument
    advance();          % Advance in the input
    parseExp3();
    rightArg = pop();  % Pop from the stack the right argument

    % Check whether we have _ variables
    if ( leftArg.pdeflag || rightArg.pdeflag )
        error('CHEBFUN:STRINGPARSER:parser:PDE','Cannot multiply time derivative')
    end

    % Can assume no PDE if we reach here
    newTree = struct('left', leftArg, 'center', {{'.*', 'OP*'}}, ...
        'right', rightArg, 'pdeflag', 0);
    push(newTree);
    parseExp2pr();

elseif ( strcmp(NEXT,'OP/') )
    leftArg  = pop();   % Pop from the stack the left argument
    advance();          % Advance in the input
    parseExp3();
    rightArg = pop();  % Pop from the stack the right argument

    % Check whether we have _ variables
    if ( leftArg.pdeflag || rightArg.pdeflag )
        error('CHEBFUN:STRINGPARSER:parser:PDE','Cannot divide with time derivatives')
    end

    % Can assume no PDE if we reach here
    newTree = struct('left', leftArg, 'center', {{'./', 'OP/'}}, ...
        'right', rightArg, 'pdeflag', 0);
    push(newTree);
    parseExp2pr();
else
    % Do nothing
end

end

function parseExp3pr()

global NEXT

if ( strcmp(NEXT,'OP^') )
    leftArg  = pop();
    advance();
    parseExp4();
    rightArg = pop();

    % Check whether we have _ variables
    if ( leftArg.pdeflag || rightArg.pdeflag )
        error('CHEBFUN:STRINGPARSER:parser:PDE','Cannot take powers with time derivative')
    end

    % Can assume no pde if we reach here
    newTree = struct('left', leftArg, 'center', {{'.^', 'OP^'}}, ...
        'right', rightArg, 'pdeflag', 0);
    push(newTree);
    parseExp3pr();

elseif ( ~isempty(strfind(NEXT, 'DER')) )
    leftArg  = pop();

    % Check whether we have _ variables
    if ( leftArg.pdeflag )
        error('CHEBFUN:STRINGPARSER:parser:PDE','Cannot differentiate time derivative')
    end

    newTree = struct('center', {{'D', NEXT}}, 'right', leftArg, 'pdeflag', 0);
    push(newTree);
    advance();
    parseExp3pr();
else
    % Do nothing
end

end

function parseExpDer()
% parseExpDer deals with ' denoting derivatives on the dependent variables
% in the problem.

global NEXT

if ( ~isempty(strfind(NEXT, 'DER')) )
    leftArg  = pop();

    % Check whether we have _ variables
    if ( leftArg.pdeflag )
        error('CHEBFUN:STRINGPARSER:parser:PDE','Cannot differentiate time derivative')
    end

    newTree = struct('center', {{'D', NEXT}}, 'right', leftArg, 'pdeflag', 0);
    push(newTree);
    advance();
else
    % Do nothing
end

end

function parseExpList()
% parseExpList deals with potential arguments to the dependent variables in
% the problem, e.g. u(3,left).

global NEXT
global COUNTER
global LEX

if ( match('LPAR') ) % We are in a u(3) or u(3,left) situation
    % The first argument expression can only be of type EXP05 or higher
    % (no = or comma-dividers are allowed)
    parseExp1();
    % Check whether we are in in u(3) or u(3,left) situation. If we
    % have a match with ), we must have a feval with two arguments. If
    % we have a match with a comma, we must have a feval with three
    % arguments, but if there is no match, we have parenthesis
    % imbalance.
    if ( match('RPAR') )
        % Here we only had one argument in u(...), so we create a feval of type
        % FUNC2 (since the feval method has two input arguments)
        secondArg = pop();
        firstArg = pop();

        % Can assume no PDE if we reach here
        newTree = struct('left', firstArg, 'center', {{'feval', 'FUNC2'}}, ...
            'right', secondArg,'pdeflag', 0);
        push(newTree);

    elseif ( match('COMMA') )
        % Here we only had two arguments in u(...), so we create a
        % feval of type FUNC3 (since the feval method has three input
        % arguments)

        % Check whether we have any of the allowed option type as the
        % second argument. If not, throw an error.
        if ( strcmp(NEXT,'STR') )
            % We got a match! But we also need to check for parenthesis
            % balance. Store the option as a temporary leaf
            optLeaf = struct('center', {{char(LEX(COUNTER)), char(NEXT)}}, ...
                'pdeflag', 0);
            % Advance and check for parenth. balance
            advance();
            if ( match('RPAR') )
                % Create a feval of type FUNC3 (since the feval method
                % has three input arguments)
                secondArg = pop();
                firstArg = pop();
                thirdArg = optLeaf;

                % Can assume no pde if we reach here
                newTree = struct('left', firstArg, ...
                    'center', {{'feval', 'FUNC3'}}, 'right', secondArg, ...
                    'arg',thirdArg, 'pdeflag', 0);
                push(newTree);
            else
                reportError('Parse:parenths', ...
                    'Parenthesis imbalance in input fields.')
            end
        else
            reportError('Parse:secondArg', ...
                'Invalid second argument to u(0,...) type of expression.')
        end

    else % There must be a parenthesis imbalance.
        reportError('Parse:parenths', 'Parenthesis imbalance in input fields.')
    end
else % We reach here for the default behaviour, e.g. u' = 1, no parentheses involved
    % Do nothing, since we've already pushed the variable onto the
    % stack.
end

end

function parseExp0pr()

global NEXT

if ( strcmp(NEXT, 'OP=') )

    leftArg  = pop();
    advance();
    parseExp05();

    rightArg = pop();

    pdeflag = leftArg.pdeflag || rightArg.pdeflag;

    newTree = struct('left', leftArg, 'center', {{'=', 'OP='}}, ...
        'right', rightArg, 'pdeflag', pdeflag);
    push(newTree);
else
	% Do nothing
end

end

function parseExpApr()

global NEXT

if ( strcmp(NEXT, 'COMMA') )

    advance();
    leftArg  = pop();
    parseExpA();
    rightArg = pop();

    pdeflag = leftArg.pdeflag || rightArg.pdeflag;

    newTree = struct('left', leftArg, 'center', {{',', 'COMMA'}}, ...
        'right', rightArg, 'pdeflag', pdeflag);
    push(newTree);
else
	% Do nothing
end

end

function parseExp05pr()

global NEXT

if ( any(strcmp(NEXT, {'OP>', 'OP>=', 'OP<', 'OP<='})) )
    tempOpType = NEXT;
    tempOpLabel = tempOpType(3:end);

    advance();
    leftArg  = pop();
    parseExp1();
    rightArg = pop();

    pdeflag = leftArg.pdeflag || rightArg.pdeflag;

    newTree = struct('left', leftArg, 'center', {{tempOpLabel, tempOpType}}, ...
        'right', rightArg, 'pdeflag', pdeflag);
    push(newTree);
else
	% Do nothing
end

end

function advance()
%ADVANCE    Move to the next token in the output from the lexer.
global NEXT
global COUNTER
global LEX

COUNTER = COUNTER + 1;
NEXT = char(LEX(COUNTER,2));

end

function m = match(label)

global NEXT
m = strcmp(label, NEXT);

% If we found a match and are not at the end of output, we want to advance
% to the NEXT symbol
if ( m && ~strcmp(label, '$') )
    advance();
end

end

function push(new)
%PUSH   Push a tree to the stack of syntax trees
global STACK

if ( ~stackRows() )
    STACK = new;
else
    % Ensure number of fields matches
    [STACK, new] = mergeFields(STACK, new);
    % Update the Stack
    STACK = [STACK ; new];
end

end

function p = pop()
%POP    Pop a tree from the stack.
global STACK

% Throw a sensible error if we have an empty stack
if ( isempty(STACK) )
    reportError('Parse:end', 'Syntax error in input expression (Empty stack)');
end

p = STACK(end,:);
STACK(end,:) = [];

end

function m = stackRows()

global STACK
[m, n] = size(STACK); %#ok<NASGU>

end

% TODO:  Can we get rid of this function entirely?
function reportError(id, msg)

error(['CHEBFUN:', id], msg);
% ME = MException(id,msg);
% throw(ME);

end

function [a, b] = mergeFields(a, b)

aFields = fieldnames(a);
bFields = fieldnames(b);

for k = 1:numel(aFields)
    if ( ~isfield(b, aFields{k}) )
        b.(aFields{k}) = [];
    end
end

for k = 1:numel(bFields)
    if ( ~isfield(a, bFields{k}) )
        a.(bFields{k}) = [];
    end
end

end


function tdetx = tdet(x,K)
% tdetx = tdet(x,K)
%
% TDET  Computes twice determinant for Lorentz block
%
% **********  INTERNAL FUNCTION OF SEDUMI **********
%
% See also sedumi

% This file is part of SeDuMi 1.1 by Imre Polik and Oleksandr Romanko
% Copyright (C) 2005 McMaster University, Hamilton, CANADA  (since 1.1)
%
% Copyright (C) 2001 Jos F. Sturm (up to 1.05R5)
%   Dept. Econometrics & O.R., Tilburg University, the Netherlands.
%   Supported by the Netherlands Organization for Scientific Research (NWO).
%
% Affiliation SeDuMi 1.03 and 1.04Beta (2000):
%   Dept. Quantitative Economics, Maastricht University, the Netherlands.
%
% Affiliations up to SeDuMi 1.02 (AUG1998):
%   CRL, McMaster University, Canada.
%   Supported by the Netherlands Organization for Scientific Research (NWO).
%
% This program is free software; you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation; either version 2 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program; if not, write to the Free Software
% Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
% 02110-1301, USA

if isempty(K.q)
    tdetx = zeros(0,1);
else
    ix = K.mainblks;
    tdetx = x(ix(1):ix(2)-1).^2 - ddot(x(ix(2):ix(3)-1),x,K.qblkstart);
end


addpath Codes1D
addpath Codes2D
addpath Codes3D
addpath ServiceRoutines
addpath CFD1D
addpath CFD2D
addpath Grid/
addpath Grid/CFD
addpath Grid/3D
addpath Grid/CNS2D
addpath Grid/Euler2D
addpath Grid/Maxwell2D
addpath Grid/Other

cpt = cputime;

EulerDriver1D
figure; plot(x,Ener); drawnow; pause(.1);

PoissonCDriver1D
figure; plot(x,u); drawnow; pause(.1);

AdvecDriver1D
figure; plot(x,u); drawnow; pause(.1);

BurgersDriver1D
figure; plot(x,u); drawnow; pause(.1);

HeatDriver1D
figure; plot(x,u); drawnow; pause(.1);

MaxwellDriver1D
figure; plot(x,E); drawnow; pause(.1);

CurvedCNSDriver2D
figure; PlotField2D(N, x, y, Q(:,:,4)); drawnow; pause(.1);

CurvedPoissonIPDGDriver2D
figure; PlotField2D(N, x, y, u); drawnow; pause(.1);

CurvedEulerDriver2D
figure; PlotField2D(N, x, y, Q(:,:,4)); drawnow; pause(.1);

MaxwellCurvedDriver2D
figure; PlotField2D(N, x, y, Ez); drawnow; pause(.1);

CurvedINSDriver2D
figure; PlotField2D(N, x, y, PR); drawnow; pause(.1);

MaxwellDriver2D
figure; PlotField2D(N, x, y, Ez); drawnow; pause(.1);

EulerDriver2D
figure; PlotField2D(N, x, y, Q(:,:,4)); drawnow; pause(.1);

MaxwellHNonConDriver2D
figure; PlotField2D(N, x, y, Ez); drawnow; pause(.1);

EulerShockDriver2D
figure; PlotField2D(N, x, y, Q(:,:,4)); drawnow; pause(.1);

PoissonDriver2D
figure; PlotField2D(N, x, y, u); drawnow; pause(.1);

MaxwellPNonConDriver2D

AdvecDriver3D
figure; PlotContour3D(N, u, linspace(0, 1, 5)); drawnow; pause(.1);

MaxwellDriver3D
figure; PlotContour3D(3*N, Ez, linspace(-1, 1, 10)); drawnow; pause(.1);

PoissonIPDGDriver3D
figure; PlotContour3D(3*N, u, linspace(.1, 1, 10)); drawnow; pause(.1);
hold on
PlotSlice3D(2*N, u, 'x', .4);
hold off

alldemostook = cputime-cpt


function [ mSynCat, vMain] = calc_SynCat(nNbkg,vSynCat,nSynCat_,nSynMode, vAfter,vEtas,mCatalog,bPSQ,vPSQ,mPSQ)
    % calculates a synthetic catalog with a backgroundrate based number of earthquakes in the catalog of the period before starting date.
    %
    % Example: [mSynCat, vMain] = calc_SynCat(2500,2.5,2.5,8,100,'January 1,1980','December 31,1990',6.5,1,0,a);
    %
    % This function (calc_SynCat) calculates a synthetic catalog with a
    % backgroundrate based number of earthquakes in the catalog of the period
    % before starting date. This function is doing the following:
    % 1) creates a synthetic catalog with background rate based on input parameters.
    % 2) calculate omori-type aftershock and add this to the background catalog,
    % that was created by the properties of the inputbackground catalog. Therefore
    % the program package ETESProject by karen Felzer was used.
    %
    % Author: van Stiphout, Thomas
    % Email: vanstiphout@sed.ethz.ch
    % Created: 2. Mar 2007
    % Changed: -
    %
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %  Variables
    % Input:
    % nNbkg         Number of events in background catalog during assigned period
    % vSynCat       Vektor with contraints on synthetic catalog
    %                   1-4: fMinLon fMaxLon fMinLat fMaxLat in degree
    %                   5-6: fMinDepth fMaxDepth  in km
    %                   7-8: fMinTime fMaxTime (duration of catalog)
    %                   9:   fRate Reduction of earthquakes for entire catalog
    %                          (0.75 = 25% reduction) in period (fT-fTw) - fT
    %                   10:  fBValue           b-value of the catalog
    %                   11:  fMc Magnitude of Completness (minimum Magnitude in Catalog)
    %                   12:  fIncMagnitude increment steps
    % nSynCat_      1: only backgorund, 2: background + ETAS
    % nSynMode      Type of synthetic catalog 0:homogeneous distr; 1:based
    %                on real catalog 2:hypocenter based on real catalog, magnitude and focal
    %                time is randomly computed
    % vAfter        Definition/Constraints for Aftershocks
    %                   1: Mmin: Minimum earthquake magnitude to report and simulate
    %                   2: MminR: Minimum earthquake magnitude for reporting
    %                       in the output catalog
    %                   3: maxm: Maximum earthquake magnitude
    %                   4: DMax: Maximum distance for aftershocks
    %                   5: magb: Cutoff magnitude for the point source vs. plane source
    %                       representation of the mainshock
    % vEtas         Parameters for ETAS:[CX, P, A] standard=[0.095, 1.34, 0.008]
    % mCatalog      Declustered catalog needed only for nSynMode=1
    % bPSQ          with (1) or without (0) PSQ
    % vPSQ          values for PSQ (lon, lat, N, Tw, T)
    %
    % Output:
    % SynCat Simulated ETES catalog [yr lon lat month day mag depth hrs min sec]
    % vMain            Vector with assignments if main (true) or aftershock (false)
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    % initialize:
    clear FaultParam;

    % background:
    fBValue=vSynCat(10);
    fMc=vSynCat(11);
    fInc=vSynCat(12);
    fMinLat=vSynCat(3);
    fMaxLat=vSynCat(4);
    fMinLon=vSynCat(1);
    fMaxLon=vSynCat(2);
    fMinDepth=vSynCat(5);
    fMaxDepth=vSynCat(6);
    nRchange=vSynCat(9);
    [fYr1, nMn1, nDay1, nHr1, nMin1, nSec1]=decyear2mat(vSynCat(7));
    [fYr2, nMM2, nDD2, nHH2, nMN2, nSS2]=decyear2mat(vSynCat(8));

    % vAfter
    Mmin=vAfter(1);
    MminR=vAfter(2);
    maxm=vAfter(3);
    DMax=vAfter(4);
    magb=vAfter(5);
    FaultParam=[0 0 0 0 0 0 0];

    % vEtas
    CX=vEtas(1);
    P=vEtas(2);
    A=vEtas(3);

    switch nSynCat_
        case 1
            % create background rate
            mSynCat=syn_catalog(nNbkg, fBValue, fMc, fInc, fMinLat,fMaxLat,...
                fMinLon,fMaxLon,fMinDepth,fMaxDepth, fYr1,fYr2,nSynMode,...
                mCatalog);
            if bPSQ
                mSynCat=sr_makePSQ(mSynCat,vPSQ(2),vPSQ(3),vPSQ(4),...
                    vPSQ(1),mPSQ);
            end
            %       mSynCat(:,1)=mSynCat(randperm(size(mSynCat,1))',1);
            vMain=ones(nNbkg,1);
        case 2   % create background rate + ETAS
            nNbkgA=floor(nNbkg/(1+nRchange)*1);
            nNbkgB=ceil(nNbkg/(1+nRchange)*nRchange);
            if nRchange == 1
                mCat=syn_catalog(nNbkg, fBValue, fMc, fInc, fMinLat,fMaxLat,...
                    fMinLon,fMaxLon,fMinDepth,fMaxDepth, fYr1,fYr2,nSynMode,...
                    mCatalog);
            else
                mCatA=syn_catalog(nNbkgA, fBValue, fMc, fInc, fMinLat,fMaxLat,...
                    fMinLon,fMaxLon,fMinDepth,fMaxDepth,fYr1,(fYr1+fYr2)/2,...
                    nSynMode,mCatalog);
                mCatB=syn_catalog(nNbkgB, fBValue, fMc, fInc, fMinLat,fMaxLat,...
                    fMinLon,fMaxLon,fMinDepth,fMaxDepth,(fYr1+fYr2)/2,fYr2,...
                    nSynMode,mCatalog);
                mCat=[mCatA;mCatB];
            end
            %         mCat(:,1)=mCat(randperm(size(mCat,1))',1);
            [Y,Idx]=sort(mCat(:,3),1);
            mCat=mCat(Idx,:);
            if bPSQ
                mCat=sr_makePSQ(mCat,vPSQ(2),vPSQ(3),vPSQ(4),...
                    vPSQ(1),mPSQ);
            end
            mCat1=[fix(mCat(:,3)) mCat(:,4) mCat(:,5) mCat(:,8) mCat(:,9) rand(size(mCat,1),1)*60 mCat(:,2) mCat(:,1) mCat(:,7) mCat(:,6)];
            %         mCat1(:,1)=mCat1(:,1)-(max(mCat1(:,1))-min(mCat1(:,1)));
            %  [N,Mmax,catalog,IDAll,HistoryAll] = ETESProject2(cat1,RateGrid2,0.095,1.34,0.008,Mmin,MminR,maxm,DMax,sYr1,sYr2,6.5,FaultParam)
            [N,Mmax,catalog,IDAll,HistoryAll] = ETESProject3(mCat1,vEtas,vAfter,fYr1,fYr2,FaultParam);

            mSynCat(:,1) = catalog(:,8); % lon
            mSynCat(:,2) = catalog(:,7);  % lat
            mSynCat(:,3) = catalog(:,1);  % years
            mSynCat(:,4) = catalog(:,2);  % month
            mSynCat(:,5) = catalog(:,3);  % day
            mSynCat(:,6) = catalog(:,10); % mag
            mSynCat(:,7) = catalog(:,9);  % depth
            mSynCat(:,8) = catalog(:,4);  % hours
            mSynCat(:,9) = catalog(:,5);  % minutes
            mSynCat(:,10) = catalog(:,6);  % seconds

            mSynCat(:,3) = decyear([mSynCat(:,3) mSynCat(:,4) mSynCat(:,5) ...
                mSynCat(:,8) mSynCat(:,9) mSynCat(:,10)]); % yrs decimal
            size(mSynCat)
            vMain=(catalog(:,12)==0);
    end
    % disp('cat fertig')
end


% This subroutine assigns creates a grid with
% spacing dx,dy (in degreees). The size will
% be selected interactiVELY. The bvalue in each
% volume around a grid point containing ni earthquakes
% will be calculated as well as the magnitude
% of completness
%   Stefan Wiemer 1/95

display ('This is /src/bdepth_ratio_auto.m');

global no1 bo1 inb1 inb2

if sel == 'in'
    % get the grid parameter
    % initial values
    %
    dx = 0.1;
    dy = 0.1 ;
    ni = 1000;
    Nmin = 50;
    stan2 = nan;
    stan = nan;
    prf = nan;
    av = nan;
    mid_point = 5;
    top_zonet = 0;
    top_zoneb = 5;
    bot_zonet = 7;
    bot_zoneb = 15;
    topstan2 = nan;
    botstan2 = nan;
    topstan = nan;
    botstan = nan;
    topav = nan;
    botav = nan;

    % make the interface
    %
    figure_w_normalized_uicontrolunits(...
        'Name','Depth Ratio Grid Input Parameter',...
        'NumberTitle','off', ...
        'MenuBar','none', ...
        'NextPlot','new', ...
        'units','points',...
        'Visible','off', ...
        'Position',[ wex+200 wey-200 650 300]);
    axis off
    labelList2=[' Automatic Mcomp (max curvature) | Fixed Mc (Mc = Mmin) | Automatic Mcomp (90% probability) | Automatic Mcomp (95% probability) | Best (?) combination (Mc95 - Mc90 - max curvature)'];

    labelPos = [0.2 0.60 0.6  0.08];
    hndl2=uicontrol(...
        'Style','popup',...
        'Position',labelPos,...
        'Units','normalized',...
        'String',labelList2,...
        'Callback','inb2 =get(hndl2,''Value''); ');

    % set(hndl2,'value',5);

    % creates a dialog box to input grid parameters
    %
    %

    % mid_point_field=uicontrol('Style','edit',...
    %     'Position',[.47 .80 .12 .08],...
    %     'Units','normalized','String',num2str(mid_point),...
    %     'Callback','mid_point=str2double(get(mid_point_field,''String'')); set(mid_point_field,''String'',num2str(mid_point));');

    top_zonet_field=uicontrol('Style','edit',...
        'Position',[.36 .80 .06 .06],...
        'Units','normalized','String',num2str(top_zonet),...
        'Callback','top_zonet=str2double(get(top_zonet_field,''String'')); set(top_zonet_field,''String'',num2str(top_zonet));');
    top_zoneb_field=uicontrol('Style','edit',...
        'Position',[.36 .74 .06 .06],...
        'Units','normalized','String',num2str(top_zoneb),...
        'Callback','top_zoneb=str2double(get(top_zoneb_field,''String'')); set(top_zoneb_field,''String'',num2str(top_zoneb));');

    bot_zonet_field=uicontrol('Style','edit',...
        'Position',[.78 .80 .06 .06],...
        'Units','normalized','String',num2str(bot_zonet),...
        'Callback','bot_zonet=str2double(get(bot_zonet_field,''String'')); set(bot_zonet_field,''String'',num2str(bot_zonet));');
    bot_zoneb_field=uicontrol('Style','edit',...
        'Position',[.78 .74 .06 .06],...
        'Units','normalized','String',num2str(bot_zoneb),...
        'Callback','bot_zoneb=str2double(get(bot_zoneb_field,''String'')); set(bot_zoneb_field,''String'',num2str(bot_zoneb));');

    freq_field=uicontrol('Style','edit',...
        'Position',[.30 .50 .12 .08],...
        'Units','normalized','String',num2str(ni),...
        'Callback','ni=str2double(get(freq_field,''String'')); set(freq_field,''String'',num2str(ni));set(tgl2,''value'',0); set(tgl1,''value'',1)');

    freq_field0=uicontrol('Style','edit',...
        'Position',[.70 .50 .12 .08],...
        'Units','normalized','String',num2str(ra),...
        'Callback','ra=str2double(get(freq_field0,''String'')); set(freq_field0,''String'',num2str(ra)) ; set(tgl2,''value'',1); set(tgl1,''value'',0)');

    freq_field2=uicontrol('Style','edit',...
        'Position',[.30 .40 .12 .08],...
        'Units','normalized','String',num2str(dx),...
        'Callback','dx=str2double(get(freq_field2,''String'')); set(freq_field2,''String'',num2str(dx));');

    freq_field3=uicontrol('Style','edit',...
        'Position',[.30 .30 .12 .08],...
        'Units','normalized','String',num2str(dy),...
        'Callback','dy=str2double(get(freq_field3,''String'')); set(freq_field3,''String'',num2str(dy));');

    tgl1 = uicontrol('Style','checkbox',...
        'string','Number of Events:',...
        'Position',[.09 .50 .2 .08], 'Callback','set(tgl2,''value'',0)',...
        'Units','normalized');

    set(tgl1,'value',1);

    tgl2 =  uicontrol('Style','checkbox',...
        'string','OR: Constant Radius',...
        'Position',[.47 .50 .2 .08], 'Callback','set(tgl1,''value'',0)',...
        'Units','normalized');

    freq_field4=uicontrol('Style','edit',...
        'Position',[.30 .20 .12 .08],...
        'Units','normalized','String',num2str(Nmin),...
        'Callback','Nmin=str2double(get(freq_field4,''String'')); % set(freq_field4,''String'',num2str(Nmin));');

    close_button=uicontrol('Style','Pushbutton',...
        'Position',[.60 .05 .15 .12 ],...
        'Units','normalized','Callback','close;done','String','Cancel');

    go_button1=uicontrol('Style','Pushbutton',...
        'Position',[.20 .05 .15 .12 ],...
        'Units','normalized',...
        'Callback',' inb1 =get(hndl2,''Value'');tgl1 =get(tgl1,''Value'');tgl2 =get(tgl2,''Value'');close,sel =''ca'', bdepth_ratio',...
        'String','Go');
    Nmin;
    text(...
        'Color',[0 0 0 ],...
        'EraseMode','normal',...
        'Position',[0.20 .75 0 ],...
        'Rotation',0 ,...
        'FontSize',ZmapGlobal.Data.fontsz.m ,...
        'FontWeight','bold',...
        'String','Please choose an Mc estimation option   ');

    mid_txt=text(...
        'Color',[0 0 0 ],...
        'EraseMode','normal',...
        'Position',[0.24 1.0 0 ],...
        'Rotation',0 ,...
        'FontSize',ZmapGlobal.Data.fontsz.m ,...
        'FontWeight','bold',...
        'String','Depth limits for depth ratio calculation  ');

    top_txt=text(...
        'Color',[0 0 0 ],...
        'EraseMode','normal',...
        'Position',[-0.10 .85 0 ],...
        'Rotation',0 ,...
        'FontSize',ZmapGlobal.Data.fontsz.m ,...
        'FontWeight','bold',...
        'String','Top and bottom for TOP zone(km):');

    bot_txt=text(...
        'Color',[0 0 0 ],...
        'EraseMode','normal',...
        'Position',[0.40 .85 0 ],...
        'Rotation',0 ,...
        'FontSize',ZmapGlobal.Data.fontsz.m ,...
        'FontWeight','bold',...
        'String','Top and bottom for BOTTOM zone(km):');

    txt3 = text(...
        'Color',[0 0 0 ],...
        'EraseMode','normal',...
        'Position',[0.30 0.64 0 ],...
        'Rotation',0 ,...
        'FontSize',ZmapGlobal.Data.fontsz.m ,...
        'FontWeight','bold',...
        'String',' Grid Parameter');
    txt5 = text(...
        'Color',[0 0 0 ],...
        'EraseMode','normal',...
        'Position',[-0.06 0.40 0 ],...
        'Rotation',0 ,...
        'FontSize',ZmapGlobal.Data.fontsz.m ,...
        'FontWeight','bold',...
        'String','Spacing in x (dx) in deg:');

    txt6 = text(...
        'Color',[0 0 0 ],...
        'EraseMode','normal',...
        'Position',[-0.06 0.29 0 ],...
        'Rotation',0 ,...
        'FontSize',ZmapGlobal.Data.fontsz.m ,...
        'FontWeight','bold',...
        'String','Spacing in y (dy) in deg:');

    txt7 = text(...
        'Color',[0 0 0 ],...
        'EraseMode','normal',...
        'Position',[-0.06 0.17 0 ],...
        'Rotation',0 ,...
        'FontSize',ZmapGlobal.Data.fontsz.m ,...
        'FontWeight','bold',...
        'String','Min. No. of events > Mc:');

    set(gcf,'visible','on');
    watchoff

end   % if nargin ==0

% get the grid-size interactively and
% calculate the b-value in the grid by sorting
% thge seimicity and selectiong the ni neighbors
% to each grid point

if sel == 'ca'
    selgp
    itotal = length(newgri(:,1));
    if length(gx) < 4  ||  length(gy) < 4
        errordlg('Selection too small! (Dx and Dy are in degreees! ');
        return
    end

    zmap_message_center.set_info(' ','Running bdepth_ratio... ');think
    %  make grid, calculate start- endtime etc.  ...
    %
    t0b = min(a.Date)  ;
    n = a.Count;
    teb = a(n,3) ;
    tdiff = round((teb - t0b)*365/par1);
    loc = zeros(3, length(gx)*length(gy));

    % loop over  all points
    %
    i2 = 0.;
    i1 = 0.;
    bvg = [];
    allcount = 0.;
    nobv = 0;
    wai = waitbar(0,' Please Wait ...  ');
    set(wai,'NumberTitle','off','Name','b-value grid - percent done');;
    drawnow

    % sort by depth

    % [s,is] = sort(a.Depth);
    % adepth = a(is(:,1),:);

    % find row index of ratio midpoint
    l = a.Depth >= top_zonet & a.Depth <  top_zoneb;
    top_zone = a.subset(l);

    l = a.Depth >= bot_zonet & a.Depth <  bot_zoneb;
    bot_zone = a.subset(l);

    %
    % overall b-value
    [bv magco stan av me mer me2,  pr] =  bvalca3(top_zone,inb1,inb2);
    tbo1 = bv; tno1 = length(top_zone(:,1));

    [bv magco stan av me mer me2,  pr] =  bvalca3(bot_zone,inb1,inb2);
    bbo1 = bv; bno1 = length(bot_zone(:,1));

    depth_ratio = tbo1/bbo1;

    disp(depth_ratio);

    % loop over all points
    for i= 1:length(newgri(:,1))
        x = newgri(i,1);y = newgri(i,2);
        allcount = allcount + 1.;
        i2 = i2+1;

        % calculate distance from center point and sort wrt distance
        l = sqrt(((a.Longitude-x)*cos(pi/180*y)*111).^2 + ((a.Latitude-y)*111).^2) ;
        [s,is] = sort(l);
        b = a(is(:,1),:) ;       % re-orders matrix to agree row-wise

        if tgl1 == 0   % take point within r
            l3 = l <= ra;
            b = a.subset(l3);      % new data per grid point (b) is sorted in distanc  (from center point)
            rd = ra;
        else
            % take first ni points
            b = b(1:ni,:);      % new data per grid point (b) is sorted in distance
            l2 = sort(l); rd = l2(ni);

        end

        %estimate the completeness and b-value
        newt2 = b;

        % sort by depth

        l = b(:,7) >= top_zonet & b(:,7) <  top_zoneb;
        topb = b(l,:);

        l = b(:,7) >= bot_zonet & b(:,7) <  bot_zoneb;
        botb = b(l,:);

       if length(topb) >= Nmin  && length(botb) >= Nmin

            if inb1 == 3
                newt2 = topb;
                mcperc_ca3;
                l = topb(:,6) >= Mc90-0.05; magco = Mc90;
                n1 = length(topb(l,:));
                if length(topb(l,:)) >= Nmin
                    [topbv magco stan av me mer me2,  pr] =  bvalca3(topb(l,:),2,2);
                    [topav2 topbv2 topstan2 ] =  bmemag(topb(l,:));
                else  topbv = nan; topbv2 = nan, magco = nan; av = nan; topav2 = nan;
                    nobv = nobv + 1;
                end
                newt2 = botb;
                mcperc_ca3;
                l = botb(:,6) >= Mc90-0.05; magco = Mc90;
                n2 = length(botb(l,:));
                if length(botb(l,:)) >= Nmin
                    [botbv magco stan av me mer me2,  pr] =  bvalca3(botb(l,:),2,2);
                    [botav2 botbv2 botstan2 ] =  bmemag(botb(l,:));

                else  botbv = nan; botbv2 = nan; magco = nan; av = nan; botav2 = nan;
                    nobv = nobv + 1;
                end

            elseif inb1 == 4
                newt2 = topb;
                mcperc_ca3;
                l = topb(:,6) >= Mc95-0.05; magco = Mc95;
                n1 = length(topb(l,:));

                if length(topb(l,:)) >= Nmin
                    [topbv magco topstan topav topme topmer topme2,  toppr] =  bvalca3(topb(l,:),2,2);
                    [topav2 topbv2 topstan2 ] =  bmemag(topb(l,:));
                else
                    topbv = nan; topbv2 = nan, magco = nan; topav = nan; topav2 = nan;
                    nobv = nobv + 1;
                end
                newt2 = botb;
                mcperc_ca3;
                l = botb(:,6) >= Mc90-0.05; magco = Mc95;
                n2 = length(botb(l,:));

                if length(botb(l,:)) >= Nmin
                    [botbv magco botstan botav botme botmer botme2,  botpr] =  bvalca3(botb(l,:),2,2);
                    [botav2 botbv2 botstan2 ] =  bmemag(botb(l,:));
                else
                    botbv = nan; botbv2 = nan, magco = nan; botav = nan; botav2 = nan;
                    nobv = nobv + 1;
                end

            elseif inb1 == 5
                newt2 = topb;
                mcperc_ca3;
                if isnan(Mc95) == 0
                    magco = Mc95;
                elseif isnan(Mc90) == 0
                    magco = Mc90;
                else
                    [bv magco stan av me mer me2,  pr] =  bvalca3(b,1,1);
                end

                l = topb(:,6) >= magco-0.05;
                n1 = length(topb(l,:));
                if length(topb(l,:)) >= Nmin
                    [topbv magco topstan topav topme topmer topme2,  toppr] =  bvalca3(topb(l,:),2,2);
                    [topav2 topbv2 topstan2 ] =  bmemag(topb(l,:));
                else
                    topbv = nan; topbv2 = nan, magco = nan; topav = nan; topav2 = nan;
                    nobv = nobv + 1;
                end

                newt2 = botb;
                mcperc_ca3;
                l = botb(:,6) >= magco-0.05;
                n2 = length(botb(l,:));

                if length(botb(l,:)) >= Nmin
                    [botbv magco botstan botav botme botmer botme2,  botpr] =  bvalca3(botb(l,:),2,2);
                    [botav2 botbv2 botstan2 ] =  bmemag(botb(l,:));
                else
                    botbv = nan; botbv2 = nan, magco = nan; botav = nan; bottopav2 = nan;
                    nobv = nobv + 1;
                end

            elseif inb1 == 1
                % newt2 = topb;
                % mcperc_ca3;
                [topbv magco topstan topav topme topmer topme2,  toppr] =  bvalca3(topb,1,1);
                l = topb(:,6) >= magco-0.05;
                if length(topb(l,:)) >= Nmin
                    [topav2 topbv2 topstan2 ] =  bmemag(topb(l,:));
                    n1 = length(topb(l,:));
                else
                    topbv = nan; topbv2 = nan; magco = nan; topav = nan; topav2 = nan;
                    n1 = nan;

                    nobv = nobv + 1;
                end
                %    newt2 = botb;
                %    mcperc_ca3;
                [botbv magco botstan botav botme botmer botme2,  botpr] =  bvalca3(botb,1,1);
                l = botb(:,6) >= magco-0.05;
                if length(botb(l,:)) >= Nmin
                    [botav2 botbv2 botstan2 ] =  bmemag(botb(l,:));
                    n2 = length(botb(l,:));

                else
                    botbv = nan; botbv2 = nan; magco = nan; botav = nan; botav2 = nan;
                    n2 = nan;
                    nobv = nobv + 1;
                end

            elseif inb1 == 2
                [topbv magco topstan topav topme topmer topme2,  toppr] =  bvalca3(topb,2,2);
                n1 = length(topb);
                [topav2 topbv2 topstan2 ] =  bmemag(topb);

                [botbv magco botstan botav botme botmer botme2,  botpr] =  bvalca3(botb,2,2);

                n2 = length(botb);
                [botav2 botbv2 botstan2 ] =  bmemag(botb);

            end

        else
            topbv = nan; topbv2 = nan; magco = nan; topav = nan; topav2 = nan;
            botbv = nan; botbv2 = nan; magco = nan; botav = nan; botav2 = nan;
            nobv = nobv + 1;

        end

        bv = topbv/botbv; bv2 = topbv2/botbv2; stan2 = topstan2/botstan2; av = topav/botav;
        stan = topstan/botstan;
        n = n1+n2; bo1 = topbv; b2 = botbv;
        da = -2*n*log(n) + 2*n1*log(n1+n2*bo1/b2) + 2*n2*log(n1*b2/bo1+n2) - 2;
        pr = (1  -  exp(-da/2-2))*100;

        bvg = [bvg ; bv magco x y rd bv2 av pr topbv botbv];
        waitbar(allcount/itotal)

    end

    snobv = num2str(nobv);
    spnobv = num2str((nobv/allcount)*100.0);
    disp(['Not enough EQs to calculate b values for ', snobv, ' gridnodes. This is ',spnobv,'% of the total'])

    save tmpncal.mat
    quit

    % for newgr
    %save cnssgrid.mat
    %quit
    % save data
    %
    catSave3 =...
        [ 'zmap_message_center.set_info(''Save Grid'',''  '');think;',...
        '[file1,path1] = uiputfile(fullfile(hodi, ''eq_data'', ''*.mat''), ''Grid Datafile Name?'') ;',...
        ' sapa2 = [''save '' path1 file1 '' bvg gx gy dx dy par1 tdiff t0b teb a main faults mainfault coastline yvect xvect tmpgri ll depth_ratio top_zonet top_zoneb bot_zoneb bot_zonet''];',...
        ' if length(file1) > 1, eval(sapa2),end , done']; eval(catSave3)

    close(wai)
    watchoff

    % plot the results
    % old and re3 (initially ) is the b-value matrix
    %
    normlap2=ones(length(tmpgri(:,1)),1)*nan;
    normlap2(ll)= bvg(:,1);
    re3=reshape(normlap2,length(yvect),length(xvect));

    normlap2(ll)= bvg(:,5);
    r=reshape(normlap2,length(yvect),length(xvect));

    normlap2(ll)= bvg(:,6);
    meg=reshape(normlap2,length(yvect),length(xvect));

    normlap2(ll)= bvg(:,2);
    old1=reshape(normlap2,length(yvect),length(xvect));

    %  normlap2(ll)= bvg(:,7);
    %  pro=reshape(normlap2,length(yvect),length(xvect));

    normlap2(ll)= bvg(:,7);
    avm=reshape(normlap2,length(yvect),length(xvect));

    %  normlap2(ll)= bvg(:,9);
    %  stanm=reshape(normlap2,length(yvect),length(xvect));

    normlap2(ll)= bvg(:,8);
    Prmap=reshape(normlap2,length(yvect),length(xvect));

    normlap2(ll)= bvg(:,9);
    top_b=reshape(normlap2,length(yvect),length(xvect));

    normlap2(ll)= bvg(:,10);
    bottom_b=reshape(normlap2,length(yvect),length(xvect));

    old = re3;

    % View the b-value map
    view_bdepth

end   % if sel = na

% Load exist b-grid
if sel == 'lo'
    [file1,path1] = uigetfile(['*.mat'],'b-value gridfile');
    if length(path1) > 1
        think
        load([path1 file1])
        normlap2=ones(length(tmpgri(:,1)),1)*nan;

        normlap2(ll)= bvg(:,1);
        re3=reshape(normlap2,length(yvect),length(xvect));

        normlap2(ll)= bvg(:,5);
        r=reshape(normlap2,length(yvect),length(xvect));

        normlap2(ll)= bvg(:,6);
        meg=reshape(normlap2,length(yvect),length(xvect));

        normlap2(ll)= bvg(:,2);
        old1=reshape(normlap2,length(yvect),length(xvect));

        %  normlap2(ll)= bvg(:,7);
        %  pro=reshape(normlap2,length(yvect),length(xvect));

        normlap2(ll)= bvg(:,7);
        avm=reshape(normlap2,length(yvect),length(xvect));

        %  normlap2(ll)= bvg(:,9);
        % stanm=reshape(normlap2,length(yvect),length(xvect));

        normlap2(ll)= bvg(:,8);
        Prmap=reshape(normlap2,length(yvect),length(xvect));

        normlap2(ll)= bvg(:,9);
        top_b=reshape(normlap2,length(yvect),length(xvect));

        normlap2(ll)= bvg(:,10);
        bottom_b=reshape(normlap2,length(yvect),length(xvect));

        old = re3;

        view_bdepth
    else
        return
    end
end