function ret = optiSolver(instr,err)
%OPTISOLVER  See if a solver is installed on your PC and details about it
%
%   optiSolver() prints a list of OPTI interfaced solvers, their version
%   numbers and and whether they are available on your PC.
%
%   optiSolver('matrix') prints a solver vs problem type matrix for
%   determining which solvers and setup for which problems.
%
%   optiSolver('config') prints a solver vs config matrix for
%   determining which options are configured for which solvers.
%
%   optiSolver(type) prints a list of solvers for a given problem, 'type',
%   together with a table of constraint and derivative information. See
%   below for heading information. Examples are 'lp', 'milp', etc.
%
%   ok = optiSolver(solver) returns true if the passed solver is available
%   on your PC. Examples are 'clp', 'ipopt', etc.
%
%   optiSolver(solver) displays information about the solver (if
%   available) and configuration specifics (no output args).
%
%   solver = optiSolver(bestSolver) returns the best available solver as a
%   string for a given problem type. Examples are 'best_lp', 'best_milp',
%   etc.
%
%   Problem Type table headings are as follows:
%
%   BD - Bounds
%   LI - Linear Inequality (* indicates a conversion will take place)
%   LE - Linear Equality   (* indicates a conversion will take place)
%   QI - Quadratic Inequality
%   SS - Special Ordered Set
%   NI - Nonlinear Inequality
%   NE - Nonlinear Equality
%   SP - Supports Sparse Matrices
%   D1 - Requires First Derivatives (Gradient) (* indicates optional)
%   D2 - Requires Second Derivatives (Hessian) (* indicates optional)
%   GL - Global Solver

%   Copyright (C) 2011-2015 Jonathan Currie (IPL)

global SLE LP MILP BILP QP QCQP MIQP MIQCQP SDP MISDP SNLE SCNLE NLS UNO NLP MINLP DESC PTYPES

%Ordered lists of 'best' solvers
SLE = {'matlab','MUMPS'};
LP = {'CPLEX','clp','mosek','scip','ooqp','glpk','lp_solve','MATLAB','csdp','dsdp','SeDuMi'};
MILP = {'CPLEX','cbc','mosek','scip','glpk','lp_solve','MATLAB'};
BILP = {'CPLEX','cbc','mosek','scip','glpk','lp_solve','MATLAB'};
QP = {'CPLEX','ooqp','clp','mosek','scip','ipopt','MATLAB'};
QCQP = {'CPLEX','mosek','scip','ipopt'};
MIQP = {'CPLEX','mosek','scip','bonmin'};
MIQCQP = {'CPLEX','mosek','scip','bonmin'};
SDP = {'csdp','dsdp','SeDuMi'}; %mosek?
MISDP = {}; %'scip'
SNLE = {'nl2sol','hybrj','mkltrnls','lmder','levmar','MATLAB','IPOPT','nomad','nlopt'};
SCNLE = {'nl2sol','mkltrnls','levmar','IPOPT','nomad','pswarm','nlopt'};
NLS = {'nl2sol','mkltrnls','lmder','levmar','gsl','MATLAB','IPOPT','nomad','pswarm','nlopt'};
UNO = {'IPOPT','matlab','m1qn3','NLOPT','nomad','scip','GMATLAB'};
NLP = {'IPOPT','MATLAB','FILTERSD','NLOPT','LBFGSB','nomad','PSwarm','baron','scip','GMATLAB'};
MINLP = {'BONMIN','nomad','baron','scip','GMATLAB'};
%Description List
DESC = containers.Map({'cplex','glpk','lp_solve','clp','matlab','mumps','ooqp','ipopt','nlopt','cbc','bonmin','lbfgsb','levmar','lmder','nl2sol','hybrj',...
                       'mkltrnls','pswarm','gmatlab','nomad','scip','m1qn3','filtersd','dsdp','csdp','sedumi','baron','mosek','gsl'},...
                      {'IBM ILOG CPLEX','GNU Linear Programming Kit','LP_Solve','COIN-OR Linear Programming',...
                       'Optimization Toolbox: ','a MUltifrontal Massively Parallel sparse direct Solver','Object-Orientated Quadratic Programming',...
                       'Interior-Point Optimization','Nonlinear Optimization','COIN-OR Branch and Cut',...
                       'Basic Open-source Nonlinear Mixed INteger programming','Limited Memory Broyden-Fletcher-Goldfarb-Shanno Bounded Optimization',...
                       'Levenberg-Marquardt Nonlinear Least Squares in C/C++','MINPACK Levenberg-Marquardt','Adaptive Nonlinear Least Squares','MINPACK Powell Hybrid',...
                       'Intel MKL Trust Region Nonlinear Least Squares','Pattern and Particle Swarm Global Optimizer','Global Optimization Toolbox: ',...
                       'Nonlinear Optimization with the MADS algorithm','Solving Constraint Integer Programs','Large-Scale Unconstrained L-BFGS Minimization',...
                       'FilterSD: Filter & Trust Region Nonlinear Optimization','Dual Scaling Interior Point SDP Solver','Predictor-Corrector Primal-Dual SDP Solver',...
                       'Self-Dual-Minimization','Branch And Reduce Optimization Navigator','MOSEK Optimization Software','GNU Scientific Library'});
%Problem Type List
PTYPES = {'sle','lp','milp','bilp','qp','qcqp','miqp','miqcqp','sdp','misdp','snle','scnle','nls','uno','nlp','minlp'};

%Default Input Args
if(nargin < 2), err = 1; end %default to create an error
if(nargin < 1), instr = []; end
%Default Internal Args
ret = [];

if(isempty(instr))
    printSolvers();     %print a list of solvers and availability
else
    %Check if we have a known solver
    allS = getAllSolvers();
    for i = 1:length(allS)
        if(strcmpi(instr,allS{i}))
            ret = check(instr,err);
            if(nargout == 0 && ret), printSolverInfo(instr); end
            return;
        end
    end

    % if 'DNLS' - change to nls
    if (strcmpi(instr,'dnls'))
        instr = 'nls';
    end

    %If we got here, check for a known problem type
    for i = 1:length(PTYPES)
        if(strcmpi(instr,PTYPES{i}))
            ret = findAllForProb(instr,nargout);
            return;
        end
    end

    %Still not found, check standard options and alternative names for solvers
    switch(lower(instr))
        %Options
        case 'matrix'
            printSolverMatrix;
        case 'config'
            printConfigMatrix;
        case 'ptypes'
            ret = PTYPES;
        case 'ver'
            ret = genSolverList();
        %Alternative Solver Names
        case 'auto'
            ret = 1; %assume we can find a solver
        case 'optim'
            ret = check('matlab',err);
            if(nargout == 0 && ret), printSolverInfo('matlab'); end
        case {'mldivide','fminsearch'}
            ret = 1; %base matlab
        case 'l-bfgs-b'
            ret = check('lbfgsb',err);
            if(nargout == 0 && ret), printSolverInfo('lbfgsb'); end
        case 'lpsolve'
            ret = check('lp_solve',err);
            if(nargout == 0 && ret), printSolverInfo('lp_solve'); end
        case {'lmdif','lm_der','lm_dif'}
            ret = check('lmder',err);
            if(nargout == 0 && ret), printSolverInfo('lmder'); end
        case 'nl2sno'
            ret = check('nl2sol',err);
            if(nargout == 0 && ret), printSolverInfo('nl2sol'); end
        case 'hybrd'
            ret = check('hybrj',err);
            if(nargout == 0 && ret), printSolverInfo('hybrd'); end
        case 'mkltr'
            ret = check('mkltrnls',err);
            if(nargout == 0 && ret), printSolverInfo('mkltrnls'); end
        case 'baron'
            ret = check('baron',err);

        %Check for all, best
        otherwise
            if(strfind(instr,'best'))
                ret = findBestSolver(instr);
            elseif(strfind(instr,'all'))
                ret = findAllForProb(instr,nargout);
            else
                error('Unknown Solver or Option: %s',instr); %not found
            end
    end
end

function ok = check(name,err,ptype)
%Check a particular solver is installed
if(nargin < 3), ptype = []; end
switch(name)
    case 'MATLAB'
        if(~isempty(ptype) && strcmpi(ptype,'milp'))
            c = which('intlinprog.m'); %only available >= 2014a (8.3)
        elseif(~isempty(ptype) && strcmpi(ptype,'bilp'))
            %bintprog removed in 2014b, used intlinprog instead if available
            if(verLessThan('matlab','8.4')) %2014b
                c = which('bintprog.m');
            else
                c = which('intlinprog.m');
            end
        else
            c = which('fmincon'); %using ver is quite slow
        end
        if(isempty(c))
            if(err)
                error('The MATLAB Optimization Toolbox must be installed');
            else
                ok = 0;
            end
        else
            ok = 1;
        end
        return
    case {'GMATLAB','gmatlab'}
        c = which('patternsearch'); %using ver is quite slow
        if(isempty(c))
            if(err)
                error('The MATLAB Global Optimization Toolbox must be installed');
            else
                ok = 0;
            end
        else
            ok = 1;
        end
        return
    case {'matlab'} %always installed (mldivide)
        ok = 1;
        return
    case {'CPLEX','cplex'} %check cplexlink mex file
        c = which(name);
        %Problems with OPTI + R2013a x64 + Cplex >= v12.5.0.0
        if(~isempty(c) && ~isempty(strfind(computer,'64')))
            if(~isempty(which(['cplexlink1250.' mexext])) || ~isempty(which(['cplexlink1251.' mexext])) || ~isempty(which(['cplexlink1260.' mexext])))
            c = []; %force not available
            end
        end
    case {'MOSEK','mosek'} %find mex file
        c = which(['mosekopt.' mexext]);
    case {'sedumi','SEDUMI','SeDuMi'}
        %Check matlab file
        c = which('sedumi');
        %Check mex file exists as well
        if(isempty(which(['partitA.' mexext])))
            c = [];
        %Check for v1.31 or higher
        elseif(~isempty(c))
            [~,major,minor] = getVerFromVerFile('sedumi');
            if(major == 1 && minor < 31)
                c = [];
            elseif(major < 1)
                c = [];
            end
        end
    case {'BARON','baron'}
        c = which('baron.m');
        if(isempty(c))
            c = which('baron.p');
        end
    case {'LIPSOL','lipsol'}
        c = which(['ls_blkslv.' mexext]);
    otherwise
        c = which([name '.' mexext]); %bug fix 22/2/12
        if(~isempty(c)) %ensure mex file is OK
            try
                a = eval(lower(name)); %#ok<NASGU>
            catch
                c = [];
            end
        end
end

if(isempty(c))
    if(err)
        throwAsCaller(MException('opti:checkSolver','OPTI cannot find a valid version of %s. Ensure it is installed on your system.',name));
    else
        ok = 0;
    end
else
    ok = 1;
end

function str = findBestSolver(prob)
%Find the best available solver for a given problem type

global SLE LP MILP BILP QP QCQP MIQP MIQCQP SDP MISDP SNLE SCNLE NLS UNO NLP MINLP PTYPES %#ok<NUSED>

s = regexp(prob,'_','split');
if(length(s) < 2)
    error('When requesting the best solver it should be of the form best_xxx, e.g. best_lp');
end

%Remove 'D' from dynamic problems
if(s{2}(1) == 'D')
    s{2} = s{2}(2:end);
end

fnd = 0;
for i = 1:length(PTYPES)
    if(strcmpi(s{2},PTYPES{i}))
        try
            list = eval(upper(s{2}));
            fnd = 1;
        catch %#ok<CTCH>
            fnd = 0; %global var doesn't exist?
        end
        break;
    end
end
%If not found, we don't have a solver for this problem
if(~fnd)
    error('Could not find a problem type match for type %s',upper(s{2}));
%Otherwise return the best solver available
else
    str = [];
    for i = 1:length(list)
        if(check(list{i},0))
            str = list{i};
            break;
        end
    end
    if(isempty(str))
        error('You do not have a solver which can solve a %s explicitly',upper(s{2}));
    end
end

function list = findAllForProb(prob,nout)
%Find all available solvers for a given problem type

global SLE LP MILP BILP QP QCQP MIQP MIQCQP SDP MISDP SNLE SCNLE NLS UNO NLP MINLP PTYPES %#ok<NUSED>

%Backwards compatibility check
if(strfind(prob,'all_'))
    s = regexp(prob,'_','split');
    prob = s{2};
end

fnd = 0;
for i = 1:length(PTYPES)
    if(strcmpi(prob,PTYPES{i}))
        try
            plist = eval(upper(prob));
            fnd = 1;
        catch %#ok<CTCH>
            fnd = 0; %global var doesn't exist?
        end
        break;
    end
end
%If found keep availale solvers
if(fnd)
    j = 1; list = [];
    for i = 1:length(plist)
        if(check(plist{i},0,prob))
            list{j} = plist{i}; %#ok<AGROW>
            j = j + 1;
        end
    end
