function [data, warns] = psse_parse(records, sections, verbose, rev)
%PSSE_PARSE  Parses the data from a PSS/E RAW data file.
%   DATA = PSSE_PARSE(RECORDS, SECTIONS)
%   DATA = PSSE_PARSE(RECORDS, SECTIONS, VERBOSE)
%   DATA = PSSE_PARSE(RECORDS, SECTIONS, VERBOSE, REV)
%   [DATA, WARNINGS] = PSSE_PARSE(RECORDS, SECTIONS, ...)
%
%   Parses the data from a PSS/E RAW data file (as read by PSSE_READ)
%   into a struct.
%
%   Inputs:
%       RECORDS : cell array of strings, corresponding to the lines
%                 in the RAW file
%       SECTIONS : struct array with indices marking the beginning
%                  and end of each section, and the name of the
%                  section, fields are:
%           first   : index into RECORDS of first line of section
%           last    : index into RECORDS of last line of section
%           name    : name of the section, as extracted from the
%                     END OF ... DATA comments
%       VERBOSE      :  1 (default) to display progress info, 0 otherwise
%       REV          :  (optional) assume the input file is of this
%                       PSS/E revision number, attempts to determine
%                       REV from the file by default
%
%   Output(s):
%       DATA :  a struct with the following fields, each with two
%               sub-fields, 'num' and 'txt' containing the numeric and
%               text data read from the file for the corresponding section
%           id
%           bus
%           load
%           gen
%           shunt
%           branch
%           trans2
%           trans3
%           area
%           twodc
%           swshunt
%       WARNINGS :  cell array of strings containing accumulated
%                   warning messages
%
%   See also PSSE2MPC, PSSE_READ, PSSE_PARSE_SECTION, PSSE_PARSE_LINE

%   MATPOWER
%   Copyright (c) 2014-2016, Power Systems Engineering Research Center (PSERC)
%   by Ray Zimmerman, PSERC Cornell
%   Based on mpreadraw.m, written by: Yujia Zhu, Jan 2014, yzhu54@asu.edu.
%
%   This file is part of MATPOWER.
%   Covered by the 3-clause BSD License (see LICENSE file for details).
%   See https://matpower.org for more info.

%% default args
if nargin < 4
    rev = 0;
    if nargin < 3
        verbose = 0;
    end
end
defaultrev = 23;

%% inititialize section counter
s = 1;
warns = {};

%%-----  case identification data  -----
%% In some files, SBASE is followed by a comment with the REV number such as:
%%  / PSS/E-29.0
%%  / PSS(tm)E-30 RAW
if verbose
    if rev
        fprintf('Forcing interpretation as PSS/E revision %d\n', rev);
    else
        fprintf('Attempting to determine PSS/E revision from content.\n');
    end
    fprintf('Parsing case identification data ...');
end
if rev
    warns{end+1} = sprintf('Conversion explicitly using PSS/E revision %d', rev);
end
[d, c] = psse_parse_line(records{1}, 'dfdfff');
nn = length(d);
data.id.IC = d{1};
if isempty(d{2}) || d{2} <= 0
    error('ERROR: Probable corrupt file, unable to read a valid SBASE value from the first line.');
else
    data.id.SBASE = d{2};
end
if ~isempty(d{3})
    data.id.REV = d{3};
else    %% attempt to extract revision from comment
    tmp = regexp(c, 'PSS(/|\(tm\))E-(?<rev>\d+)', 'tokens');
    if ~isempty(tmp) && size(tmp{1}, 2) == 2
        data.id.REV = str2num(tmp{1}{2});
    else
        data.id.REV = 0;
    end
end
if ~isempty(d{4})
    data.id.XFRRAT = d{4};
else
    data.id.XFRRAT = 0;
end
if ~isempty(d{5})
    data.id.NXFRAT = d{5};
else
    data.id.NXFRAT = 0;
end
if ~isempty(d{6})
    data.id.BASFRQ = d{6};
else
    data.id.BASFRQ = 0;
end
data.id.comment0 = c;
data.id.comment1 = records{2};
data.id.comment2 = records{3};
if verbose
    if rev
        if data.id.REV
            fprintf('.. override detected rev %2d w/%2d ... done.\n', data.id.REV, rev);
        else
            fprintf('...... unknown rev, using rev %2d ... done.\n', rev);
        end
    else
        if data.id.REV
            fprintf('......... rev %2d format detected ... done.\n', data.id.REV);
        else
            fprintf('...... unknown rev, using rev %2d ... done.\n', defaultrev);
        end
    end
end
if ~rev
    if data.id.REV
        rev = data.id.REV;      %% use detected value
    else
        rev = defaultrev;       %% none detected, use default value
        data.id.REV = defaultrev;
        warns{end+1} = sprintf('Unknown REV, using REV %2d format.', defaultrev);
    end
else
    data.id.REV = rev;          %% use override value
end
if isempty(data.id.IC) || data.id.IC ~= 0
    warns{end+1} = sprintf('IC = %d indicates that this may be a change case, rather than base case\n         PSSE2MPC is NOT designed to handle change cases.', data.id.IC);
    if verbose
        fprintf('WARNING : %s\n', warns{end});
    end
end
s = s + 1;

%%-----  bus data  -----
if rev < 24         %% includes load data
    [data.bus, warns] = psse_parse_section(warns, records, sections, s, verbose, ...
        'bus', 'ddffffdffsfd');
elseif rev < 31     %% includes fixed shunt data, load separate
    [data.bus, warns] = psse_parse_section(warns, records, sections, s, verbose, ...
        'bus', 'dsfdffddffd');
else                %% fixed shunt and load data separate
    [data.bus, warns] = psse_parse_section(warns, records, sections, s, verbose, ...
        'bus', 'dsfddddffff..');
%       'bus', 'dsfddddffffff');
end
s = s + 1;

%%-----  load data  -----
if rev >= 24
    [data.load, warns] = psse_parse_section(warns, records, sections, s, verbose, ...
        'load', 'd.d..ffffff...');
%       'load', 'dsdddffffffddd');
    s = s + 1;
end

%%-----  fixed shunt data  -----
if rev > 30     %% fixed shunt data is included in bus data for rev <= 30
    [data.shunt, warns] = psse_parse_section(warns, records, sections, s, verbose, ...
        'fixed shunt', 'd.dff');
%       'fixed shunt', 'dsdff');
    s = s + 1;
end

%%-----  generator data  -----
[data.gen, warns] = psse_parse_section(warns, records, sections, s, verbose, ...
    'generator', 'd.fffff.f.....d.ff...........');
%   'generator', 'dsfffffdffffffdfffdfdfdfdfsdf');
s = s + 1;

%%-----  branch data  -----
if rev <= 27   %% includes transformer ratio, angle
    [data.branch, warns] = psse_parse_section(warns, records, sections, s, verbose, ...
        'branch', 'dd.ffffffffffffd');
%       'branch', 'dddffffffffffffd');
elseif rev < 31
    [data.branch, warns] = psse_parse_section(warns, records, sections, s, verbose, ...
        'branch', 'dd.ffffffffffd');
%       'branch', 'ddsffffffffffdfdfdfdfdf');
else
    [data.branch, warns] = psse_parse_section(warns, records, sections, s, verbose, ...
        'branch', 'dd.ffffffffffd');
%       'branch', 'ddsffffffffffddfdfdfdfdf');
end
s = s + 1;

%%-----  skip transformer adjustment data  -----
if rev <= 27
    [s, warns] = psse_skip_section(warns, sections, s, verbose, 'transformer adjustment');
end

%%-----  transformer data  -----
if rev > 27
    %% PSS/E stores two winding and three winding transformer data in the same
    %% section in RAW file. We read in 2 passes, first pass determines the type of
    %% each, second pass reads the data.
    label = 'transformer';
    if ~isempty(sections(s).name) && ~strcmpi(label, sections(s).name)
        if verbose > 1
            fprintf('-----  WARNING:  Expected section labeled: ''%s''\n', upper(label));
            fprintf('-----            Found section labeled:    ''%s''\n', sections(s).name);
        end
    end

    %% Step 1 : Count and collect transformer types
    if verbose
        fprintf('Analyzing transformer types ...');
    end

    %% estimate max number of transformers by number of lines in section
    nt2 = round((sections(s).last - sections(s).first + 1) / 4);
    nt3 = round((sections(s).last - sections(s).first + 1) / 5);

    %% initialize indices for transformer types
    idx2 = zeros(nt2, 1);
    idx3 = zeros(nt3, 1);

    %% set up counters
    i = sections(s).first;      %% initialize record index
    i2 = 0;
    i3 = 0;

    while i <= sections(s).last
        %% determine transformer type
        pat = '[^''",\s/]+\s*(,|\s)\s*[^''",\s/]+\s*(,|\s)\s*([^''",\s/]+)';
        m = regexp(records{i}, pat, 'tokens', 'once');
        if length(m) ~= 3
            disp(m);
            error('m should be length 3');
        end
        if length(m{3}) == 1 && m{3}(1) == '0'  %% two-winding
            i2 = i2 + 1;
            idx2(i2) = i;
            i = i + 4;
        else                                    %% three-winding
            i3 = i3 + 1;
            idx3(i3) = i;
            i = i + 5;
        end
    end
    nt2 = i2;
    nt3 = i3;

    if verbose
        str = sprintf(' %d(%d) two(three)-winding.', nt2, nt3);
        spacers = repmat('.', 1, 36-length(str));
        fprintf('%s %s ... done.\n', spacers, str);
    end

    %% trim index vectors down to size
    idx2 = idx2(1:nt2);
    idx3 = idx3(1:nt3);

    %% parse record 1 (cols 1-20)
    [t2_1, warns] = psse_parse_section(warns, records(idx2), verbose, ...
        '2-winding transformers (1)', 'dd..ddd....d........');
%       '2-winding transformers (1)', 'dddsdddffdsddfdfdfdf');
    [t3_1, warns] = psse_parse_section(warns, records(idx3), verbose, ...
        '3-winding transformers (1)', 'ddd.ddd....d........');
%       '3-winding transformers (1)', 'dddsdddffdsddfdfdfdf');

    %% two-winding
    %% parse record 2 (cols 21-23)
    [t2_2, warns] = psse_parse_section(warns, records(idx2+1), verbose, ...
        '2-winding transformers (2)', 'fff');

    %% parse record 3 (cols 24-39)
    %% parse up to CX1, should warn if CNXA1 is present and non-zero
    [t2_3, warns] = psse_parse_section(warns, records(idx2+2), verbose, ...
        '2-winding transformers (3)', 'ffffff..........');
%       '2-winding transformers (3)', 'ffffffddffffddff');

    %% parse record 4 (cols 40-41)
    [t2_4, warns] = psse_parse_section(warns, records(idx2+3), verbose, ...
        '2-winding transformers (4)', 'ff');

    %% three-winding
    %% parse record 2 (cols 21-31)
    [t3_2, warns] = psse_parse_section(warns, records(idx3+1), verbose, ...
        '3-winding transformers (2)', 'fffffffffff');
%       '3-winding transformers (2)', 'fffffffffff');

    %% parse record 3 (cols 32-47)
    %% parse up to CX1, should warn if CNXA1 is present and non-zero
    [t3_3, warns] = psse_parse_section(warns, records(idx3+2), verbose, ...
        '3-winding transformers (3)', 'ffffff..........');
%       '3-winding transformers (3)', 'ffffffddffffddff');

    %% parse record 4 (cols 48-63)
    %% parse up to CX2
    [t3_4, warns] = psse_parse_section(warns, records(idx3+3), verbose, ...
        '3-winding transformers (4)', 'ffffff..........');
%       '3-winding transformers (4)', 'ffffffddffffddff');

    %% parse record 5 (cols 64-79)
    %% parse up to CX3
    [t3_5, warns] = psse_parse_section(warns, records(idx3+4), verbose, ...
        '3-winding transformers (5)', 'ffffff..........');
%       '3-winding transformers (5)', 'ffffffddffffddff');

    %% assemble two-winding transformer records
    data.trans2.num = [t2_1.num(:, 1:20) t2_2.num(:, 1:3) t2_3.num(:, 1:16) t2_4.num(:, 1:2)];
    data.trans2.txt = [t2_1.txt(:, 1:20) t2_2.txt(:, 1:3) t2_3.txt(:, 1:16) t2_4.txt(:, 1:2)];

    %% assemble three-winding transformer records
    data.trans3.num = [t3_1.num(:, 1:20) t3_2.num(:, 1:11) t3_3.num(:, 1:16) t3_4.num(:, 1:16) t3_5.num(:, 1:16)];
    data.trans3.txt = [t3_1.txt(:, 1:20) t3_2.txt(:, 1:11) t3_3.txt(:, 1:16) t3_4.txt(:, 1:16) t3_5.txt(:, 1:16)];

    % if verbose
    %     fprintf('%s\n', upper(label));
    %     fprintf('%s\n', sections(s).name);
    % end
    s = s + 1;
end

%%-----  area interchange data  -----
[data.area, warns] = psse_parse_section(warns, records, sections, s, verbose, 'area', 'ddffs');
s = s + 1;

%%-----  two-terminal DC transmission line data  -----
label = 'two-terminal DC';
if ~isempty(sections(s).name) && ~strcmpi(label, sections(s).name)
    if verbose > 1
        fprintf('-----  WARNING:  Expected section labeled: ''%s''\n', upper(label));
        fprintf('-----            Found section labeled:    ''%s''\n', sections(s).name);
    end
end
idx = sections(s).first:3:sections(s).last;
if rev < 31
    [dc1, warns] = psse_parse_section(warns, records(idx), verbose, ...
        'two-terminal DC (1)', '.d.ff.......');
%       'two-terminal DC (1)', 'ddffffffsfdf');
    [dc2, warns] = psse_parse_section(warns, records(idx+1), verbose, ...
        'two-terminal DC (2)', 'd.ff.............');
