function varargout = process_test_parametric2( varargin )
% PROCESS_TEST_PARAMETRIC2: Parametric two-sample tests (independent).
%
% USAGE:  OutputFiles = process_test_parametric2('Run', sProcess, sInput)
%                   p = process_test_parametric2('ComputePvalues', t, df, TestType='t', TestTail='two')

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

eval(macro_method);
end

%% ===== GET DESCRIPTION =====
function sProcess = GetDescription() %#ok<DEFNU>
    % Description the process
    sProcess.Comment     = 'Parametric test: Independent';
    sProcess.Category    = 'Stat2';
    sProcess.SubGroup    = 'Test';
    sProcess.Index       = 101;
    sProcess.Description = 'https://neuroimage.usc.edu/brainstorm/Tutorials/Statistics';
    % Definition of the input accepted by this process
    sProcess.InputTypes  = {'data',  'results',  'timefreq',  'matrix'};
    sProcess.OutputTypes = {'pdata', 'presults', 'ptimefreq', 'pmatrix'};
    sProcess.nInputs     = 2;
    sProcess.nMinFiles   = 2;

    % === GENERIC EXTRACT OPTIONS
    % Label
    sProcess.options.extract_title.Comment    = 'Select data to test:';
    sProcess.options.extract_title.Type       = 'label';
    % Options
    sProcess = process_extract_values('DefineExtractOptions', sProcess);
    % DISABLE ABSOLUTE VALUE
    sProcess.options.isabs.Value = 0;
    sProcess.options.isnorm.Value = 0;
    sProcess.options.isabs.Hidden = 1;
    sProcess.options.isnorm.Hidden = 1;

    % === EXCLUDE ZERO VALUES
    sProcess.options.iszerobad.Comment    = 'Exclude the zero values from the computation';
    sProcess.options.iszerobad.Type       = 'checkbox';
    sProcess.options.iszerobad.Value      = 1;
    sProcess.options.iszerobad.InputTypes = {'timefreq', 'matrix'};
    % === OUTPUT COMMENT
    sProcess.options.Comment.Comment = 'Comment (empty=default): ';
    sProcess.options.Comment.Type    = 'text';
    sProcess.options.Comment.Value   = '';

    % === TEST: title
    sProcess.options.test_title.Comment    = '
Test statistic:';
    sProcess.options.test_title.Type       = 'label';
    % === TEST: type
    sProcess.options.test_type.Comment = {['Student''s t-test   (equal variance)        A,B~N(m,v)
' ...
                                           't = (mean(A)-mean(B)) / (Sx * sqrt(1/nA + 1/nB))
' ...
                                           'Sx = sqrt(((nA-1)*var(A) + (nB-1)*var(B)) / (nA+nB-2))
' ...
                                           'df = nA + nB - 2'], ...
                                          ['Student''s t-test   (unequal variance)        A,B~N(m,v)
', ...
                                           't = (mean(A)-mean(B)) / sqrt(var(A)/nA + var(B)/nB)
' ...
                                           'df=(vA/nA+vB/nB)2 / ((vA/nA)2/(nA-1)+(vB/nB)2/(nB-1))'], ...
                                          ['Power F-test        A~N(0,vA), B~N(0,vB)
', ...
                                           'F = (sum(A^2)/nA) / (sum(B^2)/nB)      ~F(nA,nB)'], ...
                                          ['Power F-test (unconstrained sources)
', ...
                                           'F = (sum(Ax2+Ay2+Az2)/nA) / (sum(Bx2+By2+Bz2)/nB)
', ...
                                           'Ax,Ay,Az~N(0,vA), Bx,By,Bz~N(0,vB), F~F(3*nA,3*nB)']; ...
                                          'ttest_equal', 'ttest_unequal', 'power', 'power_unconstr'};
    sProcess.options.test_type.Type    = 'radio_label';
    sProcess.options.test_type.Value   = 'ttest_equal';
    % === TAIL FOR THE TEST STATISTIC
    sProcess.options.tail.Comment  = {'One-tailed (-)', 'Two-tailed', 'One-tailed (+)', ''; ...
                                      'one-', 'two', 'one+', ''};
    sProcess.options.tail.Type     = 'radio_linelabel';
    sProcess.options.tail.Value    = 'two';
end

%% ===== FORMAT COMMENT =====
function Comment = FormatComment(sProcess) %#ok<DEFNU>
    % === DATA SELECTION COMMENT ===
    strData = process_extract_time('GetTimeString', sProcess);
    if isfield(sProcess.options, 'freqrange') && isfield(sProcess.options.freqrange, 'Value') && iscell(sProcess.options.freqrange.Value) && (length(sProcess.options.freqrange.Value) == 3) && (length(sProcess.options.freqrange.Value{1}) == 2)
        FreqRange = sProcess.options.freqrange.Value{1};
        if (FreqRange(1) == FreqRange(2))
            strData = [strData, ' ' num2str(FreqRange(1)) 'Hz'];
        else
            strData = [strData, ' ' num2str(FreqRange(1)) '-' num2str(FreqRange(2)) 'Hz'];
        end
    end
    if isfield(sProcess.options, 'sensortypes') && isfield(sProcess.options.sensortypes, 'Value') && ~isempty(sProcess.options.sensortypes.Value)
        strData = [strData, ' ', sProcess.options.sensortypes.Value];
    end
    if isfield(sProcess.options, 'rows') && isfield(sProcess.options.rows, 'Value') && ~isempty(sProcess.options.rows.Value)
        strData = [strData, ' ', sProcess.options.rows.Value];
    end
    if ~isempty(strData)
        strData = [' [' strData ']'];
    end

    % === ABSOLUTE VALUE ===
    % Get options
    if isfield(sProcess.options, 'isabs') && isfield(sProcess.options.isabs, 'Value') && sProcess.options.isabs.Value
        isAbsolute = 1;
        strAbs = ' abs';
    elseif isfield(sProcess.options, 'isnorm') && isfield(sProcess.options.isnorm, 'Value') && sProcess.options.isnorm.Value
        isAbsolute = 1;
        strAbs = ' norm';

    else
        isAbsolute = 0;
        strAbs = '';
    end

    % === TEST COMMENT ===
    % Get test info
    if isfield(sProcess.options, 'test_type') && isfield(sProcess.options.test_type, 'Value') && ~isempty(sProcess.options.test_type.Value)
        TestType = sProcess.options.test_type.Value;
    else
        TestType = 'ttest_unequal';
    end
    if isfield(sProcess.options, 'tail') && isfield(sProcess.options.tail, 'Value') && ~isempty(sProcess.options.tail.Value)
        TestTail = sProcess.options.tail.Value;
    else
        TestTail = [];
    end
    % Documenting test to perform
    switch (TestType)
        case {'ttest_equal', 'ttest_unequal', 'ttest_paired', 'wilcoxon_paired'}   % , 'wilcoxon'
            strHypo = '          H0:(A=B)';
            switch(TestTail)
                case 'one-',   strHypo = [strHypo, ', H1:(A<B)'];
                case 'two',    strHypo = [strHypo, ', H1:(A<>B)'];
                case 'one+',   strHypo = [strHypo, ', H1:(A>B)'];
            end
        case 'signtest'
            strHypo = '         H0:(A=B), H1:(A<>B)';
        case 'ttest_onesample'
            strHypo = '          H0:(X=0)';
            switch(TestTail)
                case 'one-',   strHypo = [strHypo, ', H1:(X<0)'];
                case 'two',    strHypo = [strHypo, ', H1:(X<>0)'];
                case 'one+',   strHypo = [strHypo, ', H1:(X>0)'];
            end
        case 'ttest_baseline'
            strHypo = '          H0:(X=Baseline)';
            switch(TestTail)
                case 'one-',   strHypo = [strHypo, ', H1:(X<Baseline)'];
                case 'two',    strHypo = [strHypo, ', H1:(X<>Baseline)'];
                case 'one+',   strHypo = [strHypo, ', H1:(X>Baseline)'];
            end
        case {'power_baseline', 'power_baseline_unconstr'}
            strHypo = '          H0:(|X|=|Baseline|)';
            switch(TestTail)
                case 'one-',   strHypo = [strHypo, ', H1:(|X|<|Baseline|)'];
                case 'two',    strHypo = [strHypo, ', H1:(|X|<>|Baseline|)'];
                case 'one+',   strHypo = [strHypo, ', H1:(|X|>|Baseline|)'];
            end
        case 'chi2_onesample'
            strHypo = '          H0:(|Zi| = 0)';
        case 'chi2_onesample_unconstr'
            strHypo = '          H0:(|Zi| = 0)';
        case {'power', 'power_unconstr'}
            strHypo = '          H0:(vA=vB)';
            switch(TestTail)
                case 'one-',   strHypo = [strHypo, ', H1:(vA<vB)'];
                case 'two',    strHypo = [strHypo, ', H1:(vA<>vB)'];
                case 'one+',   strHypo = [strHypo, ', H1:(vA>vB)'];
            end
        case {'absmean', 'absmean_param'}
            strHypo = '          H0:(|mean(A)|=|mean(B)|)';
            switch(TestTail)
                case 'one-',   strHypo = [strHypo, ', H1:(|mean(A)|<|mean(B)|)'];
                case 'two',    strHypo = [strHypo, ', H1:(|mean(A)|<>|mean(B)|)'];
                case 'one+',   strHypo = [strHypo, ', H1:(|mean(A)|>|mean(B)|)'];
            end
    end

    % No comment when forcing a one-sided test
    if ismember(TestType, {'ttest_onesample'}) && isAbsolute
        strTail = '';
    elseif strcmpi(TestType, 'chi2_onesample') || strcmpi(TestType, 'chi2_onesample_unconstr')
        strTail = '';
        strAbs = '';
    elseif strcmpi(TestType, 'signtest')
        strTail = '';
    % Comment for one-tailed tests
    elseif ismember(TestTail, {'one-','one+'})
        strTail = [' ' TestTail];
    else
        strTail = '';
    end

    % === ASSEMBLING ===
    switch (TestType)
        case 'ttest_equal',      Comment = ['t-test equal'     strTail strAbs strData strHypo];
        case 'ttest_unequal',    Comment = ['t-test unequal'   strTail strAbs strData strHypo];
        case 'ttest_paired',     Comment = ['t-test paired'    strTail strAbs strData strHypo];
        case 'ttest_onesample',  Comment = ['t-test zero'      strTail strAbs strData strHypo];
        case 'ttest_baseline',   Comment = ['t-test baseline'  strTail strAbs strData strHypo];
        case 'power_baseline',   Comment = ['power test baseline'  strTail strData strHypo];
        case 'power_baseline_unconstr',   Comment = ['power test baseline unconstr' strTail strData strHypo];
        case 'signtest',         Comment = ['signtest'         strAbs strData strHypo];
        case 'wilcoxon_paired',  Comment = ['wilcoxon paired ' strTail strAbs strData strHypo];
        % case 'wilcoxon',         Comment = ['wilcoxon'       strTail strAbs strData strHypo];
        case 'power',            Comment = ['power test'       strTail strData strHypo];
        case 'power_unconstr',   Comment = ['power test unconstr' strTail strData strHypo];
        case 'chi2_onesample',   Comment = ['Chi2-test'        strTail strData strHypo];
        case 'chi2_onesample_unconstr',  Comment = ['Chi2-test unconstr' strTail strData strHypo];
        case 'absmean',          Comment = ['absmean test'     strTail strData strHypo];
        case 'absmean_param',    Comment = ['absmean test'     strTail strData strHypo];
    end
end