%Check if we want all...
else
    if(strcmpi(prob,'all'))
        list = getAllSolvers();
        return;
    else
        error('Unknown problem type: %s\nNote the API has changed in v1.34 to just the problem type, e.g. checkSolver(''lp'')',prob);
    end
end

%If not returning anything, just print
if(~nout)
    printSelSolvers(list,upper(prob))
    list = [];
end

function list = getAllSolvers()
%Create a list of all solvers OPTI is interfaced to (not neccesarily found however).
global SLE LP MILP BILP QP QCQP MIQP MIQCQP SDP MISDP SNLE SCNLE NLS UNO NLP MINLP

fulllist = [SLE LP MILP BILP QP QCQP MIQP MIQCQP SDP MISDP SNLE SCNLE NLS UNO NLP MINLP];
%Remove duplicates
fulllist = unique(lower(fulllist));
%Sort & Return
list = sort(upper(fulllist));

function printSolvers()
%Print each solver and if it exists

fprintf('\n------------------------------------------------\n')
fprintf('OPTI AVAILABLE SOLVERS:\n\n');
list = genSolverList();
for i = 1:length(list)
    fprintf('%s\n',list{i});
end

fprintf('------------------------------------------------\n')

function list = genSolverList()
%Generate a display compatible list of all solvers, their availability and version number
solv = getAllSolvers();
list = cell(length(solv),1);
for i = 1:length(solv)
    [avail,solVer] = getSolverVer(solv{i});
    list{i} = sprintf('%-10s       %-13s  %s',[solv{i} ':'],avail,solVer);
end

function printSelSolvers(solv,type)
%Print selected solvers

fprintf('\n------------------------------------------------\n')
fprintf(['OPTI ' upper(type) ' SOLVERS:\n']);
if(isempty(solv))
    fprintf('None Available\n');
else
    [~,hd] = getSolverInfo(solv{1},type);
    len = length(strtrim(hd));
    fprintf(' %s\n',hd); j = 1;
    for i = 1:length(solv)
        if(check(solv{i},0)) %only print if the solver is available
            fprintf(['(%2d) %-10s  %-'  num2str(len+4) 's %s\n'],j,upper(solv{i}),getSolverInfo(solv{i},type),getDesc(solv{i},type));
            j = j + 1;
        end
    end
end
fprintf('------------------------------------------------\n')

function printSolverMatrix()
%Print solver vs problem matrix

global SLE LP BILP MILP QP QCQP MIQP MIQCQP SDP MISDP SNLE SCNLE NLS UNO NLP MINLP PTYPES

fprintf('\n------------------------------------------------\n')
fprintf('OPTI SOLVER vs PROBLEM MATRIX:\n\n');
fprintf(' %-8s  %-5s %-4s %-5s %-6s %-4s %-5s %-5s %-4s %-4s %-4s %-5s %-6s %-5s %-5s %-4s %-4s\n','Solver','SLE','LP','BILP','MILP','QP','QCQP','MIQP','MIQCQP','SDP','MISDP','SNLE','SCNLE','NLS','UNO','NLP','MINLP');

solv = getAllSolvers();
list = {SLE LP BILP MILP QP QCQP MIQP MIQCQP SDP MISDP SNLE SCNLE NLS UNO NLP MINLP};

%REVERSE LOCATION OF BILP AND MILP TO SUIT ABOVE
probs = PTYPES;
idxm = strcmpi(probs,'milp');
idxb = strcmpi(probs,'bilp');
probs{idxm} = 'bilp';
probs{idxb} = 'milp';

for i = 1:length(solv)
    fprintf('%-8s |',solv{i});
    sok = optiSolver(solv{i},0);
    for j = 1:length(list)
        if(any(strcmpi(solv{i},list{j})))
            if(strcmpi(solv{i},'matlab')) %matlab has optimization toolbox + base packages
                msok = check('MATLAB',0,probs{j});
                if(msok || any(strcmp({'matlab'},list{j})))
                    fprintf('  x  |');
                else
                    fprintf('  o  |');
                end
            else
                if(sok)
                    fprintf('  x  |');
                else
                    fprintf('  o  |');
                end
            end
        else
            fprintf('     |');
        end
    end
    fprintf('\n');
end

fprintf('------------------------------------------------\n')

function printConfigMatrix()
%Print solver vs config matrix

%[miter,meval,mnode,mtime,tolr,tola,tolint,disp,opts,ctrlc,iterf]
fprintf('\n------------------------------------------------\n')
fprintf('OPTI SOLVER vs CONFIG MATRIX:\n\n');
fprintf('   %-11s %-7s %-7s %-7s %-7s %-7s %-6s %-8s %-7s %-7s %-6s %-7s\n','Solver','miter','meval','mnode','mtime','tolr','tola','tolint','disp','sOpts','ctrlc','iterfnc');

solv = getAllSolvers();

for i = 1:length(solv)
    info = optiSolverInfo(solv{i},'');
    %Separate out variables we use
    miter   = getMarker(info.opt.miter);
    meval   = getMarker(info.opt.meval);
    mnode   = getMarker(info.opt.mnode);
    mtime   = getMarker(info.opt.mtime);
    tolr    = getMarker(info.opt.tolr);
    tola    = getMarker(info.opt.tola);
    tolint  = getMarker(info.opt.tolint);
    disp    = getMarker(info.opt.disp);
    sOpts   = getMarker(info.opt.opts);
    ctrlc   = getMarker(info.opt.ctrlc);
    iterf   = getMarker(info.opt.iterf);

    %Print line
    fprintf('%-11s  |   %1s   |   %1s   |   %1s   |   %1s   |   %1s   |   %1s   |   %1s   |   %1s   |   %1s   |   %1s   |   %1s   |\n',solv{i},miter,meval,mnode,mtime,tolr,tola,tolint,disp,sOpts,ctrlc,iterf);
end

fprintf('------------------------------------------------\n')

function str = getDesc(solv,type)
%Return solver description
global DESC

str = DESC(lower(solv));
if(strcmp(solv,'MATLAB'))
    str = [str getMatlabDesc(type)];
elseif(strcmp(solv,'GMATLAB'))
    str = [str getGMatlabDesc(type)];
elseif(strcmp(solv,'matlab'))
    str = getMatlabDesc(type);
end

function str = getMatlabDesc(type)
%Return MATLAB solver

switch(lower(type))
    case 'sle'
        str = 'mldivide';
    case 'lp'
        str = 'linprog';
    case 'qp'
        str = 'quadprog';
    case 'bilp'
        if(verLessThan('matlab','8.4')) %2014b
            str = 'bintprog';
        else
            str = 'intlinprog';
        end
    case 'milp'
        str = 'intlinprog';
    case 'nlp'
        str = 'fmincon';
    case 'snle'
        str = 'fsolve';
    case 'nls'
        str = 'lsqnonlin';
    case 'uno'
        str = 'fminsearch';
    otherwise
        str = '';
end

function str = getGMatlabDesc(type)
%Return Global MATLAB Solver

switch(lower(type))
    case {'uno','nlp'}
        str = 'patternsearch';
    case 'minlp'
        str = 'ga';
    otherwise
        str = '';
end

function [avail,solVer] = getSolverVer(solver)
%Return currently installed solver version

ok = check(solver,0);
if(ok)
    avail = 'Available';
    switch(lower(solver))
        %Interfaced solvers
        case 'cplex'
            c = Cplex;
            solVer = ['v' c.getVersion];
            clear c;
        case 'matlab'
            c = ver('optim');
            if(isfield(c,'Version'))
                solVer = ['v' c.Version];
            else
                solVer = '';
            end
            clear c;
        case 'gmatlab'
            c = ver('globaloptim');
            if(isfield(c,'Version'))
                solVer = ['v' c.Version];
            else
                solVer = '';
            end
            clear c;
        case 'mosek'
            solVer = ''; %unknown for the moment
        case 'sedumi'
            solVer = getVerFromVerFile('sedumi');
        case 'lipsol'
            solVer = getVerFromVerFile('lipsol');
        case 'opti'
            c = optiver;
            solVer = ['v' num2str(c)];
        case 'baron' %unknown
            solVer = '';
        %Supplied Solvers
        otherwise
            try
                v = feval(lower(solver));
                if(~strcmp(v,''))
                    solVer = ['v' v];
                else
                    solVer = '';
                end
            catch %#ok<CTCH>
                solVer = '';
            end
    end
else
    avail = 'Not Available';
    solVer = '';
end

function [str,hd] = getSolverInfo(solver,type)
%Generate a string of solver information for a particular solver

%Get Solver Information
info = optiSolverInfo(solver,type);
%Separate out variables we use
bnd     = getMarker(info.con.bnd);
lineq   = getMarker(info.con.lineq);
leq     = getMarker(info.con.leq);
qineq   = getMarker(info.con.qineq);
ss   = getMarker(info.con.sos);
sdp     = getMarker(info.con.sdp);
nlineq  = getMarker(info.con.nlineq);
nleq    = getMarker(info.con.nleq);
der1    = getMarker(info.der1);
der2    = getMarker(info.der2);
glb     = getMarker(info.global);
sp      = getMarker(info.sparse);

lhs = '%-17s';
switch(lower(type))
    case {'lp','qp'}
        hd = sprintf([lhs 'BD LI LE SP'],'');
        str = sprintf(' %-2s %-2s %-2s %-2s',bnd,lineq,leq,sp);
    case {'milp','miqp'}
        hd = sprintf([lhs 'BD LI LE SS SP'],'');
        str = sprintf(' %-2s %-2s %-2s %-2s %-2s',bnd,lineq,leq,ss,sp);
    case 'bilp'
        hd = sprintf([lhs 'LI LE SP'],'');
        str = sprintf(' %-2s %-2s %-2s',lineq,leq,sp);
    case {'qcqp','miqcqp'}
        hd = sprintf([lhs 'BD LI LE QI SP'],'');
        str = sprintf(' %-2s %-2s %-2s %-2s',bnd,lineq,leq,qineq,sp);
    case {'sdp','misdp'}
        hd = sprintf([lhs 'BD LI LE QI SD SP'],'');
        str = sprintf(' %-2s %-2s %-2s %-2s %-2s',bnd,lineq,leq,qineq,sdp,sp);
    case 'nls'
        hd = sprintf([lhs 'BD LI LE D1'],'');
        str = sprintf(' %-2s %-2s %-2s %-2s',bnd,lineq,leq,der1);
    case {'nlp','minlp'}
        hd = sprintf([lhs 'BD LI LE QI NI NE SP D1 D2 GL'],'');
        str = sprintf(' %-2s %-2s %-2s %-2s %-2s %-2s %-2s',bnd,lineq,leq,qineq,nlineq,nleq,sp,der1,der2,glb);
    otherwise %no constraints
        hd = '';
        str = '';
end

function str = getMarker(value)
switch(value)
    case 1
        str = 'x';
    case 0
        str = '';
    case -1
        str = '*';
end

%Print Solver Information
function printSolverInfo(solver)
fprintf('\n-----------------------------------------------------------\n');
%Print heading
[~,solVer] = getSolverVer(solver);
if(strcmpi(solver,'MATLAB'))
    solverstr = 'MATLAB [Optimization Toolbox]';
elseif(strcmpi(solver,'GMATLAB'))
    solverstr = 'GMATLAB [Global Optimization Toolbox]';
else
    solverstr = upper(solver);
end
fprintf('OPTI SOLVER: %s [%s]\n',solverstr,solVer);
fprintf('-----------------------------------------------------------\n');
%Get solver info
info = optiSolverInfo(solver);
%Print Special Info
if(info.der1 || info.der2 || info.sparse || info.global || info.multialg)
   fprintf('Solver Features:\n');
   if(info.parallel == 1), fprintf('\t- Multi-Threaded (Parallel) Solver\n'); end
   if(info.multialg == 1), fprintf('\t- Contains Multiple Algorithms\n'); end
   if(info.whitebox == 1), fprintf('\t- White-Box NL Solver Which Exploits Problem Structure\n'); end
   if(info.global == 1)
       fprintf('\t- Solves Global Optimization Problems\n');
   elseif(info.global == -1)
       fprintf('\t- Some Algorithms Solve Global Optimization Problems\n');
   end
   if(info.con.int), fprintf('\t- Solves Mixed Integer Problems\n'); end
   if(info.sparse), fprintf('\t- Solves Sparse Problems\n'); end
   if(info.der1 == 1)
       fprintf('\t- Requires First Derivatives\n');
   elseif(info.der1 == -1)
       if(info.multialg==1)
            fprintf('\t- Some Algorithms Require First Derivatives\n');
       else
            fprintf('\t- Can Accept First Derivatives (Or Solver Will Approximate)\n');
       end
   end
   if(info.der2 == 1)
       fprintf('\t- Requires Second Derivatives\n');
   elseif(info.der2 == -1)
       fprintf('\t- Can Accept Second Derivatives (Or Solver Will Approximate)\n');
   end
   fprintf('-----------------------------------------------------------\n');