%       'two-terminal DC (2)', 'ddffffffffffdddsf');
    [dc3, warns] = psse_parse_section(warns, records(idx+2), verbose, ...
        'two-terminal DC (3)', 'd.ff.............');
%       'two-terminal DC (3)', 'ddffffffffffdddsf');
else
    [dc1, warns] = psse_parse_section(warns, records(idx), verbose, ...
        'two-terminal DC (1)', '.d.ff.......');
%       'two-terminal DC (1)', 'sdffffffsfdf');
    [dc2, warns] = psse_parse_section(warns, records(idx+1), verbose, ...
        'two-terminal DC (2)', 'd.ff.............');
%       'two-terminal DC (2)', 'ddffffffffffdddDf');
    [dc3, warns] = psse_parse_section(warns, records(idx+2), verbose, ...
        'two-terminal DC (3)', 'd.ff.............');
%       'two-terminal DC (3)', 'ddffffffffffdddDf');
end

%% assemble two-terminal DC transmission line
data.twodc.num = [dc1.num dc2.num dc3.num];
data.twodc.txt = [dc1.txt dc2.txt dc3.txt];
% if verbose
%     fprintf('%s\n', upper(label));
%     fprintf('%s\n', sections(s).name);
% end
s = s + 1;

%%-----  skip voltage source converter data  -----
if rev > 28
    [s, warns] = psse_skip_section(warns, sections, s, verbose, 'voltage source converter');
end

%%-----  switched shunt data  -----
if rev < 31
    %% parse up to B1
    if rev <= 27
        [data.swshunt, warns] = psse_parse_section(warns, records, sections, s, verbose, ...
            'switched shunt', 'd....f');
%           'switched shunt', 'ddffdfdfdfdfdfdfdfdfdf');
    elseif rev <= 29
        [data.swshunt, warns] = psse_parse_section(warns, records, sections, s, verbose, ...
            'switched shunt', 'd.....f');
%           'switched shunt', 'ddffdsfdfdfdfdfdfdfdfdf');
    else    %%  rev == 30
        [data.swshunt, warns] = psse_parse_section(warns, records, sections, s, verbose, ...
            'switched shunt', 'd......f');
%           'switched shunt', 'ddffdfsfdfdfdfdfdfdfdfdf');
    end
    s = s + 1;
end

%%-----  skip impedance correction data  -----
[s, warns] = psse_skip_section(warns, sections, s, verbose, 'impedance correction');

%%-----  skip multi-terminal DC data  -----
[s, warns] = psse_skip_section(warns, sections, s, verbose, 'multi-terminal DC');

%%-----  skip multi-section line data  -----
[s, warns] = psse_skip_section(warns, sections, s, verbose, 'multi-section line');

%%-----  skip zone data  -----
[s, warns] = psse_skip_section(warns, sections, s, verbose, 'zone');

%%-----  skip inter-area transfer data  -----
[s, warns] = psse_skip_section(warns, sections, s, verbose, 'inter-area transfer');

%%-----  skip owner data  -----
if rev > 24
    [s, warns] = psse_skip_section(warns, sections, s, verbose, 'owner');
end

%%-----  skip FACTS control device data  -----
if rev > 25
    [s, warns] = psse_skip_section(warns, sections, s, verbose, 'FACTS control device');
end

%%-----  switched shunt data  -----
if rev > 30
    %% parse up to B1
    if rev < 32
        [data.swshunt, warns] = psse_parse_section(warns, records, sections, s, verbose, ...
            'switched shunt', 'd......f');
%           'switched shunt', 'ddffdfsfdfdfdfdfdfdfdfdf');
    else
        [data.swshunt, warns] = psse_parse_section(warns, records, sections, s, verbose, ...
            'switched shunt', 'd........f');
%           'switched shunt', 'ddddffdfsfdfdfdfdfdfdfdfdf');
    end
    s = s + 1;
end

%%-----  skip GNE device data  -----
if rev > 31
    [s, warns] = psse_skip_section(warns, sections, s, verbose, 'GNE device');
end

%%-----  skip induction machine data  -----
if rev > 32
    [s, warns] = psse_skip_section(warns, sections, s, verbose, 'induction machine');
end

%%-----  check for extra sections  -----
if s <= length(sections)
    warns{end+1} = sprintf('Found %d additional section(s)', length(sections)-s+1);
    if verbose > 1
        fprintf('-----  WARNING:   Found %d additional section(s):\n', length(sections)-s+1);
    end
end
while s <= length(sections)
    n = sections(s).last - sections(s).first + 1;
    if n
        str = sprintf('with %d line(s)', n);
    else
        str = sprintf('(empty)');
    end
    if isempty(sections(s).name)
        warns{end+1} = sprintf('  unlabeled section %s', str);
        if verbose > 1
            fprintf('-----            unlabeled section %s\n', str);
        end
    else
        warns{end+1} = sprintf('  ''%s DATA'' %s', sections(s).name, str);
        if verbose > 1
            fprintf('-----            ''%s DATA'' %s\n', sections(s).name, str);
        end
    end
    s = s + 1;
end

%%---------------------------------------------------------------------
function [s, warns] = psse_skip_section(warns, sections, s, verbose, label)
%PSSE_SKIP_SECTION  Skips over a section without extracting any data.
%   [SIDX, WARNINGS] = PSSE_SKIP_SECTION(WARNINGS, SECTIONS, SIDX, VERBOSE, LABEL)

if s > length(sections)
    if verbose
        spacers = repmat('.', 1, 58-length(label));
        fprintf('No %s data read %s done.\n', label, spacers);
    end
else
    nr = sections(s).last - sections(s).first + 1;
    if nr > 1
        ss = 'lines';
    else
        ss = 'line';
    end
    if nr
        warns{end+1} = sprintf('Skipped %d %s of %s data.', nr, ss, label);
    end
    if ~isempty(sections(s).name) && ~strcmp(upper(label), sections(s).name)
        warns{end+1} = sprintf('Section label mismatch, found ''%s'', expected ''%s''', ...
            sections(s).name, upper(label));
        if verbose
            fprintf('-----  WARNING:  Found section labeled:    ''%s''\n', sections(s).name);
            fprintf('-----            Expected section labeled: ''%s''\n', upper(label));
        end
    end
    if verbose && nr
        spacers = repmat('.', 1, 47-length(ss)-length(label));
        fprintf('Skipping%6d %s of %s data %s done.\n', nr, ss, label, spacers);
    end
    s = s + 1;
end


function test_ft_timelockanalysis_new(datainfo,writeflag)

% MEM 2gb
% WALLTIME 00:10:00
% DEPENDENCY ft_timelockanalysis_new ft_timelockanalysis ref_datasets

% the optional writeflag determines whether the output should be saved to
% disk

%%
% this is a function for testing ft_timelockanalysis_new, which is not official yet
%
% This function is testing a new ft_timelockanalysis_new which is something
% Johanna is working on and not in SVN yet.

return

%%

if nargin<2
  writeflag = 0;
end
if nargin<1
  datainfo = ref_datasets;
end

% for k = 1:numel(datainfo)
for k = 1:10
  datanew = timelockanalysis10trials(datainfo(k), writeflag);

  fname = fullfile(datainfo(k).origdir,'latest/timelock',datainfo(k).type,'timelock_',datainfo(k).datatype);
  tmp = load(fname);
  if isfield(tmp, 'data')
    data = tmp.data;
  elseif isfield(tmp, 'datanew')
    data = tmp.datanew;
  else isfield(tmp, 'timelock')
    data = tmp.timelock;
  end

  datanew = rmfield(datanew, 'cfg'); % these are per construction different if writeflag = 0;
  data    = rmfield(data,    'cfg');
  assert(isequaln(data, datanew));
end

test_cfg_options;

function [tlck] = timelockanalysis10trials(dataset, writeflag)

cfg        = [];
cfg.inputfile  = fullfile(dataset.origdir,'latest/raw',dataset.type,['preproc_',dataset.datatype]);
if writeflag
  cfg.outputfile = fullfile(dataset.origdir,'latest/timelock',dataset.type,'timelock_',dataset.datatype);
end
tlck = ft_timelockanalysis(cfg);
tlck1 = ft_timelockanalysis_new(cfg);
return

function test_cfg_options
load(dccnpath('/home/common/matlab/fieldtrip/data/ftp/tutorial/eventrelatedaveraging/dataFC_LP.mat'));
data = dataFC_LP;
clear dataFC_LP

data.time{2}=data.time{2}+.5; % purposely add some jitter to time window
data.time{3}=data.time{3}-.5;

cfg=[];
try
    tlock=ft_timelockanalysis_new(cfg,data);
catch me
%     if ~strcmp(me.message,'the option "output" was not specified or was empty');
        error(me.message)
%     end
end
cfg=[];
cfg.output='rubbish';
try
    tlock=ft_timelockanalysis_new(cfg,data);
catch me
    if ~strcmp(me.message,'the value of cfg.output is not set correctly');
        error(me.message)
    end
end

% no latency or covlatency given, use defaults
cfg=[];
cfg.output='avg';
cfg.feedback='none';
cfg.preproc.feedback='textbar';
tlock=ft_timelockanalysis_new(cfg,data);
if ~strmatch(ft_datatype(tlock),'timelock'),error('datatype');end;
% cfg.vartrllength=1;
% tlocko=ft_timelockanalysis(cfg,data);
cfg=[];
cfg.output='cov';
cfg.feedback='none';
cfg.preproc.feedback='textbar';
cfg.keeptrials='no';
tlock=ft_timelockanalysis_new(cfg,data);
if ~strmatch(ft_datatype(tlock),'timelock'),error('datatype');end;
if ~strcmp(ft_datatype(ft_checkdata(tlock,'datatype','raw')),'raw') || ~strcmp(ft_datatype(ft_checkdata(tlock)),'timelock'),error('checkdata');end
cfg=[];
cfg.output='avgandcov';
cfg.feedback='none';
cfg.preproc.feedback='textbar';
cfg.keeptrials='no';
tlock=ft_timelockanalysis_new(cfg,data);
if ~strmatch(ft_datatype(tlock),'timelock'),error('datatype');end;
if ~strcmp(ft_datatype(ft_checkdata(tlock,'datatype','raw')),'raw') || ~strcmp(ft_datatype(ft_checkdata(tlock)),'timelock'),error('checkdata');end
% cfg.covariance='yes';
% cfg.vartrllength=1;
% tlocko=ft_timelockanalysis(cfg,data);
cfg=[];
cfg.output='avg';
cfg.feedback='none';
cfg.preproc.feedback='textbar';
cfg.keeptrials='yes';
tlock=ft_timelockanalysis_new(cfg,data);
if ~strmatch(ft_datatype(tlock),'timelock'),error('datatype');end;
if ~strcmp(ft_datatype(ft_checkdata(tlock,'datatype','raw')),'raw') || ~strcmp(ft_datatype(ft_checkdata(tlock)),'timelock'),error('checkdata');end
% cfg.vartrllength=1;
% tlocko=ft_timelockanalysis(cfg,data);
cfg=[];
cfg.output='cov';
cfg.feedback='none';
cfg.preproc.feedback='textbar';
cfg.keeptrials='yes';
tlock=ft_timelockanalysis_new(cfg,data);
if ~strmatch(ft_datatype(tlock),'timelock'),error('datatype');end;
if ~strcmp(ft_datatype(ft_checkdata(tlock,'datatype','raw')),'raw') || ~strcmp(ft_datatype(ft_checkdata(tlock)),'timelock'),error('checkdata');end
cfg=[];
cfg.output='avgandcov';
cfg.feedback='none';
cfg.preproc.feedback='textbar';
cfg.keeptrials='yes';
tlock=ft_timelockanalysis_new(cfg,data);
if ~strmatch(ft_datatype(tlock),'timelock'),error('datatype');end;
if ~strcmp(ft_datatype(ft_checkdata(tlock,'datatype','raw')),'raw') || ~strcmp(ft_datatype(ft_checkdata(tlock)),'timelock'),error('checkdata');end
% cfg.covariance='yes';
% cfg.vartrllength=1;
% tlocko=ft_timelockanalysis(cfg,data);

% options with .latency and .covlatency specified
cfg=[];
cfg.output='avg';
cfg.feedback='none';
cfg.preproc.feedback='textbar';
cfg.keeptrials='no';
cfg.latency=[min(data.time{1}) max(data.time{1})];
tlock=ft_timelockanalysis_new(cfg,data);
if ~strmatch(ft_datatype(tlock),'timelock'),error('datatype');end;
if ~strcmp(ft_datatype(ft_checkdata(tlock,'datatype','raw')),'raw') || ~strcmp(ft_datatype(ft_checkdata(tlock)),'timelock'),error('checkdata');end
% cfg.vartrllength=1;
% tlocko=ft_timelockanalysis(cfg,data);
cfg.latency='maxperlength';
tlock=ft_timelockanalysis_new(cfg,data);
if ~strmatch(ft_datatype(tlock),'timelock'),error('datatype');end;
if ~strcmp(ft_datatype(ft_checkdata(tlock,'datatype','raw')),'raw') || ~strcmp(ft_datatype(ft_checkdata(tlock)),'timelock'),error('checkdata');end
cfg.latency='minperlength';
tlock=ft_timelockanalysis_new(cfg,data);
if ~strmatch(ft_datatype(tlock),'timelock'),error('datatype');end;
if ~strcmp(ft_datatype(ft_checkdata(tlock,'datatype','raw')),'raw') || ~strcmp(ft_datatype(ft_checkdata(tlock)),'timelock'),error('checkdata');end
cfg.latency='prestim';
tlock=ft_timelockanalysis_new(cfg,data);
if ~strmatch(ft_datatype(tlock),'timelock'),error('datatype');end;
if ~strcmp(ft_datatype(ft_checkdata(tlock,'datatype','raw')),'raw') || ~strcmp(ft_datatype(ft_checkdata(tlock)),'timelock'),error('checkdata');end
cfg.latency='poststim';
tlock=ft_timelockanalysis_new(cfg,data);
if ~strmatch(ft_datatype(tlock),'timelock'),error('datatype');end;
if ~strcmp(ft_datatype(ft_checkdata(tlock,'datatype','raw')),'raw') || ~strcmp(ft_datatype(ft_checkdata(tlock)),'timelock'),error('checkdata');end

