function [accuracy,Mdl] = GiveMeCfn(XTrain,yTrain,XTest,yTest,cfnParams,beVerbose)
% GiveMeCfn    Returns classification results from training a classifier on
%               training/test data
%
%---INPUTS:
% XTrain -- training data matrix
% yTrain -- training data labels
% XTest -- testing data matrix
% yTest -- testing data labels
% cfnParams -- parameters for classification
% beVerbose -- [default: false] whether to give text output of progress
%
%---OUTPUTS:
% accuracy -- IF cfnParams.computePerFold = true:
%               a 2x1 cell of accuracy results in each fold for training data (element 1)
%               and test data (element 2).
%             IF cfnParams.computePerFold = false:
%               A real number giving the average accuracy in the test folds.
% Mdl -- the trained classification model.

% ------------------------------------------------------------------------------
% Copyright (C) 2020, Ben D. Fulcher <ben.d.fulcher@gmail.com>,
% <http://www.benfulcher.com>
%
% If you use this code for your research, please cite the following two papers:
%
% (1) B.D. Fulcher and N.S. Jones, "hctsa: A Computational Framework for Automated
% Time-Series Phenotyping Using Massive Feature Extraction, Cell Systems 5: 527 (2017).
% DOI: 10.1016/j.cels.2017.10.001
%
% (2) B.D. Fulcher, M.A. Little, N.S. Jones, "Highly comparative time-series
% analysis: the empirical structure of time series and their methods",
% J. Roy. Soc. Interface 10(83) 20130048 (2013).
% DOI: 10.1098/rsif.2013.0048
%
% This work is licensed under the Creative Commons
% Attribution-NonCommercial-ShareAlike 4.0 International License. To view a copy of
% this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/ or send
% a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View,
% California, 94041, USA.
% ------------------------------------------------------------------------------

%-------------------------------------------------------------------------------
%% Check Inputs:
%-------------------------------------------------------------------------------
% Ensure y is a column vector
if size(yTrain,1) < size(yTrain,2)
    yTrain = yTrain';
end
if nargin < 3
    XTest = [];
end
if nargin < 4
    yTest = [];
end
if nargin < 6
    beVerbose = false;
end

%-------------------------------------------------------------------------------
%% Define the classification model
%------------------------------------------------------------------------------
if strcmp(cfnParams.whatClassifier,'fast-linear')
    %--------------------------------------------------------------------------
    % Special case -- the `classify` function is faster than others
    %--------------------------------------------------------------------------
    if cfnParams.numFolds > 0
        if cfnParams.doReweight
            Mdl = fitcdiscr(XTrain,yTrain,'Prior','empirical',...
            'DiscrimType','linear','Weights',InverseProbWeight(yTrain),...
            'KFold',cfnParams.numFolds);
        else
            Mdl = fitcdiscr(XTrain,yTrain,'Prior','empirical',...
            'DiscrimType','linear','KFold',cfnParams.numFolds);
        end
    else
        if nargout == 1
            % It's faster to use classify if we only want the accuracy
            accuracy = BF_LossFunction(yTest,classify(yTest,XTrain,yTrain),...
                            cfnParams.whatLoss,cfnParams.classLabels);
            return;
        else
            Mdl = fitcdiscr(XTrain,yTrain,'Prior','empirical');
        end
    end
else
    if cfnParams.numClasses==2
        %----------------------------------------------------------------------
        % Special case: a two-class model
        %----------------------------------------------------------------------
        switch cfnParams.whatClassifier
        case 'knn'
            if beVerbose
                fprintf(1,'Using three neighbors for knn\n');
            end
            if cfnParams.numFolds > 0
                Mdl = fitcknn(XTrain,yTrain,'NumNeighbors',3,'KFold',cfnParams.numFolds);
            else
                Mdl = fitcknn(XTrain,yTrain,'NumNeighbors',3);
            end
        case 'tree'
            if cfnParams.numFolds > 0
                Mdl = fitctree(XTrain,yTrain,'KFold',cfnParams.numFolds);
            else
                Mdl = fitctree(XTrain,yTrain);
            end
        case {'linear','linclass'}
            if cfnParams.numFolds > 0
                Mdl = fitcdiscr(XTrain,yTrain,'FillCoeffs','off','SaveMemory','on','KFold',cfnParams.numFolds);
            else
                Mdl = fitcdiscr(XTrain,yTrain,'FillCoeffs','off','SaveMemory','on');
            end
        case 'logistic'
            if cfnParams.doReweight
                if cfnParams.numFolds > 0
                    Mdl = fitclinear(XTrain,yTrain,'Learner','logistic','Regularization','ridge',...
                                'Lambda','auto','Weights',InverseProbWeight(yTrain),'KFold',cfnParams.numFolds);
                else
                    Mdl = fitclinear(XTrain,yTrain,'Learner','logistic','Regularization','ridge',...
                                'Lambda','auto','Weights',InverseProbWeight(yTrain));
                end
            else
                if cfnParams.numFolds > 0
                    Mdl = fitclinear(XTrain,yTrain,'Learner','logistic','Regularization','ridge',...
                                'Lambda','auto','KFold',cfnParams.numFolds);
                else
                    Mdl = fitclinear(XTrain,yTrain,'Learner','logistic','Regularization','ridge',...
                                'Lambda','auto');
                end
            end
        case {'svm-linear','svm-linear-highdim'}
            % This one uses the fitclinear function (fast; suited for high-dimensional datasets)
            % lambdaDefault = 1/size(XTrain,1);
            % Weight observations by inverse class probability:
            if cfnParams.doReweight
                if cfnParams.numFolds > 0
                    Mdl = fitclinear(XTrain,yTrain,'Learner','svm','Lambda','auto','Regularization','ridge',...
                                'Weights',InverseProbWeight(yTrain),'KFold',cfnParams.numFolds);
                else
                    Mdl = fitclinear(XTrain,yTrain,'Learner','svm','Lambda','auto','Regularization','ridge',...
                                'Weights',InverseProbWeight(yTrain));
                end
            else
                if cfnParams.numFolds > 0
                    Mdl = fitclinear(XTrain,yTrain,'Learner','svm','Lambda','auto',...
                                        'Regularization','ridge','KFold',cfnParams.numFolds);
                else
                    Mdl = fitclinear(XTrain,yTrain,'Learner','svm','Lambda','auto','Regularization','ridge');
                end
            end
        case {'svm','svm-linear-lowdim'}
            % For low-dim datasets (e.g., using a reduced feature set), the 'traditional' linear SVM (fitcsvm)
            % can be preferable to the above (fitclinear)
            if cfnParams.doReweight
                if cfnParams.numFolds > 0
                    Mdl = fitcsvm(XTrain,yTrain,'KernelFunction','linear','Weights',...
                                    InverseProbWeight(yTrain),'KFold',cfnParams.numFolds);
                else
                    Mdl = fitcsvm(XTrain,yTrain,'KernelFunction','linear','Weights',InverseProbWeight(yTrain));
                end
            else
                if cfnParams.numFolds > 0
                    Mdl = fitcsvm(XTrain,yTrain,'KernelFunction','linear','KFold',cfnParams.numFolds);
                else
                    Mdl = fitcsvm(XTrain,yTrain,'KernelFunction','linear');
                end
            end
        case 'svm-rbf'
            % Weight observations by inverse class probability:
            if cfnParams.doReweight
                if cfnParams.numFolds > 0
                    Mdl = fitcsvm(XTrain,yTrain,'KernelFunction','rbf','Weights',...
                                    InverseProbWeight(yTrain),'KFold',cfnParams.numFolds);
                else
                    Mdl = fitcsvm(XTrain,yTrain,'KernelFunction','rbf','Weights',...
                                                    InverseProbWeight(yTrain));
                end
            else
                if cfnParams.numFolds > 0
                    Mdl = fitcsvm(XTrain,yTrain,'KernelFunction','rbf','KFold',cfnParams.numFolds);
                else
                    Mdl = fitcsvm(XTrain,yTrain,'KernelFunction','rbf');
                end
            end
        case 'diaglinear'
            if cfnParams.numFolds > 0
                Mdl = fitcnb(XTrain,yTrain,'KFold',cfnParams.numFolds);
            else
                Mdl = fitcnb(XTrain,yTrain);
            end
        otherwise
            error('Unknown classifier: ''%s''',cfnParams.whatClassifier);
        end
    else
        %----------------------------------------------------------------------
        % Define and fit a classification model involving 3 or more classes
        %----------------------------------------------------------------------
        % Define the model:
        switch cfnParams.whatClassifier
        case 'knn'
            t = templateKNN('NumNeighbors',3,'Standardize',true);
            if beVerbose, fprintf(1,'Using knn classifier\n'); end
        case 'tree'
            t = templateTree('Surrogate','off');
            if beVerbose, fprintf(1,'Using a tree classifier\n'); end
        case {'linear','linclass'}
            t = templateDiscriminant('DiscrimType','linear','FillCoeffs','off','SaveMemory','on');
            if beVerbose, fprintf(1,'Using a linear classifier\n'); end
        case 'diaglinear'
            % Naive Bayes classifier:
            t = templateNaiveBayes('DistributionNames','normal');
            if beVerbose, fprintf(1,'Using a naive Bayes classifier\n'); end
        case {'svm','svm-linear','svm-linear-lowdim'}
            t = templateSVM('KernelFunction','linear');
            if beVerbose, fprintf(1,'Using a linear svm classifier\n'); end
        case 'svm-rbf'
            t = templateSVM('KernelFunction','rbf');
            if beVerbose, fprintf(1,'Using a rbf svm classifier\n'); end
        case 'logistic'
            error('Logistic regression is not applicable to problems with >2 classes')
        otherwise
            error('Unknown classifier: ''%s''',cfnParams.whatClassifier);
        end

        % Fit the model:
        if ismember(cfnParams.whatClassifier,{'svm-linear','svm-rbf','linear','linclass','diaglinear'}) && cfnParams.doReweight
            % Reweight to give equal weight to each class (in case of class imbalance)
            if cfnParams.numFolds > 0
                Mdl = fitcecoc(XTrain,yTrain,'Learners',t,'Weights',InverseProbWeight(yTrain),'KFold',cfnParams.numFolds);
            else
                Mdl = fitcecoc(XTrain,yTrain,'Learners',t,'Weights',InverseProbWeight(yTrain));
            end
        else
            % Don't reweight:
            if cfnParams.numFolds > 0
                Mdl = fitcecoc(XTrain,yTrain,'Learners',t,'KFold',cfnParams.numFolds);
            else
                Mdl = fitcecoc(XTrain,yTrain,'Learners',t);
            end
        end
    end
end

%-------------------------------------------------------------------------------
%% Evaluate performance on test data (in-sample, or from CV folds)
%-------------------------------------------------------------------------------
% Predict the test data:
if cfnParams.numFolds == 0
    if isempty(XTest) || isempty(yTest)
        % No need to compute this if you're just after the model
        accuracy = [];
        return
    else
        yPredict = predict(Mdl,XTest);
        accuracy = BF_LossFunction(yTest,yPredict,cfnParams.whatLoss,cfnParams.classLabels);
    end
else
    % Test data is mixed through the training data provided using k-fold cross validation
    % Output is the accuracy/loss measure for each fold
    if cfnParams.computePerFold
        % Compute separately for each test fold:
        yPredict = kfoldPredict(Mdl);
        accuracyTestFolds = arrayfun(@(x) BF_LossFunction(yTrain(Mdl.Partition.test(x)),...
                            yPredict(Mdl.Partition.test(x)),cfnParams.whatLoss,...
                            cfnParams.classLabels),1:cfnParams.numFolds);

        % Compute for each training fold:
        accuracyTrainFolds = zeros(cfnParams.numFolds,1);
        for k = 1:cfnParams.numFolds
            XTrain_k = XTrain(Mdl.Partition.training(k),:);
            yTrain_k = yTrain(Mdl.Partition.training(k));
            yPredict_k = predict(Mdl.Trained{k},XTrain_k);
            accuracyTrainFolds(k) = BF_LossFunction(yTrain_k,yPredict_k,cfnParams.whatLoss,...
                            cfnParams.classLabels);
        end

        % Combine train/test accuracies:
        accuracy = {accuracyTrainFolds,accuracyTestFolds};

        % Warning for training fold over-fitting:
        if ~cfnParams.suppressWarning && all(accuracyTrainFolds==100)
            warning('100% in-sample accuracy alert (just FYI: no need for any action!)')
            % Could suggest regularization or reducing the number of features
        end
    else
        yPredict = kfoldPredict(Mdl);
        % Aggregate all outputs from folds and compute accuracy once from that aggregate:
        accuracy = BF_LossFunction(yTrain,yPredict,cfnParams.whatLoss,cfnParams.classLabels);

        % % Above is equivalent to computing the accuracy in each test fold, then computing the mean
        % % across folds:
        % accuracyTestFolds = arrayfun(@(x) BF_LossFunction(yTrain(Mdl.Partition.test(x)),...
        %                 yPredict(Mdl.Partition.test(x)),cfnParams.whatLoss,...
        %                 cfnParams.classLabels),1:cfnParams.numFolds);
        % accuracy = mean(accuracyTestFolds);
    end
end

end


function [chanMapName] = make_eMouseChannelMap_3B_short(fpath,NchanTOT)
% create a channel Map file for simulated data on a section of
% an imec 3B probe to use with eMouse
% essentially identical to the 3A version

% total number of channels = 385 (in real 3A, 384 channels + digital)
chanMap = (1:NchanTOT)';

% channels to ignore in analysis and when adding data
% in real 3A data, include the reference channels and the digital channel
% replicate the refChans that are within range of the short probe to
% preserve the geometry for channel to channel correlation in noise
% generation

