function t_most_30b_1_1_17(quiet)
%T_MOST_30B_1_1_17  Tests for MOST.

%   MOST
%   Copyright (c) 2009-2020, Power Systems Engineering Research Center (PSERC)
%   by Ray Zimmerman, PSERC Cornell
%
%   This file is part of MOST.
%   Covered by the 3-clause BSD License (see LICENSE file for details).
%   See https://github.com/MATPOWER/most for more info.

if nargin < 1
    quiet = 0;
end

n_tests = 34;

t_begin(n_tests, quiet);

casename = 't_case30_most';
fudging = struct( ...       %% paramters for fudging reserve contract for sopf2
    'fudge',    0.05, ...   %% initial value (MW)
    'step',     0.01, ...   %% if necessary, increase by this amount and retry (MW)
    'lim',      0.1);       %% upper limit (MW), give up if no convergence
                            %% with fudge equal to this limit

%% options
algs.dc     = {'DEFAULT'};  %% opf.dc.solver sequence to try for c3sopf (DC run)
algs.ac     = {'DEFAULT'};  %% opf.ac.solver sequence to try for c3sopf (AC run)
mpopt = mpoption('verbose', 0, 'out.all', 0);
mpopt = mpoption(mpopt, 'opf.violation', 5e-7, 'mips.comptol', 5e-8);
mpopt = mpoption(mpopt, 'sopf.force_Pc_eq_P0', 0);  %% don't constrain contracted == base case dispatch
if have_feature('linprog')
    if have_feature('linprog_ds')
        mpopt = mpoption(mpopt, 'linprog.Algorithm', 'dual-simplex');
    else
        mpopt = mpoption(mpopt, 'linprog.Algorithm', 'simplex');
    end
end
mpoptac = mpoption(mpopt, 'model', 'AC');
mpoptdc = mpoption(mpopt, 'model', 'DC');
mpopt = mpoption(mpopt, 'most.solver', algs.dc{1});

%% turn off warnings
s7 = warning('query', 'MATLAB:nearlySingularMatrix');
s6 = warning('query', 'MATLAB:nearlySingularMatrixUMFPACK');
warning('off', 'MATLAB:nearlySingularMatrix');
warning('off', 'MATLAB:nearlySingularMatrixUMFPACK');

%% define named indices into data matrices
[GEN_BUS, PG, QG, QMAX, QMIN, VG, MBASE, GEN_STATUS, PMAX, PMIN, ...
    MU_PMAX, MU_PMIN, MU_QMAX, MU_QMIN, PC1, PC2, QC1MIN, QC1MAX, ...
    QC2MIN, QC2MAX, RAMP_AGC, RAMP_10, RAMP_30, RAMP_Q, APF] = idx_gen;
[F_BUS, T_BUS, BR_R, BR_X, BR_B, RATE_A, RATE_B, RATE_C, ...
    TAP, SHIFT, BR_STATUS, PF, QF, PT, QT, MU_SF, MU_ST, ...
    ANGMIN, ANGMAX, MU_ANGMIN, MU_ANGMAX] = idx_brch;
[CT_LABEL, CT_PROB, CT_TABLE, CT_TBUS, CT_TGEN, CT_TBRCH, CT_TAREABUS, ...
    CT_TAREAGEN, CT_TAREABRCH, CT_ROW, CT_COL, CT_CHGTYPE, CT_REP, ...
    CT_REL, CT_ADD, CT_NEWVAL, CT_TLOAD, CT_TAREALOAD, CT_LOAD_ALL_PQ, ...
    CT_LOAD_FIX_PQ, CT_LOAD_DIS_PQ, CT_LOAD_ALL_P, CT_LOAD_FIX_P, ...
    CT_LOAD_DIS_P, CT_TGENCOST, CT_TAREAGENCOST, CT_MODCOST_F, ...
    CT_MODCOST_X] = idx_ct;

%% reserve and delta offers
xgd_table.colnames = {
    'PositiveActiveReservePrice', ...
            'PositiveActiveReserveQuantity', ...
                    'NegativeActiveReservePrice', ...
                            'NegativeActiveReserveQuantity', ...
                                    'PositiveActiveDeltaPrice', ...
                                            'NegativeActiveDeltaPrice', ...
};
xgd_table.data = [
    10.1    15      10.0    15      0.1     0.0;
    10.3    30      10.2    30      0.3     0.2;
    10.5    20      10.4    20      0.5     0.4;
    10.7    25      10.6    25      0.7     0.6;
    20.1    25      20.0    25      60.1    60.0;
    20.3    15      20.2    15      15.1    15.0;
    20.5    30      20.4    30      60.3    60.2;
    20.7    15      20.6    15      15.3    15.2;
    30.1    15      30.0    15      60.3    60.4;
    30.3    30      30.2    30      30.1    30.0;
    30.5    25      30.4    25      60.7    60.6;
    30.7    30      30.6    30      30.3    30.2;
    0.001   50      0.002   50      0       0;
    0.001   50      0.002   50      0       0;
    0.001   50      0.002   50      0       0;
    0.001   50      0.002   50      0       0;
    0.001   50      0.002   50      0       0;
    0.001   50      0.002   50      0       0;
    0.001   50      0.002   50      0       0;
    0.001   50      0.002   50      0       0;
    0.001   50      0.002   50      0       0;
    0.001   50      0.002   50      0       0;
    0.001   50      0.002   50      0       0;
    0.001   50      0.002   50      0       0;
    0.001   50      0.002   50      0       0;
    0.001   50      0.002   50      0       0;
    0.001   50      0.002   50      0       0;
    0.001   50      0.002   50      0       0;
    0.001   50      0.002   50      0       0;
    0.001   50      0.002   50      0       0;
    0.001   50      0.002   50      0       0;
    0.001   50      0.002   50      0       0;
];

%% contingency table
% label probty  type        row column      chgtype newvalue
contab = [
    1   0.002   CT_TBRCH    1   BR_STATUS   CT_REP  0;      %% line 1-2
    2   0.002   CT_TBRCH    2   BR_STATUS   CT_REP  0;      %% line 1-3, all power from gen1 flows via gen2
    3   0.002   CT_TBRCH    3   BR_STATUS   CT_REP  0;      %% line 2-4, a path to loads @ buses 7 & 8
    4   0.002   CT_TBRCH    5   BR_STATUS   CT_REP  0;      %% line 2-5, a path to loads @ buses 7 & 8
    5   0.002   CT_TBRCH    6   BR_STATUS   CT_REP  0;      %% line 2-6, a path to loads @ buses 7 & 8
    6   0.002   CT_TBRCH    36  BR_STATUS   CT_REP  0;      %% line 28-27, tie line between areas 1 & 3
    7   0.002   CT_TBRCH    15  BR_STATUS   CT_REP  0;      %% line 4-12, tie line between areas 1 & 2
    8   0.002   CT_TBRCH    12  BR_STATUS   CT_REP  0;      %% line 6-10, tie line between areas 1 & 3
    9   0.002   CT_TBRCH    14  BR_STATUS   CT_REP  0;      %% line 9-10, tie line between areas 1 & 3
    10  0.002   CT_TGEN     1   GEN_STATUS  CT_REP  0;      %% gen 1 at bus 1
    11  0.002   CT_TGEN     2   GEN_STATUS  CT_REP  0;      %% gen 2 at bus 2
    12  0.002   CT_TGEN     3   GEN_STATUS  CT_REP  0;      %% gen 3 at bus 22
    13  0.002   CT_TGEN     4   GEN_STATUS  CT_REP  0;      %% gen 4 at bus 27
    14  0.002   CT_TGEN     5   GEN_STATUS  CT_REP  0;      %% gen 5 at bus 23
    15  0.002   CT_TGEN     6   GEN_STATUS  CT_REP  0;      %% gen 6 at bus 13
    20  0.010   CT_TGEN     0   PMIN        CT_REL  1.1;    %% 10% load increase
    20  0.010   CT_TGEN     0   QMIN        CT_REL  1.1;
    21  0.010   CT_TGEN     0   PMIN        CT_REL  0.9;    %% 10% load decrease
    21  0.010   CT_TGEN     0   QMIN        CT_REL  0.9;
];
clist = unique(contab(:, CT_LABEL));
nc = length(clist);

%% load the case
mpc = loadcase(casename);
gbus = mpc.gen(:, GEN_BUS);

%%-----  get c3sopf results  -----
rdc = c3sopf_retry(algs.dc, mpc, xgd_table.data, contab, mpoptdc);
% rac = c3sopf_retry(algs.ac, mpc, xgd_table.data, contab, mpoptac);
% save t_most2_soln rdc rac -v6
% s = load('t_most2_soln');
s.rdc = rdc;
% s.rac = rac;

%%-----  set up data for DC run (most)  -----
ng = size(mpc.gen, 1);      %% number of gens
xgd = loadxgendata(xgd_table, mpc);
md = loadmd(mpc, [], xgd, [], contab);

%%-----  do DC run (most)  -----
r = most(md, mpopt);

%%-----  test the results  -----
t = 'success1';
t_ok(s.rdc.opf_results.success, t);
t = 'success2';
t_is(r.QP.exitflag, 1, 12, t);

t = 'f';
t_is(r.results.f, s.rdc.opf_results.f, 4, t);

t = 'Pg : base';
t_is(r.flow(1,1,1).mpc.gen(:, PG), s.rdc.base.gen(:, PG), 5, t);
t = 'Pg : cont ';
for k = 1:nc
    t_is(r.flow(1,1,k+1).mpc.gen(:, PG), s.rdc.cont(k).gen(:, PG), 5, sprintf('%s %d', t, k));
end

% t = 'gen : base';
% t_is(r.flow(1,1,1).mpc.gen(:,1:MU_PMIN), s.rdc.base.gen(:,1:MU_PMIN), 3, t);
% t = 'gen : cont ';
% for k = 1:nc
%     t_is(r.flow(1,1,k+1).mpc.gen(:,1:MU_PMIN), s.rdc.cont(k).gen(:,1:MU_PMIN), 3, sprintf('%s %d', t, k));
% end

t = 'energy prices';
t_is(r.results.GenPrices, s.rdc.energy.prc.sum_bus_lam_p(gbus), 6, t);

t = 'Pc';
t_is(r.results.Pc, s.rdc.energy.Pc, 4, t);

t = 'Gmin';
t_is(r.results.Pc - r.results.Rpm, s.rdc.energy.Gmin, 4, t);

t = 'Gmax';
t_is(r.results.Pc + r.results.Rpp, s.rdc.energy.Gmax, 4, t);

t = 'upward contingency reserve quantities';
t_is(r.results.Rpp, s.rdc.reserve.qty.Rp_pos, 4, t);

t = 'downward contingency reserve quantities';
t_is(r.results.Rpm, s.rdc.reserve.qty.Rp_neg, 4, t);

t = 'upward contingency reserve prices';
t_is(r.results.RppPrices, s.rdc.reserve.prc.Rp_pos, 6, t);

t = 'downward contingency reserve prices';
t_is(r.results.RpmPrices, s.rdc.reserve.prc.Rp_neg, 6, t);

t = 'contingency physical ramp price';
[vv, ll] = r.om.get_idx();
Ramp_P_max = zeros(ng, nc);
sum_muPmax = zeros(ng, 1);
sum_muPmin = zeros(ng, 1);
for k = 1:nc+1
    ii = find(r.flow(1,1,k).mpc.gen(:, GEN_STATUS) > 0);
    if k > 1
        Ramp_P_max(ii,k-1) = (r.QP.lambda.mu_u(ll.i1.rampcont(1,1,k):ll.iN.rampcont(1,1,k)) - r.QP.lambda.mu_l(ll.i1.rampcont(1,1,k):ll.iN.rampcont(1,1,k))) / mpc.baseMVA;
    end
    sum_muPmax(ii) = sum_muPmax(ii) + r.flow(1,1,k).mpc.gen(ii, MU_PMAX);
    sum_muPmin(ii) = sum_muPmin(ii) + r.flow(1,1,k).mpc.gen(ii, MU_PMIN);
end
t_is(Ramp_P_max, s.rdc.energy.mu.Ramp_P_max, 2, t);

t = 'sum_muPmax';
t_is(sum_muPmax, s.rdc.energy.sum_muPmax, 1, t);

t = 'sum_muPmin';
t_is(sum_muPmin, s.rdc.energy.sum_muPmin, 0.9, t);

t = 'Rpmax_pos';
Rpmax_pos = (r.QP.lambda.upper(vv.i1.Rpp(1):vv.iN.Rpp(1)) - r.QP.lambda.lower(vv.i1.Rpp(1):vv.iN.Rpp(1))) / mpc.baseMVA;
t_is(Rpmax_pos, s.rdc.reserve.mu.Rpmax_pos, 6, t);

t = 'Rpmax_neg';
Rpmax_neg = (r.QP.lambda.upper(vv.i1.Rpm(1):vv.iN.Rpm(1)) - r.QP.lambda.lower(vv.i1.Rpm(1):vv.iN.Rpm(1))) / mpc.baseMVA;
t_is(Rpmax_neg, s.rdc.reserve.mu.Rpmax_neg, 6, t);

% g1 = s.rdc.base.gen(:, PG);
% g2 = r.flow(1,1,1).mpc.gen(:, PG);
% for k = 1:nc
%     g1 = [ g1 s.rdc.cont(k).gen(:, PG) ];
%     g2 = [ g2 r.flow(1,1,k+1).mpc.gen(:, PG) ];
% end
% [m,n] = size(g1);
% for j = 1:n
%     fprintf('\n');
%     for i = 1:m
%         fprintf('%9.2f  %9.2f\n', g1(i,j), g2(i,j));
%     end
% end

%%-----  do AC run (most)  -----
%mostac;

%% turn warnings back on
warning(s7.state, 'MATLAB:nearlySingularMatrix');
warning(s6.state, 'MATLAB:nearlySingularMatrixUMFPACK');

t_end;


% POP_REJMENU - Main menu for rejecting trials in an EEG dataset
%
% Usage: >> pop_rejmenu(INEEG, typerej);
%
% Inputs:
%   INEEG      - input dataset
%   typerej    - data to reject on (0 = component activations;
%                1 = raw electrode data). {Default: 1 = reject on raw data}
%
% Author: Arnaud Delorme, CNL / Salk Institute, 2001
%
% See also: EEGLAB, POP_EEGPLOT, pop_eegthresh, POP_REJTREND
% POP_REJKURT, POP_JOINTPROB, POP_REJSPEC