cfg=[];
cfg.feedback='none';
cfg.preproc.feedback='textbar';
cfg.output='avg';
cfg.keeptrials='yes';
cfg.latency=[min(data.time{1}) max(data.time{1})];
tlock=ft_timelockanalysis_new(cfg,data);
if ~strmatch(ft_datatype(tlock),'timelock'),error('datatype');end;
if ~strcmp(ft_datatype(ft_checkdata(tlock,'datatype','raw')),'raw') || ~strcmp(ft_datatype(ft_checkdata(tlock)),'timelock'),error('checkdata');end
cfg.latency='maxperlength';
tlock=ft_timelockanalysis_new(cfg,data);
if ~strmatch(ft_datatype(tlock),'timelock'),error('datatype');end;
if ~strcmp(ft_datatype(ft_checkdata(tlock,'datatype','raw')),'raw') || ~strcmp(ft_datatype(ft_checkdata(tlock)),'timelock'),error('checkdata');end
cfg.latency='minperlength';
tlock=ft_timelockanalysis_new(cfg,data);
if ~strmatch(ft_datatype(tlock),'timelock'),error('datatype');end;
if ~strcmp(ft_datatype(ft_checkdata(tlock,'datatype','raw')),'raw') || ~strcmp(ft_datatype(ft_checkdata(tlock)),'timelock'),error('checkdata');end
cfg.latency='prestim';
tlock=ft_timelockanalysis_new(cfg,data);
if ~strmatch(ft_datatype(tlock),'timelock'),error('datatype');end;
if ~strcmp(ft_datatype(ft_checkdata(tlock,'datatype','raw')),'raw') || ~strcmp(ft_datatype(ft_checkdata(tlock)),'timelock'),error('checkdata');end
cfg.latency='poststim';
tlock=ft_timelockanalysis_new(cfg,data);
if ~strmatch(ft_datatype(tlock),'timelock'),error('datatype');end;
if ~strcmp(ft_datatype(ft_checkdata(tlock,'datatype','raw')),'raw') || ~strcmp(ft_datatype(ft_checkdata(tlock)),'timelock'),error('checkdata');end

cfg=[];
cfg.feedback='none';
cfg.preproc.feedback='textbar';
cfg.output='avgandcov';
cfg.keeptrials='no';
cfg.latency=[min(data.time{1}) max(data.time{1})];
cfg.covlatency=[min(data.time{1}) max(data.time{1})];
tlock=ft_timelockanalysis_new(cfg,data);
if ~strmatch(ft_datatype(tlock),'timelock'),error('datatype');end;
if ~strcmp(ft_datatype(ft_checkdata(tlock,'datatype','raw')),'raw') || ~strcmp(ft_datatype(ft_checkdata(tlock)),'timelock'),error('checkdata');end
% cfg.covariance='yes';
% cfg.vartrllength=1;
% tlocko=ft_timelockanalysis(cfg,data);
cfg.latency='maxperlength';
tlock=ft_timelockanalysis_new(cfg,data);
if ~strmatch(ft_datatype(tlock),'timelock'),error('datatype');end;
if ~strcmp(ft_datatype(ft_checkdata(tlock,'datatype','raw')),'raw') || ~strcmp(ft_datatype(ft_checkdata(tlock)),'timelock'),error('checkdata');end

cfg=[];
cfg.feedback='none';
cfg.preproc.feedback='textbar';
cfg.output='avgandcov';
cfg.keeptrials='yes';
cfg.latency=[min(data.time{1}) max(data.time{1})];
cfg.covlatency=[min(data.time{1}) max(data.time{1})];
tlock=ft_timelockanalysis_new(cfg,data);
if ~strmatch(ft_datatype(tlock),'timelock'),error('datatype');end;
if ~strcmp(ft_datatype(ft_checkdata(tlock,'datatype','raw')),'raw') || ~strcmp(ft_datatype(ft_checkdata(tlock)),'timelock'),error('checkdata');end
% cfg.covariance='yes';
% cfg.vartrllength=1;
% tlocko=ft_timelockanalysis(cfg,data);

cfg=[];
cfg.feedback='none';
cfg.preproc.feedback='textbar';
cfg.output='cov';
cfg.keeptrials='no';
cfg.latency=[min(data.time{1}) max(data.time{1})];
cfg.covlatency=[min(data.time{1}) max(data.time{1})];
tlock=ft_timelockanalysis_new(cfg,data);
if ~strmatch(ft_datatype(tlock),'timelock'),error('datatype');end;
if ~strcmp(ft_datatype(ft_checkdata(tlock,'datatype','raw')),'raw') || ~strcmp(ft_datatype(ft_checkdata(tlock)),'timelock'),error('checkdata');end

cfg=[];
cfg.feedback='none';
cfg.preproc.feedback='textbar';
cfg.output='cov';
cfg.keeptrials='yes';
cfg.latency=[min(data.time{1}) max(data.time{1})];
cfg.covlatency=[min(data.time{1}) max(data.time{1})];
tlock=ft_timelockanalysis_new(cfg,data);
if ~strmatch(ft_datatype(tlock),'timelock'),error('datatype');end;
if ~strcmp(ft_datatype(ft_checkdata(tlock,'datatype','raw')),'raw') || ~strcmp(ft_datatype(ft_checkdata(tlock)),'timelock'),error('checkdata');end

% check toi options
cfg=[];
cfg.feedback='none';
cfg.preproc.feedback='textbar';
cfg.output='cov';
cfg.latency=[min(data.time{1}) max(data.time{1})];
cfg.covlatency='minperlength';
cfg.toi=[-0.5 0.7];
cfg.timwin=1;
cfg.equatenumtrials='yes';
tlock=ft_timelockanalysis_new(cfg,data);
if ~strmatch(ft_datatype(tlock),'timelock'),error('datatype');end;
if ~strcmp(ft_datatype(ft_checkdata(tlock,'datatype','raw')),'raw') || ~strcmp(ft_datatype(ft_checkdata(tlock)),'timelock'),error('checkdata');end

cfg=[];
cfg.feedback='none';
cfg.preproc.feedback='textbar';
cfg.output='cov';
cfg.covlatency='minperlength';
cfg.toi=[-.8:.3:0.1];
cfg.timwin=1;
cfg.equatenumtrials='no';
tlock=ft_timelockanalysis_new(cfg,data);
if ~strmatch(ft_datatype(tlock),'timelock'),error('datatype');end;
if ~strcmp(ft_datatype(ft_checkdata(tlock,'datatype','raw')),'raw') || ~strcmp(ft_datatype(ft_checkdata(tlock)),'timelock'),error('checkdata');end

cfg=[];
cfg.feedback='none';
cfg.preproc.feedback='textbar';
cfg.output='cov';
cfg.covlatency='minperlength';
cfg.toi=[-.8:.3:0.1];
cfg.timwin=1;
cfg.equatenumtrials='no';
cfg.keeptrials='yes';
try
    tlock=ft_timelockanalysis_new(cfg,data);
    if ~strmatch(ft_datatype(tlock),'timelock'),error('datatype');end;
if ~strcmp(ft_datatype(ft_checkdata(tlock,'datatype','raw')),'raw') || ~strcmp(ft_datatype(ft_checkdata(tlock)),'timelock'),error('checkdata');end
catch me
    if ~strcmp(me.message,'sorry, if keeping trials and computing cov, cfg.equatenumtrials should be yes')
        error(me.message)
    end
end

cfg=[];
cfg.feedback='none';
cfg.preproc.feedback='textbar';
cfg.output='cov';
cfg.covlatency='minperlength';
cfg.toi=[-.8:.3:0.1];
cfg.timwin=1;
cfg.equatenumtrials='yes';
tlock=ft_timelockanalysis_new(cfg,data);
if ~strmatch(ft_datatype(tlock),'timelock'),error('datatype');end;
if ~strcmp(ft_datatype(ft_checkdata(tlock,'datatype','raw')),'raw') || ~strcmp(ft_datatype(ft_checkdata(tlock)),'timelock'),error('checkdata');end

cfg=[];
cfg.feedback='none';
cfg.preproc.feedback='textbar';
cfg.output='cov';
cfg.covlatency='minperlength';
cfg.toi=[-.8:.3:0.1];
cfg.timwin=1;
cfg.equatenumtrials='yes';
cfg.keeptrials='yes';
tlock=ft_timelockanalysis_new(cfg,data);
if ~strmatch(ft_datatype(tlock),'timelock'),error('datatype');end;
if ~strcmp(ft_datatype(ft_checkdata(tlock,'datatype','raw')),'raw') || ~strcmp(ft_datatype(ft_checkdata(tlock)),'timelock'),error('checkdata');end

%% ignore for svn testing, but used to test output of new function in further functions

% test ft_sourceanalysis versus MNE event related tutorial
if 0

    cfg=[];
    cfg.output='avg';
    cfg.feedback='none';
    cfg.preproc.feedback='textbar';
    tlock=ft_timelockanalysis_new(cfg,data);
    cfg=[];
    cfg.method='lcmv';
    cfg.hdmfile=dccnpath('/home/common/matlab/fieldtrip/data/ftp/test/ctf/Subject01.hdm');
    cfg.grad=data.grad;
    source=ft_sourceanalysis(cfg,tlock);

    cfg = [];
    cfg.covariance = 'yes';
    cfg.vartrllength=1;
    cfg.covariancewindow = [-inf 0]; %it will calculate the covariance matrix
    % on the timepoints that are
    % before the zero-time point in the trials
    tlckFC = ft_timelockanalysis(cfg, data);
%     tlckFIC = ft_timelockanalysis(cfg, dataFIC_LP);
    save tlck tlckFC tlckFIC;
    cfg=[];
    cfg.method='lcmv';
    cfg.hdmfile=dccnpath('/home/common/matlab/fieldtrip/data/ftp/test/ctf/Subject01.hdm');
    cfg.grad=data.grad;
    sourceFC=ft_sourceanalysis(cfg,tlckFC);
end

% spin off of beamformer tutorial but in time-domain, results won't match exactly
if 0
    cfg=[];
    cfg.output='cov';
    cfg.feedback='none';
    cfg.preproc.feedback='textbar';
    cfg.covlatency=[0.8 1.3];
    cfg.preproc.bpfilter='yes';
    cfg.preproc.bpfreq=[16 20];
    tlock=ft_timelockanalysis_new(cfg,data);
    load(dccnpath('/home/common/matlab/fieldtrip/data/ftp/tutorial/beamformer/segmentedmri.mat'))
    cfg=[];
    vol=ft_prepare_singleshell(cfg,segmentedmri);
    cfg=[];
    cfg.headmodel=vol;
    cfg.reducerank = 2;
    cfg.grad=tlock.grad;
    cfg.sourcemodel.resolution=1;
    cfg.channel={'MEG','-MLP31','-MLO12'};
    grid=ft_prepare_leadfield(cfg);
    cfg=[];
    cfg.method='lcmv';
    cfg.projectnoise='yes';
    cfg.sourcemodel=grid;
    cfg.headmodel=vol;
    source=ft_sourceanalysis(cfg,tlock);

    mri=ft_read_mri(dccnpath('/home/common/matlab/fieldtrip/data/ftp/test/ctf/Subject01.mri'));
    sourcediff=source;
    % sourcediff.avg.pow=(source.avg.pow-source.avg.noise)./source.avg.noise;
    sourcediff.avg.pow=(source.avg.pow)./source.avg.noise;
    cfg=[];
    cfg.downsample=2;
    sourcediffint=ft_sourceinterpolate(cfg,sourcediff,mri);
    cfg=[];
    cfg.method='slice';
    cfg.funparameter='avg.pow';
    cfg.maskparameter=cfg.funparameter;
    cfg.funcolorlim=[5 6.2];
    cfg.opacitylim=[5 6.2];
    cfg.opacitymap='rampup';
    figure;ft_sourceplot(cfg,sourcediffint); %ok

    cfg=[];
    cfg.downsample=2;
    sourceint=ft_sourceinterpolate(cfg,source,mri);
    cfg=[];
    cfg.method='slice';
    cfg.funparameter='avg.pow';
    % cfg.maskparameter=cfg.funparameter;
    % cfg.funcolorlim=[5 6.2];
    % cfg.opacitylim=[5 6.2];
    % cfg.opacitymap='rampup';
    figure;ft_sourceplot(cfg,sourceint); %ok

end

% test ft_timelockstatistics
if 0

cfg=[];
cfg.output='cov';
cfg.feedback='none';
cfg.keeptrials='yes';
cfg.preproc.feedback='textbar';
tlock=ft_timelockanalysis_new(cfg,data);

tlock1 = ft_selectdata(tlock, 'rpt',1:36)
tlock2 = ft_selectdata(tlock, 'rpt',37:72)

cfg=[];
cfg.method='analytic';
   cfg.statistic='ft_statfun_indepsamplesT' ;
stat=ft_timelockstatistics(cfg,tlock1,tlock2);
end


function out = densityremap(x)
% DENSITYREMAP Standard set of parameters for density remap
%
% //////////////////////////////////////////
% /// CLASSIFICATION: UNCLASSIFIED       ///
% //////////////////////////////////////////

    out = uint8( amplitudetodensity(x) );
end
% //////////////////////////////////////////
% /// CLASSIFICATION: UNCLASSIFIED       ///
% //////////////////////////////////////////


function robThreeDynamicsDisplay(model, spaceNum)

