function [minidx, inputs, outputs] = utl_gridsearch(arg1, varargin)
% Exhaustive search over multiple possible arguments to a function.
% [MinIndex, Inputs, Outputs] = utl_gridsearch(Function, Domain...)
% [MinIndex, Inputs, Outputs] = utl_gridsearch(RangeFormat, Function, Domain...)
% [MinIndex, Inputs, Outputs] = utl_gridsearch(Options, Domain...)
%
% Grid search is a brute-force and assuption-free approach to optimizing parameters of a function.
% It amounts to testing all the possibilities (out of a predetermined set), obtaining the function
% value for each, and returning the parameter combination which gave the best result (here: smallest
% function value).
%
% The arguments to try for the given function are specified as a regular argument list, in which,
% depending on the chosen RangeFormat, either arrays among the arguments are interpreted as multiple
% possibilities to try, or arguments which contain a search() expression/data structure are
% interpreted as defining multiple possibilities. The most convenient way to specify multiple
% possibilities is by using the special function search(), which packs its multiple arguments into
% an annotated container dat structure which is interpreted by utl_gridsearch as a list of
% possibilities to try.
%
% Grid search can be run in parallel across multiple processes/machines, by passing the appropriate
% parallelization options.
%
%
% In:
%   RangeFormat : argument range format, optional; either 'direct' (search ranges are directly
%                 specified as arrays), or 'clauses' (search ranges are specified using the search()
%                 clause). default: 'direct'
%
%   Function    : the function to optimize; should always return a real number (or NaN) as first
%                 output, taken as the objective value of the function; may have additional output
%                 arguments, which are also captured and optionally returned by utl_gridsearch
%
%   Options     : cell array of name-value pairs to specify detailed options (alternative to
%                 RangeFormat/Function); possible names include:
%                 'func'    : the function to optimize (see Function, mandatory)
%
%                 'argform' : argument range format, either 'direct' or 'clauses' (see RangeFormat,
%                             default: direct)
%
%                 parallelization options (see par_beginschedule)
%                 'engine_gs' : the parallelization engine to be used for the grid search
%                               (default: 'local')
%                 'pool'    : node pool to be used for parallelization, when using the BLS scheduler
%                             (default: 'global')
%                 'policy'  : scheduling policy to be used, when using the BLS scheduler
%                             (default: 'global')
%
%   Domain...   : one-dimensional argument ranges to be searched;
%                 * in 'direct' mode, each array determines the possible values at that place in the
%                   function's argument list, all combinations of specified values are tried; for
%                   cell arrays, the search runs through all given cell contents at that position
%                 * in 'clauses' mode, search ranges for a given argument are specified using the
%                   search() expression in that place of the argument list
%
% Out:
%   MinIndex    : index of the minimum function return value (first return value if multiple ones
%                 are returned), out of all tried executions (see example); can be used as index
%                 into Inputs and/or Outputs
%
%   Inputs      : cell array of all tried input combinations (each a cell array of arguments)
%
%   Outputs     : cell array of all received function outputs, one for each input (each a cell array
%                 of outputs, since the function can have multiple outputs)
%
% Example:
%   % For the four equivalent calls,
%     utl_gridsearch(@f, 1:2, {'a',{'b'},5:7,{5:7}}, [], {}, 'xy', {[1 2 3]});
%     utl_gridsearch('direct', @f, 1:2, {'a',{'b'},5:7,{5:7}}, [], {}, 'xy', {[1 2 3]});
%     utl_gridsearch('clauses', @f, search(1,2), search('a',{'b'},5:7,{5:7}), [], {}, search('x','y'), [1 2 3]);
%     utl_gridsearch({'argform','clauses', 'func',@f}, search(1,2), search('a',{'b'},5:7,{5:7}), [], {}, search('x','y'), [1 2 3]);
%
%   % ... the following 16 = 2x4x1x1x2x1 executions of f are compared:
%       f(1,'a',[],{},'x', [1 2 3])
%       f(1,'a',[],{},'y', [1 2 3])
%       f(1,{'b'},[],{},'x', [1 2 3])
%       f(1,{'b'},[],{},'y', [1 2 3])
%       f(1,5:7,[],{},'x', [1 2 3])
%       f(1,5:7,[],{},'y', [1 2 3])
%       f(1,{5:7},[],{},'x', [1 2 3])
%       f(1,{5:7},[],{},'y', [1 2 3])
%       f(2,'a',[],{},'x', [1 2 3])
%       f(2,'a',[],{},'y', [1 2 3])
%       f(2,{'b'},[],{},'x', [1 2 3])
%       f(2,{'b'},[],{},'y', [1 2 3])
%       f(2,5:7,[],{},'x', [1 2 3])
%       f(2,5:7,[],{},'y', [1 2 3])
%       f(2,{5:7},[],{},'x', [1 2 3])
%       f(2,{5:7},[],{},'y', [1 2 3])
%
% See also:
%   search
%
%                                Christian Kothe, Swartz Center for Computational Neuroscience, UCSD
%                                2010-04-06
dp;

% translate the possible formats for the control arguments into the cell-array-of-options form
if iscell(arg1)
    % utl_gridsearch({Options...}, Domain...)
    args = arg1;
elseif isa(arg1,'function_handle')
    % utl_gridsearch(Function, Domain...)
    args = {'func', arg1};
elseif ischar(arg1)
    % utl_gridsearch(RangeFormat, Function, Domain...)
    args = {'argform',arg1,'func',varargin{1}}; varargin = varargin(2:end);
else
    error('Unexpected control options format: %s',hlp_tostring(arg1));
end

% read options
opts = hlp_varargin2struct(args, 'func',mandatory, 'argform','direct', 'engine_gs','local', 'pool','global', 'policy','global');
if ~isa(opts.func,'function_handle')
    error('The Function argument must be a function handle.'); end

% rewrite arguments if given as clauses
if strcmp(opts.argform,'clauses')
    for i=1:length(varargin)
        varargin{i} = hlp_flattensearch(varargin{i},'cell'); end
elseif ~strcmp(opts.argform,'direct')
    error('Unsupported RangeFormat value: %s (allowed are ''clauses'' and ''direct'')',opts.argform);
end

% determine dimensions of the input grid
dims = cellfun('length',varargin);
pitches = [1 cumprod(max(1,dims))];
combos = prod(max(1,dims));

args = varargin;
for i=1:length(args)
    % pre-unpack cell args for singleton arguments
    if iscell(args{i}) && dims(i) == 1
        args{i} = args{i}{1}; end
end
for c=1:combos
    for i=find(dims>1)
        % select current value for non-singleton arguments
        args{i} = varargin{i}(1+floor(mod((c-1)/pitches(i),dims(i))));
        % and unpack cell args
        if iscell(varargin{i})
            args{i} = args{i}{1}; end
    end
    % generate tasks
    tasks{c} = [{@hlp_wrapresults,opts.func},args]; %#ok<AGROW>
    inputs{c} = args; %#ok<AGROW>
end

% schedule tasks
outputs = par_schedule(tasks,'engine',opts.engine_gs,'pool',opts.pool,'policy',opts.policy);

try
    minidx = argmin(cellfun(@(x)x{1},outputs));
catch
    minidx = NaN;
end


function cirbva()
    %   This subroutine "circle"  selects the Ni closest earthquakes
    %   around a interactively selected point.  Resets ZG.newcat and ZG.newt2
    %   Operates on "primeCatalog".
    %   changes newt2
    %
    % axis: h1
    % plots to: plos1 as xk
    % inCatalog: a
    % outCatalog: newt2, newcat
    % mouse controlled
    % closest events OR radius
    % calls: bdiff
    %
    % turned into function by Celso G Reyes 2017

    ZG=ZmapGlobal.Data; % used by get_zmap_globals

    %  Input Ni:
    %
    report_this_filefun();
    ZG=ZmapGlobal.Data;

    delete(findobj('Tag','plos1'));
    disp(ZmapGlobal.Data.hold_state)

    axes(h1)
    %zoom off

    titStr ='Selecting EQ in Circles                         ';
    messtext= ...
        ['                                                '
        '  Please use the LEFT mouse button              '
        ' to select the center point.                    '
        ' The "ni" events nearest to this point          '
        ' will be selected and displayed in the map.     '];

    msg.dbdisp(messtext, titStr);

    % Input center of circle with mouse
    %
    [xa0,ya0]  = ginput(1);

    stri1 = [ 'Circle: ' num2str(xa0,5) '; ' num2str(ya0,4)];
    stri = stri1;
    pause(0.1)
    %  calculate distance for each earthquake from center point
    %  and sort by distance
    %
    l = ZG.primeCatalog.epicentralDistanceTo(ya0,xa0);
    ZG.newt2=ZG.primeCatalog; % points to same thing
    if met == 'ni'
        % take first ni and sort by time
        [ZG.newt2, R2] = ZG.primeCatalog.selectClosestEvents(ni);
    elseif  met == 'ra'
        ZG.newt2 = ZG.primeCatalog.selectRadius(ra);
        R2=ra;
    elseif met == 'ti'
        global t1 t2 t3 t4

        lt =  ZG.newt2.Date >= t1 &  ZG.newt2.Date <t2 ;
        bdiff(ZG.newt2.subset(lt));
        ZG.hold_state=true;
        lt =  ZG.newt2.Date >= t3 &  ZG.newt2.Date <t4 ;
        bdiff(ZG.newt2.subset(lt));

    end
    ZG.newt2.sort('Date');
    R2 = ra;

    %
    % plot Ni clostest events on map as 'x':

    set(gca,'NextPlot','add')
    plot(ZG.newt2.Longitude,ZG.newt2.Latitude,'xk','Tag','plos1');

    % plot circle containing events as circle
    x = -pi-0.1:0.1:pi;
    pl = plot(xa0+sin(x)*R2/(cosd(ya0)*111), ya0+cos(x)*R2/(cosd(ya0)*111),'k')
    %plot(xa0+sin(x)*l(ni)/111, ya0+cos(x)*l(ni)/111,'k')

    set(gcf,'Pointer','arrow')

    %
    newcat = ZG.newt2;                   % resets ZG.newcat and ZG.newt2

    bdiff(ZG.newt2,ZmapGlobal.Data.hold_state)

end


function average = spm_cfg_eeg_average
% Configuration file for M/EEG epoching
%__________________________________________________________________________
% Copyright (C) 2008-2016 Wellcome Trust Centre for Neuroimaging

% Stefan Kiebel
% $Id: spm_cfg_eeg_average.m 6926 2016-11-09 22:13:19Z guillaume $

rev = '$Rev: 6926 $';
D = cfg_files;
D.tag = 'D';
D.name = 'File Name';
D.filter = 'mat';
D.num = [1 1];
D.help = {'Select the M/EEG mat file.'};

standard = cfg_const;
standard.tag = 'standard';
standard.name = 'Standard';
standard.val  = {false};
standard.help = {''};

ks = cfg_entry;
ks.tag = 'ks';
ks.name = 'Offset of the weighting function';
ks.strtype = 'r';
ks.val = {3};
ks.num = [1 1];
ks.help = {'Parameter determining the how far the values should be from the median, '...
    'to be considered outliers (the larger, the farther).'};

bycondition = cfg_menu;
bycondition.tag = 'bycondition';
bycondition.name = 'Compute weights by condition';
bycondition.help = {'Compute weights for each condition separately or for all conditions together.'};
bycondition.labels = {'Yes', 'No'};
bycondition.values = {true, false};
bycondition.val = {false};

savew = cfg_menu;
savew.tag = 'savew';
savew.name = 'Save weights';
savew.help = {'Save weights in a separate dataset for quality control.'};
savew.labels = {'Yes', 'No'};
savew.values = {true, false};
savew.val = {false};

removebad = cfg_menu;
removebad.tag = 'removebad';
removebad.name = 'Remove bad data';
removebad.help = {'Replace data marked as bad by NaNs before averaging',...
    'Warning: beware if there is no good data, NaNs may stay in the average.'};
removebad.labels = {'Yes', 'No'};
removebad.values = {true, false};
removebad.val = {false};

robust = cfg_branch;
robust.tag = 'robust';
robust.name = 'Robust';
robust.val = {ks, bycondition, savew, removebad};
robust.help = {''};

userobust = cfg_choice;
userobust.tag = 'userobust';
userobust.name = 'Averaging type';
userobust.help = {'choose between using standard and robust averaging.'};
userobust.values = {standard, robust};
userobust.val = {standard};

plv = cfg_menu;
plv.tag = 'plv';
plv.name = 'Compute phase-locking value';
plv.help = {'Compute phase-locking value rather than average the phase',...
    'This option is only relevant for TF-phase datasets.'};
plv.labels = {'Yes', 'No'};
plv.values = {true, false};
plv.val = {false};

prefix         = cfg_entry;
prefix.tag     = 'prefix';
prefix.name    = 'Filename Prefix';
prefix.help    = {'Specify the string to be prepended to the filenames of the averaged dataset. Default prefix is ''m''.'};
prefix.strtype = 's';
prefix.num     = [1 Inf];
prefix.val     = {'m'};

average = cfg_exbranch;
average.tag = 'average';
average.name = 'Averaging';
average.val = {D, userobust, plv, prefix};
average.help = {'Average epoched EEG/MEG data.'};
average.prog = @eeg_average;
average.vout = @vout_eeg_average;
average.modality = {'EEG'};

function out = eeg_average(job)
% construct the S struct
S.D = job.D{1};
if isfield(job.userobust, 'robust')
    S.robust = job.userobust.robust;
else
    S.robust = false;
end

S.circularise = job.plv;
S.prefix = job.prefix;

out.D = spm_eeg_average(S);
out.Dfname = {fullfile(out.D.path, out.D.fname)};

function dep = vout_eeg_average(job)
% Output is always in field "D", no matter how job is structured
dep = cfg_dep;
dep.sname = 'Average Data';
% reference field "D" from output
dep.src_output = substruct('.','D');
% this can be entered into any evaluated input
dep.tgt_spec   = cfg_findspec({{'strtype','e'}});

dep(2) = cfg_dep;
dep(2).sname = 'Averaged Datafile';
% reference field "Dfname" from output
dep(2).src_output = substruct('.','Dfname');
% this can be entered into any file selector
dep(2).tgt_spec   = cfg_findspec({{'filter','mat'}});


