function writeInputFromBlocks(inputFile)
% This function reads custom parameters from masked WEC-Sim blocks, and
% writes a new wecSimInputFile.m based on the mask variables.
%
% Steps:
% 1. Blocks are reordered into the standard WEC-Sim input file order
%    (simu, waves, body, constraint, pto, cable, mooring)
% 2. Open a new output file and create default classes
% 3. For every mask variable, call the writeLineFromVar() function for
%    that block to determine the appropriate string for the input file
%
% Note: str2num used because mask parameters are always read as type 'char'
% Note: mat2str used so that types are printed in the correct format

% Get all block names
blocks = find_system(bdroot,'Type','Block');

%% Reorder blocks
% Order blocks by class:
%    simu/waves, body, constraints, ptos, cables, moorings
iSimulation = [];
iBody = [];
iConstraint = [];
iPTO = [];
iCable = [];
iMooring = [];

for i=1:length(blocks)
    names = get_param(blocks{i},'MaskNames');
    if any(strcmp(names,'simu'))
        iSimulation = i;
    elseif any(strcmp(names,'body'))
        iBody(end+1) = i;
    elseif any(strcmp(names,'constraint'))
        iConstraint(end+1) = i;
    elseif any(strcmp(names,'pto'))
        iPTO(end+1) = i;
    elseif any(strcmp(names,'cable'))
        iCable(end+1) = i;
    elseif any(strcmp(names,'mooring'))
        iMooring(end+1) = i;
    end
end

% Order classes by number
iSorted = sortBlocksByNumber(blocks(iBody),'body');
iBody = iBody(iSorted);

iSorted = sortBlocksByNumber(blocks(iConstraint),'constraint');
iConstraint = iConstraint(iSorted);

iSorted = sortBlocksByNumber(blocks(iPTO),'pto');
iPTO = iPTO(iSorted);

iSorted = sortBlocksByNumber(blocks(iCable),'cable');
iCable = iCable(iSorted);

iSorted = sortBlocksByNumber(blocks(iMooring),'mooring');
iMooring = iMooring(iSorted);

% Order blocks
ind = [iSimulation iBody iConstraint iPTO iCable iMooring];
blocks = blocks(ind);

%% Write input file
fid = fopen(['./' inputFile '.m'],'w');
fprintf(fid,'%% %s\r\n','WEC-Sim Input File, written with custom Simulink parameters');
fprintf(fid,'%% %s\r\n',string(datetime));

% create default classes to compare values against
simu = simulationClass();
waves = waveClass('');
body = bodyClass('');
constraint = constraintClass('');
pto = ptoClass('');
cable = cableClass('','constraint','pto');
mooring = mooringClass('');

%% Write each mask to an input file section
for i=1:length(blocks)
    names = get_param(blocks{i},'MaskNames');
    values = get_param(blocks{i},'MaskValues');
    visibilities = get_param(blocks{i},'MaskVisibilities');

    % Create struct with mask variable name/value pairs
    maskVars = struct();
    maskViz = struct();
    for j = 1:length(names)
        % check if the value should be converted from a string to a float
        tmp = str2num(values{j});
        if ~isempty(tmp)
            values{j} = str2num(values{j});
        end
        % convert matlab to string to write to input file correctly
        values{j} = mat2str(values{j});
        maskVars.(names{j}) = values{j};
        maskViz.(names{j}) = visibilities{j};
    end; clear j;

    if isfield(maskVars,'simu') && isfield(maskVars,'waves')
        % Block is Global Reference Frame
        fprintf(fid,'\r\n%s\r\n','%% Simulation Class');
        fprintf(fid,'simu = simulationClass(); \r\n');
        fprintf(fid,'simu.simMechanicsFile = ''%s.slx''; \r\n',bdroot);

        fprintf(fid,writeLineFromVar(simu, 'mode', maskVars, maskViz, [], []));
        fprintf(fid,writeLineFromVar(simu, 'explorer', maskVars, maskViz, [], []));
        fprintf(fid,writeLineFromVar(simu, 'startTime', maskVars, maskViz, [], []));
        fprintf(fid,writeLineFromVar(simu, 'rampTime', maskVars, maskViz, [], []));
        fprintf(fid,writeLineFromVar(simu, 'endTime', maskVars, maskViz, [], []));
        fprintf(fid,writeLineFromVar(simu, 'solver', maskVars, maskViz, [], []));
        fprintf(fid,writeLineFromVar(simu, 'dt', maskVars, maskViz, [], []));
        fprintf(fid,writeLineFromVar(simu, 'cicEndTime', maskVars, maskViz, [], []));
        fprintf(fid,writeLineFromVar(simu, 'stateSpace', maskVars, maskViz, [], []));

        % Wave Information
        fprintf(fid,'\r\n%s\r\n','%% Wave Class');
        fprintf(fid,'waves = waveClass(%s); \r\n',maskVars.WaveClass);

        fprintf(fid,writeLineFromVar(waves, 'height', maskVars, maskViz, [], []));
        fprintf(fid,writeLineFromVar(waves, 'period', maskVars, maskViz, [], []));
        fprintf(fid,writeLineFromVar(waves, 'direction', maskVars, maskViz, [], []));
        fprintf(fid,writeLineFromVar(waves, 'spread', maskVars, maskViz, [], []));
        fprintf(fid,writeLineFromVar(waves, 'spectrumType', maskVars, maskViz, [], []));
        fprintf(fid,writeLineFromVar(waves, 'option', maskVars, maskViz, [], 'bem'));
        fprintf(fid,writeLineFromVar(waves, 'spectrumFile', maskVars, maskViz, [], []));
        fprintf(fid,writeLineFromVar(waves, 'phaseSeed', maskVars, maskViz, [], []));
        fprintf(fid,writeLineFromVar(waves, 'elevationFile', maskVars, maskViz, [], []));

    elseif isfield(maskVars,'body')
        % Block is a body
        fprintf(fid,'\r\n%s\r\n','%% Body Class');
        tmp = string(maskVars.body);
        num = str2num(extractBetween(tmp,strfind(tmp,'('),strfind(tmp,')'),'Boundaries','Exclusive'));
        fprintf(fid,'body(%d) = bodyClass(%s); \r\n',num,maskVars.h5File);

        fprintf(fid,writeLineFromVar(body, 'geometryFile', maskVars, maskViz, num, []));
        fprintf(fid,writeLineFromVar(body, 'mass', maskVars, maskViz, num, []));
        fprintf(fid,writeLineFromVar(body, 'inertia', maskVars, maskViz, num, []));
        fprintf(fid,writeLineFromVar(body, 'nonHydro', maskVars, maskViz, num, []));
        fprintf(fid,writeLineFromVar(body, 'nonlinearHydro', maskVars, maskViz, num, []));
        fprintf(fid,writeLineFromVar(body, 'flex', maskVars, maskViz, num, []));
        fprintf(fid,writeLineFromVar(body, 'centerGravity', maskVars, maskViz, num, []));
        fprintf(fid,writeLineFromVar(body, 'centerBuoyancy', maskVars, maskViz, num, []));
        fprintf(fid,writeLineFromVar(body, 'dof', maskVars, maskViz, num, []));
        fprintf(fid,writeLineFromVar(body, 'volume', maskVars, maskViz, num, []));
        fprintf(fid,writeLineFromVar(body, 'displacement', maskVars, maskViz, num, 'initial'));
        fprintf(fid,writeLineFromVar(body, 'axis', maskVars, maskViz, num, 'initial'));
        fprintf(fid,writeLineFromVar(body, 'angle', maskVars, maskViz, num, 'initial'));
        fprintf(fid,writeLineFromVar(body, 'option', maskVars, maskViz, num, 'morisonElement'));
        fprintf(fid,writeLineFromVar(body, 'cd', maskVars, maskViz, num, 'morisonElement'));
        fprintf(fid,writeLineFromVar(body, 'ca', maskVars, maskViz, num, 'morisonElement'));
        fprintf(fid,writeLineFromVar(body, 'area', maskVars, maskViz, num, 'morisonElement'));
        fprintf(fid,writeLineFromVar(body, 'VME', maskVars, maskViz, num, 'morisonElement'));
        fprintf(fid,writeLineFromVar(body, 'rgME', maskVars, maskViz, num, 'morisonElement'));
        fprintf(fid,writeLineFromVar(body, 'z', maskVars, maskViz, num, 'morisonElement'));

    elseif isfield(maskVars,'constraint')
        % Block is a constraint
        fprintf(fid,'\r\n%s\r\n','%% Constraint Class');
        tmp = string(maskVars.constraint);
        num = str2num(extractBetween(tmp,strfind(tmp,'('),strfind(tmp,')'),'Boundaries','Exclusive'));
        fprintf(fid,'constraint(%d) = constraintClass(''constraint%d''); \r\n',num,num);

        fprintf(fid,['constraint(%d).location = ' maskVars.loc '; \r\n'],num);
        fprintf(fid,writeLineFromVar(constraint, 'y', maskVars, maskViz, num, 'orientation'));
        fprintf(fid,writeLineFromVar(constraint, 'z', maskVars, maskViz, num, 'orientation'));
        fprintf(fid,writeLineFromVar(constraint, 'displacement', maskVars, maskViz, num, 'initial'));
        fprintf(fid,writeLineFromVar(constraint, 'upperLimitSpecify', maskVars, maskViz, num, 'hardStops'));
        fprintf(fid,writeLineFromVar(constraint, 'upperLimitBound', maskVars, maskViz, num, 'hardStops'));
        fprintf(fid,writeLineFromVar(constraint, 'upperLimitStiffness', maskVars, maskViz, num, 'hardStops'));
        fprintf(fid,writeLineFromVar(constraint, 'upperLimitDamping', maskVars, maskViz, num, 'hardStops'));
        fprintf(fid,writeLineFromVar(constraint, 'upperLimitTransitionRegionWidth', maskVars, maskViz, num, 'hardStops'));
        fprintf(fid,writeLineFromVar(constraint, 'lowerLimitSpecify', maskVars, maskViz, num, 'hardStops'));
        fprintf(fid,writeLineFromVar(constraint, 'lowerLimitBound', maskVars, maskViz, num, 'hardStops'));
        fprintf(fid,writeLineFromVar(constraint, 'lowerLimitStiffness', maskVars, maskViz, num, 'hardStops'));
        fprintf(fid,writeLineFromVar(constraint, 'lowerLimitDamping', maskVars, maskViz, num, 'hardStops'));
        fprintf(fid,writeLineFromVar(constraint, 'lowerLimitTransitionRegionWidth', maskVars, maskViz, num, 'hardStops'));

    elseif isfield(maskVars,'pto')
        % Block is a PTO
        fprintf(fid,'\r\n%s\r\n','%% PTO Class');
        tmp = string(maskVars.pto);
        num = str2num(extractBetween(tmp,strfind(tmp,'('),strfind(tmp,')'),'Boundaries','Exclusive'));
        fprintf(fid,'pto(%d) = ptoClass(''pto%d''); \r\n',num,num);

        fprintf(fid,writeLineFromVar(pto, 'stiffness', maskVars, maskViz, num, []));
        fprintf(fid,writeLineFromVar(pto, 'damping', maskVars, maskViz, num, []));
        fprintf(fid,['pto(%d).location = ' maskVars.loc '; \r\n'],num);
        fprintf(fid,writeLineFromVar(pto, 'y', maskVars, maskViz, num, 'orientation'));
        fprintf(fid,writeLineFromVar(pto, 'z', maskVars, maskViz, num, 'orientation'));
        fprintf(fid,writeLineFromVar(pto, 'displacement', maskVars, maskViz, num, 'initial'));
        fprintf(fid,writeLineFromVar(pto, 'upperLimitSpecify', maskVars, maskViz, num, 'hardStops'));
        fprintf(fid,writeLineFromVar(pto, 'upperLimitBound', maskVars, maskViz, num, 'hardStops'));
        fprintf(fid,writeLineFromVar(pto, 'upperLimitStiffness', maskVars, maskViz, num, 'hardStops'));
        fprintf(fid,writeLineFromVar(pto, 'upperLimitDamping', maskVars, maskViz, num, 'hardStops'));
        fprintf(fid,writeLineFromVar(pto, 'upperLimitTransitionRegionWidth', maskVars, maskViz, num, 'hardStops'));
        fprintf(fid,writeLineFromVar(pto, 'lowerLimitSpecify', maskVars, maskViz, num, 'hardStops'));
        fprintf(fid,writeLineFromVar(pto, 'lowerLimitBound', maskVars, maskViz, num, 'hardStops'));
        fprintf(fid,writeLineFromVar(pto, 'lowerLimitStiffness', maskVars, maskViz, num, 'hardStops'));
        fprintf(fid,writeLineFromVar(pto, 'lowerLimitDamping', maskVars, maskViz, num, 'hardStops'));
        fprintf(fid,writeLineFromVar(pto, 'lowerLimitTransitionRegionWidth', maskVars, maskViz, num, 'hardStops'));

    elseif isfield(maskVars,'cable')
        % Block is a cable
        fprintf(fid,'\r\n%s\r\n','%% Cable Class');
        tmp = string(maskVars.cable);
        num = str2num(extractBetween(tmp,strfind(tmp,'('),strfind(tmp,')'),'Boundaries','Exclusive'));
        fprintf(fid,'cable(%d) = cableClass(''cable%d'',%s,%s); \r\n',num,num,...
            maskVars.baseName,maskVars.followerName);
        fprintf(fid,writeLineFromVar(cable, 'stiffness', maskVars, maskViz, num, []));
        fprintf(fid,writeLineFromVar(cable, 'damping', maskVars, maskViz, num, []));
        fprintf(fid,writeLineFromVar(cable, 'L0', maskVars, maskViz, num, []));
        fprintf(fid,writeLineFromVar(cable, 'preTension', maskVars, maskViz, num, []));
        fprintf(fid,writeLineFromVar(cable, 'y', maskVars, maskViz, num, 'orientation'));
        fprintf(fid,writeLineFromVar(cable, 'z', maskVars, maskViz, num, 'orientation'));
        fprintf(fid,writeLineFromVar(cable, 'displacement', maskVars, maskViz, num, 'initial'));
        fprintf(fid,writeLineFromVar(cable, 'axis', maskVars, maskViz, num, 'initial'));
        fprintf(fid,writeLineFromVar(cable, 'angle', maskVars, maskViz, num, 'initial'));

    elseif isfield(maskVars,'mooring') && isfield(maskVars,'stiffness')
        % Block is a Mooring system
        fprintf(fid,'\r\n%s\r\n','%% Mooring Class');
        tmp = string(maskVars.mooring);
        num = str2num(extractBetween(tmp,strfind(tmp,'('),strfind(tmp,')'),'Boundaries','Exclusive'));

        fprintf(fid,'mooring(%d) = mooringClass(''mooring%d''); \r\n',num,num);
        fprintf(fid,['mooring(%d).location = ' maskVars.loc '; \r\n'],num);
        fprintf(fid,writeLineFromVar(mooring, 'stiffness', maskVars, maskViz, num, 'matrix'));
        fprintf(fid,writeLineFromVar(mooring, 'damping', maskVars, maskViz, num, 'matrix'));
        fprintf(fid,writeLineFromVar(mooring, 'preTension', maskVars, maskViz, num, 'matrix'));

    elseif isfield(maskVars,'mooring') && isfield(maskVars,'moorDynlines')
        % Block is a Mooring system
        fprintf(fid,'\r\n%s\r\n','%% Mooring Class');
        tmp = string(maskVars.mooring);
        num = str2num(extractBetween(tmp,strfind(tmp,'('),strfind(tmp,')'),'Boundaries','Exclusive'));
        fprintf(fid,'mooring(%d) = mooringClass(''mooring%d''); \r\n',num,num);
        fprintf(fid,['mooring(%d).location = ' maskVars.loc '; \r\n'],num);
        fprintf(fid,writeLineFromVar(mooring, 'moorDynLines', maskVars, maskViz, num, []));
        fprintf(fid,writeLineFromVar(mooring, 'moorDynNodes', maskVars, maskViz, num, []));
    end
    clear names values maskVars
