classdef ParadigmMKLFBCSP < ParadigmBase
    % Multiple Kernel Learning Filter-Bank Common Spatial Patterns (mklFBCSP)
    %
    % This paradigm implements a generalization of mklCSP [1] to multiple frequency bands as in
    % FBCSP [2] and to multiple time windows. See also ParadigmFBCSP and ParadigmMKLCSP.
    %
    % References:
    % [1] Samek, W., Binder, A., & Muller, K. R.
    %     "Multiple kernel learning for brain-computer interfacing."
    %     In Engineering in Medicine and Biology Society (EMBC) pp. 7048-7051 (2013)
    % [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.
    %
    % Name:
    %   Multiple Kernel Learning Filter-Bank Common Spatial Patterns
    %
    %                            Christian Kothe, Swartz Center for Computational Neuroscience, UCSD
    %                            2014-02-05

    methods

        function defaults = preprocessing_defaults(self)
            defaults = {'EpochExtraction',[0.5 3.5],'Resampling',200};
        end

        function defaults = machine_learning_defaults(self)
            % set up the default parameters for machine learning
            defaults = {'logreg', 'Variant','lars'};
        end

        function model = calibrate(self,varargin)
            % calibrate an mklCSP model from a corpus of training sets
            args = arg_define(varargin, ...
                arg_norep({'collection','Collection'}), ...
                arg_norep({'goal_identifier','GoalIdentifier'}), ...
                arg({'patterns','PatternPairs'},3,uint32([1 1 64 10000]),'Number of CSP patterns (times two).','cat','Feature Extraction','type','expression','shape','row'),...
                arg({'shrinkage','ShrinkageLevel'},0,[0 1],'Shrinkage level. The amount of shrinkage (regularization) to apply during covariance 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'), ...
                arg({'verbose','Verbose'},true,[],'Verbose output.'), ...
                arg({'hotpatching','HotPatching'},false,[],'Hot-patch the data. This can be enabled to ensure that a long-running computation survives bad data.'), ...
                arg_sub({'flt','SignalProcessing'}, self.preprocessing_defaults(), @flt_pipeline, 'Signal processing stages. These parameters control filter stages that run on the signal level; they can be enabled, disabled and configured for the given paradigm. The prediction operates on the outputs of this stage.'), ...
                arg_sub({'ml','MachineLearning'},{'Learner',self.machine_learning_defaults()},@ml_train,'Machine learning stage of the paradigm. Operates on the feature vectors that are produced by the feature-extraction stage.'),...
                arg({'arg_dialogsel','ConfigLayout'},self.dialog_layout_defaults(),[],'Parameters displayed in the config dialog. Cell array of parameter names to display (dot-notation allowed); blanks are translated into empty rows in the dialog. Referring to a structure argument lists all parameters of that struture, except if it is a switchable structure - in this case, a pulldown menu with switch options is displayed.','type','cellstr','shape','row'));

            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

            % pre-parse arguments for flt_window and flt_spectrum (for fast subsequent online use)
            for w = 1:max(size(args.freqwnds,1),size(args.timewnds,1))
                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,:)});
            end

            if args.verbose
                fprintf('Now training model for: %s...\n',hlp_tostring(args.goal_identifier)); end

            % first solve CSP for each subject in the corpus individually and aggregate CSP filters
            filters = [];
            patterns = [];

            % find the unique subjects in the collection
            try
                corpus = [args.collection{:}];
            catch e
                error('The dataset collection must have the same field names for each recording.');
            end
            if ~isfield(corpus,'subject')
                error('The datasets in the collection must each have a .subject field.'); end
            subjects = {corpus.subject};
            if all(cellfun('isclass',subjects,'char'))
                subjects = unique(subjects);
            elseif all(cellfun('isclass',subjects,'double'))
                subjects = unique([subjects{:}]);
            else
                error('The subject identifiers must either be all strings or all doubles');
            end

            if args.verbose
                fprintf('Pre-processing each of %i recordings (%i subjects) in the corpus and solving CSP...\n',length(args.collection),length(subjects)); end

            % remove actual data from corpus so we can micro-cache it
            for s=1:length(corpus)
                if isfield(corpus(s).streams{1},'tracking')
                    corpus(s).streams{1} = corpus(s).streams{1}.tracking.expression; end
            end

            % for each subject...
            for subj=subjects
                % find all recordings that match that subject
                recordings = corpus(cellfun(@(s)isequal(s,subj),{corpus.subject}));
                % calculate FBCSP filters
                [newfilters,newpatterns,chanlocs] = hlp_microcache('filters',@ParadigmMKLFBCSP.filters_for_subject,recordings, args.flt, time_args, freq_args, args.shrinkage, args.patterns);
                % if you get an error here then your data sets had varying number of channels
                filters = [filters newfilters];
                patterns = [patterns newpatterns];
            end
            model.featuremodel = struct('filters',{filters},'patterns',{patterns}, ...
                'n_subjects',length(subjects),'time_args',{time_args},'freq_args',{freq_args}, ...
                'chanlocs',{chanlocs}, 'hotpatching', {args.hotpatching});
            if args.verbose
                fprintf('Preprocessing and extracting features for reference data...\n'); end
            % get the data of the reference subject
            [reference,remaining] = utl_collection_closest(args.collection,args.goal_identifier); %#ok<ASGLU,NASGU>
            % preprocess each recording in the reference collection and concatenate them across epochs into a single set
            for r=1:length(reference)
                refsets{r} = exp_eval_optimized(flt_pipeline('signal',reference{r}.streams{1}, args.flt)); end
            refdata = exp_eval(set_joinepos(refsets{:}));
            % extract features and get target labels
            features = self.feature_extract(refdata,model.featuremodel);
            targets = set_gettarget(refdata);

            if args.hotpatching && size(features,1) < 5
                fprintf('You have too few trials in the data; hot-patching.\n');
                features = features(1+mod(0:4,size(features,1)),:);
                features = features+0.1*randn(size(features));
                targets = 1+mod(0:size(features,1)-1,2);
                targets = targets(:);
            end

            if args.hotpatching && length(targets) ~= size(features,1)
                fprintf('Your # of target markers does not match the # of extracted features; hot-patching.\n');
                if isempty(targets)
                    targets = 1+mod(0:size(features,1)-1,2);
                else
                    targets = targets(1+mod(0:size(features,1)-1,length(targets)));
                end
                targets = targets(:);
            end

            if args.hotpatching && length(unique(targets))==1
                fprintf('Your reference data has only one class; hot-patching the data.\n');
                for ii=1:min(length(targets),max(2,round(length(targets)/10)))
                    targets(ii) = 3-targets(ii); end
            end

            if args.hotpatching && any(~isfinite(features(:)))
                fprintf('Some of your features are non-finite; hot-patching the data.\n');
                tofix = find(~isfinite(features(:)));
                features(tofix) = randn(1,length(tofix));
            end

            if args.verbose
                fprintf('Training predictive model (this may take a while)...\n'); end
            % train classifier, overriding with the correct feature shape (based on the group size)
            if isfield(args.ml.learner,'shape')
                args.ml.learner.shape = [2*args.patterns,length(subjects)]; end
            try
                model.predictivemodel = ml_train('data',{features,targets}, args.ml);
            catch e
                if args.hotpatching && ~isempty(strfind(e.message,'Null probability for class'))
                    fprintf('One of the classes has a probability of 0; hot-patching the data.\n');
                    targets = 1+mod(0:length(targets)-1,2); targets = targets(:);
                    model.predictivemodel = ml_train('data',{features,targets}, args.ml);
                else
                    rethrow(e);
                end
            end
            % set the filter graph based on the reference data
            model.tracking.filter_graph = refsets{end};
            % also store channel locations for model visualization
            model.chanlocs = refdata.chanlocs;
        end

        function predictions = predict(self,bundle,model)
            % extract features
            features = self.feature_extract(bundle.streams{1},model.featuremodel);
            % apply classifier
            predictions = ml_predict(features, model.predictivemodel);
        end

        function features = feature_extract(self,signal,featuremodel)
            try
                N = featuremodel.n_subjects;
                W = length(featuremodel.freq_args);
                F = size(featuremodel.filters,2)/N;
                T = size(signal.data,3);
                features = zeros(T,F*N);
                for w = 1:W
                    % filter data in time & frequency
                    data = exp_eval(flt_spectrum('signal',flt_window('signal',signal,featuremodel.time_args{w}),featuremodel.freq_args{w}));
                    mask = false(1,F); mask((w-1)*(F/W)+(1:(F/W))) = true; mask = repmat(mask,1,N);
                    for t=1:size(signal.data,3)
                        features(t,mask) = sum((data.data(:,:,t)'*featuremodel.filters(:,mask)).^2,1); end
                end
                features = log(features/size(signal.data,2));
            catch e
                if featuremodel.hotpatching
                    fprintf('Trying to prevent error during feature extraction: %s\n',hlp_handleerror(e));
                    fprintf('size(featuremodel.filters): %s\n',hlp_tostring(size(featuremodel.filters)));
                    fprintf('size(signal.data): %s\n',hlp_tostring(size(signal.data)));
                    fprintf('trying to hot-fix the issue...');
                    featuremodel.filters = featuremodel.filters(1+mod(0:size(signal.data,1)-1,size(featuremodel.filters,1)),:);
                    features = self.feature_extract(signal,featuremodel);
                    fprintf('succeeded.\n');
                else
                    rethrow(e);
                end
            end
        end

        function visualize(self,varargin) %#ok<*INUSD>
            % visualize an mklCSP model
            args = arg_define(varargin, ...
                arg_norep({'model','Model'},[],[],'BCI Model to visualize.'), ...
                arg({'patterns','PlotPatterns'},true,[],'Plot patterns instead of filters. Whether to plot spatial patterns (forward projections) rather than spatial filters.'), ...
                arg({'paper','PaperFigure'},false,[],'Use paper-style font sizes. Whether to generate a plot with font sizes etc. adjusted for paper.'));

            f = figure;

            % mask out unused filters
            mask = args.model.predictivemodel.model.w(:)' ~= 0;
            args.model.featuremodel.patterns = args.model.featuremodel.patterns(:,mask);
            args.model.featuremodel.filters = args.model.featuremodel.filters(:,mask);

            % number of plots, and index of pattern per subplot
            np = nnz(mask);
            horz = ceil(sqrt(np));
            vert = ceil(np/horz);

            % get number of pairs, and index of pattern per subplot
            % for each CSP pattern...
            for p=1:np
                subplot(horz,vert,p,'Parent',f);
                if args.patterns
                    topoplot(args.model.featuremodel.patterns(:,p),args.model.chanlocs);
                else
                    topoplot(args.model.featuremodel.filters(:,p),args.model.chanlocs);
                end
                t = title(['CSP Pattern ' num2str(p)]);
                if args.paper
                    set(t,'FontUnits','normalized');
                    set(t,'FontSize',0.1);
                end
            end
        end

        function layout = dialog_layout_defaults(self)
            % define the default configuration dialog layout
            layout = {'SignalProcessing.Resampling.SamplingRate', 'SignalProcessing.FIRFilter.Frequencies', ...
                'SignalProcessing.FIRFilter.Type', 'SignalProcessing.EpochExtraction', '', ...
                'PatternPairs', '', 'MachineLearning.Learner'};
        end

    end

    methods (Static)
        function [filters, patterns, chanlocs] = filters_for_subject(recordings, flt, time_args, freq_args, shrinkage, n_patterns)
            % get the CSP filters for a given subject

            % preprocess and concatenate across trials
            preproc = {};
            for r=1:length(recordings)
                preproc{r} = exp_eval_optimized(flt_pipeline('signal',recordings(r).streams{1}, flt)); end
            preproc = exp_eval(set_joinepos(preproc{:}));

            % for each window...
            filters = [];
            patterns = [];
            for w = 1:length(time_args)
                for k=1:2
                    % filter trial subrange in time and frequency
                    classdata = exp_eval(flt_spectrum('signal',flt_window('signal',set_picktrials(preproc,'rank',k),time_args{w}),freq_args{w}));
                    covar{k} = reshape(classdata.data,size(classdata.data,1),[])*reshape(classdata.data,size(classdata.data,1),[])'/(size(classdata.data,2)*size(classdata.data,3)); % cov(reshape(classdata.data,size(classdata.data,1),[])');
                    covar{k}(~isfinite(covar{k})) = 0; %#ok<*AGROW>
                    covar{k} = (1-shrinkage)*covar{k} + shrinkage*eye(size(covar{k}))*trace(covar{k})/length(covar{k});
                end
                try
                    [V,D] = eig(covar{1},covar{1}+covar{2}); %#ok<ASGLU,NASGU>
                    P = inv(V);
                    % if you get an error here then your data sets had varying number of channels
                    filters = [filters V(:,[1:n_patterns end-n_patterns+1:end])];
                    patterns = [patterns P([1:n_patterns end-n_patterns+1:end],:)'];
                catch e
                    fprintf('Got a degenerate FBCSP solution, replacing by identity matrix:%s\n',e.message);
                    n_chans = preproc.nbchan;
                    if ~n_chans
                        % no epochs, need to determine the number of channels in the filter stage prior to epoching
                        raw = exp_eval_optimized(utl_get_argument(utl_find_filter(preproc,'set_makepos'),'signal'));
                        n_chans = raw.nbchan;
                    end
                    filters = [filters eye(n_chans,2*n_patterns)];
                    patterns = [patterns eye(n_chans,2*n_patterns)];
                end
            end
            chanlocs = preproc.chanlocs;
        end
    end
end

% (turn off a few editor warnings because some actual implementations are missing in this file)
%#ok<*INUSD,*STOUT,*MANU>


function schemeCoeffs = computeCoeffs(K, dt, L, M, S)
%COMPUTECOEFFS   Compute coefficients of an EXPINTEG.
%   SCHEMECOEFFS = COMPUTECOEFFS(K, DT, L, M, S) computes the coefficients
%   needed by the EXPINTEG K from the time-step DT, the linear part L, the
%   number of points for complex means M, and the SPINOPERATOR S.
%
% See also EXPINTEG.

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

% Note (1): At the end of the method the coefficients will be stored in
% SCHEMECOEFFS, a STRUCT object with the following fields:
%
% SCHEMECOEFFS.A -> sxs CELL-ARRAY that stores the coefficients A, functions of
%                   the phi-functions
% SCHEMECOEFFS.B -> sx1 CELL-ARRAY that stores the coefficients B, functions of
%                   the phi-functions
% SCHEMECOEFFS.C -> sx1 CELL-ARRAY that stores the numbers C
% SCHEMECOEFFS.E -> (s+1)x1 CELL-ARRAY that stores the s matrix exponentials
%                    e^{C(i)*dt*L}, i=1,..,s, and e^{*dt*L}
% SCHEMECOEFFS.U -> sx(q-1) CELL-ARRAY that stores the coefficients U, functions
%                   of the phi-functions
% SCHEMECOEFFS.V -> (q-1)x1 CELL-ARRAY that stores the coefficients V, functions
%                   of the phi-functions
%
% where s is the number of number of internal stages and q is number of steps
% (>1 for multistep methods). For informations about what these coefficients
% are, see [1].
%
% [1] H. Montanelli and N. Bootland, Solving periodic semilinear stiff PDEs in
% 1D/2D/3D with exponential integrators, submitted (2017).

% Note (2): EXPINTEG schemes are used for solving PDEs with diagonal linear
% operators, e.g., periodic PDEs in 1D/2D/3D with SPIN/SPIN2/SPIN3.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% PRE-PROCESSING:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Set-up:
s = K.stages;          % number of stages
q = K.steps;           % number of steps (>1 for multistep methods)
dim = getDimension(S); % spatial dimension (1, 2 or 3)
nVars = S.numVars;     % number of unknown functions
schemeName = K.scheme; % scheme
N = size(L, 1)/nVars;  % grid points

% Initialize the coefficients of the scheme:
A = cell(s);
B = cell(s, 1);
C = zeros(s, 1);
U = cell(s, q-1);
V = cell(q-1, 1);

% Create a contour around each eigenvalue of the linear part L:
LR = computeLR(S, dt, L, M);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% ETD ADAMS-BASHFORTH:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if ( strcmpi(schemeName, 'abnorsett4') == 1 )

    % Compute the phi-functions:
    phi{1} = expinteg.phiEval(1, LR, N, dim, nVars);
    phi{2} = expinteg.phiEval(2, LR, N, dim, nVars);
    phi{3} = expinteg.phiEval(3, LR, N, dim, nVars);
    phi{4} = expinteg.phiEval(4, LR, N, dim, nVars);

    % Compute the psi-functions:
    psi{1,1} = expinteg.psiEval(1, C(1), LR, N, dim, nVars);

    % Take real part for diffusive problems (real eigenvalues):
    if ( isreal(L) == 1 )
        phi = cellfun(@(f) real(f), phi, 'UniformOutput', 0);
        psi = cellfun(@(f) real(f), psi, 'UniformOutput', 0);
    end

    % Compute V:
    V{1} = -3*phi{2} - 5*phi{3} - 3*phi{4};
    V{2} = 3/2*phi{2} + 4*phi{3} + 3*phi{4};
    V{3} = -1/3*phi{2} - phi{3} - phi{4};

elseif ( strcmpi(schemeName, 'abnorsett5') == 1 )

    % Compute the phi-functions:
    phi{1} = expinteg.phiEval(1, LR, N, dim, nVars);
    phi{2} = expinteg.phiEval(2, LR, N, dim, nVars);
    phi{3} = expinteg.phiEval(3, LR, N, dim, nVars);
    phi{4} = expinteg.phiEval(4, LR, N, dim, nVars);
    phi{5} = expinteg.phiEval(5, LR, N, dim, nVars);

    % Compute the psi-functions:
    psi{1,1} = expinteg.psiEval(1, C(1), LR, N, dim, nVars);

    % Take real part for diffusive problems (real eigenvalues):
    if ( isreal(L) == 1 )
        phi = cellfun(@(f) real(f), phi, 'UniformOutput', 0);
        psi = cellfun(@(f) real(f), psi, 'UniformOutput', 0);
    end

    % Compute V:
    V{1} = -(4*phi{2} + 26/3*phi{3} + 9*phi{4} + 4*phi{5});
    V{2} = 3*phi{2} + 19/2*phi{3} + 12*phi{4} + 6*phi{5};
    V{3} = -(4/3*phi{2} + 14/3*phi{3} + 7*phi{4} + 4*phi{5});
    V{4} = 1/4*phi{2} + 11/12*phi{3} + 3/2*phi{4} + phi{5};

elseif ( strcmpi(schemeName, 'abnorsett6') == 1 )

    % Compute the phi-functions:
    phi{1} = expinteg.phiEval(1, LR, N, dim, nVars);
    phi{2} = expinteg.phiEval(2, LR, N, dim, nVars);
    phi{3} = expinteg.phiEval(3, LR, N, dim, nVars);
    phi{4} = expinteg.phiEval(4, LR, N, dim, nVars);
    phi{5} = expinteg.phiEval(5, LR, N, dim, nVars);
    phi{6} = expinteg.phiEval(6, LR, N, dim, nVars);

    % Compute the psi-functions:
    psi{1,1} = expinteg.psiEval(1, C(1), LR, N, dim, nVars);

    % Take real part for diffusive problems (real eigenvalues):
    if ( isreal(L) == 1 )
        phi = cellfun(@(f) real(f), phi, 'UniformOutput', 0);
        psi = cellfun(@(f) real(f), psi, 'UniformOutput', 0);
    end

    % Compute U:
    V{1} = -(5*phi{2} + 77/6*phi{3} + 71/4*phi{4} + 14*phi{5} + 5*phi{6});
    V{2} = 5*phi{2} + 107/6*phi{3} + 59/2*phi{4} + 26*phi{5} + 10*phi{6};
    V{3} = -(10/3*phi{2} + 13*phi{3} + 49/2*phi{4} + 24*phi{5} + 10*phi{6});
    V{4} = 5/4*phi{2} + 61/12*phi{3} + 41/4*phi{4} + 11*phi{5} + 5*phi{6};
    V{5} = -(1/5*phi{2} + 5/6*phi{3} + 7/4*phi{4} + 2*phi{5} + phi{6});

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% ETD RUNGE-KUTTA:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
elseif ( strcmpi(schemeName, 'etdrk2') == 1 )

    % Compute C:
    C(1) = 0;
    C(2) = 1;

    % Compute the phi-functions:
    phi{1} = expinteg.phiEval(1, LR, N, dim, nVars);
    phi{2} = expinteg.phiEval(2, LR, N, dim, nVars);

    % Take real part for diffusive problems (real eigenvalues):
    if ( isreal(L) == 1 )
        phi = cellfun(@(f) real(f), phi, 'UniformOutput', 0);
    end

    % Compute A:
    A{2,1} = phi{1};

    % Compute B:
    B{1} = phi{1} - phi{2};
    B{2} = phi{2};

elseif ( strcmpi(schemeName, 'etdrk4') == 1 )

    % Compute C:
    C(1) = 0;
    C(2) = 1/2;
    C(3) = 1/2;
    C(4) = 1;

    % Compute the phi-functions:
    phi{1} = expinteg.phiEval(1, LR, N, dim, nVars);
    phi{2} = expinteg.phiEval(2, LR, N, dim, nVars);
    phi{3} = expinteg.phiEval(3, LR, N, dim, nVars);

    % Compute the psi-functions:
    psi{1,2} = expinteg.psiEval(1, C(2), LR, N, dim, nVars);
    psi{1,3} = expinteg.psiEval(1, C(3), LR, N, dim, nVars);
    psi{1,4} = expinteg.psiEval(1, C(4), LR, N, dim, nVars);

    % Take real part for diffusive problems (real eigenvalues):
    if ( isreal(L) == 1 )
        phi = cellfun(@(f) real(f), phi, 'UniformOutput', 0);
        psi = cellfun(@(f) real(f), psi, 'UniformOutput', 0);
    end

    % Compute A:
    A{3,2} = psi{1,2};
    A{4,3} = 2*psi{1,2};

    % Compute B:
    B{2} = 2*phi{2} - 4*phi{3};
    B{3} = 2*phi{2} - 4*phi{3};
    B{4} = -phi{2} + 4*phi{3};

elseif ( strcmpi(schemeName, 'exprk5s8') == 1 )

    % Compute C:
    C(1) = 0;
    C(2) = 1/2;
    C(3) = 1/2;
    C(4) = 1/4;
    C(5) = 1/2;
    C(6) = 1/5;
    C(7) = 2/3;
    C(8) = 1;

    % Compute the phi-functions:
    phi{1} = expinteg.phiEval(1, LR, N, dim, nVars);
    phi{2} = expinteg.phiEval(2, LR, N, dim, nVars);
    phi{3} = expinteg.phiEval(3, LR, N, dim, nVars);
    phi{4} = expinteg.phiEval(4, LR, N, dim, nVars);

    % Compute the psi-functions:
    psi{1,2} = expinteg.psiEval(1, C(2), LR, N, dim, nVars);
    psi{1,3} = psi{1,2};
    psi{1,4} = expinteg.psiEval(1, C(4), LR, N, dim, nVars);
    psi{1,5} = psi{1,2};
    psi{1,6} = expinteg.psiEval(1, C(6), LR, N, dim, nVars);
    psi{1,7} = expinteg.psiEval(1, C(7), LR, N, dim, nVars);
    psi{1,8} = phi{1};
    psi{2,2} = expinteg.psiEval(2, C(2), LR, N, dim, nVars);
    psi{2,4} = expinteg.psiEval(2, C(4), LR, N, dim, nVars);
    psi{2,6} = expinteg.psiEval(2, C(6), LR, N, dim, nVars);
    psi{2,7} = expinteg.psiEval(2, C(7), LR, N, dim, nVars);
    psi{3,2} = expinteg.psiEval(3, C(2), LR, N, dim, nVars);
    psi{3,6} = expinteg.psiEval(3, C(6), LR, N, dim, nVars);
    psi{3,7} = expinteg.psiEval(3, C(7), LR, N, dim, nVars);
    psi{4,6} = expinteg.psiEval(4, C(6), LR, N, dim, nVars);
    psi{4,7} = expinteg.psiEval(4, C(7), LR, N, dim, nVars);

    % Take real part for diffusive problems (real eigenvalues):
    if ( isreal(L) == 1 )
        phi = cellfun(@(f) real(f), phi, 'UniformOutput', 0);
        psi = cellfun(@(f) real(f), psi, 'UniformOutput', 0);
    end

    % Compute A:
    A{3,2} = 2*psi{2,2};
    A{4,3} = 2*psi{2,4};
    A{5,3} = -2*psi{2,2} + 16*psi{3,2};
    A{5,4} = 8*psi{2,2} - 32*psi{3,2};
    A{6,4} = 8*psi{2,6} - 32*psi{3,6};
    A{7,4} = -(125/162)*A{6,4};
    A{6,5} = -2*psi{2,6} + 16*psi{3,6};
    A{7,5} = (125/1944)*A{6,4} - (4/3)*psi{2,7} + (40/3)*psi{3,7};
    Phi = (5/32)*A{6,4} - (25/28)*psi{2,6} + (81/175)*psi{2,7} - ...
        (162/25)*psi{3,7} + (150/7)*psi{4,6} + (972/35)*psi{4,7} + 6*phi{4};
    A{8,5} = -(16/3)*phi{2} + (208/3)*phi{3} - 40*Phi;
    A{7,6} = (3125/3888)*A{6,4} + (25/3)*psi{2,7} - (100/3)*psi{3,7};
    A{8,6} = (250/21)*phi{2} - (250/3)*phi{3} + (250/7)*Phi;
    A{8,7} = (27/14)*phi{2} - 27*phi{3} + (135/7)*Phi;

    % Compute B:
    B{6} = (125/14)*phi{2} - (625/14)*phi{3} + (1125/14)*phi{4};
    B{7} = -(27/14)*phi{2} + (162/7)*phi{3} - (405/7)*phi{4};
    B{8} = (1/2)*phi{2} - (13/2)*phi{3} + (45/2)*phi{4};

elseif ( strcmpi(schemeName, 'friedli') == 1 )

    % Compute C:
    C(1) = 0;
    C(2) = 1/2;
    C(3) = 1/2;
    C(4) = 1;

    % Compute the phi-functions:
    phi{1} = expinteg.phiEval(1, LR, N, dim, nVars);
    phi{2} = expinteg.phiEval(2, LR, N, dim, nVars);
    phi{3} = expinteg.phiEval(3, LR, N, dim, nVars);

    % Compute the psi-functions:
    psi{1,2} = expinteg.psiEval(1, C(2), LR, N, dim, nVars);
    psi{1,3} = expinteg.psiEval(1, C(3), LR, N, dim, nVars);
    psi{1,4} = expinteg.psiEval(1, C(4), LR, N, dim, nVars);
    psi{2,2} = expinteg.psiEval(2, C(2), LR, N, dim, nVars);

    % Take real part for diffusive problems (real eigenvalues):
    if ( isreal(L) == 1 )
        phi = cellfun(@(f) real(f), phi, 'UniformOutput', 0);
        psi = cellfun(@(f) real(f), psi, 'UniformOutput', 0);
    end

    % Compute A:
    A{3,2} = 2*psi{2,2};
    A{4,2} = -26/25*phi{1} + 2/25*phi{2};
    A{4,3} = 26/25*phi{1} + 48/25*phi{2};

    % Compute B:
    B{3} = 4*phi{2} - 8*phi{3};
    B{4} = -phi{2} + 4*phi{3};

elseif ( strcmpi(schemeName, 'hochbruck-ostermann') == 1 )

    % Compute C:
    C(1) = 0;
    C(2) = 1/2;
    C(3) = 1/2;
    C(4) = 1;
    C(5) = 1/2;

    % Compute the phi-functions:
    phi{1} = expinteg.phiEval(1, LR, N, dim, nVars);
    phi{2} = expinteg.phiEval(2, LR, N, dim, nVars);
    phi{3} = expinteg.phiEval(3, LR, N, dim, nVars);

    % Compute the psi-functions:
    psi{1,2} = expinteg.psiEval(1, C(2), LR, N, dim, nVars);
    psi{1,3} = expinteg.psiEval(1, C(3), LR, N, dim, nVars);
    psi{1,4} = expinteg.psiEval(1, C(4), LR, N, dim, nVars);
    psi{1,5} = expinteg.psiEval(1, C(5), LR, N, dim, nVars);
    psi{2,2} = expinteg.psiEval(2, C(2), LR, N, dim, nVars);
    psi{3,2} = expinteg.psiEval(3, C(2), LR, N, dim, nVars);

    % Take real part for diffusive problems (real eigenvalues):
    if ( isreal(L) == 1 )
        phi = cellfun(@(f) real(f), phi, 'UniformOutput', 0);
        psi = cellfun(@(f) real(f), psi, 'UniformOutput', 0);
    end

    % Compute A:
    A{3,2} = 4*psi{2,2};
    A{4,2} = phi{2};
    A{5,2} = 1/4*phi{2} - phi{3} + 2*psi{2,2} - 4*psi{3,2};
    A{4,3} = A{4,2};
    A{5,3} = A{5,2};
    A{5,4} = psi{2,2} - A{5,2};

    % Compute B:
    B{4} = -phi{2} + 4*phi{3};
    B{5} = 4*phi{2} - 8*phi{3};

elseif ( strcmpi(schemeName, 'krogstad') == 1 )

    % Compute C:
    C(1) = 0;
    C(2) = 1/2;
    C(3) = 1/2;
    C(4) = 1;

    % Compute the phi-functions:
    phi{1} = expinteg.phiEval(1, LR, N, dim, nVars);
    phi{2} = expinteg.phiEval(2, LR, N, dim, nVars);
    phi{3} = expinteg.phiEval(3, LR, N, dim, nVars);

    % Compute the psi-functions:
    psi{1,2} = expinteg.psiEval(1, C(2), LR, N, dim, nVars);
    psi{1,3} = expinteg.psiEval(1, C(3), LR, N, dim, nVars);
    psi{1,4} = expinteg.psiEval(1, C(4), LR, N, dim, nVars);
    psi{2,2} = expinteg.psiEval(2, C(2), LR, N, dim, nVars);

    % Take real part for diffusive problems (real eigenvalues):
    if ( isreal(L) == 1 )
        phi = cellfun(@(f) real(f), phi, 'UniformOutput', 0);
        psi = cellfun(@(f) real(f), psi, 'UniformOutput', 0);
    end

    % Compute A:
    A{3,2} = 4*psi{2,2};
    A{4,3} = 2*phi{2};

    % Compute B:
    B{2} = 2*phi{2} - 4*phi{3};
    B{3} = 2*phi{2} - 4*phi{3};
    B{4} = -phi{2} + 4*phi{3};

elseif ( strcmpi(schemeName, 'minchev') == 1 )

    % Compute C:
    C(1) = 0;
    C(2) = 1/2;
    C(3) = 1/2;
    C(4) = 1;

    % Compute the phi-functions:
    phi{1} = expinteg.phiEval(1, LR, N, dim, nVars);
    phi{2} = expinteg.phiEval(2, LR, N, dim, nVars);
    phi{3} = expinteg.phiEval(3, LR, N, dim, nVars);
    phi{4} = expinteg.phiEval(4, LR, N, dim, nVars);

    % Compute the psi-functions:
    psi{1,2} = expinteg.psiEval(1, C(2), LR, N, dim, nVars);
    psi{1,3} = expinteg.psiEval(1, C(3), LR, N, dim, nVars);
    psi{1,4} = expinteg.psiEval(1, C(4), LR, N, dim, nVars);
    psi{2,2} = expinteg.psiEval(2, C(2), LR, N, dim, nVars);

    % Take real part for diffusive problems (real eigenvalues):
    if ( isreal(L) == 1 )
        phi = cellfun(@(f) real(f), phi, 'UniformOutput', 0);
        psi = cellfun(@(f) real(f), psi, 'UniformOutput', 0);
    end

    % Compute A:
    A{3,2} = 4/25*psi{1,2} + 24/25*psi{2,2};
    A{4,2} = 21/5*phi{2} - 108/5*phi{3};
    A{4,3} = 1/20*phi{1} - 33/10*phi{2} + 123/5*phi{3};

    % Compute B:
    B{2} = -1/10*phi{1} + 1/5*phi{2} - 4*phi{3} + 12*phi{4};
    B{3} = 1/30*phi{1} + 23/5*phi{2} - 8*phi{3} - 4*phi{4};
    B{4} = 1/30*phi{1} - 7/5*phi{2} + 6*phi{3} - 4*phi{4};

elseif ( strcmpi(schemeName, 'strehmel-weiner') == 1 )

    % Compute C:
    C(1) = 0;
    C(2) = 1/2;
    C(3) = 1/2;
    C(4) = 1;

    % Compute the phi-functions:
    phi{1} = expinteg.phiEval(1, LR, N, dim, nVars);
    phi{2} = expinteg.phiEval(2, LR, N, dim, nVars);
    phi{3} = expinteg.phiEval(3, LR, N, dim, nVars);

    % Compute the psi-functions:
    psi{1,2} = expinteg.psiEval(1, C(2), LR, N, dim, nVars);
    psi{1,3} = expinteg.psiEval(1, C(3), LR, N, dim, nVars);
    psi{1,4} = expinteg.psiEval(1, C(4), LR, N, dim, nVars);
    psi{2,2} = expinteg.psiEval(2, C(2), LR, N, dim, nVars);

    % Take real part for diffusive problems (real eigenvalues):
    if ( isreal(L) == 1 )
        phi = cellfun(@(f) real(f), phi, 'UniformOutput', 0);
        psi = cellfun(@(f) real(f), psi, 'UniformOutput', 0);
    end

    % Compute A:
    A{3,2} = 2*psi{2,2};
    A{4,2} = -2*phi{2};
    A{4,3} = 4*phi{2};

    % Compute B:
    B{3} = 4*phi{2} - 8*phi{3};
    B{4} = -phi{2} + 4*phi{3};

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% LAWSON:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
elseif ( strcmpi(schemeName, 'ablawson4') == 1 )

    % Compute the phi-functions:
    phi0 = expinteg.phiEval(0, LR, N, dim, nVars);
    phi{1} = expinteg.phiEval(1, LR, N, dim, nVars);

    % Compute the psi-functions:
    psi{1,1} = expinteg.psiEval(1, C(1), LR, N, dim, nVars);

    % Take real part for diffusive problems (real eigenvalues):
    if ( isreal(L) == 1 )
        phi = cellfun(@(f) real(f), phi, 'UniformOutput', 0);
        phi0 = real(phi0);
        psi = cellfun(@(f) real(f), psi, 'UniformOutput', 0);
    end

    e2z = phi0.*phi0;
    e3z = e2z.*phi0;
    e4z = e2z.*e2z;

    % Compute B:
    B{1} = 55/24*phi0;

    % Compute V:
    V{1} = -59/24*e2z;
    V{2} = 37/24*e3z;
    V{3} = -3/8*e4z;

elseif ( strcmpi(schemeName, 'lawson4') == 1 )

    % Compute C:
    C(1) = 0;
    C(2) = 1/2;
    C(3) = 1/2;
    C(4) = 1;

    % Compute the phi-functions:
    phi0 = expinteg.phiEval(0, LR, N, dim, nVars);
    phi{1} = expinteg.phiEval(1, LR, N, dim, nVars);

    % Compute the psi-functions:
    psi02 = expinteg.psiEval(0, C(2), LR, N, dim, nVars);
    psi{1,2} = expinteg.psiEval(1, C(2), LR, N, dim, nVars);
    psi{1,3} = expinteg.psiEval(1, C(3), LR, N, dim, nVars);
    psi{1,4} = expinteg.psiEval(1, C(4), LR, N, dim, nVars);

    % Take real part for diffusive problems (real eigenvalues):
    if ( isreal(L) == 1 )
        phi0 = real(phi0);
        phi = cellfun(@(f) real(f), phi, 'UniformOutput', 0);
        psi02 = real(psi02);
        psi = cellfun(@(f) real(f), psi, 'UniformOutput', 0);
    end

    % Compute A:
    A{2,1} = 1/2*psi02;
    A{3,2} = 1/2;
    A{4,3} = psi02;

    % Compute B:
    B{1} = 1/6*phi0;
    B{2} = 1/3*psi02;
    B{3} = B{2};
    B{4} = 1/6;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% GENERALIZED LAWSON:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
elseif ( strcmpi(schemeName, 'genlawson41') == 1 )

    % Compute C:
    C(1) = 0;
    C(2) = 1/2;
    C(3) = 1/2;
    C(4) = 1;

    % Compute the phi-functions:
    phi{1} = expinteg.phiEval(1, LR, N, dim, nVars);

    % Compute the psi-functions:
    psi02 = expinteg.psiEval(0, C(2), LR, N, dim, nVars);
    psi{1,2} = expinteg.psiEval(1, C(2), LR, N, dim, nVars);
    psi{1,3} = expinteg.psiEval(1, C(3), LR, N, dim, nVars);
    psi{1,4} = expinteg.psiEval(1, C(4), LR, N, dim, nVars);

    % Take real part for diffusive problems (real eigenvalues):
    if ( isreal(L) == 1 )
        phi = cellfun(@(f) real(f), phi, 'UniformOutput', 0);
        psi02 = real(psi02);
        psi = cellfun(@(f) real(f), psi, 'UniformOutput', 0);
    end

    % Compute A:
    A{3,2} = 1/2;
    A{4,3} = psi02;

    % Compute B:
    B{2} = 1/3*psi02;
    B{3} = B{2};
    B{4} = 1/6;

elseif ( strcmpi(schemeName, 'genlawson42') == 1 )

    % Compute C:
    C(1) = 0;
    C(2) = 1/2;
    C(3) = 1/2;
    C(4) = 1;

    % Compute the phi-functions:
    phi{1} = expinteg.phiEval(1, LR, N, dim, nVars);
    phi{2} = expinteg.phiEval(2, LR, N, dim, nVars);

    % Compute the psi-functions:
    psi02 = expinteg.psiEval(0, C(2), LR, N, dim, nVars);
    psi{1,2} = expinteg.psiEval(1, C(2), LR, N, dim, nVars);
    psi{1,3} = expinteg.psiEval(1, C(3), LR, N, dim, nVars);
    psi{1,4} = expinteg.psiEval(1, C(4), LR, N, dim, nVars);
    psi{2,2} = expinteg.psiEval(2, C(2), LR, N, dim, nVars);

    % Take real part for diffusive problems (real eigenvalues):
    if ( isreal(L) == 1 )
        phi = cellfun(@(f) real(f), phi, 'UniformOutput', 0);
        psi02 = real(psi02);
        psi = cellfun(@(f) real(f), psi, 'UniformOutput', 0);
    end

    % Compute A:
    A{3,2} = 1/2;
    A{4,3} = psi02;

    % Compute B:
    B{2} = 1/3*psi02;
    B{3} = B{2};
    B{4} = 1/6;

    % Compute U:
    U{2,1} = -psi{2,2};
    U{3,1} = -psi{2,2} + 1/4;
    U{4,1} = -phi{2} + 1/2*psi02;

    % Compute V:
    V{1} = -phi{2} + 1/3*psi02 + 1/6;

elseif ( strcmpi(schemeName, 'genlawson43') == 1 )

    % Compute C:
    C(1) = 0;
    C(2) = 1/2;
    C(3) = 1/2;
    C(4) = 1;

    % Compute the phi-functions:
    phi{1} = expinteg.phiEval(1, LR, N, dim, nVars);
    phi{2} = expinteg.phiEval(2, LR, N, dim, nVars);
    phi{3} = expinteg.phiEval(3, LR, N, dim, nVars);

    % Compute the psi-functions:
    psi02 = expinteg.psiEval(0, C(2), LR, N, dim, nVars);
    psi{1,2} = expinteg.psiEval(1, C(2), LR, N, dim, nVars);
    psi{1,3} = expinteg.psiEval(1, C(3), LR, N, dim, nVars);
    psi{1,4} = expinteg.psiEval(1, C(4), LR, N, dim, nVars);
    psi{2,2} = expinteg.psiEval(2, C(2), LR, N, dim, nVars);
    psi{3,2} = expinteg.psiEval(3, C(2), LR, N, dim, nVars);

    % Take real part for diffusive problems (real eigenvalues):
    if ( isreal(L) == 1 )
        phi = cellfun(@(f) real(f), phi, 'UniformOutput', 0);
        psi02 = real(psi02);
        psi = cellfun(@(f) real(f), psi, 'UniformOutput', 0);
    end

    % Compute A:
    A{3,2} = 1/2;
    A{4,3} = psi02;

    % Compute B:
    B{2} = 1/3*psi02;
    B{3} = B{2};
    B{4} = 1/6;

    % Compute U:
    U{2,1} = -2*psi{2,2} - 2*psi{3,2};
    U{3,1} = -2*psi{2,2} - 2*psi{3,2} + 5/8;
    U{4,1} = -2*phi{2} - 2*phi{3} + 5/4*psi02;
    U{2,2} = 1/2*psi{2,2} + psi{3,2};
    U{3,2} = 1/2*psi{2,2} + psi{3,2} - 3/16;
    U{4,2} = 1/2*phi{2} + phi{3} - 3/8*psi02;

    % Compute V:
    V{1} = -2*phi{2} - 2*phi{3} + 5/6*psi02 + 1/2;
    V{2} = 1/2*phi{2} + phi{3} - 1/4*psi02 - 1/6;

elseif ( strcmpi(schemeName, 'genlawson44') == 1 )

    % Compute C:
    C(1) = 0;
    C(2) = 1/2;
    C(3) = 1/2;
    C(4) = 1;

    % Compute the phi-functions:
    phi{1} = expinteg.phiEval(1, LR, N, dim, nVars);
    phi{2} = expinteg.phiEval(2, LR, N, dim, nVars);
    phi{3} = expinteg.phiEval(3, LR, N, dim, nVars);
    phi{4} = expinteg.phiEval(4, LR, N, dim, nVars);

    % Compute the psi-functions:
    psi02 = expinteg.psiEval(0, C(2), LR, N, dim, nVars);
    psi{1,2} = expinteg.psiEval(1, C(2), LR, N, dim, nVars);
    psi{1,3} = expinteg.psiEval(1, C(3), LR, N, dim, nVars);
    psi{1,4} = expinteg.psiEval(1, C(4), LR, N, dim, nVars);
    psi{2,2} = expinteg.psiEval(2, C(2), LR, N, dim, nVars);
    psi{3,2} = expinteg.psiEval(3, C(2), LR, N, dim, nVars);
    psi{4,2} = expinteg.psiEval(4, C(2), LR, N, dim, nVars);

    % Take real part for diffusive problems (real eigenvalues):
    if ( isreal(L) == 1 )
        phi = cellfun(@(f) real(f), phi, 'UniformOutput', 0);
        psi02 = real(psi02);
        psi = cellfun(@(f) real(f), psi, 'UniformOutput', 0);
    end

    % Compute A:
    A{3,2} = 1/2;
    A{4,3} = psi02;

    % Compute B:
    B{2} = 1/3*psi02;
    B{3} = B{2};
    B{4} = 1/6;

    % Compute U:
    U{2,1} = -3*psi{2,2} - 5*psi{3,2} - 3*psi{4,2};
    U{2,2} = 3/2*psi{2,2} + 4*psi{3,2} + 3*psi{4,2};
    U{2,3} = -1/3*psi{2,2} - 1*psi{3,2} - 1*psi{4,2};
    U{3,1} = U{2,1} + 35/32;
    U{3,2} = U{2,2} - 21/32;
    U{3,3} = U{2,3} + 5/32;
    U{4,1} = -3*phi{2} - 5*phi{3} - 3*phi{4} + 35/16*psi02;
    U{4,2} = 3/2*phi{2} + 4*phi{3} + 3*phi{4} - 21/16*psi02;
    U{4,3} = -1/3*phi{2} - phi{3} - phi{4} + 5/16*psi02;

    % Compute V:
    V{1} = -3*phi{2} - 5*phi{3} - 3*phi{4} + 35/24*psi02 + 1;
    V{2} = 3/2*phi{2} + 4*phi{3} + 3*phi{4} - 7/8*psi02 - 2/3;
    V{3} = -1/3*phi{2} - 1*phi{3} - 1*phi{4} + 5/24*psi02 + 1/6;

elseif ( strcmpi(schemeName, 'genlawson45') == 1 )

    % Compute C:
    C(1) = 0;
    C(2) = 1/2;
    C(3) = 1/2;
    C(4) = 1;

    % Compute the phi-functions:
    phi{1} = expinteg.phiEval(1, LR, N, dim, nVars);
    phi{2} = expinteg.phiEval(2, LR, N, dim, nVars);
    phi{3} = expinteg.phiEval(3, LR, N, dim, nVars);
    phi{4} = expinteg.phiEval(4, LR, N, dim, nVars);
    phi{5} = expinteg.phiEval(5, LR, N, dim, nVars);

    % Compute the psi-functions:
    psi02 = expinteg.psiEval(0, C(2), LR, N, dim, nVars);
    psi{1,2} = expinteg.psiEval(1, C(2), LR, N, dim, nVars);
    psi{1,3} = expinteg.psiEval(1, C(3), LR, N, dim, nVars);
    psi{1,4} = expinteg.psiEval(1, C(4), LR, N, dim, nVars);
    psi{2,2} = expinteg.psiEval(2, C(2), LR, N, dim, nVars);
    psi{3,2} = expinteg.psiEval(3, C(2), LR, N, dim, nVars);
    psi{4,2} = expinteg.psiEval(4, C(2), LR, N, dim, nVars);
    psi{5,2} = expinteg.psiEval(5, C(2), LR, N, dim, nVars);

    % Take real part for diffusive problems (real eigenvalues):
    if ( isreal(L) == 1 )
        phi = cellfun(@(f) real(f), phi, 'UniformOutput', 0);
        psi02 = real(psi02);
        psi = cellfun(@(f) real(f), psi, 'UniformOutput', 0);
    end

    % Compute A:
    A{3,2} = 1/2;
    A{4,3} = psi02;

    % Compute B:
    B{2} = 1/3*psi02;
    B{3} = B{2};
    B{4} = 1/6;

    % Compute U:
    U{2,1} = -4*psi{2,2} - 26/3*psi{3,2} - 9*psi{4,2} - 4*psi{5,2};
    U{2,2} = 3*psi{2,2} + 19/2*psi{3,2} + 12*psi{4,2} + 6*psi{5,2};
    U{2,3} = -4/3*psi{2,2} - 14/3*psi{3,2} - 7*psi{4,2} - 4*psi{5,2};
    U{2,4} = 1/4*psi{2,2} + 88/96*psi{3,2} + 3/2*psi{4,2} + psi{5,2};
    U{3,1} = U{2,1} + 105/64;
    U{3,2} = U{2,2} - 189/128;
    U{3,3} = U{2,3} + 45/64;
    U{3,4} = U{2,4} - 35/256;
    U{4,1} = -4*phi{2} - 26/3*phi{3} - 9*phi{4} - 4*phi{5} + 105/32*psi02;
    U{4,2} = 3*phi{2} + 19/2*phi{3} + 12*phi{4} + 6*phi{5} - 189/64*psi02;
    U{4,3} = -4/3*phi{2} - 14/3*phi{3} - 7*phi{4} - 4*phi{5} + 45/32*psi02;
    U{4,4} = 1/4*phi{2} + 11/12*phi{3} + 3/2*phi{4} + phi{5} - 35/128*psi02;

    % Compute V:
    V{1} = -4*phi{2} - 26/3*phi{3} - 9*phi{4} - 4*phi{5} + 35/16*psi02 + ...
        5/3;
    V{2} = 3*phi{2} + 19/2*phi{3} + 12*phi{4} + 6*phi{5} - 63/32*psi02 - ...
        5/3;
    V{3} = -4/3*phi{2} - 14/3*phi{3} - 7*phi{4} - 4*phi{5} + ...
        15/16*psi02 + 5/6;
    V{4} = 1/4*phi{2} + 11/12*phi{3} + 3/2*phi{4} + phi{5} - ...
        35/192*psi02 - 1/6;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% MODIFIED GENERALIZED LAWSON:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
elseif ( strcmpi(schemeName, 'modgenlawson41') == 1 )

    % Compute C:
    C(1) = 0;
    C(2) = 1/2;
    C(3) = 1/2;
    C(4) = 1;

    % Compute the phi-functions:
    phi{1} = expinteg.phiEval(1, LR, N, dim, nVars);
    phi{2} = expinteg.phiEval(2, LR, N, dim, nVars);

    % Compute the psi-functions:
    psi02 = expinteg.psiEval(0, C(2), LR, N, dim, nVars);
    psi{1,2} = expinteg.psiEval(1, C(2), LR, N, dim, nVars);
    psi{1,3} = expinteg.psiEval(1, C(3), LR, N, dim, nVars);
    psi{1,4} = expinteg.psiEval(1, C(4), LR, N, dim, nVars);

    % Take real part for diffusive problems (real eigenvalues):
    if ( isreal(L) == 1 )
        phi = cellfun(@(f) real(f), phi, 'UniformOutput', 0);
        psi02 = real(psi02);
        psi = cellfun(@(f) real(f), psi, 'UniformOutput', 0);
    end

    % Compute A:
    A{3,2} = 1/2;
    A{4,3} = psi02;

    % Compute B:
    B{2} = 1/3*psi02;
    B{3} = B{2};
    B{4} = phi{2} - 1/3*psi02;

elseif ( strcmpi(schemeName, 'modgenlawson42') == 1 )

    % Compute C:
    C(1) = 0;
    C(2) = 1/2;
    C(3) = 1/2;
    C(4) = 1;

    % Compute the phi-functions:
    phi{1} = expinteg.phiEval(1, LR, N, dim, nVars);
    phi{2} = expinteg.phiEval(2, LR, N, dim, nVars);
    phi{3} = expinteg.phiEval(3, LR, N, dim, nVars);

    % Compute the psi-functions:
    psi02 = expinteg.psiEval(0, C(2), LR, N, dim, nVars);
    psi{1,2} = expinteg.psiEval(1, C(2), LR, N, dim, nVars);
    psi{1,3} = expinteg.psiEval(1, C(3), LR, N, dim, nVars);
    psi{1,4} = expinteg.psiEval(1, C(4), LR, N, dim, nVars);
    psi{2,2} = expinteg.psiEval(2, C(2), LR, N, dim, nVars);

    % Take real part for diffusive problems (real eigenvalues):
    if ( isreal(L) == 1 )
        phi = cellfun(@(f) real(f), phi, 'UniformOutput', 0);
        psi02 = real(psi02);
        psi = cellfun(@(f) real(f), psi, 'UniformOutput', 0);
    end

    % Compute A:
    A{3,2} = 1/2;
    A{4,3} = psi02;

    % Compute B:
    B{2} = 1/3*psi02;
    B{3} = B{2};
    B{4} =  1/2*phi{2} + phi{3} - 1/4*psi02;

    % Compute U:
    U{2,1} = -psi{2,2};
    U{3,1} = -psi{2,2} + 1/4;
    U{4,1} = -phi{2} + 1/2*psi02;

    % Compute V:
    V{1} = -1/2*phi{2} + phi{3} + 1/12*psi02;

elseif ( strcmpi(schemeName, 'modgenlawson43') == 1 )

    % Compute C:
    C(1) = 0;
    C(2) = 1/2;
    C(3) = 1/2;
    C(4) = 1;

    % Compute the phi-functions:
    phi{1} = expinteg.phiEval(1, LR, N, dim, nVars);
    phi{2} = expinteg.phiEval(2, LR, N, dim, nVars);
    phi{3} = expinteg.phiEval(3, LR, N, dim, nVars);
    phi{4} = expinteg.phiEval(4, LR, N, dim, nVars);

    % Compute the psi-functions:
    psi02 = expinteg.psiEval(0, C(2), LR, N, dim, nVars);
    psi{1,2} = expinteg.psiEval(1, C(2), LR, N, dim, nVars);
    psi{1,3} = expinteg.psiEval(1, C(3), LR, N, dim, nVars);
    psi{1,4} = expinteg.psiEval(1, C(4), LR, N, dim, nVars);
    psi{2,2} = expinteg.psiEval(2, C(2), LR, N, dim, nVars);
    psi{3,2} = expinteg.psiEval(3, C(2), LR, N, dim, nVars);

    % Take real part for diffusive problems (real eigenvalues):
    if ( isreal(L) == 1 )
        phi = cellfun(@(f) real(f), phi, 'UniformOutput', 0);
        psi02 = real(psi02);
        psi = cellfun(@(f) real(f), psi, 'UniformOutput', 0);
    end

    % Compute A:
    A{3,2} = 1/2;
    A{4,3} = psi02;

    % Compute B:
    B{2} = 1/3*psi02;
    B{3} = B{2};
    B{4} = 1/3*phi{2} + phi{3} + phi{4} - 5/24*psi02;

    % Compute U:
    U{2,1} = -2*psi{2,2} - 2*psi{3,2};
    U{3,1} = -2*psi{2,2} - 2*psi{3,2} + 5/8;
    U{4,1} = -2*phi{2} - 2*phi{3} + 5/4*psi02;
    U{2,2} = 1/2*psi{2,2} + psi{3,2};
    U{3,2} = 1/2*psi{2,2} + psi{3,2} - 3/16;
    U{4,2} = 1/2*phi{2} + phi{3} - 3/8*psi02;

    % Compute V:
    V{1} = -phi{2} + phi{3} + 3*phi{4} + 5/24*psi02;
    V{2} = 1/6*phi{2} - phi{4} - 1/24*psi02;

elseif ( strcmpi(schemeName, 'modgenlawson44') == 1 )

    % Compute C:
    C(1) = 0;
    C(2) = 1/2;
    C(3) = 1/2;
    C(4) = 1;

    % Compute the phi-functions:
    phi{1} = expinteg.phiEval(1, LR, N, dim, nVars);
    phi{2} = expinteg.phiEval(2, LR, N, dim, nVars);
    phi{3} = expinteg.phiEval(3, LR, N, dim, nVars);
    phi{4} = expinteg.phiEval(4, LR, N, dim, nVars);
    phi{5} = expinteg.phiEval(5, LR, N, dim, nVars);

    % Compute the psi-functions:
    psi02 = expinteg.psiEval(0, C(2), LR, N, dim, nVars);
    psi{1,2} = expinteg.psiEval(1, C(2), LR, N, dim, nVars);
    psi{1,3} = expinteg.psiEval(1, C(3), LR, N, dim, nVars);
    psi{1,4} = expinteg.psiEval(1, C(4), LR, N, dim, nVars);
    psi{2,2} = expinteg.psiEval(2, C(2), LR, N, dim, nVars);
    psi{3,2} = expinteg.psiEval(3, C(2), LR, N, dim, nVars);
    psi{4,2} = expinteg.psiEval(4, C(2), LR, N, dim, nVars);

    % Take real part for diffusive problems (real eigenvalues):
    if ( isreal(L) == 1 )
        phi = cellfun(@(f) real(f), phi, 'UniformOutput', 0);
        psi02 = real(psi02);
        psi = cellfun(@(f) real(f), psi, 'UniformOutput', 0);
    end

    % Compute A:
    A{3,2} = 1/2;
    A{4,3} = psi02;

    % Compute B:
    B{2} = 1/3*psi02;
    B{3} = B{2};
    B{4} = 1/4*phi{2} + 11/12*phi{3} + 3/2*phi{4} + phi{5} - 35/192*psi02;

    % Compute U:
    U{2,1} = -3*psi{2,2} - 5*psi{3,2} - 3*psi{4,2};
    U{2,2} = 3/2*psi{2,2} + 4*psi{3,2} + 3*psi{4,2};
    U{2,3} = -1/3*psi{2,2} - 1*psi{3,2} - 1*psi{4,2};

    U{3,1} = U{2,1} + 35/32;
    U{3,2} = U{2,2} - 21/32;
    U{3,3} = U{2,3} + 5/32;

    U{4,1} = -3*phi{2} - 5*phi{3} - 3*phi{4} + 35/16*psi02;
    U{4,2} = 3/2*phi{2} + 4*phi{3} + 3*phi{4} - 21/16*psi02;
    U{4,3} = -1/3*phi{2} - phi{3} - phi{4} + 5/16*psi02;

    % Compute V:
    V{1} = -3/2*phi{2} + 1/2*phi{3} + 6*phi{4} + 6*phi{5} + 35/96*psi02;
    V{2} = 1/2*phi{2} + 1/3*phi{3} - 3*phi{4} - 4*phi{5} - 7/48*psi02;
    V{3} = -1/12*phi{2} - 1/12*phi{3} + 1/2*phi{4} + phi{5} + 5/192*psi02;

elseif ( strcmpi(schemeName, 'modgenlawson45') == 1 )

    % Compute C:
    C(1) = 0;
    C(2) = 1/2;
    C(3) = 1/2;
    C(4) = 1;

    % Compute the phi-functions:
    phi{1} = expinteg.phiEval(1, LR, N, dim, nVars);
    phi{2} = expinteg.phiEval(2, LR, N, dim, nVars);
    phi{3} = expinteg.phiEval(3, LR, N, dim, nVars);
    phi{4} = expinteg.phiEval(4, LR, N, dim, nVars);
    phi{5} = expinteg.phiEval(5, LR, N, dim, nVars);
    phi{6} = expinteg.phiEval(6, LR, N, dim, nVars);

    % Compute the psi-functions:
    psi02 = expinteg.psiEval(0, C(2), LR, N, dim, nVars);
    psi{1,2} = expinteg.psiEval(1, C(2), LR, N, dim, nVars);
    psi{1,3} = expinteg.psiEval(1, C(3), LR, N, dim, nVars);
    psi{1,4} = expinteg.psiEval(1, C(4), LR, N, dim, nVars);
    psi{2,2} = expinteg.psiEval(2, C(2), LR, N, dim, nVars);
    psi{3,2} = expinteg.psiEval(3, C(2), LR, N, dim, nVars);
    psi{4,2} = expinteg.psiEval(4, C(2), LR, N, dim, nVars);
    psi{5,2} = expinteg.psiEval(5, C(2), LR, N, dim, nVars);

    % Take real part for diffusive problems (real eigenvalues):
    if ( isreal(L) == 1 )
        phi = cellfun(@(f) real(f), phi, 'UniformOutput', 0);
        psi02 = real(psi02);
        psi = cellfun(@(f) real(f), psi, 'UniformOutput', 0);
    end

    % Compute A:
    A{3,2} = 1/2;
    A{4,3} = psi02;

    % Compute B:
    B{2} = 1/3*psi02;
    B{3} = B{2};
    B{4} = 12/59*phi{2} + 50/59*phi{3} + 105/59*phi{4} + 120/59*phi{5} - ...
        60/59*phi{6} - 157/944*psi02;

    % Compute U:
    U{2,1} = -4*psi{2,2} - 26/3*psi{3,2} - 9*psi{4,2} - 4*psi{5,2};
    U{2,2} = 3*psi{2,2} + 19/2*psi{3,2} + 12*psi{4,2} + 6*psi{5,2};
    U{2,3} = -4/3*psi{2,2} - 14/3*psi{3,2} - 7*psi{4,2} - 4*psi{5,2};
    U{2,4} = 1/4*psi{2,2} + 88/96*psi{3,2} + 3/2*psi{4,2} + psi{5,2};
    U{3,1} = U{2,1} + 105/64;
    U{3,2} = U{2,2} - 189/128;
    U{3,3} = U{2,3} + 45/64;
    U{3,4} = U{2,4} - 35/256;
    U{4,1} = -4*phi{2} - 26/3*phi{3} - 9*phi{4} - 4*phi{5} + 105/32*psi02;
    U{4,2} = 3*phi{2} + 19/2*phi{3} + 12*phi{4} + 6*phi{5} - 189/64*psi02;
    U{4,3} = -4/3*phi{2} - 14/3*phi{3} - 7*phi{4} - 4*phi{5} + 45/32*psi02;
    U{4,4} = 1/4*phi{2} + 11/12*phi{3} + 3/2*phi{4} + phi{5} - 35/128*psi02;

    % Compute V:
    V{1} = -116/59*phi{2} - 34/177*phi{3} + 519/59*phi{4} + ...
        964/59*phi{5} - 600/59*phi{6} + 495/944*psi02;
    V{2} = 57/59*phi{2} + 121/118*phi{3} - 342/59*phi{4} - ...
        846/59*phi{5} + 600/59*phi{6} - 577/1888*psi02;
    V{3} = -56/177*phi{2} - 76/177*phi{3} + 112/59*phi{4} + ...
        364/59*phi{5} - 300/59*phi{6} + 25/236*psi02;
    V{4} = 11/236*phi{2} + 49/708*phi{3} - 33/118*phi{4} - ...
        61/59*phi{5} + 60/59*phi{6} - 181/11328*psi02;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% PREDICTOR-CORRECTOR:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
elseif ( strcmpi(schemeName, 'pec423') == 1 )

    % Compute C:
    C(1) = 0;
    C(2) = 1;

    % Compute the phi-functions:
    phi{1} = expinteg.phiEval(1, LR, N, dim, nVars);
    phi{2} = expinteg.phiEval(2, LR, N, dim, nVars);
    phi{3} = expinteg.phiEval(3, LR, N, dim, nVars);
    phi{4} = expinteg.phiEval(4, LR, N, dim, nVars);

    % Compute the psi-functions:
    psi{1,2} = expinteg.psiEval(1, C(2), LR, N, dim, nVars);

    % Take real part for diffusive problems (real eigenvalues):
    if ( isreal(L) == 1 )
        phi = cellfun(@(f) real(f), phi, 'UniformOutput', 0);
        psi = cellfun(@(f) real(f), psi, 'UniformOutput', 0);
    end

    % Compute B:
    B{2} = 1/3*phi{2} + phi{3} + phi{4};

    % Compute U:
    U{2,1} = -2*phi{2} - 2*phi{3};
    U{2,2} = 1/2*phi{2} + phi{3};

    % Compute V:
    V{1} = -phi{2} + phi{3} + 3*phi{4};
    V{2} = 1/6*phi{2} - phi{4};

elseif ( strcmpi(schemeName, 'pecec433') == 1 )

    % Compute C:
    C(1) = 0;
    C(2) = 1;
    C(3) = 1;

    % Compute the phi-functions:
    phi{1} = expinteg.phiEval(1, LR, N, dim, nVars);
    phi{2} = expinteg.phiEval(2, LR, N, dim, nVars);
    phi{3} = expinteg.phiEval(3, LR, N, dim, nVars);
    phi{4} = expinteg.phiEval(4, LR, N, dim, nVars);

    % Compute the psi-functions:
    psi{1,2} = expinteg.psiEval(1, C(2), LR, N, dim, nVars);
    psi{1,3} = expinteg.psiEval(1, C(3), LR, N, dim, nVars);

    % Take real part for diffusive problems (real eigenvalues):
    if ( isreal(L) == 1 )
        phi = cellfun(@(f) real(f), phi, 'UniformOutput', 0);
        psi = cellfun(@(f) real(f), psi, 'UniformOutput', 0);
    end

    % Compute A:
    A{3,2} = 1/3*phi{2} + phi{3} + phi{4};

    % Compute B:
    B{3} = 1/3*phi{2} + phi{3} + phi{4};

    % Compute U:
    U{2,1} = -2*phi{2} - 2*phi{3};
    U{3,1} = -phi{2} + phi{3} + 3*phi{4};
    U{2,2} = 1/2*phi{2} + phi{3};
    U{3,2} = 1/6*phi{2} - phi{4};

    % Compute V:
    V{1} = -phi{2} + phi{3} + 3*phi{4};
    V{2} = 1/6*phi{2} - phi{4};

elseif ( strcmpi(schemeName, 'pec524') == 1 )

    % Compute C:
    C(1) = 0;
    C(2) = 1;

    % Compute the phi-functions:
    phi{1} = expinteg.phiEval(1, LR, N, dim, nVars);
    phi{2} = expinteg.phiEval(2, LR, N, dim, nVars);
    phi{3} = expinteg.phiEval(3, LR, N, dim, nVars);
    phi{4} = expinteg.phiEval(4, LR, N, dim, nVars);
    phi{5} = expinteg.phiEval(5, LR, N, dim, nVars);

    % Compute the psi-functions:
    psi{1,2} = expinteg.psiEval(1, C(2), LR, N, dim, nVars);

    % Take real part for diffusive problems (real eigenvalues):
    if ( isreal(L) == 1 )
        phi = cellfun(@(f) real(f), phi, 'UniformOutput', 0);
        psi = cellfun(@(f) real(f), psi, 'UniformOutput', 0);
    end

    % Compute B:
    B{2} = 1/4*phi{2} + 11/12*phi{3} + 3/2*phi{4} + phi{5};

    % Compute U:
    U{2,1} = -3*phi{2} - 5*phi{3} - 3*phi{4};
    U{2,2} = 3/2*phi{2} + 4*phi{3} + 3*phi{4};
    U{2,3} = -1/3*phi{2} - phi{3} - phi{4};

    % Compute V:
    V{1} = -3/2*phi{2} + 1/2*phi{3} + 6*phi{4} + 6*phi{5};
    V{2} = 1/2*phi{2} + 1/3*phi{3} - 3*phi{4} - 4*phi{5};
    V{3} = -1/12*phi{2} - 1/12*phi{3} + 1/2*phi{4} + phi{5};

elseif ( strcmpi(schemeName, 'pecec534') == 1 )

    % Compute C:
    C(1) = 0;
    C(2) = 1;
    C(3) = 1;

    % Compute the phi-functions:
    phi{1} = expinteg.phiEval(1, LR, N, dim, nVars);
    phi{2} = expinteg.phiEval(2, LR, N, dim, nVars);
    phi{3} = expinteg.phiEval(3, LR, N, dim, nVars);
    phi{4} = expinteg.phiEval(4, LR, N, dim, nVars);
    phi{5} = expinteg.phiEval(5, LR, N, dim, nVars);

    % Compute the psi-functions:
    psi{1,2} = expinteg.psiEval(1, C(2), LR, N, dim, nVars);
    psi{1,3} = expinteg.psiEval(1, C(3), LR, N, dim, nVars);

    % Take real part for diffusive problems (real eigenvalues):
    if ( isreal(L) == 1 )
        phi = cellfun(@(f) real(f), phi, 'UniformOutput', 0);
        psi = cellfun(@(f) real(f), psi, 'UniformOutput', 0);
    end

    % Compute A:
    A{3,2} = 1/4*phi{2} + 11/12*phi{3} + 3/2*phi{4} + phi{5};

    % Compute B:
    B{3} = 1/4*phi{2} + 11/12*phi{3} + 3/2*phi{4} + phi{5};

    % Compute U:
    U{2,1} = -3*phi{2} - 5*phi{3} - 3*phi{4};
    U{2,2} = 3/2*phi{2} + 4*phi{3} + 3*phi{4};
    U{2,3} = -1/3*phi{2} - phi{3} - phi{4};

    U{3,1} = -3/2*phi{2} + 1/2*phi{3} + 6*phi{4} + 6*phi{5};
    U{3,2} = 1/2*phi{2} + 1/3*phi{3} - 3*phi{4} - 4*phi{5};
    U{3,3} = -1/12*phi{2} - 1/12*phi{3} + 1/2*phi{4} + phi{5};

    % Compute V:
    V{1} = -3/2*phi{2} + 1/2*phi{3} + 6*phi{4} + 6*phi{5};
    V{2} = 1/2*phi{2} + 1/3*phi{3} - 3*phi{4} - 4*phi{5};
    V{3} = -1/12*phi{2} - 1/12*phi{3} + 1/2*phi{4} + phi{5};

elseif ( strcmpi(schemeName, 'pec625') == 1 )

    % Compute C:
    C(1) = 0;
    C(2) = 1;

    % Compute the phi-functions:
    phi{1} = expinteg.phiEval(1, LR, N, dim, nVars);
    phi{2} = expinteg.phiEval(2, LR, N, dim, nVars);
    phi{3} = expinteg.phiEval(3, LR, N, dim, nVars);
    phi{4} = expinteg.phiEval(4, LR, N, dim, nVars);
    phi{5} = expinteg.phiEval(5, LR, N, dim, nVars);
    phi{6} = expinteg.phiEval(6, LR, N, dim, nVars);

    % Compute the psi-functions:
    psi{1,2} = expinteg.psiEval(1, C(2), LR, N, dim, nVars);

    % Take real part for diffusive problems (real eigenvalues):
    if ( isreal(L) == 1 )
        phi = cellfun(@(f) real(f), phi, 'UniformOutput', 0);
        psi = cellfun(@(f) real(f), psi, 'UniformOutput', 0);
    end

    % Compute B:
    B{2} = 1/5*phi{2} + 5/6*phi{3} + 7/4*phi{4} + 2*phi{5} + phi{6};

    % Compute U:
    U{2,1} = -4*phi{2} - 26/3*phi{3} - 9*phi{4} - 4*phi{5};
    U{2,2} = 3*phi{2} + 19/2*phi{3} + 12*phi{4} + 6*phi{5};
    U{2,3} = -4/3*phi{2} - 14/3*phi{3} - 7*phi{4} - 4*phi{5};
    U{2,4} = 1/4*phi{2} + 11/12*phi{3} + 3/2*phi{4} + phi{5};

    % Compute V:
    V{1} = -2*phi{2} - 1/3*phi{3} + 17/2*phi{4} + 16*phi{5} + 10*phi{6};
    V{2} = phi{2} + 7/6*phi{3} - 11/2*phi{4} - 14*phi{5} - 10*phi{6};
    V{3} = -1/3*phi{2} - 1/2*phi{3} + 7/4*phi{4} + 6*phi{5} + 5*phi{6};
    V{4} = 1/20*phi{2} + 1/12*phi{3} - 1/4*phi{4} - phi{5} - phi{6};

elseif ( strcmpi(schemeName, 'pecec635') == 1 )

    % Compute C:
    C(1) = 0;
    C(2) = 1;
    C(3) = 1;

    % Compute the phi-functions:
    phi{1} = expinteg.phiEval(1, LR, N, dim, nVars);
    phi{2} = expinteg.phiEval(2, LR, N, dim, nVars);
    phi{3} = expinteg.phiEval(3, LR, N, dim, nVars);
    phi{4} = expinteg.phiEval(4, LR, N, dim, nVars);
    phi{5} = expinteg.phiEval(5, LR, N, dim, nVars);
    phi{6} = expinteg.phiEval(6, LR, N, dim, nVars);

    % Compute the psi-functions:
    psi{1,2} = expinteg.psiEval(1, C(2), LR, N, dim, nVars);
    psi{1,3} = expinteg.psiEval(1, C(3), LR, N, dim, nVars);

    % Take real part for diffusive problems (real eigenvalues):
    if ( isreal(L) == 1 )
        phi = cellfun(@(f) real(f), phi, 'UniformOutput', 0);
        psi = cellfun(@(f) real(f), psi, 'UniformOutput', 0);
    end

    % Compute A:
    A{3,2} = 1/5*phi{2} + 5/6*phi{3} + 7/4*phi{4} + 2*phi{5} + phi{6};

    % Compute B:
    B{3} = 1/5*phi{2} + 5/6*phi{3} + 7/4*phi{4} + 2*phi{5} + phi{6};

    % Compute U:
    U{2,1} = -4*phi{2} - 26/3*phi{3} - 9*phi{4} - 4*phi{5};
    U{2,2} = 3*phi{2} + 19/2*phi{3} + 12*phi{4} + 6*phi{5};
    U{2,3} = -4/3*phi{2} - 14/3*phi{3} - 7*phi{4} - 4*phi{5};
    U{2,4} = 1/4*phi{2} + 11/12*phi{3} + 3/2*phi{4} + phi{5};
    U{3,1} = -2*phi{2} - 1/3*phi{3} + 17/2*phi{4} + 16*phi{5} + 10*phi{6};
    U{3,2} = phi{2} + 7/6*phi{3} - 11/2*phi{4} - 14*phi{5} - 10*phi{6};
    U{3,3} = -1/3*phi{2} - 1/2*phi{3} + 7/4*phi{4} + 6*phi{5} + 5*phi{6};
    U{3,4} = 1/20*phi{2} + 1/12*phi{3} - 1/4*phi{4} - phi{5} - phi{6};

    % Compute V:
    V{1} = -2*phi{2} - 1/3*phi{3} + 17/2*phi{4} + 16*phi{5} + 10*phi{6};
    V{2} = phi{2} + 7/6*phi{3} - 11/2*phi{4} - 14*phi{5} - 10*phi{6};
    V{3} = -1/3*phi{2} - 1/2*phi{3} + 7/4*phi{4} + 6*phi{5} + 5*phi{6};
    V{4} = 1/20*phi{2} + 1/12*phi{3} - 1/4*phi{4} - phi{5} - phi{6};

elseif ( strcmpi(schemeName, 'pec726') == 1 )

    % Compute C:
    C(1) = 0;
    C(2) = 1;

    % Compute the phi-functions:
    phi{1} = expinteg.phiEval(1, LR, N, dim, nVars);
    phi{2} = expinteg.phiEval(2, LR, N, dim, nVars);
    phi{3} = expinteg.phiEval(3, LR, N, dim, nVars);
    phi{4} = expinteg.phiEval(4, LR, N, dim, nVars);
    phi{5} = expinteg.phiEval(5, LR, N, dim, nVars);
    phi{6} = expinteg.phiEval(6, LR, N, dim, nVars);
    phi{7} = expinteg.phiEval(7, LR, N, dim, nVars);

    % Compute the psi-functions:
    psi{1,2} = expinteg.psiEval(1, C(2), LR, N, dim, nVars);

    % Take real part for diffusive problems (real eigenvalues):
    if ( isreal(L) == 1 )
        phi = cellfun(@(f) real(f), phi, 'UniformOutput', 0);
        psi = cellfun(@(f) real(f), psi, 'UniformOutput', 0);
    end

    % Compute B:
    B{2} = 1/6*phi{2} + 137/180*phi{3} + 15/8*phi{4} + 17/6*phi{5} + ...
        5/2*phi{6} + phi{7};

    % Compute U:
    U{2,1} = -5*phi{2} - 77/6*phi{3} - 71/4*phi{4} - 14*phi{5} - 5*phi{6};
    U{2,2} = 5*phi{2} + 107/6*phi{3} + 59/2*phi{4} + 26*phi{5} + 10*phi{6};
    U{2,3} = -10/3*phi{2} - 13*phi{3} - 49/2*phi{4} - 24*phi{5} - 10*phi{6};
    U{2,4} = 5/4*phi{2} + 61/12*phi{3} + 41/4*phi{4} + 11*phi{5} + 5*phi{6};
    U{2,5} = -1/5*phi{2} - 5/6*phi{3} - 7/4*phi{4} - 2*phi{5} - phi{6};

    % Compute V:
    V{1} = -5/2*phi{2} - 17/12*phi{3} + 83/8*phi{4} + 57/2*phi{5} + ...
        65/2*phi{6} + 15*phi{7};
    V{2} = 5/3*phi{2} + 47/18*phi{3} - 8*phi{4} - 92/3*phi{5} - ...
        40*phi{6} - 20*phi{7};
    V{3} = -5/6*phi{2} - 19/12*phi{3} + 29/8*phi{4} + 37/2*phi{5} + ...
        55/2*phi{6} + 15*phi{7};
    V{4} = 1/4*phi{2} + 31/60*phi{3} - phi{4} - 6*phi{5} - 10*phi{6} - ...
        6*phi{7};
    V{5} = -1/30*phi{2} - 13/180*phi{3} + 1/8*phi{4} + 5/6*phi{5} + ...
        3/2*phi{6} + phi{7};

elseif ( strcmpi(schemeName, 'pecec736') == 1 )

    % Compute C:
    C(1) = 0;
    C(2) = 1;
    C(3) = 1;

    % Compute the phi-functions:
    phi{1} = expinteg.phiEval(1, LR, N, dim, nVars);
    phi{2} = expinteg.phiEval(2, LR, N, dim, nVars);
    phi{3} = expinteg.phiEval(3, LR, N, dim, nVars);
    phi{4} = expinteg.phiEval(4, LR, N, dim, nVars);
    phi{5} = expinteg.phiEval(5, LR, N, dim, nVars);
    phi{6} = expinteg.phiEval(6, LR, N, dim, nVars);
    phi{7} = expinteg.phiEval(7, LR, N, dim, nVars);

    % Compute the psi-functions:
    psi{1,2} = expinteg.psiEval(1, C(2), LR, N, dim, nVars);
    psi{1,3} = expinteg.psiEval(1, C(3), LR, N, dim, nVars);

    % Take real part for diffusive problems (real eigenvalues):
    if ( isreal(L) == 1 )
        phi = cellfun(@(f) real(f), phi, 'UniformOutput', 0);
        psi = cellfun(@(f) real(f), psi, 'UniformOutput', 0);
    end

    % Compute A:
    A{3,2} = 1/6*phi{2} + 137/180*phi{3} + 15/8*phi{4} + 17/6*phi{5} + ...
        5/2*phi{6} + phi{7};

    % Compute B:
    B{3} = 1/6*phi{2} + 137/180*phi{3} + 15/8*phi{4} + 17/6*phi{5} + ...
        5/2*phi{6} + phi{7};

    % Compute U:
    U{2,1} = -5*phi{2} - 77/6*phi{3} - 71/4*phi{4} - 14*phi{5} - 5*phi{6};
    U{2,2} = 5*phi{2} + 107/6*phi{3} + 59/2*phi{4} + 26*phi{5} + 10*phi{6};
    U{2,3} = -10/3*phi{2} - 13*phi{3} - 49/2*phi{4} - 24*phi{5} - 10*phi{6};
    U{2,4} = 5/4*phi{2} + 61/12*phi{3} + 41/4*phi{4} + 11*phi{5} + 5*phi{6};
    U{2,5} = -1/5*phi{2} - 5/6*phi{3} - 7/4*phi{4} - 2*phi{5} - phi{6};
    U{3,1} = -5/2*phi{2} - 17/12*phi{3} + 83/8*phi{4} + 57/2*phi{5} + ...
        65/2*phi{6} + 15*phi{7};
    U{3,2} = 5/3*phi{2} + 47/18*phi{3} - 8*phi{4} - 92/3*phi{5} - ...
        40*phi{6} - 20*phi{7};
    U{3,3} = -5/6*phi{2} - 19/12*phi{3} + 29/8*phi{4} + 37/2*phi{5} + ...
        55/2*phi{6} + 15*phi{7};
    U{3,4} = 1/4*phi{2} + 31/60*phi{3} - phi{4} - 6*phi{5} - 10*phi{6} - ...
        6*phi{7};
    U{3,5} = -1/30*phi{2} - 13/180*phi{3} + 1/8*phi{4} + 5/6*phi{5} + ...
        3/2*phi{6} + phi{7};

    % Compute V:
    V{1} = -5/2*phi{2} - 17/12*phi{3} + 83/8*phi{4} + 57/2*phi{5} + ...
        65/2*phi{6} + 15*phi{7};
    V{2} = 5/3*phi{2} + 47/18*phi{3} - 8*phi{4} - 92/3*phi{5} - ...
        40*phi{6} - 20*phi{7};
    V{3} = -5/6*phi{2} - 19/12*phi{3} + 29/8*phi{4} + 37/2*phi{5} + ...
        55/2*phi{6} + 15*phi{7};
    V{4} = 1/4*phi{2} + 31/60*phi{3} - phi{4} - 6*phi{5} - 10*phi{6} - ...
        6*phi{7};
    V{5} = -1/30*phi{2} - 13/180*phi{3} + 1/8*phi{4} + 5/6*phi{5} + ...
        3/2*phi{6} + phi{7};

end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% POST-PROCESSING:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Put everything in SCHEMECOEFFS:
schemeCoeffs.A = A;
schemeCoeffs.B = B;
schemeCoeffs.C = C;
schemeCoeffs.U = U;
schemeCoeffs.V = V;

% Compute the missing oefficients using the summation properties of the coeffs:
% (Unless the scheme is ABLAWSON4, LAWSON4 or ETDRK2.)
if ( ~strcmpi(schemeName, 'lawson4') && ~strcmpi(schemeName, 'ablawson4') && ...
     ~strcmpi(schemeName, 'etdrk2') )
    schemeCoeffs = computeMissingCoeffs(K, schemeCoeffs, phi, psi);
end

% Precompute the E quantities:
E = cell(s+1, 1);
for i = 1:s
    E{i} = exp(C(i)*dt*L);
end
E{s+1} = exp(dt*L);
schemeCoeffs.E = E;

% Multiply by time-step:
schemeCoeffs.A = cellfun(@(A) dt*A, schemeCoeffs.A, 'UniformOutput', 0);
schemeCoeffs.B = cellfun(@(B) dt*B, schemeCoeffs.B, 'UniformOutput', 0);
schemeCoeffs.U = cellfun(@(U) dt*U, schemeCoeffs.U, 'UniformOutput', 0);
schemeCoeffs.V = cellfun(@(V) dt*V, schemeCoeffs.V, 'UniformOutput', 0);

end

function schemeCoeffs = computeMissingCoeffs(K, schemeCoeffs, phi, psi)
%COMPUTEMISSINGCOEFFS   Compute the missing oefficients of an EXPINTEG using
%the summation properties of the coefficients.
%   SCHEMECOEFFS = COMPUTEMISSINGCOEFFS(K, SCHEMECOEFFS, PHI, PSI) uses the row
%   summation properties to compute the SCHEMECOEFFS A{i,1} and B{1} of the
%   EXPINTEG K, using and the phi- and psi-functions.

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

% Get the coefficients:
A = schemeCoeffs.A;
B = schemeCoeffs.B;
U = schemeCoeffs.U;
V = schemeCoeffs.V;

% Number of internal stages S and number of steps used Q:
s = K.stages;
q = K.steps;

% Compute the coefficients A{i,1} using the row summation property:
for i = 2:s
    A{i,1} = psi{1,i};
    for j = 2:i-1
        if ( isempty(A{i,j}) == 0 )
            A{i,1} = A{i,1} - A{i,j};
        end
    end
    for j = 1:q-1
        if ( isempty(U{i,j}) == 0 )
            A{i,1} = A{i,1} - U{i,j};
        end
    end
end

% Compute the coefficient B{1} using the row summation property:
B{1} = phi{1};
for i = 2:s
    if ( isempty(B{i}) == 0 )
        B{1} = B{1} - B{i};
    end
end
for i = 1:q-1
    if ( isempty(V{i}) == 0 )
        B{1} = B{1} - V{i};
    end
end

% Put everything in SCHEMECOEFFS:
schemeCoeffs.A = A;
schemeCoeffs.B = B;

end


function [UAV] = UAV_SetUp2
%UAV_SETUP2 在此设置多无人机协同航迹规划任务
% 论文1的环境

% 航迹点设置
% （每行为一个无人机的参数）
UAV.S = [  0,           0;
                  0,           100;
                  300,       0;      ];     % 起点位置 (x,y)或(x,y,z)

UAV.G = [  875,      875;
                  800,       875;
                  875,       800;  ];      % 目标位置 (x,y)或(x,y,z)

UAV.PointNum = [  26;
                                  24;
                                  24;  ];                 % 每个无人机导航点个数（起始点之间点的个数）

UAV.PointDim = size(UAV.S, 2);        % 坐标点维度 （由 起点 坐标决定）
UAV.num = size(UAV.S, 1);                 % UAV数量 （由 起点 个数决定）

% 威胁点设置 (x,y,r) 或 (x,y,z,r)
% （每行为一个威胁的坐标和半径）
UAV.Menace.radar = [  200, 200,    20;
                                        600, 700,    20;  ];  % 雷达威胁（数学模型和其余威胁不同）

UAV.Menace.other = [  80, 40,   40;
                                        300, 300,   40;
                                        350, 600,    40;
                                        480, 450,    20;
                                        700, 700,    40;
                                        720, 760,    20;
                                        680, 760,    20;
                                        200, 400,    40;
                                        200, 520,    40;
                                        200, 600,    20;
                                        500, 200,    40;
                                        700, 200,    40;  ];   % 导弹、火炮、气象等威胁

% 无人机约束设置（min,max)
% （可单独为每个无人机设置，每行为一个无人机约束的上下限）
UAV.limt.v = 0.34*repmat([0.3, 0.7], UAV.num, 1);                 % 速度约束 （0.3Ma ~ 0.7Ma）
UAV.limt.phi = deg2rad(repmat([-60, 60], UAV.num, 1));      % 偏角约束 （-60° ~ 60°）
UAV.limt.theta = deg2rad(repmat([-45, 45], UAV.num, 1));   % 倾角约束 （-45° ~ 45°）
UAV.limt.h = repmat([0.02, 20], UAV.num, 1);                         % 高度约束 （0.02km ~ 20km）
UAV.limt.x = repmat([0, 875], UAV.num, 1);                            % 位置x约束 （0 ~ 875km）
UAV.limt.y = repmat([0, 875], UAV.num, 1);                            % 位置y约束 （0 ~ 875km）
UAV.limt.z = UAV.limt.h;                                                            % 位置z约束 （忽略地球弧度）
UAV.limt.L = zeros(UAV.num, 2);                                              % 航程约束 （最短航迹片段2km，最大航程1.5倍起始距离）
for i =1:UAV.num
    zz.max = 1.5 * norm(UAV.G(i, :) - UAV.S(i, :));
    zz.min = 2;
    UAV.limt.L(i, :) = [zz.min, zz.max];
end

% 多无人机协同设置
% （说明略）
UAV.tc = 6850;        % 协同时间 （单位s）
UAV.ds = 25;           % 安全距离 （单位km）

% 报错
ErrorCheck(UAV)
end

%% 程序自检
function ErrorCheck(UAV)

dim = UAV.PointDim;
if dim ~= size(UAV.G,2) || dim ~= size(UAV.Menace.radar,2)-1 || dim ~= size(UAV.Menace.other,2)-1
    if dim ~= size(UAV.G,2)
        error('仿真维度为%d，但目标点坐标为%d维', dim, size(UAV.G,2))
    else
        error('仿真维度为%d，但威胁点坐标为%d维', dim, size(UAV.Menace.radar,2)-1)
    end
end

num = UAV.num;
if num ~= size(UAV.G,1) || num ~= size(UAV.limt.v,1) || num ~= size(UAV.limt.phi,1) ...
        || num ~= size(UAV.limt.theta,1) || num ~= size(UAV.limt.h,1) || num ~= size(UAV.limt.x,1) ...
        || num ~= size(UAV.limt.y,1) || num ~= size(UAV.limt.z,1) || num ~= size(UAV.limt.L,1)
    if num ~= size(UAV.G,1)
        error('无人机个数为%d, 但目标点有%d个', num, size(UAV.G,1))
    else
        error('约束条件个数与无人机个数不一致')
    end
end

if num ~= size(UAV.PointNum, 1)
    error('无人机个数为%d, 但为%d个无人机设置了导航点', num, size(UAV.PointNum, 1))
end

MaxPoint = floor(UAV.limt.L(:,2) ./ UAV.limt.L(:,1)) - 1;   % 每个无人机支持的最大航迹点数量
for i = 1 : UAV.num
    if UAV.PointNum(i) > MaxPoint(i)
        error('%d号无人机导航点个数超出任务需求，请尝试减少导航点个数', i)
    end
end

end


function [p] = dot(tt1,tt2,chunk_start,chunk_end,do_qr)
%Dot  product of two TT tensors
%   [PR]=DOT(TT1,TT2) -- dot product of two TT-tensors
%
%   [PR]=DOT(TT1,TT2, DO_QR) if DO_QR==true is specified, perform the
%   left-to-right QRs of TT1,TT2
%   before the scalar product. It increases the  accuracy in some cases.
%
% In general, returns a 4D tensor of sizes
% r0(tt1), r0(tt2), rd(tt1), rd(tt2)
% If r0(tt1) = r0(tt2) = 1 it returns a matrix of size rd(tt1) x rd(tt2)
%
%   [PR]=DOT(TT1,TT2,CHUNK_START,CHUNK_END[,DO_QR]) If TT2.d>TT1.d, returns a
%   tt_tensor obtained from TT2 by projecting its chunk from CHUNK_START to
%   CHUNK_END onto TT1. Convenient for extracting slices.
%
%
% TT-Toolbox 2.2, 2009-2012
%
%This is TT Toolbox, written by Ivan Oseledets et al.
%Institute of Numerical Mathematics, Moscow, Russia
%webpage: http://spring.inm.ras.ru/osel
%
%For all questions, bugs and suggestions please mail
%ivan.oseledets@gmail.com
%---------------------------

if (nargin<3)
    do_qr = false;
end;
if (nargin==3)
    do_qr = chunk_start;
end;
if (nargin>3) % dot is only applied to a chunk of tt2
    if (nargin<5)
        do_qr = false;
    end;
    if (tt2.d>tt1.d)
        % chunk_start and chunk_end refer to tt1
        tt2_chunk1 = chunk(tt2, chunk_start, chunk_end);
        D = dot(tt1, tt2_chunk1, do_qr);
        D = reshape(D, tt2.r(chunk_start), tt2.r(chunk_end+1));
        p = [];
        if (chunk_start>1)
            tt2_chunk1 = chunk(tt2, 1, chunk_start-1);
            p = tt2_chunk1*D;
        end;
        if (chunk_end<tt2.d)
            tt2_chunk2 = chunk(tt2, chunk_end+1, tt2.d);
            if (isempty(p))
                p = D*tt2_chunk2;
            else
                p = tkron(p, tt2_chunk2);
            end;
        end;
    else
        error('chunky dot is defined only if tt2.d>tt1.d');
    end;
    return;
end;

if (do_qr)
    [tt1,rv1]=qr(tt1, 'lr');
    [tt2,rv2]=qr(tt2, 'lr');
end;

d=tt1.d;
r1=tt1.r; r2=tt2.r; ps1=tt1.ps; ps2=tt2.ps;
n=tt1.n;
core1=tt1.core; core2=tt2.core;

%ps is always r1(i-1)xr; but if there is a hanging thing? what to do?
%That means, I define a dot product of two "hanging" tensors as a matrix...
%brr.... And if it is hanging on the right?
%
% p=ones(r1(1),r2(1)); % Over r1(1) and r2(1) there is not summation blin.
% %So, if we just sum over n(1) separatedly and leave a strange QxR1(I)xR2(I)
% %matrix...
% for i=1:d
%   cr1=core1(ps1(i):ps1(i+1)-1);
%   cr2=core2(ps2(i):ps2(i+1)-1);
%   p=reshape(p,[r1(i),r2(i)]);
%   cr2=reshape(cr2,[r2(i),numel(cr2)/r2(i)]);
%   p=p*cr2; %p is Q*r1(i)xn(i)xr2(i+1);
%   cr1=reshape(cr1,[r1(i)*n(i),numel(cr1)/(r1(i)*n(i))]);
%   p=reshape(p,[r1(i)*n(i),numel(p)/(r1(i)*n(i))]);
%   p=cr1'*p;
% end

% If the first indices are not ones
p=eye(r1(1)*r2(1));
p = reshape(p, r1(1)*r2(1)*r1(1), r2(1));

for i=1:d
    cr1=core1(ps1(i):ps1(i+1)-1);
    cr2=core2(ps2(i):ps2(i+1)-1);
    cr2=reshape(cr2,[r2(i), n(i)*r2(i+1)]);

    p = p*cr2; % size r11*r21*r1-, n*r2+
    p = reshape(p,r1(1)*r2(1), r1(i)*n(i), r2(i+1));
    p = permute(p, [1, 3, 2]);
    p = reshape(p, r1(1)*r2(1)*r2(i+1), r1(i)*n(i));

    cr1=reshape(cr1,[r1(i)*n(i), r1(i+1)]);

    p = p*conj(cr1); % size r11*r12*r2+, r1+
    p = reshape(p, r1(1)*r2(1), r2(i+1), r1(i+1));
    p = permute(p, [1, 3, 2]);
    p = reshape(p, r1(1)*r2(1)*r1(i+1), r2(i+1));
end;

if (do_qr)
    r2old = size(rv2, 2);
    r1old = size(rv1,2);
    p = p*rv2;
    p = reshape(p, r1(1)*r2(1), r1(d+1), r2old);
    p = permute(p, [1, 3, 2]);
    p = reshape(p, r1(1)*r2(1)*r2old, r1(d+1));
    p = p*conj(rv1);
    p = reshape(p, r1(1), r2(1), r2old, r1old);
    p = permute(p, [1,2,4,3]);
    if ( r1(1)*r2(1) == 1 ) %Save the rabbits
        p=reshape(p, r1old, r2old);
    end
else
    p = reshape(p, r1(1), r2(1), r1(d+1), r2(d+1));
    if ( r1(1)*r2(1) == 1 ) %Save the rabbits
        p=reshape(p, r1(d+1), r2(d+1));
    end
end;
end


% rf_blochSim.m
% Jamie Near, McGill University 2014.
%
% USAGE:
% [mv,sc]=rf_blochSim(RF,tp,fspan,f0,peakB1,ph,M0);
%
% DESCRIPTION:
% Perform a bloch simulation of an RF pulse.  This code simply runs Martyn
% Klassen's excellent bloch equation simulator.  For more information, see
% help file for bes.m.  (~FID-A/rfPulseTools/mklassenTools/bes.m).
%
% INPUTS:
% RF        = RF pulse definition structure
% tp        = pulse duration in [ms]
% fspan     = Frequency span in [kHz] or, if the RF pulse includes a gradient
%             waveform (as indicated by having a 4th  column), then fspan
%             is the span of spatial positions in [cm] (optional.  Default=
%             10kHz or 10cm).
% f0        = Centre of frequnecy span [kHz] (optional.  Default=0)
% peakB1 	= Peak B1 amplitude in [kHz] (optional.  Default=RF.tw1/tp)
% ph        = Starting phase of the rf pulse [degrees] (optional. Default=0)
% M0        = Starting magnetization [units of M0] (optional. Default=[0,0,1])
%
% OUTPUTS:
% mv        = Simulated magnetization vector in three columns (x,y,z) as a
%             function of frequency.
% sc        = Frequency scale (in kHz), or if the pulse include a gradient
%             waveform, the position scale (in cm) corresponding to the
%             simulated mv vectors.

function [mv,sc]=rf_blochSim(RF,tp,fspan,f0,peakB1,ph,M0);

if nargin<7
    M0=[0,0,1];
    if nargin<6
        ph=0;
        if nargin<5
            peakB1=RF.tw1/tp;
            if nargin<4
                f0=0;
                if nargin<3
                    fspan=10;
                end
            end
        end
    end
end

[mv,sc]=bes(RF.waveform,tp,'f',peakB1,f0-fspan/2,f0+fspan/2,10000,ph,M0);

figure
subplot(4,1,1),plot(sc,mv(1,:),'LineWidth',1.2);
box off;
ylabel('M_x');

subplot(4,1,2),plot(sc,mv(2,:),'LineWidth',1.2);
box off;
ylabel('M_y');

subplot(4,1,3),plot(sc,sqrt(mv(2,:).^2 + mv(1,:).^2),'LineWidth',1.2);
box off;
ylabel('M_x_y');

subplot(4,1,4),plot(sc,mv(3,:),'LineWidth',1.2);
box off;
ylabel('M_z');

if size(RF.waveform,2)<4
    xlabel('Frequency (kHz)');
else
    xlabel('Position (cm)');
end

set(gcf,'color','w');


function CPD = hhmmF_CPD(bnet, self, Qnodes, d, D, varargin)
% HHMMF_CPD Make the CPD for an F node at depth D of a D-level hierarchical HMM
% CPD = hhmmF_CPD(bnet, self, Qnodes, d, D, ...)
%
%    Q(d-1)
%          \
%           \
%           F(d)
%         /   |
%        /    |
%    Q(d)  F(d+1)
%
% We assume nodes are ordered (numbered) as follows:
% Q(1), ... Q(d), F(d+1), F(d)
%
% F(d)=2 means level d has finished. The prob this happens depends on Q(d)
% and optionally on Q(d-1), Q(d=1), ..., Q(1).
% Also, level d can only finish if the level below has finished
% (hence the F(d+1) -> F(d) arc).
%
% If d=D, there is no F(d+1), so F(d) is just a regular tabular_CPD.
% If all models always finish in the same state (e.g., their last),
% we don't need to condition on the state of parent models (Q(d-1), ...)
%
% optional args [defaults]
%
% termprob - termprob(k,i,2) = prob finishing given Q(d)=i and Q(1:d-1)=k [ finish in last state ]
%
% hhmmF_CPD is a subclass of tabular_CPD so we inherit inference methods like CPD_to_pot, etc.
%
% We create an isolated tabular_CPD with no F parent to learn termprob
% so we can avail of e.g., entropic or Dirichlet priors.
%
% For details, see "Linear-time inference in hierarchical HMMs", Murphy and Paskin, NIPS'01.

ps = parents(bnet.dag, self);
Qps = myintersect(ps, Qnodes);
F = mysetdiff(ps, Qps);
CPD.Q = Qps(end); % Q(d)
assert(CPD.Q == Qnodes(d));
CPD.Qps = Qps(1:end-1); % all Q parents except Q(d), i.e., calling context

ns = bnet.node_sizes(:);
CPD.Qsizes = ns(Qnodes);
CPD.d = d;
CPD.D = D;

Qsz = ns(CPD.Q);
Qpsz = prod(ns(CPD.Qps));

% set default arguments
p = 0.9;
%termprob(k,i,t) Might terminate if i=Qsz; will not terminate if i<Qsz
termprob = zeros(Qpsz, Qsz, 2);
termprob(:, Qsz, 2) = p;
termprob(:, Qsz, 1) = 1-p;
termprob(:, 1:(Qsz-1), 1) = 1;

for i=1:2:length(varargin)
  switch varargin{i},
   case 'termprob', termprob = varargin{i+1};
   otherwise, error(['unrecognized argument ' varargin{i}])
  end
end

ps = [CPD.Qps CPD.Q];
% ns(self) = 2 since this is an F node
CPD.sub_CPD_term = mk_isolated_tabular_CPD(ps, ns([ps self]), {'CPT', termprob});
S = struct(CPD.sub_CPD_term);
CPD.termprob = S.CPT;

CPD = class(CPD, 'hhmmF_CPD', tabular_CPD(bnet, self));

CPD = update_CPT(CPD);


function [C, Cinv] = gp_dcov(gp, x1, x2, predcf)
%GP_COV  Evaluate covariance matrix between two input vectors.
%
%  Description
%    C = GP_DCOV(GP, X1, X2, PREDCF) takes in Gaussian process GP and
%    two matrixes X1 and X2 that contain input vectors to GP.
%    Returns covariance matrix C between the latent values in the training
%    inputs X1 & XV and the test inputs X2. XV denotes the virtual input we
%    have (GP.XV). The covariance is computed between latent vector [f
%    df/dx_1 df/dx_2, ... df/dx_d], evaluated at X1 for f and XV for df/dx
%    respectively, and [f df/dx_1 df/dx_2, ... df/dx_d] evaluated at X2
%    for both the f and its gradient.
%
% Copyright (c) 2007-2010 Jarno Vanhatalo
% Copyright (c) 2010 Tuomas Nikoskinen
% Copyright (c) 2014 Ville Tolvanen

% This software is distributed under the GNU General Public
% License (version 3 or later); please refer to the file
% License.txt, included with the software, for details.

% Split the training data for normal latent input and gradient inputs
x12=x1;
x11=gp.xv;

% Derivative observations
[n,m]=size(x1);
[n4,m4]=size(x2);
ncf=length(gp.cf);
if isfield(gp, 'nvd')
  % Only specific dimensions
  ii1=abs(gp.nvd);
else
  % All dimensions
  ii1=1:m;
end
for i1=1:ncf
  gpcf = gp.cf{i1};
  if m==1
    Gset1 = gpcf.fh.ginput4(gpcf, x11, x2);
    Gset2 = gpcf.fh.ginput4(gpcf, x2, x12);
    Kff = gpcf.fh.cov(gpcf, x12, x2);
    Kdd = gpcf.fh.ginput2(gpcf, x11, x2);

    Kdf=Gset1{1};
    Kfd=Gset2{1};
    C = [Kff Kfd'; Kdf Kdd{1}];

    % Input dimension is >1
  else
    [n,m]=size(x11);
    [n2,m2]=size(x2);

    Kff = gpcf.fh.cov(gpcf, x12, x2);
    Gset1 = gpcf.fh.ginput4(gpcf, x11,x2);
    Gset2 = gpcf.fh.ginput4(gpcf, x2, x12);

    %Gather matrices from Gset (d k(x1,x2) /d x1)
    Kfd=cat(2,Gset1{ii1});
    Kdf=cat(1,Gset1{ii1});
    Kfd22=cat(2,Gset2{ii1});
    Kdf22=cat(1,Gset2{ii1})';

    % both x derivatives, same dimension (to diagonal blocks)
    D = gpcf.fh.ginput2(gpcf, x11, x2);
    % both x derivatives, different dimension (non-diagonal blocks)
    Kdf2 = gpcf.fh.ginput3(gpcf, x11 ,x2);

    % Now build up Kdd m*n x m*n2 matrix, which contains all the
    % both partial derivative" -matrices

    % Add the diagonal matrices
    Kdd=blkdiag(D{1:m});
    % Add the non-diagonal matrices to Kdd
    ii3=0;
    for j=0:m-2
      for i=1+j:m-1
        ii3=ii3+1;
        Kdd(i*n+1:(i+1)*n,j*n2+1:j*n2+n2) = Kdf2{ii3};
        Kdd(j*n+1:j*n+n,i*n2+1:(i+1)*n2) = Kdf2{ii3};
      end
    end
    if isfield(gp, 'nvd')
      % Collect the correct gradient dimensions, i.e. select the blocks
      % that correspond to the input dimensions for which we want the
      % gradients to be monotonic
      Kddtmp=[];
      for ii2=1:length(ii1)
        for ii3=ii2:length(ii1)
          Kddtmp((ii2-1)*n+1:ii2*n, (ii3-1)*n2+1:ii3*n2) = ...
            Kdd((ii1(ii2)-1)*n+1:ii1(ii2)*n,(ii1(ii3)-1)*n2+1:ii1(ii3)*n2);
          if ii2~=ii3
            Kddtmp((ii3-1)*n+1:ii3*n, (ii2-1)*n2+1:ii2*n2) = ...
              Kdd((ii1(ii3)-1)*n+1:ii1(ii3)*n,(ii1(ii2)-1)*n2+1:ii1(ii2)*n2);
          end
        end
      end
      Kdd=Kddtmp;
    end

    % Gather all the matrices into one final matrix K which is the
    % training covariance matrix
    C = [Kff Kdf22; Kdf Kdd];
    %   C = [Kff; Kdf];
  end
  if i1==1
    CC=C;
  else
    CC=CC+C;
  end
end
C=CC;