% Copyright (C) 2001 Arnaud Delorme, Salk Institute, arno@salk.edu
%
% This file is part of EEGLAB, see http://www.eeglab.org
% for the documentation and details.
%
% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are met:
%
% 1. Redistributions of source code must retain the above copyright notice,
% this list of conditions and the following disclaimer.
%
% 2. Redistributions in binary form must reproduce the above copyright notice,
% this list of conditions and the following disclaimer in the documentation
% and/or other materials provided with the distribution.
%
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
% THE POSSIBILITY OF SUCH DAMAGE.

function pop_rejmenu( EEG, icacomp );

if icacomp == 0
	if isempty( EEG.icasphere )
		disp('Error: you must first run ICA on the data'); return;
	end
end

if icacomp == 1 	rejtitle = 'Reject trials using data statistics - pop_rejmenu()'; tagmenu = 'rejtrialraw';
else            	rejtitle = 'Reject trials using component activity statistics - pop_rejmenu()'; tagmenu = 'rejtrialica';
end;

if ~isempty( findobj('tag', tagmenu))
	error('cannot open two identical windows; close the first one first');
end

figure('visible', 'off', 'numbertitle', 'off', 'name', rejtitle, 'tag', tagmenu);

% definition of callbacks
% -----------------------
checkstatus = [ 'rejstatus = get( findobj(''parent'', gcbf, ''tag'', ''rejstatus''), ''value'');' ...
                'if rejstatus == 3,' ...
                '    EEG.reject.disprej = {};' ...
				'    if get( findobj(''parent'', gcbf, ''tag'', ''IManual''), ''value''), EEG.reject.disprej{1} = ''manual''; end;' ...
                '    if get( findobj(''parent'', gcbf, ''tag'', ''IThresh''), ''value''), EEG.reject.disprej{2} = ''thresh''; end;' ...
                '    if get( findobj(''parent'', gcbf, ''tag'', ''IConst''), ''value''), EEG.reject.disprej{3} = ''const''; end;' ...
                '    if get( findobj(''parent'', gcbf, ''tag'', ''IEnt''), ''value''), EEG.reject.disprej{4} = ''jp''; end;' ...
                '    if get( findobj(''parent'', gcbf, ''tag'', ''IKurt''), ''value''), EEG.reject.disprej{5} = ''kurt''; end;' ...
                '    if get( findobj(''parent'', gcbf, ''tag'', ''IFreq''), ''value''), EEG.reject.disprej{6} = ''freq''; end;' ...
                'end;' ...
                'rejstatus = rejstatus-1;' ]; % from 1-3 range, go to 0-2 range

if icacomp, ICAPREFIX = '';
else        ICAPREFIX = 'ica';
end
% tmp_comall is used when returning from eegplot
tmp_comall =      [ 'set(findobj(''''parent'''', findobj(''''tag'''', ''''' tagmenu ...
					'''''), ''''tag'''', ''''mantrial''''), ''''string'''', num2str(sum(EEG.reject.' ICAPREFIX 'rejmanual)));' ...
                    'set(findobj(''''parent'''', findobj(''''tag'''', ''''' tagmenu ...
					'''''), ''''tag'''', ''''threshtrial''''), ''''string'''', num2str(sum(EEG.reject.' ICAPREFIX 'rejthresh)));' ...
                    'set(findobj(''''parent'''', findobj(''''tag'''', ''''' tagmenu ...
					'''''), ''''tag'''', ''''freqtrial''''), ''''string'''', num2str(sum(EEG.reject.' ICAPREFIX 'rejfreq)));' ...
                    'set(findobj(''''parent'''', findobj(''''tag'''', ''''' tagmenu ...
					'''''), ''''tag'''', ''''consttrial''''), ''''string'''', num2str(sum(EEG.reject.' ICAPREFIX 'rejconst)));' ...
                    'set(findobj(''''parent'''', findobj(''''tag'''', ''''' tagmenu ...
					'''''), ''''tag'''', ''''enttrial''''), ''''string'''', num2str(sum(EEG.reject.' ICAPREFIX 'rejjp)));' ...
                    'set(findobj(''''parent'''', findobj(''''tag'''', ''''' tagmenu ...
					'''''), ''''tag'''', ''''kurttrial''''), ''''string'''', num2str(sum(EEG.reject.' ICAPREFIX 'rejkurt)));' ];