end

fclose(fid);

end

function iSorted = sortBlocksByNumber(blockList, className)
    % This function takes in a list of blocks (bodies or ptos or
    % constraints, but not bodies and ptos, etc) and orders the blocks
    % based on the class number
    classNums = [];
    for i=1:length(blockList)
        mask = Simulink.Mask.get(blockList{i});
        tmp = string(mask.getParameter(className).Value);
        num = str2num(extractBetween(tmp,strfind(tmp,'('),strfind(tmp,')'),'Boundaries','Exclusive'));
        classNums(i) = num;
    end
    [~,iSorted] = sort(classNums,'ascend');

end


function [data1,data2] = prtUtilDistanceParseInputs(dataSet1,dataSet2,dimCheck)
%[data1,data2] = prtUtilDistanceParseInputs(dataSet1,dataSet2)
%[data1,data2] = prtUtilDistanceParseInputs(dataSet1,dataSet2,dimCheck)
% Boolean, whether to check data dimensionality
% Internal
% xxx Need Help xxx

if nargin < 3
    dimCheck = true;
end
if (isnumeric(dataSet1) && isnumeric(dataSet2)) || (islogical(dataSet1) && islogical(dataSet2))
    data1 = dataSet1;
    data2 = dataSet2;
elseif isa(dataSet1,'prtDataSetBase') && isa(dataSet2,'prtDataSetBase')
    data1 = dataSet1.getObservations;
    data2 = dataSet2.getObservations;
else
    error('prt:prtUtilDistanceParseInputs:invalidInputs','prtDistance functions require first two inputs to be numeric or prtDataSetBase, but inputs were: %s and %s',class(dataSet1),class(dataSet2));
end
if dimCheck
    if size(data1,2) ~= size(data2,2)
        error('prt:prtUtilDistanceParseInputs:invalidInputs','prtDistance functions require the dimensionality of the two inputs to match, but inputs are of dimension %d and %d',size(data1,2),size(data2,2));
    end
end


%                               applyOpts.m
%
% This function checks if an element is in a given list. If it is not,
% an error will be raised. It is used in any place where a value is
% expected to be chosen from a list of options
% (e.g. It is used to check opts.indexChoice in the function
% validateOptions in solveCoordinateDescent.m).
%
% Inputs:
%         fieldName(string)       :  The name of the field to be checked.
%         element(string)         :  The name of the element to be checked.
%         list(cell array)        :  The list that consists of all the
%                                    valid names.
%         customizedMsg(string)   :  A customized message put at the
%                                    beginning of the error message.
%
%
% PhasePack by Rohan Chandra, Ziyuan Zhong, Justin Hontz, Val McCulloch,
% Christoph Studer, & Tom Goldstein
% Copyright (c) University of Maryland, 2017

function checkIfInList(fieldName,element,list,customizedMsg)
    if ~exist('customizedMsg','var')
        customizedMsg = '';
    end
    if ~any(strcmp(list,lower(element)))
        % Use num2str in case element is given as a number
        error('%s %s is invalid for field %s\n',customizedMsg,num2str(element),fieldName);
    end
end


function complainif_notposscalar(var,varname,callfun)

if nargin<3
    callfun = mfilename;
end

if  isempty(var) || ~isscalar(var) || ~isnumeric(var) || var<=0 || ...
        ( isinf(var) )
   error('%s: %s should be a positive scalar.',upper(callfun),varname);
end


function model = utl_complete_model(model,func,bundle)
% Internal. Complete the definition of a predicitive model with some meta-data.
% utl_complete_model(Model, PredictionFunction, Bundle)
%
% This function ensures that model structs created by BCI paradigm plugins have a uniform format
% and contain the set of fields needed by downstream processing functions (e.g., the offline and
% online prediction framework). This function relieves the BCI paradigms from specifying each and
% every of those fields manually, by automatically appending fields that can be deduced from the
% data or other parts of the model.
%
% This function also performs some early validation of the model structure to catch possible errors
% early on rather than deep into a subsequent processing run.
%
% In:
%   Model : a predictive model that will be finalized by this function; may have the following fields:
%           .tracking.filter_graph : cell array of symbolic filter expressions, one per inlet of the
%                                    prediction function; can be used to filter streams or bundles
%
%                                    Can also be passed either as an EEGLAB data set (with .tracking
%                                    field) or as a stream bundle; in this case, the processing that
%                                    has been applied to those data sets will be used as a template
%                                    to lay out the filter graph.
%
%                                    If not passed, will be filled in by the Bundle argument.
%
%           .tracking.prediction_function : function that takes a window of the output of filter graph's
%                                           output and computes a prediction from it; If not passed,
%                                           will be filled in based on the PredictionFunction argument
%
%           .tracking.prediction_channels : cell array of chanlocs structures expected by the prediction
%                                           function; has one element for each inlet of the prediction
%                                           function.
%
%                                           If not passed, will be filled in based on the Bundle argument.
%
%
%   PredictionFunction : handle of the prediction function (supplied by the framework code that calls
%                        utl_complete_model)
%
%   Bundle : a stream bundle fully preprocessed by the filter graph, used to extract some
%            meta-data (supplied by framework code)
%
% Out:
%   Model : a completed model struct; the following fields are ensured to exist:
%           .tracking                --> tracking information about the model for offline/online processing
%            .filter_graph           --> a cell array of (symbolic) filter expressions, denoting the
%                                        filter operations that yield each stream inlet that is
%                                        accepted by the model's prediction function. Each expression
%                                        may contain leaf nodes of the type @rawdata, which are free
%                                        inlets of the filter graph (tagged with the channels expected
%                                        at that inlet)
%            .prediction_function    --> a function that takes a stream bundle with one stream for
%                                        each entry of the filter graph; produces a prediction for each
%                                        epoch/segment in the data (therefore works both online and offline)
%            .prediction_channels    --> a cell array of chanlocs structs that defines that channels
%                                        expected by the prediction function at each of its inlets
%                                        (this is redundant additional information)
%
% See also:
%   bci_train
%
%                                Christian Kothe, Swartz Center for Computational Neuroscience, UCSD
%                                2011-08-28
dp;

% input validation
if ~isstruct(model) || ~isscalar(model)
    error('The given model must be a 1x1 struct, but was: %s',hlp_tostring(model,10000)); end