%% ===== RUN =====
function sOutput = Run(sProcess, sInputsA, sInputsB) %#ok<DEFNU>
    % Initialize returned variables
    sOutput = [];

    % ===== GET OPTIONS =====
    % Get generic extract options
    OPTIONS = process_extract_values('GetExtractOptions', sProcess, sInputsA(1));
    % Exclude zero values
    if isfield(sProcess.options, 'iszerobad') && isfield(sProcess.options.iszerobad, 'Value') && ~isempty(sProcess.options.iszerobad.Value)
        OPTIONS.isZeroBad = sProcess.options.iszerobad.Value;
    else
        OPTIONS.isZeroBad = 1;
    end
    % Get test type
    OPTIONS.TestType = sProcess.options.test_type.Value;
    OPTIONS.TestTail = sProcess.options.tail.Value;
    % Invalid test/tail combinations
    if ismember(OPTIONS.TestType, {'ttest_onesample'}) && OPTIONS.isAbsolute && ismember(OPTIONS.TestTail, {'two', 'one-'})
        bst_report('Warning', sProcess, [], 'Testing |X|>0: Using a positive one-tailed test (one+) instead.');
        OPTIONS.TestTail = 'one+';
    elseif ismember(OPTIONS.TestType, {'chi2_onesample', 'chi2_onesample_unconstr'}) && ismember(OPTIONS.TestTail, {'two', 'one-'})
        bst_report('Warning', sProcess, [], 'Testing |X|>0: Using a positive one-tailed test (one+) instead.');
        OPTIONS.TestTail = 'one+';
    end
    % Time-frequency: Warning if processing power
    isTfPower = false;
    if strcmpi(sInputsA(1).FileType, 'timefreq')
        TfMat = in_bst_timefreq(sInputsA(1).FileName, 0, 'Measure');
        if isequal(TfMat.Measure, 'power')
            isTfPower = true;
        end
    end
    % Get average function
    switch (OPTIONS.TestType)
        case {'ttest_equal', 'ttest_unequal', 'ttest_onesample', 'ttest_paired', 'ttest_baseline', 'absmean', 'absmean_param'}
            if isTfPower
                bst_report('Warning', sProcess, [], ['You are testing power values, while a more standard analysis is to test the magnitude (ie. sqrt(power)).' 10 ...
                    'Option #1: Recompute the time-frequency maps using the option "Measure: Magnitude".' 10 ...
                    'Option #2: Run the process "Extract > Measure from complex values", with option "Magntiude".']);
                isTfPower = false;
            end
            if OPTIONS.isAbsolute
                AvgFunction = 'norm';
                isAvgVariance = 1;
            else
                AvgFunction = 'mean';
                isAvgVariance = 1;
            end
        case {'power', 'power_baseline', 'power_baseline_unconstr', 'power_unconstr', 'chi2_onesample', 'chi2_onesample_unconstr'}
            AvgFunction = 'rms';
            isAvgVariance = 0;
    end
    isAvgWeighted = 0;
    % Baseline: Only for test against baseline
    if isfield(sProcess.options, 'baseline') && isfield(sProcess.options.baseline, 'Value') && iscell(sProcess.options.baseline.Value) && ~isempty(sProcess.options.baseline.Value) && ~isempty(sProcess.options.baseline.Value{1})
        Baseline = sProcess.options.baseline.Value{1};
    else
        Baseline = [];
    end
    % Unconstrained chi2 test: force the computation of norm
    if ismember(OPTIONS.TestType, {'chi2_onesample_unconstr', 'power_baseline_unconstr'})
        OPTIONS.isAbsolute = 1;
    end

    % ===== CHECK INPUT FILES =====
    % Make sure that file type is indentical for both sets
    if ~isempty(sInputsA) && ~isempty(sInputsB) && ~strcmpi(sInputsA(1).FileType, sInputsB(1).FileType)
        bst_report('Error', sProcess, [], 'Cannot process inputs from different types.');
        return;
    end
    % Check the number of files in input
    if (length(sInputsA) < 2) && ~strcmpi(OPTIONS.TestType, 'ttest_baseline')
        bst_report('Error', sProcess, [], 'Not enough files in input.');
        return;
    end
    % Load time vector from the first file: if same as input, discard input
    TimeVector = in_bst(sInputsA(1).FileName, 'Time');
    if ~isempty(OPTIONS.TimeWindow) && (abs(TimeVector(1) - OPTIONS.TimeWindow(1)) < 1e-4) && (abs(TimeVector(end) - OPTIONS.TimeWindow(2)) < 1e-4)
        OPTIONS.TimeWindow = [];
    end
    % Load freq range from the first file: if same as input, discard input
    if ~isempty(OPTIONS.FreqRange)
        % Load Freqs field from the input file
        TfMat = in_bst_timefreq(sInputsA(1).FileName, 0, 'Freqs');
        if iscell(TfMat.Freqs)
            BandBounds = process_tf_bands('GetBounds', TfMat.Freqs);
            FreqList = unique(BandBounds(:));
        else
            FreqList = TfMat.Freqs;
        end
        if (abs(OPTIONS.FreqRange(1) - FreqList(1)) < 1e-4) && (abs(OPTIONS.FreqRange(2) - FreqList(end)) < 1e-4)
            OPTIONS.FreqRange = [];
        end
    end

    % ===== INPUT DATA =====
    % If there is nothing special done with the files: files can be handled directly by bst_avg_files
    if isempty(OPTIONS.TimeWindow) && isempty(OPTIONS.ScoutSel) && isempty(OPTIONS.SensorTypes) && isempty(OPTIONS.Rows) && isempty(OPTIONS.FreqRange) && ~OPTIONS.isAvgTime && ~OPTIONS.isAvgRow && ~OPTIONS.isAvgFreq && ~isTfPower
        InputSetA = {sInputsA.FileName};
        if ~isempty(sInputsB)
            InputSetB = {sInputsB.FileName};
        else
            InputSetB = [];
        end
        OutputType = sInputsA(1).FileType;
    % Else: Call process "Extract values" first
    else
        % Do not concatenate the output
        OPTIONS.Dim = 0;
        % Call extraction process: FilesA
        [InputSetA, OutputType] = process_extract_values('Extract', sProcess, sInputsA, OPTIONS);
        if isempty(InputSetA)
            return;
        end
        % Read FilesB
        if ~isempty(sInputsB)
            InputSetB = process_extract_values('Extract', sProcess, sInputsB, OPTIONS);
            if isempty(InputSetB)
                return;
            end
        else
            InputSetB = [];
        end
        % Adjust time-frequency already in 'power', for power stats.
        if isTfPower
            bst_report('Info', sProcess, [], 'Data is already power values, adapting power test (not squaring again).');
            for iIn = 1:numel(InputSetA)
                [InputSetA{iIn}.TF, isError] = process_tf_measure('Compute', InputSetA{iIn}.TF, InputSetA{iIn}.Measure, 'magnitude', true);
                if isError
                    bst_report('Error', sProcess, sInputsA(1), ['Error converting time-frequency measure ' InputSetA{iIn}.Measure 'to magnitude.']);
                end
            end
            for iIn = 1:numel(InputSetB)
                [InputSetB{iIn}.TF, isError] = process_tf_measure('Compute', InputSetB{iIn}.TF, InputSetB{iIn}.Measure, 'magnitude', true);
                if isError
                    bst_report('Error', sProcess, sInputsA(1), ['Error converting time-frequency measure ' InputSetB{iIn}.Measure 'to magnitude.']);
                end
            end
        end
    end

    % === COMPUTE TEST ===
    % Branch between dependent(=paired) and independent tests
    switch (OPTIONS.TestType)

        % ===== INDEPENDENT TESTS =====
        case {'ttest_equal', 'ttest_unequal', 'absmean', 'absmean_param', 'power', 'power_unconstr'}
            % Compute mean and var for both files sets
            [StatA, MessagesA] = bst_avg_files(InputSetA, [], AvgFunction, isAvgVariance, isAvgWeighted, OPTIONS.isMatchRows, OPTIONS.isZeroBad);
            [StatB, MessagesB] = bst_avg_files(InputSetB, [], AvgFunction, isAvgVariance, isAvgWeighted, OPTIONS.isMatchRows, OPTIONS.isZeroBad);
            % Add messages to report
            if ~isempty(MessagesA)
                if isempty(StatA)
                    bst_report('Error', sProcess, sInputsA, MessagesA);
                    return;
                else
                    bst_report('Warning', sProcess, sInputsA, MessagesA);
                end
            end
            if ~isempty(MessagesB)
                if isempty(StatB)
                    bst_report('Error', sProcess, sInputsB, MessagesB);
                    return;
                else
                    bst_report('Warning', sProcess, sInputsB, MessagesB);
                end
            end
            if ~isequal(size(StatA.mean), size(StatB.mean))
                bst_report('Error', sProcess, [], 'Files A and B do not have the same number of signals or time samples.');
                return;
            end
            % Detect if the source model is unconstrained
            isUnconstrained = panel_scout('isUnconstrained', StatA);
            % Do not allow unconstrained sources without a norm
            if isUnconstrained && ~OPTIONS.isAbsolute
                bst_report('Error', sProcess, [], ['Cannot run this test on unconstrained sources:' 10 'you must compute the norm of the three orientations first.']);
                return;
            end
            % Bad channels: For recordings, keep only the channels that are good in BOTH A and B sets
            if strcmpi(sInputsA(1).FileType, 'data') && ~isempty(StatA.ChannelFlag) && ~isempty(StatB.ChannelFlag)
                ChannelFlag = StatA.ChannelFlag;
                ChannelFlag(StatB.ChannelFlag == -1) = -1;
                isGood = (ChannelFlag == 1);
            else  % case {'results', 'timefreq', 'matrix'}
                ChannelFlag = [];
                isGood = true(size(StatA.mean, 1), 1);
                isGood((StatA.nGoodSamples < 2) | (StatB.nGoodSamples < 2)) = 0;
            end

            % === COMPUTE TEST ===
            % Display progress bar
            bst_progress('start', 'Processes', 'Computing test...');
            % Get average results
            mA = StatA.mean(isGood,:,:);
            mB = StatB.mean(isGood,:,:);
            nA = repmat(StatA.nGoodSamples(isGood,:,:), [1, size(mA,2), size(mA,3)]);
            nB = repmat(StatB.nGoodSamples(isGood,:,:), [1, size(mB,2), size(mB,3)]);
            % Get variance (if needed)
            if isAvgVariance
                vA = StatA.var(isGood,:,:);
                vB = StatB.var(isGood,:,:);
                % Remove null variances
                iNull = find((vA == 0) | (vB == 0));
                vA(iNull) = eps;
                vB(iNull) = eps;
            else
                iNull = [];
            end

            % Compute test statistic
            switch (OPTIONS.TestType)
                % === T-TEST: EQUAL VARIANCE ===
                case 'ttest_equal'
                    df   = nA + nB - 2 ;
                    pvar = ((nA-1).*vA + (nB-1).*vB) ./ df;
                    tmap = (mA-mB) ./ sqrt(pvar .* (1./nA + 1./nB));
                    % Calculate p-values from t-values
                    pmap = ComputePvalues(tmap, df, 't', OPTIONS.TestTail);
                    % Units: t
                    DisplayUnits = 't';

                % === T-TEST: UNEQUAL VARIANCE ===
                case 'ttest_unequal'
                    df = (vA./nA + vB./nB).^2 ./ ...
                         ((vA./nA).^2./(nA-1) + (vB./nB).^2./(nB-1));
                    tmap = (mA-mB) ./ sqrt(vA./nA + vB./nB);
                    % Calculate p-values from t-values
                    pmap = ComputePvalues(tmap, df, 't', OPTIONS.TestTail);
                    % Units: t
                    DisplayUnits = 't';

                % ===== POWER TEST (A/B) =====
                case {'power', 'power_unconstr'}
                    % If you have xi, n normal random variables with zero mean and unit variance N(0,1), then:
                    % X1 = sum_i(xi^2) is chi-square random variable with n degrees of freedom
                    % https://en.wikipedia.org/wiki/Chi-squared_distribution
                    %
                    % If X1 and X2 are chi-square random variables with nA and nB degrees of freedom, then
                    % F = (X1/nA) / (X2/nB) is F-distributed with nA numerator degrees of freedom and nB denominator degrees of freedom.
                    % https://en.wikipedia.org/wiki/F-distribution
                    %
                    % Use case here: If A~N(0,vA) and B~N(0,vB)
                    % Then F = (sum(A^2)/nA) / (sum(B^2)/nB)  ~ F(nA,nB)
                    %
                    % Can be used for two things:
                    % 1) Testing for variance difference:   H0:(A~N(0,vA), B~N(0,vB), vA=vB)
                    %    => Samples must be zero-mean (mA=0, mB=0) so probably normalized before testing
                    % 2) Testing for power difference:      H0:(A~N(0,1) and B~(0,1))
                    %    => Samples must be normalized before testing

                    % The output of bst_avg_files is the RMS of A and B:  mA = sqrt(sum(A^2)/nA)
                    % F statistic = (sum(A^2)/nA) / (sum(B^2)/nB)
                    %             = (mA^2) / (mB^2)
                    tmap = mA.^2 ./ mB.^2;
                    % Degrees of freedom
                    if strcmpi(OPTIONS.TestType, 'power_unconstr')
                        df = {3*nA,3*nB};
                    else
                        df = {nA,nB};
                    end
                    % Calculate p-values from t-values
                    pmap = ComputePvalues(tmap, df, 'F', OPTIONS.TestTail);
                    % Units: F
                    DisplayUnits = 'F';

                % === ABSOLUTE MEAN TEST ===
                case 'absmean_param'
                    % EXPLANATIONS:
                    %   Assume the individual samples xi follow a normal distribution with mean "m" and variance "s^2":  X ~ N(m,s^2)
                    %   Then mean(x) is also normal with mean m and variance sm^2 = s^2/N:    mean(X) ~ N(m,s^2/N)
                    %
                    %   When we apply abs(mean(x)), we are folding this normal distribution to make it positive.
                    %   Details are discussed here: https://en.wikipedia.org/wiki/Folded_normal_distribution
                    %   If y=|x|, with x~N(m,sm^2), then y has a new distribution with mean my and variance sy^2:
                    %      my = sm*sqrt(2/pi)*exp(-m^2/(2*sm^2)) - m*erf(-m/sqrt(2*sm^2))
                    %         = s/sqrt(N)*sqrt(2/pi)*exp(-m^2/(2*s^2/N)) - m*erf(-m/sqrt(2*s^2/N))
                    %      sy^2 = m^2 + sm^2 - my^2
                    %           = m^2 + s^2/N - my^2
                    %
                    % RESTRICTIONS
                    %   - A and B are normally distributed (same as t-test assumptions)
                    %   - Cannot be applied if an absolute has been applied already, we need the original values

                    % Test to check that there was no abs already applied, we need the relative values
                    if all(mA(:) > 0) && all(mB(:) > 0)
                        bst_report('Error', sProcess, [], ['This test is designed for values that are positive and negative.' 10 'It cannot be applied to values for which we have already discarded the sign.' 10 'If all your measures you are testing are always strictly positive, then use a Student t-test.']);
                        return;
                    end

                    % Mean of: abs(mean(A))-abs(mean(B))
                    % mAabs = sA/sqrt(N)*sqrt(2/pi)*exp(-mA^2/(2*sA^2/nA)) - mA*erf(-mA/sqrt(2*sA^2/nA))
                    %       = sqrt(vA./nA.*(2/pi)) .* exp(-mA.^2/(2.*vA./nA)) - mA*erf(-mA./sqrt(2.*vA./nA))
                    mAabs = sqrt(vA./nA.*(2/pi)) .* exp(-mA.^2./(2.*vA./nA)) - mA.*erf(-mA./sqrt(2.*vA./nA));
                    mBabs = sqrt(vB./nB.*(2/pi)) .* exp(-mB.^2./(2.*vB./nB)) - mB.*erf(-mB./sqrt(2.*vB./nB));
                    mAB = mAabs - mBabs;
                    % Variance of: abs(mean(A))-abs(mean(B))
                    vAabs = mA.^2 + vA./nA - mAabs.^2;
                    vBabs = mB.^2 + vB./nB - mBabs.^2;
                    sdAB = sqrt(vAabs + vBabs);
                    S = (abs(mA) - abs(mB) - mAB) ./ sdAB;  %S should be zero mean, unit variance under the null hypothesis

                    % [H,P] = ztest(S,0,1);   m = 0; sigma = 1;
                    % zval = (S - m) ./ (sigma ./ sqrt(length(S)));
                    tmap = S .* sqrt(length(S));
                    % Two-tailed test
                    pmap = 2 * (1/2 * erfc(-1 * -abs(tmap) / sqrt(2)));     % 2 * normcdf(-abs(zval),0,1);
                    % No need to recompute the values on the fly
                    df = [];
                    % Units: z
                    DisplayUnits = 'z';

                otherwise
                    error('Not supported yet');
            end
            % Remove values with null variances
            if ~isempty(iNull)
                tmap(iNull) = 0;
                pmap(iNull) = 1;
            end

        % ===== PAIRED/ONE-SAMPLE TESTS =====
        case {'ttest_paired', 'ttest_onesample', 'ttest_baseline', 'chi2_onesample', 'chi2_onesample_unconstr', 'power_baseline', 'power_baseline_unconstr'}
            % Number of samples must be equal
            if (length(sInputsA) ~= length(sInputsB)) && ismember(OPTIONS.TestType, {'ttest_paired'})
                bst_report('Error', sProcess, [], 'For a paired test, the number of files must be the same in the two groups.');
                return;
            end
            % Compute the mean and variance of (samples A - samples B)
            [StatA, MessagesA] = bst_avg_files(InputSetA, InputSetB, AvgFunction, isAvgVariance, isAvgWeighted, OPTIONS.isMatchRows, OPTIONS.isZeroBad);
            % Add messages to report
            if ~isempty(MessagesA)
                if isempty(StatA)
                    bst_report('Error', sProcess, [], MessagesA);
                    return;
                else
                    bst_report('Warning', sProcess, [], MessagesA);
                end
            end
            % Display progress bar
            bst_progress('start', 'Processes', 'Computing test...');
            % Bad channels and other properties
            switch lower(sInputsA(1).FileType)
                case {'data', 'pdata'}
                    ChannelFlag = StatA.ChannelFlag;
                    isGood = (ChannelFlag == 1);
                case {'results', 'timefreq', 'matrix', 'presults', 'ptimefreq', 'pmatrix'}
                    ChannelFlag = [];
                    isGood = true(size(StatA.mean, 1), 1);
                    isGood(StatA.nGoodSamples < 2) = -1;
            end

            % === COMPUTE TEST ===
            % Display progress bar
            bst_progress('start', 'Processes', 'Computing test...');
            % Get results
            mean_diff = StatA.mean(isGood,:,:);
            nA = repmat(StatA.nGoodSamples(isGood,:,:), [1, size(mean_diff,2), size(mean_diff,3)]);
            nB = [];
            % Get variance (if needed)
            if isAvgVariance
                std_diff = sqrt(StatA.var(isGood,:,:));
                % Remove null variances
                iNull = find(std_diff == 0);
                std_diff(iNull) = eps;
            else
                iNull = [];
            end

            % Get pre-stimulus baseline (for tests vs baseline)
            if ismember(OPTIONS.TestType, {'ttest_baseline', 'power_baseline', 'power_baseline_unconstr'})
                if ~isempty(Baseline)
                    % Get baseline bounds
                    iBaseline = bst_closest(Baseline, StatA.Time);
                    if (iBaseline(1) == iBaseline(2))
                        bst_report('Error', sProcess, [], 'The baseline must be included in the time window on which you run the test.');
                        return;
                    end
                    iBaseline = iBaseline(1):iBaseline(2);
                else
                    bst_report('Warning', sProcess, [], 'Baseline is not defined, using the entire time definition.');
                    iBaseline = 1:length(TimeVector);
                end
            end

            % Compute test statistic
            switch (OPTIONS.TestType)
                case {'ttest_paired', 'ttest_onesample'}
                    % Compute t-test
                    tmap = mean_diff ./ std_diff .* sqrt(nA);
                    df = nA - 1;
                    % Test if the statistics make sense
                    if all(tmap(:) == 0)
                        bst_report('Error', sProcess, [], 'The T-statistics is zero for all the tests.');
                        return;
                    end
                    % Calculate p-values from t-values
                    pmap = ComputePvalues(tmap, df, 't', OPTIONS.TestTail);
                    % Units: t
                    DisplayUnits = 't';

                case {'chi2_onesample', 'chi2_onesample_unconstr'}
                    % https://en.wikipedia.org/wiki/Chi-squared_distribution
                    % =>  If Zi~N(0,1) i=1..n  =>  Q=sum(Zi^2) ~ Chi2(n)
                    % Variable "mean_diff" contains RMS(data)=sqrt(sum(data.^2)/n)
                    % =>  If data is ~N(0,1)   =>  (mean_diff^2 * n) ~ Chi2(n)
                    tmap = mean_diff .^ 2 .* nA;
                    % Number of degrees of freedom
                    if strcmpi(OPTIONS.TestType, 'chi2_onesample_unconstr')
                        df = 3 * nA;
                    else
                        df = nA;
                    end
                    % Calculate p-values from F-values
                    pmap = ComputePvalues(tmap, df, 'chi2', OPTIONS.TestTail);
                    % Units: t
                    DisplayUnits = 'chi2';

                case 'ttest_baseline'
                    % TEST:  Y = mean_trials(X)
                    %        t = (Y - mean_time(Y(baseline)) / std_time(Y(baseline)))
                    % Compute variance over baseline (pre-stim interval)
                    meanBaseline = mean(mean_diff(:,iBaseline,:), 2);
                    stdBaseline = std(mean_diff(:,iBaseline,:), 0, 2);
                    % Remove null variances
                    iNull = find(stdBaseline == 0);
                    stdBaseline(iNull) = eps;
                    % Compute t-statistics (formula from wikipedia)
                    tmap = bst_bsxfun(@minus, mean_diff, meanBaseline);
                    tmap = bst_bsxfun(@rdivide, tmap, stdBaseline);
                    df = repmat(length(iBaseline) - 1, size(tmap));
                    % Calculate p-values from t-values
                    pmap = ComputePvalues(tmap, df, 't', OPTIONS.TestTail);
                    % Units: t
                    DisplayUnits = 't';

                case {'power_baseline', 'power_baseline_unconstr'}
                    % TEST:  Y = sum_trials(X^2)
                    %        F = Y / mean_time(Y(baseline))     F~F(Ntrials,Ntrials)
                    %
                    % The output of bst_avg_files is the RMS of X:  data = sqrt(sum_trials(X^2)/Ntrials)
                    % => Y = data^2 * Ntrials
                    % => F = data^2 / mean(data^2(baseline))

                    % Square the RMS
                    data = mean_diff .^ 2;
                    % Compute mean over baseline
                    meanBaseline = mean(data(:,iBaseline,:), 2);
                    % Remove null denominators
                    iNull = find(meanBaseline == 0);
                    meanBaseline(iNull) = eps;
                    % Compute F statistic
                    tmap = bst_bsxfun(@rdivide, data, meanBaseline);
                    % Degrees of freedom
                    if strcmpi(OPTIONS.TestType, 'power_baseline_unconstr')
                        df = {3*nA,3*nA};
                    else
                        df = {nA,nA};
                    end
                    % Calculate p-values from F-values
                    pmap = ComputePvalues(tmap, df, 'F', OPTIONS.TestTail);
                    % No need to recompute the values on the fly
                    df = [];
                    % Units: t
                    DisplayUnits = 'F';

                otherwise
                    error('Not supported yet');
            end
            % Remove values with null variances
            if ~isempty(iNull)
                tmap(iNull) = 0;
                pmap(iNull) = 1;
            end
    end

    % Return full matrices
    if all(isGood)
        tmap_full = tmap;
        pmap_full = pmap;
        df_full   = df;
        nA_full   = nA;
        nB_full   = nB;
    else
        tmap_full = zeros(size(StatA.mean));
        tmap_full(isGood,:,:) = tmap;
        if ~isempty(df)
            df_full = zeros(size(StatA.mean));
            df_full(isGood,:,:) = df;
        else
            df_full = [];
        end
        if ~isempty(pmap)
            pmap_full = ones(size(StatA.mean));
            pmap_full(isGood,:,:) = pmap;
        else
            pmap_full = [];
        end
        if ~isempty(nA)
            nA_full = zeros(1,size(StatA.mean,1));
            nA_full(isGood) = nA(1:size(nA,1));
        else
            nA_full = [];
        end
        if ~isempty(nB)
            nB_full = zeros(1,size(StatA.mean,1));
            nB_full(isGood) = nB(1:size(nB,1));
        else
            nB_full = [];
        end
    end

    % === CONVERT BACK MATRIX => DATA ===
    % If processing recordings with only some sensor types selected
    if strcmpi(sInputsA(1).FileType, 'data') && strcmpi(OutputType, 'matrix') && ~isempty(OPTIONS.SensorTypes) && ~OPTIONS.isAvgTime && ~OPTIONS.isAvgRow && ~OPTIONS.isAvgFreq
        % Get the list of selected sensors
        dataTypes = strtrim(str_split(OPTIONS.SensorTypes, ',;'));
        % If only major data types were selected: save results in "data" format
        if ~isempty(dataTypes) && all(ismember(dataTypes, {'MEG','EEG','MEG MAG''MEG GRAD','MEG GRAD2','MEG GRAD3','SEEG','ECOG','NIRS'}))
            % Load channel file
            ChannelMat = in_bst_channel(sInputsA(1).ChannelFile);
            % Find channel names in the output row names
            [tmp,iChan,iRow] = intersect({ChannelMat.Channel.Name}, StatA.RowNames);
            % Convert output data matrices
            tmap_tmp = zeros(length(ChannelMat.Channel), size(tmap_full,2), size(tmap_full,3));
            tmap_tmp(iChan,:,:) = tmap_full(iRow,:,:);
            tmap_full = tmap_tmp;
            if ~isempty(pmap_full)
                pmap_tmp = zeros(size(tmap_tmp));
                pmap_tmp(iChan,:,:) = pmap_full(iRow,:,:);
                pmap_full = pmap_tmp;
            end
            if ~isempty(df_full)
                df_tmp = zeros(size(tmap_tmp));
                df_tmp(iChan,:,:) = df_full(iRow,:,:);
                df_full = df_tmp;
            end
            % New channel flag
            tmpChannelFlag = -1 .* ones(length(ChannelMat.Channel), 1);
            if ~isempty(ChannelFlag) && (length(ChannelFlag) == length(iChan))
                tmpChannelFlag(iChan) = ChannelFlag(iRow);
            else
                tmpChannelFlag(iChan) = 1;
            end
            ChannelFlag = tmpChannelFlag;
            % Convert Stat structure
            OutputType = 'data';
            StatA.RowNames = [];
        end
    end

    % === OUTPUT STRUCTURE ===
    % Initialize output structure
    sOutput = db_template('statmat');
    sOutput.pmap         = pmap_full;
    sOutput.tmap         = tmap_full;
    sOutput.df           = df_full;
    sOutput.Correction   = 'no';
    sOutput.Type         = OutputType;
    sOutput.ChannelFlag  = ChannelFlag;
    sOutput.Time         = StatA.Time;
    sOutput.ColormapType = 'stat2';
    sOutput.DisplayUnits = DisplayUnits;
    sOutput.nComponents  = StatA.nComponents;
    sOutput.GridAtlas    = StatA.GridAtlas;
    sOutput.Freqs        = StatA.Freqs;
    sOutput.TFmask       = StatA.TFmask;
    % Row names
    if isfield(StatA, 'RowNames') && ~isempty(StatA.RowNames)
        if strcmpi(OutputType, 'matrix')
            sOutput.Description = StatA.RowNames;
        elseif strcmpi(OutputType, 'timefreq')
            sOutput.RowNames = StatA.RowNames;
        end
    end
    % Save options
    sOutput.Options = OPTIONS;
    % Save the number of good samples used for both sets
    sOutput.Options.nGoodSamplesA = nA_full;
    sOutput.Options.nGoodSamplesB = nB_full;
