function varargout = linearProgramming(maximizeFlag, f, A, b, lb, ub, constraintTypes, variableTypes, options)
% Provides a common interface to several linear programming solvers.
% Checks each solver for errors.
%
% That is it solves the problem:
%   max f'*x
%   subject to A*x~b, lb<=x<=ub
% where "~" can be equality, or double or single-sided inequality
% constraints if the chosen linear programming solver supports those
% types of constraints. Additionally the variables, x, can, if the chosen
% linear programming solver supports it, either continuous, integer, and
% boolean valued.
%
% Constraint Types: etiher a single value, or a column vector or values:
%   'F' Free (unbounded) variable (the constraint is ignored).
%   'U' Variable with upper bound ( A(i,:)*x <= b(i)).
%   'S' Fixed Variable (A(i,:)*x = b(i)).
%   'L' Variable with lower bound (A(i,:)*x >= b(i)).
%   'D' Double-bounded variable (A(i,:)*x >= -b(i) and A(i,:)*x <= b(i)).
%
% Variable Types: either a single value, or a column vector or values:
%   'C' Continuous variable.
%   'I' Integer variable
%   'B' Binary variable
%
% Output
%  x
%  lambda,
%  fopt
%  errorFlag
%  errorMsg
%  extra
%
% Requirements: at least one of the following linear programing solvers
% - glpk -- GNU Linear programming kit, to solve linear programming problems
%   http://sourceforge.net/projects/glpkmex/
% - lp_solve -- open source solve linear programming solver
%   http://sourceforge.net/projects/lpsolve
% - clp -- open source solve linear programming solver
%   http://control.ee.ethz.ch/~joloef/mexclp.zip
% - bpmpd -- open source solve linear programming solver
%   http://www.pserc.cornell.edu/bpmpd/
% - qsopt -- open source solve linear programming solver
%   http://control.ee.ethz.ch/~joloef/mexqsopt.msql
% - Optimization Toolbox - has linprog, MATLAB's linear programming
%   solver. Note: linprog is not vevy good. We suggest you use another
%   solver instead.
%   http://www.mathworks.com/products/optimization/
%
% Author: Jonathan Karr
% Affiliation: Covert Lab, Department of Bioengineering, Stanford University
% Last updated: 10/31/2008

%- A is non-empty
%- b is real column vector of length ncols(A)
%- lb, ub are real column vectors of length nrows(A)
validateattributes(A, {'numeric'}, {'nonempty'});
validateattributes(b, {'numeric'}, {'real', 'size', [size(A, 1) 1]});
if isempty(lb)
    lb = -Inf(size(A, 2), 1);
else
    validateattributes(lb, {'numeric'}, {'real', 'size', [size(A, 2) 1]});
end
if isempty(ub)
    ub = -Inf(size(A, 2), 1);
else
    validateattributes(ub, {'numeric'}, {'real', 'size', [size(A, 2) 1]});
end

%error check length sizes of contraint and variable types, expand if
%necessary
if isscalar(constraintTypes)
    if ~ismembc(constraintTypes, 'DFLSUdflsu')
        throw(MException('ComputationUtil:LinearProgramming', 'linear programming: invalid constraints types'));
    end
    constraintTypes = constraintTypes(ones(length(b), 1), 1);
elseif length(constraintTypes) ~= length(b)
    throw(MException('ComputationUtil:LinearProgramming', 'linear programming: invalid constraints types'));
elseif ~all(ismembc(constraintTypes, 'DFLSUdflsu'))
    throw(MException('ComputationUtil:LinearProgramming', 'linear programming: invalid constraints types'));
end

if isscalar(variableTypes)
    if ~ismembc(variableTypes, 'BCIbci')
        throw(MException('ComputationUtil:LinearProgramming', 'linear programming: invalid variable types'));
    end
    variableTypes = variableTypes(ones(size(A, 2), 1), 1);
elseif length(variableTypes) ~= length(lb)
    throw(MException('ComputationUtil:LinearProgramming', 'linear programming: invalid variable types'));
elseif ~all(ismembc(variableTypes, 'BCIbci'))
    throw(MException('ComputationUtil:LinearProgramming', 'linear programming: invalid variable types'));
end

%compute sense from maximizeFlag
%if maximize='maximize'->sense=-1
%otherwise->sense=1
sense = 1 - 2 *strcmp(maximizeFlag, 'maximize');

%solver linear programming problem using chosen solver
varargout = cell(max(nargout, 1), 1);
switch options.solver(1:2)
    case 'gl'; [varargout{:}] = runglpk(   f, A, b, lb, ub, sense, constraintTypes, variableTypes, options);
    case 'li'; [varargout{:}] = runlinprog(f, A, b, lb, ub, sense, constraintTypes, variableTypes, options);
    case 'bp'; [varargout{:}] = runbpmpd(  f, A, b, lb, ub, sense, constraintTypes, variableTypes, options);
    case 'cl'; [varargout{:}] = runclp(    f, A, b, lb, ub, sense, constraintTypes, variableTypes, options);
    case 'lp'; [varargout{:}] = runlpsolve(f, A, b, lb, ub, sense, constraintTypes, variableTypes, options);
    case 'qs'; [varargout{:}] = runqsopt(  f, A, b, lb, ub, sense, constraintTypes, variableTypes, options);
    case 'to'; [varargout{:}] = runtomlab( f, A, b, lb, ub, sense, constraintTypes, variableTypes, options);
    case 'cp'; [varargout{:}] = runcplex(  f, A, b, lb, ub, sense, constraintTypes, variableTypes, options);
    case 'gu'; [varargout{:}] = rungurobi( f, A, b, lb, ub, sense, constraintTypes, variableTypes, options);
    case 'mo'; [varargout{:}] = runmosek(  f, A, b, lb, ub, sense, constraintTypes, variableTypes, options);
    otherwise; throw(MException('ComputationUtil:LinearProgramming', 'Support for %s not implemented', options.solver));
end

function [x, lambda, fopt, errorFlag, errorMsg, extra] = runlinprog(f, A, b, lb, ub, sense, constraintTypes, variableTypes, options)

%setup equality, inequality constraints based on constraint types
Aeq = [];
beq = [];
if sum(constraintTypes == 'S') == length(constraintTypes)
    Aeq = A;
    A = [];
    beq = b;
    b = [];
elseif sum(constraintTypes == 'U') == length(constraintTypes)
elseif sum(constraintTypes == 'L') == length(constraintTypes)
    A = -A;
    b = -b;
else
    throw(MException('ComputationUtil:LinearProgramming', 'linprog: constraint types invalid'));
end

%throw error if integer or boolean variable types requested, linprog
%doesn't have this capability
if sum(variableTypes == 'C') ~= length(variableTypes)
    throw(MException('ComputationUtil:LinearProgramming', 'linprog: variable types invalid'));
end

%linprog options
if isfield(options.solverOptions, 'linprog')
    linprogoptions = options.solverOptions.linprog;
else
    linprogoptions = struct;
end

try
    [x, fopt, exitflag, ~, lambda] = linprog(sense * f, A, b, Aeq, beq, lb, ub, [], linprogoptions);

    lambda = struct('reducedCosts', NaN(size(A, 2), 1), 'shadowPrices', lambda);
    fopt = fopt * sense;
    errorFlag = exitflag ~= 1;
    switch exitflag
        case  1; errorMsg = 'Function converged to a solution x.';
        case  0; errorMsg = 'Number of iterations exceeded options.MaxIter.';
        case -2; errorMsg = 'No feasible point was found.';
        case -3; errorMsg = 'Problem is unbounded.';
        case -4; errorMsg = 'NaN value was encountered during execution of the algorithm.';
        case -5; errorMsg = 'Both primal and dual problems are infeasible.';
        case -7; errorMsg = 'Search direction became too small. No further progress could be made.';
    end
catch exception
    errorFlag = 1;
    errorMsg = exception.message;
    x = NaN(size(lb));
    lambda = struct('reducedCosts', NaN(size(A, 2), 1), 'shadowPrices', NaN(size(A, 1), 1));
    fopt = NaN;
end

extra = struct;

function [x, lambda, fopt, errorFlag, errorMsg, extra] = runglpk(f, A, b, lb, ub, sense, constraintTypes, variableTypes, options)

%make structure of glpk options
if isfield(options.solverOptions, 'glpk')
    glpkoptions = options.solverOptions.glpk;
else
    glpkoptions = struct;
end

%call glpk
try
    [x, fopt, status, extra] = glpkcc(f, A, b, lb, ub, constraintTypes, variableTypes, sense, glpkoptions);
    lambda = struct('reducedCosts', extra.redcosts, 'shadowPrices', extra.lambda);
    errorFlag = ~(status == 5 || status == 2);
    switch status
        %General Errors
        case 5; errorMsg = 'Solution is optimal';
        case 2; errorMsg = 'Solution is feasible';
        case 1; errorMsg = 'Solution is undefined';
        case 3; errorMsg = 'Solution is infeasible';
        case 4; errorMsg = 'No feasible solution exists';
        case 6; errorMsg = 'Solution is unbounded';

        %Simplex method Errors:
        case 101; errorMsg = 'Invalid basis';
        case 102; errorMsg = 'Singular matrix';
        case 103; errorMsg = 'Ill-conditioned matrix';
        case 104; errorMsg = 'Invalid bounds';
        case 105; errorMsg = 'Solver failed';
        case 106; errorMsg = 'Objective lower limit reached';
        case 107; errorMsg = 'Objective upper limit reached';
        case 108; errorMsg = 'Iteration limit exceeded';
        case 109; errorMsg = 'Time limit exceeded';
        case 110; errorMsg = 'No primal feasible solution';

        %Interior point method, mixed integer problem Errors:
        case 204; errorMsg = 'Unable to start the search.';
        case 205; errorMsg = 'Objective function lower limit reached.';
        case 206; errorMsg = 'Objective function upper limit reached.';
        case 207; errorMsg = 'Iterations limit exhausted.';
        case 208; errorMsg = 'Time limit exhausted.';
        case 209; errorMsg = 'No feasible solution.';
        case 210; errorMsg = 'Numerical instability.';
        case 211; errorMsg = 'Problems with basis matrix.';
        case 212; errorMsg = 'No convergence (interior).';
        case 213; errorMsg = 'No primal feasible solution (LP presolver).';
        case 214; errorMsg = 'No dual feasible solution (LP presolver).';

        otherwise, errorMsg = sprintf('Invalid error code %d', status);
    end
catch exception
    errorFlag = 1;
    errorMsg = exception.message;
    x = NaN(size(lb));
    lambda = struct('reducedCosts', NaN(size(A, 2), 1), 'shadowPrices', NaN(size(A, 1), 1));
    fopt = NaN;
end

extra = struct;

function [x, lambda, fopt, errorFlag, errorMsg, extra] = runlpsolve(f, A, b, lb, ub, sense, constraintTypes, variableTypes, options)
%options
verbose = 3;
presolve = 0; %none
scaling = 4 + 64 + 128; %geometric + equilibrate + integers
if isfield(options.solverOptions, 'lp_solve')
    if isfield(options.solverOptions.lp_solve, 'verbose'), verbose = options.solverOptions.lp_solve.verbose; end
    if isfield(options.solverOptions.lp_solve, 'presolve'), presolve = options.solverOptions.lp_solve.presolve; end
    if isfield(options.solverOptions.lp_solve, 'scaling'), scaling = options.solverOptions.lp_solve.scaling; end
end

%convert variable types to lp_solve format'
if any(variableTypes == 'B'); throw(MException('ComputationUtil:LinearProgramming', 'lp_solve: boolean variables are not allowed')); end;
xint = NaN(size(variableTypes));
xint(variableTypes == 'I') = 1;
xint(variableTypes == 'C') = 0;

%convert constraints types to lp_solve format
if any(constraintTypes == 'F'); throw(MException('ComputationUtil:LinearProgramming', 'lp_solve: free constraints are not allowed')); end;
if any(constraintTypes == 'D'); throw(MException('ComputationUtil:LinearProgramming', 'lp_solve: double-sided constraints are not allowed')); end;
con_types = NaN(size(constraintTypes));
con_types(constraintTypes == 'L') = 2;
con_types(constraintTypes == 'S') = 3;
con_types(constraintTypes == 'U') = 1;