allRef = [37, 76, 113, 152, 189, 228, 265, 304, 341, 380];
refChan = allRef( find(allRef < max(NchanTOT)) );

connected = true(NchanTOT,1);
connected(refChan) = 0;

% copy the coordinates from MP chanmap for 3A.
halfChan = floor(NchanTOT/2);

xcoords = zeros(NchanTOT,1,'double');
ycoords = zeros(NchanTOT,1,'double');

xcoords(1:4:NchanTOT) = 43;
xcoords(2:4:NchanTOT) = 11;
xcoords(3:4:NchanTOT) = 59;
xcoords(4:4:NchanTOT) = 27;

ycoords(1:2:NchanTOT) = 20*(1:halfChan);
ycoords(2:2:NchanTOT) = 20*(1:halfChan);

% Often, multi-shank probes or tetrodes will be organized into groups of
% channels that cannot possibly share spikes with the rest of the probe. This helps
% the algorithm discard noisy templates shared across groups. In
% this case, we set kcoords to indicate which group the channel belongs to.
% In our case all channels are on the same shank in a single group so we
% assign them all to group 1.

kcoords = ones(NchanTOT,1);

% at this point in Kilosort we do data = data(connected, :), ycoords =
% ycoords(connected), xcoords = xcoords(connected) and kcoords =
% kcoords(connected) and no more channel map information is needed (in particular
% no "adjacency graphs" like in KlustaKwik).
% Now we can save our channel map for the eMouse.

% would be good to also save the sampling frequency here
fs = 30000;

chanMapName = sprintf('chanMap_3A_%dsites.mat', NchanTOT);

save(fullfile(fpath, chanMapName), 'chanMap', 'connected', 'xcoords', 'ycoords', 'kcoords', 'fs', 'NchanTOT' )


function h = label(A, B, C)
% TERNARY.LABEL label ternary phase diagram
%   TERNARY.LABEL('ALABEL', 'BLABEL', 'CLABEL') labels a ternary phase diagram created using TERNARY.PLOT
%
%   H = TERNARY.LABEL('ALABEL', 'BLABEL', 'CLABEL') returns handles to the
%   text objects created with the labels provided.  LaTeX escape codes are accepted.
%
%   See also TERNARY.PLOT

% Author: Carl Sandrock 20020827
% Modifications: FVA

% To Do

% Modifications

% Modifiers

% r(1) = text(0.5, -0.05, A, 'horizontalalignment', 'center');
% r(2) = text(1-0.45*sin(deg2rad(30)), 0.5, B, 'rotation', -60, 'horizontalalignment', 'center');
% r(3) = text(0.45*sin(deg2rad(30)), 0.5, C, 'rotation', 60, 'horizontalalignment', 'center');
r(1) = text(0.5, -0.07, A, 'horizontalalignment', 'center','Interpreter','latex');
r(2) = text(1-0.42*sin(deg2rad(30)), 0.5, B, 'rotation', -60, 'horizontalalignment', 'center','Interpreter','latex');
r(3) = text(0.40*sin(deg2rad(30)), 0.5, C, 'rotation', 60, 'horizontalalignment', 'center','Interpreter','latex');
if nargout > 0
    h = r;
end;


function vector = huff2norm(zipped,info)
%HUFF2NORM   Huffman codification (decoder)
%   For vectors, HUFF2NORM(X,INFO) returns a decoded vector from a Huffman coded version X
%   with code words given by INFO.
%   For matrices, X(:) is used as input.
%
%   Input X must be of uint8 type, while the output is a uint8 array.
%
%   For more details about how is INFO structured, see NORM2HUFF help.
%
%   See also NORM2HUFF

%   $Author: Giuseppe Ridino' $
%   $Revision: 1.0 $  $Date: 10-May-2004 15:03:04 $

% ensure to handle uint8 input vector
if ~isa(zipped,'uint8'),
	error('input argument must be a uint8 vector')
end

% create the 01 sequence
len = length(zipped);
string = repmat(uint8(0),1,len.*8);
bitindex = 1:8;
for index = 1:len,
	string(bitindex+8.*(index-1)) = uint8(bitget(zipped(index),bitindex));
end

