function [net,stats] = train_rand(net, imdb, getBatch, varargin)
%CNN_TRAIN_DAG Demonstrates training a CNN using the DagNN wrapper
%    CNN_TRAIN_DAG() is similar to CNN_TRAIN(), but works with
%    the DagNN wrapper instead of the SimpleNN wrapper.

% Copyright (C) 2014-16 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).
addpath(fullfile(vl_rootnn, 'examples'));

%%%%%%%%%%%%% new fields %%%%%%%%%%%%%
opts.saveInterval = 2;
%%%%%%%%%%%%% new fields %%%%%%%%%%%%%

opts.expDir = fullfile('data','exp') ;
opts.continue = true ;
opts.batchSize = 256 ;
opts.numSubBatches = 1 ;
opts.train = [] ;
opts.val = [] ;
opts.gpus = [] ;
opts.prefetch = false ;
opts.epochSize = inf;
opts.numEpochs = 300 ;
opts.learningRate = 0.001 ;
opts.weightDecay = 0.0005 ;

opts.solver = [] ;  % Empty array means use the default SGD solver
[opts, varargin] = vl_argparse(opts, varargin) ;
if ~isempty(opts.solver)
    assert(isa(opts.solver, 'function_handle') && nargout(opts.solver) == 2,...
        'Invalid solver; expected a function handle with two outputs.') ;
    % Call without input arguments, to get default options
    opts.solverOpts = opts.solver() ;
end

opts.momentum = 0.9 ;
opts.saveSolverState = true ;
opts.nesterovUpdate = false ;
opts.randomSeed = 0 ;
opts.profile = false ;
opts.parameterServer.method = 'mmap' ;
opts.parameterServer.prefix = 'mcn' ;

opts.derOutputs = {'objective', 1} ;
opts.extractStatsFn = @extractStats ;
opts.plotStatistics = true;
opts.postEpochFn = [] ;  % postEpochFn(net,params,state) called after each epoch; can return a new learning rate, 0 to stop, [] for no change
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 isscalar(opts.train) && isnumeric(opts.train) && isnan(opts.train)
    opts.train = [] ;
end
if isscalar(opts.val) && isnumeric(opts.val) && isnan(opts.val)
    opts.val = [] ;
end

% -------------------------------------------------------------------------
%                                                            Initialization
% -------------------------------------------------------------------------

evaluateMode = isempty(opts.train) ;
if ~evaluateMode
    if isempty(opts.derOutputs)
        error('DEROUTPUTS must be specified when training.\n') ;
    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('%s: resuming by loading epoch %d\n', mfilename, start) ;
    % [KH] make sure to use the input opts, saved maybe outdated
    opt = net.layers(end).block.opt;
    [net, state, stats] = loadState(modelPath(start)) ;
    net.layers(end).block.opt = opt;
else
    state = [] ;
end