end
%Print Constraint Info
if(info.con.con)
    fprintf('Supported Constraints:\n');
    printCon('Decision Variable Bounds',info.con.bnd);
    printCon('Linear Inequalities',info.con.lineq);
    printCon('Linear Equalities',info.con.leq);
    printCon('Quadratic Inequalities',info.con.qineq);
    printCon('Quadratic Equalities',info.con.qeq);
    printCon('Semidefinite (Linear Matrix Inequalities)',info.con.sdp);
    printCon('Nonlinear Inequalities',info.con.nlineq);
    printCon('Nonlinear Equalities',info.con.nleq);
    printCon('Integer Variables',info.con.int);
    printCon('Special Ordered Sets',info.con.sos);
    fprintf('-----------------------------------------------------------\n');
end
%See if we have any unique info
u=struct2cell(info.unq); uu = 0;
for i = 1:length(u)
    if(ischar(u{i}) || any(u{i} ~= 0))
        uu = 1;
        break;
    end
end
%Print Unique Info
if(uu)
    fprintf('Special Requirements / Options:\n');
    if(info.unq.x0 == 1)
        fprintf('\t- Requires an initial solution guess via x0\n');
    elseif(info.unq.x0 == -1)
        fprintf('\t- Can accept an initial solution guess via x0\n');
    end
    switch(info.unq.qpform)
        case 'sym'
            fprintf('\t- QP H Matrix must be symmetric\n');
        case 'tril'
            fprintf('\t- QP H Matrix must be symmetric lower triangular\n');
        case 'triu'
            fprintf('\t- QP H Matrix must be symmetric upper triangular\n');
    end
    switch(info.unq.hform)
        case 'sym'
            fprintf('\t- Nonlinear Hessian (if supplied) must be symmetric\n');
        case 'tril'
            fprintf('\t- Nonlinear Hessian (if supplied) must be symmetric lower triangular\n');
        case 'triu'
            fprintf('\t- Nonlinear Hessian (if supplied) must be symmetric upper triangular\n');
    end
    if(info.unq.jstr), fprintf('\t- Requires the structure of the Jacobian\n'); end
    if(info.unq.hstr), fprintf('\t- Requires the structure of the Hessian (if supplied)\n'); end
    fprintf('-----------------------------------------------------------\n');
end

%Print info from MEX file
switch(lower(solver))
    case {'cplex','mosek','matlab','gmatlab','sedumi','lipsol','baron'}
        %Nothing
    case {'filtersd'}
        fprintf('MEX FILE DETAILS:');
        eval([lower(solver) 'sp']);
    otherwise
        fprintf('MEX FILE DETAILS:');
        eval(lower(solver));
end

function printCon(con,val)
if(val == 1)
    fprintf('\t- %s\n',con);
elseif(val == -1)
    fprintf('\t- %s (but may be converted internally)\n',con);
end

function [solVer,major,minor] = getVerFromVerFile(solverP)
solVer = ''; major = 0; minor = 0;
%Find sedumi.m and Version .txt
p = which(solverP); p2 = which('Version.txt','-all');
if(~isempty(p) && ~isempty(p2))
    %Find final file sep in solver path
    ind = strfind(p,filesep);
    %For each version path, see if we can find a matching solver path
    for i = 1:length(p2)
        ind2 = strfind(p2{i},filesep);
        %compare paths up to find file sep
        if(strcmp(p(1:ind(end)),p2{i}(1:ind2(end))))
            %open and reading version
            fid = fopen(p2{i});
            if(fid > 0)
                v = fscanf(fid,'%d.%d');
                fclose(fid);
                major = v(1); minor = v(2);
                solVer = sprintf('v%d.%d',v(1),v(2));
            else
                fclose(fid);
            end
            break;
        end
    end
end


function hole_point = p14_hole_point ( hole_index, m )

%*****************************************************************************80
%
%% P14_HOLE_POINT returns a point inside a given hole in problem 14.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    12 April 2009
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer HOLE_INDEX, the index of the hole.
%
%    Input, integer M, the spatial dimension.
%
%    Output, real HOLE_POINT(M), a point in the hole
%
  hole_point(1:2) = [ 257.068, 278.225 ];

  return
end


function varargout = subscript(m,range,wantc)

% function varargout = subscript(m,range,wantc)
%
% <m> is a matrix (of numbers) or a string referring to a matrix
%   variable in the base workspace.  cell matrices are okay.
% <range> is:
%   (1) vector index range that does not use
%       the 'end' keyword, e.g., 4, [5 6]
%   (2) a logical indexing matrix
%   (3) the string ':'
%   (4) a cell vector where elements can be of types (1), (2), (3),
%       e.g., {':' 4:5}
% <wantc> (optional) is whether to perform cell de-referencing.  default: 0.
%
% return something like m(range), or m{range} when <wantc>.
%
% this function is useful for cases where you want
% to get a range of elements from something that
% already has parentheses or brackets.
% it is also useful for working with string-variable
% representations of matrices.  it is also useful
% for writing functions that make no assumption about
% the size of the matrices used as input.
%
% example:
% isequal(size(subscript(randn(10,10),{1:2 ':'})),[2 10])
% a = [1 2; 3 4];
% subscript('a',1)
% subscript('a',a<1.5)

% input
if ~exist('wantc','var') || isempty(wantc)
  wantc = 0;
end

% do it
if wantc
  if iscell(range)
    if ischar(m)
      varargout = evalin('base',['subscript(',m,',',cell2str(range),',1);']);
    else
      varargout = m(range{:});
    end
  else
    if ischar(m)
      if ischar(range) && isequal(range,':')
        varargout = evalin('base',[m,'(:);']);
      else
        varargout = evalin('base',[m,'(',mat2str(range),');']);
      end
    else
      if ischar(range) && isequal(range,':')
        varargout = m(:);
      else
        varargout = m(range);
      end
    end
  end
else
  varargout = cell(1,1);
  if iscell(range)
    if ischar(m)
      varargout{1} = evalin('base',['subscript(',m,',',cell2str(range),');']);
    else
      varargout{1} = m(range{:});
    end
  else
    if ischar(m)
      if ischar(range) && isequal(range,':')
        varargout{1} = evalin('base',[m,'(:);']);
      else
        varargout{1} = evalin('base',[m,'(',mat2str(range),');']);
      end
    else
      if ischar(range) && isequal(range,':')
        varargout{1} = m(:);
      else
        varargout{1} = m(range);
      end
    end
  end
end


function [mx, mt] = vl_nnmasknan(x, t, varargin)
%VL_NNMASKNAN masks out NaN data from targets
%   [MX, MT] = VL_NNMASKNAN(X,T) masks out NaN data present in targets
%   T and drops the corresponding entries in X.  Produces masked outputs
%   MX and MT where MT consists of the non-NaN entries of T and MX consists
%   of the corresponding entries of X. The shape of both MX and MT will be
%   Nx1, where N is the number of non-NaN entries of T.
%
%   NOTE: I think this function is borrowed from somewhere, since I don't
%   remember writing it.  Unfortunately I can't remember where it might
%   be from, making attribution difficult.
%
% Copyright (C) 2017 Samuel Albanie
% Licensed under The MIT License [see LICENSE.md for details]

  [~, dzdy] = vl_argparsepos(struct(), varargin) ;

  mask = isnan(t) ;
  mx = x ; mt = t ;

  if isempty(dzdy)
    mx(mask(:)) = [] ;
    mt(mask(:)) = [] ;
    mx = mx' ; mt = mt' ;
  else
    mx = zeros(size(x), 'like', x) ;
    mx(~mask) = dzdy{1} ;
  end


function i4mat_write ( output_filename, m, n, table )

%*****************************************************************************80
%
%% I4MAT_WRITE writes an I4MAT file.
%
%  Discussion:
%
%    An I4MAT is an array of I4's.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    09 August 2009
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, string OUTPUT_FILENAME, the output filename.
%
%    Input, integer M, the spatial dimension.
%
%    Input, integer N, the number of points.
%
%    Input, integer TABLE(M,N), the points.
%
%    Input, logical HEADER, is TRUE if the header is to be included.
%

%
%  Open the file.
%
  output_unit = fopen ( output_filename, 'wt' );

  if ( output_unit < 0 )
    fprintf ( 1, '\n' );
    fprintf ( 1, 'I4MAT_WRITE - Error!\n' );
    fprintf ( 1, '  Could not open the output file.\n' );
    error ( 'I4MAT_WRITE - Error!' );
    return;
  end
%
%  Write the data.
%
  for j = 1 : n
    for i = 1 : m
      fprintf ( output_unit, '  %12d', round ( table(i,j) ) );
    end
    fprintf ( output_unit, '\n' );
  end
%
%  Close the file.
%
  fclose ( output_unit );

  return
end


  function data = load_ascii_skip_header(file, varargin)
%|function data = load_ascii_skip_header(file)
%| read ascii file, skipping lines that start with '#'
%| output is double array of numeric values
%| option
%|	'char'	0|1	if 1 then output cell array of each line
%| Copyright 2007, Jeff Fessler, University of Michigan

if nargin < 1, ir_usage, end
arg.char = false;
arg = vararg_pair(arg, varargin);

fid = fopen(file, 'r');
if fid == -1
	fail('problem opening "%s"', file)
end

if arg.char
	data = {};
else
	data = [];
end

while 1
	line = fgetl(fid);
	if ~ischar(line)
		if isempty(data)
			fail('file %s ended too soon', file)
		else
			break % go to fclose
		end
	end
	if ~length(line) || line(1) == '#'
		continue
	end
	if arg.char
		data{end+1} = line;
	else
		data(end+1,:) = sscanf(line, '%f');
	end
end

tmp = fclose(fid);
if tmp ~= 0
	fail('problem with fclose for "%s"', file)
end