if exist('bundle','var')
    if ~isstruct(bundle) || ~isscalar(bundle)
        error('The given stream bundle must be a 1x1 struct, but was: %s',hlp_tostring(bundle,10000)); end
    if ~isfield(bundle,'streams') && isfield(bundle,'tracking') && isfield(bundle.tracking,'online_expression')
        bundle.streams = {bundle}; end
    for s=1:length(bundle.streams)
        utl_check_fields(bundle.streams{s},{'tracking','chanlocs'},'signal','signal'); end
end
if exist('func','var') && ~(isa(func,'function_handle') || isa(func,'char'))
    error('The given PredictionFunction argument must either be a function handle or a string.'); end

% the .tracking field holds the data that is of interest to the offline/online evaluation system
% it is allowed for the paradigm to fill in the following field manually, in order to yield custom behavior
if ~isfield(model,'tracking')
    model.tracking = struct(); end

% add the filter_graph, if necessary
if ~isfield(model.tracking,'filter_graph')
    if ~exist('bundle','var')
        error('Cannot automatically deduce the .tracking.filter_graph field for this model, because the data on which its state shall be initialized is not known or available;\nthis most likely the case if your model was calibrated on a dataset collection and the framework could not decide which dataset in the collection to initialize the filters on.\nYour can set the field directly in your paradigm''s calibrate() function.'); end
    for s=1:length(bundle.streams)
        model.tracking.filter_graph{s} = bundle.streams{s}.tracking.online_expression; end
else
    % a filter graph was passed in; check format
    if all(isfield(model.tracking.filter_graph,{'tracking','chanlocs'}))
        % ... as a data set; override the bundle with it
        bundle = struct('streams',{{model.tracking.filter_graph}});
        % set the filter graph accordingly
        model.tracking.filter_graph = {bundle.streams{1}.tracking.online_expression};
    elseif isfield(model.tracking.filter_graph,'streams')
        % ... as a stream bundle: override the bundle with it
        bundle = model.tracking.filter_graph;
        % and initialize the filter graph accordingly
        model.tracking.filter_graph = {};
        for s=1:length(bundle.streams)
            model.tracking.filter_graph{s} = bundle.streams{s}.tracking.online_expression; end
    elseif all(isfield(model.tracking.filter_graph,{'head','parts'}))
        % ... as an abstract expression
        model.tracking.filter_graph = {model.tracking.filter_graph};
    elseif iscell(model.tracking.filter_graph)
        % ... in the proper format (cell array)
        if isempty(model.tracking.filter_graph)
            error('The given model''s .tracking.filter_graph is empty.'); end
        for c=1:length(model.tracking.filter_graph)
            if ~all(isfield(model.tracking.filter_graph{c},{'head','parts'}))
                error('The given model''s .tracking.filter_graph is not a valid filter graph (at least one of its cells is lacking the required fields .head and/or .parts): %s',hlp_tostring(model.tracking.filter_graph)); end
        end
    else
        error('The given model''s .tracking.filter_graph is not a valid filter graph: %s',hlp_tostring(model.tracking.filter_graph));
    end
end

% add the prediction function, if necessary
if ~isfield(model.tracking,'prediction_function')
    if ~exist('func','var')
        error('If the given model is lacking a .tracking.prediction_function field, the prediction function should be supplied as second input to utl_complete_model.'); end
    model.tracking.prediction_function = func;
end

% validate the model's prediction function
if ischar(model.tracking.prediction_function)
    if strncmp(model.tracking.prediction_function,'Paradigm',8)
        try
            % Paradigm class reference: try to instantiate
            instance = eval(model.tracking.prediction_function); %#ok<NASGU>
        catch e
            error('Failed to instantiate the paradigm referred to by the model''s .tracking.prediction_function with error: %s',hlp_handleerror(e));
        end
    else
        if ~exist(model.tracking.prediction_function,'file')
            error('The function referred to by the model''s .tracking.prediction_function field does not exist: %s',model.tracking.prediction_function); end
    end
else
    tmp = char(model.tracking.prediction_function);
    if tmp(1) ~= '@' && ~exist(tmp,'file')
        error('The function referred to by the model''s .tracking.prediction_function field does not exist: %s',char(model.tracking.prediction_function)); end
end

% add and validate the channel locations (these are technically redundant, as they can be
% derived from the preprocessing chain) (deduced from the bundle)
try
    if ~isfield(model.tracking,'prediction_channels')
        if ~exist('bundle','var')
            error('Cannot automatically assign the .tracking.prediction_channels field because the data set from which these channels shall be determined could not be deduced by the framework; consider setting this field manually in the calibrate() function of your paradigm.'); end
        for s=1:length(bundle.streams)
            if ~((isstruct(bundle.streams{s}.chanlocs) && isfield(bundle.streams{s}.chanlocs,'labels')) || iscellstr(bundle.streams{s}.chanlocs))
                error('The .chanlocs fields in one of the streams of the stream bundle is malformed; needs to be a chanlocs struct (or cell array of channel labels), but was: %s',s,hlp_tostring(bundle.streams{s}.chanlocs)); end
            if iscellstr(bundle.streams{s}.chanlocs)
                bundle.streams{s}.chanlocs = struct('labels',bundle.streams{s}.chanlocs); end
            model.tracking.prediction_channels{s} = bundle.streams{s}.chanlocs;
        end
    else
        for s=1:length(model.tracking.prediction_channels)
            if ~((isstruct(model.tracking.prediction_channels{s}) && isfield(model.tracking.prediction_channels{s},'labels')) || iscellstr(model.tracking.prediction_channels{s}))
                error('The given model''s .tracking.prediction_channels{k} field is malformed for k=%i; needs to be a chanlocs struct or cell array of channel labels, but was: %s',s,hlp_tostring(model.tracking.prediction_channels{s})); end
        end
    end
catch e %#ok<CTCH>
    fprintf('Could not initialize prediction channels: %s\n',hlp_handleerror(e));
end

if length(model.tracking.filter_graph) ~= length(model.tracking.prediction_channels)
    error('The given filter graph has a different number of elements than the given prediction channels; they should have the same length.'); end

% finally add also a timestamp (so that we can sort them by creation time)
model.timestamp = now;


classdef imtool3DROI_rect < imtool3DROI

    properties
        fixedAspectRatio = false;
    end

    properties (SetAccess = protected, GetAccess = protected)
        position        %defines the center of the box and its width/height [cx cy width height]
        tbuff           %amount of space (in pixels) to place the text above the ROI
    end

    properties (Dependent = true, Access = public)
        aspectRatio
    end

    methods

        %constructor
        function ROI = imtool3DROI_rect(varargin)

            switch nargin
                case 0  %use the current figure

                    %let the user draw the ROI
                    h = imrect;

                    %get the parent axes
                    ha = get(h,'Parent');

                    %get the handle of the image
                    hi = imhandles(ha);
                    if length(hi)>1
                        for i=1:length(hi)
                            if ndims(get(hi(i),'CData'))<3
                                imageHandle = hi(i);
                            end
                        end
                    else
                        imageHandle = hi;
                    end

                    %get the position
                    pos = getPosition(h);
                    position = [pos(1)+pos(3)/2 pos(2)+pos(4)/2 pos(3) pos(4)];

                    %delete the imroi object
                    delete(h);
                case 1 %user inputs only the handle to the image
                    imageHandle = varargin{1};
                    parent = get(imageHandle,'Parent');
                    h = imrect(parent);
                    pos = getPosition(h);
                    position = [pos(1)+pos(3)/2 pos(2)+pos(4)/2 pos(3) pos(4)];
                    delete(h);
                case 2 %user inputs both the parent handle and a position
                    imageHandle = varargin{1};
                    position = varargin{2};
                case 3
                    imageHandle = varargin{1};
                    position = varargin{2};
                    if isempty(position)
                        parent = get(imageHandle,'Parent');
                        h = imrect(parent);
                        pos = getPosition(h);
                        position = [pos(1)+pos(3)/2 pos(2)+pos(4)/2 pos(3) pos(4)];
                        delete(h);
                    end
                    tool = varargin{3};
            end

            %get the parent axis handle
            parent = get(imageHandle,'Parent');

            %find the top left corner of the box
            pos = [position(1)-position(3)/2 position(2)-position(4)/2 position(3) position(4)];

            %Draw the rectangle at the desired spot
            graphicsHandles(1) = rectangle('Position',pos,'Parent',parent,'EdgeColor','r','LineWidth',1.5,'Curvature',[0 0]);

            %Draw a cross at the center of the box and sqaures on the sides
            nextPlot = get(parent,'NextPlot');  %make sure the new graphics don't delete the old ones
            set(parent,'NextPlot','add');
            graphicsHandles(2) = plot(position(1),position(2),'+r','MarkerSize',12,'Parent',parent); %middle cross
            graphicsHandles(3) = plot(pos(1),pos(2),'sr','MarkerSize',8,'Parent',parent,'MarkerFaceColor','r'); %top left corner
            graphicsHandles(4) = plot(pos(1)+pos(3),pos(2),'sr','MarkerSize',8,'Parent',parent,'MarkerFaceColor','r'); %top right corner
            graphicsHandles(5) = plot(pos(1),pos(2)+pos(4),'sr','MarkerSize',8,'Parent',parent,'MarkerFaceColor','r'); %bottom left corner
            graphicsHandles(6) = plot(pos(1)+pos(3),pos(2)+pos(4),'sr','MarkerSize',8,'Parent',parent,'MarkerFaceColor','r'); %bottom right corner
            graphicsHandles(7) = plot(pos(1),position(2),'sr','MarkerSize',8,'Parent',parent,'MarkerFaceColor','r'); %left
            graphicsHandles(8) = plot(pos(1)+pos(3),position(2),'sr','MarkerSize',8,'Parent',parent,'MarkerFaceColor','r'); %right
            graphicsHandles(9) = plot(position(1),pos(2),'sr','MarkerSize',8,'Parent',parent,'MarkerFaceColor','r'); %top
            graphicsHandles(10) = plot(position(1),pos(2)+pos(4),'sr','MarkerSize',8,'Parent',parent,'MarkerFaceColor','r'); %bottom
            set(graphicsHandles,'Clipping','off')
            set(parent,'NextPlot',nextPlot);

            %Define the context menu options (i.e., what happens when you
            %right click on the ROI)
            menuLabels = {'Export stats','Fix Aspect Ratio','Hide Text','Delete','poly2mask'};
            if ~exist('tool','var') || isempty(tool), menuLabels(end) = []; tool = []; end
            menuFunction = @contextMenuCallback;

            %create the ROI object from the superclass
            ROI@imtool3DROI(imageHandle,graphicsHandles,menuLabels,menuFunction, tool);

            %Create the text box
            I = get(ROI.imageHandle,'CData');
            ROI.tbuff = .02*size(I,1);
            ROI.textHandle = text(pos(1),pos(2)-ROI.tbuff,'text','Parent',parent,'Color','w','FontSize',10,'EdgeColor','w','BackgroundColor','k','HorizontalAlignment','Left','VerticalAlignment','bottom','Clipping','on');

            %Set the position property of the ROI
            ROI.position = position;

            %Set the button down functions of the graphics
            for i=1:length(graphicsHandles)
                fun = @(hObject,evnt) ButtonDownFunction(hObject,evnt,ROI,i); set(graphicsHandles(i),'ButtonDownFcn',fun);
            end

            %add a listener for changes in the image. This automatically
            %updates the ROI text when the image changes
            ROI.listenerHandle = addlistener(ROI.imageHandle,'CData','PostSet',@ROI.handlePropEvents);

            %update the text
            newPosition(ROI,position)

        end

        function position = getPosition(ROI)
            position = ROI.position;
        end

        function newPosition(ROI,position, notifoff)

            %Adjust the position if the aspect ratio should be fixed
            if ROI.fixedAspectRatio

            end

            %set the position property of the ROI
            ROI.position = position;

            %find the top left corner of the box
            pos = [position(1)-position(3)/2 position(2)-position(4)/2 position(3) position(4)];

            %get the graphics handles
            graphicsHandles = ROI.graphicsHandles;

            %set the new position of the rectangle and other graphics
            %objects
            set(graphicsHandles(1),'Position',pos);
            set(graphicsHandles(2),'Xdata',position(1),'Ydata',position(2));
            set(graphicsHandles(3),'Xdata',pos(1),'Ydata',pos(2));
            set(graphicsHandles(4),'Xdata',pos(1)+pos(3),'Ydata',pos(2));
            set(graphicsHandles(5),'Xdata',pos(1),'Ydata',pos(2)+pos(4));
            set(graphicsHandles(6),'Xdata',pos(1)+pos(3),'Ydata',pos(2)+pos(4));
            set(graphicsHandles(7),'Xdata',pos(1),'Ydata',position(2));
            set(graphicsHandles(8),'Xdata',pos(1)+pos(3),'Ydata',position(2));
            set(graphicsHandles(9),'Xdata',position(1),'Ydata',pos(2));
            set(graphicsHandles(10),'Xdata',position(1),'Ydata',pos(2)+pos(4));

            %get the ROI measurements
            stats = getMeasurements(ROI);

            %set the textbox
            str = {['Mean: ' num2str(stats.mean,'%+.2f')], ['STD:     ' num2str(stats.STD,'%.2f')]};
            set(ROI.textHandle,'String',str,'Position',[pos(1) pos(2)-ROI.tbuff]);

            %notify a new position
            if ~exist('notifoff','var') || ~notifoff
                notify(ROI,'newROIPosition');
            end

        end

        function newPositionSameSize(ROI,pos)
            position = ROI.position;
            position(1:2)=pos;
            newPosition(ROI,position);
        end

        function [x, y] = getPoly(ROI)
            %get the position
            position = ROI.position;

            %find the top left corner of the box
            pos = [position(1)-position(3)/2 position(2)-position(4)/2 position(3) position(4)];

            %make the polygon
            x = [pos(1) pos(1)+pos(3) pos(1)+pos(3) pos(1) pos(1)];
            y = [pos(2) pos(2) pos(2)+pos(4) pos(2)+pos(4) pos(2)];
        end

        function stats = getMeasurements(ROI)
            [x, y] = getPoly(ROI);
            im = double(get(ROI.imageHandle,'CData'));

            m = size(im,1);
            n = size(im,2);
            %Scale the polygon to match the size of the displayed image (in
            %the case that the displayed image is being upsampled to match
            %the screen resolution).
            x = x*n/ROI.imageHandle.XData(2);
            y = y*m/ROI.imageHandle.YData(2);

            mask = poly2mask(x,y,m,n);

            im = im(mask);

            stats.mean = mean(im);
            stats.STD = std(im);
            stats.min = min(im);
            stats.max = max(im);
            stats.mask = mask;
            stats.position = ROI.position;

        end

        function handlePropEvents(ROI,src,evnt)
            position = getPosition(ROI);
            newPosition(ROI,position);
        end

        function set.fixedAspectRatio(ROI,fixedAspectRatio)
            %Set the fixed aspect ratio property
            ROI.fixedAspectRatio = fixedAspectRatio;
            %update the context menu
            for i=1:length(ROI.menuHandles)
                if strcmp(get(ROI.menuHandles(i),'Label'),'Fix Aspect Ratio')
                    if fixedAspectRatio
                        set(ROI.menuHandles(i),'Check','on')
                    else
                        set(ROI.menuHandles(i),'Check','off')
                    end
                end
            end
        end

        function aspectRatio = get.aspectRatio(ROI)
            position = ROI.position;
            aspectRatio = position(3)/position(4);
        end

        function BB = getBoundingBox(ROI)
            %BB = [rowMin rowMax; colMin ColMax];
            lims = size(get(ROI.imageHandle,'CData'));

            BB=zeros(2);

            rowMin = floor(ROI.position(2) - ROI.position(4)/2);
            if rowMin < 1
                rowMin = 1;
            end
            colMin = floor(ROI.position(1) - ROI.position(3)/2);
            if colMin < 1
                colMin = 1;
            end
            rowMax = floor(ROI.position(2) + ROI.position(4)/2 - 1);
            if rowMax > lims(1)
                rowMax = lims(1);
            end
            colMax = floor(ROI.position(1) + ROI.position(3)/2 - 1);
            if colMax > lims(2)
                colMax = lims(2);
            end
            BB = [rowMin rowMax; colMin colMax];

        end

    end