cb_manual = [ 	checkstatus ...
				'pop_eegplot( EEG, ' int2str( icacomp ) ', rejstatus, 0,''' tmp_comall ''');' ...
				'clear rejstatus;' ];

% -----------------------------------------------------

cb_compthresh = [ '  posthresh = get( findobj(''parent'', gcbf, ''tag'', ''threshpos''), ''string'' );' ...
                  '  negthresh = get( findobj(''parent'', gcbf, ''tag'', ''threshneg''), ''string'' );' ...
                  '  startime  = get( findobj(''parent'', gcbf, ''tag'', ''threshstart''), ''string'' );' ...
                  '  endtime   = get( findobj(''parent'', gcbf, ''tag'', ''threshend''), ''string'' );' ...
                  '  elecrange = get( findobj(''parent'', gcbf, ''tag'', ''threshelec''), ''string'' );' ...
                  checkstatus ...
				  '[EEG Itmp LASTCOM] = pop_eegthresh( EEG,' int2str(icacomp) ...
				  ', elecrange, negthresh, posthresh, str2num(startime)/1000, str2num(endtime)/1000, rejstatus, 0,''' tmp_comall ''');' ...
				  'EEG = eegh(LASTCOM, EEG);' ...
				  'clear com Itmp elecrange posthresh negthresh startime endtime rejstatus;' ];

%                  '  set(findobj(''parent'', gcbf, ''tag'', ''threshtrial''), ''string'', num2str(EEG.trials - length(Itmp)));' ...
%				  'eegh(LASTCOM);' ...
%				  'clear Itmp elecrange posthresh negthresh startime endtime rejstatus;' ];

% -----------------------------------------------------
cb_compfreq   = [ '  posthresh = get( findobj(''parent'', gcbf, ''tag'', ''freqpos''), ''string'' );' ...
                  '  negthresh = get( findobj(''parent'', gcbf, ''tag'', ''freqneg''), ''string'' );' ...
                  '  startfreq = get( findobj(''parent'', gcbf, ''tag'', ''freqstart''), ''string'' );' ...
                  '  endfreq   = get( findobj(''parent'', gcbf, ''tag'', ''freqend''), ''string'' );' ...
                  '  elecrange = get( findobj(''parent'', gcbf, ''tag'', ''freqelec''), ''string'' );' ...
                  checkstatus ...
				  '[EEG Itmp LASTCOM] = pop_rejspec( EEG,' int2str(icacomp) ...
				  ', elecrange, negthresh, posthresh, startfreq, endfreq, rejstatus, 0,''' tmp_comall ''');' ...
				  'EEG = eegh(LASTCOM, EEG);' ...
				  'clear Itmp elecrange posthresh negthresh startfreq endfreq rejstatus;' ];

% -----------------------------------------------------
cb_compconstrej = [ '  minslope   = get( findobj(''parent'', gcbf, ''tag'', ''constpnts''), ''string'' );' ...
					'  minstd    = get( findobj(''parent'', gcbf, ''tag'', ''conststd''), ''string'' );' ...
					'  elecrange = get( findobj(''parent'', gcbf, ''tag'', ''constelec''), ''string'' );' ...
                    checkstatus ...
				    '[rej LASTCOM] = pop_rejtrend(EEG,' int2str(icacomp) ', elecrange, ''' ...
					int2str(EEG.pnts) ''', minslope, minstd, rejstatus, 0,''' tmp_comall ''');' ...
				    'EEG = eegh(LASTCOM, EEG);' ...
				    'clear rej elecrange minslope minstd rejstatus;' ];

% -----------------------------------------------------
cb_compenthead = [ '  locthresh  = get( findobj(''parent'', gcbf, ''tag'', ''entloc''), ''string'' );', ...
				   '  globthresh = get( findobj(''parent'', gcbf, ''tag'', ''entglob''), ''string'' );', ...
				   '  elecrange  = get( findobj(''parent'', gcbf, ''tag'', ''entelec''), ''string'' );' ];
cb_compenttail = [ '  set( findobj(''parent'', gcbf, ''tag'', ''entloc''), ''string'', num2str(locthresh) );', ...
				   '  set( findobj(''parent'', gcbf, ''tag'', ''entglob''), ''string'', num2str(globthresh) );', ...
				   '  set( findobj(''parent'', gcbf, ''tag'', ''enttrial''), ''string'', num2str(nrej) );' ...
				   'EEG = eegh(LASTCOM, EEG);' ...
				   'clear nrej elecrange locthresh globthresh rejstatus;' ];
cb_compentplot =  [ cb_compenthead ...
				    '[EEG locthresh globthresh nrej LASTCOM] = pop_jointprob( EEG, ' int2str(icacomp) ...
					', elecrange, locthresh, globthresh, 0, 0,''' tmp_comall ''');', ...
				    cb_compenttail ];
cb_compentcalc =  [ cb_compenthead ...
				    '[EEG locthresh globthresh nrej LASTCOM] = pop_jointprob( EEG, ' int2str(icacomp) ...
					', [ str2num(elecrange) ], str2num(locthresh), str2num(globthresh), 0, 0);', ...
 				    cb_compenttail ];
cb_compenteeg  =  [ cb_compenthead ...
                    checkstatus ...
				    '[EEG locthresh globthresh nrej LASTCOM] = pop_jointprob( EEG, ' int2str(icacomp) ...
					', elecrange, locthresh, globthresh, rejstatus, 0, 1,''' tmp_comall ''');', ...
 				    cb_compenttail ];

% -----------------------------------------------------
cb_compkurthead =[ '  locthresh  = get( findobj(''parent'', gcbf, ''tag'', ''kurtloc''), ''string'' );', ...
				   '  globthresh = get( findobj(''parent'', gcbf, ''tag'', ''kurtglob''), ''string'' );', ...
				   '  elecrange  = get( findobj(''parent'', gcbf, ''tag'', ''kurtelec''), ''string'' );' ];
cb_compkurttail =[ '  set( findobj(''parent'', gcbf, ''tag'', ''kurtloc''), ''string'', num2str(locthresh) );', ...
				   '  set( findobj(''parent'', gcbf, ''tag'', ''kurtglob''), ''string'', num2str(globthresh) );', ...
				   '  set( findobj(''parent'', gcbf, ''tag'', ''kurttrial''), ''string'', num2str(nrej) );' ...
				   'EEG = eegh(LASTCOM, EEG);' ...
				   'clear nrej elecrange locthresh globthresh rejstatus;' ];
cb_compkurtplot = [ cb_compkurthead ...
				    '[EEG locthresh globthresh nrej LASTCOM] = pop_rejkurt( EEG, ' int2str(icacomp) ...
					', elecrange, locthresh, globthresh, 0, 0,''' tmp_comall ''');', ...
				    cb_compkurttail ];
cb_compkurtcalc = [ cb_compkurthead ...
				    '[EEG locthresh globthresh nrej LASTCOM] = pop_rejkurt( EEG, ' int2str(icacomp) ...
					', [ str2num(elecrange) ], str2num(locthresh), str2num(globthresh), 0, 0);', ...
 				    cb_compkurttail ];
cb_compkurteeg  = [ cb_compkurthead ...
                    checkstatus ...
				    '[EEG locthresh globthresh nrej LASTCOM] = pop_rejkurt( EEG, ' int2str(icacomp) ...
					', elecrange, locthresh, globthresh, rejstatus, 0, 1,''' tmp_comall ''');', ...
 				    cb_compkurttail ];

% -----------------------------------------------------
cb_reject =      [ 'set( findobj(''parent'', gcbf, ''tag'', ''rejstatus''), ''value'', 3);' ... % force status to 3
				    checkstatus ...
				   '[EEG LASTCOM] = eeg_rejsuperpose(EEG,' int2str(icacomp) ',1,1,1,1,1,1,1); EEG = eegh(LASTCOM, EEG);' ...
                   'if isempty(find(EEG.reject.rejglobal)),' ...
                   '   warndlg2(strvcat(''No epoch selected...'', ''When using thresholding, click update'',''marks in the EEG plotting window''));' ...
				   'else,' ...
                   '   [EEG LASTCOM] = pop_rejepoch( EEG, EEG.reject.rejglobal, 1);' ...
				   '   if ~isempty(LASTCOM), ' ...
				   '      EEG = eegh(LASTCOM, EEG); [ALLEEG EEG CURRENTSET LASTCOM] = pop_newset(ALLEEG, EEG, CURRENTSET); eegh(LASTCOM);' ...
				   '   end; eeglab redraw; close(gcbf);' ...
                   'end;' ];

cb_clear =       [ 'close gcbf; EEG = rmfield( EEG, ''reject''); EEG.reject.rejmanual = [];' ...
				   'EEG=eeg_checkset(EEG); pop_rejmenu(EEG,' int2str(icacomp) ');' ];

cb_close =       [ 'close gcbf;' ...
				   'disp(''Marks stored in dataset'');' ...
				   '[ALLEEG EEG CURRENTSET] = eeg_store(ALLEEG, EEG, CURRENTSET);' ...
				   'eegh(''[ALLEEG EEG CURRENTSET] = eeg_store(ALLEEG, EEG, CURRENTSET);'');'];

lisboxoptions = { 'string', [ 'Show only the new trials marked for rejection by the measure selected above|' ...
                  'Show previous and new trials marked for rejection by the measure selected above|' ...
                  'Show all trials marked for rejection by the measure selected above or checked below'], 'tag', 'rejstatus', 'value', 1, 'callback', ...
                  [ 'if get(gcbo, ''value'') == 3,' ...
                    '   set(findobj(''parent'', gcbf, ''style'', ''checkbox''), ''enable'', ''on'');' ...
                    'else' ...
                    '   set(findobj(''parent'', gcbf, ''style'', ''checkbox''), ''enable'', ''off'');' ...
                    'end;' ] };

	chanliststr = [fastif(icacomp,'Electrode(s)','Component(s)') ];
	chanlistval = fastif(icacomp, [ '1:' int2str(EEG.nbchan) ], [ '1:' int2str(size(EEG.icaweights,1)) ]);

	% assess previous rejections
	% --------------------------
	sizeman = 0;
	sizethresh = 0;
	sizetrend = 0;
	sizejp = 0;
	sizekurt = 0;
	sizespec = 0;
	if icacomp == 1
		if ~isempty(EEG.reject.rejmanual), sizeman   = length(find(EEG.reject.rejmanual)); end
		if ~isempty(EEG.reject.rejconst),  sizetrend = length(find(EEG.reject.rejconst)); end
		if ~isempty(EEG.reject.rejjp),     sizejp    = length(find(EEG.reject.rejjp)); end
		if ~isempty(EEG.reject.rejkurt),   sizekurt  = length(find(EEG.reject.rejkurt)); end
		if ~isempty(EEG.reject.rejfreq),   sizespec  = length(find(EEG.reject.rejfreq)); end
	else
		if ~isempty(EEG.reject.icarejmanual), sizeman   = length(find(EEG.reject.icarejmanual)); end
		if ~isempty(EEG.reject.icarejconst),  sizetrend = length(find(EEG.reject.icarejconst)); end
		if ~isempty(EEG.reject.icarejjp),     sizejp    = length(find(EEG.reject.icarejjp)); end
		if ~isempty(EEG.reject.icarejkurt),   sizekurt  = length(find(EEG.reject.icarejkurt)); end
		if ~isempty(EEG.reject.icarejfreq),   sizespec  = length(find(EEG.reject.icarejfreq)); end;
	end

    stdl = [0.25 1.2 0.8 1.2 0.8]; % standard line
	titl = [0.9 0.18 1.55]; % title line
    geometry = { 	[0.883 0.195 .2 .45 .4 .4] ...
					[1] titl stdl stdl stdl stdl ...
					[1] titl stdl stdl stdl  ...
					[1] titl stdl stdl stdl  ...
					[1] titl stdl stdl stdl  ...
					[1] titl stdl stdl stdl stdl ...
					[1] [1] [1] [1 1 1] [1 1 1]  ...
					[1] [1 1 1]};

	listui = {{'Style', 'text', 'string', 'Mark trials by appearance', 'fontweight', 'bold' }, ...
    {  'Style', 'pushbutton', 'string', '', 'tag', 'butmanual', ...
	   'callback', [ 'tmpcolor = uisetcolor(EEG.reject.rejmanualcol); if length(tmpcolor) ~= 1,' ...
	   'EEG.reject.rejmanualcol=tmpcolor; set(gcbo, ''backgroundcolor'', tmpcolor); end; clear tmpcolor;'] },...
	{ } { 'Style', 'pushbutton', 'string', fastif(icacomp,'Scroll Data','Scroll Acts.'), 'callback', cb_manual }, ...
	{ 'Style', 'text', 'string', 'Marked trials' }, ...
	{ 'Style', 'text', 'string', int2str(sizeman), 'tag', 'mantrial' }, ...
	{ }, ...
	... % ---------------------------------------------------------------------------
	{ 'Style', 'text', 'string', 'Find abnormal values', 'fontweight', 'bold' }, ...
    {  'Style', 'pushbutton', 'string', '', 'tag', 'butthresh',  ...
	   'callback', [ 'tmpcolor = uisetcolor(EEG.reject.rejthreshcol); if length(tmpcolor) ~= 1,' ...
	   'EEG.reject.rejthreshcol=tmpcolor; set(gcbo, ''backgroundcolor'', tmpcolor); end; clear tmpcolor;'] }, { },...
	...
	{ }, { 'Style', 'text', 'string', ['Upper limit(s) ' fastif(icacomp,'(uV)', '(std. dev.)')] }, ...
	{ 'Style', 'edit', 'string', '25', 'tag', 'threshpos' }, ...
	{ 'Style', 'text', 'string', ['Lower limit(s) ' fastif(icacomp,'(uV)', '(std. dev.)')] }, ...
	{ 'Style', 'edit', 'string', '-25', 'tag', 'threshneg' }, ...
	...
	{ }, { 'Style', 'text', 'string', 'Start time(s) (ms)' }, ...
	{ 'Style', 'edit', 'string', int2str(EEG.xmin*1000), 'tag', 'threshstart' }, ...
	{ 'Style', 'text', 'string', 'Ending time(s) (ms)' }, ...
	{ 'Style', 'edit', 'string', int2str(EEG.xmax*1000), 'tag', 'threshend' }, ...
	...
	{ }, { 'Style', 'text', 'string', chanliststr }, ...
	{ 'Style', 'edit', 'string', chanlistval, 'tag', 'threshelec' }, ...
	{ 'Style', 'text', 'string', 'Currently marked trials' }, ...
	{ 'Style', 'text', 'string', int2str(sizethresh), 'tag', 'threshtrial' }, ...
	...
	{ }, { 'Style', 'pushbutton', 'string', 'Calc / Plot', 'callback', cb_compthresh }, ...
	{ }, { },{ 'Style', 'pushbutton', 'string', 'HELP', 'callback', 'pophelp(''pop_eegthresh'');' }, ...
	... % ---------------------------------------------------------------------------
	{ }, { 'Style', 'text', 'string', 'Find abnormal trends', 'fontweight', 'bold' }, ...
    {  'Style', 'pushbutton', 'string', '', 'tag', 'buttrend', ...
	   'callback', [ 'tmpcolor = uisetcolor(EEG.reject.rejconstcol); if length(tmpcolor) ~= 1,' ...
	   'EEG.reject.rejconstcol=tmpcolor; set(gcbo, ''backgroundcolor'', tmpcolor); end; clear tmpcolor;'] }, { },...
	...
	{ }, { 'Style', 'text', 'string', ['Max slope ' fastif(icacomp, ...
           '(uV/epoch)', ...
           '(std. dev./epoch)') ] }, ...
	{ 'Style', 'edit', 'string', '50', 'tag', 'constpnts' }, ...
	{ 'Style', 'text', 'string', 'R-squared limit (0 to 1)' }, ...
	{ 'Style', 'edit', 'string', '0.3', 'tag', 'conststd'  }, ...
    ...
	{ }, { 'Style', 'text', 'string', chanliststr }, ...
	{ 'Style', 'edit', 'string', chanlistval, 'tag', 'constelec'  }, ...
	{ 'Style', 'text', 'string', 'Currently marked trials' }, ...
	{ 'Style', 'text', 'string', int2str(sizetrend), 'tag', 'consttrial'  }, ...
	...
	{ }, { 'Style', 'pushbutton', 'string', 'Calc / Plot', 'callback', cb_compconstrej }, ...
	{ }, { }, { 'Style', 'pushbutton', 'string', 'HELP', 'callback', 'pophelp(''pop_rejtrend'');' }, ...
	...  % ---------------------------------------------------------------------------
	{ }, { 'Style', 'text', 'string', 'Find improbable data', 'fontweight', 'bold' }, ...
    {  'Style', 'pushbutton', 'string', '',  'tag', 'butjp', ...
	   'callback', [ 'tmpcolor = uisetcolor(EEG.reject.rejjpcol); if length(tmpcolor) ~= 1,' ...
	   'EEG.reject.rejjpcol=tmpcolor; set(gcbo, ''backgroundcolor'', tmpcolor); end; clear tmpcolor;'] }, { },...
	...
	{ }, { 'Style', 'text', 'string', fastif(icacomp, 'Single-channel limit (std. dev.)', 'Single-comp. limit (std. dev.)') }, ...
	{ 'Style', 'edit', 'string', fastif(icacomp, '5', '20'), 'tag', 'entloc' }, ...
	{ 'Style', 'text', 'string', fastif(icacomp, 'All channels limit (std. dev.)', 'All comp. limit (std. dev.)') }, ...
	{ 'Style', 'edit', 'string', '5', 'tag', 'entglob' }, ...
    ...
	{ }, { 'Style', 'text', 'string', chanliststr }, ...
	{ 'Style', 'edit', 'string', chanlistval, 'tag', 'entelec' }, ...
	{ 'Style', 'text', 'string', 'Currently marked trials' }, ...
	{ 'Style', 'text', 'string', int2str(sizejp), 'tag', 'enttrial' }, ...
	...
	{ }, { 'Style', 'pushbutton', 'string', 'Calculate', 'callback', cb_compentcalc }, ...
	{ 'Style', 'pushbutton', 'string', 'Scroll Data', 'callback', cb_compenteeg }, ...
	{ 'Style', 'pushbutton', 'string', 'PLOT', 'callback', cb_compentplot }, ...
	{ 'Style', 'pushbutton', 'string', 'HELP', 'callback', 'pophelp(''pop_jointprob'');' }, ...
	...  % ---------------------------------------------------------------------------
	{ }, { 'Style', 'text', 'string', 'Find abnormal distributions', 'fontweight', 'bold' }, ...
    {  'Style', 'pushbutton', 'string', '', 'tag', 'butkurt', ...
	   'callback', [ 'tmpcolor = uisetcolor(EEG.reject.rejkurtcol); if length(tmpcolor) ~= 1,' ...
	   'EEG.reject.rejkurtcol=tmpcolor; set(gcbo, ''backgroundcolor'', tmpcolor); end; clear tmpcolor;'] }, { },...
	...
	{ }, { 'Style', 'text', 'string', fastif(icacomp, 'Single-channel limit (std. dev.)', 'Single-comp. limit (std. dev.)') }, ...
	{ 'Style', 'edit', 'string', fastif(icacomp, '5', '20'), 'tag', 'kurtloc' }, ...
	{ 'Style', 'text', 'string', fastif(icacomp, 'All channels limit (std. dev.)', 'All comp. limit (std. dev.)') }, ...
	{ 'Style', 'edit', 'string', '5', 'tag', 'kurtglob' }, ...
    ...
	{ }, { 'Style', 'text', 'string', chanliststr }, ...
	{ 'Style', 'edit', 'string', chanlistval, 'tag', 'kurtelec' }, ...
	{ 'Style', 'text', 'string', 'Currently marked trials' }, ...
	{ 'Style', 'text', 'string', int2str(sizekurt), 'tag', 'kurttrial' }, ...
	...
	{ }, { 'Style', 'pushbutton', 'string', 'Calculate', 'callback', cb_compkurtcalc }, ...
	{ 'Style', 'pushbutton', 'string', 'Scroll Data', 'callback', cb_compkurteeg }, ...
	{ 'Style', 'pushbutton', 'string', 'PLOT', 'callback', cb_compkurtplot }, ...
	{ 'Style', 'pushbutton', 'string', 'HELP', 'callback', 'pophelp(''pop_rejkurt'');' }, ...
	... % ---------------------------------------------------------------------------
	{ }, { 'Style', 'text', 'string', 'Find abnormal spectra (slow)', 'fontweight', 'bold' }, ...
    {  'Style', 'pushbutton', 'string', '', 'tag', 'butspec', ...
	   'callback', [ 'tmpcolor = uisetcolor(EEG.reject.rejfreqcol); if length(tmpcolor) ~= 1,' ...
	   'EEG.reject.rejfreqcol=tmpcolor; set(gcbo, ''backgroundcolor'', tmpcolor); end; clear tmpcolor;'] }, { },...
	...
	{ }, { 'Style', 'text', 'string', 'Upper limit(s) (dB)' }, ...
	{ 'Style', 'edit', 'string', '25', 'tag', 'freqpos' }, ...
	{ 'Style', 'text', 'string', 'Lower limit(s) (dB)' }, ...
	{ 'Style', 'edit', 'string', '-25', 'tag', 'freqneg' }, ...
	...
	{ }, { 'Style', 'text', 'string', 'Low frequency(s) (Hz)' }, ...
	{ 'Style', 'edit', 'string', '0', 'tag', 'freqstart' }, ...
	{ 'Style', 'text', 'string', 'High frequency(s) (Hz)' }, ...
	{ 'Style', 'edit', 'string', '50', 'tag', 'freqend' }, ...
	...
	{ }, { 'Style', 'text', 'string', chanliststr }, ...
	{ 'Style', 'edit', 'string', chanlistval, 'tag', 'freqelec' }, ...
	{ 'Style', 'text', 'string', 'Currently marked trials' }, ...
	{ 'Style', 'text', 'string', int2str(sizespec), 'tag', 'freqtrial' }, ...
	...
	{ }, { 'Style', 'pushbutton', 'string', 'Calc / Plot', 'callback', cb_compfreq }, ...
	{ }, { },{ 'Style', 'pushbutton', 'string', 'HELP', 'callback', 'pophelp(''pop_rejspec'');' }, ...
    ...
    {}, ...  % ---------------------------------------------------------------------------
	...
	{ 'Style', 'text', 'string', 'Plotting options', 'fontweight', 'bold' }, ...
	{ 'style', 'listbox', lisboxoptions{:}, 'value', 3 }, ...
	{ 'style', 'checkbox', 'String', 'Abnormal appearance', 'tag', 'IManual', 'value', 1, ...
      'callback', 'set(gcbo, ''value'', 1); disp(''This checkbox must be set so that you may mark/unmark data epochs'');'}, ...
	{ 'style', 'checkbox', 'String', 'Abnormal values', 'tag', 'IThresh', 'value', 1}, ...
	{ 'style', 'checkbox', 'String', 'Abnormal trends', 'tag', 'IConst', 'value', 1}, ...
	{ 'style', 'checkbox', 'String', 'Improbable epochs', 'tag', 'IEnt', 'value', 1}, ...
	{ 'style', 'checkbox', 'String', 'Abnormal distributions', 'tag', 'IKurt', 'value', 1}, ...
	{ 'style', 'checkbox', 'String', 'Abnormal spectra', 'tag', 'IFreq', 'value', 1}, ...
	...
	{ }, ...
 	{ 'Style', 'pushbutton', 'string', 'CLOSE (KEEP MARKS)', 'callback', cb_close }, ...
 	{ 'Style', 'pushbutton', 'string', 'CLEAR ALL MARKS', 'callback', cb_clear  }, ...
	{ 'Style', 'pushbutton', 'string', 'REJECT MARKED TRIALS', 'callback', cb_reject }};

	allh = supergui( gcf, geometry, [], listui{:});

%	{ 'style', 'checkbox', 'String', ['Include ' fastif(icacomp, 'ica data', 'raw data')], 'tag', 'IOthertype', 'value', 1}, { }, ...
set(gcf, 'userdata', { allh });
set(findobj('parent', gcf', 'tag', 'butmanual'), 'backgroundcolor', EEG.reject.rejmanualcol);
set(findobj('parent', gcf', 'tag', 'butthresh'), 'backgroundcolor', EEG.reject.rejthreshcol);
set(findobj('parent', gcf', 'tag', 'buttrend'),  'backgroundcolor', EEG.reject.rejconstcol);
set(findobj('parent', gcf', 'tag', 'butjp'),     'backgroundcolor', EEG.reject.rejjpcol);
set(findobj('parent', gcf', 'tag', 'butkurt'),   'backgroundcolor', EEG.reject.rejkurtcol);
set(findobj('parent', gcf', 'tag', 'butspec'),   'backgroundcolor', EEG.reject.rejfreqcol);
set( findobj('parent', gcf, 'tag', 'rejstatus'), 'style', 'popup');


function cnn_compute_image_sizes( expidx, image_set )

p = exp_params(expidx);

% load annolist
if strcmp(image_set, 'test')
    load(p.testGT)
else
    load(p.trainGT)
end

num_images = length(annolist);

sizes = zeros(num_images, 2);
for i = 1:num_images
    tic_toc_print('imdb: %d/%d\n', i, num_images);
    info = imfinfo(annolist(i).image.name);
    sizes(i, :) = [info.Height info.Width];
end

max(sizes, [], 1)

data_dir = fullfile(p.expDir, p.shortName, 'data');
mkdir_if_missing(data_dir);
save(fullfile(data_dir, ['sizes_' image_set]), 'sizes');

end


function y = linef(lambda, fn, x, d, varargin)
%LINEF	Calculate function value along a line.
%
%	Description
%	LINEF(LAMBDA, FN, X, D) calculates the value of the function FN at
%	the point X+LAMBDA*D.  Here X is a row vector and LAMBDA is a scalar.
%
%	LINEF(LAMBDA, FN, X, D, P1, P2, ...) allows additional arguments to
%	be passed to FN().   This function is used for convenience in some of
%	the optimisation routines.
%
%	See also
%	GRADCHEK, LINEMIN
%

%	Copyright (c) Ian T Nabney (1996-2001)

% Check function string
fn = fcnchk(fn, length(varargin));

y = feval(fn, x+lambda.*d, varargin{:});


function M = Vec(M)
M = M(:);
end


function [hmm,fehist,feterms,rho] = hmmstrain(Xin,T,hmm,info,options)
% Stochastic HMM variational inference
%
% INPUTS
% Xin: cell with strings referring to the files containing each subject's data,
%       or cell with with matrices (time points x channels) with each
%       subject's data
% T: cell of vectors, where each element has the length of each trial per
%       subject. Dimension of T{n} has to be (1 x nTrials)
% options: HMM options for both the subject and the group runs
%
% Diego Vidaurre, OHBA, University of Oxford (2016)

N = length(Xin); K = length(hmm.state);
X = loadfile(Xin{1},T{1},options); ndim = size(X,2); XW = [];
orders = formorders(hmm.train.order,hmm.train.orderoffset,hmm.train.timelag,hmm.train.exptimelag);
pcapred = hmm.train.pcapred>0;
p = hmm.train.lowrank; do_HMM_pca = (p > 0); rangeK = 1:K;
if pcapred
    npred = hmm.train.pcapred;
else
    if isfield(hmm.train,'B') && ~isempty(hmm.train.B), Q = size(hmm.train.B,2);
    else Q = ndim;
    end
    npred = length(orders)*Q;
end
subjfe = info.subjfe;
loglik = info.loglik;
statekl = info.statekl;
Dir2d_alpha = info.Dir2d_alpha;
Dir_alpha = info.Dir_alpha;
fehist = info.fehist;

hmm_best = hmm;
% Dir2d_alpha_best = Dir2d_alpha;
% Dir_alpha_best = Dir_alpha;
cyc_best = 1;
tp_less = max(hmm.train.embeddedlags) + max(-hmm.train.embeddedlags);

clear info;

% init stochastic learning stuff
nUsed = zeros(1,N);
sampling_weights = options.BIGbase_weights;
undertol = 0; count = 0;
Tfactor = N/options.BIGNbatch;
if options.downsample > 0
    downs_ratio = (options.downsample/options.Fs);
else
    downs_ratio = 1;
end

% Stochastic learning
for cycle = 2:options.BIGcyc

    % sampling batch
    I = datasample(1:N,options.BIGNbatch,'Replace',false,'Weights',sampling_weights);
    nUsed(I) = nUsed(I) + 1;
    nUsed = nUsed - min(nUsed) + 1;
    sampling_weights = options.BIGbase_weights.^nUsed;

    % read data for this batch
    Tbatch = []; Tbatch_list = cell(length(I),1);
    for ii = 1:length(I)
        i = I(ii);
        Tbatch = [Tbatch; ceil(downs_ratio*(T{i}-tp_less)) ];
        Tbatch_list{ii} = ceil(downs_ratio*(T{i}-tp_less));
    end

    [X,XX,Y] = loadfile(Xin(I),T(I),options);
    if hmm.train.lowrank > 0,  XX = X; end

    % local parameters (Gamma, Xi, P, Pi, Dir2d_alpha and Dir_alpha),
    % and free energy relative these local parameters
    tacc = 0; t2acc = 0;
    Gamma = cell(options.BIGNbatch,1); Xi = cell(options.BIGNbatch,1);
    XXGXX = cell(K,1);
    for k = 1:K, XXGXX{k} = zeros(size(XX,2)); end
    for ii = 1:length(I)
        i = I(ii);
        t = (1:sum(Tbatch_list{ii})) + tacc;
        t2 = (1:(sum(Tbatch_list{ii})-length(Tbatch_list{ii})*options.order)) + t2acc;
        tacc = tacc + sum(Tbatch_list{ii});
        t2acc = t2acc + sum(Tbatch_list{ii}) - length(Tbatch_list{ii})*options.order;
        data = struct('X',X(t,:),'C',NaN(sum(Tbatch_list{ii})-length(Tbatch_list{ii})*options.order,K));
        XX_i = XX(t2,:); Y_i = Y(t2,:);
        hmm_i = hmm;
        [Gamma{ii},~,Xi{ii}] = hsinference(data,Tbatch_list{ii},hmm_i,Y_i,[],XX_i); % state time courses
        checkGamma(Gamma{ii},Tbatch_list{ii},hmm_i.train,i);
        if isempty(Xi{ii}) % id_mixture
            Xi{ii} = approximateXi(Gamma{ii},Tbatch_list{ii},hmm_i);
        end
        for k = 1:K
            XXGXX{k} = XXGXX{k} + (XX_i' .* repmat(Gamma{ii}(:,k)',size(XX_i,2),1)) * XX_i;
        end
        % update transition probabilities
        Dir_alpha(:,i) = 0;
        for trial = 1:length(Tbatch_list{ii})
            t3 = sum(Tbatch_list{ii}(1:trial-1)) - options.order*(trial-1) + 1;
            Dir_alpha(:,i) = Dir_alpha(:,i) + Gamma{ii}(t3,:)';
        end
        Dir2d_alpha(:,:,i) = squeeze(sum(Xi{ii},1));
    end
    %if length(unique(hmm.train.grouping))==1
    %    hmm.Dir_alpha = sum(Dir_alpha,2)' + hmm.prior.Dir_alpha;
    %    hmm.Dir2d_alpha = sum(Dir2d_alpha,3) + hmm.prior.Dir2d_alpha;
    %else
    %    g = unique(hmm.train.grouping);
    %    for i = 1:length(g)
    %        hmm.Dir_alpha(:,i) = sum(Dir_alpha(:,hmm.train.grouping==g(i)),2) + hmm.prior.Dir_alpha';
    %        hmm.Dir2d_alpha(:,:,i) = sum(Dir2d_alpha(:,:,hmm.train.grouping==g(i)),3) + hmm.prior.Dir2d_alpha;
    %    end
    %end
    hmm.Dir_alpha = sum(Dir_alpha,2)' + hmm.prior.Dir_alpha;
    hmm.Dir2d_alpha = sum(Dir2d_alpha,3) + hmm.prior.Dir2d_alpha;
    [hmm.P,hmm.Pi] = computePandPi(hmm.Dir_alpha,hmm.Dir2d_alpha);
    subjfe(:,3,cycle) = evalfreeenergy([],[],[],[],hmm,[],[],[0 0 0 1 0]) / N; % "shared" P/Pi KL
    for ii = 1:length(I)
        i = I(ii); % Gamma entropy&LL
        subjfe(i,1:2,cycle) = evalfreeenergy([],Tbatch_list{ii},Gamma{ii},Xi{ii},hmm,[],[],[1 0 1 0 0]);
    end

    % global parameters (hmm), and collect state free energy
    rho(cycle) = (cycle + options.BIGdelay)^(-options.BIGforgetrate);
    Gamma = cell2mat(Gamma);
    if do_HMM_pca
        hmm_noisy = updatePCAparam (hmm,sum(Gamma),XXGXX,Tfactor,rangeK);
        hmm = states_supdate(hmm,hmm_noisy,rho(cycle),[1 2]);
    else
        % W
        if isfield(hmm.state(1),'W') && ~isempty(hmm.state(1).W.Mu_W)
            [hmm_noisy,XW] = updateW(hmm,Gamma,Y,XX,XXGXX,Tfactor);
            hmm = states_supdate(hmm,hmm_noisy,rho(cycle),1);
        end
        % Omega
        if isfield(hmm.state(1),'Omega') || isfield(hmm,'Omega')
            hmm_noisy = updateOmega(hmm,Gamma,Y,XX,XXGXX,XW,Tfactor);
            hmm = states_supdate(hmm,hmm_noisy,rho(cycle),2);
        end
        % Priors
        if pcapred
            % beta
            hmm_noisy = updateBeta(hmm);
            hmm = states_supdate(hmm,hmm_noisy,rho(cycle),5);
        else
            % sigma
            if ~isempty(orders)
                hmm_noisy = updateSigma(hmm);
                hmm = states_supdate(hmm,hmm_noisy,rho(cycle),3);
            end
            % alpha
            if ~isempty(orders)
                hmm_noisy = updateAlpha(hmm);
                hmm = states_supdate(hmm,hmm_noisy,rho(cycle),4);
            end
        end
    end

    % rest of the free energy (states' KL and data loglikelihood)
    [fe,ll] = evalfreeenergy(X,Tbatch,Gamma,cell2mat(Xi),hmm,Y,XX,[0 1 0 0 1]); % state KL & loglik
    statekl(cycle) = sum(fe(2:end));
    tacc = 0;
    for ii = 1:length(I)
        i = I(ii);
        t = (1:sum(Tbatch_list{ii})-length(Tbatch_list{ii})*options.order) + tacc; tacc = tacc + length(t);
        loglik(i,cycle) = sum(ll(t));
    end

    % bring from last iteration whatever was not updated
    for i = setdiff(1:N,I)
        loglik(i,cycle) = loglik(i,cycle-1);
    end

    fehist(cycle) = (-sum(loglik(:,cycle)) + statekl(cycle) + sum(sum(subjfe(:,:,cycle))));
    ch = (fehist(end)-fehist(end-1)) / abs(fehist(end)-fehist(1));
    if min(fehist)==fehist(cycle)
        hmm_best = hmm;
        cyc_best = cycle;
        %Dir2d_alpha_best = Dir2d_alpha; Dir_alpha_best = Dir_alpha;
        count = 0;
    else
        count = count + 1;
    end

    if options.BIGverbose
        fprintf('Cycle %d, free energy: %g (relative change %g), rho: %g \n', ...
            cycle,fehist(end),ch,rho(cycle));
    end

    %if hmm.train.tudamonitoring
    %    hmm.tudamonitor.synch(cycle+1,:) = getSynchronicity(Gamma,T);
    %    hmm.tudamonitor.accuracy(cycle+1,:) = getAccuracy(XX,residuals,T,Gamma,[],0);
    %end

    if cycle>5 && abs(ch) < options.BIGtol
        undertol = undertol + 1;
    else
        undertol = 0;
    end
    if cycle>options.BIGmincyc
        if undertol > options.BIGundertol_tostop, break; end
        if count >= options.BIGcycnobetter_tostop, break; end
    end

end

hmm = hmm_best;
fehist = fehist(1:cyc_best);
loglik = loglik(:,1:cyc_best);
subjfe = subjfe(:,:,1:cyc_best);
statekl = statekl(1:cyc_best);
rho = rho(1:cyc_best);

feterms = struct();
feterms.loglik = loglik;
feterms.subjfe = subjfe;
feterms.statekl = statekl;

for fn = fieldnames(options)'
    if ~isfield(hmm.train,fn{1})
        hmm.train.(fn{1}) = options.(fn{1});
    end
end

if options.BIGverbose
    if do_HMM_pca
        fprintf('Model: %d states, %d subjects, batch size %d, covariance: %s \n', ...
            K,length(T),options.BIGNbatch,hmm.train.covtype);
    else
        fprintf('Model: %d states, %d subjects, batch size %d, covariance: %s \n', ...
            K,length(T),options.BIGNbatch,hmm.train.covtype);
    end
    if hmm.train.exptimelag>1
        fprintf('Exponential lapse: %g, order %g, offset %g \n', ...
            hmm.train.exptimelag,hmm.train.order,hmm.train.orderoffset)
    else
        fprintf('Lapse: %d, order %g, offset %g \n', ...
            hmm.train.timelag,hmm.train.order,hmm.train.orderoffset)
    end
    if hmm.train.useMEX==0
        fprintf('MEX file was not used \n')
    else
        fprintf('MEX file was used for acceleration \n')
    end
end

end


function [outexpdir] = ConvertLarvaeSpecies2Trx(inexpdir,rootoutputdir,varargin)

%% set parameters

% default parameters
trxfilestr = 'trx.mat';
moviefilestr = 'movie.wmv';
makesoftlink = true;
intrxfilestr = 'trx.mat';
inmovieformat = 'wmv';
arena_radius_mm = 60;
swapxy = false;

[trxfilestr,moviefilestr,makesoftlink,intrxfilestr,inmovieformat,arena_radius_mm,swapxy] = ...
  myparse(varargin,...
  'trxfilestr',trxfilestr,...
  'moviefilestr',moviefilestr,...
  'makesoftlink',makesoftlink,...
  'intrxfilestr',intrxfilestr,...
  'inmovieformat',inmovieformat,...
  'arena_radius_mm',arena_radius_mm,...
  'swapxy',swapxy);

%% get paths to input data

[rootdatadir,inexperiment_name] = fileparts(inexpdir); %#ok<ASGLU>

files = dir(fullfile(inexpdir,['*.',inmovieformat]));
if isempty(files),
  error('No %s movie found in %s',inmovieformat,inexpdir);
end
if numel(files) > 1,
  warning('More than one %s movie found in %s, choosing %s',inmovieformat,inexpdir,files(1).name);
  files = files(1);
end
inmoviefile = fullfile(inexpdir,files(1).name);

intrxfile = fullfile(inexpdir,intrxfilestr);
if ~exist(intrxfile,'file'),
  error('Could not find trx file %s',intrxfile);
end

%% create output directory

% make sure the root output directory exists
if ~exist(rootoutputdir,'dir'),
  mkdir(rootoutputdir);
end

% create the experiment directory
outexperiment_name = inexperiment_name;
outexpdir = fullfile(rootoutputdir,outexperiment_name);
if ~exist(outexpdir,'dir'),
  mkdir(outexpdir);
end

%% load in trx data
%
% load(intrxfile,'trx');
% nlarvae = numel(trx); %#ok<NODEF>

%% get arena location
%
% arena = full(trx(1).arena);
% stats = regionprops(arena,'centroid','majoraxislength');
% arena_center_x = stats.Centroid(1);
% arena_center_y = stats.Centroid(2);
% arena_radius = stats.MajorAxisLength/2;

%% swap x and y if nec
%
% if swapxy,
%   for i = 1:nlarvae,
%     tmp = trx(i).x;
%     trx(i).x = trx(i).y; %#ok<*AGROW>
%     trx(i).y = tmp;
%     isdata = ~cellfun(@isempty,trx(i).spine);
%     trx(i).spine(isdata) = cellfun(@(x) x(:,[2,1]),trx(i).spine(isdata),'UniformOutput',false);
%     trx(i).contour = cellfun(@(x) x(:,[2,1]),trx(i).contour,'UniformOutput',false);
%   end
% end

%% remove extra stuff at the end of fields

% fns = {'x','y','a','b','theta','area'};
% for i = 1:numel(trx),
%   isdiscrepency = false;
%   for j = 1:numel(fns),
%     fn = fns{j};
%     if numel(trx(i).(fn)) ~= trx(i).nframes,
%       if ~isdiscrepency,
%         fprintf('Warning: for larva %d, size of fields and nframes = %d, endframe=%d - firstframe=%d -> %d do not match: ',...
%           i,trx(i).nframes,trx(i).lastframe,trx(i).firstframe,trx(i).lastframe-trx(i).firstframe+1);
%         isdiscrepency = true;
%       end
%       fprintf('|%s|=%d ',fn,numel(trx(i).(fn)));
%       trx(i).(fn) = trx(i).(fn)(1:trx(i).nframes); %#ok<AGROW>
%     end
%   end
%   if isdiscrepency,
%     fprintf('\n');
%   end
% end

%% convert to mm

% pxpermm = arena_radius / arena_radius_mm;
%
% for i = 1:nlarvae,
%   trx(i).x_mm = (trx(i).x - arena_center_x) / pxpermm;
%   trx(i).y_mm = (trx(i).y - arena_center_y) / pxpermm;
%   trx(i).a_mm = trx(i).a / pxpermm;
%   trx(i).b_mm = trx(i).b / pxpermm;
%   trx(i).theta_mm = trx(i).theta;
%   trx(i).pxpermm = pxpermm;
%   % convert spines to matrix
%   if i == 1,
%     nspinepts = max(cellfun(@(x) size(x,1),trx(i).spine));
%   end
%   trx(i).xspine = nan(nspinepts,trx(i).nframes);
%   trx(i).yspine = nan(nspinepts,trx(i).nframes);
%   isdata = ~cellfun(@isempty,trx(i).spine);
%   trx(i).xspine(:,isdata) = cell2mat(cellfun(@(x) x(:,1),trx(i).spine(isdata),'UniformOutput',false));
%   trx(i).yspine(:,isdata) = cell2mat(cellfun(@(x) x(:,2),trx(i).spine(isdata),'UniformOutput',false));
%   trx(i).xspine_mm = (trx(i).xspine - arena_center_x) / pxpermm;
%   trx(i).yspine_mm = (trx(i).yspine - arena_center_y) / pxpermm;
%
%   trx(i).xcontour = cellfun(@(x) x(:,1),trx(i).contour,'UniformOutput',false);
%   trx(i).ycontour = cellfun(@(x) x(:,2),trx(i).contour,'UniformOutput',false);
%   trx(i).xcontour_mm = cellfun(@(x) (x - arena_center_x)/pxpermm,trx(i).xcontour,'UniformOutput',false);
%   trx(i).ycontour_mm = cellfun(@(x) (x - arena_center_y)/pxpermm,trx(i).ycontour,'UniformOutput',false);
%
%   trx(i).area_mm = trx(i).area / pxpermm^2;
%
% end
%
% %% create dt, compress arena, rename lastframe as endframe
%
% for i = 1:nlarvae,
%   trx(i).dt = repmat(1/trx(i).fps,[1,trx(i).nframes-1]);
%   trx(i).arena = struct('arena_center_mm_x',0,'arena_center_mm_y',0,'arena_radius_mm',arena_radius_mm,...
%     'arena_center_px_x',arena_center_x,'arena_center_px_y',arena_center_y,'arena_radius_px',arena_radius);
%   trx(i).endframe = trx(i).lastframe;
% end
%
% %% remove redundant fields
%
% trx = rmfield(trx,{'spine','contour','lastframe'}); %#ok<NASGU>
%
% %% create the trx file
%
% outtrxfile = fullfile(outexpdir,trxfilestr);
% save(outtrxfile,'trx');

%% copy over the movie file

outmoviefile = fullfile(outexpdir,moviefilestr);
if isunix && makesoftlink,
  if exist(outmoviefile,'file'),
    delete(outmoviefile);
  end
  cmd = sprintf('ln -s %s %s',inmoviefile,outmoviefile);
  unix(cmd);
else
  [success,msg] = copyfile(inmoviefile,outmoviefile);
  if ~success,
    error('Error copying file %s to %s: %s',inmoviefile,outmoviefile,msg);
  end
end


function newcat = excludequarries(orgcatalog,quarries)
    %this function searches with ismember every quarry defined with 'quarries'
    %(typical the variable q) in the orgcatalog (typical a) and removes
    %them in the original catalog.

    foundquars=ismember(orgcatalog,quarries,'rows');

    newcat=orgcatalog(~foundquars,:);

end


function [out1,out2,out3] = ginput4(arg1)

[out1,out2,out3] = ginput(arg1);

return;

%GINPUT Graphical input from mouse.
%   [X,Y] = GINPUT(N) gets N points from the current axes and returns
%   the X- and Y-coordinates in length N vectors X and Y.  The cursor
%   can be positioned using a mouse (or by using the Arrow Keys on some
%   systems).  Data points are entered by pressing a mouse button
%   or any key on the keyboard except carriage return, which terminates
%   the input before N points are entered.
%
%   [X,Y] = GINPUT gathers an unlimited number of points until the
%   return key is pressed.
%
%   [X,Y,BUTTON] = GINPUT(N) returns a third result, BUTTON, that
%   contains a vector of integers specifying which mouse button was
%   used (1,2,3 from left) or ASCII numbers if a key on the keyboard
%   was used.
%
%   Examples:
%       [x,y] = ginput;
%
%       [x,y] = ginput(5);
%
%       [x, y, button] = ginput(1);
%
%   See also GTEXT, UIRESTORE, UISUSPEND, WAITFORBUTTONPRESS.

%   Copyright 1984-2006 The MathWorks, Inc.
%   $Revision: 5.32.4.9 $  $Date: 2006/12/20 07:19:10 $

P = NaN*ones(16,16);
P(1:15,1:15) = 2*ones(15,15);
P(2:14,2:14) = ones(13,13);
P(3:13,3:13) = NaN*ones(11,11);
P(6:10,6:10) = 2*ones(5,5);
P(7:9,7:9) = 1*ones(3,3);

out1 = []; out2 = []; out3 = []; y = [];
c = computer;
if ~strcmp(c(1:2),'PC')
   tp = get(0,'TerminalProtocol');
else
   tp = 'micro';
end

if ~strcmp(tp,'none') && ~strcmp(tp,'x') && ~strcmp(tp,'micro'),
   if nargout == 1,
      if nargin == 1,
         out1 = trmginput(arg1);
      else
         out1 = trmginput;
      end
   elseif nargout == 2 || nargout == 0,
      if nargin == 1,
         [out1,out2] = trmginput(arg1);
      else
         [out1,out2] = trmginput;
      end
      if  nargout == 0
         out1 = [ out1 out2 ];
      end
   elseif nargout == 3,
      if nargin == 1,
         [out1,out2,out3] = trmginput(arg1);
      else
         [out1,out2,out3] = trmginput;
      end
   end
else

   fig = gcf;
   figure(gcf);

   if nargin == 0
      how_many = -1;
      b = [];
   else
      how_many = arg1;
      b = [];
      if  ischar(how_many) ...
            || size(how_many,1) ~= 1 || size(how_many,2) ~= 1 ...
            || ~(fix(how_many) == how_many) ...
            || how_many < 0
         error('MATLAB:ginput:NeedPositiveInt', 'Requires a positive integer.')
      end
      if how_many == 0
         ptr_fig = 0;
         while(ptr_fig ~= fig)
            ptr_fig = get(0,'PointerWindow');
         end
         scrn_pt = get(0,'PointerLocation');
         loc = get(fig,'Position');
         pt = [scrn_pt(1) - loc(1), scrn_pt(2) - loc(2)];
         out1 = pt(1); y = pt(2);
      elseif how_many < 0
         error('MATLAB:ginput:InvalidArgument', 'Argument must be a positive integer.')
      end
   end

   % Suspend figure functions
   state = uisuspend(fig);

   toolbar = findobj(allchild(fig),'flat','Type','uitoolbar');
   if ~isempty(toolbar)
        ptButtons = [uigettool(toolbar,'Plottools.PlottoolsOff'), ...
                     uigettool(toolbar,'Plottools.PlottoolsOn')];
        ptState = get (ptButtons,'Enable');
        set (ptButtons,'Enable','off');
   end

   %set(fig,'pointer','fullcrosshair');
   set(fig,'Pointer','custom','PointerShapeCData',P,'PointerShapeHotSpot',[8,8]);

   fig_units = get(fig,'units');
   char = 0;

   % We need to pump the event queue on unix
   % before calling WAITFORBUTTONPRESS
   drawnow

   while how_many ~= 0
      % Use no-side effect WAITFORBUTTONPRESS
      waserr = 0;
      try
	keydown = wfbp;
      catch
	waserr = 1;
      end
      if(waserr == 1)
         if(ishandle(fig))
            set(fig,'units',fig_units);
	    uirestore(state);
            error('MATLAB:ginput:Interrupted', 'Interrupted');
         else
            error('MATLAB:ginput:FigureDeletionPause', 'Interrupted by figure deletion');
         end
      end

      ptr_fig = get(0,'CurrentFigure');
      if(ptr_fig == fig)
         if keydown
            char = get(fig, 'CurrentCharacter');
            button = abs(get(fig, 'CurrentCharacter'));
            scrn_pt = get(0, 'PointerLocation');
            set(fig,'units','pixels')
            loc = get(fig, 'Position');
            % We need to compensate for an off-by-one error:
            pt = [scrn_pt(1) - loc(1) + 1, scrn_pt(2) - loc(2) + 1];
            set(fig,'CurrentPoint',pt);
         else
            button = get(fig, 'SelectionType');
            if strcmp(button,'open')
               button = 1;
            elseif strcmp(button,'normal')
               button = 1;
            elseif strcmp(button,'extend')
               button = 2;
            elseif strcmp(button,'alt')
               button = 3;
            else
               error('MATLAB:ginput:InvalidSelection', 'Invalid mouse selection.')
            end
         end
         pt = get(gca, 'CurrentPoint');

         how_many = how_many - 1;

         if(char == 13) % & how_many ~= 0)
            % if the return key was pressed, char will == 13,
            % and that's our signal to break out of here whether
            % or not we have collected all the requested data
            % points.
            % If this was an early breakout, don't include
            % the <Return> key info in the return arrays.
            % We will no longer count it if it's the last input.
            break;
         end

         out1 = [out1;pt(1,1)];
         y = [y;pt(1,2)];
         b = [b;button];
      end
   end

   uirestore(state);
   if ~isempty(toolbar) && ~isempty(ptButtons)
        set (ptButtons(1),'Enable',ptState{1});
        set (ptButtons(2),'Enable',ptState{2});
   end
   set(fig,'units',fig_units);

   if nargout > 1
      out2 = y;
      if nargout > 2
         out3 = b;
      end
   else
      out1 = [out1 y];
   end

end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function key = wfbp
%WFBP   Replacement for WAITFORBUTTONPRESS that has no side effects.

fig = gcf;
current_char = [];

% Now wait for that buttonpress, and check for error conditions
waserr = 0;
try
  h=findall(fig,'type','uimenu','accel','C');   % Disabling ^C for edit menu so the only ^C is for
  set(h,'accel','');                            % interrupting the function.
  keydown = waitforbuttonpress;
  current_char = double(get(fig,'CurrentCharacter')); % Capturing the character.
  if~isempty(current_char) && (keydown == 1)           % If the character was generated by the
	  if(current_char == 3)                       % current keypress AND is ^C, set 'waserr'to 1
		  waserr = 1;                             % so that it errors out.
	  end
  end

  set(h,'accel','C');                                 % Set back the accelerator for edit menu.
catch
  waserr = 1;
end
drawnow;
if(waserr == 1)
   set(h,'accel','C');                                % Set back the accelerator if it errored out.
   error('MATLAB:ginput:Interrupted', 'Interrupted');
end

if nargout>0, key = keydown; end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


function test_suite=test_moxunit_util_platform_version()
    try % assignment of 'localfunctions' is necessary in Matlab >= 2016
        test_functions=localfunctions();
    catch % no problem; early Matlab versions can use initTestSuite fine
    end
    initTestSuite;

function test_platform_version
    % get start of version
    v=moxunit_util_platform_version();
    assert(isnumeric(v));
    assert(numel(v)>=2);
    version_start=sprintf('%d.%d',v(1:2));

    % now compare with output from 'ver'
    if moxunit_util_platform_is_octave()
        name='Octave';
    else
        name='MATLAB';
    end

    version_struct=ver(name);
    version_s=version_struct.Version;
    n=numel(version_start);

    assertEqual(version_s(1:n),version_start);

function v=dots_to_vec(s)
    parts=regexp(s,'\.','split');
    v=cellfun(@str2num,parts);


function [event] = read_trigger(filename, varargin)

% READ_TRIGGER extracts the events from a continuous trigger channel
% This function is a helper function to read_event and can be used for all
% dataformats that have one or multiple continuously sampled TTL channels
% in the data.
%
% The optional trigshift (default is 0) causes the value of the
% trigger to be obtained from a sample that is shifted N samples away
% from the actual flank.
%
% This is a helper function for READ_EVENT
%
% TODO
%  - merge read_ctf_trigger into this function (requires trigshift and bitmasking option)
%  - merge biosemi code into this function (requires bitmasking option)

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

event = [];

% get the optional input arguments
hdr         = keyval('header',      varargin);
dataformat  = keyval('dataformat',  varargin);
begsample   = keyval('begsample',   varargin);
endsample   = keyval('endsample',   varargin);
chanindx    = keyval('chanindx',    varargin);
detectflank = keyval('detectflank', varargin);
denoise     = keyval('denoise',     varargin); if isempty(denoise),     denoise = 1;      end
trigshift   = keyval('trigshift',   varargin); if isempty(trigshift),   trigshift = 0;    end
trigpadding = keyval('trigpadding', varargin); if isempty(trigpadding), trigpadding = 1;  end
fixctf      = keyval('fixctf',      varargin); if isempty(fixctf),      fixctf = 0;       end
fixneuromag = keyval('fixneuromag', varargin); if isempty(fixneuromag), fixneuromag = 0;  end
fix4dglasgow= keyval('fix4dglasgow', varargin); if isempty(fix4dglasgow), fix4dglasgow = 0; end

if isempty(hdr)
  hdr = ft_read_header(filename);
end

if isempty(begsample)
  begsample = 1;
end

if isempty(endsample)
  endsample = hdr.nSamples*hdr.nTrials;
end

% read the trigger channel as raw data, can safely assume that it is continuous
dat = ft_read_data(filename, 'header', hdr, 'dataformat', dataformat, 'begsample', begsample, 'endsample', endsample, 'chanindx', chanindx, 'checkboundary', 0);

if isempty(dat)
  % there are no triggers to detect
  return
end

% Detect situations where the channel value changes almost at every time
% step which are likely to be noise
if denoise
  for i=1:length(chanindx)
    if (sum(diff(find(diff(dat(i,:))~=0)) == 1)/length(dat(i,:))) > 0.8
      warning(['trigger channel ' hdr.label{chanindx(i)} ' looks like noise and will be ignored']);
      dat(i,:) = 0;
    end
  end
end

if fixctf
  % correct for reading the data as signed 32-bit integer, whereas it should be interpreted as an unsigned int
  dat(dat<0) = dat(dat<0) + 2^32;
end

if fixneuromag
  % according to Joachim Gross, real events always have triggers > 5
  % this is probably to avoid the noisefloor
  dat(dat<5) = 0;
end

if fix4dglasgow
  % synchronization pulses have a value of 8192 and are set to 0
  dat = dat - bitand(dat, 8192);
  %% triggers containing the first bit assume a value of 4096 when sent by presentation
  %% this does not seem to hold for matlab; check this
  %dat = dat - bitand(dat, 4096)*4095/4096;
end

for i=1:length(chanindx)
  % process each trigger channel independently
  channel = hdr.label{chanindx(i)};
  trig    = dat(i,:);

  if trigpadding
    pad = trig(1);
  else
    pad = 0;
  end

  switch detectflank
    case 'up'
      % convert the trigger into an event with a value at a specific sample
      for j=find(diff([pad trig(:)'])>0)
        event(end+1).type   = channel;
        event(end  ).sample = j + begsample - 1;      % assign the sample at which the trigger has gone down
        event(end  ).value  = trig(j+trigshift);      % assign the trigger value just _after_ going up
      end
    case 'down'
      % convert the trigger into an event with a value at a specific sample
      for j=find(diff([pad trig(:)'])<0)
        event(end+1).type   = channel;
        event(end  ).sample = j + begsample - 1;      % assign the sample at which the trigger has gone down
        event(end  ).value  = trig(j-1-trigshift);    % assign the trigger value just _before_ going down
      end
    case 'both'
      % convert the trigger into an event with a value at a specific sample
      for j=find(diff([pad trig(:)'])>0)
        event(end+1).type   = [channel '_up'];        % distinguish between up and down flank
        event(end  ).sample = j + begsample - 1;      % assign the sample at which the trigger has gone down
        event(end  ).value  = trig(j+trigshift);      % assign the trigger value just _after_ going up
      end
      % convert the trigger into an event with a value at a specific sample
      for j=find(diff([pad trig(:)'])<0)
        event(end+1).type   = [channel '_down'];      % distinguish between up and down flank
        event(end  ).sample = j + begsample - 1;      % assign the sample at which the trigger has gone down
        event(end  ).value  = trig(j-1-trigshift);    % assign the trigger value just _before_ going down
      end
    otherwise
      error('incorrect specification of ''detectflank''');
  end
