function [net, info] = cnn_train(net, imdb, getBatch, varargin)
%CNN_TRAIN  An example implementation of SGD for training CNNs
%    CNN_TRAIN() is an example learner implementing stochastic
%    gradient descent with momentum to train a CNN. It can be used
%    with different datasets and tasks by providing a suitable
%    getBatch function.
%
%    The function automatically restarts after each training epoch by
%    checkpointing.
%
%    The function supports training on CPU or on one or more GPUs
%    (specify the list of GPU IDs in the `gpus` option). Multi-GPU
%    support is relatively primitive but sufficient to obtain a
%    noticable speedup.

% Copyright (C) 2014-15 Andrea Vedaldi.
% All rights reserved.
%
% This file is part of the VLFeat library and is made available under
% the terms of the BSD license (see the COPYING file).

opts.batchSize = 256 ;
opts.numSubBatches = 1 ;
opts.train = [] ;
opts.val = [] ;
opts.numEpochs = 300 ;
opts.gpus = [] ; % which GPU devices to use (none, one, or more)
opts.learningRate = 0.001 ;
opts.continue = false ;
opts.expDir = fullfile('data','exp') ;
opts.conserveMemory = false ;
opts.backPropDepth = +inf ;
opts.sync = false ;
opts.prefetch = false ;
opts.cudnn = true ;
opts.weightDecay = 0.0005 ;
opts.momentum = 0.9 ;
opts.errorFunction = 'multiclass' ;
opts.errorLabels = {} ;
opts.plotDiagnostics = false ;
opts.memoryMapFile = fullfile(tempdir, 'matconvnet.bin') ;
opts = vl_argparse(opts, varargin) ;

if ~exist(opts.expDir, 'dir'), mkdir(opts.expDir) ; end
if isempty(opts.train), opts.train = find(imdb.images.set==1) ; end
if isempty(opts.val), opts.val = find(imdb.images.set==2) ; end
if isnan(opts.train), opts.train = [] ; end

% -------------------------------------------------------------------------
%                                                    Network initialization
% -------------------------------------------------------------------------

evaluateMode = isempty(opts.train) ;

if ~evaluateMode
  for i=1:numel(net.layers)
    if isfield(net.layers{i}, 'weights')
      J = numel(net.layers{i}.weights) ;
      for j=1:J
        net.layers{i}.momentum{j} = zeros(size(net.layers{i}.weights{j}), 'single') ;
      end
      if ~isfield(net.layers{i}, 'learningRate')
        net.layers{i}.learningRate = ones(1, J, 'single') ;
      end
      if ~isfield(net.layers{i}, 'weightDecay')
        net.layers{i}.weightDecay = ones(1, J, 'single') ;
      end
    end
    % Legacy code: will be removed
    if isfield(net.layers{i}, 'filters')
      net.layers{i}.momentum{1} = zeros(size(net.layers{i}.filters), 'single') ;
      net.layers{i}.momentum{2} = zeros(size(net.layers{i}.biases), 'single') ;
      if ~isfield(net.layers{i}, 'learningRate')
        net.layers{i}.learningRate = ones(1, 2, 'single') ;
      end
      if ~isfield(net.layers{i}, 'weightDecay')
        net.layers{i}.weightDecay = single([1 0]) ;
      end
    end
  end
end

% setup GPUs
numGpus = numel(opts.gpus) ;
if numGpus > 1
  if isempty(gcp('nocreate')),
    parpool('local',numGpus) ;
    spmd, gpuDevice(opts.gpus(labindex)), end
  end
elseif numGpus == 1
  gpuDevice(opts.gpus)
end
if exist(opts.memoryMapFile), delete(opts.memoryMapFile) ; end

% setup error calculation function
if isstr(opts.errorFunction)
  switch opts.errorFunction
    case 'none'
      opts.errorFunction = @error_none ;
    case 'multiclass'
      opts.errorFunction = @error_multiclass ;
      if isempty(opts.errorLabels), opts.errorLabels = {'top1e', 'top5e'} ; end
    case 'binary'
      opts.errorFunction = @error_binary ;
      if isempty(opts.errorLabels), opts.errorLabels = {'bine'} ; end
    otherwise
      error('Uknown error function ''%s''', opts.errorFunction) ;
  end
end

% -------------------------------------------------------------------------
%                                                        Train and validate
% -------------------------------------------------------------------------

modelPath = @(ep) fullfile(opts.expDir, sprintf('net-epoch-%d.mat', ep));
modelFigPath = fullfile(opts.expDir, 'net-train.pdf') ;

start = opts.continue * findLastCheckpoint(opts.expDir) ;
if start >= 1
  fprintf('resuming by loading epoch %d\n', start) ;
  load(modelPath(start), 'net', 'info') ;
end