end

function ButtonDownFunction(hObject,evnt,ROI,n)

%get the parent figure handle
fig = ROI.figureHandle;

%get the type of click
click = get(fig,'SelectionType');

if strcmp(click,'normal')
    %get the current button motion and button up functions of the figure
    WBMF_old = get(fig,'WindowButtonMotionFcn');
    WBUF_old = get(fig,'WindowButtonUpFcn');

    %set the new window button motion function and button up function of the figure
    fun = @(src,evnt) ButtonMotionFunction(src,evnt,ROI,n);
    fun2=@(src,evnt)  ButtonUpFunction(src,evnt,ROI,WBMF_old,WBUF_old);
    set(fig,'WindowButtonMotionFcn',fun,'WindowButtonUpFcn',fun2);
end
end

function ButtonMotionFunction(src,evnt,ROI,n)
cp = get(ROI.axesHandle,'CurrentPoint'); cp=[cp(1,1) cp(1,2)];

position = getPosition(ROI);

switch n
    case 2                                          %middle cross
        position(1) = cp(1); position(2) = cp(2);

    case 3                                          %top left corner

        %find the bottom edge
        bottom = position(2)+position(4)/2;
        %get the new height
        height = bottom - cp(2);
        if height>1
            cy = cp(2)+height/2;
            position(2) = cy;
            position(4) = height;
            %Adjust cp(1) if you want to fix the aspect ratio
            if ROI.fixedAspectRatio
                cp(1) = position(1)+position(3)/2-height*ROI.aspectRatio;
            end
        end

        %find the right edge
        right = position(1)+position(3)/2;
        %find the new width
        width = right-cp(1);
        if width>1
            %find the new center
            cx = cp(1)+width/2;
            position(1) = cx;
            position(3) = width;
        end

    case 4                                          %top right corner
        %find the bottom edge
        bottom = position(2)+position(4)/2;
        %get the new height
        height = bottom - cp(2);
        if height>1
            cy = cp(2)+height/2;
            position(2) = cy;
            position(4) = height;
            %Adjust cp(1) if you want to fix the aspect ratio
            if ROI.fixedAspectRatio
                cp(1) = position(1)-position(3)/2+height*ROI.aspectRatio;
            end
        end

        %find the left edge
        left = position(1)-position(3)/2;
        %find the new width
        width = cp(1) - left;
        if width>1
            cx = cp(1)-width/2;
            position(1) = cx;
            position(3) = width;
        end

    case 5                                          %bottom left corner
        %find the top edge
        top = position(2)-position(4)/2;
        %get the new height
        height = cp(2) - top;
        if height>1
            cy = cp(2)-height/2;
            position(2) = cy;
            position(4) = height;
            %Adjust cp(1) if you want to fix the aspect ratio
            if ROI.fixedAspectRatio
                cp(1) = position(1)+position(3)/2+-height*ROI.aspectRatio;
            end
        end

        %find the right edge
        right = position(1)+position(3)/2;
        %find the new width
        width = right-cp(1);
        if width>1
            %find the new center
            cx = cp(1)+width/2;
            position(1) = cx;
            position(3) = width;
        end

    case 6                                          %bottom right corner
        %find the top edge
        top = position(2)-position(4)/2;
        %get the new height
        height = cp(2) - top;
        if height>1
            cy = cp(2)-height/2;
            position(2) = cy;
            position(4) = height;
            %Adjust cp(1) if you want to fix the aspect ratio
            if ROI.fixedAspectRatio
                cp(1) = position(1)-position(3)/2+height*ROI.aspectRatio;
            end
        end

        %find the left edge
        left = position(1)-position(3)/2;
        %find the new width
        width = cp(1) - left;
        if width>1
            cx = cp(1)-width/2;
            position(1) = cx;
            position(3) = width;
        end

    case 7                                          %left
        %find the right edge
        right = position(1)+position(3)/2;
        %find the new width
        width = right-cp(1);
        if width>1
            %find the new center
            cx = cp(1)+width/2;
            position(1) = cx;
            position(3) = width;
        end

        if ROI.fixedAspectRatio
            position(4) = width/ROI.aspectRatio;
        end

    case 8                                          %right
        %find the left edge
        left = position(1)-position(3)/2;
        %find the new width
        width = cp(1) - left;
        if width>1
            cx = cp(1)-width/2;
            position(1) = cx;
            position(3) = width;
        end

        if ROI.fixedAspectRatio
            position(4) = width/ROI.aspectRatio;
        end

    case 9                                          %top
        %find the bottom edge
        bottom = position(2)+position(4)/2;
        %get the new height
        height = bottom - cp(2);
        if height>1
            cy = cp(2)+height/2;
            position(2) = cy;
            position(4) = height;
        end

        if ROI.fixedAspectRatio
            position(3) = height*ROI.aspectRatio;
        end

    case 10                                         %bottom
        %find the top edge
        top = position(2)-position(4)/2;
        %get the new height
        height = cp(2) - top;
        if height>1
            cy = cp(2)-height/2;
            position(2) = cy;
            position(4) = height;
        end

        if ROI.fixedAspectRatio
            position(3) = height*ROI.aspectRatio;
        end

end

newPosition(ROI,position);

end

function ButtonUpFunction(src,evnt,ROI,WBMF_old,WBUF_old)
fig = ROI.figureHandle;
set(fig,'WindowButtonMotionFcn',WBMF_old,'WindowButtonUpFcn',WBUF_old);
end

function contextMenuCallback(source,callbackdata,ROI, tool)

switch get(source,'Label')
    case 'Delete'
        delete(ROI);
    case 'Export stats'
        stats = getMeasurements(ROI);
        name = inputdlg('Enter variable name');
        if isempty(name); return; end
        name=name{1};
        assignin('base', name, stats)
    case 'Fix Aspect Ratio'
        switch get(source,'Checked')
            case 'off'
                ROI.fixedAspectRatio=true;
            case 'on'
                ROI.fixedAspectRatio=false;
        end
    case 'Hide Text'
        switch get(source,'Checked')
            case 'off'
                set(source,'Checked','on');
                ROI.textVisible = false;
            case 'on'
                set(source,'Checked','off');
                ROI.textVisible = true;
        end
    case 'poly2mask'
        mask = tool.getCurrentMaskSlice;

        [x, y] = getPoly(ROI);

        m = size(mask,1);
        n = size(mask,2);
        %Scale the polygon to match the size of the displayed image (in
        %the case that the displayed image is being upsampled to match
        %the screen resolution).
        x = x*n/ROI.imageHandle.XData(2);
        y = y*m/ROI.imageHandle.YData(2);

        masknew = poly2mask(x,y,m,n);
        combine = true;
        tool.setCurrentMaskSlice(masknew,combine);
        notify(tool,'maskChanged')