function trainingModel = loadTrainingData(param)
% Generates the structure that contains all the training data needed for
% Component Contribution.
%
% USAGE:
%
%    trainingModel = loadTrainingData(formation_weight)
%
% INPUT:
%    formation_weight:    the relative weight to give the formation energies (Alberty's data)
%                         compared to the reaction measurements (TECRDB)
%
% OUTPUT:
%    trainingModel:       structure with data for Component Contribution
%                         *.S   `m x n` stoichiometric matrix of training data
%                         *.cids: `m x 1` compound ids
%                         *.dG0_prime: `n x 1`
%                         *.T:  `n x 1`
%                         *.I:  `n x 1`
%                         *.pH:  `n x 1`
%                         *.pMg:  `n x 1`
%                         *.weights:  `n x 1`
%                         *.balance:  `n x 1`
%                         *.cids_that_dont_decompose: k x 1 ids of compounds that do not decompose

if ~exist('param','var')
    formation_weight = 1;
    use_cached_kegg_inchis=true;
    use_model_pKas_by_default=true;
else
    if ~isfield(param,'formation_weight')
        formation_weight = 1;
    end
    if ~isfield(params,'use_cached_kegg_inchis')
        use_cached_kegg_inchis = true;
        % use_cached_kegg_inchis = false;
    else
        use_cached_kegg_inchis=params.use_cached_kegg_inchis;
    end
    if ~isfield(params,'use_model_pKas_by_default')
        use_model_pKas_by_default = true;
    else
        use_model_pKas_by_default=params.use_model_pKas_by_default;
    end
end

TECRDB_TSV_FNAME = 'data/TECRDB.tsv';
FORMATION_TSV_FNAME = 'data/formation_energies_transformed.tsv';
REDOX_TSV_FNAME = 'data/redox.tsv';

WEIGHT_TECRDB = 1;
WEIGHT_FORMATION = formation_weight;
WEIGHT_REDOX = formation_weight;

R=8.31451;
%Energies are expressed in kJ mol^-1.*)
R=R/1000; % kJ/mol/K
%Faraday Constant (kJ/mol)
F=96.48; %kJ/mol

if ~exist(TECRDB_TSV_FNAME, 'file')
    error(['file not found: ', TECRDB_TSV_FNAME]);
end

if ~exist(FORMATION_TSV_FNAME, 'file')
    error(['file not found: ', FORMATION_TSV_FNAME]);
end

if ~exist(REDOX_TSV_FNAME, 'file')
    error(['file not found: ', REDOX_TSV_FNAME]);
end

% Read the raw data of TECRDB (NIST)
reactions = {};
rxns={};
cids = [];
cids_that_dont_decompose = [];
thermo_params = []; % columns are: dG'0, T, I, pH, pMg, weight, balance?

fid = fopen(TECRDB_TSV_FNAME, 'r');
% fields are:
% 1. URL
% 2. REF_ID
% 3. METHOD
% 4. EVAL
% 5. EC
% 6. ENZYME NAME
% 7. REACTION IN KEGG IDS
% 8. REACTION IN COMPOUND NAMES
% 9. K
% 10. K'
% 11. T
% 12. I
% 13. pH
% 14. pMg
% 15. ID

res = textscan(fid, '%s%s%s%s%s%s%s%s%f%f%f%f%f%f%f', 'delimiter','\t');
fclose(fid);

inds = find(~isnan(res{10}) .* ~isnan(res{11}) .* ~isnan(res{13}));

dG0_prime = -R * res{11}(inds) .* log(res{10}(inds)); % calculate dG'0
thermo_params = [dG0_prime, res{11}(inds), res{12}(inds), res{13}(inds), ...
                 res{14}(inds), WEIGHT_TECRDB * ones(size(inds)), ...
                 true(size(inds))];

% parse the reactions in each row
for i = 1:length(inds)
    sprs = reaction2sparse(res{7}{inds(i)});
    cids = unique([cids, find(sprs)]);
    reactions = [reactions, {sprs}];
    rxns = [rxns;strtrim(['TECRDB_' int2str(res{15}(inds(i)))])];
    if strcmp('TECRDB_4403',strtrim(['TECRDB_' int2str(res{15}(inds(i)))]))
        pause(0.1);
    end
end
fprintf('Successfully added %d values from TECRDB\n', length(inds));

% Read the Formation Energy data.
fid = fopen(FORMATION_TSV_FNAME, 'r');
fgetl(fid); % skip the first header line
% fields are:
% 1. cid
% 2. name
% 3. dG'0
% 4. pH
% 5. I
% 6. pMg
% 7. T
% 8. decompose?
% 9. compound_ref
% 10. remark

res = textscan(fid, '%f%s%f%f%f%f%f%f%s%s', 'delimiter','\t');
fclose(fid);

inds = find(~isnan(res{3}));
thermo_params = [thermo_params; [res{3}(inds), res{7}(inds), res{5}(inds), ...
                                 res{4}(inds), res{6}(inds), ...
                                 WEIGHT_FORMATION * ones(size(inds)), ...
                                 false(size(inds))]];

