function linplus_test534 ( )

%*****************************************************************************80
%
%% TEST534 tests R8S3_WRITE.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    14 March 2009
%
%  Author:
%
%    John Burkardt
%
  n = 100;
  nz_num = 3 * n - 2;
  output_file = 'r8s3_matrix.txt';

  fprintf ( 1, '\n' );
  fprintf ( 1, 'TEST534\n' );
  fprintf ( 1, '  For a R8S3 system,\n' );
  fprintf ( 1, '  R8S3_WRITE writes the matrix to a file.\n' );
  fprintf ( 1, '\n' );
  fprintf ( 1, '  Matrix order N =         %d\n', n );
  fprintf ( 1, '  Matrix nonzeros NZ_NUM = %d\n', nz_num );

  isym = 0;
%
%  Set the matrix values.
%
  k = 0;
  for i = 1 : n
    k = k + 1;
    j = i;
    row(k) = i;
    col(k) = j;
    a(k) = 100 * i + j;
  end

  for i = 2 : n
    j = i - 1;
    k = k + 1;
    row(k) = i;
    col(k) = j;
    a(k) = 100 * i + j;
  end

  for i = 1 : n-1
    j = i + 1;
    k = k + 1;
    row(k) = i;
    col(k) = j;
    a(k) = 100 * i + j;
  end

  r8s3_print_some ( n, n, nz_num, isym, row, col, a, 1, 1, ...
    10, 10, '  Initial 10x10 block of R8S3 matrix:' );

  r8s3_write ( n, nz_num, isym, row, col, a, output_file );

  fprintf ( 1, '\n' );
  fprintf ( 1, '  R8S3_WRITE wrote the matrix data to "%s"\n.', ...
    output_file );

  return
end


%% SOBOL_TEST02 tests BIT_HI1_BASE_2.
%
fprintf ( 1, '\n' );
fprintf ( 1, 'SOBOL_TEST02\n' );
fprintf ( 1, '  BIT_HI1_BASE_2 returns the location of the high 1 bit.\n' );

fprintf ( 1, '\n' );
fprintf ( 1, '     I     BIT_HI1_BASE_2\n' );
fprintf ( 1, '\n' );

seed = get_seed ( 0 );

for ( test = 1 : 10 )

  [ i, seed ] = i_random ( 0, 100, seed );

  j = bit_hi1_base_2 ( i );

  fprintf ( 1, '%6d %6d %6d\n', i, j );

end


% Test file for @chebfun/assignColumns.m.

function pass = test_assignColumns(pref)

if ( nargin < 1 )
    pref = chebfunpref();
end

funlist = {@chebfun, @quasimatrix};

for k = 1:2
    myfun = funlist{k};

    % Generate a few random points to use as test values.
    seedRNG(6178);
    x = 2 * rand(10, 1) - 1;

    % Check a few examples.
    f = myfun(@(x) [sin(x) cos(x) exp(x)], [-1 0 1], pref);
    g = myfun(@(x) x, [-1 1], pref);

    h = assignColumns(f, 1, g);
    h_exact = @(x) [x cos(x) exp(x)];
    err = feval(h, x) - h_exact(x);
    pass(k,1) = norm(err(:), inf) < 10*vscale(h)*eps;

    h = assignColumns(f, 3, g);
    h_exact = @(x) [sin(x) cos(x) x];
    err = feval(h, x) - h_exact(x);
    pass(k,2) = norm(err(:), inf) < 10*vscale(h)*eps;

    g = myfun(@(x) [x x.^2], [-1 0 1], pref);

    h = assignColumns(f, [3 1], g);
    h_exact = @(x) [x.^2 cos(x) x];
    err = feval(h, x) - h_exact(x);
    pass(k,3) = norm(err(:), inf) < 10*vscale(h)*eps;

    h = assignColumns(f, [2 2], g);
    h_exact = @(x) [sin(x) x.^2 exp(x)];
    err = feval(h, x) - h_exact(x);
    pass(k,4) = norm(err(:), inf) < 10*vscale(h)*eps;

    g = myfun(@(x) [x x.^2 x.^3], [-1 -0.5 0.5 1], pref);

    h = assignColumns(f, [1 2 3], g);
    h_exact = @(x) [x x.^2 x.^3];
    err = feval(h, x) - h_exact(x);
    pass(k,5) = norm(err(:), inf) < 10*vscale(h)*eps;

    hc = assignColumns(f, ':', g);
    pass(k,6) = isequal(hc, h);

    h  = assignColumns(f, [2 1], [-0.5 0.5]);
    h_exact = @(x) [(0.5 + 0*x) (-0.5 + 0*x) exp(x)];
    err = feval(h, x) - h_exact(x);
    pass(k,7) = norm(err(:), inf) < 10*vscale(h)*eps;

    h = assignColumns(f.', [2 1], [-0.5 ; 0.5]);
    h_exact = @(x) [(0.5 + 0*x) (-0.5 + 0*x) exp(x)].';
    err = feval(h, x) - h_exact(x);
    pass(k,8) = norm(err(:), inf) < 10*vscale(h)*eps;

    % Check error conditions.
    try
        h = assignColumns(f, [1 2 3], g.');
        pass(k,9) = false;
    catch ME
        pass(k,9) = strcmp(ME.identifier, ...
            'CHEBFUN:CHEBFUN:assignColumns:numCols');
    end

    try
        h = assignColumns(f, [1 2], g);
        pass(k,10) = false;
    catch ME
        pass(k,10) = strcmp(ME.identifier, ...
            'CHEBFUN:CHEBFUN:assignColumns:numCols');
    end

    try
        g2 = myfun(@(x) [x x.^2 x.^3], [0 1], pref);
        h = assignColumns(f, [1 2 3], g2);
        pass(k,11) = false;
    catch ME
        pass(k,11) = strcmp(ME.identifier, ...
            'CHEBFUN:CHEBFUN:assignColumns:domain');
    end

    % Test assign outside of dimension of f:
    h = assignColumns(f, [1 2 4], g);
    h_exact = @(x) [x x.^2 exp(x) x.^3];
    err = feval(h, x) - h_exact(x);
    pass(k,12) = norm(err(:), inf) < 10*vscale(h)*eps;

    %% Test for function defined on unbounded domain:

    % Functions on [-inf b]:

    % Set the domain:
    dom = [-Inf -3*pi];
    domCheck = [-1e6 -3*pi];

    % Generate a few random points to use as test values:
    x = diff(domCheck) * rand(100, 1) + domCheck(1);

    % Array-valued function:
    op = @(x) [exp(x) x.*exp(x) (1-exp(x))./x];
    opg = @(x) exp(-x.^2);
    oph = @(x) [exp(x) exp(-x.^2) (1-exp(x))./x];

    f = myfun(op, dom);
    g = myfun(opg, dom);
    h = assignColumns(f, 2, g);
    err = feval(h, x) - oph(x);
    pass(k,13) = norm(err, inf) < 1e2*vscale(h)*eps;

    %% Test removing columns (#889)

    x = chebfun('x');
    xx = [x 2*x];
    if ( k == 2 )
        xx = cheb2quasi(xx);
    end
    xx(:,2) = [];
    pass(k,14) = numColumns(xx) == 1 && norm(x - xx) < eps;

    x = chebfun('x');
    xx = [x 2*abs(x)];
    if ( k == 1 )
        xx = quasi2cheb(xx);
    end
    xx(:,2) = [];
    pass(k,15) = numColumns(xx) == 1 && norm(x - xx) < eps;

end

end


function [gSig, gSiz, ring_radius, min_corr, min_pnr] = set_parameters(obj)
%% choose parameters used in CNMF-E

%% Author: Pengcheng Zhou, Columbia University, 2017
%zhoupc1988@gmail.com

gSiz = obj.options.gSiz;
gSig = obj.options.gSig;
ring_radius = obj.options.ring_radius;
min_corr = obj.options.min_corr;
min_pnr = obj.options.min_pnr;

%% choose gSiz, which is the neuron diameter
fprintf('\n*** Determine the neuron diameter (gSiz) and the gaussian width (gSig) of the filtering kernel.\n');
fprintf('\t-------------------------- GUIDE --------------------------\n');
fprintf('\tgSiz is usually slightly larger than a neuron;\n');
fprintf('\tgSig is usually selected as 1/4 of gSiz when the data is 1p\n');
fprintf('\tgSig is usually selected as 1 when the data is 2p.\n');
fprintf('\tIntegers are preferred for both values, but not necessary.\n');
fprintf('\tOdd number is preferred for gSiz. \n');
fprintf('\tWhen you want to make a change, an image will be shown.\n');
fprintf('\tYou can zoon in to see the size of a typical neuron.\n');
fprintf('\t--------------------------  END  --------------------------\n\n');

fprintf('You current selection of parameters (gSiz, gSig) are (%.1f, %.1f).\n', gSiz, gSig);
temp = input('Do you want to make a change? (y/n)    ', 's');
if strcmpi(temp, 'n')
    fprintf('Your values for (gSiz, gSig) will stay the same\n');
else
    Y = get_patch_data(obj.P.mat_data, [], [1,100]);
    [d1,d2, T] = size(Y);
    Y = reshape(Y, d1*d2, T);
    fprintf('Wait for a few second ....\n');
    if obj.options.center_psf
    [u, v] = nnmf(double(Y),1);
        img = reshape(max(double(Y)-u*v, [], 2), d1,d2);
    else
            img = reshape(max(Y,[],2), d1,d2);
    end
    fig_1 = figure;
    imagesc(img); axis equal;
    while true
        temp = input('type new values for (gSiz, gSig):  ', 's');
        try
            new_values = str2num(temp); %#ok<*ST2NM>
            gSiz = new_values(1);
            gSig = new_values(2);
            obj.options.gSig = gSig;
            obj.options.gSiz = gSiz;
            fprintf('Good! You current selection of parameters (gSiz, gSig) are (%.1f, %.1f).\n', gSiz, gSig);
            break;
        catch
            warning('values are bad. try again');
            continue;
        end
    end
    try
        close(fig_1);
    end
end

%%
%% choose the radius of the ring
if strcmpi('obj.options.background_model', 'ring')
    fprintf('\n*** Determine the ring radius for estimating background fluctuations.\n');
    fprintf('\t-------------------------- GUIDE --------------------------\n');
    fprintf('\tThe ring radius is selected to be larger than neuron diameters. \n');
    fprintf('\tThus pixels in the center only shares the common background fluctuations with pixels on the ring.\n');
    fprintf('\tA usual ratio between ring radius and neuron diameter is between 1 to 3. 1.5 is the default value.\n');
    fprintf('\tSmall values have a change of being smaller than neuron size.\n');
    fprintf('\tLarge values increase the computation cost.\n');
    fprintf('\t--------------------------  END  --------------------------\n\n');

    fprintf('You current selection of ring_radius is %d pixels.\n', ring_radius);
    temp = input('Do you want to make a change? (y/n)    ', 's');
    if strcmpi(temp, 'n')
        fprintf('Your values for ring_radius will stay the same\n');
    else
        while true
            temp = input('type new values for ring_radius:  ', 's');
            try
                ring_radius = str2double(temp);
                obj.options.ring_radius = ring_radius;
                fprintf('Good! You current selection of parameters ring_radius is %2d.\n', ring_radius);
                break;
            catch
                warning('values are bad. try again');
                continue;
            end
        end
    end
end
%% choose parameters for min_corr and min_pnr
fprintf('\n*** Determine min_corr and min_pnr for screening seed pixels.\n');
fprintf('\t-------------------------- GUIDE --------------------------\n');
fprintf('\tA seed pixel is used for initializing one neuron.\n');
fprintf('\tSeed pixel usually locates at the center of each neuron.\n');
fprintf('\tSeed pixel is determined using two values: min_corr and min_pnr.\n');
fprintf('\tmin_pnr stands for minimum local correlation. \n');
fprintf('\tPNR stands for minimum peak-to-noise ratio.\n');
fprintf('\tWe want to pick thresholds that all neuron centers are above the thresholds,\n');
fprintf('\tand include the least number of non-ROI pixels. \n');
fprintf('\tThere is a trade-of between missing neurons and acquiring more false positives.\n');
fprintf('\tWhen it is hard to determine, we tend to choose missing neurons, i.e., larger threshlds.\n');
fprintf('\tThose missed neurons can be picked up from the residual video later.\n');
fprintf('\tTip: use Data Cursor to help you pick thresholds.\n');
fprintf('\t--------------------------  END  --------------------------\n\n');

fprintf('You current selection of parameters (min_corr, min_pnr) are (%.3f, %.3f).\n', min_corr, min_pnr);
temp = input('Do you want to make a change? (y/n)    ', 's');
if strcmpi(temp, 'n')
    fprintf('Your values for (min_corr, min_pnr) will stay the same\n');
else

    if isempty(obj.Cn) || isempty(obj.PNR)
        fprintf('computing the correlation image and the peak-to-noise ratio image....\n');
        [cn, pnr] = obj.correlation_pnr_parallel([1, 5000]);
        obj.Cn = cn;
        obj.PNR = pnr;
        fprintf('Done\n');
    else
        cn = obj.Cn;
        pnr = obj.PNR;
    end

    %find all local maximum as initialization point
    tmp_d = max(1,round(gSiz/4));
    v_max = ordfilt2(cn.*pnr, tmp_d^2, true(tmp_d));
    ind = (v_max==cn.*pnr);

    figure('papersize', [12, 3]);
    init_fig;
    subplot(131);
    imagesc(cn, [0, 1]);
    title('local corr. image');
    axis equal off tight;
    subplot(132);
    pnr_vmax = max(pnr(:))*0.8;
    imagesc(pnr, [3, pnr_vmax]);
    axis equal off tight;
    title('PNR image');
    subplot(133);
    imagesc(cn.*pnr, [3, pnr_vmax]);
    hold on;

    tmp_ind = ind & (cn>=min_corr) & (pnr>=min_pnr);
    [r, c] = find(tmp_ind);
    ax_seeds = plot(c, r, '.m', 'markersize', 10);
    axis equal off tight;
    title('candidate seed pixels');
    ylabel('PNR');
    xlabel('Cn');

    while true
        temp = input('type new values for (min_corr, min_pnr):  ', 's');
        try
            new_values = str2num(temp); %#ok<*ST2NM>
            min_corr = new_values(1);
            min_pnr = new_values(2);
            obj.options.min_corr = min_corr;
            obj.options.min_pnr = min_pnr;
            tmp_ind = ind & (cn>=min_corr) & (pnr>=min_pnr);
            [r, c] = find(tmp_ind);
            delete(ax_seeds);
            subplot(133);
            ax_seeds = plot(c, r, '.m', 'markersize', 10);

            temp = input('Is this good? (y/n)   ', 's');
            if strcmpi(temp, 'y')
                fprintf('Good! You current selection of parameters (min_corr, min_pnr) are (%2d, %2d).\n', min_corr, min_pnr);
                break;

            end
        catch
            warning('values are bad. try again');
            continue;
        end
    end
end

fprintf('\nThe parameters used in CNMF-E are \ngSig:\t\t%d\ngSiz:\t\t%d\nring_radius\t%d\nmin_corr:\t%.3f\nmin_pnr:\t%.3f\n', ...
    gSig, gSiz, ring_radius, min_corr, min_pnr);
end


 function val = byte_to_uint32(buff)
%function val = byte_to_uint32(buff)
% convert 4-byte buffer into uint32

try
	val = typecast(buff, 'uint32')
catch
	u = @(x) uint32(x);
	buff = u(buff);
	s = u(256);
	val = buff(4) + s * (buff(3) + s * (buff(2) + s * buff(1)));
end


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%% BREATHING FILTER DETECTION TASK ANALYSIS %%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% -------------------------------------------------------------------------
% Author: Olivia Harrison
% Created: 14/08/2018
%
% This software 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 software 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: http://www.gnu.org/licenses/
% -------------------------------------------------------------------------

% This analysis script uses either Brian Maniscalco's single subect
% analysis or Steve Fleming's Hierarchical Bayesian toolbox (for group
% analysis) to calculate perceptual decision and metacognitive metrics,
% specific to data produced by running the tapas_filter_detection_task.

% IMPORTANT ANALYSIS NOTES:
%   1) The code requires JAGS software to run, which can be found here:
%      http://mcmc-jags.sourceforge.net/
%      The code has been tested on JAGS 3.4.0; there are compatibility
%      issues between matjags and JAGS 4.X.
%   2) The code requires the HMeta-d toolbox (provided as a submodule
%      from https://github.com/metacoglab/HMeta-d) to be on your path.
%   3) This script is looking for specific results files that were output
%      from the tapas_filter_detection_task, which are located in the
%      FDT/results/ folder. The results are expected to contain a
%      results.filterThreshold.xx structure, where values for xx:
%         - results.filterThreshold.filterNum: a value to determine the
%           number of filters where trials were performed
%         - results.filterThreshold.filters: a vector with 1 meaning
%           filters were presented, 0 when filters were absent (dummy)
%         - results.filterThreshold.response: a vector with 1 meaning
%           response was 'yes', 0 when response was 'no'
%         - results.filterThreshold.confidence: a vector containing
%           confidence score (1-10) on each trial
%      If your results are formatted differently, refer directly to the
%      original scripts from the HMeta-d toolbox
%      (https://github.com/metacoglab/HMeta-d).

% TO RUN THIS SCRIPT:
% type tapas_filter_detection_analysis into the MATLAB terminal from the
% main FDT folder, and follow the prompts.

% ANALYSIS OPTIONS:
% This script allows you to run either:
%    - A single subject analysis: A non-Bayesian estimation of meta-d',
%      fitting one subject at a time using a single point maximum
%      likelihood estimation (from the original Maniscalco & Lau 2012
%      paper, more info can be found here:
%      http://www.columbia.edu/~bsm2105/type2sdt/archive/index.html).
%      HOWEVER: Simulations have shown this analysis to be VERY unstable
%      when estimating meta-d' using 60 trials. It is STRONGLY encouraged
%      to utilise the hierarchical models, or collect many more trials
%      (200+ trials) for each subject to have a more reliable measure of
%      meta-d'.
%    - A group mean analysis: A hierarchical Bayesian analysis, whereby all
%      subjects are fit together and information from the group mean is
%      used as prior information for each subject. This hierarchical model
%      helps with much more accurate estimations of meta-d' with small
%      trial numbers, such as using 60 trials per subject.
%    - A group regression analysis: A hierarchical Bayesian regression
%      analysis that is an extension on the group mean analysis, whereby
%      subjects are fit together in one model, and the relationship between
%      single subject log(meta-d'/d') values and the covariate(s) is
%      estimated within the hierarchical model structure.
%    - A group difference analysis: A hierarchical Bayesian analysis,
%      whereby each group of subjects is fitted separately, and the groups
%      are then compared. Frequentist statistics (such as parametric
%      unpaired T-tests, or non-parametric Wilcoxon signed-rank tests) can
%      be used for all values that are not fitted using hierarchical
%      information, such as d', c, filter number, accuracy and average
%      confidence. As the group values for log(meta-d'/d') are calculated
%      using two separate hierarchical models, group differences are then
%      inferred via comparison of the resulting log(meta-d'/d')
%      distributions, and whether the 95% highest-density interval (HDI) of
%      the difference between the distributions spans zero. The HDI is the
%      shortest possible interval containing 95% of the MCMC samples, and
%      may not be symmetric. NOTE: If single subject values for
%      log(meta-d'/d') (or any related meta-d' metric) are required for
%      further analyses that span both groups (such as entry into general
%      linear models), it is recommended to fit all subjects together in
%      one regression model with a regressor denoting group identity.
%    - A session difference analysis (paired group difference): A
%      hierarchical Bayesian analysis, whereby two sessions / measures from
%      the same participants are fitted in a single model using a
%      multivariate normal distribution. This distribution allows for the
%      non-independence between sessions for each participant. NOTE:
%      Participants must be listed in the same order for analysis, and must
%      have data for both sessions / each measure.

% NOTES ON NON-BAYESIAN SINGLE SUBJECT ANALYSIS FROM AUTHORS' WEBSITE
% (columbia.edu/~bsm2105/type2sdt/archive/index.html):
% This analysis is intended to quantify metacognitive sensitivity (i.e. the
% efficacy with which confidence ratings discriminate between correct and
% incorrect judgments) in a signal detection theory framework. A central
% idea is that primary task performance can influence metacognitive
% sensitivity, and it is informative to take this influence into account.
% Description of the methodology can be found here: Maniscalco, B., & Lau,
% H. (2012). A signal detection theoretic approach for estimating
% metacognitive sensitivity from confidence ratings. Consciousness and
% Cognition, 21(1), 422–430. doi:10.1016/j.concog.2011.09.021
% If you use these analysis files, please reference the Consciousness &
% Cognition paper and website. Brian Maniscalco: brian@psych.columbia.edu

% NOTES ON HIERARCHICAL TOOLBOX FROM ORIGINAL SCRIPTS (FOR GROUP ANALYSES):
% This MATLAB toolbox implements the meta-d’ model (Maniscalco & Lau, 2012)
% in a hierarchical Bayesian framework using Matlab and JAGS, a program for
% conducting MCMC inference on arbitrary Bayesian models. A paper with more
% details on the method and the advantages of estimating meta-d’ in a
% hierarchal Bayesian framework is available here https://academic.oup.com/
% nc/article/doi/10.1093/nc/nix007/3748261/HMeta-d-hierarchical-Bayesian-
% estimation-of. For a more general introduction to Bayesian models of
% cognition see Lee & Wagenmakers, Bayesian Cognitive Modeling: A Practical
% Course http://bayesmodels.com/. This code is being released with a
% permissive open-source license. You should feel free to use or adapt the
% utility code as long as you follow the terms of the license provided. If
% you use the toolbox in a publication we ask that you cite the following
% paper: Fleming, S.M. (2017) HMeta-d: hierarchical Bayesian estimation of
% metacognitive efficiency from confidence ratings, Neuroscience of
% Consciousness, 3(1) nix007, https://doi.org/10.1093/nc/nix007. Copyright
% (c) 2017, Stephen Fleming. For more information and/or licences, please
% see the original code: https://github.com/metacoglab/HMeta-d.

% KEY ANALYSIS OUTPUT VALUES:
% The primary measures to come out of this analysis can be found in
% analysis.{single/groupMean/groupDiff}.xx, and they are:
%   1) xx = filterNum: Number of filters. Less filters means more sensitive
%           to changes in breathing.
%   2) xx = d1: d prime, discriminability between filter and dummy trials.
%           Larger d' means more discriminability at specific filter number
%   3) xx = c1: Decision criterion, or where the decision boundary exists.
%           Negative criterion values mean bias towards 'yes' (lower, more
%           liberal criterion), while positive values mean bias towards
%           'no' response (higher, more strngent criterion).
%   4) xx = meta_d: A measure of metacognition, or 'type 2' sensitivity.
%           This reflects how much information, in signal-to-noise units,
%           is available for metacognition (see Maniscalco & Lau, 2012).
%           NB: ISSUES WITH THIS ESTIMATION (AND ALL RELATED ESTIMATIONS)
%           IF USING SINGLE SUBJECT ANALYSES, AS DESCRIBED ABOVE.
%   5) xx = Mratio: Meta-d' to d' ratio, as a measure of metacognitive
%           'efficiency' (see Rouault et al., 2018).
%   6) xx = log_Mratio: log(meta_d/d1), reported in papers to help with
%           normalisation of data (see Rouault et al., 2018).
%   7) xx = avgConfidence: A second measure of metacognition, thought to be
%           independent from meta-d'. NB: Average confidence should only be
%           compared between two groups if there is no difference in d'
%           between the groups (i.e. task difficulty was comparable).