end

end


function nav=readblq(nav,obsr,obsb,fname)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%read ocean tide loading parameter(otlp) for rover and base
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
global glc
idx=strfind(fname,glc.sep); fname0=fname(idx(end)+1:end);
fprintf('Info:reading blq file %s...',fname0);
ns=0;

if obsr.n>0,sta(1)=obsr.sta;ns=1;end
if obsb.n>0,sta(2)=obsb.sta;ns=2;end
if ns==0,return;end

for i=1:ns
    fid=fopen(fname);
    sta_name=upper(sta(i).name);
    while ~feof(fid)
        line=fgets(fid);
        if ~isempty(strfind(line,'-cmc')),break;end
    end

    while ~feof(fid)
        line=fgets(fid);
        if ~isempty(strfind(line,'$$'))||size(line,2)<=2,continue;end
        name=upper(line(3:6));
        if isempty(strfind(sta_name,name)),continue;end

        [otlp,fid,stat]=decode_blqdata(fid);

        if stat==1
            nav.otlp(:,:,i)=otlp';
        end
    end
    fclose(fid);
end

fprintf('over\n');

return


function flg = checkDictUse
% Since MATLAB 7.3(2006A) and 7.4(2006B) doesnot support dicom dictionary
% this function checks the matlab version and returns the flag to allow the
% use of dictionary.
%
% DK
%
% Copyright 2010, Joseph O. Deasy, on behalf of the CERR development team.
%
% This file is part of The Computational Environment for Radiotherapy Research (CERR).
%
% CERR development has been led by:  Aditya Apte, Divya Khullar, James Alaly, and Joseph O. Deasy.
%
% CERR has been financially supported by the US National Institutes of Health under multiple grants.
%
% CERR is distributed under the terms of the Lesser GNU Public License.
%
%     This version of CERR 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.
%
% CERR 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 CERR.  If not, see <http://www.gnu.org/licenses/>.

% set flag to 1 so as to always use Dictionary
flg = 1;

mathVer = version;

if str2num(mathVer(1:3)) > 7.3
    flg = 0;
end

return


function value = isCategorical(tbl,name)
% Determine which columns in a table contain categorical variables.
%
% tbl  - The table
% name - Optional. Specify a specific column or cell array of columns to
%           check.
% OUTPUT
% value - logical matching the columns in tbl or the name cell-array
isCatColumn = @(x) (isa(x,'categorical') || iscellstr(x) || isstring(x) || ischar(x) || islogical(x));
value = logical(varfun(isCatColumn,tbl,'OutputFormat','Uniform'));
if nargin>1
    stay = ismember(tbl.Properties.VariableNames,name);
    value= value(stay);
end


function [mrk, ev]= mrk_removeVoidClasses(mrk)
%mrk= mrk_selectEvents(mrk)
%
% requires a field 'y' containing the class labels in mrk.

misc_checkType(mrk, 'STRUCT(time y)');

nonvoidClasses= find(any(mrk.y,2));
if length(nonvoidClasses)<size(mrk.y,1),
  msg= sprintf('void classes removed, %d classes remaining', ...
                  length(nonvoidClasses));
%   util_warning(msg, 'mrk', mfilename);
  warning(msg, 'mrk');
  mrk.y= mrk.y(nonvoidClasses,:);
  if isfield(mrk, 'className'),
    mrk.className= {mrk.className{nonvoidClasses}};
  end
end


function directories = XomicsToMultipleModels(modelGenerationConditions, param)
% Variations of the xomicstomodel function can be generated using this
% function.
%
% USAGE:
%
%    directories = XomicsToMultipleModels(modelGenerationConditions, param)
%
% INPUT:
%    modelGenerationConditions: Options to vary or to save the data
%
%       * .activeGenesApproach -The different approached to identify the active
%          genes (Possible options: 'deleteModelGenes' and 'oneRxnPerActiveGene';
%          default: 'deleteModelGenes');
%       * .boundsToRelaxExoMet - The type of bounds that can be relaxed, upper bounds,
%          lower bounds or both ('b'; possible options: 'u', 'l' and 'b';
%          default: 'b');
%       * .closeIons - Indicate whether the ions are open or closed (Possible options:
%          true and false; default: false);
%       * .cobraSolver - Optimisation solvers supported by the function. Possible
%          options: 'glpk', 'gurobi', 'ibm_cplex', 'matlab'; default: 'gurobi';
%       * .curationOverOmics - indicates whether curated data should take priority
%          over omics data ; default: false;
%       *. genericModel: Generic COBRA model(s)
%       * .inactiveGenesTranscriptomics - Use inactive transcriptomic genes or not
%          (Possible options: true and false; default: false);
%       * .specificData - Specific data variations (Default: empty)
%       * .limitBounds - Boundary on the model (Default: 1000).
%       * .metabolomicsBeforeExtraction - Indicate whether the metabolomic
%          data is included before or after the extraction (Possible options:
%          true and false; default: true);
%       * .tissueSpecificSolver - Extraction solver (Possible options: 'fastCore' and
%          'thermoKernel'; default: 'thermoKernel')
%       * .outputDir - Directory where the models will be generated (Default: current
%          directory)
%       * .transcriptomicThreshold - Transcriptomic thresholds that are defined by the
%          user (Default: log2(2));
%
%   param:           Variable with fixed parameters (Default: empty struct array)
%
% OUTPUTS:
%
%	directories - Array with the name of the new directories

if nargin < 2 || isempty(param)
    param = struct;
end

% Directory to save the models
if isfield(modelGenerationConditions, 'outputDir')
    outputDir = modelGenerationConditions.outputDir;
    outputDir = [regexprep(char(outputDir),'(/|\\)$',''), filesep];
else
    outputDir = [pwd filesep];
end

directories = [];

%% Set conditions

% Cobra solver
if isfield(modelGenerationConditions, 'cobraSolver')
    cobraSolver = modelGenerationConditions.cobraSolver;
else
    cobraSolver = {'gurobi'};
end
% genericModel
if isfield(modelGenerationConditions, 'genericModel')
    models = modelGenerationConditions.genericModel;
    modelsLabels = fieldnames(models);
else
    error('A generic model is needed in modelGenerationConditions.genericModel')
end
% Context-specific input data
if isfield(modelGenerationConditions, 'specificData')
    specificDataforXomics = modelGenerationConditions.specificData;
    specificDataLabels = fieldnames(specificDataforXomics);
else
    specificDataforXomics = struct;
end
% Tissue specific solver
if isfield(modelGenerationConditions, 'tissueSpecificSolver')
    tissueSpecificSolver = modelGenerationConditions.tissueSpecificSolver;
elseif ~isfield(modelGenerationConditions, 'tissueSpecificSolver') && isfield(param, 'tissueSpecificSolver')
    tissueSpecificSolver = {param.tissueSpecificSolver};
else
    tissueSpecificSolver = {'thermoKernel'};
end
% Active genes approach
if isfield(modelGenerationConditions, 'activeGenesApproach')
    activeGenesApproach = modelGenerationConditions.activeGenesApproach;
elseif ~isfield(modelGenerationConditions, 'activeGenesApproach') && isfield(param, 'activeGenesApproach')
    activeGenesApproach = {param.activeGenesApproach};
else
    activeGenesApproach = {'oneRxnPerActiveGene'};
end
% Transcriptomic threshold
if isfield(modelGenerationConditions, 'transcriptomicThreshold')
    transcriptomicThreshold = modelGenerationConditions.transcriptomicThreshold;
elseif ~isfield(modelGenerationConditions, 'transcriptomicThreshold') && isfield(param, 'transcriptomicThreshold')
    transcriptomicThreshold = param.transcriptomicThreshold;
else
    transcriptomicThreshold = 2;
end
% Limit bounds
if isfield(modelGenerationConditions, 'limitBounds')
    limitBounds = modelGenerationConditions.limitBounds;
elseif ~isfield(modelGenerationConditions, 'limitBounds') && isfield(param, 'limitBounds')
    limitBounds = param.limitBounds;
else
    limitBounds = 10e4;
end
% Use inactive genes from transcriptomics
if isfield(modelGenerationConditions, 'inactiveGenesTranscriptomics')
    inactiveGenesTranscriptomics = modelGenerationConditions.inactiveGenesTranscriptomics;
elseif ~isfield(modelGenerationConditions, 'inactiveGenesTranscriptomics') && isfield(param, 'inactiveGenesTranscriptomics')
    inactiveGenesTranscriptomics = param.inactiveGenesTranscriptomics;
else
    inactiveGenesTranscriptomics = false;
end
% Ions exchange
if isfield(modelGenerationConditions, 'closeIons')
    closeIons = modelGenerationConditions.closeIons;
elseif ~isfield(modelGenerationConditions, 'closeIons') && isfield(param, 'closeIons')
    closeIons = param.closeIons;
else
    closeIons = false;
end
% boundsToRelaxExoMet
if isfield(modelGenerationConditions, 'boundsToRelaxExoMet')
    boundsToRelaxExoMet = modelGenerationConditions.boundsToRelaxExoMet;
else
    boundsToRelaxExoMet = {'b'};
end
% curationOverOmics
if isfield(modelGenerationConditions, 'curationOverOmics')
    curationOverOmics = modelGenerationConditions.curationOverOmics;
elseif ~isfield(modelGenerationConditions, 'curationOverOmics') && isfield(param, 'curationOverOmics')
    curationOverOmics = param.curationOverOmics;
else
    curationOverOmics = false;
end
% metabolomicsBeforeExtraction
if isfield(modelGenerationConditions, 'metabolomicsBeforeExtraction')
    metabolomicsBeforeExtraction = modelGenerationConditions.metabolomicsBeforeExtraction;
elseif ~isfield(modelGenerationConditions, 'metabolomicsBeforeExtraction') && ...
        isfield(param, 'metabolomicsBeforeExtraction')
    metabolomicsBeforeExtraction = param.metabolomicsBeforeExtraction;
else
    metabolomicsBeforeExtraction = true;
end

disp('Generating models ...');

% Data with more than one variation will be included in the folder name
conditionsBool = false(11, 1);
if length(cobraSolver) > 1
    conditionsBool(1) = true;
end
if length(modelsLabels) > 1
    conditionsBool(2) = true;
end
if length(specificDataLabels) > 1
    conditionsBool(3) = true;
end
if length(tissueSpecificSolver) > 1
    conditionsBool(4) = true;
end
if length(activeGenesApproach) > 1
    conditionsBool(5) = true;
end
if length(transcriptomicThreshold) > 1
    conditionsBool(6) = true;
end
if length(limitBounds)> 1
    conditionsBool(7) = true;
end
if length(inactiveGenesTranscriptomics) > 1
    conditionsBool(8) = true;
end
if length(closeIons) > 1
    conditionsBool(9) = true;
end
if length(boundsToRelaxExoMet) > 1
    conditionsBool(10) = true;
end
if length(curationOverOmics) > 1
    conditionsBool(11) = true;