for epoch=start+1:opts.numEpochs

  % train one epoch and validate
  learningRate = opts.learningRate(min(epoch, numel(opts.learningRate))) ;
  train = opts.train(randperm(numel(opts.train))) ; % shuffle
  val = opts.val ;
  if numGpus <= 1
    [net,stats.train] = process_epoch(opts, getBatch, epoch, train, learningRate, imdb, net) ;
    [~,stats.val] = process_epoch(opts, getBatch, epoch, val, 0, imdb, net) ;
  else
    spmd(numGpus)
      [net_, stats_train_] = process_epoch(opts, getBatch, epoch, train, learningRate, imdb, net) ;
      [~, stats_val_] = process_epoch(opts, getBatch, epoch, val, 0, imdb, net_) ;
    end
    net = net_{1} ;
    stats.train = sum([stats_train_{:}],2) ;
    stats.val = sum([stats_val_{:}],2) ;
  end

  % save
  if evaluateMode, sets = {'val'} ; else sets = {'train', 'val'} ; end
  for f = sets
    f = char(f) ;
    n = numel(eval(f)) ;
    info.(f).speed(epoch) = n / stats.(f)(1) * max(1, numGpus) ;
    info.(f).objective(epoch) = stats.(f)(2) / n ;
    info.(f).error(:,epoch) = stats.(f)(3:end) / n ;
  end
  if ~evaluateMode, save(modelPath(epoch), 'net', 'info') ; end

  figure(1) ; clf ;
  hasError = isa(opts.errorFunction, 'function_handle') ;
  subplot(1,1+hasError,1) ;
  if ~evaluateMode
    semilogy(1:epoch, info.train.objective, '.-', 'linewidth', 2) ;
    hold on ;
  end
  semilogy(1:epoch, info.val.objective, '.--') ;
  xlabel('training epoch') ; ylabel('energy') ;
  grid on ;
  h=legend(sets) ;
  set(h,'color','none');
  title('objective') ;
  if hasError
    subplot(1,2,2) ; leg = {} ;
    if ~evaluateMode
      plot(1:epoch, info.train.error', '.-', 'linewidth', 2) ;
      hold on ;
      leg = horzcat(leg, strcat('train ', opts.errorLabels)) ;
    end
    plot(1:epoch, info.val.error', '.--') ;
    leg = horzcat(leg, strcat('val ', opts.errorLabels)) ;
    set(legend(leg{:}),'color','none') ;
    grid on ;
    xlabel('training epoch') ; ylabel('error') ;
    title('error') ;
  end
  drawnow ;
  print(1, modelFigPath, '-dpdf') ;
end

% -------------------------------------------------------------------------
function err = error_multiclass(opts, labels, res)
% -------------------------------------------------------------------------
predictions = gather(res(end-1).x) ;
[~,predictions] = sort(predictions, 3, 'descend') ;

% be resilient to badly formatted labels
if numel(labels) == size(predictions, 4)
  labels = reshape(labels,1,1,1,[]) ;
end

% skip null labels
mass = single(labels(:,:,1,:) > 0) ;
if size(labels,3) == 2
  % if there is a second channel in labels, used it as weights
  mass = mass .* labels(:,:,2,:) ;
  labels(:,:,2,:) = [] ;
end

error = ~bsxfun(@eq, predictions, labels) ;
err(1,1) = sum(sum(sum(mass .* error(:,:,1,:)))) ;
err(2,1) = sum(sum(sum(mass .* min(error(:,:,1:5,:),[],3)))) ;

% -------------------------------------------------------------------------
function err = error_binaryclass(opts, labels, res)
% -------------------------------------------------------------------------
predictions = gather(res(end-1).x) ;
error = bsxfun(@times, predictions, labels) < 0 ;
err = sum(error(:)) ;

% -------------------------------------------------------------------------
function err = error_none(opts, labels, res)
% -------------------------------------------------------------------------
err = zeros(0,1) ;

% -------------------------------------------------------------------------
function  [net_cpu,stats,prof] = process_epoch(opts, getBatch, epoch, subset, learningRate, imdb, net_cpu)
% -------------------------------------------------------------------------

% move CNN to GPU as needed
numGpus = numel(opts.gpus) ;
if numGpus >= 1
  net = vl_simplenn_move(net_cpu, 'gpu') ;
else
  net = net_cpu ;
  net_cpu = [] ;
end

% validation mode if learning rate is zero
training = learningRate > 0 ;
if training, mode = 'training' ; else, mode = 'validation' ; end
if nargout > 2, mpiprofile on ; end

numGpus = numel(opts.gpus) ;
if numGpus >= 1
  one = gpuArray(single(1)) ;
else
  one = single(1) ;
end
res = [] ;
mmap = [] ;
stats = [] ;
start = tic ;

for t=1:opts.batchSize:numel(subset)
  fprintf('%s: epoch %02d: batch %3d/%3d: ', mode, epoch, ...
          fix(t/opts.batchSize)+1, ceil(numel(subset)/opts.batchSize)) ;
  batchSize = min(opts.batchSize, numel(subset) - t + 1) ;
  numDone = 0 ;
  error = [] ;
  for s=1:opts.numSubBatches
    % get this image batch and prefetch the next
    batchStart = t + (labindex-1) + (s-1) * numlabs ;
    batchEnd = min(t+opts.batchSize-1, numel(subset)) ;
    batch = subset(batchStart : opts.numSubBatches * numlabs : batchEnd) ;
    [im, labels] = getBatch(imdb, batch) ;

    if opts.prefetch
      if s==opts.numSubBatches
        batchStart = t + (labindex-1) + opts.batchSize ;
        batchEnd = min(t+2*opts.batchSize-1, numel(subset)) ;
      else
        batchStart = batchStart + numlabs ;
      end
      nextBatch = subset(batchStart : opts.numSubBatches * numlabs : batchEnd) ;
      getBatch(imdb, nextBatch) ;
    end

    if numGpus >= 1
      im = gpuArray(im) ;
    end

    % evaluate CNN
    net.layers{end}.class = labels ;
    if training, dzdy = one; else, dzdy = [] ; end
    res = vl_simplenn(net, im, dzdy, res, ...
                      'accumulate', s ~= 1, ...
                      'disableDropout', ~training, ...
                      'conserveMemory', opts.conserveMemory, ...
                      'backPropDepth', opts.backPropDepth, ...
                      'sync', opts.sync, ...
                      'cudnn', opts.cudnn) ;

    % accumulate training errors
    error = sum([error, [...
      sum(double(gather(res(end).x))) ;
      reshape(opts.errorFunction(opts, labels, res),[],1) ; ]],2) ;
    numDone = numDone + numel(batch) ;
  end

  % gather and accumulate gradients across labs
  if training
    if numGpus <= 1
      [net,res] = accumulate_gradients(opts, learningRate, batchSize, net, res) ;
    else
      if isempty(mmap)
        mmap = map_gradients(opts.memoryMapFile, net, res, numGpus) ;
      end
      write_gradients(mmap, net, res) ;
      labBarrier() ;
      [net,res] = accumulate_gradients(opts, learningRate, batchSize, net, res, mmap) ;
    end
  end

  % print learning statistics

  time = toc(start) ;
  stats = sum([stats,[0 ; error]],2); % works even when stats=[]
  stats(1) = time ;
  n = (t + batchSize - 1) / max(1,numlabs) ;
  speed = n/time ;
  fprintf('%.1f Hz%s\n', speed) ;

  fprintf(' obj:%.3g', stats(2)/n) ;
  for i=1:numel(opts.errorLabels)
    fprintf(' %s:%.3g', opts.errorLabels{i}, stats(i+2)/n) ;
  end
  fprintf(' [%d/%d]', numDone, batchSize);
  fprintf('\n') ;

  % debug info
  if opts.plotDiagnostics && numGpus <= 1
    figure(2) ; vl_simplenn_diagnose(net,res) ; drawnow ;
  end
end

if nargout > 2
  prof = mpiprofile('info');
  mpiprofile off ;
end

if numGpus >= 1
  net_cpu = vl_simplenn_move(net, 'cpu') ;
else
  net_cpu = net ;
end

% -------------------------------------------------------------------------
function [net,res] = accumulate_gradients(opts, lr, batchSize, net, res, mmap)
% -------------------------------------------------------------------------
for l=numel(net.layers):-1:1
  for j=1:numel(res(l).dzdw)
    thisDecay = opts.weightDecay * net.layers{l}.weightDecay(j) ;
    thisLR = lr * net.layers{l}.learningRate(j) ;

    % accumualte from multiple labs (GPUs) if needed
    if nargin >= 6
      tag = sprintf('l%d_%d',l,j) ;
      tmp = zeros(size(mmap.Data(labindex).(tag)), 'single') ;
      for g = setdiff(1:numel(mmap.Data), labindex)
        tmp = tmp + mmap.Data(g).(tag) ;
      end
      res(l).dzdw{j} = res(l).dzdw{j} + tmp ;
    end

    if isfield(net.layers{l}, 'weights')
      net.layers{l}.momentum{j} = ...
        opts.momentum * net.layers{l}.momentum{j} ...
        - thisDecay * net.layers{l}.weights{j} ...
        - (1 / batchSize) * res(l).dzdw{j} ;
      net.layers{l}.weights{j} = net.layers{l}.weights{j} + thisLR * net.layers{l}.momentum{j} ;
    else
      % Legacy code: to be removed
      if j == 1
        net.layers{l}.momentum{j} = ...
          opts.momentum * net.layers{l}.momentum{j} ...
          - thisDecay * net.layers{l}.filters ...
          - (1 / batchSize) * res(l).dzdw{j} ;
        net.layers{l}.filters = net.layers{l}.filters + thisLR * net.layers{l}.momentum{j} ;
      else
        net.layers{l}.momentum{j} = ...
          opts.momentum * net.layers{l}.momentum{j} ...
          - thisDecay * net.layers{l}.biases ...
          - (1 / batchSize) * res(l).dzdw{j} ;
        net.layers{l}.biases = net.layers{l}.biases + thisLR * net.layers{l}.momentum{j} ;
      end
    end
  end
end

% -------------------------------------------------------------------------
function mmap = map_gradients(fname, net, res, numGpus)
% -------------------------------------------------------------------------
format = {} ;
for i=1:numel(net.layers)
  for j=1:numel(res(i).dzdw)
    format(end+1,1:3) = {'single', size(res(i).dzdw{j}), sprintf('l%d_%d',i,j)} ;
  end
end
format(end+1,1:3) = {'double', [3 1], 'errors'} ;
if ~exist(fname) && (labindex == 1)
  f = fopen(fname,'wb') ;
  for g=1:numGpus
    for i=1:size(format,1)
      fwrite(f,zeros(format{i,2},format{i,1}),format{i,1}) ;
    end
  end
  fclose(f) ;
end
labBarrier() ;
mmap = memmapfile(fname, 'Format', format, 'Repeat', numGpus, 'Writable', true) ;

% -------------------------------------------------------------------------
function write_gradients(mmap, net, res)
% -------------------------------------------------------------------------
for i=1:numel(net.layers)
  for j=1:numel(res(i).dzdw)
    mmap.Data(labindex).(sprintf('l%d_%d',i,j)) = gather(res(i).dzdw{j}) ;
  end
end

% -------------------------------------------------------------------------
function epoch = findLastCheckpoint(modelDir)
% -------------------------------------------------------------------------
list = dir(fullfile(modelDir, 'net-epoch-*.mat')) ;
tokens = regexp({list.name}, 'net-epoch-([\d]+).mat', 'tokens') ;
epoch = cellfun(@(x) sscanf(x{1}{1}, '%d'), tokens) ;
epoch = max([epoch 0]) ;


function test_suite = testCos
initTestSuite;

function testTooManyInputs
assertExceptionThrown(@() cos(1, 2), 'MATLAB:maxrhs');


%% SeDuMi Interface Guide

% See the following Wiki page:
% http://www.i2c2.aut.ac.nz/Wiki/OPTI/index.php/Solvers/SEDUMI


classdef opti < handle
%OPTI  Create an OPTI object for Linear, Quadratic & Nonlinear Optimization
%
%   optObj = opti('param1',value1,'param2',value2,...) creates an OPTI
%   object with the parameters 'param' set to their corresponding
%   values in 'value'. Parameters not specified will be set to the OPTI
%   default.
%
%   optObj = opti(...,'options',opts) creates the object with optiset
%   options supplied to the OPTI constructor.
%
%   optObj = opti(prob) creates an opti object based on the optimization
%   problem specified in prob. Use 'optiprob' to generate the structure
%   'prob'.
%
%   optObj = opti(prob,opts) creates the opti object with specified
%   options. Use 'optiset' to generate the options structure 'opts'.
%
%   opti() prints a list of all possible fields and their function.
%
%
%   Typical usage examples:
%
%   1) LP
%   optObj = opti('f',f,'ineq',A,b,'bounds',lb,ub)
%
%   2) Bounded NLP
%   optObj = opti('obj',obj,'bounds',lb,ub,'x0',x0)
%
%   3) Option Setting
%   optObj = opti('obj',obj,'options',optiset('solver','nomad'))
%
%
%   For detailed documentation see the Wiki:
%      https://inverseproblem.co.nz/OPTI
%
%
%   See also opti.solve optiset opti.plot
%
%   Copyright (C) 2011-2018 Jonathan Currie (www.inverseproblem.co.nz)

    properties (SetAccess = private)
        prob        % Problem Structure (optiprob)
        nlprob      % Nonlinear Problem Structure
        opts        % Setup & Runtime options (optiset)
        sol         % Previous solution (Vector)
        obj         % Previous objective value
        ef          % Previous Exit Flag
        info        % Previous Info Structure
        fitstat     % Previous Fit Statistics Structure
    end

    methods

        %-- Constructor --%
        function op = opti(varargin)
            %OPTI Constructor
            if(~nargin)
                optiprob;
                fprintf('\n OPTION SETTING:\n');
                fprintf('         options: [ Problem Setup & Solving Options specified via optiset() ]\n');
                fprintf('          solver: [ Optimization Solver ]\n');
                return;
            end
            [p,o] = opti.buildOpti(varargin{:});
            [op.prob,op.opts,op.nlprob] = opti.buildConfig(p,o);
            op.sol = []; op.ef = [];
        end

        %-- Solve --%
        function [x,fval,exitflag,info] = solve(optObj,x0)
            %SOLVE  Solve OPTI Optimization Problem
            %
            %   [x,fval,exitflag,info] = solve(optObj) solves the optimization
            %   problem specified by optObj and returns the solution vector x,
            %   the function value at x in fval, an exitflag and information in
            %   info regarding the solution.
            %
            %   [x,fval,exitflag,info] = solve(optObj,x0) solves the optimization
            %   problem using x0 as the initial guess. If x0 is specified within
            %   opti() / optiprob(), this x0 will take precedence.
            %
            %   ExitFlags:
            %        1 - Converged / Terminated Successfully
            %        0 - Maximum Iterations / Function Evaluations / Time Exceeded
            %       -1 - Infeasible / Could Not Converge
            %       -2 - Unbounded / Solver Error
            %       -3 - Solver Specific Errors (Set option 'display' as 'iter')
            %       -5 - User Exited via Ctrl-C
            %
            %   Information Structure:
            %         Iterations - The number of iterations taken by the solver
            %               Time - The execution time of the solver as measured by MATLAB (tic + toc)
            %          Algorithm - The solver and algorithm being used
            %             Status - A status string indicating the solver specific exit message
            %             Lambda - A structure of the Lagrange multipliers at the solution (if available)

            %Check class isn't empty
            if(isempty(optObj.prob))
                error('You cannot solve an empty OPTI object!');
            end

            %Get Optional x0
            if(nargin < 2 || isempty(x0))
                if(~isempty(optObj.prob.x0) && ~any(isnan(optObj.prob.x0)))
                    x0 = optObj.prob.x0;
                else
                    x0 = [];
                end
            else
                optObj.prob.x0 = x0; %save into class
            end
            %Solve Problem
            [x,fval,exitflag,info] = solveOpti(optObj,x0);
            %Save solve info
            optObj.sol = x;
            optObj.obj = fval;
            optObj.ef = exitflag;
            optObj.info = info;

            %Update ndec if empty
            if(isempty(optObj.prob.sizes.ndec)), optObj.prob.sizes.ndec = length(x0); end
            %Check for maximization
            if(optObj.prob.sense==-1), fval = -fval; end

            %Check if we should write an ampl solution file
            if(optObj.prob.ampl.writesol && asl('isopen') == 1)
                asl('writesol',info.Status(1:min(end,100)),x);
                %If the problem is not nonlinear, close the interface
                if(isempty(optObj.prob.fun) && isempty(optObj.prob.nlcon))
                    asl('close');
                end
            end

            %If less that 3 output arguments, user may not know if not
            %solved correctly. Depending on warning level, show a warning
            %to the user
            if(nargout < 3 && ~isempty(exitflag) && exitflag ~= 1 && optiWarnLevel(optObj.opts.warnings) >= 1 && isfield(info,'Status') && strcmpi(optObj.opts.display,'off'))
                optiwarn(['Your problem may not have solved correctly. Solver status:\n>> %s\n\nPlease enable solver output by changing the optiset setting ''display'' to ''iter'' to see more information.\n',...
                          'Alternatively you can avoid this warning by returning all four return arguments from solve(opti_obj).\n'],info.Status);
            end
        end

        %-- Multi-Solve --%
        function [x,fval,exitflag,info] = multisolve(optObj,x0,divisions,penalty,solveAll)
            %MULTISOLVE  Solve OPTI Global Optimization Problem with Multi-Start (beta)
            %
            %   x = multisolve(optObj) performs a search of the
            %   optimization problem before attempting to solve the problem
            %   at the best solution guesses found. The best optimized
            %   solution will be returned in x.
            %
            %   x = multisolve(optObj,x0) also includes a more detailed
            %   search around the initial guess x0.
            %
            %   x = multisolve(optObj,x0,divisions) determines the number
            %   of divisions each variable is searched within. The default
            %   will attempt to search around 100 points. For ND problems,
            %   a division of 10 will result in 10^N search points.
            %
            %   x = multisolve(optObj,x0,divisions,penalty) specifies the
            %   penalty factor applied to constraint violations. The
            %   default is 1e4. Larger values will be required for
            %   objective functions that exceed this value.
            %
            %   x = multisolve(optObj,x0,divisions,penalty,solveAll)
            %   specifies at each search point an optimization is also
            %   performed. Significantly longer to execute, but ensures the
            %   search space is well covered!

            %Check class isn't empty
            if(isempty(optObj.prob))
                error('You cannot solve an empty OPTI object!');
            end

            %Optional args
            if(nargin < 5), solveAll = false; end
            if(nargin < 4), penalty = []; end
            if(nargin < 3), divisions = []; end

            %Get Optional x0
            if(nargin < 2 || isempty(x0))
                if(~isempty(optObj.prob.x0) && ~any(isnan(optObj.prob.x0)))
                    x0 = optObj.prob.x0;
                else
                    x0 = [];
                end
            else
                optObj.prob.x0 = x0; %save into class
            end

            %Solve Problem
            [x,fval,exitflag,info] = multiSolveOpti(optObj,x0,divisions,penalty,solveAll);
            %Save solve info into Object
            optObj.sol = x;
            optObj.obj = fval;
            optObj.ef = exitflag;
            optObj.info = info;

            %Update ndec if empty
            if(isempty(optObj.prob.sizes.ndec)), optObj.prob.sizes.ndec = length(x0); end
            %Check for maximization
            if(optObj.prob.sense==-1), fval = -fval; end

            %Check if we should write an ampl solution file
            if(optObj.prob.ampl.writesol && asl('isopen') == 1)
                asl('writesol',info.Status(1:min(end,100)),x);
                %If the problem is not nonlinear, close the interface
                if(isempty(optObj.prob.fun) && isempty(optObj.prob.nlcon))
                    asl('close');
                end
            end
        end

        %-- Check Solution --%
        function [ok,msg] = checkSol(optObj,tol)
            %CHECKSOL  Check Optimization Solution for Errors
            %
            %   [ok,msg] = checkSol(optObj) checks the solution stored in
            %   optObj for error messages or broken constraints. It returns
            %   true in 'ok' if no problems detected, otherwise it returns
            %   false with the problem in 'msg'.
            %
            %   [ok,msg] = checkSol(optObj,tol) checks with respect to a
            %   given tolerance. The default is 1e-6.

            if(isempty(optObj.sol))
                error('This OPTI object has not been solved yet!');
            end
            if(nargin < 2), tol = 1e-6; end

            [ok,msg] = checkOptiSol(optObj,tol);
        end

        %-- Display --%
        function display(optObj)
            %Check if empty
            if(isempty(optObj.prob))
                disp(' ');
                disp('------------------------------------------------------');
                disp('Empty OPTI Object');
                disp('------------------------------------------------------');
            else
                %Otherwise normal method
                displayOPTI(optObj);
            end
        end

        %-- Plot --%
        function plot(optObj,scale,doLog,npts)
            %PLOT  Plot optimization problem (1D-5D only)
            %
            %   plot(optObj) plots the optimization field for the current
            %   OPTI object.
            %
            %   plot(optObj,scale) plots with a user defined zoom level.
            %   scale is defined as the range +- of the solution to be
            %   drawn OR if supplied as a vector controls the bounds on the
            %   plot [x1min x1max x2min x2max ... xNmin xNmax]
            %
            %   plot(obtObj,scale,dolog) plots the log of the objective
            %   function (NL only)
            %
            %   plot(obtObj,scale,dolog,npts) controls the number of points
            %   used for generating the objective and constraint contours

            %Check class isn't empty
            if(isempty(optObj.prob))
                error('You cannot plot an empty OPTI object!');
            end

            %Get Optional Arguments
            if(nargin < 4), npts = []; end
            if(nargin < 3), doLog = false; end
            if(nargin < 2), scale = []; end

            %Check 2D for problems other than least squares fits
            if(~any(strcmpi(optObj.prob.type,{'NLS','DNLS'})) && optObj.prob.sizes.ndec > 5)
                error('You can only plot problems with one - five decision variables!');
            end

            %Plot
            plotOptiProb(optObj.prob,optObj.opts,optObj.sol,optObj.fitstat,scale,doLog,npts,'normal');
        end

        %-- Multi-Plot--%
        function multiplot(optObj,doLog,npts)
            %MULTIPLOT  Plot optimization problem and overlay multi-start search area
            %
            %   multiplot(optObj) plots the optimization field for the current
            %   OPTI object and overlays the multi-start search area
            %
            %   multiplot(obtObj,dolog) plots the log of the objective
            %   function (NL only)
            %
            %   multiplot(obtObj,dolog,npts) controls the number of points
            %   used for generating the objective and constraint contours

            if(nargin < 3), npts = []; end
            if(nargin < 2), doLog = false; end

            %Check class isn't empty
            if(isempty(optObj.prob))
                error('You cannot plot an empty OPTI object!');
            end
            %Check 2D
            if(optObj.prob.sizes.ndec > 5)
                error('You can only multiplot problems with one - five decision variables!');
            end

            %Plot
            plotOptiProb(optObj.prob,optObj.opts,optObj.sol,optObj.fitstat,[],doLog,npts,'multi');
        end

        %-- Stats --%
        function [StatsStruct] = fitStats(optObj,limit,printStats)
            %CONFIDENCE  Calculate approximate ANOVA and Confidence Statistics for NLS/DNLS Fit
            %
            %   StatsStruct = fitStats(optObj) returns a structure of
            %   statistics about the fit, as detailed below:
            %       SSE - Sum Of Squared Errors
            %       Rsquare - R^2 Value
            %       AdjRSquare - Adjusted R^2 Value
            %       RMSE - Root Mean Squared Error
            %       DFE - Degrees of Freedom
            %       ContInt - Confidence Interval (theta +- ConfInt)
            %       ConfBnds - Confidence Bounds (ypred +- ConfBnds)
            %       Cov - System Covariance Matrix
            %       BndIdx - Logical Array where 1s indicate active bounds
            %       at the solution (and therefore dropped from Cov)
            %       Param - Parameter Statistics Structure with fields:
            %           StdError - Standard Error
            %           tStat - t Statistic
            %           pValues - p Values
            %       Model - Model Statistics Stucture with fields:
            %           Fstat - F Statistic
            %           pValue - p value
            %           Int - Whether Model has an Intercept or Not
            %       Conf - User specified Confidence limit
            %
            %   For a description of each statistic, consult the following
            %   site:
            %       http://www.ats.ucla.edu/stat/sas/output/reg.htm
            %
            %   StatsStruct = fitStats(optObj,limit) allows the user to
            %   specify the confidence limit (0.0 < limit < 1.0),
            %   e.g. 0.95 = 95%
            %
            %   fitStats(optObj,limit,printStats) optionally prints the
            %   statistics to the command window. Defaults if no output is
            %   selected.

            %Check class isn't empty or unsolved
            if(isempty(optObj.prob) && isempty(optObj.sol))
                error('You cannot calculate statistics of an empty OPTI object!');
            end
            if(isempty(optObj.sol))
                error('You cannot calculate statistics of an unsolved OPTI object!');
            end

            %Get Optional Arguments
            if(nargin < 3), printStats = false; end
            if(nargin < 2), limit = []; end

            %Calculate
            StatsStruct = calcStatistics(optObj,limit);
            %Save to Object
            optObj.fitstat = StatsStruct;
