function [pairwiseInteractions, pairwiseSolutions] = simulatePairwiseInteractions(pairwiseModelFolder, varargin)
% This function predicts the outcome of pairwise simulations in every
% combination from a given list of pairwise models. The pairwise models
% need to be created first with the function joinModelsPairwiseFromList.
% This script requires the COBRA Toolbox function solveCobraLP. Due to the
% coupling constraints on the pairwise models, the simulations cannot
% currently be run with optimizeCbModel.
%
% Below is a description of all possible consequences for the two joined
% organisms. Please note that the outcomes depend on the two genome-scale
% reconstructions joined and are highly dependent on the applied
% constraints.
%
% * Competition: both organisms grow slower in co-growth than separately
%   (same outcome for both).
%
% * Parasitism: one organism grows faster in co-growth than separately, while
%   the other grows slower in co-growth than separately. Outcome for
%   faster-growing organism: Parasitism_Taker, for slower-growing organism:
%   Parasitism_Giver
%
% * Amensalism: one organism's growth is unaffected by co-growth,  while
%   the other grows slower in co-growth than separately. Outcome for
%   unaffected organism: Amensalism_Unaffected, for slower-growing organism:
%   Amensalism_Affected
%
% * Neutralism: both organisms' growths are unaffected by co-growth (same
%   outcome for both)
%
% * Commensalism: one organism's growth is unaffected by co-growth,  while
%   the other grows fatser in co-growth than separately. Outcome for
%   unaffected organism: Commensalism_Giver, for slower-growing organism:
%   Commensalism_Taker
%
% * Mutualism: both organisms growth faster in co-growth than separately
%   (same outcome for both)
%
% Please note: the function takes the name of the folder containing
% previously created pairwise models as the input, e.g.:
% pairwiseInteractions, pairwiseSolutions] =
% simulatePairwiseInteractions('/Users/almut.heinken/Documents/PairwiseModels');
%
% USAGE:
%     [pairwiseInteractions, pairwiseSolutions] = simulatePairwiseInteractions(pairwiseModelFolder, varargin)
%
% INPUTS:
%     pairwiseModelFolder:     Folder where pairwise models and pairwise model info file are located
%
% OPTIONAL INPUTS:
%     inputDiet:               Cell array of strings with three columns containing
%                              exchange reaction model abbreviations, lower bounds,
%                              and upper bounds.
%                              If no diet is input then the input pairwise models
%                              will be used with unchanged constraints.
%     saveSolutionsFlag:       If true, flux solutions are stored (may result in
%                              large output files)
%     numWorkers:              Number of workers in parallel pool if desired
%     sigD:                    Difference in growth rate that counts as significant
%                              change (by default: 10%)
%
% OUTPUTS:
%     pairwiseInteractions:    Table with computed pairwise and single growth
%                              rates for all entered microbe-microbe models
%
% OPTIONAL OUTPUT:
%     pairwiseSolutions:       Table with all computed flux solutions saved
%                              (may result in large output files)
%
% .. Authors:
%       - Almut Heinken, 02/2018
%       - Almut Heinken, 02/2020: Inputs changed for more efficient computation

parser = inputParser();  % Parse input parameters
parser.addRequired('pairwiseModelFolder', @ischar);
parser.addParameter('inputDiet', {}, @iscell)
parser.addParameter('saveSolutionsFlag', false, @(x) isnumeric(x) || islogical(x))
parser.addParameter('numWorkers', 0, @(x) isnumeric(x))
parser.addParameter('sigD', 0.1, @(x) isnumeric(x))

parser.parse(pairwiseModelFolder, varargin{:});

pairwiseModelFolder = parser.Results.pairwiseModelFolder;
inputDiet = parser.Results.inputDiet;
saveSolutionsFlag = parser.Results.saveSolutionsFlag;
numWorkers = parser.Results.numWorkers;
sigD = parser.Results.sigD;

%% initialize COBRA Toolbox and parallel pool
global CBT_LP_SOLVER
if isempty(CBT_LP_SOLVER)
    initCobraToolbox
end
solver = CBT_LP_SOLVER;

if numWorkers > 0
    % with parallelization
    poolobj = gcp('nocreate');
    if isempty(poolobj)
        parpool(numWorkers)
    end
end
environment = getEnvironment();

pairwiseSolutions = {};
if saveSolutionsFlag == true
    pairwiseSolutions{1, 1} = 'pairedModelID';
    pairwiseSolutions{1, 2} = 'PairwiseSolution';
    pairwiseSolutions{1, 3} = 'SingleModel1Solution';
    pairwiseSolutions{1, 4} = 'SingleModel2Solution';
end

pairwiseInteractions{1, 1} = 'pairedModelID';
pairwiseInteractions{1, 2} = 'ModelID1';
pairwiseInteractions{1, 3} = 'ModelID2';
pairwiseInteractions{1, 4} = 'pairedGrowth_Model1';
pairwiseInteractions{1, 5} = 'pairedGrowth_Model2';
pairwiseInteractions{1, 6} = 'singleGrowth_Model1';
pairwiseInteractions{1, 7} = 'singleGrowth_Model2';
pairwiseInteractions{1, 8} = 'Outcome_Model1';
pairwiseInteractions{1, 9} = 'Outcome_Model2';
pairwiseInteractions{1, 10} = 'Total_Outcome';

growthRates={};
solutionsTmp={};

if isfile([pairwiseModelFolder filesep 'pairedModelInfo.mat'])
load([pairwiseModelFolder filesep 'pairedModelInfo.mat'],'pairedModelInfo')
else
    error('Pairwise models have not been created. Please run joinModelsPairwiseFromList first.')
end

info=pairedModelInfo;
parfor i = 1:size(info, 1)
    restoreEnvironment(environment);
    changeCobraSolver(solver, 'LP', 0, -1);
    changeCobraSolverParams('LP', 'logFile', 0);

    % load the model
    filename=[pairwiseModelFolder filesep info{i,1}];
    pairedModel=readCbModel(filename);
    pairedModelOrg=pairedModel;
    % if a diet was input
    if ~isempty(inputDiet)
        pairedModel = useDiet(pairedModel, inputDiet);
    end
    % for each paired model, set both biomass objective functions as
    % objectives
    biomass1 = strcat(info{i, 2}, '_', info{i, 3});
    biomass2 = strcat(info{i, 4}, '_', info{i, 5});
    model1biomass = find(ismember(pairedModel.rxns, biomass1));
    pairedModel.c(model1biomass, 1) = 1;
    model2biomass = find(ismember(pairedModel.rxns, biomass2));
    pairedModel.c(model2biomass, 1) = 1;
    % enforce minimal growth
    pairedModel = changeRxnBounds(pairedModel, biomass1, 0.001, 'l');
    pairedModel = changeRxnBounds(pairedModel, biomass2, 0.001, 'l');
    % calculate joint biomass
    solutionPaired = solveCobraLP(buildLPproblemFromModel(pairedModel,false));
    % separate growth
    % silence model 2 and optimize model 1
    % load the model again to avoid errors
    pairedModel=pairedModelOrg;
    % if a diet was input
    if ~isempty(inputDiet)
        pairedModel = useDiet(pairedModel, inputDiet);
    end
    pairedModel = changeObjective(pairedModel, biomass1);
    % disable flux through the second model
    pairedModel = changeRxnBounds(pairedModel, pairedModel.rxns(strmatch(strcat(info{i, 4}, '_'), pairedModel.rxns)), 0, 'b');
    % calculate single biomass
    solutionSingle1 = solveCobraLP(buildLPproblemFromModel(pairedModel,false));
    % silence model 1 and optimize model 2
    % load the model again to avoid errors
    pairedModel=pairedModelOrg;
    % if a diet was input
    if ~isempty(inputDiet)
        pairedModel = useDiet(pairedModel, inputDiet);
    end
    pairedModel = changeObjective(pairedModel, biomass2);
    % disable flux through the first model
    pairedModel = changeRxnBounds(pairedModel, pairedModel.rxns(strmatch(strcat(info{i, 2}, '_'), pairedModel.rxns)), 0, 'b');
    % calculate single biomass
    solutionSingle2 = solveCobraLP(buildLPproblemFromModel(pairedModel,false));

    % save results temporarily
    if solutionPaired.stat==1
        growthRates{i}{1,1} = solutionPaired.full(model1biomass);
        growthRates{i}{1,2} = solutionPaired.full(model2biomass);
    else
        growthRates{i}{1,1} = 0;
        growthRates{i}{1,2} = 0;
    end
    if solutionSingle1.stat==1
        growthRates{i}{1,3} = solutionSingle1.full(model1biomass);
    else
        growthRates{i}{1,3} = 0;
    end
    if solutionSingle2.stat==1
        growthRates{i}{1,4} = solutionSingle2.full(model2biomass);
    else
        growthRates{i}{1,4} = 0;
    end

    solutionsTmp{i}{1,1} = solutionPaired;
    solutionsTmp{i}{1,2} = solutionSingle1;
    solutionsTmp{i}{1,3} = solutionSingle2;
end

% backup results for large number of simulations
if size(pairedModelInfo, 1) > 1000
save('growthRates','growthRates','-v7.3');
save('solutionsTmp','solutionsTmp','-v7.3');
end

for i = 1:size(pairedModelInfo, 1)
    solPaired = solutionsTmp{i}{1,1};
    solSingle1 = solutionsTmp{i}{1,2};
    solSingle2 = solutionsTmp{i}{1,3};

    grPaired1 = growthRates{i}{1,1};
    grPaired2 = growthRates{i}{1,2};
    grSingle1 = growthRates{i}{1,3};
    grSingle2 = growthRates{i}{1,4};

    % interpret computed growth rates-only if solutions are feasible
    if solPaired.stat ~= 0 && solSingle1.stat ~= 0 && solSingle2.stat ~= 0
        [iAFirstModel, iASecondModel, iATotal] = analyzePairwiseInteractions(grPaired1, grPaired2, grSingle1, grSingle2, sigD);
    end
    %% list all results
    % fill out the results table with the model information
    pairwiseInteractions{i + 1, 1} = pairedModelInfo{i, 1};
    pairwiseInteractions{i + 1, 2} = pairedModelInfo{i, 2};
    pairwiseInteractions{i + 1, 3} = pairedModelInfo{i, 4};
    % combined growth
    pairwiseInteractions{i + 1, 4} = grPaired1;
    pairwiseInteractions{i + 1, 5} = grPaired2;
    pairwiseInteractions{i + 1, 6} = grSingle1;
    pairwiseInteractions{i + 1, 7} = grSingle2;
    % make sure that infeasible solutions are flagged
    if solPaired.stat == 0 || solSingle1.stat == 0 || solSingle2.stat == 0
        pairwiseInteractions{i + 1, 8} = 'Infeasible';
        pairwiseInteractions{i + 1, 9} = 'Infeasible';
        pairwiseInteractions{i + 1, 10} = 'Infeasible';
    else
        % save the results
        pairwiseInteractions{i + 1, 8} = iAFirstModel;
        pairwiseInteractions{i + 1, 9} = iASecondModel;
        pairwiseInteractions{i + 1, 10} = iATotal;
    end
    %% store the solutions if storeSolutionFlag==true
    if saveSolutionsFlag == true
        pairwiseSolutions{i + 1, 1} = pairedModelInfo{i, 1};
        pairwiseSolutions{i + 1, 2} = solPaired.full;
        pairwiseSolutions{i + 1, 3} = solSingle1.full;
        pairwiseSolutions{i + 1, 4} = solSingle2.full;
    end
end

end

function [iAFirstOrg, iASecondOrg, iATotal] = analyzePairwiseInteractions(pairedGrowthOrg1, pairedGrowthOrg2, singleGrowthOrg1, singleGrowthOrg2, sigD)
% This function evaluates the outcome of pairwise growth of two organisms
% compared with the two organisms separately. There are six possible
% outcomes of the interaction, and nine possible outcomes from the
% perspective of each organism.