classdef Ephem < handle
% Class for opening JPL ephemeris files and calculating raw positions
% Copyright(c) 2014 by Jonathan Kipling Knight (drkipknight@aol.com)
% DEPENDENCIES: This class requires that ephemeris files are available online by
% ftp or have been previously downloaded and placed in the Matlab path.
  properties (Constant)
    % body numbers for use with planet_ephemeris

    Mercury               = 1; % Mercury body number
    Venus                 = 2; % Venus body number
    Earth                 = 3; % Earth body number
    Mars                  = 4; % Mars body number
    Jupiter               = 5; % Jupiter body number
    Saturn                = 6; % Saturn body number
    Uranus                = 7; % Uranus body number
    Neptune               = 8; % Neptune body number
    Pluto                 = 9; % Pluto body number
    Moon                  = 10; % Geocentric Moon body number
    Sun                   = 11; % Sun body number
    SolarSystemBarycenter = 12; % Solarsystem Barycenter body number
    EarthMoonBarycenter   = 13; % Earth-Moon Barycenter body number
    Nutations             = 14; % nutations body number
    Librations            = 15; % librations body number
    LunarEulerRates       = 16; % lunar euler angel rates body number
    TTmTDB                = 17; % TT-TDB body number
  end
  properties (Constant)
    % state numbers for use with state or interpolate

    MercuryState               = 1; % Mercury state number
    VenusState                 = 2; % Venus state number
    EarthMoonBarycenterState   = 3; % Earth-Moon Barycenter state number
    MarsState                  = 4; % Mars state number
    JupiterState               = 5; % Jupiter state number
    SaturnState                = 6; % Saturn state number
    UranusState                = 7; % Uranus state number
    NeptuneState               = 8; % Neptune state number
    PlutoState                 = 9; % Pluto state number
    MoonState                  = 10; % Geocentric Moon state number
    SunState                   = 11; % Sun state number
    NutationsState             = 12; % nutations state number
    LibrationsState            = 13; % librations state number
    LunarEulerState            = 14; % lunar euler angle rates state number
    TTState                    = 15; % TT-TDB state number
  end
  properties (Constant)
    % list of available ASCII Development Ephemerides
    de_numbers = {'102','200','202','403','405','406','410','413','414','418','421','422','423','424','430','430t','431'};
    % list of minimum years for ASCII Development Ephemerides
    minyears = [-1400, 1600, 1900, 1600, 1600,-3000, 1960, 1900, 1600, 1900, 1900,-3000, 1800,-3000, 1550, 1550,-13000];
    % list of increments between files for ASCII Development Ephemerides
    incyears = [  300,   20,   50,  100,   20,  100,   20,   25,  100,  150,  150,  100,   50,  100,  100,  100,  1000];
    % list of maximum years for ASCII Development Ephemerides
    maxyears = [ 2800, 2160, 2050, 2100, 2200, 2900, 2000, 2025, 2100, 1900, 2050, 2900, 2150, 2900, 2550, 2550, 16000];
  end
  properties (Constant)
    TTLsize = 84; % max character length of the 3 TTL lines in the binary ephemeris
    CNAMsize = 6; % max character length of the Constant Name in the binary ephemeris
    OLDCONMAX = 400; % old maximum for number of constants
    JPLftpSite = 'ftp://ssd.jpl.nasa.gov/pub/eph/planets/ascii'; % site for ASCII files
  end
  properties
    de_number = 405; % DE number of data stored here
    % flag defining physical units of the output states.
    %          = 1, km and km/sec
    %          = 0, au and au/day
    % files are native km and km/sec
    KM = 1;

    SS = [0.0,0.0,0.0]; % JD of first record, JD of last record, days between records
    JPLAU = 0.0; % definition of the astronomical unit (km)
    EM_RATIO = 0.0; % ratio of Earth's mass to Moon's mass
    RECORD_LENGTH = 0; % number of bytes in record
    IPT = zeros(3,Ephem.TTState); % table of indices into record
    scan = []; % linear array of values in records
    indexInc = 0; % increment between records in scan
    indexOffset = 1; % offset for buffer
    numRecs = 0; % number of records
    jd_begin = 0.0; % Julian Date of first record
    jd_end = 0.0; % Julian Date of last record
    jd_inc = 0.0; % days between records
    masses = zeros(Ephem.Mercury,Ephem.EarthMoonBarycenter); % G*mass (AU^3/Day^2) of body number 1-13
    output = 1; % message output
  end
  methods
    function s = Ephem( denum )
      % constructor
      if nargin < 1
        denum = s.de_number;
      end
      if isnumeric(denum)
        denum = sprintf('%d',denum);
      end
      s = s.setDEConstants( denum );
    end
    [ position, velocity, acceleration, error ] = planet_ephemeris( s, tjd, target, center );
    [ target_pos, target_vel, target_acc, error ] = state( s, jed, target );
    [ position, velocity, acceleration ] = interpolate( s, nrl, target, t );
    [ s, error ] = openDEasc( s, denum, fn, dontSave );
    [ s, error ] = openDEeph( s, denum, fn, dontSave );
    [ s, error ] = setDEConstants( s, denum );
    function [ rtn ] = hasNutations( s )
      % check if ephemeris file has nutations
      rtn = (s.IPT(2,Ephem.NutationsState) > 0);
    end
    function [ rtn ] = hasLibrations( s )
      % check if ephemeris file has librations
      [~,nc] = size(s.IPT);
      if nc >= Ephem.LibrationsState && s.IPT(2,Ephem.LibrationsState) > 0
        rtn = true;
      else
        rtn = false;
      end
    end
    function [ rtn ] = hasTTmTDB( s )
      % check if ephemeris file has TT-TDB
      [~,nc] = size(s.IPT);
      if nc >= Ephem.TTState && s.IPT(2,Ephem.TTState) > 0
        rtn = true;
      else
        rtn = false;
      end
    end
    function [ rtn ] = hasLunarEulerAngleRates( s )
      % check if ephemeris file has lunar Euler angle rates
      [~,nc] = size(s.IPT);
      if nc >= Ephem.LunarEulerState && s.IPT(2,Ephem.LunarEulerState) > 0
        rtn = true;
      else
        rtn = false;
      end
    end
  end
  methods (Static)
    function vector = newVector()
      % create a new position (row vector)
      vector = zeros(1,3);
    end
    [ fr1, fr2 ] = splitDouble( tt );
    [ error ] = asc2eph( denum, fn, OUTFILE );
    [ st, error ] = readHeader( denum );
    [ scan, error ] = read_asc( denum, fn, dontSave );
    [ fn ] = asc_name( denum, year );
    [ fn ] = eph_name( denum, year );
    function [ NCOEFF ] = numCoeff( IPT )
      % calculate number of coefficients in record based on IPT matrix
      KMX = 0;
      KHI = 0;
      [~,nc] = size(IPT);
      for I = 1:nc
        if (IPT(1,I) > KMX) && (IPT(2,I) > 0)
          KMX = IPT(1,I);
          KHI = I;
        end
      end
      if KHI == 0
        NCOEFF = 0;
        return;
      end

      if (KHI == Ephem.TTState)
        ND = 1;
      elseif (KHI == Ephem.NutationsState)
        ND = 2;
      else
        ND = 3;
      end

      NCOEFF = IPT(1,KHI)+ND*IPT(2,KHI)*IPT(3,KHI)-1;
    end
    function [rtn] = createAllEphFiles( denum, regen )
      % download and convert all ASCII ephemeris files to binary
      if nargin < 2
        regen = false;
      end
      if isnumeric(denum)
        denum = sprintf('%d',denum);
      end
      index = {};
      for i=1:length(Ephem.de_numbers)
        if strcmp(Ephem.de_numbers{i},denum)
          index = i;
          break;
        end
      end
      if isempty(index)
        rtn = 1;
        return;
      end
      for yr=Ephem.minyears(index):Ephem.incyears(index):Ephem.maxyears(index)
        infile = Ephem.asc_name(denum,yr);
        outfile = Ephem.eph_name(denum,yr);
        if regen || exist(outfile,'file') ~= 2
          rtn = Ephem.asc2eph(denum,infile,outfile);
        end
      end
    end
    rtn = TESTZERODAY( denum, testCase );
    rtn = TESTPOINTS( denum, testCase );
  end
%   methods (Test)
%     function test102(testCase)
%       % test DE102
%       Ephem.TESTZERODAY(102, testCase);
%       Ephem.TESTPOINTS(102, testCase);
%     end
%     function test200(testCase)
%       % test DE200
%       %Ephem.TESTZERODAY(200, testCase);
%       Ephem.TESTPOINTS(200, testCase);
%     end
%     function test405(testCase)
%       % test DE405
%       Ephem.TESTZERODAY(405, testCase);
%       Ephem.TESTPOINTS(405, testCase);
%     end
%     function test430(testCase)
%       % test DE430
%       Ephem.TESTZERODAY(430, testCase);
%       Ephem.TESTPOINTS(430, testCase);
%     end
%     function test430t(testCase)
%       % test DE430t
%       Ephem.TESTZERODAY('430t', testCase);
%       Ephem.TESTPOINTS('430t', testCase);
%     end
%     function test431(testCase)
%       % test DE431
%       Ephem.TESTZERODAY(431, testCase);
%       Ephem.TESTPOINTS(431, testCase);
%     end
%   end

end


% %Example of user input MATLAB file for post processing
% close all
%
% %Plot waves
% waves.plotElevation(simu.rampTime);
% try
%     waves.plotSpectrum();
% catch
% end
%
% % Plot RY forces for body 1
% plotForces(output,1,5)
%
% %Plot RY response for body 1
% output.plotResponse(1,5);
%
% % Plot x forces for body 2
% plotForces(output,2,1)


function sFileOut = out_fopen_edf(OutputFile, sFileIn, ChannelMat, EpochSize, iChannels)
% OUT_FOPEN_EDF: Saves the header of a new empty EDF file.

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

%% ===== PARSE INPUTS =====
% iChannels is set only if exporting only a subset of the input channels
if (nargin < 5) || isempty(iChannels)
    iChannels = [];
end
% Reject files with epochs
if (length(sFileIn.epochs) > 1)
    error('Cannot export epoched files to continuous EDF files.');
end
% Is the input file a native EDF file
fileSamples = round(sFileIn.prop.times .* sFileIn.prop.sfreq);
isRawEdf = strcmpi(sFileIn.format, 'EEG-EDF') && ~isempty(sFileIn.header) && isfield(sFileIn.header, 'patient_id') && isfield(sFileIn.header, 'signal');
nSamples = fileSamples(2) - fileSamples(1) + 1;
% Modify input headers (EDF export reuses the input header info directly)
if isRawEdf && ~isempty(iChannels)
    sFileIn.header.nsignal = length(iChannels);
    sFileIn.header.signal = sFileIn.header.signal(iChannels);
end

%% ===== GET MAXIMUM VALUES =====
if ~isRawEdf
    % Extracts the minimum and maximum values for each sensor over all the file (by blocks of 1s).
    % This helps optimizing the conversion of the recordings to int16 values.
    BlockSize = sFileIn.prop.sfreq; % 1-second blocks
    nBlocks = ceil(nSamples ./ BlockSize);
    % Initialize min/max matrices
    Fmax = 0 * ones(length(ChannelMat.Channel), 1);
    % Loop on all the blocks
    for iBlock = 1:nBlocks
        bst_progress('text', sprintf('Finding maximum values [%d%%]', round(iBlock/nBlocks*100)));
        % Get sample indices for a block of 1s
        SamplesBounds = round([(iBlock - 1) * BlockSize + fileSamples(1), min(fileSamples(2), fileSamples(1) + iBlock * BlockSize)]);
        % Read the block from the file
        Fblock = in_fread(sFileIn, ChannelMat, 1, SamplesBounds);
        % Keep only the files to be saved in the output file
        if ~isempty(iChannels)
            Fblock = Fblock(iChannels, :);
        end
        % Extract absolute max
        Fmax = max(Fmax, max(abs(Fblock),[],2));
    end
    % Make sure we don't have cases where the maximum is zero
    Fmax(Fmax == 0) = 1;
else
    Fmax =  1 * ones(length(ChannelMat.Channel), 1);
end

%% ===== WRITE EDF HEADER =====
bst_progress('text', 'Writing EDF+ header...');
% Get file comment
[fPath, fBase, fExt] = bst_fileparts(OutputFile);

% Initialize output file
sFileOut = sFileIn;
sFileOut.filename  = OutputFile;
sFileOut.condition = '';
sFileOut.format    = 'EEG-EDF';
sFileOut.byteorder = 'l';
sFileOut.comment   = fBase;
date = datetime;

% Create a new header structure
header            = struct();
header.version    = 0;
header.startdate  = datestr(date, 'dd.mm.yy');
header.starttime  = datestr(date, 'HH.MM.SS');
header.nsignal    = length(ChannelMat.Channel);
header.nrec       = nSamples / sFileIn.prop.sfreq;

% We need to choose a record length that produces a  record size less than
% 61440 bytes (at 2 bytes per sample).
% We'll enforce this only if the epoch size is divisible to avoid problems.
header.reclen = EpochSize / sFileIn.prop.sfreq;
factors = full_factors(EpochSize);

for iFactor = 1:length(factors)
    header.reclen = EpochSize / sFileIn.prop.sfreq / factors(iFactor);
    recordSize = EpochSize / factors(iFactor) * header.nsignal * 2;

    if recordSize <= 61440
        break;
    end
end
header.nrec = ceil(header.nrec / header.reclen);

% Add an additional channel at the end to save events if necessary.
% if ~isempty(sFileIn.events)
    % If the last channel is not already an Annotation channel: add it
    if ~strcmpi(ChannelMat.Channel(end).Name, 'Annotations') || ~strcmpi(ChannelMat.Channel(end).Type, 'EDF')
        header.nsignal = header.nsignal + 1;
        Fmax(end+1) = 1;
    end
    header.annotchan = header.nsignal;

    % Some EDF+ fields are required by strict viewers such as EDFbrowser
    header.unknown1    = 'EDF+C';
    header.patient_id  = 'UNKNOWN M 01-JAN-1900 Unknown_Patient';
    header.rec_id      = ['Startdate ', upper(datestr(date, 'dd-mmm-yyyy')), ...
                          ' Unknown_Hospital Unknown_Technician Unknown_Equipment'];

    % Compute annotations
    header.annotations = {};
    maxAnnotLength     = 0;

    for iEvt = 1:numel(sFileIn.events)
        event = sFileIn.events(iEvt);
        % EDF file start at 0s: removed the start file time
        event.times = event.times - sFileIn.prop.times(1);

        for iEpc = 1:length(event.epochs)
            startTime = event.times(1,iEpc);
            annot = sprintf('+%f', startTime);

            if (size(event.times,1) == 2)
                duration = event.times(2,iEpc) - startTime;
                annot    = [annot, sprintf('%c%f', char(21), duration)];
            end

            annot = [annot, sprintf('%c%s%c%c', char(20), event.label, char(20), char(0))];
            header.annotations{end + 1} = annot;

            if length(annot) > maxAnnotLength
                maxAnnotLength = length(annot);
            end
        end
    end
% else
%     header.annotchan   = -1;
%     header.unknown1    = '';
%     header.patient_id  = '';
%     header.rec_id      = '';
% end
header.hdrlen = 256 + 256 * header.nsignal;

% Channel information
for i = 1:header.nsignal
    if ~isRawEdf
        % Choose unit based on maximal value
        if Fmax(i) > 0.1
            header.signal(i).unit = 'V';
            header.signal(i).unit_gain = 1;