%             %Optionally Plot Ellipse
%             if(plotEllipse)
%                 if(length(optObj.sol) < 2 || length(optObj.sol) > 3)
%                     optiwarn('opti:confEllipse','Cannot plot confidence ellipse/ellipsoid - only 2/3 parameter problems are supported');
%                 else
%                     plotConfEllipse(optObj.sol,StatsStruct);
%                 end
%             end
            %If no output arguments, display fit statistics
            if(nargout < 1 || printStats)
               	optiPrintFitStats(optObj.fitstat,optObj.sol);
            end
        end

        %-- write --%
        function write(optObj,filename,type)
            %WRITE  Write an OPTI Problem to a MPS/QPS/LP/SDPA/SEDUMI/GAMS file
            %
            %   write(optObj,filename) writes the OPTI problem to a
            %   filetype specified by the extension of filename.
            %
            %   write(optObj,filename,type) writes the OPTI problem to a
            %   filetype specified by the type argument.
            %
            %   Valid File Types & Extensions:
            %       Linear & Quadratic Problems
            %           - MPS [.mps] (LP/MILP)
            %           - QPS [.qps] (QP/MIQP)
            %           - LP  [.lp]  (LP)
            %       Semidefinite Problems
            %           - SDPA, DAT [.dat] (Dense)
            %           - SDPA-S, DAT-S [.dat-s] (Sparse)
            %           - MAT [.mat] (SeDuMi Sparse)
            %       General
            %           - GMS [.gms] (GAMS)

            %Check class isn't empty
            if(isempty(optObj.prob)), error('You cannot write an empty OPTI object!'); end
            if(nargin < 3), type = []; end
            %Check if we have a type
            if(~isempty(type))
                switch(lower(type))
                    case {'sdpa','sdpa-s','sedumi','sdpas','sdp','dat','dat-s','mat'}
                        sdpWrite(optObj.prob,filename,type);
                        return;
                    case {'mps','qps','lp'}
                        coinWrite(optObj.prob,filename,type);
                        return;
                    case 'gms'
                        gamsWrite(optObj.prob,filename);
                    otherwise
                        error('Unknown file type to write to!');
                end
            end
            %If not, go on extension
            if(isempty(strfind(filename,'.')))
                error('You must supply a file extension if you do not specify a file type!');
            end
            ext = regexp(filename,'\.','split');
            switch(lower(ext{end}))
                case {'dat','mat','dat-s'}
                    sdpWrite(optObj.prob,filename,type);
                case {'mps','qps','lp'}
                    coinWrite(optObj.prob,filename,type);
                case 'gms'
                    gamsWrite(optObj.prob,filename);
                otherwise
                    error('Unknown file extension to write to! For non-standard file extensions please specify the file type.');
            end
        end

        %-- getProb --%
        function retprob = getProb(optObj)
            %GETPROB  Return optiprob compatible problem structure
            %
            %   prob = getProb(optObj)

            %Check class isn't empty
            if(isempty(optObj.prob))
                error('You cannot get the problem from an empty OPTI object!');
            end

            retprob = optObj.prob;
            retprob = rmfield(retprob,{'iscon','sizes','numdif','type','ampl','save'});
            retprob.int = retprob.int.str;
        end

        %-- Set Solution (unit testing only use) --%
        function setSolution(optObj, fval, sol)
            %Check class isn't empty
            if(isempty(optObj.prob))
                error('You cannot get the problem from an empty OPTI object!');
            end
            optObj.obj = fval;
            if (numel(sol) ~= optObj.prob.sizes.ndec)
                error('Solution vector is the wrong size');
            else
                optObj.sol = sol(:);
            end
            optObj.info = struct('Status','HARDCODED SOLUTION','Algorithm','USER SPECIFIED');
        end
    end

    methods (Static)
        [prob,opts] = buildOpti(varargin);
        [prob,opts,nlprob] = buildConfig(prob,opts);
    end

end