try
    lp = mxlpsolve('make_lp', size(A, 1), size(A, 2));
    mxlpsolve('set_verbose', lp, verbose);
    mxlpsolve('set_mat', lp, A);
    mxlpsolve('set_rh_vec', lp, b);
    mxlpsolve('set_obj_fn', lp, f);
    mxlpsolve('set_sense', lp, (sense == -1) + 0);
    mxlpsolve('set_constr_type', lp, con_types);
    mxlpsolve('set_bounds', lp, lb, ub);
    mxlpsolve('set_int', lp, xint);
    mxlpsolve('set_presolve', lp, presolve, mxlpsolve('get_presolveloops', lp));
    mxlpsolve('set_scaling', lp, scaling);

    status = mxlpsolve('solve', lp);
    errorFlag =~ status == 0 || status == 1 || status == 11 || status == 12;
    if ~errorFlag
        [fopt, x, duals] = mxlpsolve('get_solution', lp);
        reducedCosts = mxlpsolve('get_reduced_costs', lp);
    else
        fopt = NaN;
        x = NaN(size(lb));
        duals = NaN(size(A, 1), 1);
        reducedCosts = NaN(size(A, 2), 1);
    end

    extra = struct;
    if nargin >= 6
        tmpFileName = ['lpsolve.' datestr(now, 30) '.txt'];

        mxlpsolve('set_outputfile', lp, tmpFileName);
        mxlpsolve('print_scales', lp);

        fid = fopen(tmpFileName, 'r');
        fgetl(fid);
        fgetl(fid);
        line = fgetl(fid);
        extra.objScaling = str2double(line(32:end));
        extra.rowScaling = zeros(size(A, 1), 1);
        extra.colScaling = zeros(size(A, 2), 1);
        for i = 1:size(A, 1)
            line = fgetl(fid);
            extra.rowScaling(i) = str2double(line(32:end));
        end
        for i = 1:size(A, 2)
            line = fgetl(fid);
            extra.colScaling(i) = str2double(line(32:end));
        end
        fclose(fid);
    end

    mxlpsolve('delete_lp', lp);

    if nargin >= 6
        delete(tmpFileName);
    end

    lambda = struct('reducedCosts', reducedCosts, 'shadowPrices', duals);
    switch status
        case -2; errorMsg = 'Out of memory';
        case 0; errorMsg = 'An optimal solution was obtained';
        case 1; errorMsg = ['The model is sub-optimal. Only happens if there are integer variables and there is already an integer solution found. The solution is not guaranteed the most optimal one.\n'...
                '* A timeout occured (set via set_timeout or with the -timeout option in lp_solve)\n'...
                '* set_break_at_first was called so that the first found integer solution is found (-f option in lp_solve)\n'...
                '* set_break_at_value was called so that when integer solution is found that is better than the specified value that it stops (-o option in lp_solve)\n'...
                '* set_mip_gap was called (-g/-ga/-gr options in lp_solve) to specify a MIP gap\n'...
                '* An abort function is installed (put_abortfunc) and this function returned TRUE\n'...
                '* At some point not enough memory could not be allocated'];
        case 2; errorMsg = 'The model is infeasible';
        case 3; errorMsg = 'The model is unbounded';
        case 4; errorMsg = 'The model is degenerative';
        case 5; errorMsg = 'Numerical failure encountered';
        case 6; errorMsg = 'The abort routine returned TRUE. See put_abortfunc';
        case 7; errorMsg = 'A timeout occurred. A timeout was set via set_timeout';
        case 9; errorMsg = 'The model could be solved by presolve. This can only happen if presolve is active via set_presolve';
        case 10; errorMsg = 'The B&B routine failed';
        case 11; errorMsg = 'The B&B was stopped because of a break-at-first (see set_break_at_first) or a break-at-value (see set_break_at_value)';
        case 12; errorMsg = 'A feasible B&B solution was found';
        case 13; errorMsg = 'No feasible B&B solution found';
    end
catch exception
    errorFlag = 1;
    errorMsg = exception.message;
    x = NaN(size(lb));
    lambda = struct('reducedCosts', NaN(size(A, 2), 1), 'shadowPrices', NaN(size(A, 1), 1));
    fopt = NaN;
    extra = struct;
end

function [x, lambda, fopt, errorFlag, errorMsg, extra] = runqsopt(f, A, b, lb, ub, sense, constraintTypes, variableTypes, options)

%setup equality, inequality constraints based on constraint types
Aeq = [];
beq = [];
if sum(constraintTypes == 'S') == length(constraintTypes)
    Aeq = A;
    A = [];
    beq = b;
    b = [];
elseif sum(constraintTypes == 'U') == length(constraintTypes)
elseif sum(constraintTypes == 'L') == length(constraintTypes)
    A = -A;
    b = -b;
else
    throw(MException('ComputationUtil:LinearProgramming', 'qsopt: constraint types invalid'));
end

%throw error if integer or boolean variable types requested, qsopt
%doesn't have this capability
if sum(variableTypes == 'C') ~= length(variableTypes)
    throw(MException('ComputationUtil:LinearProgramming', 'qsopt: variable types invalid'));
end

%make structure of qsopt options
qsoptoptions = struct;
if isfield(options.solverOptions, 'qsopt')
    qsoptoptions = options.solverOptions.qsopt;
end

try
    [x, dual, status] = qsopt(sense * f, A, b, Aeq, beq, lb, ub, qsoptoptions);
    lambda = struct('reducedCosts', NaN(size(A, 2), 1), 'shadowPrices', dual);
    fopt = f' * x;
    errorFlag = status ~= 1;
    switch status
        case 1; errorMsg = 'optimal';
        case 2; errorMsg = 'infeasible';
        case 3; errorMsg = 'unbounded';
        case 4; errorMsg = 'iteration limit';
        case 5; errorMsg = 'time limit';
        case 6; errorMsg = 'other problem';
    end
catch exception
    errorFlag = 1;
    errorMsg = exception.message;
    x = NaN(size(lb));
    lambda = struct('reducedCosts', NaN(size(A, 2), 1), 'shadowPrices', NaN(size(A, 1), 1));
    fopt = NaN;
end

extra = struct;

function [x, lambda, fopt, errorFlag, errorMsg, extra] = runclp(f, A, b, lb, ub, sense, constraintTypes, variableTypes, options)

%setup equality, inequality constraints based on constraint types
Aeq = [];
beq = [];
if sum(constraintTypes == 'S') == length(constraintTypes)
    Aeq = A;
    A = [];
    beq = b;
    b = [];
elseif sum(constraintTypes == 'U') == length(constraintTypes)
elseif sum(constraintTypes == 'L') == length(constraintTypes)
    A = -A;
    b = -b;
else
    throw(MException('ComputationUtil:LinearProgramming', 'clp: constraint types invalid'));
end

%throw error if integer or boolean variable types requested, clp
%doesn't have this capability
if sum(variableTypes == 'C') ~= length(variableTypes)
    throw(MException('ComputationUtil:LinearProgramming', 'clp: variable types invalid'));
end

%make structure of clp options
clpoptions = struct;
if isfield(options.solverOptions, 'clp')
    clpoptions = options.solverOptions.clp;
end

try
    [x, dual, status] = clp(zeros(length(f)), sense * f, A, b, Aeq, beq, lb, ub, clpoptions);
    lambda = struct('reducedCosts', NaN(size(A, 2), 1), 'shadowPrices', dual);
    fopt = f' * x;
    errorFlag = status ~= 0;
    switch status
        case 0; errorMsg = 'optimal';
        case 1; errorMsg = 'infeasible';
        case 2; errorMsg = 'unbounded';
    end
catch exception
    errorFlag = 1;
    errorMsg = exception.message;
    x = NaN(size(lb));
    lambda = struct('reducedCosts', NaN(size(A, 2), 1), 'shadowPrices', NaN(size(A, 1), 1));
    fopt = NaN;
end

extra = struct;

function [x, lambda, fopt, errorFlag, errorMsg, extra] = runbpmpd(f, A, b, lb, ub, sense, constraintTypes, variableTypes, ~)

%throw error if integer or boolean variable types requested, bpmpd
%doesn't have this capability
if sum(variableTypes == 'C') ~= length(variableTypes)
    throw(MException('ComputationUtil:LinearProgramming', 'bpmpd: variable types invalid'));
end

%convert constraints types to bpmpd format
if any(constraintTypes == 'F'); throw(MException('ComputationUtil:LinearProgramming', 'bpmpd: free constraints are not allowed')); end;
if any(constraintTypes == 'D'); throw(MException('ComputationUtil:LinearProgramming', 'bpmpd: double-sided constraints are not allowed')); end;
e = NaN(size(constraintTypes));
e(constraintTypes == 'L') = 1;
e(constraintTypes == 'S') = 0;
e(constraintTypes == 'U') = -1;

%throw error if integer or boolean variable types requested, bpmpd
%doesn't have this capability
if sum(variableTypes == 'C') ~= length(variableTypes)
    throw(MException('ComputationUtil:LinearProgramming', 'bpmpd: variable types invalid'));
end

try
    llist = (1:length(lb))';
    ulist = (1:length(ub))';
    [x, dual, ~, w, errorMsg] = bp(zeros(length(f)), A, b, sense * f, e, llist, lb, ulist, ub, bpopt,0);
    lambda = struct('reducedCosts', w, 'shadowPrices', dual);
    fopt = f' * x;
    errorFlag =~ strcmp(errorMsg, 'optimal solution');
catch exception
    errorFlag = 1;
    errorMsg = exception.message;
    x = NaN(size(lb));
    lambda = struct('reducedCosts', NaN(size(A, 2), 1), 'shadowPrices', NaN(size(A, 1), 1));
    fopt = NaN;
end

extra = struct;

function [x, lambda, fopt, errorFlag, errorMsg, extra] = runtomlab( f, A, b, lb, ub, sense, constraintTypes, ~, options)

b_L = -Inf(size(b));
b_U =  Inf(size(b));
b_L(constraintTypes == 'L') = b(constraintTypes == 'L');
b_U(constraintTypes == 'U') = b(constraintTypes == 'U');
b_L(constraintTypes == 'S') = b(constraintTypes == 'S');
b_U(constraintTypes == 'S') = b(constraintTypes == 'S');
b_L(constraintTypes == 'D') = -b(constraintTypes == 'D');
b_U(constraintTypes == 'D') =  b(constraintTypes == 'D');

prob = lpAssign(sense * f, A, b_L, b_U, lb, ub, [], [], [], [], [], [], [], [], []);

solver = 'minos';
prob.optParam = struct();
PriLev = 0;
if isfield(options.solverOptions, 'tomlab')
    if isfield(options.solverOptions.tomlab, 'solver'), solver = options.solverOptions.tomlab.solver; end
    if isfield(options.solverOptions.tomlab, 'optParam'), prob.optParam = options.solverOptions.tomlab.optParam; end
    if isfield(options.solverOptions.tomlab, 'PriLev'), PriLev = options.solverOptions.tomlab.PriLev; end
end

try
    result = tomRun(solver, prob, PriLev);
    errorFlag = result.ExitFlag ~= 0;
    errorMsg = result.ExitText;
    fopt = sense * result.f_k;
    x = result.x_k;
    lambda = struct('reducedCosts', NaN(size(A, 2), 1), 'shadowPrices', NaN(size(A, 1), 1));
catch exception
    errorFlag = 1;
    errorMsg = exception.message;
    x = NaN(size(lb));
    lambda = struct('reducedCosts', NaN(size(A, 2), 1), 'shadowPrices', NaN(size(A, 1), 1));
    fopt = NaN;
end

extra = struct;

function [x, lambda, fopt, errorFlag, errorMsg, extra] = runcplex(f, A, b, lb, ub, sense, constraintTypes, variableTypes, options)
constraintTypes = upper(constraintTypes);
if ~ismembc(constraintTypes, 'LSU')
    throw(MException('ComputationUtil:LinearProgramming', 'linear programming: invalid constraints types'));
end

Aineq = [
    -A(constraintTypes == 'L', :)
    A(constraintTypes == 'U', :)
    ];
bineq = [
    -b(constraintTypes == 'L', :)
    b(constraintTypes == 'U', :)
    ];

Aeq = A(constraintTypes == 'S', :);
beq = b(constraintTypes == 'S', :);