end

%% ===== COMPUTE P-VALUES ====
function p = ComputePvalues(t, df, TestDistrib, TestTail)
    % Default: two-tailed tests
    if (nargin < 4) || isempty(TestTail)
        TestTail = 'two';
    end
    % Default: F-distribution
    if (nargin < 3) || isempty(TestDistrib)
        TestDistrib = 'f';
    end
    % Nothing to test
    if strcmpi(TestTail, 'no')
        p = zeros(size(t));
        return;
    end

    % Different distributions
    switch lower(TestDistrib)
        % === T-TEST ===
        case 't'
            % Calculate p-values from t-values
            switch (TestTail)
                case 'one-'
                    % Inferior one-tailed t-test:   p = tcdf(t, df);
                    % Equivalent without the statistics toolbox (FieldTrip formula)
                    p = 0.5 .* ( 1 + sign(t) .* betainc( t.^2 ./ (df + t.^2), 0.5, 0.5.*df ) );
                case 'two'
                    % Two-tailed t-test:     p = 2 * (1 - tcdf(abs(t),df));
                    % Equivalent without the statistics toolbox
                    p = betainc( df ./ (df + t .^ 2), df./2, 0.5);
                    % FieldTrip equivalent: p2 = 1 - betainc( t.^2 ./ (df + t.^2), 0.5, 0.5.*df );
                case 'one+'
                    % Superior one-tailed t-test:    p = 1 - tcdf(t, df);
                    % Equivalent without the statistics toolbox (FieldTrip formula)
                    p = 0.5 .* ( 1 - sign(t) .* betainc( t.^2 ./ (df + t.^2), 0.5, 0.5.*df ) );
            end

        % === F-TEST ===
        case 'f'
            v1 = df{1};
            v2 = df{2};
            % Evaluate for which values we can compute something
            k = ((t > 0) & ~isinf(t) & (v1 > 0) & (v2 > 0));
            % Initialize returned p-values
            p = ones(size(t));
            % Calculate p-values from F-values
            switch (TestTail)
                case 'one-'
                    % Inferior one-tailed F-test
                    % p = fcdf(t, v1, v2);
                    p(k) = 1 - betainc(v2(k)./(v2(k) + v1(k).*t(k)), v2(k)./2, v1(k)./2);
                case 'two'
                    % Two tailed F-test
                    % p = 2*min(fcdf(F,df1,df2),fpval(F,df1,df2))
                    p(k) = 2 * min(...
                            1 - betainc(v2(k)./(v2(k) + v1(k).*t(k)), v2(k)./2, v1(k)./2), ...
                            1 - betainc(v1(k)./(v1(k) + v2(k)./t(k)), v1(k)./2, v2(k)./2));
                case 'one+'
                    % Superior one-tailed F-test
                    % p = fpval(t, v1, v2);
                    %   = fcdf(1/t, v2, v1);
                    p(k) = 1 - betainc(v1(k)./(v1(k) + v2(k)./t(k)), v1(k)./2, v2(k)./2);
            end

        % === CHI2-TEST ===
        case 'chi2'
            % Calculate p-values from Chi2-values
            %   chi2cdf(x,n) = gammainc(t/2, n/2)
            switch (TestTail)
                case 'one-'
                    % Inferior one-tailed Chi2-test:    p = gammainc(t./2, df./2);
                    error('Not relevant.');
                case 'two'
                    % Two-tailed Chi2-test
                    error('Not relevant.');
                case 'one+'
                    % Superior one-tailed Chi2-test:    p = 1 - gammainc(t./2, df./2);
                    p = 1 - gammainc(t./2, df./2);
            end
    end