% ADDITIONAL GROUP DIFFERENCE OUTPUT VALUES:
% If the analysis is a two-group or two-session (paired) difference, the
% following will also be calculated for the non-hierarchical measures
% (test = groupDiff or sessionDiff; xx = filterNum, d1, c1 and
% avgConfidence):
%   1) analysis.test.xx.h = Results of null hypothesis test, where h =
%      1 for rejection of the null, and h = 0 for no rejection.
%   2) analysis.test.xx.p = The p-value for the statistical test.
%   3) analysis.test.xx.stats = Further statistics (such as tstats,
%      number of samples, degrees of freedom) associated with the test.
%   4) analysis.test.xx.ci = Confidence interval of the difference,
%      calculated only when T-test is specified.
% The highest density interval (HDI) for the difference in log(meta-d'/d')
% between the groups / sessions will be calculated and recorded, as
% frequentist statistics cannot be used here. A summary Figure for each of
% these metrics will be created and saved in the FDT/analysis/ folder.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% SET UP THE ANALYSIS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function tapas_filter_detection_analysis()

% Check folder location is main FDT folder
[~,dir_name] = fileparts(pwd);
if ~strcmp(dir_name,'FDT')
   error('Not currently in main FDT folder. Please move to FDT folder and try again.');
end

% Add relevant paths
addpath('analysis');

% Display setup on screen
fprintf('\n________________________________________\n\n    SET UP ANALYSIS FOR FILTER TASK\n________________________________________\n');

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% CHOOSE THE TYPE OF ANALYSIS TO RUN AND SPECIFY FILES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

try
    analysis.type = input('Type of analysis (single or group) = ', 's'); % Ask for type of analysis to be run
catch
    fprintf('\nOption does not exist, please try again.\n');
    return
end