end


function test_ft_timelockanalysis(datainfo, writeflag, version)

% MEM 2gb
% WALLTIME 00:10:00
% DEPENDENCY

% ft_timelockanalysis ref_datasets

% writeflag determines whether the output should be saved to disk
% version determines the output directory

if nargin<1 || isempty(datainfo)
  datainfo = ref_datasets;
end
if nargin<2 || isempty(writeflag)
  writeflag = 0;
end
if nargin<3
  version = 'latest';
end

for k = 1:numel(datainfo)
  disp(['now you are in k=' num2str(k)]);

  datanew = timelockanalysis10trials(datainfo(k), writeflag, version, 'yes', 'yes');
  fname   = fullfile(datainfo(k).origdir,version,'timelock',datainfo(k).type,['timelock_cov_trl_',datainfo(k).datatype]);
  comparedata(datanew, fname);

  datanew = timelockanalysis10trials(datainfo(k), writeflag, version, 'yes', 'no');
  fname   = fullfile(datainfo(k).origdir,version,'timelock',datainfo(k).type,['timelock_cov_',datainfo(k).datatype]);
  comparedata(datanew, fname);

  datanew = timelockanalysis10trials(datainfo(k), writeflag, version, 'no', 'yes');
  fname   = fullfile(datainfo(k).origdir,version,'timelock',datainfo(k).type,['timelock_trl_',datainfo(k).datatype]);
  comparedata(datanew, fname);

  datanew = timelockanalysis10trials(datainfo(k), writeflag, version, 'no', 'no'); % should be the latest
  fname   = fullfile(datainfo(k).origdir,version,'timelock',datainfo(k).type,['timelock_',datainfo(k).datatype]);
  comparedata(datanew, fname);