end


function [e,C,v] = collect(e,c,v)
%TOUNIVARIATE Transformation to univariate polynomial
%
%On input,
% e   m x 1 array of exponents, possibly with rows occuring several times
% c   m x 1 array of corresponding (polynomial) coefficients (sparse)
% v   string or 1-item cell array of string
%
%On output, polynomial data is transformed to univariate format
%

% written  07/21/02     S.M. Rump
%

n = max(e);           % degree of polynomial
C = zeros(1,n+1);
if isa(c,'intval')
  C = intval(C);
end
C(n+1-e) = c;
e = n;
if iscell(v)
  v = v{1};
end


function plot_probabilities(sv,specs)

if nargin<2

    specs=[3,3];

end

myLimits=[-sqrt(eps),1+sqrt(eps)];

r0=specs(1);

c0=specs(2);

[f,the_regimes]=load_filters(sv);

cellform = iscell(f);

if cellform

    all_regimes=fieldnames(f{1}.smoothed_regime_probabilities);

    all_states=fieldnames(f{1}.smoothed_state_probabilities);

    regs=the_regimes{1};

    for ii=2:numel(f)

        [all_regimes,IA,IB]=intersect(all_regimes,...
            fieldnames(f{ii}.smoothed_regime_probabilities)); %#ok<ASGLU>

        regs=regs(IA);

        [all_states]=intersect(all_states,...
            fieldnames(f{ii}.smoothed_state_probabilities));

    end

    the_regimes=regs;

else

    all_regimes=fieldnames(f.smoothed_regime_probabilities);

    all_states=fieldnames(f.smoothed_state_probabilities);

end

utils.plot.multiple(@plotfuncr,all_regimes,...
    'smoothed regime probabilities',r0,c0);

utils.plot.multiple(@plotfuncs,all_states,...
    'smoothed state probabilities',r0,c0);

    function [tex,leg]=plotfuncr(vname)

        d=load_item('smoothed_regime_probabilities',vname);

        plot(d,'linewidth',2)

        ylim(myLimits)

        tex=vname;

        vname(1:numel('regime'))=[];

        vname=strrep(vname,'_','');

        tex=sprintf('%s(%s)',tex,the_regimes{str2double(vname)});

        leg='';

    end

    function [tex,leg]=plotfuncs(vname)

        d=load_item('smoothed_state_probabilities',vname);

        plot(d,'linewidth',2)

        ylim(myLimits)

        tex=vname;

        leg='';

    end

    function d=load_item(a,b)

        if cellform

            d=f{1}.(a).(b);

            for jj=2:numel(f)

                d=[d,f{jj}.(a).(b)]; %#ok<AGROW>

            end

        else

            d=f.(a).(b);

        end

    end

end


function [] = show_map(ax, bounds, map_img_filename)
% plot raster map in figure and fix plot bounds
%
% dependency
%   lat_lon_proportions, File Exchange ID = 32462,
%   (c) 2011 by Jonathan Sullivan
%   http://www.mathworks.com/matlabcentral/fileexchange/32462-correctly-proportion-a-latlon-plot
%
% 2010.11.21 (c) Ioannis Filippidis, jfilippidis@gmail.com
%
% See also PLOT_WAY.

hold(ax, 'on')

% image provided ?
if ~isempty(map_img_filename)
    map_img = imread(map_img_filename);
    image('Parent', ax, 'CData', flipdim(map_img,1),...
          'XData', bounds(1,1:2), 'YData', bounds(2,1:2))
end

plot(ax, [bounds(1,1), bounds(1,1), bounds(1,2), bounds(1,2), bounds(1,1)],...
         [bounds(2,1), bounds(2,2), bounds(2,2), bounds(2,1), bounds(2,1)],...
         'ro-')

xlabel(ax, 'Longitude (^o)')
ylabel(ax, 'Latitude (^o)')
title(ax, 'OpenStreetMap osm file')

axis(ax, 'image')
axis(ax, [bounds(1, :), bounds(2, :) ] )
lat_lon_proportions(ax)


filename='BikeTriangle';
ptype = 'MACRO';
method = 'SIMPALL';
materialType = 'ISOTROPIC';
initial_case = 'full';
cost = {'compliance'};
weights = [1 0.1];
constraint = {'volumeConstraint'};
optimizer = 'DualNestedInPrimal';
optimizerUnconstrained = 'SLERP';
incrementFactor = 1;
designVariable = 'LevelSet';
filterType = 'P1';
constraint_case = 'EQUALITY'; %'INEQUALITY';
line_search_initiator = 'STANDARD';
showBC = true;

nsteps = 50;
Vfrac_final = 0.2;
Perimeter_target = 1;
optimality_final = 1e-3;
constr_final =1e-3;

Vfrac_initial = 1;
optimality_initial = 1e-3;
constr_initial = 1e-3;
TOL.rho_plus = 1;
TOL.rho_minus = 0;
TOL.E_plus = 1;
TOL.E_minus = 1e-3;
TOL.nu_plus = 1/3;
TOL.nu_minus = 1/3;

monitoring_interval = 1;
optimalityInitial = 1e-3;
printing = false;
maxiter = 300;


function [SDR,ISR,SIR,SAR,perm]=bss_eval_images(ie,i)