try
    if strcmp(analysis.type,'single') == 1 % If single subject analysis specified
        analysis.PPIDs = input('PPID = ', 's'); % Ask for PPID
        try
            analysis.data = load(fullfile('results', ['filter_task_results_', analysis.PPIDs, '.mat'])); % Load data
            analysis.ratings = analysis.data.results.setup.confidenceUpper - analysis.data.results.setup.confidenceLower + 1; % Calculate number of confidence rating bins
        catch
            fprintf('\nInvalid PPID.\n');
            return
        end
    elseif strcmp(analysis.type,'group') == 1 % If group analysis specified
        analysis.type = input('Type of analysis (mean, diff, paired or regress) = ', 's'); % Ask for type of group analysis to be run
        if strcmp(analysis.type,'mean') == 1 % If group mean analysis specified
            analysis.PPIDs = input('Input group PPIDs (e.g. {''001'', ''002'', ''003'',...}) = '); % Ask for PPIDs
            analysis.groupsize(1) = length(analysis.PPIDs);
            for n = 1:length(analysis.PPIDs)
                PPID = char(analysis.PPIDs(n));
                try
                    analysis.data(n) = load(fullfile('results', ['filter_task_results_', PPID, '.mat'])); % Load data
                catch
                    fprintf('\nInvalid PPIDs.\n');
                    return
                end
            end
            analysis.ratings = analysis.data(1).results.setup.confidenceUpper - analysis.data(1).results.setup.confidenceLower + 1; % Calculate number of confidence rating bins
        elseif strcmp(analysis.type,'diff') == 1 % If two group difference analysis specified
            analysis.PPIDs.group1 = input('Input group 1 PPIDs (e.g. {''001'', ''002'', ''003'',...}) = '); % Ask for PPIDs
            analysis.PPIDs.group2 = input('Input group 2 PPIDs (e.g. {''004'', ''005'', ''006'',...}) = '); % Ask for PPIDs
            analysis.groupDiff.test = input('Type 1 variable tests (ttest or wilcoxon) = ', 's'); % Ask for parametric or non-parmetric test options for type 1 variables (d', c etc.)
            analysis.groupsize(1) = length(analysis.PPIDs.group1);
            analysis.groupsize(2) = length(analysis.PPIDs.group2);
            for n = 1:analysis.groupsize(1)
                PPID = char(analysis.PPIDs.group1(n));
                try
                    analysis.group1.data(n) = load(fullfile('results', ['filter_task_results_', PPID, '.mat'])); % Load data
                catch
                    fprintf('\nInvalid PPIDs for group 1.\n');
                    return
                end
            end
            for n = 1:analysis.groupsize(2)
                PPID = char(analysis.PPIDs.group2(n));
                try
                    analysis.group2.data(n) = load(fullfile('results', ['filter_task_results_', PPID, '.mat'])); % Load data
                catch
                    fprintf('\nInvalid PPIDs for group 2.\n');
                    return
                end
            end
            analysis.ratings = analysis.group1.data(1).results.setup.confidenceUpper - analysis.group1.data(1).results.setup.confidenceLower + 1; % Calculate number of confidence rating bins
        elseif strcmp(analysis.type,'paired') == 1 % If paired difference analysis specified
            fprintf('\nNOTE: PPIDs from each session need to be paired and in the same order\n');
            analysis.PPIDs.session1 = input('Input session 1 PPIDs (e.g. {''001a'', ''002a'', ''003a'',...}) = '); % Ask for PPIDs
            analysis.PPIDs.session2 = input('Input session 2 PPIDs (e.g. {''001b'', ''002b'', ''003b'',...}) = '); % Ask for PPIDs
            analysis.sessionDiff.test = input('Type 1 variable tests (ttest or wilcoxon) = ', 's'); % Ask for parametric or non-parmetric test options for type 1 variables (d', c etc.)
            analysis.sessionSize(1) = length(analysis.PPIDs.session1);
            analysis.sessionSize(2) = length(analysis.PPIDs.session2);
            if analysis.sessionSize(1) ~= analysis.sessionSize(2)
                error('Number of PPIDs in each session does not match!');
            end
            for n = 1:analysis.sessionSize(1)
                PPID = char(analysis.PPIDs.session1(n));
                try
                    analysis.session1.data(n) = load(fullfile('results', ['filter_task_results_', PPID, '.mat'])); % Load data
                catch
                    fprintf('\nInvalid PPIDs for session 1.\n');
                    return
                end
            end
            for n = 1:analysis.sessionSize(2)
                PPID = char(analysis.PPIDs.session2(n));
                try
                    analysis.session2.data(n) = load(fullfile('results', ['filter_task_results_', PPID, '.mat'])); % Load data
                catch
                    fprintf('\nInvalid PPIDs for session 2.\n');
                    return
                end
            end
            analysis.ratings = analysis.session1.data(1).results.setup.confidenceUpper - analysis.session1.data(1).results.setup.confidenceLower + 1; % Calculate number of confidence rating bins
        elseif strcmp(analysis.type,'regress') == 1 % If regression analysis specified
            analysis.PPIDs = input('Input group PPIDs (e.g. {''001'', ''002'', ''003'',...}) = '); % Ask for PPIDs
            analysis.groupsize(1) = length(analysis.PPIDs);
            for n = 1:length(analysis.PPIDs)
                PPID = char(analysis.PPIDs(n));
                try
                    analysis.data(n) = load(fullfile('results', ['filter_task_results_', PPID, '.mat'])); % Load data
                catch
                    fprintf('\nInvalid PPIDs.\n');
                    return
                end
            end
            analysis.ratings = analysis.data(1).results.setup.confidenceUpper - analysis.data(1).results.setup.confidenceLower + 1; % Calculate number of confidence rating bins
            fprintf('\nNote: Covariate text file required to be placed in results folder\n--> Scores need to be in the SAME ORDER as PPID input order.\n');
            analysis.covariate.fileName = input('Input covariate file name (e.g. covariate_example.txt) = ', 's'); % Ask for covariate file name
            try
                analysis.covariate.data = load(fullfile('results', analysis.covariate.fileName)); % Load data
            catch
                fprintf('\nCovariate file cannot be found in results folder.\n');
                return
            end
            [a,b] = size(analysis.covariate.data);
            if a > b % Re-shape vector if needed
                analysis.covariate.data = analysis.covariate.data';
            end
        end
    end
catch
    fprintf('\nInvalid.\n');
    return
end

% Save results
if strcmp(analysis.type,'single') == 1
    resultsFile = fullfile('analysis', ['filter_task_analysis_', analysis.type, analysis.PPIDs]); % Create figure file name
else
    resultsFile = fullfile('analysis', ['filter_task_analysis_', analysis.type]); % Create figure file name
end
save(resultsFile, 'analysis'); % Save results

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% RUN TRIALS2COUNTS SCRIPT
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

if strcmp(analysis.type,'single') == 1 || strcmp(analysis.type,'mean') == 1 || strcmp(analysis.type,'regress') == 1

    % Run trials2counts script for single subject or whole group
    for n = 1:length(analysis.data)
        stimID = analysis.data(n).results.thresholdTrials.filters;
        response = analysis.data(n).results.thresholdTrials.response;
        rating = analysis.data(n).results.thresholdTrials.confidence;
        [analysis.trials2counts.nR_S1{n}, analysis.trials2counts.nR_S2{n}] = trials2counts(stimID,response,rating,analysis.ratings,0); % Run for number of ratings and no cell padding
    end

elseif strcmp(analysis.type,'diff') == 1

    % Run trials2counts script for group 1
    for n = 1:length(analysis.group1.data)
        stimID = analysis.group1.data(n).results.thresholdTrials.filters;
        response = analysis.group1.data(n).results.thresholdTrials.response;
        rating = analysis.group1.data(n).results.thresholdTrials.confidence;
        [analysis.group1.trials2counts.nR_S1{n}, analysis.group1.trials2counts.nR_S2{n}] = trials2counts(stimID,response,rating,analysis.ratings,0); % Run for number of ratings and no cell padding
    end
    % Run trials2counts script for group 2
    for n = 1:length(analysis.group2.data)
        stimID = analysis.group2.data(n).results.thresholdTrials.filters;
        response = analysis.group2.data(n).results.thresholdTrials.response;
        rating = analysis.group2.data(n).results.thresholdTrials.confidence;
        [analysis.group2.trials2counts.nR_S1{n}, analysis.group2.trials2counts.nR_S2{n}] = trials2counts(stimID,response,rating,analysis.ratings,0); % Run for number of ratings and no cell padding
    end

elseif strcmp(analysis.type,'paired') == 1

    % Run trials2counts script for session 1
    for n = 1:length(analysis.session1.data)
        stimID = analysis.session1.data(n).results.thresholdTrials.filters;
        response = analysis.session1.data(n).results.thresholdTrials.response;
        rating = analysis.session1.data(n).results.thresholdTrials.confidence;
        [analysis.trials2counts.nR_S1(1).counts{n}, analysis.trials2counts.nR_S2(1).counts{n}] = trials2counts(stimID,response,rating,analysis.ratings,0); % Run for number of ratings and no cell padding
    end
    % Run trials2counts script for session 2
    for n = 1:length(analysis.session2.data)
        stimID = analysis.session2.data(n).results.thresholdTrials.filters;
        response = analysis.session2.data(n).results.thresholdTrials.response;
        rating = analysis.session2.data(n).results.thresholdTrials.confidence;
        [analysis.trials2counts.nR_S1(2).counts{n}, analysis.trials2counts.nR_S2(2).counts{n}] = trials2counts(stimID,response,rating,analysis.ratings,0); % Run for number of ratings and no cell padding
    end

end

% Save results
save(resultsFile, 'analysis');

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% RUN SINGLE SUBJECT ANALYSIS IF SPECIFIED
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

if strcmp(analysis.type,'single') == 1 % If single subject analysis specified

    % Pad ratings so that zero counts do not interfere with fit
    analysis.trials2counts.nR_S1_padded = analysis.trials2counts.nR_S1{1} + 1/(2*10);
    analysis.trials2counts.nR_S2_padded = analysis.trials2counts.nR_S2{1} + 1/(2*10);

    % Fit data
    analysis.single.fit = fit_meta_d_MLE(analysis.trials2counts.nR_S1_padded, analysis.trials2counts.nR_S2_padded);

    % Pull out key variables
    analysis.single.filterNum = mean(analysis.data.results.thresholdTrials.filterNum);
    analysis.single.accuracy = analysis.data.results.thresholdTrials.accuracyTotal;
    analysis.single.d1 = analysis.single.fit.d1;
    analysis.single.c1 = analysis.single.fit.c1;
    analysis.single.meta_d = analysis.single.fit.meta_d;
    analysis.single.Mratio = analysis.single.fit.M_ratio;
    analysis.single.fit.log_Mratio = log(analysis.single.fit.M_ratio);
    analysis.single.avgConfidence = mean(analysis.data(1).results.thresholdTrials.confidence);

    % Save results
    save(resultsFile, 'analysis'); % Save results

    % Display completion message on screen
    fprintf('\n________________________________________\n\n   COMPLETED SINGLE SUBJECT ANALYSIS\n _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _\n');
    fprintf('\n             MRATIO = %.2f\n _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _\n', analysis.single.Mratio);
    fprintf('\n        RESULTS CAN BE FOUND IN: \n             FDT/analysis/\n________________________________________\n');

end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% RUN GROUP MEAN ANALYSIS IF SPECIFIED
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

if strcmp(analysis.type,'mean') == 1 % If group mean analysis specified

    % Specify parameters
    analysis.groupMean.mcmc_params = fit_meta_d_params;

    % Fit group data all at once
    analysis.groupMean.fit = fit_meta_d_mcmc_group(analysis.trials2counts.nR_S1, analysis.trials2counts.nR_S2, analysis.groupMean.mcmc_params);

    % Calculate log of Mratio for single subjects values
    for n = 1:length(analysis.groupMean.fit.Mratio)
        analysis.groupMean.log_Mratio.singleSubject(n) = log(analysis.groupMean.fit.Mratio(n));
    end

    % Pull out group mean values
    analysis.groupMean.d1.groupMean = mean(analysis.groupMean.fit.d1);
    analysis.groupMean.d1.singleSubject = analysis.groupMean.fit.d1;
    analysis.groupMean.c1.groupMean = mean(analysis.groupMean.fit.c1);
    analysis.groupMean.c1.singleSubject = analysis.groupMean.fit.c1;
    analysis.groupMean.meta_d.groupMean = mean(analysis.groupMean.fit.meta_d);
    analysis.groupMean.meta_d.singleSubject = analysis.groupMean.fit.meta_d;
    analysis.groupMean.Mratio.groupMean = exp(analysis.groupMean.fit.mu_logMratio);
    analysis.groupMean.Mratio.singleSubject = analysis.groupMean.fit.Mratio;
    analysis.groupMean.Mratio.hdi = calc_HDI(exp(analysis.groupMean.fit.mcmc.samples.mu_logMratio(:)));
    analysis.groupMean.log_Mratio.groupMean = analysis.groupMean.fit.mu_logMratio;
    analysis.groupMean.log_Mratio.hdi = calc_HDI(analysis.groupMean.fit.mcmc.samples.mu_logMratio(:));

    % Calculate average confidence, add filter number and accuracy to analysis results
    for n = 1:length(analysis.data)
        analysis.groupMean.avgConfidence.singleSubject(n) = mean(analysis.data(n).results.thresholdTrials.confidence);
        analysis.groupMean.filterNum.singleSubject(n) = mean(analysis.data(n).results.thresholdTrials.filterNum);
        analysis.groupMean.accuracy.singleSubject(n) = analysis.data(n).results.thresholdTrials.accuracyTotal;
    end
    analysis.groupMean.avgConfidence.groupMean = mean(analysis.groupMean.avgConfidence.singleSubject);
    analysis.groupMean.filterNum.groupMean = mean(analysis.groupMean.filterNum.singleSubject);
    analysis.groupMean.accuracy.groupMean = mean(analysis.groupMean.accuracy.singleSubject);

    % Save results
    save(resultsFile, 'analysis'); % Save results

    % Display completion message on screen
    fprintf('\n________________________________________\n\n     COMPLETED GROUP MEAN ANALYSIS\n _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _\n');
    fprintf('\n GROUP MRATIO = %.2f (HDI: %.2f to %.2f)\n', analysis.groupMean.Mratio.groupMean, analysis.groupMean.Mratio.hdi(1), analysis.groupMean.Mratio.hdi(2));
    if (analysis.groupMean.Mratio.hdi(1) > 0 && analysis.groupMean.Mratio.hdi(2) > 0) || (analysis.groupMean.Mratio.hdi(1) < 0 && analysis.groupMean.Mratio.hdi(2) < 0)
        fprintf('   HDI DOES NOT SPAN ZERO: MRATIO SIG.\n _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _\n');
    else
        fprintf('   HDI SPANS ZERO: MRATIO NOT SIG.\n _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _\n');
    end
    fprintf('\n        RESULTS CAN BE FOUND IN: \n             FDT/analysis/\n________________________________________\n');

end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% RUN GROUP DIFFERENCE ANALYSIS (UNPAIRED) IF SPECIFIED
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

if strcmp(analysis.type,'diff') == 1 % If group difference analysis specified

    % For two-group difference analysis: Print message about groups being fit separately --> no t-tests possible on meta-d' metrics
    fprintf('\nNOTE: Groups are fit using separate hierarchical models.\nFrequentist statistics (such as t-tests) are not possible on any metrics involving meta-d''.\nFrequentist statistics will still be applied to all type 1 metrics and average confidence values.\n');

    % Specify parameters
    analysis.groupDiff.mcmc_params = fit_meta_d_params;

    % Fit each group using a separate hierarchical Bayesian model
    analysis.group1.fit = fit_meta_d_mcmc_group(analysis.group1.trials2counts.nR_S1, analysis.group1.trials2counts.nR_S2, analysis.groupDiff.mcmc_params);
    analysis.group2.fit = fit_meta_d_mcmc_group(analysis.group2.trials2counts.nR_S1, analysis.group2.trials2counts.nR_S2, analysis.groupDiff.mcmc_params);

    % Compute HDI of difference for log(meta-d'/d')
    analysis.group1.logMratio.groupMean = analysis.group1.fit.mu_logMratio;
    analysis.group1.logMratio.singleSubject = log(analysis.group1.fit.Mratio);
    analysis.group1.Mratio.groupMean = exp(analysis.group1.fit.mu_logMratio);
    analysis.group1.Mratio.singleSubject = analysis.group1.fit.Mratio;
    analysis.group2.logMratio.groupMean = analysis.group2.fit.mu_logMratio;
    analysis.group2.logMratio.singleSubject = log(analysis.group2.fit.Mratio);
    analysis.group2.Mratio.groupMean = exp(analysis.group2.fit.mu_logMratio);
    analysis.group2.Mratio.singleSubject = analysis.group2.fit.Mratio;
    analysis.groupDiff.log_Mratio.sampleDiff = analysis.group1.fit.mcmc.samples.mu_logMratio - analysis.group2.fit.mcmc.samples.mu_logMratio;
    analysis.groupDiff.log_Mratio.mean = analysis.group1.fit.mu_logMratio - analysis.group2.fit.mu_logMratio;
    analysis.groupDiff.log_Mratio.hdi = calc_HDI(analysis.groupDiff.log_Mratio.sampleDiff(:));
    analysis.groupDiff.Mratio.mean = exp(analysis.group1.fit.mu_logMratio) - exp(analysis.group2.fit.mu_logMratio);
    analysis.groupDiff.Mratio.hdi = calc_HDI((exp(analysis.group1.fit.mcmc.samples.mu_logMratio(:)) - exp(analysis.group2.fit.mcmc.samples.mu_logMratio(:))));

    % Pull out group mean values for all non-hierarchical metrics
    analysis.group1.d1.groupMean = mean(analysis.group1.fit.d1);
    analysis.group1.d1.singleSubject = analysis.group1.fit.d1;
    analysis.group1.c1.groupMean = mean(analysis.group1.fit.c1);
    analysis.group1.c1.singleSubject = analysis.group1.fit.c1;
    analysis.group2.d1.groupMean = mean(analysis.group2.fit.d1);
    analysis.group2.d1.singleSubject = analysis.group2.fit.d1;
    analysis.group2.c1.groupMean = mean(analysis.group2.fit.c1);
    analysis.group2.c1.singleSubject = analysis.group2.fit.c1;

    % Calculate average confidence, add filter number and accuracy to results for group 1
    for n = 1:length(analysis.group1.data)
        analysis.group1.avgConfidence.singleSubject(n) = mean(analysis.group1.data(n).results.thresholdTrials.confidence);
        analysis.group1.filterNum.singleSubject(n) = mean(analysis.group1.data(n).results.thresholdTrials.filterNum);
        analysis.group1.accuracy.singleSubject(n) = analysis.group1.data(n).results.thresholdTrials.accuracyTotal;
    end
    analysis.group1.avgConfidence.groupMean = mean(analysis.group1.avgConfidence.singleSubject);
    analysis.group1.filterNum.groupMean = mean(analysis.group1.filterNum.singleSubject);
    analysis.group1.accuracy.groupMean = mean(analysis.group1.accuracy.singleSubject);

    % Calculate average confidence, add filter number and accuracy to results for group 2
    for n = 1:length(analysis.group2.data)
        analysis.group2.avgConfidence.singleSubject(n) = mean(analysis.group2.data(n).results.thresholdTrials.confidence);
        analysis.group2.filterNum.singleSubject(n) = mean(analysis.group2.data(n).results.thresholdTrials.filterNum);
        analysis.group2.accuracy.singleSubject(n) = analysis.group2.data(n).results.thresholdTrials.accuracyTotal;
    end
    analysis.group2.avgConfidence.groupMean = mean(analysis.group2.avgConfidence.singleSubject);
    analysis.group2.filterNum.groupMean = mean(analysis.group2.filterNum.singleSubject);
    analysis.group2.accuracy.groupMean = mean(analysis.group2.accuracy.singleSubject);

    % Calculate group difference values for all non-hierarchical metrics
    analysis.groupDiff.filterNum.meanDiff = analysis.group1.filterNum.groupMean - analysis.group2.filterNum.groupMean;
    analysis.groupDiff.accuracy.meanDiff = analysis.group1.accuracy.groupMean - analysis.group2.accuracy.groupMean;
    analysis.groupDiff.d1.meanDiff = analysis.group1.d1.groupMean - analysis.group2.d1.groupMean;
    analysis.groupDiff.c1.meanDiff = analysis.group1.c1.groupMean - analysis.group2.c1.groupMean;
    analysis.groupDiff.avgConfidence.meanDiff = analysis.group1.avgConfidence.groupMean - analysis.group2.avgConfidence.groupMean;

    % Run specified statistical test on all non-hierarchical metrics
    if strcmp(analysis.groupDiff.test,'wilcoxon') == 1
        analysis.groupDiff.testType = 'Wilcoxon rank sum test';
        [analysis.groupDiff.filterNum.p,analysis.groupDiff.filterNum.h,analysis.groupDiff.filterNum.stats] = ranksum(analysis.group1.filterNum.singleSubject,analysis.group2.filterNum.singleSubject);
        [analysis.groupDiff.accuracy.p,analysis.groupDiff.accuracy.h,analysis.groupDiff.accuracy.stats] = ranksum(analysis.group1.accuracy.singleSubject,analysis.group2.accuracy.singleSubject);
        [analysis.groupDiff.d1.p,analysis.groupDiff.d1.h,analysis.groupDiff.d1.stats] = ranksum(analysis.group1.d1.singleSubject,analysis.group2.d1.singleSubject);
        [analysis.groupDiff.c1.p,analysis.groupDiff.c1.h,analysis.groupDiff.c1.stats] = ranksum(analysis.group1.c1.singleSubject,analysis.group2.c1.singleSubject);
        [analysis.groupDiff.avgConfidence.p,analysis.groupDiff.avgConfidence.h,analysis.groupDiff.avgConfidence.stats] = ranksum(analysis.group1.avgConfidence.singleSubject,analysis.group2.avgConfidence.singleSubject);
    elseif strcmp(analysis.groupDiff.test,'ttest') == 1
        analysis.groupDiff.testType = 'Unpaired T-test';
        [analysis.groupDiff.filterNum.h,analysis.groupDiff.filterNum.p,analysis.groupDiff.filterNum.ci,analysis.groupDiff.filterNum.stats] = ttest2(analysis.group1.filterNum.singleSubject,analysis.group2.filterNum.singleSubject);
        [analysis.groupDiff.accuracy.h,analysis.groupDiff.accuracy.p,analysis.groupDiff.accuracy.ci,analysis.groupDiff.accuracy.stats] = ttest2(analysis.group1.accuracy.singleSubject,analysis.group2.accuracy.singleSubject);
        [analysis.groupDiff.d1.h,analysis.groupDiff.d1.p,analysis.groupDiff.d1.ci,analysis.groupDiff.d1.stats] = ttest2(analysis.group1.d1.singleSubject,analysis.group2.d1.singleSubject);
        [analysis.groupDiff.c1.h,analysis.groupDiff.c1.p,analysis.groupDiff.c1.ci,analysis.groupDiff.c1.stats] = ttest2(analysis.group1.c1.singleSubject,analysis.group2.c1.singleSubject);
        [analysis.groupDiff.avgConfidence.h,analysis.groupDiff.avgConfidence.p,analysis.groupDiff.avgConfidence.ci,analysis.groupDiff.avgConfidence.stats] = ttest2(analysis.group1.avgConfidence.singleSubject,analysis.group2.avgConfidence.singleSubject);
    end

    % Save results
    save(resultsFile, 'analysis'); % Save results

    % Create Figure to display results
    figure
    set(gcf, 'Units', 'Normalized', 'OuterPosition', [0 0 0.6 0.6]);
    labels = ['Group 1'; 'Group 2'];
    % Plot Type 1 metrics on top line
    ax1 = subplot(2,4,1);
    bar(ax1, [analysis.group1.filterNum.groupMean; analysis.group2.filterNum.groupMean]);
    hold on
    errorbar(ax1, [analysis.group1.filterNum.groupMean; analysis.group2.filterNum.groupMean], [std(analysis.group1.filterNum.singleSubject), std(analysis.group2.filterNum.singleSubject)], 'o', 'marker', 'none', 'linewidth', 1, 'Color','k');
    set(gca,'XTickLabel',labels);
    set(gca,'XTickLabelRotation',90)
    if analysis.groupDiff.filterNum.h == 0
        title('FILTER NUMBER: NON-SIG DIFF')
    elseif analysis.groupDiff.filterNum.h > 0
        title('FILTER NUMBER: SIG DIFF')
    end
    hold off
    ax2 = subplot(2,4,2);
    bar(ax2, [analysis.group1.accuracy.groupMean; analysis.group2.accuracy.groupMean]);
    hold on
    errorbar(ax2, [analysis.group1.accuracy.groupMean; analysis.group2.accuracy.groupMean], [std(analysis.group1.accuracy.singleSubject), std(analysis.group2.accuracy.singleSubject)], 'o', 'marker', 'none', 'linewidth', 1, 'Color','k');
    set(gca,'XTickLabel',labels);
    set(gca,'XTickLabelRotation',90)
    if analysis.groupDiff.accuracy.h == 0
        title('ACCURACY: NON-SIG DIFF')
    elseif analysis.groupDiff.accuracy.h > 0
        title('ACCURACY: SIG DIFF')
    end
    ax3 = subplot(2,4,3);
    bar(ax3, [analysis.group1.d1.groupMean; analysis.group2.d1.groupMean]);
    hold on
    errorbar(ax3, [analysis.group1.d1.groupMean; analysis.group2.d1.groupMean], [std(analysis.group1.d1.singleSubject), std(analysis.group2.d1.singleSubject)], 'o', 'marker', 'none', 'linewidth', 1, 'Color','k');
    set(gca,'XTickLabel',labels);
    set(gca,'XTickLabelRotation',90)
    if analysis.groupDiff.d1.h == 0
        title('d'': NON-SIG DIFF')
    elseif analysis.groupDiff.d1.h > 0
        title('d'': SIG DIFF')
    end
    hold off
    ax4 = subplot(2,4,4);
    bar(ax4, [analysis.group1.c1.groupMean; analysis.group2.c1.groupMean]);
    hold on
    errorbar(ax4, [analysis.group1.c1.groupMean; analysis.group2.c1.groupMean], [std(analysis.group1.c1.singleSubject), std(analysis.group2.c1.singleSubject)], 'o', 'marker', 'none', 'linewidth', 1, 'Color','k');
    set(gca,'XTickLabel',labels);
    set(gca,'XTickLabelRotation',90)
    if analysis.groupDiff.c1.h == 0
        title('CRITERION: NON-SIG DIFF')
    elseif analysis.groupDiff.c1.h > 0
        title('CRITERION: SIG DIFF')
    end
    hold off
    % Plot group Mratio and the difference
    ax5 = subplot(2,4,5);
    histogram(ax5, exp(analysis.group1.fit.mcmc.samples.mu_logMratio))
    xlim([0 2])
    xlabel('MRatio');
    ylabel('Sample count');
    hold on
    histogram(ax5, exp(analysis.group2.fit.mcmc.samples.mu_logMratio))
    hold off
    lgd = legend('Group 1', 'Group 2', 'Location', 'northeast');
    lgd.FontSize = 10;
    if (analysis.groupDiff.Mratio.hdi(1) > 0 && analysis.groupDiff.Mratio.hdi(2) > 0) || (analysis.groupDiff.Mratio.hdi(1) < 0 && analysis.groupDiff.Mratio.hdi(2) < 0)
        title('MRATIO: SIG DIFF')
    else
        title('MRATIO: NON-SIG DIFF')
    end
    ax6 = subplot(2,4,6);
    histogram(ax6, analysis.group1.fit.mcmc.samples.mu_logMratio)
    xlabel('log(MRatio)');
    ylabel('Sample count');
    hold on
    histogram(ax6, analysis.group2.fit.mcmc.samples.mu_logMratio)
    hold off
    lgd = legend('Group 1', 'Group 2', 'Location', 'northwest');
    lgd.FontSize = 10;
    if (analysis.groupDiff.log_Mratio.hdi(1) > 0 && analysis.groupDiff.log_Mratio.hdi(2) > 0) || (analysis.groupDiff.log_Mratio.hdi(1) < 0 && analysis.groupDiff.log_Mratio.hdi(2) < 0)
        title('LOG(MRATIO): SIG DIFF')
    else
        title('LOG(MRATIO): NON-SIG DIFF')
    end
    ax7 = subplot(2,4,7);
    histogram(ax7, analysis.groupDiff.log_Mratio.sampleDiff)
    xlabel('log(MRatio)');
    ylabel('Sample count');
    if (analysis.groupDiff.log_Mratio.hdi(1) > 0 && analysis.groupDiff.log_Mratio.hdi(2) > 0) || (analysis.groupDiff.log_Mratio.hdi(1) < 0 && analysis.groupDiff.log_Mratio.hdi(2) < 0)
        title('LOG(MRATIO) DIFF: SIG DIFF')
    else
        title('LOG(MRATIO) DIFF: NON-SIG DIFF')
    end
    hold on
    ln2 = line([analysis.groupDiff.log_Mratio.hdi(1) analysis.groupDiff.log_Mratio.hdi(1)], [0 1800]);
    ln2.Color = 'r';
    ln2.LineWidth = 1.5;
    ln2.LineStyle = '--';
    ln2 = line([analysis.groupDiff.log_Mratio.hdi(2) analysis.groupDiff.log_Mratio.hdi(2)], [0 1800]);
    ln2.Color = 'r';
    ln2.LineWidth = 1.5;
    ln2.LineStyle = '--';
    hold off
    % Plot average confidence
    ax8 = subplot(2,4,8);
    bar(ax8, [analysis.group1.avgConfidence.groupMean; analysis.group2.avgConfidence.groupMean]);
    hold on
    errorbar(ax8, [analysis.group1.avgConfidence.groupMean; analysis.group2.avgConfidence.groupMean], [std(analysis.group1.avgConfidence.singleSubject), std(analysis.group2.avgConfidence.singleSubject)], 'o', 'marker', 'none', 'linewidth', 1, 'Color','k');
    set(gca,'XTickLabel',labels);
    set(gca,'XTickLabelRotation',90)
    if analysis.groupDiff.avgConfidence.h == 0
        title('CONFIDENCE: NON-SIG DIFF')
    elseif analysis.groupDiff.avgConfidence.h > 0
        title('CONFIDENCE: SIG DIFF')
    end
    hold off

    % Print figure
    figureFile = fullfile('analysis', ['filter_task_analysis_', analysis.type]); % Create figure file name
    print(figureFile, '-dtiff');

    % Display completion message on screen
    fprintf('\n________________________________________\n\n  COMPLETED GROUP DIFFERENCE ANALYSIS\n _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _\n');
    fprintf('\n DIFF MRATIO = %.2f (HDI: %.2f to %.2f)\n', analysis.groupDiff.Mratio.mean, analysis.groupDiff.Mratio.hdi(1), analysis.groupDiff.Mratio.hdi(2));
    if (analysis.groupDiff.Mratio.hdi(1) > 0 && analysis.groupDiff.Mratio.hdi(2) > 0) || (analysis.groupDiff.Mratio.hdi(1) < 0 && analysis.groupDiff.Mratio.hdi(2) < 0)
        fprintf('HDI DOES NOT SPAN ZERO: MRATIO DIFF SIG.\n _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _\n');
    else
        fprintf('  HDI SPANS ZERO: MRATIO DIFF NOT SIG.\n _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _\n');
    end
    fprintf('\n        RESULTS CAN BE FOUND IN: \n             FDT/analysis/\n________________________________________\n');

end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% RUN SESSION DIFFERENCE ANALYSIS (PAIRED) IF SPECIFIED
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

if strcmp(analysis.type,'paired') == 1 % If session difference analysis specified

    % For two-session difference analysis: Print message about groups being fit together --> t-tests possible on meta-d' metrics
    fprintf('\nNOTE: Groups are fit using one hierarchical model.\nFrequentist statistics (such as t-tests) are possible on any metrics involving meta-d''.\nFrequentist statistics are applied to all type 1 metrics and average confidence values.\n');

    % Specify parameters
    analysis.sessionDiff.mcmc_params = fit_meta_d_params;

    % Fit each group using a separate hierarchical Bayesian model
    analysis.sessionDiff.fit = fit_meta_d_mcmc_groupCorr(analysis.trials2counts.nR_S1, analysis.trials2counts.nR_S2, analysis.sessionDiff.mcmc_params);

    % Compute HDI of difference for log(meta-d'/d')
    analysis.session1.logMratio.sessionMean = analysis.sessionDiff.fit.mu_logMratio(1);
    analysis.session1.logMratio.singleSubject = log(analysis.sessionDiff.fit.Mratio(:,1));
    analysis.session1.Mratio.sessionMean = exp(analysis.sessionDiff.fit.mu_logMratio(1));
    analysis.session1.Mratio.singleSubject = analysis.sessionDiff.fit.Mratio(:,1);
    analysis.session2.logMratio.sessionMean = analysis.sessionDiff.fit.mu_logMratio(2);
    analysis.session2.logMratio.singleSubject = log(analysis.sessionDiff.fit.Mratio(:,2));
    analysis.session2.Mratio.sessionMean = exp(analysis.sessionDiff.fit.mu_logMratio(2));
    analysis.session2.Mratio.singleSubject = analysis.sessionDiff.fit.Mratio(:,2);
    analysis.sessionDiff.log_Mratio.sampleDiff = analysis.sessionDiff.fit.mcmc.samples.mu_logMratio(:,:,1) - analysis.sessionDiff.fit.mcmc.samples.mu_logMratio(:,:,2);
    analysis.sessionDiff.log_Mratio.mean = analysis.session1.logMratio.sessionMean - analysis.session2.logMratio.sessionMean;
    analysis.sessionDiff.log_Mratio.hdi = calc_HDI(analysis.sessionDiff.log_Mratio.sampleDiff(:));
    analysis.sessionDiff.Mratio.mean = exp(analysis.session1.logMratio.sessionMean) - exp(analysis.session2.logMratio.sessionMean);
    analysis.sessionDiff.Mratio.hdi = calc_HDI((exp(analysis.sessionDiff.fit.mcmc.samples.mu_logMratio(:,:,1)) - exp(analysis.sessionDiff.fit.mcmc.samples.mu_logMratio(:,:,2))));

    % Pull out session mean values for all non-hierarchical metrics
    analysis.session1.d1.sessionMean = mean(analysis.sessionDiff.fit.d1(:,1));
    analysis.session1.d1.singleSubject = analysis.sessionDiff.fit.d1(:,1);
    analysis.session1.c1.sessionMean = mean(analysis.sessionDiff.fit.c1(:,1));
    analysis.session1.c1.singleSubject = analysis.sessionDiff.fit.c1(:,1);
    analysis.session2.d1.sessionMean = mean(analysis.sessionDiff.fit.d1(:,2));
    analysis.session2.d1.singleSubject = analysis.sessionDiff.fit.d1(:,2);
    analysis.session2.c1.sessionMean = mean(analysis.sessionDiff.fit.c1(:,2));
    analysis.session2.c1.singleSubject = analysis.sessionDiff.fit.c1(:,2);

    % Calculate average confidence, add filter number and accuracy to results for session 1
    for n = 1:length(analysis.session1.data)
        analysis.session1.avgConfidence.singleSubject(n) = mean(analysis.session1.data(n).results.thresholdTrials.confidence);
        analysis.session1.filterNum.singleSubject(n) = mean(analysis.session1.data(n).results.thresholdTrials.filterNum);
        analysis.session1.accuracy.singleSubject(n) = analysis.session1.data(n).results.thresholdTrials.accuracyTotal;
    end
    analysis.session1.avgConfidence.sessionMean = mean(analysis.session1.avgConfidence.singleSubject);
    analysis.session1.filterNum.sessionMean = mean(analysis.session1.filterNum.singleSubject);
    analysis.session1.accuracy.sessionMean = mean(analysis.session1.accuracy.singleSubject);

    % Calculate average confidence, add filter number and accuracy to results for session 2
    for n = 1:length(analysis.session2.data)
        analysis.session2.avgConfidence.singleSubject(n) = mean(analysis.session2.data(n).results.thresholdTrials.confidence);
        analysis.session2.filterNum.singleSubject(n) = mean(analysis.session2.data(n).results.thresholdTrials.filterNum);
        analysis.session2.accuracy.singleSubject(n) = analysis.session2.data(n).results.thresholdTrials.accuracyTotal;
    end
    analysis.session2.avgConfidence.sessionMean = mean(analysis.session2.avgConfidence.singleSubject);
    analysis.session2.filterNum.sessionMean = mean(analysis.session2.filterNum.singleSubject);
    analysis.session2.accuracy.sessionMean = mean(analysis.session2.accuracy.singleSubject);

    % Calculate session difference values for all non-hierarchical metrics
    analysis.sessionDiff.filterNum.meanDiff = analysis.session1.filterNum.sessionMean - analysis.session2.filterNum.sessionMean;
    analysis.sessionDiff.accuracy.meanDiff = analysis.session1.accuracy.sessionMean - analysis.session2.accuracy.sessionMean;
    analysis.sessionDiff.d1.meanDiff = analysis.session1.d1.sessionMean - analysis.session2.d1.sessionMean;
    analysis.sessionDiff.c1.meanDiff = analysis.session1.c1.sessionMean - analysis.session2.c1.sessionMean;
    analysis.sessionDiff.avgConfidence.meanDiff = analysis.session1.avgConfidence.sessionMean - analysis.session2.avgConfidence.sessionMean;

    % Run specified statistical test on all non-hierarchical metrics
    if strcmp(analysis.sessionDiff.test,'wilcoxon') == 1
        analysis.sessionDiff.testType = 'Wilcoxon signed rank test';
        [analysis.sessionDiff.filterNum.p,analysis.sessionDiff.filterNum.h,analysis.sessionDiff.filterNum.stats] = signrank(analysis.session1.filterNum.singleSubject,analysis.session2.filterNum.singleSubject);
        [analysis.sessionDiff.accuracy.p,analysis.sessionDiff.accuracy.h,analysis.sessionDiff.accuracy.stats] = signrank(analysis.session1.accuracy.singleSubject,analysis.session2.accuracy.singleSubject);
        [analysis.sessionDiff.d1.p,analysis.sessionDiff.d1.h,analysis.sessionDiff.d1.stats] = signrank(analysis.session1.d1.singleSubject,analysis.session2.d1.singleSubject);
        [analysis.sessionDiff.c1.p,analysis.sessionDiff.c1.h,analysis.sessionDiff.c1.stats] = signrank(analysis.session1.c1.singleSubject,analysis.session2.c1.singleSubject);
        [analysis.sessionDiff.avgConfidence.p,analysis.sessionDiff.avgConfidence.h,analysis.sessionDiff.avgConfidence.stats] = signrank(analysis.session1.avgConfidence.singleSubject,analysis.session2.avgConfidence.singleSubject);
    elseif strcmp(analysis.sessionDiff.test,'ttest') == 1
        analysis.sessionDiff.testType = 'Paired T-test';
        [analysis.sessionDiff.filterNum.h,analysis.sessionDiff.filterNum.p,analysis.sessionDiff.filterNum.ci,analysis.sessionDiff.filterNum.stats] = ttest(analysis.session1.filterNum.singleSubject,analysis.session2.filterNum.singleSubject);
        [analysis.sessionDiff.accuracy.h,analysis.sessionDiff.accuracy.p,analysis.sessionDiff.accuracy.ci,analysis.sessionDiff.accuracy.stats] = ttest(analysis.session1.accuracy.singleSubject,analysis.session2.accuracy.singleSubject);
        [analysis.sessionDiff.d1.h,analysis.sessionDiff.d1.p,analysis.sessionDiff.d1.ci,analysis.sessionDiff.d1.stats] = ttest(analysis.session1.d1.singleSubject,analysis.session2.d1.singleSubject);
        [analysis.sessionDiff.c1.h,analysis.sessionDiff.c1.p,analysis.sessionDiff.c1.ci,analysis.sessionDiff.c1.stats] = ttest(analysis.session1.c1.singleSubject,analysis.session2.c1.singleSubject);
        [analysis.sessionDiff.avgConfidence.h,analysis.sessionDiff.avgConfidence.p,analysis.sessionDiff.avgConfidence.ci,analysis.sessionDiff.avgConfidence.stats] = ttest(analysis.session1.avgConfidence.singleSubject,analysis.session2.avgConfidence.singleSubject);
    end

    % Save results
    save(resultsFile, 'analysis'); % Save results

    % Create Figure to display results
    figure
    set(gcf, 'Units', 'Normalized', 'OuterPosition', [0 0 0.6 0.6]);
    labels = ['Session 1'; 'Session 2'];
    % Plot Type 1 metrics on top line
    ax1 = subplot(2,4,1);
    bar(ax1, [analysis.session1.filterNum.sessionMean; analysis.session2.filterNum.sessionMean]);
    hold on
    errorbar(ax1, [analysis.session1.filterNum.sessionMean; analysis.session2.filterNum.sessionMean], [std(analysis.session1.filterNum.singleSubject), std(analysis.session2.filterNum.singleSubject)], 'o', 'marker', 'none', 'linewidth', 1, 'Color','k');
    set(gca,'XTickLabel',labels);
    set(gca,'XTickLabelRotation',90)
    if analysis.sessionDiff.filterNum.h == 0
        title('FILTER NUMBER: NON-SIG DIFF')
    elseif analysis.sessionDiff.filterNum.h > 0
        title('FILTER NUMBER: SIG DIFF')
    end
    hold off
    ax2 = subplot(2,4,2);
    bar(ax2, [analysis.session1.accuracy.sessionMean; analysis.session2.accuracy.sessionMean]);
    hold on
    errorbar(ax2, [analysis.session1.accuracy.sessionMean; analysis.session2.accuracy.sessionMean], [std(analysis.session1.accuracy.singleSubject), std(analysis.session2.accuracy.singleSubject)], 'o', 'marker', 'none', 'linewidth', 1, 'Color','k');
    set(gca,'XTickLabel',labels);
    set(gca,'XTickLabelRotation',90)
    if analysis.sessionDiff.accuracy.h == 0
        title('ACCURACY: NON-SIG DIFF')
    elseif analysis.sessionDiff.accuracy.h > 0
        title('ACCURACY: SIG DIFF')
    end
    ax3 = subplot(2,4,3);
    bar(ax3, [analysis.session1.d1.sessionMean; analysis.session2.d1.sessionMean]);
    hold on
    errorbar(ax3, [analysis.session1.d1.sessionMean; analysis.session2.d1.sessionMean], [std(analysis.session1.d1.singleSubject), std(analysis.session2.d1.singleSubject)], 'o', 'marker', 'none', 'linewidth', 1, 'Color','k');
    set(gca,'XTickLabel',labels);
    set(gca,'XTickLabelRotation',90)
    if analysis.sessionDiff.d1.h == 0
        title('d'': NON-SIG DIFF')
    elseif analysis.sessionDiff.d1.h > 0
        title('d'': SIG DIFF')
    end
    hold off
    ax4 = subplot(2,4,4);
    bar(ax4, [analysis.session1.c1.sessionMean; analysis.session2.c1.sessionMean]);
    hold on
    errorbar(ax4, [analysis.session1.c1.sessionMean; analysis.session2.c1.sessionMean], [std(analysis.session1.c1.singleSubject), std(analysis.session2.c1.singleSubject)], 'o', 'marker', 'none', 'linewidth', 1, 'Color','k');
    set(gca,'XTickLabel',labels);
    set(gca,'XTickLabelRotation',90)
    if analysis.sessionDiff.c1.h == 0
        title('CRITERION: NON-SIG DIFF')
    elseif analysis.sessionDiff.c1.h > 0
        title('CRITERION: SIG DIFF')
    end
    hold off
    % Plot session Mratio and the difference
    ax5 = subplot(2,4,5);
    histogram(ax5, exp(analysis.sessionDiff.fit.mcmc.samples.mu_logMratio(:,:,1)))
    xlim([0 2])
    xlabel('MRatio');
    ylabel('Sample count');
    hold on
    histogram(ax5, exp(analysis.sessionDiff.fit.mcmc.samples.mu_logMratio(:,:,2)))
    hold off
    lgd = legend('Session 1', 'Session 2', 'Location', 'northeast');
    lgd.FontSize = 10;
    if (analysis.sessionDiff.Mratio.hdi(1) > 0 && analysis.sessionDiff.Mratio.hdi(2) > 0) || (analysis.sessionDiff.Mratio.hdi(1) < 0 && analysis.sessionDiff.Mratio.hdi(2) < 0)
        title('MRATIO: SIG DIFF')
    else
        title('MRATIO: NON-SIG DIFF')
    end
    ax6 = subplot(2,4,6);
    histogram(ax6, analysis.sessionDiff.fit.mcmc.samples.mu_logMratio(:,:,1))
    xlabel('log(MRatio)');
    ylabel('Sample count');
    hold on
    histogram(ax6, analysis.sessionDiff.fit.mcmc.samples.mu_logMratio(:,:,2))
    hold off
    lgd = legend('Session 1', 'Session 2', 'Location', 'northwest');
    lgd.FontSize = 10;
    if (analysis.sessionDiff.log_Mratio.hdi(1) > 0 && analysis.sessionDiff.log_Mratio.hdi(2) > 0) || (analysis.sessionDiff.log_Mratio.hdi(1) < 0 && analysis.sessionDiff.log_Mratio.hdi(2) < 0)
        title('LOG(MRATIO): SIG DIFF')
    else
        title('LOG(MRATIO): NON-SIG DIFF')
    end
    ax7 = subplot(2,4,7);
    histogram(ax7, analysis.sessionDiff.log_Mratio.sampleDiff)
    xlabel('log(MRatio)');
    ylabel('Sample count');
    if (analysis.sessionDiff.log_Mratio.hdi(1) > 0 && analysis.sessionDiff.log_Mratio.hdi(2) > 0) || (analysis.sessionDiff.log_Mratio.hdi(1) < 0 && analysis.sessionDiff.log_Mratio.hdi(2) < 0)
        title('LOG(MRATIO) DIFF: SIG DIFF')
    else
        title('LOG(MRATIO) DIFF: NON-SIG DIFF')
    end
    hold on
    ln2 = line([analysis.sessionDiff.log_Mratio.hdi(1) analysis.sessionDiff.log_Mratio.hdi(1)], [0 1800]);
    ln2.Color = 'r';
    ln2.LineWidth = 1.5;
    ln2.LineStyle = '--';
    ln2 = line([analysis.sessionDiff.log_Mratio.hdi(2) analysis.sessionDiff.log_Mratio.hdi(2)], [0 1800]);
    ln2.Color = 'r';
    ln2.LineWidth = 1.5;
    ln2.LineStyle = '--';
    hold off
    % Plot average confidence
    ax8 = subplot(2,4,8);
    bar(ax8, [analysis.session1.avgConfidence.sessionMean; analysis.session2.avgConfidence.sessionMean]);
    hold on
    errorbar(ax8, [analysis.session1.avgConfidence.sessionMean; analysis.session2.avgConfidence.sessionMean], [std(analysis.session1.avgConfidence.singleSubject), std(analysis.session2.avgConfidence.singleSubject)], 'o', 'marker', 'none', 'linewidth', 1, 'Color','k');
    set(gca,'XTickLabel',labels);
    set(gca,'XTickLabelRotation',90)
    if analysis.sessionDiff.avgConfidence.h == 0
        title('CONFIDENCE: NON-SIG DIFF')
    elseif analysis.sessionDiff.avgConfidence.h > 0
        title('CONFIDENCE: SIG DIFF')
    end
    hold off

    % Print figure
    figureFile = fullfile('analysis', ['filter_task_analysis_', analysis.type]); % Create figure file name
    print(figureFile, '-dtiff');

    % Display completion message on screen
    fprintf('\n________________________________________\n\n  COMPLETED SESSION DIFFERENCE ANALYSIS\n _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _\n');
    fprintf('\n DIFF MRATIO = %.2f (HDI: %.2f to %.2f)\n', analysis.sessionDiff.Mratio.mean, analysis.sessionDiff.Mratio.hdi(1), analysis.sessionDiff.Mratio.hdi(2));
    if (analysis.sessionDiff.Mratio.hdi(1) > 0 && analysis.sessionDiff.Mratio.hdi(2) > 0) || (analysis.sessionDiff.Mratio.hdi(1) < 0 && analysis.sessionDiff.Mratio.hdi(2) < 0)
        fprintf('HDI DOES NOT SPAN ZERO: MRATIO DIFF SIG.\n _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _\n');
    else
        fprintf('  HDI SPANS ZERO: MRATIO DIFF NOT SIG.\n _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _\n');
    end
    fprintf('\n        RESULTS CAN BE FOUND IN: \n             FDT/analysis/\n________________________________________\n');

end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% RUN GROUP REGRESSION ANALYSIS IF SPECIFIED
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

if strcmp(analysis.type,'regress') == 1 % If group regression analysis specified

    % Specify parameters
    analysis.groupRegression.mcmc_params = fit_meta_d_params;

    % Standardise covariate
    analysis.groupRegression.cov = zscore(analysis.covariate.data);

    % Fit group data all at once
    analysis.groupRegression.fit = fit_meta_d_mcmc_regression(analysis.trials2counts.nR_S1, analysis.trials2counts.nR_S2, analysis.groupRegression.cov, analysis.groupRegression.mcmc_params);

    % Calculate log of Mratio for single subjects values
    for n = 1:length(analysis.groupRegression.fit.Mratio)
        analysis.groupRegression.log_Mratio.singleSubject(n) = log(analysis.groupRegression.fit.Mratio(n));
    end

    % Pull out group and single subject values
    analysis.groupRegression.d1.groupMean = mean(analysis.groupRegression.fit.d1);
    analysis.groupRegression.d1.singleSubject = analysis.groupRegression.fit.d1;
    analysis.groupRegression.c1.groupMean = mean(analysis.groupRegression.fit.c1);
    analysis.groupRegression.c1.singleSubject = analysis.groupRegression.fit.c1;
    analysis.groupRegression.meta_d.groupMean = mean(analysis.groupRegression.fit.meta_d);
    analysis.groupRegression.meta_d.singleSubject = analysis.groupRegression.fit.meta_d;
    analysis.groupRegression.Mratio.groupMean = exp(analysis.groupRegression.fit.mu_logMratio);
    analysis.groupRegression.Mratio.singleSubject = analysis.groupRegression.fit.Mratio;
    analysis.groupRegression.Mratio.hdi = calc_HDI(exp(analysis.groupRegression.fit.mcmc.samples.mu_logMratio(:)));
    analysis.groupRegression.log_Mratio.groupMean = analysis.groupRegression.fit.mu_logMratio;
    analysis.groupRegression.log_Mratio.hdi = calc_HDI(analysis.groupRegression.fit.mcmc.samples.mu_logMratio(:));
    analysis.groupRegression.beta1.groupMean = analysis.groupRegression.fit.mu_beta1;
    analysis.groupRegression.beta1.hdi = calc_HDI(analysis.groupRegression.fit.mcmc.samples.mu_beta1(:));

    % Calculate average confidence, add filter number and accuracy to analysis results
    for n = 1:length(analysis.data)
        analysis.groupRegression.avgConfidence.singleSubject(n) = mean(analysis.data(n).results.thresholdTrials.confidence);
        analysis.groupRegression.filterNum.singleSubject(n) = mean(analysis.data(n).results.thresholdTrials.filterNum);
        analysis.groupRegression.accuracy.singleSubject(n) = analysis.data(n).results.thresholdTrials.accuracyTotal;
    end
    analysis.groupRegression.avgConfidence.groupMean = mean(analysis.groupRegression.avgConfidence.singleSubject);
    analysis.groupRegression.filterNum.groupMean = mean(analysis.groupRegression.filterNum.singleSubject);
    analysis.groupRegression.accuracy.groupMean = mean(analysis.groupRegression.accuracy.singleSubject);

    % Save results
    save(resultsFile, 'analysis'); % Save results

    % Display completion message on screen plus results
    fprintf('\n________________________________________\n\n  COMPLETED GROUP REGRESSION ANALYSIS\n _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _\n');
    fprintf('\n GROUP MRATIO = %.2f (HDI: %.2f to %.2f)\n', analysis.groupRegression.Mratio.groupMean, analysis.groupRegression.Mratio.hdi(1), analysis.groupRegression.Mratio.hdi(2));
    if analysis.groupRegression.Mratio.hdi(1) > 0
        fprintf('   HDI DOES NOT SPAN ZERO: MRATIO SIG.\n');
    else
        fprintf('   HDI SPANS ZERO: MRATIO NOT SIG.\n');
    end
    fprintf('\n GROUP BETA = %.2f (HDI: %.2f to %.2f)\n', analysis.groupRegression.beta1.groupMean, analysis.groupRegression.beta1.hdi(1), analysis.groupRegression.beta1.hdi(2));
    if analysis.groupRegression.beta1.hdi(1) > 0
        fprintf('    HDI DOES NOT SPAN ZERO: BETA SIG.\n _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _\n');
    else
        fprintf('      HDI SPANS ZERO: BETA NOT SIG.\n _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _\n');
    end
    fprintf('\n        RESULTS CAN BE FOUND IN: \n             FDT/analysis/\n________________________________________\n');

end

end

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


function [model,output] = normalizeExponentialCone(model)

% N.B: YALMIP Definition % x2*exp(x1/x2)  <= x3

% Temporarily extract the data in a more standard name min c'*y, b + Ay >= 0
output.problem = 0;
if ~isempty(model.evalMap)

    % Temporarily extract the data in a more standard name min c'*y, b + Ay >= 0
    data.A = model.F_struc(:,2:end);
    data.b = full(model.F_struc(:,1));
    data.c = full(model.c);
    cones = model.K;
    % Clear away the SDP cone to easier add new rows. Will be put back in
    % place later
    data.A(end-sum(model.K.s.^2)+1:end,:)=[];
    data.b(end-sum(model.K.s.^2)+1:end) = [];
    sdpData = model.F_struc(end-sum(model.K.s.^2)+1:end,:);

    % First check that we have only exponentials
    convexFunctions = [];
    concaveFunctions = [];
    vectorentropy = [];
    vectorkullback = [];
    vectorlogsumexp = [];
    for i = 1:length(model.evalMap)
        switch model.evalMap{i}.fcn
            case {'exp','pexp'}
                convexFunctions = [convexFunctions model.evalMap{i}.computes];
            case {'log','plog','slog'}
                concaveFunctions = [concaveFunctions model.evalMap{i}.computes];
            case 'entropy'
                concaveFunctions = [concaveFunctions model.evalMap{i}.computes];
                if length(model.evalMap{i}.variableIndex) > 1
                    vectorentropy = [vectorentropy i];
                end
            case 'kullbackleibler'
                convexFunctions = [convexFunctions model.evalMap{i}.computes];
                if length(model.evalMap{i}.variableIndex) > 2
                    vectorkullback = [vectorkullback i];
                end
            case 'logsumexp'
                convexFunctions = [convexFunctions model.evalMap{i}.computes];
                vectorlogsumexp = [vectorlogsumexp i];
            otherwise
                % Standard interface, return solver not applicable
                % This should not be able to happen as we check this
                % earlier
                output = createoutput([],[],[],-4,model.solver.tag,[],[],0);
                return
        end
    end
    % Check that all exp/log enter in a convex fashion
    if model.K.f > 0
        if nnz(data.A(1:model.K.f,convexFunctions))>0 || nnz(data.A(1:model.K.f,concaveFunctions))>0
            output = createoutput([],[],[],19,model.solver.tag,[],[],0);
            return
        end
    end
    % Check sign in objective on exp/log terms
    if any(data.c(convexFunctions) < 0) || any(data.c(concaveFunctions) > 0)
        output = createoutput([],[],[],19,model.solver.tag,[],[],0);
        return
    end
    % Check sign in elementwise inequalities on exp/log terms
    if model.K.l > 0
        if nnz(data.A(1+model.K.f:model.K.f+model.K.l,convexFunctions)>0) || nnz(data.A(1+model.K.f:model.K.f+model.K.l,concaveFunctions)<0)
            output = createoutput([],[],[],19,model.solver.tag,[],[],0);
            return
        end
    end
    % Check so there are no exp/log terms in quadratic or SDP cone
    if sum(model.K.q) + sum(model.K.s) > 0
        if nnz(data.A(1+model.K.f+model.K.l:end,convexFunctions))>0 || nnz(data.A(1+model.K.f+model.K.l:end,concaveFunctions))>0
            output = createoutput([],[],[],19,model.solver.tag,[],[],0);
            return
        end
    end
    % Scalarize entropy operator
    if ~isempty(vectorentropy)
        for i = vectorentropy(:)'
            involved = model.evalMap{i}.variableIndex;
            computes = model.evalMap{i}.computes;
            n = length(data.c);
            m = length(involved);
            data.A = [sparse(ones(1,m+1),[computes n+(1:m)],[-1 ones(1,m)],1,n+m);
                      data.A spalloc(size(data.A,1),m,0)];
            data.b = [0;data.b];
            data.c = [data.c;zeros(m,1)];
            cones.f = cones.f + 1;
            % The original strucuture now just relates to the first new term
            model.evalMap{i}.computes = n+1;
            model.evalMap{i}.variableIndex = involved(1);
            % and then we add some
            for j = 2:length(involved)
                model.evalMap{end+1} = model.evalMap{i};
                model.evalMap{end}.variableIndex = involved(j);
                model.evalMap{end}.computes = n+j;
            end
        end
    end

    % Scalarize kullbackleibler operator
    if ~isempty(vectorkullback)
        for i = vectorkullback(:)'
            involved = model.evalMap{i}.variableIndex;
            computes = model.evalMap{i}.computes;
            n = length(data.c);
            m = length(involved)/2;
            data.A = [sparse(ones(1,m+1),[computes n+(1:m)],[-1 ones(1,m)],1,n+m);
                      data.A spalloc(size(data.A,1),m,0)];
            data.b = [0;data.b];
            data.c = [data.c;zeros(m,1)];
            cones.f = cones.f + 1;
            % The original strucuture now just relates to the first new term
            model.evalMap{i}.computes = n+1;
            model.evalMap{i}.variableIndex = involved([1 m+1]);
            % and then we add some
            for j = 2:length(involved)/2
                model.evalMap{end+1} = model.evalMap{i};
                model.evalMap{end}.variableIndex = involved([j j+m]);
                model.evalMap{end}.computes = n+j;
            end
        end
    end

    % Scalarize logsumexp operator
    % write log(expx1+expx2..)<= z as exp(x1-z)+exp(x2-z)+...<=1
    if ~isempty(vectorlogsumexp)
        for i = vectorlogsumexp(:)'
            involved = model.evalMap{i}.variableIndex;
            computes = model.evalMap{i}.computes;
            % Orignal #variables
            n = length(data.c);
            % Number of terms in logsumexp
            m = length(involved);

            % exp(x1-z)+exp(x2-z)+...<=1
            data.A = [data.A(1:cones.f,:) spalloc(cones.f,m,0);
                      spalloc(1,n,0) -ones(1,m);
                      data.A(cones.f+1:end,:) spalloc(cones.l+sum(cones.q)+sum(cones.s),m,0)];
            data.b = [data.b(1:cones.f);
                      1;
                      data.b(cones.f+1:end)];
            data.c = [data.c;zeros(m,1)];
            cones.l = cones.l + 1;

            % The original strucuture now just relates to the first new term
            model.evalMap{i}.computes = n+1;
            model.evalMap{i}.variableIndex = [involved(1) computes];
            model.evalMap{i}.fcn = 'expdiff';
            % and then we add some new
            for j = 2:length(involved)
                model.evalMap{end+1} = model.evalMap{i};
                model.evalMap{end}.variableIndex = [involved(j) computes];
                model.evalMap{end}.computes = n+j;
            end
        end
    end

    % Describe all exponential cones
    m = length(model.evalMap);
    cones.e = model.K.e + m;
    for i = 1:m
        switch model.evalMap{i}.fcn
            case 'exp'
                % 1*exp(xv/1) <= xc
                % y*exp(x/y)  <= z.  y new variable, xv the original variable, and xc the "computed"
                x = model.evalMap{i}.variableIndex;
                z = model.evalMap{i}.computes;
                data.A = [data.A;
                          -sparse([1;3],[x z],-1,3,size(data.A,2))];
                data.b = [data.b;[0;1;0]];
            case 'pexp'
                % xv(1)*exp(xv(2)/xv(1)) <= xc
                x = model.evalMap{i}.variableIndex(2);
                y = model.evalMap{i}.variableIndex(1);
                z = model.evalMap{i}.computes;
                data.A = [data.A;
                          -sparse([1;2;3],[x y z],[-1 -1 -1],3,size(data.A,2))];
                data.b = [data.b;[0;0;0]];
            case 'log'
                % log(xv) >= xc i.e. xv >= exp(xc/1)*1
                z = model.evalMap{i}.variableIndex;
                x = model.evalMap{i}.computes;
                data.A = [data.A;
                          -sparse([1;3],[x z],-1,3,size(data.A,2))];
                data.b = [data.b;[0;1;0]];
            case 'plog'
                % xv(1)log(xv(2)/xv(1))>=xc i.e.
                % -xc >= -xv(1)log(xv(2)/xv(1))
                z = model.evalMap{i}.computes;
                y = model.evalMap{i}.variableIndex(1);
                x = model.evalMap{i}.variableIndex(2);
                data.A = [data.A;
                          -sparse([1;2;3],[x y z],[1 -1 1],3,size(data.A,2))];
                data.b = [data.b;[0;0;0]];
            case 'slog'
                % log(1+xv) >= xc i.e. (1+xv) >= exp(xc/1)*1
                z = model.evalMap{i}.variableIndex;
                x = model.evalMap{i}.computes;
                data.A = [data.A;
                          -sparse([1;3],[x z],-1,3,size(data.A,2))];
                data.b = [data.b;0;1;1];
            case 'entropy'
                % -xv*log(xv)>=xc i.e. 1 >= exp(xc/xv)*xv
                x = model.evalMap{i}.computes;
                y = model.evalMap{i}.variableIndex;
                data.A = [data.A;
                          -sparse([1;2],[x y],[-1 -1],3,size(data.A,2))];
                data.b = [data.b;[0;0;1]];
            case 'kullbackleibler'
                % -xv1*log(xv1/xv2)>=-xc i.e. xv2 >= exp(-xc/xv1)*xv1
                x = model.evalMap{i}.computes;
                y = model.evalMap{i}.variableIndex(1);
                z = model.evalMap{i}.variableIndex(2);
                data.A = [data.A;
                          -sparse([1;2;3],[x y z],[1 -1 -1],3,size(data.A,2))];
                data.b = [data.b;[0;0;0]];
            case 'expdiff'
                % exp(xv(1)-xv(2)) <= xc
                x = model.evalMap{i}.variableIndex;
                z = model.evalMap{i}.computes;
                data.A = [data.A;
                          -sparse([1;1;3],[x(1) x(2) z],[-1 1 -1],3,size(data.A,2))];
                data.b = [data.b;[0;1;0]];
            otherwise
        end
    end
    model.F_struc = [data.b data.A];
    model.c = data.c;
    model.K = cones;
    % Put back the SDP cone in place
    if model.K.s(1)>0
        if size(sdpData,2) < size(model.F_struc,2)
            sdpDAta(end,size(model.F_struc,2)) = 0;
        end
        model.F_struc = [model.F_struc;sdpData];
    end
end
n = size(model.F_struc,2)-1;
if n > length(model.lb)
    missing = n - length(model.lb);
    model.lb = [model.lb;-inf(missing,1)];
end
if size(model.F_struc,2)-1 > length(model.ub)
    missing = n - length(model.ub);
    model.ub = [model.ub;inf(missing,1)];
end


function hammersley_step_set ( step )

%*****************************************************************************80
%
%% HAMMERSLEY_STEP_SET sets the step of the leaped Hammersley subsequence.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    29 January 2005
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer STEP, the step of the leaped Hammersley subsequence.
%
  global hammersley_STEP

  step = floor ( step );

  if ( ~halham_step_check ( step ) )
    error ( 'HAMMERSLEY_STEP_SET - Fatal error!' );
  end

  hammersley_STEP = step;

  return
end


function pass = test_emptyObjects( pref )
% For empty diskfun objects, does each command deal with them
% appropriately?

% Check things work for empty diskfuns.
f = diskfun();
try
    f + f;
    2*f;
    f*2;
    f.^2;
    2.^f;
    f.^f;
    sqrt(f);
    sum(f);
    integral2(f);
    norm(f);
    squeeze(f);
    diff(f);
    cos(f);
    sin(f);
    sinh(f);
    f.^f + f;
    diag(f);
    trace(f);
    mean(f);
    minandmax2(f);
    median(f);
    flipud(f);
    flipdim(f,1);
    pass = 1;
catch
    pass = 0;
end
end


function vtuk_puvw_write ( output_unit, title, node_num, element_num, ...
  element_order, xyz, element_node, p, uvw )

%*****************************************************************************80
%
%% VTU_PUVW_WRITE writes pressure and velocity data to a VTU file.
%
%  Discussion:
%
%    The data is assumed to have been computed by a finite element program
%    for a 3D geometry which has been meshed using tetrahedral elements
%    of 4 or 10 nodes.
%
%    The solution data includes the pressure and velocity vector at each node.
%
%    The VTU format used here is the modern XML-based format used by the
%    Visual Toolkit for unstructured grid data.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    20 December 2010
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer OUTPUT_UNIT, the output unit.
%
%    Input, string TITLE, a title for the data.
%
%    Input, integer NODE_NUM, the number of nodes.
%
%    Input, integer ELEMENT_NUM, the number of elements.
%
%    Input, integer ELEMENT_ORDER, the order of the elements.
%
%    Input, real XYZ(3,NODE_NUM), the node coordinates.
%
%    Input, integer ELEMENT_NODE(ELEMENT_ORDER,ELEMENT_NUM), the
%    nodes that make up each element.  The node indices are zero based.
%
%    Input, real P(1,NODE_NUM), the pressure at each node.
%
%    Input, real UVW(3,NODE_NUM), the velocity at each node.
%
  if ( element_order == 10 )
    fprintf ( 1, '\n' );
    fprintf ( 1, 'VTU_PUVW_WRITE - Note:\n' );
    fprintf ( 1, '  As a temporary measure, we are handling quadratic tets\n' );
    fprintf ( 1, '  as though they were linear.  That is, the element information\n' );
    fprintf ( 1, '  will only use the vertices, not the midside nodes.\n' );
    element_order = 4;
  end

  fprintf ( output_unit, '<VTKFile type="unstructuredGrid" version="0.1" byte_order="BigEndian">\n' );
  fprintf ( output_unit, '  <UnstructuredGrid>\n' );
  fprintf ( output_unit, '    <Piece NumberOfPoints="%d" NumberOfCells="%d">\n', ...
    node_num, element_num );
  fprintf ( output_unit, '      <PointData Scalars="scalars">\n' );
  fprintf ( output_unit, '        <DataArray type="Float64" Name="pressure" Format="ascii">\n' );
  for node = 1 : node_num
    fprintf ( output_unit, '%f\n', p(node ) );
  end
  fprintf ( output_unit, '        </DataArray>\n' );
  fprintf ( output_unit, '        <DataArray type="Float64" NumberOfComponents="3" Name="velocity" Format="ascii">\n' );
  for node = 1 : node_num
    fprintf ( output_unit, '%f  %f  %f\n', uvw(1:3,node) );
  end
  fprintf ( output_unit, '        </DataArray>\n' );
  fprintf ( output_unit, '      </PointData>\n' );
  fprintf ( output_unit, '      <Points>\n' );
  fprintf ( output_unit, '        <DataArray type="Float64" NumberOfComponents="3" Format="ascii">\n' );
  for node = 1 : node_num
    fprintf ( output_unit, '%f  %f  %f\n', xyz(1:3,node) );
  end
  fprintf ( output_unit, '        </DataArray>\n' );
  fprintf ( output_unit, '      </Points>\n' );
  fprintf ( output_unit, '      <Cells>\n' );

  fprintf ( output_unit, '        <DataArray type="Int32" Name="connectivity" Format="ascii">\n' );
  for element = 1 : element_num
    for order = 1 : element_order
      fprintf ( output_unit, '  %d', element_node(order,element) );
    end
    fprintf ( output_unit, '\n' );
  end
  fprintf ( output_unit, '        </DataArray>\n' );

  fprintf ( output_unit, '        <DataArray type="Int32" Name="offsets" Format="ascii">\n' );
  offset = 0;
  for element = 1 : element_num
    offset = offset + element_order;
    fprintf ( output_unit, '%d\n', offset );
  end
  fprintf ( output_unit, '        </DataArray>\n' );
  fprintf ( output_unit, '        <DataArray type="Int32" Name="types" Format="ascii">\n' );
  if ( element_order == 4 )
    for element = 1 : element_num
      fprintf ( output_unit, '10\n' );
    end
  elseif ( element_order == 10 )
    for element = 1 : element_num
      fprintf ( output_unit, '24\n' );
    end
  end
  fprintf ( output_unit, '        </DataArray>\n' );

  fprintf ( output_unit, '      </Cells>\n' );
  fprintf ( output_unit, '    </Piece>\n' );
  fprintf ( output_unit, '  </UnstructuredGrid>\n' );
  fprintf ( output_unit, '</VTKFILE>\n' );

  return
end


function f  = replace_chromosome(intermediate_chromosome, M, V,pop)

%% function f  = replace_chromosome(intermediate_chromosome,pro,pop)
% This function replaces the chromosomes based on rank and crowding
% distance. Initially until the population size is reached each front is
% added one by one until addition of a complete front which results in
% exceeding the population size. At this point the chromosomes in that
% front is added subsequently to the population based on crowding distance.

%  Copyright (c) 2009, Aravind Seshadri
%  All rights reserved.

[N, m] = size(intermediate_chromosome);
f = zeros(pop,m);                                            % modified by zzb
% Get the index for the population sort based on the rank
% [temp,index] = sort(intermediate_chromosome(:,M + V + 1)); % modified by zzb
[~,index] = sort(intermediate_chromosome(:,M + V + 1));      % modified by zzb
% clear temp m;                                              % modified by zzb
sorted_chromosome = zeros(N,m);                              % modified by zzb
% Now sort the individuals based on the index
for i = 1 : N
    sorted_chromosome(i,:) = intermediate_chromosome(index(i),:);
end

% Find the maximum rank in the current population
max_rank = max(intermediate_chromosome(:,M + V + 1));

% Start adding each front based on rank and crowing distance until the
% whole population is filled.
previous_index = 0;
for i = 1 : max_rank
    % Get the index for current rank i.e the last the last element in the
    % sorted_chromosome with rank i.
    % current_index = max(find(sorted_chromosome(:,M + V + 1) == i));      % modified by zzb
    current_index = find(sorted_chromosome(:,M + V + 1) == i, 1, 'last' ); % modified by zzb
    % Check to see if the population is filled if all the individuals with
    % rank i is added to the population.
    if current_index > pop
        % If so then find the number of individuals with current rank i.
        remaining = pop - previous_index;
        % Get information about the individuals in the current rank i.
        temp_pop = sorted_chromosome(previous_index + 1 : current_index, :);
        % Sort the individuals with rank i in the descending order based on
        % the crowding distance.
        % [temp_sort,temp_sort_index] = sort(temp_pop(:, M + V + 2),'descend'); % modified by zzb
        [~,temp_sort_index] = sort(temp_pop(:, M + V + 2),'descend');           % modified by zzb
        % Start filling individuals into the population in descending order
        % until the population is filled.
        for j = 1 : remaining
            f(previous_index + j,:) = temp_pop(temp_sort_index(j),:);
        end
        return;
    elseif current_index < pop
        % Add all the individuals with rank i into the population.
        f(previous_index + 1 : current_index, :) = ...
            sorted_chromosome(previous_index + 1 : current_index, :);
    else
        % Add all the individuals with rank i into the population.
        f(previous_index + 1 : current_index, :) = ...
            sorted_chromosome(previous_index + 1 : current_index, :);
        return;
    end
    % Get the index for the last added individual.
    previous_index = current_index;
end


classdef prtUiRocSelector < prtUiManagerPanel

    properties

        prtDs

        pf = [];
        pd = [];
        thresholds = [];
        selectedIndex = [];

        handleStruct
    end

    properties (Hidden, SetAccess='protected', GetAccess='protected')
        retainObsUpdateCallbackDepHelper = [];
        retainObsDepHelper = [];
    end
    properties (Dependent)
        retainObs
        retainObsUpdateCallback
    end

    methods
        function self = prtUiRocSelector(varargin)
            if nargin == 1
                self.prtDs = varargin{1};
            else
                self = prtUtilAssignStringValuePairs(self,varargin{:});
            end

            if nargin~=0 && ~self.hgIsValid
               self.create()
            end

            init(self);
        end

        function init(self)
            self.handleStruct.axes = axes('parent',self.managedHandle,'units','normalized','position',[0.1 0.1 0.85 0.8]);
            hold on
            grid on
            self.handleStruct.rocLine = plot(self.handleStruct.axes,nan,nan,'k');
            self.handleStruct.selectX = plot(self.handleStruct.axes,nan,nan,'kx','MarkerSize',12,'HitTest','off');
            hold off
            self.handleStruct.title = title(self.handleStruct.axes,'');
            set(self.handleStruct.rocLine,'HitTest','on','ButtonDownFcn',@(h,e)self.infoUpdate());

            self.updateRoc();
        end
        function updateRoc(self)

            self.selectedIndex = [];
            set(self.handleStruct.selectX,'XData',nan','YData',nan);
            set(self.handleStruct.title,'String','')

            if isempty(self.prtDs)
                set(self.handleStruct.rocLine,'XData',nan','YData',nan);
                self.selectedIndex = [];
                return
            end

            try
                if ~isempty(self.retainObs)
                    [self.pf ,self.pd, self.thresholds] = prtScoreRoc(self.prtDs.retainObservations(self.retainObs));
                else
                    [self.pf ,self.pd, self.thresholds] = prtScoreRoc(self.prtDs);
                end
            catch ME
                msgbox(ME.message,ME.identifier,'Error','Modal')
                set(self.handleStruct.rocLine,'XData',nan','YData',nan);
                return
            end
            set(self.handleStruct.rocLine,'XData',self.pf,'YData',self.pd);
            axis(self.handleStruct.axes,[0 1 0 1]);
        end
        function infoUpdate(self)
            cp = get(self.handleStruct.axes,'CurrentPoint');
            self.selectedIndex = mean(find(cp(1,1) > self.pf,1,'last'),find(cp(1,2) < self.pd,1,'first'));

            set(self.handleStruct.selectX,'XData',self.pf(self.selectedIndex),'YData',self.pd(self.selectedIndex));
            set(self.handleStruct.title,'String',sprintf('Pd = %0.3f, Pf = %0.3f, Theshold = %g',self.pd(self.selectedIndex), self.pf(self.selectedIndex),self.thresholds(self.selectedIndex)));

        end

        function val = get.retainObsUpdateCallback(self)
            val = self.retainObsUpdateCallbackDepHelper;
        end
        function set.retainObsUpdateCallback(self,val)
            assert(isempty(val) || (isa(val, 'function_handle') && nargin(val)==1),'retainObsUpdateCallback must be a function handle that accepts one input')

            self.retainObsUpdateCallbackDepHelper = val;
        end
        function val = get.retainObs(self)
            val = self.retainObsDepHelper;
        end
        function set.retainObs(self, val)
            if ~isempty(self.retainObsUpdateCallback)
                self.retainObsUpdateCallback(val)
            end
            self.retainObsDepHelper = val;
            self.updateRoc();
        end
        function updateRetainObs(self, val)
            self.retainObs = val;
        end

    end
end


% FEATURE_WRAPPER Wrapper for feature functions
%
% Usage
%    feature = FEATURE_WRAPPER(x, objects, feature_fun, options)
%
% Input
%    x (numeric): The file data.
%    object (struct): The objects contained in the data.
%    feature_fun (function handle): The real feature function handle, takes as
%       input one signal (or multiple, arranged as columns of a matrix) and
%       outputs the corresponding feature vectors. These are arranged with
%       feature dimension along the first axis, time/space along second and
%       third axes and signal index along the fourth axis (if more than one
%       signal are input).
%    options (struct): Various parameters for the wrapper, such as:
%       options.input_sz (int): The size of the input vectors to be given to
%          feature_fun. If empty, takes the rectangle specified by the objects
%          structure, otherwise takes the rectangle of size input_sz centered
%          on the object (default empty).
%       options.output_sz (int): The desired size of the data covered by the
%          feature vector. If empty, keeps the output from feature_fun, other-
%          wise extracts the time/space rectangle of size output_sz centered
%          on the original data, taking into account any subsampling by
%          feature_fun (default empty).
%       options.obj_normalize (int): The normalization of the input vectors
%          before being given to feature_fun. Can be empty, 1, 2, or Inf
%          (default []).
%       options.collapse (boolean): If true, collapses the time/space dimen-
%          sion into one vector, otherwise leaves this dimension intact
%          (default false).
%
% Output
%    feature (numeric): An PxNxK array where P is the feature dimension, N is
%       the space/time dimension and K is the signal index, if multiple
%       objects are given as input.
%
% See also
%    PREPARE_DATABASE

function t = feature_wrapper(x,objects,fun,options)
	if nargin < 4
		options = struct();
	end

	options = fill_struct(options, 'input_sz', []);
	options = fill_struct(options, 'output_sz', []);
	options = fill_struct(options, 'obj_normalize', []);
	options = fill_struct(options, 'collapse', 0);

	if isempty(options.input_sz)
		sz=objects(1).u2-objects(1).u1+ones(size(objects(1).u1));

        if length(sz)==1
            sz=[sz 1];
        end
        buf = zeros([sz, ...
			length(objects)]);

		u1 = [objects.u1];
		u2 = [objects.u2];
	else
		if length(options.input_sz) == 1
			options.input_sz = [options.input_sz 1];
		end

		buf = zeros([options.input_sz,length(objects)]);

		if options.input_sz(2)==1
			u1 = round(([objects.u1]+[objects.u2]+1)/2-options.input_sz(1)/2);
			u2 = u1+options.input_sz(1)-1;
		else
			u1 = round(([objects.u1]+[objects.u2]+1)/2-options.input_sz/2);
			u2 = u1+options.input_sz-1;
		end
	end

	% extract objects with bounding box
	for l = 1:length(objects)
		n_dim = sum(size(x)>1);
		switch n_dim
			case 1
				ind = max(u1(l),1):min(u2(l),length(x));
				buf(:,1,l) = [zeros(max(0,1-u1(l)),1); x(ind); zeros(max(0,u2(l)-length(x)),1)];
			case 2
				buf = x;
				% TODO : extract bounding box
		end
	end

	% normalize
	if ~isempty(options.obj_normalize)
		if options.obj_normalize == 1
			n = sum(abs(buf),1);
		elseif options.obj_normalize == 2
			n = sqrt(sum(abs(buf).^2,1));
		elseif options.obj_normalize == Inf
			n = max(abs(buf),[],1);
		end

		buf = bsxfun(@times,buf,1./n);
	end

	t = fun(buf);

	if ~isempty(options.output_sz)
		if length(options.output_sz) == 1
			options.output_sz = [options.output_sz 1];
		end

		N = [size(t,2) size(t,3)];
		extent = floor(options.output_sz./(2*options.input_sz).*N);

		if options.input_sz(2) > 1
			t = t(:,N(1)/2+1-extent(1):N(1)/2+1+extent(1), ...
				N(2)/2+1-extent(2):N(2)/2+1+extent(2),:);
		else
			t = t(:,N(1)/2+1-extent(1):N(1)/2+1+extent(1),1,:);
		end
	end

	t = reshape(t,[size(t,1) size(t,2)*size(t,3) size(t,4)]);

	if options.collapse
		t = reshape(t,[size(t,1)*size(t,2) 1 size(t,3)]);
	end
end