function [SPM,pKX] = spm_spm_local(SPM)
% [Re]ML Estimation of a General Linear Model
% FORMAT [SPM,pKX] = spm_spm_local(SPM)
%
% Michal and Brian spent a week comparing how SPM computes the beta values
% and comparing that with Rory's/mrVIsta's glm code.
%
% The structure sent in to spm_get_data() contains information that
% specifies how the data will be scaled upon return.  If you only send in
% the file name, the data are unscaled.  We did not determined, however,
% which entry of the structure causes spm_get_data() to scale the returned
% values.
%
% Given the same design matrix and data, the two calculations are
% identical. (We didn't check the covariance stuff, this is assuming no
% whitening).  This can be hard to see because of problems in reading the
% data with SPM.  The routine spm_get_data() apparently can scale the time
% series that it returns.  This has the consequence that the beta weights
% may differ by this scale factor.  But apart from this, the beta weights
% are the same in the two cases, when the design matrix is the same.
%
%         pKX is the pseudo-inverse of the and smoothed (by K) design
%         matrix.
%
% required fields of SPM:
%
% xY.VY - nScan x 1 struct array of mapped image volumes
%         Images must have the same orientation, voxel size and data type
%       - Any scaling should have already been applied via the image handle
%         scalefactors.
%
% xX    - Structure containing design matrix information
%       - Required fields are:
%         xX.X      - Design matrix (raw, not temporally smoothed)
%         xX.name   - cellstr of parameter names corresponding to columns
%                     of design matrix
%       - Optional fields are:
%         xX.K      - cell of session-specific structures (see spm_filter)
%                   - Design & data are pre-multiplied by K
%                     (K*Y = K*X*beta + K*e)
%                   - Note that K should not smooth across block boundaries
%                   - defaults to speye(size(xX.X,1))
%         xX.W      - Optional whitening/weighting matrix used to give
%                     weighted least squares estimates (WLS). If not specified
%                     spm_spm will set this to whiten the data and render
%                     the OLS estimtes maximum likelihood
%                     i.e. W*W' = inv(xVi.V).
%
% xVi   - structure describing intrinsic temporal non-sphericity
%       - required fields are:
%         xVi.Vi    - array of non-sphericity components
%                   - defaults to {speye(size(xX.X,1))} - i.i.d.
%                   - specifying a cell array of contraints (Qi)
%                     These contraints invoke spm_reml to estimate
%                     hyperparameters assuming V is constant over voxels.
%                     that provide a high precise estimate of xX.V
%       - Optional fields are:
%         xX.V      - Optional non-sphericity matrix.  Cov(e) = sigma^2*V
%                     If not specified spm_spm will compute this using
%                     a 1st pass to identify signifcant voxels over which
%                     to estimate V.  A 2nd pass is then used to re-estimate
%                     the parameters with WLS and save the ML estimates
%                     (unless xX.W is already specified)
%
% xM    - Structure containing masking information, or a simple column vector
%         of thresholds corresponding to the images in VY.
%       - If a structure, the required fields are:
%         xM.TH - nVar x nScan matrix of analysis thresholds, one per image
%         xM.I  - Implicit masking (0=>none, 1 => implicit zero/NaN mask)
%         xM.VM - struct array of mapped explicit mask image volumes
% 		- (empty if no explicit masks)
%               - Explicit mask images are >0 for valid voxels to assess.
%               - Mask images can have any orientation, voxel size or data
%                 type. They are interpolated using nearest neighbour
%                 interpolation to the voxel locations of the data Y.
%       - Note that voxels with constant data (i.e. the same value across
%         scans) are also automatically masked out.
%
%
% In addition, the global default UFp is used to set a critical
% F-threshold for selecting voxels over which the non-sphericity
% is estimated (if required)
%
%_______________________________________________________________________
%
% spm_spm is the heart of the SPM package. Given image files and a
% General Linear Model, it estimates the model parameters, variance
% hyerparameters, and smoothness of standardised residual fields, writing
% these out to disk in the current working directory for later
% interrogation in the results section. (NB: Existing analyses in the
% current working directory are overwritten).  This directory
% now becomes the working directory for this analysis and all saved
% images are relative to this directory.
%
% The model is expressed via the design matrix (xX.X). The basic model
% at each voxel is of the form is Y = X*B + e, for data Y, design
% matrix X, (unknown) parameters B, and residual errors e. The errors
% are assummed to have a normal distribution.
%
% Sometimes confounds (e.g. drift terms in fMRI) are necessary.  These
% can be specified directly in the design matrix or implicitly, in terms
% of a residual forming matrix K to give a generalised linear model
% K*Y = K*X*B + K*e.  In fact K can be any matrix (e.g. a convolution
% matrix).
%
% In some instances i.i.d. assumptions about errors do not hold.  For
% example, with serially correlated (fMRI) data or correlations among the
% levels of a factor in repeated measures designs.  This non-sphericity
% can be specified in terms of components (SPM.xVi.Vi{i}). If specified
% these covariance components will then be estimated with ReML (restricted
% maximum likelihood) hyperparameters.  This estimation assumes the same
% non-sphericity for voxels that exceed the global F-threshold. The ReML
% estimates can then used to whiten the data giving maximum likelihood (ML)
% or Gauss-Markov estimators.  This entails a second pass of the data
% with an augmented model K*W*Y = K*W*X*B + K*W*e where W*W' = inv(xVi.V).
% xVi.V is the non-sphericity based on the hyperparameter esimates.
% W is stored in xX.W and cov(K*W*e) in xX.V.  The covariance of the
% parameter estimates is then xX.Bcov = pinv(K*W*X)*xX.V*pinv(K*W*X)';
%
% If you do not want ML estimates but want to use ordinary least squares
% (OLS) then simply set SPM.xX.W to the identity matrix. Any non-sphericity
% V will still be estimated but will be used to adjust the degrees of freedom
% of the ensuing statistics using the Satterthwaite approximation (c.f.
% the Greenhouse-Giesser corrections)
%
% If [non-spherical] variance components Vi are not specified xVi.Vi and
% xVi.V default to the identity matrix (i.e. i.i.d). The parameters are
% then estimated by OLS.  In this instance the OLS and ML estimates are
% the same.
%
% Note that only a single voxel-specific hyperaprameter (i.e. variance
% component) is estimated, even if V is not i.i.d.  This means spm_spm
% always implements a fixed-effects model.
% Random effects models can be emulated using a multi-stage procedure:
% This entails summarising the data with contrasts such that the fixed
% effects in a second model on the summary data are those effects of
% interest (i.e. the population effects). This means contrasts are
% re-entered into spm_spm to make an inference (SPM) at the next
% level. At this higher hierarchial level the residual variance for the
% model contains the appropriate variance components from lower levels.
% See spm_RandFX.man for further details and below.
%
% Under the additional assumption that the standardised residual images
% are non-stationary standard Gaussian random fields, results from
% Random field theory can be applied to estimate the significance
% statistic images (SPM's) adjusting p values for the multiple tests
% at all voxels in the search volume. The parameters required for
% this random field correction are the volume, and Lambda, the covariance
% matrix of partial derivatives of the standardised error fields.
%
% spm_est_smoothness estimates the variances of the partial derivatives
% in the axis directions (the diagonal of Lambda). The covariances (off
% diagonal elements of Lambda) are assumed to be zero.
%
%                           ----------------
%
%
% The volume analsed is the intersection of the threshold masks,
% explicit masks and implicit masks. See spm_spm_ui for further details
% on masking options.
%
%
%-----------------------------------------------------------------------
%
% The output of spm_spm takes the form of an SPM.mat file of the analysis
% parameters, and 'float' flat-file images of the parameter and variance
% [hyperparameter] estimates. An 8bit zero-one mask image indicating the
% voxels assessed is also written out, with zero indicating voxels outside
% tha analysed volume.
%
%                           ----------------
%
% The following SPM.fields are set by spm_spm (unless specified)
%
%     xVi.V      - estimated non-sphericity trace(V) = rank(V)
%     xVi.h      - hyperparameters  xVi.V = xVi.h(1)*xVi.Vi{1} + ...
%     xVi.Cy     - spatially whitened <Y*Y'> (used by ReML to estimate h)
%     xVi.CY     - <(Y - <Y>)*(Y - <Y>)'>    (used by spm_spm_Bayes)
%
%                           ----------------
%
%     Vbeta     - struct array of beta image handles (relative)
%     VResMS    - file struct of ResMS image handle  (relative)
%     VM        - file struct of Mask  image handle  (relative)
%
%                           ----------------
%     xX.W      - if not specified W*W' = inv(x.Vi.V)
%     xX.V      - V matrix (K*W*Vi*W'*K') = correlations after K*W is applied
%     xX.xKXs   - space structure for K*W*X, the 'filtered and whitened'
%                 design matrix
%               - given as spm_sp('Set',xX.K*xX.W*xX.X) - see spm_sp
%     xX.pKX    - pseudoinverse of K*W*X, computed by spm_sp
%     xX.Bcov   - xX.pKX*xX.V*xX.pKX - variance-covariance matrix of
%                 parameter estimates
%		  (when multiplied by the voxel-specific hyperparameter ResMS)
%                 (of the parameter estimates. (ResSS/xX.trRV = ResMS)    )
%     xX.trRV   - trace of R*V, computed efficiently by spm_SpUtil
%     xX.trRVRV - trace of RVRV
%     xX.erdf   - effective residual degrees of freedom (trRV^2/trRVRV)
%     xX.nKX    - design matrix (xX.xKXs.X) scaled for display
%                 (see spm_DesMtx('sca',... for details)
%
%                           ----------------
%
%     xVol.M    - 4x4 voxel->mm transformation matrix
%     xVol.iM   - 4x4 mm->voxel transformation matrix
%     xVol.DIM  - image dimensions - column vector (in voxels)
%     xVol.XYZ  - 3 x S vector of in-mask voxel coordinates
%     xVol.S    - Lebesgue measure or volume       (in voxels)
%     xVol.R    - vector of resel counts           (in resels)
%     xVol.FWHM - Smoothness of components - FWHM, (in voxels)
%
%                           ----------------
%
% xCon  - See Christensen for details of F-contrasts.  These are specified
%         at the end of spm_spm after the non-sphericity V has been defined
%         or estimated. The fist contrast tests for all effects of interest
%         assuming xX.iB and xX.iG index confounding or nuisance effects.
%
%     xCon      - Contrast structure (created by spm_FcUtil.m)
%     xCon.name - Name of contrast
%     xCon.STAT - 'F', 'T' or 'P' - for F/T-contrast ('P' for PPMs)
%     xCon.c    - (F) Contrast weights
%     xCon.X0   - Reduced design matrix (spans design space under Ho)
%                 It is in the form of a matrix (spm99b) or the
%                 coordinates of this matrix in the orthogonal basis
%                 of xX.X defined in spm_sp.
%     xCon.iX0  - Indicates how contrast was specified:
%                 If by columns for reduced design matrix then iX0 contains the
%                 column indices. Otherwise, it's a string containing the
%                 spm_FcUtil 'Set' action: Usuall one of {'c','c+','X0'}
%                 (Usually this is the input argument F_iX0.)
%     xCon.X1o  - Remaining design space (orthogonal to X0).
%                 It is in the form of a matrix (spm99b) or the
%                 coordinates of this matrix in the orthogonal basis
%                 of xX.X defined in spm_sp.
%     xCon.eidf - Effective interest degrees of freedom (numerator df)
%     xCon.Vcon - ...for handle of contrast/ESS image (empty at this stage)
%     xCon.Vspm - ...for handle of SPM image (empty at this stage)
%
%                           ----------------
%
%
% The following images are written to file
%
% mask.{img,hdr}                                   - analysis mask image
% 8-bit (uint8) image of zero-s & one's indicating which voxels were
% included in the analysis. This mask image is the intersection of the
% explicit, implicit and threshold masks specified in the xM argument.
% The XYZ matrix contains the voxel coordinates of all voxels in the
% analysis mask. The mask image is included for reference, but is not
% explicitly used by the results section.
%
%                           ----------------
%
% beta_????.{img,hdr}                                 - parameter images
% These are 16-bit (float) images of the parameter estimates. The image
% files are numbered according to the corresponding column of the
% design matrix. Voxels outside the analysis mask (mask.img) are given
% value NaN.
%
%                           ----------------
%
% ResMS.{img,hdr}                    - estimated residual variance image
% This is a 32-bit (double) image of the residual variance estimate.
% Voxels outside the analysis mask are given value NaN.
%
%                           ----------------
%
% RVP.{img,hdr}                      - estimated resels per voxel image
% This is a 32-bit (double) image of the RESELs per voxel estimate.
% Voxels outside the analysis mask are given value 0.  These images
% reflect the nonstationary aspects the spatial autocorrelations.
%
%
%-----------------------------------------------------------------------
%
% References:
%
% Christensen R (1996) Plane Answers to Complex Questions
%       Springer Verlag
%
% Friston KJ, Holmes AP, Worsley KJ, Poline JP, Frith CD, Frackowiak RSJ (1995)
% ``Statistical Parametric Maps in Functional Imaging:
%   A General Linear Approach''
%       Human Brain Mapping 2:189-210
%
% Worsley KJ, Friston KJ (1995)
% ``Analysis of fMRI Time-Series Revisited - Again''
%       Neuroimage 2:173-181
%
%-----------------------------------------------------------------------
%
% For those interested, the analysis proceeds a "block" at a time,
% The block size conforms to maxMem that can be set as a global variable
% MAXMEM (in bytes) [default = 2^20]
%
%_______________________________________________________________________
% @(#)spm_spm.m	2.66 Andrew Holmes, Jean-Baptiste Poline, Karl Friston 03/03/27
SCCSid   = '2.66';

%-Say hello
%-----------------------------------------------------------------------
SPMid    = spm('FnBanner',mfilename,SCCSid);
Finter   = spm('FigName','Stats: estimation...'); spm('Pointer','Watch')

%-Get SPM.mat if necessary
%-----------------------------------------------------------------------
if nargin ==0
	swd     = spm_str_manip(spm_get(1,'SPM.mat','Select SPM.mat'),'H');
	load(fullfile(swd,'SPM.mat'));
	SPM.swd = swd;
end

%-Change to SPM.swd if specified
%-----------------------------------------------------------------------
try
	cd(SPM.swd);
end

%-Ensure data are assigned
%-----------------------------------------------------------------------
try
	SPM.xY.VY;
catch
	helpdlg({	'Please assign data to this design',...
			'Use fMRI under model specification'});
	spm('FigName','Stats: done',Finter); spm('Pointer','Arrow')
	return
end

%-Delete files from previous analyses
%-----------------------------------------------------------------------
if exist(fullfile('.','mask.img'),'file') == 2

	str   = {'Current directory contains SPM estimation files:',...
		 'pwd = ',pwd,...
		 'Existing results will be overwritten!'};

	abort = spm_input(str,1,'bd','stop|continue',[1,0],1);
	if abort
		spm('FigName','Stats: done',Finter); spm('Pointer','Arrow')
		return
	else
		str = sprintf('Overwriting old results\n\t (pwd = %s) ',pwd)
		warning(str)
		drawnow
	end
end

files = {	'mask.???','ResMS.???','RVP.???',...
		'beta_????.???','con_????.???','ResI_????.???',...
		'ess_????.???', 'spm?_????.???'};

for i=1:length(files)
	if any(files{i} == '*'|files{i} == '?' )
		[j,null] = spm_list_files(pwd,files{i});
		for i=1:size(j,1)
			spm_unlink(deblank(j(i,:)))
		end
	else
		spm_unlink(files{i})
	end
end

%=======================================================================
% - A N A L Y S I S   P R E L I M I N A R I E S
%=======================================================================

%-Initialise
%=======================================================================
fprintf('%-40s: %30s','Initialising parameters','...computing')    %-#
xX            = SPM.xX;
[nScan nBeta] = size(xX.X);

%-If xM is not a structure then assumme it's a vector of thresholds
%-----------------------------------------------------------------------
try
	xM = SPM.xM;
catch
	xM = -ones(nScan,1)/0;
end
if ~isstruct(xM)
	xM = struct(	'T',	[],...
			'TH',	xM,...
			'I',	0,...
			'VM',	{[]},...
			'xs',	struct('Masking','analysis threshold'));
end

%-Check confounds (xX.K) and non-sphericity (xVi)
%-----------------------------------------------------------------------
if ~isfield(xX,'K')
	xX.K  = 1;
end
try
	%-If covariance components are specified use them
	%---------------------------------------------------------------
	xVi   = SPM.xVi;
catch

	%-otherwise assume i.i.d.
	%---------------------------------------------------------------
	xVi   = struct(	'form',  'i.i.d.',...
			'V',	 speye(nScan,nScan));
end

%-Get non-sphericity V
%=======================================================================
try
	%-If xVi.V is specified proceed directly to parameter estimation
	%---------------------------------------------------------------
	V     = xVi.V;
	str   = 'parameter estimation';

catch
	% otherwise invoke ReML selecting voxels under i.i.d assumptions
	%---------------------------------------------------------------
	V     = speye(nScan,nScan);
	str   = '[hyper]parameter estimation';
end

%-Get whitening/Weighting matrix: If xX.W exists we will save WLS estimates
%-----------------------------------------------------------------------
try
	%-If W is specified, use it
	%-------------------------------------------------------
	W     = xX.W;   % imagesc(W); title('Whitening matrix')
catch
	if isfield(xVi,'V')

		% otherwise make W a whitening filter W*W' = inv(V)
		%-------------------------------------------------------
		[u s] = spm_svd(xVi.V);
		s     = spdiags(1./sqrt(diag(s)),0,nScan,nScan);
		W     = u*s*u';
		W     = W.*(abs(W) > 1e-6);
		xX.W  = sparse(W);
	else
		% unless xVi.V has not been estimated - requiring 2 passes
		%-------------------------------------------------------
		W     = speye(nScan,nScan);
		str   = 'hyperparameter estimation (1st pass)';
	end
end

%-Design space and projector matrix [pseudoinverse] for WLS
%=======================================================================

% The first 'Set' initializes the SVD of the design matrix properties. In
% our default analysis, W is the identity matrix. In our case, we set K so
% that it is an impulse.
xX.xKXs = spm_sp('Set',spm_filter(xX.K,W*xX.X));		% KWX

% Hence, it turns out the with our settings, KWX is just X
% figure(1); subplot(1,3,1), plot(spm_filter(xX.K,W*xX.X));
%            subplot(1,3,2), plot(W*xX.X)
%            subplot(1,3,3), plot(xX.xKXs.X)

% Compute the pseudo-inverse of xX.xKXs (from above)
xX.pKX  = spm_sp('x-',xX.xKXs);				% projector

% So, after all this nonsense, it is still the case that pKX remains
% the pseudoinverse of X.
% max(abs(xX.X(:) - xX.xKXs.X(:)))

%-If xVi.V is not defined compute Hsqr and F-threshold under i.i.d.
%-----------------------------------------------------------------------
if ~isfield(xVi,'V')
	Fcname = 'effects of interest';
	iX0    = [SPM.xX.iB SPM.xX.iG];
	xCon   = spm_FcUtil('Set',Fcname,'F','iX0',iX0,xX.xKXs);
	X1o    = spm_FcUtil('X1o', xCon(1),xX.xKXs);
	Hsqr   = spm_FcUtil('Hsqr',xCon(1),xX.xKXs);
	trRV   = spm_SpUtil('trRV',xX.xKXs);
	trMV   = spm_SpUtil('trMV',X1o);
	UFp    = spm('GetGlobal','UFp');
	UF     = spm_invFcdf(1 - UFp,[trMV,trRV]);
end

%-Image dimensions and data
%=======================================================================
VY       = SPM.xY.VY;
M        = VY(1).mat;
DIM      = VY(1).dim(1:3)';
xdim     = DIM(1); ydim = DIM(2); zdim = DIM(3);
YNaNrep  = spm_type(VY(1).dim(4),'nanrep');

%-Maximum number of residual images for smoothness estimation
%-----------------------------------------------------------------------
global     defaults
MAXRES   = defaults.stats.maxres;

%-maxMem is the maximum amount of data processed at a time (bytes)
%-----------------------------------------------------------------------
MAXMEM   = defaults.stats.maxmem;
nSres    = min(nScan,MAXRES);
blksz    = ceil(MAXMEM/8/nScan);				%-block size
nbch     = ceil(xdim*ydim/blksz);				%-# blocks
nbch = 1;
blksz    = ceil(MAXMEM);				%-block size

fprintf('%s%30s\n',sprintf('\b')*ones(1,30),'...done')               %-#

%-Initialise output images (unless this is a 1st pass for ReML)
%=======================================================================
if isfield(xX,'W')
fprintf('%-40s: %30s','Output images','...initialising')     %-#

%-Intialise the name of the new mask : current mask & conditions on voxels
%-----------------------------------------------------------------------
VM    = struct(		'fname',	'mask.img',...
			'dim',		[DIM',spm_type('uint8')],...
			'mat',		M,...
			'pinfo',	[1 0 0]',...
			'descrip',	'spm_spm:resultant analysis mask');
VM    = spm_create_vol(VM);

%-Intialise beta image files
%-----------------------------------------------------------------------
Vbeta(1:nBeta) = deal(struct(...
			'fname',	[],...
			'dim',		[DIM',spm_type('float')],...
			'mat',		M,...
			'pinfo',	[1 0 0]',...
			'descrip',	''));
for i = 1:nBeta
	Vbeta(i).fname   = sprintf('beta_%04d.img',i);
	Vbeta(i).descrip = sprintf('spm_spm:beta (%04d) - %s',i,xX.name{i});
	spm_unlink(Vbeta(i).fname)
end
Vbeta = spm_create_vol(Vbeta,'noopen');

%-Intialise residual sum of squares image file
%-----------------------------------------------------------------------
VResMS = struct(	'fname',	'ResMS.img',...
			'dim',		[DIM',spm_type('double')],...
			'mat',		M,...
			'pinfo',	[1 0 0]',...
			'descrip',	'spm_spm:Residual sum-of-squares');
VResMS = spm_create_vol(VResMS,'noopen');

%-Intialise residual images
%-----------------------------------------------------------------------
VResI(1:nSres) = deal(struct(...
			'fname',	[],...
			'dim',		[DIM',spm_type('double')],...
			'mat',		M,...
			'pinfo',	[1 0 0]',...
			'descrip',	'spm_spm:Residual image'));

for i = 1:nSres
	VResI(i).fname   = sprintf('ResI_%04d.img', i);
	VResI(i).descrip = sprintf('spm_spm:ResI (%04d)', i);
	spm_unlink(VResI(i).fname);
end
VResI = spm_create_vol(VResI);
end % (xX,'W')

fprintf('%s%30s\n',sprintf('\b')*ones(1,30),'...initialised')        %-#

%=======================================================================
% - F I T   M O D E L   &   W R I T E   P A R A M E T E R    I M A G E S
%=======================================================================

%-Intialise variables used in the loop
%=======================================================================
xords = [1:xdim]'*ones(1,ydim); xords = xords(:)';  % plane X coordinates
yords = ones(xdim,1)*[1:ydim];  yords = yords(:)';  % plane Y coordinates
S     = 0;                                          % Volume (voxels)
s     = 0;                                          % Volume (voxels > UF)
Cy    = 0;					    % <Y*Y'> spatially whitened
CY    = 0;					    % <Y*Y'> for ReML
EY    = 0;					    % <Y>    for ReML
i_res = round(linspace(1,nScan,nSres))';	    % Indices for residual

%-Initialise XYZ matrix of in-mask voxel co-ordinates (real space)
%-----------------------------------------------------------------------
XYZ   = zeros(3,xdim*ydim*zdim);

%-Cycle over bunches blocks within planes to avoid memory problems
%=======================================================================
% spm_progress_bar('Init',100,str,'');

totalVox = 0;
for z = 1:zdim				%-loop over planes (2D or 3D data)

    % current plane-specific parameters
    %-------------------------------------------------------------------
    zords   = z*ones(xdim*ydim,1)';	%-plane Z coordinates
    CrBl    = [];			%-parameter estimates
    CrResI  = [];			%-normalized residuals
    CrResSS = [];			%-residual sum of squares
    Q       = [];			%-in mask indices for this plane

    for bch = 1:nbch			%-loop over blocks

	%-# Print progress information in command window
	%---------------------------------------------------------------
	str   = sprintf('Plane %3d/%-3d, block %3d/%-3d',z,zdim,bch,nbch);
	fprintf('\r%-40s: %30s',str,' ')                                %-#

	%-construct list of voxels in this block
	%---------------------------------------------------------------
	I     = [1:blksz] + (bch - 1)*blksz;		%-voxel indices
	I     = I(I <= xdim*ydim);			%-truncate
	xyz   = [xords(I); yords(I); zords(I)];		%-voxel coordinates
	nVox  = size(xyz,2);				%-number of voxels

	%-Get data & construct analysis mask
	%===============================================================
	fprintf('%s%30s',sprintf('\b')*ones(1,30),'...read & mask data')%-#
	Cm    = logical(ones(1,nVox));			%-current mask

	%-Compute explicit mask
	% (note that these may not have same orientations)
	%---------------------------------------------------------------
	for i = 1:length(xM.VM)

		%-Coordinates in mask image
		%-------------------------------------------------------
		j      = xM.VM(i).mat\M*[xyz;ones(1,nVox)];

		%-Load mask image within current mask & update mask
		%-------------------------------------------------------
		Cm(Cm) = spm_get_data(xM.VM(i),j(:,Cm)) > 0;
	end

	%-Get the data in mask, compute threshold & implicit masks
	%---------------------------------------------------------------
	Y     = zeros(nScan,nVox);
	for i = 1:nScan

		%-Load data in mask
		%-------------------------------------------------------
		if ~any(Cm), break, end			%-Break if empty mask
		Y(i,Cm)  = spm_get_data(VY(i),xyz(:,Cm));

        % tmp = Y(i,Cm); hist(tmp(:));

		Cm(Cm)   = Y(i,Cm) > xM.TH(i);		%-Threshold (& NaN) mask
		if xM.I & ~YNaNrep & xM.TH(i) < 0	%-Use implicit mask
			Cm(Cm) = abs(Y(i,Cm)) > eps;
        end
        max(Y(:))
	end

	%-Mask out voxels where data is constant
	%---------------------------------------------------------------
	Cm(Cm) = any(diff(Y(:,Cm),1));
	Y      = Y(:,Cm);				%-Data within mask
	CrS    = sum(Cm);				%-# current voxels

    figure(1); plot(Y);
    totalVox = CrS + totalVox
    title(sprintf('Current voxels TS %.0f, total: ',CrS,totalVox));
    pause(.2)
    foo = analyzeRead(VY(i).fname);
    hist(foo(:),100)
    imtool(foo/max(foo(:)))
	%===============================================================
	%-Proceed with General Linear Model (if there are voxels)
	%===============================================================
	if CrS

		%-Whiten/Weight data and remove filter confounds
		%-------------------------------------------------------
		fprintf('%s%30s',sprintf('\b')*ones(1,30),'filtering')	%-#

        % For us, W is the identity.
        % figure(1); imagesc(W)
        % In this case, KWY is just Y, the data
        %     max(abs(KWY(:) - Y(:)))
		KWY   = spm_filter(xX.K,W*Y);

		%-General linear model: Weighted least squares estimation
		%------------------------------------------------------
		fprintf('%s%30s',sprintf('\b')*ones(1,30),' estimation') %-#

        % Wandell notes:
        %
        %  SPM computes the beta weights using the formula
        %
        %    KWY= KX*beta
        %
        %  Here, pKX is the pseudo-inverse of KX. X is the design matrix,
        %  and K is the term that introduces the convolution with the HRF
        %
        %  KWY is the whitened, filtered, time series. I don't underwstand
        %  why K is on both sides.  Maybe, therefore, I don't understand K.
        %
        %  The formula our glm computes is Y = X*beta
        %  So the differences are that we included the HRF into X and they
        %  assume we did not.  The W is the identity, so it doesn't matter.
        %  If we set up the SPM so that K is the impulse, we should get the
        %  same.  If we set up the SPM so that we pass it X prior to
        %  filtering with the HRF, then we should also get the same.

        % So,
		beta  = xX.pKX*KWY;			%-Parameter estimates
        pKX   = xX.pKX;             % Wandell added for return

		res   = spm_sp('r',xX.xKXs,KWY);	%-Residuals
		ResSS = sum(res.^2);			%-Residual SSQ
		clear KWY				%-Clear to save memory

		%-If ReML hyperparameters are needed for xVi.V
		%-------------------------------------------------------
		if ~isfield(xVi,'V')

			%-F-threshold & accumulate spatially whitened Y*Y'
			%-----------------------------------------------
			j   = sum((Hsqr*beta).^2,1)/trMV > UF*ResSS/trRV;
			j   = find(j);
			if length(j)
				q  = size(j,2);
				s  = s + q;
				q  = spdiags(sqrt(trRV./ResSS(j)'),0,q,q);
				Y  = Y(:,j)*q;
				Cy = Cy + Y*Y';
			end

		end % (xVi,'V')

		%-if we are saving the WLS parameters
		%-------------------------------------------------------
		if isfield(xX,'W')

			%-sample covariance and mean of Y (all voxels)
			%-----------------------------------------------
			CY         = CY + Y*Y';
			EY         = EY + sum(Y,2);

			%-Save betas etc. for current plane as we go along
			%-----------------------------------------------
			CrBl 	   = [CrBl,    beta];
			CrResI     = [CrResI,  res(i_res,:)];
			CrResSS    = [CrResSS, ResSS];

		end % (xX,'W')
		clear Y				%-Clear to save memory

	end % (CrS)

	%-Append new inmask voxel locations and volumes
	%---------------------------------------------------------------
	XYZ(:,S + [1:CrS]) = xyz(:,Cm);		%-InMask XYZ voxel coords
	Q                  = [Q I(Cm)];		%-InMask XYZ voxel indices
	S                  = S + CrS;		%-Volume analysed (voxels)

    end   % (bch)

    %-Plane complete, write plane to image files (unless 1st pass)
    %===================================================================
    if isfield(xX,'W')

    	fprintf('%s%30s',sprintf('\b')*ones(1,30),'...saving plane')	%-#

	%-Write Mask image
	%-------------------------------------------------------------------
	j   = sparse(xdim,ydim);
	if length(Q), j(Q) = 1; end
	VM    = spm_write_plane(VM, j, z);

	%-Write beta images
	%-------------------------------------------------------------------
	j   = NaN*ones(xdim,ydim);
	for i = 1:nBeta
		if length(Q), j(Q) = CrBl(i,:); end
		Vbeta(i) = spm_write_plane(Vbeta(i), j, z);
	end

	%-Write residual images
	%-------------------------------------------------------------------
	for i = 1:nSres
		if length(Q), j(Q) = CrResI(i,:); end
		VResI(i) = spm_write_plane(VResI(i), j, z);
   	end

	%-Write ResSS into ResMS (variance) image scaled by tr(RV) above
	%-------------------------------------------------------------------
	if length(Q), j(Q) = CrResSS;    end
	VResMS  = spm_write_plane(VResMS,j,z);

    end % (xX,'W')

    %-Report progress
    %-------------------------------------------------------------------
    fprintf('%s%30s',sprintf('\b')*ones(1,30),'...done')             %-#
    spm_progress_bar('Set',100*(bch + nbch*(z - 1))/(nbch*zdim));

end % (for z = 1:zdim)
fprintf('\n')                                                        %-#
spm_progress_bar('Clear')

%=======================================================================
% - P O S T   E S T I M A T I O N   C L E A N U P
%=======================================================================
if S == 0, warning('No inmask voxels - empty analysis!'), end

%-close written image files (unless 1st pass)
%=======================================================================
if isfield(xX,'W')

	fprintf('%s%30s\n',sprintf('\b')*ones(1,30),'...closing files')  %-#
	VM              = spm_close_vol(VM);
	Vbeta           = spm_close_vol(Vbeta);
	VResI           = spm_close_vol(VResI);
	VResMS          = spm_close_vol(VResMS);

end % (xVi,'V')

%-average sample covariance and mean of Y (over voxels)
%-----------------------------------------------------------------------
CY          = CY/S;
EY          = EY/S;
CY          = CY - EY*EY';

%-If not defined, compute non-sphericity V using ReML Hyperparameters
%=======================================================================
if ~isfield(xVi,'V')

	%-REML estimate of residual correlations through hyperparameters (h)
	%---------------------------------------------------------------
	str    = 'Temporal non-sphericity (over voxels)';
	fprintf('%-40s: %30s\n',str,'...REML estimation') %-#
	Cy            = Cy/s;

	% ReML for separable designs and covariance components
	%---------------------------------------------------------------
	if isstruct(xX.K)
		m     = length(xVi.Vi);
		h     = zeros(m,1);
		V     = sparse(nScan,nScan);
		for i = 1:length(xX.K)

			% extract blocks from bases
			%-----------------------------------------------
			q     = xX.K(i).row;
			p     = [];
			Qp    = {};
			for j = 1:m
				if nnz(xVi.Vi{j}(q,q))
					Qp{end + 1} = xVi.Vi{j}(q,q);
					p           = [p j];
				end
			end

			% design space for ReML (with confounds in filter)
			%-----------------------------------------------
			Xp         = xX.X(q,:);
			try
				Xp = [Xp xX.K(i).X0];
			end

			% ReML
			%-----------------------------------------------
			fprintf('%-30s- %i\n','  ReML Block',i);
			[Vp,hp]  = spm_reml(Cy(q,q),Xp,Qp);
			V(q,q)   = V(q,q) + Vp;
			h(p)     = hp;
		end
	else
		[V,h] = spm_reml(Cy,xX.X,xVi.Vi);
	end

	% normalize non-sphericity and save hyperparameters
	%---------------------------------------------------------------
	V           = V*nScan/trace(V);
	xVi.h       = h;
	xVi.V       = V;			% Save non-sphericity xVi.V
	xVi.Cy      = Cy;			%-spatially whitened <Y*Y'>
	SPM.xVi     = xVi;			% non-sphericity structure

	% If xX.W is not specified use W*W' = inv(V) to give ML estimators
	%---------------------------------------------------------------
	if ~isfield(xX,'W')
		save SPM SPM
		clear
		load SPM
		SPM = spm_spm(SPM);
		return
	end
end

%-Use non-sphericity xVi.V to compute [effective] degrees of freedom
%=======================================================================
xX.V            = spm_filter(xX.K,spm_filter(xX.K,W*V*W')');	% KWVW'K'
[trRV trRVRV]   = spm_SpUtil('trRV',xX.xKXs,xX.V);		% trRV (for X)
xX.trRV         = trRV;						% <R'*y'*y*R>
xX.trRVRV       = trRVRV;					%-Satterthwaite
xX.erdf         = trRV^2/trRVRV;				% approximation
xX.Bcov         = xX.pKX*xX.V*xX.pKX';				% Cov(beta)

%-Set VResMS scalefactor as 1/trRV (raw voxel data is ResSS)
%-----------------------------------------------------------------------
VResMS.pinfo(1) = 1/xX.trRV;
VResMS          = spm_create_vol(VResMS,'noopen');

%-Create 1st contrast for 'effects of interest' (all if not specified)
%=======================================================================
Fcname          = 'effects of interest';
try
	iX0     = [xX.iB xX.iG];
catch
	iX0     = [];
end
xCon            = spm_FcUtil('Set',Fcname,'F','iX0',iX0,xX.xKXs);

%-Append contrasts for fMRI - specified by SPM.Sess(s).Fc(i)
%-----------------------------------------------------------------------
if isfield(SPM,'Sess')
    for s = 1:length(SPM.Sess)
	for i = 1:length(SPM.Sess(s).Fc)
	    iX0           = 1:nBeta;
	    iX            = SPM.Sess(s).col(SPM.Sess(s).Fc(i).i);
	    iX0(iX)       = [];
	    Fcname        = sprintf('Sess(%d):%s',s,SPM.Sess(s).Fc(i).name);
	    xCon(end + 1) = spm_FcUtil('Set',Fcname,'F','iX0',iX0,xX.xKXs);
	end
    end
end

%-Smoothness estimates of component fields and RESEL counts for volume
%=======================================================================
try
	FWHM = SPM.xVol.FWHM;
	VRpv = SPM.xVol.VRpv;
	R    = SPM.xVol.R;
catch
	[FWHM,VRpv] = spm_est_smoothness(VResI,VM);
	R           = spm_resels_vol(VM,FWHM)';
end

%-Delete the residuals images
%=======================================================================
for  i = 1:nSres,
	spm_unlink([spm_str_manip(VResI(i).fname,'r') '.img']);
	spm_unlink([spm_str_manip(VResI(i).fname,'r') '.hdr']);
	spm_unlink([spm_str_manip(VResI(i).fname,'r') '.mat']);
end

%-Compute scaled design matrix for display purposes
%-----------------------------------------------------------------------
xX.nKX        = spm_DesMtx('sca',xX.xKXs.X,xX.name);

fprintf('%s%30s\n',sprintf('\b')*ones(1,30),'...done')               %-#

%-Save remaining results files and analysis parameters
%=======================================================================
fprintf('%-40s: %30s','Saving results','...writing')                 %-#

%-place fields in SPM
%-----------------------------------------------------------------------
SPM.xVol.XYZ   = XYZ(:,1:S);			%-InMask XYZ coords (voxels)
SPM.xVol.M     = M;				%-voxels -> mm
SPM.xVol.iM    = inv(M);			%-mm -> voxels
SPM.xVol.DIM   = DIM;				%-image dimensions
SPM.xVol.FWHM  = FWHM;				%-Smoothness data
SPM.xVol.R     = R;				%-Resel counts
SPM.xVol.S     = S;				%-Volume (voxels)
SPM.xVol.VRpv  = VRpv;				%-Filehandle - Resels per voxel

SPM.Vbeta      = Vbeta;				%-Filehandle - Beta
SPM.VResMS     = VResMS;			%-Filehandle - Hyperparameter
SPM.VM         = VM;				%-Filehandle - Mask

SPM.xVi        = xVi;				% non-sphericity structure
SPM.xVi.CY     = CY;				%-<(Y - <Y>)*(Y - <Y>)'>

SPM.xX         = xX;				%-design structure
SPM.xM         = xM;				%-mask structure

SPM.xCon       = xCon;				%-contrast structure

SPM.SPMid      = SPMid;
SPM.swd        = pwd;

%-Save analysis parameters in SPM.mat file
%-----------------------------------------------------------------------
save SPM SPM

%=======================================================================
%- E N D: Cleanup GUI
%=======================================================================
fprintf('%s%30s\n',sprintf('\b')*ones(1,30),'...done')               %-#
spm('FigName','Stats: done',Finter); spm('Pointer','Arrow')
fprintf('%-40s: %30s\n','Completed',spm('time'))                     %-#
fprintf('...use the results section for assessment\n\n')             %-#


function rtk=udbias_rtkins(rtk,obsr,obsb,nav,ind)

global glc;
tt=rtk.tt;  nf=rtk.NF; MAXSAT=glc.MAXSAT; NFREQ=glc.NFREQ;
sat=ind.sat; ns=ind.ns; ir=ind.ir; ib=ind.ib;

% detect cycle slip
for i=1:ns

    for f=1:rtk.opt.nf
        tmp=rtk.sat(sat(i)).slip(f);
        rtk.sat(sat(i)).slip(f)=bitand(tmp,252);%0xFC=252
    end

    % detect cycle slip using loss of lock indicator (LLI)
    rtk = detslip_LLI(rtk,obsr,ir(i),1);
    rtk = detslip_LLI(rtk,obsb,ib(i),2);

    % detect cycle slip using geometry-free method
    rtk = detslip_gf_L1L2(rtk,obsr(ir(i)),obsb(ib(i)),nav);
    rtk = detslip_gf_L1L5(rtk,obsr(ir(i)),obsb(ib(i)),nav);

    % detect cycle slip using doppler integration method
    %rtk = detslip_dop(rtk,obsr(i),1);
    %rtk = detslip_dop(rtk,obsb(i),2);

end

% update ambiguity
for f=1:nf

    % reset ambiguity if instantaneous AR mode or expire obs outage counter
    for i=1:MAXSAT
        rtk.sat(i).outc(f)=rtk.sat(i).outc(f)+1;
        if rtk.sat(i).outc(f)>rtk.opt.maxout,reset=1;else,reset=0;end

        if rtk.opt.modear==glc.ARMODE_INST && rtk.x(rtk.ib+i)~=0
            rtk=initx(rtk,0,0,rtk.ib+(f-1)*MAXSAT+i);
        elseif reset==1 && rtk.x(rtk.ib+i)~=0
            rtk=initx(rtk,0,0,rtk.ib+(f-1)*MAXSAT+i);
            rtk.sat(i).outc(f)=0;
        end

        if rtk.opt.modear~=glc.ARMODE_INST && reset==1
            rtk.sat(i).lock(f)=-rtk.opt.minlock;
        end
    end

    % reset ambiguity if detecting cycle slip
    for i=1:ns
        j=rtk.ib+(f-1)*MAXSAT+sat(i);
        rtk.P(j,j)=rtk.P(j,j)+rtk.opt.prn(1)^2*abs(tt);
        slip=rtk.sat(sat(i)).slip(f);
        if rtk.opt.ionoopt==glc.IONOOPT_IFLC
            slip=bitor(slip,rtk.sat(sat(i)).slip(f));
        end
        if rtk.opt.modear==glc.ARMODE_INST||~bitand(slip,1),continue;end
        rtk.x(j)=0;
        rtk.sat(sat(i)).lock(f)=-rtk.opt.minlock;
    end

    bias=zeros(ns,1);

    % estimate approximate ambiguity by code adn phase comparison method
    j=0; offset=0;
    for i=1:ns
        if rtk.opt.ionoopt~=glc.IONOOPT_IFLC
            cp=sdobs(obsr(ir(i)),obsb(ib(i)),f);
            pr=sdobs(obsr(ir(i)),obsb(ib(i)),NFREQ+f);
            lami=nav.lam(sat(i),f);
            if cp==0||pr==0||lami<=0,continue;end
            bias(i)=cp-pr/lami;
        else
            cp1=sdobs(obsr(ir(i)),obsb(ib(i)),1);
            cp2=sdobs(obsr(ir(i)),obsb(ib(i)),2);
            pr1=sdobs(obsr(ir(i)),obsb(ib(i)),NFREQ+1);
            pr2=sdobs(obsr(ir(i)),obsb(ib(i)),NFREQ+2);
            lam1=nav.lam(sat(i),1);
            lam2=nav.lam(sat(i),2);
            if cp1==0||cp2==0||pr1==0||pr2==0||lam1<=0||lam2<=0,continue;end

            C1= lam2^2/(lam2^2-lam1^2);
            C2=-lam1^2/(lam2^2-lam1^2);
            bias(i)=(C1*lam1*cp1+C2*lam2*cp2)-(C1*pr1+C2*pr2);
        end

        if rtk.x(rtk.ib+(f-1)*MAXSAT+sat(i))~=0
            offset=offset+(bias(i)-rtk.x(rtk.ib+(f-1)*MAXSAT+sat(i)));
            j=j+1;
        end
    end

    % correct phase-bias offset to enssure phase-code coherency
    if j>0
        for i=1:MAXSAT
            idx=rtk.ib+(f-1)*MAXSAT+i;
            if rtk.x(idx)~=0
                rtk.x(idx)=rtk.x(idx)+offset/j;
            end
        end
    end

    % set initial ambiguity
    for i=1:ns
        idx=rtk.ib+(f-1)*MAXSAT+sat(i);
        if bias(i)==0 || rtk.x(idx)~=0,continue;end
        rtk=initx(rtk,bias(i),rtk.opt.std(1)^2,idx);
    end

end

return


function [out1] = points_on_line(inp1, inp2, inp3, inp4, inp5)
out1 = [];
ns = getNamespace();
if isempty(ns)
    return;
end
if strcmp(ns, 'AtlasViewerGUI')
    if nargin == 0
        [out1] = points_on_line_AtlasViewerGUI();
    elseif nargin == 1
        [out1] = points_on_line_AtlasViewerGUI(inp1);
    elseif nargin == 2
        [out1] = points_on_line_AtlasViewerGUI(inp1, inp2);
    elseif nargin == 3
        [out1] = points_on_line_AtlasViewerGUI(inp1, inp2, inp3);
    elseif nargin == 4
        [out1] = points_on_line_AtlasViewerGUI(inp1, inp2, inp3, inp4);
    elseif nargin == 5
        [out1] = points_on_line_AtlasViewerGUI(inp1, inp2, inp3, inp4, inp5);
    end
elseif strcmp(ns, 'Homer3') || strcmp(ns, 'DataTreeClass')
    if nargin == 0
        [out1] = points_on_line_Homer3();
    elseif nargin == 1
        [out1] = points_on_line_Homer3(inp1);
    elseif nargin == 2
        [out1] = points_on_line_Homer3(inp1, inp2);
    elseif nargin == 3
        [out1] = points_on_line_Homer3(inp1, inp2, inp3);
    elseif nargin == 4
        [out1] = points_on_line_Homer3(inp1, inp2, inp3, inp4);
    end
end

% ---------------------------------------------------------
function [p3] = points_on_line_AtlasViewerGUI(p1, p2, crop, mode, gridsize)

%
% USAGE:
%
%    p3 = points_on_line_AtlasViewerGUI(p1,p2,crop,mode)
%
% DESCRIPTION:
%
%    Function takes 2 points in 3d, p1 and p2, and a crop length
%    and finds a point on the line formed by extending p1,p2 by the
%    amount (or fraction of p1-p2 distance) crop extends away from p1.
%
%    Positive crop crops p1,p2 by the crop amount.
%    Negative crop extends p1,p2 by the crop amount.
%
%    mode = 'relative' means crop is the distance p3 extends away from p1 relative
%    to the distance between p1 and p2. This is the default if no mode is provided.
%
%    mode = 'absolute' means crop is the absolute distance (in unit length) that
%    p3 extends away from p1.
%
%    mode = 'all' returns the set of all points at intervals 1/crop on the line
%    segment between p1 and p2.
%
% AUTHOR: Jay Dubb (jdubb@nmr.mgh.harvard.edu)
% DATE:   7/7/2012
%

if ~exist('mode','var') || isempty(mode)
    mode = 'relative';
end
if ~exist('gridsize','var') || isempty(gridsize)
    gridsize = 0;
end

p3 = p1;

if isempty(p1) || isempty(p2)
    return;
end
if all(p1==p2)
    return;
end

if ~exist('crop','var') || isempty(crop)
    crop = set_line_crop_AtlasViewerGUI(p2, p1, gridsize);
end

dx0 = p1(1)-p2(1);
dy0 = p1(2)-p2(2);
dz0 = p1(3)-p2(3);
dr = (dx0*dx0 + dy0*dy0 + dz0*dz0)^0.5;
if strcmp(mode, 'relative')
    crop = crop*dr;
    dx = crop*dx0/dr;
    dy = crop*dy0/dr;
    dz = crop*dz0/dr;
elseif strcmp(mode, 'absolute')
    crop = crop/dr;
    dx = crop*dx0;
    dy = crop*dy0;
    dz = crop*dz0;
elseif strcmp(mode, 'all')
    if crop > 1
        return;
    end
    ii=1;
	step = crop;
    while (step*ii)<1
        crop = (step*ii)*dr;
        dx(ii) = crop*dx0/dr;
        dy(ii) = crop*dy0/dr;
        dz(ii) = crop*dz0/dr;
        ii=ii+1;
    end
end

for ii=1:length(dx)
    p3(ii,1) = p1(1)-dx(ii);
    p3(ii,2) = p1(2)-dy(ii);
    p3(ii,3) = p1(3)-dz(ii);
end

% ------------------------------------------------------------------------
function [gapRelative] = set_line_crop_AtlasViewerGUI(p1, p2, gridsize)
gapRelative = .05;

if isempty(p1) || isempty(p2)
    return;
end

% Set desired absolute gap distance
gapAbsolute  = 1;
lineSize     = dist3(p1, p2);

% Get relative gap distance
gapRelative = gapAbsolute / lineSize;
% fprintf('Line gap: relative = %0.2f, absolute: %0.2f\n', gapRelative, gapAbsolute);

if gapRelative > .10
    gapRelative = .02;
end

% ---------------------------------------------------------
function [p3] = points_on_line_Homer3(p1, p2, step, mode)

%
% USAGE:
%
%    p3 = points_on_line_Homer3(p1,p2,step,mode)
%
% DESCRIPTION:
%
%    Function takes 2 points in 3d, p1 and p2, and a step length
%    and finds a point on the line formed by extending p1,p2 by the
%    amount (or fraction of p1-p2 distance) step extends away from p1.
%
%    Positive step crops p1,p2 by the step amount.
%    Negative step extends p1,p2 by the step amount.
%
%    mode = 'relative' means step is the distance p3 extends away from p1 relative
%    to the distance between p1 and p2. This is the default if no mode is provided.
%
%    mode = 'absolute' means step is the absolute distance (in unit length) that
%    p3 extends away from p1.
%
%    mode = 'all' returns the set of all points at intervals 1/step on the line
%    segment between p1 and p2.
%
% AUTHOR: Jay Dubb (jdubb@nmr.mgh.harvard.edu)
% DATE:   7/7/2012
%

if ~exist('mode','var')
    mode = 'relative';
end

p3 = p1;

if all(p1==p2)
    return;
end

dx0 = p1(1)-p2(1);
dy0 = p1(2)-p2(2);
dz0 = p1(3)-p2(3);
dr = (dx0*dx0 + dy0*dy0 + dz0*dz0)^0.5;
if strcmp(mode, 'relative')
    crop = step*dr;
    dx = crop*dx0/dr;
    dy = crop*dy0/dr;
    dz = crop*dz0/dr;
elseif strcmp(mode, 'absolute')
    crop = step/dr;
    dx = crop*dx0;
    dy = crop*dy0;
    dz = crop*dz0;
elseif strcmp(mode, 'all')
    if step > 1
        return;
    end
    ii=1;
    while (step*ii)<1
        crop = (step*ii)*dr;
        dx(ii) = crop*dx0/dr;
        dy(ii) = crop*dy0/dr;
        dz(ii) = crop*dz0/dr;
        ii=ii+1;
    end
end

for ii=1:length(dx)
    p3(ii,1) = p1(1)-dx(ii);
    p3(ii,2) = p1(2)-dy(ii);
    p3(ii,3) = p1(3)-dz(ii);
end


function [MDP] = spm_MDP_VB_sleep(MDP,BMR)
% Bayesian model reduction (sleep) for MDP models
% FORMAT [MDP] = spm_MDP_VB_sleep(MDP,BMR)
%
% MDP  - (inverted) MDP structure
%
% BMR.g - modality [default: 1]
% BMR.o - outcomes - that induce REM [default: {}]
% BMR.x - increase in concentration parameters for BMR [default: 8]
% BMR.f - hearing factors to sum over [default: 0]
% BMR.T - log Bayes factor threshold [default: 1/4]
% BMR.m - indicator function to enable BMR [@(i,i1,i2,i3,i4)1]
%
%
% MDP  - (reduced) model structure: with reduced MDP.a
%
% This routine optimises the hyperparameters of a NDP model (i.e.,
% concentration parameters encoding probabilities. It uses Bayesian model
% reduction to evaluate the evidence for models with and without a
% particular parameter in the columns of MDP.a (c.f., SWS)
%
% If specified, the scheme will then recompute posterior beliefs about the
% model parameters based upon (fictive) outcomes generated under its
% (reduced) generative model.(c.f., REM sleep)
%
% See also: spm_MDP_log_evidence.m, spm_MDP_VB and spm_MDP_VB_X.m
%__________________________________________________________________________
% Copyright (C) 2005 Wellcome Trust Centre for Neuroimaging

% Karl Friston
% $Id: spm_MDP_VB_sleep.m 7679 2019-10-24 15:54:07Z spm $

% deal with a sequence of trials
%==========================================================================

% BMR options
%--------------------------------------------------------------------------
try, g   = BMR.g; catch, g = 1;   end
try, o   = BMR.o; catch, o = {};  end
try, x   = BMR.x; catch, x = 8;   end
try, f   = BMR.f; catch, f = 0;   end
try, T   = BMR.T; catch, T = 1/4; end

% model selection function
%--------------------------------------------------------------------------
if isfield(BMR,'m')
    m = BMR.m;
else
    m = @(i,i1,i2,i3,i4)1;
end

% Baysian model reduction - parameters
%--------------------------------------------------------------------------
if isfield(MDP,'a')
    [sa,ra] = spm_MDP_VB_prune(MDP(end).a{g},MDP(1).a0{g},f,x,T,m);
end

% reiterate expectation maximisation (or rapid eye movement sleep)
%--------------------------------------------------------------------------
N  = numel(o);
if N

    % remove previous experience
    %----------------------------------------------------------------------
    REM  = MDP;
    try, REM = rmfield(REM,'s'); end
    try, REM = rmfield(REM,'o'); end
    try, REM = rmfield(REM,'u'); end

    % and install a generative process and reset priors
    %----------------------------------------------------------------------
    REM.a{g}  = ra;
    REM.a0{g} = ra;
    REM.o = o{1};
    for i = 1:N
        REM(i)   = REM(1);
        REM(i).o = o{i};
    end

    % Bayesian updating and updated parameters
    %----------------------------------------------------------------------
    REM    = spm_MDP_VB_X(REM);
    MDP.a  = REM(N).a;
    MDP.a0 = REM(N).a0;

else

    % otherwise, use reduced posteriors and priors
    %----------------------------------------------------------------------
    MDP.a{g}  = sa;
    MDP.a0{g} = ra;

end

function [sA,nA] = spm_MDP_VB_prune(qA,pA,f,x,T,m)
% FORMAT [sA,nA] = spm_MDP_VB_prune(qA,pA,f,x,T,m)
% qA - posterior expectations
% pA - prior expectations
% f  - hidden factor to integrate over [defult: 0]
% x  - prior counts [default: 8]
% T  - threshold for Bayesian model reduction [default: three]
%
% sA - reduced posterior expectations
% rA - reduced prior expectations
%__________________________________________________________________________

% defaults
%--------------------------------------------------------------------------
if nargin < 4, T = 3; end
if nargin < 3, x = 8; end

% identify noninformative subspaces
%--------------------------------------------------------------------------
ndim  = ndims(pA);
for i = 1:ndim
    d    = 1:ndim;
    d(i) = [];
    s    = pA < 4 & pA > 0;
    for j = 1:(ndim - 1)
        s = sum(s,d(j));
    end
    ind{i} = find(s(:));
end

% motifs of salient (low free free energy) abilities
%--------------------------------------------------------------------------
mot{1} = [1 0 0;0 1 0;0 0 1];
mot{2} = [1 0 0;0 0 1;0 1 0];
mot{3} = [0 1 0;1 0 0;0 0 1];
mot{4} = [0 1 0;0 0 1;1 0 0];
mot{5} = [0 0 1;1 0 0;0 1 0];
mot{6} = [0 0 1;0 1 0;1 0 0];

% model space: additional concentration parameters (i.e., precision)
%--------------------------------------------------------------------------
nmot  = numel(mot);
for i = 1:nmot*nmot;
    rA{i} = spm_zeros(pA);
end

for m1 = 1:numel(mot)
    [i1,i2] = find(mot{m1});
    for m2 = 1:numel(mot)
        i  = nmot*(m1 - 1) + m2;
        for j = 1:numel(i1)
            dA    = spm_zeros(pA);
            dA(1:3,i1(j),:,i2(j),4) = mot{m2};
            rA{i} = rA{i} + dA*x;
        end
    end
end

% score models using Bayesian model reduction
%--------------------------------------------------------------------------
for i = 1:numel(rA);
    G    = spm_MDP_log_evidence(qA,pA,pA + rA{i});
    F(i) = sum(G(isfinite(G)));
end

% find any model that has greater evidence than the parent model
%--------------------------------------------------------------------------
[F,j] = min(F);
if (F + T) < 0
    rA = rA{j};
else
    rA = spm_zeros(pA);
end

% reduced posterior and prior
%--------------------------------------------------------------------------
sA     = qA;
nA     = pA;
for i1 = 1:size(qA,2)
    for i2 = 1:size(qA,3)
        for i3 = 1:size(qA,4)
            for i4 = 1:size(qA,5)

                % get posteriors, priors and reduced priors
                %----------------------------------------------------------
                p  = pA(:,i1,i2,i3,i4);
                q  = qA(:,i1,i2,i3,i4);
                r  = rA(:,i1,i2,i3,i4);
                j  = find(p);
                p  = p(j);
                q  = q(j);
                r  = j(find(r(j)));

                % redistribute concentration parameters if indicated
                %----------------------------------------------------------
                if numel(r);
                    sA(:,i1,i2,i3,i4) = 0;
                    nA(:,i1,i2,i3,i4) = 0;
                    sA(r,i1,i2,i3,i4) = sum(q);
                    nA(r,i1,i2,i3,i4) = sum(p);
                else
                    sA(j,i1,i2,i3,i4) = q;
                    nA(j,i1,i2,i3,i4) = p;
                end
            end
        end
    end
end

return

% alternative formulation with specified indicator functions
%==========================================================================

% defaults
%--------------------------------------------------------------------------
if nargin < 5, m = @(i,i1,i2,i3,i4)1; end
if nargin < 4, T = 3; end
if nargin < 3, x = 8; end
if nargin < 2, f = 0; end

% column-wise model comparison
%--------------------------------------------------------------------------
for i1 = 1:size(qA,2)
    for i2 = 1:size(qA,3)
        for i3 = 1:size(qA,4)
            for i4 = 1:size(qA,5)

                % get posteriors, priors and cycle over reduced priors
                %----------------------------------------------------------
                p  = pA(:,i1,i2,i3,i4);
                q  = qA(:,i1,i2,i3,i4);
                j  = find(p);
                p  = p(j);
                q  = q(j);

                % informative state?
                %----------------------------------------------------------
                F  = 0;
                if length(j) > 1
                    for i = 1:length(j);
                        if m(i,i1,i2,i3,i4)
                            r    = p;
                            r(i) = r(i) + x;
                            F(i) = spm_MDP_log_evidence(q,p,r);
                        else
                            F(i) = 16;
                        end
                    end
                end

                % eliminate parameter
                %----------------------------------------------------------
                [F,i] = min(F);
                mF(i1,i2,i3,i4) = F;
                iF(i1,i2,i3,i4) = j(i);
            end
        end
    end
end

% pool over s{f}
%---------------------------------------------------------------------
if f, sF = sum(mF,f); end

% column-wise reduction
%--------------------------------------------------------------------------
sA     = qA;
rA     = pA;
for i1 = 1:size(qA,2)
    for i2 = 1:size(qA,3)
        for i3 = 1:size(qA,4)
            for i4 = 1:size(qA,5)

                % get posteriors, priors and cycle over reduced priors
                %----------------------------------------------------------
                p  = pA(:,i1,i2,i3,i4);
                q  = qA(:,i1,i2,i3,i4);
                i  = iF(i1,i2,i3,i4);
                j  = find(p);
                p  = p(j);
                q  = q(j);

                % BMC
                %----------------------------------------------------------
                if f == 0
                    F = mF(i1,i2,i3,i4);
                elseif f == 1
                    F = sF( 1,i2,i3,i4);
                elseif f == 2
                    F = sF(i1, 1,i3,i4);
                elseif f == 3
                    F = sF(i1,i2, 1,i4);
                elseif f == 4
                    F = sF(i1,i2,i3, 1);
                end

                % eliminate parameter
                %----------------------------------------------------------
                if F < - T;
                    sA(:,i1,i2,i3,i4) = 0;
                    rA(:,i1,i2,i3,i4) = 0;
                    sA(i,i1,i2,i3,i4) = sum(q);
                    rA(i,i1,i2,i3,i4) = sum(p);
                else
                    sA(j,i1,i2,i3,i4) = q;
                    rA(j,i1,i2,i3,i4) = p;
                end

            end
        end
    end
end


function events = sta_lta(wave,varargin)

%STA_LTA: Short-Time-Average/Long-Time-Average event detector.
%
%USAGE: events = sta_lta(wave,prop_name_1,prop_val_1,...)
%
%DIAGRAM:
%                       /\      /\/\        /\
%              /\  /\/\/  \/\  /    \/\    /  \/\
%                \/          \/        \/\/      \
%                                           |-STA-| -->
%         --> |-----------------LTA---------------| -->
%
%INPUTS: wave     - A waveform object containing events (maybe)
%        varargin - User-defined parameter name/value pairs (below)
%
%VALID PROP_NAME:
%
%  'edp'      - Event Detection Parameters (Detailed in 'VALID PROP_VAL')
%  'lta_mode' - LTA window behavior following a 'trigger on' flag
%  'skip'     - Times for STA/LTA to skip over (if known), examples are
%               data gaps, calibration pulses, periods of excessive noise
%  'eot'      - Event output type (Detailed in 'VALID PROP_VAL')
%  'fix'      - Output event length fixed or variable?
%  'pad'      - Pad output events by a fixed amount of time before start
%               time (trigger on) and after stop time (trigger off).
%
%VALID PROP_VAL:
%
%  'edp'(1x6 numeric) ... default: [1 8 2 1.6 0 3]
%     --> [l_sta l_lta th_on th_off min_sep min_dur]
%        --> l_sta    - STA window length (s)
%        --> l_lta    - LTA window length (s)
%        --> th_on    - STA/LTA trigger on threshold
%        --> th_off   - STA/LTA trigger off threshold
%        --> min_sep  - Minimum seperation between trigger off time of an
%                       event and trigger on time of the next event (s)
%        --> min_dur  - Minimum event duration to be recorded(s)
%
%	'lta_mode' (string) ... default: 'continuous'
%     --> 'frozen' - LTA window fixed in place after trigger is turned on
%                    while STA window continues forward.
%     --> 'continuous' - LTA window continues w/ STA window after trigger
%                        is turned on (Same behavior as before trigger)
%     --> 'grow' - LTA left edge fixed, right edges continues w/ STA right
%                  edge after trigger on, thus size of LTA window is
%                  growing until trigger off, at which point LTA window
%                  returns to original size.
%
%	'skip' (nx2 numeric) ... default: [] (don't skip anything)
%     --> skip_val - List of start/stop times to skip
%
%	'eot' (string) ... default: 'sst'
%     --> 'st'  - Return event start times (nx1 numeric)
%                 (a.k.a. trigger on times)
%     --> 'sst' - Return event start/stop times (nx2 numeric)
%                 (a.k.a. trigger on/off times)
%     --> 'ssd' - Return event start/stop datapoints (nx2 integer)
%                 (referenced from first datapoint in 'wave')
%     --> 'wfa' - Return event waveform array (1xn waveform)
%
%   'fix' (1x1 numeric) ... default: 0
%     --> 'fix_val' - If 0, output events will be variable length
%                     (trigger on time to trigger off time)
%                     If a positive numeric value N, output events will be
%                     fixed length (trigger on time to fixed length N)
%                     Units of N is seconds if non-zero
%
%   'pad' (1x2 numeric) ... default: [0 0] (i.e. no padding)
%     --> 'pad_val' - If [0 0] events will have no time padding
%                     [1 2] - pad by 1s before, 2s after event
%                     this will pad events regarless of the value of fix:
%                     i.e. fix = 7, pad = [1 2], returned events are 10s
%
%OUTPUTS: events - events in format specified by 'return'

% Author: Dane Ketner, Alaska Volcano Observatory
% $Date$
% $Revision$

%% Check waveform variable
if isa(wave,'waveform')
   Fs = get(wave,'freq');         % Sampling frequency
   l_v = get(wave,'data_length'); % Length of time series
   tv = get(wave, 'timevector');  % Time vector of waveform
   if isempty(wave)
       disp('Input waveform empty, no events detected')
       events = [];
       return
   end
else
   error('STA_LTA: First Argument must be a waveform object')
end

%% Set all default parameters
l_sta = 1*Fs;     % STA window length
l_lta = 8*Fs;     % LTA window length
th_on = 2;        % Trigger on when sta_to_lta exceeds this theshold
th_off = 1.6;     % Trigger off when sta_to_lta drops below threshold
min_sep = 0*Fs;   % Skip ahead after end of event
min_dur = 3*Fs;   % Any triggers shorter than min_dur are discarded

lta_mode = 'continuous'; % Post trigger-on LTA behavior
skip = [];               % List of start/stop times to skip
eot = 'sst';             % Event output type
fix = 0*Fs;              % Fix event length
pad = [0 0]*Fs;          % Pad event start/stop times

%% Check varargin size
nv = numel(varargin);
if ~rem(nv,2) == 0
   error(['STA_LTA: Arguments after wave must appear in ',...
          'property_name/property_value pairs'])
end

%% User-defined parameters (varargin)
if nv > 0
    for p = 1:2:nv-1
        v1 = varargin{p};
        v2 = varargin{p+1};
        switch lower(v1)
            case 'edp'
                if isnumeric(v2) && numel(v2) == 6
                    l_sta = v2(1)*Fs;    % STA window length
                    l_lta = v2(2)*Fs;    % LTA window length
                    th_on = v2(3);       % Trigger on theshold
                    th_off = v2(4);      % Trigger off threshold
                    min_sep = v2(5)*Fs;  % Skip ahead after end of event
                    min_dur = v2(6)*Fs;  % Minimum event duration
                else
                    error('STA_LTA: Wrong format for input ''edp''')
                end
            case 'lta_mode'
                switch lower(v2)
                    case {'freeze','frozen'}
                        lta_mode = 'frozen';
                    case {'continue','continuous'}
                        lta_mode = 'continuous';
                    case {'grow','growing'}
                        lta_mode = 'grow';
                    otherwise
                      error('STA_LTA: Wrong format for input ''lta_mode''')
                end

            case 'skip'
                if isnumeric(v2) && size(v2,2) == 2
                    skip = v2;
                else
                    error('STA_LTA: Wrong format for input ''skip''')
                end
            case 'eot'
                switch lower(v2)
                    case 'st'
                        eot = v2;
                    case 'sst'
                        eot = v2;
                    case 'ssd'
                        eot = v2;
                    case 'wfa'
                        eot = v2;
                    otherwise
                        error('STA_LTA: Wrong format for input ''eot''')
                end
            case 'fix'
                if isnumeric(v2) && numel(v2) == 1
                    fix = v2*Fs;
                else
                    error('STA_LTA: Wrong format for input ''fix''')
                end
            case 'pad'
                if isnumeric(v2) && size(v2,1) == 1 && size(v2,2) == 2
                    pad = v2*Fs;
                else
                    error('STA_LTA: Wrong format for input ''pad''')
                end
            otherwise
                error(['STA_LTA: ''edp'', ''lta_mode'', ''skip'' ',...
                    '''eot'', ''fix'', and ''pad'' are the only ',...
                    'property names recognized'])
        end
    end
end

%% Initialize waveform data
wave = set2val(wave,skip,NaN);   % NaN skip times
wave = zero2nan(wave,10);        % NaN any data gaps
v = get(wave,'data');            % Waveform data
abs_v = abs(v);                  % Absolute value of time series

%% Initialize flags and other variables
lta_calc_flag = false;       % has the full LTA window been calculated?
ntrig = 0;               % number of triggers
trig_array = zeros(1,2); % array of trigger times: [on,off;on,off;...]

%% Loops over data
% i is the primary reference point (right end of STA/LTA window)
i = l_lta+1;
while i <= l_v % START STA_LTA MAIN LOOP

%% Skip data gaps (NaN values in LTA window)?
   if any(isnan(abs_v(i-l_lta:i)))
      gap = true;
      lta_calc_flag = false; % Force full claculations after gap
      while (gap) && (i < l_v)
         i = i+1;
         if ~any(isnan(abs_v(i-l_lta:i)))
            gap = false;
         end
      end
   end

%% Calculate STA & LTA Sum (Do Full Calculation?)
   if ~lta_calc_flag
      lta_sum = 0;
      sta_sum = 0;
      for j = i-l_lta:i-1              % Loop to compute LTA & STA
         lta_sum = lta_sum + abs_v(j); % Sum LTA window
         if (i - j) <= l_sta           % Sum STA window (right side of LTA)
            sta_sum = sta_sum + abs_v(j);
         end
      end
      lta_calc_flag = true;
   else

%% Calculate STA & LTA Sum (Single new data point if not Full)
      lta_sum = lta_sum - abs_v(i-l_lta-1) + abs_v(i-1);
      sta_sum = sta_sum - abs_v(i-l_sta-1) + abs_v(i-1);
   end

%% Calculate STA & LTA
   lta = lta_sum/l_lta;
   sta = sta_sum/l_sta;

%% Calculate STA/LTA Ratio
   sta_to_lta = sta/lta;

%% Trigger on? (Y/N)
   if (sta_to_lta > th_on)
      j = i;   % Set secondary reference point = primary
      g = 0;   % l_lta growth, only used if LTA growing
      while (sta_to_lta > th_off)
         j = j+1;
         if j < l_v
            sta_sum = sta_sum - abs_v(j-l_sta-1) + abs_v(j-1);
            switch lta_mode
               case 'frozen'
                  % LTA is good just the way it is
               case 'continuous'
                  % Add new data point, remove oldest data point
                  lta_sum = lta_sum - abs_v(j-l_lta-1) + abs_v(j-1);
               case 'grow'
                  % Add new data point, increase
                  lta_sum = lta_sum + abs_v(j-1);
                  l_lta = l_lta + 1;
                  g = g+1;
            end
            sta = sta_sum/l_sta;
            lta = lta_sum/l_lta;
            sta_to_lta = sta/lta;
            if any(isnan(abs_v(j-l_sta:j))) % NaN gaps to skip?
               sta_to_lta = 0; % Force trigger off (data gap in STA window)
            end
         else
            sta_to_lta = 0; % Force trigger off (end of data)
         end
      end
      duration = (j-i); % span from trigger on to trigger off
      l_lta = l_lta-g;

%% Triggered period long enough? (Y/N)
      if duration > min_dur % If duration < min_dur then skip it
         trig_t = i-l_sta;  % Beginning of STA window during trigger on
         end_t  = j;        % End of STA window during trigger off
         ntrig = ntrig + 1; % Event counter
         trig_array(ntrig,:) = [trig_t, end_t];
      end
      i = j + min_sep;   % Skip ahead by minimum event seperation
      lta_calc_flag = false; % Reset LTA calc flag to force new computation
   end
   i = i + 1;
end % END STA_LTA MAIN LOOP

%% Return events
if (trig_array(1,1)==0)&&(trig_array(1,2)==0)
    disp('No events detected')
    events = [];
    return
end
if fix>0
    trig_array(:,2) = trig_array(:,1) + fix;
end
trig_array(:,1) = trig_array(:,1) - pad(1);
trig_array(:,2) = trig_array(:,2) + pad(2);
% If events are padded, make sure the first and last events are not out of
% range (beginning before or ending after the span of input 'wave')
trig_array(trig_array<1) = 1;      % Set to first datapoint
trig_array(trig_array>l_v) = l_v;  % Set to last datapoint
switch lower(eot)
    case {'st'}
        events = tv(trig_array(:,1));
    case {'sst'}
        events = tv(trig_array);
    case {'ssd'}
        events = trig_array;
    case {'wfa'}
        events = [];
        for n = 1:size(trig_array,1)
            events = [events; extract(wave,'INDEX',trig_array(n,1),...
                                                   trig_array(n,2))];
        end
end


function [p ener a enerFF aFF max_doseV doseV_wt] = optSourceModel_depth6cm_blurring(planC, energy, numBin, extraBin, depth, dose, ...
    depth1, x1, profile1,  depth2, x2, profile2, depth3, x3, profile3, depth4, x4, profile4, LB, UB);
% JC Dec 09,2005
% Optimize photon spectrum, using (Fatigue-life)*Fermi distribution
% Usage:
% energy = energy of the photon beam
% numBin = number of bins to divide the energy range [0 energy]
% depth is a row vector as the depth
% dose is the corresponding dose at each depth
%
% Copyright 2010, Joseph O. Deasy, on behalf of the CERR development team.
%
% This file is part of The Computational Environment for Radiotherapy Research (CERR).
%
% CERR development has been led by:  Aditya Apte, Divya Khullar, James Alaly, and Joseph O. Deasy.
%
% CERR has been financially supported by the US National Institutes of Health under multiple grants.
%
% CERR is distributed under the terms of the Lesser GNU Public License.
%
%     This version of CERR is free software: you can redistribute it and/or modify
%     it under the terms of the GNU General Public License as published by
%     the Free Software Foundation, either version 3 of the License, or
%     (at your option) any later version.
%
% CERR is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
% without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
% See the GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with CERR.  If not, see <http://www.gnu.org/licenses/>.

% Underline assumptions:
% The calculated dose sets are the same size as the uniformizedCTscan.
% 1. Interpolation the calculated dose on to the respectively dose profiles
% coordinates may cause problem: Since the individual dose profile
% measurements do not necessarily have the same coordinates/# of points,
%

% 2. The input depth/x coordinates are in cm
%    The depth dose (PDD) is normalized. dmax = 1
%    The lateral dose profiles at various depth are also normalized. Each
%       has a maximal value as 1. It will be scaled according to PDD.

% 3. The phantom is at least 25cm in the depth direction. Otherwise, the
% interpolation of PDD will have out-flow, meaning the non-meaningful
% values, N/A. An error will be thrown out.
%   Also, the input PDD's depth may not start from zero, this may generate
%   N/A if the yVals starts from zero. So chop yVals according to PDD's
%   depth.

% 4. All input measured data are in a nx1 vector, i.e. a column vector

% 5 Add more input parameters: adjustable to different machines
% LB=[0.6  -0.1 1.2   100  1  0.1  0.005    0.005];
% UB=[1.5  0.1   10  1000  4   0.25  0.05  0.05];

% Input parameters descriptions:
% LB, UB. Upper and lower bound for the parameters.
% p, the optimazied parameters.
% p(1:4) The parameters for "Fagitue-Fermi" function, to model the primary photon
% spectrum.
% p(5) The parameter to "shrink" the primary photon spectrum
% to get the spectrum for the extra-focal (FF) components.
% p(6) The parameter to "scale" the extra-focal (FF) components,
% to determine the relative fluence contribution of the extra-focal (FF)
% to the primary photon.
% p(7) used to determine "the slope of the horn", not used anymor i.e. It is how many "pixels"/"grid".
% The dimension of the ICsigma in (cm) should be p(9)*e.
% p(8) the electron contamination (fluence relative to the primary photon.
% p(9) the "ICsigma", to convolve the MC dose, to model the spearing of the dose by IC chamber.
% If the resolution of the dose calc. grid changes, i.e.
% The dimension of the sigma is p(9)*dx, where "dx" is the grid spacing for
% dose calc.

% 6. Add another parameter for the optimization, sigma. A gaussian filter
% to blur the penumbra.

% Sept 21, 2006
% Add electron contamination components.
% For PDD, includes depth < 5cm

% Nov 27, 2006
% correct
% doseP2_arrayFF(:,i) = dose3D(find(yVals == yDepth3),[xIndexStart:xIndexEnd],int16(s));
% doseP2_arrayFF(:,i) = dose3D(find(yVals == yDepth2),[xIndexStart:xIndexEnd],int16(s));

% Get the isocenter coordinate
bf = planC{7}.FractionGroupSequence.Item_1.ReferencedBeamSequence.Item_1;
bs = planC{7}.BeamSequence.Item_1;
iC = bs.ControlPointSequence.Item_1.IsocenterPosition;

position = {planC{7}.PatientSetupSequence.(['Item_' num2str(1)]).PatientPosition};

if strcmpi(position, 'HFP')
    isocenter.x = iC(1)/10;
    isocenter.y = iC(2)/10;
    isocenter.z = -iC(3)/10;
else
    isocenter.x = iC(1)/10;
    isocenter.y = -iC(2)/10;
    isocenter.z = -iC(3)/10;
end
% So iC is the isocenter. Need to find indices from the (x, y, z) values.

[xVals, yVals, zVals] = getUniformScanXYZVals(planC{3});
% Need to confirm that iC(2), i.e. the y value is right on the skin
% surface. Thus, the depth for dose along the center ray can be determined.
j = find(strcmpi({planC{4}.structureName}, 'Skin'));

if isempty(j)
    j = find(strcmpi({planC{4}.structureName}, 'Body'));
end

if isempty(j)
    error('Skin structure must be defined to input to DPM.');
else
    j = j(1);
end

skinMask=getUniformStr(j,planC);

%confirm that the isocenter.y is right on the skin surface
bbox=boundingBox(skinMask)
%[r,c,s] = xyztom(isocenter.x,isocenter.y,isocenter.z, planC)
[r,c,s] = xyztom(isocenter.x,isocenter.y,isocenter.z, 1, planC)

%Get the indices of where yVals==depth(end)/10 ; Failed???
%[r,c,s] = xyztom(isocenter.x,yVals(find(yVals==isocenter.y-min(isocenter.y-yVals))),isocenter.z, planC)

% Get the objective dose from input(Golden Beam Data), not from planC.

% get the yVals for different depth, to pick out the appropriate lateral dose profiles in the calcualted dose sets.
dy = yVals(2)-yVals(1);  % dy is < 0, because of y increases as row number decreases
yDepth0 = yVals(bbox(1));
yDepth1 = yVals(bbox(1)+int16(-depth1/dy));
yDepth2 = yVals(bbox(1)+int16(-depth2/dy));
yDepth3 = yVals(bbox(1)+int16(-depth3/dy));
yDepth4 = yVals(bbox(1)+int16(-depth4/dy));
yDepth25cm = yVals(bbox(1)+int16(-25/dy));

%depthTPS can't start from 0, which will cause intepolated data N/A.
depthTPS = [ceil(min(depth)/(-dy))*(-dy) : -dy: yVals(bbox(1))-yDepth25cm];
PDD = interp1(depth, dose, depthTPS);
doseV_obj = PDD';
doseV_array = zeros(length(PDD),numBin+extraBin);
max_doseV = zeros(numBin+extraBin,1);

% GET the start and end indices objective for dose profile at depth 5cm
xStart = max(min([x1 x2 x3 x4]));
xEnd = min(max([x1 x2 x3 x4]));
% use margin 1 to make sure the results xTPS is within the measured region.
xIndexStart = find(abs(xStart - xVals) == min(abs(xStart - xVals))) + 1;
xIndexEnd = find(abs(xEnd - xVals) == min(abs(xEnd - xVals))) - 1 ;

% Should correct xTPS for isocenter.x? Does, then xTPS maybe outside of the
% valid region.
 xTPS = [xVals(xIndexStart)-isocenter.x : (xVals(2)-xVals(1)): xVals(xIndexEnd)-isocenter.x];
% Currently, do not correct.
% xTPS = [xVals(xIndexStart): (xVals(2)-xVals(1)): xVals(xIndexEnd)];

doseProfile1 = interp1(x1, profile1, xTPS);
% Since the input measured data is the asdfScale it to be consistent with PDD
% Needed for Golden Beam data. Not for Murty's measurement.
doseProfile1_obj = doseProfile1' * PDD(find(abs(depth1-depthTPS) == min(abs(depth1-depthTPS))));
% Creat array to hold the dose profiles for the different energy bins.
doseP1_array = zeros(length(doseProfile1_obj), numBin+extraBin);

doseProfile2 = interp1(x2, profile2, xTPS);
% Scale it to be consistent with PDD
doseProfile2_obj = doseProfile2' * PDD(find(abs(depth2-depthTPS) == min(abs(depth2-depthTPS))));
doseP2_array = zeros(length(doseProfile2_obj), numBin+extraBin);

% GET the objective for dose profile at depth 10cm
doseProfile3 = interp1(x3, profile3, xTPS);
% Scale it to be consistent with PDD
doseProfile3_obj = doseProfile3' * PDD(find(abs(depth3-depthTPS) == min(abs(depth3-depthTPS))));
doseP3_array = zeros(length(doseProfile3_obj), numBin+extraBin);

% GET the objective for dose profile at depth 20cm
doseProfile4 = interp1(x4, profile4, xTPS);
% Scale it to be consistent with PDD
doseProfile4_obj = doseProfile4' * PDD(find(abs(depth4-depthTPS) == min(abs(depth4-depthTPS))));
doseP4_array = zeros(length(doseProfile4_obj), numBin+extraBin);

figure; plot(depthTPS, PDD);
% There's shift of doseProfile2_obj, x as half voxel size. 0.1875/2 =
% 0.094cm
hold on; plot(xTPS, doseProfile1, 'c', xTPS, doseProfile2, 'b', xTPS, doseProfile3, 'r', xTPS, doseProfile4, 'g');
legend('PDD', num2str(depth1), num2str(depth2), num2str(depth3), num2str(depth4));

% Read in the dose from the DPM calculation.
for i = 1:(numBin+extraBin),
    filename = ['dose3D_', num2str(i*energy/numBin),'MV.mat'];
    load (filename, 'dose3D');
    doseV_array(:,i) = dose3D([(bbox(1)+ceil(min(depth)/(-dy))):find(yVals == yDepth25cm)],int16(c),int16(s));
    doseP1_array(:,i) = dose3D(find(yVals == yDepth1),[xIndexStart:xIndexEnd],int16(s));
    doseP2_array(:,i) = dose3D(find(yVals == yDepth2),[xIndexStart:xIndexEnd],int16(s));
    doseP3_array(:,i) = dose3D(find(yVals == yDepth3),(xIndexStart:xIndexEnd),int16(s));
    doseP4_array(:,i) = dose3D(find(yVals == yDepth4),(xIndexStart:xIndexEnd),int16(s));

  end

% currentDir = pwd;
% cd ./FlatFilter
%NOW, for the flattenning filter part, need to do the exactly same thing,
%i.e. get PDD and doseP5cm, doseP10cm, doseP20cm....
numBinFF = ceil((numBin+extraBin)/2); %15
% JC. Use all energy bins.
numBinFF = numBin+extraBin; %15
doseV_arrayFF = zeros(length(PDD),numBinFF);
doseP1_arrayFF = zeros(length(doseProfile1), numBinFF);
doseP2_arrayFF = zeros(length(doseProfile2), numBinFF);
doseP3_arrayFF = zeros(length(doseProfile3), numBinFF);
doseP4_arrayFF = zeros(length(doseProfile4), numBinFF);
% Read in the dose from the DPM calculation, for the secondary source, i.e. Flattening Filter,FF.
for i = 1 : numBinFF,
    filename = ['dose3D_FF_', num2str((double(i*energy)/double(numBin))),'MV.mat'];
    load (filename, 'dose3D');
    doseV_arrayFF(:,i) = dose3D([(bbox(1)+ceil(min(depth)/(-dy))):find(yVals == yDepth25cm)],int16(c),int16(s));
    doseP1_arrayFF(:,i) = dose3D(find(yVals == yDepth1),[xIndexStart:xIndexEnd],int16(s));
    doseP2_arrayFF(:,i) = dose3D(find(yVals == yDepth2),[xIndexStart:xIndexEnd],int16(s));
    doseP3_arrayFF(:,i) = dose3D(find(yVals == yDepth3),(xIndexStart:xIndexEnd),int16(s));
    doseP4_arrayFF(:,i) = dose3D(find(yVals == yDepth4),(xIndexStart:xIndexEnd),int16(s));
end
% cd(currentDir);

% Read the dose of the electron contamination.
load dose3D_elec.mat dose3D
doseV_e = dose3D([(bbox(1)+ceil(min(depth)/(-dy))):find(yVals == yDepth25cm)],int16(c),int16(s));
% Need doseP5cm_e etc. is a collumn vector.
doseP1_e = dose3D(find(yVals == yDepth1),[xIndexStart:xIndexEnd],int16(s))';
doseP2_e = dose3D(find(yVals == yDepth2),[xIndexStart:xIndexEnd],int16(s))';
doseP3_e = dose3D(find(yVals == yDepth3),(xIndexStart:xIndexEnd),int16(s))';
doseP4_e = dose3D(find(yVals == yDepth4),(xIndexStart:xIndexEnd),int16(s))';

%%Need to pick the indices for dose profile at 5cm, 10cm, 20cm. And shift
%%the middle as x_coordinate = 0, as in the golden beam data.
%% Also need to scale the maximum dose at this depth according to the PDD
%% at this depth, because the profile dose is relative (max = 100).
%% Now only account the dose profile at depth 5cm.

%options = optimset('DISPLAY', 'notify', 'TolX', 1.e-12);
%%%% Using Ant Colony Opt. (api.m)
%%% O.K. parameters. JC

% Parameters: p(1) till p(4), the four parameters to determine the primary
% photon spectrum, sum(a) is the weight.
% p(5) "shrink" for the Flattening filter.
% p(6) is the weight of FF photon, aFF = aFF * p(6)
% p(7) is the Horn effect weight, respective to sum(a)
% p(8) is the electron contamination weight, respective to sum(a)

FUN='PDD_Profile_res';
% "working" LB, UB settings
% Add one more bounds for the penumbra
%LB=[0.6  -0.1 1.2   100  2  0.1  0.01    0.005 0.01];
%UB=[1.5  0.1   10  1000  4   0.2  0.05  0.04 5];

% expand the LB and UB by the suggesstions from JOD. On Oct 09, 2006
% LB=[0.6  -0.1 1.2   100  1  0.1  0.005    0.001];
% UB=[1.5  0.1   10  1000  4   0.25  0.05  0.05];

NumAnts=25;Nmoves=20;LocalMoves=30;
NONLNCON=[];rpmax=[]; RES=1e-8;
p=api(FUN,LB,UB,NumAnts,Nmoves,LocalMoves,NONLNCON,rpmax,RES, ...
    doseV_obj, doseV_array, doseV_arrayFF, doseV_e, ...
    doseProfile1_obj, doseP1_array, doseP1_arrayFF, doseP1_e, ...
    doseProfile2_obj, doseP2_array, doseP2_arrayFF, doseP2_e, ...
    doseProfile3_obj, doseP3_array, doseP3_arrayFF, doseP3_e, ...
    doseProfile4_obj, doseP4_array, doseP4_arrayFF, doseP4_e, ...
    energy, numBin, extraBin, numBinFF)

% Output results
ener = energy/numBin: energy/numBin:energy*(1+extraBin/numBin);
a = Fatigue(p(1:4), ener);
a(find(isnan(a))) = 0;

cutoff = 0.85;
Ef = energy*cutoff;
kt = energy*0.15;  % change kt
f = 1./(1+exp((ener-Ef)/kt));
a = a.*f;

doseV_wt = (doseV_array)  * real(a)';
doseProfile1_wt = (doseP1_array) * real(a)';
doseProfile2_wt = (doseP2_array) * real(a)';
doseProfile3_wt = (doseP3_array)  * real(a)';
doseProfile4_wt = (doseP4_array) * real(a)';

% Get the weights/spectrum for the flattening filter
enerFF = energy/numBin : energy/numBin : numBinFF*energy/numBin;
aFF = Fatigue(p(1:4), p(5)*enerFF);
aFF(find(isnan(aFF))) = 0;

% Get modified Flattening filter spectrum/weights
f = 1./(1+exp((p(5)*enerFF-Ef)/kt));
aFF = aFF.* f;
aFF = aFF * (sum(a)/sum(aFF)*p(6)) ;  % scale FF weight respect to sum(a)

doseV_wtFF = doseV_arrayFF * real(aFF)';
doseProfile1_wtFF = doseP1_arrayFF * real(aFF)';
doseProfile2_wtFF = doseP2_arrayFF * real(aFF)';
doseProfile3_wtFF = doseP3_arrayFF * real(aFF)';
doseProfile4_wtFF = doseP4_arrayFF * real(aFF)';

filterSize = 7;
filterWindow = [-3 -2 -1 0 1 2 3];
G1 = gauss(filterWindow, p(9));  %p(9) is sigma
G1 = G1/sum(G1);   % normalize, to make sum(G1)==1;

doseV_DPM = doseV_wt + doseV_wtFF + p(8) * sum(a)* doseV_e;
doseProfile1_DPM = doseProfile1_wt + doseProfile1_wtFF + p(8) * sum(a)* doseP1_e;
doseProfile1_DPM = conv(G1, doseProfile1_DPM);
doseProfile2_DPM = doseProfile2_wt + doseProfile2_wtFF + p(8) * sum(a)* doseP2_e;
doseProfile2_DPM = conv(G1, doseProfile2_DPM);
doseProfile3_DPM = doseProfile3_wt + doseProfile3_wtFF + p(8) * sum(a)* doseP3_e;
doseProfile3_DPM = conv(G1, doseProfile3_DPM);
doseProfile4_DPM = doseProfile4_wt + doseProfile4_wtFF + p(8) * sum(a)* doseP4_e;
doseProfile4_DPM = conv(G1, doseProfile4_DPM);

figure;
subplot(2,1,1); plot(depth,dose, '.');
hold on;
plot(depthTPS,doseV_DPM, 'r');
legend('measured', 'DPM wt');
axis([0 25 0 1.1])
subplot(2,1,2); plot([0 ener], [0 a], '+-b')
hold on;  plot([0 enerFF], [0 aFF], '+-r')

% Note: There's shift in the measured dose. Now fix the display
figure;
plot(xTPS, doseProfile1_obj, 'b', xTPS, doseProfile1_DPM(4:end-3), '-.r')
hold on; plot(xTPS, doseProfile3_obj, 'c', xTPS, doseProfile3_DPM(4:end-3), '-.m')
legend('measured', ['DPM ', num2str(depth1)], 'measured', ['DPM ', num2str(depth3)]);

figure; plot(xTPS, doseProfile2_obj, 'b', xTPS, doseProfile2_DPM(4:end-3), '-.r')
hold on; plot(xTPS, doseProfile4_obj, 'c', xTPS, doseProfile4_DPM(4:end-3), '-.m');
legend('measured', ['DPM ', num2str(depth2)], 'measured', ['DPM ', num2str(depth4)]);

filename = 'modelParameters';
save(filename, 'p', 'ener', 'a', 'enerFF', 'aFF', 'max_doseV', 'doseV_wt');
disp('ok');


function targets=FSEOF(model,biomassRxn,targetRxn,iterations,coefficient,outputFile)
% FSEOF: implements the algorithm of Flux Scanning based on Enforced Objective Flux.
%
%   model           a model structure
%   biomassRxn      string with reaction ID of the biomass formation or
%                   growth reaction
%   targetRxn       string with reaction ID of target reaction
%   iterations      double indicating number of iterations (opt, default 10)
%   coefficient     double indicating ratio of optimal target reaction
%                   flux, must be less than 1 (opt, default 0.9)
%   outputFile      string with output filename (opt, default prints to
%                   command window)
%
%   targets         structure with target identifying information for each reaction
%       logical     logical array indicating FSEOF identified target reaction
%       slope       double array with FSEOF calculated slopes for each reaction

%OUTPUTS
%   This function writes an tab-delimited file or prints to command window. If an output
%   has been specified (targets), it will also generate a structure indicating for
%   each reaction whether it is identified by FSEOF as a target and the slope of the
%   reaction when switching from biomass formation to product formation.
%
%   Usage: targets=FSEOF(model,biomassRxn,targetRxn,iterations,coefficient,outputFile)

biomassRxn=char(biomassRxn);
targetRxn=char(targetRxn);

if nargin<4
    iterations=10;
    coefficient=0.9;
end

if nargin <5
    coefficient=0.9;
end

if nargin == 6
    output=1;
else
    output=0;
end

%Find out the maximum theoretical yield of target reaction
model=setParam(model,'obj',targetRxn,1);
sol=solveLP(model,1);
targetMax=abs(sol.f*coefficient);   % 90 percent of the theoretical yield

model=setParam(model,'obj',biomassRxn,1);

fseof.results=zeros(length(model.rxns),iterations);
fseof.target=zeros(length(model.rxns),1);
rxnDirection=zeros(length(model.rxns),1);

%Enforce objective flux iteratively
for i=1:iterations
    n=i*targetMax/iterations;
    model=setParam(model,'lb',targetRxn,n);

    sol=solveLP(model,1);

    fseof.results(:,i)=sol.x;

    %Loop through all fluxes and identify the ones that increased upon the
    %enforced objective flux
    for j=1:length(fseof.results)
        if fseof.results(j,1) > 0   %Check the positive fluxes

            if i == 1   %The initial round
                rxnDirection(j,1)=1;
                fseof.target(j,1)=1;
            else

                if (fseof.results(j,i) > fseof.results(j,i-1)) & fseof.target(j,1)
                    fseof.target(j,1)=1;
                else
                    fseof.target(j,1)=0;
                end
            end

        elseif fseof.results(j,1) < 0 %Check the negative fluxes

            if i == 1   %The initial round
                rxnDirection(j,1)=-1;
                fseof.target(j,1)=1;
            else
                if (fseof.results(j,i) < fseof.results(j,i-1)) & fseof.target(j,1)
                    fseof.target(j,1)=1;
                else
                    fseof.target(j,1)=0;
                end
            end

        end

    end
end

%Generating output
formatSpec='%s\t%s\t%s\t%s\t%s\t%s\t%s\n';
if output == 1    %Output to a file
    outputFile=char(outputFile);
    fid=fopen(outputFile,'w');
    fprintf(fid,formatSpec,'Slope','rowID','Enzyme ID','Enzyme Name','Subsystems','Direction','Gr Rule');
else              %Output to screen
    fprintf(formatSpec,'Slope','rowID','Enzyme ID','Enzyme Name','Subsystems','Direction','Gr Rule');
end

for num=1:length(fseof.target)
    if fseof.target(num,1) == 1
        A0=num2str(abs(fseof.results(num,iterations)-fseof.results(num,1))/abs(targetMax-targetMax/iterations)); %Slope calculation
        A1=num2str(num);                                  %row ID
        A2=char(model.rxns(num));                         %enzyme ID
        A3=char(model.rxnNames(num));                     %enzyme Name
        if isfield(model,'subSystems') && ~isempty(model.subSystems{num});
            A4=char(strjoin(model.subSystems{num,1},';'));                   %Subsystems
        else
            A4='';
        end
        A5=num2str(model.rev(num)*rxnDirection(num,1));   %reaction Dirction
        A6=char(model.grRules(num));                      %Gr Rule
        if output == 1    %Output to a file
            fprintf(fid,formatSpec,A0,A1,A2,A3,A4,A5,A6);
        else              %Output screen
            fprintf(formatSpec,A0,A1,A2,A3,A4,A5,A6);
        end
    end
end

if output == 1    %Output to a file
    fclose(fid);
end

if nargout == 1
    targets.logical=logical(fseof.target);
    targets.slope=abs(fseof.results(:,iterations)-fseof.results(:,1))/abs(targetMax-targetMax/iterations); %Slope calculation
end
end


function [val] = optGeneFitnessTilt(rxn_vector_matrix, model, targetRxn, rxnListInput, isGeneList)
% The fitness function
%
% USAGE:
%
%    [val] = optGeneFitnessTilt(rxn_vector_matrix, model, targetRxn, rxnListInput, isGeneList)
%
% INPUTS:
%    rxn_vector_matrix:    reactions vectors in a matrix
%    model:                model structure
%    targetRxn:            target reactions
%    rxnListInput:         list of reactions
%    isGeneList:           bolean checking if it is a gene list
%
% OUTPUT:
%    val:                  fitness value

global MaxKnockOuts
global CBT_LP_SOLVER
%size(rxn_vector_matrix)

%rxnGeneMat is a required field for this function, so if it does not exist,
%build it.
if isGeneList && ~isfield(model,'rxnGeneMat')
    model = buildRxnGeneMat(model);
end

popsize = size(rxn_vector_matrix,1);
val = zeros(1,popsize);

for i = 1:popsize
    rxn_vector = rxn_vector_matrix(i,:);
    rxnList = rxnListInput(logical(rxn_vector));

    %see if we've done this before
    val_temp = memoize(rxn_vector);
    if ~ isempty(val_temp)
        val(i) = val_temp;
        continue;
    end

    % check to see if mutations is above the max number allowed
    nummutations = sum(rxn_vector);
    if nummutations > MaxKnockOuts
        continue;
    end

    % generate knockout.
    if isGeneList
        modelKO = deleteModelGenes(model, rxnList);
    else % is reaction list
        [isValidRxn,removeInd] = ismember(rxnList,model.rxns);
        removeInd = removeInd(isValidRxn);
        modelKO = model;
        modelKO.ub(removeInd) = 0;
        modelKO.lb(removeInd) = 0;
    end
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % augment BOF (tilt)
    [modelKO] = augmentBOF(modelKO, targetRxn, .001);

    % find growthrate
    if exist('LPBasis', 'var')
        modelKO.LPBasis = LPBasis;
    end

    if ~isempty(CBT_LP_SOLVER) && strcmp(CBT_LP_SOLVER,'cplex')
        [solKO, LPOUT] = solveCobraLPCPLEX(modelKO, 0,1);
        LPBasis = LPOUT.LPBasis;
        growthrate = solKO.obj;
        [~,tar_loc] = ismember(targetRxn,modelKO.rxns);
        minProdAtSetGR = solKO.full(tar_loc);
    else
        solKO = optimizeCbModel(modelKO);
        if isempty(solKO.x)
            continue;
        else
            growthrate = solKO.f;
            [~,tar_loc] = ismember(targetRxn,modelKO.rxns);
            minProdAtSetGR = solKO.x(tar_loc);
        end
    end

    % check to ensure that GR is above a certain value
    if growthrate < .10
        continue;
    end

    % %    display('second optimization');
    %     % find the lowesest possible production rate (a hopefully high number)
    %     % at the max growth rate minus some set factor gamma (a growth rate slightly
    %     % smaller than the max). A positive value will eliminate solutions where the
    %     % production envelope has a vertical line at the max GR, a "non-unique"
    %     % solution. Set value to zero if "non-unique" solutions are not an issue.
    %     gamma = 0.01; % proportional to Grwoth Rate (hr-1), a value around 0.5 max.
    %
    %     %find indicies of important vectors
    %     indBOF = find(modelKO.c);
    %     indTar = findRxnIDs(modelKO, targetRxn);
    %     % generate a model with a fixed max KO growth rate
    %     modelKOsetGR = modelKO;
    %     modelKOsetGR.lb(indBOF) = growthrate - gamma; % this growth rate is required as lb.
    %     modelKOsetGR.c = zeros(size(modelKO.c));
    %     modelKOsetGR.c(indTar) = -1; % minimize for this variable b/c we want to look at the very minimum production.
    %
    %     % find the minimum production rate for the targeted reaction.
    %
    % %     solKOsetGR = optimizeCbModel(modelKOsetGR);
    % %     minProdAtSetGR1 = -solKOsetGR.f;  % This should be a negative value b/c of the minimization setup, so -1 is necessary.
    %
    %     if exist('LPBasis2', 'var')
    %         modelKOsetGR.LPBasis = LPBasis2;
    %     end
    %
    %     [solKOsetGR, LPOUT2] = solveCobraLPCPLEX(modelKOsetGR, 0,1);
    %     LPBasis2 = LPOUT2.LPBasis;
    %     minProdAtSetGR = -solKOsetGR.obj;

    % objective function for optGene algorithm = val (needs to be a negative value, since it is
    % a minimization)
    val(i) = -minProdAtSetGR;
    % penalty for a greater number of mutations

    %val(i) = -minProdAtSetGR * (.98^nummutations);

    % select best substrate-specific productivity
    % val(i) = -minProdAtSetGR * (.98^nummutations) * growthrate;

    % check to prevent very small values from being considerered improvments
    if val(i) > -1e-3
        val(i) = 0;
    end

    memoize(rxn_vector, val(i));
end

return;

%% Memoize
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%% MEMOIZE %%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% internal function to speed things up.

function [value] =  memoize(gene_vector, value)
global HTABLE
hashkey = num2str(gene_vector);
hashkey = strrep(hashkey,' ',''); % cut out white space from string (more space efficient).

if nargin == 1
    value = HTABLE.get(hashkey);
    return;
else
    if HTABLE.size() > 50000
        HTABLE = java.util.Hashtable;  %reset the hashtable if more than 50,000 entries.
    end
    HTABLE.put(hashkey, value);
    value = [];
    return;
end


function [StopFlag, Status] = StopCritBDCA(nfxk, Niter, Nmap, T, MaxNumIter, MaxNumMapEval, TimeLimit, epsilon, Stopping_Crit)
% Function checking that one of the stopping criteria
% holds to terminate LLM and GLM. It perepare the status determining why
% the algorithm is stopped.
%
% USAGE:
%
%    [StopFlag, Status] = StopCritBDCA(nfxk, Niter, Nmap, T, MaxNumIter, MaxNumMapEval, TimeLimit, epsilon, Stopping_Crit)
%
% INPUTS:
%    nhxk:             the norm 2 of `h(xk)`
%    Niter:            the number of iterations
%    Nmap:             the number of mapping calls
%    T:                the running time
%    MaxNumIter:       maximum number of iterations
%    MaxNumMapEval:    maximum number of function evaluations
%    TimeLimit:        maximum running time
%    epsilon:          accuracy parameter
%    Stopping_Crit:    stopping criterion:
%
%                        1. stop if :math:`||nfxk|| \leq \epsilon`
%                        2. stop if `MaxNumIter` is reached
%                        3. stop if `MaxNumMapEval` is reached
%                        4. stop if `TimeLimit` is reached
%                        5. stop if (default) :math:`||hxk|| \leq \epsilon` or `MaxNumIter` is reached
%
% OUTPUTS:
%    StopFlag:         1: if one of the stopping criteria holds, 0: if none of the stopping criteria holds
%    Status:           the reason of the scheme termination

switch Stopping_Crit

  case 1
    if nhxk <= epsilon
      StopFlag = 1;
      Status   = 'A solution of nonlinear system is found.';
    else
      StopFlag = 0;
      Status   = [];
    end

  case 2
    if Niter >= MaxNumIter
      StopFlag = 1;
      Status   = 'Maximum number of iterations is reached.';
    else
      StopFlag = 0;
      Status   = [];
    end

  case 3
    if Nmap >= MaxNumMapEval
      StopFlag = 1;
      Status   = 'Maximum number of mapping evaluations is reached.';
    else
      StopFlag = 0;
      Status   = [];
    end

  case 4
    if T >= TimeLimit
      StopFlag = 1;
      Status   = 'Time limit is reached.';
    else
      StopFlag = 0;
      Status   = [];
    end

  case 5
    if (nfxk <= epsilon || Niter >= MaxNumIter)
      StopFlag = 1;
      if Niter < MaxNumIter
          Status = 'a solution is found';
      else
          Status = 'Maximum number of iterations is reached.';
      end
    else
      StopFlag = 0;
      Status   = [];
    end

end

end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%% End of StopCritBDCA.m %%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%