end
if length(metabolomicsBeforeExtraction) > 1
    conditionsBool(12) = true;
end

%% Check if there are any fields in modelGenerationConditions that are not specified above
% Because these need to be added to param and cannot yet be used here.
unknownmodelGenerationConditions = setdiff(fieldnames(modelGenerationConditions), ...
    {'cobraSolver'
    'genericModel'
    'specificData'
    'tissueSpecificSolver'
    'activeGenesApproach'
    'transcriptomicThreshold'
    'limitBounds'
    'inactiveGenesTranscriptomics'
    'closeIons'
    'boundsToRelaxExoMet'
    'curationOverOmics'
    'metabolomicsBeforeExtraction'
    'outputDir'});

if ~isempty(unknownmodelGenerationConditions)
    msg = ['Unknown parameter(s) specified in modelGenerationConditions: ' strjoin(unknownmodelGenerationConditions) '. Specify these in param'];
    error(msg)
end

%% Generate models

% Select cobraSolver
dirIdx = 0;
for firstGruoup = 1:length(cobraSolver)
    switch cobraSolver{firstGruoup}
        case 'gurobi'
            solverLabel = 'gurobi';
            [solverOK, solverInstalled] = changeCobraSolver('gurobi','all');
        case 'ibm_cplex'
            solverLabel = 'ibmCplex';
            [solverOK, solverInstalled] = changeCobraSolver('ibm_cplex','all');
        case 'mosek'
            solverLabel = 'mosek';
            [solverOK, solverInstalled] = changeCobraSolver('mosek','all');
    end

    % Input model
    for secondGroup = 1:length(modelsLabels)
        modelLabel = modelsLabels{secondGroup};
        genericModel = models.(modelsLabels{secondGroup});

        % specificDataInfo
        for thirdGruoup = 1:length(specificDataLabels)
            specificDataLabel = specificDataLabels{thirdGruoup};
            specificData = specificDataforXomics.(specificDataLabels{thirdGruoup});

            % Extraction algorithm
            for fourthGroup = 1:length(tissueSpecificSolver)
                param.tissueSpecificSolver = tissueSpecificSolver{fourthGroup};

                % Active genes approach
                for fifthGroup = 1:length(activeGenesApproach)
                    param.activeGenesApproach = activeGenesApproach{fifthGroup};
                    param.activeGenesApproach = regexprep(activeGenesApproach{fifthGroup}, 'oneRxnsPerActiveGene', 'oneRxnPerActiveGene');

                    % Transcriptomic Threshold
                    for sixthGroup = 1:length(transcriptomicThreshold)
                        param.transcriptomicThreshold = transcriptomicThreshold(sixthGroup);

                        % Load input data
                        for seventhGroup = 1:length(limitBounds)
                            param.TolMinBoundary = -limitBounds(seventhGroup);
                            param.TolMaxBoundary = limitBounds(seventhGroup);

                            % Option to identify genes as inactive if
                            % expressed below transcriptomic threshold
                            for eigthGroup = 1:length(inactiveGenesTranscriptomics)
                                param.inactiveGenesTranscriptomics = inactiveGenesTranscriptomics(eigthGroup);
                                if inactiveGenesTranscriptomics(eigthGroup)
                                    inactiveGenesTLabel = 'inactiveGenesT';
                                else
                                    inactiveGenesTLabel = 'NoInactiveGenesT';
                                end

                                % closeIons
                                for ninethGroup = 1:length(closeIons)
                                    param.closeIons = closeIons(ninethGroup);
                                    if closeIons(ninethGroup)
                                        ionsLabel = 'closedIons';
                                    else
                                        ionsLabel = 'openIons';
                                    end

                                    % Bounds to relax metabolomicsToModel (TODO)
                                    for tenthGroup = 1:length(boundsToRelaxExoMet)
                                        param.relaxOptions.bounds = boundsToRelaxExoMet{tenthGroup};
                                        switch boundsToRelaxExoMet{tenthGroup}
                                            case 'upper'
                                                boundsLabel = 'mediaUBRelaxed';
                                            case 'lower'
                                                boundsLabel = 'mediaLBRelaxed';
                                            case 'both'
                                                boundsLabel = 'mediaBBRelaxed';
                                        end

                                        for eleventhGroup = 1:length(curationOverOmics)
                                            param.curationOverOmics = curationOverOmics(eleventhGroup);
                                            if curationOverOmics(eleventhGroup)
                                                priorityLabel = 'curationOverOmics';
                                            else
                                                priorityLabel = 'omicsOverCuration';
                                            end

                                            for twelfthGroup = 1:length(metabolomicsBeforeExtraction)
                                                param.metabolomicsBeforeExtraction = metabolomicsBeforeExtraction(twelfthGroup);
                                                if metabolomicsBeforeExtraction(twelfthGroup)
                                                    exoMetLabel = 'exoMetBeforeExtraction';
                                                else
                                                    exoMetLabel = 'exoMetAfterExtraction';
                                                end

                                                % Establish the working directory
                                                conditions = [solverLabel; ...
                                                    modelLabel; ...
                                                    specificDataLabel; ...
                                                    tissueSpecificSolver{fourthGroup}; ...
                                                    activeGenesApproach{fifthGroup}; ...
                                                    {['transcriptomicsT' num2str(transcriptomicThreshold(sixthGroup))]}; ...
                                                    {['limitBoundary.' num2str(limitBounds(seventhGroup))]}; ...
                                                    inactiveGenesTLabel; ...
                                                    ionsLabel; ...
                                                    boundsLabel;
                                                    priorityLabel;
                                                    exoMetLabel];

                                                workingDirectory = [outputDir ...
                                                    strjoin(conditions(conditionsBool), '_')];
                                                param.workingDirectory = workingDirectory;

                                                dirContent = dir(workingDirectory);
                                                if ~isfolder(workingDirectory)
                                                    mkdir(workingDirectory)
%                                                 elseif any(~cellfun(@isempty, regexp({dirContent.name}, '.txt')))
%                                                     continue
                                                end

                                                % Start the diary
                                                if isunix()
                                                    name = getenv('USER');
                                                else
                                                    name = getenv('username');
                                                end
                                                param.diaryFilename = [workingDirectory filesep datestr(now,30) '_' name '_diary.txt'];

                                                % Model generation only if no model already present in folder
                                                fileNames = what(workingDirectory);

                                                if any(ismember(fileNames.mat,'Model.mat'))
                                                    load([workingDirectory filesep 'Model.mat'])
                                                    %test properties of previously computed model to decide if recomputation is necessary
                                                    if size(Model.S,2)==length(Model.expressionRxns)
                                                        recompute = 0;
                                                    else
                                                        fprintf('%s\n', ['Recomputing because prexisting inconsistent model in ' workingDirectory ])
                                                        recompute = 1;
                                                    end
                                                else
                                                    recompute = 1;
                                                end

                                                if ~recompute && any(ismember(fileNames.mat,'Model.mat'))
                                                    fprintf('%s\n', ['Prexisting model in ' workingDirectory ])
                                                else
                                                    fprintf('%s\n', ['Computing new model in ' workingDirectory ])
                                                    try
                                                        [omicsModel, modelGenerationReport] = XomicsToModel(genericModel, specificData, param);
                                                        % Save the model with the correct name
                                                        Model = omicsModel;
                                                        save([workingDirectory filesep 'Model.mat'], 'Model', 'modelGenerationReport')
                                                    catch ME
                                                        warning('XomicsToModel failed to run')
                                                        disp(param)
                                                        disp(ME)
                                                        msgText = getReport(ME)
                                                        % Close the diary if the run crashed
                                                        fprintf('%s\n', ['Diary written to: ' param.diaryFilename])
                                                        diary off
                                                    end
                                                end

                                                if any(conditionsBool)
                                                    dirIdx = dirIdx + 1;
                                                    directories{dirIdx} = strjoin(conditions(conditionsBool), '_');
                                                else
                                                    directories = pwd;
                                                end
                                            end
                                        end
                                    end
                                end
                            end
                        end
                    end
                end
            end
        end
    end
end


function apAll = evalRP_multicut(expidxs, bSave, subset)

fprintf('evalRP_multicut()\n');

if (nargin < 2)
    bSave = true;
end

if (nargin < 3)
    subset = [];
end

fontSize = 18;
% figure(100); clf; hold on;
legendName = cell(0);
set(0,'DefaultAxesFontSize', fontSize)
set(0,'DefaultTextFontSize', fontSize)

prefix = 'pck-head-rpc-';
[~,parts] = util_get_parts24();
nJoints = 14;
pck_thresh = 0.5;

% clusSizeThresh = 15;

num_exps = length(expidxs);
imgidxs_missing_all = cell(num_exps, 1);
keypointsAll_all = cell(num_exps, 1);
annolist_gt_all = cell(num_exps, 1);
annolist_bbox_all = cell(num_exps, 1);

for ii = 1:num_exps
    fprintf('**********************************************************************************\n');
    expidx = expidxs(ii);

    % load experiment parameters
    p = exp_params(expidx);

    load(p.testGT);
    annolist_gt = annolist;%(1:300);

    mkdir_if_missing(p.latexDir);
    mkdir_if_missing(p.plotsDir);

    assert(length(p.pidxs) == nJoints);

    if (isfield(p,'clusSizeThresh'))
        clusSizeThresh = p.clusSizeThresh;
    else
        clusSizeThresh = 1;
    end

    if (isfield(p,'disjointGTassignment'))
        disjointGTassignment = p.disjointGTassignment;
    else
        disjointGTassignment = false;
    end

    % load keypoints
    if (isfield(p,'multicut') && p.multicut == true)
        [keypointsAll,imgidxs_missing] = det2keypointsImgMulticutMulti(expidx,parts,annolist_gt,clusSizeThresh);
    elseif (isfield(p,'oracleDetBBox') && p.oracleDetBBox == true && ~isfield(p,'poseClusters'))
        annolist_bbox = compute_gt_bbox(annolist_gt,p.pidxs,parts,p.bbox_offset);
        [keypointsAll,imgidxs_missing] = det2keypointsImgMulticutBBoxMulti(expidx,parts,annolist_bbox,disjointGTassignment);
    elseif (isfield(p,'neckDetBBox') && p.neckDetBBox == true && ~isfield(p,'poseClusters'))
        try
            assert(false);
            load([p.outputDir '/annolist_bbox'],'annolist_bbox');
        catch
            annolist_bbox = compute_det_bbox(expidx,annolist_gt,p.neck_nms_thresh,p.neck_det_thresh,p.bbox_offset);
            if (~exist(p.outputDir,'dir')),mkdir(p.outputDir);end
            save([p.outputDir '/annolist_bbox'],'annolist_bbox');
        end
        [keypointsAll,imgidxs_missing] = det2keypointsImgMulticutBBoxMulti(expidx,parts,annolist_bbox,disjointGTassignment);
    elseif (isfield(p,'poseClusters') && p.poseClusters == true)
        load(p.testGTnopad);
        annolist_gt = annolist(1:length(annolist_gt));
        [keypointsAll,imgidxs_missing] = det2keypointsImgMulticutBBoxMultiPoses(expidx,parts,length(annolist_gt));
    end

    imgidxs_missing_all{ii} = imgidxs_missing;
    keypointsAll_all{ii} = keypointsAll;
    annolist_gt_all{ii} = annolist_gt;
    %annolist_bbox_all{ii} = annolist_bbox;