eval(['cid = {' regexprep(sprintf('''C%05d''; ',res{1}),'(;\s)$','') '};']);
for i = 1:length(inds)
    sprs = sparse([]);
    sprs(res{1}(inds(i))) = 1;
    reactions = [reactions, {sprs}];
    rxns = [rxns;['FORM_' cid{i}]];
end

cids = union(cids, res{1}');
cids_that_dont_decompose = res{1}(find(res{8} == 0));

fprintf('Successfully added %d formation energies\n', length(res{1}));

% Read the Reduction potential data.
fid = fopen(REDOX_TSV_FNAME, 'r');
fgetl(fid); % skip the first header line
% fields are:
% 1. name
% 2. CID_ox
% 3. nH_ox
% 4. charge_ox
% 5. CID_red
% 6. nH_red,
% 7. charge_red
% 8. E'0
% 9. pH
% 10. I
% 11. pMg
% 12. T
% 13. ref
res = textscan(fid, '%s%f%f%f%f%f%f%f%f%f%f%f%s', 'delimiter', '\t');
fclose(fid);

delta_e = (res{6} - res{3}) - (res{7} - res{4}); % delta_nH - delta_charge
dG0_prime = -F * res{8} .* delta_e;
thermo_params = [thermo_params; [dG0_prime, res{12}, res{10}, res{9}, ...
                                 res{11}, ...
                                 WEIGHT_REDOX * ones(size(dG0_prime)), ...
                                 false(size(dG0_prime))]];

for i = 1:length(res{1})
    sprs = sparse([]);
    sprs(res{2}(i)) = -1;
    sprs(res{5}(i)) = 1;
    cids = unique([cids, res{2}(i), res{5}(i)]);
    reactions = [reactions, {sprs}];
    rxns = [rxns;['REDOX_' strrep(res{1}{i},' ','_')]];
end

fprintf('Successfully added %d redox potentials\n', length(res{1}));

% convert the list of reactions in sparse notation into a full
% stoichiometric matrix, where the rows (compounds) are according to the
% CID list 'cids'.
S = zeros(length(cids), length(reactions));
for i = 1:length(reactions)
    r = reactions{i};
    S(ismember(cids, find(r)), i) = r(r ~= 0);
    %production of h20 in certain redox reactions
    if any(strcmp(rxns{i},{'REDOX_dimethyl_sulfoxide';'REDOX_Trimethylamine'}))
        S(cids==1,i)=1;
    end
end

trainingModel.S = sparse(S);

if ~isfield(trainingModel,'rxns')
    for i=1:size(trainingModel.S,2)
        trainingModel.rxns{i,1}=['rxn' int2str(i)];
    end
end
if ~isfield(trainingModel,'lb')
    trainingModel.lb=ones(size(trainingModel.S,2),1)*-inf;
end
if ~isfield(trainingModel,'ub')
    trainingModel.ub=ones(size(trainingModel.S,2),1)*inf;
end

trainingModel.rxns = rxns;
trainingModel.cids = cids';
trainingModel.dG0_prime = thermo_params(:, 1);
trainingModel.T = thermo_params(:, 2);
trainingModel.I = thermo_params(:, 3);
trainingModel.pH = thermo_params(:, 4);
trainingModel.pMg = thermo_params(:, 5);
trainingModel.weights = thermo_params(:, 6);
trainingModel.balance = thermo_params(:, 7);
trainingModel.cids_that_dont_decompose = cids_that_dont_decompose;

%remove some problematic entries
boolRemove=false(size(trainingModel.rxns,1),1);

%http://xpdb.nist.gov/enzyme_thermodynamics/enzyme_data1.pl?col=1.&T1=66DED_421	C00089 + C06215 = C00031 + C06215	sucrose(aq) + (2,6--D-fructosyl)n(aq) = D-glucose(aq) + (2,6--D-fructosyl)n+1(aq)
%C06215 present on both sides of the reaction
%Need proper ID's for (2,6--D-fructosyl)n(aq) and (2,6--D-fructosyl)n+1(aq)
boolRemove = boolRemove | ismember(trainingModel.rxns,'TECRDB_695');

% http://xpdb.nist.gov/enzyme_thermodynamics/enzyme_data1.pl?col=1.&T1=93AND/BUL_1509	93AND/BUL	radioactivity	B	2.3.1.129	UDP-N-acetylglucosamine acyltransferase	C04688 + C00043 = C03688 + C04738	(R)-3-hydroxytetradecanoyl-[acyl-carrier-protein](aq) + UDP-N-acetyl-D-glucosamine(aq) = acyl-carrier-protein(aq) + UDP-3-O-(3-hydroxytetradecanoyl)-N-acetyl-D-glucosamine(aq)		0.007	296.15		7.4		2905
% http://xpdb.nist.gov/enzyme_thermodynamics/enzyme_data1.pl?col=1.&T1=93AND/BUL_1509	93AND/BUL	radioactivity	B	2.3.1.129	UDP-N-acetylglucosamine acyltransferase	C04688 + C00043 = C03688 + C04738	(R)-3-hydroxytetradecanoyl-[acyl-carrier-protein](aq) + UDP-N-acetyl-D-glucosamine(aq) = acyl-carrier-protein(aq) + UDP-3-O-(3-hydroxytetradecanoyl)-N-acetyl-D-glucosamine(aq)		0.004	296.15		8.5		2906
% http://xpdb.nist.gov/enzyme_thermodynamics/enzyme_data1.pl?col=1.&T1=93AND/BUL_1509	93AND/BUL	radioactivity	B	2.3.1.129	UDP-N-acetylglucosamine acyltransferase	C04688 + C00043 = C03688 + C04738	(R)-3-hydroxytetradecanoyl-[acyl-carrier-protein](aq) + UDP-N-acetyl-D-glucosamine(aq) = acyl-carrier-protein(aq) + UDP-3-O-(3-hydroxytetradecanoyl)-N-acetyl-D-glucosamine(aq)		0.002	296.15		9		2907
% http://xpdb.nist.gov/enzyme_thermodynamics/enzyme_data1.pl?col=1.&T1=93AND/BUL_1509	93AND/BUL	radioactivity	B	2.3.1.129	UDP-N-acetylglucosamine acyltransferase	C04688 + C00043 = C03688 + C04738	(R)-3-hydroxytetradecanoyl-[acyl-carrier-protein](aq) + UDP-N-acetyl-D-glucosamine(aq) = acyl-carrier-protein(aq) + UDP-3-O-(3-hydroxytetradecanoyl)-N-acetyl-D-glucosamine(aq)		0.0095	296.15		8		2908
boolRemove = boolRemove | ismember(trainingModel.rxns,'TECRDB_2905');
boolRemove = boolRemove | ismember(trainingModel.rxns,'TECRDB_2906');
boolRemove = boolRemove | ismember(trainingModel.rxns,'TECRDB_2907');
boolRemove = boolRemove | ismember(trainingModel.rxns,'TECRDB_2908');

%These were missing water on the rhs, fixed in TECRDB.tsv
%TECRDB_4540 85LIE		A	4.3.-.-	formaldehyde condensation with THF	C00101 + C00067 = C00143 + C00001	THF(aq) + formaldehyde(aq) = 5,10-CH2-THF(aq) + H2O(l)		30000	311.15	0.25	7		4540
%TECRDB_4541 +66KAL/JEN		E	4.3.-.-	formaldehyde condensation with THF	C00101 + C00067 = C00143 + C00001	THF(aq) + formaldehyde(aq) = 5,10-CH2-THF(aq) + H2O(l)		32000	298.15	1	7		4541
%TECRDB_4544 +59BLA		E	4.3.-.-	formaldehyde condensation with THF	C00101 + C00067 = C00143 + C00001	THF(aq) + formaldehyde(aq) = 5,10-CH2-THF(aq)		7700	293.15		7.2		4544

%The record of 85LIE is problematic i.e. TECRDB_870 - TECRDB_884
%e.g. http://xpdb.nist.gov/enzyme_thermodynamics/enzyme_data1.pl?col=1.&T1=85LIE_292	85LIE	spectrophotometry and enzymatic assay	A	1.4.4.2	glycine dehydrogenase (decarboxylating)	C00037 + C00725 = C80069 + C00288	glycine(aq) + lipoate(aq) = S-aminomethyldihydro--lipoate(aq) + carbon dioxide(aq)		0.031	311.15		6.39		870
%is C00725 really C00248 ???
%C80069 is InChI=1S/C9H19NO2S2/c10-7-14-6-5-8(13)3-1-2-4-9(11)12/h8,13H,1-7,10H2,(H,11,12)
%C80069	but is it  InChI=1S/C9H20N2OS2/c10-7-14-6-5-8(13)3-1-2-4-9(11)12/h8,13H,1-7,10H2,(H2,11,12)/p+1  ???
boolRemove = boolRemove | ismember(trainingModel.rxns,'TECRDB_870');
boolRemove = boolRemove | ismember(trainingModel.rxns,'TECRDB_871');
boolRemove = boolRemove | ismember(trainingModel.rxns,'TECRDB_872');
boolRemove = boolRemove | ismember(trainingModel.rxns,'TECRDB_873');
boolRemove = boolRemove | ismember(trainingModel.rxns,'TECRDB_874');
boolRemove = boolRemove | ismember(trainingModel.rxns,'TECRDB_875');
boolRemove = boolRemove | ismember(trainingModel.rxns,'TECRDB_876');
boolRemove = boolRemove | ismember(trainingModel.rxns,'TECRDB_877');
boolRemove = boolRemove | ismember(trainingModel.rxns,'TECRDB_878');
boolRemove = boolRemove | ismember(trainingModel.rxns,'TECRDB_879');
boolRemove = boolRemove | ismember(trainingModel.rxns,'TECRDB_880');
boolRemove = boolRemove | ismember(trainingModel.rxns,'TECRDB_881');
boolRemove = boolRemove | ismember(trainingModel.rxns,'TECRDB_882');
boolRemove = boolRemove | ismember(trainingModel.rxns,'TECRDB_883');
boolRemove = boolRemove | ismember(trainingModel.rxns,'TECRDB_884');
% The overall reaction reported in the abstract of the dissertation (https://digitalcommons.library.tmc.edu/dissertations/AAI8516325/) is as follows:
%85LIE		A	1.1.-.-	glycine cleavage system (a series of enzymes)	C00037 + C00003 + C00101 = C00143 + C00004 + C00014 + C00011 	glycine(aq) + NAD+(aq) + THF(aq) = 5,10-CH2-THF(aq) + NADH(aq) + NH3(aq) + CO2(aq)		0.00156	311.15	0.25	7		4539

%http://xpdb.nist.gov/enzyme_thermodynamics/enzyme_data1.pl?col=1.&T1=65STR_520	65STR	spectrophotometry	C	2.6.1.13	ornithine-oxo-acid transaminase	C00077 + C00026 = C03912 + C00025	L-ornithine(aq) + 2-oxoglutarate(aq) = DL-D-1-pyrroline-5-carboxylate(aq) + L-glutamate(aq)		71	310.15		7.1		2719
%is http://xpdb.nist.gov/enzyme_thermodynamics/enzyme_data1.pl?col=1.&T1=65STR_520	65STR	spectrophotometry	C	2.6.1.13	ornithine-oxo-acid transaminase	C00077 + C00026 = C01165 + C00025 + C00001	L-ornithine(aq) + 2-oxoglutarate(aq) = DL-D-1-pyrroline-5-carboxylate(aq) + L-glutamate(aq)		71	310.15		7.1		2719
%i.e. C00001 added to rhs, it is balanced, but is this correct?

%85LIE		A	1.1.-.-	glycine cleavage system	C00037 + C00003 + C00101 = C00143 + C00004 + C00014 + C00288 	glycine(aq) + NAD+(aq) + THF(aq) = 5,10-CH2-THF(aq) + NADH(aq) + NH3(aq) + CO2(aq)		0.00156	311.15	0.25	7		4539
%is 85LIE		A	1.1.-.-	glycine cleavage system	C00037 + C00003 + C00101 = C00143 + C00004 + C00014 + C00011 	glycine(aq) + NAD+(aq) + THF(aq) = 5,10-CH2-THF(aq) + NADH(aq) + NH3(aq) + CO2(aq)		0.00156	311.15	0.25	7		4539
%i.e. C00288 replaced by C00011

%http://xpdb.nist.gov/enzyme_thermodynamics/enzyme_data1.pl?col=1.&T1=07LIN/ALG_1584	07LIN/ALG	spectrophotometry	A	1.1.1.87	homoisocitrate dehydrogenase	 C05662 + C00003 = C00322 + C00288 + C00004	(1R,2S)-1-hydroxybutane-1,2,4-tricarboxylate(aq) + NAD(ox) = 2-oxoadipate(aq) + carbon dioxide(aq) + NAD(red)		0.45	298.15		7.5		1
%is http://xpdb.nist.gov/enzyme_thermodynamics/enzyme_data1.pl?col=1.&T1=07LIN/ALG_1584	07LIN/ALG	spectrophotometry	A	1.1.1.87	homoisocitrate dehydrogenase	C00001 + C05662 + C00003 = C00322 + C00288 + C00004	H2O(l) + (1R,2S)-1-hydroxybutane-1,2,4-tricarboxylate(aq) + NAD(ox) = 2-oxoadipate(aq) + carbon dioxide(aq) + NAD(red)		0.45	298.15		7.5		1
%i.e. lhs missing h20

%http://xpdb.nist.gov/enzyme_thermodynamics/enzyme_data1.pl?col=1.&T1=95PEL/MAC_1595	95PEL/MAC	spectrophotometry and enzymatic assay	B	1.5.1.5	methylenetetrahydrofolate dehydrogenase (NADP+)	C00143 + C00006 = C00234 + C00005	5,10-methylenetetrahydrofolate(aq) + NADP(ox)(aq) = 10-formyltetrahydrofolate(aq) + NADP(red)(aq)		16	303.15		7.3		267
%is http://xpdb.nist.gov/enzyme_thermodynamics/enzyme_data1.pl?col=1.&T1=95PEL/MAC_1595	95PEL/MAC	spectrophotometry and enzymatic assay	B	1.5.1.5	methylenetetrahydrofolate dehydrogenase (NADP+)	C00001 + C00143 + C00006 = C00234 + C00005	H2O(l) + 5,10-methylenetetrahydrofolate(aq) + NADP(ox)(aq) = 10-formyltetrahydrofolate(aq) + NADP(red)(aq)		16	303.15		7.3		267
%i.e. lhs missing h20

%remove some of the training reactions that are problematic, e.g. those involving ACP
trainingModel.S = trainingModel.S(:,~boolRemove);
trainingModel.lb = trainingModel.lb(~boolRemove);
trainingModel.ub = trainingModel.ub(~boolRemove);
trainingModel.rxns = trainingModel.rxns(~boolRemove);
trainingModel.dG0_prime = trainingModel.dG0_prime(~boolRemove);
trainingModel.T = trainingModel.T(~boolRemove);
trainingModel.I = trainingModel.I(~boolRemove);
trainingModel.pH = trainingModel.pH(~boolRemove);
trainingModel.pMg = trainingModel.pMg(~boolRemove);
trainingModel.weights = trainingModel.weights(~boolRemove);
trainingModel.balance = trainingModel.balance(~boolRemove);


function [stat] = ft_spike_xcorr(cfg, spike)

% FT_SPIKE_XCORR computes the cross-correlation histogram and shift predictor.
%
% Use as
%   [stat] = ft_spike_xcorr(cfg, data)
%
% The input SPIKE should be organised as the spike or the raw datatype, obtained from
% FT_SPIKE_MAKETRIALS or FT_PREPROCESSING (in that case, conversion is done
% within the function). A mex file is located in /contrib/spike/private
% which will be automatically mexed.
%
% Configurations options for xcorr general:
%   cfg.maxlag           = number in seconds, indicating the maximum lag for the
%                          cross-correlation function in sec (default = 0.1 sec).
%   cfg.debias           = 'yes' (default) or 'no'. If 'yes', we scale the
%                          cross-correlogram by M/(M-abs(lags)), where M = 2*N -1 with N
%                          the length of the data segment.
%   cfg.method           = 'xcorr' or 'shiftpredictor'. If 'shiftpredictor'
%                           we do not compute the normal cross-correlation
%                           but shuffle the subsequent trials.
%                           If two channels are independent, then the shift
%                           predictor should give the same correlogram as the raw
%                           correlogram calculated from the same trials.
%                           Typically, the shift predictor is subtracted from the
%                           correlogram.
%   cfg.outputunit       = - 'proportion' (value in each bin indicates proportion of occurence)
%                          - 'center' (values are scaled to center value which is set to 1)
%                          - 'raw' (default) unnormalized crosscorrelogram.
%   cfg.binsize          = [binsize] in sec (default = 0.001 sec).
%   cfg.channelcmb       = Mx2 cell-array with selection of channel pairs (default = {'all' 'all'}),
%                          see FT_CHANNELCOMBINATION for details
%   cfg.latency          = [begin end] in seconds, 'max' (default), 'min', 'prestim'(t<=0), or
%                          'poststim' (t>=0).%
%   cfg.vartriallen      = 'yes' (default) or 'no'.
%                          If 'yes' - accept variable trial lengths and use all available trials
%                          and the samples in every trial.
%                          If 'no'  - only select those trials that fully cover the window as
%                          specified by cfg.latency and discard those trials that do not.
%                          if cfg.method = 'yes', then cfg.vartriallen
%                          should be 'no' (otherwise, fewer coincidences
%                          will occur because of non-overlapping windows)
%   cfg.trials           = numeric selection of trials (default = 'all')
%   cfg.keeptrials       = 'yes' or 'no' (default)
%
% A peak at a negative lag for stat.xcorr(chan1,chan2,:) means that chan1 is leading
% chan2. Thus, a negative lag represents a spike in the second dimension of
% stat.xcorr before the channel in the third dimension of stat.stat.
%
% Variable trial length is controlled by the option cfg.vartriallen. If it is
% specified as cfg.vartriallen='yes', all trials are selected that have a minimum
% overlap with the latency window of cfg.maxlag. However, the shift predictor
% calculation demands that following trials have the same amount of data, otherwise,
% it does not control for rate non-stationarities. If cfg.vartriallen = 'yes', all
% trials should fall in the latency window, otherwise we do not compute the shift
% predictor.
%
% Output:
%    stat.xcorr            = nchans-by-nchans-by-(2*nlags+1) cross correlation histogram with dimord 'chan_chan_time'
%   or
%    stat.shiftpredictor   = nchans-by-nchans-by-(2*nlags+1) shift predictor with dimord 'chan_chan_time'
% and
%    stat.lags             = (2*nlags + 1) vector with lags in seconds.
%    stat.trial            = ntrials-by-nchans-by-nchans-by-(2*nlags + 1) with single trials and dimord 'rpt_chan_chan_time'
%    stat.label            = corresponding labels to channels in stat.xcorr
%    stat.cfg              = configurations used in this function

% Copyright (C) 2010-2012, Martin Vinck
%
% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org
% 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$

% these are used by the ft_preamble/ft_postamble function and scripts
ft_revision = '$Id$';
ft_nargin   = nargin;
ft_nargout  = nargout;

% do the general setup of the function
ft_defaults
ft_preamble init
ft_preamble provenance spike

% check input spike structure
spike = ft_checkdata(spike, 'datatype', 'spike', 'feedback', 'yes');

% get the default options
cfg.trials         = ft_getopt(cfg, 'trials', 'all');
cfg.latency        = ft_getopt(cfg, 'latency','maxperiod');
cfg.keeptrials     = ft_getopt(cfg, 'keeptrials', 'no');
cfg.method         = ft_getopt(cfg, 'method', 'xcorr');
cfg.channelcmb     = ft_getopt(cfg, 'channelcmb', 'all');
cfg.vartriallen    = ft_getopt(cfg, 'vartriallen', 'yes');
cfg.debias         = ft_getopt(cfg, 'debias', 'yes');
cfg.maxlag         = ft_getopt(cfg, 'maxlag', 0.01);
cfg.binsize        = ft_getopt(cfg, 'binsize', 0.001);
cfg.outputunit     = ft_getopt(cfg, 'outputunit', 'proportion');

% ensure that the options are valid
cfg = ft_checkopt(cfg, 'latency', {'char', 'ascendingdoublebivector'});
cfg = ft_checkopt(cfg, 'trials', {'char', 'doublevector', 'logical'});
cfg = ft_checkopt(cfg, 'keeptrials', 'char', {'yes', 'no'});
cfg = ft_checkopt(cfg, 'method', 'char', {'xcorr', 'shiftpredictor'});
cfg = ft_checkopt(cfg, 'channelcmb', {'char', 'cell'});
cfg = ft_checkopt(cfg, 'vartriallen', 'char', {'no', 'yes'});
cfg = ft_checkopt(cfg, 'debias', 'char', {'yes', 'no'});
cfg = ft_checkopt(cfg, 'maxlag', 'doublescalar');
cfg = ft_checkopt(cfg, 'binsize', 'doublescalar');
cfg = ft_checkopt(cfg, 'outputunit', 'char', {'proportion', 'center', 'raw'});

cfg = ft_checkconfig(cfg, 'allowed', {'latency', 'trials', 'keeptrials', 'method', 'channelcmb', 'vartriallen', 'debias', 'maxlag', 'binsize', 'outputunit'});

doShiftPredictor  = strcmp(cfg.method, 'shiftpredictor'); % shift predictor

% determine the corresponding indices of the requested channel combinations
cfg.channelcmb = ft_channelcombination(cfg.channelcmb, spike.label(:), true);
cmbindx        = zeros(size(cfg.channelcmb));
for k=1:size(cfg.channelcmb,1)
  cmbindx(k,1) = strmatch(cfg.channelcmb(k,1), spike.label, 'exact');
  cmbindx(k,2) = strmatch(cfg.channelcmb(k,2), spike.label, 'exact');
end
nCmbs 	   = size(cmbindx,1);
chansel    = unique(cmbindx(:)); % get the unique channels
nChans     = length(chansel);
if isempty(chansel), error('No channel was selected'); end

% get the number of trials or change SPIKE according to cfg.trials
nTrials = size(spike.trialtime,1);
if  strcmp(cfg.trials,'all')
  cfg.trials = 1:nTrials;
elseif islogical(cfg.trials) || all(cfg.trials==0 | cfg.trials==1)
  cfg.trials = find(cfg.trials);
end
cfg.trials = sort(cfg.trials(:));
if max(cfg.trials)>nTrials, error('maximum trial number in cfg.trials should not exceed length of DATA.trial'); end
if isempty(cfg.trials), errors('No trials were selected in cfg.trials'); end
nTrials = length(cfg.trials);

% get the latencies
begTrialLatency = spike.trialtime(cfg.trials,1);
endTrialLatency = spike.trialtime(cfg.trials,2);
trialDur = endTrialLatency - begTrialLatency;

% select the latencies
if strcmp(cfg.latency,'minperiod')
  cfg.latency = [max(begTrialLatency) min(endTrialLatency)];
elseif strcmp(cfg.latency,'maxperiod')
  cfg.latency = [min(begTrialLatency) max(endTrialLatency)];
elseif strcmp(cfg.latency,'prestim')
  cfg.latency = [min(begTrialLatency) 0];
elseif strcmp(cfg.latency,'poststim')
  cfg.latency = [0 max(endTrialLatency)];
end

% check whether the time window fits with the data
if (cfg.latency(1) < min(begTrialLatency)), cfg.latency(1) = min(begTrialLatency);
  warning('Correcting begin latency of averaging window');
end
if (cfg.latency(2) > max(endTrialLatency)), cfg.latency(2) = max(endTrialLatency);
  warning('Correcting begin latency of averaging window');
end

% get the maximum number of lags in samples
fsample = (1/cfg.binsize);
if cfg.maxlag>(cfg.latency(2)-cfg.latency(1))
  warning('Correcting cfg.maxlag since it exceeds latency period');
  cfg.maxlag = (cfg.latency(2) - cfg.latency(1));
end
nLags        = round(cfg.maxlag*fsample);
lags         = (-nLags:nLags)/fsample; % create the lag axis

% check which trials will be used based on the latency
fullDur       = trialDur>=(cfg.maxlag); % only trials which are larger than maximum lag
overlaps      = endTrialLatency>(cfg.latency(1)+cfg.maxlag) & begTrialLatency<(cfg.latency(2)-cfg.maxlag);
hasWindow     = ones(nTrials,1);
if strcmp(cfg.vartriallen,'no') % only select trials that fully cover our latency window
  startsLater    = single(begTrialLatency) > (single(cfg.latency(1)) + 0.5*cfg.binsize);
  endsEarlier    = single(endTrialLatency) < (single(cfg.latency(2)) - 0.5*cfg.binsize);
  hasWindow      = ~(startsLater | endsEarlier); % check this in all other funcs
end
trialSel           = fullDur(:) & overlaps(:) & hasWindow(:);
cfg.trials         = cfg.trials(trialSel);
begTrialLatency    = begTrialLatency(trialSel);
endTrialLatency    = endTrialLatency(trialSel);

if isempty(cfg.trials), warning('No trials were selected'); end
if length(cfg.trials)<2&&doShiftPredictor
  error('Shift predictor can only be calculated with more than 1 selected trial.');
end

% if we allow variable trial lengths
if strcmp(cfg.vartriallen,'yes') && doShiftPredictor && ~strcmp(cfg.outputunit,'proportion')
  warning('using cfg.vartriallen = "yes" and shift predictor method: please use cfg.outputunit = "proportion"');
end
nTrials   = length(cfg.trials); % only now reset nTrials

% preallocate the sum and the single trials and the shift predictor
keepTrials   = strcmp(cfg.keeptrials,'yes');
s     = zeros(nChans,nChans,2*nLags);
if keepTrials
  warning('storing single trials for cross correlation is memory expensive, please check');
  singleTrials = zeros(nTrials,nChans,nChans,2*nLags);
end

if strcmp(cfg.method,'shiftpredictor'), singleTrials(1,:,:,:) = NaN; end

if ((cfg.latency(2)-cfg.latency(1))/cfg.maxlag)<2.5
  warning('selected latency will cause highly variable xcorr at borders');
end

ft_progress('init', 'text',     'Please wait...');
for iTrial = 1:nTrials
  origTrial = cfg.trials(iTrial);
  ft_progress(iTrial/nTrials, 'Processing trial %d from %d', iTrial, nTrials);

  for iCmb = 1:nCmbs
    % take only the times that are in this trial and latency window
    indx  = cmbindx(iCmb,:);
    inTrial1 = spike.trial{indx(1)}==origTrial;
    inTrial2 = spike.trial{indx(2)}==origTrial;
    inWindow1 = spike.time{indx(1)}>=cfg.latency(1) &  spike.time{indx(1)}<=cfg.latency(2);
    inWindow2 = spike.time{indx(2)}>=cfg.latency(1) &  spike.time{indx(2)}<=cfg.latency(2);
    ts1 = sort(spike.time{indx(1)}(inTrial1(:) & inWindow1(:)));
    ts2 = sort(spike.time{indx(2)}(inTrial2(:) & inWindow2(:)));

    switch cfg.method
      case 'xcorr'
        % compute the xcorr if both are non-empty
        if ~isempty(ts1) && ~isempty(ts2)
          if indx(1)<=indx(2)
            [x]   = spike_crossx_matlab(ts1(:),ts2(:),cfg.binsize,nLags*2+1);  % removed the mex file for now, until issue on windows is resolved
          else
            [x]   = spike_crossx_matlab(ts2(:),ts1(:),cfg.binsize,nLags*2+1); % removed the mex file for now, until issue on windows is resolved
          end

          % remove the center peaks from the auto-correlogram
          if indx(1)==indx(2), x(nLags:nLags+1) = 0; end

          if strcmp(cfg.debias,'yes')
            lags = (-nLags:nLags)*cfg.binsize;
            lags = (lags(2:end)+lags(1:end-1))/2;
            T    = nanmin([endTrialLatency(iTrial);cfg.latency(2)])-nanmax([begTrialLatency(iTrial);cfg.latency(1)]);
            sc = (T./(T-abs(lags(:))));
            sc = length(sc)*sc./sum(sc);
            x  = x(:).*sc(:);
          end
          % sum the xcorr
          s(indx(1),indx(2),:) = s(indx(1),indx(2),:) + shiftdim(x(:),-2);
          s(indx(2),indx(1),:) = s(indx(2),indx(1),:) + shiftdim(flipud(x(:)),-2);

          % store individual trials if requested
          if keepTrials
            singleTrials(iTrial,indx(1),indx(2),:) = shiftdim(x(:),-3);
            singleTrials(iTrial,indx(2),indx(1),:) = shiftdim(flipud(x(:)),-3);
          end
        end
      case 'shiftpredictor'
        if iTrial>1
          % symmetric, get x21 to x12 and x22 to x11
          inTrial1_old = spike.trial{indx(1)}==cfg.trials(iTrial-1);
          ts1_old      = sort(spike.time{indx(1)}(inTrial1_old(:) & inWindow1(:)));
          inTrial2_old = spike.trial{indx(2)}==cfg.trials(iTrial-1);
          ts2_old      = sort(spike.time{indx(2)}(inTrial2_old(:) & inWindow2(:)));

          % compute both combinations
          for k = 1:2

            % take chan from this and previous channel
            if k==1
              A = ts1;
              B = ts2_old;
            else
              A = ts1_old;
              B = ts2;
            end
            if ~isempty(A) && ~isempty(B)
              if indx(1)<=indx(2)
                [x]   = spike_crossx_matlab(A(:),B(:),cfg.binsize,nLags*2+1);
              else
                [x]   = spike_crossx_matlab(B(:),A(:),cfg.binsize,nLags*2+1);
              end
              % remove the center peaks from the auto-correlogram
              if indx(1)==indx(2), x(nLags:nLags+1) = 0; end

              if strcmp(cfg.debias,'yes')
                lags = (-nLags:nLags)*cfg.binsize;
                lags = (lags(2:end)+lags(1:end-1))/2;
                T    = nanmin([endTrialLatency(iTrial);cfg.latency(2)])-nanmax([begTrialLatency(iTrial);cfg.latency(1)]);
                sc = (T./(T-abs(lags(:))));
                sc = length(sc)*sc./sum(sc);
                x  = x(:).*sc(:);
              end
              % compute the sum
              s(indx(1),indx(2),:) =  s(indx(1),indx(2),:) + shiftdim(x(:),-2);
              s(indx(2),indx(1),:) =  s(indx(2),indx(1),:) + shiftdim(flipud(x(:)),-2);
              if keepTrials
                singleTrials(iTrial,indx(1),indx(2),:) = shiftdim(x(:)/2,-3);
                singleTrials(iTrial,indx(2),indx(1),:) = shiftdim(flipud(x(:))/2,-3);
              end
            end
          end
        end
    end % symmetric shift predictor loop
  end % combinations
end % trial loop
ft_progress('close')
% multiply the shift sum by a factor so it has the same scale as raw
dofShiftPred = 2*(nTrials-1);
if doShiftPredictor, s = s*nTrials/dofShiftPred; end
switch cfg.outputunit
  case 'proportion'
    sm = repmat(nansum(s,3),[1 1 nLags*2]);
    s = s./sm;
    if keepTrials
      singleTrials       = singleTrials./repmat(shiftdim(sm,-1),[nTrials 1 1 1]);
    end
  case 'center'
    center   = repmat(s(:,:,nLags+1),[1 1 nLags*2]);
    s        = s./center;
    if keepTrials
      singleTrials       = singleTrials./repmat(shiftdim(center,-1),[nTrials 1 1 1]);
    end
end

% return the results
stat.(cfg.method)      = s;
lags = (-nLags:nLags)*cfg.binsize;
lags = (lags(2:end)+lags(1:end-1))/2;
stat.time              = lags;
stat.dimord            = 'chan_chan_time';
if keepTrials
  stat.trial   = singleTrials;
  stat.dimord  = 'trial_chan_chan_time';
end
stat.label       = spike.label(chansel);

% do the general cleanup and bookkeeping at the end of the function

ft_postamble previous   spike
ft_postamble provenance stat
ft_postamble history    stat

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% SUBFUNCTION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [C] = spike_crossx_matlab(tX,tY,binsize,nbins)
tX = sort(tX(:));
tY = sort(tY(:));

minLag = - binsize * (nbins-1) / 2;
j = 0:nbins-1;
B = minLag + j * binsize;
tX(tX<(tY(1)+minLag) | tX>(tY(end)-minLag))   = [];
if isempty(tX)
  C = zeros(1,length(B)-1);
  return;
end
tY(tY>(tX(end)-minLag) | tY<(tX(1)+minLag)) = [];
if isempty(tY)
  C = zeros(1,length(B)-1);
  return;
end
nX = length(tX); nY = length(tY);

% compute all distances at once using a multiplication trick
if (nX*nY)<2*10^7  % allow matrix to grow to about 150 MB, should always work
  D = log(exp(-tX(:))*exp(tY(:)'));
  D = D(:);
  D(abs(D)>abs(minLag)) = [];
  [C] = histc(D,B);
  C(end) = [];
else
  % break it down in pieces such that nX*nY<2*10*7
  k   = 2;
  nXs = round(nX/k);
  while (nXs*nY)>=(2*10^7)
    k = k+1;
    nXs = round(nX/k);
  end

  % get the indices
  steps = round(nX/k);
  begs = 1:steps:steps*k;
  ends = begs+(steps-1);
  rm   = begs>nX;
  begs(rm) = [];
  ends(rm) = [];
  ends(end) = nX;
  nSteps    = length(begs);

  C = zeros(1,length(B));
  for iStep = 1:nSteps
    d = log(exp(-tX(begs(iStep):ends(iStep)))*exp(tY(:)'));
    d = d(:);
    d(abs(d)>abs(minLag)) = [];
    C = C + histc(d,B)';
  end
  C(end) = [];
end

if isempty(C)
  C = zeros(1,length(B)-1);
end


function Next = SurrogateAssistedSelection(Problem,net,p0,p1,Ref,Input,wmax,tr)
% Surrogate-assisted selection for selecting promising solutions

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

% This function is written by Cheng He

    Next  = OperatorGA(Problem,[Input;Ref.decs],{1,15,1,5});
    Label = net.predict(Next);
    a     = tr;
    b     = 1 - tr;
    i     = 0;
    if p0<0.4 || (p1<a&&p0<b)
        while i < wmax
            [~,index] = sort(Label,'descend');
            Input     = Next(index(1:length(Ref)),:);
            Next      = OperatorGA(Problem,[Input;Ref.decs],{1,15,1,5});
            Label     = net.predict(Next);
            i = i+size(Next,1);
        end
        Next = Next(Label>0.9,:);
    elseif p0>b && p1<a
        Next = [];
    elseif p1 > b
        while i<wmax
            [~,index] = sort(Label);
            Input     = Next(index(1:length(Ref)),:);
            Next      = OperatorGA(Problem,[Input;Ref.decs],{1,15,1,5});
            Label     = net.predict(Next);
            i = i+size(Next,1);
        end
        Next = Next(Label<0.1,:);
    else
        Next = Next(randi(end),:);
    end
end


function anal = recomputeChopTseriesMulti(tSeries);
% anal = recomputeChopTseriesMulti(tSeries);
%
% For time course UI, put up a dialog to get
% parameters such as baseline period, peak period,
% onsetDelta, etc., for chopping up a tSeries
% according to condition onsets, then re-chop up
% the time course and add it back to the UI.
%
% If the optional useDefaults flag is set to 1 (default
% 0), then it will forego the dialog and just recompute with
% the default values.
%
% 07/04 ras.
if nargin < 2
    useDefaults = 0;
end
defaults{1} = num2str(tc.timeWindow);
defaults{2} = num2str(tc.bslPeriod);
defaults{3} = num2str(tc.peakPeriod);
defaults{4} = num2str(tc.settings.alpha);
defaults{5} = num2str(tc.settings.onsetDelta);
defaults{6} = num2str(tc.settings.snrConds);
defaults{7} = num2str(tc.settings.glmHRF);
defaults{8} = num2str(tc.settings.normBsl);

if useDefaults==0
	prompt{1} = 'Time Window, in seconds (incl. pre-stimulus onset period):';
	prompt{2} = 'Baseline Period, in seconds:';
	prompt{3} = 'Peak Period, in seconds:';
	prompt{4} = 'Alpha for significant activation (peak vs baseline):';
	prompt{5} = 'Shift onsets relative to time course, in seconds:';
    prompt{6} = 'Use which conditions for computing SNR?';
    prompt{7} = 'HRF to use for GLM? (0 = Boynton gamma, 1 = avg TC for ROI):';
	prompt{8} = 'Normalize all trials during the baseline period? (0 for no, 1 for yes)';

	AddOpts.Resize = 'on';
	AddOpts.Interpreter = 'tex';
	AddOpts.WindowStyle = 'Normal';
	answers = inputdlg(prompt,'Re-chop tSeries Settings...',1,defaults,AddOpts);
else
    answers = defaults;
end

% exit if cancel is selected
if isempty(answers)
    return;
end

% parse the user responses / defaults
twin = str2num(answers{1});
bsl = str2num(answers{2});
peak = str2num(answers{3});
alpha = str2num(answers{4});
onsetDelta = str2num(answers{5});
snrConds = str2num(answers{6});
glmHRF = str2num(answers{7});
normBsl = str2num(answers{8});

anal = er_chopTSeries2(tc.wholeTc,tc.trialInfo,'timeWindow',twin,...
                       'bslPeriod',bsl,'peakPeriod',peak,...
                       'alpha',alpha,'onsetDelta',onsetDelta,...
                       'snrConds',snrConds,'normBsl',normBsl);

% assign over to tc struct (ugly, but there are other fields
% in tc I don't want to erase -- is there a way to eat the whole
% struct in one go?)
tc.wholeTc = anal.wholeTc;
tc.allTcs = anal.allTcs;
tc.meanTcs = anal.meanTcs;
tc.sems = anal.sems;
tc.timeWindow = anal.timeWindow;
tc.peakPeriod = anal.peakPeriod;
tc.bslPeriod = anal.bslPeriod;
tc.Hs = anal.Hs;
tc.ps = anal.ps;
tc.amps = anal.amps;
tc.relamps = anal.relamps;
tc.SNR = anal.SNR;
tc.SNRdb = anal.SNRdb;
tc.settings.alpha = alpha;
tc.settings.onsetDelta = onsetDelta;
tc.settings.snrConds = snrConds;
tc.settings.glmHRF = glmHRF;
tc.settings.normBsl = normBsl;

% update dataTYPES parameters for these scans
if isfield(tc.params,'viewName')
	mrGlobals;
    tmp = findstr('{',tc.params.viewName);
    varName = tc.params.viewName(1:tmp(1)-1);
    if exist(varName,'var')
        view = eval(tc.params.viewName);

		settings.timeWindow = anal.timeWindow;
		settings.peakPeriod = anal.peakPeriod;
		settings.bslPeriod = anal.bslPeriod;
		settings.normBsl = normBsl;
		settings.alpha = alpha;
		settings.onsetDelta = onsetDelta;
		settings.glmHRF = glmHRF;

		for s = tc.params.scans
            er_setParams(view,settings,s);
        end
    end
end

% assign back to the UI and refresh
set(gcf,'UserData',tc);
timeCourseUI;

return


function SNR = hsvargplvmShowSNR(model, layers, displ)

if nargin < 3
    displ = true;
end

if nargin < 2 || isempty(layers)
    layers = 1:model.H;
end

for h=layers
    if displ
        fprintf('# SNR Layer %d\n',h)
    end
    for m=1:model.layer{h}.M
        if isfield(model.layer{h}.comp{m}, 'mOrig')
            varY = var(model.layer{h}.comp{m}.mOrig(:));
        else
            varY = var(model.layer{h}.comp{m}.m(:));
        end
        beta = model.layer{h}.comp{m}.beta;
        SNR{h}(m) = varY * beta;
        if displ
            fprintf('    Model %d: %f  (varY=%f, 1/beta=%f)\n', m, SNR{h}(m), varY, 1/beta)
        end
    end
end


function mvroi_mdsfig_plot2(CLUSTER,SPEC,sigmatavg,sigmatdif,titlestr)
% ::
%
%    mvroi_mdsfig_plot2(CLUSTER,SPEC,DATA.CORRELS.AVGSTATS.sigmat_uncorrected,DATA.CORRELS.DIFSTATS.sigmat_uncorrected,titlestr)
%
% :Example:
% ::
%
%    mvroi_mdsfig_plot2(DATA.CLUSTER,DATA.SPEC,DATA.CORRELS.AVGSTATS.sigmat_uncorrected,DATA.CORRELS.DIFSTATS.sigmat_uncorrected,'Uncorrected');
%
scnsize = get(0,'ScreenSize');
figure('position',[50 50 scnsize(3)-100 scnsize(4)/2],'color','white')

subplot(1,2,1);
set(gca,'FontSize',14)
mdsfig(CLUSTER.Gs(:,1:2),CLUSTER.names,CLUSTER.classes,sigmatavg);
title(['Average across conditions, ', titlestr])

subplot(1,2,2);
set(gca,'FontSize',14)
mdsfig(CLUSTER.Gs(:,1:2),CLUSTER.names,CLUSTER.classes,sigmatdif);
title(sprintf(['Contrast [' repmat('%3.0f ',1,size(SPEC.comps,2)) '] across conditions, %s'],SPEC.comps(1,:),SPEC.comptitle))

return


%  This program is free software; you can redistribute it and/or modify
%  it under the terms of the GNU General Public License as published by
%  the Free Software Foundation; either version 2 of the License, or
%  (at your option) any later version.
%
%  This program is distributed in the hope that it will be useful,
%  but WITHOUT ANY WARRANTY; without even the implied warranty of
%  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%  GNU General Public License for more details.
%
%  You should have received a copy of the GNU General Public License
%  along with this program; if not, write to the Free Software
%  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
%function fmpart
%FMPART	Unit test for the function FMPAR.

%	O. Lemoine - February 1996.

N=256;
t=1:N;

% NARGIN=2
P1=[0.5 -0.007 2.7*10^(-5)];
[x,iflaw]=fmpar(N,P1);

if any(abs(iflaw-(P1(1)+P1(2).*t+P1(3).*t.^2)')>sqrt(eps))~=0,
  error('fmpar test 1 failed');
end

% NARGIN=4
P1=[10 0.4];
P2=[120 0.05];
P3=[246 0.39];
[x,iflaw]=fmpar(N,P1,P2,P3);
if abs(iflaw(P1(1))-P1(2))>sqrt(eps) | abs(iflaw(P2(1))-P2(2))>sqrt(eps) | ...
   abs(iflaw(P3(1))-P3(2))>sqrt(eps),
  error('fmpar test 2 failed');
end

P1=[20 0.13];
P2=[145 0.45];
P3=[226 0.19];
[x,iflaw]=fmpar(N,P1,P2,P3);
if abs(iflaw(P1(1))-P1(2))>sqrt(eps) | abs(iflaw(P2(1))-P2(2))>sqrt(eps) | ...
   abs(iflaw(P3(1))-P3(2))>sqrt(eps),
  error('fmpar test 3 failed');
end

N=251;
t=1:N;

% NARGIN=2
P1=[0.5 -0.007 2.7*10^(-5)];
[x,iflaw]=fmpar(N,P1);

if any(abs(iflaw-(P1(1)+P1(2).*t+P1(3).*t.^2)')>sqrt(eps))~=0,
  error('fmpar test 4 failed');
end

% NARGIN=4
P1=[10 0.4];
P2=[120 0.05];
P3=[246 0.39];
[x,iflaw]=fmpar(N,P1,P2,P3);
if abs(iflaw(P1(1))-P1(2))>sqrt(eps) | abs(iflaw(P2(1))-P2(2))>sqrt(eps) | ...
   abs(iflaw(P3(1))-P3(2))>sqrt(eps),
  error('fmpar test 5 failed');
end

P1=[20 0.13];
P2=[145 0.45];
P3=[226 0.19];
[x,iflaw]=fmpar(N,P1,P2,P3);
if abs(iflaw(P1(1))-P1(2))>sqrt(eps) | abs(iflaw(P2(1))-P2(2))>sqrt(eps) | ...
   abs(iflaw(P3(1))-P3(2))>sqrt(eps),
  error('fmpar test 6 failed');
end


function varargout = setxor(varargin)
% Adds support for 'stable' input flag with one output
% Also, z = vertcat(z, y); is used instead of z = [z; y]; because of an
% Octave bug when vertically concatenating a 1x0 char array with another
% char array; see https://savannah.gnu.org/bugs/index.php?52542. It seems
% to be avoided using `vertcat` instead of `;`
if iscell(varargin{1}) && ~iscell(varargin{2}), varargin{2} = {varargin{2}}; end
if iscell(varargin{2}) && ~iscell(varargin{1}), varargin{1} = {varargin{1}}; end
if nargin>=3 && strcmp(varargin{end},'stable')
    % This is almost like setdiff:
    if strcmp(varargin{3},'rows')
        y = [varargin{1}; varargin{2}];
        [a, ~, x] = unique(y,'rows');
        x1 = x(1:size(varargin{1},1));
        x2 = x(size(varargin{1},1)+1:end);
    else
        y = [varargin{1}(:); varargin{2}(:)];
        [a, ~, x] = unique(y);
        x1 = x(1:numel(varargin{1}));
        x2 = x(numel(varargin{1})+1:end);
    end
    ind = ~ismember(x1, x2);
    x = x1(ind);
    if ~isempty(x)
        x = x(~any(triu(bsxfun(@eq, x, x.'),1)));
    end
    if strcmp(varargin{3},'rows')
        y = a(x,:);
    else
        y = a(x);
        y = y(:);
    end
    % Keep partial output:
    z = y;
    % Same with inputs reversed:
    if strcmp(varargin{3},'rows')
        y = [varargin{2}; varargin{1}];
        [a, ~, x] = unique(y,'rows');
        x2 = x(1:size(varargin{2},1));
        x1 = x(size(varargin{2},1)+1:end);
    else
        y = [varargin{2}(:); varargin{1}(:)];
        [a, ~, x] = unique(y);
        x2 = x(1:numel(varargin{2}));
        x1 = x(numel(varargin{2})+1:end);
    end
    ind = ~ismember(x2, x1);
    x = x2(ind);
    if ~isempty(x)
        x = x(~any(triu(bsxfun(@eq, x, x.'),1)));
    end
    if strcmp(varargin{3},'rows')
        y = a(x,:);
    else
        y = a(x);
        y = y(:);
    end
    % Join partial outputs:
    z = vertcat(z, y);
    if ~strcmp(varargin{3},'rows') && isrow(varargin{1}) && isrow(varargin{2})
        z = z.';
    end
    varargout{1} = z;
else
    if strcmp(varargin{end},'sorted'), varargin(end) = []; end
    varargout = cell(1,max(nargout,1)); % if called without outputs, nargout will be zero. In that case we return one output
    [varargout{:}] = builtin('setxor', varargin{:});
end
end


function h = plus(f, g)
%+   Plus for BALLFUN.
% F + G adds F and G. F and G can be scalars or BALLFUNs.

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

fIsBallfun = isa(f, 'ballfun');
gIsBallfun = isa(g, 'ballfun');

% Empty check
if isempty( f )
    h = g;
    if ~gIsBallfun
        h = ballfun(h);
    end
    return
end

% Empty check
if isempty( g )
    h = f;
    if ~fIsBallfun
        h = ballfun(h);
    end
    return
end

if (fIsBallfun && gIsBallfun)
    [mf,nf,pf] = size(f);
    [mg,ng,pg] = size(g);
    m = max(mf,mg);
    n = max(nf,ng);
    p = max(pf,pg);
    X = zeros(m,n,p);
    X(1:mf,floor(n/2)+1-floor(nf/2):floor(n/2)+nf-floor(nf/2),floor(p/2)+1-floor(pf/2):floor(p/2)+pf-floor(pf/2)) = f.coeffs;
    X(1:mg,floor(n/2)+1-floor(ng/2):floor(n/2)+ng-floor(ng/2),floor(p/2)+1-floor(pg/2):floor(p/2)+pg-floor(pg/2)) = ...
    X(1:mg,floor(n/2)+1-floor(ng/2):floor(n/2)+ng-floor(ng/2),floor(p/2)+1-floor(pg/2):floor(p/2)+pg-floor(pg/2)) + g.coeffs;
    h = ballfun(X,'coeffs');
elseif (fIsBallfun && isnumeric(g))
    S = size(f);
    X = f.coeffs;
    % Add the constant g
    X(1,floor(S(2)/2)+1,floor(S(3)/2)+1) = X(1,floor(S(2)/2)+1,floor(S(3)/2)+1) + g;
    h = ballfun(X,'coeffs');
elseif (isnumeric(f) && gIsBallfun)
    S = size(g);
    X = g.coeffs;
    % Add the constant f
    X(1,floor(S(2)/2)+1,floor(S(3)/2)+1) = X(1,floor(S(2)/2)+1,floor(S(3)/2)+1) + f;
    h = ballfun(X,'coeffs');
else
    error('BALLFUN:plus:unknown', ...
          ['Undefined function ''plus'' for input arguments of type ' ...
           '%s and %s.'], class(f), class(g));
end
end


function [bdEdge,bdNode,isBdEdge,isBdNode] = findboundaryedge3(edge,elem2edge,bdFlag)
%% FINDBOUNDARYEDGE3 find boundary edges of a three dimensional mesh
%
% [bdEdge,bdNode] = findboundaryedge3(edge,elem2edge,bdFlag) finds all boundary
% edges and boundary nodes of 3D mesh using bdFlag.
%
% For a 2D mesh, use findboundary
%
% See also findboundary3, findboundary
%
% Copyright (C) Long Chen. See COPYRIGHT.txt for details.

% Find boundary edges and nodes
NE = size(edge,1);
N = max(edge(:));
isBdEdge = false(NE,1);
isBdNode = false(N,1);
isBdEdge(elem2edge(bdFlag(:,1) == 1,[4,5,6])) = true;
isBdEdge(elem2edge(bdFlag(:,2) == 1,[2,3,6])) = true;
isBdEdge(elem2edge(bdFlag(:,3) == 1,[1,3,5])) = true;
isBdEdge(elem2edge(bdFlag(:,4) == 1,[1,2,4])) = true;
bdEdge = edge(isBdEdge,:);
isBdNode(bdEdge) = true;
bdNode = find(isBdNode);


function printSampleStats(sampledModel, commonModel, sampleNames, fileName)
% Prints out sample statistics for multiple samples
%
% USAGE:
%
%    printSampleStats(samples, commonModel, sampleNames, fileName)
%
% INPUTS:
%    sampledModel:   Samples to plot
%    commonModel:    COBRA model structure
%    sampleNames:    Names of the models
%
% OPTIONAL INPUT:
%    fileName:       Name of tab delimited CSV file to generate
%                    (Default = print to command window)
%
% .. Author: - Markus Herrgard

if nargin > 3
    fid = fopen(fileName, 'w');
else
    fid = 1;
end

sampleStats = calcSampleStats(sampledModel);

fprintf(fid, 'Rxn\t');
if (isfield(commonModel, 'subSystems'))
    fprintf(fid, 'Subsystem\t');
end

for i = 1:length(sampleNames)
    fprintf(fid, '%s-mode\t', sampleNames{i});
end
for i = 1:length(sampleNames)
    fprintf(fid, '%s-mean\t', sampleNames{i});
end
for i = 1:length(sampleNames)
    fprintf(fid, '%s-median\t', sampleNames{i});
end
for i = 1:length(sampleNames)
    fprintf(fid, '%s-std\t', sampleNames{i});
end
fprintf(fid, '\n');

for i = 1:length(commonModel.rxns)
    fprintf(fid, '%s\t', commonModel.rxns{i});
    if (isfield(commonModel, 'subSystems'))
        fprintf(fid, '%s\t', strjoin(commonModel.subSystems{i},';'));
    end
    %for j = 1:length(samples)
    fprintf(fid, '%8.6f\t', sampleStats.mode(i));
    %end
    %for j = 1:length(samples)
    fprintf(fid, '%8.6f\t', sampleStats.mean(i, :));
    %end
    %for j = 1:length(samples)
    fprintf(fid, '%8.6f\t', sampleStats.median(i, :));
    %end
    %for j = 1:length(samples)
    fprintf(fid, '%8.6f\t', sampleStats.std(i, :));
    %end
    fprintf(fid, '\n');
end
if (fid > 1)
    fclose(fid);
end


% Displays a matrix in a pop-up window for convenient editing.
% This control presents an m-by-n table of matrix entries.

% Copyright 2008-2010 Levente Hunyadi
classdef MatrixEditor < UIControl
    properties
        Type = PropertyType.empty(1,0);
    end
    properties (Dependent)
        Control;
        Item;
        % Whether to show transpose of the matrix instead of actual matrix.
        Transpose;
        % Whether matrix is read-only.
        ReadOnly;
    end
    properties (Access = protected)
        MatrixTranspose = false;
        Matrix;
        Table;
        JideTableModel;
        JideTable;
    end
    properties (Access = private)
        Menu;
    end
    methods
        function self = MatrixEditor(varargin)
            self = self@UIControl(varargin{:});
        end

        function self = Instantiate(self, parent)
            if nargin < 2
                parent = figure;
            end

            % container panel
            panel = uipanel(parent, ...
                'Units', 'normalized', ...
                'Position', [0 0 1 1], ...
                'Tag', '__MatrixEditor__', ...
                'UserData', self);

            % initialization
            com.mathworks.mwswing.MJUtilities.initJIDE;
            com.jidesoft.grid.CellRendererManager.initDefaultRenderer();
            com.jidesoft.grid.CellEditorManager.initDefaultEditor();

            % table model
            self.JideTableModel = handle(javax.swing.table.DefaultTableModel(), 'CallbackProperties');
            set(self.JideTableModel, 'TableChangedCallback', @MatrixEditor.OnTableChanged);

            % JIDE table grid control
            self.JideTable = objectEDT('com.jidesoft.grid.ContextSensitiveTable', self.JideTableModel);
            self.JideTable.setCellSelectionEnabled(true);

            pixelpos = getpixelposition(panel);
            [control,self.Table] = javacomponent(self.JideTable, [10 10 pixelpos(3)-20 pixelpos(4)-20], panel); %#ok<ASGLU>
            set(self.Table, ...
                'Units', 'normalized');

            % menu
            menu = uimenu(ancestor(parent, 'figure'), ...
                'Label', 'Matrix', ...
                'Callback', @self.OnMatrixMenuClicked);
            self.Menu = struct( ...
                'ShowTranspose', uimenu(menu, 'Label', 'Show transpose', 'Callback', @self.OnShowTranspose), ...
                'AddColumnBefore', uimenu(menu, 'Label', 'Add column before', 'Callback', @self.OnAddColumnBefore), ...
                'AddColumnAfter', uimenu(menu, 'Label', 'Add column after', 'Callback', @self.OnAddColumnAfter), ...
                'AddRowBefore', uimenu(menu, 'Label', 'Add row before', 'Callback', @self.OnAddRowBefore), ...
                'AddRowAfter', uimenu(menu, 'Label', 'Add row after', 'Callback', @self.OnAddRowAfter), ...
                'RemoveColumn', uimenu(menu, 'Label', 'Remove column', 'Callback', @self.OnRemoveColumn), ...
                'RemoveRow', uimenu(menu, 'Label', 'Remove row', 'Callback', @self.OnRemoveRow));
            self.OnMatrixMenuClicked();
        end

        function control = get.Control(self)
            control = self.Table;
        end

        function t = get.Transpose(self)
            t = self.MatrixTranspose;
        end

        function self = set.Transpose(self, t)
            validateattributes(t, {'logical'}, {'scalar'});
            item = self.Item;
            self.MatrixTranspose = t;
            self.Item = item;
        end

        function r = get.ReadOnly(self)
            r = ~self.JideTable.isEnabled();
        end

        function self = set.ReadOnly(self, r)
            validateattributes(r, {'logical'}, {'scalar'});
            self.JideTable.setEnabled(~r);
        end

        function matrix = get.Item(self)
            matrix = self.Matrix;
            if self.MatrixTranspose
                matrix = transpose(matrix);
            end
        end

        function self = set.Item(self, matrix)
            validateattributes(matrix, {'numeric','logical'}, {'2d'});
            if self.MatrixTranspose
                matrix = transpose(matrix);
            end
            self.Matrix = matrix;
            if isempty(self.Type)
                self.Type = PropertyType.AutoDiscover(matrix);
            end
            data = self.Type.GetJavaVectorOfVectors(matrix);
            if size(matrix,2) > 0
                list = java.util.Arrays.asList(javaArray('java.lang.String', size(matrix,2)));  % no labels
                labels = java.util.Vector(list);
            else
                labels = java.util.Vector();
            end
            self.JideTableModel.setDataVector(data, labels);
            self.JideTable.doLayout();
        end

        function self = set.Type(self, type)
            validateattributes(type, {'PropertyType'}, {'scalar'});
            self.Type = type;
        end

        function item = GetItemAsString(self)
            validateattributes(self, {'MatrixEditor'}, {'scalar'});
            matrix = self.Matrix;
            if self.MatrixTranspose
                matrix = transpose(matrix);
            end
            item = mat2str(matrix);
        end
    end
    methods (Static, Access = private)
        function self = FindEditor(obj)
        % Finds the editor object to which a table model object belongs.
        %
        % Input arguments:
        % obj:
        %    a javax.swing.table.DefaultTableModel instance
            h = findobjuser(@(userdata) userdata.JideTableModel == obj, '__MatrixEditor__');  % find which HGO contains the object for which the callback is executing
            self = get(h, 'UserData');
        end
    end
    methods (Static)
        function OnTableChanged(source, event)
        % Fired when a matrix entry has been changed.
            javarow = event.getFirstRow();
            javacol = event.getColumn();
            if javarow < 0 || javacol < 0 || event.getType() ~= javax.swing.event.TableModelEvent.UPDATE
                return;  % no selection
            end
            self = MatrixEditor.FindEditor(source);
            row = javarow + 1;
            col = javacol + 1;

            entryvalue = source.getValueAt(javarow, javacol);
            value = entryvalue;  % represented as-is by default
            if ischar(entryvalue)  % represented as text
                if isnumeric(self.Type)
                    value = str2double(entryvalue);
                    if isnan(value)
                        value = [];
                    end
                elseif islogical(self.Type)
                    switch entryvalue
                        case 'true'
                            value = true;
                        case 'false'
                            value = false;
                        otherwise
                            value = str2double(entryvalue);
                            if isnan(value)
                                value = [];
                            end
                    end
                end
            end
            if ~isempty(value)
                try
                    value = self.Type.ConvertFromMatLab(value);
                    self.Matrix(row,col) = value;  % persist changes
                catch %#ok<CTCH>
                    % no action
                end
            end
            if ischar(entryvalue)
                value = self.Matrix(row,col);
                javavalue = self.Type.GetPrimitiveJavaValue(value);
                if javavalue ~= source.getValueAt(javarow, javacol)  % do not trigger change event if value to set is equivalent to value in cell
                    source.setValueAt(javavalue, javarow, javacol);
                end
            end
        end
    end
    methods (Access = private)
        function self = OnMatrixMenuClicked(self, source, event) %#ok<INUSD>
        % Fired when the Matrix menu (or the context menu) is to be shown.
            if ~isempty(self.Type) && ~strcmp(self.Type.Shape, 'column')
                addcolstate = 'on';
            else
                addcolstate = 'off';
            end
            if ~isempty(self.Type) && ~strcmp(self.Type.Shape, 'row')
                addrowstate = 'on';
            else
                addrowstate = 'off';
            end
            if ~isempty(self.Type) && ~strcmp(self.Type.Shape, 'column') ...  % cannot remove single column for column vector
                    && size(self.Matrix,2) > 0 ...     % check if there is anything to remove
                    && ~isempty(self.GetSelectedColumn())
                remcolstate = 'on';
            else
                remcolstate = 'off';
            end
            if ~isempty(self.Type) && ~strcmp(self.Type.Shape, 'row') ...  % cannot remove single row for row vector
                    && size(self.Matrix,1) > 0 ...  % check if there is anything to remove
                    && ~isempty(self.GetSelectedRow())
                remrowstate = 'on';
            else
                remrowstate = 'off';
            end
            set(self.Menu.AddColumnBefore, 'Enable', addcolstate);
            set(self.Menu.AddColumnAfter, 'Enable', addcolstate);
            set(self.Menu.AddRowBefore, 'Enable', addrowstate);
            set(self.Menu.AddRowAfter, 'Enable', addrowstate);
            set(self.Menu.RemoveColumn, 'Enable', remcolstate);
            set(self.Menu.RemoveRow, 'Enable', remrowstate);
        end

        function self = OnShowTranspose(self, source, event) %#ok<INUSD>
            self.Transpose = ~self.Transpose;  % call setter method
        end

        function self = OnAddColumnBefore(self, source, event) %#ok<INUSD>
            index = self.GetSelectedColumn();
            if isempty(index)
                index = 0;
            end
            self.AddColumnAt(index);  % index ranges from 0 to column count-1
        end

        function self = OnAddColumnAfter(self, source, event) %#ok<INUSD>
            index = self.GetSelectedColumn();
            if ~isempty(index)
                self.AddColumnAt(index+1);  % index+1 ranges from 1 to column count
            else
                self.AddColumnAt(size(self.Matrix,2));
            end
        end

        function self = OnAddRowBefore(self, source, event) %#ok<INUSD>
            index = self.GetSelectedRow();
            if isempty(index)
                index = 0;
            end
            self.AddRowAt(index);  % index ranges from 0 to row count-1
        end

        function self = OnAddRowAfter(self, source, event) %#ok<INUSD>
            index = self.GetSelectedRow();
            if ~isempty(index)
                self.AddRowAt(index+1);  % index+1 ranges from 1 to row count
            else
                self.AddRowAt(size(self.Matrix,1));
            end
        end

        function self = OnRemoveColumn(self, source, event) %#ok<INUSD>
            index = self.GetSelectedColumn();  % index ranges from 0 to column count-1
            if isempty(index)
                return;
            end
            matrix = self.Matrix;
            matrix(:,index+1) = [];
            self.Matrix = matrix;
            data = self.JideTableModel.getDataVector();
            for k = 0 : size(matrix,1)-1
                row = data.elementAt(k);
                row.removeElementAt(index);
            end
            self.JideTableModel.setColumnCount(size(matrix,2));
            self.JideTable.repaint();
        end

        function self = OnRemoveRow(self, source, event) %#ok<INUSD>
            index = self.GetSelectedRow();  % index ranges from 0 to row count-1
            if isempty(index)
                return;
            end
            matrix = self.Matrix;
            matrix(index+1,:) = [];
            self.Matrix = matrix;
            self.JideTableModel.removeRow(index);
            self.JideTable.repaint();
        end

        function index = GetSelectedColumn(self)
        % Returns the currently selected column.
        %
        % Output arguments:
        % index:
        %    a zero-based index
            index = self.JideTable.getSelectedColumn();
            if index < 0  % no row is selected
                index = [];
            end
        end

        function index = GetSelectedRow(self)
        % Returns the currently selected row.
        %
        % Output arguments:
        % index:
        %    a zero-based index
            index = self.JideTable.getSelectedRow();
            if index < 0  % no row is selected
                index = [];
            end
        end

        function self = AddColumnAt(self, index)
        % Add new column at the specified index.
        % Items whose index is greater than the specified index are shifted
        % to the right.
        %
        % Input argument:
        % index:
        %    an insertion index between 0 (to insert as first column) and
        %    size(self.Matrix,2) (to insert as last column)
            matrix = self.Matrix;
            if isempty(matrix)
                ix1 = [];
                ix2 = [];
            else
                ix1 = 1 : index;
                ix2 = index+1 : size(matrix,2);
            end
            matrix = [ matrix(:,ix1) , zeros(size(matrix,1),1) , matrix(:,ix2) ];
            self.Matrix = matrix;
            data = self.JideTableModel.getDataVector();
            for k = 0 : size(matrix,1)-1
                row = data.elementAt(k);
                row.insertElementAt(self.Type.GetPrimitiveJavaValue(0), index);
            end
            self.JideTableModel.setColumnCount(size(matrix,2));
            self.JideTable.repaint();
        end

        function self = AddRowAt(self, index)
        % Add new row at the specified index.
        % Items whose index is greater than the specified index are shifted
        % downwards.
        %
        % Input argument:
        % index:
        %    an insertion index between 0 (to insert as first row) and
        %    size(self.Matrix,1) (to insert as last row)
            matrix = self.Matrix;
            if isempty(matrix)
                ix1 = [];
                ix2 = [];
            else
                ix1 = 1 : index;
                ix2 = index+1 : size(matrix,1);
            end
            matrix = [ matrix(ix1,:) ; zeros(1,size(matrix,2)) ; matrix(ix2,:) ];
            self.Matrix = matrix;
            data = self.Type.GetJavaVector(zeros(1,size(matrix,2)));
            self.JideTableModel.insertRow(index, data);
            self.JideTable.repaint();
        end
    end
end


function isargcoord(varargin)
%ISARGCOORD throws an error if not a point in an euclidian coordinate system
%
%   Usage: isargcoord(args)
%
%   Input parameters:
%       args        - list of args
%
%   ISARGCOORD(args) tests if all given args are a point in an euclidian
%   coordinate system. This means they have to be a vector with a length of 1,2
%   or 3. Returns an error otherwise.
%
%   See also: isargvector

%*****************************************************************************
% The MIT License (MIT)                                                      *
%                                                                            *
% Copyright (c) 2010-2019 SFS Toolbox Developers                             *
%                                                                            *
% Permission is hereby granted,  free of charge,  to any person  obtaining a *
% copy of this software and associated documentation files (the "Software"), *
% to deal in the Software without  restriction, including without limitation *
% the rights  to use, copy, modify, merge,  publish, distribute, sublicense, *
% and/or  sell copies of  the Software,  and to permit  persons to whom  the *
% Software is furnished to do so, subject to the following conditions:       *
%                                                                            *
% The above copyright notice and this permission notice shall be included in *
% all copies or substantial portions of the Software.                        *
%                                                                            *
% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
% IMPLIED, INCLUDING BUT  NOT LIMITED TO THE  WARRANTIES OF MERCHANTABILITY, *
% FITNESS  FOR A PARTICULAR  PURPOSE AND  NONINFRINGEMENT. IN NO EVENT SHALL *
% THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
% LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT, TORT  OR OTHERWISE, ARISING *
% FROM,  OUT OF  OR IN  CONNECTION  WITH THE  SOFTWARE OR  THE USE  OR OTHER *
% DEALINGS IN THE SOFTWARE.                                                  *
%                                                                            *
% The SFS Toolbox  allows to simulate and  investigate sound field synthesis *
% methods like wave field synthesis or higher order ambisonics.              *
%                                                                            *
% https://sfs.readthedocs.io                            sfstoolbox@gmail.com *
%*****************************************************************************

%% ===== Checking for vector =============================================
for ii = 1:nargin
    if ~isnumeric(varargin{ii}) || ~isvector(varargin{ii}) || ...
        length(varargin{ii})>3
        error('%s need to be a vector with length <4.',inputname(ii));
    end
end


function [mm, order] = vargplvmReduceModel(model, P, dims)

% VARGPLVMREDUCEMODEL prunes out dimensions of the model.
% FORMAT
% DESC order the latent dimensions acorrding to the inputScales and
% reduces the model to have smaller number of latent dimensions.
% ARG model : the model to be reduced.
% ARG P : the number of dimensions to move to (setting to model.q will
% just reorder the dimensions in the model).
% ARG dims : (optional) explicit set of dimensions to use
% RETURN model : the model with the reduced number of dimensions.
%
% COPYRIGHT : Michalis K. Titsias, 2009
%
% MODIFICATIONS : Neil D. Lawrence, 2009, Patrick Sauer, 2011
%
% SEEALSO : vargplvmCreate

% VARGPLVM

if nargin == 3
    P = length(dims);
end

% create temporary model
options = vargplvmOptions('dtcvar');
options.kern =[];
options.numActive = model.k;
if ~strcmp(model.kern.type,'cmpnd')
  options.kern = model.kern.type;
elseif strcmp(model.kern.type,'cmpnd')
  options.kern{1} = model.kern.comp{1}.type;
  for i = 2:length(model.kern.comp)
    options.kern{i} = model.kern.comp{i}.type;
  end
end

if isstruct( model.betaTransform )
    options.betaTransform = model.betaTransform;
end

mm = vargplvmCreate(P, model.d, model.y, options);
N = size(model.vardist.means,1);

if ~strcmp(model.kern.type,'cmpnd')
  %
  if strcmp(model.kern.type,'rbfardjit') | strcmp(model.kern.type,'linard2') | strcmp(model.kern.type,'rbfard2')
    %
    if nargin == 2
        [vals, order] = sort(-model.kern.inputScales);
        mm.kern.inputScales = model.kern.inputScales(order(1:P));
    else
        order = [dims setdiff(1:length(model.kern.inputScales), dims)];
        mm.kern.inputScales = model.kern.inputScales(order);
    end
    %
  end
  %
else
  %
  for i = 1:length(model.kern.comp)
    %
    if strcmp(model.kern.comp{i}.type,'rbfardjit') | strcmp(model.kern.comp{i}.type,'linard2') | strcmp(model.kern.comp{i}.type,'rbfard2')
      %
      if nargin == 2
          [vals, order] = sort(-model.kern.comp{i}.inputScales);
          mm.kern.comp{i}.inputScales = model.kern.comp{i}.inputScales(order(1:P));
      else
          order = [dims setdiff(1:length(model.kern.comp{i}.inputScales), dims)];
          mm.kern.comp{i}.inputScales = model.kern.comp{i}.inputScales(order);
      end
      % you order only wrt the first ARD kernel you find
      break;
      %
    end
    %
  end
  %
end

mm.vardist.means  = model.vardist.means(:,order(1:P));
mm.vardist.covars = model.vardist.covars(:,order(1:P));

mm.X_u = model.X_u(:,order(1:P));
mm.X   = model.vardist.means(:,order(1:P));
mm.inputSclsOrder = order;

initParams   = vargplvmExtractParam(mm);
mm.numParams = length(initParams);

% This forces kernel computation.
mm = vargplvmExpandParam(mm, initParams);


%==============================================================================
% This code is part of the Matlab-based toolbox
% FAIR - Flexible Algorithms for Image Registration.
% For details see
% - https://github.com/C4IR and
% - http://www.siam.org/books/fa06/
%==============================================================================
% This is the kernel of FAIR
%
% the kernel contains
%   contents.m	this file
%
%   data                data and examples included in the toolbox
%   distances           various distance measures (SSD etc.) and their administration
%   examples            numerous examples (organized by chapters of the FAIR book)
%   imgModel            various image models (interpolation etc.) and their administration
%   landmarks           tools for landmark registration
%   matrixfree	        tools for matrixfree operations
%   numerics            numerical tools including optimization
%   regularizers        various regularizer (elastic etc.) and their administration
%   tools               numerous tools
%   transformations     various transformation models (affine etc.) and their administration
%==============================================================================
function debit = contents
if nargout == 0, help(mfilename); return; end;

debit = {
  'contents.m'
};
%==============================================================================


close all
clear

%------------------------------------------------------------------------------------------
nb = 1000;%Total number of initial states
nmpcd=zeros(1,nb);%Store computing time for one time-step for NMPC-DCBF
impcd=zeros(1,nb);%Store computing time for one time-step for iMPC-DCBF
nmpcinf=zeros(1,1);%Store infeasible rate for one time-step for NMPC-DCBF
impcinf=zeros(1,1);%Store infeasible rate for one time-step for iMPC-DCBF
i=24;%i is number of horozon
ts = 1;%Total time steps
load(gamma1_0p6\InitialStateData');%Load the generated 1000 initial states
[a, b, c, d]=compare(i,ts,nb,InitialMat);%Involves NMPC-DCBF and iMPC-DCBF
nmpcd(1,:)=a;
impcd(1,:)=b;
nmpcinf(1,1)=c;
impcinf(1,1)=d;
nmpcplot1=nmpcd(1,:);
nmpcplot1(nmpcplot1==-1)=[];%Eliminate infeasible one time-step trajectories
impcplot1=impcd(1,:);
impcplot1(impcplot1==-1)=[];%Eliminate infeasible one time-step trajectories

save('timecom24','nmpcplot1','impcplot1');
save('feasibility24','nmpcinf','impcinf');

distnmpc = fitdist(nmpcplot1','Normal');
distimpc = fitdist(impcplot1','Normal');
mu1=distnmpc.mu;%Get mean of sample of computing time for NMPC-DCBF
mu2=distimpc.mu;%Get mean of sample of computing time for iMPC-DCBF
sigma1=distnmpc.sigma;%Get variance of sample of computing time for NMPC-DCBF
sigma2=distimpc.sigma;%Get variance of sample of computing time for iMPC-DCBF

%Initialize atmosphere parameters
function [tnmpc, timpc, ratiotnmpc, ratiotimpc]=compare(N11,ttsim,samplen,InitialMat)
tnmpc=[];
timpc=[];
for i=1:samplen
N1=N11;
tsim=ttsim;
xini1=InitialMat(1,i);
yini1=InitialMat(2,i);
thetaini1=InitialMat(3,i);
vini1=InitialMat(4,i);
x01=[xini1;yini1;thetaini1;vini1];
x02=[xini1 yini1 thetaini1 vini1];
t1 = nmpcdcbf(x01, N1, tsim);
t2 = impcdcbf(x02, N1, tsim);
tindex=[N11 i];
disp(tindex);
tnmpc=[tnmpc t1];%Computing time for NMPC-DCBF
timpc=[timpc t2];%Computing time for iMPC-DCBF
end
nnmpc1 = length(tnmpc);
nimpc1 = length(timpc);
tnmpcs = tnmpc;
timpcs = timpc;
tnmpcs(tnmpcs==-1)=[];
timpcs(timpcs==-1)=[];
nnmpc2 = length(tnmpcs);
nimpc2 = length(timpcs);
ratiotnmpc = (nnmpc1-nnmpc2)/nnmpc1;%Infeasible rate for NMPC-DCBF
ratiotimpc = (nimpc1-nimpc2)/nimpc1;%Infeasible rate for iMPC-DCBF
end

function t1 = nmpcdcbf(x00, N1, ttsim)
%% General Flags
run_nmpc_dcbf_one = true;
run_nmpc_dcbf_multiple = true;

%% Setup and Parameters
x0 = x00;
dt = 0.1;
time_total = ttsim *dt;
P = zeros(4,4);%Weight matrix P
P(1,1) = 10; P(2,2) = 10; P(3,3) = 10;P(4,4) = 10;
Q = zeros(4,4);%Weight matrix Q
Q(1,1) = 10; Q(2,2) = 10; Q(3,3) = 10;Q(4,4) = 10;
R = zeros(4,4);%Weight matrix R
R(1,1) = 1; R(2,2) = 1;R(3,3) = 1000;R(4,4) = 1000;
%Variables range as below
xmin = [-10; -10; -10;-10];
xmax = [10; 10; 10;10];
umin = [-7; -5;-inf;-inf];
umax = [7; 5;inf;inf];

%% Discrete-time unicycle model
system.dt = dt;
system.A = [1 0 0 0;
    0 1 0 0;
    0 0 1 0;
    0 0 0 1];
system.B = [x0(4,1)*cos(x0(3,1))*dt;
    x0(4,1)*sin(x0(3,1))*dt;
    0;
    0];
system.C =[0 0 0 0;
    0 0 0 0;
    1*dt 0 0 0;
    0 1*dt 0 0];
system.xl = xmin;
system.xu = xmax;
system.ul = umin;
system.uu = umax;

%% NMPC-DCBF parameters
params_nmpc_dcbf.Q = Q;
params_nmpc_dcbf.R = R;
params_nmpc_dcbf.P = P;
params_nmpc_dcbf.gamma1 = 0.6;
params_nmpc_dcbf.gamma2 = 0.6;

%% Obstacle
obs.pos1 = [0; 0];
obs.r1 = 1;

%% Simulate NMPC-DCBF with other N
params_nmpc_dcbf.N = N1;
if run_nmpc_dcbf_one
    fprintf('Run NMPC-DCBF-24\n');
    controller_nmpc_dcbf_multiple24 = NMPCDCBF1(x0, system, params_nmpc_dcbf);%Highest order mcbf=2
    controller_nmpc_dcbf_multiple24.obs = obs;
    controller_nmpc_dcbf_multiple24.sim(time_total);
end
t1=controller_nmpc_dcbf_multiple24.tt;
end

function t2=impcdcbf(x00, N1, ttsim)
t2=0;
xo = 0;
yo = 0;
r = 1;
N = N1;%Number of horizon
K1 = 1000;%Maximum iteration times, jmax
K = 1000;%Maximum iteration times, jmax
dt = 0.1;
gamma1 = 6;
gamma2 = 6;
nsim = ttsim;

tic;

% Constraints

umin = [-7; -5; -inf; -inf;];
umax = [7; 5; Inf; Inf];
xmin = [-10; -10; -10; -10];
xmax = [ 10;  10;  10;  10];

% Objective function
Q = diag([10 10 10 10]);
QN = Q;
R = 1 * eye(4);
R(3,3) = 1000;
R(4,4) = 1000;
% Initial and reference states
x0 = x00;
xr = [3; 0.01; 0; 0];
ur = [0; 0; 1; 1];

% Dynamic system initialization
BB = [0 0 0 0;0 0 0 0;dt 0 0 0;0 dt 0 0];

% Convex MPC frame
[nx, nu] = size(BB);
% Initialize states set (x00,x01,,,x0N）
x_0 = [];
x0 = x0';
u_0 = zeros(nu, N);
u0 = zeros(nu, 1);% we may need to change this for a better warm-up start
impc = zeros(nx, nsim + 1);
impc(:, 1) = x0;
x0new = x0;
for i=1 : (N+1)
    x_0 = [x_0 x0new];
    x0new = [dt*x0new(4)*cos(x0new(3))+x0new(1);dt*x0new(4)*sin(x0new(3))+x0new(2);dt*u0(1)+x0new(3);dt*u0(2)+x0new(4)];
end

abc = tangentline(x_0, r);
Acbf = cbfmat(abc, nx, nu, dt, gamma1, x0);%First order CBF constraints
lcbf = -abc(3, :);
lcbf(1) = [];
lcbf = lcbf';
ucbf = inf * ones(N, 1);
A2cbf = cbfmat2(abc, nx, nu, dt, gamma1, gamma2, x0);%Second order CBF constraints
lcbf2 = getlcbf2(abc, dt, gamma1, gamma2);
ucbf2 = inf * ones(N-1, 1);

% Cast MPC problem to a QP: x = (x(0),x(1),...,x(N),u(0),...,u(N-1))
% - quadratic objective
P = blkdiag( kron(speye(N), Q), QN, kron(speye(N), R) );
% - linear objective
q = [repmat(-Q*xr, N, 1); -QN*xr; repmat(-R*ur, N, 1)];
% - linear dynamics
Ax = getax(x_0, dt, N, nx);
Bu = kron([sparse(1, N); speye(N)], BB);
Cx = getcx(x_0, u_0, dt, N, nx);
Aeq = [Ax, Bu];
leq = [-x0; -Cx];
ueq = leq;
% - input and state constraints
Aineq = speye((N+1)*nx + N*nu);
lineq = [repmat(xmin, N+1, 1); repmat(umin, N, 1)];
uineq = [repmat(xmax, N+1, 1); repmat(umax, N, 1)];

% - OSQP constraints
A = [Aeq; Aineq; Acbf; A2cbf];
l = [leq; lineq; lcbf; lcbf2];
u = [ueq; uineq; ucbf; ucbf2];

% Create an OSQP object
prob = osqp;
% Setup workspace
prob.setup(P, q, A, l, u, 'warm_start', true);
% Solve
res = prob.solve();

% Check solver status
if ~strcmp(res.info.status, 'solved')
%    error('OSQP did not solve the problem!')
        t2=-1;
        return
end
ctrlx = res.x(1:(N+1)*nx);
ctrlu = res.x((N+1)*nx+1:(N+1)*nx+N*nu);

for j = 1 : (K1-1)%Iterations for the first time-step
    storagex = ctrlx;%store the state variables and inputs in order to compare them with the next iteration
    storageu = ctrlu;
    x_0 = trans(x0, ctrlx, nx, N+1);
    u_0 = transu(ctrlu, nu, N);
    abc = tangentline(x_0, r);
    Acbf = cbfmat(abc, nx, nu, dt, gamma1, x0);
    lcbf = -abc(3, :);
    lcbf(1) = [];
    lcbf = lcbf';
    ucbf = inf * ones(N, 1);
    A2cbf = cbfmat2(abc, nx, nu, dt, gamma1, gamma2, x0);
    lcbf2 = getlcbf2(abc, dt, gamma1, gamma2);
    ucbf2 = inf * ones(N-1, 1);
    % Cast MPC problem to a QP: x = (x(0),x(1),...,x(N),u(0),...,u(N-1))
    % - quadratic objective
    P = blkdiag( kron(speye(N), Q), QN, kron(speye(N), R) );
    % - linear objective
    q = [repmat(-Q*xr, N, 1); -QN*xr; repmat(-R*ur, N, 1)];
    % - linear dynamics
    Ax = getax(x_0, dt, N, nx);
    Bu = kron([sparse(1, N); speye(N)], BB);
    Cx = getcx(x_0, u_0, dt, N, nx);
    Aeq = [Ax, Bu];
    leq = [-x0; -Cx];
    ueq = leq;
    % - input and state constraints
    Aineq = speye((N+1)*nx + N*nu);
    lineq = [repmat(xmin, N+1, 1); repmat(umin, N, 1)];
    uineq = [repmat(xmax, N+1, 1); repmat(umax, N, 1)];

    % - OSQP constraints
    A = [Aeq; Aineq; Acbf; A2cbf];
    l = [leq; lineq; lcbf; lcbf2];
    u = [ueq; uineq; ucbf; ucbf2];
    % Create an OSQP object
    prob = osqp;
    % Setup workspace
    prob.setup(P, q, A, l, u, 'warm_start', true);
    % Solve
    res = prob.solve();

    % Check solver status
    if ~strcmp(res.info.status, 'solved')
%        error('OSQP did not solve the problem!')
        t2=-1;
        return
    end
    ctrlx = res.x(1:(N+1)*nx);
    ctrlu = res.x((N+1)*nx+1:(N+1)*nx+N*nu);
    testx = (storagex - ctrlx)'*(storagex - ctrlx);
    testu = (storageu - ctrlu)'*(storageu - ctrlu);
    test = (testx)/(storagex'*storagex);
    if (test)^(0.5)<=10^(-2)&& (testx/((N+1)*nx))^(0.5)<=10^(-4)%Convergence criterion
      break
    end
end % Move for the first step
ctrl = ctrlu(1:nu);
x0 = [dt*x0(4)*cos(x0(3))+x0(1);dt*x0(4)*sin(x0(3))+x0(2);dt*ctrl(1)+x0(3);dt*ctrl(2)+x0(4)];
ctrlu = rewrite(ctrlu, nu, N);
t2=t2+toc;
return

x_0 = newinit(x0, ctrlu, nx, nu, N, dt);
u_0 = transu(ctrlu, nu, N);
impc(:, 2) = x0;
storagex = ctrlx;
storageu = ctrlu;

for i = 1 : (nsim-1)%Iterations for the left time steps

   for j = 1 : K
    abc = tangentline(x_0, r);
    Acbf = cbfmat(abc, nx, nu, dt, gamma1, x0);
    lcbf = -abc(3, :);
    lcbf(1) = [];
    lcbf = lcbf';
    ucbf = inf * ones(N, 1);
    A2cbf = cbfmat2(abc, nx, nu, dt, gamma1, gamma2, x0);
    lcbf2 = getlcbf2(abc, dt, gamma1, gamma2);
    ucbf2 = inf * ones(N-1, 1);
    % Cast MPC problem to a QP: x = (x(0),x(1),...,x(N),u(0),...,u(N-1))
    % - quadratic objective
    P = blkdiag( kron(speye(N), Q), QN, kron(speye(N), R) );
    % - linear objective
    q = [repmat(-Q*xr, N, 1); -QN*xr; repmat(-R*ur, N, 1)];
    % - linear dynamics
    Ax = getax(x_0, dt, N, nx);
    Bu = kron([sparse(1, N); speye(N)], BB);
    Cx = getcx(x_0, u_0, dt, N, nx);
    Aeq = [Ax, Bu];
    leq = [-x0; -Cx];
    ueq = leq;
    % - input and state constraints
    Aineq = speye((N+1)*nx + N*nu);
    lineq = [repmat(xmin, N+1, 1); repmat(umin, N, 1)];
    uineq = [repmat(xmax, N+1, 1); repmat(umax, N, 1)];

    % - OSQP constraints
    A = [Aeq; Aineq; Acbf; A2cbf];
    l = [leq; lineq; lcbf; lcbf2];
    u = [ueq; uineq; ucbf; ucbf2];
    % Create an OSQP object
    prob = osqp;
    % Setup workspace
    prob.setup(P, q, A, l, u, 'warm_start', true);
    % Solve
    res = prob.solve();

    % Check solver status
    if ~strcmp(res.info.status, 'solved')
%        error('OSQP did not solve the problem!')
        t2=-1;
        return
    end
    ctrlx = res.x(1:(N+1)*nx);
    ctrlu = res.x((N+1)*nx+1:(N+1)*nx+N*nu);
    x0 = ctrlx(1:nx);
    testx0 = [testx0 x0];
    x_0 = trans(x0, ctrlx, nx, N+1);
    u_0 = transu(ctrlu, nu, N);
    testx = (storagex - ctrlx)'*(storagex - ctrlx);
    testu = (storageu - ctrlu)'*(storageu - ctrlu);
    test = (testx)/(storagex'*storagex);
    if (test)^(0.5)<=10^(-2)&& (testx/((N+1)*nx))^(0.5)<=10^(-4)%Convergence criterion
      break
    end
    storagex = ctrlx;
    storageu = ctrlu;
   end
   ctrl = ctrlu(1:nu);
   x0 = [dt*x0(4)*cos(x0(3))+x0(1);dt*x0(4)*sin(x0(3))+x0(2);dt*ctrl(1)+x0(3);dt*ctrl(2)+x0(4)];
   ctrlu = rewrite(ctrlu, nu, N);
   x_0 = newinit(x0, ctrlu, nx, nu, N, dt);
   u_0 = transu(ctrlu, nu, N);
   impc(:, i+2) = x0;
end
end

% Linerize the CBF constraints (get a, b, c for lines)
function abc = tangentline(xy, r)% x and y from initialize states set, abc are coeeficients for linear equation a*x+b*y+c=0
[xx, ~] = size(xy);%xx=2,yy=N+1
xy(xx,:) = []; % this part should be changed for other case
xy((xx-1),:) = []; % this part should be changed other case
[xx, yy] = size(xy);%xx=2,yy=N+1
xyjiao = zeros(xx, yy);%intersection points
for i = 1 : xx
    for j = 1 : yy
        xyjiao(i, j) = r * xy(i, j) * (1 / (xy(:, j)' * xy(:, j)))^(0.5);%calculate coordinates of intersection points
    end
end
cc = -r^2 * ones(1, yy);
abc = [xyjiao; cc];
end

% Get CBF constraints matrix
function Acbf = cbfmat(abc, nx, nu, dt, gamma, x0)
[~, yy] = size(abc);
Acbfx = zeros((yy-1), yy*nx);
Acbfu = zeros((yy-1), (yy-1)*nu);
for i = 1 : (yy-1)
    Acbfx(i, (i*nx)+1) = abc(1, (i+1));
    Acbfx(i, (i*nx)+2) = abc(2, (i+1));
end
for i = 1 : (yy-1)
    Acbfu(i, ((i-1)*nu+3)) = - (1 - dt * gamma)^(i) * (abc(1, 1) * x0(1, 1) + abc(2, 1) * x0(2, 1) + abc(3, 1));
end
Acbf = [Acbfx Acbfu];
end

% Transfer vector x into matrix
function res = trans(x0, vector, nxx, nyy)%nxx=nx,nyy=N+1
 res = zeros(nxx, nyy);
 res(:,1) = x0;
 for i = 1 : (nyy -1)
     res(:,i+1)= vector(((i)*nxx+1):(i+1)*nxx);
 end
end

% Transfer vector u into matrix
function resu = transu(vector, nxx, nyy)%nxx=nu,nyy=N
 resu = zeros(nxx, nyy);
 for i = 1 : (nyy)
     resu(:,i)= vector(((i-1)*nxx+1):(i)*nxx);
 end
end

% Rewrite u vector
function reu = rewrite(vector, nu, N)
append = vector((N-1)*nu+1:N*nu);
vector(1:nu) = [];
reu = [vector;append];
end

% Get new x_0
function x_0 = newinit(x0, ctrlu, nx, nu, N, dt)
 x_0 = zeros(nx, N+1);
 x_0(:, 1) = x0;
 for i=1 : N
    u0 = ctrlu((i-1)*nu+1:i*nu);
    x0 = [dt*x0(4)*cos(x0(3))+x0(1);dt*x0(4)*sin(x0(3))+x0(2);dt*u0(1)+x0(3);dt*u0(2)+x0(4)];
    x_0(:, i + 1) = x0;
 end
end

% Get AA matrix
function AA = getaa(x0, dt)
 AA = [1 0 -x0(4)*sin(x0(3))*dt cos(x0(3))*dt;0 1 x0(4)*cos(x0(3))*dt sin(x0(3))*dt;0 0 1 0;0 0 0 1];
end
% Get CC matrix
function CC = getcc(x0, x1, u0, dt)
 CC = [x0(4)*sin(x0(3))*x0(3)*dt-x0(4)*cos(x0(3))*dt+x1(1)-x0(1);-x0(4)*cos(x0(3))*x0(3)*dt-x0(4)*sin(x0(3))*dt+x1(2)-x0(2);-u0(1)*dt+x1(3)-x0(3);-u0(2)*dt+x1(4)-x0(4)];
end
% Get Ax matrix
function Ax = getax(x_0, dt, N, nx)
x0 = x_0(:,1);
AA = getaa(x0, dt);
Ax = kron(speye(N+1), -speye(nx)) + kron(sparse(diag(ones(N, 1), -1)), AA);
for i = 1 : (N-1)
   x0 = x_0(:,i+1);
   AA = getaa(x0, dt);
   Ax(nx*(i+1)+1:nx*(i+1)+nx,nx*i+1:nx*i+nx) = AA;
end
end
% Get Cx matrix
function Cx = getcx(x_0, u_0, dt, N, nx)
Cx = zeros(N*nx, 1);
for i = 1 : N
    u0 = u_0(:,i);
    x0 = x_0(:,i);
    x1 = x_0(:,i+1);
    CC = getcc(x0, x1, u0, dt);
    Cx((i-1)*nx+1:(i-1)*nx+nx) = CC;
end
end
% Get A2cbf
function A2cbf = cbfmat2(abc, nx, nu, dt, gamma1, gamma2, x0)
[~, yy] = size(abc);
Acbfx2 = zeros((yy-2), yy*nx);
Acbfx22 = zeros((yy-2), yy*nx);
Acbfu2 = zeros((yy-2), (yy-1)*nu);
for i = 1 : (yy-2)
    Acbfx2(i, (i*nx)+1) = (gamma1-1/dt)*abc(1, (i+1));
    Acbfx2(i, (i*nx)+2) = (gamma1-1/dt)*abc(2, (i+1));
    Acbfx2(i, ((i+1)*nx)+1) = (1/dt)*abc(1, (i+2));
    Acbfx2(i, ((i+1)*nx)+2) = (1/dt)*abc(2, (i+2));
end
for i = 1 : (yy-2)
    Acbfx22(i, (1*nx)+1) = -(1-dt*gamma2)^(i)/dt*abc(1, (1+1));
    Acbfx22(i, (1*nx)+2) = -(1-dt*gamma2)^(i)/dt*abc(2, (1+1));
end
Acbfx2 = Acbfx2 + Acbfx22;
for i = 1 : (yy-2)
    Acbfu2(i, ((i-1)*nu+4)) = - (1 - dt * gamma2)^(i)*(gamma1-1/dt)*(abc(1, 1) * x0(1, 1) + abc(2, 1) * x0(2, 1) + abc(3, 1));
end
A2cbf = [Acbfx2 Acbfu2];
end
% Get lcbf2
function lcbf2 = getlcbf2(abc, dt, gamma1, gamma2)
[~, yy] = size(abc);
lcbf2 = zeros((yy-2),1);
for i = 1 : (yy-2)
    lcbf2(i, 1) = -abc(3, (i+2))/dt-(gamma1-1/dt)*abc(3, (i+1))+(1 - dt * gamma2)^(i)*abc(3, (1+1))/dt;
end
end


function pick = nms(boxes, overlap)
% top = nms(boxes, overlap)
% Non-maximum suppression. (FAST VERSION)
% Greedily select high-scoring detections and skip detections
% that are significantly covered by a previously selected
% detection.
%
% NOTE: This is adapted from Pedro Felzenszwalb's version (nms.m),
% but an inner loop has been eliminated to significantly speed it
% up in the case of a large number of boxes

% Copyright (C) 2011-12 by Tomasz Malisiewicz
% All rights reserved.
%
% This file is part of the Exemplar-SVM library and is made
% available under the terms of the MIT license (see COPYING file).
% Project homepage: https://github.com/quantombone/exemplarsvm

if isempty(boxes)
  pick = [];
  return;
end

x1 = boxes(:,1);
y1 = boxes(:,2);
x2 = boxes(:,3);
y2 = boxes(:,4);
if size(boxes,2)==4
  s = ones(1,size(boxes,1));
else
  s = boxes(:,end);
end

area = (x2-x1+1) .* (y2-y1+1);
[~, I] = sort(s);

pick = s*0;
counter = 1;
while ~isempty(I)
  last = length(I);
  i = I(last);
  pick(counter) = i;
  counter = counter + 1;

  xx1 = max(x1(i), x1(I(1:last-1)));
  yy1 = max(y1(i), y1(I(1:last-1)));
  xx2 = min(x2(i), x2(I(1:last-1)));
  yy2 = min(y2(i), y2(I(1:last-1)));

  w = max(0.0, xx2-xx1+1);
  h = max(0.0, yy2-yy1+1);

  inter = w.*h;
  o = inter ./ (area(i) + area(I(1:last-1)) - inter);

%   I = I(find(o<=overlap));
  I = I((o<=overlap));
end

pick = pick(1:(counter-1));