end

function [timelock] = timelockanalysis10trials(dataset, writeflag, version, covariance, keeptrials)

% --- HISTORICAL --- attempt forward compatibility with function handles
if ~exist('ft_timelockanalysis') && exist('timelockanalysis')
  eval('ft_timelockanalysis = @timelockanalysis;');
end

if isempty(covariance)
  covariance = 'no';
  % covariance = 'yes';
end

if isempty(keeptrials)
  keeptrials = 'no';
  % keeptrials = 'yes';
end

% the file names should distinguish between the cfg.covariance and cfg.keeptrials option
postfix = '';
switch covariance
  case 'no'
    % don't change
  case 'yes'
    postfix = [postfix 'cov_'];
  otherwise
    error('unexpected keeptrials');
end

% the file names should distinguish between the cfg.covariance and cfg.keeptrials option
switch keeptrials
  case 'no'
    % don't change
  case 'yes'
    postfix = [postfix 'trl_'];
  otherwise
    error('unexpected keeptrials');
end

cfg = [];
cfg.keeptrials = keeptrials;
cfg.covariance = covariance;
cfg.inputfile  = fullfile(dataset.origdir,version,'raw',dataset.type,['preproc_',dataset.datatype]);
if writeflag
  cfg.outputfile = fullfile(dataset.origdir,version,'timelock',dataset.type,['timelock_',postfix,dataset.datatype]);