end

imgidxs_missing = [];

for ii = 1:num_exps
    imgidxs_missing = [imgidxs_missing; imgidxs_missing_all{ii}];
end

imgidxs_missing = unique(imgidxs_missing);

%im_idx = 7;
%imgidxs_missing = [1:(im_idx-1) (im_idx+1):1758];

fprintf('final images missing %d\n', length(imgidxs_missing));

for ii = 1:num_exps
    expidx = expidxs(ii);
    p = exp_params(expidx);

    keypointsAll = keypointsAll_all{ii};
    annolist_gt = annolist_gt_all{ii};
    %annolist_bbox = annolist_bbox_all{ii};

    keypointsAll16 = keypointsAll;
    if isempty(subset)
        subset = true(length(annolist_gt), 1);
        subset(imgidxs_missing) = 0;
    end
    annolist_gt = annolist_gt(subset);
    keypointsAll = keypointsAll(subset);

    assert(length(annolist_gt) == length(keypointsAll));
    [scoresAll, labelsAll, nGTall] = assignGTMulticutMulti(keypointsAll,annolist_gt,p.pidxs,parts,pck_thresh);

%     groups = {0,2,4,5,7,9,12,14,16,17,19,21,22,23};
    partLabels = {'rankle','rknee','rhip','lhip','lknee','lankle','rwrist','relbow','rshoulder','lshoulder','lelbow','lwrist','neck','tophead'};

    apAll = zeros(length(p.pidxs),1);
%     figure(100); clf;

    for i = 1:length(p.pidxs)
        scores = [];
        labels = [];
        for imgidx = 1:length(annolist_gt)
            scores = [scores; scoresAll{i}{imgidx}];
            labels = [labels; labelsAll{i}{imgidx}];
        end
        [precision,recall,sorted_scores,sortidx,sorted_labels] = getRPC(scores,labels,sum(nGTall(i,:)));
        ap = VOCap(recall,precision)*100;
        apAll(i) = ap;
        legendName = sprintf('%s, AP: %1.1f%%', p.name, ap);
        fprintf('%s\n',legendName);

%         plotRPC(precision,recall,p.colorName,'-',partLabels{i});
%         legend(legendName,'Location','NorthWest');
%         if (bSave)
%             print(gcf, '-dpng', [p.plotsDir '/' prefix partLabels{i} '-' 'expidx' num2str(expidx) '.png']);
%             printpdf([p.plotsDir '/' prefix partLabels{i} '-' 'expidx' num2str(expidx) '.pdf']);
%         end
    end

    num_images = length(labelsAll{1});
    num_parts = length(labelsAll);
    simple_scores = zeros(num_images, 1);
    for k = 1:num_images
        num_hits = 0;
        num_all = 0;
        for i = 1:num_parts
            lbl = labelsAll{i}{k};
            num_all = num_all + length(lbl);
            num_hits = num_hits + sum(lbl);
        end

        if num_all ~= 0
            simple_scores(k) = num_hits/num_all;
        end
    end

    mkdir_if_missing(fullfile(p.exp_dir, 'data'));
    save(fullfile(p.exp_dir, 'data', 'scores'), 'simple_scores', 'subset', 'keypointsAll16');

    if (bSave)
        tableFilename = [p.latexDir '/ap-expidx' num2str(expidx) '.tex'];
        apAll(15,:) = mean(apAll(7:14));
        apAll(16,:) = mean(apAll);
        [row, header] = genTable(apAll,p.name);
        fid = fopen(tableFilename,'wt');assert(fid ~= -1);
        fprintf(fid,'%s\n',row{1});fclose(fid);
        fid = fopen([p.latexDir '/ap-header.tex'],'wt');assert(fid ~= -1);
        fprintf(fid,'%s\n',header);fclose(fid);
        save([p.latexDir '/ap-expidx' num2str(expidx) '.mat'],'apAll');
    end
end
end


classdef PTKCoordinateSystem
    % PTKCoordinateSystem. Legacy support class for backwards compatibility. Replaced by MimCoordinateSystem
    %
    %     Licence
    %     -------
    %     Part of the TD MIM Toolkit. https://github.com/tomdoel
    %     Author: Tom Doel, 2013.  www.tomdoel.com
    %     Distributed under the MIT licence. Please see website for details.
    %

    properties (Constant)
        PTK = MimCoordinateSystem.PTK;                 % Coordinates in mm relatve to the top-left corner of the image volume
        Dicom = MimCoordinateSystem.Dicom              % Coordinates in mm relative to the scanner origin
        DicomUntranslated = MimCoordinateSystem.DicomUntranslated  % Origin in the centre of the first voxel of the image
    end
end


classdef ParameterServer < handle
  properties
    params
    paramsRegister
    method
    prefix
    memoryMapFile
    memoryMap
    pinnedMemory
    otherLabs
    inplace
    tflowOpts
  end

  methods
    function obj = ParameterServer(varargin)
      obj.prefix = 'mcn' ;
      obj.params = struct('name', {}, 'shape', {}, ...
                          'dataType', {}, 'deviceType', {}, ...
                          'value', {}) ;
      obj.paramsRegister = struct() ;
      switch computer('arch')
        case 'win64'
          obj.method = 'mmap' ;
        otherwise
          obj.method = 'tflow' ;
      end
      obj.pinnedMemory = true ;
      obj.inplace = true ;
      obj.tflowOpts = { } ;
      obj = vl_argparse(obj, varargin) ;

      % Compute the memory map file from the prefix.  This is used only by
      % the 'mmap' mode.
      obj.memoryMapFile = fullfile(tempdir, sprintf('%s.bin',obj.prefix)) ;
    end

    function register(obj, name, shape, dataType, deviceType)
      x = zeros(shape, dataType) ;
      if strcmp(deviceType,'gpu')
        x = gpuArray(x) ;
      end
      obj.params(end+1).name = name ;
      obj.params(end).shape = shape ;
      obj.params(end).dataType = dataType ;
      obj.params(end).deviceType = deviceType ;
      obj.params(end).isGPU = strcmp(deviceType, 'gpu') ;
      obj.params(end).value =  x ;
      obj.paramsRegister.(name) = numel(obj.params) ;
    end

    function delete(obj)
      obj.stop()
    end

    function start(obj)
      switch obj.method
        case 'mmap'
          if labindex == 1 && exist(obj.memoryMapFile)
            delete(obj.memoryMapFile) ;
          end
          labBarrier() ;
          obj = startWithMMap(obj) ;
        case 'tflow'
          obj = startWithTFlow(obj) ;
      end
    end

    function stop(obj)
      switch obj.method
        case 'mmap', stopWithMMap(obj) ;
        case 'tflow', stopWithTFlow(obj) ;
      end
    end

    function push(obj, name, value)
      p = obj.paramsRegister.(name) ;
      obj.pushWithIndex(p, value) ;
    end

    function value = pull(obj, name)
      p = obj.paramsRegister.(name) ;
      value = obj.pullWithIndex(p) ;
    end

    function pushWithIndex(obj, p, value)
      switch obj.method
        case 'mmap'
          obj.params(p).value = value ;

        case 'tflow'
          if obj.params(p).isGPU && obj.inplace
            obj.params(p).value = value ;
            vl_tflow('push', obj.params(p).name, value, 'inplace', obj.tflowOpts{:}) ;
          else
            vl_tflow('push', obj.params(p).name, value, obj.tflowOpts{:}) ;
          end

        otherwise
          assert(false) ;
      end
    end

    function value = pullWithIndex(obj,p)
      switch obj.method
        case 'mmap'
          for l = obj.otherLabs
            obj.params(p).value = obj.params(p).value ...
                + obj.memoryMap.Data(l).(obj.params(p).name) ;
          end
          value = obj.params(p).value ;

        case 'tflow'
          if obj.params(p).isGPU && obj.inplace
            vl_tflow('pull', obj.params(p).name, 'inplace', obj.tflowOpts{:}) ;
            value = obj.params(p).value ;
          else
            value = vl_tflow('pull', obj.params(p).name, obj.tflowOpts{:}) ;
          end
      end
    end

    function sync(obj)
      switch obj.method
        case 'mmap'
          for p = 1:numel(obj.params)
            if ~obj.pinnedMemory || ~obj.params(p).isGPU
              obj.memoryMap.Data(labindex).(obj.params(p).name) = ...
                  gather(obj.params(p).value) ;
            else
              vl_cudatool('cudaCopyDeviceToHost', ...
                       obj.memoryMap.Data(labindex).(obj.params(p).name), ...
                       obj.params(p).value) ;
            end
          end
          labBarrier() ;
        case 'tflow'
          % nothing to do
      end
    end
  end

  methods (Access = protected)
    function obj = startWithMMap(obj)
      obj.otherLabs = setdiff(1:numlabs, labindex) ;
      format = {} ;
      for i=1:numel(obj.params)
        format(i,1:3) = {obj.params(i).dataType, ...
                         obj.params(i).shape, ...
                         obj.params(i).name} ;
      end
      if labindex == 1
        f = fopen(obj.memoryMapFile,'wb') ;
        for g = 1:numlabs
          for i = 1:size(format,1)
            fwrite(f,zeros(format{i,2},format{i,1}),format{i,1}) ;
          end
        end
        fclose(f) ;
      end
      labBarrier() ;
      obj.memoryMap = memmapfile(obj.memoryMapFile, ...
                                 'Format', format, ...
                                 'Repeat', numlabs, ...
                                 'Writable', true) ;
      if obj.pinnedMemory && any([obj.params.isGPU])
        for i=1:numel(obj.memoryMap.Data)
          names = fieldnames(obj.memoryMap.Data(i))' ;
          for name = names
            vl_cudatool('cudaRegister', obj.memoryMap.Data(i).(char(name))) ;
          end
        end
      end
    end

    function stopWithMMap(obj)
      if ~isempty(obj.memoryMap)
        if obj.pinnedMemory && any([obj.params.isGPU])
          for i=1:numel(obj.memoryMap.Data)
            names = fieldnames(obj.memoryMap.Data(i))' ;
            for name = names
              vl_cudatool('cudaUnregister', obj.memoryMap.Data(i).(char(name))) ;
            end
          end
        end
        obj.memoryMap = [] ;
        if labindex == 1
          delete(obj.memoryMapFile) ;
        end
      end
    end

    function obj = startWithTFlow(obj)
      format = {} ;
      for i=1:numel(obj.params)
        format(i,1:4) = {obj.params(i).dataType, ...
                         obj.params(i).shape, ...
                         obj.params(i).name, ...
                         obj.params(i).deviceType} ;
      end
      vl_tflow('reset', obj.tflowOpts{:}) ;
      vl_tflow('init', format, ...
               labindex, numlabs, ...
               'prefix', obj.prefix, ...
               obj.tflowOpts{:}) ;
    end

    function stopWithTFlow(obj)
      vl_tflow('reset', obj.tflowOpts{:}) ;
    end

  end
end