for epoch=start+1:opts.numEpochs

    % Set the random seed based on the epoch and opts.randomSeed.
    % This is important for reproducibility, including when training
    % is restarted from a checkpoint.

    rng(epoch + opts.randomSeed) ;
    prepareGPUs(opts, epoch == start+1) ;

    % Train for one epoch.
    params = opts ;
    params.epoch = epoch ;
    params.learningRate = opts.learningRate(min(epoch, numel(opts.learningRate))) ;
    params.train = opts.train; %(randperm(numel(opts.train))) ; % shuffle
    params.train = params.train; %(1:min(opts.epochSize, numel(opts.train)));
    params.val = opts.val; %(randperm(numel(opts.val))) ;
    params.imdb = imdb ;
    params.getBatch = getBatch ;

    if numel(opts.gpus) <= 1
        [net, state] = processEpoch(net, state, params, 'train') ;
        [net, state] = processEpoch(net, state, params, 'val') ;
        if ~evaluateMode && ~mod(epoch, opts.saveInterval)
            saveState(modelPath(epoch), net, state) ;
        end
        lastStats = state.stats ;
    else
        spmd
            [net, state] = processEpoch(net, state, params, 'train') ;
            [net, state] = processEpoch(net, state, params, 'val') ;
            if labindex == 1 && ~evaluateMode && ~mod(epoch, opts.saveInterval)
                saveState(modelPath(epoch), net, state) ;
            end
            lastStats = state.stats ;
        end
        lastStats = accumulateStats(lastStats) ;
    end

    stats.train(epoch) = lastStats.train ;
    stats.val(epoch) = lastStats.val ;
    clear lastStats ;
    saveStats(modelPath(epoch), stats) ;

    if opts.plotStatistics
        switchFigure(1) ; clf ;
        plots = setdiff(...
            cat(2,...
            fieldnames(stats.train)', ...
            fieldnames(stats.val)'), {'num', 'time'}) ;
        for p = plots
            p = char(p) ;
            values = zeros(0, epoch) ;
            leg = {} ;
            for f = {'train', 'val'}
                f = char(f) ;
                if isfield(stats.(f), p)
                    tmp = [stats.(f).(p)] ;
                    values(end+1,:) = tmp(1,:)' ;
                    leg{end+1} = f ;
                end
            end
            subplot(1,numel(plots),find(strcmp(p,plots))) ;
            plot(1:epoch, values','o-') ;
            xlabel('epoch') ;
            title(p) ;
            legend(leg{:}) ;
            grid on ;
        end
        drawnow ;
        print(1, modelFigPath, '-dpdf') ;
    end

    if ~isempty(opts.postEpochFn)
        if nargout(opts.postEpochFn) == 0
            opts.postEpochFn(net, params, state) ;
        else
            [lr, params] = opts.postEpochFn(net, params, state) ;
            if ~isempty(lr), opts.learningRate = lr; end
            if opts.learningRate == 0, break; end
            opts.train = params.train;
        end
    end
end

% With multiple GPUs, return one copy
if isa(net, 'Composite'), net = net{1} ; end

% -------------------------------------------------------------------------
function [net, state] = processEpoch(net, state, params, mode)
% -------------------------------------------------------------------------
% Note that net is not strictly needed as an output argument as net
% is a handle class. However, this fixes some aliasing issue in the
% spmd caller.

% initialize with momentum 0
if isempty(state) || isempty(state.solverState)
    state.solverState = cell(1, numel(net.params)) ;
    state.solverState(:) = {0} ;
end

% move CNN  to GPU as needed
numGpus = numel(params.gpus) ;
if numGpus >= 1
    net.move('gpu') ;
    for i = 1:numel(state.solverState)
        s = state.solverState{i} ;
        if isnumeric(s)
            state.solverState{i} = gpuArray(s) ;
        elseif isstruct(s)
            state.solverState{i} = structfun(@gpuArray, s, 'UniformOutput', false) ;
        end
    end
end
if numGpus > 1
    parserv = ParameterServer(params.parameterServer) ;
    net.setParameterServer(parserv) ;
else
    parserv = [] ;
end

% profile
if params.profile
    if numGpus <= 1
        profile clear ;
        profile on ;
    else
        mpiprofile reset ;
        mpiprofile on ;
    end
end

num = 0 ;
epoch = params.epoch ;
subset = params.(mode) ;
adjustTime = 0 ;

stats.num = 0 ; % return something even if subset = []
stats.time = 0 ;

if numel(subset) > 0
logInfo('Epoch %d/%d: %s LR=%d', epoch, params.numEpochs, ...
    func2str(params.solver), params.learningRate);
start = tic;  a = tic;
objs = [];
for t=1:params.batchSize:numel(subset)
    batchSize = min(params.batchSize, numel(subset) - t + 1) ;

    for s=1:params.numSubBatches
        % get this image batch and prefetch the next
        batchStart = t + (labindex-1) + (s-1) * numlabs ;
        batchEnd = min(t+params.batchSize-1, numel(subset)) ;
        batch = subset(batchStart : params.numSubBatches * numlabs : batchEnd) ;
        num = num + numel(batch) ;
        if numel(batch) == 0, continue ; end

        inputs = params.getBatch(params.imdb, batch) ;

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

        if strcmp(mode, 'train')
            net.mode = 'normal' ;
            net.conserveMemory = true;
            net.accumulateParamDers = (s ~= 1) ;
            net.eval(inputs, params.derOutputs, 'holdOn', s < params.numSubBatches) ;
        else
            net.mode = 'test' ;
            net.eval(inputs) ;
        end
    end

    % Accumulate gradient.
    if strcmp(mode, 'train')
        if ~isempty(parserv), parserv.sync() ; end
        state = accumulateGradients(net, state, params, batchSize, parserv) ;
    end

    % Get statistics.
    time = toc(start) + adjustTime ;
    batchTime = time - stats.time ;
    stats.num = num ;
    stats.time = time ;
    stats = params.extractStatsFn(stats,net) ;
    currentSpeed = batchSize / batchTime ;
    averageSpeed = (t + batchSize - 1) / time ;
    if t == 3*params.batchSize + 1
        % compensate for the first three iterations, which are outliers
        adjustTime = 4*batchTime - time ;
        stats.time = time + adjustTime ;
    end

    if toc(a) > 30
        fprintf('%s-%s ep%02d: %3d/%3d (%d):', ...
            net.layers(end).block.opt.dataset, mode, epoch, ...
            fix((t-1)/params.batchSize)+1, ceil(numel(subset)/params.batchSize), ...
            params.batchSize) ;
        %fprintf(' %.1f (%.1f) Hz', averageSpeed, currentSpeed) ;
        fprintf(' %.1fHz', averageSpeed) ;
        for f = setdiff(fieldnames(stats)', {'num', 'time'})
            f = char(f) ;
            fprintf(' %s: %.3f', f, stats.(f)) ;
        end
        fprintf('\n') ;
        a = tic;
    end
    objs = [objs, stats.objective];
end
logInfo('Epoch %d, Avg %s obj = %g', epoch, mode, mean(objs(~isnan(objs))));
end  % if N>0

% Save back to state.
state.stats.(mode) = stats ;
if params.profile
    if numGpus <= 1
        state.prof.(mode) = profile('info') ;
        profile off ;
    else
        state.prof.(mode) = mpiprofile('info');
        mpiprofile off ;
    end
end
if ~params.saveSolverState
    state.solverState = [] ;
else
    for i = 1:numel(state.solverState)
        s = state.solverState{i} ;
        if isnumeric(s)
            state.solverState{i} = gather(s) ;
        elseif isstruct(s)
            state.solverState{i} = structfun(@gather, s, 'UniformOutput', false) ;
        end
    end
end

net.reset() ;
net.move('cpu') ;

% -------------------------------------------------------------------------
function state = accumulateGradients(net, state, params, batchSize, parserv)
% -------------------------------------------------------------------------
numGpus = numel(params.gpus) ;
otherGpus = setdiff(1:numGpus, labindex) ;

for p=1:numel(net.params)

    if ~isempty(parserv)
        parDer = parserv.pullWithIndex(p) ;
    else
        parDer = net.params(p).der ;
    end

    switch net.params(p).trainMethod
    case 'average' % mainly for batch normalization
        thisLR = net.params(p).learningRate ;
        net.params(p).value = vl_taccum(...
            1 - thisLR, net.params(p).value, ...
            (thisLR/batchSize/net.params(p).fanout),  parDer) ;

    case 'gradient'
        thisDecay = params.weightDecay * net.params(p).weightDecay ;
        thisLR = params.learningRate * net.params(p).learningRate ;

        if thisLR>0 || thisDecay>0
            % Normalize gradient and incorporate weight decay.
            try
            parDer = vl_taccum(1/batchSize, parDer, ...
                thisDecay, net.params(p).value) ;
        catch, net.params(p), keyboard, end

            if isempty(params.solver)
                % Default solver is the optimised SGD.
                % Update momentum.
                state.solverState{p} = vl_taccum(...
                    params.momentum, state.solverState{p}, ...
                    -1, parDer) ;

                % Nesterov update (aka one step ahead).
                if params.nesterovUpdate
                    delta = params.momentum * state.solverState{p} - parDer ;
                else
                    delta = state.solverState{p} ;
                end

                % Update parameters.
                net.params(p).value = vl_taccum(...
                    1,  net.params(p).value, thisLR, delta) ;

            else
                % call solver function to update weights
                [net.params(p).value, state.solverState{p}] = ...
                    params.solver(net.params(p).value, state.solverState{p}, ...
                    parDer, params.solverOpts, thisLR) ;
            end
        end
    otherwise
        error('Unknown training method ''%s'' for parameter ''%s''.', ...
            net.params(p).trainMethod, ...
            net.params(p).name) ;
    end
end

% -------------------------------------------------------------------------
function stats = accumulateStats(stats_)
% -------------------------------------------------------------------------
for s = {'train', 'val'}
    s = char(s) ;
    total = 0 ;

    % initialize stats stucture with same fields and same order as
    % stats_{1}
    stats__ = stats_{1} ;
    names = fieldnames(stats__.(s))' ;
    values = zeros(1, numel(names)) ;
    fields = cat(1, names, num2cell(values)) ;
    stats.(s) = struct(fields{:}) ;

    for g = 1:numel(stats_)
        stats__ = stats_{g} ;
        num__ = stats__.(s).num ;
        total = total + num__ ;

        for f = setdiff(fieldnames(stats__.(s))', 'num')
            f = char(f) ;
            stats.(s).(f) = stats.(s).(f) + stats__.(s).(f) * num__ ;

            if g == numel(stats_)
                stats.(s).(f) = stats.(s).(f) / total ;
            end
        end
    end
    stats.(s).num = total ;
end

% -------------------------------------------------------------------------
function stats = extractStats(stats, net)
% -------------------------------------------------------------------------
sel = find(cellfun(@(x) isa(x,'dagnn.Loss'), {net.layers.block})) ;
for i = 1:numel(sel)
    if net.layers(sel(i)).block.ignoreAverage, continue; end;
    stats.(net.layers(sel(i)).outputs{1}) = net.layers(sel(i)).block.average ;
end

% -------------------------------------------------------------------------
function saveState(fileName, net_, state)
% -------------------------------------------------------------------------
net = net_.saveobj() ;
save(fileName, 'net', 'state') ;
logInfo('saved: %s', fileName);

% -------------------------------------------------------------------------
function saveStats(fileName, stats)
% -------------------------------------------------------------------------
if exist(fileName)
    try, save(fileName, 'stats', '-append') ; end
else
    try, save(fileName, 'stats') ; end
end

% -------------------------------------------------------------------------
function [net, state, stats] = loadState(fileName)
% -------------------------------------------------------------------------
load(fileName, 'net', 'state', 'stats') ;
net = dagnn.DagNN.loadobj(net) ;
if isempty(whos('stats'))
    warning('Epoch ''%s'' was only partially saved. Delete this file and try again.', ...
        fileName) ;
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 switchFigure(n)
% -------------------------------------------------------------------------
if get(0,'CurrentFigure') ~= n
    try
        set(0,'CurrentFigure',n) ;
    catch
        figure(n) ;
    end
end

% -------------------------------------------------------------------------
function clearMex()
% -------------------------------------------------------------------------
clear vl_tmove vl_imreadjpeg ;

% -------------------------------------------------------------------------
function prepareGPUs(opts, cold)
% -------------------------------------------------------------------------
numGpus = numel(opts.gpus) ;
if numGpus > 1
    % check parallel pool integrity as it could have timed out
    pool = gcp('nocreate') ;
    if ~isempty(pool) && pool.NumWorkers ~= numGpus
        delete(pool) ;
    end
    pool = gcp('nocreate') ;
    if isempty(pool)
        parpool('local', numGpus) ;
        cold = true ;
    end

end
if numGpus >= 1 && cold
    fprintf('%s: resetting GPU\n', mfilename)
    clearMex() ;
    if numGpus == 1
        gpuDevice(opts.gpus)
    else
        spmd
            clearMex() ;
            gpuDevice(opts.gpus(labindex))
        end
    end
end


function [inside, outside] = find_inside_vol(pos, vol);

% FIND_INSIDE_VOL locates dipole locations inside/outside the source
% compartment of a volume conductor model.
%
% [inside, outside] = find_inside_vol(pos, vol)
%
% This function is obsolete and its use in other functions should be replaced
% by inside_vol

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

inside  = ft_inside_vol(pos, vol);
% replace boolean vector with indexing vectors
outside = find(~inside);
inside  = find(inside);


% go to every subject in the BA44-45 list
% load each fiber group in the BA44-45\dissections dir
% transpose  fg.fibers
% save as .._fixed.mat

baseDir = 'U:\data\reading_longitude\dti_adults\';
subDirs = {'ab050307','as050307','aw040809','bw040806','da050311','gm050308',...
        'jl040902','ka040923','mbs040503', 'me050126', 'mz040828',...
        'pp050208', 'rd040630','sn040831','sp050303'};
fiberDir = 'fibers\BA44-45\dissections\';

for ii = 1:length(subDirs)
    workDir = fullfile(baseDir,subDirs{ii},fiberDir);
    cd(workDir);
    d = dir('?BA*.mat');
    fileNames = {d.name};
    for jj = 1:length(fileNames)
        curFileName = fileNames{jj};
        load(curFileName);
        fg.fibers = fg.fibers';
        newFileName = [curFileName(1:end-4) '_fixed.mat'];
        save(newFileName,'fg','versionNum','coordinateSpace');
        clear fg versionNum coordinateSpace
    end
end


function val = get( f, propName )
%GET GET method for BALLFUN class.
%   P = GET(F, PROP) returns the property P specified in the string PROP from
%   the BALLFUN F. Valid entries for the string PROP are:
%    'coeffs', 'isReal', 'domain'

% Copyright 2019 by The University of Oxford and The Chebfun Developers.
% See http://www.chebfun.org/ for Chebfun information.

% Get the properties.
switch ( propName )
    case 'coeffs'
        val = f.coeffs;
    case 'isReal'
        val = f.isReal;
    case 'domain'
        val = f.domain;
otherwise
        error('CHEBFUN:BALLFUN:get:propName', ...
            [propName,' is not a valid BALLFUN property.'])
end


function r = initialGuess(i_vars, stateGuess, algvarGuess)
ig = ocl.Variable.create(i_vars, 0);
ig.states.set(stateGuess);
ig.algvars.set(algvarGuess);
r = ig.value;


% -*- INTERNAL UNDOCUMENTED FUNCTION -*-
function [geometry, msh, space_v, vel, space_p, press] = ...
                          solve_stokes_2d (problem_data, method_data)

warning ('geopdes:obsolete', 'Function SOLVE_STOKES_2D is obsolete. Using SOLVE_STOKES instead')

[geometry, msh, space_v, vel, space_p, press] = solve_stokes (problem_data, method_data);


%kcparallel_signature 'Create a signature for a class for Parallelepiped classification '
% This MatLab function was automatically generated by a converter (KhorosToMatLab) from the Khoros cparallel_signature.pane file
%
% Parameters:
% InputFile: isample 'Input Sample', required: 'input image file with the sample'
% OutputFile: osig 'Output Signature', required: 'output file with signature'
% Toggle: useasis 'Use data as-is for bounds extraction', default: 0: 'if TRUE will use data as is for bounds extraction'
% Integer: usethreshold 'Use a threshold for bounds extraction', default: 0: 'if chosen will use a threshold for bounds extraction'
%
% Example: osig = kcparallel_signature(isample, {'isample','';'osig','';'useasis',0;'usethreshold',0})
%
% Khoros helpfile follows below:
%
%  PROGRAM
% cparallel_signature - Create a signature for a class for Parallelepiped classification
%
%  DESCRIPTION
% This routine creates a signature for the Parallelepiped classifier from a ROI or sample image (passed with the [-isample] parameter). Output will be written to the file specified with the [-osig] parameter.
% Signatures for the Parallelepiped classifier consist of two values per feature denoting a lower and upper threshold that determine if a pixel belongs or not to that class.
% Determination of these thresholds can be done in three modes:
% 1- With the parameter [-useasis], in this case the region will be scanned and for each element the smallest and larger values for each element will be used as signatures;
% 2- With the parameter [-usethreshold], a threshold will be used to filter the minimum values of each element - this is usually done to avoid using minimum values that are not representatives of the classes.
% 3- With the parameter [-usestddevs], a number of standard deviations will be used with the averages for each element to determine the minimum and maximum values of the signature for that element.
% The dimensions of the signature for the Parallelepiped classifier will be Ex2x1x1x1 where E is the number of element dimensions in the input data object. Since the signature has a fixed size, it should be appended in blocks with the "kappend" operator for classification with the \fIcparallel_classify\fP operator.
% Signature contents can be printed in tables with the "cparallel_printsig" operator.
%
%
%
%  EXAMPLES
% All examples for the Classify toolbox are listed on the Classify Toolbox Manual. For an example of this operator, please see the example workspace Classify:workspaces:PARALLEL.
%
%  "SEE ALSO"
% cparallel_printsig, cparallel_classify, kappend. See also the Classify Toolbox Manual for details about the Parallelepiped Classification Algorithm.
%
%  RESTRICTIONS
%
%  REFERENCES
% All references for the Classify toolbox are listed on the Classify Toolbox Manual.
%
%  COPYRIGHT
% Copyright (C) 1997 Rafael Santos. Khoros (C) Khoral Research, Inc.
%

function varargout = kcparallel_signature(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,..] = kcparallel_signature(Inputs,arglist).');
end
if size(arglist,2)~=2
  error('arglist must be of form {''ParameterTag1'',value1;''ParameterTag2'',value2}')
 end
narglist={'isample', '__input';'osig', '__output';'useasis', 0;'usethreshold', 0};
maxval={0,0,0,0};
minval={0,0,0,0};
istoggle=[0,0,1,1];
was_set=istoggle * 0;
paramtype={'InputFile','OutputFile','Toggle','Integer'};
% 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 'cparallel_signature"  '],Inputs,narglist);


% STD_BUILDDESIGNMAT - Build the design matrix for an specific design
% specified in the structure ''design'' provided as input
%
% Usage:
%
% [tmpdmat,colLabels,catflag] = std_builddesignmat(design, trialinfo, 1)
%
% Inputs:
%  design      - Design structure as in the STUDY
%  trialinfo   - Structure of trial information. Each field should be a
%                cell array with one element for each trial
% expanding    - Expand the design matrix
% Optional inputs:
%
% Outputs:
%   tmpdmat    - Design matrix
%   colLabels  - Labels for each column of the design matrix
%   catflag    - Binary vector with dimension equal to the number of columns in the design matrix.
%                [0] mean a continuous regressor, [1] means a categotical one.
%
% See also: std_combtrialinfo , std_plodtmat
%
% Authors: Ramon Martinez-Cancino
%
% Copyright (C) 2015  Ramon Martinez-Cancino, UCSD, INC, SCCN
%
% This file is part of EEGLAB, see http://www.eeglab.org
% for the documentation and details.
%
% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are met:
%
% 1. Redistributions of source code must retain the above copyright notice,
% this list of conditions and the following disclaimer.
%
% 2. Redistributions in binary form must reproduce the above copyright notice,
% this list of conditions and the following disclaimer in the documentation
% and/or other materials provided with the distribution.
%
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
% THE POSSIBILITY OF SUCH DAMAGE.

function [tmpdmat,colLabels,catflag] = std_builddesignmat(design, trialinfo, expanding)

if nargin < 3, expanding = 0; end
ntrials = length(trialinfo);
varindx = 1:length(design.variable);

% checking if 'group' var (temporal commit to detect group vars, right now only detecting variable 'group')
groupindx = find(strcmp({design.variable.label},'group'));
if ~isempty(groupindx)
    varindx(groupindx) = [];
end

tmpdmat   = NaN(ntrials,length(varindx));
catflag   = strcmp({design.variable.vartype}, 'categorical');
colLabels = {design.variable.label};

for i = 1 : length(varindx)
     % case for cont variables
    if ~catflag(i)
         [trialindsx, eventvals] = std_gettrialsind(trialinfo,design.variable(varindx(i)).label, '');
         if ~isempty(trialindsx)
             tmpdmat(trialindsx,i) = eventvals;
         end
    else % case for cat variables
        varvaluetmp = design.variable(varindx(i)).value;

        % Expanding cells cells
        c = 1; varlist = {}; varvallength = []; facval_indx = []; dmatval = 1; jcount = 1;
        for ivar = 1:length(varvaluetmp)

            % Length of varvaluetmp(i)
            if iscellstr(varvaluetmp(ivar))
                varvallength(ivar) = length(varvaluetmp(ivar)) ;
            elseif iscell(varvaluetmp(ivar))
                varvallength(ivar) = length(varvaluetmp{ivar}) ;
            else
                varvallength(ivar) = length(varvaluetmp{ivar}) ;
            end
            % Retrieving value and assigning index in design matrix
            if ~iscell(varvaluetmp{ivar})  && varvallength(ivar) == 1
                varlist{c} = varvaluetmp{ivar};
                varindxjoint{ivar} = c;
                facval_indx(c) = dmatval;
                c = c+1;
            else
                tmpcindx = [];
                for ival = 1: varvallength(ivar)
                    if iscellstr(varvaluetmp{ivar}(ival))
                        varlist{c} =  varvaluetmp{ivar}{ival};
                    else
                        varlist{c} =  varvaluetmp{ivar}(ival);
                    end
                    facval_indx(c) = dmatval;
                    tmpcindx       = cat(1,tmpcindx,c);
                    c = c+1;
                end
                varindxjoint{ivar} = tmpcindx;
            end
            dmatval = dmatval+1;
            %---
            for j = 1 :varvallength(ivar)
                %
                if iscellstr(varlist(varindxjoint{ivar}))
                    facval = varlist{varindxjoint{ivar}(j)};
                elseif iscell(varlist(varindxjoint{ivar}))
                    tmpval = varlist(varindxjoint{ivar});
                    if isnumeric(tmpval{j})
                        facval = tmpval{j};
                    else
                        facval = tmpval(j);
                    end
                elseif  isnumeric(varlist(varindxjoint{ivar}))
                    facval = varlist(varindxjoint{ivar}(j));
                elseif ischar(varlist(varindxjoint{ivar}))
                    %                     facval = varlist(varindxjoint{ivar}(j));
                end
                % Find indices of triasl for facval
                [trialindsx, eventvals] = std_gettrialsind(trialinfo,design.variable(varindx(i)).label, facval);

                % Populating the design matrix
                if ~isempty(trialindsx)
                    tmpdmat(trialindsx,i) = facval_indx(jcount);
                    jcount = jcount+1;
                end
            end
            %---
        end
    end
end
% -------------------------------------------------------------------------
% expand categ var
if expanding == 1
    % count number of columns
    nCols = 0;
    for iCol = 1:size(tmpdmat,2)
        if catflag(iCol)
             nCols = nCols+length(design.variable(varindx(iCol)).value);
        else
            nCols = nCols+1;
        end
    end

    tmpdmatExpanded = NaN(size(tmpdmat,1),nCols);
    countCol = 0;
    for iCol = 1:size(tmpdmat,2)
        if catflag(iCol)
            % get unique values for this given categ var
            uniqueVals = unique(tmpdmat(:,iCol));
            uniqueVals(isnan(uniqueVals)) = [];

            % scan unique values
            prevCol = countCol;
            for iUnique = 1:length(design.variable(varindx(iCol)).value)
                countCol    = countCol+1;
                try tmpval = uniqueVals(iUnique); catch, tmpval = NaN; end
                trialSelect = tmpdmat(:,iCol) == tmpval;
                otherCols = prevCol+setdiff(1:length(uniqueVals), iUnique);
                % putting 0 in other columns ensures we process the NaNs
                tmpdmatExpanded(trialSelect, countCol ) = 1;
                tmpdmatExpanded(trialSelect, otherCols) = 0;

                % get the label for that column
                if ischar(design.variable(varindx(iCol)).value{iUnique})
                    colLabels{countCol} = [design.variable(varindx(iCol)).label '-' design.variable(varindx(iCol)).value{iUnique}];
                elseif iscell(design.variable(varindx(iCol)).value{iUnique})
                    % Concat all vals
                    varnametmp = design.variable(varindx(iCol)).value{iUnique}{1};
                    for ivar = 2: length(design.variable(varindx(iCol)).value{iUnique})
                        varnametmp = [varnametmp '&' design.variable(varindx(iCol)).value{iUnique}{ivar}];
                    end
                    colLabels{countCol} = [design.variable(varindx(iCol)).label '-' varnametmp];
                elseif isnumeric(design.variable(varindx(iCol)).value{iUnique})
                    colLabels{countCol} = [design.variable(varindx(iCol)).label '-' int2str(design.variable(varindx(iCol)).value{iUnique})];
                end
            end
        else
            countCol = countCol+1;
            colLabels{countCol} = design.variable(varindx(iCol)).label;
            tmpdmatExpanded(:,countCol) = tmpdmat(:,iCol);
        end
    end

    tmpdmat = tmpdmatExpanded;
end

tmpdmat(:,end+1) = 1;
colLabels{numel(colLabels)+1}  = 'constant';


function [ output_meta ] = meta2sicd_nisar( HDF5_fid )
%META2SICD_NISAR Converts NISAR SLC HDF5 into a SICD-style metadata structure
%
% Takes as input a file identifier to an open HDF5 file-- as is returned by
% H5F.open
%
% Note that MATLAB's h5disp is generally a good tool for manually browsing
% through HDF5 metadata like that found in NISAR format.
%
% Known issues with simulated NISAR data (from UAVSAR) to date:
%
% 1) ISCEVersion not populated
% 2) Doppler rate provided is positive.  JPL says will it fix in future
% update.
% 3) processedAzimuthBandwidth is acknowledged by JPL to be wrong and will
% be fixed in a future update.
% 4) Weighting description is currently a placeholder, not an accurate
% description of UAVSAR data used for simulated data foramt.
%
% Written by: Wade Schwartzkopf, NGA/Research
%
% //////////////////////////////////////////
% /// CLASSIFICATION: UNCLASSIFIED       ///
% //////////////////////////////////////////