% adjust string
string = logical(string(:)'); % make a row of it
len = length(string);
string((len-info.pad+1):end) = []; % remove 0 padding
len = length(string);

% build output
weights = 2.^(0:51);
vector = repmat(uint8(0),1,info.length);
vectorindex = 1;
codeindex = 1;
code = 0;
for index = 1:len,
	code = bitset(code,codeindex,string(index));
	codeindex = codeindex+1;
	byte = decode(bitset(code,codeindex),info);
	if byte>0, % a code has been found
		vector(vectorindex) = byte-1;
		codeindex = 1;
		code = 0;
		vectorindex = vectorindex+1;
	end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function byte = decode(code,info)
byte = info.huffcodes(code);


%TIME_8
%
%   The purpose of this test is to determine whether it pays of to do
%   special optimizations for p=1 q=2 and p=2 q=3%

Lr=[480000*sf^2,262144*sf^2,900*sf^2];
ar=[     600*sf,        512,       2];
Mr=[     800*sf,       1024,  600*sf];

for ii=1:length(Lr)

  L=Lr(ii);

  M=Mr(ii);
  a=ar(ii);

  g=rand(L,1);
  f=rand(L,1);

  [L, a, M]

  gf=comp_wfac(g,a,M);

  c1=mex_dgt_fac_1(f,gf,a,M,1);

  % This is an attempt of cacheflushing
  f=rand(L,1); gf=rand(size(gf));

  c2=mex_dgt_fac_7(f,gf,a,M,1);

end;


 function [C, ugibb] = Csparse(arg0, arg1, arg2, arg3)
%function C = Csparse('b2info', kappa, mask, chat)
%function C = Csparse('center', Cscale, mask, chat)
%function C = Csparse('maskleak', mask, chat)
%function C = Csparse('maskleak3', [beta_xy beta_z], mask, chat)
%function C = Csparse('maskbar', mask, chat)
%function C = Csparse('test')
%function Csparse('plot', C, mask)
% create 2D (or 1D or 3D) penalty matrix C
% R = beta * C'C is the penalty Hessian (in quadratic case)
% in
%	kappa	[nx,ny]		as in :srp paper, i.e.
%				sqrt( (A.^2)'*nder2(:) ./ sum(A.^2)' )
%	mask	[nx,ny[,nz]]	support
% out
%	C	[nc,np]		np = # of pixels in mask
%
% maskleak: penalize across mask boundary, like in ASPIRE (C*1 != 0)
%	(matches "-" penalty in ASPIRE)
% maskbar: barrier across mask boundary (C*1 = 0)
% b2info: as in 'b2info' penalty in ASPIRE
% center: as in 'center' penalty in ASPIRE

if nargin == 1 && streq(arg0, 'test'), Csparse_test, return, end
if nargin < 1, ir_usage, end

warning 'Csparse is obsolete.  C2sparse is recommended instead!'

betas = 1;

if strcmp(arg0, 'maskleak3')
	Cscale = 1;
	mask = arg2 ~= 0;
	betas = arg1;
	chat = 0;		if 1==exist('arg3'), chat = arg3; end

elseif strcmp(arg0, 'maskleak') || strcmp(arg0, 'maskbar')
	Cscale = 1;
	mask = arg1 ~= 0;
	chat = 0;		if 1==exist('arg2'), chat = arg2; end

elseif strcmp(arg0, 'center')
	Cscale = arg1;
	mask = arg2 ~= 0;
	chat = 0;		if 1==exist('arg3'), chat = arg3; end
	arg0 = 'maskleak';	% trick!

elseif strcmp(arg0, 'b2info')
	kappa = arg1;
	mask = kappa~=0;	if 1==exist('arg2'), mask = arg2; end
	chat = 0;		if 1==exist('arg3'), chat = arg3; end
	if ~islogical(mask), error 'mask must be logical', end
	if any(mask(:,1)) || any(mask(:,end)) || any(mask(1,:)) || any(mask(end,:))
		error 'mask must not include outer row/col'
	end

%
% test by calculating penalty for an image here and in aspire
%
elseif streq(arg0, 'test')
	nx = 20; ny = 12;	nb = nx+1; na = ny;
	mask = zeros(nx,ny);	mask(4:14,4:8) = ones(11,5);
	mask = mask ~= 0;

	A = [spdiag(mask(:), 'nowarn'); zeros(ny,nx*ny)];
	rng(0)
	yy = rand(nb,na);
	xx = mask .* rand(nx,ny);
	dd = 1 + 1 * rand(nb,na);
	kappa = mask+0;
	jb = mask(:);
	kappa(jb) = sqrt( (A(:,jb).^2)'*dd(:) ./ sum(A(:,jb).^2)' );

whos
error('hmm')
	delete('t,A.wtf')
	wtf_write('t,A.wtf', A, nx, ny, nb, na)
	save('t,d', 'dd')
	save('t,y', 'yy')
	save('t,x', 'xx')

	err = yy(:)-A*xx(:);
%	C = Csparse('maskleak', mask);		method = '@1@alg@0,quad,1,-'
	C = Csparse('b2info', kappa, mask, 1);	method = '@1@alg@0,quad,1,b2info'

	printm('like=%.11e penal=%.11e', ...
		err' * spdiag(dd, 'nowarn') * err / 2, norm(C * xx(jb)).^2 / 2)
	eval(['!i best,pwls2 - t,x.mat t,y.mat - t,d.mat t,A.wtf ' method])
	C = 0;
	return

elseif strcmp(arg0, 'plot')
	C = arg1;
	mask = arg2 ~= 0;
%	im(221, kappa, 'kappa')
	if im
		subplot(338), spy(C), title 'C'
	end
	if ncol(C) < 500
		im(339, C'*C, 'C''C')
	else
		im(339, embed(diag(C'*C), mask), 'Diag(C''C)'), cbar
	end
	return

else
	help Csparse
	error([arg0 ' unknown'])
end

	[nx,ny,nz] = size(mask);

%
% C for 1st-order differences
%
if 1
	dx	= sparse(filtmat('1d', [-1 1 0], nx));
	if ~strcmp(arg0, 'maskleak')
		dx(1,1) = 0;
	end
%	dx	= dx(2:end,:);
	dx	= kron(speye(ny*nz), dx);
	C	= dx;	clear dx
	if ny > 1
		dy	= sparse(filtmat('1d', [-1 1 0], ny));
		dy(1,1) = 0;
%		dy	= dy(2:end,:);
		if nz > 1
			dy	= kron(speye(nz), kron(dy, speye(nx)));
		else
			dy	= kron(dy, speye(nx));
		end
		b.xy = betas(1);
		C	= sqrt(b.xy) * [C; dy];	clear dy
	end
	if nz > 1
		dz	= sparse(filtmat('1d', [-1 1 0], nz));
%		dz	= dz(2:end,:);
		dz(1,1) = 0;
		dz	= kron(dz, speye(nx*ny));
		b.z = betas(2);
		C	= [C; sqrt(b.z)*dz];	clear dz
	end
end

if strcmp(arg0, 'maskleak') || strcmp(arg0, 'maskleak3')
	C = Cscale * C(:, mask(:));
	if chat
		Csparse('plot', C, mask);
	end
	return
end

% only rows (and cols) of C where all elements are in mask
if strcmp(arg0, 'maskbar')
	t = abs(C) * mask(:);
	t = full(t == sum(abs(C)')');
	C = C(t,:);
	C = C(:,mask(:));
	return
end

if ~strcmp(arg0, 'b2info'),	error bug, end

	if any(kappa(~mask(:))), error('kappa outside of mask'), end

	%
	% 'fix edges' of kappa, as in ASPIRE
	%
	offset = [-1 1 -nx nx nx-1 nx+1 1-nx -1-nx];
	for jj = find(mask(:))'
		bad = offset(mask(jj + offset) == 0);
		if bad % ~= []
			kappa(jj + bad) = kappa(jj) * ones(size(bad));
		end
	end

	t = abs(C) * spdiag(1:nx*ny, 'nowarn');
	t2 = max(t')';
	t1 = sum(t')' - t2;	% trick works with 1st order only!
	if any(t1 == t2 | t1 <= 0 | t2 <= 0)
		if sum(t1 == 0) ~= nx + ny, error bug_t1, end
		if sum(t2 == 0) ~= nx + ny, error bug_t2, end
		if any(t1(t1 ~= 0) == t2(t2 ~= 0)), error bug_t12, end
		tdiag = zeros(nx*ny,1);
		tdiag(t1 ~= 0) = sqrt(kappa(t1(t1 ~= 0)) .* kappa(t2(t1 ~= 0)));
		% all the above because now we keep zero rows in C...
	else
		tdiag = sqrt(kappa(t1) .* kappa(t2));
	end
	C = spdiag(tdiag, 'nowarn') * C;

	C = C(:,mask(:));

	if chat
		Csparse('plot', C, mask);
	end

	if nargout > 1
		ugibb.x = zeros(nx,ny);
		ugibb.y = zeros(nx,ny);
		ugibb.p = zeros(nx,ny);
		ugibb.n = zeros(nx,ny);

		t = (t2 == t1 + 1) & (tdiag ~= 0);
		sum(t)
		ugibb.x(t2(t)) = tdiag(t);

		t = (t2 == t1 + nx) & (tdiag ~= 0);
		sum(t)
		ugibb.y(t2(t)) = tdiag(t);
		if chat
			im([ugibb.x ugibb.y])
		end
	end

%
% self test
%
function Csparse_test
% run test with 'kappa' for b2info
x = shepplogan(64,64,1); x = x / max(x(:));
mask = true(size(x));
mask([1 end],:) = false; mask(:,[1 end]) = false;
im pl 2 2
im(1, mask+x, 'mask + x'), cbar

kappa = 2 - double(x > 0.5);
kappa = mask .* kappa;
im(2, kappa, 'kappa'), cbar

C = Csparse('b2info', kappa, mask, 0);
if im, im subplot 3, spy(C), end

tmp = embed(sum(abs(C))', mask);
im(4, tmp, '|C|''1'), cbar


function [uOutput] = scecdcimp(nFunction, sFilename)

% Filter function switchyard
if nFunction == 0     % Return info about filter
  uOutput = 'NIED (Kanto-Tokai) catalog with focal mechanism';
elseif nFunction == 2 % Return filename of help-file
  uOutput = 'import_NIED_doc.html';
elseif nFunction == 1 % Import and return catalog
  % Read formated data
  mData = textread(sFilename, '%s', 'delimiter', '\n', 'whitespace', '');
  % Create empty catalog
  uOutput = zeros(length(mData), 13);
  % Loop thru all lines of catalog and convert them
  for i = 1:length(mData)
    if rem(i,100) == 0 ; disp([ num2str(i) ' of ' num2str(length(mData)) ' events processed ']); end
    try
      l = find(mData{i} == ' ');
      mData{i}(l) = '0';
      uOutput(i,1) = str2num(mData{i}(37:44));  % Longitude
      uOutput(i,2) =  str2num(mData{i}(29:35)); % Latitude
      uOutput(i,3) = str2num(mData{i}(8:11));   % Year
      uOutput(i,4) = str2num(mData{i}(12:13));  % Month
      uOutput(i,5) = str2num(mData{i}(14:15));  % Day
      uOutput(i,6) = str2num(mData{i}(52:55));  % Magnitude
      uOutput(i,7) = str2num(mData{i}(46:50));  % Depth
      uOutput(i,8) = str2num(mData{i}(17:18));  % Hour
      uOutput(i,9) = str2num(mData{i}(20:21));  % Minute

      uOutput(i,10) = str2num(mData{i}(113:120));  % strike of nodal plane 1
      uOutput(i,11) = str2num(mData{i}(122:129));  % dip of nodal plane
      uOutput(i,12) = str2num(mData{i}(131:138));  % rake of nodal plane 1

      uOutput(i,13) = str2num(mData{i}(107:111));  % quality

      uOutput(i,14) = str2num(mData{i}(140:147));  % strike of nodal plane 2
      uOutput(i,15) = str2num(mData{i}(149:156));  % dip of nodal plane 2
      uOutput(i,16) = str2num(mData{i}(158:165));  % rake of nodal plane 2

      %Create decimal year
      uOutput(i,3) = decyear([uOutput(i,3) uOutput(i,4) uOutput(i,5) uOutput(i,8) uOutput(i,9)]);
    catch
      disp(['Import: Problem in line ' num2str(i) ' of ' sFilename '. Line ignored.']);
      uOutput(i,:) = uOutput(i,:)*nan;
    end
  end
  l = isnan(uOutput(:,1));
  uOutput(l,:) = [];
end


%%
 %  Copyright (c) 2014, Facebook, Inc.
 %  All rights reserved.
 %
 %  This source code is licensed under the BSD-style license found in the
 %  LICENSE file in the root directory of this source tree. An additional grant
 %  of patent rights can be found in the PATENTS file in the same directory.
 %
 %%
 function level2_features = generate_level2_features_cnn(level1_features, gfeatures, config)
 %combine deep learning features/scores for a second level feature
 %is_score = 1 means combining scores, otherwise combining the features.
 assert(~isempty(level1_features));

 basis = size(level1_features{1}.features,2);
 is_score = (basis==1);
 num_poselets = length(level1_features);

 num_examples = size(gfeatures,1);

 level2_features = zeros(num_examples, num_poselets*basis, 'single');

 for i = 1:num_poselets
     %if features is empty, meaning no cnn model of this poselet
     if isempty(level1_features{i})
         continue;
     end

     if is_score
         level2_features(level1_features{i}.used,i) = (level1_features{i}.features-0.5);
     else
         level2_features(level1_features{i}.used,(i-1)*basis+1:i*basis) = level1_features{i}.features;
     end
 end

 switch config.CNN_MODEL_TYPE
     case 'combined'
         level2_features = [level2_features gfeatures];
         fprintf('Combined model\n');
     case 'holistic'
         level2_features = gfeatures;
         fprintf('Global-only model\n');
     case 'parts'
         fprintf('Parts-only model\n');
 end

 %normalization
 switch config.CNN_NORMALIZATION_TYPE
     case 'max'
         fprintf('Normalization: max\n');
         level2_features = level2_features ./repmat(max(level2_features,[],2),1,size(level2_features,2));
     case 'L1'
         fprintf('Normalization: L1\n');
         level2_features = level2_features ./repmat(sum(abs(level2_features),2),1,size(level2_features,2));
     case 'power'
         ppp = 0.3;
         for i = 1:size(level2_features,1)
             level2_features(i,:) = sign(level2_features(i,:)).*abs(level2_features(i,:)).^ppp;
         end
         fprintf('Normalization: power %f\n',ppp);
 end
 assert(~any(isnan(level2_features(:))));
 end


function midi=matrix2midi(M,ticks_per_quarter_note,timesig)
% midi=matrix2midi(M,ticks_per_quarter_note)
%
% generates a midi matlab structure from a matrix
%  specifying a list of notes.  The structure output
%  can then be used by writemidi.m
%
% M: input matrix:
%   1     2    3  4   5  6
%  [track chan nn vel t1 t2] (any more cols ignored...)
%
% optional arguments:
% - ticks_per_quarter_note: integer (default 300)
% - timesig: a vector of len 4 (default [4,2,24,8])
%

% Copyright (c) 2009 Ken Schutte
% more info at: http://www.kenschutte.com/midi

% TODO options:
%  - note-off vs vel=0
%  - tempo, ticks, etc

if nargin < 2
  ticks_per_quarter_note = 300;
end

if nargin < 3
  timesig = [4,2,24,8];
end

tracks = unique(M(:,1));
Ntracks = length(tracks);

% start building 'midi' struct

if (Ntracks==1)
  midi.format = 0;
else
  midi.format = 1;
end

midi.ticks_per_quarter_note = ticks_per_quarter_note;

tempo = 500000;   % could be set by user, etc...
% (microsec per quarter note)

for i=1:Ntracks

  trM = M(tracks(i)==M(:,1),:);

  note_events_onoff = [];
  note_events_n = [];
  note_events_ticktime = [];

  % gather all the notes:
  for j=1:size(trM,1)
    % note on event:
    note_events_onoff(end+1)    = 1;
    note_events_n(end+1)        = j;
    note_events_ticktime(end+1) = 1e6 * trM(j,5) * ticks_per_quarter_note / tempo;

    % note off event:
    note_events_onoff(end+1)    = 0;
    note_events_n(end+1)        = j;
    note_events_ticktime(end+1) = 1e6 * trM(j,6) * ticks_per_quarter_note / tempo;
  end

  msgCtr = 1;

  % set tempo...
  midi.track(i).messages(msgCtr).deltatime = 0;
  midi.track(i).messages(msgCtr).type = 81;
  midi.track(i).messages(msgCtr).midimeta = 0;
  midi.track(i).messages(msgCtr).data = encode_int(tempo,3);
  midi.track(i).messages(msgCtr).chan = [];
  msgCtr = msgCtr + 1;

  % set time sig...
  midi.track(i).messages(msgCtr).deltatime = 0;
  midi.track(i).messages(msgCtr).type = 88;
  midi.track(i).messages(msgCtr).midimeta = 0;
  midi.track(i).messages(msgCtr).data = timesig(:);
  midi.track(i).messages(msgCtr).chan = [];
  msgCtr = msgCtr + 1;

  [junk,ord] = sort(note_events_ticktime);

  prevtick = 0;
  for j=1:length(ord)

    n = note_events_n(ord(j));
    cumticks = note_events_ticktime(ord(j));

    midi.track(i).messages(msgCtr).deltatime = cumticks - prevtick;
    midi.track(i).messages(msgCtr).midimeta = 1;
    midi.track(i).messages(msgCtr).chan = trM(n,2);
    midi.track(i).messages(msgCtr).used_running_mode = 0;

    if (note_events_onoff(ord(j))==1)
      % note on:
      midi.track(i).messages(msgCtr).type = 144;
      midi.track(i).messages(msgCtr).data = [trM(n,3); trM(n,4)];
    else
      %-- note off msg:
      %midi.track(i).messages(msgCtr).type = 128;
      %midi.track(i).messages(msgCtr).data = [trM(n,3); trM(n,4)];
      %-- note on vel=0:
      midi.track(i).messages(msgCtr).type = 144;
      midi.track(i).messages(msgCtr).data = [trM(n,3); 0];
    end
    msgCtr = msgCtr + 1;

    prevtick = cumticks;
  end

  % end of track:
  midi.track(i).messages(msgCtr).deltatime = 0;
  midi.track(i).messages(msgCtr).type = 47;
  midi.track(i).messages(msgCtr).midimeta = 0;
  midi.track(i).messages(msgCtr).data = [];
  midi.track(i).messages(msgCtr).chan = [];
  msgCtr = msgCtr + 1;

end

% return a _column_ vector
% (copied from writemidi.m)
function A=encode_int(val,Nbytes)

A = zeros(Nbytes,1);  %ensure col vector (diff from writemidi.m...)
for i=1:Nbytes
  A(i) = bitand(bitshift(val, -8*(Nbytes-i)), 255);
end


close all
clear
clc
graphics_toolkit qt

h.user = 'username';
h.ii = 1;
h.measurements = {};
h.measurements(1, :) = {'Measurement', 'Frequency', 'Amplitude', 'Phase'};
h.freqs = [177, 297, 500, 841, 1414, 2378, 4000, 6727, 11314];

h.gui = figure('Position', [10 10 2000 1200],
       'NumberTitle', 'off',
       'Name', 'Comb-filter phase measurement GUI',
       'toolbar', 'none',
       'menubar', 'none');

% Selection of the pure tone frequency and adaption of the figure axes
function set_freq(obj)
  h = guidata(obj);
  string = get(obj, 'string');
  h.f = str2num(string);
  set(h.dot,'visible','off')
  h.dot = plot(0, 0, 'o', 'color', 'r', 'markersize', 1);
  guidata(obj, h);
end

% Callback for click in the figure
function clickcallback(obj)
  h = guidata(obj);
  axesHandle  = get(obj, 'Parent');
  coordinates = get(gca, 'CurrentPoint');
  coordinates = coordinates(1, 1:2);
  [x_max, y_max] = size(get(obj, 'cdata'));
  y = coordinates(2);
  coordinates(2) = abs((coordinates(2) - 128));
  h.x = coordinates(1);
  h.y = coordinates(2)./128;
  guidata(obj, h);
  coord2param(obj);
  h = guidata(obj);
  h.fs = 48000;
  delay = 1+h.phi/(2*pi).*h.fs./h.f;
  b = zeros(1, 100);
  b(round(delay)) = h.a;
  system(['echo mha.transducers.mhachain.injector.B = [' sprintf('%.15f ',b) '] | nc -w 1 127.0.0.1 33337']);
  hold all
  delete(h.dot);
  h.dot = plot(h.x, y, 'o', 'markerfacecolor', 'r', 'markeredgecolor', 'r', 'markersize', 8);
  drawnow;
  guidata(obj, h);
end

% Convert input values to amplitude (a) and phase (phi)
function coord2param(obj)
  h = guidata(obj);
  h.a = h.y;
  h.phi = (5/2*pi)/160.*h.x;
  guidata(obj, h);
  amplification_rule(obj);
  h = guidata(obj);
  guidata(obj, h);
end

% Scale amplitude
function amplification_rule(obj)
  h = guidata(obj);
  switch h.f
    case 177
      h.a = 10.^((h.a*30-30)/20); % 0 dB to -30 dB
    case 297
      h.a = 10.^((h.a*30-30)/20); % 0 dB to -30 dB
    case 500
      h.a = 10.^((h.a*30-30)/20); % 0 dB to -30 dB
    case 841
      h.a = 10.^((h.a*30-35)/20); % -5 dB to -35 dB
    case 1414
      h.a = 10.^((h.a*35-45)/20); % -10 dB to -40 dB
    case 2378
      h.a = 10.^((h.a*35-50)/20); % -15 dB to -45 dB
    case 4000
      h.a = 10.^((h.a*30-30)/20); %  -5 dB to -35 dB  (0 dB to -30 dB)
    case 6727
      h.a = 10.^((h.a*30-35)/20); %  -5 dB to -35 dB
    case 11314
      h.a = 10.^((h.a*30-35)/20); %  -5 dB to -35 dB
  endswitch
  guidata(obj, h);
end

% Saving of the data
function finish(obj)
  h = guidata(obj);
  h.measurements(end+1, :) = {h.ii, h.f, h.a, h.phi};
  progress = [num2str(h.ii), ' out of 9 complete'];
  set(h.measurement_complete, 'string', progress);
  if h.ii == 9 % # of measurements
    set(h.measurement_complete, 'string', 'Measurement complete!');
    filename = num2str([h.user, '.csv']);
    cell2csv(filename, h.measurements)
  end
  h.ii = h.ii + 1;
  guidata(obj, h);
end

% Toggle between amplification and no amplification
function play_reference(obj)
  h = guidata(obj);
  b = zeros(1, 100);
  string = get(obj, 'string');
  switch string
    case 'Amp off'
      system(['echo mha.transducers.mhachain.injector.B = [' sprintf('%.15f ',b) '] | nc -w 1 127.0.0.1 33337']);
    case 'Amp on'
      delay = 1+h.phi/(2*pi).*h.fs./h.f;
      b(round(delay)) = h.a;
      system(['echo mha.transducers.mhachain.injector.B = [' sprintf('%.15f ',b) '] | nc -w 1 127.0.0.1 33337']);
  endswitch
  guidata(obj, h);
end

% Keyboard event detection
function keyPress(obj, e)
  h = guidata(obj);
  switch e.Key
    case '1'
      play_reference(h.reference, [], []);
    case '2'
      play_reference(h.adjusted, [], []);
  endswitch
  guidata(obj);
end

h.finish = uicontrol('style', 'pushbutton',
  'units', 'normalized',
  'string', 'Save',
  'fontsize', 18,
  'fontweight', 'bold',
  'callback', @finish,
  'position', [0.75 0.1 0.2 0.1]);

h.amplification_buttons = uibuttongroup('position', [0.75 0.35 0.2 0.1]);
h.amp_off = uicontrol(h.amplification_buttons, 'style', 'radiobutton',
  'units', 'normalized',
  'string', 'Amp off',
  'fontsize', 10,
  'callback', @play_reference,
  'position', [0.1 0.4 0.3 0.2]);
h.amp_on = uicontrol(h.amplification_buttons, 'style', 'radiobutton',
  'units', 'normalized',
  'string', 'Amp on',
  'fontsize', 10,
  'callback', @play_reference,
  'position', [0.6 0.4 0.3 0.2]);

h.freq_buttons = uibuttongroup('position', [0.75 0.475 0.09 0.3]);
h.freq_177 = uicontrol(h.freq_buttons, 'style', 'radiobutton',
  'units', 'normalized',
  'selected', 'on',
  'string', '177',
  'fontsize',10,
  'fontweight','bold',
  'callback', @set_freq,
  'position', [0.05 0.85 0.9 0.05]);
h.freq_297 = uicontrol(h.freq_buttons, 'style', 'radiobutton',
  'units', 'normalized',
  'string', '297',
  'fontsize',10,
  'fontweight','bold',
  'callback', @set_freq,
  'position', [0.05 0.75 0.9 0.05]);
h.freq_500 = uicontrol(h.freq_buttons, 'style', 'radiobutton',
  'units', 'normalized',
  'string', '500',
  'fontsize',10,
  'fontweight','bold',
  'callback', @set_freq,
  'position', [0.05 0.65 0.9 0.05]);
h.freq_841 = uicontrol(h.freq_buttons, 'style', 'radiobutton',
  'units', 'normalized',
  'string', '841',
  'fontsize', 10,
  'fontweight','bold',
  'callback', @set_freq,
  'position', [0.05 0.55 0.9 0.05]);
h.freq_1414 = uicontrol(h.freq_buttons, 'style', 'radiobutton',
  'units', 'normalized',
  'string', '1414',
  'fontsize',10,
  'fontweight','bold',
  'callback', @set_freq,
  'position', [0.05 0.45 0.9 0.05]);
h.freq_2378 = uicontrol(h.freq_buttons, 'style', 'radiobutton',
  'units', 'normalized',
  'string', '2378',
  'fontsize',10,
  'fontweight','bold',
  'callback', @set_freq,
  'position', [0.05 0.35 0.9 0.05]);
h.freq_4000 = uicontrol(h.freq_buttons, 'style', 'radiobutton',
  'units', 'normalized',
  'string', '4000',
  'fontsize',10,
  'fontweight','bold',
  'callback', @set_freq,
  'position', [0.05 0.25 0.9 0.05]);
h.freq_6727 = uicontrol(h.freq_buttons, 'style', 'radiobutton',
  'units', 'normalized',
  'string', '6727',
  'fontsize',10,
  'fontweight','bold',
  'callback', @set_freq,
  'position', [0.05 0.15 0.9 0.05]);
h.freq_11314 = uicontrol(h.freq_buttons, 'style', 'radiobutton',
  'units', 'normalized',
  'string', '11314',
  'fontsize',10,
  'fontweight','bold',
  'callback', @set_freq,
  'position', [0.05 0.05 0.9 0.05]);

h.measurement_complete = uicontrol('style', 'text',
  'units', 'normalized',
  'string', '',
  'fontsize', 11,
  'fontweight', 'bold',
  'position', [0.75 0.225 0.2 0.1]);

img = imread('grid_extended.png');
min_x = 0;
max_x = 160;
min_y = 0;
max_y = 128;

fig_pos_click = {[0.05 0.1 0.65 0.8]};
h.ax_click = axes('position', fig_pos_click{});
box('on')
h_image = imagesc([min_x max_x], [min_y, max_y], img);

set(gca, 'xtick', [0 32 64 96 128 160]);
set(gca, 'xticklabel', {'0', '^{\pi}/_{2}', '\pi', '^{3\pi}/_{2}', '2\pi', '^{5\pi}/_{2}'});
set(gca, 'ytick', [0 64/3 128/3 64 256/3 320/3 128]);
set(gca, 'yticklabel', {'+15', '+10', '+5', '0', '-5', '-10', '-15'});
set(gca, 'linewidth', 3, 'fontsize', 20);
hold all

h.dot = plot(0, 0, 'o', 'color', 'r', 'markersize', 1);

set(h_image,'ButtonDownFcn',@clickcallback);
set(gcf, 'KeyPressFcn', @keyPress);
guidata(gcf, h);


function [satclk0,varc0,stat]=pephclk(time,sat,nav,satclk0,varc0)

global glc
stat=1;

if nav.nc<2||timediff(time,nav.peph(1).time)<-900||...
        timediff(time,nav.peph(end).time)>900
    return;
end

%binary search
i=0; j=nav.nc-1;
while i<j
    k=fix((i+j)/2);
    if timediff(nav.pclk(k+1).time,time)<0
        i=k+1;
    else
        j=k;
    end
end

if i<=0,idx=0; else,idx=i-1; end
idx=idx+1;

%linear interpolation for clock
t0=timediff(time,nav.pclk(idx).time);
t1=timediff(time,nav.pclk(idx+1).time);
c0=nav.pclk(idx).clk(sat);
c1=nav.pclk(idx+1).clk(sat);

if t0<=0
    satclk=c0;
    if satclk==0,stat=0;return;end
    varc=(nav.pclk(idx).std(sat)*glc.CLIGHT-1E-3*t0)^2;
elseif t1>=0
    satclk=c1;
    if satclk==0,stat=0;return;end
    varc=(nav.pclk(idx+1).std(sat)*glc.CLIGHT+1E-3*t1)^2;
elseif c0~=0 && c1~=0
    satclk=(c1*t0-c0*t1)/(t0-t1);
    if abs(t0)-abs(t1)>0
        varc=(nav.pclk(idx+1).std(sat)*glc.CLIGHT+1E-3*abs(t1))^2;
    else
        varc=(nav.pclk(idx).std(sat)*glc.CLIGHT+1E-3*abs(t0))^2;
    end
else
    stat=0; return;
end

if satclk~=0
    satclk0=satclk;
    varc0=varc;
end

return


function EarTraining()

%Richard Moore
%November 18, 2012

%This program is intended to test/train for perfect pitch.  Currently my
%plan is to:
%-Start with just a couple of pitches, probably C and G.
%-Play the tones in different octaves and have the observer guess the
%pitch.
%-As proficiency is demonstrated within a moving average, add the next note
%in the circle of fifths.
%-Also, when a mistake is made, notify the user that they have made a
%mistake and let them know what the correct note name is.

%C, G, D, A, E, B, F#, C#, G#, Eb, Bb, F
NoteNames = {'C'; 'G'; 'D'; 'A'; 'E'; 'B'; 'F#'; 'C#'; 'G#'; 'Eb'; 'Bb'; 'F'};
%Row = presented
%Column = responded
ResponseMat = zeros(12,12);
A = 440;
halfStep = 2^(1/12);
baseFreqs = [A*halfStep^3; A*halfStep^(-2); A*halfStep^5; A; A*halfStep^7; A*halfStep^2; A*halfStep^9; A*halfStep^4; A*halfStep^(-1); A*halfStep^6; A*halfStep^1; A*halfStep^8];
%numPitches = 8;
AccThresh = 0.9;
MinNumTrials = 20;

disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');
disp('EAR TRAINING FOR PITCH RECOGNITION');
disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');
disp(' ');
disp('You will hear notes and be asked to identify them.');
disp(['Once you have correctly identified at least ' num2str(AccThresh*100) '% of ' num2str(MinNumTrials) ' trials, another note will be added.']);
numPitches = input('How many pitches would you like to start out with? ');
disp(['Very weel, then.  You will be starting with ' num2str(numPitches) ' different notes.']);
disp([NoteNames{1:numPitches}]);
disp(' ');
pause(3);
disp('May the force be with you.');
pause(1);
clc;
%So, if the probability exceeds a threshold, add another pitch.
trialNum = 0;
testFlag = 1;
sampRate = 44100;
noteLength = 0.5; %In seconds
while testFlag
    %
    trialNum = trialNum + 1;
    %Present the pitch:
    oct = ceil(rand*4)-2;
    currPitch = ceil(rand*numPitches);
    soundsc(sin(2*pi*(2^oct)*baseFreqs(currPitch)*[0:(1/sampRate):noteLength]),sampRate);
    %Take the response:
    resp = input('Name that note: ','s');
    %This won't work quite yet.  The strings are not all the same length.
    switch resp
        case {'c','C'}
            resp2 = 1;
        case {'g','G'}
            resp2 = 2;
        case {'d','D'}
            resp2 = 3;
        case {'a','A'}
            resp2 = 4;
        case {'e','E'}
            resp2 = 5;
        case {'b','B'}
            resp2 = 6;
        case {'f#','F#','gb','Gb'}
            resp2 = 7;
        case {'c#','C#','db','Db'}
            resp2 = 8;
        case {'g#','G#','ab','Ab'}
            resp2 = 9;
        case {'eb','Eb','d#','D#'}
            resp2 = 10;
        case {'bb','Bb','a#','A#'}
            resp2 = 11;
        case {'f','F'}
            resp2 = 12;
        otherwise
            disp('Ummm, I think that you just made up a new note.');
    end
    %Populate the confusion matrix:
    ResponseMat(currPitch,resp2) = ResponseMat(currPitch,resp2) + 1;
    %Notify the user of a mistake, if applicable:
    if currPitch~=resp2
        disp(['Whoopsie-daisies!  That was actually ' NoteNames{currPitch}]);
    end
    %If skill level is mastered, increase numPitches and notify user:
    currNum = sum(sum(ResponseMat.*eye(size(ResponseMat,1))));
    currDen = sum(ResponseMat(:));
    %Require a minimum score of 90% and a minimum number of 10 trials
    if (currDen>=MinNumTrials)&&((currNum/currDen)>=AccThresh)
        numPitches = numPitches + 1;
        %disp(ResponseMat);
        clc;
        disp('%%%%%%%%%%%%%%%%%%%%');
        disp('Congrats!  You just leveled up!');
        disp(['You now have to identify ' num2str(numPitches) ' different notes: ' NoteNames{1:numPitches}]);
        disp('%%%%%%%%%%%%%%%%%%%%');
        ResponseMat = zeros(12,12);
    end
    if rem(trialNum,10)==0
        %disp(ResponseMat);
        disp(['Since the last level-up your accuracy has been ' num2str(100*currNum/currDen) '%.']);
        testFlag = input('Wanna keep testing? 1=yes, 0=no: ');
    end
end

clc;
disp('Sure, go do something else.');
disp(' ');
pause(1);
disp('I''m sure that Netflix is more important than becoming a ear-training-jedi-master.');
pause(3);


function [X,Y,Z] = elec_cm2m(X,Y,Z)

% elec_cm2m - Convert electrode coordinates from centimeters to meters
%
%   [X,Y,Z] = elec_cm2m(X,Y,Z)
%
%   Simply:  X = X / 100;  Y = Y / 100;  Z = Z / 100;
%

% $Revision: 1.1 $ $Date: 2009-04-28 22:13:54 $

% Licence:  GNU GPL, no implied or express warranties
% History:  07/2001, Darren.Weber_at_radiology.ucsf.edu
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    X = X / 100;  Y = Y / 100;  Z = Z / 100;


function test_suite = test_sphericalAngle
%TESTSPHERICALANGLE  One-line description here, please.
%
%   output = testSphericalAngle(input)
%
%   Example
%   testSphericalAngle
%
%   See also
%
%
% ------
% Author: David Legland
% e-mail: david.legland@grignon.inra.fr
% Created: 2011-06-21,    using Matlab 7.9.0.529 (R2009b)
% Copyright 2011 INRA - Cepia Software Platform.

test_suite = functiontests(localfunctions);

function testP2At100(testCase) %#ok<*DEFNU>

p2 = [1 0 0];
p1 = [0 1 0];
p3 = [0 0 1];

alpha = sphericalAngle(p1, p2, p3);

testCase.assertEqual(pi/2, alpha);

% try in the other direction
alpha = sphericalAngle(p3, p2, p1);

testCase.assertEqual(3*pi/2, alpha, 'AbsTol', 1e-4);

function testP2At010(testCase)

p1 = [1 0 0];
p2 = [0 1 0];
p3 = [0 0 1];

alpha = sphericalAngle(p1, p2, p3);

testCase.assertEqual(3*pi/2, alpha);

% try in the other direction
alpha = sphericalAngle(p3, p2, p1);

testCase.assertEqual(pi/2, alpha, 'AbsTol', 1e-4);

function testSpherical(testCase)

sph1 = [.1 0];
sph2 = [0 0];
sph3 = [0 .1];

alpha = sphericalAngle(sph1, sph2, sph3);
testCase.assertEqual(pi/2, alpha, 'AbsTol', 1e-4);


function [H, L] = storeR( F, levelF, o, H, L)
%------------------------------------------------------------------------------
%
% This function stores a two-dimensional gridfunction F into H for
% future extraction. Properties and location are stored in the book-
% keeping matrix L.
% This function is a two-dimensional lifting scheme utility.
%
% F = 2D gridfunction of coefficients
%
% levelF = integer designated as the level of F
%
% o = character, should be either 'a' or 'd',
%     describing the type of F:
%     'a' relates to approximation (coefficients) and
%     'd' relates to detail (coefficients)
%     other characters 'n' may be added to this list infuture versions
%
% L = 2D integer array of bookkeeping, its contents are updated by calls
%     to functions as storeQ, storeQ.... and does not require the
%     attention of the user.
%     L is organised as follows:
%       (for the j-th gridfunction F appended to H)
%       L(j, 1) = level of F as it has been assigned by the user.
%       L(j, 2) = 0 means that F corresponds to a rectangular domain,
%                   storeR always assigns this value. Functions like
%                   storeQ, storeQ.... assign values other than 0.
%       L(j, 3) = 0 stands for approximation type coefficients, 'a',
%                 1 stands for detail type coefficients, 'd'.
%       L(j, 4) = (integer) 1st dimension of gridfunction F.
%       L(j, 5) = (integer) 2nd dimension of gridfunction F.
%       L(j, 6) = integer that points to the end of the j-th
%                 addition to the heap.
%     With each call to storeR, storeQ, storeQ.... L obtains additional
%     row(s).
%
% H = 1D array that functions as storage (heap). The coefficients of F
%     are appended to H as a result of calling storeR. Bookkeeping is
%     maintained in L. No attention of the user is required.
%
% See also: retrieveR, storeQ
%
% Design and implementation by:
% Dr. Paul M. de Zeeuw <Paul.de.Zeeuw@cwi.nl>  http://homepages.cwi.nl/~pauldz/
% Last Revision: November 12, 2001.
% (c) 1999-2002 Stichting CWI, Amsterdam.
%------------------------------------------------------------------------------
addend = [0 0 0 0 0 0];
if isempty(L)
  if ~isempty(H)
    error(' storeR - books empty but heap is not ')
  else
    nL = 0;
%   mL = 6;
    heaptr = 1;
  end
else
  if isempty(H)
    error(' storeR - books not empty but heap is ')
  else
    [nL, mL] = size(L);
    if mL ~= 6
      error(' storeR - books do not fit ')
    else
      heaptr = L(nL, 6);
      if heaptr < 2
        error(' storeR - bookkeeping error ')
      end
    end
  end
end
%
L = [L; addend];
nL = nL + 1;
%
L(nL, 1) = levelF;
L(nL, 2) = 0;
switch o
    case 'a' , L(nL, 3) = 0;
    case 'd' , L(nL, 3) = 1;
%   case 'n' , L(nL, 3) = 2;
    otherwise
        error(' storeR - unknown type of coefficients ')
end
if isempty(F)
  error(' storeR - addition is empty ')
else
  [nF, mF] = size(F);
end
L(nL, 4) = nF;
L(nL, 5) = mF;
L(nL, 6) = heaptr + nF * mF;
%
H = addtoheap(F, H);
%------------------------------------------------------------------------------


function msm_to_mm_array_complex_general ( output_filename, a )

%*****************************************************************************80
%
%% MSM_TO_MM_ARRAY_COMPLEX_GENERAL writes a "matrix array complex general" Matrix Market file.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    01 November 2008
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, string OUTPUT_FILENAME, the name of the file to which the information
%    should be written.
%
%    Input, sparse matrix A, the NROW by NCOL matrix, stored in MATLAB sparse
%    matrix format, which is to be written to the file.
%
  [ nrow, ncol ] = size ( a );

  fid = fopen ( output_filename, 'wt+' );

  if ( fid < 0 );
    fprintf ( 1, '\n' );
    fprintf ( 1, 'MSM_TO_MM_ARRAY_COMPLEX_GENERAL - Fatal error!\n' );
    fprintf ( 1, '  Cannot open the output file.\n' );
    error ( 'MSM_TO_MM_ARRAY_COMPLEX_GENERAL - Fatal error!' );
  end

  fprintf ( fid, '%%%%MatrixMarket matrix array complex general\n');
  fprintf ( fid, '%%%%  Created by MSM_TO_MM_ARRAY_COMPLEX_GENERAL.M\n' );
  fprintf ( fid, '  %d  %d\n', nrow, ncol );

  for j = 1 : ncol
    for i = 1 : nrow
      fprintf ( fid, '  %f  %f\n', real ( a(i,j) ), imag ( a(i,j) ) );
    end
  end

  fclose ( fid );

  return
end


function [results, success, raw] = opf_execute(om, mpopt)
%OPF_EXECUTE  Executes the OPF specified by an OPF model object.
%   [RESULTS, SUCCESS, RAW] = OPF_EXECUTE(OM, MPOPT)
%
%   RESULTS are returned with internal indexing, all equipment
%   in-service, etc.
%
%   See also OPF, OPF_SETUP.

%   MATPOWER
%   Copyright (c) 2009-2020, Power Systems Engineering Research Center (PSERC)
%   by Ray Zimmerman, PSERC Cornell
%
%   This file is part of MATPOWER.
%   Covered by the 3-clause BSD License (see LICENSE file for details).
%   See https://matpower.org for more info.

%% define named indices into data matrices
[PQ, PV, REF, NONE, BUS_I, BUS_TYPE, PD, QD, GS, BS, BUS_AREA, VM, ...
    VA, BASE_KV, ZONE, VMAX, VMIN, LAM_P, LAM_Q, MU_VMAX, MU_VMIN] = idx_bus;
[GEN_BUS, PG, QG, QMAX, QMIN, VG, MBASE, GEN_STATUS, PMAX, PMIN, ...
    MU_PMAX, MU_PMIN, MU_QMAX, MU_QMIN, PC1, PC2, QC1MIN, QC1MAX, ...
    QC2MIN, QC2MAX, RAMP_AGC, RAMP_10, RAMP_30, RAMP_Q, APF] = idx_gen;
[F_BUS, T_BUS, BR_R, BR_X, BR_B, RATE_A, RATE_B, RATE_C, ...
    TAP, SHIFT, BR_STATUS, PF, QF, PT, QT, MU_SF, MU_ST, ...
    ANGMIN, ANGMAX, MU_ANGMIN, MU_ANGMAX] = idx_brch;

%%-----  setup  -----
%% options
dc  = strcmp(upper(mpopt.model), 'DC');
alg = upper(mpopt.opf.ac.solver);
switch alg
    case {'PDIPM', 'TRALM', 'MINOPF', 'SDPOPF'}
        legacy_solver = 1;
    otherwise
        legacy_solver = 0;
end
sdp = strcmp(alg, 'SDPOPF');
vcart = ~dc && mpopt.opf.v_cartesian;

%% get indexing
[vv, ll, nne, nni] = om.get_idx();

if mpopt.verbose > 0
    v = mpver('all');
    fprintf('\nMATPOWER Version %s, %s', v.Version, v.Date);
end

if dc
    %%-----  run DC OPF solver  -----
    if mpopt.verbose > 0
        fprintf(' -- DC Optimal Power Flow\n');
    end
    [results, success, raw] = dcopf_solver(om, mpopt);
else
    %%-----  run AC OPF solver  -----
    if mpopt.verbose > 0
        fprintf(' -- AC Optimal Power Flow\n  AC OPF formulation: ');
        if sdp
            fprintf('SDP relaxation\n');
        else
            if vcart
                v = 'cartesian';
            else
                v = 'polar';
            end
            if mpopt.opf.current_balance
                v2 = 'current';
            else
                v2 = 'power';
            end
            fprintf('%s voltages, %s balance eqns\n', v, v2);
        end
    end

    %% ZIP loads?
    if legacy_solver && ( ...
            (~isempty(mpopt.exp.sys_wide_zip_loads.pw) && ...
              any(mpopt.exp.sys_wide_zip_loads.pw(2:3))) || ...
            (~isempty(mpopt.exp.sys_wide_zip_loads.qw) && ...
              any(mpopt.exp.sys_wide_zip_loads.qw(2:3))) )
        warning('opf_execute: ''%s'' solver does not support ZIP load model. Converting to constant power loads.', alg)
        mpopt = mpoption(mpopt, 'exp.sys_wide_zip_loads', ...
                          struct('pw', [], 'qw', []));
    end

    %% run specific AC OPF solver
    if legacy_solver
        switch alg
            case 'PDIPM'
                if mpopt.pdipm.step_control
                    if ~have_feature('scpdipmopf')
                        error('opf_execute: MPOPT.opf.ac.solver = ''%s'' requires SCPDIPMOPF (see http://www.pserc.cornell.edu/tspopf/)', alg);
                    end
                else
                    if ~have_feature('pdipmopf')
                        error('opf_execute: MPOPT.opf.ac.solver = ''%s'' requires PDIPMOPF (see http://www.pserc.cornell.edu/tspopf/)', alg);
                    end
                end
                [results, success, raw] = tspopf_solver(om, mpopt);
            case 'TRALM'
                if ~have_feature('tralmopf')
                    error('opf_execute: MPOPT.opf.ac.solver = ''%s'' requires TRALM (see http://www.pserc.cornell.edu/tspopf/)', alg);
                end
                [results, success, raw] = tspopf_solver(om, mpopt);
            case 'MINOPF'
                if ~have_feature('minopf')
                    error('opf_execute: MPOPT.opf.ac.solver = ''%s'' requires MINOPF (see http://www.pserc.cornell.edu/minopf/)', alg);
                end
                [results, success, raw] = mopf_solver(om, mpopt);
            case 'SDPOPF'
                if ~have_feature('yalmip')
                    error('opf_execute: MPOPT.opf.ac.solver = ''%s'' requires YALMIP (see https://yalmip.github.io)', alg);
                end
                [results, success, raw] = sdpopf_solver(om, mpopt);
            otherwise
                error('opf_execute: MPOPT.opf.ac.solver = ''%s'' is not a valid AC OPF solver selection', alg);
        end
    else    %% not legacy solver
        switch alg
            case 'IPOPT'
                if ~have_feature('ipopt')
                    error('opf_execute: MPOPT.opf.ac.solver = ''%s'' requires IPOPT (see https://github.com/coin-or/Ipopt)', alg);
                end
            case 'FMINCON'
                if ~have_feature('fmincon')
                    error('opf_execute: MPOPT.opf.ac.solver = ''%s'' requires FMINCON (Optimization Toolbox 2.x or later)', alg);
                end
            case 'KNITRO'
                if ~have_feature('knitro')
                    error('opf_execute: MPOPT.opf.ac.solver = ''%s'' requires Artelys Knitro (see https://www.artelys.com/solvers/knitro/)', alg);
                end
        end
        [results, success, raw] = nlpopf_solver(om, mpopt);
    end     %% if legacy_solver
end %% if dc
if ~isfield(raw, 'output') || ~isfield(raw.output, 'alg') || isempty(raw.output.alg)
    raw.output.alg = alg;
end

if success
  if ~dc && ~sdp
    %% copy bus voltages back to gen matrix
    results.gen(:, VG) = results.bus(results.gen(:, GEN_BUS), VM);

    %% cartesian voltage magnitude multipliers
    if vcart
        results.bus(:, MU_VMIN) = results.bus(:, MU_VMIN) .* results.bus(:, VM) * 2;
        results.bus(:, MU_VMAX) = results.bus(:, MU_VMAX) .* results.bus(:, VM) * 2;
    end

    %% gen PQ capability curve multipliers
    if ll.N.PQh > 0 || ll.N.PQl > 0
      mu_PQh = results.mu.lin.l(ll.i1.PQh:ll.iN.PQh) - results.mu.lin.u(ll.i1.PQh:ll.iN.PQh);
      mu_PQl = results.mu.lin.l(ll.i1.PQl:ll.iN.PQl) - results.mu.lin.u(ll.i1.PQl:ll.iN.PQl);
      Apqdata = om.get_userdata('Apqdata');
      results.gen = update_mupq(results.baseMVA, results.gen, mu_PQh, mu_PQl, Apqdata);
    end

    %% compute g, dg, f, df, d2f if requested by opf.return_raw_der = 1
    if mpopt.opf.return_raw_der
      %% move from results to raw if using v4.0 of MINOPF or TSPOPF
      if isfield(results, 'dg')
        raw.dg = results.dg;
        raw.g = results.g;
      end
      %% compute g, dg, unless already done by post-v4.0 MINOPF or TSPOPF
      if ~isfield(raw, 'dg')
        [g, geq, dg, dgeq] = nlp_consfcn(om, results.x);
        raw.g = [ geq; g];
        raw.dg = [ dgeq'; dg'];   %% true Jacobian organization
      end
      %% compute df, d2f
      [f, df, d2f] = nlp_costfcn(om, results.x);
      raw.df = df;
      raw.d2f = d2f;
    end
  end

  %% delete g and dg fields from results if using v4.0 of MINOPF or TSPOPF
  if isfield(results, 'dg')
    rmfield(results, 'dg');
    rmfield(results, 'g');
  end

  %% angle limit constraint multipliers
  iang = om.get_userdata('iang');
  if ~sdp && length(iang)
    if vcart
      iang = om.get_userdata('iang');
      results.branch(iang, MU_ANGMIN) = results.mu.nli(nni.i1.angL:nni.iN.angL) * pi/180;
      results.branch(iang, MU_ANGMAX) = results.mu.nli(nni.i1.angU:nni.iN.angU) * pi/180;
    else
      iang = om.get_userdata('iang');
      results.branch(iang, MU_ANGMIN) = results.mu.lin.l(ll.i1.ang:ll.iN.ang) * pi/180;
      results.branch(iang, MU_ANGMAX) = results.mu.lin.u(ll.i1.ang:ll.iN.ang) * pi/180;
    end
  end
else
  %% assign empty g, dg, f, df, d2f if requested by opf.return_raw_der = 1
  if ~dc && mpopt.opf.return_raw_der
    raw.dg = [];
    raw.g = [];
    raw.df = [];
    raw.d2f = [];
  end
end

if ~sdp
  %% assign values and limit shadow prices for variables
  om_var_order = om.get('var', 'order');
  for k = 1:length(om_var_order)
    name = om_var_order(k).name;
    if om.getN('var', name)
      idx = vv.i1.(name):vv.iN.(name);
      results.var.val.(name) = results.x(idx);
      results.var.mu.l.(name) = results.mu.var.l(idx);
      results.var.mu.u.(name) = results.mu.var.u(idx);
    end
  end

  %% assign shadow prices for linear constraints
  om_lin_order = om.get('lin', 'order');
  for k = 1:length(om_lin_order)
    name = om_lin_order(k).name;
    if om.getN('lin', name)
      idx = ll.i1.(name):ll.iN.(name);
      results.lin.mu.l.(name) = results.mu.lin.l(idx);
      results.lin.mu.u.(name) = results.mu.lin.u(idx);
    end
  end

  %% assign shadow prices for nonlinear constraints
  if ~dc
    om_nle_order = om.get('nle', 'order');
    for k = 1:length(om_nle_order)
      name = om_nle_order(k).name;
      if om.getN('nle', name)
        results.nle.lambda.(name) = results.mu.nle(nne.i1.(name):nne.iN.(name));
      end
    end

    om_nli_order = om.get('nli', 'order');
    for k = 1:length(om_nli_order)
      name = om_nli_order(k).name;
      if om.getN('nli', name)
        results.nli.mu.(name) = results.mu.nli(nni.i1.(name):nni.iN.(name));
      end
    end
  end

  %% assign values for components of quadratic cost
  om_qdc_order = om.get('qdc', 'order');
  for k = 1:length(om_qdc_order)
    name = om_qdc_order(k).name;
    if om.getN('qdc', name)
      results.qdc.(name) = om.eval_quad_cost(results.x, name);
    end
  end

  %% assign values for components of general nonlinear cost
  om_nlc_order = om.get('nlc', 'order');
  for k = 1:length(om_nlc_order)
    name = om_nlc_order(k).name;
    if om.getN('nlc', name)
      results.nlc.(name) = om.eval_nln_cost(results.x, name);
    end
  end

  %% assign values for components of legacy user cost
  om_cost_order = om.get('cost', 'order');
  for k = 1:length(om_cost_order)
    name = om_cost_order(k).name;
    if om.getN('cost', name)
      results.cost.(name) = om.eval_legacy_cost(results.x, name);
    end
  end

  %% if single-block PWL costs were converted to POLY, insert dummy y into x
  %% Note: The "y" portion of x will be nonsense, but everything should at
  %%       least be in the expected locations.
  pwl1 = om.get_userdata('pwl1');
  if ~isempty(pwl1) && ~strcmp(alg, 'TRALM') && ~(strcmp(alg, 'PDIPM') && mpopt.pdipm.step_control)
    %% get indexing
    vv = om.get_idx();
    if dc
      nx = vv.iN.Pg;
    else
      nx = vv.iN.Qg;
    end
    y = zeros(length(pwl1), 1);
    raw.xr = [ raw.xr(1:nx); y; raw.xr(nx+1:end)];
    results.x = [ results.x(1:nx); y; results.x(nx+1:end)];
  end
end


%A case of Buck DC-DC converter
C=440;
L=20;
RC=0.002;
RL=0.001;
Vor=5;
Vin=24;
Vc0=5;
ILmin=0;


function x = false(x)
% FALSE Constrains a binary variable to be false (0)
%
% FALSE(x) returns the constraint x<=0.5.
%
%   See also SDPVAR/TRUE, SDPVAR/AND, SDPVAR/OR, SDPVAR/NOT, BINVAR, BINARY

x = (x<=0.5);


function plotPDF(SO3F,h,varargin)
% plot pole density function
%
% Syntax
%   plotPDF(odf,[h1,..,hN])
%   plotPDF(odf,{[h11,h12],h2,hN],'superposition',{[c11,c12],c2,cN})
%   plotPDF(odf,pf.h,'superposition',pf.c)
%
% Input
%  odf - @SO3FunHarmonic
%  h   - @Miller crystallographic directions
%  c   - structure coefficients
%
% Options
%  resolution    - resolution of the plots
%  superposition - plot superposed pole figures
%
% Flags
%  noTitle   - suppress the Miller indices at the top
%  antipodal - include <VectorsAxes.html antipodal symmetry>
%  complete  - plot entire (hemi)--sphere
%
% See also
% S2Grid/plot annotate savefigure Plotting Annotations_demo ColorCoding_demo PlotTypes_demo
% SphericalProjection_demo

if numel(SO3F)>1
  warning(['You try to plot an multivariate function. Plot the desired components ' ...
    'manually. In the following the first component is plotted.'])
end

plotPDF@SO3Fun(SO3F.subSet(1),h,varargin{:});

end


classdef EventSelectionParameters
    %holds basic values for specifying samples, either out to a distance, or a specified number
    %
    %CONSTRUCTION OPTIONS
    %  obj = EventSelectionParameters()
    %  obj = EventSelectionParameters('NumClosestEvents', N)
    %  obj = EventSelectionParameters('AllEventsInRadius', R)
    %  obj = EventSelectionParameters('NumClosestEventsUpToRadius', N, R)
    %  obj = EventSelectionParameters( ... , 'DistanceUnits', unitname)
    %
    %USE CASE
    %
    %  select based on distances, where D is a vector of distances of length N, in unit U.
    %  The unit is flexible, and can be any value accepted by either checkangleunits or ValidateLengthUnits
    %
    %    MASK = obj.SelectionFromDistances(D, U)
    %
    %  MASK will be a logical vector of length N. It is TRUE where D matches the conditions contained
    %  in obj.
    %
    %see also EventSelectionParameters/EventSelectionParameters

    properties
        % Samples will be collected out to this radius. To ignore radius, set as inf (default)
        MaxSampleRadius         (1,1) double {mustBePositive}    = inf
        % These many samples will be collected. To collect all, set as inf (default)
        NumClosestEvents        (1,1) double {mustBePositive}    = inf
        % Units of distance for the sample radius. Defaults to kilometers.
        DistanceUnits           (1,1) string {mustBeNonempty}    = standardizeDistanceUnits('km')
    end

    properties(Dependent)
        UseNumClosestEvents
        UseEventsInRadius
    end

    properties(Hidden)
        PrevMaxSampleRadius     (1,1) double    = inf;
        PrevNumClosestEvents    (1,1) double    = inf;
    end

    properties(Dependent,Hidden)
        % for backwards compatibility
        RadiusKm
    end

    methods
        function obj = EventSelectionParameters(style, varargin)
            %EventSelectionParameters constructor
            %obj = EventSelectionParameters()
            %obj = EventSelectionParameters('NumClosestEvents', N) choose the N closest events
            %
            %obj = EventSelectionParameters('AllEventsInRadius', R) choose ALL events within radius R
            %
            %obj = EventSelectionParameters('NumClosestEventsUpToRadius', N, R) choose the N
            %closest events, limited to radius R.
            %
            % The radius units can be specified as an optional Name,Value pair.
            %
            %obj = EventSelectionParameters( ... , 'DistanceUnits', unitname) where unitname is
            %unit understood by the validateLengthUnits or checkangleunits functions.

            if ~exist('style','var')
                style = 'none';
            end
            p=inputParser();
            switch style
                case 'AllEventsInRadius'
                    p.addRequired('MaxSampleRadius');
                    p.addParameter('DistanceUnits',obj.DistanceUnits);
                case 'NumClosestEvents'
                    p.addRequired('NumClosestEvents');
                case 'NumClosestEventsUpToRadius'
                    p.addRequired('NumClosestEvents');
                    p.addRequired('MaxSampleRadius');
                    p.addParameter('DistanceUnits',obj.DistanceUnits,@isStandardDistanceUnit);
                case 'none'
            end
            p.parse(varargin{:});
            obj=copyfields(obj, p.Results);
            %{
            fn =fieldnames(p.Results);
            for i=1:numel(fn)
                obj.(fn{i}) = p.Results.(fn{i});
            end
            %}

        end

        function rk = get.RadiusKm(obj)
            if isinf(obj.MaxSampleRadius)
                rk = obj.MaxSampleRadius;
            elseif obj.DistanceUnits == "kilometer"
                rk = obj.MaxSampleRadius;
            elseif obj.DistanceUnits== "degrees"
                rk = deg2km(obj.MaxSampleRadius);
            elseif obj.DistanceUnits == "radians"
                rk = rad2km(obj.MaxSampleRadius);
            else
                rk = unitsratio('kilometer', obj.DistanceUnits) * obj.MaxSampleRadius;
            end
        end
        function obj = set.RadiusKm(obj,val)
            obj.MaxSampleRadius = val;
            obj.DistanceUnits = 'kilometer';
        end

        function tf = get.UseNumClosestEvents(obj)
            tf = obj.NumClosestEvents < inf;
        end

        function tf = get.UseEventsInRadius(obj)
            tf = obj.MaxSampleRadius < inf;
        end

        function obj = set.UseNumClosestEvents(obj, val)
            if val
                obj.NumClosestEvents = obj.PrevNumClosestEvents;
            else
                if ~isinf(obj.NumClosestEvents)
                    obj.PrevNumClosestEvents = obj.NumClosestEvents;
                end
                obj.NumClosestEvents = inf;
            end
        end

        function obj = set.UseEventsInRadius(obj, val)
            if val
                obj.MaxSampleRadius = obj.PrevMaxSampleRadius;
            else
                if ~isinf(obj.MaxSampleRadius)
                    obj.PrevMaxSampleRadius = obj.MaxSampleRadius;
                end
                obj.MaxSampleRadius = inf;
            end
        end

        function obj = set.DistanceUnits(obj, units)
            obj.DistanceUnits = standardizeDistanceUnits(units);
        end

        function obj = set.MaxSampleRadius(obj, value)
            prev = obj.MaxSampleRadius;
            obj.MaxSampleRadius = value;
            if ~isinf(prev)
                obj.PrevMaxSampleRadius = prev; %#ok<MCSUP>
            end
        end
        function obj = set.NumClosestEvents(obj,value)
            if value==floor(value) || isinf(value)
                prev = obj.NumClosestEvents;
                obj.NumClosestEvents = value;
                if ~isinf(prev)
                    obj.PrevNumClosestEvents = prev; %#ok<MCSUP>
                end
            else
                error('NumClosestEvents must be either a positive integer value, or be inf');
            end
        end

        function mask = SelectionFromDistances(obj, distances, distUnits)
            %select values, based on distances
            %
            %MASK = obj.SelectionFromDistances(DISTANCES, UNITS) returns a logical vector of
            %length N that is TRUE for each value of DISTANCES that matches the conditions
            %contained in this object. The UNITS specify the units of distances, (eg. 'km',
            %'radians', 'degrees', etc..) and accepts any value understood by either the
            %checkangleunits or validateLengthUnit functions.
            %
            %This object's maximum radius will be compared (with the appropriate conversion)
            %with the provided distance vector.
            %
            % Example of automatic unit conversion
            %  >> esp = EventSelectionParameters('AllEventsInRadius', 1, 'DistanceUnits', 'degrees');
            %  >> esp.SelectionFromDistances([100 110 120], 'km')  % one degree of arc is ~111km
            %     ans =
            %       1×3 logical array
            %        1   1   0
            %
            %  >> esp.DistanceUnits = 'miles';
            %  >> esp.SelectionFromDistances([2.0, 1.5 1.0],'km'); % comparing against 1 mile
            %     ans =
            %       1×3 logical array
            %        0   1   1
            %
            %
            %When specifying a number of events and several distances fall across the boundary, it
            %is undefined which values will be selected. For example...
            %
            %  >> D = [ 1 1 1 1 1 1 1 1 ];  % 8 events, all at the same distance
            %  >> esp = EventSelectionParameters('NumClosestEvents',5);
            %  >> esp.SelectionFromDistances(D, 'km')
            %
            %   ans =
            %     1×8 logical array
            %     1   1   1   1   1   0   0   0
            %
            %see also checkangleunits, validateLengthUnit

            %% deal with the maximum radius

            if isinf(obj.MaxSampleRadius)                                 % distances do not matter
                mask = true; % scaler because unused later
            elseif distUnits == obj.DistanceUnits                       % simple case: Units match
                mask = distances <= obj.MaxSampleRadius;
            else
                distUnits = standardizeDistanceUnits(distUnits);
                if distUnits == obj.DistUnits
                    mask = distances <= obj.MaxSampleRadius;
                else
                    mask = [];
                    % convert Selection Units into provided units, then compare
                    OtherIsAngle = ismember(distUnits,["radians" , "degrees"]);
                    ThisIsAngle  = ismember(obj.DistanceUnits,["radians" , "degrees"]);
                end
            end

            % remember that UNITSRATIO arguments are (TO , FROM)
            if ~isempty(mask)
                % mask has been calculated already

            elseif OtherIsAngle == ThisIsAngle                      % both are length or both are angle
                maxDist = unitsratio(distUnits, obj.DistanceUnits) * obj.MaxSampleRadius;
                mask = distances < maxDist;

            elseif distUnits == "radians"
                maxDist = unitsratio(obj.DistanceUnits,'kilometer') * km2rad(obj.MaxSampleRadius);
                mask = distances < maxDist;

            elseif distUnits == "degrees"
                maxDist = unitsratio(obj.DistanceUnits,'kilometer') * km2deg(obj.MaxSampleRadius);
                mask = distances < maxDist;

            elseif obj.DistanceUnits == "radians"
                maxDist = unitsratio(distUnits, 'kilometer') * rad2km(obj.MaxSampleRadius);
                mask = distances < maxDist;

            elseif obj.DistanceUnits == "degrees"
                maxDist = unitsratio(distUnits, 'kilometer') * deg2km(obj.MaxSampleRadius);
                mask = distances < maxDist;
            end

            %% deal with the N closest events
            if ~isinf(obj.NumClosestEvents)
                [~,I]=mink(distances, obj.NumClosestEvents);
                % I'm sure this logic could be simplified...
                II = false(size(distances));
                II(I)=true;
                mask = mask & II;
            end

        end
    end

    methods(Static)
        function obj = fromStruct(s)
            %Backwards Compatibility with EventSelectionChoice
            if ~isstruct(s)
                error('expected a struct');
            end
            obj = EventSelectionParameters();

            if isfield(s,'UseEventsInRadius') && s.UseEventsInRadius
                if isfield(s,'RadiusKm')
                    obj.MaxSampleRadius = s.RadiusKm;
                end
            end
            if isfield(s,'UseNumNearbyEvents') && s.UseNumNearbyEvents
                if isfield(s,'NumClosestEvents')
                    obj.NumClosestEvents = s.NumClosestEvents;
                end
                if isfield(s,'MaxRadiusKm')
                    obj.MaxSampleRadius = s.MaxRadiusKm;
                end
            end
        end
    end
end


function [scan,frame,mn] = motionCompNearestImage(view, scans, frames, meanImage)

%    [scan,frame,mn] = motionCompNearestImage(view, [scans], [frames], [meanImage])
%
% gb 01/22/05
%
% Finds the image closest to the mean image. Returns the scan and the frame
% of this image and also its Mean Square Error with the mean image
% Default : scans = current scan
%           frames = all frames
%           meanImage = first frame of the first scan

% Initilizes arguments and variables
global dataTYPES

if ieNotDefined('scans')
    scans = viewGet(view,'curScan');
end
if ieNotDefined('frames')
    frames = 1:length(dataTYPES(curType).scanParams(scans(1)).nFrames);
end
if ieNotDefined('meanImage')
    meanImage = motionCompLoadImages(view,scans(1),1);
end

% In order to save memory, it is necessary to search for the image scan after scan.
%       - If the input 'scans' is a single value, it returns the nearest image
%         for this scan.
%       - If the input 'scans' is an array, it calls it recursively scan
%         after scan and finds the nearest image among the different nearest images

if length(scans) == 1

    % If 'scans' is a single value
    % Loads the whole volume
    scan = scans;
    tSeriesAllSlices = motionCompLoadImages(view, scan);

    % Computes the Mean Square Error of the difference between the images
    % and the mean image
    MSE = motionCompMSE(tSeriesAllSlices(frames,:,:), meanImage);

    % Finds the minimum of this mean square error
    [mn,frame] = min(MSE);

    return
else

    % If 'scans' is an array
    % Initializes the variable
    results = zeros(length(scans),3);

    % Calls itself recursively scan after scan
    for scanIndex = 1:length(scans)
        scanNum = scans(scanIndex);

        fprintf('Searching nearest image in scan %d... ',scanNum);
        [results(scanIndex,1),results(scanIndex,2),results(scanIndex,3)] = ...
            motionCompNearestImage(view, scanNum, frames, meanImage);
        fprintf('Done\n');
    end

    % Finds the closest image among the images found in each scan
    [mn, baseScanIndex] = min(results(:,3));
    scan = results(baseScanIndex,1);
    frame = results(baseScanIndex,2);
    return
end


function [fAValue] = calc_MaxLikelihoodACombined(mCatalog, mControl, fBValue, bReBin)
    % Maximum likelihood a-values for a combined catalog containing different periods while using a fixed b-value
    % [fAValue] = calc_MaxLikelihoodACombined(mCatalog, mControl, fBValue, bReBin)
    %
    %
    % Input parameters:
    %   mCatalog        Catalog containing different periods with varying
    %                   magnitude of completeness
    %   mControl        Controlmatrix containing informations about the single catalogs
    %                   mControl(n,:) contains information about caCatalogs{n}
    %                   Column 1: Starting time of catalog
    %                   Column 2: Magnitude of completeness
    %                   Column 3: Starting magnitude bin
    %                   Column 4: Magnitude bin stepsize (must be 0.1)
    %   fBValue         Fixed b-value
    %   bReBin          Rebin the catalogs to the given binning in mControl
    %                   (must be 0)
    %
    % Output parameters:
    %   fAValue         Maximum likelihood a-value
    %
    % Danijel Schorlemmer
    % July 17, 2002

    % Get the number of different periods in the catalog
    [nRow_, nColumn_] = size(mControl);
    % Init vector with ending times for each period
    vMaxTime_ = zeros(nRow_, 1);
    % Loop over the control matrix
    for nCnt_ = 1:nRow_
        % Determine starting time of period
        fMinTime_ = mControl(nCnt_, 1);
        % Determine ending time of period
        if nCnt_ < nRow_
            vMaxTime_(nCnt_) = mControl(nCnt_+1, 1);
        else
            vMaxTime_(nCnt_) = max(mCatalog.Date);
        end
        % Create subcatalog for period
        vSel_ = (mCatalog.Date >= fMinTime_) & (mCatalog.Date < vMaxTime_(nCnt_));
        mTmpCatalog_ = mCatalog.subset(vSel_);
        % Rebin the catalog (should not be used)
        if bReBin
            fMaxMag_ = max(mTmpCatalog_(:,6));
            for nBin_ = mControl(nCnt_,3):mControl(nCnt_,4):(fMaxMag_ + mControl(nCnt_,4))
                vSel_ = (mTmpCatalog_(:,6) >= (nBin_ - (mControl(nCnt_,4)/2))) & (mTmpCatalog_(:,6) < (nBin_ + (mControl(nCnt_,4)/2)));
                mTmpCatalog_(vSel_,6) = nBin_;
            end
        end
        % Cut the subcatalog at magnitude of completeness
        vSel_ = mTmpCatalog_(:,6) >= mControl(nCnt_,2);
        mTmpCatalog_ = mTmpCatalog_(vSel_,:);
        % Store the subcatalog
        caCatalogs_{nCnt_} = mTmpCatalog_;
    end
    % Add the ending times to the control matrix
    mControl = [mControl vMaxTime_];
    % Set the callback starting values
    vStartValue = 1;
    % Find the maximum likelihood solution
    [fAValue, ~, bExitFlag_] = fminsearch(@callback_LogLikelihoodACombined, vStartValue, [], caCatalogs_, mControl, fBValue);
end


%Works reasonably well since double represents integers <= about 2^50 exactly
function I = colon(a,b,c)
	if nargin == 2
		I  = double(a):double(b);
	else
		I = double(a):double(b):double(c);
	end
	I = int64(I);
end


function [node,elem,face]=cgals2m(v,f,opt,maxvol,varargin)
%
% [node,elem,face]=cgals2m(v,f,opt,maxvol)
%
% wrapper for CGAL 3D mesher (CGAL 3.5 and newer)
% convert a triangular surface to tetrahedral mesh
%
% http://www.cgal.org/Manual/3.5/doc_html/cgal_manual/Mesh_3/Chapter_main.html
%
% author: Qianqian Fang (fangq <at> nmr.mgh.harvard.edu)
%
% input:
%	 v: the node coordinate list of a surface mesh (nn x 3)
%	 f: the face element list of a surface mesh (be x 3)
%	 opt: parameters for CGAL mesher, if opt is a structure, then
%	     opt.radbound: defines the maximum surface element size
%	     opt.angbound: defines the miminum angle of a surface triangle
%	     opt.distbound: defines the maximum distance between the
%		 center of the surface bounding circle and center of the
%		 element bounding sphere
%	     opt.reratio:  maximum radius-edge ratio
%	     if opt is a scalar, it only specifies radbound.
%	 maxvol: target maximum tetrahedral elem volume
%
% output:
%	 node: output, node coordinates of the tetrahedral mesh
%	 elem: output, element list of the tetrahedral mesh, the last
%	      column is the region id
%	 face: output, mesh surface element list of the tetrahedral mesh
%	      the last column denotes the boundary ID
%
% -- this function is part of iso2mesh toolbox (http://iso2mesh.sf.net)
%

fprintf(1,'creating surface and tetrahedral mesh from a polyhedral surface ...\n');

exesuff=fallbackexeext(getexeext,'cgalpoly');

ang=30;
ssize=6;
approx=0.5;
reratio=3;

flags=varargin2struct(varargin{:});

if(~isstruct(opt))
	ssize=opt;
end

if(isstruct(opt) && length(opt)==1)  % does not support settings for multiple labels
	if(isfield(opt,'radbound'))   ssize=opt.radbound; end
	if(isfield(opt,'angbound'))   ang=opt.angbound; end
	if(isfield(opt,'distbound'))  approx=opt.distbound; end
	if(isfield(opt,'reratio'))    reratio=opt.reratio; end
end
if(getoptkey('DoRepair',0,flags)==1)
    [v,f]=meshcheckrepair(v,f);
end
saveoff(v,f,mwpath('pre_cgalpoly.off'));
deletemeshfile(mwpath('post_cgalpoly.mesh'));

randseed=hex2dec('623F9A9E'); % "U+623F U+9A9E"

if(~isempty(getvarfrom({'caller','base'},'ISO2MESH_RANDSEED')))
        randseed=getvarfrom({'caller','base'},'ISO2MESH_RANDSEED');
end

cmd=sprintf('"%s%s" "%s" "%s" %.16f %.16f %.16f %.16f %.16f %d',mcpath('cgalpoly'),exesuff,...
    mwpath('pre_cgalpoly.off'),mwpath('post_cgalpoly.mesh'),ang,ssize,...
    approx,reratio,maxvol,randseed);
system(cmd);
if(~exist(mwpath('post_cgalpoly.mesh'),'file'))
    error(sprintf('output file was not found, failure was encountered when running command: \n%s\n',cmd));
end
[node,elem,face]=readmedit(mwpath('post_cgalpoly.mesh'));

fprintf(1,'node number:\t%d\ntriangles:\t%d\ntetrahedra:\t%d\nregions:\t%d\n',...
    size(node,1),size(face,1),size(elem,1),length(unique(elem(:,end))));
fprintf(1,'surface and volume meshes complete\n');


function view = recomputeSSImage(view,numGrays,numColors,clipMode)
%
% function view = recomputeSSImage(view,numGrays,numColors,clipMode)
%
% rmk 09/17/98 based on recomputeImage

% Initialize images
anatIm=[];

% Get anatClip and overlayClip from sliders
anatClip = getAnatClip(view);

% Get anatomy image
anatIm = view.anat;

% Rescale anatIm to [1:numGrays], anatClip determines the range
% of anatomy values that gets mapped to the available grayscales.
% If anatClip=[0,1] then there is no clipping and the entire
% range anatomy values is scaled to the range of available gray
% scales.
minVal = min(anatIm(:));
maxVal = max(anatIm(:));
anatClipMin = min(anatClip)*(maxVal-minVal) + minVal;
anatClipMax = max(anatClip)*(maxVal-minVal) + minVal;
if (length(anatIm(:))~=0)
    anatIm=rescale2(anatIm,[anatClipMin,anatClipMax],[1,numGrays]);
end

% Replace NaNs
indices = find(isnan(anatIm));
anatIm(indices) = 1;

% Finally, set the view.ui.image field
view.ui.image = uint8(anatIm-1);


function poly = selectPolygon(varargin)
% select a polygon by mouse
%
% Syntax
%   poly = selectPolygon
%   ebsd = ebsd(inpolygon(ebsd,poly))

disp('Use the mouse to select the vertices of a polygon.');
disp('Click right to finish your selection.')

poly = [];
button = 1;
hold on

h = line('xdata',[],'ydata',[],'Color','k','MarkerFaceColor','k','MarkerEdgeColor',...
  'w','MarkerSize',10,'LineWidth',2,'Marker','s');

while button == 1

    [xi,yi,button] = ginput(1);
    poly(end+1,:) = [xi,yi]; %#ok<AGROW>

    set(h,'XData',poly(:,1),'YData',poly(:,2));

end

% ensure the polygon is always closed
poly(end+1,:) = poly(1,:);
set(h,'XData',poly(:,1),'YData',poly(:,2));

hold off


%-------------------------------------------------------------------------------------------------------------------%
%
% IB2d is an Immersed Boundary Code (IB) for solving fully coupled non-linear
% 	fluid-structure interaction models. This version of the code is based off of
%	Peskin's Immersed Boundary Method Paper in Acta Numerica, 2002.
%
% Author: Nicholas A. Battista
% Email:  nick.battista@unc.edu
% Date Created: May 27th, 2015
% Institution: UNC-CH
%
% This code is capable of creating Lagrangian Structures using:
% 	1. Springs
% 	2. Beams (*torsional springs)
% 	3. Target Points
%	4. Muscle-Model (combined Force-Length-Velocity model, "HIll+(Length-Tension)")
%
% One is able to update those Lagrangian Structure parameters, e.g., spring constants, resting %%	lengths, etc
%
% There are a number of built in Examples, mostly used for teaching purposes.
%
% If you would like us %to add a specific muscle model, please let Nick (nick.battista@unc.edu) know.
%
%--------------------------------------------------------------------------------------------------------------------%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% FUNCTION: creates the CHANNEL_CHANNEL-EXAMPLE geometry and prints associated input files
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function Channel_Channel()

%
% Grid Parameters (MAKE SURE MATCHES IN input2d !!!)
%
Nx =  128;        % # of Eulerian Grid Pts. in x-Direction (MUST BE EVEN!!!)
Ny =  128;        % # of Eulerian Grid Pts. in y-Direction (MUST BE EVEN!!!)
Lx = 1.0;        % Length of Eulerian Grid in x-Direction
Ly = 1.0;        % Length of Eulerian Grid in y-Direction

% Immersed Structure Geometric / Dynamic Parameters %
ds= min(Lx/(2*Nx),Ly/(2*Ny));  % Lagrangian spacing
L = 0.9*Lx;                    % Length of Channel
w = 0.2*Ly;                    % Width of Channel
struct_name = 'channel'; % Name for .vertex, .spring, etc files.

% Call function to construct geometry
[xLag,yLag] = give_Me_Immsersed_Boundary_Geometry(ds,L,w,Lx,Ly);

% Plot Geometry to test
plot(xLag(1:end/2),yLag(1:end/2),'r-'); hold on;
plot(xLag(end/2+1:end),yLag(end/2+1:end),'r-'); hold on;

plot(xLag,yLag,'*'); hold on;
xlabel('x'); ylabel('y');
axis square;

% Prints .vertex file!
print_Lagrangian_Vertices(xLag,yLag,struct_name);

% Prints .spring file!
%k_Spring = 1e7;
%print_Lagrangian_Springs(xLag,yLag,k_Spring,ds_Rest,struct_name);

% Prints .beam file!
%k_Beam = 0.5; C = 0.0;
%print_Lagrangian_Beams(xLag,yLag,k_Beam,C,struct_name);

% Prints .target file!
k_Target = 1e7;
print_Lagrangian_Target_Pts(xLag,k_Target,struct_name);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% FUNCTION: prints VERTEX points to a file called rubberband.vertex
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function print_Lagrangian_Vertices(xLag,yLag,struct_name)

    N = length(xLag);

    vertex_fid = fopen([struct_name '.vertex'], 'w');

    fprintf(vertex_fid, '%d\n', N );

    %Loops over all Lagrangian Pts.
    for s = 1:N
        X_v = xLag(s);
        Y_v = yLag(s);
        fprintf(vertex_fid, '%1.16e %1.16e\n', X_v, Y_v);
    end

    fclose(vertex_fid);

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% FUNCTION: prints Vertex points to a file called rubberband.vertex
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function print_Lagrangian_Target_Pts(xLag,k_Target,struct_name)

    N = length(xLag);

    target_fid = fopen([struct_name '.target'], 'w');

    fprintf(target_fid, '%d\n', N );

    %Loops over all Lagrangian Pts.
    for s = 1:N
        fprintf(target_fid, '%d %1.16e\n', s, k_Target);
    end

    fclose(target_fid);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% FUNCTION: prints BEAM (Torsional Spring) points to a file called rubberband.beam
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function print_Lagrangian_Beams(xLag,yLag,k_Beam,C,struct_name)

    % k_Beam: beam stiffness
    % C: beam curvature

    N = length(xLag); % NOTE: Total number of beams = Number of Total Lag Pts. - 2

    beam_fid = fopen([struct_name '.beam'], 'w');

    fprintf(beam_fid, '%d\n', N );

    %spring_force = kappa_spring*ds/(ds^2);

    %BEAMS BETWEEN VERTICES
    for s = 2:N-1
            if  s <= N-1
                fprintf(beam_fid, '%d %d %d %1.16e %1.16e\n',s-1, s, s+1, k_Beam, C);
            else
                %Case s=N
                fprintf(beam_fid, '%d %d %d %1.16e %1.16e\n',s-1, s, 1,   k_Beam, C);
            end
    end
    fclose(beam_fid);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% FUNCTION: prints SPRING points to a file called rubberband.spring
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function print_Lagrangian_Springs(xLag,yLag,k_Spring,ds_Rest,struct_name)

    N = length(xLag);

    spring_fid = fopen([struct_name '.spring'], 'w');

    fprintf(spring_fid, '%d\n', N );

    %spring_force = kappa_spring*ds/(ds^2);

    %SPRINGS BETWEEN VERTICES
    for s = 1:N
            if s < N
                fprintf(spring_fid, '%d %d %1.16e %1.16e\n', s, s+1, k_Spring, ds_Rest);
            else
                %Case s=N
                fprintf(spring_fid, '%d %d %1.16e %1.16e\n', s, 1,   k_Spring, ds_Rest);
            end
    end
    fclose(spring_fid);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% FUNCTION: creates the Lagrangian structure geometry
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function [xLag,yLag] = give_Me_Immsersed_Boundary_Geometry(ds,L,w,Lx,Ly)

% The immsersed structure is a channel %
x = (Lx-L)/2:ds:(L+(Lx-L)/2);  %xPts
yBot = (Ly-w)/2;               %yVal for bottom of Channel
yTop = Ly - (Ly-w)/2;          %yVal for top of Channel

xLag = [x x];
yLag = [yBot*ones(1,length(x)) yTop*ones(1,length(x))];


% 3 DOF mobile robot example.
% by Almaskhan Baimyshev
% 08/28/2013

map = struct('name', 'bench_june1.mat', 'start_point', [-14.5 -7.5], 'goal_point', [10 -0.65]);
max_iter = 20e3;
is_benchmark = false;
rand_seed = 40;
variant = 'FNSimple3D';
result = rrt_star(map, max_iter, is_benchmark, rand_seed, variant);


% Batch run autoclusting, Sweep master threshold with 2-fold CV
% ie all clustering thresholds are the same
% Need to run Batch_k20_CV12_AK first

%% Initialize Parameters

range_fish = 8:15; % select which fish to analyze (can be multiple)
M_stim = 2; % select which stimuli to use when clustering
masterThresh_sweep = 0.5:.1:0.9; % values of thresh to sweep

isFullData = 1; % If using all cells

masterDef = 0.7; % Default values of clusParams
mergeDef = masterDef;
capDef = masterDef;
reg1Def = masterDef;
reg2Def = masterDef;
minSizeDef = 10;
k1Def = 20;
k2Def = 500;

clusParams = struct('merge',mergeDef,'cap',capDef,'reg1',reg1Def,...
    'reg2',reg2Def,'minSize',minSizeDef,'k1',k1Def,'k2',k2Def);

masterThresh_nClus = zeros(length(masterThresh_sweep),length(range_fish));
masterThresh_CVscore = zeros(length(masterThresh_sweep),length(range_fish));
masterThresh_CVscore_raw = cell(length(masterThresh_sweep),length(range_fish));
masterThresh_nCells = zeros(length(masterThresh_sweep),length(range_fish));
masterThresh_compTime = zeros(length(masterThresh_sweep),length(range_fish));

masterThresh_data = struct('clusParams',clusParams,'nClus',masterThresh_nClus,'CVscore',masterThresh_CVscore,...
    'nCells',masterThresh_nCells,'compTime',masterThresh_compTime,'clusA',[],'clusB',[]);

data_masterdir = GetCurrentDataDir();

%%
disp(['Batch Script: Sweep masterThresh, iFish']);
scriptStart = tic;
clear leg;
for k_fish = 1:length(range_fish),

    %% Load fish data
    i_fish = range_fish(k_fish);
    leg{k_fish} = ['Fish ' num2str(range_fish(k_fish))];
    LoadFullFish(hfig,i_fish,isFullData); % Load the data
    absIX = getappdata(hfig,'absIX');

    %% Partitions for CV
    timelists = getappdata(hfig,'timelists');
    timelists_names = getappdata(hfig,'timelists_names');
    periods = getappdata(hfig,'periods');

    timelistsCV = cell(length(M_stim),2);

    % Divides each stimulous in half
    for k_stim = 1:length(M_stim),
        i_stim = M_stim(k_stim);
        TL = timelists{i_stim};
        period = periods(i_stim);
        nrep = size(TL,2)/periods(i_stim); % integer
        n = floor(nrep/2);
        timelistsCV{k_stim,1} = TL(1):TL(n*period);
        timelistsCV{k_stim,2} = TL(1+n*period):TL(2*n*period);
    end

    %% Sweep through k2 and do clustering
    for k_masterThresh = 1:length(masterThresh_sweep),

        sweepStart = tic;
        clusParams.merge = masterThresh_sweep(k_masterThresh);
        clusParams.cap = masterThresh_sweep(k_masterThresh);
        clusParams.reg1 = masterThresh_sweep(k_masterThresh);
        clusParams.reg2 = masterThresh_sweep(k_masterThresh);
        Score = zeros(1,2);%(length(M_stim),2);
        %%
        nClus = zeros(1,2);
        nCells = zeros(1,2);
        CIX = cell(1,2);
        GIX = cell(1,2);
        for half = 1:2, % CV halves, load them from saved
            %%
            i_ClusGroup = 2;
            i_Cluster = half+2;
            % this matches the assignments
            % in Batch_k20_CV12_AK
            [cIX,gIX] = LoadCluster_Direct(i_fish,i_ClusGroup,i_Cluster,absIX);

            tIX = timelistsCV{k_stim,half};
            M_0 = GetTimeIndexedData_Default_Direct(hfig,[],tIX,'isAllCells');

            isWkmeans = 0;
            [cIX,gIX] = AutoClustering(cIX,gIX,M_0,isWkmeans,clusParams);
%%
            nClus(half) = length(unique(gIX));
            nCells(half) = length(unique(cIX));
            CIX{half} = cIX;
            GIX{half} = gIX;
        end

        % Perform 2x Cross Validation
        Score(1) = HungarianCV(nClus(1),nClus(2),CIX{1},CIX{2},GIX{1},GIX{2});% true,timelists_names{i_stim});
        Score(2) = HungarianCV(nClus(2),nClus(1),CIX{2},CIX{1},GIX{2},GIX{1});% true,timelists_names{i_stim});
        masterThresh_CVscore_raw{k_masterThresh,k_fish} = Score;

        % Save clusters and scores

        masterThresh_CVscore(k_masterThresh,k_fish) = mean(Score);
        masterThresh_nClus(k_masterThresh,k_fish) = mean([nClus(1),nClus(2)]);
        masterThresh_nCells(k_masterThresh,k_fish) = mean([nCells(1),nCells(2)]);
        masterThresh_compTime(k_masterThresh,k_fish) = toc(sweepStart);

        masterThresh_data(k_masterThresh,k_fish).CVscore = mean(Score);
        masterThresh_data(k_masterThresh,k_fish).nClus = mean([nClus(1),nClus(2)]);
        masterThresh_data(k_masterThresh,k_fish).nCells = mean([nCells(1),nCells(2)]);
        masterThresh_data(k_masterThresh,k_fish).clusParams = clusParams;
        masterThresh_data(k_masterThresh,k_fish).compTime = toc(sweepStart);

        masterThresh_data(k_masterThresh,k_fish).clusA = struct('cIX',CIX{1},'gIX',GIX{1});
        masterThresh_data(k_masterThresh,k_fish).clusB = struct('cIX',CIX{2},'gIX',GIX{2});
        end
end
scriptTime = toc(scriptStart);
disp(['Batch Script Finished, Took ' num2str(scriptTime) ' sec']);

%%
figure;
subplot(2,2,1)
plot(masterThresh_sweep,masterThresh_nClus,'-o')
% legend('Fish8','Fish9');
%xlabel('total k for kmeans')
ylabel('# of auto-clusters')

subplot(2,2,2)
plot(masterThresh_sweep,masterThresh_CVscore,'-o')
ylim([0,1])
ylabel('CV (overlapping cell %)')

subplot(2,2,3)
plot(masterThresh_sweep,masterThresh_nCells,'-o');
%xlabel('total k for kameans')
ylabel('total # of cells included in clusters')
xlabel('master threshold')

subplot(2,2,4)
plot(masterThresh_sweep,masterThresh_nCells.*masterThresh_CVscore,'-o');
%xlabel('total k for kameans')
ylabel('total # of cells passing CV')
xlabel('master threshold')
legend(leg,'location','best');


function toggle_aspectratio(src, ~, ax, options)
    % TOGGLE_ASPECTRATIO toggles axes aspect ratio and sets checked property
    % uses the checked state to determine currentstate.
    % aspect ratio is based on current latitude (Y)
    %
    % TOGGLE_ASPECTRATIO(source, event, axesHandle, option)
    % options can be:
    %    "SetGlobal",in which case it affects ZmapGlobal
    %    "UpdateOnly",in which case it does not toggle values, but fixes daspect.
    %
    % to set multiple options, use either cell of chars, or array of strings

    if ~exist('options','var')
        options = "";
    end
    assert(all(ismember(options,["", "UpdateOnly","SetGlobal","on","off"]) ),...
        'options must be member of ["UpdateOnly", "SetGlobal","on","off"]');

    if ~any(options == "UpdateOnly")
        if any(options=="on")
            src.Checked="on";
        elseif any(options =="off")
            src.Checked ="off";
        else
            src.Checked=toggleOnOff(src.Checked);
        end
    end

    switch src.Checked
        case 'on'
            for i=1:numel(ax)
                daspect(ax(i), [1 cosd(mean(ax(i).YLim)) 10]);
            end
        case 'off'
            for i=1:numel(ax)
                daspect(ax(i),'auto');
            end
    end

    if any(options == "SetGlobal")
        ZG = ZmapGlobal.Data;
        ZG.lock_aspect = matlab.lang.OnOffSwitchState(src.Checked);
    end
end