if abs(1 - (pairedGrowthOrg1 / singleGrowthOrg1)) < sigD
    % first microbe unaffected - all possible cases resulting froms
    % second microbe's growth
    if abs(1 - (pairedGrowthOrg2 / singleGrowthOrg2)) < sigD
        % second microbe unaffected
        iAFirstOrg = 'Neutralism';
        iASecondOrg = 'Neutralism';
        iATotal = 'Neutralism';
    elseif abs((pairedGrowthOrg2 / singleGrowthOrg2)) > 1 + sigD
        % second microbe grows better
        iAFirstOrg = 'Commensalism_Giver';
        iASecondOrg = 'Commensalism_Taker';
        iATotal = 'Commensalism';
    elseif abs((singleGrowthOrg2 / pairedGrowthOrg2)) > 1 + sigD
        % second microbe grows slower
        iAFirstOrg = 'Amensalism_Unaffected';
        iASecondOrg = 'Amensalism_Affected';
        iATotal = 'Amensalism';
    else
        % if no case fits - needs inspection!
        iAFirstOrg = 'No_Result';
        iASecondOrg = 'No_Result';
        iATotal = 'No_Result';
    end
elseif abs((pairedGrowthOrg1 / singleGrowthOrg1)) > 1 + sigD
    % first microbe grows better - all possible cases resulting froms
    % second microbe's growth
    if abs(1 - (pairedGrowthOrg2 / singleGrowthOrg2)) < sigD
        % second microbe unaffected
        iAFirstOrg = 'Commensalism_Taker';
        iASecondOrg = 'Commensalism_Giver';
        iATotal = 'Commensalism';
    elseif abs((pairedGrowthOrg2 / singleGrowthOrg2)) > 1 + sigD
        % second microbe grows better
        iAFirstOrg = 'Mutualism';
        iASecondOrg = 'Mutualism';
        iATotal = 'Mutualism';
    elseif abs((singleGrowthOrg2 / pairedGrowthOrg2)) > 1 + sigD
        % second microbe grows slower
        iAFirstOrg = 'Parasitism_Taker';
        iASecondOrg = 'Parasitism_Giver';
        iATotal = 'Parasitism';
    else
        % if no case fits - needs inspection!
        iAFirstOrg = 'No_Result';
        iASecondOrg = 'No_Result';
        iATotal = 'No_Result';
    end
elseif abs((singleGrowthOrg1 / pairedGrowthOrg1)) > 1 + sigD
    % first microbe grows slower - all possible cases resulting froms
    % second microbe's growth
    if abs(1 - (pairedGrowthOrg2 / singleGrowthOrg2)) < sigD
        % second microbe unaffected
        iAFirstOrg = 'Amensalism_Affected';
        iASecondOrg = 'Amensalism_Unaffected';
        iATotal = 'Amensalism';
    elseif abs((pairedGrowthOrg2 / singleGrowthOrg2)) > 1 + sigD
        % second microbe grows better
        iAFirstOrg = 'Parasitism_Giver';
        iASecondOrg = 'Parasitism_Taker';
        iATotal = 'Parasitism';
    elseif abs((singleGrowthOrg2 / pairedGrowthOrg2)) > 1 + sigD
        % second microbe grows slower
        iAFirstOrg = 'Competition';
        iASecondOrg = 'Competition';
        iATotal = 'Competition';
    else
        % if no case fits - needs inspection!
        iAFirstOrg = 'No_Result';
        iASecondOrg = 'No_Result';
        iATotal = 'No_Result';
    end
else
    % if no case fits - needs inspection!
    iAFirstOrg = 'No_Result';
    iASecondOrg = 'No_Result';
    iATotal = 'No_Result';
end
end


function test_ft_sourcegrandaverage

% WALLTIME 00:10:00
% MEM 1gb
% DEPENDENCY ft_sourcegrandaverage ft_selectdata ft_selectdata_new ft_datatype_source

%% format 1

source = [];
source.dim = [10 11 12];
source.transform = eye(4);
source.avg.pow = rand(10*11*12,1);
source.inside = 1:660;
source.outside = 661:1320;

ft_checkdata(source, 'datatype', 'source');

cfg = [];
cfg.parameter = 'pow';
cfg.keepindividual = 'no';
grandavg = ft_sourcegrandaverage(cfg, source, source);

cfg.keepindividual = 'yes';
grandavg = ft_sourcegrandaverage(cfg, source, source);

%% format 2

source = [];
source.transform = eye(4);
source.pos = rand(1320,3);
source.pow = rand(1320,1);
source.inside = 1:660;
source.outside = 661:1320;

ft_checkdata(source, 'datatype', 'source');

cfg = [];
cfg.parameter = 'pow';
cfg.keepindividual = 'no';
grandavg = ft_sourcegrandaverage(cfg, source, source);

cfg.keepindividual = 'yes';
grandavg = ft_sourcegrandaverage(cfg, source, source);

%% format 3

source = [];
source.dim = [10 11 12];
source.transform = eye(4);
source.pow = rand(10*11*12,1);
source.inside = 1:660;
source.outside = 661:1320;

ft_checkdata(source, 'datatype', 'source');

cfg = [];
cfg.parameter = 'pow';
cfg.keepindividual = 'no';
grandavg = ft_sourcegrandaverage(cfg, source, source);

cfg.keepindividual = 'yes';
grandavg = ft_sourcegrandaverage(cfg, source, source);

%% format 4

source = [];
source.pos = rand(1320,3);
source.time = 1:25;
source.avg.pow = rand(10*11*12,25);
source.inside = 1:660;
source.outside = 661:1320;

ft_checkdata(source, 'datatype', 'source');

cfg = [];
cfg.parameter = 'pow';
cfg.keepindividual = 'no';
grandavg = ft_sourcegrandaverage(cfg, source, source);
assert(isfield(grandavg, 'time'), 'time field is missing');

cfg.keepindividual = 'yes';
grandavg = ft_sourcegrandaverage(cfg, source, source);
assert(isfield(grandavg, 'time'), 'time field is missing');

%% format 5

source = [];
source.pos = rand(1320,3);
source.freq = 1:6;
source.time = 1:5;
source.avg.pow = rand(10*11*12,6,5);
source.inside = 1:660;
source.outside = 661:1320;

ft_checkdata(source, 'datatype', 'source');

cfg = [];
cfg.parameter = 'pow';
cfg.keepindividual = 'no';
grandavg = ft_sourcegrandaverage(cfg, source, source);
assert(isfield(grandavg, 'freq'), 'freq field is missing');
assert(isfield(grandavg, 'time'), 'time field is missing');

cfg.keepindividual = 'yes';
grandavg = ft_sourcegrandaverage(cfg, source, source);
assert(isfield(grandavg, 'freq'), 'freq field is missing');
assert(isfield(grandavg, 'time'), 'time field is missing');

%% format 6

source = [];
source.pos = rand(1320,3);
source.time = 1:5;
source.inside = 1:660;
source.outside = 661:1320;
source.avg.pow = cell(1320,1);
for i=source.inside
  source.avg.pow{i} = randn(3,5);
end
for i=source.outside
  source.avg.pow{i} = [];
end

ft_checkdata(source, 'datatype', 'source');

cfg = [];
cfg.parameter = 'pow';
cfg.keepindividual = 'no';
grandavg = ft_sourcegrandaverage(cfg, source, source);
assert(isfield(grandavg, 'time'), 'time field is missing');

cfg.keepindividual = 'yes';
grandavg = ft_sourcegrandaverage(cfg, source, source);
assert(isfield(grandavg, 'time'), 'time field is missing');

%% format 7

source = [];
source.pos = rand(1320,3);
source.freq = 1:6;
source.time = 1:5;
source.inside = 1:660;
source.outside = 661:1320;
source.avg.pow = cell(1320,1);
for i=source.inside
  source.avg.pow{i} = randn(3,6,5);
end
for i=source.outside
  source.avg.pow{i} = [];
end

ft_checkdata(source, 'datatype', 'source');

cfg = [];
cfg.parameter = 'pow';
cfg.keepindividual = 'no';
grandavg = ft_sourcegrandaverage(cfg, source, source);
assert(isfield(grandavg, 'freq'), 'freq field is missing');
assert(isfield(grandavg, 'time'), 'time field is missing');

cfg.keepindividual = 'yes';
grandavg = ft_sourcegrandaverage(cfg, source, source);
assert(isfield(grandavg, 'freq'), 'freq field is missing');
assert(isfield(grandavg, 'time'), 'time field is missing');


function objectiveAbbr = printObjective(model)
% Prints out the Stoichiometric Coefficients for each
% Metabolite, with the name of the objective
%
% USAGE:
%
%    objectiveAbbr = printObjective(model)
%
% INPUT:
%    model:            COBRA model structure
%
% OUTPUT:
%    objectiveAbbr:    Objective reaction abbreviation
%
% .. Authors:
%       - Ronan Fleming 22/10/2008
%       - Thomas Pfau 15/12/2015 - Made the function compatible with sparse S matrices
%       - Laurent Heirendt March 2017 - Compatibility with large models and conversion to table

objRxnInd = find(model.c ~= 0);
objectiveAbbr = model.rxns(objRxnInd);
T = cell(length(objRxnInd), 1);

if isempty(objRxnInd)
    warning('There is no objective!');
else
    objMetVect = {};
    objRxnVect = {};
    objCoeffVect = {};
    for k = 1:length(objRxnInd)
        objMetInd = find(model.S(:, objRxnInd(k)));
        objMetVect{k} = model.mets(objMetInd);
        rxnName = model.rxns(objRxnInd(k));
        objCoeffVect{k} = full(model.S(objMetInd, objRxnInd(k)));

        objRxnVect = {};
        objRxnIDVect = {};
        % fill the list with the reaction name and ID
        for m = 1:length(objMetInd)
            objRxnVect{m} = char(rxnName);
            objRxnIDVect{m} = objRxnInd(k);
        end

        % save the table for reaction k
        T{k} = table(objCoeffVect{k}, categorical(objMetVect{k}), objMetInd, categorical(objRxnVect'), cell2mat(objRxnIDVect'), ...
                     'VariableNames', {'Coefficient', 'Metabolite', 'metID', 'Reaction', 'RxnID'});

        % concatenate the tables
        if k == 1
            summaryT = T{1};
        else
            summaryT = vertcat(summaryT, T{k});
        end
    end

    % display a summary
    display(summaryT);
end


function det = load_det(varargin)
% Load detection results of different local/pairwise models

%% parse parameters
opts = struct;
% model type
opts.fix_ann.x_off = 0;
opts.fix_ann.y_off = 0;
opts.fix_ann.w = inf;
opts.fix_ann.h = inf;

opts.regression = struct;               % detection regression parameters
opts.regression.param = [0 0 1 1];
opts.regression.fix_ann = struct;       % fix annotation if needed
opts.regression.fix_ann.x_off = 0;
opts.regression.fix_ann.y_off = 0;
opts.regression.fix_ann.w = inf;
opts.regression.fix_ann.h = inf;
opts.regression.warp = 'none';

opts.nms = struct;
opts.nms.nmsIntersectionOverAreaThreshold = 0.3;
opts.nms.numBoundingBoxMax = inf;

opts.regression_before_nms = false;     % do regression before nms

opts.det = struct;
opts.det.modeltype = 'local';
opts.det.thres = -inf;
opts.det.path_format = '';
opts.det.scoretype = 'raw';
opts.det.as_filter = false;             % remove *weird* aspect ratio
opts.det.as_ratio = 1.5;

opts.im_path_format = '';
opts.im_set = [];

opts.viz = struct;                      % visulization
opts.viz.doviz = false;
opts.viz.max_det = 5;

opts.verbose = true;
opts.progress_part = 5;

opts = vl_argparse(opts, varargin);

det = struct;
numimage = length(opts.im_set);

fprintf('Loading detections');
progress_part_num = ceil(numimage/opts.progress_part);

for i = 1:numimage
    if (opts.verbose)
        if (i==numimage)
            fprintf('...100%%\n');
        else
            if ~mod(i,progress_part_num)
                fprintf('...%d%%', i*100/(progress_part_num*opts.progress_part));
            end
        end
    end

    if (~iscell(opts.im_set))
        idname = opts.im_set(i);
        det_path = sprintf(opts.det.path_format, idname);
        im_path = sprintf(opts.im_path_format , idname);

    else
        idname = opts.im_set{i};
        det_path = sprintf(opts.det.path_format, opts.im_set{i});
        im_path = sprintf(opts.im_path_format , opts.im_set{i});
    end
    det(i).path = det_path;
    det(i).impath = im_path;
    det(i).id = idname;

    im = imread(im_path);
    [img_h, img_w, ~] = size(im);

    BB = load_BB(opts.det.modeltype, det_path);
    if (isempty(BB))
        det(i).bb = BB;
        continue;
    end