% BSS_EVAL_IMAGES Ordering and measurement of the separation quality for
% estimated source spatial image signals in terms of true source, spatial
% (or filtering) distortion, interference and artifacts.
%
% [SDR,ISR,SIR,SAR,perm]=bss_eval_images(ie,i)
%
% Inputs:
% ie: nsrc x nsampl x nchan matrix containing estimated source images
% i: nsrc x nsampl x nchan matrix containing true source images
%
% Outputs:
% SDR: nsrc x 1 vector of Signal to Distortion Ratios
% ISR: nsrc x 1 vector of source Image to Spatial distortion Ratios
% SIR: nsrc x 1 vector of Source to Interference Ratios
% SAR: nsrc x 1 vector of Sources to Artifacts Ratios
% perm: nsrc x 1 vector containing the best ordering of estimated source
% images in the mean SIR sense (estimated source image number perm(j)
% corresponds to true source image number j)
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Copyright 2007-2008 Emmanuel Vincent
% This software is distributed under the terms of the GNU Public License
% version 3 (http://www.gnu.org/licenses/gpl.txt)
% If you find it useful, please cite the following reference:
% Emmanuel Vincent, Hiroshi Sawada, Pau Bofill, Shoji Makino and Justinian
% P. Rosca, "First stereo audio source separation evaluation campaign:
% data, algorithms and results," In Proc. Int. Conf. on Independent
% Component Analysis and Blind Source Separation (ICA), pp. 552-559, 2007.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%% Errors %%%
if nargin<2, error('Not enough input arguments.'); end
[nsrc,nsampl,nchan]=size(ie);
[nsrc2,nsampl2,nchan2]=size(i);
if nsrc2~=nsrc, error('The number of estimated source images and reference source images must be equal.'); end
if nsampl2~=nsampl, error('The estimated source images and reference source images must have the same duration.'); end
if nchan2~=nchan, error('The estimated source images and reference source images must have the same number of channels.'); end

%%% Performance criteria %%%
% Computation of the criteria for all possible pair matches
SDR=zeros(nsrc,nsrc);
ISR=zeros(nsrc,nsrc);
SIR=zeros(nsrc,nsrc);
SAR=zeros(nsrc,nsrc);
for jest=1:nsrc,
    for jtrue=1:nsrc,
        [s_true,e_spat,e_interf,e_artif]=bss_decomp_mtifilt(reshape(ie(jest,:,:),nsampl,nchan).',i,jtrue,512);
        [SDR(jest,jtrue),ISR(jest,jtrue),SIR(jest,jtrue),SAR(jest,jtrue)]=bss_image_crit(s_true,e_spat,e_interf,e_artif);
    end
end
% Selection of the best ordering
perm=perms(1:nsrc);
nperm=size(perm,1);
meanSIR=zeros(nperm,1);
for p=1:nperm,
    meanSIR(p)=mean(SIR((0:nsrc-1)*nsrc+perm(p,:)));
end
[meanSIR,popt]=max(meanSIR);
perm=perm(popt,:).';
SDR=SDR((0:nsrc-1).'*nsrc+perm);
ISR=ISR((0:nsrc-1).'*nsrc+perm);
SIR=SIR((0:nsrc-1).'*nsrc+perm);
SAR=SAR((0:nsrc-1).'*nsrc+perm);

return;

function [s_true,e_spat,e_interf,e_artif]=bss_decomp_mtifilt(se,s,j,flen)

% BSS_DECOMP_MTIFILT Decomposition of an estimated source image into four
% components representing respectively the true source image, spatial (or
% filtering) distortion, interference and artifacts, derived from the true
% source images using multichannel time-invariant filters.
%
% [s_true,e_spat,e_interf,e_artif]=bss_decomp_mtifilt(se,s,j,flen)
%
% Inputs:
% se: nchan x nsampl matrix containing the estimated source image (one row per channel)
% s: nsrc x nsampl x nchan matrix containing the true source images
% j: source index corresponding to the estimated source image in s
% flen: length of the multichannel time-invariant filters in samples
%
% Outputs:
% s_true: nchan x nsampl matrix containing the true source image (one row per channel)
% e_spat: nchan x nsampl matrix containing the spatial (or filtering) distortion component
% e_interf: nchan x nsampl matrix containing the interference component
% e_artif: nchan x nsampl matrix containing the artifacts component

%%% Errors %%%
if nargin<4, error('Not enough input arguments.'); end
[nchan2,nsampl2]=size(se);
[nsrc,nsampl,nchan]=size(s);
if nchan2~=nchan, error('The number of channels of the true source images and the estimated source image must be equal.'); end
if nsampl2~=nsampl, error('The duration of the true source images and the estimated source image must be equal.'); end

%%% Decomposition %%%
% True source image
s_true=[reshape(s(j,:,:),nsampl,nchan).',zeros(nchan,flen-1)];
% Spatial (or filtering) distortion
e_spat=project(se,s(j,:,:),flen)-s_true;
% Interference
e_interf=project(se,s,flen)-s_true-e_spat;
% Artifacts
e_artif=[se,zeros(nchan,flen-1)]-s_true-e_spat-e_interf;

return;

function sproj=project(se,s,flen)

% SPROJ Least-squares projection of each channel of se on the subspace
% spanned by delayed versions of the channels of s, with delays between 0
% and flen-1

[nsrc,nsampl,nchan]=size(s);
s=reshape(permute(s,[3 1 2]),nchan*nsrc,nsampl);

%%% Computing coefficients of least squares problem via FFT %%%
% Zero padding and FFT of input data
s=[s,zeros(nchan*nsrc,flen-1)];
se=[se,zeros(nchan,flen-1)];
fftlen=2^nextpow2(nsampl+flen-1);
sf=fft(s,fftlen,2);
sef=fft(se,fftlen,2);
% Inner products between delayed versions of s
G=zeros(nchan*nsrc*flen);
for k1=0:nchan*nsrc-1,
    for k2=0:k1,
        ssf=sf(k1+1,:).*conj(sf(k2+1,:));
        ssf=real(ifft(ssf));
        ss=toeplitz(ssf([1 fftlen:-1:fftlen-flen+2]),ssf(1:flen));
        G(k1*flen+1:k1*flen+flen,k2*flen+1:k2*flen+flen)=ss;
        G(k2*flen+1:k2*flen+flen,k1*flen+1:k1*flen+flen)=ss.';
    end
end
% Inner products between se and delayed versions of s
D=zeros(nchan*nsrc*flen,nchan);
for k=0:nchan*nsrc-1,
    for i=1:nchan,
        ssef=sf(k+1,:).*conj(sef(i,:));
        ssef=real(ifft(ssef,[],2));
        D(k*flen+1:k*flen+flen,i)=ssef(:,[1 fftlen:-1:fftlen-flen+2]).';
    end
end

%%% Computing projection %%%
% Distortion filters
C=G\D;
C=reshape(C,flen,nchan*nsrc,nchan);
% Filtering
sproj=zeros(nchan,nsampl+flen-1);
for k=1:nchan*nsrc,
    for i=1:nchan,
        sproj(i,:)=sproj(i,:)+fftfilt(C(:,k,i).',s(k,:));
    end
end

return;

function [SDR,ISR,SIR,SAR]=bss_image_crit(s_true,e_spat,e_interf,e_artif)

% BSS_IMAGE_CRIT Measurement of the separation quality for a given source
% image in terms of true source, spatial (or filtering) distortion,
% interference and artifacts.
%
% [SDR,ISR,SIR,SAR]=bss_image_crit(s_true,e_spat,e_interf,e_artif)
%
% Inputs:
% s_true: nchan x nsampl matrix containing the true source image (one row per channel)
% e_spat: nchan x nsampl matrix containing the spatial (or filtering) distortion component
% e_interf: nchan x nsampl matrix containing the interference component
% e_artif: nchan x nsampl matrix containing the artifacts component
%
% Outputs:
% SDR: Signal to Distortion Ratio
% ISR: source Image to Spatial distortion Ratio
% SIR: Source to Interference Ratio
% SAR: Sources to Artifacts Ratio

%%% Errors %%%
if nargin<4, error('Not enough input arguments.'); end
[nchant,nsamplt]=size(s_true);
[nchans,nsampls]=size(e_spat);
[nchani,nsampli]=size(e_interf);
[nchana,nsampla]=size(e_artif);
if ~((nchant==nchans)&&(nchant==nchani)&&(nchant==nchana)), error('All the components must have the same number of channels.'); end
if ~((nsamplt==nsampls)&&(nsamplt==nsampli)&&(nsamplt==nsampla)), error('All the components must have the same duration.'); end

%%% Energy ratios %%%
% SDR
SDR=10*log10(sum(sum(s_true.^2))/sum(sum((e_spat+e_interf+e_artif).^2)));
% ISR
ISR=10*log10(sum(sum(s_true.^2))/sum(sum(e_spat.^2)));
% SIR
SIR=10*log10(sum(sum((s_true+e_spat).^2))/sum(sum(e_interf.^2)));
% SAR
SAR=10*log10(sum(sum((s_true+e_spat+e_interf).^2))/sum(sum(e_artif.^2)));

return;


function X_cache=fmridesign(frametimes,slicetimes, ...
                            events,S,exclude,hrf_parameters)

%FMRIDESIGN
%
% Produces a set of design matrices, one for each slice, for fmristat.
% With just the frametimes, it gives the hemodynamic response function.
%
% X_CACHE = FMRIDESIGN( FRAME_TIMES [, SLICE_TIMES [, EVENTS , [S ,
%                       [, EXCLUDE [, HRF_PARAMETERS ]]]]] )
%
% FRAME_TIMES is a row vector of frame acquisition times in seconds.
%
% SLICE_TIMES is a row vector of relative slice acquisition times,
% i.e. absolute acquisition time of a slice is FRAME_TIMES + SLICE_TIMES.
% Default is 0.
%
% EVENTS is a matrix whose rows are events and whose columns are:
% 1. id - an integer from 1:(number of events) to identify event type;
% 2. times - start of event, synchronised with frame and slice times;
% 3. durations (optional - default is 0) - duration of event;
% 4. heights (optional - default is 1) - height of response for event.
% For each event type, the response is a box function starting at the event
% times, with the specified durations and heights, convolved with the
% hemodynamic response function (see below). If the duration is zero, the
% response is the hemodynamic response function whose integral is
% the specified height - useful for `instantaneous' stimuli such as visual
% stimuli. The response is then subsampled at the appropriate frame and slice
% times to create a design matrix for each slice, whose columns correspond
% to the event id number. EVENT_TIMES=[] will ignore event times and just
% use the stimulus design matrix S (see next). Default is [1 0].
%
% S: Events can also be supplied by a stimulus design matrix,
% whose rows are the frames, and column are the event types. Events
% are created for each column, beginning at the frame time for each row
% of S, with a duration equal to the time to the next frame, and a height
% equal to the value of S for that row and column. Note that a
% constant term is not usually required, since it is removed by the
% polynomial trend terms provided N_POLY>=0. Note that all values for
% all frames must be supplied, because smoothing and lagging by the
% hemodynamic resonse is done BEFORE excluding time points by EXCLUDE.
% Default is [].
%
% EXCLUDE is a list of frames that should be excluded from the
% analysis. This must be used with Siemens EPI scans to remove the
% first few frames, which do not represent steady-state images.
% Default is [].
%
% HRF_PARAMETERS is a matrix whose rows are 6 parameters for the
% hemodynamic response function, one row for each event type and column
% of S (if there is just one row, this is repeated as necessary).
% The hrf is modeled as the difference of two
% gamma density functions (Glover, NeuroImage, 9:416-429).
% The components of HRF_PARAMETERS are:
% 1. PEAK1: time to the peak of the first gamma density;
% 2. FWHM1: approximate FWHM of the first gamma density;
% 3. PEAK2: time to the peak of the second gamma density;
% 4. FWHM2: approximate FWHM of the second gamma density;
% 5. DIP: coefficient of the second gamma density;
%    Final hrf is:   gamma1/max(gamma1)-DIP*gamma2/max(gamma2)
%    scaled so that its total integral is 1.
% 6. FIT_SCALE: 1 - fit the time scale of the hrf by convolving its
%    derivative with the specified column of the design matrix, to create an
%    additional column for the design matrix. Dividing the effect
%    of this column by the effect of the hrf itself estimates the
%    scale shift. 0 ignores this option.
% If PEAK1=0 then there is no smoothing of that event type with the hrf.
% Default is: [5.4 5.2 10.8 7.35 0.35 0] chosen by Glover (1999) for
% an auditory stimulus.
%
% X_CACHE: A cache of the design matrices; rows are the non-excluded frames,
% columns are all the regressor variables, with slices running slowest.

%############################################################################
% COPYRIGHT:   Copyright 2000 K.J. Worsley and C. Liao,
%              Department of Mathematics and Statistics,
%              McConnell Brain Imaging Center,
%              Montreal Neurological Institute,
%              McGill University, Montreal, Quebec, Canada.
%              worsley@math.mcgill.ca, liao@math.mcgill.ca
%
%              Permission to use, copy, modify, and distribute this
%              software and its documentation for any purpose and without
%              fee is hereby granted, provided that the above copyright
%              notice appear in all copies.  The author and McGill University
%              make no representations about the suitability of this
%              software for any purpose.  It is provided "as is" without
%              express or implied warranty.
%############################################################################

% Defaults:

if nargin < 2
   slicetimes=0
end
if nargin < 3
   events=[1 0]
end
if nargin < 4
   S=[]
end
if nargin < 5
   exclude=[]
end
if nargin < 6
   hrf_parameters=[5.4 5.2 10.8 7.35 0.35 0]
end

numframes=length(frametimes);
numslices=length(slicetimes);

% Keep time points that are not excluded:

allpts = 1:numframes;
allpts(exclude) = zeros(1,length(exclude));
keep = allpts( find( allpts ) );
n=length(keep);
scantimes=frametimes(keep);

if ~isempty(events)
   numevents=size(events,1);
   eventid=events(:,1);
   numeventypes=max(eventid);
   eventime=events(:,2);
   if size(events,2)>=3
      duration=events(:,3);
   else
      duration=zeros(numevents,1);
   end
   if size(events,2)>=4
      height=events(:,4);
   else
      height=ones(numevents,1);
   end
   mineventime=min(eventime);
   maxeventime=max(eventime+duration);
else
   numeventypes=0;
   mineventime=Inf;
   maxeventime=-Inf;
end

if ~isempty(S)
   numcolS=size(S,2);
else
   numcolS=0;
end

% Set up response matrix:

dt=0.02;
  startime=min(mineventime,min(frametimes)+min([slicetimes 0]));
finishtime=max(maxeventime,max(frametimes)+max([slicetimes 0]));
numtimes=ceil((finishtime-startime)/dt)+1;
numresponses=numeventypes+numcolS;
response=zeros(numtimes,numresponses);

if ~isempty(events)
   height=height./(1+(duration==0)*(dt-1));
   for k=1:numevents
      type=eventid(k);
      n1=ceil((eventime(k)-startime)/dt)+1;
      n2=ceil((eventime(k)+duration(k)-startime)/dt)+(duration(k)==0);
      if n2>=n1
         response(n1:n2,type)=response(n1:n2,type)+height(k)*ones(n2-n1+1,1);
      end
   end
end

if ~isempty(S)
   for j=1:numcolS
      for i=find(S(:,j)')
         n1=ceil((frametimes(i)-startime)/dt)+1;
         if i<numframes
            n2=ceil((frametimes(i+1)-startime)/dt);
         else
            n2=numtimes;
         end
         if n2>=n1
            response(n1:n2,numeventypes+j)= ...
               response(n1:n2,numeventypes+j)+S(i,j)*ones(n2-n1+1,1);
         end
      end
   end
end

% Set hrf parameters:

numscale=0;
for k=1:numresponses
   if k<=size(hrf_parameters,1)
      if hrf_parameters(k,1)>0
         peak1=hrf_parameters(k,1);
         fwhm1=hrf_parameters(k,2);
         peak2=hrf_parameters(k,3);
         fwhm2=hrf_parameters(k,4);
         dip=hrf_parameters(k,5);
         alpha1=peak1^2/fwhm1^2*8*log(2);
         alpha2=peak2^2/fwhm2^2*8*log(2);
         beta1=fwhm1^2/peak1/8/log(2);
         beta2=fwhm2^2/peak2/8/log(2);

         numlags=ceil(max(peak1+2*fwhm1,peak2+2*fwhm2)/dt);
         time=(0:(numlags-1))'*dt;
         gamma1=(time/peak1).^alpha1.*exp(-(time-peak1)./beta1);
         gamma2=(time/peak2).^alpha2.*exp(-(time-peak2)./beta2);
         hrf=gamma1-dip*gamma2;
         sumhrf=sum(hrf);
         hrf=hrf/sumhrf;
         if hrf_parameters(k,6)==1
            fit_scale=1;
            d_hrf=((time/beta1-alpha1-1).*gamma1- ...
               dip*(time/beta2-alpha2-1).*gamma2);
            d_hrf=d_hrf/sumhrf;
         else
            fit_scale=0;
         end
      else
         fit_scale=0;
         hrf=1;
      end
   end
   eventmatrix(:,k)=conv2(response(:,k),hrf);
   if fit_scale==1
      numscale=numscale+1;
      eventmatrix(:,numresponses+numscale)=conv2(response(:,k),d_hrf);
   end
end

% Make all the design matrices for each slice:

numcolX=numresponses+numscale;
X_cache=zeros(n,numcolX*numslices);
for slice = 1:numslices
   subtime=floor((scantimes+slicetimes(slice)-startime)/dt)+1;
   X_cache(:,(1:numcolX)+(slice-1)*numcolX)=eventmatrix(subtime,:);
end

% End.


%   CLASS IRNSS_SS
% =========================================================================
%
% DESCRIPTION
%   container of IRNSS Satellite System parameters
%
% REFERENCES
%   CRS parameters, according to each GNSS system CRS definition
%   (ICD document in brackets):
%
%   *_QZS --> GRS80    (IS-IRNSS 1.8E)
%   Standard: https://www.google.it/url?sa=t&rct=j&q=&esrc=s&source=web&cd=2&ved=0ahUKEwjF5LXHyOPWAhUJLZoKHenOD4MQFggvMAE&url=https%3A%2F%2Fforum.nasaspaceflight.com%2Findex.php%3Faction%3Ddlattach%3Btopic%3D36710.0%3Battach%3D634319&usg=AOvVaw0sOE3-CqbC2o1p2tjjO8IM
%
%   Other useful links
%     - http://www.navipedia.net/index.php/IRNSS_Signal_Plan
%     - Ellipsoid: http://www.unoosa.org/pdf/icg/2012/template/WGS_84.pdf
%       note that GM and OMEGAE_DOT are redefined in the standard IS-GPS200H (GPS is not using WGS_84 values)
%     - http://www.navipedia.net/index.php/Reference_Frames_in_GNSS
%     - http://gage6.upc.es/eknot/Professional_Training/PDF/Reference_Systems.pdf

%--------------------------------------------------------------------------
%               ___ ___ ___
%     __ _ ___ / __| _ | __|
%    / _` / _ \ (_ |  _|__ \
%    \__, \___/\___|_| |___/
%    |___/                    v 1.0RC1
%
%--------------------------------------------------------------------------
%  Copyright (C) 2021 Geomatics Research & Development srl (GReD)
%  Written by:        Andrea Gatti
%  Contributors:      Andrea Gatti, Giulio Tagliaferro ...
%  A list of all the historical goGPS contributors is in CREDITS.nfo
%--------------------------------------------------------------------------
%
%    This program is free software: you can redistribute it and/or modify
%    it under the terms of the GNU General Public License as published by
%    the Free Software Foundation, either version 3 of the License, or
%    (at your option) any later version.
%
%    This program is distributed in the hope that it will be useful,
%    but WITHOUT ANY WARRANTY; without even the implied warranty of
%    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%    GNU General Public License for more details.
%
%    You should have received a copy of the GNU General Public License
%    along with this program.  If not, see <http://www.gnu.org/licenses/>.
%
%--------------------------------------------------------------------------
% 01100111 01101111 01000111 01010000 01010011
%--------------------------------------------------------------------------

classdef IRNSS_SS < Satellite_System
    properties (Constant, Access = 'public')
        SYS_EXT_NAME = 'IRNSS';    % full name of the constellation
        SYS_NAME     = 'IRN';     % 3 characters name of the constellation, this "short name" is used as fields of the property list (struct) to identify a constellation
        SYS_C        = 'I';       % Satellite system (ss) character id

        % System frequencies as struct [MHz]
        F = struct('L5', 1176.450, ...
                   'S',  2492.028)

        % Array of supported frequencies [MHz]
        F_VEC = struct2array(IRNSS_SS.F) * 1e6;

        % Array of the corresponding wavelength - lambda => wavelengths
        L_VEC = 299792458 ./ IRNSS_SS.F_VEC;

        N_SAT = 9;                    % Maximum number of satellite in the constellation
        PRN = (1 : IRNSS_SS.N_SAT)';  % Satellites id numbers as defined in the constellation

        % CODE2DATA ftp://igs.org/pub/data/format/rinex303.pdf
        CODE_RIN3_ATTRIB  = {'XCBA F', 'XCBA F'}; % last letter of the observation code e.g. C5A - C5B - C5C - C5X
        CODE_RIN3_DEFAULT_ATTRIB  = {'C' 'C'}; % last letter of the observation code
        CODE_RIN3_2BAND  = '59';             % id for the freq as stored in F_VEC e.g. L5 -> C5A, S -> C9A
        IONO_FREE_PREF  = ['59'];  % to be evaluated which combination is really better
end

    properties (Constant, Access = 'private')
        % IRNSS (GRS80) Ellipsoid semi-major axis [m]
        ELL_A = 6378137;
        % IRNSS (GRS80) Ellipsoid flattening
        ELL_F = 1/298.257222101;
        % IRNSS (GRS80) Ellipsoid Eccentricity^2
        ELL_E2 = (1 - (1 - IRNSS_SS.ELL_F) ^ 2);
        % IRNSS (GRS80) Ellipsoid Eccentricity
        ELL_E = sqrt(IRNSS_SS.ELL_E2);
    end

    properties (Constant, Access = 'public')
        % Structure of orbital parameters (ellipsoid, GM, OMEGA_EARTH_DOT)
        ORBITAL_P = struct('GM', 3.986005e14, ...                  % Gravitational constant * (mass of Earth) [m^3/s^2]
                           'OMEGAE_DOT', 7.2921151467e-5, ...      % Angular velocity of the Earth rotation [rad/s]
                           'ELL',struct( ...                       % Ellipsoidal parameters IRNSS (GRS80)
                           'A', IRNSS_SS.ELL_A, ...             % Ellipsoid semi-major axis [m]
                           'F', IRNSS_SS.ELL_F, ...             % Ellipsoid flattening
                           'E', IRNSS_SS.ELL_E, ...             % Eccentricity
                           'E2', IRNSS_SS.ELL_E2));             % Eccentricity^2
        ORBITAL_INC = 31;    % Orbital inclination
        ORBITAL_RADIUS  = 35786000 + 6378137; % Orbital radius
    end

    methods
        function this = IRNSS_SS(offset)
            % Creator
            % SYNTAX: IRNSS_SS(<offset>)
            if (nargin == 0)
                offset = 0;
            end
            this@Satellite_System(offset);
        end

        function copy = getCopy(this)
            % Get a copy of this
            copy = IRNSS_SS(this.getOffset());
            copy.import(this);
        end
    end
end


function title = p10_title ( )

%*****************************************************************************80
%
%% P10_TITLE returns the title of problem 10.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    06 February 2012
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Output, string TITLE, the title of the problem.
%
  title = 'Y = 2 + 5*X + 10*N(0,1).';

  return
end


%%********************************************************************
%% infeaspt: generate an initial point for sdp.m
%%
%%  [X0,y0,Z0] = infeaspt(blk,At,C,b,options,scalefac);
%%
%%  options = 1  if want X0,Z0 to be scaled identity matrices
%%          = 2  if want X0,Z0 to be scalefac*(identity matrices).
%%
%% SDPT3: version 3.1
%% Copyright (c) 1997 by
%% K.C. Toh, M.J. Todd, R.H. Tutuncu
%% Last Modified: 16 Sep 2004
%%********************************************************************

   function [X0,y0,Z0] = infeaspt(blk,At,C,b,options,scalefac);
%%
   if (nargin < 5); options = 1; end;
   if (options == 1); scalefac = []; end;
   if (options == 2) & (nargin < 6); scalefac = 1000; end;
   if (scalefac <= 0); error('scalefac must a positive number'); end;
   state = rand('state');
   rand('state',0);
%%
   if ~iscell(At); At = {At}; end;
   if ~iscell(C);  C = {C}; end;
   m = length(b);
   if all(size(At) == [size(blk,1) m]);
      convertyes = zeros(size(blk,1),1);
      for p = 1:size(blk,1)
         if strcmp(blk{p,1},'s') & all(size(At{p,1}) == sum(blk{p,2}))
            convertyes(p) = 1;
         end
      end
      if any(convertyes)
         At = svec(blk,At,ones(size(blk,1),1));
      end
   end;
%%
   %%[blk,At,C,b] = validate(blk,At,C,b);
%%
   X0 = cell(size(C)); Z0 = cell(size(C));
   m = length(b);
   for p = 1:size(blk,1);
      pblk = blk(p,:);
      blktmp = pblk{2};
      n = length(C{p});
      y0 = zeros(m,1);
      b2 = 1 + abs(b');
      if (options == 1);
         if strcmp(pblk{1},'s');
            normAni = [];
            X0{p} = sparse(n,n); Z0{p} = sparse(n,n);
            ss = [0, cumsum(blktmp)];
            tt = [0, cumsum(blktmp.*(blktmp+1)/2)];
            for i = 1:length(pblk{2})
               if ~isempty(At{p,1})
                  pos = [tt(i)+1 : tt(i+1)];
                  Ai = At{p,1}(pos,:);
                  normAni = 1+sqrt(sum(Ai.*Ai));
               end
               if (length(At(p,:)) >= 2) %% for low rank constraints
                  dd = At{p,3};
                  qq = [0, cumsum(pblk{3})]; normtmp = ones(1,length(pblk{3}));
                  idxD = [0; find(diff(dd(:,1))); size(dd,1)];
                  for k = 1:length(pblk{3})
                      idx = [qq(k)+1 : qq(k+1)];
                      idx2 = [idxD(k)+1: idxD(k+1)];
                      Ak = At{p,2}(:,idx);
                      ii = dd(idx2,2)-qq(k); %% undo cumulative indexing
                      jj = dd(idx2,3)-qq(k);
                      len = pblk{3}(k);
                      Dk = spconvert([ii,jj,dd(idx2,4); len,len,0]);
                      tmp = Ak'*Ak*Dk;
                      normtmp(1,k) = 1+sqrt(sum(sum(tmp.*tmp')));
                  end
                  normAni = [normAni, normtmp];
               end
               pos = [ss(i)+1 : ss(i+1)];  ni = length(pos);
               tmp = C{p}(pos,pos);
               normCni = 1+sqrt(sum(sum(tmp.*tmp)));
               const  = 10; %%--- old: const = 1;
               constX = max([const,sqrt(ni),ni*(b2./normAni)]);
               constZ = max([const,sqrt(ni),normAni,normCni]);
               X0{p}(pos,pos) = constX*spdiags(1+1e-10*rand(ni,1),0,ni,ni);
               Z0{p}(pos,pos) = constZ*spdiags(1+1e-10*rand(ni,1),0,ni,ni);
            end
         elseif strcmp(pblk{1},'q');
            s = 1+[0, cumsum(blktmp)];
            len = length(blktmp);
            normC = 1+norm(C{p});
            normA = 1+sqrt(sum(At{p,1}.*At{p,1}));
            idenqX = zeros(sum(blktmp),1);
            idenqZ = zeros(sum(blktmp),1);
            idenqX(s(1:len)) = max([1,b2./normA])*sqrt(blktmp') ;
            idenqZ(s(1:len)) = max([sqrt(blktmp); max([normA,normC])*ones(1,len)])';
            idenqX(s(1:len)) = idenqX(s(1:len)).*(1+1e-10*rand(len,1));
            idenqZ(s(1:len)) = idenqZ(s(1:len)).*(1+1e-10*rand(len,1));
            X0{p} = idenqX;
            Z0{p} = idenqZ;
         elseif strcmp(pblk{1},'l');
            normC = 1+norm(C{p});
            normA = 1+sqrt(sum(At{p,1}.*At{p,1}));
            const = 10; %%--- old: const =1;
            constX = max([const,sqrt(n),sqrt(n)*b2./normA]);
            constZ = max([const,sqrt(n),normA,normC]);
            X0{p} = constX*(1+1e-10*rand(n,1));
            Z0{p} = constZ*(1+1e-10*rand(n,1));
         elseif strcmp(pblk{1},'u');
            X0{p} = sparse(n,1);
            Z0{p} = sparse(n,1);
         else
            error(' blk: some fields not specified correctly');
         end;
      elseif (options == 2);
         if strcmp(pblk{1},'s');
            n = sum(blktmp);
            X0{p} = scalefac*spdiags(1+1e-10*rand(n,1),0,n,n);
            Z0{p} = scalefac*spdiags(1+1e-10*rand(n,1),0,n,n);
         elseif strcmp(pblk{1},'q');
            s = 1+[0, cumsum(blktmp)];
            len = length(blktmp);
            idenq = zeros(sum(blktmp),1);
            idenq(s(1:len)) = 1+1e-10*rand(len,1);
            X0{p} = scalefac*idenq;
            Z0{p} = scalefac*idenq;
         elseif strcmp(pblk{1},'l');
            X0{p} = scalefac*(1+1e-10*rand(n,1));
            Z0{p} = scalefac*(1+1e-10*rand(n,1));
         elseif strcmp(pblk{1},'u');
            X0{p} = sparse(n,1);
            Z0{p} = sparse(n,1);
         else
            error(' blk: some fields not specified correctly');
         end
      end
   end
   rand('state',state);
%%********************************************************************


function im = rgb_to_nrgb(im)
% Transforms image into NRGB color space
% Code by Ross Girshick

if ~isa(im, 'double')
  im = double(im);
end
im = im./repmat(sum(im,3), [1 1 3]);

im = 255*im;


% This file is part of the following project:
% Oliver Parson, Siddhartha Ghosh, Mark Weal, Alex Rogers.
% Non-intrusive Load Monitoring using Prior Models of General Appliance Types.
% In: 26th AAAI Conference on Artificial Intelligence. Toronto, Canada. 2012.
% Code available for download: https://sites.google.com/site/oliparson/phd-work/research-files/aaai-2012-code.zip?attredirects=0
% Copyright: Oliver Parson et al., University of Southhampton, 2012.

% Modified by Romano Cicchetti, ETH Zurich, in the context of the NILM-Eval project

function [mpe] = my_viterbi_diff(bnet, evidence2, ignore_obs, lik_thres)

observed = ~isemptycell(evidence2);

T = length(evidence2);
mpe = evidence2(1:2,:);
d_states = bnet.node_sizes(bnet.dnodes_slice);
max_prob = zeros(d_states,T);
edges = zeros(d_states,T);

initial = struct(bnet.CPD{1});
emission2 = struct(bnet.CPD{2});
transition = struct(bnet.CPD{3});
emission = struct(bnet.CPD{4});

% prob of transitions
trans = transition.CPT;
diffMeans = permute(emission.mean, [2,3,1]);
diffCovs = sqrt(permute(emission.cov, [3,4,1,2]));
absoluteMeans = emission2.mean(:)';
absoluteCovs = emission2.cov(:)';

% max product forward pass
for t=1:T,
    % prob of states at t-1
    if t>1
        chain = max_prob(:,t-1);
    end

    % prob of diff emission
    emit = normpdf(evidence2{2,t}, diffMeans, diffCovs);

    % prob of absolute emission
    emit2 = normcdf(evidence2{3,t}, absoluteMeans, absoluteCovs);
    emit2 = emit2 / sum(emit2(:));

    % ignore observations of low probability
    if ignore_obs && sum(emit(:)) < lik_thres
        if t==1
            max_prob(:,t) = log(initial.CPT);
        else
            % product
            product = repmat(chain,[1,d_states]) + log(trans) + log(repmat(emit2,[d_states,1]));
            % max
            [max_prob(:,t), edges(:,t)] = max(product);
        end
    else
        if t==1
            max_prob(:,t) = log(initial.CPT);
        else
            %normalise emission probabilites
            emit = emit / sum(emit(:));
            % product
            product = repmat(chain,[1,d_states]) + log(trans) + log(emit) + log(repmat(emit2,[d_states,1]));
            % max
            [max_prob(:,t), edges(:,t)] = max(product);
        end
    end

    %normalise
    max_prob(:,t) = max_prob(:,t) - max(max_prob(:,t));

    a = max_prob(:,t);
    max_prob((isinf(a)),t) = min(a(~isinf(a))) - 100;

    if any(isinf(max_prob(:,t)))
        1;
    end

    % if observed then prob = 1
    if observed(1,t)
        max_prob(:,t) = 0;
        max_prob(evidence2{1,t},t) = 1;
    end
end

% backward pass
[~, mpe{1,T}] = max(max_prob(:,T));
for t=T:-1:2,
    mpe{1,t-1} = edges(mpe{1,t},t);
end


function test_failed=test_involute

Lr=[9,10];

test_failed=0;

disp(' ===============  TEST_INVOLUTE ===========');

for ii=1:length(Lr)

  L=Lr(ii);
  f=tester_crand(L,1);

  r1=conj(dft(f));
  r2=dft(involute(f));

  res=norm(r1-r2);
  [test_failed,fail]=ltfatdiditfail(res,test_failed);
  s=sprintf('INVOLUTE  L:%3i %0.5g %s',L,res,fail);
  disp(s);

end;


function varargout = bst_meanvar(varargin)
%BST_MEANVAR: Mex-file to compute mean and variance along first dimension (with imprecise algorithm)
%
% USAGE: [mean,var,nAvg] = bst_meanvar(x, isZeroBad)
%
% INPUTS:
%    - x         : [NxM] double matrix with values to process
%    - isZeroBad : If 1, excludes all the zero values from the computation
%
% OUTPUTS:
%    - mean : [1xM] averages values
%    - var  : [1xM] unbiased estimator of the variance (computed with an algorithm prone to rounding errors)
%    - nAvg : [1xM] number of non-zero values that were averaged
%
% COMPILE:
%    mex -v bst_meanvar.c

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

error('Mex-function bst_meanvar.c not compiled.');


clc; close all; clear all;
%%%%%%%%% this directory should be properly changed.
%%%%%% make sure that RANSAC and GP_toolbox had been added the path.
% addpath('GPSegmentation\Ransac\');
% addpath('iGPR/gpml-matlab-v4.0-2016-10-19');
% run('iGPR/gpml-matlab-v4.0-2016-10-19/setpath.m');
% load data_straightRoad.mat
load data_intersection.mat
cloud = pcdownsample(pointCloud(data'), 'gridAverage', 0.1);
data = cloud.Location';
Dist = sqrt(sum(data(1:2, :).^2));
data = data(:, Dist >= 3.0 & Dist <= 60.0);
params = [];
params.GapThr = [0.10 1.0];
params.RadArray = 0.0 : 2.0 : 80.0;
params.AngRes   = deg2rad(5.0);
params.GroundH  = -1.8;
params.IS_SHOW = 1;
tic
[GrdIdx, ObsIdx, UnkownIdx, GrdPts] = GPSegFun(data, params);
toc
%% Run DBSCAN Clustering Algorithm
obsData = data(:, ObsIdx);
X = obsData(1:2, :)';
epsilon = 1.0; % the unit is meter.
MinPts = 10;
tic
IDX = DBSCAN(X,epsilon,MinPts);
toc
%% Plot Results
figure;
hold on;
grid on;
axis equal;
xlabel('X/m');
ylabel('Y/m');
% PlotClusterinResult(X, IDX);
Colors = hsv(20);
nDim = 3;
Legends = {};
%%%%% IDX == 0 denotes noise.
K=max(IDX);
for i=1:K
    Tmp = obsData(:, IDX==i);
    if length(Tmp) <= 20 %%%% discarding small obstacle
        continue;
    end
    minRange = min(Tmp');
    maxRange = max(Tmp');
    xRange = [minRange(1) maxRange(1)];
    yRange = [minRange(2) maxRange(2)];
    zRange = [minRange(3) maxRange(3)];
    boxPts = [xRange([1 1 2 2 1]);
              yRange([1 2 2 1 1]);
              zRange([1 1 1 1 1])];
    id = mod(i, length(Colors));
    if id == 0
        id = length(Colors);
    end
    Color = Colors(id,:);
    plot3(boxPts(1, :), boxPts(2, :), boxPts(3, :), 'color', Color, 'linestyle', '-', ...
        'linewidth', 2, 'marker', 's');
    if ~isempty(Tmp)
        str = sprintf('Id=%02d, Len=%02d', i-1, length(Tmp));
        pt = Tmp(:, 1);
        text(pt(1), pt(2), pt(3), str, 'FontSize', 12, 'color', 'k' );
        pcshow(Tmp', Color);
    end
end
title(['DBSCAN Clustering (\epsilon = ' num2str(epsilon) ', MinPts = ' num2str(MinPts) ')']);


%GOBJ_ELLIPSE Create ellipse geometry object.
%
%   [ GOBJ ] = GOBJ_ELLIPSE( P, RX, RY, TAG ) Creates an ellipse
%   geometry object. Accepts the following input parameters.
%
%       Parameter   Value/{Default}           Description
%       -----------------------------------------------------------------------------------
%       p           array  {[0 0]}            Coordinates of center point
%       rx          scalar {1}                Radius along x-axis
%       ry          scalar {0.5}              Radius along y-axis
%       tag         string {E1}               Geometry object tag/name

% Copyright 2013-2022 Precise Simulation, Ltd.


function c = tapas_kf_config
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Contains the configuration for the Kalman filter
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% The Kalman filter configuration consists of the priors of parameters and initial values. All
% priors are Gaussian in the space where the quantity they refer to is estimated. They are specified
% by their sufficient statistics: mean and variance (NOT standard deviation).
%
% Quantities are estimated in their native space if they are unbounded (e.g., mu_0). They are
% estimated in log-space if they have a natural lower bound at zero (e.g., the pi_u).
%
% Parameters can be fixed (i.e., set to a fixed value) by setting the variance of their prior to
% zero.
%
% Fitted trajectories can be plotted by using the command
%
% >> tapas_kf_plotTraj(est)
%
% where est is the stucture returned by tapas_fitModel. This structure contains the estimated
% perceptual parameters in est.p_prc and the estimated trajectories of the filter's
% representations. Their meanings are:
%
%         est.p_prc.g_0        initial value of gain
%         est.p_prc.mu_0       initial values of hidden state mean
%         est.p_prc.om         process variance
%         est.p_prc.pi_u       observation precision
%
%         est.traj.da          prediction error
%         est.traj.g           gain
%         est.traj.mu          hidden state mean
%
% Tips:
% - When analyzing a new dataset, take your inputs u and use
%
%   >> est = tapas_fitModel([], u, 'tapas_kf_config', 'tapas_bayes_optimal_config');
%
%   to determine the Bayes optimal perceptual parameters (given your current priors as defined in
%   this file here, so choose them wide and loose to let the inputs influence the result). You can
%   then use the optimal parameters as your new prior means for the perceptual parameters.
%
% - If the log-model evidence cannot be calculated because the Hessian poses problems, look at
%   est.optim.H and fix the parameters that lead to NaNs.
%
% - Your guide to all these adjustments is the log-model evidence (LME). Whenever the LME increases
%   by at least 3 across datasets, the adjustment was a good idea and can be justified by just this:
%   the LME increased, so you had a better model.
%
% --------------------------------------------------------------------------------------------------
% Copyright (C) 2016 Christoph Mathys, TNU, UZH & ETHZ
%
% This file is part of the HGF toolbox, which is released under the terms of the GNU General Public
% Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL
% (either version 3 or, at your option, any later version). For further details, see the file
% COPYING or <http://www.gnu.org/licenses/>.

% Config structure
c = struct;

% Model name
c.model = 'Kalman filter';

% Sufficient statistics of Gaussian parameter priors

% PLACEHOLDER VALUES
% It is often convenient to set some priors to values
% derived from the inputs. This can be achieved by
% using placeholder values. The available placeholders
% are:
%
% 99991   Value of the first input
%         Usually a good choice for mu_0mu(1)
% 99992   Variance of the first 20 inputs
%         Usually a good choice for mu_0sa(1)
% 99993   Log-variance of the first 20 inputs
%         Usually a good choice for logsa_0mu(1), and
%         its negative, ie the log-precision of the
%         first 20 inputs, for logpiumu
% 99994   Log-variance of the first 20 inputs minus two
%         Usually a good choice for ommu(1)

% Initial gain
c.logg_0mu = 0.1;
c.logg_0sa = 1;

% Initial hidden state mean
c.mu_0mu = 99991;
c.mu_0sa = 99992;

% Process variance
c.ommu = 99993;
c.omsa = 1;

% Pi_u
% Fix this to zero (no percpeptual uncertainty) by setting
% logpiumu = -Inf; logpiusa = 0;
c.logpiumu = -99993;
c.logpiusa = 1;

% Gather prior settings in vectors
c.priormus = [
    c.logg_0mu,...
    c.mu_0mu,...
    c.ommu,...
    c.logpiumu,...
         ];

c.priorsas = [
    c.logg_0sa,...
    c.mu_0sa,...
    c.omsa,...
    c.logpiusa,...
         ];

% Model function handle
c.prc_fun = @tapas_kf;

% Handle to function that transforms perceptual parameters to their native space
% from the space they are estimated in
c.transp_prc_fun = @tapas_kf_transp;

return;


% eeg_point2lat() - convert latency in data points to latency in ms relative
%                   to the time locking. Used in eeglab().
% Usage:
%       >> [newlat ] = eeg_point2lat( lat_array, [], srate);
%       >> [newlat ] = eeg_point2lat( lat_array, epoch_array,...
%                                 srate, timelimits, timeunit);
% Inputs:
%   lat_array   - latency array in data points assuming concatenated
%                 data epochs (see eeglab() event structure)
%   epoch_array - epoch number corresponding to each latency value
%   srate       - data sampling rate in Hz
%   timelimits  - [min max] timelimits in 'timeunit' units (see below)
%   timeunit    - time unit in second. Default is 1 = seconds.
%
% Outputs:
%   newlat      - converted latency values (in 'timeunit' units) for each epoch
%
% Example:
%   tmpevent = EEG.event;
%   eeg_point2lat( [ tmpevent.latency ], [], EEG.srate);
%   % returns the latency of all events in second for a continuous
%   % dataset EEG
%
%   eeg_point2lat( [ tmpevent.latency ], [ tmpevent.epoch ],
%                 EEG.srate, [EEG.xmin EEG.xmax]*1000, 1E-3);
%   % returns the latency of all events in millisecond for a dataset
%   % containing data epochs.
%
%
% Author: Arnaud Delorme, CNL / Salk Institute, 2 Mai 2002
%
% See also: eeg_lat2point(), eeglab(), pop_editieventvals(), pop_loaddat()

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

function newlat = eeg_point2lat( lat_array, epoch_array, srate, timewin, timeunit);

if nargin <3
    help eeg_point2lat;
    return;
end;
if isempty( epoch_array )
    epoch_array = ones( size(lat_array) );
end;
if nargin <4
    timewin = 0;
end;
if nargin <5
	timeunit = 1;
end;

if length(lat_array) ~= length(epoch_array)
	if length(epoch_array)~= 1
		disp('eeg_point2lat: latency and epoch arrays must have the same length'); return;
	else
		epoch_array = ones(1,length(lat_array))*epoch_array;
	end;
end;
if length(timewin) ~= 2
    disp('eeg_point2lat: timelimits array must have length 2'); return;
end;
if iscell(epoch_array)
	epoch_array = [ epoch_array{:} ];
end;
if iscell(lat_array)
	lat_array = [ lat_array{:} ];
end

timewin = timewin*timeunit;

if length(timewin) == 2
    pnts = (timewin(2)-timewin(1))*srate+1;
else
    pnts = 0;
end;
newlat  = ((lat_array - (epoch_array-1)*pnts-1)/srate+timewin(1))/timeunit;
newlat = round(newlat*1E9)*1E-9;


function [Population,FrontNo] = Select(Population,FrontNo,y)
% And one offspring to the population and delete the worst one

%------------------------------- Copyright --------------------------------
% Copyright (c) 2023 BIMK Group. You are free to use the PlatEMO for
% research purposes. All publications which use this platform or any code
% in the platform should acknowledge the use of "PlatEMO" and reference "Ye
% Tian, Ran Cheng, Xingyi Zhang, and Yaochu Jin, PlatEMO: A MATLAB platform
% for evolutionary multi-objective optimization [educational forum], IEEE
% Computational Intelligence Magazine, 2017, 12(4): 73-87".
%--------------------------------------------------------------------------

    %% Identify the solutions in the last front
    Population = [Population,y];
    PopObj     = Population.objs;
    [N,M]      = size(PopObj);
    FrontNo    = UpdateFront(PopObj,FrontNo);

    %% Delete the worst solution
    if max(FrontNo) > 1
        Distance  = pdist2(PopObj,PopObj);
        Distance(logical(eye(N))) = inf;
        LastFront = find(FrontNo==max(FrontNo));
        [~,worst] = min(min(Distance(LastFront,:),[],2));
        worst     = LastFront(worst);
    else
        deltaS = inf(1,N);
        if M == 2
            [~,rank] = sortrows(PopObj);
            for i = 2 : N-1
                deltaS(rank(i)) = (PopObj(rank(i+1),1)-PopObj(rank(i),1)).*(PopObj(rank(i-1),2)-PopObj(rank(i),2));
            end
        elseif N > 1
            deltaS = CalHV(PopObj,max(PopObj,[],1)*1.1,1,10000);
        end
        [~,worst] = min(deltaS);
    end
    FrontNo = UpdateFront(PopObj,FrontNo,worst);
    Population(worst) = [];
end


classdef CAEAD < ALGORITHM
% <multi> <real/integer/label/binary/permutation> <constrained>
% Dual-population evolutionary algorithm based on alternative evolution and degeneration
% type --- 1 --- Type of operator (1. DE 2. GA)

%------------------------------- Reference --------------------------------
% J. Zou, R. Sun, S. Yang, and J. Zheng, A dual-population algorithm based
% on alternative evolution and degeneration for solving constrained multi-
% objective optimization problems, Informaction Scinece, 2021, 239: 89-102.
%------------------------------- Copyright --------------------------------
% Copyright (c) 2023 BIMK Group. You are free to use the PlatEMO for
% research purposes. All publications which use this platform or any code
% in the platform should acknowledge the use of "PlatEMO" and reference "Ye
% Tian, Ran Cheng, Xingyi Zhang, and Yaochu Jin, PlatEMO: A MATLAB Platform
% for Evolutionary Multi-Objective Optimization [Educational Forum], IEEE
% Computational Intelligence Magazine, 2017, 12(4): 73-87".
%--------------------------------------------------------------------------

   methods
       function main(Algorithm,Problem)
           type = Algorithm.ParameterSet(1);

           %% Generate random population
           Population1 = Problem.Initialization();
           Population2 = Problem.Initialization();
           Fitness1    = CalFitness(Population1.objs,Population1.cons,0);
           Fitness2    = CalFitness(Population2.objs,Population2.cons,1e6);

           min_epsilon      = 1e-4;
           change_threshold = 1e-2;
           max_change       = 1;
           epsilon_k        = 1e8;
           tao              = 0.05;
           max_ep           = 0;
           gen              = 1;
           stage            = false;

           %% Optimization
           while Algorithm.NotTerminated(Population1)
               pop_cons2      = Population2.cons;
               cv2            = overall_cv(pop_cons2);
               population     = [Population2.decs,Population2.objs,cv2];
               Objvalues(gen) = sum(sum(Population2.objs,1));
               ep(gen)        =  epsilon_k;
               if type == 1
                   MatingPool1 = TournamentSelection(2,2*Problem.N,Fitness1);
                   MatingPool2 = TournamentSelection(2,2*Problem.N,Fitness2);
                   Offspring1  = OperatorDE(Problem,Population1,Population1(MatingPool1(1:end/2)),Population1(MatingPool1(end/2+1:end)));
                   Offspring2  = OperatorDE(Problem,Population2,Population2(MatingPool2(1:end/2)),Population2(MatingPool2(end/2+1:end)));
               elseif type == 2
                   MatingPool1 = TournamentSelection(2,Problem.N,Fitness1);
                   MatingPool2 = TournamentSelection(2,Problem.N,Fitness2);
                   Offspring1  = OperatorGAhalf(Problem,Population1(MatingPool1));
                   Offspring2  = OperatorGAhalf(Problem,Population2(MatingPool2));
               end
               [FrontNo2,~] = NDSort(Population2.objs,size(Population2.objs,1));
               NC2 = size(find(FrontNo2==1),2);
               if gen ~= 1
                   max_change = abs(Objvalues(gen)-Objvalues(gen-1));
               end
               if max_change <= change_threshold &&NC2 == Problem.N && stage == false
                   epsilon_k = max(population(:,end),[],1);
                   stage = true;
               end
               Offspring3 = [];
               if stage == true
                   if type == 1
                       Offspring3 = OperatorDE(Problem,Population1,Population2(MatingPool2(1:end/2)),Population2(MatingPool2(end/2+1:end)));
                   elseif type == 2
                       for i=1:Problem.N/2
                           Offtemp = OperatorGAhalf(Problem,[Population1(MatingPool1(i)),Population2(MatingPool2(i))]);
                           Offspring3 = [Offspring3,Offtemp];
                       end
                   end
               end
               if stage == true
                   [stage,epsilon_k] =  update_epsilon(stage,tao,epsilon_k,max_ep,min_epsilon);
               end
               if epsilon_k < 9e5
                   max_ep = max(max_ep,epsilon_k);
               end
               [Population1,Fitness1] = EnvironmentalSelection([Population1,Offspring1,Offspring2,Offspring3],Problem.N,true,0);
               [Population2,Fitness2] = EnvironmentalSelection([Population2,Offspring2],Problem.N,false,epsilon_k);
               gen = gen+1;
           end
       end
   end
end

function result = overall_cv(cv)
    cv(cv <= 0) = 0;cv = abs(cv);
    result = sum(cv,2);
end

function [stage,result] = update_epsilon(stage,tao,epsilon_k,epsilon_0,min_epsilon)
    if epsilon_k > min_epsilon
        result = (1 - tao) * epsilon_k;
        stage = true;
    else
        result = epsilon_0;
        stage = false;
    end
end


% Same syntax as subplot(nrow,ncol,indx)
% except that everytying is plotted much tighter together.
%
function sptight(nrow,ncol,indx)
    if (indx > nrow*ncol)
       error('invalid index');
    end

    intcol = 1/ncol;
    introw = 1/nrow;

    % compute the row,col we are in
    myrow = ceil(indx/ncol);
    mycol = mod(indx,ncol);
    if mycol==0, mycol = ncol; end

    left = intcol*(mycol-1);
    bottom = introw*(nrow-myrow);

    % make sub-plot
    subplot('Position',[left,bottom,intcol,introw]);
    set(gca,'XTick',[],'YTick',[]);
end


% This is material illustrating the methods from the book
% Financial Modelling  - Theory, Implementation and Practice with Matlab
% source
% Wiley Finance Series
% ISBN 978-0-470-74489-5
%
% Date: 02.05.2012
%
% Authors:  Joerg Kienitz
%           Daniel Wetterau
%
% Please send comments, suggestions, bugs, code etc. to
% kienitzwetterau_FinModelling@gmx.de
%
% (C) Joerg Kienitz, Daniel Wetterau
%
% Since this piece of code is distributed via the mathworks file-exchange
% it is covered by the BSD license
%
% This code is being provided solely for information and general
% illustrative purposes. The authors will not be responsible for the
% consequences of reliance upon using the code or for numbers produced
% from using the code.

function PlotIt(X1, YMatrix1)
%CREATEFIGURE(X1,YMATRIX1)
%  X1:  vector of x data
%  YMATRIX1:  matrix of y data

%  Auto-generated by MATLAB on 22-May-2012 09:40:50

% Create figure
figure1 = figure('PaperSize',[20.98 29.68],'Color',[1 1 1]);

% Create axes
axes1 = axes('Parent',figure1);
hold('all');

% Create multiple lines using matrix input to plot
plot1 = plot(X1,YMatrix1,'Parent',axes1,'Color',[0 0 0]);
set(plot1(1),'MarkerSize',2,'Marker','*','DisplayName','N=4');
set(plot1(2),'MarkerSize',3,'Marker','x','DisplayName','N=5');
set(plot1(3),'LineStyle','-.','DisplayName','N=6');
set(plot1(4),'LineStyle',':','DisplayName','N=7');
set(plot1(5),'LineStyle','--','DisplayName','N=8');
set(plot1(6),'Marker','.','DisplayName','N=9');
set(plot1(7),'Marker','o');

% Create xlabel
xlabel('Strike');

% Create ylabel
ylabel('Price');

% Create title
title('\Gamma Bermuda Option COS');

% Create legend
legend(axes1,'show');


function showHOG(w)

hogim = HOGpicture(w);
imagesc(hogim)
axis image
axis off
grid on
drawnow