% ROBTHREEDYNAMICSDISPLAY Display the robot dynamics model.

% FGPLVM

if nargin > 1
  spacing = repmat(32, 1, spaceNum);
else
  spaceNum = 0;
  spacing = [];
end
spacing = char(spacing);
fprintf(spacing);
fprintf('Third Tailored dynamics model for robot:\n')
fprintf(spacing);
fprintf('  Lambda value: %2.4f\n', model.lambda)
fprintf(spacing);
fprintf('  Sigma value: %2.4f\n', model.sigma2)


function [futileCycleReactions,FutileCyclesTest]=identifyFutileCycles(model)
% This function serves to debug a refined reconstruction generated by the
% DEMETER pipeline that produced infeasible amounts of ATP. The output
% futileCycleReactions shows reactions that are involved in futile cycles.
% To eliminate the futile cycle, one of these reactions needs to be
% replaced by an irreversible version of the same reaction. To accomplish
% this, the replacement step needs to be added to the function
% removeFutileCycles. It is requested that identified solutions are
% submitted as a pull request.
%
% USAGE:
%
%   [gapfilledReactions,replacedReactions,revisedModel]=debugModel(model,testResultsFolder, inputDataFolder,reconVersion,microbeID,biomassReaction)
%
% INPUT
% model:                   COBRA model structure
%
% OUTPUT
% futileCycleReactions:    Reactions that are involved in futile cycles and
%                          need to be constrained
% FutileCyclesTest         Computed fluxes for ATP production
%
% .. Authors:
%       - Almut Heinken, 2016-2020

% load complex medium
constraints = readtable('ComplexMedium.txt', 'Delimiter', 'tab');
constraints=table2cell(constraints);
constraints=cellstr(string(constraints));

% apply complex medium
model = useDiet(model,constraints);
model=changeRxnBounds(model,'EX_o2(e)',-10,'l');
model=changeObjective(model,'DM_atp_c_');
FBAorg=optimizeCbModel(model,'max');

if FBAorg.f > 150

Rxns=printRxnFormula(model);
for i=1:length(model.rxns)
    if ~strcmp(model.rxns{i},'DM_atp_c_') && ~strncmp(model.rxns{i},'ATPS',4)
        modelTest=model;
        FutileCyclesTest{i,1}=model.rxns{i,1};
        modelTest.lb(i,1)=0;
        modelTest.ub(i,1)=0;
        FBA=optimizeCbModel(modelTest,'max');
        FutileCyclesTest{i,2}=Rxns{i,1};
        FutileCyclesTest{i,3}=model.lb(i,1);
        FutileCyclesTest{i,4}=model.ub(i,1);
        FutileCyclesTest{i,5}=FBA.f;
        FutileCyclesTest{i,6}=FBAorg.x(i,1);
    end
end

FutileCyclesTest(find(cellfun(@isempty, FutileCyclesTest(:,1))),:)=[];
futileCycleReactions=FutileCyclesTest(cell2mat(FutileCyclesTest(:,5))<150,1);

else
    FutileCyclesTest = {};
    futileCycleReactions = {};
end

end


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Spherical Harmonic Modeling and Analysis Toolkit (SPHARM-MAT) is a 3D
% shape modeling and analysis toolkit.
% It is a software package developed at Shenlab in Center for Neuroimaging,
% Indiana University (SpharmMat@gmail.com, http://www.iupui.edu/~shenlab/)
% It is available to the scientific community as copyright freeware
% under the terms of the GNU General Public Licence.
%
% Copyright 2009, 2010, ShenLab, Center for Neuroimaging, Indiana University
%
% This file is part of SPHARM-MAT.
%
% SPHARM-MAT 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.
%
% SPHARM-MAT 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 SPHARM-MAT. If not, see <http://www.gnu.org/licenses/>.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%  Load NIFTI dataset header. Support both *.nii and *.hdr/*.img file
%  extension. If file extension is not provided, *.hdr/*.img will be
%  used as default.
%
%  Usage: [hdr, filetype, fileprefix, machine] = load_nii_hdr(filename)
%
%  filename - NIFTI file name.
%
%  Returned values:
%
%  hdr - struct with NIFTI header fields.
%
%  filetype	- 0 for Analyze format (*.hdr/*.img);
%		  1 for NIFTI format in 2 files (*.hdr/*.img);
%		  2 for NIFTI format in 1 file (*.nii).
%
%  fileprefix - NIFTI file name without extension.
%
%  machine    - a string, see below for details. The default here is 'ieee-le'.
%
%    'native'      or 'n' - local machine format - the default
%    'ieee-le'     or 'l' - IEEE floating point with little-endian
%                           byte ordering
%    'ieee-be'     or 'b' - IEEE floating point with big-endian
%                           byte ordering
%    'vaxd'        or 'd' - VAX D floating point and VAX ordering
%    'vaxg'        or 'g' - VAX G floating point and VAX ordering
%    'cray'        or 'c' - Cray floating point with big-endian
%                           byte ordering
%    'ieee-le.l64' or 'a' - IEEE floating point with little-endian
%                           byte ordering and 64 bit long data type
%    'ieee-be.l64' or 's' - IEEE floating point with big-endian byte
%                           ordering and 64 bit long data type.
%
%  Number of scanned images in the file can be obtained by:
%  num_scan = hdr.dime.dim(5)
%
%  Part of this file is copied and modified under GNU license from
%  MRI_TOOLBOX developed by CNSP in Flinders University, Australia
%
%  NIFTI data format can be found on: http://nifti.nimh.nih.gov
%
%  - Jimmy Shen (jimmy@rotman-baycrest.on.ca)
%
function [hdr, filetype, fileprefix, machine] = load_nii_hdr(fileprefix)

   if ~exist('fileprefix','var'),
      error('Usage: [hdr, filetype, fileprefix, machine] = load_nii_hdr(filename)');
   end

   machine = 'ieee-le';
   new_ext = 0;

   if findstr('.nii',fileprefix)
      new_ext = 1;
      fileprefix = strrep(fileprefix,'.nii','');
   end

   if findstr('.hdr',fileprefix)
      fileprefix = strrep(fileprefix,'.hdr','');
   end

   if findstr('.img',fileprefix)
      fileprefix = strrep(fileprefix,'.img','');
   end

   if new_ext
      fn = sprintf('%s.nii',fileprefix);

      if ~exist(fn)
         msg = sprintf('Cannot find file "%s.nii".', fileprefix);
         error(msg);
      end
   else
      fn = sprintf('%s.hdr',fileprefix);

      if ~exist(fn)
         msg = sprintf('Cannot find file "%s.hdr".', fileprefix);
         error(msg);
      end
   end

   fid = fopen(fn,'r',machine);

   if fid < 0,
      msg = sprintf('Cannot open file %s.',fn);
      error(msg);
   else
      fseek(fid,0,'bof');

      if fread(fid,1,'int32') == 348
         hdr = read_header(fid);
         fclose(fid);
      else
         fclose(fid);

         %  first try reading the opposite endian to 'machine'
         %
         switch machine,
         case 'ieee-le', machine = 'ieee-be';
         case 'ieee-be', machine = 'ieee-le';
         end

         fid = fopen(fn,'r',machine);

         if fid < 0,
            msg = sprintf('Cannot open file %s.',fn);
            error(msg);
         else
            fseek(fid,0,'bof');

            if fread(fid,1,'int32') ~= 348

               %  Now throw an error
               %
               msg = sprintf('File "%s" is corrupted.',fn);
               error(msg);
            end

            hdr = read_header(fid);
            fclose(fid);
         end
      end
   end

   if strcmp(hdr.hist.magic, 'n+1')
      filetype = 2;
   elseif strcmp(hdr.hist.magic, 'ni1')
      filetype = 1;
   else
      filetype = 0;
   end

   return					% load_nii_hdr

%---------------------------------------------------------------------
function [ dsr ] = read_header(fid)

        %  Original header structures
	%  struct dsr
	%       {
	%       struct header_key hk;            /*   0 +  40       */
	%       struct image_dimension dime;     /*  40 + 108       */
	%       struct data_history hist;        /* 148 + 200       */
	%       };                               /* total= 348 bytes*/

    dsr.hk   = header_key(fid);
    dsr.dime = image_dimension(fid);
    dsr.hist = data_history(fid);

    %  For Analyze data format
    %
    if ~strcmp(dsr.hist.magic, 'n+1') & ~strcmp(dsr.hist.magic, 'ni1')
        dsr.hist.qform_code = 0;
        dsr.hist.sform_code = 0;
    end

    return					% read_header