SECONDS_IN_A_DAY = 24*60*60;

% Query for frequencies and polarization (each of which would create its
% own SICD)
freqs_id = H5D.open(HDF5_fid,'/science/LSAR/identification/listOfFrequencies');
freqs = H5D.read(freqs_id);
freqs = freqs(1,:);
H5D.close(freqs_id);
for i=1:numel(freqs)
    pol_id = H5D.open(HDF5_fid,['/science/LSAR/SLC/swaths/frequency' freqs(i) '/listOfPolarizations']);
    pols{i} = H5D.read(pol_id)';
    H5D.close(pol_id);
end

%% CollectionInfo
% CollectorName could also be deblank(get_hdf_data(HDF5_fid,'/science/LSAR/identification','missionId')')
output_meta.CollectionInfo.CollectorName=get_hdf_attribute(HDF5_fid,'/','mission_name');
% JPL suggested this as best way to form unique string for each collection
output_meta.CollectionInfo.CoreName=[...
    num2str(get_hdf_data(HDF5_fid,'/science/LSAR/identification','absoluteOrbitNumber'),'%07u') ...
    get_hdf_data(HDF5_fid,'/science/LSAR/identification','trackNumber')'];
output_meta.CollectionInfo.CollectType='MONOSTATIC';
output_meta.CollectionInfo.RadarMode.ModeType='STRIPMAP';
output_meta.CollectionInfo.Classification='UNCLASSIFIED';

%% ImageCreation
try
    % TODO: UNTESTED
    % Sample data did not have valid values for this
    output_meta.ImageCreation.Application=['ISCE ' ...
        deblank(get_hdf_data(HDF5_fid,'/science/LSAR/SLC/metadata/processingInformation/algorithms','ISCEVersion')')];
end
output_meta.ImageCreation.Profile='Prototype';

%% ImageData
output_meta.ImageData=struct(); % Just a placeholder
% Most subfields added below in "per band" section
% Used for computing SCP later

%% GeoData
output_meta.GeoData.EarthModel='WGS_84';
% Initially, we just seed this with a very rough value.  Later we will
% put in something more precise.
poly_str = get_hdf_data(HDF5_fid,'/science/LSAR/identification','boundingPolygon')';
poly_str(isletter(poly_str)|poly_str=='('|poly_str==')')='';
poly_str(poly_str==',')=';';
bound_poly=str2num(poly_str);
bound_poly=bound_poly(1:(end-1),:);
rough_center = mean(bound_poly);
output_meta.GeoData.SCP.LLH.Lat=rough_center(2);
output_meta.GeoData.SCP.LLH.Lon=rough_center(1);
output_meta.GeoData.SCP.LLH.HAE=...
    mean(get_hdf_data(HDF5_fid,'/science/LSAR/SLC/metadata/processingInformation/parameters/','referenceTerrainHeight'));
% NISAR stores this as single, which is sufficient for storage, but we need
% double precision for some of the computations later on, to include
% geopositioning of exact SCP.
output_meta.GeoData.SCP.LLH.HAE=double(output_meta.GeoData.SCP.LLH.HAE);
ecf=geodetic_to_ecf([output_meta.GeoData.SCP.LLH.Lat output_meta.GeoData.SCP.LLH.Lon output_meta.GeoData.SCP.LLH.HAE]);
output_meta.GeoData.SCP.ECF.X=ecf(1);
output_meta.GeoData.SCP.ECF.Y=ecf(2);
output_meta.GeoData.SCP.ECF.Z=ecf(3);
% Calling derived_sicd_fields at the end will populate these fields
% with the sensor model, so we don't need to do it here.
% latlon=get_hdf_attribute(dset_id(i),'Top Left Geodetic Coordinates');
% output_meta.GeoData.ImageCorners.ICP.FRFC.Lat=bound_poly(1,1);
% output_meta.GeoData.ImageCorners.ICP.FRFC.Lon=bound_poly(1,2);
% latlon=get_hdf_attribute(dset_id(i),'Bottom Left Geodetic Coordinates');
% output_meta.GeoData.ImageCorners.ICP.FRLC.Lat=bound_poly(2,1);
% output_meta.GeoData.ImageCorners.ICP.FRLC.Lon=bound_poly(2,2);
% latlon=get_hdf_attribute(dset_id(i),'Bottom Right Geodetic Coordinates');
% output_meta.GeoData.ImageCorners.ICP.LRLC.Lat=bound_poly(3,1);
% output_meta.GeoData.ImageCorners.ICP.LRLC.Lon=bound_poly(3,2);
% latlon=get_hdf_attribute(dset_id(i),'Top Right Geodetic Coordinates');
% output_meta.GeoData.ImageCorners.ICP.LRFC.Lat=bound_poly(4,1);
% output_meta.GeoData.ImageCorners.ICP.LRFC.Lon=bound_poly(4,2);

%% Grid
output_meta.Grid.ImagePlane='SLANT';
output_meta.Grid.Type='RGZERO';
output_meta.Grid.Row.Sgn=-1; % Always true for NISAR
output_meta.Grid.Col.Sgn=-1; % Always true for NISAR
output_meta.Grid.Col.KCtr=0;
output_meta.Grid.Row.DeltaKCOAPoly=0;
% TODO: JPL states that uniform weighting in data simulated from UAVSAR is
% placeholder, not an accurate description of the data.  At this point, it
% is not clear what the final weighting description for NISAR will be.
output_meta.Grid.Row.WgtFunct = get_hdf_data(HDF5_fid,...
    '/science/LSAR/SLC/metadata/processingInformation/parameters','rangeChirpWeighting');
if all(output_meta.Grid.Row.WgtFunct(1)==output_meta.Grid.Row.WgtFunct)
    output_meta.Grid.Row.WgtType.WindowName = 'UNIFORM'; % In the sample datasets
else
    output_meta.Grid.Row.WgtType.WindowName = 'UNKNOWN';
end
output_meta.Grid.Col.WgtFunct = get_hdf_data(HDF5_fid,...
    '/science/LSAR/SLC/metadata/processingInformation/parameters','azimuthChirpWeighting');
if all(output_meta.Grid.Col.WgtFunct(1)==output_meta.Grid.Col.WgtFunct)
    output_meta.Grid.Col.WgtType.WindowName = 'UNIFORM'; % In the sample datasets
else
    output_meta.Grid.Col.WgtType.WindowName = 'UNKNOWN';
end
% More subfields added below in "per band" section

%% Timeline
% Using zero Doppler times.  Pulse transmission likely started before this
[collectStart, collectStartFrac]=datenum_w_frac(...
    deblank(get_hdf_data(HDF5_fid, '/science/LSAR/identification', ...
    'zeroDopplerStartTime')'));
[collectEnd, collectEndFrac]=datenum_w_frac(...
    deblank(get_hdf_data(HDF5_fid, '/science/LSAR/identification', ...
    'zeroDopplerEndTime')'));
output_meta.Timeline.CollectStart=collectStart + (collectStartFrac/SECONDS_IN_A_DAY);
output_meta.Timeline.CollectDuration=...
    round((collectEnd-collectStart)*SECONDS_IN_A_DAY) + ... % Convert days to seconds
    (collectEndFrac-collectStartFrac); % Handle fractional seconds
output_meta.Timeline.IPP.Set.TStart=0;
output_meta.Timeline.IPP.Set.TEnd=0; % Apply real value later.  Just a placeholder.
output_meta.Timeline.IPP.Set.IPPStart=uint32(0);
% More subfields added below in "per band" section

%% Position
% Compute polynomial from state vectors
% Get reference time
ref_time = get_ref_time(HDF5_fid,'/science/LSAR/SLC/metadata/orbit/time');
% Times in SICD are with respect to time from start of collect.
ref_time_offset = round((ref_time-collectStart)*SECONDS_IN_A_DAY) + ... % Convert from days to secs
    collectStartFrac; % Handle fractional seconds
state_vector_T  = get_hdf_data(HDF5_fid,'/science/LSAR/SLC/metadata/orbit','time')';
state_vector_T = state_vector_T + ref_time_offset; % Make with respect to Timeline.CollectStart
state_vector_pos  = get_hdf_data(HDF5_fid,'/science/LSAR/SLC/metadata/orbit','position');
% sv2poly.m shows ways to determine best polynomial order, but we just use 6th here
polyorder=min(6, numel(state_vector_T) - 1);
old_state = warning('off','MATLAB:polyfit:RepeatedPointsOrRescale');
P_x  = polyfit(state_vector_T, state_vector_pos(1,:), polyorder);
P_y  = polyfit(state_vector_T, state_vector_pos(2,:), polyorder);
P_z  = polyfit(state_vector_T, state_vector_pos(3,:), polyorder);
% We don't use these since they are derivable from the position polynomial
% state_vector_vel = get_hdf_data(HDF5_fid,'/science/LSAR/SLC/metadata/orbit','velocity');
% state_vector_acc = get_hdf_data(HDF5_fid,'/science/LSAR/SLC/metadata/orbit','acceleration');
% P_vx = polyfit(state_vector_T, state_vector_vel(1,:), polyorder);
% P_vy = polyfit(state_vector_T, state_vector_vel(2,:), polyorder);
% P_vz = polyfit(state_vector_T, state_vector_vel(3,:), polyorder);
% P_ax = polyfit(state_vector_T, state_vector_acc(1,:), polyorder);
% P_ay = polyfit(state_vector_T, state_vector_acc(2,:), polyorder);
% P_az = polyfit(state_vector_T, state_vector_acc(3,:), polyorder);
warning(old_state);
% Store position polynomial
output_meta.Position.ARPPoly.X  = P_x(end:-1:1).';
output_meta.Position.ARPPoly.Y  = P_y(end:-1:1).';
output_meta.Position.ARPPoly.Z  = P_z(end:-1:1).';

%% SCPCOA
% This should almost always be left looking for NISAR.
output_meta.SCPCOA.SideOfTrack=get_hdf_data(HDF5_fid,'/science/LSAR/identification','lookDirection');
output_meta.SCPCOA.SideOfTrack=upper(output_meta.SCPCOA.SideOfTrack(1));
% Most subfields added below in "per band" section.

%% ImageFormation
output_meta.ImageFormation.RcvChanProc=struct('NumChanProc',1,'PRFScaleFactor',1);
output_meta.ImageFormation.ImageFormAlgo='RMA';
output_meta.ImageFormation.TStartProc=0;
output_meta.ImageFormation.TEndProc=output_meta.Timeline.CollectDuration;
output_meta.ImageFormation.STBeamComp='NO';
output_meta.ImageFormation.ImageBeamComp='SV';
output_meta.ImageFormation.AzAutofocus='NO';
output_meta.ImageFormation.RgAutofocus='NO';
% More subfields added below in "per band" section
output_meta.RMA.RMAlgoType='OMEGA_K';
output_meta.RMA.ImageType='INCA';
output_meta.RMA.INCA.DopCentroidCOA=true;
% Get reference time
ref_time = get_ref_time(HDF5_fid,'/science/LSAR/SLC/swaths/zeroDopplerTime');
% Times in SICD are with respect to time from start of collect.
ref_time_offset = round((ref_time-collectStart)*SECONDS_IN_A_DAY) + ... % Convert from days to secs
    collectStartFrac; % Handle fractional seconds
zd_t = get_hdf_data(HDF5_fid,'/science/LSAR/SLC/swaths','zeroDopplerTime') + ref_time_offset;
ss_az_s = get_hdf_data(HDF5_fid,'/science/LSAR/SLC/swaths','zeroDopplerTimeSpacing');
if output_meta.SCPCOA.SideOfTrack(1)=='L'
    zd_t = zd_t(end:-1:1);
    ss_az_s = -ss_az_s;
end
grid_r = get_hdf_data(HDF5_fid,'/science/LSAR/SLC/metadata/processingInformation/parameters','slantRange');
% Get reference time
ref_time = get_ref_time(HDF5_fid,'/science/LSAR/SLC/metadata/processingInformation/parameters/zeroDopplerTime');
% Times in SICD are with respect to time from start of collect.
ref_time_offset = round((ref_time-collectStart)*SECONDS_IN_A_DAY) + ... % Convert from days to secs
    collectStartFrac; % Handle fractional seconds
grid_zd = get_hdf_data(HDF5_fid,'/science/LSAR/SLC/metadata/processingInformation/parameters','zeroDopplerTime') + ref_time_offset;

%% Radiometric
cal_fields = {'beta0','gamma0','sigma0'};
sicd_cal_fields = {'BetaZeroSFPoly','GammaZeroSFPoly','SigmaZeroSFPoly'};
cal_data = cell(3,1);  % beta0, gamma0, sigma0
for i = 1:numel(cal_fields)
    cal_data{i} = get_hdf_data(HDF5_fid, '/science/LSAR/SLC/metadata/calibrationInformation/geometry',cal_fields{i}).';
    % Get fill value
    gid = H5G.open(HDF5_fid, '/science/LSAR/SLC/metadata/calibrationInformation/geometry/');
    data = H5D.open(gid, cal_fields{i});
    H5G.close(gid);
    attribute = H5A.open(data, '_FillValue');
    H5D.close(data);
    fill = H5A.read(attribute,H5A.get_type(attribute));
    H5A.close(attribute);
    fill = cast(fill,class(cal_data{i}));
    cal_data{i}(cal_data{i}==fill) = NaN;
    if any(diff(cal_data{i}(isfinite(cal_data{i}))))
        % We expect NISAR to be constant beta, although simulated data from
        % UAVSAR is constant sigma
        if strcmp(cal_fields{i},'beta0')
            warning('META2SICD_NISAR:unexpectedCalibrationValues',...
                'Beta-0 values expected to be constant.');
        end
    else
        output_meta.Radiometric.(sicd_cal_fields{i}) = mean(cal_data{i}(:),'omitnan');
    end
    % We will derive non-constant Radiometric fields from the constant one
    % (likely beta).
end
output_meta.Radiometric.NoiseLevel.NoiseLevelType = 'ABSOLUTE';

%% Process fields specific to each polarimetric band
band_independent_meta=output_meta; % Values that are consistent across all bands
grouped_meta=cell(sum(cellfun(@(x) size(x,1),pols)),1);
meta_i=0;
for i=1:numel(freqs)
    freq_meta = band_independent_meta;

    freq_id=H5G.open(HDF5_fid,['/science/LSAR/SLC/swaths/frequency' freqs(i)]);

    %% Grid
    row_ss_id=H5D.open(freq_id,'slantRangeSpacing');
    freq_meta.Grid.Row.SS=H5D.read(row_ss_id);
    H5D.close(row_ss_id);
    % Col.SS is derived after DRateSFPoly below, rather than used from this
    % given field, so that SICD metadata can be internally consistent:
    % col_ss_id=H5D.open(freq_id,'sceneCenterAlongTrackSpacing');
    % output_meta.Grid.Col.SS=H5D.read(col_ss_id);
    % H5D.close(col_ss_id);
    proc_bw_id=H5D.open(freq_id,'processedRangeBandwidth');
    freq_meta.Grid.Row.ImpRespBW=2*H5D.read(proc_bw_id)/SPEED_OF_LIGHT;
    H5D.close(proc_bw_id);
    freq_meta.Grid.Row.DeltaK1=-freq_meta.Grid.Row.ImpRespBW/2;
    freq_meta.Grid.Row.DeltaK2=-freq_meta.Grid.Row.DeltaK1;

    %% Timeline
    prf_id=H5D.open(freq_id,'nominalAcquisitionPRF');
    prf=H5D.read(prf_id);
    H5D.close(prf_id);
    freq_meta.Timeline.IPP.Set.IPPEnd=uint32(floor(prf*band_independent_meta.Timeline.CollectDuration));
    freq_meta.Timeline.IPP.Set.IPPPoly=[0; prf];
    freq_meta.Timeline.IPP.Set.TEnd=freq_meta.Timeline.CollectDuration;

    %% RadarCollection
    fc_id=H5D.open(freq_id,'acquiredCenterFrequency');
    fc=H5D.read(fc_id);
    H5D.close(fc_id);
    bw_id=H5D.open(freq_id,'acquiredRangeBandwidth');
    bw=H5D.read(bw_id);
    H5D.close(bw_id);
    freq_meta.RadarCollection.TxFrequency.Min=fc-(bw/2);
    freq_meta.RadarCollection.TxFrequency.Max=fc+(bw/2);
    % No waveform parameters provided
    for j=1:size(pols{i},1)
        freq_meta.RadarCollection.RcvChannels.ChanParameters(j).TxRcvPolarization=[pols{i}(j,1) ':' pols{i}(j,2)];
    end
    tx_pol = unique(pols{i}(:,1));
    if numel(tx_pol)==1
        freq_meta.RadarCollection.TxPolarization = tx_pol;
    else
        freq_meta.RadarCollection.TxPolarization = 'SEQUENCE';
        for j = 1:numel(tx_pol)
            freq_meta.RadarCollection.TxSequence.TxStep(j).WFIndex = j;
            freq_meta.RadarCollection.TxSequence.TxStep(j).TxPolarization = tx_pol(j);
        end
    end

    %% ImageFormation
    fc_id=H5D.open(freq_id,'processedCenterFrequency');
    fc=H5D.read(fc_id);
    H5D.close(fc_id);
    bw_id=H5D.open(freq_id,'processedRangeBandwidth');
    bw=H5D.read(bw_id);
    H5D.close(bw_id);
    freq_meta.ImageFormation.TxFrequencyProc.MinProc=fc-(bw/2);
    freq_meta.ImageFormation.TxFrequencyProc.MaxProc=fc+(bw/2);
    range_id=H5D.open(freq_id,'slantRange');
    r_ca_sampled=H5D.read(range_id);
    H5D.close(range_id);
    % TODO: processedAzimuthBandwidth acknowledged by JPL to be wrong in
    % simulated datasets.
    dop_bw_id=H5D.open(freq_id,'processedAzimuthBandwidth');
    dop_bw=H5D.read(dop_bw_id);
    H5D.close(dop_bw_id);
    H5G.close(freq_id);
    dopcentroid_sampled = get_hdf_data(HDF5_fid,[...
        '/science/LSAR/SLC/metadata/processingInformation/parameters/frequency' freqs(i)], ...
        'dopplerCentroid').';
    doprate_sampled = get_hdf_data(HDF5_fid,[...
        '/science/LSAR/SLC/metadata/processingInformation/parameters/frequency' freqs(i)], ...
        'azimuthFMRate').';

    for j=1:size(pols{i},1)
        pol_meta=freq_meta;

        %% ImageData
        dset_id=H5D.open(HDF5_fid,['/science/LSAR/SLC/swaths/frequency' freqs(i) '/' pols{i}(j,:)]);
        dspace_id=H5D.get_space(dset_id);
        [~, datasize] = H5S.get_simple_extent_dims(dspace_id);
        pol_meta.ImageData.NumCols=uint32(datasize(1));
        pol_meta.ImageData.NumRows=uint32(datasize(2));
        pol_meta.ImageData.FullImage=pol_meta.ImageData;
        if (H5T.get_class(H5D.get_type(dset_id)) == H5ML.get_constant_value('H5T_COMPOUND'))
            if (H5T.get_member_class(H5D.get_type(dset_id),0) == H5ML.get_constant_value('H5T_FLOAT'))
                % Could be either 16- or 32-bit float.  Either way it will
                % cast to this SICD type.
                pol_meta.ImageData.PixelType='RE32F_IM32F';
            elseif (H5T.get_member_class(H5D.get_type(dset_id),0) == H5ML.get_constant_value('H5T_INTEGER')) && ...
                    H5T.get_size(H5D.get_type(dset_id)) == 4
                pol_meta.ImageData.PixelType='RE16I_IM16I';
            else
                error('META2SICD_NISAR:unrecognizedDataType','Must be 16- or 32-bit complex float.');
            end
        else
            error('META2SICD_NISAR:unrecognizedDataType','Must be 16- or 32-bit complex float.');
        end
        H5S.close(dspace_id);
        H5D.close(dset_id);
        pol_meta.ImageData.FirstRow=uint32(0); pol_meta.ImageData.FirstCol=uint32(0);
        % SCP pick is arbitrary.  Just pick something near center.
        pol_meta.ImageData.SCPPixel.Col = floor(datasize(1)/2);
        pol_meta.ImageData.SCPPixel.Row = floor(datasize(2)/2);

        %% ImageFormation
        pol_meta.ImageFormation.RcvChanProc.ChanIndex=meta_i;
        pol_meta.ImageFormation.TxRcvPolarizationProc=...
            freq_meta.RadarCollection.RcvChannels.ChanParameters(j).TxRcvPolarization;

        %% RMA
        pol_meta.RMA.INCA.R_CA_SCP = r_ca_sampled(pol_meta.ImageData.SCPPixel.Row+1);
        scp_ca_time = zd_t(pol_meta.ImageData.SCPPixel.Col+1);
        % Compute DRateSFPoly
        % For the purposes of the DRateSFPoly computation, we ignore any
        % changes in velocity or doppler rate over the azimuth dimension.
        pos_coefs = [P_x(:) P_y(:) P_z(:)];
        % Velocity is derivate of position.
        vel_coefs=pos_coefs(1:end-1,:).*repmat(((size(pos_coefs,1)-1):-1:1)',[1 3]);
        vel_x = polyval(vel_coefs(:,1), scp_ca_time);
        vel_y = polyval(vel_coefs(:,2), scp_ca_time);
        vel_z = polyval(vel_coefs(:,3), scp_ca_time);
        vm_ca_sq = vel_x.^2 + vel_y.^2 + vel_z.^2; % Magnitude of the velocity squared
        r_ca_poly = [pol_meta.RMA.INCA.R_CA_SCP; 1]; % Polynomial representing range as a function of range distance from SCP
        [~, min_ind] = min(abs(grid_zd - scp_ca_time));  % Closest Doppler rate polynomial to SCP
        coords_rg_m = grid_r - pol_meta.RMA.INCA.R_CA_SCP;
        % TODO: Is it the NISAR convention to give the absolute value of
        % Doppler rate? (It should be negative.) Sample datasets appear
        % this way.
        old_warning_state=warning('off','MATLAB:polyfit:RepeatedPointsOrRescale');
        dop_rate_poly=polyfit(coords_rg_m, -doprate_sampled(min_ind,:).', 4);
        warning(old_warning_state);
        % figure; plot(r,dop_rate(:,min_ind),r,polyval(dop_rate_poly,r));  % Show closeness of fit
        dop_rate_poly = dop_rate_poly(end:-1:1);
        pol_meta.RMA.INCA.DRateSFPoly = - conv(dop_rate_poly,r_ca_poly) * ... % Multiplication of two polynomials is just a convolution of their coefficients
            SPEED_OF_LIGHT / (2 * fc * vm_ca_sq(1)); % Assumes a SGN of -1
        % Fields dependent on Doppler rate
        pol_meta.Grid.Col.SS = sqrt(vm_ca_sq(1)) * abs(ss_az_s) * pol_meta.RMA.INCA.DRateSFPoly(1,1);
        % Should be close to this:
        % colss_id=H5D.open(freq_id,'sceneCenterAlongTrackSpacing');
        % pol_meta.Grid.Col.SS = H5D.read(colss_id)
        % H5D.close(colss_id);
        pol_meta.Grid.Col.ImpRespBW = ... % Convert to azimuth spatial bandwidth (cycles per meter)
            min(dop_bw*abs(ss_az_s),1)/pol_meta.Grid.Col.SS; % Can't have more bandwidth in data than sample spacing
        pol_meta.RMA.INCA.TimeCAPoly = [scp_ca_time; ... % With respect to start of collect
            ss_az_s/pol_meta.Grid.Col.SS]; % Convert zero doppler spacing from sec/pixels to sec/meters
        % TimeCOAPoly/DopCentroidPoly/DeltaKCOAPoly
        POLY_ORDER = 3; % Order of polynomial which we want to compute
        coords_az_m = (grid_zd - scp_ca_time) * pol_meta.Grid.Col.SS / ss_az_s;
        timeca_sampled = repmat(grid_zd,[1 numel(grid_r)]);
        % TimeCOAPoly=TimeCA+(DopCentroid/dop_rate)
        timecoa_sampled = timeca_sampled+(dopcentroid_sampled./doprate_sampled);
        pol_meta.RMA.INCA.DopCentroidPoly=polyfit2d(dopcentroid_sampled, coords_az_m, coords_rg_m, POLY_ORDER);
        pol_meta.Grid.Col.DeltaKCOAPoly=...
            pol_meta.RMA.INCA.DopCentroidPoly*ss_az_s/pol_meta.Grid.Col.SS;
        pol_meta.Grid.TimeCOAPoly=polyfit2d(timecoa_sampled, coords_az_m, coords_rg_m, POLY_ORDER);

        %% Radiometric
        % In the simulated data (from UAVSAR), we expect these noise values
        % to be only valid for the same incidence angle as NISAR (small
        % part of swath).  We also expect the values to be in dB, not
        % linear.
        nesz = get_hdf_data(HDF5_fid,['/science/LSAR/SLC/metadata/calibrationInformation/frequency' freqs(i) '/' pols{i}(j,:)], 'nes0').';
        sigma0sf = cal_data{3};
        noise_samples = nesz - (10*log10(sigma0sf));
        pol_meta.Radiometric.NoiseLevel.NoisePoly = ...
            polyfit2d(noise_samples, coords_az_m, coords_rg_m, POLY_ORDER);
        % The conversion between radiometric fields in simulated data don't
        % seem to be valid, so we will skip using the non-constant terms
        % for now.  But this is how one would do it:
        % Fit polynomial only to valid data in image
        % az_bounds1 = [-double(pol_meta.ImageData.SCPPixel.Col) ...
        %     double(pol_meta.ImageData.NumCols - pol_meta.ImageData.SCPPixel.Col - 1)] *...
        %     pol_meta.Grid.Col.SS;
        % rg_bounds1 = [-double(pol_meta.ImageData.SCPPixel.Row) ...
        %     double(pol_meta.ImageData.NumRows - pol_meta.ImageData.SCPPixel.Row - 1)] *...
        %     pol_meta.Grid.Row.SS;
        % for k = 1:numel(cal_fields)
        %     % Only fit valid values
        %     rg_val = all(isfinite(cal_data{k}),1)&coords_rg_m'>rg_bounds1(1)&coords_rg_m'<rg_bounds1(2);
        %     if ~any(diff(cal_data{k}(isfinite(cal_data{k}))))  % Constant across image
        %         pol_meta.Radiometric.(sicd_cal_fields{k}) = mean(cal_data{k}(:),'omitnan');
        %     elseif all(all(diff(cal_data{k},1,1)==0) | all(~isfinite(cal_data{k})))% Only varies in range
        %         rad_poly = polyfit(coords_rg_m(rg_val), cal_data{k}(1,rg_val), 7);
        %         pol_meta.Radiometric.(sicd_cal_fields{k}) = rad_poly(end:-1:1).';
        %     else  % Varies 2-dimensionally.  Not tested.  POLY_ORDER may not be reasonable.
        %         pol_meta.Radiometric.(sicd_cal_fields{k}) = ...
        %             polyfit2d(cal_data{k}(:,rg_val), coords_az_m, coords_rg_m(rg_val), POLY_ORDER);
        %     end
        % end

        %% GeoData
        % Now that sensor model fields have been populated, we can populate
        % GeoData.SCP more precisely.
        ecf = point_image_to_ground([pol_meta.ImageData.SCPPixel.Row;pol_meta.ImageData.SCPPixel.Col],pol_meta);
        pol_meta.GeoData.SCP.ECF.X=ecf(1);
        pol_meta.GeoData.SCP.ECF.Y=ecf(2);
        pol_meta.GeoData.SCP.ECF.Z=ecf(3);
        llh=ecf_to_geodetic([pol_meta.GeoData.SCP.ECF.X pol_meta.GeoData.SCP.ECF.Y pol_meta.GeoData.SCP.ECF.Z]);
        pol_meta.GeoData.SCP.LLH.Lat=llh(1);
        pol_meta.GeoData.SCP.LLH.Lon=llh(2);
        pol_meta.GeoData.SCP.LLH.HAE=llh(3);

        pol_meta = derived_sicd_fields(pol_meta);
        meta_i=meta_i+1;
        grouped_meta{meta_i}=pol_meta;
    end
end
output_meta = grouped_meta;

end

% Reads HDF5 attribute
function value = get_hdf_attribute(hid_t,path,attribute_name)
gid = H5G.open(hid_t,path);
attribute = H5A.open(gid,attribute_name);
value = H5A.read(attribute,H5A.get_type(attribute));
% The behavior of whether HDF5 attributes are returned in cells seems to
% have changed in 2020a. The behavior also changed in h5readatt.
if iscell(value)
    value = value{1};
end
H5G.close(gid);
H5A.close(attribute);
end

% Reads HDF5 data
function value = get_hdf_data(hid_t,path,data_name)
gid = H5G.open(hid_t,path);
data = H5D.open(gid,data_name);
value = H5D.read(data);
H5G.close(gid);
H5D.close(data);
end

function ref_time = get_ref_time(hid_t,data_path)
    data = H5D.open(hid_t,data_path);
    attribute = H5A.open(data,'units');
    ref_time_string = H5A.read(attribute,H5A.get_type(attribute))';
    H5A.close(attribute);
    H5D.close(data);
    [ref_time_string,~] = regexp(ref_time_string,'seconds since (\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\s*$','tokens','match');
    if numel(ref_time_string)==1  % Should always be the case according to JPL
        ref_time = datenum(ref_time_string{1}{1},'yyyy-mm-dd HH:MM:SS');
    else
        error('META2SICD_NISAR:unrecognizedTimeUnit','Time units must be seconds since a reference UTC time.');
    end
end

% MATLAB's datenum function won't handle precise times down under a
% millisecond, because 1) It won't accept a format with more than 3 .FFF in
% the string description of the date format and 2) the resulting serial
% date number is stored in days from 00-JAN-0000 and just doesn't have
% enough bits to handle fractional seconds to the level we want.  Here we
% handle the fractional seconds separately so we can read date with the
% precision we need.
function [datenum_s, datenum_frac] = datenum_w_frac(datestring)
    datenum_s = datenum(datestring,'yyyy-mm-ddTHH:MM:SS');
    datenum_frac = str2double(regexp(datestring,'\.\d*','match'));
    if any(isnan(datenum_frac))||isempty(datenum_frac), datenum_frac = 0; end
end

function p = polyfit2d(samples, dim1, dim2, poly_order)
    % Least squares fit for 2D polynomial
    % A*x = b
    [dim1_2d, dim2_2d] = ndgrid(dim1, dim2);
    a = zeros(numel(samples), (poly_order+1)^2);
    for k = 0:poly_order
        for m = 0:poly_order
            a(:,k*(poly_order+1)+m+1) = (dim2_2d(:).^m).*(dim1_2d(:).^k);
        end
    end
    b = zeros((poly_order+1)^2,1);
    for k=1:((poly_order+1)^2)
        b(k)=sum(samples(:).*a(:,k)); % center of aperture
    end
    A=zeros((poly_order+1)^2);
    for k=1:((poly_order+1)^2)
        for m=1:((poly_order+1)^2)
            A(k,m)=sum(a(:,k).*a(:,m));
        end
    end
    old_warning_state=warning('off','MATLAB:nearlySingularMatrix');
    x_coa=A\b; % MATLAB often flags this as badly scaled, but results still appear valid
    warning(old_warning_state);
    p=reshape(x_coa, poly_order+1, poly_order+1);
end

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


%--- help for set_inputs ---
%
%  INTERNAL FUNCTION: Sets inputs for the VAR
%
%  ::
%
%     var_obj = set_inputs(var_obj,varargin);
%
%  Args:
%     var_obj (var object): VAR object
%     varargin: Options to set. Must come in pairs
%
%        - **'data'**: Data for the VAR. Can be either a struct or ts object
%        - **'prior'**: Prior to use if BVAR. Available priors are
%
%           - minnesota
%           - jeffrey
%           - nw
%           - normal-wishart
%           - inw
%           - sz
%
%        - **'linear_restrictions'**: Linear restrictions. For the format, see XXXXXXXXX
%
%  Returns:
%     :
%
%        - var_obj (var object): New VAR with the given inputs updated.
%
%


%kPlot2D '2D Plot Object Menuform'
% This MatLab function was automatically generated by a converter (KhorosToMatLab) from the Khoros Plot2D.pane file
%
% Parameters:
% MultiChoice: plot2DXOrientation 'X Orientation', default: 1: 'x orientation'
%    Choices are:
%   1: 'Width'
%   2: 'Height'
%   3: 'Depth'
%   4: 'Time'
%   5: 'Elements'
% Integer: plot2DWidthOffset 'Width Offset', default: 0: 'width offset'
% Integer: plot2DHeightOffset 'Height Offset', default: 0: 'height offset'
% Integer: plot2DDepthOffset 'Depth Offset', default: 0: 'depth offset'
% Integer: plot2DTimeOffset 'Time Offset', default: 0: 'time offset'
% Integer: plot2DElementsOffset 'Elements Offset', default: 0: 'elements offset'
%
% Example: kPlot2D( {'plot2DXOrientation',1;'plot2DWidthOffset',0;'plot2DHeightOffset',0;'plot2DDepthOffset',0;'plot2DTimeOffset',0;'plot2DElementsOffset',0})
%
% Khoros helpfile follows below:
%
% A 2D plot visual object supports the display of a 2D plot.
% .begin tagged
% .item "Plot Type"
% This list selection lets you set the plot type of the 2D plot object.
% Choices include: "Line Plot," "Discrete," "Bar Graph," "Polymarker," and
% "Line Marker."  Line Plot is a simple line graph.  Discrete displays a set of
% vertical lines, from each (x,y) point down to the X axis.  Bar Graph
% displays the usual bar graph.  Polymarker displays a single marker at
% each (x,y) location.  Line Marker is a combination of Line Plot and
% Polymarker, resulting in a "connect the dots" display.
% .item "Line Type"
% There are seven line types available: "Solid," "Dotted," "Dot Dash,"
% "Short Dash," "Long Dash," "Odd Dash," and "Grid Dotted."  The line
% type is not used with Polymarker or color marker plots.
% .item "Marker Type"
% This list selection allows you to control the marker type that is used
% with Polymarker, line marker, and colormarker plots.  There is a wide
% variety of marker types that may be used.
% .item "Color Origination"
% When this attribute is set to "Use Foreground Color," the plot is colored in
% the foreground color.   When set to "Use Data Values," the plot is colored
% according to the plot data.
% .item "Foreground Color"
% This string list lets you select (or type) the foreground color of the
% 2D plot.  It is not used with plot types that are automatically
% colored according to the "D" values in the plot data, which include
% color marker and colorline plots.
% .item "X Orientation"
% This toggle only takes effect when the plot data input is provided via
% a data object (as opposed to an array of Coords).  Furthermore, it
% only applies when the plot data is stored in the value segment of the
% data object (as opposed to the location segment of the data object).
% It dictates how the 2D plot data is to be extracted from the value
% segment of the data object.  The 2D plot data is always extracted as a
% single line, but that line may be oriented by width, height, depth,
% time, or elements (see manual for more details).
% .item "Width, Height, Depth, Time, And Elements Offsets"
% These integer values may be set in conjunction with the "X
% Orientation" toggle.  When "X Orientation" is "not" set to
% "Width," the "Width Offset" may be used to specify the width offset at
% which the line of plot data is to be extracted.  Similarly, "Height
% Offset," "Depth Offset," "Time Offset," and "Elements Offset" may be
% used to specify offsets into height, depth, time, and elements.  Note
% that an offset may only be used when that dimension of the value data
% is greater than 1.
% .end tagged

function varargout = kPlot2D(varargin)
Inputs={};
if nargin ==0
  arglist={'',''};
elseif nargin ==1
  arglist=varargin{1};
else error('Usage: [out1,..] = kPlot2D(arglist).');
end
if size(arglist,2)~=2
  error('arglist must be of form {''ParameterTag1'',value1;''ParameterTag2'',value2}')
 end
narglist={'plot2DXOrientation', 1;'plot2DWidthOffset', 0;'plot2DHeightOffset', 0;'plot2DDepthOffset', 0;'plot2DTimeOffset', 0;'plot2DElementsOffset', 0};
maxval={0,1,1,1,1,1};
minval={0,1,1,1,1,1};
istoggle=[0,0,0,0,0,0];
was_set=istoggle * 0;
paramtype={'MultiChoice','Integer','Integer','Integer','Integer','Integer'};
% identify the input arrays and assign them to the arguments as stated by the user
if ~iscell(Inputs)
Inputs = {Inputs};
end
NumReqOutputs=0; 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(0);
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
callKhoros([w 'Plot2D" '],Inputs,narglist);


%UIDrawSubCodes UIDrawSubCodes enumeration for the brick
%
% Notes::
% - UIDrawSubCodes can be found in the EV3 documentation and source code
% (bytecodes.h)
% - USE SCREEN FOR GRAPHICS
%   -----------------------
%
%     opUI_DRAW,LC0(FILLWINDOW),LC0(0),LC0(0),LC0(0)
%
%       (clear entire screen by filling it with zero - background color)
%
%
%     opUI_DRAW, - - - - - -
%     opUI_DRAW, - - - - - -
%     opUI_DRAW, - - - - - -
%
%       (draw graphical stuff on the screen)
%
%
%     opUI_DRAW,LC0(UPDATE)
%
%       (show the stuff by updating the screen)
%
%
%
%   RESTORE RUN SCREEN
%   ------------------
%
%     opUI_WRITE,LC0(INIT_RUN)
%
%       (enable the animated run screen)

classdef UIDrawSubCodes < uint8
    enumeration
         Update(0)
         Clean (1)
         Pixel (2)
         Line (3)
         Circle (4)
         Text (5)
         Icon (6)
         Picture (7)
         Value (8)
         Fillrect(9)
         Rect (10)
         Notification (11)
         Question (12)
         Keyboard (13)
         Browse (14)
         Vertbar (15)
         Inverserect (16)
         SelectFont (17)
         Topline (18)
         Fillwindow (19)
         Scroll (20)
         Dotline (21)
         ViewValue (22)
         ViewUnit (23)
         Fillcircle (24)
         Store (25)
         Restore (26)
         IconQuestion (27)
         Bmppfile(28)
         Popup (29)
         GraphSetup (30)
         GraphDraw (31)
         Textbox (32)
    end
end


function ret = spm_ov_contour(varargin)
% Contour tool - plugin for spm_orthviews
%
% This routine is a plugin to spm_orthviews. For general help about
% spm_orthviews and plugins type
%             help spm_orthviews
% at the MATLAB prompt.
%__________________________________________________________________________
% Copyright (C) 2014-2016 Wellcome Trust Centre for Neuroimaging

% Guillaume Flandin
% $Id: spm_ov_contour.m 6774 2016-04-20 12:25:08Z guillaume $

switch lower(varargin{1})
    % Context menu and callbacks
    case 'context_menu'
        item0 = uimenu(varargin{3}, ...
            'Label', 'Contour',...
            'UserData',struct('nblines',3,'style','r-','id',varargin{2}));
        item1 = uimenu(item0,...
            'Label','Display onto');
        item1_1 = uimenu(item1,...
            'Label','all',...
            'Callback',@(hObj,event) contour_menu(hObj,event,varargin{2},Inf));
        item1_2 = uimenu(item1,...
            'Label','all but current',...
            'Callback',@(hObj,event) contour_menu(hObj,event,varargin{2},NaN));
        item1_3 = uimenu(item1,...
            'Label','select...',...
            'Callback',@(hObj,event) contour_menu(hObj,event,varargin{2}));
        item2 = uimenu(item0,...
            'Label','Options');
        item2_1 = uimenu(item2,...
            'Label', 'Number of lines...',...
            'UserData',struct('str','Number of lines', 'field','nblines'),...
            'Callback',@(hObj,event) contour_options(hObj,event,item0));
        item2_2 = uimenu(item2,...
            'Label','Line style...',...
            'UserData',struct('str','Line style', 'field','style'),...
            'Callback',@(hObj,event) contour_options(hObj,event,item0));
        %'Callback',@(hObj,event) contour_display(hObj,event,varargin{2}));
        ret = item0;
    case 'display'
        if nargin == 1
            varargin{2} = spm_input('Select image', '!+1', 'e');
        end
        contour_display(varargin{2:end});
    case 'redraw'
        contour_redraw(varargin{2:end});
    otherwise
end

%==========================================================================
function contour_options(hObj,event,hM)
Finter = spm_figure('FindWin', 'Interactive');
spm_input('!DeleteInputObj',Finter);
UDm = get(hObj,'UserData');
UDc = get(hM,'UserData');
if isnumeric(UDc.(UDm.field))
    N = 1;
    T = 'e';
else
    N = Inf;
    T = 's';
end
UDc.(UDm.field) = spm_input(UDm.str, '!+1', T, UDc.(UDm.field), N);
set(hM,'UserData',UDc);
try, contour_redraw(UDc.id); end

%==========================================================================
function contour_menu(hObj,event,i,varargin)

global st

allOpts = get(get(hObj,'parent'),'children');
current = strcmp(get(hObj,'Checked'),'on');
if any(strcmp(get(allOpts,'Checked'),'on'))
    set(allOpts, 'Checked','off');
    contour_delete(i);
    st.vols{i} = rmfield(st.vols{i},'contour');
end
if ~current
    set(hObj, 'Checked','on');
    contour_display(i,varargin{:});
end

%==========================================================================
function contour_display(i,o)

global st

if nargin < 2
    o = spm_input('Select image(s)', '!+1', 'e', ...
        num2str(spm_orthviews('valid_handles')));
    o = intersect(spm_orthviews('valid_handles'),o);
elseif isinf(o)
    o = spm_orthviews('valid_handles');
elseif isnan(o)
    o = setxor(spm_orthviews('valid_handles'),i);
end

try
    hM = findobj(st.vols{i}.ax{1}.cm,'Label','Contour');
    UD = get(hM,'UserData');
    nblines = UD.nblines;
    linestyle = UD.style;
catch
    nblines = 3;
    linestyle = 'r-';
end
linewidth = 1;

contour_delete(i);

lh = {};
sw = warning('off','MATLAB:contour:ConstantData');
for d = 1:3
    CData = sqrt(sum(get(st.vols{i}.ax{d}.d,'CData').^2, 3));
    CData(isinf(CData)) = NaN;
    CData(isnan(CData)) = 0;
    for h = o(:)'
        set(st.vols{h}.ax{d}.ax,'NextPlot','add');
        [C,lh{end+1}] = ...
            contour(st.vols{h}.ax{d}.ax,CData,...
            nblines,linestyle,'LineWidth',linewidth);
    end
end
warning(sw);
set(cat(1,lh{:}),'HitTest','off');

st.vols{i}.contour.images = o;
st.vols{i}.contour.handles = lh;

%==========================================================================
function contour_redraw(i,varargin) %i, TM0, TD, CM0, CD, SM0, SD

global st

contour_delete(i);
contour_display(i,st.vols{i}.contour.images);

%==========================================================================
function contour_delete(i)

global st

try, delete(cat(1,st.vols{i}.contour.handles{:})); end


function [model, lbls] = fgplvmReadFromFID(FID)

% FGPLVMREADFROMFID Load from a FID produced by the C++ implementation.
% FORMAT
% DESC loads in from a file stream the data format produced by the
% C++ implementation of the GP-LVM.
% ARG FID : the file ID from where the data is loaded.
% RETURN model : the model loaded in from the file.
% RETURN lbls : any data labels associated with the data in the
% model.
%
% COPYRIGHT : Neil D. Lawrence, 2005, 2006, 2009
%
% SEEALSO : fgplvmReadFromFile

% FGPLVM
try
  version = readVersionFromFID(FID);
catch
  if strcmp(lasterr, 'Incorrect file format')
    version = readDoubleFromFID(FID, 'gplvmVersion');
  else
    error(lasterr)
  end
end

if version > 0.2
  error('Incorrect file version.')
end

if version>0.11
  baseType = readStringFromFID(FID, 'baseType')
  if ~strcmp(baseType, 'datamodel')
    error('Incorrect base type in file.')
  end
  type = readStringFromFID(FID, 'type')
  if ~strcmp(type, 'gplvm')
    error('Incorrect type in file.')
  end
end
numData = readIntFromFID(FID, 'numData')
if version > 0.11
  strv = 'outputDim';
else
  strv = 'numProcesses';
end
dataDim = readIntFromFID(FID, strv)
if version > 0.11
  strv = 'inputDim';
else
  strv = 'latentDim';
end
latentDim = readIntFromFID(FID, strv);
if(version>0.1)
  latentRegularised = readBoolFromFID(FID, 'latentRegularised');
  backConstrained = readBoolFromFID(FID, 'backConstrained');
  dynamicsLearnt = readBoolFromFID(FID,  'dynamicsLearnt');
else
  latentRegularised = 0;
  backConstrained = 0;
  dynamicsLearnt = 0;
end
if version > 0.11
  kern = modelReadFromFID(FID);
else
  kern = kernReadFromFID(FID, version);
end
if dynamicsLearnt
  if version > 0.11
    dynKern = modelReadFromFID(FID);
  else
    dynKern = kernReadFromFID(FID, version);
  end
end
if version > 0.11
  noise = modelReadFromFID(FID);
else
  noise = noiseReadFromFID(FID, version);
end

labelsPresent = 0;
labels=ones(numData, 1);
lineStr = getline(FID);
tokens = tokenise(lineStr,',');
for i=1:length(tokens)
  subTokens = tokenise(tokens{i}, ':');
  if strcmp(subTokens{1}, 'Y')
    if str2num(subTokens{2})~=dataDim
      error('Incorrect file format.');
    end
  elseif strcmp(subTokens{1}, 'X')
    if str2num(subTokens{2})~=latentDim
      error('Incorrect file format.');
    end
  elseif strcmp(subTokens{1}, 'labels')
    labelsPresent = 1;
    if str2num(subTokens{2})~=1
      error('Incorrect file format.');
    end
  else
    error('Incorrect file format.');
  end
end

X = zeros(numData, latentDim);
y = zeros(numData, dataDim);

for i=1:numData
  lineStr = getline(FID);
  tokens = tokenise(lineStr);
  for j=1:dataDim
    y(i, j) = str2num(tokens{j});
  end
  for j=1:latentDim
    X(i, j) = str2num(tokens{j+dataDim});
  end
  if labelsPresent
    labels(i) = str2num(tokens{dataDim+latentDim+1});
  end
end
if(~strcmp(noise.type, 'scale'))
  error('Can only load noise of type scale.')
end
if latentRegularised
  prior = 'gaussian';
else
  prior = [];
end
if backConstrained
  warning('Not loading form of back constraints.')
end
options = fgplvmOptions('ftc');
options.kern = kern;
options.prior = prior;
model = fgplvmCreate(latentDim, size(y, 2), y, options);
model.X = X;
model.scale = noise.scale;
model.bias = noise.bias;
model.m = gpComputeM(model);

% This forces kernel computation.
initParams = fgplvmExtractParam(model);
model = fgplvmExpandParam(model, initParams);
if dynamicsLearnt
  options = gpOptions;
  options.kern = dynKern;
  model = fgplvmAddDynamics(model, 'gp', options);
end
lbls=[];
if labelsPresent
  minLbl = min(labels);
  maxLbl = max(labels);
  counter = 0;
  for i=minLbl:maxLbl
    counter = counter + 1;
    lbl = zeros(size(labels));
    lbl(find(labels==i))=1;
    lbls = [lbls lbl];
  end
end


function [tests,pass,perf]=test_snip(varargin)

inputs={'verbose'};
verbose=0;
for n=1:nargin
    if(~isempty(varargin{n}))
        eval([inputs{n} '=varargin{n};']);
    end
end

%Test the examples
test_string={'Fs=360;err=snip(''mitdb/100'',''100cut'',Fs*60,Fs*2*60);'};
clean_up={['delete([pwd filesep ''100cut*'']);']};
[tests,pass,perf]=test_wrapper(test_string,clean_up,verbose);


function [x, zone, variable, index_c1, index_c2, index_bc] = ...
         twod_readcadyf(file,num_fig)
%-----------------------------------------------------------------------
%  twod_readcadyf.m - Reads in mesh, connectivity and boundary node
%                     numbers from a *.cadyf file generated with
%                     amiral (preprocessor for cadyf).
%
%                     This code is quite problem specific, since the
%                     *.cadyf file is setup to solve the Navier-Stokes
%                     equations.
%
%                     ! NOTE: Matlab has difficulty with D+00 double
%                     precision notation, globally change to E+00
%
%  Copyright (c) 2001, Jeff Borggaard, Virginia Tech
%  Version: 1.0a
%
%  Usage:     [x, zone, variable, index_c1, index_c2, index_bc] = ...
%             twod_readcadyf(file,num_fig)
%
%  Variables:     file
%                        Name of the *.cadyf file.
%                 num_fig
%                        If included, plot the mesh
%
%                 x
%                        Nodal coordinates
%                 zone(#).e_conn
%                        Element connectivity
%                 variable
%                        Contains finite element information
%                 index_c1
%                        Node numbers corresponding to curve c1
%                 index_c2
%                        Node numbers corresponding to curve c2
%                 index_bc
%                        Node numbers corresponding to curve bc
%-----------------------------------------------------------------------
fid = fopen(file);

% match up with indata.f and contrl.f in cadyf

%-----------------------------------------------------------------------
%  From control blocks
%-----------------------------------------------------------------------
% card # 1
temp  = fgetl(fid);           % title line

% card # 2
A     = fscanf(fid,'%i',9);   % itim, lin, etc.    (cart43.f)
itemp = A(5);
iturb = A(8);

A     = fscanf(fid,'%g',9);   % icomp, itype, etc.

if (iturb == 3)
  temp = fgetl(fid);          % turbulent source terms
  temp = fgetl(fid);          % model coefficients
end

% card # 3
A = fscanf(fid,'%i %i %i %i %i %i %i %i %i',9); %  (cart44.f)

% get the number of nodes and elements
n_node  = A(1);
n_elem  = A(2);
n_zones = A(3);

% fprintf('twod_readcadyf: reading in mesh with %g nodes and %g elements\n',...
%          n_node,n_elem);

% skip over cards # 4 and # 5
temp = fscanf(fid,'%s',4);  % execution type, etc.  (cart45.f)
temp = fscanf(fid,'%s',4);  % body forces           (cart46.f)

% card # 6
A = fscanf(fid,'%i %i %i %i %i %i',6);    %         (cart47.f)

% read in number of specified boundary conditions
n_dbc = sum(A(1:6)); % number of bcs for u,v,w,T,k,e
% fprintf('                reading %g boundary conditions\n',n_dbc);

% card # 7 (assuming no neuman bc's)
temp = fscanf(fid,'%g',6);                %         (cart48.f)
% card # 8 (assuming no periodic bc's)
temp = fscanf(fid,'%g',1);                %         (cart49.f)

temp = fscanf(fid,'%g',12); % card #9     %         (ct4101.f)
temp = fscanf(fid,'%g',7);  % card #10    %         (ct4102.f)

% card #11
A = fscanf(fid,'%g',8);                   %         (ct411.f)
ninitu = A(5); % number of initial guess to read/skip
ninitt = A(6);
ninitk = A(7);
ninite = A(8);
ntoti  = ninitu + ninitt + ninitk + ninite;
% fprintf('                reading %g initial guess lines\n',ntoti);

temp = fscanf(fid,'%g',15); % card #12
temp = fscanf(fid,'%g',8);  % card #13
temp = fscanf(fid,'%g',2);  % card #14
temp = fscanf(fid,'%g',5);  % card #15
temp = fscanf(fid,'%g',6);  % card #16              (ct416.f)

%-----------------------------------------------------------------------
%  Now read in nodes and connectivity information
%-----------------------------------------------------------------------
% read in inblok data (skipped for now)
% read in time functions (intfns) (skipped for now)
% read in periodic nodes (skipped for now)

% read in nodal coordinates

for i=1:n_node
  A = fscanf(fid,'%g',12);    %      (cart7.f)
  if (length(A)<12)
    error('You need to change Ds to Es, Matlab doesnt recognize doubles')
  end
  x(i,1) = A(10);
  x(i,2) = A(11);
end

% read in blade normal information (inblad) (skipped for now)

% read in boundary conditions (incons)
%  this can be used to "tag" certain nodes...
index_c1 = [];
index_c2 = [];
index_bc = [];
for i=1:n_dbc
  A = fscanf(fid,'%i %g %i %g',4);      %   (cart9.f)

  if (A(2)==1)
    index_c1 = [index_c1 A(1)];
  elseif (A(2)==2)
    index_c2 = [index_c2 A(1)];
  else
    index_bc = [index_bc A(1)];
  end

  variable.dirichlet_node(i) = A(1);
  variable.dirichlet_value(i) = max(0,A(2));
end

% read in neuman data (inload, skipped for now)
% read in volumetric body forces (invol, skipped for now)

% read in, or skip, reading in the initial guess... (inital)
for i=1:ninitu
  temp = fscanf(fid,'%g',4);              %  (cart10.f)
end
for i=1:ninitt
  temp = fscanf(fid,'%g',2);
end
for i=1:ninitk
  temp = fscanf(fid,'%g',1);
end
for i=1:ninite
  temp = fscanf(fid,'%g',1);
end

for n=1:n_zones
  % skip over three lines
  A = fscanf(fid,'%i',8);
  n_elzone = A(2);

  A = fscanf(fid,'%i',8);
  A = fscanf(fid,'%i',8);

  % skip over material properties
  temp   = fscanf(fid,'%g',1); % rho
  temp   = fscanf(fid,'%g',1); % viscosity
  if ( itemp==1 | itemp==2 )
    temp = fscanf(fid,'%g',1); % cp
    temp = fscanf(fid,'%g',1); % cond
  end

  % read in element connectivity (assuming 6 noded elements)
  zone(n).e_conn = [];
  for i=1:n_elzone
    A = fscanf(fid,'%i',7);
    zone(n).e_conn(i,:) = A(2:7)';
  end
end

if ( nargin==2 )
  for n=1:n_zones
    twod_plotm2(num_fig,x,zone(n).e_conn)
  end
  figure(num_fig)

  hold on
  for i=1:length(index_c1)
    n = index_c1(i);
    plot(x(n,1),x(n,2),'g*')
  end
  for i=1:length(index_c2)
    n = index_c2(i);
    plot(x(n,1),x(n,2),'r*')
  end
  for i=1:length(index_bc)
    n = index_bc(i);
    plot(x(n,1),x(n,2),'b*')
  end
  pause(.001)
end


function params = setParams(params)

% HW params
params.Fs = 400;
% Number of channels
params.nChans = 16;

% Subjects to include
params.trainSubs = {'1', '2', '3'};
nSubs = numel(params.trainSubs);
params.testSubs = {'1', '2', '3'};

% OK Check params
% Marks files as OK in fileList if proportion of zeros is below this
% threshold
% Not used anymore
params.OKThresh = 0.5;

% Feature extraction params
params.HillsBands.Range = 1:47;