%         elseif Fmax(i) * 1e3 > 1
%             header.signal(i).unit = 'mV';
%             header.signal(i).unit_gain = 1e3;
        else
            header.signal(i).unit = 'uV';
            header.signal(i).unit_gain = 1e6;
        end

        chScale = 2^15 / Fmax(i);
        header.signal(i).digital_min  = -2^15;
        header.signal(i).digital_max  = 2^15 - 1;
        header.signal(i).physical_min = header.signal(i).digital_min / chScale .* header.signal(i).unit_gain;
        header.signal(i).physical_max = header.signal(i).digital_max / chScale .* header.signal(i).unit_gain;
        header.signal(i).filters      = '';
        header.signal(i).unknown2     = '';
    end

    % Approximate size of annotation channel
    header.signal(i).type  = '';
    if i == header.annotchan
        header.signal(i).label    = 'EDF Annotations';
        eventsPerRecord           = ceil(numel(header.annotations) / header.nrec);
        header.signal(i).nsamples = eventsPerRecord * maxAnnotLength + 15; % For first annotation of each record
        % Convert chars (1-byte) to 2-byte integers, the size of a sample
        header.signal(i).nsamples = int64((header.signal(i).nsamples + 1) / 2);
    else
        if strcmpi(ChannelMat.Channel(i).Type, 'EEG_NO_LOC')
            header.signal(i).label = ['EEG ', ChannelMat.Channel(i).Name];
        else
            header.signal(i).label = [ChannelMat.Channel(i).Type, ' ', ChannelMat.Channel(i).Name];
        end
        header.signal(i).nsamples = header.reclen * sFileIn.prop.sfreq;
    end
end

% Copy some values from the original header if possible
if isRawEdf
    header.patient_id = sFileIn.header.patient_id;
    header.rec_id     = sFileIn.header.rec_id;
    header.startdate  = sFileIn.header.startdate;
    header.starttime  = sFileIn.header.starttime;
    header.unknown1   = sFileIn.header.unknown1;
    for i = 1:sFileIn.header.nsignal
        header.signal(i).label        = sFileIn.header.signal(i).label;
        header.signal(i).type         = sFileIn.header.signal(i).type;
        header.signal(i).filters      = sFileIn.header.signal(i).filters;
        header.signal(i).unit         = sFileIn.header.signal(i).unit;
        header.signal(i).unknown2     = sFileIn.header.signal(i).unknown2;
        header.signal(i).physical_min = sFileIn.header.signal(i).physical_min;
        header.signal(i).physical_max = sFileIn.header.signal(i).physical_max;
        header.signal(i).digital_min  = sFileIn.header.signal(i).digital_min;
        header.signal(i).digital_max  = sFileIn.header.signal(i).digital_max;

        switch (header.signal(i).unit)
            case 'mV',                        header.signal(i).unit_gain = 1e3;
            case {'uV', char([166 204 86])},  header.signal(i).unit_gain = 1e6;
            otherwise,                        header.signal(i).unit_gain = 1;
        end
    end
end

% Open file
fid = fopen(OutputFile, 'w+', sFileOut.byteorder);
if (fid == -1)
    error('Could not open output file.');
end

% Write header
fwrite(fid, str_zeros(header.version, 8));
fwrite(fid, str_zeros(header.patient_id, 80));
fwrite(fid, str_zeros(header.rec_id, 80));
fwrite(fid, header.startdate);
fwrite(fid, header.starttime);
fwrite(fid, str_zeros(header.hdrlen, 8));
fwrite(fid, str_zeros(header.unknown1, 44));
fwrite(fid, str_zeros(header.nrec, 8));
fwrite(fid, str_zeros(header.reclen, 8));
fwrite(fid, str_zeros(header.nsignal, 4));

% Channel information
for i = 1:header.nsignal
    fwrite(fid, str_zeros(header.signal(i).label, 16));
end
for i = 1:header.nsignal
    fwrite(fid, str_zeros(header.signal(i).type, 80));
end
for i = 1:header.nsignal
    fwrite(fid, str_zeros(header.signal(i).unit, 8));
end
for i = 1:header.nsignal
    fwrite(fid, str_zeros(header.signal(i).physical_min, 8));
end
for i = 1:header.nsignal
    fwrite(fid, str_zeros(header.signal(i).physical_max, 8));
end
for i = 1:header.nsignal
    fwrite(fid, str_zeros(header.signal(i).digital_min, 8));
end
for i = 1:header.nsignal
    fwrite(fid, str_zeros(header.signal(i).digital_max, 8));
end
for i = 1:header.nsignal
    fwrite(fid, str_zeros(header.signal(i).filters, 80));
end
for i = 1:header.nsignal
    fwrite(fid, str_zeros(header.signal(i).nsamples, 8));
end
for i = 1:header.nsignal
    fwrite(fid, str_zeros(header.signal(i).unknown2, 32));
end

% Sanity check on the written header
assert(ftell(fid) == header.hdrlen);

% Close file
fclose(fid);
% Copy header to the sFile structure
sFileOut.header = header;

end

%% ===== HELPER FUNCTIONS =====
function sout = str_zeros(sin, N)
    if (isnumeric(sin))
        sin_str = num2str(sin);
        if (length(sin_str) > N)
            if ((sin > 10^-(N-3)) && (sin < 10^N-1)) || (N < 7)
                sin_str = sprintf('%f', sin);
            else
                sin_str = sprintf(sprintf('%%0.%de',N-7), sin);
            end
        end
        sin = sin_str;
    end

    sout = char(double(' ') * ones(1,N));
    if (length(sin) <= N)
        sout(1:length(sin)) = sin;
    else
        sout = sin(1:N);
    end
end

% This extracts a subset of the full factors of a number, not just the
% prime factors like factors() returns.
% E.g.: factor(100)      = [2,2,5,5]
%       full_factors(100) = [2,4,5,10,20,25,50]
function factors = full_factors(n)
    factors = factor(n);
    if length(factors) == 1
        factors = [];
        return;
    end
    [occurences, factors] = hist(factors, unique(factors));
    factors = factors(occurences > 0);
    occurences = occurences(occurences > 0);
    nFactors = length(factors);
    possibleFactors = ones(sum(occurences) * nFactors * nFactors);
    iPos = 1;
    for iFactor = 1:nFactors
        for iOcc = 1:occurences(iFactor)
            fact = factors(iFactor) ^ iOcc;
            possibleFactors(iPos) = fact;
            iPos = iPos + 1;
            for iFactor2 = 1:nFactors
                if iFactor ~= iFactor2
                    possibleFactors(iPos) = fact * factors(iFactor2);
                    iPos = iPos + 1;
                end
            end
        end
    end
    factors = unique(possibleFactors(1:iPos-1));
end


function pstruct = tapas_gaussian_obs_namep(pvec)
% --------------------------------------------------------------------------------------------------
% Copyright (C) 2012-2013 Christoph Mathys, TNU, UZH & ETHZ
%
% This file is part of the HGF toolbox, which is released under the terms of the GNU General Public
% Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL
% (either version 3 or, at your option, any later version). For further details, see the file
% COPYING or <http://www.gnu.org/licenses/>.

pstruct = struct;

pstruct.ze = pvec(1);

return;


function mm_size_print ( input_filename, rep, symm, nrow, ncol, nnz )

%*****************************************************************************80
%
%% MM_SIZE_PRINT prints size information from a Matrix Market file.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    29 April 2004
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, character ( len = * ) INPUT_FILENAME, the input file name.
%
%    Input, character ( len = 10 ) REP, the Matrix Market 'representation'
%    indicator.  Possible values include:
%    'coordinate'   (for sparse data)
%    'array'        (for dense data)
%    'elemental'    (to be added)
%
%    Input, character ( len = 19 ) SYMM, the Matrix Market symmetry.
%    Possible values include:
%    'symmetric'
%    'hermitian'
%    'skew-symmetric'
%    'general'
%
%    Input, integer NROW, the number of rows in the matrix.
%
%    Input, integer NCOL, the number of columns in the matrix.
%
%    Input, integer NNZ, the number of nonzero entries required to store
%    the matrix.
%
  fprintf ( 1, '\n' );
  fprintf ( 1, 'MM_SIZE_PRINT:\n' );
  fprintf ( 1, '  Size information from Matrix Market file "%s"\n', ...
    input_filename );
  fprintf ( 1, '\n' );
  fprintf ( 1, '\n' );
  fprintf ( 1, '  Number of rows    NROW = %d\n', nrow );
  fprintf ( 1, '  Number of columns NCOL = %d\n', ncol );

  if ( s_eqi ( rep, 'coordinate' ) )
    fprintf ( 1, '  Declared number of nonzeros NNZ = %d\n', nnz );
  elseif ( s_eqi ( rep, 'array' ) )
    nnz = mm_nnz_set ( rep, symm, nrow, ncol );
    fprintf ( 1, '  Inferred number of nonzeros NNZ = %d\n', nnz );
  end

  return
end