function f = draw_3Dskeleton_MSRA(d2img,coord_world)

    cubicSz = 150;
    coord_world = squeeze(coord_world);

    x = coord_world(1,:);
    y = coord_world(2,:);
    z = coord_world(3,:);
    coord_world(1,:) = -z;
    coord_world(2,:) = x;
    coord_world(3,:) = y;
    line_width = 8;

    f = figure('Position',[100 100 600 450]);
    set(f, 'visible', 'off');
    hold on;
    grid on;

    plot3([coord_world(1,1),coord_world(1,18)],[coord_world(2,1),coord_world(2,18)],[coord_world(3,1),coord_world(3,18)],'Color',[255/255,153/255,153/255],'LineWidth',line_width) %wrist to thumb mcp
    plot3([coord_world(1,18),coord_world(1,19)],[coord_world(2,18),coord_world(2,19)],[coord_world(3,18),coord_world(3,19)],'Color',[255/255,102/255,102/255],'LineWidth',line_width) %thumb mcp to thumb pip
    plot3([coord_world(1,19),coord_world(1,20)],[coord_world(2,19),coord_world(2,20)],[coord_world(3,19),coord_world(3,20)],'Color',[255/255,51/255,51/255],'LineWidth',line_width) %thumb pip to thumb dip
    plot3([coord_world(1,20),coord_world(1,21)],[coord_world(2,20),coord_world(2,21)],[coord_world(3,20),coord_world(3,21)],'Color',[255/255,0/255,0/255],'LineWidth',line_width) %thumb dip to thumb tip

    plot3([coord_world(1,1),coord_world(1,2)],[coord_world(2,1),coord_world(2,2)],[coord_world(3,1),coord_world(3,2)],'Color',[153/255,255/255,153/255],'LineWidth',line_width) %wrist to index mcp
    plot3([coord_world(1,2),coord_world(1,3)],[coord_world(2,2),coord_world(2,3)],[coord_world(3,2),coord_world(3,3)],'Color',[102/255,255/255,102/255],'LineWidth',line_width) %index mcp to index pip
    plot3([coord_world(1,3),coord_world(1,4)],[coord_world(2,3),coord_world(2,4)],[coord_world(3,3),coord_world(3,4)],'Color',[51/255,255/255,51/255],'LineWidth',line_width) %index pip to index dip
    plot3([coord_world(1,4),coord_world(1,5)],[coord_world(2,4),coord_world(2,5)],[coord_world(3,4),coord_world(3,5)],'Color',[0/255,255/255,0/255],'LineWidth',line_width) %index dip to index tip

    plot3([coord_world(1,1),coord_world(1,6)],[coord_world(2,1),coord_world(2,6)],[coord_world(3,1),coord_world(3,6)],'Color',[255/255,204/255,153/255],'LineWidth',line_width) %wrist to middle mcp
    plot3([coord_world(1,6),coord_world(1,7)],[coord_world(2,6),coord_world(2,7)],[coord_world(3,6),coord_world(3,7)],'Color',[255/255,178/255,102/255],'LineWidth',line_width) %middle mcp to middle pip
    plot3([coord_world(1,7),coord_world(1,8)],[coord_world(2,7),coord_world(2,8)],[coord_world(3,7),coord_world(3,8)],'Color',[255/255,153/255,51/255],'LineWidth',line_width) %middle pip to middle dip
    plot3([coord_world(1,8),coord_world(1,9)],[coord_world(2,8),coord_world(2,9)],[coord_world(3,8),coord_world(3,9)],'Color',[255/255,128/255,0/255],'LineWidth',line_width) %middle dip to middle tip

    plot3([coord_world(1,1),coord_world(1,10)],[coord_world(2,1),coord_world(2,10)],[coord_world(3,1),coord_world(3,10)],'Color',[153/255,204/255,255/255],'LineWidth',line_width) %wrist to ring mcp
    plot3([coord_world(1,10),coord_world(1,11)],[coord_world(2,10),coord_world(2,11)],[coord_world(3,10),coord_world(3,11)],'Color',[102/255,178/255,255/255],'LineWidth',line_width) %ring mcp to ring pip
    plot3([coord_world(1,11),coord_world(1,12)],[coord_world(2,11),coord_world(2,12)],[coord_world(3,11),coord_world(3,12)],'Color',[51/255,153/255,255/255],'LineWidth',line_width) %ring pip to ring dip
    plot3([coord_world(1,12),coord_world(1,13)],[coord_world(2,12),coord_world(2,13)],[coord_world(3,12),coord_world(3,13)],'Color',[0/255,128/255,255/255],'LineWidth',line_width) %ring dip to ring tip

    plot3([coord_world(1,1),coord_world(1,14)],[coord_world(2,1),coord_world(2,14)],[coord_world(3,1),coord_world(3,14)],'Color',[255/255,153/255,255/255],'LineWidth',line_width) %wrist to little mcp
    plot3([coord_world(1,14),coord_world(1,15)],[coord_world(2,14),coord_world(2,15)],[coord_world(3,14),coord_world(3,15)],'Color',[255/255,102/255,255/255],'LineWidth',line_width) %little mcp to little pip
    plot3([coord_world(1,15),coord_world(1,16)],[coord_world(2,15),coord_world(2,16)],[coord_world(3,15),coord_world(3,16)],'Color',[255/255,51/255,255/255],'LineWidth',line_width) %little pip to little dip
    plot3([coord_world(1,16),coord_world(1,17)],[coord_world(2,16),coord_world(2,17)],[coord_world(3,16),coord_world(3,17)],'Color',[255/255,0/255,255/255],'LineWidth',line_width) %little dip to little tip

    colorList = [
    230/255 230/255 0/255;

    153/255 255/255 153/255;
    102/255 255/255 102/255;
    51/255 255/255 51/255;
    0/255 255/255 0/255;

    255/255 204/255 153/255;
    255/255 178/255 102/255;
    255/255 153/255 51/255;
    255/255 128/255 0/255;

    153/255 204/255 255/255;
    102/255 178/255 255/255;
    51/255 153/255 255/255;
    0/255 128/255 255/255;

    255/255 153/255 255/255;
    255/255 102/255 255/255;
    255/255 51/255 255/255;
    255/255 0/255 255/255;

    255/255 153/255 153/255;
    255/255 102/255 102/255;
    255/255 51/255 51/255;
    255/255 0/255 0/255;
    ];
    scatter3(coord_world(1,:),coord_world(2,:),coord_world(3,:),150,colorList,'filled');

    set(gca, 'color', [255/255 255/255 255/255])

    set(gca,'XTickLabel',[]);
    set(gca,'YTickLabel',[]);
    set(gca,'ZTickLabel',[]);

    xcenter = (min(coord_world(1,:)) + max(coord_world(1,:)))/2;
    ycenter = (min(coord_world(2,:)) + max(coord_world(2,:)))/2;
    zcenter = (min(coord_world(3,:)) + max(coord_world(3,:)))/2;

    xmin = xcenter - 3*cubicSz;
    xmax = max(coord_world(1,:));
    ymin = min(ycenter-cubicSz/2, min(coord_world(2,:)));
    ymax = max(coord_world(2,:))+30;
    zmin = min(zcenter-cubicSz/2, min(coord_world(3,:)));
    zmax = max(zcenter+cubicSz/2, max(coord_world(3,:)));

    xlim([xmin xmax]);
    ylim([ymin ymax]);
    zlim([zmin zmax]);

    h_img = surf([xmin;xmin],[ymin ymax;ymin ymax],[zmax zmax;zmin zmin],'CData',d2img,'FaceColor','texturemap');
    set(h_img,'edgecolor',[96/255 96/255 96/255]);

    view(62,7);

end


function [g,dgdx,dgdP] = g_GLM(x,P,u,in)

if isempty(u) || isequal(u,0)
    g = in.X*P;
    dgdx = [];
    dgdP = in.X';
else
    g = in.X(u,:)*P;
    dgdx = [];
    dgdP = in.X(u,:)';
end


%                                            Lden = symbcholden(L,dense,DAt)
% SYMBCHOLDEN  Creates Lden.{LAD, perm,dz, sign, first}
%
% ******************** INTERNAL FUNCTION OF SEDUMI ********************
%
% See also sedumi, dpr1fact

function Lden = symbcholden(L,dense,DAt)
%
% This file is part of SeDuMi 1.1 by Imre Polik and Oleksandr Romanko
% Copyright (C) 2005 McMaster University, Hamilton, CANADA  (since 1.1)
%
% Copyright (C) 2001 Jos F. Sturm (up to 1.05R5)
%   Dept. Econometrics & O.R., Tilburg University, the Netherlands.
%   Supported by the Netherlands Organization for Scientific Research (NWO).
%
% Affiliation SeDuMi 1.03 and 1.04Beta (2000):
%   Dept. Quantitative Economics, Maastricht University, the Netherlands.
%
% Affiliations up to SeDuMi 1.02 (AUG1998):
%   CRL, McMaster University, Canada.
%   Supported by the Netherlands Organization for Scientific Research (NWO).
%
% 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.,  51 Franklin Street, Fifth Floor, Boston, MA
% 02110-1301, USA
%

% ------------------------------------------------------------
% Symbolic forward Cholesky of dense columns, in order
% [LP, Q-blk, Q-norm, Q-tr]
% ------------------------------------------------------------
i1 = dense.l + 1;
i2 = i1 + length(dense.q);
LAD = [symbfwblk(L,dense.A(:,1:i1-1)), symbfwblk(L,DAt.denq),...
    symbfwblk(L,dense.A(:,i2:end)),symbfwblk(L,dense.A(:,i1:i2-1))];
% ------------------------------------------------------------
% Incremental ordering heuristic, excluding the Lorentz-trace cols
% ------------------------------------------------------------
[perm, dz] = incorder(LAD(:,1:length(dense.cols)));
% ------------------------------------------------------------
% Insert the trace cols with a "-1"-factor just after corresponding
% Lorentz-block columns
% ------------------------------------------------------------
Lden = finsymbden(LAD,perm,dz,i1);


function [vertex,face,d,c] = read_ply(filename)

% read_ply - read data from PLY file.
%
%   [vertex,face] = read_ply(filename);
%
%   'vertex' is a 'nb.vert x 3' array specifying the position of the vertices.
%   'face' is a 'nb.face x 3' array specifying the connectivity of the mesh.
%
%
%   Copyright (c) 2003 Gabriel Peyr?

[d,c] = plyread(filename);
vi = d.face.vertex_indices;
nf = length(vi);
% http://stackoverflow.com/a/6210539/148668
% This is slow...
%lengths = cellfun(@(x)numel(x),vi);
lengths = cellfun('length',vi);
maxLength=max(lengths);
if all(maxLength == lengths)
  face = cell2mat(vi)+1;
else
  face = cell2mat( ...
    cellfun( ...
      @(x)cat(2,x,-ones(1,maxLength-length(x))), ...
      vi, ...
      'UniformOutput',false))+1;
end
vertex = [d.vertex.x, d.vertex.y, d.vertex.z];

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [Elements,varargout] = plyread(Path,Str)
%PLYREAD   Read a PLY 3D data file.
%   [DATA,COMMENTS] = PLYREAD(FILENAME) reads a version 1.0 PLY file
%   FILENAME and returns a structure DATA.  The fields in this structure
%   are defined by the PLY header; each element type is a field and each
%   element property is a subfield.  If the file contains any comments,
%   they are returned in a cell string array COMMENTS.
%
%   [TRI,PTS] = PLYREAD(FILENAME,'tri') or
%   [TRI,PTS,DATA,COMMENTS] = PLYREAD(FILENAME,'tri') converts vertex
%   and face data into triangular connectivity and vertex arrays.  The
%   mesh can then be displayed using the TRISURF command.
%
%   Note: This function is slow for large mesh files (+50K faces),
%   especially when reading data with list type properties.
%
%   Example:
%   [Tri,Pts] = PLYREAD('cow.ply','tri');
%   trisurf(Tri,Pts(:,1),Pts(:,2),Pts(:,3));
%   colormap(gray); axis equal;
%
%   See also: PLYWRITE