%     if (strcmp(detector,'globalmasked')~=0)
%         scale_factor = 224/size(im,2);
%         pad_size = floor((size(im,2)-size(im,1))/2);
%         BB_unpad = BB/scale_factor;
%         BB_unpad(:, 2) = BB_unpad(:, 2) - pad_size;
%         BB = BB_unpad;
%     end

    %thresholding lowscore detections
    BB = BB(BB(:,5)>opts.det.thres, :);
    if (isempty(BB)) det(i).bb = BB; continue; end

    %remove *weird* aspect ratio if needed
    if (opts.det.as_filter)
        w = BB(:,3);
        h = BB(:,4);
        as = w./h;
        BB = BB(as < opts.det.as_ratio && as > 1/opts.det.as_ratio, :);
    end
    if (isempty(BB)) det(i).bb = BB; continue; end

    if (~opts.regression_before_nms)
        top = selectBoundingBoxesNonMaxSup(BB(:,1:4), BB(:,5), opts.nms);
        BB = BB(top, :);
        BB = do_regression(BB, img_w, img_h, opts.regression);
    else
        BB = do_regression(BB, img_w, img_h, opts.regression);
        top = selectBoundingBoxesNonMaxSup(BB(:,1:4), BB(:,5), opts.nms);
        BB = BB(top, :);
    end

    if (opts.viz.doviz)
        fig = figure;
        imshow(im); hold on;
        for z=1:min(opts.viz.max_det, size(BB,1))
            rectangle('position', BB(z, 1:4), 'edgecolor', 'r');
            text(double(BB(z, 1))+5, double(BB(z, 2))+5, num2str(BB(z, 5)), 'color', 'yellow');
        end
        disp('Press any key to continue...');
        pause;
        close(fig);
    end

    det(i).bb = BB;
end
end


%  doinvers
% This file calculates orintation of the stress tensor
% based on Gephard's algorithm.
% stress tensor orientation. The actual calculation is done
% using a call to a fortran program.
%
% Stefan Wiemer 03/96

global mi mif1 mif2  hndl3 a newcat2 mi2
global tmp cumu2
report_this_filefun(mfilename('fullpath'));
think

% select the gridpoints

% do the loop