end

if ~strcmp(version, 'latest') && str2num(version)<20100000
  % -- HISTORICAL --- older FieldTrip versions don't support inputfile and outputfile
  load(cfg.inputfile, 'data');
  timelock = ft_timelockanalysis(cfg, data);
  save(cfg.outputfile, 'timelock');
else
  timelock = ft_timelockanalysis(cfg);
end

function comparedata(datanew, fname)

tmp = load(fname);
if isfield(tmp, 'data')
  data = tmp.data;
elseif isfield(tmp, 'datanew')
  data = tmp.datanew;
elseif isfield(tmp, 'timelock')
  data = tmp.timelock;
end

try
  % these are sensitive to numerical erorrs, especially for MEG data and when the covariance is close to zero
  datanew.cov = trimnumericalerror(datanew.cov, eps*1e3);
  data.cov    = trimnumericalerror(data.cov   , eps*1e3);
end

datanew = removefields(datanew, 'cfg'); % these are per construction different if writeflag = 0
data    = removefields(data,    'cfg');
[ok,msg] = isalmostequal(data, datanew, 'reltol', eps*1e8);
if ~ok
  error('stored and computed data not identical: %s', msg{:});
end


function ExchangeRxnMatrix = createXMatrix(compoundsIn, transport, compartment)
% Creates a matrix full of exchange reactions based
% on the input list (creates an exchange reaction for each of the
% metabolites present in the model)
%
% USAGE:
%
%    ExchangeRxnMatrix = createXMatrix(compoundsIn, transport, compartment)
%
% INPUTS:
%    compoundsIn:         SU matrix
%    transport:           if 1, transport reactions will be defined as well for
%                         every compound (default: 0, which corresponds to only
%                         exchange reactions (from the specified compartment)
%    compartment:         (default = '[c]') --> transport from cytoplasm [c] to
%                         extracellular space [e] (or sink)
%                         any other compartment (except '[e]' creates transport from [c] to
%                         the compartment, from the compartment to the external compartment
%                         and an exchanger in the external compartment.
% OUTPUT:
%   ExchangeRxnMatrix:    model containing all exchange reactions for all
%                         compounds in compoundsIn
%
% .. Author: - IT 11-10-07
%    Modifications TP October 2017

if ~exist('transport','var') || isempty(transport)
    transport = 0;
end
if ~exist('compartment','var') || isempty(compartment)
    compartment = '[c]';
else
    if strcmp(compartment,'[e]')
        error('Cannot use the external compartment as target compartment');
    end
end

%Bring the compounds in the right dimension.
compoundsIn = columnVector(compoundsIn);

showprogress(0,'Exchange reaction list ...');

ExchangeRxnMatrix = createModel;

cnt=1;
HTABLE = java.util.Hashtable;

%removes the compartment from compound name
compoundsIn=regexprep(compoundsIn,'(\w*)]','');
compoundsIn=regexprep(compoundsIn,'[','');
compoundsIn=regexprep(compoundsIn,']','');
compoundsIn=regexprep(compoundsIn,' ','');

compounds=unique(compoundsIn);

%creates sparse matrix with corresponding dimensions
if  transport==0
    ExchangeRxnMatrix.S=spalloc(length(compounds),length(compounds),length(compounds));
    %Create one sink reaction per input metabolit = 1 S entry, one model
    %metabolite and one reaction per input metabolite
elseif transport == 1
    if (strcmp(compartment,'[c]')==1)
        ExchangeRxnMatrix.S=spalloc(2 * length(compounds),2*length(compounds),3*length(compounds));
        %This has 1 transporter and 1 Exchanger = 3 non zero entries per input metabolite.
        %2 model metabolites per input metabolites and 2 reactions per
        %input metabolite.
    else
        ExchangeRxnMatrix.S=spalloc(3 * length(compounds),3 * length(compounds),5*length(compounds));
        %Generate 2 transporters and 1 Exchanger = 5 non zero entries per input metabolite.
        %3 model metabolites (c,e,comp) per input metabolites and 3 reactions per
        %input metabolite.
    end
end

ExchangeRxnMatrix.mets=compounds;
for i=1:length(compounds)
    HTABLE.put(compounds{i}, i);
end

for i=1:length(compounds)
    if ~isempty(compounds(i))
        if transport == 0
            tmp = ['sink_' compounds(i) compartment];
            ExchangeRxnMatrix.rxns(cnt,1) = strcat(tmp(1),tmp(2),tmp(3));
            tmp = ['sink for ' compounds(i)];
            ExchangeRxnMatrix.rxnsNames(cnt,1) =   strcat(tmp(1),tmp(2));
            tmp = ['1 ' compounds(i) compartment ' <==>'];
            ExchangeRxnMatrix.rxnFormulas(cnt,1) =  strcat(tmp(1),tmp(2),tmp(3));
            tmp = [compounds(i) compartment];
            ExchangeRxnMatrix.mets(i) = strcat(tmp(1),tmp(2));
            %   ExchangeRxnMatrix.grRules{cnt}='';
            [ExchangeRxnMatrix] = addReactionGEM(ExchangeRxnMatrix,ExchangeRxnMatrix.rxns(cnt,1),ExchangeRxnMatrix.rxnsNames(cnt,1),ExchangeRxnMatrix.rxnFormulas(cnt,1),1,-10000,10000);
            cnt = cnt + 1;

        elseif transport == 1 %currently only this branch is taken.
            tmp = ['Ex_' compounds(i) '[e]'];
            ExchangeRxnMatrix.rxns(cnt,1) = strcat(tmp(1),tmp(2),tmp(3));
            tmp = ['Exchange of ' compounds(i)];
            ExchangeRxnMatrix.rxnsNames(cnt,1) =   strcat(tmp(1),tmp(2));
            tmp = ['1 ' compounds(i) '[e] <==>'];
            ExchangeRxnMatrix.rxnFormulas(cnt,1) =  strcat(tmp(1),tmp(2),tmp(3));
            tmp = [compounds(i) '[e]'];
            ExchangeRxnMatrix.mets(i) = strcat(tmp(1),tmp(2));
            HTABLE.put(ExchangeRxnMatrix.mets{i}, i);
            %   ExchangeRxnMatrix.grRules{cnt}='';
            [ExchangeRxnMatrix, HTABLE] = addReactionGEM(ExchangeRxnMatrix,ExchangeRxnMatrix.rxns(cnt,1),ExchangeRxnMatrix.rxnsNames(cnt,1),ExchangeRxnMatrix.rxnFormulas(cnt,1),1,-10000,10000,[],[],[],[],[], HTABLE);
            cnt = cnt + 1;

            if (strcmp(compartment,'[c]')==1)
                % creates transport reaction from [c] to [e]
                tmp = [compounds{i} 'tr'];
                ExchangeRxnMatrix.rxns{cnt,1} = tmp;
                tmp = ['Transport of ' compounds{i}];
                ExchangeRxnMatrix.rxnsNames{cnt,1} =   tmp;
                tmp = ['1 ' compounds{i} '[e] <==> 1 ' compounds{i} '[c]'];
                ExchangeRxnMatrix.rxnFormulas{cnt,1} =  tmp;
                tmp = [compounds{i} '[c]'];
                ExchangeRxnMatrix.mets{length(ExchangeRxnMatrix.mets)+1,1} = tmp;
                HTABLE.put(ExchangeRxnMatrix.mets{end}, length(ExchangeRxnMatrix.mets));
                %  ExchangeRxnMatrix.grRules{cnt}='';
                [ExchangeRxnMatrix, HTABLE] = addReactionGEM(ExchangeRxnMatrix,ExchangeRxnMatrix.rxns(cnt,1),ExchangeRxnMatrix.rxnsNames(cnt,1),ExchangeRxnMatrix.rxnFormulas(cnt,1),1,-10000,10000,[],[],[],[],[], HTABLE);
                cnt = cnt + 1;

            else % keep this branch the same for now.
                % creates transport reaction from [c] to [p]
                tmp = [compounds{i} 't' compartment(2:end-1) 'r'];
                ExchangeRxnMatrix.rxns{cnt,1} = tmp;
                tmp = ['[c] to ' compartment ' Transport of ' compounds{i}];
                ExchangeRxnMatrix.rxnsNames{cnt,1} =   tmp;
                tmp = ['1 ' compounds{i} compartment ' <==> 1 ' compounds{i} '[c]'];
                ExchangeRxnMatrix.rxnFormulas{cnt,1} =  tmp;
                tmp = [compounds{i} '[c]'];
                ExchangeRxnMatrix.mets{length(ExchangeRxnMatrix.mets)+1,1} = tmp;
                tmp = [compounds{i} compartment];
                ExchangeRxnMatrix.mets{length(ExchangeRxnMatrix.mets)+1,1} = tmp;
                %  ExchangeRxnMatrix.grRules{cnt}='';
                [ExchangeRxnMatrix] = addReactionGEM(ExchangeRxnMatrix,ExchangeRxnMatrix.rxns(cnt,1),ExchangeRxnMatrix.rxnsNames(cnt,1),ExchangeRxnMatrix.rxnFormulas(cnt,1),1,-10000,10000);
                cnt = cnt + 1;

                % creates transport reaction from [p] to [e]
                tmp = [compounds{i} 'tr'];
                ExchangeRxnMatrix.rxns{cnt,1} = tmp;
                tmp = [compartment ' to [e] Transport of ' compounds{i}];
                ExchangeRxnMatrix.rxnsNames{cnt,1} =   tmp;
                tmp = ['1 ' compounds{i} '[e] <==> 1 ' compounds{i} compartment];
                ExchangeRxnMatrix.rxnFormulas{cnt,1} =  tmp;
                %    ExchangeRxnMatrix.grRules{cnt}='';
                [ExchangeRxnMatrix] = addReactionGEM(ExchangeRxnMatrix,ExchangeRxnMatrix.rxns(cnt,1),ExchangeRxnMatrix.rxnsNames(cnt,1),ExchangeRxnMatrix.rxnFormulas(cnt,1),1,-10000,10000);
                cnt = cnt + 1;
            end
        end
    end

    showprogress(i/length(compounds));
end


function [nodes, edges, vSize] = readGrayGraph(filename,hemi)
% Read a gray graph written out by mrGray
%
%    [nodes, edges, vSize] = readGrayGraph(filename,[hemisphere='l'])
%
% AUTHOR:  Teo
% DATE:    1996
% ARGUMENT:
%   name of gray graph file written by mrGray
%
% RETURNS:
% nodes:  8xN array of
%    Nx(x,y,z,num_edges,edge_offset,layer,dist,pqindex).
% edges:  1xM array of node indices.  The edge_offset of
%    each node points into the starting location of its set
%    of edges.
% where N, M are the number of nodes, edges in the graph.
% vSize = size of the original volume of data containing the anatomicals
%
% Modified 8.20.98  WP/BW
%   Added the big-endian flag and the check for existence step.
% This was done for compatibility with the PCs that normally read
% in using little-endian.
%
% DJH, 8/4/99
%   Add 1 to coordinates because MATLAB starts indexing from 1 instead
% of 0.  This fixes a mis-registration problem in mrLoadRet in the Gray
% view.  The only effect that this has on mrUnfold is that the gLocs3d
% are now incremented by one.
% TODO: Return a timestamp that is saved in the .gray file. This will be
%       used to maintain consistency between gray and flat maps.

warning('Obsolete. Vistasoft uses nifti class files, not mrGray');

if ~exist(filename,'file')
  errordlg(sprintf('Can not find file %s\n',filename));
  % error(str);
end

[p f e] =  fileparts(filename);
if(strcmpi(f, 'coords') || strcmpi(e,'.mat'))
    % Load nodes/edges from a coords file
    if(~exist('hemi','var')||isempty(hemi))
        hemi = 'l';
    end
    tmp = load(filename);
    if(lower(hemi(1))=='l')
        nodes = tmp.allLeftNodes;
        edges = tmp.allLeftEdges;
        classFileName = tmp.leftClassFile;
    elseif(lower(hemi(1))=='r')
        nodes = tmp.allRightNodes;
        edges = tmp.allRightEdges;
        classFileName = tmp.rightClassFile;
    else
        error('Unrecognized hemifield: %s. Cannot read the gray graph', hemi);
    end
    % *** TODO: confirm that we don't need to add 1 to nodes and edges!
    if(nargout>2)
        % *** TODO: check to see if vSize should be reordered to match old
        % vAnat ordering.
        ni = niftiRead(classFileName,[]);
        vSize = ni.dim;
    end
else
    % Load an old-style .Gray file

    % mrGray on all machines writes out using ieee big endian
    % format.  So, we must always open our files that way.
    %
    fid = fopen(filename,'r','b');

    % Read xsize, ysize, zsize.
    [sizes, cnt] = fread(fid, [1 3], 'int');

    % [rows, cols, planes]; [ysize, xsize, zsize].
    vSize = [sizes(2), sizes(1), sizes(3)];

    % Read in number of nodes and edges.
    [sizes, cnt] = fread(fid, [1 2], 'int');

    % Read header.
    num_nodes = sizes(1);
    num_edges = sizes(2);

    % Read nodes.
    nodes = fread(fid, [6 num_nodes], 'int');

    % Offset coordinates by 1 because MATLAB indexes arrays
    % starting from 1
    nodes(1:3,:) = nodes(1:3,:) + 1;

    % Offset edge_offset by 1 because MATLAB implements arrays
    % with base 1 *@#!#!
    nodes(5,:) = nodes(5,:) + 1;

    % Add two additional attributes for miscellaneous use.
    % These are used by the shortest path algorithm to store
    % intermediate distance calculations and to mark position
    % in the priority queue.
    nodes = [nodes ; zeros(2,num_nodes)];

    % Read edges.
    edges = fread(fid, [1 num_edges], 'int');

    % Offset all node indices by 1 because MATLAB implements arrays
    % with base 1 *@#!#!
    edges = edges + 1;

    % Insert timestamp stuff here.

    fclose(fid);

end

return;


function varargout = panel_femcond(varargin)
% PANEL_FEMCOND Edit FEM conductivity for a list of named layers (isotropic/anisotropic).
%
% USAGE:  bstPanel = panel_femcond('CreatePanel', OPTIONS)           : Call from the interactive interface
%         bstPanel = panel_femcond('CreatePanel', sProcess, sFiles)  : Call from the process editor
%                s = panel_femcond('GetPanelContents')

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

eval(macro_method);
end

%% ===== CREATE PANEL =====
function [bstPanelNew, panelName] = CreatePanel(sProcess, sFiles) %#ok<DEFNU>
    panelName = 'FemCondOptions';
    % Java initializations
    import java.awt.*;
    import javax.swing.*;

    % GUI CALL:  panel_femcond('CreatePanel', OPTIONS)
    if (nargin == 1)
        OPTIONS = sProcess;
    % PROCESS CALL:  panel_femcond('CreatePanel', sProcess, sFiles)
    else
        OPTIONS = sProcess.options.femcond.Value;
        % Get FEM files
        sSubject = bst_get('Subject', sProcess.options.subjectname.Value);
        if isempty(sSubject.iFEM)
            error('No available FEM mesh file for this subject.');
        end
        OPTIONS.FemFile = file_fullpath(sSubject.Surface(sSubject.iFEM).FileName);
    end

    % ==== GET MESH INFO ====
    % Load tissue labels
    FemMat = load(OPTIONS.FemFile, 'TissueLabels');
    % Get default conductivities
    OPTIONS.FemNames = FemMat.TissueLabels;
    OPTIONS.FemCond = panel_duneuro('GetDefaultCondutivity', OPTIONS.FemNames);

    % ==== FRAME STRUCTURE ====
    jPanelNew = java_create('javax.swing.JPanel');
    jPanelNew.setLayout(BoxLayout(jPanelNew, BoxLayout.PAGE_AXIS));
    jPanelNew.setBorder(BorderFactory.createEmptyBorder(12,12,12,12));

    % ===== FEM LAYERS =====
    jPanelLayers = gui_river([6,6], [-5,6,15,6], 'FEM conductivities');
        nLayers = length(OPTIONS.FemNames);
        jRadioLayerIso = javaArray('javax.swing.JRadioButton', nLayers);
        jRadioLayerAniso = javaArray('javax.swing.JRadioButton', nLayers);
        jTextCond = javaArray('javax.swing.JTextField', nLayers);
        % Loop on each layer
        for i = 1:nLayers
            gui_component('label', jPanelLayers, 'br', [OPTIONS.FemNames{i} ':'], [], [], [], []);
            jTextCond(i) = gui_component('texttime', jPanelLayers, 'tab', '', [], [], [], []);
            gui_validate_text(jTextCond(i), [], [], {0.0001,1000,10000}, 'S/m', [], OPTIONS.FemCond(i), []);
            gui_component('label', jPanelLayers, 'tab', 'S/m     ', [], [], [], []);
            jGroupRadio = ButtonGroup();
            jRadioLayerIso(i) = gui_component('radio', jPanelLayers, 'tab', 'Isotropic', jGroupRadio, [], @(h,ev)UpdatePanel(), []);
            jRadioLayerAniso(i) = gui_component('radio', jPanelLayers, 'tab', 'Anisotropic', jGroupRadio, [], @(h,ev)UpdatePanel(), []);
            jRadioLayerIso(i).setSelected(1);
        end
    jPanelNew.add(jPanelLayers);

    % ===== ANISOTROPY OPTIONS =====
    jPanelAniso = gui_river([2,2], [6,6,6,6], 'Anisotropy method');
        jGroupAniso = ButtonGroup();
        jRadioMethodEma = gui_component('radio', jPanelAniso, '', 'EMA: Effective Medium Approach (k=0.736) (DTI)
[Rullmann et al., 2009, Tuch et al., 1999, Haueisen et al., 2002]', jGroupAniso, [], @(h,ev)UpdatePanel(), []);
        jRadioMethodEmaVc = gui_component('radio', jPanelAniso, 'br', 'EMA+VC: EMA with volume constraint (DTI)
[Rullmann et al., 2009, Vorwerk et al., 2014]', jGroupAniso, [], @(h,ev)UpdatePanel(), []);
        jRadioMethodSim = gui_component('radio', jPanelAniso, 'br', 'Simulated: Artificial anisotropy (no DTI)', jGroupAniso, [], @(h,ev)UpdatePanel(), []);
        jRadioMethodEma.setSelected(1);
    jPanelNew.add(jPanelAniso);

    % ===== SIMULATED OPTIONS =====
    jPanelSim = gui_river([2,2], [6,6,6,6], 'Simulated anisotropy');
        % Ratio
        jLabelRatio = gui_component('label', jPanelSim, '', 'Ratio longitudinal/transversal: ', [], [], [], []);
        SimRatio = 10;
        jTextSimRatio = gui_component('texttime', jPanelSim, '', '', [], [], [], []);
        gui_validate_text(jTextSimRatio, [], [], {0.0001,100,100}, '', 2, SimRatio, []);
        % Constraint
        jGroupConstr = ButtonGroup();
        jRadioConstrWang = gui_component('radio', jPanelSim, 'br', 'Wang''s constraint (Wolters, 2003):
[sig_r*sig_t = sig_iso^2]', jGroupConstr, [], [], []);
        jRadioConstrWolters = gui_component('radio', jPanelSim, 'br', 'Volume''s constraint (Wolters, 2003):
[(4/3)*pi*(sig_r*sig_t^2) = (4/3)*pi*(sig_iso^3)]', jGroupConstr, [], [], []);
        jRadioConstrWolters.setSelected(1);
    jPanelNew.add(jPanelSim);

    % ===== VALIDATION BUTTONS =====
    jPanelValidation = gui_river([10 0], [6 10 0 10]);
        gui_component('Button', jPanelValidation, 'br right', 'Cancel', [], [], @ButtonCancel_Callback, []);
        gui_component('Button', jPanelValidation, [], 'OK', [], [], @ButtonOk_Callback, []);
    jPanelNew.add(jPanelValidation);

    % ===== PANEL CREATION =====
    % Return a mutex to wait for panel close
    bst_mutex('create', panelName);
    % Create the BstPanel object that is returned by the function
    ctrl = struct('jTextCond',           jTextCond, ...
                  'jRadioLayerIso',      jRadioLayerIso, ...
                  'jRadioLayerAniso',    jRadioLayerAniso, ...
                  'jPanelAniso',         jPanelAniso, ...
                  'jRadioMethodEma',     jRadioMethodEma, ...
                  'jRadioMethodEmaVc',   jRadioMethodEmaVc, ...
                  'jRadioMethodSim',     jRadioMethodSim, ...
                  'jPanelSim',           jPanelSim, ...
                  'jRadioConstrWang',    jRadioConstrWang, ...
                  'jRadioConstrWolters', jRadioConstrWolters, ...
                  'jLabelRatio',         jLabelRatio, ...
                  'jTextSimRatio',       jTextSimRatio);
    ctrl.FemNames = OPTIONS.FemNames;
    % Create the BstPanel object that is returned by the function
    bstPanelNew = BstPanel(panelName, jPanelNew, ctrl);
    % Redraw panel
    UpdatePanel();

%% =================================================================================
%  === LOCAL CALLBACKS  ============================================================
%  =================================================================================
    %% ===== BUTTON: CANCEL =====
    function ButtonCancel_Callback(varargin)
        % Close panel
        gui_hide(panelName);
    end

    %% ===== BUTTON: OK =====
    function ButtonOk_Callback(varargin)
        % Release mutex and keep the panel opened
        bst_mutex('release', panelName);
    end

    %% ===== UPDATE PANEL ======
    function UpdatePanel()
        % FEM Layers
        isIsotropic = false(1, nLayers);
        for j = 1:nLayers
            isIsotropic(j) = jRadioLayerIso(j).isSelected();
            % jTextCond(j).setEnabled(isIsotropic(j));
        end
        % All isotropic: disable anisotropy options
        isAllIso = all(isIsotropic);
        jPanelAniso.setVisible(~isAllIso);
        % Simulated
        isSim = ~isAllIso && jRadioMethodSim.isSelected();
        jPanelSim.setVisible(isSim);

        % Get panel
        [bstPanel iPanel] = bst_get('Panel', 'FemCondOptions');
        container = get(bstPanel, 'container');
        % Re-pack frame
        if ~isempty(container)
            jFrame = container.handle{1};
            if ~isempty(jFrame)
                jFrame.pack();
            end
        end
    end
end

%% =================================================================================
%  === EXTERNAL CALLBACKS  =========================================================
%  =================================================================================
%% ===== GET PANEL CONTENTS =====
function s = GetPanelContents() %#ok<DEFNU>
    % Get panel controls handles
    ctrl = bst_get('PanelControls', 'FemCondOptions');
    if isempty(ctrl)
        s = [];
        return;
    end
    % FEM layers
    for i = 1:length(ctrl.jTextCond)
        s.FemCond(i) = str2double(char(ctrl.jTextCond(i).getText()));
        s.isIsotropic(i) = ctrl.jRadioLayerIso(i).isSelected();
    end
    % Anisotropy options
    if ctrl.jRadioMethodEma.isSelected()
        s.AnisoMethod = 'ema';
    elseif ctrl.jRadioMethodEmaVc.isSelected()
        s.AnisoMethod = 'ema+vc';
    elseif ctrl.jRadioMethodSim.isSelected()
        s.AnisoMethod = 'simulated';
    end
    % Simulated: Ratio
    s.SimRatio = str2double(char(ctrl.jTextSimRatio.getText()));
    % Simulated: Constraint method
    if ctrl.jRadioConstrWang.isSelected()
        s.SimConstrMethod = 'wang';
    elseif ctrl.jRadioConstrWolters.isSelected()
        s.SimConstrMethod = 'wolters';
    end
end


function sb_write_materials(filename,values,labels,resolution)
%
% SB_WRITE_MATERIALS writes a .mat file (read by the simbio-vgrid mesher)
% in which each tissue compartment is assigned to an integer value
%
% Use as
%   sb_write_materials(values,labels,weights)
%
% where:
%   values are the integer values assigned to the different compartments
%   labels is a cell with the labels descibing the comartments (default: {'scalp' 'skull' 'brain'})
%   weights are the weights assigned to the different compartments (default: [1.0 1.0 1.0])
%
% $Id$

% Copyright (C) 2011, Cristiano Micheli

if nargin<3 & length(values)==3
  weights = [1. 1. 1.];
  labels  = {'skin', 'skull', 'brain'}; % it assumes this order
elseif nargin<3 & length(values)~=3
  error('You should specify the labels for models with more (or less) than 3 compartments')
end

if isempty(resolution)
  resolution = 1;
end

% open the file and write the header
try
  fid = fopen(filename, 'w');
  fprintf(fid,'%s\n','material bg 1 0 0   2   1.0');
  for i=1:numel(values)
    fprintf(fid,'%s%s %d %d %d %d %0.1f\n','material ',labels{i},resolution,values(i),values(i),1,1);
  end
  fclose(fid);
catch
  disp('Error in writing the file')
  rethrow(ME)
end


classdef SynchronizationFile < handle
    properties (Access = public)
        synchronizationPointsMap;
        fileName;
        count;%dependent property
    end

    methods
        function c = get.count(obj)
            c = obj.synchronizationPointsMap.Count;
        end
    end

    methods (Access = public)
        function obj = SynchronizationFile(samples,frames)
            obj.synchronizationPointsMap = containers.Map('KeyType','uint64','ValueType','uint32');
            if nargin > 0
                obj.synchronizationPointsMap.keys = samples;
                obj.synchronizationPointsMap.values = frames;
            end
        end

        function isValid = isValidSynchronizationFile(obj)
            isValid = (length(obj.synchronizationPointsMap.keys) >= 2);
        end

        function setSynchronizationPoint(obj,sample,frame)
            obj.synchronizationPointsMap(uint64(sample)) = uint32(frame);
        end

        function removeSynchronizationPointWithSample(obj,sample)
            obj.synchronizationPointsMap.remove(sample);
        end

        function videoFrame = sampleToVideoFrame(obj, sample)
            if ~obj.isValidSynchronizationFile()
                videoFrame = [];
            else
                [sample1, frame1] = obj.getFirstSynchronizationPoint();
                [sample2, frame2] = obj.getLastSynchronizationPoint();

                a = double(frame2 - frame1) / double(sample2 - sample1);
                videoFrame = a * (sample - double(sample1)) + double(frame1);

                if videoFrame < 1
                    videoFrame = 1;
                end

                videoFrame = uint32(videoFrame);
            end
        end

        function sample = videoFrameToSample(obj, videoFrame)
            if ~obj.isValidSynchronizationFile()
                sample = [];
            else
                [sample1, frame1] = obj.getFirstSynchronizationPoint();
                [sample2, frame2] = obj.getLastSynchronizationPoint();

                a = (double(sample2) - double(sample1)) / (double(frame2) - double(frame1));
                sample = a * (double(videoFrame) - double(frame1)) + double(sample1);

                if sample < 1
                    sample = 1;
                end

                sample = uint32(sample);
            end
        end
    end

    methods (Access = private)

        function [sample, frame] = getFirstSynchronizationPoint(obj)
            samples = obj.synchronizationPointsMap.keys;
            frames = obj.synchronizationPointsMap.values;
            if isempty(samples)
                sample = [];
                frame = [];
            else
                [~, minIdx] = min(cell2mat(samples));
                sample = samples{minIdx};
                frame = frames{minIdx};
            end
        end

        function [sample, frame] = getLastSynchronizationPoint(obj)
            samples = obj.synchronizationPointsMap.keys;
            frames = obj.synchronizationPointsMap.values;
            if isempty(samples)
                sample = [];
                frame = [];
            else
                [~, maxIdx] = max(cell2mat(samples));
                sample = samples{maxIdx};
                frame = frames{maxIdx};
            end
        end
    end
end


function failed_ft_headmodel_bemcp

% MEM 12gb
% WALLTIME 03:00:00
% DEPENDENCY ft_headmodel_localspheres ft_prepare_localspheres

% to test actual numerical output of bemcp, rather than simply successful
% running and correct inputs (which is what test_ft_prepare_headmodel tests).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% method 1: segment MRI then compute bnd from it

% read in the mri, this is already (non?)linearly aligned with MNI
mri = ft_read_mri(dccnpath('/home/common/matlab/fieldtrip/template/headmodel/standard_mri.mat'));

cfg = [];
cfg.output = {'brain', 'skull', 'scalp'};
segmentedmri_new = ft_volumesegment(cfg, mri);

cfg = [];
cfg.tissue = {'brain', 'skull', 'scalp'};
cfg.numvertices = [1500 1000 500]; % these numbers later match the precomputed bnd
mesh_new = ft_prepare_mesh(cfg, segmentedmri_new);

% I'm choosing 'mm' as units since that is what both the MRI and the
% standard_bem are in already. Whether that is optimal for 'bemcp' is not
% clear to me.

% Create simple concentricspheres to compare to BEMCP
cfg = [];
cfg.method = 'concentricspheres';
vol_new_cs = ft_prepare_headmodel(cfg, ft_convert_units(mesh_new, 'mm'));

cfg = [];
cfg.method = 'bemcp';
vol_new_bemcp = ft_prepare_headmodel(cfg, ft_convert_units(mesh_new, 'mm'));

try
  cfg = [];
  cfg.method = 'dipoli';
  vol_new_dipoli = ft_prepare_headmodel(cfg, ft_convert_units(mesh_new, 'mm'));
catch
  vol_new_dipoli = [];
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% method 2: compute bnd from segmented MRI

% read in the already-segmented mri (in case its segmentation is better than above)
segmentedmri = ft_read_mri(dccnpath('/home/common/matlab/fieldtrip/template/headmodel/standard_seg.mat'));

cfg = [];
cfg.tissue = [3 2 1]; % brain, skull, scalp
cfg.numvertices = [3000 2000 1000];
mesh_seg = ft_prepare_mesh(cfg, segmentedmri);

figure;
ft_plot_mesh(mesh_new(1), 'facecolor', [0.2 0.2 0.2], 'facealpha', 0.3, 'edgecolor', [1 1 1], 'edgealpha', 0.05);
hold on;
ft_plot_mesh(mesh_seg(3), 'facecolor', [0.4 0.6 0.4], 'facealpha', 0.3, 'edgecolor', 'none');
% aside from possible different order, they should align spatially

% Create simple concentricspheres to compare to BEMCP
cfg = [];
cfg.method = 'concentricspheres';
vol_seg_cs = ft_prepare_headmodel(cfg, ft_convert_units(mesh_seg, 'mm'));

cfg = [];
cfg.method = 'bemcp';
vol_seg_bemcp = ft_prepare_headmodel(cfg, ft_convert_units(mesh_seg, 'mm'));

try
  cfg = [];
  cfg.method = 'dipoli';
  vol_seg_dipoli = ft_prepare_headmodel(cfg, ft_convert_units(mesh_new, 'mm'));
catch
  vol_seg_dipoli = [];
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% method 3: use existing bnd in standard_bem

% presumably the mesh that was used to create this 'standard' output from
% dipoli should work (irrespective of segmentation above).
load(dccnpath('/home/common/matlab/fieldtrip/template/headmodel/standard_bem.mat'));
vol_exist_dipoli = vol;
mesh_exist = vol.bnd;
mesh_exist = ft_convert_units(mesh_exist, 'mm');
clear vol

figure;
ft_plot_mesh(mesh_new(3), 'facecolor', [0.2 0.2 0.2], 'facealpha', 0.3, 'edgecolor', [1 1 1], 'edgealpha', 0.05);
hold on;
ft_plot_mesh(mesh_exist(3), 'edgecolor', 'none', 'facecolor', [0.4 0.6 0.4]);
% aside from possible different order, they should align spatially

% Create simple concentricspheres to compare to BEMCP
cfg = [];
cfg.method = 'concentricspheres';
vol_exist_cs = ft_prepare_headmodel(cfg, mesh_exist);

cfg = [];
cfg.method = 'bemcp';
vol_exist_bemcp = ft_prepare_headmodel(cfg, mesh_exist);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Check coregistration and view results

load standard_sourcemodel3d8mm;
sourcemodel = ft_convert_units(sourcemodel, 'mm');

elec = ft_read_sens(dccnpath('/home/common/matlab/fieldtrip/template/electrode/standard_1005.elc'));
elec = ft_convert_units(elec, 'mm');

% check for general spatial alignment
figure; ft_plot_mesh(vol_new_bemcp.bnd(3), 'facecolor', [0.2 0.2 0.2], 'facealpha', 0.3, 'edgecolor', [1 1 1], 'edgealpha', 0.05);
hold on; ft_plot_sens(elec);
hold on; ft_plot_mesh(sourcemodel.pos(sourcemodel.inside, :));

figure; ft_plot_mesh(vol_seg_bemcp.bnd(3), 'facecolor', [0.2 0.2 0.2], 'facealpha', 0.3, 'edgecolor', [1 1 1], 'edgealpha', 0.05);
hold on; ft_plot_sens(elec);
hold on; ft_plot_mesh(sourcemodel.pos(sourcemodel.inside, :));

figure; ft_plot_mesh(vol_exist_bemcp.bnd(3), 'facecolor', [0.2 0.2 0.2], 'facealpha', 0.3, 'edgecolor', [1 1 1], 'edgealpha', 0.05);
hold on; ft_plot_sens(elec);
hold on; ft_plot_mesh(sourcemodel.pos(sourcemodel.inside, :));

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% View leadfields

% make sure vol*.mat of bemcp not NaN
% see bug 1954
if any(isnan(vol_new_bemcp.mat(:)))
  warning('NaN in vol_new_bemcp.mat')
end

close all
clear volnames vol
volnames = whos('vol*');

% these would need to be recomputed given the headmodel geometry
% sourcemodel = rmfield(sourcemodel, 'inside');
% sourcemodel = rmfield(sourcemodel, 'outside');

% or even faster
sourcemodel = [];
sourcemodel.pos = [-20 0 50];
sourcemodel.unit = 'mm';

for ll = 1:length(volnames)
  vol = eval(volnames(ll).name);

  if ~isempty(vol)
    % vol can be empty in case it failed to be created above, e.g. one of
    % the dipoli examples, for one reason or another
    cfg = [];
    cfg.sourcemodel = sourcemodel;
    cfg.elec = elec;
    cfg.headmodel = vol;

    lf = ft_prepare_leadfield(cfg);

    % Hack: to make LF appear as if it were an ERP
    tlock = [];
    tlock.time = [1 2 3];
    tlock.avg = lf.leadfield{dsearchn(sourcemodel.pos, [-20 0 50])};
    tlock.label = lf.cfg.channel;
    tlock.dimord = 'chan_time';

    cfg = [];
    cfg.layout = 'elec1010.lay';
    cfg.xlim = [0.9 1.1];
    figure;
    ft_topoplotER(cfg, tlock);
    title(volnames(ll).name)
    cfg.xlim = [1.9 2.1];
    figure;
    ft_topoplotER(cfg, tlock);
    title(volnames(ll).name)
    cfg.xlim = [2.9 3.1];
    figure;
    ft_topoplotER(cfg, tlock);
    title(volnames(ll).name)

    disp(volnames(ll).name)
  end
end
% For me, sensible patterns come from the vol*cs and vol*dipoli, but not from the vol*bemcp