% Pascal Getreuer 2004

[fid,Msg] = fopen(Path,'rt');	% open file in read text mode

if fid == -1, error(Msg); end

Buf = fscanf(fid,'%s',1);
if ~strcmp(Buf,'ply')
   fclose(fid);
   error('Not a PLY file.');
end

%%% read header %%%

Position = ftell(fid);
Format = '';
NumComments = 0;
Comments = {};				% for storing any file comments
NumElements = 0;
NumProperties = 0;
Elements = [];				% structure for holding the element data
ElementCount = [];		% number of each type of element in file
PropertyTypes = [];		% corresponding structure recording property types
ElementNames = {};		% list of element names in the order they are stored in the file
PropertyNames = [];		% structure of lists of property names

while 1
   Buf = fgetl(fid);   								% read one line from file
   BufRem = Buf;
   Token = {};
   Count = 0;

   while ~isempty(BufRem)								% split line into tokens
      [tmp,BufRem] = strtok(BufRem);

      if ~isempty(tmp)
         Count = Count + 1;							% count tokens
         Token{Count} = tmp;
      end
   end

   if Count 		% parse line
      switch lower(Token{1})
      case 'format'		% read data format
         if Count >= 2
            Format = lower(Token{2});

            if Count == 3 & ~strcmp(Token{3},'1.0')
               fclose(fid);
               error('Only PLY format version 1.0 supported.');
            end
         end
      case 'comment'		% read file comment
         NumComments = NumComments + 1;
         Comments{NumComments} = '';
         for i = 2:Count
            Comments{NumComments} = [Comments{NumComments},Token{i},' '];
         end
      case 'element'		% element name
         if Count >= 3
            if isfield(Elements,Token{2})
               fclose(fid);
               error(['Duplicate element name, ''',Token{2},'''.']);
            end

            NumElements = NumElements + 1;
            NumProperties = 0;
   	      Elements = setfield(Elements,Token{2},[]);
            PropertyTypes = setfield(PropertyTypes,Token{2},[]);
            ElementNames{NumElements} = Token{2};
            PropertyNames = setfield(PropertyNames,Token{2},{});
            CurElement = Token{2};
            ElementCount(NumElements) = str2double(Token{3});

            if isnan(ElementCount(NumElements))
               fclose(fid);
               error(['Bad element definition: ',Buf]);
            end
         else
            error(['Bad element definition: ',Buf]);
         end
      case 'property'	% element property
         if ~isempty(CurElement) & Count >= 3
            NumProperties = NumProperties + 1;
            eval(['tmp=isfield(Elements.',CurElement,',Token{Count});'],...
               'fclose(fid);error([''Error reading property: '',Buf])');

            if tmp
               error(['Duplicate property name, ''',CurElement,'.',Token{2},'''.']);
            end

            % add property subfield to Elements
            eval(['Elements.',CurElement,'.',Token{Count},'=[];'], ...
               'fclose(fid);error([''Error reading property: '',Buf])');
            % add property subfield to PropertyTypes and save type
            eval(['PropertyTypes.',CurElement,'.',Token{Count},'={Token{2:Count-1}};'], ...
               'fclose(fid);error([''Error reading property: '',Buf])');
            % record property name order
            eval(['PropertyNames.',CurElement,'{NumProperties}=Token{Count};'], ...
               'fclose(fid);error([''Error reading property: '',Buf])');
         else
            fclose(fid);

            if isempty(CurElement)
               error(['Property definition without element definition: ',Buf]);
            else
               error(['Bad property definition: ',Buf]);
            end
         end
      case 'end_header'	% end of header, break from while loop
         break;
      end
   end
end

%%% set reading for specified data format %%%

if isempty(Format)
	warning('Data format unspecified, assuming ASCII.');
   Format = 'ascii';
end

switch Format
case 'ascii'
   Format = 0;
case 'binary_little_endian'
   Format = 1;
case 'binary_big_endian'
   Format = 2;
otherwise
   fclose(fid);
   error(['Data format ''',Format,''' not supported.']);
end

if ~Format
   Buf = fscanf(fid,'%f');		% read the rest of the file as ASCII data
   BufOff = 1;
else
   % reopen the file in read binary mode
   fclose(fid);

   if Format == 1
      fid = fopen(Path,'r','ieee-le.l64');		% little endian
   else
      fid = fopen(Path,'r','ieee-be.l64');		% big endian
   end

   % find the end of the header again (using ftell on the old handle doesn't give the correct position)
   BufSize = 8192;
   Buf = [blanks(10),char(fread(fid,BufSize,'uchar')')];
   i = [];
   tmp = -11;

   while isempty(i)
   	i = findstr(Buf,['end_header',13,10]);			% look for end_header + CR/LF
   	i = [i,findstr(Buf,['end_header',10])];		% look for end_header + LF

      if isempty(i)
         tmp = tmp + BufSize;
         Buf = [Buf(BufSize+1:BufSize+10),char(fread(fid,BufSize,'uchar')')];
      end
   end

   % seek to just after the line feed
   fseek(fid,i + tmp + 11 + (Buf(i + 10) == 13),-1);
end

%%% read element data %%%

% PLY and MATLAB data types (for fread)
PlyTypeNames = {'char','uchar','short','ushort','int','uint','float','double', ...
   'char8','uchar8','short16','ushort16','int32','uint32','float32','double64'};
MatlabTypeNames = {'schar','uchar','int16','uint16','int32','uint32','single','double'};
SizeOf = [1,1,2,2,4,4,4,8];	% size in bytes of each type

for i = 1:NumElements
   % get current element property information
   eval(['CurPropertyNames=PropertyNames.',ElementNames{i},';']);
   eval(['CurPropertyTypes=PropertyTypes.',ElementNames{i},';']);
   NumProperties = size(CurPropertyNames,2);

%   fprintf('Reading %s...\n',ElementNames{i});

   if ~Format	%%% read ASCII data %%%
      for j = 1:NumProperties
         Token = getfield(CurPropertyTypes,CurPropertyNames{j});

         if strcmpi(Token{1},'list')
            Type(j) = 1;
         else
            Type(j) = 0;
			end
      end

      % parse buffer
      if ~any(Type)
         % no list types
         Data = reshape(Buf(BufOff:BufOff+ElementCount(i)*NumProperties-1),NumProperties,ElementCount(i))';
         BufOff = BufOff + ElementCount(i)*NumProperties;
      else
         ListData = cell(NumProperties,1);

         for k = 1:NumProperties
            ListData{k} = cell(ElementCount(i),1);
         end

         % list type
		   for j = 1:ElementCount(i)
   	      for k = 1:NumProperties
      	      if ~Type(k)
         	      Data(j,k) = Buf(BufOff);
            	   BufOff = BufOff + 1;
	            else
   	            tmp = Buf(BufOff);
      	         ListData{k}{j} = Buf(BufOff+(1:tmp))';
         	      BufOff = BufOff + tmp + 1;
            	end
            end
         end
      end
   else		%%% read binary data %%%
      % translate PLY data type names to MATLAB data type names
      ListFlag = 0;		% = 1 if there is a list type
      SameFlag = 1;     % = 1 if all types are the same

      for j = 1:NumProperties
         Token = getfield(CurPropertyTypes,CurPropertyNames{j});

         if ~strcmp(Token{1},'list')			% non-list type
	         tmp = rem(strmatch(Token{1},PlyTypeNames,'exact')-1,8)+1;

            if ~isempty(tmp)
               TypeSize(j) = SizeOf(tmp);
               Type{j} = MatlabTypeNames{tmp};
               TypeSize2(j) = 0;
               Type2{j} = '';

               SameFlag = SameFlag & strcmp(Type{1},Type{j});
	         else
   	         fclose(fid);
               error(['Unknown property data type, ''',Token{1},''', in ', ...
                     ElementNames{i},'.',CurPropertyNames{j},'.']);
         	end
         else											% list type
            if length(Token) == 3
               ListFlag = 1;
               SameFlag = 0;
               tmp = rem(strmatch(Token{2},PlyTypeNames,'exact')-1,8)+1;
               tmp2 = rem(strmatch(Token{3},PlyTypeNames,'exact')-1,8)+1;

               if ~isempty(tmp) & ~isempty(tmp2)
                  TypeSize(j) = SizeOf(tmp);
                  Type{j} = MatlabTypeNames{tmp};
                  TypeSize2(j) = SizeOf(tmp2);
                  Type2{j} = MatlabTypeNames{tmp2};
	       else
   	   	 fclose(fid);
               	 error(['Unknown property data type, ''list ',Token{2},' ',Token{3},''', in ', ...
                   ElementNames{i},'.',CurPropertyNames{j},'.']);
               end
            else
               fclose(fid);
               error(['Invalid list syntax in ',ElementNames{i},'.',CurPropertyNames{j},'.']);
            end
         end
      end

      % read file
      if ~ListFlag
         if SameFlag
            % no list types, all the same type (fast)
            Data = fread(fid,[NumProperties,ElementCount(i)],Type{1})';
         else
            % no list types, mixed type
            Data = zeros(ElementCount(i),NumProperties);

         	for j = 1:ElementCount(i)
        			for k = 1:NumProperties
               	Data(j,k) = fread(fid,1,Type{k});
              	end
         	end
         end
      else
         ListData = cell(NumProperties,1);

         for k = 1:NumProperties
            ListData{k} = cell(ElementCount(i),1);
         end

         if NumProperties == 1
            BufSize = 512;
            SkipNum = 4;
            j = 0;

            % list type, one property (fast if lists are usually the same length)
            while j < ElementCount(i)
               Position = ftell(fid);
               % read in BufSize count values, assuming all counts = SkipNum
               [Buf,BufSize] = fread(fid,BufSize,Type{1},SkipNum*TypeSize2(1));
               Miss = find(Buf ~= SkipNum);					% find first count that is not SkipNum
               fseek(fid,Position + TypeSize(1),-1); 		% seek back to after first count

               if isempty(Miss)									% all counts are SkipNum
                  Buf = fread(fid,[SkipNum,BufSize],[int2str(SkipNum),'*',Type2{1}],TypeSize(1))';
                  fseek(fid,-TypeSize(1),0); 				% undo last skip

                  for k = 1:BufSize
                     ListData{1}{j+k} = Buf(k,:);
                  end

                  j = j + BufSize;
                  BufSize = floor(1.5*BufSize);
               else
                  if Miss(1) > 1									% some counts are SkipNum
                     [Buf2,cc] = fread(fid,[SkipNum,Miss(1)-1],[int2str(SkipNum),'*',Type2{1}],TypeSize(1));
                     Buf2 = Buf2';

                     for k = 1:Miss(1)-1
                        ListData{1}{j+k} = Buf2(k,:);
                     end

                     j = j + k;
                  end
                  % Alec: check if done and rewind one step
                  if j >= ElementCount(i)
                    fseek(fid,-1,0);
                    break;
                  end

                  % read in the list with the missed count
                  SkipNum = Buf(Miss(1));
                  j = j + 1;
                  ListData{1}{j} = fread(fid,[1,SkipNum],Type2{1});
                  BufSize = ceil(0.6*BufSize);
               end
            end
         else
            % list type(s), multiple properties (slow)
            Data = zeros(ElementCount(i),NumProperties);

            for j = 1:ElementCount(i)
         		for k = 1:NumProperties
            		if isempty(Type2{k})
               		Data(j,k) = fread(fid,1,Type{k});
            		else
               		tmp = fread(fid,1,Type{k});
               		ListData{k}{j} = fread(fid,[1,tmp],Type2{k});
		            end
      		   end
      		end
         end
      end
   end

   % put data into Elements structure
   for k = 1:NumProperties
   	if (~Format & ~Type(k)) | (Format & isempty(Type2{k}))
      	eval(['Elements.',ElementNames{i},'.',CurPropertyNames{k},'=Data(:,k);']);
      else
      	eval(['Elements.',ElementNames{i},'.',CurPropertyNames{k},'=ListData{k};']);
		end
   end