function h=m_gshhs(resolution,varargin)
% M_GSHHS Add a coastline to a given map using
%           the Global Self-consistant Hierarchical High-resolution
%           Shorelines, Rivers, and Borders
%
%         M_GSHHS(RES, (standard line option,...,...) ) draws the coastline
%         river network, or borders as  simple lines.
%
%         M_GSHHS(RES,'patch' ( ,standard patch options,...,...) ) draws the
%         coastline as a number of patches (rivers and borders are not
%         arranged so patches can be drawn).
%
%         M_GSHHS(RES,'save',FILENAME) saves the extracted coastline data
%         for the current projection in a file FILENAME. This allows
%         speedier replotting using M_USERCOAST(FILENAME).
%
%         RES: A one-char string (optionally 2 or 3)
%
%         First char: resolution - one of
%                      'c'  crude
%                      'l'  low
%                      'i'  intermediate
%                      'h'  high
%                      'f'  full
%
%         Second char: type - one of
%                      'c' GSHHS coastline (default)
%                      'b' WDB Border
%                      'r' WDB River
%
%         Third char - if 2nd char is 'b':
%                      '1' Country borders
%                      '2' State/Province and Country borders
%                    - if 2nd char is 'r': '1','2','3','4'
%                      add successively more tributaries
%
%         (also maintained is this optional format:
%
%         RES - selections resolution
%                  1  or 'crude'
%                  2  or 'low'
%                  3  or 'intermediate'
%                  4  or 'high'
%                  5  or 'full
%
%         but please don't use this).
%
%         See also M_PROJ, M_GRID, M_COAST, M_GSHHS_L, M_GSHHS_H, M_GSHHS_C
%         M_USERCOAST

% Rich Pawlowicz (rich@ocgy.ubc.ca) 15/June/98
%
%
% This software is provided "as is" without warranty of any kind. But
% it's mine, so you can't sell it.
%
%  16/Dec/2005
%*********************************************************************
%  Modified after code provided by Bruce Lipphardt (brucel@udel.edu) to
%  reduce the hierarchy of M_GSHHS_* routines to a single routine with a
%  variable resolution input:
% 20/Jan/2008 - added borders and rivers from gshhs v1.10
% 4/DEc/11 - isstr to ischar
% Sep/14 - added hierarchy to borders
% Aug/18 - fixed error that occurred when called m_gshhs_X with 'save'
%          option (Thanks to H. Grant for pointing this out).

% Root of directories where all the gshhs_X.b, wdb_borders-X.b and wdb_rivers_X.b
% files live
FILNAME='private/';

%-------------don't change below here----------------------------

res_list = {'c','l','i','h','f'};
typ_list={'c','b','r'};
typ_names={'gshhs_','wdb_borders_','wdb_rivers_'};

typ=1;
flaglim='9';

if ischar(resolution)
 if length(resolution)>=2
   typ = find(strcmpi(resolution(2),typ_list));
 end
 if length(resolution)>=3
   flaglim = resolution(3);
 end
 resolution =  find(strcmpi(resolution(1),res_list));
end

if isempty(resolution) || resolution<1 || resolution> length(res_list)
  error('**Don''t recognize the specified resolution');
end
if isempty(typ) || typ<1 || typ> length(res_list)
  error('**Don''t recognize the specified type');
end

res_char = res_list{resolution} ;
file     = [FILNAME,sprintf('%s%s.b',typ_names{typ},res_char)] ;
tag_name = sprintf('m_%s%s',typ_names{typ},res_char) ;

% Set current projection to geographic
Currentmap=m_coord('set');
m_coord('geographic');

if length(varargin)>1 && strcmp(varargin{1},'save')
  [ncst,Area,k]=mu_coast(res_char,file);
  save(varargin{2},'ncst','k','Area');
  h=varargin{2};   % Error if you call m_gshhs_i with 'save' option - thanks HG, Aug/1/2018
else
  h=mu_coast([res_char flaglim],file,varargin{:},'tag',tag_name);
end

m_coord(Currentmap.name);

if nargout==0
    clear h
end


%REMCLASS Remove small classes
%
%   B = REMCLASS(A,N)
%
% INPUT
%   A   Dataset
%   N   Integer, maximum class size to be removed (optional; default 0)
%
% OUTPUT
%   B   Dataset
%
% DESCRIPTION
% Classes having N objects or less are removed. The corresponding objects
% are made unlabeled. Use SELDAT to remove unlabeled objects.
%
% SEE ALSO
% SELDAT, GENDAT

function [b, L] = my_remclass(a,n)
	prtrace(mfilename);
	if nargin < 2, n = 0; end

	N = classsizes(a);
	J = find(N <= n);
	L = findnlab(a,J);
	b = setnlab(a,0,L);
	b = setlablist(b);

return


function [lon,lat]=nj_lonlat(ncRef,var);
% NJ_LONLAT  find longitude and latitude variables
% [lon,lat]=nj_lonlat(fname,var);
%    Inputs: fname = file name, url, or netcdf file object
%            var   = variable name (e.g. 'U10')
%         idouble  = [0] default, [1] convert output to double
%    Output: lon   = longitude array corresponding to "var"
%            lat   = latitude array corresponding to "var"

% import the NetCDF-Java methods we need for this example

% Rich Signell (rsignell@usgs.gov);

if nargin < 2, help(mfilename), return, end

try
    if (isa(ncRef, 'ncgeodataset')) %check for ncgeodataset Object
        nc = ncRef;
    else
        % open CF-compliant NetCDF File as a Common Data Model (CDM) "Grid Dataset"
        nc = ncgeodataset(ncRef);
    end
    gvar=nc.geovariable(var);
    lon=nc{gvar.getlonname}(:);
    lat=nc{gvar.getlatname}(:);
catch
    %gets the last error generated
    err = lasterror();
    disp(err.message);
end


% envproj() - plot envelopes of projections of selected ICA component
%             projections against envelope of the original data
%
% Usage:    >> [envdata] = envproj(data,weights,compnums);
%           >> [envdata] = envproj(data,weights,compnums, ...
%                                  title,limits,chanlist,compnames,colors);
% Inputs:
%   data      = runica() input data (chans,frames) <- best one epoch only!
%   weights   = unmixing weight matrix (runica() weights*sphere)
%   compnums  = list of component numbers to project and plot
%
% Optional inputs:
%   title     = 'fairly short plot title' (in quotes) (0 -> none)
%   limits    = [xmin xmax ymin ymax] (x's in msec)
%                       (0 or both y's 0 -> [min max])
%   chanlist  = list of data channels to use for max/min (0 -> all)
%   compnames = file of component labels (exactly 5 chars per line,
%               trailing '.' = space) (default|0 -> compnums)
%   colors    = file of color codes, 3 chars per line  ('.' = space)
%                       (0 -> default color order (black/white first))
%
% Output:
%   envdata   = [2,(frames*length(compnums)+1)] envelopes of data, comps.
%
% Author: Scott Makeig, SCCN/INC/UCSD, La Jolla, 1/1997
%
% See also: envtopo()

% Copyright (C) 01-23-97 Scott Makeig, SCCN/INC/UCSD, scott@sccn.ucsd.edu
%
% This program is free software; you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation; either version 2 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program; if not, write to the Free Software
% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

% 03-19-97 use datamean instead of frames/baseframes, diag(cov()) for var() -sm
% 04-03-97 changed name to envproj() -sm
% 05-20-97 used sum-of-squares for var instead of diag() to allow long data
%          read MAXPLOTDATACHANS from icadefs.m -sm
% 06-07-97 changed order of args to conform to runica -sm
% 06-18-97 read MAXENVPLOTCHANS instead of MAXPLOTDATACHANS -sm
% 07-23-97 dropped datamean from args; mean is distributed among components -sm
% 10-11-97 range of max/min for default ylimits over chanlist only! -sm
% 11-05-97 disallowed white lines unless axis color is white -sm & ch
% 11-13-97 rm'd errorcode variable to improve limits handling -sm
% 11-18-97 added legend to plot; varied line types -sm
% 11-30-97 added f=figure below to keep previous plot from being altered -sm
% 12-01-97 added thicker lines for data envelope -sm
% 12-08-97 debugged thicker lines for data envelope -sm
% 12-10-97 changed f=figure below to f=gcf to avoid matlab bug (gcf confusion) -sm
% 12-10-97 implemented compnames arg -sm
% 12-13-97 took out compnames=compnames'; bug -sm
% 02-06-98 added 'show-largest' feature to legend when numcomps > 7 -sm
% 02-09-98 fixed numcomps bug -sm
% 02-12-98 test for outofbounds compnums early -sm
% 04-28-98 made to work for rectangular weights -sm
% 06-05-98 made NEG_UP optional -sm
% 02-22-99 added FILL mode default when numcomps==1 -sm
% 03-14-99 made envelope finding code more efficient -sm
% 12-19-00 updated icaproj() args -sm
% 01-25-02 reformated help & license, added links -ad

function [envdata] = envproj(data,weights,compnums,titl,limits,chanlist,compnames,colors)

FILL = 1; % 1;  % 1 = fill in component envelope unless ncomps>1
DATA_LINEWIDTH = 1.5; % 2
COMP_LINEWIDTH = 1.5; % 1
MAXENVPLOTCHANS = 256;

if nargin < 3,
    help envproj
    fprintf('envproj(): must have at least three arguments.\n');
    return
end

icadefs        % load ENVCOLORS & MAXENVPLOTCHANS default variables
MAX_LEG = 7;   % maximum number of component line types to label in legend
NEG_UP = 0;    % 1 = plot negative up

%
%%%%%%%%%%%% Substitute for omitted arguments %%%%%%%%%%%%%%%%%%%%%%%%
%
if nargin < 8,
    colors = 'envproj.col';
elseif colors(1)==0,
    colors = 'envproj.col';
end

[chans,frames] = size(data);

if nargin < 7
   compnames = 0;
end

if nargin < 6
    chanlist = 0;
end
if nargin < 5,
    limits = 0;
end
if nargin < 4,
    titl = 0;
end
%
%%%%%%%%%%%%%%%%%%%%%%%% Test data size %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
[wr,wc]       = size(weights);
%
%%%%%%%%%%%%%%%% Substitute for zero arguments %%%%%%%%%%%%%%%%%%%%%%%%%
%
if chanlist == 0,
    chanlist = [1:chans];
end;
if compnums == 0,
    compnums = [1:wr];
end;
if size(compnums,1)>1,        % handle column of compnums !
    compnums = compnums';
end;
numcomps = length(compnums);
if numcomps > MAXENVPLOTCHANS,
    fprintf(...
'envproj(): cannot plot more than %d channels of data at once.\n',...
                  MAXENVPLOTCHANS);
    return
end;

if max(compnums)>wr
 fprintf('\nenvproj(): Component index %d out of bounds (1:%d).\n',...
                                             max(compnums),wr);
 return
end

if min(compnums)<1,
 fprintf('\nenvproj(): Component index %d out of bounds (1:%d).\n',...
                                             min(compnums),wr);
 return
end

if FILL>0 & numcomps == 1
   FILL = 1;
else
   FILL = 0;
end
%
%%%%%%%%%%%%%%%%%%%% Read and adjust limits %%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
  if limits==0,
    xmin=0;xmax=1;
    ymin=min(min(data(chanlist,:)));
    ymax=max(max(data(chanlist,:)));
  else
    if length(limits)~=4,
        fprintf( ...
 'envproj(): limits should be 0 or an array [xmin xmax ymin ymax].\n');
        return
      end;
        if limits(1,1) == 0 & limits(1,2) ==0,
            xmin=0;
            xmax=0;
        else
            xmin = limits(1,1);
            xmax = limits(1,2);
        end;
         if limits(1,3) == 0 & limits(1,4) ==0,
            ymin=0;
            ymax=0;
        else
            ymin = limits(1,3);
            ymax = limits(1,4);
        end;
  end;

  if xmax == 0 & xmin == 0,
    x = (0:1:frames-1);
    xmin = 0;
    xmax = frames-1;
  else
    dx = (xmax-xmin)/(frames-1);
    x=xmin*ones(1,frames)+dx*(0:frames-1); % construct x-values
  end;

  if ymax == 0 & ymin == 0,
    ymax=max(max(data(chanlist,:)));
    ymin=min(min(data(chanlist,:)));
  end

%
%%%%%%%%%%%%%%%%%%%% Read the color names %%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
    if ~isstr(colors)
        fprintf('envproj(): color file name must be a string.\n');
        return
    end
    cid = fopen(colors,'r');
    if cid <3,
        fprintf('envproj(): cannot open file %s.\n',colors);
        return
    else
        colors = fscanf(cid,'%s',[3 MAXENVPLOTCHANS]);
        colors = colors';
        [r c] = size(colors);
        for i=1:r
            for j=1:c
                if colors(i,j)=='.',
                    colors(i,j)=' ';
                end;
            end;
        end;
    end;
[rr cc] = size(colors);

%
%%%%%%%%%%%% Compute projected data for single components %%%%%%%%%%%%%
%
projdata = [data(chanlist,:)];
envdata = [min(projdata); max(projdata)]; % begin with envelope of data
fprintf('envproj(): Projecting component(s) ');
maxvars = zeros(1,length(compnums));
n=1;
for c=compnums,            % for each component
  fprintf('%d ',c);
  % [icaproj] = icaproj(data,weights,compindex); % new arg order 12/00
  proj = icaproj(data,weights,c); % let offsets be
                                         % distributed among comps.
  [val,i] = max(sum(proj.*proj)); % find max variance
  maxvars(n) = val;
  proj = proj(chanlist,:);
  projdata = [projdata proj];
  envtmp = [ min(proj) ; max(proj)];
  envdata = [envdata envtmp];
                 % append envelope of projected data sets onto envdata
  % Note: size(envdata) = [length(chanlist)  frames*(numcomps+1)]
  n = n+1;
end;
fprintf('\n');
%
%%%%%%%%%%%%%%%%%%%%%%%% Make the plot %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
if ~isstr(titl)
  if titl==0,
    titl = ' ';
  else
    fprintf('envproj(): titl unrecognized.\n');
    return
  end
end

set(gcf,'Color',BACKCOLOR); % set the background color
set(gca,'Color','none'); % set the axis color to the background color
if NEG_UP
  set(gca,'Ydir','reverse');
end
n = 1;
H = zeros(1,numcomps+1);

for c=1:numcomps+1;
  %
  % Plot envelopes
  %
  if n <= 6
    if n == 1              % thick lines, store H for legend()
      H(n)=plot(x,envdata(1,(c-1)*frames+1:c*frames)',...
                                     colors(c),'Linewidth',DATA_LINEWIDTH);
      hold on;
           plot(x,envdata(2,(c-1)*frames+1:c*frames)',...
                                     colors(c),'Linewidth',DATA_LINEWIDTH);
    else
       if FILL
            fillx = [x x(frames:-1:1)];
            filly = [envdata(1,(c-1)*frames+1:c*frames) ...
                            envdata(2,c*frames:-1:(c-1)*frames+1)];
            i = find(isnan(filly)==1);
            if ~isempty(i)
                fprintf('Replacing %d NaNs in envelope with 0s.\n',length(i)/2);
                filly(i)=0;
            end
            H(n) = fill(fillx,filly,colors(c));
       else
            H(n)= plot(x,envdata(1,(c-1)*frames+1:c*frames)',...
                                     colors(c),'Linewidth',COMP_LINEWIDTH);
             hold on;
            plot(x,envdata(2,(c-1)*frames+1:c*frames)',...
                                     colors(c),'Linewidth',COMP_LINEWIDTH);
       end
    end
  else
    H(n)= plot(x,envdata(1,(c-1)*frames+1:c*frames)',...
                                     colors(c),'LineStyle',':','LineWidth',DATA_LINEWIDTH);
    hold on;
          plot(x,envdata(2,(c-1)*frames+1:c*frames)',...
                                     colors(c),'LineStyle',':','LineWidth',DATA_LINEWIDTH);
  end
  n = n+1;
end
set(gca,'Color','none'); % set the axis color to the background color

l=xlabel('Time (ms)');      % xaxis label
set(l,'FontSize',14);

l=ylabel('Potential (uV)'); % yaxis label
set(l,'FontSize',14);

if xmax > 0  & xmin < 0
  plot([0 0],[ymin ymax],'k','linewidth',1.5); % plot vertical line at time 0
end

if ymax ~= 0 |ymin~= 0,
    axis([min(x),max(x),ymin,ymax]);
else
    axis([min(x),max(x),min(envdata(1,:)'),max(envdata(2,:)')]);
end
legtext = [ 'Data ' ];
if compnames(1) ~= 0
    pid = fopen(compnames,'r');
    if pid < 3
       fprintf('envproj(): file %s not found.\n',compnames);
       return
    end
    compnames = fscanf(pid,'%s',[5 numcomps]);
    compnames = [ ['Data.']' compnames];
    compnames = compnames';
    if size(compnames,1) ~= numcomps+1
      fprintf('envproj(): no. of compnames must equal no. of compnums.\n');
      return
    end
    for c=1:5 % remove padding with .'s
      for r = 1:size(compnames,1)
         if compnames(r,c) == '.'
           compnames(r,c) = ' ';
         end
      end
    end
    legtext = compnames;
else
  legtext = ['Data'];
  for c = compnums
      legtext = strvcat(legtext,int2str(c));
  end
end

if numcomps<MAX_LEG+1
  if FILL <= 0  % no legend in FILL mode
       l=legend(H,legtext,0);      % plot legend
  end
else
  fprintf('Finding largest components for legend...\n');
  [maxvars,i] = sort(maxvars);
  maxvars = maxvars(length(maxvars):-1:1); % sort large-to-small
  i= i(length(i):-1:1);
  H = [H(1) H(i+1)];
  H = H(1:MAX_LEG+1);
  legtext = [legtext(1,:);legtext(i+1,:)];
  legtext = legtext(1:MAX_LEG+1,:);
  legend(H,legtext,0); % MAX_LEG largest-peaked comps
end

set(gca,'FontSize',12);
fprintf('If desired, use mouse to move legend or >> delete(legend).\n');

%
%%%%%%%%%%%%% Compute percentage of variance accounted for %%%%%%%%%%%%
%
sumdata = zeros(length(chanlist),frames);
for e=2:numcomps+1,              % sum the component activities
    sumdata = sumdata + projdata(:,(e-1)*frames+1:e*frames);
end

sigssqr = sum(data(chanlist,:).*data(chanlist,:))/(length(chanlist)-1);
dif    = data(chanlist,:)-sumdata;
difssqr = sum(dif.*dif)/(length(chanlist)-1);
pvaf   = round(100.0*(1.0-difssqr/sigssqr)); % % variance accounted for
rtitl = ['(' int2str(pvaf) '%)'];
%titl = [titl '   ' rtitl];

t=title(titl);              % plot title
set(t,'FontSize',14);


function polyOut = parsePolygon(poly, format, varargin)
%PARSEPOLYGON Conversion between different polygon formats.
%
%   POLYOUT = parsePolygon(POLY, FORMAT)
%   Converts POLY to the specified format while FORMAT must be one of the
%   following options: 'cell','nan','repetition','polyshape'. Mainly used
%   in other polygon functions to bring the polygon into the required
%   format.
%
%   Example
%     r = [2.5, 2, 1];
%     poly = flipud(circleToPolygon([0 0 r(1)], round(2*pi*r(1))));
%     midCircle = circleToPolygon([0 0 r(2)], round(2*pi*r(2)));
%     innerCircle = flipud(circleToPolygon([0 0 r(3)], round(2*pi*r(3))));
%     poly = {poly, midCircle, innerCircle};
%     polyOut = parsePolygon(poly, 'repetition');
%     polyOut = parsePolygon(polyOut, 'nan');
%     polyOut = parsePolygon(polyOut, 'polyshape');
%     polyOut = parsePolygon(polyOut, 'cell');
%     assert(isequal(poly, polyOut))
%
%   See also
%   polygonToPolyshape
%

% ------
% Author: oqilipo
% E-mail: N/A
% Created: 2023-01-01, using MATLAB 9.13.0.2080170 (R2022b) Update 1
% Copyright 2023

% Parsing
p = inputParser;
addRequired(p,'format',...
    @(x) any(validatestring(x,{'cell','nan','repetition','polyshape'})));
parse(p,format,varargin{:});

format = p.Results.format;

% If polygon is a polyshape object
if isa(poly,'polyshape')
    polyOut = parsePolygon(poly.Vertices, format);
    return
end

% If polygon is a cell
if iscell(poly)
    [r, c] = size(poly);
    if r > 1 && c > 1
        error(['Non-supported polygon cell format with ' num2str(r) ' rows and ' num2str(c) 'columns!'])
    elseif r > 1 && c == 1
        polyCell = poly';
    elseif r == 1 && c > 1
        polyCell = poly;
    elseif r == 1 && c == 1
        polyCell = poly;
    end
    if any(cellfun(@(x) any(isnan(x(:))), poly))
        error('Non-supported polygon cell format. Polygons in cell format should not contain NaN values!')
    end
end

if ~iscell(poly) && any(isnan(poly(:)))
    % If polygon contains nan
    polyCell = splitPolygons(poly)';
elseif ~iscell(poly)
    % If polygon contains vertex repetitions
    polyBackup = poly;
    polyCell = {};
    count = 0;
    while ~isempty(poly)
        count = count+1;
        repLIdx = ismember(poly, poly(1,:), 'rows');
        repIdx = find(repLIdx);
        if sum(repLIdx) == 1
            polyCell = {poly};
            if count == 1
                break
            else
                warning('Contains vertex repetitions but the last vertex is not a repetiton!')
                polyCell = {polyBackup};
            end
        elseif sum(repLIdx)>1
            polyCell = [polyCell, {poly(1:repIdx(2)-1,:)}]; %#ok<AGROW>
            poly(1:repIdx(2),:) = [];
        end
    end
end

% Convert to output format
switch format
    case 'cell'
        polyOut = polyCell;
    case 'nan'
        polyOut = cell2mat((cellfun(@(x) [x; nan(1, size(x,2))], polyCell, 'UniformOutput',false))');
        polyOut(end,:) = [];
    case 'repetition'
        polyOut = cell2mat((cellfun(@(x) [x; x(1,:)], polyCell, 'UniformOutput',false))');
    case 'polyshape'
        polyOut = polyshape(polyCell{1});
        NoP = length(polyCell);
        if NoP > 1
            for p=2:NoP
                polyOut = addboundary(polyOut, polyCell{p});
            end
        end
end

end


function test_suite = test_setopt
initTestSuite;

function test_setopt_all_defaults
  x            = [];
  default_opts = [5 6 7 8];
  z = setopt(x, default_opts);
  z_corr       = [5 6 7 8];
assertVectorsAlmostEqual(z, z_corr, 'relative', 0.0001);

function test_setopt_nonzero_becomes_zero
  x            = [1 0 3];
  default_opts = [5 6 7 8];
  z = setopt(x, default_opts);
  z_corr       = [1 6 3 8];
  %z_corr       = [1 0 3 8];   % This would be more intuitive
assertVectorsAlmostEqual(z, z_corr, 'relative', 0.0001);


function value = p13_autonomous ( )

%*****************************************************************************80
%
%% P13_AUTONOMOUS reports whether problem P13 is autonomous.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    17 February 2013
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Output, integer VALUE, is 1 if the equation is autonomous.
%
  value = 1;

  return
end


function test_suite = test_clipRay
% Tests the function 'clipRay'
%   output = testClipRay(input)
%
%   Example
%   testClipRay
%
%   See also
%
%
% ------
% Author: David Legland
% e-mail: david.legland@grignon.inra.fr
% Created: 2009-04-22,    using Matlab 7.7.0.471 (R2008b)
% Copyright 2009 INRA - Cepia Software Platform.
% Licensed under the terms of the LGPL, see the file "license.txt"

test_suite = functiontests(localfunctions);

function testRightSide(testCase) %#ok<*DEFNU>
% test edges totally inside window, possibly touching edges

% a basic bounding box
box = [0 100 0 100];

% inside
origin      = [30 40];
direction   = [10 0];
ray         = [origin direction];
expected    = [30 40 100 40];
testCase.assertEqual(expected, clipRay(ray, box), 'AbsTol', .01);

% outside
origin      = [30 140];
direction   = [10 0];
ray         = [origin direction];
testCase.assertTrue(sum(isnan(clipRay(ray, box)))==4);

% line inside, but ray outside
origin      = [130 40];
direction   = [10 0];
ray         = [origin direction];
testCase.assertTrue(sum(isnan(clipRay(ray, box)))==4);

function testLeftSide(testCase)
% test edges totally inside window, possibly touching edges

% a basic bounding box
box = [0 100 0 100];

% inside
origin      = [30 40];
direction   = [-10 0];
ray         = [origin direction];
expected    = [30 40 0 40];
testCase.assertEqual(expected, clipRay(ray, box), 'AbsTol', .01);

% outside
origin      = [30 140];
direction   = [-10 0];
ray         = [origin direction];
testCase.assertTrue(sum(isnan(clipRay(ray, box)))==4);

% line inside, but ray outside
origin      = [-30 40];
direction   = [-10 0];
ray         = [origin direction];
testCase.assertTrue(sum(isnan(clipRay(ray, box)))==4);

function testUpSide(testCase)
% test edges totally inside window, possibly touching edges

box = [0 100 0 100];

% inside
origin      = [30 40];
direction   = [0 10];
ray         = [origin direction];
expected    = [30 40 30 100];
testCase.assertEqual(expected, clipRay(ray, box), 'AbsTol', .01);

% outside
origin      = [130 40];
direction   = [0 10];
ray         = [origin direction];
testCase.assertTrue(sum(isnan(clipRay(ray, box)))==4);

% line inside, but ray outside
origin      = [30 140];
direction   = [0 10];
ray         = [origin direction];
testCase.assertTrue(sum(isnan(clipRay(ray, box)))==4);

function testDownSide(testCase)
% test edges totally inside window, possibly touching edges

box = [0 100 0 100];

% inside
origin      = [30 40];
direction   = [0 -10];
ray         = [origin direction];
expected    = [30 40 30 0];
testCase.assertEqual(expected, clipRay(ray, box), 'AbsTol', .01);

% outside
origin      = [130 40];
direction   = [0 -10];
ray         = [origin direction];
testCase.assertTrue(sum(isnan(clipRay(ray, box)))==4);

% line inside, but ray outside
origin      = [30 -40];
direction   = [0 -10];
ray         = [origin direction];
testCase.assertTrue(sum(isnan(clipRay(ray, box)))==4);

function testArray(testCase)
% test with an array of rays and a box

box = [0 100 0 100];

origins     = [30 40;30 40;30 140;130 40];
directions  = [10 0;0 10;10 0;0 10];
rays        = [origins directions];
expected    = [30 40 100 40;30 40 30 100;NaN NaN NaN NaN;NaN NaN NaN NaN];
clipped     = clipRay(rays, box);
testCase.assertEqual(expected, clipped, 'AbsTol', .01);


function DEM = Readopentopo(varargin)

%READOPENTOPO read DEM using the opentopography.org API
%
% Syntax
%
%     DEM = readopentopo(pn,pv,...)
%
% Description
%
%     readopentopo reads DEMs from opentopography.org using the API
%     described on:
%     http://www.opentopography.org/developers
%     The DEM comes in geographic coordinates (WGS84) and should be
%     projected to a projected coordinate system (use reproject2utm) before
%     analysis in TopoToolbox.
%
% Input arguments
%
%     Parameter name values
%     'filename'       provide filename. By default, the function will save
%                      the DEM to a temporary file in the system's temporary
%                      folder.
%     'north'          northern boundary in geographic coordinates (WGS84)
%     'south'          southern boundary
%     'west'           western boundary
%     'east'           eastern boundary
%     'demtype'        The global raster dataset - SRTM GL3 (90m) is
%                      'SRTMGL3', SRTM GL1 (30m) is 'SRTMGL1', SRTM GL1
%                      (Ellipsoidal) is 'SRTMGL1_E', and ALOS World 3D 30m
%                      is 'AW3D30'
%     'deletefile'     'true' or false. True, if file should be deleted
%                      after it was downloaded and added to the workspace.
%
% Output arguments
%
%     DEM            Digital elevation model in geographic coordinates
%                    (GRIDobj)
%
% See also: GRIDobj, websave
%
% Reference: http://www.opentopography.org/developers
%
% Author: Wolfgang Schwanghart (w.schwanghart[at]geo.uni-potsdam.de)
% Date: 19. June, 2017

p = inputParser;
addParameter(p,'filename',[tempname '.tif']);
% addParameter(p,'interactive',false);
addParameter(p,'north',37.091337);
addParameter(p,'south',36.738884);
addParameter(p,'west',-120.168457);
addParameter(p,'east',-119.465576);
addParameter(p,'demtype','SRTMGL3');
addParameter(p,'deletefile',true);
parse(p,varargin{:});

demtype = validatestring(p.Results.demtype,{'SRTMGL3','SRTMGL1','SRTMGL1_E','AW3D30'},'readopentopo');

url = 'http://opentopo.sdsc.edu/otr/getdem';

% create output file
f = fullfile(p.Results.filename);

% save to drive
options = weboptions('Timeout',inf);

west = p.Results.west;
east = p.Results.east;
south = p.Results.south;
north = p.Results.north;

% if any([isempty(west) isempty(east) isempty(south) isempty(north)]) || p.Results.interactive;
%     wm = webmap;
%     % get dialog box
%     messagetext = ['Zoom and resize the webmap window to choose DEM extent. ' ...
%                          'Click the close button when you''re done.'];
%     d = waitdialog(messagetext);
%     uiwait(d);
%     [latlim,lonlim] = wmlimits(wm);
%     west = lonlim(1);
%     east = lonlim(2);
%     south = latlim(1);
%     north = latlim(2);
% end

websave(f,url,'west',west,...
              'east',east,...
              'north',north,...
              'south',south,...
              'outputFormat', 'GTiff', ...
              'demtype', demtype, ...
              options);

DEM      = GRIDobj(f);
DEM.name = demtype;

if p.Results.deletefile
    delete(f);
end
end

% function d = waitdialog(messagetext)
%     d = dialog('Position',[300 300 250 150],'Name','Choose rectangle region',...
%         'WindowStyle','normal');
%
%     txt = uicontrol('Parent',d,...
%                'Style','text',...
%                'Position',[20 80 210 40],...
%                'String',messagetext);
%
%     btn = uicontrol('Parent',d,...
%                'Position',[85 20 70 25],...
%                'String','Close',...
%                'Callback','delete(gcf)');
% end


function x = p12_start ( option, nvar )

%*****************************************************************************80
%
%% P12_START returns a starting point for problem 12.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    03 September 2008
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer OPTION, the option index.
%
%    Input, integer NVAR, the number of variables.
%
%    Output, real X(NVAR), the starting point.
%
  x = zeros ( nvar, 1 );

  return
end


function [out] = rowvector(in)
% Transposes the input vector to a row vector if it is not
% already a row vector. Called by `verifyModel`.
%
% USAGE:
%
%    out = columnvector(in)
%
% INPUTS:
%    in:     Vector
%
% OUTPUTS:
%    out:    Column vector
%
% Please cite:
% `Sauls, J. T., & Buescher, J. M. (2014). Assimilating genome-scale
% metabolic reconstructions with modelBorgifier. Bioinformatics
% (Oxford, England), 30(7), 1036?8`. http://doi.org/10.1093/bioinformatics/btt747
%
% ..
%    Edit the above text to modify the response to help addMetInfo
%    Last Modified by GUIDE v2.5 06-Dec-2013 14:19:28
%    This file is published under Creative Commons BY-NC-SA.
%
%    Correspondance:
%    johntsauls@gmail.com
%
%    Developed at:
%    BRAIN Aktiengesellschaft
%    Microbial Production Technologies Unit
%    Quantitative Biology and Sequencing Platform
%    Darmstaeter Str. 34-36
%    64673 Zwingenberg, Germany
%    www.brain-biotech.de

dim = size(in) ;
if dim(1) > dim(2)
    out = in' ;
else
    out = in ;
end


function status = isrealvec(x)

% ISREALVEC returns true for a real row or column vector
%
% Use as
%   status = isrealvec(x)
%
% See also ISNUMERIC, ISREAL, ISVECTOR, ISREALMAT

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

% This is a drop-in replacement for MATLAB/toolbox/ident/idutils/isrealvec.m
% which requires the Ident toolbox. The following can be used instead to
% replicate the same functionality.
%
% built-in (/Applications/MATLAB_R2010a.app/toolbox/matlab/datatypes/isnumeric)
% built-in (/Applications/MATLAB_R2010a.app/toolbox/matlab/elfun/isreal)
% built-in (/Applications/MATLAB_R2010a.app/toolbox/matlab/elmat/isvector)
% /Applications/MATLAB_R2010a.app/toolbox/matlab/elmat/ndims.m

status = isnumeric(x) && isreal(x) && length(size(x))==2 && any(size(x)==1);


function res = isdigit(s)
% res = isdigit(s);
% res(i) = 1 is s(1) is a digit between 0 and 9 (inclusive)
%
id = find(s >= '0' & s <= '9');
res = zeros(size(s));
res(id) = 1;

return;


function i4mat_transpose_print_some ( m, n, a, ilo, jlo, ihi, jhi, title )

%*****************************************************************************80
%
%% I4MAT_TRANSPOSE_PRINT_SOME prints some of an I4MAT, transposed.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    21 June 2005
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer M, N, the number of rows and columns.
%
%    Input, integer A(M,N), an M by N matrix to be printed.
%
%    Input, integer ILO, JLO, the first row and column to print.
%
%    Input, integer IHI, JHI, the last row and column to print.
%
%    Input, string TITLE, an optional title.
%
  incx = 10;

  if ( 0 < s_len_trim ( title ) )
    fprintf ( 1, '\n' );
    fprintf ( 1, '%s\n', title );
  end

  for i2lo = max ( ilo, 1 ) : incx : min ( ihi, m )

    i2hi = i2lo + incx - 1;
    i2hi = min ( i2hi, m );
    i2hi = min ( i2hi, ihi );

    inc = i2hi + 1 - i2lo;

    fprintf ( 1, '\n' );
    fprintf ( 1, '  Row: ' );
    for i = i2lo : i2hi
      fprintf ( 1, '%7d  ', i );
    end
    fprintf ( 1, '\n' );
    fprintf ( 1, '  Col\n' );
    fprintf ( 1, '\n' );

    j2lo = max ( jlo, 1 );
    j2hi = min ( jhi, n );

    for j = j2lo : j2hi

      fprintf ( 1, '%5d  ', j );
      for i2 = 1 : inc
        i = i2lo - 1 + i2;
        fprintf ( 1, '%7d  ', a(i,j) );
      end
      fprintf ( 1, '\n' );

    end

  end

  fprintf ( 1, '\n' );

  return
end


function [ y, m, d, f ] = frac_carry_republican ( y, m, d, f )

%*****************************************************************************80
%
%% FRAC_CARRY_REPUBLICAN carries fractions from days in a Republican YMDF date.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    20 June 2012
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input/output, integer Y, M, D, real F,
%    a YMDF date.
%
  if ( f < 1.0 )
    return
  end

  days = floor ( f );

  f = f - days;
  d = d + days;

  [ y, m, d ] = day_carry_republican ( y, m, d );

  return
end


function [ node_num, node_dim ] = triangle_node_size_example ( )

%*****************************************************************************80
%
%% GMSH_MESH2D_NODE_SIZE_EXAMPLE: sizes of node information for the example.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    23 October 2014
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Output, integer NODE_NUM, the number of nodes.
%
%    Output, integer NODE_DIM, the spatial dimension.
%
  node_num = 21;
  node_dim = 2;

  return
end


function [ algs ] = sgd_solver_list(category)
% Return list of solvers.
%
% Inputs:
%       category    category to be returned.
% Output:
%       algs        list of solvers in the category
%
% This file is part of GDLibrary.
%
% Created by H.Kasai on Nov. 1, 2016
% Modified by H.Kasai on Sep. 28, 2017

    % supported algorithms by SGDLibrary
    basic_algs = {'SGD','SGD-CM','SGD-CM-NAG','AdaGrad','RMSProp','AdaDelta','Adam','AdaMax'};
    vr_algs = {'SVRG','SAG','SAGA','SARAH'};
    qnewton_algs = {'SQN','SVRG-SQN','SVRG-LBFGS','SS-SVRG', ...
        'oBFGS-Inf','oBFGS-Lim','Reg-oBFGS-Inf','Reg-oBFGS-Lim','Damp-oBFGS-Inf','Damp-oBFGS-Lim'};
    else_algs = {'SVRG-BB','IQN'};

    switch category
        case 'BASIC'
            algs = basic_algs;
        case 'VR'
            algs = vr_algs;
        case 'QN'
            algs = qnewton_algs;
        case 'ALL'
            algs = [basic_algs, vr_algs, qnewton_algs, else_algs];
        otherwise
    end

end


function [id, gc_x, gc_y, gc_z] = load_gc_traj_data(gcTrajFile)
%     NeuroSLAM System Copyright (C) 2018-2019
%     NeuroSLAM: A Brain inspired SLAM System for 3D Environments
%
%     Fangwen Yu (www.yufangwen.com), Jianga Shang, Youjian Hu, Michael Milford(www.michaelmilford.com)
%
%     The NeuroSLAM V1.0 (MATLAB) was developed based on the OpenRatSLAM (David et al. 2013).
%     The RatSLAM V0.3 (MATLAB) developed by David Ball, Michael Milford and Gordon Wyeth in 2008.
%
%     Reference:
%     Ball, David, Scott Heath, Janet Wiles, Gordon Wyeth, Peter Corke, and Michael Milford.
%     "OpenRatSLAM: an open source brain-based SLAM system." Autonomous Robots 34, no. 3 (2013): 149-176.
%
%     This program is free software: you can redistribute it and/or modify
%     it under the terms of the GNU General Public License as published by
%     the Free Software Foundation, either version 3 of the License, or
%     (at your option) any later version.
%
%     This program is distributed in the hope that it will be useful,
%     but WITHOUT ANY WARRANTY; without even the implied warranty of
%     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%     GNU General Public License for more details.
%
%     You should have received a copy of the GNU General Public License
%     along with this program.  If not, see <http://www.gnu.org/licenses/>.

    %% Import data from text file.
    % Script for importing data from the following text file:

    %% Initialize variables.
    delimiter = ' ';
    startRow = 1;

    %% Format string for each line of text:
    %   column1: double (%d)
    %	column2: double (%f)
    %   column3: double (%f)
    %	column4: double (%f)
    %   column5: double (%f)
    %	column6: double (%f)
    %   column7: double (%f)
    % For more information, see the TEXTSCAN documentation.
    formatSpec = '%d%f%f%f%[^\n\r]';

    %% Open the text file.
    fileID = fopen(gcTrajFile,'r');

    %% Read columns of data according to format string.
    % This call is based on the structure of the file used to generate this
    % code. If an error occurs for a different file, try regenerating the code
    % from the Import Tool.
    dataArray = textscan(fileID, formatSpec, 'Delimiter', delimiter, 'EmptyValue' ,NaN,'HeaderLines' ,startRow-1, 'ReturnOnError', false);

    %% Close the text file.
    fclose(fileID);

    %% Allocate imported array to column variable names
    id = dataArray{:, 1};
    gc_x = dataArray{:, 2};
    gc_y = dataArray{:, 3};
    gc_z = dataArray{:, 4};

    %% Clear temporary variables
    clearvars gcTrajFile delimiter startRow formatSpec fileID dataArray ans;
end


classdef TestRotate
    %TestRotate

    methods (Static)
        function test_1
            src = magic(5);

            codes = {'90CW', '90CCW', '180'};
            for i=1:numel(codes)
                dst = cv.rotate(src, codes{i});
                validateattributes(dst, {class(src)}, {'size',size(src)});
            end
        end

        function test_error_argnum
            try
                cv.rotate();
                throw('UnitTest:Fail');
            catch e
                assert(strcmp(e.identifier,'mexopencv:error'));
            end
        end
    end

end


function [varargout]=graphicsModels(varargin)

% function [F,V]=graphicsModels(modelID)
% ------------------------------------------------------------------------
%
%
% Kevin Mattheus Moerman
% gibbon.toolbox@gmail.com
%
% 2015/04/25 Added to GIBBON
% 2019 added David, dolfin
% 2020 added spine
% 2020/07/28 added zero output mode (show surface)
%------------------------------------------------------------------------

%% Parse input

switch nargin
    case 0
        modelID=1;
    case 1
        modelID=varargin{1};
end

%%
defaultFolder = fileparts(fileparts(mfilename('fullpath')));
pathName=fullfile(defaultFolder,'data','libSurf');

switch modelID
    case {1,'stanford_bunny'}
        fileName=fullfile(pathName,'stanford_bunny_closed.mat');
        meshData=load(fileName);
    case {2,'utah_teapot'}
        fileName=fullfile(pathName,'utah_teapot.mat');
        meshData=load(fileName);
    case {3,'cow'}
        fileName=fullfile(pathName,'cow.mat');
        meshData=load(fileName);
    case {4,'parasaurolophus'}
        fileName=fullfile(pathName,'parasaurolophus.mat');
        meshData=load(fileName);
    case {5,'femur'}
        fileName=fullfile(pathName,'femur.mat');
        meshData=load(fileName);
    case {6,'hip_implant'}
        fileName=fullfile(pathName,'hip_implant.mat');
        meshData=load(fileName);
    case {7,'elephant'}
        fileName=fullfile(pathName,'elephant.mat');
        meshData=load(fileName);
    case {8,'dolphin'}
        fileName=fullfile(pathName,'dolphin.mat');
        meshData=load(fileName);
    case {9,'david'}
        % Reduced density version of a David model from SMK - Statens Museum for Kunst
        fileName=fullfile(pathName,'david.mat');
        meshData=load(fileName);
    case {10,'nefertiti'}
        fileName=fullfile(pathName,'nefertiti.mat');
        meshData=load(fileName);
    case {11,'vertebra'}
        fileName=fullfile(pathName,'vertebra.mat');
        meshData=load(fileName);
    case {12,'suzanne'}
        fileName=fullfile(pathName,'suzanne.mat');
        meshData=load(fileName);
    case {13,'nefertiti_high'}
        % Resampled version of nefertiti model available from:
        % https://www.cs.cmu.edu/~kmcrane/Projects/ModelRepository/
        % The original mesh was scanned by Nora Al-Badri and Jan Nikolai
        % Nelles from the Nefertiti bust, which was created in 1345 BC by
        % Thutmose. Released under creative commons license.
        fileName=fullfile(pathName,'nefertiti_high.mat');
        meshData=load(fileName);
end

F=meshData.F;
V=meshData.V;

switch nargout
    case 0
        cFigure;
        gpatch(F,V,'w','k');
        axisGeom;
        camlight headlight;
        gdrawnow;
    case 1
        varargout{1}=meshData;
    case 2
        varargout{1}=F;
        varargout{2}=V;
    case 3
        varargout{1}=F;
        varargout{2}=V;
        %Get color data
        if isfield(meshData,'C')
            C=meshData.C;
        else
            C=ones(size(F,1),1);
        end
        varargout{3}=C;
end

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