function tests = test_spm_dcm_peb_to_gcm
% Unit Tests for spm_dcm_peb_to_gcm
%__________________________________________________________________________

% Copyright (C) 2015-2022 Wellcome Centre for Human Neuroimaging


tests = functiontests(localfunctions);

%--------------------------------------------------------------------------
function data_path = get_data_path()
data_path = fullfile( spm('Dir'), 'tests', ...
    'data', 'test_spm_dcm_peb_to_gcm');

%--------------------------------------------------------------------------
function test_2groups(testCase)
% Tests creating GCMs for two groups of subjects who differ in a single
% parameter

% Settings
n     = 100;        % Subjects
b_Hz  = 0.6;        % Strength of group difference
param = 'B(2,1,3)'; % Parameter to vary between groups
beta  = 16;         % Within:between variance ratio for simulated parameters

rng(1);

% Load template DCM
DCM_template = fullfile(get_data_path(), 'DCM_attention.mat');
DCM_template = load(DCM_template);
DCM_template = DCM_template.DCM;

% Indices of subjects in each group
group    = cell(1,2);
group{1} = 1:ceil(n/2);
group{2} = ceil(n/2)+1:n;

% PEB design matrix
X             = ones(n,2);
X(group{2},2) = -1;

% Create dummy PEB
PEB = create_peb(DCM_template, X, b_Hz, param, beta);

% Create GCM array
options = struct('nsubjects',n);
[GCM,PEB] = spm_dcm_peb_to_gcm(PEB,DCM_template,options);

% Check size
testCase.assertTrue( all(size(GCM)==[n 1]) );

% Get group arithmetic average of parameters
Eps = [];
for s = 1:n
    Eps(:,s) = spm_vec(GCM{s,1}.Ep);
end
Eps = Eps(PEB.Pind,:);

% Check subject' parameters are similar to PEB.Ep(:,1)
testCase.assertGreaterThanOrEqual(...
    one_sample_tt(mean(Eps,2)-PEB.Ep(:,1)),...
    0.05);

% Check group difference in B-parameter is similar to PEB.Ep(idx,2)
idx       = spm_fieldindices(GCM{1}.M.pE,param);
common    = PEB.Ep(PEB.Pind == idx,1);
attention = PEB.Ep(PEB.Pind == idx,2);
expected  = [common+attention common-attention]';

for g = 1:length(group)    
    % Gather group's parameters
    Eps = [];
    for s = 1:length(group{g})
        Eps(:,s) = spm_vec(GCM{group{g}(s),1}.Ep);
    end
    
    % Check group mean is similar to PEB parameter
    testCase.assertGreaterThanOrEqual(...
        one_sample_tt(Eps(idx,:), expected(g)),...
        0.05);
    
    % Check that between-subjects variance of parameters is as expected
    testCase.assertGreaterThanOrEqual(...
        one_sample_tt(diag(PEB.Ce) - var(Eps(PEB.Pind,:),[],2)),...
        0.05);    
    
end

%--------------------------------------------------------------------------
function PEB = create_peb(DCM, X, b_Hz, param, beta)
% Generate dummy PEB for simulation
%
% DCM   - Template DCM
% X     - PEB design matrix [parameters x regressors]
% b_Hz  - Group difference on a connection in Hz
% param - Parameter name to vary between groups
% beta  - Within:between variance ratio for simulated parameters

% Fields to include in the PEB
fields = {'A','B','C','transit','decay','epsilon'};

% Get within-subject priors from template DCM
[pE,pC] = spm_dcm_fmri_priors(DCM.a,DCM.b,DCM.c,DCM.d);

% Sample once from priors to get 'true' group-level parameter means
pE_struct = pE;
pE        = full(spm_vec(pE));
gP        = spm_normrnd(pE,pC,1);

% Force C-matrix (driving input) to be positive
idx = spm_fieldindices(pE_struct,'C');
gP(idx) = abs(gP(idx));

% Get indices of parameters to include in the PEB
Pind = spm_find_pC(pC,pE_struct,fields);

% Create group difference regressor
att = zeros(length(pE),1);
idx = spm_fieldindices(pE_struct,param);
att(idx) = b_Hz;

% Collate PEB
clear PEB;
PEB.Ep   = [gP(Pind) att(Pind)];
PEB.beta = beta;
PEB.Pind = Pind;
PEB.M.X  = X;

% -------------------------------------------------------------------------
function p = one_sample_tt(data,mu)
% One sample t-test

if nargin < 2, mu = 0; end
t = (mean(data)-mu) / (std(data) / sqrt(length(data)));
p = spm_Tcdf(-t,length(data)-1) * 2;