end

clear Data ListData;
fclose(fid);

if (nargin > 1 & strcmpi(Str,'Tri')) | nargout > 2
   % find vertex element field
   Name = {'vertex','Vertex','point','Point','pts','Pts'};
   Names = [];

   for i = 1:length(Name)
      if any(strcmp(ElementNames,Name{i}))
         Names = getfield(PropertyNames,Name{i});
         Name = Name{i};
         break;
      end
   end

   if any(strcmp(Names,'x')) & any(strcmp(Names,'y')) & any(strcmp(Names,'z'))
      eval(['varargout{1}=[Elements.',Name,'.x,Elements.',Name,'.y,Elements.',Name,'.z];']);
   else
      varargout{1} = zeros(1,3);
	end

   varargout{2} = Elements;
   varargout{3} = Comments;
   Elements = [];

   % find face element field
   Name = {'face','Face','poly','Poly','tri','Tri'};
   Names = [];

   for i = 1:length(Name)
      if any(strcmp(ElementNames,Name{i}))
         Names = getfield(PropertyNames,Name{i});
         Name = Name{i};
         break;
      end
   end

   if ~isempty(Names)
      % find vertex indices property subfield
	   PropertyName = {'vertex_indices','vertex_indexes','vertex_index','indices','indexes'};

   	for i = 1:length(PropertyName)
      	if any(strcmp(Names,PropertyName{i}))
         	PropertyName = PropertyName{i};
	         break;
   	   end
      end

      if ~iscell(PropertyName)
         % convert face index lists to triangular connectivity
         eval(['FaceIndices=varargout{2}.',Name,'.',PropertyName,';']);
  			N = length(FaceIndices);
   		Elements = zeros(N*2,3);
   		Extra = 0;

			for k = 1:N
   			Elements(k,:) = FaceIndices{k}(1:3);

   			for j = 4:length(FaceIndices{k})
      			Extra = Extra + 1;
	      		Elements(N + Extra,:) = [Elements(k,[1,j-1]),FaceIndices{k}(j)];
   			end
         end
         Elements = Elements(1:N+Extra,:) + 1;
      end
   end
else
   varargout{1} = Comments;
end


function [ node_num, element_num, element_type ] = tec_zone_line_parse ( line )

%*****************************************************************************80
%
%% TEC_ZONE_LINE_PARSE parses the "ZONE" line of a TEC file.
%
%  Discussion:
%
%    The string begins with the substring "ZONE" and is followed by
%    a sequence of keywords and values joined by an equals sign.
%
%    We expect the following, but in arbitrary order, separated
%    by spaces or commas:
%
%      N = number of nodes
%      E = number of elements
%      T = optional title (we can't handle this field right now)
%      PACKING = POINT
%      ZONETYPE = FETRIANGLE or FEQUADRILATERAL or FETETRAHEDRON or FEBRICK.
%
%    Other arguments that may appear on this line will be ignore.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    20 February 2006
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, string LINE, a string of characters, representing the
%    "VARIABLES=" line of the file.
%
%    Output, integer NODE_NUM, the number of nodes.
%
%    Output, integer ELEMENT_NUM, the number of elements.
%
%    Output, string ELEMENT_TYPE, the element type:
%    FETRIANGLE or FEQUADRILATERAL or FETETRAHEDRON or FEBRICK..
%

%
%  Remove the initial "ZONE"
%
  line = s_behead_substring ( line, 'ZONE' );
%
%  Replace each EQUALS sign by a space.
%  Also get rid of commas and periods.
%  Do single and double quotes have to go, also?
%
  line = s_replace_ch ( line, '=', ' ' );
  line = s_replace_ch ( line, ',', ' ' );
  line = s_replace_ch ( line, '.', ' ' );
%
%  Now each pair of words represents a name and a value.
%
  node_num = -1;
  element_num = -1;
  element_type = [];

  found_num = 0;

  while ( 1 )

    [ name, line ] = s_word_extract ( line );

    if ( isempty ( name ) )
      fprintf ( 1, '\n' );
      fprintf ( 1, 'TEC_ZONE_LINE_PARSE - Fatal error!\n' );
      fprintf ( 1, '  Unexpected End of input.\n' );
      error ( 'TEC_ZONE_LINE_PARSE - Fatal error!' );
    end

    [ value, line ] = s_word_extract ( line );

    if ( isempty ( value ) )
      fprintf ( 1, '\n' );
      fprintf ( 1, 'TEC_ZONE_LINE_PARSE - Fatal error!\n' );
      fprintf ( 1, '  Unexpected End of input.\n' );
      error ( 'TEC_ZONE_LINE_PARSE - Fatal error!' );
    end

    if ( ch_eqi ( name(1), 'N' ) & node_num == -1 )

      node_num = s_to_i4 ( value );
      found_num = found_num + 1;

    elseif ( ch_eqi ( name(1), 'E' ) & element_num == -1 )

      element_num = s_to_i4 ( value );
      found_num = found_num + 1;

    elseif ( s_eqi ( name, 'DATAPACKING' ) )

      if ( ~s_eqi ( value, 'POINT' ) )
        fprintf ( 1, '\n' );
        fprintf ( 1, 'TEC_ZONE_LINE_PARSE - Fatal error!\n' );
        fprintf ( 1, '  Value of DATAPACKING argument must be POINT.\n' );
        error ( 'TEC_ZONE_LINE_PARSE - Fatal error!' );
      end

    elseif ( s_eqi ( name, 'ZONETYPE' ) & isempty ( element_type ) )

      found_num = found_num + 1;
      element_type = value;

    else

      fprintf ( 1, '\n' );
      fprintf ( 1, '  Ignoring "%s = %s".\n', name, value );

    end

    if ( found_num == 3 )
      break
    end

  end

  return
end


function line = UIAddLine(dir,varargin)

%UIAddLine - Interactively add horizontal/vertical line to existing plot.
%
%  USAGE
%
%    line = UIAddLine(dir,<options>)
%
%    dir            'v' for vertical, 'h' for horizontal
%    <options>      options for function plot
%
%  NOTE
%
%    Click where the line should appear, then hit 'ENTER'.

% Copyright (C) 2004-2011 by Michaël Zugaro
%
% 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.

% Check number of parameters
if nargin < 1,
  error('Incorrect number of parameters (type ''help UIAddLine'' for details).');
end

hold on;
x = ginput;
if strcmp(lower(dir),'v'),
	line = plot([x(end,1) x(end,1)],ylim,varargin{:});
else
	line = plot(xlim,[x(end,2) x(end,2)],varargin{:});
end


% requires:
% - moxunit: https://github.com/MOxUnit/MOxUnit
% - bids-matlab

addpath(fullfile(pwd, '..'));

input_file = fullfile(pwd, '..', 'input', 'spreadsheet_to_convert.xlsx');
files_out = bids_spreadsheet2participants(input_file, 'ignore', 'comment', 'export', pwd);

json_expected = jsonread(fullfile(pwd, 'data', 'participants.json'));
json_actual = jsonread(fullfile(pwd, 'participants.json'));
% uses moxunit assertEqual
assertEqual(json_actual, json_expected);

tsv_expected  = bids.util.tsvread(fullfile(pwd, 'data', 'participants.tsv'));
tsv_actual  = bids.util.tsvread(fullfile(pwd, 'participants.tsv'));
assertEqual(tsv_actual, tsv_expected);


function [color, marker, line_style] = color_spec()
% -------------------------------------------------------------------------
%   Description:
%       return color information to plot figures in our paper
%
%   Citation:
%       A Comparative Study for Single Image Blind Deblurring
%       Wei-Sheng Lai, Jia-Bin Huang, Zhe Hu, Narendra Ahuja, and Ming-Hsuan Yang
%       IEEE Conference on Computer Vision and Pattern Recognition (CVPR), 2016
%
%   Contact:
%       Wei-Sheng Lai
%       wlai24@ucmerced.edu
%       University of California, Merced
% -------------------------------------------------------------------------

color = {};                             marker = {};            line_style = {};
color{end+1} = [1, 0.5, 39/255];        marker{end+1} = 'o';    line_style{end+1} = '--';
color{end+1} = [0, 1, 0];               marker{end+1} = 'o';    line_style{end+1} = '--';
color{end+1} = [136/255, 0, 21/255];    marker{end+1} = 'o';    line_style{end+1} = '--';
color{end+1} = [1, 0, 1];               marker{end+1} = 'o';    line_style{end+1} = '--';
color{end+1} = [0, 0, 1];               marker{end+1} = 'o';    line_style{end+1} = '--';
color{end+1} = [1, 0, 0];               marker{end+1} = 'o';    line_style{end+1} = '--';
color{end+1} = [0, 162/255, 232/255];   marker{end+1} = 'o';    line_style{end+1} = '--';

color{end+1} = [1, 0.5, 39/255];        marker{end+1} = 's';    line_style{end+1} = '-';
color{end+1} = [0, 1, 0];               marker{end+1} = 's';    line_style{end+1} = '-';
color{end+1} = [136/255, 0, 21/255];    marker{end+1} = 's';    line_style{end+1} = '-';
color{end+1} = [1, 0, 1];               marker{end+1} = 's';    line_style{end+1} = '-';
color{end+1} = [0, 0, 1];               marker{end+1} = 's';    line_style{end+1} = '-';
color{end+1} = [1, 0, 0];               marker{end+1} = 's';    line_style{end+1} = '-';
color{end+1} = [0, 162/255, 232/255];   marker{end+1} = 's';    line_style{end+1} = '-';


function varargout=lpczz2ss(varargin)
%
% For calling details please see v_lpczz2ss.m
%
% This dummy routine is included for backward compatibility only
% and will be removed in a future release of voicebox. Please use
% v_lpczz2ss.m in future and/or update with v_voicebox_update.m
%
%      Copyright (C) Mike Brookes 2018
%      Version: $Id: lpczz2ss.m 10863 2018-09-21 15:39:23Z dmb $
%
if nargout
    varargout=cell(1,nargout);
    [varargout{:}]=v_lpczz2ss(varargin{:});
else
    v_lpczz2ss(varargin{:});
end


% see also retreating_stall_envelope.m

profile_cd = .008; %blade Cd0
ClimbEfficiency = .9;
Kind = 1.05;
max_tip_M = .92;


%kvcentfilt 'Center Filter'
% This MatLab function was automatically generated by a converter (KhorosToMatLab) from the Khoros vcentfilt.pane file
%
% Parameters:
% InputFile: i 'Input Image', required: 'input image'
% InputFile: str 'Struct Element', required: 'Structuring Element'
% OutputFile: o 'Output Image ', required: 'resulting output image'
%
% Example: o = kvcentfilt({i, str}, {'i','';'str','';'o',''})
%
% Khoros helpfile follows below:
%
%  PROGRAM
% vcentfilt - Center Filter
%
%  DESCRIPTION
% .I vcentfilt
% will do the center filter of an input image by a set structuring element.
%
% The output image data type will be the same as that of the highest data type
% of the input image.
%
%
%
%  EXAMPLES
% vcentfilt -i ball.xv -str b272.str -o outimage.xv
%
% Will do the center filter of the image "ball.xv" by the structuring set b272.str, with the resulting image written to "outimage.xv".
%
%  "SEE ALSO"
%
%  RESTRICTIONS
% .I vcentfilt
% can be defined for all data types supported by Khoros, but at the moment it has been implemented just for the bit and unsigned char types.
% The structuring elements are subsets of the 3x3 matrix and the origin is always at the center of this matrix.
%
%  REFERENCES
%
%  COPYRIGHT
% Copyright (C) 1993-1997 Junior Barrera, Roberto Lotufo.  All rights reserved.
%

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

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


function paramV = mtrFilename2Param(fileName,paramName)

% Will retrieve paramnames from files of the form *paramName_#_*.dat

sI = strfind(fileName,paramName)+length(paramName)+1;
eIVec = strfind(fileName,'_');
eI = min( eIVec(eIVec>sI) )-1;
if isempty(eI)
    eI = max(strfind(fileName,'.'))-1;
end
paramV = str2num(fileName(sI:eI));
return;


function options = lipsolset(varargin)
%LIPSOLSET  Create or alter the options for Optimization with LIPSOL
%
% options = lipsolset('param1',value1,'param2',value2,...) creates an LIPSOL
% options structure with the parameters 'param' set to their corresponding
% values in 'value'. Parameters not specified will be set to the LIPSOL
% default.
%
% options = lipsolset(oldopts,'param1',value1,...) creates a copy of the old
% options 'oldopts' and then fills in (or writes over) the parameters
% specified by 'param' and 'value'.
%
% options = lipsolset() creates an options structure with all fields set to
% lipsolset defaults.
%
% lipsolset() prints a list of all possible fields and their function.
%
% See supplied LIPSOL Documentation for further details of these options.

%   Copyright (C) 2013 Jonathan Currie (I2C2)

% Print out possible values of properties.
if((nargin == 0) && (nargout == 0))
    printfields();
    return
end

%Names and Defaults
Names = {'phi0';'tau0';'monitor';'verb';'big';'cache_size';'loop_level'};
Defaults = {1e-5,0.9995,0,1,1e32,16,8}';

%Enter and check user args
try
    options = opticheckset(Names,Defaults,@checkfield,varargin{:});
catch ME
    throw(ME);
end

function checkfield(field,value)
%Check a field contains correct data type
switch lower(field)
    %Scalar double
    case 'big'
        err = opticheckval.checkScalarDbl(value,field);
    %Scalar non negative double
    case 'phi0'
        err = opticheckval.checkScalarNonNeg(value,field);
    %Scalar double 0 < val <= 1
    case 'tau0'
        err = opticheckval.checkScalarBoundLLE(value,field,0,1);
    %Scalar 0/1
    case 'monitor'
        err = opticheckval.checkScalar01(value,field);
    %Scalar Int 0-2
    case 'verb'
        err = opticheckval.checkScalarIntBoundLELE(value,field,0,2);
    %Scalar Int 1 - 100
    case 'cache_size'
        err = opticheckval.checkScalarIntBoundLELE(value,field,1,100);
    %Scalar Int 1-8
    case 'loop_level'
        err = opticheckval.checkScalarIntBoundLELE(value,field,1,8);
    otherwise
        err = MException('OPTI:SetFieldError','Unrecognized parameter name ''%s''.', field);
end
if(~isempty(err)), throw(err); end

function printfields()
%Print out fields with defaults
fprintf('       phi0: [ Relative Termination Tolerance: {1e-5} ] \n');
fprintf('       tau0: [ Initial Line-Search Step-Size Multiplier: {0.9995} ] \n');
fprintf('        big: [ Bound Infinite Value: {1e32} ] \n');
fprintf('    monitor: [ Graphical Iteration Monitor: On (1), Off {0} ] \n');
fprintf('       verb: [ Verbosity: On (1), Off {0} ] \n');
fprintf(' cache_size: [ SYMFCT Cache Size: {16} ] \n');
fprintf(' loop_level: [ BLKFCT Loop Unroll Level: 1,2,4,{8} ] \n');

fprintf('\n');


function L = eq(p1,p2)
%PREAL/EQ Overloaded EQ (==) operator for class preal.

global useUnitsFlag

if ~(useUnitsFlag) % If physunits is disabled...
    L=eq(double(p1),double(p2)); % ... treat as double.
    return
end

p1=preal(p1);
p2=preal(p2);
tol=0.001;
if isscalar(p1)
    p1=preal(p1.value*ones(size(p2)),p1.units);
elseif isscalar(p2)
    p2=preal(p2.value*ones(size(p1)),p2.units);
elseif ndims(p1)==ndims(p2)&size(p1)==size(p2)
    % do nothing
else
    error('Array dimensions must agree')
end

L=false(size(p1));
for k=1:numel(L)
    if any(abs(p1(k).units-p2(k).units)>tol)
        error('Attempt to compare dimensionally inconsistent preals')
    end
    L(k)=eq(p1(k).value,p2(k).value);
end


%
% Plot a motor trajectory on top of the image
%
% Input
%   I [105 x 105] image (double or binary) (large numbers are BLACK)
%   drawings: [nested cell] of strokes in motor space
function plot_image_only(I)
    assert(size(I,1)==105);
    assert(size(I,2)==105);
    newI = 1-I;
    newI(isinf(I)) = 0.5;
    image([1 105],[1 105],repmat(newI,[1 1 3]));
    set(gca,'YDir','reverse','XTick',[],'YTick',[]);
    xlim([1 105]);
    ylim([1 105]);
end


function []=strsdisp(decimaldates1,stringdates1,strshocks_estimates,endo,pref,IRFt,strctident)

if IRFt==1||IRFt==2||IRFt==3
    identified=size(endo,1); % fully identified
    labels=endo; % simply use the name of the endogenous variables as labels
elseif IRFt==4 || IRFt==6 %if the model is identified by sign restrictions or sign restrictions (+ IV)
    identified=size(strctident.signreslabels_shocks,1); % count the labels provided in the sign res sheet (+ IV)
    labels=strctident.signreslabels_shocks; % signreslabels, only identified shocks
elseif IRFt==5
    identified=1; % one IV shock
    labels{identified,1}=strcat('IV Shock (',strctident.Instrument,')'); % one label for the IV shock
end

if pref.plot==1
% create structural shocks figure
strshocks=figure('Tag','BEARresults');
set(strshocks,'Color',[0.9 0.9 0.9]);
set(strshocks,'name','structural shocks');
ncolumns=ceil(identified^0.5);
nrows=ceil(identified/ncolumns);
for ii=1:identified
subplot(nrows,ncolumns,ii);
hold on
Xpatch=[decimaldates1' fliplr(decimaldates1')];
Ypatch=[strshocks_estimates{ii,1}(1,:) fliplr(strshocks_estimates{ii,1}(3,:))];
Fpatch=patch(Xpatch,Ypatch,[0.7 0.78 1]);
set(Fpatch,'facealpha',0.6);
set(Fpatch,'edgecolor','none');
plot(decimaldates1,strshocks_estimates{ii,1}(2,:),'Color',[0.4 0.4 1],'LineWidth',2);
plot([decimaldates1(1,1),decimaldates1(end,1)],[0 0],'k--');
hold off
minband=min(strshocks_estimates{ii,1}(1,:));
maxband=max(strshocks_estimates{ii,1}(3,:));
space=maxband-minband;
if space==0
    space=0.001;