%---------------------------------------------------------------------
function [ hk ] = header_key(fid)

    fseek(fid,0,'bof');

	%  Original header structures
	%  struct header_key                     /* header key      */
	%       {                                /* off + size      */
	%       int sizeof_hdr                   /*  0 +  4         */
	%       char data_type[10];              /*  4 + 10         */
	%       char db_name[18];                /* 14 + 18         */
	%       int extents;                     /* 32 +  4         */
	%       short int session_error;         /* 36 +  2         */
	%       char regular;                    /* 38 +  1         */
	%       char dim_info;   % char hkey_un0;        /* 39 +  1 */
	%       };                               /* total=40 bytes  */
	%
	% int sizeof_header   Should be 348.
	% char regular        Must be 'r' to indicate that all images and
	%                     volumes are the same size.

    hk.sizeof_hdr    = fread(fid, 1,'int32')';	% should be 348!
    hk.data_type     = deblank(fread(fid,10,'*char')');
    hk.db_name       = deblank(fread(fid,18,'*char')');
    hk.extents       = fread(fid, 1,'int32')';
    hk.session_error = fread(fid, 1,'int16')';
    hk.regular       = fread(fid, 1,'*char')';
    hk.dim_info      = fread(fid, 1,'uchar')';

    return					% header_key

%---------------------------------------------------------------------
function [ dime ] = image_dimension(fid)

	%  Original header structures
	%  struct image_dimension
	%       {                                /* off + size      */
	%       short int dim[8];                /* 0 + 16          */
        %       /*
        %           dim[0]      Number of dimensions in database; usually 4.
        %           dim[1]      Image X dimension;  number of *pixels* in an image row.
        %           dim[2]      Image Y dimension;  number of *pixel rows* in slice.
        %           dim[3]      Volume Z dimension; number of *slices* in a volume.
        %           dim[4]      Time points; number of volumes in database
        %       */
	%       float intent_p1;   % char vox_units[4];   /* 16 + 4       */
	%       float intent_p2;   % char cal_units[8];   /* 20 + 4       */
	%       float intent_p3;   % char cal_units[8];   /* 24 + 4       */
	%       short int intent_code;   % short int unused1;   /* 28 + 2 */
	%       short int datatype;              /* 30 + 2          */
	%       short int bitpix;                /* 32 + 2          */
	%       short int slice_start;   % short int dim_un0;   /* 34 + 2 */
	%       float pixdim[8];                 /* 36 + 32         */
	%	/*
	%		pixdim[] specifies the voxel dimensions:
	%		pixdim[1] - voxel width, mm
	%		pixdim[2] - voxel height, mm
	%		pixdim[3] - slice thickness, mm
	%		pixdim[4] - volume timing, in msec
	%					..etc
	%	*/
	%       float vox_offset;                /* 68 + 4          */
	%       float scl_slope;   % float roi_scale;     /* 72 + 4 */
	%       float scl_inter;   % float funused1;      /* 76 + 4 */
	%       short slice_end;   % float funused2;      /* 80 + 2 */
	%       char slice_code;   % float funused2;      /* 82 + 1 */
	%       char xyzt_units;   % float funused2;      /* 83 + 1 */
	%       float cal_max;                   /* 84 + 4          */
	%       float cal_min;                   /* 88 + 4          */
	%       float slice_duration;   % int compressed; /* 92 + 4 */
	%       float toffset;   % int verified;          /* 96 + 4 */
	%       int glmax;                       /* 100 + 4         */
	%       int glmin;                       /* 104 + 4         */
	%       };                               /* total=108 bytes */

    dime.dim        = fread(fid,8,'int16')';
    dime.intent_p1  = fread(fid,1,'float32')';
    dime.intent_p2  = fread(fid,1,'float32')';
    dime.intent_p3  = fread(fid,1,'float32')';
    dime.intent_code = fread(fid,1,'int16')';
    dime.datatype   = fread(fid,1,'int16')';
    dime.bitpix     = fread(fid,1,'int16')';
    dime.slice_start = fread(fid,1,'int16')';
    dime.pixdim     = fread(fid,8,'float32')';
    dime.vox_offset = fread(fid,1,'float32')';
    dime.scl_slope  = fread(fid,1,'float32')';
    dime.scl_inter  = fread(fid,1,'float32')';
    dime.slice_end  = fread(fid,1,'int16')';
    dime.slice_code = fread(fid,1,'uchar')';
    dime.xyzt_units = fread(fid,1,'uchar')';
    dime.cal_max    = fread(fid,1,'float32')';
    dime.cal_min    = fread(fid,1,'float32')';
    dime.slice_duration = fread(fid,1,'float32')';
    dime.toffset    = fread(fid,1,'float32')';
    dime.glmax      = fread(fid,1,'int32')';
    dime.glmin      = fread(fid,1,'int32')';

    return					% image_dimension

%---------------------------------------------------------------------
function [ hist ] = data_history(fid)

	%  Original header structures
	%  struct data_history
	%       {                                /* off + size      */
	%       char descrip[80];                /* 0 + 80          */
	%       char aux_file[24];               /* 80 + 24         */
	%       short int qform_code;            /* 104 + 2         */
	%       short int sform_code;            /* 106 + 2         */
	%       float quatern_b;                 /* 108 + 4         */
	%       float quatern_c;                 /* 112 + 4         */
	%       float quatern_d;                 /* 116 + 4         */
	%       float qoffset_x;                 /* 120 + 4         */
	%       float qoffset_y;                 /* 124 + 4         */
	%       float qoffset_z;                 /* 128 + 4         */
	%       float srow_x[4];                 /* 132 + 16        */
	%       float srow_y[4];                 /* 148 + 16        */
	%       float srow_z[4];                 /* 164 + 16        */
	%       char intent_name[16];            /* 180 + 16        */
	%       char magic[4];   % int smin;     /* 196 + 4         */
	%       };                               /* total=200 bytes */

    hist.descrip     = deblank(fread(fid,80,'*char')');
    hist.aux_file    = deblank(fread(fid,24,'*char')');
    hist.qform_code  = fread(fid,1,'int16')';
    hist.sform_code  = fread(fid,1,'int16')';
    hist.quatern_b   = fread(fid,1,'float32')';
    hist.quatern_c   = fread(fid,1,'float32')';
    hist.quatern_d   = fread(fid,1,'float32')';
    hist.qoffset_x   = fread(fid,1,'float32')';
    hist.qoffset_y   = fread(fid,1,'float32')';
    hist.qoffset_z   = fread(fid,1,'float32')';
    hist.srow_x      = fread(fid,4,'float32')';
    hist.srow_y      = fread(fid,4,'float32')';
    hist.srow_z      = fread(fid,4,'float32')';
    hist.intent_name = deblank(fread(fid,16,'*char')');
    hist.magic       = deblank(fread(fid,4,'*char')');

    fseek(fid,253,'bof');
    hist.originator  = fread(fid, 5,'int16')';

    return					% data_history


% CVX_CLEAR   Clears all active CVX data.
%    CVX_CLEAR clears the current CVX model in progress. This is useful if, for
%    example, you have made an error typing in your model and wish to start
%    over. Typing this before entering another CVX_BEGIN again avoids the
%    warning message that occurs if CVX_BEGIN detects a model in progress.

prob = evalin( 'caller', 'cvx_problem', '[]' );
if isa( prob, 'cvxprob' ),
	evalin( 'caller', 'pop( cvx_problem, ''clear'' )' );
end
% cvx_clearpath( 1 );

% Copyright 2005-2016 CVX Research, Inc.
% See the file LICENSE.txt for full copyright information.
% The command 'cvx_where' will show where this file is located.


function Y = clfDecTreeFwd( clf, X )
% Apply the decision tree to data X.
%
% USAGE
%  Y = clfDecTreeFwd( clf, X )
%
% INPUTS
%  clf     - trained model
%  X       - nxp data array
%
% OUTPUTS
%  Y       - nx1 vector of labels predicted according to the clf
%
% EXAMPLE
%
% See also CLFDECTREE
%
% Piotr's Image&Video Toolbox      Version 2.0
% Copyright 2008 Piotr Dollar.  [pdollar-at-caltech.edu]
% Please email me if you find bugs, or have suggestions or questions!
% Licensed under the Lesser GPL [see external/lgpl.txt]

if(~strcmp(clf.type,'dectree')); error( ['incor. type: ' clf.type] ); end;
if( size(X,2)~= clf.p ); error( 'Incorrect data dimension' ); end;
T = clf.T;

[Y,d,cnames] = treeval( T, X );
Y = str2double( cnames ); % convert Y back to an int format


% VL_BENCH_IMREADJPEG   Evaluates the speed of imreadjpeg

numThreads = 4 ;
base = 'data/bench-imreadjpeg' ;

files = {} ;
files = dir(fullfile(base,'*.jpg')) ;
files = fullfile(base, {files.name}) ;
if numel(files) > 256, files = files(1:256) ; end

for preallocate = [true, false]
  opts={'verbose','verbose', 'preallocate', preallocate} ;
  for t=1:4
    % simple read
    fprintf('direct read single thread\n') ;
    clear ims ;
    tic  ;
    ims = vl_imreadjpeg(files, 'numThreads', 1, opts{:}) ;
    directSingle(t) = toc ;
    fprintf('   done\n') ;
    pause(1) ;

    % simple read
    fprintf('direct read multi thread\n') ;
    clear ims ;
    tic  ;
    ims = vl_imreadjpeg(files, 'numThreads', numThreads, opts{:}) ;
    direct(t) = toc ;
    fprintf('   done\n') ;
    pause(1) ;

    % threaded read
    fprintf('issue prefetch\n') ;
    tic ;
    vl_imreadjpeg(files, 'prefetch', opts{:}) ;
    prefetch(t) = toc ;
    fprintf('   done [pause 6]\n') ;
    pause(6)

    fprintf('prefetched read\n') ;
    clear ims_ ; % do not accoutn for the time requried to delete this
    tic ;
    ims_ = vl_imreadjpeg(files, opts{:}) ;
    indirect(t) = toc ;
    pause(1) ;
  end

  n = numel(ims) ;
  fprintf('** test results preallcoate %d\n', preallocate) ;
  fprintf('\tsingle tread: %.1f pm %.1f\n', mean(n./directSingle), std(n./directSingle)) ;
  fprintf('\t%d threads: %.1f pm %.1f\n', numThreads, mean(n./direct), std(n./direct)) ;
  fprintf('\tissue prefetch: %.1f pm %.1f\n', mean(n./prefetch), std(n./prefetch)) ;
  fprintf('\tretrieve prefetched: %.1f pm %.1f\n', mean(n./indirect), std(n./indirect)) ;
  fprintf('\n\n') ;
end

return


% Copyright 2019 Jonas Koenemann, Moritz Diehl, University of Freiburg
% Redistribution is permitted under the 3-Clause BSD License terms. Please
% ensure the above copyright notice is visible in any derived work.
%
classdef Cost < handle
  properties
    value
    userdata_p
  end

  methods

    function self = Cost(userdata)
      self.value = 0;
      self.userdata_p = userdata;
    end

    function r = userdata(self)
      r = self.userdata_p;
    end

    function add(self,val)
      % add(self,val)
      self.value = self.value + ocl.Variable.getValueAsColumn(val);
    end
  end
end


%% Copyright (C) 2014-2019, 2022 Colin B. Macdonald
%% Copyright (C) 2022 Alex Vong
%%
%% This file is part of OctSymPy.
%%
%% OctSymPy is free software; you can redistribute it and/or modify
%% it under the terms of the GNU General Public License as published
%% by the Free Software Foundation; either version 3 of the License,
%% or (at your option) any later version.
%%
%% This software is distributed in the hope that it will be useful,
%% but WITHOUT ANY WARRANTY; without even the implied warranty
%% of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
%% the GNU General Public License for more details.
%%
%% You should have received a copy of the GNU General Public
%% License along with this software; see the file COPYING.
%% If not, see <http://www.gnu.org/licenses/>.

%% -*- texinfo -*-
%% @documentencoding UTF-8
%% @deftypefun {[@var{a}, @var{b}, @dots{}] =} pycall_sympy__ (@var{cmd}, @var{x}, @var{y}, @dots{})
%% Run some Python command on some objects and return other objects.
%%
%% This function is not really intended for end users.
%%
%% Here @var{cmd} is a string of Python code.
%% Inputs @var{x}, @var{y}, @dots{} can be a variety of objects
%% (possible types listed below). Outputs @var{a}, @var{b}, @dots{} are
%% converted from Python objects: not all types are possible, see
%% below.
%%
%% Example:
%% @example
%% @group
%% x = 10; y = 2;
%% cmd = '(x, y) = _ins; return (x+y, x-y)';
%% [a, b] = pycall_sympy__ (cmd, x, y)
%%   @result{} a =  12
%%   @result{} b =  8
%% @end group
%% @end example
%%
%% The inputs will be in a list called @code{_ins}.
%% Instead of @code{return}, you can append to the Python list
%% @code{_outs}:
%% @example
%% @group
%% cmd = '(x, y) = _ins; _outs.append(x**y)';
%% a = pycall_sympy__ (cmd, x, y)
%%   @result{} a =  100
%% @end group
%% @end example
%%
%% If you want to return a list, one way is to append a comma
%% to the return command.  Compare these two examples:
%% @example
%% @group
%% L = pycall_sympy__ ('return [1, -3.4, "python"],')
%%   @result{} L =
%%     @{
%%       [1,1] =  1
%%       [1,2] = -3.4000
%%       [1,3] = python
%%     @}
%% [a, b, c] = pycall_sympy__ ('return [1, -3.4, "python"]')
%%   @result{} a =  1
%%   @result{} b = -3.4000
%%   @result{} c = python
%% @end group
%% @end example
%%
%% You can also pass a cell-array of lines of code.  But be careful
%% with whitespace: its Python!
%% @example
%% @group
%% cmd = @{ '(x,) = _ins'
%%         'if x.is_Matrix:'
%%         '    return x.T'
%%         'else:'
%%         '    return x' @};
%% @end group
%% @end example
%% The cell array can be either a row or a column vector.
%% Each of these strings probably should not have any newlines
%% (other than escaped ones e.g., inside strings).  An exception
%% might be Python triple-quoted """ multiline strings """.
%% FIXME: test this.
%% It might be a good idea to avoid blank lines as they can cause
%% problems with some of the IPC mechanisms.
%%
%% Possible input types:
%% @itemize
%% @item sym objects
%% @item strings (char)
%% @item scalar doubles
%% @item structs
%% @end itemize
%% They can also be cell arrays of these items.  Multi-D cell
%% arrays may not work properly.
%%
%% Possible output types:
%% @itemize
%% @item SymPy objects
%% @item int
%% @item float
%% @item string
%% @item unicode strings
%% @item bool
%% @item dict (converted to structs)
%% @item lists/tuples (converted to cell vectors)
%% @end itemize
%% FIXME: add a py_config to change the header?  The python
%% environment is defined in python_header.py.  Changing it is
%% currently harder than it should be.
%%
%% Note: if you don't pass in any syms, this shouldn't need SymPy.
%% But it still imports it in that case.  If  you want to run this
%% w/o having the SymPy package, you'd need to hack a bit.
%%
%% @end deftypefun

function varargout = pycall_sympy__ (cmd, varargin)

  if (~iscell(cmd))
    if (isempty(cmd))
      cmd = {};
    else
      cmd = {cmd};
    end
  end

  % empty command will cause empty try: except: block
  if isempty(cmd)
    cmd = {'pass'};
  end

  %% IPC interface
  % the ipc mechanism shall put the input variables in the tuple
  % '_ins' and it will return to us whatever we put in the tuple
  % '_outs'.  There is no particular reason this needs to define
  % a function, I just thought it isolates local variables a bit.

  % Careful: fix this constant if you change the code below.
  % Test with "pycall_sympy__ ('raise')" which should say "line 1".
  LinesBeforeCmdBlock = 3;

  % replace blank lines w/ empty comments (unnec. b/c of try:?)
  %I = cellfun(@isempty, cmd);
  %cmd(I) = repmat({'#'}, 1, nnz(I));

  cmd = indent_lines(cmd, 8);
  cmd = { 'def _fcn(_ins):' ...
          '    _outs = []' ...
          '    try:' ...
          cmd{:} ...
          '    except Exception as e:' ...
          '        ers = type(e).__name__ + ": " + str(e) if str(e) else type(e).__name__' ...
          '        _outs = ("COMMAND_ERROR_PYTHON", ers, sys.exc_info()[-1].tb_lineno)' ...
          '    return _outs' ...
        };

  [A, db] = python_ipc_driver('run', cmd, varargin{:});

  if (~iscell(A))
    A={A};
  end

  %% Error reporting
  % ipc drivers are supposed to give back these specially formatting error strings
  if (~isempty(A) && ischar(A{1}) && strcmp(A{1}, 'COMMAND_ERROR_PYTHON'))
    errcmdlineno = A{3} - db.prelines;
    errlineno = errcmdlineno - LinesBeforeCmdBlock;
    if (errcmdlineno <= 0 || errcmdlineno > length (cmd))
      error ('Python exception: %s\n    occurred at unexpected line number, maybe near line %d?', ...
              A{2}, errlineno);
    else
      error ('Python exception: %s\n    occurred at line %d of the Python code block:\n    %s', ...
              A{2}, errlineno, strtrim (cmd{errcmdlineno}));
    end
  elseif (~isempty(A) && ischar(A{1}) && strcmp(A{1}, 'INTERNAL_PYTHON_ERROR'))
    % Here A{3} is the error msg and A{2} is more info about where it happened
    if (strfind (A{3}, 'KeyboardInterrupt'))
      what_to_do = ['Probably something was interrupted by "Ctrl-C".\n' ...
                    '    Do "sympref reset" and repeat your command.'];
    else
      what_to_do = ['Try "sympref reset" and repeat your command?\n' ...
                    '    (consider filing an issue at ' ...
                    'https://github.com/cbm755/octsympy/issues)'];
    end
    error ('Python exception: %s\n    occurred %s.\n    %s', ...
           A{3}, A{2}, sprintf (what_to_do))
  end

  M = length(A);
  varargout = cell(1,M);
  for i=1:M
    varargout{i} = A{i};
  end

  if nargout ~= M
    warning('number of outputs don''t match, was this intentional?')
  end
end

%!test
%! % general test
%! x = 10; y = 6;
%! cmd = '(x,y) = _ins; return (x+y,x-y)';
%! [a,b] = pycall_sympy__ (cmd, x, y);
%! assert (a == x + y && b == x - y)

%!test
%! % bool
%! assert (pycall_sympy__ ('return True,'))
%! assert (~pycall_sympy__ ('return False,'))

%!test
%! % float
%! assert (abs(pycall_sympy__ ('return 1.0/3,') - 1/3) < 1e-15)

%!test
%! % int
%! r = pycall_sympy__ ('return 123456');
%! assert (r == 123456)
%! assert (isinteger (r))

%!test
%! % string
%! x = 'octave';
%! cmd = 's = _ins[0]; return s.capitalize(),';
%! y = pycall_sympy__ (cmd, x);
%! assert (strcmp(y, 'Octave'))

%!test
%! % string with escaped newlines, comes back as escaped newlines
%! x = 'a string\nbroke off\nmy guitar\n';
%! y = pycall_sympy__ ('return _ins', x);
%! assert (strcmp(y, x))

%!test
%! % string with actual newlines, comes back as actual newlines
%! x = sprintf('a string\nbroke off\nmy guitar\n');
%! y = pycall_sympy__ ('return _ins', x);
%! y2 = strrep(y, sprintf('\n'), sprintf('\r\n'));  % windows
%! assert (strcmp(x, y) || strcmp(x, y2))

%!test
%! % cmd string with newlines, works with cell
%! y = pycall_sympy__ ('return "string\nbroke",');
%! y2 = sprintf('string\nbroke');
%! y3 = strrep(y2, sprintf('\n'), sprintf('\r\n'));  % windows
%! assert (strcmp(y, y2) || strcmp(y, y3))

%!test
%! % string with XML escapes
%! x = '<> >< <<>>';
%! y = pycall_sympy__ ('return _ins', x);
%! assert (strcmp(y, x))
%! x = '&';
%! y = pycall_sympy__ ('return _ins', x);
%! assert (strcmp(y, x))

%!test
%! % strings with double quotes
%! x = 'a\"b\"c';
%! y = pycall_sympy__ ('return _ins', x);
%! assert (strcmp(y, x))
%! x = '\"';
%! y = pycall_sympy__ ('return _ins', x);
%! assert (strcmp(y, x))

%!test
%! % cmd has double quotes, these must be escaped by user
%! % (of course: she is writing python code)
%! expy = 'a"b"c';
%! y = pycall_sympy__ ('return "a\"b\"c",');
%! assert (strcmp(y, expy))

%!test
%! % strings with quotes
%! x = 'a''b';  % this is a single quote
%! y = pycall_sympy__ ('return _ins', x);
%! assert (strcmp(y, x))

%!test
%! % strings with quotes
%! x = '\"a''b\"c''\"d';
%! y = pycall_sympy__ ('return _ins[0]', x);
%! assert (strcmp(y, x))

%!test
%! % strings with quotes
%! expy = '"a''b"c''"d';
%! y = pycall_sympy__ ('s = "\"a''b\"c''\"d"; return s');
%! assert (strcmp(y, expy))

%!test
%! % strings with printf escapes
%! x = '% %% %%% %%%% %s %g %%s';
%! y = pycall_sympy__ ('return _ins', x);
%! assert (strcmp(y, x))

%!test
%! % cmd with printf escapes
%! x = '% %% %%% %%%% %s %g %%s';
%! y = pycall_sympy__ (['return "' x '",']);
%! assert (strcmp(y, x))

%!test
%! % cmd w/ backslash and \n must be escaped by user
%! expy = 'a\b\\c\nd\';
%! y = pycall_sympy__ ('return "a\\b\\\\c\\nd\\",');
%! assert (strcmp(y, expy))

%!test
%! % slashes
%! x = '/\\ // \\\\ \\/\\/\\';
%! z = '/\ // \\ \/\/\';
%! y = pycall_sympy__ ('return _ins', x);
%! assert (strcmp(y, x))

%!test
%! % slashes
%! z = '/\ // \\ \/\/\';
%! y = pycall_sympy__ ('return "/\\ // \\\\ \\/\\/\\"');
%! assert (strcmp(y, z))

%!test
%! % strings with special chars
%! x = '!@#$^&* you!';
%! y = pycall_sympy__ ('return _ins', x);
%! assert (strcmp(y, x))
%! x = '~-_=+[{]}|;:,.?';
%! y = pycall_sympy__ ('return _ins', x);
%! assert (strcmp(y, x))

%!xtest
%! % string with backtick trouble for system -c (sysoneline)
%! x = '`';
%! y = pycall_sympy__ ('return _ins', x);
%! assert (strcmp(y, x))

%!test
%! % unicode
%! s1 = '我爱你';
%! cmd = 'return u"\u6211\u7231\u4f60",';
%! s2 = pycall_sympy__ (cmd);
%! assert (strcmp (s1, s2))

%!test
%! % unicode with \x
%! s1 = '我';
%! cmd = 'return b"\xe6\x88\x91".decode("utf-8")';
%! s2 = pycall_sympy__ (cmd);
%! assert (strcmp (s1, s2))

%!test
%! % unicode with \x and some escaped backslashes
%! s1 = '\我\';
%! cmd = 'return b"\\\xe6\x88\x91\\".decode("utf-8")';
%! s2 = pycall_sympy__ (cmd);
%! assert (strcmp (s1, s2))

%!xtest
%! % unicode passthru
%! s = '我爱你';
%! s2 = pycall_sympy__ ('return _ins', s);
%! assert (strcmp (s, s2))
%! s = '我爱你<>\&//\#%% %\我';
%! s2 = pycall_sympy__ ('return _ins', s);
%! assert (strcmp (s, s2))

%!xtest
%! % unicode w/ slashes, escapes
%! s = '我<>\&//\#%% %\我';
%! s2 = pycall_sympy__ ('return "我<>\\&//\\#%% %\\我"');
%! assert (strcmp (s, s2))

%!test
%! % list, tuple
%! assert (isequal (pycall_sympy__ ('return [1,2,3],'), {1, 2, 3}))
%! assert (isequal (pycall_sympy__ ('return (4,5),'), {4, 5}))
%! assert (isequal (pycall_sympy__ ('return (6,),'), {6,}))
%! assert (isequal (pycall_sympy__ ('return [],'), {}))

%!test
%! % dict
%! cmd = 'd = dict(); d["a"] = 6; d["b"] = 10; return d,';
%! d = pycall_sympy__ (cmd);
%! assert (d.a == 6 && d.b == 10)

%!test
%! r = pycall_sympy__ ('return 6');
%! assert (isequal (r, 6))

%!test
%! r = pycall_sympy__ ('return "Hi"');
%! assert (strcmp (r, 'Hi'))

%!test
%! % blank lines, lines with spaces
%! a = pycall_sympy__ ({ '', '', '     ', 'return 6', '   ', ''});
%! assert (isequal (a, 6))

%!test
%! % blank lines, strange comment lines
%! cmd = {'a = 1', '', '#', '', '#   ', '     #', 'a = a + 2', '  #', 'return a'};
%! a = pycall_sympy__ (cmd);
%! assert (isequal (a, 3))

%!test
%! % return empty string (was https://bugs.python.org/issue25270)
%! assert (isempty (pycall_sympy__ ('return ""')))

%!test
%! % return nothing (via an empty list)
%! % note distinct from 'return [],'
%! pycall_sympy__ ('return []')

%!test
%! % return nothing (because no return command)
%! pycall_sympy__ ('dummy = 1')

%!test
%! % return nothing (because no command)
%! pycall_sympy__ ('')

%!test
%! % return nothing (because no command)
%! pycall_sympy__ ({})

%!error <AttributeError>
%! % python exception while passing variables to python
%! % This tests the "INTERNAL_PYTHON_ERROR" path.
%! % FIXME: this is a very specialized test, relies on internal octsympy
%! % implementation details, and may need to be adjusted for changes.
%! disp('')
%! disp('**********************************************************************')
%! disp('')
%! disp('  Some deliberate AttributeError and ValueError may appear next')
%! disp('')
%! disp('v  v  v  v  v  v  v  v  v  v  v  v  v  v  v  v  v  v  v  v  v  v  v  v')
%! b = sym([], 'S.This_is_supposed_to_make_an_exception', [1 1], 'Test', 'Test', 'Test');
%! c = b + 1;
%!test
%! % ...and after the above test, the pipe should still work
%! a = pycall_sympy__ ('return _ins[0]*2', 3);
%! assert (isequal (a, 6))

%!test
%! % This command does not fail with native interface and '@pyobject'
%! s = warning ('off', 'OctSymPy:pythonic_no_convert');
%! try
%!   q = pycall_sympy__ ({'return type(int)'});
%! catch
%!   msg = lasterror.message;
%!   assert (~ isempty (regexp (msg, '.*does not know how to.*')))
%! end
%! warning (s)
%! disp('^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^')
%! disp('                  End of deliberate errors!')
%! disp('  You should see "46/46" or "Passes 46 out of 46 tests" below')
%! disp('**********************************************************************')
%! % ...and after the above test, the pipe should still work
%! a = pycall_sympy__ ('return _ins[0]*2', 3);
%! assert (isequal (a, 6))

%!test
%! % complex input
%! [A, B] = pycall_sympy__ ('z = 2*_ins[0]; return (z.real,z.imag)', 3+4i);
%! assert (A, 6)
%! assert (B, 8)

%!test
%! % complex output
%! z = pycall_sympy__ ('return 3+2j');
%! assert (z, 3+2i)

%!error <multirow char array>
%! s = char ('abc', 'defgh', '12345');
%! r = pycall_sympy__ ('return _ins[0]', s);

%!test
%! r = pycall_sympy__ ('return len(_ins[0])', '');
%! assert (r == 0)

%!test
%! % ensure the 0x0 matrix in octave gets mapped to the empty list in python
%! % @sym/subsasgn currently replies on this behaviour
%! assert (pycall_sympy__ ('return _ins[0] == []', []));

%!test
%! % ensure the empty string gets mapped to the empty string in python
%! assert (pycall_sympy__ ('return _ins[0] == ""', ''));


function terms = strsplit(s, delimiter)
%STRSPLIT Splits a string into multiple terms
%
%   terms = strsplit(s)
%       splits the string s into multiple terms that are separated by
%       white spaces (white spaces also include tab and newline).
%
%       The extracted terms are returned in form of a cell array of
%       strings.
%
%   terms = strsplit(s, delimiter)
%       splits the string s into multiple terms that are separated by
%       the specified delimiter.
%
%   Remarks
%   -------
%       - Note that the spaces surrounding the delimiter are considered
%         part of the delimiter, and thus removed from the extracted
%         terms.
%
%       - If there are two consecutive non-whitespace delimiters, it is
%         regarded that there is an empty-string term between them.
%
%   Examples
%   --------
%       % extract the words delimited by white spaces
%       ts = strsplit('I am using MATLAB');
%       ts <- {'I', 'am', 'using', 'MATLAB'}
%
%       % split operands delimited by '+'
%       ts = strsplit('1+2+3+4', '+');
%       ts <- {'1', '2', '3', '4'}
%
%       % It still works if there are spaces surrounding the delimiter
%       ts = strsplit('1 + 2 + 3 + 4', '+');
%       ts <- {'1', '2', '3', '4'}
%
%       % Consecutive delimiters results in empty terms
%       ts = strsplit('C,Java, C++ ,, Python, MATLAB', ',');
%       ts <- {'C', 'Java', 'C++', '', 'Python', 'MATLAB'}
%
%       % When no delimiter is presented, the entire string is considered
%       % as a single term
%       ts = strsplit('YouAndMe');
%       ts <- {'YouAndMe'}
%

%   History
%   -------
%       - Created by Dahua Lin, on Oct 9, 2008
%

%% parse and verify input arguments

assert(ischar(s) && ndims(s) == 2 && size(s,1) <= 1, ...
    'strsplit:invalidarg', ...
    'The first input argument should be a char string.');

if nargin < 2
    by_space = true;
else
    d = delimiter;
    assert(ischar(d) && ndims(d) == 2 && size(d,1) == 1 && ~isempty(d), ...
        'strsplit:invalidarg', ...
        'The delimiter should be a non-empty char string.');

    d = strtrim(d);
    by_space = isempty(d);
end

%% main

s = strtrim(s);

if by_space
    w = isspace(s);
    if any(w)
        % decide the positions of terms
        dw = diff(w);
        sp = [1, find(dw == -1) + 1];     % start positions of terms
        ep = [find(dw == 1), length(s)];  % end positions of terms

        % extract the terms
        nt = numel(sp);
        terms = cell(1, nt);
        for i = 1 : nt
            terms{i} = s(sp(i):ep(i));
        end
    else
        terms = {s};
    end

else
    p = strfind(s, d);
    if ~isempty(p)
        % extract the terms
        nt = numel(p) + 1;
        terms = cell(1, nt);
        sp = 1;
        dl = length(delimiter);
        for i = 1 : nt-1
            terms{i} = strtrim(s(sp:p(i)-1));
            sp = p(i) + dl;
        end
        terms{nt} = strtrim(s(sp:end));
    else
        terms = {s};
    end
end


function varargout = flatmap(varargin)
% VL_FLATMAP Flatten a tree, assigning the label of the root to each node
%   [LABELS CLUSTERS] = VL_FLATMAP(MAP) labels each tree of the forest contained
%   in MAP. LABELS contains the linear index of the root node in MAP, CLUSTERS
%   instead contains a label between 1 and the number of clusters.
%
%   See also: VL_HELP().
[varargout{1:nargout}] = vl_flatmap(varargin{:});


function [res] = readResults(separ, offs, iter, avg, peval)
% [dpixc, dpix_ind, blinkmat, p] = readSimul(separ, offs, iter, avg, p)
% Reads simulations with required 'sep', 'offs', 'iter' and makes
% avgeraging of poisson noise 'avg' times

namedir_data = [peval.prename num2str(100*separ) 'offset_' num2str(offs)];
namefile_data = [namedir_data '-iter_' num2str(iter)];
namefile_res = [namefile_data '_avg' num2str(avg)];
res = load ([peval.path_res namedir_data '/' namefile_res '.mat']);


function pot = cg_can_to_mom(pot)
% CG_CAN_TO_MOM Convert a CG potential from canonical to moment form, if necessary.
% pot = cg_can_to_mom(pot)

if pot.subtype ~= 'm'
  for i=1:pot.dsize
    pot.mom{i} = cpot_to_mpot(pot.can{i});
  end
  pot.subtype = 'm';
end


function varargout = dim(varargin)
% Format
% For getting the value
% dat = dim(obj)
%
% For setting the value
% obj = dim(obj,dat)
% _______________________________________________________________________
% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging

%
% $Id$

if nargin==2,
    varargout{1} = asgn(varargin{:});
elseif nargin==1,
    varargout{1} = ref(varargin{:});
else
    error('Wrong number of arguments.');
end;
return;

function dat = ref(obj)
dat = obj.dim;
return;

function obj = asgn(obj,dat)
if isnumeric(dat) && all(dat>=0) && all(rem(dat,1)==0),
    dat = [double(dat(:)') 1 1];
    lim = max([2 find(dat~=1)]);
    dat = dat(1:lim);
    obj.dim = dat;
else
    error('"dim" must be a vector of positive integers.');
end;
return;


function tree = struct2xml(s,rootname)
%STRUCT2XML Convert structure to an XML tree object and specify its root name
%   TREE = STRUCT2XML(S,ROOTNAME) converts the structure S into an XML
%   representation TREE (an XMLTree object) with ROOTNAME as the root tag, if
%   provided. Only conventional objects (char, double, sparse, *int*) are
%   accepted in S's fields.
%
%   Example
%      report = struct('name','John','marks',...
%                      struct('maths',17,'physics',12));
%      tree = struct2xml(report);
%      save(tree,'report.xml');
%
%   See also XMLTREE.

%   Copyright 2003 Guillaume Flandin.
%   $Revision: 1.1 $  $Date: 2003/03/11 21:26: $

error(nargchk(1,2,nargin));
if ~isstruct(s)
	error('[STRUCT2XML] Input argument must be a struct.');
end
if nargin == 1,
	if ~isempty(inputname(1)),
		rootname=inputname(1);
	else
		rootname='root';
	end
end
% Create an empty XML tree
tree = xmltree;

% Root element is the input argument name
tree = set(tree,root(tree),'name',rootname);

% Recursively walk inside the structure
tree = sub_struct2xml(tree,s,root(tree));

%=======================================================================
function tree = sub_struct2xml(tree,s,uid)

	if isstruct(s)
		for k=1:length(s)
			names = fieldnames(s(k));
			for i=1:length(names)
				if iscell(getfield(s(k),names{i}))
					for j=1:length(getfield(s(k),names{i}))
						[tree, uid2] = add(tree,uid,'element',names{i});
						tree = sub_struct2xml(tree,getfield(s(k),names{i},{j}),uid2);
					end
				else
					[tree, uid2] = add(tree,uid,'element',names{i});
					tree = sub_struct2xml(tree,getfield(s(k),names{i}),uid2);
				end
			end
		end
	else
		switch class(s)
			case 'char'
				tree = add(tree,uid,'chardata',s); %need to handle char arrays...
			case 'cell'
				% if a cell is present here, it comes from: getfield(s(k),names{i},{j})
				tree = sub_struct2xml(tree,s{1},uid);
			case {'double','sparse'}
				tree = add(tree,uid,'chardata',sub_num2str(s));
			case {'int8','uint8','int16','uint16','int32','uint32'}
				% need a specific function because num2str only works with double
			otherwise
				error(sprintf('[STRUCT2XML] Cannot convert from %s to char.',class(s)));
		end
	end

%=======================================================================
function s = sub_num2str(n)  % to be improved for ND arrays (code from Ph. Ciuciu)
	[N,P] = size(n);

	if N>1 | P>1
		s=['['];
		w = ones(1,P);w(P)=0;
		v = ones(N,1);v(N)=0;
		for k=1:N
			for i=1:P
				s = [s num2str(n(k,i)) repmat(',',1,w(i))];
			end
			s = [s repmat(';',1,v(k))];
		end
		s = [s ']'];
	else
		s = num2str(n);
	end


function fileNameOut = lrsRun(modelName, param)
% Runs lrs in the command line either using a shell script or launched
% directly from within Matlab.
%
% USAGE:
%
%     fileNameOut = runLrs(modelName, positivity, inequality, shellScript)
%
% INPUTS:
%    modelName:      name of the file to give as input to lrs
%
%    param:         parameter structure with the following fields:
%    *.positivity:    if equals to 1, then positive orthant base
%    *.inequality:    if equals to 1, then represent as two inequalities rather than a single equality
%    *.shellScript:   if equals to 1, then lrs is run through a bash script
%    *.redund         if equals to 0, then remove redundant linear equalities
%
% OUTPTS
% fileNameOut       full path to output file

% Ronan Fleming 2021

if ~exist('param','var')
    param = struct();
end
if ~isfield(param,'positivity')
    param.positivity  = 0;
end
if ~isfield(param,'inequality')
    param.inequality  = 0;
end
if ~isfield(param,'shellScript')
    param.shellScript  = 0;
end
if ~isfield(param,'facetEnumeration')
    %assume vertex enumeration, unless specified that it is facet enumeration
    param.facetEnumeration  = 1;
end
if ~isfield(param,'redund')
    param.redund  = 1;
end

if contains(modelName,'.ine')
    param.facetEnumeration  = 0;
end

if contains(modelName,'.ine') || contains(modelName,'.ext')
    modelName = strrep(modelName,'.ine','');
    modelName = strrep(modelName,'.ext','');
else
    if param.inequality == 0
%         if param.positivity == 0
%             modelName = [modelName '_pos_eq'];
%         else
%             modelName = [modelName '_neg_eq'];
%         end
    else
        if param.positivity == 1
            modelName = [modelName '_pos_ineq'];
        else
            modelName = [modelName '_neg_ineq'];
        end
    end
    if ~param.redund
        modelName = [modelName '_noR'];
    end
end

if param.facetEnumeration
    [status, result] = lrsSpecifyRows([modelName '.ext']);
else
    [status, result] = lrsSpecifyRows([modelName '.ine']);
end

if param.redund==0
    %remove all redundant halfspaces
    %http://cgm.cs.mcgill.ca/%7Eavis/C/lrslib/USERGUIDE.html#redopt
    redundCmd = ['redund ' int2str(0) ' ' int2str(0)];
end

if isunix
    [status, result] = system('which lrs');
    if ~isempty(result)
        if param.shellScript
            % call lrs through a bash script and wait until extreme pathways have been calculated
            systemCallText = ['sh ' pwd filesep modelName '.sh'];
            [status, result] = system(systemCallText);
            if status == 0
                error(['Failure to run Bash script ' pwd filesep modelName '.sh']);
            end
        else
            if param.facetEnumeration
                fileNameOut = [modelName '.ine'];
                % call lrs to compute halfspaces
                systemCallText = ['lrs ' pwd filesep modelName '.ext > ' pwd filesep fileNameOut];
                localCallText = ['lrs ' modelName '.ext > ' fileNameOut];
                disp(localCallText)
            else
                fileNameOut = [modelName '.ext'];
                % call lrs to compute vertices
                systemCallText = ['lrs ' pwd filesep modelName '.ine > ' pwd filesep fileNameOut];
                localCallText = ['lrs ' modelName '.ine > ' fileNameOut];
                disp(localCallText)
            end
            [status, result] = system(systemCallText);
            if status == 1
                error(['lsr failed on file ', pwd filesep modelName '.ine']);
            end
            if param.facetEnumeration
                if param.redund == 0
                    fileNameIn = fileNameOut;
                    fileNameOut = [modelName '_noR.ine'];
                    uselrsRedundOption =0;

                    [status, result] = lrsSpecifyRows(fileNameIn);

                    if uselrsRedundOption
                        [status, result] =  system(['sed -i ''s/end/end\nredund 0 0/g'' ' fileNameIn]);
                        systemCallText = ['lrs ' pwd filesep fileNameIn ' > ' pwd filesep fileNameOut];
                        localCallText = ['lrs ' fileNameIn ' > ' fileNameOut];
                    else
                        systemCallText = ['redund ' pwd filesep fileNameIn ' > ' pwd filesep fileNameOut];
                        localCallText = ['redund ' fileNameIn ' > ' fileNameOut];
                    end
                    disp(localCallText);
                    [status, result] = system(systemCallText);
                    if status == 1
                        disp(result)
                        error(['lsr failed on file ', pwd filesep modelName '.ine']);
                    end
                end
            end
        end
    else
        fprintf('lrs not installed or not in path\n');
    end
else
    fprintf('non unix machines not yet supported\n');
end
end


function convertAnnotationsToVersion11()
% convertAnnotationsToVersion11()
%
% This script takes the unzipped contents of cocostuff-10k-v1.0.zip and
% converts the annotations from version 1.0 to 1.1, where COCO has 91 thing
% classes instead of 80. Afterwards the annotations1.1 folder needs to be
% manually renamed to annotations.
%
% Copyright by Holger Caesar, 2017

% Settings
cocoStuffFolder = '/home/holger/Downloads/cocostuff-10k-v1.0';
inputFolder = fullfile(cocoStuffFolder, 'annotations');
outputFolder = fullfile(cocoStuffFolder, 'annotations1.1');

% Create output Folder
if ~exist(outputFolder, 'dir')
    mkdir(outputFolder);
end

% Get file list
[fileList, fileCount] = dirSubfolders(inputFolder, '.mat', true);

for fileIdx = 1 : fileCount
    % Check if output file exists
    fileName = fileList{fileIdx};
    filePath = fullfile(inputFolder, [fileName, '.mat']);
    outFilePath = fullfile(outputFolder, [fileName, '.mat']);
    if exist(outFilePath, 'file')
        fprintf('Skipping existing file: %s\n', outFilePath);
        continue;
    end

    % Load file
    fileStruct = load(filePath);

    % Update names
    assert(numel(fileStruct.names) == 172)
    thingNames = {'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', 'truck', 'boat', 'traffic light', 'fire hydrant', 'street sign', 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', 'cow', 'elephant', 'bear', 'zebra', 'giraffe', 'hat', 'backpack', 'umbrella', 'shoe', 'eyeglasses', 'handbag', 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard', 'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard', 'surfboard', 'tennis racket', 'bottle', 'plate', 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple', 'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair', 'couch', 'potted plant', 'bed', 'mirror-things', 'dining table', 'window', 'desk-things', 'toilet', 'door-things', 'tv', 'laptop', 'mouse', 'remote', 'keyboard', 'cell phone', 'microwave', 'oven', 'toaster', 'sink', 'refrigerator', 'blender', 'book', 'clock', 'vase', 'scissors', 'teddy bear', 'hair drier', 'toothbrush', 'hairbrush'}';
    stuffNames = fileStruct.names(82:end)';
    assert(isempty(intersect(thingNames, stuffNames)));
    namesNew = [thingNames; stuffNames]; % Unlabeled is not listed anymore and will take label 0

    % Create mapping from old to new
    mapping = [0; find(ismember(namesNew, fileStruct.names))];

    % Map labels
    Snew = mapping(fileStruct.S);
    regionLabelsStuffNew = mapping(fileStruct.regionLabelsStuff);
    assert(isequal(namesNew(Snew(Snew > 0)), fileStruct.names(fileStruct.S(Snew > 0))'));

    % Save changes
    fileStruct.names = namesNew;
    fileStruct.S = Snew;
    fileStruct.regionLabelsStuff = regionLabelsStuffNew;
    save(outFilePath, '-struct', 'fileStruct');
end


function camCreateHARDIFitScript(scr_modelfit_filename,scr_after_modelfit_filename,scheme_filename,raw_filename,num_procs,bgthresh)
%
%
%
%
%

% Parameters

% Number parallel processes
if ieNotDefined('num_procs')
    num_procs=1;
end
% Reduced Encoding for spherical deconvolution
re = 16;
% Threshold on b0 for processing background voxels
if ieNotDefined('bgthresh')
    bgthresh = 200;
end
% Volumetric information
raw = niftiRead(raw_filename);
vol_dim = raw.dim;
xd = vol_dim(1);
yd = vol_dim(2);
zd = vol_dim(3);
gd = vol_dim(4);

fid = fopen(scr_modelfit_filename,'wt');
fprintf(fid,'#!/usr/bin/env bash\n\n');

fprintf(fid,'cam_run_mp_hardi_slice.sh %d %d %d %d %d %d %d %s \n', num_procs, re, xd, yd, zd, gd, bgthresh, scheme_filename);

fclose(fid);

fid = fopen(scr_after_modelfit_filename,'wt');
fprintf(fid,'#!/usr/bin/env bash\n\n');

fprintf(fid,'cam_run_after_mp_hardi_slice.sh %d %d %d %d %d %d %d %s \n', num_procs, re, xd, yd, zd, gd, bgthresh, scheme_filename);

fclose(fid);

return


function p00_story ( prob )

%*****************************************************************************80
%
%% P00_STORY prints the "story" for any problem.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    29 July 2012
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    None
%
  if ( prob == 1 )
    p01_story ( );
  elseif ( prob == 2 )
    p02_story ( );
  elseif ( prob == 3 )
    p03_story ( );
  elseif ( prob == 4 )
    p04_story ( );
  elseif ( prob == 5 )
    p05_story ( );
  elseif ( prob == 6 )
    p06_story ( );
  elseif ( prob == 7 )
    p07_story ( );
  elseif ( prob == 8 )
    p08_story ( );
  else
    fprintf ( 1, '\n' );
    fprintf ( 1, 'P00_STORY - Fatal error!\n' );
    fprintf ( 1, '  Unexpected input value of PROB.\n' );
    error ( 'P00_STORY - Fatal error!' );
  end

  return
end


function military = ch_to_military ( ch )

%*****************************************************************************80
%
%% CH_TO_MILITARY converts an ASCII character to a Military code word.
%
%  Example:
%
%    'A'  'Alpha'
%    'B'  'Bravo'
%    'Z'  'Zulu'
%    'a'  'alpha'
%    '7'  '7'
%    '%'  '%'
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    22 February 2010
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, character CH, the ASCII character.
%
%    Output, string MILITARY, the military code word.
%    If CH is not an alphabetic letter, then MILITARY is simply set equal to CH.
%
    code = [ ...
    'alpha   '; ...
    'bravo   '; ...
    'charlie '; ...
    'delta   '; ...
    'echo    '; ...
    'foxtrot '; ...
    'golf    '; ...
    'hotel   '; ...
    'india   '; ...
    'juliet  '; ...
    'kilo    '; ...
    'lima    '; ...
    'mike    '; ...
    'november'; ...
    'oscar   '; ...
    'papa    '; ...
    'quebec  '; ...
    'romeo   '; ...
    'sierra  '; ...
    'tango   '; ...
    'uniform '; ...
    'victor  '; ...
    'whiskey '; ...
    'x-ray   '; ...
    'yankee  '; ...
    'zulu    ' ];

  if ( 'A' <= ch && ch <= 'Z' )
    military = code( ch - 'A' + 1, : );
    military(1) = ch_cap ( military(1) );
  elseif ( 'a' <= ch && ch <= 'z' )
    military = code( ch - 'a' + 1, : );
  else
    military = ch;
  end

  return
end


function [map] = cell2map(cell)
%%
% Minh-Thang Luong
%
% Convert a cell (a cell) into a map by mapping cell values into their indices
%%
  if isempty(cell)
    map = containers.Map();
  else
    map = containers.Map(cell, 1:length(cell)); %num2cell(1:length(cell)));
  end


function hFig = bst_warp_prepare(ChannelFile, Options)
% BST_WARP_PREPARE:  Set up everything for a call to bst_warp (deformation of default anatomy)
%
% USAGE:  bst_warp_prepare(ChannelFile, Options)
%         bst_warp_prepare(ChannelFile)
%
% INPUTS:
%     - ChannelFile : Channel file that contains the HeadPoints for the deformation
%     - Options     : Structure of the options
%         |- tolerance    : Percentage of outliers head points, ignored in the calulation of the deformation.
%         |                 Set to more than 0 when you know your head points have some outliers.
%         |                 If not specified: asked to the user (default
%         |- isInterp     : If 0, do not do a full interpolation (default: 1)
%         |- isScaleOnly  : If 1, do not perform the full deformation but only a linear scaling in the three directions (default: 0)
%         |- isSurfaceOnly: If 1, do not warp/scale the MRI (default: 0)
%         |- isInteractive: If 0, do not ask anything to the user (default: 1)

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

%% ===== CHECK PARAMETERS =====
% Default options
defOptions = struct(...
    'tolerance',     [], ...
    'isInterp',      1, ...
    'isScaleOnly',   [], ...
    'isSurfaceOnly', 0, ...
    'isInteractive', 1);
% Copy default options
if (nargin < 2)
    Options = defOptions;
else
    Options = struct_copy_fields(Options, defOptions, 0);
end
% Additional non-interactive defaults
if ~Options.isInteractive && isempty(Options.isScaleOnly)
    Options.isScaleOnly = 0;
end
if ~Options.isInteractive && isempty(Options.tolerance)
    Options.tolerance = 2;
end
hFig = [];

%% ===== GET DEFAULT ANAT =====
% Start by unloading everything
bst_memory('UnloadAll', 'Forced');
% Progress bar
if Options.isInteractive
    bst_progress('start', 'Warp anatomy', 'Initialization...');
end
% Get default anatomy
sDefSubject = bst_get('Subject', 0);
% If no default anatomy defined: error
if isempty(sDefSubject.Anatomy) || isempty(sDefSubject.Surface) || isempty(sDefSubject.iScalp)
    local_error('Please define a default anatomy before doing this.', Options.isInteractive);
    return;
end
% Load fiducials from default MRI
srcMriFile = file_fullpath(sDefSubject.Anatomy(sDefSubject.iAnatomy).FileName);
srcMriMat = load(srcMriFile, 'SCS', 'Voxsize');
% Check whether fiducials are defined on MRI
if ~isfield(srcMriMat, 'SCS') || ~isfield(srcMriMat.SCS, 'NAS') || isempty(srcMriMat.SCS.NAS) || isempty(srcMriMat.SCS.LPA) || isempty(srcMriMat.SCS.RPA)
    local_error('Fiducials are not defined on default MRI.', Options.isInteractive);
    return;
end

%% ===== GET DIGITIZED HEAD POINTS =====
% Get head points
HeadPoints = channel_get_headpoints(ChannelFile, 1);
% Number of head points
if isempty(HeadPoints)
    nhp = 0;
    destPts = [];
else
    nhp = length(HeadPoints.Loc);
    destPts = HeadPoints.Loc';
end
% Not enough head points
if (nhp < 3)
    local_error('Not enough digitized head points to perform this operation.', Options.isInteractive);
    return;
% Few head points: Scale anatomy ?
elseif Options.isInteractive && isempty(Options.isScaleOnly)
    if (nhp < 70)
        msg = ['There are only ' num2str(nhp) ' digitized head points, this might be ' 10 ...
               'insufficient to deform the default anatomy precisely. ' 10 10 ...
               'You could instead perform a simple scaling of the anatomy.' 10 10];
    else
        msg = ['Do you want to perform a simple scaling (Scale)' 10 ...
               'or a deformation (Warp) of the default anatomy?' 10 10];
    end
    res = java_dialog('question', msg, 'Warp anatomy', [], {'Warp', 'Scale', 'Cancel'}, 'Scale');
    if isempty(res) || strcmpi(res, 'Cancel')
        bst_progress('stop');
        return;
    elseif strcmpi(res, 'Scale')
        Options.isScaleOnly = 1;
    else
        Options.isScaleOnly = 0;
    end
    if (nhp < 70)
        if ~java_dialog('confirm', ...
              ['There are only ' num2str(nhp) ' digitized head points, this might be ' 10 ...
               'insufficient to deform the default anatomy precisely. ' 10 10 ...
               'Proceed anyway?'], 'Warp default anatomy');
            bst_progress('stop');
            return;
        end
    end
end

%% ===== GET SUBJECT ANAT =====
% Get study for this channel file
sStudy = bst_get('ChannelFile', ChannelFile);
% Get subject for this channel file
[sSubject, iSubject] = bst_get('Subject', sStudy.BrainStormSubject, 1);
% If there is already an anatomy defined for this subject: warning
if (~isempty(sSubject.Anatomy) || ~isempty(sSubject.Surface))
    if Options.isInteractive
        isOk = java_dialog('confirm', ['Warning: there is already an anatomy defined for this subject.' 10 10 ...
                                       'This process will delete the existing anatomy, and replace it with ' 10 ...
                                       'a deformed version of the default anatomy.' 10 10 ...
                                       'Delete current subject anatomy ?' 10 10], ...
                                       'Warp default anatomy');
        if ~isOk
            bst_progress('stop');
            return
        end
    end
    % Delete previous surfaces
    if ~isempty(sSubject.Surface)
        SurfaceFiles = cellfun(@(c)file_fullpath(c), {sSubject.Surface.FileName}, 'UniformOutput', 0);
        file_delete(SurfaceFiles, 1);
    end
    if ~isempty(sSubject.Anatomy)
        MriFiles = cellfun(@(c)file_fullpath(c), {sSubject.Anatomy.FileName}, 'UniformOutput', 0);
        file_delete(MriFiles, 1);
    end
end
% Force subject to us default anatomy
s.UseDefaultAnat = 0;
s.Anatomy = [];
s.Surface = [];
% Update subject file
bst_save(file_fullpath(sSubject.FileName), s, 'v7', 1);
% Load default scalp
srcScalpFile = sDefSubject.Surface(sDefSubject.iScalp).FileName;
srcSurf = in_tess_bst(srcScalpFile);
% Display initial surface and head points
if Options.isInteractive
    view_headpoints(ChannelFile, srcScalpFile);
end

%% ===== PROJECT HEAD POINTS =====
% Compute center of mass of the head points, use as the center of projection
if (length(destPts) > 50)
    center = mean(destPts);
else
    center = [0 0 0];
end
% Project digitized head points on the scalp
[destPtsProj, dist] = project_on_surface(srcSurf, destPts, center);

%% ===== REMOVING OUTLIERS =====
if (length(destPts) > 15)
    % Ask the user the tolerance for outliers
    if Options.isInteractive && isempty(Options.tolerance)
        res = java_dialog('input', ['You can choose to ignore the digitized head points that are far ' 10 ...
                                    'away from the scalp in the calculation of the deformation field.' 10 10 ...
                                    'Percentage of head points to ignore [0-100]:'], ...
                                    'Warp default anatomy', [], '2');
        if isempty(res) || isnan(str2double(res))
            bst_progress('stop');
            return
        end
        Options.tolerance = str2double(res) / 100;
    end
    % Remove points that have the maximum distances
    if (Options.tolerance > 0)
        % Number of points to remove
        nRemove = ceil(Options.tolerance * length(destPts));
        disp(['WARP> Remove ' num2str(nRemove) ' points from the head points.']);
        % Sort points by distance to scalp
        [tmp__, iSort] = sort(dist, 1, 'descend');
        iRemove = iSort(1:nRemove);
        % Remove from list of destination points
        destPts(iRemove,:) = [];
        destPtsProj(iRemove,:) = [];
    end
end

%% ===== BUILD TRANSFORMATION =====
% Full deformation
if ~Options.isScaleOnly
    if Options.isInterp
        % Destination landmarks: Fit spherical harmonic to the digitized head points
        fvh = hsdig2fv(destPts, 5, 15/1000, 40*pi/180, 0);
        destPtsParam = fvh.vertices;
        % Source landmarks: Project remeshed digitized surface on scalp
        srcPtsParam = project_on_surface(srcSurf, destPtsParam, center);
    else
        % Destination landmarks: head points
        destPtsParam = destPts;
        % Source landmarks: head points projected on the scalp
        srcPtsParam = destPtsProj;
    end

% Scaling only
else
    % Downsample heavily the scalp surface
    [srcFacesParam, srcPtsParam] = reducepatch(srcSurf.Faces, srcSurf.Vertices, 300 / length(srcSurf.Vertices));
    % Destination surface: scale version of the downsampled scalp surface
    destPtsParam = srcPtsParam;
    % Scaling in each direction (x,y,z)
    for dim = 1:3
        % Compute scale factor
        fall = destPts(:,dim) ./ destPtsProj(:,dim);
        fall(isnan(fall) | (fall < .5) | (fall > 1.5)) = [];
        % Apply scale factor
        if ~isempty(fall)
            f = mean(fall);
            destPtsParam(:,dim) = f .* destPtsParam(:,dim);
        end
    end
end

%% ===== WARP =====
% Create list of files to process
SurfaceFiles     = {sDefSubject.Surface.FileName};
SurfaceFilesFull = cellfun(@(c)file_fullpath(c), SurfaceFiles, 'UniformOutput', 0);
MriFiles         = {sDefSubject.Anatomy([sDefSubject.iAnatomy, setdiff(1:length(sDefSubject.Anatomy), sDefSubject.iAnatomy)]).FileName};
MriFilesFull     = cellfun(@(c)file_fullpath(c), MriFiles, 'UniformOutput', 0);
OutputTag        = '_warped';
OutputDir        = bst_fileparts(file_fullpath(sSubject.FileName));
% Main call
bst_warp(destPtsParam, srcPtsParam, SurfaceFilesFull, MriFilesFull, OutputTag, OutputDir, Options.isSurfaceOnly);

%% ===== COPY ATLASES =====
% Get list of atlas files (scout files in the default anat folder)
atlasDir = bst_fileparts(file_fullpath(sDefSubject.FileName));
dirScout = dir(bst_fullfile(atlasDir, 'scout_*.mat'));
% Copy all the files file
for i = 1:length(dirScout)
    file_copy(bst_fullfile(atlasDir, dirScout(i).name), bst_fullfile(OutputDir, dirScout(i).name))
end

%% ===== UPDATE DATABASE =====
% Reload subject
db_reload_subjects(iSubject);
% Unload all the surfaces, close all the figures
bst_memory('UnloadAll', 'Forced');
% Get subject again
sSubject = bst_get('Subject', iSubject);
% Display warp head and cortex surfaces
if Options.isInteractive && ~isempty(sSubject.iScalp)
    ScalpFile = sSubject.Surface(sSubject.iScalp).FileName;
    hFig = view_surface(ScalpFile);
end
if Options.isInteractive && ~isempty(sSubject.iCortex)
    CortexFile = sSubject.Surface(sSubject.iCortex).FileName;
    hFig = view_surface(CortexFile);
end

% Close progress bar
if Options.isInteractive
    bst_progress('stop');
end

end

%% ========================================================================
%  ===== HELPER FUNCTIONS =================================================
%  ========================================================================

%% ===== PROJECT ON SURFACE =====
function [Q, dist] = project_on_surface(sSurf, P, center)
    % Center on (0,0,0)
    P = bst_bsxfun(@minus, P, center);
    sSurf.Vertices = bst_bsxfun(@minus, sSurf.Vertices, center);
    % Project points on surface
    Q = 0 .* P;
    for i = 1:length(P)
        proj = tess_ray_intersect(sSurf.Vertices, sSurf.Faces, [0 0 0], P(i,:))';
        if isempty(proj)
            Q(i,:) = P(i,:);
        elseif (size(proj,1) > 1)
            Q(i,:) = proj(1,:);
        else
            Q(i,:) = proj;
        end
    end
    % Compute the distance between each point and its projection (distance to the scalp surface)
    if (nargout >= 2)
        dist = sum(sqrt((P - Q).^2), 2);
    end
    % Restore center
    Q = bst_bsxfun(@plus, Q, center);
end

%% ===== LOCAL ERROR PROCESSING =====
function local_error(errMsg, isInteractive)
    if isInteractive
        bst_error(errMsg, 'Warp default anatomy', 0);
        bst_progress('stop');
    else
        bst_report('Error', 'process_warp', [], errMsg);
    end
end