i2 = 0;
clear howmany
for i= 1:length(newgri(:,1))
    x = newgri(i,1);y = newgri(i,2);
    allcount = allcount + 1.;
    i2 = i2+1;

    % calculate distance from center point and sort wrt distance
    l = sqrt(((xsecx' - x)).^2 + ((xsecy + y)).^2) ;
    [s,is] = sort(l);
    b = newa(is(:,1),:) ;       % re-orders matrix to agree row-wise
    b = b(1:ni,:);

    tmp = [b(:,10:14)];
    comm = ['!mkdir /home/david/tmp/in' num2str(i2)]
    eval(comm)
    comm = ['save /home/david/tmp/in' num2str(i2) '/data.inp tmp -ascii']
    eval(comm)
    comm = ['save /home/david/tmp/in' num2str(i2) '/loc.inp  x y  -ascii']
    eval(comm)
end
save tmpinv.mat

com =['gps        '
    'ugle       '
    'dutton     '
    'kiska      '
    'moment     '
    %'nordic     '
    'kanaga     '
    'spurr      '
    'chaos      '
    'megathrust '
    %'pele       '
    %'geoid      '
    'model      '
    'marvin     '];

cd /home/stefan/ZMAP/invers
i = 1;

while i <  length(newgri(:,1))

    for k = 1:length(com(:,1))

        comm = ['!/bin/rm /home/stefan/ZMAP/invers/howmany.dat '];
        eval(comm)
        comm = ['!rsh ' com(k,:) ' ps -axuw | grep fmsiWindow | cut -c10-15 ',...
            ' > /home/stefan/ZMAP/invers/howmany.dat '];
        eval(comm)
        pause(3)
        comm =['load /home/stefan/ZMAP/invers/howmany.dat '];
        eval(comm,'disp('' no'')')

        if ~exist('howmany','var'); howmany = 0; end
        if com(k,:) == 'moment     ' ; howmany(1:2) = [] ; end
        if length(howmany(:,1)) < 2
            comm = ['! rsh ' com(k,:) ' /home/stefan/ZMAP/invers/invshell ',...
                num2str(length(tmp(:,1))) ' ' num2str(i)  ' &']
            eval(comm)
            i = i+1
            clear howmany
            pause(3)
        end   % if  length
        clear howmany
    end   % for k

    % wait for 1 minutes
    pause(10)

end  % while i


function [ readerobj ] = subset_reader(ro, dim1_limits, dim2_limits)
%SUBSET_READER Creates a reader with visibility into only part of a file
%
% Written by: Wade Schwartzkopf, NGA Research
%
% //////////////////////////////////////////
% /// CLASSIFICATION: UNCLASSIFIED       ///
% //////////////////////////////////////////

%% Define object methods
readerobj = ro; % Copy most methods
readerobj.read_chip = @chip_fun; % Subset chipper
% Subset metadata
meta = readerobj.get_meta();
meta.ImageData.NumCols = diff(dim1_limits) + 1;
meta.ImageData.NumRows = diff(dim2_limits) + 1;
if isfield(meta.ImageData,'FirstCol')
    meta.ImageData.FirstCol = meta.ImageData.FirstCol + dim1_limits(1) - 1;
end
if isfield(meta.ImageData,'FirstRow')
    meta.ImageData.FirstRow = meta.ImageData.FirstRow + dim2_limits(1) - 1;
end
readerobj.get_meta = @() meta;

    % Check to see if indices are out of bounds of subset area and index
    % based on subset, not full image.
    function out = chip_fun(varargin)
        [ dim1range, dim2range, subsample ] =...
            check_chipper_args( [diff(dim1_limits) diff(dim2_limits)] + 1, varargin{:});
        out = ro.read_chip(dim1range + double(dim1_limits(1)) - 1, ...
            dim2range + double(dim2_limits(1)) - 1, subsample, varargin{4:end});
    end

end

% //////////////////////////////////////////
% /// CLASSIFICATION: UNCLASSIFIED       ///
% //////////////////////////////////////////


% performs argument checks/transformations similar to those found in
% gp.m from GPML, but Hessian friendly

% Copyright (c) 2014--2015 Roman Garnett.

function gpstruct = gpstruct_check(gpstruct, x)

% Default gp values
defaultstruct = struct( ...
    'inf',{@exact_inference}, ...
    'cov', [], ...
    'lik',{@likGauss}, ...
    'mean', {@zero_mean}, ...
    'hyp', struct('cov',[],'lik',[],'mean',[]), ...
    'prior', [], ...
    'bounds', [] ...
    );

% Assign default values
for field = fields(defaultstruct)'
    if ~isfield(gpstruct,field{:}) || isempty(gpstruct.(field{:}))
        gpstruct.(field{:}) = defaultstruct.(field{:});
    end
end

% No default covariance function
if isempty(gpstruct.cov)
    error('gpstruct:missing_argument', ...
          'covariance function must be defined');
end

% Allow string/function handle input; convert to cell arrays if necessary
for field = {'inf', 'cov', 'lik', 'mean'}
    if ischar(gpstruct.(field{:})) || isa(gpstruct.(field{:}), 'function_handle')
        gpstruct.(field{:}) = {gpstruct.(field{:})};
    end
end

% Check struct of hyperparameters
if ~isfield(gpstruct,'hyp')
    error('gpstruct:missing_argument', ...
        'hyperparameter structure must be defined');
end

% If HYP is a cell array, convert to struct array
if iscell(gpstruct.hyp) && isstruct(gpstruct.hyp{1})
    hyptemp = struct('cov','lik','mean');
    for i = 1:length(gpstruct.hyp)
        hyptemp(i) = gpstruct.hyp{i};
    end
    gpstruct.hyp = hyptemp;
end

% Ensure all hyperparameter fields exist
for field = {'cov', 'lik', 'mean'}
    for i = 1:numel(gpstruct.hyp)
        if (~isfield(gpstruct.hyp(i), field{:}))
            gpstruct.hyp(i).(field{:}) = [];
        end
    end
end

% Check dimension of hyperparameter fields
D = size(x, 2);
shortfields = {'cov','lik','mean'};
longfields = {'covariance','likelihood','mean'};
for ifield = 1:numel(shortfields)
    field = shortfields{ifield};
    expression = feval(gpstruct.(field){:});
    for i = 1:numel(gpstruct.hyp)
        nparams = numel(gpstruct.hyp(i).(field));
        if (nparams ~= eval(expression))
            error('gpstruct:incorrect_specification', ...
                  'wrong number of %s hyperparameters (%i given, %s expected)', ...
                  longfields{ifield}, ...
                  nparams, ...
                  expression);
        end
    end
end

% Check prior and bounds
for field = {'cov', 'lik', 'mean'}
    % Ensure all prior fields exist
    if (~isfield(gpstruct.prior, field{:}))
        gpstruct.prior.(field{:}) = [];
    end
    % Ensure all bounds fields exist
    if (~isfield(gpstruct.bounds, field{:}))
        gpstruct.bounds.(field{:}) = [];
    end
    % Assign [-Inf;Inf] bounds by default
    num_entries = numel(gpstruct.hyp(1).(field{:}));
    for i = 1:num_entries
        if numel(gpstruct.prior.(field{:})) < i
            gpstruct.prior.(field{:}){i} = [];
        end
        if numel(gpstruct.bounds.(field{:})) < i || ...
                isempty(gpstruct.bounds.(field{:}){i})
            gpstruct.bounds.(field{:}){i} = [-Inf,Inf];
        end
    end
end


function [params, names] = cmpndKernExtractParam(kern)

% CMPNDKERNEXTRACTPARAM Extract parameters from the CMPND kernel structure.
% FORMAT
% DESC Extract parameters from the compound kernel matrix into a vector of
% parameters for optimisation.
% ARG kern : the kernel structure containing the parameters to be
% extracted.
% RETURN param : vector of parameters extracted from the
% kernel. The vector of 'transforms' is assumed to be empty
% here. Any transformations of parameters should be done in
% component kernels.
%
% SEEALSO cmpndKernParamInit, cmpndKernExpandParam, kernExtractParam, scg, conjgrad
%
% COPYRIGHT : Neil D. Lawrence, 2004, 2005, 2006

% KERN

params = zeros(1, kern.nParams);
if nargout > 1
  namesTemp = cell(1, kern.nParams);
end
startVal = 1;
endVal = 0;
storedTypes = cell(0);
for i = 1:length(kern.comp)
  endVal = endVal + kern.comp{i}.nParams;
  if nargout > 1
    [params(1, startVal:endVal), namesTemp(startVal:endVal)] = kernExtractParam(kern.comp{i});
    instNum = sum(strcmp(kern.comp{i}.type, storedTypes)) + 1;
    for j = startVal:endVal
      namesTemp{1, j} = [kern.comp{i}.type ' ' num2str(instNum) ' ' ...
                         namesTemp{1, j}];
    end
    storedTypes{end+1} = kern.comp{i}.type;
  else
    params(1, startVal:endVal) = kernExtractParam(kern.comp{i});
  end
    startVal = endVal + 1;
end

% If any parameters are 'tied together' deal with them.
paramGroups = kern.paramGroups;
for i = 1:size(paramGroups, 2)
  ind = find(paramGroups(:, i));
  if nargout > 1
    names{i} = namesTemp{ind(1)};
    if length(ind) > 1
      for j = 2:length(ind)
        names{i} = [names{i} ', ' namesTemp{ind(j)}];
      end
    end
  end
  paramGroups(ind(2:end), i) = 0;
end
params = params*paramGroups;


% Read all images and extract point coordinates.
%
% All information needed are stored and retrieved
% from the function read_configuration,
% which in turn uses the --config=FILENAME command-line
% option.

% $Author: svoboda $
% $Revision: 2.0 $
% $Id: im2points.m,v 2.0 2003/06/19 12:07:11 svoboda Exp $
% $State: Exp $

clear all;

% add path to config data (TODO: obsolete?)
addpath ../Cfg
% add path for graphical output if needed
addpath ../OutputFunctions

SHOWFIG	  = 0; % show images during point extraction
STEP4STAT = 5; % step for computing average and std images, if 1 then all images taken

% Read configuration from whatever is specified on command-line (via --config=FILENAME)
config = read_configuration();

im.dir = config.paths.img;
im.ext = config.files.imgext;

NoCams = size(config.files.idxcams,2);	% number of cameras

% load image names
for i=1:NoCams,
  seq(i).camId = config.files.idxcams(i);
  if seq(i).camId > -1
	if strfind(expname,'oscar')
	  seq(i).data = dir([sprintf(im.dir,seq(i).camId),config.files.imnames,'*.',im.ext]);
	else
	  seq(i).data = dir([sprintf(im.dir,seq(i).camId),sprintf(config.files.imnames,seq(i).camId),im.ext]);
	end
  else
	seq(i).data = dir([im.dir,sprintf(config.files.imnames),im.ext]);
  end
  seq(i).size = size(seq(i).data,1);
  if seq(i).size<4
	error('Not enough images found. Wrong image path or name pattern?');
  end
end

% Expected number of 3D points is equal to the number of frames.
% In fact, some frames might be without any calibration point

% Becouse of non-consistent stopping of capturing, the sequences might
% have different number of images, select the minimal value as the right one
NoPoints = min([seq.size]);
pointsIdx = [1:STEP4STAT:NoPoints];

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% beginning of the findings

t = cputime;
for i=1:NoCams,
  if ~exist(sprintf(config.files.avIM,seq(i).camId)),
	disp(sprintf('The average image of the camera %d is being computed',seq(i).camId));
	avIM = zeros(size(imread([sprintf(im.dir,seq(i).camId),seq(i).data(1).name])));
	for j=pointsIdx,
	  IM = imread([sprintf(im.dir,seq(i).camId),seq(i).data(j).name]);
	  avIM = avIM + double(IM);
	end
	avIM = uint8(round(avIM./size(pointsIdx,2)));
	imwrite(avIM,sprintf(config.files.avIM,seq(i).camId));
  else	disp('Average files already exist');
  end
end
disp(sprintf('Elapsed time for average images: %d [sec]',cputime-t))
% compute the standard deviations images that will be used for finding LEDs
% if not already computed
t = cputime;
for i=1:NoCams,
  if ~exist(sprintf(config.files.stdIM,seq(i).camId)),
	avIM = double(imread(sprintf(config.files.avIM,seq(i).camId)));
	disp(sprintf('The image of standard deviations of the camera %d is being computed',seq(i).camId));
	stdIM = zeros(size(imread([sprintf(im.dir,seq(i).camId),seq(i).data(1).name])));
	for j=pointsIdx,
	  IM = imread([sprintf(im.dir,seq(i).camId),seq(i).data(j).name]);
	  stdIM = stdIM + (double(IM)-avIM).^2;
	end
	stdIM = uint8(round(sqrt(stdIM./(size(pointsIdx,2)-1))));
	imwrite(stdIM,sprintf(config.files.stdIM,seq(i).camId));
  else
	disp('Image of standard deviations already exist')
  end
end

disp(sprintf('Elapsed time for computation of images [sec]: %d',cputime-t))

% find points in the images
Ws    = [];	  % joint image matrix
Res	  = [];	  % resolution of cameras
IdMat = ones(NoCams,NoPoints);
% IdMat is very important for Martinec&Pajdla filling [ECCV2002]
% it is a NoCams x NoPoints matrix,
% IdMat(i,j) = 0 -> no j-th point in i-th
% IdMat(i,j) = 1 -> point successfully detected

disp('*********************************************')
disp('Finding points (laser projections) in cameras')
disp(sprintf('Totally %d cameras, %d images for each cam', NoCams, NoPoints'))
disp('*********************************************')
for i=1:NoCams,
  t1 = cputime;
  disp(sprintf('Finding points in camera No: %0.2d',config.files.idxcams(i)))
  Points = [];
  avIM  = imread(sprintf(config.files.avIM,seq(i).camId));
  stdIM	= imread(sprintf(config.files.stdIM,seq(i).camId));
  for j=1:NoPoints,
	[pos,err] = getpoint([sprintf(im.dir,seq(i).camId),seq(i).data(j).name], SHOWFIG, config.imgs, avIM, stdIM);
	if err
	  IdMat(i,j) = 0;
	  Points = [Points, [NaN; NaN; NaN]];
	else
	  Points = [Points, [pos; 1]];
	end
  end
  Ws = [Ws; Points];
  Res= [Res; size(avIM,2), size(avIM,1)];
  t2 = cputime;
  disp(sprintf('Elapsed time for finding points in one camera: %d minutes %d seconds',floor((t2-t1)/60), round(mod((t2-t1),60))))
end

%%% End of the findings
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

if strfind(expname,'oscar')
  % needs special care for handling projector data
  ProjPoints = load(config.files.projdata,'-ASCII');
  Ws = [Ws; ProjPoints(:,2:3)'; ones(size(ProjPoints(:,1)'))];
  IdMat = [IdMat; ones(size(ProjPoints(:,1)'))];
  Res	= [Res; config.imgs.projres];
end

save(config.files.points, 'Ws','-ASCII')
save(config.files.Res, 'Res', '-ASCII')
save(config.files.IdMat, 'IdMat', '-ASCII')

% display the overall statistics
disp('Overall statistics from im2points:  ************************  ')
disp(sprintf('Total number of frames (possible 3D points): %d',NoPoints))
disp(sprintf('Total number of cameras %d', NoCams))
disp('More important statistics: *********************************  ')
disp(sprintf('Detected 3D points:                    %d', sum(sum(IdMat)>0)))
disp(sprintf('Detected 3D points in at least 3 cams: %d', sum(sum(IdMat)>2)))
disp(sprintf('Detected 3D points in ALL cameras:     %d', sum(sum(IdMat)==NoCams)))


%
% Perform an iteration of MCMC
% for re-sampling the relations, which
% blends type-level and token-levle variables
%
function mcmc_iter_relations(MH,M,lib)

    % relations
    for sid=2:M.ns
        MH.mh_relation(sid,M,lib);
    end

    % evaluation token spots
    for sid=1:M.ns
       if strcmp(M.S{sid}.R.type,'mid')
          MH.mh_eval_spot_token(sid,M,lib);
       end
    end

end


ops.chanMap             = 'D:\GitHub\KiloSort2\configFiles\neuropixPhase3A_kilosortChanMap.mat';
% ops.chanMap = 1:ops.Nchan; % treated as linear probe if no chanMap file

% sample rate
ops.fs = 30000;

% frequency for high pass filtering (150)
ops.fshigh = 150;

% minimum firing rate on a "good" channel (0 to skip)
ops.minfr_goodchannels = 0.1;

% threshold on projections (like in Kilosort1, can be different for last pass like [10 4])
ops.Th = [10 4];

% how important is the amplitude penalty (like in Kilosort1, 0 means not used, 10 is average, 50 is a lot)
ops.lam = 10;

% splitting a cluster at the end requires at least this much isolation for each sub-cluster (max = 1)
ops.AUCsplit = 0.9;

% minimum spike rate (Hz), if a cluster falls below this for too long it gets removed
ops.minFR = 1/50;

% number of samples to average over (annealed from first to second value)
ops.momentum = [20 400];

% spatial constant in um for computing residual variance of spike
ops.sigmaMask = 30;

% threshold crossings for pre-clustering (in PCA projection space)
ops.ThPre = 8;
%% danger, changing these settings can lead to fatal errors
% options for determining PCs
ops.spkTh           = -6;      % spike threshold in standard deviations (-6)
ops.reorder         = 1;       % whether to reorder batches for drift correction.
ops.nskip           = 25;  % how many batches to skip for determining spike PCs

ops.GPU                 = 1; % has to be 1, no CPU version yet, sorry
% ops.Nfilt               = 1024; % max number of clusters
ops.nfilt_factor        = 4; % max number of clusters per good channel (even temporary ones)
ops.ntbuff              = 64;    % samples of symmetrical buffer for whitening and spike detection
ops.NT                  = 64*1024+ ops.ntbuff; % must be multiple of 32 + ntbuff. This is the batch size (try decreasing if out of memory).
ops.whiteningRange      = 32; % number of channels to use for whitening each channel
ops.nSkipCov            = 25; % compute whitening matrix from every N-th batch
ops.scaleproc           = 200;   % int16 scaling of whitened data
ops.nPCs                = 3; % how many PCs to project the spikes into
ops.useRAM              = 0; % not yet available

%%


classdef KSPTOT_BodyInfo < matlab.mixin.SetGet
    %KSPTOT_BodyInfo Summary of this class goes here
    %   Detailed explanation goes here

    properties
        %Orbit
        epoch double
        sma double
        ecc double
        inc double
        raan double
        arg double
        mean double

        %Physical
        gm double
        radius double

        %Atmo
        atmohgt double
        atmopresscurve = griddedInterpolant([0 1]',[0 0]','spline', 'nearest');
        atmotempcurve = griddedInterpolant([0 1]',[0 0]','spline', 'nearest');
        atmotempsunmultcurve = griddedInterpolant([0 1]',[0 0]','spline', 'nearest');
        lattempbiascurve = griddedInterpolant([0 1]',[0 0]','spline', 'nearest');
        lattempsunmultcurve = griddedInterpolant([0 1]',[0 0]','spline', 'nearest');
        axialtempsunbiascurve = griddedInterpolant([0 1]',[0 0]','spline', 'nearest');
        axialtempsunmultcurve = griddedInterpolant([0 1]',[0 0]','spline', 'nearest');
        ecctempbiascurve = griddedInterpolant([0 1]',[0 0]','spline', 'nearest');
        atmomolarmass double

        %Rotation
        rotperiod double
        rotini double

        %Display
        bodycolor char

        %Others
        canbecentral double
        canbearrivedepart double
        parent
        parentid double
        name char
        id double

        %body surface textures
        surftexturefile (1,:) char = '';
        surftexturezrotoffset(1,1) double = 0; %degrees

        %orientation of inertial frame (spin axis handling)
        bodyZAxis(3,1) double = [0;0;1];
        bodyXAxis(3,1) double = [1;0;0];
        bodyRotMatFromGlobalInertialToBodyInertial = [];

        %Propagation type
        proptype(1,:) char = 'analytic_two_body';
        propTypeEnum(1,1) BodyPropagationTypeEnum = BodyPropagationTypeEnum.TwoBody
        numIntStateCache CelestialBodySunRelStateDataCache
    end

    properties
        celBodyData

        parentBodyInfo KSPTOT_BodyInfo
        parentBodyInfoNeedsUpdate logical = true;

        childrenBodyInfo KSPTOT_BodyInfo
        childrenBodyNames cell
        childrenBodyInfoNeedsUpdate logical = true;
    end

    properties(Transient)
        doNotUseAtmoTempSunMultCurve(1,1) logical = false;
        doNotUseLatTempSunMultCurve(1,1) logical = false;
        doNotUseAxialTempSunMultCurve(1,1) logical = false;
        doNotUseAxialTempSunBiasCurveGI(1,1) logical = false;
        doNotUseEccTempBiasCurveGI(1,1) logical = false;
        doNotUseAtmoPressCurveGI(1,1) logical = false;

        orbitElemsChainCache cell = {};
        fixedFrameFromInertialFrameCache cell = {};

        surfTextureCache uint8 = [];

        lastComputedTime = NaN;
        lastComputedRVect = NaN(3,1);
        lastComputedVVect = NaN(3,1);

        lastComputedRVectVVect = [];

        propTypeIsTwoBody(1,1) logical = true;
        propTypeIsNumerical(1,1) logical = false;
    end

    properties(Access=private)
        bodyInertialFrameCache BodyCenteredInertialFrame = BodyCenteredInertialFrame.empty(1,0);
        bodyFixedFrameCache BodyFixedFrame = BodyFixedFrame.empty(1,0);

        atmoTempCache AtmoTempDataCache = AtmoTempDataCache.empty(1,0);
        sunDotNormalCache SunDotNormalDataCache = SunDotNormalDataCache.empty(1,0);
        parentGmuCache(1,1) double = NaN;

        soiRadiusCache(1,1) double = NaN;
    end

    methods
        function obj = KSPTOT_BodyInfo()
            obj.sunDotNormalCache = SunDotNormalDataCache(obj);
            obj.atmoTempCache = AtmoTempDataCache(obj);
            obj.numIntStateCache = CelestialBodySunRelStateDataCache(obj);

            obj.bodyInertialFrameCache = BodyCenteredInertialFrame(obj, obj.celBodyData);
            obj.bodyFixedFrameCache = BodyFixedFrame(obj, obj.celBodyData);

            if(obj.propTypeEnum == BodyPropagationTypeEnum.TwoBody)
                obj.propTypeIsTwoBody = true;
                obj.propTypeIsNumerical = false;
            elseif(obj.propTypeEnum == BodyPropagationTypeEnum.Numerical)
                obj.propTypeIsTwoBody = false;
                obj.propTypeIsNumerical = true;
            end
        end

        function set.atmotempsunmultcurve(obj,newValue)
            obj.atmotempsunmultcurve = newValue;
            obj.doNotUseAtmoTempSunMultCurve = isa(obj.atmotempsunmultcurve,'griddedInterpolant') && all(obj.atmotempsunmultcurve.Values == 0); %#ok<MCSUP>
        end

        function set.lattempsunmultcurve(obj,newValue)
            obj.lattempsunmultcurve = newValue;
            obj.doNotUseLatTempSunMultCurve = isa(obj.lattempsunmultcurve,'griddedInterpolant') && all(obj.lattempsunmultcurve.Values == 0); %#ok<MCSUP>
        end

        function set.axialtempsunmultcurve(obj,newValue)
            obj.axialtempsunmultcurve = newValue;
            obj.doNotUseAxialTempSunMultCurve = isa(obj.axialtempsunmultcurve,'griddedInterpolant') && all(obj.axialtempsunmultcurve.Values == 0); %#ok<MCSUP>
        end

        function set.axialtempsunbiascurve(obj,newValue)
            obj.axialtempsunbiascurve = newValue;
            obj.doNotUseAxialTempSunBiasCurveGI = isa(obj.axialtempsunbiascurve,'griddedInterpolant') && all(obj.axialtempsunbiascurve.Values == 0); %#ok<MCSUP>
        end

        function set.ecctempbiascurve(obj,newValue)
            obj.ecctempbiascurve = newValue;
            obj.doNotUseEccTempBiasCurveGI = isa(obj.ecctempbiascurve,'griddedInterpolant') && all(obj.ecctempbiascurve.Values == 0); %#ok<MCSUP>
        end

        function set.atmopresscurve(obj,newValue)
            obj.atmopresscurve = newValue;

            if(not(isempty(obj.atmohgt))) %#ok<MCSUP>
                obj.doNotUseAtmoPressCurveGI = logical(obj.atmohgt > 0 && isempty(obj.atmopresscurve)); %#ok<MCSUP>
            end
        end

        function set.propTypeEnum(obj, newValue)
            obj.propTypeEnum = newValue;

            if(obj.propTypeEnum == BodyPropagationTypeEnum.TwoBody)
                obj.propTypeIsTwoBody = true; %#ok<MCSUP>
                obj.propTypeIsNumerical = false; %#ok<MCSUP>
            elseif(obj.propTypeEnum == BodyPropagationTypeEnum.Numerical)
                obj.propTypeIsTwoBody = false; %#ok<MCSUP>
                obj.propTypeIsNumerical = true; %#ok<MCSUP>
            end
        end

        function parentBodyInfo = getParBodyInfo(obj, ~)
            if(obj.parentBodyInfoNeedsUpdate || (obj.parentid > 0 && isempty(obj.parentBodyInfo)))
                pBodyInfo = getParentBodyInfo(obj, obj.celBodyData);

                if(not(isempty(pBodyInfo)))
                    obj.parentBodyInfo = pBodyInfo;
                end

                obj.soiRadiusCache = NaN;
                obj.parentBodyInfoNeedsUpdate = false;
            end

            parentBodyInfo = obj.parentBodyInfo;
        end

        function [childrenBodyInfo, cbNames] = getChildrenBodyInfo(obj, celBodyData)
            if(obj.childrenBodyInfoNeedsUpdate)
                [cBodyInfo,cbNames] = getChildrenOfParentInfo(celBodyData, obj.name);

                if(not(isempty(cBodyInfo)))
                    for(i=1:length(cBodyInfo)) %#ok<*NO4LP>
                        obj.childrenBodyInfo(i) = cBodyInfo{i};
                    end
                    obj.childrenBodyNames = cbNames;
                end

                obj.childrenBodyInfoNeedsUpdate = false;
            end

            childrenBodyInfo = obj.childrenBodyInfo;
            cbNames = obj.childrenBodyNames;
        end

        function soiRadius = getCachedSoIRadius(obj)
            if(isnan(obj.soiRadiusCache))
                obj.soiRadiusCache = getSOIRadius(obj, obj.getParBodyInfo);
            end

            soiRadius = obj.soiRadiusCache;
        end

        function rotMat = get.bodyRotMatFromGlobalInertialToBodyInertial(obj)
            if(isempty(obj.bodyRotMatFromGlobalInertialToBodyInertial))
                obj.bodyZAxis = normVector(obj.bodyZAxis);
                obj.bodyXAxis = normVector(obj.bodyXAxis);

                rotY = normVector(crossARH(obj.bodyZAxis, obj.bodyXAxis));
                rotX = normVector(crossARH(rotY, obj.bodyZAxis));
                rotMat = [rotX, rotY, obj.bodyZAxis];

                obj.bodyRotMatFromGlobalInertialToBodyInertial = rotMat;
            else
                rotMat = obj.bodyRotMatFromGlobalInertialToBodyInertial;
            end
        end

        function frame = getBodyCenteredInertialFrame(obj)
%             if(isempty(obj.bodyInertialFrameCache))
%                 obj.bodyInertialFrameCache = BodyCenteredInertialFrame(obj, obj.celBodyData);
%             end

            frame = obj.bodyInertialFrameCache;
        end

        function frame = getBodyFixedFrame(obj)
%             if(isempty(obj.bodyFixedFrameCache))
%                 obj.bodyFixedFrameCache = BodyFixedFrame(obj, obj.celBodyData);
%             end

            frame = obj.bodyFixedFrameCache;
        end

        function states = getElementSetsForTimes(obj, times)
            parBodyInfo = obj.getParBodyInfo();
            if(not(isempty(parBodyInfo)))
                frame = parBodyInfo.getBodyCenteredInertialFrame();
                gmu = obj.getParentGmuFromCache();

                [rVects, vVects] = getStateAtTime(obj, times, gmu);

                states = CartesianElementSet(times, rVects, vVects, frame);
            else
                frame = obj.getBodyCenteredInertialFrame();

                for(i=1:length(times))
                    states(i) = CartesianElementSet(times(i), [0;0;0], [0;0;0], frame); %#ok<AGROW>
                end
            end
        end

        function [rVect, vVect] = getCachedPositionVelWrtSun(obj, time)
            if(numel(time) == 1)
                if(time == obj.lastComputedTime)
                    rVect = obj.lastComputedRVect;
                    vVect = obj.lastComputedVVect;
                else
                    [rVect, vVect] = getPositOfBodyWRTSun(time, obj, obj.celBodyData);

                    obj.lastComputedRVect = rVect;
                    obj.lastComputedVVect = vVect;
                end
            else
                [rVect, vVect] = getPositOfBodyWRTSun(time, obj, obj.celBodyData);
            end
        end

        function bColorRGB = getBodyRGB(obj)
            bColor = obj.bodycolor;

%             cmap = colormap(bColor);
            try
                cmap = feval(bColor);
            catch
                cmap = feval('gray');
                warning('Could not find a colormap for body color "%s".  Defaulting to grey.', bColor);
            end

            midRow = round(size(cmap,1)/2);
            bColorRGB = cmap(midRow,:);
        end

        function temperature = getBodyAtmoTemperature(obj, time, lat, long, alt)
            temperature = getTemperatureAtAltitude(obj, alt, lat, time, long);
        end

        function pressure = getBodyAtmoPressure(obj, altitude)
            pressure = getPressureAtAltitude(obj, altitude);
        end

        function sunDotNormal = getCachedSunDotNormal(obj, time, long)
            sunDotNormal = obj.sunDotNormalCache.getCachedBodyStateAtTimeAndLong(time, long);
        end

        function parentGM = getParentGmuFromCache(obj)
            if(isnan(obj.parentGmuCache) || obj.parentBodyInfoNeedsUpdate == true)
                obj.parentGmuCache = getParentGM(obj, obj.celBodyData);
                obj.parentBodyInfoNeedsUpdate = false;
            end

            parentGM = obj.parentGmuCache;
        end

        function chain = getOrbitElemsChain(obj)
            if(isempty(obj.orbitElemsChainCache))
                smas = [];
                eccs = [];
                incs = [];
                raans = [];
                args = [];
                means = [];
                epochs = [];
                parentGMs = [];

                loop = true;
                bodyInfo = obj;
                while(loop)
                    smas(end+1) = bodyInfo.sma; %#ok<AGROW>
                    eccs(end+1) = bodyInfo.ecc; %#ok<AGROW>
                    incs(end+1) = bodyInfo.inc; %#ok<AGROW>
                    raans(end+1) = bodyInfo.raan; %#ok<AGROW>
                    args(end+1) = bodyInfo.arg; %#ok<AGROW>
                    means(end+1) = bodyInfo.mean; %#ok<AGROW>
                    epochs(end+1) = bodyInfo.epoch; %#ok<AGROW>

                    try
                        thisParentBodyInfo = bodyInfo.getParBodyInfo(obj.celBodyData);
                    catch
                        thisParentBodyInfo = getParentBodyInfo(bodyInfo, obj.celBodyData);
                    end

                    if(isempty(thisParentBodyInfo))
                        parentGMs(end+1) = 0;

                        break;
                    else
                        parentGMs(end+1) = bodyInfo.getParentGmuFromCache(); %#ok<AGROW>

                        bodyInfo = thisParentBodyInfo;
                    end
                end

                obj.orbitElemsChainCache = {smas, eccs, incs, raans, args, means, epochs, parentGMs};
            end

            chain = obj.orbitElemsChainCache;
        end

        function inputs = getFixedFrameFromInertialFrameInputsCache(obj)
            if(isempty(obj.fixedFrameFromInertialFrameCache))
                obj.fixedFrameFromInertialFrameCache = {obj.rotperiod, obj.rotini};
            end

            inputs = obj.fixedFrameFromInertialFrameCache;
        end

        function [surfTexture] = getSurfaceTexture(obj)
            if(isempty(obj.surfTextureCache))
                if(not(isempty(obj.surftexturefile)))
                    try
                        [I,~] = imread(obj.surftexturefile);
                        I = flip(I, 1);
                    catch ME
                        I = NaN;
                        warning('Could not load surface texture for "%s".  Message: \n\n%s', obj.name, ME.message);
                    end

                    obj.surfTextureCache = I;
                else
                    obj.surfTextureCache = NaN;
                end
            end

            surfTexture = obj.surfTextureCache;
        end

        function tf = eq(A,B)
            tf = [A.id] == [B.id];

            if(isempty(tf))
                tf = false;
            end
        end

        function setPropTypeEnum(obj)
            switch obj.proptype
                case 'analytic_two_body'
                    obj.propTypeEnum = BodyPropagationTypeEnum.TwoBody;
                case 'numerical_integration'
                    obj.propTypeEnum = BodyPropagationTypeEnum.Numerical;
                otherwise
                    error('Unknown Body Prop Type: %s', obj.propType);
            end
        end

        function setStateCacheData(obj, times, stateVects, frame)
            obj.numIntStateCache.setStateData(times, stateVects, frame);
        end
    end

    methods
        function setBaseBodySurfaceTexture(obj)
            if(isempty(obj.surftexturefile) && strcmpi(obj.name,'Sun') && obj.id == 0)
                obj.surftexturefile = 'images/body_textures/surface/sunSurface.jpg';

            elseif(isempty(obj.surftexturefile) && strcmpi(obj.name,'Kerbin') && obj.id == 1)
                obj.surftexturefile = 'images/body_textures/surface/kerbinSurface.png';

            elseif(isempty(obj.surftexturefile) && strcmpi(obj.name,'Mun') && obj.id == 2)
                obj.surftexturefile = 'images/body_textures/surface/munSurface.png';

            elseif(isempty(obj.surftexturefile) && strcmpi(obj.name,'Minmus') && obj.id == 3)
                obj.surftexturefile = 'images/body_textures/surface/minmusSurface.png';

            elseif(isempty(obj.surftexturefile) && strcmpi(obj.name,'Moho') && obj.id == 4)
                obj.surftexturefile = 'images/body_textures/surface/mohoSurface.png';

            elseif(isempty(obj.surftexturefile) && strcmpi(obj.name,'Eve') && obj.id == 5)
                obj.surftexturefile = 'images/body_textures/surface/eveSurface.png';

            elseif(isempty(obj.surftexturefile) && strcmpi(obj.name,'Gilly') && obj.id == 13)
                obj.surftexturefile = 'images/body_textures/surface/gillySurface.png';

            elseif(isempty(obj.surftexturefile) && strcmpi(obj.name,'Duna') && obj.id == 6)
                obj.surftexturefile = 'images/body_textures/surface/dunaSurface.png';

            elseif(isempty(obj.surftexturefile) && strcmpi(obj.name,'Ike') && obj.id == 7)
                obj.surftexturefile = 'images/body_textures/surface/ikeSurface.png';

            elseif(isempty(obj.surftexturefile) && strcmpi(obj.name,'Dres') && obj.id == 15)
                obj.surftexturefile = 'images/body_textures/surface/dresSurface.png';

            elseif(isempty(obj.surftexturefile) && strcmpi(obj.name,'Jool') && obj.id == 8)
                obj.surftexturefile = 'images/body_textures/surface/joolSurface.png';

            elseif(isempty(obj.surftexturefile) && strcmpi(obj.name,'Laythe') && obj.id == 9)
                obj.surftexturefile = 'images/body_textures/surface/laytheSurface.png';

            elseif(isempty(obj.surftexturefile) && strcmpi(obj.name,'Vall') && obj.id == 10)
                obj.surftexturefile = 'images/body_textures/surface/vallSurface.png';

            elseif(isempty(obj.surftexturefile) && strcmpi(obj.name,'Tylo') && obj.id == 12)
                obj.surftexturefile = 'images/body_textures/surface/tyloSurface.png';

            elseif(isempty(obj.surftexturefile) && strcmpi(obj.name,'Bop') && obj.id == 11)
                obj.surftexturefile = 'images/body_textures/surface/bopSurface.png';

            elseif(isempty(obj.surftexturefile) && strcmpi(obj.name,'Pol') && obj.id == 14)
                obj.surftexturefile = 'images/body_textures/surface/polSurface.png';

            elseif(isempty(obj.surftexturefile) && strcmpi(obj.name,'Eeloo') && obj.id == 16)
                obj.surftexturefile = 'images/body_textures/surface/eelooSurface.png';

            elseif(isempty(obj.surftexturefile) && strcmpi(obj.name,'Mercury') && obj.id == 199)
                obj.surftexturefile = 'images/body_textures/surface/mercurySurface.png';

            elseif(isempty(obj.surftexturefile) && strcmpi(obj.name,'Venus') && obj.id == 299)
                obj.surftexturefile = 'images/body_textures/surface/venusSurface.png';

            elseif(isempty(obj.surftexturefile) && strcmpi(obj.name,'Earth') && obj.id == 399)
                obj.surftexturefile = 'images/body_textures/surface/earthSurface.png';

            elseif(isempty(obj.surftexturefile) && strcmpi(obj.name,'Moon') && obj.id == 301)
                obj.surftexturefile = 'images/body_textures/surface/moonSurface.png';

            elseif(isempty(obj.surftexturefile) && strcmpi(obj.name,'Mars') && obj.id == 499)
                obj.surftexturefile = 'images/body_textures/surface/marsSurface.png';

            elseif(isempty(obj.surftexturefile) && strcmpi(obj.name,'Pluto') && obj.id == 999)
                obj.surftexturefile = 'images/body_textures/surface/plutoSurface.png';
            end
        end
    end

	methods(Static)
        function obj = loadobj(obj)
            if(isempty(obj.epoch))
                return; %this should only happen if something is bugged out
            end

            obj.doNotUseAtmoTempSunMultCurve = isa(obj.atmotempsunmultcurve,'griddedInterpolant') && all(obj.atmotempsunmultcurve.Values == 0);
            obj.doNotUseLatTempSunMultCurve = isa(obj.lattempsunmultcurve,'griddedInterpolant') && all(obj.lattempsunmultcurve.Values == 0);
            obj.doNotUseAxialTempSunMultCurve = isa(obj.axialtempsunmultcurve,'griddedInterpolant') && all(obj.axialtempsunmultcurve.Values == 0);
            obj.doNotUseAxialTempSunBiasCurveGI = isa(obj.axialtempsunbiascurve,'griddedInterpolant') && all(obj.axialtempsunbiascurve.Values == 0);
            obj.doNotUseEccTempBiasCurveGI = isa(obj.ecctempbiascurve,'griddedInterpolant') && all(obj.ecctempbiascurve.Values == 0);
            obj.doNotUseAtmoPressCurveGI = (obj.atmohgt > 0 && isempty(obj.atmopresscurve));

            obj.parentBodyInfoNeedsUpdate = true;
            obj.getParBodyInfo(obj.celBodyData);

            obj.childrenBodyInfoNeedsUpdate = true;
            obj.getChildrenBodyInfo(obj.celBodyData);

            obj.setBaseBodySurfaceTexture();

            if(isempty(obj.sunDotNormalCache))
                obj.sunDotNormalCache = SunDotNormalDataCache(obj);
            end

            if(isempty(obj.atmoTempCache))
                obj.atmoTempCache = AtmoTempDataCache(obj);
            end

            if(isempty(obj.numIntStateCache))
                obj.numIntStateCache = CelestialBodySunRelStateDataCache(obj);
            end

            obj.setPropTypeEnum();

            if(isempty(obj.bodyInertialFrameCache))
                obj.bodyInertialFrameCache = BodyCenteredInertialFrame(obj, obj.celBodyData);
            end

            if(isempty(obj.bodyFixedFrameCache))
                obj.bodyFixedFrameCache = BodyFixedFrame(obj, obj.celBodyData);
            end

            if(obj.propTypeEnum == BodyPropagationTypeEnum.TwoBody)
                obj.propTypeIsTwoBody = true;
                obj.propTypeIsNumerical = false;
            elseif(obj.propTypeEnum == BodyPropagationTypeEnum.Numerical)
                obj.propTypeIsTwoBody = false;
                obj.propTypeIsNumerical = true;
            end
        end

        function bodyObj = getObjFromBodyInfoStruct(bodyInfo)
            bodyObj = KSPTOT_BodyInfo();

            bodyObj.epoch = bodyInfo.epoch;
            bodyObj.sma = bodyInfo.sma;
            bodyObj.ecc = bodyInfo.ecc;
            bodyObj.inc = bodyInfo.inc;
            bodyObj.raan = bodyInfo.raan;
            bodyObj.arg = bodyInfo.arg;
            bodyObj.mean = bodyInfo.mean;
            bodyObj.gm = bodyInfo.gm;
            bodyObj.radius = bodyInfo.radius;
            bodyObj.atmohgt = bodyInfo.atmohgt;
            bodyObj.atmopresscurve = bodyInfo.atmopresscurve;
            bodyObj.atmotempcurve = bodyInfo.atmotempcurve;
            bodyObj.atmotempsunmultcurve = bodyInfo.atmotempsunmultcurve;
            bodyObj.lattempbiascurve = bodyInfo.lattempbiascurve;
            bodyObj.lattempsunmultcurve = bodyInfo.lattempsunmultcurve;
            bodyObj.atmomolarmass = bodyInfo.atmomolarmass;
            bodyObj.rotperiod = bodyInfo.rotperiod;
            bodyObj.rotini = bodyInfo.rotini;
            bodyObj.bodycolor = bodyInfo.bodycolor;
            bodyObj.canbecentral = bodyInfo.canbecentral;
            bodyObj.canbearrivedepart = bodyInfo.canbearrivedepart;
            bodyObj.parent = bodyInfo.parent;
            bodyObj.parentid = bodyInfo.parentid;
            bodyObj.name = bodyInfo.name;
            bodyObj.id = bodyInfo.id;
            bodyObj.celBodyData = bodyInfo.celBodyData;

            bodyObj.bodyZAxis = bodyInfo.bodyZAxis;
            bodyObj.bodyXAxis = bodyInfo.bodyXAxis;
            bodyObj.bodyRotMatFromGlobalInertialToBodyInertial = bodyInfo.bodyRotMatFromGlobalInertialToBodyInertial;
        end
    end
end


function F = lmi(X,handlestring,dummy,noprune,symmetryKnown)

superiorto('sdpvar')

persistent temp
persistent F0

fast = 0;
noprune = 0;
symmetryKnown = 0;
switch nargin
    case 0
        F.clauses={};
        F.LMIid    = [];
        F.savedata = [];
        F.dualized = 0;
        F = class(F,'lmi');
        F0 = F;
        return
    case 1
        handlestring = '';
        % ok probably, check later
    case 2
        if ~isa(handlestring,'char')
            error('The handle must be a string')
        end
    case 4
        handlestring = '';
        noprune = 1;
    case 5
        handlestring = '';
        symmetryKnown = 1;

    otherwise
        error('Wrong number of arguments')
end

if isempty(F0)
    F.clauses  = {};
    F.LMIid    = [];
    F.savedata = [];
    F.dualized = 0;
    try
        F = class(F,'lmi');
    catch
        disp('Failure in creating SET object. Restart MATLAB and try again, or type clear classes')
    end
    F0 = F;
else
    F = F0;
end

% Return empty LMI in degenerate case
if isempty(X)
    % A bit inconsitently, we create a constraint, which is empty. We count
    % the number of ocnstraints by looking at LMIid, which still is zero
    F.clauses{1}.data=[];
    F.clauses{1}.type = [];
    F.clauses{1}.symbolic=[];
    F.clauses{1}.handle=[];
    F.clauses{1}.strict = [];
    F.clauses{1}.cut = [];
    F.clauses{1}.expanded = [];
    F.clauses{1}.lift = [];
    F.clauses{1}.schurfun  = [];
    F.clauses{1}.schurdata = [];
    F.clauses{1}.jointprobabilistic = [];
    F.clauses{1}.confidencelevel = [];
    F.clauses{1}.extra = [];
    return
end

% 0 : Nothing (i.e error)
% 1 : LMI
% 2 : Elementwise
% 3 : Equality
% 4 : SOCC
% 5 : Rotated Lorentz
% 9 : KYP constraint
%10 : Eigenvalue constraint
%11 : Sum of square constraint
%12 : Logic CNF
%15 : Deterministic uncertain variable
%16 : Random uncertain variable
%20 : Power cone
%21 : EXP
%22 : Stacked EXPCONE
%30 : User generated Schur
%40 : Generalized KYP
%50 : SOS2
%51 : SOS1
%52 : semivar
%53 : semiintvar
%54 : Stacked SOCP
%55 : Complementary
%56 : Meta constraint to be expanded (implies, iff)
%58 : Stacked power cone
%60 : Chance constraint

switch class(X)
    case 'lmi'
        F = X;
        return
    case 'char'
        try
            % Parse (check for old notation, take care of ||...|| notation)
            sdpvarExpr = parseLMI(X);
            % Old notation
            X = strrep(X,'.>','>');
            X = strrep(X,'.<','<');
        catch
            error(lasterr)
        end
        % Evaluate
        try
            Fi=evalin('caller',sdpvarExpr);
            switch class(Fi)
                case 'sdpvar'
                    Fi = {Fi};
                    strict = 0;
                case 'constraint'
                    [Fi,strict] = getlist(Fi);
                case 'lmi'
                    F = Fi;
                    return;
                otherwise
                    error('The string does not define an SDPVAR object')
            end

        catch
            error(lasterr)
        end

    case 'sdpvar'
        Fi = {X};
        strict = 0;
        X='Numeric value';

    case 'logic'
        Fi = {X};
        strict = 0;
        X = 'Numeric value';

    case {'struct','constraint'}
        [Fi,strict,LMIIdentifiers,tags] = getlist(X);
        if isempty(handlestring) || length(handlestring)==0
            handlestring = tags{1};
        end
        X='Numeric value';

    otherwise
        error('The first argument should be an sdpvar object or string.')
end

% Return empty LMI in degenerate case
if all(cellfun('isempty',Fi))
    return
end

TypeofConstraint = zeros(length(Fi),1)-1;

% support for SOS constraints placed in vector
if length(Fi) == 1
    if isa(Fi{1},'sdpvar')% is(Fi{1},'sos')
        if gethackflag(Fi{1})==1
            % Expand to a set of SOS constraints
            if ~issymmetric(Fi{1})
                p = Fi{1}(:);
                for i = 1:length(p)
                    Fi{i} = p(i);
                end
                TypeofConstraint = zeros(length(Fi),1)-1;
                strict = zeros(length(Fi),1)-1;
            end
        end
    end
end

i = 1;
while i <= length(Fi)
    thisFi = Fi{i};
    if ~isempty(thisFi)
        if isa(thisFi,'logic')
            TypeofConstraint(i) = 12;
        else
            TypeofConstraint(i) = gethackflag(Fi{i});
        end
        % User just sent a sdpvar object, interpret as X>0
        if (TypeofConstraint(i)==0)
            TypeofConstraint(i) = 1;
        end

        if (TypeofConstraint(i)==1) && (symmetryKnown == 0)
            [n,m]=size(thisFi);
            if (n~=m) || ((TypeofConstraint(i) == 1) && (n*m==1)) || ~ishermitian(thisFi)
                TypeofConstraint(i) = 2;
            end
        end

        if TypeofConstraint(i) == 2

            % remove constraint of the type set(0 >= 0)
            B = getbase(thisFi(:));
            if ~noprune
                Bv = B(:,2:end);
                notused = find((~any(Bv,2)) & (B(:,1)>=0));
                if ~isempty(notused)
                    used = setdiff(1:size(Bv,1),notused);
                    thisFi = thisFi(used);
                end
            end
        end

        % save cleaned version
        Fi{i} = thisFi;

        switch TypeofConstraint(i)
            case {1,2,3,4,5,7,8,9,10,11,12,13,15,16,20,21,22,30,40,50,51,52,53,54,57,58}
                i = i + 1;
            otherwise
                error('Error in argument in LMI. Please report bug');
        end
    end
end

% Special case for x<y<z<...
% Merge into one constraint, gives nicer displays
if ~exist('LMIIdentifiers','var')
   LMIIdentifiers = yalmip('lmiid');
end

if all(TypeofConstraint == 2) && all(strict==strict(1))
    if length(Fi)>1
        vecF = [];
        sizes = zeros(length(Fi),1);
        % Speed up concatenation of merging
        for i = 1:length(Fi)
            sizes(i) = prod(size(Fi{i}));
        end
        if all(sizes == 1)
            vecF = [Fi{:}]';
        else
            for i = 1:length(Fi)
                fi = Fi{i};
                if sizes(i) > 1
                    fi = reshape(fi,prod(size(fi)),1);
                end
                vecF = [vecF;fi];
            end
        end
    else
        vecF = Fi{1};
        if prod(size(vecF))>1
            vecF = vecF(:);
        end
    end
    if isempty(temp)
        temp.data=vecF;
        temp.type = 2;
        temp.symbolic=X;
        temp.handle=handlestring;
        temp.strict = strict(1);
        temp.cut = 0;
        temp.expanded = 0;
        temp.lift = 0;
        temp.schurfun  = '';
        temp.schurdata = [];
        temp.jointprobabilistic = [];
        temp.confidencelevel = [];
        temp.extra = [];
    else
        temp.data=vecF;
        temp.symbolic=X;
        temp.handle=handlestring;
        temp.strict = strict(1);
    end
    F.clauses{1}{1} = temp;

%     F.clauses{1}{1}.data=vecF;
%     F.clauses{1}{1}.type = 2;
%     F.clauses{1}{1}.symbolic=X;
%     F.clauses{1}{1}.handle=handlestring;
%     F.clauses{1}{1}.strict = strict(1);
%     F.clauses{1}{1}.cut = 0;
%     F.clauses{1}{1}.expanded = 0;
%     F.clauses{1}{1}.lift = 0;
%     F.clauses{1}{1}.schurfun  = '';
%     F.clauses{1}{1}.schurdata = [];
%     F.clauses{1}{1}.jointprobabilistic = [];
%     F.clauses{1}{1}.confidencelevel = [];
%     F.clauses{1}{1}.extra = [];
    F.LMIid = [F.LMIid LMIIdentifiers(1)];
else
    for i = 1:length(Fi)
        switch TypeofConstraint(i)
            case {1,2,3,4,5,7,8,9,10,11,12,13,15,16,20,21,22,30,40,50,51,52,53,54,57,58}
                F.clauses{1}{i}.data=Fi{i};
                F.clauses{1}{i}.type = TypeofConstraint(i);
                F.clauses{1}{i}.symbolic=X;
                F.clauses{1}{i}.handle=handlestring;
                F.clauses{1}{i}.strict = strict(i);
                F.clauses{1}{i}.cut = 0;
                F.clauses{1}{i}.expanded = 0;
                F.clauses{1}{i}.lift = 0;
                F.clauses{1}{i}.schurfun  = '';
                F.clauses{1}{i}.schurdata = [];
                F.clauses{1}{i}.jointprobabilistic = [];
                F.clauses{1}{i}.confidencelevel = [];
                F.clauses{1}{i}.extra = [];
                F.LMIid = [F.LMIid LMIIdentifiers(i)];
                i = i + 1;
            otherwise
                error('Error in argument in LMI. Please report bug');
        end
    end
end


function [TB] = EB2TB(EB)
% Convert computery things from exabytes to terabytes.
% Chad A. Greene 2012
TB = EB*1048576 ;


function []=panel1fdisp(N,n,T,Units,endo,Y,stringdates2,decimaldates2,Fstartlocation,Fendlocation,forecast_estimates,pref)

% preliminary task: gather in a cell the values to be plotted
% initiate the cell
plotdata={};
% because forecasts have to be computed for each unit, loop over units
for ii=1:N
% each cell entry is a matrix of actual and forecast values
% this matrix comprises 4 rows: the first row is actual data, while the three other rows are the estimates (point estimates and confidence bands) for the forecasts
% also, this matrix has a number of rows equal to the dimension of decimaldates2, which comprises the total period sample+forecasts
   % loop over variables
   for jj=1:n
      plotdata{jj,1,ii}=nan(4,size(decimaldates2,1));
      % record actual sample values
      plotdata{jj,1,ii}(1,1:T)=Y(:,jj,ii)';
      % copy the last point of the actual sample for the forecast part of the matrix (required to have a clean plot)
      plotdata{jj,1,ii}(:,Fstartlocation-1)=repmat(Y(Fstartlocation-1,jj,ii),4,1);
      % record forecast, lower bound
      plotdata{jj,1,ii}(2,Fstartlocation:Fendlocation)=forecast_estimates{jj,1,ii}(1,:);
      % record forecast, point estimate
      plotdata{jj,1,ii}(3,Fstartlocation:Fendlocation)=forecast_estimates{jj,1,ii}(2,:);
      % record forecast, upper bound
      plotdata{jj,1,ii}(4,Fstartlocation:Fendlocation)=forecast_estimates{jj,1,ii}(3,:);
   end
end
if pref.plot
% then plot the figure
forecast=figure('Tag','BEARresults');
set(forecast,'Color',[0.9 0.9 0.9]);
set(forecast,'name','unconditional forecasts');
% initiate the count
count=0;
% loop over units
for ii=1:N
   % loop over endogenous variables
   for jj=1:n
   % increment count
   count=count+1;
   % then plot
   subplot(N,n,count)
   hold on
   Xpatch=[decimaldates2(Fstartlocation-1:Fendlocation,1)' fliplr((decimaldates2(Fstartlocation-1:Fendlocation,1))')];
   Ypatch=[plotdata{jj,1,ii}(2,Fstartlocation-1:Fendlocation) fliplr(plotdata{jj,1,ii}(4,Fstartlocation-1:Fendlocation))];
   Fpatch=patch(Xpatch,Ypatch,[0.7 0.78 1]);
   set(Fpatch,'facealpha',0.6);
   set(Fpatch,'edgecolor','none');
   plot(decimaldates2,plotdata{jj,1,ii}(3,:),'Color',[0.4 0.4 1],'LineWidth',2);
   plot(decimaldates2,plotdata{jj,1,ii}(1,:),'Color',[0 0 0],'LineWidth',2);
   hold off
   set(gca,'XLim',[decimaldates2(1,1) decimaldates2(end,1)],'FontName','Times New Roman');
   set(gca,'XGrid','on');
   set(gca,'YGrid','on');
      % top labels
      if count<=n
      title(endo{count,1},'FontWeight','normal');
      end
      % side labels
      if jj==1
      ylabel(Units{ii,1},'FontWeight','normal');
      end
   end
end
end

% save on Excel
% create the cell that will be saved on excel
forecastcell={};
% build preliminary elements: space between the tables
vertspace=repmat({''},size(stringdates2,1)+3,1);
horzspace=repmat({''},3,6*n);
% loop over units
for ii=1:N
% initiate the cell of results
unitcell={};
   % loop over endogenous variables (horizontal dimension)
   for jj=1:n
   % create a header
   header=[{[Units{ii,1} ': ' endo{jj,1}]} {''} {''} {''} {''};{''} {''} {''} {''} {''};{''} {'actual'} {'lower bound'} {'median'} {'upper bound'}];
   % complete the cell
   endocell=[[header;stringdates2 num2cell((plotdata{jj,1,ii})')] vertspace];
   % concatenate to the previous parts of unitcell
   unitcell=[unitcell endocell];
   end
% concatenate to the previous parts of afcell
forecastcell=[forecastcell;horzspace;unitcell];
end
% trim
forecastcell=forecastcell(4:end,1:end-1);
% write in excel
if pref.results==1
    bear.xlswritegeneral(fullfile(pref.results_path, [pref.results_sub '.xlsx']),forecastcell,'forecasts','B2');
end


% Copyright (C) 1993-2013, by Peter I. Corke
%
% This file is part of The Machine Vision Toolbox for MATLAB (MVTB).
%
% MVTB is free software: you can redistribute it and/or modify
% it under the terms of the GNU Lesser General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% MVTB 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 Lesser General Public License for more details.
%
% You should have received a copy of the GNU Leser General Public License
% along with MVTB.  If not, see <http://www.gnu.org/licenses/>.
%
% http://www.petercorke.com

%%begin

% We demonstrate computing Harris corner features on an image and an
% image sequence.

% We load an image
im = iread('eiffel2-1.jpg', 'grey');
% and display it
idisp(im)

% Now we compute the coordinates of the 200 strongest Harris corner features
h = icorner(im, 'nfeat', 200);
about h
% we see that the result is a vector of PointFeature objects, which have values
h(1)
% We overlay the features on the image
h.plot();
% We note that the features are located where there is very high image contrast
% such as the edges of trees and the tower

% We load an image sequence
im = iread('~/rvc/bridge-l/*.png', 'roi', [20 750; 20 480]);
% Note that We have used the 'roi' option to chop off the ragged edges
% of these image

% The size of the resulting image is
about im
% which is slightly unusual in that the pixels are uint16 types.  The image
% has three dimensions, the third dimension is the number in the sequence.

% Once again we compute the coordinates of the features
h = icorner(im, 'nfeat', 200);
about h
% and because the input was an image sequence the features are computed for
% every image in the sequence.  The result is a cell array, one cell per image
% in the sequence which contains a vector of corner feature objects
about(h{1})

% We can animate the image sequence and the features
ianimate(im, h, 'fps', 10)
% Note that frame to frame the Harris features tend to stick the same point
% in the world such as cars, trees, road markings.

% Finally we can compute Harris featurse on live video.  You will find the features
% tend to stick to corners of pictures, books, shelves and not so much to smoother
% objects likes hands and faces.

% Create an object which represents a video camera connected to your computer
% If you have a laptop you should see the video recording light come on
camera = VideoCamera(0, 'grey');
% Now create a loop to grab frames from the camera and display them
while true
    % grab the next frame from the attached camera
    im = camera.grab();
    % and display it
    idisp(im);
    % compute Harris corners
    h = icorner(im, 'nfeat', 100);
    % and overlay them
    h.plot();
    drawnow

    % test whether to quit, hit 'q'
    if get(gcf,'CurrentCharacter') == 'q' break; end;
end
clear camera

% See also demo/tracking.


% 2021 EDIT: This function is now deprecated. The current version of the
% shaped PRESS simulation is sim_press_shaped.m, which employs
% coherence selection rather than phase cycling to null undesired signal.

% sim_press_shaped_phCyc.m
% Robin Simpson and Jamie Near, 2014.
%
% USAGE:
% out = sim_press_shaped_phCyc(n,sw,Bfield,linewidth,sys,tau1,tau2,RF,tp,dx,dy,Gx,Gy,phCyc1,phCyc2,flipAngle,centreFreq)
%
% DESCRIPTION:
% This function simulates the PRESS experiment.  The excitation is
% simulated as an instantaneous rotation, and the refocusing pulse is
% simulated as a shaped rotation.
%
% This code enables the choice of the phase of the refocusing pulses.  This
% enables phase cycling of the refocusing pulses by repeating simulations
% with different editing pulse phases, which is necessary to remove phase
% artefacts from the editing pulses.  A four step phase cycling scheme is typically
% sufficient, where both refocusing pulses are phase cycled by 0 and 90 degrees, and
% the phase are combined in the following way:
%
% signal = ([0 90] - [0 0]) + ([90 0] - [90 90]);
%
% where, in [X Y], X is the phase of the first refocusing pulse and Y is
% the phase of the second refocusing pulse
%
% Finally, this code simulates the spectrum at a given point in space (x,y),
% given the values of the slice selection gradients (Gx, and Gy).  The pulse
% waveform is assumed to be the same for both refocusing pulses.  In order
% to fully simulate the MEGA-PRESS experiment, you have to run this
% simulation many times at various points in space (x,y), and then add
% together the resulting spectra.
%
% INPUTS:
% n         = number of points in fid/spectrum
% sw        = desired spectral width in [Hz]
% Bfield    = main magnetic field strength in [T]
% linewidth = linewidth in [Hz]
% sys       = spin system definition structure
% tau1      = echo time 1 in [ms].
% tau2      = echo time 2 in [ms].
% RF        = RF pulse definition structure for refoc pulses (obtain using 'io_loadRFwaveform.m')
% tp        = RF pulse duration in [ms]
% dx        = position offset in x-direction (corresponding to first refocusing pulse) [cm]
% dy        = position offset in y-direction (corresponding to second refocusing pulse) [cm]
% Gx        = gradient strength for first selective refocusing pulse [G/cm]
% Gy        = gradient strength for second selective refocusing pulse [G/cm]
% phCycl    = initial phase of the first refocusing pulse in [degrees];
% phCycl2   = initial phase of the second refocusing pulse in [degrees];
% flipAngle = flip angle of refocusing pulses [degrees] (Optional.  Default = 180 deg)
% centreFreq= centre frequency of the spectrum in [ppm] (Optional.  Default = 2.3)
%
% OUTPUTS:
% out       = simulated spectrum, in FID-A structure format, using PRESS
%             sequence.

function out = sim_press_shaped_phCyc(n,sw,Bfield,linewidth,sys,tau1,tau2,RF,tp,dx,dy,Gx,Gy,phCyc1,phCyc2,flipAngle,centreFreq)

if nargin<17
    centreFreq=2.3;
    if nargin<16
        flipAngle=180;
    end
end

if tau1<tp/1000
    error('ERROR:  Echo-time 1 cannot be less than duration of refocusing pulse! ABORTING!!');
end
if tau2<tp/1000
    error('ERROR:  Echo-time 2 cannot be less than duration of refocusing pulse! ABORTING!!');
end

%Set water to centre
%centreFreq=2.3;
for k=1:length(sys)
    sys(k).shifts=sys(k).shifts-centreFreq;
end

%Calculate Hamiltonian matrices and starting density matrix.
[H,d]=sim_Hamiltonian(sys,Bfield);

%Calculate new delays by subtracting the pulse duration from tau1 and tau2;
delays=zeros(2);
delays(1)=tau1-tp;
delays(2)=tau2-tp;
if sum(delays<0)
    error(['ERROR! The following taus are too short: ' num2str(find(delays<0)) '.']);
end

%BEGIN PULSE SEQUENCE************
d=sim_excite(d,H,'x');                                    %EXCITE
d=sim_evolve(d,H,delays(1)/2000);                            %Evolve by delays(1)/2
d=sim_shapedRF(d,H,RF,tp,flipAngle,90+phCyc1,dx,Gx);          %1st shaped 180 degree refocusing pulse
d=sim_evolve(d,H,(delays(1)+delays(2))/2000);                     %Evolve by (delays(1)+delays(2))/2
d=sim_shapedRF(d,H,RF,tp,flipAngle,90+phCyc2,dy,Gy);          %2nd shaped 180 degree refocusing pulse
d=sim_evolve(d,H,delays(2)/2000);                            %Evolve by delays(2)/2
[out,dout]=sim_readout(d,H,n,sw,linewidth,90);      %Readout along y (90 degree phase);
%END PULSE SEQUENCE**************

%Correct the ppm scale:
out.ppm=out.ppm-(4.65-centreFreq);

%Fill in structure header fields:
out.seq='press';
out.te=tau1+tau2;
out.sim='shaped';

%Additional fields for compatibility with FID-A processing tools.
out.sz=size(out.specs);
out.date=date;
out.dims.t=1;
out.dims.coils=0;
out.dims.averages=0;
out.dims.subSpecs=0;
out.dims.extras=0;
out.averages=1;
out.rawAverages=1;
out.subspecs=1;
out.rawSubspecs=1;
out.flags.writtentostruct=1;
out.flags.gotparams=1;
out.flags.leftshifted=0;
out.flags.filtered=0;
out.flags.zeropadded=0;
out.flags.freqcorrected=0;
out.flags.phasecorrected=0;
out.flags.averaged=1;
out.flags.addedrcvrs=1;
out.flags.subtracted=1;
out.flags.writtentotext=0;
out.flags.downsampled=0;
out.flags.isFourSteps=0;


function analyse_eval_lin_and_qual_fusion(key_dev,ndx_eval,dev_scores_obj_array,eval_scores_obj_array,dev_scr_lin,eval_scr_lin,dev_scr_qual,eval_scr_qual)
% Does some analyses on evaluation scores for all systems and all
% fusions.  For each system, it calculates the estimated percentage
% of targets in the database.  It also calculates a KL divergence
% matrix of all systems against all systems.
% Inputs:
%   key_dev: The Key for the dev trials.
%   ndx_eval: The Ndx describing the eval trials.
%   dev_scores_obj_array: An array of Scores objects containing
%     system scores for the dev trials.
%   eval_scores_obj_array: An array of Scores objects containing
%     system scores for the eval trials.
%   dev_scr_lin: The dev scores resulting from the linear fusion.
%   eval_scr_lin: The eval scores resulting from the linear fusion.
%   dev_scr_qual: The dev scores resulting from the quality fusion.
%   eval_scr_qual: The eval scores resulting from the quality fusion.

assert(nargin==8)
assert(isa(key_dev,'Key'))
assert(isa(ndx_eval,'Ndx'))
assert(isa(dev_scr_lin,'Scores'))
assert(isa(eval_scr_lin,'Scores'))
assert(isa(dev_scr_qual,'Scores'))
assert(isa(eval_scr_qual,'Scores'))
assert(key_dev.validate())
assert(ndx_eval.validate())
assert(dev_scr_lin.validate())
assert(eval_scr_lin.validate())
assert(dev_scr_qual.validate())
assert(eval_scr_qual.validate())

eval_stacked_scores = stackScores(ndx_eval,eval_scores_obj_array);
trialmask = ndx_eval.trialmask;
eval_lin_fusion = eval_scr_lin.scoremat(trialmask(:))';
eval_qfusion = eval_scr_qual.scoremat(trialmask(:))';
eval_scores = [eval_stacked_scores;eval_lin_fusion;eval_qfusion];

ptar = estimate_target_proportions(eval_scores,0.08);
logprint(Logger.Info,'\nSystem Ptar estimates:\n');
logprint(Logger.Info,'%s',sprintfmatrix(100*ptar'));

ptar_best = ptar(end-1);

dev_stacked_scores = stackScores(key_dev,dev_scores_obj_array);
trialmask = key_dev.to_ndx().trialmask;
dev_lin_fusion = dev_scr_lin.scoremat(trialmask(:))';
dev_qfusion = dev_scr_qual.scoremat(trialmask(:))';
dev_scores = [dev_stacked_scores;dev_lin_fusion;dev_qfusion];

devKL = 100*kldivergence_of_systems(dev_scores,ptar_best,key_dev);
evalKL = 100*kldivergence_of_systems(eval_scores,ptar_best);
logprint(Logger.Info,'\nDev score divergence:\n%s',sprintfmatrix(devKL));
logprint(Logger.Info,'\nEval score divergence:\n%s',sprintfmatrix(evalKL));
end


function dat = rebuild_volinfo_from_dat(dat, newdat)
% Will rebuild volInfo (the image space, or sometimes "mask") from a vectorized image.
% In other words, will rebuild dat.volInfo from newdat.
%
% Also resets all voxels to be significant, if a statistic image
%
% :Input:
%
%   **dat:**
%        an image_vector
%
%   **newdat:**
%        a vector that MUST be size of ENTIRE image (dat.volInfo.nvox)
%
% :Output:
%
%   **dat:**
%        dat.dat contains the non-zero values of newdat, and dat.volInfo is
%        correctly defining the image space

if length(newdat) ~= dat.volInfo.nvox
    error('newdat MUST be size of ENTIRE image (dat.volInfo.nvox).  Image_vector.reconstruct_image may be helpful');
end

wh_new = newdat ~=0 & ~isnan(newdat);

if isa(dat, 'statistic_image')
    % Rebuild fields specific to statistic_images

    dat = replace_empty(dat);
    k = size(dat.dat, 2);

    if isempty(dat.ste), dat.ste = single(zeros(size(dat.dat))); end
    if isempty(dat.sig), dat.sig = single(zeros(size(dat.dat))); end

    for i = 1:k

        p = ones(dat.volInfo.nvox, k);
        p(dat.volInfo.wh_inmask, i) = dat.p(:, i);

        ste = Inf .* ones(dat.volInfo.nvox, k);
        ste(dat.volInfo.wh_inmask, i) = dat.ste(:, i);

        sig = zeros(dat.volInfo.nvox, k);
        sig(dat.volInfo.wh_inmask, i) = dat.sig(:, i);

    end

    dat.p = p(wh_new, :);
    dat.ste = ste(wh_new, :);
    dat.sig = sig(wh_new, :);

end

% rebuild volInfo from newdat
dat.volInfo.image_indx = wh_new;
dat.volInfo.wh_inmask = find(dat.volInfo.image_indx);
dat.volInfo.n_inmask = length(dat.volInfo.wh_inmask);
[i, j, k] = ind2sub(dat.volInfo(1).dim(1:3), dat.volInfo(1).wh_inmask);
dat.volInfo(1).xyzlist = [i j k];

% rebuild dat.dat with new data
dat.dat = newdat(dat.volInfo.image_indx);

dat.removed_voxels = false(dat.volInfo.n_inmask, 1);

% rebuild volInfo.cluster
dat.volInfo.cluster = dat.volInfo.wh_inmask;
% this forces reparse_contiguous to rebuild the cluster correctly.
% maybe not ideal solution, but maybe yes -- don't know the code well enough.  Yoni 7/14

dat = reparse_contiguous(dat);

end


function [ state ] =movePTP_ConditionalTorque_ArcYZ_AC(t_Kuka,theta,c,VEL,joints_indices,max_torque,min_torque)
%% This function is used for moving the endeffector on an arc in the YZ plane, for the KUKA iiwa 7 R 800.
% this motion is interruptible when one or more of the joints-torques exceed the predefined limits

%% Syntax:
% [ state ] =movePTP_ConditionalTorque_ArcYZ_AC(t_Kuka,theta,c,VEL,joints_indices,max_torque,min_torque)

%% About:
% This function is used to move the end-effector on an arc in the YZ plane,

%% Arreguments:
% t_Kuka: is the TCP/IP connection
% theta: is the arc angle, in radians
% c: the YZ coordinates of the center of the circle, it is 1x2 vector.
% VEL : is a double, defines the motion velocity mm/sec.
% joints_indices: is a vector of the indices of the joints where the
% torques limits are to be imposed, the joints are indexed starting from one.
% max_torque: is a vector of the maximum torque limits for the joints
% specified in the (joints_indices) vector.
% min_torque: is a vecotr of the minimum torque limits for the joints
% specified in the (joints_indices) vector.

%% Return value:
% state: a number equals to one if the motion is completed sccessfully or a
% zero if the motion was interrupted due to external contact with the
% robot.
% if there is an error, the return value is minus one

% Copy right, Mohammad SAFEEA, 9th of April 2018

if((size(VEL,1)==1)&&(size(VEL,2)==1))
else
    disp('Error, velocity is a double and shall not be an array');
    state=-1;
    return;
end

k=[1;0;0];
pos=getEEFPos( t_Kuka );
c=colVec(c);
c1=[pos{1};c(1);c(2)];
[ state ] =movePTP_ConditionalTorque_Arc_AC(t_Kuka,theta,c1,k,VEL,joints_indices,max_torque,min_torque);

end

function [ y ] = colVec( v)
% Convert a vector to a column vector:
    if(size(v,2)==1)
        y=v;
    else
        y=v';
    end
end