try
    [x, fopt, exitFlag, output] =  cplexmilp(...
        sense * f, ...
        Aineq, bineq, ...
        Aeq, beq, ...
        [], [], [], lb, ub, variableTypes');
    errorFlag = exitFlag ~= 1;
    errorMsg = output.message;
catch exception
    errorFlag = 1;
    errorMsg = exception.message;
    x = NaN(size(lb));
    fopt = NaN;
end
lambda = struct('reducedCosts', NaN(size(A, 2), 1), 'shadowPrices', NaN(size(A, 1), 1));

extra = struct('status', output.cplexstatus);

function [x, lambda, fopt, errorFlag, errorMsg, extra] = rungurobi( f, A, b, lb, ub, sense, constraintTypes, variableTypes, options)
b_L = -Inf(size(b));
b_U =  Inf(size(b));
b_L(constraintTypes == 'L') = b(constraintTypes == 'L');
b_U(constraintTypes == 'U') = b(constraintTypes == 'U');
b_L(constraintTypes == 'S') = b(constraintTypes == 'S');
b_U(constraintTypes == 'S') = b(constraintTypes == 'S');
b_L(constraintTypes == 'D') = -b(constraintTypes == 'D');
b_U(constraintTypes == 'D') =  b(constraintTypes == 'D');

xint = NaN(size(variableTypes));
xint(variableTypes == 'I') = 1;
xint(variableTypes == 'C') = 0;

PriLev = 0;
grbControl = struct();
if isfield(options.solverOptions, 'gurobi')
    if isfield(options.solverOptions.gurobi, 'PriLev'), PriLev = options.solverOptions.gurobi.PriLev; end
    if isfield(options.solverOptions.gurobi, 'grbControl'), grbControl = options.solverOptions.gurobi.grbControl; end
end

try
    [x, ~, v, rc, fopt, ~, ~, status] = ...
        gurobi(sense * f, A, lb, ub, b_L, b_U, [], ...
        grbControl, PriLev, xint);
    fopt = fopt * sense;
    [errorMsg, exitFlag] = grbStatus(status);
    errorFlag = exitFlag ~= 0;
    lambda = struct('reducedCosts', rc, 'shadowPrices', v);
catch exception
    errorFlag = 1;
    errorMsg = exception.message;
    x = NaN(size(lb));
    lambda = struct('reducedCosts', NaN(size(A, 2), 1), 'shadowPrices', NaN(size(A, 1), 1));
    fopt = NaN;
end

extra = struct;

function [x, lambda, fopt, errorFlag, errorMsg, extra] = runmosek( f, A, b, lb, ub, sense, constraintTypes, ~, options)
if sense == 1
    cmd = 'minimize';
else
    cmd = 'maximize';
end

b_L = -Inf(size(b));
b_U =  Inf(size(b));
b_L(constraintTypes == 'L') = b(constraintTypes == 'L');
b_U(constraintTypes == 'U') = b(constraintTypes == 'U');
b_L(constraintTypes == 'S') = b(constraintTypes == 'S');
b_U(constraintTypes == 'S') = b(constraintTypes == 'S');
b_L(constraintTypes == 'D') = -b(constraintTypes == 'D');
b_U(constraintTypes == 'D') =  b(constraintTypes == 'D');

lb = max(lb, -1e7);
ub = min(ub,  1e7);

opts = struct(...
    'MSK_IPAR_LOG', 0, 'MSK_IPAR_MAX_NUM_WARNINGS', 1e5, ...
    'MSK_IPAR_INTPNT_SCALING', 0, 'MSK_IPAR_SIM_SCALING', 0, 'MSK_IPAR_SIM_SCALING_METHOD', 1);
if isfield(options.solverOptions, 'mosek')
    opts = options.solverOptions.mosek;
end

try
    result = msklpopt(f, A, b_L, b_U, lb, ub, opts, cmd);
    fopt = result.sol.itr.pobjval;
    x = result.sol.itr.xx;
    errorFlag = result.rcode ~= 0;
    errorMsg = result.rmsg;
    lambda = struct(...
        'reducedCosts', result.sol.itr.slx + result.sol.itr.sux, ...
        'shadowPrices', result.sol.itr.slc + result.sol.itr.suc);
catch exception
    errorFlag = 1;
    errorMsg = exception.message;
    x = NaN(size(lb));
    lambda = struct('reducedCosts', NaN(size(A, 2), 1), 'shadowPrices', NaN(size(A, 1), 1));
    fopt = NaN;
end

extra = struct;


classdef ICMA < ALGORITHM
% <multi> <real/integer> <constrained>
% Indicator-based constrained multi-objective algorithm

%------------------------------- Reference --------------------------------
% J. Yuan, H. Liu, Y. Ong, and Z. He, Indicator-based evolutionary
% algorithm for solving constrained multi-objective optimization problems,
% IEEE Transactions on Evolutionary Computation, 2022, 26(2): 379-391.
%------------------------------- Copyright --------------------------------
% Copyright (c) 2023 BIMK Group. You are free to use the PlatEMO for
% research purposes. All publications which use this platform or any code
% in the platform should acknowledge the use of "PlatEMO" and reference "Ye
% Tian, Ran Cheng, Xingyi Zhang, and Yaochu Jin, PlatEMO: A MATLAB platform
% for evolutionary multi-objective optimization [educational forum], IEEE
% Computational Intelligence Magazine, 2017, 12(4): 73-87".
%--------------------------------------------------------------------------

% This function is written by Jiawei Yuan

    methods
        function main(Algorithm,Problem)
            %% Generate the random population
            Population = Problem.Initialization();
            Zmin       = min(Population.objs,[],1);
            Fmin       = min(Population(all(Population.cons<=0,2)).objs,[],1);
            Archive    = Population;
            W          = UniformPoint(Problem.N,Problem.M);
            Ra         = 1;

            %% Optimization
            while Algorithm.NotTerminated(Archive)
                Nt = floor(Ra*Problem.N);
                MatingPool = [Population(randsample(Problem.N,Nt)),Archive(randsample(Problem.N,Problem.N-Nt))];

                [Mate1,Mate2,Mate3] = Neighbor_Pairing_Strategy(MatingPool,Zmin);
                if rand > 0.5
                    Offspring = OperatorDE(Problem,Mate1,Mate2,Mate3);
                else
                    Offspring = OperatorDE(Problem,Mate1,Mate2,Mate3,{0.5,0.5,0.5,0.75});
                end

                Fmin = min([Fmin;Offspring(all(Offspring.cons<=0,2)).objs],[],1);
                Zmin = min([Zmin;Offspring.objs],[],1);
                [Population,Archive] = ICMA_Update([Population,Offspring,Archive],Problem.N,W,Zmin,Fmin);

                Ra = 1 - Problem.FE/Problem.maxFE;
            end
        end
    end
end


%compute dxheadsm_mm

function [data,units]=compute_dxheadsm_mm(trx,n)

larvae=trx.exp2flies{n};
numlarvae=numel(larvae);
dxheadsm_mm=cell(1,numlarvae);
for i=1:numlarvae
    larva=larvae(i);
    dxheadsm_mm{1,i}=(trx(larva).xheadsm_mm(2:end)-trx(larva).xheadsm_mm(1:end-1))./trx(larva).dt;
end

units=parseunits('mm/s');
data=dxheadsm_mm;


function joint = xyzhumaneva2joint(pos,transf)

% XYZHUMANEVA2JOINT
%
% COPYRIGHT : Carl Henrik Ek and Neil Lawrence, 2008

% MOCAP

joint(:,1) = pos(1:3:end);
joint(:,2) = pos(2:3:end);
joint(:,3) = pos(3:3:end);

if(exist('transf','var'))
  joint = joint - repmat(transf.center,size(joint,1),1);
  M = rotationMatrix(transf.x,transf.y,transf.z);
  joint = joint*M;
  joint = joint + repmat(transf.center,size(joint,1),1);
end

return


function [featuresM,volToEval,mask3M,minr,maxr,minc,maxc,mins,maxs] = getLawsAndHaralickFeatures(structNum,...
    rowMargin,colMargin,slcMargin,minIntensity,maxIntensity,planC,varargin)

% --- Optional inputs ---
% varargin{1} : haralOnlyFlag (default : 1)
% varargin{2} : Vector of flags indicating haralick textures to be computed
%               (default : all ones)
%               flagV = [energyFlg,entropyFlag,sumAvgFlg,homogFlg,...
%               contrastFlg,corrFlg,clustShadFlg,clustPromFlg,haralCorrFlg];
% varargin{3} : No. gray levels   (default:32 )
% varargin{4} : patchRadius       (default:[1,2])
% ------------------------
% AI 9/1/17 Also returns volToEval, mask3M, boundingbox coords
% AI 9/7/17 Added optional inputs

if ~exist('planC','var')
    global planC
end
indexS = planC{end};

% Get optional inputs
minargs = 7;
maxargs = 8;
nOptArgs = nargin - minargs;
optC = {1,ones(1,9),32,[1,2]};
[optC{1:nOptArgs}] = varargin{:};
[haralOnlyFlag,flagV,nLevel,patchRadius] = optC{:};

% get the region of interest
if ~iscell(structNum)
    [volToEval,maskBoundingBox3M] = ...
        getROI(structNum,rowMargin,colMargin,slcMargin,planC);
else
    volToEval = structNum{1};
    maskBoundingBox3M = structNum{2};
end

nanIntenityV = volToEval < -400;

% Generate Law's texture
if ~haralOnlyFlag
    % padd with mean intensities
    meanVol = nanmean(volToEval(:));
    if exist('padarray.m','file')
        paddedVolM = padarray(volToEval,[5 5 5],meanVol,'both');
    else
        paddedVolM = padarray_oct(volToEval,[5 5 5],meanVol,'both');
    end
    lawsMasksS = getLawsMasks();

    fieldNamesC = fieldnames(lawsMasksS);
    numFeatures = length(fieldNamesC);
    % initialize features matrix
    featuresM = zeros(sum(maskBoundingBox3M(~nanIntenityV)),numFeatures);
    for i = 1:numFeatures
        disp(i)
        text3M = convn(paddedVolM,lawsMasksS.(fieldNamesC{i}),'same');
        text3M = text3M(6:end-5,6:end-5,6:end-5);
        % featuresM(:,i) = text3M(maskBoundingBox3M); % for non cubic roi
        featuresM(:,i) = text3M(~nanIntenityV); % for the entire cubic roi
    end

else
    featuresM = [];
end

% Intensity as a feature
featuresM(:,end+1) = volToEval(~nanIntenityV);

% Filter intensities
nanIntenityV = volToEval < -400;
%nanIntenityV = false(size(nanIntenityV));
volToEval(nanIntenityV) = NaN;
% volToEval(volToEval > maxIntensity) = NaN;

% Genarate Haralick textures
separateDirnFlag = 1;
nOff = 1;
if separateDirnFlag
    offsetsM = getOffsets(2);
    nOff = size(offsetsM,1);
    numVox = sum(~nanIntenityV(:));
    nanIntenityV = repmat(nanIntenityV,[1 1 1 nOff]);
end

%featuresM = zeros(sum(maskBoundingBox3M(:)),0);
for patchSiz = patchRadius

    patchSizeV  = [patchSiz patchSiz 0];

    for numGrLevels = nLevel
        offsetsM = getOffsets(2);

        % Haralick texture
        waitH = NaN;

        [energy3M,entropy3M,sumAvg3M,corr3M,invDiffMom3M,contrast3M,...
            clustShade3M,clustPromin3M,haralCorr3M] = gpuTextureByPatch(volToEval,...
            numGrLevels,patchSizeV,offsetsM,flagV,waitH,minIntensity,maxIntensity,separateDirnFlag);

        tempC = {energy3M,entropy3M,sumAvg3M,corr3M,invDiffMom3M,contrast3M,...
            clustShade3M,clustPromin3M,haralCorr3M};
        idx = find(flagV);
        for n = 1:length(idx)
            featuresM(:,end+1:end+nOff) = reshape(tempC{idx(n)}(~nanIntenityV),numVox,nOff);
        end

    end % gray level
end % patch radius


% Author: Ricardo Baptista and Matthias Poloczek
% Date:   June 2018
%
% See LICENSE.md for copyright information
%

function particle_system = move_particles(particle_system)
% MOVE_PARTICLES: Function moves the current particles according to an MCMC
% kernel with an adaptive logistic regression proposal.

% Extract function inputs
n_vars     = particle_system.n_vars;
model_orig = particle_system.models;
model_val  = particle_system.model_val;
rho        = particle_system.rho;
logistic_A = particle_system.logistic_A;

% Determine the number of current models, and dimensions
[n_models, ~] = size(model_orig);

% Compute the old particle diversity ratio
diversity_old = unique_particles(model_orig);

% Declare vector for accept_ratios
mh_accept_vect = [];

total_fn_count = particle_system.total_fn_count(end);

iter_temp = 0;
while(1)

    % Generate matrix to store new samples and data
    new_model = zeros(n_models, n_vars);
    new_mval  = zeros(n_models, 1);

    % Declare vector to store acceptance ratio
    accept_ratio = 0;

    for i=1:n_models

        % Propose new model
        [prop_model, ~] = logistic_sample(n_vars, logistic_A);

        % Evaluate proposal density under both models
        q_new = evaluate_logit(particle_system, prop_model);
        q_old = evaluate_logit(particle_system, model_orig(i,:));

        % Evaluate probability function for both models
        total_fn_count = total_fn_count + 1;
        model_obj_new = particle_system.objective(prop_model);
        log_post_new = rho*model_obj_new;
        log_post_old = rho*model_val(i);

        % Compute acceptance probability
        accept_prob = exp(log_post_new - log_post_old)*(q_old/q_new);

        % Accept proposed sample/data or assign old values
        if rand < min(1,accept_prob)
            new_model(i,:) = prop_model;
            new_mval(i)    = model_obj_new;
            accept_ratio   = accept_ratio + 1;
        else
            new_model(i,:) = model_orig(i,:);
            new_mval(i)    = model_val(i);
        end

    end

    %% Update Sample Information

    % Update variables
    model_orig     = new_model;
    model_val      = new_mval;
    mh_accept_vect = [mh_accept_vect, accept_ratio/n_models];

    % Compute the new particle diversity
    diversity_new = unique_particles(new_model);

    % Compare the diversity
    if (abs(diversity_new - diversity_old) < 0.02 || diversity_new > 0.95)
        break
    end

    % Update diversity_old
    diversity_old = diversity_new;

    iter_temp = iter_temp + 1;
    if iter_temp > 100
        break
    end

end

% Update particle_system
particle_system.models     = model_orig;
particle_system.model_val  = model_val;
particle_system.mh_accept  = min(mh_accept_vect);
particle_system.total_fn_count = [particle_system.total_fn_count, total_fn_count];

end


function yp=predict(tb,yb,n,f_unk,B_un,mass);

% functions of the generalized state equations of a Linear System

g(1:n,1)=yb(n+1:2*n);
[stiff,damp,xkp,xcp]=kcm(n,yb);
g(n+1:2*n)=B_un*f_unk+inv(mass)*(-stiff*yb(1:n)-damp*yb(n+1:2*n));
g(2*n+1:3*n+2)=zeros(n+2,1);

yp =g;


function M = slpweval(X1, X2, f, varargin)
%SLPWEVAL Perform pairwise computation
%
% $ Syntax $
%   - M = slpweval(X1, X2, f)
%   - M = slpweval(X1, X2, f, ...)
%
% $ Arguments $
%   - X1:       the matrix of vectors serving as first argument of f
%   - X2:       the matrix of vectors serving as second argument of f
%   - f:        the function maps two vectors to a single scalar value
%   - M:        the matrix of pairwise evaluaton results
%
% $ Description $
%   - M = slpweval(X1, X2, f) takes vector arguments from X1 and X2, and
%     computes the pairwise evaluation result with f. The vectors in X1
%     and X2 are stored in a column-wise manner. Suppose X1 and X2 have
%     m and n columns respectively, then the resultant matrix M would be
%     of size m x n, with the M(i, j) = f(X1(:,i), X2(:,j)).
%
%   - M = slpweval(X1, X2, f, ...) conducts the computation with extra
%     parameters to f, i.e. M(i, j) = f(X1(:,i), X2(:,j), ...).
%
% $ Remarks $
%   - The vector length of the vectors in X1 and X2 are not necessarily
%     equal. The requirment on their dimensions depends on the callback
%     function f.
%   - For efficiency, the function would invoke f to evaluate in batch.
%     Thus f should support batch-evaluation. When the input arguments
%     to f have n columns, f should return an 1 x n row vector.
%
% $ History $
%   - Created by Dahua Lin on Apr 21, 2006
%   - Modified by Dahua Lin on Sep 10, 2006
%       - make some minor changes to suppress warnings
%

%% parse and verify input arguments

if nargin < 3
    raise_lackinput('slpweval', 3);
end
[d1, n1] = size(X1);
[d2, n2] = size(X2);
slignorevars(d1, d2);

%% compute

% prepare output matrix
M = zeros(n1, n2);

if n1 > n2      % expand each column in X2 to n1 copies

    inds_e = ones(1, n1);
    for i = 1 : n2
        x2 = X2(:, i);
        X2e = x2(:, inds_e);
        M(:, i) = feval(f, X1, X2e, varargin{:})';
    end

else            % expand each column in X1 to n2 copies

    inds_e = ones(1, n2);
    for i = 1 : n1
        x1 = X1(:, i);
        X1e = x1(:, inds_e);
        M(i, :) = feval(f, X1e, X2, varargin{:});
    end

end


function linpack_s_test04 ( )

%*****************************************************************************80
%
%% TEST04 tests SGBCO.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    24 June 2009
%
%  Author:
%
%    John Burkardt
%
  n = 10;
  ml = 1;
  mu = 1;
  lda = 2 * ml + mu + 1;

  fprintf ( 1, '\n' );
  fprintf ( 1, 'TEST04\n' );
  fprintf ( 1, '  For a general banded matrix,\n' );
  fprintf ( 1, '  SGBCO estimates the reciprocal condition number.\n' );
  fprintf ( 1, '  The matrix size is N = %d\n', n );
%
%  Set the matrix A.
%
  m = ml + mu + 1;
  fprintf ( 1, '  The bandwidth of the matrix is %d\n', m );

  for j = 1 : n
    a(m-1,j) = -1.0;
    a(m,  j) =  2.0;
    a(m+1,j) = -1.0;
  end
%
%  Estimate the condition.
%
  fprintf ( 1, '\n' );
  fprintf ( 1, '  Estimate the condition.\n' );

  [ a, ipivot, rcond, z ] = sgbco ( a, lda, n, ml, mu );

  fprintf ( 1, '\n' );
  fprintf ( 1, '  Estimated reciprocal condition = %f\n', rcond );

  return
end


function blas1_d_test08 ( )

%*****************************************************************************80
%
%% TEST08 tests DROTG.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    09 November 2006
%
%  Author:
%
%    John Burkardt
%
  test_num = 5;

  fprintf ( 1, '\n' );
  fprintf ( 1, 'TEST08\n' );
  fprintf ( 1, '  DROTG generates a real Givens rotation\n' );
  fprintf ( 1, '    (  C  S ) * ( A ) = ( R )\n' );
  fprintf ( 1, '    ( -S  C )   ( B )   ( 0 )\n' );
  fprintf ( 1, '\n' );

  seed = 123456789;

  for test = 1 : test_num

    [ a, seed ] = r8_uniform_01 ( seed );
    [ b, seed ] = r8_uniform_01 ( seed );

    [ c, s, r, z ] = drotg ( a, b );

    fprintf ( 1, '\n' );
    fprintf ( 1, '  A =  %f  B =  %f\n', a, b );
    fprintf ( 1, '  C =  %f  S =  %f\n', c, s );
    fprintf ( 1, '  R =  %f  Z =  %f\n', r, z );
    fprintf ( 1, '   C*A+S*B = %f\n',  c * a + s * b );
    fprintf ( 1, '  -S*A+C*B = %f\n', -s * a + c * b );

  end

  return
end


%kvdiskstr 'Generates a disk structuring element'
% This MatLab function was automatically generated by a converter (KhorosToMatLab) from the Khoros vdiskstr.pane file
%
% Parameters:
% OutputFile: o 'Output Struc File', required: 'output resulting struc file definition'
% InputFile: trigger 'Trigger Input ', optional: 'trigger input'
% Integer: d 'Disk Diameter ', default: 5: 'diameter of the disk to be created'
% Integer: off 'Nonplanar offset', default: 0: 'value to be added to nonplanar elements'
% MultiChoice: t 'Select Metric', default: 1: 'choose euclidean metric'
% String: t 'Euclidean ', default: 'euclidean': 'choose city-block metric'
% String: t 'City-Block', default: 'city-block': 'choose chessboard metric'
% String: t 'Chessboard', default: 'chessboard': 'lets user choose which kind of disk is to be created'
%    Choices are:
%
% Example: o = kvdiskstr(trigger, {'o','';'trigger','';'d',5;'off',0;'t',1;'t','euclidean';'t','city-block';'t','chessboard'})
%
% Khoros helpfile follows below:
%
%  PROGRAM
% vdiskstr - Generates a disk structuring element
%
%  DESCRIPTION
% .I vdiskstr
% generates special structuring elements which are disks in one of three well-known
% metrics (2-D case):
%
%  _
% Euclidean : D(p,q) = sqrt( (px-qx)^2 + (py-qy)^2 )
%
%  _
% City-Block: D(p,q) = |px-qx| + |py-qy|
%
%  _
% Chessboard: D(p,q) = max( |px-qx| , |py-qy| )
%
% Disks in each one of the above metrics are respectively circles,
% diamonds and squares. For a concise review of this subject, see Gonzales &
% Wintz, "Digital Image Processing", chapter 2.
%
% The disks generated may be planar or nonplanar, 2-dimensional or 3-dimensional.
% The default is euclidean planar 2-D.
%
% The offset makes sense only for the nonplanar case, in which case the value
% specified is added to the element. If no offset or 0 is specified, the element
% values will be adjusted so as to have zero in the borders. For example, a
% 5x5 chessboard nonplanar 2-D element will look like this:
%                           0 0 0 0 0
%                           0 1 1 1 0
%                           0 1 2 1 0
%                           0 1 1 1 0
%                           0 0 0 0 0
%
% The disk-like structuring elements  are very useful in many morphological operations
% such as  distance transforms, granulometry, skeleton algorithms etc.
%
%
%
%  EXAMPLES
%
% vdiskstr -r 3  -t "city-block"
% Creates a 2-D disk-like structuring element of radius 3 according to the
% city-block metric.
%
%  "SEE ALSO"
% lvdiskstr(3)
%
%  RESTRICTIONS
%
%  REFERENCES
%
%  COPYRIGHT
% Copyright (C) 1993-1997 Junior Barrera, Roberto Lotufo.  All rights reserved.
%

function varargout = kvdiskstr(varargin)
if nargin ==0
  Inputs={};arglist={'',''};
elseif nargin ==1
  Inputs=varargin{1};arglist={'',''};
elseif nargin ==2
  Inputs=varargin{1}; arglist=varargin{2};
else error('Usage: [out1,..] = kvdiskstr(Inputs,arglist).');
end
if size(arglist,2)~=2
  error('arglist must be of form {''ParameterTag1'',value1;''ParameterTag2'',value2}')
 end
narglist={'o', '__output';'trigger', '__input';'d', 5;'off', 0;'t', 1;'t', 'euclidean';'t', 'city-block';'t', 'chessboard'};
maxval={0,1,2,0,0,0,0,0};
minval={0,1,2,0,0,0,0,0};
istoggle=[0,1,0,0,0,1,1,1];
was_set=istoggle * 0;
paramtype={'OutputFile','InputFile','Integer','Integer','MultiChoice','String','String','String'};
% identify the input arrays and assign them to the arguments as stated by the user
if ~iscell(Inputs)
Inputs = {Inputs};
end
NumReqOutputs=1; nextinput=1; nextoutput=1;
  for ii=1:size(arglist,1)
  wasmatched=0;
  for jj=1:size(narglist,1)
   if strcmp(arglist{ii,1},narglist{jj,1})  % a given argument was matched to the possible arguments
     wasmatched = 1;
     was_set(jj) = 1;
     if strcmp(narglist{jj,2}, '__input')
      if (nextinput > length(Inputs))
        error(['Input ' narglist{jj,1} ' has no corresponding input!']);
      end
      narglist{jj,2} = 'OK_in';
      nextinput = nextinput + 1;
     elseif strcmp(narglist{jj,2}, '__output')
      if (nextoutput > nargout)
        error(['Output nr. ' narglist{jj,1} ' is not present in the assignment list of outputs !']);
      end
      if (isempty(arglist{ii,2}))
        narglist{jj,2} = 'OK_out';
      else
        narglist{jj,2} = arglist{ii,2};
      end

      nextoutput = nextoutput + 1;
      if (minval{jj} == 0)
         NumReqOutputs = NumReqOutputs - 1;
      end
     elseif isstr(arglist{ii,2})
      narglist{jj,2} = arglist{ii,2};
     else
        if strcmp(paramtype{jj}, 'Integer') & (round(arglist{ii,2}) ~= arglist{ii,2})
            error(['Argument ' arglist{ii,1} ' is of integer type but non-integer number ' arglist{ii,2} ' was supplied']);
        end
        if (minval{jj} ~= 0 | maxval{jj} ~= 0)
          if (minval{jj} == 1 & maxval{jj} == 1 & arglist{ii,2} < 0)
            error(['Argument ' arglist{ii,1} ' must be bigger or equal to zero!']);
          elseif (minval{jj} == -1 & maxval{jj} == -1 & arglist{ii,2} > 0)
            error(['Argument ' arglist{ii,1} ' must be smaller or equal to zero!']);
          elseif (minval{jj} == 2 & maxval{jj} == 2 & arglist{ii,2} <= 0)
            error(['Argument ' arglist{ii,1} ' must be bigger than zero!']);
          elseif (minval{jj} == -2 & maxval{jj} == -2 & arglist{ii,2} >= 0)
            error(['Argument ' arglist{ii,1} ' must be smaller than zero!']);
          elseif (minval{jj} ~= maxval{jj} & arglist{ii,2} < minval{jj})
            error(['Argument ' arglist{ii,1} ' must be bigger than ' num2str(minval{jj})]);
          elseif (minval{jj} ~= maxval{jj} & arglist{ii,2} > maxval{jj})
            error(['Argument ' arglist{ii,1} ' must be smaller than ' num2str(maxval{jj})]);
          end
        end
     end
     if ~strcmp(narglist{jj,2},'OK_out') &  ~strcmp(narglist{jj,2},'OK_in')
       narglist{jj,2} = arglist{ii,2};
     end
   end
   end
   if (wasmatched == 0 & ~strcmp(arglist{ii,1},''))
        error(['Argument ' arglist{ii,1} ' is not a valid argument for this function']);
   end
end
% match the remaining inputs/outputs to the unused arguments and test for missing required inputs
 for jj=1:size(narglist,1)
     if  strcmp(paramtype{jj}, 'Toggle')
        if (narglist{jj,2} ==0)
          narglist{jj,1} = '';
        end;
        narglist{jj,2} = '';
     end;
     if  ~strcmp(narglist{jj,2},'__input') && ~strcmp(narglist{jj,2},'__output') && istoggle(jj) && ~ was_set(jj)
          narglist{jj,1} = '';
          narglist{jj,2} = '';
     end;
     if strcmp(narglist{jj,2}, '__input')
      if (minval{jj} == 0)  % meaning this input is required
        if (nextinput > size(Inputs))
           error(['Required input ' narglist{jj,1} ' has no corresponding input in the list!']);
        else
          narglist{jj,2} = 'OK_in';
          nextinput = nextinput + 1;
        end
      else  % this is an optional input
        if (nextinput <= length(Inputs))
          narglist{jj,2} = 'OK_in';
          nextinput = nextinput + 1;
        else
          narglist{jj,1} = '';
          narglist{jj,2} = '';
        end;
      end;
     else
     if strcmp(narglist{jj,2}, '__output')
      if (minval{jj} == 0) % this is a required output
        if (nextoutput > nargout & nargout > 1)
           error(['Required output ' narglist{jj,1} ' is not stated in the assignment list!']);
        else
          narglist{jj,2} = 'OK_out';
          nextoutput = nextoutput + 1;
          NumReqOutputs = NumReqOutputs-1;
        end
      else % this is an optional output
        if (nargout - nextoutput >= NumReqOutputs)
          narglist{jj,2} = 'OK_out';
          nextoutput = nextoutput + 1;
        else
          narglist{jj,1} = '';
          narglist{jj,2} = '';
        end;
      end
     end
  end
end
if nargout
   varargout = cell(1,nargout);
else
  varargout = cell(1,1);
end
global KhorosRoot
if exist('KhorosRoot') && ~isempty(KhorosRoot)
w=['"' KhorosRoot];
else
if ispc
  w='"C:\Program Files\dip\khorosBin\';
else
[s,w] = system('which cantata');
w=['"' w(1:end-8)];
end
end
[varargout{:}]=callKhoros([w 'vdiskstr"  '],Inputs,narglist);


function inside = p15_inside ( m, n, point )

%*****************************************************************************80
%
%% P15_INSIDE reports if a point is inside the region in problem 15.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    12 April 2009
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer M, the spatial dimension.
%
%    Input, integer N, the number of points.
%
%    Input, real POINT(M,N), the coordinates of the points.
%
%    Output, logical INSIDE(N), is TRUE if the point is in the region.
%
  inside(1:n) =                                          ...
       ( -8.0 <= point(1,1:n) & point(1,1:n) <= 2.0 &    ...
         -1.0 <= point(2,1:n) & point(2,1:n) <= 0.0    ) ...
    |                                                    ...
       ( -2.0 <= point(1,1:n) & point(1,1:n) <= 8.0 &    ...
          0.0 <= point(2,1:n) & point(2,1:n) <= 1.0 );

  return
end


function out = regress(dat, varargin)
% Multiple regression with an fmri_data object (dat), predicting brain data with a design matrix stored in dat.X (or vice versa)
%
% Regress dat.X on dat.dat at each voxel, and return voxel-wise statistic
% images. Each column of dat.X is a predictor in a multiple regression,
% and the intercept is the last column. Intercept will automatically be
% added if not detected unless 'nointercept' is specified.
%
% Key pointers:
% - Output structure (regression_results_ols) contains beta and t images in statistic_image objects
%
% - If contrasts are entered, output structure contains contrast images and contrast t images in statistic_image objects
%
% - T images are thresholded, but beta/contrast images are not. These can be re-thresholded with the threshold( ) method
%
% - statistic_image objects can be visuallized with any CANlab display methods, e.g., surface( ), montage( ), and orthviews( )
%
% - Use the get_wh_image( ) method to select specific beta, t, contrast, contrast t images from statistic_image objects
%
% - Enter condition names and contrast names as cell vectors of strings Labels are saved in the .image_labels property in output objects
%
% - Some diagnostics, including VIFs and leverages, are automatically checked and returned
%
% - There is an option for robust regression, specified using the 'robust' flag
%
% - For first-level models (only), the grandmeanscale option is recommended to increase homogeneity in scale across participants
%
% - The regress( ) method does not use covariates field of fmri_data(). You must include covariates
%    manually in dat.X.
%
% - The regress( ) method can also create a map of brain regions that predict the dat.Y vector using the 'brainony' option.
%   This is essentially a univariate version of the 'predict' command.  Warning: this is very slow as it loops
%   through all voxels.
%
% Creates thresholded plot by default
%
% :Usage:
% ::
%
%    out = regress(dat, varargin)
%
% :Inputs:
%  **dat:**
%        should be an fmri_data object with X field defined.
%        dat.X can be a design_matrix() object.
%
% :Optional Inputs:
%  **[threshold, 'unc']:**
%        p-value threshold string indicating threshold type
%        (see help statistic_image.threshold for options)
%
%  **robust:**
%        Run a robust regression (default is OLS).  Robust is considerably
%        slower than OLS
%
%  **grandmeanscale:**
%        Scale overall grand mean to a value of 100. Intended to reduce inter-subject
%        variability in 1st-level analysis (single-subject) when doing
%        multi-subject group analyses on resulting contrasts/beta images.
%        Assumes mask and overall brain size are consistent across replicates (e.g., participants, in a first-level analysis)
%        Do not use for 2nd-level or standard single-level analyses.
%
%  **C**
%       Followed by a contrast matrix, each column is a contrast across conditions/events
%       [k x c] matrix, where k = size(X, 2) and c is number of contrasts
%
%       Note: k must be the number including the intercept. If your input
%       model X does not include an intercept, contrast matrix must have
%       one more element than input X has rows, to account for the added
%       intercept.
%
%  **nointercept:**
%        Do not add intercept to model
%
%  **display, display_results:**
%        Show thresholded results usin orthviews (if < 10 regressors)
%
%  **nodisplay:**
%        Do not plot thresholded results using orthviews
%
%  **brainony:**
%        univariate approach to predict obj.Y from brain data
%
%  **residual:**
%        Output residual as fmri_data() object
%
%  **noverbose:**
%        Suppress verbose outputs
%
%  **variable_names:** (or 'names')
%       Followed by a cell array of variable/regressor names, for
%       non-intercept regressors
%
%  **contrast_names:**
%       Followed by a cell array of contrast names
%
%  **analysis_name:**
%       Followed by a string with a name/description for this analysis.
%
%
% :Outputs:
%
%  **out:**
%        A structure containing stats_img and fmri_data objects.
%        In addition to the main outputs below, the out structure also has
%        fields for input_parameters, the design matrix (X), variable
%        names, and warnings.
%
%  **out.b:**
%        stats_img object of beta values estimated from regression
%
%  **out.t:**
%        stats_img object of t-values with input threshold
%
%  **out.df:**
%        fmri_data object of degrees of freedom
%
%  **out.sigma:**
%        fmri_data object of variance of residual
%
%  **out.residual:**
%        fmri_data object of residual data after model has been regressed out (optional).
%
%  **out.diagnostics:***
%        A structure containing VIFs and leverage values for the design matrix
%        out.diagnostics.Variance_inflation_factors = VIFs
%        out.diagnostics.Leverages = leverage values
%
% :Examples:
% ::
%
%    % Run regression with liberal threshold
%    out = regress(dat, .05, 'unc');
%
%    % Run regression with conservative threshold and save residual
%    out = regress(dat, .001, 'unc', 'residual);
%
%    % Run robust regression with fdr threshold
%    out = regress(dat, .05, 'fdr','robust');
%
%    % Run a regression predicting behavior from brain at liberal threshold
%    out  = regress(data_comb, .05, 'unc', 'brainony')
%
%    % Re-threshold at different values
%    out.t = threshold(out.t, .05, 'fdr');
%    out.t = threshold(out.t, .001, 'unc');
%
%    % Re-display results of thresholding
%    orthviews(out.t);
%
%    % Write out beta image to current directory
%    out.b.fullpath = fullfile(pwd,'beta.nii');
%    write(out)
%
%    % Plot diagnostics
%    figure; subplot(1,2,1); title('VIFs')
%    plot(regression_results.diagnostics.Variance_inflation_factors);
%    subplot(1,2,2); title('Leverage of each observation')
%    plot(regression_results.diagnostics.Leverages);
%
%   % Run with options:
%   regression_results = regress(dat, 'variable_names', names, 'analysis_name', 'Pinel localizer 1st-level GLM', 'noverbose');
%
%   regression_results_ols = regress(dat, 'variable_names', names, ...
%     'analysis_name', 'Pinel localizer 1st-level GLM', ...
%     'C', C.weights, 'contrast_names', C.names);
%
% Show some results for selected individual conditions
% % Thresholded t map for the first condition
% t = get_wh_image(regression_results_ols.t, 1);
%
% create_figure('montage'); axis off
% display_obj = montage(t);
%
% figure; surface(t);
%
% % Montages for thresholded t images, condition [2 4 6 8]
%
% t = get_wh_image(regression_results_ols.t, [2 4 6 8]);
%
% create_figure('montage2'); axis off
% display_obj = montage(t);
% Show  results for contrasts
% t = get_wh_image(regression_results_ols.con_t, 1:3);
%
% create_figure('montage3'); axis off
% display_obj = montage(t);
%
% t = get_wh_image(regression_results_ols.con_t, 4:6);
%
% create_figure('montage4'); axis off
% display_obj = montage(t);

% ..
%    Copyright (c) 2015 Tor Wager & Luke Chang
%
%    Permission is hereby granted, free of charge, to any person obtaining a
%    copy of this software and associated documentation files (the "Software"),
%    to deal in the Software without restriction, including without limitation
%    the rights to use, copy, modify, merge, publish, distribute, sublicense,
%    and/or sell copies of the Software, and to permit persons to whom the
%    Software is furnished to do so, subject to the following conditions:
%
%    The above copyright notice and this permission notice shall be included
%    in all copies or substantial portions of the Software.
%
%    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
%    OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
%    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
%    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
%    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
%    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
%    DEALINGS IN THE SOFTWARE.
% ..

% ..
%    Programmers' Notes:
%    c Tor Wager, Dec 2010
%    Edited by Luke Chang, 9/27/2012 to add optional input to reverse X & Y (i.e., create a map of voxels that predict the behavioral variable)
%    Edited by Luke Chang, 9/28/2012 to add optional input to run robust regression for brainony
%    Edited by Luke Chang, 10/24/2012 to save residuals (i.e., out.r), which is helpful for denoising an image
%    Edited by Luke Chang, 3/26/2013 to add optional input to not add an intercept - allows for more flexible modeling options
%    Code completely refactored by Luke Chang 2/24/25
%    Verbose option updated by Tor, 7/2015
%    Help, some outputs, robust % update: added by Tor, 5/2021
% ..

% ..
%    ---------------------------------------------------------------------
%    Defaults
%    ---------------------------------------------------------------------
% ..
inputargs = {.001, 'uncorrected'}; % default options for thresholding
do_display = false;
brain_is_outcome = 1; %else do brain on Y
do_robust = 0;
do_intercept = 1;
do_resid = 0;
doverbose = true;
variable_names = {};
contrast_names = {};
analysis_name = '';
C = [];
grandmeanscale = false;

% ---------------------------------------------------------------------
% Parse Inputs
% ---------------------------------------------------------------------
for varg = 1:length(varargin)

    if ischar(varargin{varg})
        % reserved keywords
        if strcmpi('unc',varargin{varg})
            inputargs = {varargin{varg-1}, 'uncorrected'};
            varargin{varg} = {}; varargin{varg - 1} = {};
        end
        if strcmpi('fdr',varargin{varg})
            inputargs = {varargin{varg-1}, 'fdr'};
            varargin{varg} = {}; varargin{varg - 1} = {};
        end

        if strcmpi('nodisplay',varargin{varg})
            do_display = 0;
            varargin{varg} = {};
        end

        if strcmpi('display',varargin{varg}) | strcmpi('display_results',varargin{varg})
            do_display = 0;
            varargin{varg} = {};
        end

        if strcmpi('robust',varargin{varg})
            do_robust = 1;
            varargin{varg} = {};
        end

        if strcmpi('brainony',varargin{varg}) | strcmpi('brain_is_predictor',varargin{varg}) %#ok<*OR2>
            brain_is_outcome = 0;
            varargin{varg} = {};
        end
        if strcmpi('nointercept',varargin{varg})
            do_intercept = 0;
            varargin{varg} = {};
        end
        if strcmpi('residual',varargin{varg})
            do_resid = 1;
            varargin{varg} = {};
        end
        if strcmpi('noverbose',varargin{varg})
            doverbose = false;
            varargin{varg} = {};
        end

        if strcmpi('names',varargin{varg}) | strcmpi('variable_names',varargin{varg})
            variable_names = varargin{varg + 1};
            varargin{varg} = {}; varargin{varg + 1} = {};
        end

        if strcmpi('C',varargin{varg}) | strcmpi('contrasts',varargin{varg})
            C = varargin{varg + 1};
            varargin{varg} = {}; varargin{varg + 1} = {};
        end

        if strcmpi('contrast_names',varargin{varg})
            contrast_names = varargin{varg + 1};
            varargin{varg} = {}; varargin{varg + 1} = {};
        end

        if strcmpi('analysis_name',varargin{varg})
            analysis_name = varargin{varg + 1};
            varargin{varg} = {}; varargin{varg + 1} = {};
        end

        if strcmpi('grandmeanscale',varargin{varg})
            grandmeanscale = true;
            varargin{varg} = {};
        end

    end % if ischar
end

if ~doverbose % add to pass into threshold( )
    inputargs{end+1} = 'noverbose';
end

% ---------------------------------------------------------------------
% Check Data and Diagnostics
% ---------------------------------------------------------------------

mywarnings = {};

% Check if fmri_data or image_vector
% ---------------------------------------------------------------------

if ~isa(dat,'fmri_data')
    error('dat input must be fmri_data object')
end

% Check data bit rate
% ---------------------------------------------------------------------

nuniquevals = length(unique(dat.dat(:)));

if nuniquevals < 2^10
    mywarnings{end+1} = sprintf('Number of unique values in dataset is low (%d, %3.2f bits), indicating possible restriction of bit rate. For comparison, Int16 has 65,536 unique values', nuniquevals, log2(nuniquevals));
end

% Check of dat.X exists and is correct format
% ---------------------------------------------------------------------

if brain_is_outcome
    if isempty(dat.X)
        error('Make sure you include a design matrix in dat.X')
    end
    if size(dat.dat, 2) ~= size(dat.X, 1)
        error('dat.dat must have same number of columns as dat.X has rows.')
    end
    if isa(dat.X,'design_matrix')
        dat.X = dat.X.dat;
    end
else % Check if dat.Y exists and is in correct format if running brainony
    if isempty(dat.Y)
        error('Make sure you include a vector in dat.Y.')
    end
    if size(dat.dat, 2) ~= size(dat.Y, 1)
        error('dat.dat must have same number of columns as dat.Y has rows.')
    end
end

% Check if Rank Deficient
% ---------------------------------------------------------------------

if rank(dat.X) < size(dat.X,2)
    mywarnings{end+1} = 'Warning:  dat.X is rank deficient.';
end

intercept_string = 'intercept is last';

% Check if Intercept is in model or specified for x_on_brain default
% ---------------------------------------------------------------------

if do_intercept && brain_is_outcome
    wh_int = intercept(dat.X, 'which');

    if isempty(wh_int)
        % add intercept and update wh_int (used later)
        if doverbose, mywarnings{end+1} = 'No intercept detected, adding intercept to last column of design matrix'; end
        X = intercept(dat.X, 'add');
        variable_names{end + 1} = 'Intercept';
%         wh_int = intercept(X, 'which');

    else
        intercept_string = sprintf('Intercept detected in column %1.0f of dat.X', wh_int);

        if doverbose, mywarnings{end+1} = intercept_string; end
        X = dat.X;

    end

else
    % No intercept or exogenous variables are outcome

    X = dat.X;
end

% Predictor centering
% ---------------------------------------------------------------------

m = mean(X);
wh_int = intercept(X, 'which');
m(wh_int) = [];
non_centered = abs(m) > 100 * eps;

% For effects-coded values [-1 1], ok, we want the intercept to reflect stats at average of 2 groups
iseffectcode = all(abs(X) == 1); % for each column
iseffectcode(wh_int) = [];

if any(non_centered & iseffectcode)

    mywarnings{end+1} = 'Warning:  Group sizes are unequal for effects-coded [1 -1] variable.';

end

if any(non_centered & ~iseffectcode)
    mywarnings{end+1} = 'Warning:  Predictors are not centered -- intercept is not interpretable as stats for average subject';
end

% Variance inflation
% ---------------------------------------------------------------------

vifs = getvif(X);

if any(vifs > 4)

    mywarnings{end+1} = 'Warning!!!  Design multicolinearity. Some regressors have variance inflation factors > 4. Check out.diagnostics';

end

% Leverages
% ---------------------------------------------------------------------

H = X*pinv(X);
%H = X*inv(X'*X)*X'  will be identical if not rank deficient
leverages = diag(H);

if any(abs(zscore(leverages)) >= 3)
     mywarnings{end+1} = 'Warning!!!  Some observations have high leverage values relative to others, regression may be unstable. abs(z(leverage)) > 3';
end

% Names
% ---------------------------------------------------------------------

k = size(X, 2);
if length(variable_names) < k
   if ~isempty(variable_names), mywarnings{end+1} = 'Warning!!!  Too few variable names entered, less than size(X, 2). Names may be inaccurate.'; end % suppress warning if NO names entered

   for i = length(variable_names)+1:k %#ok<*FXUP>
       variable_names{i} = sprintf('R%d', i); %#ok<*AGROW>
   end
end

if length(variable_names) > k
   mywarnings{end+1} = 'Warning!!!  Too many variable names entered, more than size(X, 2). Names may be inaccurate.';

   variable_names = variable_names(1:k);
end

% Check contrasts
% ---------------------------------------------------------------------

if ~isempty(C) && ~(size(C, 1) == size(X, 2))
    % Do this *after* adding intercept to X if needed

    disp('Contrasts entered, but size(C, 1) does not equal size(X, 2).');
    disp('Must have a contrast entry for each column of X (including the intercept)');
    error('Quitting.')

end

% Contrast names
% ---------------------------------------------------------------------

if ~isempty(C)

    kc = size(C, 2);

    if length(contrast_names) < kc
        if ~isempty(contrast_names), mywarnings{end+1} = 'Warning!!!  Too few contrast names entered, less than size(C, 2). Names may be inaccurate.'; end % suppress warning if NO names entered

        for i = length(contrast_names)+1:kc
            contrast_names{i} = sprintf('Con%d', i); %#ok<*AGROW>
        end
    end

    if length(contrast_names) > kc
        mywarnings{end+1} = 'Warning!!!  Too many contrast names entered, more than size(C, 2). Names may be inaccurate.';

        contrast_names = contrast_names(1:kc);
    end

end

% Enforce proper shape
if ~iscolumn(variable_names), variable_names = variable_names'; end
if ~isrow(contrast_names), contrast_names = contrast_names'; end

% Enforce valid names: Eliminate special characters and leading numbers
[variable_names, namewarnings] = format_text_letters_only(variable_names, 'numbers', 'cleanup', 'squeeze', 'underscore_ok');
mywarnings = [mywarnings namewarnings];
if ~isempty(namewarnings), mywarnings{end+1} = 'Enter valid variable_names'; end

if ~isempty(C)

    [contrast_names, namewarnings] = format_text_letters_only(contrast_names, 'numbers', 'cleanup', 'squeeze', 'underscore_ok');
    mywarnings = [mywarnings namewarnings];
    if ~isempty(namewarnings), mywarnings{end+1} = 'Enter valid contrast_names'; end

end

% Data scaling and format
% ---------------------------------------------------------------------

% Enforce double-format (just in case)
dat.dat = double(dat.dat);

if grandmeanscale

    % Scale grand mean to 100; approximates what SPM and other packages do. Assumes mask and overall brain size are consistent across replicates (e.g., participants, in a first-level analysis
    dat.dat = dat.dat .* 100 / nanmean(dat.dat(:));

end

if doverbose

    fprintf('Analysis: %s\n', analysis_name);
    disp('----------------------------------');

    nowarnings = all(cellfun(@isempty, mywarnings));

    disp('Design matrix warnings:');
    disp('----------------------------------');
    if nowarnings
        disp('None')

    else
        disp(char(mywarnings{:}))

    end

    disp(' ');
end

% ---------------------------------------------------------------------
% Run Regression
% ---------------------------------------------------------------------

tic
warning off

if brain_is_outcome
    % default is to regress dat.X on dat.dat (x on brain)
    % ---------------------------------------------------------------------

    % display info about regression
    if doverbose
        linestr = '______________________________________________________';
        disp(linestr);

        fprintf('Running regression: %3.0f voxels. Design: %3.0f obs, %3.0f regressors, %s\n', size(dat.dat, 1), size(X, 1), size(X, 2), intercept_string);
        if brain_is_outcome
            fprintf('\nPredicting exogenous variable(s) in dat.X using brain data as predictors, mass univariate');

        else % default
            fprintf('\nPredicting Brain Activity from dat.X, mass univariate');
        end
    end

    if do_robust
        % Robust - Regress stim/behavior(X) on brain (Y)
        %need to loop through voxels - Slow!

        if doverbose
            fprintf('\nRunning in Robust Mode ___%%');
        end

        v = size(dat.dat, 1);
        [n, k] = size(X);

        % Initialize outputs
        [b, t, stderr] = deal(zeros(k, v));
        p = ones(k, v);
        [dfe, sigma] = deal(zeros(1, v));

        for i = 1:v
            % For each voxel

            [bb,stats] = robustfit(X, dat.dat(i,:)', 'bisquare', [], 'off');

            b(:,i)=bb; %Betas
            t(:,i)=stats.t; %t-values
            p(:,i)=stats.p; %p-values
            dfe(:,i)=stats.dfe; %degrees of freedom
            stderr(:,i)=stats.se; %Standard Error
            sigma(:,i)=stats.robust_s; %robust estimate of sigma. LC not sure this is the best choice can switch to 'OLS_s','MAD_s', or 's'

            if doverbose && mod(i, 100) == 0
                fprintf('\b\b\b\b%03d%%', 100 * round(i/v))
            end
        end
        r = dat.dat' - X*b; %residual

    else
        % OLS - X predicting brain
        % - vectorized - Fast!

        if doverbose, fprintf('\nRunning in OLS Mode'); end

        % Estimate Betas in vector

        b = pinv(X) * dat.dat';

        % Error
        r = dat.dat' - X*b;

        % Residual variance
        [stderr, sigma] = get_std_errors(r, X);

        % Inference
        [t,dfe,p,sigma] = param_t_test(X,b,stderr,sigma);

    end

else
    % Regress brain (X) on stim/behavior (Y) - loops through voxels, slow!
    % ---------------------------------------------------------------------

    if doverbose
        % display info about regression
        fprintf('regression > X: %3.0f voxels. Y: %3.0f obs, %3.0f regressors, %s\n', size(dat.dat, 1), size(dat.Y, 2), intercept_string);
        fprintf('\nPredicting dat.Y from Brain Activity');
    end

    if do_robust %need to loop through voxels - Slow!
        if doverbose
            fprintf('\nRunning in Robust Mode ___%%');
        end

        v = size(dat.dat, 1);
        n = size(dat.dat, 2);
        k = 2; % one predictor (brain) and an intercept

        % Initialize outputs
        [b, t, stderr] = deal(zeros(k, v));
        p = ones(k, v);
        [dfe, sigma] = deal(zeros(1, v));

        for i = 1:v
            % Create X from brain Data
            if do_intercept
                X = intercept(dat.dat(i,:)','add');
            else
                X = dat.dat(i,:)';
            end

            [bb,stats] = robustfit(X, dat.Y, 'bisquare', [], 'off');

            b(:,i)=bb; %Betas
            t(:,i)=stats.t; %t-values
            p(:,i)=stats.p; %p-values
            dfe(:,i)=stats.dfe; %degrees of freedom
            stderr(:,i)=stats.se; %Standard Error
            sigma(:,i)=stats.robust_s; %robust estimate of sigma. LC not sure this is the best choice can switch to 'OLS_s','MAD_s', or 's'
            r(:,i) = dat.Y - X * b(:,i); %residual

            if doverbose && mod(i, 100) == 0
                fprintf('\b\b\b\b%03d%%', 100 * round(i/v))
            end

        end % voxel

    else
        % ---------------------------------------------------------------------
        %OLS -- Regress brain on Y

        if doverbose, fprintf('\nRunning in OLS Mode'); end

        for i = 1:size(dat.dat,1)

            % Create X from brain Data
            if do_intercept
                X = intercept(dat.dat(i,:)','add');
            else
                X = dat.dat(i,:)';
            end

            % Estimate Betas in vector
            b(:,i) = pinv(X) * dat.Y;

            % Error
            r(:,i) = dat.Y - X * b(:,i);

            %             sigma(i) = std(r(:,i)); % wrong
            %             stderr(:,i) = ( diag(inv(X' * X)) .^ .5 ) * sigma(i);  % params x voxels matrix of std. errors
        end

        % Residual variance - can do this at end because X is always the same size
        [stderr, sigma] = get_std_errors(r, X);

        % Inference
        [t,dfe,p,sigma] = param_t_test(X,b,stderr,sigma);

    end % dorobust

end % brain_is_outcome or not

stop = toc;
if doverbose, fprintf('\nModel run in %d minutes and %.2f seconds\n',floor(stop/60),rem(stop,60)); end

% ---------------------------------------------------------------------
% Contrasts
% ---------------------------------------------------------------------

if ~isempty(C)

    con_vals = C' * b;

    % Contrast STE
    con_ste = diag(C' * inv(X' * X) * C) .^ .5 * sigma;

    [con_t, ~, con_p] = param_t_test(X, con_vals, con_ste, sigma);

end

% ---------------------------------------------------------------------
% Create Output
% ---------------------------------------------------------------------

out = struct;

out.analysis_name = analysis_name;

out.input_parameters = struct( ...
    'brain_is_predictor', brain_is_outcome, 'do_robust', do_robust, 'grandmeanscale', grandmeanscale, ...
    'do_intercept', do_intercept, ...
    'do_resid', do_resid, 'doverbose', doverbose, 'do_display', do_display);

out.input_parameters.initial_statistical_threshold = inputargs;

out.input_image_metadata.source_notes = dat.source_notes;
out.input_image_metadata.history = dat.history;
out.input_image_metadata.image_names = dat.image_names;
out.input_image_metadata.fullpath = dat.fullpath;

% design, contrasts, and diagnostics

out.X = X;
out.variable_names = variable_names;
out.C = C;
out.contrast_names = contrast_names;

out.contrast_summary_table = table();

if ~isempty(C)
    % Contrast summary table

    for i = 1:size(C, 2)
        out.contrast_summary_table(:, i) = table(C(:, i));
    end

    out.contrast_summary_table.Properties.RowNames = variable_names;
    out.contrast_summary_table.Properties.VariableNames = contrast_names;

    if doverbose
        fprintf('\nSummary of conditions and contrasts\n%s\n', linestr);
        disp(out.contrast_summary_table);
    end

end

out.diagnostics = struct('Variance_inflation_factors', vifs, 'Leverages', leverages);
out.warnings = mywarnings;

% Create objects
if doverbose
    fprintf('\nCreating beta and t images, thresholding t images\n%s\n', linestr);
end

% Betas
out.b = statistic_image;
out.b.type = 'Beta';
out.b.p = p';
out.b.ste = stderr';
out.b.N = n;
out.b.dat = b';
out.b.dat_descrip = sprintf('Beta Values from regression, intercept is column %d', wh_int);
out.b.volInfo = dat.volInfo;
out.b.removed_voxels = dat.removed_voxels;
out.b.removed_images = false;  % this image does not have the same dims as the original dataset
out.b.image_labels = variable_names;

out.b = enforce_variable_types(out.b);

% - beta and contrast images are unthresholded, t images from both are thresholded
% if doverbose, fprintf('Thresholding b images at %3.6f %s\n', inputargs{1}, inputargs{2}); end
% out.b = threshold(out.b, inputargs{:}, 'noverbose'); % Threshold image

% T stats
out.t = statistic_image;
out.t.type = 'T';
out.t.p = p';
out.t.ste = stderr';
out.t.N = n;
out.t.dat = t';
out.t.dat_descrip = sprintf('t-values from regression, intercept is column %d', wh_int);
out.t.volInfo = dat.volInfo;
out.t.removed_voxels = dat.removed_voxels;
out.t.removed_images = false;  % this image does not have the same dims as the original dataset
out.t.image_labels = variable_names;

out.t = enforce_variable_types(out.t);

if doverbose
    fprintf('Thresholding t images at %3.6f %s\n', inputargs{1}, inputargs{2});
end
out.t = threshold(out.t, inputargs{:}); %Threshold image

% DF as fmri_data
out.df = dat;
out.df.dat = dfe';
out.df.dat_descrip = sprintf('Degrees of Freedom');

% Sigma as fmri_data
out.sigma = dat;
out.sigma.dat = sigma';
out.sigma.dat_descrip = sprintf('Sigma from Regression');

% Residual as fmri_data
if do_resid
    out.resid = dat;
    out.resid.dat = r';
    out.resid.dat_descrip = sprintf('Residual from Regression');
end

if ~isempty(C)

    if doverbose, fprintf('\nCreating contrast and t images and thresholding t images\n%s\n', linestr); end

    % Contrast values
    out.contrast_images = statistic_image;
    out.contrast_images.type = 'Contrast';
    out.contrast_images.p = con_p';
    out.contrast_images.ste = stderr';
    out.contrast_images.N = n;
    out.contrast_images.dat = con_vals';
    out.contrast_images.dat_descrip = 'Contrast Values from regression';
    out.contrast_images.volInfo = dat.volInfo;
    out.contrast_images.removed_voxels = dat.removed_voxels;
    out.contrast_images.removed_images = false;  % this image does not have the same dims as the original dataset
    out.contrast_images.image_labels = contrast_names;

    out.contrast_images = enforce_variable_types(out.contrast_images);

%     if doverbose, fprintf('Thresholding contrast images at %3.6f %s\n', inputargs{1}, inputargs{2}); end
%     out.contrast_images = threshold(out.contrast_images, inputargs{:}, 'noverbose'); % Threshold image

    % T stats
    out.con_t = statistic_image;
    out.con_t.type = 'T';
    out.con_t.p = con_p';
    out.con_t.ste = con_ste';
    out.con_t.N = n;
    out.con_t.dat = con_t';
    out.con_t.dat_descrip = sprintf('t-values from regression, intercept is column %d', wh_int);
    out.con_t.volInfo = dat.volInfo;
    out.con_t.removed_voxels = dat.removed_voxels;
    out.con_t.removed_images = false;  % this image does not have the same dims as the original dataset
    out.con_t.image_labels = contrast_names;

    out.con_t = enforce_variable_types(out.con_t);

    if doverbose
        fprintf('Thresholding t images at %3.6f %s\n', inputargs{1}, inputargs{2});

    end
    out.con_t = threshold(out.con_t, inputargs{:}); %Threshold image

end

% ---------------------------------------------------------------------
% Plot Results
% ---------------------------------------------------------------------
if k < 10 && do_display

    orthviews(out.t);

elseif do_display && doverbose
    disp('Warning: No display because >= 10 images.');

end

warning on

% ---------------------------------------------------------------------
% Subfunctions
% ---------------------------------------------------------------------

    function [stderr, sigma] = get_std_errors(r, X)

        [n, k] = size(X);
        v = size(dat.dat, 1);

        % We want diag(r' * r), but matrix size can be large
        % std(r) does not account for k parameters used, so is incorrect
        % residual std. not sqrt(var(resid)) -- we must account for k params used
        for i = 1:v
            sigma(1, i) = (r(:, i)' * r(:, i) ./ (n - k)) .^ .5;
        end

        stderr = ( diag(inv(X' * X)) .^ .5 ) * sigma;  % params x voxels matrix of std. errors

    end

    function [t,dfe,p,sigma] = param_t_test(X,b,stderr,sigma)
        % test whether parameter is significantly different from zero
        %
        % Inputs:
        % X:        design matrix
        % b:        beta values
        % stderr:   standard error of beta estimate
        % sigma:    standard deviation of residual
        %
        % Returns:
        % t:        t-value
        % dfe:      degrees of freedom
        % p:        p-value
        % sigma:    standard deviation of residual

        [n, k] = size(X);
        t = b ./ stderr;
        dfe = n - k;
        p = 2 * (1 - tcdf(abs(t), dfe));

        sigma(sigma == 0) = Inf;
        t(isnan(t)) = 0;
        p(isnan(p)) = 0;
        dfe = repmat(dfe,1,size(t,2));
    end

end % Main Function


function [  ] = vshowSpectralData( vdata, wavelength, opts )
%VSHOWSPECTRALDATA Showing a set of spectral data volume as suplots of a
%figure.
%   See also SPECTRUMRGB, TIGHT_SUBPLOT.

% default parameter setting
npcol    = 6; % number of columns for subplots
% magsize  = 1; % magnification of the image size
width    = 1080; % width of the plot
textpos  = [12 24]; % position of the text on the sub-images
fontsize = 15; % font size of the text (wavelength nm) on images
fontweight = 'normal'; % font weight of the text (wavelength) on images
fontname = 'FixedWidth'; % font name of the text (wavelength nm) on images
% parameters from the input opts
if isfield(opts,'npcol'),           npcol = opts.npcol;      end
if isfield(opts,'magsize'),       magsize = opts.magsize;    end
if isfield(opts,'width'),           width = opts.width;      end
if isfield(opts,'textpos'),       textpos = opts.textpos;    end
if isfield(opts,'fontsize'),     fontsize = opts.fontsize;   end
if isfield(opts,'fontweight'), fontweight = opts.fontweight; end
if isfield(opts,'fontname'),     fontname = opts.fontname;   end

% nw = length(wavelength); % number of wavelength bands
[nr,nc,nw] = size(vdata); % number of rows, columns, and wavelength bands
nprow = ceil(nw/npcol); % number of rows for subplots
% f = figure;
% f = figure('Position',[50 50 1100 900]); % depending on the size of images
% f = figure('Position',[50 50 npcol*nc*magsize nprow*nr*magsize]); % depending on the size of images
f = figure('Position',[50 50 width width/(npcol*nc)*nprow*nr]); % depending on the size of images
h = tight_subplot(nprow,npcol,[.002 .002],[.01 .01],[.01 .01]);
set(f,'Color','white');
% display each wavelength in each black with the same order as the
% wavelength
for iw = 1:nw
    lambda = wavelength(iw); % wavelength (nm)
    % colormap with the corresponding RGB wavelength
    wmap = (gray*kron(ones(3,1),spectrumRGB(lambda)));
    wmap = wmap/max(wmap(:));

    % subplot(nprow,npcol,iw);
    axes(h(iw));
    imshow(vdata(:,:,iw),'Colormap',wmap);
    text(textpos(1),textpos(2),[num2str(lambda,3) ' nm'],'Color','white',...
        'FontWeight',fontweight,'FontName',fontname,'FontSize',fontsize);
end
% display white image for the remaining blacks
for iw = nw+1:nprow*npcol
    axes(h(iw));
    imshow(ones(size(vdata,2)));
end

end


function cmap= cmap_whitered(m, varargin)
%CMAP_WHITERED - Colormap going from white to red
%
%Synopsis:
% MAP= cmap_whitered(M, <OPT>)
%
%Input:
% M: Size of the colormap (number of entries). Default value: Same size
%    as current colormap
% OPT: Struct or property/value list of optinal properties:
%  .MinSat - minimal saturation (HSV model) from which fading is started
%  .MinVal - minimal value (in HSV model) from which fading is started
%  .MaxVal - maximal value (in HSV model) to which fading is performed
%
%Output:
% MAP: A colormap matrix of size [M 3]
%
%Example:
% clf;
% colormap(cmap_whitered(15));
% imagesc(toeplitz(1:15)); colorbar;
%
%See also COLORMAP, HSV2RGB, CMAP_HSVFADE

props= {'MinSat',  0.4,  'DOUBLE[1]'
        'MinVal',  0,    'DOUBLE[1]'
        'MaxVal',  0.8,  'DOUBLE[1]'};

if nargin<1 | isempty(m),
  m= size(get(gcf,'colormap'),1);
end

opt= opt_proplistToStruct(varargin{:});
[opt, isdefault]= opt_setDefaults(opt, props);
opt_checkProplist(opt, props);

m1= floor(m/2.5);
m2= m-m1-1;
map1= cmap_hsvFade(m1+1, 0, 1, [opt.MinSat 1]);
map2= cmap_hsvFade(m2+1, 0, [opt.MaxVal opt.MinVal], 1);

cmap= flipud([map1; map2(2:end,:)]);


function tr = traces(a)

%function tr = traces(a)
%
% Provides a vector of traces of the vector LTI a
%

%S. de Waele, March 2003.

a_len = kingsize(a,3);
tr = zeros(a_len,1);

for t = 1:a_len,
   tr(t) = trace(a(:,:,t));
end


function M = dunzip(Z)
% DUNZIP - decompress DZIP output to recover original data
%
% USAGE:
% M = dzip(Z)
%
% VARIABLES:
% Z = compressed variable to decompress
% M = decompressed output
%
% NOTES: (1) The input variable Z is created by the DZIP function and
%            is a vector of type uint8
%        (2) The decompressed output will have the same data type and
%            dimensions as the original data provided to DZIP.
%        (3) See DZIP for other notes.
%        (4) Carefully tested, but no warranty; use at your own risk.
%        (5) Michael Kleder, Nov 2005

import com.mathworks.mlwidgets.io.InterruptibleStreamCopier
a=java.io.ByteArrayInputStream(Z);
b=java.util.zip.InflaterInputStream(a);
isc = InterruptibleStreamCopier.getInterruptibleStreamCopier;
c = java.io.ByteArrayOutputStream;
isc.copyStream(b,c);
Q=typecast(c.toByteArray,'uint8');
cn = double(Q(1)); % class
nd = double(Q(2)); % # dims
s = typecast(Q(3:8*nd+2),'double')'; % size
Q=Q(8*nd+3:end);
if cn == 3
    M  = logical(Q);
elseif cn == 4
    M = char(Q);
else
    ct = {'double','single','logical','char','int8','uint8',...
        'int16','uint16','int32','uint32','int64','uint64'};
    M = typecast(Q,ct{cn});
end
M=reshape(M,s);
return


function sys = optimizer(Constraints,Objective,options,x,u)
%OPTIMIZER  Container for optimization problem
%
%   OPT = OPTIMIZER(Constraints,Objective,options,x,u) exports an object that
%   contains precompiled numerical data to be solved for varying arguments
%   x, returning the optimal value of the expression u.
%
%   OPTIMIZER works most efficiently if the varying data x enters the
%   optmization problem affinely. For the general case, much more logic has
%   to be applied when instantiating the numerical data for a parametric
%   value, and when compiling the model, it is harder fpr YALMIP to
%   understand what kind of model it will be once the parameters are fixed.
%
%   By default, display is turned off (since optimizer is used in
%   situations where many problems are solved repeatedly. To turn on
%   display, set the verbose option in sdpsetting to 2.
%
%   Example
%
%    The following problem creates an LP with varying upper and lower
%    bounds on the decision variable.
%
%     A = randn(10,3);
%     b = rand(10,1)*19;
%     c = randn(3,1);
%
%     z = sdpvar(3,1);
%     sdpvar UB LB
%
%     Constraints = [A*z <= b, LB <= z <= UB];
%     Objective = c'*z
%     % We want the optimal z as a function of [LB;UB]
%     optZ = optimizer(Constraints,Objective,[],[LB; UB],z);
%
%     % Compute the optimal z when LB=1, UB = 3;
%     zopt = optZ([1; 3])
%
%     % Compute two solutions, one for (LB,UB) [1;3] and one for (LB,UB) [2;6]
%     zopt = optZ([[1; 3], [2;6]])
%
%     % A second output argument can be used to catch infeasibility
%     [zopt,infeasible] = optZ([1; 3])
%
%     % To avoid the need to vectorize in order to handle multiple
%     parameters, a cell-based definition can be used
%
%     optZ = optimizer(Constraints,Objective,[],{LB,UB},{z,sum(z)})
%     [zopt,infeasible] = optZ({1,3});
%     zopt{1}
%     zopt{2}

if nargin < 5
    error('OPTIMIZER requires 5 inputs');
end

% With the new optional cell-based format, the internal format is always a
% vector with all information stacked, both in and out. Hence, we need to
% save original sizes before stacking things up
xoriginal = x;
if ~isa(x,'cell')
    xoriginal = {x};
end
if isa(x,'cell')
    xvec = [];
  for i = 1:length(x)
      if ~(isa(x{i},'sdpvar') | isa(x{i},'ndsdpvar'))
          error(['The parameters must be SDPVAR objects. Parameter #' num2str(i) ' is a ' upper(class(x{i}))]);
      end
      if is(x{i},'complex')
          x{i} = [real(x{i});imag(x{i})];
          complexInput(i) = 1;
      else
          complexInput(i) = 0;
      end
      sizeOrigIn{i} = size(x{i});
      z = x{i}(:);
      mask{i} = uniqueNonZeroRows(z);
      xvec = [xvec;z(mask{i})];
  end
  x = xvec;
else
    if ~isreal(x)%,'complex')
        complexInput(1) = 1;
        x = [real(x);imag(x)];
    else
        complexInput(1) = 0;
    end
    sizeOrigIn{1} = size(x);
    x = x(:);
    mask{1} = uniqueNonZeroRows(x);
    x = x(mask{1});
end
nIn = length(x);
mIn = 1;

if isa(x,'sdpvar')
    if ~is(x,'lpcone')
    error('All parameter arguments have to be simple variables (i.e., not expressions such a+b or 1+a)');
    end
end

if isa(u,'cell')
    uvec = [];
    for i = 1:length(u)
        if is(u{i},'complex')
            complexOutput(i) = 1;
            u{i} = [real(u{i});imag(u{i})];
        else
            complexOutput(i) = 0;
        end
        sizeOrigOut{i} = size(u{i});
        uvec = [uvec;u{i}(:)];
    end
    u = uvec;
else
    if (isa(u,'sdpvar') || isa(u,'ndsdpvar')) && is(u,'complex')
        complexOutput(1) = 1;
        u = [real(u);imag(u)];
    else
        complexOutput(1) = 0;
    end
    sizeOrigOut{1} = size(u);
    u = u(:);
end
nOut = length(u);
mOut = 1;

if isempty(options)
    options = sdpsettings;
end

if ~isa(options,'struct')
    error('Third argument in OPTIMIZER should be an options structure.');
end

% Silent by default. If we want displays, set to 2
options.verbose = max(options.verbose-1,0);

% Since code is based on a fake equality, we must avoid bound propagation
% based on equalities
options.avoidequalitybounds=1;

% Normalize...
if isa(Constraints,'constraint')
    Constraints = lmi(Constraints);
end

if ~isempty(Constraints)
    if ~isa(Constraints,'constraint') &  ~isa(Constraints,'lmi')
        error('The first argument in OPTIMIZER should be a set of constraints');
    end
end

if ~isempty(Constraints)
    if any(is(Constraints,'sos'))
        tempOps = options;
        tempOps.sos.model = 2;
        tempOps.verbose = max(0,tempOps.verbose-1);
        parameter_sos = [x;u;recover(depends(Objective))];
        parameter_sos = depends(parameter_sos);
        for i = 1:length(Constraints)
            if ~is(Constraints,'sos')
                parameter_sos = [parameter_sos depends(Constraints(i))];
            end
        end
        parameter_sos = recover(parameter_sos);
        [Constraints,Objective] = compilesos(Constraints,Objective,tempOps,parameter_sos);
    end
end

if ~isequal(options.solver,'')
    % User has specified solver. Let us impose this solver forcefully to
    % the compilation code, in order to handle nonlinear parameterizations
    if ~strcmp(options.solver(1),'+')
        options.solver = ['+' options.solver];
    end
end

if options.removeequalities
    error('''removeequalities'' in optimizer objects not allowed.');
end

if ~isempty(Constraints) & any(is(Constraints,'uncertain'))
    [Constraints,Objective,failure] = robustify(Constraints,Objective,options);
    [aux1,aux2,aux3,model] = export((x == repmat(pi,nIn*mIn,1))+Constraints,Objective,options,[],[],0);
else
    [aux1,aux2,aux3,model] = export((x == repmat(pi,nIn*mIn,1))+Constraints,Objective,options,[],[],0);
end

if ~isempty(aux3)
    if isstruct(aux3)
        if ismember(aux3.problem, [-9 -5 -4 -3 -2 -1 14])
            error(['Failed exporting the model: ' aux3.info])
        end
    end
end

if norm(model.F_struc(1:nIn*mIn,1)-repmat(pi,length(x),1),inf) > 1e-10
    error('Failed exporting the model (try to specify another solver)')
end

% Try to set up an optimal way to compute the output
base = getbase(u);
if isempty(u) || (is(u,'linear') & all(sum(base | base,2) == 1) & all(sum(base,2)==1) & all(base(:,1)==0))
    % This is just a vector of variables
    z = [];
    map = [];
    uvec = u(:);
    % Setup to do fast getvariables(uvec(i))
    U = getbase(uvec);
    Uvar = getvariables(uvec);
    U = U(:,2:end);
    [ii,jj,ss] = find(U');
    for i = 1:length(uvec)
        %var = getvariables(uvec(i)); Slow
        %var = Uvar(find(U(i,:)));
        var = Uvar(ii(i));
        mapIndex = find(var == model.used_variables);
        if ~isempty(mapIndex)
            map = [map;mapIndex];
        else
            map = [map;0];
        end
    end
else
    % Some expression which we will use assign and double to evaluate
    vars = depends(u);
    z = recover(vars);
    map = [];
    for i = 1:length(z)
        var = vars(i);
        mapIndex = find(var == model.used_variables);
        if ~isempty(mapIndex)
            map = [map;mapIndex];
        else
            map = [map;0];
        end
    end
end

if ~isempty(u) && (isempty(map) | min(size(map))==0)
    error('The requested decision variable (argument 4) is not in model');
end

model.getsolvertime = 0;

model.solver.callhandle = str2func(model.solver.call);

model.options = pruneOptions(model.options);
model.hashCache = gen_rand_hash(0,size(model.monomtable,2),1);

sys.recover = aux2;
sys.model = model;
sys.dimin = [nIn mIn];
sys.dimout = [nOut mOut];
sys.diminOrig = sizeOrigIn;
sys.dimoutOrig = sizeOrigOut;
sys.complexInput = complexInput;
sys.complexOutput = complexOutput;
sys.mask = mask;
sys.map = map;
sys.input.xoriginal = xoriginal;
sys.input.expression = x;
sys.output.expression = u;
sys.output.z = z;
sys.lastsolution = [];
sys.ParametricSolution = [];
sys.model.infeasible = 0;
% This is not guaranteed to give the index in the order the variables where
% given (tested in test_optimizer2
% [a,b,c] = find(sys.model.F_struc(1:prod(sys.dimin),2:end));
% Could be done using
[b,a,c] = find(sys.model.F_struc(1:prod(sys.dimin),2:end)');
% but let us be safe
%b = [];
%for i = 1:prod(sys.dimin)
%    b = [b;find(sys.model.F_struc(i,2:end))];
%end
sys.model.parameterIndex = b;
used_in = find(any(sys.model.monomtable(:,b),2));
Q = sys.model.Q;
Qa = Q;Qa(:,b)=[];Qa(b,:)=[];
Qb = Q(:,b);Qb(b,:)=[];
if nnz(Q)>0
    zeroRow = find(~any(Q,1));
    Qtest = Q;Q(zeroRow,:)=[];Q(:,zeroRow)=[];
    problematicQP = nonconvexQuadratic(Qtest);%min(eig(full(Qtest)))<-1e-14;
else
    problematicQP = 0;
end
if  any(sum(sys.model.monomtable(used_in,:),2)>1) | any(sum(sys.model.monomtable(used_in,:) | sys.model.monomtable(used_in,:),2) > 1) | problematicQP | ~isempty(sys.model.evalMap) | any(any(sys.model.monomtable<0))
    sys.nonlinear = 1;
else
    sys.nonlinear = 0;
end
sys.F = Constraints;
sys.h = Objective;
sys.ops = options;

sys.complicatedEvalMap = 0;
% Are all nonlinear operators acting on simple parameters? Elimination
% strategy will only be applied on simple problems such as x<=exp(par)
if ~model.solver.evaluation
    for i = 1:length(sys.model.evalMap)
        if ~all(ismember(sys.model.evalMap{i}.variableIndex,sys.model.parameterIndex))
            sys.complicatedEvalMap = 1;
        end
        if length(sys.model.evalMap{i}.arg)>2
            sys.complicatedEvalMap = 1;
        end
    end
    if sys.complicatedEvalMap
        error('Parameters are currently only allowed to enter function such as exp, sin etc as exp(a), sin(b) etc.')
    end
end

sys.model.evalParameters = [];
if sys.nonlinear
    % These artificial equalities are removed if we will use eliminate variables

    %   sys.model.F_struc(1:length(sys.parameters),:) = [];
    %   sys.model.K.f = sys.model.K.f - length(sys.parameters);

    % Which variables are simple nonlinear operators acting on parameters
    evalParameters = [];
    for i = 1:length(sys.model.evalMap)
        if all(ismember(sys.model.evalMap{i}.variableIndex,sys.model.parameterIndex))
            evalParameters = [evalParameters;sys.model.evalMap{i}.computes(:)];
        end
    end
    sys.model.evalParameters = evalParameters;
end

% In case we perform partial instantiation, we have to remember where we
% came from originallty when finally solving problems
sys.instatiatedvalues = zeros(length(model.used_variables),1);
sys.orginal_usedvariables = sys.model.used_variables;
sys.orginal_parameterIndex = sys.model.parameterIndex;

sys.input.stochastics = cell(1,length(sys.diminOrig));
if ~isempty(Constraints)
    randDefinitions = find(is(Constraints,'random'));
    if ~isempty(randDefinitions)

        for i = 1:length(randDefinitions)
            Fi = Constraints(randDefinitions(i));
            randDef{i}.distribution = struct(struct(Fi).clauses{1}.data).extra.distribution;
            randDef{i}.variables = sdpvar(Fi);

            for j = 1:length(sys.diminOrig)
                if isequal(getbase(sys.input.xoriginal{j}),getbase(randDef{i}.variables)) && isequal(getvariables(sys.input.xoriginal{j}),getvariables(randDef{i}.variables))
                    sys.input.stochastics{j} = randDef{i}.distribution;
                end
            end
        end
    end
end

% Remove place holder constraints. No longer used
sys.model.F_struc(1:prod(sys.dimin),:)=[];
sys.model.K.f = sys.model.K.f-prod(sys.dimin);

sys = class(sys,'optimizer');
sys = optimizer_precalc(sys);

function i = uniqueNonZeroRows(x);
B = getbase(x);
% Quick check for trivially unique rows, typical 99% case
[n,m] = size(B);
if n == m-1 && nnz(B)==n
    if isequal(B,[spalloc(n,1,0) speye(n)])
        i = 1:n;
        return
    end
end
if  length(unique(B*randn(size(B,2),1))) == n && nnz(B*randn(size(B,2),1)) == size(B,1)
    i = 1:n;
    return
end
[temp,i] = unique(B*randn(size(B,2),1));
z = find(~any(B,2));
i = setdiff(i,z);
i = i(:);


function y = perform_huffcoding(x,T,dir)

% perform_huffcoding - perform huffman coding
%
%   y = perform_huffcoding(x,T,dir);
%
%   dir=+1 for coding
%   dir=-1 for decoding
%
%   T is a Huffman tree, computed with compute_hufftree
%
%   Copyright (c) 2008 Gabriel Peyre

if dir==1
    %%% CODING %%%
    C = huffman_gencode(T);
    m = length(C);
    x = round(x);
    if min(x)<1 || max(x)>m
        error('Too small or too large token');
    end
    y = [];
    for i=1:length(x)
        y = [y C{x(i)}];
    end
else
    %%% DE-CODING %%%
    t = T{1};
    y = [];
    while not(isempty(x))
        if x(1)==0
            t = t{1};
        else
            t = t{2};
        end
        x(1) = [];
        if not(iscell(t))
            y = [y t];
            t = T{1};
        end
    end
    y = y(:);
end

%%

function C = huffman_gencode(T)

if not(iscell(T)) % || not(length(T)==2)
    C = {};
    C{T} = -1;
elseif length(T)==1
    C = huffman_gencode(T{1});
    % remove traling -1
    for i=1:length(C)
        C{i} = C{i}(1:end-1);
    end
elseif length(T)==2
    C1 = huffman_gencode(T{1});
    C2 = huffman_gencode(T{2});
    C = {};
    for i=1:length(C1)
        if not(isempty(C1{i}))
            C{i} = [0 C1{i}];
        end
    end
    for i=1:length(C2)
        if not(isempty(C2{i}))
            C{i} = [1 C2{i}];
        end
    end
else
    error('Problem');
end