end
Ymin=minband-0.2*space;
Ymax=maxband+0.2*space;
set(gca,'XLim',[decimaldates1(1,1) decimaldates1(end,1)],'YLim',[Ymin,Ymax],'FontName','Times New Roman');
% create title
title(labels{ii,1},'FontName','Times New Roman','FontSize',10,'FontWeight','normal','interpreter','none');
end

end % pref.plot

% finally, record the results on excel
if pref.results==1
bear.data.excelrecord9fcn(stringdates1, labels, strshocks_estimates, identified, pref)
end


classdef prtCluster < prtAction
    % prtCluster   Base class for prt clustering objects
    %
    %   prtCluster is an abstract class and cannot be instantiated.
    %
    %   All prtCluster objects inherit all properities and methods from the
    %   prtAction object.
    %
    %   prtCluster objects have the following methods:
    %
    %   plot          - Plot the output cluster of a trained clustering
    %                   algorithm. Only valid when the clutering algorithm
    %                   has been trained with a data set of 3 dimensions or
    %                   less.
    %
    %   A prtCluster object inherits the TRAIN, RUN, CROSSVALIDATE and KFOLDS
    %   functions from the prtAction object.
    %
    %   See also prtClusterGmm, prtClusterKmeans

    properties (Abstract)
        nClusters  % The number of clusters
    end
    properties (Dependent)
        internalDecider  % Optional prtDecider object for making decisions
    end
    properties (SetAccess = private, GetAccess = private, Hidden=true)
        internalDeciderDepHelper = [];
    end
    properties (Dependent)
        includesDecision % Flag indicating if result includes a decision
    end
    properties (SetAccess=protected, Hidden = true)
        yieldsMaryOutput = nan; % Determined in trainProcessing()
    end
    properties (Hidden = true)
        plotOptions = prtClass.initializePlotOptions();
    end
    properties (SetAccess = protected)
        isSupervised = false;  % False
        isCrossValidateValid = true; % True
	end

	methods (Hidden = true)
        function featureNameModificationFunction = getFeatureNameModificationFunction(self)
            if ~self.includesDecision
				featureNameModificationFunction = prtUtilFeatureNameModificationFunctionHandleCreator('%s Membership in cluster #index#', self.nameAbbreviation);
			else
				featureNameModificationFunction = prtUtilFeatureNameModificationFunctionHandleCreator('Class Label');
            end
        end
	end

    methods
        function self = prtCluster()
            % As an action subclass we must set the properties to reflect
            % our dataset requirements
            self.classTrain = 'prtDataSetClass'; % Can't this be prtDataSetStandard?
            self.classRun = 'prtDataSetStandard';
            self.classRunRetained = true;
        end

        function val = get.internalDecider(self)
            val = self.internalDeciderDepHelper;
        end
        function self = set.internalDecider(self,val)
            if ~isempty(val) && ~isa(val,'prtDecision')
                error('prtClass:internalDecider','internalDecider must be an empty vector ([]) of type prtDecision, but input is a %s',class(val));
            end

            if ~isempty(val) && ~val.isTrained && self.isTrained
                % We are adding a non-trained decision to a trained
                % classifier if we have verboseStorage = true we can train
                % the decider. If we don't we have to untrain and warn

                if ~isempty(self.dataSet)
                    % We have the dataset
                    self.internalDeciderDepHelper = val;
                    self = postTrainProcessing(self, self.dataSet);
                    self.yieldsMaryOutput = false;
                else
                    %Make self.isTrained false:
                    self.isTrained = false;
                    %warn user?
                    warning('prt:internalDecider:isTrained','Setting the internalDecider property of a prtClass object with an un-trained internalDecider sets the prtClass object''s isTrained to false');
                end
            else
                self.internalDeciderDepHelper = val;
            end

        end

        function has = get.includesDecision(self)
            has = ~isempty(self.internalDecider);
        end

        function varargout = plot(self)
            % PLOT  Plot the output of the prtCluster object
            %
            %   OBJ.plot() plots the output confidence of a prtClass
            %   object. This function only operates when the dimensionality
            %   of dataset is 3 or less. When verboseStorage is set to
            %   'true', the training data points are also displayed on the
            %   plot.
            %
            %   See also: prtClass\plotDecision

            assert(self.isTrained,'Clusterer must be trained before it can be plotted.');
            assert(self.dataSetSummary.nFeatures < 4, 'nFeatures in the training dataset must be less than or equal to 3');

            if self.yieldsMaryOutput
                self = trainAutoDecision(self);
            end

            handleStructure = plotBinaryClusterConfidence(self);

            if ~isempty(self.dataSet) && ~isempty(self.dataSet.name)
                title(sprintf('%s (%s)',self.name,self.dataSet.name));
            else
                title(self.name);
            end

            varargout = {};
            if nargout > 0
                varargout = {handleStructure};
            end
        end
    end

    methods (Access = protected, Hidden = true)

        function self = postTrainProcessing(self,ds)
             if ~isempty(self.internalDecider)
                tempself = self;
                tempself.internalDecider = [];
                yOut = tempself.run(ds);
                self.internalDecider = self.internalDecider.train(yOut);
                self.internalDecider.classList = 1:self.nClusters;
            end
        end

        function self = preTrainProcessing(self, ds)
            % Overload preTrainProcessing() so that we can determine mary
            % output status

            self.yieldsMaryOutput = ~self.includesDecision;

            self = preTrainProcessing@prtAction(self, ds);
        end

        function outputDs = postRunProcessing(self, inputDs, outputDs)
            % Overload postRunProcessing (from prtAction) so that we can
            % enforce the internaldecider

            if ~isempty(self.internalDecider)
                outputDs = self.internalDecider.run(outputDs);
            end

            outputDs = postRunProcessing@prtAction(self, inputDs, outputDs);
        end

        % Plotting functions
        function [outputDs, linGrid, gridSize] = runClustererOnGrid(self, upperBounds, lowerBounds)

            if nargin < 3 || isempty(lowerBounds)
                lowerBounds = self.dataSetSummary.lowerBounds;
            end

            if nargin < 2 || isempty(upperBounds)
                upperBounds = self.dataSetSummary.upperBounds;
            end

            [linGrid, gridSize] = prtPlotUtilGenerateGrid(upperBounds, lowerBounds, self.plotOptions.nSamplesPerDim);

            outputDs = run(self,prtDataSetClass(linGrid));
        end

        function handleStructure = plotBinaryClusterConfidence(self)

            [outputDs, linGrid, gridSize] = runClustererOnGrid(self);

            %internalDeciders* output the right colors:
            imageHandle = prtPlotUtilPlotGriddedEvaledClassifier(outputDs.getObservations(), linGrid, gridSize, prtPlotUtilLightenColors(self.plotOptions.colorsFunction(self.nClusters)),self);

            if ~isempty(self.dataSet)
                hold on;
                handles = plot(self.dataSet);
                hold off;
                handleStructure.Axes = struct('imageHandle',{imageHandle},'handles',{handles});
            else
                handleStructure.Axes = struct('imageHandle',{imageHandle},'handles',{[]});
            end
        end

        function self = trainAutoDecision(self)
            if ~isempty(self.dataSet)
                % warning('prt:prtCluster:plot:autoDecision','prtCluster.plot() requires a prtCluster with an internal decider. A prtDecisionMap has been trained and set as the internalDecider to enable plotting.');

                % It is much easier (and safer) to just train the
                % prtDecisionMap
                self.internalDecider = prtDecisionMap;
                self = postTrainProcessing(self, self.dataSet);
                self.yieldsMaryOutput = false;
            else
                % warning('prt:prtCluster:plot:autoDecision','prtCluster.plot() requires a prtCluster with an internal decider. A prtDecisionMap has been trained and set as the internalDecider to enable plotting.');
                % error('prt:prtCluster:plot','prtCluster.plot() requires a prtCluster with an internal decider. A prtDecisionMap cannot be trained and set as the internalDecider to enable plotting because this cluster object does not have verboseStorege turned on and therefore the dataSet used to train the clusterer is unknow. To enable plotting, set an internalDecider and retrain the clusterer.');

                % Since we don't have the dataset we just mock train
                % This is slightly dangerous in the case when
                % prtDecisionMap changes to include more parameters. or
                % dataSetSummary changes. (It is currently only a struct)
                tempInternalDecider = prtDecisionMap;
                tempInternalDecider.isTrained = true;
                tempInternalDecider.classList = 1:self.nClusters;
                tempInternalDecider.dataSetSummary = self.dataSetSummary;
                tempInternalDecider.dataSetSummary.nFeatures = self.nClusters;

                self.internalDecider = tempInternalDecider;
            end
        end
    end

    methods (Static, Hidden = true)
        function plotOptions = initializePlotOptions()
            if ~isdeployed
                plotOptions = prtOptionsGet('prtOptionsClusterPlot');
            else
                plotOptions = prtOptions.prtOptionsClusterPlot;
            end
        end
    end