classdef StimClass < matlab.mixin.Copyable

    % SNIRF-spec class properties
    properties
        name
        data
    end

    % Non-SNIRF class properties
    properties (Access = private)
        filename
        fileformat
    end

    % Properties not part of the SNIRF spec. These parameters aren't loaded or saved to files
    properties (Access = private)
        errmargin
    end

    methods

        % -------------------------------------------------------
        function obj = StimClass(varargin)
            % Set class properties not part of the SNIRF format
            obj.fileformat = 'hdf5';
            obj.errmargin = 1e-3;

            if nargin==1
                if isa(varargin{1}, 'StimClass')
                    obj.Copy(varargin{1});
                elseif ischar(varargin{1})
                    if exist(varargin{1}, 'file')==2
                        obj.filename = varargin{1};
                        obj.Load(varargin{1});
                    else
                        obj.name = varargin{1};
                        obj.data = [];
                    end
                end
            elseif nargin==2
                if ischar(varargin{1})
                    obj.filename = varargin{1};
                    obj.Load(varargin{1}, obj.fileformat, varargin{2});
                else
                    t        = varargin{1};
                    CondName = varargin{2};
                    obj.name = CondName;
                    for ii=1:length(t)
                        obj.data(end+1,:) = [t(ii), 5, 1];
                    end
                end
            elseif nargin==3
                s        = varargin{1};
                t        = varargin{2};
                CondName = varargin{3};
                obj.name = CondName;
                k = s>0 | s==-1 | s==-2;  % Include stim marks with these values
                obj.data = [t(k), 5*ones(length(t(k)),1), s(k)];
            elseif nargin==0
                obj.name = '';
                obj.data = [];
            end

        end

        % -------------------------------------------------------
        function err = LoadHdf5(obj, fileobj, location)
            err = 0;

            % Arg 1
            if ~exist('fileobj','var') || (ischar(fileobj) && ~exist(fileobj,'file'))
                fileobj = '';
            end

            % Arg 2
            if ~exist('location', 'var') || isempty(location)
                location = '/nirs/stim1';
            elseif location(1)~='/'
                location = ['/',location];
            end

            % Error checking
            if ~isempty(fileobj) && ischar(fileobj)
                obj.filename = fileobj;
            elseif isempty(fileobj)
                fileobj = obj.filename;
            end
            if isempty(fileobj)
               err = -1;
               return;
            end

            try

                % Open group
                [gid, fid] = HDF5_GroupOpen(fileobj, location);

                % Load datasets
                obj.name   = HDF5_DatasetLoad(gid, 'name');
                obj.data   = HDF5_DatasetLoad(gid, 'data', [], '2D');
                if all(obj.data(:)==0)
                    obj.data = [];
                end

                % Close group
                HDF5_GroupClose(fileobj, gid, fid);

            catch ME

                err = -1;
                return

            end

        end

        % -------------------------------------------------------
        function SaveHdf5(obj, fileobj, location)
            if isempty(obj.data)
                obj.data = 0;
            end

            % Arg 1
            if ~exist('fileobj', 'var') || isempty(fileobj)
                error('Unable to save file. No file name given.')
            end

            % Arg 2
            if ~exist('location', 'var') || isempty(location)
                location = '/nirs/stim1';
            elseif location(1)~='/'
                location = ['/',location];
            end

            if ~exist(fileobj, 'file')
                fid = H5F.create(fileobj, 'H5F_ACC_TRUNC', 'H5P_DEFAULT', 'H5P_DEFAULT');
                H5F.close(fid);
            end
            hdf5write_safe(fileobj, [location, '/name'], obj.name);

            % Since this is a writable writeable parameter AFTER it's creation, we
            % call hdf5write_safe with the 'rw' option
            hdf5write_safe(fileobj, [location, '/data'], obj.data, 'rw:2D');
        end

        % -------------------------------------------------------
        function Update(obj, fileobj, location)
            if ~exist(fileobj, 'file')
                fid = H5F.create(fileobj, 'H5F_ACC_TRUNC', 'H5P_DEFAULT', 'H5P_DEFAULT');
                H5F.close(fid);
            end
            hdf5write_safe(fileobj, [location, '/name'], obj.name);
            hdf5write_safe(fileobj, [location, '/data'], obj.data, 'w');
        end

        % -------------------------------------------------------
        function Copy(obj, obj2)
            obj.name = obj2.name;
            obj.data = obj2.data;
        end

        % -------------------------------------------------------
        function B = eq(obj, obj2)
            B = false;
            if ~strcmp(obj.name, obj2.name)
                return;
            end

            % Dimensions matter so dimensions must equal
            if ~all(size(obj.data)==size(obj2.data))
                return;
            end

            % Now check contents
            if ~all(obj.data(:)==obj2.data(:))
                return;
            end
            B = true;
        end

    end

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % Public Set/Get methods
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    methods

        % -------------------------------------------------------
        function SetName(obj, val)
            obj.name = val;
        end

        % -------------------------------------------------------
        function val = GetName(obj)
            val = obj.name;
        end

        % -------------------------------------------------------
        function SetData(obj, val)
            obj.data = val;
        end

        % -------------------------------------------------------
        function val = GetData(obj)
            val = obj.data;
        end

    end

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % All other public methods
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    methods

        % ----------------------------------------------------------------------------------
        function b = Exists(obj, tPt)
            b = false;
            if ~exist('tPt','var')
                return;
            end
            if isempty(obj.data)
                return;
            end
            if isempty(find( abs(obj.data(:,1)-tPt) <  obj.errmargin ))
                return;
            end
            b = true;
        end

        % ----------------------------------------------------------------------------------
        function AddStims(obj, tPts, duration, val)
            if ~exist('duration','var')
                duration = 5;
            end
            if ~exist('val','var')
                val = 1;
            end
            for ii=1:length(tPts)
                if ~obj.Exists(tPts(ii))
                    obj.data(end+1,:) = [tPts(ii), duration, val];
                end
            end
        end

        % ----------------------------------------------------------------------------------
        function EditValue(obj, tPts, val)
            if isempty(obj.data)
                return;
            end
            if ~exist('val','var')
                val = 1;
            end
            for ii=1:length(tPts)
                k = find( abs(obj.data(:,1)-tPts(ii)) < obj.errmargin );
                if isempty(k)
                    continue;
                end
                obj.data(k,3) = val;
            end
        end

        % -------------------------------------------------------
        function [ts, v] = GetStim(obj)
            ts = [];
            v = [];
            if isempty(obj.data)
                return;
            end
            ts = obj.data(:,1);
            v = obj.data(:,3);
        end

        % ----------------------------------------------------------------------------------
        function SetTpts(obj, tPts, k)
            if ~exist('k','var')
                k = 1:size(obj.data,1);
            end
            if ~exist('tPts','var')
                return;
            end
            if length(tPts)~=1 && length(tPts)~=length(k)
                return;
            end
            obj.data(k,1) = tPts;
        end

        % -------------------------------------------------------
        function tPts = GetTpts(obj, k)
            tPts = [];
            if isempty(obj.data)
                return;
            end
            if ~exist('k','var')
                k = 1:size(obj.data,1);
            end
            tPts = obj.data(k,1);
        end

        % ----------------------------------------------------------------------------------
        function SetDuration(obj, duration, tPts)
            if isempty(obj.data)
                return;
            end
            if ~exist('duration','var')
                duration = 5;
            end
            if ~exist('tPts','var')
                tPts = obj.data(:,1);
            end
            k=[];
            for ii=1:length(tPts)
                k(ii) = find( abs(obj.data(:,1)-tPts(ii)) < obj.errmargin );
                if isempty(k)
                    continue;
                end
            end
            obj.data(k,2) = duration;
        end

        % -------------------------------------------------------
        function duration = GetDuration(obj, tPts)
            duration = [];
            if isempty(obj.data)
                return;
            end
            if ~exist('tPts','var')
                tPts = obj.data(:,1);
            end
            k = [];
            for ii=1:length(tPts)
                k(ii) = find( abs(obj.data(:,1)-tPts(ii)) < obj.errmargin );
            end
            duration = obj.data(k,2);
        end

        % ----------------------------------------------------------------------------------
        function SetValues(obj, vals, tPts)
            if isempty(obj.data)
                return;
            end
            if ~exist('vals','var')
                vals = 1;
            end
            if ~exist('tPts','var')
                tPts = obj.data(:,1);
            end
            k=[];
            for ii=1:length(tPts)
                k(ii) = find( abs(obj.data(:,1)-tPts(ii)) < obj.errmargin );
                if isempty(k)
                    continue;
                end
            end
            obj.data(k,3) = vals;
        end

        % -------------------------------------------------------
        function vals = GetValues(obj, tPts)
            vals = [];
            if isempty(obj.data)
                return;
            end
            if ~exist('tPts','var')
                tPts = obj.data(:,1);
            end
            k = [];
            for ii=1:length(tPts)
                k(ii) = find( abs(obj.data(:,1)-tPts(ii)) < obj.errmargin );
            end
            vals = obj.data(k,3);
        end

        % ----------------------------------------------------------------------------------
        function DeleteStims(obj, tPts)
            if isempty(obj.data)
                return;
            end

            % Find all stims for any conditions which match the time points and
            % delete them from data.
            k = [];
            for ii=1:length(tPts)
                k = [k, find( abs(obj.data(:,1)-tPts(ii)) < obj.errmargin )];
            end
            obj.data(k,:) = [];
        end

        % ----------------------------------------------------------------------------------
        function ToggleStims(obj, tPts)
            if isempty(obj.data)
                return;
            end

            % Find all stims for any conditions which match the time points and
            % flip it's value.
            k = [];
            for ii=1:length(tPts)
                k = [k, find( abs(obj.data(:,1)-tPts(ii)) < obj.errmargin )];
            end
            obj.data(k,3) = -1*obj.data(k,3);
        end

        % ----------------------------------------------------------------------------------
        function b = IsEmpty(obj)
            b = true;
            if isempty(obj)
                return;
            end
            if isempty(obj.name)
                return;
            end
            if isempty(obj.data)
                return;
            end
            if all(obj.data(:)==0)
                return;
            end
            b = false;
        end

        % ----------------------------------------------------------------------------------
        function nbytes = MemoryRequired(obj)
            nbytes = 0;
            if isempty(obj)
                return
            end
            nbytes = sizeof(obj.name) + sizeof(obj.data) + sizeof(obj.filename) + sizeof(obj.fileformat) + sizeof(obj.supportedFomats) + 8;
        end

    end

end


function [t]=vec2strIntDouble(varargin)

%%
switch nargin
    case 1
        n=varargin{1};
        formatDouble='%6.7e';
        rowWrapLength=[];
    case 2
        n=varargin{1};
        formatDouble=varargin{2};
        rowWrapLength=[];
    case 3
        n=varargin{1};
        formatDouble=varargin{2};
        rowWrapLength=varargin{3};
end

%%

optionStruct.formatDouble=formatDouble;
optionStruct.rowWrapLength=rowWrapLength;
[t]=mat2strIntDouble(n,optionStruct);

%%
% _*GIBBON footer text*_
%
% License: <https://github.com/gibbonCode/GIBBON/blob/master/LICENSE>
%
% GIBBON: The Geometry and Image-based Bioengineering add-On. A toolbox for
% image segmentation, image-based modeling, meshing, and finite element
% analysis.
%
% Copyright (C) 2006-2022 Kevin Mattheus Moerman and the GIBBON contributors
%
% This program is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program.  If not, see <http://www.gnu.org/licenses/>.