end


function [dietPerformance] = compareDietPerformance(model, diets,rois, varargin)
% Function to check the effect of different diets on the possible flux
% range for specified reactions.
%
% [dietPerformance] = compareDietPerformance(model, diets, rois, varargin)
%
% Example: [dietPerformance] = compareDietPerformance(model, diets, rois,
% 'optPercentage', 0.95, 'microbiotaExcretionBound', 0.5)
%
% INPUTS:
%   model:          COBRA model where the rois are present
%   diets:          Cell array where each cell contains an cell array with
%                   The metabolite composition of the diet {'met', value}
%   rois:           Cell array with reactions to investigate under
%                   different diets
%
% OPTIONAL INPUTS:
%   optPercentage:  Integer, used in fluxVariability to indicate the
%                   percentage of the objective for which FVA is solved.
%                   Defaults to 99
%   microbiotaExretionBound: Integer, used to constrain both bounds of
%                   Excretion_EX_microbiota_LI_biomass. Defaults to 1
%   dietNames:      Cell array with names for diets in that will be used in
%                   the output. Defaults to Diet1, Diet2 etcetc.
% OUTPUT:
%   dietPerformance:    Table for each given diet, the min and max flux for
%                   each roi.
%
% .. Authors: - Bram Nap 06-2022

% Initialise the inputs
parser = inputParser();
parser.addRequired('model', @isstruct);
parser.addRequired('diets', @iscell);
parser.addRequired('rois', @iscell);
parser.addParameter('optPercentage', 99, @isnumeric);
parser.addParameter('microbiotaExcretionBound', 1, @isnumeric);
parser.addParameter('dietNames', cell(0), @iscell);

parser.parse(model, diets, rois, varargin{:});

model = parser.Results.model;
diets = parser.Results.diets;
rois = parser.Results.rois;
optPercentage = parser.Results.optPercentage;
microbiotaExcretionBound = parser.Results.microbiotaExcretionBound;
dietNames = parser.Results.dietNames;

% If the dietNames are not specified, create general names
if isempty(dietNames)
    for i = 1:length(diets)
        dietNames(i) = {['Diet' + string(i)]};
    end
    % Required to prevent errors
    dietNames = string(dietNames);
end

% Initialise array to store results in
dietPerformance = cell(2*length(rois), length(diets)+2);

% Loop over the diets
for i = 1:length(diets)
    diet = diets{i};

    % Constrain the model to the diet and microbiota exchange bounds
    modelDiet = setFoodConstraints(model, diet);
    modelDiet = changeRxnBounds(modelDiet, 'Excretion_EX_microbiota_LI_biomass',microbiotaExcretionBound,'b');

    % Solve with FVA and store results
    [minFlux, maxFlux] = fluxVariability(modelDiet, optPercentage, 'max', rois);
    for j = 1:length(rois)
        dietPerformance(j*2-1:j*2,1) = {rois(j)};
        dietPerformance(j*2-1:j*2,2) = {{'min'};{'max'}};
        dietPerformance(j*2-1:j*2,i+2) = {minFlux(j); maxFlux(j)};
    end
end

% Create the final table
dietPerformance = cell2table(dietPerformance);
dietPerformance.Properties.VariableNames(1:2) = {'Reactions', 'MinMax'};
dietPerformance.Properties.VariableNames(3:end) = dietNames;

end


function tests = test_spm_cfg_dcm_fmri
% Unit Tests for spm_cfg_dcm_fmri (DCM fMRI spec batch)
%__________________________________________________________________________
% Copyright (C) 2016 Wellcome Trust Centre for Neuroimaging

% $Id: test_spm_cfg_dcm_fmri.m 7479 2018-11-09 14:17:33Z peter $

tests = functiontests(localfunctions);

% -------------------------------------------------------------------------
function setup(testCase)
% Delete artefacts before each test

% Delete GCM from the tmp directory
outpath = fullfile(get_data_path(), 'tmp');
if exist(outpath,'file')
    delete(fullfile(outpath,'*.mat'));
end

% Delete DCMs from the GLM directory
glmdir = fullfile(get_data_path(), 'GLM');
dcms   = cellstr(spm_select('FPListRec',glmdir,'^DCM_.*mat$'));
for i = 1:length(dcms)
    spm_unlink(dcms{i});
end

% -------------------------------------------------------------------------
function test_specify_group(testCase)

data_path = get_data_path();

% Output path & directory
outpath = fullfile(data_path, 'tmp');
dir_out = outpath;
if ~exist(dir_out,'file')
    mkdir(dir_out);
end

% Template DCMs
fullm   = fullfile(data_path,'models','DCM_s1_m1.mat');
altm    = {fullfile(data_path,'models','DCM_s1_m2.mat');
           fullfile(data_path,'models','DCM_s1_m3.mat');
           fullfile(data_path,'models','DCM_s1_m4.mat')};

% All subjects' SPMs and VOIs
spms    = cellstr(spm_select('FPListRec',data_path,'SPM.mat'));
voi1    = cellstr(spm_select('FPListRec',data_path,'VOI_R1.mat'));
voi2    = cellstr(spm_select('FPListRec',data_path,'VOI_R2.mat'));

% Run
matlabbatch{1}.spm.dcm.spec.fmri.group.output.dir  = cellstr(dir_out);
matlabbatch{1}.spm.dcm.spec.fmri.group.output.name = 'specify_group_test';
matlabbatch{1}.spm.dcm.spec.fmri.group.template.fulldcm = cellstr(fullm);
matlabbatch{1}.spm.dcm.spec.fmri.group.template.altdcm  = altm;
matlabbatch{1}.spm.dcm.spec.fmri.group.data.spmmats      = spms;
matlabbatch{1}.spm.dcm.spec.fmri.group.data.session      = 1;
matlabbatch{1}.spm.dcm.spec.fmri.group.data.region = {voi1
                                                      voi2}';

out = spm_jobman('run',matlabbatch);

% Load output
GCM = load(out{1}.gcmmat{1});
GCM = GCM.GCM;
GCM = spm_dcm_load(GCM);

% Check each subject's DCM timeseries match their VOIs
for s = 1:size(GCM,1)

    % Load VOI files
    xY1 = load(voi1{s});
    xY2 = load(voi2{s});
    Y_expected = [xY1.Y xY2.Y];

    % Check VOI match actual timeseries
    for m = 1:size(GCM,2)
        Y_actual   = GCM{s,m}.Y.y;
        testCase.assertTrue(all(Y_expected(:)==Y_actual(:)), ...
            sprintf('Timeseries for subject %d model %m wrong',s,m));
    end
end

% -------------------------------------------------------------------------
function data_path = get_data_path()

data_path = fullfile( spm('Dir'), 'tests', ...
    'data', 'fMRI', 'simulated_2region');